diff options
Diffstat (limited to 'libraries')
205 files changed, 123548 insertions, 0 deletions
diff --git a/libraries/Makefile.in b/libraries/Makefile.in new file mode 100644 index 0000000..907c708 --- /dev/null +++ b/libraries/Makefile.in @@ -0,0 +1,22 @@ +# Libraries Makefile for OpenLDAP +# $OpenLDAP$ +## This work is part of OpenLDAP Software <http://www.openldap.org/>. +## +## Copyright 1998-2021 The OpenLDAP Foundation. +## All rights reserved. +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted only as authorized by the OpenLDAP +## Public License. +## +## A copy of this license is available in the file LICENSE in the +## top-level directory of the distribution or, alternatively, at +## <http://www.OpenLDAP.org/license.html>. + +SUBDIRS= \ + liblutil \ + liblber \ + liblunicode \ + libldap libldap_r \ + librewrite + diff --git a/libraries/liblber/Makefile.in b/libraries/liblber/Makefile.in new file mode 100644 index 0000000..2247868 --- /dev/null +++ b/libraries/liblber/Makefile.in @@ -0,0 +1,53 @@ +# LIBLBER +# $OpenLDAP$ +## This work is part of OpenLDAP Software <http://www.openldap.org/>. +## +## Copyright 1998-2021 The OpenLDAP Foundation. +## All rights reserved. +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted only as authorized by the OpenLDAP +## Public License. +## +## A copy of this license is available in the file LICENSE in the +## top-level directory of the distribution or, alternatively, at +## <http://www.OpenLDAP.org/license.html>. + +LIBRARY = liblber.la + +NT_SRCS = nt_err.c +NT_OBJS = nt_err.lo + +UNIX_SRCS = stdio.c +UNIX_OBJS = stdio.lo + +LIB_DEFS = -DLBER_LIBRARY + +SRCS= assert.c decode.c encode.c io.c bprint.c debug.c \ + memory.c options.c sockbuf.c $(@PLAT@_SRCS) +OBJS= assert.lo decode.lo encode.lo io.lo bprint.lo debug.lo \ + memory.lo options.lo sockbuf.lo $(@PLAT@_OBJS) +XSRCS= version.c + +PROGRAMS= dtest etest idtest + +LDAP_INCDIR= ../../include +LDAP_LIBDIR= ../../libraries + +XLIBS = $(LIBRARY) $(LDAP_LIBLUTIL_A) +XXLIBS = +NT_LINK_LIBS = $(AC_LIBS) +UNIX_LINK_LIBS = $(AC_LIBS) + +dtest: $(XLIBS) dtest.o + $(LTLINK) -o $@ dtest.o $(LIBS) +etest: $(XLIBS) etest.o + $(LTLINK) -o $@ etest.o $(LIBS) +idtest: $(XLIBS) idtest.o + $(LTLINK) -o $@ idtest.o $(LIBS) + +install-local: FORCE + -$(MKDIR) $(DESTDIR)$(libdir) + $(LTINSTALL) $(INSTALLFLAGS) -m 644 $(LIBRARY) $(DESTDIR)$(libdir) + $(LTFINISH) $(DESTDIR)$(libdir) + diff --git a/libraries/liblber/assert.c b/libraries/liblber/assert.c new file mode 100644 index 0000000..5e87dfb --- /dev/null +++ b/libraries/liblber/assert.c @@ -0,0 +1,40 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#ifdef LDAP_NEED_ASSERT + +#include <stdio.h> + +/* + * helper for our private assert() macro + * + * note: if assert() doesn't exist, like abort() or raise() won't either. + * could use kill() but that might be problematic. I'll just ignore this + * issue for now. + */ + +void +ber_pvt_assert( const char *file, int line, const char *test ) +{ + fprintf(stderr, + _("Assertion failed: %s, file %s, line %d\n"), + test, file, line); + + abort(); +} + +#endif diff --git a/libraries/liblber/bprint.c b/libraries/liblber/bprint.c new file mode 100644 index 0000000..c080b62 --- /dev/null +++ b/libraries/liblber/bprint.c @@ -0,0 +1,296 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* + * Copyright (c) 1991 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ +/* ACKNOWLEDGEMENTS: + * This work was originally developed by the University of Michigan + * (as part of U-MICH LDAP). + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/ctype.h> +#include <ac/stdarg.h> +#include <ac/string.h> + +#include "lber-int.h" + +#define ber_log_check(errlvl, loglvl) ((errlvl) & (loglvl)) + +BER_LOG_FN ber_int_log_proc = NULL; + +/* + * We don't just set ber_pvt_err_file to stderr here, because in NT, + * stderr is a symbol imported from a DLL. As such, the compiler + * doesn't recognize the symbol as having a constant address. Thus + * we set ber_pvt_err_file to stderr later, when it first gets + * referenced. + */ +FILE *ber_pvt_err_file = NULL; + +/* + * ber errno + */ +BER_ERRNO_FN ber_int_errno_fn = NULL; + +int * ber_errno_addr(void) +{ + static int ber_int_errno = LBER_ERROR_NONE; + + if( ber_int_errno_fn ) { + return (*ber_int_errno_fn)(); + } + + return &ber_int_errno; +} + +/* + * Print stuff + */ +void ber_error_print( LDAP_CONST char *data ) +{ + assert( data != NULL ); + + if (!ber_pvt_err_file) ber_pvt_err_file = stderr; + + fputs( data, ber_pvt_err_file ); + + /* Print to both streams */ + if (ber_pvt_err_file != stderr) { + fputs( data, stderr ); + fflush( stderr ); + } + + fflush( ber_pvt_err_file ); +} + +BER_LOG_PRINT_FN ber_pvt_log_print = ber_error_print; + +/* + * lber log + */ + +int ber_pvt_log_output( + const char *subsystem, + int level, + const char *fmt, + ... ) +{ + char buf[1024]; + va_list vl; + va_start( vl, fmt ); + + if ( ber_int_log_proc != NULL ) { + ber_int_log_proc( ber_pvt_err_file, subsystem, level, fmt, vl ); + + } else { + int level; + ber_get_option( NULL, LBER_OPT_BER_DEBUG, &level ); + buf[sizeof(buf) - 1] = '\0'; + vsnprintf( buf, sizeof(buf)-1, fmt, vl ); + if ( ber_log_check( LDAP_DEBUG_BER, level ) ) { + (*ber_pvt_log_print)( buf ); + } + } + + va_end(vl); + return 1; +} + +int ber_pvt_log_printf( int errlvl, int loglvl, const char *fmt, ... ) +{ + char buf[1024]; + va_list ap; + + assert( fmt != NULL ); + + if ( !ber_log_check( errlvl, loglvl )) { + return 0; + } + + va_start( ap, fmt ); + + buf[sizeof(buf) - 1] = '\0'; + vsnprintf( buf, sizeof(buf)-1, fmt, ap ); + + va_end(ap); + + (*ber_pvt_log_print)( buf ); + return 1; +} + +#if 0 +static int ber_log_puts(int errlvl, int loglvl, char *buf) +{ + assert( buf != NULL ); + + if ( !ber_log_check( errlvl, loglvl )) { + return 0; + } + + (*ber_pvt_log_print)( buf ); + return 1; +} +#endif + +/* + * Print arbitrary stuff, for debugging. + */ + +int +ber_log_bprint(int errlvl, + int loglvl, + const char *data, + ber_len_t len ) +{ + assert( data != NULL ); + + if ( !ber_log_check( errlvl, loglvl )) { + return 0; + } + + ber_bprint(data, len); + return 1; +} + +void +ber_bprint( + LDAP_CONST char *data, + ber_len_t len ) +{ + static const char hexdig[] = "0123456789abcdef"; +#define BP_OFFSET 9 +#define BP_GRAPH 60 +#define BP_LEN 80 + char line[BP_LEN]; + ber_len_t i; + + assert( data != NULL ); + + /* in case len is zero */ + line[0] = '\n'; + line[1] = '\0'; + + for ( i = 0 ; i < len ; i++ ) { + int n = i % 16; + unsigned off; + + if( !n ) { + if( i ) (*ber_pvt_log_print)( line ); + memset( line, ' ', sizeof(line)-2 ); + line[sizeof(line)-2] = '\n'; + line[sizeof(line)-1] = '\0'; + + off = i % 0x0ffffU; + + line[2] = hexdig[0x0f & (off >> 12)]; + line[3] = hexdig[0x0f & (off >> 8)]; + line[4] = hexdig[0x0f & (off >> 4)]; + line[5] = hexdig[0x0f & off]; + line[6] = ':'; + } + + off = BP_OFFSET + n*3 + ((n >= 8)?1:0); + line[off] = hexdig[0x0f & ( data[i] >> 4 )]; + line[off+1] = hexdig[0x0f & data[i]]; + + off = BP_GRAPH + n + ((n >= 8)?1:0); + + if ( isprint( (unsigned char) data[i] )) { + line[BP_GRAPH + n] = data[i]; + } else { + line[BP_GRAPH + n] = '.'; + } + } + + (*ber_pvt_log_print)( line ); +} + + +int +ber_log_dump( + int errlvl, + int loglvl, + BerElement *ber, + int inout ) +{ + assert( ber != NULL ); + assert( LBER_VALID( ber ) ); + + if ( !ber_log_check( errlvl, loglvl )) { + return 0; + } + + ber_dump(ber, inout); + return 1; +} + +void +ber_dump( + BerElement *ber, + int inout ) +{ + char buf[132]; + ber_len_t len; + + assert( ber != NULL ); + assert( LBER_VALID( ber ) ); + + if ( inout == 1 ) { + len = ber_pvt_ber_remaining(ber); + } else { + len = ber_pvt_ber_write(ber); + } + + sprintf( buf, "ber_dump: buf=%p ptr=%p end=%p len=%ld\n", + ber->ber_buf, + ber->ber_ptr, + ber->ber_end, + (long) len ); + + (void) (*ber_pvt_log_print)( buf ); + + ber_bprint( ber->ber_ptr, len ); +} + +typedef struct seqorset Seqorset; + +/* Exists for binary compatibility with OpenLDAP 2.4.17-- */ +int +ber_log_sos_dump( + int errlvl, + int loglvl, + Seqorset *sos ) +{ + return 0; +} + +/* Exists for binary compatibility with OpenLDAP 2.4.17-- */ +void +ber_sos_dump( + Seqorset *sos ) +{ +} diff --git a/libraries/liblber/debug.c b/libraries/liblber/debug.c new file mode 100644 index 0000000..7d1f772 --- /dev/null +++ b/libraries/liblber/debug.c @@ -0,0 +1,105 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdarg.h> +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/time.h> +#include <ac/ctype.h> + +#ifdef LDAP_SYSLOG +#include <ac/syslog.h> +#endif + +#include "ldap_log.h" +#include "ldap_defaults.h" +#include "lber.h" +#include "ldap_pvt.h" + +static FILE *log_file = NULL; +static int debug_lastc = '\n'; + +int lutil_debug_file( FILE *file ) +{ + log_file = file; + ber_set_option( NULL, LBER_OPT_LOG_PRINT_FILE, file ); + + return 0; +} + +void (lutil_debug)( int debug, int level, const char *fmt, ... ) +{ + char buffer[4096]; + va_list vl; + int len, off; + + if ( !(level & debug ) ) return; + +#ifdef HAVE_WINSOCK + if( log_file == NULL ) { + log_file = fopen( LDAP_RUNDIR LDAP_DIRSEP "openldap.log", "w" ); + + if ( log_file == NULL ) { + log_file = fopen( "openldap.log", "w" ); + if ( log_file == NULL ) return; + } + + ber_set_option( NULL, LBER_OPT_LOG_PRINT_FILE, log_file ); + } +#endif + + if (debug_lastc == '\n') { + sprintf(buffer, "%08x ", (unsigned) time(0L)); + off = 9; + } else { + off = 0; + } + va_start( vl, fmt ); + len = vsnprintf( buffer+off, sizeof(buffer)-off, fmt, vl ); + if (len > sizeof(buffer)-off) + len = sizeof(buffer)-off; + debug_lastc = buffer[len+off-1]; + buffer[sizeof(buffer)-1] = '\0'; + if( log_file != NULL ) { + fputs( buffer, log_file ); + fflush( log_file ); + } + fputs( buffer, stderr ); + va_end( vl ); +} + +#if defined(HAVE_EBCDIC) && defined(LDAP_SYSLOG) +#undef syslog +void eb_syslog( int pri, const char *fmt, ... ) +{ + char buffer[4096]; + va_list vl; + + va_start( vl, fmt ); + vsnprintf( buffer, sizeof(buffer), fmt, vl ); + buffer[sizeof(buffer)-1] = '\0'; + + /* The syslog function appears to only work with pure EBCDIC */ + __atoe(buffer); +#pragma convlit(suspend) + syslog( pri, "%s", buffer ); +#pragma convlit(resume) + va_end( vl ); +} +#endif diff --git a/libraries/liblber/decode.c b/libraries/liblber/decode.c new file mode 100644 index 0000000..a357ec9 --- /dev/null +++ b/libraries/liblber/decode.c @@ -0,0 +1,1000 @@ +/* decode.c - ber input decoding routines */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ +/* ACKNOWLEDGEMENTS: + * This work was originally developed by the University of Michigan + * (as part of U-MICH LDAP). + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> +#include <ac/stdarg.h> +#include <ac/string.h> +#include <ac/socket.h> + +#include "lber-int.h" + + +/* out->bv_len should be the buffer size on input */ +int +ber_decode_oid( BerValue *in, BerValue *out ) +{ + const unsigned char *der; + unsigned long val; + unsigned val1; + ber_len_t i; + char *ptr; + + assert( in != NULL ); + assert( out != NULL ); + + /* need 4 chars/inbyte + \0 for input={7f 7f 7f...} */ + if ( !out->bv_val || (out->bv_len+3)/4 <= in->bv_len ) + return -1; + + ptr = NULL; + der = (unsigned char *) in->bv_val; + val = 0; + for ( i=0; i < in->bv_len; i++ ) { + val |= der[i] & 0x7f; + if ( !( der[i] & 0x80 )) { + if ( ptr == NULL ) { + /* Initial "x.y": val=x*40+y, x<=2, y<40 if x<2 */ + ptr = out->bv_val; + val1 = (val < 80 ? val/40 : 2); + val -= val1*40; + ptr += sprintf( ptr, "%u", val1 ); + } + ptr += sprintf( ptr, ".%lu", val ); + val = 0; + } else if ( val - 1UL < LBER_OID_COMPONENT_MAX >> 7 ) { + val <<= 7; + } else { + /* val would overflow, or is 0 from invalid initial 0x80 octet */ + return -1; + } + } + if ( ptr == NULL || val != 0 ) + return -1; + + out->bv_len = ptr - out->bv_val; + return 0; +} + +/* Return tag, with *bv = rest of element (starting at length octets) */ +static ber_tag_t +ber_tag_and_rest( const BerElement *ber, struct berval *bv ) +{ + ber_tag_t tag; + ptrdiff_t rest; + unsigned char *ptr; + + assert( ber != NULL ); + assert( LBER_VALID( ber ) ); + + ptr = (unsigned char *) ber->ber_ptr; + rest = (unsigned char *) ber->ber_end - ptr; + if ( rest <= 0 ) { + goto fail; + } + + tag = ber->ber_tag; + if ( (char *) ptr == ber->ber_buf ) { + tag = *ptr; + } + ptr++; + rest--; + if ( (tag & LBER_BIG_TAG_MASK) != LBER_BIG_TAG_MASK ) { + goto done; + } + + do { + if ( rest <= 0 ) { + break; + } + tag <<= 8; + tag |= *ptr++ & 0xffU; + rest--; + + if ( ! (tag & LBER_MORE_TAG_MASK) ) { + goto done; + } + } while ( tag <= (ber_tag_t)-1 / 256 ); + + fail: + /* Error or unsupported tag size */ + tag = LBER_DEFAULT; + + done: + bv->bv_len = rest; + bv->bv_val = (char *) ptr; + return tag; +} + +/* Return the tag - LBER_DEFAULT returned means trouble */ +ber_tag_t +ber_get_tag( BerElement *ber ) +{ + struct berval bv; + ber_tag_t tag = ber_tag_and_rest( ber, &bv ); + + ber->ber_ptr = bv.bv_val; + return tag; +} + +/* Return next element's tag and point *bv at its contents in-place */ +ber_tag_t +ber_peek_element( const BerElement *ber, struct berval *bv ) +{ + ber_tag_t tag; + ber_len_t len, rest; + unsigned i; + unsigned char *ptr; + + assert( bv != NULL ); + + /* + * Any ber element looks like this: tag length contents. + * Assuming everything's ok, we return the tag, and point + * bv at the contents. + * + * Assumptions: + * 1) definite lengths + * 2) primitive encodings used whenever possible + */ + + len = 0; + + /* + * First, we read the tag. + */ + tag = ber_tag_and_rest( ber, bv ); + + rest = bv->bv_len; + ptr = (unsigned char *) bv->bv_val; + if ( tag == LBER_DEFAULT || rest == 0 ) { + goto fail; + } + + /* + * Next, read the length. The first octet determines the length + * of the length. If bit 8 is 0, the length is the short form, + * otherwise if the octet != 0x80 it's the long form, otherwise + * the ber element has the unsupported indefinite-length format. + * Lengths that do not fit in a ber_len_t are not accepted. + */ + + len = *ptr++; + rest--; + + if ( len & 0x80U ) { + len &= 0x7fU; + if ( len - 1U > sizeof(ber_len_t) - 1U || rest < len ) { + /* Indefinite-length/too long length/not enough data */ + goto fail; + } + + rest -= len; + i = len; + for( len = *ptr++ & 0xffU; --i; len |= *ptr++ & 0xffU ) { + len <<= 8; + } + } + + /* BER element should have enough data left */ + if( len > rest ) { + fail: + tag = LBER_DEFAULT; + } + + bv->bv_len = len; + bv->bv_val = (char *) ptr; + return tag; +} + +/* Move past next element, point *bv at it in-place, and return its tag. + * The caller may \0-terminate *bv, as next octet is saved in ber->ber_tag. + * Similar to ber_get_stringbv(ber, bv, LBER_BV_NOTERM) except on error. + */ +ber_tag_t +ber_skip_element( BerElement *ber, struct berval *bv ) +{ + ber_tag_t tag = ber_peek_element( ber, bv ); + + if ( tag != LBER_DEFAULT ) { + ber->ber_ptr = bv->bv_val + bv->bv_len; + ber->ber_tag = *(unsigned char *) ber->ber_ptr; + } + + return tag; +} + +ber_tag_t +ber_peek_tag( + BerElement *ber, + ber_len_t *len ) +{ + struct berval bv; + ber_tag_t tag = ber_peek_element( ber, &bv ); + + *len = bv.bv_len; + return tag; +} + +ber_tag_t +ber_skip_tag( BerElement *ber, ber_len_t *lenp ) +{ + struct berval bv; + ber_tag_t tag = ber_peek_element( ber, &bv ); + + ber->ber_ptr = bv.bv_val; + ber->ber_tag = *(unsigned char *) ber->ber_ptr; + + *lenp = bv.bv_len; + return tag; +} + +ber_tag_t +ber_get_int( + BerElement *ber, + ber_int_t *num ) +{ + ber_tag_t tag; + ber_len_t len; + struct berval bv; + + assert( num != NULL ); + + tag = ber_skip_element( ber, &bv ); + len = bv.bv_len; + if ( tag == LBER_DEFAULT || len > sizeof(ber_int_t) ) { + return LBER_DEFAULT; + } + + /* parse two's complement integer */ + if( len ) { + unsigned char *buf = (unsigned char *) bv.bv_val; + ber_len_t i; + ber_int_t netnum = buf[0] & 0xff; + + /* sign extend */ + netnum = (netnum ^ 0x80) - 0x80; + + /* shift in the bytes */ + for( i = 1; i < len; i++ ) { + netnum = (netnum << 8 ) | buf[i]; + } + + *num = netnum; + + } else { + *num = 0; + } + + return tag; +} + +ber_tag_t +ber_get_enum( + BerElement *ber, + ber_int_t *num ) +{ + return ber_get_int( ber, num ); +} + +ber_tag_t +ber_get_stringb( + BerElement *ber, + char *buf, + ber_len_t *len ) +{ + struct berval bv; + ber_tag_t tag; + + if ( (tag = ber_skip_element( ber, &bv )) == LBER_DEFAULT ) { + return LBER_DEFAULT; + } + + /* must fit within allocated space with termination */ + if ( bv.bv_len >= *len ) { + return LBER_DEFAULT; + } + + memcpy( buf, bv.bv_val, bv.bv_len ); + buf[bv.bv_len] = '\0'; + + *len = bv.bv_len; + return tag; +} + +/* Definitions for get_string vector + * + * ChArray, BvArray, and BvVec are self-explanatory. + * BvOff is a struct berval embedded in an array of larger structures + * of siz bytes at off bytes from the beginning of the struct. + */ +enum bgbvc { ChArray, BvArray, BvVec, BvOff }; + +/* Use this single cookie for state, to keep actual + * stack use to the absolute minimum. + */ +typedef struct bgbvr { + const enum bgbvc choice; + const int option; /* (ALLOC unless BvOff) | (STRING if ChArray) */ + ber_len_t siz; /* input array element size, output count */ + ber_len_t off; /* BvOff offset to the struct berval */ + void *result; +} bgbvr; + +static ber_tag_t +ber_get_stringbvl( BerElement *ber, bgbvr *b ) +{ + int i = 0, n; + ber_tag_t tag; + ber_len_t tot_size = 0, siz = b->siz; + char *last, *orig; + struct berval bv, *bvp = NULL; + union stringbvl_u { + char **ca; /* ChArray */ + BerVarray ba; /* BvArray */ + struct berval **bv; /* BvVec */ + char *bo; /* BvOff */ + } res; + + tag = ber_skip_tag( ber, &bv.bv_len ); + + if ( tag != LBER_DEFAULT ) { + tag = 0; + orig = ber->ber_ptr; + last = orig + bv.bv_len; + + for ( ; ber->ber_ptr < last; i++, tot_size += siz ) { + if ( ber_skip_element( ber, &bv ) == LBER_DEFAULT ) + break; + } + if ( ber->ber_ptr != last ) { + i = 0; + tag = LBER_DEFAULT; + } + + ber->ber_ptr = orig; + ber->ber_tag = *(unsigned char *) orig; + } + + b->siz = i; + if ( i == 0 ) { + return tag; + } + + /* Allocate and NULL-terminate the result vector */ + b->result = ber_memalloc_x( tot_size + siz, ber->ber_memctx ); + if ( b->result == NULL ) { + return LBER_DEFAULT; + } + switch (b->choice) { + case ChArray: + res.ca = b->result; + res.ca[i] = NULL; + break; + case BvArray: + res.ba = b->result; + res.ba[i].bv_val = NULL; + break; + case BvVec: + res.bv = b->result; + res.bv[i] = NULL; + break; + case BvOff: + res.bo = (char *) b->result + b->off; + ((struct berval *) (res.bo + tot_size))->bv_val = NULL; + tot_size = 0; + break; + } + + n = 0; + do { + tag = ber_get_stringbv( ber, &bv, b->option ); + if ( tag == LBER_DEFAULT ) { + goto failed; + } + + /* store my result */ + switch (b->choice) { + case ChArray: + res.ca[n] = bv.bv_val; + break; + case BvArray: + res.ba[n] = bv; + break; + case BvVec: + bvp = ber_memalloc_x( sizeof( struct berval ), + ber->ber_memctx ); + if ( !bvp ) { + ber_memfree_x( bv.bv_val, ber->ber_memctx ); + goto failed; + } + res.bv[n] = bvp; + *bvp = bv; + break; + case BvOff: + *(struct berval *)(res.bo + tot_size) = bv; + tot_size += siz; + break; + } + } while (++n < i); + return tag; + +failed: + if (b->choice != BvOff) { /* BvOff does not have LBER_BV_ALLOC set */ + while (--n >= 0) { + switch(b->choice) { + case ChArray: + ber_memfree_x(res.ca[n], ber->ber_memctx); + break; + case BvArray: + ber_memfree_x(res.ba[n].bv_val, ber->ber_memctx); + break; + case BvVec: + ber_memfree_x(res.bv[n]->bv_val, ber->ber_memctx); + ber_memfree_x(res.bv[n], ber->ber_memctx); + break; + default: + break; + } + } + } + ber_memfree_x(b->result, ber->ber_memctx); + b->result = NULL; + return LBER_DEFAULT; +} + +ber_tag_t +ber_get_stringbv( BerElement *ber, struct berval *bv, int option ) +{ + ber_tag_t tag; + char *data; + + tag = ber_skip_element( ber, bv ); + if ( tag == LBER_DEFAULT || + (( option & LBER_BV_STRING ) && + bv->bv_len && memchr( bv->bv_val, 0, bv->bv_len - 1 ))) + { + bv->bv_val = NULL; + return LBER_DEFAULT; + } + + data = bv->bv_val; + if ( option & LBER_BV_ALLOC ) { + bv->bv_val = (char *) ber_memalloc_x( bv->bv_len + 1, + ber->ber_memctx ); + if ( bv->bv_val == NULL ) { + return LBER_DEFAULT; + } + + if ( bv->bv_len != 0 ) { + memcpy( bv->bv_val, data, bv->bv_len ); + } + data = bv->bv_val; + } + if ( !( option & LBER_BV_NOTERM )) + data[bv->bv_len] = '\0'; + + return tag; +} + +ber_tag_t +ber_get_stringbv_null( BerElement *ber, struct berval *bv, int option ) +{ + ber_tag_t tag; + char *data; + + tag = ber_skip_element( ber, bv ); + if ( tag == LBER_DEFAULT || bv->bv_len == 0 ) { + bv->bv_val = NULL; + return tag; + } + + if (( option & LBER_BV_STRING ) && + memchr( bv->bv_val, 0, bv->bv_len - 1 )) + { + bv->bv_val = NULL; + return LBER_DEFAULT; + } + + data = bv->bv_val; + if ( option & LBER_BV_ALLOC ) { + bv->bv_val = (char *) ber_memalloc_x( bv->bv_len + 1, + ber->ber_memctx ); + if ( bv->bv_val == NULL ) { + return LBER_DEFAULT; + } + + memcpy( bv->bv_val, data, bv->bv_len ); + data = bv->bv_val; + } + if ( !( option & LBER_BV_NOTERM )) + data[bv->bv_len] = '\0'; + + return tag; +} + +ber_tag_t +ber_get_stringa( BerElement *ber, char **buf ) +{ + BerValue bv; + ber_tag_t tag; + + assert( buf != NULL ); + + tag = ber_get_stringbv( ber, &bv, LBER_BV_ALLOC | LBER_BV_STRING ); + *buf = bv.bv_val; + + return tag; +} + +ber_tag_t +ber_get_stringa_null( BerElement *ber, char **buf ) +{ + BerValue bv; + ber_tag_t tag; + + assert( buf != NULL ); + + tag = ber_get_stringbv_null( ber, &bv, LBER_BV_ALLOC | LBER_BV_STRING ); + *buf = bv.bv_val; + + return tag; +} + +ber_tag_t +ber_get_stringal( BerElement *ber, struct berval **bv ) +{ + ber_tag_t tag; + + assert( ber != NULL ); + assert( bv != NULL ); + + *bv = (struct berval *) ber_memalloc_x( sizeof(struct berval), + ber->ber_memctx ); + if ( *bv == NULL ) { + return LBER_DEFAULT; + } + + tag = ber_get_stringbv( ber, *bv, LBER_BV_ALLOC ); + if ( tag == LBER_DEFAULT ) { + ber_memfree_x( *bv, ber->ber_memctx ); + *bv = NULL; + } + return tag; +} + +ber_tag_t +ber_get_bitstringa( + BerElement *ber, + char **buf, + ber_len_t *blen ) +{ + ber_tag_t tag; + struct berval data; + unsigned char unusedbits; + + assert( buf != NULL ); + assert( blen != NULL ); + + if ( (tag = ber_skip_element( ber, &data )) == LBER_DEFAULT ) { + goto fail; + } + + if ( --data.bv_len > (ber_len_t)-1 / 8 ) { + goto fail; + } + unusedbits = *(unsigned char *) data.bv_val++; + if ( unusedbits > 7 ) { + goto fail; + } + + if ( memchr( data.bv_val, 0, data.bv_len )) { + goto fail; + } + + *buf = (char *) ber_memalloc_x( data.bv_len, ber->ber_memctx ); + if ( *buf == NULL ) { + return LBER_DEFAULT; + } + memcpy( *buf, data.bv_val, data.bv_len ); + + *blen = data.bv_len * 8 - unusedbits; + return tag; + + fail: + *buf = NULL; + return LBER_DEFAULT; +} + +ber_tag_t +ber_get_null( BerElement *ber ) +{ + ber_len_t len; + ber_tag_t tag = ber_skip_tag( ber, &len ); + + return( len == 0 ? tag : LBER_DEFAULT ); +} + +ber_tag_t +ber_get_boolean( + BerElement *ber, + ber_int_t *boolval ) +{ + return ber_get_int( ber, boolval ); +} + +ber_tag_t +ber_first_element( + BerElement *ber, + ber_len_t *len, + char **last ) +{ + assert( last != NULL ); + + /* skip the sequence header, use the len to mark where to stop */ + if ( ber_skip_tag( ber, len ) == LBER_DEFAULT ) { + *last = NULL; + return LBER_DEFAULT; + } + + *last = ber->ber_ptr + *len; + + if ( *len == 0 ) { + return LBER_DEFAULT; + } + + return ber_peek_tag( ber, len ); +} + +ber_tag_t +ber_next_element( + BerElement *ber, + ber_len_t *len, + LDAP_CONST char *last ) +{ + assert( ber != NULL ); + assert( last != NULL ); + assert( LBER_VALID( ber ) ); + + if ( ber->ber_ptr >= last ) { + return LBER_DEFAULT; + } + + return ber_peek_tag( ber, len ); +} + +/* VARARGS */ +ber_tag_t +ber_scanf ( BerElement *ber, + LDAP_CONST char *fmt, + ... ) +{ + va_list ap; + LDAP_CONST char *fmt_reset; + char *s, **ss, ***sss; + struct berval data, *bval, **bvp, ***bvpp; + ber_int_t *i; + ber_len_t *l; + ber_tag_t *t; + ber_tag_t rc; + ber_len_t len; + + va_start( ap, fmt ); + + assert( ber != NULL ); + assert( fmt != NULL ); + assert( LBER_VALID( ber ) ); + + fmt_reset = fmt; + + if ( ber->ber_debug & (LDAP_DEBUG_TRACE|LDAP_DEBUG_BER)) { + ber_log_printf( LDAP_DEBUG_TRACE, ber->ber_debug, + "ber_scanf fmt (%s) ber:\n", fmt ); + ber_log_dump( LDAP_DEBUG_BER, ber->ber_debug, ber, 1 ); + } + + for ( rc = 0; *fmt && rc != LBER_DEFAULT; fmt++ ) { + /* When this is modified, remember to update + * the error-cleanup code below accordingly. */ + switch ( *fmt ) { + case '!': { /* Hook */ + BERDecodeCallback *f; + void *p; + + f = va_arg( ap, BERDecodeCallback * ); + p = va_arg( ap, void * ); + + rc = (*f)( ber, p, 0 ); + } break; + + case 'a': /* octet string - allocate storage as needed */ + ss = va_arg( ap, char ** ); + rc = ber_get_stringa( ber, ss ); + break; + + case 'A': /* octet string - allocate storage as needed, + * but return NULL if len == 0 */ + ss = va_arg( ap, char ** ); + rc = ber_get_stringa_null( ber, ss ); + break; + + case 'b': /* boolean */ + i = va_arg( ap, ber_int_t * ); + rc = ber_get_boolean( ber, i ); + break; + + case 'B': /* bit string - allocate storage as needed */ + ss = va_arg( ap, char ** ); + l = va_arg( ap, ber_len_t * ); /* for length, in bits */ + rc = ber_get_bitstringa( ber, ss, l ); + break; + + case 'e': /* enumerated */ + case 'i': /* integer */ + i = va_arg( ap, ber_int_t * ); + rc = ber_get_int( ber, i ); + break; + + case 'l': /* length of next item */ + l = va_arg( ap, ber_len_t * ); + rc = ber_peek_tag( ber, l ); + break; + + case 'm': /* octet string in berval, in-place */ + bval = va_arg( ap, struct berval * ); + rc = ber_get_stringbv( ber, bval, 0 ); + break; + + case 'M': /* bvoffarray - must include address of + * a record len, and record offset. + * number of records will be returned thru + * len ptr on finish. parsed in-place. + */ + { + bgbvr cookie = { BvOff, 0 }; + bvp = va_arg( ap, struct berval ** ); + l = va_arg( ap, ber_len_t * ); + cookie.siz = *l; + cookie.off = va_arg( ap, ber_len_t ); + rc = ber_get_stringbvl( ber, &cookie ); + *bvp = cookie.result; + *l = cookie.siz; + break; + } + + case 'n': /* null */ + rc = ber_get_null( ber ); + break; + + case 'o': /* octet string in a supplied berval */ + bval = va_arg( ap, struct berval * ); + rc = ber_get_stringbv( ber, bval, LBER_BV_ALLOC ); + break; + + case 'O': /* octet string - allocate & include length */ + bvp = va_arg( ap, struct berval ** ); + rc = ber_get_stringal( ber, bvp ); + break; + + case 's': /* octet string - in a buffer */ + s = va_arg( ap, char * ); + l = va_arg( ap, ber_len_t * ); + rc = ber_get_stringb( ber, s, l ); + break; + + case 't': /* tag of next item */ + t = va_arg( ap, ber_tag_t * ); + *t = rc = ber_peek_tag( ber, &len ); + break; + + case 'T': /* skip tag of next item */ + t = va_arg( ap, ber_tag_t * ); + *t = rc = ber_skip_tag( ber, &len ); + break; + + case 'v': /* sequence of strings */ + { + bgbvr cookie = { + ChArray, LBER_BV_ALLOC | LBER_BV_STRING, sizeof( char * ) + }; + rc = ber_get_stringbvl( ber, &cookie ); + *(va_arg( ap, char *** )) = cookie.result; + break; + } + + case 'V': /* sequence of strings + lengths */ + { + bgbvr cookie = { + BvVec, LBER_BV_ALLOC, sizeof( struct berval * ) + }; + rc = ber_get_stringbvl( ber, &cookie ); + *(va_arg( ap, struct berval *** )) = cookie.result; + break; + } + + case 'W': /* bvarray */ + { + bgbvr cookie = { + BvArray, LBER_BV_ALLOC, sizeof( struct berval ) + }; + rc = ber_get_stringbvl( ber, &cookie ); + *(va_arg( ap, struct berval ** )) = cookie.result; + break; + } + + case 'x': /* skip the next element - whatever it is */ + rc = ber_skip_element( ber, &data ); + break; + + case '{': /* begin sequence */ + case '[': /* begin set */ + switch ( fmt[1] ) { + case 'v': case 'V': case 'W': case 'M': + break; + default: + rc = ber_skip_tag( ber, &len ); + break; + } + break; + + case '}': /* end sequence */ + case ']': /* end set */ + break; + + default: + if( ber->ber_debug ) { + ber_log_printf( LDAP_DEBUG_ANY, ber->ber_debug, + "ber_scanf: unknown fmt %c\n", *fmt ); + } + rc = LBER_DEFAULT; + break; + } + } + + va_end( ap ); + + if ( rc == LBER_DEFAULT ) { + /* + * Error. Reclaim malloced memory that was given to the caller. + * Set allocated pointers to NULL, "data length" outvalues to 0. + */ + va_start( ap, fmt ); + + for ( ; fmt_reset < fmt; fmt_reset++ ) { + switch ( *fmt_reset ) { + case '!': { /* Hook */ + BERDecodeCallback *f; + void *p; + + f = va_arg( ap, BERDecodeCallback * ); + p = va_arg( ap, void * ); + + (void) (*f)( ber, p, 1 ); + } break; + + case 'a': /* octet string - allocate storage as needed */ + case 'A': + ss = va_arg( ap, char ** ); + ber_memfree_x( *ss, ber->ber_memctx ); + *ss = NULL; + break; + + case 'b': /* boolean */ + case 'e': /* enumerated */ + case 'i': /* integer */ + (void) va_arg( ap, ber_int_t * ); + break; + + case 'l': /* length of next item */ + *(va_arg( ap, ber_len_t * )) = 0; + break; + + case 'm': /* berval in-place */ + bval = va_arg( ap, struct berval * ); + BER_BVZERO( bval ); + break; + + case 'M': /* BVoff array in-place */ + bvp = va_arg( ap, struct berval ** ); + ber_memfree_x( *bvp, ber->ber_memctx ); + *bvp = NULL; + *(va_arg( ap, ber_len_t * )) = 0; + (void) va_arg( ap, ber_len_t ); + break; + + case 'o': /* octet string in a supplied berval */ + bval = va_arg( ap, struct berval * ); + ber_memfree_x( bval->bv_val, ber->ber_memctx ); + BER_BVZERO( bval ); + break; + + case 'O': /* octet string - allocate & include length */ + bvp = va_arg( ap, struct berval ** ); + ber_bvfree_x( *bvp, ber->ber_memctx ); + *bvp = NULL; + break; + + case 's': /* octet string - in a buffer */ + (void) va_arg( ap, char * ); + *(va_arg( ap, ber_len_t * )) = 0; + break; + + case 't': /* tag of next item */ + case 'T': /* skip tag of next item */ + (void) va_arg( ap, ber_tag_t * ); + break; + + case 'B': /* bit string - allocate storage as needed */ + ss = va_arg( ap, char ** ); + ber_memfree_x( *ss, ber->ber_memctx ); + *ss = NULL; + *(va_arg( ap, ber_len_t * )) = 0; /* for length, in bits */ + break; + + case 'v': /* sequence of strings */ + sss = va_arg( ap, char *** ); + ber_memvfree_x( (void **) *sss, ber->ber_memctx ); + *sss = NULL; + break; + + case 'V': /* sequence of strings + lengths */ + bvpp = va_arg( ap, struct berval *** ); + ber_bvecfree_x( *bvpp, ber->ber_memctx ); + *bvpp = NULL; + break; + + case 'W': /* BerVarray */ + bvp = va_arg( ap, struct berval ** ); + ber_bvarray_free_x( *bvp, ber->ber_memctx ); + *bvp = NULL; + break; + + case 'n': /* null */ + case 'x': /* skip the next element - whatever it is */ + case '{': /* begin sequence */ + case '[': /* begin set */ + case '}': /* end sequence */ + case ']': /* end set */ + break; + + default: + /* format should be good */ + assert( 0 ); + } + } + + va_end( ap ); + } + + return rc; +} diff --git a/libraries/liblber/dtest.c b/libraries/liblber/dtest.c new file mode 100644 index 0000000..2595252 --- /dev/null +++ b/libraries/liblber/dtest.c @@ -0,0 +1,121 @@ +/* dtest.c - lber decoding test program */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ +/* ACKNOWLEDGEMENTS: + * This work was originally developed by the University of Michigan + * (as part of U-MICH LDAP). + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/socket.h> +#include <ac/unistd.h> +#include <ac/errno.h> + +#ifdef HAVE_CONSOLE_H +#include <console.h> +#endif + +#include <lber.h> + +static void usage( const char *name ) +{ + fprintf( stderr, "usage: %s fmt\n", name ); +} + +int +main( int argc, char **argv ) +{ + char *s; + + ber_tag_t tag; + ber_len_t len; + + BerElement *ber; + Sockbuf *sb; + int fd; + + /* enable debugging */ + int ival = -1; + ber_set_option( NULL, LBER_OPT_DEBUG_LEVEL, &ival ); + + if ( argc < 2 ) { + usage( argv[0] ); + return( EXIT_FAILURE ); + } + +#ifdef HAVE_CONSOLE_H + ccommand( &argv ); + cshow( stdout ); +#endif + + sb = ber_sockbuf_alloc(); + fd = fileno( stdin ); + ber_sockbuf_add_io( sb, &ber_sockbuf_io_fd, LBER_SBIOD_LEVEL_PROVIDER, + (void *)&fd ); + + ber = ber_alloc_t(LBER_USE_DER); + if( ber == NULL ) { + perror( "ber_alloc_t" ); + return( EXIT_FAILURE ); + } + + for (;;) { + tag = ber_get_next( sb, &len, ber); + if( tag != LBER_ERROR ) break; + + if( errno == EWOULDBLOCK ) continue; + if( errno == EAGAIN ) continue; + + perror( "ber_get_next" ); + return( EXIT_FAILURE ); + } + + printf("decode: message tag 0x%lx and length %ld\n", + (unsigned long) tag, (long) len ); + + for( s = argv[1]; *s; s++ ) { + char buf[128]; + char fmt[2]; + fmt[0] = *s; + fmt[1] = '\0'; + + printf("decode: format %s\n", fmt ); + len = sizeof(buf); + tag = ber_scanf( ber, fmt, &buf[0], &len ); + + if( tag == LBER_ERROR ) { + perror( "ber_scanf" ); + return( EXIT_FAILURE ); + } + } + + ber_sockbuf_free( sb ); + return( EXIT_SUCCESS ); +} diff --git a/libraries/liblber/encode.c b/libraries/liblber/encode.c new file mode 100644 index 0000000..7ce5901 --- /dev/null +++ b/libraries/liblber/encode.c @@ -0,0 +1,651 @@ +/* encode.c - ber output encoding routines */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ +/* ACKNOWLEDGEMENTS: + * This work was originally developed by the University of Michigan + * (as part of U-MICH LDAP). + */ + +#include "portable.h" + +#include <ctype.h> +#include <limits.h> +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/stdarg.h> +#include <ac/socket.h> +#include <ac/string.h> + +#include "lber-int.h" + + +#define OCTET_SIZE(type) ((ber_len_t) (sizeof(type)*CHAR_BIT + 7) / 8) +#define TAGBUF_SIZE OCTET_SIZE(ber_tag_t) +#define LENBUF_SIZE (1 + OCTET_SIZE(ber_len_t)) +#define HEADER_SIZE (TAGBUF_SIZE + LENBUF_SIZE) + +/* + * BER element size constrains: + * + * - We traditionally support a length of max 0xffffffff. However + * some functions return an int length so that is their max. + * MAXINT_BERSIZE is the max for those functions. + * + * - MAXINT_BERSIZE must fit in MAXINT_BERSIZE_OCTETS octets. + * + * - sizeof(ber_elem_size_t) is normally MAXINT_BERSIZE_OCTETS: + * Big enough for MAXINT_BERSIZE, but not more. (Larger wastes + * space in the working encoding and DER encoding of a sequence + * or set. Smaller further limits sizes near a sequence/set.) + * + * ber_len_t is mostly unrelated to this. Which may be for the best, + * since it is also used for lengths of data that are never encoded. + */ +#define MAXINT_BERSIZE \ + (INT_MAX>0xffffffffUL ? (ber_len_t) 0xffffffffUL : INT_MAX-HEADER_SIZE) +#define MAXINT_BERSIZE_OCTETS 4 +typedef ber_uint_t ber_elem_size_t; /* normally 32 bits */ + + +/* Prepend tag to ptr, which points to the end of a tag buffer */ +static unsigned char * +ber_prepend_tag( unsigned char *ptr, ber_tag_t tag ) +{ + do { + *--ptr = (unsigned char) tag & 0xffU; + } while ( (tag >>= 8) != 0 ); + + return ptr; +} + +/* Prepend ber length to ptr, which points to the end of a length buffer */ +static unsigned char * +ber_prepend_len( unsigned char *ptr, ber_len_t len ) +{ + /* + * short len if it's less than 128 - one byte giving the len, + * with bit 8 0. + * long len otherwise - one byte with bit 8 set, giving the + * length of the length, followed by the length itself. + */ + + *--ptr = (unsigned char) len & 0xffU; + + if ( len >= 0x80 ) { + unsigned char *endptr = ptr--; + + while ( (len >>= 8) != 0 ) { + *ptr-- = (unsigned char) len & 0xffU; + } + *ptr = (unsigned char) (endptr - ptr) + 0x80U; + } + + return ptr; +} + +/* out->bv_len should be the buffer size on input */ +int +ber_encode_oid( BerValue *in, BerValue *out ) +{ + unsigned char *der; + unsigned long val1, val; + int i, j, len; + char *ptr, *end, *inend; + + assert( in != NULL ); + assert( out != NULL ); + + if ( !out->bv_val || out->bv_len < in->bv_len/2 ) + return -1; + + der = (unsigned char *) out->bv_val; + ptr = in->bv_val; + inend = ptr + in->bv_len; + + /* OIDs start with <0-1>.<0-39> or 2.<any>, DER-encoded 40*val1+val2 */ + if ( !isdigit( (unsigned char) *ptr )) return -1; + val1 = strtoul( ptr, &end, 10 ); + if ( end == ptr || val1 > 2 ) return -1; + if ( *end++ != '.' || !isdigit( (unsigned char) *end )) return -1; + val = strtoul( end, &ptr, 10 ); + if ( ptr == end ) return -1; + if ( val > (val1 < 2 ? 39 : LBER_OID_COMPONENT_MAX - 80) ) return -1; + val += val1 * 40; + + for (;;) { + if ( ptr > inend ) return -1; + + /* Write the OID component little-endian, then reverse it */ + len = 0; + do { + der[len++] = (val & 0xff) | 0x80; + } while ( (val >>= 7) != 0 ); + der[0] &= 0x7f; + for ( i = 0, j = len; i < --j; i++ ) { + unsigned char tmp = der[i]; + der[i] = der[j]; + der[j] = tmp; + } + der += len; + + if ( ptr == inend ) + break; + + if ( *ptr++ != '.' ) return -1; + if ( !isdigit( (unsigned char) *ptr )) return -1; + val = strtoul( ptr, &end, 10 ); + if ( end == ptr || val > LBER_OID_COMPONENT_MAX ) return -1; + ptr = end; + } + + out->bv_len = (char *)der - out->bv_val; + return 0; +} + +static int +ber_put_int_or_enum( + BerElement *ber, + ber_int_t num, + ber_tag_t tag ) +{ + ber_uint_t unum; + unsigned char sign, data[TAGBUF_SIZE+1 + OCTET_SIZE(ber_int_t)], *ptr; + + sign = 0; + unum = num; /* Bit fiddling should be done with unsigned values */ + if ( num < 0 ) { + sign = 0xffU; + unum = ~unum; + } + for ( ptr = &data[sizeof(data) - 1] ;; unum >>= 8 ) { + *ptr-- = (sign ^ (unsigned char) unum) & 0xffU; + if ( unum < 0x80 ) /* top bit at *ptr is sign bit */ + break; + } + + *ptr = (unsigned char) (&data[sizeof(data) - 1] - ptr); /* length */ + ptr = ber_prepend_tag( ptr, tag ); + + return ber_write( ber, (char *) ptr, &data[sizeof(data)] - ptr, 0 ); +} + +int +ber_put_enum( + BerElement *ber, + ber_int_t num, + ber_tag_t tag ) +{ + if ( tag == LBER_DEFAULT ) { + tag = LBER_ENUMERATED; + } + + return ber_put_int_or_enum( ber, num, tag ); +} + +int +ber_put_int( + BerElement *ber, + ber_int_t num, + ber_tag_t tag ) +{ + if ( tag == LBER_DEFAULT ) { + tag = LBER_INTEGER; + } + + return ber_put_int_or_enum( ber, num, tag ); +} + +int +ber_put_ostring( + BerElement *ber, + LDAP_CONST char *str, + ber_len_t len, + ber_tag_t tag ) +{ + int rc; + unsigned char header[HEADER_SIZE], *ptr; + + if ( tag == LBER_DEFAULT ) { + tag = LBER_OCTETSTRING; + } + + if ( len > MAXINT_BERSIZE ) { + return -1; + } + + ptr = ber_prepend_len( &header[sizeof(header)], len ); + ptr = ber_prepend_tag( ptr, tag ); + + rc = ber_write( ber, (char *) ptr, &header[sizeof(header)] - ptr, 0 ); + if ( rc >= 0 && ber_write( ber, str, len, 0 ) >= 0 ) { + /* length(tag + length + contents) */ + return rc + (int) len; + } + + return -1; +} + +int +ber_put_berval( + BerElement *ber, + struct berval *bv, + ber_tag_t tag ) +{ + if( bv == NULL || bv->bv_len == 0 ) { + return ber_put_ostring( ber, "", (ber_len_t) 0, tag ); + } + + return ber_put_ostring( ber, bv->bv_val, bv->bv_len, tag ); +} + +int +ber_put_string( + BerElement *ber, + LDAP_CONST char *str, + ber_tag_t tag ) +{ + assert( str != NULL ); + + return ber_put_ostring( ber, str, strlen( str ), tag ); +} + +int +ber_put_bitstring( + BerElement *ber, + LDAP_CONST char *str, + ber_len_t blen /* in bits */, + ber_tag_t tag ) +{ + int rc; + ber_len_t len; + unsigned char unusedbits, header[HEADER_SIZE + 1], *ptr; + + if ( tag == LBER_DEFAULT ) { + tag = LBER_BITSTRING; + } + + unusedbits = (unsigned char) -blen & 7; + len = blen / 8 + (unusedbits != 0); /* (blen+7)/8 without overflow */ + if ( len >= MAXINT_BERSIZE ) { + return -1; + } + + header[sizeof(header) - 1] = unusedbits; + ptr = ber_prepend_len( &header[sizeof(header) - 1], len + 1 ); + ptr = ber_prepend_tag( ptr, tag ); + + rc = ber_write( ber, (char *) ptr, &header[sizeof(header)] - ptr, 0 ); + if ( rc >= 0 && ber_write( ber, str, len, 0 ) >= 0 ) { + /* length(tag + length + unused bit count + bitstring) */ + return rc + (int) len; + } + + return -1; +} + +int +ber_put_null( BerElement *ber, ber_tag_t tag ) +{ + unsigned char data[TAGBUF_SIZE + 1], *ptr; + + if ( tag == LBER_DEFAULT ) { + tag = LBER_NULL; + } + + data[sizeof(data) - 1] = 0; /* length */ + ptr = ber_prepend_tag( &data[sizeof(data) - 1], tag ); + + return ber_write( ber, (char *) ptr, &data[sizeof(data)] - ptr, 0 ); +} + +int +ber_put_boolean( + BerElement *ber, + ber_int_t boolval, + ber_tag_t tag ) +{ + unsigned char data[TAGBUF_SIZE + 2], *ptr; + + if ( tag == LBER_DEFAULT ) + tag = LBER_BOOLEAN; + + data[sizeof(data) - 1] = boolval ? 0xff : 0; + data[sizeof(data) - 2] = 1; /* length */ + ptr = ber_prepend_tag( &data[sizeof(data) - 2], tag ); + + return ber_write( ber, (char *) ptr, &data[sizeof(data)] - ptr, 0 ); +} + + +/* Max number of length octets in a sequence or set, normally 5 */ +#define SOS_LENLEN (1 + (sizeof(ber_elem_size_t) > MAXINT_BERSIZE_OCTETS ? \ + (ber_len_t) sizeof(ber_elem_size_t) : MAXINT_BERSIZE_OCTETS)) + +/* Header of incomplete sequence or set */ +typedef struct seqorset_header { + char xtagbuf[TAGBUF_SIZE + 1]; /* room for tag + len(tag or len) */ + union { + ber_elem_size_t offset; /* enclosing seqence/set */ + char padding[SOS_LENLEN-1]; /* for final length encoding */ + } next_sos; +# define SOS_TAG_END(header) ((unsigned char *) &(header).next_sos - 1) +} Seqorset_header; + +/* Start a sequence or set */ +static int +ber_start_seqorset( + BerElement *ber, + ber_tag_t tag ) +{ + /* + * Write the tag and SOS_LENLEN octets reserved for length, to ber. + * For now, length octets = (tag length, previous ber_sos_inner). + * + * Update ber_sos_inner and the write-cursor ber_sos_ptr. ber_ptr + * will not move until the outermost sequence or set is complete. + */ + + Seqorset_header header; + unsigned char *headptr; + ber_len_t taglen, headlen; + char *dest, **p; + + assert( ber != NULL ); + assert( LBER_VALID( ber ) ); + + if ( ber->ber_sos_ptr == NULL ) { /* outermost sequence/set? */ + header.next_sos.offset = 0; + p = &ber->ber_ptr; + } else { + if ( (ber_len_t) -1 > (ber_elem_size_t) -1 ) { + if ( ber->ber_sos_inner > (ber_elem_size_t) -1 ) + return -1; + } + header.next_sos.offset = ber->ber_sos_inner; + p = &ber->ber_sos_ptr; + } + headptr = ber_prepend_tag( SOS_TAG_END(header), tag ); + *SOS_TAG_END(header) = taglen = SOS_TAG_END(header) - headptr; + headlen = taglen + SOS_LENLEN; + + /* As ber_write(,headptr,headlen,) except update ber_sos_ptr, not *p */ + if ( headlen > (ber_len_t) (ber->ber_end - *p) ) { + if ( ber_realloc( ber, headlen ) != 0 ) + return -1; + } + dest = *p; + AC_MEMCPY( dest, headptr, headlen ); + ber->ber_sos_ptr = dest + headlen; + + ber->ber_sos_inner = dest + taglen - ber->ber_buf; + + /* + * Do not return taglen + SOS_LENLEN here - then ber_put_seqorset() + * should return lenlen - SOS_LENLEN + len, which can be < 0. + */ + return 0; +} + +int +ber_start_seq( BerElement *ber, ber_tag_t tag ) +{ + if ( tag == LBER_DEFAULT ) { + tag = LBER_SEQUENCE; + } + + return ber_start_seqorset( ber, tag ); +} + +int +ber_start_set( BerElement *ber, ber_tag_t tag ) +{ + if ( tag == LBER_DEFAULT ) { + tag = LBER_SET; + } + + return ber_start_seqorset( ber, tag ); +} + +/* End a sequence or set */ +static int +ber_put_seqorset( BerElement *ber ) +{ + Seqorset_header header; + unsigned char *lenptr; /* length octets in the sequence/set */ + ber_len_t len; /* length(contents) */ + ber_len_t xlen; /* len + length(length) */ + + assert( ber != NULL ); + assert( LBER_VALID( ber ) ); + + if ( ber->ber_sos_ptr == NULL ) return -1; + + lenptr = (unsigned char *) ber->ber_buf + ber->ber_sos_inner; + xlen = ber->ber_sos_ptr - (char *) lenptr; + if ( xlen > MAXINT_BERSIZE + SOS_LENLEN ) { + return -1; + } + + /* Extract sequence/set information from length octets */ + memcpy( SOS_TAG_END(header), lenptr, SOS_LENLEN ); + + /* Store length, and close gap of leftover reserved length octets */ + len = xlen - SOS_LENLEN; + if ( !(ber->ber_options & LBER_USE_DER) ) { + int i; + lenptr[0] = SOS_LENLEN - 1 + 0x80; /* length(length)-1 */ + for( i = SOS_LENLEN; --i > 0; len >>= 8 ) { + lenptr[i] = len & 0xffU; + } + } else { + unsigned char *p = ber_prepend_len( lenptr + SOS_LENLEN, len ); + ber_len_t unused = p - lenptr; + if ( unused != 0 ) { + /* length(length) < the reserved SOS_LENLEN bytes */ + xlen -= unused; + AC_MEMCPY( lenptr, p, xlen ); + ber->ber_sos_ptr = (char *) lenptr + xlen; + } + } + + ber->ber_sos_inner = header.next_sos.offset; + if ( header.next_sos.offset == 0 ) { /* outermost sequence/set? */ + /* The ber_ptr is at the set/seq start - move it to the end */ + ber->ber_ptr = ber->ber_sos_ptr; + ber->ber_sos_ptr = NULL; + } + + return xlen + *SOS_TAG_END(header); /* lenlen + len + taglen */ +} + +int +ber_put_seq( BerElement *ber ) +{ + return ber_put_seqorset( ber ); +} + +int +ber_put_set( BerElement *ber ) +{ + return ber_put_seqorset( ber ); +} + +/* N tag */ +static ber_tag_t lber_int_null = 0; + +/* VARARGS */ +int +ber_printf( BerElement *ber, LDAP_CONST char *fmt, ... ) +{ + va_list ap; + char *s, **ss; + struct berval *bv, **bvp; + int rc; + ber_int_t i; + ber_len_t len; + + assert( ber != NULL ); + assert( fmt != NULL ); + assert( LBER_VALID( ber ) ); + + va_start( ap, fmt ); + + for ( rc = 0; *fmt && rc != -1; fmt++ ) { + switch ( *fmt ) { + case '!': { /* hook */ + BEREncodeCallback *f; + void *p; + + ber->ber_usertag = 0; + + f = va_arg( ap, BEREncodeCallback * ); + p = va_arg( ap, void * ); + rc = (*f)( ber, p ); + + if ( ber->ber_usertag ) { + goto next; + } + } break; + + case 'b': /* boolean */ + i = va_arg( ap, ber_int_t ); + rc = ber_put_boolean( ber, i, ber->ber_tag ); + break; + + case 'i': /* int */ + i = va_arg( ap, ber_int_t ); + rc = ber_put_int( ber, i, ber->ber_tag ); + break; + + case 'e': /* enumeration */ + i = va_arg( ap, ber_int_t ); + rc = ber_put_enum( ber, i, ber->ber_tag ); + break; + + case 'n': /* null */ + rc = ber_put_null( ber, ber->ber_tag ); + break; + + case 'N': /* Debug NULL */ + rc = 0; + if( lber_int_null != 0 ) { + /* Insert NULL to ensure peer ignores unknown tags */ + rc = ber_put_null( ber, lber_int_null ); + } + break; + + case 'o': /* octet string (non-null terminated) */ + s = va_arg( ap, char * ); + len = va_arg( ap, ber_len_t ); + rc = ber_put_ostring( ber, s, len, ber->ber_tag ); + break; + + case 'O': /* berval octet string */ + bv = va_arg( ap, struct berval * ); + if( bv == NULL ) break; + rc = ber_put_berval( ber, bv, ber->ber_tag ); + break; + + case 's': /* string */ + s = va_arg( ap, char * ); + rc = ber_put_string( ber, s, ber->ber_tag ); + break; + + case 'B': /* bit string */ + case 'X': /* bit string (deprecated) */ + s = va_arg( ap, char * ); + len = va_arg( ap, ber_len_t ); /* in bits */ + rc = ber_put_bitstring( ber, s, len, ber->ber_tag ); + break; + + case 't': /* tag for the next element */ + ber->ber_tag = va_arg( ap, ber_tag_t ); + goto next; + + case 'v': /* vector of strings */ + if ( (ss = va_arg( ap, char ** )) == NULL ) + break; + for ( i = 0; ss[i] != NULL; i++ ) { + if ( (rc = ber_put_string( ber, ss[i], + ber->ber_tag )) == -1 ) + break; + } + break; + + case 'V': /* sequences of strings + lengths */ + if ( (bvp = va_arg( ap, struct berval ** )) == NULL ) + break; + for ( i = 0; bvp[i] != NULL; i++ ) { + if ( (rc = ber_put_berval( ber, bvp[i], + ber->ber_tag )) == -1 ) + break; + } + break; + + case 'W': /* BerVarray */ + if ( (bv = va_arg( ap, BerVarray )) == NULL ) + break; + for ( i = 0; bv[i].bv_val != NULL; i++ ) { + if ( (rc = ber_put_berval( ber, &bv[i], + ber->ber_tag )) == -1 ) + break; + } + break; + + case '{': /* begin sequence */ + rc = ber_start_seq( ber, ber->ber_tag ); + break; + + case '}': /* end sequence */ + rc = ber_put_seqorset( ber ); + break; + + case '[': /* begin set */ + rc = ber_start_set( ber, ber->ber_tag ); + break; + + case ']': /* end set */ + rc = ber_put_seqorset( ber ); + break; + + default: + if( ber->ber_debug ) { + ber_log_printf( LDAP_DEBUG_ANY, ber->ber_debug, + "ber_printf: unknown fmt %c\n", *fmt ); + } + rc = -1; + break; + } + + ber->ber_tag = LBER_DEFAULT; + next:; + } + + va_end( ap ); + + return rc; +} diff --git a/libraries/liblber/etest.c b/libraries/liblber/etest.c new file mode 100644 index 0000000..e34cb92 --- /dev/null +++ b/libraries/liblber/etest.c @@ -0,0 +1,181 @@ +/* etest.c - lber encoding test program */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ +/* ACKNOWLEDGEMENTS: + * This work was originally developed by the University of Michigan + * (as part of U-MICH LDAP). + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/unistd.h> + +#ifdef HAVE_CONSOLE_H +#include <console.h> +#endif /* HAVE_CONSOLE_H */ + +#include "lber.h" + +static void usage( const char *name ) +{ + fprintf( stderr, "usage: %s fmtstring\n", name ); +} + +static char* getbuf( void ) { + char *p; + static char buf[1024]; + + if ( fgets( buf, sizeof(buf), stdin ) == NULL ) return NULL; + + if ( (p = strchr( buf, '\n' )) != NULL ) *p = '\0'; + + return buf; +} + +int +main( int argc, char **argv ) +{ + char *s; + int tag; + + int fd, rc; + BerElement *ber; + Sockbuf *sb; + + /* enable debugging */ + int ival = -1; + ber_set_option( NULL, LBER_OPT_DEBUG_LEVEL, &ival ); + + if ( argc < 2 ) { + usage( argv[0] ); + return( EXIT_FAILURE ); + } + +#ifdef HAVE_CONSOLE_H + ccommand( &argv ); + cshow( stdout ); + + if (( fd = open( "lber-test", O_WRONLY|O_CREAT|O_TRUNC|O_BINARY )) + < 0 ) { + perror( "open" ); + return( EXIT_FAILURE ); + } + +#else + fd = fileno(stdout); +#endif + + sb = ber_sockbuf_alloc(); + ber_sockbuf_add_io( sb, &ber_sockbuf_io_fd, LBER_SBIOD_LEVEL_PROVIDER, + (void *)&fd ); + + if( sb == NULL ) { + perror( "ber_sockbuf_alloc_fd" ); + return( EXIT_FAILURE ); + } + + if ( (ber = ber_alloc_t( LBER_USE_DER )) == NULL ) { + perror( "ber_alloc" ); + return( EXIT_FAILURE ); + } + + fprintf(stderr, "encode: start\n" ); + if( ber_printf( ber, "{" /*}*/ ) ) { + perror( "ber_printf {" /*}*/ ); + return( EXIT_FAILURE ); + } + + for ( s = argv[1]; *s; s++ ) { + char *buf; + char fmt[2]; + + fmt[0] = *s; + fmt[1] = '\0'; + + fprintf(stderr, "encode: %s\n", fmt ); + switch ( *s ) { + case 'i': /* int */ + case 'b': /* boolean */ + case 'e': /* enumeration */ + buf = getbuf(); + rc = ber_printf( ber, fmt, atoi(buf) ); + break; + + case 'n': /* null */ + case '{': /* begin sequence */ + case '}': /* end sequence */ + case '[': /* begin set */ + case ']': /* end set */ + rc = ber_printf( ber, fmt ); + break; + + case 'o': /* octet string (non-null terminated) */ + case 'B': /* bit string */ + buf = getbuf(); + rc = ber_printf( ber, fmt, buf, strlen(buf) ); + break; + + case 's': /* string */ + buf = getbuf(); + rc = ber_printf( ber, fmt, buf ); + break; + case 't': /* tag for the next element */ + buf = getbuf(); + tag = atoi(buf); + rc = ber_printf( ber, fmt, tag ); + break; + + default: + fprintf( stderr, "encode: unknown fmt %c\n", *fmt ); + rc = -1; + break; + } + + if( rc == -1 ) { + perror( "ber_printf" ); + return( EXIT_FAILURE ); + } + } + + fprintf(stderr, "encode: end\n" ); + if( ber_printf( ber, /*{*/ "N}" ) == -1 ) { + perror( /*{*/ "ber_printf }" ); + return( EXIT_FAILURE ); + } + + if ( ber_flush2( sb, ber, LBER_FLUSH_FREE_ALWAYS ) == -1 ) { + perror( "ber_flush2" ); + return( EXIT_FAILURE ); + } + + ber_sockbuf_free( sb ); + return( EXIT_SUCCESS ); +} diff --git a/libraries/liblber/idtest.c b/libraries/liblber/idtest.c new file mode 100644 index 0000000..9f0522d --- /dev/null +++ b/libraries/liblber/idtest.c @@ -0,0 +1,87 @@ +/* idtest.c - ber decoding test program using isode libraries */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ +/* ACKNOWLEDGEMENTS: + * This work was originally developed by the University of Michigan + * (as part of U-MICH LDAP). + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#ifdef HAVE_PSAP_H +#include <psap.h> +#include <quipu/attr.h> +#endif + +int +main( int argc, char **argv ) +{ +#ifdef HAVE_PSAP_H + PE pe; + PS psin, psout, pserr; + + /* read the pe from standard in */ + if ( (psin = ps_alloc( std_open )) == NULLPS ) { + perror( "ps_alloc" ); + exit( EXIT_FAILURE ); + } + if ( std_setup( psin, stdin ) == NOTOK ) { + perror( "std_setup" ); + exit( EXIT_FAILURE ); + } + /* write the pe to standard out */ + if ( (psout = ps_alloc( std_open )) == NULLPS ) { + perror( "ps_alloc" ); + exit( EXIT_FAILURE ); + } + if ( std_setup( psout, stdout ) == NOTOK ) { + perror( "std_setup" ); + exit( EXIT_FAILURE ); + } + /* pretty print it to standard error */ + if ( (pserr = ps_alloc( std_open )) == NULLPS ) { + perror( "ps_alloc" ); + exit( EXIT_FAILURE ); + } + if ( std_setup( pserr, stderr ) == NOTOK ) { + perror( "std_setup" ); + exit( EXIT_FAILURE ); + } + + while ( (pe = ps2pe( psin )) != NULLPE ) { + pe2pl( pserr, pe ); + pe2ps( psout, pe ); + } + + exit( EXIT_SUCCESS ); +#else + fprintf(stderr, "requires ISODE X.500 distribution.\n"); + return( EXIT_FAILURE ); +#endif +} diff --git a/libraries/liblber/io.c b/libraries/liblber/io.c new file mode 100644 index 0000000..65f3cba --- /dev/null +++ b/libraries/liblber/io.c @@ -0,0 +1,725 @@ +/* io.c - ber general i/o routines */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ +/* ACKNOWLEDGEMENTS: + * This work was originally developed by the University of Michigan + * (as part of U-MICH LDAP). + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/ctype.h> +#include <ac/errno.h> +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/unistd.h> + +#ifdef HAVE_IO_H +#include <io.h> +#endif + +#include "lber-int.h" +#include "ldap_log.h" + +ber_slen_t +ber_skip_data( + BerElement *ber, + ber_len_t len ) +{ + ber_len_t actuallen, nleft; + + assert( ber != NULL ); + assert( LBER_VALID( ber ) ); + + nleft = ber_pvt_ber_remaining( ber ); + actuallen = nleft < len ? nleft : len; + ber->ber_ptr += actuallen; + ber->ber_tag = *(unsigned char *)ber->ber_ptr; + + return( (ber_slen_t) actuallen ); +} + +/* + * Read from the ber buffer. The caller must maintain ber->ber_tag. + * Do not use to read whole tags. See ber_get_tag() and ber_skip_data(). + */ +ber_slen_t +ber_read( + BerElement *ber, + char *buf, + ber_len_t len ) +{ + ber_len_t actuallen, nleft; + + assert( ber != NULL ); + assert( buf != NULL ); + assert( LBER_VALID( ber ) ); + + nleft = ber_pvt_ber_remaining( ber ); + actuallen = nleft < len ? nleft : len; + + AC_MEMCPY( buf, ber->ber_ptr, actuallen ); + + ber->ber_ptr += actuallen; + + return( (ber_slen_t) actuallen ); +} + +/* + * Write to the ber buffer. + * Note that ber_start_seqorset/ber_put_seqorset() bypass ber_write(). + */ +ber_slen_t +ber_write( + BerElement *ber, + LDAP_CONST char *buf, + ber_len_t len, + int zero ) /* nonzero is unsupported from OpenLDAP 2.4.18 */ +{ + char **p; + + assert( ber != NULL ); + assert( buf != NULL ); + assert( LBER_VALID( ber ) ); + + if ( zero != 0 ) { + ber_log_printf( LDAP_DEBUG_ANY, ber->ber_debug, "%s", + "ber_write: nonzero 4th argument not supported\n" ); + return( -1 ); + } + + p = ber->ber_sos_ptr == NULL ? &ber->ber_ptr : &ber->ber_sos_ptr; + if ( len > (ber_len_t) (ber->ber_end - *p) ) { + if ( ber_realloc( ber, len ) != 0 ) return( -1 ); + } + AC_MEMCPY( *p, buf, len ); + *p += len; + + return( (ber_slen_t) len ); +} + +/* Resize the ber buffer */ +int +ber_realloc( BerElement *ber, ber_len_t len ) +{ + ber_len_t total, offset, sos_offset, rw_offset; + char *buf; + + assert( ber != NULL ); + assert( LBER_VALID( ber ) ); + + /* leave room for ber_flatten() to \0-terminate ber_buf */ + if ( ++len == 0 ) { + return( -1 ); + } + + total = ber_pvt_ber_total( ber ); + +#define LBER_EXBUFSIZ 4060 /* a few words less than 2^N for binary buddy */ +#if defined( LBER_EXBUFSIZ ) && LBER_EXBUFSIZ > 0 +# ifndef notdef + /* don't realloc by small amounts */ + total += len < LBER_EXBUFSIZ ? LBER_EXBUFSIZ : len; +# else + { /* not sure what value this adds. reduce fragmentation? */ + ber_len_t have = (total + (LBER_EXBUFSIZE - 1)) / LBER_EXBUFSIZ; + ber_len_t need = (len + (LBER_EXBUFSIZ - 1)) / LBER_EXBUFSIZ; + total = ( have + need ) * LBER_EXBUFSIZ; + } +# endif +#else + total += len; /* realloc just what's needed */ +#endif + + if ( total < len || total > (ber_len_t)-1 / 2 /* max ber_slen_t */ ) { + return( -1 ); + } + + buf = ber->ber_buf; + offset = ber->ber_ptr - buf; + sos_offset = ber->ber_sos_ptr ? ber->ber_sos_ptr - buf : 0; + /* if ber_sos_ptr != NULL, it is > ber_buf so that sos_offset > 0 */ + rw_offset = ber->ber_rwptr ? ber->ber_rwptr - buf : 0; + + buf = (char *) ber_memrealloc_x( buf, total, ber->ber_memctx ); + if ( buf == NULL ) { + return( -1 ); + } + + ber->ber_buf = buf; + ber->ber_end = buf + total; + ber->ber_ptr = buf + offset; + if ( sos_offset ) + ber->ber_sos_ptr = buf + sos_offset; + if ( ber->ber_rwptr ) + ber->ber_rwptr = buf + rw_offset; + + return( 0 ); +} + +void +ber_free_buf( BerElement *ber ) +{ + assert( LBER_VALID( ber ) ); + + if ( ber->ber_buf) ber_memfree_x( ber->ber_buf, ber->ber_memctx ); + + ber->ber_buf = NULL; + ber->ber_sos_ptr = NULL; + ber->ber_valid = LBER_UNINITIALIZED; +} + +void +ber_free( BerElement *ber, int freebuf ) +{ + if( ber == NULL ) { + LDAP_MEMORY_DEBUG_ASSERT( ber != NULL ); + return; + } + + if( freebuf ) ber_free_buf( ber ); + + ber_memfree_x( (char *) ber, ber->ber_memctx ); +} + +int +ber_flush( Sockbuf *sb, BerElement *ber, int freeit ) +{ + return ber_flush2( sb, ber, + freeit ? LBER_FLUSH_FREE_ON_SUCCESS + : LBER_FLUSH_FREE_NEVER ); +} + +int +ber_flush2( Sockbuf *sb, BerElement *ber, int freeit ) +{ + ber_len_t towrite; + ber_slen_t rc; + + assert( sb != NULL ); + assert( ber != NULL ); + assert( SOCKBUF_VALID( sb ) ); + assert( LBER_VALID( ber ) ); + + if ( ber->ber_rwptr == NULL ) { + ber->ber_rwptr = ber->ber_buf; + } + towrite = ber->ber_ptr - ber->ber_rwptr; + + if ( sb->sb_debug ) { + ber_log_printf( LDAP_DEBUG_TRACE, sb->sb_debug, + "ber_flush2: %ld bytes to sd %ld%s\n", + towrite, (long) sb->sb_fd, + ber->ber_rwptr != ber->ber_buf ? " (re-flush)" : "" ); + ber_log_bprint( LDAP_DEBUG_BER, sb->sb_debug, + ber->ber_rwptr, towrite ); + } + + while ( towrite > 0 ) { +#ifdef LBER_TRICKLE + sleep(1); + rc = ber_int_sb_write( sb, ber->ber_rwptr, 1 ); +#else + rc = ber_int_sb_write( sb, ber->ber_rwptr, towrite ); +#endif + if ( rc <= 0 ) { + if ( freeit & LBER_FLUSH_FREE_ON_ERROR ) ber_free( ber, 1 ); + return -1; + } + towrite -= rc; + ber->ber_rwptr += rc; + } + + if ( freeit & LBER_FLUSH_FREE_ON_SUCCESS ) ber_free( ber, 1 ); + + return 0; +} + +BerElement * +ber_alloc_t( int options ) +{ + BerElement *ber; + + ber = (BerElement *) LBER_CALLOC( 1, sizeof(BerElement) ); + + if ( ber == NULL ) { + return NULL; + } + + ber->ber_valid = LBER_VALID_BERELEMENT; + ber->ber_tag = LBER_DEFAULT; + ber->ber_options = options; + ber->ber_debug = ber_int_debug; + + assert( LBER_VALID( ber ) ); + return ber; +} + +BerElement * +ber_alloc( void ) /* deprecated */ +{ + return ber_alloc_t( 0 ); +} + +BerElement * +der_alloc( void ) /* deprecated */ +{ + return ber_alloc_t( LBER_USE_DER ); +} + +BerElement * +ber_dup( BerElement *ber ) +{ + BerElement *new; + + assert( ber != NULL ); + assert( LBER_VALID( ber ) ); + + if ( (new = ber_alloc_t( ber->ber_options )) == NULL ) { + return NULL; + } + + *new = *ber; + + assert( LBER_VALID( new ) ); + return( new ); +} + + +void +ber_init2( BerElement *ber, struct berval *bv, int options ) +{ + assert( ber != NULL ); + + (void) memset( (char *)ber, '\0', sizeof( BerElement )); + ber->ber_valid = LBER_VALID_BERELEMENT; + ber->ber_tag = LBER_DEFAULT; + ber->ber_options = (char) options; + ber->ber_debug = ber_int_debug; + + if ( bv != NULL ) { + ber->ber_buf = bv->bv_val; + ber->ber_ptr = ber->ber_buf; + ber->ber_end = ber->ber_buf + bv->bv_len; + } + + assert( LBER_VALID( ber ) ); +} + +/* OLD U-Mich ber_init() */ +void +ber_init_w_nullc( BerElement *ber, int options ) +{ + ber_init2( ber, NULL, options ); +} + +/* New C-API ber_init() */ +/* This function constructs a BerElement containing a copy +** of the data in the bv argument. +*/ +BerElement * +ber_init( struct berval *bv ) +{ + BerElement *ber; + + assert( bv != NULL ); + + if ( bv == NULL ) { + return NULL; + } + + ber = ber_alloc_t( 0 ); + + if( ber == NULL ) { + /* allocation failed */ + return NULL; + } + + /* copy the data */ + if ( ((ber_len_t) ber_write ( ber, bv->bv_val, bv->bv_len, 0 )) + != bv->bv_len ) + { + /* write failed, so free and return NULL */ + ber_free( ber, 1 ); + return NULL; + } + + ber_reset( ber, 1 ); /* reset the pointer to the start of the buffer */ + return ber; +} + +/* New C-API ber_flatten routine */ +/* This routine allocates a struct berval whose contents are a BER +** encoding taken from the ber argument. The bvPtr pointer points to +** the returned berval. +** +** ber_flatten2 is the same, but uses a struct berval passed by +** the caller. If alloc is 0 the returned bv uses the ber buf directly. +*/ +int ber_flatten2( + BerElement *ber, + struct berval *bv, + int alloc ) +{ + assert( bv != NULL ); + + if ( bv == NULL ) { + return -1; + } + + if ( ber == NULL ) { + /* ber is null, create an empty berval */ + bv->bv_val = NULL; + bv->bv_len = 0; + + } else if ( ber->ber_sos_ptr != NULL ) { + /* unmatched "{" and "}" */ + return -1; + + } else { + /* copy the berval */ + ber_len_t len = ber_pvt_ber_write( ber ); + + if ( alloc ) { + bv->bv_val = (char *) ber_memalloc_x( len + 1, ber->ber_memctx ); + if ( bv->bv_val == NULL ) { + return -1; + } + AC_MEMCPY( bv->bv_val, ber->ber_buf, len ); + bv->bv_val[len] = '\0'; + } else if ( ber->ber_buf != NULL ) { + bv->bv_val = ber->ber_buf; + bv->bv_val[len] = '\0'; + } else { + bv->bv_val = ""; + } + bv->bv_len = len; + } + return 0; +} + +int ber_flatten( + BerElement *ber, + struct berval **bvPtr) +{ + struct berval *bv; + int rc; + + assert( bvPtr != NULL ); + + if(bvPtr == NULL) { + return -1; + } + + bv = ber_memalloc_x( sizeof(struct berval), ber->ber_memctx ); + if ( bv == NULL ) { + return -1; + } + rc = ber_flatten2(ber, bv, 1); + if (rc == -1) { + ber_memfree_x(bv, ber->ber_memctx); + } else { + *bvPtr = bv; + } + return rc; +} + +void +ber_reset( BerElement *ber, int was_writing ) +{ + assert( ber != NULL ); + assert( LBER_VALID( ber ) ); + + if ( was_writing ) { + ber->ber_end = ber->ber_ptr; + ber->ber_ptr = ber->ber_buf; + + } else { + ber->ber_ptr = ber->ber_end; + } + + ber->ber_rwptr = NULL; +} + +/* + * A rewrite of ber_get_next that can safely be called multiple times + * for the same packet. It will simply continue where it stopped until + * a full packet is read. + */ + +#define LENSIZE 4 + +ber_tag_t +ber_get_next( + Sockbuf *sb, + ber_len_t *len, + BerElement *ber ) +{ + assert( sb != NULL ); + assert( len != NULL ); + assert( ber != NULL ); + assert( SOCKBUF_VALID( sb ) ); + assert( LBER_VALID( ber ) ); + + if ( ber->ber_debug & LDAP_DEBUG_TRACE ) { + ber_log_printf( LDAP_DEBUG_TRACE, ber->ber_debug, + "ber_get_next\n" ); + } + + /* + * Any ber element looks like this: tag length contents. + * Assuming everything's ok, we return the tag byte (we + * can assume a single byte), return the length in len, + * and the rest of the undecoded element in buf. + * + * Assumptions: + * 1) small tags (less than 128) + * 2) definite lengths + * 3) primitive encodings used whenever possible + * + * The code also handles multi-byte tags. The first few bytes + * of the message are read to check for multi-byte tags and + * lengths. These bytes are temporarily stored in the ber_tag, + * ber_len, and ber_usertag fields of the berelement until + * tag/len parsing is complete. After this parsing, any leftover + * bytes and the rest of the message are copied into the ber_buf. + * + * We expect tag and len to be at most 32 bits wide. + */ + + if (ber->ber_rwptr == NULL) { + assert( ber->ber_buf == NULL ); + ber->ber_rwptr = (char *) &ber->ber_len-1; + ber->ber_ptr = ber->ber_rwptr; + ber->ber_tag = 0; + } + + while (ber->ber_rwptr > (char *)&ber->ber_tag && ber->ber_rwptr < + (char *)&ber->ber_len + LENSIZE*2) { + ber_slen_t sblen; + char buf[sizeof(ber->ber_len)-1]; + ber_len_t tlen = 0; + + /* The tag & len can be at most 9 bytes; we try to read up to 8 here */ + sock_errset(0); + sblen=((char *)&ber->ber_len + LENSIZE*2 - 1)-ber->ber_rwptr; + /* Trying to read the last len byte of a 9 byte tag+len */ + if (sblen<1) + sblen = 1; + sblen=ber_int_sb_read( sb, ber->ber_rwptr, sblen ); + if (sblen<=0) return LBER_DEFAULT; + ber->ber_rwptr += sblen; + + /* We got at least one byte, try to parse the tag. */ + if (ber->ber_ptr == (char *)&ber->ber_len-1) { + ber_tag_t tag; + unsigned char *p = (unsigned char *)ber->ber_ptr; + tag = *p++; + if ((tag & LBER_BIG_TAG_MASK) == LBER_BIG_TAG_MASK) { + ber_len_t i; + for (i=1; (char *)p<ber->ber_rwptr; i++) { + tag <<= 8; + tag |= *p++; + if (!(tag & LBER_MORE_TAG_MASK)) + break; + /* Is the tag too big? */ + if (i == sizeof(ber_tag_t)-1) { + sock_errset(ERANGE); + return LBER_DEFAULT; + } + } + /* Did we run out of bytes? */ + if ((char *)p == ber->ber_rwptr) { + sock_errset(EWOULDBLOCK); + return LBER_DEFAULT; + } + } + ber->ber_tag = tag; + ber->ber_ptr = (char *)p; + } + + if ( ber->ber_ptr == ber->ber_rwptr ) { + sock_errset(EWOULDBLOCK); + return LBER_DEFAULT; + } + + /* Now look for the length */ + if (*ber->ber_ptr & 0x80) { /* multi-byte */ + int i; + unsigned char *p = (unsigned char *)ber->ber_ptr; + int llen = *p++ & 0x7f; + if (llen > LENSIZE) { + sock_errset(ERANGE); + return LBER_DEFAULT; + } + /* Not enough bytes? */ + if (ber->ber_rwptr - (char *)p < llen) { + sock_errset(EWOULDBLOCK); + return LBER_DEFAULT; + } + for (i=0; i<llen; i++) { + tlen <<=8; + tlen |= *p++; + } + ber->ber_ptr = (char *)p; + } else { + tlen = *(unsigned char *)ber->ber_ptr++; + } + + /* Are there leftover data bytes inside ber->ber_len? */ + if (ber->ber_ptr < (char *)&ber->ber_usertag) { + if (ber->ber_rwptr < (char *)&ber->ber_usertag) { + sblen = ber->ber_rwptr - ber->ber_ptr; + } else { + sblen = (char *)&ber->ber_usertag - ber->ber_ptr; + } + AC_MEMCPY(buf, ber->ber_ptr, sblen); + ber->ber_ptr += sblen; + } else { + sblen = 0; + } + ber->ber_len = tlen; + + /* now fill the buffer. */ + + /* make sure length is reasonable */ + if ( ber->ber_len == 0 ) { + sock_errset(ERANGE); + return LBER_DEFAULT; + } + + if ( sb->sb_max_incoming && ber->ber_len > sb->sb_max_incoming ) { + ber_log_printf( LDAP_DEBUG_CONNS, ber->ber_debug, + "ber_get_next: sockbuf_max_incoming exceeded " + "(%ld > %ld)\n", ber->ber_len, sb->sb_max_incoming ); + sock_errset(ERANGE); + return LBER_DEFAULT; + } + + if (ber->ber_buf==NULL) { + ber_len_t l = ber->ber_rwptr - ber->ber_ptr; + /* ber->ber_ptr is always <= ber->ber->ber_rwptr. + * make sure ber->ber_len agrees with what we've + * already read. + */ + if ( ber->ber_len < sblen + l ) { + sock_errset(ERANGE); + return LBER_DEFAULT; + } + ber->ber_buf = (char *) ber_memalloc_x( ber->ber_len + 1, ber->ber_memctx ); + if (ber->ber_buf==NULL) { + return LBER_DEFAULT; + } + ber->ber_end = ber->ber_buf + ber->ber_len; + if (sblen) { + AC_MEMCPY(ber->ber_buf, buf, sblen); + } + if (l > 0) { + AC_MEMCPY(ber->ber_buf + sblen, ber->ber_ptr, l); + sblen += l; + } + *ber->ber_end = '\0'; + ber->ber_ptr = ber->ber_buf; + ber->ber_usertag = 0; + if ((ber_len_t)sblen == ber->ber_len) { + goto done; + } + ber->ber_rwptr = ber->ber_buf + sblen; + } + } + + if ((ber->ber_rwptr>=ber->ber_buf) && (ber->ber_rwptr<ber->ber_end)) { + ber_slen_t res; + ber_slen_t to_go; + + to_go = ber->ber_end - ber->ber_rwptr; + /* unsigned/signed overflow */ + if (to_go<0) return LBER_DEFAULT; + + sock_errset(0); + res = ber_int_sb_read( sb, ber->ber_rwptr, to_go ); + if (res<=0) return LBER_DEFAULT; + ber->ber_rwptr+=res; + + if (res<to_go) { + sock_errset(EWOULDBLOCK); + return LBER_DEFAULT; + } +done: + ber->ber_rwptr = NULL; + *len = ber->ber_len; + if ( ber->ber_debug ) { + ber_log_printf( LDAP_DEBUG_TRACE, ber->ber_debug, + "ber_get_next: tag 0x%lx len %ld contents:\n", + ber->ber_tag, ber->ber_len ); + ber_log_dump( LDAP_DEBUG_BER, ber->ber_debug, ber, 1 ); + } + return (ber->ber_tag); + } + + /* invalid input */ + return LBER_DEFAULT; +} + +char * +ber_start( BerElement* ber ) +{ + return ber->ber_buf; +} + +int +ber_len( BerElement* ber ) +{ + return ( ber->ber_end - ber->ber_buf ); +} + +int +ber_ptrlen( BerElement* ber ) +{ + return ( ber->ber_ptr - ber->ber_buf ); +} + +void +ber_rewind ( BerElement * ber ) +{ + ber->ber_rwptr = NULL; + ber->ber_sos_ptr = NULL; + ber->ber_end = ber->ber_ptr; + ber->ber_ptr = ber->ber_buf; +#if 0 /* TODO: Should we add this? */ + ber->ber_tag = LBER_DEFAULT; + ber->ber_usertag = 0; +#endif +} + +int +ber_remaining( BerElement * ber ) +{ + return ber_pvt_ber_remaining( ber ); +} diff --git a/libraries/liblber/lber-int.h b/libraries/liblber/lber-int.h new file mode 100644 index 0000000..ed88efd --- /dev/null +++ b/libraries/liblber/lber-int.h @@ -0,0 +1,224 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ + +#ifndef _LBER_INT_H +#define _LBER_INT_H + +#include "lber.h" +#include "ldap_log.h" +#include "lber_pvt.h" +#include "ldap_queue.h" + +LDAP_BEGIN_DECL + +typedef void (*BER_LOG_FN)(FILE *file, + const char *subsys, int level, const char *fmt, ... ); + +LBER_V (BER_ERRNO_FN) ber_int_errno_fn; + +#ifdef LDAP_MEMORY_TRACE +# ifndef LDAP_MEMORY_DEBUG +# define LDAP_MEMORY_DEBUG 1 +# endif +#endif + +#ifdef LDAP_MEMORY_DEBUG +LBER_V (long) ber_int_meminuse; +#endif +#if defined(LDAP_MEMORY_DEBUG) && ((LDAP_MEMORY_DEBUG +0) & 2) +# define LDAP_MEMORY_DEBUG_ASSERT assert +#else +# define LDAP_MEMORY_DEBUG_ASSERT(expr) ((void) 0) +#endif + +struct lber_options { + short lbo_valid; + unsigned short lbo_options; + int lbo_debug; +}; + +LBER_F( int ) ber_pvt_log_output( + const char *subsystem, + int level, + const char *fmt, ... ); + +#define LBER_UNINITIALIZED 0x0 +#define LBER_INITIALIZED 0x1 +#define LBER_VALID_BERELEMENT 0x2 +#define LBER_VALID_SOCKBUF 0x3 + +LBER_V (struct lber_options) ber_int_options; +#define ber_int_debug ber_int_options.lbo_debug + +/* Data encoded in ASN.1 BER format */ +struct berelement { + struct lber_options ber_opts; +#define ber_valid ber_opts.lbo_valid +#define ber_options ber_opts.lbo_options +#define ber_debug ber_opts.lbo_debug + + /* + * The members below, when not NULL/LBER_DEFAULT/etc, are: + * ber_buf Data buffer. Other pointers normally point into it. + * ber_rwptr Read/write cursor for Sockbuf I/O. + * ber_memctx Context passed to ber_memalloc() & co. + * When decoding data (reading it from the BerElement): + * ber_end End of BER data. + * ber_ptr Read cursor, except for 1st octet of tags. + * ber_tag 1st octet of next tag, saved from *ber_ptr when + * ber_ptr may be pointing at a tag and is >ber_buf. + * The octet *ber_ptr itself may get overwritten with + * a \0, to terminate the preceding element. + * When encoding data (writing it to the BerElement): + * ber_end End of allocated buffer - 1 (allowing a final \0). + * ber_ptr Last complete BER element (normally write cursor). + * ber_sos_ptr NULL or write cursor for incomplete sequence or set. + * ber_sos_inner offset(seq/set length octets) if ber_sos_ptr!=NULL. + * ber_tag Default tag for next ber_printf() element. + * ber_usertag Boolean set by ber_printf "!" if it sets ber_tag. + * ber_len Reused for ber_sos_inner. + * When output to a Sockbuf: + * ber_ptr End of encoded data to write. + * When input from a Sockbuf: + * See ber_get_next(). + */ + + /* Do not change the order of these 3 fields! see ber_get_next */ + ber_tag_t ber_tag; + ber_len_t ber_len; + ber_tag_t ber_usertag; + + char *ber_buf; + char *ber_ptr; + char *ber_end; + + char *ber_sos_ptr; +# define ber_sos_inner ber_len /* reused for binary compat */ + + char *ber_rwptr; + void *ber_memctx; +}; +#define LBER_VALID(ber) ((ber)->ber_valid==LBER_VALID_BERELEMENT) + +#define ber_pvt_ber_remaining(ber) ((ber)->ber_end - (ber)->ber_ptr) +#define ber_pvt_ber_total(ber) ((ber)->ber_end - (ber)->ber_buf) +#define ber_pvt_ber_write(ber) ((ber)->ber_ptr - (ber)->ber_buf) + +struct sockbuf { + struct lber_options sb_opts; + Sockbuf_IO_Desc *sb_iod; /* I/O functions */ +#define sb_valid sb_opts.lbo_valid +#define sb_options sb_opts.lbo_options +#define sb_debug sb_opts.lbo_debug + ber_socket_t sb_fd; + ber_len_t sb_max_incoming; + unsigned int sb_trans_needs_read:1; + unsigned int sb_trans_needs_write:1; +#ifdef LDAP_PF_LOCAL_SENDMSG + char sb_ungetlen; + char sb_ungetbuf[8]; +#endif +}; + +#define SOCKBUF_VALID( sb ) ( (sb)->sb_valid == LBER_VALID_SOCKBUF ) + + +/* + * decode.c, encode.c + */ + +/* Simplest OID max-DER-component to implement in both decode and encode */ +#define LBER_OID_COMPONENT_MAX ((unsigned long)-1 - 128) + + +/* + * io.c + */ +LBER_F( int ) +ber_realloc LDAP_P(( + BerElement *ber, + ber_len_t len )); + +LBER_F (char *) ber_start LDAP_P(( BerElement * )); +LBER_F (int) ber_len LDAP_P(( BerElement * )); +LBER_F (int) ber_ptrlen LDAP_P(( BerElement * )); +LBER_F (void) ber_rewind LDAP_P(( BerElement * )); + +/* + * bprint.c + */ +#define ber_log_printf ber_pvt_log_printf + +LBER_F( int ) +ber_log_bprint LDAP_P(( + int errlvl, + int loglvl, + const char *data, + ber_len_t len )); + +LBER_F( int ) +ber_log_dump LDAP_P(( + int errlvl, + int loglvl, + BerElement *ber, + int inout )); + +LBER_V (BER_LOG_FN) ber_int_log_proc; +LBER_V (FILE *) ber_pvt_err_file; + +/* memory.c */ + /* simple macros to realloc for now */ +LBER_V (BerMemoryFunctions *) ber_int_memory_fns; +LBER_F (char *) ber_strndup( LDAP_CONST char *, ber_len_t ); +LBER_F (char *) ber_strndup_x( LDAP_CONST char *, ber_len_t, void *ctx ); + +#define LBER_MALLOC(s) ber_memalloc((s)) +#define LBER_CALLOC(n,s) ber_memcalloc((n),(s)) +#define LBER_REALLOC(p,s) ber_memrealloc((p),(s)) +#define LBER_FREE(p) ber_memfree((p)) +#define LBER_VFREE(v) ber_memvfree((void**)(v)) +#define LBER_STRDUP(s) ber_strdup((s)) +#define LBER_STRNDUP(s,l) ber_strndup((s),(l)) + +/* sockbuf.c */ + +LBER_F( int ) +ber_int_sb_init LDAP_P(( Sockbuf *sb )); + +LBER_F( int ) +ber_int_sb_close LDAP_P(( Sockbuf *sb )); + +LBER_F( int ) +ber_int_sb_destroy LDAP_P(( Sockbuf *sb )); + +LBER_F( ber_slen_t ) +ber_int_sb_read LDAP_P(( Sockbuf *sb, void *buf, ber_len_t len )); + +LBER_F( ber_slen_t ) +ber_int_sb_write LDAP_P(( Sockbuf *sb, void *buf, ber_len_t len )); + +LDAP_END_DECL + +#endif /* _LBER_INT_H */ diff --git a/libraries/liblber/memory.c b/libraries/liblber/memory.c new file mode 100644 index 0000000..d93104d --- /dev/null +++ b/libraries/liblber/memory.c @@ -0,0 +1,825 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <ac/stdlib.h> +#include <ac/string.h> + +#include "lber-int.h" + +#ifdef LDAP_MEMORY_TRACE +#include <stdio.h> +#endif + +#ifdef LDAP_MEMORY_DEBUG +/* + * LDAP_MEMORY_DEBUG should only be enabled for the purposes of + * debugging memory management within OpenLDAP libraries and slapd. + * + * It should only be enabled by an experienced developer as it causes + * the inclusion of numerous assert()'s, many of which may be triggered + * by a prefectly valid program. If LDAP_MEMORY_DEBUG & 2 is true, + * that includes asserts known to break both slapd and current clients. + * + * The code behind this macro is subject to change as needed to + * support this testing. + */ + +struct ber_mem_hdr { + ber_int_t bm_top; /* Pattern to detect buf overrun from prev buffer */ + ber_int_t bm_length; /* Length of user allocated area */ +#ifdef LDAP_MEMORY_TRACE + ber_int_t bm_sequence; /* Allocation sequence number */ +#endif + union bmu_align_u { /* Force alignment, pattern to detect back clobber */ + ber_len_t bmu_len_t; + ber_tag_t bmu_tag_t; + ber_int_t bmu_int_t; + + size_t bmu_size_t; + void * bmu_voidp; + double bmu_double; + long bmu_long; + long (*bmu_funcp)( double ); + unsigned char bmu_char[4]; + } ber_align; +#define bm_junk ber_align.bmu_len_t +#define bm_data ber_align.bmu_char[1] +#define bm_char ber_align.bmu_char +}; + +/* Pattern at top of allocated space */ +#define LBER_MEM_JUNK ((ber_int_t) 0xdeaddada) + +static const struct ber_mem_hdr ber_int_mem_hdr = { LBER_MEM_JUNK }; + +/* Note sequence and ber_int_meminuse are counters, but are not + * thread safe. If you want to use these values for multithreaded applications, + * you must put mutexes around them, otherwise they will have incorrect values. + * When debugging, if you sort the debug output, the sequence number will + * put allocations/frees together. It is then a simple matter to write a script + * to find any allocations that don't have a buffer free function. + */ +long ber_int_meminuse = 0; +#ifdef LDAP_MEMORY_TRACE +static ber_int_t sequence = 0; +#endif + +/* Pattern placed just before user data */ +static unsigned char toppattern[4] = { 0xde, 0xad, 0xba, 0xde }; +/* Pattern placed just after user data */ +static unsigned char endpattern[4] = { 0xd1, 0xed, 0xde, 0xca }; + +#define mbu_len sizeof(ber_int_mem_hdr.ber_align) + +/* Test if pattern placed just before user data is good */ +#define testdatatop(val) ( \ + *(val->bm_char+mbu_len-4)==toppattern[0] && \ + *(val->bm_char+mbu_len-3)==toppattern[1] && \ + *(val->bm_char+mbu_len-2)==toppattern[2] && \ + *(val->bm_char+mbu_len-1)==toppattern[3] ) + +/* Place pattern just before user data */ +#define setdatatop(val) *(val->bm_char+mbu_len-4)=toppattern[0]; \ + *(val->bm_char+mbu_len-3)=toppattern[1]; \ + *(val->bm_char+mbu_len-2)=toppattern[2]; \ + *(val->bm_char+mbu_len-1)=toppattern[3]; + +/* Test if pattern placed just after user data is good */ +#define testend(val) ( *((unsigned char *)val+0)==endpattern[0] && \ + *((unsigned char *)val+1)==endpattern[1] && \ + *((unsigned char *)val+2)==endpattern[2] && \ + *((unsigned char *)val+3)==endpattern[3] ) + +/* Place pattern just after user data */ +#define setend(val) *((unsigned char *)val+0)=endpattern[0]; \ + *((unsigned char *)val+1)=endpattern[1]; \ + *((unsigned char *)val+2)=endpattern[2]; \ + *((unsigned char *)val+3)=endpattern[3]; + +#define BER_MEM_BADADDR ((void *) &ber_int_mem_hdr.bm_data) +#define BER_MEM_VALID(p) do { \ + assert( (p) != BER_MEM_BADADDR ); \ + assert( (p) != (void *) &ber_int_mem_hdr ); \ + } while(0) + +#else +#define BER_MEM_VALID(p) /* no-op */ +#endif + +BerMemoryFunctions *ber_int_memory_fns = NULL; + +void +ber_memfree_x( void *p, void *ctx ) +{ + if( p == NULL ) { + return; + } + + BER_MEM_VALID( p ); + + if( ber_int_memory_fns == NULL || ctx == NULL ) { +#ifdef LDAP_MEMORY_DEBUG + struct ber_mem_hdr *mh = (struct ber_mem_hdr *) + ((char *)p - sizeof(struct ber_mem_hdr)); + assert( mh->bm_top == LBER_MEM_JUNK); + assert( testdatatop( mh)); + assert( testend( (char *)&mh[1] + mh->bm_length) ); + ber_int_meminuse -= mh->bm_length; + +#ifdef LDAP_MEMORY_TRACE + fprintf(stderr, "0x%08lx 0x%08lx -f- %ld ber_memfree %ld\n", + (long)mh->bm_sequence, (long)mh, (long)mh->bm_length, + ber_int_meminuse); +#endif + /* Fill the free space with poison */ + memset( mh, 0xff, mh->bm_length + sizeof(struct ber_mem_hdr) + sizeof(ber_int_t)); + free( mh ); +#else + free( p ); +#endif + return; + } + + assert( ber_int_memory_fns->bmf_free != 0 ); + + (*ber_int_memory_fns->bmf_free)( p, ctx ); +} + +void +ber_memfree( void *p ) +{ + ber_memfree_x(p, NULL); +} + +void +ber_memvfree_x( void **vec, void *ctx ) +{ + int i; + + if( vec == NULL ) { + return; + } + + BER_MEM_VALID( vec ); + + for ( i = 0; vec[i] != NULL; i++ ) { + ber_memfree_x( vec[i], ctx ); + } + + ber_memfree_x( vec, ctx ); +} + +void +ber_memvfree( void **vec ) +{ + ber_memvfree_x( vec, NULL ); +} + +void * +ber_memalloc_x( ber_len_t s, void *ctx ) +{ + void *new; + + if( s == 0 ) { + LDAP_MEMORY_DEBUG_ASSERT( s != 0 ); + return NULL; + } + + if( ber_int_memory_fns == NULL || ctx == NULL ) { +#ifdef LDAP_MEMORY_DEBUG + new = malloc(s + sizeof(struct ber_mem_hdr) + sizeof( ber_int_t)); + if( new ) + { + struct ber_mem_hdr *mh = new; + mh->bm_top = LBER_MEM_JUNK; + mh->bm_length = s; + setdatatop( mh); + setend( (char *)&mh[1] + mh->bm_length ); + + ber_int_meminuse += mh->bm_length; /* Count mem inuse */ + +#ifdef LDAP_MEMORY_TRACE + mh->bm_sequence = sequence++; + fprintf(stderr, "0x%08lx 0x%08lx -a- %ld ber_memalloc %ld\n", + (long)mh->bm_sequence, (long)mh, (long)mh->bm_length, + ber_int_meminuse); +#endif + /* poison new memory */ + memset( (char *)&mh[1], 0xff, s); + + BER_MEM_VALID( &mh[1] ); + new = &mh[1]; + } +#else + new = malloc( s ); +#endif + } else { + new = (*ber_int_memory_fns->bmf_malloc)( s, ctx ); + } + + if( new == NULL ) { + ber_errno = LBER_ERROR_MEMORY; + } + + return new; +} + +void * +ber_memalloc( ber_len_t s ) +{ + return ber_memalloc_x( s, NULL ); +} + +void * +ber_memcalloc_x( ber_len_t n, ber_len_t s, void *ctx ) +{ + void *new; + + if( n == 0 || s == 0 ) { + LDAP_MEMORY_DEBUG_ASSERT( n != 0 && s != 0); + return NULL; + } + + if( ber_int_memory_fns == NULL || ctx == NULL ) { +#ifdef LDAP_MEMORY_DEBUG + new = n < (-sizeof(struct ber_mem_hdr) - sizeof(ber_int_t)) / s + ? calloc(1, n*s + sizeof(struct ber_mem_hdr) + sizeof(ber_int_t)) + : NULL; + if( new ) + { + struct ber_mem_hdr *mh = new; + + mh->bm_top = LBER_MEM_JUNK; + mh->bm_length = n*s; + setdatatop( mh); + setend( (char *)&mh[1] + mh->bm_length ); + + ber_int_meminuse += mh->bm_length; + +#ifdef LDAP_MEMORY_TRACE + mh->bm_sequence = sequence++; + fprintf(stderr, "0x%08lx 0x%08lx -a- %ld ber_memcalloc %ld\n", + (long)mh->bm_sequence, (long)mh, (long)mh->bm_length, + ber_int_meminuse); +#endif + BER_MEM_VALID( &mh[1] ); + new = &mh[1]; + } +#else + new = calloc( n, s ); +#endif + + } else { + new = (*ber_int_memory_fns->bmf_calloc)( n, s, ctx ); + } + + if( new == NULL ) { + ber_errno = LBER_ERROR_MEMORY; + } + + return new; +} + +void * +ber_memcalloc( ber_len_t n, ber_len_t s ) +{ + return ber_memcalloc_x( n, s, NULL ); +} + +void * +ber_memrealloc_x( void* p, ber_len_t s, void *ctx ) +{ + void *new = NULL; + + /* realloc(NULL,s) -> malloc(s) */ + if( p == NULL ) { + return ber_memalloc_x( s, ctx ); + } + + /* realloc(p,0) -> free(p) */ + if( s == 0 ) { + ber_memfree_x( p, ctx ); + return NULL; + } + + BER_MEM_VALID( p ); + + if( ber_int_memory_fns == NULL || ctx == NULL ) { +#ifdef LDAP_MEMORY_DEBUG + ber_int_t oldlen; + struct ber_mem_hdr *mh = (struct ber_mem_hdr *) + ((char *)p - sizeof(struct ber_mem_hdr)); + assert( mh->bm_top == LBER_MEM_JUNK); + assert( testdatatop( mh)); + assert( testend( (char *)&mh[1] + mh->bm_length) ); + oldlen = mh->bm_length; + + p = realloc( mh, s + sizeof(struct ber_mem_hdr) + sizeof(ber_int_t) ); + if( p == NULL ) { + ber_errno = LBER_ERROR_MEMORY; + return NULL; + } + + mh = p; + mh->bm_length = s; + setend( (char *)&mh[1] + mh->bm_length ); + if( s > oldlen ) { + /* poison any new memory */ + memset( (char *)&mh[1] + oldlen, 0xff, s - oldlen); + } + + assert( mh->bm_top == LBER_MEM_JUNK); + assert( testdatatop( mh)); + + ber_int_meminuse += s - oldlen; +#ifdef LDAP_MEMORY_TRACE + fprintf(stderr, "0x%08lx 0x%08lx -a- %ld ber_memrealloc %ld\n", + (long)mh->bm_sequence, (long)mh, (long)mh->bm_length, + ber_int_meminuse); +#endif + BER_MEM_VALID( &mh[1] ); + return &mh[1]; +#else + new = realloc( p, s ); +#endif + } else { + new = (*ber_int_memory_fns->bmf_realloc)( p, s, ctx ); + } + + if( new == NULL ) { + ber_errno = LBER_ERROR_MEMORY; + } + + return new; +} + +void * +ber_memrealloc( void* p, ber_len_t s ) +{ + return ber_memrealloc_x( p, s, NULL ); +} + +void +ber_bvfree_x( struct berval *bv, void *ctx ) +{ + if( bv == NULL ) { + return; + } + + BER_MEM_VALID( bv ); + + if ( bv->bv_val != NULL ) { + ber_memfree_x( bv->bv_val, ctx ); + } + + ber_memfree_x( (char *) bv, ctx ); +} + +void +ber_bvfree( struct berval *bv ) +{ + ber_bvfree_x( bv, NULL ); +} + +void +ber_bvecfree_x( struct berval **bv, void *ctx ) +{ + int i; + + if( bv == NULL ) { + return; + } + + BER_MEM_VALID( bv ); + + /* count elements */ + for ( i = 0; bv[i] != NULL; i++ ) ; + + /* free in reverse order */ + for ( i--; i >= 0; i-- ) { + ber_bvfree_x( bv[i], ctx ); + } + + ber_memfree_x( (char *) bv, ctx ); +} + +void +ber_bvecfree( struct berval **bv ) +{ + ber_bvecfree_x( bv, NULL ); +} + +int +ber_bvecadd_x( struct berval ***bvec, struct berval *bv, void *ctx ) +{ + ber_len_t i; + struct berval **new; + + if( *bvec == NULL ) { + if( bv == NULL ) { + /* nothing to add */ + return 0; + } + + *bvec = ber_memalloc_x( 2 * sizeof(struct berval *), ctx ); + + if( *bvec == NULL ) { + return -1; + } + + (*bvec)[0] = bv; + (*bvec)[1] = NULL; + + return 1; + } + + BER_MEM_VALID( bvec ); + + /* count entries */ + for ( i = 0; (*bvec)[i] != NULL; i++ ) { + /* EMPTY */; + } + + if( bv == NULL ) { + return i; + } + + new = ber_memrealloc_x( *bvec, (i+2) * sizeof(struct berval *), ctx); + + if( new == NULL ) { + return -1; + } + + *bvec = new; + + (*bvec)[i++] = bv; + (*bvec)[i] = NULL; + + return i; +} + +int +ber_bvecadd( struct berval ***bvec, struct berval *bv ) +{ + return ber_bvecadd_x( bvec, bv, NULL ); +} + +struct berval * +ber_dupbv_x( + struct berval *dst, struct berval *src, void *ctx ) +{ + struct berval *new, tmp; + + if( src == NULL ) { + ber_errno = LBER_ERROR_PARAM; + return NULL; + } + + if ( dst ) { + new = &tmp; + } else { + if(( new = ber_memalloc_x( sizeof(struct berval), ctx )) == NULL ) { + return NULL; + } + } + + if ( src->bv_val == NULL ) { + new->bv_val = NULL; + new->bv_len = 0; + } else { + + if(( new->bv_val = ber_memalloc_x( src->bv_len + 1, ctx )) == NULL ) { + if ( !dst ) + ber_memfree_x( new, ctx ); + return NULL; + } + + AC_MEMCPY( new->bv_val, src->bv_val, src->bv_len ); + new->bv_val[src->bv_len] = '\0'; + new->bv_len = src->bv_len; + } + + if ( dst ) { + *dst = *new; + new = dst; + } + + return new; +} + +struct berval * +ber_dupbv( + struct berval *dst, struct berval *src ) +{ + return ber_dupbv_x( dst, src, NULL ); +} + +struct berval * +ber_bvdup( + struct berval *src ) +{ + return ber_dupbv_x( NULL, src, NULL ); +} + +struct berval * +ber_str2bv_x( + LDAP_CONST char *s, ber_len_t len, int dup, struct berval *bv, + void *ctx) +{ + struct berval *new; + + if( s == NULL ) { + ber_errno = LBER_ERROR_PARAM; + return NULL; + } + + if( bv ) { + new = bv; + } else { + if(( new = ber_memalloc_x( sizeof(struct berval), ctx )) == NULL ) { + return NULL; + } + } + + new->bv_len = len ? len : strlen( s ); + if ( dup ) { + if ( (new->bv_val = ber_memalloc_x( new->bv_len+1, ctx )) == NULL ) { + if ( !bv ) + ber_memfree_x( new, ctx ); + return NULL; + } + + AC_MEMCPY( new->bv_val, s, new->bv_len ); + new->bv_val[new->bv_len] = '\0'; + } else { + new->bv_val = (char *) s; + } + + return( new ); +} + +struct berval * +ber_str2bv( + LDAP_CONST char *s, ber_len_t len, int dup, struct berval *bv) +{ + return ber_str2bv_x( s, len, dup, bv, NULL ); +} + +struct berval * +ber_mem2bv_x( + LDAP_CONST char *s, ber_len_t len, int dup, struct berval *bv, + void *ctx) +{ + struct berval *new; + + if( s == NULL ) { + ber_errno = LBER_ERROR_PARAM; + return NULL; + } + + if( bv ) { + new = bv; + } else { + if(( new = ber_memalloc_x( sizeof(struct berval), ctx )) == NULL ) { + return NULL; + } + } + + new->bv_len = len; + if ( dup ) { + if ( (new->bv_val = ber_memalloc_x( new->bv_len+1, ctx )) == NULL ) { + if ( !bv ) { + ber_memfree_x( new, ctx ); + } + return NULL; + } + + AC_MEMCPY( new->bv_val, s, new->bv_len ); + new->bv_val[new->bv_len] = '\0'; + } else { + new->bv_val = (char *) s; + } + + return( new ); +} + +struct berval * +ber_mem2bv( + LDAP_CONST char *s, ber_len_t len, int dup, struct berval *bv) +{ + return ber_mem2bv_x( s, len, dup, bv, NULL ); +} + +char * +ber_strdup_x( LDAP_CONST char *s, void *ctx ) +{ + char *p; + size_t len; + +#ifdef LDAP_MEMORY_DEBUG + assert(s != NULL); /* bv damn better point to something */ +#endif + + if( s == NULL ) { + ber_errno = LBER_ERROR_PARAM; + return NULL; + } + + len = strlen( s ) + 1; + if ( (p = ber_memalloc_x( len, ctx )) != NULL ) { + AC_MEMCPY( p, s, len ); + } + + return p; +} + +char * +ber_strdup( LDAP_CONST char *s ) +{ + return ber_strdup_x( s, NULL ); +} + +ber_len_t +ber_strnlen( LDAP_CONST char *s, ber_len_t len ) +{ + ber_len_t l; + + for ( l = 0; l < len && s[l] != '\0'; l++ ) ; + + return l; +} + +char * +ber_strndup_x( LDAP_CONST char *s, ber_len_t l, void *ctx ) +{ + char *p; + size_t len; + +#ifdef LDAP_MEMORY_DEBUG + assert(s != NULL); /* bv damn better point to something */ +#endif + + if( s == NULL ) { + ber_errno = LBER_ERROR_PARAM; + return NULL; + } + + len = ber_strnlen( s, l ); + if ( (p = ber_memalloc_x( len + 1, ctx )) != NULL ) { + AC_MEMCPY( p, s, len ); + p[len] = '\0'; + } + + return p; +} + +char * +ber_strndup( LDAP_CONST char *s, ber_len_t l ) +{ + return ber_strndup_x( s, l, NULL ); +} + +/* + * dst is resized as required by src and the value of src is copied into dst + * dst->bv_val must be NULL (and dst->bv_len must be 0), or it must be + * alloc'ed with the context ctx + */ +struct berval * +ber_bvreplace_x( struct berval *dst, LDAP_CONST struct berval *src, void *ctx ) +{ + assert( dst != NULL ); + assert( !BER_BVISNULL( src ) ); + + if ( BER_BVISNULL( dst ) || dst->bv_len < src->bv_len ) { + dst->bv_val = ber_memrealloc_x( dst->bv_val, src->bv_len + 1, ctx ); + } + + AC_MEMCPY( dst->bv_val, src->bv_val, src->bv_len + 1 ); + dst->bv_len = src->bv_len; + + return dst; +} + +struct berval * +ber_bvreplace( struct berval *dst, LDAP_CONST struct berval *src ) +{ + return ber_bvreplace_x( dst, src, NULL ); +} + +void +ber_bvarray_free_x( BerVarray a, void *ctx ) +{ + int i; + + if (a) { + BER_MEM_VALID( a ); + + /* count elements */ + for (i=0; a[i].bv_val; i++) ; + + /* free in reverse order */ + for (i--; i>=0; i--) { + ber_memfree_x(a[i].bv_val, ctx); + } + + ber_memfree_x(a, ctx); + } +} + +void +ber_bvarray_free( BerVarray a ) +{ + ber_bvarray_free_x(a, NULL); +} + +int +ber_bvarray_dup_x( BerVarray *dst, BerVarray src, void *ctx ) +{ + int i, j; + BerVarray new; + + if ( !src ) { + *dst = NULL; + return 0; + } + + for (i=0; !BER_BVISNULL( &src[i] ); i++) ; + new = ber_memalloc_x(( i+1 ) * sizeof(BerValue), ctx ); + if ( !new ) + return -1; + for (j=0; j<i; j++) { + ber_dupbv_x( &new[j], &src[j], ctx ); + if ( BER_BVISNULL( &new[j] )) { + ber_bvarray_free_x( new, ctx ); + return -1; + } + } + BER_BVZERO( &new[j] ); + *dst = new; + return 0; +} + +int +ber_bvarray_add_x( BerVarray *a, BerValue *bv, void *ctx ) +{ + int n; + + if ( *a == NULL ) { + if (bv == NULL) { + return 0; + } + n = 0; + + *a = (BerValue *) ber_memalloc_x( 2 * sizeof(BerValue), ctx ); + if ( *a == NULL ) { + return -1; + } + + } else { + BerVarray atmp; + BER_MEM_VALID( a ); + + for ( n = 0; *a != NULL && (*a)[n].bv_val != NULL; n++ ) { + ; /* just count them */ + } + + if (bv == NULL) { + return n; + } + + atmp = (BerValue *) ber_memrealloc_x( (char *) *a, + (n + 2) * sizeof(BerValue), ctx ); + + if( atmp == NULL ) { + return -1; + } + + *a = atmp; + } + + (*a)[n++] = *bv; + (*a)[n].bv_val = NULL; + (*a)[n].bv_len = 0; + + return n; +} + +int +ber_bvarray_add( BerVarray *a, BerValue *bv ) +{ + return ber_bvarray_add_x( a, bv, NULL ); +} diff --git a/libraries/liblber/nt_err.c b/libraries/liblber/nt_err.c new file mode 100644 index 0000000..93a496a --- /dev/null +++ b/libraries/liblber/nt_err.c @@ -0,0 +1,96 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#ifdef HAVE_WINSOCK2 +#include <winsock2.h> +#elif defined(HAVE_WINSOCK) +#include <winsock.h> +#endif /* HAVE_WINSOCK(2) */ + +#define LBER_RETSTR( x ) case x: return #x; + +char *ber_pvt_wsa_err2string( int err ) +{ + switch( err ) { + LBER_RETSTR( WSAEINTR ) + LBER_RETSTR( WSAEBADF ) + LBER_RETSTR( WSAEACCES ) + LBER_RETSTR( WSAEFAULT ) + LBER_RETSTR( WSAEINVAL ) + LBER_RETSTR( WSAEMFILE ) + LBER_RETSTR( WSAEWOULDBLOCK ) + LBER_RETSTR( WSAEINPROGRESS ) + LBER_RETSTR( WSAEALREADY ) + LBER_RETSTR( WSAENOTSOCK ) + LBER_RETSTR( WSAEDESTADDRREQ ) + LBER_RETSTR( WSAEMSGSIZE ) + LBER_RETSTR( WSAEPROTOTYPE ) + LBER_RETSTR( WSAENOPROTOOPT ) + LBER_RETSTR( WSAEPROTONOSUPPORT ) + LBER_RETSTR( WSAESOCKTNOSUPPORT ) + LBER_RETSTR( WSAEOPNOTSUPP ) + LBER_RETSTR( WSAEPFNOSUPPORT ) + LBER_RETSTR( WSAEAFNOSUPPORT ) + LBER_RETSTR( WSAEADDRINUSE ) + LBER_RETSTR( WSAEADDRNOTAVAIL ) + LBER_RETSTR( WSAENETDOWN ) + LBER_RETSTR( WSAENETUNREACH ) + LBER_RETSTR( WSAENETRESET ) + LBER_RETSTR( WSAECONNABORTED ) + LBER_RETSTR( WSAECONNRESET ) + LBER_RETSTR( WSAENOBUFS ) + LBER_RETSTR( WSAEISCONN ) + LBER_RETSTR( WSAENOTCONN ) + LBER_RETSTR( WSAESHUTDOWN ) + LBER_RETSTR( WSAETOOMANYREFS ) + LBER_RETSTR( WSAETIMEDOUT ) + LBER_RETSTR( WSAECONNREFUSED ) + LBER_RETSTR( WSAELOOP ) + LBER_RETSTR( WSAENAMETOOLONG ) + LBER_RETSTR( WSAEHOSTDOWN ) + LBER_RETSTR( WSAEHOSTUNREACH ) + LBER_RETSTR( WSAENOTEMPTY ) + LBER_RETSTR( WSAEPROCLIM ) + LBER_RETSTR( WSAEUSERS ) + LBER_RETSTR( WSAEDQUOT ) + LBER_RETSTR( WSAESTALE ) + LBER_RETSTR( WSAEREMOTE ) + LBER_RETSTR( WSASYSNOTREADY ) + LBER_RETSTR( WSAVERNOTSUPPORTED ) + LBER_RETSTR( WSANOTINITIALISED ) + LBER_RETSTR( WSAEDISCON ) + +#ifdef HAVE_WINSOCK2 + LBER_RETSTR( WSAENOMORE ) + LBER_RETSTR( WSAECANCELLED ) + LBER_RETSTR( WSAEINVALIDPROCTABLE ) + LBER_RETSTR( WSAEINVALIDPROVIDER ) + LBER_RETSTR( WSASYSCALLFAILURE ) + LBER_RETSTR( WSASERVICE_NOT_FOUND ) + LBER_RETSTR( WSATYPE_NOT_FOUND ) + LBER_RETSTR( WSA_E_NO_MORE ) + LBER_RETSTR( WSA_E_CANCELLED ) + LBER_RETSTR( WSAEREFUSED ) +#endif /* HAVE_WINSOCK2 */ + + LBER_RETSTR( WSAHOST_NOT_FOUND ) + LBER_RETSTR( WSATRY_AGAIN ) + LBER_RETSTR( WSANO_RECOVERY ) + LBER_RETSTR( WSANO_DATA ) + } + return "unknown WSA error"; +} diff --git a/libraries/liblber/options.c b/libraries/liblber/options.c new file mode 100644 index 0000000..9b2baaf --- /dev/null +++ b/libraries/liblber/options.c @@ -0,0 +1,233 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/stdarg.h> +#include "lber-int.h" + +char ber_pvt_opt_on; /* used to get a non-NULL address for *_OPT_ON */ + +struct lber_options ber_int_options = { + LBER_UNINITIALIZED, 0, 0 }; + +static BerMemoryFunctions ber_int_memory_fns_datum; + +int +ber_get_option( + void *item, + int option, + void *outvalue) +{ + const BerElement *ber; + const Sockbuf *sb; + + if(outvalue == NULL) { + /* no place to get to */ + ber_errno = LBER_ERROR_PARAM; + return LBER_OPT_ERROR; + } + + if(item == NULL) { + switch ( option ) { + case LBER_OPT_BER_DEBUG: + * (int *) outvalue = ber_int_debug; + return LBER_OPT_SUCCESS; + + case LBER_OPT_MEMORY_INUSE: + /* The memory inuse is a global variable on kernal implementations. + * This means that memory debug is shared by all LDAP processes + * so for this variable to have much meaning, only one LDAP process + * should be running and memory inuse should be initialized to zero + * using the lber_set_option() function during startup. + * The counter is not accurate for multithreaded ldap applications. + */ +#ifdef LDAP_MEMORY_DEBUG + * (int *) outvalue = ber_int_meminuse; + return LBER_OPT_SUCCESS; +#else + return LBER_OPT_ERROR; +#endif + + case LBER_OPT_LOG_PRINT_FILE: + *((FILE**)outvalue) = (FILE*)ber_pvt_err_file; + return LBER_OPT_SUCCESS; + } + + ber_errno = LBER_ERROR_PARAM; + return LBER_OPT_ERROR; + } + + ber = item; + sb = item; + + switch(option) { + case LBER_OPT_BER_OPTIONS: + assert( LBER_VALID( ber ) ); + * (int *) outvalue = ber->ber_options; + return LBER_OPT_SUCCESS; + + case LBER_OPT_BER_DEBUG: + assert( LBER_VALID( ber ) ); + * (int *) outvalue = ber->ber_debug; + return LBER_OPT_SUCCESS; + + case LBER_OPT_BER_REMAINING_BYTES: + assert( LBER_VALID( ber ) ); + *((ber_len_t *) outvalue) = ber_pvt_ber_remaining(ber); + return LBER_OPT_SUCCESS; + + case LBER_OPT_BER_TOTAL_BYTES: + assert( LBER_VALID( ber ) ); + *((ber_len_t *) outvalue) = ber_pvt_ber_total(ber); + return LBER_OPT_SUCCESS; + + case LBER_OPT_BER_BYTES_TO_WRITE: + assert( LBER_VALID( ber ) ); + *((ber_len_t *) outvalue) = ber_pvt_ber_write(ber); + return LBER_OPT_SUCCESS; + + case LBER_OPT_BER_MEMCTX: + assert( LBER_VALID( ber ) ); + *((void **) outvalue) = ber->ber_memctx; + return LBER_OPT_SUCCESS; + + default: + /* bad param */ + ber_errno = LBER_ERROR_PARAM; + break; + } + + return LBER_OPT_ERROR; +} + +int +ber_set_option( + void *item, + int option, + LDAP_CONST void *invalue) +{ + BerElement *ber; + Sockbuf *sb; + + if(invalue == NULL) { + /* no place to set from */ + ber_errno = LBER_ERROR_PARAM; + return LBER_OPT_ERROR; + } + + if(item == NULL) { + switch ( option ) { + case LBER_OPT_BER_DEBUG: + ber_int_debug = * (const int *) invalue; + return LBER_OPT_SUCCESS; + + case LBER_OPT_LOG_PRINT_FN: + ber_pvt_log_print = (BER_LOG_PRINT_FN) invalue; + return LBER_OPT_SUCCESS; + + case LBER_OPT_LOG_PRINT_FILE: + ber_pvt_err_file = (void *) invalue; + return LBER_OPT_SUCCESS; + + case LBER_OPT_MEMORY_INUSE: + /* The memory inuse is a global variable on kernal implementations. + * This means that memory debug is shared by all LDAP processes + * so for this variable to have much meaning, only one LDAP process + * should be running and memory inuse should be initialized to zero + * using the lber_set_option() function during startup. + * The counter is not accurate for multithreaded applications. + */ +#ifdef LDAP_MEMORY_DEBUG + ber_int_meminuse = * (int *) invalue; + return LBER_OPT_SUCCESS; +#else + return LBER_OPT_ERROR; +#endif + case LBER_OPT_MEMORY_FNS: + if ( ber_int_memory_fns == NULL ) + { + const BerMemoryFunctions *f = + (const BerMemoryFunctions *) invalue; + /* make sure all functions are provided */ + if(!( f->bmf_malloc && f->bmf_calloc + && f->bmf_realloc && f->bmf_free )) + { + ber_errno = LBER_ERROR_PARAM; + return LBER_OPT_ERROR; + } + + ber_int_memory_fns = &ber_int_memory_fns_datum; + + AC_MEMCPY(ber_int_memory_fns, f, + sizeof(BerMemoryFunctions)); + + return LBER_OPT_SUCCESS; + } + break; + + case LBER_OPT_LOG_PROC: + ber_int_log_proc = (BER_LOG_FN)invalue; + return LBER_OPT_SUCCESS; + } + + ber_errno = LBER_ERROR_PARAM; + return LBER_OPT_ERROR; + } + + ber = item; + sb = item; + + switch(option) { + case LBER_OPT_BER_OPTIONS: + assert( LBER_VALID( ber ) ); + ber->ber_options = * (const int *) invalue; + return LBER_OPT_SUCCESS; + + case LBER_OPT_BER_DEBUG: + assert( LBER_VALID( ber ) ); + ber->ber_debug = * (const int *) invalue; + return LBER_OPT_SUCCESS; + + case LBER_OPT_BER_REMAINING_BYTES: + assert( LBER_VALID( ber ) ); + ber->ber_end = &ber->ber_ptr[* (const ber_len_t *) invalue]; + return LBER_OPT_SUCCESS; + + case LBER_OPT_BER_TOTAL_BYTES: + assert( LBER_VALID( ber ) ); + ber->ber_end = &ber->ber_buf[* (const ber_len_t *) invalue]; + return LBER_OPT_SUCCESS; + + case LBER_OPT_BER_BYTES_TO_WRITE: + assert( LBER_VALID( ber ) ); + ber->ber_ptr = &ber->ber_buf[* (const ber_len_t *) invalue]; + return LBER_OPT_SUCCESS; + + case LBER_OPT_BER_MEMCTX: + assert( LBER_VALID( ber ) ); + ber->ber_memctx = *(void **)invalue; + return LBER_OPT_SUCCESS; + + default: + /* bad param */ + ber_errno = LBER_ERROR_PARAM; + break; + } + + return LBER_OPT_ERROR; +} diff --git a/libraries/liblber/sockbuf.c b/libraries/liblber/sockbuf.c new file mode 100644 index 0000000..a84160a --- /dev/null +++ b/libraries/liblber/sockbuf.c @@ -0,0 +1,988 @@ +/* sockbuf.c - i/o routines with support for adding i/o layers. */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/ctype.h> +#include <ac/errno.h> +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/unistd.h> + +#ifdef HAVE_IO_H +#include <io.h> +#endif /* HAVE_IO_H */ + +#if defined( HAVE_FCNTL_H ) +#include <fcntl.h> +#endif + +#if defined( HAVE_SYS_FILIO_H ) +#include <sys/filio.h> +#elif defined( HAVE_SYS_IOCTL_H ) +#include <sys/ioctl.h> +#endif + +#include "lber-int.h" + +#ifndef LBER_MIN_BUFF_SIZE +#define LBER_MIN_BUFF_SIZE 4096 +#endif +#ifndef LBER_MAX_BUFF_SIZE +#define LBER_MAX_BUFF_SIZE (65536*256) +#endif +#ifndef LBER_DEFAULT_READAHEAD +#define LBER_DEFAULT_READAHEAD 16384 +#endif + +Sockbuf * +ber_sockbuf_alloc( void ) +{ + Sockbuf *sb; + + sb = LBER_CALLOC( 1, sizeof( Sockbuf ) ); + + if( sb == NULL ) return NULL; + + ber_int_sb_init( sb ); + return sb; +} + +void +ber_sockbuf_free( Sockbuf *sb ) +{ + assert( sb != NULL ); + assert( SOCKBUF_VALID( sb ) ); + + ber_int_sb_close( sb ); + ber_int_sb_destroy( sb ); + LBER_FREE( sb ); +} + +/* Return values: -1: error, 0: no operation performed or the answer is false, + * 1: successful operation or the answer is true + */ +int +ber_sockbuf_ctrl( Sockbuf *sb, int opt, void *arg ) +{ + Sockbuf_IO_Desc *p; + int ret = 0; + + assert( sb != NULL ); + assert( SOCKBUF_VALID( sb ) ); + + switch ( opt ) { + case LBER_SB_OPT_HAS_IO: + p = sb->sb_iod; + while ( p && p->sbiod_io != (Sockbuf_IO *)arg ) { + p = p->sbiod_next; + } + + if ( p ) { + ret = 1; + } + break; + + case LBER_SB_OPT_GET_FD: + if ( arg != NULL ) { + *((ber_socket_t *)arg) = sb->sb_fd; + } + ret = ( sb->sb_fd == AC_SOCKET_INVALID ? -1 : 1); + break; + + case LBER_SB_OPT_SET_FD: + sb->sb_fd = *((ber_socket_t *)arg); + ret = 1; + break; + + case LBER_SB_OPT_SET_NONBLOCK: + ret = ber_pvt_socket_set_nonblock( sb->sb_fd, arg != NULL) + ? -1 : 1; + break; + + case LBER_SB_OPT_DRAIN: { + /* Drain the data source to enable possible errors (e.g. + * TLS) to be propagated to the upper layers + */ + char buf[LBER_MIN_BUFF_SIZE]; + + do { + ret = ber_int_sb_read( sb, buf, sizeof( buf ) ); + } while ( ret == sizeof( buf ) ); + + ret = 1; + } break; + + case LBER_SB_OPT_NEEDS_READ: + ret = ( sb->sb_trans_needs_read ? 1 : 0 ); + break; + + case LBER_SB_OPT_NEEDS_WRITE: + ret = ( sb->sb_trans_needs_write ? 1 : 0 ); + break; + + case LBER_SB_OPT_GET_MAX_INCOMING: + if ( arg != NULL ) { + *((ber_len_t *)arg) = sb->sb_max_incoming; + } + ret = 1; + break; + + case LBER_SB_OPT_SET_MAX_INCOMING: + sb->sb_max_incoming = *((ber_len_t *)arg); + ret = 1; + break; + + case LBER_SB_OPT_UNGET_BUF: +#ifdef LDAP_PF_LOCAL_SENDMSG + sb->sb_ungetlen = ((struct berval *)arg)->bv_len; + if ( sb->sb_ungetlen <= sizeof( sb->sb_ungetbuf )) { + AC_MEMCPY( sb->sb_ungetbuf, ((struct berval *)arg)->bv_val, + sb->sb_ungetlen ); + ret = 1; + } else { + sb->sb_ungetlen = 0; + ret = -1; + } +#endif + break; + + default: + ret = sb->sb_iod->sbiod_io->sbi_ctrl( sb->sb_iod, opt, arg ); + break; + } + + return ret; +} + +int +ber_sockbuf_add_io( Sockbuf *sb, Sockbuf_IO *sbio, int layer, void *arg ) +{ + Sockbuf_IO_Desc *d, *p, **q; + + assert( sb != NULL ); + assert( SOCKBUF_VALID( sb ) ); + + if ( sbio == NULL ) { + return -1; + } + + q = &sb->sb_iod; + p = *q; + while ( p && p->sbiod_level > layer ) { + q = &p->sbiod_next; + p = *q; + } + + d = LBER_MALLOC( sizeof( *d ) ); + if ( d == NULL ) { + return -1; + } + + d->sbiod_level = layer; + d->sbiod_sb = sb; + d->sbiod_io = sbio; + memset( &d->sbiod_pvt, '\0', sizeof( d->sbiod_pvt ) ); + d->sbiod_next = p; + *q = d; + + if ( sbio->sbi_setup != NULL && ( sbio->sbi_setup( d, arg ) < 0 ) ) { + return -1; + } + + return 0; +} + +int +ber_sockbuf_remove_io( Sockbuf *sb, Sockbuf_IO *sbio, int layer ) +{ + Sockbuf_IO_Desc *p, **q; + + assert( sb != NULL ); + assert( SOCKBUF_VALID( sb ) ); + + if ( sb->sb_iod == NULL ) { + return -1; + } + + q = &sb->sb_iod; + while ( *q != NULL ) { + p = *q; + if ( layer == p->sbiod_level && p->sbiod_io == sbio ) { + if ( p->sbiod_io->sbi_remove != NULL && + p->sbiod_io->sbi_remove( p ) < 0 ) + { + return -1; + } + *q = p->sbiod_next; + LBER_FREE( p ); + break; + } + q = &p->sbiod_next; + } + + return 0; +} + +void +ber_pvt_sb_buf_init( Sockbuf_Buf *buf ) +{ + buf->buf_base = NULL; + buf->buf_ptr = 0; + buf->buf_end = 0; + buf->buf_size = 0; +} + +void +ber_pvt_sb_buf_destroy( Sockbuf_Buf *buf ) +{ + assert( buf != NULL); + + if (buf->buf_base) { + LBER_FREE( buf->buf_base ); + } + ber_pvt_sb_buf_init( buf ); +} + +int +ber_pvt_sb_grow_buffer( Sockbuf_Buf *buf, ber_len_t minsize ) +{ + ber_len_t pw; + char *p; + + assert( buf != NULL ); + + for ( pw = LBER_MIN_BUFF_SIZE; pw < minsize; pw <<= 1 ) { + if (pw > LBER_MAX_BUFF_SIZE) return -1; + } + + if ( buf->buf_size < pw ) { + p = LBER_REALLOC( buf->buf_base, pw ); + if ( p == NULL ) return -1; + buf->buf_base = p; + buf->buf_size = pw; + } + return 0; +} + +ber_len_t +ber_pvt_sb_copy_out( Sockbuf_Buf *sbb, char *buf, ber_len_t len ) +{ + ber_len_t max; + + assert( buf != NULL ); + assert( sbb != NULL ); +#if 0 + assert( sbb->buf_size > 0 ); +#endif + + max = sbb->buf_end - sbb->buf_ptr; + max = ( max < len) ? max : len; + if ( max ) { + AC_MEMCPY( buf, sbb->buf_base + sbb->buf_ptr, max ); + sbb->buf_ptr += max; + if ( sbb->buf_ptr >= sbb->buf_end ) { + sbb->buf_ptr = sbb->buf_end = 0; + } + } + return max; +} + +ber_slen_t +ber_pvt_sb_do_write( Sockbuf_IO_Desc *sbiod, Sockbuf_Buf *buf_out ) +{ + ber_len_t to_go; + ber_slen_t ret; + + assert( sbiod != NULL ); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + + to_go = buf_out->buf_end - buf_out->buf_ptr; + assert( to_go > 0 ); + + for(;;) { + ret = LBER_SBIOD_WRITE_NEXT( sbiod, buf_out->buf_base + + buf_out->buf_ptr, to_go ); +#ifdef EINTR + if ((ret<0) && (errno==EINTR)) continue; +#endif + break; + } + + if ( ret <= 0 ) return ret; + + buf_out->buf_ptr += ret; + if (buf_out->buf_ptr == buf_out->buf_end) { + buf_out->buf_end = buf_out->buf_ptr = 0; + } + + return ret; +} + +int +ber_pvt_socket_set_nonblock( ber_socket_t sd, int nb ) +{ +#ifdef HAVE_FCNTL + int flags = fcntl( sd, F_GETFL); + if( nb ) { + flags |= O_NONBLOCK; + } else { + flags &= ~O_NONBLOCK; + } + return fcntl( sd, F_SETFL, flags ); + +#elif defined( FIONBIO ) + ioctl_t status = nb ? 1 : 0; + return ioctl( sd, FIONBIO, &status ); +#endif +} + +int +ber_int_sb_init( Sockbuf *sb ) +{ + assert( sb != NULL); + + sb->sb_valid=LBER_VALID_SOCKBUF; + sb->sb_options = 0; + sb->sb_debug = ber_int_debug; + sb->sb_fd = AC_SOCKET_INVALID; + sb->sb_iod = NULL; + sb->sb_trans_needs_read = 0; + sb->sb_trans_needs_write = 0; + + assert( SOCKBUF_VALID( sb ) ); + return 0; +} + +int +ber_int_sb_close( Sockbuf *sb ) +{ + Sockbuf_IO_Desc *p; + + assert( sb != NULL); + + p = sb->sb_iod; + while ( p ) { + if ( p->sbiod_io->sbi_close && p->sbiod_io->sbi_close( p ) < 0 ) { + return -1; + } + p = p->sbiod_next; + } + + sb->sb_fd = AC_SOCKET_INVALID; + + return 0; +} + +int +ber_int_sb_destroy( Sockbuf *sb ) +{ + Sockbuf_IO_Desc *p; + + assert( sb != NULL); + assert( SOCKBUF_VALID( sb ) ); + + while ( sb->sb_iod ) { + p = sb->sb_iod->sbiod_next; + ber_sockbuf_remove_io( sb, sb->sb_iod->sbiod_io, + sb->sb_iod->sbiod_level ); + sb->sb_iod = p; + } + + return ber_int_sb_init( sb ); +} + +ber_slen_t +ber_int_sb_read( Sockbuf *sb, void *buf, ber_len_t len ) +{ + ber_slen_t ret; + + assert( buf != NULL ); + assert( sb != NULL); + assert( sb->sb_iod != NULL ); + assert( SOCKBUF_VALID( sb ) ); + + for (;;) { + ret = sb->sb_iod->sbiod_io->sbi_read( sb->sb_iod, buf, len ); + +#ifdef EINTR + if ( ( ret < 0 ) && ( errno == EINTR ) ) continue; +#endif + break; + } + + return ret; +} + +ber_slen_t +ber_int_sb_write( Sockbuf *sb, void *buf, ber_len_t len ) +{ + ber_slen_t ret; + + assert( buf != NULL ); + assert( sb != NULL); + assert( sb->sb_iod != NULL ); + assert( SOCKBUF_VALID( sb ) ); + + for (;;) { + ret = sb->sb_iod->sbiod_io->sbi_write( sb->sb_iod, buf, len ); + +#ifdef EINTR + if ( ( ret < 0 ) && ( errno == EINTR ) ) continue; +#endif + break; + } + + return ret; +} + +/* + * Support for TCP + */ + +static ber_slen_t +sb_stream_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len ) +{ + assert( sbiod != NULL); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + +#if defined(MACOS) +/* + * MacTCP/OpenTransport + */ + return tcpread( sbiod->sbiod_sb->sb_fd, 0, (unsigned char *)buf, + len, NULL ); + +#elif defined( HAVE_PCNFS ) || \ + defined( HAVE_WINSOCK ) || defined ( __BEOS__ ) +/* + * PCNFS (under DOS) + */ +/* + * Windows Socket API (under DOS/Windows 3.x) + */ +/* + * 32-bit Windows Socket API (under Windows NT or Windows 95) + */ + return recv( sbiod->sbiod_sb->sb_fd, buf, len, 0 ); + +#elif defined( HAVE_NCSA ) +/* + * NCSA Telnet TCP/IP stack (under DOS) + */ + return nread( sbiod->sbiod_sb->sb_fd, buf, len ); + +#else + return read( sbiod->sbiod_sb->sb_fd, buf, len ); +#endif +} + +static ber_slen_t +sb_stream_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len ) +{ + assert( sbiod != NULL); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + +#if defined(MACOS) +/* + * MacTCP/OpenTransport + */ +#define MAX_WRITE 65535 + return tcpwrite( sbiod->sbiod_sb->sb_fd, (unsigned char *)buf, + (len<MAX_WRITE) ? len : MAX_WRITE ); + +#elif defined( HAVE_PCNFS) \ + || defined( HAVE_WINSOCK) || defined ( __BEOS__ ) +/* + * PCNFS (under DOS) + */ +/* + * Windows Socket API (under DOS/Windows 3.x) + */ +/* + * 32-bit Windows Socket API (under Windows NT or Windows 95) + */ + return send( sbiod->sbiod_sb->sb_fd, buf, len, 0 ); + +#elif defined(HAVE_NCSA) + return netwrite( sbiod->sbiod_sb->sb_fd, buf, len ); + +#elif defined(VMS) +/* + * VMS -- each write must be 64K or smaller + */ +#define MAX_WRITE 65535 + return write( sbiod->sbiod_sb->sb_fd, buf, + (len<MAX_WRITE) ? len : MAX_WRITE); +#else + return write( sbiod->sbiod_sb->sb_fd, buf, len ); +#endif +} + +static int +sb_stream_close( Sockbuf_IO_Desc *sbiod ) +{ + assert( sbiod != NULL ); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + if ( sbiod->sbiod_sb->sb_fd != AC_SOCKET_INVALID ) + tcp_close( sbiod->sbiod_sb->sb_fd ); + return 0; +} + +/* The argument is a pointer to the socket descriptor */ +static int +sb_stream_setup( Sockbuf_IO_Desc *sbiod, void *arg ) { + assert( sbiod != NULL ); + + if ( arg != NULL ) { + sbiod->sbiod_sb->sb_fd = *((int *)arg); + } + return 0; +} + +static int +sb_stream_ctrl( Sockbuf_IO_Desc *sbiod, int opt, void *arg ) { + /* This is an end IO descriptor */ + return 0; +} + +Sockbuf_IO ber_sockbuf_io_tcp = { + sb_stream_setup, /* sbi_setup */ + NULL, /* sbi_remove */ + sb_stream_ctrl, /* sbi_ctrl */ + sb_stream_read, /* sbi_read */ + sb_stream_write, /* sbi_write */ + sb_stream_close /* sbi_close */ +}; + + +/* + * Support for readahead (UDP needs it) + */ + +static int +sb_rdahead_setup( Sockbuf_IO_Desc *sbiod, void *arg ) +{ + Sockbuf_Buf *p; + + assert( sbiod != NULL ); + + p = LBER_MALLOC( sizeof( *p ) ); + if ( p == NULL ) return -1; + + ber_pvt_sb_buf_init( p ); + + if ( arg == NULL ) { + ber_pvt_sb_grow_buffer( p, LBER_DEFAULT_READAHEAD ); + } else { + ber_pvt_sb_grow_buffer( p, *((int *)arg) ); + } + + sbiod->sbiod_pvt = p; + return 0; +} + +static int +sb_rdahead_remove( Sockbuf_IO_Desc *sbiod ) +{ + Sockbuf_Buf *p; + + assert( sbiod != NULL ); + + p = (Sockbuf_Buf *)sbiod->sbiod_pvt; + + if ( p->buf_ptr != p->buf_end ) return -1; + + ber_pvt_sb_buf_destroy( (Sockbuf_Buf *)(sbiod->sbiod_pvt) ); + LBER_FREE( sbiod->sbiod_pvt ); + sbiod->sbiod_pvt = NULL; + + return 0; +} + +static ber_slen_t +sb_rdahead_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len ) +{ + Sockbuf_Buf *p; + ber_slen_t bufptr = 0, ret, max; + + assert( sbiod != NULL ); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + assert( sbiod->sbiod_next != NULL ); + + p = (Sockbuf_Buf *)sbiod->sbiod_pvt; + + assert( p->buf_size > 0 ); + + /* Are there anything left in the buffer? */ + ret = ber_pvt_sb_copy_out( p, buf, len ); + bufptr += ret; + len -= ret; + + if ( len == 0 ) return bufptr; + + max = p->buf_size - p->buf_end; + ret = 0; + while ( max > 0 ) { + ret = LBER_SBIOD_READ_NEXT( sbiod, p->buf_base + p->buf_end, + max ); +#ifdef EINTR + if ( ( ret < 0 ) && ( errno == EINTR ) ) continue; +#endif + break; + } + + if ( ret < 0 ) { + return ( bufptr ? bufptr : ret ); + } + + p->buf_end += ret; + bufptr += ber_pvt_sb_copy_out( p, (char *) buf + bufptr, len ); + return bufptr; +} + +static ber_slen_t +sb_rdahead_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len ) +{ + assert( sbiod != NULL ); + assert( sbiod->sbiod_next != NULL ); + + return LBER_SBIOD_WRITE_NEXT( sbiod, buf, len ); +} + +static int +sb_rdahead_close( Sockbuf_IO_Desc *sbiod ) +{ + assert( sbiod != NULL ); + + /* Just erase the buffer */ + ber_pvt_sb_buf_destroy((Sockbuf_Buf *)sbiod->sbiod_pvt); + return 0; +} + +static int +sb_rdahead_ctrl( Sockbuf_IO_Desc *sbiod, int opt, void *arg ) +{ + Sockbuf_Buf *p; + + p = (Sockbuf_Buf *)sbiod->sbiod_pvt; + + if ( opt == LBER_SB_OPT_DATA_READY ) { + if ( p->buf_ptr != p->buf_end ) { + return 1; + } + + } else if ( opt == LBER_SB_OPT_SET_READAHEAD ) { + if ( p->buf_size >= *((ber_len_t *)arg) ) { + return 0; + } + return ( ber_pvt_sb_grow_buffer( p, *((int *)arg) ) ? + -1 : 1 ); + } + + return LBER_SBIOD_CTRL_NEXT( sbiod, opt, arg ); +} + +Sockbuf_IO ber_sockbuf_io_readahead = { + sb_rdahead_setup, /* sbi_setup */ + sb_rdahead_remove, /* sbi_remove */ + sb_rdahead_ctrl, /* sbi_ctrl */ + sb_rdahead_read, /* sbi_read */ + sb_rdahead_write, /* sbi_write */ + sb_rdahead_close /* sbi_close */ +}; + +/* + * Support for simple file IO + */ + +static ber_slen_t +sb_fd_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len ) +{ + assert( sbiod != NULL); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + +#ifdef LDAP_PF_LOCAL_SENDMSG + if ( sbiod->sbiod_sb->sb_ungetlen ) { + ber_len_t blen = sbiod->sbiod_sb->sb_ungetlen; + if ( blen > len ) + blen = len; + AC_MEMCPY( buf, sbiod->sbiod_sb->sb_ungetbuf, blen ); + buf = (char *) buf + blen; + len -= blen; + sbiod->sbiod_sb->sb_ungetlen -= blen; + if ( sbiod->sbiod_sb->sb_ungetlen ) { + AC_MEMCPY( sbiod->sbiod_sb->sb_ungetbuf, + sbiod->sbiod_sb->sb_ungetbuf+blen, + sbiod->sbiod_sb->sb_ungetlen ); + } + if ( len == 0 ) + return blen; + } +#endif + return read( sbiod->sbiod_sb->sb_fd, buf, len ); +} + +static ber_slen_t +sb_fd_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len ) +{ + assert( sbiod != NULL); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + + return write( sbiod->sbiod_sb->sb_fd, buf, len ); +} + +static int +sb_fd_close( Sockbuf_IO_Desc *sbiod ) +{ + assert( sbiod != NULL ); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + + if ( sbiod->sbiod_sb->sb_fd != AC_SOCKET_INVALID ) + close( sbiod->sbiod_sb->sb_fd ); + return 0; +} + +/* The argument is a pointer to the file descriptor */ +static int +sb_fd_setup( Sockbuf_IO_Desc *sbiod, void *arg ) { + assert( sbiod != NULL ); + + if ( arg != NULL ) + sbiod->sbiod_sb->sb_fd = *((int *)arg); + return 0; +} + +static int +sb_fd_ctrl( Sockbuf_IO_Desc *sbiod, int opt, void *arg ) { + /* This is an end IO descriptor */ + return 0; +} + +Sockbuf_IO ber_sockbuf_io_fd = { + sb_fd_setup, /* sbi_setup */ + NULL, /* sbi_remove */ + sb_fd_ctrl, /* sbi_ctrl */ + sb_fd_read, /* sbi_read */ + sb_fd_write, /* sbi_write */ + sb_fd_close /* sbi_close */ +}; + +/* + * Debugging layer + */ + +static int +sb_debug_setup( Sockbuf_IO_Desc *sbiod, void *arg ) +{ + assert( sbiod != NULL ); + + if ( arg == NULL ) arg = "sockbuf_"; + + sbiod->sbiod_pvt = LBER_MALLOC( strlen( arg ) + 1 ); + if ( sbiod->sbiod_pvt == NULL ) return -1; + + strcpy( (char *)sbiod->sbiod_pvt, (char *)arg ); + return 0; +} + +static int +sb_debug_remove( Sockbuf_IO_Desc *sbiod ) +{ + assert( sbiod != NULL ); + assert( sbiod->sbiod_pvt != NULL ); + + LBER_FREE( sbiod->sbiod_pvt ); + sbiod->sbiod_pvt = NULL; + return 0; +} + +static int +sb_debug_ctrl( Sockbuf_IO_Desc *sbiod, int opt, void *arg ) +{ + return LBER_SBIOD_CTRL_NEXT( sbiod, opt, arg ); +} + +static ber_slen_t +sb_debug_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len ) +{ + ber_slen_t ret; + char ebuf[128]; + + ret = LBER_SBIOD_READ_NEXT( sbiod, buf, len ); + if (sbiod->sbiod_sb->sb_debug & LDAP_DEBUG_PACKETS) { + int err = sock_errno(); + if ( ret < 0 ) { + ber_log_printf( LDAP_DEBUG_PACKETS, sbiod->sbiod_sb->sb_debug, + "%sread: want=%ld error=%s\n", (char *)sbiod->sbiod_pvt, + (long)len, AC_STRERROR_R( err, ebuf, sizeof ebuf ) ); + } else { + ber_log_printf( LDAP_DEBUG_PACKETS, sbiod->sbiod_sb->sb_debug, + "%sread: want=%ld, got=%ld\n", (char *)sbiod->sbiod_pvt, + (long)len, (long)ret ); + ber_log_bprint( LDAP_DEBUG_PACKETS, sbiod->sbiod_sb->sb_debug, + (const char *)buf, ret ); + } + sock_errset(err); + } + return ret; +} + +static ber_slen_t +sb_debug_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len ) +{ + ber_slen_t ret; + char ebuf[128]; + + ret = LBER_SBIOD_WRITE_NEXT( sbiod, buf, len ); + if (sbiod->sbiod_sb->sb_debug & LDAP_DEBUG_PACKETS) { + int err = sock_errno(); + if ( ret < 0 ) { + ber_log_printf( LDAP_DEBUG_PACKETS, sbiod->sbiod_sb->sb_debug, + "%swrite: want=%ld error=%s\n", + (char *)sbiod->sbiod_pvt, (long)len, + AC_STRERROR_R( err, ebuf, sizeof ebuf ) ); + } else { + ber_log_printf( LDAP_DEBUG_PACKETS, sbiod->sbiod_sb->sb_debug, + "%swrite: want=%ld, written=%ld\n", + (char *)sbiod->sbiod_pvt, (long)len, (long)ret ); + ber_log_bprint( LDAP_DEBUG_PACKETS, sbiod->sbiod_sb->sb_debug, + (const char *)buf, ret ); + } + sock_errset(err); + } + + return ret; +} + +Sockbuf_IO ber_sockbuf_io_debug = { + sb_debug_setup, /* sbi_setup */ + sb_debug_remove, /* sbi_remove */ + sb_debug_ctrl, /* sbi_ctrl */ + sb_debug_read, /* sbi_read */ + sb_debug_write, /* sbi_write */ + NULL /* sbi_close */ +}; + +#ifdef LDAP_CONNECTIONLESS + +/* + * Support for UDP (CLDAP) + * + * All I/O at this level must be atomic. For ease of use, the sb_readahead + * must be used above this module. All data reads and writes are prefixed + * with a sockaddr_storage containing the address of the remote entity. Upper levels + * must read and write this sockaddr_storage before doing the usual ber_printf/scanf + * operations on LDAP messages. + */ + +static int +sb_dgram_setup( Sockbuf_IO_Desc *sbiod, void *arg ) +{ + assert( sbiod != NULL); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + + if ( arg != NULL ) sbiod->sbiod_sb->sb_fd = *((int *)arg); + return 0; +} + +static ber_slen_t +sb_dgram_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len ) +{ + ber_slen_t rc; + ber_socklen_t addrlen; + struct sockaddr *src; + + assert( sbiod != NULL ); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + assert( buf != NULL ); + + addrlen = sizeof( struct sockaddr_storage ); + src = buf; + buf = (char *) buf + addrlen; + len -= addrlen; + rc = recvfrom( sbiod->sbiod_sb->sb_fd, buf, len, 0, src, &addrlen ); + + return rc > 0 ? rc+sizeof(struct sockaddr_storage) : rc; +} + +static ber_slen_t +sb_dgram_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len ) +{ + ber_slen_t rc; + struct sockaddr *dst; + socklen_t dstsize; + + assert( sbiod != NULL ); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + assert( buf != NULL ); + + dst = buf; + buf = (char *) buf + sizeof( struct sockaddr_storage ); + len -= sizeof( struct sockaddr_storage ); + dstsize = dst->sa_family == AF_INET ? sizeof( struct sockaddr_in ) +#ifdef LDAP_PF_INET6 + : dst->sa_family == AF_INET6 ? sizeof( struct sockaddr_in6 ) +#endif + : sizeof( struct sockaddr_storage ); + rc = sendto( sbiod->sbiod_sb->sb_fd, buf, len, 0, dst, dstsize ); + + if ( rc < 0 ) return -1; + + /* fake error if write was not atomic */ + if (rc < len) { +# ifdef EMSGSIZE + errno = EMSGSIZE; +# endif + return -1; + } + rc = len + sizeof(struct sockaddr_storage); + return rc; +} + +static int +sb_dgram_close( Sockbuf_IO_Desc *sbiod ) +{ + assert( sbiod != NULL ); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + + if ( sbiod->sbiod_sb->sb_fd != AC_SOCKET_INVALID ) + tcp_close( sbiod->sbiod_sb->sb_fd ); + return 0; +} + +static int +sb_dgram_ctrl( Sockbuf_IO_Desc *sbiod, int opt, void *arg ) +{ + /* This is an end IO descriptor */ + return 0; +} + +Sockbuf_IO ber_sockbuf_io_udp = +{ + sb_dgram_setup, /* sbi_setup */ + NULL, /* sbi_remove */ + sb_dgram_ctrl, /* sbi_ctrl */ + sb_dgram_read, /* sbi_read */ + sb_dgram_write, /* sbi_write */ + sb_dgram_close /* sbi_close */ +}; + +#endif /* LDAP_CONNECTIONLESS */ diff --git a/libraries/liblber/stdio.c b/libraries/liblber/stdio.c new file mode 100644 index 0000000..064096b --- /dev/null +++ b/libraries/liblber/stdio.c @@ -0,0 +1,243 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/stdarg.h> +#include <ac/string.h> +#include <ac/ctype.h> +#include <lutil.h> + +#if !defined(HAVE_VSNPRINTF) && !defined(HAVE_EBCDIC) +/* Write at most n characters to the buffer in str, return the + * number of chars written or -1 if the buffer would have been + * overflowed. + * + * This is portable to any POSIX-compliant system. We use pipe() + * to create a valid file descriptor, and then fdopen() it to get + * a valid FILE pointer. The user's buffer and size are assigned + * to the FILE pointer using setvbuf. Then we close the read side + * of the pipe to invalidate the descriptor. + * + * If the write arguments all fit into size n, the write will + * return successfully. If the write is too large, the stdio + * buffer will need to be flushed to the underlying file descriptor. + * The flush will fail because it is attempting to write to a + * broken pipe, and the write will be terminated. + * -- hyc, 2002-07-19 + */ +/* This emulation uses vfprintf; on OS/390 we're also emulating + * that function so it's more efficient just to have a separate + * version of vsnprintf there. + */ +#include <ac/signal.h> +int ber_pvt_vsnprintf( char *str, size_t n, const char *fmt, va_list ap ) +{ + int fds[2], res; + FILE *f; + RETSIGTYPE (*sig)(); + + if (pipe( fds )) return -1; + + f = fdopen( fds[1], "w" ); + if ( !f ) { + close( fds[1] ); + close( fds[0] ); + return -1; + } + setvbuf( f, str, _IOFBF, n ); + sig = signal( SIGPIPE, SIG_IGN ); + close( fds[0] ); + + res = vfprintf( f, fmt, ap ); + + fclose( f ); + signal( SIGPIPE, sig ); + if ( res > 0 && res < n ) { + res = vsprintf( str, fmt, ap ); + } + return res; +} +#endif + +#ifndef HAVE_SNPRINTF +int ber_pvt_snprintf( char *str, size_t n, const char *fmt, ... ) +{ + va_list ap; + int res; + + va_start( ap, fmt ); + res = vsnprintf( str, n, fmt, ap ); + va_end( ap ); + return res; +} +#endif /* !HAVE_SNPRINTF */ + +#ifdef HAVE_EBCDIC +/* stdio replacements with ASCII/EBCDIC translation for OS/390. + * The OS/390 port depends on the CONVLIT compiler option being + * used to force character and string literals to be compiled in + * ISO8859-1, and the __LIBASCII cpp symbol to be defined to use the + * OS/390 ASCII-compatibility library. This library only supplies + * an ASCII version of sprintf, so other needed functions are + * provided here. + * + * All of the internal character manipulation is done in ASCII, + * but file I/O is EBCDIC, so we catch any stdio reading/writing + * of files here and do the translations. + */ + +#undef fputs +#undef fgets + +char *ber_pvt_fgets( char *s, int n, FILE *fp ) +{ + s = (char *)fgets( s, n, fp ); + if ( s ) __etoa( s ); + return s; +} + +int ber_pvt_fputs( const char *str, FILE *fp ) +{ + char buf[8192]; + + strncpy( buf, str, sizeof(buf) ); + __atoe( buf ); + return fputs( buf, fp ); +} + +/* The __LIBASCII doesn't include a working vsprintf, so we make do + * using just sprintf. This is a very simplistic parser that looks for + * format strings and uses sprintf to process them one at a time. + * Literal text is just copied straight to the destination. + * The result is appended to the destination string. The parser + * recognizes field-width specifiers and the 'l' qualifier; it + * may need to be extended to recognize other qualifiers but so + * far this seems to be enough. + */ +int ber_pvt_vsnprintf( char *str, size_t n, const char *fmt, va_list ap ) +{ + char *ptr, *pct, *s2, *f2, *end; + char fm2[64]; + int len, rem; + + ptr = (char *)fmt; + s2 = str; + fm2[0] = '%'; + if (n) { + end = str + n; + } else { + end = NULL; + } + + for (pct = strchr(ptr, '%'); pct; pct = strchr(ptr, '%')) { + len = pct-ptr; + if (end) { + rem = end-s2; + if (rem < 1) return -1; + if (rem < len) len = rem; + } + s2 = lutil_strncopy( s2, ptr, len ); + /* Did we cheat the length above? If so, bail out */ + if (len < pct-ptr) return -1; + for (pct++, f2 = fm2+1; isdigit(*pct);) *f2++ = *pct++; + if (*pct == 'l') *f2++ = *pct++; + if (*pct == '%') { + *s2++ = '%'; + } else { + *f2++ = *pct; + *f2 = '\0'; + if (*pct == 's') { + char *ss = va_arg(ap, char *); + /* Attempt to limit sprintf output. This + * may be thrown off if field widths were + * specified for this string. + * + * If it looks like the string is too + * long for the remaining buffer, bypass + * sprintf and just copy what fits, then + * quit. + */ + if (end && strlen(ss) > (rem=end-s2)) { + strncpy(s2, ss, rem); + return -1; + } else { + s2 += sprintf(s2, fm2, ss); + } + } else { + s2 += sprintf(s2, fm2, va_arg(ap, int)); + } + } + ptr = pct + 1; + } + if (end) { + rem = end-s2; + if (rem > 0) { + len = strlen(ptr); + s2 = lutil_strncopy( s2, ptr, rem ); + rem -= len; + } + if (rem < 0) return -1; + } else { + s2 = lutil_strcopy( s2, ptr ); + } + return s2 - str; +} + +int ber_pvt_vsprintf( char *str, const char *fmt, va_list ap ) +{ + return vsnprintf( str, 0, fmt, ap ); +} + +/* The fixed buffer size here is a problem, we don't know how + * to flush the buffer and keep printing if the msg is too big. + * Hopefully we never try to write something bigger than this + * in a log msg... + */ +int ber_pvt_vfprintf( FILE *fp, const char *fmt, va_list ap ) +{ + char buf[8192]; + int res; + + vsnprintf( buf, sizeof(buf), fmt, ap ); + __atoe( buf ); + res = fputs( buf, fp ); + if (res == EOF) res = -1; + return res; +} + +int ber_pvt_printf( const char *fmt, ... ) +{ + va_list ap; + int res; + + va_start( ap, fmt ); + res = ber_pvt_vfprintf( stdout, fmt, ap ); + va_end( ap ); + return res; +} + +int ber_pvt_fprintf( FILE *fp, const char *fmt, ... ) +{ + va_list ap; + int res; + + va_start( ap, fmt ); + res = ber_pvt_vfprintf( fp, fmt, ap ); + va_end( ap ); + return res; +} +#endif diff --git a/libraries/libldap/Makefile.in b/libraries/libldap/Makefile.in new file mode 100644 index 0000000..92856d2 --- /dev/null +++ b/libraries/libldap/Makefile.in @@ -0,0 +1,84 @@ +# Makefile.in for LDAP -lldap +# $OpenLDAP$ +## This work is part of OpenLDAP Software <http://www.openldap.org/>. +## +## Copyright 1998-2021 The OpenLDAP Foundation. +## All rights reserved. +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted only as authorized by the OpenLDAP +## Public License. +## +## A copy of this license is available in the file LICENSE in the +## top-level directory of the distribution or, alternatively, at +## <http://www.OpenLDAP.org/license.html>. + +LIBRARY = libldap.la + +PROGRAMS = apitest dntest ftest ltest urltest + +SRCS = bind.c open.c result.c error.c compare.c search.c \ + controls.c messages.c references.c extended.c cyrus.c \ + modify.c add.c modrdn.c delete.c abandon.c \ + sasl.c gssapi.c sbind.c unbind.c cancel.c \ + filter.c free.c sort.c passwd.c whoami.c \ + getdn.c getentry.c getattr.c getvalues.c addentry.c \ + request.c os-ip.c url.c pagectrl.c sortctrl.c vlvctrl.c \ + init.c options.c print.c string.c util-int.c schema.c \ + charray.c os-local.c dnssrv.c utf-8.c utf-8-conv.c \ + tls2.c tls_o.c tls_g.c tls_m.c \ + turn.c ppolicy.c dds.c txn.c ldap_sync.c stctrl.c \ + assertion.c deref.c ldif.c fetch.c + +OBJS = bind.lo open.lo result.lo error.lo compare.lo search.lo \ + controls.lo messages.lo references.lo extended.lo cyrus.lo \ + modify.lo add.lo modrdn.lo delete.lo abandon.lo \ + sasl.lo gssapi.lo sbind.lo unbind.lo cancel.lo \ + filter.lo free.lo sort.lo passwd.lo whoami.lo \ + getdn.lo getentry.lo getattr.lo getvalues.lo addentry.lo \ + request.lo os-ip.lo url.lo pagectrl.lo sortctrl.lo vlvctrl.lo \ + init.lo options.lo print.lo string.lo util-int.lo schema.lo \ + charray.lo os-local.lo dnssrv.lo utf-8.lo utf-8-conv.lo \ + tls2.lo tls_o.lo tls_g.lo tls_m.lo \ + turn.lo ppolicy.lo dds.lo txn.lo ldap_sync.lo stctrl.lo \ + assertion.lo deref.lo ldif.lo fetch.lo + +LDAP_INCDIR= ../../include +LDAP_LIBDIR= ../../libraries + +LIB_DEFS = -DLDAP_LIBRARY + +XLIBS = $(LIBRARY) $(LDAP_LIBLBER_LA) $(LDAP_LIBLUTIL_A) +XXLIBS = $(SECURITY_LIBS) $(LUTIL_LIBS) +NT_LINK_LIBS = $(LDAP_LIBLBER_LA) $(AC_LIBS) $(SECURITY_LIBS) +UNIX_LINK_LIBS = $(LDAP_LIBLBER_LA) $(AC_LIBS) $(SECURITY_LIBS) + +apitest: $(XLIBS) apitest.o + $(LTLINK) -o $@ apitest.o $(LIBS) +dntest: $(XLIBS) dntest.o + $(LTLINK) -o $@ dntest.o $(LIBS) +ftest: $(XLIBS) ftest.o + $(LTLINK) -o $@ ftest.o $(LIBS) +ltest: $(XLIBS) test.o + $(LTLINK) -o $@ test.o $(LIBS) +urltest: $(XLIBS) urltest.o + $(LTLINK) -o $@ urltest.o $(LIBS) + +CFFILES=ldap.conf + +install-local: $(CFFILES) FORCE + -$(MKDIR) $(DESTDIR)$(libdir) + $(LTINSTALL) $(INSTALLFLAGS) -m 644 $(LIBRARY) $(DESTDIR)$(libdir) + $(LTFINISH) $(DESTDIR)$(libdir) + -$(MKDIR) $(DESTDIR)$(sysconfdir) + @for i in $(CFFILES); do \ + if test ! -f $(DESTDIR)$(sysconfdir)/$$i; then \ + echo "installing $$i in $(sysconfdir)"; \ + echo "$(INSTALL) $(INSTALLFLAGS) -m 644 $(srcdir)/$$i $(DESTDIR)$(sysconfdir)/$$i"; \ + $(INSTALL) $(INSTALLFLAGS) -m 644 $(srcdir)/$$i $(DESTDIR)$(sysconfdir)/$$i; \ + else \ + echo "PRESERVING EXISTING CONFIGURATION FILE $(sysconfdir)/$$i" ; \ + fi; \ + $(INSTALL) $(INSTALLFLAGS) -m 644 $(srcdir)/$$i $(DESTDIR)$(sysconfdir)/$$i.default; \ + done + diff --git a/libraries/libldap/abandon.c b/libraries/libldap/abandon.c new file mode 100644 index 0000000..1b8912e --- /dev/null +++ b/libraries/libldap/abandon.c @@ -0,0 +1,460 @@ +/* abandon.c */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +/* + * An abandon request looks like this: + * AbandonRequest ::= [APPLICATION 16] MessageID + * and has no response. (Source: RFC 4511) + */ +#include "lutil.h" + +static int +do_abandon( + LDAP *ld, + ber_int_t origid, + ber_int_t msgid, + LDAPControl **sctrls, + int sendabandon ); + +/* + * ldap_abandon_ext - perform an ldap extended abandon operation. + * + * Parameters: + * ld LDAP descriptor + * msgid The message id of the operation to abandon + * scntrls Server Controls + * ccntrls Client Controls + * + * ldap_abandon_ext returns a LDAP error code. + * (LDAP_SUCCESS if everything went ok) + * + * Example: + * ldap_abandon_ext( ld, msgid, scntrls, ccntrls ); + */ +int +ldap_abandon_ext( + LDAP *ld, + int msgid, + LDAPControl **sctrls, + LDAPControl **cctrls ) +{ + int rc; + + Debug( LDAP_DEBUG_TRACE, "ldap_abandon_ext %d\n", msgid, 0, 0 ); + + /* check client controls */ + LDAP_MUTEX_LOCK( &ld->ld_req_mutex ); + + rc = ldap_int_client_controls( ld, cctrls ); + if ( rc == LDAP_SUCCESS ) { + rc = do_abandon( ld, msgid, msgid, sctrls, 1 ); + } + + LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex ); + + return rc; +} + + +/* + * ldap_abandon - perform an ldap abandon operation. Parameters: + * + * ld LDAP descriptor + * msgid The message id of the operation to abandon + * + * ldap_abandon returns 0 if everything went ok, -1 otherwise. + * + * Example: + * ldap_abandon( ld, msgid ); + */ +int +ldap_abandon( LDAP *ld, int msgid ) +{ + Debug( LDAP_DEBUG_TRACE, "ldap_abandon %d\n", msgid, 0, 0 ); + return ldap_abandon_ext( ld, msgid, NULL, NULL ) == LDAP_SUCCESS + ? 0 : -1; +} + + +int +ldap_pvt_discard( + LDAP *ld, + ber_int_t msgid ) +{ + int rc; + + LDAP_MUTEX_LOCK( &ld->ld_req_mutex ); + rc = do_abandon( ld, msgid, msgid, NULL, 0 ); + LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex ); + return rc; +} + +static int +do_abandon( + LDAP *ld, + ber_int_t origid, + ber_int_t msgid, + LDAPControl **sctrls, + int sendabandon ) +{ + BerElement *ber; + int i, err; + Sockbuf *sb; + LDAPRequest *lr; + + Debug( LDAP_DEBUG_TRACE, "do_abandon origid %d, msgid %d\n", + origid, msgid, 0 ); + + /* find the request that we are abandoning */ +start_again:; + lr = ld->ld_requests; + while ( lr != NULL ) { + /* this message */ + if ( lr->lr_msgid == msgid ) { + break; + } + + /* child: abandon it */ + if ( lr->lr_origid == msgid && !lr->lr_abandoned ) { + (void)do_abandon( ld, lr->lr_origid, lr->lr_msgid, + sctrls, sendabandon ); + + /* restart, as lr may now be dangling... */ + goto start_again; + } + + lr = lr->lr_next; + } + + if ( lr != NULL ) { + if ( origid == msgid && lr->lr_parent != NULL ) { + /* don't let caller abandon child requests! */ + ld->ld_errno = LDAP_PARAM_ERROR; + return( LDAP_PARAM_ERROR ); + } + if ( lr->lr_status != LDAP_REQST_INPROGRESS ) { + /* no need to send abandon message */ + sendabandon = 0; + } + } + + /* ldap_msgdelete locks the res_mutex. Give up the req_mutex + * while we're in there. + */ + LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex ); + err = ldap_msgdelete( ld, msgid ); + LDAP_MUTEX_LOCK( &ld->ld_req_mutex ); + if ( err == 0 ) { + ld->ld_errno = LDAP_SUCCESS; + return LDAP_SUCCESS; + } + + /* fetch again the request that we are abandoning */ + if ( lr != NULL ) { + for ( lr = ld->ld_requests; lr != NULL; lr = lr->lr_next ) { + /* this message */ + if ( lr->lr_msgid == msgid ) { + break; + } + } + } + + err = 0; + if ( sendabandon ) { + if ( ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, NULL ) == -1 ) { + /* not connected */ + err = -1; + ld->ld_errno = LDAP_SERVER_DOWN; + + } else if ( ( ber = ldap_alloc_ber_with_options( ld ) ) == NULL ) { + /* BER element allocation failed */ + err = -1; + ld->ld_errno = LDAP_NO_MEMORY; + + } else { + /* + * We already have the mutex in LDAP_R_COMPILE, so + * don't try to get it again. + * LDAP_NEXT_MSGID(ld, i); + */ + + LDAP_NEXT_MSGID(ld, i); +#ifdef LDAP_CONNECTIONLESS + if ( LDAP_IS_UDP(ld) ) { + struct sockaddr_storage sa = {0}; + /* dummy, filled with ldo_peer in request.c */ + err = ber_write( ber, (char *) &sa, sizeof(sa), 0 ); + } + if ( LDAP_IS_UDP(ld) && ld->ld_options.ldo_version == + LDAP_VERSION2 ) + { + char *dn; + LDAP_MUTEX_LOCK( &ld->ld_options.ldo_mutex ); + dn = ld->ld_options.ldo_cldapdn; + if (!dn) dn = ""; + err = ber_printf( ber, "{isti", /* '}' */ + i, dn, + LDAP_REQ_ABANDON, msgid ); + LDAP_MUTEX_UNLOCK( &ld->ld_options.ldo_mutex ); + } else +#endif + { + /* create a message to send */ + err = ber_printf( ber, "{iti", /* '}' */ + i, + LDAP_REQ_ABANDON, msgid ); + } + + if ( err == -1 ) { + /* encoding error */ + ld->ld_errno = LDAP_ENCODING_ERROR; + + } else { + /* Put Server Controls */ + if ( ldap_int_put_controls( ld, sctrls, ber ) + != LDAP_SUCCESS ) + { + err = -1; + + } else { + /* close '{' */ + err = ber_printf( ber, /*{*/ "N}" ); + + if ( err == -1 ) { + /* encoding error */ + ld->ld_errno = LDAP_ENCODING_ERROR; + } + } + } + + if ( err == -1 ) { + ber_free( ber, 1 ); + + } else { + /* send the message */ + if ( lr != NULL ) { + assert( lr->lr_conn != NULL ); + sb = lr->lr_conn->lconn_sb; + } else { + sb = ld->ld_sb; + } + + if ( ber_flush2( sb, ber, LBER_FLUSH_FREE_ALWAYS ) != 0 ) { + ld->ld_errno = LDAP_SERVER_DOWN; + err = -1; + } else { + err = 0; + } + } + } + } + + if ( lr != NULL ) { + LDAPConn *lc; + int freeconn = 0; + if ( sendabandon || lr->lr_status == LDAP_REQST_WRITING ) { + freeconn = 1; + lc = lr->lr_conn; + } + if ( origid == msgid ) { + ldap_free_request( ld, lr ); + + } else { + lr->lr_abandoned = 1; + } + + if ( freeconn ) { + /* release ld_req_mutex while grabbing ld_conn_mutex to + * prevent deadlock. + */ + LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex ); + LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); + ldap_free_connection( ld, lc, 0, 1 ); + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); + LDAP_MUTEX_LOCK( &ld->ld_req_mutex ); + } + } + + LDAP_MUTEX_LOCK( &ld->ld_abandon_mutex ); + + /* use bisection */ + i = 0; + if ( ld->ld_nabandoned == 0 || + ldap_int_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, &i ) == 0 ) + { + ldap_int_bisect_insert( &ld->ld_abandoned, &ld->ld_nabandoned, msgid, i ); + } + + if ( err != -1 ) { + ld->ld_errno = LDAP_SUCCESS; + } + + LDAP_MUTEX_UNLOCK( &ld->ld_abandon_mutex ); + return( ld->ld_errno ); +} + +/* + * ldap_int_bisect_find + * + * args: + * v: array of length n (in) + * n: length of array v (in) + * id: value to look for (in) + * idxp: pointer to location of value/insert point + * + * return: + * 0: not found + * 1: found + * -1: error + */ +int +ldap_int_bisect_find( ber_int_t *v, ber_len_t n, ber_int_t id, int *idxp ) +{ + int begin, + end, + rc = 0; + + assert( id >= 0 ); + + begin = 0; + end = n - 1; + + if ( n <= 0 || id < v[ begin ] ) { + *idxp = 0; + + } else if ( id > v[ end ] ) { + *idxp = n; + + } else { + int pos; + ber_int_t curid; + + do { + pos = (begin + end)/2; + curid = v[ pos ]; + + if ( id < curid ) { + end = pos - 1; + + } else if ( id > curid ) { + begin = ++pos; + + } else { + /* already abandoned? */ + rc = 1; + break; + } + } while ( end >= begin ); + + *idxp = pos; + } + + return rc; +} + +/* + * ldap_int_bisect_insert + * + * args: + * vp: pointer to array of length *np (in/out) + * np: pointer to length of array *vp (in/out) + * id: value to insert (in) + * idx: location of insert point (as computed by ldap_int_bisect_find()) + * + * return: + * 0: inserted + * -1: error + */ +int +ldap_int_bisect_insert( ber_int_t **vp, ber_len_t *np, int id, int idx ) +{ + ber_int_t *v; + ber_len_t n; + int i; + + assert( vp != NULL ); + assert( np != NULL ); + assert( idx >= 0 ); + assert( (unsigned) idx <= *np ); + + n = *np; + + v = ber_memrealloc( *vp, sizeof( ber_int_t ) * ( n + 1 ) ); + if ( v == NULL ) { + return -1; + } + *vp = v; + + for ( i = n; i > idx; i-- ) { + v[ i ] = v[ i - 1 ]; + } + v[ idx ] = id; + ++(*np); + + return 0; +} + +/* + * ldap_int_bisect_delete + * + * args: + * vp: pointer to array of length *np (in/out) + * np: pointer to length of array *vp (in/out) + * id: value to delete (in) + * idx: location of value to delete (as computed by ldap_int_bisect_find()) + * + * return: + * 0: deleted + */ +int +ldap_int_bisect_delete( ber_int_t **vp, ber_len_t *np, int id, int idx ) +{ + ber_int_t *v; + ber_len_t i, n; + + assert( vp != NULL ); + assert( np != NULL ); + assert( idx >= 0 ); + assert( (unsigned) idx < *np ); + + v = *vp; + + assert( v[ idx ] == id ); + + --(*np); + n = *np; + + for ( i = idx; i < n; i++ ) { + v[ i ] = v[ i + 1 ]; + } + + return 0; +} diff --git a/libraries/libldap/add.c b/libraries/libldap/add.c new file mode 100644 index 0000000..e9453fd --- /dev/null +++ b/libraries/libldap/add.c @@ -0,0 +1,263 @@ +/* add.c */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +/* An LDAP Add Request/Response looks like this: + * AddRequest ::= [APPLICATION 8] SEQUENCE { + * entry LDAPDN, + * attributes AttributeList } + * + * AttributeList ::= SEQUENCE OF attribute Attribute + * + * Attribute ::= PartialAttribute(WITH COMPONENTS { + * ..., + * vals (SIZE(1..MAX))}) + * + * PartialAttribute ::= SEQUENCE { + * type AttributeDescription, + * vals SET OF value AttributeValue } + * + * AttributeDescription ::= LDAPString + * -- Constrained to <attributedescription> [RFC4512] + * + * AttributeValue ::= OCTET STRING + * + * AddResponse ::= [APPLICATION 9] LDAPResult + * (Source: RFC 4511) + */ + +/* + * ldap_add - initiate an ldap add operation. Parameters: + * + * ld LDAP descriptor + * dn DN of the entry to add + * mods List of attributes for the entry. This is a null- + * terminated array of pointers to LDAPMod structures. + * only the type and values in the structures need be + * filled in. + * + * Example: + * LDAPMod *attrs[] = { + * { 0, "cn", { "babs jensen", "babs", 0 } }, + * { 0, "sn", { "jensen", 0 } }, + * { 0, "objectClass", { "person", 0 } }, + * 0 + * } + * msgid = ldap_add( ld, dn, attrs ); + */ +int +ldap_add( LDAP *ld, LDAP_CONST char *dn, LDAPMod **attrs ) +{ + int rc; + int msgid; + + rc = ldap_add_ext( ld, dn, attrs, NULL, NULL, &msgid ); + + if ( rc != LDAP_SUCCESS ) + return -1; + + return msgid; +} + + +BerElement * +ldap_build_add_req( + LDAP *ld, + const char *dn, + LDAPMod **attrs, + LDAPControl **sctrls, + LDAPControl **cctrls, + ber_int_t *msgidp ) +{ + BerElement *ber; + int i, rc; + + /* create a message to send */ + if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) { + return( NULL ); + } + + LDAP_NEXT_MSGID(ld, *msgidp); + rc = ber_printf( ber, "{it{s{", /* '}}}' */ + *msgidp, LDAP_REQ_ADD, dn ); + + if ( rc == -1 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return( NULL ); + } + + /* allow attrs to be NULL ("touch"; should fail...) */ + if ( attrs ) { + /* for each attribute in the entry... */ + for ( i = 0; attrs[i] != NULL; i++ ) { + if ( ( attrs[i]->mod_op & LDAP_MOD_BVALUES) != 0 ) { + int j; + + if ( attrs[i]->mod_bvalues == NULL ) { + ld->ld_errno = LDAP_PARAM_ERROR; + ber_free( ber, 1 ); + return( NULL ); + } + + for ( j = 0; attrs[i]->mod_bvalues[ j ] != NULL; j++ ) { + if ( attrs[i]->mod_bvalues[ j ]->bv_val == NULL ) { + ld->ld_errno = LDAP_PARAM_ERROR; + ber_free( ber, 1 ); + return( NULL ); + } + } + + rc = ber_printf( ber, "{s[V]N}", attrs[i]->mod_type, + attrs[i]->mod_bvalues ); + + } else { + if ( attrs[i]->mod_values == NULL ) { + ld->ld_errno = LDAP_PARAM_ERROR; + ber_free( ber, 1 ); + return( NULL ); + } + + rc = ber_printf( ber, "{s[v]N}", attrs[i]->mod_type, + attrs[i]->mod_values ); + } + if ( rc == -1 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return( NULL ); + } + } + } + + if ( ber_printf( ber, /*{{*/ "N}N}" ) == -1 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return( NULL ); + } + + /* Put Server Controls */ + if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) { + ber_free( ber, 1 ); + return( NULL ); + } + + if ( ber_printf( ber, /*{*/ "N}" ) == -1 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return( NULL ); + } + + return( ber ); +} + +/* + * ldap_add_ext - initiate an ldap extended add operation. Parameters: + * + * ld LDAP descriptor + * dn DN of the entry to add + * mods List of attributes for the entry. This is a null- + * terminated array of pointers to LDAPMod structures. + * only the type and values in the structures need be + * filled in. + * sctrl Server Controls + * cctrl Client Controls + * msgidp Message ID pointer + * + * Example: + * LDAPMod *attrs[] = { + * { 0, "cn", { "babs jensen", "babs", 0 } }, + * { 0, "sn", { "jensen", 0 } }, + * { 0, "objectClass", { "person", 0 } }, + * 0 + * } + * rc = ldap_add_ext( ld, dn, attrs, NULL, NULL, &msgid ); + */ +int +ldap_add_ext( + LDAP *ld, + LDAP_CONST char *dn, + LDAPMod **attrs, + LDAPControl **sctrls, + LDAPControl **cctrls, + int *msgidp ) +{ + BerElement *ber; + int rc; + ber_int_t id; + + Debug( LDAP_DEBUG_TRACE, "ldap_add_ext\n", 0, 0, 0 ); + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( dn != NULL ); + assert( msgidp != NULL ); + + /* check client controls */ + rc = ldap_int_client_controls( ld, cctrls ); + if( rc != LDAP_SUCCESS ) return rc; + + ber = ldap_build_add_req( ld, dn, attrs, sctrls, cctrls, &id ); + if( !ber ) + return ld->ld_errno; + + /* send the message */ + *msgidp = ldap_send_initial_request( ld, LDAP_REQ_ADD, dn, ber, id ); + + if(*msgidp < 0) + return ld->ld_errno; + + return LDAP_SUCCESS; +} + +int +ldap_add_ext_s( + LDAP *ld, + LDAP_CONST char *dn, + LDAPMod **attrs, + LDAPControl **sctrls, + LDAPControl **cctrls ) +{ + int msgid, rc; + LDAPMessage *res; + + rc = ldap_add_ext( ld, dn, attrs, sctrls, cctrls, &msgid ); + + if ( rc != LDAP_SUCCESS ) + return( rc ); + + if ( ldap_result( ld, msgid, LDAP_MSG_ALL, (struct timeval *) NULL, &res ) == -1 || !res ) + return( ld->ld_errno ); + + return( ldap_result2error( ld, res, 1 ) ); +} + +int +ldap_add_s( LDAP *ld, LDAP_CONST char *dn, LDAPMod **attrs ) +{ + return ldap_add_ext_s( ld, dn, attrs, NULL, NULL ); +} + diff --git a/libraries/libldap/addentry.c b/libraries/libldap/addentry.c new file mode 100644 index 0000000..514833b --- /dev/null +++ b/libraries/libldap/addentry.c @@ -0,0 +1,72 @@ +/* addentry.c */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +LDAPMessage * +ldap_delete_result_entry( LDAPMessage **list, LDAPMessage *e ) +{ + LDAPMessage *tmp, *prev = NULL; + + assert( list != NULL ); + assert( e != NULL ); + + for ( tmp = *list; tmp != NULL && tmp != e; tmp = tmp->lm_chain ) + prev = tmp; + + if ( tmp == NULL ) + return( NULL ); + + if ( prev == NULL ) { + if ( tmp->lm_chain ) + tmp->lm_chain->lm_chain_tail = (*list)->lm_chain_tail; + *list = tmp->lm_chain; + } else { + prev->lm_chain = tmp->lm_chain; + if ( prev->lm_chain == NULL ) + (*list)->lm_chain_tail = prev; + } + tmp->lm_chain = NULL; + + return( tmp ); +} + +void +ldap_add_result_entry( LDAPMessage **list, LDAPMessage *e ) +{ + assert( list != NULL ); + assert( e != NULL ); + + e->lm_chain = *list; + if ( *list ) + e->lm_chain_tail = (*list)->lm_chain_tail; + else + e->lm_chain_tail = e; + *list = e; +} diff --git a/libraries/libldap/apitest.c b/libraries/libldap/apitest.c new file mode 100644 index 0000000..b15b848 --- /dev/null +++ b/libraries/libldap/apitest.c @@ -0,0 +1,241 @@ +/* apitest.c -- OpenLDAP API Test Program */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * Portions Copyright 1998-2003 Kurt D. Zeilenga. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: + * This program was orignally developed by Kurt D. Zeilenga for inclusion in + * OpenLDAP Software. + */ +#include "portable.h" + +#include <ac/stdlib.h> + +#include <stdio.h> + +#include <ldap.h> + +int +main(int argc, char **argv) +{ + LDAPAPIInfo api; + int ival; + char *sval; + + printf("Compile time API Information\n"); + +#ifdef LDAP_API_INFO_VERSION + api.ldapai_info_version = LDAP_API_INFO_VERSION; + printf(" API Info version: %d\n", (int) api.ldapai_info_version); +#else + api.ldapai_info_version = 1; + printf(" API Info version: unknown\n"); +#endif + +#ifdef LDAP_FEATURE_INFO_VERSION + printf(" Feature Info version: %d\n", (int) LDAP_FEATURE_INFO_VERSION); +#else + printf(" Feature Info version: unknown\n"); + api.ldapai_info_version = 1; +#endif + +#ifdef LDAP_API_VERSION + printf(" API version: %d\n", (int) LDAP_API_VERSION); +#else + printf(" API version: unknown\n"); +#endif + +#ifdef LDAP_VERSION + printf(" Protocol Version: %d\n", (int) LDAP_VERSION); +#else + printf(" Protocol Version: unknown\n"); +#endif +#ifdef LDAP_VERSION_MIN + printf(" Protocol Min: %d\n", (int) LDAP_VERSION_MIN); +#else + printf(" Protocol Min: unknown\n"); +#endif +#ifdef LDAP_VERSION_MAX + printf(" Protocol Max: %d\n", (int) LDAP_VERSION_MAX); +#else + printf(" Protocol Max: unknown\n"); +#endif +#ifdef LDAP_VENDOR_NAME + printf(" Vendor Name: %s\n", LDAP_VENDOR_NAME); +#else + printf(" Vendor Name: unknown\n"); +#endif +#ifdef LDAP_VENDOR_VERSION + printf(" Vendor Version: %d\n", (int) LDAP_VENDOR_VERSION); +#else + printf(" Vendor Version: unknown\n"); +#endif + + if(ldap_get_option(NULL, LDAP_OPT_API_INFO, &api) != LDAP_SUCCESS) { + fprintf(stderr, "%s: ldap_get_option(API_INFO) failed\n", argv[0]); + return EXIT_FAILURE; + } + + printf("\nExecution time API Information\n"); + printf(" API Info version: %d\n", api.ldapai_info_version); + + if (api.ldapai_info_version != LDAP_API_INFO_VERSION) { + printf(" API INFO version mismatch: got %d, expected %d\n", + api.ldapai_info_version, LDAP_API_INFO_VERSION); + return EXIT_FAILURE; + } + + printf(" API Version: %d\n", api.ldapai_api_version); + printf(" Protocol Max: %d\n", api.ldapai_protocol_version); + + if(api.ldapai_extensions == NULL) { + printf(" Extensions: none\n"); + + } else { + int i; + for(i=0; api.ldapai_extensions[i] != NULL; i++) /* empty */; + printf(" Extensions: %d\n", i); + for(i=0; api.ldapai_extensions[i] != NULL; i++) { +#ifdef LDAP_OPT_API_FEATURE_INFO + LDAPAPIFeatureInfo fi; + fi.ldapaif_info_version = LDAP_FEATURE_INFO_VERSION; + fi.ldapaif_name = api.ldapai_extensions[i]; + fi.ldapaif_version = 0; + + if( ldap_get_option(NULL, LDAP_OPT_API_FEATURE_INFO, &fi) == LDAP_SUCCESS ) { + if(fi.ldapaif_info_version != LDAP_FEATURE_INFO_VERSION) { + printf(" %s feature info mismatch: got %d, expected %d\n", + api.ldapai_extensions[i], + LDAP_FEATURE_INFO_VERSION, + fi.ldapaif_info_version); + + } else { + printf(" %s: version %d\n", + fi.ldapaif_name, + fi.ldapaif_version); + } + + } else { + printf(" %s (NO FEATURE INFO)\n", + api.ldapai_extensions[i]); + } + +#else + printf(" %s\n", + api.ldapai_extensions[i]); +#endif + + ldap_memfree(api.ldapai_extensions[i]); + } + ldap_memfree(api.ldapai_extensions); + } + + printf(" Vendor Name: %s\n", api.ldapai_vendor_name); + ldap_memfree(api.ldapai_vendor_name); + + printf(" Vendor Version: %d\n", api.ldapai_vendor_version); + + printf("\nExecution time Default Options\n"); + + if(ldap_get_option(NULL, LDAP_OPT_DEREF, &ival) != LDAP_SUCCESS) { + fprintf(stderr, "%s: ldap_get_option(api) failed\n", argv[0]); + return EXIT_FAILURE; + } + printf(" DEREF: %d\n", ival); + + if(ldap_get_option(NULL, LDAP_OPT_SIZELIMIT, &ival) != LDAP_SUCCESS) { + fprintf(stderr, "%s: ldap_get_option(sizelimit) failed\n", argv[0]); + return EXIT_FAILURE; + } + printf(" SIZELIMIT: %d\n", ival); + + if(ldap_get_option(NULL, LDAP_OPT_TIMELIMIT, &ival) != LDAP_SUCCESS) { + fprintf(stderr, "%s: ldap_get_option(timelimit) failed\n", argv[0]); + return EXIT_FAILURE; + } + printf(" TIMELIMIT: %d\n", ival); + + if(ldap_get_option(NULL, LDAP_OPT_REFERRALS, &ival) != LDAP_SUCCESS) { + fprintf(stderr, "%s: ldap_get_option(referrals) failed\n", argv[0]); + return EXIT_FAILURE; + } + printf(" REFERRALS: %s\n", ival ? "on" : "off"); + + if(ldap_get_option(NULL, LDAP_OPT_RESTART, &ival) != LDAP_SUCCESS) { + fprintf(stderr, "%s: ldap_get_option(restart) failed\n", argv[0]); + return EXIT_FAILURE; + } + printf(" RESTART: %s\n", ival ? "on" : "off"); + + if(ldap_get_option(NULL, LDAP_OPT_PROTOCOL_VERSION, &ival) != LDAP_SUCCESS) { + fprintf(stderr, "%s: ldap_get_option(protocol version) failed\n", argv[0]); + return EXIT_FAILURE; + } + printf(" PROTOCOL VERSION: %d\n", ival); + + if(ldap_get_option(NULL, LDAP_OPT_HOST_NAME, &sval) != LDAP_SUCCESS) { + fprintf(stderr, "%s: ldap_get_option(host name) failed\n", argv[0]); + return EXIT_FAILURE; + } + if( sval != NULL ) { + printf(" HOST NAME: %s\n", sval); + ldap_memfree(sval); + } else { + puts(" HOST NAME: <not set>"); + } + +#if 0 + /* API tests */ + { /* bindless unbind */ + LDAP *ld; + int rc; + + ld = ldap_init( "localhost", 389 ); + if( ld == NULL ) { + perror("ldap_init"); + return EXIT_FAILURE; + } + + rc = ldap_unbind( ld ); + if( rc != LDAP_SUCCESS ) { + perror("ldap_unbind"); + return EXIT_FAILURE; + } + } + { /* bindless unbind */ + LDAP *ld; + int rc; + + ld = ldap_init( "localhost", 389 ); + if( ld == NULL ) { + perror("ldap_init"); + return EXIT_FAILURE; + } + + rc = ldap_abandon_ext( ld, 0, NULL, NULL ); + if( rc != LDAP_SERVER_DOWN ) { + ldap_perror( ld, "ldap_abandon"); + return EXIT_FAILURE; + } + + rc = ldap_unbind( ld ); + if( rc != LDAP_SUCCESS ) { + perror("ldap_unbind"); + return EXIT_FAILURE; + } + } +#endif + + return EXIT_SUCCESS; +} diff --git a/libraries/libldap/assertion.c b/libraries/libldap/assertion.c new file mode 100644 index 0000000..d888d79 --- /dev/null +++ b/libraries/libldap/assertion.c @@ -0,0 +1,100 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +int +ldap_create_assertion_control_value( + LDAP *ld, + char *assertion, + struct berval *value ) +{ + BerElement *ber = NULL; + int err; + + ld->ld_errno = LDAP_SUCCESS; + + if ( assertion == NULL || assertion[ 0 ] == '\0' ) { + ld->ld_errno = LDAP_PARAM_ERROR; + return ld->ld_errno; + } + + if ( value == NULL ) { + ld->ld_errno = LDAP_PARAM_ERROR; + return ld->ld_errno; + } + + BER_BVZERO( value ); + + ber = ldap_alloc_ber_with_options( ld ); + if ( ber == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + return ld->ld_errno; + } + + err = ldap_pvt_put_filter( ber, assertion ); + if ( err < 0 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + goto done; + } + + err = ber_flatten2( ber, value, 1 ); + if ( err < 0 ) { + ld->ld_errno = LDAP_NO_MEMORY; + goto done; + } + +done:; + if ( ber != NULL ) { + ber_free( ber, 1 ); + } + + return ld->ld_errno; +} + +int +ldap_create_assertion_control( + LDAP *ld, + char *assertion, + int iscritical, + LDAPControl **ctrlp ) +{ + struct berval value; + + if ( ctrlp == NULL ) { + ld->ld_errno = LDAP_PARAM_ERROR; + return ld->ld_errno; + } + + ld->ld_errno = ldap_create_assertion_control_value( ld, + assertion, &value ); + if ( ld->ld_errno == LDAP_SUCCESS ) { + ld->ld_errno = ldap_control_create( LDAP_CONTROL_ASSERT, + iscritical, &value, 0, ctrlp ); + if ( ld->ld_errno != LDAP_SUCCESS ) { + LDAP_FREE( value.bv_val ); + } + } + + return ld->ld_errno; +} + diff --git a/libraries/libldap/bind.c b/libraries/libldap/bind.c new file mode 100644 index 0000000..d533a66 --- /dev/null +++ b/libraries/libldap/bind.c @@ -0,0 +1,127 @@ +/* bind.c */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" +#include "ldap_log.h" + +/* + * BindRequest ::= SEQUENCE { + * version INTEGER, + * name DistinguishedName, -- who + * authentication CHOICE { + * simple [0] OCTET STRING -- passwd + * krbv42ldap [1] OCTET STRING -- OBSOLETE + * krbv42dsa [2] OCTET STRING -- OBSOLETE + * sasl [3] SaslCredentials -- LDAPv3 + * } + * } + * + * BindResponse ::= SEQUENCE { + * COMPONENTS OF LDAPResult, + * serverSaslCreds OCTET STRING OPTIONAL -- LDAPv3 + * } + * + * (Source: RFC 2251) + */ + +/* + * ldap_bind - bind to the ldap server (and X.500). The dn and password + * of the entry to which to bind are supplied, along with the authentication + * method to use. The msgid of the bind request is returned on success, + * -1 if there's trouble. ldap_result() should be called to find out the + * outcome of the bind request. + * + * Example: + * ldap_bind( ld, "cn=manager, o=university of michigan, c=us", "secret", + * LDAP_AUTH_SIMPLE ) + */ + +int +ldap_bind( LDAP *ld, LDAP_CONST char *dn, LDAP_CONST char *passwd, int authmethod ) +{ + Debug( LDAP_DEBUG_TRACE, "ldap_bind\n", 0, 0, 0 ); + + switch ( authmethod ) { + case LDAP_AUTH_SIMPLE: + return( ldap_simple_bind( ld, dn, passwd ) ); + +#ifdef HAVE_GSSAPI + case LDAP_AUTH_NEGOTIATE: + return( ldap_gssapi_bind_s( ld, dn, passwd) ); +#endif + + case LDAP_AUTH_SASL: + /* user must use ldap_sasl_bind */ + /* FALL-THRU */ + + default: + ld->ld_errno = LDAP_AUTH_UNKNOWN; + return( -1 ); + } +} + +/* + * ldap_bind_s - bind to the ldap server (and X.500). The dn and password + * of the entry to which to bind are supplied, along with the authentication + * method to use. This routine just calls whichever bind routine is + * appropriate and returns the result of the bind (e.g. LDAP_SUCCESS or + * some other error indication). + * + * Examples: + * ldap_bind_s( ld, "cn=manager, o=university of michigan, c=us", + * "secret", LDAP_AUTH_SIMPLE ) + * ldap_bind_s( ld, "cn=manager, o=university of michigan, c=us", + * NULL, LDAP_AUTH_KRBV4 ) + */ +int +ldap_bind_s( + LDAP *ld, + LDAP_CONST char *dn, + LDAP_CONST char *passwd, + int authmethod ) +{ + Debug( LDAP_DEBUG_TRACE, "ldap_bind_s\n", 0, 0, 0 ); + + switch ( authmethod ) { + case LDAP_AUTH_SIMPLE: + return( ldap_simple_bind_s( ld, dn, passwd ) ); + +#ifdef HAVE_GSSAPI + case LDAP_AUTH_NEGOTIATE: + return( ldap_gssapi_bind_s( ld, dn, passwd) ); +#endif + + case LDAP_AUTH_SASL: + /* user must use ldap_sasl_bind */ + /* FALL-THRU */ + + default: + return( ld->ld_errno = LDAP_AUTH_UNKNOWN ); + } +} diff --git a/libraries/libldap/cancel.c b/libraries/libldap/cancel.c new file mode 100644 index 0000000..13b0b35 --- /dev/null +++ b/libraries/libldap/cancel.c @@ -0,0 +1,76 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: + * This program was originally developed by Kurt D. Zeilenga for inclusion + * in OpenLDAP Software. + */ + +/* + * LDAPv3 Cancel Operation Request + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/stdlib.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" +#include "ldap_log.h" + +int +ldap_cancel( + LDAP *ld, + int cancelid, + LDAPControl **sctrls, + LDAPControl **cctrls, + int *msgidp ) +{ + BerElement *cancelidber = NULL; + struct berval cancelidvalp = { 0, NULL }; + int rc; + + cancelidber = ber_alloc_t( LBER_USE_DER ); + ber_printf( cancelidber, "{i}", cancelid ); + ber_flatten2( cancelidber, &cancelidvalp, 0 ); + rc = ldap_extended_operation( ld, LDAP_EXOP_CANCEL, + &cancelidvalp, sctrls, cctrls, msgidp ); + ber_free( cancelidber, 1 ); + return rc; +} + +int +ldap_cancel_s( + LDAP *ld, + int cancelid, + LDAPControl **sctrls, + LDAPControl **cctrls ) +{ + BerElement *cancelidber = NULL; + struct berval cancelidvalp = { 0, NULL }; + int rc; + + cancelidber = ber_alloc_t( LBER_USE_DER ); + ber_printf( cancelidber, "{i}", cancelid ); + ber_flatten2( cancelidber, &cancelidvalp, 0 ); + rc = ldap_extended_operation_s( ld, LDAP_EXOP_CANCEL, + &cancelidvalp, sctrls, cctrls, NULL, NULL ); + ber_free( cancelidber, 1 ); + return rc; +} + diff --git a/libraries/libldap/charray.c b/libraries/libldap/charray.c new file mode 100644 index 0000000..5731edb --- /dev/null +++ b/libraries/libldap/charray.c @@ -0,0 +1,275 @@ +/* charray.c - routines for dealing with char * arrays */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/string.h> +#include <ac/socket.h> + +#include "ldap-int.h" + +int +ldap_charray_add( + char ***a, + const char *s +) +{ + int n; + + if ( *a == NULL ) { + *a = (char **) LDAP_MALLOC( 2 * sizeof(char *) ); + n = 0; + + if( *a == NULL ) { + return -1; + } + + } else { + char **new; + + for ( n = 0; *a != NULL && (*a)[n] != NULL; n++ ) { + ; /* NULL */ + } + + new = (char **) LDAP_REALLOC( (char *) *a, + (n + 2) * sizeof(char *) ); + + if( new == NULL ) { + /* caller is required to call ldap_charray_free(*a) */ + return -1; + } + + *a = new; + } + + (*a)[n] = LDAP_STRDUP(s); + + if( (*a)[n] == NULL ) { + return 1; + } + + (*a)[++n] = NULL; + + return 0; +} + +int +ldap_charray_merge( + char ***a, + char **s +) +{ + int i, n, nn; + char **aa; + + for ( n = 0; *a != NULL && (*a)[n] != NULL; n++ ) { + ; /* NULL */ + } + for ( nn = 0; s[nn] != NULL; nn++ ) { + ; /* NULL */ + } + + aa = (char **) LDAP_REALLOC( (char *) *a, (n + nn + 1) * sizeof(char *) ); + + if( aa == NULL ) { + return -1; + } + + *a = aa; + + for ( i = 0; i < nn; i++ ) { + (*a)[n + i] = LDAP_STRDUP(s[i]); + + if( (*a)[n + i] == NULL ) { + for( --i ; i >= 0 ; i-- ) { + LDAP_FREE( (*a)[n + i] ); + (*a)[n + i] = NULL; + } + return -1; + } + } + + (*a)[n + nn] = NULL; + return 0; +} + +void +ldap_charray_free( char **a ) +{ + char **p; + + if ( a == NULL ) { + return; + } + + for ( p = a; *p != NULL; p++ ) { + if ( *p != NULL ) { + LDAP_FREE( *p ); + } + } + + LDAP_FREE( (char *) a ); +} + +int +ldap_charray_inlist( + char **a, + const char *s +) +{ + int i; + + if( a == NULL ) return 0; + + for ( i=0; a[i] != NULL; i++ ) { + if ( strcasecmp( s, a[i] ) == 0 ) { + return 1; + } + } + + return 0; +} + +char ** +ldap_charray_dup( char **a ) +{ + int i; + char **new; + + for ( i = 0; a[i] != NULL; i++ ) + ; /* NULL */ + + new = (char **) LDAP_MALLOC( (i + 1) * sizeof(char *) ); + + if( new == NULL ) { + return NULL; + } + + for ( i = 0; a[i] != NULL; i++ ) { + new[i] = LDAP_STRDUP( a[i] ); + + if( new[i] == NULL ) { + for( --i ; i >= 0 ; i-- ) { + LDAP_FREE( new[i] ); + } + LDAP_FREE( new ); + return NULL; + } + } + new[i] = NULL; + + return( new ); +} + +char ** +ldap_str2charray( const char *str_in, const char *brkstr ) +{ + char **res; + char *str, *s; + char *lasts; + int i; + + /* protect the input string from strtok */ + str = LDAP_STRDUP( str_in ); + if( str == NULL ) { + return NULL; + } + + i = 1; + for ( s = str; ; LDAP_UTF8_INCR(s) ) { + s = ldap_utf8_strpbrk( s, brkstr ); + if ( !s ) break; + i++; + } + + res = (char **) LDAP_MALLOC( (i + 1) * sizeof(char *) ); + + if( res == NULL ) { + LDAP_FREE( str ); + return NULL; + } + + i = 0; + + for ( s = ldap_utf8_strtok( str, brkstr, &lasts ); + s != NULL; + s = ldap_utf8_strtok( NULL, brkstr, &lasts ) ) + { + res[i] = LDAP_STRDUP( s ); + + if(res[i] == NULL) { + for( --i ; i >= 0 ; i-- ) { + LDAP_FREE( res[i] ); + } + LDAP_FREE( res ); + LDAP_FREE( str ); + return NULL; + } + + i++; + } + + res[i] = NULL; + + LDAP_FREE( str ); + return( res ); +} + +char * ldap_charray2str( char **a, const char *sep ) +{ + char *s, **v, *p; + int len; + int slen; + + if( sep == NULL ) sep = " "; + + slen = strlen( sep ); + len = 0; + + for ( v = a; *v != NULL; v++ ) { + len += strlen( *v ) + slen; + } + + if ( len == 0 ) { + return NULL; + } + + /* trim extra sep len */ + len -= slen; + + s = LDAP_MALLOC ( len + 1 ); + + if ( s == NULL ) { + return NULL; + } + + p = s; + for ( v = a; *v != NULL; v++ ) { + if ( v != a ) { + strncpy( p, sep, slen ); + p += slen; + } + + len = strlen( *v ); + strncpy( p, *v, len ); + p += len; + } + + *p = '\0'; + return s; +} diff --git a/libraries/libldap/compare.c b/libraries/libldap/compare.c new file mode 100644 index 0000000..9ff4f87 --- /dev/null +++ b/libraries/libldap/compare.c @@ -0,0 +1,197 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" +#include "ldap_log.h" + +/* The compare request looks like this: + * CompareRequest ::= SEQUENCE { + * entry DistinguishedName, + * ava SEQUENCE { + * type AttributeType, + * value AttributeValue + * } + * } + */ + +BerElement * +ldap_build_compare_req( + LDAP *ld, + LDAP_CONST char *dn, + LDAP_CONST char *attr, + struct berval *bvalue, + LDAPControl **sctrls, + LDAPControl **cctrls, + int *msgidp ) +{ + BerElement *ber; + int rc; + + /* create a message to send */ + if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) { + return( NULL ); + } + + LDAP_NEXT_MSGID(ld, *msgidp); + rc = ber_printf( ber, "{it{s{sON}N}", /* '}' */ + *msgidp, + LDAP_REQ_COMPARE, dn, attr, bvalue ); + if ( rc == -1 ) + { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return( NULL ); + } + + /* Put Server Controls */ + if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) { + ber_free( ber, 1 ); + return( NULL ); + } + + if( ber_printf( ber, /*{*/ "N}" ) == -1 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return( NULL ); + } + + return( ber ); +} + +/* + * ldap_compare_ext - perform an ldap extended compare operation. The dn + * of the entry to compare to and the attribute and value to compare (in + * attr and value) are supplied. The msgid of the response is returned. + * + * Example: + * struct berval bvalue = { "secret", sizeof("secret")-1 }; + * rc = ldap_compare( ld, "c=us@cn=bob", + * "userPassword", &bvalue, + * sctrl, cctrl, &msgid ) + */ +int +ldap_compare_ext( + LDAP *ld, + LDAP_CONST char *dn, + LDAP_CONST char *attr, + struct berval *bvalue, + LDAPControl **sctrls, + LDAPControl **cctrls, + int *msgidp ) +{ + int rc; + BerElement *ber; + ber_int_t id; + + Debug( LDAP_DEBUG_TRACE, "ldap_compare\n", 0, 0, 0 ); + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( dn != NULL ); + assert( attr != NULL ); + assert( msgidp != NULL ); + + /* check client controls */ + rc = ldap_int_client_controls( ld, cctrls ); + if( rc != LDAP_SUCCESS ) return rc; + + ber = ldap_build_compare_req( + ld, dn, attr, bvalue, sctrls, cctrls, &id ); + if( !ber ) + return ld->ld_errno; + + /* send the message */ + *msgidp = ldap_send_initial_request( ld, LDAP_REQ_COMPARE, dn, ber, id ); + return ( *msgidp < 0 ? ld->ld_errno : LDAP_SUCCESS ); +} + +/* + * ldap_compare_ext - perform an ldap extended compare operation. The dn + * of the entry to compare to and the attribute and value to compare (in + * attr and value) are supplied. The msgid of the response is returned. + * + * Example: + * msgid = ldap_compare( ld, "c=us@cn=bob", "userPassword", "secret" ) + */ +int +ldap_compare( + LDAP *ld, + LDAP_CONST char *dn, + LDAP_CONST char *attr, + LDAP_CONST char *value ) +{ + int msgid; + struct berval bvalue; + + assert( value != NULL ); + + bvalue.bv_val = (char *) value; + bvalue.bv_len = (value == NULL) ? 0 : strlen( value ); + + return ldap_compare_ext( ld, dn, attr, &bvalue, NULL, NULL, &msgid ) == LDAP_SUCCESS + ? msgid : -1; +} + +int +ldap_compare_ext_s( + LDAP *ld, + LDAP_CONST char *dn, + LDAP_CONST char *attr, + struct berval *bvalue, + LDAPControl **sctrl, + LDAPControl **cctrl ) +{ + int rc; + int msgid; + LDAPMessage *res; + + rc = ldap_compare_ext( ld, dn, attr, bvalue, sctrl, cctrl, &msgid ); + + if ( rc != LDAP_SUCCESS ) + return( rc ); + + if ( ldap_result( ld, msgid, LDAP_MSG_ALL, (struct timeval *) NULL, &res ) == -1 || !res ) + return( ld->ld_errno ); + + return( ldap_result2error( ld, res, 1 ) ); +} + +int +ldap_compare_s( + LDAP *ld, + LDAP_CONST char *dn, + LDAP_CONST char *attr, + LDAP_CONST char *value ) +{ + struct berval bvalue; + + assert( value != NULL ); + + bvalue.bv_val = (char *) value; + bvalue.bv_len = (value == NULL) ? 0 : strlen( value ); + + return ldap_compare_ext_s( ld, dn, attr, &bvalue, NULL, NULL ); +} diff --git a/libraries/libldap/controls.c b/libraries/libldap/controls.c new file mode 100644 index 0000000..184866d --- /dev/null +++ b/libraries/libldap/controls.c @@ -0,0 +1,552 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* This notice applies to changes, created by or for Novell, Inc., + * to preexisting works for which notices appear elsewhere in this file. + * + * Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved. + * + * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND TREATIES. + * USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT TO VERSION + * 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS AVAILABLE AT + * HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE" IN THE + * TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION OF THIS + * WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP PUBLIC + * LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT THE + * PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY. + *--- + * Note: A verbatim copy of version 2.0.1 of the OpenLDAP Public License + * can be found in the file "build/LICENSE-2.0.1" in this distribution + * of OpenLDAP Software. + */ + +#include "portable.h" + +#include <ac/stdlib.h> + +#include <ac/time.h> +#include <ac/string.h> + +#include "ldap-int.h" + +/* LDAPv3 Controls (RFC 4511) + * + * Controls ::= SEQUENCE OF control Control + * + * Control ::= SEQUENCE { + * controlType LDAPOID, + * criticality BOOLEAN DEFAULT FALSE, + * controlValue OCTET STRING OPTIONAL + * } + */ + +int +ldap_pvt_put_control( + const LDAPControl *c, + BerElement *ber ) +{ + if ( ber_printf( ber, "{s" /*}*/, c->ldctl_oid ) == -1 ) { + return LDAP_ENCODING_ERROR; + } + + if ( c->ldctl_iscritical /* only if true */ + && ( ber_printf( ber, "b", + (ber_int_t) c->ldctl_iscritical ) == -1 ) ) + { + return LDAP_ENCODING_ERROR; + } + + if ( !BER_BVISNULL( &c->ldctl_value ) /* only if we have a value */ + && ( ber_printf( ber, "O", &c->ldctl_value ) == -1 ) ) + { + return LDAP_ENCODING_ERROR; + } + + if ( ber_printf( ber, /*{*/"N}" ) == -1 ) { + return LDAP_ENCODING_ERROR; + } + + return LDAP_SUCCESS; +} + + +/* + * ldap_int_put_controls + */ + +int +ldap_int_put_controls( + LDAP *ld, + LDAPControl *const *ctrls, + BerElement *ber ) +{ + LDAPControl *const *c; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( ber != NULL ); + + if( ctrls == NULL ) { + /* use default server controls */ + ctrls = ld->ld_sctrls; + } + + if( ctrls == NULL || *ctrls == NULL ) { + return LDAP_SUCCESS; + } + + if ( ld->ld_version < LDAP_VERSION3 ) { + /* LDAPv2 doesn't support controls, + * error if any control is critical + */ + for( c = ctrls ; *c != NULL; c++ ) { + if( (*c)->ldctl_iscritical ) { + ld->ld_errno = LDAP_NOT_SUPPORTED; + return ld->ld_errno; + } + } + + return LDAP_SUCCESS; + } + + /* Controls are encoded as a sequence of sequences */ + if( ber_printf( ber, "t{"/*}*/, LDAP_TAG_CONTROLS ) == -1 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + return ld->ld_errno; + } + + for( c = ctrls ; *c != NULL; c++ ) { + ld->ld_errno = ldap_pvt_put_control( *c, ber ); + if ( ld->ld_errno != LDAP_SUCCESS ) { + return ld->ld_errno; + } + } + + + if( ber_printf( ber, /*{*/ "}" ) == -1 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + return ld->ld_errno; + } + + return LDAP_SUCCESS; +} + +int ldap_pvt_get_controls( + BerElement *ber, + LDAPControl ***ctrls ) +{ + int nctrls; + ber_tag_t tag; + ber_len_t len; + char *opaque; + + assert( ber != NULL ); + + if( ctrls == NULL ) { + return LDAP_SUCCESS; + } + *ctrls = NULL; + + len = ber_pvt_ber_remaining( ber ); + + if( len == 0) { + /* no controls */ + return LDAP_SUCCESS; + } + + if(( tag = ber_peek_tag( ber, &len )) != LDAP_TAG_CONTROLS ) { + if( tag == LBER_ERROR ) { + /* decoding error */ + return LDAP_DECODING_ERROR; + } + + /* ignore unexpected input */ + return LDAP_SUCCESS; + } + + /* set through each element */ + nctrls = 0; + *ctrls = LDAP_MALLOC( 1 * sizeof(LDAPControl *) ); + + if( *ctrls == NULL ) { + return LDAP_NO_MEMORY; + } + + *ctrls[nctrls] = NULL; + + for( tag = ber_first_element( ber, &len, &opaque ); + tag != LBER_ERROR; + tag = ber_next_element( ber, &len, opaque ) ) + { + LDAPControl *tctrl; + LDAPControl **tctrls; + + tctrl = LDAP_CALLOC( 1, sizeof(LDAPControl) ); + + /* allocate pointer space for current controls (nctrls) + * + this control + extra NULL + */ + tctrls = (tctrl == NULL) ? NULL : + LDAP_REALLOC(*ctrls, (nctrls+2) * sizeof(LDAPControl *)); + + if( tctrls == NULL ) { + /* one of the above allocation failed */ + + if( tctrl != NULL ) { + LDAP_FREE( tctrl ); + } + + ldap_controls_free(*ctrls); + *ctrls = NULL; + + return LDAP_NO_MEMORY; + } + + + tctrls[nctrls++] = tctrl; + tctrls[nctrls] = NULL; + + tag = ber_scanf( ber, "{a" /*}*/, &tctrl->ldctl_oid ); + + if( tag == LBER_ERROR ) { + *ctrls = NULL; + ldap_controls_free( tctrls ); + return LDAP_DECODING_ERROR; + } + + tag = ber_peek_tag( ber, &len ); + + if( tag == LBER_BOOLEAN ) { + ber_int_t crit; + tag = ber_scanf( ber, "b", &crit ); + tctrl->ldctl_iscritical = crit ? (char) 0 : (char) ~0; + tag = ber_peek_tag( ber, &len ); + } + + if( tag == LBER_OCTETSTRING ) { + tag = ber_scanf( ber, "o", &tctrl->ldctl_value ); + } else { + BER_BVZERO( &tctrl->ldctl_value ); + } + + *ctrls = tctrls; + } + + return LDAP_SUCCESS; +} + +/* + * Free a LDAPControl + */ +void +ldap_control_free( LDAPControl *c ) +{ + LDAP_MEMORY_DEBUG_ASSERT( c != NULL ); + + if ( c != NULL ) { + if( c->ldctl_oid != NULL) { + LDAP_FREE( c->ldctl_oid ); + } + + if( c->ldctl_value.bv_val != NULL ) { + LDAP_FREE( c->ldctl_value.bv_val ); + } + + LDAP_FREE( c ); + } +} + +/* + * Free an array of LDAPControl's + */ +void +ldap_controls_free( LDAPControl **controls ) +{ + LDAP_MEMORY_DEBUG_ASSERT( controls != NULL ); + + if ( controls != NULL ) { + int i; + + for( i=0; controls[i] != NULL; i++) { + ldap_control_free( controls[i] ); + } + + LDAP_FREE( controls ); + } +} + +/* + * Duplicate an array of LDAPControl + */ +LDAPControl ** +ldap_controls_dup( LDAPControl *const *controls ) +{ + LDAPControl **new; + int i; + + if ( controls == NULL ) { + return NULL; + } + + /* count the controls */ + for(i=0; controls[i] != NULL; i++) /* empty */ ; + + if( i < 1 ) { + /* no controls to duplicate */ + return NULL; + } + + new = (LDAPControl **) LDAP_MALLOC( (i+1) * sizeof(LDAPControl *) ); + + if( new == NULL ) { + /* memory allocation failure */ + return NULL; + } + + /* duplicate the controls */ + for(i=0; controls[i] != NULL; i++) { + new[i] = ldap_control_dup( controls[i] ); + + if( new[i] == NULL ) { + ldap_controls_free( new ); + return NULL; + } + } + + new[i] = NULL; + + return new; +} + +/* + * Duplicate a LDAPControl + */ +LDAPControl * +ldap_control_dup( const LDAPControl *c ) +{ + LDAPControl *new; + + if ( c == NULL || c->ldctl_oid == NULL ) { + return NULL; + } + + new = (LDAPControl *) LDAP_MALLOC( sizeof(LDAPControl) ); + + if( new == NULL ) { + return NULL; + } + + new->ldctl_oid = LDAP_STRDUP( c->ldctl_oid ); + + if(new->ldctl_oid == NULL) { + LDAP_FREE( new ); + return NULL; + } + + if( c->ldctl_value.bv_val != NULL ) { + new->ldctl_value.bv_val = + (char *) LDAP_MALLOC( c->ldctl_value.bv_len + 1 ); + + if(new->ldctl_value.bv_val == NULL) { + if(new->ldctl_oid != NULL) { + LDAP_FREE( new->ldctl_oid ); + } + LDAP_FREE( new ); + return NULL; + } + + new->ldctl_value.bv_len = c->ldctl_value.bv_len; + + AC_MEMCPY( new->ldctl_value.bv_val, c->ldctl_value.bv_val, + c->ldctl_value.bv_len ); + + new->ldctl_value.bv_val[new->ldctl_value.bv_len] = '\0'; + + } else { + new->ldctl_value.bv_len = 0; + new->ldctl_value.bv_val = NULL; + } + + new->ldctl_iscritical = c->ldctl_iscritical; + return new; +} + +/* + * Find a LDAPControl - deprecated + */ +LDAPControl * +ldap_find_control( + LDAP_CONST char *oid, + LDAPControl **ctrls ) +{ + if( ctrls == NULL || *ctrls == NULL ) { + return NULL; + } + + for( ; *ctrls != NULL; ctrls++ ) { + if( strcmp( (*ctrls)->ldctl_oid, oid ) == 0 ) { + return *ctrls; + } + } + + return NULL; +} + +/* + * Find a LDAPControl + */ +LDAPControl * +ldap_control_find( + LDAP_CONST char *oid, + LDAPControl **ctrls, + LDAPControl ***nextctrlp ) +{ + if ( oid == NULL || ctrls == NULL || *ctrls == NULL ) { + return NULL; + } + + for( ; *ctrls != NULL; ctrls++ ) { + if( strcmp( (*ctrls)->ldctl_oid, oid ) == 0 ) { + if ( nextctrlp != NULL ) { + *nextctrlp = ctrls + 1; + } + + return *ctrls; + } + } + + if ( nextctrlp != NULL ) { + *nextctrlp = NULL; + } + + return NULL; +} + +/* + * Create a LDAPControl, optionally from ber - deprecated + */ +int +ldap_create_control( + LDAP_CONST char *requestOID, + BerElement *ber, + int iscritical, + LDAPControl **ctrlp ) +{ + LDAPControl *ctrl; + + assert( requestOID != NULL ); + assert( ctrlp != NULL ); + + ctrl = (LDAPControl *) LDAP_MALLOC( sizeof(LDAPControl) ); + if ( ctrl == NULL ) { + return LDAP_NO_MEMORY; + } + + BER_BVZERO(&ctrl->ldctl_value); + if ( ber && ( ber_flatten2( ber, &ctrl->ldctl_value, 1 ) == -1 )) { + LDAP_FREE( ctrl ); + return LDAP_NO_MEMORY; + } + + ctrl->ldctl_oid = LDAP_STRDUP( requestOID ); + ctrl->ldctl_iscritical = iscritical; + + if ( requestOID != NULL && ctrl->ldctl_oid == NULL ) { + ldap_control_free( ctrl ); + return LDAP_NO_MEMORY; + } + + *ctrlp = ctrl; + return LDAP_SUCCESS; +} + +/* + * Create a LDAPControl, optionally from value + */ +int +ldap_control_create( + LDAP_CONST char *requestOID, + int iscritical, + struct berval *value, + int dupval, + LDAPControl **ctrlp ) +{ + LDAPControl *ctrl; + + assert( requestOID != NULL ); + assert( ctrlp != NULL ); + + ctrl = (LDAPControl *) LDAP_CALLOC( sizeof(LDAPControl), 1 ); + if ( ctrl == NULL ) { + return LDAP_NO_MEMORY; + } + + ctrl->ldctl_iscritical = iscritical; + if ( requestOID != NULL ) { + ctrl->ldctl_oid = LDAP_STRDUP( requestOID ); + if ( ctrl->ldctl_oid == NULL ) { + ldap_control_free( ctrl ); + return LDAP_NO_MEMORY; + } + } + + if ( value && !BER_BVISNULL( value ) ) { + if ( dupval ) { + ber_dupbv( &ctrl->ldctl_value, value ); + if ( BER_BVISNULL( &ctrl->ldctl_value ) ) { + ldap_control_free( ctrl ); + return LDAP_NO_MEMORY; + } + + } else { + ctrl->ldctl_value = *value; + } + } + + *ctrlp = ctrl; + + return LDAP_SUCCESS; +} + +/* + * check for critical client controls and bitch if present + * if we ever support critical controls, we'll have to + * find a means for maintaining per API call control + * information. + */ +int ldap_int_client_controls( LDAP *ld, LDAPControl **ctrls ) +{ + LDAPControl *const *c; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + + if( ctrls == NULL ) { + /* use default client controls */ + ctrls = ld->ld_cctrls; + } + + if( ctrls == NULL || *ctrls == NULL ) { + return LDAP_SUCCESS; + } + + for( c = ctrls ; *c != NULL; c++ ) { + if( (*c)->ldctl_iscritical ) { + ld->ld_errno = LDAP_NOT_SUPPORTED; + return ld->ld_errno; + } + } + + return LDAP_SUCCESS; +} diff --git a/libraries/libldap/cyrus.c b/libraries/libldap/cyrus.c new file mode 100644 index 0000000..4c0089d --- /dev/null +++ b/libraries/libldap/cyrus.c @@ -0,0 +1,1237 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include "ldap-int.h" + +#ifdef HAVE_CYRUS_SASL + +#include <stdio.h> + +#include <ac/socket.h> +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/time.h> +#include <ac/errno.h> +#include <ac/ctype.h> +#include <ac/unistd.h> + +#ifdef HAVE_LIMITS_H +#include <limits.h> +#endif + +#ifndef INT_MAX +#define INT_MAX 2147483647 /* 32 bit signed max */ +#endif + +#if !defined(HOST_NAME_MAX) && defined(_POSIX_HOST_NAME_MAX) +#define HOST_NAME_MAX _POSIX_HOST_NAME_MAX +#endif + +#ifdef HAVE_SASL_SASL_H +#include <sasl/sasl.h> +#else +#include <sasl.h> +#endif + +#if SASL_VERSION_MAJOR >= 2 +#define SASL_CONST const +#else +#define SASL_CONST +#endif + +/* +* Various Cyrus SASL related stuff. +*/ + +static const sasl_callback_t client_callbacks[] = { +#ifdef SASL_CB_GETREALM + { SASL_CB_GETREALM, NULL, NULL }, +#endif + { SASL_CB_USER, NULL, NULL }, + { SASL_CB_AUTHNAME, NULL, NULL }, + { SASL_CB_PASS, NULL, NULL }, + { SASL_CB_ECHOPROMPT, NULL, NULL }, + { SASL_CB_NOECHOPROMPT, NULL, NULL }, + { SASL_CB_LIST_END, NULL, NULL } +}; + +/* + * ldap_int_initialize is responsible for calling this only once. + */ +int ldap_int_sasl_init( void ) +{ +#ifdef HAVE_SASL_VERSION + /* stringify the version number, sasl.h doesn't do it for us */ +#define VSTR0(maj, min, pat) #maj "." #min "." #pat +#define VSTR(maj, min, pat) VSTR0(maj, min, pat) +#define SASL_VERSION_STRING VSTR(SASL_VERSION_MAJOR, SASL_VERSION_MINOR, \ + SASL_VERSION_STEP) + { int rc; + sasl_version( NULL, &rc ); + if ( ((rc >> 16) != ((SASL_VERSION_MAJOR << 8)|SASL_VERSION_MINOR)) || + (rc & 0xffff) < SASL_VERSION_STEP) { + char version[sizeof("xxx.xxx.xxxxx")]; + sprintf( version, "%u.%d.%d", (unsigned)rc >> 24, (rc >> 16) & 0xff, + rc & 0xffff ); + + Debug( LDAP_DEBUG_ANY, + "ldap_int_sasl_init: SASL library version mismatch:" + " expected " SASL_VERSION_STRING "," + " got %s\n", version, 0, 0 ); + return -1; + } + } +#endif + +/* SASL 2 takes care of its own memory completely internally */ +#if SASL_VERSION_MAJOR < 2 && !defined(CSRIMALLOC) + sasl_set_alloc( + ber_memalloc, + ber_memcalloc, + ber_memrealloc, + ber_memfree ); +#endif /* CSRIMALLOC */ + +#ifdef LDAP_R_COMPILE + sasl_set_mutex( + ldap_pvt_sasl_mutex_new, + ldap_pvt_sasl_mutex_lock, + ldap_pvt_sasl_mutex_unlock, + ldap_pvt_sasl_mutex_dispose ); +#endif + + if ( sasl_client_init( NULL ) == SASL_OK ) { + return 0; + } + +#if SASL_VERSION_MAJOR < 2 + /* A no-op to make sure we link with Cyrus 1.5 */ + sasl_client_auth( NULL, NULL, NULL, 0, NULL, NULL ); +#endif + return -1; +} + +static void +sb_sasl_cyrus_init( + struct sb_sasl_generic_data *p, + ber_len_t *min_send, + ber_len_t *max_send, + ber_len_t *max_recv) +{ + sasl_conn_t *sasl_context = (sasl_conn_t *)p->ops_private; + ber_len_t maxbuf; + + sasl_getprop( sasl_context, SASL_MAXOUTBUF, + (SASL_CONST void **)(char *) &maxbuf ); + + *min_send = SASL_MIN_BUFF_SIZE; + *max_send = maxbuf; + *max_recv = SASL_MAX_BUFF_SIZE; +} + +static ber_int_t +sb_sasl_cyrus_encode( + struct sb_sasl_generic_data *p, + unsigned char *buf, + ber_len_t len, + Sockbuf_Buf *dst) +{ + sasl_conn_t *sasl_context = (sasl_conn_t *)p->ops_private; + ber_int_t ret; + unsigned tmpsize = dst->buf_size; + + ret = sasl_encode( sasl_context, (char *)buf, len, + (SASL_CONST char **)&dst->buf_base, + &tmpsize ); + + dst->buf_size = tmpsize; + dst->buf_end = dst->buf_size; + + if ( ret != SASL_OK ) { + ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug, + "sb_sasl_cyrus_encode: failed to encode packet: %s\n", + sasl_errstring( ret, NULL, NULL ) ); + return -1; + } + + return 0; +} + +static ber_int_t +sb_sasl_cyrus_decode( + struct sb_sasl_generic_data *p, + const Sockbuf_Buf *src, + Sockbuf_Buf *dst) +{ + sasl_conn_t *sasl_context = (sasl_conn_t *)p->ops_private; + ber_int_t ret; + unsigned tmpsize = dst->buf_size; + + ret = sasl_decode( sasl_context, + src->buf_base, src->buf_end, + (SASL_CONST char **)&dst->buf_base, + (unsigned *)&tmpsize ); + + + dst->buf_size = tmpsize; + dst->buf_end = dst->buf_size; + + if ( ret != SASL_OK ) { + ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug, + "sb_sasl_cyrus_decode: failed to decode packet: %s\n", + sasl_errstring( ret, NULL, NULL ) ); + return -1; + } + + return 0; +} + +static void +sb_sasl_cyrus_reset_buf( + struct sb_sasl_generic_data *p, + Sockbuf_Buf *buf) +{ +#if SASL_VERSION_MAJOR >= 2 + ber_pvt_sb_buf_init( buf ); +#else + ber_pvt_sb_buf_destroy( buf ); +#endif +} + +static void +sb_sasl_cyrus_fini( + struct sb_sasl_generic_data *p) +{ +#if SASL_VERSION_MAJOR >= 2 + /* + * SASLv2 encode/decode buffers are managed by + * libsasl2. Ensure they are not freed by liblber. + */ + p->buf_in.buf_base = NULL; + p->buf_out.buf_base = NULL; +#endif +} + +static const struct sb_sasl_generic_ops sb_sasl_cyrus_ops = { + sb_sasl_cyrus_init, + sb_sasl_cyrus_encode, + sb_sasl_cyrus_decode, + sb_sasl_cyrus_reset_buf, + sb_sasl_cyrus_fini + }; + +int ldap_pvt_sasl_install( Sockbuf *sb, void *ctx_arg ) +{ + struct sb_sasl_generic_install install_arg; + + install_arg.ops = &sb_sasl_cyrus_ops; + install_arg.ops_private = ctx_arg; + + return ldap_pvt_sasl_generic_install( sb, &install_arg ); +} + +void ldap_pvt_sasl_remove( Sockbuf *sb ) +{ + ldap_pvt_sasl_generic_remove( sb ); +} + +static int +sasl_err2ldap( int saslerr ) +{ + int rc; + + /* map SASL errors to LDAP API errors returned by: + * sasl_client_new() + * SASL_OK, SASL_NOMECH, SASL_NOMEM + * sasl_client_start() + * SASL_OK, SASL_NOMECH, SASL_NOMEM, SASL_INTERACT + * sasl_client_step() + * SASL_OK, SASL_INTERACT, SASL_BADPROT, SASL_BADSERV + */ + + switch (saslerr) { + case SASL_CONTINUE: + rc = LDAP_MORE_RESULTS_TO_RETURN; + break; + case SASL_INTERACT: + rc = LDAP_LOCAL_ERROR; + break; + case SASL_OK: + rc = LDAP_SUCCESS; + break; + case SASL_NOMEM: + rc = LDAP_NO_MEMORY; + break; + case SASL_NOMECH: + rc = LDAP_AUTH_UNKNOWN; + break; + case SASL_BADPROT: + rc = LDAP_DECODING_ERROR; + break; + case SASL_BADSERV: + rc = LDAP_AUTH_UNKNOWN; + break; + + /* other codes */ + case SASL_BADAUTH: + rc = LDAP_AUTH_UNKNOWN; + break; + case SASL_NOAUTHZ: + rc = LDAP_PARAM_ERROR; + break; + case SASL_FAIL: + rc = LDAP_LOCAL_ERROR; + break; + case SASL_TOOWEAK: + case SASL_ENCRYPT: + rc = LDAP_AUTH_UNKNOWN; + break; + default: + rc = LDAP_LOCAL_ERROR; + break; + } + + assert( rc == LDAP_SUCCESS || LDAP_API_ERROR( rc ) ); + return rc; +} + +int +ldap_int_sasl_open( + LDAP *ld, + LDAPConn *lc, + const char * host ) +{ + int rc; + sasl_conn_t *ctx; + + assert( lc->lconn_sasl_authctx == NULL ); + + if ( host == NULL ) { + ld->ld_errno = LDAP_LOCAL_ERROR; + return ld->ld_errno; + } + +#if SASL_VERSION_MAJOR >= 2 + rc = sasl_client_new( "ldap", host, NULL, NULL, + client_callbacks, 0, &ctx ); +#else + rc = sasl_client_new( "ldap", host, client_callbacks, + SASL_SECURITY_LAYER, &ctx ); +#endif + + if ( rc != SASL_OK ) { + ld->ld_errno = sasl_err2ldap( rc ); + return ld->ld_errno; + } + + Debug( LDAP_DEBUG_TRACE, "ldap_int_sasl_open: host=%s\n", + host, 0, 0 ); + + lc->lconn_sasl_authctx = ctx; + + return LDAP_SUCCESS; +} + +int ldap_int_sasl_close( LDAP *ld, LDAPConn *lc ) +{ + sasl_conn_t *ctx = lc->lconn_sasl_authctx; + + if( ctx != NULL ) { + sasl_dispose( &ctx ); + if ( lc->lconn_sasl_sockctx && + lc->lconn_sasl_authctx != lc->lconn_sasl_sockctx ) { + ctx = lc->lconn_sasl_sockctx; + sasl_dispose( &ctx ); + } + lc->lconn_sasl_sockctx = NULL; + lc->lconn_sasl_authctx = NULL; + } + + return LDAP_SUCCESS; +} + +int +ldap_int_sasl_bind( + LDAP *ld, + const char *dn, + const char *mechs, + LDAPControl **sctrls, + LDAPControl **cctrls, + unsigned flags, + LDAP_SASL_INTERACT_PROC *interact, + void *defaults, + LDAPMessage *result, + const char **rmech, + int *msgid ) +{ + const char *mech; + sasl_ssf_t *ssf; + sasl_conn_t *ctx; + sasl_interact_t *prompts = NULL; + struct berval ccred = BER_BVNULL; + int saslrc, rc; + unsigned credlen; +#if !defined(_WIN32) + char my_hostname[HOST_NAME_MAX + 1]; +#endif + int free_saslhost = 0; + + Debug( LDAP_DEBUG_TRACE, "ldap_int_sasl_bind: %s\n", + mechs ? mechs : "<null>", 0, 0 ); + + /* do a quick !LDAPv3 check... ldap_sasl_bind will do the rest. */ + if (ld->ld_version < LDAP_VERSION3) { + ld->ld_errno = LDAP_NOT_SUPPORTED; + return ld->ld_errno; + } + + /* Starting a Bind */ + if ( !result ) { + const char *pmech = NULL; + sasl_conn_t *oldctx; + ber_socket_t sd; + void *ssl; + + rc = 0; + LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); + ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, &sd ); + + if ( sd == AC_SOCKET_INVALID || !ld->ld_defconn ) { + /* not connected yet */ + + rc = ldap_open_defconn( ld ); + + if ( rc == 0 ) { + ber_sockbuf_ctrl( ld->ld_defconn->lconn_sb, + LBER_SB_OPT_GET_FD, &sd ); + + if( sd == AC_SOCKET_INVALID ) { + ld->ld_errno = LDAP_LOCAL_ERROR; + rc = ld->ld_errno; + } + } + } + if ( rc == 0 && ld->ld_defconn && + ld->ld_defconn->lconn_status == LDAP_CONNST_CONNECTING ) { + rc = ldap_int_check_async_open( ld, sd ); + } + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); + if( rc != 0 ) return ld->ld_errno; + + oldctx = ld->ld_defconn->lconn_sasl_authctx; + + /* If we already have an authentication context, clear it out */ + if( oldctx ) { + if ( oldctx != ld->ld_defconn->lconn_sasl_sockctx ) { + sasl_dispose( &oldctx ); + } + ld->ld_defconn->lconn_sasl_authctx = NULL; + } + + { + char *saslhost; + int nocanon = (int)LDAP_BOOL_GET( &ld->ld_options, + LDAP_BOOL_SASL_NOCANON ); + + /* If we don't need to canonicalize just use the host + * from the LDAP URI. + * Always use the result of gethostname() for LDAPI. + * Skip for Windows which doesn't support LDAPI. + */ +#if !defined(_WIN32) + if (ld->ld_defconn->lconn_server->lud_scheme != NULL && + strcmp("ldapi", ld->ld_defconn->lconn_server->lud_scheme) == 0) { + rc = gethostname(my_hostname, HOST_NAME_MAX + 1); + if (rc == 0) { + saslhost = my_hostname; + } else { + saslhost = "localhost"; + } + } else +#endif + if ( nocanon ) + saslhost = ld->ld_defconn->lconn_server->lud_host; + else { + saslhost = ldap_host_connected_to( ld->ld_defconn->lconn_sb, + "localhost" ); + free_saslhost = 1; + } + rc = ldap_int_sasl_open( ld, ld->ld_defconn, saslhost ); + if ( free_saslhost ) + LDAP_FREE( saslhost ); + } + + if ( rc != LDAP_SUCCESS ) return rc; + + ctx = ld->ld_defconn->lconn_sasl_authctx; + +#ifdef HAVE_TLS + /* Check for TLS */ + ssl = ldap_pvt_tls_sb_ctx( ld->ld_defconn->lconn_sb ); + if ( ssl ) { + struct berval authid = BER_BVNULL; + ber_len_t fac; + + fac = ldap_pvt_tls_get_strength( ssl ); + /* failure is OK, we just can't use SASL EXTERNAL */ + (void) ldap_pvt_tls_get_my_dn( ssl, &authid, NULL, 0 ); + + (void) ldap_int_sasl_external( ld, ld->ld_defconn, authid.bv_val, fac ); + LDAP_FREE( authid.bv_val ); + } +#endif + +#if !defined(_WIN32) + /* Check for local */ + if ( ldap_pvt_url_scheme2proto( + ld->ld_defconn->lconn_server->lud_scheme ) == LDAP_PROTO_IPC ) + { + char authid[sizeof("gidNumber=4294967295+uidNumber=4294967295," + "cn=peercred,cn=external,cn=auth")]; + sprintf( authid, "gidNumber=%u+uidNumber=%u," + "cn=peercred,cn=external,cn=auth", + getegid(), geteuid() ); + (void) ldap_int_sasl_external( ld, ld->ld_defconn, authid, + LDAP_PVT_SASL_LOCAL_SSF ); + } +#endif + + /* (re)set security properties */ + sasl_setprop( ctx, SASL_SEC_PROPS, + &ld->ld_options.ldo_sasl_secprops ); + + mech = NULL; + + do { + saslrc = sasl_client_start( ctx, + mechs, +#if SASL_VERSION_MAJOR < 2 + NULL, +#endif + &prompts, + (SASL_CONST char **)&ccred.bv_val, + &credlen, + &mech ); + + if( pmech == NULL && mech != NULL ) { + pmech = mech; + *rmech = mech; + + if( flags != LDAP_SASL_QUIET ) { + fprintf(stderr, + "SASL/%s authentication started\n", + pmech ); + } + } + + if( saslrc == SASL_INTERACT ) { + int res; + if( !interact ) break; + res = (interact)( ld, flags, defaults, prompts ); + + if( res != LDAP_SUCCESS ) break; + } + } while ( saslrc == SASL_INTERACT ); + rc = LDAP_SASL_BIND_IN_PROGRESS; + + } else { + /* continuing an in-progress Bind */ + struct berval *scred = NULL; + + ctx = ld->ld_defconn->lconn_sasl_authctx; + + rc = ldap_parse_sasl_bind_result( ld, result, &scred, 0 ); + if ( rc != LDAP_SUCCESS ) { + if ( scred ) + ber_bvfree( scred ); + goto done; + } + + rc = ldap_result2error( ld, result, 0 ); + if ( rc != LDAP_SUCCESS && rc != LDAP_SASL_BIND_IN_PROGRESS ) { + if( scred ) { + /* and server provided us with data? */ + Debug( LDAP_DEBUG_TRACE, + "ldap_int_sasl_bind: rc=%d len=%ld\n", + rc, scred ? (long) scred->bv_len : -1L, 0 ); + ber_bvfree( scred ); + scred = NULL; + } + goto done; + } + + mech = *rmech; + if ( rc == LDAP_SUCCESS && mech == NULL ) { + if ( scred ) + ber_bvfree( scred ); + goto success; + } + + do { + if( ! scred ) { + /* no data! */ + Debug( LDAP_DEBUG_TRACE, + "ldap_int_sasl_bind: no data in step!\n", + 0, 0, 0 ); + } + + saslrc = sasl_client_step( ctx, + (scred == NULL) ? NULL : scred->bv_val, + (scred == NULL) ? 0 : scred->bv_len, + &prompts, + (SASL_CONST char **)&ccred.bv_val, + &credlen ); + + Debug( LDAP_DEBUG_TRACE, "sasl_client_step: %d\n", + saslrc, 0, 0 ); + + if( saslrc == SASL_INTERACT ) { + int res; + if( !interact ) break; + res = (interact)( ld, flags, defaults, prompts ); + if( res != LDAP_SUCCESS ) break; + } + } while ( saslrc == SASL_INTERACT ); + + ber_bvfree( scred ); + } + + if ( (saslrc != SASL_OK) && (saslrc != SASL_CONTINUE) ) { + rc = ld->ld_errno = sasl_err2ldap( saslrc ); +#if SASL_VERSION_MAJOR >= 2 + if ( ld->ld_error ) { + LDAP_FREE( ld->ld_error ); + } + ld->ld_error = LDAP_STRDUP( sasl_errdetail( ctx ) ); +#endif + goto done; + } + + if ( saslrc == SASL_OK ) + *rmech = NULL; + + ccred.bv_len = credlen; + + if ( rc == LDAP_SASL_BIND_IN_PROGRESS ) { + rc = ldap_sasl_bind( ld, dn, mech, &ccred, sctrls, cctrls, msgid ); + + if ( ccred.bv_val != NULL ) { +#if SASL_VERSION_MAJOR < 2 + LDAP_FREE( ccred.bv_val ); +#endif + ccred.bv_val = NULL; + } + if ( rc == LDAP_SUCCESS ) + rc = LDAP_SASL_BIND_IN_PROGRESS; + goto done; + } + +success: + /* Conversation was completed successfully by now */ + if( flags != LDAP_SASL_QUIET ) { + char *data; + saslrc = sasl_getprop( ctx, SASL_USERNAME, + (SASL_CONST void **)(char *) &data ); + if( saslrc == SASL_OK && data && *data ) { + fprintf( stderr, "SASL username: %s\n", data ); + } + +#if SASL_VERSION_MAJOR < 2 + saslrc = sasl_getprop( ctx, SASL_REALM, + (SASL_CONST void **) &data ); + if( saslrc == SASL_OK && data && *data ) { + fprintf( stderr, "SASL realm: %s\n", data ); + } +#endif + } + + ssf = NULL; + saslrc = sasl_getprop( ctx, SASL_SSF, (SASL_CONST void **)(char *) &ssf ); + if( saslrc == SASL_OK ) { + if( flags != LDAP_SASL_QUIET ) { + fprintf( stderr, "SASL SSF: %lu\n", + (unsigned long) *ssf ); + } + + if( ssf && *ssf ) { + if ( ld->ld_defconn->lconn_sasl_sockctx ) { + sasl_conn_t *oldctx = ld->ld_defconn->lconn_sasl_sockctx; + sasl_dispose( &oldctx ); + ldap_pvt_sasl_remove( ld->ld_defconn->lconn_sb ); + } + ldap_pvt_sasl_install( ld->ld_defconn->lconn_sb, ctx ); + ld->ld_defconn->lconn_sasl_sockctx = ctx; + + if( flags != LDAP_SASL_QUIET ) { + fprintf( stderr, "SASL data security layer installed.\n" ); + } + } + } + ld->ld_defconn->lconn_sasl_authctx = ctx; + +done: + return rc; +} + +int +ldap_int_sasl_external( + LDAP *ld, + LDAPConn *conn, + const char * authid, + ber_len_t ssf ) +{ + int sc; + sasl_conn_t *ctx; +#if SASL_VERSION_MAJOR < 2 + sasl_external_properties_t extprops; +#else + sasl_ssf_t sasl_ssf = ssf; +#endif + + ctx = conn->lconn_sasl_authctx; + + if ( ctx == NULL ) { + return LDAP_LOCAL_ERROR; + } + +#if SASL_VERSION_MAJOR >= 2 + sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL, &sasl_ssf ); + if ( sc == SASL_OK ) + sc = sasl_setprop( ctx, SASL_AUTH_EXTERNAL, authid ); +#else + memset( &extprops, '\0', sizeof(extprops) ); + extprops.ssf = ssf; + extprops.auth_id = (char *) authid; + + sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL, + (void *) &extprops ); +#endif + + if ( sc != SASL_OK ) { + return LDAP_LOCAL_ERROR; + } + + return LDAP_SUCCESS; +} + + +#define GOT_MINSSF 1 +#define GOT_MAXSSF 2 +#define GOT_MAXBUF 4 + +static struct { + struct berval key; + int sflag; + int ival; + int idef; +} sprops[] = { + { BER_BVC("none"), 0, 0, 0 }, + { BER_BVC("nodict"), SASL_SEC_NODICTIONARY, 0, 0 }, + { BER_BVC("noplain"), SASL_SEC_NOPLAINTEXT, 0, 0 }, + { BER_BVC("noactive"), SASL_SEC_NOACTIVE, 0, 0 }, + { BER_BVC("passcred"), SASL_SEC_PASS_CREDENTIALS, 0, 0 }, + { BER_BVC("forwardsec"), SASL_SEC_FORWARD_SECRECY, 0, 0 }, + { BER_BVC("noanonymous"), SASL_SEC_NOANONYMOUS, 0, 0 }, + { BER_BVC("minssf="), 0, GOT_MINSSF, 0 }, + { BER_BVC("maxssf="), 0, GOT_MAXSSF, INT_MAX }, + { BER_BVC("maxbufsize="), 0, GOT_MAXBUF, 65536 }, + { BER_BVNULL, 0, 0, 0 } +}; + +void ldap_pvt_sasl_secprops_unparse( + sasl_security_properties_t *secprops, + struct berval *out ) +{ + int i, l = 0; + int comma; + char *ptr; + + if ( secprops == NULL || out == NULL ) { + return; + } + + comma = 0; + for ( i=0; !BER_BVISNULL( &sprops[i].key ); i++ ) { + if ( sprops[i].ival ) { + int v = 0; + + switch( sprops[i].ival ) { + case GOT_MINSSF: v = secprops->min_ssf; break; + case GOT_MAXSSF: v = secprops->max_ssf; break; + case GOT_MAXBUF: v = secprops->maxbufsize; break; + } + /* It is the default, ignore it */ + if ( v == sprops[i].idef ) continue; + + l += sprops[i].key.bv_len + 24; + } else if ( sprops[i].sflag ) { + if ( sprops[i].sflag & secprops->security_flags ) { + l += sprops[i].key.bv_len; + } + } else if ( secprops->security_flags == 0 ) { + l += sprops[i].key.bv_len; + } + if ( comma ) l++; + comma = 1; + } + l++; + + out->bv_val = LDAP_MALLOC( l ); + if ( out->bv_val == NULL ) { + out->bv_len = 0; + return; + } + + ptr = out->bv_val; + comma = 0; + for ( i=0; !BER_BVISNULL( &sprops[i].key ); i++ ) { + if ( sprops[i].ival ) { + int v = 0; + + switch( sprops[i].ival ) { + case GOT_MINSSF: v = secprops->min_ssf; break; + case GOT_MAXSSF: v = secprops->max_ssf; break; + case GOT_MAXBUF: v = secprops->maxbufsize; break; + } + /* It is the default, ignore it */ + if ( v == sprops[i].idef ) continue; + + if ( comma ) *ptr++ = ','; + ptr += sprintf(ptr, "%s%d", sprops[i].key.bv_val, v ); + comma = 1; + } else if ( sprops[i].sflag ) { + if ( sprops[i].sflag & secprops->security_flags ) { + if ( comma ) *ptr++ = ','; + ptr += sprintf(ptr, "%s", sprops[i].key.bv_val ); + comma = 1; + } + } else if ( secprops->security_flags == 0 ) { + if ( comma ) *ptr++ = ','; + ptr += sprintf(ptr, "%s", sprops[i].key.bv_val ); + comma = 1; + } + } + out->bv_len = ptr - out->bv_val; +} + +int ldap_pvt_sasl_secprops( + const char *in, + sasl_security_properties_t *secprops ) +{ + unsigned i, j, l; + char **props; + unsigned sflags = 0; + int got_sflags = 0; + sasl_ssf_t max_ssf = 0; + int got_max_ssf = 0; + sasl_ssf_t min_ssf = 0; + int got_min_ssf = 0; + unsigned maxbufsize = 0; + int got_maxbufsize = 0; + + if( secprops == NULL ) { + return LDAP_PARAM_ERROR; + } + props = ldap_str2charray( in, "," ); + if( props == NULL ) { + return LDAP_PARAM_ERROR; + } + + for( i=0; props[i]; i++ ) { + l = strlen( props[i] ); + for ( j=0; !BER_BVISNULL( &sprops[j].key ); j++ ) { + if ( l < sprops[j].key.bv_len ) continue; + if ( strncasecmp( props[i], sprops[j].key.bv_val, + sprops[j].key.bv_len )) continue; + if ( sprops[j].ival ) { + unsigned v; + char *next = NULL; + if ( !isdigit( (unsigned char)props[i][sprops[j].key.bv_len] )) + continue; + v = strtoul( &props[i][sprops[j].key.bv_len], &next, 10 ); + if ( next == &props[i][sprops[j].key.bv_len] || next[0] != '\0' ) continue; + switch( sprops[j].ival ) { + case GOT_MINSSF: + min_ssf = v; got_min_ssf++; break; + case GOT_MAXSSF: + max_ssf = v; got_max_ssf++; break; + case GOT_MAXBUF: + maxbufsize = v; got_maxbufsize++; break; + } + } else { + if ( props[i][sprops[j].key.bv_len] ) continue; + if ( sprops[j].sflag ) + sflags |= sprops[j].sflag; + else + sflags = 0; + got_sflags++; + } + break; + } + if ( BER_BVISNULL( &sprops[j].key )) { + ldap_charray_free( props ); + return LDAP_NOT_SUPPORTED; + } + } + + if(got_sflags) { + secprops->security_flags = sflags; + } + if(got_min_ssf) { + secprops->min_ssf = min_ssf; + } + if(got_max_ssf) { + secprops->max_ssf = max_ssf; + } + if(got_maxbufsize) { + secprops->maxbufsize = maxbufsize; + } + + ldap_charray_free( props ); + return LDAP_SUCCESS; +} + +int +ldap_int_sasl_config( struct ldapoptions *lo, int option, const char *arg ) +{ + int rc; + + switch( option ) { + case LDAP_OPT_X_SASL_SECPROPS: + rc = ldap_pvt_sasl_secprops( arg, &lo->ldo_sasl_secprops ); + if( rc == LDAP_SUCCESS ) return 0; + } + + return -1; +} + +int +ldap_int_sasl_get_option( LDAP *ld, int option, void *arg ) +{ + if ( option == LDAP_OPT_X_SASL_MECHLIST ) { + *(char ***)arg = (char **)sasl_global_listmech(); + return 0; + } + + if ( ld == NULL ) + return -1; + + switch ( option ) { + case LDAP_OPT_X_SASL_MECH: { + *(char **)arg = ld->ld_options.ldo_def_sasl_mech + ? LDAP_STRDUP( ld->ld_options.ldo_def_sasl_mech ) : NULL; + } break; + case LDAP_OPT_X_SASL_REALM: { + *(char **)arg = ld->ld_options.ldo_def_sasl_realm + ? LDAP_STRDUP( ld->ld_options.ldo_def_sasl_realm ) : NULL; + } break; + case LDAP_OPT_X_SASL_AUTHCID: { + *(char **)arg = ld->ld_options.ldo_def_sasl_authcid + ? LDAP_STRDUP( ld->ld_options.ldo_def_sasl_authcid ) : NULL; + } break; + case LDAP_OPT_X_SASL_AUTHZID: { + *(char **)arg = ld->ld_options.ldo_def_sasl_authzid + ? LDAP_STRDUP( ld->ld_options.ldo_def_sasl_authzid ) : NULL; + } break; + + case LDAP_OPT_X_SASL_SSF: { + int sc; + sasl_ssf_t *ssf; + sasl_conn_t *ctx; + + if( ld->ld_defconn == NULL ) { + return -1; + } + + ctx = ld->ld_defconn->lconn_sasl_sockctx; + + if ( ctx == NULL ) { + return -1; + } + + sc = sasl_getprop( ctx, SASL_SSF, + (SASL_CONST void **)(char *) &ssf ); + + if ( sc != SASL_OK ) { + return -1; + } + + *(ber_len_t *)arg = *ssf; + } break; + + case LDAP_OPT_X_SASL_SSF_EXTERNAL: + /* this option is write only */ + return -1; + + case LDAP_OPT_X_SASL_SSF_MIN: + *(ber_len_t *)arg = ld->ld_options.ldo_sasl_secprops.min_ssf; + break; + case LDAP_OPT_X_SASL_SSF_MAX: + *(ber_len_t *)arg = ld->ld_options.ldo_sasl_secprops.max_ssf; + break; + case LDAP_OPT_X_SASL_MAXBUFSIZE: + *(ber_len_t *)arg = ld->ld_options.ldo_sasl_secprops.maxbufsize; + break; + case LDAP_OPT_X_SASL_NOCANON: + *(int *)arg = (int) LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_SASL_NOCANON ); + break; + + case LDAP_OPT_X_SASL_USERNAME: { + int sc; + char *username; + sasl_conn_t *ctx; + + if( ld->ld_defconn == NULL ) { + return -1; + } + + ctx = ld->ld_defconn->lconn_sasl_authctx; + + if ( ctx == NULL ) { + return -1; + } + + sc = sasl_getprop( ctx, SASL_USERNAME, + (SASL_CONST void **)(char **) &username ); + + if ( sc != SASL_OK ) { + return -1; + } + + *(char **)arg = username ? LDAP_STRDUP( username ) : NULL; + } break; + + case LDAP_OPT_X_SASL_SECPROPS: + /* this option is write only */ + return -1; + +#ifdef SASL_GSS_CREDS + case LDAP_OPT_X_SASL_GSS_CREDS: { + sasl_conn_t *ctx; + int sc; + + if ( ld->ld_defconn == NULL ) + return -1; + + ctx = ld->ld_defconn->lconn_sasl_authctx; + if ( ctx == NULL ) + return -1; + + sc = sasl_getprop( ctx, SASL_GSS_CREDS, arg ); + if ( sc != SASL_OK ) + return -1; + } + break; +#endif + + default: + return -1; + } + return 0; +} + +int +ldap_int_sasl_set_option( LDAP *ld, int option, void *arg ) +{ + if ( ld == NULL ) + return -1; + + if ( arg == NULL && option != LDAP_OPT_X_SASL_NOCANON ) + return -1; + + switch ( option ) { + case LDAP_OPT_X_SASL_SSF: + case LDAP_OPT_X_SASL_USERNAME: + /* This option is read-only */ + return -1; + + case LDAP_OPT_X_SASL_SSF_EXTERNAL: { + int sc; +#if SASL_VERSION_MAJOR < 2 + sasl_external_properties_t extprops; +#else + sasl_ssf_t sasl_ssf; +#endif + sasl_conn_t *ctx; + + if( ld->ld_defconn == NULL ) { + return -1; + } + + ctx = ld->ld_defconn->lconn_sasl_authctx; + + if ( ctx == NULL ) { + return -1; + } + +#if SASL_VERSION_MAJOR >= 2 + sasl_ssf = * (ber_len_t *)arg; + sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL, &sasl_ssf); +#else + memset(&extprops, 0L, sizeof(extprops)); + + extprops.ssf = * (ber_len_t *) arg; + + sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL, + (void *) &extprops ); +#endif + + if ( sc != SASL_OK ) { + return -1; + } + } break; + + case LDAP_OPT_X_SASL_SSF_MIN: + ld->ld_options.ldo_sasl_secprops.min_ssf = *(ber_len_t *)arg; + break; + case LDAP_OPT_X_SASL_SSF_MAX: + ld->ld_options.ldo_sasl_secprops.max_ssf = *(ber_len_t *)arg; + break; + case LDAP_OPT_X_SASL_MAXBUFSIZE: + ld->ld_options.ldo_sasl_secprops.maxbufsize = *(ber_len_t *)arg; + break; + case LDAP_OPT_X_SASL_NOCANON: + if ( arg == LDAP_OPT_OFF ) { + LDAP_BOOL_CLR(&ld->ld_options, LDAP_BOOL_SASL_NOCANON ); + } else { + LDAP_BOOL_SET(&ld->ld_options, LDAP_BOOL_SASL_NOCANON ); + } + break; + + case LDAP_OPT_X_SASL_SECPROPS: { + int sc; + sc = ldap_pvt_sasl_secprops( (char *) arg, + &ld->ld_options.ldo_sasl_secprops ); + + return sc == LDAP_SUCCESS ? 0 : -1; + } + +#ifdef SASL_GSS_CREDS + case LDAP_OPT_X_SASL_GSS_CREDS: { + sasl_conn_t *ctx; + int sc; + + if ( ld->ld_defconn == NULL ) + return -1; + + ctx = ld->ld_defconn->lconn_sasl_authctx; + if ( ctx == NULL ) + return -1; + + sc = sasl_setprop( ctx, SASL_GSS_CREDS, arg ); + if ( sc != SASL_OK ) + return -1; + } + break; +#endif + + default: + return -1; + } + return 0; +} + +#ifdef LDAP_R_COMPILE +#define LDAP_DEBUG_R_SASL +void *ldap_pvt_sasl_mutex_new(void) +{ + ldap_pvt_thread_mutex_t *mutex; + + mutex = (ldap_pvt_thread_mutex_t *) LDAP_CALLOC( 1, + sizeof(ldap_pvt_thread_mutex_t) ); + + if ( ldap_pvt_thread_mutex_init( mutex ) == 0 ) { + return mutex; + } + LDAP_FREE( mutex ); +#ifndef LDAP_DEBUG_R_SASL + assert( 0 ); +#endif /* !LDAP_DEBUG_R_SASL */ + return NULL; +} + +int ldap_pvt_sasl_mutex_lock(void *mutex) +{ +#ifdef LDAP_DEBUG_R_SASL + if ( mutex == NULL ) { + return SASL_OK; + } +#else /* !LDAP_DEBUG_R_SASL */ + assert( mutex != NULL ); +#endif /* !LDAP_DEBUG_R_SASL */ + return ldap_pvt_thread_mutex_lock( (ldap_pvt_thread_mutex_t *)mutex ) + ? SASL_FAIL : SASL_OK; +} + +int ldap_pvt_sasl_mutex_unlock(void *mutex) +{ +#ifdef LDAP_DEBUG_R_SASL + if ( mutex == NULL ) { + return SASL_OK; + } +#else /* !LDAP_DEBUG_R_SASL */ + assert( mutex != NULL ); +#endif /* !LDAP_DEBUG_R_SASL */ + return ldap_pvt_thread_mutex_unlock( (ldap_pvt_thread_mutex_t *)mutex ) + ? SASL_FAIL : SASL_OK; +} + +void ldap_pvt_sasl_mutex_dispose(void *mutex) +{ +#ifdef LDAP_DEBUG_R_SASL + if ( mutex == NULL ) { + return; + } +#else /* !LDAP_DEBUG_R_SASL */ + assert( mutex != NULL ); +#endif /* !LDAP_DEBUG_R_SASL */ + (void) ldap_pvt_thread_mutex_destroy( (ldap_pvt_thread_mutex_t *)mutex ); + LDAP_FREE( mutex ); +} +#endif + +#else +int ldap_int_sasl_init( void ) +{ return LDAP_SUCCESS; } + +int ldap_int_sasl_close( LDAP *ld, LDAPConn *lc ) +{ return LDAP_SUCCESS; } + +int +ldap_int_sasl_bind( + LDAP *ld, + const char *dn, + const char *mechs, + LDAPControl **sctrls, + LDAPControl **cctrls, + unsigned flags, + LDAP_SASL_INTERACT_PROC *interact, + void *defaults, + LDAPMessage *result, + const char **rmech, + int *msgid ) +{ return LDAP_NOT_SUPPORTED; } + +int +ldap_int_sasl_external( + LDAP *ld, + LDAPConn *conn, + const char * authid, + ber_len_t ssf ) +{ return LDAP_SUCCESS; } + +#endif /* HAVE_CYRUS_SASL */ diff --git a/libraries/libldap/dds.c b/libraries/libldap/dds.c new file mode 100644 index 0000000..1e908fb --- /dev/null +++ b/libraries/libldap/dds.c @@ -0,0 +1,156 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2005-2021 The OpenLDAP Foundation. + * Portions Copyright 2005-2006 SysNet s.n.c. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: + * This work was developed by Pierangelo Masarati for inclusion + * in OpenLDAP Software */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +int +ldap_parse_refresh( LDAP *ld, LDAPMessage *res, ber_int_t *newttl ) +{ + int rc; + struct berval *retdata = NULL; + ber_tag_t tag; + BerElement *ber; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( res != NULL ); + assert( newttl != NULL ); + + *newttl = 0; + + rc = ldap_parse_extended_result( ld, res, NULL, &retdata, 0 ); + + if ( rc != LDAP_SUCCESS ) { + return rc; + } + + if ( ld->ld_errno != LDAP_SUCCESS ) { + return ld->ld_errno; + } + + if ( retdata == NULL ) { + rc = ld->ld_errno = LDAP_DECODING_ERROR; + return rc; + } + + ber = ber_init( retdata ); + if ( ber == NULL ) { + rc = ld->ld_errno = LDAP_NO_MEMORY; + goto done; + } + + /* check the tag */ + tag = ber_scanf( ber, "{i}", newttl ); + ber_free( ber, 1 ); + + if ( tag != LDAP_TAG_EXOP_REFRESH_RES_TTL ) { + *newttl = 0; + rc = ld->ld_errno = LDAP_DECODING_ERROR; + } + +done:; + if ( retdata ) { + ber_bvfree( retdata ); + } + + return rc; +} + +int +ldap_refresh( + LDAP *ld, + struct berval *dn, + ber_int_t ttl, + LDAPControl **sctrls, + LDAPControl **cctrls, + int *msgidp ) +{ + struct berval bv = { 0, NULL }; + BerElement *ber = NULL; + int rc; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( dn != NULL ); + assert( msgidp != NULL ); + + *msgidp = -1; + + ber = ber_alloc_t( LBER_USE_DER ); + + if ( ber == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + return ld->ld_errno; + } + + ber_printf( ber, "{tOtiN}", + LDAP_TAG_EXOP_REFRESH_REQ_DN, dn, + LDAP_TAG_EXOP_REFRESH_REQ_TTL, ttl ); + + rc = ber_flatten2( ber, &bv, 0 ); + + if ( rc < 0 ) { + rc = ld->ld_errno = LDAP_ENCODING_ERROR; + goto done; + } + + rc = ldap_extended_operation( ld, LDAP_EXOP_REFRESH, &bv, + sctrls, cctrls, msgidp ); + +done:; + ber_free( ber, 1 ); + + return rc; +} + +int +ldap_refresh_s( + LDAP *ld, + struct berval *dn, + ber_int_t ttl, + ber_int_t *newttl, + LDAPControl **sctrls, + LDAPControl **cctrls ) +{ + int rc; + int msgid; + LDAPMessage *res; + + rc = ldap_refresh( ld, dn, ttl, sctrls, cctrls, &msgid ); + if ( rc != LDAP_SUCCESS ) return rc; + + rc = ldap_result( ld, msgid, LDAP_MSG_ALL, (struct timeval *)NULL, &res ); + if( rc == -1 || !res ) return ld->ld_errno; + + rc = ldap_parse_refresh( ld, res, newttl ); + if( rc != LDAP_SUCCESS ) { + ldap_msgfree( res ); + return rc; + } + + return ldap_result2error( ld, res, 1 ); +} + diff --git a/libraries/libldap/delete.c b/libraries/libldap/delete.c new file mode 100644 index 0000000..256b6b5 --- /dev/null +++ b/libraries/libldap/delete.c @@ -0,0 +1,174 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +/* + * A delete request looks like this: + * DelRequet ::= DistinguishedName, + */ + +BerElement * +ldap_build_delete_req( + LDAP *ld, + LDAP_CONST char *dn, + LDAPControl **sctrls, + LDAPControl **cctrls, + int *msgidp ) +{ + BerElement *ber; + int rc; + + /* create a message to send */ + if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) { + return( NULL ); + } + + LDAP_NEXT_MSGID( ld, *msgidp ); + rc = ber_printf( ber, "{its", /* '}' */ + *msgidp, LDAP_REQ_DELETE, dn ); + if ( rc == -1 ) + { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return( NULL ); + } + + /* Put Server Controls */ + if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) { + ber_free( ber, 1 ); + return( NULL ); + } + + if ( ber_printf( ber, /*{*/ "N}" ) == -1 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return( NULL ); + } + + return( ber ); +} + +/* + * ldap_delete_ext - initiate an ldap extended delete operation. Parameters: + * + * ld LDAP descriptor + * dn DN of the object to delete + * sctrls Server Controls + * cctrls Client Controls + * msgidp Message Id Pointer + * + * Example: + * rc = ldap_delete( ld, dn, sctrls, cctrls, msgidp ); + */ +int +ldap_delete_ext( + LDAP *ld, + LDAP_CONST char* dn, + LDAPControl **sctrls, + LDAPControl **cctrls, + int *msgidp ) +{ + int rc; + BerElement *ber; + ber_int_t id; + + Debug( LDAP_DEBUG_TRACE, "ldap_delete_ext\n", 0, 0, 0 ); + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( dn != NULL ); + assert( msgidp != NULL ); + + /* check client controls */ + rc = ldap_int_client_controls( ld, cctrls ); + if( rc != LDAP_SUCCESS ) return rc; + + ber = ldap_build_delete_req( ld, dn, sctrls, cctrls, &id ); + if( !ber ) + return ld->ld_errno; + + /* send the message */ + *msgidp = ldap_send_initial_request( ld, LDAP_REQ_DELETE, dn, ber, id ); + + if(*msgidp < 0) + return ld->ld_errno; + + return LDAP_SUCCESS; +} + +int +ldap_delete_ext_s( + LDAP *ld, + LDAP_CONST char *dn, + LDAPControl **sctrls, + LDAPControl **cctrls ) +{ + int msgid; + int rc; + LDAPMessage *res; + + rc = ldap_delete_ext( ld, dn, sctrls, cctrls, &msgid ); + + if( rc != LDAP_SUCCESS ) + return( ld->ld_errno ); + + if ( ldap_result( ld, msgid, LDAP_MSG_ALL, (struct timeval *) NULL, &res ) == -1 || !res ) + return( ld->ld_errno ); + + return( ldap_result2error( ld, res, 1 ) ); +} +/* + * ldap_delete - initiate an ldap (and X.500) delete operation. Parameters: + * + * ld LDAP descriptor + * dn DN of the object to delete + * + * Example: + * msgid = ldap_delete( ld, dn ); + */ +int +ldap_delete( LDAP *ld, LDAP_CONST char *dn ) +{ + int msgid; + + /* + * A delete request looks like this: + * DelRequet ::= DistinguishedName, + */ + + Debug( LDAP_DEBUG_TRACE, "ldap_delete\n", 0, 0, 0 ); + + return ldap_delete_ext( ld, dn, NULL, NULL, &msgid ) == LDAP_SUCCESS + ? msgid : -1 ; +} + + +int +ldap_delete_s( LDAP *ld, LDAP_CONST char *dn ) +{ + return ldap_delete_ext_s( ld, dn, NULL, NULL ); +} diff --git a/libraries/libldap/deref.c b/libraries/libldap/deref.c new file mode 100644 index 0000000..a5071c0 --- /dev/null +++ b/libraries/libldap/deref.c @@ -0,0 +1,282 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * Portions Copyright 2008 Pierangelo Masarati. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: + * This work was initially developed by Pierangelo Masarati + * for inclusion in OpenLDAP Software. + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +int +ldap_create_deref_control_value( + LDAP *ld, + LDAPDerefSpec *ds, + struct berval *value ) +{ + BerElement *ber = NULL; + ber_tag_t tag; + int i; + + if ( ld == NULL || value == NULL || ds == NULL ) + { + if ( ld ) + ld->ld_errno = LDAP_PARAM_ERROR; + return LDAP_PARAM_ERROR; + } + + assert( LDAP_VALID( ld ) ); + + value->bv_val = NULL; + value->bv_len = 0; + ld->ld_errno = LDAP_SUCCESS; + + ber = ldap_alloc_ber_with_options( ld ); + if ( ber == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + return ld->ld_errno; + } + + tag = ber_printf( ber, "{" /*}*/ ); + if ( tag == LBER_ERROR ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + goto done; + } + + for ( i = 0; ds[i].derefAttr != NULL; i++ ) { + int j; + + tag = ber_printf( ber, "{s{" /*}}*/ , ds[i].derefAttr ); + if ( tag == LBER_ERROR ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + goto done; + } + + for ( j = 0; ds[i].attributes[j] != NULL; j++ ) { + tag = ber_printf( ber, "s", ds[i].attributes[ j ] ); + if ( tag == LBER_ERROR ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + goto done; + } + } + + tag = ber_printf( ber, /*{{*/ "}N}" ); + if ( tag == LBER_ERROR ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + goto done; + } + } + + tag = ber_printf( ber, /*{*/ "}" ); + if ( tag == LBER_ERROR ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + goto done; + } + + if ( ber_flatten2( ber, value, 1 ) == -1 ) { + ld->ld_errno = LDAP_NO_MEMORY; + } + +done:; + if ( ber != NULL ) { + ber_free( ber, 1 ); + } + + return ld->ld_errno; +} + +int +ldap_create_deref_control( + LDAP *ld, + LDAPDerefSpec *ds, + int iscritical, + LDAPControl **ctrlp ) +{ + struct berval value; + + if ( ctrlp == NULL ) { + ld->ld_errno = LDAP_PARAM_ERROR; + return ld->ld_errno; + } + + ld->ld_errno = ldap_create_deref_control_value( ld, ds, &value ); + if ( ld->ld_errno == LDAP_SUCCESS ) { + ld->ld_errno = ldap_control_create( LDAP_CONTROL_X_DEREF, + iscritical, &value, 0, ctrlp ); + if ( ld->ld_errno != LDAP_SUCCESS ) { + LDAP_FREE( value.bv_val ); + } + } + + return ld->ld_errno; +} + +void +ldap_derefresponse_free( LDAPDerefRes *dr ) +{ + for ( ; dr; ) { + LDAPDerefRes *drnext = dr->next; + LDAPDerefVal *dv; + + LDAP_FREE( dr->derefAttr ); + LDAP_FREE( dr->derefVal.bv_val ); + + for ( dv = dr->attrVals; dv; ) { + LDAPDerefVal *dvnext = dv->next; + LDAP_FREE( dv->type ); + ber_bvarray_free( dv->vals ); + LDAP_FREE( dv ); + dv = dvnext; + } + + LDAP_FREE( dr ); + + dr = drnext; + } +} + +int +ldap_parse_derefresponse_control( + LDAP *ld, + LDAPControl *ctrl, + LDAPDerefRes **drp2 ) +{ + BerElement *ber; + ber_tag_t tag; + ber_len_t len; + char *last; + LDAPDerefRes *drhead = NULL, **drp; + + if ( ld == NULL || ctrl == NULL || drp2 == NULL ) { + if ( ld ) + ld->ld_errno = LDAP_PARAM_ERROR; + return LDAP_PARAM_ERROR; + } + + /* Create a BerElement from the berval returned in the control. */ + ber = ber_init( &ctrl->ldctl_value ); + + if ( ber == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + return ld->ld_errno; + } + + /* Extract the count and cookie from the control. */ + drp = &drhead; + for ( tag = ber_first_element( ber, &len, &last ); + tag != LBER_DEFAULT; + tag = ber_next_element( ber, &len, last ) ) + { + LDAPDerefRes *dr; + LDAPDerefVal **dvp; + char *last2; + + dr = LDAP_CALLOC( 1, sizeof(LDAPDerefRes) ); + dvp = &dr->attrVals; + + tag = ber_scanf( ber, "{ao", &dr->derefAttr, &dr->derefVal ); + if ( tag == LBER_ERROR ) { + goto done; + } + + tag = ber_peek_tag( ber, &len ); + if ( tag == (LBER_CONSTRUCTED|LBER_CLASS_CONTEXT) ) { + for ( tag = ber_first_element( ber, &len, &last2 ); + tag != LBER_DEFAULT; + tag = ber_next_element( ber, &len, last2 ) ) + { + LDAPDerefVal *dv; + + dv = LDAP_CALLOC( 1, sizeof(LDAPDerefVal) ); + + tag = ber_scanf( ber, "{a[W]}", &dv->type, &dv->vals ); + if ( tag == LBER_ERROR ) { + goto done; + } + + *dvp = dv; + dvp = &dv->next; + } + } + + tag = ber_scanf( ber, "}" ); + if ( tag == LBER_ERROR ) { + goto done; + } + + *drp = dr; + drp = &dr->next; + } + + tag = 0; + +done:; + ber_free( ber, 1 ); + + if ( tag == LBER_ERROR ) { + if ( drhead != NULL ) { + ldap_derefresponse_free( drhead ); + } + + *drp2 = NULL; + ld->ld_errno = LDAP_DECODING_ERROR; + + } else { + *drp2 = drhead; + ld->ld_errno = LDAP_SUCCESS; + } + + return ld->ld_errno; +} + +int +ldap_parse_deref_control( + LDAP *ld, + LDAPControl **ctrls, + LDAPDerefRes **drp ) +{ + LDAPControl *c; + + if ( drp == NULL ) { + ld->ld_errno = LDAP_PARAM_ERROR; + return ld->ld_errno; + } + + *drp = NULL; + + if ( ctrls == NULL ) { + ld->ld_errno = LDAP_CONTROL_NOT_FOUND; + return ld->ld_errno; + } + + c = ldap_control_find( LDAP_CONTROL_X_DEREF, ctrls, NULL ); + if ( c == NULL ) { + /* No deref control was found. */ + ld->ld_errno = LDAP_CONTROL_NOT_FOUND; + return ld->ld_errno; + } + + ld->ld_errno = ldap_parse_derefresponse_control( ld, c, drp ); + + return ld->ld_errno; +} + diff --git a/libraries/libldap/dnssrv.c b/libraries/libldap/dnssrv.c new file mode 100644 index 0000000..e665c27 --- /dev/null +++ b/libraries/libldap/dnssrv.c @@ -0,0 +1,431 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +/* + * locate LDAP servers using DNS SRV records. + * Location code based on MIT Kerberos KDC location code. + */ +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/param.h> +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +#ifdef HAVE_ARPA_NAMESER_H +#include <arpa/nameser.h> +#endif +#ifdef HAVE_RESOLV_H +#include <resolv.h> +#endif + +int ldap_dn2domain( + LDAP_CONST char *dn_in, + char **domainp) +{ + int i, j; + char *ndomain; + LDAPDN dn = NULL; + LDAPRDN rdn = NULL; + LDAPAVA *ava = NULL; + struct berval domain = BER_BVNULL; + static const struct berval DC = BER_BVC("DC"); + static const struct berval DCOID = BER_BVC("0.9.2342.19200300.100.1.25"); + + assert( dn_in != NULL ); + assert( domainp != NULL ); + + *domainp = NULL; + + if ( ldap_str2dn( dn_in, &dn, LDAP_DN_FORMAT_LDAP ) != LDAP_SUCCESS ) { + return -2; + } + + if( dn ) for( i=0; dn[i] != NULL; i++ ) { + rdn = dn[i]; + + for( j=0; rdn[j] != NULL; j++ ) { + ava = rdn[j]; + + if( rdn[j+1] == NULL && + (ava->la_flags & LDAP_AVA_STRING) && + ava->la_value.bv_len && + ( ber_bvstrcasecmp( &ava->la_attr, &DC ) == 0 + || ber_bvcmp( &ava->la_attr, &DCOID ) == 0 ) ) + { + if( domain.bv_len == 0 ) { + ndomain = LDAP_REALLOC( domain.bv_val, + ava->la_value.bv_len + 1); + + if( ndomain == NULL ) { + goto return_error; + } + + domain.bv_val = ndomain; + + AC_MEMCPY( domain.bv_val, ava->la_value.bv_val, + ava->la_value.bv_len ); + + domain.bv_len = ava->la_value.bv_len; + domain.bv_val[domain.bv_len] = '\0'; + + } else { + ndomain = LDAP_REALLOC( domain.bv_val, + ava->la_value.bv_len + sizeof(".") + domain.bv_len ); + + if( ndomain == NULL ) { + goto return_error; + } + + domain.bv_val = ndomain; + domain.bv_val[domain.bv_len++] = '.'; + AC_MEMCPY( &domain.bv_val[domain.bv_len], + ava->la_value.bv_val, ava->la_value.bv_len ); + domain.bv_len += ava->la_value.bv_len; + domain.bv_val[domain.bv_len] = '\0'; + } + } else { + domain.bv_len = 0; + } + } + } + + + if( domain.bv_len == 0 && domain.bv_val != NULL ) { + LDAP_FREE( domain.bv_val ); + domain.bv_val = NULL; + } + + ldap_dnfree( dn ); + *domainp = domain.bv_val; + return 0; + +return_error: + ldap_dnfree( dn ); + LDAP_FREE( domain.bv_val ); + return -1; +} + +int ldap_domain2dn( + LDAP_CONST char *domain_in, + char **dnp) +{ + char *domain, *s, *tok_r, *dn, *dntmp; + size_t loc; + + assert( domain_in != NULL ); + assert( dnp != NULL ); + + domain = LDAP_STRDUP(domain_in); + if (domain == NULL) { + return LDAP_NO_MEMORY; + } + dn = NULL; + loc = 0; + + for (s = ldap_pvt_strtok(domain, ".", &tok_r); + s != NULL; + s = ldap_pvt_strtok(NULL, ".", &tok_r)) + { + size_t len = strlen(s); + + dntmp = (char *) LDAP_REALLOC(dn, loc + sizeof(",dc=") + len ); + if (dntmp == NULL) { + if (dn != NULL) + LDAP_FREE(dn); + LDAP_FREE(domain); + return LDAP_NO_MEMORY; + } + + dn = dntmp; + + if (loc > 0) { + /* not first time. */ + strcpy(dn + loc, ","); + loc++; + } + strcpy(dn + loc, "dc="); + loc += sizeof("dc=")-1; + + strcpy(dn + loc, s); + loc += len; + } + + LDAP_FREE(domain); + *dnp = dn; + return LDAP_SUCCESS; +} + +#ifdef HAVE_RES_QUERY +#define DNSBUFSIZ (64*1024) +#define MAXHOST 254 /* RFC 1034, max length is 253 chars */ +typedef struct srv_record { + u_short priority; + u_short weight; + u_short port; + char hostname[MAXHOST]; +} srv_record; + +/* Linear Congruential Generator - we don't need + * high quality randomness, and we don't want to + * interfere with anyone else's use of srand(). + * + * The PRNG here cycles thru 941,955 numbers. + */ +static float srv_seed; + +static void srv_srand(int seed) { + srv_seed = (float)seed / (float)RAND_MAX; +} + +static float srv_rand() { + float val = 9821.0 * srv_seed + .211327; + srv_seed = val - (int)val; + return srv_seed; +} + +static int srv_cmp(const void *aa, const void *bb){ + srv_record *a=(srv_record *)aa; + srv_record *b=(srv_record *)bb; + int i = a->priority - b->priority; + if (i) return i; + return b->weight - a->weight; +} + +static void srv_shuffle(srv_record *a, int n) { + int i, j, total = 0, r, p; + + for (i=0; i<n; i++) + total += a[i].weight; + + /* all weights are zero, do a straight Fisher-Yates shuffle */ + if (!total) { + while (n) { + srv_record t; + i = srv_rand() * n--; + t = a[n]; + a[n] = a[i]; + a[i] = t; + } + return; + } + + /* Do a shuffle per RFC2782 Page 4 */ + p = n; + for (i=0; i<n-1; i++) { + r = srv_rand() * total; + for (j=0; j<p; j++) { + r -= a[j].weight; + if (r <= 0) { + if (j) { + srv_record t = a[0]; + a[0] = a[j]; + a[j] = t; + } + total -= a[0].weight; + a++; + p--; + break; + } + } + } +} +#endif /* HAVE_RES_QUERY */ + +/* + * Lookup and return LDAP servers for domain (using the DNS + * SRV record _ldap._tcp.domain). + */ +int ldap_domain2hostlist( + LDAP_CONST char *domain, + char **list ) +{ +#ifdef HAVE_RES_QUERY + char *request; + char *hostlist = NULL; + srv_record *hostent_head=NULL; + int i, j; + int rc, len, cur = 0; + unsigned char reply[DNSBUFSIZ]; + int hostent_count=0; + + assert( domain != NULL ); + assert( list != NULL ); + if( *domain == '\0' ) { + return LDAP_PARAM_ERROR; + } + + request = LDAP_MALLOC(strlen(domain) + sizeof("_ldap._tcp.")); + if (request == NULL) { + return LDAP_NO_MEMORY; + } + sprintf(request, "_ldap._tcp.%s", domain); + + LDAP_MUTEX_LOCK(&ldap_int_resolv_mutex); + + rc = LDAP_UNAVAILABLE; +#ifdef NS_HFIXEDSZ + /* Bind 8/9 interface */ + len = res_query(request, ns_c_in, ns_t_srv, reply, sizeof(reply)); +# ifndef T_SRV +# define T_SRV ns_t_srv +# endif +#else + /* Bind 4 interface */ +# ifndef T_SRV +# define T_SRV 33 +# endif + + len = res_query(request, C_IN, T_SRV, reply, sizeof(reply)); +#endif + if (len >= 0) { + unsigned char *p; + char host[DNSBUFSIZ]; + int status; + u_short port, priority, weight; + + /* Parse out query */ + p = reply; + +#ifdef NS_HFIXEDSZ + /* Bind 8/9 interface */ + p += NS_HFIXEDSZ; +#elif defined(HFIXEDSZ) + /* Bind 4 interface w/ HFIXEDSZ */ + p += HFIXEDSZ; +#else + /* Bind 4 interface w/o HFIXEDSZ */ + p += sizeof(HEADER); +#endif + + status = dn_expand(reply, reply + len, p, host, sizeof(host)); + if (status < 0) { + goto out; + } + p += status; + p += 4; + + while (p < reply + len) { + int type, class, ttl, size; + status = dn_expand(reply, reply + len, p, host, sizeof(host)); + if (status < 0) { + goto out; + } + p += status; + type = (p[0] << 8) | p[1]; + p += 2; + class = (p[0] << 8) | p[1]; + p += 2; + ttl = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; + p += 4; + size = (p[0] << 8) | p[1]; + p += 2; + if (type == T_SRV) { + status = dn_expand(reply, reply + len, p + 6, host, sizeof(host)); + if (status < 0) { + goto out; + } + + /* Get priority weight and port */ + priority = (p[0] << 8) | p[1]; + weight = (p[2] << 8) | p[3]; + port = (p[4] << 8) | p[5]; + + if ( port == 0 || host[ 0 ] == '\0' ) { + goto add_size; + } + + hostent_head = (srv_record *) LDAP_REALLOC(hostent_head, (hostent_count+1)*(sizeof(srv_record))); + if(hostent_head==NULL){ + rc=LDAP_NO_MEMORY; + goto out; + } + hostent_head[hostent_count].priority=priority; + hostent_head[hostent_count].weight=weight; + hostent_head[hostent_count].port=port; + strncpy(hostent_head[hostent_count].hostname, host, MAXHOST-1); + hostent_head[hostent_count].hostname[MAXHOST-1] = '\0'; + hostent_count++; + } +add_size:; + p += size; + } + if (!hostent_head) goto out; + qsort(hostent_head, hostent_count, sizeof(srv_record), srv_cmp); + + if (!srv_seed) + srv_srand(time(0L)); + + /* shuffle records of same priority */ + j = 0; + priority = hostent_head[0].priority; + for (i=1; i<hostent_count; i++) { + if (hostent_head[i].priority != priority) { + priority = hostent_head[i].priority; + if (i-j > 1) + srv_shuffle(hostent_head+j, i-j); + j = i; + } + } + if (i-j > 1) + srv_shuffle(hostent_head+j, i-j); + + for(i=0; i<hostent_count; i++){ + int buflen; + buflen = strlen(hostent_head[i].hostname) + STRLENOF(":65535 "); + hostlist = (char *) LDAP_REALLOC(hostlist, cur+buflen+1); + if (hostlist == NULL) { + rc = LDAP_NO_MEMORY; + goto out; + } + if(cur>0){ + hostlist[cur++]=' '; + } + cur += sprintf(&hostlist[cur], "%s:%hu", hostent_head[i].hostname, hostent_head[i].port); + } + } + + if (hostlist == NULL) { + /* No LDAP servers found in DNS. */ + rc = LDAP_UNAVAILABLE; + goto out; + } + + rc = LDAP_SUCCESS; + *list = hostlist; + + out: + LDAP_MUTEX_UNLOCK(&ldap_int_resolv_mutex); + + if (request != NULL) { + LDAP_FREE(request); + } + if (hostent_head != NULL) { + LDAP_FREE(hostent_head); + } + if (rc != LDAP_SUCCESS && hostlist != NULL) { + LDAP_FREE(hostlist); + } + return rc; +#else + return LDAP_NOT_SUPPORTED; +#endif /* HAVE_RES_QUERY */ +} diff --git a/libraries/libldap/dntest.c b/libraries/libldap/dntest.c new file mode 100644 index 0000000..49b4002 --- /dev/null +++ b/libraries/libldap/dntest.c @@ -0,0 +1,296 @@ +/* dntest.c -- OpenLDAP DN API Test Program */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENT: + * This program was initially developed by Pierangelo Masarati <ando@OpenLDAP.org> + * for inclusion in OpenLDAP Software. + */ + +/* + * This program is designed to test the ldap_str2dn/ldap_dn2str + * functions + */ +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/unistd.h> + +#include <ldap.h> + +#include "ldap-int.h" + +#include "ldif.h" +#include "lutil.h" +#include "lutil_ldap.h" +#include "ldap_defaults.h" + +int +main( int argc, char *argv[] ) +{ + int rc, i, debug = 0, f2 = 0; + unsigned flags[ 2 ] = { 0U, 0 }; + char *strin, *str = NULL, buf[ 1024 ]; + LDAPDN dn, dn2 = NULL; + + while ( 1 ) { + int opt = getopt( argc, argv, "d:" ); + + if ( opt == EOF ) { + break; + } + + switch ( opt ) { + case 'd': + debug = atoi( optarg ); + break; + } + } + + optind--; + argc -= optind; + argv += optind; + + if ( argc < 2 ) { + fprintf( stderr, "usage: dntest <dn> [flags-in[,...]] [flags-out[,...]]\n\n" ); + fprintf( stderr, "\tflags-in: V3,V2,DCE,<flags>\n" ); + fprintf( stderr, "\tflags-out: V3,V2,UFN,DCE,AD,<flags>\n\n" ); + fprintf( stderr, "\t<flags>: PRETTY,PEDANTIC,NOSPACES,NOONESPACE\n\n" ); + return( 0 ); + } + + if ( ber_set_option( NULL, LBER_OPT_DEBUG_LEVEL, &debug ) != LBER_OPT_SUCCESS ) { + fprintf( stderr, "Could not set LBER_OPT_DEBUG_LEVEL %d\n", debug ); + } + if ( ldap_set_option( NULL, LDAP_OPT_DEBUG_LEVEL, &debug ) != LDAP_OPT_SUCCESS ) { + fprintf( stderr, "Could not set LDAP_OPT_DEBUG_LEVEL %d\n", debug ); + } + + if ( strcmp( argv[ 1 ], "-" ) == 0 ) { + size_t len = fgets( buf, sizeof( buf ), stdin ) ? strlen( buf ) : 0; + + if ( len == 0 || buf[ --len ] == '\n' ) { + buf[ len ] = '\0'; + } + strin = buf; + } else { + strin = argv[ 1 ]; + } + + if ( argc >= 3 ) { + for ( i = 0; i < argc - 2; i++ ) { + char *s, *e; + for ( s = argv[ 2 + i ]; s; s = e ) { + e = strchr( s, ',' ); + if ( e != NULL ) { + e[ 0 ] = '\0'; + e++; + } + + if ( !strcasecmp( s, "V3" ) ) { + flags[ i ] |= LDAP_DN_FORMAT_LDAPV3; + } else if ( !strcasecmp( s, "V2" ) ) { + flags[ i ] |= LDAP_DN_FORMAT_LDAPV2; + } else if ( !strcasecmp( s, "DCE" ) ) { + flags[ i ] |= LDAP_DN_FORMAT_DCE; + } else if ( !strcasecmp( s, "UFN" ) ) { + flags[ i ] |= LDAP_DN_FORMAT_UFN; + } else if ( !strcasecmp( s, "AD" ) ) { + flags[ i ] |= LDAP_DN_FORMAT_AD_CANONICAL; + } else if ( !strcasecmp( s, "PRETTY" ) ) { + flags[ i ] |= LDAP_DN_PRETTY; + } else if ( !strcasecmp( s, "PEDANTIC" ) ) { + flags[ i ] |= LDAP_DN_PEDANTIC; + } else if ( !strcasecmp( s, "NOSPACES" ) ) { + flags[ i ] |= LDAP_DN_P_NOLEADTRAILSPACES; + } else if ( !strcasecmp( s, "NOONESPACE" ) ) { + flags[ i ] |= LDAP_DN_P_NOSPACEAFTERRDN; + } + } + } + } + + if ( flags[ 1 ] == 0 ) + flags[ 1 ] = LDAP_DN_FORMAT_LDAPV3; + + f2 = 1; + + rc = ldap_str2dn( strin, &dn, flags[ 0 ] ); + + if ( rc == LDAP_SUCCESS ) { + int i; + if ( dn ) { + for ( i = 0; dn[ i ]; i++ ) { + LDAPRDN rdn = dn[ i ]; + char *rstr = NULL; + + if ( ldap_rdn2str( rdn, &rstr, flags[ f2 ] ) ) { + fprintf( stdout, "\tldap_rdn2str() failed\n" ); + continue; + } + + fprintf( stdout, "\tldap_rdn2str() = \"%s\"\n", rstr ); + ldap_memfree( rstr ); + } + } else { + fprintf( stdout, "\tempty DN\n" ); + } + } + + str = NULL; + if ( rc == LDAP_SUCCESS && + ldap_dn2str( dn, &str, flags[ f2 ] ) == LDAP_SUCCESS ) + { + char **values, *tmp, *tmp2, *str2 = NULL; + int n; + + fprintf( stdout, "\nldap_dn2str(ldap_str2dn(\"%s\"))\n" + "\t= \"%s\"\n", strin, str ); + + switch ( flags[ f2 ] & LDAP_DN_FORMAT_MASK ) { + case LDAP_DN_FORMAT_UFN: + case LDAP_DN_FORMAT_AD_CANONICAL: + return( 0 ); + + case LDAP_DN_FORMAT_LDAPV3: + case LDAP_DN_FORMAT_LDAPV2: + n = ldap_dn2domain( strin, &tmp ); + if ( n ) { + fprintf( stdout, "\nldap_dn2domain(\"%s\") FAILED\n", strin ); + } else { + fprintf( stdout, "\nldap_dn2domain(\"%s\")\n" + "\t= \"%s\"\n", strin, tmp ? tmp : "" ); + } + ldap_memfree( tmp ); + + tmp = ldap_dn2ufn( strin ); + fprintf( stdout, "\nldap_dn2ufn(\"%s\")\n" + "\t= \"%s\"\n", strin, tmp ? tmp : "" ); + ldap_memfree( tmp ); + + tmp = ldap_dn2dcedn( strin ); + fprintf( stdout, "\nldap_dn2dcedn(\"%s\")\n" + "\t= \"%s\"\n", strin, tmp ? tmp : "" ); + tmp2 = ldap_dcedn2dn( tmp ); + fprintf( stdout, "\nldap_dcedn2dn(\"%s\")\n" + "\t= \"%s\"\n", + tmp ? tmp : "", tmp2 ? tmp2 : "" ); + ldap_memfree( tmp ); + ldap_memfree( tmp2 ); + + tmp = ldap_dn2ad_canonical( strin ); + fprintf( stdout, "\nldap_dn2ad_canonical(\"%s\")\n" + "\t= \"%s\"\n", strin, tmp ? tmp : "" ); + ldap_memfree( tmp ); + + fprintf( stdout, "\nldap_explode_dn(\"%s\"):\n", str ); + values = ldap_explode_dn( str, 0 ); + for ( n = 0; values && values[ n ]; n++ ) { + char **vv; + int nn; + + fprintf( stdout, "\t\"%s\"\n", values[ n ] ); + + fprintf( stdout, "\tldap_explode_rdn(\"%s\")\n", + values[ n ] ); + vv = ldap_explode_rdn( values[ n ], 0 ); + for ( nn = 0; vv && vv[ nn ]; nn++ ) { + fprintf( stdout, "\t\t'%s'\n", + vv[ nn ] ); + } + LDAP_VFREE( vv ); + + fprintf( stdout, "\tldap_explode_rdn(\"%s\")" + " (no types)\n", values[ n ] ); + vv = ldap_explode_rdn( values[ n ], 1 ); + for ( nn = 0; vv && vv[ nn ]; nn++ ) { + fprintf( stdout, "\t\t\t\"%s\"\n", + vv[ nn ] ); + } + LDAP_VFREE( vv ); + + } + LDAP_VFREE( values ); + + fprintf( stdout, "\nldap_explode_dn(\"%s\")" + " (no types):\n", str ); + values = ldap_explode_dn( str, 1 ); + for ( n = 0; values && values[ n ]; n++ ) { + fprintf( stdout, "\t\"%s\"\n", values[ n ] ); + } + LDAP_VFREE( values ); + + break; + } + + dn2 = NULL; + rc = ldap_str2dn( str, &dn2, flags[ f2 ] ); + str2 = NULL; + if ( rc == LDAP_SUCCESS && + ldap_dn2str( dn2, &str2, flags[ f2 ] ) + == LDAP_SUCCESS ) { + int iRDN; + + fprintf( stdout, "\n\"%s\"\n\t == \"%s\" ? %s\n", + str, str2, + strcmp( str, str2 ) == 0 ? "yes" : "no" ); + + if( dn != NULL && dn2 == NULL ) { + fprintf( stdout, "dn mismatch\n" ); + } else if (( dn != NULL ) && (dn2 != NULL)) + for ( iRDN = 0; dn[ iRDN ] && dn2[ iRDN ]; iRDN++ ) + { + LDAPRDN r = dn[ iRDN ]; + LDAPRDN r2 = dn2[ iRDN ]; + int iAVA; + + for ( iAVA = 0; r[ iAVA ] && r2[ iAVA ]; iAVA++ ) { + LDAPAVA *a = r[ iAVA ]; + LDAPAVA *a2 = r2[ iAVA ]; + + if ( a->la_attr.bv_len != a2->la_attr.bv_len ) { + fprintf( stdout, "ava(%d), rdn(%d) attr len mismatch (%ld->%ld)\n", + iAVA + 1, iRDN + 1, + a->la_attr.bv_len, a2->la_attr.bv_len ); + } else if ( memcmp( a->la_attr.bv_val, a2->la_attr.bv_val, a->la_attr.bv_len ) ) { + fprintf( stdout, "ava(%d), rdn(%d) attr mismatch\n", + iAVA + 1, iRDN + 1 ); + } else if ( a->la_flags != a2->la_flags ) { + fprintf( stdout, "ava(%d), rdn(%d) flag mismatch (%x->%x)\n", + iAVA + 1, iRDN + 1, a->la_flags, a2->la_flags ); + } else if ( a->la_value.bv_len != a2->la_value.bv_len ) { + fprintf( stdout, "ava(%d), rdn(%d) value len mismatch (%ld->%ld)\n", + iAVA + 1, iRDN + 1, + a->la_value.bv_len, a2->la_value.bv_len ); + } else if ( memcmp( a->la_value.bv_val, a2->la_value.bv_val, a->la_value.bv_len ) ) { + fprintf( stdout, "ava(%d), rdn(%d) value mismatch\n", + iAVA + 1, iRDN + 1 ); + } + } + } + + ldap_dnfree( dn2 ); + ldap_memfree( str2 ); + } + ldap_memfree( str ); + } + ldap_dnfree( dn ); + + /* note: dn is not freed */ + + return( 0 ); +} diff --git a/libraries/libldap/error.c b/libraries/libldap/error.c new file mode 100644 index 0000000..c3dba31 --- /dev/null +++ b/libraries/libldap/error.c @@ -0,0 +1,398 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +void ldap_int_error_init( void ) { +} + +char * +ldap_err2string( int err ) +{ + char *m; + + Debug( LDAP_DEBUG_TRACE, "ldap_err2string\n", 0, 0, 0 ); + + switch ( err ) { +# define C(code, message) case code: m = message; break + + /* LDAPv3 (RFC 4511) codes */ + C(LDAP_SUCCESS, N_("Success")); + C(LDAP_OPERATIONS_ERROR, N_("Operations error")); + C(LDAP_PROTOCOL_ERROR, N_("Protocol error")); + C(LDAP_TIMELIMIT_EXCEEDED, N_("Time limit exceeded")); + C(LDAP_SIZELIMIT_EXCEEDED, N_("Size limit exceeded")); + C(LDAP_COMPARE_FALSE, N_("Compare False")); + C(LDAP_COMPARE_TRUE, N_("Compare True")); + C(LDAP_STRONG_AUTH_NOT_SUPPORTED,N_("Authentication method not supported")); + C(LDAP_STRONG_AUTH_REQUIRED, N_("Strong(er) authentication required")); + + C(LDAP_REFERRAL, N_("Referral")); + C(LDAP_ADMINLIMIT_EXCEEDED, N_("Administrative limit exceeded")); + C(LDAP_UNAVAILABLE_CRITICAL_EXTENSION, + N_("Critical extension is unavailable")); + C(LDAP_CONFIDENTIALITY_REQUIRED,N_("Confidentiality required")); + C(LDAP_SASL_BIND_IN_PROGRESS, N_("SASL bind in progress")); + + C(LDAP_NO_SUCH_ATTRIBUTE, N_("No such attribute")); + C(LDAP_UNDEFINED_TYPE, N_("Undefined attribute type")); + C(LDAP_INAPPROPRIATE_MATCHING, N_("Inappropriate matching")); + C(LDAP_CONSTRAINT_VIOLATION, N_("Constraint violation")); + C(LDAP_TYPE_OR_VALUE_EXISTS, N_("Type or value exists")); + C(LDAP_INVALID_SYNTAX, N_("Invalid syntax")); + + C(LDAP_NO_SUCH_OBJECT, N_("No such object")); + C(LDAP_ALIAS_PROBLEM, N_("Alias problem")); + C(LDAP_INVALID_DN_SYNTAX, N_("Invalid DN syntax")); + + C(LDAP_ALIAS_DEREF_PROBLEM, N_("Alias dereferencing problem")); + + C(LDAP_INAPPROPRIATE_AUTH, N_("Inappropriate authentication")); + C(LDAP_INVALID_CREDENTIALS, N_("Invalid credentials")); + C(LDAP_INSUFFICIENT_ACCESS, N_("Insufficient access")); + C(LDAP_BUSY, N_("Server is busy")); + C(LDAP_UNAVAILABLE, N_("Server is unavailable")); + C(LDAP_UNWILLING_TO_PERFORM, N_("Server is unwilling to perform")); + C(LDAP_LOOP_DETECT, N_("Loop detected")); + + C(LDAP_NAMING_VIOLATION, N_("Naming violation")); + C(LDAP_OBJECT_CLASS_VIOLATION, N_("Object class violation")); + C(LDAP_NOT_ALLOWED_ON_NONLEAF, N_("Operation not allowed on non-leaf")); + C(LDAP_NOT_ALLOWED_ON_RDN, N_("Operation not allowed on RDN")); + C(LDAP_ALREADY_EXISTS, N_("Already exists")); + C(LDAP_NO_OBJECT_CLASS_MODS, N_("Cannot modify object class")); + + C(LDAP_AFFECTS_MULTIPLE_DSAS, N_("Operation affects multiple DSAs")); + + /* Virtual List View draft */ + C(LDAP_VLV_ERROR, N_("Virtual List View error")); + + C(LDAP_OTHER, N_("Other (e.g., implementation specific) error")); + + /* LDAPv2 (RFC 1777) codes */ + C(LDAP_PARTIAL_RESULTS, N_("Partial results and referral received")); + C(LDAP_IS_LEAF, N_("Entry is a leaf")); + + /* Connection-less LDAP (CLDAP - RFC 1798) code */ + C(LDAP_RESULTS_TOO_LARGE, N_("Results too large")); + + /* Cancel Operation (RFC 3909) codes */ + C(LDAP_CANCELLED, N_("Cancelled")); + C(LDAP_NO_SUCH_OPERATION, N_("No Operation to Cancel")); + C(LDAP_TOO_LATE, N_("Too Late to Cancel")); + C(LDAP_CANNOT_CANCEL, N_("Cannot Cancel")); + + /* Assert Control (RFC 4528 and old internet-draft) codes */ + C(LDAP_ASSERTION_FAILED, N_("Assertion Failed")); + C(LDAP_X_ASSERTION_FAILED, N_("Assertion Failed (X)")); + + /* Proxied Authorization Control (RFC 4370 and I-D) codes */ + C(LDAP_PROXIED_AUTHORIZATION_DENIED, N_("Proxied Authorization Denied")); + C(LDAP_X_PROXY_AUTHZ_FAILURE, N_("Proxy Authorization Failure (X)")); + + /* Content Sync Operation (RFC 4533 and I-D) codes */ + C(LDAP_SYNC_REFRESH_REQUIRED, N_("Content Sync Refresh Required")); + C(LDAP_X_SYNC_REFRESH_REQUIRED, N_("Content Sync Refresh Required (X)")); + + /* No-Op Control (draft-zeilenga-ldap-noop) code */ + C(LDAP_X_NO_OPERATION, N_("No Operation (X)")); + + /* Client Update Protocol (RFC 3928) codes */ + C(LDAP_CUP_RESOURCES_EXHAUSTED, N_("LCUP Resources Exhausted")); + C(LDAP_CUP_SECURITY_VIOLATION, N_("LCUP Security Violation")); + C(LDAP_CUP_INVALID_DATA, N_("LCUP Invalid Data")); + C(LDAP_CUP_UNSUPPORTED_SCHEME, N_("LCUP Unsupported Scheme")); + C(LDAP_CUP_RELOAD_REQUIRED, N_("LCUP Reload Required")); + +#ifdef LDAP_X_TXN + /* Codes related to LDAP Transactions (draft-zeilenga-ldap-txn) */ + C(LDAP_X_TXN_SPECIFY_OKAY, N_("TXN specify okay")); + C(LDAP_X_TXN_ID_INVALID, N_("TXN ID is invalid")); +#endif + + /* API codes - renumbered since draft-ietf-ldapext-ldap-c-api */ + C(LDAP_SERVER_DOWN, N_("Can't contact LDAP server")); + C(LDAP_LOCAL_ERROR, N_("Local error")); + C(LDAP_ENCODING_ERROR, N_("Encoding error")); + C(LDAP_DECODING_ERROR, N_("Decoding error")); + C(LDAP_TIMEOUT, N_("Timed out")); + C(LDAP_AUTH_UNKNOWN, N_("Unknown authentication method")); + C(LDAP_FILTER_ERROR, N_("Bad search filter")); + C(LDAP_USER_CANCELLED, N_("User cancelled operation")); + C(LDAP_PARAM_ERROR, N_("Bad parameter to an ldap routine")); + C(LDAP_NO_MEMORY, N_("Out of memory")); + C(LDAP_CONNECT_ERROR, N_("Connect error")); + C(LDAP_NOT_SUPPORTED, N_("Not Supported")); + C(LDAP_CONTROL_NOT_FOUND, N_("Control not found")); + C(LDAP_NO_RESULTS_RETURNED, N_("No results returned")); + C(LDAP_MORE_RESULTS_TO_RETURN, N_("More results to return")); + C(LDAP_CLIENT_LOOP, N_("Client Loop")); + C(LDAP_REFERRAL_LIMIT_EXCEEDED, N_("Referral Limit Exceeded")); + C(LDAP_X_CONNECTING, N_("Connecting (X)")); +# undef C + + default: + m = (LDAP_API_ERROR(err) ? N_("Unknown API error") + : LDAP_E_ERROR(err) ? N_("Unknown (extension) error") + : LDAP_X_ERROR(err) ? N_("Unknown (private extension) error") + : N_("Unknown error")); + break; + } + + return _(m); +} + +/* deprecated */ +void +ldap_perror( LDAP *ld, LDAP_CONST char *str ) +{ + int i; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( str != NULL ); + + fprintf( stderr, "%s: %s (%d)\n", + str ? str : "ldap_perror", + ldap_err2string( ld->ld_errno ), + ld->ld_errno ); + + if ( ld->ld_matched != NULL && ld->ld_matched[0] != '\0' ) { + fprintf( stderr, _("\tmatched DN: %s\n"), ld->ld_matched ); + } + + if ( ld->ld_error != NULL && ld->ld_error[0] != '\0' ) { + fprintf( stderr, _("\tadditional info: %s\n"), ld->ld_error ); + } + + if ( ld->ld_referrals != NULL && ld->ld_referrals[0] != NULL) { + fprintf( stderr, _("\treferrals:\n") ); + for (i=0; ld->ld_referrals[i]; i++) { + fprintf( stderr, _("\t\t%s\n"), ld->ld_referrals[i] ); + } + } + + fflush( stderr ); +} + +/* deprecated */ +int +ldap_result2error( LDAP *ld, LDAPMessage *r, int freeit ) +{ + int rc, err; + + rc = ldap_parse_result( ld, r, &err, + NULL, NULL, NULL, NULL, freeit ); + + return err != LDAP_SUCCESS ? err : rc; +} + +/* + * Parse LDAPResult Messages: + * + * LDAPResult ::= SEQUENCE { + * resultCode ENUMERATED, + * matchedDN LDAPDN, + * errorMessage LDAPString, + * referral [3] Referral OPTIONAL } + * + * including Bind results: + * + * BindResponse ::= [APPLICATION 1] SEQUENCE { + * COMPONENTS OF LDAPResult, + * serverSaslCreds [7] OCTET STRING OPTIONAL } + * + * and ExtendedOp results: + * + * ExtendedResponse ::= [APPLICATION 24] SEQUENCE { + * COMPONENTS OF LDAPResult, + * responseName [10] LDAPOID OPTIONAL, + * response [11] OCTET STRING OPTIONAL } + * + */ +int +ldap_parse_result( + LDAP *ld, + LDAPMessage *r, + int *errcodep, + char **matcheddnp, + char **errmsgp, + char ***referralsp, + LDAPControl ***serverctrls, + int freeit ) +{ + LDAPMessage *lm; + ber_int_t errcode = LDAP_SUCCESS; + + ber_tag_t tag; + BerElement *ber; + + Debug( LDAP_DEBUG_TRACE, "ldap_parse_result\n", 0, 0, 0 ); + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( r != NULL ); + + if(errcodep != NULL) *errcodep = LDAP_SUCCESS; + if(matcheddnp != NULL) *matcheddnp = NULL; + if(errmsgp != NULL) *errmsgp = NULL; + if(referralsp != NULL) *referralsp = NULL; + if(serverctrls != NULL) *serverctrls = NULL; + + LDAP_MUTEX_LOCK( &ld->ld_res_mutex ); + /* Find the result, last msg in chain... */ + lm = r->lm_chain_tail; + /* FIXME: either this is not possible (assert?) + * or it should be handled */ + if ( lm != NULL ) { + switch ( lm->lm_msgtype ) { + case LDAP_RES_SEARCH_ENTRY: + case LDAP_RES_SEARCH_REFERENCE: + case LDAP_RES_INTERMEDIATE: + lm = NULL; + break; + + default: + break; + } + } + + if( lm == NULL ) { + errcode = ld->ld_errno = LDAP_NO_RESULTS_RETURNED; + LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex ); + goto done; + } + + if ( ld->ld_error ) { + LDAP_FREE( ld->ld_error ); + ld->ld_error = NULL; + } + if ( ld->ld_matched ) { + LDAP_FREE( ld->ld_matched ); + ld->ld_matched = NULL; + } + if ( ld->ld_referrals ) { + LDAP_VFREE( ld->ld_referrals ); + ld->ld_referrals = NULL; + } + + /* parse results */ + + ber = ber_dup( lm->lm_ber ); + + if ( ld->ld_version < LDAP_VERSION2 ) { + tag = ber_scanf( ber, "{iA}", + &ld->ld_errno, &ld->ld_error ); + + } else { + ber_len_t len; + + tag = ber_scanf( ber, "{iAA" /*}*/, + &ld->ld_errno, &ld->ld_matched, &ld->ld_error ); + + if( tag != LBER_ERROR ) { + /* peek for referrals */ + if( ber_peek_tag(ber, &len) == LDAP_TAG_REFERRAL ) { + tag = ber_scanf( ber, "v", &ld->ld_referrals ); + } + } + + /* need to clean out misc items */ + if( tag != LBER_ERROR ) { + if( lm->lm_msgtype == LDAP_RES_BIND ) { + /* look for sasl result creditials */ + if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SASL_RES_CREDS ) { + /* skip 'em */ + tag = ber_scanf( ber, "x" ); + } + + } else if( lm->lm_msgtype == LDAP_RES_EXTENDED ) { + /* look for exop result oid or value */ + if ( ber_peek_tag( ber, &len ) == LDAP_TAG_EXOP_RES_OID ) { + /* skip 'em */ + tag = ber_scanf( ber, "x" ); + } + + if ( tag != LBER_ERROR && + ber_peek_tag( ber, &len ) == LDAP_TAG_EXOP_RES_VALUE ) + { + /* skip 'em */ + tag = ber_scanf( ber, "x" ); + } + } + } + + if( tag != LBER_ERROR ) { + int rc = ldap_pvt_get_controls( ber, serverctrls ); + + if( rc != LDAP_SUCCESS ) { + tag = LBER_ERROR; + } + } + + if( tag != LBER_ERROR ) { + tag = ber_scanf( ber, /*{*/"}" ); + } + } + + if ( tag == LBER_ERROR ) { + ld->ld_errno = errcode = LDAP_DECODING_ERROR; + } + + if( ber != NULL ) { + ber_free( ber, 0 ); + } + + /* return */ + if( errcodep != NULL ) { + *errcodep = ld->ld_errno; + } + if ( errcode == LDAP_SUCCESS ) { + if( matcheddnp != NULL ) { + if ( ld->ld_matched ) + { + *matcheddnp = LDAP_STRDUP( ld->ld_matched ); + } + } + if( errmsgp != NULL ) { + if ( ld->ld_error ) + { + *errmsgp = LDAP_STRDUP( ld->ld_error ); + } + } + + if( referralsp != NULL) { + *referralsp = ldap_value_dup( ld->ld_referrals ); + } + } + LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex ); + +done: + if ( freeit ) { + ldap_msgfree( r ); + } + + return errcode; +} diff --git a/libraries/libldap/extended.c b/libraries/libldap/extended.c new file mode 100644 index 0000000..0bec086 --- /dev/null +++ b/libraries/libldap/extended.c @@ -0,0 +1,419 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/stdlib.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" +#include "ldap_log.h" + +BerElement * +ldap_build_extended_req( + LDAP *ld, + LDAP_CONST char *reqoid, + struct berval *reqdata, + LDAPControl **sctrls, + LDAPControl **cctrls, + ber_int_t *msgidp ) +{ + BerElement *ber; + int rc; + + /* create a message to send */ + if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) { + return( NULL ); + } + + LDAP_NEXT_MSGID( ld, *msgidp ); + if ( reqdata != NULL ) { + rc = ber_printf( ber, "{it{tstON}", /* '}' */ + *msgidp, LDAP_REQ_EXTENDED, + LDAP_TAG_EXOP_REQ_OID, reqoid, + LDAP_TAG_EXOP_REQ_VALUE, reqdata ); + + } else { + rc = ber_printf( ber, "{it{tsN}", /* '}' */ + *msgidp, LDAP_REQ_EXTENDED, + LDAP_TAG_EXOP_REQ_OID, reqoid ); + } + + if( rc == -1 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return( NULL ); + } + + /* Put Server Controls */ + if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) { + ber_free( ber, 1 ); + return( NULL ); + } + + if ( ber_printf( ber, /*{*/ "N}" ) == -1 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return( NULL ); + } + + return( ber ); +} + +/* + * LDAPv3 Extended Operation Request + * ExtendedRequest ::= [APPLICATION 23] SEQUENCE { + * requestName [0] LDAPOID, + * requestValue [1] OCTET STRING OPTIONAL + * } + * + * LDAPv3 Extended Operation Response + * ExtendedResponse ::= [APPLICATION 24] SEQUENCE { + * COMPONENTS OF LDAPResult, + * responseName [10] LDAPOID OPTIONAL, + * response [11] OCTET STRING OPTIONAL + * } + * + * (Source RFC 4511) + */ + +int +ldap_extended_operation( + LDAP *ld, + LDAP_CONST char *reqoid, + struct berval *reqdata, + LDAPControl **sctrls, + LDAPControl **cctrls, + int *msgidp ) +{ + BerElement *ber; + ber_int_t id; + + Debug( LDAP_DEBUG_TRACE, "ldap_extended_operation\n", 0, 0, 0 ); + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( reqoid != NULL && *reqoid != '\0' ); + assert( msgidp != NULL ); + + /* must be version 3 (or greater) */ + if ( ld->ld_version < LDAP_VERSION3 ) { + ld->ld_errno = LDAP_NOT_SUPPORTED; + return( ld->ld_errno ); + } + + ber = ldap_build_extended_req( ld, reqoid, reqdata, + sctrls, cctrls, &id ); + if ( !ber ) + return( ld->ld_errno ); + + /* send the message */ + *msgidp = ldap_send_initial_request( ld, LDAP_REQ_EXTENDED, NULL, ber, id ); + + return( *msgidp < 0 ? ld->ld_errno : LDAP_SUCCESS ); +} + +int +ldap_extended_operation_s( + LDAP *ld, + LDAP_CONST char *reqoid, + struct berval *reqdata, + LDAPControl **sctrls, + LDAPControl **cctrls, + char **retoidp, + struct berval **retdatap ) +{ + int rc; + int msgid; + LDAPMessage *res; + + Debug( LDAP_DEBUG_TRACE, "ldap_extended_operation_s\n", 0, 0, 0 ); + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( reqoid != NULL && *reqoid != '\0' ); + + rc = ldap_extended_operation( ld, reqoid, reqdata, + sctrls, cctrls, &msgid ); + + if ( rc != LDAP_SUCCESS ) { + return( rc ); + } + + if ( ldap_result( ld, msgid, LDAP_MSG_ALL, (struct timeval *) NULL, &res ) == -1 || !res ) { + return( ld->ld_errno ); + } + + if ( retoidp != NULL ) *retoidp = NULL; + if ( retdatap != NULL ) *retdatap = NULL; + + rc = ldap_parse_extended_result( ld, res, retoidp, retdatap, 0 ); + + if( rc != LDAP_SUCCESS ) { + ldap_msgfree( res ); + return rc; + } + + return( ldap_result2error( ld, res, 1 ) ); +} + +/* Parse an extended result */ +int +ldap_parse_extended_result ( + LDAP *ld, + LDAPMessage *res, + char **retoidp, + struct berval **retdatap, + int freeit ) +{ + BerElement *ber; + ber_tag_t rc; + ber_tag_t tag; + ber_len_t len; + struct berval *resdata; + ber_int_t errcode; + char *resoid; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( res != NULL ); + + Debug( LDAP_DEBUG_TRACE, "ldap_parse_extended_result\n", 0, 0, 0 ); + + if( ld->ld_version < LDAP_VERSION3 ) { + ld->ld_errno = LDAP_NOT_SUPPORTED; + return ld->ld_errno; + } + + if( res->lm_msgtype != LDAP_RES_EXTENDED ) { + ld->ld_errno = LDAP_PARAM_ERROR; + return ld->ld_errno; + } + + if( retoidp != NULL ) *retoidp = NULL; + if( retdatap != NULL ) *retdatap = NULL; + + if ( ld->ld_error ) { + LDAP_FREE( ld->ld_error ); + ld->ld_error = NULL; + } + + if ( ld->ld_matched ) { + LDAP_FREE( ld->ld_matched ); + ld->ld_matched = NULL; + } + + ber = ber_dup( res->lm_ber ); + + if ( ber == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + return ld->ld_errno; + } + + rc = ber_scanf( ber, "{eAA" /*}*/, &errcode, + &ld->ld_matched, &ld->ld_error ); + + if( rc == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + ber_free( ber, 0 ); + return ld->ld_errno; + } + + resoid = NULL; + resdata = NULL; + + tag = ber_peek_tag( ber, &len ); + + if( tag == LDAP_TAG_REFERRAL ) { + /* skip over referral */ + if( ber_scanf( ber, "x" ) == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + ber_free( ber, 0 ); + return ld->ld_errno; + } + + tag = ber_peek_tag( ber, &len ); + } + + if( tag == LDAP_TAG_EXOP_RES_OID ) { + /* we have a resoid */ + if( ber_scanf( ber, "a", &resoid ) == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + ber_free( ber, 0 ); + return ld->ld_errno; + } + + assert( resoid[ 0 ] != '\0' ); + + tag = ber_peek_tag( ber, &len ); + } + + if( tag == LDAP_TAG_EXOP_RES_VALUE ) { + /* we have a resdata */ + if( ber_scanf( ber, "O", &resdata ) == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + ber_free( ber, 0 ); + if( resoid != NULL ) LDAP_FREE( resoid ); + return ld->ld_errno; + } + } + + ber_free( ber, 0 ); + + if( retoidp != NULL ) { + *retoidp = resoid; + } else { + LDAP_FREE( resoid ); + } + + if( retdatap != NULL ) { + *retdatap = resdata; + } else { + ber_bvfree( resdata ); + } + + ld->ld_errno = errcode; + + if( freeit ) { + ldap_msgfree( res ); + } + + return LDAP_SUCCESS; +} + + +/* Parse an extended partial */ +int +ldap_parse_intermediate ( + LDAP *ld, + LDAPMessage *res, + char **retoidp, + struct berval **retdatap, + LDAPControl ***serverctrls, + int freeit ) +{ + BerElement *ber; + ber_tag_t tag; + ber_len_t len; + struct berval *resdata; + char *resoid; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( res != NULL ); + + Debug( LDAP_DEBUG_TRACE, "ldap_parse_intermediate\n", 0, 0, 0 ); + + if( ld->ld_version < LDAP_VERSION3 ) { + ld->ld_errno = LDAP_NOT_SUPPORTED; + return ld->ld_errno; + } + + if( res->lm_msgtype != LDAP_RES_INTERMEDIATE ) { + ld->ld_errno = LDAP_PARAM_ERROR; + return ld->ld_errno; + } + + if( retoidp != NULL ) *retoidp = NULL; + if( retdatap != NULL ) *retdatap = NULL; + if( serverctrls != NULL ) *serverctrls = NULL; + + ber = ber_dup( res->lm_ber ); + + if ( ber == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + return ld->ld_errno; + } + + tag = ber_scanf( ber, "{" /*}*/ ); + + if( tag == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + ber_free( ber, 0 ); + return ld->ld_errno; + } + + resoid = NULL; + resdata = NULL; + + tag = ber_peek_tag( ber, &len ); + + /* + * NOTE: accept intermediate and extended response tag values + * as older versions of slapd(8) incorrectly used extended + * response tags. + * Should be removed when 2.2 is moved to Historic. + */ + if( tag == LDAP_TAG_IM_RES_OID || tag == LDAP_TAG_EXOP_RES_OID ) { + /* we have a resoid */ + if( ber_scanf( ber, "a", &resoid ) == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + ber_free( ber, 0 ); + return ld->ld_errno; + } + + assert( resoid[ 0 ] != '\0' ); + + tag = ber_peek_tag( ber, &len ); + } + + if( tag == LDAP_TAG_IM_RES_VALUE || tag == LDAP_TAG_EXOP_RES_VALUE ) { + /* we have a resdata */ + if( ber_scanf( ber, "O", &resdata ) == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + ber_free( ber, 0 ); + if( resoid != NULL ) LDAP_FREE( resoid ); + return ld->ld_errno; + } + } + + if ( serverctrls == NULL ) { + ld->ld_errno = LDAP_SUCCESS; + goto free_and_return; + } + + if ( ber_scanf( ber, /*{*/ "}" ) == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + goto free_and_return; + } + + ld->ld_errno = ldap_pvt_get_controls( ber, serverctrls ); + +free_and_return: + ber_free( ber, 0 ); + + if( retoidp != NULL ) { + *retoidp = resoid; + } else { + LDAP_FREE( resoid ); + } + + if( retdatap != NULL ) { + *retdatap = resdata; + } else { + ber_bvfree( resdata ); + } + + if( freeit ) { + ldap_msgfree( res ); + } + + return ld->ld_errno; +} + diff --git a/libraries/libldap/fetch.c b/libraries/libldap/fetch.c new file mode 100644 index 0000000..cdc69b8 --- /dev/null +++ b/libraries/libldap/fetch.c @@ -0,0 +1,146 @@ +/* fetch.c - routines for fetching data at URLs */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1999-2021 The OpenLDAP Foundation. + * Portions Copyright 1999-2003 Kurt D. Zeilenga. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* This work was initially developed by Kurt D. Zeilenga for + * inclusion in OpenLDAP Software. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/string.h> +#include <ac/socket.h> +#include <ac/time.h> + +#ifdef HAVE_FETCH +#include <fetch.h> +#endif + +#include "lber_pvt.h" +#include "ldap_pvt.h" +#include "ldap_config.h" +#include "ldif.h" + +FILE * +ldif_open_url( + LDAP_CONST char *urlstr ) +{ + FILE *url; + + if( strncasecmp( "file:", urlstr, sizeof("file:")-1 ) == 0 ) { + char *p; + urlstr += sizeof("file:")-1; + + /* we don't check for LDAP_DIRSEP since URLs should contain '/' */ + if ( urlstr[0] == '/' && urlstr[1] == '/' ) { + urlstr += 2; + /* path must be absolute if authority is present + * technically, file://hostname/path is also legal but we don't + * accept a non-empty hostname + */ + if ( urlstr[0] != '/' ) { +#ifdef _WIN32 + /* An absolute path in improper file://C:/foo/bar format */ + if ( urlstr[1] != ':' ) +#endif + return NULL; + } +#ifdef _WIN32 + /* An absolute path in proper file:///C:/foo/bar format */ + if ( urlstr[2] == ':' ) + urlstr++; +#endif + } + + p = ber_strdup( urlstr ); + + /* But we should convert to LDAP_DIRSEP before use */ + if ( LDAP_DIRSEP[0] != '/' ) { + char *s = p; + while (( s = strchr( s, '/' ))) + *s++ = LDAP_DIRSEP[0]; + } + + ldap_pvt_hex_unescape( p ); + + url = fopen( p, "rb" ); + + ber_memfree( p ); + } else { +#ifdef HAVE_FETCH + url = fetchGetURL( (char*) urlstr, "" ); +#else + url = NULL; +#endif + } + return url; +} + +int +ldif_fetch_url( + LDAP_CONST char *urlstr, + char **valuep, + ber_len_t *vlenp ) +{ + FILE *url; + char buffer[1024]; + char *p = NULL; + size_t total; + size_t bytes; + + *valuep = NULL; + *vlenp = 0; + + url = ldif_open_url( urlstr ); + + if( url == NULL ) { + return -1; + } + + total = 0; + + while( (bytes = fread( buffer, 1, sizeof(buffer), url )) != 0 ) { + char *newp = ber_memrealloc( p, total + bytes + 1 ); + if( newp == NULL ) { + ber_memfree( p ); + fclose( url ); + return -1; + } + p = newp; + AC_MEMCPY( &p[total], buffer, bytes ); + total += bytes; + } + + fclose( url ); + + if( total == 0 ) { + char *newp = ber_memrealloc( p, 1 ); + if( newp == NULL ) { + ber_memfree( p ); + return -1; + } + p = newp; + } + + p[total] = '\0'; + *valuep = p; + *vlenp = total; + + return 0; +} diff --git a/libraries/libldap/filter.c b/libraries/libldap/filter.c new file mode 100644 index 0000000..c666298 --- /dev/null +++ b/libraries/libldap/filter.c @@ -0,0 +1,1124 @@ +/* search.c */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +static int put_simple_vrFilter LDAP_P(( + BerElement *ber, + char *str )); + +static int put_vrFilter_list LDAP_P(( + BerElement *ber, + char *str )); + +static char *put_complex_filter LDAP_P(( + BerElement *ber, + char *str, + ber_tag_t tag, + int not )); + +static int put_simple_filter LDAP_P(( + BerElement *ber, + char *str )); + +static int put_substring_filter LDAP_P(( + BerElement *ber, + char *type, + char *str, + char *nextstar )); + +static int put_filter_list LDAP_P(( + BerElement *ber, + char *str, + ber_tag_t tag )); + +static int ldap_is_oid ( const char *str ) +{ + int i; + + if( LDAP_ALPHA( str[0] )) { + for( i=1; str[i]; i++ ) { + if( !LDAP_LDH( str[i] )) { + return 0; + } + } + return 1; + + } else if LDAP_DIGIT( str[0] ) { + int dot=0; + for( i=1; str[i]; i++ ) { + if( LDAP_DIGIT( str[i] )) { + dot=0; + + } else if ( str[i] == '.' ) { + if( ++dot > 1 ) return 0; + + } else { + return 0; + } + } + return !dot; + } + + return 0; +} + +static int ldap_is_desc ( const char *str ) +{ + int i; + + if( LDAP_ALPHA( str[0] )) { + for( i=1; str[i]; i++ ) { + if( str[i] == ';' ) { + str = &str[i+1]; + goto options; + } + + if( !LDAP_LDH( str[i] )) { + return 0; + } + } + return 1; + + } else if LDAP_DIGIT( str[0] ) { + int dot=0; + for( i=1; str[i]; i++ ) { + if( str[i] == ';' ) { + if( dot ) return 0; + str = &str[i+1]; + goto options; + } + + if( LDAP_DIGIT( str[i] )) { + dot=0; + + } else if ( str[i] == '.' ) { + if( ++dot > 1 ) return 0; + + } else { + return 0; + } + } + return !dot; + } + + return 0; + +options: + if( !LDAP_LDH( str[0] )) { + return 0; + } + for( i=1; str[i]; i++ ) { + if( str[i] == ';' ) { + str = &str[i+1]; + goto options; + } + if( !LDAP_LDH( str[i] )) { + return 0; + } + } + return 1; +} + +static char * +find_right_paren( char *s ) +{ + int balance, escape; + + balance = 1; + escape = 0; + while ( *s && balance ) { + if ( !escape ) { + if ( *s == '(' ) { + balance++; + } else if ( *s == ')' ) { + balance--; + } + } + + escape = ( *s == '\\' && !escape ); + + if ( balance ) s++; + } + + return *s ? s : NULL; +} + +static int hex2value( int c ) +{ + if( c >= '0' && c <= '9' ) { + return c - '0'; + } + + if( c >= 'A' && c <= 'F' ) { + return c + (10 - (int) 'A'); + } + + if( c >= 'a' && c <= 'f' ) { + return c + (10 - (int) 'a'); + } + + return -1; +} + +char * +ldap_pvt_find_wildcard( const char *s ) +{ + for( ; *s; s++ ) { + switch( *s ) { + case '*': /* found wildcard */ + return (char *) s; + + case '(': + case ')': + return NULL; + + case '\\': + if( s[1] == '\0' ) return NULL; + + if( LDAP_HEX( s[1] ) && LDAP_HEX( s[2] ) ) { + s+=2; + + } else switch( s[1] ) { + default: + return NULL; + + /* allow RFC 1960 escapes */ + case '*': + case '(': + case ')': + case '\\': + s++; + } + } + } + + return (char *) s; +} + +/* unescape filter value */ +/* support both LDAP v2 and v3 escapes */ +/* output can include nul characters! */ +ber_slen_t +ldap_pvt_filter_value_unescape( char *fval ) +{ + ber_slen_t r, v; + int v1, v2; + + for( r=v=0; fval[v] != '\0'; v++ ) { + switch( fval[v] ) { + case '(': + case ')': + case '*': + return -1; + + case '\\': + /* escape */ + v++; + + if ( fval[v] == '\0' ) { + /* escape at end of string */ + return -1; + } + + if (( v1 = hex2value( fval[v] )) >= 0 ) { + /* LDAPv3 escape */ + if (( v2 = hex2value( fval[v+1] )) < 0 ) { + /* must be two digit code */ + return -1; + } + + fval[r++] = v1 * 16 + v2; + v++; + + } else { + /* LDAPv2 escape */ + switch( fval[v] ) { + case '(': + case ')': + case '*': + case '\\': + fval[r++] = fval[v]; + break; + default: + /* illegal escape */ + return -1; + } + } + break; + + default: + fval[r++] = fval[v]; + } + } + + fval[r] = '\0'; + return r; +} + +static char * +put_complex_filter( BerElement *ber, char *str, ber_tag_t tag, int not ) +{ + char *next; + + /* + * We have (x(filter)...) with str sitting on + * the x. We have to find the paren matching + * the one before the x and put the intervening + * filters by calling put_filter_list(). + */ + + /* put explicit tag */ + if ( ber_printf( ber, "t{" /*"}"*/, tag ) == -1 ) { + return NULL; + } + + str++; + if ( (next = find_right_paren( str )) == NULL ) { + return NULL; + } + + *next = '\0'; + if ( put_filter_list( ber, str, tag ) == -1 ) { + return NULL; + } + + /* close the '(' */ + *next++ = ')'; + + /* flush explicit tagged thang */ + if ( ber_printf( ber, /*"{"*/ "N}" ) == -1 ) { + return NULL; + } + + return next; +} + +int +ldap_pvt_put_filter( BerElement *ber, const char *str_in ) +{ + int rc; + char *freeme; + char *str; + char *next; + int parens, balance, escape; + + /* + * A Filter looks like this (RFC 4511 as extended by RFC 4526): + * Filter ::= CHOICE { + * and [0] SET SIZE (0..MAX) OF filter Filter, + * or [1] SET SIZE (0..MAX) OF filter Filter, + * not [2] Filter, + * equalityMatch [3] AttributeValueAssertion, + * substrings [4] SubstringFilter, + * greaterOrEqual [5] AttributeValueAssertion, + * lessOrEqual [6] AttributeValueAssertion, + * present [7] AttributeDescription, + * approxMatch [8] AttributeValueAssertion, + * extensibleMatch [9] MatchingRuleAssertion, + * ... } + * + * SubstringFilter ::= SEQUENCE { + * type AttributeDescription, + * substrings SEQUENCE SIZE (1..MAX) OF substring CHOICE { + * initial [0] AssertionValue, -- only once + * any [1] AssertionValue, + * final [2] AssertionValue -- only once + * } + * } + * + * MatchingRuleAssertion ::= SEQUENCE { + * matchingRule [1] MatchingRuleId OPTIONAL, + * type [2] AttributeDescription OPTIONAL, + * matchValue [3] AssertionValue, + * dnAttributes [4] BOOLEAN DEFAULT FALSE } + * + * Note: tags in a CHOICE are always explicit + */ + + Debug( LDAP_DEBUG_TRACE, "put_filter: \"%s\"\n", str_in, 0, 0 ); + + freeme = LDAP_STRDUP( str_in ); + if( freeme == NULL ) return LDAP_NO_MEMORY; + str = freeme; + + parens = 0; + while ( *str ) { + switch ( *str ) { + case '(': /*')'*/ + str++; + parens++; + + /* skip spaces */ + while( LDAP_SPACE( *str ) ) str++; + + switch ( *str ) { + case '&': + Debug( LDAP_DEBUG_TRACE, "put_filter: AND\n", + 0, 0, 0 ); + + str = put_complex_filter( ber, str, + LDAP_FILTER_AND, 0 ); + if( str == NULL ) { + rc = -1; + goto done; + } + + parens--; + break; + + case '|': + Debug( LDAP_DEBUG_TRACE, "put_filter: OR\n", + 0, 0, 0 ); + + str = put_complex_filter( ber, str, + LDAP_FILTER_OR, 0 ); + if( str == NULL ) { + rc = -1; + goto done; + } + + parens--; + break; + + case '!': + Debug( LDAP_DEBUG_TRACE, "put_filter: NOT\n", + 0, 0, 0 ); + + str = put_complex_filter( ber, str, + LDAP_FILTER_NOT, 0 ); + if( str == NULL ) { + rc = -1; + goto done; + } + + parens--; + break; + + case '(': + rc = -1; + goto done; + + default: + Debug( LDAP_DEBUG_TRACE, "put_filter: simple\n", + 0, 0, 0 ); + + balance = 1; + escape = 0; + next = str; + + while ( *next && balance ) { + if ( escape == 0 ) { + if ( *next == '(' ) { + balance++; + } else if ( *next == ')' ) { + balance--; + } + } + + if ( *next == '\\' && ! escape ) { + escape = 1; + } else { + escape = 0; + } + + if ( balance ) next++; + } + + if ( balance != 0 ) { + rc = -1; + goto done; + } + + *next = '\0'; + + if ( put_simple_filter( ber, str ) == -1 ) { + rc = -1; + goto done; + } + + *next++ = /*'('*/ ')'; + + str = next; + parens--; + break; + } + break; + + case /*'('*/ ')': + Debug( LDAP_DEBUG_TRACE, "put_filter: end\n", + 0, 0, 0 ); + if ( ber_printf( ber, /*"["*/ "]" ) == -1 ) { + rc = -1; + goto done; + } + str++; + parens--; + break; + + case ' ': + str++; + break; + + default: /* assume it's a simple type=value filter */ + Debug( LDAP_DEBUG_TRACE, "put_filter: default\n", + 0, 0, 0 ); + next = strchr( str, '\0' ); + if ( put_simple_filter( ber, str ) == -1 ) { + rc = -1; + goto done; + } + str = next; + break; + } + if ( !parens ) + break; + } + + rc = ( parens || *str ) ? -1 : 0; + +done: + LDAP_FREE( freeme ); + return rc; +} + +/* + * Put a list of filters like this "(filter1)(filter2)..." + */ + +static int +put_filter_list( BerElement *ber, char *str, ber_tag_t tag ) +{ + char *next = NULL; + char save; + + Debug( LDAP_DEBUG_TRACE, "put_filter_list \"%s\"\n", + str, 0, 0 ); + + while ( *str ) { + while ( *str && LDAP_SPACE( (unsigned char) *str ) ) { + str++; + } + if ( *str == '\0' ) break; + + if ( (next = find_right_paren( str + 1 )) == NULL ) { + return -1; + } + save = *++next; + + /* now we have "(filter)" with str pointing to it */ + *next = '\0'; + if ( ldap_pvt_put_filter( ber, str ) == -1 ) return -1; + *next = save; + str = next; + + if( tag == LDAP_FILTER_NOT ) break; + } + + if( tag == LDAP_FILTER_NOT && ( next == NULL || *str )) { + return -1; + } + + return 0; +} + +static int +put_simple_filter( + BerElement *ber, + char *str ) +{ + char *s; + char *value; + ber_tag_t ftype; + int rc = -1; + + Debug( LDAP_DEBUG_TRACE, "put_simple_filter: \"%s\"\n", + str, 0, 0 ); + + str = LDAP_STRDUP( str ); + if( str == NULL ) return -1; + + if ( (s = strchr( str, '=' )) == NULL ) { + goto done; + } + + value = s + 1; + *s-- = '\0'; + + switch ( *s ) { + case '<': + ftype = LDAP_FILTER_LE; + *s = '\0'; + break; + + case '>': + ftype = LDAP_FILTER_GE; + *s = '\0'; + break; + + case '~': + ftype = LDAP_FILTER_APPROX; + *s = '\0'; + break; + + case ':': + /* RFC 4515 extensible filters are off the form: + * type [:dn] [:rule] := value + * or [:dn]:rule := value + */ + ftype = LDAP_FILTER_EXT; + *s = '\0'; + + { + char *dn = strchr( str, ':' ); + char *rule = NULL; + + if( dn != NULL ) { + *dn++ = '\0'; + rule = strchr( dn, ':' ); + + if( rule == NULL ) { + /* one colon */ + if ( strcasecmp(dn, "dn") == 0 ) { + /* must have attribute */ + if( !ldap_is_desc( str ) ) { + goto done; + } + + rule = ""; + + } else { + rule = dn; + dn = NULL; + } + + } else { + /* two colons */ + *rule++ = '\0'; + + if ( strcasecmp(dn, "dn") != 0 ) { + /* must have "dn" */ + goto done; + } + } + + } + + if ( *str == '\0' && ( !rule || *rule == '\0' ) ) { + /* must have either type or rule */ + goto done; + } + + if ( *str != '\0' && !ldap_is_desc( str ) ) { + goto done; + } + + if ( rule && *rule != '\0' && !ldap_is_oid( rule ) ) { + goto done; + } + + rc = ber_printf( ber, "t{" /*"}"*/, ftype ); + + if( rc != -1 && rule && *rule != '\0' ) { + rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_OID, rule ); + } + + if( rc != -1 && *str != '\0' ) { + rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_TYPE, str ); + } + + if( rc != -1 ) { + ber_slen_t len = ldap_pvt_filter_value_unescape( value ); + + if( len >= 0 ) { + rc = ber_printf( ber, "to", + LDAP_FILTER_EXT_VALUE, value, len ); + } else { + rc = -1; + } + } + + if( rc != -1 && dn ) { + rc = ber_printf( ber, "tb", + LDAP_FILTER_EXT_DNATTRS, (ber_int_t) 1 ); + } + + if( rc != -1 ) { + rc = ber_printf( ber, /*"{"*/ "N}" ); + } + } + goto done; + + default: + if( !ldap_is_desc( str ) ) { + goto done; + + } else { + char *nextstar = ldap_pvt_find_wildcard( value ); + + if ( nextstar == NULL ) { + goto done; + + } else if ( *nextstar == '\0' ) { + ftype = LDAP_FILTER_EQUALITY; + + } else if ( strcmp( value, "*" ) == 0 ) { + ftype = LDAP_FILTER_PRESENT; + + } else { + rc = put_substring_filter( ber, str, value, nextstar ); + goto done; + } + } break; + } + + if( !ldap_is_desc( str ) ) goto done; + + if ( ftype == LDAP_FILTER_PRESENT ) { + rc = ber_printf( ber, "ts", ftype, str ); + + } else { + ber_slen_t len = ldap_pvt_filter_value_unescape( value ); + + if( len >= 0 ) { + rc = ber_printf( ber, "t{soN}", + ftype, str, value, len ); + } + } + +done: + if( rc != -1 ) rc = 0; + LDAP_FREE( str ); + return rc; +} + +static int +put_substring_filter( BerElement *ber, char *type, char *val, char *nextstar ) +{ + int gotstar = 0; + ber_tag_t ftype = LDAP_FILTER_SUBSTRINGS; + + Debug( LDAP_DEBUG_TRACE, "put_substring_filter \"%s=%s\"\n", + type, val, 0 ); + + if ( ber_printf( ber, "t{s{" /*"}}"*/, ftype, type ) == -1 ) { + return -1; + } + + for( ; *val; val=nextstar ) { + if ( gotstar ) + nextstar = ldap_pvt_find_wildcard( val ); + + if ( nextstar == NULL ) { + return -1; + } + + if ( *nextstar == '\0' ) { + ftype = LDAP_SUBSTRING_FINAL; + } else { + *nextstar++ = '\0'; + if ( gotstar++ == 0 ) { + ftype = LDAP_SUBSTRING_INITIAL; + } else { + ftype = LDAP_SUBSTRING_ANY; + } + } + + if ( *val != '\0' || ftype == LDAP_SUBSTRING_ANY ) { + ber_slen_t len = ldap_pvt_filter_value_unescape( val ); + + if ( len <= 0 ) { + return -1; + } + + if ( ber_printf( ber, "to", ftype, val, len ) == -1 ) { + return -1; + } + } + } + + if ( ber_printf( ber, /*"{{"*/ "N}N}" ) == -1 ) { + return -1; + } + + return 0; +} + +static int +put_vrFilter( BerElement *ber, const char *str_in ) +{ + int rc; + char *freeme; + char *str; + char *next; + int parens, balance, escape; + + /* + * A ValuesReturnFilter looks like this: + * + * ValuesReturnFilter ::= SEQUENCE OF SimpleFilterItem + * SimpleFilterItem ::= CHOICE { + * equalityMatch [3] AttributeValueAssertion, + * substrings [4] SubstringFilter, + * greaterOrEqual [5] AttributeValueAssertion, + * lessOrEqual [6] AttributeValueAssertion, + * present [7] AttributeType, + * approxMatch [8] AttributeValueAssertion, + * extensibleMatch [9] SimpleMatchingAssertion -- LDAPv3 + * } + * + * SubstringFilter ::= SEQUENCE { + * type AttributeType, + * SEQUENCE OF CHOICE { + * initial [0] IA5String, + * any [1] IA5String, + * final [2] IA5String + * } + * } + * + * SimpleMatchingAssertion ::= SEQUENCE { -- LDAPv3 + * matchingRule [1] MatchingRuleId OPTIONAL, + * type [2] AttributeDescription OPTIONAL, + * matchValue [3] AssertionValue } + * + * (Source: RFC 3876) + */ + + Debug( LDAP_DEBUG_TRACE, "put_vrFilter: \"%s\"\n", str_in, 0, 0 ); + + freeme = LDAP_STRDUP( str_in ); + if( freeme == NULL ) return LDAP_NO_MEMORY; + str = freeme; + + parens = 0; + while ( *str ) { + switch ( *str ) { + case '(': /*')'*/ + str++; + parens++; + + /* skip spaces */ + while( LDAP_SPACE( *str ) ) str++; + + switch ( *str ) { + case '(': + if ( (next = find_right_paren( str )) == NULL ) { + rc = -1; + goto done; + } + + *next = '\0'; + + if ( put_vrFilter_list( ber, str ) == -1 ) { + rc = -1; + goto done; + } + + /* close the '(' */ + *next++ = ')'; + + str = next; + + parens--; + break; + + + default: + Debug( LDAP_DEBUG_TRACE, "put_vrFilter: simple\n", + 0, 0, 0 ); + + balance = 1; + escape = 0; + next = str; + + while ( *next && balance ) { + if ( escape == 0 ) { + if ( *next == '(' ) { + balance++; + } else if ( *next == ')' ) { + balance--; + } + } + + if ( *next == '\\' && ! escape ) { + escape = 1; + } else { + escape = 0; + } + + if ( balance ) next++; + } + + if ( balance != 0 ) { + rc = -1; + goto done; + } + + *next = '\0'; + + if ( put_simple_vrFilter( ber, str ) == -1 ) { + rc = -1; + goto done; + } + + *next++ = /*'('*/ ')'; + + str = next; + parens--; + break; + } + break; + + case /*'('*/ ')': + Debug( LDAP_DEBUG_TRACE, "put_vrFilter: end\n", + 0, 0, 0 ); + if ( ber_printf( ber, /*"["*/ "]" ) == -1 ) { + rc = -1; + goto done; + } + str++; + parens--; + break; + + case ' ': + str++; + break; + + default: /* assume it's a simple type=value filter */ + Debug( LDAP_DEBUG_TRACE, "put_vrFilter: default\n", + 0, 0, 0 ); + next = strchr( str, '\0' ); + if ( put_simple_vrFilter( ber, str ) == -1 ) { + rc = -1; + goto done; + } + str = next; + break; + } + } + + rc = parens ? -1 : 0; + +done: + LDAP_FREE( freeme ); + return rc; +} + +int +ldap_put_vrFilter( BerElement *ber, const char *str_in ) +{ + int rc =0; + + if ( ber_printf( ber, "{" /*"}"*/ ) == -1 ) { + return -1; + } + + rc = put_vrFilter( ber, str_in ); + + if ( ber_printf( ber, /*"{"*/ "N}" ) == -1 ) { + rc = -1; + } + + return rc; +} + +static int +put_vrFilter_list( BerElement *ber, char *str ) +{ + char *next = NULL; + char save; + + Debug( LDAP_DEBUG_TRACE, "put_vrFilter_list \"%s\"\n", + str, 0, 0 ); + + while ( *str ) { + while ( *str && LDAP_SPACE( (unsigned char) *str ) ) { + str++; + } + if ( *str == '\0' ) break; + + if ( (next = find_right_paren( str + 1 )) == NULL ) { + return -1; + } + save = *++next; + + /* now we have "(filter)" with str pointing to it */ + *next = '\0'; + if ( put_vrFilter( ber, str ) == -1 ) return -1; + *next = save; + str = next; + } + + return 0; +} + +static int +put_simple_vrFilter( + BerElement *ber, + char *str ) +{ + char *s; + char *value; + ber_tag_t ftype; + int rc = -1; + + Debug( LDAP_DEBUG_TRACE, "put_simple_vrFilter: \"%s\"\n", + str, 0, 0 ); + + str = LDAP_STRDUP( str ); + if( str == NULL ) return -1; + + if ( (s = strchr( str, '=' )) == NULL ) { + goto done; + } + + value = s + 1; + *s-- = '\0'; + + switch ( *s ) { + case '<': + ftype = LDAP_FILTER_LE; + *s = '\0'; + break; + + case '>': + ftype = LDAP_FILTER_GE; + *s = '\0'; + break; + + case '~': + ftype = LDAP_FILTER_APPROX; + *s = '\0'; + break; + + case ':': + /* According to ValuesReturnFilter control definition + * extensible filters are off the form: + * type [:rule] := value + * or :rule := value + */ + ftype = LDAP_FILTER_EXT; + *s = '\0'; + + { + char *rule = strchr( str, ':' ); + + if( rule == NULL ) { + /* must have attribute */ + if( !ldap_is_desc( str ) ) { + goto done; + } + rule = ""; + } else { + *rule++ = '\0'; + } + + if ( *str == '\0' && ( !rule || *rule == '\0' ) ) { + /* must have either type or rule */ + goto done; + } + + if ( *str != '\0' && !ldap_is_desc( str ) ) { + goto done; + } + + if ( rule && *rule != '\0' && !ldap_is_oid( rule ) ) { + goto done; + } + + rc = ber_printf( ber, "t{" /*"}"*/, ftype ); + + if( rc != -1 && rule && *rule != '\0' ) { + rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_OID, rule ); + } + + if( rc != -1 && *str != '\0' ) { + rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_TYPE, str ); + } + + if( rc != -1 ) { + ber_slen_t len = ldap_pvt_filter_value_unescape( value ); + + if( len >= 0 ) { + rc = ber_printf( ber, "to", + LDAP_FILTER_EXT_VALUE, value, len ); + } else { + rc = -1; + } + } + + if( rc != -1 ) { + rc = ber_printf( ber, /*"{"*/ "N}" ); + } + } + goto done; + + default: + if( !ldap_is_desc( str ) ) { + goto done; + + } else { + char *nextstar = ldap_pvt_find_wildcard( value ); + + if ( nextstar == NULL ) { + goto done; + + } else if ( *nextstar == '\0' ) { + ftype = LDAP_FILTER_EQUALITY; + + } else if ( strcmp( value, "*" ) == 0 ) { + ftype = LDAP_FILTER_PRESENT; + + } else { + rc = put_substring_filter( ber, str, value, nextstar ); + goto done; + } + } break; + } + + if( !ldap_is_desc( str ) ) goto done; + + if ( ftype == LDAP_FILTER_PRESENT ) { + rc = ber_printf( ber, "ts", ftype, str ); + + } else { + ber_slen_t len = ldap_pvt_filter_value_unescape( value ); + + if( len >= 0 ) { + rc = ber_printf( ber, "t{soN}", + ftype, str, value, len ); + } + } + +done: + if( rc != -1 ) rc = 0; + LDAP_FREE( str ); + return rc; +} + diff --git a/libraries/libldap/free.c b/libraries/libldap/free.c new file mode 100644 index 0000000..6595e29 --- /dev/null +++ b/libraries/libldap/free.c @@ -0,0 +1,107 @@ +/* free.c */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1994 The Regents of the University of Michigan. + * All rights reserved. + */ + +/* + * free.c - some free routines are included here to avoid having to + * link in lots of extra code when not using certain features + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/stdlib.h> + +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +/* + * C-API deallocator + */ +void +ldap_memfree( void *p ) +{ + LDAP_FREE( p ); +} + +void +ldap_memvfree( void **v ) +{ + LDAP_VFREE( v ); +} + +void * +ldap_memalloc( ber_len_t s ) +{ + return LDAP_MALLOC( s ); +} + +void * +ldap_memcalloc( ber_len_t n, ber_len_t s ) +{ + return LDAP_CALLOC( n, s ); +} + +void * +ldap_memrealloc( void* p, ber_len_t s ) +{ + return LDAP_REALLOC( p, s ); +} + +char * +ldap_strdup( LDAP_CONST char *p ) +{ + return LDAP_STRDUP( p ); +} + +/* + * free a null-terminated array of pointers to mod structures. the + * structures are freed, not the array itself, unless the freemods + * flag is set. + */ + +void +ldap_mods_free( LDAPMod **mods, int freemods ) +{ + int i; + + if ( mods == NULL ) + return; + + for ( i = 0; mods[i] != NULL; i++ ) { + if ( mods[i]->mod_op & LDAP_MOD_BVALUES ) { + if( mods[i]->mod_bvalues != NULL ) + ber_bvecfree( mods[i]->mod_bvalues ); + + } else if( mods[i]->mod_values != NULL ) { + LDAP_VFREE( mods[i]->mod_values ); + } + + if ( mods[i]->mod_type != NULL ) { + LDAP_FREE( mods[i]->mod_type ); + } + + LDAP_FREE( (char *) mods[i] ); + } + + if ( freemods ) { + LDAP_FREE( (char *) mods ); + } +} diff --git a/libraries/libldap/ftest.c b/libraries/libldap/ftest.c new file mode 100644 index 0000000..c0952b5 --- /dev/null +++ b/libraries/libldap/ftest.c @@ -0,0 +1,119 @@ +/* ftest.c -- OpenLDAP Filter API Test */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/unistd.h> + +#include <stdio.h> + +#include <ldap.h> + +#include "ldap_pvt.h" +#include "lber_pvt.h" + +#include "ldif.h" +#include "lutil.h" +#include "lutil_ldap.h" +#include "ldap_defaults.h" + +static int filter2ber( char *filter ); + +int usage() +{ + fprintf( stderr, "usage:\n" + " ftest [-d n] filter\n" + " filter - RFC 4515 string representation of an " + "LDAP search filter\n" ); + return EXIT_FAILURE; +} + +int +main( int argc, char *argv[] ) +{ + int c; + int debug=0; + + while( (c = getopt( argc, argv, "d:" )) != EOF ) { + switch ( c ) { + case 'd': + debug = atoi( optarg ); + break; + default: + fprintf( stderr, "ftest: unrecognized option -%c\n", + optopt ); + return usage(); + } + } + + if ( debug ) { + if ( ber_set_option( NULL, LBER_OPT_DEBUG_LEVEL, &debug ) + != LBER_OPT_SUCCESS ) + { + fprintf( stderr, "Could not set LBER_OPT_DEBUG_LEVEL %d\n", + debug ); + } + if ( ldap_set_option( NULL, LDAP_OPT_DEBUG_LEVEL, &debug ) + != LDAP_OPT_SUCCESS ) + { + fprintf( stderr, "Could not set LDAP_OPT_DEBUG_LEVEL %d\n", + debug ); + } + } + + if ( argc - optind != 1 ) { + return usage(); + } + + return filter2ber( strdup( argv[optind] ) ); +} + +static int filter2ber( char *filter ) +{ + int rc; + struct berval bv = BER_BVNULL; + BerElement *ber; + + printf( "Filter: %s\n", filter ); + + ber = ber_alloc_t( LBER_USE_DER ); + if( ber == NULL ) { + perror( "ber_alloc_t" ); + return EXIT_FAILURE; + } + + rc = ldap_pvt_put_filter( ber, filter ); + if( rc < 0 ) { + fprintf( stderr, "Filter error!\n"); + return EXIT_FAILURE; + } + + rc = ber_flatten2( ber, &bv, 0 ); + if( rc < 0 ) { + perror( "ber_flatten2" ); + return EXIT_FAILURE; + } + + printf( "BER encoding (len=%ld):\n", (long) bv.bv_len ); + ber_bprint( bv.bv_val, bv.bv_len ); + + ber_free( ber, 1 ); + + return EXIT_SUCCESS; +} + diff --git a/libraries/libldap/getattr.c b/libraries/libldap/getattr.c new file mode 100644 index 0000000..0ee21f7 --- /dev/null +++ b/libraries/libldap/getattr.c @@ -0,0 +1,157 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/stdlib.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +char * +ldap_first_attribute( LDAP *ld, LDAPMessage *entry, BerElement **berout ) +{ + int rc; + ber_tag_t tag; + ber_len_t len = 0; + char *attr; + BerElement *ber; + + Debug( LDAP_DEBUG_TRACE, "ldap_first_attribute\n", 0, 0, 0 ); + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( entry != NULL ); + assert( berout != NULL ); + + *berout = NULL; + + ber = ldap_alloc_ber_with_options( ld ); + if( ber == NULL ) { + return NULL; + } + + *ber = *entry->lm_ber; + + /* + * Skip past the sequence, dn, sequence of sequence leaving + * us at the first attribute. + */ + + tag = ber_scanf( ber, "{xl{" /*}}*/, &len ); + if( tag == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + ber_free( ber, 0 ); + return NULL; + } + + /* set the length to avoid overrun */ + rc = ber_set_option( ber, LBER_OPT_REMAINING_BYTES, &len ); + if( rc != LBER_OPT_SUCCESS ) { + ld->ld_errno = LDAP_LOCAL_ERROR; + ber_free( ber, 0 ); + return NULL; + } + + if ( ber_pvt_ber_remaining( ber ) == 0 ) { + assert( len == 0 ); + ber_free( ber, 0 ); + return NULL; + } + assert( len != 0 ); + + /* snatch the first attribute */ + tag = ber_scanf( ber, "{ax}", &attr ); + if( tag == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + ber_free( ber, 0 ); + return NULL; + } + + *berout = ber; + return attr; +} + +/* ARGSUSED */ +char * +ldap_next_attribute( LDAP *ld, LDAPMessage *entry, BerElement *ber ) +{ + ber_tag_t tag; + char *attr; + + Debug( LDAP_DEBUG_TRACE, "ldap_next_attribute\n", 0, 0, 0 ); + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( entry != NULL ); + assert( ber != NULL ); + + if ( ber_pvt_ber_remaining( ber ) == 0 ) { + return NULL; + } + + /* skip sequence, snarf attribute type, skip values */ + tag = ber_scanf( ber, "{ax}", &attr ); + if( tag == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + return NULL; + } + + return attr; +} + +/* Fetch attribute type and optionally fetch values. The type + * and values are referenced in-place from the BerElement, they are + * not dup'd into malloc'd memory. + */ +/* ARGSUSED */ +int +ldap_get_attribute_ber( LDAP *ld, LDAPMessage *entry, BerElement *ber, + BerValue *attr, BerVarray *vals ) +{ + ber_tag_t tag; + int rc = LDAP_SUCCESS; + + Debug( LDAP_DEBUG_TRACE, "ldap_get_attribute_ber\n", 0, 0, 0 ); + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( entry != NULL ); + assert( ber != NULL ); + assert( attr != NULL ); + + attr->bv_val = NULL; + attr->bv_len = 0; + + if ( ber_pvt_ber_remaining( ber ) ) { + ber_len_t siz = sizeof( BerValue ); + + /* skip sequence, snarf attribute type */ + tag = ber_scanf( ber, vals ? "{mM}" : "{mx}", attr, vals, + &siz, (ber_len_t)0 ); + if( tag == LBER_ERROR ) { + rc = ld->ld_errno = LDAP_DECODING_ERROR; + } + } + + return rc; +} diff --git a/libraries/libldap/getdn.c b/libraries/libldap/getdn.c new file mode 100644 index 0000000..5d43e69 --- /dev/null +++ b/libraries/libldap/getdn.c @@ -0,0 +1,3305 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1994 Regents of the University of Michigan. + * All rights reserved. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" +#include "ldap_schema.h" +#include "ldif.h" + +/* extension to UFN that turns trailing "dc=value" rdns in DNS style, + * e.g. "ou=People,dc=openldap,dc=org" => "People, openldap.org" */ +#define DC_IN_UFN + +/* parsing/printing routines */ +static int str2strval( const char *str, ber_len_t stoplen, struct berval *val, + const char **next, unsigned flags, int *retFlags, void *ctx ); +static int DCE2strval( const char *str, struct berval *val, + const char **next, unsigned flags, void *ctx ); +static int IA52strval( const char *str, struct berval *val, + const char **next, unsigned flags, void *ctx ); +static int quotedIA52strval( const char *str, struct berval *val, + const char **next, unsigned flags, void *ctx ); +static int hexstr2binval( const char *str, struct berval *val, + const char **next, unsigned flags, void *ctx ); +static int hexstr2bin( const char *str, char *c ); +static int byte2hexpair( const char *val, char *pair ); +static int binval2hexstr( struct berval *val, char *str ); +static int strval2strlen( struct berval *val, unsigned flags, + ber_len_t *len ); +static int strval2str( struct berval *val, char *str, unsigned flags, + ber_len_t *len ); +static int strval2IA5strlen( struct berval *val, unsigned flags, + ber_len_t *len ); +static int strval2IA5str( struct berval *val, char *str, unsigned flags, + ber_len_t *len ); +static int strval2DCEstrlen( struct berval *val, unsigned flags, + ber_len_t *len ); +static int strval2DCEstr( struct berval *val, char *str, unsigned flags, + ber_len_t *len ); +static int strval2ADstrlen( struct berval *val, unsigned flags, + ber_len_t *len ); +static int strval2ADstr( struct berval *val, char *str, unsigned flags, + ber_len_t *len ); +static int dn2domain( LDAPDN dn, struct berval *bv, int pos, int *iRDN ); + +/* AVA helpers */ +static LDAPAVA * ldapava_new( + const struct berval *attr, const struct berval *val, unsigned flags, void *ctx ); + +/* Higher level helpers */ +static int rdn2strlen( LDAPRDN rdn, unsigned flags, ber_len_t *len, + int ( *s2l )( struct berval *, unsigned, ber_len_t * ) ); +static int rdn2str( LDAPRDN rdn, char *str, unsigned flags, ber_len_t *len, + int ( *s2s )( struct berval *, char *, unsigned, ber_len_t * )); +static int rdn2UFNstrlen( LDAPRDN rdn, unsigned flags, ber_len_t *len ); +static int rdn2UFNstr( LDAPRDN rdn, char *str, unsigned flags, ber_len_t *len ); +static int rdn2DCEstrlen( LDAPRDN rdn, unsigned flags, ber_len_t *len ); +static int rdn2DCEstr( LDAPRDN rdn, char *str, unsigned flag, ber_len_t *len, int first ); +static int rdn2ADstrlen( LDAPRDN rdn, unsigned flags, ber_len_t *len ); +static int rdn2ADstr( LDAPRDN rdn, char *str, unsigned flags, ber_len_t *len, int first ); + +/* + * RFC 1823 ldap_get_dn + */ +char * +ldap_get_dn( LDAP *ld, LDAPMessage *entry ) +{ + char *dn; + BerElement tmp; + + Debug( LDAP_DEBUG_TRACE, "ldap_get_dn\n", 0, 0, 0 ); + + assert( ld != NULL ); + assert( LDAP_VALID(ld) ); + assert( entry != NULL ); + + tmp = *entry->lm_ber; /* struct copy */ + if ( ber_scanf( &tmp, "{a" /*}*/, &dn ) == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + return( NULL ); + } + + return( dn ); +} + +int +ldap_get_dn_ber( LDAP *ld, LDAPMessage *entry, BerElement **berout, + BerValue *dn ) +{ + BerElement tmp, *ber; + ber_len_t len = 0; + int rc = LDAP_SUCCESS; + + Debug( LDAP_DEBUG_TRACE, "ldap_get_dn_ber\n", 0, 0, 0 ); + + assert( ld != NULL ); + assert( LDAP_VALID(ld) ); + assert( entry != NULL ); + assert( dn != NULL ); + + dn->bv_val = NULL; + dn->bv_len = 0; + + if ( berout ) { + *berout = NULL; + ber = ldap_alloc_ber_with_options( ld ); + if( ber == NULL ) { + return LDAP_NO_MEMORY; + } + *berout = ber; + } else { + ber = &tmp; + } + + *ber = *entry->lm_ber; /* struct copy */ + if ( ber_scanf( ber, "{ml{" /*}*/, dn, &len ) == LBER_ERROR ) { + rc = ld->ld_errno = LDAP_DECODING_ERROR; + } + if ( rc == LDAP_SUCCESS ) { + /* set the length to avoid overrun */ + rc = ber_set_option( ber, LBER_OPT_REMAINING_BYTES, &len ); + if( rc != LBER_OPT_SUCCESS ) { + rc = ld->ld_errno = LDAP_LOCAL_ERROR; + } + } + if ( rc != LDAP_SUCCESS && berout ) { + ber_free( ber, 0 ); + *berout = NULL; + } + return rc; +} + +/* + * RFC 1823 ldap_dn2ufn + */ +char * +ldap_dn2ufn( LDAP_CONST char *dn ) +{ + char *out = NULL; + + Debug( LDAP_DEBUG_TRACE, "ldap_dn2ufn\n", 0, 0, 0 ); + + ( void )ldap_dn_normalize( dn, LDAP_DN_FORMAT_LDAP, + &out, LDAP_DN_FORMAT_UFN ); + + return( out ); +} + +/* + * RFC 1823 ldap_explode_dn + */ +char ** +ldap_explode_dn( LDAP_CONST char *dn, int notypes ) +{ + LDAPDN tmpDN; + char **values = NULL; + int iRDN; + unsigned flag = notypes ? LDAP_DN_FORMAT_UFN : LDAP_DN_FORMAT_LDAPV3; + + Debug( LDAP_DEBUG_TRACE, "ldap_explode_dn\n", 0, 0, 0 ); + + if ( ldap_str2dn( dn, &tmpDN, LDAP_DN_FORMAT_LDAP ) + != LDAP_SUCCESS ) { + return NULL; + } + + if( tmpDN == NULL ) { + values = LDAP_MALLOC( sizeof( char * ) ); + if( values == NULL ) return NULL; + + values[0] = NULL; + return values; + } + + for ( iRDN = 0; tmpDN[ iRDN ]; iRDN++ ); + + values = LDAP_MALLOC( sizeof( char * ) * ( 1 + iRDN ) ); + if ( values == NULL ) { + ldap_dnfree( tmpDN ); + return NULL; + } + + for ( iRDN = 0; tmpDN[ iRDN ]; iRDN++ ) { + ldap_rdn2str( tmpDN[ iRDN ], &values[ iRDN ], flag ); + } + ldap_dnfree( tmpDN ); + values[ iRDN ] = NULL; + + return values; +} + +char ** +ldap_explode_rdn( LDAP_CONST char *rdn, int notypes ) +{ + LDAPRDN tmpRDN; + char **values = NULL; + const char *p; + int iAVA; + + Debug( LDAP_DEBUG_TRACE, "ldap_explode_rdn\n", 0, 0, 0 ); + + /* + * we only parse the first rdn + * FIXME: we prefer efficiency over checking if the _ENTIRE_ + * dn can be parsed + */ + if ( ldap_str2rdn( rdn, &tmpRDN, (char **) &p, LDAP_DN_FORMAT_LDAP ) + != LDAP_SUCCESS ) { + return( NULL ); + } + + for ( iAVA = 0; tmpRDN[ iAVA ]; iAVA++ ) ; + values = LDAP_MALLOC( sizeof( char * ) * ( 1 + iAVA ) ); + if ( values == NULL ) { + ldap_rdnfree( tmpRDN ); + return( NULL ); + } + + for ( iAVA = 0; tmpRDN[ iAVA ]; iAVA++ ) { + ber_len_t l = 0, vl, al = 0; + char *str; + LDAPAVA *ava = tmpRDN[ iAVA ]; + + if ( ava->la_flags & LDAP_AVA_BINARY ) { + vl = 1 + 2 * ava->la_value.bv_len; + + } else { + if ( strval2strlen( &ava->la_value, + ava->la_flags, &vl ) ) { + goto error_return; + } + } + + if ( !notypes ) { + al = ava->la_attr.bv_len; + l = vl + ava->la_attr.bv_len + 1; + + str = LDAP_MALLOC( l + 1 ); + AC_MEMCPY( str, ava->la_attr.bv_val, + ava->la_attr.bv_len ); + str[ al++ ] = '='; + + } else { + l = vl; + str = LDAP_MALLOC( l + 1 ); + } + + if ( ava->la_flags & LDAP_AVA_BINARY ) { + str[ al++ ] = '#'; + if ( binval2hexstr( &ava->la_value, &str[ al ] ) ) { + goto error_return; + } + + } else { + if ( strval2str( &ava->la_value, &str[ al ], + ava->la_flags, &vl ) ) { + goto error_return; + } + } + + str[ l ] = '\0'; + values[ iAVA ] = str; + } + values[ iAVA ] = NULL; + + ldap_rdnfree( tmpRDN ); + + return( values ); + +error_return:; + LBER_VFREE( values ); + ldap_rdnfree( tmpRDN ); + return( NULL ); +} + +char * +ldap_dn2dcedn( LDAP_CONST char *dn ) +{ + char *out = NULL; + + Debug( LDAP_DEBUG_TRACE, "ldap_dn2dcedn\n", 0, 0, 0 ); + + ( void )ldap_dn_normalize( dn, LDAP_DN_FORMAT_LDAP, + &out, LDAP_DN_FORMAT_DCE ); + + return( out ); +} + +char * +ldap_dcedn2dn( LDAP_CONST char *dce ) +{ + char *out = NULL; + + Debug( LDAP_DEBUG_TRACE, "ldap_dcedn2dn\n", 0, 0, 0 ); + + ( void )ldap_dn_normalize( dce, LDAP_DN_FORMAT_DCE, &out, LDAP_DN_FORMAT_LDAPV3 ); + + return( out ); +} + +char * +ldap_dn2ad_canonical( LDAP_CONST char *dn ) +{ + char *out = NULL; + + Debug( LDAP_DEBUG_TRACE, "ldap_dn2ad_canonical\n", 0, 0, 0 ); + + ( void )ldap_dn_normalize( dn, LDAP_DN_FORMAT_LDAP, + &out, LDAP_DN_FORMAT_AD_CANONICAL ); + + return( out ); +} + +/* + * function that changes the string representation of dnin + * from ( fin & LDAP_DN_FORMAT_MASK ) to ( fout & LDAP_DN_FORMAT_MASK ) + * + * fin can be one of: + * LDAP_DN_FORMAT_LDAP (RFC 4514 liberal, plus some RFC 1779) + * LDAP_DN_FORMAT_LDAPV3 (RFC 4514) + * LDAP_DN_FORMAT_LDAPV2 (RFC 1779) + * LDAP_DN_FORMAT_DCE (?) + * + * fout can be any of the above except + * LDAP_DN_FORMAT_LDAP + * plus: + * LDAP_DN_FORMAT_UFN (RFC 1781, partial and with extensions) + * LDAP_DN_FORMAT_AD_CANONICAL (?) + */ +int +ldap_dn_normalize( LDAP_CONST char *dnin, + unsigned fin, char **dnout, unsigned fout ) +{ + int rc; + LDAPDN tmpDN = NULL; + + Debug( LDAP_DEBUG_TRACE, "ldap_dn_normalize\n", 0, 0, 0 ); + + assert( dnout != NULL ); + + *dnout = NULL; + + if ( dnin == NULL ) { + return( LDAP_SUCCESS ); + } + + rc = ldap_str2dn( dnin , &tmpDN, fin ); + if ( rc != LDAP_SUCCESS ) { + return( rc ); + } + + rc = ldap_dn2str( tmpDN, dnout, fout ); + + ldap_dnfree( tmpDN ); + + return( rc ); +} + +/* States */ +#define B4AVA 0x0000 + +/* #define B4ATTRTYPE 0x0001 */ +#define B4OIDATTRTYPE 0x0002 +#define B4STRINGATTRTYPE 0x0003 + +#define B4AVAEQUALS 0x0100 +#define B4AVASEP 0x0200 +#define B4RDNSEP 0x0300 +#define GOTAVA 0x0400 + +#define B4ATTRVALUE 0x0010 +#define B4STRINGVALUE 0x0020 +#define B4IA5VALUEQUOTED 0x0030 +#define B4IA5VALUE 0x0040 +#define B4BINARYVALUE 0x0050 + +/* + * Helpers (mostly from slap.h) + * c is assumed to Unicode in an ASCII compatible format (UTF-8) + * Macros assume "C" Locale (ASCII) + */ +#define LDAP_DN_ASCII_SPACE(c) \ + ( (c) == ' ' || (c) == '\t' || (c) == '\n' || (c) == '\r' ) +#define LDAP_DN_ASCII_LOWER(c) LDAP_LOWER(c) +#define LDAP_DN_ASCII_UPPER(c) LDAP_UPPER(c) +#define LDAP_DN_ASCII_ALPHA(c) LDAP_ALPHA(c) + +#define LDAP_DN_ASCII_DIGIT(c) LDAP_DIGIT(c) +#define LDAP_DN_ASCII_LCASE_HEXALPHA(c) LDAP_HEXLOWER(c) +#define LDAP_DN_ASCII_UCASE_HEXALPHA(c) LDAP_HEXUPPER(c) +#define LDAP_DN_ASCII_HEXDIGIT(c) LDAP_HEX(c) +#define LDAP_DN_ASCII_ALNUM(c) LDAP_ALNUM(c) +#define LDAP_DN_ASCII_PRINTABLE(c) ( (c) >= ' ' && (c) <= '~' ) + +/* attribute type */ +#define LDAP_DN_OID_LEADCHAR(c) LDAP_DIGIT(c) +#define LDAP_DN_DESC_LEADCHAR(c) LDAP_ALPHA(c) +#define LDAP_DN_DESC_CHAR(c) LDAP_LDH(c) +#define LDAP_DN_LANG_SEP(c) ( (c) == ';' ) +#define LDAP_DN_ATTRDESC_CHAR(c) \ + ( LDAP_DN_DESC_CHAR(c) || LDAP_DN_LANG_SEP(c) ) + +/* special symbols */ +#define LDAP_DN_AVA_EQUALS(c) ( (c) == '=' ) +#define LDAP_DN_AVA_SEP(c) ( (c) == '+' ) +#define LDAP_DN_RDN_SEP(c) ( (c) == ',' ) +#define LDAP_DN_RDN_SEP_V2(c) ( LDAP_DN_RDN_SEP(c) || (c) == ';' ) +#define LDAP_DN_OCTOTHORPE(c) ( (c) == '#' ) +#define LDAP_DN_QUOTES(c) ( (c) == '\"' ) +#define LDAP_DN_ESCAPE(c) ( (c) == '\\' ) +#define LDAP_DN_VALUE_END(c) \ + ( LDAP_DN_RDN_SEP(c) || LDAP_DN_AVA_SEP(c) ) + +/* NOTE: according to RFC 4514, '=' can be escaped and treated as special, + * i.e. escaped both as "\<hexpair>" and * as "\=", but it is treated as + * a regular char, i.e. it can also appear as '='. + * + * As such, in 2.2 we used to allow reading unescaped '=', but we always + * produced escaped '\3D'; this changes since 2.3, if compatibility issues + * do not arise + */ +#define LDAP_DN_NE(c) \ + ( LDAP_DN_RDN_SEP_V2(c) || LDAP_DN_AVA_SEP(c) \ + || LDAP_DN_QUOTES(c) \ + || (c) == '<' || (c) == '>' ) +#define LDAP_DN_MAYESCAPE(c) \ + ( LDAP_DN_ESCAPE(c) || LDAP_DN_NE(c) \ + || LDAP_DN_AVA_EQUALS(c) \ + || LDAP_DN_ASCII_SPACE(c) || LDAP_DN_OCTOTHORPE(c) ) +#define LDAP_DN_SHOULDESCAPE(c) ( LDAP_DN_AVA_EQUALS(c) ) + +#define LDAP_DN_NEEDESCAPE(c) \ + ( LDAP_DN_ESCAPE(c) || LDAP_DN_NE(c) ) +#define LDAP_DN_NEEDESCAPE_LEAD(c) LDAP_DN_MAYESCAPE(c) +#define LDAP_DN_NEEDESCAPE_TRAIL(c) \ + ( LDAP_DN_ASCII_SPACE(c) || LDAP_DN_NEEDESCAPE(c) ) +#define LDAP_DN_WILLESCAPE_CHAR(c) \ + ( LDAP_DN_RDN_SEP(c) || LDAP_DN_AVA_SEP(c) || LDAP_DN_ESCAPE(c) ) +#define LDAP_DN_IS_PRETTY(f) ( (f) & LDAP_DN_PRETTY ) +#define LDAP_DN_WILLESCAPE_HEX(f, c) \ + ( ( !LDAP_DN_IS_PRETTY( f ) ) && LDAP_DN_WILLESCAPE_CHAR(c) ) + +/* LDAPv2 */ +#define LDAP_DN_VALUE_END_V2(c) \ + ( LDAP_DN_RDN_SEP_V2(c) || LDAP_DN_AVA_SEP(c) ) +/* RFC 1779 */ +#define LDAP_DN_V2_SPECIAL(c) \ + ( LDAP_DN_RDN_SEP_V2(c) || LDAP_DN_AVA_EQUALS(c) \ + || LDAP_DN_AVA_SEP(c) || (c) == '<' || (c) == '>' \ + || LDAP_DN_OCTOTHORPE(c) ) +#define LDAP_DN_V2_PAIR(c) \ + ( LDAP_DN_V2_SPECIAL(c) || LDAP_DN_ESCAPE(c) || LDAP_DN_QUOTES(c) ) + +/* + * DCE (mostly from Luke Howard and IBM implementation for AIX) + * + * From: "Application Development Guide - Directory Services" (FIXME: add link?) + * Here escapes and valid chars for GDS are considered; as soon as more + * specific info is found, the macros will be updated. + * + * Chars: 'a'-'z', 'A'-'Z', '0'-'9', + * '.', ':', ',', ''', '+', '-', '=', '(', ')', '?', '/', ' '. + * + * Metachars: '/', ',', '=', '\'. + * + * the '\' is used to escape other metachars. + * + * Assertion: '=' + * RDN separator: '/' + * AVA separator: ',' + * + * Attribute types must start with alphabetic chars and can contain + * alphabetic chars and digits (FIXME: no '-'?). OIDs are allowed. + */ +#define LDAP_DN_RDN_SEP_DCE(c) ( (c) == '/' ) +#define LDAP_DN_AVA_SEP_DCE(c) ( (c) == ',' ) +#define LDAP_DN_ESCAPE_DCE(c) ( LDAP_DN_ESCAPE(c) ) +#define LDAP_DN_VALUE_END_DCE(c) \ + ( LDAP_DN_RDN_SEP_DCE(c) || LDAP_DN_AVA_SEP_DCE(c) ) +#define LDAP_DN_NEEDESCAPE_DCE(c) \ + ( LDAP_DN_VALUE_END_DCE(c) || LDAP_DN_AVA_EQUALS(c) ) + +/* AD Canonical */ +#define LDAP_DN_RDN_SEP_AD(c) ( (c) == '/' ) +#define LDAP_DN_ESCAPE_AD(c) ( LDAP_DN_ESCAPE(c) ) +#define LDAP_DN_AVA_SEP_AD(c) ( (c) == ',' ) /* assume same as DCE */ +#define LDAP_DN_VALUE_END_AD(c) \ + ( LDAP_DN_RDN_SEP_AD(c) || LDAP_DN_AVA_SEP_AD(c) ) +#define LDAP_DN_NEEDESCAPE_AD(c) \ + ( LDAP_DN_VALUE_END_AD(c) || LDAP_DN_AVA_EQUALS(c) ) + +/* generics */ +#define LDAP_DN_HEXPAIR(s) \ + ( LDAP_DN_ASCII_HEXDIGIT((s)[0]) && LDAP_DN_ASCII_HEXDIGIT((s)[1]) ) +/* better look at the AttributeDescription? */ + +/* FIXME: no composite rdn or non-"dc" types, right? + * (what about "dc" in OID form?) */ +/* FIXME: we do not allow binary values in domain, right? */ +/* NOTE: use this macro only when ABSOLUTELY SURE rdn IS VALID! */ +/* NOTE: don't use strcasecmp() as it is locale specific! */ +#define LDAP_DC_ATTR "dc" +#define LDAP_DC_ATTRU "DC" +#define LDAP_DN_IS_RDN_DC( r ) \ + ( (r) && (r)[0] && !(r)[1] \ + && ((r)[0]->la_flags & LDAP_AVA_STRING) \ + && ((r)[0]->la_attr.bv_len == 2) \ + && (((r)[0]->la_attr.bv_val[0] == LDAP_DC_ATTR[0]) \ + || ((r)[0]->la_attr.bv_val[0] == LDAP_DC_ATTRU[0])) \ + && (((r)[0]->la_attr.bv_val[1] == LDAP_DC_ATTR[1]) \ + || ((r)[0]->la_attr.bv_val[1] == LDAP_DC_ATTRU[1]))) + +/* Composite rules */ +#define LDAP_DN_ALLOW_ONE_SPACE(f) \ + ( LDAP_DN_LDAPV2(f) \ + || !( (f) & LDAP_DN_P_NOSPACEAFTERRDN ) ) +#define LDAP_DN_ALLOW_SPACES(f) \ + ( LDAP_DN_LDAPV2(f) \ + || !( (f) & ( LDAP_DN_P_NOLEADTRAILSPACES | LDAP_DN_P_NOSPACEAFTERRDN ) ) ) +#define LDAP_DN_LDAP(f) \ + ( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_LDAP ) +#define LDAP_DN_LDAPV3(f) \ + ( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_LDAPV3 ) +#define LDAP_DN_LDAPV2(f) \ + ( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_LDAPV2 ) +#define LDAP_DN_DCE(f) \ + ( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_DCE ) +#define LDAP_DN_UFN(f) \ + ( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_UFN ) +#define LDAP_DN_ADC(f) \ + ( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_AD_CANONICAL ) +#define LDAP_DN_FORMAT(f) ( (f) & LDAP_DN_FORMAT_MASK ) + +/* + * LDAPAVA helpers (will become part of the API for operations + * on structural representations of DNs). + */ +static LDAPAVA * +ldapava_new( const struct berval *attr, const struct berval *val, + unsigned flags, void *ctx ) +{ + LDAPAVA *ava; + + assert( attr != NULL ); + assert( val != NULL ); + + ava = LDAP_MALLOCX( sizeof( LDAPAVA ) + attr->bv_len + 1, ctx ); + + if ( ava ) { + ava->la_attr.bv_len = attr->bv_len; + ava->la_attr.bv_val = (char *)(ava+1); + AC_MEMCPY( ava->la_attr.bv_val, attr->bv_val, attr->bv_len ); + ava->la_attr.bv_val[attr->bv_len] = '\0'; + + ava->la_value = *val; + ava->la_flags = flags | LDAP_AVA_FREE_VALUE; + + ava->la_private = NULL; + } + + return( ava ); +} + +static void +ldapava_free( LDAPAVA *ava, void *ctx ) +{ + assert( ava != NULL ); + +#if 0 + /* ava's private must be freed by caller + * (at present let's skip this check because la_private + * basically holds static data) */ + assert( ava->la_private == NULL ); +#endif + + if (ava->la_flags & LDAP_AVA_FREE_VALUE) + LDAP_FREEX( ava->la_value.bv_val, ctx ); + + LDAP_FREEX( ava, ctx ); +} + +void +ldap_rdnfree( LDAPRDN rdn ) +{ + ldap_rdnfree_x( rdn, NULL ); +} + +void +ldap_rdnfree_x( LDAPRDN rdn, void *ctx ) +{ + int iAVA; + + if ( rdn == NULL ) { + return; + } + + for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) { + ldapava_free( rdn[ iAVA ], ctx ); + } + + LDAP_FREEX( rdn, ctx ); +} + +void +ldap_dnfree( LDAPDN dn ) +{ + ldap_dnfree_x( dn, NULL ); +} + +void +ldap_dnfree_x( LDAPDN dn, void *ctx ) +{ + int iRDN; + + if ( dn == NULL ) { + return; + } + + for ( iRDN = 0; dn[ iRDN ]; iRDN++ ) { + ldap_rdnfree_x( dn[ iRDN ], ctx ); + } + + LDAP_FREEX( dn, ctx ); +} + +/* + * Converts a string representation of a DN (in LDAPv3, LDAPv2 or DCE) + * into a structural representation of the DN, by separating attribute + * types and values encoded in the more appropriate form, which is + * string or OID for attribute types and binary form of the BER encoded + * value or Unicode string. Formats different from LDAPv3 are parsed + * according to their own rules and turned into the more appropriate + * form according to LDAPv3. + * + * NOTE: I realize the code is getting spaghettish; it is rather + * experimental and will hopefully turn into something more simple + * and readable as soon as it works as expected. + */ + +/* + * Default sizes of AVA and RDN static working arrays; if required + * the are dynamically resized. The values can be tuned in case + * of special requirements (e.g. very deep DN trees or high number + * of AVAs per RDN). + */ +#define TMP_AVA_SLOTS 8 +#define TMP_RDN_SLOTS 32 + +int +ldap_str2dn( LDAP_CONST char *str, LDAPDN *dn, unsigned flags ) +{ + struct berval bv; + + assert( str != NULL ); + + bv.bv_len = strlen( str ); + bv.bv_val = (char *) str; + + return ldap_bv2dn_x( &bv, dn, flags, NULL ); +} + +int +ldap_bv2dn( struct berval *bv, LDAPDN *dn, unsigned flags ) +{ + return ldap_bv2dn_x( bv, dn, flags, NULL ); +} + +int +ldap_bv2dn_x( struct berval *bvin, LDAPDN *dn, unsigned flags, void *ctx ) +{ + const char *p; + int rc = LDAP_DECODING_ERROR; + int nrdns = 0; + + LDAPDN newDN = NULL; + LDAPRDN newRDN = NULL, tmpDN_[TMP_RDN_SLOTS], *tmpDN = tmpDN_; + int num_slots = TMP_RDN_SLOTS; + char *str, *end; + struct berval bvtmp, *bv = &bvtmp; + + assert( bvin != NULL ); + assert( bvin->bv_val != NULL ); + assert( dn != NULL ); + + *bv = *bvin; + str = bv->bv_val; + end = str + bv->bv_len; + + Debug( LDAP_DEBUG_ARGS, "=> ldap_bv2dn(%s,%u)\n", str, flags, 0 ); + + *dn = NULL; + + switch ( LDAP_DN_FORMAT( flags ) ) { + case LDAP_DN_FORMAT_LDAP: + case LDAP_DN_FORMAT_LDAPV3: + case LDAP_DN_FORMAT_DCE: + break; + + /* allow DN enclosed in brackets */ + case LDAP_DN_FORMAT_LDAPV2: + if ( str[0] == '<' ) { + if ( bv->bv_len < 2 || end[ -1 ] != '>' ) { + rc = LDAP_DECODING_ERROR; + goto parsing_error; + } + bv->bv_val++; + bv->bv_len -= 2; + str++; + end--; + } + break; + + /* unsupported in str2dn */ + case LDAP_DN_FORMAT_UFN: + case LDAP_DN_FORMAT_AD_CANONICAL: + return LDAP_PARAM_ERROR; + + case LDAP_DN_FORMAT_LBER: + default: + return LDAP_PARAM_ERROR; + } + + if ( bv->bv_len == 0 ) { + return LDAP_SUCCESS; + } + + if( memchr( bv->bv_val, '\0', bv->bv_len ) != NULL ) { + /* value must have embedded NULs */ + return LDAP_DECODING_ERROR; + } + + p = str; + if ( LDAP_DN_DCE( flags ) ) { + + /* + * (from Luke Howard: thnx) A RDN separator is required + * at the beginning of an (absolute) DN. + */ + if ( !LDAP_DN_RDN_SEP_DCE( p[ 0 ] ) ) { + goto parsing_error; + } + p++; + + /* + * actually we do not want to accept by default the DCE form, + * we do not want to auto-detect it + */ +#if 0 + } else if ( LDAP_DN_LDAP( flags ) ) { + /* + * if dn starts with '/' let's make it a DCE dn + */ + if ( LDAP_DN_RDN_SEP_DCE( p[ 0 ] ) ) { + flags |= LDAP_DN_FORMAT_DCE; + p++; + } +#endif + } + + for ( ; p < end; p++ ) { + int err; + struct berval tmpbv; + tmpbv.bv_len = bv->bv_len - ( p - str ); + tmpbv.bv_val = (char *)p; + + err = ldap_bv2rdn_x( &tmpbv, &newRDN, (char **) &p, flags,ctx); + if ( err != LDAP_SUCCESS ) { + goto parsing_error; + } + + /* + * We expect a rdn separator + */ + if ( p < end && p[ 0 ] ) { + switch ( LDAP_DN_FORMAT( flags ) ) { + case LDAP_DN_FORMAT_LDAPV3: + if ( !LDAP_DN_RDN_SEP( p[ 0 ] ) ) { + rc = LDAP_DECODING_ERROR; + goto parsing_error; + } + break; + + case LDAP_DN_FORMAT_LDAP: + case LDAP_DN_FORMAT_LDAPV2: + if ( !LDAP_DN_RDN_SEP_V2( p[ 0 ] ) ) { + rc = LDAP_DECODING_ERROR; + goto parsing_error; + } + break; + + case LDAP_DN_FORMAT_DCE: + if ( !LDAP_DN_RDN_SEP_DCE( p[ 0 ] ) ) { + rc = LDAP_DECODING_ERROR; + goto parsing_error; + } + break; + } + } + + + tmpDN[nrdns++] = newRDN; + newRDN = NULL; + + /* + * make the static RDN array dynamically rescalable + */ + if ( nrdns == num_slots ) { + LDAPRDN *tmp; + + if ( tmpDN == tmpDN_ ) { + tmp = LDAP_MALLOCX( num_slots * 2 * sizeof( LDAPRDN * ), ctx ); + if ( tmp == NULL ) { + rc = LDAP_NO_MEMORY; + goto parsing_error; + } + AC_MEMCPY( tmp, tmpDN, num_slots * sizeof( LDAPRDN * ) ); + + } else { + tmp = LDAP_REALLOCX( tmpDN, num_slots * 2 * sizeof( LDAPRDN * ), ctx ); + if ( tmp == NULL ) { + rc = LDAP_NO_MEMORY; + goto parsing_error; + } + } + + tmpDN = tmp; + num_slots *= 2; + } + + if ( p >= end || p[ 0 ] == '\0' ) { + /* + * the DN is over, phew + */ + newDN = (LDAPDN)LDAP_MALLOCX( sizeof(LDAPRDN *) * (nrdns+1), ctx ); + if ( newDN == NULL ) { + rc = LDAP_NO_MEMORY; + goto parsing_error; + } else { + int i; + + if ( LDAP_DN_DCE( flags ) ) { + /* add in reversed order */ + for ( i=0; i<nrdns; i++ ) + newDN[i] = tmpDN[nrdns-1-i]; + } else { + for ( i=0; i<nrdns; i++ ) + newDN[i] = tmpDN[i]; + } + newDN[nrdns] = NULL; + rc = LDAP_SUCCESS; + } + goto return_result; + } + } + +parsing_error:; + if ( newRDN ) { + ldap_rdnfree_x( newRDN, ctx ); + } + + for ( nrdns-- ;nrdns >= 0; nrdns-- ) { + ldap_rdnfree_x( tmpDN[nrdns], ctx ); + } + +return_result:; + + if ( tmpDN != tmpDN_ ) { + LDAP_FREEX( tmpDN, ctx ); + } + + Debug( LDAP_DEBUG_ARGS, "<= ldap_bv2dn(%s)=%d %s\n", str, rc, + rc ? ldap_err2string( rc ) : "" ); + *dn = newDN; + + return( rc ); +} + +/* + * ldap_str2rdn + * + * Parses a relative DN according to flags up to a rdn separator + * or to the end of str. + * Returns the rdn and a pointer to the string continuation, which + * corresponds to the rdn separator or to '\0' in case the string is over. + */ +int +ldap_str2rdn( LDAP_CONST char *str, LDAPRDN *rdn, + char **n_in, unsigned flags ) +{ + struct berval bv; + + assert( str != NULL ); + assert( str[ 0 ] != '\0' ); /* FIXME: is this required? */ + + bv.bv_len = strlen( str ); + bv.bv_val = (char *) str; + + return ldap_bv2rdn_x( &bv, rdn, n_in, flags, NULL ); +} + +int +ldap_bv2rdn( struct berval *bv, LDAPRDN *rdn, + char **n_in, unsigned flags ) +{ + return ldap_bv2rdn_x( bv, rdn, n_in, flags, NULL ); +} + +int +ldap_bv2rdn_x( struct berval *bv, LDAPRDN *rdn, + char **n_in, unsigned flags, void *ctx ) +{ + const char **n = (const char **) n_in; + const char *p; + int navas = 0; + int state = B4AVA; + int rc = LDAP_DECODING_ERROR; + int attrTypeEncoding = LDAP_AVA_STRING, + attrValueEncoding = LDAP_AVA_STRING; + + struct berval attrType = BER_BVNULL; + struct berval attrValue = BER_BVNULL; + + LDAPRDN newRDN = NULL; + LDAPAVA *tmpRDN_[TMP_AVA_SLOTS], **tmpRDN = tmpRDN_; + int num_slots = TMP_AVA_SLOTS; + + char *str; + ber_len_t stoplen; + + assert( bv != NULL ); + assert( bv->bv_len != 0 ); + assert( bv->bv_val != NULL ); + assert( rdn || flags & LDAP_DN_SKIP ); + assert( n != NULL ); + + str = bv->bv_val; + stoplen = bv->bv_len; + + if ( rdn ) { + *rdn = NULL; + } + *n = NULL; + + switch ( LDAP_DN_FORMAT( flags ) ) { + case LDAP_DN_FORMAT_LDAP: + case LDAP_DN_FORMAT_LDAPV3: + case LDAP_DN_FORMAT_LDAPV2: + case LDAP_DN_FORMAT_DCE: + break; + + /* unsupported in str2dn */ + case LDAP_DN_FORMAT_UFN: + case LDAP_DN_FORMAT_AD_CANONICAL: + return LDAP_PARAM_ERROR; + + case LDAP_DN_FORMAT_LBER: + default: + return LDAP_PARAM_ERROR; + } + + if ( bv->bv_len == 0 ) { + return LDAP_SUCCESS; + + } + + if( memchr( bv->bv_val, '\0', bv->bv_len ) != NULL ) { + /* value must have embedded NULs */ + return LDAP_DECODING_ERROR; + } + + p = str; + for ( ; p[ 0 ] || state == GOTAVA; ) { + + /* + * The parser in principle advances one token a time, + * or toggles state if preferable. + */ + switch (state) { + + /* + * an AttributeType can be encoded as: + * - its string representation; in detail, implementations + * MUST recognize AttributeType string type names listed + * in Section 3 of RFC 4514, and MAY recognize other names. + * - its numeric OID (a dotted decimal string) + */ + case B4AVA: + if ( LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) { + if ( !LDAP_DN_ALLOW_ONE_SPACE( flags ) ) { + /* error */ + goto parsing_error; + } + p++; + } + + if ( LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) { + if ( !LDAP_DN_ALLOW_SPACES( flags ) ) { + /* error */ + goto parsing_error; + } + + /* whitespace is allowed (and trimmed) */ + p++; + while ( p[ 0 ] && LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) { + p++; + } + + if ( !p[ 0 ] ) { + /* error: we expected an AVA */ + goto parsing_error; + } + } + + /* oid */ + if ( LDAP_DN_OID_LEADCHAR( p[ 0 ] ) ) { + state = B4OIDATTRTYPE; + break; + } + + /* else must be alpha */ + if ( !LDAP_DN_DESC_LEADCHAR( p[ 0 ] ) ) { + goto parsing_error; + } + + /* LDAPv2 "oid." prefix */ + if ( LDAP_DN_LDAPV2( flags ) ) { + /* + * to be overly pedantic, we only accept + * "OID." or "oid." + */ + if ( flags & LDAP_DN_PEDANTIC ) { + if ( !strncmp( p, "OID.", 4 ) + || !strncmp( p, "oid.", 4 ) ) { + p += 4; + state = B4OIDATTRTYPE; + break; + } + } else { + if ( !strncasecmp( p, "oid.", 4 ) ) { + p += 4; + state = B4OIDATTRTYPE; + break; + } + } + } + + state = B4STRINGATTRTYPE; + break; + + case B4OIDATTRTYPE: { + int err = LDAP_SUCCESS; + + attrType.bv_val = ldap_int_parse_numericoid( &p, &err, + LDAP_SCHEMA_SKIP); + + if ( err != LDAP_SUCCESS ) { + goto parsing_error; + } + attrType.bv_len = p - attrType.bv_val; + + attrTypeEncoding = LDAP_AVA_BINARY; + + state = B4AVAEQUALS; + break; + } + + case B4STRINGATTRTYPE: { + const char *startPos, *endPos = NULL; + ber_len_t len; + + /* + * the starting char has been found to be + * a LDAP_DN_DESC_LEADCHAR so we don't re-check it + * FIXME: DCE attr types seem to have a more + * restrictive syntax (no '-' ...) + */ + for ( startPos = p++; p[ 0 ]; p++ ) { + if ( LDAP_DN_DESC_CHAR( p[ 0 ] ) ) { + continue; + } + + if ( LDAP_DN_LANG_SEP( p[ 0 ] ) ) { + + /* + * RFC 4514 explicitly does not allow attribute + * description options, such as language tags. + */ + if ( flags & LDAP_DN_PEDANTIC ) { + goto parsing_error; + } + + /* + * we trim ';' and following lang + * and so from attribute types + */ + endPos = p; + for ( ; LDAP_DN_ATTRDESC_CHAR( p[ 0 ] ) + || LDAP_DN_LANG_SEP( p[ 0 ] ); p++ ) { + /* no op */ ; + } + break; + } + break; + } + + len = ( endPos ? endPos : p ) - startPos; + if ( len == 0 ) { + goto parsing_error; + } + + attrTypeEncoding = LDAP_AVA_STRING; + + /* + * here we need to decide whether to use it as is + * or turn it in OID form; as a consequence, we + * need to decide whether to binary encode the value + */ + + state = B4AVAEQUALS; + + if ( flags & LDAP_DN_SKIP ) { + break; + } + + attrType.bv_val = (char *)startPos; + attrType.bv_len = len; + + break; + } + + case B4AVAEQUALS: + /* spaces may not be allowed */ + if ( LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) { + if ( !LDAP_DN_ALLOW_SPACES( flags ) ) { + goto parsing_error; + } + + /* trim spaces */ + for ( p++; LDAP_DN_ASCII_SPACE( p[ 0 ] ); p++ ) { + /* no op */ + } + } + + /* need equal sign */ + if ( !LDAP_DN_AVA_EQUALS( p[ 0 ] ) ) { + goto parsing_error; + } + p++; + + /* spaces may not be allowed */ + if ( LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) { + if ( !LDAP_DN_ALLOW_SPACES( flags ) ) { + goto parsing_error; + } + + /* trim spaces */ + for ( p++; LDAP_DN_ASCII_SPACE( p[ 0 ] ); p++ ) { + /* no op */ + } + } + + /* + * octothorpe means a BER encoded value will follow + * FIXME: I don't think DCE will allow it + */ + if ( LDAP_DN_OCTOTHORPE( p[ 0 ] ) ) { + p++; + attrValueEncoding = LDAP_AVA_BINARY; + state = B4BINARYVALUE; + break; + } + + /* STRING value expected */ + + /* + * if we're pedantic, an attribute type in OID form + * SHOULD imply a BER encoded attribute value; we + * should at least issue a warning + */ + if ( ( flags & LDAP_DN_PEDANTIC ) + && ( attrTypeEncoding == LDAP_AVA_BINARY ) ) { + /* OID attrType SHOULD use binary encoding */ + goto parsing_error; + } + + attrValueEncoding = LDAP_AVA_STRING; + + /* + * LDAPv2 allows the attribute value to be quoted; + * also, IA5 values are expected, in principle + */ + if ( LDAP_DN_LDAPV2( flags ) || LDAP_DN_LDAP( flags ) ) { + if ( LDAP_DN_QUOTES( p[ 0 ] ) ) { + p++; + state = B4IA5VALUEQUOTED; + break; + } + + if ( LDAP_DN_LDAPV2( flags ) ) { + state = B4IA5VALUE; + break; + } + } + + /* + * here STRING means RFC 4514 string + * FIXME: what about DCE strings? + */ + if ( !p[ 0 ] ) { + /* empty value */ + state = GOTAVA; + } else { + state = B4STRINGVALUE; + } + break; + + case B4BINARYVALUE: + if ( hexstr2binval( p, &attrValue, &p, flags, ctx ) ) { + goto parsing_error; + } + + state = GOTAVA; + break; + + case B4STRINGVALUE: + switch ( LDAP_DN_FORMAT( flags ) ) { + case LDAP_DN_FORMAT_LDAP: + case LDAP_DN_FORMAT_LDAPV3: + if ( str2strval( p, stoplen - ( p - str ), + &attrValue, &p, flags, + &attrValueEncoding, ctx ) ) { + goto parsing_error; + } + break; + + case LDAP_DN_FORMAT_DCE: + if ( DCE2strval( p, &attrValue, &p, flags, ctx ) ) { + goto parsing_error; + } + break; + + default: + assert( 0 ); + } + + state = GOTAVA; + break; + + case B4IA5VALUE: + if ( IA52strval( p, &attrValue, &p, flags, ctx ) ) { + goto parsing_error; + } + + state = GOTAVA; + break; + + case B4IA5VALUEQUOTED: + + /* lead quote already stripped */ + if ( quotedIA52strval( p, &attrValue, + &p, flags, ctx ) ) { + goto parsing_error; + } + + state = GOTAVA; + break; + + case GOTAVA: { + int rdnsep = 0; + + if ( !( flags & LDAP_DN_SKIP ) ) { + LDAPAVA *ava; + + /* + * we accept empty values + */ + ava = ldapava_new( &attrType, &attrValue, + attrValueEncoding, ctx ); + if ( ava == NULL ) { + rc = LDAP_NO_MEMORY; + goto parsing_error; + } + tmpRDN[navas++] = ava; + + attrValue.bv_val = NULL; + attrValue.bv_len = 0; + + /* + * prepare room for new AVAs if needed + */ + if (navas == num_slots) { + LDAPAVA **tmp; + + if ( tmpRDN == tmpRDN_ ) { + tmp = LDAP_MALLOCX( num_slots * 2 * sizeof( LDAPAVA * ), ctx ); + if ( tmp == NULL ) { + rc = LDAP_NO_MEMORY; + goto parsing_error; + } + AC_MEMCPY( tmp, tmpRDN, num_slots * sizeof( LDAPAVA * ) ); + + } else { + tmp = LDAP_REALLOCX( tmpRDN, num_slots * 2 * sizeof( LDAPAVA * ), ctx ); + if ( tmp == NULL ) { + rc = LDAP_NO_MEMORY; + goto parsing_error; + } + } + + tmpRDN = tmp; + num_slots *= 2; + } + } + + /* + * if we got an AVA separator ('+', or ',' for DCE ) + * we expect a new AVA for this RDN; otherwise + * we add the RDN to the DN + */ + switch ( LDAP_DN_FORMAT( flags ) ) { + case LDAP_DN_FORMAT_LDAP: + case LDAP_DN_FORMAT_LDAPV3: + case LDAP_DN_FORMAT_LDAPV2: + if ( !LDAP_DN_AVA_SEP( p[ 0 ] ) ) { + rdnsep = 1; + } + break; + + case LDAP_DN_FORMAT_DCE: + if ( !LDAP_DN_AVA_SEP_DCE( p[ 0 ] ) ) { + rdnsep = 1; + } + break; + } + + if ( rdnsep ) { + /* + * the RDN is over, phew + */ + *n = p; + if ( !( flags & LDAP_DN_SKIP ) ) { + newRDN = (LDAPRDN)LDAP_MALLOCX( + sizeof(LDAPAVA) * (navas+1), ctx ); + if ( newRDN == NULL ) { + rc = LDAP_NO_MEMORY; + goto parsing_error; + } else { + AC_MEMCPY( newRDN, tmpRDN, sizeof(LDAPAVA *) * navas); + newRDN[navas] = NULL; + } + + } + rc = LDAP_SUCCESS; + goto return_result; + } + + /* they should have been used in an AVA */ + attrType.bv_val = NULL; + attrValue.bv_val = NULL; + + p++; + state = B4AVA; + break; + } + + default: + assert( 0 ); + goto parsing_error; + } + } + *n = p; + +parsing_error:; + /* They are set to NULL after they're used in an AVA */ + + if ( attrValue.bv_val ) { + LDAP_FREEX( attrValue.bv_val, ctx ); + } + + for ( navas-- ; navas >= 0; navas-- ) { + ldapava_free( tmpRDN[navas], ctx ); + } + +return_result:; + + if ( tmpRDN != tmpRDN_ ) { + LDAP_FREEX( tmpRDN, ctx ); + } + + if ( rdn ) { + *rdn = newRDN; + } + + return( rc ); +} + +/* + * reads in a UTF-8 string value, unescaping stuff: + * '\' + LDAP_DN_NEEDESCAPE(c) -> 'c' + * '\' + HEXPAIR(p) -> unhex(p) + */ +static int +str2strval( const char *str, ber_len_t stoplen, struct berval *val, const char **next, unsigned flags, int *retFlags, void *ctx ) +{ + const char *p, *end, *startPos, *endPos = NULL; + ber_len_t len, escapes; + + assert( str != NULL ); + assert( val != NULL ); + assert( next != NULL ); + + *next = NULL; + end = str + stoplen; + for ( startPos = p = str, escapes = 0; p < end; p++ ) { + if ( LDAP_DN_ESCAPE( p[ 0 ] ) ) { + p++; + if ( p[ 0 ] == '\0' ) { + return( 1 ); + } + if ( LDAP_DN_MAYESCAPE( p[ 0 ] ) ) { + escapes++; + continue; + } + + if ( LDAP_DN_HEXPAIR( p ) ) { + char c; + + hexstr2bin( p, &c ); + escapes += 2; + + if ( !LDAP_DN_ASCII_PRINTABLE( c ) ) { + + /* + * we assume the string is UTF-8 + */ + *retFlags = LDAP_AVA_NONPRINTABLE; + } + p++; + + continue; + } + + if ( LDAP_DN_PEDANTIC & flags ) { + return( 1 ); + } + /* + * we do not allow escaping + * of chars that don't need + * to and do not belong to + * HEXDIGITS + */ + return( 1 ); + + } else if ( !LDAP_DN_ASCII_PRINTABLE( p[ 0 ] ) ) { + if ( p[ 0 ] == '\0' ) { + return( 1 ); + } + *retFlags = LDAP_AVA_NONPRINTABLE; + + } else if ( ( LDAP_DN_LDAP( flags ) && LDAP_DN_VALUE_END_V2( p[ 0 ] ) ) + || ( LDAP_DN_LDAPV3( flags ) && LDAP_DN_VALUE_END( p[ 0 ] ) ) ) { + break; + + } else if ( LDAP_DN_NEEDESCAPE( p[ 0 ] ) ) { + /* + * FIXME: maybe we can add + * escapes if not pedantic? + */ + return( 1 ); + } + } + + /* + * we do allow unescaped spaces at the end + * of the value only in non-pedantic mode + */ + if ( p > startPos + 1 && LDAP_DN_ASCII_SPACE( p[ -1 ] ) && + !LDAP_DN_ESCAPE( p[ -2 ] ) ) { + if ( flags & LDAP_DN_PEDANTIC ) { + return( 1 ); + } + + /* strip trailing (unescaped) spaces */ + for ( endPos = p - 1; + endPos > startPos + 1 && + LDAP_DN_ASCII_SPACE( endPos[ -1 ] ) && + !LDAP_DN_ESCAPE( endPos[ -2 ] ); + endPos-- ) { + /* no op */ + } + } + + *next = p; + if ( flags & LDAP_DN_SKIP ) { + return( 0 ); + } + + /* + * FIXME: test memory? + */ + len = ( endPos ? endPos : p ) - startPos - escapes; + val->bv_len = len; + + if ( escapes == 0 ) { + if ( *retFlags & LDAP_AVA_NONPRINTABLE ) { + val->bv_val = LDAP_MALLOCX( len + 1, ctx ); + AC_MEMCPY( val->bv_val, startPos, len ); + val->bv_val[ len ] = '\0'; + } else { + val->bv_val = LDAP_STRNDUPX( startPos, len, ctx ); + } + + } else { + ber_len_t s, d; + + val->bv_val = LDAP_MALLOCX( len + 1, ctx ); + for ( s = 0, d = 0; d < len; ) { + if ( LDAP_DN_ESCAPE( startPos[ s ] ) ) { + s++; + if ( LDAP_DN_MAYESCAPE( startPos[ s ] ) ) { + val->bv_val[ d++ ] = + startPos[ s++ ]; + + } else if ( LDAP_DN_HEXPAIR( &startPos[ s ] ) ) { + char c; + + hexstr2bin( &startPos[ s ], &c ); + val->bv_val[ d++ ] = c; + s += 2; + + } else { + /* we should never get here */ + assert( 0 ); + } + + } else { + val->bv_val[ d++ ] = startPos[ s++ ]; + } + } + + val->bv_val[ d ] = '\0'; + assert( d == len ); + } + + return( 0 ); +} + +static int +DCE2strval( const char *str, struct berval *val, const char **next, unsigned flags, void *ctx ) +{ + const char *p, *startPos, *endPos = NULL; + ber_len_t len, escapes; + + assert( str != NULL ); + assert( val != NULL ); + assert( next != NULL ); + + *next = NULL; + + for ( startPos = p = str, escapes = 0; p[ 0 ]; p++ ) { + if ( LDAP_DN_ESCAPE_DCE( p[ 0 ] ) ) { + p++; + if ( LDAP_DN_NEEDESCAPE_DCE( p[ 0 ] ) ) { + escapes++; + + } else { + return( 1 ); + } + + } else if ( LDAP_DN_VALUE_END_DCE( p[ 0 ] ) ) { + break; + } + + /* + * FIXME: can we accept anything else? I guess we need + * to stop if a value is not legal + */ + } + + /* + * (unescaped) trailing spaces are trimmed must be silently ignored; + * so we eat them + */ + if ( p > startPos + 1 && LDAP_DN_ASCII_SPACE( p[ -1 ] ) && + !LDAP_DN_ESCAPE( p[ -2 ] ) ) { + if ( flags & LDAP_DN_PEDANTIC ) { + return( 1 ); + } + + /* strip trailing (unescaped) spaces */ + for ( endPos = p - 1; + endPos > startPos + 1 && + LDAP_DN_ASCII_SPACE( endPos[ -1 ] ) && + !LDAP_DN_ESCAPE( endPos[ -2 ] ); + endPos-- ) { + /* no op */ + } + } + + *next = p; + if ( flags & LDAP_DN_SKIP ) { + return( 0 ); + } + + len = ( endPos ? endPos : p ) - startPos - escapes; + val->bv_len = len; + if ( escapes == 0 ){ + val->bv_val = LDAP_STRNDUPX( startPos, len, ctx ); + + } else { + ber_len_t s, d; + + val->bv_val = LDAP_MALLOCX( len + 1, ctx ); + for ( s = 0, d = 0; d < len; ) { + /* + * This point is reached only if escapes + * are properly used, so all we need to + * do is eat them + */ + if ( LDAP_DN_ESCAPE_DCE( startPos[ s ] ) ) { + s++; + + } + val->bv_val[ d++ ] = startPos[ s++ ]; + } + val->bv_val[ d ] = '\0'; + assert( strlen( val->bv_val ) == len ); + } + + return( 0 ); +} + +static int +IA52strval( const char *str, struct berval *val, const char **next, unsigned flags, void *ctx ) +{ + const char *p, *startPos, *endPos = NULL; + ber_len_t len, escapes; + + assert( str != NULL ); + assert( val != NULL ); + assert( next != NULL ); + + *next = NULL; + + /* + * LDAPv2 (RFC 1779) + */ + + for ( startPos = p = str, escapes = 0; p[ 0 ]; p++ ) { + if ( LDAP_DN_ESCAPE( p[ 0 ] ) ) { + p++; + if ( p[ 0 ] == '\0' ) { + return( 1 ); + } + + if ( !LDAP_DN_NEEDESCAPE( p[ 0 ] ) + && ( LDAP_DN_PEDANTIC & flags ) ) { + return( 1 ); + } + escapes++; + + } else if ( LDAP_DN_VALUE_END_V2( p[ 0 ] ) ) { + break; + } + + /* + * FIXME: can we accept anything else? I guess we need + * to stop if a value is not legal + */ + } + + /* strip trailing (unescaped) spaces */ + for ( endPos = p; + endPos > startPos + 1 && + LDAP_DN_ASCII_SPACE( endPos[ -1 ] ) && + !LDAP_DN_ESCAPE( endPos[ -2 ] ); + endPos-- ) { + /* no op */ + } + + *next = p; + if ( flags & LDAP_DN_SKIP ) { + return( 0 ); + } + + len = ( endPos ? endPos : p ) - startPos - escapes; + val->bv_len = len; + if ( escapes == 0 ) { + val->bv_val = LDAP_STRNDUPX( startPos, len, ctx ); + + } else { + ber_len_t s, d; + + val->bv_val = LDAP_MALLOCX( len + 1, ctx ); + for ( s = 0, d = 0; d < len; ) { + if ( LDAP_DN_ESCAPE( startPos[ s ] ) ) { + s++; + } + val->bv_val[ d++ ] = startPos[ s++ ]; + } + val->bv_val[ d ] = '\0'; + assert( strlen( val->bv_val ) == len ); + } + + return( 0 ); +} + +static int +quotedIA52strval( const char *str, struct berval *val, const char **next, unsigned flags, void *ctx ) +{ + const char *p, *startPos, *endPos = NULL; + ber_len_t len; + unsigned escapes = 0; + + assert( str != NULL ); + assert( val != NULL ); + assert( next != NULL ); + + *next = NULL; + + /* initial quote already eaten */ + for ( startPos = p = str; p[ 0 ]; p++ ) { + /* + * According to RFC 1779, the quoted value can + * contain escaped as well as unescaped special values; + * as a consequence we tolerate escaped values + * (e.g. '"\,"' -> '\,') and escape unescaped specials + * (e.g. '","' -> '\,'). + */ + if ( LDAP_DN_ESCAPE( p[ 0 ] ) ) { + if ( p[ 1 ] == '\0' ) { + return( 1 ); + } + p++; + + if ( !LDAP_DN_V2_PAIR( p[ 0 ] ) + && ( LDAP_DN_PEDANTIC & flags ) ) { + /* + * do we allow to escape normal chars? + * LDAPv2 does not allow any mechanism + * for escaping chars with '\' and hex + * pair + */ + return( 1 ); + } + escapes++; + + } else if ( LDAP_DN_QUOTES( p[ 0 ] ) ) { + endPos = p; + /* eat closing quotes */ + p++; + break; + } + + /* + * FIXME: can we accept anything else? I guess we need + * to stop if a value is not legal + */ + } + + if ( endPos == NULL ) { + return( 1 ); + } + + /* Strip trailing (unescaped) spaces */ + for ( ; p[ 0 ] && LDAP_DN_ASCII_SPACE( p[ 0 ] ); p++ ) { + /* no op */ + } + + *next = p; + if ( flags & LDAP_DN_SKIP ) { + return( 0 ); + } + + len = endPos - startPos - escapes; + assert( endPos >= startPos + escapes ); + val->bv_len = len; + if ( escapes == 0 ) { + val->bv_val = LDAP_STRNDUPX( startPos, len, ctx ); + + } else { + ber_len_t s, d; + + val->bv_val = LDAP_MALLOCX( len + 1, ctx ); + val->bv_len = len; + + for ( s = d = 0; d < len; ) { + if ( LDAP_DN_ESCAPE( str[ s ] ) ) { + s++; + } + val->bv_val[ d++ ] = str[ s++ ]; + } + val->bv_val[ d ] = '\0'; + assert( strlen( val->bv_val ) == len ); + } + + return( 0 ); +} + +static int +hexstr2bin( const char *str, char *c ) +{ + char c1, c2; + + assert( str != NULL ); + assert( c != NULL ); + + c1 = str[ 0 ]; + c2 = str[ 1 ]; + + if ( LDAP_DN_ASCII_DIGIT( c1 ) ) { + *c = c1 - '0'; + + } else { + if ( LDAP_DN_ASCII_UCASE_HEXALPHA( c1 ) ) { + *c = c1 - 'A' + 10; + } else { + assert( LDAP_DN_ASCII_LCASE_HEXALPHA( c1 ) ); + *c = c1 - 'a' + 10; + } + } + + *c <<= 4; + + if ( LDAP_DN_ASCII_DIGIT( c2 ) ) { + *c += c2 - '0'; + + } else { + if ( LDAP_DN_ASCII_UCASE_HEXALPHA( c2 ) ) { + *c += c2 - 'A' + 10; + } else { + assert( LDAP_DN_ASCII_LCASE_HEXALPHA( c2 ) ); + *c += c2 - 'a' + 10; + } + } + + return( 0 ); +} + +static int +hexstr2binval( const char *str, struct berval *val, const char **next, unsigned flags, void *ctx ) +{ + const char *p, *startPos, *endPos = NULL; + ber_len_t len; + ber_len_t s, d; + + assert( str != NULL ); + assert( val != NULL ); + assert( next != NULL ); + + *next = NULL; + + for ( startPos = p = str; p[ 0 ]; p += 2 ) { + switch ( LDAP_DN_FORMAT( flags ) ) { + case LDAP_DN_FORMAT_LDAPV3: + if ( LDAP_DN_VALUE_END( p[ 0 ] ) ) { + goto end_of_value; + } + break; + + case LDAP_DN_FORMAT_LDAP: + case LDAP_DN_FORMAT_LDAPV2: + if ( LDAP_DN_VALUE_END_V2( p[ 0 ] ) ) { + goto end_of_value; + } + break; + + case LDAP_DN_FORMAT_DCE: + if ( LDAP_DN_VALUE_END_DCE( p[ 0 ] ) ) { + goto end_of_value; + } + break; + } + + if ( LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) { + if ( flags & LDAP_DN_PEDANTIC ) { + return( 1 ); + } + endPos = p; + + for ( ; p[ 0 ]; p++ ) { + switch ( LDAP_DN_FORMAT( flags ) ) { + case LDAP_DN_FORMAT_LDAPV3: + if ( LDAP_DN_VALUE_END( p[ 0 ] ) ) { + goto end_of_value; + } + break; + + case LDAP_DN_FORMAT_LDAP: + case LDAP_DN_FORMAT_LDAPV2: + if ( LDAP_DN_VALUE_END_V2( p[ 0 ] ) ) { + goto end_of_value; + } + break; + + case LDAP_DN_FORMAT_DCE: + if ( LDAP_DN_VALUE_END_DCE( p[ 0 ] ) ) { + goto end_of_value; + } + break; + } + } + break; + } + + if ( !LDAP_DN_HEXPAIR( p ) ) { + return( 1 ); + } + } + +end_of_value:; + + *next = p; + if ( flags & LDAP_DN_SKIP ) { + return( 0 ); + } + + len = ( ( endPos ? endPos : p ) - startPos ) / 2; + /* must be even! */ + assert( 2 * len == (ber_len_t) (( endPos ? endPos : p ) - startPos )); + + val->bv_len = len; + val->bv_val = LDAP_MALLOCX( len + 1, ctx ); + if ( val->bv_val == NULL ) { + return( LDAP_NO_MEMORY ); + } + + for ( s = 0, d = 0; d < len; s += 2, d++ ) { + char c; + + hexstr2bin( &startPos[ s ], &c ); + + val->bv_val[ d ] = c; + } + + val->bv_val[ d ] = '\0'; + + return( 0 ); +} + +/* + * convert a byte in a hexadecimal pair + */ +static int +byte2hexpair( const char *val, char *pair ) +{ + static const char hexdig[] = "0123456789ABCDEF"; + + assert( val != NULL ); + assert( pair != NULL ); + + /* + * we assume the string has enough room for the hex encoding + * of the value + */ + + pair[ 0 ] = hexdig[ 0x0f & ( val[ 0 ] >> 4 ) ]; + pair[ 1 ] = hexdig[ 0x0f & val[ 0 ] ]; + + return( 0 ); +} + +/* + * convert a binary value in hexadecimal pairs + */ +static int +binval2hexstr( struct berval *val, char *str ) +{ + ber_len_t s, d; + + assert( val != NULL ); + assert( str != NULL ); + + if ( val->bv_len == 0 ) { + return( 0 ); + } + + /* + * we assume the string has enough room for the hex encoding + * of the value + */ + + for ( s = 0, d = 0; s < val->bv_len; s++, d += 2 ) { + byte2hexpair( &val->bv_val[ s ], &str[ d ] ); + } + + return( 0 ); +} + +/* + * Length of the string representation, accounting for escaped hex + * of UTF-8 chars + */ +static int +strval2strlen( struct berval *val, unsigned flags, ber_len_t *len ) +{ + ber_len_t l, cl = 1; + char *p, *end; + int escaped_byte_len = LDAP_DN_IS_PRETTY( flags ) ? 1 : 3; +#ifdef PRETTY_ESCAPE + int escaped_ascii_len = LDAP_DN_IS_PRETTY( flags ) ? 2 : 3; +#endif /* PRETTY_ESCAPE */ + + assert( val != NULL ); + assert( len != NULL ); + + *len = 0; + if ( val->bv_len == 0 ) { + return( 0 ); + } + + end = val->bv_val + val->bv_len - 1; + for ( l = 0, p = val->bv_val; p <= end; p += cl ) { + + /* + * escape '%x00' + */ + if ( p[ 0 ] == '\0' ) { + cl = 1; + l += 3; + continue; + } + + cl = LDAP_UTF8_CHARLEN2( p, cl ); + if ( cl == 0 ) { + /* illegal utf-8 char! */ + return( -1 ); + + } else if ( cl > 1 ) { + ber_len_t cnt; + + for ( cnt = 1; cnt < cl; cnt++ ) { + if ( ( p[ cnt ] & 0xc0 ) != 0x80 ) { + return( -1 ); + } + } + l += escaped_byte_len * cl; + + } else if ( LDAP_DN_NEEDESCAPE( p[ 0 ] ) + || LDAP_DN_SHOULDESCAPE( p[ 0 ] ) + || ( p == val->bv_val && LDAP_DN_NEEDESCAPE_LEAD( p[ 0 ] ) ) + || ( p == end && LDAP_DN_NEEDESCAPE_TRAIL( p[ 0 ] ) ) ) { +#ifdef PRETTY_ESCAPE +#if 0 + if ( LDAP_DN_WILLESCAPE_HEX( flags, p[ 0 ] ) ) { +#else + if ( LDAP_DN_WILLESCAPE_CHAR( p[ 0 ] ) ) { +#endif + + /* + * there might be some chars we want + * to escape in form of a couple + * of hexdigits for optimization purposes + */ + l += 3; + + } else { + l += escaped_ascii_len; + } +#else /* ! PRETTY_ESCAPE */ + l += 3; +#endif /* ! PRETTY_ESCAPE */ + + } else { + l++; + } + } + + *len = l; + + return( 0 ); +} + +/* + * convert to string representation, escaping with hex the UTF-8 stuff; + * assume the destination has enough room for escaping + */ +static int +strval2str( struct berval *val, char *str, unsigned flags, ber_len_t *len ) +{ + ber_len_t s, d, end; + + assert( val != NULL ); + assert( str != NULL ); + assert( len != NULL ); + + if ( val->bv_len == 0 ) { + *len = 0; + return( 0 ); + } + + /* + * we assume the string has enough room for the hex encoding + * of the value + */ + for ( s = 0, d = 0, end = val->bv_len - 1; s < val->bv_len; ) { + ber_len_t cl; + + /* + * escape '%x00' + */ + if ( val->bv_val[ s ] == '\0' ) { + cl = 1; + str[ d++ ] = '\\'; + str[ d++ ] = '0'; + str[ d++ ] = '0'; + s++; + continue; + } + + /* + * The length was checked in strval2strlen(); + */ + cl = LDAP_UTF8_CHARLEN( &val->bv_val[ s ] ); + + /* + * there might be some chars we want to escape in form + * of a couple of hexdigits for optimization purposes + */ + if ( ( cl > 1 && !LDAP_DN_IS_PRETTY( flags ) ) +#ifdef PRETTY_ESCAPE +#if 0 + || LDAP_DN_WILLESCAPE_HEX( flags, val->bv_val[ s ] ) +#else + || LDAP_DN_WILLESCAPE_CHAR( val->bv_val[ s ] ) +#endif +#else /* ! PRETTY_ESCAPE */ + || LDAP_DN_NEEDESCAPE( val->bv_val[ s ] ) + || LDAP_DN_SHOULDESCAPE( val->bv_val[ s ] ) + || ( d == 0 && LDAP_DN_NEEDESCAPE_LEAD( val->bv_val[ s ] ) ) + || ( s == end && LDAP_DN_NEEDESCAPE_TRAIL( val->bv_val[ s ] ) ) + +#endif /* ! PRETTY_ESCAPE */ + ) { + for ( ; cl--; ) { + str[ d++ ] = '\\'; + byte2hexpair( &val->bv_val[ s ], &str[ d ] ); + s++; + d += 2; + } + + } else if ( cl > 1 ) { + for ( ; cl--; ) { + str[ d++ ] = val->bv_val[ s++ ]; + } + + } else { +#ifdef PRETTY_ESCAPE + if ( LDAP_DN_NEEDESCAPE( val->bv_val[ s ] ) + || LDAP_DN_SHOULDESCAPE( val->bv_val[ s ] ) + || ( d == 0 && LDAP_DN_NEEDESCAPE_LEAD( val->bv_val[ s ] ) ) + || ( s == end && LDAP_DN_NEEDESCAPE_TRAIL( val->bv_val[ s ] ) ) ) { + str[ d++ ] = '\\'; + if ( !LDAP_DN_IS_PRETTY( flags ) ) { + byte2hexpair( &val->bv_val[ s ], &str[ d ] ); + s++; + d += 2; + continue; + } + } +#endif /* PRETTY_ESCAPE */ + str[ d++ ] = val->bv_val[ s++ ]; + } + } + + *len = d; + + return( 0 ); +} + +/* + * Length of the IA5 string representation (no UTF-8 allowed) + */ +static int +strval2IA5strlen( struct berval *val, unsigned flags, ber_len_t *len ) +{ + ber_len_t l; + char *p; + + assert( val != NULL ); + assert( len != NULL ); + + *len = 0; + if ( val->bv_len == 0 ) { + return( 0 ); + } + + if ( flags & LDAP_AVA_NONPRINTABLE ) { + /* + * Turn value into a binary encoded BER + */ + return( -1 ); + + } else { + for ( l = 0, p = val->bv_val; p[ 0 ]; p++ ) { + if ( LDAP_DN_NEEDESCAPE( p[ 0 ] ) + || LDAP_DN_SHOULDESCAPE( p[ 0 ] ) + || ( p == val->bv_val && LDAP_DN_NEEDESCAPE_LEAD( p[ 0 ] ) ) + || ( !p[ 1 ] && LDAP_DN_NEEDESCAPE_TRAIL( p[ 0 ] ) ) ) { + l += 2; + + } else { + l++; + } + } + } + + *len = l; + + return( 0 ); +} + +/* + * convert to string representation (np UTF-8) + * assume the destination has enough room for escaping + */ +static int +strval2IA5str( struct berval *val, char *str, unsigned flags, ber_len_t *len ) +{ + ber_len_t s, d, end; + + assert( val != NULL ); + assert( str != NULL ); + assert( len != NULL ); + + if ( val->bv_len == 0 ) { + *len = 0; + return( 0 ); + } + + if ( flags & LDAP_AVA_NONPRINTABLE ) { + /* + * Turn value into a binary encoded BER + */ + *len = 0; + return( -1 ); + + } else { + /* + * we assume the string has enough room for the hex encoding + * of the value + */ + + for ( s = 0, d = 0, end = val->bv_len - 1; s < val->bv_len; ) { + if ( LDAP_DN_NEEDESCAPE( val->bv_val[ s ] ) + || LDAP_DN_SHOULDESCAPE( val->bv_val[ s ] ) + || ( s == 0 && LDAP_DN_NEEDESCAPE_LEAD( val->bv_val[ s ] ) ) + || ( s == end && LDAP_DN_NEEDESCAPE_TRAIL( val->bv_val[ s ] ) ) ) { + str[ d++ ] = '\\'; + } + str[ d++ ] = val->bv_val[ s++ ]; + } + } + + *len = d; + + return( 0 ); +} + +/* + * Length of the (supposedly) DCE string representation, + * accounting for escaped hex of UTF-8 chars + */ +static int +strval2DCEstrlen( struct berval *val, unsigned flags, ber_len_t *len ) +{ + ber_len_t l; + char *p; + + assert( val != NULL ); + assert( len != NULL ); + + *len = 0; + if ( val->bv_len == 0 ) { + return( 0 ); + } + + if ( flags & LDAP_AVA_NONPRINTABLE ) { + /* + * FIXME: Turn the value into a binary encoded BER? + */ + return( -1 ); + + } else { + for ( l = 0, p = val->bv_val; p[ 0 ]; p++ ) { + if ( LDAP_DN_NEEDESCAPE_DCE( p[ 0 ] ) ) { + l += 2; + + } else { + l++; + } + } + } + + *len = l; + + return( 0 ); +} + +/* + * convert to (supposedly) DCE string representation, + * escaping with hex the UTF-8 stuff; + * assume the destination has enough room for escaping + */ +static int +strval2DCEstr( struct berval *val, char *str, unsigned flags, ber_len_t *len ) +{ + ber_len_t s, d; + + assert( val != NULL ); + assert( str != NULL ); + assert( len != NULL ); + + if ( val->bv_len == 0 ) { + *len = 0; + return( 0 ); + } + + if ( flags & LDAP_AVA_NONPRINTABLE ) { + /* + * FIXME: Turn the value into a binary encoded BER? + */ + *len = 0; + return( -1 ); + + } else { + + /* + * we assume the string has enough room for the hex encoding + * of the value + */ + + for ( s = 0, d = 0; s < val->bv_len; ) { + if ( LDAP_DN_NEEDESCAPE_DCE( val->bv_val[ s ] ) ) { + str[ d++ ] = '\\'; + } + str[ d++ ] = val->bv_val[ s++ ]; + } + } + + *len = d; + + return( 0 ); +} + +/* + * Length of the (supposedly) AD canonical string representation, + * accounting for chars that need to be escaped + */ +static int +strval2ADstrlen( struct berval *val, unsigned flags, ber_len_t *len ) +{ + ber_len_t l, cl; + char *p; + + assert( val != NULL ); + assert( len != NULL ); + + *len = 0; + if ( val->bv_len == 0 ) { + return( 0 ); + } + + for ( l = 0, p = val->bv_val; p[ 0 ]; p += cl ) { + cl = LDAP_UTF8_CHARLEN2( p, cl ); + if ( cl == 0 ) { + /* illegal utf-8 char */ + return -1; + } else if ( (cl == 1) && LDAP_DN_NEEDESCAPE_AD( p[ 0 ] ) ) { + l += 2; + } else { + l += cl; + } + } + + *len = l; + + return( 0 ); +} + +/* + * convert to (supposedly) AD string representation, + * assume the destination has enough room for escaping + */ +static int +strval2ADstr( struct berval *val, char *str, unsigned flags, ber_len_t *len ) +{ + ber_len_t s, d, cl; + + assert( val != NULL ); + assert( str != NULL ); + assert( len != NULL ); + + if ( val->bv_len == 0 ) { + *len = 0; + return( 0 ); + } + + /* + * we assume the string has enough room for the escaping + * of the value + */ + + for ( s = 0, d = 0; s < val->bv_len; ) { + cl = LDAP_UTF8_CHARLEN2( val->bv_val+s, cl ); + if ( cl == 0 ) { + /* illegal utf-8 char */ + return -1; + } else if ( (cl == 1) && LDAP_DN_NEEDESCAPE_AD(val->bv_val[ s ]) ) { + str[ d++ ] = '\\'; + } + for (; cl--;) { + str[ d++ ] = val->bv_val[ s++ ]; + } + } + + *len = d; + + return( 0 ); +} + +/* + * If the DN is terminated by single-AVA RDNs with attribute type of "dc", + * the first part of the AD representation of the DN is written in DNS + * form, i.e. dot separated domain name components (as suggested + * by Luke Howard, http://www.padl.com/~lukeh) + */ +static int +dn2domain( LDAPDN dn, struct berval *bv, int pos, int *iRDN ) +{ + int i; + int domain = 0, first = 1; + ber_len_t l = 1; /* we move the null also */ + char *str; + + /* we are guaranteed there's enough memory in str */ + + /* sanity */ + assert( dn != NULL ); + assert( bv != NULL ); + assert( iRDN != NULL ); + assert( *iRDN >= 0 ); + + str = bv->bv_val + pos; + + for ( i = *iRDN; i >= 0; i-- ) { + LDAPRDN rdn; + LDAPAVA *ava; + + assert( dn[ i ] != NULL ); + rdn = dn[ i ]; + + assert( rdn[ 0 ] != NULL ); + ava = rdn[ 0 ]; + + if ( !LDAP_DN_IS_RDN_DC( rdn ) ) { + break; + } + + if ( ldif_is_not_printable( ava->la_value.bv_val, ava->la_value.bv_len ) ) { + domain = 0; + break; + } + + domain = 1; + + if ( first ) { + first = 0; + AC_MEMCPY( str, ava->la_value.bv_val, + ava->la_value.bv_len + 1); + l += ava->la_value.bv_len; + + } else { + AC_MEMCPY( str + ava->la_value.bv_len + 1, bv->bv_val + pos, l); + AC_MEMCPY( str, ava->la_value.bv_val, + ava->la_value.bv_len ); + str[ ava->la_value.bv_len ] = '.'; + l += ava->la_value.bv_len + 1; + } + } + + *iRDN = i; + bv->bv_len = pos + l - 1; + + return( domain ); +} + +static int +rdn2strlen( LDAPRDN rdn, unsigned flags, ber_len_t *len, + int ( *s2l )( struct berval *v, unsigned f, ber_len_t *l ) ) +{ + int iAVA; + ber_len_t l = 0; + + *len = 0; + + for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) { + LDAPAVA *ava = rdn[ iAVA ]; + + /* len(type) + '=' + '+' | ',' */ + l += ava->la_attr.bv_len + 2; + + if ( ava->la_flags & LDAP_AVA_BINARY ) { + /* octothorpe + twice the length */ + l += 1 + 2 * ava->la_value.bv_len; + + } else { + ber_len_t vl; + unsigned f = flags | ava->la_flags; + + if ( ( *s2l )( &ava->la_value, f, &vl ) ) { + return( -1 ); + } + l += vl; + } + } + + *len = l; + + return( 0 ); +} + +static int +rdn2str( LDAPRDN rdn, char *str, unsigned flags, ber_len_t *len, + int ( *s2s ) ( struct berval *v, char * s, unsigned f, ber_len_t *l ) ) +{ + int iAVA; + ber_len_t l = 0; + + for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) { + LDAPAVA *ava = rdn[ iAVA ]; + + AC_MEMCPY( &str[ l ], ava->la_attr.bv_val, + ava->la_attr.bv_len ); + l += ava->la_attr.bv_len; + + str[ l++ ] = '='; + + if ( ava->la_flags & LDAP_AVA_BINARY ) { + str[ l++ ] = '#'; + if ( binval2hexstr( &ava->la_value, &str[ l ] ) ) { + return( -1 ); + } + l += 2 * ava->la_value.bv_len; + + } else { + ber_len_t vl; + unsigned f = flags | ava->la_flags; + + if ( ( *s2s )( &ava->la_value, &str[ l ], f, &vl ) ) { + return( -1 ); + } + l += vl; + } + str[ l++ ] = ( rdn[ iAVA + 1] ? '+' : ',' ); + } + + *len = l; + + return( 0 ); +} + +static int +rdn2DCEstrlen( LDAPRDN rdn, unsigned flags, ber_len_t *len ) +{ + int iAVA; + ber_len_t l = 0; + + *len = 0; + + for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) { + LDAPAVA *ava = rdn[ iAVA ]; + + /* len(type) + '=' + ',' | '/' */ + l += ava->la_attr.bv_len + 2; + + if ( ava->la_flags & LDAP_AVA_BINARY ) { + /* octothorpe + twice the length */ + l += 1 + 2 * ava->la_value.bv_len; + } else { + ber_len_t vl; + unsigned f = flags | ava->la_flags; + + if ( strval2DCEstrlen( &ava->la_value, f, &vl ) ) { + return( -1 ); + } + l += vl; + } + } + + *len = l; + + return( 0 ); +} + +static int +rdn2DCEstr( LDAPRDN rdn, char *str, unsigned flags, ber_len_t *len, int first ) +{ + int iAVA; + ber_len_t l = 0; + + for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) { + LDAPAVA *ava = rdn[ iAVA ]; + + if ( first ) { + first = 0; + } else { + str[ l++ ] = ( iAVA ? ',' : '/' ); + } + + AC_MEMCPY( &str[ l ], ava->la_attr.bv_val, + ava->la_attr.bv_len ); + l += ava->la_attr.bv_len; + + str[ l++ ] = '='; + + if ( ava->la_flags & LDAP_AVA_BINARY ) { + str[ l++ ] = '#'; + if ( binval2hexstr( &ava->la_value, &str[ l ] ) ) { + return( -1 ); + } + l += 2 * ava->la_value.bv_len; + } else { + ber_len_t vl; + unsigned f = flags | ava->la_flags; + + if ( strval2DCEstr( &ava->la_value, &str[ l ], f, &vl ) ) { + return( -1 ); + } + l += vl; + } + } + + *len = l; + + return( 0 ); +} + +static int +rdn2UFNstrlen( LDAPRDN rdn, unsigned flags, ber_len_t *len ) +{ + int iAVA; + ber_len_t l = 0; + + assert( rdn != NULL ); + assert( len != NULL ); + + *len = 0; + + for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) { + LDAPAVA *ava = rdn[ iAVA ]; + + /* ' + ' | ', ' */ + l += ( rdn[ iAVA + 1 ] ? 3 : 2 ); + + /* FIXME: are binary values allowed in UFN? */ + if ( ava->la_flags & LDAP_AVA_BINARY ) { + /* octothorpe + twice the value */ + l += 1 + 2 * ava->la_value.bv_len; + + } else { + ber_len_t vl; + unsigned f = flags | ava->la_flags; + + if ( strval2strlen( &ava->la_value, f, &vl ) ) { + return( -1 ); + } + l += vl; + } + } + + *len = l; + + return( 0 ); +} + +static int +rdn2UFNstr( LDAPRDN rdn, char *str, unsigned flags, ber_len_t *len ) +{ + int iAVA; + ber_len_t l = 0; + + for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) { + LDAPAVA *ava = rdn[ iAVA ]; + + if ( ava->la_flags & LDAP_AVA_BINARY ) { + str[ l++ ] = '#'; + if ( binval2hexstr( &ava->la_value, &str[ l ] ) ) { + return( -1 ); + } + l += 2 * ava->la_value.bv_len; + + } else { + ber_len_t vl; + unsigned f = flags | ava->la_flags; + + if ( strval2str( &ava->la_value, &str[ l ], f, &vl ) ) { + return( -1 ); + } + l += vl; + } + + if ( rdn[ iAVA + 1 ] ) { + AC_MEMCPY( &str[ l ], " + ", 3 ); + l += 3; + + } else { + AC_MEMCPY( &str[ l ], ", ", 2 ); + l += 2; + } + } + + *len = l; + + return( 0 ); +} + +static int +rdn2ADstrlen( LDAPRDN rdn, unsigned flags, ber_len_t *len ) +{ + int iAVA; + ber_len_t l = 0; + + assert( rdn != NULL ); + assert( len != NULL ); + + *len = 0; + + for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) { + LDAPAVA *ava = rdn[ iAVA ]; + + /* ',' | '/' */ + l++; + + /* FIXME: are binary values allowed in UFN? */ + if ( ava->la_flags & LDAP_AVA_BINARY ) { + /* octothorpe + twice the value */ + l += 1 + 2 * ava->la_value.bv_len; + } else { + ber_len_t vl; + unsigned f = flags | ava->la_flags; + + if ( strval2ADstrlen( &ava->la_value, f, &vl ) ) { + return( -1 ); + } + l += vl; + } + } + + *len = l; + + return( 0 ); +} + +static int +rdn2ADstr( LDAPRDN rdn, char *str, unsigned flags, ber_len_t *len, int first ) +{ + int iAVA; + ber_len_t l = 0; + + for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) { + LDAPAVA *ava = rdn[ iAVA ]; + + if ( first ) { + first = 0; + } else { + str[ l++ ] = ( iAVA ? ',' : '/' ); + } + + if ( ava->la_flags & LDAP_AVA_BINARY ) { + str[ l++ ] = '#'; + if ( binval2hexstr( &ava->la_value, &str[ l ] ) ) { + return( -1 ); + } + l += 2 * ava->la_value.bv_len; + } else { + ber_len_t vl; + unsigned f = flags | ava->la_flags; + + if ( strval2ADstr( &ava->la_value, &str[ l ], f, &vl ) ) { + return( -1 ); + } + l += vl; + } + } + + *len = l; + + return( 0 ); +} + +/* + * ldap_rdn2str + * + * Returns in str a string representation of rdn based on flags. + * There is some duplication of code between this and ldap_dn2str; + * this is wanted to reduce the allocation of temporary buffers. + */ +int +ldap_rdn2str( LDAPRDN rdn, char **str, unsigned flags ) +{ + struct berval bv; + int rc; + + assert( str != NULL ); + + if((flags & LDAP_DN_FORMAT_MASK) == LDAP_DN_FORMAT_LBER) { + return LDAP_PARAM_ERROR; + } + + rc = ldap_rdn2bv_x( rdn, &bv, flags, NULL ); + *str = bv.bv_val; + return rc; +} + +int +ldap_rdn2bv( LDAPRDN rdn, struct berval *bv, unsigned flags ) +{ + return ldap_rdn2bv_x( rdn, bv, flags, NULL ); +} + +int +ldap_rdn2bv_x( LDAPRDN rdn, struct berval *bv, unsigned flags, void *ctx ) +{ + int rc, back; + ber_len_t l; + + assert( bv != NULL ); + + bv->bv_len = 0; + bv->bv_val = NULL; + + if ( rdn == NULL ) { + bv->bv_val = LDAP_STRDUPX( "", ctx ); + return( LDAP_SUCCESS ); + } + + /* + * This routine wastes "back" bytes at the end of the string + */ + + switch ( LDAP_DN_FORMAT( flags ) ) { + case LDAP_DN_FORMAT_LDAPV3: + if ( rdn2strlen( rdn, flags, &l, strval2strlen ) ) { + return LDAP_DECODING_ERROR; + } + break; + + case LDAP_DN_FORMAT_LDAPV2: + if ( rdn2strlen( rdn, flags, &l, strval2IA5strlen ) ) { + return LDAP_DECODING_ERROR; + } + break; + + case LDAP_DN_FORMAT_UFN: + if ( rdn2UFNstrlen( rdn, flags, &l ) ) { + return LDAP_DECODING_ERROR; + } + break; + + case LDAP_DN_FORMAT_DCE: + if ( rdn2DCEstrlen( rdn, flags, &l ) ) { + return LDAP_DECODING_ERROR; + } + break; + + case LDAP_DN_FORMAT_AD_CANONICAL: + if ( rdn2ADstrlen( rdn, flags, &l ) ) { + return LDAP_DECODING_ERROR; + } + break; + + default: + return LDAP_PARAM_ERROR; + } + + bv->bv_val = LDAP_MALLOCX( l + 1, ctx ); + + switch ( LDAP_DN_FORMAT( flags ) ) { + case LDAP_DN_FORMAT_LDAPV3: + rc = rdn2str( rdn, bv->bv_val, flags, &l, strval2str ); + back = 1; + break; + + case LDAP_DN_FORMAT_LDAPV2: + rc = rdn2str( rdn, bv->bv_val, flags, &l, strval2IA5str ); + back = 1; + break; + + case LDAP_DN_FORMAT_UFN: + rc = rdn2UFNstr( rdn, bv->bv_val, flags, &l ); + back = 2; + break; + + case LDAP_DN_FORMAT_DCE: + rc = rdn2DCEstr( rdn, bv->bv_val, flags, &l, 1 ); + back = 0; + break; + + case LDAP_DN_FORMAT_AD_CANONICAL: + rc = rdn2ADstr( rdn, bv->bv_val, flags, &l, 1 ); + back = 0; + break; + + default: + /* need at least one of the previous */ + return LDAP_PARAM_ERROR; + } + + if ( rc ) { + LDAP_FREEX( bv->bv_val, ctx ); + return rc; + } + + bv->bv_len = l - back; + bv->bv_val[ bv->bv_len ] = '\0'; + + return LDAP_SUCCESS; +} + +/* + * Very bulk implementation; many optimizations can be performed + * - a NULL dn results in an empty string "" + * + * FIXME: doubts + * a) what do we do if a UTF-8 string must be converted in LDAPv2? + * we must encode it in binary form ('#' + HEXPAIRs) + * b) does DCE/AD support UTF-8? + * no clue; don't think so. + * c) what do we do when binary values must be converted in UTF/DCE/AD? + * use binary encoded BER + */ +int ldap_dn2str( LDAPDN dn, char **str, unsigned flags ) +{ + struct berval bv; + int rc; + + assert( str != NULL ); + + if((flags & LDAP_DN_FORMAT_MASK) == LDAP_DN_FORMAT_LBER) { + return LDAP_PARAM_ERROR; + } + + rc = ldap_dn2bv_x( dn, &bv, flags, NULL ); + *str = bv.bv_val; + return rc; +} + +int ldap_dn2bv( LDAPDN dn, struct berval *bv, unsigned flags ) +{ + return ldap_dn2bv_x( dn, bv, flags, NULL ); +} + +int ldap_dn2bv_x( LDAPDN dn, struct berval *bv, unsigned flags, void *ctx ) +{ + int iRDN; + int rc = LDAP_ENCODING_ERROR; + ber_len_t len, l; + + /* stringifying helpers for LDAPv3/LDAPv2 */ + int ( *sv2l ) ( struct berval *v, unsigned f, ber_len_t *l ); + int ( *sv2s ) ( struct berval *v, char *s, unsigned f, ber_len_t *l ); + + assert( bv != NULL ); + bv->bv_len = 0; + bv->bv_val = NULL; + + Debug( LDAP_DEBUG_ARGS, "=> ldap_dn2bv(%u)\n", flags, 0, 0 ); + + /* + * a null dn means an empty dn string + * FIXME: better raise an error? + */ + if ( dn == NULL || dn[0] == NULL ) { + bv->bv_val = LDAP_STRDUPX( "", ctx ); + return( LDAP_SUCCESS ); + } + + switch ( LDAP_DN_FORMAT( flags ) ) { + case LDAP_DN_FORMAT_LDAPV3: + sv2l = strval2strlen; + sv2s = strval2str; + + if( 0 ) { + case LDAP_DN_FORMAT_LDAPV2: + sv2l = strval2IA5strlen; + sv2s = strval2IA5str; + } + + for ( iRDN = 0, len = 0; dn[ iRDN ]; iRDN++ ) { + ber_len_t rdnl; + if ( rdn2strlen( dn[ iRDN ], flags, &rdnl, sv2l ) ) { + goto return_results; + } + + len += rdnl; + } + + if ( ( bv->bv_val = LDAP_MALLOCX( len + 1, ctx ) ) == NULL ) { + rc = LDAP_NO_MEMORY; + break; + } + + for ( l = 0, iRDN = 0; dn[ iRDN ]; iRDN++ ) { + ber_len_t rdnl; + + if ( rdn2str( dn[ iRDN ], &bv->bv_val[ l ], flags, + &rdnl, sv2s ) ) { + LDAP_FREEX( bv->bv_val, ctx ); + bv->bv_val = NULL; + goto return_results; + } + l += rdnl; + } + + assert( l == len ); + + /* + * trim the last ',' (the allocated memory + * is one byte longer than required) + */ + bv->bv_len = len - 1; + bv->bv_val[ bv->bv_len ] = '\0'; + + rc = LDAP_SUCCESS; + break; + + case LDAP_DN_FORMAT_UFN: { + /* + * FIXME: quoting from RFC 1781: + * + To take a distinguished name, and generate a name of this format with + attribute types omitted, the following steps are followed. + + 1. If the first attribute is of type CommonName, the type may be + omitted. + + 2. If the last attribute is of type Country, the type may be + omitted. + + 3. If the last attribute is of type Country, the last + Organisation attribute may have the type omitted. + + 4. All attributes of type OrganisationalUnit may have the type + omitted, unless they are after an Organisation attribute or + the first attribute is of type OrganisationalUnit. + + * this should be the pedantic implementation. + * + * Here the standard implementation reflects + * the one historically provided by OpenLDAP + * (and UMIch, I presume), with the variant + * of spaces and plusses (' + ') separating + * rdn components. + * + * A non-standard but nice implementation could + * be to turn the final "dc" attributes into a + * dot-separated domain. + * + * Other improvements could involve the use of + * friendly country names and so. + */ +#ifdef DC_IN_UFN + int leftmost_dc = -1; + int last_iRDN = -1; +#endif /* DC_IN_UFN */ + + for ( iRDN = 0, len = 0; dn[ iRDN ]; iRDN++ ) { + ber_len_t rdnl; + + if ( rdn2UFNstrlen( dn[ iRDN ], flags, &rdnl ) ) { + goto return_results; + } + len += rdnl; + +#ifdef DC_IN_UFN + if ( LDAP_DN_IS_RDN_DC( dn[ iRDN ] ) ) { + if ( leftmost_dc == -1 ) { + leftmost_dc = iRDN; + } + } else { + leftmost_dc = -1; + } +#endif /* DC_IN_UFN */ + } + + if ( ( bv->bv_val = LDAP_MALLOCX( len + 1, ctx ) ) == NULL ) { + rc = LDAP_NO_MEMORY; + break; + } + +#ifdef DC_IN_UFN + if ( leftmost_dc == -1 ) { +#endif /* DC_IN_UFN */ + for ( l = 0, iRDN = 0; dn[ iRDN ]; iRDN++ ) { + ber_len_t vl; + + if ( rdn2UFNstr( dn[ iRDN ], &bv->bv_val[ l ], + flags, &vl ) ) { + LDAP_FREEX( bv->bv_val, ctx ); + bv->bv_val = NULL; + goto return_results; + } + l += vl; + } + + /* + * trim the last ', ' (the allocated memory + * is two bytes longer than required) + */ + bv->bv_len = len - 2; + bv->bv_val[ bv->bv_len ] = '\0'; +#ifdef DC_IN_UFN + } else { + last_iRDN = iRDN - 1; + + for ( l = 0, iRDN = 0; iRDN < leftmost_dc; iRDN++ ) { + ber_len_t vl; + + if ( rdn2UFNstr( dn[ iRDN ], &bv->bv_val[ l ], + flags, &vl ) ) { + LDAP_FREEX( bv->bv_val, ctx ); + bv->bv_val = NULL; + goto return_results; + } + l += vl; + } + + if ( !dn2domain( dn, bv, l, &last_iRDN ) ) { + LDAP_FREEX( bv->bv_val, ctx ); + bv->bv_val = NULL; + goto return_results; + } + + /* the string is correctly terminated by dn2domain */ + } +#endif /* DC_IN_UFN */ + + rc = LDAP_SUCCESS; + + } break; + + case LDAP_DN_FORMAT_DCE: + for ( iRDN = 0, len = 0; dn[ iRDN ]; iRDN++ ) { + ber_len_t rdnl; + if ( rdn2DCEstrlen( dn[ iRDN ], flags, &rdnl ) ) { + goto return_results; + } + + len += rdnl; + } + + if ( ( bv->bv_val = LDAP_MALLOCX( len + 1, ctx ) ) == NULL ) { + rc = LDAP_NO_MEMORY; + break; + } + + for ( l = 0; iRDN--; ) { + ber_len_t rdnl; + + if ( rdn2DCEstr( dn[ iRDN ], &bv->bv_val[ l ], flags, + &rdnl, 0 ) ) { + LDAP_FREEX( bv->bv_val, ctx ); + bv->bv_val = NULL; + goto return_results; + } + l += rdnl; + } + + assert( l == len ); + + bv->bv_len = len; + bv->bv_val[ bv->bv_len ] = '\0'; + + rc = LDAP_SUCCESS; + break; + + case LDAP_DN_FORMAT_AD_CANONICAL: { + int trailing_slash = 1; + + /* + * Sort of UFN for DCE DNs: a slash ('/') separated + * global->local DN with no types; strictly speaking, + * the naming context should be a domain, which is + * written in DNS-style, e.g. dot-deparated. + * + * Example: + * + * "givenName=Bill+sn=Gates,ou=People,dc=microsoft,dc=com" + * + * will read + * + * "microsoft.com/People/Bill,Gates" + */ + for ( iRDN = 0, len = -1; dn[ iRDN ]; iRDN++ ) { + ber_len_t rdnl; + + if ( rdn2ADstrlen( dn[ iRDN ], flags, &rdnl ) ) { + goto return_results; + } + + len += rdnl; + } + + /* reserve room for trailing '/' in case the DN + * is exactly a domain */ + if ( ( bv->bv_val = LDAP_MALLOCX( len + 1 + 1, ctx ) ) == NULL ) + { + rc = LDAP_NO_MEMORY; + break; + } + + iRDN--; + if ( iRDN && dn2domain( dn, bv, 0, &iRDN ) != 0 ) { + for ( l = bv->bv_len; iRDN >= 0 ; iRDN-- ) { + ber_len_t rdnl; + + trailing_slash = 0; + + if ( rdn2ADstr( dn[ iRDN ], &bv->bv_val[ l ], + flags, &rdnl, 0 ) ) { + LDAP_FREEX( bv->bv_val, ctx ); + bv->bv_val = NULL; + goto return_results; + } + l += rdnl; + } + + } else { + int first = 1; + + /* + * Strictly speaking, AD canonical requires + * a DN to be in the form "..., dc=smtg", + * i.e. terminated by a domain component + */ + if ( flags & LDAP_DN_PEDANTIC ) { + LDAP_FREEX( bv->bv_val, ctx ); + bv->bv_val = NULL; + rc = LDAP_ENCODING_ERROR; + break; + } + + for ( l = 0; iRDN >= 0 ; iRDN-- ) { + ber_len_t rdnl; + + if ( rdn2ADstr( dn[ iRDN ], &bv->bv_val[ l ], + flags, &rdnl, first ) ) { + LDAP_FREEX( bv->bv_val, ctx ); + bv->bv_val = NULL; + goto return_results; + } + if ( first ) { + first = 0; + } + l += rdnl; + } + } + + if ( trailing_slash ) { + /* the DN is exactly a domain -- need a trailing + * slash; room was reserved in advance */ + bv->bv_val[ len ] = '/'; + len++; + } + + bv->bv_len = len; + bv->bv_val[ bv->bv_len ] = '\0'; + + rc = LDAP_SUCCESS; + } break; + + default: + return LDAP_PARAM_ERROR; + } + + Debug( LDAP_DEBUG_ARGS, "<= ldap_dn2bv(%s)=%d %s\n", + bv->bv_val, rc, rc ? ldap_err2string( rc ) : "" ); + +return_results:; + return( rc ); +} + diff --git a/libraries/libldap/getentry.c b/libraries/libldap/getentry.c new file mode 100644 index 0000000..372cb40 --- /dev/null +++ b/libraries/libldap/getentry.c @@ -0,0 +1,124 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/stdlib.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +/* ARGSUSED */ +LDAPMessage * +ldap_first_entry( LDAP *ld, LDAPMessage *chain ) +{ + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( chain != NULL ); + + return chain->lm_msgtype == LDAP_RES_SEARCH_ENTRY + ? chain + : ldap_next_entry( ld, chain ); +} + +LDAPMessage * +ldap_next_entry( LDAP *ld, LDAPMessage *entry ) +{ + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( entry != NULL ); + + for( + entry = entry->lm_chain; + entry != NULL; + entry = entry->lm_chain ) + { + if( entry->lm_msgtype == LDAP_RES_SEARCH_ENTRY ) { + return( entry ); + } + } + + return( NULL ); +} + +int +ldap_count_entries( LDAP *ld, LDAPMessage *chain ) +{ + int i; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + + for ( i = 0; chain != NULL; chain = chain->lm_chain ) { + if( chain->lm_msgtype == LDAP_RES_SEARCH_ENTRY ) { + i++; + } + } + + return( i ); +} + +int +ldap_get_entry_controls( + LDAP *ld, + LDAPMessage *entry, + LDAPControl ***sctrls ) +{ + int rc; + BerElement be; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( entry != NULL ); + assert( sctrls != NULL ); + + if ( entry->lm_msgtype != LDAP_RES_SEARCH_ENTRY ) { + return LDAP_PARAM_ERROR; + } + + /* make a local copy of the BerElement */ + AC_MEMCPY(&be, entry->lm_ber, sizeof(be)); + + if ( ber_scanf( &be, "{xx" /*}*/ ) == LBER_ERROR ) { + rc = LDAP_DECODING_ERROR; + goto cleanup_and_return; + } + + rc = ldap_pvt_get_controls( &be, sctrls ); + +cleanup_and_return: + if( rc != LDAP_SUCCESS ) { + ld->ld_errno = rc; + + if( ld->ld_matched != NULL ) { + LDAP_FREE( ld->ld_matched ); + ld->ld_matched = NULL; + } + + if( ld->ld_error != NULL ) { + LDAP_FREE( ld->ld_error ); + ld->ld_error = NULL; + } + } + + return rc; +} diff --git a/libraries/libldap/getvalues.c b/libraries/libldap/getvalues.c new file mode 100644 index 0000000..b4fef9f --- /dev/null +++ b/libraries/libldap/getvalues.c @@ -0,0 +1,211 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/ctype.h> +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +char ** +ldap_get_values( LDAP *ld, LDAPMessage *entry, LDAP_CONST char *target ) +{ + BerElement ber; + char *attr; + int found = 0; + char **vals; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( entry != NULL ); + assert( target != NULL ); + + Debug( LDAP_DEBUG_TRACE, "ldap_get_values\n", 0, 0, 0 ); + + ber = *entry->lm_ber; + + /* skip sequence, dn, sequence of, and snag the first attr */ + if ( ber_scanf( &ber, "{x{{a" /*}}}*/, &attr ) == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + return( NULL ); + } + + if ( strcasecmp( target, attr ) == 0 ) + found = 1; + + /* break out on success, return out on error */ + while ( ! found ) { + LDAP_FREE(attr); + attr = NULL; + + if ( ber_scanf( &ber, /*{*/ "x}{a" /*}*/, &attr ) == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + return( NULL ); + } + + if ( strcasecmp( target, attr ) == 0 ) + break; + + } + + LDAP_FREE(attr); + attr = NULL; + + /* + * if we get this far, we've found the attribute and are sitting + * just before the set of values. + */ + + if ( ber_scanf( &ber, "[v]", &vals ) == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + return( NULL ); + } + + return( vals ); +} + +struct berval ** +ldap_get_values_len( LDAP *ld, LDAPMessage *entry, LDAP_CONST char *target ) +{ + BerElement ber; + char *attr; + int found = 0; + struct berval **vals; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( entry != NULL ); + assert( target != NULL ); + + Debug( LDAP_DEBUG_TRACE, "ldap_get_values_len\n", 0, 0, 0 ); + + ber = *entry->lm_ber; + + /* skip sequence, dn, sequence of, and snag the first attr */ + if ( ber_scanf( &ber, "{x{{a" /* }}} */, &attr ) == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + return( NULL ); + } + + if ( strcasecmp( target, attr ) == 0 ) + found = 1; + + /* break out on success, return out on error */ + while ( ! found ) { + LDAP_FREE( attr ); + attr = NULL; + + if ( ber_scanf( &ber, /*{*/ "x}{a" /*}*/, &attr ) == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + return( NULL ); + } + + if ( strcasecmp( target, attr ) == 0 ) + break; + } + + LDAP_FREE( attr ); + attr = NULL; + + /* + * if we get this far, we've found the attribute and are sitting + * just before the set of values. + */ + + if ( ber_scanf( &ber, "[V]", &vals ) == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + return( NULL ); + } + + return( vals ); +} + +int +ldap_count_values( char **vals ) +{ + int i; + + if ( vals == NULL ) + return( 0 ); + + for ( i = 0; vals[i] != NULL; i++ ) + ; /* NULL */ + + return( i ); +} + +int +ldap_count_values_len( struct berval **vals ) +{ + return( ldap_count_values( (char **) vals ) ); +} + +void +ldap_value_free( char **vals ) +{ + LDAP_VFREE( vals ); +} + +void +ldap_value_free_len( struct berval **vals ) +{ + ber_bvecfree( vals ); +} + +char ** +ldap_value_dup( char *const *vals ) +{ + char **new; + int i; + + if( vals == NULL ) { + return NULL; + } + + for( i=0; vals[i]; i++ ) { + ; /* Count the number of values */ + } + + if( i == 0 ) { + return NULL; + } + + new = LDAP_MALLOC( (i+1)*sizeof(char *) ); /* Alloc array of pointers */ + if( new == NULL ) { + return NULL; + } + + for( i=0; vals[i]; i++ ) { + new[i] = LDAP_STRDUP( vals[i] ); /* Dup each value */ + if( new[i] == NULL ) { + LDAP_VFREE( new ); + return NULL; + } + } + new[i] = NULL; + + return new; +} + diff --git a/libraries/libldap/gssapi.c b/libraries/libldap/gssapi.c new file mode 100644 index 0000000..8251ea3 --- /dev/null +++ b/libraries/libldap/gssapi.c @@ -0,0 +1,1011 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Author: Stefan Metzmacher <metze@sernet.de> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/socket.h> +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/time.h> +#include <ac/errno.h> +#include <ac/ctype.h> +#include <ac/unistd.h> + +#ifdef HAVE_LIMITS_H +#include <limits.h> +#endif + +#include "ldap-int.h" + +#ifdef HAVE_GSSAPI + +#ifdef HAVE_GSSAPI_GSSAPI_H +#include <gssapi/gssapi.h> +#else +#include <gssapi.h> +#endif + +static char * +gsserrstr( + char *buf, + ber_len_t buf_len, + gss_OID mech, + int gss_rc, + OM_uint32 minor_status ) +{ + OM_uint32 min2; + gss_buffer_desc mech_msg = GSS_C_EMPTY_BUFFER; + gss_buffer_desc gss_msg = GSS_C_EMPTY_BUFFER; + gss_buffer_desc minor_msg = GSS_C_EMPTY_BUFFER; + OM_uint32 msg_ctx = 0; + + if (buf == NULL) { + return NULL; + } + + if (buf_len == 0) { + return NULL; + } + +#ifdef HAVE_GSS_OID_TO_STR + gss_oid_to_str(&min2, mech, &mech_msg); +#endif + gss_display_status(&min2, gss_rc, GSS_C_GSS_CODE, + mech, &msg_ctx, &gss_msg); + gss_display_status(&min2, minor_status, GSS_C_MECH_CODE, + mech, &msg_ctx, &minor_msg); + + snprintf(buf, buf_len, "gss_rc[%d:%*s] mech[%*s] minor[%u:%*s]", + gss_rc, (int)gss_msg.length, + (const char *)(gss_msg.value?gss_msg.value:""), + (int)mech_msg.length, + (const char *)(mech_msg.value?mech_msg.value:""), + minor_status, (int)minor_msg.length, + (const char *)(minor_msg.value?minor_msg.value:"")); + + gss_release_buffer(&min2, &mech_msg); + gss_release_buffer(&min2, &gss_msg); + gss_release_buffer(&min2, &minor_msg); + + buf[buf_len-1] = '\0'; + + return buf; +} + +static void +sb_sasl_gssapi_init( + struct sb_sasl_generic_data *p, + ber_len_t *min_send, + ber_len_t *max_send, + ber_len_t *max_recv ) +{ + gss_ctx_id_t gss_ctx = (gss_ctx_id_t)p->ops_private; + int gss_rc; + OM_uint32 minor_status; + gss_OID ctx_mech = GSS_C_NO_OID; + OM_uint32 ctx_flags = 0; + int conf_req_flag = 0; + OM_uint32 max_input_size; + + gss_inquire_context(&minor_status, + gss_ctx, + NULL, + NULL, + NULL, + &ctx_mech, + &ctx_flags, + NULL, + NULL); + + if (ctx_flags & (GSS_C_CONF_FLAG)) { + conf_req_flag = 1; + } + +#if defined(HAVE_CYRUS_SASL) +#define SEND_PREALLOC_SIZE SASL_MIN_BUFF_SIZE +#else +#define SEND_PREALLOC_SIZE 4096 +#endif +#define SEND_MAX_WIRE_SIZE 0x00A00000 +#define RECV_MAX_WIRE_SIZE 0x0FFFFFFF +#define FALLBACK_SEND_MAX_SIZE 0x009FFFB8 /* from MIT 1.5.x */ + + gss_rc = gss_wrap_size_limit(&minor_status, gss_ctx, + conf_req_flag, GSS_C_QOP_DEFAULT, + SEND_MAX_WIRE_SIZE, &max_input_size); + if ( gss_rc != GSS_S_COMPLETE ) { + char msg[256]; + ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug, + "sb_sasl_gssapi_init: failed to wrap size limit: %s\n", + gsserrstr( msg, sizeof(msg), ctx_mech, gss_rc, minor_status ) ); + ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug, + "sb_sasl_gssapi_init: fallback to default wrap size limit\n"); + /* + * some libgssglue/libgssapi versions + * have a broken gss_wrap_size_limit() + * implementation + */ + max_input_size = FALLBACK_SEND_MAX_SIZE; + } + + *min_send = SEND_PREALLOC_SIZE; + *max_send = max_input_size; + *max_recv = RECV_MAX_WIRE_SIZE; +} + +static ber_int_t +sb_sasl_gssapi_encode( + struct sb_sasl_generic_data *p, + unsigned char *buf, + ber_len_t len, + Sockbuf_Buf *dst ) +{ + gss_ctx_id_t gss_ctx = (gss_ctx_id_t)p->ops_private; + int gss_rc; + OM_uint32 minor_status; + gss_buffer_desc unwrapped, wrapped; + gss_OID ctx_mech = GSS_C_NO_OID; + OM_uint32 ctx_flags = 0; + int conf_req_flag = 0; + int conf_state; + unsigned char *b; + ber_len_t pkt_len; + + unwrapped.value = buf; + unwrapped.length = len; + + gss_inquire_context(&minor_status, + gss_ctx, + NULL, + NULL, + NULL, + &ctx_mech, + &ctx_flags, + NULL, + NULL); + + if (ctx_flags & (GSS_C_CONF_FLAG)) { + conf_req_flag = 1; + } + + gss_rc = gss_wrap(&minor_status, gss_ctx, + conf_req_flag, GSS_C_QOP_DEFAULT, + &unwrapped, &conf_state, + &wrapped); + if ( gss_rc != GSS_S_COMPLETE ) { + char msg[256]; + ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug, + "sb_sasl_gssapi_encode: failed to encode packet: %s\n", + gsserrstr( msg, sizeof(msg), ctx_mech, gss_rc, minor_status ) ); + return -1; + } + + if ( conf_req_flag && conf_state == 0 ) { + ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug, + "sb_sasl_gssapi_encode: GSS_C_CONF_FLAG was ignored by our gss_wrap()\n" ); + return -1; + } + + pkt_len = 4 + wrapped.length; + + /* Grow the packet buffer if neccessary */ + if ( dst->buf_size < pkt_len && + ber_pvt_sb_grow_buffer( dst, pkt_len ) < 0 ) + { + ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug, + "sb_sasl_gssapi_encode: failed to grow the buffer to %lu bytes\n", + pkt_len ); + return -1; + } + + dst->buf_end = pkt_len; + + b = (unsigned char *)dst->buf_base; + + b[0] = (unsigned char)(wrapped.length >> 24); + b[1] = (unsigned char)(wrapped.length >> 16); + b[2] = (unsigned char)(wrapped.length >> 8); + b[3] = (unsigned char)(wrapped.length >> 0); + + /* copy the wrapped blob to the right location */ + memcpy(b + 4, wrapped.value, wrapped.length); + + gss_release_buffer(&minor_status, &wrapped); + + return 0; +} + +static ber_int_t +sb_sasl_gssapi_decode( + struct sb_sasl_generic_data *p, + const Sockbuf_Buf *src, + Sockbuf_Buf *dst ) +{ + gss_ctx_id_t gss_ctx = (gss_ctx_id_t)p->ops_private; + int gss_rc; + OM_uint32 minor_status; + gss_buffer_desc unwrapped, wrapped; + gss_OID ctx_mech = GSS_C_NO_OID; + OM_uint32 ctx_flags = 0; + int conf_req_flag = 0; + int conf_state; + unsigned char *b; + + wrapped.value = src->buf_base + 4; + wrapped.length = src->buf_end - 4; + + gss_inquire_context(&minor_status, + gss_ctx, + NULL, + NULL, + NULL, + &ctx_mech, + &ctx_flags, + NULL, + NULL); + + if (ctx_flags & (GSS_C_CONF_FLAG)) { + conf_req_flag = 1; + } + + gss_rc = gss_unwrap(&minor_status, gss_ctx, + &wrapped, &unwrapped, + &conf_state, GSS_C_QOP_DEFAULT); + if ( gss_rc != GSS_S_COMPLETE ) { + char msg[256]; + ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug, + "sb_sasl_gssapi_decode: failed to decode packet: %s\n", + gsserrstr( msg, sizeof(msg), ctx_mech, gss_rc, minor_status ) ); + return -1; + } + + if ( conf_req_flag && conf_state == 0 ) { + ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug, + "sb_sasl_gssapi_encode: GSS_C_CONF_FLAG was ignored by our peer\n" ); + return -1; + } + + /* Grow the packet buffer if neccessary */ + if ( dst->buf_size < unwrapped.length && + ber_pvt_sb_grow_buffer( dst, unwrapped.length ) < 0 ) + { + ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug, + "sb_sasl_gssapi_decode: failed to grow the buffer to %lu bytes\n", + unwrapped.length ); + return -1; + } + + dst->buf_end = unwrapped.length; + + b = (unsigned char *)dst->buf_base; + + /* copy the wrapped blob to the right location */ + memcpy(b, unwrapped.value, unwrapped.length); + + gss_release_buffer(&minor_status, &unwrapped); + + return 0; +} + +static void +sb_sasl_gssapi_reset_buf( + struct sb_sasl_generic_data *p, + Sockbuf_Buf *buf ) +{ + ber_pvt_sb_buf_destroy( buf ); +} + +static void +sb_sasl_gssapi_fini( struct sb_sasl_generic_data *p ) +{ +} + +static const struct sb_sasl_generic_ops sb_sasl_gssapi_ops = { + sb_sasl_gssapi_init, + sb_sasl_gssapi_encode, + sb_sasl_gssapi_decode, + sb_sasl_gssapi_reset_buf, + sb_sasl_gssapi_fini +}; + +static int +sb_sasl_gssapi_install( + Sockbuf *sb, + gss_ctx_id_t gss_ctx ) +{ + struct sb_sasl_generic_install install_arg; + + install_arg.ops = &sb_sasl_gssapi_ops; + install_arg.ops_private = gss_ctx; + + return ldap_pvt_sasl_generic_install( sb, &install_arg ); +} + +static void +sb_sasl_gssapi_remove( Sockbuf *sb ) +{ + ldap_pvt_sasl_generic_remove( sb ); +} + +static int +map_gsserr2ldap( + LDAP *ld, + gss_OID mech, + int gss_rc, + OM_uint32 minor_status ) +{ + char msg[256]; + + Debug( LDAP_DEBUG_ANY, "%s\n", + gsserrstr( msg, sizeof(msg), mech, gss_rc, minor_status ), + NULL, NULL ); + + if (gss_rc == GSS_S_COMPLETE) { + ld->ld_errno = LDAP_SUCCESS; + } else if (GSS_CALLING_ERROR(gss_rc)) { + ld->ld_errno = LDAP_LOCAL_ERROR; + } else if (GSS_ROUTINE_ERROR(gss_rc)) { + ld->ld_errno = LDAP_INAPPROPRIATE_AUTH; + } else if (gss_rc == GSS_S_CONTINUE_NEEDED) { + ld->ld_errno = LDAP_SASL_BIND_IN_PROGRESS; + } else if (GSS_SUPPLEMENTARY_INFO(gss_rc)) { + ld->ld_errno = LDAP_AUTH_UNKNOWN; + } else if (GSS_ERROR(gss_rc)) { + ld->ld_errno = LDAP_AUTH_UNKNOWN; + } else { + ld->ld_errno = LDAP_OTHER; + } + + return ld->ld_errno; +} + + +static int +ldap_gssapi_get_rootdse_infos ( + LDAP *ld, + char **pmechlist, + char **pldapServiceName, + char **pdnsHostName ) +{ + /* we need to query the server for supported mechs anyway */ + LDAPMessage *res, *e; + char *attrs[] = { + "supportedSASLMechanisms", + "ldapServiceName", + "dnsHostName", + NULL + }; + char **values, *mechlist; + char *ldapServiceName = NULL; + char *dnsHostName = NULL; + int rc; + + Debug( LDAP_DEBUG_TRACE, "ldap_gssapi_get_rootdse_infos\n", 0, 0, 0 ); + + rc = ldap_search_s( ld, "", LDAP_SCOPE_BASE, + NULL, attrs, 0, &res ); + + if ( rc != LDAP_SUCCESS ) { + return ld->ld_errno; + } + + e = ldap_first_entry( ld, res ); + if ( e == NULL ) { + ldap_msgfree( res ); + if ( ld->ld_errno == LDAP_SUCCESS ) { + ld->ld_errno = LDAP_NO_SUCH_OBJECT; + } + return ld->ld_errno; + } + + values = ldap_get_values( ld, e, "supportedSASLMechanisms" ); + if ( values == NULL ) { + ldap_msgfree( res ); + ld->ld_errno = LDAP_NO_SUCH_ATTRIBUTE; + return ld->ld_errno; + } + + mechlist = ldap_charray2str( values, " " ); + if ( mechlist == NULL ) { + LDAP_VFREE( values ); + ldap_msgfree( res ); + ld->ld_errno = LDAP_NO_MEMORY; + return ld->ld_errno; + } + + LDAP_VFREE( values ); + + values = ldap_get_values( ld, e, "ldapServiceName" ); + if ( values == NULL ) { + goto get_dns_host_name; + } + + ldapServiceName = ldap_charray2str( values, " " ); + if ( ldapServiceName == NULL ) { + LDAP_FREE( mechlist ); + LDAP_VFREE( values ); + ldap_msgfree( res ); + ld->ld_errno = LDAP_NO_MEMORY; + return ld->ld_errno; + } + LDAP_VFREE( values ); + +get_dns_host_name: + + values = ldap_get_values( ld, e, "dnsHostName" ); + if ( values == NULL ) { + goto done; + } + + dnsHostName = ldap_charray2str( values, " " ); + if ( dnsHostName == NULL ) { + LDAP_FREE( mechlist ); + LDAP_FREE( ldapServiceName ); + LDAP_VFREE( values ); + ldap_msgfree( res ); + ld->ld_errno = LDAP_NO_MEMORY; + return ld->ld_errno; + } + LDAP_VFREE( values ); + +done: + ldap_msgfree( res ); + + *pmechlist = mechlist; + *pldapServiceName = ldapServiceName; + *pdnsHostName = dnsHostName; + + return LDAP_SUCCESS; +} + + +static int check_for_gss_spnego_support( LDAP *ld, const char *mechs_str ) +{ + int rc; + char **mechs_list = NULL; + + mechs_list = ldap_str2charray( mechs_str, " " ); + if ( mechs_list == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + return ld->ld_errno; + } + + rc = ldap_charray_inlist( mechs_list, "GSS-SPNEGO" ); + ldap_charray_free( mechs_list ); + if ( rc != 1) { + ld->ld_errno = LDAP_STRONG_AUTH_NOT_SUPPORTED; + return ld->ld_errno; + } + + return LDAP_SUCCESS; +} + +static int +guess_service_principal( + LDAP *ld, + const char *ldapServiceName, + const char *dnsHostName, + gss_name_t *principal ) +{ + gss_buffer_desc input_name; + /* GSS_KRB5_NT_PRINCIPAL_NAME */ + gss_OID_desc nt_principal = + {10, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x01"}; + const char *host = ld->ld_defconn->lconn_server->lud_host; + OM_uint32 minor_status; + int gss_rc; + int ret; + size_t svc_principal_size; + char *svc_principal = NULL; + const char *principal_fmt = NULL; + const char *str = NULL; + const char *givenstr = NULL; + const char *ignore = "not_defined_in_RFC4178@please_ignore"; + int allow_remote = 0; + + if (ldapServiceName) { + givenstr = strchr(ldapServiceName, ':'); + if (givenstr && givenstr[1]) { + givenstr++; + if (strcmp(givenstr, ignore) == 0) { + givenstr = NULL; + } + } else { + givenstr = NULL; + } + } + + if ( ld->ld_options.ldo_gssapi_options & LDAP_GSSAPI_OPT_ALLOW_REMOTE_PRINCIPAL ) { + allow_remote = 1; + } + + if (allow_remote && givenstr) { + principal_fmt = "%s"; + svc_principal_size = strlen(givenstr) + 1; + str = givenstr; + + } else if (allow_remote && dnsHostName) { + principal_fmt = "ldap/%s"; + svc_principal_size = STRLENOF("ldap/") + strlen(dnsHostName) + 1; + str = dnsHostName; + + } else { + principal_fmt = "ldap/%s"; + svc_principal_size = STRLENOF("ldap/") + strlen(host) + 1; + str = host; + } + + svc_principal = (char*) ldap_memalloc(svc_principal_size * sizeof(char)); + if ( svc_principal == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + return ld->ld_errno; + } + + ret = snprintf( svc_principal, svc_principal_size, principal_fmt, str ); + if (ret < 0 || (size_t)ret >= svc_principal_size) { + ld->ld_errno = LDAP_LOCAL_ERROR; + return ld->ld_errno; + } + + Debug( LDAP_DEBUG_TRACE, "principal for host[%s]: '%s'\n", + host, svc_principal, 0 ); + + input_name.value = svc_principal; + input_name.length = (size_t)ret; + + gss_rc = gss_import_name( &minor_status, &input_name, &nt_principal, principal ); + ldap_memfree( svc_principal ); + if ( gss_rc != GSS_S_COMPLETE ) { + return map_gsserr2ldap( ld, GSS_C_NO_OID, gss_rc, minor_status ); + } + + return LDAP_SUCCESS; +} + +void ldap_int_gssapi_close( LDAP *ld, LDAPConn *lc ) +{ + if ( lc && lc->lconn_gss_ctx ) { + OM_uint32 minor_status; + OM_uint32 ctx_flags = 0; + gss_ctx_id_t old_gss_ctx = GSS_C_NO_CONTEXT; + old_gss_ctx = (gss_ctx_id_t)lc->lconn_gss_ctx; + + gss_inquire_context(&minor_status, + old_gss_ctx, + NULL, + NULL, + NULL, + NULL, + &ctx_flags, + NULL, + NULL); + + if (!( ld->ld_options.ldo_gssapi_options & LDAP_GSSAPI_OPT_DO_NOT_FREE_GSS_CONTEXT )) { + gss_delete_sec_context( &minor_status, &old_gss_ctx, GSS_C_NO_BUFFER ); + } + lc->lconn_gss_ctx = GSS_C_NO_CONTEXT; + + if (ctx_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG)) { + /* remove wrapping layer */ + sb_sasl_gssapi_remove( lc->lconn_sb ); + } + } +} + +static void +ldap_int_gssapi_setup( + LDAP *ld, + LDAPConn *lc, + gss_ctx_id_t gss_ctx) +{ + OM_uint32 minor_status; + OM_uint32 ctx_flags = 0; + + ldap_int_gssapi_close( ld, lc ); + + gss_inquire_context(&minor_status, + gss_ctx, + NULL, + NULL, + NULL, + NULL, + &ctx_flags, + NULL, + NULL); + + lc->lconn_gss_ctx = gss_ctx; + + if (ctx_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG)) { + /* setup wrapping layer */ + sb_sasl_gssapi_install( lc->lconn_sb, gss_ctx ); + } +} + +#ifdef LDAP_R_COMPILE +ldap_pvt_thread_mutex_t ldap_int_gssapi_mutex; +#endif + +static int +ldap_int_gss_spnego_bind_s( LDAP *ld ) +{ + int rc; + int gss_rc; + OM_uint32 minor_status; + char *mechlist = NULL; + char *ldapServiceName = NULL; + char *dnsHostName = NULL; + gss_OID_set supported_mechs = GSS_C_NO_OID_SET; + int spnego_support = 0; +#define __SPNEGO_OID_LENGTH 6 +#define __SPNEGO_OID "\053\006\001\005\005\002" + gss_OID_desc spnego_oid = {__SPNEGO_OID_LENGTH, __SPNEGO_OID}; + gss_OID req_mech = GSS_C_NO_OID; + gss_OID ret_mech = GSS_C_NO_OID; + gss_ctx_id_t gss_ctx = GSS_C_NO_CONTEXT; + gss_name_t principal = GSS_C_NO_NAME; + OM_uint32 req_flags; + OM_uint32 ret_flags; + gss_buffer_desc input_token, output_token = GSS_C_EMPTY_BUFFER; + struct berval cred, *scred = NULL; + + LDAP_MUTEX_LOCK( &ldap_int_gssapi_mutex ); + + /* get information from RootDSE entry */ + rc = ldap_gssapi_get_rootdse_infos ( ld, &mechlist, + &ldapServiceName, &dnsHostName); + if ( rc != LDAP_SUCCESS ) { + return rc; + } + + /* check that the server supports GSS-SPNEGO */ + rc = check_for_gss_spnego_support( ld, mechlist ); + if ( rc != LDAP_SUCCESS ) { + goto rc_error; + } + + /* prepare new gss_ctx_id_t */ + rc = guess_service_principal( ld, ldapServiceName, dnsHostName, &principal ); + if ( rc != LDAP_SUCCESS ) { + goto rc_error; + } + + /* see if our gssapi library supports spnego */ + gss_rc = gss_indicate_mechs( &minor_status, &supported_mechs ); + if ( gss_rc != GSS_S_COMPLETE ) { + goto gss_error; + } + gss_rc = gss_test_oid_set_member( &minor_status, + &spnego_oid, supported_mechs, &spnego_support); + gss_release_oid_set( &minor_status, &supported_mechs); + if ( gss_rc != GSS_S_COMPLETE ) { + goto gss_error; + } + if ( spnego_support != 0 ) { + req_mech = &spnego_oid; + } + + req_flags = ld->ld_options.ldo_gssapi_flags; + req_flags |= GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG; + + /* + * loop around gss_init_sec_context() and ldap_sasl_bind_s() + */ + input_token.value = NULL; + input_token.length = 0; + gss_rc = gss_init_sec_context(&minor_status, + GSS_C_NO_CREDENTIAL, + &gss_ctx, + principal, + req_mech, + req_flags, + 0, + NULL, + &input_token, + &ret_mech, + &output_token, + &ret_flags, + NULL); + if ( gss_rc == GSS_S_COMPLETE ) { + rc = LDAP_INAPPROPRIATE_AUTH; + goto rc_error; + } + if ( gss_rc != GSS_S_CONTINUE_NEEDED ) { + goto gss_error; + } + while (1) { + cred.bv_val = (char *)output_token.value; + cred.bv_len = output_token.length; + rc = ldap_sasl_bind_s( ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred ); + gss_release_buffer( &minor_status, &output_token ); + if ( rc != LDAP_SUCCESS && rc != LDAP_SASL_BIND_IN_PROGRESS ) { + goto rc_error; + } + + if ( scred ) { + input_token.value = scred->bv_val; + input_token.length = scred->bv_len; + } else { + input_token.value = NULL; + input_token.length = 0; + } + + gss_rc = gss_init_sec_context(&minor_status, + GSS_C_NO_CREDENTIAL, + &gss_ctx, + principal, + req_mech, + req_flags, + 0, + NULL, + &input_token, + &ret_mech, + &output_token, + &ret_flags, + NULL); + if ( scred ) { + ber_bvfree( scred ); + } + if ( gss_rc == GSS_S_COMPLETE ) { + gss_release_buffer( &minor_status, &output_token ); + break; + } + + if ( gss_rc != GSS_S_CONTINUE_NEEDED ) { + goto gss_error; + } + } + + ldap_int_gssapi_setup( ld, ld->ld_defconn, gss_ctx); + gss_ctx = GSS_C_NO_CONTEXT; + + rc = LDAP_SUCCESS; + goto rc_error; + +gss_error: + rc = map_gsserr2ldap( ld, + (ret_mech != GSS_C_NO_OID ? ret_mech : req_mech ), + gss_rc, minor_status ); +rc_error: + LDAP_MUTEX_UNLOCK( &ldap_int_gssapi_mutex ); + LDAP_FREE( mechlist ); + LDAP_FREE( ldapServiceName ); + LDAP_FREE( dnsHostName ); + gss_release_buffer( &minor_status, &output_token ); + if ( gss_ctx != GSS_C_NO_CONTEXT ) { + gss_delete_sec_context( &minor_status, &gss_ctx, GSS_C_NO_BUFFER ); + } + if ( principal != GSS_C_NO_NAME ) { + gss_release_name( &minor_status, &principal ); + } + return rc; +} + +int +ldap_int_gssapi_config( struct ldapoptions *lo, int option, const char *arg ) +{ + int ok = 0; + + switch( option ) { + case LDAP_OPT_SIGN: + + if (!arg) { + } else if (strcasecmp(arg, "on") == 0) { + ok = 1; + } else if (strcasecmp(arg, "yes") == 0) { + ok = 1; + } else if (strcasecmp(arg, "true") == 0) { + ok = 1; + + } + if (ok) { + lo->ldo_gssapi_flags |= GSS_C_INTEG_FLAG; + } + + return 0; + + case LDAP_OPT_ENCRYPT: + + if (!arg) { + } else if (strcasecmp(arg, "on") == 0) { + ok = 1; + } else if (strcasecmp(arg, "yes") == 0) { + ok = 1; + } else if (strcasecmp(arg, "true") == 0) { + ok = 1; + } + + if (ok) { + lo->ldo_gssapi_flags |= GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG; + } + + return 0; + + case LDAP_OPT_X_GSSAPI_ALLOW_REMOTE_PRINCIPAL: + + if (!arg) { + } else if (strcasecmp(arg, "on") == 0) { + ok = 1; + } else if (strcasecmp(arg, "yes") == 0) { + ok = 1; + } else if (strcasecmp(arg, "true") == 0) { + ok = 1; + } + + if (ok) { + lo->ldo_gssapi_options |= LDAP_GSSAPI_OPT_ALLOW_REMOTE_PRINCIPAL; + } + + return 0; + } + + return -1; +} + +int +ldap_int_gssapi_get_option( LDAP *ld, int option, void *arg ) +{ + if ( ld == NULL ) + return -1; + + switch ( option ) { + case LDAP_OPT_SSPI_FLAGS: + * (unsigned *) arg = (unsigned) ld->ld_options.ldo_gssapi_flags; + break; + + case LDAP_OPT_SIGN: + if ( ld->ld_options.ldo_gssapi_flags & GSS_C_INTEG_FLAG ) { + * (int *) arg = (int)-1; + } else { + * (int *) arg = (int)0; + } + break; + + case LDAP_OPT_ENCRYPT: + if ( ld->ld_options.ldo_gssapi_flags & GSS_C_CONF_FLAG ) { + * (int *) arg = (int)-1; + } else { + * (int *) arg = (int)0; + } + break; + + case LDAP_OPT_SASL_METHOD: + * (char **) arg = LDAP_STRDUP("GSS-SPNEGO"); + break; + + case LDAP_OPT_SECURITY_CONTEXT: + if ( ld->ld_defconn && ld->ld_defconn->lconn_gss_ctx ) { + * (gss_ctx_id_t *) arg = (gss_ctx_id_t)ld->ld_defconn->lconn_gss_ctx; + } else { + * (gss_ctx_id_t *) arg = GSS_C_NO_CONTEXT; + } + break; + + case LDAP_OPT_X_GSSAPI_DO_NOT_FREE_CONTEXT: + if ( ld->ld_options.ldo_gssapi_options & LDAP_GSSAPI_OPT_DO_NOT_FREE_GSS_CONTEXT ) { + * (int *) arg = (int)-1; + } else { + * (int *) arg = (int)0; + } + break; + + case LDAP_OPT_X_GSSAPI_ALLOW_REMOTE_PRINCIPAL: + if ( ld->ld_options.ldo_gssapi_options & LDAP_GSSAPI_OPT_ALLOW_REMOTE_PRINCIPAL ) { + * (int *) arg = (int)-1; + } else { + * (int *) arg = (int)0; + } + break; + + default: + return -1; + } + + return 0; +} + +int +ldap_int_gssapi_set_option( LDAP *ld, int option, void *arg ) +{ + if ( ld == NULL ) + return -1; + + switch ( option ) { + case LDAP_OPT_SSPI_FLAGS: + if ( arg != LDAP_OPT_OFF ) { + ld->ld_options.ldo_gssapi_flags = * (unsigned *)arg; + } + break; + + case LDAP_OPT_SIGN: + if ( arg != LDAP_OPT_OFF ) { + ld->ld_options.ldo_gssapi_flags |= GSS_C_INTEG_FLAG; + } + break; + + case LDAP_OPT_ENCRYPT: + if ( arg != LDAP_OPT_OFF ) { + ld->ld_options.ldo_gssapi_flags |= GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG; + } + break; + + case LDAP_OPT_SASL_METHOD: + if ( arg != LDAP_OPT_OFF ) { + const char *m = (const char *)arg; + if ( strcmp( "GSS-SPNEGO", m ) != 0 ) { + /* we currently only support GSS-SPNEGO */ + return -1; + } + } + break; + + case LDAP_OPT_SECURITY_CONTEXT: + if ( arg != LDAP_OPT_OFF && ld->ld_defconn) { + ldap_int_gssapi_setup( ld, ld->ld_defconn, + (gss_ctx_id_t) arg); + } + break; + + case LDAP_OPT_X_GSSAPI_DO_NOT_FREE_CONTEXT: + if ( arg != LDAP_OPT_OFF ) { + ld->ld_options.ldo_gssapi_options |= LDAP_GSSAPI_OPT_DO_NOT_FREE_GSS_CONTEXT; + } + break; + + case LDAP_OPT_X_GSSAPI_ALLOW_REMOTE_PRINCIPAL: + if ( arg != LDAP_OPT_OFF ) { + ld->ld_options.ldo_gssapi_options |= LDAP_GSSAPI_OPT_ALLOW_REMOTE_PRINCIPAL; + } + break; + + default: + return -1; + } + + return 0; +} + +#else /* HAVE_GSSAPI */ +#define ldap_int_gss_spnego_bind_s(ld) LDAP_NOT_SUPPORTED +#endif /* HAVE_GSSAPI */ + +int +ldap_gssapi_bind( + LDAP *ld, + LDAP_CONST char *dn, + LDAP_CONST char *creds ) +{ + return LDAP_NOT_SUPPORTED; +} + +int +ldap_gssapi_bind_s( + LDAP *ld, + LDAP_CONST char *dn, + LDAP_CONST char *creds ) +{ + if ( dn != NULL ) { + return LDAP_NOT_SUPPORTED; + } + + if ( creds != NULL ) { + return LDAP_NOT_SUPPORTED; + } + + return ldap_int_gss_spnego_bind_s(ld); +} diff --git a/libraries/libldap/init.c b/libraries/libldap/init.c new file mode 100644 index 0000000..182ef7d --- /dev/null +++ b/libraries/libldap/init.c @@ -0,0 +1,728 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/stdlib.h> + +#ifdef HAVE_GETEUID +#include <ac/unistd.h> +#endif + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/ctype.h> +#include <ac/time.h> + +#ifdef HAVE_LIMITS_H +#include <limits.h> +#endif + +#include "ldap-int.h" +#include "ldap_defaults.h" +#include "lutil.h" + +struct ldapoptions ldap_int_global_options = + { LDAP_UNINITIALIZED, LDAP_DEBUG_NONE + LDAP_LDO_NULLARG + LDAP_LDO_CONNECTIONLESS_NULLARG + LDAP_LDO_TLS_NULLARG + LDAP_LDO_SASL_NULLARG + LDAP_LDO_GSSAPI_NULLARG + LDAP_LDO_MUTEX_NULLARG }; + +#define ATTR_NONE 0 +#define ATTR_BOOL 1 +#define ATTR_INT 2 +#define ATTR_KV 3 +#define ATTR_STRING 4 +#define ATTR_OPTION 5 + +#define ATTR_SASL 6 +#define ATTR_TLS 7 + +#define ATTR_OPT_TV 8 +#define ATTR_OPT_INT 9 + +#define ATTR_GSSAPI 10 + +struct ol_keyvalue { + const char * key; + int value; +}; + +static const struct ol_keyvalue deref_kv[] = { + {"never", LDAP_DEREF_NEVER}, + {"searching", LDAP_DEREF_SEARCHING}, + {"finding", LDAP_DEREF_FINDING}, + {"always", LDAP_DEREF_ALWAYS}, + {NULL, 0} +}; + +static const struct ol_attribute { + int useronly; + int type; + const char * name; + const void * data; + size_t offset; +} attrs[] = { + {0, ATTR_OPT_TV, "TIMEOUT", NULL, LDAP_OPT_TIMEOUT}, + {0, ATTR_OPT_TV, "NETWORK_TIMEOUT", NULL, LDAP_OPT_NETWORK_TIMEOUT}, + {0, ATTR_OPT_INT, "VERSION", NULL, LDAP_OPT_PROTOCOL_VERSION}, + {0, ATTR_KV, "DEREF", deref_kv, /* or &deref_kv[0] */ + offsetof(struct ldapoptions, ldo_deref)}, + {0, ATTR_INT, "SIZELIMIT", NULL, + offsetof(struct ldapoptions, ldo_sizelimit)}, + {0, ATTR_INT, "TIMELIMIT", NULL, + offsetof(struct ldapoptions, ldo_timelimit)}, + {1, ATTR_STRING, "BINDDN", NULL, + offsetof(struct ldapoptions, ldo_defbinddn)}, + {0, ATTR_STRING, "BASE", NULL, + offsetof(struct ldapoptions, ldo_defbase)}, + {0, ATTR_INT, "PORT", NULL, /* deprecated */ + offsetof(struct ldapoptions, ldo_defport)}, + {0, ATTR_OPTION, "HOST", NULL, LDAP_OPT_HOST_NAME}, /* deprecated */ + {0, ATTR_OPTION, "URI", NULL, LDAP_OPT_URI}, /* replaces HOST/PORT */ + {0, ATTR_BOOL, "REFERRALS", NULL, LDAP_BOOL_REFERRALS}, +#if 0 + /* This should only be allowed via ldap_set_option(3) */ + {0, ATTR_BOOL, "RESTART", NULL, LDAP_BOOL_RESTART}, +#endif + +#ifdef HAVE_CYRUS_SASL + {0, ATTR_STRING, "SASL_MECH", NULL, + offsetof(struct ldapoptions, ldo_def_sasl_mech)}, + {0, ATTR_STRING, "SASL_REALM", NULL, + offsetof(struct ldapoptions, ldo_def_sasl_realm)}, + {1, ATTR_STRING, "SASL_AUTHCID", NULL, + offsetof(struct ldapoptions, ldo_def_sasl_authcid)}, + {1, ATTR_STRING, "SASL_AUTHZID", NULL, + offsetof(struct ldapoptions, ldo_def_sasl_authzid)}, + {0, ATTR_SASL, "SASL_SECPROPS", NULL, LDAP_OPT_X_SASL_SECPROPS}, + {0, ATTR_BOOL, "SASL_NOCANON", NULL, LDAP_BOOL_SASL_NOCANON}, +#endif + +#ifdef HAVE_GSSAPI + {0, ATTR_GSSAPI,"GSSAPI_SIGN", NULL, LDAP_OPT_SIGN}, + {0, ATTR_GSSAPI,"GSSAPI_ENCRYPT", NULL, LDAP_OPT_ENCRYPT}, + {0, ATTR_GSSAPI,"GSSAPI_ALLOW_REMOTE_PRINCIPAL",NULL, LDAP_OPT_X_GSSAPI_ALLOW_REMOTE_PRINCIPAL}, +#endif + +#ifdef HAVE_TLS + {1, ATTR_TLS, "TLS_CERT", NULL, LDAP_OPT_X_TLS_CERTFILE}, + {1, ATTR_TLS, "TLS_KEY", NULL, LDAP_OPT_X_TLS_KEYFILE}, + {0, ATTR_TLS, "TLS_CACERT", NULL, LDAP_OPT_X_TLS_CACERTFILE}, + {0, ATTR_TLS, "TLS_CACERTDIR", NULL, LDAP_OPT_X_TLS_CACERTDIR}, + {0, ATTR_TLS, "TLS_REQCERT", NULL, LDAP_OPT_X_TLS_REQUIRE_CERT}, + {0, ATTR_TLS, "TLS_REQSAN", NULL, LDAP_OPT_X_TLS_REQUIRE_SAN}, + {0, ATTR_TLS, "TLS_RANDFILE", NULL, LDAP_OPT_X_TLS_RANDOM_FILE}, + {0, ATTR_TLS, "TLS_CIPHER_SUITE", NULL, LDAP_OPT_X_TLS_CIPHER_SUITE}, + {0, ATTR_TLS, "TLS_PROTOCOL_MIN", NULL, LDAP_OPT_X_TLS_PROTOCOL_MIN}, + {0, ATTR_TLS, "TLS_ECNAME", NULL, LDAP_OPT_X_TLS_ECNAME}, + +#ifdef HAVE_OPENSSL_CRL + {0, ATTR_TLS, "TLS_CRLCHECK", NULL, LDAP_OPT_X_TLS_CRLCHECK}, +#endif +#ifdef HAVE_GNUTLS + {0, ATTR_TLS, "TLS_CRLFILE", NULL, LDAP_OPT_X_TLS_CRLFILE}, +#endif + +#endif + + {0, ATTR_NONE, NULL, NULL, 0} +}; + +#define MAX_LDAP_ATTR_LEN sizeof("GSSAPI_ALLOW_REMOTE_PRINCIPAL") +#define MAX_LDAP_ENV_PREFIX_LEN 8 + +static void openldap_ldap_init_w_conf( + const char *file, int userconf ) +{ + char linebuf[ AC_LINE_MAX ]; + FILE *fp; + int i; + char *cmd, *opt; + char *start, *end; + struct ldapoptions *gopts; + + if ((gopts = LDAP_INT_GLOBAL_OPT()) == NULL) { + return; /* Could not allocate mem for global options */ + } + + if (file == NULL) { + /* no file name */ + return; + } + + Debug(LDAP_DEBUG_TRACE, "ldap_init: trying %s\n", file, 0, 0); + + fp = fopen(file, "r"); + if(fp == NULL) { + /* could not open file */ + return; + } + + Debug(LDAP_DEBUG_TRACE, "ldap_init: using %s\n", file, 0, 0); + + while((start = fgets(linebuf, sizeof(linebuf), fp)) != NULL) { + /* skip lines starting with '#' */ + if(*start == '#') continue; + + /* trim leading white space */ + while((*start != '\0') && isspace((unsigned char) *start)) + start++; + + /* anything left? */ + if(*start == '\0') continue; + + /* trim trailing white space */ + end = &start[strlen(start)-1]; + while(isspace((unsigned char)*end)) end--; + end[1] = '\0'; + + /* anything left? */ + if(*start == '\0') continue; + + + /* parse the command */ + cmd=start; + while((*start != '\0') && !isspace((unsigned char)*start)) { + start++; + } + if(*start == '\0') { + /* command has no argument */ + continue; + } + + *start++ = '\0'; + + /* we must have some whitespace to skip */ + while(isspace((unsigned char)*start)) start++; + opt = start; + + for(i=0; attrs[i].type != ATTR_NONE; i++) { + void *p; + + if( !userconf && attrs[i].useronly ) { + continue; + } + + if(strcasecmp(cmd, attrs[i].name) != 0) { + continue; + } + + switch(attrs[i].type) { + case ATTR_BOOL: + if((strcasecmp(opt, "on") == 0) + || (strcasecmp(opt, "yes") == 0) + || (strcasecmp(opt, "true") == 0)) + { + LDAP_BOOL_SET(gopts, attrs[i].offset); + + } else { + LDAP_BOOL_CLR(gopts, attrs[i].offset); + } + + break; + + case ATTR_INT: { + char *next; + long l; + p = &((char *) gopts)[attrs[i].offset]; + l = strtol( opt, &next, 10 ); + if ( next != opt && next[ 0 ] == '\0' ) { + * (int*) p = l; + } + } break; + + case ATTR_KV: { + const struct ol_keyvalue *kv; + + for(kv = attrs[i].data; + kv->key != NULL; + kv++) { + + if(strcasecmp(opt, kv->key) == 0) { + p = &((char *) gopts)[attrs[i].offset]; + * (int*) p = kv->value; + break; + } + } + } break; + + case ATTR_STRING: + p = &((char *) gopts)[attrs[i].offset]; + if (* (char**) p != NULL) LDAP_FREE(* (char**) p); + * (char**) p = LDAP_STRDUP(opt); + break; + case ATTR_OPTION: + ldap_set_option( NULL, attrs[i].offset, opt ); + break; + case ATTR_SASL: +#ifdef HAVE_CYRUS_SASL + ldap_int_sasl_config( gopts, attrs[i].offset, opt ); +#endif + break; + case ATTR_GSSAPI: +#ifdef HAVE_GSSAPI + ldap_int_gssapi_config( gopts, attrs[i].offset, opt ); +#endif + break; + case ATTR_TLS: +#ifdef HAVE_TLS + ldap_int_tls_config( NULL, attrs[i].offset, opt ); +#endif + break; + case ATTR_OPT_TV: { + struct timeval tv; + char *next; + tv.tv_usec = 0; + tv.tv_sec = strtol( opt, &next, 10 ); + if ( next != opt && next[ 0 ] == '\0' && tv.tv_sec > 0 ) { + (void)ldap_set_option( NULL, attrs[i].offset, (const void *)&tv ); + } + } break; + case ATTR_OPT_INT: { + long l; + char *next; + l = strtol( opt, &next, 10 ); + if ( next != opt && next[ 0 ] == '\0' && l > 0 && (long)((int)l) == l ) { + int v = (int)l; + (void)ldap_set_option( NULL, attrs[i].offset, (const void *)&v ); + } + } break; + } + + break; + } + } + + fclose(fp); +} + +static void openldap_ldap_init_w_sysconf(const char *file) +{ + openldap_ldap_init_w_conf( file, 0 ); +} + +static void openldap_ldap_init_w_userconf(const char *file) +{ + char *home; + char *path = NULL; + + if (file == NULL) { + /* no file name */ + return; + } + + home = getenv("HOME"); + + if (home != NULL) { + Debug(LDAP_DEBUG_TRACE, "ldap_init: HOME env is %s\n", + home, 0, 0); + path = LDAP_MALLOC(strlen(home) + strlen(file) + sizeof( LDAP_DIRSEP ".")); + } else { + Debug(LDAP_DEBUG_TRACE, "ldap_init: HOME env is NULL\n", + 0, 0, 0); + } + + if(home != NULL && path != NULL) { + /* we assume UNIX path syntax is used... */ + + /* try ~/file */ + sprintf(path, "%s" LDAP_DIRSEP "%s", home, file); + openldap_ldap_init_w_conf(path, 1); + + /* try ~/.file */ + sprintf(path, "%s" LDAP_DIRSEP ".%s", home, file); + openldap_ldap_init_w_conf(path, 1); + } + + if(path != NULL) { + LDAP_FREE(path); + } + + /* try file */ + openldap_ldap_init_w_conf(file, 1); +} + +static void openldap_ldap_init_w_env( + struct ldapoptions *gopts, + const char *prefix) +{ + char buf[MAX_LDAP_ATTR_LEN+MAX_LDAP_ENV_PREFIX_LEN]; + int len; + int i; + void *p; + char *value; + + if (prefix == NULL) { + prefix = LDAP_ENV_PREFIX; + } + + strncpy(buf, prefix, MAX_LDAP_ENV_PREFIX_LEN); + buf[MAX_LDAP_ENV_PREFIX_LEN] = '\0'; + len = strlen(buf); + + for(i=0; attrs[i].type != ATTR_NONE; i++) { + strcpy(&buf[len], attrs[i].name); + value = getenv(buf); + + if(value == NULL) { + continue; + } + + switch(attrs[i].type) { + case ATTR_BOOL: + if((strcasecmp(value, "on") == 0) + || (strcasecmp(value, "yes") == 0) + || (strcasecmp(value, "true") == 0)) + { + LDAP_BOOL_SET(gopts, attrs[i].offset); + + } else { + LDAP_BOOL_CLR(gopts, attrs[i].offset); + } + break; + + case ATTR_INT: + p = &((char *) gopts)[attrs[i].offset]; + * (int*) p = atoi(value); + break; + + case ATTR_KV: { + const struct ol_keyvalue *kv; + + for(kv = attrs[i].data; + kv->key != NULL; + kv++) { + + if(strcasecmp(value, kv->key) == 0) { + p = &((char *) gopts)[attrs[i].offset]; + * (int*) p = kv->value; + break; + } + } + } break; + + case ATTR_STRING: + p = &((char *) gopts)[attrs[i].offset]; + if (* (char**) p != NULL) LDAP_FREE(* (char**) p); + if (*value == '\0') { + * (char**) p = NULL; + } else { + * (char**) p = LDAP_STRDUP(value); + } + break; + case ATTR_OPTION: + ldap_set_option( NULL, attrs[i].offset, value ); + break; + case ATTR_SASL: +#ifdef HAVE_CYRUS_SASL + ldap_int_sasl_config( gopts, attrs[i].offset, value ); +#endif + break; + case ATTR_GSSAPI: +#ifdef HAVE_GSSAPI + ldap_int_gssapi_config( gopts, attrs[i].offset, value ); +#endif + break; + case ATTR_TLS: +#ifdef HAVE_TLS + ldap_int_tls_config( NULL, attrs[i].offset, value ); +#endif + break; + case ATTR_OPT_TV: { + struct timeval tv; + char *next; + tv.tv_usec = 0; + tv.tv_sec = strtol( value, &next, 10 ); + if ( next != value && next[ 0 ] == '\0' && tv.tv_sec > 0 ) { + (void)ldap_set_option( NULL, attrs[i].offset, (const void *)&tv ); + } + } break; + case ATTR_OPT_INT: { + long l; + char *next; + l = strtol( value, &next, 10 ); + if ( next != value && next[ 0 ] == '\0' && l > 0 && (long)((int)l) == l ) { + int v = (int)l; + (void)ldap_set_option( NULL, attrs[i].offset, (const void *)&v ); + } + } break; + } + } +} + +#if defined(__GNUC__) +/* Declare this function as a destructor so that it will automatically be + * invoked either at program exit (if libldap is a static library) or + * at unload time (if libldap is a dynamic library). + * + * Sorry, don't know how to handle this for non-GCC environments. + */ +static void ldap_int_destroy_global_options(void) + __attribute__ ((destructor)); +#endif + +static void +ldap_int_destroy_global_options(void) +{ + struct ldapoptions *gopts = LDAP_INT_GLOBAL_OPT(); + + if ( gopts == NULL ) + return; + + gopts->ldo_valid = LDAP_UNINITIALIZED; + + if ( gopts->ldo_defludp ) { + ldap_free_urllist( gopts->ldo_defludp ); + gopts->ldo_defludp = NULL; + } +#if defined(HAVE_WINSOCK) || defined(HAVE_WINSOCK2) + WSACleanup( ); +#endif + +#if defined(HAVE_TLS) || defined(HAVE_CYRUS_SASL) + if ( ldap_int_hostname ) { + LDAP_FREE( ldap_int_hostname ); + ldap_int_hostname = NULL; + } +#endif +#ifdef HAVE_CYRUS_SASL + if ( gopts->ldo_def_sasl_authcid ) { + LDAP_FREE( gopts->ldo_def_sasl_authcid ); + gopts->ldo_def_sasl_authcid = NULL; + } +#endif +#ifdef HAVE_TLS + ldap_int_tls_destroy( gopts ); +#endif +} + +/* + * Initialize the global options structure with default values. + */ +void ldap_int_initialize_global_options( struct ldapoptions *gopts, int *dbglvl ) +{ + if (dbglvl) + gopts->ldo_debug = *dbglvl; + else + gopts->ldo_debug = 0; + + gopts->ldo_version = LDAP_VERSION2; + gopts->ldo_deref = LDAP_DEREF_NEVER; + gopts->ldo_timelimit = LDAP_NO_LIMIT; + gopts->ldo_sizelimit = LDAP_NO_LIMIT; + + gopts->ldo_tm_api.tv_sec = -1; + gopts->ldo_tm_net.tv_sec = -1; + + /* ldo_defludp will be freed by the termination handler + */ + ldap_url_parselist(&gopts->ldo_defludp, "ldap://localhost/"); + gopts->ldo_defport = LDAP_PORT; +#if !defined(__GNUC__) && !defined(PIC) + /* Do this only for a static library, and only if we can't + * arrange for it to be executed as a library destructor + */ + atexit(ldap_int_destroy_global_options); +#endif + + gopts->ldo_refhoplimit = LDAP_DEFAULT_REFHOPLIMIT; + gopts->ldo_rebind_proc = NULL; + gopts->ldo_rebind_params = NULL; + + LDAP_BOOL_ZERO(gopts); + + LDAP_BOOL_SET(gopts, LDAP_BOOL_REFERRALS); + +#ifdef LDAP_CONNECTIONLESS + gopts->ldo_peer = NULL; + gopts->ldo_cldapdn = NULL; + gopts->ldo_is_udp = 0; +#endif + +#ifdef HAVE_CYRUS_SASL + gopts->ldo_def_sasl_mech = NULL; + gopts->ldo_def_sasl_realm = NULL; + gopts->ldo_def_sasl_authcid = NULL; + gopts->ldo_def_sasl_authzid = NULL; + + memset( &gopts->ldo_sasl_secprops, + '\0', sizeof(gopts->ldo_sasl_secprops) ); + + gopts->ldo_sasl_secprops.max_ssf = INT_MAX; + gopts->ldo_sasl_secprops.maxbufsize = SASL_MAX_BUFF_SIZE; + gopts->ldo_sasl_secprops.security_flags = + SASL_SEC_NOPLAINTEXT | SASL_SEC_NOANONYMOUS; +#endif + +#ifdef HAVE_TLS + gopts->ldo_tls_connect_cb = NULL; + gopts->ldo_tls_connect_arg = NULL; + gopts->ldo_tls_require_cert = LDAP_OPT_X_TLS_DEMAND; + gopts->ldo_tls_require_san = LDAP_OPT_X_TLS_ALLOW; +#endif + gopts->ldo_keepalive_probes = 0; + gopts->ldo_keepalive_interval = 0; + gopts->ldo_keepalive_idle = 0; + + gopts->ldo_valid = LDAP_INITIALIZED; + return; +} + +#if defined(HAVE_TLS) || defined(HAVE_CYRUS_SASL) +char * ldap_int_hostname = NULL; +#endif + +void ldap_int_initialize( struct ldapoptions *gopts, int *dbglvl ) +{ +#ifdef LDAP_R_COMPILE + static ldap_pvt_thread_mutex_t init_mutex; + LDAP_PVT_MUTEX_FIRSTCREATE( init_mutex ); + + LDAP_MUTEX_LOCK( &init_mutex ); +#endif + if ( gopts->ldo_valid == LDAP_INITIALIZED ) { + /* someone else got here first */ + goto done; + } + + ldap_int_error_init(); + + ldap_int_utils_init(); + +#ifdef HAVE_WINSOCK2 +{ WORD wVersionRequested; + WSADATA wsaData; + + wVersionRequested = MAKEWORD( 2, 0 ); + if ( WSAStartup( wVersionRequested, &wsaData ) != 0 ) { + /* Tell the user that we couldn't find a usable */ + /* WinSock DLL. */ + goto done; + } + + /* Confirm that the WinSock DLL supports 2.0.*/ + /* Note that if the DLL supports versions greater */ + /* than 2.0 in addition to 2.0, it will still return */ + /* 2.0 in wVersion since that is the version we */ + /* requested. */ + + if ( LOBYTE( wsaData.wVersion ) != 2 || + HIBYTE( wsaData.wVersion ) != 0 ) + { + /* Tell the user that we couldn't find a usable */ + /* WinSock DLL. */ + WSACleanup( ); + goto done; + } +} /* The WinSock DLL is acceptable. Proceed. */ +#elif defined(HAVE_WINSOCK) +{ WSADATA wsaData; + if ( WSAStartup( 0x0101, &wsaData ) != 0 ) { + goto done; + } +} +#endif + +#if defined(HAVE_TLS) || defined(HAVE_CYRUS_SASL) + LDAP_MUTEX_LOCK( &ldap_int_hostname_mutex ); + { + char *name = ldap_int_hostname; + + ldap_int_hostname = ldap_pvt_get_fqdn( name ); + + if ( name != NULL && name != ldap_int_hostname ) { + LDAP_FREE( name ); + } + } + LDAP_MUTEX_UNLOCK( &ldap_int_hostname_mutex ); +#endif + +#ifndef HAVE_POLL + if ( ldap_int_tblsize == 0 ) ldap_int_ip_init(); +#endif + +#ifdef HAVE_CYRUS_SASL + if ( ldap_int_sasl_init() != 0 ) { + goto done; + } +#endif + + ldap_int_initialize_global_options(gopts, dbglvl); + + if( getenv("LDAPNOINIT") != NULL ) { + goto done; + } + +#ifdef HAVE_CYRUS_SASL + { + /* set authentication identity to current user name */ + char *user = getenv("USER"); + + if( user == NULL ) user = getenv("USERNAME"); + if( user == NULL ) user = getenv("LOGNAME"); + + if( user != NULL ) { + gopts->ldo_def_sasl_authcid = LDAP_STRDUP( user ); + } + } +#endif + + openldap_ldap_init_w_sysconf(LDAP_CONF_FILE); + +#ifdef HAVE_GETEUID + if ( geteuid() != getuid() ) + goto done; +#endif + + openldap_ldap_init_w_userconf(LDAP_USERRC_FILE); + + { + char *altfile = getenv(LDAP_ENV_PREFIX "CONF"); + + if( altfile != NULL ) { + Debug(LDAP_DEBUG_TRACE, "ldap_init: %s env is %s\n", + LDAP_ENV_PREFIX "CONF", altfile, 0); + openldap_ldap_init_w_sysconf( altfile ); + } + else + Debug(LDAP_DEBUG_TRACE, "ldap_init: %s env is NULL\n", + LDAP_ENV_PREFIX "CONF", 0, 0); + } + + { + char *altfile = getenv(LDAP_ENV_PREFIX "RC"); + + if( altfile != NULL ) { + Debug(LDAP_DEBUG_TRACE, "ldap_init: %s env is %s\n", + LDAP_ENV_PREFIX "RC", altfile, 0); + openldap_ldap_init_w_userconf( altfile ); + } + else + Debug(LDAP_DEBUG_TRACE, "ldap_init: %s env is NULL\n", + LDAP_ENV_PREFIX "RC", 0, 0); + } + + openldap_ldap_init_w_env(gopts, NULL); + +done:; +#ifdef LDAP_R_COMPILE + LDAP_MUTEX_UNLOCK( &init_mutex ); +#endif +} diff --git a/libraries/libldap/ldap-int.h b/libraries/libldap/ldap-int.h new file mode 100644 index 0000000..98ad4dc --- /dev/null +++ b/libraries/libldap/ldap-int.h @@ -0,0 +1,889 @@ +/* ldap-int.h - defines & prototypes internal to the LDAP library */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1995 Regents of the University of Michigan. + * All rights reserved. + */ + +#ifndef _LDAP_INT_H +#define _LDAP_INT_H 1 + +#ifdef LDAP_R_COMPILE +#define LDAP_THREAD_SAFE 1 +#endif + +#include "../liblber/lber-int.h" +#include "lutil.h" + +#ifdef LDAP_R_COMPILE +#include <ldap_pvt_thread.h> +#endif + +#ifdef HAVE_CYRUS_SASL + /* the need for this should be removed */ +#ifdef HAVE_SASL_SASL_H +#include <sasl/sasl.h> +#else +#include <sasl.h> +#endif + +#define SASL_MAX_BUFF_SIZE (0xffffff) +#define SASL_MIN_BUFF_SIZE 4096 +#endif + +/* for struct timeval */ +#include <ac/time.h> +#ifdef _WIN32 +#include <ac/socket.h> +#endif + +#undef TV2MILLISEC +#define TV2MILLISEC(tv) (((tv)->tv_sec * 1000) + ((tv)->tv_usec/1000)) + +/* + * Support needed if the library is running in the kernel + */ +#if LDAP_INT_IN_KERNEL + /* + * Platform specific function to return a pointer to the + * process-specific global options. + * + * This function should perform the following functions: + * Allocate and initialize a global options struct on a per process basis + * Use callers process identifier to return its global options struct + * Note: Deallocate structure when the process exits + */ +# define LDAP_INT_GLOBAL_OPT() ldap_int_global_opt() + struct ldapoptions *ldap_int_global_opt(void); +#else +# define LDAP_INT_GLOBAL_OPT() (&ldap_int_global_options) +#endif + +#define ldap_debug ((LDAP_INT_GLOBAL_OPT())->ldo_debug) + +#include "ldap_log.h" + +#undef Debug + +#ifdef LDAP_DEBUG + +#define DebugTest( level ) \ + ( ldap_debug & level ) + +#define Debug( level, fmt, arg1, arg2, arg3 ) \ + do { if ( ldap_debug & level ) \ + ldap_log_printf( NULL, (level), (fmt), (arg1), (arg2), (arg3) ); \ + } while ( 0 ) + +#define LDAP_Debug( subsystem, level, fmt, arg1, arg2, arg3 )\ + ldap_log_printf( NULL, (level), (fmt), (arg1), (arg2), (arg3) ) + +#else + +#define DebugTest( level ) (0 == 1) +#define Debug( level, fmt, arg1, arg2, arg3 ) ((void)0) +#define LDAP_Debug( subsystem, level, fmt, arg1, arg2, arg3 ) ((void)0) + +#endif /* LDAP_DEBUG */ + +#define LDAP_DEPRECATED 1 +#include "ldap.h" + +#include "ldap_pvt.h" + +LDAP_BEGIN_DECL + +#define LDAP_URL_PREFIX "ldap://" +#define LDAP_URL_PREFIX_LEN STRLENOF(LDAP_URL_PREFIX) +#define LDAPS_URL_PREFIX "ldaps://" +#define LDAPS_URL_PREFIX_LEN STRLENOF(LDAPS_URL_PREFIX) +#define LDAPI_URL_PREFIX "ldapi://" +#define LDAPI_URL_PREFIX_LEN STRLENOF(LDAPI_URL_PREFIX) +#ifdef LDAP_CONNECTIONLESS +#define LDAPC_URL_PREFIX "cldap://" +#define LDAPC_URL_PREFIX_LEN STRLENOF(LDAPC_URL_PREFIX) +#endif +#define LDAP_URL_URLCOLON "URL:" +#define LDAP_URL_URLCOLON_LEN STRLENOF(LDAP_URL_URLCOLON) + +#define LDAP_REF_STR "Referral:\n" +#define LDAP_REF_STR_LEN STRLENOF(LDAP_REF_STR) +#define LDAP_LDAP_REF_STR LDAP_URL_PREFIX +#define LDAP_LDAP_REF_STR_LEN LDAP_URL_PREFIX_LEN + +#define LDAP_DEFAULT_REFHOPLIMIT 5 + +#define LDAP_BOOL_REFERRALS 0 +#define LDAP_BOOL_RESTART 1 +#define LDAP_BOOL_TLS 3 +#define LDAP_BOOL_CONNECT_ASYNC 4 +#define LDAP_BOOL_SASL_NOCANON 5 + +#define LDAP_BOOLEANS unsigned long +#define LDAP_BOOL(n) ((LDAP_BOOLEANS)1 << (n)) +#define LDAP_BOOL_GET(lo, bool) \ + ((lo)->ldo_booleans & LDAP_BOOL(bool) ? -1 : 0) +#define LDAP_BOOL_SET(lo, bool) ((lo)->ldo_booleans |= LDAP_BOOL(bool)) +#define LDAP_BOOL_CLR(lo, bool) ((lo)->ldo_booleans &= ~LDAP_BOOL(bool)) +#define LDAP_BOOL_ZERO(lo) ((lo)->ldo_booleans = 0) + +/* + * This structure represents both ldap messages and ldap responses. + * These are really the same, except in the case of search responses, + * where a response has multiple messages. + */ + +struct ldapmsg { + ber_int_t lm_msgid; /* the message id */ + ber_tag_t lm_msgtype; /* the message type */ + BerElement *lm_ber; /* the ber encoded message contents */ + struct ldapmsg *lm_chain; /* for search - next msg in the resp */ + struct ldapmsg *lm_chain_tail; + struct ldapmsg *lm_next; /* next response */ + time_t lm_time; /* used to maintain cache */ +}; + +#ifdef HAVE_TLS +struct ldaptls { + char *lt_certfile; + char *lt_keyfile; + char *lt_dhfile; + char *lt_cacertfile; + char *lt_cacertdir; + char *lt_ciphersuite; + char *lt_crlfile; + char *lt_randfile; /* OpenSSL only */ + char *lt_ecname; /* OpenSSL only */ + int lt_protocol_min; +}; +#endif + +typedef struct ldaplist { + struct ldaplist *ll_next; + void *ll_data; +} ldaplist; + +/* + * structure representing get/set'able options + * which have global defaults. + * Protect access to this struct with ldo_mutex + * ldap_log.h:ldapoptions_prefix must match the head of this struct. + */ +struct ldapoptions { + short ldo_valid; +#define LDAP_UNINITIALIZED 0x0 +#define LDAP_INITIALIZED 0x1 +#define LDAP_VALID_SESSION 0x2 +#define LDAP_TRASHED_SESSION 0xFF + int ldo_debug; + + ber_int_t ldo_version; + ber_int_t ldo_deref; + ber_int_t ldo_timelimit; + ber_int_t ldo_sizelimit; + + /* per API call timeout */ + struct timeval ldo_tm_api; + struct timeval ldo_tm_net; + + LDAPURLDesc *ldo_defludp; + int ldo_defport; + char* ldo_defbase; + char* ldo_defbinddn; /* bind dn */ + + /* + * Per connection tcp-keepalive settings (Linux only, + * ignored where unsupported) + */ + ber_int_t ldo_keepalive_idle; + ber_int_t ldo_keepalive_probes; + ber_int_t ldo_keepalive_interval; + + int ldo_refhoplimit; /* limit on referral nesting */ + + /* LDAPv3 server and client controls */ + LDAPControl **ldo_sctrls; + LDAPControl **ldo_cctrls; + + /* LDAP rebind callback function */ + LDAP_REBIND_PROC *ldo_rebind_proc; + void *ldo_rebind_params; + LDAP_NEXTREF_PROC *ldo_nextref_proc; + void *ldo_nextref_params; + LDAP_URLLIST_PROC *ldo_urllist_proc; + void *ldo_urllist_params; + + /* LDAP connection callback stack */ + ldaplist *ldo_conn_cbs; + + LDAP_BOOLEANS ldo_booleans; /* boolean options */ + +#define LDAP_LDO_NULLARG ,0,0,0,0 ,{0},{0} ,0,0,0,0, 0,0,0,0, 0,0, 0,0,0,0,0,0, 0, 0 + +#ifdef LDAP_CONNECTIONLESS +#define LDAP_IS_UDP(ld) ((ld)->ld_options.ldo_is_udp) + void* ldo_peer; /* struct sockaddr* */ + char* ldo_cldapdn; + int ldo_is_udp; +#define LDAP_LDO_CONNECTIONLESS_NULLARG ,0,0,0 +#else +#define LDAP_LDO_CONNECTIONLESS_NULLARG +#endif + +#ifdef HAVE_TLS + /* tls context */ + void *ldo_tls_ctx; + LDAP_TLS_CONNECT_CB *ldo_tls_connect_cb; + void* ldo_tls_connect_arg; + struct ldaptls ldo_tls_info; +#define ldo_tls_certfile ldo_tls_info.lt_certfile +#define ldo_tls_keyfile ldo_tls_info.lt_keyfile +#define ldo_tls_dhfile ldo_tls_info.lt_dhfile +#define ldo_tls_ecname ldo_tls_info.lt_ecname +#define ldo_tls_cacertfile ldo_tls_info.lt_cacertfile +#define ldo_tls_cacertdir ldo_tls_info.lt_cacertdir +#define ldo_tls_ciphersuite ldo_tls_info.lt_ciphersuite +#define ldo_tls_protocol_min ldo_tls_info.lt_protocol_min +#define ldo_tls_crlfile ldo_tls_info.lt_crlfile +#define ldo_tls_randfile ldo_tls_info.lt_randfile + int ldo_tls_mode; + int ldo_tls_require_cert; + int ldo_tls_impl; + int ldo_tls_crlcheck; + int ldo_tls_require_san; +#define LDAP_LDO_TLS_NULLARG ,0,0,0,{0,0,0,0,0,0,0,0,0},0,0,0,0,0 +#else +#define LDAP_LDO_TLS_NULLARG +#endif + +#ifdef HAVE_CYRUS_SASL + char* ldo_def_sasl_mech; /* SASL Mechanism(s) */ + char* ldo_def_sasl_realm; /* SASL realm */ + char* ldo_def_sasl_authcid; /* SASL authentication identity */ + char* ldo_def_sasl_authzid; /* SASL authorization identity */ + + /* SASL Security Properties */ + struct sasl_security_properties ldo_sasl_secprops; +#define LDAP_LDO_SASL_NULLARG ,0,0,0,0,{0} +#else +#define LDAP_LDO_SASL_NULLARG +#endif + +#ifdef HAVE_GSSAPI + unsigned ldo_gssapi_flags; +#define LDAP_GSSAPI_OPT_DO_NOT_FREE_GSS_CONTEXT 0x0001 +#define LDAP_GSSAPI_OPT_ALLOW_REMOTE_PRINCIPAL 0x0002 + unsigned ldo_gssapi_options; +#define LDAP_LDO_GSSAPI_NULLARG ,0,0 +#else +#define LDAP_LDO_GSSAPI_NULLARG +#endif + +#ifdef LDAP_R_COMPILE + ldap_pvt_thread_mutex_t ldo_mutex; +#define LDAP_LDO_MUTEX_NULLARG , LDAP_PVT_MUTEX_NULL +#else +#define LDAP_LDO_MUTEX_NULLARG +#endif +}; + + +/* + * structure for representing an LDAP server connection + */ +typedef struct ldap_conn { + Sockbuf *lconn_sb; +#ifdef HAVE_CYRUS_SASL + void *lconn_sasl_authctx; /* context for bind */ + void *lconn_sasl_sockctx; /* for security layer */ +#endif +#ifdef HAVE_GSSAPI + void *lconn_gss_ctx; /* gss_ctx_id_t */ +#endif + int lconn_refcnt; + time_t lconn_created; /* time */ + time_t lconn_lastused; /* time */ + int lconn_rebind_inprogress; /* set if rebind in progress */ + char ***lconn_rebind_queue; /* used if rebind in progress */ + int lconn_status; +#define LDAP_CONNST_NEEDSOCKET 1 +#define LDAP_CONNST_CONNECTING 2 +#define LDAP_CONNST_CONNECTED 3 + LDAPURLDesc *lconn_server; + BerElement *lconn_ber; /* ber receiving on this conn. */ + + struct ldap_conn *lconn_next; +} LDAPConn; + + +/* + * structure used to track outstanding requests + */ +typedef struct ldapreq { + ber_int_t lr_msgid; /* the message id */ + int lr_status; /* status of request */ +#define LDAP_REQST_COMPLETED 0 +#define LDAP_REQST_INPROGRESS 1 +#define LDAP_REQST_CHASINGREFS 2 +#define LDAP_REQST_NOTCONNECTED 3 +#define LDAP_REQST_WRITING 4 + int lr_refcnt; /* count of references */ + int lr_outrefcnt; /* count of outstanding referrals */ + int lr_abandoned; /* the request has been abandoned */ + ber_int_t lr_origid; /* original request's message id */ + int lr_parentcnt; /* count of parent requests */ + ber_tag_t lr_res_msgtype; /* result message type */ + ber_int_t lr_res_errno; /* result LDAP errno */ + char *lr_res_error; /* result error string */ + char *lr_res_matched;/* result matched DN string */ + BerElement *lr_ber; /* ber encoded request contents */ + LDAPConn *lr_conn; /* connection used to send request */ + struct berval lr_dn; /* DN of request, in lr_ber */ + struct ldapreq *lr_parent; /* request that spawned this referral */ + struct ldapreq *lr_child; /* first child request */ + struct ldapreq *lr_refnext; /* next referral spawned */ + struct ldapreq *lr_prev; /* previous request */ + struct ldapreq *lr_next; /* next request */ +} LDAPRequest; + +/* + * structure for client cache + */ +#define LDAP_CACHE_BUCKETS 31 /* cache hash table size */ +typedef struct ldapcache { + LDAPMessage *lc_buckets[LDAP_CACHE_BUCKETS];/* hash table */ + LDAPMessage *lc_requests; /* unfulfilled reqs */ + long lc_timeout; /* request timeout */ + ber_len_t lc_maxmem; /* memory to use */ + ber_len_t lc_memused; /* memory in use */ + int lc_enabled; /* enabled? */ + unsigned long lc_options; /* options */ +#define LDAP_CACHE_OPT_CACHENOERRS 0x00000001 +#define LDAP_CACHE_OPT_CACHEALLERRS 0x00000002 +} LDAPCache; + +/* + * structure containing referral request info for rebind procedure + */ +typedef struct ldapreqinfo { + ber_len_t ri_msgid; + int ri_request; + char *ri_url; +} LDAPreqinfo; + +/* + * structure representing an ldap connection + */ + +struct ldap_common { + Sockbuf *ldc_sb; /* socket descriptor & buffer */ +#define ld_sb ldc->ldc_sb + + unsigned short ldc_lberoptions; +#define ld_lberoptions ldc->ldc_lberoptions + + /* protected by msgid_mutex */ + ber_len_t ldc_msgid; +#define ld_msgid ldc->ldc_msgid + + /* do not mess with these */ + /* protected by req_mutex */ + LDAPRequest *ldc_requests; /* list of outstanding requests */ + /* protected by res_mutex */ + LDAPMessage *ldc_responses; /* list of outstanding responses */ +#define ld_requests ldc->ldc_requests +#define ld_responses ldc->ldc_responses + + /* protected by abandon_mutex */ + ber_len_t ldc_nabandoned; + ber_int_t *ldc_abandoned; /* array of abandoned requests */ +#define ld_nabandoned ldc->ldc_nabandoned +#define ld_abandoned ldc->ldc_abandoned + + /* unused by libldap */ + LDAPCache *ldc_cache; /* non-null if cache is initialized */ +#define ld_cache ldc->ldc_cache + + /* do not mess with the rest though */ + + /* protected by conn_mutex */ + LDAPConn *ldc_defconn; /* default connection */ +#define ld_defconn ldc->ldc_defconn + LDAPConn *ldc_conns; /* list of server connections */ +#define ld_conns ldc->ldc_conns + void *ldc_selectinfo;/* platform specifics for select */ +#define ld_selectinfo ldc->ldc_selectinfo + + /* ldap_common refcnt - free only if 0 */ + /* protected by ldc_mutex */ + unsigned int ldc_refcnt; +#define ld_ldcrefcnt ldc->ldc_refcnt + + /* protected by ldo_mutex */ + struct ldapoptions ldc_options; +#define ld_options ldc->ldc_options + +#define ld_valid ld_options.ldo_valid +#define ld_debug ld_options.ldo_debug + +#define ld_deref ld_options.ldo_deref +#define ld_timelimit ld_options.ldo_timelimit +#define ld_sizelimit ld_options.ldo_sizelimit + +#define ld_defbinddn ld_options.ldo_defbinddn +#define ld_defbase ld_options.ldo_defbase +#define ld_defhost ld_options.ldo_defhost +#define ld_defport ld_options.ldo_defport + +#define ld_refhoplimit ld_options.ldo_refhoplimit + +#define ld_sctrls ld_options.ldo_sctrls +#define ld_cctrls ld_options.ldo_cctrls +#define ld_rebind_proc ld_options.ldo_rebind_proc +#define ld_rebind_params ld_options.ldo_rebind_params +#define ld_nextref_proc ld_options.ldo_nextref_proc +#define ld_nextref_params ld_options.ldo_nextref_params +#define ld_urllist_proc ld_options.ldo_urllist_proc +#define ld_urllist_params ld_options.ldo_urllist_params + +#define ld_version ld_options.ldo_version + +#ifdef LDAP_R_COMPILE + ldap_pvt_thread_mutex_t ldc_mutex; + ldap_pvt_thread_mutex_t ldc_msgid_mutex; + ldap_pvt_thread_mutex_t ldc_conn_mutex; + ldap_pvt_thread_mutex_t ldc_req_mutex; + ldap_pvt_thread_mutex_t ldc_res_mutex; + ldap_pvt_thread_mutex_t ldc_abandon_mutex; +#define ld_ldopts_mutex ld_options.ldo_mutex +#define ld_ldcmutex ldc->ldc_mutex +#define ld_msgid_mutex ldc->ldc_msgid_mutex +#define ld_conn_mutex ldc->ldc_conn_mutex +#define ld_req_mutex ldc->ldc_req_mutex +#define ld_res_mutex ldc->ldc_res_mutex +#define ld_abandon_mutex ldc->ldc_abandon_mutex +#endif +}; + +struct ldap { + /* thread shared */ + struct ldap_common *ldc; + + /* thread specific */ + ber_int_t ld_errno; + char *ld_error; + char *ld_matched; + char **ld_referrals; +}; + +#define LDAP_VALID(ld) ( (ld)->ld_valid == LDAP_VALID_SESSION ) +#define LDAP_TRASHED(ld) ( (ld)->ld_valid == LDAP_TRASHED_SESSION ) +#define LDAP_TRASH(ld) ( (ld)->ld_valid = LDAP_TRASHED_SESSION ) + +#ifdef LDAP_R_COMPILE +LDAP_V ( ldap_pvt_thread_mutex_t ) ldap_int_resolv_mutex; +LDAP_V ( ldap_pvt_thread_mutex_t ) ldap_int_hostname_mutex; + +#ifdef HAVE_GSSAPI +LDAP_V( ldap_pvt_thread_mutex_t ) ldap_int_gssapi_mutex; +#endif +#endif + +#ifdef LDAP_R_COMPILE +#define LDAP_MUTEX_LOCK(mutex) ldap_pvt_thread_mutex_lock( mutex ) +#define LDAP_MUTEX_UNLOCK(mutex) ldap_pvt_thread_mutex_unlock( mutex ) +#define LDAP_ASSERT_MUTEX_OWNER(mutex) \ + LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER(mutex) +#else +#define LDAP_MUTEX_LOCK(mutex) ((void) 0) +#define LDAP_MUTEX_UNLOCK(mutex) ((void) 0) +#define LDAP_ASSERT_MUTEX_OWNER(mutex) ((void) 0) +#endif + +#define LDAP_NEXT_MSGID(ld, id) do { \ + LDAP_MUTEX_LOCK( &(ld)->ld_msgid_mutex ); \ + (id) = ++(ld)->ld_msgid; \ + LDAP_MUTEX_UNLOCK( &(ld)->ld_msgid_mutex ); \ +} while (0) + +/* + * in abandon.c + */ + +LDAP_F (int) +ldap_int_bisect_find( ber_int_t *v, ber_len_t n, ber_int_t id, int *idxp ); +LDAP_F (int) +ldap_int_bisect_insert( ber_int_t **vp, ber_len_t *np, int id, int idx ); +LDAP_F (int) +ldap_int_bisect_delete( ber_int_t **vp, ber_len_t *np, int id, int idx ); + +/* + * in add.c + */ + +LDAP_F (BerElement *) ldap_build_add_req LDAP_P(( + LDAP *ld, + const char *dn, + LDAPMod **attrs, + LDAPControl **sctrls, + LDAPControl **cctrls, + ber_int_t *msgidp )); + +/* + * in compare.c + */ + +LDAP_F (BerElement *) ldap_build_compare_req LDAP_P(( + LDAP *ld, + const char *dn, + const char *attr, + struct berval *bvalue, + LDAPControl **sctrls, + LDAPControl **cctrls, + ber_int_t *msgidp )); + +/* + * in delete.c + */ + +LDAP_F (BerElement *) ldap_build_delete_req LDAP_P(( + LDAP *ld, + const char *dn, + LDAPControl **sctrls, + LDAPControl **cctrls, + ber_int_t *msgidp )); + +/* + * in extended.c + */ + +LDAP_F (BerElement *) ldap_build_extended_req LDAP_P(( + LDAP *ld, + const char *reqoid, + struct berval *reqdata, + LDAPControl **sctrls, + LDAPControl **cctrls, + ber_int_t *msgidp )); + +/* + * in init.c + */ + +LDAP_V ( struct ldapoptions ) ldap_int_global_options; + +LDAP_F ( void ) ldap_int_initialize LDAP_P((struct ldapoptions *, int *)); +LDAP_F ( void ) ldap_int_initialize_global_options LDAP_P(( + struct ldapoptions *, int *)); + +/* memory.c */ + /* simple macros to realloc for now */ +#define LDAP_MALLOC(s) (ber_memalloc_x((s),NULL)) +#define LDAP_CALLOC(n,s) (ber_memcalloc_x((n),(s),NULL)) +#define LDAP_REALLOC(p,s) (ber_memrealloc_x((p),(s),NULL)) +#define LDAP_FREE(p) (ber_memfree_x((p),NULL)) +#define LDAP_VFREE(v) (ber_memvfree_x((void **)(v),NULL)) +#define LDAP_STRDUP(s) (ber_strdup_x((s),NULL)) +#define LDAP_STRNDUP(s,l) (ber_strndup_x((s),(l),NULL)) + +#define LDAP_MALLOCX(s,x) (ber_memalloc_x((s),(x))) +#define LDAP_CALLOCX(n,s,x) (ber_memcalloc_x((n),(s),(x))) +#define LDAP_REALLOCX(p,s,x) (ber_memrealloc_x((p),(s),(x))) +#define LDAP_FREEX(p,x) (ber_memfree_x((p),(x))) +#define LDAP_VFREEX(v,x) (ber_memvfree_x((void **)(v),(x))) +#define LDAP_STRDUPX(s,x) (ber_strdup_x((s),(x))) +#define LDAP_STRNDUPX(s,l,x) (ber_strndup_x((s),(l),(x))) + +/* + * in error.c + */ +LDAP_F (void) ldap_int_error_init( void ); + +/* + * in modify.c + */ + +LDAP_F (BerElement *) ldap_build_modify_req LDAP_P(( + LDAP *ld, + const char *dn, + LDAPMod **mods, + LDAPControl **sctrls, + LDAPControl **cctrls, + ber_int_t *msgidp )); + +/* + * in modrdn.c + */ + +LDAP_F (BerElement *) ldap_build_moddn_req LDAP_P(( + LDAP *ld, + const char *dn, + const char *newrdn, + const char *newSuperior, + int deleteoldrdn, + LDAPControl **sctrls, + LDAPControl **cctrls, + ber_int_t *msgidp )); + +/* + * in unit-int.c + */ +LDAP_F (void) ldap_int_utils_init LDAP_P(( void )); + + +/* + * in print.c + */ +LDAP_F (int) ldap_log_printf LDAP_P((LDAP *ld, int level, const char *fmt, ...)) LDAP_GCCATTR((format(printf, 3, 4))); + +/* + * in cache.c + */ +LDAP_F (void) ldap_add_request_to_cache LDAP_P(( LDAP *ld, ber_tag_t msgtype, + BerElement *request )); +LDAP_F (void) ldap_add_result_to_cache LDAP_P(( LDAP *ld, LDAPMessage *result )); +LDAP_F (int) ldap_check_cache LDAP_P(( LDAP *ld, ber_tag_t msgtype, BerElement *request )); + +/* + * in controls.c + */ +LDAP_F (int) ldap_int_put_controls LDAP_P(( + LDAP *ld, + LDAPControl *const *ctrls, + BerElement *ber )); + +LDAP_F (int) ldap_int_client_controls LDAP_P(( + LDAP *ld, + LDAPControl **ctrlp )); + +/* + * in dsparse.c + */ +LDAP_F (int) ldap_int_next_line_tokens LDAP_P(( char **bufp, ber_len_t *blenp, char ***toksp )); + + +/* + * in open.c + */ +LDAP_F (int) ldap_open_defconn( LDAP *ld ); +LDAP_F (int) ldap_int_open_connection( LDAP *ld, + LDAPConn *conn, LDAPURLDesc *srvlist, int async ); +LDAP_F (int) ldap_int_check_async_open( LDAP *ld, ber_socket_t sd ); + +/* + * in os-ip.c + */ +#ifndef HAVE_POLL +LDAP_V (int) ldap_int_tblsize; +LDAP_F (void) ldap_int_ip_init( void ); +#endif + +LDAP_F (int) ldap_int_timeval_dup( struct timeval **dest, + const struct timeval *tm ); +LDAP_F (int) ldap_connect_to_host( LDAP *ld, Sockbuf *sb, + int proto, LDAPURLDesc *srv, int async ); +LDAP_F (int) ldap_int_poll( LDAP *ld, ber_socket_t s, + struct timeval *tvp, int wr ); + +#if defined(HAVE_TLS) || defined(HAVE_CYRUS_SASL) +LDAP_V (char *) ldap_int_hostname; +LDAP_F (char *) ldap_host_connected_to( Sockbuf *sb, + const char *host ); +#endif + +LDAP_F (int) ldap_int_select( LDAP *ld, struct timeval *timeout ); +LDAP_F (void *) ldap_new_select_info( void ); +LDAP_F (void) ldap_free_select_info( void *sip ); +LDAP_F (void) ldap_mark_select_write( LDAP *ld, Sockbuf *sb ); +LDAP_F (void) ldap_mark_select_read( LDAP *ld, Sockbuf *sb ); +LDAP_F (void) ldap_mark_select_clear( LDAP *ld, Sockbuf *sb ); +LDAP_F (void) ldap_clear_select_write( LDAP *ld, Sockbuf *sb ); +LDAP_F (int) ldap_is_read_ready( LDAP *ld, Sockbuf *sb ); +LDAP_F (int) ldap_is_write_ready( LDAP *ld, Sockbuf *sb ); + +LDAP_F (int) ldap_int_connect_cbs( LDAP *ld, Sockbuf *sb, + ber_socket_t *s, LDAPURLDesc *srv, struct sockaddr *addr ); + +/* + * in os-local.c + */ +#ifdef LDAP_PF_LOCAL +LDAP_F (int) ldap_connect_to_path( LDAP *ld, Sockbuf *sb, + LDAPURLDesc *srv, int async ); +#endif /* LDAP_PF_LOCAL */ + +/* + * in request.c + */ +LDAP_F (ber_int_t) ldap_send_initial_request( LDAP *ld, ber_tag_t msgtype, + const char *dn, BerElement *ber, ber_int_t msgid ); +LDAP_F (BerElement *) ldap_alloc_ber_with_options( LDAP *ld ); +LDAP_F (void) ldap_set_ber_options( LDAP *ld, BerElement *ber ); + +LDAP_F (int) ldap_send_server_request( LDAP *ld, BerElement *ber, + ber_int_t msgid, LDAPRequest *parentreq, LDAPURLDesc **srvlist, + LDAPConn *lc, LDAPreqinfo *bind, int noconn, int m_res ); +LDAP_F (LDAPConn *) ldap_new_connection( LDAP *ld, LDAPURLDesc **srvlist, + int use_ldsb, int connect, LDAPreqinfo *bind, int m_req, int m_res ); +LDAP_F (LDAPRequest *) ldap_find_request_by_msgid( LDAP *ld, ber_int_t msgid ); +LDAP_F (void) ldap_return_request( LDAP *ld, LDAPRequest *lr, int freeit ); +LDAP_F (void) ldap_free_request( LDAP *ld, LDAPRequest *lr ); +LDAP_F (void) ldap_free_connection( LDAP *ld, LDAPConn *lc, int force, int unbind ); +LDAP_F (void) ldap_dump_connection( LDAP *ld, LDAPConn *lconns, int all ); +LDAP_F (void) ldap_dump_requests_and_responses( LDAP *ld ); +LDAP_F (int) ldap_chase_referrals( LDAP *ld, LDAPRequest *lr, + char **errstrp, int sref, int *hadrefp ); +LDAP_F (int) ldap_chase_v3referrals( LDAP *ld, LDAPRequest *lr, + char **refs, int sref, char **referralsp, int *hadrefp ); +LDAP_F (int) ldap_append_referral( LDAP *ld, char **referralsp, char *s ); +LDAP_F (int) ldap_int_flush_request( LDAP *ld, LDAPRequest *lr ); + +/* + * in result.c: + */ +LDAP_F (const char *) ldap_int_msgtype2str( ber_tag_t tag ); + +/* + * in search.c + */ +LDAP_F (BerElement *) ldap_build_search_req LDAP_P(( + LDAP *ld, + const char *base, + ber_int_t scope, + const char *filter, + char **attrs, + ber_int_t attrsonly, + LDAPControl **sctrls, + LDAPControl **cctrls, + ber_int_t timelimit, + ber_int_t sizelimit, + ber_int_t deref, + ber_int_t *msgidp)); + + +/* + * in unbind.c + */ +LDAP_F (int) ldap_ld_free LDAP_P(( + LDAP *ld, + int close, + LDAPControl **sctrls, + LDAPControl **cctrls )); + +LDAP_F (int) ldap_send_unbind LDAP_P(( + LDAP *ld, + Sockbuf *sb, + LDAPControl **sctrls, + LDAPControl **cctrls )); + +/* + * in url.c + */ +LDAP_F (LDAPURLDesc *) ldap_url_dup LDAP_P(( + LDAPURLDesc *ludp )); + +LDAP_F (LDAPURLDesc *) ldap_url_duplist LDAP_P(( + LDAPURLDesc *ludlist )); + +LDAP_F (int) ldap_url_parsehosts LDAP_P(( + LDAPURLDesc **ludlist, + const char *hosts, + int port )); + +LDAP_F (char *) ldap_url_list2hosts LDAP_P(( + LDAPURLDesc *ludlist )); + +/* + * in cyrus.c + */ + +LDAP_F (int) ldap_int_sasl_init LDAP_P(( void )); + +LDAP_F (int) ldap_int_sasl_open LDAP_P(( + LDAP *ld, LDAPConn *conn, + const char* host )); +LDAP_F (int) ldap_int_sasl_close LDAP_P(( LDAP *ld, LDAPConn *conn )); + +LDAP_F (int) ldap_int_sasl_external LDAP_P(( + LDAP *ld, LDAPConn *conn, + const char* authid, ber_len_t ssf )); + +LDAP_F (int) ldap_int_sasl_get_option LDAP_P(( LDAP *ld, + int option, void *arg )); +LDAP_F (int) ldap_int_sasl_set_option LDAP_P(( LDAP *ld, + int option, void *arg )); +LDAP_F (int) ldap_int_sasl_config LDAP_P(( struct ldapoptions *lo, + int option, const char *arg )); + +LDAP_F (int) ldap_int_sasl_bind LDAP_P(( + LDAP *ld, + const char *, + const char *, + LDAPControl **, LDAPControl **, + + /* should be passed in client controls */ + unsigned flags, + LDAP_SASL_INTERACT_PROC *interact, + void *defaults, + LDAPMessage *result, + const char **rmech, + int *msgid )); + +/* in sasl.c */ + +LDAP_F (BerElement *) ldap_build_bind_req LDAP_P(( + LDAP *ld, + const char *dn, + const char *mech, + struct berval *cred, + LDAPControl **sctrls, + LDAPControl **cctrls, + ber_int_t *msgidp )); + +/* in schema.c */ +LDAP_F (char *) ldap_int_parse_numericoid LDAP_P(( + const char **sp, + int *code, + const int flags )); + +/* + * in tls.c + */ +LDAP_F (int) ldap_int_tls_config LDAP_P(( LDAP *ld, + int option, const char *arg )); + +LDAP_F (int) ldap_int_tls_start LDAP_P(( LDAP *ld, + LDAPConn *conn, LDAPURLDesc *srv )); + +LDAP_F (void) ldap_int_tls_destroy LDAP_P(( struct ldapoptions *lo )); + +/* + * in getvalues.c + */ +LDAP_F (char **) ldap_value_dup LDAP_P(( + char *const *vals )); + +/* + * in gssapi.c + */ +#ifdef HAVE_GSSAPI +LDAP_F(int) ldap_int_gssapi_get_option LDAP_P(( LDAP *ld, int option, void *arg )); +LDAP_F(int) ldap_int_gssapi_set_option LDAP_P(( LDAP *ld, int option, void *arg )); +LDAP_F(int) ldap_int_gssapi_config LDAP_P(( struct ldapoptions *lo, int option, const char *arg )); +LDAP_F(void) ldap_int_gssapi_close LDAP_P(( LDAP *ld, LDAPConn *lc )); +#endif + +LDAP_END_DECL + +#endif /* _LDAP_INT_H */ diff --git a/libraries/libldap/ldap-tls.h b/libraries/libldap/ldap-tls.h new file mode 100644 index 0000000..c8a2711 --- /dev/null +++ b/libraries/libldap/ldap-tls.h @@ -0,0 +1,77 @@ +/* ldap-tls.h - TLS defines & prototypes internal to the LDAP library */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2008-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#ifndef _LDAP_TLS_H +#define _LDAP_TLS_H 1 + +struct tls_impl; + +struct tls_ctx; +struct tls_session; + +typedef struct tls_ctx tls_ctx; +typedef struct tls_session tls_session; + +typedef int (TI_tls_init)(void); +typedef void (TI_tls_destroy)(void); + +typedef tls_ctx *(TI_ctx_new)(struct ldapoptions *lo); +typedef void (TI_ctx_ref)(tls_ctx *ctx); +typedef void (TI_ctx_free)(tls_ctx *ctx); +typedef int (TI_ctx_init)(struct ldapoptions *lo, struct ldaptls *lt, int is_server); + +typedef tls_session *(TI_session_new)(tls_ctx *ctx, int is_server); +typedef int (TI_session_connect)(LDAP *ld, tls_session *s); +typedef int (TI_session_accept)(tls_session *s); +typedef int (TI_session_upflags)(Sockbuf *sb, tls_session *s, int rc); +typedef char *(TI_session_errmsg)(tls_session *s, int rc, char *buf, size_t len ); +typedef int (TI_session_dn)(tls_session *sess, struct berval *dn); +typedef int (TI_session_chkhost)(LDAP *ld, tls_session *s, const char *name_in); +typedef int (TI_session_strength)(tls_session *sess); + +typedef void (TI_thr_init)(void); + +typedef struct tls_impl { + const char *ti_name; + + TI_tls_init *ti_tls_init; /* library initialization */ + TI_tls_destroy *ti_tls_destroy; + + TI_ctx_new *ti_ctx_new; + TI_ctx_ref *ti_ctx_ref; + TI_ctx_free *ti_ctx_free; + TI_ctx_init *ti_ctx_init; + + TI_session_new *ti_session_new; + TI_session_connect *ti_session_connect; + TI_session_accept *ti_session_accept; + TI_session_upflags *ti_session_upflags; + TI_session_errmsg *ti_session_errmsg; + TI_session_dn *ti_session_my_dn; + TI_session_dn *ti_session_peer_dn; + TI_session_chkhost *ti_session_chkhost; + TI_session_strength *ti_session_strength; + + Sockbuf_IO *ti_sbio; + + TI_thr_init *ti_thr_init; + + int ti_inited; +} tls_impl; + +extern tls_impl ldap_int_tls_impl; + +#endif /* _LDAP_TLS_H */ diff --git a/libraries/libldap/ldap.conf b/libraries/libldap/ldap.conf new file mode 100644 index 0000000..af738ad --- /dev/null +++ b/libraries/libldap/ldap.conf @@ -0,0 +1,13 @@ +# +# LDAP Defaults +# + +# See ldap.conf(5) for details +# This file should be world readable but not world writable. + +#BASE dc=example,dc=com +#URI ldap://ldap.example.com ldap://ldap-provider.example.com:666 + +#SIZELIMIT 12 +#TIMELIMIT 15 +#DEREF never diff --git a/libraries/libldap/ldap_sync.c b/libraries/libldap/ldap_sync.c new file mode 100644 index 0000000..b5ac207 --- /dev/null +++ b/libraries/libldap/ldap_sync.c @@ -0,0 +1,928 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2006-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: + * This program was originally developed by Pierangelo Masarati + * for inclusion in OpenLDAP Software. + */ + +/* + * Proof-of-concept API that implement the client-side + * of the "LDAP Content Sync Operation" (RFC 4533) + */ + +#include "portable.h" + +#include <ac/time.h> + +#include "ldap-int.h" + +#ifdef LDAP_SYNC_TRACE +static const char * +ldap_sync_state2str( int state ) +{ + switch ( state ) { + case LDAP_SYNC_PRESENT: + return "LDAP_SYNC_PRESENT"; + + case LDAP_SYNC_ADD: + return "LDAP_SYNC_ADD"; + + case LDAP_SYNC_MODIFY: + return "LDAP_SYNC_MODIFY"; + + case LDAP_SYNC_DELETE: + return "LDAP_SYNC_DELETE"; + + default: + return "(unknown)"; + } +} +#endif + +/* + * initialize the persistent search structure + */ +ldap_sync_t * +ldap_sync_initialize( ldap_sync_t *ls_in ) +{ + ldap_sync_t *ls = ls_in; + + if ( ls == NULL ) { + ls = ldap_memalloc( sizeof( ldap_sync_t ) ); + if ( ls == NULL ) { + return NULL; + } + } + memset( ls, 0, sizeof( ldap_sync_t ) ); + + ls->ls_scope = LDAP_SCOPE_SUBTREE; + ls->ls_timeout = -1; + + return ls; +} + +/* + * destroy the persistent search structure + */ +void +ldap_sync_destroy( ldap_sync_t *ls, int freeit ) +{ + assert( ls != NULL ); + + if ( ls->ls_base != NULL ) { + ldap_memfree( ls->ls_base ); + ls->ls_base = NULL; + } + + if ( ls->ls_filter != NULL ) { + ldap_memfree( ls->ls_filter ); + ls->ls_filter = NULL; + } + + if ( ls->ls_attrs != NULL ) { + int i; + + for ( i = 0; ls->ls_attrs[ i ] != NULL; i++ ) { + ldap_memfree( ls->ls_attrs[ i ] ); + } + ldap_memfree( ls->ls_attrs ); + ls->ls_attrs = NULL; + } + + if ( ls->ls_ld != NULL ) { + (void)ldap_unbind_ext( ls->ls_ld, NULL, NULL ); +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, "ldap_unbind_ext()\n" ); +#endif /* LDAP_SYNC_TRACE */ + ls->ls_ld = NULL; + } + + if ( ls->ls_cookie.bv_val != NULL ) { + ldap_memfree( ls->ls_cookie.bv_val ); + ls->ls_cookie.bv_val = NULL; + } + + if ( freeit ) { + ldap_memfree( ls ); + } +} + +/* + * handle the LDAP_RES_SEARCH_ENTRY response + */ +static int +ldap_sync_search_entry( ldap_sync_t *ls, LDAPMessage *res ) +{ + LDAPControl **ctrls = NULL; + int rc = LDAP_OTHER, + i; + BerElement *ber = NULL; + struct berval entryUUID = { 0 }, + cookie = { 0 }; + int state = -1; + ber_len_t len; + ldap_sync_refresh_t phase; + +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, "\tgot LDAP_RES_SEARCH_ENTRY\n" ); +#endif /* LDAP_SYNC_TRACE */ + + assert( ls != NULL ); + assert( res != NULL ); + + phase = ls->ls_refreshPhase; + + /* OK */ + + /* extract: + * - data + * - entryUUID + * + * check that: + * - Sync State Control is "add" + */ + + /* the control MUST be present */ + + /* extract controls */ + ldap_get_entry_controls( ls->ls_ld, res, &ctrls ); + if ( ctrls == NULL ) { + goto done; + } + + /* lookup the sync state control */ + for ( i = 0; ctrls[ i ] != NULL; i++ ) { + if ( strcmp( ctrls[ i ]->ldctl_oid, LDAP_CONTROL_SYNC_STATE ) == 0 ) { + break; + } + } + + /* control must be present; there might be other... */ + if ( ctrls[ i ] == NULL ) { + goto done; + } + + /* extract data */ + ber = ber_init( &ctrls[ i ]->ldctl_value ); + if ( ber == NULL ) { + goto done; + } + /* scan entryUUID in-place ("m") */ + if ( ber_scanf( ber, "{em" /*"}"*/, &state, &entryUUID ) == LBER_ERROR + || entryUUID.bv_len == 0 ) + { + goto done; + } + + if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SYNC_COOKIE ) { + /* scan cookie in-place ("m") */ + if ( ber_scanf( ber, /*"{"*/ "m}", &cookie ) == LBER_ERROR ) { + goto done; + } + if ( cookie.bv_val != NULL ) { + ber_bvreplace( &ls->ls_cookie, &cookie ); + } +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, "\t\tgot cookie=%s\n", + cookie.bv_val ? cookie.bv_val : "(null)" ); +#endif /* LDAP_SYNC_TRACE */ + } + + switch ( state ) { + case LDAP_SYNC_PRESENT: + case LDAP_SYNC_DELETE: + case LDAP_SYNC_ADD: + case LDAP_SYNC_MODIFY: + /* NOTE: ldap_sync_refresh_t is defined + * as the corresponding LDAP_SYNC_* + * for the 4 above cases */ + phase = state; +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, "\t\tgot syncState=%s\n", ldap_sync_state2str( state ) ); +#endif /* LDAP_SYNC_TRACE */ + break; + + default: +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, "\t\tgot unknown syncState=%d\n", state ); +#endif /* LDAP_SYNC_TRACE */ + goto done; + } + + rc = ls->ls_search_entry + ? ls->ls_search_entry( ls, res, &entryUUID, phase ) + : LDAP_SUCCESS; + +done:; + if ( ber != NULL ) { + ber_free( ber, 1 ); + } + + if ( ctrls != NULL ) { + ldap_controls_free( ctrls ); + } + + return rc; +} + +/* + * handle the LDAP_RES_SEARCH_REFERENCE response + * (to be implemented yet) + */ +static int +ldap_sync_search_reference( ldap_sync_t *ls, LDAPMessage *res ) +{ + int rc = 0; + +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, "\tgot LDAP_RES_SEARCH_REFERENCE\n" ); +#endif /* LDAP_SYNC_TRACE */ + + assert( ls != NULL ); + assert( res != NULL ); + + if ( ls->ls_search_reference ) { + rc = ls->ls_search_reference( ls, res ); + } + + return rc; +} + +/* + * handle the LDAP_RES_SEARCH_RESULT response + */ +static int +ldap_sync_search_result( ldap_sync_t *ls, LDAPMessage *res ) +{ + int err; + char *matched = NULL, + *msg = NULL; + LDAPControl **ctrls = NULL; + int rc; + int refreshDeletes = -1; + +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, "\tgot LDAP_RES_SEARCH_RESULT\n" ); +#endif /* LDAP_SYNC_TRACE */ + + assert( ls != NULL ); + assert( res != NULL ); + + /* should not happen in refreshAndPersist... */ + rc = ldap_parse_result( ls->ls_ld, + res, &err, &matched, &msg, NULL, &ctrls, 0 ); +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, + "\tldap_parse_result(%d, \"%s\", \"%s\") == %d\n", + err, + matched ? matched : "", + msg ? msg : "", + rc ); +#endif /* LDAP_SYNC_TRACE */ + if ( rc == LDAP_SUCCESS ) { + rc = err; + } + + ls->ls_refreshPhase = LDAP_SYNC_CAPI_DONE; + + switch ( rc ) { + case LDAP_SUCCESS: { + int i; + BerElement *ber = NULL; + ber_len_t len; + struct berval cookie = { 0 }; + + rc = LDAP_OTHER; + + /* deal with control; then fallthru to handler */ + if ( ctrls == NULL ) { + goto done; + } + + /* lookup the sync state control */ + for ( i = 0; ctrls[ i ] != NULL; i++ ) { + if ( strcmp( ctrls[ i ]->ldctl_oid, + LDAP_CONTROL_SYNC_DONE ) == 0 ) + { + break; + } + } + + /* control must be present; there might be other... */ + if ( ctrls[ i ] == NULL ) { + goto done; + } + + /* extract data */ + ber = ber_init( &ctrls[ i ]->ldctl_value ); + if ( ber == NULL ) { + goto done; + } + + if ( ber_scanf( ber, "{" /*"}"*/) == LBER_ERROR ) { + goto ber_done; + } + if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SYNC_COOKIE ) { + if ( ber_scanf( ber, "m", &cookie ) == LBER_ERROR ) { + goto ber_done; + } + if ( cookie.bv_val != NULL ) { + ber_bvreplace( &ls->ls_cookie, &cookie ); + } +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, "\t\tgot cookie=%s\n", + cookie.bv_val ? cookie.bv_val : "(null)" ); +#endif /* LDAP_SYNC_TRACE */ + } + + refreshDeletes = 0; + if ( ber_peek_tag( ber, &len ) == LDAP_TAG_REFRESHDELETES ) { + if ( ber_scanf( ber, "b", &refreshDeletes ) == LBER_ERROR ) { + goto ber_done; + } + if ( refreshDeletes ) { + refreshDeletes = 1; + } + } + + if ( ber_scanf( ber, /*"{"*/ "}" ) != LBER_ERROR ) { + rc = LDAP_SUCCESS; + } + + ber_done:; + ber_free( ber, 1 ); + if ( rc != LDAP_SUCCESS ) { + break; + } + +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, "\t\tgot refreshDeletes=%s\n", + refreshDeletes ? "TRUE" : "FALSE" ); +#endif /* LDAP_SYNC_TRACE */ + + /* FIXME: what should we do with the refreshDelete? */ + switch ( refreshDeletes ) { + case 0: + ls->ls_refreshPhase = LDAP_SYNC_CAPI_PRESENTS; + break; + + default: + ls->ls_refreshPhase = LDAP_SYNC_CAPI_DELETES; + break; + } + + } /* fallthru */ + + case LDAP_SYNC_REFRESH_REQUIRED: + /* TODO: check for Sync Done Control */ + /* FIXME: perhaps the handler should be called + * also in case of failure; we'll deal with this + * later when implementing refreshOnly */ + if ( ls->ls_search_result ) { + err = ls->ls_search_result( ls, res, refreshDeletes ); + } + break; + } + +done:; + if ( matched != NULL ) { + ldap_memfree( matched ); + } + + if ( msg != NULL ) { + ldap_memfree( msg ); + } + + if ( ctrls != NULL ) { + ldap_controls_free( ctrls ); + } + + ls->ls_refreshPhase = LDAP_SYNC_CAPI_DONE; + + return rc; +} + +/* + * handle the LDAP_RES_INTERMEDIATE response + */ +static int +ldap_sync_search_intermediate( ldap_sync_t *ls, LDAPMessage *res, int *refreshDone ) +{ + int rc; + char *retoid = NULL; + struct berval *retdata = NULL; + BerElement *ber = NULL; + ber_len_t len; + ber_tag_t syncinfo_tag; + struct berval cookie; + int refreshDeletes = 0; + BerVarray syncUUIDs = NULL; + ldap_sync_refresh_t phase; + +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, "\tgot LDAP_RES_INTERMEDIATE\n" ); +#endif /* LDAP_SYNC_TRACE */ + + assert( ls != NULL ); + assert( res != NULL ); + assert( refreshDone != NULL ); + + *refreshDone = 0; + + rc = ldap_parse_intermediate( ls->ls_ld, res, + &retoid, &retdata, NULL, 0 ); +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, "\t%sldap_parse_intermediate(%s) == %d\n", + rc != LDAP_SUCCESS ? "!!! " : "", + retoid == NULL ? "\"\"" : retoid, + rc ); +#endif /* LDAP_SYNC_TRACE */ + /* parsing must be successful, and yield the OID + * of the sync info intermediate response */ + if ( rc != LDAP_SUCCESS ) { + goto done; + } + + rc = LDAP_OTHER; + + if ( retoid == NULL || strcmp( retoid, LDAP_SYNC_INFO ) != 0 ) { + goto done; + } + + /* init ber using the value in the response */ + ber = ber_init( retdata ); + if ( ber == NULL ) { + goto done; + } + + syncinfo_tag = ber_peek_tag( ber, &len ); + switch ( syncinfo_tag ) { + case LDAP_TAG_SYNC_NEW_COOKIE: + if ( ber_scanf( ber, "m", &cookie ) == LBER_ERROR ) { + goto done; + } + if ( cookie.bv_val != NULL ) { + ber_bvreplace( &ls->ls_cookie, &cookie ); + } +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, "\t\tgot cookie=%s\n", + cookie.bv_val ? cookie.bv_val : "(null)" ); +#endif /* LDAP_SYNC_TRACE */ + break; + + case LDAP_TAG_SYNC_REFRESH_DELETE: + case LDAP_TAG_SYNC_REFRESH_PRESENT: + if ( syncinfo_tag == LDAP_TAG_SYNC_REFRESH_DELETE ) { +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, "\t\tgot refreshDelete\n" ); +#endif /* LDAP_SYNC_TRACE */ + switch ( ls->ls_refreshPhase ) { + case LDAP_SYNC_CAPI_NONE: + case LDAP_SYNC_CAPI_PRESENTS: + ls->ls_refreshPhase = LDAP_SYNC_CAPI_DELETES; + break; + + default: + /* TODO: impossible; handle */ + goto done; + } + + } else { +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, "\t\tgot refreshPresent\n" ); +#endif /* LDAP_SYNC_TRACE */ + switch ( ls->ls_refreshPhase ) { + case LDAP_SYNC_CAPI_NONE: + ls->ls_refreshPhase = LDAP_SYNC_CAPI_PRESENTS; + break; + + default: + /* TODO: impossible; handle */ + goto done; + } + } + + if ( ber_scanf( ber, "{" /*"}"*/ ) == LBER_ERROR ) { + goto done; + } + if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SYNC_COOKIE ) { + if ( ber_scanf( ber, "m", &cookie ) == LBER_ERROR ) { + goto done; + } + if ( cookie.bv_val != NULL ) { + ber_bvreplace( &ls->ls_cookie, &cookie ); + } +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, "\t\tgot cookie=%s\n", + cookie.bv_val ? cookie.bv_val : "(null)" ); +#endif /* LDAP_SYNC_TRACE */ + } + + *refreshDone = 1; + if ( ber_peek_tag( ber, &len ) == LDAP_TAG_REFRESHDONE ) { + if ( ber_scanf( ber, "b", refreshDone ) == LBER_ERROR ) { + goto done; + } + } + +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, "\t\tgot refreshDone=%s\n", + *refreshDone ? "TRUE" : "FALSE" ); +#endif /* LDAP_SYNC_TRACE */ + + if ( ber_scanf( ber, /*"{"*/ "}" ) == LBER_ERROR ) { + goto done; + } + + if ( *refreshDone ) { + ls->ls_refreshPhase = LDAP_SYNC_CAPI_DONE; + } + + if ( ls->ls_intermediate ) { + ls->ls_intermediate( ls, res, NULL, ls->ls_refreshPhase ); + } + + break; + + case LDAP_TAG_SYNC_ID_SET: +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, "\t\tgot syncIdSet\n" ); +#endif /* LDAP_SYNC_TRACE */ + if ( ber_scanf( ber, "{" /*"}"*/ ) == LBER_ERROR ) { + goto done; + } + if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SYNC_COOKIE ) { + if ( ber_scanf( ber, "m", &cookie ) == LBER_ERROR ) { + goto done; + } + if ( cookie.bv_val != NULL ) { + ber_bvreplace( &ls->ls_cookie, &cookie ); + } +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, "\t\tgot cookie=%s\n", + cookie.bv_val ? cookie.bv_val : "(null)" ); +#endif /* LDAP_SYNC_TRACE */ + } + + if ( ber_peek_tag( ber, &len ) == LDAP_TAG_REFRESHDELETES ) { + if ( ber_scanf( ber, "b", &refreshDeletes ) == LBER_ERROR ) { + goto done; + } + } + + if ( ber_scanf( ber, /*"{"*/ "[W]}", &syncUUIDs ) == LBER_ERROR + || syncUUIDs == NULL ) + { + goto done; + } + +#ifdef LDAP_SYNC_TRACE + { + int i; + + fprintf( stderr, "\t\tgot refreshDeletes=%s\n", + refreshDeletes ? "TRUE" : "FALSE" ); + for ( i = 0; syncUUIDs[ i ].bv_val != NULL; i++ ) { + char buf[ BUFSIZ ]; + fprintf( stderr, "\t\t%s\n", + lutil_uuidstr_from_normalized( + syncUUIDs[ i ].bv_val, syncUUIDs[ i ].bv_len, + buf, sizeof( buf ) ) ); + } + } +#endif /* LDAP_SYNC_TRACE */ + + if ( refreshDeletes ) { + phase = LDAP_SYNC_CAPI_DELETES_IDSET; + + } else { + phase = LDAP_SYNC_CAPI_PRESENTS_IDSET; + } + + /* FIXME: should touch ls->ls_refreshPhase? */ + if ( ls->ls_intermediate ) { + ls->ls_intermediate( ls, res, syncUUIDs, phase ); + } + + ber_bvarray_free( syncUUIDs ); + break; + + default: +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, "\t\tunknown tag!\n" ); +#endif /* LDAP_SYNC_TRACE */ + goto done; + } + + rc = LDAP_SUCCESS; + +done:; + if ( ber != NULL ) { + ber_free( ber, 1 ); + } + + if ( retoid != NULL ) { + ldap_memfree( retoid ); + } + + if ( retdata != NULL ) { + ber_bvfree( retdata ); + } + + return rc; +} + +/* + * initialize the sync + */ +int +ldap_sync_init( ldap_sync_t *ls, int mode ) +{ + LDAPControl ctrl = { 0 }, + *ctrls[ 2 ]; + BerElement *ber = NULL; + int rc; + struct timeval tv = { 0 }, + *tvp = NULL; + LDAPMessage *res = NULL; + +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, "ldap_sync_init(%s)...\n", + mode == LDAP_SYNC_REFRESH_AND_PERSIST ? + "LDAP_SYNC_REFRESH_AND_PERSIST" : + ( mode == LDAP_SYNC_REFRESH_ONLY ? + "LDAP_SYNC_REFRESH_ONLY" : "unknown" ) ); +#endif /* LDAP_SYNC_TRACE */ + + assert( ls != NULL ); + assert( ls->ls_ld != NULL ); + + /* support both refreshOnly and refreshAndPersist */ + switch ( mode ) { + case LDAP_SYNC_REFRESH_AND_PERSIST: + case LDAP_SYNC_REFRESH_ONLY: + break; + + default: + fprintf( stderr, "ldap_sync_init: unknown mode=%d\n", mode ); + return LDAP_PARAM_ERROR; + } + + /* check consistency of cookie and reloadHint at initial refresh */ + if ( ls->ls_cookie.bv_val == NULL && ls->ls_reloadHint != 0 ) { + fprintf( stderr, "ldap_sync_init: inconsistent cookie/rhint\n" ); + return LDAP_PARAM_ERROR; + } + + ctrls[ 0 ] = &ctrl; + ctrls[ 1 ] = NULL; + + /* prepare the Sync Request control */ + ber = ber_alloc_t( LBER_USE_DER ); +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, "%sber_alloc_t() %s= NULL\n", + ber == NULL ? "!!! " : "", + ber == NULL ? "=" : "!" ); +#endif /* LDAP_SYNC_TRACE */ + if ( ber == NULL ) { + rc = LDAP_NO_MEMORY; + goto done; + } + + ls->ls_refreshPhase = LDAP_SYNC_CAPI_NONE; + + if ( ls->ls_cookie.bv_val != NULL ) { + ber_printf( ber, "{eOb}", mode, + &ls->ls_cookie, ls->ls_reloadHint ); + + } else { + ber_printf( ber, "{eb}", mode, ls->ls_reloadHint ); + } + + rc = ber_flatten2( ber, &ctrl.ldctl_value, 0 ); +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, + "%sber_flatten2() == %d\n", + rc ? "!!! " : "", + rc ); +#endif /* LDAP_SYNC_TRACE */ + if ( rc < 0 ) { + rc = LDAP_OTHER; + goto done; + } + + /* make the control critical, as we cannot proceed without */ + ctrl.ldctl_oid = LDAP_CONTROL_SYNC; + ctrl.ldctl_iscritical = 1; + + /* timelimit? */ + if ( ls->ls_timelimit ) { + tv.tv_sec = ls->ls_timelimit; + tvp = &tv; + } + + /* actually run the search */ + rc = ldap_search_ext( ls->ls_ld, + ls->ls_base, ls->ls_scope, ls->ls_filter, + ls->ls_attrs, 0, ctrls, NULL, + tvp, ls->ls_sizelimit, &ls->ls_msgid ); +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, + "%sldap_search_ext(\"%s\", %d, \"%s\") == %d\n", + rc ? "!!! " : "", + ls->ls_base, ls->ls_scope, ls->ls_filter, rc ); +#endif /* LDAP_SYNC_TRACE */ + if ( rc != LDAP_SUCCESS ) { + goto done; + } + + /* initial content/content update phase */ + for ( ; ; ) { + LDAPMessage *msg = NULL; + + /* NOTE: this very short timeout is just to let + * ldap_result() yield long enough to get something */ + tv.tv_sec = 0; + tv.tv_usec = 100000; + + rc = ldap_result( ls->ls_ld, ls->ls_msgid, + LDAP_MSG_RECEIVED, &tv, &res ); +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, + "\t%sldap_result(%d) == %d\n", + rc == -1 ? "!!! " : "", + ls->ls_msgid, rc ); +#endif /* LDAP_SYNC_TRACE */ + switch ( rc ) { + case 0: + /* + * timeout + * + * TODO: can do something else in the meanwhile) + */ + break; + + case -1: + /* smtg bad! */ + goto done; + + default: + for ( msg = ldap_first_message( ls->ls_ld, res ); + msg != NULL; + msg = ldap_next_message( ls->ls_ld, msg ) ) + { + int refreshDone; + + switch ( ldap_msgtype( msg ) ) { + case LDAP_RES_SEARCH_ENTRY: + rc = ldap_sync_search_entry( ls, res ); + break; + + case LDAP_RES_SEARCH_REFERENCE: + rc = ldap_sync_search_reference( ls, res ); + break; + + case LDAP_RES_SEARCH_RESULT: + rc = ldap_sync_search_result( ls, res ); + goto done_search; + + case LDAP_RES_INTERMEDIATE: + rc = ldap_sync_search_intermediate( ls, res, &refreshDone ); + if ( rc != LDAP_SUCCESS || refreshDone ) { + goto done_search; + } + break; + + default: +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, "\tgot something unexpected...\n" ); +#endif /* LDAP_SYNC_TRACE */ + + ldap_msgfree( res ); + + rc = LDAP_OTHER; + goto done; + } + } + ldap_msgfree( res ); + res = NULL; + break; + } + } + +done_search:; + ldap_msgfree( res ); + +done:; + if ( ber != NULL ) { + ber_free( ber, 1 ); + } + + return rc; +} + +/* + * initialize the refreshOnly sync + */ +int +ldap_sync_init_refresh_only( ldap_sync_t *ls ) +{ + return ldap_sync_init( ls, LDAP_SYNC_REFRESH_ONLY ); +} + +/* + * initialize the refreshAndPersist sync + */ +int +ldap_sync_init_refresh_and_persist( ldap_sync_t *ls ) +{ + return ldap_sync_init( ls, LDAP_SYNC_REFRESH_AND_PERSIST ); +} + +/* + * poll for new responses + */ +int +ldap_sync_poll( ldap_sync_t *ls ) +{ + struct timeval tv, + *tvp = NULL; + LDAPMessage *res = NULL, + *msg; + int rc = 0; + +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, "ldap_sync_poll...\n" ); +#endif /* LDAP_SYNC_TRACE */ + + assert( ls != NULL ); + assert( ls->ls_ld != NULL ); + + if ( ls->ls_timeout != -1 ) { + tv.tv_sec = ls->ls_timeout; + tv.tv_usec = 0; + tvp = &tv; + } + + rc = ldap_result( ls->ls_ld, ls->ls_msgid, + LDAP_MSG_RECEIVED, tvp, &res ); + if ( rc <= 0 ) { + return rc; + } + + for ( msg = ldap_first_message( ls->ls_ld, res ); + msg; + msg = ldap_next_message( ls->ls_ld, msg ) ) + { + int refreshDone; + + switch ( ldap_msgtype( msg ) ) { + case LDAP_RES_SEARCH_ENTRY: + rc = ldap_sync_search_entry( ls, res ); + break; + + case LDAP_RES_SEARCH_REFERENCE: + rc = ldap_sync_search_reference( ls, res ); + break; + + case LDAP_RES_SEARCH_RESULT: + rc = ldap_sync_search_result( ls, res ); + goto done_search; + + case LDAP_RES_INTERMEDIATE: + rc = ldap_sync_search_intermediate( ls, res, &refreshDone ); + if ( rc != LDAP_SUCCESS || refreshDone ) { + goto done_search; + } + break; + + default: +#ifdef LDAP_SYNC_TRACE + fprintf( stderr, "\tgot something unexpected...\n" ); +#endif /* LDAP_SYNC_TRACE */ + + ldap_msgfree( res ); + + rc = LDAP_OTHER; + goto done; + } + } + +done_search:; + ldap_msgfree( res ); + +done:; + return rc; +} diff --git a/libraries/libldap/ldif.c b/libraries/libldap/ldif.c new file mode 100644 index 0000000..55d9852 --- /dev/null +++ b/libraries/libldap/ldif.c @@ -0,0 +1,944 @@ +/* ldif.c - routines for dealing with LDIF files */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1992-1996 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. This + * software is provided ``as is'' without express or implied warranty. + */ +/* This work was originally developed by the University of Michigan + * and distributed as part of U-MICH LDAP. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> +#include <ac/ctype.h> + +#include <ac/string.h> +#include <ac/socket.h> +#include <ac/time.h> + +int ldif_debug = 0; + +#include "ldap_log.h" +#include "lber_pvt.h" +#include "ldif.h" + +#define RIGHT2 0x03 +#define RIGHT4 0x0f +#define CONTINUED_LINE_MARKER '\r' + +#ifdef CSRIMALLOC +#define ber_memalloc malloc +#define ber_memcalloc calloc +#define ber_memrealloc realloc +#define ber_strdup strdup +#endif + +static const char nib2b64[0x40] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +static const unsigned char b642nib[0x80] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f, + 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, + 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, + 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, + 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, + 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, + 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, + 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff +}; + +/* + * ldif_parse_line - takes a line of the form "type:[:] value" and splits it + * into components "type" and "value". if a double colon separates type from + * value, then value is encoded in base 64, and parse_line un-decodes it + * (in place) before returning. The type and value are stored in malloc'd + * memory which must be freed by the caller. + * + * ldif_parse_line2 - operates in-place on input buffer, returning type + * in-place. Will return value in-place if possible, (must malloc for + * fetched URLs). If freeval is NULL, all return data will be malloc'd + * and the input line will be unmodified. Otherwise freeval is set to + * True if the value was malloc'd. + */ + +int +ldif_parse_line( + LDAP_CONST char *line, + char **typep, + char **valuep, + ber_len_t *vlenp +) +{ + struct berval type, value; + int rc = ldif_parse_line2( (char *)line, &type, &value, NULL ); + + *typep = type.bv_val; + *valuep = value.bv_val; + *vlenp = value.bv_len; + return rc; +} + +int +ldif_parse_line2( + char *line, + struct berval *type, + struct berval *value, + int *freeval +) +{ + char *s, *p, *d; + char nib; + int b64, url; + + BER_BVZERO( type ); + BER_BVZERO( value ); + + /* skip any leading space */ + while ( isspace( (unsigned char) *line ) ) { + line++; + } + + if ( freeval ) { + *freeval = 0; + } else { + line = ber_strdup( line ); + + if( line == NULL ) { + ber_pvt_log_printf( LDAP_DEBUG_ANY, ldif_debug, + _("ldif_parse_line: line malloc failed\n")); + return( -1 ); + } + } + + type->bv_val = line; + + s = strchr( type->bv_val, ':' ); + + if ( s == NULL ) { + ber_pvt_log_printf( LDAP_DEBUG_PARSE, ldif_debug, + _("ldif_parse_line: missing ':' after %s\n"), + type->bv_val ); + if ( !freeval ) ber_memfree( line ); + return( -1 ); + } + + /* trim any space between type and : */ + for ( p = &s[-1]; p > type->bv_val && isspace( * (unsigned char *) p ); p-- ) { + *p = '\0'; + } + *s++ = '\0'; + type->bv_len = s - type->bv_val - 1; + + url = 0; + b64 = 0; + + if ( *s == '<' ) { + s++; + url = 1; + + } else if ( *s == ':' ) { + /* base 64 encoded value */ + s++; + b64 = 1; + } + + /* skip space between : and value */ + while ( isspace( (unsigned char) *s ) ) { + s++; + } + + /* check for continued line markers that should be deleted */ + for ( p = s, d = s; *p; p++ ) { + if ( *p != CONTINUED_LINE_MARKER ) + *d++ = *p; + } + *d = '\0'; + + if ( b64 ) { + char *byte = s; + + if ( *s == '\0' ) { + /* no value is present, error out */ + ber_pvt_log_printf( LDAP_DEBUG_PARSE, ldif_debug, + _("ldif_parse_line: %s missing base64 value\n"), + type->bv_val ); + if ( !freeval ) ber_memfree( line ); + return( -1 ); + } + + byte = value->bv_val = s; + + for ( p = s, value->bv_len = 0; p < d; p += 4, value->bv_len += 3 ) { + int i; + for ( i = 0; i < 4; i++ ) { + if ( p[i] != '=' && (p[i] & 0x80 || + b642nib[ p[i] & 0x7f ] > 0x3f) ) { + ber_pvt_log_printf( LDAP_DEBUG_ANY, ldif_debug, + _("ldif_parse_line: %s: invalid base64 encoding" + " char (%c) 0x%x\n"), + type->bv_val, p[i], p[i] ); + if ( !freeval ) ber_memfree( line ); + return( -1 ); + } + } + + /* first digit */ + nib = b642nib[ p[0] & 0x7f ]; + byte[0] = nib << 2; + /* second digit */ + nib = b642nib[ p[1] & 0x7f ]; + byte[0] |= nib >> 4; + byte[1] = (nib & RIGHT4) << 4; + /* third digit */ + if ( p[2] == '=' ) { + value->bv_len += 1; + break; + } + nib = b642nib[ p[2] & 0x7f ]; + byte[1] |= nib >> 2; + byte[2] = (nib & RIGHT2) << 6; + /* fourth digit */ + if ( p[3] == '=' ) { + value->bv_len += 2; + break; + } + nib = b642nib[ p[3] & 0x7f ]; + byte[2] |= nib; + + byte += 3; + } + s[ value->bv_len ] = '\0'; + + } else if ( url ) { + if ( *s == '\0' ) { + /* no value is present, error out */ + ber_pvt_log_printf( LDAP_DEBUG_PARSE, ldif_debug, + _("ldif_parse_line: %s missing URL value\n"), + type->bv_val ); + if ( !freeval ) ber_memfree( line ); + return( -1 ); + } + + if( ldif_fetch_url( s, &value->bv_val, &value->bv_len ) ) { + ber_pvt_log_printf( LDAP_DEBUG_ANY, ldif_debug, + _("ldif_parse_line: %s: URL \"%s\" fetch failed\n"), + type->bv_val, s ); + if ( !freeval ) ber_memfree( line ); + return( -1 ); + } + if ( freeval ) *freeval = 1; + + } else { + value->bv_val = s; + value->bv_len = (int) (d - s); + } + + if ( !freeval ) { + struct berval bv = *type; + + ber_dupbv( type, &bv ); + + if( BER_BVISNULL( type )) { + ber_pvt_log_printf( LDAP_DEBUG_ANY, ldif_debug, + _("ldif_parse_line: type malloc failed\n")); + if( url ) ber_memfree( value->bv_val ); + ber_memfree( line ); + return( -1 ); + } + + if( !url ) { + bv = *value; + ber_dupbv( value, &bv ); + if( BER_BVISNULL( value )) { + ber_pvt_log_printf( LDAP_DEBUG_ANY, ldif_debug, + _("ldif_parse_line: value malloc failed\n")); + ber_memfree( type->bv_val ); + ber_memfree( line ); + return( -1 ); + } + } + + ber_memfree( line ); + } + + return( 0 ); +} + +/* + * ldif_getline - return the next "line" (minus newline) of input from a + * string buffer of lines separated by newlines, terminated by \n\n + * or \0. this routine handles continued lines, bundling them into + * a single big line before returning. if a line begins with a white + * space character, it is a continuation of the previous line. the white + * space character (nb: only one char), and preceeding newline are changed + * into CONTINUED_LINE_MARKER chars, to be deleted later by the + * ldif_parse_line() routine above. + * + * ldif_getline will skip over any line which starts '#'. + * + * ldif_getline takes a pointer to a pointer to the buffer on the first call, + * which it updates and must be supplied on subsequent calls. + */ + +int +ldif_countlines( LDAP_CONST char *buf ) +{ + char *nl; + int ret = 0; + + if ( !buf ) return ret; + + for ( nl = strchr(buf, '\n'); nl; nl = strchr(nl, '\n') ) { + nl++; + if ( *nl != ' ' ) ret++; + } + return ret; +} + +char * +ldif_getline( char **next ) +{ + char *line; + + do { + if ( *next == NULL || **next == '\n' || **next == '\0' ) { + return( NULL ); + } + + line = *next; + + while ( (*next = strchr( *next, '\n' )) != NULL ) { +#if CONTINUED_LINE_MARKER != '\r' + if ( (*next)[-1] == '\r' ) { + (*next)[-1] = CONTINUED_LINE_MARKER; + } +#endif + + if ( (*next)[1] != ' ' ) { + if ( (*next)[1] == '\r' && (*next)[2] == '\n' ) { + *(*next)++ = '\0'; + } + *(*next)++ = '\0'; + break; + } + + **next = CONTINUED_LINE_MARKER; + (*next)[1] = CONTINUED_LINE_MARKER; + (*next)++; + } + } while( *line == '#' ); + + return( line ); +} + +/* + * name and OID of attributeTypes that must be base64 encoded in any case + */ +typedef struct must_b64_encode_s { + struct berval name; + struct berval oid; +} must_b64_encode_s; + +static must_b64_encode_s default_must_b64_encode[] = { + { BER_BVC( "userPassword" ), BER_BVC( "2.5.4.35" ) }, + { BER_BVNULL, BER_BVNULL } +}; + +static must_b64_encode_s *must_b64_encode = default_must_b64_encode; + +/* + * register name and OID of attributeTypes that must always be base64 + * encoded + * + * NOTE: this routine mallocs memory in a static struct which must + * be explicitly freed when no longer required + */ +int +ldif_must_b64_encode_register( LDAP_CONST char *name, LDAP_CONST char *oid ) +{ + int i; + ber_len_t len; + + assert( must_b64_encode != NULL ); + assert( name != NULL ); + assert( oid != NULL ); + + len = strlen( name ); + + for ( i = 0; !BER_BVISNULL( &must_b64_encode[i].name ); i++ ) { + if ( len != must_b64_encode[i].name.bv_len ) { + continue; + } + + if ( strcasecmp( name, must_b64_encode[i].name.bv_val ) == 0 ) { + break; + } + } + + if ( !BER_BVISNULL( &must_b64_encode[i].name ) ) { + return 1; + } + + for ( i = 0; !BER_BVISNULL( &must_b64_encode[i].name ); i++ ) + /* just count */ ; + + if ( must_b64_encode == default_must_b64_encode ) { + must_b64_encode = ber_memalloc( sizeof( must_b64_encode_s ) * ( i + 2 ) ); + + for ( i = 0; !BER_BVISNULL( &default_must_b64_encode[i].name ); i++ ) { + ber_dupbv( &must_b64_encode[i].name, &default_must_b64_encode[i].name ); + ber_dupbv( &must_b64_encode[i].oid, &default_must_b64_encode[i].oid ); + } + + } else { + must_b64_encode_s *tmp; + + tmp = ber_memrealloc( must_b64_encode, + sizeof( must_b64_encode_s ) * ( i + 2 ) ); + if ( tmp == NULL ) { + return 1; + } + must_b64_encode = tmp; + } + + ber_str2bv( name, len, 1, &must_b64_encode[i].name ); + ber_str2bv( oid, 0, 1, &must_b64_encode[i].oid ); + + BER_BVZERO( &must_b64_encode[i + 1].name ); + + return 0; +} + +void +ldif_must_b64_encode_release( void ) +{ + int i; + + assert( must_b64_encode != NULL ); + + if ( must_b64_encode == default_must_b64_encode ) { + return; + } + + for ( i = 0; !BER_BVISNULL( &must_b64_encode[i].name ); i++ ) { + ber_memfree( must_b64_encode[i].name.bv_val ); + ber_memfree( must_b64_encode[i].oid.bv_val ); + } + + ber_memfree( must_b64_encode ); + + must_b64_encode = default_must_b64_encode; +} + +/* + * returns 1 iff the string corresponds to the name or the OID of any + * of the attributeTypes listed in must_b64_encode + */ +static int +ldif_must_b64_encode( LDAP_CONST char *s ) +{ + int i; + struct berval bv; + + assert( must_b64_encode != NULL ); + assert( s != NULL ); + + ber_str2bv( s, 0, 0, &bv ); + + for ( i = 0; !BER_BVISNULL( &must_b64_encode[i].name ); i++ ) { + if ( ber_bvstrcasecmp( &must_b64_encode[i].name, &bv ) == 0 + || ber_bvcmp( &must_b64_encode[i].oid, &bv ) == 0 ) + { + return 1; + } + } + + return 0; +} + +/* compatibility with U-Mich off by two bug */ +#define LDIF_KLUDGE 2 + +/* NOTE: only preserved for binary compatibility */ +void +ldif_sput( + char **out, + int type, + LDAP_CONST char *name, + LDAP_CONST char *val, + ber_len_t vlen ) +{ + ldif_sput_wrap( out, type, name, val, vlen, LDIF_LINE_WIDTH+LDIF_KLUDGE ); +} + +void +ldif_sput_wrap( + char **out, + int type, + LDAP_CONST char *name, + LDAP_CONST char *val, + ber_len_t vlen, + ber_len_t wrap ) +{ + const unsigned char *byte, *stop; + unsigned char buf[3]; + unsigned long bits; + char *save; + int pad; + int namelen = 0; + + ber_len_t savelen; + ber_len_t len=0; + ber_len_t i; + + if ( !wrap ) + wrap = LDIF_LINE_WIDTH+LDIF_KLUDGE; + + /* prefix */ + switch( type ) { + case LDIF_PUT_COMMENT: + *(*out)++ = '#'; + len++; + + if( vlen ) { + *(*out)++ = ' '; + len++; + } + + break; + + case LDIF_PUT_SEP: + *(*out)++ = '\n'; + return; + } + + /* name (attribute type) */ + if( name != NULL ) { + /* put the name + ":" */ + namelen = strlen(name); + strcpy(*out, name); + *out += namelen; + len += namelen; + + if( type != LDIF_PUT_COMMENT ) { + *(*out)++ = ':'; + len++; + } + + } +#ifdef LDAP_DEBUG + else { + assert( type == LDIF_PUT_COMMENT ); + } +#endif + + if( vlen == 0 ) { + *(*out)++ = '\n'; + return; + } + + switch( type ) { + case LDIF_PUT_NOVALUE: + *(*out)++ = '\n'; + return; + + case LDIF_PUT_URL: /* url value */ + *(*out)++ = '<'; + len++; + break; + + case LDIF_PUT_B64: /* base64 value */ + *(*out)++ = ':'; + len++; + break; + } + + switch( type ) { + case LDIF_PUT_TEXT: + case LDIF_PUT_URL: + case LDIF_PUT_B64: + *(*out)++ = ' '; + len++; + /* fall-thru */ + + case LDIF_PUT_COMMENT: + /* pre-encoded names */ + for ( i=0; i < vlen; i++ ) { + if ( len > wrap ) { + *(*out)++ = '\n'; + *(*out)++ = ' '; + len = 1; + } + + *(*out)++ = val[i]; + len++; + } + *(*out)++ = '\n'; + return; + } + + save = *out; + savelen = len; + + *(*out)++ = ' '; + len++; + + stop = (const unsigned char *) (val + vlen); + + if ( type == LDIF_PUT_VALUE + && isgraph( (unsigned char) val[0] ) && val[0] != ':' && val[0] != '<' + && isgraph( (unsigned char) val[vlen-1] ) +#ifndef LDAP_BINARY_DEBUG + && strstr( name, ";binary" ) == NULL +#endif +#ifndef LDAP_PASSWD_DEBUG + && !ldif_must_b64_encode( name ) +#endif + ) { + int b64 = 0; + + for ( byte = (const unsigned char *) val; byte < stop; + byte++, len++ ) + { + if ( !isascii( *byte ) || !isprint( *byte ) ) { + b64 = 1; + break; + } + if ( len >= wrap ) { + *(*out)++ = '\n'; + *(*out)++ = ' '; + len = 1; + } + *(*out)++ = *byte; + } + + if( !b64 ) { + *(*out)++ = '\n'; + return; + } + } + + *out = save; + *(*out)++ = ':'; + *(*out)++ = ' '; + len = savelen + 2; + + /* convert to base 64 (3 bytes => 4 base 64 digits) */ + for ( byte = (const unsigned char *) val; + byte < stop - 2; + byte += 3 ) + { + bits = (byte[0] & 0xff) << 16; + bits |= (byte[1] & 0xff) << 8; + bits |= (byte[2] & 0xff); + + for ( i = 0; i < 4; i++, len++, bits <<= 6 ) { + if ( len >= wrap ) { + *(*out)++ = '\n'; + *(*out)++ = ' '; + len = 1; + } + + /* get b64 digit from high order 6 bits */ + *(*out)++ = nib2b64[ (bits & 0xfc0000L) >> 18 ]; + } + } + + /* add padding if necessary */ + if ( byte < stop ) { + for ( i = 0; byte + i < stop; i++ ) { + buf[i] = byte[i]; + } + for ( pad = 0; i < 3; i++, pad++ ) { + buf[i] = '\0'; + } + byte = buf; + bits = (byte[0] & 0xff) << 16; + bits |= (byte[1] & 0xff) << 8; + bits |= (byte[2] & 0xff); + + for ( i = 0; i < 4; i++, len++, bits <<= 6 ) { + if ( len >= wrap ) { + *(*out)++ = '\n'; + *(*out)++ = ' '; + len = 1; + } + + if( i + pad < 4 ) { + /* get b64 digit from low order 6 bits */ + *(*out)++ = nib2b64[ (bits & 0xfc0000L) >> 18 ]; + } else { + *(*out)++ = '='; + } + } + } + *(*out)++ = '\n'; +} + + +/* + * ldif_type_and_value return BER malloc'd, zero-terminated LDIF line + */ + +/* NOTE: only preserved for binary compatibility */ +char * +ldif_put( + int type, + LDAP_CONST char *name, + LDAP_CONST char *val, + ber_len_t vlen ) +{ + return ldif_put_wrap( type, name, val, vlen, LDIF_LINE_WIDTH ); +} + +char * +ldif_put_wrap( + int type, + LDAP_CONST char *name, + LDAP_CONST char *val, + ber_len_t vlen, + ber_len_t wrap ) +{ + char *buf, *p; + ber_len_t nlen; + + nlen = ( name != NULL ) ? strlen( name ) : 0; + + buf = (char *) ber_memalloc( LDIF_SIZE_NEEDED_WRAP( nlen, vlen, wrap ) + 1 ); + + if ( buf == NULL ) { + ber_pvt_log_printf( LDAP_DEBUG_ANY, ldif_debug, + _("ldif_type_and_value: malloc failed!")); + return NULL; + } + + p = buf; + ldif_sput_wrap( &p, type, name, val, vlen, wrap ); + *p = '\0'; + + return( buf ); +} + +int ldif_is_not_printable( + LDAP_CONST char *val, + ber_len_t vlen ) +{ + if( vlen == 0 || val == NULL ) { + return -1; + } + + if( isgraph( (unsigned char) val[0] ) && val[0] != ':' && val[0] != '<' && + isgraph( (unsigned char) val[vlen-1] ) ) + { + ber_len_t i; + + for ( i = 0; val[i]; i++ ) { + if ( !isascii( val[i] ) || !isprint( (unsigned char) val[i] ) ) { + return 1; + } + } + + return 0; + } + + return 1; +} + +LDIFFP * +ldif_open( + LDAP_CONST char *file, + LDAP_CONST char *mode +) +{ + FILE *fp = fopen( file, mode ); + LDIFFP *lfp = NULL; + + if ( fp ) { + lfp = ber_memalloc( sizeof( LDIFFP )); + lfp->fp = fp; + lfp->prev = NULL; + } + return lfp; +} + +void +ldif_close( + LDIFFP *lfp +) +{ + LDIFFP *prev; + + while ( lfp ) { + fclose( lfp->fp ); + prev = lfp->prev; + ber_memfree( lfp ); + lfp = prev; + } +} + +#define LDIF_MAXLINE 4096 + +/* + * ldif_read_record - read an ldif record. Return 1 for success, 0 for EOF, + * -1 for error. + */ +int +ldif_read_record( + LDIFFP *lfp, + unsigned long *lno, /* ptr to line number counter */ + char **bufp, /* ptr to malloced output buffer */ + int *buflenp ) /* ptr to length of *bufp */ +{ + char line[LDIF_MAXLINE], *nbufp; + ber_len_t lcur = 0, len; + int last_ch = '\n', found_entry = 0, stop, top_comment = 0; + + for ( stop = 0; !stop; last_ch = line[len-1] ) { + /* If we're at the end of this file, see if we should pop + * back to a previous file. (return from an include) + */ + while ( feof( lfp->fp )) { + if ( lfp->prev ) { + LDIFFP *tmp = lfp->prev; + fclose( lfp->fp ); + *lfp = *tmp; + ber_memfree( tmp ); + } else { + stop = 1; + break; + } + } + if ( !stop ) { + if ( fgets( line, sizeof( line ), lfp->fp ) == NULL ) { + stop = 1; + len = 0; + } else { + len = strlen( line ); + } + } + + if ( stop ) { + /* Add \n in case the file does not end with newline */ + if (last_ch != '\n') { + len = 1; + line[0] = '\n'; + line[1] = '\0'; + goto last; + } + break; + } + + /* Squash \r\n to \n */ + if ( len > 1 && line[len-2] == '\r' ) { + len--; + line[len-1] = '\n'; + } + + if ( last_ch == '\n' ) { + (*lno)++; + + if ( line[0] == '\n' ) { + if ( !found_entry ) { + lcur = 0; + top_comment = 0; + continue; + } + break; + } + + if ( !found_entry ) { + if ( line[0] == '#' ) { + top_comment = 1; + } else if ( ! ( top_comment && line[0] == ' ' ) ) { + /* Found a new entry */ + found_entry = 1; + + if ( isdigit( (unsigned char) line[0] ) ) { + /* skip index */ + continue; + } + if ( !strncasecmp( line, "include:", + STRLENOF("include:"))) { + FILE *fp2; + char *ptr; + found_entry = 0; + + if ( line[len-1] == '\n' ) { + len--; + line[len] = '\0'; + } + + ptr = line + STRLENOF("include:"); + while (isspace((unsigned char) *ptr)) ptr++; + fp2 = ldif_open_url( ptr ); + if ( fp2 ) { + LDIFFP *lnew = ber_memalloc( sizeof( LDIFFP )); + if ( lnew == NULL ) { + fclose( fp2 ); + return 0; + } + lnew->prev = lfp->prev; + lnew->fp = lfp->fp; + lfp->prev = lnew; + lfp->fp = fp2; + line[len] = '\n'; + len++; + continue; + } else { + /* We failed to open the file, this should + * be reported as an error somehow. + */ + ber_pvt_log_printf( LDAP_DEBUG_ANY, ldif_debug, + _("ldif_read_record: include %s failed\n"), ptr ); + return -1; + } + } + } + } + } + +last: + if ( *buflenp - lcur <= len ) { + *buflenp += len + LDIF_MAXLINE; + nbufp = ber_memrealloc( *bufp, *buflenp ); + if( nbufp == NULL ) { + return 0; + } + *bufp = nbufp; + } + strcpy( *bufp + lcur, line ); + lcur += len; + } + + return( found_entry ); +} diff --git a/libraries/libldap/messages.c b/libraries/libldap/messages.c new file mode 100644 index 0000000..80a5ea4 --- /dev/null +++ b/libraries/libldap/messages.c @@ -0,0 +1,68 @@ +/* messages.c */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +LDAPMessage * +ldap_first_message( LDAP *ld, LDAPMessage *chain ) +{ + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( chain != NULL ); + + return chain; +} + +LDAPMessage * +ldap_next_message( LDAP *ld, LDAPMessage *msg ) +{ + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( msg != NULL ); + + return msg->lm_chain; +} + +int +ldap_count_messages( LDAP *ld, LDAPMessage *chain ) +{ + int i; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + + for ( i = 0; chain != NULL; chain = chain->lm_chain ) { + i++; + } + + return( i ); +} + +BerElement* +ldap_get_message_ber( LDAPMessage *ld ) +{ + return ld->lm_ber; +} diff --git a/libraries/libldap/modify.c b/libraries/libldap/modify.c new file mode 100644 index 0000000..69be8c4 --- /dev/null +++ b/libraries/libldap/modify.c @@ -0,0 +1,233 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +/* A modify request/response looks like this: + * ModifyRequest ::= [APPLICATION 6] SEQUENCE { + * object LDAPDN, + * changes SEQUENCE OF change SEQUENCE { + * operation ENUMERATED { + * add (0), + * delete (1), + * replace (2), + * ... }, + * modification PartialAttribute } } + * + * PartialAttribute ::= SEQUENCE { + * type AttributeDescription, + * vals SET OF value AttributeValue } + * + * AttributeDescription ::= LDAPString + * -- Constrained to <attributedescription> [RFC4512] + * + * AttributeValue ::= OCTET STRING + * + * ModifyResponse ::= [APPLICATION 7] LDAPResult + * + * (Source: RFC 4511) + */ + +BerElement * +ldap_build_modify_req( + LDAP *ld, + LDAP_CONST char *dn, + LDAPMod **mods, + LDAPControl **sctrls, + LDAPControl **cctrls, + ber_int_t *msgidp ) +{ + BerElement *ber; + int i, rc; + + /* create a message to send */ + if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) { + return( NULL ); + } + + LDAP_NEXT_MSGID( ld, *msgidp ); + rc = ber_printf( ber, "{it{s{" /*}}}*/, *msgidp, LDAP_REQ_MODIFY, dn ); + if ( rc == -1 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return( NULL ); + } + + /* allow mods to be NULL ("touch") */ + if ( mods ) { + /* for each modification to be performed... */ + for ( i = 0; mods[i] != NULL; i++ ) { + if (( mods[i]->mod_op & LDAP_MOD_BVALUES) != 0 ) { + rc = ber_printf( ber, "{e{s[V]N}N}", + (ber_int_t) ( mods[i]->mod_op & ~LDAP_MOD_BVALUES ), + mods[i]->mod_type, mods[i]->mod_bvalues ); + } else { + rc = ber_printf( ber, "{e{s[v]N}N}", + (ber_int_t) mods[i]->mod_op, + mods[i]->mod_type, mods[i]->mod_values ); + } + + if ( rc == -1 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return( NULL ); + } + } + } + + if ( ber_printf( ber, /*{{*/ "N}N}" ) == -1 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return( NULL ); + } + + /* Put Server Controls */ + if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) { + ber_free( ber, 1 ); + return( NULL ); + } + + if ( ber_printf( ber, /*{*/ "N}" ) == -1 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return( NULL ); + } + + return( ber ); +} + +/* + * ldap_modify_ext - initiate an ldap extended modify operation. + * + * Parameters: + * + * ld LDAP descriptor + * dn DN of the object to modify + * mods List of modifications to make. This is null-terminated + * array of struct ldapmod's, specifying the modifications + * to perform. + * sctrls Server Controls + * cctrls Client Controls + * msgidp Message ID pointer + * + * Example: + * LDAPMod *mods[] = { + * { LDAP_MOD_ADD, "cn", { "babs jensen", "babs", 0 } }, + * { LDAP_MOD_REPLACE, "sn", { "babs jensen", "babs", 0 } }, + * { LDAP_MOD_DELETE, "ou", 0 }, + * { LDAP_MOD_INCREMENT, "uidNumber, { "1", 0 } } + * 0 + * } + * rc= ldap_modify_ext( ld, dn, mods, sctrls, cctrls, &msgid ); + */ +int +ldap_modify_ext( LDAP *ld, + LDAP_CONST char *dn, + LDAPMod **mods, + LDAPControl **sctrls, + LDAPControl **cctrls, + int *msgidp ) +{ + BerElement *ber; + int rc; + ber_int_t id; + + Debug( LDAP_DEBUG_TRACE, "ldap_modify_ext\n", 0, 0, 0 ); + + /* check client controls */ + rc = ldap_int_client_controls( ld, cctrls ); + if( rc != LDAP_SUCCESS ) return rc; + + ber = ldap_build_modify_req( ld, dn, mods, sctrls, cctrls, &id ); + if( !ber ) + return ld->ld_errno; + + /* send the message */ + *msgidp = ldap_send_initial_request( ld, LDAP_REQ_MODIFY, dn, ber, id ); + return( *msgidp < 0 ? ld->ld_errno : LDAP_SUCCESS ); +} + +/* + * ldap_modify - initiate an ldap modify operation. + * + * Parameters: + * + * ld LDAP descriptor + * dn DN of the object to modify + * mods List of modifications to make. This is null-terminated + * array of struct ldapmod's, specifying the modifications + * to perform. + * + * Example: + * LDAPMod *mods[] = { + * { LDAP_MOD_ADD, "cn", { "babs jensen", "babs", 0 } }, + * { LDAP_MOD_REPLACE, "sn", { "babs jensen", "babs", 0 } }, + * { LDAP_MOD_DELETE, "ou", 0 }, + * { LDAP_MOD_INCREMENT, "uidNumber, { "1", 0 } } + * 0 + * } + * msgid = ldap_modify( ld, dn, mods ); + */ +int +ldap_modify( LDAP *ld, LDAP_CONST char *dn, LDAPMod **mods ) +{ + int rc, msgid; + + Debug( LDAP_DEBUG_TRACE, "ldap_modify\n", 0, 0, 0 ); + + rc = ldap_modify_ext( ld, dn, mods, NULL, NULL, &msgid ); + + if ( rc != LDAP_SUCCESS ) + return -1; + + return msgid; +} + +int +ldap_modify_ext_s( LDAP *ld, LDAP_CONST char *dn, + LDAPMod **mods, LDAPControl **sctrl, LDAPControl **cctrl ) +{ + int rc; + int msgid; + LDAPMessage *res; + + rc = ldap_modify_ext( ld, dn, mods, sctrl, cctrl, &msgid ); + + if ( rc != LDAP_SUCCESS ) + return( rc ); + + if ( ldap_result( ld, msgid, LDAP_MSG_ALL, (struct timeval *) NULL, &res ) == -1 || !res ) + return( ld->ld_errno ); + + return( ldap_result2error( ld, res, 1 ) ); +} + +int +ldap_modify_s( LDAP *ld, LDAP_CONST char *dn, LDAPMod **mods ) +{ + return ldap_modify_ext_s( ld, dn, mods, NULL, NULL ); +} + diff --git a/libraries/libldap/modrdn.c b/libraries/libldap/modrdn.c new file mode 100644 index 0000000..bd210c0 --- /dev/null +++ b/libraries/libldap/modrdn.c @@ -0,0 +1,273 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + */ +/* Copyright 1999, Juan C. Gomez, All rights reserved. + * This software is not subject to any license of Silicon Graphics + * Inc. or Purdue University. + * + * Redistribution and use in source and binary forms are permitted + * without restriction or fee of any kind as long as this notice + * is preserved. + */ + +/* ACKNOWLEDGEMENTS: + * Juan C. Gomez + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +/* + * A modify rdn request looks like this: + * ModifyRDNRequest ::= SEQUENCE { + * entry DistinguishedName, + * newrdn RelativeDistinguishedName, + * deleteoldrdn BOOLEAN + * newSuperior [0] DistinguishedName [v3 only] + * } + */ + +BerElement * +ldap_build_moddn_req( + LDAP *ld, + LDAP_CONST char *dn, + LDAP_CONST char *newrdn, + LDAP_CONST char *newSuperior, + int deleteoldrdn, + LDAPControl **sctrls, + LDAPControl **cctrls, + ber_int_t *msgidp ) +{ + BerElement *ber; + int rc; + + /* create a message to send */ + if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) { + return( NULL ); + } + + LDAP_NEXT_MSGID( ld, *msgidp ); + if( newSuperior != NULL ) { + /* must be version 3 (or greater) */ + if ( ld->ld_version < LDAP_VERSION3 ) { + ld->ld_errno = LDAP_NOT_SUPPORTED; + ber_free( ber, 1 ); + return( NULL ); + } + rc = ber_printf( ber, "{it{ssbtsN}", /* '}' */ + *msgidp, LDAP_REQ_MODDN, + dn, newrdn, (ber_int_t) deleteoldrdn, + LDAP_TAG_NEWSUPERIOR, newSuperior ); + + } else { + rc = ber_printf( ber, "{it{ssbN}", /* '}' */ + *msgidp, LDAP_REQ_MODDN, + dn, newrdn, (ber_int_t) deleteoldrdn ); + } + + if ( rc < 0 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return( NULL ); + } + + /* Put Server Controls */ + if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) { + ber_free( ber, 1 ); + return( NULL ); + } + + rc = ber_printf( ber, /*{*/ "N}" ); + if ( rc < 0 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return( NULL ); + } + + return( ber ); +} + +/* + * ldap_rename - initiate an ldap extended modifyDN operation. + * + * Parameters: + * ld LDAP descriptor + * dn DN of the object to modify + * newrdn RDN to give the object + * deleteoldrdn nonzero means to delete old rdn values from the entry + * newSuperior DN of the new parent if applicable + * + * Returns the LDAP error code. + */ + +int +ldap_rename( + LDAP *ld, + LDAP_CONST char *dn, + LDAP_CONST char *newrdn, + LDAP_CONST char *newSuperior, + int deleteoldrdn, + LDAPControl **sctrls, + LDAPControl **cctrls, + int *msgidp ) +{ + BerElement *ber; + int rc; + ber_int_t id; + + Debug( LDAP_DEBUG_TRACE, "ldap_rename\n", 0, 0, 0 ); + + /* check client controls */ + rc = ldap_int_client_controls( ld, cctrls ); + if( rc != LDAP_SUCCESS ) return rc; + + ber = ldap_build_moddn_req( ld, dn, newrdn, newSuperior, + deleteoldrdn, sctrls, cctrls, &id ); + if( !ber ) + return ld->ld_errno; + + /* send the message */ + *msgidp = ldap_send_initial_request( ld, LDAP_REQ_MODRDN, dn, ber, id ); + + if( *msgidp < 0 ) { + return( ld->ld_errno ); + } + + return LDAP_SUCCESS; +} + + +/* + * ldap_rename2 - initiate an ldap (and X.500) modifyDN operation. Parameters: + * (LDAP V3 MODIFYDN REQUEST) + * ld LDAP descriptor + * dn DN of the object to modify + * newrdn RDN to give the object + * deleteoldrdn nonzero means to delete old rdn values from the entry + * newSuperior DN of the new parent if applicable + * + * ldap_rename2 uses a U-Mich Style API. It returns the msgid. + */ + +int +ldap_rename2( + LDAP *ld, + LDAP_CONST char *dn, + LDAP_CONST char *newrdn, + LDAP_CONST char *newSuperior, + int deleteoldrdn ) +{ + int msgid; + int rc; + + Debug( LDAP_DEBUG_TRACE, "ldap_rename2\n", 0, 0, 0 ); + + rc = ldap_rename( ld, dn, newrdn, newSuperior, + deleteoldrdn, NULL, NULL, &msgid ); + + return rc == LDAP_SUCCESS ? msgid : -1; +} + + +/* + * ldap_modrdn2 - initiate an ldap modifyRDN operation. Parameters: + * + * ld LDAP descriptor + * dn DN of the object to modify + * newrdn RDN to give the object + * deleteoldrdn nonzero means to delete old rdn values from the entry + * + * Example: + * msgid = ldap_modrdn( ld, dn, newrdn ); + */ +int +ldap_modrdn2( LDAP *ld, + LDAP_CONST char *dn, + LDAP_CONST char *newrdn, + int deleteoldrdn ) +{ + return ldap_rename2( ld, dn, newrdn, NULL, deleteoldrdn ); +} + +int +ldap_modrdn( LDAP *ld, LDAP_CONST char *dn, LDAP_CONST char *newrdn ) +{ + return( ldap_rename2( ld, dn, newrdn, NULL, 1 ) ); +} + + +int +ldap_rename_s( + LDAP *ld, + LDAP_CONST char *dn, + LDAP_CONST char *newrdn, + LDAP_CONST char *newSuperior, + int deleteoldrdn, + LDAPControl **sctrls, + LDAPControl **cctrls ) +{ + int rc; + int msgid; + LDAPMessage *res; + + rc = ldap_rename( ld, dn, newrdn, newSuperior, + deleteoldrdn, sctrls, cctrls, &msgid ); + + if( rc != LDAP_SUCCESS ) { + return rc; + } + + rc = ldap_result( ld, msgid, LDAP_MSG_ALL, NULL, &res ); + + if( rc == -1 || !res ) { + return ld->ld_errno; + } + + return ldap_result2error( ld, res, 1 ); +} + +int +ldap_rename2_s( + LDAP *ld, + LDAP_CONST char *dn, + LDAP_CONST char *newrdn, + LDAP_CONST char *newSuperior, + int deleteoldrdn ) +{ + return ldap_rename_s( ld, dn, newrdn, newSuperior, + deleteoldrdn, NULL, NULL ); +} + +int +ldap_modrdn2_s( LDAP *ld, LDAP_CONST char *dn, LDAP_CONST char *newrdn, int deleteoldrdn ) +{ + return ldap_rename_s( ld, dn, newrdn, NULL, deleteoldrdn, NULL, NULL ); +} + +int +ldap_modrdn_s( LDAP *ld, LDAP_CONST char *dn, LDAP_CONST char *newrdn ) +{ + return ldap_rename_s( ld, dn, newrdn, NULL, 1, NULL, NULL ); +} + diff --git a/libraries/libldap/open.c b/libraries/libldap/open.c new file mode 100644 index 0000000..5882b63 --- /dev/null +++ b/libraries/libldap/open.c @@ -0,0 +1,611 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1995 Regents of the University of Michigan. + * All rights reserved. + */ + +#include "portable.h" + +#include <stdio.h> +#ifdef HAVE_LIMITS_H +#include <limits.h> +#endif + +#include <ac/stdlib.h> + +#include <ac/param.h> +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include <ac/unistd.h> + +#include "ldap-int.h" +#include "ldap.h" +#include "ldap_log.h" + +/* Caller must hold the conn_mutex since simultaneous accesses are possible */ +int ldap_open_defconn( LDAP *ld ) +{ + ld->ld_defconn = ldap_new_connection( ld, + &ld->ld_options.ldo_defludp, 1, 1, NULL, 0, 0 ); + + if( ld->ld_defconn == NULL ) { + ld->ld_errno = LDAP_SERVER_DOWN; + return -1; + } + + ++ld->ld_defconn->lconn_refcnt; /* so it never gets closed/freed */ + return 0; +} + +/* + * ldap_open - initialize and connect to an ldap server. A magic cookie to + * be used for future communication is returned on success, NULL on failure. + * "host" may be a space-separated list of hosts or IP addresses + * + * Example: + * LDAP *ld; + * ld = ldap_open( hostname, port ); + */ + +LDAP * +ldap_open( LDAP_CONST char *host, int port ) +{ + int rc; + LDAP *ld; + + Debug( LDAP_DEBUG_TRACE, "ldap_open(%s, %d)\n", + host, port, 0 ); + + ld = ldap_init( host, port ); + if ( ld == NULL ) { + return( NULL ); + } + + LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); + rc = ldap_open_defconn( ld ); + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); + + if( rc < 0 ) { + ldap_ld_free( ld, 0, NULL, NULL ); + ld = NULL; + } + + Debug( LDAP_DEBUG_TRACE, "ldap_open: %s\n", + ld != NULL ? "succeeded" : "failed", 0, 0 ); + + return ld; +} + + + +int +ldap_create( LDAP **ldp ) +{ + LDAP *ld; + struct ldapoptions *gopts; + + *ldp = NULL; + /* Get pointer to global option structure */ + if ( (gopts = LDAP_INT_GLOBAL_OPT()) == NULL) { + return LDAP_NO_MEMORY; + } + + /* Initialize the global options, if not already done. */ + if( gopts->ldo_valid != LDAP_INITIALIZED ) { + ldap_int_initialize(gopts, NULL); + if ( gopts->ldo_valid != LDAP_INITIALIZED ) + return LDAP_LOCAL_ERROR; + } + + Debug( LDAP_DEBUG_TRACE, "ldap_create\n", 0, 0, 0 ); + + if ( (ld = (LDAP *) LDAP_CALLOC( 1, sizeof(LDAP) )) == NULL ) { + return( LDAP_NO_MEMORY ); + } + + if ( (ld->ldc = (struct ldap_common *) LDAP_CALLOC( 1, + sizeof(struct ldap_common) )) == NULL ) { + LDAP_FREE( (char *)ld ); + return( LDAP_NO_MEMORY ); + } + /* copy the global options */ + LDAP_MUTEX_LOCK( &gopts->ldo_mutex ); + AC_MEMCPY(&ld->ld_options, gopts, sizeof(ld->ld_options)); +#ifdef LDAP_R_COMPILE + /* Properly initialize the structs mutex */ + ldap_pvt_thread_mutex_init( &(ld->ld_ldopts_mutex) ); +#endif + LDAP_MUTEX_UNLOCK( &gopts->ldo_mutex ); + + ld->ld_valid = LDAP_VALID_SESSION; + + /* but not pointers to malloc'ed items */ + ld->ld_options.ldo_sctrls = NULL; + ld->ld_options.ldo_cctrls = NULL; + ld->ld_options.ldo_defludp = NULL; + ld->ld_options.ldo_conn_cbs = NULL; + +#ifdef HAVE_CYRUS_SASL + ld->ld_options.ldo_def_sasl_mech = gopts->ldo_def_sasl_mech + ? LDAP_STRDUP( gopts->ldo_def_sasl_mech ) : NULL; + ld->ld_options.ldo_def_sasl_realm = gopts->ldo_def_sasl_realm + ? LDAP_STRDUP( gopts->ldo_def_sasl_realm ) : NULL; + ld->ld_options.ldo_def_sasl_authcid = gopts->ldo_def_sasl_authcid + ? LDAP_STRDUP( gopts->ldo_def_sasl_authcid ) : NULL; + ld->ld_options.ldo_def_sasl_authzid = gopts->ldo_def_sasl_authzid + ? LDAP_STRDUP( gopts->ldo_def_sasl_authzid ) : NULL; +#endif + +#ifdef HAVE_TLS + /* We explicitly inherit the SSL_CTX, don't need the names/paths. Leave + * them empty to allow new SSL_CTX's to be created from scratch. + */ + memset( &ld->ld_options.ldo_tls_info, 0, + sizeof( ld->ld_options.ldo_tls_info )); + ld->ld_options.ldo_tls_ctx = NULL; +#endif + + if ( gopts->ldo_defludp ) { + ld->ld_options.ldo_defludp = ldap_url_duplist(gopts->ldo_defludp); + + if ( ld->ld_options.ldo_defludp == NULL ) goto nomem; + } + + if (( ld->ld_selectinfo = ldap_new_select_info()) == NULL ) goto nomem; + + ld->ld_lberoptions = LBER_USE_DER; + + ld->ld_sb = ber_sockbuf_alloc( ); + if ( ld->ld_sb == NULL ) goto nomem; + +#ifdef LDAP_R_COMPILE + ldap_pvt_thread_mutex_init( &ld->ld_msgid_mutex ); + ldap_pvt_thread_mutex_init( &ld->ld_conn_mutex ); + ldap_pvt_thread_mutex_init( &ld->ld_req_mutex ); + ldap_pvt_thread_mutex_init( &ld->ld_res_mutex ); + ldap_pvt_thread_mutex_init( &ld->ld_abandon_mutex ); + ldap_pvt_thread_mutex_init( &ld->ld_ldcmutex ); +#endif + ld->ld_ldcrefcnt = 1; + *ldp = ld; + return LDAP_SUCCESS; + +nomem: + ldap_free_select_info( ld->ld_selectinfo ); + ldap_free_urllist( ld->ld_options.ldo_defludp ); +#ifdef HAVE_CYRUS_SASL + LDAP_FREE( ld->ld_options.ldo_def_sasl_authzid ); + LDAP_FREE( ld->ld_options.ldo_def_sasl_authcid ); + LDAP_FREE( ld->ld_options.ldo_def_sasl_realm ); + LDAP_FREE( ld->ld_options.ldo_def_sasl_mech ); +#endif + LDAP_FREE( (char *)ld ); + return LDAP_NO_MEMORY; +} + +/* + * ldap_init - initialize the LDAP library. A magic cookie to be used for + * future communication is returned on success, NULL on failure. + * "host" may be a space-separated list of hosts or IP addresses + * + * Example: + * LDAP *ld; + * ld = ldap_init( host, port ); + */ +LDAP * +ldap_init( LDAP_CONST char *defhost, int defport ) +{ + LDAP *ld; + int rc; + + rc = ldap_create(&ld); + if ( rc != LDAP_SUCCESS ) + return NULL; + + if (defport != 0) + ld->ld_options.ldo_defport = defport; + + if (defhost != NULL) { + rc = ldap_set_option(ld, LDAP_OPT_HOST_NAME, defhost); + if ( rc != LDAP_SUCCESS ) { + ldap_ld_free(ld, 1, NULL, NULL); + return NULL; + } + } + + return( ld ); +} + + +int +ldap_initialize( LDAP **ldp, LDAP_CONST char *url ) +{ + int rc; + LDAP *ld; + + *ldp = NULL; + rc = ldap_create(&ld); + if ( rc != LDAP_SUCCESS ) + return rc; + + if (url != NULL) { + rc = ldap_set_option(ld, LDAP_OPT_URI, url); + if ( rc != LDAP_SUCCESS ) { + ldap_ld_free(ld, 1, NULL, NULL); + return rc; + } +#ifdef LDAP_CONNECTIONLESS + if (ldap_is_ldapc_url(url)) + LDAP_IS_UDP(ld) = 1; +#endif + } + + *ldp = ld; + return LDAP_SUCCESS; +} + +int +ldap_init_fd( + ber_socket_t fd, + int proto, + LDAP_CONST char *url, + LDAP **ldp +) +{ + int rc; + LDAP *ld; + LDAPConn *conn; +#ifdef LDAP_CONNECTIONLESS + ber_socklen_t len; +#endif + + *ldp = NULL; + rc = ldap_create( &ld ); + if( rc != LDAP_SUCCESS ) + return( rc ); + + if (url != NULL) { + rc = ldap_set_option(ld, LDAP_OPT_URI, url); + if ( rc != LDAP_SUCCESS ) { + ldap_ld_free(ld, 1, NULL, NULL); + return rc; + } + } + + LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); + /* Attach the passed socket as the LDAP's connection */ + conn = ldap_new_connection( ld, NULL, 1, 0, NULL, 0, 0 ); + if( conn == NULL ) { + ldap_unbind_ext( ld, NULL, NULL ); + return( LDAP_NO_MEMORY ); + } + if( url ) + conn->lconn_server = ldap_url_dup( ld->ld_options.ldo_defludp ); + ber_sockbuf_ctrl( conn->lconn_sb, LBER_SB_OPT_SET_FD, &fd ); + ld->ld_defconn = conn; + ++ld->ld_defconn->lconn_refcnt; /* so it never gets closed/freed */ + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); + + switch( proto ) { + case LDAP_PROTO_TCP: +#ifdef LDAP_DEBUG + ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_debug, + LBER_SBIOD_LEVEL_PROVIDER, (void *)"tcp_" ); +#endif + ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_tcp, + LBER_SBIOD_LEVEL_PROVIDER, NULL ); + break; + +#ifdef LDAP_CONNECTIONLESS + case LDAP_PROTO_UDP: + LDAP_IS_UDP(ld) = 1; + if( ld->ld_options.ldo_peer ) + ldap_memfree( ld->ld_options.ldo_peer ); + ld->ld_options.ldo_peer = ldap_memcalloc( 1, sizeof( struct sockaddr_storage ) ); + len = sizeof( struct sockaddr_storage ); + if( getpeername ( fd, ld->ld_options.ldo_peer, &len ) < 0) { + ldap_unbind_ext( ld, NULL, NULL ); + return( AC_SOCKET_ERROR ); + } +#ifdef LDAP_DEBUG + ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_debug, + LBER_SBIOD_LEVEL_PROVIDER, (void *)"udp_" ); +#endif + ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_udp, + LBER_SBIOD_LEVEL_PROVIDER, NULL ); + ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_readahead, + LBER_SBIOD_LEVEL_PROVIDER, NULL ); + break; +#endif /* LDAP_CONNECTIONLESS */ + + case LDAP_PROTO_IPC: +#ifdef LDAP_DEBUG + ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_debug, + LBER_SBIOD_LEVEL_PROVIDER, (void *)"ipc_" ); +#endif + ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_fd, + LBER_SBIOD_LEVEL_PROVIDER, NULL ); + break; + + case LDAP_PROTO_EXT: + /* caller must supply sockbuf handlers */ + break; + + default: + ldap_unbind_ext( ld, NULL, NULL ); + return LDAP_PARAM_ERROR; + } + +#ifdef LDAP_DEBUG + ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_debug, + INT_MAX, (void *)"ldap_" ); +#endif + + /* Add the connection to the *LDAP's select pool */ + ldap_mark_select_read( ld, conn->lconn_sb ); + + *ldp = ld; + return LDAP_SUCCESS; +} + +/* Protected by ld_conn_mutex */ +int +ldap_int_open_connection( + LDAP *ld, + LDAPConn *conn, + LDAPURLDesc *srv, + int async ) +{ + int rc = -1; + int proto; + + Debug( LDAP_DEBUG_TRACE, "ldap_int_open_connection\n", 0, 0, 0 ); + + switch ( proto = ldap_pvt_url_scheme2proto( srv->lud_scheme ) ) { + case LDAP_PROTO_TCP: + rc = ldap_connect_to_host( ld, conn->lconn_sb, + proto, srv, async ); + + if ( rc == -1 ) return rc; +#ifdef LDAP_DEBUG + ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_debug, + LBER_SBIOD_LEVEL_PROVIDER, (void *)"tcp_" ); +#endif + ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_tcp, + LBER_SBIOD_LEVEL_PROVIDER, NULL ); + + break; + +#ifdef LDAP_CONNECTIONLESS + case LDAP_PROTO_UDP: + LDAP_IS_UDP(ld) = 1; + rc = ldap_connect_to_host( ld, conn->lconn_sb, + proto, srv, async ); + + if ( rc == -1 ) return rc; +#ifdef LDAP_DEBUG + ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_debug, + LBER_SBIOD_LEVEL_PROVIDER, (void *)"udp_" ); +#endif + ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_udp, + LBER_SBIOD_LEVEL_PROVIDER, NULL ); + + ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_readahead, + LBER_SBIOD_LEVEL_PROVIDER, NULL ); + + break; +#endif + case LDAP_PROTO_IPC: +#ifdef LDAP_PF_LOCAL + /* only IPC mechanism supported is PF_LOCAL (PF_UNIX) */ + rc = ldap_connect_to_path( ld, conn->lconn_sb, + srv, async ); + if ( rc == -1 ) return rc; +#ifdef LDAP_DEBUG + ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_debug, + LBER_SBIOD_LEVEL_PROVIDER, (void *)"ipc_" ); +#endif + ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_fd, + LBER_SBIOD_LEVEL_PROVIDER, NULL ); + + break; +#endif /* LDAP_PF_LOCAL */ + default: + return -1; + break; + } + + conn->lconn_created = time( NULL ); + +#ifdef LDAP_DEBUG + ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_debug, + INT_MAX, (void *)"ldap_" ); +#endif + +#ifdef LDAP_CONNECTIONLESS + if( proto == LDAP_PROTO_UDP ) return 0; +#endif + +#ifdef HAVE_TLS + if ((rc == 0 || rc == -2) && ( ld->ld_options.ldo_tls_mode == LDAP_OPT_X_TLS_HARD || + strcmp( srv->lud_scheme, "ldaps" ) == 0 )) + { + ++conn->lconn_refcnt; /* avoid premature free */ + + rc = ldap_int_tls_start( ld, conn, srv ); + + --conn->lconn_refcnt; + + if (rc != LDAP_SUCCESS) { + /* process connection callbacks */ + { + struct ldapoptions *lo; + ldaplist *ll; + ldap_conncb *cb; + + lo = &ld->ld_options; + LDAP_MUTEX_LOCK( &lo->ldo_mutex ); + if ( lo->ldo_conn_cbs ) { + for ( ll=lo->ldo_conn_cbs; ll; ll=ll->ll_next ) { + cb = ll->ll_data; + cb->lc_del( ld, conn->lconn_sb, cb ); + } + } + LDAP_MUTEX_UNLOCK( &lo->ldo_mutex ); + lo = LDAP_INT_GLOBAL_OPT(); + LDAP_MUTEX_LOCK( &lo->ldo_mutex ); + if ( lo->ldo_conn_cbs ) { + for ( ll=lo->ldo_conn_cbs; ll; ll=ll->ll_next ) { + cb = ll->ll_data; + cb->lc_del( ld, conn->lconn_sb, cb ); + } + } + LDAP_MUTEX_UNLOCK( &lo->ldo_mutex ); + } + ber_int_sb_close( conn->lconn_sb ); + return -1; + } + } +#endif + + return( 0 ); +} + +/* + * ldap_open_internal_connection - open connection and set file descriptor + * + * note: ldap_init_fd() may be preferable + */ + +int +ldap_open_internal_connection( LDAP **ldp, ber_socket_t *fdp ) +{ + int rc; + LDAPConn *c; + LDAPRequest *lr; + LDAP *ld; + + rc = ldap_create( &ld ); + if( rc != LDAP_SUCCESS ) { + *ldp = NULL; + return( rc ); + } + + /* Make it appear that a search request, msgid 0, was sent */ + lr = (LDAPRequest *)LDAP_CALLOC( 1, sizeof( LDAPRequest )); + if( lr == NULL ) { + ldap_unbind_ext( ld, NULL, NULL ); + *ldp = NULL; + return( LDAP_NO_MEMORY ); + } + memset(lr, 0, sizeof( LDAPRequest )); + lr->lr_msgid = 0; + lr->lr_status = LDAP_REQST_INPROGRESS; + lr->lr_res_errno = LDAP_SUCCESS; + /* no mutex lock needed, we just created this ld here */ + ld->ld_requests = lr; + + LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); + /* Attach the passed socket as the *LDAP's connection */ + c = ldap_new_connection( ld, NULL, 1, 0, NULL, 0, 0 ); + if( c == NULL ) { + ldap_unbind_ext( ld, NULL, NULL ); + *ldp = NULL; + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); + return( LDAP_NO_MEMORY ); + } + ber_sockbuf_ctrl( c->lconn_sb, LBER_SB_OPT_SET_FD, fdp ); +#ifdef LDAP_DEBUG + ber_sockbuf_add_io( c->lconn_sb, &ber_sockbuf_io_debug, + LBER_SBIOD_LEVEL_PROVIDER, (void *)"int_" ); +#endif + ber_sockbuf_add_io( c->lconn_sb, &ber_sockbuf_io_tcp, + LBER_SBIOD_LEVEL_PROVIDER, NULL ); + ld->ld_defconn = c; + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); + + /* Add the connection to the *LDAP's select pool */ + ldap_mark_select_read( ld, c->lconn_sb ); + + /* Make this connection an LDAP V3 protocol connection */ + rc = LDAP_VERSION3; + ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &rc ); + *ldp = ld; + + ++ld->ld_defconn->lconn_refcnt; /* so it never gets closed/freed */ + + return( LDAP_SUCCESS ); +} + +LDAP * +ldap_dup( LDAP *old ) +{ + LDAP *ld; + + if ( old == NULL ) { + return( NULL ); + } + + Debug( LDAP_DEBUG_TRACE, "ldap_dup\n", 0, 0, 0 ); + + if ( (ld = (LDAP *) LDAP_CALLOC( 1, sizeof(LDAP) )) == NULL ) { + return( NULL ); + } + + LDAP_MUTEX_LOCK( &old->ld_ldcmutex ); + ld->ldc = old->ldc; + old->ld_ldcrefcnt++; + LDAP_MUTEX_UNLOCK( &old->ld_ldcmutex ); + return ( ld ); +} + +int +ldap_int_check_async_open( LDAP *ld, ber_socket_t sd ) +{ + struct timeval tv = { 0 }; + int rc; + + rc = ldap_int_poll( ld, sd, &tv, 1 ); + switch ( rc ) { + case 0: + /* now ready to start tls */ + ld->ld_defconn->lconn_status = LDAP_CONNST_CONNECTED; + break; + + default: + ld->ld_errno = LDAP_CONNECT_ERROR; + return -1; + + case -2: + /* connect not completed yet */ + ld->ld_errno = LDAP_X_CONNECTING; + return rc; + } + +#ifdef HAVE_TLS + if ( ld->ld_options.ldo_tls_mode == LDAP_OPT_X_TLS_HARD || + !strcmp( ld->ld_defconn->lconn_server->lud_scheme, "ldaps" )) { + + ++ld->ld_defconn->lconn_refcnt; /* avoid premature free */ + + rc = ldap_int_tls_start( ld, ld->ld_defconn, ld->ld_defconn->lconn_server ); + + --ld->ld_defconn->lconn_refcnt; + } +#endif + return rc; +} diff --git a/libraries/libldap/options.c b/libraries/libldap/options.c new file mode 100644 index 0000000..c571d9d --- /dev/null +++ b/libraries/libldap/options.c @@ -0,0 +1,940 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +#define LDAP_OPT_REBIND_PROC 0x4e814d +#define LDAP_OPT_REBIND_PARAMS 0x4e814e + +#define LDAP_OPT_NEXTREF_PROC 0x4e815d +#define LDAP_OPT_NEXTREF_PARAMS 0x4e815e + +#define LDAP_OPT_URLLIST_PROC 0x4e816d +#define LDAP_OPT_URLLIST_PARAMS 0x4e816e + +static const LDAPAPIFeatureInfo features[] = { +#ifdef LDAP_API_FEATURE_X_OPENLDAP + { /* OpenLDAP Extensions API Feature */ + LDAP_FEATURE_INFO_VERSION, + "X_OPENLDAP", + LDAP_API_FEATURE_X_OPENLDAP + }, +#endif + +#ifdef LDAP_API_FEATURE_THREAD_SAFE + { /* Basic Thread Safe */ + LDAP_FEATURE_INFO_VERSION, + "THREAD_SAFE", + LDAP_API_FEATURE_THREAD_SAFE + }, +#endif +#ifdef LDAP_API_FEATURE_SESSION_THREAD_SAFE + { /* Session Thread Safe */ + LDAP_FEATURE_INFO_VERSION, + "SESSION_THREAD_SAFE", + LDAP_API_FEATURE_SESSION_THREAD_SAFE + }, +#endif +#ifdef LDAP_API_FEATURE_OPERATION_THREAD_SAFE + { /* Operation Thread Safe */ + LDAP_FEATURE_INFO_VERSION, + "OPERATION_THREAD_SAFE", + LDAP_API_FEATURE_OPERATION_THREAD_SAFE + }, +#endif +#ifdef LDAP_API_FEATURE_X_OPENLDAP_REENTRANT + { /* OpenLDAP Reentrant */ + LDAP_FEATURE_INFO_VERSION, + "X_OPENLDAP_REENTRANT", + LDAP_API_FEATURE_X_OPENLDAP_REENTRANT + }, +#endif +#if defined( LDAP_API_FEATURE_X_OPENLDAP_THREAD_SAFE ) && \ + defined( LDAP_THREAD_SAFE ) + { /* OpenLDAP Thread Safe */ + LDAP_FEATURE_INFO_VERSION, + "X_OPENLDAP_THREAD_SAFE", + LDAP_API_FEATURE_X_OPENLDAP_THREAD_SAFE + }, +#endif +#ifdef LDAP_API_FEATURE_X_OPENLDAP_V2_REFERRALS + { /* V2 Referrals */ + LDAP_FEATURE_INFO_VERSION, + "X_OPENLDAP_V2_REFERRALS", + LDAP_API_FEATURE_X_OPENLDAP_V2_REFERRALS + }, +#endif + {0, NULL, 0} +}; + +int +ldap_get_option( + LDAP *ld, + int option, + void *outvalue) +{ + struct ldapoptions *lo; + int rc = LDAP_OPT_ERROR; + + /* Get pointer to global option structure */ + lo = LDAP_INT_GLOBAL_OPT(); + if (NULL == lo) { + return LDAP_NO_MEMORY; + } + + if( lo->ldo_valid != LDAP_INITIALIZED ) { + ldap_int_initialize(lo, NULL); + if ( lo->ldo_valid != LDAP_INITIALIZED ) + return LDAP_LOCAL_ERROR; + } + + if(ld != NULL) { + assert( LDAP_VALID( ld ) ); + + if( !LDAP_VALID( ld ) ) { + return LDAP_OPT_ERROR; + } + + lo = &ld->ld_options; + } + + if(outvalue == NULL) { + /* no place to get to */ + return LDAP_OPT_ERROR; + } + + LDAP_MUTEX_LOCK( &lo->ldo_mutex ); + + switch(option) { + case LDAP_OPT_API_INFO: { + struct ldapapiinfo *info = (struct ldapapiinfo *) outvalue; + + if(info == NULL) { + /* outvalue must point to an apiinfo structure */ + break; /* LDAP_OPT_ERROR */ + } + + if(info->ldapai_info_version != LDAP_API_INFO_VERSION) { + /* api info version mismatch */ + info->ldapai_info_version = LDAP_API_INFO_VERSION; + break; /* LDAP_OPT_ERROR */ + } + + info->ldapai_api_version = LDAP_API_VERSION; + info->ldapai_protocol_version = LDAP_VERSION_MAX; + + if(features[0].ldapaif_name == NULL) { + info->ldapai_extensions = NULL; + } else { + int i; + info->ldapai_extensions = LDAP_MALLOC(sizeof(char *) * + sizeof(features)/sizeof(LDAPAPIFeatureInfo)); + + for(i=0; features[i].ldapaif_name != NULL; i++) { + info->ldapai_extensions[i] = + LDAP_STRDUP(features[i].ldapaif_name); + } + + info->ldapai_extensions[i] = NULL; + } + + info->ldapai_vendor_name = LDAP_STRDUP(LDAP_VENDOR_NAME); + info->ldapai_vendor_version = LDAP_VENDOR_VERSION; + + rc = LDAP_OPT_SUCCESS; + break; + } break; + + case LDAP_OPT_DESC: + if( ld == NULL || ld->ld_sb == NULL ) { + /* bad param */ + break; + } + + ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, outvalue ); + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_SOCKBUF: + if( ld == NULL ) break; + *(Sockbuf **)outvalue = ld->ld_sb; + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_TIMEOUT: + /* the caller has to free outvalue ! */ + if ( lo->ldo_tm_api.tv_sec < 0 ) { + *(void **)outvalue = NULL; + } else if ( ldap_int_timeval_dup( outvalue, &lo->ldo_tm_api ) != 0 ) { + break; /* LDAP_OPT_ERROR */ + } + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_NETWORK_TIMEOUT: + /* the caller has to free outvalue ! */ + if ( lo->ldo_tm_net.tv_sec < 0 ) { + *(void **)outvalue = NULL; + } else if ( ldap_int_timeval_dup( outvalue, &lo->ldo_tm_net ) != 0 ) { + break; /* LDAP_OPT_ERROR */ + } + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_DEREF: + * (int *) outvalue = lo->ldo_deref; + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_SIZELIMIT: + * (int *) outvalue = lo->ldo_sizelimit; + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_TIMELIMIT: + * (int *) outvalue = lo->ldo_timelimit; + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_REFERRALS: + * (int *) outvalue = (int) LDAP_BOOL_GET(lo, LDAP_BOOL_REFERRALS); + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_RESTART: + * (int *) outvalue = (int) LDAP_BOOL_GET(lo, LDAP_BOOL_RESTART); + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_PROTOCOL_VERSION: + * (int *) outvalue = lo->ldo_version; + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_SERVER_CONTROLS: + * (LDAPControl ***) outvalue = + ldap_controls_dup( lo->ldo_sctrls ); + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_CLIENT_CONTROLS: + * (LDAPControl ***) outvalue = + ldap_controls_dup( lo->ldo_cctrls ); + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_HOST_NAME: + * (char **) outvalue = ldap_url_list2hosts(lo->ldo_defludp); + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_URI: + * (char **) outvalue = ldap_url_list2urls(lo->ldo_defludp); + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_DEFBASE: + if( lo->ldo_defbase == NULL ) { + * (char **) outvalue = NULL; + } else { + * (char **) outvalue = LDAP_STRDUP(lo->ldo_defbase); + } + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_CONNECT_ASYNC: + * (int *) outvalue = (int) LDAP_BOOL_GET(lo, LDAP_BOOL_CONNECT_ASYNC); + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_CONNECT_CB: + { + /* Getting deletes the specified callback */ + ldaplist **ll = &lo->ldo_conn_cbs; + for (;*ll;ll = &(*ll)->ll_next) { + if ((*ll)->ll_data == outvalue) { + ldaplist *lc = *ll; + *ll = lc->ll_next; + LDAP_FREE(lc); + break; + } + } + } + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_RESULT_CODE: + if(ld == NULL) { + /* bad param */ + break; + } + * (int *) outvalue = ld->ld_errno; + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_DIAGNOSTIC_MESSAGE: + if(ld == NULL) { + /* bad param */ + break; + } + + if( ld->ld_error == NULL ) { + * (char **) outvalue = NULL; + } else { + * (char **) outvalue = LDAP_STRDUP(ld->ld_error); + } + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_MATCHED_DN: + if(ld == NULL) { + /* bad param */ + break; + } + + if( ld->ld_matched == NULL ) { + * (char **) outvalue = NULL; + } else { + * (char **) outvalue = LDAP_STRDUP( ld->ld_matched ); + } + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_REFERRAL_URLS: + if(ld == NULL) { + /* bad param */ + break; + } + + if( ld->ld_referrals == NULL ) { + * (char ***) outvalue = NULL; + } else { + * (char ***) outvalue = ldap_value_dup(ld->ld_referrals); + } + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_API_FEATURE_INFO: { + LDAPAPIFeatureInfo *info = (LDAPAPIFeatureInfo *) outvalue; + int i; + + if(info == NULL) + break; /* LDAP_OPT_ERROR */ + + if(info->ldapaif_info_version != LDAP_FEATURE_INFO_VERSION) { + /* api info version mismatch */ + info->ldapaif_info_version = LDAP_FEATURE_INFO_VERSION; + break; /* LDAP_OPT_ERROR */ + } + + if(info->ldapaif_name == NULL) + break; /* LDAP_OPT_ERROR */ + + for(i=0; features[i].ldapaif_name != NULL; i++) { + if(!strcmp(info->ldapaif_name, features[i].ldapaif_name)) { + info->ldapaif_version = + features[i].ldapaif_version; + rc = LDAP_OPT_SUCCESS; + break; + } + } + } + break; + + case LDAP_OPT_DEBUG_LEVEL: + * (int *) outvalue = lo->ldo_debug; + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_SESSION_REFCNT: + if(ld == NULL) { + /* bad param */ + break; + } + LDAP_MUTEX_LOCK( &ld->ld_ldcmutex ); + * (int *) outvalue = ld->ld_ldcrefcnt; + LDAP_MUTEX_UNLOCK( &ld->ld_ldcmutex ); + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_X_KEEPALIVE_IDLE: + * (int *) outvalue = lo->ldo_keepalive_idle; + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_X_KEEPALIVE_PROBES: + * (int *) outvalue = lo->ldo_keepalive_probes; + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_X_KEEPALIVE_INTERVAL: + * (int *) outvalue = lo->ldo_keepalive_interval; + rc = LDAP_OPT_SUCCESS; + break; + + default: +#ifdef HAVE_TLS + if ( ldap_pvt_tls_get_option( ld, option, outvalue ) == 0 ) { + rc = LDAP_OPT_SUCCESS; + break; + } +#endif +#ifdef HAVE_CYRUS_SASL + if ( ldap_int_sasl_get_option( ld, option, outvalue ) == 0 ) { + rc = LDAP_OPT_SUCCESS; + break; + } +#endif +#ifdef HAVE_GSSAPI + if ( ldap_int_gssapi_get_option( ld, option, outvalue ) == 0 ) { + rc = LDAP_OPT_SUCCESS; + break; + } +#endif + /* bad param */ + break; + } + + LDAP_MUTEX_UNLOCK( &lo->ldo_mutex ); + return ( rc ); +} + +int +ldap_set_option( + LDAP *ld, + int option, + LDAP_CONST void *invalue) +{ + struct ldapoptions *lo; + int *dbglvl = NULL; + int rc = LDAP_OPT_ERROR; + + /* Get pointer to global option structure */ + lo = LDAP_INT_GLOBAL_OPT(); + if (lo == NULL) { + return LDAP_NO_MEMORY; + } + + /* + * The architecture to turn on debugging has a chicken and egg + * problem. Thus, we introduce a fix here. + */ + + if (option == LDAP_OPT_DEBUG_LEVEL) { + dbglvl = (int *) invalue; + } + + if( lo->ldo_valid != LDAP_INITIALIZED ) { + ldap_int_initialize(lo, dbglvl); + if ( lo->ldo_valid != LDAP_INITIALIZED ) + return LDAP_LOCAL_ERROR; + } + + if(ld != NULL) { + assert( LDAP_VALID( ld ) ); + + if( !LDAP_VALID( ld ) ) { + return LDAP_OPT_ERROR; + } + + lo = &ld->ld_options; + } + + LDAP_MUTEX_LOCK( &lo->ldo_mutex ); + + switch ( option ) { + + /* options with boolean values */ + case LDAP_OPT_REFERRALS: + if(invalue == LDAP_OPT_OFF) { + LDAP_BOOL_CLR(lo, LDAP_BOOL_REFERRALS); + } else { + LDAP_BOOL_SET(lo, LDAP_BOOL_REFERRALS); + } + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_RESTART: + if(invalue == LDAP_OPT_OFF) { + LDAP_BOOL_CLR(lo, LDAP_BOOL_RESTART); + } else { + LDAP_BOOL_SET(lo, LDAP_BOOL_RESTART); + } + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_CONNECT_ASYNC: + if(invalue == LDAP_OPT_OFF) { + LDAP_BOOL_CLR(lo, LDAP_BOOL_CONNECT_ASYNC); + } else { + LDAP_BOOL_SET(lo, LDAP_BOOL_CONNECT_ASYNC); + } + rc = LDAP_OPT_SUCCESS; + break; + + /* options which can withstand invalue == NULL */ + case LDAP_OPT_SERVER_CONTROLS: { + LDAPControl *const *controls = + (LDAPControl *const *) invalue; + + if( lo->ldo_sctrls ) + ldap_controls_free( lo->ldo_sctrls ); + + if( controls == NULL || *controls == NULL ) { + lo->ldo_sctrls = NULL; + rc = LDAP_OPT_SUCCESS; + break; + } + + lo->ldo_sctrls = ldap_controls_dup( controls ); + + if(lo->ldo_sctrls == NULL) { + /* memory allocation error ? */ + break; /* LDAP_OPT_ERROR */ + } + } + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_CLIENT_CONTROLS: { + LDAPControl *const *controls = + (LDAPControl *const *) invalue; + + if( lo->ldo_cctrls ) + ldap_controls_free( lo->ldo_cctrls ); + + if( controls == NULL || *controls == NULL ) { + lo->ldo_cctrls = NULL; + rc = LDAP_OPT_SUCCESS; + break; + } + + lo->ldo_cctrls = ldap_controls_dup( controls ); + + if(lo->ldo_cctrls == NULL) { + /* memory allocation error ? */ + break; /* LDAP_OPT_ERROR */ + } + } + rc = LDAP_OPT_SUCCESS; + break; + + + case LDAP_OPT_HOST_NAME: { + const char *host = (const char *) invalue; + LDAPURLDesc *ludlist = NULL; + rc = LDAP_OPT_SUCCESS; + + if(host != NULL) { + rc = ldap_url_parsehosts( &ludlist, host, + lo->ldo_defport ? lo->ldo_defport : LDAP_PORT ); + + } else if(ld == NULL) { + /* + * must want global default returned + * to initial condition. + */ + rc = ldap_url_parselist_ext(&ludlist, "ldap://localhost/", NULL, + LDAP_PVT_URL_PARSE_NOEMPTY_HOST + | LDAP_PVT_URL_PARSE_DEF_PORT ); + + } else { + /* + * must want the session default + * updated to the current global default + */ + ludlist = ldap_url_duplist( + ldap_int_global_options.ldo_defludp); + if (ludlist == NULL) + rc = LDAP_NO_MEMORY; + } + + if (rc == LDAP_OPT_SUCCESS) { + if (lo->ldo_defludp != NULL) + ldap_free_urllist(lo->ldo_defludp); + lo->ldo_defludp = ludlist; + } + break; + } + + case LDAP_OPT_URI: { + const char *urls = (const char *) invalue; + LDAPURLDesc *ludlist = NULL; + rc = LDAP_OPT_SUCCESS; + + if(urls != NULL) { + rc = ldap_url_parselist_ext(&ludlist, urls, NULL, + LDAP_PVT_URL_PARSE_NOEMPTY_HOST + | LDAP_PVT_URL_PARSE_DEF_PORT ); + } else if(ld == NULL) { + /* + * must want global default returned + * to initial condition. + */ + rc = ldap_url_parselist_ext(&ludlist, "ldap://localhost/", NULL, + LDAP_PVT_URL_PARSE_NOEMPTY_HOST + | LDAP_PVT_URL_PARSE_DEF_PORT ); + + } else { + /* + * must want the session default + * updated to the current global default + */ + ludlist = ldap_url_duplist( + ldap_int_global_options.ldo_defludp); + if (ludlist == NULL) + rc = LDAP_URL_ERR_MEM; + } + + switch (rc) { + case LDAP_URL_SUCCESS: /* Success */ + rc = LDAP_SUCCESS; + break; + + case LDAP_URL_ERR_MEM: /* can't allocate memory space */ + rc = LDAP_NO_MEMORY; + break; + + case LDAP_URL_ERR_PARAM: /* parameter is bad */ + case LDAP_URL_ERR_BADSCHEME: /* URL doesn't begin with "ldap[si]://" */ + case LDAP_URL_ERR_BADENCLOSURE: /* URL is missing trailing ">" */ + case LDAP_URL_ERR_BADURL: /* URL is bad */ + case LDAP_URL_ERR_BADHOST: /* host port is bad */ + case LDAP_URL_ERR_BADATTRS: /* bad (or missing) attributes */ + case LDAP_URL_ERR_BADSCOPE: /* scope string is invalid (or missing) */ + case LDAP_URL_ERR_BADFILTER: /* bad or missing filter */ + case LDAP_URL_ERR_BADEXTS: /* bad or missing extensions */ + rc = LDAP_PARAM_ERROR; + break; + } + + if (rc == LDAP_SUCCESS) { + if (lo->ldo_defludp != NULL) + ldap_free_urllist(lo->ldo_defludp); + lo->ldo_defludp = ludlist; + } + break; + } + + case LDAP_OPT_DEFBASE: { + const char *newbase = (const char *) invalue; + char *defbase = NULL; + + if ( newbase != NULL ) { + defbase = LDAP_STRDUP( newbase ); + if ( defbase == NULL ) { + rc = LDAP_NO_MEMORY; + break; + } + + } else if ( ld != NULL ) { + defbase = LDAP_STRDUP( ldap_int_global_options.ldo_defbase ); + if ( defbase == NULL ) { + rc = LDAP_NO_MEMORY; + break; + } + } + + if ( lo->ldo_defbase != NULL ) + LDAP_FREE( lo->ldo_defbase ); + lo->ldo_defbase = defbase; + } + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_DIAGNOSTIC_MESSAGE: { + const char *err = (const char *) invalue; + + if(ld == NULL) { + /* need a struct ldap */ + break; /* LDAP_OPT_ERROR */ + } + + if( ld->ld_error ) { + LDAP_FREE(ld->ld_error); + ld->ld_error = NULL; + } + + if ( err ) { + ld->ld_error = LDAP_STRDUP(err); + } + } + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_MATCHED_DN: { + const char *matched = (const char *) invalue; + + if (ld == NULL) { + /* need a struct ldap */ + break; /* LDAP_OPT_ERROR */ + } + + if( ld->ld_matched ) { + LDAP_FREE(ld->ld_matched); + ld->ld_matched = NULL; + } + + if ( matched ) { + ld->ld_matched = LDAP_STRDUP( matched ); + } + } + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_REFERRAL_URLS: { + char *const *referrals = (char *const *) invalue; + + if(ld == NULL) { + /* need a struct ldap */ + break; /* LDAP_OPT_ERROR */ + } + + if( ld->ld_referrals ) { + LDAP_VFREE(ld->ld_referrals); + } + + if ( referrals ) { + ld->ld_referrals = ldap_value_dup(referrals); + } + } + rc = LDAP_OPT_SUCCESS; + break; + + /* Only accessed from inside this function by ldap_set_rebind_proc() */ + case LDAP_OPT_REBIND_PROC: { + lo->ldo_rebind_proc = (LDAP_REBIND_PROC *)invalue; + } + rc = LDAP_OPT_SUCCESS; + break; + case LDAP_OPT_REBIND_PARAMS: { + lo->ldo_rebind_params = (void *)invalue; + } + rc = LDAP_OPT_SUCCESS; + break; + + /* Only accessed from inside this function by ldap_set_nextref_proc() */ + case LDAP_OPT_NEXTREF_PROC: { + lo->ldo_nextref_proc = (LDAP_NEXTREF_PROC *)invalue; + } + rc = LDAP_OPT_SUCCESS; + break; + case LDAP_OPT_NEXTREF_PARAMS: { + lo->ldo_nextref_params = (void *)invalue; + } + rc = LDAP_OPT_SUCCESS; + break; + + /* Only accessed from inside this function by ldap_set_urllist_proc() */ + case LDAP_OPT_URLLIST_PROC: { + lo->ldo_urllist_proc = (LDAP_URLLIST_PROC *)invalue; + } + rc = LDAP_OPT_SUCCESS; + break; + case LDAP_OPT_URLLIST_PARAMS: { + lo->ldo_urllist_params = (void *)invalue; + } + rc = LDAP_OPT_SUCCESS; + break; + + /* read-only options */ + case LDAP_OPT_API_INFO: + case LDAP_OPT_DESC: + case LDAP_OPT_SOCKBUF: + case LDAP_OPT_API_FEATURE_INFO: + break; /* LDAP_OPT_ERROR */ + + /* options which cannot withstand invalue == NULL */ + case LDAP_OPT_DEREF: + case LDAP_OPT_SIZELIMIT: + case LDAP_OPT_TIMELIMIT: + case LDAP_OPT_PROTOCOL_VERSION: + case LDAP_OPT_RESULT_CODE: + case LDAP_OPT_DEBUG_LEVEL: + case LDAP_OPT_TIMEOUT: + case LDAP_OPT_NETWORK_TIMEOUT: + case LDAP_OPT_CONNECT_CB: + case LDAP_OPT_X_KEEPALIVE_IDLE: + case LDAP_OPT_X_KEEPALIVE_PROBES : + case LDAP_OPT_X_KEEPALIVE_INTERVAL : + if(invalue == NULL) { + /* no place to set from */ + LDAP_MUTEX_UNLOCK( &lo->ldo_mutex ); + return ( LDAP_OPT_ERROR ); + } + break; + + default: +#ifdef HAVE_TLS + if ( ldap_pvt_tls_set_option( ld, option, (void *)invalue ) == 0 ) { + LDAP_MUTEX_UNLOCK( &lo->ldo_mutex ); + return ( LDAP_OPT_SUCCESS ); + } +#endif +#ifdef HAVE_CYRUS_SASL + if ( ldap_int_sasl_set_option( ld, option, (void *)invalue ) == 0 ) { + LDAP_MUTEX_UNLOCK( &lo->ldo_mutex ); + return ( LDAP_OPT_SUCCESS ); + } +#endif +#ifdef HAVE_GSSAPI + if ( ldap_int_gssapi_set_option( ld, option, (void *)invalue ) == 0 ) { + LDAP_MUTEX_UNLOCK( &lo->ldo_mutex ); + return ( LDAP_OPT_SUCCESS ); + } +#endif + /* bad param */ + break; /* LDAP_OPT_ERROR */ + } + + /* options which cannot withstand invalue == NULL */ + + switch(option) { + case LDAP_OPT_DEREF: + /* FIXME: check value for protocol compliance? */ + lo->ldo_deref = * (const int *) invalue; + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_SIZELIMIT: + /* FIXME: check value for protocol compliance? */ + lo->ldo_sizelimit = * (const int *) invalue; + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_TIMELIMIT: + /* FIXME: check value for protocol compliance? */ + lo->ldo_timelimit = * (const int *) invalue; + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_TIMEOUT: { + const struct timeval *tv = + (const struct timeval *) invalue; + + lo->ldo_tm_api = *tv; + } + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_NETWORK_TIMEOUT: { + const struct timeval *tv = + (const struct timeval *) invalue; + + lo->ldo_tm_net = *tv; + } + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_PROTOCOL_VERSION: { + int vers = * (const int *) invalue; + if (vers < LDAP_VERSION_MIN || vers > LDAP_VERSION_MAX) { + /* not supported */ + break; + } + lo->ldo_version = vers; + } + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_RESULT_CODE: { + int err = * (const int *) invalue; + + if(ld == NULL) { + /* need a struct ldap */ + break; + } + + ld->ld_errno = err; + } + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_DEBUG_LEVEL: + lo->ldo_debug = * (const int *) invalue; + rc = LDAP_OPT_SUCCESS; + break; + + case LDAP_OPT_CONNECT_CB: + { + /* setting pushes the callback */ + ldaplist *ll; + ll = LDAP_MALLOC( sizeof( *ll )); + ll->ll_data = (void *)invalue; + ll->ll_next = lo->ldo_conn_cbs; + lo->ldo_conn_cbs = ll; + } + rc = LDAP_OPT_SUCCESS; + break; + case LDAP_OPT_X_KEEPALIVE_IDLE: + lo->ldo_keepalive_idle = * (const int *) invalue; + rc = LDAP_OPT_SUCCESS; + break; + case LDAP_OPT_X_KEEPALIVE_PROBES : + lo->ldo_keepalive_probes = * (const int *) invalue; + rc = LDAP_OPT_SUCCESS; + break; + case LDAP_OPT_X_KEEPALIVE_INTERVAL : + lo->ldo_keepalive_interval = * (const int *) invalue; + rc = LDAP_OPT_SUCCESS; + break; + + } + LDAP_MUTEX_UNLOCK( &lo->ldo_mutex ); + return ( rc ); +} + +int +ldap_set_rebind_proc( LDAP *ld, LDAP_REBIND_PROC *proc, void *params ) +{ + int rc; + rc = ldap_set_option( ld, LDAP_OPT_REBIND_PROC, (void *)proc ); + if( rc != LDAP_OPT_SUCCESS ) return rc; + + rc = ldap_set_option( ld, LDAP_OPT_REBIND_PARAMS, (void *)params ); + return rc; +} + +int +ldap_set_nextref_proc( LDAP *ld, LDAP_NEXTREF_PROC *proc, void *params ) +{ + int rc; + rc = ldap_set_option( ld, LDAP_OPT_NEXTREF_PROC, (void *)proc ); + if( rc != LDAP_OPT_SUCCESS ) return rc; + + rc = ldap_set_option( ld, LDAP_OPT_NEXTREF_PARAMS, (void *)params ); + return rc; +} + +int +ldap_set_urllist_proc( LDAP *ld, LDAP_URLLIST_PROC *proc, void *params ) +{ + int rc; + rc = ldap_set_option( ld, LDAP_OPT_URLLIST_PROC, (void *)proc ); + if( rc != LDAP_OPT_SUCCESS ) return rc; + + rc = ldap_set_option( ld, LDAP_OPT_URLLIST_PARAMS, (void *)params ); + return rc; +} diff --git a/libraries/libldap/os-ip.c b/libraries/libldap/os-ip.c new file mode 100644 index 0000000..9d9242a --- /dev/null +++ b/libraries/libldap/os-ip.c @@ -0,0 +1,1154 @@ +/* os-ip.c -- platform-specific TCP & UDP related code */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * Portions Copyright 1999 Lars Uffmann. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1995 Regents of the University of Michigan. + * All rights reserved. + */ +/* Significant additional contributors include: + * Lars Uffman + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/errno.h> +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> +#include <ac/unistd.h> + +#ifdef HAVE_IO_H +#include <io.h> +#endif /* HAVE_IO_H */ +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif + +#include "ldap-int.h" + +#if defined( HAVE_GETADDRINFO ) && defined( HAVE_INET_NTOP ) +# ifdef LDAP_PF_INET6 +int ldap_int_inet4or6 = AF_UNSPEC; +# else +int ldap_int_inet4or6 = AF_INET; +# endif +#endif + +#ifdef LDAP_DEBUG + +#define osip_debug(ld,fmt,arg1,arg2,arg3) \ +do { \ + ldap_log_printf(NULL, LDAP_DEBUG_TRACE, fmt, arg1, arg2, arg3); \ +} while(0) + +#else + +#define osip_debug(ld,fmt,arg1,arg2,arg3) ((void)0) + +#endif /* LDAP_DEBUG */ + +static void +ldap_pvt_set_errno(int err) +{ + sock_errset(err); +} + +int +ldap_int_timeval_dup( struct timeval **dest, const struct timeval *src ) +{ + struct timeval *new; + + assert( dest != NULL ); + + if (src == NULL) { + *dest = NULL; + return 0; + } + + new = (struct timeval *) LDAP_MALLOC(sizeof(struct timeval)); + + if( new == NULL ) { + *dest = NULL; + return 1; + } + + AC_MEMCPY( (char *) new, (const char *) src, sizeof(struct timeval)); + + *dest = new; + return 0; +} + +static int +ldap_pvt_ndelay_on(LDAP *ld, int fd) +{ + osip_debug(ld, "ldap_ndelay_on: %d\n",fd,0,0); + return ber_pvt_socket_set_nonblock( fd, 1 ); +} + +static int +ldap_pvt_ndelay_off(LDAP *ld, int fd) +{ + osip_debug(ld, "ldap_ndelay_off: %d\n",fd,0,0); + return ber_pvt_socket_set_nonblock( fd, 0 ); +} + +static ber_socket_t +ldap_int_socket(LDAP *ld, int family, int type ) +{ + ber_socket_t s = socket(family, type, 0); + osip_debug(ld, "ldap_new_socket: %d\n",s,0,0); +#ifdef FD_CLOEXEC + fcntl(s, F_SETFD, FD_CLOEXEC); +#endif + return ( s ); +} + +static int +ldap_pvt_close_socket(LDAP *ld, int s) +{ + osip_debug(ld, "ldap_close_socket: %d\n",s,0,0); + return tcp_close(s); +} + +static int +ldap_int_prepare_socket(LDAP *ld, int s, int proto ) +{ + osip_debug( ld, "ldap_prepare_socket: %d\n", s, 0, 0 ); + +#if defined( SO_KEEPALIVE ) || defined( TCP_NODELAY ) + if ( proto == LDAP_PROTO_TCP ) { + int dummy = 1; +#ifdef SO_KEEPALIVE + if ( setsockopt( s, SOL_SOCKET, SO_KEEPALIVE, + (char*) &dummy, sizeof(dummy) ) == AC_SOCKET_ERROR ) + { + osip_debug( ld, "ldap_prepare_socket: " + "setsockopt(%d, SO_KEEPALIVE) failed (ignored).\n", + s, 0, 0 ); + } + if ( ld->ld_options.ldo_keepalive_idle > 0 ) + { +#ifdef TCP_KEEPIDLE + if ( setsockopt( s, IPPROTO_TCP, TCP_KEEPIDLE, + (void*) &ld->ld_options.ldo_keepalive_idle, + sizeof(ld->ld_options.ldo_keepalive_idle) ) == AC_SOCKET_ERROR ) + { + osip_debug( ld, "ldap_prepare_socket: " + "setsockopt(%d, TCP_KEEPIDLE) failed (ignored).\n", + s, 0, 0 ); + } +#else + osip_debug( ld, "ldap_prepare_socket: " + "sockopt TCP_KEEPIDLE not supported on this system.\n", + 0, 0, 0 ); +#endif /* TCP_KEEPIDLE */ + } + if ( ld->ld_options.ldo_keepalive_probes > 0 ) + { +#ifdef TCP_KEEPCNT + if ( setsockopt( s, IPPROTO_TCP, TCP_KEEPCNT, + (void*) &ld->ld_options.ldo_keepalive_probes, + sizeof(ld->ld_options.ldo_keepalive_probes) ) == AC_SOCKET_ERROR ) + { + osip_debug( ld, "ldap_prepare_socket: " + "setsockopt(%d, TCP_KEEPCNT) failed (ignored).\n", + s, 0, 0 ); + } +#else + osip_debug( ld, "ldap_prepare_socket: " + "sockopt TCP_KEEPCNT not supported on this system.\n", + 0, 0, 0 ); +#endif /* TCP_KEEPCNT */ + } + if ( ld->ld_options.ldo_keepalive_interval > 0 ) + { +#ifdef TCP_KEEPINTVL + if ( setsockopt( s, IPPROTO_TCP, TCP_KEEPINTVL, + (void*) &ld->ld_options.ldo_keepalive_interval, + sizeof(ld->ld_options.ldo_keepalive_interval) ) == AC_SOCKET_ERROR ) + { + osip_debug( ld, "ldap_prepare_socket: " + "setsockopt(%d, TCP_KEEPINTVL) failed (ignored).\n", + s, 0, 0 ); + } +#else + osip_debug( ld, "ldap_prepare_socket: " + "sockopt TCP_KEEPINTVL not supported on this system.\n", + 0, 0, 0 ); +#endif /* TCP_KEEPINTVL */ + } +#endif /* SO_KEEPALIVE */ +#ifdef TCP_NODELAY + if ( setsockopt( s, IPPROTO_TCP, TCP_NODELAY, + (char*) &dummy, sizeof(dummy) ) == AC_SOCKET_ERROR ) + { + osip_debug( ld, "ldap_prepare_socket: " + "setsockopt(%d, TCP_NODELAY) failed (ignored).\n", + s, 0, 0 ); + } +#endif /* TCP_NODELAY */ + } +#endif /* SO_KEEPALIVE || TCP_NODELAY */ + + return 0; +} + +#ifndef HAVE_WINSOCK + +#undef TRACE +#define TRACE do { \ + osip_debug(ld, \ + "ldap_is_socket_ready: error on socket %d: errno: %d (%s)\n", \ + s, \ + errno, \ + sock_errstr(errno) ); \ +} while( 0 ) + +/* + * check the socket for errors after select returned. + */ +static int +ldap_pvt_is_socket_ready(LDAP *ld, int s) +{ + osip_debug(ld, "ldap_is_sock_ready: %d\n",s,0,0); + +#if defined( notyet ) /* && defined( SO_ERROR ) */ +{ + int so_errno; + ber_socklen_t dummy = sizeof(so_errno); + if ( getsockopt( s, SOL_SOCKET, SO_ERROR, &so_errno, &dummy ) + == AC_SOCKET_ERROR ) + { + return -1; + } + if ( so_errno ) { + ldap_pvt_set_errno(so_errno); + TRACE; + return -1; + } + return 0; +} +#else +{ + /* error slippery */ +#ifdef LDAP_PF_INET6 + struct sockaddr_storage sin; +#else + struct sockaddr_in sin; +#endif + char ch; + ber_socklen_t dummy = sizeof(sin); + if ( getpeername( s, (struct sockaddr *) &sin, &dummy ) + == AC_SOCKET_ERROR ) + { + /* XXX: needs to be replace with ber_stream_read() */ + (void)read(s, &ch, 1); + TRACE; + return -1; + } + return 0; +} +#endif + return -1; +} +#undef TRACE + +#endif /* HAVE_WINSOCK */ + +/* NOTE: this is identical to analogous code in os-local.c */ +int +ldap_int_poll( + LDAP *ld, + ber_socket_t s, + struct timeval *tvp, + int wr ) +{ + int rc; + + + osip_debug(ld, "ldap_int_poll: fd: %d tm: %ld\n", + s, tvp ? tvp->tv_sec : -1L, 0); + +#ifdef HAVE_POLL + { + struct pollfd fd; + int timeout = INFTIM; + short event = wr ? POLL_WRITE : POLL_READ; + + fd.fd = s; + fd.events = event; + + if ( tvp != NULL ) { + timeout = TV2MILLISEC( tvp ); + } + do { + fd.revents = 0; + rc = poll( &fd, 1, timeout ); + + } while ( rc == AC_SOCKET_ERROR && errno == EINTR && + LDAP_BOOL_GET( &ld->ld_options, LDAP_BOOL_RESTART ) ); + + if ( rc == AC_SOCKET_ERROR ) { + return rc; + } + + if ( timeout == 0 && rc == 0 ) { + return -2; + } + + if ( fd.revents & event ) { + if ( ldap_pvt_is_socket_ready( ld, s ) == -1 ) { + return -1; + } + + if ( ldap_pvt_ndelay_off( ld, s ) == -1 ) { + return -1; + } + return 0; + } + } +#else + { + fd_set wfds, *z = NULL; +#ifdef HAVE_WINSOCK + fd_set efds; +#endif + struct timeval tv = { 0 }; + +#if defined( FD_SETSIZE ) && !defined( HAVE_WINSOCK ) + if ( s >= FD_SETSIZE ) { + rc = AC_SOCKET_ERROR; + tcp_close( s ); + ldap_pvt_set_errno( EMFILE ); + return rc; + } +#endif + + if ( tvp != NULL ) { + tv = *tvp; + } + + do { + FD_ZERO(&wfds); + FD_SET(s, &wfds ); + +#ifdef HAVE_WINSOCK + FD_ZERO(&efds); + FD_SET(s, &efds ); +#endif + + rc = select( ldap_int_tblsize, z, &wfds, +#ifdef HAVE_WINSOCK + &efds, +#else + z, +#endif + tvp ? &tv : NULL ); + } while ( rc == AC_SOCKET_ERROR && errno == EINTR && + LDAP_BOOL_GET( &ld->ld_options, LDAP_BOOL_RESTART ) ); + + if ( rc == AC_SOCKET_ERROR ) { + return rc; + } + + if ( rc == 0 && tvp && tvp->tv_sec == 0 && tvp->tv_usec == 0 ) { + return -2; + } + +#ifdef HAVE_WINSOCK + /* This means the connection failed */ + if ( FD_ISSET(s, &efds) ) { + int so_errno; + ber_socklen_t dummy = sizeof(so_errno); + if ( getsockopt( s, SOL_SOCKET, SO_ERROR, + (char *) &so_errno, &dummy ) == AC_SOCKET_ERROR || !so_errno ) + { + /* impossible */ + so_errno = WSAGetLastError(); + } + ldap_pvt_set_errno( so_errno ); + osip_debug(ld, "ldap_int_poll: error on socket %d: " + "errno: %d (%s)\n", s, errno, sock_errstr( errno )); + return -1; + } +#endif + if ( FD_ISSET(s, &wfds) ) { +#ifndef HAVE_WINSOCK + if ( ldap_pvt_is_socket_ready( ld, s ) == -1 ) { + return -1; + } +#endif + if ( ldap_pvt_ndelay_off(ld, s) == -1 ) { + return -1; + } + return 0; + } + } +#endif + + osip_debug(ld, "ldap_int_poll: timed out\n",0,0,0); + ldap_pvt_set_errno( ETIMEDOUT ); + return -1; +} + +static int +ldap_pvt_connect(LDAP *ld, ber_socket_t s, + struct sockaddr *sin, ber_socklen_t addrlen, + int async) +{ + int rc, err; + struct timeval tv, *opt_tv = NULL; + +#ifdef LDAP_CONNECTIONLESS + /* We could do a connect() but that would interfere with + * attempts to poll a broadcast address + */ + if (LDAP_IS_UDP(ld)) { + if (ld->ld_options.ldo_peer) + ldap_memfree(ld->ld_options.ldo_peer); + ld->ld_options.ldo_peer=ldap_memcalloc(1, sizeof(struct sockaddr_storage)); + AC_MEMCPY(ld->ld_options.ldo_peer,sin,addrlen); + return ( 0 ); + } +#endif + if ( ld->ld_options.ldo_tm_net.tv_sec >= 0 ) { + tv = ld->ld_options.ldo_tm_net; + opt_tv = &tv; + } + + osip_debug(ld, "ldap_pvt_connect: fd: %d tm: %ld async: %d\n", + s, opt_tv ? tv.tv_sec : -1L, async); + + if ( opt_tv && ldap_pvt_ndelay_on(ld, s) == -1 ) + return ( -1 ); + + do{ + osip_debug(ld, "attempting to connect: \n", 0, 0, 0); + if ( connect(s, sin, addrlen) != AC_SOCKET_ERROR ) { + osip_debug(ld, "connect success\n", 0, 0, 0); + + if ( !async && opt_tv && ldap_pvt_ndelay_off(ld, s) == -1 ) + return ( -1 ); + return ( 0 ); + } + err = sock_errno(); + osip_debug(ld, "connect errno: %d\n", err, 0, 0); + + } while(err == EINTR && + LDAP_BOOL_GET( &ld->ld_options, LDAP_BOOL_RESTART )); + + if ( err != EINPROGRESS && err != EWOULDBLOCK ) { + return ( -1 ); + } + + if ( async ) { + /* caller will call ldap_int_poll() as appropriate? */ + return ( -2 ); + } + + rc = ldap_int_poll( ld, s, opt_tv, 1 ); + + osip_debug(ld, "ldap_pvt_connect: %d\n", rc, 0, 0); + + return rc; +} + +#ifndef HAVE_INET_ATON +int +ldap_pvt_inet_aton( const char *host, struct in_addr *in) +{ + unsigned long u = inet_addr( host ); + +#ifdef INADDR_NONE + if ( u == INADDR_NONE ) return 0; +#endif + if ( u == 0xffffffffUL || u == (unsigned long) -1L ) return 0; + + in->s_addr = u; + return 1; +} +#endif + +int +ldap_int_connect_cbs(LDAP *ld, Sockbuf *sb, ber_socket_t *s, LDAPURLDesc *srv, struct sockaddr *addr) +{ + struct ldapoptions *lo; + ldaplist *ll; + ldap_conncb *cb; + int rc; + + ber_sockbuf_ctrl( sb, LBER_SB_OPT_SET_FD, s ); + + /* Invoke all handle-specific callbacks first */ + lo = &ld->ld_options; + for (ll = lo->ldo_conn_cbs; ll; ll = ll->ll_next) { + cb = ll->ll_data; + rc = cb->lc_add( ld, sb, srv, addr, cb ); + /* on any failure, call the teardown functions for anything + * that previously succeeded + */ + if ( rc ) { + ldaplist *l2; + for (l2 = lo->ldo_conn_cbs; l2 != ll; l2 = l2->ll_next) { + cb = l2->ll_data; + cb->lc_del( ld, sb, cb ); + } + /* a failure might have implicitly closed the fd */ + ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, s ); + return rc; + } + } + lo = LDAP_INT_GLOBAL_OPT(); + for (ll = lo->ldo_conn_cbs; ll; ll = ll->ll_next) { + cb = ll->ll_data; + rc = cb->lc_add( ld, sb, srv, addr, cb ); + if ( rc ) { + ldaplist *l2; + for (l2 = lo->ldo_conn_cbs; l2 != ll; l2 = l2->ll_next) { + cb = l2->ll_data; + cb->lc_del( ld, sb, cb ); + } + lo = &ld->ld_options; + for (l2 = lo->ldo_conn_cbs; l2; l2 = l2->ll_next) { + cb = l2->ll_data; + cb->lc_del( ld, sb, cb ); + } + ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, s ); + return rc; + } + } + return 0; +} + +int +ldap_connect_to_host(LDAP *ld, Sockbuf *sb, + int proto, LDAPURLDesc *srv, + int async ) +{ + int rc; + int socktype, port; + ber_socket_t s = AC_SOCKET_INVALID; + char *host; + +#if defined( HAVE_GETADDRINFO ) && defined( HAVE_INET_NTOP ) + char serv[7]; + int err; + struct addrinfo hints, *res, *sai; +#else + int i; + int use_hp = 0; + struct hostent *hp = NULL; + struct hostent he_buf; + struct in_addr in; + char *ha_buf=NULL; +#endif + + if ( srv->lud_host == NULL || *srv->lud_host == 0 ) { + host = "localhost"; + } else { + host = srv->lud_host; + } + + port = srv->lud_port; + + if( !port ) { + if( strcmp(srv->lud_scheme, "ldaps") == 0 ) { + port = LDAPS_PORT; + } else { + port = LDAP_PORT; + } + } + + switch(proto) { + case LDAP_PROTO_TCP: socktype = SOCK_STREAM; + osip_debug( ld, + "ldap_connect_to_host: TCP %s:%d\n", + host, port, 0); + break; + case LDAP_PROTO_UDP: socktype = SOCK_DGRAM; + osip_debug( ld, + "ldap_connect_to_host: UDP %s:%d\n", + host, port, 0); + break; + default: + osip_debug( ld, "ldap_connect_to_host: unknown proto: %d\n", + proto, 0, 0 ); + return -1; + } + +#if defined( HAVE_GETADDRINFO ) && defined( HAVE_INET_NTOP ) + memset( &hints, '\0', sizeof(hints) ); +#ifdef USE_AI_ADDRCONFIG /* FIXME: configure test needed */ + /* Use AI_ADDRCONFIG only on systems where its known to be needed. */ + hints.ai_flags = AI_ADDRCONFIG; +#endif + hints.ai_family = ldap_int_inet4or6; + hints.ai_socktype = socktype; + snprintf(serv, sizeof serv, "%d", port ); + + /* most getaddrinfo(3) use non-threadsafe resolver libraries */ + LDAP_MUTEX_LOCK(&ldap_int_resolv_mutex); + + err = getaddrinfo( host, serv, &hints, &res ); + + LDAP_MUTEX_UNLOCK(&ldap_int_resolv_mutex); + + if ( err != 0 ) { + osip_debug(ld, "ldap_connect_to_host: getaddrinfo failed: %s\n", + AC_GAI_STRERROR(err), 0, 0); + return -1; + } + rc = -1; + + for( sai=res; sai != NULL; sai=sai->ai_next) { + if( sai->ai_addr == NULL ) { + osip_debug(ld, "ldap_connect_to_host: getaddrinfo " + "ai_addr is NULL?\n", 0, 0, 0); + continue; + } + +#ifndef LDAP_PF_INET6 + if ( sai->ai_family == AF_INET6 ) continue; +#endif + /* we assume AF_x and PF_x are equal for all x */ + s = ldap_int_socket( ld, sai->ai_family, socktype ); + if ( s == AC_SOCKET_INVALID ) { + continue; + } + + if ( ldap_int_prepare_socket(ld, s, proto ) == -1 ) { + ldap_pvt_close_socket(ld, s); + break; + } + + switch (sai->ai_family) { +#ifdef LDAP_PF_INET6 + case AF_INET6: { + char addr[INET6_ADDRSTRLEN]; + inet_ntop( AF_INET6, + &((struct sockaddr_in6 *)sai->ai_addr)->sin6_addr, + addr, sizeof addr); + osip_debug(ld, "ldap_connect_to_host: Trying %s %s\n", + addr, serv, 0); + } break; +#endif + case AF_INET: { + char addr[INET_ADDRSTRLEN]; + inet_ntop( AF_INET, + &((struct sockaddr_in *)sai->ai_addr)->sin_addr, + addr, sizeof addr); + osip_debug(ld, "ldap_connect_to_host: Trying %s:%s\n", + addr, serv, 0); + } break; + } + + rc = ldap_pvt_connect( ld, s, + sai->ai_addr, sai->ai_addrlen, async ); + if ( rc == 0 || rc == -2 ) { + err = ldap_int_connect_cbs( ld, sb, &s, srv, sai->ai_addr ); + if ( err ) + rc = err; + else + break; + } + ldap_pvt_close_socket(ld, s); + } + freeaddrinfo(res); + +#else + if (! inet_aton( host, &in ) ) { + int local_h_errno; + rc = ldap_pvt_gethostbyname_a( host, &he_buf, &ha_buf, + &hp, &local_h_errno ); + + if ( (rc < 0) || (hp == NULL) ) { +#ifdef HAVE_WINSOCK + ldap_pvt_set_errno( WSAGetLastError() ); +#else + /* not exactly right, but... */ + ldap_pvt_set_errno( EHOSTUNREACH ); +#endif + if (ha_buf) LDAP_FREE(ha_buf); + return -1; + } + + use_hp = 1; + } + + rc = s = -1; + for ( i = 0; !use_hp || (hp->h_addr_list[i] != 0); ++i, rc = -1 ) { + struct sockaddr_in sin; + + s = ldap_int_socket( ld, PF_INET, socktype ); + if ( s == AC_SOCKET_INVALID ) { + /* use_hp ? continue : break; */ + break; + } + + if ( ldap_int_prepare_socket( ld, s, proto ) == -1 ) { + ldap_pvt_close_socket(ld, s); + break; + } + + (void)memset((char *)&sin, '\0', sizeof sin); + sin.sin_family = AF_INET; + sin.sin_port = htons((unsigned short) port); + + if( use_hp ) { + AC_MEMCPY( &sin.sin_addr, hp->h_addr_list[i], + sizeof(sin.sin_addr) ); + } else { + AC_MEMCPY( &sin.sin_addr, &in.s_addr, + sizeof(sin.sin_addr) ); + } + +#ifdef HAVE_INET_NTOA_B + { + /* for VxWorks */ + char address[INET_ADDR_LEN]; + inet_ntoa_b(sin.sin_address, address); + osip_debug(ld, "ldap_connect_to_host: Trying %s:%d\n", + address, port, 0); + } +#else + osip_debug(ld, "ldap_connect_to_host: Trying %s:%d\n", + inet_ntoa(sin.sin_addr), port, 0); +#endif + + rc = ldap_pvt_connect(ld, s, + (struct sockaddr *)&sin, sizeof(sin), + async); + + if ( (rc == 0) || (rc == -2) ) { + int err = ldap_int_connect_cbs( ld, sb, &s, srv, (struct sockaddr *)&sin ); + if ( err ) + rc = err; + else + break; + } + + ldap_pvt_close_socket(ld, s); + + if (!use_hp) break; + } + if (ha_buf) LDAP_FREE(ha_buf); +#endif + + return rc; +} + +#if defined( HAVE_CYRUS_SASL ) +char * +ldap_host_connected_to( Sockbuf *sb, const char *host ) +{ + ber_socklen_t len; +#ifdef LDAP_PF_INET6 + struct sockaddr_storage sabuf; +#else + struct sockaddr sabuf; +#endif + struct sockaddr *sa = (struct sockaddr *) &sabuf; + ber_socket_t sd; + + (void)memset( (char *)sa, '\0', sizeof sabuf ); + len = sizeof sabuf; + + ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, &sd ); + if ( getpeername( sd, sa, &len ) == -1 ) { + return( NULL ); + } + + /* + * do a reverse lookup on the addr to get the official hostname. + * this is necessary for kerberos to work right, since the official + * hostname is used as the kerberos instance. + */ + + switch (sa->sa_family) { +#ifdef LDAP_PF_LOCAL + case AF_LOCAL: + return LDAP_STRDUP( ldap_int_hostname ); +#endif +#ifdef LDAP_PF_INET6 + case AF_INET6: + { + struct in6_addr localhost = IN6ADDR_LOOPBACK_INIT; + if( memcmp ( &((struct sockaddr_in6 *)sa)->sin6_addr, + &localhost, sizeof(localhost)) == 0 ) + { + return LDAP_STRDUP( ldap_int_hostname ); + } + } + break; +#endif + case AF_INET: + { + struct in_addr localhost; + localhost.s_addr = htonl( INADDR_ANY ); + + if( memcmp ( &((struct sockaddr_in *)sa)->sin_addr, + &localhost, sizeof(localhost) ) == 0 ) + { + return LDAP_STRDUP( ldap_int_hostname ); + } + +#ifdef INADDR_LOOPBACK + localhost.s_addr = htonl( INADDR_LOOPBACK ); + + if( memcmp ( &((struct sockaddr_in *)sa)->sin_addr, + &localhost, sizeof(localhost) ) == 0 ) + { + return LDAP_STRDUP( ldap_int_hostname ); + } +#endif + } + break; + + default: + return( NULL ); + break; + } + + { + char *herr; +#ifdef NI_MAXHOST + char hbuf[NI_MAXHOST]; +#elif defined( MAXHOSTNAMELEN ) + char hbuf[MAXHOSTNAMELEN]; +#else + char hbuf[256]; +#endif + hbuf[0] = 0; + + if (ldap_pvt_get_hname( sa, len, hbuf, sizeof(hbuf), &herr ) == 0 + && hbuf[0] ) + { + return LDAP_STRDUP( hbuf ); + } + } + + return host ? LDAP_STRDUP( host ) : NULL; +} +#endif + + +struct selectinfo { +#ifdef HAVE_POLL + /* for UNIX poll(2) */ + int si_maxfd; + struct pollfd si_fds[FD_SETSIZE]; +#else + /* for UNIX select(2) */ + fd_set si_readfds; + fd_set si_writefds; + fd_set si_use_readfds; + fd_set si_use_writefds; +#endif +}; + +void +ldap_mark_select_write( LDAP *ld, Sockbuf *sb ) +{ + struct selectinfo *sip; + ber_socket_t sd; + + sip = (struct selectinfo *)ld->ld_selectinfo; + + ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, &sd ); + +#ifdef HAVE_POLL + /* for UNIX poll(2) */ + { + int empty=-1; + int i; + for(i=0; i < sip->si_maxfd; i++) { + if( sip->si_fds[i].fd == sd ) { + sip->si_fds[i].events |= POLL_WRITE; + return; + } + if( empty==-1 && sip->si_fds[i].fd == -1 ) { + empty=i; + } + } + + if( empty == -1 ) { + if( sip->si_maxfd >= FD_SETSIZE ) { + /* FIXME */ + return; + } + empty = sip->si_maxfd++; + } + + sip->si_fds[empty].fd = sd; + sip->si_fds[empty].events = POLL_WRITE; + } +#else + /* for UNIX select(2) */ + if ( !FD_ISSET( sd, &sip->si_writefds )) { + FD_SET( sd, &sip->si_writefds ); + } +#endif +} + + +void +ldap_mark_select_read( LDAP *ld, Sockbuf *sb ) +{ + struct selectinfo *sip; + ber_socket_t sd; + + sip = (struct selectinfo *)ld->ld_selectinfo; + + ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, &sd ); + +#ifdef HAVE_POLL + /* for UNIX poll(2) */ + { + int empty=-1; + int i; + for(i=0; i < sip->si_maxfd; i++) { + if( sip->si_fds[i].fd == sd ) { + sip->si_fds[i].events |= POLL_READ; + return; + } + if( empty==-1 && sip->si_fds[i].fd == -1 ) { + empty=i; + } + } + + if( empty == -1 ) { + if( sip->si_maxfd >= FD_SETSIZE ) { + /* FIXME */ + return; + } + empty = sip->si_maxfd++; + } + + sip->si_fds[empty].fd = sd; + sip->si_fds[empty].events = POLL_READ; + } +#else + /* for UNIX select(2) */ + if ( !FD_ISSET( sd, &sip->si_readfds )) { + FD_SET( sd, &sip->si_readfds ); + } +#endif +} + + +void +ldap_mark_select_clear( LDAP *ld, Sockbuf *sb ) +{ + struct selectinfo *sip; + ber_socket_t sd; + + sip = (struct selectinfo *)ld->ld_selectinfo; + + ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, &sd ); + +#ifdef HAVE_POLL + /* for UNIX poll(2) */ + { + int i; + for(i=0; i < sip->si_maxfd; i++) { + if( sip->si_fds[i].fd == sd ) { + sip->si_fds[i].fd = -1; + } + } + } +#else + /* for UNIX select(2) */ + FD_CLR( sd, &sip->si_writefds ); + FD_CLR( sd, &sip->si_readfds ); +#endif +} + +void +ldap_clear_select_write( LDAP *ld, Sockbuf *sb ) +{ + struct selectinfo *sip; + ber_socket_t sd; + + sip = (struct selectinfo *)ld->ld_selectinfo; + + ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, &sd ); + +#ifdef HAVE_POLL + /* for UNIX poll(2) */ + { + int i; + for(i=0; i < sip->si_maxfd; i++) { + if( sip->si_fds[i].fd == sd ) { + sip->si_fds[i].events &= ~POLL_WRITE; + } + } + } +#else + /* for UNIX select(2) */ + FD_CLR( sd, &sip->si_writefds ); +#endif +} + + +int +ldap_is_write_ready( LDAP *ld, Sockbuf *sb ) +{ + struct selectinfo *sip; + ber_socket_t sd; + + sip = (struct selectinfo *)ld->ld_selectinfo; + + ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, &sd ); + +#ifdef HAVE_POLL + /* for UNIX poll(2) */ + { + int i; + for(i=0; i < sip->si_maxfd; i++) { + if( sip->si_fds[i].fd == sd ) { + return sip->si_fds[i].revents & POLL_WRITE; + } + } + + return 0; + } +#else + /* for UNIX select(2) */ + return( FD_ISSET( sd, &sip->si_use_writefds )); +#endif +} + + +int +ldap_is_read_ready( LDAP *ld, Sockbuf *sb ) +{ + struct selectinfo *sip; + ber_socket_t sd; + + sip = (struct selectinfo *)ld->ld_selectinfo; + + if (ber_sockbuf_ctrl( sb, LBER_SB_OPT_DATA_READY, NULL )) + return 1; + + ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, &sd ); + +#ifdef HAVE_POLL + /* for UNIX poll(2) */ + { + int i; + for(i=0; i < sip->si_maxfd; i++) { + if( sip->si_fds[i].fd == sd ) { + return sip->si_fds[i].revents & POLL_READ; + } + } + + return 0; + } +#else + /* for UNIX select(2) */ + return( FD_ISSET( sd, &sip->si_use_readfds )); +#endif +} + + +void * +ldap_new_select_info( void ) +{ + struct selectinfo *sip; + + sip = (struct selectinfo *)LDAP_CALLOC( 1, sizeof( struct selectinfo )); + + if ( sip == NULL ) return NULL; + +#ifdef HAVE_POLL + /* for UNIX poll(2) */ + /* sip->si_maxfd=0 */ +#else + /* for UNIX select(2) */ + FD_ZERO( &sip->si_readfds ); + FD_ZERO( &sip->si_writefds ); +#endif + + return( (void *)sip ); +} + + +void +ldap_free_select_info( void *sip ) +{ + LDAP_FREE( sip ); +} + + +#ifndef HAVE_POLL +int ldap_int_tblsize = 0; + +void +ldap_int_ip_init( void ) +{ +#if defined( HAVE_SYSCONF ) + long tblsize = sysconf( _SC_OPEN_MAX ); + if( tblsize > INT_MAX ) tblsize = INT_MAX; + +#elif defined( HAVE_GETDTABLESIZE ) + int tblsize = getdtablesize(); +#else + int tblsize = FD_SETSIZE; +#endif /* !USE_SYSCONF */ + +#ifdef FD_SETSIZE + if( tblsize > FD_SETSIZE ) tblsize = FD_SETSIZE; +#endif /* FD_SETSIZE */ + + ldap_int_tblsize = tblsize; +} +#endif + + +int +ldap_int_select( LDAP *ld, struct timeval *timeout ) +{ + int rc; + struct selectinfo *sip; + + Debug( LDAP_DEBUG_TRACE, "ldap_int_select\n", 0, 0, 0 ); + +#ifndef HAVE_POLL + if ( ldap_int_tblsize == 0 ) ldap_int_ip_init(); +#endif + + sip = (struct selectinfo *)ld->ld_selectinfo; + assert( sip != NULL ); + +#ifdef HAVE_POLL + { + int to = timeout ? TV2MILLISEC( timeout ) : INFTIM; + rc = poll( sip->si_fds, sip->si_maxfd, to ); + } +#else + sip->si_use_readfds = sip->si_readfds; + sip->si_use_writefds = sip->si_writefds; + + rc = select( ldap_int_tblsize, + &sip->si_use_readfds, &sip->si_use_writefds, + NULL, timeout ); +#endif + + return rc; +} diff --git a/libraries/libldap/os-local.c b/libraries/libldap/os-local.c new file mode 100644 index 0000000..9f7e48b --- /dev/null +++ b/libraries/libldap/os-local.c @@ -0,0 +1,363 @@ +/* os-local.c -- platform-specific domain socket code */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1995 Regents of the University of Michigan. + * All rights reserved. + */ +/* Portions (C) Copyright PADL Software Pty Ltd. 1999 + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that this notice is preserved + * and that due credit is given to PADL Software Pty Ltd. This software + * is provided ``as is'' without express or implied warranty. + */ + +#include "portable.h" + +#ifdef LDAP_PF_LOCAL + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/errno.h> +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> +#include <ac/unistd.h> + +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +#ifdef HAVE_SYS_UIO_H +#include <sys/uio.h> +#endif + +#ifdef HAVE_IO_H +#include <io.h> +#endif /* HAVE_IO_H */ +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif + +#include "ldap-int.h" +#include "ldap_defaults.h" + +#ifdef LDAP_DEBUG + +#define oslocal_debug(ld,fmt,arg1,arg2,arg3) \ +do { \ + ldap_log_printf(ld, LDAP_DEBUG_TRACE, fmt, arg1, arg2, arg3); \ +} while(0) + +#else + +#define oslocal_debug(ld,fmt,arg1,arg2,arg3) ((void)0) + +#endif /* LDAP_DEBUG */ + +static void +ldap_pvt_set_errno(int err) +{ + errno = err; +} + +static int +ldap_pvt_ndelay_on(LDAP *ld, int fd) +{ + oslocal_debug(ld, "ldap_ndelay_on: %d\n",fd,0,0); + return ber_pvt_socket_set_nonblock( fd, 1 ); +} + +static int +ldap_pvt_ndelay_off(LDAP *ld, int fd) +{ + oslocal_debug(ld, "ldap_ndelay_off: %d\n",fd,0,0); + return ber_pvt_socket_set_nonblock( fd, 0 ); +} + +static ber_socket_t +ldap_pvt_socket(LDAP *ld) +{ + ber_socket_t s = socket(PF_LOCAL, SOCK_STREAM, 0); + oslocal_debug(ld, "ldap_new_socket: %d\n",s,0,0); +#ifdef FD_CLOEXEC + fcntl(s, F_SETFD, FD_CLOEXEC); +#endif + return ( s ); +} + +static int +ldap_pvt_close_socket(LDAP *ld, int s) +{ + oslocal_debug(ld, "ldap_close_socket: %d\n",s,0,0); + return tcp_close(s); +} + +#undef TRACE +#define TRACE do { \ + char ebuf[128]; \ + oslocal_debug(ld, \ + "ldap_is_socket_ready: error on socket %d: errno: %d (%s)\n", \ + s, \ + errno, \ + AC_STRERROR_R(errno, ebuf, sizeof ebuf)); \ +} while( 0 ) + +/* + * check the socket for errors after select returned. + */ +static int +ldap_pvt_is_socket_ready(LDAP *ld, int s) +{ + oslocal_debug(ld, "ldap_is_sock_ready: %d\n",s,0,0); + +#if defined( notyet ) /* && defined( SO_ERROR ) */ +{ + int so_errno; + ber_socklen_t dummy = sizeof(so_errno); + if ( getsockopt( s, SOL_SOCKET, SO_ERROR, &so_errno, &dummy ) + == AC_SOCKET_ERROR ) + { + return -1; + } + if ( so_errno ) { + ldap_pvt_set_errno(so_errno); + TRACE; + return -1; + } + return 0; +} +#else +{ + /* error slippery */ + struct sockaddr_un sa; + char ch; + ber_socklen_t dummy = sizeof(sa); + if ( getpeername( s, (struct sockaddr *) &sa, &dummy ) + == AC_SOCKET_ERROR ) + { + /* XXX: needs to be replace with ber_stream_read() */ + (void)read(s, &ch, 1); + TRACE; + return -1; + } + return 0; +} +#endif + return -1; +} +#undef TRACE + +#ifdef LDAP_PF_LOCAL_SENDMSG +static const char abandonPDU[] = {LDAP_TAG_MESSAGE, 6, + LDAP_TAG_MSGID, 1, 0, LDAP_REQ_ABANDON, 1, 0}; +#endif + +static int +ldap_pvt_connect(LDAP *ld, ber_socket_t s, struct sockaddr_un *sa, int async) +{ + int rc; + struct timeval tv, *opt_tv = NULL; + + if ( ld->ld_options.ldo_tm_net.tv_sec >= 0 ) { + tv = ld->ld_options.ldo_tm_net; + opt_tv = &tv; + } + + oslocal_debug(ld, "ldap_connect_timeout: fd: %d tm: %ld async: %d\n", + s, opt_tv ? tv.tv_sec : -1L, async); + + if ( ldap_pvt_ndelay_on(ld, s) == -1 ) return -1; + + if ( connect(s, (struct sockaddr *) sa, sizeof(struct sockaddr_un)) + != AC_SOCKET_ERROR ) + { + if ( ldap_pvt_ndelay_off(ld, s) == -1 ) return -1; + +#ifdef LDAP_PF_LOCAL_SENDMSG + /* Send a dummy message with access rights. Remote side will + * obtain our uid/gid by fstat'ing this descriptor. The + * descriptor permissions must match exactly, and we also + * send the socket name, which must also match. + */ +sendcred: + { + int fds[2]; + ber_socklen_t salen = sizeof(*sa); + if (pipe(fds) == 0) { + /* Abandon, noop, has no reply */ + struct iovec iov; + struct msghdr msg = {0}; +# ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL +# ifndef CMSG_SPACE +# define CMSG_SPACE(len) (_CMSG_ALIGN( sizeof(struct cmsghdr)) + _CMSG_ALIGN(len) ) +# endif +# ifndef CMSG_LEN +# define CMSG_LEN(len) (_CMSG_ALIGN( sizeof(struct cmsghdr)) + (len) ) +# endif + union { + struct cmsghdr cm; + unsigned char control[CMSG_SPACE(sizeof(int))]; + } control_un; + struct cmsghdr *cmsg; +# endif /* HAVE_STRUCT_MSGHDR_MSG_CONTROL */ + msg.msg_name = NULL; + msg.msg_namelen = 0; + iov.iov_base = (char *) abandonPDU; + iov.iov_len = sizeof abandonPDU; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; +# ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL + msg.msg_control = control_un.control; + msg.msg_controllen = sizeof( control_un.control ); + msg.msg_flags = 0; + + cmsg = CMSG_FIRSTHDR( &msg ); + cmsg->cmsg_len = CMSG_LEN( sizeof(int) ); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + + *((int *)CMSG_DATA(cmsg)) = fds[0]; +# else + msg.msg_accrights = (char *)fds; + msg.msg_accrightslen = sizeof(int); +# endif /* HAVE_STRUCT_MSGHDR_MSG_CONTROL */ + getpeername( s, (struct sockaddr *) sa, &salen ); + fchmod( fds[0], S_ISUID|S_IRWXU ); + write( fds[1], sa, salen ); + sendmsg( s, &msg, 0 ); + close(fds[0]); + close(fds[1]); + } + } +#endif + return 0; + } + + if ( errno != EINPROGRESS && errno != EWOULDBLOCK ) return -1; + +#ifdef notyet + if ( async ) return -2; +#endif + +#ifdef HAVE_POLL + { + struct pollfd fd; + int timeout = INFTIM; + + if( opt_tv != NULL ) timeout = TV2MILLISEC( &tv ); + + fd.fd = s; + fd.events = POLL_WRITE; + + do { + fd.revents = 0; + rc = poll( &fd, 1, timeout ); + } while( rc == AC_SOCKET_ERROR && errno == EINTR && + LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_RESTART )); + + if( rc == AC_SOCKET_ERROR ) return rc; + + if( fd.revents & POLL_WRITE ) { + if ( ldap_pvt_is_socket_ready(ld, s) == -1 ) return -1; + if ( ldap_pvt_ndelay_off(ld, s) == -1 ) return -1; +#ifdef LDAP_PF_LOCAL_SENDMSG + goto sendcred; +#else + return ( 0 ); +#endif + } + } +#else + { + fd_set wfds, *z=NULL; + +#ifdef FD_SETSIZE + if ( s >= FD_SETSIZE ) { + rc = AC_SOCKET_ERROR; + tcp_close( s ); + ldap_pvt_set_errno( EMFILE ); + return rc; + } +#endif + do { + FD_ZERO(&wfds); + FD_SET(s, &wfds ); + rc = select( ldap_int_tblsize, z, &wfds, z, opt_tv ? &tv : NULL ); + } while( rc == AC_SOCKET_ERROR && errno == EINTR && + LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_RESTART )); + + if( rc == AC_SOCKET_ERROR ) return rc; + + if ( FD_ISSET(s, &wfds) ) { + if ( ldap_pvt_is_socket_ready(ld, s) == -1 ) return -1; + if ( ldap_pvt_ndelay_off(ld, s) == -1 ) return -1; +#ifdef LDAP_PF_LOCAL_SENDMSG + goto sendcred; +#else + return ( 0 ); +#endif + } + } +#endif + + oslocal_debug(ld, "ldap_connect_timeout: timed out\n",0,0,0); + ldap_pvt_set_errno( ETIMEDOUT ); + return ( -1 ); +} + +int +ldap_connect_to_path(LDAP *ld, Sockbuf *sb, LDAPURLDesc *srv, int async) +{ + struct sockaddr_un server; + ber_socket_t s; + int rc; + const char *path = srv->lud_host; + + oslocal_debug(ld, "ldap_connect_to_path\n",0,0,0); + + if ( path == NULL || path[0] == '\0' ) { + path = LDAPI_SOCK; + } else { + if ( strlen(path) > (sizeof( server.sun_path ) - 1) ) { + ldap_pvt_set_errno( ENAMETOOLONG ); + return -1; + } + } + + s = ldap_pvt_socket( ld ); + if ( s == AC_SOCKET_INVALID ) { + return -1; + } + + oslocal_debug(ld, "ldap_connect_to_path: Trying %s\n", path, 0, 0); + + memset( &server, '\0', sizeof(server) ); + server.sun_family = AF_LOCAL; + strcpy( server.sun_path, path ); + + rc = ldap_pvt_connect(ld, s, &server, async); + + if (rc == 0) { + rc = ldap_int_connect_cbs( ld, sb, &s, srv, (struct sockaddr *)&server ); + } + if ( rc ) { + ldap_pvt_close_socket(ld, s); + } + return rc; +} +#else +static int dummy; /* generate also a warning: 'dummy' defined but not used (at least here) */ +#endif /* LDAP_PF_LOCAL */ diff --git a/libraries/libldap/pagectrl.c b/libraries/libldap/pagectrl.c new file mode 100644 index 0000000..b3a5b72 --- /dev/null +++ b/libraries/libldap/pagectrl.c @@ -0,0 +1,271 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * Copyright 2006 Hans Leidekker + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +/* --------------------------------------------------------------------------- + ldap_create_page_control_value + + Create and encode the value of the paged results control (RFC 2696). + + ld (IN) An LDAP session handle + pagesize (IN) Page size requested + cookie (IN) Opaque structure used by the server to track its + location in the search results. NULL on the + first call. + value (OUT) Control value, SHOULD be freed by calling + ldap_memfree() when done. + + pagedResultsControl ::= SEQUENCE { + controlType 1.2.840.113556.1.4.319, + criticality BOOLEAN DEFAULT FALSE, + controlValue searchControlValue } + + searchControlValue ::= SEQUENCE { + size INTEGER (0..maxInt), + -- requested page size from client + -- result set size estimate from server + cookie OCTET STRING } + + ---------------------------------------------------------------------------*/ + +int +ldap_create_page_control_value( + LDAP *ld, + ber_int_t pagesize, + struct berval *cookie, + struct berval *value ) +{ + BerElement *ber = NULL; + ber_tag_t tag; + struct berval null_cookie = { 0, NULL }; + + if ( ld == NULL || value == NULL || + pagesize < 1 || pagesize > LDAP_MAXINT ) + { + if ( ld ) + ld->ld_errno = LDAP_PARAM_ERROR; + return LDAP_PARAM_ERROR; + } + + assert( LDAP_VALID( ld ) ); + + value->bv_val = NULL; + value->bv_len = 0; + ld->ld_errno = LDAP_SUCCESS; + + if ( cookie == NULL ) { + cookie = &null_cookie; + } + + ber = ldap_alloc_ber_with_options( ld ); + if ( ber == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + return ld->ld_errno; + } + + tag = ber_printf( ber, "{iO}", pagesize, cookie ); + if ( tag == LBER_ERROR ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + goto done; + } + + if ( ber_flatten2( ber, value, 1 ) == -1 ) { + ld->ld_errno = LDAP_NO_MEMORY; + } + +done:; + if ( ber != NULL ) { + ber_free( ber, 1 ); + } + + return ld->ld_errno; +} + + +/* --------------------------------------------------------------------------- + ldap_create_page_control + + Create and encode a page control. + + ld (IN) An LDAP session handle + pagesize (IN) Page size requested + cookie (IN) Opaque structure used by the server to track its + location in the search results. NULL on the + first call. + value (OUT) Control value, SHOULD be freed by calling + ldap_memfree() when done. + iscritical (IN) Criticality + ctrlp (OUT) LDAP control, SHOULD be freed by calling + ldap_control_free() when done. + + pagedResultsControl ::= SEQUENCE { + controlType 1.2.840.113556.1.4.319, + criticality BOOLEAN DEFAULT FALSE, + controlValue searchControlValue } + + searchControlValue ::= SEQUENCE { + size INTEGER (0..maxInt), + -- requested page size from client + -- result set size estimate from server + cookie OCTET STRING } + + ---------------------------------------------------------------------------*/ + +int +ldap_create_page_control( + LDAP *ld, + ber_int_t pagesize, + struct berval *cookie, + int iscritical, + LDAPControl **ctrlp ) +{ + struct berval value; + + if ( ctrlp == NULL ) { + ld->ld_errno = LDAP_PARAM_ERROR; + return ld->ld_errno; + } + + ld->ld_errno = ldap_create_page_control_value( ld, + pagesize, cookie, &value ); + if ( ld->ld_errno == LDAP_SUCCESS ) { + ld->ld_errno = ldap_control_create( LDAP_CONTROL_PAGEDRESULTS, + iscritical, &value, 0, ctrlp ); + if ( ld->ld_errno != LDAP_SUCCESS ) { + LDAP_FREE( value.bv_val ); + } + } + + return ld->ld_errno; +} + + +/* --------------------------------------------------------------------------- + ldap_parse_pageresponse_control + + Decode a page control. + + ld (IN) An LDAP session handle + ctrl (IN) The page response control + count (OUT) The number of entries in the page. + cookie (OUT) Opaque cookie. Use ldap_memfree() to + free the bv_val member of this structure. + + ---------------------------------------------------------------------------*/ + +int +ldap_parse_pageresponse_control( + LDAP *ld, + LDAPControl *ctrl, + ber_int_t *countp, + struct berval *cookie ) +{ + BerElement *ber; + ber_tag_t tag; + ber_int_t count; + + if ( ld == NULL || ctrl == NULL || cookie == NULL ) { + if ( ld ) + ld->ld_errno = LDAP_PARAM_ERROR; + return LDAP_PARAM_ERROR; + } + + /* Create a BerElement from the berval returned in the control. */ + ber = ber_init( &ctrl->ldctl_value ); + + if ( ber == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + return ld->ld_errno; + } + + /* Extract the count and cookie from the control. */ + tag = ber_scanf( ber, "{io}", &count, cookie ); + ber_free( ber, 1 ); + + if ( tag == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + } else { + ld->ld_errno = LDAP_SUCCESS; + + if ( countp != NULL ) { + *countp = (unsigned long)count; + } + } + + return ld->ld_errno; +} + +/* --------------------------------------------------------------------------- + ldap_parse_page_control + + Decode a page control. + + ld (IN) An LDAP session handle + ctrls (IN) Response controls + count (OUT) The number of entries in the page. + cookie (OUT) Opaque cookie. Use ldap_memfree() to + free the bv_val member of this structure. + + ---------------------------------------------------------------------------*/ + +int +ldap_parse_page_control( + LDAP *ld, + LDAPControl **ctrls, + ber_int_t *countp, + struct berval **cookiep ) +{ + LDAPControl *c; + struct berval cookie; + + if ( cookiep == NULL ) { + ld->ld_errno = LDAP_PARAM_ERROR; + return ld->ld_errno; + } + + if ( ctrls == NULL ) { + ld->ld_errno = LDAP_CONTROL_NOT_FOUND; + return ld->ld_errno; + } + + c = ldap_control_find( LDAP_CONTROL_PAGEDRESULTS, ctrls, NULL ); + if ( c == NULL ) { + /* No page control was found. */ + ld->ld_errno = LDAP_CONTROL_NOT_FOUND; + return ld->ld_errno; + } + + ld->ld_errno = ldap_parse_pageresponse_control( ld, c, countp, &cookie ); + if ( ld->ld_errno == LDAP_SUCCESS ) { + *cookiep = LDAP_MALLOC( sizeof( struct berval ) ); + if ( *cookiep == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + } else { + **cookiep = cookie; + } + } + + return ld->ld_errno; +} + diff --git a/libraries/libldap/passwd.c b/libraries/libldap/passwd.c new file mode 100644 index 0000000..9d4904a --- /dev/null +++ b/libraries/libldap/passwd.c @@ -0,0 +1,170 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: + * This program was orignally developed by Kurt D. Zeilenga for inclusion in + * OpenLDAP Software. + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +/* + * LDAP Password Modify (Extended) Operation (RFC 3062) + */ + +int ldap_parse_passwd( + LDAP *ld, + LDAPMessage *res, + struct berval *newpasswd ) +{ + int rc; + struct berval *retdata = NULL; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( res != NULL ); + assert( newpasswd != NULL ); + + newpasswd->bv_val = NULL; + newpasswd->bv_len = 0; + + rc = ldap_parse_extended_result( ld, res, NULL, &retdata, 0 ); + if ( rc != LDAP_SUCCESS ) { + return rc; + } + + if ( retdata != NULL ) { + ber_tag_t tag; + BerElement *ber = ber_init( retdata ); + + if ( ber == NULL ) { + rc = ld->ld_errno = LDAP_NO_MEMORY; + goto done; + } + + /* we should check the tag */ + tag = ber_scanf( ber, "{o}", newpasswd ); + ber_free( ber, 1 ); + + if ( tag == LBER_ERROR ) { + rc = ld->ld_errno = LDAP_DECODING_ERROR; + } + } + +done:; + ber_bvfree( retdata ); + + return rc; +} + +int +ldap_passwd( LDAP *ld, + struct berval *user, + struct berval *oldpw, + struct berval *newpw, + LDAPControl **sctrls, + LDAPControl **cctrls, + int *msgidp ) +{ + int rc; + struct berval bv = BER_BVNULL; + BerElement *ber = NULL; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( msgidp != NULL ); + + if( user != NULL || oldpw != NULL || newpw != NULL ) { + /* build change password control */ + ber = ber_alloc_t( LBER_USE_DER ); + + if( ber == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + return ld->ld_errno; + } + + ber_printf( ber, "{" /*}*/ ); + + if( user != NULL ) { + ber_printf( ber, "tO", + LDAP_TAG_EXOP_MODIFY_PASSWD_ID, user ); + } + + if( oldpw != NULL ) { + ber_printf( ber, "tO", + LDAP_TAG_EXOP_MODIFY_PASSWD_OLD, oldpw ); + } + + if( newpw != NULL ) { + ber_printf( ber, "tO", + LDAP_TAG_EXOP_MODIFY_PASSWD_NEW, newpw ); + } + + ber_printf( ber, /*{*/ "N}" ); + + rc = ber_flatten2( ber, &bv, 0 ); + + if( rc < 0 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + return ld->ld_errno; + } + + } + + rc = ldap_extended_operation( ld, LDAP_EXOP_MODIFY_PASSWD, + bv.bv_val ? &bv : NULL, sctrls, cctrls, msgidp ); + + ber_free( ber, 1 ); + + return rc; +} + +int +ldap_passwd_s( + LDAP *ld, + struct berval *user, + struct berval *oldpw, + struct berval *newpw, + struct berval *newpasswd, + LDAPControl **sctrls, + LDAPControl **cctrls ) +{ + int rc; + int msgid; + LDAPMessage *res; + + rc = ldap_passwd( ld, user, oldpw, newpw, sctrls, cctrls, &msgid ); + if ( rc != LDAP_SUCCESS ) { + return rc; + } + + if ( ldap_result( ld, msgid, LDAP_MSG_ALL, (struct timeval *) NULL, &res ) == -1 || !res ) { + return ld->ld_errno; + } + + rc = ldap_parse_passwd( ld, res, newpasswd ); + if( rc != LDAP_SUCCESS ) { + ldap_msgfree( res ); + return rc; + } + + return( ldap_result2error( ld, res, 1 ) ); +} diff --git a/libraries/libldap/ppolicy.c b/libraries/libldap/ppolicy.c new file mode 100644 index 0000000..77cb20b --- /dev/null +++ b/libraries/libldap/ppolicy.c @@ -0,0 +1,256 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2004-2021 The OpenLDAP Foundation. + * Portions Copyright 2004 Hewlett-Packard Company. + * Portions Copyright 2004 Howard Chu, Symas Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: + * This work was developed by Howard Chu for inclusion in + * OpenLDAP Software, based on prior work by Neil Dunbar (HP). + * This work was sponsored by the Hewlett-Packard Company. + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +#ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST + +/* IMPLICIT TAGS, all context-specific */ +#define PPOLICY_WARNING 0xa0L /* constructed + 0 */ +#define PPOLICY_ERROR 0x81L /* primitive + 1 */ + +#define PPOLICY_EXPIRE 0x80L /* primitive + 0 */ +#define PPOLICY_GRACE 0x81L /* primitive + 1 */ + +/*--- + ldap_create_passwordpolicy_control + + Create and encode the Password Policy Request + + ld (IN) An LDAP session handle, as obtained from a call to + ldap_init(). + + ctrlp (OUT) A result parameter that will be assigned the address + of an LDAPControl structure that contains the + passwordPolicyRequest control created by this function. + The memory occupied by the LDAPControl structure + SHOULD be freed when it is no longer in use by + calling ldap_control_free(). + + + There is no control value for a password policy request + ---*/ + +int +ldap_create_passwordpolicy_control( LDAP *ld, + LDAPControl **ctrlp ) +{ + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( ctrlp != NULL ); + + ld->ld_errno = ldap_control_create( LDAP_CONTROL_PASSWORDPOLICYREQUEST, + 0, NULL, 0, ctrlp ); + + return ld->ld_errno; +} + + +/*--- + ldap_parse_passwordpolicy_control + + Decode the passwordPolicyResponse control and return information. + + ld (IN) An LDAP session handle. + + ctrl (IN) The address of an + LDAPControl structure, either obtained + by running thorugh the list of response controls or + by a call to ldap_control_find(). + + exptimep (OUT) This result parameter is filled in with the number of seconds before + the password will expire, if expiration is imminent + (imminency defined by the password policy). If expiration + is not imminent, the value is set to -1. + + gracep (OUT) This result parameter is filled in with the number of grace logins after + the password has expired, before no further login attempts + will be allowed. + + errorcodep (OUT) This result parameter is filled in with the error code of the password operation + If no error was detected, this error is set to PP_noError. + + Ber encoding + + PasswordPolicyResponseValue ::= SEQUENCE { + warning [0] CHOICE { + timeBeforeExpiration [0] INTEGER (0 .. maxInt), + graceLoginsRemaining [1] INTEGER (0 .. maxInt) } OPTIONAL + error [1] ENUMERATED { + passwordExpired (0), + accountLocked (1), + changeAfterReset (2), + passwordModNotAllowed (3), + mustSupplyOldPassword (4), + invalidPasswordSyntax (5), + passwordTooShort (6), + passwordTooYoung (7), + passwordInHistory (8) } OPTIONAL } + +---*/ + +int +ldap_parse_passwordpolicy_control( + LDAP *ld, + LDAPControl *ctrl, + ber_int_t *expirep, + ber_int_t *gracep, + LDAPPasswordPolicyError *errorp ) +{ + BerElement *ber; + int exp = -1, grace = -1; + ber_tag_t tag; + ber_len_t berLen; + char *last; + int err = PP_noError; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( ctrl != NULL ); + + if ( !ctrl->ldctl_value.bv_val ) { + ld->ld_errno = LDAP_DECODING_ERROR; + return(ld->ld_errno); + } + + /* Create a BerElement from the berval returned in the control. */ + ber = ber_init(&ctrl->ldctl_value); + + if (ber == NULL) { + ld->ld_errno = LDAP_NO_MEMORY; + return(ld->ld_errno); + } + + tag = ber_peek_tag( ber, &berLen ); + if (tag != LBER_SEQUENCE) goto exit; + + for( tag = ber_first_element( ber, &berLen, &last ); + tag != LBER_DEFAULT; + tag = ber_next_element( ber, &berLen, last ) ) + { + switch (tag) { + case PPOLICY_WARNING: + ber_skip_tag(ber, &berLen ); + tag = ber_peek_tag( ber, &berLen ); + switch( tag ) { + case PPOLICY_EXPIRE: + if (ber_get_int( ber, &exp ) == LBER_DEFAULT) goto exit; + break; + case PPOLICY_GRACE: + if (ber_get_int( ber, &grace ) == LBER_DEFAULT) goto exit; + break; + default: + goto exit; + } + break; + case PPOLICY_ERROR: + if (ber_get_enum( ber, &err ) == LBER_DEFAULT) goto exit; + break; + default: + goto exit; + } + } + + ber_free(ber, 1); + + /* Return data to the caller for items that were requested. */ + if (expirep) *expirep = exp; + if (gracep) *gracep = grace; + if (errorp) *errorp = err; + + ld->ld_errno = LDAP_SUCCESS; + return(ld->ld_errno); + + exit: + ber_free(ber, 1); + ld->ld_errno = LDAP_DECODING_ERROR; + return(ld->ld_errno); +} + +const char * +ldap_passwordpolicy_err2txt( LDAPPasswordPolicyError err ) +{ + switch(err) { + case PP_passwordExpired: return "Password expired"; + case PP_accountLocked: return "Account locked"; + case PP_changeAfterReset: return "Password must be changed"; + case PP_passwordModNotAllowed: return "Policy prevents password modification"; + case PP_mustSupplyOldPassword: return "Policy requires old password in order to change password"; + case PP_insufficientPasswordQuality: return "Password fails quality checks"; + case PP_passwordTooShort: return "Password is too short for policy"; + case PP_passwordTooYoung: return "Password has been changed too recently"; + case PP_passwordInHistory: return "New password is in list of old passwords"; + case PP_noError: return "No error"; + default: return "Unknown error code"; + } +} + +#endif /* LDAP_CONTROL_PASSWORDPOLICYREQUEST */ + +#ifdef LDAP_CONTROL_X_PASSWORD_EXPIRING + +int +ldap_parse_password_expiring_control( + LDAP *ld, + LDAPControl *ctrl, + long *secondsp ) +{ + long seconds = 0; + char buf[sizeof("-2147483648")]; + char *next; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( ctrl != NULL ); + + if ( BER_BVISEMPTY( &ctrl->ldctl_value ) || + ctrl->ldctl_value.bv_len >= sizeof(buf) ) { + ld->ld_errno = LDAP_DECODING_ERROR; + return(ld->ld_errno); + } + + memcpy( buf, ctrl->ldctl_value.bv_val, ctrl->ldctl_value.bv_len ); + buf[ctrl->ldctl_value.bv_len] = '\0'; + + seconds = strtol( buf, &next, 10 ); + if ( next == buf || next[0] != '\0' ) goto exit; + + if ( secondsp != NULL ) { + *secondsp = seconds; + } + + ld->ld_errno = LDAP_SUCCESS; + return(ld->ld_errno); + + exit: + ld->ld_errno = LDAP_DECODING_ERROR; + return(ld->ld_errno); +} + +#endif /* LDAP_CONTROL_X_PASSWORD_EXPIRING */ diff --git a/libraries/libldap/print.c b/libraries/libldap/print.c new file mode 100644 index 0000000..5f7650a --- /dev/null +++ b/libraries/libldap/print.c @@ -0,0 +1,62 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/ctype.h> +#include <ac/stdarg.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +/* + * ldap log + */ + +static int ldap_log_check( LDAP *ld, int loglvl ) +{ + int errlvl; + + if(ld == NULL) { + errlvl = ldap_debug; + } else { + errlvl = ld->ld_debug; + } + + return errlvl & loglvl ? 1 : 0; +} + +int ldap_log_printf( LDAP *ld, int loglvl, const char *fmt, ... ) +{ + char buf[ 1024 ]; + va_list ap; + + if ( !ldap_log_check( ld, loglvl )) { + return 0; + } + + va_start( ap, fmt ); + + buf[sizeof(buf) - 1] = '\0'; + vsnprintf( buf, sizeof(buf)-1, fmt, ap ); + + va_end(ap); + + (*ber_pvt_log_print)( buf ); + return 1; +} diff --git a/libraries/libldap/references.c b/libraries/libldap/references.c new file mode 100644 index 0000000..01e9c24 --- /dev/null +++ b/libraries/libldap/references.c @@ -0,0 +1,147 @@ +/* references.c */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +LDAPMessage * +ldap_first_reference( LDAP *ld, LDAPMessage *chain ) +{ + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( chain != NULL ); + + return chain->lm_msgtype == LDAP_RES_SEARCH_REFERENCE + ? chain + : ldap_next_reference( ld, chain ); +} + +LDAPMessage * +ldap_next_reference( LDAP *ld, LDAPMessage *ref ) +{ + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( ref != NULL ); + + for ( + ref = ref->lm_chain; + ref != NULL; + ref = ref->lm_chain ) + { + if( ref->lm_msgtype == LDAP_RES_SEARCH_REFERENCE ) { + return( ref ); + } + } + + return( NULL ); +} + +int +ldap_count_references( LDAP *ld, LDAPMessage *chain ) +{ + int i; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + + for ( i = 0; chain != NULL; chain = chain->lm_chain ) { + if( chain->lm_msgtype == LDAP_RES_SEARCH_REFERENCE ) { + i++; + } + } + + return( i ); +} + +int +ldap_parse_reference( + LDAP *ld, + LDAPMessage *ref, + char ***referralsp, + LDAPControl ***serverctrls, + int freeit) +{ + BerElement be; + char **refs = NULL; + int rc; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( ref != NULL ); + + if( ref->lm_msgtype != LDAP_RES_SEARCH_REFERENCE ) { + return LDAP_PARAM_ERROR; + } + + /* make a private copy of BerElement */ + AC_MEMCPY(&be, ref->lm_ber, sizeof(be)); + + if ( ber_scanf( &be, "{v" /*}*/, &refs ) == LBER_ERROR ) { + rc = LDAP_DECODING_ERROR; + goto free_and_return; + } + + if ( serverctrls == NULL ) { + rc = LDAP_SUCCESS; + goto free_and_return; + } + + if ( ber_scanf( &be, /*{*/ "}" ) == LBER_ERROR ) { + rc = LDAP_DECODING_ERROR; + goto free_and_return; + } + + rc = ldap_pvt_get_controls( &be, serverctrls ); + +free_and_return: + + if( referralsp != NULL ) { + /* provide references regradless of return code */ + *referralsp = refs; + + } else { + LDAP_VFREE( refs ); + } + + if( freeit ) { + ldap_msgfree( ref ); + } + + if( rc != LDAP_SUCCESS ) { + ld->ld_errno = rc; + + if( ld->ld_matched != NULL ) { + LDAP_FREE( ld->ld_matched ); + ld->ld_matched = NULL; + } + + if( ld->ld_error != NULL ) { + LDAP_FREE( ld->ld_error ); + ld->ld_error = NULL; + } + } + + return rc; +} diff --git a/libraries/libldap/request.c b/libraries/libldap/request.c new file mode 100644 index 0000000..e551bd9 --- /dev/null +++ b/libraries/libldap/request.c @@ -0,0 +1,1699 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1995 Regents of the University of Michigan. + * All rights reserved. + */ +/* This notice applies to changes, created by or for Novell, Inc., + * to preexisting works for which notices appear elsewhere in this file. + * + * Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved. + * + * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND TREATIES. + * USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT TO VERSION + * 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS AVAILABLE AT + * HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE" IN THE + * TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION OF THIS + * WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP PUBLIC + * LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT THE + * PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY. + *--- + * Modification to OpenLDAP source by Novell, Inc. + * April 2000 sfs Added code to chase V3 referrals + * request.c - sending of ldap requests; handling of referrals + *--- + * Note: A verbatim copy of version 2.0.1 of the OpenLDAP Public License + * can be found in the file "build/LICENSE-2.0.1" in this distribution + * of OpenLDAP Software. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/errno.h> +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> +#include <ac/unistd.h> + +#include "ldap-int.h" +#include "lber.h" + +/* used by ldap_send_server_request and ldap_new_connection */ +#ifdef LDAP_R_COMPILE +#define LDAP_CONN_LOCK_IF(nolock) \ + { if (nolock) LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); } +#define LDAP_CONN_UNLOCK_IF(nolock) \ + { if (nolock) LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); } +#define LDAP_REQ_LOCK_IF(nolock) \ + { if (nolock) LDAP_MUTEX_LOCK( &ld->ld_req_mutex ); } +#define LDAP_REQ_UNLOCK_IF(nolock) \ + { if (nolock) LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex ); } +#define LDAP_RES_LOCK_IF(nolock) \ + { if (nolock) LDAP_MUTEX_LOCK( &ld->ld_res_mutex ); } +#define LDAP_RES_UNLOCK_IF(nolock) \ + { if (nolock) LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex ); } +#else +#define LDAP_CONN_LOCK_IF(nolock) +#define LDAP_CONN_UNLOCK_IF(nolock) +#define LDAP_REQ_LOCK_IF(nolock) +#define LDAP_REQ_UNLOCK_IF(nolock) +#define LDAP_RES_LOCK_IF(nolock) +#define LDAP_RES_UNLOCK_IF(nolock) +#endif + +static LDAPConn *find_connection LDAP_P(( LDAP *ld, LDAPURLDesc *srv, int any )); +static void use_connection LDAP_P(( LDAP *ld, LDAPConn *lc )); +static void ldap_free_request_int LDAP_P(( LDAP *ld, LDAPRequest *lr )); + +static BerElement * +re_encode_request( LDAP *ld, + BerElement *origber, + ber_int_t msgid, + int sref, + LDAPURLDesc *srv, + int *type ); + +BerElement * +ldap_alloc_ber_with_options( LDAP *ld ) +{ + BerElement *ber; + + ber = ber_alloc_t( ld->ld_lberoptions ); + if ( ber == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + } + + return( ber ); +} + + +void +ldap_set_ber_options( LDAP *ld, BerElement *ber ) +{ + /* ld_lberoptions is constant, hence no lock */ + ber->ber_options = ld->ld_lberoptions; +} + + +/* sets needed mutexes - no mutexes set to this point */ +ber_int_t +ldap_send_initial_request( + LDAP *ld, + ber_tag_t msgtype, + const char *dn, + BerElement *ber, + ber_int_t msgid) +{ + int rc = 1; + ber_socket_t sd = AC_SOCKET_INVALID; + + Debug( LDAP_DEBUG_TRACE, "ldap_send_initial_request\n", 0, 0, 0 ); + + LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); + if ( ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, &sd ) == -1 ) { + /* not connected yet */ + rc = ldap_open_defconn( ld ); + if ( rc == 0 ) { + ber_sockbuf_ctrl( ld->ld_defconn->lconn_sb, + LBER_SB_OPT_GET_FD, &sd ); + } + } + if ( ld->ld_defconn && ld->ld_defconn->lconn_status == LDAP_CONNST_CONNECTING ) + rc = ldap_int_check_async_open( ld, sd ); + if( rc < 0 ) { + ber_free( ber, 1 ); + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); + return( -1 ); + } else if ( rc == 0 ) { + Debug( LDAP_DEBUG_TRACE, + "ldap_open_defconn: successful\n", + 0, 0, 0 ); + } + +#ifdef LDAP_CONNECTIONLESS + if (LDAP_IS_UDP(ld)) { + if (msgtype == LDAP_REQ_BIND) { + LDAP_MUTEX_LOCK( &ld->ld_options.ldo_mutex ); + if (ld->ld_options.ldo_cldapdn) + ldap_memfree(ld->ld_options.ldo_cldapdn); + ld->ld_options.ldo_cldapdn = ldap_strdup(dn); + ber_free( ber, 1 ); + LDAP_MUTEX_UNLOCK( &ld->ld_options.ldo_mutex ); + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); + return 0; + } + if (msgtype != LDAP_REQ_ABANDON && msgtype != LDAP_REQ_SEARCH) + { + ber_free( ber, 1 ); + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); + return LDAP_PARAM_ERROR; + } + } +#endif + LDAP_MUTEX_LOCK( &ld->ld_req_mutex ); + rc = ldap_send_server_request( ld, ber, msgid, NULL, + NULL, NULL, NULL, 0, 0 ); + LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex ); + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); + return(rc); +} + + +/* protected by conn_mutex */ +int +ldap_int_flush_request( + LDAP *ld, + LDAPRequest *lr ) +{ + LDAPConn *lc = lr->lr_conn; + + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex ); + if ( ber_flush2( lc->lconn_sb, lr->lr_ber, LBER_FLUSH_FREE_NEVER ) != 0 ) { + if (( sock_errno() == EAGAIN ) || ( sock_errno() == ENOTCONN )) { + /* ENOTCONN is returned in Solaris 10 */ + /* need to continue write later */ + lr->lr_status = LDAP_REQST_WRITING; + ldap_mark_select_write( ld, lc->lconn_sb ); + ld->ld_errno = LDAP_BUSY; + return -2; + } else { + ld->ld_errno = LDAP_SERVER_DOWN; + ldap_free_request( ld, lr ); + ldap_free_connection( ld, lc, 0, 0 ); + return( -1 ); + } + } else { + if ( lr->lr_parent == NULL ) { + lr->lr_ber->ber_end = lr->lr_ber->ber_ptr; + lr->lr_ber->ber_ptr = lr->lr_ber->ber_buf; + } + lr->lr_status = LDAP_REQST_INPROGRESS; + + /* sent -- waiting for a response */ + ldap_mark_select_read( ld, lc->lconn_sb ); + ldap_clear_select_write( ld, lc->lconn_sb ); + } + return 0; +} + +/* + * protected by req_mutex + * if m_noconn then protect using conn_lock + * else already protected with conn_lock + * if m_res then also protected by res_mutex + */ + +int +ldap_send_server_request( + LDAP *ld, + BerElement *ber, + ber_int_t msgid, + LDAPRequest *parentreq, + LDAPURLDesc **srvlist, + LDAPConn *lc, + LDAPreqinfo *bind, + int m_noconn, + int m_res ) +{ + LDAPRequest *lr; + int incparent, rc; + + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex ); + Debug( LDAP_DEBUG_TRACE, "ldap_send_server_request\n", 0, 0, 0 ); + + incparent = 0; + ld->ld_errno = LDAP_SUCCESS; /* optimistic */ + + LDAP_CONN_LOCK_IF(m_noconn); + if ( lc == NULL ) { + if ( srvlist == NULL ) { + lc = ld->ld_defconn; + } else { + lc = find_connection( ld, *srvlist, 1 ); + if ( lc == NULL ) { + if ( (bind != NULL) && (parentreq != NULL) ) { + /* Remember the bind in the parent */ + incparent = 1; + ++parentreq->lr_outrefcnt; + } + lc = ldap_new_connection( ld, srvlist, 0, + 1, bind, 1, m_res ); + } + } + } + + /* async connect... */ + if ( lc != NULL && lc->lconn_status == LDAP_CONNST_CONNECTING ) { + ber_socket_t sd = AC_SOCKET_ERROR; + struct timeval tv = { 0 }; + + ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_GET_FD, &sd ); + + /* poll ... */ + switch ( ldap_int_poll( ld, sd, &tv, 1 ) ) { + case 0: + /* go on! */ + lc->lconn_status = LDAP_CONNST_CONNECTED; + break; + + case -2: + /* async only occurs if a network timeout is set */ + + /* honor network timeout */ + LDAP_MUTEX_LOCK( &ld->ld_options.ldo_mutex ); + if ( time( NULL ) - lc->lconn_created <= ld->ld_options.ldo_tm_net.tv_sec ) + { + /* caller will have to call again */ + ld->ld_errno = LDAP_X_CONNECTING; + } + LDAP_MUTEX_UNLOCK( &ld->ld_options.ldo_mutex ); + /* fallthru */ + + default: + /* error */ + break; + } + } + + if ( lc == NULL || lc->lconn_status != LDAP_CONNST_CONNECTED ) { + if ( ld->ld_errno == LDAP_SUCCESS ) { + ld->ld_errno = LDAP_SERVER_DOWN; + } + + ber_free( ber, 1 ); + if ( incparent ) { + /* Forget about the bind */ + --parentreq->lr_outrefcnt; + } + LDAP_CONN_UNLOCK_IF(m_noconn); + return( -1 ); + } + + use_connection( ld, lc ); + +#ifdef LDAP_CONNECTIONLESS + if ( LDAP_IS_UDP( ld )) { + BerElement tmpber = *ber; + ber_rewind( &tmpber ); + LDAP_MUTEX_LOCK( &ld->ld_options.ldo_mutex ); + rc = ber_write( &tmpber, ld->ld_options.ldo_peer, + sizeof( struct sockaddr_storage ), 0 ); + LDAP_MUTEX_UNLOCK( &ld->ld_options.ldo_mutex ); + if ( rc == -1 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + LDAP_CONN_UNLOCK_IF(m_noconn); + return rc; + } + } +#endif + + /* If we still have an incomplete write, try to finish it before + * dealing with the new request. If we don't finish here, return + * LDAP_BUSY and let the caller retry later. We only allow a single + * request to be in WRITING state. + */ + rc = 0; + if ( ld->ld_requests && + ld->ld_requests->lr_status == LDAP_REQST_WRITING && + ldap_int_flush_request( ld, ld->ld_requests ) < 0 ) + { + rc = -1; + } + if ( rc ) { + ber_free( ber, 1 ); + LDAP_CONN_UNLOCK_IF(m_noconn); + return rc; + } + + lr = (LDAPRequest *)LDAP_CALLOC( 1, sizeof( LDAPRequest ) ); + if ( lr == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + ldap_free_connection( ld, lc, 0, 0 ); + ber_free( ber, 1 ); + if ( incparent ) { + /* Forget about the bind */ + --parentreq->lr_outrefcnt; + } + LDAP_CONN_UNLOCK_IF(m_noconn); + return( -1 ); + } + lr->lr_msgid = msgid; + lr->lr_status = LDAP_REQST_INPROGRESS; + lr->lr_res_errno = LDAP_SUCCESS; /* optimistic */ + lr->lr_ber = ber; + lr->lr_conn = lc; + if ( parentreq != NULL ) { /* sub-request */ + if ( !incparent ) { + /* Increment if we didn't do it before the bind */ + ++parentreq->lr_outrefcnt; + } + lr->lr_origid = parentreq->lr_origid; + lr->lr_parentcnt = ++parentreq->lr_parentcnt; + lr->lr_parent = parentreq; + lr->lr_refnext = parentreq->lr_child; + parentreq->lr_child = lr; + } else { /* original request */ + lr->lr_origid = lr->lr_msgid; + } + + /* Extract requestDN for future reference */ +#ifdef LDAP_CONNECTIONLESS + if ( !LDAP_IS_UDP(ld) ) +#endif + { + BerElement tmpber = *ber; + ber_int_t bint; + ber_tag_t tag, rtag; + + ber_reset( &tmpber, 1 ); + rtag = ber_scanf( &tmpber, "{it", /*}*/ &bint, &tag ); + switch ( tag ) { + case LDAP_REQ_BIND: + rtag = ber_scanf( &tmpber, "{i" /*}*/, &bint ); + break; + case LDAP_REQ_DELETE: + break; + default: + rtag = ber_scanf( &tmpber, "{" /*}*/ ); + case LDAP_REQ_ABANDON: + break; + } + if ( tag != LDAP_REQ_ABANDON ) { + ber_skip_tag( &tmpber, &lr->lr_dn.bv_len ); + lr->lr_dn.bv_val = tmpber.ber_ptr; + } + } + + lr->lr_prev = NULL; + lr->lr_next = ld->ld_requests; + if ( lr->lr_next != NULL ) { + lr->lr_next->lr_prev = lr; + } + ld->ld_requests = lr; + + ld->ld_errno = LDAP_SUCCESS; + if ( ldap_int_flush_request( ld, lr ) == -1 ) { + msgid = -1; + } + + LDAP_CONN_UNLOCK_IF(m_noconn); + return( msgid ); +} + +/* return 0 if no StartTLS ext, 1 if present, 2 if critical */ +static int +find_tls_ext( LDAPURLDesc *srv ) +{ + int i, crit; + char *ext; + + if ( !srv->lud_exts ) + return 0; + + for (i=0; srv->lud_exts[i]; i++) { + crit = 0; + ext = srv->lud_exts[i]; + if ( ext[0] == '!') { + ext++; + crit = 1; + } + if ( !strcasecmp( ext, "StartTLS" ) || + !strcasecmp( ext, "X-StartTLS" ) || + !strcmp( ext, LDAP_EXOP_START_TLS )) { + return crit + 1; + } + } + return 0; +} + +/* + * always protected by conn_mutex + * optionally protected by req_mutex and res_mutex + */ +LDAPConn * +ldap_new_connection( LDAP *ld, LDAPURLDesc **srvlist, int use_ldsb, + int connect, LDAPreqinfo *bind, int m_req, int m_res ) +{ + LDAPConn *lc; + int async = 0; + + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex ); + Debug( LDAP_DEBUG_TRACE, "ldap_new_connection %d %d %d\n", + use_ldsb, connect, (bind != NULL) ); + /* + * make a new LDAP server connection + * XXX open connection synchronously for now + */ + lc = (LDAPConn *)LDAP_CALLOC( 1, sizeof( LDAPConn ) ); + if ( lc == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + return( NULL ); + } + + if ( use_ldsb ) { + assert( ld->ld_sb != NULL ); + lc->lconn_sb = ld->ld_sb; + + } else { + lc->lconn_sb = ber_sockbuf_alloc(); + if ( lc->lconn_sb == NULL ) { + LDAP_FREE( (char *)lc ); + ld->ld_errno = LDAP_NO_MEMORY; + return( NULL ); + } + } + + if ( connect ) { + LDAPURLDesc **srvp, *srv = NULL; + + async = LDAP_BOOL_GET( &ld->ld_options, LDAP_BOOL_CONNECT_ASYNC ); + + for ( srvp = srvlist; *srvp != NULL; srvp = &(*srvp)->lud_next ) { + int rc; + + rc = ldap_int_open_connection( ld, lc, *srvp, async ); + if ( rc != -1 ) { + srv = *srvp; + + /* If we fully connected, async is moot */ + if ( rc == 0 ) + async = 0; + + if ( ld->ld_urllist_proc && ( !async || rc != -2 ) ) { + ld->ld_urllist_proc( ld, srvlist, srvp, ld->ld_urllist_params ); + } + + break; + } + } + + if ( srv == NULL ) { + if ( !use_ldsb ) { + ber_sockbuf_free( lc->lconn_sb ); + } + LDAP_FREE( (char *)lc ); + ld->ld_errno = LDAP_SERVER_DOWN; + return( NULL ); + } + + lc->lconn_server = ldap_url_dup( srv ); + if ( !lc->lconn_server ) { + if ( !use_ldsb ) + ber_sockbuf_free( lc->lconn_sb ); + LDAP_FREE( (char *)lc ); + ld->ld_errno = LDAP_NO_MEMORY; + return( NULL ); + } + } + + lc->lconn_status = async ? LDAP_CONNST_CONNECTING : LDAP_CONNST_CONNECTED; + lc->lconn_next = ld->ld_conns; + ld->ld_conns = lc; + + if ( connect ) { +#ifdef HAVE_TLS + if ( lc->lconn_server->lud_exts ) { + int rc, ext = find_tls_ext( lc->lconn_server ); + if ( ext ) { + LDAPConn *savedefconn; + + savedefconn = ld->ld_defconn; + ++lc->lconn_refcnt; /* avoid premature free */ + ld->ld_defconn = lc; + + LDAP_REQ_UNLOCK_IF(m_req); + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); + LDAP_RES_UNLOCK_IF(m_res); + rc = ldap_start_tls_s( ld, NULL, NULL ); + LDAP_RES_LOCK_IF(m_res); + LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); + LDAP_REQ_LOCK_IF(m_req); + ld->ld_defconn = savedefconn; + --lc->lconn_refcnt; + + if ( rc != LDAP_SUCCESS && ext == 2 ) { + ldap_free_connection( ld, lc, 1, 0 ); + return NULL; + } + } + } +#endif + } + + if ( bind != NULL ) { + int err = 0; + LDAPConn *savedefconn; + + /* Set flag to prevent additional referrals + * from being processed on this + * connection until the bind has completed + */ + lc->lconn_rebind_inprogress = 1; + /* V3 rebind function */ + if ( ld->ld_rebind_proc != NULL) { + LDAPURLDesc *srvfunc; + + srvfunc = ldap_url_dup( *srvlist ); + if ( srvfunc == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + err = -1; + } else { + savedefconn = ld->ld_defconn; + ++lc->lconn_refcnt; /* avoid premature free */ + ld->ld_defconn = lc; + + Debug( LDAP_DEBUG_TRACE, "Call application rebind_proc\n", 0, 0, 0); + LDAP_REQ_UNLOCK_IF(m_req); + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); + LDAP_RES_UNLOCK_IF(m_res); + err = (*ld->ld_rebind_proc)( ld, + bind->ri_url, bind->ri_request, bind->ri_msgid, + ld->ld_rebind_params ); + LDAP_RES_LOCK_IF(m_res); + LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); + LDAP_REQ_LOCK_IF(m_req); + + ld->ld_defconn = savedefconn; + --lc->lconn_refcnt; + + if ( err != 0 ) { + err = -1; + ldap_free_connection( ld, lc, 1, 0 ); + lc = NULL; + } + ldap_free_urldesc( srvfunc ); + } + + } else { + int msgid, rc; + struct berval passwd = BER_BVNULL; + + savedefconn = ld->ld_defconn; + ++lc->lconn_refcnt; /* avoid premature free */ + ld->ld_defconn = lc; + + Debug( LDAP_DEBUG_TRACE, + "anonymous rebind via ldap_sasl_bind(\"\")\n", + 0, 0, 0); + + LDAP_REQ_UNLOCK_IF(m_req); + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); + LDAP_RES_UNLOCK_IF(m_res); + rc = ldap_sasl_bind( ld, "", LDAP_SASL_SIMPLE, &passwd, + NULL, NULL, &msgid ); + if ( rc != LDAP_SUCCESS ) { + err = -1; + + } else { + for ( err = 1; err > 0; ) { + struct timeval tv = { 0, 100000 }; + LDAPMessage *res = NULL; + + switch ( ldap_result( ld, msgid, LDAP_MSG_ALL, &tv, &res ) ) { + case -1: + err = -1; + break; + + case 0: +#ifdef LDAP_R_COMPILE + ldap_pvt_thread_yield(); +#endif + break; + + case LDAP_RES_BIND: + rc = ldap_parse_result( ld, res, &err, NULL, NULL, NULL, NULL, 1 ); + if ( rc != LDAP_SUCCESS ) { + err = -1; + + } else if ( err != LDAP_SUCCESS ) { + err = -1; + } + /* else err == LDAP_SUCCESS == 0 */ + break; + + default: + Debug( LDAP_DEBUG_TRACE, + "ldap_new_connection %p: " + "unexpected response %d " + "from BIND request id=%d\n", + (void *) ld, ldap_msgtype( res ), msgid ); + err = -1; + break; + } + } + } + LDAP_RES_LOCK_IF(m_res); + LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); + LDAP_REQ_LOCK_IF(m_req); + ld->ld_defconn = savedefconn; + --lc->lconn_refcnt; + + if ( err != 0 ) { + ldap_free_connection( ld, lc, 1, 0 ); + lc = NULL; + } + } + if ( lc != NULL ) + lc->lconn_rebind_inprogress = 0; + } + return( lc ); +} + + +/* protected by ld_conn_mutex */ +static LDAPConn * +find_connection( LDAP *ld, LDAPURLDesc *srv, int any ) +/* + * return an existing connection (if any) to the server srv + * if "any" is non-zero, check for any server in the "srv" chain + */ +{ + LDAPConn *lc; + LDAPURLDesc *lcu, *lsu; + int lcu_port, lsu_port; + int found = 0; + + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex ); + for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) { + lcu = lc->lconn_server; + lcu_port = ldap_pvt_url_scheme_port( lcu->lud_scheme, + lcu->lud_port ); + + for ( lsu = srv; lsu != NULL; lsu = lsu->lud_next ) { + lsu_port = ldap_pvt_url_scheme_port( lsu->lud_scheme, + lsu->lud_port ); + + if ( lsu_port == lcu_port + && strcmp( lcu->lud_scheme, lsu->lud_scheme ) == 0 + && lcu->lud_host != NULL && lsu->lud_host != NULL + && strcasecmp( lsu->lud_host, lcu->lud_host ) == 0 ) + { + found = 1; + break; + } + + if ( !any ) break; + } + if ( found ) + break; + } + return lc; +} + + + +/* protected by ld_conn_mutex */ +static void +use_connection( LDAP *ld, LDAPConn *lc ) +{ + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex ); + ++lc->lconn_refcnt; + lc->lconn_lastused = time( NULL ); +} + + +/* protected by ld_conn_mutex */ +void +ldap_free_connection( LDAP *ld, LDAPConn *lc, int force, int unbind ) +{ + LDAPConn *tmplc, *prevlc; + + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex ); + Debug( LDAP_DEBUG_TRACE, + "ldap_free_connection %d %d\n", + force, unbind, 0 ); + + if ( force || --lc->lconn_refcnt <= 0 ) { + /* remove from connections list first */ + + for ( prevlc = NULL, tmplc = ld->ld_conns; + tmplc != NULL; + tmplc = tmplc->lconn_next ) + { + if ( tmplc == lc ) { + if ( prevlc == NULL ) { + ld->ld_conns = tmplc->lconn_next; + } else { + prevlc->lconn_next = tmplc->lconn_next; + } + if ( ld->ld_defconn == lc ) { + ld->ld_defconn = NULL; + } + break; + } + prevlc = tmplc; + } + + /* process connection callbacks */ + { + struct ldapoptions *lo; + ldaplist *ll; + ldap_conncb *cb; + + lo = &ld->ld_options; + LDAP_MUTEX_LOCK( &lo->ldo_mutex ); + if ( lo->ldo_conn_cbs ) { + for ( ll=lo->ldo_conn_cbs; ll; ll=ll->ll_next ) { + cb = ll->ll_data; + cb->lc_del( ld, lc->lconn_sb, cb ); + } + } + LDAP_MUTEX_UNLOCK( &lo->ldo_mutex ); + lo = LDAP_INT_GLOBAL_OPT(); + LDAP_MUTEX_LOCK( &lo->ldo_mutex ); + if ( lo->ldo_conn_cbs ) { + for ( ll=lo->ldo_conn_cbs; ll; ll=ll->ll_next ) { + cb = ll->ll_data; + cb->lc_del( ld, lc->lconn_sb, cb ); + } + } + LDAP_MUTEX_UNLOCK( &lo->ldo_mutex ); + } + + if ( lc->lconn_status == LDAP_CONNST_CONNECTED ) { + ldap_mark_select_clear( ld, lc->lconn_sb ); + if ( unbind ) { + ldap_send_unbind( ld, lc->lconn_sb, + NULL, NULL ); + } + } + + if ( lc->lconn_ber != NULL ) { + ber_free( lc->lconn_ber, 1 ); + } + + ldap_int_sasl_close( ld, lc ); +#ifdef HAVE_GSSAPI + ldap_int_gssapi_close( ld, lc ); +#endif + + ldap_free_urllist( lc->lconn_server ); + + /* FIXME: is this at all possible? + * ldap_ld_free() in unbind.c calls ldap_free_connection() + * with force == 1 __after__ explicitly calling + * ldap_free_request() on all requests */ + if ( force ) { + LDAPRequest *lr; + + for ( lr = ld->ld_requests; lr; ) { + LDAPRequest *lr_next = lr->lr_next; + + if ( lr->lr_conn == lc ) { + ldap_free_request_int( ld, lr ); + } + + lr = lr_next; + } + } + + if ( lc->lconn_sb != ld->ld_sb ) { + ber_sockbuf_free( lc->lconn_sb ); + } else { + ber_int_sb_close( lc->lconn_sb ); + } + + if ( lc->lconn_rebind_queue != NULL) { + int i; + for( i = 0; lc->lconn_rebind_queue[i] != NULL; i++ ) { + LDAP_VFREE( lc->lconn_rebind_queue[i] ); + } + LDAP_FREE( lc->lconn_rebind_queue ); + } + + LDAP_FREE( lc ); + + Debug( LDAP_DEBUG_TRACE, + "ldap_free_connection: actually freed\n", + 0, 0, 0 ); + + } else { + lc->lconn_lastused = time( NULL ); + Debug( LDAP_DEBUG_TRACE, "ldap_free_connection: refcnt %d\n", + lc->lconn_refcnt, 0, 0 ); + } +} + + +/* Protects self with ld_conn_mutex */ +#ifdef LDAP_DEBUG +void +ldap_dump_connection( LDAP *ld, LDAPConn *lconns, int all ) +{ + LDAPConn *lc; + char timebuf[32]; + + Debug( LDAP_DEBUG_TRACE, "** ld %p Connection%s:\n", (void *)ld, all ? "s" : "", 0 ); + LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); + for ( lc = lconns; lc != NULL; lc = lc->lconn_next ) { + if ( lc->lconn_server != NULL ) { + Debug( LDAP_DEBUG_TRACE, "* host: %s port: %d%s\n", + ( lc->lconn_server->lud_host == NULL ) ? "(null)" + : lc->lconn_server->lud_host, + lc->lconn_server->lud_port, ( lc->lconn_sb == + ld->ld_sb ) ? " (default)" : "" ); + } + Debug( LDAP_DEBUG_TRACE, " refcnt: %d status: %s\n", lc->lconn_refcnt, + ( lc->lconn_status == LDAP_CONNST_NEEDSOCKET ) + ? "NeedSocket" : + ( lc->lconn_status == LDAP_CONNST_CONNECTING ) + ? "Connecting" : "Connected", 0 ); + Debug( LDAP_DEBUG_TRACE, " last used: %s%s\n", + ldap_pvt_ctime( &lc->lconn_lastused, timebuf ), + lc->lconn_rebind_inprogress ? " rebind in progress" : "", 0 ); + if ( lc->lconn_rebind_inprogress ) { + if ( lc->lconn_rebind_queue != NULL) { + int i; + + for ( i = 0; lc->lconn_rebind_queue[i] != NULL; i++ ) { + int j; + for( j = 0; lc->lconn_rebind_queue[i][j] != 0; j++ ) { + Debug( LDAP_DEBUG_TRACE, " queue %d entry %d - %s\n", + i, j, lc->lconn_rebind_queue[i][j] ); + } + } + } else { + Debug( LDAP_DEBUG_TRACE, " queue is empty\n", 0, 0, 0 ); + } + } + Debug( LDAP_DEBUG_TRACE, "\n", 0, 0, 0 ); + if ( !all ) { + break; + } + } + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); +} + + +/* protected by req_mutex and res_mutex */ +void +ldap_dump_requests_and_responses( LDAP *ld ) +{ + LDAPRequest *lr; + LDAPMessage *lm, *l; + int i; + + Debug( LDAP_DEBUG_TRACE, "** ld %p Outstanding Requests:\n", + (void *)ld, 0, 0 ); + lr = ld->ld_requests; + if ( lr == NULL ) { + Debug( LDAP_DEBUG_TRACE, " Empty\n", 0, 0, 0 ); + } + for ( i = 0; lr != NULL; lr = lr->lr_next, i++ ) { + Debug( LDAP_DEBUG_TRACE, " * msgid %d, origid %d, status %s\n", + lr->lr_msgid, lr->lr_origid, + ( lr->lr_status == LDAP_REQST_INPROGRESS ) ? "InProgress" : + ( lr->lr_status == LDAP_REQST_CHASINGREFS ) ? "ChasingRefs" : + ( lr->lr_status == LDAP_REQST_NOTCONNECTED ) ? "NotConnected" : + ( lr->lr_status == LDAP_REQST_WRITING ) ? "Writing" : + ( lr->lr_status == LDAP_REQST_COMPLETED ) ? "RequestCompleted" + : "InvalidStatus" ); + Debug( LDAP_DEBUG_TRACE, " outstanding referrals %d, parent count %d\n", + lr->lr_outrefcnt, lr->lr_parentcnt, 0 ); + } + Debug( LDAP_DEBUG_TRACE, " ld %p request count %d (abandoned %lu)\n", + (void *)ld, i, ld->ld_nabandoned ); + Debug( LDAP_DEBUG_TRACE, "** ld %p Response Queue:\n", (void *)ld, 0, 0 ); + if ( ( lm = ld->ld_responses ) == NULL ) { + Debug( LDAP_DEBUG_TRACE, " Empty\n", 0, 0, 0 ); + } + for ( i = 0; lm != NULL; lm = lm->lm_next, i++ ) { + Debug( LDAP_DEBUG_TRACE, " * msgid %d, type %lu\n", + lm->lm_msgid, (unsigned long)lm->lm_msgtype, 0 ); + if ( lm->lm_chain != NULL ) { + Debug( LDAP_DEBUG_TRACE, " chained responses:\n", 0, 0, 0 ); + for ( l = lm->lm_chain; l != NULL; l = l->lm_chain ) { + Debug( LDAP_DEBUG_TRACE, + " * msgid %d, type %lu\n", + l->lm_msgid, + (unsigned long)l->lm_msgtype, 0 ); + } + } + } + Debug( LDAP_DEBUG_TRACE, " ld %p response count %d\n", (void *)ld, i, 0 ); +} +#endif /* LDAP_DEBUG */ + +/* protected by req_mutex */ +static void +ldap_free_request_int( LDAP *ld, LDAPRequest *lr ) +{ + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex ); + /* if lr_refcnt > 0, the request has been looked up + * by ldap_find_request_by_msgid(); if in the meanwhile + * the request is free()'d by someone else, just decrease + * the reference count and extract it from the request + * list; later on, it will be freed. */ + if ( lr->lr_prev == NULL ) { + if ( lr->lr_refcnt == 0 ) { + /* free'ing the first request? */ + assert( ld->ld_requests == lr ); + } + + if ( ld->ld_requests == lr ) { + ld->ld_requests = lr->lr_next; + } + + } else { + lr->lr_prev->lr_next = lr->lr_next; + } + + if ( lr->lr_next != NULL ) { + lr->lr_next->lr_prev = lr->lr_prev; + } + + if ( lr->lr_refcnt > 0 ) { + lr->lr_refcnt = -lr->lr_refcnt; + + lr->lr_prev = NULL; + lr->lr_next = NULL; + + return; + } + + if ( lr->lr_ber != NULL ) { + ber_free( lr->lr_ber, 1 ); + lr->lr_ber = NULL; + } + + if ( lr->lr_res_error != NULL ) { + LDAP_FREE( lr->lr_res_error ); + lr->lr_res_error = NULL; + } + + if ( lr->lr_res_matched != NULL ) { + LDAP_FREE( lr->lr_res_matched ); + lr->lr_res_matched = NULL; + } + + LDAP_FREE( lr ); +} + +/* protected by req_mutex */ +void +ldap_free_request( LDAP *ld, LDAPRequest *lr ) +{ + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex ); + Debug( LDAP_DEBUG_TRACE, "ldap_free_request (origid %d, msgid %d)\n", + lr->lr_origid, lr->lr_msgid, 0 ); + + /* free all referrals (child requests) */ + while ( lr->lr_child ) { + ldap_free_request( ld, lr->lr_child ); + } + + if ( lr->lr_parent != NULL ) { + LDAPRequest **lrp; + + --lr->lr_parent->lr_outrefcnt; + for ( lrp = &lr->lr_parent->lr_child; + *lrp && *lrp != lr; + lrp = &(*lrp)->lr_refnext ); + + if ( *lrp == lr ) { + *lrp = lr->lr_refnext; + } + } + ldap_free_request_int( ld, lr ); +} + +/* + * call first time with *cntp = -1 + * when returns *cntp == -1, no referrals are left + * + * NOTE: may replace *refsp, or shuffle the contents + * of the original array. + */ +static int ldap_int_nextref( + LDAP *ld, + char ***refsp, + int *cntp, + void *params ) +{ + assert( refsp != NULL ); + assert( *refsp != NULL ); + assert( cntp != NULL ); + + if ( *cntp < -1 ) { + *cntp = -1; + return -1; + } + + (*cntp)++; + + if ( (*refsp)[ *cntp ] == NULL ) { + *cntp = -1; + } + + return 0; +} + +/* + * Chase v3 referrals + * + * Parameters: + * (IN) ld = LDAP connection handle + * (IN) lr = LDAP Request structure + * (IN) refs = array of pointers to referral strings that we will chase + * The array will be free'd by this function when no longer needed + * (IN) sref != 0 if following search reference + * (OUT) errstrp = Place to return a string of referrals which could not be followed + * (OUT) hadrefp = 1 if sucessfully followed referral + * + * Return value - number of referrals followed + * + * Protected by res_mutex, conn_mutex and req_mutex (try_read1msg) + */ +int +ldap_chase_v3referrals( LDAP *ld, LDAPRequest *lr, char **refs, int sref, char **errstrp, int *hadrefp ) +{ + char *unfollowed; + int unfollowedcnt = 0; + LDAPRequest *origreq; + LDAPURLDesc *srv = NULL; + BerElement *ber; + char **refarray = NULL; + LDAPConn *lc; + int rc, count, i, j, id; + LDAPreqinfo rinfo; + LDAP_NEXTREF_PROC *nextref_proc = ld->ld_nextref_proc ? ld->ld_nextref_proc : ldap_int_nextref; + + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex ); + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex ); + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex ); + Debug( LDAP_DEBUG_TRACE, "ldap_chase_v3referrals\n", 0, 0, 0 ); + + ld->ld_errno = LDAP_SUCCESS; /* optimistic */ + *hadrefp = 0; + + unfollowed = NULL; + rc = count = 0; + + /* If no referrals in array, return */ + if ( (refs == NULL) || ( (refs)[0] == NULL) ) { + rc = 0; + goto done; + } + + /* Check for hop limit exceeded */ + if ( lr->lr_parentcnt >= ld->ld_refhoplimit ) { + Debug( LDAP_DEBUG_ANY, + "more than %d referral hops (dropping)\n", ld->ld_refhoplimit, 0, 0 ); + ld->ld_errno = LDAP_REFERRAL_LIMIT_EXCEEDED; + rc = -1; + goto done; + } + + /* find original request */ + for ( origreq = lr; + origreq->lr_parent != NULL; + origreq = origreq->lr_parent ) + { + /* empty */ ; + } + + refarray = refs; + refs = NULL; + + /* parse out & follow referrals */ + /* NOTE: if nextref_proc == ldap_int_nextref, params is ignored */ + i = -1; + for ( nextref_proc( ld, &refarray, &i, ld->ld_nextref_params ); + i != -1; + nextref_proc( ld, &refarray, &i, ld->ld_nextref_params ) ) + { + + /* Parse the referral URL */ + rc = ldap_url_parse_ext( refarray[i], &srv, LDAP_PVT_URL_PARSE_NOEMPTY_DN ); + if ( rc != LDAP_URL_SUCCESS ) { + /* ldap_url_parse_ext() returns LDAP_URL_* errors + * which do not map on API errors */ + ld->ld_errno = LDAP_PARAM_ERROR; + rc = -1; + goto done; + } + + if( srv->lud_crit_exts ) { + int ok = 0; +#ifdef HAVE_TLS + /* If StartTLS is the only critical ext, OK. */ + if ( find_tls_ext( srv ) == 2 && srv->lud_crit_exts == 1 ) + ok = 1; +#endif + if ( !ok ) { + /* we do not support any other extensions */ + ld->ld_errno = LDAP_NOT_SUPPORTED; + rc = -1; + goto done; + } + } + + /* check connection for re-bind in progress */ + if (( lc = find_connection( ld, srv, 1 )) != NULL ) { + /* See if we've already requested this DN with this conn */ + LDAPRequest *lp; + int looped = 0; + ber_len_t len = srv->lud_dn ? strlen( srv->lud_dn ) : 0; + for ( lp = origreq; lp; ) { + if ( lp->lr_conn == lc + && len == lp->lr_dn.bv_len + && len + && strncmp( srv->lud_dn, lp->lr_dn.bv_val, len ) == 0 ) + { + looped = 1; + break; + } + if ( lp == origreq ) { + lp = lp->lr_child; + } else { + lp = lp->lr_refnext; + } + } + if ( looped ) { + ldap_free_urllist( srv ); + srv = NULL; + ld->ld_errno = LDAP_CLIENT_LOOP; + rc = -1; + continue; + } + + if ( lc->lconn_rebind_inprogress ) { + /* We are already chasing a referral or search reference and a + * bind on that connection is in progress. We must queue + * referrals on that connection, so we don't get a request + * going out before the bind operation completes. This happens + * if two search references come in one behind the other + * for the same server with different contexts. + */ + Debug( LDAP_DEBUG_TRACE, + "ldap_chase_v3referrals: queue referral \"%s\"\n", + refarray[i], 0, 0); + if( lc->lconn_rebind_queue == NULL ) { + /* Create a referral list */ + lc->lconn_rebind_queue = + (char ***) LDAP_MALLOC( sizeof(void *) * 2); + + if( lc->lconn_rebind_queue == NULL) { + ld->ld_errno = LDAP_NO_MEMORY; + rc = -1; + goto done; + } + + lc->lconn_rebind_queue[0] = refarray; + lc->lconn_rebind_queue[1] = NULL; + refarray = NULL; + + } else { + /* Count how many referral arrays we already have */ + for( j = 0; lc->lconn_rebind_queue[j] != NULL; j++) { + /* empty */; + } + + /* Add the new referral to the list */ + lc->lconn_rebind_queue = (char ***) LDAP_REALLOC( + lc->lconn_rebind_queue, sizeof(void *) * (j + 2)); + + if( lc->lconn_rebind_queue == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + rc = -1; + goto done; + } + lc->lconn_rebind_queue[j] = refarray; + lc->lconn_rebind_queue[j+1] = NULL; + refarray = NULL; + } + + /* We have queued the referral/reference, now just return */ + rc = 0; + *hadrefp = 1; + count = 1; /* Pretend we already followed referral */ + goto done; + } + } + /* Re-encode the request with the new starting point of the search. + * Note: In the future we also need to replace the filter if one + * was provided with the search reference + */ + + /* For references we don't want old dn if new dn empty */ + if ( sref && srv->lud_dn == NULL ) { + srv->lud_dn = LDAP_STRDUP( "" ); + } + + LDAP_NEXT_MSGID( ld, id ); + ber = re_encode_request( ld, origreq->lr_ber, id, + sref, srv, &rinfo.ri_request ); + + if( ber == NULL ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + rc = -1; + goto done; + } + + Debug( LDAP_DEBUG_TRACE, + "ldap_chase_v3referral: msgid %d, url \"%s\"\n", + lr->lr_msgid, refarray[i], 0); + + /* Send the new request to the server - may require a bind */ + rinfo.ri_msgid = origreq->lr_origid; + rinfo.ri_url = refarray[i]; + rc = ldap_send_server_request( ld, ber, id, + origreq, &srv, NULL, &rinfo, 0, 1 ); + if ( rc < 0 ) { + /* Failure, try next referral in the list */ + Debug( LDAP_DEBUG_ANY, "Unable to chase referral \"%s\" (%d: %s)\n", + refarray[i], ld->ld_errno, ldap_err2string( ld->ld_errno ) ); + unfollowedcnt += ldap_append_referral( ld, &unfollowed, refarray[i] ); + ldap_free_urllist( srv ); + srv = NULL; + ld->ld_errno = LDAP_REFERRAL; + } else { + /* Success, no need to try this referral list further */ + rc = 0; + ++count; + *hadrefp = 1; + + /* check if there is a queue of referrals that came in during bind */ + if ( lc == NULL) { + lc = find_connection( ld, srv, 1 ); + if ( lc == NULL ) { + ld->ld_errno = LDAP_OPERATIONS_ERROR; + rc = -1; + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); + goto done; + } + } + + if ( lc->lconn_rebind_queue != NULL ) { + /* Release resources of previous list */ + LDAP_VFREE( refarray ); + refarray = NULL; + ldap_free_urllist( srv ); + srv = NULL; + + /* Pull entries off end of queue so list always null terminated */ + for( j = 0; lc->lconn_rebind_queue[j] != NULL; j++ ) + ; + refarray = lc->lconn_rebind_queue[j - 1]; + lc->lconn_rebind_queue[j-1] = NULL; + /* we pulled off last entry from queue, free queue */ + if ( j == 1 ) { + LDAP_FREE( lc->lconn_rebind_queue ); + lc->lconn_rebind_queue = NULL; + } + /* restart the loop the with new referral list */ + i = -1; + continue; + } + break; /* referral followed, break out of for loop */ + } + } /* end for loop */ +done: + LDAP_VFREE( refarray ); + ldap_free_urllist( srv ); + LDAP_FREE( *errstrp ); + + if( rc == 0 ) { + *errstrp = NULL; + LDAP_FREE( unfollowed ); + return count; + } else { + *errstrp = unfollowed; + return rc; + } +} + +/* + * XXX merging of errors in this routine needs to be improved + * Protected by res_mutex, conn_mutex and req_mutex (try_read1msg) + */ +int +ldap_chase_referrals( LDAP *ld, + LDAPRequest *lr, + char **errstrp, + int sref, + int *hadrefp ) +{ + int rc, count, id; + unsigned len; + char *p, *ref, *unfollowed; + LDAPRequest *origreq; + LDAPURLDesc *srv; + BerElement *ber; + LDAPreqinfo rinfo; + LDAPConn *lc; + + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex ); + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex ); + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex ); + Debug( LDAP_DEBUG_TRACE, "ldap_chase_referrals\n", 0, 0, 0 ); + + ld->ld_errno = LDAP_SUCCESS; /* optimistic */ + *hadrefp = 0; + + if ( *errstrp == NULL ) { + return( 0 ); + } + + len = strlen( *errstrp ); + for ( p = *errstrp; len >= LDAP_REF_STR_LEN; ++p, --len ) { + if ( strncasecmp( p, LDAP_REF_STR, LDAP_REF_STR_LEN ) == 0 ) { + *p = '\0'; + p += LDAP_REF_STR_LEN; + break; + } + } + + if ( len < LDAP_REF_STR_LEN ) { + return( 0 ); + } + + if ( lr->lr_parentcnt >= ld->ld_refhoplimit ) { + Debug( LDAP_DEBUG_ANY, + "more than %d referral hops (dropping)\n", + ld->ld_refhoplimit, 0, 0 ); + /* XXX report as error in ld->ld_errno? */ + return( 0 ); + } + + /* find original request */ + for ( origreq = lr; origreq->lr_parent != NULL; + origreq = origreq->lr_parent ) { + /* empty */; + } + + unfollowed = NULL; + rc = count = 0; + + /* parse out & follow referrals */ + for ( ref = p; rc == 0 && ref != NULL; ref = p ) { + p = strchr( ref, '\n' ); + if ( p != NULL ) { + *p++ = '\0'; + } + + rc = ldap_url_parse_ext( ref, &srv, LDAP_PVT_URL_PARSE_NOEMPTY_DN ); + if ( rc != LDAP_URL_SUCCESS ) { + Debug( LDAP_DEBUG_TRACE, + "ignoring %s referral <%s>\n", + ref, rc == LDAP_URL_ERR_BADSCHEME ? "unknown" : "incorrect", 0 ); + rc = ldap_append_referral( ld, &unfollowed, ref ); + *hadrefp = 1; + continue; + } + + Debug( LDAP_DEBUG_TRACE, + "chasing LDAP referral: <%s>\n", ref, 0, 0 ); + + *hadrefp = 1; + + /* See if we've already been here */ + if (( lc = find_connection( ld, srv, 1 )) != NULL ) { + LDAPRequest *lp; + int looped = 0; + ber_len_t len = srv->lud_dn ? strlen( srv->lud_dn ) : 0; + for ( lp = lr; lp; lp = lp->lr_parent ) { + if ( lp->lr_conn == lc + && len == lp->lr_dn.bv_len ) + { + if ( len && strncmp( srv->lud_dn, lp->lr_dn.bv_val, len ) ) + continue; + looped = 1; + break; + } + } + if ( looped ) { + ldap_free_urllist( srv ); + ld->ld_errno = LDAP_CLIENT_LOOP; + rc = -1; + continue; + } + } + + LDAP_NEXT_MSGID( ld, id ); + ber = re_encode_request( ld, origreq->lr_ber, + id, sref, srv, &rinfo.ri_request ); + + if ( ber == NULL ) { + ldap_free_urllist( srv ); + return -1 ; + } + + /* copy the complete referral for rebind process */ + rinfo.ri_url = LDAP_STRDUP( ref ); + + rinfo.ri_msgid = origreq->lr_origid; + + rc = ldap_send_server_request( ld, ber, id, + lr, &srv, NULL, &rinfo, 0, 1 ); + LDAP_FREE( rinfo.ri_url ); + + if( rc >= 0 ) { + ++count; + } else { + Debug( LDAP_DEBUG_ANY, + "Unable to chase referral \"%s\" (%d: %s)\n", + ref, ld->ld_errno, ldap_err2string( ld->ld_errno ) ); + rc = ldap_append_referral( ld, &unfollowed, ref ); + } + + ldap_free_urllist(srv); + } + + LDAP_FREE( *errstrp ); + *errstrp = unfollowed; + + return(( rc == 0 ) ? count : rc ); +} + + +int +ldap_append_referral( LDAP *ld, char **referralsp, char *s ) +{ + int first; + + if ( *referralsp == NULL ) { + first = 1; + *referralsp = (char *)LDAP_MALLOC( strlen( s ) + LDAP_REF_STR_LEN + + 1 ); + } else { + first = 0; + *referralsp = (char *)LDAP_REALLOC( *referralsp, + strlen( *referralsp ) + strlen( s ) + 2 ); + } + + if ( *referralsp == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + return( -1 ); + } + + if ( first ) { + strcpy( *referralsp, LDAP_REF_STR ); + } else { + strcat( *referralsp, "\n" ); + } + strcat( *referralsp, s ); + + return( 0 ); +} + + + +static BerElement * +re_encode_request( LDAP *ld, + BerElement *origber, + ber_int_t msgid, + int sref, + LDAPURLDesc *srv, + int *type ) +{ + /* + * XXX this routine knows way too much about how the lber library works! + */ + ber_int_t along; + ber_tag_t tag; + ber_tag_t rtag; + ber_int_t ver; + ber_int_t scope; + int rc; + BerElement tmpber, *ber; + struct berval dn; + + Debug( LDAP_DEBUG_TRACE, + "re_encode_request: new msgid %ld, new dn <%s>\n", + (long) msgid, + ( srv == NULL || srv->lud_dn == NULL) ? "NONE" : srv->lud_dn, 0 ); + + tmpber = *origber; + + /* + * all LDAP requests are sequences that start with a message id. + * For all except delete, this is followed by a sequence that is + * tagged with the operation code. For delete, the provided DN + * is not wrapped by a sequence. + */ + rtag = ber_scanf( &tmpber, "{it", /*}*/ &along, &tag ); + + if ( rtag == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + return( NULL ); + } + + assert( tag != 0); + if ( tag == LDAP_REQ_BIND ) { + /* bind requests have a version number before the DN & other stuff */ + rtag = ber_scanf( &tmpber, "{im" /*}*/, &ver, &dn ); + + } else if ( tag == LDAP_REQ_DELETE ) { + /* delete requests don't have a DN wrapping sequence */ + rtag = ber_scanf( &tmpber, "m", &dn ); + + } else if ( tag == LDAP_REQ_SEARCH ) { + /* search requests need to be re-scope-ed */ + rtag = ber_scanf( &tmpber, "{me" /*"}"*/, &dn, &scope ); + + if( srv->lud_scope != LDAP_SCOPE_DEFAULT ) { + /* use the scope provided in reference */ + scope = srv->lud_scope; + + } else if ( sref ) { + /* use scope implied by previous operation + * base -> base + * one -> base + * subtree -> subtree + * subordinate -> subtree + */ + switch( scope ) { + default: + case LDAP_SCOPE_BASE: + case LDAP_SCOPE_ONELEVEL: + scope = LDAP_SCOPE_BASE; + break; + case LDAP_SCOPE_SUBTREE: + case LDAP_SCOPE_SUBORDINATE: + scope = LDAP_SCOPE_SUBTREE; + break; + } + } + + } else { + rtag = ber_scanf( &tmpber, "{m" /*}*/, &dn ); + } + + if( rtag == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + return NULL; + } + + /* restore character zero'd out by ber_scanf*/ + dn.bv_val[dn.bv_len] = tmpber.ber_tag; + + if (( ber = ldap_alloc_ber_with_options( ld )) == NULL ) { + return NULL; + } + + if ( srv->lud_dn ) { + ber_str2bv( srv->lud_dn, 0, 0, &dn ); + } + + if ( tag == LDAP_REQ_BIND ) { + rc = ber_printf( ber, "{it{iO" /*}}*/, msgid, tag, ver, &dn ); + } else if ( tag == LDAP_REQ_DELETE ) { + rc = ber_printf( ber, "{itON}", msgid, tag, &dn ); + } else if ( tag == LDAP_REQ_SEARCH ) { + rc = ber_printf( ber, "{it{Oe" /*}}*/, msgid, tag, &dn, scope ); + } else { + rc = ber_printf( ber, "{it{O" /*}}*/, msgid, tag, &dn ); + } + + if ( rc == -1 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return NULL; + } + + if ( tag != LDAP_REQ_DELETE && ( + ber_write(ber, tmpber.ber_ptr, ( tmpber.ber_end - tmpber.ber_ptr ), 0) + != ( tmpber.ber_end - tmpber.ber_ptr ) || + ber_printf( ber, /*{{*/ "N}N}" ) == -1 ) ) + { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return NULL; + } + +#ifdef LDAP_DEBUG + if ( ldap_debug & LDAP_DEBUG_PACKETS ) { + Debug( LDAP_DEBUG_ANY, "re_encode_request new request is:\n", + 0, 0, 0 ); + ber_log_dump( LDAP_DEBUG_BER, ldap_debug, ber, 0 ); + } +#endif /* LDAP_DEBUG */ + + *type = tag; /* return request type */ + return ber; +} + + +/* protected by req_mutex */ +LDAPRequest * +ldap_find_request_by_msgid( LDAP *ld, ber_int_t msgid ) +{ + LDAPRequest *lr; + + for ( lr = ld->ld_requests; lr != NULL; lr = lr->lr_next ) { + if ( lr->lr_status == LDAP_REQST_COMPLETED ) { + continue; /* Skip completed requests */ + } + if ( msgid == lr->lr_msgid ) { + lr->lr_refcnt++; + break; + } + } + + return( lr ); +} + +/* protected by req_mutex */ +void +ldap_return_request( LDAP *ld, LDAPRequest *lrx, int freeit ) +{ + LDAPRequest *lr; + + for ( lr = ld->ld_requests; lr != NULL; lr = lr->lr_next ) { + if ( lr == lrx ) { + if ( lr->lr_refcnt > 0 ) { + lr->lr_refcnt--; + + } else if ( lr->lr_refcnt < 0 ) { + lr->lr_refcnt++; + if ( lr->lr_refcnt == 0 ) { + lr = NULL; + } + } + break; + } + } + if ( lr == NULL ) { + ldap_free_request_int( ld, lrx ); + + } else if ( freeit ) { + ldap_free_request( ld, lrx ); + } +} diff --git a/libraries/libldap/result.c b/libraries/libldap/result.c new file mode 100644 index 0000000..5b39c11 --- /dev/null +++ b/libraries/libldap/result.c @@ -0,0 +1,1382 @@ +/* result.c - wait for an ldap result */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + */ +/* This notice applies to changes, created by or for Novell, Inc., + * to preexisting works for which notices appear elsewhere in this file. + * + * Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved. + * + * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND TREATIES. + * USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT TO VERSION + * 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS AVAILABLE AT + * HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE" IN THE + * TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION OF THIS + * WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP PUBLIC + * LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT THE + * PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY. + *--- + * Modification to OpenLDAP source by Novell, Inc. + * April 2000 sfs Add code to process V3 referrals and search results + *--- + * Note: A verbatim copy of version 2.0.1 of the OpenLDAP Public License + * can be found in the file "build/LICENSE-2.0.1" in this distribution + * of OpenLDAP Software. + */ + +/* + * LDAPv3 (RFC 4511) + * LDAPResult ::= SEQUENCE { + * resultCode ENUMERATED { ... }, + * matchedDN LDAPDN, + * diagnosticMessage LDAPString, + * referral [3] Referral OPTIONAL + * } + * Referral ::= SEQUENCE OF LDAPURL (one or more) + * LDAPURL ::= LDAPString (limited to URL chars) + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/errno.h> +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> +#include <ac/unistd.h> + +#include "ldap-int.h" +#include "ldap_log.h" +#include "lutil.h" + +static int ldap_abandoned LDAP_P(( LDAP *ld, ber_int_t msgid )); +static int ldap_mark_abandoned LDAP_P(( LDAP *ld, ber_int_t msgid )); +static int wait4msg LDAP_P(( LDAP *ld, ber_int_t msgid, int all, struct timeval *timeout, + LDAPMessage **result )); +static ber_tag_t try_read1msg LDAP_P(( LDAP *ld, ber_int_t msgid, + int all, LDAPConn *lc, LDAPMessage **result )); +static ber_tag_t build_result_ber LDAP_P(( LDAP *ld, BerElement **bp, LDAPRequest *lr )); +static void merge_error_info LDAP_P(( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr )); +static LDAPMessage * chkResponseList LDAP_P(( LDAP *ld, int msgid, int all)); + +#define LDAP_MSG_X_KEEP_LOOKING (-2) + + +/* + * ldap_result - wait for an ldap result response to a message from the + * ldap server. If msgid is LDAP_RES_ANY (-1), any message will be + * accepted. If msgid is LDAP_RES_UNSOLICITED (0), any unsolicited + * message is accepted. Otherwise ldap_result will wait for a response + * with msgid. If all is LDAP_MSG_ONE (0) the first message with id + * msgid will be accepted, otherwise, ldap_result will wait for all + * responses with id msgid and then return a pointer to the entire list + * of messages. In general, this is only useful for search responses, + * which can be of three message types (zero or more entries, zero or + * search references, followed by an ldap result). An extension to + * LDAPv3 allows partial extended responses to be returned in response + * to any request. The type of the first message received is returned. + * When waiting, any messages that have been abandoned/discarded are + * discarded. + * + * Example: + * ldap_result( s, msgid, all, timeout, result ) + */ +int +ldap_result( + LDAP *ld, + int msgid, + int all, + struct timeval *timeout, + LDAPMessage **result ) +{ + int rc; + + assert( ld != NULL ); + assert( result != NULL ); + + Debug( LDAP_DEBUG_TRACE, "ldap_result ld %p msgid %d\n", (void *)ld, msgid, 0 ); + + if (ld->ld_errno == LDAP_LOCAL_ERROR || ld->ld_errno == LDAP_SERVER_DOWN) + return -1; + + LDAP_MUTEX_LOCK( &ld->ld_res_mutex ); + rc = wait4msg( ld, msgid, all, timeout, result ); + LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex ); + + return rc; +} + +/* protected by res_mutex */ +static LDAPMessage * +chkResponseList( + LDAP *ld, + int msgid, + int all) +{ + LDAPMessage *lm, **lastlm, *nextlm; + int cnt = 0; + + /* + * Look through the list of responses we have received on + * this association and see if the response we're interested in + * is there. If it is, return it. If not, call wait4msg() to + * wait until it arrives or timeout occurs. + */ + + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex ); + + Debug( LDAP_DEBUG_TRACE, + "ldap_chkResponseList ld %p msgid %d all %d\n", + (void *)ld, msgid, all ); + + lastlm = &ld->ld_responses; + for ( lm = ld->ld_responses; lm != NULL; lm = nextlm ) { + nextlm = lm->lm_next; + ++cnt; + + if ( ldap_abandoned( ld, lm->lm_msgid ) ) { + Debug( LDAP_DEBUG_ANY, + "response list msg abandoned, " + "msgid %d message type %s\n", + lm->lm_msgid, ldap_int_msgtype2str( lm->lm_msgtype ), 0 ); + + switch ( lm->lm_msgtype ) { + case LDAP_RES_SEARCH_ENTRY: + case LDAP_RES_SEARCH_REFERENCE: + case LDAP_RES_INTERMEDIATE: + break; + + default: + /* there's no need to keep the id + * in the abandoned list any longer */ + ldap_mark_abandoned( ld, lm->lm_msgid ); + break; + } + + /* Remove this entry from list */ + *lastlm = nextlm; + + ldap_msgfree( lm ); + + continue; + } + + if ( msgid == LDAP_RES_ANY || lm->lm_msgid == msgid ) { + LDAPMessage *tmp; + + if ( all == LDAP_MSG_ONE || + all == LDAP_MSG_RECEIVED || + msgid == LDAP_RES_UNSOLICITED ) + { + break; + } + + tmp = lm->lm_chain_tail; + if ( tmp->lm_msgtype == LDAP_RES_SEARCH_ENTRY || + tmp->lm_msgtype == LDAP_RES_SEARCH_REFERENCE || + tmp->lm_msgtype == LDAP_RES_INTERMEDIATE ) + { + tmp = NULL; + } + + if ( tmp == NULL ) { + lm = NULL; + } + + break; + } + lastlm = &lm->lm_next; + } + + if ( lm != NULL ) { + /* Found an entry, remove it from the list */ + if ( all == LDAP_MSG_ONE && lm->lm_chain != NULL ) { + *lastlm = lm->lm_chain; + lm->lm_chain->lm_next = lm->lm_next; + lm->lm_chain->lm_chain_tail = ( lm->lm_chain_tail != lm ) ? lm->lm_chain_tail : lm->lm_chain; + lm->lm_chain = NULL; + lm->lm_chain_tail = NULL; + } else { + *lastlm = lm->lm_next; + } + lm->lm_next = NULL; + } + +#ifdef LDAP_DEBUG + if ( lm == NULL) { + Debug( LDAP_DEBUG_TRACE, + "ldap_chkResponseList returns ld %p NULL\n", (void *)ld, 0, 0); + } else { + Debug( LDAP_DEBUG_TRACE, + "ldap_chkResponseList returns ld %p msgid %d, type 0x%02lx\n", + (void *)ld, lm->lm_msgid, (unsigned long)lm->lm_msgtype ); + } +#endif + + return lm; +} + +/* protected by res_mutex */ +static int +wait4msg( + LDAP *ld, + ber_int_t msgid, + int all, + struct timeval *timeout, + LDAPMessage **result ) +{ + int rc; + struct timeval tv = { 0 }, + tv0 = { 0 }, + start_time_tv = { 0 }, + *tvp = NULL; + LDAPConn *lc; + + assert( ld != NULL ); + assert( result != NULL ); + + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex ); + + if ( timeout == NULL && ld->ld_options.ldo_tm_api.tv_sec >= 0 ) { + tv = ld->ld_options.ldo_tm_api; + timeout = &tv; + } + +#ifdef LDAP_DEBUG + if ( timeout == NULL ) { + Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p msgid %d (infinite timeout)\n", + (void *)ld, msgid, 0 ); + } else { + Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p msgid %d (timeout %ld usec)\n", + (void *)ld, msgid, (long)timeout->tv_sec * 1000000 + timeout->tv_usec ); + } +#endif /* LDAP_DEBUG */ + + if ( timeout != NULL && timeout->tv_sec != -1 ) { + tv0 = *timeout; + tv = *timeout; + tvp = &tv; +#ifdef HAVE_GETTIMEOFDAY + gettimeofday( &start_time_tv, NULL ); +#else /* ! HAVE_GETTIMEOFDAY */ + start_time_tv.tv_sec = time( NULL ); + start_time_tv.tv_usec = 0; +#endif /* ! HAVE_GETTIMEOFDAY */ + } + + rc = LDAP_MSG_X_KEEP_LOOKING; + while ( rc == LDAP_MSG_X_KEEP_LOOKING ) { +#ifdef LDAP_DEBUG + if ( ldap_debug & LDAP_DEBUG_TRACE ) { + Debug( LDAP_DEBUG_TRACE, "wait4msg continue ld %p msgid %d all %d\n", + (void *)ld, msgid, all ); + ldap_dump_connection( ld, ld->ld_conns, 1 ); + LDAP_MUTEX_LOCK( &ld->ld_req_mutex ); + ldap_dump_requests_and_responses( ld ); + LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex ); + } +#endif /* LDAP_DEBUG */ + + if ( ( *result = chkResponseList( ld, msgid, all ) ) != NULL ) { + rc = (*result)->lm_msgtype; + + } else { + int lc_ready = 0; + + LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); + for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) { + if ( ber_sockbuf_ctrl( lc->lconn_sb, + LBER_SB_OPT_DATA_READY, NULL ) ) + { + lc_ready = 2; /* ready at ber level, not socket level */ + break; + } + } + + if ( !lc_ready ) { + int err; + rc = ldap_int_select( ld, tvp ); + if ( rc == -1 ) { + err = sock_errno(); +#ifdef LDAP_DEBUG + Debug( LDAP_DEBUG_TRACE, + "ldap_int_select returned -1: errno %d\n", + err, 0, 0 ); +#endif + } + + if ( rc == 0 || ( rc == -1 && ( + !LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_RESTART) + || err != EINTR ) ) ) + { + ld->ld_errno = (rc == -1 ? LDAP_SERVER_DOWN : + LDAP_TIMEOUT); + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); + return( rc ); + } + + if ( rc == -1 ) { + rc = LDAP_MSG_X_KEEP_LOOKING; /* select interrupted: loop */ + + } else { + lc_ready = 1; + } + } + if ( lc_ready ) { + LDAPConn *lnext; + int serviced = 0; + rc = LDAP_MSG_X_KEEP_LOOKING; + LDAP_MUTEX_LOCK( &ld->ld_req_mutex ); + if ( ld->ld_requests && + ld->ld_requests->lr_status == LDAP_REQST_WRITING && + ldap_is_write_ready( ld, + ld->ld_requests->lr_conn->lconn_sb ) ) + { + serviced = 1; + ldap_int_flush_request( ld, ld->ld_requests ); + } + for ( lc = ld->ld_conns; + rc == LDAP_MSG_X_KEEP_LOOKING && lc != NULL; + lc = lnext ) + { + if ( lc->lconn_status == LDAP_CONNST_CONNECTED && + ldap_is_read_ready( ld, lc->lconn_sb ) ) + { + serviced = 1; + /* Don't let it get freed out from under us */ + ++lc->lconn_refcnt; + rc = try_read1msg( ld, msgid, all, lc, result ); + lnext = lc->lconn_next; + + /* Only take locks if we're really freeing */ + if ( lc->lconn_refcnt <= 1 ) { + ldap_free_connection( ld, lc, 0, 1 ); + } else { + --lc->lconn_refcnt; + } + } else { + lnext = lc->lconn_next; + } + } + LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex ); + /* Quit looping if no one handled any socket events */ + if (!serviced && lc_ready == 1) + rc = -1; + } + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); + } + + if ( rc == LDAP_MSG_X_KEEP_LOOKING && tvp != NULL ) { + struct timeval curr_time_tv = { 0 }, + delta_time_tv = { 0 }; + +#ifdef HAVE_GETTIMEOFDAY + gettimeofday( &curr_time_tv, NULL ); +#else /* ! HAVE_GETTIMEOFDAY */ + curr_time_tv.tv_sec = time( NULL ); + curr_time_tv.tv_usec = 0; +#endif /* ! HAVE_GETTIMEOFDAY */ + + /* delta_time = tmp_time - start_time */ + delta_time_tv.tv_sec = curr_time_tv.tv_sec - start_time_tv.tv_sec; + delta_time_tv.tv_usec = curr_time_tv.tv_usec - start_time_tv.tv_usec; + if ( delta_time_tv.tv_usec < 0 ) { + delta_time_tv.tv_sec--; + delta_time_tv.tv_usec += 1000000; + } + + /* tv0 < delta_time ? */ + if ( ( tv0.tv_sec < delta_time_tv.tv_sec ) || + ( ( tv0.tv_sec == delta_time_tv.tv_sec ) && ( tv0.tv_usec < delta_time_tv.tv_usec ) ) ) + { + rc = 0; /* timed out */ + ld->ld_errno = LDAP_TIMEOUT; + break; + } + + /* tv0 -= delta_time */ + tv0.tv_sec -= delta_time_tv.tv_sec; + tv0.tv_usec -= delta_time_tv.tv_usec; + if ( tv0.tv_usec < 0 ) { + tv0.tv_sec--; + tv0.tv_usec += 1000000; + } + + tv.tv_sec = tv0.tv_sec; + tv.tv_usec = tv0.tv_usec; + + Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p %ld s %ld us to go\n", + (void *)ld, (long) tv.tv_sec, (long) tv.tv_usec ); + + start_time_tv.tv_sec = curr_time_tv.tv_sec; + start_time_tv.tv_usec = curr_time_tv.tv_usec; + } + } + + return( rc ); +} + + +/* protected by res_mutex, conn_mutex and req_mutex */ +static ber_tag_t +try_read1msg( + LDAP *ld, + ber_int_t msgid, + int all, + LDAPConn *lc, + LDAPMessage **result ) +{ + BerElement *ber; + LDAPMessage *newmsg, *l, *prev; + ber_int_t id; + ber_tag_t tag; + ber_len_t len; + int foundit = 0; + LDAPRequest *lr, *tmplr, dummy_lr = { 0 }; + BerElement tmpber; + int rc, refer_cnt, hadref, simple_request, err; + ber_int_t lderr; + +#ifdef LDAP_CONNECTIONLESS + LDAPMessage *tmp = NULL, *chain_head = NULL; + int moremsgs = 0, isv2 = 0; +#endif + + assert( ld != NULL ); + assert( lc != NULL ); + + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex ); + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex ); + LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex ); + + Debug( LDAP_DEBUG_TRACE, "read1msg: ld %p msgid %d all %d\n", + (void *)ld, msgid, all ); + +retry: + if ( lc->lconn_ber == NULL ) { + lc->lconn_ber = ldap_alloc_ber_with_options( ld ); + + if ( lc->lconn_ber == NULL ) { + return -1; + } + } + + ber = lc->lconn_ber; + assert( LBER_VALID (ber) ); + + /* get the next message */ + sock_errset(0); +#ifdef LDAP_CONNECTIONLESS + if ( LDAP_IS_UDP(ld) ) { + struct sockaddr_storage from; + if ( ber_int_sb_read( lc->lconn_sb, &from, sizeof(struct sockaddr_storage) ) < 0 ) + goto fail; + if ( ld->ld_options.ldo_version == LDAP_VERSION2 ) isv2 = 1; + } +nextresp3: +#endif + tag = ber_get_next( lc->lconn_sb, &len, ber ); + switch ( tag ) { + case LDAP_TAG_MESSAGE: + /* + * We read a complete message. + * The connection should no longer need this ber. + */ + lc->lconn_ber = NULL; + break; + + case LBER_DEFAULT: +fail: + err = sock_errno(); +#ifdef LDAP_DEBUG + Debug( LDAP_DEBUG_CONNS, + "ber_get_next failed, errno=%d.\n", err, 0, 0 ); +#endif + if ( err == EWOULDBLOCK ) return LDAP_MSG_X_KEEP_LOOKING; + if ( err == EAGAIN ) return LDAP_MSG_X_KEEP_LOOKING; + ld->ld_errno = LDAP_SERVER_DOWN; + --lc->lconn_refcnt; + lc->lconn_status = 0; + return -1; + + default: + ld->ld_errno = LDAP_LOCAL_ERROR; + return -1; + } + + /* message id */ + if ( ber_get_int( ber, &id ) == LBER_ERROR ) { + ber_free( ber, 1 ); + ld->ld_errno = LDAP_DECODING_ERROR; + return( -1 ); + } + + /* id == 0 iff unsolicited notification message (RFC 4511) */ + + /* id < 0 is invalid, just toss it. FIXME: should we disconnect? */ + if ( id < 0 ) { + goto retry_ber; + } + + /* if it's been abandoned, toss it */ + if ( id > 0 ) { + if ( ldap_abandoned( ld, id ) ) { + /* the message type */ + tag = ber_peek_tag( ber, &len ); + switch ( tag ) { + case LDAP_RES_SEARCH_ENTRY: + case LDAP_RES_SEARCH_REFERENCE: + case LDAP_RES_INTERMEDIATE: + case LBER_ERROR: + break; + + default: + /* there's no need to keep the id + * in the abandoned list any longer */ + ldap_mark_abandoned( ld, id ); + break; + } + + Debug( LDAP_DEBUG_ANY, + "abandoned/discarded ld %p msgid %d message type %s\n", + (void *)ld, id, ldap_int_msgtype2str( tag ) ); + +retry_ber: + ber_free( ber, 1 ); + if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) { + goto retry; + } + return( LDAP_MSG_X_KEEP_LOOKING ); /* continue looking */ + } + + lr = ldap_find_request_by_msgid( ld, id ); + if ( lr == NULL ) { + const char *msg = "unknown"; + + /* the message type */ + tag = ber_peek_tag( ber, &len ); + switch ( tag ) { + case LBER_ERROR: + break; + + default: + msg = ldap_int_msgtype2str( tag ); + break; + } + + Debug( LDAP_DEBUG_ANY, + "no request for response on ld %p msgid %d message type %s (tossing)\n", + (void *)ld, id, msg ); + + goto retry_ber; + } + +#ifdef LDAP_CONNECTIONLESS + if ( LDAP_IS_UDP(ld) && isv2 ) { + ber_scanf(ber, "x{"); + } +nextresp2: + ; +#endif + } + + /* the message type */ + tag = ber_peek_tag( ber, &len ); + if ( tag == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + ber_free( ber, 1 ); + return( -1 ); + } + + Debug( LDAP_DEBUG_TRACE, + "read1msg: ld %p msgid %d message type %s\n", + (void *)ld, id, ldap_int_msgtype2str( tag ) ); + + if ( id == 0 ) { + /* unsolicited notification message (RFC 4511) */ + if ( tag != LDAP_RES_EXTENDED ) { + /* toss it */ + goto retry_ber; + + /* strictly speaking, it's an error; from RFC 4511: + +4.4. Unsolicited Notification + + An unsolicited notification is an LDAPMessage sent from the server to + the client that is not in response to any LDAPMessage received by the + server. It is used to signal an extraordinary condition in the + server or in the LDAP session between the client and the server. The + notification is of an advisory nature, and the server will not expect + any response to be returned from the client. + + The unsolicited notification is structured as an LDAPMessage in which + the messageID is zero and protocolOp is set to the extendedResp + choice using the ExtendedResponse type (See Section 4.12). The + responseName field of the ExtendedResponse always contains an LDAPOID + that is unique for this notification. + + * however, since unsolicited responses + * are of advisory nature, better + * toss it, right now + */ + +#if 0 + ld->ld_errno = LDAP_DECODING_ERROR; + ber_free( ber, 1 ); + return( -1 ); +#endif + } + + lr = &dummy_lr; + } + + id = lr->lr_origid; + refer_cnt = 0; + hadref = simple_request = 0; + rc = LDAP_MSG_X_KEEP_LOOKING; /* default is to keep looking (no response found) */ + lr->lr_res_msgtype = tag; + + /* + * Check for V3 search reference + */ + if ( tag == LDAP_RES_SEARCH_REFERENCE ) { + if ( ld->ld_version > LDAP_VERSION2 ) { + /* This is a V3 search reference */ + if ( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) || + lr->lr_parent != NULL ) + { + char **refs = NULL; + tmpber = *ber; + + /* Get the referral list */ + if ( ber_scanf( &tmpber, "{v}", &refs ) == LBER_ERROR ) { + rc = LDAP_DECODING_ERROR; + + } else { + /* Note: refs array is freed by ldap_chase_v3referrals */ + refer_cnt = ldap_chase_v3referrals( ld, lr, refs, + 1, &lr->lr_res_error, &hadref ); + if ( refer_cnt > 0 ) { + /* successfully chased reference */ + /* If haven't got end search, set chasing referrals */ + if ( lr->lr_status != LDAP_REQST_COMPLETED ) { + lr->lr_status = LDAP_REQST_CHASINGREFS; + Debug( LDAP_DEBUG_TRACE, + "read1msg: search ref chased, " + "mark request chasing refs, " + "id = %d\n", + lr->lr_msgid, 0, 0 ); + } + } + } + } + } + + } else if ( tag != LDAP_RES_SEARCH_ENTRY && tag != LDAP_RES_INTERMEDIATE ) { + /* All results that just return a status, i.e. don't return data + * go through the following code. This code also chases V2 referrals + * and checks if all referrals have been chased. + */ + char *lr_res_error = NULL; + + tmpber = *ber; /* struct copy */ + if ( ber_scanf( &tmpber, "{eAA", &lderr, + &lr->lr_res_matched, &lr_res_error ) + != LBER_ERROR ) + { + if ( lr_res_error != NULL ) { + if ( lr->lr_res_error != NULL ) { + (void)ldap_append_referral( ld, &lr->lr_res_error, lr_res_error ); + LDAP_FREE( (char *)lr_res_error ); + + } else { + lr->lr_res_error = lr_res_error; + } + lr_res_error = NULL; + } + + /* Do we need to check for referrals? */ + if ( tag != LDAP_RES_BIND && + ( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) || + lr->lr_parent != NULL )) + { + char **refs = NULL; + ber_len_t len; + + /* Check if V3 referral */ + if ( ber_peek_tag( &tmpber, &len ) == LDAP_TAG_REFERRAL ) { + if ( ld->ld_version > LDAP_VERSION2 ) { + /* Get the referral list */ + if ( ber_scanf( &tmpber, "{v}", &refs) == LBER_ERROR) { + rc = LDAP_DECODING_ERROR; + lr->lr_status = LDAP_REQST_COMPLETED; + Debug( LDAP_DEBUG_TRACE, + "read1msg: referral decode error, " + "mark request completed, ld %p msgid %d\n", + (void *)ld, lr->lr_msgid, 0 ); + + } else { + /* Chase the referral + * refs array is freed by ldap_chase_v3referrals + */ + refer_cnt = ldap_chase_v3referrals( ld, lr, refs, + 0, &lr->lr_res_error, &hadref ); + lr->lr_status = LDAP_REQST_COMPLETED; + Debug( LDAP_DEBUG_TRACE, + "read1msg: referral %s chased, " + "mark request completed, ld %p msgid %d\n", + refer_cnt > 0 ? "" : "not", + (void *)ld, lr->lr_msgid); + if ( refer_cnt < 0 ) { + refer_cnt = 0; + } + } + } + } else { + switch ( lderr ) { + case LDAP_SUCCESS: + case LDAP_COMPARE_TRUE: + case LDAP_COMPARE_FALSE: + break; + + default: + if ( lr->lr_res_error == NULL ) { + break; + } + + /* pedantic, should never happen */ + if ( lr->lr_res_error[ 0 ] == '\0' ) { + LDAP_FREE( lr->lr_res_error ); + lr->lr_res_error = NULL; + break; + } + + /* V2 referrals are in error string */ + refer_cnt = ldap_chase_referrals( ld, lr, + &lr->lr_res_error, -1, &hadref ); + lr->lr_status = LDAP_REQST_COMPLETED; + Debug( LDAP_DEBUG_TRACE, + "read1msg: V2 referral chased, " + "mark request completed, id = %d\n", + lr->lr_msgid, 0, 0 ); + break; + } + } + } + + /* save errno, message, and matched string */ + if ( !hadref || lr->lr_res_error == NULL ) { + lr->lr_res_errno = + lderr == LDAP_PARTIAL_RESULTS + ? LDAP_SUCCESS : lderr; + + } else if ( ld->ld_errno != LDAP_SUCCESS ) { + lr->lr_res_errno = ld->ld_errno; + + } else { + lr->lr_res_errno = LDAP_PARTIAL_RESULTS; + } + } + + /* in any case, don't leave any lr_res_error 'round */ + if ( lr_res_error ) { + LDAP_FREE( lr_res_error ); + } + + Debug( LDAP_DEBUG_TRACE, + "read1msg: ld %p %d new referrals\n", + (void *)ld, refer_cnt, 0 ); + + if ( refer_cnt != 0 ) { /* chasing referrals */ + ber_free( ber, 1 ); + ber = NULL; + if ( refer_cnt < 0 ) { + ldap_return_request( ld, lr, 0 ); + return( -1 ); /* fatal error */ + } + lr->lr_res_errno = LDAP_SUCCESS; /* sucessfully chased referral */ + if ( lr->lr_res_matched ) { + LDAP_FREE( lr->lr_res_matched ); + lr->lr_res_matched = NULL; + } + + } else { + if ( lr->lr_outrefcnt <= 0 && lr->lr_parent == NULL ) { + /* request without any referrals */ + simple_request = ( hadref ? 0 : 1 ); + + } else { + /* request with referrals or child request */ + ber_free( ber, 1 ); + ber = NULL; + } + + lr->lr_status = LDAP_REQST_COMPLETED; /* declare this request done */ + Debug( LDAP_DEBUG_TRACE, + "read1msg: mark request completed, ld %p msgid %d\n", + (void *)ld, lr->lr_msgid, 0); + tmplr = lr; + while ( lr->lr_parent != NULL ) { + merge_error_info( ld, lr->lr_parent, lr ); + + lr = lr->lr_parent; + if ( --lr->lr_outrefcnt > 0 ) { + break; /* not completely done yet */ + } + } + /* ITS#6744: Original lr was refcounted when we retrieved it, + * must release it now that we're working with the parent + */ + if ( tmplr->lr_parent ) { + ldap_return_request( ld, tmplr, 0 ); + } + + /* Check if all requests are finished, lr is now parent */ + tmplr = lr; + if ( tmplr->lr_status == LDAP_REQST_COMPLETED ) { + for ( tmplr = lr->lr_child; + tmplr != NULL; + tmplr = tmplr->lr_refnext ) + { + if ( tmplr->lr_status != LDAP_REQST_COMPLETED ) break; + } + } + + /* This is the parent request if the request has referrals */ + if ( lr->lr_outrefcnt <= 0 && + lr->lr_parent == NULL && + tmplr == NULL ) + { + id = lr->lr_msgid; + tag = lr->lr_res_msgtype; + Debug( LDAP_DEBUG_TRACE, "request done: ld %p msgid %d\n", + (void *)ld, id, 0 ); + Debug( LDAP_DEBUG_TRACE, + "res_errno: %d, res_error: <%s>, " + "res_matched: <%s>\n", + lr->lr_res_errno, + lr->lr_res_error ? lr->lr_res_error : "", + lr->lr_res_matched ? lr->lr_res_matched : "" ); + if ( !simple_request ) { + ber_free( ber, 1 ); + ber = NULL; + if ( build_result_ber( ld, &ber, lr ) + == LBER_ERROR ) + { + rc = -1; /* fatal error */ + } + } + + if ( lr != &dummy_lr ) { + ldap_return_request( ld, lr, 1 ); + } + lr = NULL; + } + + /* + * RFC 4511 unsolicited (id == 0) responses + * shouldn't necessarily end the connection + */ + if ( lc != NULL && id != 0 ) { + --lc->lconn_refcnt; + lc = NULL; + } + } + } + + if ( lr != NULL ) { + if ( lr != &dummy_lr ) { + ldap_return_request( ld, lr, 0 ); + } + lr = NULL; + } + + if ( ber == NULL ) { + return( rc ); + } + + /* try to handle unsolicited responses as appropriate */ + if ( id == 0 && msgid > LDAP_RES_UNSOLICITED ) { + int is_nod = 0; + + tag = ber_peek_tag( &tmpber, &len ); + + /* we have a res oid */ + if ( tag == LDAP_TAG_EXOP_RES_OID ) { + static struct berval bv_nod = BER_BVC( LDAP_NOTICE_OF_DISCONNECTION ); + struct berval resoid = BER_BVNULL; + + if ( ber_scanf( &tmpber, "m", &resoid ) == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + ber_free( ber, 1 ); + return -1; + } + + assert( !BER_BVISEMPTY( &resoid ) ); + + is_nod = ber_bvcmp( &resoid, &bv_nod ) == 0; + + tag = ber_peek_tag( &tmpber, &len ); + } + +#if 0 /* don't need right now */ + /* we have res data */ + if ( tag == LDAP_TAG_EXOP_RES_VALUE ) { + struct berval resdata; + + if ( ber_scanf( &tmpber, "m", &resdata ) == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + ber_free( ber, 0 ); + return ld->ld_errno; + } + + /* use it... */ + } +#endif + + /* handle RFC 4511 "Notice of Disconnection" locally */ + + if ( is_nod ) { + if ( tag == LDAP_TAG_EXOP_RES_VALUE ) { + ld->ld_errno = LDAP_DECODING_ERROR; + ber_free( ber, 1 ); + return -1; + } + + /* get rid of the connection... */ + if ( lc != NULL ) { + --lc->lconn_refcnt; + } + + /* need to return -1, because otherwise + * a valid result is expected */ + ld->ld_errno = lderr; + return -1; + } + } + + /* make a new ldap message */ + newmsg = (LDAPMessage *) LDAP_CALLOC( 1, sizeof(LDAPMessage) ); + if ( newmsg == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + return( -1 ); + } + newmsg->lm_msgid = (int)id; + newmsg->lm_msgtype = tag; + newmsg->lm_ber = ber; + newmsg->lm_chain_tail = newmsg; + +#ifdef LDAP_CONNECTIONLESS + /* CLDAP replies all fit in a single datagram. In LDAPv2 RFC1798 + * the responses are all a sequence wrapped in one message. In + * LDAPv3 each response is in its own message. The datagram must + * end with a SearchResult. We can't just parse each response in + * separate calls to try_read1msg because the header info is only + * present at the beginning of the datagram, not at the beginning + * of each response. So parse all the responses at once and queue + * them up, then pull off the first response to return to the + * caller when all parsing is complete. + */ + if ( LDAP_IS_UDP(ld) ) { + /* If not a result, look for more */ + if ( tag != LDAP_RES_SEARCH_RESULT ) { + int ok = 0; + moremsgs = 1; + if (isv2) { + /* LDAPv2: dup the current ber, skip past the current + * response, and see if there are any more after it. + */ + ber = ber_dup( ber ); + ber_scanf( ber, "x" ); + if ( ber_peek_tag( ber, &len ) != LBER_DEFAULT ) { + /* There's more - dup the ber buffer so they can all be + * individually freed by ldap_msgfree. + */ + struct berval bv; + ber_get_option( ber, LBER_OPT_BER_REMAINING_BYTES, &len ); + bv.bv_val = LDAP_MALLOC( len ); + if ( bv.bv_val ) { + ok = 1; + ber_read( ber, bv.bv_val, len ); + bv.bv_len = len; + ber_init2( ber, &bv, ld->ld_lberoptions ); + } + } + } else { + /* LDAPv3: Just allocate a new ber. Since this is a buffered + * datagram, if the sockbuf is readable we still have data + * to parse. + */ + ber = ldap_alloc_ber_with_options( ld ); + if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) ok = 1; + } + /* set up response chain */ + if ( tmp == NULL ) { + newmsg->lm_next = ld->ld_responses; + ld->ld_responses = newmsg; + chain_head = newmsg; + } else { + tmp->lm_chain = newmsg; + } + chain_head->lm_chain_tail = newmsg; + tmp = newmsg; + /* "ok" means there's more to parse */ + if ( ok ) { + if ( isv2 ) { + goto nextresp2; + + } else { + goto nextresp3; + } + } else { + /* got to end of datagram without a SearchResult. Free + * our dup'd ber, but leave any buffer alone. For v2 case, + * the previous response is still using this buffer. For v3, + * the new ber has no buffer to free yet. + */ + ber_free( ber, 0 ); + return -1; + } + } else if ( moremsgs ) { + /* got search result, and we had multiple responses in 1 datagram. + * stick the result onto the end of the chain, and then pull the + * first response off the head of the chain. + */ + tmp->lm_chain = newmsg; + chain_head->lm_chain_tail = newmsg; + *result = chkResponseList( ld, msgid, all ); + ld->ld_errno = LDAP_SUCCESS; + return( (*result)->lm_msgtype ); + } + } +#endif /* LDAP_CONNECTIONLESS */ + + /* is this the one we're looking for? */ + if ( msgid == LDAP_RES_ANY || id == msgid ) { + if ( all == LDAP_MSG_ONE + || ( newmsg->lm_msgtype != LDAP_RES_SEARCH_RESULT + && newmsg->lm_msgtype != LDAP_RES_SEARCH_ENTRY + && newmsg->lm_msgtype != LDAP_RES_INTERMEDIATE + && newmsg->lm_msgtype != LDAP_RES_SEARCH_REFERENCE ) ) + { + *result = newmsg; + ld->ld_errno = LDAP_SUCCESS; + return( tag ); + + } else if ( newmsg->lm_msgtype == LDAP_RES_SEARCH_RESULT) { + foundit = 1; /* return the chain later */ + } + } + + /* + * if not, we must add it to the list of responses. if + * the msgid is already there, it must be part of an existing + * search response. + */ + + prev = NULL; + for ( l = ld->ld_responses; l != NULL; l = l->lm_next ) { + if ( l->lm_msgid == newmsg->lm_msgid ) { + break; + } + prev = l; + } + + /* not part of an existing search response */ + if ( l == NULL ) { + if ( foundit ) { + *result = newmsg; + goto exit; + } + + newmsg->lm_next = ld->ld_responses; + ld->ld_responses = newmsg; + goto exit; + } + + Debug( LDAP_DEBUG_TRACE, "adding response ld %p msgid %d type %ld:\n", + (void *)ld, newmsg->lm_msgid, (long) newmsg->lm_msgtype ); + + /* part of a search response - add to end of list of entries */ + l->lm_chain_tail->lm_chain = newmsg; + l->lm_chain_tail = newmsg; + + /* return the whole chain if that's what we were looking for */ + if ( foundit ) { + if ( prev == NULL ) { + ld->ld_responses = l->lm_next; + } else { + prev->lm_next = l->lm_next; + } + *result = l; + } + +exit: + if ( foundit ) { + ld->ld_errno = LDAP_SUCCESS; + return( tag ); + } + if ( lc && ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) { + goto retry; + } + return( LDAP_MSG_X_KEEP_LOOKING ); /* continue looking */ +} + + +static ber_tag_t +build_result_ber( LDAP *ld, BerElement **bp, LDAPRequest *lr ) +{ + ber_len_t len; + ber_tag_t tag; + ber_int_t along; + BerElement *ber; + + *bp = NULL; + ber = ldap_alloc_ber_with_options( ld ); + + if( ber == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + return LBER_ERROR; + } + + if ( ber_printf( ber, "{it{ess}}", lr->lr_msgid, + lr->lr_res_msgtype, lr->lr_res_errno, + lr->lr_res_matched ? lr->lr_res_matched : "", + lr->lr_res_error ? lr->lr_res_error : "" ) == -1 ) + { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return( LBER_ERROR ); + } + + ber_reset( ber, 1 ); + + if ( ber_skip_tag( ber, &len ) == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + ber_free( ber, 1 ); + return( LBER_ERROR ); + } + + if ( ber_get_enum( ber, &along ) == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + ber_free( ber, 1 ); + return( LBER_ERROR ); + } + + tag = ber_peek_tag( ber, &len ); + + if ( tag == LBER_ERROR ) { + ld->ld_errno = LDAP_DECODING_ERROR; + ber_free( ber, 1 ); + return( LBER_ERROR ); + } + + *bp = ber; + return tag; +} + + +/* + * Merge error information in "lr" with "parentr" error code and string. + */ +static void +merge_error_info( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr ) +{ + if ( lr->lr_res_errno == LDAP_PARTIAL_RESULTS ) { + parentr->lr_res_errno = lr->lr_res_errno; + if ( lr->lr_res_error != NULL ) { + (void)ldap_append_referral( ld, &parentr->lr_res_error, + lr->lr_res_error ); + } + + } else if ( lr->lr_res_errno != LDAP_SUCCESS && + parentr->lr_res_errno == LDAP_SUCCESS ) + { + parentr->lr_res_errno = lr->lr_res_errno; + if ( parentr->lr_res_error != NULL ) { + LDAP_FREE( parentr->lr_res_error ); + } + parentr->lr_res_error = lr->lr_res_error; + lr->lr_res_error = NULL; + if ( LDAP_NAME_ERROR( lr->lr_res_errno ) ) { + if ( parentr->lr_res_matched != NULL ) { + LDAP_FREE( parentr->lr_res_matched ); + } + parentr->lr_res_matched = lr->lr_res_matched; + lr->lr_res_matched = NULL; + } + } + + Debug( LDAP_DEBUG_TRACE, "merged parent (id %d) error info: ", + parentr->lr_msgid, 0, 0 ); + Debug( LDAP_DEBUG_TRACE, "result errno %d, error <%s>, matched <%s>\n", + parentr->lr_res_errno, + parentr->lr_res_error ? parentr->lr_res_error : "", + parentr->lr_res_matched ? parentr->lr_res_matched : "" ); +} + + + +int +ldap_msgtype( LDAPMessage *lm ) +{ + assert( lm != NULL ); + return ( lm != NULL ) ? (int)lm->lm_msgtype : -1; +} + + +int +ldap_msgid( LDAPMessage *lm ) +{ + assert( lm != NULL ); + + return ( lm != NULL ) ? lm->lm_msgid : -1; +} + + +const char * +ldap_int_msgtype2str( ber_tag_t tag ) +{ + switch( tag ) { + case LDAP_RES_ADD: return "add"; + case LDAP_RES_BIND: return "bind"; + case LDAP_RES_COMPARE: return "compare"; + case LDAP_RES_DELETE: return "delete"; + case LDAP_RES_EXTENDED: return "extended-result"; + case LDAP_RES_INTERMEDIATE: return "intermediate"; + case LDAP_RES_MODIFY: return "modify"; + case LDAP_RES_RENAME: return "rename"; + case LDAP_RES_SEARCH_ENTRY: return "search-entry"; + case LDAP_RES_SEARCH_REFERENCE: return "search-reference"; + case LDAP_RES_SEARCH_RESULT: return "search-result"; + } + return "unknown"; +} + +int +ldap_msgfree( LDAPMessage *lm ) +{ + LDAPMessage *next; + int type = 0; + + Debug( LDAP_DEBUG_TRACE, "ldap_msgfree\n", 0, 0, 0 ); + + for ( ; lm != NULL; lm = next ) { + next = lm->lm_chain; + type = lm->lm_msgtype; + ber_free( lm->lm_ber, 1 ); + LDAP_FREE( (char *) lm ); + } + + return type; +} + +/* + * ldap_msgdelete - delete a message. It returns: + * 0 if the entire message was deleted + * -1 if the message was not found, or only part of it was found + */ +int +ldap_msgdelete( LDAP *ld, int msgid ) +{ + LDAPMessage *lm, *prev; + int rc = 0; + + assert( ld != NULL ); + + Debug( LDAP_DEBUG_TRACE, "ldap_msgdelete ld=%p msgid=%d\n", + (void *)ld, msgid, 0 ); + + LDAP_MUTEX_LOCK( &ld->ld_res_mutex ); + prev = NULL; + for ( lm = ld->ld_responses; lm != NULL; lm = lm->lm_next ) { + if ( lm->lm_msgid == msgid ) { + break; + } + prev = lm; + } + + if ( lm == NULL ) { + rc = -1; + + } else { + if ( prev == NULL ) { + ld->ld_responses = lm->lm_next; + } else { + prev->lm_next = lm->lm_next; + } + } + LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex ); + if ( lm ) { + switch ( ldap_msgfree( lm ) ) { + case LDAP_RES_SEARCH_ENTRY: + case LDAP_RES_SEARCH_REFERENCE: + case LDAP_RES_INTERMEDIATE: + rc = -1; + break; + + default: + break; + } + } + + return rc; +} + + +/* + * ldap_abandoned + * + * return the location of the message id in the array of abandoned + * message ids, or -1 + */ +static int +ldap_abandoned( LDAP *ld, ber_int_t msgid ) +{ + int ret, idx; + assert( msgid >= 0 ); + + LDAP_MUTEX_LOCK( &ld->ld_abandon_mutex ); + ret = ldap_int_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, &idx ); + LDAP_MUTEX_UNLOCK( &ld->ld_abandon_mutex ); + return ret; +} + +/* + * ldap_mark_abandoned + */ +static int +ldap_mark_abandoned( LDAP *ld, ber_int_t msgid ) +{ + int ret, idx; + + assert( msgid >= 0 ); + LDAP_MUTEX_LOCK( &ld->ld_abandon_mutex ); + ret = ldap_int_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, &idx ); + if (ret <= 0) { /* error or already deleted by another thread */ + LDAP_MUTEX_UNLOCK( &ld->ld_abandon_mutex ); + return ret; + } + /* still in abandoned array, so delete */ + ret = ldap_int_bisect_delete( &ld->ld_abandoned, &ld->ld_nabandoned, + msgid, idx ); + LDAP_MUTEX_UNLOCK( &ld->ld_abandon_mutex ); + return ret; +} diff --git a/libraries/libldap/sasl.c b/libraries/libldap/sasl.c new file mode 100644 index 0000000..ff6988d --- /dev/null +++ b/libraries/libldap/sasl.c @@ -0,0 +1,868 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +/* + * BindRequest ::= SEQUENCE { + * version INTEGER, + * name DistinguishedName, -- who + * authentication CHOICE { + * simple [0] OCTET STRING -- passwd + * krbv42ldap [1] OCTET STRING -- OBSOLETE + * krbv42dsa [2] OCTET STRING -- OBSOLETE + * sasl [3] SaslCredentials -- LDAPv3 + * } + * } + * + * BindResponse ::= SEQUENCE { + * COMPONENTS OF LDAPResult, + * serverSaslCreds OCTET STRING OPTIONAL -- LDAPv3 + * } + * + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/socket.h> +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/time.h> +#include <ac/errno.h> + +#include "ldap-int.h" + +BerElement * +ldap_build_bind_req( + LDAP *ld, + LDAP_CONST char *dn, + LDAP_CONST char *mechanism, + struct berval *cred, + LDAPControl **sctrls, + LDAPControl **cctrls, + ber_int_t *msgidp ) +{ + BerElement *ber; + int rc; + + if( mechanism == LDAP_SASL_SIMPLE ) { + if( dn == NULL && cred != NULL && cred->bv_len ) { + /* use default binddn */ + dn = ld->ld_defbinddn; + } + + } else if( ld->ld_version < LDAP_VERSION3 ) { + ld->ld_errno = LDAP_NOT_SUPPORTED; + return( NULL ); + } + + if ( dn == NULL ) { + dn = ""; + } + + /* create a message to send */ + if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) { + return( NULL ); + } + + LDAP_NEXT_MSGID( ld, *msgidp ); + if( mechanism == LDAP_SASL_SIMPLE ) { + /* simple bind */ + rc = ber_printf( ber, "{it{istON}" /*}*/, + *msgidp, LDAP_REQ_BIND, + ld->ld_version, dn, LDAP_AUTH_SIMPLE, + cred ); + + } else if ( cred == NULL || cred->bv_val == NULL ) { + /* SASL bind w/o credentials */ + rc = ber_printf( ber, "{it{ist{sN}N}" /*}*/, + *msgidp, LDAP_REQ_BIND, + ld->ld_version, dn, LDAP_AUTH_SASL, + mechanism ); + + } else { + /* SASL bind w/ credentials */ + rc = ber_printf( ber, "{it{ist{sON}N}" /*}*/, + *msgidp, LDAP_REQ_BIND, + ld->ld_version, dn, LDAP_AUTH_SASL, + mechanism, cred ); + } + + if( rc == -1 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return( NULL ); + } + + /* Put Server Controls */ + if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) { + ber_free( ber, 1 ); + return( NULL ); + } + + if ( ber_printf( ber, /*{*/ "N}" ) == -1 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return( NULL ); + } + + return( ber ); +} + +/* + * ldap_sasl_bind - bind to the ldap server (and X.500). + * The dn (usually NULL), mechanism, and credentials are provided. + * The message id of the request initiated is provided upon successful + * (LDAP_SUCCESS) return. + * + * Example: + * ldap_sasl_bind( ld, NULL, "mechanism", + * cred, NULL, NULL, &msgid ) + */ + +int +ldap_sasl_bind( + LDAP *ld, + LDAP_CONST char *dn, + LDAP_CONST char *mechanism, + struct berval *cred, + LDAPControl **sctrls, + LDAPControl **cctrls, + int *msgidp ) +{ + BerElement *ber; + int rc; + ber_int_t id; + + Debug( LDAP_DEBUG_TRACE, "ldap_sasl_bind\n", 0, 0, 0 ); + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( msgidp != NULL ); + + /* check client controls */ + rc = ldap_int_client_controls( ld, cctrls ); + if( rc != LDAP_SUCCESS ) return rc; + + ber = ldap_build_bind_req( ld, dn, mechanism, cred, sctrls, cctrls, &id ); + if( !ber ) + return ld->ld_errno; + + /* send the message */ + *msgidp = ldap_send_initial_request( ld, LDAP_REQ_BIND, dn, ber, id ); + + if(*msgidp < 0) + return ld->ld_errno; + + return LDAP_SUCCESS; +} + + +int +ldap_sasl_bind_s( + LDAP *ld, + LDAP_CONST char *dn, + LDAP_CONST char *mechanism, + struct berval *cred, + LDAPControl **sctrls, + LDAPControl **cctrls, + struct berval **servercredp ) +{ + int rc, msgid; + LDAPMessage *result; + struct berval *scredp = NULL; + + Debug( LDAP_DEBUG_TRACE, "ldap_sasl_bind_s\n", 0, 0, 0 ); + + /* do a quick !LDAPv3 check... ldap_sasl_bind will do the rest. */ + if( servercredp != NULL ) { + if (ld->ld_version < LDAP_VERSION3) { + ld->ld_errno = LDAP_NOT_SUPPORTED; + return ld->ld_errno; + } + *servercredp = NULL; + } + + rc = ldap_sasl_bind( ld, dn, mechanism, cred, sctrls, cctrls, &msgid ); + + if ( rc != LDAP_SUCCESS ) { + return( rc ); + } + +#ifdef LDAP_CONNECTIONLESS + if (LDAP_IS_UDP(ld)) { + return( rc ); + } +#endif + + if ( ldap_result( ld, msgid, LDAP_MSG_ALL, NULL, &result ) == -1 || !result ) { + return( ld->ld_errno ); /* ldap_result sets ld_errno */ + } + + /* parse the results */ + scredp = NULL; + if( servercredp != NULL ) { + rc = ldap_parse_sasl_bind_result( ld, result, &scredp, 0 ); + } + + if ( rc != LDAP_SUCCESS ) { + ldap_msgfree( result ); + return( rc ); + } + + rc = ldap_result2error( ld, result, 1 ); + + if ( rc == LDAP_SUCCESS || rc == LDAP_SASL_BIND_IN_PROGRESS ) { + if( servercredp != NULL ) { + *servercredp = scredp; + scredp = NULL; + } + } + + if ( scredp != NULL ) { + ber_bvfree(scredp); + } + + return rc; +} + + +/* +* Parse BindResponse: +* +* BindResponse ::= [APPLICATION 1] SEQUENCE { +* COMPONENTS OF LDAPResult, +* serverSaslCreds [7] OCTET STRING OPTIONAL } +* +* LDAPResult ::= SEQUENCE { +* resultCode ENUMERATED, +* matchedDN LDAPDN, +* errorMessage LDAPString, +* referral [3] Referral OPTIONAL } +*/ + +int +ldap_parse_sasl_bind_result( + LDAP *ld, + LDAPMessage *res, + struct berval **servercredp, + int freeit ) +{ + ber_int_t errcode; + struct berval* scred; + + ber_tag_t tag; + BerElement *ber; + + Debug( LDAP_DEBUG_TRACE, "ldap_parse_sasl_bind_result\n", 0, 0, 0 ); + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( res != NULL ); + + if( servercredp != NULL ) { + if( ld->ld_version < LDAP_VERSION2 ) { + return LDAP_NOT_SUPPORTED; + } + *servercredp = NULL; + } + + if( res->lm_msgtype != LDAP_RES_BIND ) { + ld->ld_errno = LDAP_PARAM_ERROR; + return ld->ld_errno; + } + + scred = NULL; + + if ( ld->ld_error ) { + LDAP_FREE( ld->ld_error ); + ld->ld_error = NULL; + } + if ( ld->ld_matched ) { + LDAP_FREE( ld->ld_matched ); + ld->ld_matched = NULL; + } + + /* parse results */ + + ber = ber_dup( res->lm_ber ); + + if( ber == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + return ld->ld_errno; + } + + if ( ld->ld_version < LDAP_VERSION2 ) { + tag = ber_scanf( ber, "{iA}", + &errcode, &ld->ld_error ); + + if( tag == LBER_ERROR ) { + ber_free( ber, 0 ); + ld->ld_errno = LDAP_DECODING_ERROR; + return ld->ld_errno; + } + + } else { + ber_len_t len; + + tag = ber_scanf( ber, "{eAA" /*}*/, + &errcode, &ld->ld_matched, &ld->ld_error ); + + if( tag == LBER_ERROR ) { + ber_free( ber, 0 ); + ld->ld_errno = LDAP_DECODING_ERROR; + return ld->ld_errno; + } + + tag = ber_peek_tag(ber, &len); + + if( tag == LDAP_TAG_REFERRAL ) { + /* skip 'em */ + if( ber_scanf( ber, "x" ) == LBER_ERROR ) { + ber_free( ber, 0 ); + ld->ld_errno = LDAP_DECODING_ERROR; + return ld->ld_errno; + } + + tag = ber_peek_tag(ber, &len); + } + + if( tag == LDAP_TAG_SASL_RES_CREDS ) { + if( ber_scanf( ber, "O", &scred ) == LBER_ERROR ) { + ber_free( ber, 0 ); + ld->ld_errno = LDAP_DECODING_ERROR; + return ld->ld_errno; + } + } + } + + ber_free( ber, 0 ); + + if ( servercredp != NULL ) { + *servercredp = scred; + + } else if ( scred != NULL ) { + ber_bvfree( scred ); + } + + ld->ld_errno = errcode; + + if ( freeit ) { + ldap_msgfree( res ); + } + + return( LDAP_SUCCESS ); +} + +int +ldap_pvt_sasl_getmechs ( LDAP *ld, char **pmechlist ) +{ + /* we need to query the server for supported mechs anyway */ + LDAPMessage *res, *e; + char *attrs[] = { "supportedSASLMechanisms", NULL }; + char **values, *mechlist; + int rc; + + Debug( LDAP_DEBUG_TRACE, "ldap_pvt_sasl_getmech\n", 0, 0, 0 ); + + rc = ldap_search_s( ld, "", LDAP_SCOPE_BASE, + NULL, attrs, 0, &res ); + + if ( rc != LDAP_SUCCESS ) { + return ld->ld_errno; + } + + e = ldap_first_entry( ld, res ); + if ( e == NULL ) { + ldap_msgfree( res ); + if ( ld->ld_errno == LDAP_SUCCESS ) { + ld->ld_errno = LDAP_NO_SUCH_OBJECT; + } + return ld->ld_errno; + } + + values = ldap_get_values( ld, e, "supportedSASLMechanisms" ); + if ( values == NULL ) { + ldap_msgfree( res ); + ld->ld_errno = LDAP_NO_SUCH_ATTRIBUTE; + return ld->ld_errno; + } + + mechlist = ldap_charray2str( values, " " ); + if ( mechlist == NULL ) { + LDAP_VFREE( values ); + ldap_msgfree( res ); + ld->ld_errno = LDAP_NO_MEMORY; + return ld->ld_errno; + } + + LDAP_VFREE( values ); + ldap_msgfree( res ); + + *pmechlist = mechlist; + + return LDAP_SUCCESS; +} + +/* + * ldap_sasl_interactive_bind - interactive SASL authentication + * + * This routine uses interactive callbacks. + * + * LDAP_SUCCESS is returned upon success, the ldap error code + * otherwise. LDAP_SASL_BIND_IN_PROGRESS is returned if further + * calls are needed. + */ +int +ldap_sasl_interactive_bind( + LDAP *ld, + LDAP_CONST char *dn, /* usually NULL */ + LDAP_CONST char *mechs, + LDAPControl **serverControls, + LDAPControl **clientControls, + unsigned flags, + LDAP_SASL_INTERACT_PROC *interact, + void *defaults, + LDAPMessage *result, + const char **rmech, + int *msgid ) +{ + char *smechs = NULL; + int rc; + +#ifdef LDAP_CONNECTIONLESS + if( LDAP_IS_UDP(ld) ) { + /* Just force it to simple bind, silly to make the user + * ask all the time. No, we don't ever actually bind, but I'll + * let the final bind handler take care of saving the cdn. + */ + rc = ldap_simple_bind( ld, dn, NULL ); + rc = rc < 0 ? rc : 0; + goto done; + } else +#endif + + /* First time */ + if ( !result ) { + +#ifdef HAVE_CYRUS_SASL + if( mechs == NULL || *mechs == '\0' ) { + mechs = ld->ld_options.ldo_def_sasl_mech; + } +#endif + + if( mechs == NULL || *mechs == '\0' ) { + /* FIXME: this needs to be asynchronous too; + * perhaps NULL should be disallowed for async usage? + */ + rc = ldap_pvt_sasl_getmechs( ld, &smechs ); + if( rc != LDAP_SUCCESS ) { + goto done; + } + + Debug( LDAP_DEBUG_TRACE, + "ldap_sasl_interactive_bind: server supports: %s\n", + smechs, 0, 0 ); + + mechs = smechs; + + } else { + Debug( LDAP_DEBUG_TRACE, + "ldap_sasl_interactive_bind: user selected: %s\n", + mechs, 0, 0 ); + } + } + rc = ldap_int_sasl_bind( ld, dn, mechs, + serverControls, clientControls, + flags, interact, defaults, result, rmech, msgid ); + +done: + if ( smechs ) LDAP_FREE( smechs ); + + return rc; +} + +/* + * ldap_sasl_interactive_bind_s - interactive SASL authentication + * + * This routine uses interactive callbacks. + * + * LDAP_SUCCESS is returned upon success, the ldap error code + * otherwise. + */ +int +ldap_sasl_interactive_bind_s( + LDAP *ld, + LDAP_CONST char *dn, /* usually NULL */ + LDAP_CONST char *mechs, + LDAPControl **serverControls, + LDAPControl **clientControls, + unsigned flags, + LDAP_SASL_INTERACT_PROC *interact, + void *defaults ) +{ + const char *rmech = NULL; + LDAPMessage *result = NULL; + int rc, msgid; + + do { + rc = ldap_sasl_interactive_bind( ld, dn, mechs, + serverControls, clientControls, + flags, interact, defaults, result, &rmech, &msgid ); + + ldap_msgfree( result ); + + if ( rc != LDAP_SASL_BIND_IN_PROGRESS ) + break; + +#ifdef LDAP_CONNECTIONLESS + if (LDAP_IS_UDP(ld)) { + break; + } +#endif + + if ( ldap_result( ld, msgid, LDAP_MSG_ALL, NULL, &result ) == -1 || !result ) { + return( ld->ld_errno ); /* ldap_result sets ld_errno */ + } + } while ( rc == LDAP_SASL_BIND_IN_PROGRESS ); + + return rc; +} + +#ifdef HAVE_CYRUS_SASL + +#ifdef HAVE_SASL_SASL_H +#include <sasl/sasl.h> +#else +#include <sasl.h> +#endif + +#endif /* HAVE_CYRUS_SASL */ + +static int +sb_sasl_generic_remove( Sockbuf_IO_Desc *sbiod ); + +static int +sb_sasl_generic_setup( Sockbuf_IO_Desc *sbiod, void *arg ) +{ + struct sb_sasl_generic_data *p; + struct sb_sasl_generic_install *i; + + assert( sbiod != NULL ); + + i = (struct sb_sasl_generic_install *)arg; + + p = LBER_MALLOC( sizeof( *p ) ); + if ( p == NULL ) + return -1; + p->ops = i->ops; + p->ops_private = i->ops_private; + p->sbiod = sbiod; + p->flags = 0; + ber_pvt_sb_buf_init( &p->sec_buf_in ); + ber_pvt_sb_buf_init( &p->buf_in ); + ber_pvt_sb_buf_init( &p->buf_out ); + + sbiod->sbiod_pvt = p; + + p->ops->init( p, &p->min_send, &p->max_send, &p->max_recv ); + + if ( ber_pvt_sb_grow_buffer( &p->sec_buf_in, p->min_send ) < 0 ) { + sb_sasl_generic_remove( sbiod ); + sock_errset(ENOMEM); + return -1; + } + + return 0; +} + +static int +sb_sasl_generic_remove( Sockbuf_IO_Desc *sbiod ) +{ + struct sb_sasl_generic_data *p; + + assert( sbiod != NULL ); + + p = (struct sb_sasl_generic_data *)sbiod->sbiod_pvt; + + p->ops->fini(p); + + ber_pvt_sb_buf_destroy( &p->sec_buf_in ); + ber_pvt_sb_buf_destroy( &p->buf_in ); + ber_pvt_sb_buf_destroy( &p->buf_out ); + LBER_FREE( p ); + sbiod->sbiod_pvt = NULL; + return 0; +} + +static ber_len_t +sb_sasl_generic_pkt_length( + struct sb_sasl_generic_data *p, + const unsigned char *buf, + int debuglevel ) +{ + ber_len_t size; + + assert( buf != NULL ); + + size = buf[0] << 24 + | buf[1] << 16 + | buf[2] << 8 + | buf[3]; + + if ( size > p->max_recv ) { + /* somebody is trying to mess me up. */ + ber_log_printf( LDAP_DEBUG_ANY, debuglevel, + "sb_sasl_generic_pkt_length: " + "received illegal packet length of %lu bytes\n", + (unsigned long)size ); + size = 16; /* this should lead to an error. */ + } + + return size + 4; /* include the size !!! */ +} + +/* Drop a processed packet from the input buffer */ +static void +sb_sasl_generic_drop_packet ( + struct sb_sasl_generic_data *p, + int debuglevel ) +{ + ber_slen_t len; + + len = p->sec_buf_in.buf_ptr - p->sec_buf_in.buf_end; + if ( len > 0 ) + AC_MEMCPY( p->sec_buf_in.buf_base, p->sec_buf_in.buf_base + + p->sec_buf_in.buf_end, len ); + + if ( len >= 4 ) { + p->sec_buf_in.buf_end = sb_sasl_generic_pkt_length(p, + (unsigned char *) p->sec_buf_in.buf_base, debuglevel); + } + else { + p->sec_buf_in.buf_end = 0; + } + p->sec_buf_in.buf_ptr = len; +} + +static ber_slen_t +sb_sasl_generic_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len) +{ + struct sb_sasl_generic_data *p; + ber_slen_t ret, bufptr; + + assert( sbiod != NULL ); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + + p = (struct sb_sasl_generic_data *)sbiod->sbiod_pvt; + + /* Are there anything left in the buffer? */ + ret = ber_pvt_sb_copy_out( &p->buf_in, buf, len ); + bufptr = ret; + len -= ret; + + if ( len == 0 ) + return bufptr; + + p->ops->reset_buf( p, &p->buf_in ); + + /* Read the length of the packet */ + while ( p->sec_buf_in.buf_ptr < 4 ) { + ret = LBER_SBIOD_READ_NEXT( sbiod, p->sec_buf_in.buf_base + + p->sec_buf_in.buf_ptr, + 4 - p->sec_buf_in.buf_ptr ); +#ifdef EINTR + if ( ( ret < 0 ) && ( errno == EINTR ) ) + continue; +#endif + if ( ret <= 0 ) + return bufptr ? bufptr : ret; + + p->sec_buf_in.buf_ptr += ret; + } + + /* The new packet always starts at p->sec_buf_in.buf_base */ + ret = sb_sasl_generic_pkt_length(p, (unsigned char *) p->sec_buf_in.buf_base, + sbiod->sbiod_sb->sb_debug ); + + /* Grow the packet buffer if neccessary */ + if ( ( p->sec_buf_in.buf_size < (ber_len_t) ret ) && + ber_pvt_sb_grow_buffer( &p->sec_buf_in, ret ) < 0 ) + { + sock_errset(ENOMEM); + return -1; + } + p->sec_buf_in.buf_end = ret; + + /* Did we read the whole encrypted packet? */ + while ( p->sec_buf_in.buf_ptr < p->sec_buf_in.buf_end ) { + /* No, we have got only a part of it */ + ret = p->sec_buf_in.buf_end - p->sec_buf_in.buf_ptr; + + ret = LBER_SBIOD_READ_NEXT( sbiod, p->sec_buf_in.buf_base + + p->sec_buf_in.buf_ptr, ret ); +#ifdef EINTR + if ( ( ret < 0 ) && ( errno == EINTR ) ) + continue; +#endif + if ( ret <= 0 ) + return bufptr ? bufptr : ret; + + p->sec_buf_in.buf_ptr += ret; + } + + /* Decode the packet */ + ret = p->ops->decode( p, &p->sec_buf_in, &p->buf_in ); + + /* Drop the packet from the input buffer */ + sb_sasl_generic_drop_packet( p, sbiod->sbiod_sb->sb_debug ); + + if ( ret != 0 ) { + ber_log_printf( LDAP_DEBUG_ANY, sbiod->sbiod_sb->sb_debug, + "sb_sasl_generic_read: failed to decode packet\n" ); + sock_errset(EIO); + return -1; + } + + bufptr += ber_pvt_sb_copy_out( &p->buf_in, (char*) buf + bufptr, len ); + + return bufptr; +} + +static ber_slen_t +sb_sasl_generic_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len) +{ + struct sb_sasl_generic_data *p; + int ret; + ber_len_t len2; + + assert( sbiod != NULL ); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + + p = (struct sb_sasl_generic_data *)sbiod->sbiod_pvt; + + /* Is there anything left in the buffer? */ + if ( p->buf_out.buf_ptr != p->buf_out.buf_end ) { + ret = ber_pvt_sb_do_write( sbiod, &p->buf_out ); + if ( ret < 0 ) return ret; + + /* Still have something left?? */ + if ( p->buf_out.buf_ptr != p->buf_out.buf_end ) { + sock_errset(EAGAIN); + return -1; + } + } + + len2 = p->max_send - 100; /* For safety margin */ + len2 = len > len2 ? len2 : len; + + /* If we're just retrying a partial write, tell the + * caller it's done. Let them call again if there's + * still more left to write. + */ + if ( p->flags & LDAP_PVT_SASL_PARTIAL_WRITE ) { + p->flags ^= LDAP_PVT_SASL_PARTIAL_WRITE; + return len2; + } + + /* now encode the next packet. */ + p->ops->reset_buf( p, &p->buf_out ); + + ret = p->ops->encode( p, buf, len2, &p->buf_out ); + + if ( ret != 0 ) { + ber_log_printf( LDAP_DEBUG_ANY, sbiod->sbiod_sb->sb_debug, + "sb_sasl_generic_write: failed to encode packet\n" ); + sock_errset(EIO); + return -1; + } + + ret = ber_pvt_sb_do_write( sbiod, &p->buf_out ); + + if ( ret < 0 ) { + /* error? */ + int err = sock_errno(); + /* caller can retry this */ + if ( err == EAGAIN || err == EWOULDBLOCK || err == EINTR ) + p->flags |= LDAP_PVT_SASL_PARTIAL_WRITE; + return ret; + } else if ( p->buf_out.buf_ptr != p->buf_out.buf_end ) { + /* partial write? pretend nothing got written */ + p->flags |= LDAP_PVT_SASL_PARTIAL_WRITE; + sock_errset(EAGAIN); + len2 = -1; + } + + /* return number of bytes encoded, not written, to ensure + * no byte is encoded twice (even if only sent once). + */ + return len2; +} + +static int +sb_sasl_generic_ctrl( Sockbuf_IO_Desc *sbiod, int opt, void *arg ) +{ + struct sb_sasl_generic_data *p; + + p = (struct sb_sasl_generic_data *)sbiod->sbiod_pvt; + + if ( opt == LBER_SB_OPT_DATA_READY ) { + if ( p->buf_in.buf_ptr != p->buf_in.buf_end ) return 1; + } + + return LBER_SBIOD_CTRL_NEXT( sbiod, opt, arg ); +} + +Sockbuf_IO ldap_pvt_sockbuf_io_sasl_generic = { + sb_sasl_generic_setup, /* sbi_setup */ + sb_sasl_generic_remove, /* sbi_remove */ + sb_sasl_generic_ctrl, /* sbi_ctrl */ + sb_sasl_generic_read, /* sbi_read */ + sb_sasl_generic_write, /* sbi_write */ + NULL /* sbi_close */ +}; + +int ldap_pvt_sasl_generic_install( + Sockbuf *sb, + struct sb_sasl_generic_install *install_arg ) +{ + Debug( LDAP_DEBUG_TRACE, "ldap_pvt_sasl_generic_install\n", + 0, 0, 0 ); + + /* don't install the stuff unless security has been negotiated */ + + if ( !ber_sockbuf_ctrl( sb, LBER_SB_OPT_HAS_IO, + &ldap_pvt_sockbuf_io_sasl_generic ) ) + { +#ifdef LDAP_DEBUG + ber_sockbuf_add_io( sb, &ber_sockbuf_io_debug, + LBER_SBIOD_LEVEL_APPLICATION, (void *)"sasl_generic_" ); +#endif + ber_sockbuf_add_io( sb, &ldap_pvt_sockbuf_io_sasl_generic, + LBER_SBIOD_LEVEL_APPLICATION, install_arg ); + } + + return LDAP_SUCCESS; +} + +void ldap_pvt_sasl_generic_remove( Sockbuf *sb ) +{ + ber_sockbuf_remove_io( sb, &ldap_pvt_sockbuf_io_sasl_generic, + LBER_SBIOD_LEVEL_APPLICATION ); +#ifdef LDAP_DEBUG + ber_sockbuf_remove_io( sb, &ber_sockbuf_io_debug, + LBER_SBIOD_LEVEL_APPLICATION ); +#endif +} diff --git a/libraries/libldap/sbind.c b/libraries/libldap/sbind.c new file mode 100644 index 0000000..2b0d087 --- /dev/null +++ b/libraries/libldap/sbind.c @@ -0,0 +1,115 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1993 Regents of the University of Michigan. + * All rights reserved. + */ + +/* + * BindRequest ::= SEQUENCE { + * version INTEGER, + * name DistinguishedName, -- who + * authentication CHOICE { + * simple [0] OCTET STRING -- passwd + * krbv42ldap [1] OCTET STRING -- OBSOLETE + * krbv42dsa [2] OCTET STRING -- OBSOLETE + * sasl [3] SaslCredentials -- LDAPv3 + * } + * } + * + * BindResponse ::= SEQUENCE { + * COMPONENTS OF LDAPResult, + * serverSaslCreds OCTET STRING OPTIONAL -- LDAPv3 + * } + * + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +/* + * ldap_simple_bind - bind to the ldap server (and X.500). The dn and + * password of the entry to which to bind are supplied. The message id + * of the request initiated is returned. + * + * Example: + * ldap_simple_bind( ld, "cn=manager, o=university of michigan, c=us", + * "secret" ) + */ + +int +ldap_simple_bind( + LDAP *ld, + LDAP_CONST char *dn, + LDAP_CONST char *passwd ) +{ + int rc; + int msgid; + struct berval cred; + + Debug( LDAP_DEBUG_TRACE, "ldap_simple_bind\n", 0, 0, 0 ); + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + + if ( passwd != NULL ) { + cred.bv_val = (char *) passwd; + cred.bv_len = strlen( passwd ); + } else { + cred.bv_val = ""; + cred.bv_len = 0; + } + + rc = ldap_sasl_bind( ld, dn, LDAP_SASL_SIMPLE, &cred, + NULL, NULL, &msgid ); + + return rc == LDAP_SUCCESS ? msgid : -1; +} + +/* + * ldap_simple_bind - bind to the ldap server (and X.500) using simple + * authentication. The dn and password of the entry to which to bind are + * supplied. LDAP_SUCCESS is returned upon success, the ldap error code + * otherwise. + * + * Example: + * ldap_simple_bind_s( ld, "cn=manager, o=university of michigan, c=us", + * "secret" ) + */ + +int +ldap_simple_bind_s( LDAP *ld, LDAP_CONST char *dn, LDAP_CONST char *passwd ) +{ + struct berval cred; + + Debug( LDAP_DEBUG_TRACE, "ldap_simple_bind_s\n", 0, 0, 0 ); + + if ( passwd != NULL ) { + cred.bv_val = (char *) passwd; + cred.bv_len = strlen( passwd ); + } else { + cred.bv_val = ""; + cred.bv_len = 0; + } + + return ldap_sasl_bind_s( ld, dn, LDAP_SASL_SIMPLE, &cred, + NULL, NULL, NULL ); +} diff --git a/libraries/libldap/schema.c b/libraries/libldap/schema.c new file mode 100644 index 0000000..24c6efe --- /dev/null +++ b/libraries/libldap/schema.c @@ -0,0 +1,3385 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +/* + * schema.c: parsing routines used by servers and clients to process + * schema definitions + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/stdlib.h> + +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +#include <ldap_schema.h> + +static const char EndOfInput[] = "end of input"; + +static const char * +choose_name( char *names[], const char *fallback ) +{ + return (names != NULL && names[0] != NULL) ? names[0] : fallback; +} + +LDAP_CONST char * +ldap_syntax2name( LDAPSyntax * syn ) +{ + if (!syn) return NULL; + return( syn->syn_oid ); +} + +LDAP_CONST char * +ldap_matchingrule2name( LDAPMatchingRule * mr ) +{ + if (!mr) return NULL; + return( choose_name( mr->mr_names, mr->mr_oid ) ); +} + +LDAP_CONST char * +ldap_matchingruleuse2name( LDAPMatchingRuleUse * mru ) +{ + if (!mru) return NULL; + return( choose_name( mru->mru_names, mru->mru_oid ) ); +} + +LDAP_CONST char * +ldap_attributetype2name( LDAPAttributeType * at ) +{ + if (!at) return NULL; + return( choose_name( at->at_names, at->at_oid ) ); +} + +LDAP_CONST char * +ldap_objectclass2name( LDAPObjectClass * oc ) +{ + if (!oc) return NULL; + return( choose_name( oc->oc_names, oc->oc_oid ) ); +} + +LDAP_CONST char * +ldap_contentrule2name( LDAPContentRule * cr ) +{ + if (!cr) return NULL; + return( choose_name( cr->cr_names, cr->cr_oid ) ); +} + +LDAP_CONST char * +ldap_nameform2name( LDAPNameForm * nf ) +{ + if (!nf) return NULL; + return( choose_name( nf->nf_names, nf->nf_oid ) ); +} + +LDAP_CONST char * +ldap_structurerule2name( LDAPStructureRule * sr ) +{ + if (!sr) return NULL; + return( choose_name( sr->sr_names, NULL ) ); +} + +/* + * When pretty printing the entities we will be appending to a buffer. + * Since checking for overflow, realloc'ing and checking if no error + * is extremely boring, we will use a protection layer that will let + * us blissfully ignore the error until the end. This layer is + * implemented with the help of the next type. + */ + +typedef struct safe_string { + char * val; + ber_len_t size; + ber_len_t pos; + int at_whsp; +} safe_string; + +static safe_string * +new_safe_string(int size) +{ + safe_string * ss; + + ss = LDAP_MALLOC(sizeof(safe_string)); + if ( !ss ) + return(NULL); + + ss->val = LDAP_MALLOC(size); + if ( !ss->val ) { + LDAP_FREE(ss); + return(NULL); + } + + ss->size = size; + ss->pos = 0; + ss->at_whsp = 0; + + return ss; +} + +static void +safe_string_free(safe_string * ss) +{ + if ( !ss ) + return; + LDAP_FREE(ss->val); + LDAP_FREE(ss); +} + +#if 0 /* unused */ +static char * +safe_string_val(safe_string * ss) +{ + ss->val[ss->pos] = '\0'; + return(ss->val); +} +#endif + +static char * +safe_strdup(safe_string * ss) +{ + char *ret = LDAP_MALLOC(ss->pos+1); + if (!ret) + return NULL; + AC_MEMCPY(ret, ss->val, ss->pos); + ret[ss->pos] = '\0'; + return ret; +} + +static int +append_to_safe_string(safe_string * ss, char * s) +{ + int l = strlen(s); + char * temp; + + /* + * Some runaway process is trying to append to a string that + * overflowed and we could not extend. + */ + if ( !ss->val ) + return -1; + + /* We always make sure there is at least one position available */ + if ( ss->pos + l >= ss->size-1 ) { + ss->size *= 2; + if ( ss->pos + l >= ss->size-1 ) { + ss->size = ss->pos + l + 1; + } + + temp = LDAP_REALLOC(ss->val, ss->size); + if ( !temp ) { + /* Trouble, out of memory */ + LDAP_FREE(ss->val); + return -1; + } + ss->val = temp; + } + strncpy(&ss->val[ss->pos], s, l); + ss->pos += l; + if ( ss->pos > 0 && LDAP_SPACE(ss->val[ss->pos-1]) ) + ss->at_whsp = 1; + else + ss->at_whsp = 0; + + return 0; +} + +static int +print_literal(safe_string *ss, char *s) +{ + return(append_to_safe_string(ss,s)); +} + +static int +print_whsp(safe_string *ss) +{ + if ( ss->at_whsp ) + return(append_to_safe_string(ss,"")); + else + return(append_to_safe_string(ss," ")); +} + +static int +print_numericoid(safe_string *ss, char *s) +{ + if ( s ) + return(append_to_safe_string(ss,s)); + else + return(append_to_safe_string(ss,"")); +} + +/* This one is identical to print_qdescr */ +static int +print_qdstring(safe_string *ss, char *s) +{ + print_whsp(ss); + print_literal(ss,"'"); + append_to_safe_string(ss,s); + print_literal(ss,"'"); + return(print_whsp(ss)); +} + +static int +print_qdescr(safe_string *ss, char *s) +{ + print_whsp(ss); + print_literal(ss,"'"); + append_to_safe_string(ss,s); + print_literal(ss,"'"); + return(print_whsp(ss)); +} + +static int +print_qdescrlist(safe_string *ss, char **sa) +{ + char **sp; + int ret = 0; + + for (sp=sa; *sp; sp++) { + ret = print_qdescr(ss,*sp); + } + /* If the list was empty, we return zero that is potentially + * incorrect, but since we will be still appending things, the + * overflow will be detected later. Maybe FIX. + */ + return(ret); +} + +static int +print_qdescrs(safe_string *ss, char **sa) +{ + /* The only way to represent an empty list is as a qdescrlist + * so, if the list is empty we treat it as a long list. + * Really, this is what the syntax mandates. We should not + * be here if the list was empty, but if it happens, a label + * has already been output and we cannot undo it. + */ + if ( !sa[0] || ( sa[0] && sa[1] ) ) { + print_whsp(ss); + print_literal(ss,"("/*)*/); + print_qdescrlist(ss,sa); + print_literal(ss,/*(*/")"); + return(print_whsp(ss)); + } else { + return(print_qdescr(ss,*sa)); + } +} + +static int +print_woid(safe_string *ss, char *s) +{ + print_whsp(ss); + append_to_safe_string(ss,s); + return print_whsp(ss); +} + +static int +print_oidlist(safe_string *ss, char **sa) +{ + char **sp; + + for (sp=sa; *(sp+1); sp++) { + print_woid(ss,*sp); + print_literal(ss,"$"); + } + return(print_woid(ss,*sp)); +} + +static int +print_oids(safe_string *ss, char **sa) +{ + if ( sa[0] && sa[1] ) { + print_literal(ss,"("/*)*/); + print_oidlist(ss,sa); + print_whsp(ss); + return(print_literal(ss,/*(*/")")); + } else { + return(print_woid(ss,*sa)); + } +} + +static int +print_noidlen(safe_string *ss, char *s, int l) +{ + char buf[64]; + int ret; + + ret = print_numericoid(ss,s); + if ( l ) { + snprintf(buf, sizeof buf, "{%d}",l); + ret = print_literal(ss,buf); + } + return(ret); +} + +static int +print_ruleid(safe_string *ss, int rid) +{ + char buf[64]; + snprintf(buf, sizeof buf, "%d", rid); + return print_literal(ss,buf); +} + +static int +print_ruleids(safe_string *ss, int n, int *rids) +{ + int i; + + if( n == 1 ) { + print_ruleid(ss,rids[0]); + return print_whsp(ss); + } else { + print_literal(ss,"("/*)*/); + for( i=0; i<n; i++ ) { + print_whsp(ss); + print_ruleid(ss,rids[i]); + } + print_whsp(ss); + return print_literal(ss,/*(*/")"); + } +} + + +static int +print_extensions(safe_string *ss, LDAPSchemaExtensionItem **extensions) +{ + LDAPSchemaExtensionItem **ext; + + if ( extensions ) { + print_whsp(ss); + for ( ext = extensions; *ext != NULL; ext++ ) { + print_literal(ss, (*ext)->lsei_name); + print_whsp(ss); + /* Should be print_qdstrings */ + print_qdescrs(ss, (*ext)->lsei_values); + print_whsp(ss); + } + } + + return 0; +} + +char * +ldap_syntax2str( LDAPSyntax * syn ) +{ + struct berval bv; + if (ldap_syntax2bv( syn, &bv )) + return(bv.bv_val); + else + return NULL; +} + +struct berval * +ldap_syntax2bv( LDAPSyntax * syn, struct berval *bv ) +{ + safe_string * ss; + + if ( !syn || !bv ) + return NULL; + + ss = new_safe_string(256); + if ( !ss ) + return NULL; + + print_literal(ss,"("/*)*/); + print_whsp(ss); + + print_numericoid(ss, syn->syn_oid); + print_whsp(ss); + + if ( syn->syn_desc ) { + print_literal(ss,"DESC"); + print_qdstring(ss,syn->syn_desc); + } + + print_whsp(ss); + + print_extensions(ss, syn->syn_extensions); + + print_literal(ss,/*(*/ ")"); + + bv->bv_val = safe_strdup(ss); + bv->bv_len = ss->pos; + safe_string_free(ss); + return(bv); +} + +char * +ldap_matchingrule2str( LDAPMatchingRule * mr ) +{ + struct berval bv; + if (ldap_matchingrule2bv( mr, &bv )) + return(bv.bv_val); + else + return NULL; +} + +struct berval * +ldap_matchingrule2bv( LDAPMatchingRule * mr, struct berval *bv ) +{ + safe_string * ss; + + if ( !mr || !bv ) + return NULL; + + ss = new_safe_string(256); + if ( !ss ) + return NULL; + + print_literal(ss,"(" /*)*/); + print_whsp(ss); + + print_numericoid(ss, mr->mr_oid); + print_whsp(ss); + + if ( mr->mr_names ) { + print_literal(ss,"NAME"); + print_qdescrs(ss,mr->mr_names); + } + + if ( mr->mr_desc ) { + print_literal(ss,"DESC"); + print_qdstring(ss,mr->mr_desc); + } + + if ( mr->mr_obsolete ) { + print_literal(ss, "OBSOLETE"); + print_whsp(ss); + } + + if ( mr->mr_syntax_oid ) { + print_literal(ss,"SYNTAX"); + print_whsp(ss); + print_literal(ss, mr->mr_syntax_oid); + print_whsp(ss); + } + + print_whsp(ss); + + print_extensions(ss, mr->mr_extensions); + + print_literal(ss,/*(*/")"); + + bv->bv_val = safe_strdup(ss); + bv->bv_len = ss->pos; + safe_string_free(ss); + return(bv); +} + +char * +ldap_matchingruleuse2str( LDAPMatchingRuleUse * mru ) +{ + struct berval bv; + if (ldap_matchingruleuse2bv( mru, &bv )) + return(bv.bv_val); + else + return NULL; +} + +struct berval * +ldap_matchingruleuse2bv( LDAPMatchingRuleUse * mru, struct berval *bv ) +{ + safe_string * ss; + + if ( !mru || !bv ) + return NULL; + + ss = new_safe_string(256); + if ( !ss ) + return NULL; + + print_literal(ss,"(" /*)*/); + print_whsp(ss); + + print_numericoid(ss, mru->mru_oid); + print_whsp(ss); + + if ( mru->mru_names ) { + print_literal(ss,"NAME"); + print_qdescrs(ss,mru->mru_names); + } + + if ( mru->mru_desc ) { + print_literal(ss,"DESC"); + print_qdstring(ss,mru->mru_desc); + } + + if ( mru->mru_obsolete ) { + print_literal(ss, "OBSOLETE"); + print_whsp(ss); + } + + if ( mru->mru_applies_oids ) { + print_literal(ss,"APPLIES"); + print_whsp(ss); + print_oids(ss, mru->mru_applies_oids); + print_whsp(ss); + } + + print_whsp(ss); + + print_extensions(ss, mru->mru_extensions); + + print_literal(ss,/*(*/")"); + + bv->bv_val = safe_strdup(ss); + bv->bv_len = ss->pos; + safe_string_free(ss); + return(bv); +} + +char * +ldap_objectclass2str( LDAPObjectClass * oc ) +{ + struct berval bv; + if (ldap_objectclass2bv( oc, &bv )) + return(bv.bv_val); + else + return NULL; +} + +struct berval * +ldap_objectclass2bv( LDAPObjectClass * oc, struct berval *bv ) +{ + safe_string * ss; + + if ( !oc || !bv ) + return NULL; + + ss = new_safe_string(256); + if ( !ss ) + return NULL; + + print_literal(ss,"("/*)*/); + print_whsp(ss); + + print_numericoid(ss, oc->oc_oid); + print_whsp(ss); + + if ( oc->oc_names ) { + print_literal(ss,"NAME"); + print_qdescrs(ss,oc->oc_names); + } + + if ( oc->oc_desc ) { + print_literal(ss,"DESC"); + print_qdstring(ss,oc->oc_desc); + } + + if ( oc->oc_obsolete ) { + print_literal(ss, "OBSOLETE"); + print_whsp(ss); + } + + if ( oc->oc_sup_oids ) { + print_literal(ss,"SUP"); + print_whsp(ss); + print_oids(ss,oc->oc_sup_oids); + print_whsp(ss); + } + + switch (oc->oc_kind) { + case LDAP_SCHEMA_ABSTRACT: + print_literal(ss,"ABSTRACT"); + break; + case LDAP_SCHEMA_STRUCTURAL: + print_literal(ss,"STRUCTURAL"); + break; + case LDAP_SCHEMA_AUXILIARY: + print_literal(ss,"AUXILIARY"); + break; + default: + print_literal(ss,"KIND-UNKNOWN"); + break; + } + print_whsp(ss); + + if ( oc->oc_at_oids_must ) { + print_literal(ss,"MUST"); + print_whsp(ss); + print_oids(ss,oc->oc_at_oids_must); + print_whsp(ss); + } + + if ( oc->oc_at_oids_may ) { + print_literal(ss,"MAY"); + print_whsp(ss); + print_oids(ss,oc->oc_at_oids_may); + print_whsp(ss); + } + + print_whsp(ss); + + print_extensions(ss, oc->oc_extensions); + + print_literal(ss, /*(*/")"); + + bv->bv_val = safe_strdup(ss); + bv->bv_len = ss->pos; + safe_string_free(ss); + return(bv); +} + +char * +ldap_contentrule2str( LDAPContentRule * cr ) +{ + struct berval bv; + if (ldap_contentrule2bv( cr, &bv )) + return(bv.bv_val); + else + return NULL; +} + +struct berval * +ldap_contentrule2bv( LDAPContentRule * cr, struct berval *bv ) +{ + safe_string * ss; + + if ( !cr || !bv ) + return NULL; + + ss = new_safe_string(256); + if ( !ss ) + return NULL; + + print_literal(ss,"("/*)*/); + print_whsp(ss); + + print_numericoid(ss, cr->cr_oid); + print_whsp(ss); + + if ( cr->cr_names ) { + print_literal(ss,"NAME"); + print_qdescrs(ss,cr->cr_names); + } + + if ( cr->cr_desc ) { + print_literal(ss,"DESC"); + print_qdstring(ss,cr->cr_desc); + } + + if ( cr->cr_obsolete ) { + print_literal(ss, "OBSOLETE"); + print_whsp(ss); + } + + if ( cr->cr_oc_oids_aux ) { + print_literal(ss,"AUX"); + print_whsp(ss); + print_oids(ss,cr->cr_oc_oids_aux); + print_whsp(ss); + } + + if ( cr->cr_at_oids_must ) { + print_literal(ss,"MUST"); + print_whsp(ss); + print_oids(ss,cr->cr_at_oids_must); + print_whsp(ss); + } + + if ( cr->cr_at_oids_may ) { + print_literal(ss,"MAY"); + print_whsp(ss); + print_oids(ss,cr->cr_at_oids_may); + print_whsp(ss); + } + + if ( cr->cr_at_oids_not ) { + print_literal(ss,"NOT"); + print_whsp(ss); + print_oids(ss,cr->cr_at_oids_not); + print_whsp(ss); + } + + print_whsp(ss); + print_extensions(ss, cr->cr_extensions); + + print_literal(ss, /*(*/")"); + + bv->bv_val = safe_strdup(ss); + bv->bv_len = ss->pos; + safe_string_free(ss); + return(bv); +} + +char * +ldap_structurerule2str( LDAPStructureRule * sr ) +{ + struct berval bv; + if (ldap_structurerule2bv( sr, &bv )) + return(bv.bv_val); + else + return NULL; +} + +struct berval * +ldap_structurerule2bv( LDAPStructureRule * sr, struct berval *bv ) +{ + safe_string * ss; + + if ( !sr || !bv ) + return NULL; + + ss = new_safe_string(256); + if ( !ss ) + return NULL; + + print_literal(ss,"("/*)*/); + print_whsp(ss); + + print_ruleid(ss, sr->sr_ruleid); + print_whsp(ss); + + if ( sr->sr_names ) { + print_literal(ss,"NAME"); + print_qdescrs(ss,sr->sr_names); + } + + if ( sr->sr_desc ) { + print_literal(ss,"DESC"); + print_qdstring(ss,sr->sr_desc); + } + + if ( sr->sr_obsolete ) { + print_literal(ss, "OBSOLETE"); + print_whsp(ss); + } + + print_literal(ss,"FORM"); + print_whsp(ss); + print_woid(ss,sr->sr_nameform); + print_whsp(ss); + + if ( sr->sr_nsup_ruleids ) { + print_literal(ss,"SUP"); + print_whsp(ss); + print_ruleids(ss,sr->sr_nsup_ruleids,sr->sr_sup_ruleids); + print_whsp(ss); + } + + print_whsp(ss); + print_extensions(ss, sr->sr_extensions); + + print_literal(ss, /*(*/")"); + + bv->bv_val = safe_strdup(ss); + bv->bv_len = ss->pos; + safe_string_free(ss); + return(bv); +} + + +char * +ldap_nameform2str( LDAPNameForm * nf ) +{ + struct berval bv; + if (ldap_nameform2bv( nf, &bv )) + return(bv.bv_val); + else + return NULL; +} + +struct berval * +ldap_nameform2bv( LDAPNameForm * nf, struct berval *bv ) +{ + safe_string * ss; + + if ( !nf || !bv ) + return NULL; + + ss = new_safe_string(256); + if ( !ss ) + return NULL; + + print_literal(ss,"("/*)*/); + print_whsp(ss); + + print_numericoid(ss, nf->nf_oid); + print_whsp(ss); + + if ( nf->nf_names ) { + print_literal(ss,"NAME"); + print_qdescrs(ss,nf->nf_names); + } + + if ( nf->nf_desc ) { + print_literal(ss,"DESC"); + print_qdstring(ss,nf->nf_desc); + } + + if ( nf->nf_obsolete ) { + print_literal(ss, "OBSOLETE"); + print_whsp(ss); + } + + print_literal(ss,"OC"); + print_whsp(ss); + print_woid(ss,nf->nf_objectclass); + print_whsp(ss); + + print_literal(ss,"MUST"); + print_whsp(ss); + print_oids(ss,nf->nf_at_oids_must); + print_whsp(ss); + + + if ( nf->nf_at_oids_may ) { + print_literal(ss,"MAY"); + print_whsp(ss); + print_oids(ss,nf->nf_at_oids_may); + print_whsp(ss); + } + + print_whsp(ss); + print_extensions(ss, nf->nf_extensions); + + print_literal(ss, /*(*/")"); + + bv->bv_val = safe_strdup(ss); + bv->bv_len = ss->pos; + safe_string_free(ss); + return(bv); +} + +char * +ldap_attributetype2str( LDAPAttributeType * at ) +{ + struct berval bv; + if (ldap_attributetype2bv( at, &bv )) + return(bv.bv_val); + else + return NULL; +} + +struct berval * +ldap_attributetype2bv( LDAPAttributeType * at, struct berval *bv ) +{ + safe_string * ss; + + if ( !at || !bv ) + return NULL; + + ss = new_safe_string(256); + if ( !ss ) + return NULL; + + print_literal(ss,"("/*)*/); + print_whsp(ss); + + print_numericoid(ss, at->at_oid); + print_whsp(ss); + + if ( at->at_names ) { + print_literal(ss,"NAME"); + print_qdescrs(ss,at->at_names); + } + + if ( at->at_desc ) { + print_literal(ss,"DESC"); + print_qdstring(ss,at->at_desc); + } + + if ( at->at_obsolete ) { + print_literal(ss, "OBSOLETE"); + print_whsp(ss); + } + + if ( at->at_sup_oid ) { + print_literal(ss,"SUP"); + print_woid(ss,at->at_sup_oid); + } + + if ( at->at_equality_oid ) { + print_literal(ss,"EQUALITY"); + print_woid(ss,at->at_equality_oid); + } + + if ( at->at_ordering_oid ) { + print_literal(ss,"ORDERING"); + print_woid(ss,at->at_ordering_oid); + } + + if ( at->at_substr_oid ) { + print_literal(ss,"SUBSTR"); + print_woid(ss,at->at_substr_oid); + } + + if ( at->at_syntax_oid ) { + print_literal(ss,"SYNTAX"); + print_whsp(ss); + print_noidlen(ss,at->at_syntax_oid,at->at_syntax_len); + print_whsp(ss); + } + + if ( at->at_single_value == LDAP_SCHEMA_YES ) { + print_literal(ss,"SINGLE-VALUE"); + print_whsp(ss); + } + + if ( at->at_collective == LDAP_SCHEMA_YES ) { + print_literal(ss,"COLLECTIVE"); + print_whsp(ss); + } + + if ( at->at_no_user_mod == LDAP_SCHEMA_YES ) { + print_literal(ss,"NO-USER-MODIFICATION"); + print_whsp(ss); + } + + if ( at->at_usage != LDAP_SCHEMA_USER_APPLICATIONS ) { + print_literal(ss,"USAGE"); + print_whsp(ss); + switch (at->at_usage) { + case LDAP_SCHEMA_DIRECTORY_OPERATION: + print_literal(ss,"directoryOperation"); + break; + case LDAP_SCHEMA_DISTRIBUTED_OPERATION: + print_literal(ss,"distributedOperation"); + break; + case LDAP_SCHEMA_DSA_OPERATION: + print_literal(ss,"dSAOperation"); + break; + default: + print_literal(ss,"UNKNOWN"); + break; + } + } + + print_whsp(ss); + + print_extensions(ss, at->at_extensions); + + print_literal(ss,/*(*/")"); + + bv->bv_val = safe_strdup(ss); + bv->bv_len = ss->pos; + safe_string_free(ss); + return(bv); +} + +/* + * Now come the parsers. There is one parser for each entity type: + * objectclasses, attributetypes, etc. + * + * Each of them is written as a recursive-descent parser, except that + * none of them is really recursive. But the idea is kept: there + * is one routine per non-terminal that eithers gobbles lexical tokens + * or calls lower-level routines, etc. + * + * The scanner is implemented in the routine get_token. Actually, + * get_token is more than a scanner and will return tokens that are + * in fact non-terminals in the grammar. So you can see the whole + * approach as the combination of a low-level bottom-up recognizer + * combined with a scanner and a number of top-down parsers. Or just + * consider that the real grammars recognized by the parsers are not + * those of the standards. As a matter of fact, our parsers are more + * liberal than the spec when there is no ambiguity. + * + * The difference is pretty academic (modulo bugs or incorrect + * interpretation of the specs). + */ + +typedef enum tk_t { + TK_NOENDQUOTE = -2, + TK_OUTOFMEM = -1, + TK_EOS = 0, + TK_UNEXPCHAR = 1, + TK_BAREWORD = 2, + TK_QDSTRING = 3, + TK_LEFTPAREN = 4, + TK_RIGHTPAREN = 5, + TK_DOLLAR = 6, + TK_QDESCR = TK_QDSTRING +} tk_t; + +static tk_t +get_token( const char ** sp, char ** token_val ) +{ + tk_t kind; + const char * p; + const char * q; + char * res; + + *token_val = NULL; + switch (**sp) { + case '\0': + kind = TK_EOS; + (*sp)++; + break; + case '(': + kind = TK_LEFTPAREN; + (*sp)++; + break; + case ')': + kind = TK_RIGHTPAREN; + (*sp)++; + break; + case '$': + kind = TK_DOLLAR; + (*sp)++; + break; + case '\'': + kind = TK_QDSTRING; + (*sp)++; + p = *sp; + while ( **sp != '\'' && **sp != '\0' ) + (*sp)++; + if ( **sp == '\'' ) { + q = *sp; + res = LDAP_MALLOC(q-p+1); + if ( !res ) { + kind = TK_OUTOFMEM; + } else { + strncpy(res,p,q-p); + res[q-p] = '\0'; + *token_val = res; + } + (*sp)++; + } else { + kind = TK_NOENDQUOTE; + } + break; + default: + kind = TK_BAREWORD; + p = *sp; + while ( !LDAP_SPACE(**sp) && + **sp != '(' && + **sp != ')' && + **sp != '$' && + **sp != '\'' && + /* for suggested minimum upper bound on the number + * of characters (RFC 4517) */ + **sp != '{' && + **sp != '\0' ) + (*sp)++; + q = *sp; + res = LDAP_MALLOC(q-p+1); + if ( !res ) { + kind = TK_OUTOFMEM; + } else { + strncpy(res,p,q-p); + res[q-p] = '\0'; + *token_val = res; + } + break; +/* kind = TK_UNEXPCHAR; */ +/* break; */ + } + + return kind; +} + +/* Gobble optional whitespace */ +static void +parse_whsp(const char **sp) +{ + while (LDAP_SPACE(**sp)) + (*sp)++; +} + +/* TBC:!! + * General note for all parsers: to guarantee the algorithm halts they + * must always advance the pointer even when an error is found. For + * this one is not that important since an error here is fatal at the + * upper layers, but it is a simple strategy that will not get in + * endless loops. + */ + +/* Parse a sequence of dot-separated decimal strings */ +char * +ldap_int_parse_numericoid(const char **sp, int *code, const int flags) +{ + char * res = NULL; + const char * start = *sp; + int len; + int quoted = 0; + + /* Netscape puts the SYNTAX value in quotes (incorrectly) */ + if ( flags & LDAP_SCHEMA_ALLOW_QUOTED && **sp == '\'' ) { + quoted = 1; + (*sp)++; + start++; + } + /* Each iteration of this loop gets one decimal string */ + while (**sp) { + if ( !LDAP_DIGIT(**sp) ) { + /* + * Initial char is not a digit or char after dot is + * not a digit + */ + *code = LDAP_SCHERR_NODIGIT; + return NULL; + } + (*sp)++; + while ( LDAP_DIGIT(**sp) ) + (*sp)++; + if ( **sp != '.' ) + break; + /* Otherwise, gobble the dot and loop again */ + (*sp)++; + } + /* Now *sp points at the char past the numericoid. Perfect. */ + len = *sp - start; + if ( flags & LDAP_SCHEMA_ALLOW_QUOTED && quoted ) { + if ( **sp == '\'' ) { + (*sp)++; + } else { + *code = LDAP_SCHERR_UNEXPTOKEN; + return NULL; + } + } + if (flags & LDAP_SCHEMA_SKIP) { + res = (char *)start; + } else { + res = LDAP_MALLOC(len+1); + if (!res) { + *code = LDAP_SCHERR_OUTOFMEM; + return(NULL); + } + strncpy(res,start,len); + res[len] = '\0'; + } + return(res); +} + +/* Parse a sequence of dot-separated decimal strings */ +int +ldap_int_parse_ruleid(const char **sp, int *code, const int flags, int *ruleid) +{ + *ruleid=0; + + if ( !LDAP_DIGIT(**sp) ) { + *code = LDAP_SCHERR_NODIGIT; + return -1; + } + *ruleid = (**sp) - '0'; + (*sp)++; + + while ( LDAP_DIGIT(**sp) ) { + *ruleid *= 10; + *ruleid += (**sp) - '0'; + (*sp)++; + } + + return 0; +} + +/* Parse a qdescr or a list of them enclosed in () */ +static char ** +parse_qdescrs(const char **sp, int *code) +{ + char ** res; + char ** res1; + tk_t kind; + char * sval; + int size; + int pos; + + parse_whsp(sp); + kind = get_token(sp,&sval); + if ( kind == TK_LEFTPAREN ) { + /* Let's presume there will be at least 2 entries */ + size = 3; + res = LDAP_CALLOC(3,sizeof(char *)); + if ( !res ) { + *code = LDAP_SCHERR_OUTOFMEM; + return NULL; + } + pos = 0; + while (1) { + parse_whsp(sp); + kind = get_token(sp,&sval); + if ( kind == TK_RIGHTPAREN ) + break; + if ( kind == TK_QDESCR ) { + if ( pos == size-2 ) { + size++; + res1 = LDAP_REALLOC(res,size*sizeof(char *)); + if ( !res1 ) { + LDAP_VFREE(res); + LDAP_FREE(sval); + *code = LDAP_SCHERR_OUTOFMEM; + return(NULL); + } + res = res1; + } + res[pos++] = sval; + res[pos] = NULL; + parse_whsp(sp); + } else { + LDAP_VFREE(res); + LDAP_FREE(sval); + *code = LDAP_SCHERR_UNEXPTOKEN; + return(NULL); + } + } + parse_whsp(sp); + return(res); + } else if ( kind == TK_QDESCR ) { + res = LDAP_CALLOC(2,sizeof(char *)); + if ( !res ) { + *code = LDAP_SCHERR_OUTOFMEM; + return NULL; + } + res[0] = sval; + res[1] = NULL; + parse_whsp(sp); + return res; + } else { + LDAP_FREE(sval); + *code = LDAP_SCHERR_BADNAME; + return NULL; + } +} + +/* Parse a woid */ +static char * +parse_woid(const char **sp, int *code) +{ + char * sval; + tk_t kind; + + parse_whsp(sp); + kind = get_token(sp, &sval); + if ( kind != TK_BAREWORD ) { + LDAP_FREE(sval); + *code = LDAP_SCHERR_UNEXPTOKEN; + return NULL; + } + parse_whsp(sp); + return sval; +} + +/* Parse a noidlen */ +static char * +parse_noidlen(const char **sp, int *code, int *len, int flags) +{ + char * sval; + const char *savepos; + int quoted = 0; + int allow_quoted = ( flags & LDAP_SCHEMA_ALLOW_QUOTED ); + int allow_oidmacro = ( flags & LDAP_SCHEMA_ALLOW_OID_MACRO ); + + *len = 0; + /* Netscape puts the SYNTAX value in quotes (incorrectly) */ + if ( allow_quoted && **sp == '\'' ) { + quoted = 1; + (*sp)++; + } + savepos = *sp; + sval = ldap_int_parse_numericoid(sp, code, 0); + if ( !sval ) { + if ( allow_oidmacro + && *sp == savepos + && *code == LDAP_SCHERR_NODIGIT ) + { + if ( get_token(sp, &sval) != TK_BAREWORD ) { + if ( sval != NULL ) { + LDAP_FREE(sval); + } + return NULL; + } + } else { + return NULL; + } + } + if ( **sp == '{' /*}*/ ) { + (*sp)++; + *len = atoi(*sp); + while ( LDAP_DIGIT(**sp) ) + (*sp)++; + if ( **sp != /*{*/ '}' ) { + *code = LDAP_SCHERR_UNEXPTOKEN; + LDAP_FREE(sval); + return NULL; + } + (*sp)++; + } + if ( allow_quoted && quoted ) { + if ( **sp == '\'' ) { + (*sp)++; + } else { + *code = LDAP_SCHERR_UNEXPTOKEN; + LDAP_FREE(sval); + return NULL; + } + } + return sval; +} + +/* + * Next routine will accept a qdstring in place of an oid if + * allow_quoted is set. This is necessary to interoperate with + * Netscape Directory server that will improperly quote each oid (at + * least those of the descr kind) in the SUP clause. + */ + +/* Parse a woid or a $-separated list of them enclosed in () */ +static char ** +parse_oids(const char **sp, int *code, const int allow_quoted) +{ + char ** res; + char ** res1; + tk_t kind; + char * sval; + int size; + int pos; + + /* + * Strictly speaking, doing this here accepts whsp before the + * ( at the begining of an oidlist, but this is harmless. Also, + * we are very liberal in what we accept as an OID. Maybe + * refine later. + */ + parse_whsp(sp); + kind = get_token(sp,&sval); + if ( kind == TK_LEFTPAREN ) { + /* Let's presume there will be at least 2 entries */ + size = 3; + res = LDAP_CALLOC(3,sizeof(char *)); + if ( !res ) { + *code = LDAP_SCHERR_OUTOFMEM; + return NULL; + } + pos = 0; + parse_whsp(sp); + kind = get_token(sp,&sval); + if ( kind == TK_BAREWORD || + ( allow_quoted && kind == TK_QDSTRING ) ) { + res[pos++] = sval; + res[pos] = NULL; + } else if ( kind == TK_RIGHTPAREN ) { + /* FIXME: be liberal in what we accept... */ + parse_whsp(sp); + LDAP_FREE(res); + return NULL; + } else { + *code = LDAP_SCHERR_UNEXPTOKEN; + LDAP_FREE(sval); + LDAP_VFREE(res); + return NULL; + } + parse_whsp(sp); + while (1) { + kind = get_token(sp,&sval); + if ( kind == TK_RIGHTPAREN ) + break; + if ( kind == TK_DOLLAR ) { + parse_whsp(sp); + kind = get_token(sp,&sval); + if ( kind == TK_BAREWORD || + ( allow_quoted && + kind == TK_QDSTRING ) ) { + if ( pos == size-2 ) { + size++; + res1 = LDAP_REALLOC(res,size*sizeof(char *)); + if ( !res1 ) { + LDAP_FREE(sval); + LDAP_VFREE(res); + *code = LDAP_SCHERR_OUTOFMEM; + return(NULL); + } + res = res1; + } + res[pos++] = sval; + res[pos] = NULL; + } else { + *code = LDAP_SCHERR_UNEXPTOKEN; + LDAP_FREE(sval); + LDAP_VFREE(res); + return NULL; + } + parse_whsp(sp); + } else { + *code = LDAP_SCHERR_UNEXPTOKEN; + LDAP_FREE(sval); + LDAP_VFREE(res); + return NULL; + } + } + parse_whsp(sp); + return(res); + } else if ( kind == TK_BAREWORD || + ( allow_quoted && kind == TK_QDSTRING ) ) { + res = LDAP_CALLOC(2,sizeof(char *)); + if ( !res ) { + LDAP_FREE(sval); + *code = LDAP_SCHERR_OUTOFMEM; + return NULL; + } + res[0] = sval; + res[1] = NULL; + parse_whsp(sp); + return res; + } else { + LDAP_FREE(sval); + *code = LDAP_SCHERR_BADNAME; + return NULL; + } +} + +static int +add_extension(LDAPSchemaExtensionItem ***extensions, + char * name, char ** values) +{ + int n; + LDAPSchemaExtensionItem **tmp, *ext; + + ext = LDAP_CALLOC(1, sizeof(LDAPSchemaExtensionItem)); + if ( !ext ) + return 1; + ext->lsei_name = name; + ext->lsei_values = values; + + if ( !*extensions ) { + *extensions = + LDAP_CALLOC(2, sizeof(LDAPSchemaExtensionItem *)); + if ( !*extensions ) { + LDAP_FREE( ext ); + return 1; + } + n = 0; + } else { + for ( n=0; (*extensions)[n] != NULL; n++ ) + ; + tmp = LDAP_REALLOC(*extensions, + (n+2)*sizeof(LDAPSchemaExtensionItem *)); + if ( !tmp ) { + LDAP_FREE( ext ); + return 1; + } + *extensions = tmp; + } + (*extensions)[n] = ext; + (*extensions)[n+1] = NULL; + return 0; +} + +static void +free_extensions(LDAPSchemaExtensionItem **extensions) +{ + LDAPSchemaExtensionItem **ext; + + if ( extensions ) { + for ( ext = extensions; *ext != NULL; ext++ ) { + LDAP_FREE((*ext)->lsei_name); + LDAP_VFREE((*ext)->lsei_values); + LDAP_FREE(*ext); + } + LDAP_FREE(extensions); + } +} + +void +ldap_syntax_free( LDAPSyntax * syn ) +{ + if ( !syn ) return; + LDAP_FREE(syn->syn_oid); + if (syn->syn_names) LDAP_VFREE(syn->syn_names); + if (syn->syn_desc) LDAP_FREE(syn->syn_desc); + free_extensions(syn->syn_extensions); + LDAP_FREE(syn); +} + +LDAPSyntax * +ldap_str2syntax( LDAP_CONST char * s, + int * code, + LDAP_CONST char ** errp, + LDAP_CONST unsigned flags ) +{ + tk_t kind; + const char * ss = s; + char * sval; + int seen_name = 0; + int seen_desc = 0; + LDAPSyntax * syn; + char ** ext_vals; + + if ( !s ) { + *code = LDAP_SCHERR_EMPTY; + *errp = ""; + return NULL; + } + + *errp = s; + syn = LDAP_CALLOC(1,sizeof(LDAPSyntax)); + + if ( !syn ) { + *code = LDAP_SCHERR_OUTOFMEM; + return NULL; + } + + kind = get_token(&ss,&sval); + if ( kind != TK_LEFTPAREN ) { + LDAP_FREE(sval); + *code = LDAP_SCHERR_NOLEFTPAREN; + ldap_syntax_free(syn); + return NULL; + } + + parse_whsp(&ss); + syn->syn_oid = ldap_int_parse_numericoid(&ss,code,0); + if ( !syn->syn_oid ) { + *errp = ss; + ldap_syntax_free(syn); + return NULL; + } + parse_whsp(&ss); + + /* + * Beyond this point we will be liberal and accept the items + * in any order. + */ + while (1) { + kind = get_token(&ss,&sval); + switch (kind) { + case TK_EOS: + *code = LDAP_SCHERR_NORIGHTPAREN; + *errp = EndOfInput; + ldap_syntax_free(syn); + return NULL; + case TK_RIGHTPAREN: + return syn; + case TK_BAREWORD: + if ( !strcasecmp(sval,"NAME") ) { + LDAP_FREE(sval); + if ( seen_name ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_syntax_free(syn); + return(NULL); + } + seen_name = 1; + syn->syn_names = parse_qdescrs(&ss,code); + if ( !syn->syn_names ) { + if ( *code != LDAP_SCHERR_OUTOFMEM ) + *code = LDAP_SCHERR_BADNAME; + *errp = ss; + ldap_syntax_free(syn); + return NULL; + } + } else if ( !strcasecmp(sval,"DESC") ) { + LDAP_FREE(sval); + if ( seen_desc ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_syntax_free(syn); + return(NULL); + } + seen_desc = 1; + parse_whsp(&ss); + kind = get_token(&ss,&sval); + if ( kind != TK_QDSTRING ) { + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_syntax_free(syn); + return NULL; + } + syn->syn_desc = sval; + parse_whsp(&ss); + } else if ( sval[0] == 'X' && sval[1] == '-' ) { + /* Should be parse_qdstrings */ + ext_vals = parse_qdescrs(&ss, code); + if ( !ext_vals ) { + *errp = ss; + ldap_syntax_free(syn); + return NULL; + } + if ( add_extension(&syn->syn_extensions, + sval, ext_vals) ) { + *code = LDAP_SCHERR_OUTOFMEM; + *errp = ss; + LDAP_FREE(sval); + ldap_syntax_free(syn); + return NULL; + } + } else { + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_syntax_free(syn); + return NULL; + } + break; + default: + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_syntax_free(syn); + return NULL; + } + } +} + +void +ldap_matchingrule_free( LDAPMatchingRule * mr ) +{ + if (!mr) return; + LDAP_FREE(mr->mr_oid); + if (mr->mr_names) LDAP_VFREE(mr->mr_names); + if (mr->mr_desc) LDAP_FREE(mr->mr_desc); + if (mr->mr_syntax_oid) LDAP_FREE(mr->mr_syntax_oid); + free_extensions(mr->mr_extensions); + LDAP_FREE(mr); +} + +LDAPMatchingRule * +ldap_str2matchingrule( LDAP_CONST char * s, + int * code, + LDAP_CONST char ** errp, + LDAP_CONST unsigned flags ) +{ + tk_t kind; + const char * ss = s; + char * sval; + int seen_name = 0; + int seen_desc = 0; + int seen_obsolete = 0; + int seen_syntax = 0; + LDAPMatchingRule * mr; + char ** ext_vals; + const char * savepos; + + if ( !s ) { + *code = LDAP_SCHERR_EMPTY; + *errp = ""; + return NULL; + } + + *errp = s; + mr = LDAP_CALLOC(1,sizeof(LDAPMatchingRule)); + + if ( !mr ) { + *code = LDAP_SCHERR_OUTOFMEM; + return NULL; + } + + kind = get_token(&ss,&sval); + if ( kind != TK_LEFTPAREN ) { + *code = LDAP_SCHERR_NOLEFTPAREN; + LDAP_FREE(sval); + ldap_matchingrule_free(mr); + return NULL; + } + + parse_whsp(&ss); + savepos = ss; + mr->mr_oid = ldap_int_parse_numericoid(&ss,code,flags); + if ( !mr->mr_oid ) { + if ( flags & LDAP_SCHEMA_ALLOW_NO_OID ) { + /* Backtracking */ + ss = savepos; + kind = get_token(&ss,&sval); + if ( kind == TK_BAREWORD ) { + if ( !strcasecmp(sval, "NAME") || + !strcasecmp(sval, "DESC") || + !strcasecmp(sval, "OBSOLETE") || + !strcasecmp(sval, "SYNTAX") || + !strncasecmp(sval, "X-", 2) ) { + /* Missing OID, backtrack */ + ss = savepos; + } else { + /* Non-numerical OID, ignore */ + } + } + LDAP_FREE(sval); + } else { + *errp = ss; + ldap_matchingrule_free(mr); + return NULL; + } + } + parse_whsp(&ss); + + /* + * Beyond this point we will be liberal and accept the items + * in any order. + */ + while (1) { + kind = get_token(&ss,&sval); + switch (kind) { + case TK_EOS: + *code = LDAP_SCHERR_NORIGHTPAREN; + *errp = EndOfInput; + ldap_matchingrule_free(mr); + return NULL; + case TK_RIGHTPAREN: + if( !seen_syntax ) { + *code = LDAP_SCHERR_MISSING; + ldap_matchingrule_free(mr); + return NULL; + } + return mr; + case TK_BAREWORD: + if ( !strcasecmp(sval,"NAME") ) { + LDAP_FREE(sval); + if ( seen_name ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_matchingrule_free(mr); + return(NULL); + } + seen_name = 1; + mr->mr_names = parse_qdescrs(&ss,code); + if ( !mr->mr_names ) { + if ( *code != LDAP_SCHERR_OUTOFMEM ) + *code = LDAP_SCHERR_BADNAME; + *errp = ss; + ldap_matchingrule_free(mr); + return NULL; + } + } else if ( !strcasecmp(sval,"DESC") ) { + LDAP_FREE(sval); + if ( seen_desc ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_matchingrule_free(mr); + return(NULL); + } + seen_desc = 1; + parse_whsp(&ss); + kind = get_token(&ss,&sval); + if ( kind != TK_QDSTRING ) { + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_matchingrule_free(mr); + return NULL; + } + mr->mr_desc = sval; + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"OBSOLETE") ) { + LDAP_FREE(sval); + if ( seen_obsolete ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_matchingrule_free(mr); + return(NULL); + } + seen_obsolete = 1; + mr->mr_obsolete = LDAP_SCHEMA_YES; + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"SYNTAX") ) { + LDAP_FREE(sval); + if ( seen_syntax ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_matchingrule_free(mr); + return(NULL); + } + seen_syntax = 1; + parse_whsp(&ss); + mr->mr_syntax_oid = + ldap_int_parse_numericoid(&ss,code,flags); + if ( !mr->mr_syntax_oid ) { + *errp = ss; + ldap_matchingrule_free(mr); + return NULL; + } + parse_whsp(&ss); + } else if ( sval[0] == 'X' && sval[1] == '-' ) { + /* Should be parse_qdstrings */ + ext_vals = parse_qdescrs(&ss, code); + if ( !ext_vals ) { + *errp = ss; + ldap_matchingrule_free(mr); + return NULL; + } + if ( add_extension(&mr->mr_extensions, + sval, ext_vals) ) { + *code = LDAP_SCHERR_OUTOFMEM; + *errp = ss; + LDAP_FREE(sval); + ldap_matchingrule_free(mr); + return NULL; + } + } else { + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_matchingrule_free(mr); + return NULL; + } + break; + default: + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_matchingrule_free(mr); + return NULL; + } + } +} + +void +ldap_matchingruleuse_free( LDAPMatchingRuleUse * mru ) +{ + if (!mru) return; + LDAP_FREE(mru->mru_oid); + if (mru->mru_names) LDAP_VFREE(mru->mru_names); + if (mru->mru_desc) LDAP_FREE(mru->mru_desc); + if (mru->mru_applies_oids) LDAP_VFREE(mru->mru_applies_oids); + free_extensions(mru->mru_extensions); + LDAP_FREE(mru); +} + +LDAPMatchingRuleUse * +ldap_str2matchingruleuse( LDAP_CONST char * s, + int * code, + LDAP_CONST char ** errp, + LDAP_CONST unsigned flags ) +{ + tk_t kind; + const char * ss = s; + char * sval; + int seen_name = 0; + int seen_desc = 0; + int seen_obsolete = 0; + int seen_applies = 0; + LDAPMatchingRuleUse * mru; + char ** ext_vals; + const char * savepos; + + if ( !s ) { + *code = LDAP_SCHERR_EMPTY; + *errp = ""; + return NULL; + } + + *errp = s; + mru = LDAP_CALLOC(1,sizeof(LDAPMatchingRuleUse)); + + if ( !mru ) { + *code = LDAP_SCHERR_OUTOFMEM; + return NULL; + } + + kind = get_token(&ss,&sval); + if ( kind != TK_LEFTPAREN ) { + *code = LDAP_SCHERR_NOLEFTPAREN; + LDAP_FREE(sval); + ldap_matchingruleuse_free(mru); + return NULL; + } + + parse_whsp(&ss); + savepos = ss; + mru->mru_oid = ldap_int_parse_numericoid(&ss,code,flags); + if ( !mru->mru_oid ) { + if ( flags & LDAP_SCHEMA_ALLOW_NO_OID ) { + /* Backtracking */ + ss = savepos; + kind = get_token(&ss,&sval); + if ( kind == TK_BAREWORD ) { + if ( !strcasecmp(sval, "NAME") || + !strcasecmp(sval, "DESC") || + !strcasecmp(sval, "OBSOLETE") || + !strcasecmp(sval, "APPLIES") || + !strncasecmp(sval, "X-", 2) ) { + /* Missing OID, backtrack */ + ss = savepos; + } else { + /* Non-numerical OID, ignore */ + } + } + LDAP_FREE(sval); + } else { + *errp = ss; + ldap_matchingruleuse_free(mru); + return NULL; + } + } + parse_whsp(&ss); + + /* + * Beyond this point we will be liberal and accept the items + * in any order. + */ + while (1) { + kind = get_token(&ss,&sval); + switch (kind) { + case TK_EOS: + *code = LDAP_SCHERR_NORIGHTPAREN; + *errp = EndOfInput; + ldap_matchingruleuse_free(mru); + return NULL; + case TK_RIGHTPAREN: + if( !seen_applies ) { + *code = LDAP_SCHERR_MISSING; + ldap_matchingruleuse_free(mru); + return NULL; + } + return mru; + case TK_BAREWORD: + if ( !strcasecmp(sval,"NAME") ) { + LDAP_FREE(sval); + if ( seen_name ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_matchingruleuse_free(mru); + return(NULL); + } + seen_name = 1; + mru->mru_names = parse_qdescrs(&ss,code); + if ( !mru->mru_names ) { + if ( *code != LDAP_SCHERR_OUTOFMEM ) + *code = LDAP_SCHERR_BADNAME; + *errp = ss; + ldap_matchingruleuse_free(mru); + return NULL; + } + } else if ( !strcasecmp(sval,"DESC") ) { + LDAP_FREE(sval); + if ( seen_desc ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_matchingruleuse_free(mru); + return(NULL); + } + seen_desc = 1; + parse_whsp(&ss); + kind = get_token(&ss,&sval); + if ( kind != TK_QDSTRING ) { + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_matchingruleuse_free(mru); + return NULL; + } + mru->mru_desc = sval; + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"OBSOLETE") ) { + LDAP_FREE(sval); + if ( seen_obsolete ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_matchingruleuse_free(mru); + return(NULL); + } + seen_obsolete = 1; + mru->mru_obsolete = LDAP_SCHEMA_YES; + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"APPLIES") ) { + LDAP_FREE(sval); + if ( seen_applies ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_matchingruleuse_free(mru); + return(NULL); + } + seen_applies = 1; + mru->mru_applies_oids = parse_oids(&ss, + code, + flags); + if ( !mru->mru_applies_oids && *code != LDAP_SUCCESS ) { + *errp = ss; + ldap_matchingruleuse_free(mru); + return NULL; + } + } else if ( sval[0] == 'X' && sval[1] == '-' ) { + /* Should be parse_qdstrings */ + ext_vals = parse_qdescrs(&ss, code); + if ( !ext_vals ) { + *errp = ss; + ldap_matchingruleuse_free(mru); + return NULL; + } + if ( add_extension(&mru->mru_extensions, + sval, ext_vals) ) { + *code = LDAP_SCHERR_OUTOFMEM; + *errp = ss; + LDAP_FREE(sval); + ldap_matchingruleuse_free(mru); + return NULL; + } + } else { + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_matchingruleuse_free(mru); + return NULL; + } + break; + default: + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_matchingruleuse_free(mru); + return NULL; + } + } +} + +void +ldap_attributetype_free(LDAPAttributeType * at) +{ + if (!at) return; + LDAP_FREE(at->at_oid); + if (at->at_names) LDAP_VFREE(at->at_names); + if (at->at_desc) LDAP_FREE(at->at_desc); + if (at->at_sup_oid) LDAP_FREE(at->at_sup_oid); + if (at->at_equality_oid) LDAP_FREE(at->at_equality_oid); + if (at->at_ordering_oid) LDAP_FREE(at->at_ordering_oid); + if (at->at_substr_oid) LDAP_FREE(at->at_substr_oid); + if (at->at_syntax_oid) LDAP_FREE(at->at_syntax_oid); + free_extensions(at->at_extensions); + LDAP_FREE(at); +} + +LDAPAttributeType * +ldap_str2attributetype( LDAP_CONST char * s, + int * code, + LDAP_CONST char ** errp, + LDAP_CONST unsigned flags ) +{ + tk_t kind; + const char * ss = s; + char * sval; + int seen_name = 0; + int seen_desc = 0; + int seen_obsolete = 0; + int seen_sup = 0; + int seen_equality = 0; + int seen_ordering = 0; + int seen_substr = 0; + int seen_syntax = 0; + int seen_usage = 0; + LDAPAttributeType * at; + char ** ext_vals; + const char * savepos; + + if ( !s ) { + *code = LDAP_SCHERR_EMPTY; + *errp = ""; + return NULL; + } + + *errp = s; + at = LDAP_CALLOC(1,sizeof(LDAPAttributeType)); + + if ( !at ) { + *code = LDAP_SCHERR_OUTOFMEM; + return NULL; + } + + kind = get_token(&ss,&sval); + if ( kind != TK_LEFTPAREN ) { + *code = LDAP_SCHERR_NOLEFTPAREN; + LDAP_FREE(sval); + ldap_attributetype_free(at); + return NULL; + } + + /* + * Definitions MUST begin with an OID in the numericoid format. + * However, this routine is used by clients to parse the response + * from servers and very well known servers will provide an OID + * in the wrong format or even no OID at all. We do our best to + * extract info from those servers. + */ + parse_whsp(&ss); + savepos = ss; + at->at_oid = ldap_int_parse_numericoid(&ss,code,0); + if ( !at->at_oid ) { + if ( ( flags & ( LDAP_SCHEMA_ALLOW_NO_OID + | LDAP_SCHEMA_ALLOW_OID_MACRO ) ) + && (ss == savepos) ) + { + /* Backtracking */ + ss = savepos; + kind = get_token(&ss,&sval); + if ( kind == TK_BAREWORD ) { + if ( !strcasecmp(sval, "NAME") || + !strcasecmp(sval, "DESC") || + !strcasecmp(sval, "OBSOLETE") || + !strcasecmp(sval, "SUP") || + !strcasecmp(sval, "EQUALITY") || + !strcasecmp(sval, "ORDERING") || + !strcasecmp(sval, "SUBSTR") || + !strcasecmp(sval, "SYNTAX") || + !strcasecmp(sval, "SINGLE-VALUE") || + !strcasecmp(sval, "COLLECTIVE") || + !strcasecmp(sval, "NO-USER-MODIFICATION") || + !strcasecmp(sval, "USAGE") || + !strncasecmp(sval, "X-", 2) ) + { + /* Missing OID, backtrack */ + ss = savepos; + } else if ( flags + & LDAP_SCHEMA_ALLOW_OID_MACRO) + { + /* Non-numerical OID ... */ + int len = ss-savepos; + at->at_oid = LDAP_MALLOC(len+1); + strncpy(at->at_oid, savepos, len); + at->at_oid[len] = 0; + } + } + LDAP_FREE(sval); + } else { + *errp = ss; + ldap_attributetype_free(at); + return NULL; + } + } + parse_whsp(&ss); + + /* + * Beyond this point we will be liberal and accept the items + * in any order. + */ + while (1) { + kind = get_token(&ss,&sval); + switch (kind) { + case TK_EOS: + *code = LDAP_SCHERR_NORIGHTPAREN; + *errp = EndOfInput; + ldap_attributetype_free(at); + return NULL; + case TK_RIGHTPAREN: + return at; + case TK_BAREWORD: + if ( !strcasecmp(sval,"NAME") ) { + LDAP_FREE(sval); + if ( seen_name ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_attributetype_free(at); + return(NULL); + } + seen_name = 1; + at->at_names = parse_qdescrs(&ss,code); + if ( !at->at_names ) { + if ( *code != LDAP_SCHERR_OUTOFMEM ) + *code = LDAP_SCHERR_BADNAME; + *errp = ss; + ldap_attributetype_free(at); + return NULL; + } + } else if ( !strcasecmp(sval,"DESC") ) { + LDAP_FREE(sval); + if ( seen_desc ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_attributetype_free(at); + return(NULL); + } + seen_desc = 1; + parse_whsp(&ss); + kind = get_token(&ss,&sval); + if ( kind != TK_QDSTRING ) { + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_attributetype_free(at); + return NULL; + } + at->at_desc = sval; + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"OBSOLETE") ) { + LDAP_FREE(sval); + if ( seen_obsolete ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_attributetype_free(at); + return(NULL); + } + seen_obsolete = 1; + at->at_obsolete = LDAP_SCHEMA_YES; + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"SUP") ) { + LDAP_FREE(sval); + if ( seen_sup ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_attributetype_free(at); + return(NULL); + } + seen_sup = 1; + at->at_sup_oid = parse_woid(&ss,code); + if ( !at->at_sup_oid ) { + *errp = ss; + ldap_attributetype_free(at); + return NULL; + } + } else if ( !strcasecmp(sval,"EQUALITY") ) { + LDAP_FREE(sval); + if ( seen_equality ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_attributetype_free(at); + return(NULL); + } + seen_equality = 1; + at->at_equality_oid = parse_woid(&ss,code); + if ( !at->at_equality_oid ) { + *errp = ss; + ldap_attributetype_free(at); + return NULL; + } + } else if ( !strcasecmp(sval,"ORDERING") ) { + LDAP_FREE(sval); + if ( seen_ordering ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_attributetype_free(at); + return(NULL); + } + seen_ordering = 1; + at->at_ordering_oid = parse_woid(&ss,code); + if ( !at->at_ordering_oid ) { + *errp = ss; + ldap_attributetype_free(at); + return NULL; + } + } else if ( !strcasecmp(sval,"SUBSTR") ) { + LDAP_FREE(sval); + if ( seen_substr ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_attributetype_free(at); + return(NULL); + } + seen_substr = 1; + at->at_substr_oid = parse_woid(&ss,code); + if ( !at->at_substr_oid ) { + *errp = ss; + ldap_attributetype_free(at); + return NULL; + } + } else if ( !strcasecmp(sval,"SYNTAX") ) { + LDAP_FREE(sval); + if ( seen_syntax ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_attributetype_free(at); + return(NULL); + } + seen_syntax = 1; + parse_whsp(&ss); + savepos = ss; + at->at_syntax_oid = + parse_noidlen(&ss, + code, + &at->at_syntax_len, + flags); + if ( !at->at_syntax_oid ) { + if ( flags & LDAP_SCHEMA_ALLOW_OID_MACRO ) { + kind = get_token(&ss,&sval); + if (kind == TK_BAREWORD) + { + char *sp = strchr(sval, '{'); + at->at_syntax_oid = sval; + if (sp) + { + *sp++ = 0; + at->at_syntax_len = atoi(sp); + while ( LDAP_DIGIT(*sp) ) + sp++; + if ( *sp != '}' ) { + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + ldap_attributetype_free(at); + return NULL; + } + } + } + } else { + *errp = ss; + ldap_attributetype_free(at); + return NULL; + } + } + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"SINGLE-VALUE") ) { + LDAP_FREE(sval); + if ( at->at_single_value ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_attributetype_free(at); + return(NULL); + } + at->at_single_value = LDAP_SCHEMA_YES; + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"COLLECTIVE") ) { + LDAP_FREE(sval); + if ( at->at_collective ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_attributetype_free(at); + return(NULL); + } + at->at_collective = LDAP_SCHEMA_YES; + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"NO-USER-MODIFICATION") ) { + LDAP_FREE(sval); + if ( at->at_no_user_mod ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_attributetype_free(at); + return(NULL); + } + at->at_no_user_mod = LDAP_SCHEMA_YES; + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"USAGE") ) { + LDAP_FREE(sval); + if ( seen_usage ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_attributetype_free(at); + return(NULL); + } + seen_usage = 1; + parse_whsp(&ss); + kind = get_token(&ss,&sval); + if ( kind != TK_BAREWORD ) { + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_attributetype_free(at); + return NULL; + } + if ( !strcasecmp(sval,"userApplications") ) + at->at_usage = + LDAP_SCHEMA_USER_APPLICATIONS; + else if ( !strcasecmp(sval,"directoryOperation") ) + at->at_usage = + LDAP_SCHEMA_DIRECTORY_OPERATION; + else if ( !strcasecmp(sval,"distributedOperation") ) + at->at_usage = + LDAP_SCHEMA_DISTRIBUTED_OPERATION; + else if ( !strcasecmp(sval,"dSAOperation") ) + at->at_usage = + LDAP_SCHEMA_DSA_OPERATION; + else { + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_attributetype_free(at); + return NULL; + } + LDAP_FREE(sval); + parse_whsp(&ss); + } else if ( sval[0] == 'X' && sval[1] == '-' ) { + /* Should be parse_qdstrings */ + ext_vals = parse_qdescrs(&ss, code); + if ( !ext_vals ) { + *errp = ss; + ldap_attributetype_free(at); + return NULL; + } + if ( add_extension(&at->at_extensions, + sval, ext_vals) ) { + *code = LDAP_SCHERR_OUTOFMEM; + *errp = ss; + LDAP_FREE(sval); + ldap_attributetype_free(at); + return NULL; + } + } else { + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_attributetype_free(at); + return NULL; + } + break; + default: + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_attributetype_free(at); + return NULL; + } + } +} + +void +ldap_objectclass_free(LDAPObjectClass * oc) +{ + if (!oc) return; + LDAP_FREE(oc->oc_oid); + if (oc->oc_names) LDAP_VFREE(oc->oc_names); + if (oc->oc_desc) LDAP_FREE(oc->oc_desc); + if (oc->oc_sup_oids) LDAP_VFREE(oc->oc_sup_oids); + if (oc->oc_at_oids_must) LDAP_VFREE(oc->oc_at_oids_must); + if (oc->oc_at_oids_may) LDAP_VFREE(oc->oc_at_oids_may); + free_extensions(oc->oc_extensions); + LDAP_FREE(oc); +} + +LDAPObjectClass * +ldap_str2objectclass( LDAP_CONST char * s, + int * code, + LDAP_CONST char ** errp, + LDAP_CONST unsigned flags ) +{ + tk_t kind; + const char * ss = s; + char * sval; + int seen_name = 0; + int seen_desc = 0; + int seen_obsolete = 0; + int seen_sup = 0; + int seen_kind = 0; + int seen_must = 0; + int seen_may = 0; + LDAPObjectClass * oc; + char ** ext_vals; + const char * savepos; + + if ( !s ) { + *code = LDAP_SCHERR_EMPTY; + *errp = ""; + return NULL; + } + + *errp = s; + oc = LDAP_CALLOC(1,sizeof(LDAPObjectClass)); + + if ( !oc ) { + *code = LDAP_SCHERR_OUTOFMEM; + return NULL; + } + oc->oc_kind = LDAP_SCHEMA_STRUCTURAL; + + kind = get_token(&ss,&sval); + if ( kind != TK_LEFTPAREN ) { + *code = LDAP_SCHERR_NOLEFTPAREN; + LDAP_FREE(sval); + ldap_objectclass_free(oc); + return NULL; + } + + /* + * Definitions MUST begin with an OID in the numericoid format. + * However, this routine is used by clients to parse the response + * from servers and very well known servers will provide an OID + * in the wrong format or even no OID at all. We do our best to + * extract info from those servers. + */ + parse_whsp(&ss); + savepos = ss; + oc->oc_oid = ldap_int_parse_numericoid(&ss,code,0); + if ( !oc->oc_oid ) { + if ( (flags & LDAP_SCHEMA_ALLOW_ALL) && (ss == savepos) ) { + /* Backtracking */ + ss = savepos; + kind = get_token(&ss,&sval); + if ( kind == TK_BAREWORD ) { + if ( !strcasecmp(sval, "NAME") || + !strcasecmp(sval, "DESC") || + !strcasecmp(sval, "OBSOLETE") || + !strcasecmp(sval, "SUP") || + !strcasecmp(sval, "ABSTRACT") || + !strcasecmp(sval, "STRUCTURAL") || + !strcasecmp(sval, "AUXILIARY") || + !strcasecmp(sval, "MUST") || + !strcasecmp(sval, "MAY") || + !strncasecmp(sval, "X-", 2) ) { + /* Missing OID, backtrack */ + ss = savepos; + } else if ( flags & + LDAP_SCHEMA_ALLOW_OID_MACRO ) { + /* Non-numerical OID, ignore */ + int len = ss-savepos; + oc->oc_oid = LDAP_MALLOC(len+1); + strncpy(oc->oc_oid, savepos, len); + oc->oc_oid[len] = 0; + } + } + LDAP_FREE(sval); + *code = 0; + } else { + *errp = ss; + ldap_objectclass_free(oc); + return NULL; + } + } + parse_whsp(&ss); + + /* + * Beyond this point we will be liberal an accept the items + * in any order. + */ + while (1) { + kind = get_token(&ss,&sval); + switch (kind) { + case TK_EOS: + *code = LDAP_SCHERR_NORIGHTPAREN; + *errp = EndOfInput; + ldap_objectclass_free(oc); + return NULL; + case TK_RIGHTPAREN: + return oc; + case TK_BAREWORD: + if ( !strcasecmp(sval,"NAME") ) { + LDAP_FREE(sval); + if ( seen_name ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_objectclass_free(oc); + return(NULL); + } + seen_name = 1; + oc->oc_names = parse_qdescrs(&ss,code); + if ( !oc->oc_names ) { + if ( *code != LDAP_SCHERR_OUTOFMEM ) + *code = LDAP_SCHERR_BADNAME; + *errp = ss; + ldap_objectclass_free(oc); + return NULL; + } + } else if ( !strcasecmp(sval,"DESC") ) { + LDAP_FREE(sval); + if ( seen_desc ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_objectclass_free(oc); + return(NULL); + } + seen_desc = 1; + parse_whsp(&ss); + kind = get_token(&ss,&sval); + if ( kind != TK_QDSTRING ) { + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_objectclass_free(oc); + return NULL; + } + oc->oc_desc = sval; + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"OBSOLETE") ) { + LDAP_FREE(sval); + if ( seen_obsolete ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_objectclass_free(oc); + return(NULL); + } + seen_obsolete = 1; + oc->oc_obsolete = LDAP_SCHEMA_YES; + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"SUP") ) { + LDAP_FREE(sval); + if ( seen_sup ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_objectclass_free(oc); + return(NULL); + } + seen_sup = 1; + oc->oc_sup_oids = parse_oids(&ss, + code, + flags); + if ( !oc->oc_sup_oids && *code != LDAP_SUCCESS ) { + *errp = ss; + ldap_objectclass_free(oc); + return NULL; + } + *code = 0; + } else if ( !strcasecmp(sval,"ABSTRACT") ) { + LDAP_FREE(sval); + if ( seen_kind ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_objectclass_free(oc); + return(NULL); + } + seen_kind = 1; + oc->oc_kind = LDAP_SCHEMA_ABSTRACT; + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"STRUCTURAL") ) { + LDAP_FREE(sval); + if ( seen_kind ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_objectclass_free(oc); + return(NULL); + } + seen_kind = 1; + oc->oc_kind = LDAP_SCHEMA_STRUCTURAL; + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"AUXILIARY") ) { + LDAP_FREE(sval); + if ( seen_kind ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_objectclass_free(oc); + return(NULL); + } + seen_kind = 1; + oc->oc_kind = LDAP_SCHEMA_AUXILIARY; + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"MUST") ) { + LDAP_FREE(sval); + if ( seen_must ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_objectclass_free(oc); + return(NULL); + } + seen_must = 1; + oc->oc_at_oids_must = parse_oids(&ss,code,0); + if ( !oc->oc_at_oids_must && *code != LDAP_SUCCESS ) { + *errp = ss; + ldap_objectclass_free(oc); + return NULL; + } + *code = 0; + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"MAY") ) { + LDAP_FREE(sval); + if ( seen_may ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_objectclass_free(oc); + return(NULL); + } + seen_may = 1; + oc->oc_at_oids_may = parse_oids(&ss,code,0); + if ( !oc->oc_at_oids_may && *code != LDAP_SUCCESS ) { + *errp = ss; + ldap_objectclass_free(oc); + return NULL; + } + *code = 0; + parse_whsp(&ss); + } else if ( sval[0] == 'X' && sval[1] == '-' ) { + /* Should be parse_qdstrings */ + ext_vals = parse_qdescrs(&ss, code); + *code = 0; + if ( !ext_vals ) { + *errp = ss; + ldap_objectclass_free(oc); + return NULL; + } + if ( add_extension(&oc->oc_extensions, + sval, ext_vals) ) { + *code = LDAP_SCHERR_OUTOFMEM; + *errp = ss; + LDAP_FREE(sval); + ldap_objectclass_free(oc); + return NULL; + } + } else { + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_objectclass_free(oc); + return NULL; + } + break; + default: + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_objectclass_free(oc); + return NULL; + } + } +} + +void +ldap_contentrule_free(LDAPContentRule * cr) +{ + if (!cr) return; + LDAP_FREE(cr->cr_oid); + if (cr->cr_names) LDAP_VFREE(cr->cr_names); + if (cr->cr_desc) LDAP_FREE(cr->cr_desc); + if (cr->cr_oc_oids_aux) LDAP_VFREE(cr->cr_oc_oids_aux); + if (cr->cr_at_oids_must) LDAP_VFREE(cr->cr_at_oids_must); + if (cr->cr_at_oids_may) LDAP_VFREE(cr->cr_at_oids_may); + if (cr->cr_at_oids_not) LDAP_VFREE(cr->cr_at_oids_not); + free_extensions(cr->cr_extensions); + LDAP_FREE(cr); +} + +LDAPContentRule * +ldap_str2contentrule( LDAP_CONST char * s, + int * code, + LDAP_CONST char ** errp, + LDAP_CONST unsigned flags ) +{ + tk_t kind; + const char * ss = s; + char * sval; + int seen_name = 0; + int seen_desc = 0; + int seen_obsolete = 0; + int seen_aux = 0; + int seen_must = 0; + int seen_may = 0; + int seen_not = 0; + LDAPContentRule * cr; + char ** ext_vals; + const char * savepos; + + if ( !s ) { + *code = LDAP_SCHERR_EMPTY; + *errp = ""; + return NULL; + } + + *errp = s; + cr = LDAP_CALLOC(1,sizeof(LDAPContentRule)); + + if ( !cr ) { + *code = LDAP_SCHERR_OUTOFMEM; + return NULL; + } + + kind = get_token(&ss,&sval); + if ( kind != TK_LEFTPAREN ) { + *code = LDAP_SCHERR_NOLEFTPAREN; + LDAP_FREE(sval); + ldap_contentrule_free(cr); + return NULL; + } + + /* + * Definitions MUST begin with an OID in the numericoid format. + */ + parse_whsp(&ss); + savepos = ss; + cr->cr_oid = ldap_int_parse_numericoid(&ss,code,0); + if ( !cr->cr_oid ) { + if ( (flags & LDAP_SCHEMA_ALLOW_ALL) && (ss == savepos) ) { + /* Backtracking */ + ss = savepos; + kind = get_token(&ss,&sval); + if ( kind == TK_BAREWORD ) { + if ( !strcasecmp(sval, "NAME") || + !strcasecmp(sval, "DESC") || + !strcasecmp(sval, "OBSOLETE") || + !strcasecmp(sval, "AUX") || + !strcasecmp(sval, "MUST") || + !strcasecmp(sval, "MAY") || + !strcasecmp(sval, "NOT") || + !strncasecmp(sval, "X-", 2) ) { + /* Missing OID, backtrack */ + ss = savepos; + } else if ( flags & + LDAP_SCHEMA_ALLOW_OID_MACRO ) { + /* Non-numerical OID, ignore */ + int len = ss-savepos; + cr->cr_oid = LDAP_MALLOC(len+1); + strncpy(cr->cr_oid, savepos, len); + cr->cr_oid[len] = 0; + } + } + LDAP_FREE(sval); + } else { + *errp = ss; + ldap_contentrule_free(cr); + return NULL; + } + } + parse_whsp(&ss); + + /* + * Beyond this point we will be liberal an accept the items + * in any order. + */ + while (1) { + kind = get_token(&ss,&sval); + switch (kind) { + case TK_EOS: + *code = LDAP_SCHERR_NORIGHTPAREN; + *errp = EndOfInput; + ldap_contentrule_free(cr); + return NULL; + case TK_RIGHTPAREN: + return cr; + case TK_BAREWORD: + if ( !strcasecmp(sval,"NAME") ) { + LDAP_FREE(sval); + if ( seen_name ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_contentrule_free(cr); + return(NULL); + } + seen_name = 1; + cr->cr_names = parse_qdescrs(&ss,code); + if ( !cr->cr_names ) { + if ( *code != LDAP_SCHERR_OUTOFMEM ) + *code = LDAP_SCHERR_BADNAME; + *errp = ss; + ldap_contentrule_free(cr); + return NULL; + } + } else if ( !strcasecmp(sval,"DESC") ) { + LDAP_FREE(sval); + if ( seen_desc ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_contentrule_free(cr); + return(NULL); + } + seen_desc = 1; + parse_whsp(&ss); + kind = get_token(&ss,&sval); + if ( kind != TK_QDSTRING ) { + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_contentrule_free(cr); + return NULL; + } + cr->cr_desc = sval; + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"OBSOLETE") ) { + LDAP_FREE(sval); + if ( seen_obsolete ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_contentrule_free(cr); + return(NULL); + } + seen_obsolete = 1; + cr->cr_obsolete = LDAP_SCHEMA_YES; + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"AUX") ) { + LDAP_FREE(sval); + if ( seen_aux ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_contentrule_free(cr); + return(NULL); + } + seen_aux = 1; + cr->cr_oc_oids_aux = parse_oids(&ss,code,0); + if ( !cr->cr_oc_oids_aux ) { + *errp = ss; + ldap_contentrule_free(cr); + return NULL; + } + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"MUST") ) { + LDAP_FREE(sval); + if ( seen_must ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_contentrule_free(cr); + return(NULL); + } + seen_must = 1; + cr->cr_at_oids_must = parse_oids(&ss,code,0); + if ( !cr->cr_at_oids_must && *code != LDAP_SUCCESS ) { + *errp = ss; + ldap_contentrule_free(cr); + return NULL; + } + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"MAY") ) { + LDAP_FREE(sval); + if ( seen_may ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_contentrule_free(cr); + return(NULL); + } + seen_may = 1; + cr->cr_at_oids_may = parse_oids(&ss,code,0); + if ( !cr->cr_at_oids_may && *code != LDAP_SUCCESS ) { + *errp = ss; + ldap_contentrule_free(cr); + return NULL; + } + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"NOT") ) { + LDAP_FREE(sval); + if ( seen_not ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_contentrule_free(cr); + return(NULL); + } + seen_not = 1; + cr->cr_at_oids_not = parse_oids(&ss,code,0); + if ( !cr->cr_at_oids_not && *code != LDAP_SUCCESS ) { + *errp = ss; + ldap_contentrule_free(cr); + return NULL; + } + parse_whsp(&ss); + } else if ( sval[0] == 'X' && sval[1] == '-' ) { + /* Should be parse_qdstrings */ + ext_vals = parse_qdescrs(&ss, code); + if ( !ext_vals ) { + *errp = ss; + ldap_contentrule_free(cr); + return NULL; + } + if ( add_extension(&cr->cr_extensions, + sval, ext_vals) ) { + *code = LDAP_SCHERR_OUTOFMEM; + *errp = ss; + LDAP_FREE(sval); + ldap_contentrule_free(cr); + return NULL; + } + } else { + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_contentrule_free(cr); + return NULL; + } + break; + default: + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_contentrule_free(cr); + return NULL; + } + } +} + +void +ldap_structurerule_free(LDAPStructureRule * sr) +{ + if (!sr) return; + if (sr->sr_names) LDAP_VFREE(sr->sr_names); + if (sr->sr_desc) LDAP_FREE(sr->sr_desc); + if (sr->sr_nameform) LDAP_FREE(sr->sr_nameform); + if (sr->sr_sup_ruleids) LDAP_FREE(sr->sr_sup_ruleids); + free_extensions(sr->sr_extensions); + LDAP_FREE(sr); +} + +LDAPStructureRule * +ldap_str2structurerule( LDAP_CONST char * s, + int * code, + LDAP_CONST char ** errp, + LDAP_CONST unsigned flags ) +{ + tk_t kind; + int ret; + const char * ss = s; + char * sval; + int seen_name = 0; + int seen_desc = 0; + int seen_obsolete = 0; + int seen_nameform = 0; + LDAPStructureRule * sr; + char ** ext_vals; + const char * savepos; + + if ( !s ) { + *code = LDAP_SCHERR_EMPTY; + *errp = ""; + return NULL; + } + + *errp = s; + sr = LDAP_CALLOC(1,sizeof(LDAPStructureRule)); + + if ( !sr ) { + *code = LDAP_SCHERR_OUTOFMEM; + return NULL; + } + + kind = get_token(&ss,&sval); + if ( kind != TK_LEFTPAREN ) { + *code = LDAP_SCHERR_NOLEFTPAREN; + LDAP_FREE(sval); + ldap_structurerule_free(sr); + return NULL; + } + + /* + * Definitions MUST begin with a ruleid. + */ + parse_whsp(&ss); + savepos = ss; + ret = ldap_int_parse_ruleid(&ss,code,0,&sr->sr_ruleid); + if ( ret ) { + *errp = ss; + ldap_structurerule_free(sr); + return NULL; + } + parse_whsp(&ss); + + /* + * Beyond this point we will be liberal an accept the items + * in any order. + */ + while (1) { + kind = get_token(&ss,&sval); + switch (kind) { + case TK_EOS: + *code = LDAP_SCHERR_NORIGHTPAREN; + *errp = EndOfInput; + ldap_structurerule_free(sr); + return NULL; + case TK_RIGHTPAREN: + if( !seen_nameform ) { + *code = LDAP_SCHERR_MISSING; + ldap_structurerule_free(sr); + return NULL; + } + return sr; + case TK_BAREWORD: + if ( !strcasecmp(sval,"NAME") ) { + LDAP_FREE(sval); + if ( seen_name ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_structurerule_free(sr); + return(NULL); + } + seen_name = 1; + sr->sr_names = parse_qdescrs(&ss,code); + if ( !sr->sr_names ) { + if ( *code != LDAP_SCHERR_OUTOFMEM ) + *code = LDAP_SCHERR_BADNAME; + *errp = ss; + ldap_structurerule_free(sr); + return NULL; + } + } else if ( !strcasecmp(sval,"DESC") ) { + LDAP_FREE(sval); + if ( seen_desc ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_structurerule_free(sr); + return(NULL); + } + seen_desc = 1; + parse_whsp(&ss); + kind = get_token(&ss,&sval); + if ( kind != TK_QDSTRING ) { + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_structurerule_free(sr); + return NULL; + } + sr->sr_desc = sval; + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"OBSOLETE") ) { + LDAP_FREE(sval); + if ( seen_obsolete ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_structurerule_free(sr); + return(NULL); + } + seen_obsolete = 1; + sr->sr_obsolete = LDAP_SCHEMA_YES; + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"FORM") ) { + LDAP_FREE(sval); + if ( seen_nameform ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_structurerule_free(sr); + return(NULL); + } + seen_nameform = 1; + sr->sr_nameform = parse_woid(&ss,code); + if ( !sr->sr_nameform ) { + *errp = ss; + ldap_structurerule_free(sr); + return NULL; + } + parse_whsp(&ss); + } else if ( sval[0] == 'X' && sval[1] == '-' ) { + /* Should be parse_qdstrings */ + ext_vals = parse_qdescrs(&ss, code); + if ( !ext_vals ) { + *errp = ss; + ldap_structurerule_free(sr); + return NULL; + } + if ( add_extension(&sr->sr_extensions, + sval, ext_vals) ) { + *code = LDAP_SCHERR_OUTOFMEM; + *errp = ss; + LDAP_FREE(sval); + ldap_structurerule_free(sr); + return NULL; + } + } else { + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_structurerule_free(sr); + return NULL; + } + break; + default: + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_structurerule_free(sr); + return NULL; + } + } +} + +void +ldap_nameform_free(LDAPNameForm * nf) +{ + if (!nf) return; + LDAP_FREE(nf->nf_oid); + if (nf->nf_names) LDAP_VFREE(nf->nf_names); + if (nf->nf_desc) LDAP_FREE(nf->nf_desc); + if (nf->nf_objectclass) LDAP_FREE(nf->nf_objectclass); + if (nf->nf_at_oids_must) LDAP_VFREE(nf->nf_at_oids_must); + if (nf->nf_at_oids_may) LDAP_VFREE(nf->nf_at_oids_may); + free_extensions(nf->nf_extensions); + LDAP_FREE(nf); +} + +LDAPNameForm * +ldap_str2nameform( LDAP_CONST char * s, + int * code, + LDAP_CONST char ** errp, + LDAP_CONST unsigned flags ) +{ + tk_t kind; + const char * ss = s; + char * sval; + int seen_name = 0; + int seen_desc = 0; + int seen_obsolete = 0; + int seen_class = 0; + int seen_must = 0; + int seen_may = 0; + LDAPNameForm * nf; + char ** ext_vals; + const char * savepos; + + if ( !s ) { + *code = LDAP_SCHERR_EMPTY; + *errp = ""; + return NULL; + } + + *errp = s; + nf = LDAP_CALLOC(1,sizeof(LDAPNameForm)); + + if ( !nf ) { + *code = LDAP_SCHERR_OUTOFMEM; + return NULL; + } + + kind = get_token(&ss,&sval); + if ( kind != TK_LEFTPAREN ) { + *code = LDAP_SCHERR_NOLEFTPAREN; + LDAP_FREE(sval); + ldap_nameform_free(nf); + return NULL; + } + + /* + * Definitions MUST begin with an OID in the numericoid format. + * However, this routine is used by clients to parse the response + * from servers and very well known servers will provide an OID + * in the wrong format or even no OID at all. We do our best to + * extract info from those servers. + */ + parse_whsp(&ss); + savepos = ss; + nf->nf_oid = ldap_int_parse_numericoid(&ss,code,0); + if ( !nf->nf_oid ) { + *errp = ss; + ldap_nameform_free(nf); + return NULL; + } + parse_whsp(&ss); + + /* + * Beyond this point we will be liberal an accept the items + * in any order. + */ + while (1) { + kind = get_token(&ss,&sval); + switch (kind) { + case TK_EOS: + *code = LDAP_SCHERR_NORIGHTPAREN; + *errp = EndOfInput; + ldap_nameform_free(nf); + return NULL; + case TK_RIGHTPAREN: + if( !seen_class || !seen_must ) { + *code = LDAP_SCHERR_MISSING; + ldap_nameform_free(nf); + return NULL; + } + return nf; + case TK_BAREWORD: + if ( !strcasecmp(sval,"NAME") ) { + LDAP_FREE(sval); + if ( seen_name ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_nameform_free(nf); + return(NULL); + } + seen_name = 1; + nf->nf_names = parse_qdescrs(&ss,code); + if ( !nf->nf_names ) { + if ( *code != LDAP_SCHERR_OUTOFMEM ) + *code = LDAP_SCHERR_BADNAME; + *errp = ss; + ldap_nameform_free(nf); + return NULL; + } + } else if ( !strcasecmp(sval,"DESC") ) { + LDAP_FREE(sval); + if ( seen_desc ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_nameform_free(nf); + return(NULL); + } + seen_desc = 1; + parse_whsp(&ss); + kind = get_token(&ss,&sval); + if ( kind != TK_QDSTRING ) { + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_nameform_free(nf); + return NULL; + } + nf->nf_desc = sval; + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"OBSOLETE") ) { + LDAP_FREE(sval); + if ( seen_obsolete ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_nameform_free(nf); + return(NULL); + } + seen_obsolete = 1; + nf->nf_obsolete = LDAP_SCHEMA_YES; + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"OC") ) { + LDAP_FREE(sval); + if ( seen_class ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_nameform_free(nf); + return(NULL); + } + seen_class = 1; + nf->nf_objectclass = parse_woid(&ss,code); + if ( !nf->nf_objectclass ) { + *errp = ss; + ldap_nameform_free(nf); + return NULL; + } + } else if ( !strcasecmp(sval,"MUST") ) { + LDAP_FREE(sval); + if ( seen_must ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_nameform_free(nf); + return(NULL); + } + seen_must = 1; + nf->nf_at_oids_must = parse_oids(&ss,code,0); + if ( !nf->nf_at_oids_must && *code != LDAP_SUCCESS ) { + *errp = ss; + ldap_nameform_free(nf); + return NULL; + } + parse_whsp(&ss); + } else if ( !strcasecmp(sval,"MAY") ) { + LDAP_FREE(sval); + if ( seen_may ) { + *code = LDAP_SCHERR_DUPOPT; + *errp = ss; + ldap_nameform_free(nf); + return(NULL); + } + seen_may = 1; + nf->nf_at_oids_may = parse_oids(&ss,code,0); + if ( !nf->nf_at_oids_may && *code != LDAP_SUCCESS ) { + *errp = ss; + ldap_nameform_free(nf); + return NULL; + } + parse_whsp(&ss); + } else if ( sval[0] == 'X' && sval[1] == '-' ) { + /* Should be parse_qdstrings */ + ext_vals = parse_qdescrs(&ss, code); + if ( !ext_vals ) { + *errp = ss; + ldap_nameform_free(nf); + return NULL; + } + if ( add_extension(&nf->nf_extensions, + sval, ext_vals) ) { + *code = LDAP_SCHERR_OUTOFMEM; + *errp = ss; + LDAP_FREE(sval); + ldap_nameform_free(nf); + return NULL; + } + } else { + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_nameform_free(nf); + return NULL; + } + break; + default: + *code = LDAP_SCHERR_UNEXPTOKEN; + *errp = ss; + LDAP_FREE(sval); + ldap_nameform_free(nf); + return NULL; + } + } +} + +static char *const err2text[] = { + N_("Success"), + N_("Out of memory"), + N_("Unexpected token"), + N_("Missing opening parenthesis"), + N_("Missing closing parenthesis"), + N_("Expecting digit"), + N_("Expecting a name"), + N_("Bad description"), + N_("Bad superiors"), + N_("Duplicate option"), + N_("Unexpected end of data"), + N_("Missing required field"), + N_("Out of order field") +}; + +char * +ldap_scherr2str(int code) +{ + if ( code < 0 || code >= (int)(sizeof(err2text)/sizeof(char *)) ) { + return _("Unknown error"); + } else { + return _(err2text[code]); + } +} diff --git a/libraries/libldap/search.c b/libraries/libldap/search.c new file mode 100644 index 0000000..a148fb5 --- /dev/null +++ b/libraries/libldap/search.c @@ -0,0 +1,545 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" +#include "ldap_log.h" + +/* + * ldap_search_ext - initiate an ldap search operation. + * + * Parameters: + * + * ld LDAP descriptor + * base DN of the base object + * scope the search scope - one of + * LDAP_SCOPE_BASE (baseObject), + * LDAP_SCOPE_ONELEVEL (oneLevel), + * LDAP_SCOPE_SUBTREE (subtree), or + * LDAP_SCOPE_SUBORDINATE (children) -- OpenLDAP extension + * filter a string containing the search filter + * (e.g., "(|(cn=bob)(sn=bob))") + * attrs list of attribute types to return for matches + * attrsonly 1 => attributes only 0 => attributes and values + * + * Example: + * char *attrs[] = { "mail", "title", 0 }; + * ldap_search_ext( ld, "dc=example,dc=com", LDAP_SCOPE_SUBTREE, "cn~=bob", + * attrs, attrsonly, sctrls, ctrls, timeout, sizelimit, + * &msgid ); + */ +int +ldap_search_ext( + LDAP *ld, + LDAP_CONST char *base, + int scope, + LDAP_CONST char *filter, + char **attrs, + int attrsonly, + LDAPControl **sctrls, + LDAPControl **cctrls, + struct timeval *timeout, + int sizelimit, + int *msgidp ) +{ + return ldap_pvt_search( ld, base, scope, filter, attrs, + attrsonly, sctrls, cctrls, timeout, sizelimit, -1, msgidp ); +} + +int +ldap_pvt_search( + LDAP *ld, + LDAP_CONST char *base, + int scope, + LDAP_CONST char *filter, + char **attrs, + int attrsonly, + LDAPControl **sctrls, + LDAPControl **cctrls, + struct timeval *timeout, + int sizelimit, + int deref, + int *msgidp ) +{ + int rc; + BerElement *ber; + int timelimit; + ber_int_t id; + + Debug( LDAP_DEBUG_TRACE, "ldap_search_ext\n", 0, 0, 0 ); + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + + /* check client controls */ + rc = ldap_int_client_controls( ld, cctrls ); + if( rc != LDAP_SUCCESS ) return rc; + + /* + * if timeout is provided, both tv_sec and tv_usec must + * not be zero + */ + if( timeout != NULL ) { + if( timeout->tv_sec == 0 && timeout->tv_usec == 0 ) { + return LDAP_PARAM_ERROR; + } + + /* timelimit must be non-zero if timeout is provided */ + timelimit = timeout->tv_sec != 0 ? timeout->tv_sec : 1; + + } else { + /* no timeout, no timelimit */ + timelimit = -1; + } + + ber = ldap_build_search_req( ld, base, scope, filter, attrs, + attrsonly, sctrls, cctrls, timelimit, sizelimit, deref, &id ); + + if ( ber == NULL ) { + return ld->ld_errno; + } + + + /* send the message */ + *msgidp = ldap_send_initial_request( ld, LDAP_REQ_SEARCH, base, ber, id ); + + if( *msgidp < 0 ) + return ld->ld_errno; + + return LDAP_SUCCESS; +} + +int +ldap_search_ext_s( + LDAP *ld, + LDAP_CONST char *base, + int scope, + LDAP_CONST char *filter, + char **attrs, + int attrsonly, + LDAPControl **sctrls, + LDAPControl **cctrls, + struct timeval *timeout, + int sizelimit, + LDAPMessage **res ) +{ + return ldap_pvt_search_s( ld, base, scope, filter, attrs, + attrsonly, sctrls, cctrls, timeout, sizelimit, -1, res ); +} + +int +ldap_pvt_search_s( + LDAP *ld, + LDAP_CONST char *base, + int scope, + LDAP_CONST char *filter, + char **attrs, + int attrsonly, + LDAPControl **sctrls, + LDAPControl **cctrls, + struct timeval *timeout, + int sizelimit, + int deref, + LDAPMessage **res ) +{ + int rc; + int msgid; + + *res = NULL; + + rc = ldap_pvt_search( ld, base, scope, filter, attrs, attrsonly, + sctrls, cctrls, timeout, sizelimit, deref, &msgid ); + + if ( rc != LDAP_SUCCESS ) { + return( rc ); + } + + rc = ldap_result( ld, msgid, LDAP_MSG_ALL, timeout, res ); + + if( rc <= 0 ) { + /* error(-1) or timeout(0) */ + if ( ld->ld_errno == LDAP_TIMEOUT ) { + /* cleanup request */ + (void) ldap_abandon( ld, msgid ); + ld->ld_errno = LDAP_TIMEOUT; + } + return( ld->ld_errno ); + } + + if( rc == LDAP_RES_SEARCH_REFERENCE || rc == LDAP_RES_INTERMEDIATE ) { + return( ld->ld_errno ); + } + + return( ldap_result2error( ld, *res, 0 ) ); +} + +/* + * ldap_search - initiate an ldap search operation. + * + * Parameters: + * + * ld LDAP descriptor + * base DN of the base object + * scope the search scope - one of + * LDAP_SCOPE_BASE (baseObject), + * LDAP_SCOPE_ONELEVEL (oneLevel), + * LDAP_SCOPE_SUBTREE (subtree), or + * LDAP_SCOPE_SUBORDINATE (children) -- OpenLDAP extension + * filter a string containing the search filter + * (e.g., "(|(cn=bob)(sn=bob))") + * attrs list of attribute types to return for matches + * attrsonly 1 => attributes only 0 => attributes and values + * + * Example: + * char *attrs[] = { "mail", "title", 0 }; + * msgid = ldap_search( ld, "dc=example,dc=com", LDAP_SCOPE_SUBTREE, "cn~=bob", + * attrs, attrsonly ); + */ +int +ldap_search( + LDAP *ld, LDAP_CONST char *base, int scope, LDAP_CONST char *filter, + char **attrs, int attrsonly ) +{ + BerElement *ber; + ber_int_t id; + + Debug( LDAP_DEBUG_TRACE, "ldap_search\n", 0, 0, 0 ); + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + + ber = ldap_build_search_req( ld, base, scope, filter, attrs, + attrsonly, NULL, NULL, -1, -1, -1, &id ); + + if ( ber == NULL ) { + return( -1 ); + } + + + /* send the message */ + return ( ldap_send_initial_request( ld, LDAP_REQ_SEARCH, base, ber, id )); +} + + +BerElement * +ldap_build_search_req( + LDAP *ld, + LDAP_CONST char *base, + ber_int_t scope, + LDAP_CONST char *filter, + char **attrs, + ber_int_t attrsonly, + LDAPControl **sctrls, + LDAPControl **cctrls, + ber_int_t timelimit, + ber_int_t sizelimit, + ber_int_t deref, + ber_int_t *idp) +{ + BerElement *ber; + int err; + + /* + * Create the search request. It looks like this: + * SearchRequest := [APPLICATION 3] SEQUENCE { + * baseObject DistinguishedName, + * scope ENUMERATED { + * baseObject (0), + * singleLevel (1), + * wholeSubtree (2) + * }, + * derefAliases ENUMERATED { + * neverDerefaliases (0), + * derefInSearching (1), + * derefFindingBaseObj (2), + * alwaysDerefAliases (3) + * }, + * sizelimit INTEGER (0 .. 65535), + * timelimit INTEGER (0 .. 65535), + * attrsOnly BOOLEAN, + * filter Filter, + * attributes SEQUENCE OF AttributeType + * } + * wrapped in an ldap message. + */ + + /* create a message to send */ + if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) { + return( NULL ); + } + + if ( base == NULL ) { + /* no base provided, use session default base */ + base = ld->ld_options.ldo_defbase; + + if ( base == NULL ) { + /* no session default base, use top */ + base = ""; + } + } + + LDAP_NEXT_MSGID( ld, *idp ); +#ifdef LDAP_CONNECTIONLESS + if ( LDAP_IS_UDP(ld) ) { + struct sockaddr_storage sa = {0}; + /* dummy, filled with ldo_peer in request.c */ + err = ber_write( ber, (char *) &sa, sizeof( sa ), 0 ); + } + if ( LDAP_IS_UDP(ld) && ld->ld_options.ldo_version == LDAP_VERSION2) { + char *dn = ld->ld_options.ldo_cldapdn; + if (!dn) dn = ""; + err = ber_printf( ber, "{ist{seeiib", *idp, dn, + LDAP_REQ_SEARCH, base, (ber_int_t) scope, + (deref < 0) ? ld->ld_deref : deref, + (sizelimit < 0) ? ld->ld_sizelimit : sizelimit, + (timelimit < 0) ? ld->ld_timelimit : timelimit, + attrsonly ); + } else +#endif + { + err = ber_printf( ber, "{it{seeiib", *idp, + LDAP_REQ_SEARCH, base, (ber_int_t) scope, + (deref < 0) ? ld->ld_deref : deref, + (sizelimit < 0) ? ld->ld_sizelimit : sizelimit, + (timelimit < 0) ? ld->ld_timelimit : timelimit, + attrsonly ); + } + + if ( err == -1 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return( NULL ); + } + + if( filter == NULL ) { + filter = "(objectclass=*)"; + } + + err = ldap_pvt_put_filter( ber, filter ); + + if ( err == -1 ) { + ld->ld_errno = LDAP_FILTER_ERROR; + ber_free( ber, 1 ); + return( NULL ); + } + +#ifdef LDAP_DEBUG + if ( ldap_debug & LDAP_DEBUG_ARGS ) { + char buf[ BUFSIZ ], *ptr = " *"; + + if ( attrs != NULL ) { + int i, len, rest = sizeof( buf ); + + for ( i = 0; attrs[ i ] != NULL && rest > 0; i++ ) { + ptr = &buf[ sizeof( buf ) - rest ]; + len = snprintf( ptr, rest, " %s", attrs[ i ] ); + rest -= (len >= 0 ? len : (int) sizeof( buf )); + } + + if ( rest <= 0 ) { + AC_MEMCPY( &buf[ sizeof( buf ) - STRLENOF( "...(truncated)" ) - 1 ], + "...(truncated)", STRLENOF( "...(truncated)" ) + 1 ); + } + ptr = buf; + } + + Debug( LDAP_DEBUG_ARGS, "ldap_build_search_req ATTRS:%s\n", ptr, 0,0 ); + } +#endif /* LDAP_DEBUG */ + + if ( ber_printf( ber, /*{*/ "{v}N}", attrs ) == -1 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return( NULL ); + } + + /* Put Server Controls */ + if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) { + ber_free( ber, 1 ); + return( NULL ); + } + + if ( ber_printf( ber, /*{*/ "N}" ) == -1 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return( NULL ); + } + + return( ber ); +} + +int +ldap_search_st( + LDAP *ld, LDAP_CONST char *base, int scope, + LDAP_CONST char *filter, char **attrs, + int attrsonly, struct timeval *timeout, LDAPMessage **res ) +{ + int msgid; + + *res = NULL; + + if ( (msgid = ldap_search( ld, base, scope, filter, attrs, attrsonly )) + == -1 ) + return( ld->ld_errno ); + + if ( ldap_result( ld, msgid, LDAP_MSG_ALL, timeout, res ) == -1 || !*res ) + return( ld->ld_errno ); + + if ( ld->ld_errno == LDAP_TIMEOUT ) { + (void) ldap_abandon( ld, msgid ); + ld->ld_errno = LDAP_TIMEOUT; + return( ld->ld_errno ); + } + + return( ldap_result2error( ld, *res, 0 ) ); +} + +int +ldap_search_s( + LDAP *ld, + LDAP_CONST char *base, + int scope, + LDAP_CONST char *filter, + char **attrs, + int attrsonly, + LDAPMessage **res ) +{ + int msgid; + + *res = NULL; + + if ( (msgid = ldap_search( ld, base, scope, filter, attrs, attrsonly )) + == -1 ) + return( ld->ld_errno ); + + if ( ldap_result( ld, msgid, LDAP_MSG_ALL, (struct timeval *) NULL, res ) == -1 || !*res ) + return( ld->ld_errno ); + + return( ldap_result2error( ld, *res, 0 ) ); +} + +static char escape[128] = { + 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, + + 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1 +}; +#define NEEDFLTESCAPE(c) ((c) & 0x80 || escape[ (unsigned)(c) ]) + +/* + * compute the length of the escaped value + */ +ber_len_t +ldap_bv2escaped_filter_value_len( struct berval *in ) +{ + ber_len_t i, l; + + assert( in != NULL ); + + if ( in->bv_len == 0 ) { + return 0; + } + + for( l = 0, i = 0; i < in->bv_len; l++, i++ ) { + char c = in->bv_val[ i ]; + if ( NEEDFLTESCAPE( c ) ) { + l += 2; + } + } + + return l; +} + +int +ldap_bv2escaped_filter_value( struct berval *in, struct berval *out ) +{ + return ldap_bv2escaped_filter_value_x( in, out, 0, NULL ); +} + +int +ldap_bv2escaped_filter_value_x( struct berval *in, struct berval *out, int inplace, void *ctx ) +{ + ber_len_t i, l; + + assert( in != NULL ); + assert( out != NULL ); + + BER_BVZERO( out ); + + if ( in->bv_len == 0 ) { + return 0; + } + + /* assume we'll escape everything */ + l = ldap_bv2escaped_filter_value_len( in ); + if ( l == in->bv_len ) { + if ( inplace ) { + *out = *in; + } else { + ber_dupbv( out, in ); + } + return 0; + } + out->bv_val = LDAP_MALLOCX( l + 1, ctx ); + if ( out->bv_val == NULL ) { + return -1; + } + + for ( i = 0; i < in->bv_len; i++ ) { + char c = in->bv_val[ i ]; + if ( NEEDFLTESCAPE( c ) ) { + assert( out->bv_len < l - 2 ); + out->bv_val[out->bv_len++] = '\\'; + out->bv_val[out->bv_len++] = "0123456789ABCDEF"[0x0f & (c>>4)]; + out->bv_val[out->bv_len++] = "0123456789ABCDEF"[0x0f & c]; + + } else { + assert( out->bv_len < l ); + out->bv_val[out->bv_len++] = c; + } + } + + out->bv_val[out->bv_len] = '\0'; + + return 0; +} + diff --git a/libraries/libldap/sort.c b/libraries/libldap/sort.c new file mode 100644 index 0000000..27ea682 --- /dev/null +++ b/libraries/libldap/sort.c @@ -0,0 +1,183 @@ +/* sort.c -- LDAP library entry and value sort routines */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1994 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/stdlib.h> + +#include <ac/ctype.h> +#include <ac/string.h> +#include <ac/time.h> + + +#include "ldap-int.h" + +struct entrything { + char **et_vals; + LDAPMessage *et_msg; + int (*et_cmp_fn) LDAP_P((const char *a, const char *b)); +}; + +static int et_cmp LDAP_P(( const void *aa, const void *bb)); + + +int +ldap_sort_strcasecmp( + LDAP_CONST void *a, + LDAP_CONST void *b +) +{ + return( strcasecmp( *(char *const *)a, *(char *const *)b ) ); +} + +static int +et_cmp( + const void *aa, + const void *bb +) +{ + int i, rc; + const struct entrything *a = (const struct entrything *)aa; + const struct entrything *b = (const struct entrything *)bb; + + if ( a->et_vals == NULL && b->et_vals == NULL ) + return( 0 ); + if ( a->et_vals == NULL ) + return( -1 ); + if ( b->et_vals == NULL ) + return( 1 ); + + for ( i = 0; a->et_vals[i] && b->et_vals[i]; i++ ) { + if ( (rc = a->et_cmp_fn( a->et_vals[i], b->et_vals[i] )) != 0 ) { + return( rc ); + } + } + + if ( a->et_vals[i] == NULL && b->et_vals[i] == NULL ) + return( 0 ); + if ( a->et_vals[i] == NULL ) + return( -1 ); + return( 1 ); +} + +int +ldap_sort_entries( + LDAP *ld, + LDAPMessage **chain, + LDAP_CONST char *attr, /* NULL => sort by DN */ + int (*cmp) (LDAP_CONST char *, LDAP_CONST char *) +) +{ + int i, count = 0; + struct entrything *et; + LDAPMessage *e, *ehead = NULL, *etail = NULL; + LDAPMessage *ohead = NULL, *otail = NULL; + LDAPMessage **ep; + + assert( ld != NULL ); + + /* Separate entries from non-entries */ + for ( e = *chain; e; e=e->lm_chain ) { + if ( e->lm_msgtype == LDAP_RES_SEARCH_ENTRY ) { + count++; + if ( !ehead ) ehead = e; + if ( etail ) etail->lm_chain = e; + etail = e; + } else { + if ( !ohead ) ohead = e; + if ( otail ) otail->lm_chain = e; + otail = e; + } + } + + if ( count < 2 ) { + /* zero or one entries -- already sorted! */ + if ( ehead ) { + etail->lm_chain = ohead; + *chain = ehead; + } else { + *chain = ohead; + } + return 0; + } + + if ( (et = (struct entrything *) LDAP_MALLOC( count * + sizeof(struct entrything) )) == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + return( -1 ); + } + + e = ehead; + for ( i = 0; i < count; i++ ) { + et[i].et_cmp_fn = cmp; + et[i].et_msg = e; + if ( attr == NULL ) { + char *dn; + + dn = ldap_get_dn( ld, e ); + et[i].et_vals = ldap_explode_dn( dn, 1 ); + LDAP_FREE( dn ); + } else { + et[i].et_vals = ldap_get_values( ld, e, attr ); + } + + e = e->lm_chain; + } + + qsort( et, count, sizeof(struct entrything), et_cmp ); + + ep = chain; + for ( i = 0; i < count; i++ ) { + *ep = et[i].et_msg; + ep = &(*ep)->lm_chain; + + LDAP_VFREE( et[i].et_vals ); + } + *ep = ohead; + (*chain)->lm_chain_tail = otail ? otail : etail; + + LDAP_FREE( (char *) et ); + + return( 0 ); +} + +int +ldap_sort_values( + LDAP *ld, + char **vals, + int (*cmp) (LDAP_CONST void *, LDAP_CONST void *) +) +{ + int nel; + + for ( nel = 0; vals[nel] != NULL; nel++ ) + ; /* NULL */ + + qsort( vals, nel, sizeof(char *), cmp ); + + return( 0 ); +} diff --git a/libraries/libldap/sortctrl.c b/libraries/libldap/sortctrl.c new file mode 100644 index 0000000..fb79555 --- /dev/null +++ b/libraries/libldap/sortctrl.c @@ -0,0 +1,552 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved. + * + * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND + * TREATIES. USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT + * TO VERSION 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS + * AVAILABLE AT HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE" + * IN THE TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION + * OF THIS WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP + * PUBLIC LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT + * THE PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY. + */ +/* Note: A verbatim copy of version 2.0.1 of the OpenLDAP Public License + * can be found in the file "build/LICENSE-2.0.1" in this distribution + * of OpenLDAP Software. + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +#define LDAP_MATCHRULE_IDENTIFIER 0x80L +#define LDAP_REVERSEORDER_IDENTIFIER 0x81L +#define LDAP_ATTRTYPES_IDENTIFIER 0x80L + + + +/* --------------------------------------------------------------------------- + countKeys + + Internal function to determine the number of keys in the string. + + keyString (IN) String of items separated by whitespace. + ---------------------------------------------------------------------------*/ + +static int countKeys(char *keyString) +{ + char *p = keyString; + int count = 0; + + for (;;) + { + while (LDAP_SPACE(*p)) /* Skip leading whitespace */ + p++; + + if (*p == '\0') /* End of string? */ + return count; + + count++; /* Found start of a key */ + + while (!LDAP_SPACE(*p)) /* Skip till next space or end of string. */ + if (*p++ == '\0') + return count; + } +} + + +/* --------------------------------------------------------------------------- + readNextKey + + Internal function to parse the next sort key in the string. + Allocate an LDAPSortKey structure and initialize it with + attribute name, reverse flag, and matching rule OID. + + Each sort key in the string has the format: + [whitespace][-]attribute[:[OID]] + + pNextKey (IN/OUT) Points to the next key in the sortkey string to parse. + The pointer is updated to point to the next character + after the sortkey being parsed. + + key (OUT) Points to the address of an LDAPSortKey stucture + which has been allocated by this routine and + initialized with information from the next sortkey. + ---------------------------------------------------------------------------*/ + +static int readNextKey( char **pNextKey, LDAPSortKey **key) +{ + char *p = *pNextKey; + int rev = 0; + char *attrStart; + int attrLen; + char *oidStart = NULL; + int oidLen = 0; + + /* Skip leading white space. */ + while (LDAP_SPACE(*p)) + p++; + + if (*p == '-') /* Check if the reverse flag is present. */ + { + rev=1; + p++; + } + + /* We're now positioned at the start of the attribute. */ + attrStart = p; + + /* Get the length of the attribute until the next whitespace or ":". */ + attrLen = strcspn(p, " \t:"); + p += attrLen; + + if (attrLen == 0) /* If no attribute name was present, quit. */ + return LDAP_PARAM_ERROR; + + if (*p == ':') + { + oidStart = ++p; /* Start of the OID, after the colon */ + oidLen = strcspn(p, " \t"); /* Get length of OID till next whitespace */ + p += oidLen; + } + + *pNextKey = p; /* Update argument to point to next key */ + + /* Allocate an LDAPSortKey structure */ + *key = LDAP_MALLOC(sizeof(LDAPSortKey)); + if (*key == NULL) return LDAP_NO_MEMORY; + + /* Allocate memory for the attribute and copy to it. */ + (*key)->attributeType = LDAP_MALLOC(attrLen+1); + if ((*key)->attributeType == NULL) { + LDAP_FREE(*key); + return LDAP_NO_MEMORY; + } + + strncpy((*key)->attributeType, attrStart, attrLen); + (*key)->attributeType[attrLen] = 0; + + /* If present, allocate memory for the OID and copy to it. */ + if (oidLen) { + (*key)->orderingRule = LDAP_MALLOC(oidLen+1); + if ((*key)->orderingRule == NULL) { + LDAP_FREE((*key)->attributeType); + LDAP_FREE(*key); + return LDAP_NO_MEMORY; + } + strncpy((*key)->orderingRule, oidStart, oidLen); + (*key)->orderingRule[oidLen] = 0; + + } else { + (*key)->orderingRule = NULL; + } + + (*key)->reverseOrder = rev; + + return LDAP_SUCCESS; +} + + +/* --------------------------------------------------------------------------- + ldap_create_sort_keylist + + Create an array of pointers to LDAPSortKey structures, containing the + information specified by the string representation of one or more + sort keys. + + sortKeyList (OUT) Points to a null-terminated array of pointers to + LDAPSortKey structures allocated by this routine. + This memory SHOULD be freed by the calling program + using ldap_free_sort_keylist(). + + keyString (IN) Points to a string of one or more sort keys. + + ---------------------------------------------------------------------------*/ + +int +ldap_create_sort_keylist ( LDAPSortKey ***sortKeyList, char *keyString ) +{ + int numKeys, rc, i; + char *nextKey; + LDAPSortKey **keyList = NULL; + + assert( sortKeyList != NULL ); + assert( keyString != NULL ); + + *sortKeyList = NULL; + + /* Determine the number of sort keys so we can allocate memory. */ + if (( numKeys = countKeys(keyString)) == 0) { + return LDAP_PARAM_ERROR; + } + + /* Allocate the array of pointers. Initialize to NULL. */ + keyList=(LDAPSortKey**)LBER_CALLOC(numKeys+1, sizeof(LDAPSortKey*)); + if ( keyList == NULL) return LDAP_NO_MEMORY; + + /* For each sort key in the string, create an LDAPSortKey structure + and add it to the list. + */ + nextKey = keyString; /* Points to the next key in the string */ + for (i=0; i < numKeys; i++) { + rc = readNextKey(&nextKey, &keyList[i]); + + if (rc != LDAP_SUCCESS) { + ldap_free_sort_keylist(keyList); + return rc; + } + } + + *sortKeyList = keyList; + return LDAP_SUCCESS; +} + + +/* --------------------------------------------------------------------------- + ldap_free_sort_keylist + + Frees the sort key structures created by ldap_create_sort_keylist(). + Frees the memory referenced by the LDAPSortKey structures, + the LDAPSortKey structures themselves, and the array of pointers + to the structures. + + keyList (IN) Points to an array of pointers to LDAPSortKey structures. + ---------------------------------------------------------------------------*/ + +void +ldap_free_sort_keylist ( LDAPSortKey **keyList ) +{ + int i; + LDAPSortKey *nextKeyp; + + if (keyList == NULL) return; + + i=0; + while ( 0 != (nextKeyp = keyList[i++]) ) { + if (nextKeyp->attributeType) { + LBER_FREE(nextKeyp->attributeType); + } + + if (nextKeyp->orderingRule != NULL) { + LBER_FREE(nextKeyp->orderingRule); + } + + LBER_FREE(nextKeyp); + } + + LBER_FREE(keyList); +} + + +/* --------------------------------------------------------------------------- + ldap_create_sort_control_value + + Create and encode the value of the server-side sort control. + + ld (IN) An LDAP session handle, as obtained from a call to + ldap_init(). + + keyList (IN) Points to a null-terminated array of pointers to + LDAPSortKey structures, containing a description of + each of the sort keys to be used. The description + consists of an attribute name, ascending/descending flag, + and an optional matching rule (OID) to use. + + value (OUT) Contains the control value; the bv_val member of the berval structure + SHOULD be freed by calling ldap_memfree() when done. + + + Ber encoding + + SortKeyList ::= SEQUENCE OF SEQUENCE { + attributeType AttributeDescription, + orderingRule [0] MatchingRuleId OPTIONAL, + reverseOrder [1] BOOLEAN DEFAULT FALSE } + + ---------------------------------------------------------------------------*/ + +int +ldap_create_sort_control_value( + LDAP *ld, + LDAPSortKey **keyList, + struct berval *value ) +{ + int i; + BerElement *ber = NULL; + ber_tag_t tag; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + + if ( ld == NULL ) return LDAP_PARAM_ERROR; + if ( keyList == NULL || value == NULL ) { + ld->ld_errno = LDAP_PARAM_ERROR; + return LDAP_PARAM_ERROR; + } + + value->bv_val = NULL; + value->bv_len = 0; + ld->ld_errno = LDAP_SUCCESS; + + ber = ldap_alloc_ber_with_options( ld ); + if ( ber == NULL) { + ld->ld_errno = LDAP_NO_MEMORY; + return ld->ld_errno; + } + + tag = ber_printf( ber, "{" /*}*/ ); + if ( tag == LBER_ERROR ) { + goto error_return; + } + + for ( i = 0; keyList[i] != NULL; i++ ) { + tag = ber_printf( ber, "{s" /*}*/, keyList[i]->attributeType ); + if ( tag == LBER_ERROR ) { + goto error_return; + } + + if ( keyList[i]->orderingRule != NULL ) { + tag = ber_printf( ber, "ts", + LDAP_MATCHRULE_IDENTIFIER, + keyList[i]->orderingRule ); + + if ( tag == LBER_ERROR ) { + goto error_return; + } + } + + if ( keyList[i]->reverseOrder ) { + tag = ber_printf( ber, "tb", + LDAP_REVERSEORDER_IDENTIFIER, + keyList[i]->reverseOrder ); + + if ( tag == LBER_ERROR ) { + goto error_return; + } + } + + tag = ber_printf( ber, /*{*/ "N}" ); + if ( tag == LBER_ERROR ) { + goto error_return; + } + } + + tag = ber_printf( ber, /*{*/ "N}" ); + if ( tag == LBER_ERROR ) { + goto error_return; + } + + if ( ber_flatten2( ber, value, 1 ) == -1 ) { + ld->ld_errno = LDAP_NO_MEMORY; + } + + if ( 0 ) { +error_return:; + ld->ld_errno = LDAP_ENCODING_ERROR; + } + + if ( ber != NULL ) { + ber_free( ber, 1 ); + } + + return ld->ld_errno; +} + + +/* --------------------------------------------------------------------------- + ldap_create_sort_control + + Create and encode the server-side sort control. + + ld (IN) An LDAP session handle, as obtained from a call to + ldap_init(). + + keyList (IN) Points to a null-terminated array of pointers to + LDAPSortKey structures, containing a description of + each of the sort keys to be used. The description + consists of an attribute name, ascending/descending flag, + and an optional matching rule (OID) to use. + + isCritical (IN) 0 - Indicates the control is not critical to the operation. + non-zero - The control is critical to the operation. + + ctrlp (OUT) Returns a pointer to the LDAPControl created. This control + SHOULD be freed by calling ldap_control_free() when done. + + + Ber encoding + + SortKeyList ::= SEQUENCE OF SEQUENCE { + attributeType AttributeDescription, + orderingRule [0] MatchingRuleId OPTIONAL, + reverseOrder [1] BOOLEAN DEFAULT FALSE } + + ---------------------------------------------------------------------------*/ + +int +ldap_create_sort_control( + LDAP *ld, + LDAPSortKey **keyList, + int isCritical, + LDAPControl **ctrlp ) +{ + struct berval value; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + + if ( ld == NULL ) { + return LDAP_PARAM_ERROR; + } + + if ( ctrlp == NULL ) { + ld->ld_errno = LDAP_PARAM_ERROR; + return ld->ld_errno; + } + + ld->ld_errno = ldap_create_sort_control_value( ld, keyList, &value ); + if ( ld->ld_errno == LDAP_SUCCESS ) { + ld->ld_errno = ldap_control_create( LDAP_CONTROL_SORTREQUEST, + isCritical, &value, 0, ctrlp ); + if ( ld->ld_errno != LDAP_SUCCESS ) { + LDAP_FREE( value.bv_val ); + } + } + + return ld->ld_errno; +} + + +/* --------------------------------------------------------------------------- + ldap_parse_sortedresult_control + + Decode the server-side sort control return information. + + ld (IN) An LDAP session handle, as obtained from a call to + ldap_init(). + + ctrl (IN) The address of the LDAP Control Structure. + + returnCode (OUT) This result parameter is filled in with the sort control + result code. This parameter MUST not be NULL. + + attribute (OUT) If an error occured the server may return a string + indicating the first attribute in the sortkey list + that was in error. If a string is returned, the memory + should be freed with ldap_memfree. If this parameter is + NULL, no string is returned. + + + Ber encoding for sort control + + SortResult ::= SEQUENCE { + sortResult ENUMERATED { + success (0), -- results are sorted + operationsError (1), -- server internal failure + timeLimitExceeded (3), -- timelimit reached before + -- sorting was completed + strongAuthRequired (8), -- refused to return sorted + -- results via insecure + -- protocol + adminLimitExceeded (11), -- too many matching entries + -- for the server to sort + noSuchAttribute (16), -- unrecognized attribute + -- type in sort key + inappropriateMatching (18), -- unrecognized or inappro- + -- priate matching rule in + -- sort key + insufficientAccessRights (50), -- refused to return sorted + -- results to this client + busy (51), -- too busy to process + unwillingToPerform (53), -- unable to sort + other (80) + }, + attributeType [0] AttributeDescription OPTIONAL } + ---------------------------------------------------------------------------*/ + +int +ldap_parse_sortresponse_control( + LDAP *ld, + LDAPControl *ctrl, + ber_int_t *returnCode, + char **attribute ) +{ + BerElement *ber; + ber_tag_t tag, berTag; + ber_len_t berLen; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + + if (ld == NULL) { + return LDAP_PARAM_ERROR; + } + + if (ctrl == NULL) { + ld->ld_errno = LDAP_PARAM_ERROR; + return(ld->ld_errno); + } + + if (attribute) { + *attribute = NULL; + } + + if ( strcmp(LDAP_CONTROL_SORTRESPONSE, ctrl->ldctl_oid) != 0 ) { + /* Not sort result control */ + ld->ld_errno = LDAP_CONTROL_NOT_FOUND; + return(ld->ld_errno); + } + + /* Create a BerElement from the berval returned in the control. */ + ber = ber_init(&ctrl->ldctl_value); + + if (ber == NULL) { + ld->ld_errno = LDAP_NO_MEMORY; + return(ld->ld_errno); + } + + /* Extract the result code from the control. */ + tag = ber_scanf(ber, "{e" /*}*/, returnCode); + + if( tag == LBER_ERROR ) { + ber_free(ber, 1); + ld->ld_errno = LDAP_DECODING_ERROR; + return(ld->ld_errno); + } + + /* If caller wants the attribute name, and if it's present in the control, + extract the attribute name which caused the error. */ + if (attribute && (LDAP_ATTRTYPES_IDENTIFIER == ber_peek_tag(ber, &berLen))) + { + tag = ber_scanf(ber, "ta", &berTag, attribute); + + if (tag == LBER_ERROR ) { + ber_free(ber, 1); + ld->ld_errno = LDAP_DECODING_ERROR; + return(ld->ld_errno); + } + } + + ber_free(ber,1); + + ld->ld_errno = LDAP_SUCCESS; + return(ld->ld_errno); +} diff --git a/libraries/libldap/stctrl.c b/libraries/libldap/stctrl.c new file mode 100644 index 0000000..8d2d06a --- /dev/null +++ b/libraries/libldap/stctrl.c @@ -0,0 +1,302 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * Portions Copyright 2007 Pierangelo Masarati. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: + * This work was developed by Pierangelo Masarati for inclusion in + * OpenLDAP Software. + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +#ifdef LDAP_CONTROL_X_SESSION_TRACKING + +/* + * Client-side of <draft-wahl-ldap-session-03> + */ + +int +ldap_create_session_tracking_value( + LDAP *ld, + char *sessionSourceIp, + char *sessionSourceName, + char *formatOID, + struct berval *sessionTrackingIdentifier, + struct berval *value ) +{ + BerElement *ber = NULL; + ber_tag_t tag; + + struct berval ip, name, oid, id; + + if ( ld == NULL || + formatOID == NULL || + value == NULL ) + { +param_error:; + if ( ld ) { + ld->ld_errno = LDAP_PARAM_ERROR; + } + + return LDAP_PARAM_ERROR; + } + + assert( LDAP_VALID( ld ) ); + ld->ld_errno = LDAP_SUCCESS; + + /* check sizes according to I.D. */ + if ( sessionSourceIp == NULL ) { + BER_BVSTR( &ip, "" ); + + } else { + ber_str2bv( sessionSourceIp, 0, 0, &ip ); + /* NOTE: we're strict because we don't want + * to send out bad data */ + if ( ip.bv_len > 128 ) goto param_error; + } + + if ( sessionSourceName == NULL ) { + BER_BVSTR( &name, "" ); + + } else { + ber_str2bv( sessionSourceName, 0, 0, &name ); + /* NOTE: we're strict because we don't want + * to send out bad data */ + if ( name.bv_len > 65536 ) goto param_error; + } + + ber_str2bv( formatOID, 0, 0, &oid ); + /* NOTE: we're strict because we don't want + * to send out bad data */ + if ( oid.bv_len > 1024 ) goto param_error; + + if ( sessionTrackingIdentifier == NULL || + sessionTrackingIdentifier->bv_val == NULL ) + { + BER_BVSTR( &id, "" ); + + } else { + id = *sessionTrackingIdentifier; + } + + /* prepare value */ + value->bv_val = NULL; + value->bv_len = 0; + + ber = ldap_alloc_ber_with_options( ld ); + if ( ber == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + return ld->ld_errno; + } + + tag = ber_printf( ber, "{OOOO}", &ip, &name, &oid, &id ); + if ( tag == LBER_ERROR ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + goto done; + } + + if ( ber_flatten2( ber, value, 1 ) == -1 ) { + ld->ld_errno = LDAP_NO_MEMORY; + } + +done:; + if ( ber != NULL ) { + ber_free( ber, 1 ); + } + + return ld->ld_errno; +} + +/* + * NOTE: this API is bad; it could be much more efficient... + */ +int +ldap_create_session_tracking_control( + LDAP *ld, + char *sessionSourceIp, + char *sessionSourceName, + char *formatOID, + struct berval *sessionTrackingIdentifier, + LDAPControl **ctrlp ) +{ + struct berval value; + + if ( ctrlp == NULL ) { + ld->ld_errno = LDAP_PARAM_ERROR; + return ld->ld_errno; + } + + ld->ld_errno = ldap_create_session_tracking_value( ld, + sessionSourceIp, sessionSourceName, formatOID, + sessionTrackingIdentifier, &value ); + if ( ld->ld_errno == LDAP_SUCCESS ) { + ld->ld_errno = ldap_control_create( LDAP_CONTROL_X_SESSION_TRACKING, + 0, &value, 0, ctrlp ); + if ( ld->ld_errno != LDAP_SUCCESS ) { + LDAP_FREE( value.bv_val ); + } + } + + return ld->ld_errno; +} + +int +ldap_parse_session_tracking_control( + LDAP *ld, + LDAPControl *ctrl, + struct berval *ip, + struct berval *name, + struct berval *oid, + struct berval *id ) +{ + BerElement *ber; + ber_tag_t tag; + ber_len_t len; + + if ( ld == NULL || + ctrl == NULL || + ip == NULL || + name == NULL || + oid == NULL || + id == NULL ) + { + if ( ld ) { + ld->ld_errno = LDAP_PARAM_ERROR; + } + + /* NOTE: we want the caller to get all or nothing; + * we could allow some of the pointers to be NULL, + * if one does not want part of the data */ + return LDAP_PARAM_ERROR; + } + + BER_BVZERO( ip ); + BER_BVZERO( name ); + BER_BVZERO( oid ); + BER_BVZERO( id ); + + ber = ber_init( &ctrl->ldctl_value ); + + if ( ber == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + return ld->ld_errno; + } + + tag = ber_skip_tag( ber, &len ); + if ( tag != LBER_SEQUENCE ) { + tag = LBER_ERROR; + goto error; + } + + /* sessionSourceIp */ + tag = ber_peek_tag( ber, &len ); + if ( tag == LBER_DEFAULT ) { + tag = LBER_ERROR; + goto error; + } + + if ( len == 0 ) { + tag = ber_skip_tag( ber, &len ); + + } else { + if ( len > 128 ) { + /* should be LDAP_DECODING_ERROR, + * but we're liberal in what we accept */ + } + tag = ber_scanf( ber, "o", ip ); + } + + /* sessionSourceName */ + tag = ber_peek_tag( ber, &len ); + if ( tag == LBER_DEFAULT ) { + tag = LBER_ERROR; + goto error; + } + + if ( len == 0 ) { + tag = ber_skip_tag( ber, &len ); + + } else { + if ( len > 65536 ) { + /* should be LDAP_DECODING_ERROR, + * but we're liberal in what we accept */ + } + tag = ber_scanf( ber, "o", name ); + } + + /* formatOID */ + tag = ber_peek_tag( ber, &len ); + if ( tag == LBER_DEFAULT ) { + tag = LBER_ERROR; + goto error; + } + + if ( len == 0 ) { + ld->ld_errno = LDAP_DECODING_ERROR; + goto error; + + } else { + if ( len > 1024 ) { + /* should be LDAP_DECODING_ERROR, + * but we're liberal in what we accept */ + } + tag = ber_scanf( ber, "o", oid ); + } + + /* FIXME: should check if it is an OID... leave it to the caller */ + + /* sessionTrackingIdentifier */ + tag = ber_peek_tag( ber, &len ); + if ( tag == LBER_DEFAULT ) { + tag = LBER_ERROR; + goto error; + } + + if ( len == 0 ) { + tag = ber_skip_tag( ber, &len ); + + } else { +#if 0 + if ( len > 65536 ) { + /* should be LDAP_DECODING_ERROR, + * but we're liberal in what we accept */ + } +#endif + tag = ber_scanf( ber, "o", id ); + } + + /* closure */ + tag = ber_skip_tag( ber, &len ); + if ( tag == LBER_DEFAULT && len == 0 ) { + tag = 0; + } + +error:; + (void)ber_free( ber, 1 ); + + if ( tag == LBER_ERROR ) { + return LDAP_DECODING_ERROR; + } + + return ld->ld_errno; +} + +#endif /* LDAP_CONTROL_X_SESSION_TRACKING */ diff --git a/libraries/libldap/string.c b/libraries/libldap/string.c new file mode 100644 index 0000000..62b9b3d --- /dev/null +++ b/libraries/libldap/string.c @@ -0,0 +1,177 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +/* + * Locale-specific 1-byte character versions + * See utf-8.c for UTF-8 versions + */ + +#include "portable.h" + +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/time.h> +#include <ac/ctype.h> + +#include "ldap-int.h" + + +#if defined ( HAVE_STRSPN ) +#define int_strspn strspn +#else +static int int_strspn( const char *str, const char *delim ) +{ + int pos; + const char *p=delim; + + for( pos=0; (*str) ; pos++,str++) { + if (*str!=*p) { + for( p=delim; (*p) ; p++ ) { + if (*str==*p) { + break; + } + } + } + + if (*p=='\0') { + return pos; + } + } + return pos; +} +#endif + +#if defined( HAVE_STRPBRK ) +#define int_strpbrk strpbrk +#else +static char *(int_strpbrk)( const char *str, const char *accept ) +{ + const char *p; + + for( ; (*str) ; str++ ) { + for( p=accept; (*p) ; p++) { + if (*str==*p) { + return str; + } + } + } + + return NULL; +} +#endif + +char *(ldap_pvt_strtok)( char *str, const char *delim, char **pos ) +{ + char *p; + + if (pos==NULL) { + return NULL; + } + + if (str==NULL) { + if (*pos==NULL) { + return NULL; + } + + str=*pos; + } + + /* skip any initial delimiters */ + str += int_strspn( str, delim ); + if (*str == '\0') { + return NULL; + } + + p = int_strpbrk( str, delim ); + if (p==NULL) { + *pos = NULL; + + } else { + *p ='\0'; + *pos = p+1; + } + + return str; +} + +char * +ldap_pvt_str2upper( char *str ) +{ + char *s; + + /* to upper */ + if ( str ) { + for ( s = str; *s; s++ ) { + *s = TOUPPER( (unsigned char) *s ); + } + } + + return( str ); +} + +struct berval * +ldap_pvt_str2upperbv( char *str, struct berval *bv ) +{ + char *s = NULL; + + assert( bv != NULL ); + + /* to upper */ + if ( str ) { + for ( s = str; *s; s++ ) { + *s = TOUPPER( (unsigned char) *s ); + } + } + + bv->bv_val = str; + bv->bv_len = (ber_len_t)(s - str); + + return( bv ); +} + +char * +ldap_pvt_str2lower( char *str ) +{ + char *s; + + /* to lower */ + if ( str ) { + for ( s = str; *s; s++ ) { + *s = TOLOWER( (unsigned char) *s ); + } + } + + return( str ); +} + +struct berval * +ldap_pvt_str2lowerbv( char *str, struct berval *bv ) +{ + char *s = NULL; + + assert( bv != NULL ); + + /* to lower */ + if ( str ) { + for ( s = str; *s; s++ ) { + *s = TOLOWER( (unsigned char) *s ); + } + } + + bv->bv_val = str; + bv->bv_len = (ber_len_t)(s - str); + + return( bv ); +} diff --git a/libraries/libldap/t61.c b/libraries/libldap/t61.c new file mode 100644 index 0000000..4556439 --- /dev/null +++ b/libraries/libldap/t61.c @@ -0,0 +1,692 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2002-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: + * This work was initially developed by Howard Chu for inclusion in + * OpenLDAP Software. + */ + +/* + * Basic T.61 <-> UTF-8 conversion + * + * These routines will perform a lossless translation from T.61 to UTF-8 + * and a lossy translation from UTF-8 to T.61. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" +#include "ldap_utf8.h" + +#include "ldap_defaults.h" + +/* + * T.61 is somewhat braindead; even in the 7-bit space it is not + * completely equivalent to 7-bit US-ASCII. Our definition of the + * character set comes from RFC 1345 with a slightly more readable + * rendition at http://std.dkuug.dk/i18n/charmaps/T.61-8BIT. + * + * Even though '#' and '$' are present in the 7-bit US-ASCII space, + * (x23 and x24, resp.) in T.61 they are mapped to 8-bit characters + * xA6 and xA4. + * + * Also T.61 lacks + * backslash \ (x5C) + * caret ^ (x5E) + * backquote ` (x60) + * left brace { (x7B) + * right brace } (x7D) + * tilde ~ (x7E) + * + * In T.61, the codes xC1 to xCF (excluding xC9, unused) are non-spacing + * accents of some form or another. There are predefined combinations + * for certain characters, but they can also be used arbitrarily. The + * table at dkuug.dk maps these accents to the E000 "private use" range + * of the Unicode space, but I believe they more properly belong in the + * 0300 range (non-spacing accents). The transformation is complicated + * slightly because Unicode wants the non-spacing character to follow + * the base character, while T.61 has the non-spacing character leading. + * Also, T.61 specifically recognizes certain combined pairs as "characters" + * but doesn't specify how to treat unrecognized pairs. This code will + * always attempt to combine pairs when a known Unicode composite exists. + */ + +static const wchar_t t61_tab[] = { + 0x000, 0x001, 0x002, 0x003, 0x004, 0x005, 0x006, 0x007, + 0x008, 0x009, 0x00a, 0x00b, 0x00c, 0x00d, 0x00e, 0x00f, + 0x010, 0x011, 0x012, 0x013, 0x014, 0x015, 0x016, 0x017, + 0x018, 0x019, 0x01a, 0x01b, 0x01c, 0x01d, 0x01e, 0x01f, + 0x020, 0x021, 0x022, 0x000, 0x000, 0x025, 0x026, 0x027, + 0x028, 0x029, 0x02a, 0x02b, 0x02c, 0x02d, 0x02e, 0x02f, + 0x030, 0x031, 0x032, 0x033, 0x034, 0x035, 0x036, 0x037, + 0x038, 0x039, 0x03a, 0x03b, 0x03c, 0x03d, 0x03e, 0x03f, + 0x040, 0x041, 0x042, 0x043, 0x044, 0x045, 0x046, 0x047, + 0x048, 0x049, 0x04a, 0x04b, 0x04c, 0x04d, 0x04e, 0x04f, + 0x050, 0x051, 0x052, 0x053, 0x054, 0x055, 0x056, 0x057, + 0x058, 0x059, 0x05a, 0x05b, 0x000, 0x05d, 0x000, 0x05f, + 0x000, 0x061, 0x062, 0x063, 0x064, 0x065, 0x066, 0x067, + 0x068, 0x069, 0x06a, 0x06b, 0x06c, 0x06d, 0x06e, 0x06f, + 0x070, 0x071, 0x072, 0x073, 0x074, 0x075, 0x076, 0x077, + 0x078, 0x079, 0x07a, 0x000, 0x07c, 0x000, 0x000, 0x07f, + 0x080, 0x081, 0x082, 0x083, 0x084, 0x085, 0x086, 0x087, + 0x088, 0x089, 0x08a, 0x08b, 0x08c, 0x08d, 0x08e, 0x08f, + 0x090, 0x091, 0x092, 0x093, 0x094, 0x095, 0x096, 0x097, + 0x098, 0x099, 0x09a, 0x09b, 0x09c, 0x09d, 0x09e, 0x09f, + 0x0a0, 0x0a1, 0x0a2, 0x0a3, 0x024, 0x0a5, 0x023, 0x0a7, + 0x0a4, 0x000, 0x000, 0x0ab, 0x000, 0x000, 0x000, 0x000, + 0x0b0, 0x0b1, 0x0b2, 0x0b3, 0x0d7, 0x0b5, 0x0b6, 0x0b7, + 0x0f7, 0x000, 0x000, 0x0bb, 0x0bc, 0x0bd, 0x0be, 0x0bf, + 0x000, 0x300, 0x301, 0x302, 0x303, 0x304, 0x306, 0x307, + 0x308, 0x000, 0x30a, 0x327, 0x332, 0x30b, 0x328, 0x30c, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x2126, 0xc6, 0x0d0, 0x0aa, 0x126, 0x000, 0x132, 0x13f, + 0x141, 0x0d8, 0x152, 0x0ba, 0x0de, 0x166, 0x14a, 0x149, + 0x138, 0x0e6, 0x111, 0x0f0, 0x127, 0x131, 0x133, 0x140, + 0x142, 0x0f8, 0x153, 0x0df, 0x0fe, 0x167, 0x14b, 0x000 +}; + +typedef wchar_t wvec16[16]; +typedef wchar_t wvec32[32]; +typedef wchar_t wvec64[64]; + +/* Substitutions when 0xc1-0xcf appears by itself or with space 0x20 */ +static const wvec16 accents = { + 0x000, 0x060, 0x0b4, 0x05e, 0x07e, 0x0af, 0x2d8, 0x2d9, + 0x0a8, 0x000, 0x2da, 0x0b8, 0x000, 0x2dd, 0x2db, 0x2c7}; + +/* In the following tables, base characters commented in (parentheses) + * are not defined by T.61 but are mapped anyway since their Unicode + * composite exists. + */ + +/* Grave accented chars AEIOU (NWY) */ +static const wvec32 c1_vec1 = { + /* Upper case */ + 0, 0xc0, 0, 0, 0, 0xc8, 0, 0, 0, 0xcc, 0, 0, 0, 0, 0x1f8, 0xd2, + 0, 0, 0, 0, 0, 0xd9, 0, 0x1e80, 0, 0x1ef2, 0, 0, 0, 0, 0, 0}; +static const wvec32 c1_vec2 = { + /* Lower case */ + 0, 0xe0, 0, 0, 0, 0xe8, 0, 0, 0, 0xec, 0, 0, 0, 0, 0x1f9, 0xf2, + 0, 0, 0, 0, 0, 0xf9, 0, 0x1e81, 0, 0x1ef3, 0, 0, 0, 0, 0, 0}; + +static const wvec32 *c1_grave[] = { + NULL, NULL, &c1_vec1, &c1_vec2, NULL, NULL, NULL, NULL +}; + +/* Acute accented chars AEIOUYCLNRSZ (GKMPW) */ +static const wvec32 c2_vec1 = { + /* Upper case */ + 0, 0xc1, 0, 0x106, 0, 0xc9, 0, 0x1f4, + 0, 0xcd, 0, 0x1e30, 0x139, 0x1e3e, 0x143, 0xd3, + 0x1e54, 0, 0x154, 0x15a, 0, 0xda, 0, 0x1e82, + 0, 0xdd, 0x179, 0, 0, 0, 0, 0}; +static const wvec32 c2_vec2 = { + /* Lower case */ + 0, 0xe1, 0, 0x107, 0, 0xe9, 0, 0x1f5, + 0, 0xed, 0, 0x1e31, 0x13a, 0x1e3f, 0x144, 0xf3, + 0x1e55, 0, 0x155, 0x15b, 0, 0xfa, 0, 0x1e83, + 0, 0xfd, 0x17a, 0, 0, 0, 0, 0}; +static const wvec32 c2_vec3 = { + /* (AE and ae) */ + 0, 0x1fc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0x1fd, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +static const wvec32 *c2_acute[] = { + NULL, NULL, &c2_vec1, &c2_vec2, NULL, NULL, NULL, &c2_vec3 +}; + +/* Circumflex AEIOUYCGHJSW (Z) */ +static const wvec32 c3_vec1 = { + /* Upper case */ + 0, 0xc2, 0, 0x108, 0, 0xca, 0, 0x11c, + 0x124, 0xce, 0x134, 0, 0, 0, 0, 0xd4, + 0, 0, 0, 0x15c, 0, 0xdb, 0, 0x174, + 0, 0x176, 0x1e90, 0, 0, 0, 0, 0}; +static const wvec32 c3_vec2 = { + /* Lower case */ + 0, 0xe2, 0, 0x109, 0, 0xea, 0, 0x11d, + 0x125, 0xee, 0x135, 0, 0, 0, 0, 0xf4, + 0, 0, 0, 0x15d, 0, 0xfb, 0, 0x175, + 0, 0x177, 0x1e91, 0, 0, 0, 0, 0}; +static const wvec32 *c3_circumflex[] = { + NULL, NULL, &c3_vec1, &c3_vec2, NULL, NULL, NULL, NULL +}; + +/* Tilde AIOUN (EVY) */ +static const wvec32 c4_vec1 = { + /* Upper case */ + 0, 0xc3, 0, 0, 0, 0x1ebc, 0, 0, 0, 0x128, 0, 0, 0, 0, 0xd1, 0xd5, + 0, 0, 0, 0, 0, 0x168, 0x1e7c, 0, 0, 0x1ef8, 0, 0, 0, 0, 0, 0}; +static const wvec32 c4_vec2 = { + /* Lower case */ + 0, 0xe3, 0, 0, 0, 0x1ebd, 0, 0, 0, 0x129, 0, 0, 0, 0, 0xf1, 0xf5, + 0, 0, 0, 0, 0, 0x169, 0x1e7d, 0, 0, 0x1ef9, 0, 0, 0, 0, 0, 0}; +static const wvec32 *c4_tilde[] = { + NULL, NULL, &c4_vec1, &c4_vec2, NULL, NULL, NULL, NULL +}; + +/* Macron AEIOU (YG) */ +static const wvec32 c5_vec1 = { + /* Upper case */ + 0, 0x100, 0, 0, 0, 0x112, 0, 0x1e20, 0, 0x12a, 0, 0, 0, 0, 0, 0x14c, + 0, 0, 0, 0, 0, 0x16a, 0, 0, 0, 0x232, 0, 0, 0, 0, 0, 0}; +static const wvec32 c5_vec2 = { + /* Lower case */ + 0, 0x101, 0, 0, 0, 0x113, 0, 0x1e21, 0, 0x12b, 0, 0, 0, 0, 0, 0x14d, + 0, 0, 0, 0, 0, 0x16b, 0, 0, 0, 0x233, 0, 0, 0, 0, 0, 0}; +static const wvec32 c5_vec3 = { + /* (AE and ae) */ + 0, 0x1e2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0x1e3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const wvec32 *c5_macron[] = { + NULL, NULL, &c5_vec1, &c5_vec2, NULL, NULL, NULL, &c5_vec3 +}; + +/* Breve AUG (EIO) */ +static const wvec32 c6_vec1 = { + /* Upper case */ + 0, 0x102, 0, 0, 0, 0x114, 0, 0x11e, 0, 0x12c, 0, 0, 0, 0, 0, 0x14e, + 0, 0, 0, 0, 0, 0x16c, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const wvec32 c6_vec2 = { + /* Lower case */ + 0, 0x103, 0, 0, 0, 0x115, 0, 0x11f, 0, 0x12d, 0, 0, 0, 0, 0, 0x14f, + 0, 0, 0, 0, 0, 0x16d, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const wvec32 *c6_breve[] = { + NULL, NULL, &c6_vec1, &c6_vec2, NULL, NULL, NULL, NULL +}; + +/* Dot Above CEGIZ (AOBDFHMNPRSTWXY) */ +static const wvec32 c7_vec1 = { + /* Upper case */ + 0, 0x226, 0x1e02, 0x10a, 0x1e0a, 0x116, 0x1e1e, 0x120, + 0x1e22, 0x130, 0, 0, 0, 0x1e40, 0x1e44, 0x22e, + 0x1e56, 0, 0x1e58, 0x1e60, 0x1e6a, 0, 0, 0x1e86, + 0x1e8a, 0x1e8e, 0x17b, 0, 0, 0, 0, 0}; +static const wvec32 c7_vec2 = { + /* Lower case */ + 0, 0x227, 0x1e03, 0x10b, 0x1e0b, 0x117, 0x1e1f, 0x121, + 0x1e23, 0, 0, 0, 0, 0x1e41, 0x1e45, 0x22f, + 0x1e57, 0, 0x1e59, 0x1e61, 0x1e6b, 0, 0, 0x1e87, + 0x1e8b, 0x1e8f, 0x17c, 0, 0, 0, 0, 0}; +static const wvec32 *c7_dotabove[] = { + NULL, NULL, &c7_vec1, &c7_vec2, NULL, NULL, NULL, NULL +}; + +/* Diaeresis AEIOUY (HWXt) */ +static const wvec32 c8_vec1 = { + /* Upper case */ + 0, 0xc4, 0, 0, 0, 0xcb, 0, 0, 0x1e26, 0xcf, 0, 0, 0, 0, 0, 0xd6, + 0, 0, 0, 0, 0, 0xdc, 0, 0x1e84, 0x1e8c, 0x178, 0, 0, 0, 0, 0, 0}; +static const wvec32 c8_vec2 = { + /* Lower case */ + 0, 0xe4, 0, 0, 0, 0xeb, 0, 0, 0x1e27, 0xef, 0, 0, 0, 0, 0, 0xf6, + 0, 0, 0, 0, 0x1e97, 0xfc, 0, 0x1e85, 0x1e8d, 0xff, 0, 0, 0, 0, 0, 0}; +static const wvec32 *c8_diaeresis[] = { + NULL, NULL, &c8_vec1, &c8_vec2, NULL, NULL, NULL, NULL +}; + +/* Ring Above AU (wy) */ +static const wvec32 ca_vec1 = { + /* Upper case */ + 0, 0xc5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0x16e, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const wvec32 ca_vec2 = { + /* Lower case */ + 0, 0xe5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0x16f, 0, 0x1e98, 0, 0x1e99, 0, 0, 0, 0, 0, 0}; +static const wvec32 *ca_ringabove[] = { + NULL, NULL, &ca_vec1, &ca_vec2, NULL, NULL, NULL, NULL +}; + +/* Cedilla CGKLNRST (EDH) */ +static const wvec32 cb_vec1 = { + /* Upper case */ + 0, 0, 0, 0xc7, 0x1e10, 0x228, 0, 0x122, + 0x1e28, 0, 0, 0x136, 0x13b, 0, 0x145, 0, + 0, 0, 0x156, 0x15e, 0x162, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const wvec32 cb_vec2 = { + /* Lower case */ + 0, 0, 0, 0xe7, 0x1e11, 0x229, 0, 0x123, + 0x1e29, 0, 0, 0x137, 0x13c, 0, 0x146, 0, + 0, 0, 0x157, 0x15f, 0x163, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const wvec32 *cb_cedilla[] = { + NULL, NULL, &cb_vec1, &cb_vec2, NULL, NULL, NULL, NULL +}; + +/* Double Acute Accent OU */ +static const wvec32 cd_vec1 = { + /* Upper case */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x150, + 0, 0, 0, 0, 0, 0x170, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const wvec32 cd_vec2 = { + /* Lower case */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x151, + 0, 0, 0, 0, 0, 0x171, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const wvec32 *cd_doubleacute[] = { + NULL, NULL, &cd_vec1, &cd_vec2, NULL, NULL, NULL, NULL +}; + +/* Ogonek AEIU (O) */ +static const wvec32 ce_vec1 = { + /* Upper case */ + 0, 0x104, 0, 0, 0, 0x118, 0, 0, 0, 0x12e, 0, 0, 0, 0, 0, 0x1ea, + 0, 0, 0, 0, 0, 0x172, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const wvec32 ce_vec2 = { + /* Lower case */ + 0, 0x105, 0, 0, 0, 0x119, 0, 0, 0, 0x12f, 0, 0, 0, 0, 0, 0x1eb, + 0, 0, 0, 0, 0, 0x173, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static const wvec32 *ce_ogonek[] = { + NULL, NULL, &ce_vec1, &ce_vec2, NULL, NULL, NULL, NULL +}; + +/* Caron CDELNRSTZ (AIOUGKjH) */ +static const wvec32 cf_vec1 = { + /* Upper case */ + 0, 0x1cd, 0, 0x10c, 0x10e, 0x11a, 0, 0x1e6, + 0x21e, 0x1cf, 0, 0x1e8, 0x13d, 0, 0x147, 0x1d1, + 0, 0, 0x158, 0x160, 0x164, 0x1d3, 0, 0, + 0, 0, 0x17d, 0, 0, 0, 0, 0}; +static const wvec32 cf_vec2 = { + /* Lower case */ + 0, 0x1ce, 0, 0x10d, 0x10f, 0x11b, 0, 0x1e7, + 0x21f, 0x1d0, 0x1f0, 0x1e9, 0x13e, 0, 0x148, 0x1d2, + 0, 0, 0x159, 0x161, 0x165, 0x1d4, 0, 0, + 0, 0, 0x17e, 0, 0, 0, 0, 0}; +static const wvec32 *cf_caron[] = { + NULL, NULL, &cf_vec1, &cf_vec2, NULL, NULL, NULL, NULL +}; + +static const wvec32 **cx_tab[] = { + NULL, c1_grave, c2_acute, c3_circumflex, c4_tilde, c5_macron, + c6_breve, c7_dotabove, c8_diaeresis, NULL, ca_ringabove, + cb_cedilla, NULL, cd_doubleacute, ce_ogonek, cf_caron }; + +int ldap_t61s_valid( struct berval *str ) +{ + unsigned char *c = (unsigned char *)str->bv_val; + int i; + + for (i=0; i < str->bv_len; c++,i++) + if (!t61_tab[*c]) + return 0; + return 1; +} + +/* Transform a T.61 string to UTF-8. + */ +int ldap_t61s_to_utf8s( struct berval *src, struct berval *dst ) +{ + unsigned char *c; + char *d; + int i, wlen = 0; + + /* Just count the length of the UTF-8 result first */ + for (i=0,c=(unsigned char *)src->bv_val; i < src->bv_len; c++,i++) { + /* Invalid T.61 characters? */ + if (!t61_tab[*c]) + return LDAP_INVALID_SYNTAX; + if ((*c & 0xf0) == 0xc0) { + int j = *c & 0x0f; + /* If this is the end of the string, or if the base + * character is just a space, treat this as a regular + * spacing character. + */ + if ((!c[1] || c[1] == 0x20) && accents[j]) { + wlen += ldap_x_wc_to_utf8(NULL, accents[j], 0); + } else if (cx_tab[j] && cx_tab[j][c[1]>>5] && + /* We have a composite mapping for this pair */ + (*cx_tab[j][c[1]>>5])[c[1]&0x1f]) { + wlen += ldap_x_wc_to_utf8( NULL, + (*cx_tab[j][c[1]>>5])[c[1]&0x1f], 0); + } else { + /* No mapping, just swap it around so the base + * character comes first. + */ + wlen += ldap_x_wc_to_utf8(NULL, c[1], 0); + wlen += ldap_x_wc_to_utf8(NULL, + t61_tab[*c], 0); + } + c++; i++; + continue; + } else { + wlen += ldap_x_wc_to_utf8(NULL, t61_tab[*c], 0); + } + } + + /* Now transform the string */ + dst->bv_len = wlen; + dst->bv_val = LDAP_MALLOC( wlen+1 ); + d = dst->bv_val; + if (!d) + return LDAP_NO_MEMORY; + + for (i=0,c=(unsigned char *)src->bv_val; i < src->bv_len; c++,i++) { + if ((*c & 0xf0) == 0xc0) { + int j = *c & 0x0f; + /* If this is the end of the string, or if the base + * character is just a space, treat this as a regular + * spacing character. + */ + if ((!c[1] || c[1] == 0x20) && accents[j]) { + d += ldap_x_wc_to_utf8(d, accents[j], 6); + } else if (cx_tab[j] && cx_tab[j][c[1]>>5] && + /* We have a composite mapping for this pair */ + (*cx_tab[j][c[1]>>5])[c[1]&0x1f]) { + d += ldap_x_wc_to_utf8(d, + (*cx_tab[j][c[1]>>5])[c[1]&0x1f], 6); + } else { + /* No mapping, just swap it around so the base + * character comes first. + */ + d += ldap_x_wc_to_utf8(d, c[1], 6); + d += ldap_x_wc_to_utf8(d, t61_tab[*c], 6); + } + c++; i++; + continue; + } else { + d += ldap_x_wc_to_utf8(d, t61_tab[*c], 6); + } + } + *d = '\0'; + return LDAP_SUCCESS; +} + +/* For the reverse mapping, we just pay attention to the Latin-oriented + * code blocks. These are + * 0000 - 007f Basic Latin + * 0080 - 00ff Latin-1 Supplement + * 0100 - 017f Latin Extended-A + * 0180 - 024f Latin Extended-B + * 1e00 - 1eff Latin Extended Additional + * + * We have a special case to map Ohm U2126 back to T.61 0xe0. All other + * unrecognized characters are replaced with '?' 0x3f. + */ + +static const wvec64 u000 = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, + 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, + 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, + 0x0020, 0x0021, 0x0022, 0x00a6, 0x00a4, 0x0025, 0x0026, 0x0027, + 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f}; + +/* In this range, we've mapped caret to xc3/x20, backquote to xc1/x20, + * and tilde to xc4/x20. T.61 (stupidly!) doesn't define these characters + * on their own, even though it provides them as combiners for other + * letters. T.61 doesn't define these pairings either, so this may just + * have to be replaced with '?' 0x3f if other software can't cope with it. + */ +static const wvec64 u001 = { + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, + 0x0058, 0x0059, 0x005a, 0x005b, 0x003f, 0x005d, 0xc320, 0x005f, + 0xc120, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, + 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, + 0x0078, 0x0079, 0x007a, 0x003f, 0x007c, 0x003f, 0xc420, 0x007f}; + +static const wvec64 u002 = { + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, + 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, + 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, + 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f, + 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a8, 0x00a5, 0x003f, 0x00a7, + 0xc820, 0x003f, 0x00e3, 0x00ab, 0x003f, 0x003f, 0x003f, 0xc520, + 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0xc220, 0x00b5, 0x00b6, 0x00b7, + 0xcb20, 0x003f, 0x00eb, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf}; + +static const wvec64 u003 = { + 0xc141, 0xc241, 0xc341, 0xc441, 0xc841, 0xca41, 0x00e1, 0xcb43, + 0xc145, 0xc245, 0xc345, 0xc845, 0xc149, 0xc249, 0xc349, 0xc849, + 0x00e2, 0xc44e, 0xc14f, 0xc24f, 0xc34f, 0xc44f, 0xc84f, 0x00b4, + 0x00e9, 0xc155, 0xc255, 0xc355, 0xc855, 0xc259, 0x00ec, 0x00fb, + 0xc161, 0xc261, 0xc361, 0xc461, 0xc861, 0xca61, 0x00f1, 0xcb63, + 0xc165, 0xc265, 0xc365, 0xc865, 0xc169, 0xc269, 0xc369, 0xc869, + 0x00f3, 0xc46e, 0xc16f, 0xc26f, 0xc36f, 0xc46f, 0xc86f, 0x00b8, + 0x00f9, 0xc175, 0xc275, 0xc375, 0xc875, 0xc279, 0x00fc, 0xc879}; + +/* These codes are used here but not defined by T.61: + * x114 = xc6/x45, x115 = xc6/x65, x12c = xc6/x49, x12d = xc6/x69 + */ +static const wvec64 u010 = { + 0xc541, 0xc561, 0xc641, 0xc661, 0xce41, 0xce61, 0xc243, 0xc263, + 0xc343, 0xc363, 0xc743, 0xc763, 0xcf43, 0xcf63, 0xcf44, 0xcf64, + 0x003f, 0x00f2, 0xc545, 0xc565, 0xc645, 0xc665, 0xc745, 0xc765, + 0xce45, 0xce65, 0xcf45, 0xcf65, 0xc347, 0xc367, 0xc647, 0xc667, + 0xc747, 0xc767, 0xcb47, 0xcb67, 0xc348, 0xc368, 0x00e4, 0x00f4, + 0xc449, 0xc469, 0xc549, 0xc569, 0xc649, 0xc669, 0xce49, 0xce69, + 0xc749, 0x00f5, 0x00e6, 0x00f6, 0xc34a, 0xc36a, 0xcb4b, 0xcb6b, + 0x00f0, 0xc24c, 0xc26c, 0xcb4c, 0xcb6c, 0xcf4c, 0xcf6c, 0x00e7}; + +/* These codes are used here but not defined by T.61: + * x14e = xc6/x4f, x14f = xc6/x6f + */ +static const wvec64 u011 = { + 0x00f7, 0x00e8, 0x00f8, 0xc24e, 0xc26e, 0xcb4e, 0xcb6e, 0xcf4e, + 0xcf6e, 0x00ef, 0x00ee, 0x00fe, 0xc54f, 0xc56f, 0xc64f, 0xc66f, + 0xcd4f, 0xcd6f, 0x00ea, 0x00fa, 0xc252, 0xc272, 0xcb52, 0xcb72, + 0xcf52, 0xcf72, 0xc253, 0xc273, 0xc353, 0xc373, 0xcb53, 0xcb73, + 0xcf53, 0xcf73, 0xcb54, 0xcb74, 0xcf54, 0xcf74, 0x00ed, 0x00fd, + 0xc455, 0xc475, 0xc555, 0xc575, 0xc655, 0xc675, 0xca55, 0xca75, + 0xcd55, 0xcd75, 0xce55, 0xce75, 0xc357, 0xc377, 0xc359, 0xc379, + 0xc859, 0xc25a, 0xc27a, 0xc75a, 0xc77a, 0xcf5a, 0xcf7a, 0x003f}; + +/* All of the codes in this block are undefined in T.61. + */ +static const wvec64 u013 = { + 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, + 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0xcf41, 0xcf61, 0xcf49, + 0xcf69, 0xcf4f, 0xcf6f, 0xcf55, 0xcf75, 0x003f, 0x003f, 0x003f, + 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, + 0x003f, 0x003f, 0xc5e1, 0xc5f1, 0x003f, 0x003f, 0xcf47, 0xcf67, + 0xcf4b, 0xcf6b, 0xce4f, 0xce6f, 0x003f, 0x003f, 0x003f, 0x003f, + 0xcf6a, 0x003f, 0x003f, 0x003f, 0xc247, 0xc267, 0x003f, 0x003f, + 0xc14e, 0xc16e, 0x003f, 0x003f, 0xc2e1, 0xc2f1, 0x003f, 0x003f}; + +/* All of the codes in this block are undefined in T.61. + */ +static const wvec64 u020 = { + 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, + 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, + 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, + 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0xcf48, 0xcf68, + 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0xc741, 0xc761, + 0xcb45, 0xcb65, 0x003f, 0x003f, 0x003f, 0x003f, 0xc74f, 0xc76f, + 0x003f, 0x003f, 0xc559, 0xc579, 0x003f, 0x003f, 0x003f, 0x003f, + 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f}; + +static const wvec64 u023 = { + 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0xcf20, + 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, + 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, + 0xc620, 0xc720, 0xca20, 0xce20, 0x003f, 0xcd20, 0x003f, 0x003f, + 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, + 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, + 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, + 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f}; + +/* These are the non-spacing characters by themselves. They should + * never appear by themselves in actual text. + */ +static const wvec64 u030 = { + 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x003f, 0x00c6, 0x00c7, + 0x00c8, 0x003f, 0x00ca, 0x00cd, 0x00cf, 0x003f, 0x003f, 0x003f, + 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, + 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, + 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x00cb, + 0x00ce, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, + 0x003f, 0x003f, 0x00cc, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, + 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f}; + +/* None of the following blocks are defined in T.61. + */ +static const wvec64 u1e0 = { + 0x003f, 0x003f, 0xc742, 0xc762, 0x003f, 0x003f, 0x003f, 0x003f, + 0x003f, 0x003f, 0xc744, 0xc764, 0x003f, 0x003f, 0x003f, 0x003f, + 0xcb44, 0xcb64, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, + 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0xc746, 0xc766, + 0xc547, 0xc567, 0xc748, 0xc768, 0x003f, 0x003f, 0xc848, 0xc868, + 0xcb48, 0xcb68, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, + 0xc24b, 0xc26b, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, + 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0xc24d, 0xc26d, +}; + +static const wvec64 u1e1 = { + 0xc74d, 0xc76d, 0x003f, 0x003f, 0xc74e, 0xc76e, 0x003f, 0x003f, + 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, + 0x003f, 0x003f, 0x003f, 0x003f, 0xc250, 0xc270, 0xc750, 0xc770, + 0xc752, 0xc772, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, + 0xc753, 0xc773, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, + 0x003f, 0x003f, 0xc754, 0xc774, 0x003f, 0x003f, 0x003f, 0x003f, + 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, + 0x003f, 0x003f, 0x003f, 0x003f, 0xc456, 0xc476, 0x003f, 0x003f, +}; + +static const wvec64 u1e2 = { + 0xc157, 0xc177, 0xc257, 0xc277, 0xc857, 0xc877, 0xc757, 0xc777, + 0x003f, 0x003f, 0xc758, 0xc778, 0xc858, 0xc878, 0xc759, 0xc779, + 0xc35a, 0xc37a, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0xc874, + 0xca77, 0xca79, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, + 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, + 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, + 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, + 0x003f, 0x003f, 0x003f, 0x003f, 0xc445, 0xc465, 0x003f, 0x003f, +}; + +static const wvec64 u1e3 = { + 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, + 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, + 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, + 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, + 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, + 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, + 0x003f, 0x003f, 0xc159, 0xc179, 0x003f, 0x003f, 0x003f, 0x003f, + 0xc459, 0xc479, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, +}; + +static const wvec64 *wc00[] = { + &u000, &u001, &u002, &u003, + &u010, &u011, NULL, &u013, + &u020, NULL, NULL, &u023, + &u030, NULL, NULL, NULL}; + +static const wvec64 *wc1e[] = { + &u1e0, &u1e1, &u1e2, &u1e3}; + + +int ldap_utf8s_to_t61s( struct berval *src, struct berval *dst ) +{ + char *c, *d; + wchar_t tmp; + int i, j, tlen = 0; + + /* Just count the length of the T.61 result first */ + for (i=0,c=src->bv_val; i < src->bv_len;) { + j = ldap_x_utf8_to_wc( &tmp, c ); + if (j == -1) + return LDAP_INVALID_SYNTAX; + switch (tmp >> 8) { + case 0x00: + case 0x01: + case 0x02: + case 0x03: + if (wc00[tmp >> 6] && + ((*wc00[tmp >> 6])[tmp & 0x3f] & 0xff00)) { + tlen++; + } + tlen++; + break; + case 0x1e: + if ((*wc1e[(tmp >> 6) & 3])[tmp & 0x3f] & 0xff00) { + tlen++; + } + case 0x21: + default: + tlen ++; + break; + } + i += j; + c += j; + } + dst->bv_len = tlen; + dst->bv_val = LDAP_MALLOC( tlen+1 ); + if (!dst->bv_val) + return LDAP_NO_MEMORY; + + d = dst->bv_val; + for (i=0,c=src->bv_val; i < src->bv_len;) { + j = ldap_x_utf8_to_wc( &tmp, c ); + switch (tmp >> 8) { + case 0x00: + case 0x01: + case 0x02: + if (wc00[tmp >> 6]) { + tmp = (*wc00[tmp >> 6])[tmp & 0x3f]; + if (tmp & 0xff00) + *d++ = (tmp >> 8); + *d++ = tmp & 0xff; + } else { + *d++ = 0x3f; + } + break; + case 0x03: + /* swap order of non-spacing characters */ + if (wc00[tmp >> 6]) { + wchar_t t2 = (*wc00[tmp >> 6])[tmp & 0x3f]; + if (t2 != 0x3f) { + d[0] = d[-1]; + d[-1] = t2; + d++; + } else { + *d++ = 0x3f; + } + } else { + *d++ = 0x3f; + } + break; + case 0x1e: + tmp = (*wc1e[(tmp >> 6) & 3])[tmp & 0x3f]; + if (tmp & 0xff00) + *d++ = (tmp >> 8); + *d++ = tmp & 0xff; + break; + case 0x21: + if (tmp == 0x2126) { + *d++ = 0xe0; + break; + } + /* FALLTHRU */ + default: + *d++ = 0x3f; + break; + } + i += j; + c += j; + } + *d = '\0'; + return LDAP_SUCCESS; +} diff --git a/libraries/libldap/test.c b/libraries/libldap/test.c new file mode 100644 index 0000000..eb44430 --- /dev/null +++ b/libraries/libldap/test.c @@ -0,0 +1,807 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/ctype.h> +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> +#include <ac/unistd.h> + +#include <sys/stat.h> + +#ifdef HAVE_SYS_FILE_H +#include <sys/file.h> +#endif +#ifdef HAVE_IO_H +#include <io.h> +#endif + +#include <fcntl.h> + +/* including the "internal" defs is legit and nec. since this test routine has + * a-priori knowledge of libldap internal workings. + * hodges@stanford.edu 5-Feb-96 + */ +#include "ldap-int.h" + +/* local functions */ +static char *get_line LDAP_P(( char *line, int len, FILE *fp, const char *prompt )); +static char **get_list LDAP_P(( const char *prompt )); +static int file_read LDAP_P(( const char *path, struct berval *bv )); +static LDAPMod **get_modlist LDAP_P(( const char *prompt1, + const char *prompt2, const char *prompt3 )); +static void handle_result LDAP_P(( LDAP *ld, LDAPMessage *lm )); +static void print_ldap_result LDAP_P(( LDAP *ld, LDAPMessage *lm, + const char *s )); +static void print_search_entry LDAP_P(( LDAP *ld, LDAPMessage *res )); +static void free_list LDAP_P(( char **list )); + +static char *dnsuffix; + +static char * +get_line( char *line, int len, FILE *fp, const char *prompt ) +{ + fputs(prompt, stdout); + + if ( fgets( line, len, fp ) == NULL ) + return( NULL ); + + line[ strlen( line ) - 1 ] = '\0'; + + return( line ); +} + +static char ** +get_list( const char *prompt ) +{ + static char buf[256]; + int num; + char **result; + + num = 0; + result = (char **) 0; + while ( 1 ) { + get_line( buf, sizeof(buf), stdin, prompt ); + + if ( *buf == '\0' ) + break; + + if ( result == (char **) 0 ) + result = (char **) malloc( sizeof(char *) ); + else + result = (char **) realloc( result, + sizeof(char *) * (num + 1) ); + + result[num++] = (char *) strdup( buf ); + } + if ( result == (char **) 0 ) + return( NULL ); + result = (char **) realloc( result, sizeof(char *) * (num + 1) ); + result[num] = NULL; + + return( result ); +} + + +static void +free_list( char **list ) +{ + int i; + + if ( list != NULL ) { + for ( i = 0; list[ i ] != NULL; ++i ) { + free( list[ i ] ); + } + free( (char *)list ); + } +} + + +static int +file_read( const char *path, struct berval *bv ) +{ + FILE *fp; + ber_slen_t rlen; + int eof; + + if (( fp = fopen( path, "r" )) == NULL ) { + perror( path ); + return( -1 ); + } + + if ( fseek( fp, 0L, SEEK_END ) != 0 ) { + perror( path ); + fclose( fp ); + return( -1 ); + } + + bv->bv_len = ftell( fp ); + + if (( bv->bv_val = (char *)malloc( bv->bv_len )) == NULL ) { + perror( "malloc" ); + fclose( fp ); + return( -1 ); + } + + if ( fseek( fp, 0L, SEEK_SET ) != 0 ) { + perror( path ); + fclose( fp ); + return( -1 ); + } + + rlen = fread( bv->bv_val, 1, bv->bv_len, fp ); + eof = feof( fp ); + fclose( fp ); + + if ( (ber_len_t) rlen != bv->bv_len ) { + perror( path ); + free( bv->bv_val ); + return( -1 ); + } + + return( bv->bv_len ); +} + + +static LDAPMod ** +get_modlist( + const char *prompt1, + const char *prompt2, + const char *prompt3 ) +{ + static char buf[256]; + int num; + LDAPMod tmp = { 0 }; + LDAPMod **result; + struct berval **bvals; + + num = 0; + result = NULL; + while ( 1 ) { + if ( prompt1 ) { + get_line( buf, sizeof(buf), stdin, prompt1 ); + tmp.mod_op = atoi( buf ); + + if ( tmp.mod_op == -1 || buf[0] == '\0' ) + break; + } + + get_line( buf, sizeof(buf), stdin, prompt2 ); + if ( buf[0] == '\0' ) + break; + tmp.mod_type = strdup( buf ); + + tmp.mod_values = get_list( prompt3 ); + + if ( tmp.mod_values != NULL ) { + int i; + + for ( i = 0; tmp.mod_values[i] != NULL; ++i ) + ; + bvals = (struct berval **)calloc( i + 1, + sizeof( struct berval *)); + for ( i = 0; tmp.mod_values[i] != NULL; ++i ) { + bvals[i] = (struct berval *)malloc( + sizeof( struct berval )); + if ( strncmp( tmp.mod_values[i], "{FILE}", + 6 ) == 0 ) { + if ( file_read( tmp.mod_values[i] + 6, + bvals[i] ) < 0 ) { + free( bvals ); + for ( i = 0; i<num; i++ ) + free( result[ i ] ); + free( result ); + return( NULL ); + } + } else { + bvals[i]->bv_val = tmp.mod_values[i]; + bvals[i]->bv_len = + strlen( tmp.mod_values[i] ); + } + } + tmp.mod_bvalues = bvals; + tmp.mod_op |= LDAP_MOD_BVALUES; + } + + if ( result == NULL ) + result = (LDAPMod **) malloc( sizeof(LDAPMod *) ); + else + result = (LDAPMod **) realloc( result, + sizeof(LDAPMod *) * (num + 1) ); + + result[num] = (LDAPMod *) malloc( sizeof(LDAPMod) ); + *(result[num]) = tmp; /* struct copy */ + num++; + } + if ( result == NULL ) + return( NULL ); + result = (LDAPMod **) realloc( result, sizeof(LDAPMod *) * (num + 1) ); + result[num] = NULL; + + return( result ); +} + + +static int +bind_prompt( LDAP *ld, + LDAP_CONST char *url, + ber_tag_t request, ber_int_t msgid, + void *params ) +{ + static char dn[256], passwd[256]; + int authmethod; + + printf("rebind for request=%ld msgid=%ld url=%s\n", + request, (long) msgid, url ); + + authmethod = LDAP_AUTH_SIMPLE; + + get_line( dn, sizeof(dn), stdin, "re-bind dn? " ); + strcat( dn, dnsuffix ); + + if ( authmethod == LDAP_AUTH_SIMPLE && dn[0] != '\0' ) { + get_line( passwd, sizeof(passwd), stdin, + "re-bind password? " ); + } else { + passwd[0] = '\0'; + } + + return ldap_bind_s( ld, dn, passwd, authmethod); +} + + +int +main( int argc, char **argv ) +{ + LDAP *ld = NULL; + int i, c, port, errflg, method, id, msgtype; + char line[256], command1, command2, command3; + char passwd[64], dn[256], rdn[64], attr[64], value[256]; + char filter[256], *host, **types; + char **exdn; + static const char usage[] = + "usage: %s [-u] [-h host] [-d level] [-s dnsuffix] [-p port] [-t file] [-T file]\n"; + int bound, all, scope, attrsonly; + LDAPMessage *res; + LDAPMod **mods, **attrs; + struct timeval timeout; + char *copyfname = NULL; + int copyoptions = 0; + LDAPURLDesc *ludp; + + host = NULL; + port = LDAP_PORT; + dnsuffix = ""; + errflg = 0; + + while (( c = getopt( argc, argv, "h:d:s:p:t:T:" )) != -1 ) { + switch( c ) { + case 'd': +#ifdef LDAP_DEBUG + ldap_debug = atoi( optarg ); +#ifdef LBER_DEBUG + if ( ldap_debug & LDAP_DEBUG_PACKETS ) { + ber_set_option( NULL, LBER_OPT_DEBUG_LEVEL, &ldap_debug ); + } +#endif +#else + printf( "Compile with -DLDAP_DEBUG for debugging\n" ); +#endif + break; + + case 'h': + host = optarg; + break; + + case 's': + dnsuffix = optarg; + break; + + case 'p': + port = atoi( optarg ); + break; + + case 't': /* copy ber's to given file */ + copyfname = strdup( optarg ); +/* copyoptions = LBER_TO_FILE; */ + break; + + case 'T': /* only output ber's to given file */ + copyfname = strdup( optarg ); +/* copyoptions = (LBER_TO_FILE | LBER_TO_FILE_ONLY); */ + break; + + default: + ++errflg; + } + } + + if ( host == NULL && optind == argc - 1 ) { + host = argv[ optind ]; + ++optind; + } + + if ( errflg || optind < argc - 1 ) { + fprintf( stderr, usage, argv[ 0 ] ); + exit( EXIT_FAILURE ); + } + + printf( "ldap_init( %s, %d )\n", + host == NULL ? "(null)" : host, port ); + + ld = ldap_init( host, port ); + + if ( ld == NULL ) { + perror( "ldap_init" ); + exit( EXIT_FAILURE ); + } + + if ( copyfname != NULL ) { + if ( ( ld->ld_sb->sb_fd = open( copyfname, O_WRONLY|O_CREAT|O_EXCL, + 0600 )) == -1 ) { + perror( copyfname ); + exit ( EXIT_FAILURE ); + } + ld->ld_sb->sb_options = copyoptions; + } + + bound = 0; + timeout.tv_sec = 0; + timeout.tv_usec = 0; + + (void) memset( line, '\0', sizeof(line) ); + while ( get_line( line, sizeof(line), stdin, "\ncommand? " ) != NULL ) { + command1 = line[0]; + command2 = line[1]; + command3 = line[2]; + + switch ( command1 ) { + case 'a': /* add or abandon */ + switch ( command2 ) { + case 'd': /* add */ + get_line( dn, sizeof(dn), stdin, "dn? " ); + strcat( dn, dnsuffix ); + if ( (attrs = get_modlist( NULL, "attr? ", + "value? " )) == NULL ) + break; + if ( (id = ldap_add( ld, dn, attrs )) == -1 ) + ldap_perror( ld, "ldap_add" ); + else + printf( "Add initiated with id %d\n", + id ); + break; + + case 'b': /* abandon */ + get_line( line, sizeof(line), stdin, "msgid? " ); + id = atoi( line ); + if ( ldap_abandon( ld, id ) != 0 ) + ldap_perror( ld, "ldap_abandon" ); + else + printf( "Abandon successful\n" ); + break; + default: + printf( "Possibilities: [ad]d, [ab]ort\n" ); + } + break; + + case 'b': /* asynch bind */ + method = LDAP_AUTH_SIMPLE; + get_line( dn, sizeof(dn), stdin, "dn? " ); + strcat( dn, dnsuffix ); + + if ( method == LDAP_AUTH_SIMPLE && dn[0] != '\0' ) + get_line( passwd, sizeof(passwd), stdin, + "password? " ); + else + passwd[0] = '\0'; + + if ( ldap_bind( ld, dn, passwd, method ) == -1 ) { + fprintf( stderr, "ldap_bind failed\n" ); + ldap_perror( ld, "ldap_bind" ); + } else { + printf( "Bind initiated\n" ); + bound = 1; + } + break; + + case 'B': /* synch bind */ + method = LDAP_AUTH_SIMPLE; + get_line( dn, sizeof(dn), stdin, "dn? " ); + strcat( dn, dnsuffix ); + + if ( dn[0] != '\0' ) + get_line( passwd, sizeof(passwd), stdin, + "password? " ); + else + passwd[0] = '\0'; + + if ( ldap_bind_s( ld, dn, passwd, method ) != + LDAP_SUCCESS ) { + fprintf( stderr, "ldap_bind_s failed\n" ); + ldap_perror( ld, "ldap_bind_s" ); + } else { + printf( "Bind successful\n" ); + bound = 1; + } + break; + + case 'c': /* compare */ + get_line( dn, sizeof(dn), stdin, "dn? " ); + strcat( dn, dnsuffix ); + get_line( attr, sizeof(attr), stdin, "attr? " ); + get_line( value, sizeof(value), stdin, "value? " ); + + if ( (id = ldap_compare( ld, dn, attr, value )) == -1 ) + ldap_perror( ld, "ldap_compare" ); + else + printf( "Compare initiated with id %d\n", id ); + break; + + case 'd': /* turn on debugging */ +#ifdef LDAP_DEBUG + get_line( line, sizeof(line), stdin, "debug level? " ); + ldap_debug = atoi( line ); +#ifdef LBER_DEBUG + if ( ldap_debug & LDAP_DEBUG_PACKETS ) { + ber_set_option( NULL, LBER_OPT_DEBUG_LEVEL, &ldap_debug ); + } +#endif +#else + printf( "Compile with -DLDAP_DEBUG for debugging\n" ); +#endif + break; + + case 'E': /* explode a dn */ + get_line( line, sizeof(line), stdin, "dn? " ); + exdn = ldap_explode_dn( line, 0 ); + for ( i = 0; exdn != NULL && exdn[i] != NULL; i++ ) { + printf( "\t%s\n", exdn[i] ); + } + break; + + case 'g': /* set next msgid */ + get_line( line, sizeof(line), stdin, "msgid? " ); + ld->ld_msgid = atoi( line ); + break; + + case 'v': /* set version number */ + get_line( line, sizeof(line), stdin, "version? " ); + ld->ld_version = atoi( line ); + break; + + case 'm': /* modify or modifyrdn */ + if ( strncmp( line, "modify", 4 ) == 0 ) { + get_line( dn, sizeof(dn), stdin, "dn? " ); + strcat( dn, dnsuffix ); + if ( (mods = get_modlist( + "mod (0=>add, 1=>delete, 2=>replace -1=>done)? ", + "attribute type? ", "attribute value? " )) + == NULL ) + break; + if ( (id = ldap_modify( ld, dn, mods )) == -1 ) + ldap_perror( ld, "ldap_modify" ); + else + printf( "Modify initiated with id %d\n", + id ); + } else if ( strncmp( line, "modrdn", 4 ) == 0 ) { + get_line( dn, sizeof(dn), stdin, "dn? " ); + strcat( dn, dnsuffix ); + get_line( rdn, sizeof(rdn), stdin, "newrdn? " ); + if ( (id = ldap_modrdn( ld, dn, rdn )) == -1 ) + ldap_perror( ld, "ldap_modrdn" ); + else + printf( "Modrdn initiated with id %d\n", + id ); + } else { + printf( "Possibilities: [modi]fy, [modr]dn\n" ); + } + break; + + case 'q': /* quit */ + ldap_unbind( ld ); + exit( EXIT_SUCCESS ); + break; + + case 'r': /* result or remove */ + switch ( command3 ) { + case 's': /* result */ + get_line( line, sizeof(line), stdin, + "msgid (-1=>any)? " ); + if ( line[0] == '\0' ) + id = -1; + else + id = atoi( line ); + get_line( line, sizeof(line), stdin, + "all (0=>any, 1=>all)? " ); + if ( line[0] == '\0' ) + all = 1; + else + all = atoi( line ); + if (( msgtype = ldap_result( ld, id, all, + &timeout, &res )) < 1 ) { + ldap_perror( ld, "ldap_result" ); + break; + } + printf( "\nresult: msgtype %d msgid %d\n", + msgtype, res->lm_msgid ); + handle_result( ld, res ); + res = NULL; + break; + + case 'm': /* remove */ + get_line( dn, sizeof(dn), stdin, "dn? " ); + strcat( dn, dnsuffix ); + if ( (id = ldap_delete( ld, dn )) == -1 ) + ldap_perror( ld, "ldap_delete" ); + else + printf( "Remove initiated with id %d\n", + id ); + break; + + default: + printf( "Possibilities: [rem]ove, [res]ult\n" ); + break; + } + break; + + case 's': /* search */ + get_line( dn, sizeof(dn), stdin, "searchbase? " ); + strcat( dn, dnsuffix ); + get_line( line, sizeof(line), stdin, + "scope (0=baseObject, 1=oneLevel, 2=subtree, 3=children)? " ); + scope = atoi( line ); + get_line( filter, sizeof(filter), stdin, + "search filter (e.g. sn=jones)? " ); + types = get_list( "attrs to return? " ); + get_line( line, sizeof(line), stdin, + "attrsonly (0=attrs&values, 1=attrs only)? " ); + attrsonly = atoi( line ); + + if (( id = ldap_search( ld, dn, scope, filter, + types, attrsonly )) == -1 ) { + ldap_perror( ld, "ldap_search" ); + } else { + printf( "Search initiated with id %d\n", id ); + } + free_list( types ); + break; + + case 't': /* set timeout value */ + get_line( line, sizeof(line), stdin, "timeout? " ); + timeout.tv_sec = atoi( line ); + break; + + case 'p': /* parse LDAP URL */ + get_line( line, sizeof(line), stdin, "LDAP URL? " ); + if (( i = ldap_url_parse( line, &ludp )) != 0 ) { + fprintf( stderr, "ldap_url_parse: error %d\n", i ); + } else { + printf( "\t host: " ); + if ( ludp->lud_host == NULL ) { + printf( "DEFAULT\n" ); + } else { + printf( "<%s>\n", ludp->lud_host ); + } + printf( "\t port: " ); + if ( ludp->lud_port == 0 ) { + printf( "DEFAULT\n" ); + } else { + printf( "%d\n", ludp->lud_port ); + } + printf( "\t dn: <%s>\n", ludp->lud_dn ); + printf( "\t attrs:" ); + if ( ludp->lud_attrs == NULL ) { + printf( " ALL" ); + } else { + for ( i = 0; ludp->lud_attrs[ i ] != NULL; ++i ) { + printf( " <%s>", ludp->lud_attrs[ i ] ); + } + } + printf( "\n\t scope: %s\n", + ludp->lud_scope == LDAP_SCOPE_BASE ? "baseObject" + : ludp->lud_scope == LDAP_SCOPE_ONELEVEL ? "oneLevel" + : ludp->lud_scope == LDAP_SCOPE_SUBTREE ? "subtree" +#ifdef LDAP_SCOPE_SUBORDINATE + : ludp->lud_scope == LDAP_SCOPE_SUBORDINATE ? "children" +#endif + : "**invalid**" ); + printf( "\tfilter: <%s>\n", ludp->lud_filter ); + ldap_free_urldesc( ludp ); + } + break; + + case 'n': /* set dn suffix, for convenience */ + get_line( line, sizeof(line), stdin, "DN suffix? " ); + strcpy( dnsuffix, line ); + break; + + case 'o': /* set ldap options */ + get_line( line, sizeof(line), stdin, "alias deref (0=never, 1=searching, 2=finding, 3=always)?" ); + ld->ld_deref = atoi( line ); + get_line( line, sizeof(line), stdin, "timelimit?" ); + ld->ld_timelimit = atoi( line ); + get_line( line, sizeof(line), stdin, "sizelimit?" ); + ld->ld_sizelimit = atoi( line ); + + LDAP_BOOL_ZERO(&ld->ld_options); + + get_line( line, sizeof(line), stdin, + "Recognize and chase referrals (0=no, 1=yes)?" ); + if ( atoi( line ) != 0 ) { + LDAP_BOOL_SET(&ld->ld_options, LDAP_BOOL_REFERRALS); + get_line( line, sizeof(line), stdin, + "Prompt for bind credentials when chasing referrals (0=no, 1=yes)?" ); + if ( atoi( line ) != 0 ) { + ldap_set_rebind_proc( ld, bind_prompt, NULL ); + } + } + break; + + case '?': /* help */ + printf( +"Commands: [ad]d [ab]andon [b]ind\n" +" [B]ind async [c]ompare\n" +" [modi]fy [modr]dn [rem]ove\n" +" [res]ult [s]earch [q]uit/unbind\n\n" +" [d]ebug set ms[g]id\n" +" d[n]suffix [t]imeout [v]ersion\n" +" [?]help [o]ptions" +" [E]xplode dn [p]arse LDAP URL\n" ); + break; + + default: + printf( "Invalid command. Type ? for help.\n" ); + break; + } + + (void) memset( line, '\0', sizeof(line) ); + } + + return( 0 ); +} + +static void +handle_result( LDAP *ld, LDAPMessage *lm ) +{ + switch ( lm->lm_msgtype ) { + case LDAP_RES_COMPARE: + printf( "Compare result\n" ); + print_ldap_result( ld, lm, "compare" ); + break; + + case LDAP_RES_SEARCH_RESULT: + printf( "Search result\n" ); + print_ldap_result( ld, lm, "search" ); + break; + + case LDAP_RES_SEARCH_ENTRY: + printf( "Search entry\n" ); + print_search_entry( ld, lm ); + break; + + case LDAP_RES_ADD: + printf( "Add result\n" ); + print_ldap_result( ld, lm, "add" ); + break; + + case LDAP_RES_DELETE: + printf( "Delete result\n" ); + print_ldap_result( ld, lm, "delete" ); + break; + + case LDAP_RES_MODRDN: + printf( "ModRDN result\n" ); + print_ldap_result( ld, lm, "modrdn" ); + break; + + case LDAP_RES_BIND: + printf( "Bind result\n" ); + print_ldap_result( ld, lm, "bind" ); + break; + + default: + printf( "Unknown result type 0x%lx\n", + (unsigned long) lm->lm_msgtype ); + print_ldap_result( ld, lm, "unknown" ); + } +} + +static void +print_ldap_result( LDAP *ld, LDAPMessage *lm, const char *s ) +{ + ldap_result2error( ld, lm, 1 ); + ldap_perror( ld, s ); +/* + if ( ld->ld_error != NULL && *ld->ld_error != '\0' ) + fprintf( stderr, "Additional info: %s\n", ld->ld_error ); + if ( LDAP_NAME_ERROR( ld->ld_errno ) && ld->ld_matched != NULL ) + fprintf( stderr, "Matched DN: %s\n", ld->ld_matched ); +*/ +} + +static void +print_search_entry( LDAP *ld, LDAPMessage *res ) +{ + LDAPMessage *e; + + for ( e = ldap_first_entry( ld, res ); e != NULL; + e = ldap_next_entry( ld, e ) ) + { + BerElement *ber = NULL; + char *a, *dn, *ufn; + + if ( e->lm_msgtype == LDAP_RES_SEARCH_RESULT ) + break; + + dn = ldap_get_dn( ld, e ); + printf( "\tDN: %s\n", dn ); + + ufn = ldap_dn2ufn( dn ); + printf( "\tUFN: %s\n", ufn ); + + free( dn ); + free( ufn ); + + for ( a = ldap_first_attribute( ld, e, &ber ); a != NULL; + a = ldap_next_attribute( ld, e, ber ) ) + { + struct berval **vals; + + printf( "\t\tATTR: %s\n", a ); + if ( (vals = ldap_get_values_len( ld, e, a )) + == NULL ) { + printf( "\t\t\t(no values)\n" ); + } else { + int i; + for ( i = 0; vals[i] != NULL; i++ ) { + int j, nonascii; + + nonascii = 0; + for ( j = 0; (ber_len_t) j < vals[i]->bv_len; j++ ) + if ( !isascii( vals[i]->bv_val[j] ) ) { + nonascii = 1; + break; + } + + if ( nonascii ) { + printf( "\t\t\tlength (%ld) (not ascii)\n", vals[i]->bv_len ); +#ifdef BPRINT_NONASCII + ber_bprint( vals[i]->bv_val, + vals[i]->bv_len ); +#endif /* BPRINT_NONASCII */ + continue; + } + printf( "\t\t\tlength (%ld) %s\n", + vals[i]->bv_len, vals[i]->bv_val ); + } + ber_bvecfree( vals ); + } + } + + if(ber != NULL) { + ber_free( ber, 0 ); + } + } + + if ( res->lm_msgtype == LDAP_RES_SEARCH_RESULT + || res->lm_chain != NULL ) + print_ldap_result( ld, res, "search" ); +} diff --git a/libraries/libldap/tls2.c b/libraries/libldap/tls2.c new file mode 100644 index 0000000..82ca527 --- /dev/null +++ b/libraries/libldap/tls2.c @@ -0,0 +1,1413 @@ +/* tls.c - Handle tls/ssl. */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: restructured by Howard Chu. + */ + +#include "portable.h" +#include "ldap_config.h" + +#include <stdio.h> + +#include <ac/stdlib.h> +#include <ac/errno.h> +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/ctype.h> +#include <ac/time.h> +#include <ac/unistd.h> +#include <ac/param.h> +#include <ac/dirent.h> + +#include "ldap-int.h" + +#ifdef HAVE_TLS + +#include "ldap-tls.h" + +static tls_impl *tls_imp = &ldap_int_tls_impl; +#define HAS_TLS( sb ) ber_sockbuf_ctrl( sb, LBER_SB_OPT_HAS_IO, \ + (void *)tls_imp->ti_sbio ) + +#endif /* HAVE_TLS */ + +#ifdef LDAP_DEVEL +#define LDAP_USE_NON_BLOCKING_TLS +#endif /* LDAP_DEVEL */ + +/* RFC2459 minimum required set of supported attribute types + * in a certificate DN + */ +typedef struct oid_name { + struct berval oid; + struct berval name; +} oid_name; + +static oid_name oids[] = { + { BER_BVC("2.5.4.3"), BER_BVC("cn") }, + { BER_BVC("2.5.4.4"), BER_BVC("sn") }, + { BER_BVC("2.5.4.6"), BER_BVC("c") }, + { BER_BVC("2.5.4.7"), BER_BVC("l") }, + { BER_BVC("2.5.4.8"), BER_BVC("st") }, + { BER_BVC("2.5.4.10"), BER_BVC("o") }, + { BER_BVC("2.5.4.11"), BER_BVC("ou") }, + { BER_BVC("2.5.4.12"), BER_BVC("title") }, + { BER_BVC("2.5.4.41"), BER_BVC("name") }, + { BER_BVC("2.5.4.42"), BER_BVC("givenName") }, + { BER_BVC("2.5.4.43"), BER_BVC("initials") }, + { BER_BVC("2.5.4.44"), BER_BVC("generationQualifier") }, + { BER_BVC("2.5.4.46"), BER_BVC("dnQualifier") }, + { BER_BVC("1.2.840.113549.1.9.1"), BER_BVC("email") }, + { BER_BVC("0.9.2342.19200300.100.1.25"), BER_BVC("dc") }, + { BER_BVNULL, BER_BVNULL } +}; + +#ifdef HAVE_TLS + +void +ldap_pvt_tls_ctx_free ( void *c ) +{ + if ( !c ) return; + tls_imp->ti_ctx_free( c ); +} + +static void +tls_ctx_ref( tls_ctx *ctx ) +{ + if ( !ctx ) return; + + tls_imp->ti_ctx_ref( ctx ); +} + +#ifdef LDAP_R_COMPILE +/* + * an extra mutex for the default ctx. + */ +static ldap_pvt_thread_mutex_t tls_def_ctx_mutex; +#endif + +void +ldap_int_tls_destroy( struct ldapoptions *lo ) +{ + if ( lo->ldo_tls_ctx ) { + ldap_pvt_tls_ctx_free( lo->ldo_tls_ctx ); + lo->ldo_tls_ctx = NULL; + } + + if ( lo->ldo_tls_certfile ) { + LDAP_FREE( lo->ldo_tls_certfile ); + lo->ldo_tls_certfile = NULL; + } + if ( lo->ldo_tls_keyfile ) { + LDAP_FREE( lo->ldo_tls_keyfile ); + lo->ldo_tls_keyfile = NULL; + } + if ( lo->ldo_tls_dhfile ) { + LDAP_FREE( lo->ldo_tls_dhfile ); + lo->ldo_tls_dhfile = NULL; + } + if ( lo->ldo_tls_ecname ) { + LDAP_FREE( lo->ldo_tls_ecname ); + lo->ldo_tls_ecname = NULL; + } + if ( lo->ldo_tls_cacertfile ) { + LDAP_FREE( lo->ldo_tls_cacertfile ); + lo->ldo_tls_cacertfile = NULL; + } + if ( lo->ldo_tls_cacertdir ) { + LDAP_FREE( lo->ldo_tls_cacertdir ); + lo->ldo_tls_cacertdir = NULL; + } + if ( lo->ldo_tls_ciphersuite ) { + LDAP_FREE( lo->ldo_tls_ciphersuite ); + lo->ldo_tls_ciphersuite = NULL; + } + if ( lo->ldo_tls_crlfile ) { + LDAP_FREE( lo->ldo_tls_crlfile ); + lo->ldo_tls_crlfile = NULL; + } +} + +/* + * Tear down the TLS subsystem. Should only be called once. + */ +void +ldap_pvt_tls_destroy( void ) +{ + struct ldapoptions *lo = LDAP_INT_GLOBAL_OPT(); + + ldap_int_tls_destroy( lo ); + + tls_imp->ti_tls_destroy(); +} + +/* + * Initialize a particular TLS implementation. + * Called once per implementation. + */ +static int +tls_init(tls_impl *impl ) +{ + static int tls_initialized = 0; + + if ( !tls_initialized++ ) { +#ifdef LDAP_R_COMPILE + ldap_pvt_thread_mutex_init( &tls_def_ctx_mutex ); +#endif + } + + if ( impl->ti_inited++ ) return 0; + +#ifdef LDAP_R_COMPILE + impl->ti_thr_init(); +#endif + return impl->ti_tls_init(); +} + +/* + * Initialize TLS subsystem. Called once per implementation. + */ +int +ldap_pvt_tls_init( void ) +{ + return tls_init( tls_imp ); +} + +/* + * initialize a new TLS context + */ +static int +ldap_int_tls_init_ctx( struct ldapoptions *lo, int is_server ) +{ + int rc = 0; + tls_impl *ti = tls_imp; + struct ldaptls lts = lo->ldo_tls_info; + + if ( lo->ldo_tls_ctx ) + return 0; + + tls_init( ti ); + + if ( is_server && !lts.lt_certfile && !lts.lt_keyfile && + !lts.lt_cacertfile && !lts.lt_cacertdir ) { + /* minimum configuration not provided */ + return LDAP_NOT_SUPPORTED; + } + +#ifdef HAVE_EBCDIC + /* This ASCII/EBCDIC handling is a real pain! */ + if ( lts.lt_ciphersuite ) { + lts.lt_ciphersuite = LDAP_STRDUP( lts.lt_ciphersuite ); + __atoe( lts.lt_ciphersuite ); + } + if ( lts.lt_cacertfile ) { + lts.lt_cacertfile = LDAP_STRDUP( lts.lt_cacertfile ); + __atoe( lts.lt_cacertfile ); + } + if ( lts.lt_certfile ) { + lts.lt_certfile = LDAP_STRDUP( lts.lt_certfile ); + __atoe( lts.lt_certfile ); + } + if ( lts.lt_keyfile ) { + lts.lt_keyfile = LDAP_STRDUP( lts.lt_keyfile ); + __atoe( lts.lt_keyfile ); + } + if ( lts.lt_crlfile ) { + lts.lt_crlfile = LDAP_STRDUP( lts.lt_crlfile ); + __atoe( lts.lt_crlfile ); + } + if ( lts.lt_cacertdir ) { + lts.lt_cacertdir = LDAP_STRDUP( lts.lt_cacertdir ); + __atoe( lts.lt_cacertdir ); + } + if ( lts.lt_dhfile ) { + lts.lt_dhfile = LDAP_STRDUP( lts.lt_dhfile ); + __atoe( lts.lt_dhfile ); + } + if ( lts.lt_ecname ) { + lts.lt_ecname = LDAP_STRDUP( lts.lt_ecname ); + __atoe( lts.lt_ecname ); + } +#endif + lo->ldo_tls_ctx = ti->ti_ctx_new( lo ); + if ( lo->ldo_tls_ctx == NULL ) { + Debug( LDAP_DEBUG_ANY, + "TLS: could not allocate default ctx.\n", + 0,0,0); + rc = -1; + goto error_exit; + } + + rc = ti->ti_ctx_init( lo, <s, is_server ); + +error_exit: + if ( rc < 0 && lo->ldo_tls_ctx != NULL ) { + ldap_pvt_tls_ctx_free( lo->ldo_tls_ctx ); + lo->ldo_tls_ctx = NULL; + } +#ifdef HAVE_EBCDIC + LDAP_FREE( lts.lt_ciphersuite ); + LDAP_FREE( lts.lt_cacertfile ); + LDAP_FREE( lts.lt_certfile ); + LDAP_FREE( lts.lt_keyfile ); + LDAP_FREE( lts.lt_crlfile ); + LDAP_FREE( lts.lt_cacertdir ); + LDAP_FREE( lts.lt_dhfile ); + LDAP_FREE( lts.lt_ecname ); +#endif + return rc; +} + +/* + * initialize the default context + */ +int +ldap_pvt_tls_init_def_ctx( int is_server ) +{ + struct ldapoptions *lo = LDAP_INT_GLOBAL_OPT(); + int rc; + LDAP_MUTEX_LOCK( &tls_def_ctx_mutex ); + rc = ldap_int_tls_init_ctx( lo, is_server ); + LDAP_MUTEX_UNLOCK( &tls_def_ctx_mutex ); + return rc; +} + +static tls_session * +alloc_handle( void *ctx_arg, int is_server ) +{ + tls_ctx *ctx; + tls_session *ssl; + + if ( ctx_arg ) { + ctx = ctx_arg; + } else { + struct ldapoptions *lo = LDAP_INT_GLOBAL_OPT(); + if ( ldap_pvt_tls_init_def_ctx( is_server ) < 0 ) return NULL; + ctx = lo->ldo_tls_ctx; + } + + ssl = tls_imp->ti_session_new( ctx, is_server ); + if ( ssl == NULL ) { + Debug( LDAP_DEBUG_ANY,"TLS: can't create ssl handle.\n",0,0,0); + return NULL; + } + return ssl; +} + +static int +update_flags( Sockbuf *sb, tls_session * ssl, int rc ) +{ + sb->sb_trans_needs_read = 0; + sb->sb_trans_needs_write = 0; + + return tls_imp->ti_session_upflags( sb, ssl, rc ); +} + +/* + * Call this to do a TLS connect on a sockbuf. ctx_arg can be + * a SSL_CTX * or NULL, in which case the default ctx is used. + * + * Return value: + * + * 0 - Success. Connection is ready for communication. + * <0 - Error. Can't create a TLS stream. + * >0 - Partial success. + * Do a select (using information from lber_pvt_sb_needs_{read,write} + * and call again. + */ + +static int +ldap_int_tls_connect( LDAP *ld, LDAPConn *conn, const char *host ) +{ + Sockbuf *sb = conn->lconn_sb; + int err; + tls_session *ssl = NULL; + + if ( HAS_TLS( sb )) { + ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_SSL, (void *)&ssl ); + } else { + struct ldapoptions *lo; + tls_ctx *ctx; + + ctx = ld->ld_options.ldo_tls_ctx; + + ssl = alloc_handle( ctx, 0 ); + + if ( ssl == NULL ) return -1; + +#ifdef LDAP_DEBUG + ber_sockbuf_add_io( sb, &ber_sockbuf_io_debug, + LBER_SBIOD_LEVEL_TRANSPORT, (void *)"tls_" ); +#endif + ber_sockbuf_add_io( sb, tls_imp->ti_sbio, + LBER_SBIOD_LEVEL_TRANSPORT, (void *)ssl ); + + lo = LDAP_INT_GLOBAL_OPT(); + if( ctx == NULL ) { + ctx = lo->ldo_tls_ctx; + ld->ld_options.ldo_tls_ctx = ctx; + tls_ctx_ref( ctx ); + } + if ( ld->ld_options.ldo_tls_connect_cb ) + ld->ld_options.ldo_tls_connect_cb( ld, ssl, ctx, + ld->ld_options.ldo_tls_connect_arg ); + if ( lo && lo->ldo_tls_connect_cb && lo->ldo_tls_connect_cb != + ld->ld_options.ldo_tls_connect_cb ) + lo->ldo_tls_connect_cb( ld, ssl, ctx, lo->ldo_tls_connect_arg ); + } + + err = tls_imp->ti_session_connect( ld, ssl ); + +#ifdef HAVE_WINSOCK + errno = WSAGetLastError(); +#endif + + if ( err == 0 ) { + err = ldap_pvt_tls_check_hostname( ld, ssl, host ); + } + + if ( err < 0 ) + { + char buf[256], *msg; + if ( update_flags( sb, ssl, err )) { + return 1; + } + + msg = tls_imp->ti_session_errmsg( ssl, err, buf, sizeof(buf) ); + if ( msg ) { + if ( ld->ld_error ) { + LDAP_FREE( ld->ld_error ); + } + ld->ld_error = LDAP_STRDUP( msg ); +#ifdef HAVE_EBCDIC + if ( ld->ld_error ) __etoa(ld->ld_error); +#endif + } + + Debug( LDAP_DEBUG_ANY,"TLS: can't connect: %s.\n", + ld->ld_error ? ld->ld_error : "" ,0,0); + + ber_sockbuf_remove_io( sb, tls_imp->ti_sbio, + LBER_SBIOD_LEVEL_TRANSPORT ); +#ifdef LDAP_DEBUG + ber_sockbuf_remove_io( sb, &ber_sockbuf_io_debug, + LBER_SBIOD_LEVEL_TRANSPORT ); +#endif + return -1; + } + + return 0; +} + +/* + * Call this to do a TLS accept on a sockbuf. + * Everything else is the same as with tls_connect. + */ +int +ldap_pvt_tls_accept( Sockbuf *sb, void *ctx_arg ) +{ + int err; + tls_session *ssl = NULL; + + if ( HAS_TLS( sb )) { + ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_SSL, (void *)&ssl ); + } else { + ssl = alloc_handle( ctx_arg, 1 ); + if ( ssl == NULL ) return -1; + +#ifdef LDAP_DEBUG + ber_sockbuf_add_io( sb, &ber_sockbuf_io_debug, + LBER_SBIOD_LEVEL_TRANSPORT, (void *)"tls_" ); +#endif + ber_sockbuf_add_io( sb, tls_imp->ti_sbio, + LBER_SBIOD_LEVEL_TRANSPORT, (void *)ssl ); + } + + err = tls_imp->ti_session_accept( ssl ); + +#ifdef HAVE_WINSOCK + errno = WSAGetLastError(); +#endif + + if ( err < 0 ) + { + if ( update_flags( sb, ssl, err )) return 1; + + if ( DebugTest( LDAP_DEBUG_ANY ) ) { + char buf[256], *msg; + msg = tls_imp->ti_session_errmsg( ssl, err, buf, sizeof(buf) ); + Debug( LDAP_DEBUG_ANY,"TLS: can't accept: %s.\n", + msg ? msg : "(unknown)", 0, 0 ); + } + + ber_sockbuf_remove_io( sb, tls_imp->ti_sbio, + LBER_SBIOD_LEVEL_TRANSPORT ); +#ifdef LDAP_DEBUG + ber_sockbuf_remove_io( sb, &ber_sockbuf_io_debug, + LBER_SBIOD_LEVEL_TRANSPORT ); +#endif + return -1; + } + return 0; +} + +int +ldap_pvt_tls_inplace ( Sockbuf *sb ) +{ + return HAS_TLS( sb ) ? 1 : 0; +} + +int +ldap_tls_inplace( LDAP *ld ) +{ + Sockbuf *sb = NULL; + + if ( ld->ld_defconn && ld->ld_defconn->lconn_sb ) { + sb = ld->ld_defconn->lconn_sb; + + } else if ( ld->ld_sb ) { + sb = ld->ld_sb; + + } else { + return 0; + } + + return ldap_pvt_tls_inplace( sb ); +} + +int +ldap_pvt_tls_get_peer_dn( void *s, struct berval *dn, + LDAPDN_rewrite_dummy *func, unsigned flags ) +{ + tls_session *session = s; + struct berval bvdn; + int rc; + + rc = tls_imp->ti_session_peer_dn( session, &bvdn ); + if ( rc ) return rc; + + rc = ldap_X509dn2bv( &bvdn, dn, + (LDAPDN_rewrite_func *)func, flags); + return rc; +} + +int +ldap_pvt_tls_check_hostname( LDAP *ld, void *s, const char *name_in ) +{ + tls_session *session = s; + + if (ld->ld_options.ldo_tls_require_cert != LDAP_OPT_X_TLS_NEVER && + ld->ld_options.ldo_tls_require_cert != LDAP_OPT_X_TLS_ALLOW) { + ld->ld_errno = tls_imp->ti_session_chkhost( ld, session, name_in ); + if (ld->ld_errno != LDAP_SUCCESS) { + return ld->ld_errno; + } + } + + return LDAP_SUCCESS; +} + +int +ldap_int_tls_config( LDAP *ld, int option, const char *arg ) +{ + int i; + + switch( option ) { + case LDAP_OPT_X_TLS_CACERTFILE: + case LDAP_OPT_X_TLS_CACERTDIR: + case LDAP_OPT_X_TLS_CERTFILE: + case LDAP_OPT_X_TLS_KEYFILE: + case LDAP_OPT_X_TLS_RANDOM_FILE: + case LDAP_OPT_X_TLS_CIPHER_SUITE: + case LDAP_OPT_X_TLS_DHFILE: + case LDAP_OPT_X_TLS_ECNAME: + case LDAP_OPT_X_TLS_CRLFILE: /* GnuTLS only */ + return ldap_pvt_tls_set_option( ld, option, (void *) arg ); + + case LDAP_OPT_X_TLS_REQUIRE_CERT: + case LDAP_OPT_X_TLS_REQUIRE_SAN: + case LDAP_OPT_X_TLS: + i = -1; + if ( strcasecmp( arg, "never" ) == 0 ) { + i = LDAP_OPT_X_TLS_NEVER ; + + } else if ( strcasecmp( arg, "demand" ) == 0 ) { + i = LDAP_OPT_X_TLS_DEMAND ; + + } else if ( strcasecmp( arg, "allow" ) == 0 ) { + i = LDAP_OPT_X_TLS_ALLOW ; + + } else if ( strcasecmp( arg, "try" ) == 0 ) { + i = LDAP_OPT_X_TLS_TRY ; + + } else if ( ( strcasecmp( arg, "hard" ) == 0 ) || + ( strcasecmp( arg, "on" ) == 0 ) || + ( strcasecmp( arg, "yes" ) == 0) || + ( strcasecmp( arg, "true" ) == 0 ) ) + { + i = LDAP_OPT_X_TLS_HARD ; + } + + if (i >= 0) { + return ldap_pvt_tls_set_option( ld, option, &i ); + } + return -1; + case LDAP_OPT_X_TLS_PROTOCOL_MIN: { + char *next; + long l; + l = strtol( arg, &next, 10 ); + if ( l < 0 || l > 0xff || next == arg || + ( *next != '\0' && *next != '.' ) ) + return -1; + i = l << 8; + if (*next == '.') { + arg = next + 1; + l = strtol( arg, &next, 10 ); + if ( l < 0 || l > 0xff || next == arg || *next != '\0' ) + return -1; + i += l; + } + return ldap_pvt_tls_set_option( ld, option, &i ); + } +#ifdef HAVE_OPENSSL_CRL + case LDAP_OPT_X_TLS_CRLCHECK: /* OpenSSL only */ + i = -1; + if ( strcasecmp( arg, "none" ) == 0 ) { + i = LDAP_OPT_X_TLS_CRL_NONE ; + } else if ( strcasecmp( arg, "peer" ) == 0 ) { + i = LDAP_OPT_X_TLS_CRL_PEER ; + } else if ( strcasecmp( arg, "all" ) == 0 ) { + i = LDAP_OPT_X_TLS_CRL_ALL ; + } + if (i >= 0) { + return ldap_pvt_tls_set_option( ld, option, &i ); + } + return -1; +#endif + } + return -1; +} + +int +ldap_pvt_tls_get_option( LDAP *ld, int option, void *arg ) +{ + struct ldapoptions *lo; + + if( option == LDAP_OPT_X_TLS_PACKAGE ) { + *(char **)arg = LDAP_STRDUP( tls_imp->ti_name ); + return 0; + } + + if( ld != NULL ) { + assert( LDAP_VALID( ld ) ); + + if( !LDAP_VALID( ld ) ) { + return LDAP_OPT_ERROR; + } + + lo = &ld->ld_options; + + } else { + /* Get pointer to global option structure */ + lo = LDAP_INT_GLOBAL_OPT(); + if ( lo == NULL ) { + return LDAP_NO_MEMORY; + } + } + + switch( option ) { + case LDAP_OPT_X_TLS: + *(int *)arg = lo->ldo_tls_mode; + break; + case LDAP_OPT_X_TLS_CTX: + *(void **)arg = lo->ldo_tls_ctx; + if ( lo->ldo_tls_ctx ) { + tls_ctx_ref( lo->ldo_tls_ctx ); + } + break; + case LDAP_OPT_X_TLS_CACERTFILE: + *(char **)arg = lo->ldo_tls_cacertfile ? + LDAP_STRDUP( lo->ldo_tls_cacertfile ) : NULL; + break; + case LDAP_OPT_X_TLS_CACERTDIR: + *(char **)arg = lo->ldo_tls_cacertdir ? + LDAP_STRDUP( lo->ldo_tls_cacertdir ) : NULL; + break; + case LDAP_OPT_X_TLS_CERTFILE: + *(char **)arg = lo->ldo_tls_certfile ? + LDAP_STRDUP( lo->ldo_tls_certfile ) : NULL; + break; + case LDAP_OPT_X_TLS_KEYFILE: + *(char **)arg = lo->ldo_tls_keyfile ? + LDAP_STRDUP( lo->ldo_tls_keyfile ) : NULL; + break; + case LDAP_OPT_X_TLS_DHFILE: + *(char **)arg = lo->ldo_tls_dhfile ? + LDAP_STRDUP( lo->ldo_tls_dhfile ) : NULL; + break; + case LDAP_OPT_X_TLS_ECNAME: + *(char **)arg = lo->ldo_tls_ecname ? + LDAP_STRDUP( lo->ldo_tls_ecname ) : NULL; + break; + case LDAP_OPT_X_TLS_CRLFILE: /* GnuTLS only */ + *(char **)arg = lo->ldo_tls_crlfile ? + LDAP_STRDUP( lo->ldo_tls_crlfile ) : NULL; + break; + case LDAP_OPT_X_TLS_REQUIRE_CERT: + *(int *)arg = lo->ldo_tls_require_cert; + break; + case LDAP_OPT_X_TLS_REQUIRE_SAN: + *(int *)arg = lo->ldo_tls_require_san; + break; +#ifdef HAVE_OPENSSL_CRL + case LDAP_OPT_X_TLS_CRLCHECK: /* OpenSSL only */ + *(int *)arg = lo->ldo_tls_crlcheck; + break; +#endif + case LDAP_OPT_X_TLS_CIPHER_SUITE: + *(char **)arg = lo->ldo_tls_ciphersuite ? + LDAP_STRDUP( lo->ldo_tls_ciphersuite ) : NULL; + break; + case LDAP_OPT_X_TLS_PROTOCOL_MIN: + *(int *)arg = lo->ldo_tls_protocol_min; + break; + case LDAP_OPT_X_TLS_RANDOM_FILE: + *(char **)arg = lo->ldo_tls_randfile ? + LDAP_STRDUP( lo->ldo_tls_randfile ) : NULL; + break; + case LDAP_OPT_X_TLS_SSL_CTX: { + void *retval = 0; + if ( ld != NULL ) { + LDAPConn *conn = ld->ld_defconn; + if ( conn != NULL ) { + Sockbuf *sb = conn->lconn_sb; + retval = ldap_pvt_tls_sb_ctx( sb ); + } + } + *(void **)arg = retval; + break; + } + case LDAP_OPT_X_TLS_CONNECT_CB: + *(LDAP_TLS_CONNECT_CB **)arg = lo->ldo_tls_connect_cb; + break; + case LDAP_OPT_X_TLS_CONNECT_ARG: + *(void **)arg = lo->ldo_tls_connect_arg; + break; + default: + return -1; + } + return 0; +} + +int +ldap_pvt_tls_set_option( LDAP *ld, int option, void *arg ) +{ + struct ldapoptions *lo; + + if( ld != NULL ) { + assert( LDAP_VALID( ld ) ); + + if( !LDAP_VALID( ld ) ) { + return LDAP_OPT_ERROR; + } + + lo = &ld->ld_options; + + } else { + /* Get pointer to global option structure */ + lo = LDAP_INT_GLOBAL_OPT(); + if ( lo == NULL ) { + return LDAP_NO_MEMORY; + } + } + + switch( option ) { + case LDAP_OPT_X_TLS: + if ( !arg ) return -1; + + switch( *(int *) arg ) { + case LDAP_OPT_X_TLS_NEVER: + case LDAP_OPT_X_TLS_DEMAND: + case LDAP_OPT_X_TLS_ALLOW: + case LDAP_OPT_X_TLS_TRY: + case LDAP_OPT_X_TLS_HARD: + if (lo != NULL) { + lo->ldo_tls_mode = *(int *)arg; + } + + return 0; + } + return -1; + + case LDAP_OPT_X_TLS_CTX: + if ( lo->ldo_tls_ctx ) + ldap_pvt_tls_ctx_free( lo->ldo_tls_ctx ); + lo->ldo_tls_ctx = arg; + tls_ctx_ref( lo->ldo_tls_ctx ); + return 0; + case LDAP_OPT_X_TLS_CONNECT_CB: + lo->ldo_tls_connect_cb = (LDAP_TLS_CONNECT_CB *)arg; + return 0; + case LDAP_OPT_X_TLS_CONNECT_ARG: + lo->ldo_tls_connect_arg = arg; + return 0; + case LDAP_OPT_X_TLS_CACERTFILE: + if ( lo->ldo_tls_cacertfile ) LDAP_FREE( lo->ldo_tls_cacertfile ); + lo->ldo_tls_cacertfile = (arg && *(char *)arg) ? LDAP_STRDUP( (char *) arg ) : NULL; + return 0; + case LDAP_OPT_X_TLS_CACERTDIR: + if ( lo->ldo_tls_cacertdir ) LDAP_FREE( lo->ldo_tls_cacertdir ); + lo->ldo_tls_cacertdir = (arg && *(char *)arg) ? LDAP_STRDUP( (char *) arg ) : NULL; + return 0; + case LDAP_OPT_X_TLS_CERTFILE: + if ( lo->ldo_tls_certfile ) LDAP_FREE( lo->ldo_tls_certfile ); + lo->ldo_tls_certfile = (arg && *(char *)arg) ? LDAP_STRDUP( (char *) arg ) : NULL; + return 0; + case LDAP_OPT_X_TLS_KEYFILE: + if ( lo->ldo_tls_keyfile ) LDAP_FREE( lo->ldo_tls_keyfile ); + lo->ldo_tls_keyfile = (arg && *(char *)arg) ? LDAP_STRDUP( (char *) arg ) : NULL; + return 0; + case LDAP_OPT_X_TLS_DHFILE: + if ( lo->ldo_tls_dhfile ) LDAP_FREE( lo->ldo_tls_dhfile ); + lo->ldo_tls_dhfile = (arg && *(char *)arg) ? LDAP_STRDUP( (char *) arg ) : NULL; + return 0; + case LDAP_OPT_X_TLS_ECNAME: + if ( lo->ldo_tls_ecname ) LDAP_FREE( lo->ldo_tls_ecname ); + lo->ldo_tls_ecname = (arg && *(char *)arg) ? LDAP_STRDUP( (char *) arg ) : NULL; + return 0; + case LDAP_OPT_X_TLS_CRLFILE: /* GnuTLS only */ + if ( lo->ldo_tls_crlfile ) LDAP_FREE( lo->ldo_tls_crlfile ); + lo->ldo_tls_crlfile = (arg && *(char *)arg) ? LDAP_STRDUP( (char *) arg ) : NULL; + return 0; + case LDAP_OPT_X_TLS_REQUIRE_CERT: + if ( !arg ) return -1; + switch( *(int *) arg ) { + case LDAP_OPT_X_TLS_NEVER: + case LDAP_OPT_X_TLS_DEMAND: + case LDAP_OPT_X_TLS_ALLOW: + case LDAP_OPT_X_TLS_TRY: + case LDAP_OPT_X_TLS_HARD: + lo->ldo_tls_require_cert = * (int *) arg; + return 0; + } + return -1; + case LDAP_OPT_X_TLS_REQUIRE_SAN: + if ( !arg ) return -1; + switch( *(int *) arg ) { + case LDAP_OPT_X_TLS_NEVER: + case LDAP_OPT_X_TLS_DEMAND: + case LDAP_OPT_X_TLS_ALLOW: + case LDAP_OPT_X_TLS_TRY: + case LDAP_OPT_X_TLS_HARD: + lo->ldo_tls_require_san = * (int *) arg; + return 0; + } + return -1; +#ifdef HAVE_OPENSSL_CRL + case LDAP_OPT_X_TLS_CRLCHECK: /* OpenSSL only */ + if ( !arg ) return -1; + switch( *(int *) arg ) { + case LDAP_OPT_X_TLS_CRL_NONE: + case LDAP_OPT_X_TLS_CRL_PEER: + case LDAP_OPT_X_TLS_CRL_ALL: + lo->ldo_tls_crlcheck = * (int *) arg; + return 0; + } + return -1; +#endif + case LDAP_OPT_X_TLS_CIPHER_SUITE: + if ( lo->ldo_tls_ciphersuite ) LDAP_FREE( lo->ldo_tls_ciphersuite ); + lo->ldo_tls_ciphersuite = (arg && *(char *)arg) ? LDAP_STRDUP( (char *) arg ) : NULL; + return 0; + + case LDAP_OPT_X_TLS_PROTOCOL_MIN: + if ( !arg ) return -1; + lo->ldo_tls_protocol_min = *(int *)arg; + return 0; + case LDAP_OPT_X_TLS_RANDOM_FILE: + if ( ld != NULL ) + return -1; + if ( lo->ldo_tls_randfile ) LDAP_FREE (lo->ldo_tls_randfile ); + lo->ldo_tls_randfile = (arg && *(char *)arg) ? LDAP_STRDUP( (char *) arg ) : NULL; + break; + case LDAP_OPT_X_TLS_NEWCTX: + if ( !arg ) return -1; + if ( lo->ldo_tls_ctx ) + ldap_pvt_tls_ctx_free( lo->ldo_tls_ctx ); + lo->ldo_tls_ctx = NULL; + return ldap_int_tls_init_ctx( lo, *(int *)arg ); + default: + return -1; + } + return 0; +} + +int +ldap_int_tls_start ( LDAP *ld, LDAPConn *conn, LDAPURLDesc *srv ) +{ + Sockbuf *sb; + char *host; + void *ssl; + int ret, async; +#ifdef LDAP_USE_NON_BLOCKING_TLS + struct timeval start_time_tv, tv, tv0; + ber_socket_t sd = AC_SOCKET_ERROR; +#endif /* LDAP_USE_NON_BLOCKING_TLS */ + + if ( !conn ) + return LDAP_PARAM_ERROR; + + sb = conn->lconn_sb; + if( srv ) { + host = srv->lud_host; + } else { + host = conn->lconn_server->lud_host; + } + + /* avoid NULL host */ + if( host == NULL ) { + host = "localhost"; + } + + (void) tls_init( tls_imp ); + +#ifdef LDAP_USE_NON_BLOCKING_TLS + /* + * Use non-blocking io during SSL Handshake when a timeout is configured + */ + async = LDAP_BOOL_GET( &ld->ld_options, LDAP_BOOL_CONNECT_ASYNC ); + if ( ld->ld_options.ldo_tm_net.tv_sec >= 0 ) { + if ( !async ) { + /* if async, this has already been set */ + ber_sockbuf_ctrl( sb, LBER_SB_OPT_SET_NONBLOCK, (void*)1 ); + } + ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, &sd ); + tv = ld->ld_options.ldo_tm_net; + tv0 = tv; +#ifdef HAVE_GETTIMEOFDAY + gettimeofday( &start_time_tv, NULL ); +#else /* ! HAVE_GETTIMEOFDAY */ + time( &start_time_tv.tv_sec ); + start_time_tv.tv_usec = 0; +#endif /* ! HAVE_GETTIMEOFDAY */ + } + +#endif /* LDAP_USE_NON_BLOCKING_TLS */ + + ld->ld_errno = LDAP_SUCCESS; + ret = ldap_int_tls_connect( ld, conn, host ); + + /* this mainly only happens for non-blocking io + * but can also happen when the handshake is too + * big for a single network message. + */ + while ( ret > 0 ) { +#ifdef LDAP_USE_NON_BLOCKING_TLS + if ( async ) { + struct timeval curr_time_tv, delta_tv; + int wr=0; + + if ( sb->sb_trans_needs_read ) { + wr=0; + } else if ( sb->sb_trans_needs_write ) { + wr=1; + } + Debug( LDAP_DEBUG_TRACE, "ldap_int_tls_start: ldap_int_tls_connect needs %s\n", + wr ? "write": "read", 0, 0 ); + + /* This is mostly copied from result.c:wait4msg(), should + * probably be moved into a separate function */ +#ifdef HAVE_GETTIMEOFDAY + gettimeofday( &curr_time_tv, NULL ); +#else /* ! HAVE_GETTIMEOFDAY */ + time( &curr_time_tv.tv_sec ); + curr_time_tv.tv_usec = 0; +#endif /* ! HAVE_GETTIMEOFDAY */ + + /* delta = curr - start */ + delta_tv.tv_sec = curr_time_tv.tv_sec - start_time_tv.tv_sec; + delta_tv.tv_usec = curr_time_tv.tv_usec - start_time_tv.tv_usec; + if ( delta_tv.tv_usec < 0 ) { + delta_tv.tv_sec--; + delta_tv.tv_usec += 1000000; + } + + /* tv0 < delta ? */ + if ( ( tv0.tv_sec < delta_tv.tv_sec ) || + ( ( tv0.tv_sec == delta_tv.tv_sec ) && + ( tv0.tv_usec < delta_tv.tv_usec ) ) ) + { + ret = -1; + ld->ld_errno = LDAP_TIMEOUT; + break; + } + /* timeout -= delta_time */ + tv0.tv_sec -= delta_tv.tv_sec; + tv0.tv_usec -= delta_tv.tv_usec; + if ( tv0.tv_usec < 0 ) { + tv0.tv_sec--; + tv0.tv_usec += 1000000; + } + start_time_tv.tv_sec = curr_time_tv.tv_sec; + start_time_tv.tv_usec = curr_time_tv.tv_usec; + tv = tv0; + Debug( LDAP_DEBUG_TRACE, "ldap_int_tls_start: ld %p %ld s %ld us to go\n", + (void *)ld, (long) tv.tv_sec, (long) tv.tv_usec ); + ret = ldap_int_poll( ld, sd, &tv, wr); + if ( ret < 0 ) { + ld->ld_errno = LDAP_TIMEOUT; + break; + } + } +#endif /* LDAP_USE_NON_BLOCKING_TLS */ + ret = ldap_int_tls_connect( ld, conn, host ); + } + + if ( ret < 0 ) { + if ( ld->ld_errno == LDAP_SUCCESS ) + ld->ld_errno = LDAP_CONNECT_ERROR; + return (ld->ld_errno); + } + + return LDAP_SUCCESS; +} + +void * +ldap_pvt_tls_sb_ctx( Sockbuf *sb ) +{ + void *p = NULL; + + ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_SSL, (void *)&p ); + return p; +} + +int +ldap_pvt_tls_get_strength( void *s ) +{ + tls_session *session = s; + + return tls_imp->ti_session_strength( session ); +} + +int +ldap_pvt_tls_get_my_dn( void *s, struct berval *dn, LDAPDN_rewrite_dummy *func, unsigned flags ) +{ + tls_session *session = s; + struct berval der_dn; + int rc; + + rc = tls_imp->ti_session_my_dn( session, &der_dn ); + if ( rc == LDAP_SUCCESS ) + rc = ldap_X509dn2bv(&der_dn, dn, (LDAPDN_rewrite_func *)func, flags ); + return rc; +} +#endif /* HAVE_TLS */ + +int +ldap_start_tls( LDAP *ld, + LDAPControl **serverctrls, + LDAPControl **clientctrls, + int *msgidp ) +{ + return ldap_extended_operation( ld, LDAP_EXOP_START_TLS, + NULL, serverctrls, clientctrls, msgidp ); +} + +int +ldap_install_tls( LDAP *ld ) +{ +#ifndef HAVE_TLS + return LDAP_NOT_SUPPORTED; +#else + if ( ldap_tls_inplace( ld ) ) { + return LDAP_LOCAL_ERROR; + } + + return ldap_int_tls_start( ld, ld->ld_defconn, NULL ); +#endif +} + +int +ldap_start_tls_s ( LDAP *ld, + LDAPControl **serverctrls, + LDAPControl **clientctrls ) +{ +#ifndef HAVE_TLS + return LDAP_NOT_SUPPORTED; +#else + int rc; + char *rspoid = NULL; + struct berval *rspdata = NULL; + + /* XXYYZ: this initiates operation only on default connection! */ + + if ( ldap_tls_inplace( ld ) ) { + return LDAP_LOCAL_ERROR; + } + + rc = ldap_extended_operation_s( ld, LDAP_EXOP_START_TLS, + NULL, serverctrls, clientctrls, &rspoid, &rspdata ); + + if ( rspoid != NULL ) { + LDAP_FREE(rspoid); + } + + if ( rspdata != NULL ) { + ber_bvfree( rspdata ); + } + + if ( rc == LDAP_SUCCESS ) { + rc = ldap_int_tls_start( ld, ld->ld_defconn, NULL ); + } + + return rc; +#endif +} + +/* These tags probably all belong in lber.h, but they're + * not normally encountered when processing LDAP, so maybe + * they belong somewhere else instead. + */ + +#define LBER_TAG_OID ((ber_tag_t) 0x06UL) + +/* Tags for string types used in a DirectoryString. + * + * Note that IA5string is not one of the defined choices for + * DirectoryString in X.520, but it gets used for email AVAs. + */ +#define LBER_TAG_UTF8 ((ber_tag_t) 0x0cUL) +#define LBER_TAG_PRINTABLE ((ber_tag_t) 0x13UL) +#define LBER_TAG_TELETEX ((ber_tag_t) 0x14UL) +#define LBER_TAG_IA5 ((ber_tag_t) 0x16UL) +#define LBER_TAG_UNIVERSAL ((ber_tag_t) 0x1cUL) +#define LBER_TAG_BMP ((ber_tag_t) 0x1eUL) + +static oid_name * +find_oid( struct berval *oid ) +{ + int i; + + for ( i=0; !BER_BVISNULL( &oids[i].oid ); i++ ) { + if ( oids[i].oid.bv_len != oid->bv_len ) continue; + if ( !strcmp( oids[i].oid.bv_val, oid->bv_val )) + return &oids[i]; + } + return NULL; +} + +/* Converts BER Bitstring value to LDAP BitString value (RFC4517) + * + * berValue : IN + * rfc4517Value: OUT + * + * berValue and ldapValue should not be NULL + */ + +#define BITS_PER_BYTE 8 +#define SQUOTE_LENGTH 1 +#define B_CHAR_LENGTH 1 +#define STR_OVERHEAD (2*SQUOTE_LENGTH + B_CHAR_LENGTH) + +static int +der_to_ldap_BitString (struct berval *berValue, + struct berval *ldapValue) +{ + ber_len_t bitPadding=0; + ber_len_t bits, maxBits; + char *tmpStr; + unsigned char byte; + ber_len_t bitLength; + ber_len_t valLen; + unsigned char* valPtr; + + ldapValue->bv_len=0; + ldapValue->bv_val=NULL; + + /* Gets padding and points to binary data */ + valLen=berValue->bv_len; + valPtr=(unsigned char*)berValue->bv_val; + if (valLen) { + bitPadding=(ber_len_t)(valPtr[0]); + valLen--; + valPtr++; + } + /* If Block is non DER encoding fixes to DER encoding */ + if (bitPadding >= BITS_PER_BYTE) { + if (valLen*BITS_PER_BYTE > bitPadding ) { + valLen-=(bitPadding/BITS_PER_BYTE); + bitPadding%=BITS_PER_BYTE; + } else { + valLen=0; + bitPadding=0; + } + } + /* Just in case bad encoding */ + if (valLen*BITS_PER_BYTE < bitPadding ) { + bitPadding=0; + valLen=0; + } + + /* Gets buffer to hold RFC4517 Bit String format */ + bitLength=valLen*BITS_PER_BYTE-bitPadding; + tmpStr=LDAP_MALLOC(bitLength + STR_OVERHEAD + 1); + + if (!tmpStr) + return LDAP_NO_MEMORY; + + ldapValue->bv_val=tmpStr; + ldapValue->bv_len=bitLength + STR_OVERHEAD; + + /* Formatting in '*binary-digit'B format */ + maxBits=BITS_PER_BYTE; + *tmpStr++ ='\''; + while(valLen) { + byte=*valPtr; + if (valLen==1) + maxBits-=bitPadding; + for (bits=0; bits<maxBits; bits++) { + if (0x80 & byte) + *tmpStr='1'; + else + *tmpStr='0'; + tmpStr++; + byte<<=1; + } + valPtr++; + valLen--; + } + *tmpStr++ ='\''; + *tmpStr++ ='B'; + *tmpStr=0; + + return LDAP_SUCCESS; +} + +/* Convert a structured DN from an X.509 certificate into an LDAPV3 DN. + * x509_name must be raw DER. If func is non-NULL, the + * constructed DN will use numeric OIDs to identify attributeTypes, + * and the func() will be invoked to rewrite the DN with the given + * flags. + * + * Otherwise the DN will use shortNames from a hardcoded table. + */ +int +ldap_X509dn2bv( void *x509_name, struct berval *bv, LDAPDN_rewrite_func *func, + unsigned flags ) +{ + LDAPDN newDN; + LDAPRDN newRDN; + LDAPAVA *newAVA, *baseAVA; + BerElementBuffer berbuf; + BerElement *ber = (BerElement *)&berbuf; + char oids[8192], *oidptr = oids, *oidbuf = NULL; + void *ptrs[2048]; + char *dn_end, *rdn_end; + int i, navas, nrdns, rc = LDAP_SUCCESS; + size_t dnsize, oidrem = sizeof(oids), oidsize = 0; + int csize; + ber_tag_t tag; + ber_len_t len; + oid_name *oidname; + + struct berval Oid, Val, oid2, *in = x509_name; + + assert( bv != NULL ); + + bv->bv_len = 0; + bv->bv_val = NULL; + + navas = 0; + nrdns = 0; + + /* A DN is a SEQUENCE of RDNs. An RDN is a SET of AVAs. + * An AVA is a SEQUENCE of attr and value. + * Count the number of AVAs and RDNs + */ + ber_init2( ber, in, LBER_USE_DER ); + tag = ber_peek_tag( ber, &len ); + if ( tag != LBER_SEQUENCE ) + return LDAP_DECODING_ERROR; + + for ( tag = ber_first_element( ber, &len, &dn_end ); + tag == LBER_SET; + tag = ber_next_element( ber, &len, dn_end )) { + nrdns++; + for ( tag = ber_first_element( ber, &len, &rdn_end ); + tag == LBER_SEQUENCE; + tag = ber_next_element( ber, &len, rdn_end )) { + if ( rdn_end > dn_end ) + return LDAP_DECODING_ERROR; + tag = ber_skip_tag( ber, &len ); + ber_skip_data( ber, len ); + navas++; + } + } + + /* Rewind and prepare to extract */ + ber_rewind( ber ); + tag = ber_first_element( ber, &len, &dn_end ); + if ( tag != LBER_SET ) + return LDAP_DECODING_ERROR; + + /* Allocate the DN/RDN/AVA stuff as a single block */ + dnsize = sizeof(LDAPRDN) * (nrdns+1); + dnsize += sizeof(LDAPAVA *) * (navas+nrdns); + dnsize += sizeof(LDAPAVA) * navas; + if (dnsize > sizeof(ptrs)) { + newDN = (LDAPDN)LDAP_MALLOC( dnsize ); + if ( newDN == NULL ) + return LDAP_NO_MEMORY; + } else { + newDN = (LDAPDN)(char *)ptrs; + } + + newDN[nrdns] = NULL; + newRDN = (LDAPRDN)(newDN + nrdns+1); + newAVA = (LDAPAVA *)(newRDN + navas + nrdns); + baseAVA = newAVA; + + for ( i = nrdns - 1; i >= 0; i-- ) { + newDN[i] = newRDN; + + for ( tag = ber_first_element( ber, &len, &rdn_end ); + tag == LBER_SEQUENCE; + tag = ber_next_element( ber, &len, rdn_end )) { + + *newRDN++ = newAVA; + tag = ber_skip_tag( ber, &len ); + tag = ber_get_stringbv( ber, &Oid, LBER_BV_NOTERM ); + if ( tag != LBER_TAG_OID ) { + rc = LDAP_DECODING_ERROR; + goto nomem; + } + + oid2.bv_val = oidptr; + oid2.bv_len = oidrem; + if ( ber_decode_oid( &Oid, &oid2 ) < 0 ) { + rc = LDAP_DECODING_ERROR; + goto nomem; + } + oidname = find_oid( &oid2 ); + if ( !oidname ) { + newAVA->la_attr = oid2; + oidptr += oid2.bv_len + 1; + oidrem -= oid2.bv_len + 1; + + /* Running out of OID buffer space? */ + if (oidrem < 128) { + if ( oidsize == 0 ) { + oidsize = sizeof(oids) * 2; + oidrem = oidsize; + oidbuf = LDAP_MALLOC( oidsize ); + if ( oidbuf == NULL ) goto nomem; + oidptr = oidbuf; + } else { + char *old = oidbuf; + oidbuf = LDAP_REALLOC( oidbuf, oidsize*2 ); + if ( oidbuf == NULL ) goto nomem; + /* Buffer moved! Fix AVA pointers */ + if ( old != oidbuf ) { + LDAPAVA *a; + long dif = oidbuf - old; + + for (a=baseAVA; a<=newAVA; a++){ + if (a->la_attr.bv_val >= old && + a->la_attr.bv_val <= (old + oidsize)) + a->la_attr.bv_val += dif; + } + } + oidptr = oidbuf + oidsize - oidrem; + oidrem += oidsize; + oidsize *= 2; + } + } + } else { + if ( func ) { + newAVA->la_attr = oidname->oid; + } else { + newAVA->la_attr = oidname->name; + } + } + newAVA->la_private = NULL; + newAVA->la_flags = LDAP_AVA_STRING; + tag = ber_get_stringbv( ber, &Val, LBER_BV_NOTERM ); + switch(tag) { + case LBER_TAG_UNIVERSAL: + /* This uses 32-bit ISO 10646-1 */ + csize = 4; goto to_utf8; + case LBER_TAG_BMP: + /* This uses 16-bit ISO 10646-1 */ + csize = 2; goto to_utf8; + case LBER_TAG_TELETEX: + /* This uses 8-bit, assume ISO 8859-1 */ + csize = 1; +to_utf8: rc = ldap_ucs_to_utf8s( &Val, csize, &newAVA->la_value ); + newAVA->la_flags |= LDAP_AVA_NONPRINTABLE; +allocd: + newAVA->la_flags |= LDAP_AVA_FREE_VALUE; + if (rc != LDAP_SUCCESS) goto nomem; + break; + case LBER_TAG_UTF8: + newAVA->la_flags |= LDAP_AVA_NONPRINTABLE; + /* This is already in UTF-8 encoding */ + case LBER_TAG_IA5: + case LBER_TAG_PRINTABLE: + /* These are always 7-bit strings */ + newAVA->la_value = Val; + break; + case LBER_BITSTRING: + /* X.690 bitString value converted to RFC4517 Bit String */ + rc = der_to_ldap_BitString( &Val, &newAVA->la_value ); + goto allocd; + case LBER_DEFAULT: + /* decode error */ + rc = LDAP_DECODING_ERROR; + goto nomem; + default: + /* Not a string type at all */ + newAVA->la_flags = 0; + newAVA->la_value = Val; + break; + } + newAVA++; + } + *newRDN++ = NULL; + tag = ber_next_element( ber, &len, dn_end ); + } + + if ( func ) { + rc = func( newDN, flags, NULL ); + if ( rc != LDAP_SUCCESS ) + goto nomem; + } + + rc = ldap_dn2bv_x( newDN, bv, LDAP_DN_FORMAT_LDAPV3, NULL ); + +nomem: + for (;baseAVA < newAVA; baseAVA++) { + if (baseAVA->la_flags & LDAP_AVA_FREE_ATTR) + LDAP_FREE( baseAVA->la_attr.bv_val ); + if (baseAVA->la_flags & LDAP_AVA_FREE_VALUE) + LDAP_FREE( baseAVA->la_value.bv_val ); + } + + if ( oidsize != 0 ) + LDAP_FREE( oidbuf ); + if ( newDN != (LDAPDN)(char *) ptrs ) + LDAP_FREE( newDN ); + return rc; +} + diff --git a/libraries/libldap/tls_g.c b/libraries/libldap/tls_g.c new file mode 100644 index 0000000..3b72cd2 --- /dev/null +++ b/libraries/libldap/tls_g.c @@ -0,0 +1,940 @@ +/* tls_g.c - Handle tls/ssl using GNUTLS. */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2008-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: GNUTLS support written by Howard Chu and + * Emily Backes; sponsored by The Written Word (thewrittenword.com) + * and Stanford University (stanford.edu). + */ + +#include "portable.h" + +#ifdef HAVE_GNUTLS + +#include "ldap_config.h" + +#include <stdio.h> + +#include <ac/stdlib.h> +#include <ac/errno.h> +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/ctype.h> +#include <ac/time.h> +#include <ac/unistd.h> +#include <ac/param.h> +#include <ac/dirent.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include "ldap-int.h" +#include "ldap-tls.h" + +#include <gnutls/gnutls.h> +#include <gnutls/x509.h> + +typedef struct tlsg_ctx { + gnutls_certificate_credentials_t cred; + gnutls_dh_params_t dh_params; + unsigned long verify_depth; + int refcount; + int reqcert; + gnutls_priority_t prios; +#ifdef LDAP_R_COMPILE + ldap_pvt_thread_mutex_t ref_mutex; +#endif +} tlsg_ctx; + +typedef struct tlsg_session { + gnutls_session_t session; + tlsg_ctx *ctx; + struct berval peer_der_dn; +} tlsg_session; + +static int tlsg_parse_ciphers( tlsg_ctx *ctx, char *suites ); +static int tlsg_cert_verify( tlsg_session *s ); + +#ifdef LDAP_R_COMPILE + +static void +tlsg_thr_init( void ) +{ + /* do nothing */ +} +#endif /* LDAP_R_COMPILE */ + +/* + * Initialize TLS subsystem. Should be called only once. + */ +static int +tlsg_init( void ) +{ + gnutls_global_init(); + return 0; +} + +/* + * Tear down the TLS subsystem. Should only be called once. + */ +static void +tlsg_destroy( void ) +{ + gnutls_global_deinit(); +} + +static tls_ctx * +tlsg_ctx_new ( struct ldapoptions *lo ) +{ + tlsg_ctx *ctx; + + ctx = ber_memcalloc ( 1, sizeof (*ctx) ); + if ( ctx ) { + if ( gnutls_certificate_allocate_credentials( &ctx->cred )) { + ber_memfree( ctx ); + return NULL; + } + ctx->refcount = 1; + gnutls_priority_init( &ctx->prios, "NORMAL", NULL ); +#ifdef LDAP_R_COMPILE + ldap_pvt_thread_mutex_init( &ctx->ref_mutex ); +#endif + } + return (tls_ctx *)ctx; +} + +static void +tlsg_ctx_ref( tls_ctx *ctx ) +{ + tlsg_ctx *c = (tlsg_ctx *)ctx; + LDAP_MUTEX_LOCK( &c->ref_mutex ); + c->refcount++; + LDAP_MUTEX_UNLOCK( &c->ref_mutex ); +} + +static void +tlsg_ctx_free ( tls_ctx *ctx ) +{ + tlsg_ctx *c = (tlsg_ctx *)ctx; + int refcount; + + if ( !c ) return; + + LDAP_MUTEX_LOCK( &c->ref_mutex ); + refcount = --c->refcount; + LDAP_MUTEX_UNLOCK( &c->ref_mutex ); + if ( refcount ) + return; + gnutls_priority_deinit( c->prios ); + gnutls_certificate_free_credentials( c->cred ); + if ( c->dh_params ) + gnutls_dh_params_deinit( c->dh_params ); + ber_memfree ( c ); +} + +static int +tlsg_getfile( const char *path, gnutls_datum_t *buf ) +{ + int rc = -1, fd; + struct stat st; + + fd = open( path, O_RDONLY ); + if ( fd >= 0 && fstat( fd, &st ) == 0 ) { + buf->size = st.st_size; + buf->data = LDAP_MALLOC( st.st_size + 1 ); + if ( buf->data ) { + rc = read( fd, buf->data, st.st_size ); + close( fd ); + if ( rc < st.st_size ) + rc = -1; + else + rc = 0; + } + } + return rc; +} + +/* This is the GnuTLS default */ +#define VERIFY_DEPTH 6 + +/* + * initialize a new TLS context + */ +static int +tlsg_ctx_init( struct ldapoptions *lo, struct ldaptls *lt, int is_server ) +{ + tlsg_ctx *ctx = lo->ldo_tls_ctx; + int rc; + + if ( lo->ldo_tls_ciphersuite && + tlsg_parse_ciphers( ctx, lt->lt_ciphersuite )) { + Debug( LDAP_DEBUG_ANY, + "TLS: could not set cipher list %s.\n", + lo->ldo_tls_ciphersuite, 0, 0 ); + return -1; + } + + if (lo->ldo_tls_cacertdir != NULL) { + Debug( LDAP_DEBUG_ANY, + "TLS: warning: cacertdir not implemented for gnutls\n", + NULL, NULL, NULL ); + } + + if (lo->ldo_tls_cacertfile != NULL) { + rc = gnutls_certificate_set_x509_trust_file( + ctx->cred, + lt->lt_cacertfile, + GNUTLS_X509_FMT_PEM ); + if ( rc < 0 ) return -1; + } + + if ( lo->ldo_tls_certfile && lo->ldo_tls_keyfile ) { + gnutls_x509_privkey_t key; + gnutls_datum_t buf; + gnutls_x509_crt_t certs[VERIFY_DEPTH]; + unsigned int max = VERIFY_DEPTH; + + rc = gnutls_x509_privkey_init( &key ); + if ( rc ) return -1; + + /* OpenSSL builds the cert chain for us, but GnuTLS + * expects it to be present in the certfile. If it's + * not, we have to build it ourselves. So we have to + * do some special checks here... + */ + rc = tlsg_getfile( lt->lt_keyfile, &buf ); + if ( rc ) return -1; + rc = gnutls_x509_privkey_import( key, &buf, + GNUTLS_X509_FMT_PEM ); + LDAP_FREE( buf.data ); + if ( rc < 0 ) return rc; + + rc = tlsg_getfile( lt->lt_certfile, &buf ); + if ( rc ) return -1; + rc = gnutls_x509_crt_list_import( certs, &max, &buf, + GNUTLS_X509_FMT_PEM, 0 ); + LDAP_FREE( buf.data ); + if ( rc < 0 ) return rc; + + /* If there's only one cert and it's not self-signed, + * then we have to build the cert chain. + */ + if ( max == 1 && !gnutls_x509_crt_check_issuer( certs[0], certs[0] )) { + unsigned int i; + for ( i = 1; i<VERIFY_DEPTH; i++ ) { + if ( gnutls_certificate_get_issuer( ctx->cred, certs[i-1], &certs[i], 0 )) + break; + max++; + /* If this CA is self-signed, we're done */ + if ( gnutls_x509_crt_check_issuer( certs[i], certs[i] )) + break; + } + } + rc = gnutls_certificate_set_x509_key( ctx->cred, certs, max, key ); + if ( rc ) return -1; + } else if ( lo->ldo_tls_certfile || lo->ldo_tls_keyfile ) { + Debug( LDAP_DEBUG_ANY, + "TLS: only one of certfile and keyfile specified\n", + NULL, NULL, NULL ); + return -1; + } + + if ( lo->ldo_tls_crlfile ) { + rc = gnutls_certificate_set_x509_crl_file( + ctx->cred, + lt->lt_crlfile, + GNUTLS_X509_FMT_PEM ); + if ( rc < 0 ) return -1; + rc = 0; + } + + /* FIXME: ITS#5992 - this should be configurable, + * and V1 CA certs should be phased out ASAP. + */ + gnutls_certificate_set_verify_flags( ctx->cred, + GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT ); + + if ( is_server && lo->ldo_tls_dhfile ) { + gnutls_datum_t buf; + rc = tlsg_getfile( lo->ldo_tls_dhfile, &buf ); + if ( rc ) return -1; + rc = gnutls_dh_params_init( &ctx->dh_params ); + if ( rc == 0 ) + rc = gnutls_dh_params_import_pkcs3( ctx->dh_params, &buf, + GNUTLS_X509_FMT_PEM ); + LDAP_FREE( buf.data ); + if ( rc ) return -1; + gnutls_certificate_set_dh_params( ctx->cred, ctx->dh_params ); + } + + ctx->reqcert = lo->ldo_tls_require_cert; + + return 0; +} + +static tls_session * +tlsg_session_new ( tls_ctx * ctx, int is_server ) +{ + tlsg_ctx *c = (tlsg_ctx *)ctx; + tlsg_session *session; + + session = ber_memcalloc ( 1, sizeof (*session) ); + if ( !session ) + return NULL; + + session->ctx = c; + gnutls_init( &session->session, is_server ? GNUTLS_SERVER : GNUTLS_CLIENT ); + gnutls_priority_set( session->session, c->prios ); + if ( c->cred ) + gnutls_credentials_set( session->session, GNUTLS_CRD_CERTIFICATE, c->cred ); + + if ( is_server ) { + int flag = 0; + if ( c->reqcert ) { + flag = GNUTLS_CERT_REQUEST; + if ( c->reqcert == LDAP_OPT_X_TLS_DEMAND || + c->reqcert == LDAP_OPT_X_TLS_HARD ) + flag = GNUTLS_CERT_REQUIRE; + gnutls_certificate_server_set_request( session->session, flag ); + } + } + return (tls_session *)session; +} + +static int +tlsg_session_accept( tls_session *session ) +{ + tlsg_session *s = (tlsg_session *)session; + int rc; + + rc = gnutls_handshake( s->session ); + if ( rc == 0 && s->ctx->reqcert != LDAP_OPT_X_TLS_NEVER ) { + const gnutls_datum_t *peer_cert_list; + unsigned int list_size; + + peer_cert_list = gnutls_certificate_get_peers( s->session, + &list_size ); + if ( !peer_cert_list && s->ctx->reqcert == LDAP_OPT_X_TLS_TRY ) + rc = 0; + else { + rc = tlsg_cert_verify( s ); + if ( rc && s->ctx->reqcert == LDAP_OPT_X_TLS_ALLOW ) + rc = 0; + } + } + return rc; +} + +static int +tlsg_session_connect( LDAP *ld, tls_session *session ) +{ + return tlsg_session_accept( session); +} + +static int +tlsg_session_upflags( Sockbuf *sb, tls_session *session, int rc ) +{ + tlsg_session *s = (tlsg_session *)session; + + if ( rc != GNUTLS_E_INTERRUPTED && rc != GNUTLS_E_AGAIN ) + return 0; + + switch (gnutls_record_get_direction (s->session)) { + case 0: + sb->sb_trans_needs_read = 1; + return 1; + case 1: + sb->sb_trans_needs_write = 1; + return 1; + } + return 0; +} + +static char * +tlsg_session_errmsg( tls_session *sess, int rc, char *buf, size_t len ) +{ + return (char *)gnutls_strerror( rc ); +} + +static void +tlsg_x509_cert_dn( struct berval *cert, struct berval *dn, int get_subject ) +{ + BerElementBuffer berbuf; + BerElement *ber = (BerElement *)&berbuf; + ber_tag_t tag; + ber_len_t len; + ber_int_t i; + + ber_init2( ber, cert, LBER_USE_DER ); + tag = ber_skip_tag( ber, &len ); /* Sequence */ + tag = ber_skip_tag( ber, &len ); /* Sequence */ + tag = ber_peek_tag( ber, &len ); /* Context + Constructed (version) */ + if ( tag == 0xa0 ) { /* Version is optional */ + tag = ber_skip_tag( ber, &len ); + tag = ber_get_int( ber, &i ); /* Int: Version */ + } + tag = ber_skip_tag( ber, &len ); /* Int: Serial (can be longer than ber_int_t) */ + ber_skip_data( ber, len ); + tag = ber_skip_tag( ber, &len ); /* Sequence: Signature */ + ber_skip_data( ber, len ); + if ( !get_subject ) { + tag = ber_peek_tag( ber, &len ); /* Sequence: Issuer DN */ + } else { + tag = ber_skip_tag( ber, &len ); + ber_skip_data( ber, len ); + tag = ber_skip_tag( ber, &len ); /* Sequence: Validity */ + ber_skip_data( ber, len ); + tag = ber_peek_tag( ber, &len ); /* Sequence: Subject DN */ + } + len = ber_ptrlen( ber ); + dn->bv_val = cert->bv_val + len; + dn->bv_len = cert->bv_len - len; +} + +static int +tlsg_session_my_dn( tls_session *session, struct berval *der_dn ) +{ + tlsg_session *s = (tlsg_session *)session; + const gnutls_datum_t *x; + struct berval bv; + + x = gnutls_certificate_get_ours( s->session ); + + if (!x) return LDAP_INVALID_CREDENTIALS; + + bv.bv_val = (char *) x->data; + bv.bv_len = x->size; + + tlsg_x509_cert_dn( &bv, der_dn, 1 ); + return 0; +} + +static int +tlsg_session_peer_dn( tls_session *session, struct berval *der_dn ) +{ + tlsg_session *s = (tlsg_session *)session; + if ( !s->peer_der_dn.bv_val ) { + const gnutls_datum_t *peer_cert_list; + unsigned int list_size; + struct berval bv; + + peer_cert_list = gnutls_certificate_get_peers( s->session, + &list_size ); + if ( !peer_cert_list ) return LDAP_INVALID_CREDENTIALS; + + bv.bv_len = peer_cert_list->size; + bv.bv_val = (char *) peer_cert_list->data; + + tlsg_x509_cert_dn( &bv, &s->peer_der_dn, 1 ); + } + *der_dn = s->peer_der_dn; + return 0; +} + +/* what kind of hostname were we given? */ +#define IS_DNS 0 +#define IS_IP4 1 +#define IS_IP6 2 + +#define CN_OID "2.5.4.3" + +static int +tlsg_session_chkhost( LDAP *ld, tls_session *session, const char *name_in ) +{ + tlsg_session *s = (tlsg_session *)session; + int i, ret; + int chkSAN = ld->ld_options.ldo_tls_require_san, gotSAN = 0; + const gnutls_datum_t *peer_cert_list; + unsigned int list_size; + char altname[NI_MAXHOST]; + size_t altnamesize; + + gnutls_x509_crt_t cert; + const char *name; + char *ptr; + char *domain = NULL; +#ifdef LDAP_PF_INET6 + struct in6_addr addr; +#else + struct in_addr addr; +#endif + int len1 = 0, len2 = 0; + int ntype = IS_DNS; + + if( ldap_int_hostname && + ( !name_in || !strcasecmp( name_in, "localhost" ) ) ) + { + name = ldap_int_hostname; + } else { + name = name_in; + } + + peer_cert_list = gnutls_certificate_get_peers( s->session, + &list_size ); + if ( !peer_cert_list ) { + Debug( LDAP_DEBUG_ANY, + "TLS: unable to get peer certificate.\n", + 0, 0, 0 ); + /* If this was a fatal condition, things would have + * aborted long before now. + */ + return LDAP_SUCCESS; + } + ret = gnutls_x509_crt_init( &cert ); + if ( ret < 0 ) + return LDAP_LOCAL_ERROR; + ret = gnutls_x509_crt_import( cert, peer_cert_list, GNUTLS_X509_FMT_DER ); + if ( ret ) { + gnutls_x509_crt_deinit( cert ); + return LDAP_LOCAL_ERROR; + } + +#ifdef LDAP_PF_INET6 + if (inet_pton(AF_INET6, name, &addr)) { + ntype = IS_IP6; + } else +#endif + if ((ptr = strrchr(name, '.')) && isdigit((unsigned char)ptr[1])) { + if (inet_aton(name, (struct in_addr *)&addr)) ntype = IS_IP4; + } + + if (ntype == IS_DNS) { + len1 = strlen(name); + domain = strchr(name, '.'); + if (domain) { + len2 = len1 - (domain-name); + } + } + + if (chkSAN) { + for ( i=0, ret=0; ret >= 0; i++ ) { + altnamesize = sizeof(altname); + ret = gnutls_x509_crt_get_subject_alt_name( cert, i, + altname, &altnamesize, NULL ); + if ( ret < 0 ) break; + + gotSAN = 1; + /* ignore empty */ + if ( altnamesize == 0 ) continue; + + if ( ret == GNUTLS_SAN_DNSNAME ) { + if (ntype != IS_DNS) continue; + + /* Is this an exact match? */ + if ((len1 == altnamesize) && !strncasecmp(name, altname, len1)) { + break; + } + + /* Is this a wildcard match? */ + if (domain && (altname[0] == '*') && (altname[1] == '.') && + (len2 == altnamesize-1) && !strncasecmp(domain, &altname[1], len2)) + { + break; + } + } else if ( ret == GNUTLS_SAN_IPADDRESS ) { + if (ntype == IS_DNS) continue; + +#ifdef LDAP_PF_INET6 + if (ntype == IS_IP6 && altnamesize != sizeof(struct in6_addr)) { + continue; + } else +#endif + if (ntype == IS_IP4 && altnamesize != sizeof(struct in_addr)) { + continue; + } + if (!memcmp(altname, &addr, altnamesize)) { + break; + } + } + } + if ( ret >= 0 ) { + ret = LDAP_SUCCESS; + } + } + if (ret != LDAP_SUCCESS && chkSAN) { + switch(chkSAN) { + case LDAP_OPT_X_TLS_DEMAND: + case LDAP_OPT_X_TLS_HARD: + if (!gotSAN) { + Debug( LDAP_DEBUG_ANY, + "TLS: unable to get subjectAltName from peer certificate.\n", 0, 0, 0 ); + ret = LDAP_CONNECT_ERROR; + if ( ld->ld_error ) { + LDAP_FREE( ld->ld_error ); + } + ld->ld_error = LDAP_STRDUP( + _("TLS: unable to get subjectAltName from peer certificate")); + goto done; + } + /* FALLTHRU */ + case LDAP_OPT_X_TLS_TRY: + if (gotSAN) { + Debug( LDAP_DEBUG_ANY, "TLS: hostname (%s) does not match " + "subjectAltName in certificate.\n", + name, 0, 0 ); + ret = LDAP_CONNECT_ERROR; + if ( ld->ld_error ) { + LDAP_FREE( ld->ld_error ); + } + ld->ld_error = LDAP_STRDUP( + _("TLS: hostname does not match subjectAltName in peer certificate")); + goto done; + } + break; + case LDAP_OPT_X_TLS_ALLOW: + break; + } + } + + if ( ret != LDAP_SUCCESS ){ + /* find the last CN */ + i=0; + do { + altnamesize = 0; + ret = gnutls_x509_crt_get_dn_by_oid( cert, CN_OID, + i, 1, altname, &altnamesize ); + if ( ret == GNUTLS_E_SHORT_MEMORY_BUFFER ) + i++; + else + break; + } while ( 1 ); + + if ( i ) { + altnamesize = sizeof(altname); + ret = gnutls_x509_crt_get_dn_by_oid( cert, CN_OID, + i-1, 0, altname, &altnamesize ); + } + + if ( ret < 0 ) { + Debug( LDAP_DEBUG_ANY, + "TLS: unable to get common name from peer certificate.\n", + 0, 0, 0 ); + ret = LDAP_CONNECT_ERROR; + if ( ld->ld_error ) { + LDAP_FREE( ld->ld_error ); + } + ld->ld_error = LDAP_STRDUP( + _("TLS: unable to get CN from peer certificate")); + + } else { + ret = LDAP_LOCAL_ERROR; + if ( !len1 ) len1 = strlen( name ); + if ( len1 == altnamesize && strncasecmp(name, altname, altnamesize) == 0 ) { + ret = LDAP_SUCCESS; + + } else if (( altname[0] == '*' ) && ( altname[1] == '.' )) { + /* Is this a wildcard match? */ + if( domain && + (len2 == altnamesize-1) && !strncasecmp(domain, &altname[1], len2)) { + ret = LDAP_SUCCESS; + } + } + } + + if( ret == LDAP_LOCAL_ERROR ) { + altname[altnamesize] = '\0'; + Debug( LDAP_DEBUG_ANY, "TLS: hostname (%s) does not match " + "common name in certificate (%s).\n", + name, altname, 0 ); + ret = LDAP_CONNECT_ERROR; + if ( ld->ld_error ) { + LDAP_FREE( ld->ld_error ); + } + ld->ld_error = LDAP_STRDUP( + _("TLS: hostname does not match name in peer certificate")); + } + } +done: + gnutls_x509_crt_deinit( cert ); + return ret; +} + +static int +tlsg_session_strength( tls_session *session ) +{ + tlsg_session *s = (tlsg_session *)session; + gnutls_cipher_algorithm_t c; + + c = gnutls_cipher_get( s->session ); + return gnutls_cipher_get_key_size( c ) * 8; +} + +/* suites is a string of colon-separated cipher suite names. */ +static int +tlsg_parse_ciphers( tlsg_ctx *ctx, char *suites ) +{ + const char *err; + int rc = gnutls_priority_init( &ctx->prios, suites, &err ); + if ( rc ) + ctx->prios = NULL; + return rc; +} + +/* + * TLS support for LBER Sockbufs + */ + +struct tls_data { + tlsg_session *session; + Sockbuf_IO_Desc *sbiod; +}; + +static ssize_t +tlsg_recv( gnutls_transport_ptr_t ptr, void *buf, size_t len ) +{ + struct tls_data *p; + + if ( buf == NULL || len <= 0 ) return 0; + + p = (struct tls_data *)ptr; + + if ( p == NULL || p->sbiod == NULL ) { + return 0; + } + + return LBER_SBIOD_READ_NEXT( p->sbiod, buf, len ); +} + +static ssize_t +tlsg_send( gnutls_transport_ptr_t ptr, const void *buf, size_t len ) +{ + struct tls_data *p; + + if ( buf == NULL || len <= 0 ) return 0; + + p = (struct tls_data *)ptr; + + if ( p == NULL || p->sbiod == NULL ) { + return 0; + } + + return LBER_SBIOD_WRITE_NEXT( p->sbiod, (char *)buf, len ); +} + +static int +tlsg_sb_setup( Sockbuf_IO_Desc *sbiod, void *arg ) +{ + struct tls_data *p; + tlsg_session *session = arg; + + assert( sbiod != NULL ); + + p = LBER_MALLOC( sizeof( *p ) ); + if ( p == NULL ) { + return -1; + } + + gnutls_transport_set_ptr( session->session, (gnutls_transport_ptr)p ); + gnutls_transport_set_pull_function( session->session, tlsg_recv ); + gnutls_transport_set_push_function( session->session, tlsg_send ); + p->session = session; + p->sbiod = sbiod; + sbiod->sbiod_pvt = p; + return 0; +} + +static int +tlsg_sb_remove( Sockbuf_IO_Desc *sbiod ) +{ + struct tls_data *p; + + assert( sbiod != NULL ); + assert( sbiod->sbiod_pvt != NULL ); + + p = (struct tls_data *)sbiod->sbiod_pvt; + gnutls_deinit ( p->session->session ); + LBER_FREE( p->session ); + LBER_FREE( sbiod->sbiod_pvt ); + sbiod->sbiod_pvt = NULL; + return 0; +} + +static int +tlsg_sb_close( Sockbuf_IO_Desc *sbiod ) +{ + struct tls_data *p; + + assert( sbiod != NULL ); + assert( sbiod->sbiod_pvt != NULL ); + + p = (struct tls_data *)sbiod->sbiod_pvt; + gnutls_bye ( p->session->session, GNUTLS_SHUT_WR ); + return 0; +} + +static int +tlsg_sb_ctrl( Sockbuf_IO_Desc *sbiod, int opt, void *arg ) +{ + struct tls_data *p; + + assert( sbiod != NULL ); + assert( sbiod->sbiod_pvt != NULL ); + + p = (struct tls_data *)sbiod->sbiod_pvt; + + if ( opt == LBER_SB_OPT_GET_SSL ) { + *((tlsg_session **)arg) = p->session; + return 1; + + } else if ( opt == LBER_SB_OPT_DATA_READY ) { + if( gnutls_record_check_pending( p->session->session ) > 0 ) { + return 1; + } + } + + return LBER_SBIOD_CTRL_NEXT( sbiod, opt, arg ); +} + +static ber_slen_t +tlsg_sb_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len) +{ + struct tls_data *p; + ber_slen_t ret; + + assert( sbiod != NULL ); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + + p = (struct tls_data *)sbiod->sbiod_pvt; + + ret = gnutls_record_recv ( p->session->session, buf, len ); + switch (ret) { + case GNUTLS_E_INTERRUPTED: + case GNUTLS_E_AGAIN: + sbiod->sbiod_sb->sb_trans_needs_read = 1; + sock_errset(EWOULDBLOCK); + ret = 0; + break; + case GNUTLS_E_REHANDSHAKE: + for ( ret = gnutls_handshake ( p->session->session ); + ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN; + ret = gnutls_handshake ( p->session->session ) ); + sbiod->sbiod_sb->sb_trans_needs_read = 1; + ret = 0; + break; + default: + sbiod->sbiod_sb->sb_trans_needs_read = 0; + } + return ret; +} + +static ber_slen_t +tlsg_sb_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len) +{ + struct tls_data *p; + ber_slen_t ret; + + assert( sbiod != NULL ); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + + p = (struct tls_data *)sbiod->sbiod_pvt; + + ret = gnutls_record_send ( p->session->session, (char *)buf, len ); + + if ( ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN ) { + sbiod->sbiod_sb->sb_trans_needs_write = 1; + sock_errset(EWOULDBLOCK); + ret = 0; + } else { + sbiod->sbiod_sb->sb_trans_needs_write = 0; + } + return ret; +} + +static Sockbuf_IO tlsg_sbio = +{ + tlsg_sb_setup, /* sbi_setup */ + tlsg_sb_remove, /* sbi_remove */ + tlsg_sb_ctrl, /* sbi_ctrl */ + tlsg_sb_read, /* sbi_read */ + tlsg_sb_write, /* sbi_write */ + tlsg_sb_close /* sbi_close */ +}; + +/* Certs are not automatically varified during the handshake */ +static int +tlsg_cert_verify( tlsg_session *ssl ) +{ + unsigned int status = 0; + int err; + time_t now = time(0); + time_t peertime; + + err = gnutls_certificate_verify_peers2( ssl->session, &status ); + if ( err < 0 ) { + Debug( LDAP_DEBUG_ANY,"TLS: gnutls_certificate_verify_peers2 failed %d\n", + err,0,0 ); + return -1; + } + if ( status ) { + Debug( LDAP_DEBUG_TRACE,"TLS: peer cert untrusted or revoked (0x%x)\n", + status, 0,0 ); + return -1; + } + peertime = gnutls_certificate_expiration_time_peers( ssl->session ); + if ( peertime == (time_t) -1 ) { + Debug( LDAP_DEBUG_ANY, "TLS: gnutls_certificate_expiration_time_peers failed\n", + 0, 0, 0 ); + return -1; + } + if ( peertime < now ) { + Debug( LDAP_DEBUG_ANY, "TLS: peer certificate is expired\n", + 0, 0, 0 ); + return -1; + } + peertime = gnutls_certificate_activation_time_peers( ssl->session ); + if ( peertime == (time_t) -1 ) { + Debug( LDAP_DEBUG_ANY, "TLS: gnutls_certificate_activation_time_peers failed\n", + 0, 0, 0 ); + return -1; + } + if ( peertime > now ) { + Debug( LDAP_DEBUG_ANY, "TLS: peer certificate not yet active\n", + 0, 0, 0 ); + return -1; + } + return 0; +} + +tls_impl ldap_int_tls_impl = { + "GnuTLS", + + tlsg_init, + tlsg_destroy, + + tlsg_ctx_new, + tlsg_ctx_ref, + tlsg_ctx_free, + tlsg_ctx_init, + + tlsg_session_new, + tlsg_session_connect, + tlsg_session_accept, + tlsg_session_upflags, + tlsg_session_errmsg, + tlsg_session_my_dn, + tlsg_session_peer_dn, + tlsg_session_chkhost, + tlsg_session_strength, + + &tlsg_sbio, + +#ifdef LDAP_R_COMPILE + tlsg_thr_init, +#else + NULL, +#endif + + 0 +}; + +#endif /* HAVE_GNUTLS */ diff --git a/libraries/libldap/tls_m.c b/libraries/libldap/tls_m.c new file mode 100644 index 0000000..43fbae4 --- /dev/null +++ b/libraries/libldap/tls_m.c @@ -0,0 +1,3324 @@ +/* tls_m.c - Handle tls/ssl using Mozilla NSS. */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2008-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: Initial version written by Howard Chu. + * Additional support by Rich Megginson. + */ + +#include "portable.h" + +#ifdef HAVE_MOZNSS + +#include "ldap_config.h" + +#include <stdio.h> + +#if defined( HAVE_FCNTL_H ) +#include <fcntl.h> +#endif + +#include <ac/stdlib.h> +#include <ac/errno.h> +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/ctype.h> +#include <ac/time.h> +#include <ac/unistd.h> +#include <ac/param.h> +#include <ac/dirent.h> +#include <ac/regex.h> + +#include "ldap-int.h" +#include "ldap-tls.h" + +#define READ_PASSWORD_FROM_STDIN +#define READ_PASSWORD_FROM_FILE + +#ifdef READ_PASSWORD_FROM_STDIN +#include <termios.h> /* for echo on/off */ +#endif + +#include <nspr/nspr.h> +#include <nspr/private/pprio.h> +#include <nss/nss.h> +#include <nss/ssl.h> +#include <nss/sslerr.h> +#include <nss/sslproto.h> +#include <nss/pk11pub.h> +#include <nss/secerr.h> +#include <nss/keyhi.h> +#include <nss/secmod.h> +#include <nss/cert.h> + +#undef NSS_VERSION_INT +#define NSS_VERSION_INT ((NSS_VMAJOR << 24) | (NSS_VMINOR << 16) | \ + (NSS_VPATCH << 8) | NSS_VBUILD) + +/* NSS 3.12.5 and later have NSS_InitContext */ +#if NSS_VERSION_INT >= 0x030c0500 +#define HAVE_NSS_INITCONTEXT 1 +#endif + +/* NSS 3.12.9 and later have SECMOD_RestartModules */ +#if NSS_VERSION_INT >= 0x030c0900 +#define HAVE_SECMOD_RESTARTMODULES 1 +#endif + +/* InitContext does not currently work in server mode */ +/* #define INITCONTEXT_HACK 1 */ + +typedef struct tlsm_ctx { + PRFileDesc *tc_model; + int tc_refcnt; + int tc_unique; /* unique number associated with this ctx */ + PRBool tc_verify_cert; + CERTCertDBHandle *tc_certdb; + PK11SlotInfo *tc_certdb_slot; + CERTCertificate *tc_certificate; + SECKEYPrivateKey *tc_private_key; + char *tc_pin_file; + struct ldaptls *tc_config; + int tc_is_server; + int tc_require_cert; + PRCallOnceType tc_callonce; + PRBool tc_using_pem; +#ifdef HAVE_NSS_INITCONTEXT + NSSInitContext *tc_initctx; /* the NSS context */ +#endif + PK11GenericObject **tc_pem_objs; /* array of objects to free */ + int tc_n_pem_objs; /* number of objects */ + PRBool tc_warn_only; /* only warn of errors in validation */ +#ifdef LDAP_R_COMPILE + ldap_pvt_thread_mutex_t tc_refmutex; +#endif +} tlsm_ctx; + +typedef PRFileDesc tlsm_session; + +static int tlsm_ctx_count; +#define TLSM_CERTDB_DESC_FMT "ldap(%d)" + +static PRDescIdentity tlsm_layer_id; + +static const PRIOMethods tlsm_PR_methods; + +#define CERTDB_NONE NULL +#define PREFIX_NONE NULL + +#define PEM_LIBRARY "nsspem" +#define PEM_MODULE "PEM" +#define PEM_CA_HASH_FILE_REGEX "^[0-9a-f]{8}\\.[0-9]+$" + +static SECMODModule *pem_module; + +#define DEFAULT_TOKEN_NAME "default" +#define TLSM_PEM_SLOT_CACERTS "PEM Token #0" +#define TLSM_PEM_SLOT_CERTS "PEM Token #1" + +#define PK11_SETATTRS(x,id,v,l) (x).type = (id); \ + (x).pValue=(v); (x).ulValueLen = (l); + +/* forward declaration */ +static int tlsm_init( void ); + +#ifdef LDAP_R_COMPILE + +/* it doesn't seem guaranteed that a client will call + tlsm_thr_init in a non-threaded context - so we have + to wrap the mutex creation in a prcallonce +*/ +static ldap_pvt_thread_mutex_t tlsm_ctx_count_mutex; +static ldap_pvt_thread_mutex_t tlsm_init_mutex; +static ldap_pvt_thread_mutex_t tlsm_pem_mutex; +static PRCallOnceType tlsm_init_mutex_callonce = {0,0}; + +static PRStatus PR_CALLBACK +tlsm_thr_init_callonce( void ) +{ + if ( ldap_pvt_thread_mutex_init( &tlsm_ctx_count_mutex ) ) { + Debug( LDAP_DEBUG_ANY, + "TLS: could not create mutex for context counter: %d\n", errno, 0, 0 ); + return PR_FAILURE; + } + + if ( ldap_pvt_thread_mutex_init( &tlsm_init_mutex ) ) { + Debug( LDAP_DEBUG_ANY, + "TLS: could not create mutex for moznss initialization: %d\n", errno, 0, 0 ); + return PR_FAILURE; + } + + if ( ldap_pvt_thread_mutex_init( &tlsm_pem_mutex ) ) { + Debug( LDAP_DEBUG_ANY, + "TLS: could not create mutex for PEM module: %d\n", errno, 0, 0 ); + return PR_FAILURE; + } + + return PR_SUCCESS; +} + +static void +tlsm_thr_init( void ) +{ + ( void )PR_CallOnce( &tlsm_init_mutex_callonce, tlsm_thr_init_callonce ); +} + +#endif /* LDAP_R_COMPILE */ + +static const char * +tlsm_dump_cipher_info(PRFileDesc *fd) +{ + PRUint16 ii; + + for (ii = 0; ii < SSL_NumImplementedCiphers; ++ii) { + PRInt32 cipher = (PRInt32)SSL_ImplementedCiphers[ii]; + PRBool enabled = PR_FALSE; + PRInt32 policy = 0; + SSLCipherSuiteInfo info; + + if (fd) { + SSL_CipherPrefGet(fd, cipher, &enabled); + } else { + SSL_CipherPrefGetDefault(cipher, &enabled); + } + SSL_CipherPolicyGet(cipher, &policy); + SSL_GetCipherSuiteInfo(cipher, &info, (PRUintn)sizeof(info)); + Debug( LDAP_DEBUG_TRACE, + "TLS: cipher: %d - %s, enabled: %d, ", + info.cipherSuite, info.cipherSuiteName, enabled ); + Debug( LDAP_DEBUG_TRACE, + "policy: %d\n", policy, 0, 0 ); + } + + return ""; +} + +/* Cipher definitions */ +typedef struct { + char *ossl_name; /* The OpenSSL cipher name */ + int num; /* The cipher id */ + int attr; /* cipher attributes: algorithms, etc */ + int version; /* protocol version valid for this cipher */ + int bits; /* bits of strength */ + int alg_bits; /* bits of the algorithm */ + int strength; /* LOW, MEDIUM, HIGH */ + int enabled; /* Enabled by default? */ +} cipher_properties; + +/* cipher attributes */ +#define SSL_kRSA 0x00000001L +#define SSL_aRSA 0x00000002L +#define SSL_aDSS 0x00000004L +#define SSL_DSS SSL_aDSS +#define SSL_eNULL 0x00000008L +#define SSL_DES 0x00000010L +#define SSL_3DES 0x00000020L +#define SSL_RC4 0x00000040L +#define SSL_RC2 0x00000080L +#define SSL_AES 0x00000100L +#define SSL_MD5 0x00000200L +#define SSL_SHA1 0x00000400L +#define SSL_SHA SSL_SHA1 +#define SSL_RSA (SSL_kRSA|SSL_aRSA) + +/* cipher strength */ +#define SSL_NULL 0x00000001L +#define SSL_EXPORT40 0x00000002L +#define SSL_EXPORT56 0x00000004L +#define SSL_LOW 0x00000008L +#define SSL_MEDIUM 0x00000010L +#define SSL_HIGH 0x00000020L + +#define SSL2 0x00000001L +#define SSL3 0x00000002L +/* OpenSSL treats SSL3 and TLSv1 the same */ +#define TLS1 SSL3 + +/* Cipher translation */ +static cipher_properties ciphers_def[] = { + /* SSL 2 ciphers */ + {"DES-CBC3-MD5", SSL_EN_DES_192_EDE3_CBC_WITH_MD5, SSL_kRSA|SSL_aRSA|SSL_3DES|SSL_MD5, SSL2, 168, 168, SSL_HIGH, SSL_ALLOWED}, + {"RC2-CBC-MD5", SSL_EN_RC2_128_CBC_WITH_MD5, SSL_kRSA|SSL_aRSA|SSL_RC2|SSL_MD5, SSL2, 128, 128, SSL_MEDIUM, SSL_ALLOWED}, + {"RC4-MD5", SSL_EN_RC4_128_WITH_MD5, SSL_kRSA|SSL_aRSA|SSL_RC4|SSL_MD5, SSL2, 128, 128, SSL_MEDIUM, SSL_ALLOWED}, + {"DES-CBC-MD5", SSL_EN_DES_64_CBC_WITH_MD5, SSL_kRSA|SSL_aRSA|SSL_DES|SSL_MD5, SSL2, 56, 56, SSL_LOW, SSL_ALLOWED}, + {"EXP-RC2-CBC-MD5", SSL_EN_RC2_128_CBC_EXPORT40_WITH_MD5, SSL_kRSA|SSL_aRSA|SSL_RC2|SSL_MD5, SSL2, 40, 128, SSL_EXPORT40, SSL_ALLOWED}, + {"EXP-RC4-MD5", SSL_EN_RC4_128_EXPORT40_WITH_MD5, SSL_kRSA|SSL_aRSA|SSL_RC4|SSL_MD5, SSL2, 40, 128, SSL_EXPORT40, SSL_ALLOWED}, + + /* SSL3 ciphers */ + {"RC4-MD5", SSL_RSA_WITH_RC4_128_MD5, SSL_kRSA|SSL_aRSA|SSL_RC4|SSL_MD5, SSL3, 128, 128, SSL_MEDIUM, SSL_ALLOWED}, + {"RC4-SHA", SSL_RSA_WITH_RC4_128_SHA, SSL_kRSA|SSL_aRSA|SSL_RC4|SSL_SHA1, SSL3, 128, 128, SSL_MEDIUM, SSL_ALLOWED}, + {"DES-CBC3-SHA", SSL_RSA_WITH_3DES_EDE_CBC_SHA, SSL_kRSA|SSL_aRSA|SSL_3DES|SSL_SHA1, SSL3, 168, 168, SSL_HIGH, SSL_ALLOWED}, + {"DES-CBC-SHA", SSL_RSA_WITH_DES_CBC_SHA, SSL_kRSA|SSL_aRSA|SSL_DES|SSL_SHA1, SSL3, 56, 56, SSL_LOW, SSL_ALLOWED}, + {"EXP-RC4-MD5", SSL_RSA_EXPORT_WITH_RC4_40_MD5, SSL_kRSA|SSL_aRSA|SSL_RC4|SSL_MD5, SSL3, 40, 128, SSL_EXPORT40, SSL_ALLOWED}, + {"EXP-RC2-CBC-MD5", SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5, SSL_kRSA|SSL_aRSA|SSL_RC2|SSL_MD5, SSL3, 0, 0, SSL_EXPORT40, SSL_ALLOWED}, + {"NULL-MD5", SSL_RSA_WITH_NULL_MD5, SSL_kRSA|SSL_aRSA|SSL_eNULL|SSL_MD5, SSL3, 0, 0, SSL_NULL, SSL_NOT_ALLOWED}, + {"NULL-SHA", SSL_RSA_WITH_NULL_SHA, SSL_kRSA|SSL_aRSA|SSL_eNULL|SSL_SHA1, SSL3, 0, 0, SSL_NULL, SSL_NOT_ALLOWED}, + + /* TLSv1 ciphers */ + {"EXP1024-DES-CBC-SHA", TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA, SSL_kRSA|SSL_aRSA|SSL_DES|SSL_SHA, TLS1, 56, 56, SSL_EXPORT56, SSL_ALLOWED}, + {"EXP1024-RC4-SHA", TLS_RSA_EXPORT1024_WITH_RC4_56_SHA, SSL_kRSA|SSL_aRSA|SSL_RC4|SSL_SHA, TLS1, 56, 56, SSL_EXPORT56, SSL_ALLOWED}, + {"AES128-SHA", TLS_RSA_WITH_AES_128_CBC_SHA, SSL_kRSA|SSL_aRSA|SSL_AES|SSL_SHA, TLS1, 128, 128, SSL_HIGH, SSL_ALLOWED}, + {"AES256-SHA", TLS_RSA_WITH_AES_256_CBC_SHA, SSL_kRSA|SSL_aRSA|SSL_AES|SSL_SHA, TLS1, 256, 256, SSL_HIGH, SSL_ALLOWED}, +}; + +#define ciphernum (sizeof(ciphers_def)/sizeof(cipher_properties)) + +/* given err which is the current errno, calls PR_SetError with + the corresponding NSPR error code */ +static void +tlsm_map_error(int err) +{ + PRErrorCode prError; + + switch ( err ) { + case EACCES: + prError = PR_NO_ACCESS_RIGHTS_ERROR; + break; + case EADDRINUSE: + prError = PR_ADDRESS_IN_USE_ERROR; + break; + case EADDRNOTAVAIL: + prError = PR_ADDRESS_NOT_AVAILABLE_ERROR; + break; + case EAFNOSUPPORT: + prError = PR_ADDRESS_NOT_SUPPORTED_ERROR; + break; + case EAGAIN: + prError = PR_WOULD_BLOCK_ERROR; + break; + /* + * On QNX and Neutrino, EALREADY is defined as EBUSY. + */ +#if EALREADY != EBUSY + case EALREADY: + prError = PR_ALREADY_INITIATED_ERROR; + break; +#endif + case EBADF: + prError = PR_BAD_DESCRIPTOR_ERROR; + break; +#ifdef EBADMSG + case EBADMSG: + prError = PR_IO_ERROR; + break; +#endif + case EBUSY: + prError = PR_FILESYSTEM_MOUNTED_ERROR; + break; + case ECONNABORTED: + prError = PR_CONNECT_ABORTED_ERROR; + break; + case ECONNREFUSED: + prError = PR_CONNECT_REFUSED_ERROR; + break; + case ECONNRESET: + prError = PR_CONNECT_RESET_ERROR; + break; + case EDEADLK: + prError = PR_DEADLOCK_ERROR; + break; +#ifdef EDIRCORRUPTED + case EDIRCORRUPTED: + prError = PR_DIRECTORY_CORRUPTED_ERROR; + break; +#endif +#ifdef EDQUOT + case EDQUOT: + prError = PR_NO_DEVICE_SPACE_ERROR; + break; +#endif + case EEXIST: + prError = PR_FILE_EXISTS_ERROR; + break; + case EFAULT: + prError = PR_ACCESS_FAULT_ERROR; + break; + case EFBIG: + prError = PR_FILE_TOO_BIG_ERROR; + break; + case EHOSTUNREACH: + prError = PR_HOST_UNREACHABLE_ERROR; + break; + case EINPROGRESS: + prError = PR_IN_PROGRESS_ERROR; + break; + case EINTR: + prError = PR_PENDING_INTERRUPT_ERROR; + break; + case EINVAL: + prError = PR_INVALID_ARGUMENT_ERROR; + break; + case EIO: + prError = PR_IO_ERROR; + break; + case EISCONN: + prError = PR_IS_CONNECTED_ERROR; + break; + case EISDIR: + prError = PR_IS_DIRECTORY_ERROR; + break; + case ELOOP: + prError = PR_LOOP_ERROR; + break; + case EMFILE: + prError = PR_PROC_DESC_TABLE_FULL_ERROR; + break; + case EMLINK: + prError = PR_MAX_DIRECTORY_ENTRIES_ERROR; + break; + case EMSGSIZE: + prError = PR_INVALID_ARGUMENT_ERROR; + break; +#ifdef EMULTIHOP + case EMULTIHOP: + prError = PR_REMOTE_FILE_ERROR; + break; +#endif + case ENAMETOOLONG: + prError = PR_NAME_TOO_LONG_ERROR; + break; + case ENETUNREACH: + prError = PR_NETWORK_UNREACHABLE_ERROR; + break; + case ENFILE: + prError = PR_SYS_DESC_TABLE_FULL_ERROR; + break; + /* + * On SCO OpenServer 5, ENOBUFS is defined as ENOSR. + */ +#if defined(ENOBUFS) && (ENOBUFS != ENOSR) + case ENOBUFS: + prError = PR_INSUFFICIENT_RESOURCES_ERROR; + break; +#endif + case ENODEV: + prError = PR_FILE_NOT_FOUND_ERROR; + break; + case ENOENT: + prError = PR_FILE_NOT_FOUND_ERROR; + break; + case ENOLCK: + prError = PR_FILE_IS_LOCKED_ERROR; + break; +#ifdef ENOLINK + case ENOLINK: + prError = PR_REMOTE_FILE_ERROR; + break; +#endif + case ENOMEM: + prError = PR_OUT_OF_MEMORY_ERROR; + break; + case ENOPROTOOPT: + prError = PR_INVALID_ARGUMENT_ERROR; + break; + case ENOSPC: + prError = PR_NO_DEVICE_SPACE_ERROR; + break; +#ifdef ENOSR + case ENOSR: + prError = PR_INSUFFICIENT_RESOURCES_ERROR; + break; +#endif + case ENOTCONN: + prError = PR_NOT_CONNECTED_ERROR; + break; + case ENOTDIR: + prError = PR_NOT_DIRECTORY_ERROR; + break; + case ENOTSOCK: + prError = PR_NOT_SOCKET_ERROR; + break; + case ENXIO: + prError = PR_FILE_NOT_FOUND_ERROR; + break; + case EOPNOTSUPP: + prError = PR_NOT_TCP_SOCKET_ERROR; + break; +#ifdef EOVERFLOW + case EOVERFLOW: + prError = PR_BUFFER_OVERFLOW_ERROR; + break; +#endif + case EPERM: + prError = PR_NO_ACCESS_RIGHTS_ERROR; + break; + case EPIPE: + prError = PR_CONNECT_RESET_ERROR; + break; +#ifdef EPROTO + case EPROTO: + prError = PR_IO_ERROR; + break; +#endif + case EPROTONOSUPPORT: + prError = PR_PROTOCOL_NOT_SUPPORTED_ERROR; + break; + case EPROTOTYPE: + prError = PR_ADDRESS_NOT_SUPPORTED_ERROR; + break; + case ERANGE: + prError = PR_INVALID_METHOD_ERROR; + break; + case EROFS: + prError = PR_READ_ONLY_FILESYSTEM_ERROR; + break; + case ESPIPE: + prError = PR_INVALID_METHOD_ERROR; + break; + case ETIMEDOUT: + prError = PR_IO_TIMEOUT_ERROR; + break; +#if EWOULDBLOCK != EAGAIN + case EWOULDBLOCK: + prError = PR_WOULD_BLOCK_ERROR; + break; +#endif + case EXDEV: + prError = PR_NOT_SAME_DEVICE_ERROR; + break; + default: + prError = PR_UNKNOWN_ERROR; + break; + } + PR_SetError( prError, err ); +} + +/* + * cipher_list is an integer array with the following values: + * -1: never enable this cipher + * 0: cipher disabled + * 1: cipher enabled + */ +static int +nss_parse_ciphers(const char *cipherstr, int cipher_list[ciphernum]) +{ + int i; + char *cipher; + char *ciphers; + char *ciphertip; + int action; + int rv; + + /* All disabled to start */ + for (i=0; i<ciphernum; i++) + cipher_list[i] = 0; + + ciphertip = strdup(cipherstr); + cipher = ciphers = ciphertip; + + while (ciphers && (strlen(ciphers))) { + while ((*cipher) && (isspace(*cipher))) + ++cipher; + + action = 1; + switch(*cipher) { + case '+': /* Add something */ + action = 1; + cipher++; + break; + case '-': /* Subtract something */ + action = 0; + cipher++; + break; + case '!': /* Disable something */ + action = -1; + cipher++; + break; + default: + /* do nothing */ + break; + } + + if ((ciphers = strchr(cipher, ':'))) { + *ciphers++ = '\0'; + } + + /* Do the easy one first */ + if (!strcmp(cipher, "ALL")) { + for (i=0; i<ciphernum; i++) { + if (!(ciphers_def[i].attr & SSL_eNULL)) + cipher_list[i] = action; + } + } else if (!strcmp(cipher, "COMPLEMENTOFALL")) { + for (i=0; i<ciphernum; i++) { + if ((ciphers_def[i].attr & SSL_eNULL)) + cipher_list[i] = action; + } + } else if (!strcmp(cipher, "DEFAULT")) { + for (i=0; i<ciphernum; i++) { + cipher_list[i] = ciphers_def[i].enabled == SSL_ALLOWED ? 1 : 0; + } + } else { + int mask = 0; + int strength = 0; + int protocol = 0; + char *c; + + c = cipher; + while (c && (strlen(c))) { + + if ((c = strchr(cipher, '+'))) { + *c++ = '\0'; + } + + if (!strcmp(cipher, "RSA")) { + mask |= SSL_RSA; + } else if ((!strcmp(cipher, "NULL")) || (!strcmp(cipher, "eNULL"))) { + mask |= SSL_eNULL; + } else if (!strcmp(cipher, "AES")) { + mask |= SSL_AES; + } else if (!strcmp(cipher, "3DES")) { + mask |= SSL_3DES; + } else if (!strcmp(cipher, "DES")) { + mask |= SSL_DES; + } else if (!strcmp(cipher, "RC4")) { + mask |= SSL_RC4; + } else if (!strcmp(cipher, "RC2")) { + mask |= SSL_RC2; + } else if (!strcmp(cipher, "MD5")) { + mask |= SSL_MD5; + } else if ((!strcmp(cipher, "SHA")) || (!strcmp(cipher, "SHA1"))) { + mask |= SSL_SHA1; + } else if (!strcmp(cipher, "SSLv2")) { + protocol |= SSL2; + } else if (!strcmp(cipher, "SSLv3")) { + protocol |= SSL3; + } else if (!strcmp(cipher, "TLSv1")) { + protocol |= TLS1; + } else if (!strcmp(cipher, "HIGH")) { + strength |= SSL_HIGH; + } else if (!strcmp(cipher, "MEDIUM")) { + strength |= SSL_MEDIUM; + } else if (!strcmp(cipher, "LOW")) { + strength |= SSL_LOW; + } else if ((!strcmp(cipher, "EXPORT")) || (!strcmp(cipher, "EXP"))) { + strength |= SSL_EXPORT40|SSL_EXPORT56; + } else if (!strcmp(cipher, "EXPORT40")) { + strength |= SSL_EXPORT40; + } else if (!strcmp(cipher, "EXPORT56")) { + strength |= SSL_EXPORT56; + } + + if (c) + cipher = c; + + } /* while */ + + /* If we have a mask, apply it. If not then perhaps they provided + * a specific cipher to enable. + */ + if (mask || strength || protocol) { + for (i=0; i<ciphernum; i++) { + if (((ciphers_def[i].attr & mask) || + (ciphers_def[i].strength & strength) || + (ciphers_def[i].version & protocol)) && + (cipher_list[i] != -1)) { + /* Enable the NULL ciphers only if explicity + * requested */ + if (ciphers_def[i].attr & SSL_eNULL) { + if (mask & SSL_eNULL) + cipher_list[i] = action; + } else + cipher_list[i] = action; + } + } + } else { + for (i=0; i<ciphernum; i++) { + if (!strcmp(ciphers_def[i].ossl_name, cipher) && + cipher_list[i] != -1) + cipher_list[i] = action; + } + } + } + + if (ciphers) + cipher = ciphers; + } + + /* See if any ciphers were enabled */ + rv = 0; + for (i=0; i<ciphernum; i++) { + if (cipher_list[i] == 1) + rv = 1; + } + + free(ciphertip); + + return rv; +} + +static int +tlsm_parse_ciphers(tlsm_ctx *ctx, const char *str) +{ + int cipher_state[ciphernum]; + int rv, i; + + if (!ctx) + return 0; + + rv = nss_parse_ciphers(str, cipher_state); + + if (rv) { + /* First disable everything */ + for (i = 0; i < SSL_NumImplementedCiphers; i++) + SSL_CipherPrefSet(ctx->tc_model, SSL_ImplementedCiphers[i], SSL_NOT_ALLOWED); + + /* Now enable what was requested */ + for (i=0; i<ciphernum; i++) { + SSLCipherSuiteInfo suite; + PRBool enabled; + + if (SSL_GetCipherSuiteInfo(ciphers_def[i].num, &suite, sizeof suite) + == SECSuccess) { + enabled = cipher_state[i] < 0 ? 0 : cipher_state[i]; + if (enabled == SSL_ALLOWED) { + if (PK11_IsFIPS() && !suite.isFIPS) + enabled = SSL_NOT_ALLOWED; + } + SSL_CipherPrefSet(ctx->tc_model, ciphers_def[i].num, enabled); + } + } + } + + return rv == 1 ? 0 : -1; +} + +static SECStatus +tlsm_bad_cert_handler(void *arg, PRFileDesc *ssl) +{ + SECStatus success = SECSuccess; + PRErrorCode err; + tlsm_ctx *ctx = (tlsm_ctx *)arg; + + if (!ssl || !ctx) { + return SECFailure; + } + + err = PORT_GetError(); + + switch (err) { + case SEC_ERROR_UNTRUSTED_ISSUER: + case SEC_ERROR_UNKNOWN_ISSUER: + case SEC_ERROR_EXPIRED_CERTIFICATE: + case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE: + if (ctx->tc_verify_cert) { + success = SECFailure; + } + break; + /* we bypass NSS's hostname checks and do our own */ + case SSL_ERROR_BAD_CERT_DOMAIN: + break; + default: + success = SECFailure; + break; + } + + return success; +} + +static const char * +tlsm_dump_security_status(PRFileDesc *fd) +{ + char * cp; /* bulk cipher name */ + char * ip; /* cert issuer DN */ + char * sp; /* cert subject DN */ + int op; /* High, Low, Off */ + int kp0; /* total key bits */ + int kp1; /* secret key bits */ + SSL3Statistics * ssl3stats = SSL_GetStatistics(); + + SSL_SecurityStatus( fd, &op, &cp, &kp0, &kp1, &ip, &sp ); + Debug( LDAP_DEBUG_TRACE, + "TLS certificate verification: subject: %s, issuer: %s, cipher: %s, ", + sp ? sp : "-unknown-", ip ? ip : "-unknown-", cp ? cp : "-unknown-" ); + PR_Free(cp); + PR_Free(ip); + PR_Free(sp); + Debug( LDAP_DEBUG_TRACE, + "security level: %s, secret key bits: %d, total key bits: %d, ", + ((op == SSL_SECURITY_STATUS_ON_HIGH) ? "high" : + ((op == SSL_SECURITY_STATUS_ON_LOW) ? "low" : "off")), + kp1, kp0 ); + + Debug( LDAP_DEBUG_TRACE, + "cache hits: %ld, cache misses: %ld, cache not reusable: %ld\n", + ssl3stats->hch_sid_cache_hits, ssl3stats->hch_sid_cache_misses, + ssl3stats->hch_sid_cache_not_ok ); + + return ""; +} + +static void +tlsm_handshake_complete_cb( PRFileDesc *fd, void *client_data ) +{ + tlsm_dump_security_status( fd ); +} + +#ifdef READ_PASSWORD_FROM_FILE +static char * +tlsm_get_pin_from_file(const char *token_name, tlsm_ctx *ctx) +{ + char *pwdstr = NULL; + char *contents = NULL; + char *lasts = NULL; + char *line = NULL; + char *candidate = NULL; + PRFileInfo file_info; + PRFileDesc *pwd_fileptr = PR_Open( ctx->tc_pin_file, PR_RDONLY, 00400 ); + + /* open the password file */ + if ( !pwd_fileptr ) { + PRErrorCode errcode = PR_GetError(); + Debug( LDAP_DEBUG_ANY, + "TLS: could not open security pin file %s - error %d:%s.\n", + ctx->tc_pin_file, errcode, + PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) ); + goto done; + } + + /* get the file size */ + if ( PR_SUCCESS != PR_GetFileInfo( ctx->tc_pin_file, &file_info ) ) { + PRErrorCode errcode = PR_GetError(); + Debug( LDAP_DEBUG_ANY, + "TLS: could not get file info from pin file %s - error %d:%s.\n", + ctx->tc_pin_file, errcode, + PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) ); + goto done; + } + + /* create a buffer to hold the file contents */ + if ( !( contents = PR_CALLOC( file_info.size + 1 ) ) ) { + PRErrorCode errcode = PR_GetError(); + Debug( LDAP_DEBUG_ANY, + "TLS: could not alloc a buffer for contents of pin file %s - error %d:%s.\n", + ctx->tc_pin_file, errcode, + PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) ); + goto done; + } + + /* read file into the buffer */ + if( PR_Read( pwd_fileptr, contents, file_info.size ) <= 0 ) { + PRErrorCode errcode = PR_GetError(); + Debug( LDAP_DEBUG_ANY, + "TLS: could not read the file contents from pin file %s - error %d:%s.\n", + ctx->tc_pin_file, errcode, + PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) ); + goto done; + } + + /* format is [tokenname:]password EOL [tokenname:]password EOL ... */ + /* if you want to use a password containing a colon character, use + the special tokenname "default" */ + for ( line = PL_strtok_r( contents, "\r\n", &lasts ); line; + line = PL_strtok_r( NULL, "\r\n", &lasts ) ) { + char *colon; + + if ( !*line ) { + continue; /* skip blank lines */ + } + colon = PL_strchr( line, ':' ); + if ( colon ) { + if ( *(colon + 1) && token_name && + !PL_strncmp( token_name, line, colon-line ) ) { + candidate = colon + 1; /* found a definite match */ + break; + } else if ( !PL_strncmp( DEFAULT_TOKEN_NAME, line, colon-line ) ) { + candidate = colon + 1; /* found possible match */ + } + } else { /* no token name */ + candidate = line; + } + } +done: + if ( pwd_fileptr ) { + PR_Close( pwd_fileptr ); + } + if ( candidate ) { + pwdstr = PL_strdup( candidate ); + } + PL_strfree( contents ); + + return pwdstr; +} +#endif /* READ_PASSWORD_FROM_FILE */ + +#ifdef READ_PASSWORD_FROM_STDIN +/* + * Turn the echoing off on a tty. + */ +static void +echoOff(int fd) +{ + if ( isatty( fd ) ) { + struct termios tio; + tcgetattr( fd, &tio ); + tio.c_lflag &= ~ECHO; + tcsetattr( fd, TCSAFLUSH, &tio ); + } +} + +/* + * Turn the echoing on on a tty. + */ +static void +echoOn(int fd) +{ + if ( isatty( fd ) ) { + struct termios tio; + tcgetattr( fd, &tio ); + tio.c_lflag |= ECHO; + tcsetattr( fd, TCSAFLUSH, &tio ); + tcsetattr( fd, TCSAFLUSH, &tio ); + } +} +#endif /* READ_PASSWORD_FROM_STDIN */ + +/* + * This does the actual work of reading the pin/password/pass phrase + */ +static char * +tlsm_get_pin(PK11SlotInfo *slot, PRBool retry, tlsm_ctx *ctx) +{ + char *token_name = NULL; + char *pwdstr = NULL; + + token_name = PK11_GetTokenName( slot ); +#ifdef READ_PASSWORD_FROM_FILE + /* Try to get the passwords from the password file if it exists. + * THIS IS UNSAFE and is provided for convenience only. Without this + * capability the server would have to be started in foreground mode + * if using an encrypted key. + */ + if ( ctx && ctx->tc_pin_file ) { + pwdstr = tlsm_get_pin_from_file( token_name, ctx ); + if ( retry && pwdstr != NULL ) + return NULL; + } +#endif /* RETRIEVE_PASSWORD_FROM_FILE */ +#ifdef READ_PASSWORD_FROM_STDIN + if ( !pwdstr ) { + int infd = PR_FileDesc2NativeHandle( PR_STDIN ); + int isTTY = isatty( infd ); + unsigned char phrase[200]; + /* Prompt for password */ + if ( isTTY ) { + fprintf( stdout, + "Please enter pin, password, or pass phrase for security token '%s': ", + token_name ? token_name : DEFAULT_TOKEN_NAME ); + echoOff( infd ); + } + fgets( (char*)phrase, sizeof(phrase), stdin ); + if ( isTTY ) { + fprintf( stdout, "\n" ); + echoOn( infd ); + } + /* stomp on newline */ + phrase[strlen((char*)phrase)-1] = 0; + + pwdstr = PL_strdup( (char*)phrase ); + } + +#endif /* READ_PASSWORD_FROM_STDIN */ + return pwdstr; +} + +/* + * PKCS11 devices (including the internal softokn cert/key database) + * may be protected by a pin or password or even pass phrase + * MozNSS needs a way for the user to provide that + */ +static char * +tlsm_pin_prompt(PK11SlotInfo *slot, PRBool retry, void *arg) +{ + tlsm_ctx *ctx = (tlsm_ctx *)arg; + + return tlsm_get_pin( slot, retry, ctx ); +} + +static char * +tlsm_ctx_subject_name(tlsm_ctx *ctx) +{ + if ( !ctx || !ctx->tc_certificate ) + return "(unknown)"; + + return ctx->tc_certificate->subjectName; +} + +static SECStatus +tlsm_get_basic_constraint_extension( CERTCertificate *cert, + CERTBasicConstraints *cbcval ) +{ + SECItem encodedVal = { 0, NULL }; + SECStatus rc; + + rc = CERT_FindCertExtension( cert, SEC_OID_X509_BASIC_CONSTRAINTS, + &encodedVal); + if ( rc != SECSuccess ) { + return rc; + } + + rc = CERT_DecodeBasicConstraintValue( cbcval, &encodedVal ); + + /* free the raw extension data */ + PORT_Free( encodedVal.data ); + + return rc; +} + +static PRBool +tlsm_cert_is_self_issued( CERTCertificate *cert ) +{ + /* A cert is self-issued if its subject and issuer are equal and + * both are of non-zero length. + */ + PRBool is_self_issued = cert && + (PRBool)SECITEM_ItemsAreEqual( &cert->derIssuer, + &cert->derSubject ) && + cert->derSubject.len > 0; + return is_self_issued; +} + +/* + * The private key for used certificate can be already unlocked by other + * thread or library. Find the unlocked key if possible. + */ +static SECKEYPrivateKey * +tlsm_find_unlocked_key( tlsm_ctx *ctx, void *pin_arg ) +{ + SECKEYPrivateKey *result = NULL; + + PK11SlotList *slots = PK11_GetAllSlotsForCert( ctx->tc_certificate, NULL ); + if ( !slots ) { + PRErrorCode errcode = PR_GetError(); + Debug( LDAP_DEBUG_ANY, + "TLS: cannot get all slots for certificate '%s' (error %d: %s)", + tlsm_ctx_subject_name( ctx ), errcode, + PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) ); + return result; + } + + PK11SlotListElement *le; + for ( le = slots->head; le; le = le->next ) { + PK11SlotInfo *slot = le->slot; + if ( PK11_IsLoggedIn( slot, NULL ) ) { + result = PK11_FindKeyByDERCert( slot, ctx->tc_certificate, pin_arg ); + break; + } + } + + PK11_FreeSlotList( slots ); + return result; +} + +static SECStatus +tlsm_verify_cert(CERTCertDBHandle *handle, CERTCertificate *cert, void *pinarg, + PRBool checksig, SECCertificateUsage certUsage, PRBool warn_only, + PRBool ignore_issuer ) +{ + CERTVerifyLog verifylog; + SECStatus ret = SECSuccess; + const char *name; + int debug_level = LDAP_DEBUG_ANY; + + if ( warn_only ) { + debug_level = LDAP_DEBUG_TRACE; + } + + /* the log captures information about every cert in the chain, so we can tell + which cert caused the problem and what the problem was */ + memset( &verifylog, 0, sizeof( verifylog ) ); + verifylog.arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE ); + if ( verifylog.arena == NULL ) { + Debug( LDAP_DEBUG_ANY, + "TLS certificate verification: Out of memory for certificate verification logger\n", + 0, 0, 0 ); + return SECFailure; + } + ret = CERT_VerifyCertificate( handle, cert, checksig, certUsage, PR_Now(), pinarg, &verifylog, + NULL ); + if ( ( name = cert->subjectName ) == NULL ) { + name = cert->nickname; + } + if ( verifylog.head == NULL ) { + /* it is possible for CERT_VerifyCertificate return with an error with no logging */ + if ( ret != SECSuccess ) { + PRErrorCode errcode = PR_GetError(); + Debug( debug_level, + "TLS: certificate [%s] is not valid - error %d:%s.\n", + name ? name : "(unknown)", + errcode, PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) ); + } + } else { + const char *name; + CERTVerifyLogNode *node; + + ret = SECSuccess; /* reset */ + node = verifylog.head; + while ( node ) { + if ( ( name = node->cert->subjectName ) == NULL ) { + name = node->cert->nickname; + } + if ( node->error ) { + /* NSS does not like CA certs that have the basic constraints extension + with the CA flag set to FALSE - openssl doesn't check if the cert + is self issued */ + if ( ( node->error == SEC_ERROR_CA_CERT_INVALID ) && + tlsm_cert_is_self_issued( node->cert ) ) { + + PRErrorCode orig_error = PR_GetError(); + PRInt32 orig_oserror = PR_GetOSError(); + + CERTBasicConstraints basicConstraint; + SECStatus rv = tlsm_get_basic_constraint_extension( node->cert, &basicConstraint ); + if ( ( rv == SECSuccess ) && ( basicConstraint.isCA == PR_FALSE ) ) { + Debug( LDAP_DEBUG_TRACE, + "TLS: certificate [%s] is not correct because it is a CA cert and the " + "BasicConstraint CA flag is set to FALSE - allowing for now, but " + "please fix your certs if possible\n", name, 0, 0 ); + } else { /* does not have basicconstraint, or some other error */ + ret = SECFailure; + Debug( debug_level, + "TLS: certificate [%s] is not valid - CA cert is not valid\n", + name, 0, 0 ); + } + + PR_SetError( orig_error, orig_oserror ); + + } else if ( warn_only || ( ignore_issuer && ( + node->error == SEC_ERROR_UNKNOWN_ISSUER || + node->error == SEC_ERROR_UNTRUSTED_ISSUER ) + ) ) { + ret = SECSuccess; + Debug( debug_level, + "TLS: Warning: ignoring error for certificate [%s] - error %ld:%s.\n", + name, node->error, PR_ErrorToString( node->error, PR_LANGUAGE_I_DEFAULT ) ); + } else { + ret = SECFailure; + Debug( debug_level, + "TLS: certificate [%s] is not valid - error %ld:%s.\n", + name, node->error, PR_ErrorToString( node->error, PR_LANGUAGE_I_DEFAULT ) ); + } + } + CERT_DestroyCertificate( node->cert ); + node = node->next; + } + } + + PORT_FreeArena( verifylog.arena, PR_FALSE ); + + if ( ret == SECSuccess ) { + Debug( LDAP_DEBUG_TRACE, + "TLS: certificate [%s] is valid\n", name, 0, 0 ); + } + + return ret; +} + +static SECStatus +tlsm_auth_cert_handler(void *arg, PRFileDesc *fd, + PRBool checksig, PRBool isServer) +{ + SECCertificateUsage certUsage = isServer ? certificateUsageSSLClient : certificateUsageSSLServer; + SECStatus ret = SECSuccess; + CERTCertificate *peercert = SSL_PeerCertificate( fd ); + tlsm_ctx *ctx = (tlsm_ctx *)arg; + + ret = tlsm_verify_cert( ctx->tc_certdb, peercert, + SSL_RevealPinArg( fd ), + checksig, certUsage, ctx->tc_warn_only, PR_FALSE ); + CERT_DestroyCertificate( peercert ); + + return ret; +} + +static PRCallOnceType tlsm_register_shutdown_callonce = {0,0}; + +static SECStatus +tlsm_nss_shutdown_cb( void *appData, void *nssData ) +{ + SECStatus rc = SECSuccess; + + SSL_ShutdownServerSessionIDCache(); + + if ( pem_module ) { + SECMOD_UnloadUserModule( pem_module ); + SECMOD_DestroyModule( pem_module ); + pem_module = NULL; + } + + /* init callonce so it can be armed again for cases like persistent daemon with LDAP_OPT_X_TLS_NEWCTX */ + tlsm_register_shutdown_callonce.initialized = 0; + tlsm_register_shutdown_callonce.inProgress = 0; + tlsm_register_shutdown_callonce.status = 0; + + return rc; +} + +static PRStatus PR_CALLBACK +tlsm_register_nss_shutdown_cb( void ) +{ + if ( SECSuccess == NSS_RegisterShutdown( tlsm_nss_shutdown_cb, + NULL ) ) { + return PR_SUCCESS; + } + return PR_FAILURE; +} + +static PRStatus +tlsm_register_nss_shutdown( void ) +{ + return PR_CallOnce( &tlsm_register_shutdown_callonce, + tlsm_register_nss_shutdown_cb ); +} + +static int +tlsm_init_pem_module( void ) +{ + int rc = 0; + char *fullname = NULL; + char *configstring = NULL; + + if ( pem_module ) { + return rc; + } + + /* not loaded - load it */ + /* get the system dependent library name */ + fullname = PR_GetLibraryName( NULL, PEM_LIBRARY ); + /* Load our PKCS#11 module */ + configstring = PR_smprintf( "library=%s name=" PEM_MODULE " parameters=\"\"", fullname ); + PL_strfree( fullname ); + + pem_module = SECMOD_LoadUserModule( configstring, NULL, PR_FALSE ); + PR_smprintf_free( configstring ); + + if ( !pem_module || !pem_module->loaded ) { + if ( pem_module ) { + SECMOD_DestroyModule( pem_module ); + pem_module = NULL; + } + rc = -1; + } + + return rc; +} + +static void +tlsm_add_pem_obj( tlsm_ctx *ctx, PK11GenericObject *obj ) +{ + int idx = ctx->tc_n_pem_objs; + ctx->tc_n_pem_objs++; + ctx->tc_pem_objs = (PK11GenericObject **) + PORT_Realloc( ctx->tc_pem_objs, ctx->tc_n_pem_objs * sizeof( PK11GenericObject * ) ); + ctx->tc_pem_objs[idx] = obj; +} + +static void +tlsm_free_pem_objs( tlsm_ctx *ctx ) +{ + /* free in reverse order of allocation */ + while ( ctx->tc_n_pem_objs-- ) { + PK11_DestroyGenericObject( ctx->tc_pem_objs[ctx->tc_n_pem_objs] ); + ctx->tc_pem_objs[ctx->tc_n_pem_objs] = NULL; + } + PORT_Free(ctx->tc_pem_objs); + ctx->tc_pem_objs = NULL; + ctx->tc_n_pem_objs = 0; +} + +static int +tlsm_add_cert_from_file( tlsm_ctx *ctx, const char *filename, PRBool isca ) +{ + PK11SlotInfo *slot; + PK11GenericObject *cert; + CK_ATTRIBUTE attrs[4]; + CK_BBOOL cktrue = CK_TRUE; + CK_BBOOL ckfalse = CK_FALSE; + CK_OBJECT_CLASS objClass = CKO_CERTIFICATE; + char *slotname; + PRFileInfo fi; + PRStatus status; + SECItem certDER = { 0, NULL, 0 }; + + memset( &fi, 0, sizeof(fi) ); + status = PR_GetFileInfo( filename, &fi ); + if ( PR_SUCCESS != status) { + PRErrorCode errcode = PR_GetError(); + Debug( LDAP_DEBUG_ANY, + "TLS: could not read certificate file %s - error %d:%s.\n", + filename, errcode, + PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) ); + return -1; + } + + if ( fi.type != PR_FILE_FILE ) { + PR_SetError(PR_IS_DIRECTORY_ERROR, 0); + Debug( LDAP_DEBUG_ANY, + "TLS: error: the certificate file %s is not a file.\n", + filename, 0 ,0 ); + return -1; + } + + slotname = isca ? TLSM_PEM_SLOT_CACERTS : TLSM_PEM_SLOT_CERTS; + slot = PK11_FindSlotByName( slotname ); + + if ( !slot ) { + PRErrorCode errcode = PR_GetError(); + Debug( LDAP_DEBUG_ANY, + "TLS: could not find the slot for the certificate '%s' - error %d:%s.\n", + filename, errcode, PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) ); + return -1; + } + + PK11_SETATTRS( attrs[0], CKA_CLASS, &objClass, sizeof( objClass ) ); + PK11_SETATTRS( attrs[1], CKA_TOKEN, &cktrue, sizeof( CK_BBOOL ) ); + PK11_SETATTRS( attrs[2], CKA_LABEL, (unsigned char *) filename, strlen( filename ) + 1 ); + PK11_SETATTRS( attrs[3], CKA_TRUST, isca ? &cktrue : &ckfalse, sizeof( CK_BBOOL ) ); + + cert = PK11_CreateGenericObject( slot, attrs, 4, PR_FALSE /* isPerm */ ); + + if ( !cert ) { + PRErrorCode errcode = PR_GetError(); + Debug( LDAP_DEBUG_ANY, + "TLS: could not add the certificate '%s' - error %d:%s.\n", + filename, errcode, PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) ); + PK11_FreeSlot( slot ); + return -1; + } + + /* if not CA, we store the certificate in ctx->tc_certificate */ + if ( !isca ) { + if ( PK11_ReadRawAttribute( PK11_TypeGeneric, cert, CKA_VALUE, &certDER ) != SECSuccess ) { + PRErrorCode errcode = PR_GetError(); + Debug( LDAP_DEBUG_ANY, + "TLS: could not get DER of the '%s' certificate - error %d:%s.\n", + filename, errcode, PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) ); + PK11_DestroyGenericObject( cert ); + PK11_FreeSlot( slot ); + return -1; + } + + ctx->tc_certificate = PK11_FindCertFromDERCertItem( slot, &certDER, NULL ); + SECITEM_FreeItem( &certDER, PR_FALSE ); + + if ( !ctx->tc_certificate ) { + PRErrorCode errcode = PR_GetError(); + Debug( LDAP_DEBUG_ANY, + "TLS: could not get certificate '%s' using DER - error %d:%s.\n", + filename, errcode, PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) ); + PK11_DestroyGenericObject( cert ); + PK11_FreeSlot( slot ); + return -1; + } + } + + tlsm_add_pem_obj( ctx, cert ); + + PK11_FreeSlot( slot ); + + return 0; +} + +static int +tlsm_ctx_load_private_key( tlsm_ctx *ctx ) +{ + if ( !ctx->tc_certificate ) + return -1; + + if ( ctx->tc_private_key ) + return 0; + + void *pin_arg = SSL_RevealPinArg( ctx->tc_model ); + + SECKEYPrivateKey *unlocked_key = tlsm_find_unlocked_key( ctx, pin_arg ); + Debug( LDAP_DEBUG_ANY, + "TLS: %s unlocked certificate for certificate '%s'.\n", + unlocked_key ? "found" : "no", tlsm_ctx_subject_name( ctx ), 0 ); + + /* prefer unlocked key, then key from opened certdb, then any other */ + if ( unlocked_key ) + ctx->tc_private_key = unlocked_key; + else if ( ctx->tc_certdb_slot && !ctx->tc_using_pem ) + ctx->tc_private_key = PK11_FindKeyByDERCert( ctx->tc_certdb_slot, ctx->tc_certificate, pin_arg ); + else + ctx->tc_private_key = PK11_FindKeyByAnyCert( ctx->tc_certificate, pin_arg ); + + if ( !ctx->tc_private_key ) { + PRErrorCode errcode = PR_GetError(); + Debug(LDAP_DEBUG_ANY, + "TLS: cannot find private key for certificate '%s' (error %d: %s)", + tlsm_ctx_subject_name( ctx ), errcode, + PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) ); + return -1; + } + + return 0; +} + +static int +tlsm_add_key_from_file( tlsm_ctx *ctx, const char *filename ) +{ + PK11SlotInfo * slot = NULL; + PK11GenericObject *key; + CK_ATTRIBUTE attrs[3]; + CK_BBOOL cktrue = CK_TRUE; + CK_OBJECT_CLASS objClass = CKO_PRIVATE_KEY; + int retcode = 0; + PRFileInfo fi; + PRStatus status; + + memset( &fi, 0, sizeof(fi) ); + status = PR_GetFileInfo( filename, &fi ); + if ( PR_SUCCESS != status) { + PRErrorCode errcode = PR_GetError(); + Debug( LDAP_DEBUG_ANY, + "TLS: could not read key file %s - error %d:%s.\n", + filename, errcode, + PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) ); + return -1; + } + + if ( fi.type != PR_FILE_FILE ) { + PR_SetError(PR_IS_DIRECTORY_ERROR, 0); + Debug( LDAP_DEBUG_ANY, + "TLS: error: the key file %s is not a file.\n", + filename, 0 ,0 ); + return -1; + } + + slot = PK11_FindSlotByName( TLSM_PEM_SLOT_CERTS ); + + if ( !slot ) { + PRErrorCode errcode = PR_GetError(); + Debug( LDAP_DEBUG_ANY, + "TLS: could not find the slot for the private key '%s' - error %d:%s.\n", + filename, errcode, PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) ); + return -1; + } + + PK11_SETATTRS( attrs[0], CKA_CLASS, &objClass, sizeof( objClass ) ); + PK11_SETATTRS( attrs[1], CKA_TOKEN, &cktrue, sizeof( CK_BBOOL ) ); + PK11_SETATTRS( attrs[2], CKA_LABEL, (unsigned char *)filename, strlen( filename ) + 1 ); + + key = PK11_CreateGenericObject( slot, attrs, 3, PR_FALSE /* isPerm */ ); + + if ( !key ) { + PRErrorCode errcode = PR_GetError(); + Debug( LDAP_DEBUG_ANY, + "TLS: could not add the private key '%s' - error %d:%s.\n", + filename, errcode, PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) ); + retcode = -1; + } else { + tlsm_add_pem_obj( ctx, key ); + retcode = 0; + + /* When adding an encrypted key the PKCS#11 will be set as removed */ + /* This will force the token to be seen as re-inserted */ + SECMOD_WaitForAnyTokenEvent( pem_module, 0, 0 ); + PK11_IsPresent( slot ); + } + + PK11_FreeSlot( slot ); + + return retcode; +} + +static int +tlsm_init_ca_certs( tlsm_ctx *ctx, const char *cacertfile, const char *cacertdir ) +{ + PRBool isca = PR_TRUE; + PRStatus status = PR_SUCCESS; + PRErrorCode errcode = PR_SUCCESS; + + if ( !cacertfile && !cacertdir ) { + /* no checking - not good, but allowed */ + return 0; + } + + if ( cacertfile ) { + int rc = tlsm_add_cert_from_file( ctx, cacertfile, isca ); + if ( rc ) { + errcode = PR_GetError(); + Debug( LDAP_DEBUG_ANY, + "TLS: %s is not a valid CA certificate file - error %d:%s.\n", + cacertfile, errcode, + PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) ); + /* failure with cacertfile is a hard failure even if cacertdir is + also specified and contains valid CA cert files */ + status = PR_FAILURE; + } else { + Debug( LDAP_DEBUG_TRACE, + "TLS: loaded CA certificate file %s.\n", + cacertfile, 0, 0 ); + } + } + + /* if cacertfile above failed, we will return failure, even + if there is a valid CA cert in cacertdir - but we still + process cacertdir in case the user has enabled trace level + debugging so they can see the processing for cacertdir too */ + /* any cacertdir failures are "soft" failures - if the user specifies + no cert checking, then we allow the tls/ssl to continue, no matter + what was specified for cacertdir, or the contents of the directory + - this is different behavior than that of cacertfile */ + if ( cacertdir ) { + PRFileInfo fi; + PRDir *dir; + PRDirEntry *entry; + PRStatus fistatus = PR_FAILURE; + regex_t hashfile_re; + + memset( &fi, 0, sizeof(fi) ); + fistatus = PR_GetFileInfo( cacertdir, &fi ); + if ( PR_SUCCESS != fistatus) { + errcode = PR_GetError(); + Debug( LDAP_DEBUG_ANY, + "TLS: could not get info about the CA certificate directory %s - error %d:%s.\n", + cacertdir, errcode, + PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) ); + goto done; + } + + if ( fi.type != PR_FILE_DIRECTORY ) { + Debug( LDAP_DEBUG_ANY, + "TLS: error: the CA certificate directory %s is not a directory.\n", + cacertdir, 0 ,0 ); + goto done; + } + + dir = PR_OpenDir( cacertdir ); + if ( NULL == dir ) { + errcode = PR_GetError(); + Debug( LDAP_DEBUG_ANY, + "TLS: could not open the CA certificate directory %s - error %d:%s.\n", + cacertdir, errcode, + PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) ); + goto done; + } + + if ( regcomp( &hashfile_re, PEM_CA_HASH_FILE_REGEX, REG_NOSUB|REG_EXTENDED ) != 0 ) { + Debug( LDAP_DEBUG_ANY, "TLS: cannot compile regex for CA hash files matching\n", 0, 0, 0 ); + goto done; + } + + do { + entry = PR_ReadDir( dir, PR_SKIP_BOTH | PR_SKIP_HIDDEN ); + if ( ( NULL != entry ) && ( NULL != entry->name ) ) { + char *fullpath = NULL; + int match; + + match = regexec( &hashfile_re, entry->name, 0, NULL, 0 ); + if ( match == REG_NOMATCH ) { + Debug( LDAP_DEBUG_TRACE, + "TLS: skipping '%s' - filename does not have expected format " + "(certificate hash with numeric suffix)\n", entry->name, 0, 0 ); + continue; + } else if ( match != 0 ) { + Debug( LDAP_DEBUG_ANY, + "TLS: cannot execute regex for CA hash file matching (%d).\n", + match, 0, 0 ); + continue; + } + + fullpath = PR_smprintf( "%s/%s", cacertdir, entry->name ); + if ( !tlsm_add_cert_from_file( ctx, fullpath, isca ) ) { + Debug( LDAP_DEBUG_TRACE, + "TLS: loaded CA certificate file %s from CA certificate directory %s.\n", + fullpath, cacertdir, 0 ); + } else { + errcode = PR_GetError(); + Debug( LDAP_DEBUG_TRACE, + "TLS: %s is not a valid CA certificate file - error %d:%s.\n", + fullpath, errcode, + PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) ); + } + PR_smprintf_free( fullpath ); + } + } while ( NULL != entry ); + regfree ( &hashfile_re ); + PR_CloseDir( dir ); + } +done: + if ( status != PR_SUCCESS ) { + return -1; + } + + return 0; +} + +/* + * NSS supports having multiple cert/key databases in the same + * directory, each one having a unique string prefix e.g. + * slapd-01-cert8.db - the prefix here is "slapd-01-" + * this function examines the given certdir - if it looks like + * /path/to/directory/prefix it will return the + * /path/to/directory part in realcertdir, and the prefix in prefix + */ +static void +tlsm_get_certdb_prefix( const char *certdir, char **realcertdir, char **prefix ) +{ + char sep = PR_GetDirectorySeparator(); + char *ptr = NULL; + struct PRFileInfo prfi; + PRStatus prc; + + *realcertdir = (char *)certdir; /* default is the one passed in */ + + /* if certdir is not given, just return */ + if ( !certdir ) { + return; + } + + prc = PR_GetFileInfo( certdir, &prfi ); + /* if certdir exists (file or directory) then it cannot specify a prefix */ + if ( prc == PR_SUCCESS ) { + return; + } + + /* if certdir was given, and there is a '/' in certdir, see if there + is anything after the last '/' - if so, assume it is the prefix */ + if ( ( ( ptr = strrchr( certdir, sep ) ) ) && *(ptr+1) ) { + *realcertdir = PL_strndup( certdir, ptr-certdir ); + *prefix = PL_strdup( ptr+1 ); + } + + return; +} + +/* + * Currently mutiple MozNSS contexts share one certificate storage. When the + * certdb is being opened, only new certificates are added to the storage. + * When different databases are used, conflicting nicknames make the + * certificate lookup by the nickname impossible. In addition a token + * description might be prepended in certain conditions. + * + * In order to make the certificate lookup by nickname possible, we explicitly + * open each database using SECMOD_OpenUserDB and assign it the token + * description. The token description is generated using ctx->tc_unique value, + * which is unique for each context. + */ +static PK11SlotInfo * +tlsm_init_open_certdb( tlsm_ctx *ctx, const char *dbdir, const char *prefix ) +{ + PK11SlotInfo *slot = NULL; + char *token_desc = NULL; + char *config = NULL; + + token_desc = PR_smprintf( TLSM_CERTDB_DESC_FMT, ctx->tc_unique ); + config = PR_smprintf( "configDir='%s' tokenDescription='%s' certPrefix='%s' keyPrefix='%s' flags=readOnly", + dbdir, token_desc, prefix, prefix ); + Debug( LDAP_DEBUG_TRACE, "TLS: certdb config: %s\n", config, 0, 0 ); + + slot = SECMOD_OpenUserDB( config ); + if ( !slot ) { + PRErrorCode errcode = PR_GetError(); + Debug( LDAP_DEBUG_TRACE, "TLS: cannot open certdb '%s', error %d:%s\n", dbdir, errcode, + PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) ); + } + + if ( token_desc ) + PR_smprintf_free( token_desc ); + if ( config ) + PR_smprintf_free( config ); + + return slot; +} + +/* + * This is the part of the init we defer until we get the + * actual security configuration information. This is + * only called once, protected by a PRCallOnce + * NOTE: This must be done before the first call to SSL_ImportFD, + * especially the setting of the policy + * NOTE: This must be called after fork() + */ +static int +tlsm_deferred_init( void *arg ) +{ + tlsm_ctx *ctx = (tlsm_ctx *)arg; + struct ldaptls *lt = ctx->tc_config; + const char *securitydirs[3]; + int ii; + int nn; + PRErrorCode errcode = 1; +#ifdef HAVE_NSS_INITCONTEXT + NSSInitParameters initParams; + NSSInitContext *initctx = NULL; + PK11SlotInfo *certdb_slot = NULL; +#endif + SECStatus rc; + int done = 0; + +#ifdef HAVE_SECMOD_RESTARTMODULES + /* NSS enforces the pkcs11 requirement that modules should be unloaded after + a fork() - since there is no portable way to determine if NSS has been + already initialized in a parent process, we just call SECMOD_RestartModules + with force == FALSE - if the module has been unloaded due to a fork, it will + be reloaded, otherwise, it is a no-op */ + if ( SECFailure == ( rc = SECMOD_RestartModules(PR_FALSE /* do not force */) ) ) { + errcode = PORT_GetError(); + if ( errcode != SEC_ERROR_NOT_INITIALIZED ) { + Debug( LDAP_DEBUG_TRACE, + "TLS: could not restart the security modules: %d:%s\n", + errcode, PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ), 0 ); + } else { + errcode = 1; + } + } +#endif + +#ifdef HAVE_NSS_INITCONTEXT + memset( &initParams, 0, sizeof( initParams ) ); + initParams.length = sizeof( initParams ); +#endif /* HAVE_NSS_INITCONTEXT */ + +#ifdef LDAP_R_COMPILE + if ( PR_CallOnce( &tlsm_init_mutex_callonce, tlsm_thr_init_callonce ) ) { + return -1; + } +#endif /* LDAP_R_COMPILE */ + +#ifndef HAVE_NSS_INITCONTEXT + if ( !NSS_IsInitialized() ) { +#endif /* HAVE_NSS_INITCONTEXT */ + /* + MOZNSS_DIR will override everything else - you can + always set MOZNSS_DIR to force the use of this + directory + If using MOZNSS, specify the location of the moznss db dir + in the cacertdir directive of the OpenLDAP configuration. + DEFAULT_MOZNSS_DIR will only be used if the code cannot + find a security dir to use based on the current + settings + */ + nn = 0; + securitydirs[nn++] = PR_GetEnv( "MOZNSS_DIR" ); + securitydirs[nn++] = lt->lt_cacertdir; + securitydirs[nn++] = PR_GetEnv( "DEFAULT_MOZNSS_DIR" ); + for ( ii = 0; !done && ( ii < nn ); ++ii ) { + char *realcertdir = NULL; + const char *defprefix = ""; + char *prefix = (char *)defprefix; + const char *securitydir = securitydirs[ii]; + if ( NULL == securitydir ) { + continue; + } + + tlsm_get_certdb_prefix( securitydir, &realcertdir, &prefix ); + + /* initialize only moddb; certdb will be initialized explicitly */ +#ifdef HAVE_NSS_INITCONTEXT +#ifdef INITCONTEXT_HACK + if ( !NSS_IsInitialized() && ctx->tc_is_server ) { + rc = NSS_Initialize( realcertdir, prefix, prefix, SECMOD_DB, NSS_INIT_READONLY ); + } else { + initctx = NSS_InitContext( realcertdir, prefix, prefix, SECMOD_DB, + &initParams, NSS_INIT_READONLY|NSS_INIT_NOCERTDB ); + } +#else + initctx = NSS_InitContext( realcertdir, prefix, prefix, SECMOD_DB, + &initParams, NSS_INIT_READONLY|NSS_INIT_NOCERTDB ); +#endif + rc = SECFailure; + + if ( initctx != NULL ) { + certdb_slot = tlsm_init_open_certdb( ctx, realcertdir, prefix ); + if ( certdb_slot ) { + rc = SECSuccess; + ctx->tc_initctx = initctx; + ctx->tc_certdb_slot = certdb_slot; + } else { + NSS_ShutdownContext( initctx ); + initctx = NULL; + } + } +#else + rc = NSS_Initialize( realcertdir, prefix, prefix, SECMOD_DB, NSS_INIT_READONLY ); +#endif + + if ( rc != SECSuccess ) { + errcode = PORT_GetError(); + if ( securitydirs[ii] != lt->lt_cacertdir) { + Debug( LDAP_DEBUG_TRACE, + "TLS: could not initialize moznss using security dir %s prefix %s - error %d.\n", + realcertdir, prefix, errcode ); + } + } else { + /* success */ + Debug( LDAP_DEBUG_TRACE, "TLS: using moznss security dir %s prefix %s.\n", + realcertdir, prefix, 0 ); + errcode = 0; + done = 1; + } + if ( realcertdir != securitydir ) { + PL_strfree( realcertdir ); + } + if ( prefix != defprefix ) { + PL_strfree( prefix ); + } + } + + if ( errcode ) { /* no moznss db found, or not using moznss db */ +#ifdef HAVE_NSS_INITCONTEXT + int flags = NSS_INIT_READONLY|NSS_INIT_NOCERTDB|NSS_INIT_NOMODDB; +#ifdef INITCONTEXT_HACK + if ( !NSS_IsInitialized() && ctx->tc_is_server ) { + rc = NSS_NoDB_Init( NULL ); + } else { + initctx = NSS_InitContext( CERTDB_NONE, PREFIX_NONE, PREFIX_NONE, SECMOD_DB, + &initParams, flags ); + rc = (initctx == NULL) ? SECFailure : SECSuccess; + } +#else + initctx = NSS_InitContext( CERTDB_NONE, PREFIX_NONE, PREFIX_NONE, SECMOD_DB, + &initParams, flags ); + if ( initctx ) { + ctx->tc_initctx = initctx; + rc = SECSuccess; + } else { + rc = SECFailure; + } +#endif +#else + rc = NSS_NoDB_Init( NULL ); +#endif + if ( rc != SECSuccess ) { + errcode = PORT_GetError(); + Debug( LDAP_DEBUG_ANY, + "TLS: could not initialize moznss - error %d:%s.\n", + errcode, PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ), 0 ); + return -1; + } + } + + if ( errcode || lt->lt_cacertfile ) { + /* initialize the PEM module */ + if ( tlsm_init_pem_module() ) { + int pem_errcode = PORT_GetError(); + Debug( LDAP_DEBUG_ANY, + "TLS: could not initialize moznss PEM module - error %d:%s.\n", + pem_errcode, PR_ErrorToString( pem_errcode, PR_LANGUAGE_I_DEFAULT ), 0 ); + + if ( errcode ) /* PEM is required */ + return -1; + + } else if ( !errcode ) { + tlsm_init_ca_certs( ctx, lt->lt_cacertfile, NULL ); + } + } + + if ( errcode ) { + if ( tlsm_init_ca_certs( ctx, lt->lt_cacertfile, lt->lt_cacertdir ) ) { + /* if we tried to use lt->lt_cacertdir as an NSS key/cert db, errcode + will be a value other than 1 - print an error message so that the + user will know that failed too */ + if ( ( errcode != 1 ) && ( lt->lt_cacertdir ) ) { + char *realcertdir = NULL; + char *prefix = NULL; + tlsm_get_certdb_prefix( lt->lt_cacertdir, &realcertdir, &prefix ); + Debug( LDAP_DEBUG_TRACE, + "TLS: could not initialize moznss using security dir %s prefix %s - error %d.\n", + realcertdir, prefix ? prefix : "", errcode ); + if ( realcertdir != lt->lt_cacertdir ) { + PL_strfree( realcertdir ); + } + PL_strfree( prefix ); + } + return -1; + } + } + + NSS_SetDomesticPolicy(); + + PK11_SetPasswordFunc( tlsm_pin_prompt ); + + /* register cleanup function */ + if ( tlsm_register_nss_shutdown() ) { + errcode = PORT_GetError(); + Debug( LDAP_DEBUG_ANY, + "TLS: could not register NSS shutdown function: %d:%s\n", + errcode, PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ), 0 ); + return -1; + } + + if ( ctx->tc_is_server ) { + /* 0 means use the defaults here */ + SSL_ConfigServerSessionIDCache( 0, 0, 0, NULL ); + } + +#ifndef HAVE_NSS_INITCONTEXT + } +#endif /* HAVE_NSS_INITCONTEXT */ + + return 0; +} + +/* + * Find and verify the certificate. + * The key is loaded and stored in ctx->tc_private_key + */ +static int +tlsm_find_and_verify_cert_key( tlsm_ctx *ctx ) +{ + SECCertificateUsage certUsage; + PRBool checkSig; + SECStatus status; + void *pin_arg; + + if ( tlsm_ctx_load_private_key( ctx ) ) + return -1; + + pin_arg = SSL_RevealPinArg( ctx->tc_model ); + certUsage = ctx->tc_is_server ? certificateUsageSSLServer : certificateUsageSSLClient; + checkSig = ctx->tc_verify_cert ? PR_TRUE : PR_FALSE; + + status = tlsm_verify_cert( ctx->tc_certdb, ctx->tc_certificate, pin_arg, + checkSig, certUsage, ctx->tc_warn_only, PR_TRUE ); + + return status == SECSuccess ? 0 : -1; +} + +static int +tlsm_get_client_auth_data( void *arg, PRFileDesc *fd, + CERTDistNames *caNames, CERTCertificate **pRetCert, + SECKEYPrivateKey **pRetKey ) +{ + tlsm_ctx *ctx = (tlsm_ctx *)arg; + + if ( pRetCert ) + *pRetCert = CERT_DupCertificate( ctx->tc_certificate ); + + if ( pRetKey ) + *pRetKey = SECKEY_CopyPrivateKey( ctx->tc_private_key ); + + return SECSuccess; +} + +/* + * ctx must have a tc_model that is valid +*/ +static int +tlsm_clientauth_init( tlsm_ctx *ctx ) +{ + SECStatus status = SECFailure; + int rc; + PRBool saveval; + + saveval = ctx->tc_warn_only; + ctx->tc_warn_only = PR_TRUE; + rc = tlsm_find_and_verify_cert_key(ctx); + ctx->tc_warn_only = saveval; + if ( rc ) { + Debug( LDAP_DEBUG_ANY, + "TLS: error: unable to set up client certificate authentication for " + "certificate named %s\n", tlsm_ctx_subject_name(ctx), 0, 0 ); + return -1; + } + + status = SSL_GetClientAuthDataHook( ctx->tc_model, + tlsm_get_client_auth_data, + (void *)ctx ); + + return ( status == SECSuccess ? 0 : -1 ); +} + +/* + * Tear down the TLS subsystem. Should only be called once. + */ +static void +tlsm_destroy( void ) +{ +#ifdef LDAP_R_COMPILE + ldap_pvt_thread_mutex_destroy( &tlsm_ctx_count_mutex ); + ldap_pvt_thread_mutex_destroy( &tlsm_init_mutex ); + ldap_pvt_thread_mutex_destroy( &tlsm_pem_mutex ); +#endif +} + +static struct ldaptls * +tlsm_copy_config ( const struct ldaptls *config ) +{ + struct ldaptls *copy; + + assert( config ); + + copy = LDAP_MALLOC( sizeof( *copy ) ); + if ( !copy ) + return NULL; + + memset( copy, 0, sizeof( *copy ) ); + + if ( config->lt_certfile ) + copy->lt_certfile = LDAP_STRDUP( config->lt_certfile ); + if ( config->lt_keyfile ) + copy->lt_keyfile = LDAP_STRDUP( config->lt_keyfile ); + if ( config->lt_dhfile ) + copy->lt_dhfile = LDAP_STRDUP( config->lt_dhfile ); + if ( config->lt_cacertfile ) + copy->lt_cacertfile = LDAP_STRDUP( config->lt_cacertfile ); + if ( config->lt_cacertdir ) + copy->lt_cacertdir = LDAP_STRDUP( config->lt_cacertdir ); + if ( config->lt_ciphersuite ) + copy->lt_ciphersuite = LDAP_STRDUP( config->lt_ciphersuite ); + if ( config->lt_crlfile ) + copy->lt_crlfile = LDAP_STRDUP( config->lt_crlfile ); + if ( config->lt_randfile ) + copy->lt_randfile = LDAP_STRDUP( config->lt_randfile ); + + copy->lt_protocol_min = config->lt_protocol_min; + + return copy; +} + +static void +tlsm_free_config ( struct ldaptls *config ) +{ + assert( config ); + + if ( config->lt_certfile ) + LDAP_FREE( config->lt_certfile ); + if ( config->lt_keyfile ) + LDAP_FREE( config->lt_keyfile ); + if ( config->lt_dhfile ) + LDAP_FREE( config->lt_dhfile ); + if ( config->lt_cacertfile ) + LDAP_FREE( config->lt_cacertfile ); + if ( config->lt_cacertdir ) + LDAP_FREE( config->lt_cacertdir ); + if ( config->lt_ciphersuite ) + LDAP_FREE( config->lt_ciphersuite ); + if ( config->lt_crlfile ) + LDAP_FREE( config->lt_crlfile ); + if ( config->lt_randfile ) + LDAP_FREE( config->lt_randfile ); + + LDAP_FREE( config ); +} + +static tls_ctx * +tlsm_ctx_new ( struct ldapoptions *lo ) +{ + tlsm_ctx *ctx; + + ctx = LDAP_MALLOC( sizeof (*ctx) ); + if ( ctx ) { + ctx->tc_refcnt = 1; +#ifdef LDAP_R_COMPILE + ldap_pvt_thread_mutex_init( &ctx->tc_refmutex ); +#endif + LDAP_MUTEX_LOCK( &tlsm_ctx_count_mutex ); + ctx->tc_unique = tlsm_ctx_count++; + LDAP_MUTEX_UNLOCK( &tlsm_ctx_count_mutex ); + ctx->tc_config = NULL; /* populated later by tlsm_ctx_init */ + ctx->tc_certdb = NULL; + ctx->tc_certdb_slot = NULL; + ctx->tc_certificate = NULL; + ctx->tc_private_key = NULL; + ctx->tc_pin_file = NULL; + ctx->tc_model = NULL; + memset(&ctx->tc_callonce, 0, sizeof(ctx->tc_callonce)); + ctx->tc_require_cert = lo->ldo_tls_require_cert; + ctx->tc_verify_cert = PR_FALSE; + ctx->tc_using_pem = PR_FALSE; +#ifdef HAVE_NSS_INITCONTEXT + ctx->tc_initctx = NULL; +#endif /* HAVE_NSS_INITCONTEXT */ + ctx->tc_pem_objs = NULL; + ctx->tc_n_pem_objs = 0; + ctx->tc_warn_only = PR_FALSE; + } + return (tls_ctx *)ctx; +} + +static void +tlsm_ctx_ref( tls_ctx *ctx ) +{ + tlsm_ctx *c = (tlsm_ctx *)ctx; + LDAP_MUTEX_LOCK( &c->tc_refmutex ); + c->tc_refcnt++; + LDAP_MUTEX_UNLOCK( &c->tc_refmutex ); +} + +static void +tlsm_ctx_free ( tls_ctx *ctx ) +{ + tlsm_ctx *c = (tlsm_ctx *)ctx; + int refcount; + + if ( !c ) return; + + LDAP_MUTEX_LOCK( &c->tc_refmutex ); + refcount = --c->tc_refcnt; + LDAP_MUTEX_UNLOCK( &c->tc_refmutex ); + if ( refcount ) + return; + + LDAP_MUTEX_LOCK( &tlsm_init_mutex ); + if ( c->tc_model ) + PR_Close( c->tc_model ); + if ( c->tc_certificate ) + CERT_DestroyCertificate( c->tc_certificate ); + if ( c->tc_private_key ) + SECKEY_DestroyPrivateKey( c->tc_private_key ); + c->tc_certdb = NULL; /* if not the default, may have to clean up */ + if ( c->tc_certdb_slot ) { + if ( SECMOD_CloseUserDB( c->tc_certdb_slot ) ) { + PRErrorCode errcode = PR_GetError(); + Debug( LDAP_DEBUG_ANY, + "TLS: could not close certdb slot - error %d:%s.\n", + errcode, PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ), 0 ); + } + } + if ( c->tc_pin_file ) { + PL_strfree( c->tc_pin_file ); + c->tc_pin_file = NULL; + } + tlsm_free_pem_objs( c ); +#ifdef HAVE_NSS_INITCONTEXT + if ( c->tc_initctx ) { + if ( NSS_ShutdownContext( c->tc_initctx ) ) { + PRErrorCode errcode = PR_GetError(); + Debug( LDAP_DEBUG_ANY, + "TLS: could not shutdown NSS - error %d:%s.\n", + errcode, PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ), 0 ); + } + } + c->tc_initctx = NULL; +#endif /* HAVE_NSS_INITCONTEXT */ + LDAP_MUTEX_UNLOCK( &tlsm_init_mutex ); +#ifdef LDAP_R_COMPILE + ldap_pvt_thread_mutex_destroy( &c->tc_refmutex ); +#endif + + if ( c->tc_config ) + tlsm_free_config( c->tc_config ); + + LDAP_FREE( c ); +} + +/* + * initialize a new TLS context + */ +static int +tlsm_ctx_init( struct ldapoptions *lo, struct ldaptls *lt, int is_server ) +{ + tlsm_ctx *ctx = (tlsm_ctx *)lo->ldo_tls_ctx; + ctx->tc_config = tlsm_copy_config( lt ); + ctx->tc_is_server = is_server; + + return 0; +} + +/* returns true if the given string looks like + "tokenname" ":" "certnickname" + This is true if there is a ':' colon character + in the string and the colon is not the first + or the last character in the string +*/ +static int +tlsm_is_tokenname_certnick( const char *certfile ) +{ + if ( certfile ) { + const char *ptr = PL_strchr( certfile, ':' ); + return ptr && (ptr != certfile) && (*(ptr+1)); + } + return 0; +} + +static int +tlsm_deferred_ctx_init( void *arg ) +{ + tlsm_ctx *ctx = (tlsm_ctx *)arg; + PRBool sslv2 = PR_FALSE; + PRBool sslv3 = PR_TRUE; + PRBool tlsv1 = PR_TRUE; + PRBool request_cert = PR_FALSE; + PRInt32 require_cert = PR_FALSE; + PRFileDesc *fd; + struct ldaptls *lt; + + if ( tlsm_deferred_init( ctx ) ) { + Debug( LDAP_DEBUG_ANY, + "TLS: could not perform TLS system initialization.\n", + 0, 0, 0 ); + return -1; + } + + ctx->tc_certdb = CERT_GetDefaultCertDB(); /* If there is ever a per-context db, change this */ + + fd = PR_CreateIOLayerStub( tlsm_layer_id, &tlsm_PR_methods ); + if ( fd ) { + ctx->tc_model = SSL_ImportFD( NULL, fd ); + } + + if ( !ctx->tc_model ) { + PRErrorCode err = PR_GetError(); + Debug( LDAP_DEBUG_ANY, + "TLS: could perform TLS socket I/O layer initialization - error %d:%s.\n", + err, PR_ErrorToString( err, PR_LANGUAGE_I_DEFAULT ), NULL ); + + if ( fd ) { + PR_Close( fd ); + } + return -1; + } + + if ( SSL_SetPKCS11PinArg(ctx->tc_model, ctx) ) { + Debug( LDAP_DEBUG_ANY, + "TLS: could not set pin prompt argument\n", 0, 0, 0); + return -1; + } + + if ( SECSuccess != SSL_OptionSet( ctx->tc_model, SSL_SECURITY, PR_TRUE ) ) { + Debug( LDAP_DEBUG_ANY, + "TLS: could not set secure mode on.\n", + 0, 0, 0 ); + return -1; + } + + lt = ctx->tc_config; + + /* default is sslv3 and tlsv1 */ + if ( lt->lt_protocol_min ) { + if ( lt->lt_protocol_min > LDAP_OPT_X_TLS_PROTOCOL_SSL3 ) { + sslv3 = PR_FALSE; + } else if ( lt->lt_protocol_min <= LDAP_OPT_X_TLS_PROTOCOL_SSL2 ) { + sslv2 = PR_TRUE; + Debug( LDAP_DEBUG_ANY, + "TLS: warning: minimum TLS protocol level set to " + "include SSLv2 - SSLv2 is insecure - do not use\n", 0, 0, 0 ); + } + } + if ( SECSuccess != SSL_OptionSet( ctx->tc_model, SSL_ENABLE_SSL2, sslv2 ) ) { + Debug( LDAP_DEBUG_ANY, + "TLS: could not set SSLv2 mode on.\n", + 0, 0, 0 ); + return -1; + } + if ( SECSuccess != SSL_OptionSet( ctx->tc_model, SSL_ENABLE_SSL3, sslv3 ) ) { + Debug( LDAP_DEBUG_ANY, + "TLS: could not set SSLv3 mode on.\n", + 0, 0, 0 ); + return -1; + } + if ( SECSuccess != SSL_OptionSet( ctx->tc_model, SSL_ENABLE_TLS, tlsv1 ) ) { + Debug( LDAP_DEBUG_ANY, + "TLS: could not set TLSv1 mode on.\n", + 0, 0, 0 ); + return -1; + } + + if ( SECSuccess != SSL_OptionSet( ctx->tc_model, SSL_HANDSHAKE_AS_CLIENT, !ctx->tc_is_server ) ) { + Debug( LDAP_DEBUG_ANY, + "TLS: could not set handshake as client.\n", + 0, 0, 0 ); + return -1; + } + if ( SECSuccess != SSL_OptionSet( ctx->tc_model, SSL_HANDSHAKE_AS_SERVER, ctx->tc_is_server ) ) { + Debug( LDAP_DEBUG_ANY, + "TLS: could not set handshake as server.\n", + 0, 0, 0 ); + return -1; + } + + if ( lt->lt_ciphersuite ) { + if ( tlsm_parse_ciphers( ctx, lt->lt_ciphersuite ) ) { + Debug( LDAP_DEBUG_ANY, + "TLS: could not set cipher list %s.\n", + lt->lt_ciphersuite, 0, 0 ); + return -1; + } + } else if ( tlsm_parse_ciphers( ctx, "DEFAULT" ) ) { + Debug( LDAP_DEBUG_ANY, + "TLS: could not set cipher list DEFAULT.\n", + 0, 0, 0 ); + return -1; + } + + if ( !ctx->tc_require_cert ) { + ctx->tc_verify_cert = PR_FALSE; + } else if ( !ctx->tc_is_server ) { + request_cert = PR_TRUE; + require_cert = SSL_REQUIRE_NO_ERROR; + if ( ctx->tc_require_cert == LDAP_OPT_X_TLS_DEMAND || + ctx->tc_require_cert == LDAP_OPT_X_TLS_HARD ) { + require_cert = SSL_REQUIRE_ALWAYS; + } + if ( ctx->tc_require_cert != LDAP_OPT_X_TLS_ALLOW ) + ctx->tc_verify_cert = PR_TRUE; + } else { /* server */ + /* server does not request certs by default */ + /* if allow - client may send cert, server will ignore if errors */ + /* if try - client may send cert, server will error if bad cert */ + /* if hard or demand - client must send cert, server will error if bad cert */ + request_cert = PR_TRUE; + require_cert = SSL_REQUIRE_NO_ERROR; + if ( ctx->tc_require_cert == LDAP_OPT_X_TLS_DEMAND || + ctx->tc_require_cert == LDAP_OPT_X_TLS_HARD ) { + require_cert = SSL_REQUIRE_ALWAYS; + } + if ( ctx->tc_require_cert != LDAP_OPT_X_TLS_ALLOW ) { + ctx->tc_verify_cert = PR_TRUE; + } else { + ctx->tc_warn_only = PR_TRUE; + } + } + + if ( SECSuccess != SSL_OptionSet( ctx->tc_model, SSL_REQUEST_CERTIFICATE, request_cert ) ) { + Debug( LDAP_DEBUG_ANY, + "TLS: could not set request certificate mode.\n", + 0, 0, 0 ); + return -1; + } + + if ( SECSuccess != SSL_OptionSet( ctx->tc_model, SSL_REQUIRE_CERTIFICATE, require_cert ) ) { + Debug( LDAP_DEBUG_ANY, + "TLS: could not set require certificate mode.\n", + 0, 0, 0 ); + return -1; + } + + /* set up our cert and key, if any */ + if ( lt->lt_certfile ) { + + /* first search in certdb (lt_certfile is nickname) */ + if ( ctx->tc_certdb ) { + char *tmp_certname; + + if ( tlsm_is_tokenname_certnick( lt->lt_certfile )) { + /* assume already in form tokenname:certnickname */ + tmp_certname = PL_strdup( lt->lt_certfile ); + } else if ( ctx->tc_certdb_slot ) { + tmp_certname = PR_smprintf( TLSM_CERTDB_DESC_FMT ":%s", ctx->tc_unique, lt->lt_certfile ); + } else { + tmp_certname = PR_smprintf( "%s", lt->lt_certfile ); + } + + ctx->tc_certificate = PK11_FindCertFromNickname( tmp_certname, SSL_RevealPinArg( ctx->tc_model ) ); + PR_smprintf_free( tmp_certname ); + + if ( !ctx->tc_certificate ) { + PRErrorCode errcode = PR_GetError(); + Debug( LDAP_DEBUG_ANY, + "TLS: error: the certificate '%s' could not be found in the database - error %d:%s.\n", + lt->lt_certfile, errcode, PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) ); + } + } + + /* fallback to PEM module (lt_certfile is filename) */ + if ( !ctx->tc_certificate ) { + if ( !pem_module && tlsm_init_pem_module() ) { + int pem_errcode = PORT_GetError(); + Debug( LDAP_DEBUG_ANY, + "TLS: fallback to PEM impossible, module cannot be loaded - error %d:%s.\n", + pem_errcode, PR_ErrorToString( pem_errcode, PR_LANGUAGE_I_DEFAULT ), 0 ); + return -1; + } + + /* this sets ctx->tc_certificate to the correct value */ + if ( !tlsm_add_cert_from_file( ctx, lt->lt_certfile, PR_FALSE ) ) { + ctx->tc_using_pem = PR_TRUE; + } + } + + if ( ctx->tc_certificate ) { + Debug( LDAP_DEBUG_ANY, + "TLS: certificate '%s' successfully loaded from %s.\n", lt->lt_certfile, + ctx->tc_using_pem ? "PEM file" : "moznss database", 0); + } else { + return -1; + } + } + + if ( lt->lt_keyfile ) { + /* if using the PEM module, load the PEM file specified by lt_keyfile */ + /* otherwise, assume this is the pininfo for the key */ + if ( ctx->tc_using_pem ) { + int rc = tlsm_add_key_from_file( ctx, lt->lt_keyfile ); + if ( rc ) { + return rc; + } + } else { + if ( ctx->tc_pin_file ) + PL_strfree( ctx->tc_pin_file ); + ctx->tc_pin_file = PL_strdup( lt->lt_keyfile ); + } + } + + /* Set up callbacks for use by clients */ + if ( !ctx->tc_is_server ) { + if ( SSL_OptionSet( ctx->tc_model, SSL_NO_CACHE, PR_TRUE ) != SECSuccess ) { + PRErrorCode err = PR_GetError(); + Debug( LDAP_DEBUG_ANY, + "TLS: error: could not set nocache option for moznss - error %d:%s\n", + err, PR_ErrorToString( err, PR_LANGUAGE_I_DEFAULT ), NULL ); + return -1; + } + + if ( SSL_BadCertHook( ctx->tc_model, tlsm_bad_cert_handler, ctx ) != SECSuccess ) { + PRErrorCode err = PR_GetError(); + Debug( LDAP_DEBUG_ANY, + "TLS: error: could not set bad cert handler for moznss - error %d:%s\n", + err, PR_ErrorToString( err, PR_LANGUAGE_I_DEFAULT ), NULL ); + return -1; + } + + /* + since a cert has been specified, assume the client wants to do cert auth + */ + if ( ctx->tc_certificate ) { + if ( tlsm_clientauth_init( ctx ) ) { + Debug( LDAP_DEBUG_ANY, + "TLS: error: unable to set up client certificate authentication using '%s'\n", + tlsm_ctx_subject_name(ctx), 0, 0 ); + return -1; + } + } + } else { /* set up secure server */ + SSLKEAType certKEA; + SECStatus status; + + /* must have a certificate for the server to use */ + if ( !ctx->tc_certificate ) { + Debug( LDAP_DEBUG_ANY, + "TLS: error: no server certificate: must specify a certificate for the server to use\n", + 0, 0, 0 ); + return -1; + } + + if ( tlsm_find_and_verify_cert_key( ctx ) ) { + Debug( LDAP_DEBUG_ANY, + "TLS: error: unable to find and verify server's cert and key for certificate %s\n", + tlsm_ctx_subject_name(ctx), 0, 0 ); + return -1; + } + + /* configure the socket to be a secure server socket */ + certKEA = NSS_FindCertKEAType( ctx->tc_certificate ); + status = SSL_ConfigSecureServer( ctx->tc_model, ctx->tc_certificate, ctx->tc_private_key, certKEA ); + + if ( SECSuccess != status ) { + PRErrorCode err = PR_GetError(); + Debug( LDAP_DEBUG_ANY, + "TLS: error: unable to configure secure server using certificate '%s' - error %d:%s\n", + tlsm_ctx_subject_name(ctx), err, PR_ErrorToString( err, PR_LANGUAGE_I_DEFAULT ) ); + return -1; + } + } + + /* Callback for authenticating certificate */ + if ( SSL_AuthCertificateHook( ctx->tc_model, tlsm_auth_cert_handler, + ctx ) != SECSuccess ) { + PRErrorCode err = PR_GetError(); + Debug( LDAP_DEBUG_ANY, + "TLS: error: could not set auth cert handler for moznss - error %d:%s\n", + err, PR_ErrorToString( err, PR_LANGUAGE_I_DEFAULT ), NULL ); + return -1; + } + + if ( SSL_HandshakeCallback( ctx->tc_model, tlsm_handshake_complete_cb, ctx ) ) { + PRErrorCode err = PR_GetError(); + Debug( LDAP_DEBUG_ANY, + "TLS: error: could not set handshake callback for moznss - error %d:%s\n", + err, PR_ErrorToString( err, PR_LANGUAGE_I_DEFAULT ), NULL ); + return -1; + } + + tlsm_free_config( ctx->tc_config ); + ctx->tc_config = NULL; + + return 0; +} + +struct tls_data { + tlsm_session *session; + Sockbuf_IO_Desc *sbiod; + /* there seems to be no portable way to determine if the + sockbuf sd has been set to nonblocking mode - the + call to ber_pvt_socket_set_nonblock() takes place + before the tls socket is set up, so we cannot + intercept that call either. + On systems where fcntl is available, we can just + F_GETFL and test for O_NONBLOCK. On other systems, + we will just see if the IO op returns EAGAIN or EWOULDBLOCK, + and just set this flag */ + PRBool nonblock; + /* + * NSS tries hard to be backwards compatible with SSLv2 clients, or + * clients that send an SSLv2 client hello. This message is not + * tagged in any way, so NSS has no way to know if the incoming + * message is a valid SSLv2 client hello or just some bogus data + * (or cleartext LDAP). We store the first byte read from the + * client here. The most common case will be a client sending + * LDAP data instead of SSL encrypted LDAP data. This can happen, + * for example, if using ldapsearch -Z - if the starttls fails, + * the client will fallback to plain cleartext LDAP. So if we + * see that the firstbyte is a valid LDAP tag, we can be + * pretty sure this is happening. + */ + ber_tag_t firsttag; + /* + * NSS doesn't return SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE, etc. + * when it is blocked, so we have to set a flag in the wrapped send + * and recv calls that tells us what operation NSS was last blocked + * on + */ +#define TLSM_READ 1 +#define TLSM_WRITE 2 + int io_flag; +}; + +static struct tls_data * +tlsm_get_pvt_tls_data( PRFileDesc *fd ) +{ + struct tls_data *p; + PRFileDesc *myfd; + + if ( !fd ) { + return NULL; + } + + myfd = PR_GetIdentitiesLayer( fd, tlsm_layer_id ); + + if ( !myfd ) { + return NULL; + } + + p = (struct tls_data *)myfd->secret; + + return p; +} + +static int +tlsm_is_non_ssl_message( PRFileDesc *fd, ber_tag_t *thebyte ) +{ + struct tls_data *p; + + if ( thebyte ) { + *thebyte = LBER_DEFAULT; + } + + p = tlsm_get_pvt_tls_data( fd ); + if ( p == NULL || p->sbiod == NULL ) { + return 0; + } + + if ( p->firsttag == LBER_SEQUENCE ) { + if ( thebyte ) { + *thebyte = p->firsttag; + } + return 1; + } + + return 0; +} + +static tls_session * +tlsm_session_new ( tls_ctx * ctx, int is_server ) +{ + tlsm_ctx *c = (tlsm_ctx *)ctx; + tlsm_session *session; + PRFileDesc *fd; + PRStatus status; + int rc; + + c->tc_is_server = is_server; + LDAP_MUTEX_LOCK( &tlsm_init_mutex ); + status = PR_CallOnceWithArg( &c->tc_callonce, tlsm_deferred_ctx_init, c ); + LDAP_MUTEX_UNLOCK( &tlsm_init_mutex ); + if ( PR_SUCCESS != status ) { + PRErrorCode err = PR_GetError(); + Debug( LDAP_DEBUG_ANY, + "TLS: error: could not initialize moznss security context - error %d:%s\n", + err, PR_ErrorToString(err, PR_LANGUAGE_I_DEFAULT), NULL ); + return NULL; + } + + fd = PR_CreateIOLayerStub( tlsm_layer_id, &tlsm_PR_methods ); + if ( !fd ) { + return NULL; + } + + session = SSL_ImportFD( c->tc_model, fd ); + if ( !session ) { + PR_DELETE( fd ); + return NULL; + } + + rc = SSL_ResetHandshake( session, is_server ); + if ( rc ) { + PRErrorCode err = PR_GetError(); + Debug( LDAP_DEBUG_TRACE, + "TLS: error: new session - reset handshake failure %d - error %d:%s\n", + rc, err, + err ? PR_ErrorToString( err, PR_LANGUAGE_I_DEFAULT ) : "unknown" ); + PR_DELETE( fd ); + PR_Close( session ); + session = NULL; + } + + return (tls_session *)session; +} + +static int +tlsm_session_accept_or_connect( tls_session *session, int is_accept ) +{ + tlsm_session *s = (tlsm_session *)session; + int rc; + const char *op = is_accept ? "accept" : "connect"; + + if ( pem_module ) { + LDAP_MUTEX_LOCK( &tlsm_pem_mutex ); + } + rc = SSL_ForceHandshake( s ); + if ( pem_module ) { + LDAP_MUTEX_UNLOCK( &tlsm_pem_mutex ); + } + if ( rc ) { + PRErrorCode err = PR_GetError(); + rc = -1; + if ( err == PR_WOULD_BLOCK_ERROR ) { + ber_tag_t thetag = LBER_DEFAULT; + /* see if we are blocked because of a bogus packet */ + if ( tlsm_is_non_ssl_message( s, &thetag ) ) { /* see if we received a non-SSL message */ + Debug( LDAP_DEBUG_ANY, + "TLS: error: %s - error - received non-SSL message [0x%x]\n", + op, (unsigned int)thetag, 0 ); + /* reset error to something more descriptive */ + PR_SetError( SSL_ERROR_RX_MALFORMED_HELLO_REQUEST, EPROTO ); + } + } else { + Debug( LDAP_DEBUG_ANY, + "TLS: error: %s - force handshake failure: errno %d - moznss error %d\n", + op, errno, err ); + } + } + + return rc; +} +static int +tlsm_session_accept( tls_session *session ) +{ + return tlsm_session_accept_or_connect( session, 1 ); +} + +static int +tlsm_session_connect( LDAP *ld, tls_session *session ) +{ + return tlsm_session_accept_or_connect( session, 0 ); +} + +static int +tlsm_session_upflags( Sockbuf *sb, tls_session *session, int rc ) +{ + int prerror = PR_GetError(); + + if ( ( prerror == PR_PENDING_INTERRUPT_ERROR ) || ( prerror == PR_WOULD_BLOCK_ERROR ) ) { + tlsm_session *s = (tlsm_session *)session; + struct tls_data *p = tlsm_get_pvt_tls_data( s ); + + if ( p && ( p->io_flag == TLSM_READ ) ) { + sb->sb_trans_needs_read = 1; + return 1; + } else if ( p && ( p->io_flag == TLSM_WRITE ) ) { + sb->sb_trans_needs_write = 1; + return 1; + } + } + + return 0; +} + +static char * +tlsm_session_errmsg( tls_session *sess, int rc, char *buf, size_t len ) +{ + int i; + int prerror = PR_GetError(); + + i = PR_GetErrorTextLength(); + if ( i > len ) { + char *msg = LDAP_MALLOC( i+1 ); + PR_GetErrorText( msg ); + memcpy( buf, msg, len ); + LDAP_FREE( msg ); + } else if ( i ) { + PR_GetErrorText( buf ); + } else if ( prerror ) { + i = PR_snprintf( buf, len, "TLS error %d:%s", + prerror, PR_ErrorToString( prerror, PR_LANGUAGE_I_DEFAULT ) ); + } + + return ( i > 0 ) ? buf : NULL; +} + +static int +tlsm_session_my_dn( tls_session *session, struct berval *der_dn ) +{ + tlsm_session *s = (tlsm_session *)session; + CERTCertificate *cert; + + cert = SSL_LocalCertificate( s ); + if (!cert) return LDAP_INVALID_CREDENTIALS; + + der_dn->bv_val = (char *)cert->derSubject.data; + der_dn->bv_len = cert->derSubject.len; + CERT_DestroyCertificate( cert ); + return 0; +} + +static int +tlsm_session_peer_dn( tls_session *session, struct berval *der_dn ) +{ + tlsm_session *s = (tlsm_session *)session; + CERTCertificate *cert; + + cert = SSL_PeerCertificate( s ); + if (!cert) return LDAP_INVALID_CREDENTIALS; + + der_dn->bv_val = (char *)cert->derSubject.data; + der_dn->bv_len = cert->derSubject.len; + CERT_DestroyCertificate( cert ); + return 0; +} + +/* what kind of hostname were we given? */ +#define IS_DNS 0 +#define IS_IP4 1 +#define IS_IP6 2 + +static int +tlsm_session_chkhost( LDAP *ld, tls_session *session, const char *name_in ) +{ + tlsm_session *s = (tlsm_session *)session; + CERTCertificate *cert; + const char *name, *domain = NULL, *ptr; + int ret, ntype = IS_DNS, nlen, dlen; +#ifdef LDAP_PF_INET6 + struct in6_addr addr; +#else + struct in_addr addr; +#endif + SECItem altname; + SECStatus rv; + + if( ldap_int_hostname && + ( !name_in || !strcasecmp( name_in, "localhost" ) ) ) + { + name = ldap_int_hostname; + } else { + name = name_in; + } + nlen = strlen( name ); + + cert = SSL_PeerCertificate( s ); + if (!cert) { + Debug( LDAP_DEBUG_ANY, + "TLS: unable to get peer certificate.\n", + 0, 0, 0 ); + /* if this was a fatal condition, things would have + * aborted long before now. + */ + return LDAP_SUCCESS; + } + +#ifdef LDAP_PF_INET6 + if (inet_pton(AF_INET6, name, &addr)) { + ntype = IS_IP6; + } else +#endif + if ((ptr = strrchr(name, '.')) && isdigit((unsigned char)ptr[1])) { + if (inet_aton(name, (struct in_addr *)&addr)) ntype = IS_IP4; + } + if (ntype == IS_DNS ) { + domain = strchr( name, '.' ); + if ( domain ) + dlen = nlen - ( domain - name ); + } + + ret = LDAP_LOCAL_ERROR; + + rv = CERT_FindCertExtension( cert, SEC_OID_X509_SUBJECT_ALT_NAME, + &altname ); + if ( rv == SECSuccess && altname.data ) { + PRArenaPool *arena; + CERTGeneralName *names, *cur; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if ( !arena ) { + ret = LDAP_NO_MEMORY; + goto fail; + } + + names = cur = CERT_DecodeAltNameExtension(arena, &altname); + if ( !cur ) + goto altfail; + + do { + char *host; + int hlen; + + /* ignore empty */ + if ( !cur->name.other.len ) continue; + + host = (char *)cur->name.other.data; + hlen = cur->name.other.len; + + if ( cur->type == certDNSName ) { + if ( ntype != IS_DNS ) continue; + + /* is this an exact match? */ + if ( nlen == hlen && !strncasecmp( name, host, nlen )) { + ret = LDAP_SUCCESS; + break; + } + + /* is this a wildcard match? */ + if ( domain && host[0] == '*' && host[1] == '.' && + dlen == hlen-1 && !strncasecmp( domain, host+1, dlen )) { + ret = LDAP_SUCCESS; + break; + } + } else if ( cur->type == certIPAddress ) { + if ( ntype == IS_DNS ) continue; + +#ifdef LDAP_PF_INET6 + if (ntype == IS_IP6 && hlen != sizeof(struct in6_addr)) { + continue; + } else +#endif + if (ntype == IS_IP4 && hlen != sizeof(struct in_addr)) { + continue; + } + if (!memcmp(host, &addr, hlen)) { + ret = LDAP_SUCCESS; + break; + } + } + } while (( cur = CERT_GetNextGeneralName( cur )) != names ); +altfail: + PORT_FreeArena( arena, PR_FALSE ); + SECITEM_FreeItem( &altname, PR_FALSE ); + } + /* no altnames matched, try the CN */ + if ( ret != LDAP_SUCCESS ) { + /* find the last CN */ + CERTRDN *rdn, **rdns; + CERTAVA *lastava = NULL; + char buf[2048]; + + buf[0] = '\0'; + rdns = cert->subject.rdns; + while ( rdns && ( rdn = *rdns++ )) { + CERTAVA *ava, **avas = rdn->avas; + while ( avas && ( ava = *avas++ )) { + if ( CERT_GetAVATag( ava ) == SEC_OID_AVA_COMMON_NAME ) + lastava = ava; + } + } + if ( lastava ) { + SECItem *av = CERT_DecodeAVAValue( &lastava->value ); + if ( av ) { + if ( av->len == nlen && !strncasecmp( name, (char *)av->data, nlen )) { + ret = LDAP_SUCCESS; + } else if ( av->data[0] == '*' && av->data[1] == '.' && + domain && dlen == av->len - 1 && !strncasecmp( domain, + (char *)(av->data+1), dlen )) { + ret = LDAP_SUCCESS; + } else { + int len = av->len; + if ( len >= sizeof(buf) ) + len = sizeof(buf)-1; + memcpy( buf, av->data, len ); + buf[len] = '\0'; + } + SECITEM_FreeItem( av, PR_TRUE ); + } + } + if ( ret != LDAP_SUCCESS ) { + Debug( LDAP_DEBUG_ANY, "TLS: hostname (%s) does not match " + "common name in certificate (%s).\n", + name, buf, 0 ); + ret = LDAP_CONNECT_ERROR; + if ( ld->ld_error ) { + LDAP_FREE( ld->ld_error ); + } + ld->ld_error = LDAP_STRDUP( + _("TLS: hostname does not match CN in peer certificate")); + } + } + +fail: + CERT_DestroyCertificate( cert ); + return ret; +} + +static int +tlsm_session_strength( tls_session *session ) +{ + tlsm_session *s = (tlsm_session *)session; + int rc, keySize; + + rc = SSL_SecurityStatus( s, NULL, NULL, NULL, &keySize, + NULL, NULL ); + return rc ? 0 : keySize; +} + +/* + * TLS support for LBER Sockbufs + */ + +static PRStatus PR_CALLBACK +tlsm_PR_Close(PRFileDesc *fd) +{ + int rc = PR_SUCCESS; + + /* we don't need to actually close anything here, just + pop our io layer off the stack */ + fd->secret = NULL; /* must have been freed before calling PR_Close */ + if ( fd->lower ) { + fd = PR_PopIOLayer( fd, tlsm_layer_id ); + /* if we are not the last layer, pass the close along */ + if ( fd ) { + if ( fd->dtor ) { + fd->dtor( fd ); + } + rc = fd->methods->close( fd ); + } + } else { + /* we are the last layer - just call our dtor */ + fd->dtor(fd); + } + + return rc; +} + +static PRStatus PR_CALLBACK +tlsm_PR_Shutdown(PRFileDesc *fd, PRShutdownHow how) +{ + int rc = PR_SUCCESS; + + if ( fd->lower ) { + rc = PR_Shutdown( fd->lower, how ); + } + + return rc; +} + +static int PR_CALLBACK +tlsm_PR_Recv(PRFileDesc *fd, void *buf, PRInt32 len, PRIntn flags, + PRIntervalTime timeout) +{ + struct tls_data *p; + int rc; + + if ( buf == NULL || len <= 0 ) return 0; + + p = tlsm_get_pvt_tls_data( fd ); + + if ( p == NULL || p->sbiod == NULL ) { + return 0; + } + + rc = LBER_SBIOD_READ_NEXT( p->sbiod, buf, len ); + if (rc <= 0) { + tlsm_map_error( errno ); + if ( errno == EAGAIN || errno == EWOULDBLOCK ) { + p->nonblock = PR_TRUE; /* fd is using non-blocking io */ + } else if ( errno ) { /* real error */ + Debug( LDAP_DEBUG_TRACE, + "TLS: error: tlsm_PR_Recv returned %d - error %d:%s\n", + rc, errno, STRERROR(errno) ); + } + } else if ( ( rc > 0 ) && ( len > 0 ) && ( p->firsttag == LBER_DEFAULT ) ) { + p->firsttag = (ber_tag_t)*((char *)buf); + } + p->io_flag = TLSM_READ; + + return rc; +} + +static int PR_CALLBACK +tlsm_PR_Send(PRFileDesc *fd, const void *buf, PRInt32 len, PRIntn flags, + PRIntervalTime timeout) +{ + struct tls_data *p; + int rc; + + if ( buf == NULL || len <= 0 ) return 0; + + p = tlsm_get_pvt_tls_data( fd ); + + if ( p == NULL || p->sbiod == NULL ) { + return 0; + } + + rc = LBER_SBIOD_WRITE_NEXT( p->sbiod, (char *)buf, len ); + if (rc <= 0) { + tlsm_map_error( errno ); + if ( errno == EAGAIN || errno == EWOULDBLOCK ) { + p->nonblock = PR_TRUE; + } else if ( errno ) { /* real error */ + Debug( LDAP_DEBUG_TRACE, + "TLS: error: tlsm_PR_Send returned %d - error %d:%s\n", + rc, errno, STRERROR(errno) ); + } + } + p->io_flag = TLSM_WRITE; + + return rc; +} + +static int PR_CALLBACK +tlsm_PR_Read(PRFileDesc *fd, void *buf, PRInt32 len) +{ + return tlsm_PR_Recv( fd, buf, len, 0, PR_INTERVAL_NO_TIMEOUT ); +} + +static int PR_CALLBACK +tlsm_PR_Write(PRFileDesc *fd, const void *buf, PRInt32 len) +{ + return tlsm_PR_Send( fd, buf, len, 0, PR_INTERVAL_NO_TIMEOUT ); +} + +static PRStatus PR_CALLBACK +tlsm_PR_GetPeerName(PRFileDesc *fd, PRNetAddr *addr) +{ + struct tls_data *p; + ber_socklen_t len; + + p = tlsm_get_pvt_tls_data( fd ); + + if ( p == NULL || p->sbiod == NULL ) { + return PR_FAILURE; + } + len = sizeof(PRNetAddr); + return getpeername( p->sbiod->sbiod_sb->sb_fd, (struct sockaddr *)addr, &len ); +} + +static PRStatus PR_CALLBACK +tlsm_PR_GetSocketOption(PRFileDesc *fd, PRSocketOptionData *data) +{ + struct tls_data *p; + p = tlsm_get_pvt_tls_data( fd ); + + if ( p == NULL || data == NULL ) { + return PR_FAILURE; + } + + /* only the nonblocking option is supported at this time + MozNSS SSL code needs it */ + if ( data->option != PR_SockOpt_Nonblocking ) { + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return PR_FAILURE; + } +#ifdef HAVE_FCNTL + int flags = fcntl( p->sbiod->sbiod_sb->sb_fd, F_GETFL ); + data->value.non_blocking = (flags & O_NONBLOCK) ? PR_TRUE : PR_FALSE; +#else /* punt :P */ + data->value.non_blocking = p->nonblock; +#endif + return PR_SUCCESS; +} + +static PRStatus PR_CALLBACK +tlsm_PR_prs_unimp() +{ + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return PR_FAILURE; +} + +static PRFileDesc * PR_CALLBACK +tlsm_PR_pfd_unimp() +{ + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return NULL; +} + +static PRInt16 PR_CALLBACK +tlsm_PR_i16_unimp() +{ + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return SECFailure; +} + +static PRInt32 PR_CALLBACK +tlsm_PR_i32_unimp() +{ + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return SECFailure; +} + +static PRInt64 PR_CALLBACK +tlsm_PR_i64_unimp() +{ + PRInt64 res; + + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + LL_I2L(res, -1L); + return res; +} + +static const PRIOMethods tlsm_PR_methods = { + PR_DESC_LAYERED, + tlsm_PR_Close, /* close */ + tlsm_PR_Read, /* read */ + tlsm_PR_Write, /* write */ + tlsm_PR_i32_unimp, /* available */ + tlsm_PR_i64_unimp, /* available64 */ + tlsm_PR_prs_unimp, /* fsync */ + tlsm_PR_i32_unimp, /* seek */ + tlsm_PR_i64_unimp, /* seek64 */ + tlsm_PR_prs_unimp, /* fileInfo */ + tlsm_PR_prs_unimp, /* fileInfo64 */ + tlsm_PR_i32_unimp, /* writev */ + tlsm_PR_prs_unimp, /* connect */ + tlsm_PR_pfd_unimp, /* accept */ + tlsm_PR_prs_unimp, /* bind */ + tlsm_PR_prs_unimp, /* listen */ + (PRShutdownFN)tlsm_PR_Shutdown, /* shutdown */ + tlsm_PR_Recv, /* recv */ + tlsm_PR_Send, /* send */ + tlsm_PR_i32_unimp, /* recvfrom */ + tlsm_PR_i32_unimp, /* sendto */ + (PRPollFN)tlsm_PR_i16_unimp, /* poll */ + tlsm_PR_i32_unimp, /* acceptread */ + tlsm_PR_i32_unimp, /* transmitfile */ + tlsm_PR_prs_unimp, /* getsockname */ + tlsm_PR_GetPeerName, /* getpeername */ + tlsm_PR_i32_unimp, /* getsockopt OBSOLETE */ + tlsm_PR_i32_unimp, /* setsockopt OBSOLETE */ + tlsm_PR_GetSocketOption, /* getsocketoption */ + tlsm_PR_i32_unimp, /* setsocketoption */ + tlsm_PR_i32_unimp, /* Send a (partial) file with header/trailer*/ + (PRConnectcontinueFN)tlsm_PR_prs_unimp, /* connectcontinue */ + tlsm_PR_i32_unimp, /* reserved for future use */ + tlsm_PR_i32_unimp, /* reserved for future use */ + tlsm_PR_i32_unimp, /* reserved for future use */ + tlsm_PR_i32_unimp /* reserved for future use */ +}; + +/* + * Initialize TLS subsystem. Should be called only once. + * See tlsm_deferred_init for the bulk of the init process + */ +static int +tlsm_init( void ) +{ + char *nofork = PR_GetEnv( "NSS_STRICT_NOFORK" ); + + PR_Init(0, 0, 0); + + tlsm_layer_id = PR_GetUniqueIdentity( "OpenLDAP" ); + + /* + * There are some applications that acquire a crypto context in the parent process + * and expect that crypto context to work after a fork(). This does not work + * with NSS using strict PKCS11 compliance mode. We set this environment + * variable here to tell the software encryption module/token to allow crypto + * contexts to persist across a fork(). However, if you are using some other + * module or encryption device that supports and expects full PKCS11 semantics, + * the only recourse is to rewrite the application with atfork() handlers to save + * the crypto context in the parent and restore (and SECMOD_RestartModules) the + * context in the child. + */ + if ( !nofork ) { + /* will leak one time */ + char *noforkenvvar = PL_strdup( "NSS_STRICT_NOFORK=DISABLED" ); + PR_SetEnv( noforkenvvar ); + } + + return 0; +} + +static int +tlsm_sb_setup( Sockbuf_IO_Desc *sbiod, void *arg ) +{ + struct tls_data *p; + tlsm_session *session = arg; + PRFileDesc *fd; + + assert( sbiod != NULL ); + + p = LBER_MALLOC( sizeof( *p ) ); + if ( p == NULL ) { + return -1; + } + + fd = PR_GetIdentitiesLayer( session, tlsm_layer_id ); + if ( !fd ) { + LBER_FREE( p ); + return -1; + } + + fd->secret = (PRFilePrivate *)p; + p->session = session; + p->sbiod = sbiod; + p->firsttag = LBER_DEFAULT; + sbiod->sbiod_pvt = p; + return 0; +} + +static int +tlsm_sb_remove( Sockbuf_IO_Desc *sbiod ) +{ + struct tls_data *p; + + assert( sbiod != NULL ); + assert( sbiod->sbiod_pvt != NULL ); + + p = (struct tls_data *)sbiod->sbiod_pvt; + PR_Close( p->session ); + LBER_FREE( sbiod->sbiod_pvt ); + sbiod->sbiod_pvt = NULL; + return 0; +} + +static int +tlsm_sb_close( Sockbuf_IO_Desc *sbiod ) +{ + struct tls_data *p; + + assert( sbiod != NULL ); + assert( sbiod->sbiod_pvt != NULL ); + + p = (struct tls_data *)sbiod->sbiod_pvt; + PR_Shutdown( p->session, PR_SHUTDOWN_BOTH ); + return 0; +} + +static int +tlsm_sb_ctrl( Sockbuf_IO_Desc *sbiod, int opt, void *arg ) +{ + struct tls_data *p; + + assert( sbiod != NULL ); + assert( sbiod->sbiod_pvt != NULL ); + + p = (struct tls_data *)sbiod->sbiod_pvt; + + if ( opt == LBER_SB_OPT_GET_SSL ) { + *((tlsm_session **)arg) = p->session; + return 1; + + } else if ( opt == LBER_SB_OPT_DATA_READY ) { + if ( p && ( SSL_DataPending( p->session ) > 0 ) ) { + return 1; + } + + } + + return LBER_SBIOD_CTRL_NEXT( sbiod, opt, arg ); +} + +static ber_slen_t +tlsm_sb_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len) +{ + struct tls_data *p; + ber_slen_t ret; + int err; + + assert( sbiod != NULL ); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + + p = (struct tls_data *)sbiod->sbiod_pvt; + + ret = PR_Recv( p->session, buf, len, 0, PR_INTERVAL_NO_TIMEOUT ); + if ( ret < 0 ) { + err = PR_GetError(); + if ( err == PR_PENDING_INTERRUPT_ERROR || err == PR_WOULD_BLOCK_ERROR ) { + sbiod->sbiod_sb->sb_trans_needs_read = 1; + sock_errset(EWOULDBLOCK); + } + } else { + sbiod->sbiod_sb->sb_trans_needs_read = 0; + } + return ret; +} + +static ber_slen_t +tlsm_sb_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len) +{ + struct tls_data *p; + ber_slen_t ret; + int err; + + assert( sbiod != NULL ); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + + p = (struct tls_data *)sbiod->sbiod_pvt; + + ret = PR_Send( p->session, (char *)buf, len, 0, PR_INTERVAL_NO_TIMEOUT ); + if ( ret < 0 ) { + err = PR_GetError(); + if ( err == PR_PENDING_INTERRUPT_ERROR || err == PR_WOULD_BLOCK_ERROR ) { + sbiod->sbiod_sb->sb_trans_needs_write = 1; + sock_errset(EWOULDBLOCK); + ret = 0; + } + } else { + sbiod->sbiod_sb->sb_trans_needs_write = 0; + } + return ret; +} + +static Sockbuf_IO tlsm_sbio = +{ + tlsm_sb_setup, /* sbi_setup */ + tlsm_sb_remove, /* sbi_remove */ + tlsm_sb_ctrl, /* sbi_ctrl */ + tlsm_sb_read, /* sbi_read */ + tlsm_sb_write, /* sbi_write */ + tlsm_sb_close /* sbi_close */ +}; + +tls_impl ldap_int_tls_impl = { + "MozNSS", + + tlsm_init, + tlsm_destroy, + + tlsm_ctx_new, + tlsm_ctx_ref, + tlsm_ctx_free, + tlsm_ctx_init, + + tlsm_session_new, + tlsm_session_connect, + tlsm_session_accept, + tlsm_session_upflags, + tlsm_session_errmsg, + tlsm_session_my_dn, + tlsm_session_peer_dn, + tlsm_session_chkhost, + tlsm_session_strength, + + &tlsm_sbio, + +#ifdef LDAP_R_COMPILE + tlsm_thr_init, +#else + NULL, +#endif + + 0 +}; + +#endif /* HAVE_MOZNSS */ +/* + emacs settings + Local Variables: + indent-tabs-mode: t + tab-width: 4 + End: +*/ diff --git a/libraries/libldap/tls_o.c b/libraries/libldap/tls_o.c new file mode 100644 index 0000000..a13f11f --- /dev/null +++ b/libraries/libldap/tls_o.c @@ -0,0 +1,1378 @@ +/* tls_o.c - Handle tls/ssl using OpenSSL */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2008-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: Rewritten by Howard Chu + */ + +#include "portable.h" + +#ifdef HAVE_OPENSSL + +#include "ldap_config.h" + +#include <stdio.h> + +#include <ac/stdlib.h> +#include <ac/errno.h> +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/ctype.h> +#include <ac/time.h> +#include <ac/unistd.h> +#include <ac/param.h> +#include <ac/dirent.h> + +#include "ldap-int.h" +#include "ldap-tls.h" + +#ifdef HAVE_OPENSSL_SSL_H +#include <openssl/ssl.h> +#include <openssl/x509v3.h> +#include <openssl/err.h> +#include <openssl/rand.h> +#include <openssl/safestack.h> +#include <openssl/bn.h> +#include <openssl/rsa.h> +#include <openssl/dh.h> +#elif defined( HAVE_SSL_H ) +#include <ssl.h> +#endif + +#if OPENSSL_VERSION_NUMBER >= 0x10100000 +#define ASN1_STRING_data(x) ASN1_STRING_get0_data(x) +#endif + +typedef SSL_CTX tlso_ctx; +typedef SSL tlso_session; + +static BIO_METHOD * tlso_bio_method = NULL; +static BIO_METHOD * tlso_bio_setup( void ); + +static int tlso_opt_trace = 1; + +static void tlso_report_error( void ); + +static void tlso_info_cb( const SSL *ssl, int where, int ret ); +static int tlso_verify_cb( int ok, X509_STORE_CTX *ctx ); +static int tlso_verify_ok( int ok, X509_STORE_CTX *ctx ); +static int tlso_seed_PRNG( const char *randfile ); +#if OPENSSL_VERSION_NUMBER < 0x10100000 +/* + * OpenSSL 1.1 API and later has new locking code +*/ +static RSA * tlso_tmp_rsa_cb( SSL *ssl, int is_export, int key_length ); + +#ifdef LDAP_R_COMPILE +/* + * provide mutexes for the OpenSSL library. + */ +static ldap_pvt_thread_mutex_t tlso_mutexes[CRYPTO_NUM_LOCKS]; + +static void tlso_locking_cb( int mode, int type, const char *file, int line ) +{ + if ( mode & CRYPTO_LOCK ) { + ldap_pvt_thread_mutex_lock( &tlso_mutexes[type] ); + } else { + ldap_pvt_thread_mutex_unlock( &tlso_mutexes[type] ); + } +} + +#if OPENSSL_VERSION_NUMBER >= 0x0909000 +static void tlso_thread_self( CRYPTO_THREADID *id ) +{ + CRYPTO_THREADID_set_pointer( id, (void *)ldap_pvt_thread_self() ); +} +#define CRYPTO_set_id_callback(foo) CRYPTO_THREADID_set_callback(foo) +#else +static unsigned long tlso_thread_self( void ) +{ + /* FIXME: CRYPTO_set_id_callback only works when ldap_pvt_thread_t + * is an integral type that fits in an unsigned long + */ + + /* force an error if the ldap_pvt_thread_t type is too large */ + enum { ok = sizeof( ldap_pvt_thread_t ) <= sizeof( unsigned long ) }; + typedef struct { int dummy: ok ? 1 : -1; } Check[ok ? 1 : -1]; + + return (unsigned long) ldap_pvt_thread_self(); +} +#endif + +static void tlso_thr_init( void ) +{ + int i; + + for( i=0; i< CRYPTO_NUM_LOCKS ; i++ ) { + ldap_pvt_thread_mutex_init( &tlso_mutexes[i] ); + } + CRYPTO_set_locking_callback( tlso_locking_cb ); + CRYPTO_set_id_callback( tlso_thread_self ); +} +#endif /* LDAP_R_COMPILE */ +#else +#ifdef LDAP_R_COMPILE +static void tlso_thr_init( void ) {} +#endif +#endif /* OpenSSL 1.1 */ + +#if OPENSSL_VERSION_NUMBER < 0x10100000 +/* + * OpenSSL 1.1 API and later makes the BIO method concrete types internal. + */ + +static BIO_METHOD * +BIO_meth_new( int type, const char *name ) +{ + BIO_METHOD *method = LDAP_MALLOC( sizeof(BIO_METHOD) ); + memset( method, 0, sizeof(BIO_METHOD) ); + + method->type = type; + method->name = name; + + return method; +} + +static void +BIO_meth_free( BIO_METHOD *meth ) +{ + if ( meth == NULL ) { + return; + } + + LDAP_FREE( meth ); +} + +#define BIO_meth_set_write(m, f) (m)->bwrite = (f) +#define BIO_meth_set_read(m, f) (m)->bread = (f) +#define BIO_meth_set_puts(m, f) (m)->bputs = (f) +#define BIO_meth_set_gets(m, f) (m)->bgets = (f) +#define BIO_meth_set_ctrl(m, f) (m)->ctrl = (f) +#define BIO_meth_set_create(m, f) (m)->create = (f) +#define BIO_meth_set_destroy(m, f) (m)->destroy = (f) + +#endif /* OpenSSL 1.1 */ + +static STACK_OF(X509_NAME) * +tlso_ca_list( char * bundle, char * dir ) +{ + STACK_OF(X509_NAME) *ca_list = NULL; + + if ( bundle ) { + ca_list = SSL_load_client_CA_file( bundle ); + } +#if defined(HAVE_DIRENT_H) || defined(dirent) + if ( dir ) { + int freeit = 0; + + if ( !ca_list ) { + ca_list = sk_X509_NAME_new_null(); + freeit = 1; + } + if ( !SSL_add_dir_cert_subjects_to_stack( ca_list, dir ) && + freeit ) { + sk_X509_NAME_free( ca_list ); + ca_list = NULL; + } + } +#endif + return ca_list; +} + +/* + * Initialize TLS subsystem. Should be called only once. + */ +static int +tlso_init( void ) +{ + struct ldapoptions *lo = LDAP_INT_GLOBAL_OPT(); +#ifdef HAVE_EBCDIC + { + char *file = LDAP_STRDUP( lo->ldo_tls_randfile ); + if ( file ) __atoe( file ); + (void) tlso_seed_PRNG( file ); + LDAP_FREE( file ); + } +#else + (void) tlso_seed_PRNG( lo->ldo_tls_randfile ); +#endif + +#if OPENSSL_VERSION_NUMBER < 0x10100000 + SSL_load_error_strings(); + SSL_library_init(); + OpenSSL_add_all_digests(); +#else + OPENSSL_init_ssl(0, NULL); +#endif + + /* FIXME: mod_ssl does this */ + X509V3_add_standard_extensions(); + + tlso_bio_method = tlso_bio_setup(); + + return 0; +} + +/* + * Tear down the TLS subsystem. Should only be called once. + */ +static void +tlso_destroy( void ) +{ + struct ldapoptions *lo = LDAP_INT_GLOBAL_OPT(); + + BIO_meth_free( tlso_bio_method ); + +#if OPENSSL_VERSION_NUMBER < 0x10100000 + EVP_cleanup(); +#if OPENSSL_VERSION_NUMBER < 0x10000000 + ERR_remove_state(0); +#else + ERR_remove_thread_state(NULL); +#endif + ERR_free_strings(); +#endif + + if ( lo->ldo_tls_randfile ) { + LDAP_FREE( lo->ldo_tls_randfile ); + lo->ldo_tls_randfile = NULL; + } +} + +static tls_ctx * +tlso_ctx_new( struct ldapoptions *lo ) +{ + return (tls_ctx *) SSL_CTX_new( SSLv23_method() ); +} + +static void +tlso_ctx_ref( tls_ctx *ctx ) +{ + tlso_ctx *c = (tlso_ctx *)ctx; +#if OPENSSL_VERSION_NUMBER < 0x10100000 +#define SSL_CTX_up_ref(ctx) CRYPTO_add( &(ctx->references), 1, CRYPTO_LOCK_SSL_CTX ) +#endif + SSL_CTX_up_ref( c ); +} + +static void +tlso_ctx_free ( tls_ctx *ctx ) +{ + tlso_ctx *c = (tlso_ctx *)ctx; + SSL_CTX_free( c ); +} + +/* + * initialize a new TLS context + */ +static int +tlso_ctx_init( struct ldapoptions *lo, struct ldaptls *lt, int is_server ) +{ + tlso_ctx *ctx = (tlso_ctx *)lo->ldo_tls_ctx; + int i; + + if ( is_server ) { + SSL_CTX_set_session_id_context( ctx, + (const unsigned char *) "OpenLDAP", sizeof("OpenLDAP")-1 ); + } + +#ifdef SSL_OP_NO_TLSv1 +#ifdef SSL_OP_NO_TLSv1_1 +#ifdef SSL_OP_NO_TLSv1_2 + if ( lo->ldo_tls_protocol_min > LDAP_OPT_X_TLS_PROTOCOL_TLS1_2) + SSL_CTX_set_options( ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | + SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | + SSL_OP_NO_TLSv1_2 ); + else +#endif + if ( lo->ldo_tls_protocol_min > LDAP_OPT_X_TLS_PROTOCOL_TLS1_1) + SSL_CTX_set_options( ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | + SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 ); + else +#endif + if ( lo->ldo_tls_protocol_min > LDAP_OPT_X_TLS_PROTOCOL_TLS1_0) + SSL_CTX_set_options( ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | + SSL_OP_NO_TLSv1); + else +#endif + if ( lo->ldo_tls_protocol_min > LDAP_OPT_X_TLS_PROTOCOL_SSL3 ) + SSL_CTX_set_options( ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 ); + else if ( lo->ldo_tls_protocol_min > LDAP_OPT_X_TLS_PROTOCOL_SSL2 ) + SSL_CTX_set_options( ctx, SSL_OP_NO_SSLv2 ); + + if ( lo->ldo_tls_ciphersuite && + !SSL_CTX_set_cipher_list( ctx, lt->lt_ciphersuite ) ) + { + Debug( LDAP_DEBUG_ANY, + "TLS: could not set cipher list %s.\n", + lo->ldo_tls_ciphersuite, 0, 0 ); + tlso_report_error(); + return -1; + } + + if ( lo->ldo_tls_cacertfile == NULL && lo->ldo_tls_cacertdir == NULL ) { + if ( !SSL_CTX_set_default_verify_paths( ctx ) ) { + Debug( LDAP_DEBUG_ANY, "TLS: " + "could not use default certificate paths", 0, 0, 0 ); + tlso_report_error(); + return -1; + } + } else { + if ( !SSL_CTX_load_verify_locations( ctx, + lt->lt_cacertfile, lt->lt_cacertdir ) ) + { + Debug( LDAP_DEBUG_ANY, "TLS: " + "could not load verify locations (file:`%s',dir:`%s').\n", + lo->ldo_tls_cacertfile ? lo->ldo_tls_cacertfile : "", + lo->ldo_tls_cacertdir ? lo->ldo_tls_cacertdir : "", + 0 ); + tlso_report_error(); + return -1; + } + + if ( is_server ) { + STACK_OF(X509_NAME) *calist; + /* List of CA names to send to a client */ + calist = tlso_ca_list( lt->lt_cacertfile, lt->lt_cacertdir ); + if ( !calist ) { + Debug( LDAP_DEBUG_ANY, "TLS: " + "could not load client CA list (file:`%s',dir:`%s').\n", + lo->ldo_tls_cacertfile ? lo->ldo_tls_cacertfile : "", + lo->ldo_tls_cacertdir ? lo->ldo_tls_cacertdir : "", + 0 ); + tlso_report_error(); + return -1; + } + + SSL_CTX_set_client_CA_list( ctx, calist ); + } + } + + if ( lo->ldo_tls_certfile && + !SSL_CTX_use_certificate_file( ctx, + lt->lt_certfile, SSL_FILETYPE_PEM ) ) + { + Debug( LDAP_DEBUG_ANY, + "TLS: could not use certificate `%s'.\n", + lo->ldo_tls_certfile,0,0); + tlso_report_error(); + return -1; + } + + /* Key validity is checked automatically if cert has already been set */ + if ( lo->ldo_tls_keyfile && + !SSL_CTX_use_PrivateKey_file( ctx, + lt->lt_keyfile, SSL_FILETYPE_PEM ) ) + { + Debug( LDAP_DEBUG_ANY, + "TLS: could not use key file `%s'.\n", + lo->ldo_tls_keyfile,0,0); + tlso_report_error(); + return -1; + } + + if ( is_server && lo->ldo_tls_dhfile ) { + DH *dh; + BIO *bio; + + if (( bio=BIO_new_file( lt->lt_dhfile,"r" )) == NULL ) { + Debug( LDAP_DEBUG_ANY, + "TLS: could not use DH parameters file `%s'.\n", + lo->ldo_tls_dhfile,0,0); + tlso_report_error(); + return -1; + } + if (!( dh=PEM_read_bio_DHparams( bio, NULL, NULL, NULL ))) { + Debug( LDAP_DEBUG_ANY, + "TLS: could not read DH parameters file `%s'.\n", + lo->ldo_tls_dhfile,0,0); + tlso_report_error(); + BIO_free( bio ); + return -1; + } + BIO_free( bio ); + SSL_CTX_set_tmp_dh( ctx, dh ); + SSL_CTX_set_options( ctx, SSL_OP_SINGLE_DH_USE ); + DH_free( dh ); + } + + if ( lo->ldo_tls_ecname ) { +#ifdef OPENSSL_NO_EC + Debug( LDAP_DEBUG_ANY, + "TLS: Elliptic Curves not supported.\n", 0,0,0 ); + return -1; +#else + if ( !SSL_CTX_set1_curves_list( ctx, lt->lt_ecname )) { + Debug( LDAP_DEBUG_ANY, + "TLS: could not set EC name `%s'.\n", + lo->ldo_tls_ecname,0,0); + tlso_report_error(); + return -1; + } + /* + * This is a NOP in OpenSSL 1.1.0 and later, where curves are always + * auto-negotiated. + */ +#if OPENSSL_VERSION_NUMBER < 0x10100000UL + if ( SSL_CTX_set_ecdh_auto( ctx, 1 ) <= 0 ) { + Debug( LDAP_DEBUG_ANY, + "TLS: could not enable automatic EC negotiation.\n", 0, 0, 0 ); + } +#endif +#endif /* OPENSSL_NO_EC */ + } + + if ( tlso_opt_trace ) { + SSL_CTX_set_info_callback( ctx, tlso_info_cb ); + } + + i = SSL_VERIFY_NONE; + if ( lo->ldo_tls_require_cert ) { + i = SSL_VERIFY_PEER; + if ( lo->ldo_tls_require_cert == LDAP_OPT_X_TLS_DEMAND || + lo->ldo_tls_require_cert == LDAP_OPT_X_TLS_HARD ) { + i |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; + } + } + + SSL_CTX_set_verify( ctx, i, + lo->ldo_tls_require_cert == LDAP_OPT_X_TLS_ALLOW ? + tlso_verify_ok : tlso_verify_cb ); +#if OPENSSL_VERSION_NUMBER < 0x10100000 + SSL_CTX_set_tmp_rsa_callback( ctx, tlso_tmp_rsa_cb ); +#endif +#ifdef HAVE_OPENSSL_CRL + if ( lo->ldo_tls_crlcheck ) { + X509_STORE *x509_s = SSL_CTX_get_cert_store( ctx ); + if ( lo->ldo_tls_crlcheck == LDAP_OPT_X_TLS_CRL_PEER ) { + X509_STORE_set_flags( x509_s, X509_V_FLAG_CRL_CHECK ); + } else if ( lo->ldo_tls_crlcheck == LDAP_OPT_X_TLS_CRL_ALL ) { + X509_STORE_set_flags( x509_s, + X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL ); + } + } +#endif + return 0; +} + +static tls_session * +tlso_session_new( tls_ctx *ctx, int is_server ) +{ + tlso_ctx *c = (tlso_ctx *)ctx; + return (tls_session *)SSL_new( c ); +} + +static int +tlso_session_connect( LDAP *ld, tls_session *sess ) +{ + tlso_session *s = (tlso_session *)sess; + + /* Caller expects 0 = success, OpenSSL returns 1 = success */ + int rc = SSL_connect( s ) - 1; +#ifdef LDAP_USE_NON_BLOCKING_TLS + if ( rc < 0 ) { + int sockerr = sock_errno(); + int sslerr = SSL_get_error( s, rc+1 ); + if ( sslerr == SSL_ERROR_WANT_READ || sslerr == SSL_ERROR_WANT_WRITE ) { + rc = 0; + } else if ( sslerr == SSL_ERROR_SYSCALL && + ( sockerr == EAGAIN || sockerr == ENOTCONN )) { + rc = 0; + } + } +#endif /* LDAP_USE_NON_BLOCKING_TLS */ + return rc; +} + +static int +tlso_session_accept( tls_session *sess ) +{ + tlso_session *s = (tlso_session *)sess; + + /* Caller expects 0 = success, OpenSSL returns 1 = success */ + return SSL_accept( s ) - 1; +} + +static int +tlso_session_upflags( Sockbuf *sb, tls_session *sess, int rc ) +{ + tlso_session *s = (tlso_session *)sess; + + /* 1 was subtracted above, offset it back now */ + rc = SSL_get_error(s, rc+1); + if (rc == SSL_ERROR_WANT_READ) { + sb->sb_trans_needs_read = 1; + return 1; + + } else if (rc == SSL_ERROR_WANT_WRITE) { + sb->sb_trans_needs_write = 1; + return 1; + + } else if (rc == SSL_ERROR_WANT_CONNECT) { + return 1; + } + return 0; +} + +static char * +tlso_session_errmsg( tls_session *sess, int rc, char *buf, size_t len ) +{ + char err[256] = ""; + const char *certerr=NULL; + tlso_session *s = (tlso_session *)sess; + + rc = ERR_peek_error(); + if ( rc ) { + ERR_error_string_n( rc, err, sizeof(err) ); + if ( ( ERR_GET_LIB(rc) == ERR_LIB_SSL ) && + ( ERR_GET_REASON(rc) == SSL_R_CERTIFICATE_VERIFY_FAILED ) ) { + int certrc = SSL_get_verify_result(s); + certerr = (char *)X509_verify_cert_error_string(certrc); + } + snprintf(buf, len, "%s%s%s%s", err, certerr ? " (" :"", + certerr ? certerr : "", certerr ? ")" : "" ); + return buf; + } + return NULL; +} + +static int +tlso_session_my_dn( tls_session *sess, struct berval *der_dn ) +{ + tlso_session *s = (tlso_session *)sess; + X509 *x; + X509_NAME *xn; + + x = SSL_get_certificate( s ); + + if (!x) return LDAP_INVALID_CREDENTIALS; + + xn = X509_get_subject_name(x); +#if OPENSSL_VERSION_NUMBER < 0x10100000 + der_dn->bv_len = i2d_X509_NAME( xn, NULL ); + der_dn->bv_val = xn->bytes->data; +#else + { + size_t len = 0; + der_dn->bv_val = NULL; + X509_NAME_get0_der( xn, (const unsigned char **)&der_dn->bv_val, &len ); + der_dn->bv_len = len; + } +#endif + /* Don't X509_free, the session is still using it */ + return 0; +} + +static X509 * +tlso_get_cert( SSL *s ) +{ + /* If peer cert was bad, treat as if no cert was given */ + if (SSL_get_verify_result(s)) { + return NULL; + } + return SSL_get_peer_certificate(s); +} + +static int +tlso_session_peer_dn( tls_session *sess, struct berval *der_dn ) +{ + tlso_session *s = (tlso_session *)sess; + X509 *x = tlso_get_cert( s ); + X509_NAME *xn; + + if ( !x ) + return LDAP_INVALID_CREDENTIALS; + + xn = X509_get_subject_name(x); +#if OPENSSL_VERSION_NUMBER < 0x10100000 + der_dn->bv_len = i2d_X509_NAME( xn, NULL ); + der_dn->bv_val = xn->bytes->data; +#else + { + size_t len = 0; + der_dn->bv_val = NULL; + X509_NAME_get0_der( xn, (const unsigned char **)&der_dn->bv_val, &len ); + der_dn->bv_len = len; + } +#endif + X509_free(x); + return 0; +} + +/* what kind of hostname were we given? */ +#define IS_DNS 0 +#define IS_IP4 1 +#define IS_IP6 2 + +static int +tlso_session_chkhost( LDAP *ld, tls_session *sess, const char *name_in ) +{ + tlso_session *s = (tlso_session *)sess; + int i, ret = LDAP_LOCAL_ERROR; + int chkSAN = ld->ld_options.ldo_tls_require_san, gotSAN = 0; + X509 *x; + const char *name; + char *ptr; + int ntype = IS_DNS, nlen; +#ifdef LDAP_PF_INET6 + struct in6_addr addr; +#else + struct in_addr addr; +#endif + + if( ldap_int_hostname && + ( !name_in || !strcasecmp( name_in, "localhost" ) ) ) + { + name = ldap_int_hostname; + } else { + name = name_in; + } + nlen = strlen(name); + + x = tlso_get_cert(s); + if (!x) { + Debug( LDAP_DEBUG_ANY, + "TLS: unable to get peer certificate.\n", + 0, 0, 0 ); + /* If this was a fatal condition, things would have + * aborted long before now. + */ + return LDAP_SUCCESS; + } + +#ifdef LDAP_PF_INET6 + if (inet_pton(AF_INET6, name, &addr)) { + ntype = IS_IP6; + } else +#endif + if ((ptr = strrchr(name, '.')) && isdigit((unsigned char)ptr[1])) { + if (inet_aton(name, (struct in_addr *)&addr)) ntype = IS_IP4; + } + + if (chkSAN) { + i = X509_get_ext_by_NID(x, NID_subject_alt_name, -1); + if (i >= 0) { + X509_EXTENSION *ex; + STACK_OF(GENERAL_NAME) *alt; + + ex = X509_get_ext(x, i); + alt = X509V3_EXT_d2i(ex); + if (alt) { + int n, len2 = 0; + char *domain = NULL; + GENERAL_NAME *gn; + + gotSAN = 1; + if (ntype == IS_DNS) { + domain = strchr(name, '.'); + if (domain) { + len2 = nlen - (domain-name); + } + } + n = sk_GENERAL_NAME_num(alt); + for (i=0; i<n; i++) { + char *sn; + int sl; + gn = sk_GENERAL_NAME_value(alt, i); + if (gn->type == GEN_DNS) { + if (ntype != IS_DNS) continue; + + sn = (char *) ASN1_STRING_data(gn->d.ia5); + sl = ASN1_STRING_length(gn->d.ia5); + + /* ignore empty */ + if (sl == 0) continue; + + /* Is this an exact match? */ + if ((nlen == sl) && !strncasecmp(name, sn, nlen)) { + break; + } + + /* Is this a wildcard match? */ + if (domain && (sn[0] == '*') && (sn[1] == '.') && + (len2 == sl-1) && !strncasecmp(domain, &sn[1], len2)) + { + break; + } + + } else if (gn->type == GEN_IPADD) { + if (ntype == IS_DNS) continue; + + sn = (char *) ASN1_STRING_data(gn->d.ia5); + sl = ASN1_STRING_length(gn->d.ia5); + +#ifdef LDAP_PF_INET6 + if (ntype == IS_IP6 && sl != sizeof(struct in6_addr)) { + continue; + } else +#endif + if (ntype == IS_IP4 && sl != sizeof(struct in_addr)) { + continue; + } + if (!memcmp(sn, &addr, sl)) { + break; + } + } + } + + GENERAL_NAMES_free(alt); + if (i < n) { /* Found a match */ + ret = LDAP_SUCCESS; + } + } + } + } + if (ret != LDAP_SUCCESS && chkSAN) { + switch(chkSAN) { + case LDAP_OPT_X_TLS_DEMAND: + case LDAP_OPT_X_TLS_HARD: + if (!gotSAN) { + Debug( LDAP_DEBUG_ANY, + "TLS: unable to get subjectAltName from peer certificate.\n", 0, 0, 0 ); + ret = LDAP_CONNECT_ERROR; + if ( ld->ld_error ) { + LDAP_FREE( ld->ld_error ); + } + ld->ld_error = LDAP_STRDUP( + _("TLS: unable to get subjectAltName from peer certificate")); + goto done; + } + /* FALLTHRU */ + case LDAP_OPT_X_TLS_TRY: + if (gotSAN) { + Debug( LDAP_DEBUG_ANY, "TLS: hostname (%s) does not match " + "subjectAltName in certificate.\n", + name, 0, 0 ); + ret = LDAP_CONNECT_ERROR; + if ( ld->ld_error ) { + LDAP_FREE( ld->ld_error ); + } + ld->ld_error = LDAP_STRDUP( + _("TLS: hostname does not match subjectAltName in peer certificate")); + goto done; + } + break; + case LDAP_OPT_X_TLS_ALLOW: + break; + } + } + + if (ret != LDAP_SUCCESS) { + X509_NAME *xn; + X509_NAME_ENTRY *ne; + ASN1_OBJECT *obj; + ASN1_STRING *cn = NULL; + int navas; + + /* find the last CN */ + obj = OBJ_nid2obj( NID_commonName ); + if ( !obj ) goto no_cn; /* should never happen */ + + xn = X509_get_subject_name(x); + navas = X509_NAME_entry_count( xn ); + for ( i=navas-1; i>=0; i-- ) { + ne = X509_NAME_get_entry( xn, i ); + if ( !OBJ_cmp( X509_NAME_ENTRY_get_object(ne), obj )) { + cn = X509_NAME_ENTRY_get_data( ne ); + break; + } + } + + if( !cn ) + { +no_cn: + Debug( LDAP_DEBUG_ANY, + "TLS: unable to get common name from peer certificate.\n", + 0, 0, 0 ); + ret = LDAP_CONNECT_ERROR; + if ( ld->ld_error ) { + LDAP_FREE( ld->ld_error ); + } + ld->ld_error = LDAP_STRDUP( + _("TLS: unable to get CN from peer certificate")); + + } else if ( cn->length == nlen && + strncasecmp( name, (char *) cn->data, nlen ) == 0 ) { + ret = LDAP_SUCCESS; + + } else if (( cn->data[0] == '*' ) && ( cn->data[1] == '.' )) { + char *domain = strchr(name, '.'); + if( domain ) { + int dlen; + + dlen = nlen - (domain-name); + + /* Is this a wildcard match? */ + if ((dlen == cn->length-1) && + !strncasecmp(domain, (char *) &cn->data[1], dlen)) { + ret = LDAP_SUCCESS; + } + } + } + + if( ret == LDAP_LOCAL_ERROR ) { + Debug( LDAP_DEBUG_ANY, "TLS: hostname (%s) does not match " + "common name in certificate (%.*s).\n", + name, cn->length, cn->data ); + ret = LDAP_CONNECT_ERROR; + if ( ld->ld_error ) { + LDAP_FREE( ld->ld_error ); + } + ld->ld_error = LDAP_STRDUP( + _("TLS: hostname does not match name in peer certificate")); + } + } +done: + X509_free(x); + return ret; +} + +static int +tlso_session_strength( tls_session *sess ) +{ + tlso_session *s = (tlso_session *)sess; + + return SSL_CIPHER_get_bits(SSL_get_current_cipher(s), NULL); +} + +/* + * TLS support for LBER Sockbufs + */ + +struct tls_data { + tlso_session *session; + Sockbuf_IO_Desc *sbiod; +}; + +#if OPENSSL_VERSION_NUMBER < 0x10100000 +#define BIO_set_init(b, x) b->init = x +#define BIO_set_data(b, x) b->ptr = x +#define BIO_clear_flags(b, x) b->flags &= ~(x) +#define BIO_get_data(b) b->ptr +#endif +static int +tlso_bio_create( BIO *b ) { + BIO_set_init( b, 1 ); + BIO_set_data( b, NULL ); + BIO_clear_flags( b, ~0 ); + return 1; +} + +static int +tlso_bio_destroy( BIO *b ) +{ + if ( b == NULL ) return 0; + + BIO_set_data( b, NULL ); /* sb_tls_remove() will free it */ + BIO_set_init( b, 0 ); + BIO_clear_flags( b, ~0 ); + return 1; +} + +static int +tlso_bio_read( BIO *b, char *buf, int len ) +{ + struct tls_data *p; + int ret; + + if ( buf == NULL || len <= 0 ) return 0; + + p = (struct tls_data *)BIO_get_data(b); + + if ( p == NULL || p->sbiod == NULL ) { + return 0; + } + + ret = LBER_SBIOD_READ_NEXT( p->sbiod, buf, len ); + + BIO_clear_retry_flags( b ); + if ( ret < 0 ) { + int err = sock_errno(); + if ( err == EAGAIN || err == EWOULDBLOCK ) { + BIO_set_retry_read( b ); + } + } + + return ret; +} + +static int +tlso_bio_write( BIO *b, const char *buf, int len ) +{ + struct tls_data *p; + int ret; + + if ( buf == NULL || len <= 0 ) return 0; + + p = (struct tls_data *)BIO_get_data(b); + + if ( p == NULL || p->sbiod == NULL ) { + return 0; + } + + ret = LBER_SBIOD_WRITE_NEXT( p->sbiod, (char *)buf, len ); + + BIO_clear_retry_flags( b ); + if ( ret < 0 ) { + int err = sock_errno(); + if ( err == EAGAIN || err == EWOULDBLOCK ) { + BIO_set_retry_write( b ); + } + } + + return ret; +} + +static long +tlso_bio_ctrl( BIO *b, int cmd, long num, void *ptr ) +{ + if ( cmd == BIO_CTRL_FLUSH ) { + /* The OpenSSL library needs this */ + return 1; + } + return 0; +} + +static int +tlso_bio_gets( BIO *b, char *buf, int len ) +{ + return -1; +} + +static int +tlso_bio_puts( BIO *b, const char *str ) +{ + return tlso_bio_write( b, str, strlen( str ) ); +} + +static BIO_METHOD * +tlso_bio_setup( void ) +{ + /* it's a source/sink BIO */ + BIO_METHOD * method = BIO_meth_new( 100 | 0x400, "sockbuf glue" ); + BIO_meth_set_write( method, tlso_bio_write ); + BIO_meth_set_read( method, tlso_bio_read ); + BIO_meth_set_puts( method, tlso_bio_puts ); + BIO_meth_set_gets( method, tlso_bio_gets ); + BIO_meth_set_ctrl( method, tlso_bio_ctrl ); + BIO_meth_set_create( method, tlso_bio_create ); + BIO_meth_set_destroy( method, tlso_bio_destroy ); + + return method; +} + +static int +tlso_sb_setup( Sockbuf_IO_Desc *sbiod, void *arg ) +{ + struct tls_data *p; + BIO *bio; + + assert( sbiod != NULL ); + + p = LBER_MALLOC( sizeof( *p ) ); + if ( p == NULL ) { + return -1; + } + + p->session = arg; + p->sbiod = sbiod; + bio = BIO_new( tlso_bio_method ); + BIO_set_data( bio, p ); + SSL_set_bio( p->session, bio, bio ); + sbiod->sbiod_pvt = p; + return 0; +} + +static int +tlso_sb_remove( Sockbuf_IO_Desc *sbiod ) +{ + struct tls_data *p; + + assert( sbiod != NULL ); + assert( sbiod->sbiod_pvt != NULL ); + + p = (struct tls_data *)sbiod->sbiod_pvt; + SSL_free( p->session ); + LBER_FREE( sbiod->sbiod_pvt ); + sbiod->sbiod_pvt = NULL; + return 0; +} + +static int +tlso_sb_close( Sockbuf_IO_Desc *sbiod ) +{ + struct tls_data *p; + + assert( sbiod != NULL ); + assert( sbiod->sbiod_pvt != NULL ); + + p = (struct tls_data *)sbiod->sbiod_pvt; + SSL_shutdown( p->session ); + return 0; +} + +static int +tlso_sb_ctrl( Sockbuf_IO_Desc *sbiod, int opt, void *arg ) +{ + struct tls_data *p; + + assert( sbiod != NULL ); + assert( sbiod->sbiod_pvt != NULL ); + + p = (struct tls_data *)sbiod->sbiod_pvt; + + if ( opt == LBER_SB_OPT_GET_SSL ) { + *((tlso_session **)arg) = p->session; + return 1; + + } else if ( opt == LBER_SB_OPT_DATA_READY ) { + if( SSL_pending( p->session ) > 0 ) { + return 1; + } + } + + return LBER_SBIOD_CTRL_NEXT( sbiod, opt, arg ); +} + +static ber_slen_t +tlso_sb_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len) +{ + struct tls_data *p; + ber_slen_t ret; + int err; + + assert( sbiod != NULL ); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + + p = (struct tls_data *)sbiod->sbiod_pvt; + + ret = SSL_read( p->session, (char *)buf, len ); +#ifdef HAVE_WINSOCK + errno = WSAGetLastError(); +#endif + err = SSL_get_error( p->session, ret ); + if (err == SSL_ERROR_WANT_READ ) { + sbiod->sbiod_sb->sb_trans_needs_read = 1; + sock_errset(EWOULDBLOCK); + } + else + sbiod->sbiod_sb->sb_trans_needs_read = 0; + return ret; +} + +static ber_slen_t +tlso_sb_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len) +{ + struct tls_data *p; + ber_slen_t ret; + int err; + + assert( sbiod != NULL ); + assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); + + p = (struct tls_data *)sbiod->sbiod_pvt; + + ret = SSL_write( p->session, (char *)buf, len ); +#ifdef HAVE_WINSOCK + errno = WSAGetLastError(); +#endif + err = SSL_get_error( p->session, ret ); + if (err == SSL_ERROR_WANT_WRITE ) { + sbiod->sbiod_sb->sb_trans_needs_write = 1; + sock_errset(EWOULDBLOCK); + + } else { + sbiod->sbiod_sb->sb_trans_needs_write = 0; + } + return ret; +} + +static Sockbuf_IO tlso_sbio = +{ + tlso_sb_setup, /* sbi_setup */ + tlso_sb_remove, /* sbi_remove */ + tlso_sb_ctrl, /* sbi_ctrl */ + tlso_sb_read, /* sbi_read */ + tlso_sb_write, /* sbi_write */ + tlso_sb_close /* sbi_close */ +}; + +/* Derived from openssl/apps/s_cb.c */ +static void +tlso_info_cb( const SSL *ssl, int where, int ret ) +{ + int w; + char *op; + char *state = (char *) SSL_state_string_long( (SSL *)ssl ); + + w = where & ~SSL_ST_MASK; + if ( w & SSL_ST_CONNECT ) { + op = "SSL_connect"; + } else if ( w & SSL_ST_ACCEPT ) { + op = "SSL_accept"; + } else { + op = "undefined"; + } + +#ifdef HAVE_EBCDIC + if ( state ) { + state = LDAP_STRDUP( state ); + __etoa( state ); + } +#endif + if ( where & SSL_CB_LOOP ) { + Debug( LDAP_DEBUG_TRACE, + "TLS trace: %s:%s\n", + op, state, 0 ); + + } else if ( where & SSL_CB_ALERT ) { + char *atype = (char *) SSL_alert_type_string_long( ret ); + char *adesc = (char *) SSL_alert_desc_string_long( ret ); + op = ( where & SSL_CB_READ ) ? "read" : "write"; +#ifdef HAVE_EBCDIC + if ( atype ) { + atype = LDAP_STRDUP( atype ); + __etoa( atype ); + } + if ( adesc ) { + adesc = LDAP_STRDUP( adesc ); + __etoa( adesc ); + } +#endif + Debug( LDAP_DEBUG_TRACE, + "TLS trace: SSL3 alert %s:%s:%s\n", + op, atype, adesc ); +#ifdef HAVE_EBCDIC + if ( atype ) LDAP_FREE( atype ); + if ( adesc ) LDAP_FREE( adesc ); +#endif + } else if ( where & SSL_CB_EXIT ) { + if ( ret == 0 ) { + Debug( LDAP_DEBUG_TRACE, + "TLS trace: %s:failed in %s\n", + op, state, 0 ); + } else if ( ret < 0 ) { + Debug( LDAP_DEBUG_TRACE, + "TLS trace: %s:error in %s\n", + op, state, 0 ); + } + } +#ifdef HAVE_EBCDIC + if ( state ) LDAP_FREE( state ); +#endif +} + +static int +tlso_verify_cb( int ok, X509_STORE_CTX *ctx ) +{ + X509 *cert; + int errnum; + int errdepth; + X509_NAME *subject; + X509_NAME *issuer; + char *sname; + char *iname; + char *certerr = NULL; + + cert = X509_STORE_CTX_get_current_cert( ctx ); + errnum = X509_STORE_CTX_get_error( ctx ); + errdepth = X509_STORE_CTX_get_error_depth( ctx ); + + /* + * X509_get_*_name return pointers to the internal copies of + * those things requested. So do not free them. + */ + subject = X509_get_subject_name( cert ); + issuer = X509_get_issuer_name( cert ); + /* X509_NAME_oneline, if passed a NULL buf, allocate memomry */ + sname = X509_NAME_oneline( subject, NULL, 0 ); + iname = X509_NAME_oneline( issuer, NULL, 0 ); + if ( !ok ) certerr = (char *)X509_verify_cert_error_string( errnum ); +#ifdef HAVE_EBCDIC + if ( sname ) __etoa( sname ); + if ( iname ) __etoa( iname ); + if ( certerr ) { + certerr = LDAP_STRDUP( certerr ); + __etoa( certerr ); + } +#endif + Debug( LDAP_DEBUG_TRACE, + "TLS certificate verification: depth: %d, err: %d, subject: %s,", + errdepth, errnum, + sname ? sname : "-unknown-" ); + Debug( LDAP_DEBUG_TRACE, " issuer: %s\n", iname ? iname : "-unknown-", 0, 0 ); + if ( !ok ) { + Debug( LDAP_DEBUG_ANY, + "TLS certificate verification: Error, %s\n", + certerr, 0, 0 ); + } + if ( sname ) + OPENSSL_free ( sname ); + if ( iname ) + OPENSSL_free ( iname ); +#ifdef HAVE_EBCDIC + if ( certerr ) LDAP_FREE( certerr ); +#endif + return ok; +} + +static int +tlso_verify_ok( int ok, X509_STORE_CTX *ctx ) +{ + (void) tlso_verify_cb( ok, ctx ); + return 1; +} + +/* Inspired by ERR_print_errors in OpenSSL */ +static void +tlso_report_error( void ) +{ + unsigned long l; + char buf[200]; + const char *file; + int line; + + while ( ( l = ERR_get_error_line( &file, &line ) ) != 0 ) { + ERR_error_string_n( l, buf, sizeof( buf ) ); +#ifdef HAVE_EBCDIC + if ( file ) { + file = LDAP_STRDUP( file ); + __etoa( (char *)file ); + } + __etoa( buf ); +#endif + Debug( LDAP_DEBUG_ANY, "TLS: %s %s:%d\n", + buf, file, line ); +#ifdef HAVE_EBCDIC + if ( file ) LDAP_FREE( (void *)file ); +#endif + } +} + +#if OPENSSL_VERSION_NUMBER < 0x10100000 +static RSA * +tlso_tmp_rsa_cb( SSL *ssl, int is_export, int key_length ) +{ + RSA *tmp_rsa; + /* FIXME: Pregenerate the key on startup */ + /* FIXME: Who frees the key? */ +#if OPENSSL_VERSION_NUMBER >= 0x00908000 + BIGNUM *bn = BN_new(); + tmp_rsa = NULL; + if ( bn ) { + if ( BN_set_word( bn, RSA_F4 )) { + tmp_rsa = RSA_new(); + if ( tmp_rsa && !RSA_generate_key_ex( tmp_rsa, key_length, bn, NULL )) { + RSA_free( tmp_rsa ); + tmp_rsa = NULL; + } + } + BN_free( bn ); + } +#else + tmp_rsa = RSA_generate_key( key_length, RSA_F4, NULL, NULL ); +#endif + + if ( !tmp_rsa ) { + Debug( LDAP_DEBUG_ANY, + "TLS: Failed to generate temporary %d-bit %s RSA key\n", + key_length, is_export ? "export" : "domestic", 0 ); + } + return tmp_rsa; +} +#endif /* OPENSSL_VERSION_NUMBER < 1.1 */ + +static int +tlso_seed_PRNG( const char *randfile ) +{ +#ifndef URANDOM_DEVICE + /* no /dev/urandom (or equiv) */ + long total=0; + char buffer[MAXPATHLEN]; + + if (randfile == NULL) { + /* The seed file is $RANDFILE if defined, otherwise $HOME/.rnd. + * If $HOME is not set or buffer too small to hold the pathname, + * an error occurs. - From RAND_file_name() man page. + * The fact is that when $HOME is NULL, .rnd is used. + */ + randfile = RAND_file_name( buffer, sizeof( buffer ) ); + } +#ifndef OPENSSL_NO_EGD + else if (RAND_egd(randfile) > 0) { + /* EGD socket */ + return 0; + } +#endif + + if (randfile == NULL) { + Debug( LDAP_DEBUG_ANY, + "TLS: Use configuration file or $RANDFILE to define seed PRNG\n", + 0, 0, 0); + return -1; + } + + total = RAND_load_file(randfile, -1); + + if (RAND_status() == 0) { + Debug( LDAP_DEBUG_ANY, + "TLS: PRNG not been seeded with enough data\n", + 0, 0, 0); + return -1; + } + + /* assume if there was enough bits to seed that it's okay + * to write derived bits to the file + */ + RAND_write_file(randfile); + +#endif + + return 0; +} + + +tls_impl ldap_int_tls_impl = { + "OpenSSL", + + tlso_init, + tlso_destroy, + + tlso_ctx_new, + tlso_ctx_ref, + tlso_ctx_free, + tlso_ctx_init, + + tlso_session_new, + tlso_session_connect, + tlso_session_accept, + tlso_session_upflags, + tlso_session_errmsg, + tlso_session_my_dn, + tlso_session_peer_dn, + tlso_session_chkhost, + tlso_session_strength, + + &tlso_sbio, + +#ifdef LDAP_R_COMPILE + tlso_thr_init, +#else + NULL, +#endif + + 0 +}; + +#endif /* HAVE_OPENSSL */ diff --git a/libraries/libldap/turn.c b/libraries/libldap/turn.c new file mode 100644 index 0000000..99c4274 --- /dev/null +++ b/libraries/libldap/turn.c @@ -0,0 +1,96 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2005-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: + * This program was orignally developed by Kurt D. Zeilenga for inclusion in + * OpenLDAP Software. + */ + +/* + * LDAPv3 Turn Operation Request + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/stdlib.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" +#include "ldap_log.h" + +int +ldap_turn( + LDAP *ld, + int mutual, + LDAP_CONST char* identifier, + LDAPControl **sctrls, + LDAPControl **cctrls, + int *msgidp ) +{ +#ifdef LDAP_EXOP_X_TURN + BerElement *turnvalber = NULL; + struct berval *turnvalp = NULL; + int rc; + + turnvalber = ber_alloc_t( LBER_USE_DER ); + if( mutual ) { + ber_printf( turnvalber, "{bs}", mutual, identifier ); + } else { + ber_printf( turnvalber, "{s}", identifier ); + } + ber_flatten( turnvalber, &turnvalp ); + + rc = ldap_extended_operation( ld, LDAP_EXOP_X_TURN, + turnvalp, sctrls, cctrls, msgidp ); + ber_free( turnvalber, 1 ); + return rc; +#else + return LDAP_CONTROL_NOT_FOUND; +#endif +} + +int +ldap_turn_s( + LDAP *ld, + int mutual, + LDAP_CONST char* identifier, + LDAPControl **sctrls, + LDAPControl **cctrls ) +{ +#ifdef LDAP_EXOP_X_TURN + BerElement *turnvalber = NULL; + struct berval *turnvalp = NULL; + int rc; + + turnvalber = ber_alloc_t( LBER_USE_DER ); + if( mutual ) { + ber_printf( turnvalber, "{bs}", 0xFF, identifier ); + } else { + ber_printf( turnvalber, "{s}", identifier ); + } + ber_flatten( turnvalber, &turnvalp ); + + rc = ldap_extended_operation_s( ld, LDAP_EXOP_X_TURN, + turnvalp, sctrls, cctrls, NULL, NULL ); + ber_free( turnvalber, 1 ); + return rc; +#else + return LDAP_CONTROL_NOT_FOUND; +#endif +} + diff --git a/libraries/libldap/txn.c b/libraries/libldap/txn.c new file mode 100644 index 0000000..fe8400b --- /dev/null +++ b/libraries/libldap/txn.c @@ -0,0 +1,155 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2006-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: + * This program was orignally developed by Kurt D. Zeilenga for inclusion + * in OpenLDAP Software. + */ + +/* + * LDAPv3 Transactions (draft-zeilenga-ldap-txn) + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/stdlib.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" +#include "ldap_log.h" + +#ifdef LDAP_X_TXN +int +ldap_txn_start( + LDAP *ld, + LDAPControl **sctrls, + LDAPControl **cctrls, + int *msgidp ) +{ + return ldap_extended_operation( ld, LDAP_EXOP_X_TXN_START, + NULL, sctrls, cctrls, msgidp ); +} + +int +ldap_txn_start_s( + LDAP *ld, + LDAPControl **sctrls, + LDAPControl **cctrls, + struct berval **txnid ) +{ + assert( txnid != NULL ); + + return ldap_extended_operation_s( ld, LDAP_EXOP_X_TXN_START, + NULL, sctrls, cctrls, NULL, txnid ); +} + +int +ldap_txn_end( + LDAP *ld, + int commit, + struct berval *txnid, + LDAPControl **sctrls, + LDAPControl **cctrls, + int *msgidp ) +{ + int rc; + BerElement *txnber = NULL; + struct berval *txnval = NULL; + + assert( txnid != NULL ); + + txnber = ber_alloc_t( LBER_USE_DER ); + + if( commit ) { + ber_printf( txnber, "{ON}", txnid ); + } else { + ber_printf( txnber, "{bON}", commit, txnid ); + } + + ber_flatten( txnber, &txnval ); + + rc = ldap_extended_operation( ld, LDAP_EXOP_X_TXN_END, + txnval, sctrls, cctrls, msgidp ); + + ber_free( txnber, 1 ); + return rc; +} + +int +ldap_txn_end_s( + LDAP *ld, + int commit, + struct berval *txnid, + LDAPControl **sctrls, + LDAPControl **cctrls, + int *retidp ) +{ + int rc; + BerElement *txnber = NULL; + struct berval *txnval = NULL; + struct berval *retdata = NULL; + + if ( retidp != NULL ) *retidp = -1; + + txnber = ber_alloc_t( LBER_USE_DER ); + + if( commit ) { + ber_printf( txnber, "{ON}", txnid ); + } else { + ber_printf( txnber, "{bON}", commit, txnid ); + } + + ber_flatten( txnber, &txnval ); + + rc = ldap_extended_operation_s( ld, LDAP_EXOP_X_TXN_END, + txnval, sctrls, cctrls, NULL, &retdata ); + + ber_free( txnber, 1 ); + + /* parse retdata */ + if( retdata != NULL ) { + BerElement *ber; + ber_tag_t tag; + ber_int_t retid; + + if( retidp == NULL ) goto done; + + ber = ber_init( retdata ); + + if( ber == NULL ) { + rc = ld->ld_errno = LDAP_NO_MEMORY; + goto done; + } + + tag = ber_scanf( ber, "i", &retid ); + ber_free( ber, 1 ); + + if ( tag != LBER_INTEGER ) { + rc = ld->ld_errno = LDAP_DECODING_ERROR; + goto done; + } + + *retidp = (int) retid; + +done: + ber_bvfree( retdata ); + } + + return rc; +} +#endif diff --git a/libraries/libldap/unbind.c b/libraries/libldap/unbind.c new file mode 100644 index 0000000..11797c2 --- /dev/null +++ b/libraries/libldap/unbind.c @@ -0,0 +1,309 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1990 Regents of the University of Michigan. + * All rights reserved. + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/stdlib.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +/* An Unbind Request looks like this: + * + * UnbindRequest ::= [APPLICATION 2] NULL + * + * and has no response. (Source: RFC 4511) + */ + +int +ldap_unbind_ext( + LDAP *ld, + LDAPControl **sctrls, + LDAPControl **cctrls ) +{ + int rc; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + + /* check client controls */ + rc = ldap_int_client_controls( ld, cctrls ); + if( rc != LDAP_SUCCESS ) return rc; + + return ldap_ld_free( ld, 1, sctrls, cctrls ); +} + +int +ldap_unbind_ext_s( + LDAP *ld, + LDAPControl **sctrls, + LDAPControl **cctrls ) +{ + return ldap_unbind_ext( ld, sctrls, cctrls ); +} + +int +ldap_unbind( LDAP *ld ) +{ + Debug( LDAP_DEBUG_TRACE, "ldap_unbind\n", 0, 0, 0 ); + + return( ldap_unbind_ext( ld, NULL, NULL ) ); +} + + +int +ldap_ld_free( + LDAP *ld, + int close, + LDAPControl **sctrls, + LDAPControl **cctrls ) +{ + LDAPMessage *lm, *next; + int err = LDAP_SUCCESS; + + LDAP_MUTEX_LOCK( &ld->ld_ldcmutex ); + /* Someone else is still using this ld. */ + if (ld->ld_ldcrefcnt > 1) { /* but not last thread */ + /* clean up self only */ + ld->ld_ldcrefcnt--; + if ( ld->ld_error != NULL ) { + LDAP_FREE( ld->ld_error ); + ld->ld_error = NULL; + } + + if ( ld->ld_matched != NULL ) { + LDAP_FREE( ld->ld_matched ); + ld->ld_matched = NULL; + } + if ( ld->ld_referrals != NULL) { + LDAP_VFREE(ld->ld_referrals); + ld->ld_referrals = NULL; + } + LDAP_MUTEX_UNLOCK( &ld->ld_ldcmutex ); + LDAP_FREE( (char *) ld ); + return( err ); + } + + /* This ld is the last thread. */ + LDAP_MUTEX_UNLOCK( &ld->ld_ldcmutex ); + + /* free LDAP structure and outstanding requests/responses */ + LDAP_MUTEX_LOCK( &ld->ld_req_mutex ); + while ( ld->ld_requests != NULL ) { + ldap_free_request( ld, ld->ld_requests ); + } + LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex ); + LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); + + /* free and unbind from all open connections */ + while ( ld->ld_conns != NULL ) { + ldap_free_connection( ld, ld->ld_conns, 1, close ); + } + LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); + LDAP_MUTEX_LOCK( &ld->ld_res_mutex ); + for ( lm = ld->ld_responses; lm != NULL; lm = next ) { + next = lm->lm_next; + ldap_msgfree( lm ); + } + + if ( ld->ld_abandoned != NULL ) { + LDAP_FREE( ld->ld_abandoned ); + ld->ld_abandoned = NULL; + } + LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex ); + + /* Should already be closed by ldap_free_connection which knows not to free + * this one */ + ber_int_sb_destroy( ld->ld_sb ); + LBER_FREE( ld->ld_sb ); + + LDAP_MUTEX_LOCK( &ld->ld_ldopts_mutex ); + + /* final close callbacks */ + { + ldaplist *ll, *next; + + for ( ll = ld->ld_options.ldo_conn_cbs; ll; ll = next ) { + ldap_conncb *cb = ll->ll_data; + next = ll->ll_next; + cb->lc_del( ld, NULL, cb ); + LDAP_FREE( ll ); + } + } + + if ( ld->ld_error != NULL ) { + LDAP_FREE( ld->ld_error ); + ld->ld_error = NULL; + } + + if ( ld->ld_matched != NULL ) { + LDAP_FREE( ld->ld_matched ); + ld->ld_matched = NULL; + } + + if ( ld->ld_referrals != NULL) { + LDAP_VFREE(ld->ld_referrals); + ld->ld_referrals = NULL; + } + + if ( ld->ld_selectinfo != NULL ) { + ldap_free_select_info( ld->ld_selectinfo ); + ld->ld_selectinfo = NULL; + } + + if ( ld->ld_options.ldo_defludp != NULL ) { + ldap_free_urllist( ld->ld_options.ldo_defludp ); + ld->ld_options.ldo_defludp = NULL; + } + +#ifdef LDAP_CONNECTIONLESS + if ( ld->ld_options.ldo_peer != NULL ) { + LDAP_FREE( ld->ld_options.ldo_peer ); + ld->ld_options.ldo_peer = NULL; + } + + if ( ld->ld_options.ldo_cldapdn != NULL ) { + LDAP_FREE( ld->ld_options.ldo_cldapdn ); + ld->ld_options.ldo_cldapdn = NULL; + } +#endif + +#ifdef HAVE_CYRUS_SASL + if ( ld->ld_options.ldo_def_sasl_mech != NULL ) { + LDAP_FREE( ld->ld_options.ldo_def_sasl_mech ); + ld->ld_options.ldo_def_sasl_mech = NULL; + } + + if ( ld->ld_options.ldo_def_sasl_realm != NULL ) { + LDAP_FREE( ld->ld_options.ldo_def_sasl_realm ); + ld->ld_options.ldo_def_sasl_realm = NULL; + } + + if ( ld->ld_options.ldo_def_sasl_authcid != NULL ) { + LDAP_FREE( ld->ld_options.ldo_def_sasl_authcid ); + ld->ld_options.ldo_def_sasl_authcid = NULL; + } + + if ( ld->ld_options.ldo_def_sasl_authzid != NULL ) { + LDAP_FREE( ld->ld_options.ldo_def_sasl_authzid ); + ld->ld_options.ldo_def_sasl_authzid = NULL; + } +#endif + +#ifdef HAVE_TLS + ldap_int_tls_destroy( &ld->ld_options ); +#endif + + if ( ld->ld_options.ldo_sctrls != NULL ) { + ldap_controls_free( ld->ld_options.ldo_sctrls ); + ld->ld_options.ldo_sctrls = NULL; + } + + if ( ld->ld_options.ldo_cctrls != NULL ) { + ldap_controls_free( ld->ld_options.ldo_cctrls ); + ld->ld_options.ldo_cctrls = NULL; + } + LDAP_MUTEX_UNLOCK( &ld->ld_ldopts_mutex ); + +#ifdef LDAP_R_COMPILE + ldap_pvt_thread_mutex_destroy( &ld->ld_msgid_mutex ); + ldap_pvt_thread_mutex_destroy( &ld->ld_conn_mutex ); + ldap_pvt_thread_mutex_destroy( &ld->ld_req_mutex ); + ldap_pvt_thread_mutex_destroy( &ld->ld_res_mutex ); + ldap_pvt_thread_mutex_destroy( &ld->ld_abandon_mutex ); + ldap_pvt_thread_mutex_destroy( &ld->ld_ldopts_mutex ); + ldap_pvt_thread_mutex_destroy( &ld->ld_ldcmutex ); +#endif +#ifndef NDEBUG + LDAP_TRASH(ld); +#endif + LDAP_FREE( (char *) ld->ldc ); + LDAP_FREE( (char *) ld ); + + return( err ); +} + +int +ldap_destroy( LDAP *ld ) +{ + return ( ldap_ld_free( ld, 1, NULL, NULL ) ); +} + +int +ldap_unbind_s( LDAP *ld ) +{ + return( ldap_unbind_ext( ld, NULL, NULL ) ); +} + +/* FIXME: this function is called only by ldap_free_connection(), + * which, most of the times, is called with ld_req_mutex locked */ +int +ldap_send_unbind( + LDAP *ld, + Sockbuf *sb, + LDAPControl **sctrls, + LDAPControl **cctrls ) +{ + BerElement *ber; + ber_int_t id; + + Debug( LDAP_DEBUG_TRACE, "ldap_send_unbind\n", 0, 0, 0 ); + +#ifdef LDAP_CONNECTIONLESS + if (LDAP_IS_UDP(ld)) + return LDAP_SUCCESS; +#endif + /* create a message to send */ + if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) { + return( ld->ld_errno ); + } + + LDAP_NEXT_MSGID(ld, id); + + /* fill it in */ + if ( ber_printf( ber, "{itn" /*}*/, id, + LDAP_REQ_UNBIND ) == -1 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return( ld->ld_errno ); + } + + /* Put Server Controls */ + if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) { + ber_free( ber, 1 ); + return ld->ld_errno; + } + + if ( ber_printf( ber, /*{*/ "N}", LDAP_REQ_UNBIND ) == -1 ) { + ld->ld_errno = LDAP_ENCODING_ERROR; + ber_free( ber, 1 ); + return( ld->ld_errno ); + } + + ld->ld_errno = LDAP_SUCCESS; + /* send the message */ + if ( ber_flush2( sb, ber, LBER_FLUSH_FREE_ALWAYS ) == -1 ) { + ld->ld_errno = LDAP_SERVER_DOWN; + } + + return( ld->ld_errno ); +} diff --git a/libraries/libldap/url.c b/libraries/libldap/url.c new file mode 100644 index 0000000..b39f70b --- /dev/null +++ b/libraries/libldap/url.c @@ -0,0 +1,1622 @@ +/* LIBLDAP url.c -- LDAP URL (RFC 4516) related routines */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1996 Regents of the University of Michigan. + * All rights reserved. + */ + + +/* + * LDAP URLs look like this: + * ldap[is]://host[:port][/[dn[?[attributes][?[scope][?[filter][?exts]]]]]] + * + * where: + * attributes is a comma separated list + * scope is one of these three strings: base one sub (default=base) + * filter is an string-represented filter as in RFC 4515 + * + * e.g., ldap://host:port/dc=com?o,cn?base?(o=openldap)?extension + * + * We also tolerate URLs that look like: <ldapurl> and <URL:ldapurl> + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> +#include <ac/ctype.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +/* local functions */ +static const char* skip_url_prefix LDAP_P(( + const char *url, + int *enclosedp, + const char **scheme )); + +int ldap_pvt_url_scheme2proto( const char *scheme ) +{ + assert( scheme != NULL ); + + if( scheme == NULL ) { + return -1; + } + + if( strcmp("ldap", scheme) == 0 ) { + return LDAP_PROTO_TCP; + } + + if( strcmp("ldapi", scheme) == 0 ) { + return LDAP_PROTO_IPC; + } + + if( strcmp("ldaps", scheme) == 0 ) { + return LDAP_PROTO_TCP; + } +#ifdef LDAP_CONNECTIONLESS + if( strcmp("cldap", scheme) == 0 ) { + return LDAP_PROTO_UDP; + } +#endif + + return -1; +} + +int ldap_pvt_url_scheme_port( const char *scheme, int port ) +{ + assert( scheme != NULL ); + + if( port ) return port; + if( scheme == NULL ) return port; + + if( strcmp("ldap", scheme) == 0 ) { + return LDAP_PORT; + } + + if( strcmp("ldapi", scheme) == 0 ) { + return -1; + } + + if( strcmp("ldaps", scheme) == 0 ) { + return LDAPS_PORT; + } + +#ifdef LDAP_CONNECTIONLESS + if( strcmp("cldap", scheme) == 0 ) { + return LDAP_PORT; + } +#endif + + return -1; +} + +int +ldap_pvt_url_scheme2tls( const char *scheme ) +{ + assert( scheme != NULL ); + + if( scheme == NULL ) { + return -1; + } + + return strcmp("ldaps", scheme) == 0; +} + +int +ldap_is_ldap_url( LDAP_CONST char *url ) +{ + int enclosed; + const char * scheme; + + if( url == NULL ) { + return 0; + } + + if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) { + return 0; + } + + return 1; +} + +int +ldap_is_ldaps_url( LDAP_CONST char *url ) +{ + int enclosed; + const char * scheme; + + if( url == NULL ) { + return 0; + } + + if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) { + return 0; + } + + return strcmp(scheme, "ldaps") == 0; +} + +int +ldap_is_ldapi_url( LDAP_CONST char *url ) +{ + int enclosed; + const char * scheme; + + if( url == NULL ) { + return 0; + } + + if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) { + return 0; + } + + return strcmp(scheme, "ldapi") == 0; +} + +#ifdef LDAP_CONNECTIONLESS +int +ldap_is_ldapc_url( LDAP_CONST char *url ) +{ + int enclosed; + const char * scheme; + + if( url == NULL ) { + return 0; + } + + if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) { + return 0; + } + + return strcmp(scheme, "cldap") == 0; +} +#endif + +static const char* +skip_url_prefix( + const char *url, + int *enclosedp, + const char **scheme ) +{ + /* + * return non-zero if this looks like a LDAP URL; zero if not + * if non-zero returned, *urlp will be moved past "ldap://" part of URL + */ + const char *p; + + if ( url == NULL ) { + return( NULL ); + } + + p = url; + + /* skip leading '<' (if any) */ + if ( *p == '<' ) { + *enclosedp = 1; + ++p; + } else { + *enclosedp = 0; + } + + /* skip leading "URL:" (if any) */ + if ( strncasecmp( p, LDAP_URL_URLCOLON, LDAP_URL_URLCOLON_LEN ) == 0 ) { + p += LDAP_URL_URLCOLON_LEN; + } + + /* check for "ldap://" prefix */ + if ( strncasecmp( p, LDAP_URL_PREFIX, LDAP_URL_PREFIX_LEN ) == 0 ) { + /* skip over "ldap://" prefix and return success */ + p += LDAP_URL_PREFIX_LEN; + *scheme = "ldap"; + return( p ); + } + + /* check for "ldaps://" prefix */ + if ( strncasecmp( p, LDAPS_URL_PREFIX, LDAPS_URL_PREFIX_LEN ) == 0 ) { + /* skip over "ldaps://" prefix and return success */ + p += LDAPS_URL_PREFIX_LEN; + *scheme = "ldaps"; + return( p ); + } + + /* check for "ldapi://" prefix */ + if ( strncasecmp( p, LDAPI_URL_PREFIX, LDAPI_URL_PREFIX_LEN ) == 0 ) { + /* skip over "ldapi://" prefix and return success */ + p += LDAPI_URL_PREFIX_LEN; + *scheme = "ldapi"; + return( p ); + } + +#ifdef LDAP_CONNECTIONLESS + /* check for "cldap://" prefix */ + if ( strncasecmp( p, LDAPC_URL_PREFIX, LDAPC_URL_PREFIX_LEN ) == 0 ) { + /* skip over "cldap://" prefix and return success */ + p += LDAPC_URL_PREFIX_LEN; + *scheme = "cldap"; + return( p ); + } +#endif + + return( NULL ); +} + +int +ldap_pvt_scope2bv( int scope, struct berval *bv ) +{ + switch ( scope ) { + case LDAP_SCOPE_BASE: + BER_BVSTR( bv, "base" ); + break; + + case LDAP_SCOPE_ONELEVEL: + BER_BVSTR( bv, "one" ); + break; + + case LDAP_SCOPE_SUBTREE: + BER_BVSTR( bv, "sub" ); + break; + + case LDAP_SCOPE_SUBORDINATE: + BER_BVSTR( bv, "subordinate" ); + break; + + default: + return LDAP_OTHER; + } + + return LDAP_SUCCESS; +} + +const char * +ldap_pvt_scope2str( int scope ) +{ + struct berval bv; + + if ( ldap_pvt_scope2bv( scope, &bv ) == LDAP_SUCCESS ) { + return bv.bv_val; + } + + return NULL; +} + +int +ldap_pvt_bv2scope( struct berval *bv ) +{ + static struct { + struct berval bv; + int scope; + } v[] = { + { BER_BVC( "one" ), LDAP_SCOPE_ONELEVEL }, + { BER_BVC( "onelevel" ), LDAP_SCOPE_ONELEVEL }, + { BER_BVC( "base" ), LDAP_SCOPE_BASE }, + { BER_BVC( "sub" ), LDAP_SCOPE_SUBTREE }, + { BER_BVC( "subtree" ), LDAP_SCOPE_SUBTREE }, + { BER_BVC( "subord" ), LDAP_SCOPE_SUBORDINATE }, + { BER_BVC( "subordinate" ), LDAP_SCOPE_SUBORDINATE }, + { BER_BVC( "children" ), LDAP_SCOPE_SUBORDINATE }, + { BER_BVNULL, -1 } + }; + int i; + + for ( i = 0; v[ i ].scope != -1; i++ ) { + if ( ber_bvstrcasecmp( bv, &v[ i ].bv ) == 0 ) { + return v[ i ].scope; + } + } + + return( -1 ); +} + +int +ldap_pvt_str2scope( const char *p ) +{ + struct berval bv; + + ber_str2bv( p, 0, 0, &bv ); + + return ldap_pvt_bv2scope( &bv ); +} + +static const char hex[] = "0123456789ABCDEF"; + +#define URLESC_NONE 0x0000U +#define URLESC_COMMA 0x0001U +#define URLESC_SLASH 0x0002U + +static int +hex_escape_len( const char *s, unsigned list ) +{ + int len; + + if ( s == NULL ) { + return 0; + } + + for ( len = 0; s[0]; s++ ) { + switch ( s[0] ) { + /* RFC 2396: reserved */ + case '?': + len += 3; + break; + + case ',': + if ( list & URLESC_COMMA ) { + len += 3; + } else { + len++; + } + break; + + case '/': + if ( list & URLESC_SLASH ) { + len += 3; + } else { + len++; + } + break; + + case ';': + case ':': + case '@': + case '&': + case '=': + case '+': + case '$': + + /* RFC 2396: unreserved mark */ + case '-': + case '_': + case '.': + case '!': + case '~': + case '*': + case '\'': + case '(': + case ')': + len++; + break; + + /* RFC 2396: unreserved alphanum */ + default: + if ( !isalnum( (unsigned char) s[0] ) ) { + len += 3; + } else { + len++; + } + break; + } + } + + return len; +} + +static int +hex_escape( char *buf, int len, const char *s, unsigned list ) +{ + int i; + int pos; + + if ( s == NULL ) { + return 0; + } + + for ( pos = 0, i = 0; s[i] && pos < len; i++ ) { + int escape = 0; + + switch ( s[i] ) { + /* RFC 2396: reserved */ + case '?': + escape = 1; + break; + + case ',': + if ( list & URLESC_COMMA ) { + escape = 1; + } + break; + + case '/': + if ( list & URLESC_SLASH ) { + escape = 1; + } + break; + + case ';': + case ':': + case '@': + case '&': + case '=': + case '+': + case '$': + + /* RFC 2396: unreserved mark */ + case '-': + case '_': + case '.': + case '!': + case '~': + case '*': + case '\'': + case '(': + case ')': + break; + + /* RFC 2396: unreserved alphanum */ + default: + if ( !isalnum( (unsigned char) s[i] ) ) { + escape = 1; + } + break; + } + + if ( escape ) { + buf[pos++] = '%'; + buf[pos++] = hex[ (s[i] >> 4) & 0x0f ]; + buf[pos++] = hex[ s[i] & 0x0f ]; + + } else { + buf[pos++] = s[i]; + } + } + + buf[pos] = '\0'; + + return pos; +} + +static int +hex_escape_len_list( char **s, unsigned flags ) +{ + int len; + int i; + + if ( s == NULL ) { + return 0; + } + + len = 0; + for ( i = 0; s[i] != NULL; i++ ) { + if ( len ) { + len++; + } + len += hex_escape_len( s[i], flags ); + } + + return len; +} + +static int +hex_escape_list( char *buf, int len, char **s, unsigned flags ) +{ + int pos; + int i; + + if ( s == NULL ) { + return 0; + } + + pos = 0; + for ( i = 0; s[i] != NULL; i++ ) { + int curlen; + + if ( pos ) { + buf[pos++] = ','; + len--; + } + curlen = hex_escape( &buf[pos], len, s[i], flags ); + len -= curlen; + pos += curlen; + } + + return pos; +} + +static int +desc2str_len( LDAPURLDesc *u ) +{ + int sep = 0; + int len = 0; + int is_ipc = 0; + struct berval scope; + + if ( u == NULL || u->lud_scheme == NULL ) { + return -1; + } + + if ( !strcmp( "ldapi", u->lud_scheme )) { + is_ipc = 1; + } + + if ( u->lud_exts ) { + len += hex_escape_len_list( u->lud_exts, URLESC_COMMA ); + if ( !sep ) { + sep = 5; + } + } + + if ( u->lud_filter ) { + len += hex_escape_len( u->lud_filter, URLESC_NONE ); + if ( !sep ) { + sep = 4; + } + } + + if ( ldap_pvt_scope2bv( u->lud_scope, &scope ) == LDAP_SUCCESS ) { + len += scope.bv_len; + if ( !sep ) { + sep = 3; + } + } + + if ( u->lud_attrs ) { + len += hex_escape_len_list( u->lud_attrs, URLESC_NONE ); + if ( !sep ) { + sep = 2; + } + } + + if ( u->lud_dn && u->lud_dn[0] ) { + len += hex_escape_len( u->lud_dn, URLESC_NONE ); + if ( !sep ) { + sep = 1; + } + }; + + len += sep; + + if ( u->lud_port ) { + unsigned p = u->lud_port; + if ( p > 65535 ) + return -1; + + len += (p > 999 ? 5 + (p > 9999) : p > 99 ? 4 : 2 + (p > 9)); + } + + if ( u->lud_host && u->lud_host[0] ) { + char *ptr; + len += hex_escape_len( u->lud_host, URLESC_SLASH ); + if ( !is_ipc && ( ptr = strchr( u->lud_host, ':' ))) { + if ( strchr( ptr+1, ':' )) + len += 2; /* IPv6, [] */ + } + } + + len += strlen( u->lud_scheme ) + STRLENOF( "://" ); + + return len; +} + +static int +desc2str( LDAPURLDesc *u, char *s, int len ) +{ + int i; + int sep = 0; + int sofar = 0; + int is_v6 = 0; + int is_ipc = 0; + struct berval scope = BER_BVNULL; + char *ptr; + + if ( u == NULL ) { + return -1; + } + + if ( s == NULL ) { + return -1; + } + + if ( u->lud_scheme && !strcmp( "ldapi", u->lud_scheme )) { + is_ipc = 1; + } + + ldap_pvt_scope2bv( u->lud_scope, &scope ); + + if ( u->lud_exts ) { + sep = 5; + } else if ( u->lud_filter ) { + sep = 4; + } else if ( !BER_BVISEMPTY( &scope ) ) { + sep = 3; + } else if ( u->lud_attrs ) { + sep = 2; + } else if ( u->lud_dn && u->lud_dn[0] ) { + sep = 1; + } + + if ( !is_ipc && u->lud_host && ( ptr = strchr( u->lud_host, ':' ))) { + if ( strchr( ptr+1, ':' )) + is_v6 = 1; + } + + if ( u->lud_port ) { + sofar = sprintf( s, "%s://%s%s%s:%d", u->lud_scheme, + is_v6 ? "[" : "", + u->lud_host ? u->lud_host : "", + is_v6 ? "]" : "", + u->lud_port ); + len -= sofar; + + } else { + sofar = sprintf( s, "%s://", u->lud_scheme ); + len -= sofar; + if ( u->lud_host && u->lud_host[0] ) { + if ( is_v6 ) { + s[sofar++] = '['; + len--; + } + i = hex_escape( &s[sofar], len, u->lud_host, URLESC_SLASH ); + sofar += i; + len -= i; + if ( is_v6 ) { + s[sofar++] = ']'; + len--; + } + } + } + + assert( len >= 0 ); + + if ( sep < 1 ) { + goto done; + } + + s[sofar++] = '/'; + len--; + + assert( len >= 0 ); + + if ( u->lud_dn && u->lud_dn[0] ) { + i = hex_escape( &s[sofar], len, u->lud_dn, URLESC_NONE ); + sofar += i; + len -= i; + + assert( len >= 0 ); + } + + if ( sep < 2 ) { + goto done; + } + s[sofar++] = '?'; + len--; + + assert( len >= 0 ); + + i = hex_escape_list( &s[sofar], len, u->lud_attrs, URLESC_NONE ); + sofar += i; + len -= i; + + assert( len >= 0 ); + + if ( sep < 3 ) { + goto done; + } + s[sofar++] = '?'; + len--; + + assert( len >= 0 ); + + if ( !BER_BVISNULL( &scope ) ) { + strcpy( &s[sofar], scope.bv_val ); + sofar += scope.bv_len; + len -= scope.bv_len; + } + + assert( len >= 0 ); + + if ( sep < 4 ) { + goto done; + } + s[sofar++] = '?'; + len--; + + assert( len >= 0 ); + + i = hex_escape( &s[sofar], len, u->lud_filter, URLESC_NONE ); + sofar += i; + len -= i; + + assert( len >= 0 ); + + if ( sep < 5 ) { + goto done; + } + s[sofar++] = '?'; + len--; + + assert( len >= 0 ); + + i = hex_escape_list( &s[sofar], len, u->lud_exts, URLESC_COMMA ); + sofar += i; + len -= i; + + assert( len >= 0 ); + +done: + if ( len < 0 ) { + return -1; + } + + return sofar; +} + +char * +ldap_url_desc2str( LDAPURLDesc *u ) +{ + int len; + char *s; + + if ( u == NULL ) { + return NULL; + } + + len = desc2str_len( u ); + if ( len < 0 ) { + return NULL; + } + + /* allocate enough to hex escape everything -- overkill */ + s = LDAP_MALLOC( len + 1 ); + + if ( s == NULL ) { + return NULL; + } + + if ( desc2str( u, s, len ) != len ) { + LDAP_FREE( s ); + return NULL; + } + + s[len] = '\0'; + + return s; +} + +int +ldap_url_parse_ext( LDAP_CONST char *url_in, LDAPURLDesc **ludpp, unsigned flags ) +{ +/* + * Pick apart the pieces of an LDAP URL. + */ + + LDAPURLDesc *ludp; + char *p, *q, *r; + int i, enclosed, proto, is_v6 = 0; + const char *scheme = NULL; + const char *url_tmp; + char *url; + + int check_dn = 1; + + if( url_in == NULL || ludpp == NULL ) { + return LDAP_URL_ERR_PARAM; + } + +#ifndef LDAP_INT_IN_KERNEL + /* Global options may not be created yet + * We can't test if the global options are initialized + * because a call to LDAP_INT_GLOBAL_OPT() will try to allocate + * the options and cause infinite recursion + */ + Debug( LDAP_DEBUG_TRACE, "ldap_url_parse_ext(%s)\n", url_in, 0, 0 ); +#endif + + *ludpp = NULL; /* pessimistic */ + + url_tmp = skip_url_prefix( url_in, &enclosed, &scheme ); + + if ( url_tmp == NULL ) { + return LDAP_URL_ERR_BADSCHEME; + } + + assert( scheme != NULL ); + + proto = ldap_pvt_url_scheme2proto( scheme ); + if ( proto == -1 ) { + return LDAP_URL_ERR_BADSCHEME; + } + + /* make working copy of the remainder of the URL */ + url = LDAP_STRDUP( url_tmp ); + if ( url == NULL ) { + return LDAP_URL_ERR_MEM; + } + + if ( enclosed ) { + p = &url[strlen(url)-1]; + + if( *p != '>' ) { + LDAP_FREE( url ); + return LDAP_URL_ERR_BADENCLOSURE; + } + + *p = '\0'; + } + + /* allocate return struct */ + ludp = (LDAPURLDesc *)LDAP_CALLOC( 1, sizeof( LDAPURLDesc )); + + if ( ludp == NULL ) { + LDAP_FREE( url ); + return LDAP_URL_ERR_MEM; + } + + ludp->lud_next = NULL; + ludp->lud_host = NULL; + ludp->lud_port = 0; + ludp->lud_dn = NULL; + ludp->lud_attrs = NULL; + ludp->lud_scope = ( flags & LDAP_PVT_URL_PARSE_NODEF_SCOPE ) ? LDAP_SCOPE_BASE : LDAP_SCOPE_DEFAULT; + ludp->lud_filter = NULL; + ludp->lud_exts = NULL; + + ludp->lud_scheme = LDAP_STRDUP( scheme ); + + if ( ludp->lud_scheme == NULL ) { + LDAP_FREE( url ); + ldap_free_urldesc( ludp ); + return LDAP_URL_ERR_MEM; + } + + /* scan forward for '/' that marks end of hostport and begin. of dn */ + p = strchr( url, '/' ); + q = NULL; + + if( p != NULL ) { + /* terminate hostport; point to start of dn */ + *p++ = '\0'; + } else { + /* check for Novell kludge, see below */ + p = strchr( url, '?' ); + if ( p ) { + *p++ = '\0'; + q = p; + p = NULL; + } + } + + if ( proto != LDAP_PROTO_IPC ) { + /* IPv6 syntax with [ip address]:port */ + if ( *url == '[' ) { + r = strchr( url, ']' ); + if ( r == NULL ) { + LDAP_FREE( url ); + ldap_free_urldesc( ludp ); + return LDAP_URL_ERR_BADURL; + } + *r++ = '\0'; + q = strchr( r, ':' ); + if ( q && q != r ) { + LDAP_FREE( url ); + ldap_free_urldesc( ludp ); + return LDAP_URL_ERR_BADURL; + } + is_v6 = 1; + } else { + q = strchr( url, ':' ); + } + + if ( q != NULL ) { + char *next; + + *q++ = '\0'; + ldap_pvt_hex_unescape( q ); + + if( *q == '\0' ) { + LDAP_FREE( url ); + ldap_free_urldesc( ludp ); + return LDAP_URL_ERR_BADURL; + } + + ludp->lud_port = strtol( q, &next, 10 ); + if ( next == q || next[0] != '\0' ) { + LDAP_FREE( url ); + ldap_free_urldesc( ludp ); + return LDAP_URL_ERR_BADURL; + } + /* check for Novell kludge */ + if ( !p ) { + if ( *next != '\0' ) { + q = &next[1]; + } else { + q = NULL; + } + } + } + + if ( ( flags & LDAP_PVT_URL_PARSE_DEF_PORT ) && ludp->lud_port == 0 ) { + if ( strcmp( ludp->lud_scheme, "ldaps" ) == 0 ) { + ludp->lud_port = LDAPS_PORT; + } else { + ludp->lud_port = LDAP_PORT; + } + } + } + + ldap_pvt_hex_unescape( url ); + + /* If [ip address]:port syntax, url is [ip and we skip the [ */ + ludp->lud_host = LDAP_STRDUP( url + is_v6 ); + + if( ludp->lud_host == NULL ) { + LDAP_FREE( url ); + ldap_free_urldesc( ludp ); + return LDAP_URL_ERR_MEM; + } + + if ( ( flags & LDAP_PVT_URL_PARSE_NOEMPTY_HOST ) + && ludp->lud_host != NULL + && *ludp->lud_host == '\0' ) + { + LDAP_FREE( ludp->lud_host ); + ludp->lud_host = NULL; + } + + /* + * Kludge. ldap://111.222.333.444:389??cn=abc,o=company + * + * On early Novell releases, search references/referrals were returned + * in this format, i.e., the dn was kind of in the scope position, + * but the required slash is missing. The whole thing is illegal syntax, + * but we need to account for it. Fortunately it can't be confused with + * anything real. + */ + if( (p == NULL) && (q != NULL) && (*q == '?') ) { + /* ? immediately followed by question */ + q++; + if( *q != '\0' ) { + /* parse dn part */ + ldap_pvt_hex_unescape( q ); + ludp->lud_dn = LDAP_STRDUP( q ); + + } else if ( !( flags & LDAP_PVT_URL_PARSE_NOEMPTY_DN ) ) { + ludp->lud_dn = LDAP_STRDUP( "" ); + + } else { + check_dn = 0; + } + + if ( check_dn && ludp->lud_dn == NULL ) { + LDAP_FREE( url ); + ldap_free_urldesc( ludp ); + return LDAP_URL_ERR_MEM; + } + } + + if( p == NULL ) { + LDAP_FREE( url ); + *ludpp = ludp; + return LDAP_URL_SUCCESS; + } + + /* scan forward for '?' that may marks end of dn */ + q = strchr( p, '?' ); + + if( q != NULL ) { + /* terminate dn part */ + *q++ = '\0'; + } + + if( *p != '\0' ) { + /* parse dn part */ + ldap_pvt_hex_unescape( p ); + ludp->lud_dn = LDAP_STRDUP( p ); + + } else if ( !( flags & LDAP_PVT_URL_PARSE_NOEMPTY_DN ) ) { + ludp->lud_dn = LDAP_STRDUP( "" ); + + } else { + check_dn = 0; + } + + if( check_dn && ludp->lud_dn == NULL ) { + LDAP_FREE( url ); + ldap_free_urldesc( ludp ); + return LDAP_URL_ERR_MEM; + } + + if( q == NULL ) { + /* no more */ + LDAP_FREE( url ); + *ludpp = ludp; + return LDAP_URL_SUCCESS; + } + + /* scan forward for '?' that may marks end of attributes */ + p = q; + q = strchr( p, '?' ); + + if( q != NULL ) { + /* terminate attributes part */ + *q++ = '\0'; + } + + if( *p != '\0' ) { + /* parse attributes */ + ldap_pvt_hex_unescape( p ); + ludp->lud_attrs = ldap_str2charray( p, "," ); + + if( ludp->lud_attrs == NULL ) { + LDAP_FREE( url ); + ldap_free_urldesc( ludp ); + return LDAP_URL_ERR_BADATTRS; + } + } + + if ( q == NULL ) { + /* no more */ + LDAP_FREE( url ); + *ludpp = ludp; + return LDAP_URL_SUCCESS; + } + + /* scan forward for '?' that may marks end of scope */ + p = q; + q = strchr( p, '?' ); + + if( q != NULL ) { + /* terminate the scope part */ + *q++ = '\0'; + } + + if( *p != '\0' ) { + /* parse the scope */ + ldap_pvt_hex_unescape( p ); + ludp->lud_scope = ldap_pvt_str2scope( p ); + + if( ludp->lud_scope == -1 ) { + LDAP_FREE( url ); + ldap_free_urldesc( ludp ); + return LDAP_URL_ERR_BADSCOPE; + } + } + + if ( q == NULL ) { + /* no more */ + LDAP_FREE( url ); + *ludpp = ludp; + return LDAP_URL_SUCCESS; + } + + /* scan forward for '?' that may marks end of filter */ + p = q; + q = strchr( p, '?' ); + + if( q != NULL ) { + /* terminate the filter part */ + *q++ = '\0'; + } + + if( *p != '\0' ) { + /* parse the filter */ + ldap_pvt_hex_unescape( p ); + + if( ! *p ) { + /* missing filter */ + LDAP_FREE( url ); + ldap_free_urldesc( ludp ); + return LDAP_URL_ERR_BADFILTER; + } + + ludp->lud_filter = LDAP_STRDUP( p ); + + if( ludp->lud_filter == NULL ) { + LDAP_FREE( url ); + ldap_free_urldesc( ludp ); + return LDAP_URL_ERR_MEM; + } + } + + if ( q == NULL ) { + /* no more */ + LDAP_FREE( url ); + *ludpp = ludp; + return LDAP_URL_SUCCESS; + } + + /* scan forward for '?' that may marks end of extensions */ + p = q; + q = strchr( p, '?' ); + + if( q != NULL ) { + /* extra '?' */ + LDAP_FREE( url ); + ldap_free_urldesc( ludp ); + return LDAP_URL_ERR_BADURL; + } + + /* parse the extensions */ + ludp->lud_exts = ldap_str2charray( p, "," ); + + if( ludp->lud_exts == NULL ) { + LDAP_FREE( url ); + ldap_free_urldesc( ludp ); + return LDAP_URL_ERR_BADEXTS; + } + + for( i=0; ludp->lud_exts[i] != NULL; i++ ) { + ldap_pvt_hex_unescape( ludp->lud_exts[i] ); + + if( *ludp->lud_exts[i] == '!' ) { + /* count the number of critical extensions */ + ludp->lud_crit_exts++; + } + } + + if( i == 0 ) { + /* must have 1 or more */ + LDAP_FREE( url ); + ldap_free_urldesc( ludp ); + return LDAP_URL_ERR_BADEXTS; + } + + /* no more */ + *ludpp = ludp; + LDAP_FREE( url ); + return LDAP_URL_SUCCESS; +} + +int +ldap_url_parse( LDAP_CONST char *url_in, LDAPURLDesc **ludpp ) +{ + return ldap_url_parse_ext( url_in, ludpp, LDAP_PVT_URL_PARSE_HISTORIC ); +} + +LDAPURLDesc * +ldap_url_dup ( LDAPURLDesc *ludp ) +{ + LDAPURLDesc *dest; + + if ( ludp == NULL ) { + return NULL; + } + + dest = LDAP_MALLOC( sizeof(LDAPURLDesc) ); + if (dest == NULL) + return NULL; + + *dest = *ludp; + dest->lud_scheme = NULL; + dest->lud_host = NULL; + dest->lud_dn = NULL; + dest->lud_filter = NULL; + dest->lud_attrs = NULL; + dest->lud_exts = NULL; + dest->lud_next = NULL; + + if ( ludp->lud_scheme != NULL ) { + dest->lud_scheme = LDAP_STRDUP( ludp->lud_scheme ); + if (dest->lud_scheme == NULL) { + ldap_free_urldesc(dest); + return NULL; + } + } + + if ( ludp->lud_host != NULL ) { + dest->lud_host = LDAP_STRDUP( ludp->lud_host ); + if (dest->lud_host == NULL) { + ldap_free_urldesc(dest); + return NULL; + } + } + + if ( ludp->lud_dn != NULL ) { + dest->lud_dn = LDAP_STRDUP( ludp->lud_dn ); + if (dest->lud_dn == NULL) { + ldap_free_urldesc(dest); + return NULL; + } + } + + if ( ludp->lud_filter != NULL ) { + dest->lud_filter = LDAP_STRDUP( ludp->lud_filter ); + if (dest->lud_filter == NULL) { + ldap_free_urldesc(dest); + return NULL; + } + } + + if ( ludp->lud_attrs != NULL ) { + dest->lud_attrs = ldap_charray_dup( ludp->lud_attrs ); + if (dest->lud_attrs == NULL) { + ldap_free_urldesc(dest); + return NULL; + } + } + + if ( ludp->lud_exts != NULL ) { + dest->lud_exts = ldap_charray_dup( ludp->lud_exts ); + if (dest->lud_exts == NULL) { + ldap_free_urldesc(dest); + return NULL; + } + } + + return dest; +} + +LDAPURLDesc * +ldap_url_duplist (LDAPURLDesc *ludlist) +{ + LDAPURLDesc *dest, *tail, *ludp, *newludp; + + dest = NULL; + tail = NULL; + for (ludp = ludlist; ludp != NULL; ludp = ludp->lud_next) { + newludp = ldap_url_dup(ludp); + if (newludp == NULL) { + ldap_free_urllist(dest); + return NULL; + } + if (tail == NULL) + dest = newludp; + else + tail->lud_next = newludp; + tail = newludp; + } + return dest; +} + +static int +ldap_url_parselist_int (LDAPURLDesc **ludlist, const char *url, const char *sep, unsigned flags ) + +{ + int i, rc; + LDAPURLDesc *ludp; + char **urls; + + assert( ludlist != NULL ); + assert( url != NULL ); + + *ludlist = NULL; + + if ( sep == NULL ) { + sep = ", "; + } + + urls = ldap_str2charray( url, sep ); + if (urls == NULL) + return LDAP_URL_ERR_MEM; + + /* count the URLs... */ + for (i = 0; urls[i] != NULL; i++) ; + /* ...and put them in the "stack" backward */ + while (--i >= 0) { + rc = ldap_url_parse_ext( urls[i], &ludp, flags ); + if ( rc != 0 ) { + ldap_charray_free( urls ); + ldap_free_urllist( *ludlist ); + *ludlist = NULL; + return rc; + } + ludp->lud_next = *ludlist; + *ludlist = ludp; + } + ldap_charray_free( urls ); + return LDAP_URL_SUCCESS; +} + +int +ldap_url_parselist (LDAPURLDesc **ludlist, const char *url ) +{ + return ldap_url_parselist_int( ludlist, url, ", ", LDAP_PVT_URL_PARSE_HISTORIC ); +} + +int +ldap_url_parselist_ext (LDAPURLDesc **ludlist, const char *url, const char *sep, unsigned flags ) +{ + return ldap_url_parselist_int( ludlist, url, sep, flags ); +} + +int +ldap_url_parsehosts( + LDAPURLDesc **ludlist, + const char *hosts, + int port ) +{ + int i; + LDAPURLDesc *ludp; + char **specs, *p; + + assert( ludlist != NULL ); + assert( hosts != NULL ); + + *ludlist = NULL; + + specs = ldap_str2charray(hosts, ", "); + if (specs == NULL) + return LDAP_NO_MEMORY; + + /* count the URLs... */ + for (i = 0; specs[i] != NULL; i++) /* EMPTY */; + + /* ...and put them in the "stack" backward */ + while (--i >= 0) { + ludp = LDAP_CALLOC( 1, sizeof(LDAPURLDesc) ); + if (ludp == NULL) { + ldap_charray_free(specs); + ldap_free_urllist(*ludlist); + *ludlist = NULL; + return LDAP_NO_MEMORY; + } + ludp->lud_port = port; + ludp->lud_host = specs[i]; + specs[i] = NULL; + p = strchr(ludp->lud_host, ':'); + if (p != NULL) { + /* more than one :, IPv6 address */ + if ( strchr(p+1, ':') != NULL ) { + /* allow [address] and [address]:port */ + if ( *ludp->lud_host == '[' ) { + p = LDAP_STRDUP(ludp->lud_host+1); + /* copied, make sure we free source later */ + specs[i] = ludp->lud_host; + ludp->lud_host = p; + p = strchr( ludp->lud_host, ']' ); + if ( p == NULL ) { + LDAP_FREE(ludp); + ldap_charray_free(specs); + return LDAP_PARAM_ERROR; + } + *p++ = '\0'; + if ( *p != ':' ) { + if ( *p != '\0' ) { + LDAP_FREE(ludp); + ldap_charray_free(specs); + return LDAP_PARAM_ERROR; + } + p = NULL; + } + } else { + p = NULL; + } + } + if (p != NULL) { + char *next; + + *p++ = 0; + ldap_pvt_hex_unescape(p); + ludp->lud_port = strtol( p, &next, 10 ); + if ( next == p || next[0] != '\0' ) { + LDAP_FREE(ludp); + ldap_charray_free(specs); + return LDAP_PARAM_ERROR; + } + } + } + ldap_pvt_hex_unescape(ludp->lud_host); + ludp->lud_scheme = LDAP_STRDUP("ldap"); + ludp->lud_next = *ludlist; + *ludlist = ludp; + } + + /* this should be an array of NULLs now */ + /* except entries starting with [ */ + ldap_charray_free(specs); + return LDAP_SUCCESS; +} + +char * +ldap_url_list2hosts (LDAPURLDesc *ludlist) +{ + LDAPURLDesc *ludp; + int size; + char *s, *p, buf[32]; /* big enough to hold a long decimal # (overkill) */ + + if (ludlist == NULL) + return NULL; + + /* figure out how big the string is */ + size = 1; /* nul-term */ + for (ludp = ludlist; ludp != NULL; ludp = ludp->lud_next) { + if ( ludp->lud_host == NULL ) continue; + size += strlen(ludp->lud_host) + 1; /* host and space */ + if (strchr(ludp->lud_host, ':')) /* will add [ ] below */ + size += 2; + if (ludp->lud_port != 0) + size += sprintf(buf, ":%d", ludp->lud_port); + } + s = LDAP_MALLOC(size); + if (s == NULL) + return NULL; + + p = s; + for (ludp = ludlist; ludp != NULL; ludp = ludp->lud_next) { + if ( ludp->lud_host == NULL ) continue; + if (strchr(ludp->lud_host, ':')) { + p += sprintf(p, "[%s]", ludp->lud_host); + } else { + strcpy(p, ludp->lud_host); + p += strlen(ludp->lud_host); + } + if (ludp->lud_port != 0) + p += sprintf(p, ":%d", ludp->lud_port); + *p++ = ' '; + } + if (p != s) + p--; /* nuke that extra space */ + *p = '\0'; + return s; +} + +char * +ldap_url_list2urls( + LDAPURLDesc *ludlist ) +{ + LDAPURLDesc *ludp; + int size, sofar; + char *s; + + if ( ludlist == NULL ) { + return NULL; + } + + /* figure out how big the string is */ + for ( size = 0, ludp = ludlist; ludp != NULL; ludp = ludp->lud_next ) { + int len = desc2str_len( ludp ); + if ( len < 0 ) { + return NULL; + } + size += len + 1; + } + + s = LDAP_MALLOC( size ); + + if ( s == NULL ) { + return NULL; + } + + for ( sofar = 0, ludp = ludlist; ludp != NULL; ludp = ludp->lud_next ) { + int len; + + len = desc2str( ludp, &s[sofar], size ); + + if ( len < 0 ) { + LDAP_FREE( s ); + return NULL; + } + + sofar += len; + size -= len; + + s[sofar++] = ' '; + size--; + + assert( size >= 0 ); + } + + s[sofar - 1] = '\0'; + + return s; +} + +void +ldap_free_urllist( LDAPURLDesc *ludlist ) +{ + LDAPURLDesc *ludp, *next; + + for (ludp = ludlist; ludp != NULL; ludp = next) { + next = ludp->lud_next; + ldap_free_urldesc(ludp); + } +} + +void +ldap_free_urldesc( LDAPURLDesc *ludp ) +{ + if ( ludp == NULL ) { + return; + } + + if ( ludp->lud_scheme != NULL ) { + LDAP_FREE( ludp->lud_scheme ); + } + + if ( ludp->lud_host != NULL ) { + LDAP_FREE( ludp->lud_host ); + } + + if ( ludp->lud_dn != NULL ) { + LDAP_FREE( ludp->lud_dn ); + } + + if ( ludp->lud_filter != NULL ) { + LDAP_FREE( ludp->lud_filter); + } + + if ( ludp->lud_attrs != NULL ) { + LDAP_VFREE( ludp->lud_attrs ); + } + + if ( ludp->lud_exts != NULL ) { + LDAP_VFREE( ludp->lud_exts ); + } + + LDAP_FREE( ludp ); +} + +static int +ldap_int_is_hexpair( char *s ) +{ + int i; + + for ( i = 0; i < 2; i++ ) { + if ( s[i] >= '0' && s[i] <= '9' ) { + continue; + } + + if ( s[i] >= 'A' && s[i] <= 'F' ) { + continue; + } + + if ( s[i] >= 'a' && s[i] <= 'f' ) { + continue; + } + + return 0; + } + + return 1; +} + +static int +ldap_int_unhex( int c ) +{ + return( c >= '0' && c <= '9' ? c - '0' + : c >= 'A' && c <= 'F' ? c - 'A' + 10 + : c - 'a' + 10 ); +} + +void +ldap_pvt_hex_unescape( char *s ) +{ + /* + * Remove URL hex escapes from s... done in place. The basic concept for + * this routine is borrowed from the WWW library HTUnEscape() routine. + */ + char *p, + *save_s = s; + + for ( p = s; *s != '\0'; ++s ) { + if ( *s == '%' ) { + /* + * FIXME: what if '%' is followed + * by non-hexpair chars? + */ + if ( !ldap_int_is_hexpair( s + 1 ) ) { + p = save_s; + break; + } + + if ( *++s == '\0' ) { + break; + } + *p = ldap_int_unhex( *s ) << 4; + if ( *++s == '\0' ) { + break; + } + *p++ += ldap_int_unhex( *s ); + } else { + *p++ = *s; + } + } + + *p = '\0'; +} + diff --git a/libraries/libldap/urltest.c b/libraries/libldap/urltest.c new file mode 100644 index 0000000..1abfa57 --- /dev/null +++ b/libraries/libldap/urltest.c @@ -0,0 +1,128 @@ +/* urltest.c -- OpenLDAP URL API Test Program */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENT: + * This program was initially developed by Pierangelo Masarati + * <ando@OpenLDAP.org> for inclusion in OpenLDAP Software. + */ + +/* + * This program is designed to test the ldap_url_* functions + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/unistd.h> + +#include <ldap.h> + +#include "ldap-int.h" + +#include "ldap_defaults.h" + +int +main(int argc, char *argv[]) +{ + const char *url, + *scope = NULL; + LDAPURLDesc *lud; + enum { + IS_LDAP = 0, + IS_LDAPS, + IS_LDAPI + } type = IS_LDAP; + int rc; + + if ( argc != 2 ) { + fprintf( stderr, "usage: urltest <url>\n" ); + exit( EXIT_FAILURE ); + } + + url = argv[ 1 ]; + + if ( ldap_is_ldaps_url( url ) ) { + fprintf( stdout, "LDAPS url\n" ); + type = IS_LDAPS; + + } else if ( ldap_is_ldapi_url( url ) ) { + fprintf( stdout, "LDAPI url\n" ); + type = IS_LDAPI; + + } else if ( ldap_is_ldap_url( url ) ) { + fprintf( stdout, "generic LDAP url\n" ); + + } else { + fprintf( stderr, "Need a valid LDAP url\n" ); + exit( EXIT_FAILURE ); + } + + rc = ldap_url_parse( url, &lud ); + if ( rc != LDAP_URL_SUCCESS ) { + fprintf( stderr, "ldap_url_parse(%s) failed (%d)\n", url, rc ); + exit( EXIT_FAILURE ); + } + + fprintf( stdout, "PROTO: %s\n", lud->lud_scheme ); + switch ( type ) { + case IS_LDAPI: + fprintf( stdout, "PATH: %s\n", lud->lud_host ); + break; + + default: + fprintf( stdout, "HOST: %s\n", lud->lud_host ); + if ( lud->lud_port != 0 ) { + fprintf( stdout, "PORT: %d\n", lud->lud_port ); + } + } + + if ( lud->lud_dn && lud->lud_dn[ 0 ] ) { + fprintf( stdout, "DN: %s\n", lud->lud_dn ); + } + + if ( lud->lud_attrs ) { + int i; + + fprintf( stdout, "ATTRS:\n" ); + for ( i = 0; lud->lud_attrs[ i ]; i++ ) { + fprintf( stdout, "\t%s\n", lud->lud_attrs[ i ] ); + } + } + + scope = ldap_pvt_scope2str( lud->lud_scope ); + if ( scope ) { + fprintf( stdout, "SCOPE: %s\n", scope ); + } + + if ( lud->lud_filter ) { + fprintf( stdout, "FILTER: %s\n", lud->lud_filter ); + } + + if ( lud->lud_exts ) { + int i; + + fprintf( stdout, "EXTS:\n" ); + for ( i = 0; lud->lud_exts[ i ]; i++ ) { + fprintf( stdout, "\t%s\n", lud->lud_exts[ i ] ); + } + } + + fprintf( stdout, "URL: %s\n", ldap_url_desc2str( lud )); + + return EXIT_SUCCESS; +} diff --git a/libraries/libldap/utf-8-conv.c b/libraries/libldap/utf-8-conv.c new file mode 100644 index 0000000..eb55509 --- /dev/null +++ b/libraries/libldap/utf-8-conv.c @@ -0,0 +1,485 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved. + * + * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND + * TREATIES. USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT + * TO VERSION 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS + * AVAILABLE AT HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE" + * IN THE TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION + * OF THIS WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP + * PUBLIC LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT + * THE PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY. + *--- + * Note: A verbatim copy of version 2.0.1 of the OpenLDAP Public License + * can be found in the file "build/LICENSE-2.0.1" in this distribution + * of OpenLDAP Software. + */ + +/* + * UTF-8 Conversion Routines + * + * These routines convert between Wide Character and UTF-8, + * or between MultiByte and UTF-8 encodings. + * + * Both single character and string versions of the functions are provided. + * All functions return -1 if the character or string cannot be converted. + */ + +#include "portable.h" + +#if SIZEOF_WCHAR_T >= 4 +/* These routines assume ( sizeof(wchar_t) >= 4 ) */ + +#include <stdio.h> +#include <ac/stdlib.h> /* For wctomb, wcstombs, mbtowc, mbstowcs */ +#include <ac/string.h> +#include <ac/time.h> /* for time_t */ + +#include "ldap-int.h" + +#include <ldap_utf8.h> + +static unsigned char mask[] = { 0, 0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01 }; + + +/*----------------------------------------------------------------------------- + UTF-8 Format Summary + +ASCII chars 7 bits + 0xxxxxxx + +2-character UTF-8 sequence: 11 bits + 110xxxxx 10xxxxxx + +3-character UTF-8 16 bits + 1110xxxx 10xxxxxx 10xxxxxx + +4-char UTF-8 21 bits + 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + +5-char UTF-8 26 bits + 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + +6-char UTF-8 31 bits + 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + +Unicode address space (0 - 0x10FFFF) 21 bits +ISO-10646 address space (0 - 0x7FFFFFFF) 31 bits + +Note: This code does not prevent UTF-8 sequences which are longer than + necessary from being decoded. +*/ + +/*----------------------------------------------------------------------------- + Convert a UTF-8 character to a wide char. + Return the length of the UTF-8 input character in bytes. +*/ +int +ldap_x_utf8_to_wc ( wchar_t *wchar, const char *utf8char ) +{ + int utflen, i; + wchar_t ch; + + if (utf8char == NULL) return -1; + + /* Get UTF-8 sequence length from 1st byte */ + utflen = LDAP_UTF8_CHARLEN2(utf8char, utflen); + + if( utflen==0 || utflen > (int)LDAP_MAX_UTF8_LEN ) return -1; + + /* First byte minus length tag */ + ch = (wchar_t)(utf8char[0] & mask[utflen]); + + for(i=1; i < utflen; i++) { + /* Subsequent bytes must start with 10 */ + if ((utf8char[i] & 0xc0) != 0x80) return -1; + + ch <<= 6; /* 6 bits of data in each subsequent byte */ + ch |= (wchar_t)(utf8char[i] & 0x3f); + } + + if (wchar) *wchar = ch; + + return utflen; +} + +/*----------------------------------------------------------------------------- + Convert a UTF-8 string to a wide char string. + No more than 'count' wide chars will be written to the output buffer. + Return the size of the converted string in wide chars, excl null terminator. +*/ +int +ldap_x_utf8s_to_wcs ( wchar_t *wcstr, const char *utf8str, size_t count ) +{ + size_t wclen = 0; + int utflen, i; + wchar_t ch; + + + /* If input ptr is NULL or empty... */ + if (utf8str == NULL || !*utf8str) { + if ( wcstr ) + *wcstr = 0; + return 0; + } + + /* Examine next UTF-8 character. If output buffer is NULL, ignore count */ + while ( *utf8str && (wcstr==NULL || wclen<count) ) { + /* Get UTF-8 sequence length from 1st byte */ + utflen = LDAP_UTF8_CHARLEN2(utf8str, utflen); + + if( utflen==0 || utflen > (int)LDAP_MAX_UTF8_LEN ) return -1; + + /* First byte minus length tag */ + ch = (wchar_t)(utf8str[0] & mask[utflen]); + + for(i=1; i < utflen; i++) { + /* Subsequent bytes must start with 10 */ + if ((utf8str[i] & 0xc0) != 0x80) return -1; + + ch <<= 6; /* 6 bits of data in each subsequent byte */ + ch |= (wchar_t)(utf8str[i] & 0x3f); + } + + if (wcstr) wcstr[wclen] = ch; + + utf8str += utflen; /* Move to next UTF-8 character */ + wclen++; /* Count number of wide chars stored/required */ + } + + /* Add null terminator if there's room in the buffer. */ + if (wcstr && wclen < count) wcstr[wclen] = 0; + + return wclen; +} + + +/*----------------------------------------------------------------------------- + Convert one wide char to a UTF-8 character. + Return the length of the converted UTF-8 character in bytes. + No more than 'count' bytes will be written to the output buffer. +*/ +int +ldap_x_wc_to_utf8 ( char *utf8char, wchar_t wchar, size_t count ) +{ + int len=0; + + if (utf8char == NULL) /* Just determine the required UTF-8 char length. */ + { /* Ignore count */ + if( wchar < 0 ) + return -1; + if( wchar < 0x80 ) + return 1; + if( wchar < 0x800 ) + return 2; + if( wchar < 0x10000 ) + return 3; + if( wchar < 0x200000 ) + return 4; + if( wchar < 0x4000000 ) + return 5; +#if SIZEOF_WCHAR_T > 4 + /* UL is not strictly needed by ANSI C */ + if( wchar < (wchar_t)0x80000000UL ) +#endif /* SIZEOF_WCHAR_T > 4 */ + return 6; + return -1; + } + + + if ( wchar < 0 ) { /* Invalid wide character */ + len = -1; + + } else if( wchar < 0x80 ) { + if (count >= 1) { + utf8char[len++] = (char)wchar; + } + + } else if( wchar < 0x800 ) { + if (count >=2) { + utf8char[len++] = 0xc0 | ( wchar >> 6 ); + utf8char[len++] = 0x80 | ( wchar & 0x3f ); + } + + } else if( wchar < 0x10000 ) { + if (count >= 3) { + utf8char[len++] = 0xe0 | ( wchar >> 12 ); + utf8char[len++] = 0x80 | ( (wchar >> 6) & 0x3f ); + utf8char[len++] = 0x80 | ( wchar & 0x3f ); + } + + } else if( wchar < 0x200000 ) { + if (count >= 4) { + utf8char[len++] = 0xf0 | ( wchar >> 18 ); + utf8char[len++] = 0x80 | ( (wchar >> 12) & 0x3f ); + utf8char[len++] = 0x80 | ( (wchar >> 6) & 0x3f ); + utf8char[len++] = 0x80 | ( wchar & 0x3f ); + } + + } else if( wchar < 0x4000000 ) { + if (count >= 5) { + utf8char[len++] = 0xf8 | ( wchar >> 24 ); + utf8char[len++] = 0x80 | ( (wchar >> 18) & 0x3f ); + utf8char[len++] = 0x80 | ( (wchar >> 12) & 0x3f ); + utf8char[len++] = 0x80 | ( (wchar >> 6) & 0x3f ); + utf8char[len++] = 0x80 | ( wchar & 0x3f ); + } + + } else +#if SIZEOF_WCHAR_T > 4 + /* UL is not strictly needed by ANSI C */ + if( wchar < (wchar_t)0x80000000UL ) +#endif /* SIZEOF_WCHAR_T > 4 */ + { + if (count >= 6) { + utf8char[len++] = 0xfc | ( wchar >> 30 ); + utf8char[len++] = 0x80 | ( (wchar >> 24) & 0x3f ); + utf8char[len++] = 0x80 | ( (wchar >> 18) & 0x3f ); + utf8char[len++] = 0x80 | ( (wchar >> 12) & 0x3f ); + utf8char[len++] = 0x80 | ( (wchar >> 6) & 0x3f ); + utf8char[len++] = 0x80 | ( wchar & 0x3f ); + } + +#if SIZEOF_WCHAR_T > 4 + } else { + len = -1; +#endif /* SIZEOF_WCHAR_T > 4 */ + } + + return len; + +} + + +/*----------------------------------------------------------------------------- + Convert a wide char string to a UTF-8 string. + No more than 'count' bytes will be written to the output buffer. + Return the # of bytes written to the output buffer, excl null terminator. +*/ +int +ldap_x_wcs_to_utf8s ( char *utf8str, const wchar_t *wcstr, size_t count ) +{ + int len = 0; + int n; + char *p = utf8str; + wchar_t empty = 0; /* To avoid use of L"" construct */ + + if (wcstr == NULL) /* Treat input ptr NULL as an empty string */ + wcstr = ∅ + + if (utf8str == NULL) /* Just compute size of output, excl null */ + { + while (*wcstr) + { + /* Get UTF-8 size of next wide char */ + n = ldap_x_wc_to_utf8( NULL, *wcstr++, LDAP_MAX_UTF8_LEN); + if (n == -1) + return -1; + len += n; + } + + return len; + } + + + /* Do the actual conversion. */ + + n = 1; /* In case of empty wcstr */ + while (*wcstr) + { + n = ldap_x_wc_to_utf8( p, *wcstr++, count); + + if (n <= 0) /* If encoding error (-1) or won't fit (0), quit */ + break; + + p += n; + count -= n; /* Space left in output buffer */ + } + + /* If not enough room for last character, pad remainder with null + so that return value = original count, indicating buffer full. */ + if (n == 0) + { + while (count--) + *p++ = 0; + } + + /* Add a null terminator if there's room. */ + else if (count) + *p = 0; + + if (n == -1) /* Conversion encountered invalid wide char. */ + return -1; + + /* Return the number of bytes written to output buffer, excl null. */ + return (p - utf8str); +} + +#ifdef ANDROID +int wctomb(char *s, wchar_t wc) { return wcrtomb(s,wc,NULL); } +int mbtowc(wchar_t *pwc, const char *s, size_t n) { return mbrtowc(pwc, s, n, NULL); } +#endif + +/*----------------------------------------------------------------------------- + Convert a UTF-8 character to a MultiByte character. + Return the size of the converted character in bytes. +*/ +int +ldap_x_utf8_to_mb ( char *mbchar, const char *utf8char, + int (*f_wctomb)(char *mbchar, wchar_t wchar) ) +{ + wchar_t wchar; + int n; + char tmp[6]; /* Large enough for biggest multibyte char */ + + if (f_wctomb == NULL) /* If no conversion function was given... */ + f_wctomb = wctomb; /* use the local ANSI C function */ + + /* First convert UTF-8 char to a wide char */ + n = ldap_x_utf8_to_wc( &wchar, utf8char); + + if (n == -1) + return -1; /* Invalid UTF-8 character */ + + if (mbchar == NULL) + n = f_wctomb( tmp, wchar ); + else + n = f_wctomb( mbchar, wchar); + + return n; +} + +/*----------------------------------------------------------------------------- + Convert a UTF-8 string to a MultiByte string. + No more than 'count' bytes will be written to the output buffer. + Return the size of the converted string in bytes, excl null terminator. +*/ +int +ldap_x_utf8s_to_mbs ( char *mbstr, const char *utf8str, size_t count, + size_t (*f_wcstombs)(char *mbstr, const wchar_t *wcstr, size_t count) ) +{ + wchar_t *wcs; + size_t wcsize; + int n; + + if (f_wcstombs == NULL) /* If no conversion function was given... */ + f_wcstombs = wcstombs; /* use the local ANSI C function */ + + if (utf8str == NULL || *utf8str == 0) /* NULL or empty input string */ + { + if (mbstr) + *mbstr = 0; + return 0; + } + +/* Allocate memory for the maximum size wchar string that we could get. */ + wcsize = strlen(utf8str) + 1; + wcs = (wchar_t *)LDAP_MALLOC(wcsize * sizeof(wchar_t)); + if (wcs == NULL) + return -1; /* Memory allocation failure. */ + + /* First convert the UTF-8 string to a wide char string */ + n = ldap_x_utf8s_to_wcs( wcs, utf8str, wcsize); + + /* Then convert wide char string to multi-byte string */ + if (n != -1) + { + n = f_wcstombs(mbstr, wcs, count); + } + + LDAP_FREE(wcs); + + return n; +} + +/*----------------------------------------------------------------------------- + Convert a MultiByte character to a UTF-8 character. + 'mbsize' indicates the number of bytes of 'mbchar' to check. + Returns the number of bytes written to the output character. +*/ +int +ldap_x_mb_to_utf8 ( char *utf8char, const char *mbchar, size_t mbsize, + int (*f_mbtowc)(wchar_t *wchar, const char *mbchar, size_t count) ) +{ + wchar_t wchar; + int n; + + if (f_mbtowc == NULL) /* If no conversion function was given... */ + f_mbtowc = mbtowc; /* use the local ANSI C function */ + + if (mbsize == 0) /* 0 is not valid. */ + return -1; + + if (mbchar == NULL || *mbchar == 0) + { + if (utf8char) + *utf8char = 0; + return 1; + } + + /* First convert the MB char to a Wide Char */ + n = f_mbtowc( &wchar, mbchar, mbsize); + + if (n == -1) + return -1; + + /* Convert the Wide Char to a UTF-8 character. */ + n = ldap_x_wc_to_utf8( utf8char, wchar, LDAP_MAX_UTF8_LEN); + + return n; +} + + +/*----------------------------------------------------------------------------- + Convert a MultiByte string to a UTF-8 string. + No more than 'count' bytes will be written to the output buffer. + Return the size of the converted string in bytes, excl null terminator. +*/ +int +ldap_x_mbs_to_utf8s ( char *utf8str, const char *mbstr, size_t count, + size_t (*f_mbstowcs)(wchar_t *wcstr, const char *mbstr, size_t count) ) +{ + wchar_t *wcs; + int n; + size_t wcsize; + + if (mbstr == NULL) /* Treat NULL input string as an empty string */ + mbstr = ""; + + if (f_mbstowcs == NULL) /* If no conversion function was given... */ + f_mbstowcs = mbstowcs; /* use the local ANSI C function */ + + /* Allocate memory for the maximum size wchar string that we could get. */ + wcsize = strlen(mbstr) + 1; + wcs = (wchar_t *)LDAP_MALLOC( wcsize * sizeof(wchar_t) ); + if (wcs == NULL) + return -1; + + /* First convert multi-byte string to a wide char string */ + n = f_mbstowcs(wcs, mbstr, wcsize); + + /* Convert wide char string to UTF-8 string */ + if (n != -1) + { + n = ldap_x_wcs_to_utf8s( utf8str, wcs, count); + } + + LDAP_FREE(wcs); + + return n; +} + +#endif /* SIZEOF_WCHAR_T >= 4 */ diff --git a/libraries/libldap/utf-8.c b/libraries/libldap/utf-8.c new file mode 100644 index 0000000..7a3431a --- /dev/null +++ b/libraries/libldap/utf-8.c @@ -0,0 +1,562 @@ +/* utf-8.c -- Basic UTF-8 routines */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Basic UTF-8 routines + * + * These routines are "dumb". Though they understand UTF-8, + * they don't grok Unicode. That is, they can push bits, + * but don't have a clue what the bits represent. That's + * good enough for use with the LDAP Client SDK. + * + * These routines are not optimized. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap_utf8.h" + +#include "ldap-int.h" +#include "ldap_defaults.h" + +/* + * return the number of bytes required to hold the + * NULL-terminated UTF-8 string NOT INCLUDING the + * termination. + */ +ber_len_t ldap_utf8_bytes( const char * p ) +{ + ber_len_t bytes; + + for( bytes=0; p[bytes]; bytes++ ) { + /* EMPTY */ ; + } + + return bytes; +} + +ber_len_t ldap_utf8_chars( const char * p ) +{ + /* could be optimized and could check for invalid sequences */ + ber_len_t chars=0; + + for( ; *p ; LDAP_UTF8_INCR(p) ) { + chars++; + } + + return chars; +} + +/* return offset to next character */ +int ldap_utf8_offset( const char * p ) +{ + return LDAP_UTF8_NEXT(p) - p; +} + +/* + * Returns length indicated by first byte. + */ +const char ldap_utf8_lentab[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 0, 0 }; + +int ldap_utf8_charlen( const char * p ) +{ + if (!(*p & 0x80)) + return 1; + + return ldap_utf8_lentab[*(const unsigned char *)p ^ 0x80]; +} + +/* + * Make sure the UTF-8 char used the shortest possible encoding + * returns charlen if valid, 0 if not. + * + * Here are the valid UTF-8 encodings, taken from RFC 2279 page 4. + * The table is slightly modified from that of the RFC. + * + * UCS-4 range (hex) UTF-8 sequence (binary) + * 0000 0000-0000 007F 0....... + * 0000 0080-0000 07FF 110++++. 10...... + * 0000 0800-0000 FFFF 1110++++ 10+..... 10...... + * 0001 0000-001F FFFF 11110+++ 10++.... 10...... 10...... + * 0020 0000-03FF FFFF 111110++ 10+++... 10...... 10...... 10...... + * 0400 0000-7FFF FFFF 1111110+ 10++++.. 10...... 10...... 10...... 10...... + * + * The '.' bits are "don't cares". When validating a UTF-8 sequence, + * at least one of the '+' bits must be set, otherwise the character + * should have been encoded in fewer octets. Note that in the two-octet + * case, only the first octet needs to be validated, and this is done + * in the ldap_utf8_lentab[] above. + */ + +/* mask of required bits in second octet */ +#undef c +#define c const char +c ldap_utf8_mintab[] = { + (c)0x20, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80, + (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80, + (c)0x30, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80, + (c)0x38, (c)0x80, (c)0x80, (c)0x80, (c)0x3c, (c)0x80, (c)0x00, (c)0x00 }; +#undef c + +int ldap_utf8_charlen2( const char * p ) +{ + int i = LDAP_UTF8_CHARLEN( p ); + + if ( i > 2 ) { + if ( !( ldap_utf8_mintab[*p & 0x1f] & p[1] ) ) + i = 0; + } + return i; +} + +/* conv UTF-8 to UCS-4, useful for comparisons */ +ldap_ucs4_t ldap_x_utf8_to_ucs4( const char * p ) +{ + const unsigned char *c = (const unsigned char *) p; + ldap_ucs4_t ch; + int len, i; + static unsigned char mask[] = { + 0, 0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01 }; + + len = LDAP_UTF8_CHARLEN2(p, len); + + if( len == 0 ) return LDAP_UCS4_INVALID; + + ch = c[0] & mask[len]; + + for(i=1; i < len; i++) { + if ((c[i] & 0xc0) != 0x80) { + return LDAP_UCS4_INVALID; + } + + ch <<= 6; + ch |= c[i] & 0x3f; + } + + return ch; +} + +/* conv UCS-4 to UTF-8, not used */ +int ldap_x_ucs4_to_utf8( ldap_ucs4_t c, char *buf ) +{ + int len=0; + unsigned char* p = (unsigned char *) buf; + + /* not a valid Unicode character */ + if ( c < 0 ) return 0; + + /* Just return length, don't convert */ + if(buf == NULL) { + if( c < 0x80 ) return 1; + else if( c < 0x800 ) return 2; + else if( c < 0x10000 ) return 3; + else if( c < 0x200000 ) return 4; + else if( c < 0x4000000 ) return 5; + else return 6; + } + + if( c < 0x80 ) { + p[len++] = c; + + } else if( c < 0x800 ) { + p[len++] = 0xc0 | ( c >> 6 ); + p[len++] = 0x80 | ( c & 0x3f ); + + } else if( c < 0x10000 ) { + p[len++] = 0xe0 | ( c >> 12 ); + p[len++] = 0x80 | ( (c >> 6) & 0x3f ); + p[len++] = 0x80 | ( c & 0x3f ); + + } else if( c < 0x200000 ) { + p[len++] = 0xf0 | ( c >> 18 ); + p[len++] = 0x80 | ( (c >> 12) & 0x3f ); + p[len++] = 0x80 | ( (c >> 6) & 0x3f ); + p[len++] = 0x80 | ( c & 0x3f ); + + } else if( c < 0x4000000 ) { + p[len++] = 0xf8 | ( c >> 24 ); + p[len++] = 0x80 | ( (c >> 18) & 0x3f ); + p[len++] = 0x80 | ( (c >> 12) & 0x3f ); + p[len++] = 0x80 | ( (c >> 6) & 0x3f ); + p[len++] = 0x80 | ( c & 0x3f ); + + } else /* if( c < 0x80000000 ) */ { + p[len++] = 0xfc | ( c >> 30 ); + p[len++] = 0x80 | ( (c >> 24) & 0x3f ); + p[len++] = 0x80 | ( (c >> 18) & 0x3f ); + p[len++] = 0x80 | ( (c >> 12) & 0x3f ); + p[len++] = 0x80 | ( (c >> 6) & 0x3f ); + p[len++] = 0x80 | ( c & 0x3f ); + } + + return len; +} + +#define LDAP_UCS_UTF8LEN(c) \ + c < 0 ? 0 : (c < 0x80 ? 1 : (c < 0x800 ? 2 : (c < 0x10000 ? 3 : \ + (c < 0x200000 ? 4 : (c < 0x4000000 ? 5 : 6))))) + +/* Convert a string to UTF-8 format. The input string is expected to + * have characters of 1, 2, or 4 octets (in network byte order) + * corresponding to the ASN.1 T61STRING, BMPSTRING, and UNIVERSALSTRING + * types respectively. (Here T61STRING just means that there is one + * octet per character and characters may use the high bit of the octet. + * The characters are assumed to use ISO mappings, no provision is made + * for converting from T.61 coding rules to Unicode.) + */ + +int +ldap_ucs_to_utf8s( struct berval *ucs, int csize, struct berval *utf8s ) +{ + unsigned char *in, *end; + char *ptr; + ldap_ucs4_t u; + int i, l = 0; + + utf8s->bv_val = NULL; + utf8s->bv_len = 0; + + in = (unsigned char *)ucs->bv_val; + + /* Make sure we stop at an even multiple of csize */ + end = in + ( ucs->bv_len & ~(csize-1) ); + + for (; in < end; ) { + u = *in++; + if (csize > 1) { + u <<= 8; + u |= *in++; + } + if (csize > 2) { + u <<= 8; + u |= *in++; + u <<= 8; + u |= *in++; + } + i = LDAP_UCS_UTF8LEN(u); + if (i == 0) + return LDAP_INVALID_SYNTAX; + l += i; + } + + utf8s->bv_val = LDAP_MALLOC( l+1 ); + if (utf8s->bv_val == NULL) + return LDAP_NO_MEMORY; + utf8s->bv_len = l; + + ptr = utf8s->bv_val; + for (in = (unsigned char *)ucs->bv_val; in < end; ) { + u = *in++; + if (csize > 1) { + u <<= 8; + u |= *in++; + } + if (csize > 2) { + u <<= 8; + u |= *in++; + u <<= 8; + u |= *in++; + } + ptr += ldap_x_ucs4_to_utf8(u, ptr); + } + *ptr = '\0'; + return LDAP_SUCCESS; +} + +/* + * Advance to the next UTF-8 character + * + * Ignores length of multibyte character, instead rely on + * continuation markers to find start of next character. + * This allows for "resyncing" of when invalid characters + * are provided provided the start of the next character + * is appears within the 6 bytes examined. + */ +char* ldap_utf8_next( const char * p ) +{ + int i; + const unsigned char *u = (const unsigned char *) p; + + if( LDAP_UTF8_ISASCII(u) ) { + return (char *) &p[1]; + } + + for( i=1; i<6; i++ ) { + if ( ( u[i] & 0xc0 ) != 0x80 ) { + return (char *) &p[i]; + } + } + + return (char *) &p[i]; +} + +/* + * Advance to the previous UTF-8 character + * + * Ignores length of multibyte character, instead rely on + * continuation markers to find start of next character. + * This allows for "resyncing" of when invalid characters + * are provided provided the start of the next character + * is appears within the 6 bytes examined. + */ +char* ldap_utf8_prev( const char * p ) +{ + int i; + const unsigned char *u = (const unsigned char *) p; + + for( i=-1; i>-6 ; i-- ) { + if ( ( u[i] & 0xc0 ) != 0x80 ) { + return (char *) &p[i]; + } + } + + return (char *) &p[i]; +} + +/* + * Copy one UTF-8 character from src to dst returning + * number of bytes copied. + * + * Ignores length of multibyte character, instead rely on + * continuation markers to find start of next character. + * This allows for "resyncing" of when invalid characters + * are provided provided the start of the next character + * is appears within the 6 bytes examined. + */ +int ldap_utf8_copy( char* dst, const char *src ) +{ + int i; + const unsigned char *u = (const unsigned char *) src; + + dst[0] = src[0]; + + if( LDAP_UTF8_ISASCII(u) ) { + return 1; + } + + for( i=1; i<6; i++ ) { + if ( ( u[i] & 0xc0 ) != 0x80 ) { + return i; + } + dst[i] = src[i]; + } + + return i; +} + +#ifndef UTF8_ALPHA_CTYPE +/* + * UTF-8 ctype routines + * Only deals with characters < 0x80 (ie: US-ASCII) + */ + +int ldap_utf8_isascii( const char * p ) +{ + unsigned c = * (const unsigned char *) p; + return LDAP_ASCII(c); +} + +int ldap_utf8_isdigit( const char * p ) +{ + unsigned c = * (const unsigned char *) p; + + if(!LDAP_ASCII(c)) return 0; + + return LDAP_DIGIT( c ); +} + +int ldap_utf8_isxdigit( const char * p ) +{ + unsigned c = * (const unsigned char *) p; + + if(!LDAP_ASCII(c)) return 0; + + return LDAP_HEX(c); +} + +int ldap_utf8_isspace( const char * p ) +{ + unsigned c = * (const unsigned char *) p; + + if(!LDAP_ASCII(c)) return 0; + + switch(c) { + case ' ': + case '\t': + case '\n': + case '\r': + case '\v': + case '\f': + return 1; + } + + return 0; +} + +/* + * These are not needed by the C SDK and are + * not "good enough" for general use. + */ +int ldap_utf8_isalpha( const char * p ) +{ + unsigned c = * (const unsigned char *) p; + + if(!LDAP_ASCII(c)) return 0; + + return LDAP_ALPHA(c); +} + +int ldap_utf8_isalnum( const char * p ) +{ + unsigned c = * (const unsigned char *) p; + + if(!LDAP_ASCII(c)) return 0; + + return LDAP_ALNUM(c); +} + +int ldap_utf8_islower( const char * p ) +{ + unsigned c = * (const unsigned char *) p; + + if(!LDAP_ASCII(c)) return 0; + + return LDAP_LOWER(c); +} + +int ldap_utf8_isupper( const char * p ) +{ + unsigned c = * (const unsigned char *) p; + + if(!LDAP_ASCII(c)) return 0; + + return LDAP_UPPER(c); +} +#endif + + +/* + * UTF-8 string routines + */ + +/* like strchr() */ +char * (ldap_utf8_strchr)( const char *str, const char *chr ) +{ + for( ; *str != '\0'; LDAP_UTF8_INCR(str) ) { + if( ldap_x_utf8_to_ucs4( str ) == ldap_x_utf8_to_ucs4( chr ) ) { + return (char *) str; + } + } + + return NULL; +} + +/* like strcspn() but returns number of bytes, not characters */ +ber_len_t (ldap_utf8_strcspn)( const char *str, const char *set ) +{ + const char *cstr; + const char *cset; + + for( cstr = str; *cstr != '\0'; LDAP_UTF8_INCR(cstr) ) { + for( cset = set; *cset != '\0'; LDAP_UTF8_INCR(cset) ) { + if( ldap_x_utf8_to_ucs4( cstr ) == ldap_x_utf8_to_ucs4( cset ) ) { + return cstr - str; + } + } + } + + return cstr - str; +} + +/* like strspn() but returns number of bytes, not characters */ +ber_len_t (ldap_utf8_strspn)( const char *str, const char *set ) +{ + const char *cstr; + const char *cset; + + for( cstr = str; *cstr != '\0'; LDAP_UTF8_INCR(cstr) ) { + for( cset = set; ; LDAP_UTF8_INCR(cset) ) { + if( *cset == '\0' ) { + return cstr - str; + } + + if( ldap_x_utf8_to_ucs4( cstr ) == ldap_x_utf8_to_ucs4( cset ) ) { + break; + } + } + } + + return cstr - str; +} + +/* like strpbrk(), replaces strchr() as well */ +char *(ldap_utf8_strpbrk)( const char *str, const char *set ) +{ + for( ; *str != '\0'; LDAP_UTF8_INCR(str) ) { + const char *cset; + + for( cset = set; *cset != '\0'; LDAP_UTF8_INCR(cset) ) { + if( ldap_x_utf8_to_ucs4( str ) == ldap_x_utf8_to_ucs4( cset ) ) { + return (char *) str; + } + } + } + + return NULL; +} + +/* like strtok_r(), not strtok() */ +char *(ldap_utf8_strtok)(char *str, const char *sep, char **last) +{ + char *begin; + char *end; + + if( last == NULL ) return NULL; + + begin = str ? str : *last; + + begin += ldap_utf8_strspn( begin, sep ); + + if( *begin == '\0' ) { + *last = NULL; + return NULL; + } + + end = &begin[ ldap_utf8_strcspn( begin, sep ) ]; + + if( *end != '\0' ) { + char *next = LDAP_UTF8_NEXT( end ); + *end = '\0'; + end = next; + } + + *last = end; + return begin; +} diff --git a/libraries/libldap/util-int.c b/libraries/libldap/util-int.c new file mode 100644 index 0000000..d4c0dfa --- /dev/null +++ b/libraries/libldap/util-int.c @@ -0,0 +1,923 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * Portions Copyright 1998 A. Hartgers. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: + * This work was initially developed by Bart Hartgers for inclusion in + * OpenLDAP Software. + */ + +/* + * util-int.c Various functions to replace missing threadsafe ones. + * Without the real *_r funcs, things will + * work, but might not be threadsafe. + */ + +#include "portable.h" + +#include <ac/stdlib.h> + +#include <ac/errno.h> +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> +#include <ac/unistd.h> + +#include "ldap-int.h" + +#ifndef h_errno +/* newer systems declare this in <netdb.h> for you, older ones don't. + * harmless to declare it again (unless defined by a macro). + */ +extern int h_errno; +#endif + +#ifdef HAVE_HSTRERROR +# define HSTRERROR(e) hstrerror(e) +#else +# define HSTRERROR(e) hp_strerror(e) +#endif + +#ifndef LDAP_R_COMPILE +# undef HAVE_REENTRANT_FUNCTIONS +# undef HAVE_CTIME_R +# undef HAVE_GETHOSTBYNAME_R +# undef HAVE_GETHOSTBYADDR_R + +#else +# include <ldap_pvt_thread.h> + ldap_pvt_thread_mutex_t ldap_int_resolv_mutex; + ldap_pvt_thread_mutex_t ldap_int_hostname_mutex; + static ldap_pvt_thread_mutex_t ldap_int_gettime_mutex; + +# if (defined( HAVE_CTIME_R ) || defined( HAVE_REENTRANT_FUNCTIONS)) \ + && defined( CTIME_R_NARGS ) +# define USE_CTIME_R +# else + static ldap_pvt_thread_mutex_t ldap_int_ctime_mutex; +# endif + +/* USE_GMTIME_R and USE_LOCALTIME_R defined in ldap_pvt.h */ + +#ifdef LDAP_DEVEL + /* to be released with 2.5 */ +#if !defined( USE_GMTIME_R ) || !defined( USE_LOCALTIME_R ) + /* we use the same mutex for gmtime(3) and localtime(3) + * because implementations may use the same buffer + * for both functions */ + static ldap_pvt_thread_mutex_t ldap_int_gmtime_mutex; +#endif +#else /* ! LDAP_DEVEL */ + ldap_pvt_thread_mutex_t ldap_int_gmtime_mutex; +#endif /* ! LDAP_DEVEL */ + +# if defined(HAVE_GETHOSTBYNAME_R) && \ + (GETHOSTBYNAME_R_NARGS < 5) || (6 < GETHOSTBYNAME_R_NARGS) + /* Don't know how to handle this version, pretend it's not there */ +# undef HAVE_GETHOSTBYNAME_R +# endif +# if defined(HAVE_GETHOSTBYADDR_R) && \ + (GETHOSTBYADDR_R_NARGS < 7) || (8 < GETHOSTBYADDR_R_NARGS) + /* Don't know how to handle this version, pretend it's not there */ +# undef HAVE_GETHOSTBYADDR_R +# endif +#endif /* LDAP_R_COMPILE */ + +char *ldap_pvt_ctime( const time_t *tp, char *buf ) +{ +#ifdef USE_CTIME_R +# if (CTIME_R_NARGS > 3) || (CTIME_R_NARGS < 2) +# error "CTIME_R_NARGS should be 2 or 3" +# elif CTIME_R_NARGS > 2 && defined(CTIME_R_RETURNS_INT) + return( ctime_r(tp,buf,26) < 0 ? 0 : buf ); +# elif CTIME_R_NARGS > 2 + return ctime_r(tp,buf,26); +# else + return ctime_r(tp,buf); +# endif + +#else + + LDAP_MUTEX_LOCK( &ldap_int_ctime_mutex ); + AC_MEMCPY( buf, ctime(tp), 26 ); + LDAP_MUTEX_UNLOCK( &ldap_int_ctime_mutex ); + + return buf; +#endif +} + +#if !defined( USE_GMTIME_R ) || !defined( USE_LOCALTIME_R ) +int +ldap_pvt_gmtime_lock( void ) +{ +# ifndef LDAP_R_COMPILE + return 0; +# else /* LDAP_R_COMPILE */ + return ldap_pvt_thread_mutex_lock( &ldap_int_gmtime_mutex ); +# endif /* LDAP_R_COMPILE */ +} + +int +ldap_pvt_gmtime_unlock( void ) +{ +# ifndef LDAP_R_COMPILE + return 0; +# else /* LDAP_R_COMPILE */ + return ldap_pvt_thread_mutex_unlock( &ldap_int_gmtime_mutex ); +# endif /* LDAP_R_COMPILE */ +} +#endif /* !USE_GMTIME_R || !USE_LOCALTIME_R */ + +#ifndef USE_GMTIME_R +struct tm * +ldap_pvt_gmtime( const time_t *timep, struct tm *result ) +{ + struct tm *tm_ptr; + + LDAP_MUTEX_LOCK( &ldap_int_gmtime_mutex ); + tm_ptr = gmtime( timep ); + if ( tm_ptr == NULL ) { + result = NULL; + + } else { + *result = *tm_ptr; + } + LDAP_MUTEX_UNLOCK( &ldap_int_gmtime_mutex ); + + return result; +} +#endif /* !USE_GMTIME_R */ + +#ifndef USE_LOCALTIME_R +struct tm * +ldap_pvt_localtime( const time_t *timep, struct tm *result ) +{ + struct tm *tm_ptr; + + LDAP_MUTEX_LOCK( &ldap_int_gmtime_mutex ); + tm_ptr = localtime( timep ); + if ( tm_ptr == NULL ) { + result = NULL; + + } else { + *result = *tm_ptr; + } + LDAP_MUTEX_UNLOCK( &ldap_int_gmtime_mutex ); + + return result; +} +#endif /* !USE_LOCALTIME_R */ + +static int _ldap_pvt_gt_subs; + +#ifdef _WIN32 +/* Windows SYSTEMTIME only has 10 millisecond resolution, so we + * also need to use a high resolution timer to get microseconds. + * This is pretty clunky. + */ +static LARGE_INTEGER _ldap_pvt_gt_freq; +static LARGE_INTEGER _ldap_pvt_gt_prev; +static int _ldap_pvt_gt_offset; + +#define SEC_TO_UNIX_EPOCH 11644473600LL +#define TICKS_PER_SECOND 10000000 + +static int +ldap_pvt_gettimeusec(int *sec) +{ + LARGE_INTEGER count; + + QueryPerformanceCounter( &count ); + + /* It shouldn't ever go backwards, but multiple CPUs might + * be able to hit in the same tick. + */ + LDAP_MUTEX_LOCK( &ldap_int_gettime_mutex ); + /* We assume Windows has at least a vague idea of + * when a second begins. So we align our microsecond count + * with the Windows millisecond count using this offset. + * We retain the submillisecond portion of our own count. + * + * Note - this also assumes that the relationship between + * the PerformanceCounter and SystemTime stays constant; + * that assumption breaks if the SystemTime is adjusted by + * an external action. + */ + if ( !_ldap_pvt_gt_freq.QuadPart ) { + LARGE_INTEGER c2; + ULARGE_INTEGER ut; + FILETIME ft0, ft1; + long long t; + int usec; + + /* Initialize our offset */ + QueryPerformanceFrequency( &_ldap_pvt_gt_freq ); + + /* Wait for a tick of the system time: 10-15ms */ + GetSystemTimeAsFileTime( &ft0 ); + do { + GetSystemTimeAsFileTime( &ft1 ); + } while ( ft1.dwLowDateTime == ft0.dwLowDateTime ); + + ut.LowPart = ft1.dwLowDateTime; + ut.HighPart = ft1.dwHighDateTime; + QueryPerformanceCounter( &c2 ); + + /* get second and fraction portion of counter */ + t = c2.QuadPart % (_ldap_pvt_gt_freq.QuadPart*10); + + /* convert to microseconds */ + t *= 1000000; + usec = t / _ldap_pvt_gt_freq.QuadPart; + + ut.QuadPart /= 10; + ut.QuadPart %= 10000000; + _ldap_pvt_gt_offset = usec - ut.QuadPart; + count = c2; + } + if ( count.QuadPart <= _ldap_pvt_gt_prev.QuadPart ) { + _ldap_pvt_gt_subs++; + } else { + _ldap_pvt_gt_subs = 0; + _ldap_pvt_gt_prev = count; + } + LDAP_MUTEX_UNLOCK( &ldap_int_gettime_mutex ); + + /* convert to microseconds */ + count.QuadPart %= _ldap_pvt_gt_freq.QuadPart*10; + count.QuadPart *= 1000000; + count.QuadPart /= _ldap_pvt_gt_freq.QuadPart; + count.QuadPart -= _ldap_pvt_gt_offset; + + /* We've extracted the 1s and microseconds. + * The 1sec digit is used to detect wraparound in microsecnds. + */ + if (count.QuadPart < 0) + count.QuadPart += 10000000; + else if (count.QuadPart >= 10000000) + count.QuadPart -= 10000000; + + *sec = count.QuadPart / 1000000; + return count.QuadPart % 1000000; +} + + +/* emulate POSIX gettimeofday */ +int +ldap_pvt_gettimeofday( struct timeval *tv, void *unused ) +{ + FILETIME ft; + ULARGE_INTEGER ut; + int sec, sec0; + + GetSystemTimeAsFileTime( &ft ); + ut.LowPart = ft.dwLowDateTime; + ut.HighPart = ft.dwHighDateTime; + + /* convert to usec */ + ut.QuadPart /= (TICKS_PER_SECOND / 1000000); + + tv->tv_usec = ldap_pvt_gettimeusec(&sec); + tv->tv_sec = ut.QuadPart / 1000000 - SEC_TO_UNIX_EPOCH; + + /* check for carry from microseconds */ + sec0 = tv->tv_sec % 10; + if (sec0 < sec || (sec0 == 9 && !sec)) + tv->tv_sec++; + + return 0; +} + +/* return a broken out time, with microseconds + */ +void +ldap_pvt_gettime( struct lutil_tm *tm ) +{ + SYSTEMTIME st; + int sec, sec0; + static const char daysPerMonth[] = { + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + + GetSystemTime( &st ); + tm->tm_usec = ldap_pvt_gettimeusec(&sec); + tm->tm_usub = _ldap_pvt_gt_subs; + + /* any difference larger than microseconds is + * already reflected in st + */ + tm->tm_sec = st.wSecond; + tm->tm_min = st.wMinute; + tm->tm_hour = st.wHour; + tm->tm_mday = st.wDay; + tm->tm_mon = st.wMonth - 1; + tm->tm_year = st.wYear - 1900; + + /* check for carry from microseconds */ + sec0 = tm->tm_sec % 10; + if (sec0 < sec || (sec0 == 9 && !sec)) { + tm->tm_sec++; + /* FIXME: we don't handle leap seconds */ + if (tm->tm_sec > 59) { + tm->tm_sec = 0; + tm->tm_min++; + if (tm->tm_min > 59) { + tm->tm_min = 0; + tm->tm_hour++; + if (tm->tm_hour > 23) { + int days = daysPerMonth[tm->tm_mon]; + tm->tm_hour = 0; + tm->tm_mday++; + + /* if it's February of a leap year, + * add 1 day to this month + */ + if (tm->tm_mon == 1 && + ((!(st.wYear % 4) && (st.wYear % 100)) || + !(st.wYear % 400))) + days++; + + if (tm->tm_mday > days) { + tm->tm_mday = 1; + tm->tm_mon++; + if (tm->tm_mon > 11) { + tm->tm_mon = 0; + tm->tm_year++; + } + } + } + } + } + } +} +#else + +static struct timeval _ldap_pvt_gt_prevTv; + +void +ldap_pvt_gettime( struct lutil_tm *ltm ) +{ + struct timeval tv; + + struct tm tm; + time_t t; + + gettimeofday( &tv, NULL ); + t = tv.tv_sec; + + LDAP_MUTEX_LOCK( &ldap_int_gettime_mutex ); + if ( tv.tv_sec < _ldap_pvt_gt_prevTv.tv_sec + || ( tv.tv_sec == _ldap_pvt_gt_prevTv.tv_sec + && tv.tv_usec <= _ldap_pvt_gt_prevTv.tv_usec )) { + _ldap_pvt_gt_subs++; + } else { + _ldap_pvt_gt_subs = 0; + _ldap_pvt_gt_prevTv = tv; + } + LDAP_MUTEX_UNLOCK( &ldap_int_gettime_mutex ); + + ltm->tm_usub = _ldap_pvt_gt_subs; + + ldap_pvt_gmtime( &t, &tm ); + + ltm->tm_sec = tm.tm_sec; + ltm->tm_min = tm.tm_min; + ltm->tm_hour = tm.tm_hour; + ltm->tm_mday = tm.tm_mday; + ltm->tm_mon = tm.tm_mon; + ltm->tm_year = tm.tm_year; + ltm->tm_usec = tv.tv_usec; +} +#endif + +size_t +ldap_pvt_csnstr(char *buf, size_t len, unsigned int replica, unsigned int mod) +{ + struct lutil_tm tm; + int n; + + ldap_pvt_gettime( &tm ); + + n = snprintf( buf, len, + "%4d%02d%02d%02d%02d%02d.%06dZ#%06x#%03x#%06x", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, + tm.tm_min, tm.tm_sec, tm.tm_usec, tm.tm_usub, replica, mod ); + + if( n < 0 ) return 0; + return ( (size_t) n < len ) ? n : 0; +} + +#define BUFSTART (1024-32) +#define BUFMAX (32*1024-32) + +#if defined(LDAP_R_COMPILE) +static char *safe_realloc( char **buf, int len ); + +#if !(defined(HAVE_GETHOSTBYNAME_R) && defined(HAVE_GETHOSTBYADDR_R)) +static int copy_hostent( struct hostent *res, + char **buf, struct hostent * src ); +#endif +#endif + +int ldap_pvt_gethostbyname_a( + const char *name, + struct hostent *resbuf, + char **buf, + struct hostent **result, + int *herrno_ptr ) +{ +#if defined( HAVE_GETHOSTBYNAME_R ) + +# define NEED_SAFE_REALLOC 1 + int r=-1; + int buflen=BUFSTART; + *buf = NULL; + for(;buflen<BUFMAX;) { + if (safe_realloc( buf, buflen )==NULL) + return r; + +#if (GETHOSTBYNAME_R_NARGS < 6) + *result=gethostbyname_r( name, resbuf, *buf, buflen, herrno_ptr ); + r = (*result == NULL) ? -1 : 0; +#else + r = gethostbyname_r( name, resbuf, *buf, + buflen, result, herrno_ptr ); +#endif + + Debug( LDAP_DEBUG_TRACE, "ldap_pvt_gethostbyname_a: host=%s, r=%d\n", + name, r, 0 ); + +#ifdef NETDB_INTERNAL + if ((r<0) && + (*herrno_ptr==NETDB_INTERNAL) && + (errno==ERANGE)) + { + buflen*=2; + continue; + } +#endif + return r; + } + return -1; +#elif defined( LDAP_R_COMPILE ) +# define NEED_COPY_HOSTENT + struct hostent *he; + int retval; + *buf = NULL; + + LDAP_MUTEX_LOCK( &ldap_int_resolv_mutex ); + + he = gethostbyname( name ); + + if (he==NULL) { + *herrno_ptr = h_errno; + retval = -1; + } else if (copy_hostent( resbuf, buf, he )<0) { + *herrno_ptr = -1; + retval = -1; + } else { + *result = resbuf; + retval = 0; + } + + LDAP_MUTEX_UNLOCK( &ldap_int_resolv_mutex ); + + return retval; +#else + *buf = NULL; + *result = gethostbyname( name ); + + if (*result!=NULL) { + return 0; + } + + *herrno_ptr = h_errno; + + return -1; +#endif +} + +#if !defined( HAVE_GETNAMEINFO ) && !defined( HAVE_HSTRERROR ) +static const char * +hp_strerror( int err ) +{ + switch (err) { + case HOST_NOT_FOUND: return _("Host not found (authoritative)"); + case TRY_AGAIN: return _("Host not found (server fail?)"); + case NO_RECOVERY: return _("Non-recoverable failure"); + case NO_DATA: return _("No data of requested type"); +#ifdef NETDB_INTERNAL + case NETDB_INTERNAL: return STRERROR( errno ); +#endif + } + return _("Unknown resolver error"); +} +#endif + +int ldap_pvt_get_hname( + const struct sockaddr *sa, + int len, + char *name, + int namelen, + char **err ) +{ + int rc; +#if defined( HAVE_GETNAMEINFO ) + + LDAP_MUTEX_LOCK( &ldap_int_resolv_mutex ); + rc = getnameinfo( sa, len, name, namelen, NULL, 0, 0 ); + LDAP_MUTEX_UNLOCK( &ldap_int_resolv_mutex ); + if ( rc ) *err = (char *)AC_GAI_STRERROR( rc ); + return rc; + +#else /* !HAVE_GETNAMEINFO */ + char *addr; + int alen; + struct hostent *hp = NULL; +#ifdef HAVE_GETHOSTBYADDR_R + struct hostent hb; + int buflen=BUFSTART, h_errno; + char *buf=NULL; +#endif + +#ifdef LDAP_PF_INET6 + if (sa->sa_family == AF_INET6) { + struct sockaddr_in6 *sin = (struct sockaddr_in6 *)sa; + addr = (char *)&sin->sin6_addr; + alen = sizeof(sin->sin6_addr); + } else +#endif + if (sa->sa_family == AF_INET) { + struct sockaddr_in *sin = (struct sockaddr_in *)sa; + addr = (char *)&sin->sin_addr; + alen = sizeof(sin->sin_addr); + } else { + rc = NO_RECOVERY; + *err = (char *)HSTRERROR( rc ); + return rc; + } +#if defined( HAVE_GETHOSTBYADDR_R ) + for(;buflen<BUFMAX;) { + if (safe_realloc( &buf, buflen )==NULL) { + *err = (char *)STRERROR( ENOMEM ); + return ENOMEM; + } +#if (GETHOSTBYADDR_R_NARGS < 8) + hp=gethostbyaddr_r( addr, alen, sa->sa_family, + &hb, buf, buflen, &h_errno ); + rc = (hp == NULL) ? -1 : 0; +#else + rc = gethostbyaddr_r( addr, alen, sa->sa_family, + &hb, buf, buflen, + &hp, &h_errno ); +#endif +#ifdef NETDB_INTERNAL + if ((rc<0) && + (h_errno==NETDB_INTERNAL) && + (errno==ERANGE)) + { + buflen*=2; + continue; + } +#endif + break; + } + if (hp) { + strncpy( name, hp->h_name, namelen ); + } else { + *err = (char *)HSTRERROR( h_errno ); + } + LDAP_FREE(buf); +#else /* HAVE_GETHOSTBYADDR_R */ + + LDAP_MUTEX_LOCK( &ldap_int_resolv_mutex ); + hp = gethostbyaddr( addr, alen, sa->sa_family ); + if (hp) { + strncpy( name, hp->h_name, namelen ); + rc = 0; + } else { + rc = h_errno; + *err = (char *)HSTRERROR( h_errno ); + } + LDAP_MUTEX_UNLOCK( &ldap_int_resolv_mutex ); + +#endif /* !HAVE_GETHOSTBYADDR_R */ + return rc; +#endif /* !HAVE_GETNAMEINFO */ +} + +int ldap_pvt_gethostbyaddr_a( + const char *addr, + int len, + int type, + struct hostent *resbuf, + char **buf, + struct hostent **result, + int *herrno_ptr ) +{ +#if defined( HAVE_GETHOSTBYADDR_R ) + +# undef NEED_SAFE_REALLOC +# define NEED_SAFE_REALLOC + int r=-1; + int buflen=BUFSTART; + *buf = NULL; + for(;buflen<BUFMAX;) { + if (safe_realloc( buf, buflen )==NULL) + return r; +#if (GETHOSTBYADDR_R_NARGS < 8) + *result=gethostbyaddr_r( addr, len, type, + resbuf, *buf, buflen, herrno_ptr ); + r = (*result == NULL) ? -1 : 0; +#else + r = gethostbyaddr_r( addr, len, type, + resbuf, *buf, buflen, + result, herrno_ptr ); +#endif + +#ifdef NETDB_INTERNAL + if ((r<0) && + (*herrno_ptr==NETDB_INTERNAL) && + (errno==ERANGE)) + { + buflen*=2; + continue; + } +#endif + return r; + } + return -1; +#elif defined( LDAP_R_COMPILE ) +# undef NEED_COPY_HOSTENT +# define NEED_COPY_HOSTENT + struct hostent *he; + int retval; + *buf = NULL; + + LDAP_MUTEX_LOCK( &ldap_int_resolv_mutex ); + he = gethostbyaddr( addr, len, type ); + + if (he==NULL) { + *herrno_ptr = h_errno; + retval = -1; + } else if (copy_hostent( resbuf, buf, he )<0) { + *herrno_ptr = -1; + retval = -1; + } else { + *result = resbuf; + retval = 0; + } + LDAP_MUTEX_UNLOCK( &ldap_int_resolv_mutex ); + + return retval; + +#else /* gethostbyaddr() */ + *buf = NULL; + *result = gethostbyaddr( addr, len, type ); + + if (*result!=NULL) { + return 0; + } + return -1; +#endif +} +/* + * ldap_int_utils_init() should be called before any other function. + */ + +void ldap_int_utils_init( void ) +{ + static int done=0; + if (done) + return; + done=1; + +#ifdef LDAP_R_COMPILE +#if !defined( USE_CTIME_R ) && !defined( HAVE_REENTRANT_FUNCTIONS ) + ldap_pvt_thread_mutex_init( &ldap_int_ctime_mutex ); +#endif +#if !defined( USE_GMTIME_R ) && !defined( USE_LOCALTIME_R ) + ldap_pvt_thread_mutex_init( &ldap_int_gmtime_mutex ); +#endif + ldap_pvt_thread_mutex_init( &ldap_int_resolv_mutex ); + + ldap_pvt_thread_mutex_init( &ldap_int_hostname_mutex ); + + ldap_pvt_thread_mutex_init( &ldap_int_gettime_mutex ); + +#ifdef HAVE_GSSAPI + ldap_pvt_thread_mutex_init( &ldap_int_gssapi_mutex ); +#endif +#endif + + /* call other module init functions here... */ +} + +#if defined( NEED_COPY_HOSTENT ) +# undef NEED_SAFE_REALLOC +#define NEED_SAFE_REALLOC + +static char *cpy_aliases( + char ***tgtio, + char *buf, + char **src ) +{ + int len; + char **tgt=*tgtio; + for( ; (*src) ; src++ ) { + len = strlen( *src ) + 1; + AC_MEMCPY( buf, *src, len ); + *tgt++=buf; + buf+=len; + } + *tgtio=tgt; + return buf; +} + +static char *cpy_addresses( + char ***tgtio, + char *buf, + char **src, + int len ) +{ + char **tgt=*tgtio; + for( ; (*src) ; src++ ) { + AC_MEMCPY( buf, *src, len ); + *tgt++=buf; + buf+=len; + } + *tgtio=tgt; + return buf; +} + +static int copy_hostent( + struct hostent *res, + char **buf, + struct hostent * src ) +{ + char **p; + char **tp; + char *tbuf; + int name_len; + int n_alias=0; + int total_alias_len=0; + int n_addr=0; + int total_addr_len=0; + int total_len; + + /* calculate the size needed for the buffer */ + name_len = strlen( src->h_name ) + 1; + + if( src->h_aliases != NULL ) { + for( p = src->h_aliases; (*p) != NULL; p++ ) { + total_alias_len += strlen( *p ) + 1; + n_alias++; + } + } + + if( src->h_addr_list != NULL ) { + for( p = src->h_addr_list; (*p) != NULL; p++ ) { + n_addr++; + } + total_addr_len = n_addr * src->h_length; + } + + total_len = (n_alias + n_addr + 2) * sizeof( char * ) + + total_addr_len + total_alias_len + name_len; + + if (safe_realloc( buf, total_len )) { + tp = (char **) *buf; + tbuf = *buf + (n_alias + n_addr + 2) * sizeof( char * ); + AC_MEMCPY( res, src, sizeof( struct hostent ) ); + /* first the name... */ + AC_MEMCPY( tbuf, src->h_name, name_len ); + res->h_name = tbuf; tbuf+=name_len; + /* now the aliases */ + res->h_aliases = tp; + if ( src->h_aliases != NULL ) { + tbuf = cpy_aliases( &tp, tbuf, src->h_aliases ); + } + *tp++=NULL; + /* finally the addresses */ + res->h_addr_list = tp; + if ( src->h_addr_list != NULL ) { + tbuf = cpy_addresses( &tp, tbuf, src->h_addr_list, src->h_length ); + } + *tp++=NULL; + return 0; + } + return -1; +} +#endif + +#if defined( NEED_SAFE_REALLOC ) +static char *safe_realloc( char **buf, int len ) +{ + char *tmpbuf; + tmpbuf = LDAP_REALLOC( *buf, len ); + if (tmpbuf) { + *buf=tmpbuf; + } + return tmpbuf; +} +#endif + +char * ldap_pvt_get_fqdn( char *name ) +{ +#ifdef HAVE_GETADDRINFO + struct addrinfo hints, *res; +#else + char *ha_buf; + struct hostent *hp, he_buf; + int local_h_errno; +#endif + int rc; + char *fqdn, hostbuf[MAXHOSTNAMELEN+1]; + + if( name == NULL ) { + if( gethostname( hostbuf, MAXHOSTNAMELEN ) == 0 ) { + hostbuf[MAXHOSTNAMELEN] = '\0'; + name = hostbuf; + } else { + name = "localhost"; + } + } + +#ifdef HAVE_GETADDRINFO + memset( &hints, 0, sizeof( hints )); + hints.ai_family = AF_UNSPEC; + hints.ai_flags = AI_CANONNAME; + + LDAP_MUTEX_LOCK( &ldap_int_resolv_mutex ); + rc = getaddrinfo( name, NULL, &hints, &res ); + LDAP_MUTEX_UNLOCK( &ldap_int_resolv_mutex ); + if ( rc == 0 && res->ai_canonname ) { + fqdn = LDAP_STRDUP( res->ai_canonname ); + } else { + fqdn = LDAP_STRDUP( name ); + } + if ( rc == 0 ) + freeaddrinfo( res ); +#else + rc = ldap_pvt_gethostbyname_a( name, + &he_buf, &ha_buf, &hp, &local_h_errno ); + + if( rc < 0 || hp == NULL || hp->h_name == NULL ) { + fqdn = LDAP_STRDUP( name ); + } else { + fqdn = LDAP_STRDUP( hp->h_name ); + } + + LDAP_FREE( ha_buf ); +#endif + return fqdn; +} + +#if ( defined( HAVE_GETADDRINFO ) || defined( HAVE_GETNAMEINFO ) ) \ + && !defined( HAVE_GAI_STRERROR ) +char *ldap_pvt_gai_strerror (int code) { + static struct { + int code; + const char *msg; + } values[] = { +#ifdef EAI_ADDRFAMILY + { EAI_ADDRFAMILY, N_("Address family for hostname not supported") }, +#endif + { EAI_AGAIN, N_("Temporary failure in name resolution") }, + { EAI_BADFLAGS, N_("Bad value for ai_flags") }, + { EAI_FAIL, N_("Non-recoverable failure in name resolution") }, + { EAI_FAMILY, N_("ai_family not supported") }, + { EAI_MEMORY, N_("Memory allocation failure") }, +#ifdef EAI_NODATA + { EAI_NODATA, N_("No address associated with hostname") }, +#endif + { EAI_NONAME, N_("Name or service not known") }, + { EAI_SERVICE, N_("Servname not supported for ai_socktype") }, + { EAI_SOCKTYPE, N_("ai_socktype not supported") }, +#ifdef EAI_SYSTEM + { EAI_SYSTEM, N_("System error") }, +#endif + { 0, NULL } + }; + + int i; + + for ( i = 0; values[i].msg != NULL; i++ ) { + if ( values[i].code == code ) { + return (char *) _(values[i].msg); + } + } + + return _("Unknown error"); +} +#endif diff --git a/libraries/libldap/vlvctrl.c b/libraries/libldap/vlvctrl.c new file mode 100644 index 0000000..6357e61 --- /dev/null +++ b/libraries/libldap/vlvctrl.c @@ -0,0 +1,361 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved. + * + * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND + * TREATIES. USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT + * TO VERSION 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS + * AVAILABLE AT HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE" + * IN THE TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION + * OF THIS WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP + * PUBLIC LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT + * THE PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY. + *--- + * Note: A verbatim copy of version 2.0.1 of the OpenLDAP Public License + * can be found in the file "build/LICENSE-2.0.1" in this distribution + * of OpenLDAP Software. + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +#define LDAP_VLVBYINDEX_IDENTIFIER 0xa0L +#define LDAP_VLVBYVALUE_IDENTIFIER 0x81L +#define LDAP_VLVCONTEXT_IDENTIFIER 0x04L + + +/*--- + ldap_create_vlv_control + + Create and encode the Virtual List View control. + + ld (IN) An LDAP session handle. + + vlvinfop (IN) The address of an LDAPVLVInfo structure whose contents + are used to construct the value of the control + that is created. + + value (OUT) A struct berval that contains the value to be assigned to the ldctl_value member + of an LDAPControl structure that contains the + VirtualListViewRequest control. + The bv_val member of the berval structure + SHOULD be freed when it is no longer in use by + calling ldap_memfree(). + + + Ber encoding + + VirtualListViewRequest ::= SEQUENCE { + beforeCount INTEGER (0 .. maxInt), + afterCount INTEGER (0 .. maxInt), + CHOICE { + byoffset [0] SEQUENCE, { + offset INTEGER (0 .. maxInt), + contentCount INTEGER (0 .. maxInt) } + [1] greaterThanOrEqual assertionValue } + contextID OCTET STRING OPTIONAL } + + + Note: The first time the VLV control is created, the ldvlv_context + field of the LDAPVLVInfo structure should be set to NULL. + The context obtained from calling ldap_parse_vlv_control() + should be used as the context in the next ldap_create_vlv_control + call. + + ---*/ + +int +ldap_create_vlv_control_value( + LDAP *ld, + LDAPVLVInfo *vlvinfop, + struct berval *value ) +{ + ber_tag_t tag; + BerElement *ber; + + if ( ld == NULL || vlvinfop == NULL || value == NULL ) { + if ( ld ) + ld->ld_errno = LDAP_PARAM_ERROR; + return LDAP_PARAM_ERROR; + } + + assert( LDAP_VALID( ld ) ); + + value->bv_val = NULL; + value->bv_len = 0; + ld->ld_errno = LDAP_SUCCESS; + + ber = ldap_alloc_ber_with_options( ld ); + if ( ber == NULL ) { + ld->ld_errno = LDAP_NO_MEMORY; + return ld->ld_errno; + } + + tag = ber_printf( ber, "{ii" /*}*/, + vlvinfop->ldvlv_before_count, + vlvinfop->ldvlv_after_count ); + if ( tag == LBER_ERROR ) { + goto error_return; + } + + if ( vlvinfop->ldvlv_attrvalue == NULL ) { + tag = ber_printf( ber, "t{iiN}", + LDAP_VLVBYINDEX_IDENTIFIER, + vlvinfop->ldvlv_offset, + vlvinfop->ldvlv_count ); + if ( tag == LBER_ERROR ) { + goto error_return; + } + + } else { + tag = ber_printf( ber, "tO", + LDAP_VLVBYVALUE_IDENTIFIER, + vlvinfop->ldvlv_attrvalue ); + if ( tag == LBER_ERROR ) { + goto error_return; + } + } + + if ( vlvinfop->ldvlv_context ) { + tag = ber_printf( ber, "tO", + LDAP_VLVCONTEXT_IDENTIFIER, + vlvinfop->ldvlv_context ); + if ( tag == LBER_ERROR ) { + goto error_return; + } + } + + tag = ber_printf( ber, /*{*/ "N}" ); + if ( tag == LBER_ERROR ) { + goto error_return; + } + + if ( ber_flatten2( ber, value, 1 ) == -1 ) { + ld->ld_errno = LDAP_NO_MEMORY; + } + + if ( 0 ) { +error_return:; + ld->ld_errno = LDAP_ENCODING_ERROR; + } + + if ( ber != NULL ) { + ber_free( ber, 1 ); + } + + return ld->ld_errno; +} + +/*--- + ldap_create_vlv_control + + Create and encode the Virtual List View control. + + ld (IN) An LDAP session handle. + + vlvinfop (IN) The address of an LDAPVLVInfo structure whose contents + are used to construct the value of the control + that is created. + + ctrlp (OUT) A result parameter that will be assigned the address + of an LDAPControl structure that contains the + VirtualListViewRequest control created by this function. + The memory occupied by the LDAPControl structure + SHOULD be freed when it is no longer in use by + calling ldap_control_free(). + + + Ber encoding + + VirtualListViewRequest ::= SEQUENCE { + beforeCount INTEGER (0 .. maxInt), + afterCount INTEGER (0 .. maxInt), + CHOICE { + byoffset [0] SEQUENCE, { + offset INTEGER (0 .. maxInt), + contentCount INTEGER (0 .. maxInt) } + [1] greaterThanOrEqual assertionValue } + contextID OCTET STRING OPTIONAL } + + + Note: The first time the VLV control is created, the ldvlv_context + field of the LDAPVLVInfo structure should be set to NULL. + The context obtained from calling ldap_parse_vlv_control() + should be used as the context in the next ldap_create_vlv_control + call. + + ---*/ + +int +ldap_create_vlv_control( + LDAP *ld, + LDAPVLVInfo *vlvinfop, + LDAPControl **ctrlp ) +{ + struct berval value; + + if ( ctrlp == NULL ) { + ld->ld_errno = LDAP_PARAM_ERROR; + return ld->ld_errno; + } + + ld->ld_errno = ldap_create_vlv_control_value( ld, vlvinfop, &value ); + if ( ld->ld_errno == LDAP_SUCCESS ) { + + ld->ld_errno = ldap_control_create( LDAP_CONTROL_VLVREQUEST, + 1, &value, 0, ctrlp ); + if ( ld->ld_errno != LDAP_SUCCESS ) { + LDAP_FREE( value.bv_val ); + } + } + + return ld->ld_errno; +} + + +/*--- + ldap_parse_vlvresponse_control + + Decode the Virtual List View control return information. + + ld (IN) An LDAP session handle. + + ctrl (IN) The address of the LDAPControl structure. + + target_posp (OUT) This result parameter is filled in with the list + index of the target entry. If this parameter is + NULL, the target position is not returned. + + list_countp (OUT) This result parameter is filled in with the server's + estimate of the size of the list. If this parameter + is NULL, the size is not returned. + + contextp (OUT) This result parameter is filled in with the address + of a struct berval that contains the server- + generated context identifier if one was returned by + the server. If the server did not return a context + identifier, this parameter will be set to NULL, even + if an error occured. + The returned context SHOULD be used in the next call + to create a VLV sort control. The struct berval + returned SHOULD be disposed of by calling ber_bvfree() + when it is no longer needed. If NULL is passed for + contextp, the context identifier is not returned. + + errcodep (OUT) This result parameter is filled in with the VLV + result code. If this parameter is NULL, the result + code is not returned. + + + Ber encoding + + VirtualListViewResponse ::= SEQUENCE { + targetPosition INTEGER (0 .. maxInt), + contentCount INTEGER (0 .. maxInt), + virtualListViewResult ENUMERATED { + success (0), + operatonsError (1), + unwillingToPerform (53), + insufficientAccessRights (50), + busy (51), + timeLimitExceeded (3), + adminLimitExceeded (11), + sortControlMissing (60), + offsetRangeError (61), + other (80) }, + contextID OCTET STRING OPTIONAL } + +---*/ + +int +ldap_parse_vlvresponse_control( + LDAP *ld, + LDAPControl *ctrl, + ber_int_t *target_posp, + ber_int_t *list_countp, + struct berval **contextp, + ber_int_t *errcodep ) +{ + BerElement *ber; + ber_int_t pos, count, err; + ber_tag_t tag, berTag; + ber_len_t berLen; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + + if (contextp) { + *contextp = NULL; /* Make sure we return a NULL if error occurs. */ + } + + if (ctrl == NULL) { + ld->ld_errno = LDAP_PARAM_ERROR; + return(ld->ld_errno); + } + + if (strcmp(LDAP_CONTROL_VLVRESPONSE, ctrl->ldctl_oid) != 0) { + /* Not VLV Response control */ + ld->ld_errno = LDAP_CONTROL_NOT_FOUND; + return(ld->ld_errno); + } + + /* Create a BerElement from the berval returned in the control. */ + ber = ber_init(&ctrl->ldctl_value); + + if (ber == NULL) { + ld->ld_errno = LDAP_NO_MEMORY; + return(ld->ld_errno); + } + + /* Extract the data returned in the control. */ + tag = ber_scanf(ber, "{iie" /*}*/, &pos, &count, &err); + + if( tag == LBER_ERROR) { + ber_free(ber, 1); + ld->ld_errno = LDAP_DECODING_ERROR; + return(ld->ld_errno); + } + + + /* Since the context is the last item encoded, if caller doesn't want + it returned, don't decode it. */ + if (contextp) { + if (LDAP_VLVCONTEXT_IDENTIFIER == ber_peek_tag(ber, &berLen)) { + tag = ber_scanf(ber, "tO", &berTag, contextp); + + if( tag == LBER_ERROR) { + ber_free(ber, 1); + ld->ld_errno = LDAP_DECODING_ERROR; + return(ld->ld_errno); + } + } + } + + ber_free(ber, 1); + + /* Return data to the caller for items that were requested. */ + if (target_posp) *target_posp = pos; + if (list_countp) *list_countp = count; + if (errcodep) *errcodep = err; + + ld->ld_errno = LDAP_SUCCESS; + return(ld->ld_errno); +} diff --git a/libraries/libldap/whoami.c b/libraries/libldap/whoami.c new file mode 100644 index 0000000..a14cadb --- /dev/null +++ b/libraries/libldap/whoami.c @@ -0,0 +1,102 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: + * This program was orignally developed by Kurt D. Zeilenga for inclusion in + * OpenLDAP Software. + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" + +/* + * LDAP Who Am I? (Extended) Operation <draft-zeilenga-ldap-authzid-xx.txt> + */ + +int ldap_parse_whoami( + LDAP *ld, + LDAPMessage *res, + struct berval **authzid ) +{ + int rc; + char *retoid = NULL; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( res != NULL ); + assert( authzid != NULL ); + + *authzid = NULL; + + rc = ldap_parse_extended_result( ld, res, &retoid, authzid, 0 ); + + if( rc != LDAP_SUCCESS ) { + ldap_perror( ld, "ldap_parse_whoami" ); + return rc; + } + + ber_memfree( retoid ); + return rc; +} + +int +ldap_whoami( LDAP *ld, + LDAPControl **sctrls, + LDAPControl **cctrls, + int *msgidp ) +{ + int rc; + + assert( ld != NULL ); + assert( LDAP_VALID( ld ) ); + assert( msgidp != NULL ); + + rc = ldap_extended_operation( ld, LDAP_EXOP_WHO_AM_I, + NULL, sctrls, cctrls, msgidp ); + + return rc; +} + +int +ldap_whoami_s( + LDAP *ld, + struct berval **authzid, + LDAPControl **sctrls, + LDAPControl **cctrls ) +{ + int rc; + int msgid; + LDAPMessage *res; + + rc = ldap_whoami( ld, sctrls, cctrls, &msgid ); + if ( rc != LDAP_SUCCESS ) return rc; + + if ( ldap_result( ld, msgid, LDAP_MSG_ALL, (struct timeval *) NULL, &res ) == -1 || !res ) { + return ld->ld_errno; + } + + rc = ldap_parse_whoami( ld, res, authzid ); + if( rc != LDAP_SUCCESS ) { + ldap_msgfree( res ); + return rc; + } + + return( ldap_result2error( ld, res, 1 ) ); +} diff --git a/libraries/libldap_r/Makefile.in b/libraries/libldap_r/Makefile.in new file mode 100644 index 0000000..85eff3d --- /dev/null +++ b/libraries/libldap_r/Makefile.in @@ -0,0 +1,88 @@ +# Makefile.in for LDAP -lldap +# $OpenLDAP$ +## This work is part of OpenLDAP Software <http://www.openldap.org/>. +## +## Copyright 1998-2021 The OpenLDAP Foundation. +## All rights reserved. +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted only as authorized by the OpenLDAP +## Public License. +## +## A copy of this license is available in the file LICENSE in the +## top-level directory of the distribution or, alternatively, at +## <http://www.OpenLDAP.org/license.html>. + +LIBRARY = libldap_r.la + +PROGRAMS = apitest ltest + +XXDIR = $(srcdir)/../libldap +XXSRCS = apitest.c test.c \ + bind.c open.c result.c error.c compare.c search.c \ + controls.c messages.c references.c extended.c cyrus.c \ + modify.c add.c modrdn.c delete.c abandon.c \ + sasl.c gssapi.c sbind.c unbind.c cancel.c \ + filter.c free.c sort.c passwd.c whoami.c \ + getdn.c getentry.c getattr.c getvalues.c addentry.c \ + request.c os-ip.c url.c pagectrl.c sortctrl.c vlvctrl.c \ + init.c options.c print.c string.c util-int.c schema.c \ + charray.c os-local.c dnssrv.c utf-8.c utf-8-conv.c \ + tls2.c tls_o.c tls_g.c tls_m.c \ + turn.c ppolicy.c dds.c txn.c ldap_sync.c stctrl.c \ + assertion.c deref.c ldif.c fetch.c +SRCS = threads.c rdwr.c rmutex.c tpool.c rq.c \ + thr_posix.c thr_cthreads.c thr_thr.c thr_nt.c \ + thr_pth.c thr_stub.c thr_debug.c +OBJS = threads.lo rdwr.lo rmutex.lo tpool.lo rq.lo \ + thr_posix.lo thr_cthreads.lo thr_thr.lo thr_nt.lo \ + thr_pth.lo thr_stub.lo thr_debug.lo \ + bind.lo open.lo result.lo error.lo compare.lo search.lo \ + controls.lo messages.lo references.lo extended.lo cyrus.lo \ + modify.lo add.lo modrdn.lo delete.lo abandon.lo \ + sasl.lo gssapi.lo sbind.lo unbind.lo cancel.lo \ + filter.lo free.lo sort.lo passwd.lo whoami.lo \ + getdn.lo getentry.lo getattr.lo getvalues.lo addentry.lo \ + request.lo os-ip.lo url.lo pagectrl.lo sortctrl.lo vlvctrl.lo \ + init.lo options.lo print.lo string.lo util-int.lo schema.lo \ + charray.lo os-local.lo dnssrv.lo utf-8.lo utf-8-conv.lo \ + tls2.lo tls_o.lo tls_g.lo tls_m.lo \ + turn.lo ppolicy.lo dds.lo txn.lo ldap_sync.lo stctrl.lo \ + assertion.lo deref.lo ldif.lo fetch.lo + +LDAP_INCDIR= ../../include +LDAP_LIBDIR= ../../libraries + +LIB_DEFS = -DLDAP_LIBRARY + +XDEFS = -DLDAP_R_COMPILE -I$(XXDIR) +XLIBS = $(LIBRARY) $(LDAP_LIBLBER_LA) $(LDAP_LIBLUTIL_A) +XXLIBS = $(SECURITY_LIBS) $(LUTIL_LIBS) +XXXLIBS = $(LTHREAD_LIBS) +NT_LINK_LIBS = $(LDAP_LIBLBER_LA) $(AC_LIBS) $(SECURITY_LIBS) +UNIX_LINK_LIBS = $(LDAP_LIBLBER_LA) $(AC_LIBS) $(SECURITY_LIBS) $(LTHREAD_LIBS) + +.links : Makefile + @for i in $(XXSRCS); do \ + $(RM) $$i ; \ + $(LN_S) $(XXDIR)/$$i . ; \ + done + touch .links + +$(XXSRCS) : .links + +clean-local: FORCE + @$(RM) .links + +depend-common: .links + +apitest: $(XLIBS) apitest.o + $(LTLINK) -o $@ apitest.o $(LIBS) +ltest: $(XLIBS) test.o + $(LTLINK) -o $@ test.o $(LIBS) + +install-local: $(CFFILES) FORCE + -$(MKDIR) $(DESTDIR)$(libdir) + $(LTINSTALL) $(INSTALLFLAGS) -m 644 $(LIBRARY) $(DESTDIR)$(libdir) + $(LTFINISH) $(DESTDIR)$(libdir) + diff --git a/libraries/libldap_r/ldap_thr_debug.h b/libraries/libldap_r/ldap_thr_debug.h new file mode 100644 index 0000000..6bf9314 --- /dev/null +++ b/libraries/libldap_r/ldap_thr_debug.h @@ -0,0 +1,191 @@ +/* ldap_thr_debug.h - preprocessor magic for LDAP_THREAD_DEBUG */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2005-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#ifdef LDAP_THREAD_DEBUG + +/* + * libldap_r .c files should include this file after ldap_pvt_thread.h, + * with the appropriate LDAP_THREAD*_IMPLEMENTATION macro(s) defined. + */ + +#ifndef _LDAP_PVT_THREAD_H +#error "ldap_pvt_thread.h" must be included before "ldap_thr_debug.h" +#endif + +/* + * Support for thr_debug.c: + * + * thr_debug.c defines ldap_pvt_thread_* as wrappers around the real + * ldap_pvt_thread_* implementation, which this file renames to + * ldap_int_thread_*. + * + * Implementation: + * + * This file re#defines selected ldap_pvt_thread_* names to + * ldap_int_thread_*, which will be used from wrappers in thr_debug.c. + * Two ldap_int_*() calls are redirected to call ldap_debug_*(): These + * are wrappers around the originals, whose definitions are not renamed. + * This file then #includes ldap_pvt_thread.h to declare the renamed + * functions/types. If #included from thr_debug.c it finally #undefines + * the macros again. + * + * include/ldap_pvt_thread.h declares the typedefs ldap_pvt_thread*_t as + * either wrapper types ldap_debug_thread*_t or their usual definitions + * ldap_int_thread*_t, depending on the LDAP_THREAD_DEBUG_WRAP option. + * When defining the underlying implementation, this file then redirects + * the type names back to the original ldap_int_thread*_t types. + * include/ldap_<int,pvt>_thread.h also do some thr_debug magic. + * + * So, + * libldap_r/<not thr_debug.c> thus define ldap_int_thread_*() instead + * of ldap_pvt_thread_*(). + * thr_debug.c defines the ldap_pvt_*() and ldap_debug_*() functions. + * In thread.c, ldap_pvt_thread_<initialize/destroy>() will call + * ldap_debug_thread_*() instead of ldap_int_thread_*(). + * In tpool.c, ldap_int_thread_pool_shutdown() has explicit thr_debug.c + * support which treats ldap_pvt_thread_pool_destroy() the same way. + */ + +#ifndef LDAP_THREAD_IMPLEMENTATION /* for first part of threads.c */ +#define ldap_int_thread_initialize ldap_debug_thread_initialize +#define ldap_int_thread_destroy ldap_debug_thread_destroy +#else /* LDAP_THREAD_IMPLEMENTATION -- for thr_*.c and end of threads.c */ +#undef ldap_int_thread_initialize +#undef ldap_int_thread_destroy +#ifdef LDAP_THREAD_DEBUG_WRAP /* see ldap_pvt_thread.h */ +#define ldap_pvt_thread_mutex_t ldap_int_thread_mutex_t +#define ldap_pvt_thread_cond_t ldap_int_thread_cond_t +#endif +#define ldap_pvt_thread_sleep ldap_int_thread_sleep +#define ldap_pvt_thread_get_concurrency ldap_int_thread_get_concurrency +#define ldap_pvt_thread_set_concurrency ldap_int_thread_set_concurrency +#define ldap_pvt_thread_create ldap_int_thread_create +#define ldap_pvt_thread_exit ldap_int_thread_exit +#define ldap_pvt_thread_join ldap_int_thread_join +#define ldap_pvt_thread_kill ldap_int_thread_kill +#define ldap_pvt_thread_yield ldap_int_thread_yield +#define ldap_pvt_thread_cond_init ldap_int_thread_cond_init +#define ldap_pvt_thread_cond_destroy ldap_int_thread_cond_destroy +#define ldap_pvt_thread_cond_signal ldap_int_thread_cond_signal +#define ldap_pvt_thread_cond_broadcast ldap_int_thread_cond_broadcast +#define ldap_pvt_thread_cond_wait ldap_int_thread_cond_wait +#define ldap_pvt_thread_mutex_init ldap_int_thread_mutex_init +#define ldap_pvt_thread_mutex_destroy ldap_int_thread_mutex_destroy +#define ldap_pvt_thread_mutex_lock ldap_int_thread_mutex_lock +#define ldap_pvt_thread_mutex_trylock ldap_int_thread_mutex_trylock +#define ldap_pvt_thread_mutex_unlock ldap_int_thread_mutex_unlock +#define ldap_pvt_thread_self ldap_int_thread_self +#endif /* LDAP_THREAD_IMPLEMENTATION */ + +#ifdef LDAP_THREAD_RDWR_IMPLEMENTATION /* rdwr.c, thr_debug.c */ +#ifdef LDAP_THREAD_DEBUG_WRAP /* see ldap_pvt_thread.h */ +#define ldap_pvt_thread_rdwr_t ldap_int_thread_rdwr_t +#endif +#define ldap_pvt_thread_rdwr_init ldap_int_thread_rdwr_init +#define ldap_pvt_thread_rdwr_destroy ldap_int_thread_rdwr_destroy +#define ldap_pvt_thread_rdwr_rlock ldap_int_thread_rdwr_rlock +#define ldap_pvt_thread_rdwr_rtrylock ldap_int_thread_rdwr_rtrylock +#define ldap_pvt_thread_rdwr_runlock ldap_int_thread_rdwr_runlock +#define ldap_pvt_thread_rdwr_wlock ldap_int_thread_rdwr_wlock +#define ldap_pvt_thread_rdwr_wtrylock ldap_int_thread_rdwr_wtrylock +#define ldap_pvt_thread_rdwr_wunlock ldap_int_thread_rdwr_wunlock +#define ldap_pvt_thread_rdwr_readers ldap_int_thread_rdwr_readers +#define ldap_pvt_thread_rdwr_writers ldap_int_thread_rdwr_writers +#define ldap_pvt_thread_rdwr_active ldap_int_thread_rdwr_active +#endif /* LDAP_THREAD_RDWR_IMPLEMENTATION */ + +#ifdef LDAP_THREAD_POOL_IMPLEMENTATION /* tpool.c, thr_stub.c, thr_debug.c */ +#ifdef LDAP_THREAD_DEBUG_WRAP /* see ldap_pvt_thread.h */ +#define ldap_pvt_thread_pool_t ldap_int_thread_pool_t +#endif +#define ldap_pvt_thread_pool_init ldap_int_thread_pool_init +#define ldap_pvt_thread_pool_submit ldap_int_thread_pool_submit +#define ldap_pvt_thread_pool_maxthreads ldap_int_thread_pool_maxthreads +#define ldap_pvt_thread_pool_backload ldap_int_thread_pool_backload +#define ldap_pvt_thread_pool_pause ldap_int_thread_pool_pause +#define ldap_pvt_thread_pool_resume ldap_int_thread_pool_resume +#define ldap_pvt_thread_pool_destroy ldap_int_thread_pool_destroy +#define ldap_pvt_thread_pool_getkey ldap_int_thread_pool_getkey +#define ldap_pvt_thread_pool_setkey ldap_int_thread_pool_setkey +#define ldap_pvt_thread_pool_purgekey ldap_int_thread_pool_purgekey +#define ldap_pvt_thread_pool_context ldap_int_thread_pool_context +#define ldap_pvt_thread_pool_context_reset ldap_int_thread_pool_context_reset +#endif /* LDAP_THREAD_POOL_IMPLEMENTATION */ + +#undef _LDAP_PVT_THREAD_H +#include "ldap_pvt_thread.h" + +#ifdef LDAP_THREAD_POOL_IMPLEMENTATION /* tpool.c */ +/* + * tpool.c:ldap_int_thread_pool_shutdown() needs this. Could not + * use it for ldap_pvt_thread.h above because of its use of LDAP_P(). + */ +#undef ldap_pvt_thread_pool_destroy +#define ldap_pvt_thread_pool_destroy(p,r) ldap_int_thread_pool_destroy(p,r) +#endif + +#ifdef LDAP_THREAD_DEBUG_IMPLEMENTATION /* thr_debug.c */ +#undef ldap_pvt_thread_mutex_t +#undef ldap_pvt_thread_cond_t +#undef ldap_pvt_thread_sleep +#undef ldap_pvt_thread_get_concurrency +#undef ldap_pvt_thread_set_concurrency +#undef ldap_pvt_thread_create +#undef ldap_pvt_thread_exit +#undef ldap_pvt_thread_join +#undef ldap_pvt_thread_kill +#undef ldap_pvt_thread_yield +#undef ldap_pvt_thread_cond_init +#undef ldap_pvt_thread_cond_destroy +#undef ldap_pvt_thread_cond_signal +#undef ldap_pvt_thread_cond_broadcast +#undef ldap_pvt_thread_cond_wait +#undef ldap_pvt_thread_mutex_init +#undef ldap_pvt_thread_mutex_destroy +#undef ldap_pvt_thread_mutex_lock +#undef ldap_pvt_thread_mutex_trylock +#undef ldap_pvt_thread_mutex_unlock +#undef ldap_pvt_thread_self +/* LDAP_THREAD_RDWR_IMPLEMENTATION: */ +#undef ldap_pvt_thread_rdwr_t +#undef ldap_pvt_thread_rdwr_init +#undef ldap_pvt_thread_rdwr_destroy +#undef ldap_pvt_thread_rdwr_rlock +#undef ldap_pvt_thread_rdwr_rtrylock +#undef ldap_pvt_thread_rdwr_runlock +#undef ldap_pvt_thread_rdwr_wlock +#undef ldap_pvt_thread_rdwr_wtrylock +#undef ldap_pvt_thread_rdwr_wunlock +#undef ldap_pvt_thread_rdwr_readers +#undef ldap_pvt_thread_rdwr_writers +#undef ldap_pvt_thread_rdwr_active +/* LDAP_THREAD_POOL_IMPLEMENTATION: */ +#undef ldap_pvt_thread_pool_t +#undef ldap_pvt_thread_pool_init +#undef ldap_pvt_thread_pool_submit +#undef ldap_pvt_thread_pool_maxthreads +#undef ldap_pvt_thread_pool_backload +#undef ldap_pvt_thread_pool_pause +#undef ldap_pvt_thread_pool_resume +#undef ldap_pvt_thread_pool_destroy +#undef ldap_pvt_thread_pool_getkey +#undef ldap_pvt_thread_pool_setkey +#undef ldap_pvt_thread_pool_purgekey +#undef ldap_pvt_thread_pool_context +#undef ldap_pvt_thread_pool_context_reset +#endif /* LDAP_THREAD_DEBUG_IMPLEMENTATION */ + +#endif /* LDAP_THREAD_DEBUG */ diff --git a/libraries/libldap_r/rdwr.c b/libraries/libldap_r/rdwr.c new file mode 100644 index 0000000..424e83e --- /dev/null +++ b/libraries/libldap_r/rdwr.c @@ -0,0 +1,458 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* This work was initially developed by Kurt D. Zeilenga for inclusion + * in OpenLDAP Software. Additional significant contributors include: + * Stuart Lynne + */ + +/* + * This is an improved implementation of Reader/Writer locks does + * not protect writers from starvation. That is, if a writer is + * currently waiting on a reader, any new reader will get + * the lock before the writer. + * + * Does not support cancellation nor does any status checking. + */ +/* Adapted from publically available examples for: + * "Programming with Posix Threads" + * by David R Butenhof, Addison-Wesley + * http://cseng.aw.com/bookpage.taf?ISBN=0-201-63392-2 + */ + +#include "portable.h" + +#include <ac/stdlib.h> + +#include <ac/errno.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" +#include "ldap_pvt_thread.h" /* Get the thread interface */ +#define LDAP_THREAD_RDWR_IMPLEMENTATION +#include "ldap_thr_debug.h" /* May rename the symbols defined below */ + +/* + * implementations that provide their own compatible + * reader/writer locks define LDAP_THREAD_HAVE_RDWR + * in ldap_pvt_thread.h + */ +#ifndef LDAP_THREAD_HAVE_RDWR + +struct ldap_int_thread_rdwr_s { + ldap_pvt_thread_mutex_t ltrw_mutex; + ldap_pvt_thread_cond_t ltrw_read; /* wait for read */ + ldap_pvt_thread_cond_t ltrw_write; /* wait for write */ + int ltrw_valid; +#define LDAP_PVT_THREAD_RDWR_VALID 0x0bad + int ltrw_r_active; + int ltrw_w_active; + int ltrw_r_wait; + int ltrw_w_wait; +#ifdef LDAP_RDWR_DEBUG + /* keep track of who has these locks */ +#define MAX_READERS 32 + int ltrw_more_readers; /* Set if ltrw_readers[] is incomplete */ + ldap_pvt_thread_t ltrw_readers[MAX_READERS]; + ldap_pvt_thread_t ltrw_writer; +#endif +}; + +int +ldap_pvt_thread_rdwr_init( ldap_pvt_thread_rdwr_t *rwlock ) +{ + struct ldap_int_thread_rdwr_s *rw; + + assert( rwlock != NULL ); + + rw = (struct ldap_int_thread_rdwr_s *) LDAP_CALLOC( 1, + sizeof( struct ldap_int_thread_rdwr_s ) ); + if ( !rw ) + return LDAP_NO_MEMORY; + + /* we should check return results */ + ldap_pvt_thread_mutex_init( &rw->ltrw_mutex ); + ldap_pvt_thread_cond_init( &rw->ltrw_read ); + ldap_pvt_thread_cond_init( &rw->ltrw_write ); + + rw->ltrw_valid = LDAP_PVT_THREAD_RDWR_VALID; + + *rwlock = rw; + return 0; +} + +int +ldap_pvt_thread_rdwr_destroy( ldap_pvt_thread_rdwr_t *rwlock ) +{ + struct ldap_int_thread_rdwr_s *rw; + + assert( rwlock != NULL ); + rw = *rwlock; + + assert( rw != NULL ); + assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID ); + + if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID ) + return LDAP_PVT_THREAD_EINVAL; + + ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex ); + + assert( rw->ltrw_w_active >= 0 ); + assert( rw->ltrw_w_wait >= 0 ); + assert( rw->ltrw_r_active >= 0 ); + assert( rw->ltrw_r_wait >= 0 ); + + /* active threads? */ + if( rw->ltrw_r_active > 0 || rw->ltrw_w_active > 0) { + ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex ); + return LDAP_PVT_THREAD_EBUSY; + } + + /* waiting threads? */ + if( rw->ltrw_r_wait > 0 || rw->ltrw_w_wait > 0) { + ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex ); + return LDAP_PVT_THREAD_EBUSY; + } + + rw->ltrw_valid = 0; + + ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex ); + + ldap_pvt_thread_mutex_destroy( &rw->ltrw_mutex ); + ldap_pvt_thread_cond_destroy( &rw->ltrw_read ); + ldap_pvt_thread_cond_destroy( &rw->ltrw_write ); + + LDAP_FREE(rw); + *rwlock = NULL; + return 0; +} + +int ldap_pvt_thread_rdwr_rlock( ldap_pvt_thread_rdwr_t *rwlock ) +{ + struct ldap_int_thread_rdwr_s *rw; + + assert( rwlock != NULL ); + rw = *rwlock; + + assert( rw != NULL ); + assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID ); + + if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID ) + return LDAP_PVT_THREAD_EINVAL; + + ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex ); + + assert( rw->ltrw_w_active >= 0 ); + assert( rw->ltrw_w_wait >= 0 ); + assert( rw->ltrw_r_active >= 0 ); + assert( rw->ltrw_r_wait >= 0 ); + + if( rw->ltrw_w_active > 0 ) { + /* writer is active */ + + rw->ltrw_r_wait++; + + do { + ldap_pvt_thread_cond_wait( + &rw->ltrw_read, &rw->ltrw_mutex ); + } while( rw->ltrw_w_active > 0 ); + + rw->ltrw_r_wait--; + assert( rw->ltrw_r_wait >= 0 ); + } + +#ifdef LDAP_RDWR_DEBUG + if( rw->ltrw_r_active < MAX_READERS ) + rw->ltrw_readers[rw->ltrw_r_active] = ldap_pvt_thread_self(); + else + rw->ltrw_more_readers = 1; +#endif + rw->ltrw_r_active++; + + + ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex ); + + return 0; +} + +int ldap_pvt_thread_rdwr_rtrylock( ldap_pvt_thread_rdwr_t *rwlock ) +{ + struct ldap_int_thread_rdwr_s *rw; + + assert( rwlock != NULL ); + rw = *rwlock; + + assert( rw != NULL ); + assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID ); + + if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID ) + return LDAP_PVT_THREAD_EINVAL; + + ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex ); + + assert( rw->ltrw_w_active >= 0 ); + assert( rw->ltrw_w_wait >= 0 ); + assert( rw->ltrw_r_active >= 0 ); + assert( rw->ltrw_r_wait >= 0 ); + + if( rw->ltrw_w_active > 0) { + ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex ); + return LDAP_PVT_THREAD_EBUSY; + } + +#ifdef LDAP_RDWR_DEBUG + if( rw->ltrw_r_active < MAX_READERS ) + rw->ltrw_readers[rw->ltrw_r_active] = ldap_pvt_thread_self(); + else + rw->ltrw_more_readers = 1; +#endif + rw->ltrw_r_active++; + + ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex ); + + return 0; +} + +int ldap_pvt_thread_rdwr_runlock( ldap_pvt_thread_rdwr_t *rwlock ) +{ + struct ldap_int_thread_rdwr_s *rw; + + assert( rwlock != NULL ); + rw = *rwlock; + + assert( rw != NULL ); + assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID ); + + if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID ) + return LDAP_PVT_THREAD_EINVAL; + + ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex ); + + rw->ltrw_r_active--; +#ifdef LDAP_RDWR_DEBUG + /* Remove us from the list of readers */ + { + ldap_pvt_thread_t self = ldap_pvt_thread_self(); + int i, j; + for( i = j = rw->ltrw_r_active; i >= 0; i--) { + if (rw->ltrw_readers[i] == self) { + rw->ltrw_readers[i] = rw->ltrw_readers[j]; + rw->ltrw_readers[j] = 0; + break; + } + } + if( !rw->ltrw_more_readers ) + assert( i >= 0 ); + else if( j == 0 ) + rw->ltrw_more_readers = 0; + } +#endif + + assert( rw->ltrw_w_active >= 0 ); + assert( rw->ltrw_w_wait >= 0 ); + assert( rw->ltrw_r_active >= 0 ); + assert( rw->ltrw_r_wait >= 0 ); + + if (rw->ltrw_r_active == 0 && rw->ltrw_w_wait > 0 ) { + ldap_pvt_thread_cond_signal( &rw->ltrw_write ); + } + + ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex ); + + return 0; +} + +int ldap_pvt_thread_rdwr_wlock( ldap_pvt_thread_rdwr_t *rwlock ) +{ + struct ldap_int_thread_rdwr_s *rw; + + assert( rwlock != NULL ); + rw = *rwlock; + + assert( rw != NULL ); + assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID ); + + if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID ) + return LDAP_PVT_THREAD_EINVAL; + + ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex ); + + assert( rw->ltrw_w_active >= 0 ); + assert( rw->ltrw_w_wait >= 0 ); + assert( rw->ltrw_r_active >= 0 ); + assert( rw->ltrw_r_wait >= 0 ); + + if ( rw->ltrw_w_active > 0 || rw->ltrw_r_active > 0 ) { + rw->ltrw_w_wait++; + + do { + ldap_pvt_thread_cond_wait( + &rw->ltrw_write, &rw->ltrw_mutex ); + } while ( rw->ltrw_w_active > 0 || rw->ltrw_r_active > 0 ); + + rw->ltrw_w_wait--; + assert( rw->ltrw_w_wait >= 0 ); + } + +#ifdef LDAP_RDWR_DEBUG + rw->ltrw_writer = ldap_pvt_thread_self(); +#endif + rw->ltrw_w_active++; + + ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex ); + + return 0; +} + +int ldap_pvt_thread_rdwr_wtrylock( ldap_pvt_thread_rdwr_t *rwlock ) +{ + struct ldap_int_thread_rdwr_s *rw; + + assert( rwlock != NULL ); + rw = *rwlock; + + assert( rw != NULL ); + assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID ); + + if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID ) + return LDAP_PVT_THREAD_EINVAL; + + ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex ); + + assert( rw->ltrw_w_active >= 0 ); + assert( rw->ltrw_w_wait >= 0 ); + assert( rw->ltrw_r_active >= 0 ); + assert( rw->ltrw_r_wait >= 0 ); + + if ( rw->ltrw_w_active > 0 || rw->ltrw_r_active > 0 ) { + ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex ); + return LDAP_PVT_THREAD_EBUSY; + } + +#ifdef LDAP_RDWR_DEBUG + rw->ltrw_writer = ldap_pvt_thread_self(); +#endif + rw->ltrw_w_active++; + + ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex ); + + return 0; +} + +int ldap_pvt_thread_rdwr_wunlock( ldap_pvt_thread_rdwr_t *rwlock ) +{ + struct ldap_int_thread_rdwr_s *rw; + + assert( rwlock != NULL ); + rw = *rwlock; + + assert( rw != NULL ); + assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID ); + + if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID ) + return LDAP_PVT_THREAD_EINVAL; + + ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex ); + + rw->ltrw_w_active--; + + assert( rw->ltrw_w_active >= 0 ); + assert( rw->ltrw_w_wait >= 0 ); + assert( rw->ltrw_r_active >= 0 ); + assert( rw->ltrw_r_wait >= 0 ); + + if (rw->ltrw_r_wait > 0) { + ldap_pvt_thread_cond_broadcast( &rw->ltrw_read ); + + } else if (rw->ltrw_w_wait > 0) { + ldap_pvt_thread_cond_signal( &rw->ltrw_write ); + } + +#ifdef LDAP_RDWR_DEBUG + assert( rw->ltrw_writer == ldap_pvt_thread_self() ); + rw->ltrw_writer = 0; +#endif + ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex ); + + return 0; +} + +#ifdef LDAP_RDWR_DEBUG + +/* just for testing, + * return 0 if false, suitable for assert(ldap_pvt_thread_rdwr_Xchk(rdwr)) + * + * Currently they don't check if the calling thread is the one + * that has the lock, just that there is a reader or writer. + * + * Basically sufficent for testing that places that should have + * a lock are caught. + */ + +int ldap_pvt_thread_rdwr_readers(ldap_pvt_thread_rdwr_t *rwlock) +{ + struct ldap_int_thread_rdwr_s *rw; + + assert( rwlock != NULL ); + rw = *rwlock; + + assert( rw != NULL ); + assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID ); + assert( rw->ltrw_w_active >= 0 ); + assert( rw->ltrw_w_wait >= 0 ); + assert( rw->ltrw_r_active >= 0 ); + assert( rw->ltrw_r_wait >= 0 ); + + return( rw->ltrw_r_active ); +} + +int ldap_pvt_thread_rdwr_writers(ldap_pvt_thread_rdwr_t *rwlock) +{ + struct ldap_int_thread_rdwr_s *rw; + + assert( rwlock != NULL ); + rw = *rwlock; + + assert( rw != NULL ); + assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID ); + assert( rw->ltrw_w_active >= 0 ); + assert( rw->ltrw_w_wait >= 0 ); + assert( rw->ltrw_r_active >= 0 ); + assert( rw->ltrw_r_wait >= 0 ); + + return( rw->ltrw_w_active ); +} + +int ldap_pvt_thread_rdwr_active(ldap_pvt_thread_rdwr_t *rwlock) +{ + struct ldap_int_thread_rdwr_s *rw; + + assert( rwlock != NULL ); + rw = *rwlock; + + assert( rw != NULL ); + assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID ); + assert( rw->ltrw_w_active >= 0 ); + assert( rw->ltrw_w_wait >= 0 ); + assert( rw->ltrw_r_active >= 0 ); + assert( rw->ltrw_r_wait >= 0 ); + + return(ldap_pvt_thread_rdwr_readers(rwlock) + + ldap_pvt_thread_rdwr_writers(rwlock)); +} + +#endif /* LDAP_RDWR_DEBUG */ + +#endif /* LDAP_THREAD_HAVE_RDWR */ diff --git a/libraries/libldap_r/rmutex.c b/libraries/libldap_r/rmutex.c new file mode 100644 index 0000000..6099a2a --- /dev/null +++ b/libraries/libldap_r/rmutex.c @@ -0,0 +1,219 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2006-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* This work was initially developed by Howard Chu for inclusion + * in OpenLDAP Software. + */ + +/* + * This is an implementation of recursive mutexes. + */ + +#include "portable.h" + +#include <ac/stdlib.h> + +#include <ac/errno.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" +#include "ldap_pvt_thread.h" /* Get the thread interface */ + +struct ldap_int_thread_rmutex_s { + ldap_pvt_thread_mutex_t ltrm_mutex; + ldap_pvt_thread_cond_t ltrm_cond; + ldap_pvt_thread_t ltrm_owner; + int ltrm_valid; +#define LDAP_PVT_THREAD_RMUTEX_VALID 0x0cdb + int ltrm_depth; + int ltrm_waits; +}; + +static const ldap_pvt_thread_t tid_zero; + +int +ldap_pvt_thread_rmutex_init( ldap_pvt_thread_rmutex_t *rmutex ) +{ + struct ldap_int_thread_rmutex_s *rm; + + assert( rmutex != NULL ); + + rm = (struct ldap_int_thread_rmutex_s *) LDAP_CALLOC( 1, + sizeof( struct ldap_int_thread_rmutex_s ) ); + if ( !rm ) + return LDAP_NO_MEMORY; + + /* we should check return results */ + ldap_pvt_thread_mutex_init( &rm->ltrm_mutex ); + ldap_pvt_thread_cond_init( &rm->ltrm_cond ); + + rm->ltrm_valid = LDAP_PVT_THREAD_RMUTEX_VALID; + + *rmutex = rm; + return 0; +} + +int +ldap_pvt_thread_rmutex_destroy( ldap_pvt_thread_rmutex_t *rmutex ) +{ + struct ldap_int_thread_rmutex_s *rm; + + assert( rmutex != NULL ); + rm = *rmutex; + + assert( rm != NULL ); + assert( rm->ltrm_valid == LDAP_PVT_THREAD_RMUTEX_VALID ); + + if( rm->ltrm_valid != LDAP_PVT_THREAD_RMUTEX_VALID ) + return LDAP_PVT_THREAD_EINVAL; + + ldap_pvt_thread_mutex_lock( &rm->ltrm_mutex ); + + assert( rm->ltrm_depth >= 0 ); + assert( rm->ltrm_waits >= 0 ); + + /* in use? */ + if( rm->ltrm_depth > 0 || rm->ltrm_waits > 0 ) { + ldap_pvt_thread_mutex_unlock( &rm->ltrm_mutex ); + return LDAP_PVT_THREAD_EBUSY; + } + + rm->ltrm_valid = 0; + + ldap_pvt_thread_mutex_unlock( &rm->ltrm_mutex ); + + ldap_pvt_thread_mutex_destroy( &rm->ltrm_mutex ); + ldap_pvt_thread_cond_destroy( &rm->ltrm_cond ); + + LDAP_FREE(rm); + *rmutex = NULL; + return 0; +} + +int ldap_pvt_thread_rmutex_lock( ldap_pvt_thread_rmutex_t *rmutex, + ldap_pvt_thread_t owner ) +{ + struct ldap_int_thread_rmutex_s *rm; + + assert( rmutex != NULL ); + rm = *rmutex; + + assert( rm != NULL ); + assert( rm->ltrm_valid == LDAP_PVT_THREAD_RMUTEX_VALID ); + + if( rm->ltrm_valid != LDAP_PVT_THREAD_RMUTEX_VALID ) + return LDAP_PVT_THREAD_EINVAL; + + ldap_pvt_thread_mutex_lock( &rm->ltrm_mutex ); + + assert( rm->ltrm_depth >= 0 ); + assert( rm->ltrm_waits >= 0 ); + + if( rm->ltrm_depth > 0 ) { + /* already locked */ + if ( !ldap_pvt_thread_equal( rm->ltrm_owner, owner )) { + rm->ltrm_waits++; + do { + ldap_pvt_thread_cond_wait( &rm->ltrm_cond, + &rm->ltrm_mutex ); + } while( rm->ltrm_depth > 0 ); + + rm->ltrm_waits--; + assert( rm->ltrm_waits >= 0 ); + rm->ltrm_owner = owner; + } + } else { + rm->ltrm_owner = owner; + } + + rm->ltrm_depth++; + + ldap_pvt_thread_mutex_unlock( &rm->ltrm_mutex ); + + return 0; +} + +int ldap_pvt_thread_rmutex_trylock( ldap_pvt_thread_rmutex_t *rmutex, + ldap_pvt_thread_t owner ) +{ + struct ldap_int_thread_rmutex_s *rm; + + assert( rmutex != NULL ); + rm = *rmutex; + + assert( rm != NULL ); + assert( rm->ltrm_valid == LDAP_PVT_THREAD_RMUTEX_VALID ); + + if( rm->ltrm_valid != LDAP_PVT_THREAD_RMUTEX_VALID ) + return LDAP_PVT_THREAD_EINVAL; + + ldap_pvt_thread_mutex_lock( &rm->ltrm_mutex ); + + assert( rm->ltrm_depth >= 0 ); + assert( rm->ltrm_waits >= 0 ); + + if( rm->ltrm_depth > 0 ) { + if ( !ldap_pvt_thread_equal( owner, rm->ltrm_owner )) { + ldap_pvt_thread_mutex_unlock( &rm->ltrm_mutex ); + return LDAP_PVT_THREAD_EBUSY; + } + } else { + rm->ltrm_owner = owner; + } + + rm->ltrm_depth++; + + ldap_pvt_thread_mutex_unlock( &rm->ltrm_mutex ); + + return 0; +} + +int ldap_pvt_thread_rmutex_unlock( ldap_pvt_thread_rmutex_t *rmutex, + ldap_pvt_thread_t owner ) +{ + struct ldap_int_thread_rmutex_s *rm; + + assert( rmutex != NULL ); + rm = *rmutex; + + assert( rm != NULL ); + assert( rm->ltrm_valid == LDAP_PVT_THREAD_RMUTEX_VALID ); + + if( rm->ltrm_valid != LDAP_PVT_THREAD_RMUTEX_VALID ) + return LDAP_PVT_THREAD_EINVAL; + + ldap_pvt_thread_mutex_lock( &rm->ltrm_mutex ); + + if( !ldap_pvt_thread_equal( owner, rm->ltrm_owner )) { + ldap_pvt_thread_mutex_unlock( &rm->ltrm_mutex ); + return LDAP_PVT_THREAD_EINVAL; + } + + rm->ltrm_depth--; + if ( !rm->ltrm_depth ) + rm->ltrm_owner = tid_zero; + + assert( rm->ltrm_depth >= 0 ); + assert( rm->ltrm_waits >= 0 ); + + if ( !rm->ltrm_depth && rm->ltrm_waits ) { + ldap_pvt_thread_cond_signal( &rm->ltrm_cond ); + } + + ldap_pvt_thread_mutex_unlock( &rm->ltrm_mutex ); + + return 0; +} + diff --git a/libraries/libldap_r/rq.c b/libraries/libldap_r/rq.c new file mode 100644 index 0000000..5857cc6 --- /dev/null +++ b/libraries/libldap_r/rq.c @@ -0,0 +1,221 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2003-2021 The OpenLDAP Foundation. + * Portions Copyright 2003 IBM Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* This work was initially developed by Jong Hyuk Choi for inclusion + * in OpenLDAP Software. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdarg.h> +#include <ac/stdlib.h> +#include <ac/errno.h> +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include "ldap-int.h" +#include "ldap_pvt_thread.h" +#include "ldap_queue.h" +#include "ldap_rq.h" + +struct re_s * +ldap_pvt_runqueue_insert( + struct runqueue_s* rq, + time_t interval, + ldap_pvt_thread_start_t *routine, + void *arg, + char *tname, + char *tspec +) +{ + struct re_s* entry; + + entry = (struct re_s *) LDAP_CALLOC( 1, sizeof( struct re_s )); + if ( entry ) { + entry->interval.tv_sec = interval; + entry->interval.tv_usec = 0; + entry->next_sched.tv_sec = time( NULL ); + entry->next_sched.tv_usec = 0; + entry->routine = routine; + entry->arg = arg; + entry->tname = tname; + entry->tspec = tspec; + LDAP_STAILQ_INSERT_HEAD( &rq->task_list, entry, tnext ); + } + return entry; +} + +struct re_s * +ldap_pvt_runqueue_find( + struct runqueue_s *rq, + ldap_pvt_thread_start_t *routine, + void *arg +) +{ + struct re_s* e; + + LDAP_STAILQ_FOREACH( e, &rq->task_list, tnext ) { + if ( e->routine == routine && e->arg == arg ) + return e; + } + return NULL; +} + +void +ldap_pvt_runqueue_remove( + struct runqueue_s* rq, + struct re_s* entry +) +{ + struct re_s* e; + + LDAP_STAILQ_FOREACH( e, &rq->task_list, tnext ) { + if ( e == entry) + break; + } + + assert( e == entry ); + + LDAP_STAILQ_REMOVE( &rq->task_list, entry, re_s, tnext ); + + LDAP_FREE( entry ); +} + +struct re_s* +ldap_pvt_runqueue_next_sched( + struct runqueue_s* rq, + struct timeval* next_run +) +{ + struct re_s* entry; + + entry = LDAP_STAILQ_FIRST( &rq->task_list ); + if ( entry == NULL || entry->next_sched.tv_sec == 0 ) { + return NULL; + } else { + *next_run = entry->next_sched; + return entry; + } +} + +void +ldap_pvt_runqueue_runtask( + struct runqueue_s* rq, + struct re_s* entry +) +{ + LDAP_STAILQ_INSERT_TAIL( &rq->run_list, entry, rnext ); +} + +void +ldap_pvt_runqueue_stoptask( + struct runqueue_s* rq, + struct re_s* entry +) +{ + LDAP_STAILQ_REMOVE( &rq->run_list, entry, re_s, rnext ); +} + +int +ldap_pvt_runqueue_isrunning( + struct runqueue_s* rq, + struct re_s* entry +) +{ + struct re_s* e; + + LDAP_STAILQ_FOREACH( e, &rq->run_list, rnext ) { + if ( e == entry ) { + return 1; + } + } + return 0; +} + +void +ldap_pvt_runqueue_resched( + struct runqueue_s* rq, + struct re_s* entry, + int defer +) +{ + struct re_s* prev; + struct re_s* e; + + LDAP_STAILQ_FOREACH( e, &rq->task_list, tnext ) { + if ( e == entry ) + break; + } + + assert ( e == entry ); + + LDAP_STAILQ_REMOVE( &rq->task_list, entry, re_s, tnext ); + + if ( !defer ) { + entry->next_sched.tv_sec = time( NULL ) + entry->interval.tv_sec; + } else { + entry->next_sched.tv_sec = 0; + } + + if ( LDAP_STAILQ_EMPTY( &rq->task_list )) { + LDAP_STAILQ_INSERT_HEAD( &rq->task_list, entry, tnext ); + } else if ( entry->next_sched.tv_sec == 0 ) { + LDAP_STAILQ_INSERT_TAIL( &rq->task_list, entry, tnext ); + } else { + prev = NULL; + LDAP_STAILQ_FOREACH( e, &rq->task_list, tnext ) { + if ( e->next_sched.tv_sec == 0 ) { + if ( prev == NULL ) { + LDAP_STAILQ_INSERT_HEAD( &rq->task_list, entry, tnext ); + } else { + LDAP_STAILQ_INSERT_AFTER( &rq->task_list, prev, entry, tnext ); + } + return; + } else if ( e->next_sched.tv_sec > entry->next_sched.tv_sec ) { + if ( prev == NULL ) { + LDAP_STAILQ_INSERT_HEAD( &rq->task_list, entry, tnext ); + } else { + LDAP_STAILQ_INSERT_AFTER( &rq->task_list, prev, entry, tnext ); + } + return; + } + prev = e; + } + LDAP_STAILQ_INSERT_TAIL( &rq->task_list, entry, tnext ); + } +} + +int +ldap_pvt_runqueue_persistent_backload( + struct runqueue_s* rq +) +{ + struct re_s* e; + int count = 0; + + ldap_pvt_thread_mutex_lock( &rq->rq_mutex ); + if ( !LDAP_STAILQ_EMPTY( &rq->task_list )) { + LDAP_STAILQ_FOREACH( e, &rq->task_list, tnext ) { + if ( e->next_sched.tv_sec == 0 ) + count++; + } + } + ldap_pvt_thread_mutex_unlock( &rq->rq_mutex ); + return count; +} + diff --git a/libraries/libldap_r/thr_cthreads.c b/libraries/libldap_r/thr_cthreads.c new file mode 100644 index 0000000..6cc4789 --- /dev/null +++ b/libraries/libldap_r/thr_cthreads.c @@ -0,0 +1,180 @@ +/* thr_cthreads.c - wrapper for mach cthreads */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* This work was initially developed by Luke Howard for inclusion + * in U-MICH LDAP 3.3. + */ + +#include "portable.h" + +#if defined( HAVE_MACH_CTHREADS ) +#include "ldap_pvt_thread.h" /* Get the thread interface */ +#define LDAP_THREAD_IMPLEMENTATION +#include "ldap_thr_debug.h" /* May rename the symbols defined below */ + +int +ldap_int_thread_initialize( void ) +{ + return 0; +} + +int +ldap_int_thread_destroy( void ) +{ + return 0; +} + +int +ldap_pvt_thread_create( ldap_pvt_thread_t * thread, + int detach, + void *(*start_routine)( void *), void *arg) +{ + *thread = cthread_fork( (cthread_fn_t) start_routine, arg); + return ( *thread == NULL ? -1 : 0 ); +} + +void +ldap_pvt_thread_exit( void *retval ) +{ + cthread_exit( (any_t) retval ); +} + +int +ldap_pvt_thread_join( ldap_pvt_thread_t thread, void **thread_return ) +{ + void *status; + status = (void *) cthread_join ( thread ); + if (thread_return != NULL) + { + *thread_return = status; + } + return 0; +} + +int +ldap_pvt_thread_kill( ldap_pvt_thread_t thread, int signo ) +{ + return 0; +} + +int +ldap_pvt_thread_yield( void ) +{ + cthread_yield(); + return 0; +} + +int +ldap_pvt_thread_cond_init( ldap_pvt_thread_cond_t *cond ) +{ + condition_init( cond ); + return( 0 ); +} + +int +ldap_pvt_thread_cond_destroy( ldap_pvt_thread_cond_t *cond ) +{ + condition_clear( cond ); + return( 0 ); +} + +int +ldap_pvt_thread_cond_signal( ldap_pvt_thread_cond_t *cond ) +{ + condition_signal( cond ); + return( 0 ); +} + +int +ldap_pvt_thread_cond_broadcast( ldap_pvt_thread_cond_t *cond ) +{ + condition_broadcast( cond ); + return( 0 ); +} + +int +ldap_pvt_thread_cond_wait( ldap_pvt_thread_cond_t *cond, + ldap_pvt_thread_mutex_t *mutex ) +{ + condition_wait( cond, mutex ); + return( 0 ); +} + +int +ldap_pvt_thread_mutex_init( ldap_pvt_thread_mutex_t *mutex ) +{ + mutex_init( mutex ); + mutex->name = NULL; + return ( 0 ); +} + +int +ldap_pvt_thread_mutex_destroy( ldap_pvt_thread_mutex_t *mutex ) +{ + mutex_clear( mutex ); + return ( 0 ); +} + +int +ldap_pvt_thread_mutex_lock( ldap_pvt_thread_mutex_t *mutex ) +{ + mutex_lock( mutex ); + return ( 0 ); +} + +int +ldap_pvt_thread_mutex_unlock( ldap_pvt_thread_mutex_t *mutex ) +{ + mutex_unlock( mutex ); + return ( 0 ); +} + +int +ldap_pvt_thread_mutex_trylock( ldap_pvt_thread_mutex_t *mutex ) +{ + return mutex_try_lock( mutex ); +} + +ldap_pvt_thread_t +ldap_pvt_thread_self( void ) +{ + return cthread_self(); +} + +int +ldap_pvt_thread_key_create( ldap_pvt_thread_key_t *key ) +{ + return cthread_keycreate( key ); +} + +int +ldap_pvt_thread_key_destroy( ldap_pvt_thread_key_t key ) +{ + return( 0 ); +} + +int +ldap_pvt_thread_key_setdata( ldap_pvt_thread_key_t key, void *data ) +{ + return cthread_setspecific( key, data ); +} + +int +ldap_pvt_thread_key_getdata( ldap_pvt_thread_key_t key, void **data ) +{ + return cthread_getspecific( key, data ); +} + +#endif /* HAVE_MACH_CTHREADS */ diff --git a/libraries/libldap_r/thr_debug.c b/libraries/libldap_r/thr_debug.c new file mode 100644 index 0000000..24ac74b --- /dev/null +++ b/libraries/libldap_r/thr_debug.c @@ -0,0 +1,1292 @@ +/* thr_debug.c - wrapper around the chosen thread wrapper, for debugging. */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2005-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +/* + * This package provides several types of thread operation debugging: + * + * - Check the results of operations on threads, mutexes, condition + * variables and read/write locks. Also check some thread pool + * operations, but not those for which failure can happen in normal + * slapd operation. + * + * - Wrap those types except threads and pools in structs with state + * information, and check that on all operations: + * + * + Check that the resources are initialized and are only used at + * their original address (i.e. not realloced or copied). + * + * + Check the owner (thread ID) on mutex operations. + * + * + Optionally allocate a reference to a byte of dummy memory. + * This lets malloc debuggers see some incorrect use as memory + * leaks, access to freed memory, etc. + * + * - Print an error message and by default abort() upon errors. + * + * - Print a count of leaked thread resources after cleanup. + * + * Compile-time (./configure) setup: Macros defined in CPPFLAGS. + * + * LDAP_THREAD_DEBUG or LDAP_THREAD_DEBUG=2 + * Enables debugging, but value & 2 turns off type wrapping. + * + * LDAP_UINTPTR_T=integer type to hold pointers, preferably unsigned. + * Used by dummy memory option "scramble". Default = unsigned long. + * + * LDAP_DEBUG_THREAD_NONE = initializer for a "no thread" thread ID. + * + * In addition, you may need to set up an implementation-specific way + * to enable whatever error checking your thread library provides. + * Currently only implemented for Posix threads (pthreads), where + * you may need to define LDAP_INT_THREAD_MUTEXATTR. The default + * is PTHREAD_MUTEX_ERRORCHECK, or PTHREAD_MUTEX_ERRORCHECK_NP for + * Linux threads. See pthread_mutexattr_settype(3). + * + * Run-time configuration: + * + * Memory debugging tools: + * Tools that report uninitialized memory accesses should disable + * such warnings about the function debug_already_initialized(). + * Alternatively, include "noreinit" (below) in $LDAP_THREAD_DEBUG. + * + * Environment variable $LDAP_THREAD_DEBUG: + * The variable may contain a comma- or space-separated option list. + * Options: + * off - Disable this package. (It still slows things down). + * tracethreads - Report create/join/exit/kill of threads. + * noabort - Do not abort() on errors. + * noerror - Do not report errors. Implies noabort. + * nocount - Do not report counts of unreleased resources. + * nosync - Disable tests that use synchronizaion and thus + * clearly affect thread scheduling: + * Implies nocount, and cancels threadID if that is set. + * Note that if you turn on tracethreads or malloc + * debugging, these also use library calls which may + * affect thread scheduling (fprintf and malloc). + * The following options do not apply if type wrapping is disabled: + * nomem - Do not check memory operations. + * Implies noreinit,noalloc. + * noreinit - Do not catch reinitialization of existing resources. + * (That test accesses uninitialized memory). + * threadID - Trace thread IDs. Currently mostly useless. + * Malloc debugging -- allocate dummy memory for initialized + * resources, so malloc debuggers will report them as memory leaks: + * noalloc - Default. Do not allocate dummy memory. + * alloc - Store a pointer to dummy memory. However, leak + * detectors might not catch unreleased resources in + * global variables. + * scramble - Store bitwise complement of dummy memory pointer. + * That never escapes memory leak detectors - + * but detection while the program is running will + * report active resources as leaks. Do not + * use this if a garbage collector is in use:-) + * adjptr - Point to end of dummy memory. + * Purify reports these as "potential leaks" (PLK). + * I have not checked other malloc debuggers. + */ + +#include "portable.h" + +#if defined( LDAP_THREAD_DEBUG ) + +#include <stdio.h> +#include <ac/errno.h> +#include <ac/stdlib.h> +#include <ac/string.h> + +#include "ldap_pvt_thread.h" /* Get the thread interface */ +#define LDAP_THREAD_IMPLEMENTATION +#define LDAP_THREAD_DEBUG_IMPLEMENTATION +#define LDAP_THREAD_RDWR_IMPLEMENTATION +#define LDAP_THREAD_POOL_IMPLEMENTATION +#include "ldap_thr_debug.h" /* Get the underlying implementation */ + +#ifndef LDAP_THREAD_DEBUG_WRAP +#undef LDAP_THREAD_DEBUG_THREAD_ID +#elif !defined LDAP_THREAD_DEBUG_THREAD_ID +#define LDAP_THREAD_DEBUG_THREAD_ID 1 +#endif + +/* Use native malloc - the OpenLDAP wrappers may defeat malloc debuggers */ +#undef malloc +#undef calloc +#undef realloc +#undef free + + +/* Options from environment variable $LDAP_THREAD_DEBUG */ +enum { Count_no = 0, Count_yes, Count_reported, Count_reported_more }; +static int count = Count_yes; +#ifdef LDAP_THREAD_DEBUG_WRAP +enum { Wrap_noalloc, Wrap_alloc, Wrap_scramble, Wrap_adjptr }; +static int wraptype = Wrap_noalloc, wrap_offset, unwrap_offset; +static int nomem, noreinit; +#endif +#if LDAP_THREAD_DEBUG_THREAD_ID +0 +static int threadID; +#else +enum { threadID = 0 }; +#endif +static int nodebug, noabort, noerror, nosync, tracethreads; +static int wrap_threads; +static int options_done; + + +/* ldap_pvt_thread_initialize() called, ldap_pvt_thread_destroy() not called */ +static int threading_enabled; + + +/* Resource counts */ +enum { + Idx_unexited_thread, Idx_unjoined_thread, Idx_locked_mutex, + Idx_mutex, Idx_cond, Idx_rdwr, Idx_tpool, Idx_max +}; +static int resource_counts[Idx_max]; +static const char *const resource_names[] = { + "unexited threads", "unjoined threads", "locked mutexes", + "mutexes", "conds", "rdwrs", "thread pools" +}; +static ldap_int_thread_mutex_t resource_mutexes[Idx_max]; + + +/* Hide pointers from malloc debuggers. */ +#define SCRAMBLE(ptr) (~(LDAP_UINTPTR_T) (ptr)) +#define UNSCRAMBLE_usagep(num) ((ldap_debug_usage_info_t *) ~(num)) +#define UNSCRAMBLE_dummyp(num) ((unsigned char *) ~(num)) + + +#define WARN(var, msg) (warn (__FILE__, __LINE__, (msg), #var, (var))) +#define WARN_IF(rc, msg) {if (rc) warn (__FILE__, __LINE__, (msg), #rc, (rc));} + +#define ERROR(var, msg) { \ + if (!noerror) { \ + errmsg(__FILE__, __LINE__, (msg), #var, (var)); \ + if( !noabort ) abort(); \ + } \ +} + +#define ERROR_IF(rc, msg) { \ + if (!noerror) { \ + int rc_ = (rc); \ + if (rc_) { \ + errmsg(__FILE__, __LINE__, (msg), #rc, rc_); \ + if( !noabort ) abort(); \ + } \ + } \ +} + +#ifdef LDAP_THREAD_DEBUG_WRAP +#define MEMERROR_IF(rc, msg, mem_act) { \ + if (!noerror) { \ + int rc_ = (rc); \ + if (rc_) { \ + errmsg(__FILE__, __LINE__, (msg), #rc, rc_); \ + if( wraptype != Wrap_noalloc ) { mem_act; } \ + if( !noabort ) abort(); \ + } \ + } \ +} +#endif /* LDAP_THREAD_DEBUG_WRAP */ + +#if 0 +static void +warn( const char *file, int line, const char *msg, const char *var, int val ) +{ + fprintf( stderr, + (strpbrk( var, "!=" ) + ? "%s:%d: %s warning: %s\n" + : "%s:%d: %s warning: %s is %d\n"), + file, line, msg, var, val ); +} +#endif + +static void +errmsg( const char *file, int line, const char *msg, const char *var, int val ) +{ + fprintf( stderr, + (strpbrk( var, "!=" ) + ? "%s:%d: %s error: %s\n" + : "%s:%d: %s error: %s is %d\n"), + file, line, msg, var, val ); +} + +static void +count_resource_leaks( void ) +{ + int i, j; + char errbuf[200]; + if( count == Count_yes ) { + count = Count_reported; +#if 0 /* Could break if there are still threads after atexit */ + for( i = j = 0; i < Idx_max; i++ ) + j |= ldap_int_thread_mutex_destroy( &resource_mutexes[i] ); + WARN_IF( j, "ldap_debug_thread_destroy:mutexes" ); +#endif + for( i = j = 0; i < Idx_max; i++ ) + if( resource_counts[i] ) + j += sprintf( errbuf + j, ", %d %s", + resource_counts[i], resource_names[i] ); + if( j ) + fprintf( stderr, "== thr_debug: Leaked%s. ==\n", errbuf + 1 ); + } +} + +static void +get_options( void ) +{ + static const struct option_info_s { + const char *name; + int *var, val; + } option_info[] = { + { "off", &nodebug, 1 }, + { "noabort", &noabort, 1 }, + { "noerror", &noerror, 1 }, + { "nocount", &count, Count_no }, + { "nosync", &nosync, 1 }, +#if LDAP_THREAD_DEBUG_THREAD_ID +0 + { "threadID", &threadID, 1 }, +#endif +#ifdef LDAP_THREAD_DEBUG_WRAP + { "nomem", &nomem, 1 }, + { "noreinit", &noreinit, 1 }, + { "noalloc", &wraptype, Wrap_noalloc }, + { "alloc", &wraptype, Wrap_alloc }, + { "adjptr", &wraptype, Wrap_adjptr }, + { "scramble", &wraptype, Wrap_scramble }, +#endif + { "tracethreads", &tracethreads, 1 }, + { NULL, NULL, 0 } + }; + const char *s = getenv( "LDAP_THREAD_DEBUG" ); + if( s != NULL ) { + while( *(s += strspn( s, ", \t\r\n" )) != '\0' ) { + size_t optlen = strcspn( s, ", \t\r\n" ); + const struct option_info_s *oi = option_info; + while( oi->name && + (strncasecmp( oi->name, s, optlen ) || oi->name[optlen]) ) + oi++; + if( oi->name ) + *oi->var = oi->val; + else + fprintf( stderr, + "== thr_debug: Unknown $%s option '%.*s' ==\n", + "LDAP_THREAD_DEBUG", (int) optlen, s ); + s += optlen; + } + } + if( nodebug ) { + tracethreads = 0; + nosync = noerror = 1; + } + if( nosync ) + count = Count_no; + if( noerror ) + noabort = 1; +#if LDAP_THREAD_DEBUG_THREAD_ID +0 + if( nosync ) + threadID = 0; +#endif +#ifdef LDAP_THREAD_DEBUG_WRAP + if( noerror ) + nomem = 1; + if( !nomem ) { + static const ldap_debug_usage_info_t usage; + if( sizeof(LDAP_UINTPTR_T) < sizeof(unsigned char *) + || sizeof(LDAP_UINTPTR_T) < sizeof(ldap_debug_usage_info_t *) + || UNSCRAMBLE_usagep( SCRAMBLE( &usage ) ) != &usage + || UNSCRAMBLE_dummyp( SCRAMBLE( (unsigned char *) 0 ) ) ) + { + fputs( "== thr_debug: Memory checks unsupported, " + "adding nomem to $LDAP_THREAD_DEBUG ==\n", stderr ); + nomem = 1; + } + } + if( nomem ) { + noreinit = 1; + wraptype = Wrap_noalloc; + } + unwrap_offset = -(wrap_offset = (wraptype == Wrap_adjptr)); +#endif + wrap_threads = (tracethreads || threadID || count); + options_done = 1; +} + + +#ifndef LDAP_THREAD_DEBUG_WRAP + +#define WRAPPED(ptr) (ptr) +#define GET_OWNER(ptr) 0 +#define SET_OWNER(ptr, thread) ((void) 0) +#define RESET_OWNER(ptr) ((void) 0) +#define ASSERT_OWNER(ptr, msg) ((void) 0) +#define ASSERT_NO_OWNER(ptr, msg) ((void) 0) + +#define init_usage(ptr, msg) ((void) 0) +#define check_usage(ptr, msg) ((void) 0) +#define destroy_usage(ptr) ((void) 0) + +#else /* LDAP_THREAD_DEBUG_WRAP */ + +/* Specialize this if the initializer is not appropriate. */ +/* The ASSERT_NO_OWNER() definition may also need an override. */ +#ifndef LDAP_DEBUG_THREAD_NONE +#define LDAP_DEBUG_THREAD_NONE { -1 } /* "no thread" ldap_int_thread_t value */ +#endif + +static const ldap_int_thread_t ldap_debug_thread_none = LDAP_DEBUG_THREAD_NONE; + +#define THREAD_MUTEX_OWNER(mutex) \ + ldap_int_thread_equal( (mutex)->owner, ldap_int_thread_self() ) + +void +ldap_debug_thread_assert_mutex_owner( + const char *file, + int line, + const char *msg, + ldap_pvt_thread_mutex_t *mutex ) +{ + if( !(noerror || THREAD_MUTEX_OWNER( mutex )) ) { + errmsg( file, line, msg, "ASSERT_MUTEX_OWNER", 0 ); + if( !noabort ) abort(); + } +} + +#define WRAPPED(ptr) (&(ptr)->wrapped) +#define GET_OWNER(ptr) ((ptr)->owner) +#define SET_OWNER(ptr, thread) ((ptr)->owner = (thread)) +#define RESET_OWNER(ptr) ((ptr)->owner = ldap_debug_thread_none) +#define ASSERT_OWNER(ptr, msg) ERROR_IF( !THREAD_MUTEX_OWNER( ptr ), msg ) +#ifndef ASSERT_NO_OWNER +#define ASSERT_NO_OWNER(ptr, msg) ERROR_IF( \ + !ldap_int_thread_equal( (ptr)->owner, ldap_debug_thread_none ), msg ) +#endif + +/* Try to provoke memory access error (for malloc debuggers) */ +#define PEEK(mem) {if (-*(volatile const unsigned char *)(mem)) debug_noop();} + +static void debug_noop( void ); +static int debug_already_initialized( const ldap_debug_usage_info_t *usage ); + +/* Name used for clearer error message */ +#define IS_COPY_OR_MOVED(usage) ((usage)->self != SCRAMBLE( usage )) + +#define DUMMY_ADDR(usage) \ + (wraptype == Wrap_scramble \ + ? UNSCRAMBLE_dummyp( (usage)->mem.num ) \ + : (usage)->mem.ptr + unwrap_offset) + +/* Mark resource as initialized */ +static void +init_usage( ldap_debug_usage_info_t *usage, const char *msg ) +{ + if( !options_done ) + get_options(); + if( !nomem ) { + if( !noreinit ) { + MEMERROR_IF( debug_already_initialized( usage ), msg, { + /* Provoke malloc debuggers */ + unsigned char *dummy = DUMMY_ADDR( usage ); + PEEK( dummy ); + free( dummy ); + free( dummy ); + } ); + } + if( wraptype != Wrap_noalloc ) { + unsigned char *dummy = malloc( 1 ); + assert( dummy != NULL ); + if( wraptype == Wrap_scramble ) { + usage->mem.num = SCRAMBLE( dummy ); + /* Verify that ptr<->integer casts work on this host */ + assert( UNSCRAMBLE_dummyp( usage->mem.num ) == dummy ); + } else { + usage->mem.ptr = dummy + wrap_offset; + } + } + } else { + /* Unused, but set for readability in debugger */ + usage->mem.ptr = NULL; + } + usage->self = SCRAMBLE( usage ); /* If nomem, only for debugger */ + usage->magic = ldap_debug_magic; + usage->state = ldap_debug_state_inited; +} + +/* Check that resource is initialized and not copied/realloced */ +static void +check_usage( const ldap_debug_usage_info_t *usage, const char *msg ) +{ + enum { Is_destroyed = 1 }; /* Name used for clearer error message */ + + if( usage->magic != ldap_debug_magic ) { + ERROR( usage->magic, msg ); + return; + } + switch( usage->state ) { + case ldap_debug_state_destroyed: + MEMERROR_IF( Is_destroyed, msg, { + PEEK( DUMMY_ADDR( usage ) ); + } ); + break; + default: + ERROR( usage->state, msg ); + break; + case ldap_debug_state_inited: + if( !nomem ) { + MEMERROR_IF( IS_COPY_OR_MOVED( usage ), msg, { + PEEK( DUMMY_ADDR( usage ) ); + PEEK( UNSCRAMBLE_usagep( usage->self ) ); + } ); + } + break; + } +} + +/* Mark resource as destroyed. */ +/* Does not check for errors, call check_usage()/init_usage() first. */ +static void +destroy_usage( ldap_debug_usage_info_t *usage ) +{ + if( usage->state == ldap_debug_state_inited ) { + if( wraptype != Wrap_noalloc ) { + free( DUMMY_ADDR( usage ) ); + /* Do not reset the DUMMY_ADDR, leave it for malloc debuggers + * in case the resource is used after it is freed. */ + } + usage->state = ldap_debug_state_destroyed; + } +} + +/* Define these after they are used, so they are hopefully not inlined */ + +static void +debug_noop( void ) +{ +} + +/* + * Valid programs access uninitialized memory here unless "noreinit". + * + * Returns true if the resource is initialized and not copied/realloced. + */ +LDAP_GCCATTR((noinline)) +static int +debug_already_initialized( const ldap_debug_usage_info_t *usage ) +{ + /* + * 'ret' keeps the Valgrind warning "Conditional jump or move + * depends on uninitialised value(s)" _inside_ this function. + */ + volatile int ret = 0; + if( usage->state == ldap_debug_state_inited ) + if( !IS_COPY_OR_MOVED( usage ) ) + if( usage->magic == ldap_debug_magic ) + ret = 1; + return ret; +} + +#endif /* LDAP_THREAD_DEBUG_WRAP */ + + +#if !(LDAP_THREAD_DEBUG_THREAD_ID +0) + +typedef void ldap_debug_thread_t; +#define init_thread_info() {} +#define with_thread_info_lock(statements) { statements; } +#define thread_info_detached(t) 0 +#define add_thread_info(msg, thr, det) ((void) 0) +#define remove_thread_info(tinfo, msg) ((void) 0) +#define get_thread_info(thread, msg) NULL + +#else /* LDAP_THREAD_DEBUG_THREAD_ID */ + +/* + * Thread ID tracking. Currently acieves little. + * Should be either expanded or deleted. + */ + +/* + * Array of threads. Used instead of making ldap_pvt_thread_t a wrapper + * around ldap_int_thread_t, which would slow down ldap_pvt_thread_self(). + */ +typedef struct { + ldap_pvt_thread_t wrapped; + ldap_debug_usage_info_t usage; + int detached; + int idx; +} ldap_debug_thread_t; + +static ldap_debug_thread_t **thread_info; +static unsigned int thread_info_size, thread_info_used; +static ldap_int_thread_mutex_t thread_info_mutex; + +#define init_thread_info() { \ + if( threadID ) { \ + int mutex_init_rc = ldap_int_thread_mutex_init( &thread_info_mutex ); \ + assert( mutex_init_rc == 0 ); \ + } \ +} + +#define with_thread_info_lock(statements) { \ + int rc_wtl_ = ldap_int_thread_mutex_lock( &thread_info_mutex ); \ + assert( rc_wtl_ == 0 ); \ + { statements; } \ + rc_wtl_ = ldap_int_thread_mutex_unlock( &thread_info_mutex ); \ + assert( rc_wtl_ == 0 ); \ +} + +#define thread_info_detached(t) ((t)->detached) + +static void +add_thread_info( + const char *msg, + const ldap_pvt_thread_t *thread, + int detached ) +{ + ldap_debug_thread_t *t; + + if( thread_info_used >= thread_info_size ) { + unsigned int more = thread_info_size + 8; + unsigned int new_size = thread_info_size + more; + + t = calloc( more, sizeof(ldap_debug_thread_t) ); + assert( t != NULL ); + thread_info = realloc( thread_info, new_size * sizeof(*thread_info) ); + assert( thread_info != NULL ); + do { + t->idx = thread_info_size; + thread_info[thread_info_size++] = t++; + } while( thread_info_size < new_size ); + } + + t = thread_info[thread_info_used]; + init_usage( &t->usage, msg ); + t->wrapped = *thread; + t->detached = detached; + thread_info_used++; +} + +static void +remove_thread_info( ldap_debug_thread_t *t, const char *msg ) +{ + ldap_debug_thread_t *last; + int idx; + check_usage( &t->usage, msg ); + destroy_usage( &t->usage ); + idx = t->idx; + assert( thread_info[idx] == t ); + last = thread_info[--thread_info_used]; + assert( last->idx == thread_info_used ); + (thread_info[idx] = last)->idx = idx; + (thread_info[thread_info_used] = t )->idx = thread_info_used; +} + +static ldap_debug_thread_t * +get_thread_info( ldap_pvt_thread_t thread, const char *msg ) +{ + unsigned int i; + ldap_debug_thread_t *t; + for( i = 0; i < thread_info_used; i++ ) { + if( ldap_pvt_thread_equal( thread, thread_info[i]->wrapped ) ) + break; + } + ERROR_IF( i == thread_info_used, msg ); + t = thread_info[i]; + check_usage( &t->usage, msg ); + return t; +} + +#endif /* LDAP_THREAD_DEBUG_THREAD_ID */ + + +static char * +thread_name( char *buf, int bufsize, ldap_pvt_thread_t thread ) +{ + int i; + --bufsize; + if( bufsize > 2*sizeof(thread) ) + bufsize = 2*sizeof(thread); + for( i = 0; i < bufsize; i += 2 ) + snprintf( buf+i, 3, "%02x", ((unsigned char *)&thread)[i/2] ); + return buf; +} + + +/* Add <adjust> (+/-1) to resource count <which> unless "nocount". */ +static void +adjust_count( int which, int adjust ) +{ + int rc; + switch( count ) { + case Count_no: + break; + case Count_yes: + rc = ldap_int_thread_mutex_lock( &resource_mutexes[which] ); + assert( rc == 0 ); + resource_counts[which] += adjust; + rc = ldap_int_thread_mutex_unlock( &resource_mutexes[which] ); + assert( rc == 0 ); + break; + case Count_reported: + fputs( "== thr_debug: More thread activity after exit ==\n", stderr ); + count = Count_reported_more; + /* FALL THROUGH */ + case Count_reported_more: + /* Not used, but result might be inspected with debugger */ + /* (Hopefully threading is disabled by now...) */ + resource_counts[which] += adjust; + break; + } +} + + +/* Wrappers for LDAP_THREAD_IMPLEMENTATION: */ + +/* Used instead of ldap_int_thread_initialize by ldap_pvt_thread_initialize */ +int +ldap_debug_thread_initialize( void ) +{ + int i, rc, rc2; + if( !options_done ) + get_options(); + ERROR_IF( threading_enabled, "ldap_debug_thread_initialize" ); + threading_enabled = 1; + rc = ldap_int_thread_initialize(); + if( rc ) { + ERROR( rc, "ldap_debug_thread_initialize:threads" ); + threading_enabled = 0; + } else { + init_thread_info(); + if( count != Count_no ) { + for( i = rc2 = 0; i < Idx_max; i++ ) + rc2 |= ldap_int_thread_mutex_init( &resource_mutexes[i] ); + assert( rc2 == 0 ); + /* FIXME: Only for static libldap_r as in init.c? If so, why? */ + atexit( count_resource_leaks ); + } + } + return rc; +} + +/* Used instead of ldap_int_thread_destroy by ldap_pvt_thread_destroy */ +int +ldap_debug_thread_destroy( void ) +{ + int rc; + ERROR_IF( !threading_enabled, "ldap_debug_thread_destroy" ); + /* sleep(1) -- need to wait for thread pool to finish? */ + rc = ldap_int_thread_destroy(); + if( rc ) { + ERROR( rc, "ldap_debug_thread_destroy:threads" ); + } else { + threading_enabled = 0; + } + return rc; +} + +int +ldap_pvt_thread_set_concurrency( int n ) +{ + int rc; + ERROR_IF( !threading_enabled, "ldap_pvt_thread_set_concurrency" ); + rc = ldap_int_thread_set_concurrency( n ); + ERROR_IF( rc, "ldap_pvt_thread_set_concurrency" ); + return rc; +} + +int +ldap_pvt_thread_get_concurrency( void ) +{ + int rc; + ERROR_IF( !threading_enabled, "ldap_pvt_thread_get_concurrency" ); + rc = ldap_int_thread_get_concurrency(); + ERROR_IF( rc, "ldap_pvt_thread_get_concurrency" ); + return rc; +} + +unsigned int +ldap_pvt_thread_sleep( unsigned int interval ) +{ + int rc; + ERROR_IF( !threading_enabled, "ldap_pvt_thread_sleep" ); + rc = ldap_int_thread_sleep( interval ); + ERROR_IF( rc, "ldap_pvt_thread_sleep" ); + return 0; +} + +static void +thread_exiting( const char *how, const char *msg ) +{ + ldap_pvt_thread_t thread; +#if 0 /* Detached threads may exit after ldap_debug_thread_destroy(). */ + ERROR_IF( !threading_enabled, msg ); +#endif + thread = ldap_pvt_thread_self(); + if( tracethreads ) { + char buf[40]; + fprintf( stderr, "== thr_debug: %s thread %s ==\n", + how, thread_name( buf, sizeof(buf), thread ) ); + } + if( threadID ) { + with_thread_info_lock({ + ldap_debug_thread_t *t = get_thread_info( thread, msg ); + if( thread_info_detached( t ) ) + remove_thread_info( t, msg ); + }); + } + adjust_count( Idx_unexited_thread, -1 ); +} + +void +ldap_pvt_thread_exit( void *retval ) +{ + thread_exiting( "Exiting", "ldap_pvt_thread_exit" ); + ldap_int_thread_exit( retval ); +} + +typedef struct { + void *(*start_routine)( void * ); + void *arg; +} ldap_debug_thread_call_t; + +static void * +ldap_debug_thread_wrapper( void *arg ) +{ + void *ret; + ldap_debug_thread_call_t call = *(ldap_debug_thread_call_t *)arg; + free( arg ); + ret = call.start_routine( call.arg ); + thread_exiting( "Returning from", "ldap_debug_thread_wrapper" ); + return ret; +} + +int +ldap_pvt_thread_create( + ldap_pvt_thread_t *thread, + int detach, + void *(*start_routine)( void * ), + void *arg ) +{ + int rc; + if( !options_done ) + get_options(); + ERROR_IF( !threading_enabled, "ldap_pvt_thread_create" ); + + if( wrap_threads ) { + ldap_debug_thread_call_t *call = malloc( + sizeof( ldap_debug_thread_call_t ) ); + assert( call != NULL ); + call->start_routine = start_routine; + call->arg = arg; + start_routine = ldap_debug_thread_wrapper; + arg = call; + } + if( threadID ) { + with_thread_info_lock({ + rc = ldap_int_thread_create( thread, detach, start_routine, arg ); + if( rc == 0 ) + add_thread_info( "ldap_pvt_thread_create", thread, detach ); + }); + } else { + rc = ldap_int_thread_create( thread, detach, start_routine, arg ); + } + if( rc ) { + ERROR( rc, "ldap_pvt_thread_create" ); + if( wrap_threads ) + free( arg ); + } else { + if( tracethreads ) { + char buf[40], buf2[40]; + fprintf( stderr, + "== thr_debug: Created thread %s%s from thread %s ==\n", + thread_name( buf, sizeof(buf), *thread ), + detach ? " (detached)" : "", + thread_name( buf2, sizeof(buf2), ldap_pvt_thread_self() ) ); + } + adjust_count( Idx_unexited_thread, +1 ); + if( !detach ) + adjust_count( Idx_unjoined_thread, +1 ); + } + return rc; +} + +int +ldap_pvt_thread_join( ldap_pvt_thread_t thread, void **thread_return ) +{ + int rc; + ldap_debug_thread_t *t = NULL; + ERROR_IF( !threading_enabled, "ldap_pvt_thread_join" ); + if( tracethreads ) { + char buf[40], buf2[40]; + fprintf( stderr, "== thr_debug: Joining thread %s in thread %s ==\n", + thread_name( buf, sizeof(buf), thread ), + thread_name( buf2, sizeof(buf2), ldap_pvt_thread_self() ) ); + } + if( threadID ) + with_thread_info_lock( { + t = get_thread_info( thread, "ldap_pvt_thread_join" ); + ERROR_IF( thread_info_detached( t ), "ldap_pvt_thread_join" ); + } ); + rc = ldap_int_thread_join( thread, thread_return ); + if( rc ) { + ERROR( rc, "ldap_pvt_thread_join" ); + } else { + if( threadID ) + with_thread_info_lock( + remove_thread_info( t, "ldap_pvt_thread_join" ) ); + adjust_count( Idx_unjoined_thread, -1 ); + } + + return rc; +} + +int +ldap_pvt_thread_kill( ldap_pvt_thread_t thread, int signo ) +{ + int rc; + ERROR_IF( !threading_enabled, "ldap_pvt_thread_kill" ); + if( tracethreads ) { + char buf[40], buf2[40]; + fprintf( stderr, + "== thr_debug: Killing thread %s (sig %i) from thread %s ==\n", + thread_name( buf, sizeof(buf), thread ), signo, + thread_name( buf2, sizeof(buf2), ldap_pvt_thread_self() ) ); + } + rc = ldap_int_thread_kill( thread, signo ); + ERROR_IF( rc, "ldap_pvt_thread_kill" ); + return rc; +} + +int +ldap_pvt_thread_yield( void ) +{ + int rc; + ERROR_IF( !threading_enabled, "ldap_pvt_thread_yield" ); + rc = ldap_int_thread_yield(); + ERROR_IF( rc, "ldap_pvt_thread_yield" ); + return rc; +} + +ldap_pvt_thread_t +ldap_pvt_thread_self( void ) +{ +#if 0 /* Function is used by ch_free() via slap_sl_contxt() in slapd */ + ERROR_IF( !threading_enabled, "ldap_pvt_thread_self" ); +#endif + return ldap_int_thread_self(); +} + +int +ldap_pvt_thread_cond_init( ldap_pvt_thread_cond_t *cond ) +{ + int rc; + init_usage( &cond->usage, "ldap_pvt_thread_cond_init" ); + rc = ldap_int_thread_cond_init( WRAPPED( cond ) ); + if( rc ) { + ERROR( rc, "ldap_pvt_thread_cond_init" ); + destroy_usage( &cond->usage ); + } else { + adjust_count( Idx_cond, +1 ); + } + return rc; +} + +int +ldap_pvt_thread_cond_destroy( ldap_pvt_thread_cond_t *cond ) +{ + int rc; + check_usage( &cond->usage, "ldap_pvt_thread_cond_destroy" ); + rc = ldap_int_thread_cond_destroy( WRAPPED( cond ) ); + if( rc ) { + ERROR( rc, "ldap_pvt_thread_cond_destroy" ); + } else { + destroy_usage( &cond->usage ); + adjust_count( Idx_cond, -1 ); + } + return rc; +} + +int +ldap_pvt_thread_cond_signal( ldap_pvt_thread_cond_t *cond ) +{ + int rc; + check_usage( &cond->usage, "ldap_pvt_thread_cond_signal" ); + rc = ldap_int_thread_cond_signal( WRAPPED( cond ) ); + ERROR_IF( rc, "ldap_pvt_thread_cond_signal" ); + return rc; +} + +int +ldap_pvt_thread_cond_broadcast( ldap_pvt_thread_cond_t *cond ) +{ + int rc; + check_usage( &cond->usage, "ldap_pvt_thread_cond_broadcast" ); + rc = ldap_int_thread_cond_broadcast( WRAPPED( cond ) ); + ERROR_IF( rc, "ldap_pvt_thread_cond_broadcast" ); + return rc; +} + +int +ldap_pvt_thread_cond_wait( + ldap_pvt_thread_cond_t *cond, + ldap_pvt_thread_mutex_t *mutex ) +{ + int rc; + ldap_int_thread_t owner; + check_usage( &cond->usage, "ldap_pvt_thread_cond_wait:cond" ); + check_usage( &mutex->usage, "ldap_pvt_thread_cond_wait:mutex" ); + adjust_count( Idx_locked_mutex, -1 ); + owner = GET_OWNER( mutex ); + ASSERT_OWNER( mutex, "ldap_pvt_thread_cond_wait" ); + RESET_OWNER( mutex ); + rc = ldap_int_thread_cond_wait( WRAPPED( cond ), WRAPPED( mutex ) ); + ASSERT_NO_OWNER( mutex, "ldap_pvt_thread_cond_wait" ); + SET_OWNER( mutex, rc ? owner : ldap_int_thread_self() ); + adjust_count( Idx_locked_mutex, +1 ); + ERROR_IF( rc, "ldap_pvt_thread_cond_wait" ); + return rc; +} + +int +ldap_pvt_thread_mutex_init( ldap_pvt_thread_mutex_t *mutex ) +{ + int rc; + init_usage( &mutex->usage, "ldap_pvt_thread_mutex_init" ); + rc = ldap_int_thread_mutex_init( WRAPPED( mutex ) ); + if( rc ) { + ERROR( rc, "ldap_pvt_thread_mutex_init" ); + destroy_usage( &mutex->usage ); + } else { + RESET_OWNER( mutex ); + adjust_count( Idx_mutex, +1 ); + } + return rc; +} + +int +ldap_pvt_thread_mutex_destroy( ldap_pvt_thread_mutex_t *mutex ) +{ + int rc; + check_usage( &mutex->usage, "ldap_pvt_thread_mutex_destroy" ); + ASSERT_NO_OWNER( mutex, "ldap_pvt_thread_mutex_destroy" ); + rc = ldap_int_thread_mutex_destroy( WRAPPED( mutex ) ); + if( rc ) { + ERROR( rc, "ldap_pvt_thread_mutex_destroy" ); + } else { + destroy_usage( &mutex->usage ); + RESET_OWNER( mutex ); + adjust_count( Idx_mutex, -1 ); + } + return rc; +} + +int +ldap_pvt_thread_mutex_lock( ldap_pvt_thread_mutex_t *mutex ) +{ + int rc; + check_usage( &mutex->usage, "ldap_pvt_thread_mutex_lock" ); + rc = ldap_int_thread_mutex_lock( WRAPPED( mutex ) ); + if( rc ) { + ERROR_IF( rc, "ldap_pvt_thread_mutex_lock" ); + } else { + ASSERT_NO_OWNER( mutex, "ldap_pvt_thread_mutex_lock" ); + SET_OWNER( mutex, ldap_int_thread_self() ); + adjust_count( Idx_locked_mutex, +1 ); + } + return rc; +} + +int +ldap_pvt_thread_mutex_trylock( ldap_pvt_thread_mutex_t *mutex ) +{ + int rc; + check_usage( &mutex->usage, "ldap_pvt_thread_mutex_trylock" ); + rc = ldap_int_thread_mutex_trylock( WRAPPED( mutex ) ); + if( rc == 0 ) { + ASSERT_NO_OWNER( mutex, "ldap_pvt_thread_mutex_trylock" ); + SET_OWNER( mutex, ldap_int_thread_self() ); + adjust_count( Idx_locked_mutex, +1 ); + } + return rc; +} + +int +ldap_pvt_thread_mutex_unlock( ldap_pvt_thread_mutex_t *mutex ) +{ + int rc; + check_usage( &mutex->usage, "ldap_pvt_thread_mutex_unlock" ); + ASSERT_OWNER( mutex, "ldap_pvt_thread_mutex_unlock" ); + RESET_OWNER( mutex ); /* Breaks if this thread did not own the mutex */ + rc = ldap_int_thread_mutex_unlock( WRAPPED( mutex ) ); + if( rc ) { + ERROR_IF( rc, "ldap_pvt_thread_mutex_unlock" ); + } else { + adjust_count( Idx_locked_mutex, -1 ); + } + return rc; +} + + +/* Wrappers for LDAP_THREAD_RDWR_IMPLEMENTATION: */ + +int +ldap_pvt_thread_rdwr_init( ldap_pvt_thread_rdwr_t *rwlock ) +{ + int rc; + init_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_init" ); + rc = ldap_int_thread_rdwr_init( WRAPPED( rwlock ) ); + if( rc ) { + ERROR( rc, "ldap_pvt_thread_rdwr_init" ); + destroy_usage( &rwlock->usage ); + } else { + adjust_count( Idx_rdwr, +1 ); + } + return rc; +} + +int +ldap_pvt_thread_rdwr_destroy( ldap_pvt_thread_rdwr_t *rwlock ) +{ + int rc; + check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_destroy" ); + rc = ldap_int_thread_rdwr_destroy( WRAPPED( rwlock ) ); + if( rc ) { + ERROR( rc, "ldap_pvt_thread_rdwr_destroy" ); + } else { + destroy_usage( &rwlock->usage ); + adjust_count( Idx_rdwr, -1 ); + } + return rc; +} + +int +ldap_pvt_thread_rdwr_rlock( ldap_pvt_thread_rdwr_t *rwlock ) +{ + int rc; + check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_rlock" ); + rc = ldap_int_thread_rdwr_rlock( WRAPPED( rwlock ) ); + ERROR_IF( rc, "ldap_pvt_thread_rdwr_rlock" ); + return rc; +} + +int +ldap_pvt_thread_rdwr_rtrylock( ldap_pvt_thread_rdwr_t *rwlock ) +{ + check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_rtrylock" ); + return ldap_int_thread_rdwr_rtrylock( WRAPPED( rwlock ) ); +} + +int +ldap_pvt_thread_rdwr_runlock( ldap_pvt_thread_rdwr_t *rwlock ) +{ + int rc; + check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_runlock" ); + rc = ldap_int_thread_rdwr_runlock( WRAPPED( rwlock ) ); + ERROR_IF( rc, "ldap_pvt_thread_rdwr_runlock" ); + return rc; +} + +int +ldap_pvt_thread_rdwr_wlock( ldap_pvt_thread_rdwr_t *rwlock ) +{ + int rc; + check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_wlock" ); + rc = ldap_int_thread_rdwr_wlock( WRAPPED( rwlock ) ); + ERROR_IF( rc, "ldap_pvt_thread_rdwr_wlock" ); + return rc; +} + +int +ldap_pvt_thread_rdwr_wtrylock( ldap_pvt_thread_rdwr_t *rwlock ) +{ + check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_wtrylock" ); + return ldap_int_thread_rdwr_wtrylock( WRAPPED( rwlock ) ); +} + +int +ldap_pvt_thread_rdwr_wunlock( ldap_pvt_thread_rdwr_t *rwlock ) +{ + int rc; + check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_wunlock" ); + rc = ldap_int_thread_rdwr_wunlock( WRAPPED( rwlock ) ); + ERROR_IF( rc, "ldap_pvt_thread_rdwr_wunlock" ); + return rc; +} + +#if defined(LDAP_RDWR_DEBUG) && !defined(LDAP_THREAD_HAVE_RDWR) + +int +ldap_pvt_thread_rdwr_readers( ldap_pvt_thread_rdwr_t *rwlock ) +{ + check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_readers" ); + return ldap_int_thread_rdwr_readers( WRAPPED( rwlock ) ); +} + +int +ldap_pvt_thread_rdwr_writers( ldap_pvt_thread_rdwr_t *rwlock ) +{ + check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_writers" ); + return ldap_int_thread_rdwr_writers( WRAPPED( rwlock ) ); +} + +int +ldap_pvt_thread_rdwr_active( ldap_pvt_thread_rdwr_t *rwlock ) +{ + check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_active" ); + return ldap_int_thread_rdwr_active( WRAPPED( rwlock ) ); +} + +#endif /* LDAP_RDWR_DEBUG && !LDAP_THREAD_HAVE_RDWR */ + + +/* Some wrappers for LDAP_THREAD_POOL_IMPLEMENTATION: */ +#ifdef LDAP_THREAD_POOL_IMPLEMENTATION + +int +ldap_pvt_thread_pool_init( + ldap_pvt_thread_pool_t *tpool, + int max_threads, + int max_pending ) +{ + int rc; + if( !options_done ) + get_options(); + ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_init" ); + rc = ldap_int_thread_pool_init( tpool, max_threads, max_pending ); + if( rc ) { + ERROR( rc, "ldap_pvt_thread_pool_init" ); + } else { + adjust_count( Idx_tpool, +1 ); + } + return rc; +} + +int +ldap_pvt_thread_pool_submit( + ldap_pvt_thread_pool_t *tpool, + ldap_pvt_thread_start_t *start_routine, void *arg ) +{ + int rc, has_pool; + ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_submit" ); + has_pool = (tpool && *tpool); + rc = ldap_int_thread_pool_submit( tpool, start_routine, arg ); + if( has_pool ) + ERROR_IF( rc, "ldap_pvt_thread_pool_submit" ); + return rc; +} + +int +ldap_pvt_thread_pool_maxthreads( + ldap_pvt_thread_pool_t *tpool, + int max_threads ) +{ + ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_maxthreads" ); + return ldap_int_thread_pool_maxthreads( tpool, max_threads ); +} + +int +ldap_pvt_thread_pool_backload( ldap_pvt_thread_pool_t *tpool ) +{ + ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_backload" ); + return ldap_int_thread_pool_backload( tpool ); +} + +int +ldap_pvt_thread_pool_destroy( ldap_pvt_thread_pool_t *tpool, int run_pending ) +{ + int rc, has_pool; + ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_destroy" ); + has_pool = (tpool && *tpool); + rc = ldap_int_thread_pool_destroy( tpool, run_pending ); + if( has_pool ) { + if( rc ) { + ERROR( rc, "ldap_pvt_thread_pool_destroy" ); + } else { + adjust_count( Idx_tpool, -1 ); + } + } + return rc; +} + +int +ldap_pvt_thread_pool_pause( ldap_pvt_thread_pool_t *tpool ) +{ + ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_pause" ); + return ldap_int_thread_pool_pause( tpool ); +} + +int +ldap_pvt_thread_pool_resume( ldap_pvt_thread_pool_t *tpool ) +{ + ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_resume" ); + return ldap_int_thread_pool_resume( tpool ); +} + +int +ldap_pvt_thread_pool_getkey( + void *xctx, + void *key, + void **data, + ldap_pvt_thread_pool_keyfree_t **kfree ) +{ +#if 0 /* Function is used by ch_free() via slap_sl_contxt() in slapd */ + ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_getkey" ); +#endif + return ldap_int_thread_pool_getkey( xctx, key, data, kfree ); +} + +int +ldap_pvt_thread_pool_setkey( + void *xctx, + void *key, + void *data, + ldap_pvt_thread_pool_keyfree_t *kfree, + void **olddatap, + ldap_pvt_thread_pool_keyfree_t **oldkfreep ) +{ + int rc; + ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_setkey" ); + rc = ldap_int_thread_pool_setkey( + xctx, key, data, kfree, olddatap, oldkfreep ); + ERROR_IF( rc, "ldap_pvt_thread_pool_setkey" ); + return rc; +} + +void +ldap_pvt_thread_pool_purgekey( void *key ) +{ + ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_purgekey" ); + ldap_int_thread_pool_purgekey( key ); +} + +void * +ldap_pvt_thread_pool_context( void ) +{ +#if 0 /* Function is used by ch_free() via slap_sl_contxt() in slapd */ + ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_context" ); +#endif + return ldap_int_thread_pool_context(); +} + +void +ldap_pvt_thread_pool_context_reset( void *vctx ) +{ + ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_context_reset" ); + ldap_int_thread_pool_context_reset( vctx ); +} + +#endif /* LDAP_THREAD_POOL_IMPLEMENTATION */ + +#endif /* LDAP_THREAD_DEBUG */ diff --git a/libraries/libldap_r/thr_nt.c b/libraries/libldap_r/thr_nt.c new file mode 100644 index 0000000..1de232d --- /dev/null +++ b/libraries/libldap_r/thr_nt.c @@ -0,0 +1,245 @@ +/* thr_nt.c - wrapper around NT threads */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#if defined( HAVE_NT_THREADS ) + +#define _WIN32_WINNT 0x0400 +#include <windows.h> +#include <process.h> + +#include "ldap_pvt_thread.h" /* Get the thread interface */ +#define LDAP_THREAD_IMPLEMENTATION +#include "ldap_thr_debug.h" /* May rename the symbols defined below */ + +typedef struct ldap_int_thread_s { + long tid; + HANDLE thd; +} ldap_int_thread_s; + +#ifndef NT_MAX_THREADS +#define NT_MAX_THREADS 1024 +#endif + +static ldap_int_thread_s tids[NT_MAX_THREADS]; +static int ntids; + + +/* mingw compiler very sensitive about getting prototypes right */ +typedef unsigned __stdcall thrfunc_t(void *); + +int +ldap_int_thread_initialize( void ) +{ + return 0; +} + +int +ldap_int_thread_destroy( void ) +{ + return 0; +} + +int +ldap_int_mutex_firstcreate( ldap_int_thread_mutex_t *mutex ) +{ + if ( *mutex == NULL ) { + HANDLE p = CreateMutex( NULL, 0, NULL ); + if ( InterlockedCompareExchangePointer((PVOID*)mutex, (PVOID)p, NULL) != NULL) + CloseHandle( p ); + } + return 0; +} + +int +ldap_pvt_thread_create( ldap_pvt_thread_t * thread, + int detach, + void *(*start_routine)( void *), + void *arg) +{ + unsigned tid; + HANDLE thd; + int rc = -1; + + thd = (HANDLE) _beginthreadex(NULL, LDAP_PVT_THREAD_STACK_SIZE, (thrfunc_t *) start_routine, + arg, 0, &tid); + + if ( thd ) { + *thread = (ldap_pvt_thread_t) tid; + tids[ntids].tid = tid; + tids[ntids].thd = thd; + ntids++; + rc = 0; + } + return rc; +} + +void +ldap_pvt_thread_exit( void *retval ) +{ + _endthread( ); +} + +int +ldap_pvt_thread_join( ldap_pvt_thread_t thread, void **thread_return ) +{ + DWORD status; + int i; + + for (i=0; i<ntids; i++) { + if ( tids[i].tid == thread ) + break; + } + if ( i > ntids ) return -1; + + status = WaitForSingleObject( tids[i].thd, INFINITE ); + for (; i<ntids; i++) { + tids[i] = tids[i+1]; + } + ntids--; + return status == WAIT_FAILED ? -1 : 0; +} + +int +ldap_pvt_thread_kill( ldap_pvt_thread_t thread, int signo ) +{ + return 0; +} + +int +ldap_pvt_thread_yield( void ) +{ + Sleep( 0 ); + return 0; +} + +int +ldap_pvt_thread_cond_init( ldap_pvt_thread_cond_t *cond ) +{ + *cond = CreateEvent( NULL, FALSE, FALSE, NULL ); + return( 0 ); +} + +int +ldap_pvt_thread_cond_destroy( ldap_pvt_thread_cond_t *cv ) +{ + CloseHandle( *cv ); + return( 0 ); +} + +int +ldap_pvt_thread_cond_signal( ldap_pvt_thread_cond_t *cond ) +{ + SetEvent( *cond ); + return( 0 ); +} + +int +ldap_pvt_thread_cond_wait( ldap_pvt_thread_cond_t *cond, + ldap_pvt_thread_mutex_t *mutex ) +{ + SignalObjectAndWait( *mutex, *cond, INFINITE, FALSE ); + WaitForSingleObject( *mutex, INFINITE ); + return( 0 ); +} + +int +ldap_pvt_thread_cond_broadcast( ldap_pvt_thread_cond_t *cond ) +{ + while ( WaitForSingleObject( *cond, 0 ) == WAIT_TIMEOUT ) + SetEvent( *cond ); + return( 0 ); +} + +int +ldap_pvt_thread_mutex_init( ldap_pvt_thread_mutex_t *mutex ) +{ + *mutex = CreateMutex( NULL, 0, NULL ); + return ( 0 ); +} + +int +ldap_pvt_thread_mutex_destroy( ldap_pvt_thread_mutex_t *mutex ) +{ + CloseHandle( *mutex ); + return ( 0 ); +} + +int +ldap_pvt_thread_mutex_lock( ldap_pvt_thread_mutex_t *mutex ) +{ + DWORD status; + status = WaitForSingleObject( *mutex, INFINITE ); + return status == WAIT_FAILED ? -1 : 0; +} + +int +ldap_pvt_thread_mutex_unlock( ldap_pvt_thread_mutex_t *mutex ) +{ + ReleaseMutex( *mutex ); + return ( 0 ); +} + +int +ldap_pvt_thread_mutex_trylock( ldap_pvt_thread_mutex_t *mp ) +{ + DWORD status; + status = WaitForSingleObject( *mp, 0 ); + return status == WAIT_FAILED || status == WAIT_TIMEOUT + ? -1 : 0; +} + +ldap_pvt_thread_t +ldap_pvt_thread_self( void ) +{ + return GetCurrentThreadId(); +} + +int +ldap_pvt_thread_key_create( ldap_pvt_thread_key_t *keyp ) +{ + DWORD key = TlsAlloc(); + if ( key != TLS_OUT_OF_INDEXES ) { + *keyp = key; + return 0; + } else { + return -1; + } +} + +int +ldap_pvt_thread_key_destroy( ldap_pvt_thread_key_t key ) +{ + /* TlsFree returns 0 on failure */ + return( TlsFree( key ) == 0 ); +} + +int +ldap_pvt_thread_key_setdata( ldap_pvt_thread_key_t key, void *data ) +{ + return ( TlsSetValue( key, data ) == 0 ); +} + +int +ldap_pvt_thread_key_getdata( ldap_pvt_thread_key_t key, void **data ) +{ + void *ptr = TlsGetValue( key ); + *data = ptr; + return( ptr ? GetLastError() : 0 ); +} + +#endif diff --git a/libraries/libldap_r/thr_posix.c b/libraries/libldap_r/thr_posix.c new file mode 100644 index 0000000..e4b4357 --- /dev/null +++ b/libraries/libldap_r/thr_posix.c @@ -0,0 +1,388 @@ +/* thr_posix.c - wrapper around posix and posixish thread implementations. */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#if defined( HAVE_PTHREADS ) + +#include <ac/errno.h> + +#ifdef REPLACE_BROKEN_YIELD +#ifndef HAVE_NANOSLEEP +#include <ac/socket.h> +#endif +#include <ac/time.h> +#endif + +#include "ldap_pvt_thread.h" /* Get the thread interface */ +#define LDAP_THREAD_IMPLEMENTATION +#define LDAP_THREAD_RDWR_IMPLEMENTATION +#include "ldap_thr_debug.h" /* May rename the symbols defined below */ +#include <signal.h> /* For pthread_kill() */ + +#if HAVE_PTHREADS < 6 +# define LDAP_INT_THREAD_ATTR_DEFAULT pthread_attr_default +# define LDAP_INT_THREAD_CONDATTR_DEFAULT pthread_condattr_default +# define LDAP_INT_THREAD_MUTEXATTR_DEFAULT pthread_mutexattr_default +#else +# define LDAP_INT_THREAD_ATTR_DEFAULT NULL +# define LDAP_INT_THREAD_CONDATTR_DEFAULT NULL +# define LDAP_INT_THREAD_MUTEXATTR_DEFAULT NULL +#endif + +#ifdef LDAP_THREAD_DEBUG +# if defined LDAP_INT_THREAD_MUTEXATTR /* May be defined in CPPFLAGS */ +# elif defined HAVE_PTHREAD_KILL_OTHER_THREADS_NP + /* LinuxThreads hack */ +# define LDAP_INT_THREAD_MUTEXATTR PTHREAD_MUTEX_ERRORCHECK_NP +# else +# define LDAP_INT_THREAD_MUTEXATTR PTHREAD_MUTEX_ERRORCHECK +# endif +static pthread_mutexattr_t mutex_attr; +# undef LDAP_INT_THREAD_MUTEXATTR_DEFAULT +# define LDAP_INT_THREAD_MUTEXATTR_DEFAULT &mutex_attr +#endif + +#if HAVE_PTHREADS < 7 +#define ERRVAL(val) ((val) < 0 ? errno : 0) +#else +#define ERRVAL(val) (val) +#endif + +int +ldap_int_thread_initialize( void ) +{ +#ifdef LDAP_INT_THREAD_MUTEXATTR + pthread_mutexattr_init( &mutex_attr ); + pthread_mutexattr_settype( &mutex_attr, LDAP_INT_THREAD_MUTEXATTR ); +#endif + return 0; +} + +int +ldap_int_thread_destroy( void ) +{ +#ifdef HAVE_PTHREAD_KILL_OTHER_THREADS_NP + /* LinuxThreads: kill clones */ + pthread_kill_other_threads_np(); +#endif +#ifdef LDAP_INT_THREAD_MUTEXATTR + pthread_mutexattr_destroy( &mutex_attr ); +#endif + return 0; +} + +#ifdef LDAP_THREAD_HAVE_SETCONCURRENCY +int +ldap_pvt_thread_set_concurrency(int n) +{ +#ifdef HAVE_PTHREAD_SETCONCURRENCY + return pthread_setconcurrency( n ); +#elif defined(HAVE_THR_SETCONCURRENCY) + return thr_setconcurrency( n ); +#else + return 0; +#endif +} +#endif + +#ifdef LDAP_THREAD_HAVE_GETCONCURRENCY +int +ldap_pvt_thread_get_concurrency(void) +{ +#ifdef HAVE_PTHREAD_GETCONCURRENCY + return pthread_getconcurrency(); +#elif defined(HAVE_THR_GETCONCURRENCY) + return thr_getconcurrency(); +#else + return 0; +#endif +} +#endif + +/* detachstate appeared in Draft 6, but without manifest constants. + * in Draft 7 they were called PTHREAD_CREATE_UNDETACHED and ...DETACHED. + * in Draft 8 on, ...UNDETACHED became ...JOINABLE. + */ +#ifndef PTHREAD_CREATE_JOINABLE +#ifdef PTHREAD_CREATE_UNDETACHED +#define PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED +#else +#define PTHREAD_CREATE_JOINABLE 0 +#endif +#endif + +#ifndef PTHREAD_CREATE_DETACHED +#define PTHREAD_CREATE_DETACHED 1 +#endif + +int +ldap_pvt_thread_create( ldap_pvt_thread_t * thread, + int detach, + void *(*start_routine)( void * ), + void *arg) +{ + int rtn; + pthread_attr_t attr; + +/* Always create the thread attrs, so we can set stacksize if we need to */ +#if HAVE_PTHREADS > 5 + pthread_attr_init(&attr); +#else + pthread_attr_create(&attr); +#endif + +#ifdef LDAP_PVT_THREAD_SET_STACK_SIZE + /* this should be tunable */ + pthread_attr_setstacksize( &attr, LDAP_PVT_THREAD_STACK_SIZE ); +#endif + +#if HAVE_PTHREADS > 5 + detach = detach ? PTHREAD_CREATE_DETACHED : PTHREAD_CREATE_JOINABLE; +#if HAVE_PTHREADS == 6 + pthread_attr_setdetachstate(&attr, &detach); +#else + pthread_attr_setdetachstate(&attr, detach); +#endif +#endif + +#if HAVE_PTHREADS < 5 + rtn = pthread_create( thread, attr, start_routine, arg ); +#else + rtn = pthread_create( thread, &attr, start_routine, arg ); +#endif + +#if HAVE_PTHREADS > 5 + pthread_attr_destroy(&attr); +#else + pthread_attr_delete(&attr); + if( detach ) { + pthread_detach( thread ); + } +#endif + +#if HAVE_PTHREADS < 7 + if ( rtn < 0 ) rtn = errno; +#endif + return rtn; +} + +void +ldap_pvt_thread_exit( void *retval ) +{ + pthread_exit( retval ); +} + +int +ldap_pvt_thread_join( ldap_pvt_thread_t thread, void **thread_return ) +{ +#if HAVE_PTHREADS < 7 + void *dummy; + if (thread_return==NULL) + thread_return=&dummy; +#endif + return ERRVAL( pthread_join( thread, thread_return ) ); +} + +int +ldap_pvt_thread_kill( ldap_pvt_thread_t thread, int signo ) +{ +#if defined(HAVE_PTHREAD_KILL) && HAVE_PTHREADS > 4 + /* MacOS 10.1 is detected as v10 but has no pthread_kill() */ + return ERRVAL( pthread_kill( thread, signo ) ); +#else + /* pthread package with DCE */ + if (kill( getpid(), signo )<0) + return errno; + return 0; +#endif +} + +int +ldap_pvt_thread_yield( void ) +{ +#ifdef REPLACE_BROKEN_YIELD +#ifdef HAVE_NANOSLEEP + struct timespec t = { 0, 0 }; + nanosleep(&t, NULL); +#else + struct timeval tv = {0,0}; + select( 0, NULL, NULL, NULL, &tv ); +#endif + return 0; + +#elif defined(HAVE_THR_YIELD) + thr_yield(); + return 0; + +#elif HAVE_PTHREADS == 10 + return sched_yield(); + +#elif defined(_POSIX_THREAD_IS_GNU_PTH) + sched_yield(); + return 0; + +#elif HAVE_PTHREADS == 6 + pthread_yield(NULL); + return 0; + +#else + pthread_yield(); + return 0; +#endif +} + +int +ldap_pvt_thread_cond_init( ldap_pvt_thread_cond_t *cond ) +{ + return ERRVAL( pthread_cond_init( + cond, LDAP_INT_THREAD_CONDATTR_DEFAULT ) ); +} + +int +ldap_pvt_thread_cond_destroy( ldap_pvt_thread_cond_t *cond ) +{ + return ERRVAL( pthread_cond_destroy( cond ) ); +} + +int +ldap_pvt_thread_cond_signal( ldap_pvt_thread_cond_t *cond ) +{ + return ERRVAL( pthread_cond_signal( cond ) ); +} + +int +ldap_pvt_thread_cond_broadcast( ldap_pvt_thread_cond_t *cond ) +{ + return ERRVAL( pthread_cond_broadcast( cond ) ); +} + +int +ldap_pvt_thread_cond_wait( ldap_pvt_thread_cond_t *cond, + ldap_pvt_thread_mutex_t *mutex ) +{ + return ERRVAL( pthread_cond_wait( cond, mutex ) ); +} + +int +ldap_pvt_thread_mutex_init( ldap_pvt_thread_mutex_t *mutex ) +{ + return ERRVAL( pthread_mutex_init( + mutex, LDAP_INT_THREAD_MUTEXATTR_DEFAULT ) ); +} + +int +ldap_pvt_thread_mutex_destroy( ldap_pvt_thread_mutex_t *mutex ) +{ + return ERRVAL( pthread_mutex_destroy( mutex ) ); +} + +int +ldap_pvt_thread_mutex_lock( ldap_pvt_thread_mutex_t *mutex ) +{ + return ERRVAL( pthread_mutex_lock( mutex ) ); +} + +int +ldap_pvt_thread_mutex_trylock( ldap_pvt_thread_mutex_t *mutex ) +{ + return ERRVAL( pthread_mutex_trylock( mutex ) ); +} + +int +ldap_pvt_thread_mutex_unlock( ldap_pvt_thread_mutex_t *mutex ) +{ + return ERRVAL( pthread_mutex_unlock( mutex ) ); +} + +ldap_pvt_thread_t ldap_pvt_thread_self( void ) +{ + return pthread_self(); +} + +int +ldap_pvt_thread_key_create( ldap_pvt_thread_key_t *key ) +{ + return pthread_key_create( key, NULL ); +} + +int +ldap_pvt_thread_key_destroy( ldap_pvt_thread_key_t key ) +{ + return pthread_key_delete( key ); +} + +int +ldap_pvt_thread_key_setdata( ldap_pvt_thread_key_t key, void *data ) +{ + return pthread_setspecific( key, data ); +} + +int +ldap_pvt_thread_key_getdata( ldap_pvt_thread_key_t key, void **data ) +{ + *data = pthread_getspecific( key ); + return 0; +} + +#ifdef LDAP_THREAD_HAVE_RDWR +#ifdef HAVE_PTHREAD_RWLOCK_DESTROY +int +ldap_pvt_thread_rdwr_init( ldap_pvt_thread_rdwr_t *rw ) +{ + return ERRVAL( pthread_rwlock_init( rw, NULL ) ); +} + +int +ldap_pvt_thread_rdwr_destroy( ldap_pvt_thread_rdwr_t *rw ) +{ + return ERRVAL( pthread_rwlock_destroy( rw ) ); +} + +int ldap_pvt_thread_rdwr_rlock( ldap_pvt_thread_rdwr_t *rw ) +{ + return ERRVAL( pthread_rwlock_rdlock( rw ) ); +} + +int ldap_pvt_thread_rdwr_rtrylock( ldap_pvt_thread_rdwr_t *rw ) +{ + return ERRVAL( pthread_rwlock_tryrdlock( rw ) ); +} + +int ldap_pvt_thread_rdwr_runlock( ldap_pvt_thread_rdwr_t *rw ) +{ + return ERRVAL( pthread_rwlock_unlock( rw ) ); +} + +int ldap_pvt_thread_rdwr_wlock( ldap_pvt_thread_rdwr_t *rw ) +{ + return ERRVAL( pthread_rwlock_wrlock( rw ) ); +} + +int ldap_pvt_thread_rdwr_wtrylock( ldap_pvt_thread_rdwr_t *rw ) +{ + return ERRVAL( pthread_rwlock_trywrlock( rw ) ); +} + +int ldap_pvt_thread_rdwr_wunlock( ldap_pvt_thread_rdwr_t *rw ) +{ + return ERRVAL( pthread_rwlock_unlock( rw ) ); +} + +#endif /* HAVE_PTHREAD_RWLOCK_DESTROY */ +#endif /* LDAP_THREAD_HAVE_RDWR */ +#endif /* HAVE_PTHREADS */ + diff --git a/libraries/libldap_r/thr_pth.c b/libraries/libldap_r/thr_pth.c new file mode 100644 index 0000000..13377a0 --- /dev/null +++ b/libraries/libldap_r/thr_pth.c @@ -0,0 +1,231 @@ +/* thr_pth.c - wrappers around GNU Pth */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#if defined( HAVE_GNU_PTH ) + +#include "ldap_pvt_thread.h" /* Get the thread interface */ +#define LDAP_THREAD_IMPLEMENTATION +#define LDAP_THREAD_RDWR_IMPLEMENTATION +#include "ldap_thr_debug.h" /* May rename the symbols defined below */ + +#include <errno.h> + +/******************* + * * + * GNU Pth Threads * + * * + *******************/ + +static pth_attr_t detach_attr; +static pth_attr_t joined_attr; + +int +ldap_int_thread_initialize( void ) +{ + if( !pth_init() ) { + return -1; + } + detach_attr = pth_attr_new(); + joined_attr = pth_attr_new(); +#ifdef LDAP_PVT_THREAD_SET_STACK_SIZE + pth_attr_set( joined_attr, PTH_ATTR_STACK_SIZE, LDAP_PVT_THREAD_STACK_SIZE ); + pth_attr_set( detach_attr, PTH_ATTR_STACK_SIZE, LDAP_PVT_THREAD_STACK_SIZE ); +#endif + return pth_attr_set( detach_attr, PTH_ATTR_JOINABLE, FALSE ); +} + +int +ldap_int_thread_destroy( void ) +{ + pth_attr_destroy(detach_attr); + pth_kill(); + return 0; +} + +int +ldap_pvt_thread_create( ldap_pvt_thread_t * thread, + int detach, + void *(*start_routine)( void *), + void *arg) +{ + *thread = pth_spawn( detach ? detach_attr : joined_attr, + start_routine, arg ); + + return *thread == NULL ? errno : 0; +} + +void +ldap_pvt_thread_exit( void *retval ) +{ + pth_exit( retval ); +} + +int ldap_pvt_thread_join( ldap_pvt_thread_t thread, void **thread_return ) +{ + return pth_join( thread, thread_return ) ? 0 : errno; +} + +int +ldap_pvt_thread_kill( ldap_pvt_thread_t thread, int signo ) +{ + return pth_raise( thread, signo ) ? 0 : errno; +} + +int +ldap_pvt_thread_yield( void ) +{ + return pth_yield(NULL) ? 0 : errno; +} + +int +ldap_pvt_thread_cond_init( ldap_pvt_thread_cond_t *cond ) +{ + return( pth_cond_init( cond ) ? 0 : errno ); +} + +int +ldap_pvt_thread_cond_signal( ldap_pvt_thread_cond_t *cond ) +{ + return( pth_cond_notify( cond, 0 ) ? 0 : errno ); +} + +int +ldap_pvt_thread_cond_broadcast( ldap_pvt_thread_cond_t *cond ) +{ + return( pth_cond_notify( cond, 1 ) ? 0 : errno ); +} + +int +ldap_pvt_thread_cond_wait( ldap_pvt_thread_cond_t *cond, + ldap_pvt_thread_mutex_t *mutex ) +{ + return( pth_cond_await( cond, mutex, NULL ) ? 0 : errno ); +} + +int +ldap_pvt_thread_cond_destroy( ldap_pvt_thread_cond_t *cv ) +{ + return 0; +} + +int +ldap_pvt_thread_mutex_init( ldap_pvt_thread_mutex_t *mutex ) +{ + return( pth_mutex_init( mutex ) ? 0 : errno ); +} + +int +ldap_pvt_thread_mutex_destroy( ldap_pvt_thread_mutex_t *mutex ) +{ + return 0; +} + +int +ldap_pvt_thread_mutex_lock( ldap_pvt_thread_mutex_t *mutex ) +{ + return( pth_mutex_acquire( mutex, 0, NULL ) ? 0 : errno ); +} + +int +ldap_pvt_thread_mutex_unlock( ldap_pvt_thread_mutex_t *mutex ) +{ + return( pth_mutex_release( mutex ) ? 0 : errno ); +} + +int +ldap_pvt_thread_mutex_trylock( ldap_pvt_thread_mutex_t *mutex ) +{ + return( pth_mutex_acquire( mutex, 1, NULL ) ? 0 : errno ); +} + +ldap_pvt_thread_t +ldap_pvt_thread_self( void ) +{ + return pth_self(); +} + +int +ldap_pvt_thread_key_create( ldap_pvt_thread_key_t *key ) +{ + return pth_key_create( key, NULL ); +} + +int +ldap_pvt_thread_key_destroy( ldap_pvt_thread_key_t key ) +{ + return pth_key_delete( key ); +} + +int +ldap_pvt_thread_key_setdata( ldap_pvt_thread_key_t key, void *data ) +{ + return pth_key_setdata( key, data ); +} + +int +ldap_pvt_thread_key_getdata( ldap_pvt_thread_key_t key, void **data ) +{ + *data = pth_key_getdata( key ); + return 0; +} + +#ifdef LDAP_THREAD_HAVE_RDWR +int +ldap_pvt_thread_rdwr_init( ldap_pvt_thread_rdwr_t *rw ) +{ + return pth_rwlock_init( rw ) ? 0 : errno; +} + +int +ldap_pvt_thread_rdwr_destroy( ldap_pvt_thread_rdwr_t *rw ) +{ + return 0; +} + +int ldap_pvt_thread_rdwr_rlock( ldap_pvt_thread_rdwr_t *rw ) +{ + return pth_rwlock_acquire( rw, PTH_RWLOCK_RD, 0, NULL ) ? 0 : errno; +} + +int ldap_pvt_thread_rdwr_rtrylock( ldap_pvt_thread_rdwr_t *rw ) +{ + return pth_rwlock_acquire( rw, PTH_RWLOCK_RD, 1, NULL ) ? 0 : errno; +} + +int ldap_pvt_thread_rdwr_runlock( ldap_pvt_thread_rdwr_t *rw ) +{ + return pth_rwlock_release( rw ) ? 0 : errno; +} + +int ldap_pvt_thread_rdwr_wlock( ldap_pvt_thread_rdwr_t *rw ) +{ + return pth_rwlock_acquire( rw, PTH_RWLOCK_RW, 0, NULL ) ? 0 : errno; +} + +int ldap_pvt_thread_rdwr_wtrylock( ldap_pvt_thread_rdwr_t *rw ) +{ + return pth_rwlock_acquire( rw, PTH_RWLOCK_RW, 1, NULL ) ? 0 : errno; +} + +int ldap_pvt_thread_rdwr_wunlock( ldap_pvt_thread_rdwr_t *rw ) +{ + return pth_rwlock_release( rw ) ? 0 : errno; +} + +#endif /* LDAP_THREAD_HAVE_RDWR */ +#endif /* HAVE_GNU_PTH */ diff --git a/libraries/libldap_r/thr_stub.c b/libraries/libldap_r/thr_stub.c new file mode 100644 index 0000000..05357d9 --- /dev/null +++ b/libraries/libldap_r/thr_stub.c @@ -0,0 +1,305 @@ +/* thr_stub.c - stubs for the threads */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#if defined( NO_THREADS ) + +#include "ldap_pvt_thread.h" /* Get the thread interface */ +#define LDAP_THREAD_IMPLEMENTATION +#define LDAP_THREAD_POOL_IMPLEMENTATION +#include "ldap_thr_debug.h" /* May rename the symbols defined below */ + +/*********************************************************************** + * * + * no threads package defined for this system - fake ok returns from * + * all threads routines (making it single-threaded). * + * * + ***********************************************************************/ + +int +ldap_int_thread_initialize( void ) +{ + return 0; +} + +int +ldap_int_thread_destroy( void ) +{ + return 0; +} + +static void* ldap_int_status = NULL; + +int +ldap_pvt_thread_create( ldap_pvt_thread_t * thread, + int detach, + void *(*start_routine)(void *), + void *arg) +{ + if( ! detach ) ldap_int_status = NULL; + start_routine( arg ); + return 0; +} + +void +ldap_pvt_thread_exit( void *retval ) +{ + if( retval != NULL ) { + ldap_int_status = retval; + } + return; +} + +int +ldap_pvt_thread_join( ldap_pvt_thread_t thread, void **status ) +{ + if(status != NULL) *status = ldap_int_status; + return 0; +} + +int +ldap_pvt_thread_kill( ldap_pvt_thread_t thread, int signo ) +{ + return 0; +} + +int +ldap_pvt_thread_yield( void ) +{ + return 0; +} + +int +ldap_pvt_thread_cond_init( ldap_pvt_thread_cond_t *cond ) +{ + return 0; +} + +int +ldap_pvt_thread_cond_destroy( ldap_pvt_thread_cond_t *cond ) +{ + return 0; +} + +int +ldap_pvt_thread_cond_signal( ldap_pvt_thread_cond_t *cond ) +{ + return 0; +} + +int +ldap_pvt_thread_cond_broadcast( ldap_pvt_thread_cond_t *cond ) +{ + return 0; +} + +int +ldap_pvt_thread_cond_wait( ldap_pvt_thread_cond_t *cond, + ldap_pvt_thread_mutex_t *mutex ) +{ + return 0; +} + +int +ldap_pvt_thread_mutex_init( ldap_pvt_thread_mutex_t *mutex ) +{ + return 0; +} + +int +ldap_pvt_thread_mutex_destroy( ldap_pvt_thread_mutex_t *mutex ) +{ + return 0; +} + +int +ldap_pvt_thread_mutex_lock( ldap_pvt_thread_mutex_t *mutex ) +{ + return 0; +} + +int +ldap_pvt_thread_mutex_trylock( ldap_pvt_thread_mutex_t *mutex ) +{ + return 0; +} + +int +ldap_pvt_thread_mutex_unlock( ldap_pvt_thread_mutex_t *mutex ) +{ + return 0; +} + +/* + * NO_THREADS requires a separate tpool implementation since + * generic ldap_pvt_thread_pool_wrapper loops forever. + */ +int +ldap_pvt_thread_pool_init ( + ldap_pvt_thread_pool_t *pool_out, + int max_concurrency, int max_pending ) +{ + *pool_out = (ldap_pvt_thread_pool_t) 0; + return(0); +} + +int +ldap_pvt_thread_pool_submit ( + ldap_pvt_thread_pool_t *pool, + ldap_pvt_thread_start_t *start_routine, void *arg ) +{ + (start_routine)(NULL, arg); + return(0); +} + +int +ldap_pvt_thread_pool_retract ( + ldap_pvt_thread_pool_t *pool, + ldap_pvt_thread_start_t *start_routine, void *arg ) +{ + return(0); +} + +int +ldap_pvt_thread_pool_maxthreads ( ldap_pvt_thread_pool_t *tpool, int max_threads ) +{ + return(0); +} + +int +ldap_pvt_thread_pool_query( ldap_pvt_thread_pool_t *tpool, + ldap_pvt_thread_pool_param_t param, void *value ) +{ + *(int *)value = -1; + return(-1); +} + +int +ldap_pvt_thread_pool_backload ( + ldap_pvt_thread_pool_t *pool ) +{ + return(0); +} + +int +ldap_pvt_thread_pool_destroy ( + ldap_pvt_thread_pool_t *pool, int run_pending ) +{ + return(0); +} + +void +ldap_pvt_thread_pool_idle ( ldap_pvt_thread_pool_t *pool ) +{ + return; +} + +void +ldap_pvt_thread_pool_unidle ( ldap_pvt_thread_pool_t *pool ) +{ + return; +} + +int ldap_pvt_thread_pool_getkey ( + void *ctx, void *key, void **data, ldap_pvt_thread_pool_keyfree_t **kfree ) +{ + return(0); +} + +int ldap_pvt_thread_pool_setkey ( + void *ctx, void *key, + void *data, ldap_pvt_thread_pool_keyfree_t *kfree, + void **olddatap, ldap_pvt_thread_pool_keyfree_t **oldkfreep ) +{ + if ( olddatap ) *olddatap = NULL; + if ( oldkfreep ) *oldkfreep = 0; + return(0); +} + +void ldap_pvt_thread_pool_purgekey( void *key ) +{ +} + +int ldap_pvt_thread_pool_pause ( + ldap_pvt_thread_pool_t *tpool ) +{ + return(0); +} + +int ldap_pvt_thread_pool_resume ( + ldap_pvt_thread_pool_t *tpool ) +{ + return(0); +} + +int ldap_pvt_thread_pool_pausing( ldap_pvt_thread_pool_t *tpool ) +{ + return(0); +} + +ldap_pvt_thread_pool_pausecheck( ldap_pvt_thread_pool_t *tpool ) +{ + return(0); +} + +void *ldap_pvt_thread_pool_context( ) +{ + return(NULL); +} + +void ldap_pvt_thread_pool_context_reset( void *vctx ) +{ +} + +ldap_pvt_thread_t +ldap_pvt_thread_self( void ) +{ + return(0); +} + +int +ldap_pvt_thread_key_create( ldap_pvt_thread_key_t *key ) +{ + return(0); +} + +int +ldap_pvt_thread_key_destroy( ldap_pvt_thread_key_t key ) +{ + return(0); +} + +int +ldap_pvt_thread_key_setdata( ldap_pvt_thread_key_t key, void *data ) +{ + return(0); +} + +int +ldap_pvt_thread_key_getdata( ldap_pvt_thread_key_t key, void **data ) +{ + return(0); +} + +ldap_pvt_thread_t +ldap_pvt_thread_pool_tid( void *vctx ) +{ + + return(0); +} + +#endif /* NO_THREADS */ diff --git a/libraries/libldap_r/thr_thr.c b/libraries/libldap_r/thr_thr.c new file mode 100644 index 0000000..dfb41a6 --- /dev/null +++ b/libraries/libldap_r/thr_thr.c @@ -0,0 +1,186 @@ +/* thr_thr.c - wrappers around solaris threads */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#if defined( HAVE_THR ) + +#include "ldap_pvt_thread.h" /* Get the thread interface */ +#define LDAP_THREAD_IMPLEMENTATION +#include "ldap_thr_debug.h" /* May rename the symbols defined below */ + +/******************* + * * + * Solaris Threads * + * * + *******************/ + +int +ldap_int_thread_initialize( void ) +{ + return 0; +} + +int +ldap_int_thread_destroy( void ) +{ + return 0; +} + +#ifdef LDAP_THREAD_HAVE_SETCONCURRENCY +int +ldap_pvt_thread_set_concurrency(int n) +{ + return thr_setconcurrency( n ); +} +#endif + +#ifdef LDAP_THREAD_HAVE_GETCONCURRENCY +int +ldap_pvt_thread_get_concurrency(void) +{ + return thr_getconcurrency(); +} +#endif + +int +ldap_pvt_thread_create( ldap_pvt_thread_t * thread, + int detach, + void *(*start_routine)( void *), + void *arg) +{ + return( thr_create( NULL, LDAP_PVT_THREAD_STACK_SIZE, start_routine, + arg, detach ? THR_DETACHED : 0, thread ) ); +} + +void +ldap_pvt_thread_exit( void *retval ) +{ + thr_exit( NULL ); +} + +int ldap_pvt_thread_join( ldap_pvt_thread_t thread, void **thread_return ) +{ + thr_join( thread, NULL, thread_return ); + return 0; +} + +int +ldap_pvt_thread_kill( ldap_pvt_thread_t thread, int signo ) +{ + thr_kill( thread, signo ); + return 0; +} + +int +ldap_pvt_thread_yield( void ) +{ + thr_yield(); + return 0; +} + +int +ldap_pvt_thread_cond_init( ldap_pvt_thread_cond_t *cond ) +{ + return( cond_init( cond, USYNC_THREAD, NULL ) ); +} + +int +ldap_pvt_thread_cond_signal( ldap_pvt_thread_cond_t *cond ) +{ + return( cond_signal( cond ) ); +} + +int +ldap_pvt_thread_cond_broadcast( ldap_pvt_thread_cond_t *cv ) +{ + return( cond_broadcast( cv ) ); +} + +int +ldap_pvt_thread_cond_wait( ldap_pvt_thread_cond_t *cond, + ldap_pvt_thread_mutex_t *mutex ) +{ + return( cond_wait( cond, mutex ) ); +} + +int +ldap_pvt_thread_cond_destroy( ldap_pvt_thread_cond_t *cv ) +{ + return( cond_destroy( cv ) ); +} + +int +ldap_pvt_thread_mutex_init( ldap_pvt_thread_mutex_t *mutex ) +{ + return( mutex_init( mutex, USYNC_THREAD, NULL ) ); +} + +int +ldap_pvt_thread_mutex_destroy( ldap_pvt_thread_mutex_t *mutex ) +{ + return( mutex_destroy( mutex ) ); +} + +int +ldap_pvt_thread_mutex_lock( ldap_pvt_thread_mutex_t *mutex ) +{ + return( mutex_lock( mutex ) ); +} + +int +ldap_pvt_thread_mutex_unlock( ldap_pvt_thread_mutex_t *mutex ) +{ + return( mutex_unlock( mutex ) ); +} + +int +ldap_pvt_thread_mutex_trylock( ldap_pvt_thread_mutex_t *mp ) +{ + return( mutex_trylock( mp ) ); +} + +ldap_pvt_thread_t +ldap_pvt_thread_self( void ) +{ + return thr_self(); +} + +int +ldap_pvt_thread_key_create( ldap_pvt_thread_key_t *key ) +{ + return thr_keycreate( key, NULL ); +} + +int +ldap_pvt_thread_key_destroy( ldap_pvt_thread_key_t key ) +{ + return( 0 ); +} + +int +ldap_pvt_thread_key_setdata( ldap_pvt_thread_key_t key, void *data ) +{ + return thr_setspecific( key, data ); +} + +int +ldap_pvt_thread_key_getdata( ldap_pvt_thread_key_t key, void **data ) +{ + return thr_getspecific( key, data ); +} + +#endif /* HAVE_THR */ diff --git a/libraries/libldap_r/threads.c b/libraries/libldap_r/threads.c new file mode 100644 index 0000000..ff9ed8f --- /dev/null +++ b/libraries/libldap_r/threads.c @@ -0,0 +1,113 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdarg.h> +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/unistd.h> + +#include "ldap_pvt_thread.h" /* Get the thread interface */ +#include "ldap_thr_debug.h" /* May redirect thread initialize/destroy calls */ + + +/* + * Common LDAP thread routines + * see thr_*.c for implementation specific routines + * see rdwr.c for generic reader/writer lock implementation + * see tpool.c for generic thread pool implementation + */ + + +int ldap_pvt_thread_initialize( void ) +{ + int rc; + static int init = 0; + ldap_pvt_thread_rmutex_t rm; + ldap_pvt_thread_t tid; + + /* we only get one shot at this */ + if( init++ ) return -1; + + rc = ldap_int_thread_initialize(); + if( rc ) return rc; + +#ifndef LDAP_THREAD_HAVE_TPOOL + rc = ldap_int_thread_pool_startup(); + if( rc ) return rc; +#endif + + /* kludge to pull symbol definitions in */ + ldap_pvt_thread_rmutex_init( &rm ); + tid = ldap_pvt_thread_self(); + ldap_pvt_thread_rmutex_lock( &rm, tid ); + ldap_pvt_thread_rmutex_trylock( &rm, tid ); + ldap_pvt_thread_rmutex_unlock( &rm, tid ); + ldap_pvt_thread_rmutex_unlock( &rm, tid ); + ldap_pvt_thread_rmutex_destroy( &rm ); + + return 0; +} + +int ldap_pvt_thread_destroy( void ) +{ +#ifndef LDAP_THREAD_HAVE_TPOOL + (void) ldap_int_thread_pool_shutdown(); +#endif + return ldap_int_thread_destroy(); +} + + +/* + * Default implementations of some LDAP thread routines + */ + +#define LDAP_THREAD_IMPLEMENTATION +#include "ldap_thr_debug.h" /* May rename the symbols defined below */ + + +#ifndef LDAP_THREAD_HAVE_GETCONCURRENCY +int +ldap_pvt_thread_get_concurrency ( void ) +{ + return 1; +} +#endif + +#ifndef LDAP_THREAD_HAVE_SETCONCURRENCY +int +ldap_pvt_thread_set_concurrency ( int concurrency ) +{ + return 1; +} +#endif + +#ifndef LDAP_THREAD_HAVE_SLEEP +/* + * Here we assume we have fully preemptive threads and that sleep() + * does the right thing. + */ +unsigned int +ldap_pvt_thread_sleep( + unsigned int interval +) +{ + sleep( interval ); + return 0; +} +#endif diff --git a/libraries/libldap_r/tpool.c b/libraries/libldap_r/tpool.c new file mode 100644 index 0000000..17cee3b --- /dev/null +++ b/libraries/libldap_r/tpool.c @@ -0,0 +1,1032 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/signal.h> +#include <ac/stdarg.h> +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/time.h> +#include <ac/errno.h> + +#include "ldap-int.h" +#include "ldap_pvt_thread.h" /* Get the thread interface */ +#include "ldap_queue.h" +#define LDAP_THREAD_POOL_IMPLEMENTATION +#include "ldap_thr_debug.h" /* May rename symbols defined below */ + +#ifndef LDAP_THREAD_HAVE_TPOOL + +/* Thread-specific key with data and optional free function */ +typedef struct ldap_int_tpool_key_s { + void *ltk_key; + void *ltk_data; + ldap_pvt_thread_pool_keyfree_t *ltk_free; +} ldap_int_tpool_key_t; + +/* Max number of thread-specific keys we store per thread. + * We don't expect to use many... + */ +#define MAXKEYS 32 + +/* Max number of threads */ +#define LDAP_MAXTHR 1024 /* must be a power of 2 */ + +/* (Theoretical) max number of pending requests */ +#define MAX_PENDING (INT_MAX/2) /* INT_MAX - (room to avoid overflow) */ + +/* pool->ltp_pause values */ +enum { NOT_PAUSED = 0, WANT_PAUSE = 1, PAUSED = 2 }; + +/* Context: thread ID and thread-specific key/data pairs */ +typedef struct ldap_int_thread_userctx_s { + ldap_pvt_thread_t ltu_id; + ldap_int_tpool_key_t ltu_key[MAXKEYS]; +} ldap_int_thread_userctx_t; + + +/* Simple {thread ID -> context} hash table; key=ctx->ltu_id. + * Protected by ldap_pvt_thread_pool_mutex except during pauses, + * when it is read-only (used by pool_purgekey and pool_context). + * Protected by tpool->ltp_mutex during pauses. + */ +static struct { + ldap_int_thread_userctx_t *ctx; + /* ctx is valid when not NULL or DELETED_THREAD_CTX */ +# define DELETED_THREAD_CTX (&ldap_int_main_thrctx + 1) /* dummy addr */ +} thread_keys[LDAP_MAXTHR]; + +#define TID_HASH(tid, hash) do { \ + unsigned const char *ptr_ = (unsigned const char *)&(tid); \ + unsigned i_; \ + for (i_ = 0, (hash) = ptr_[0]; ++i_ < sizeof(tid);) \ + (hash) += ((hash) << 5) ^ ptr_[i_]; \ +} while(0) + + +/* Task for a thread to perform */ +typedef struct ldap_int_thread_task_s { + union { + LDAP_STAILQ_ENTRY(ldap_int_thread_task_s) q; + LDAP_SLIST_ENTRY(ldap_int_thread_task_s) l; + } ltt_next; + ldap_pvt_thread_start_t *ltt_start_routine; + void *ltt_arg; +} ldap_int_thread_task_t; + +typedef LDAP_STAILQ_HEAD(tcq, ldap_int_thread_task_s) ldap_int_tpool_plist_t; + +struct ldap_int_thread_pool_s { + LDAP_STAILQ_ENTRY(ldap_int_thread_pool_s) ltp_next; + + /* protect members below, and protect thread_keys[] during pauses */ + ldap_pvt_thread_mutex_t ltp_mutex; + + /* not paused and something to do for pool_<wrapper/pause/destroy>() */ + ldap_pvt_thread_cond_t ltp_cond; + + /* ltp_active_count <= 1 && ltp_pause */ + ldap_pvt_thread_cond_t ltp_pcond; + + /* ltp_pause == 0 ? <p_pending_list : &empty_pending_list, + * maintaned to reduce work for pool_wrapper() + */ + ldap_int_tpool_plist_t *ltp_work_list; + + /* pending tasks, and unused task objects */ + ldap_int_tpool_plist_t ltp_pending_list; + LDAP_SLIST_HEAD(tcl, ldap_int_thread_task_s) ltp_free_list; + + /* The pool is finishing, waiting for its threads to close. + * They close when ltp_pending_list is done. pool_submit() + * rejects new tasks. ltp_max_pending = -(its old value). + */ + int ltp_finishing; + + /* Some active task needs to be the sole active task. + * Atomic variable so ldap_pvt_thread_pool_pausing() can read it. + * Note: Pauses adjust ltp_<open_count/vary_open_count/work_list>, + * so pool_<submit/wrapper>() mostly can avoid testing ltp_pause. + */ + volatile sig_atomic_t ltp_pause; + + /* Max number of threads in pool, or 0 for default (LDAP_MAXTHR) */ + int ltp_max_count; + + /* Max pending + paused + idle tasks, negated when ltp_finishing */ + int ltp_max_pending; + + int ltp_pending_count; /* Pending + paused + idle tasks */ + int ltp_active_count; /* Active, not paused/idle tasks */ + int ltp_open_count; /* Number of threads, negated when ltp_pause */ + int ltp_starting; /* Currenlty starting threads */ + + /* >0 if paused or we may open a thread, <0 if we should close a thread. + * Updated when ltp_<finishing/pause/max_count/open_count> change. + * Maintained to reduce the time ltp_mutex must be locked in + * ldap_pvt_thread_pool_<submit/wrapper>(). + */ + int ltp_vary_open_count; +# define SET_VARY_OPEN_COUNT(pool) \ + ((pool)->ltp_vary_open_count = \ + (pool)->ltp_pause ? 1 : \ + (pool)->ltp_finishing ? -1 : \ + ((pool)->ltp_max_count ? (pool)->ltp_max_count : LDAP_MAXTHR) \ + - (pool)->ltp_open_count) +}; + +static ldap_int_tpool_plist_t empty_pending_list = + LDAP_STAILQ_HEAD_INITIALIZER(empty_pending_list); + +static int ldap_int_has_thread_pool = 0; +static LDAP_STAILQ_HEAD(tpq, ldap_int_thread_pool_s) + ldap_int_thread_pool_list = + LDAP_STAILQ_HEAD_INITIALIZER(ldap_int_thread_pool_list); + +static ldap_pvt_thread_mutex_t ldap_pvt_thread_pool_mutex; + +static void *ldap_int_thread_pool_wrapper( void *pool ); + +static ldap_pvt_thread_key_t ldap_tpool_key; + +/* Context of the main thread */ +static ldap_int_thread_userctx_t ldap_int_main_thrctx; + +int +ldap_int_thread_pool_startup ( void ) +{ + ldap_int_main_thrctx.ltu_id = ldap_pvt_thread_self(); + ldap_pvt_thread_key_create( &ldap_tpool_key ); + return ldap_pvt_thread_mutex_init(&ldap_pvt_thread_pool_mutex); +} + +int +ldap_int_thread_pool_shutdown ( void ) +{ + struct ldap_int_thread_pool_s *pool; + + while ((pool = LDAP_STAILQ_FIRST(&ldap_int_thread_pool_list)) != NULL) { + (ldap_pvt_thread_pool_destroy)(&pool, 0); /* ignore thr_debug macro */ + } + ldap_pvt_thread_mutex_destroy(&ldap_pvt_thread_pool_mutex); + ldap_pvt_thread_key_destroy( ldap_tpool_key ); + return(0); +} + + +/* Create a thread pool */ +int +ldap_pvt_thread_pool_init ( + ldap_pvt_thread_pool_t *tpool, + int max_threads, + int max_pending ) +{ + ldap_pvt_thread_pool_t pool; + int rc; + + /* multiple pools are currently not supported (ITS#4943) */ + assert(!ldap_int_has_thread_pool); + + if (! (0 <= max_threads && max_threads <= LDAP_MAXTHR)) + max_threads = 0; + if (! (1 <= max_pending && max_pending <= MAX_PENDING)) + max_pending = MAX_PENDING; + + *tpool = NULL; + pool = (ldap_pvt_thread_pool_t) LDAP_CALLOC(1, + sizeof(struct ldap_int_thread_pool_s)); + + if (pool == NULL) return(-1); + + rc = ldap_pvt_thread_mutex_init(&pool->ltp_mutex); + if (rc != 0) { +fail1: + LDAP_FREE(pool); + return(rc); + } + rc = ldap_pvt_thread_cond_init(&pool->ltp_cond); + if (rc != 0) { +fail2: + ldap_pvt_thread_mutex_destroy(&pool->ltp_mutex); + goto fail1; + } + rc = ldap_pvt_thread_cond_init(&pool->ltp_pcond); + if (rc != 0) { + ldap_pvt_thread_cond_destroy(&pool->ltp_cond); + goto fail2; + } + + ldap_int_has_thread_pool = 1; + + pool->ltp_max_count = max_threads; + SET_VARY_OPEN_COUNT(pool); + pool->ltp_max_pending = max_pending; + + LDAP_STAILQ_INIT(&pool->ltp_pending_list); + pool->ltp_work_list = &pool->ltp_pending_list; + LDAP_SLIST_INIT(&pool->ltp_free_list); + + ldap_pvt_thread_mutex_lock(&ldap_pvt_thread_pool_mutex); + LDAP_STAILQ_INSERT_TAIL(&ldap_int_thread_pool_list, pool, ltp_next); + ldap_pvt_thread_mutex_unlock(&ldap_pvt_thread_pool_mutex); + + /* Start no threads just yet. That can break if the process forks + * later, as slapd does in order to daemonize. On at least POSIX, + * only the forking thread would survive in the child. Yet fork() + * can't unlock/clean up other threads' locks and data structures, + * unless pthread_atfork() handlers have been set up to do so. + */ + + *tpool = pool; + return(0); +} + + +/* Submit a task to be performed by the thread pool */ +int +ldap_pvt_thread_pool_submit ( + ldap_pvt_thread_pool_t *tpool, + ldap_pvt_thread_start_t *start_routine, void *arg ) +{ + struct ldap_int_thread_pool_s *pool; + ldap_int_thread_task_t *task; + ldap_pvt_thread_t thr; + + if (tpool == NULL) + return(-1); + + pool = *tpool; + + if (pool == NULL) + return(-1); + + ldap_pvt_thread_mutex_lock(&pool->ltp_mutex); + + if (pool->ltp_pending_count >= pool->ltp_max_pending) + goto failed; + + task = LDAP_SLIST_FIRST(&pool->ltp_free_list); + if (task) { + LDAP_SLIST_REMOVE_HEAD(&pool->ltp_free_list, ltt_next.l); + } else { + task = (ldap_int_thread_task_t *) LDAP_MALLOC(sizeof(*task)); + if (task == NULL) + goto failed; + } + + task->ltt_start_routine = start_routine; + task->ltt_arg = arg; + + pool->ltp_pending_count++; + LDAP_STAILQ_INSERT_TAIL(&pool->ltp_pending_list, task, ltt_next.q); + + /* true if ltp_pause != 0 or we should open (create) a thread */ + if (pool->ltp_vary_open_count > 0 && + pool->ltp_open_count < pool->ltp_active_count+pool->ltp_pending_count) + { + if (pool->ltp_pause) + goto done; + + pool->ltp_starting++; + pool->ltp_open_count++; + SET_VARY_OPEN_COUNT(pool); + + if (0 != ldap_pvt_thread_create( + &thr, 1, ldap_int_thread_pool_wrapper, pool)) + { + /* couldn't create thread. back out of + * ltp_open_count and check for even worse things. + */ + pool->ltp_starting--; + pool->ltp_open_count--; + SET_VARY_OPEN_COUNT(pool); + + if (pool->ltp_open_count == 0) { + /* no open threads at all?!? + */ + ldap_int_thread_task_t *ptr; + + /* let pool_destroy know there are no more threads */ + ldap_pvt_thread_cond_signal(&pool->ltp_cond); + + LDAP_STAILQ_FOREACH(ptr, &pool->ltp_pending_list, ltt_next.q) + if (ptr == task) break; + if (ptr == task) { + /* no open threads, task not handled, so + * back out of ltp_pending_count, free the task, + * report the error. + */ + pool->ltp_pending_count--; + LDAP_STAILQ_REMOVE(&pool->ltp_pending_list, task, + ldap_int_thread_task_s, ltt_next.q); + LDAP_SLIST_INSERT_HEAD(&pool->ltp_free_list, task, + ltt_next.l); + goto failed; + } + } + /* there is another open thread, so this + * task will be handled eventually. + */ + } + } + ldap_pvt_thread_cond_signal(&pool->ltp_cond); + + done: + ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex); + return(0); + + failed: + ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex); + return(-1); +} + +static void * +no_task( void *ctx, void *arg ) +{ + return NULL; +} + +/* Cancel a pending task that was previously submitted. + * Return 1 if the task was successfully cancelled, 0 if + * not found, -1 for invalid parameters + */ +int +ldap_pvt_thread_pool_retract ( + ldap_pvt_thread_pool_t *tpool, + ldap_pvt_thread_start_t *start_routine, void *arg ) +{ + struct ldap_int_thread_pool_s *pool; + ldap_int_thread_task_t *task; + + if (tpool == NULL) + return(-1); + + pool = *tpool; + + if (pool == NULL) + return(-1); + + ldap_pvt_thread_mutex_lock(&pool->ltp_mutex); + LDAP_STAILQ_FOREACH(task, &pool->ltp_pending_list, ltt_next.q) + if (task->ltt_start_routine == start_routine && + task->ltt_arg == arg) { + /* Could LDAP_STAILQ_REMOVE the task, but that + * walks ltp_pending_list again to find it. + */ + task->ltt_start_routine = no_task; + task->ltt_arg = NULL; + break; + } + ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex); + return task != NULL; +} + +/* Set max #threads. value <= 0 means max supported #threads (LDAP_MAXTHR) */ +int +ldap_pvt_thread_pool_maxthreads( + ldap_pvt_thread_pool_t *tpool, + int max_threads ) +{ + struct ldap_int_thread_pool_s *pool; + + if (! (0 <= max_threads && max_threads <= LDAP_MAXTHR)) + max_threads = 0; + + if (tpool == NULL) + return(-1); + + pool = *tpool; + + if (pool == NULL) + return(-1); + + ldap_pvt_thread_mutex_lock(&pool->ltp_mutex); + + pool->ltp_max_count = max_threads; + SET_VARY_OPEN_COUNT(pool); + + ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex); + return(0); +} + +/* Inspect the pool */ +int +ldap_pvt_thread_pool_query( + ldap_pvt_thread_pool_t *tpool, + ldap_pvt_thread_pool_param_t param, + void *value ) +{ + struct ldap_int_thread_pool_s *pool; + int count = -1; + + if ( tpool == NULL || value == NULL ) { + return -1; + } + + pool = *tpool; + + if ( pool == NULL ) { + return 0; + } + + ldap_pvt_thread_mutex_lock(&pool->ltp_mutex); + switch ( param ) { + case LDAP_PVT_THREAD_POOL_PARAM_MAX: + count = pool->ltp_max_count; + break; + + case LDAP_PVT_THREAD_POOL_PARAM_MAX_PENDING: + count = pool->ltp_max_pending; + if (count < 0) + count = -count; + if (count == MAX_PENDING) + count = 0; + break; + + case LDAP_PVT_THREAD_POOL_PARAM_OPEN: + count = pool->ltp_open_count; + if (count < 0) + count = -count; + break; + + case LDAP_PVT_THREAD_POOL_PARAM_STARTING: + count = pool->ltp_starting; + break; + + case LDAP_PVT_THREAD_POOL_PARAM_ACTIVE: + count = pool->ltp_active_count; + break; + + case LDAP_PVT_THREAD_POOL_PARAM_PAUSING: + count = (pool->ltp_pause != 0); + break; + + case LDAP_PVT_THREAD_POOL_PARAM_PENDING: + count = pool->ltp_pending_count; + break; + + case LDAP_PVT_THREAD_POOL_PARAM_BACKLOAD: + count = pool->ltp_pending_count + pool->ltp_active_count; + break; + + case LDAP_PVT_THREAD_POOL_PARAM_ACTIVE_MAX: + break; + + case LDAP_PVT_THREAD_POOL_PARAM_PENDING_MAX: + break; + + case LDAP_PVT_THREAD_POOL_PARAM_BACKLOAD_MAX: + break; + + case LDAP_PVT_THREAD_POOL_PARAM_STATE: + *((char **)value) = + pool->ltp_pause ? "pausing" : + !pool->ltp_finishing ? "running" : + pool->ltp_pending_count ? "finishing" : "stopping"; + break; + + case LDAP_PVT_THREAD_POOL_PARAM_UNKNOWN: + break; + } + ldap_pvt_thread_mutex_unlock( &pool->ltp_mutex ); + + if ( count > -1 ) { + *((int *)value) = count; + } + + return ( count == -1 ? -1 : 0 ); +} + +/* + * true if pool is pausing; does not lock any mutex to check. + * 0 if not pause, 1 if pause, -1 if error or no pool. + */ +int +ldap_pvt_thread_pool_pausing( ldap_pvt_thread_pool_t *tpool ) +{ + int rc = -1; + struct ldap_int_thread_pool_s *pool; + + if ( tpool != NULL && (pool = *tpool) != NULL ) { + rc = (pool->ltp_pause != 0); + } + + return rc; +} + +/* + * wrapper for ldap_pvt_thread_pool_query(), left around + * for backwards compatibility + */ +int +ldap_pvt_thread_pool_backload ( ldap_pvt_thread_pool_t *tpool ) +{ + int rc, count; + + rc = ldap_pvt_thread_pool_query( tpool, + LDAP_PVT_THREAD_POOL_PARAM_BACKLOAD, (void *)&count ); + + if ( rc == 0 ) { + return count; + } + + return rc; +} + +/* Destroy the pool after making its threads finish */ +int +ldap_pvt_thread_pool_destroy ( ldap_pvt_thread_pool_t *tpool, int run_pending ) +{ + struct ldap_int_thread_pool_s *pool, *pptr; + ldap_int_thread_task_t *task; + + if (tpool == NULL) + return(-1); + + pool = *tpool; + + if (pool == NULL) return(-1); + + ldap_pvt_thread_mutex_lock(&ldap_pvt_thread_pool_mutex); + LDAP_STAILQ_FOREACH(pptr, &ldap_int_thread_pool_list, ltp_next) + if (pptr == pool) break; + if (pptr == pool) + LDAP_STAILQ_REMOVE(&ldap_int_thread_pool_list, pool, + ldap_int_thread_pool_s, ltp_next); + ldap_pvt_thread_mutex_unlock(&ldap_pvt_thread_pool_mutex); + + if (pool != pptr) return(-1); + + ldap_pvt_thread_mutex_lock(&pool->ltp_mutex); + + pool->ltp_finishing = 1; + SET_VARY_OPEN_COUNT(pool); + if (pool->ltp_max_pending > 0) + pool->ltp_max_pending = -pool->ltp_max_pending; + + if (!run_pending) { + while ((task = LDAP_STAILQ_FIRST(&pool->ltp_pending_list)) != NULL) { + LDAP_STAILQ_REMOVE_HEAD(&pool->ltp_pending_list, ltt_next.q); + LDAP_FREE(task); + } + pool->ltp_pending_count = 0; + } + + while (pool->ltp_open_count) { + if (!pool->ltp_pause) + ldap_pvt_thread_cond_broadcast(&pool->ltp_cond); + ldap_pvt_thread_cond_wait(&pool->ltp_cond, &pool->ltp_mutex); + } + + while ((task = LDAP_SLIST_FIRST(&pool->ltp_free_list)) != NULL) + { + LDAP_SLIST_REMOVE_HEAD(&pool->ltp_free_list, ltt_next.l); + LDAP_FREE(task); + } + + ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex); + ldap_pvt_thread_cond_destroy(&pool->ltp_pcond); + ldap_pvt_thread_cond_destroy(&pool->ltp_cond); + ldap_pvt_thread_mutex_destroy(&pool->ltp_mutex); + LDAP_FREE(pool); + *tpool = NULL; + ldap_int_has_thread_pool = 0; + return(0); +} + +/* Thread loop. Accept and handle submitted tasks. */ +static void * +ldap_int_thread_pool_wrapper ( + void *xpool ) +{ + struct ldap_int_thread_pool_s *pool = xpool; + ldap_int_thread_task_t *task; + ldap_int_tpool_plist_t *work_list; + ldap_int_thread_userctx_t ctx, *kctx; + unsigned i, keyslot, hash; + + assert(pool != NULL); + + for ( i=0; i<MAXKEYS; i++ ) { + ctx.ltu_key[i].ltk_key = NULL; + } + + ctx.ltu_id = ldap_pvt_thread_self(); + TID_HASH(ctx.ltu_id, hash); + + ldap_pvt_thread_key_setdata( ldap_tpool_key, &ctx ); + + ldap_pvt_thread_mutex_lock(&pool->ltp_mutex); + + /* thread_keys[] is read-only when paused */ + while (pool->ltp_pause) + ldap_pvt_thread_cond_wait(&pool->ltp_cond, &pool->ltp_mutex); + + /* find a key slot to give this thread ID and store a + * pointer to our keys there; start at the thread ID + * itself (mod LDAP_MAXTHR) and look for an empty slot. + */ + ldap_pvt_thread_mutex_lock(&ldap_pvt_thread_pool_mutex); + for (keyslot = hash & (LDAP_MAXTHR-1); + (kctx = thread_keys[keyslot].ctx) && kctx != DELETED_THREAD_CTX; + keyslot = (keyslot+1) & (LDAP_MAXTHR-1)); + thread_keys[keyslot].ctx = &ctx; + ldap_pvt_thread_mutex_unlock(&ldap_pvt_thread_pool_mutex); + + pool->ltp_starting--; + pool->ltp_active_count++; + + for (;;) { + work_list = pool->ltp_work_list; /* help the compiler a bit */ + task = LDAP_STAILQ_FIRST(work_list); + if (task == NULL) { /* paused or no pending tasks */ + if (--(pool->ltp_active_count) < 2) { + /* Notify pool_pause it is the sole active thread. */ + ldap_pvt_thread_cond_signal(&pool->ltp_pcond); + } + + do { + if (pool->ltp_vary_open_count < 0) { + /* Not paused, and either finishing or too many + * threads running (can happen if ltp_max_count + * was reduced). Let this thread die. + */ + goto done; + } + + /* We could check an idle timer here, and let the + * thread die if it has been inactive for a while. + * Only die if there are other open threads (i.e., + * always have at least one thread open). + * The check should be like this: + * if (pool->ltp_open_count>1 && pool->ltp_starting==0) + * check timer, wait if ltp_pause, leave thread; + * + * Just use pthread_cond_timedwait() if we want to + * check idle time. + */ + ldap_pvt_thread_cond_wait(&pool->ltp_cond, &pool->ltp_mutex); + + work_list = pool->ltp_work_list; + task = LDAP_STAILQ_FIRST(work_list); + } while (task == NULL); + + pool->ltp_active_count++; + } + + LDAP_STAILQ_REMOVE_HEAD(work_list, ltt_next.q); + pool->ltp_pending_count--; + ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex); + + task->ltt_start_routine(&ctx, task->ltt_arg); + + ldap_pvt_thread_mutex_lock(&pool->ltp_mutex); + LDAP_SLIST_INSERT_HEAD(&pool->ltp_free_list, task, ltt_next.l); + } + done: + + assert(!pool->ltp_pause); /* thread_keys writable, ltp_open_count >= 0 */ + + /* The ltp_mutex lock protects ctx->ltu_key from pool_purgekey() + * during this call, since it prevents new pauses. */ + ldap_pvt_thread_pool_context_reset(&ctx); + + ldap_pvt_thread_mutex_lock(&ldap_pvt_thread_pool_mutex); + thread_keys[keyslot].ctx = DELETED_THREAD_CTX; + ldap_pvt_thread_mutex_unlock(&ldap_pvt_thread_pool_mutex); + + pool->ltp_open_count--; + SET_VARY_OPEN_COUNT(pool); + /* let pool_destroy know we're all done */ + if (pool->ltp_open_count == 0) + ldap_pvt_thread_cond_signal(&pool->ltp_cond); + + ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex); + + ldap_pvt_thread_exit(NULL); + return(NULL); +} + +/* Arguments > ltp_pause to handle_pause(,PAUSE_ARG()). arg=PAUSE_ARG + * ensures (arg-ltp_pause) sets GO_* at need and keeps DO_PAUSE/GO_*. + */ +#define GO_IDLE 8 +#define GO_UNIDLE 16 +#define CHECK_PAUSE 32 /* if ltp_pause: GO_IDLE; wait; GO_UNIDLE */ +#define DO_PAUSE 64 /* CHECK_PAUSE; pause the pool */ +#define PAUSE_ARG(a) \ + ((a) | ((a) & (GO_IDLE|GO_UNIDLE) ? GO_IDLE-1 : CHECK_PAUSE)) + +static int +handle_pause( ldap_pvt_thread_pool_t *tpool, int pause_type ) +{ + struct ldap_int_thread_pool_s *pool; + int ret = 0, pause, max_ltp_pause; + + if (tpool == NULL) + return(-1); + + pool = *tpool; + + if (pool == NULL) + return(0); + + if (pause_type == CHECK_PAUSE && !pool->ltp_pause) + return(0); + + /* Let pool_unidle() ignore requests for new pauses */ + max_ltp_pause = pause_type==PAUSE_ARG(GO_UNIDLE) ? WANT_PAUSE : NOT_PAUSED; + + ldap_pvt_thread_mutex_lock(&pool->ltp_mutex); + + pause = pool->ltp_pause; /* NOT_PAUSED, WANT_PAUSE or PAUSED */ + + /* If ltp_pause and not GO_IDLE|GO_UNIDLE: Set GO_IDLE,GO_UNIDLE */ + pause_type -= pause; + + if (pause_type & GO_IDLE) { + pool->ltp_pending_count++; + pool->ltp_active_count--; + if (pause && pool->ltp_active_count < 2) { + /* Tell the task waiting to DO_PAUSE it can proceed */ + ldap_pvt_thread_cond_signal(&pool->ltp_pcond); + } + } + + if (pause_type & GO_UNIDLE) { + /* Wait out pause if any, then cancel GO_IDLE */ + if (pause > max_ltp_pause) { + ret = 1; + do { + ldap_pvt_thread_cond_wait(&pool->ltp_cond, &pool->ltp_mutex); + } while (pool->ltp_pause > max_ltp_pause); + } + pool->ltp_pending_count--; + pool->ltp_active_count++; + } + + if (pause_type & DO_PAUSE) { + /* Tell everyone else to pause or finish, then await that */ + ret = 0; + assert(!pool->ltp_pause); + pool->ltp_pause = WANT_PAUSE; + /* Let ldap_pvt_thread_pool_submit() through to its ltp_pause test, + * and do not finish threads in ldap_pvt_thread_pool_wrapper() */ + pool->ltp_open_count = -pool->ltp_open_count; + SET_VARY_OPEN_COUNT(pool); + /* Hide pending tasks from ldap_pvt_thread_pool_wrapper() */ + pool->ltp_work_list = &empty_pending_list; + /* Wait for this task to become the sole active task */ + while (pool->ltp_active_count > 1) { + ldap_pvt_thread_cond_wait(&pool->ltp_pcond, &pool->ltp_mutex); + } + assert(pool->ltp_pause == WANT_PAUSE); + pool->ltp_pause = PAUSED; + } + + ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex); + return(ret); +} + +/* Consider this task idle: It will not block pool_pause() in other tasks. */ +void +ldap_pvt_thread_pool_idle( ldap_pvt_thread_pool_t *tpool ) +{ + handle_pause(tpool, PAUSE_ARG(GO_IDLE)); +} + +/* Cancel pool_idle(). If the pool is paused, wait it out first. */ +void +ldap_pvt_thread_pool_unidle( ldap_pvt_thread_pool_t *tpool ) +{ + handle_pause(tpool, PAUSE_ARG(GO_UNIDLE)); +} + +/* + * If a pause was requested, wait for it. If several threads + * are waiting to pause, let through one or more pauses. + * The calling task must be active, not idle. + * Return 1 if we waited, 0 if not, -1 at parameter error. + */ +int +ldap_pvt_thread_pool_pausecheck( ldap_pvt_thread_pool_t *tpool ) +{ + return handle_pause(tpool, PAUSE_ARG(CHECK_PAUSE)); +} + +/* + * Pause the pool. The calling task must be active, not idle. + * Return when all other tasks are paused or idle. + */ +int +ldap_pvt_thread_pool_pause( ldap_pvt_thread_pool_t *tpool ) +{ + return handle_pause(tpool, PAUSE_ARG(DO_PAUSE)); +} + +/* End a pause */ +int +ldap_pvt_thread_pool_resume ( + ldap_pvt_thread_pool_t *tpool ) +{ + struct ldap_int_thread_pool_s *pool; + + if (tpool == NULL) + return(-1); + + pool = *tpool; + + if (pool == NULL) + return(0); + + ldap_pvt_thread_mutex_lock(&pool->ltp_mutex); + + assert(pool->ltp_pause == PAUSED); + pool->ltp_pause = 0; + if (pool->ltp_open_count <= 0) /* true when paused, but be paranoid */ + pool->ltp_open_count = -pool->ltp_open_count; + SET_VARY_OPEN_COUNT(pool); + pool->ltp_work_list = &pool->ltp_pending_list; + + ldap_pvt_thread_cond_broadcast(&pool->ltp_cond); + + ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex); + return(0); +} + +/* + * Get the key's data and optionally free function in the given context. + */ +int ldap_pvt_thread_pool_getkey( + void *xctx, + void *key, + void **data, + ldap_pvt_thread_pool_keyfree_t **kfree ) +{ + ldap_int_thread_userctx_t *ctx = xctx; + int i; + + if ( !ctx || !key || !data ) return EINVAL; + + for ( i=0; i<MAXKEYS && ctx->ltu_key[i].ltk_key; i++ ) { + if ( ctx->ltu_key[i].ltk_key == key ) { + *data = ctx->ltu_key[i].ltk_data; + if ( kfree ) *kfree = ctx->ltu_key[i].ltk_free; + return 0; + } + } + return ENOENT; +} + +static void +clear_key_idx( ldap_int_thread_userctx_t *ctx, int i ) +{ + for ( ; i < MAXKEYS-1 && ctx->ltu_key[i+1].ltk_key; i++ ) + ctx->ltu_key[i] = ctx->ltu_key[i+1]; + ctx->ltu_key[i].ltk_key = NULL; +} + +/* + * Set or remove data for the key in the given context. + * key can be any unique pointer. + * kfree() is an optional function to free the data (but not the key): + * pool_context_reset() and pool_purgekey() call kfree(key, data), + * but pool_setkey() does not. For pool_setkey() it is the caller's + * responsibility to free any existing data with the same key. + * kfree() must not call functions taking a tpool argument. + */ +int ldap_pvt_thread_pool_setkey( + void *xctx, + void *key, + void *data, + ldap_pvt_thread_pool_keyfree_t *kfree, + void **olddatap, + ldap_pvt_thread_pool_keyfree_t **oldkfreep ) +{ + ldap_int_thread_userctx_t *ctx = xctx; + int i, found; + + if ( !ctx || !key ) return EINVAL; + + for ( i=found=0; i<MAXKEYS; i++ ) { + if ( ctx->ltu_key[i].ltk_key == key ) { + found = 1; + break; + } else if ( !ctx->ltu_key[i].ltk_key ) { + break; + } + } + + if ( olddatap ) { + if ( found ) { + *olddatap = ctx->ltu_key[i].ltk_data; + } else { + *olddatap = NULL; + } + } + + if ( oldkfreep ) { + if ( found ) { + *oldkfreep = ctx->ltu_key[i].ltk_free; + } else { + *oldkfreep = 0; + } + } + + if ( data || kfree ) { + if ( i>=MAXKEYS ) + return ENOMEM; + ctx->ltu_key[i].ltk_key = key; + ctx->ltu_key[i].ltk_data = data; + ctx->ltu_key[i].ltk_free = kfree; + } else if ( found ) { + clear_key_idx( ctx, i ); + } + + return 0; +} + +/* Free all elements with this key, no matter which thread they're in. + * May only be called while the pool is paused. + */ +void ldap_pvt_thread_pool_purgekey( void *key ) +{ + int i, j; + ldap_int_thread_userctx_t *ctx; + + assert ( key != NULL ); + + for ( i=0; i<LDAP_MAXTHR; i++ ) { + ctx = thread_keys[i].ctx; + if ( ctx && ctx != DELETED_THREAD_CTX ) { + for ( j=0; j<MAXKEYS && ctx->ltu_key[j].ltk_key; j++ ) { + if ( ctx->ltu_key[j].ltk_key == key ) { + if (ctx->ltu_key[j].ltk_free) + ctx->ltu_key[j].ltk_free( ctx->ltu_key[j].ltk_key, + ctx->ltu_key[j].ltk_data ); + clear_key_idx( ctx, j ); + break; + } + } + } + } +} + +/* + * Find the context of the current thread. + * This is necessary if the caller does not have access to the + * thread context handle (for example, a slapd plugin calling + * slapi_search_internal()). No doubt it is more efficient + * for the application to keep track of the thread context + * handles itself. + */ +void *ldap_pvt_thread_pool_context( ) +{ + void *ctx = NULL; + + ldap_pvt_thread_key_getdata( ldap_tpool_key, &ctx ); + return ctx ? ctx : (void *) &ldap_int_main_thrctx; +} + +/* + * Free the context's keys. + * Must not call functions taking a tpool argument (because this + * thread already holds ltp_mutex when called from pool_wrapper()). + */ +void ldap_pvt_thread_pool_context_reset( void *vctx ) +{ + ldap_int_thread_userctx_t *ctx = vctx; + int i; + + for ( i=MAXKEYS-1; i>=0; i--) { + if ( !ctx->ltu_key[i].ltk_key ) + continue; + if ( ctx->ltu_key[i].ltk_free ) + ctx->ltu_key[i].ltk_free( ctx->ltu_key[i].ltk_key, + ctx->ltu_key[i].ltk_data ); + ctx->ltu_key[i].ltk_key = NULL; + } +} + +ldap_pvt_thread_t ldap_pvt_thread_pool_tid( void *vctx ) +{ + ldap_int_thread_userctx_t *ctx = vctx; + + return ctx->ltu_id; +} +#endif /* LDAP_THREAD_HAVE_TPOOL */ diff --git a/libraries/liblmdb/CHANGES b/libraries/liblmdb/CHANGES new file mode 100644 index 0000000..5b9325f --- /dev/null +++ b/libraries/liblmdb/CHANGES @@ -0,0 +1,259 @@ +LMDB 0.9 Change Log + +LMDB 0.9.27 Release (2020/10/26) + ITS#9376 fix repeated DUPSORT cursor deletes + +LMDB 0.9.26 Release (2020/08/11) + ITS#9278 fix robust mutex cleanup for FreeBSD + +LMDB 0.9.25 Release (2020/01/30) + ITS#9068 fix mdb_dump/load backslashes in printable content + ITS#9118 add MAP_NOSYNC for FreeBSD + ITS#9155 free mt_spill_pgs in non-nested txn on end + +LMDB 0.9.24 Release (2019/07/24) + ITS#8969 Tweak mdb_page_split + ITS#8975 WIN32 fix writemap set_mapsize crash + ITS#9007 Fix loose pages in WRITEMAP + +LMDB 0.9.23 Release (2018/12/19) + ITS#8756 Fix loose pages in dirty list + ITS#8831 Fix mdb_load flag init + ITS#8844 Fix mdb_env_close in forked process + Documentation + ITS#8857 mdb_cursor_del doesn't invalidate cursor + ITS#8908 GET_MULTIPLE etc don't change passed in key + +LMDB 0.9.22 Release (2018/03/22) + Fix MDB_DUPSORT alignment bug (ITS#8819) + Fix regression with new db from 0.9.19 (ITS#8760) + Fix liblmdb to build on Solaris (ITS#8612) + Fix delete behavior with DUPSORT DB (ITS#8622) + Fix mdb_cursor_get/mdb_cursor_del behavior (ITS#8722) + +LMDB 0.9.21 Release (2017/06/01) + Fix xcursor after cursor_del (ITS#8622) + +LMDB 0.9.20 (Withdrawn) + Fix mdb_load with escaped plaintext (ITS#8558) + Fix mdb_cursor_last / mdb_put interaction (ITS#8557) + +LMDB 0.9.19 Release (2016/12/28) + Fix mdb_env_cwalk cursor init (ITS#8424) + Fix robust mutexes on Solaris 10/11 (ITS#8339) + Tweak Win32 error message buffer + Fix MDB_GET_BOTH on non-dup record (ITS#8393) + Optimize mdb_drop + Fix xcursors after mdb_cursor_del (ITS#8406) + Fix MDB_NEXT_DUP after mdb_cursor_del (ITS#8412) + Fix mdb_cursor_put resetting C_EOF (ITS#8489) + Fix mdb_env_copyfd2 to return EPIPE on SIGPIPE (ITS#8504) + Fix mdb_env_copy with empty DB (ITS#8209) + Fix behaviors with fork (ITS#8505) + Fix mdb_dbi_open with mainDB cursors (ITS#8542) + Fix robust mutexes on kFreeBSD (ITS#8554) + Fix utf8_to_utf16 error checks (ITS#7992) + Fix F_NOCACHE on MacOS, error is non-fatal (ITS#7682) + Build + Make shared lib suffix overridable (ITS#8481) + Documentation + Cleanup doxygen nits + Note reserved vs actual mem/disk usage + + +LMDB 0.9.18 Release (2016/02/05) + Fix robust mutex detection on glibc 2.10-11 (ITS#8330) + Fix page_search_root assert on FreeDB (ITS#8336) + Fix MDB_APPENDDUP vs. rewrite(single item) (ITS#8334) + Fix mdb_copy of large files on Windows + Fix subcursor move after delete (ITS#8355) + Fix mdb_midl_shirnk off-by-one (ITS#8363) + Check for utf8_to_utf16 failures (ITS#7992) + Catch strdup failure in mdb_dbi_open + Build + Additional makefile var tweaks (ITS#8169) + Documentation + Add Getting Started page + Update WRITEMAP description + + +LMDB 0.9.17 Release (2015/11/30) + Fix ITS#7377 catch calloc failure + Fix ITS#8237 regression from ITS#7589 + Fix ITS#8238 page_split for DUPFIXED pages + Fix ITS#8221 MDB_PAGE_FULL on delete/rebalance + Fix ITS#8258 rebalance/split assert + Fix ITS#8263 cursor_put cursor tracking + Fix ITS#8264 cursor_del cursor tracking + Fix ITS#8310 cursor_del cursor tracking + Fix ITS#8299 mdb_del cursor tracking + Fix ITS#8300 mdb_del cursor tracking + Fix ITS#8304 mdb_del cursor tracking + Fix ITS#7771 fakepage cursor tracking + Fix ITS#7789 ensure mapsize >= pages in use + Fix ITS#7971 mdb_txn_renew0() new reader slots + Fix ITS#7969 use __sync_synchronize on non-x86 + Fix ITS#8311 page_split from update_key + Fix ITS#8312 loose pages in nested txn + Fix ITS#8313 mdb_rebalance dummy cursor + Fix ITS#8315 dirty_room in nested txn + Fix ITS#8323 dirty_list in nested txn + Fix ITS#8316 page_merge cursor tracking + Fix ITS#8321 cursor tracking + Fix ITS#8319 mdb_load error messages + Fix ITS#8320 mdb_load plaintext input + Added mdb_txn_id() (ITS#7994) + Added robust mutex support + Miscellaneous cleanup/simplification + Build + Create install dirs if needed (ITS#8256) + Fix ThreadProc decl on Win32/MSVC (ITS#8270) + Added ssize_t typedef for MSVC (ITS#8067) + Use ANSI apis on Windows (ITS#8069) + Use O_SYNC if O_DSYNC,MDB_DSYNC are not defined (ITS#7209) + Allow passing AR to make (ITS#8168) + Allow passing mandir to make install (ITS#8169) + +LMDB 0.9.16 Release (2015/08/14) + Fix cursor EOF bug (ITS#8190) + Fix handling of subDB records (ITS#8181) + Fix mdb_midl_shrink() usage (ITS#8200) + +LMDB 0.9.15 Release (2015/06/19) + Fix txn init (ITS#7961,#7987) + Fix MDB_PREV_DUP (ITS#7955,#7671) + Fix compact of empty env (ITS#7956) + Fix mdb_copy file mode + Fix mdb_env_close() after failed mdb_env_open() + Fix mdb_rebalance collapsing root (ITS#8062) + Fix mdb_load with large values (ITS#8066) + Fix to retry writes on EINTR (ITS#8106) + Fix mdb_cursor_del on empty DB (ITS#8109) + Fix MDB_INTEGERDUP key compare (ITS#8117) + Fix error handling (ITS#7959,#8157,etc.) + Fix race conditions (ITS#7969,7970) + Added workaround for fdatasync bug in ext3fs + Build + Don't use -fPIC for static lib + Update .gitignore (ITS#7952,#7953) + Cleanup for "make test" (ITS#7841), "make clean", mtest*.c + Misc. Android/Windows cleanup + Documentation + Fix MDB_APPEND doc + Fix MDB_MAXKEYSIZE doc (ITS#8156) + Fix mdb_cursor_put,mdb_cursor_del EACCES description + Fix mdb_env_sync(MDB_RDONLY env) doc (ITS#8021) + Clarify MDB_WRITEMAP doc (ITS#8021) + Clarify mdb_env_open doc + Clarify mdb_dbi_open doc + +LMDB 0.9.14 Release (2014/09/20) + Fix to support 64K page size (ITS#7713) + Fix to persist decreased as well as increased mapsizes (ITS#7789) + Fix cursor bug when deleting last node of a DUPSORT key + Fix mdb_env_info to return FIXEDMAP address + Fix ambiguous error code from writing to closed DBI (ITS#7825) + Fix mdb_copy copying past end of file (ITS#7886) + Fix cursor bugs from page_merge/rebalance + Fix to dirty fewer pages in deletes (mdb_page_loose()) + Fix mdb_dbi_open creating subDBs (ITS#7917) + Fix mdb_cursor_get(_DUP) with single value (ITS#7913) + Fix Windows compat issues in mtests (ITS#7879) + Add compacting variant of mdb_copy + Add BigEndian integer key compare code + Add mdb_dump/mdb_load utilities + +LMDB 0.9.13 Release (2014/06/18) + Fix mdb_page_alloc unlimited overflow page search + Documentation + Re-fix MDB_CURRENT doc (ITS#7793) + Fix MDB_GET_MULTIPLE/MDB_NEXT_MULTIPLE doc + +LMDB 0.9.12 Release (2014/06/13) + Fix MDB_GET_BOTH regression (ITS#7875,#7681) + Fix MDB_MULTIPLE writing multiple keys (ITS#7834) + Fix mdb_rebalance (ITS#7829) + Fix mdb_page_split (ITS#7815) + Fix md_entries count (ITS#7861,#7828,#7793) + Fix MDB_CURRENT (ITS#7793) + Fix possible crash on Windows DLL detach + Misc code cleanup + Documentation + mdb_cursor_put: cursor moves on error (ITS#7771) + + +LMDB 0.9.11 Release (2014/01/15) + Add mdb_env_set_assert() (ITS#7775) + Fix: invalidate txn on page allocation errors (ITS#7377) + Fix xcursor tracking in mdb_cursor_del0() (ITS#7771) + Fix corruption from deletes (ITS#7756) + Fix Windows/MSVC build issues + Raise safe limit of max MDB_MAXKEYSIZE + Misc code cleanup + Documentation + Remove spurious note about non-overlapping flags (ITS#7665) + +LMDB 0.9.10 Release (2013/11/12) + Add MDB_NOMEMINIT option + Fix mdb_page_split() again (ITS#7589) + Fix MDB_NORDAHEAD definition (ITS#7734) + Fix mdb_cursor_del() positioning (ITS#7733) + Partial fix for larger page sizes (ITS#7713) + Fix Windows64/MSVC build issues + +LMDB 0.9.9 Release (2013/10/24) + Add mdb_env_get_fd() + Add MDB_NORDAHEAD option + Add MDB_NOLOCK option + Avoid wasting space in mdb_page_split() (ITS#7589) + Fix mdb_page_merge() cursor fixup (ITS#7722) + Fix mdb_cursor_del() on last delete (ITS#7718) + Fix adding WRITEMAP on existing env (ITS#7715) + Fix nested txns (ITS#7515) + Fix mdb_env_copy() O_DIRECT bug (ITS#7682) + Fix mdb_cursor_set(SET_RANGE) return code (ITS#7681) + Fix mdb_rebalance() cursor fixup (ITS#7701) + Misc code cleanup + Documentation + Note that by default, readers need write access + + +LMDB 0.9.8 Release (2013/09/09) + Allow mdb_env_set_mapsize() on an open environment + Fix mdb_dbi_flags() (ITS#7672) + Fix mdb_page_unspill() in nested txns + Fix mdb_cursor_get(CURRENT|NEXT) after a delete + Fix mdb_cursor_get(DUP) to always return key (ITS#7671) + Fix mdb_cursor_del() to always advance to next item (ITS#7670) + Fix mdb_cursor_set(SET_RANGE) for tree with single page (ITS#7681) + Fix mdb_env_copy() retry open if O_DIRECT fails (ITS#7682) + Tweak mdb_page_spill() to be less aggressive + Documentation + Update caveats since mdb_reader_check() added in 0.9.7 + +LMDB 0.9.7 Release (2013/08/17) + Don't leave stale lockfile on failed RDONLY open (ITS#7664) + Fix mdb_page_split() ref beyond cursor depth + Fix read txn data race (ITS#7635) + Fix mdb_rebalance (ITS#7536, #7538) + Fix mdb_drop() (ITS#7561) + Misc DEBUG macro fixes + Add MDB_NOTLS envflag + Add mdb_env_copyfd() + Add mdb_txn_env() (ITS#7660) + Add mdb_dbi_flags() (ITS#7661) + Add mdb_env_get_maxkeysize() + Add mdb_env_reader_list()/mdb_env_reader_check() + Add mdb_page_spill/unspill, remove hard txn size limit + Use shorter names for semaphores (ITS#7615) + Build + Fix install target (ITS#7656) + Documentation + Misc updates for cursors, DB handles, data lifetime + +LMDB 0.9.6 Release (2013/02/25) + Many fixes/enhancements + +LMDB 0.9.5 Release (2012/11/30) + Renamed from libmdb to liblmdb + Many fixes/enhancements diff --git a/libraries/liblmdb/COPYRIGHT b/libraries/liblmdb/COPYRIGHT new file mode 100644 index 0000000..d9118b9 --- /dev/null +++ b/libraries/liblmdb/COPYRIGHT @@ -0,0 +1,20 @@ +Copyright 2011-2020 Howard Chu, Symas Corp. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted only as authorized by the OpenLDAP +Public License. + +A copy of this license is available in the file LICENSE in the +top-level directory of the distribution or, alternatively, at +<http://www.OpenLDAP.org/license.html>. + +OpenLDAP is a registered trademark of the OpenLDAP Foundation. + +Individual files and/or contributed packages may be copyright by +other parties and/or subject to additional restrictions. + +This work also contains materials derived from public sources. + +Additional information about OpenLDAP can be obtained at +<http://www.openldap.org/>. diff --git a/libraries/liblmdb/Doxyfile b/libraries/liblmdb/Doxyfile new file mode 100644 index 0000000..5ca2cfe --- /dev/null +++ b/libraries/liblmdb/Doxyfile @@ -0,0 +1,1631 @@ +# Doxyfile 1.7.1 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = LMDB + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, +# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, +# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given extension. +# Doxygen has a built-in mapping, but you can override or extend it using this +# tag. The format is ext=language, where ext is a file extension, and language +# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, +# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make +# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C +# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions +# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate getter +# and setter methods for a property. Setting this option to YES (the default) +# will make doxygen to replace the get and set methods by a property in the +# documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = YES + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +INLINE_GROUPED_CLASSES = YES +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = YES + +# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to +# determine which symbols to keep in memory and which to flush to disk. +# When the cache is full, less often used symbols will be written to disk. +# For small to medium size projects (<1000 input files) the default value is +# probably good enough. For larger projects a too small cache size can cause +# doxygen to be busy swapping symbols to and from disk most of the time +# causing a significant performance penality. +# If the system has enough physical memory increasing the cache will improve the +# performance by keeping more symbols in memory. Note that the value works on +# a logarithmic scale so increasing the size by one will rougly double the +# memory usage. The cache size is given by this formula: +# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols + +SYMBOL_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespace are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen +# will list include files with double quotes in the documentation +# rather than with sharp brackets. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = NO + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen +# will sort the (brief and detailed) documentation of class members so that +# constructors and destructors are listed first. If set to NO (the default) +# the constructors will appear in the respective orders defined by +# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. +# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO +# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = NO + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. +# This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command <command> <input-file>, where <command> is the value of +# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. The create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. +# You can optionally specify a file name after the option, if omitted +# DoxygenLayout.xml will be used as the name of the layout file. + +LAYOUT_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = lmdb.h midl.h mdb.c midl.c intro.doc + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx +# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command <filter> <input-file>, where <filter> +# is the value of the INPUT_FILTER tag, and <input-file> is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. +# If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. +# Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. +# The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. +# Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. +# Doxygen will adjust the colors in the stylesheet and background images +# according to this color. Hue is specified as an angle on a colorwheel, +# see http://en.wikipedia.org/wiki/Hue for more information. +# For instance the value 0 represents red, 60 is yellow, 120 is green, +# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. +# The allowed range is 0 to 359. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of +# the colors in the HTML output. For a value of 0 the output will use +# grayscales only. A value of 255 will produce the most vivid colors. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to +# the luminance component of the colors in the HTML output. Values below +# 100 gradually make the output lighter, whereas values above 100 make +# the output darker. The value divided by 100 is the actual gamma applied, +# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, +# and 100 does not change the gamma. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting +# this to NO can help when comparing the output of multiple runs. + +HTML_TIMESTAMP = YES + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). + +HTML_DYNAMIC_SECTIONS = NO + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated +# that can be used as input for Qt's qhelpgenerator to generate a +# Qt Compressed Help (.qch) of the generated HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to +# add. For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see +# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters"> +# Qt Help Project / Custom Filters</a>. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's +# filter section matches. +# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes"> +# Qt Help Project / Filter Attributes</a>. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files +# will be generated, which together with the HTML files, form an Eclipse help +# plugin. To install this plugin and make it available under the help contents +# menu in Eclipse, the contents of the directory containing the HTML and XML +# files needs to be copied into the plugins directory of eclipse. The name of +# the directory within the plugins directory should be the same as +# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before +# the help appears. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have +# this name. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to YES, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). +# Windows users are probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, +# and Class Hierarchy pages using a tree view instead of an ordered list. + +USE_INLINE_TREES = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open +# links to external symbols imported via tag files in a separate window. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are +# not supported properly for IE 6.0, but are supported on all modern browsers. +# Note that when changing this option you need to delete any form_*.png files +# in the HTML output before the changes have effect. + +FORMULA_TRANSPARENT = YES + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box +# for the HTML output. The underlying search engine uses javascript +# and DHTML and should work on any modern browser. Note that when using +# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets +# (GENERATE_DOCSET) there is already a search function so this one should +# typically be disabled. For large projects the javascript based search engine +# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. + +SEARCHENGINE = YES + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a PHP enabled web server instead of at the web client +# using Javascript. Doxygen will generate the search PHP script and index +# file to put on the web server. The advantage of the server +# based approach is that it scales better to large projects and allows +# full text search. The disadvances is that it is more difficult to setup +# and does not have live searching capabilities. + +SERVER_BASED_SEARCH = NO + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. +# Note that when enabling USE_PDFLATEX this option is only used for +# generating bitmaps for formulas in the HTML output, but not in the +# Makefile that is written to the output directory. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +# If LATEX_SOURCE_CODE is set to YES then doxygen will include +# source code with syntax highlighting in the LaTeX output. +# Note that which sources are shown also depends on other settings +# such as SOURCE_BROWSER. + +LATEX_SOURCE_CODE = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = YES + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. +# This is useful +# if you want to understand what is going on. +# On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = DEBUG=2 __GNUC__=1 + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse +# the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = tooltag=./man1 + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more +# powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is +# allowed to run in parallel. When set to 0 (the default) doxygen will +# base this on the number of processors available in the system. You can set it +# explicitly to a value larger than 0 to get control over the balance +# between CPU load and processing speed. + +DOT_NUM_THREADS = 0 + +# By default doxygen will write a font called FreeSans.ttf to the output +# directory and reference it in all dot files that doxygen generates. This +# font does not include all possible unicode characters however, so when you need +# these (or just want a differently looking font) you can specify the font name +# using DOT_FONTNAME. You need need to make sure dot is able to find the font, +# which can be done by putting it in a standard location or by setting the +# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory +# containing the font. + +DOT_FONTNAME = FreeSans.ttf + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the output directory to look for the +# FreeSans.ttf font (which doxygen will put there itself). If you specify a +# different font using DOT_FONTNAME you can set the path where dot +# can find it using this tag. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = YES + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES diff --git a/libraries/liblmdb/LICENSE b/libraries/liblmdb/LICENSE new file mode 100644 index 0000000..05ad757 --- /dev/null +++ b/libraries/liblmdb/LICENSE @@ -0,0 +1,47 @@ +The OpenLDAP Public License + Version 2.8, 17 August 2003 + +Redistribution and use of this software and associated documentation +("Software"), with or without modification, are permitted provided +that the following conditions are met: + +1. Redistributions in source form must retain copyright statements + and notices, + +2. Redistributions in binary form must reproduce applicable copyright + statements and notices, this list of conditions, and the following + disclaimer in the documentation and/or other materials provided + with the distribution, and + +3. Redistributions must contain a verbatim copy of this document. + +The OpenLDAP Foundation may revise this license from time to time. +Each revision is distinguished by a version number. You may use +this Software under terms of this license revision or under the +terms of any subsequent revision of the license. + +THIS SOFTWARE IS PROVIDED BY THE OPENLDAP FOUNDATION AND ITS +CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE OPENLDAP FOUNDATION, ITS CONTRIBUTORS, OR THE AUTHOR(S) +OR OWNER(S) OF THE SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +The names of the authors and copyright holders must not be used in +advertising or otherwise to promote the sale, use or other dealing +in this Software without specific, written prior permission. Title +to copyright in this Software shall at all times remain with copyright +holders. + +OpenLDAP is a registered trademark of the OpenLDAP Foundation. + +Copyright 1999-2003 The OpenLDAP Foundation, Redwood City, +California, USA. All Rights Reserved. Permission to copy and +distribute verbatim copies of this document is granted. diff --git a/libraries/liblmdb/Makefile b/libraries/liblmdb/Makefile new file mode 100644 index 0000000..f254511 --- /dev/null +++ b/libraries/liblmdb/Makefile @@ -0,0 +1,117 @@ +# Makefile for liblmdb (Lightning memory-mapped database library). + +######################################################################## +# Configuration. The compiler options must enable threaded compilation. +# +# Preprocessor macros (for CPPFLAGS) of interest... +# Note that the defaults should already be correct for most +# platforms; you should not need to change any of these. +# Read their descriptions in mdb.c if you do: +# +# - MDB_USE_POSIX_SEM +# - MDB_DSYNC +# - MDB_FDATASYNC +# - MDB_FDATASYNC_WORKS +# - MDB_USE_PWRITEV +# - MDB_USE_ROBUST +# +# There may be other macros in mdb.c of interest. You should +# read mdb.c before changing any of them. +# +CC = gcc +AR = ar +W = -W -Wall -Wno-unused-parameter -Wbad-function-cast -Wuninitialized +THREADS = -pthread +OPT = -O2 -g +CFLAGS = $(THREADS) $(OPT) $(W) $(XCFLAGS) +LDLIBS = +SOLIBS = +SOEXT = .so +prefix = /usr/local +exec_prefix = $(prefix) +bindir = $(exec_prefix)/bin +libdir = $(exec_prefix)/lib +includedir = $(prefix)/include +datarootdir = $(prefix)/share +mandir = $(datarootdir)/man + +######################################################################## + +IHDRS = lmdb.h +ILIBS = liblmdb.a liblmdb$(SOEXT) +IPROGS = mdb_stat mdb_copy mdb_dump mdb_load +IDOCS = mdb_stat.1 mdb_copy.1 mdb_dump.1 mdb_load.1 +PROGS = $(IPROGS) mtest mtest2 mtest3 mtest4 mtest5 +all: $(ILIBS) $(PROGS) + +install: $(ILIBS) $(IPROGS) $(IHDRS) + mkdir -p $(DESTDIR)$(bindir) + mkdir -p $(DESTDIR)$(libdir) + mkdir -p $(DESTDIR)$(includedir) + mkdir -p $(DESTDIR)$(mandir)/man1 + for f in $(IPROGS); do cp $$f $(DESTDIR)$(bindir); done + for f in $(ILIBS); do cp $$f $(DESTDIR)$(libdir); done + for f in $(IHDRS); do cp $$f $(DESTDIR)$(includedir); done + for f in $(IDOCS); do cp $$f $(DESTDIR)$(mandir)/man1; done + +clean: + rm -rf $(PROGS) *.[ao] *.[ls]o *~ testdb + +test: all + rm -rf testdb && mkdir testdb + ./mtest && ./mdb_stat testdb + +liblmdb.a: mdb.o midl.o + $(AR) rs $@ mdb.o midl.o + +liblmdb$(SOEXT): mdb.lo midl.lo +# $(CC) $(LDFLAGS) -pthread -shared -Wl,-Bsymbolic -o $@ mdb.o midl.o $(SOLIBS) + $(CC) $(LDFLAGS) -pthread -shared -o $@ mdb.lo midl.lo $(SOLIBS) + +mdb_stat: mdb_stat.o liblmdb.a +mdb_copy: mdb_copy.o liblmdb.a +mdb_dump: mdb_dump.o liblmdb.a +mdb_load: mdb_load.o liblmdb.a +mtest: mtest.o liblmdb.a +mtest2: mtest2.o liblmdb.a +mtest3: mtest3.o liblmdb.a +mtest4: mtest4.o liblmdb.a +mtest5: mtest5.o liblmdb.a +mtest6: mtest6.o liblmdb.a + +mdb.o: mdb.c lmdb.h midl.h + $(CC) $(CFLAGS) $(CPPFLAGS) -c mdb.c + +midl.o: midl.c midl.h + $(CC) $(CFLAGS) $(CPPFLAGS) -c midl.c + +mdb.lo: mdb.c lmdb.h midl.h + $(CC) $(CFLAGS) -fPIC $(CPPFLAGS) -c mdb.c -o $@ + +midl.lo: midl.c midl.h + $(CC) $(CFLAGS) -fPIC $(CPPFLAGS) -c midl.c -o $@ + +%: %.o + $(CC) $(CFLAGS) $(LDFLAGS) $^ $(LDLIBS) -o $@ + +%.o: %.c lmdb.h + $(CC) $(CFLAGS) $(CPPFLAGS) -c $< + +COV_FLAGS=-fprofile-arcs -ftest-coverage +COV_OBJS=xmdb.o xmidl.o + +coverage: xmtest + for i in mtest*.c [0-9]*.c; do j=`basename \$$i .c`; $(MAKE) $$j.o; \ + gcc -o x$$j $$j.o $(COV_OBJS) -pthread $(COV_FLAGS); \ + rm -rf testdb; mkdir testdb; ./x$$j; done + gcov xmdb.c + gcov xmidl.c + +xmtest: mtest.o xmdb.o xmidl.o + gcc -o xmtest mtest.o xmdb.o xmidl.o -pthread $(COV_FLAGS) + +xmdb.o: mdb.c lmdb.h midl.h + $(CC) $(CFLAGS) -fPIC $(CPPFLAGS) -O0 $(COV_FLAGS) -c mdb.c -o $@ + +xmidl.o: midl.c midl.h + $(CC) $(CFLAGS) -fPIC $(CPPFLAGS) -O0 $(COV_FLAGS) -c midl.c -o $@ diff --git a/libraries/liblmdb/intro.doc b/libraries/liblmdb/intro.doc new file mode 100644 index 0000000..4853af7 --- /dev/null +++ b/libraries/liblmdb/intro.doc @@ -0,0 +1,192 @@ +/* + * Copyright 2015-2020 Howard Chu, Symas Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/** @page starting Getting Started + +LMDB is compact, fast, powerful, and robust and implements a simplified +variant of the BerkeleyDB (BDB) API. (BDB is also very powerful, and verbosely +documented in its own right.) After reading this page, the main +\ref mdb documentation should make sense. Thanks to Bert Hubert +for creating the +<a href="https://github.com/ahupowerdns/ahutils/blob/master/lmdb-semantics.md"> +initial version</a> of this writeup. + +Everything starts with an environment, created by #mdb_env_create(). +Once created, this environment must also be opened with #mdb_env_open(). + +#mdb_env_open() gets passed a name which is interpreted as a directory +path. Note that this directory must exist already, it is not created +for you. Within that directory, a lock file and a storage file will be +generated. If you don't want to use a directory, you can pass the +#MDB_NOSUBDIR option, in which case the path you provided is used +directly as the data file, and another file with a "-lock" suffix +added will be used for the lock file. + +Once the environment is open, a transaction can be created within it +using #mdb_txn_begin(). Transactions may be read-write or read-only, +and read-write transactions may be nested. A transaction must only +be used by one thread at a time. Transactions are always required, +even for read-only access. The transaction provides a consistent +view of the data. + +Once a transaction has been created, a database can be opened within it +using #mdb_dbi_open(). If only one database will ever be used in the +environment, a NULL can be passed as the database name. For named +databases, the #MDB_CREATE flag must be used to create the database +if it doesn't already exist. Also, #mdb_env_set_maxdbs() must be +called after #mdb_env_create() and before #mdb_env_open() to set the +maximum number of named databases you want to support. + +Note: a single transaction can open multiple databases. Generally +databases should only be opened once, by the first transaction in +the process. After the first transaction completes, the database +handles can freely be used by all subsequent transactions. + +Within a transaction, #mdb_get() and #mdb_put() can store single +key/value pairs if that is all you need to do (but see \ref Cursors +below if you want to do more). + +A key/value pair is expressed as two #MDB_val structures. This struct +has two fields, \c mv_size and \c mv_data. The data is a \c void pointer to +an array of \c mv_size bytes. + +Because LMDB is very efficient (and usually zero-copy), the data returned +in an #MDB_val structure may be memory-mapped straight from disk. In +other words <b>look but do not touch</b> (or free() for that matter). +Once a transaction is closed, the values can no longer be used, so +make a copy if you need to keep them after that. + +@section Cursors Cursors + +To do more powerful things, we must use a cursor. + +Within the transaction, a cursor can be created with #mdb_cursor_open(). +With this cursor we can store/retrieve/delete (multiple) values using +#mdb_cursor_get(), #mdb_cursor_put(), and #mdb_cursor_del(). + +#mdb_cursor_get() positions itself depending on the cursor operation +requested, and for some operations, on the supplied key. For example, +to list all key/value pairs in a database, use operation #MDB_FIRST for +the first call to #mdb_cursor_get(), and #MDB_NEXT on subsequent calls, +until the end is hit. + +To retrieve all keys starting from a specified key value, use #MDB_SET. +For more cursor operations, see the \ref mdb docs. + +When using #mdb_cursor_put(), either the function will position the +cursor for you based on the \b key, or you can use operation +#MDB_CURRENT to use the current position of the cursor. Note that +\b key must then match the current position's key. + +@subsection summary Summarizing the Opening + +So we have a cursor in a transaction which opened a database in an +environment which is opened from a filesystem after it was +separately created. + +Or, we create an environment, open it from a filesystem, create a +transaction within it, open a database within that transaction, +and create a cursor within all of the above. + +Got it? + +@section thrproc Threads and Processes + +LMDB uses POSIX locks on files, and these locks have issues if one +process opens a file multiple times. Because of this, do not +#mdb_env_open() a file multiple times from a single process. Instead, +share the LMDB environment that has opened the file across all threads. +Otherwise, if a single process opens the same environment multiple times, +closing it once will remove all the locks held on it, and the other +instances will be vulnerable to corruption from other processes. + +Also note that a transaction is tied to one thread by default using +Thread Local Storage. If you want to pass read-only transactions across +threads, you can use the #MDB_NOTLS option on the environment. + +@section txns Transactions, Rollbacks, etc. + +To actually get anything done, a transaction must be committed using +#mdb_txn_commit(). Alternatively, all of a transaction's operations +can be discarded using #mdb_txn_abort(). In a read-only transaction, +any cursors will \b not automatically be freed. In a read-write +transaction, all cursors will be freed and must not be used again. + +For read-only transactions, obviously there is nothing to commit to +storage. The transaction still must eventually be aborted to close +any database handle(s) opened in it, or committed to keep the +database handles around for reuse in new transactions. + +In addition, as long as a transaction is open, a consistent view of +the database is kept alive, which requires storage. A read-only +transaction that no longer requires this consistent view should +be terminated (committed or aborted) when the view is no longer +needed (but see below for an optimization). + +There can be multiple simultaneously active read-only transactions +but only one that can write. Once a single read-write transaction +is opened, all further attempts to begin one will block until the +first one is committed or aborted. This has no effect on read-only +transactions, however, and they may continue to be opened at any time. + +@section dupkeys Duplicate Keys + +#mdb_get() and #mdb_put() respectively have no and only some support +for multiple key/value pairs with identical keys. If there are multiple +values for a key, #mdb_get() will only return the first value. + +When multiple values for one key are required, pass the #MDB_DUPSORT +flag to #mdb_dbi_open(). In an #MDB_DUPSORT database, by default +#mdb_put() will not replace the value for a key if the key existed +already. Instead it will add the new value to the key. In addition, +#mdb_del() will pay attention to the value field too, allowing for +specific values of a key to be deleted. + +Finally, additional cursor operations become available for +traversing through and retrieving duplicate values. + +@section optim Some Optimization + +If you frequently begin and abort read-only transactions, as an +optimization, it is possible to only reset and renew a transaction. + +#mdb_txn_reset() releases any old copies of data kept around for +a read-only transaction. To reuse this reset transaction, call +#mdb_txn_renew() on it. Any cursors in this transaction must also +be renewed using #mdb_cursor_renew(). + +Note that #mdb_txn_reset() is similar to #mdb_txn_abort() and will +close any databases you opened within the transaction. + +To permanently free a transaction, reset or not, use #mdb_txn_abort(). + +@section cleanup Cleaning Up + +For read-only transactions, any cursors created within it must +be closed using #mdb_cursor_close(). + +It is very rarely necessary to close a database handle, and in +general they should just be left open. + +@section onward The Full API + +The full \ref mdb documentation lists further details, like how to: + + \li size a database (the default limits are intentionally small) + \li drop and clean a database + \li detect and report errors + \li optimize (bulk) loading speed + \li (temporarily) reduce robustness to gain even more speed + \li gather statistics about the database + \li define custom sort orders + +*/ diff --git a/libraries/liblmdb/lmdb.h b/libraries/liblmdb/lmdb.h new file mode 100644 index 0000000..361c930 --- /dev/null +++ b/libraries/liblmdb/lmdb.h @@ -0,0 +1,1608 @@ +/** @file lmdb.h + * @brief Lightning memory-mapped database library + * + * @mainpage Lightning Memory-Mapped Database Manager (LMDB) + * + * @section intro_sec Introduction + * LMDB is a Btree-based database management library modeled loosely on the + * BerkeleyDB API, but much simplified. The entire database is exposed + * in a memory map, and all data fetches return data directly + * from the mapped memory, so no malloc's or memcpy's occur during + * data fetches. As such, the library is extremely simple because it + * requires no page caching layer of its own, and it is extremely high + * performance and memory-efficient. It is also fully transactional with + * full ACID semantics, and when the memory map is read-only, the + * database integrity cannot be corrupted by stray pointer writes from + * application code. + * + * The library is fully thread-aware and supports concurrent read/write + * access from multiple processes and threads. Data pages use a copy-on- + * write strategy so no active data pages are ever overwritten, which + * also provides resistance to corruption and eliminates the need of any + * special recovery procedures after a system crash. Writes are fully + * serialized; only one write transaction may be active at a time, which + * guarantees that writers can never deadlock. The database structure is + * multi-versioned so readers run with no locks; writers cannot block + * readers, and readers don't block writers. + * + * Unlike other well-known database mechanisms which use either write-ahead + * transaction logs or append-only data writes, LMDB requires no maintenance + * during operation. Both write-ahead loggers and append-only databases + * require periodic checkpointing and/or compaction of their log or database + * files otherwise they grow without bound. LMDB tracks free pages within + * the database and re-uses them for new write operations, so the database + * size does not grow without bound in normal use. + * + * The memory map can be used as a read-only or read-write map. It is + * read-only by default as this provides total immunity to corruption. + * Using read-write mode offers much higher write performance, but adds + * the possibility for stray application writes thru pointers to silently + * corrupt the database. Of course if your application code is known to + * be bug-free (...) then this is not an issue. + * + * If this is your first time using a transactional embedded key/value + * store, you may find the \ref starting page to be helpful. + * + * @section caveats_sec Caveats + * Troubleshooting the lock file, plus semaphores on BSD systems: + * + * - A broken lockfile can cause sync issues. + * Stale reader transactions left behind by an aborted program + * cause further writes to grow the database quickly, and + * stale locks can block further operation. + * + * Fix: Check for stale readers periodically, using the + * #mdb_reader_check function or the \ref mdb_stat_1 "mdb_stat" tool. + * Stale writers will be cleared automatically on some systems: + * - Windows - automatic + * - Linux, systems using POSIX mutexes with Robust option - automatic + * - not on BSD, systems using POSIX semaphores. + * Otherwise just make all programs using the database close it; + * the lockfile is always reset on first open of the environment. + * + * - On BSD systems or others configured with MDB_USE_POSIX_SEM, + * startup can fail due to semaphores owned by another userid. + * + * Fix: Open and close the database as the user which owns the + * semaphores (likely last user) or as root, while no other + * process is using the database. + * + * Restrictions/caveats (in addition to those listed for some functions): + * + * - Only the database owner should normally use the database on + * BSD systems or when otherwise configured with MDB_USE_POSIX_SEM. + * Multiple users can cause startup to fail later, as noted above. + * + * - There is normally no pure read-only mode, since readers need write + * access to locks and lock file. Exceptions: On read-only filesystems + * or with the #MDB_NOLOCK flag described under #mdb_env_open(). + * + * - An LMDB configuration will often reserve considerable \b unused + * memory address space and maybe file size for future growth. + * This does not use actual memory or disk space, but users may need + * to understand the difference so they won't be scared off. + * + * - By default, in versions before 0.9.10, unused portions of the data + * file might receive garbage data from memory freed by other code. + * (This does not happen when using the #MDB_WRITEMAP flag.) As of + * 0.9.10 the default behavior is to initialize such memory before + * writing to the data file. Since there may be a slight performance + * cost due to this initialization, applications may disable it using + * the #MDB_NOMEMINIT flag. Applications handling sensitive data + * which must not be written should not use this flag. This flag is + * irrelevant when using #MDB_WRITEMAP. + * + * - A thread can only use one transaction at a time, plus any child + * transactions. Each transaction belongs to one thread. See below. + * The #MDB_NOTLS flag changes this for read-only transactions. + * + * - Use an MDB_env* in the process which opened it, not after fork(). + * + * - Do not have open an LMDB database twice in the same process at + * the same time. Not even from a plain open() call - close()ing it + * breaks fcntl() advisory locking. (It is OK to reopen it after + * fork() - exec*(), since the lockfile has FD_CLOEXEC set.) + * + * - Avoid long-lived transactions. Read transactions prevent + * reuse of pages freed by newer write transactions, thus the + * database can grow quickly. Write transactions prevent + * other write transactions, since writes are serialized. + * + * - Avoid suspending a process with active transactions. These + * would then be "long-lived" as above. Also read transactions + * suspended when writers commit could sometimes see wrong data. + * + * ...when several processes can use a database concurrently: + * + * - Avoid aborting a process with an active transaction. + * The transaction becomes "long-lived" as above until a check + * for stale readers is performed or the lockfile is reset, + * since the process may not remove it from the lockfile. + * + * This does not apply to write transactions if the system clears + * stale writers, see above. + * + * - If you do that anyway, do a periodic check for stale readers. Or + * close the environment once in a while, so the lockfile can get reset. + * + * - Do not use LMDB databases on remote filesystems, even between + * processes on the same host. This breaks flock() on some OSes, + * possibly memory map sync, and certainly sync between programs + * on different hosts. + * + * - Opening a database can fail if another process is opening or + * closing it at exactly the same time. + * + * @author Howard Chu, Symas Corporation. + * + * @copyright Copyright 2011-2020 Howard Chu, Symas Corp. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + * + * @par Derived From: + * This code is derived from btree.c written by Martin Hedenfalk. + * + * Copyright (c) 2009, 2010 Martin Hedenfalk <martin@bzero.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef _LMDB_H_ +#define _LMDB_H_ + +#include <sys/types.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** Unix permissions for creating files, or dummy definition for Windows */ +#ifdef _MSC_VER +typedef int mdb_mode_t; +#else +typedef mode_t mdb_mode_t; +#endif + +/** An abstraction for a file handle. + * On POSIX systems file handles are small integers. On Windows + * they're opaque pointers. + */ +#ifdef _WIN32 +typedef void *mdb_filehandle_t; +#else +typedef int mdb_filehandle_t; +#endif + +/** @defgroup mdb LMDB API + * @{ + * @brief OpenLDAP Lightning Memory-Mapped Database Manager + */ +/** @defgroup Version Version Macros + * @{ + */ +/** Library major version */ +#define MDB_VERSION_MAJOR 0 +/** Library minor version */ +#define MDB_VERSION_MINOR 9 +/** Library patch version */ +#define MDB_VERSION_PATCH 27 + +/** Combine args a,b,c into a single integer for easy version comparisons */ +#define MDB_VERINT(a,b,c) (((a) << 24) | ((b) << 16) | (c)) + +/** The full library version as a single integer */ +#define MDB_VERSION_FULL \ + MDB_VERINT(MDB_VERSION_MAJOR,MDB_VERSION_MINOR,MDB_VERSION_PATCH) + +/** The release date of this library version */ +#define MDB_VERSION_DATE "October 26, 2020" + +/** A stringifier for the version info */ +#define MDB_VERSTR(a,b,c,d) "LMDB " #a "." #b "." #c ": (" d ")" + +/** A helper for the stringifier macro */ +#define MDB_VERFOO(a,b,c,d) MDB_VERSTR(a,b,c,d) + +/** The full library version as a C string */ +#define MDB_VERSION_STRING \ + MDB_VERFOO(MDB_VERSION_MAJOR,MDB_VERSION_MINOR,MDB_VERSION_PATCH,MDB_VERSION_DATE) +/** @} */ + +/** @brief Opaque structure for a database environment. + * + * A DB environment supports multiple databases, all residing in the same + * shared-memory map. + */ +typedef struct MDB_env MDB_env; + +/** @brief Opaque structure for a transaction handle. + * + * All database operations require a transaction handle. Transactions may be + * read-only or read-write. + */ +typedef struct MDB_txn MDB_txn; + +/** @brief A handle for an individual database in the DB environment. */ +typedef unsigned int MDB_dbi; + +/** @brief Opaque structure for navigating through a database */ +typedef struct MDB_cursor MDB_cursor; + +/** @brief Generic structure used for passing keys and data in and out + * of the database. + * + * Values returned from the database are valid only until a subsequent + * update operation, or the end of the transaction. Do not modify or + * free them, they commonly point into the database itself. + * + * Key sizes must be between 1 and #mdb_env_get_maxkeysize() inclusive. + * The same applies to data sizes in databases with the #MDB_DUPSORT flag. + * Other data items can in theory be from 0 to 0xffffffff bytes long. + */ +typedef struct MDB_val { + size_t mv_size; /**< size of the data item */ + void *mv_data; /**< address of the data item */ +} MDB_val; + +/** @brief A callback function used to compare two keys in a database */ +typedef int (MDB_cmp_func)(const MDB_val *a, const MDB_val *b); + +/** @brief A callback function used to relocate a position-dependent data item + * in a fixed-address database. + * + * The \b newptr gives the item's desired address in + * the memory map, and \b oldptr gives its previous address. The item's actual + * data resides at the address in \b item. This callback is expected to walk + * through the fields of the record in \b item and modify any + * values based at the \b oldptr address to be relative to the \b newptr address. + * @param[in,out] item The item that is to be relocated. + * @param[in] oldptr The previous address. + * @param[in] newptr The new address to relocate to. + * @param[in] relctx An application-provided context, set by #mdb_set_relctx(). + * @todo This feature is currently unimplemented. + */ +typedef void (MDB_rel_func)(MDB_val *item, void *oldptr, void *newptr, void *relctx); + +/** @defgroup mdb_env Environment Flags + * @{ + */ + /** mmap at a fixed address (experimental) */ +#define MDB_FIXEDMAP 0x01 + /** no environment directory */ +#define MDB_NOSUBDIR 0x4000 + /** don't fsync after commit */ +#define MDB_NOSYNC 0x10000 + /** read only */ +#define MDB_RDONLY 0x20000 + /** don't fsync metapage after commit */ +#define MDB_NOMETASYNC 0x40000 + /** use writable mmap */ +#define MDB_WRITEMAP 0x80000 + /** use asynchronous msync when #MDB_WRITEMAP is used */ +#define MDB_MAPASYNC 0x100000 + /** tie reader locktable slots to #MDB_txn objects instead of to threads */ +#define MDB_NOTLS 0x200000 + /** don't do any locking, caller must manage their own locks */ +#define MDB_NOLOCK 0x400000 + /** don't do readahead (no effect on Windows) */ +#define MDB_NORDAHEAD 0x800000 + /** don't initialize malloc'd memory before writing to datafile */ +#define MDB_NOMEMINIT 0x1000000 +/** @} */ + +/** @defgroup mdb_dbi_open Database Flags + * @{ + */ + /** use reverse string keys */ +#define MDB_REVERSEKEY 0x02 + /** use sorted duplicates */ +#define MDB_DUPSORT 0x04 + /** numeric keys in native byte order: either unsigned int or size_t. + * The keys must all be of the same size. */ +#define MDB_INTEGERKEY 0x08 + /** with #MDB_DUPSORT, sorted dup items have fixed size */ +#define MDB_DUPFIXED 0x10 + /** with #MDB_DUPSORT, dups are #MDB_INTEGERKEY-style integers */ +#define MDB_INTEGERDUP 0x20 + /** with #MDB_DUPSORT, use reverse string dups */ +#define MDB_REVERSEDUP 0x40 + /** create DB if not already existing */ +#define MDB_CREATE 0x40000 +/** @} */ + +/** @defgroup mdb_put Write Flags + * @{ + */ +/** For put: Don't write if the key already exists. */ +#define MDB_NOOVERWRITE 0x10 +/** Only for #MDB_DUPSORT<br> + * For put: don't write if the key and data pair already exist.<br> + * For mdb_cursor_del: remove all duplicate data items. + */ +#define MDB_NODUPDATA 0x20 +/** For mdb_cursor_put: overwrite the current key/data pair */ +#define MDB_CURRENT 0x40 +/** For put: Just reserve space for data, don't copy it. Return a + * pointer to the reserved space. + */ +#define MDB_RESERVE 0x10000 +/** Data is being appended, don't split full pages. */ +#define MDB_APPEND 0x20000 +/** Duplicate data is being appended, don't split full pages. */ +#define MDB_APPENDDUP 0x40000 +/** Store multiple data items in one call. Only for #MDB_DUPFIXED. */ +#define MDB_MULTIPLE 0x80000 +/* @} */ + +/** @defgroup mdb_copy Copy Flags + * @{ + */ +/** Compacting copy: Omit free space from copy, and renumber all + * pages sequentially. + */ +#define MDB_CP_COMPACT 0x01 +/* @} */ + +/** @brief Cursor Get operations. + * + * This is the set of all operations for retrieving data + * using a cursor. + */ +typedef enum MDB_cursor_op { + MDB_FIRST, /**< Position at first key/data item */ + MDB_FIRST_DUP, /**< Position at first data item of current key. + Only for #MDB_DUPSORT */ + MDB_GET_BOTH, /**< Position at key/data pair. Only for #MDB_DUPSORT */ + MDB_GET_BOTH_RANGE, /**< position at key, nearest data. Only for #MDB_DUPSORT */ + MDB_GET_CURRENT, /**< Return key/data at current cursor position */ + MDB_GET_MULTIPLE, /**< Return up to a page of duplicate data items + from current cursor position. Move cursor to prepare + for #MDB_NEXT_MULTIPLE. Only for #MDB_DUPFIXED */ + MDB_LAST, /**< Position at last key/data item */ + MDB_LAST_DUP, /**< Position at last data item of current key. + Only for #MDB_DUPSORT */ + MDB_NEXT, /**< Position at next data item */ + MDB_NEXT_DUP, /**< Position at next data item of current key. + Only for #MDB_DUPSORT */ + MDB_NEXT_MULTIPLE, /**< Return up to a page of duplicate data items + from next cursor position. Move cursor to prepare + for #MDB_NEXT_MULTIPLE. Only for #MDB_DUPFIXED */ + MDB_NEXT_NODUP, /**< Position at first data item of next key */ + MDB_PREV, /**< Position at previous data item */ + MDB_PREV_DUP, /**< Position at previous data item of current key. + Only for #MDB_DUPSORT */ + MDB_PREV_NODUP, /**< Position at last data item of previous key */ + MDB_SET, /**< Position at specified key */ + MDB_SET_KEY, /**< Position at specified key, return key + data */ + MDB_SET_RANGE, /**< Position at first key greater than or equal to specified key. */ + MDB_PREV_MULTIPLE /**< Position at previous page and return up to + a page of duplicate data items. Only for #MDB_DUPFIXED */ +} MDB_cursor_op; + +/** @defgroup errors Return Codes + * + * BerkeleyDB uses -30800 to -30999, we'll go under them + * @{ + */ + /** Successful result */ +#define MDB_SUCCESS 0 + /** key/data pair already exists */ +#define MDB_KEYEXIST (-30799) + /** key/data pair not found (EOF) */ +#define MDB_NOTFOUND (-30798) + /** Requested page not found - this usually indicates corruption */ +#define MDB_PAGE_NOTFOUND (-30797) + /** Located page was wrong type */ +#define MDB_CORRUPTED (-30796) + /** Update of meta page failed or environment had fatal error */ +#define MDB_PANIC (-30795) + /** Environment version mismatch */ +#define MDB_VERSION_MISMATCH (-30794) + /** File is not a valid LMDB file */ +#define MDB_INVALID (-30793) + /** Environment mapsize reached */ +#define MDB_MAP_FULL (-30792) + /** Environment maxdbs reached */ +#define MDB_DBS_FULL (-30791) + /** Environment maxreaders reached */ +#define MDB_READERS_FULL (-30790) + /** Too many TLS keys in use - Windows only */ +#define MDB_TLS_FULL (-30789) + /** Txn has too many dirty pages */ +#define MDB_TXN_FULL (-30788) + /** Cursor stack too deep - internal error */ +#define MDB_CURSOR_FULL (-30787) + /** Page has not enough space - internal error */ +#define MDB_PAGE_FULL (-30786) + /** Database contents grew beyond environment mapsize */ +#define MDB_MAP_RESIZED (-30785) + /** Operation and DB incompatible, or DB type changed. This can mean: + * <ul> + * <li>The operation expects an #MDB_DUPSORT / #MDB_DUPFIXED database. + * <li>Opening a named DB when the unnamed DB has #MDB_DUPSORT / #MDB_INTEGERKEY. + * <li>Accessing a data record as a database, or vice versa. + * <li>The database was dropped and recreated with different flags. + * </ul> + */ +#define MDB_INCOMPATIBLE (-30784) + /** Invalid reuse of reader locktable slot */ +#define MDB_BAD_RSLOT (-30783) + /** Transaction must abort, has a child, or is invalid */ +#define MDB_BAD_TXN (-30782) + /** Unsupported size of key/DB name/data, or wrong DUPFIXED size */ +#define MDB_BAD_VALSIZE (-30781) + /** The specified DBI was changed unexpectedly */ +#define MDB_BAD_DBI (-30780) + /** The last defined error code */ +#define MDB_LAST_ERRCODE MDB_BAD_DBI +/** @} */ + +/** @brief Statistics for a database in the environment */ +typedef struct MDB_stat { + unsigned int ms_psize; /**< Size of a database page. + This is currently the same for all databases. */ + unsigned int ms_depth; /**< Depth (height) of the B-tree */ + size_t ms_branch_pages; /**< Number of internal (non-leaf) pages */ + size_t ms_leaf_pages; /**< Number of leaf pages */ + size_t ms_overflow_pages; /**< Number of overflow pages */ + size_t ms_entries; /**< Number of data items */ +} MDB_stat; + +/** @brief Information about the environment */ +typedef struct MDB_envinfo { + void *me_mapaddr; /**< Address of map, if fixed */ + size_t me_mapsize; /**< Size of the data memory map */ + size_t me_last_pgno; /**< ID of the last used page */ + size_t me_last_txnid; /**< ID of the last committed transaction */ + unsigned int me_maxreaders; /**< max reader slots in the environment */ + unsigned int me_numreaders; /**< max reader slots used in the environment */ +} MDB_envinfo; + + /** @brief Return the LMDB library version information. + * + * @param[out] major if non-NULL, the library major version number is copied here + * @param[out] minor if non-NULL, the library minor version number is copied here + * @param[out] patch if non-NULL, the library patch version number is copied here + * @retval "version string" The library version as a string + */ +char *mdb_version(int *major, int *minor, int *patch); + + /** @brief Return a string describing a given error code. + * + * This function is a superset of the ANSI C X3.159-1989 (ANSI C) strerror(3) + * function. If the error code is greater than or equal to 0, then the string + * returned by the system function strerror(3) is returned. If the error code + * is less than 0, an error string corresponding to the LMDB library error is + * returned. See @ref errors for a list of LMDB-specific error codes. + * @param[in] err The error code + * @retval "error message" The description of the error + */ +char *mdb_strerror(int err); + + /** @brief Create an LMDB environment handle. + * + * This function allocates memory for a #MDB_env structure. To release + * the allocated memory and discard the handle, call #mdb_env_close(). + * Before the handle may be used, it must be opened using #mdb_env_open(). + * Various other options may also need to be set before opening the handle, + * e.g. #mdb_env_set_mapsize(), #mdb_env_set_maxreaders(), #mdb_env_set_maxdbs(), + * depending on usage requirements. + * @param[out] env The address where the new handle will be stored + * @return A non-zero error value on failure and 0 on success. + */ +int mdb_env_create(MDB_env **env); + + /** @brief Open an environment handle. + * + * If this function fails, #mdb_env_close() must be called to discard the #MDB_env handle. + * @param[in] env An environment handle returned by #mdb_env_create() + * @param[in] path The directory in which the database files reside. This + * directory must already exist and be writable. + * @param[in] flags Special options for this environment. This parameter + * must be set to 0 or by bitwise OR'ing together one or more of the + * values described here. + * Flags set by mdb_env_set_flags() are also used. + * <ul> + * <li>#MDB_FIXEDMAP + * use a fixed address for the mmap region. This flag must be specified + * when creating the environment, and is stored persistently in the environment. + * If successful, the memory map will always reside at the same virtual address + * and pointers used to reference data items in the database will be constant + * across multiple invocations. This option may not always work, depending on + * how the operating system has allocated memory to shared libraries and other uses. + * The feature is highly experimental. + * <li>#MDB_NOSUBDIR + * By default, LMDB creates its environment in a directory whose + * pathname is given in \b path, and creates its data and lock files + * under that directory. With this option, \b path is used as-is for + * the database main data file. The database lock file is the \b path + * with "-lock" appended. + * <li>#MDB_RDONLY + * Open the environment in read-only mode. No write operations will be + * allowed. LMDB will still modify the lock file - except on read-only + * filesystems, where LMDB does not use locks. + * <li>#MDB_WRITEMAP + * Use a writeable memory map unless MDB_RDONLY is set. This uses + * fewer mallocs but loses protection from application bugs + * like wild pointer writes and other bad updates into the database. + * This may be slightly faster for DBs that fit entirely in RAM, but + * is slower for DBs larger than RAM. + * Incompatible with nested transactions. + * Do not mix processes with and without MDB_WRITEMAP on the same + * environment. This can defeat durability (#mdb_env_sync etc). + * <li>#MDB_NOMETASYNC + * Flush system buffers to disk only once per transaction, omit the + * metadata flush. Defer that until the system flushes files to disk, + * or next non-MDB_RDONLY commit or #mdb_env_sync(). This optimization + * maintains database integrity, but a system crash may undo the last + * committed transaction. I.e. it preserves the ACI (atomicity, + * consistency, isolation) but not D (durability) database property. + * This flag may be changed at any time using #mdb_env_set_flags(). + * <li>#MDB_NOSYNC + * Don't flush system buffers to disk when committing a transaction. + * This optimization means a system crash can corrupt the database or + * lose the last transactions if buffers are not yet flushed to disk. + * The risk is governed by how often the system flushes dirty buffers + * to disk and how often #mdb_env_sync() is called. However, if the + * filesystem preserves write order and the #MDB_WRITEMAP flag is not + * used, transactions exhibit ACI (atomicity, consistency, isolation) + * properties and only lose D (durability). I.e. database integrity + * is maintained, but a system crash may undo the final transactions. + * Note that (#MDB_NOSYNC | #MDB_WRITEMAP) leaves the system with no + * hint for when to write transactions to disk, unless #mdb_env_sync() + * is called. (#MDB_MAPASYNC | #MDB_WRITEMAP) may be preferable. + * This flag may be changed at any time using #mdb_env_set_flags(). + * <li>#MDB_MAPASYNC + * When using #MDB_WRITEMAP, use asynchronous flushes to disk. + * As with #MDB_NOSYNC, a system crash can then corrupt the + * database or lose the last transactions. Calling #mdb_env_sync() + * ensures on-disk database integrity until next commit. + * This flag may be changed at any time using #mdb_env_set_flags(). + * <li>#MDB_NOTLS + * Don't use Thread-Local Storage. Tie reader locktable slots to + * #MDB_txn objects instead of to threads. I.e. #mdb_txn_reset() keeps + * the slot reseved for the #MDB_txn object. A thread may use parallel + * read-only transactions. A read-only transaction may span threads if + * the user synchronizes its use. Applications that multiplex many + * user threads over individual OS threads need this option. Such an + * application must also serialize the write transactions in an OS + * thread, since LMDB's write locking is unaware of the user threads. + * <li>#MDB_NOLOCK + * Don't do any locking. If concurrent access is anticipated, the + * caller must manage all concurrency itself. For proper operation + * the caller must enforce single-writer semantics, and must ensure + * that no readers are using old transactions while a writer is + * active. The simplest approach is to use an exclusive lock so that + * no readers may be active at all when a writer begins. + * <li>#MDB_NORDAHEAD + * Turn off readahead. Most operating systems perform readahead on + * read requests by default. This option turns it off if the OS + * supports it. Turning it off may help random read performance + * when the DB is larger than RAM and system RAM is full. + * The option is not implemented on Windows. + * <li>#MDB_NOMEMINIT + * Don't initialize malloc'd memory before writing to unused spaces + * in the data file. By default, memory for pages written to the data + * file is obtained using malloc. While these pages may be reused in + * subsequent transactions, freshly malloc'd pages will be initialized + * to zeroes before use. This avoids persisting leftover data from other + * code (that used the heap and subsequently freed the memory) into the + * data file. Note that many other system libraries may allocate + * and free memory from the heap for arbitrary uses. E.g., stdio may + * use the heap for file I/O buffers. This initialization step has a + * modest performance cost so some applications may want to disable + * it using this flag. This option can be a problem for applications + * which handle sensitive data like passwords, and it makes memory + * checkers like Valgrind noisy. This flag is not needed with #MDB_WRITEMAP, + * which writes directly to the mmap instead of using malloc for pages. The + * initialization is also skipped if #MDB_RESERVE is used; the + * caller is expected to overwrite all of the memory that was + * reserved in that case. + * This flag may be changed at any time using #mdb_env_set_flags(). + * </ul> + * @param[in] mode The UNIX permissions to set on created files and semaphores. + * This parameter is ignored on Windows. + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>#MDB_VERSION_MISMATCH - the version of the LMDB library doesn't match the + * version that created the database environment. + * <li>#MDB_INVALID - the environment file headers are corrupted. + * <li>ENOENT - the directory specified by the path parameter doesn't exist. + * <li>EACCES - the user didn't have permission to access the environment files. + * <li>EAGAIN - the environment was locked by another process. + * </ul> + */ +int mdb_env_open(MDB_env *env, const char *path, unsigned int flags, mdb_mode_t mode); + + /** @brief Copy an LMDB environment to the specified path. + * + * This function may be used to make a backup of an existing environment. + * No lockfile is created, since it gets recreated at need. + * @note This call can trigger significant file size growth if run in + * parallel with write transactions, because it employs a read-only + * transaction. See long-lived transactions under @ref caveats_sec. + * @param[in] env An environment handle returned by #mdb_env_create(). It + * must have already been opened successfully. + * @param[in] path The directory in which the copy will reside. This + * directory must already exist and be writable but must otherwise be + * empty. + * @return A non-zero error value on failure and 0 on success. + */ +int mdb_env_copy(MDB_env *env, const char *path); + + /** @brief Copy an LMDB environment to the specified file descriptor. + * + * This function may be used to make a backup of an existing environment. + * No lockfile is created, since it gets recreated at need. + * @note This call can trigger significant file size growth if run in + * parallel with write transactions, because it employs a read-only + * transaction. See long-lived transactions under @ref caveats_sec. + * @param[in] env An environment handle returned by #mdb_env_create(). It + * must have already been opened successfully. + * @param[in] fd The filedescriptor to write the copy to. It must + * have already been opened for Write access. + * @return A non-zero error value on failure and 0 on success. + */ +int mdb_env_copyfd(MDB_env *env, mdb_filehandle_t fd); + + /** @brief Copy an LMDB environment to the specified path, with options. + * + * This function may be used to make a backup of an existing environment. + * No lockfile is created, since it gets recreated at need. + * @note This call can trigger significant file size growth if run in + * parallel with write transactions, because it employs a read-only + * transaction. See long-lived transactions under @ref caveats_sec. + * @param[in] env An environment handle returned by #mdb_env_create(). It + * must have already been opened successfully. + * @param[in] path The directory in which the copy will reside. This + * directory must already exist and be writable but must otherwise be + * empty. + * @param[in] flags Special options for this operation. This parameter + * must be set to 0 or by bitwise OR'ing together one or more of the + * values described here. + * <ul> + * <li>#MDB_CP_COMPACT - Perform compaction while copying: omit free + * pages and sequentially renumber all pages in output. This option + * consumes more CPU and runs more slowly than the default. + * Currently it fails if the environment has suffered a page leak. + * </ul> + * @return A non-zero error value on failure and 0 on success. + */ +int mdb_env_copy2(MDB_env *env, const char *path, unsigned int flags); + + /** @brief Copy an LMDB environment to the specified file descriptor, + * with options. + * + * This function may be used to make a backup of an existing environment. + * No lockfile is created, since it gets recreated at need. See + * #mdb_env_copy2() for further details. + * @note This call can trigger significant file size growth if run in + * parallel with write transactions, because it employs a read-only + * transaction. See long-lived transactions under @ref caveats_sec. + * @param[in] env An environment handle returned by #mdb_env_create(). It + * must have already been opened successfully. + * @param[in] fd The filedescriptor to write the copy to. It must + * have already been opened for Write access. + * @param[in] flags Special options for this operation. + * See #mdb_env_copy2() for options. + * @return A non-zero error value on failure and 0 on success. + */ +int mdb_env_copyfd2(MDB_env *env, mdb_filehandle_t fd, unsigned int flags); + + /** @brief Return statistics about the LMDB environment. + * + * @param[in] env An environment handle returned by #mdb_env_create() + * @param[out] stat The address of an #MDB_stat structure + * where the statistics will be copied + */ +int mdb_env_stat(MDB_env *env, MDB_stat *stat); + + /** @brief Return information about the LMDB environment. + * + * @param[in] env An environment handle returned by #mdb_env_create() + * @param[out] stat The address of an #MDB_envinfo structure + * where the information will be copied + */ +int mdb_env_info(MDB_env *env, MDB_envinfo *stat); + + /** @brief Flush the data buffers to disk. + * + * Data is always written to disk when #mdb_txn_commit() is called, + * but the operating system may keep it buffered. LMDB always flushes + * the OS buffers upon commit as well, unless the environment was + * opened with #MDB_NOSYNC or in part #MDB_NOMETASYNC. This call is + * not valid if the environment was opened with #MDB_RDONLY. + * @param[in] env An environment handle returned by #mdb_env_create() + * @param[in] force If non-zero, force a synchronous flush. Otherwise + * if the environment has the #MDB_NOSYNC flag set the flushes + * will be omitted, and with #MDB_MAPASYNC they will be asynchronous. + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EACCES - the environment is read-only. + * <li>EINVAL - an invalid parameter was specified. + * <li>EIO - an error occurred during synchronization. + * </ul> + */ +int mdb_env_sync(MDB_env *env, int force); + + /** @brief Close the environment and release the memory map. + * + * Only a single thread may call this function. All transactions, databases, + * and cursors must already be closed before calling this function. Attempts to + * use any such handles after calling this function will cause a SIGSEGV. + * The environment handle will be freed and must not be used again after this call. + * @param[in] env An environment handle returned by #mdb_env_create() + */ +void mdb_env_close(MDB_env *env); + + /** @brief Set environment flags. + * + * This may be used to set some flags in addition to those from + * #mdb_env_open(), or to unset these flags. If several threads + * change the flags at the same time, the result is undefined. + * @param[in] env An environment handle returned by #mdb_env_create() + * @param[in] flags The flags to change, bitwise OR'ed together + * @param[in] onoff A non-zero value sets the flags, zero clears them. + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_env_set_flags(MDB_env *env, unsigned int flags, int onoff); + + /** @brief Get environment flags. + * + * @param[in] env An environment handle returned by #mdb_env_create() + * @param[out] flags The address of an integer to store the flags + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_env_get_flags(MDB_env *env, unsigned int *flags); + + /** @brief Return the path that was used in #mdb_env_open(). + * + * @param[in] env An environment handle returned by #mdb_env_create() + * @param[out] path Address of a string pointer to contain the path. This + * is the actual string in the environment, not a copy. It should not be + * altered in any way. + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_env_get_path(MDB_env *env, const char **path); + + /** @brief Return the filedescriptor for the given environment. + * + * This function may be called after fork(), so the descriptor can be + * closed before exec*(). Other LMDB file descriptors have FD_CLOEXEC. + * (Until LMDB 0.9.18, only the lockfile had that.) + * + * @param[in] env An environment handle returned by #mdb_env_create() + * @param[out] fd Address of a mdb_filehandle_t to contain the descriptor. + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_env_get_fd(MDB_env *env, mdb_filehandle_t *fd); + + /** @brief Set the size of the memory map to use for this environment. + * + * The size should be a multiple of the OS page size. The default is + * 10485760 bytes. The size of the memory map is also the maximum size + * of the database. The value should be chosen as large as possible, + * to accommodate future growth of the database. + * This function should be called after #mdb_env_create() and before #mdb_env_open(). + * It may be called at later times if no transactions are active in + * this process. Note that the library does not check for this condition, + * the caller must ensure it explicitly. + * + * The new size takes effect immediately for the current process but + * will not be persisted to any others until a write transaction has been + * committed by the current process. Also, only mapsize increases are + * persisted into the environment. + * + * If the mapsize is increased by another process, and data has grown + * beyond the range of the current mapsize, #mdb_txn_begin() will + * return #MDB_MAP_RESIZED. This function may be called with a size + * of zero to adopt the new size. + * + * Any attempt to set a size smaller than the space already consumed + * by the environment will be silently changed to the current size of the used space. + * @param[in] env An environment handle returned by #mdb_env_create() + * @param[in] size The size in bytes + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EINVAL - an invalid parameter was specified, or the environment has + * an active write transaction. + * </ul> + */ +int mdb_env_set_mapsize(MDB_env *env, size_t size); + + /** @brief Set the maximum number of threads/reader slots for the environment. + * + * This defines the number of slots in the lock table that is used to track readers in the + * the environment. The default is 126. + * Starting a read-only transaction normally ties a lock table slot to the + * current thread until the environment closes or the thread exits. If + * MDB_NOTLS is in use, #mdb_txn_begin() instead ties the slot to the + * MDB_txn object until it or the #MDB_env object is destroyed. + * This function may only be called after #mdb_env_create() and before #mdb_env_open(). + * @param[in] env An environment handle returned by #mdb_env_create() + * @param[in] readers The maximum number of reader lock table slots + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EINVAL - an invalid parameter was specified, or the environment is already open. + * </ul> + */ +int mdb_env_set_maxreaders(MDB_env *env, unsigned int readers); + + /** @brief Get the maximum number of threads/reader slots for the environment. + * + * @param[in] env An environment handle returned by #mdb_env_create() + * @param[out] readers Address of an integer to store the number of readers + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_env_get_maxreaders(MDB_env *env, unsigned int *readers); + + /** @brief Set the maximum number of named databases for the environment. + * + * This function is only needed if multiple databases will be used in the + * environment. Simpler applications that use the environment as a single + * unnamed database can ignore this option. + * This function may only be called after #mdb_env_create() and before #mdb_env_open(). + * + * Currently a moderate number of slots are cheap but a huge number gets + * expensive: 7-120 words per transaction, and every #mdb_dbi_open() + * does a linear search of the opened slots. + * @param[in] env An environment handle returned by #mdb_env_create() + * @param[in] dbs The maximum number of databases + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EINVAL - an invalid parameter was specified, or the environment is already open. + * </ul> + */ +int mdb_env_set_maxdbs(MDB_env *env, MDB_dbi dbs); + + /** @brief Get the maximum size of keys and #MDB_DUPSORT data we can write. + * + * Depends on the compile-time constant #MDB_MAXKEYSIZE. Default 511. + * See @ref MDB_val. + * @param[in] env An environment handle returned by #mdb_env_create() + * @return The maximum size of a key we can write + */ +int mdb_env_get_maxkeysize(MDB_env *env); + + /** @brief Set application information associated with the #MDB_env. + * + * @param[in] env An environment handle returned by #mdb_env_create() + * @param[in] ctx An arbitrary pointer for whatever the application needs. + * @return A non-zero error value on failure and 0 on success. + */ +int mdb_env_set_userctx(MDB_env *env, void *ctx); + + /** @brief Get the application information associated with the #MDB_env. + * + * @param[in] env An environment handle returned by #mdb_env_create() + * @return The pointer set by #mdb_env_set_userctx(). + */ +void *mdb_env_get_userctx(MDB_env *env); + + /** @brief A callback function for most LMDB assert() failures, + * called before printing the message and aborting. + * + * @param[in] env An environment handle returned by #mdb_env_create(). + * @param[in] msg The assertion message, not including newline. + */ +typedef void MDB_assert_func(MDB_env *env, const char *msg); + + /** Set or reset the assert() callback of the environment. + * Disabled if liblmdb is buillt with NDEBUG. + * @note This hack should become obsolete as lmdb's error handling matures. + * @param[in] env An environment handle returned by #mdb_env_create(). + * @param[in] func An #MDB_assert_func function, or 0. + * @return A non-zero error value on failure and 0 on success. + */ +int mdb_env_set_assert(MDB_env *env, MDB_assert_func *func); + + /** @brief Create a transaction for use with the environment. + * + * The transaction handle may be discarded using #mdb_txn_abort() or #mdb_txn_commit(). + * @note A transaction and its cursors must only be used by a single + * thread, and a thread may only have a single transaction at a time. + * If #MDB_NOTLS is in use, this does not apply to read-only transactions. + * @note Cursors may not span transactions. + * @param[in] env An environment handle returned by #mdb_env_create() + * @param[in] parent If this parameter is non-NULL, the new transaction + * will be a nested transaction, with the transaction indicated by \b parent + * as its parent. Transactions may be nested to any level. A parent + * transaction and its cursors may not issue any other operations than + * mdb_txn_commit and mdb_txn_abort while it has active child transactions. + * @param[in] flags Special options for this transaction. This parameter + * must be set to 0 or by bitwise OR'ing together one or more of the + * values described here. + * <ul> + * <li>#MDB_RDONLY + * This transaction will not perform any write operations. + * </ul> + * @param[out] txn Address where the new #MDB_txn handle will be stored + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>#MDB_PANIC - a fatal error occurred earlier and the environment + * must be shut down. + * <li>#MDB_MAP_RESIZED - another process wrote data beyond this MDB_env's + * mapsize and this environment's map must be resized as well. + * See #mdb_env_set_mapsize(). + * <li>#MDB_READERS_FULL - a read-only transaction was requested and + * the reader lock table is full. See #mdb_env_set_maxreaders(). + * <li>ENOMEM - out of memory. + * </ul> + */ +int mdb_txn_begin(MDB_env *env, MDB_txn *parent, unsigned int flags, MDB_txn **txn); + + /** @brief Returns the transaction's #MDB_env + * + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + */ +MDB_env *mdb_txn_env(MDB_txn *txn); + + /** @brief Return the transaction's ID. + * + * This returns the identifier associated with this transaction. For a + * read-only transaction, this corresponds to the snapshot being read; + * concurrent readers will frequently have the same transaction ID. + * + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @return A transaction ID, valid if input is an active transaction. + */ +size_t mdb_txn_id(MDB_txn *txn); + + /** @brief Commit all the operations of a transaction into the database. + * + * The transaction handle is freed. It and its cursors must not be used + * again after this call, except with #mdb_cursor_renew(). + * @note Earlier documentation incorrectly said all cursors would be freed. + * Only write-transactions free cursors. + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EINVAL - an invalid parameter was specified. + * <li>ENOSPC - no more disk space. + * <li>EIO - a low-level I/O error occurred while writing. + * <li>ENOMEM - out of memory. + * </ul> + */ +int mdb_txn_commit(MDB_txn *txn); + + /** @brief Abandon all the operations of the transaction instead of saving them. + * + * The transaction handle is freed. It and its cursors must not be used + * again after this call, except with #mdb_cursor_renew(). + * @note Earlier documentation incorrectly said all cursors would be freed. + * Only write-transactions free cursors. + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + */ +void mdb_txn_abort(MDB_txn *txn); + + /** @brief Reset a read-only transaction. + * + * Abort the transaction like #mdb_txn_abort(), but keep the transaction + * handle. #mdb_txn_renew() may reuse the handle. This saves allocation + * overhead if the process will start a new read-only transaction soon, + * and also locking overhead if #MDB_NOTLS is in use. The reader table + * lock is released, but the table slot stays tied to its thread or + * #MDB_txn. Use mdb_txn_abort() to discard a reset handle, and to free + * its lock table slot if MDB_NOTLS is in use. + * Cursors opened within the transaction must not be used + * again after this call, except with #mdb_cursor_renew(). + * Reader locks generally don't interfere with writers, but they keep old + * versions of database pages allocated. Thus they prevent the old pages + * from being reused when writers commit new data, and so under heavy load + * the database size may grow much more rapidly than otherwise. + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + */ +void mdb_txn_reset(MDB_txn *txn); + + /** @brief Renew a read-only transaction. + * + * This acquires a new reader lock for a transaction handle that had been + * released by #mdb_txn_reset(). It must be called before a reset transaction + * may be used again. + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>#MDB_PANIC - a fatal error occurred earlier and the environment + * must be shut down. + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_txn_renew(MDB_txn *txn); + +/** Compat with version <= 0.9.4, avoid clash with libmdb from MDB Tools project */ +#define mdb_open(txn,name,flags,dbi) mdb_dbi_open(txn,name,flags,dbi) +/** Compat with version <= 0.9.4, avoid clash with libmdb from MDB Tools project */ +#define mdb_close(env,dbi) mdb_dbi_close(env,dbi) + + /** @brief Open a database in the environment. + * + * A database handle denotes the name and parameters of a database, + * independently of whether such a database exists. + * The database handle may be discarded by calling #mdb_dbi_close(). + * The old database handle is returned if the database was already open. + * The handle may only be closed once. + * + * The database handle will be private to the current transaction until + * the transaction is successfully committed. If the transaction is + * aborted the handle will be closed automatically. + * After a successful commit the handle will reside in the shared + * environment, and may be used by other transactions. + * + * This function must not be called from multiple concurrent + * transactions in the same process. A transaction that uses + * this function must finish (either commit or abort) before + * any other transaction in the process may use this function. + * + * To use named databases (with name != NULL), #mdb_env_set_maxdbs() + * must be called before opening the environment. Database names are + * keys in the unnamed database, and may be read but not written. + * + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] name The name of the database to open. If only a single + * database is needed in the environment, this value may be NULL. + * @param[in] flags Special options for this database. This parameter + * must be set to 0 or by bitwise OR'ing together one or more of the + * values described here. + * <ul> + * <li>#MDB_REVERSEKEY + * Keys are strings to be compared in reverse order, from the end + * of the strings to the beginning. By default, Keys are treated as strings and + * compared from beginning to end. + * <li>#MDB_DUPSORT + * Duplicate keys may be used in the database. (Or, from another perspective, + * keys may have multiple data items, stored in sorted order.) By default + * keys must be unique and may have only a single data item. + * <li>#MDB_INTEGERKEY + * Keys are binary integers in native byte order, either unsigned int + * or size_t, and will be sorted as such. + * The keys must all be of the same size. + * <li>#MDB_DUPFIXED + * This flag may only be used in combination with #MDB_DUPSORT. This option + * tells the library that the data items for this database are all the same + * size, which allows further optimizations in storage and retrieval. When + * all data items are the same size, the #MDB_GET_MULTIPLE, #MDB_NEXT_MULTIPLE + * and #MDB_PREV_MULTIPLE cursor operations may be used to retrieve multiple + * items at once. + * <li>#MDB_INTEGERDUP + * This option specifies that duplicate data items are binary integers, + * similar to #MDB_INTEGERKEY keys. + * <li>#MDB_REVERSEDUP + * This option specifies that duplicate data items should be compared as + * strings in reverse order. + * <li>#MDB_CREATE + * Create the named database if it doesn't exist. This option is not + * allowed in a read-only transaction or a read-only environment. + * </ul> + * @param[out] dbi Address where the new #MDB_dbi handle will be stored + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>#MDB_NOTFOUND - the specified database doesn't exist in the environment + * and #MDB_CREATE was not specified. + * <li>#MDB_DBS_FULL - too many databases have been opened. See #mdb_env_set_maxdbs(). + * </ul> + */ +int mdb_dbi_open(MDB_txn *txn, const char *name, unsigned int flags, MDB_dbi *dbi); + + /** @brief Retrieve statistics for a database. + * + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] dbi A database handle returned by #mdb_dbi_open() + * @param[out] stat The address of an #MDB_stat structure + * where the statistics will be copied + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_stat(MDB_txn *txn, MDB_dbi dbi, MDB_stat *stat); + + /** @brief Retrieve the DB flags for a database handle. + * + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] dbi A database handle returned by #mdb_dbi_open() + * @param[out] flags Address where the flags will be returned. + * @return A non-zero error value on failure and 0 on success. + */ +int mdb_dbi_flags(MDB_txn *txn, MDB_dbi dbi, unsigned int *flags); + + /** @brief Close a database handle. Normally unnecessary. Use with care: + * + * This call is not mutex protected. Handles should only be closed by + * a single thread, and only if no other threads are going to reference + * the database handle or one of its cursors any further. Do not close + * a handle if an existing transaction has modified its database. + * Doing so can cause misbehavior from database corruption to errors + * like MDB_BAD_VALSIZE (since the DB name is gone). + * + * Closing a database handle is not necessary, but lets #mdb_dbi_open() + * reuse the handle value. Usually it's better to set a bigger + * #mdb_env_set_maxdbs(), unless that value would be large. + * + * @param[in] env An environment handle returned by #mdb_env_create() + * @param[in] dbi A database handle returned by #mdb_dbi_open() + */ +void mdb_dbi_close(MDB_env *env, MDB_dbi dbi); + + /** @brief Empty or delete+close a database. + * + * See #mdb_dbi_close() for restrictions about closing the DB handle. + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] dbi A database handle returned by #mdb_dbi_open() + * @param[in] del 0 to empty the DB, 1 to delete it from the + * environment and close the DB handle. + * @return A non-zero error value on failure and 0 on success. + */ +int mdb_drop(MDB_txn *txn, MDB_dbi dbi, int del); + + /** @brief Set a custom key comparison function for a database. + * + * The comparison function is called whenever it is necessary to compare a + * key specified by the application with a key currently stored in the database. + * If no comparison function is specified, and no special key flags were specified + * with #mdb_dbi_open(), the keys are compared lexically, with shorter keys collating + * before longer keys. + * @warning This function must be called before any data access functions are used, + * otherwise data corruption may occur. The same comparison function must be used by every + * program accessing the database, every time the database is used. + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] dbi A database handle returned by #mdb_dbi_open() + * @param[in] cmp A #MDB_cmp_func function + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_set_compare(MDB_txn *txn, MDB_dbi dbi, MDB_cmp_func *cmp); + + /** @brief Set a custom data comparison function for a #MDB_DUPSORT database. + * + * This comparison function is called whenever it is necessary to compare a data + * item specified by the application with a data item currently stored in the database. + * This function only takes effect if the database was opened with the #MDB_DUPSORT + * flag. + * If no comparison function is specified, and no special key flags were specified + * with #mdb_dbi_open(), the data items are compared lexically, with shorter items collating + * before longer items. + * @warning This function must be called before any data access functions are used, + * otherwise data corruption may occur. The same comparison function must be used by every + * program accessing the database, every time the database is used. + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] dbi A database handle returned by #mdb_dbi_open() + * @param[in] cmp A #MDB_cmp_func function + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_set_dupsort(MDB_txn *txn, MDB_dbi dbi, MDB_cmp_func *cmp); + + /** @brief Set a relocation function for a #MDB_FIXEDMAP database. + * + * @todo The relocation function is called whenever it is necessary to move the data + * of an item to a different position in the database (e.g. through tree + * balancing operations, shifts as a result of adds or deletes, etc.). It is + * intended to allow address/position-dependent data items to be stored in + * a database in an environment opened with the #MDB_FIXEDMAP option. + * Currently the relocation feature is unimplemented and setting + * this function has no effect. + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] dbi A database handle returned by #mdb_dbi_open() + * @param[in] rel A #MDB_rel_func function + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_set_relfunc(MDB_txn *txn, MDB_dbi dbi, MDB_rel_func *rel); + + /** @brief Set a context pointer for a #MDB_FIXEDMAP database's relocation function. + * + * See #mdb_set_relfunc and #MDB_rel_func for more details. + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] dbi A database handle returned by #mdb_dbi_open() + * @param[in] ctx An arbitrary pointer for whatever the application needs. + * It will be passed to the callback function set by #mdb_set_relfunc + * as its \b relctx parameter whenever the callback is invoked. + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_set_relctx(MDB_txn *txn, MDB_dbi dbi, void *ctx); + + /** @brief Get items from a database. + * + * This function retrieves key/data pairs from the database. The address + * and length of the data associated with the specified \b key are returned + * in the structure to which \b data refers. + * If the database supports duplicate keys (#MDB_DUPSORT) then the + * first data item for the key will be returned. Retrieval of other + * items requires the use of #mdb_cursor_get(). + * + * @note The memory pointed to by the returned values is owned by the + * database. The caller need not dispose of the memory, and may not + * modify it in any way. For values returned in a read-only transaction + * any modification attempts will cause a SIGSEGV. + * @note Values returned from the database are valid only until a + * subsequent update operation, or the end of the transaction. + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] dbi A database handle returned by #mdb_dbi_open() + * @param[in] key The key to search for in the database + * @param[out] data The data corresponding to the key + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>#MDB_NOTFOUND - the key was not in the database. + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_get(MDB_txn *txn, MDB_dbi dbi, MDB_val *key, MDB_val *data); + + /** @brief Store items into a database. + * + * This function stores key/data pairs in the database. The default behavior + * is to enter the new key/data pair, replacing any previously existing key + * if duplicates are disallowed, or adding a duplicate data item if + * duplicates are allowed (#MDB_DUPSORT). + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] dbi A database handle returned by #mdb_dbi_open() + * @param[in] key The key to store in the database + * @param[in,out] data The data to store + * @param[in] flags Special options for this operation. This parameter + * must be set to 0 or by bitwise OR'ing together one or more of the + * values described here. + * <ul> + * <li>#MDB_NODUPDATA - enter the new key/data pair only if it does not + * already appear in the database. This flag may only be specified + * if the database was opened with #MDB_DUPSORT. The function will + * return #MDB_KEYEXIST if the key/data pair already appears in the + * database. + * <li>#MDB_NOOVERWRITE - enter the new key/data pair only if the key + * does not already appear in the database. The function will return + * #MDB_KEYEXIST if the key already appears in the database, even if + * the database supports duplicates (#MDB_DUPSORT). The \b data + * parameter will be set to point to the existing item. + * <li>#MDB_RESERVE - reserve space for data of the given size, but + * don't copy the given data. Instead, return a pointer to the + * reserved space, which the caller can fill in later - before + * the next update operation or the transaction ends. This saves + * an extra memcpy if the data is being generated later. + * LMDB does nothing else with this memory, the caller is expected + * to modify all of the space requested. This flag must not be + * specified if the database was opened with #MDB_DUPSORT. + * <li>#MDB_APPEND - append the given key/data pair to the end of the + * database. This option allows fast bulk loading when keys are + * already known to be in the correct order. Loading unsorted keys + * with this flag will cause a #MDB_KEYEXIST error. + * <li>#MDB_APPENDDUP - as above, but for sorted dup data. + * </ul> + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>#MDB_MAP_FULL - the database is full, see #mdb_env_set_mapsize(). + * <li>#MDB_TXN_FULL - the transaction has too many dirty pages. + * <li>EACCES - an attempt was made to write in a read-only transaction. + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_put(MDB_txn *txn, MDB_dbi dbi, MDB_val *key, MDB_val *data, + unsigned int flags); + + /** @brief Delete items from a database. + * + * This function removes key/data pairs from the database. + * If the database does not support sorted duplicate data items + * (#MDB_DUPSORT) the data parameter is ignored. + * If the database supports sorted duplicates and the data parameter + * is NULL, all of the duplicate data items for the key will be + * deleted. Otherwise, if the data parameter is non-NULL + * only the matching data item will be deleted. + * This function will return #MDB_NOTFOUND if the specified key/data + * pair is not in the database. + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] dbi A database handle returned by #mdb_dbi_open() + * @param[in] key The key to delete from the database + * @param[in] data The data to delete + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EACCES - an attempt was made to write in a read-only transaction. + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_del(MDB_txn *txn, MDB_dbi dbi, MDB_val *key, MDB_val *data); + + /** @brief Create a cursor handle. + * + * A cursor is associated with a specific transaction and database. + * A cursor cannot be used when its database handle is closed. Nor + * when its transaction has ended, except with #mdb_cursor_renew(). + * It can be discarded with #mdb_cursor_close(). + * A cursor in a write-transaction can be closed before its transaction + * ends, and will otherwise be closed when its transaction ends. + * A cursor in a read-only transaction must be closed explicitly, before + * or after its transaction ends. It can be reused with + * #mdb_cursor_renew() before finally closing it. + * @note Earlier documentation said that cursors in every transaction + * were closed when the transaction committed or aborted. + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] dbi A database handle returned by #mdb_dbi_open() + * @param[out] cursor Address where the new #MDB_cursor handle will be stored + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_cursor_open(MDB_txn *txn, MDB_dbi dbi, MDB_cursor **cursor); + + /** @brief Close a cursor handle. + * + * The cursor handle will be freed and must not be used again after this call. + * Its transaction must still be live if it is a write-transaction. + * @param[in] cursor A cursor handle returned by #mdb_cursor_open() + */ +void mdb_cursor_close(MDB_cursor *cursor); + + /** @brief Renew a cursor handle. + * + * A cursor is associated with a specific transaction and database. + * Cursors that are only used in read-only + * transactions may be re-used, to avoid unnecessary malloc/free overhead. + * The cursor may be associated with a new read-only transaction, and + * referencing the same database handle as it was created with. + * This may be done whether the previous transaction is live or dead. + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] cursor A cursor handle returned by #mdb_cursor_open() + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_cursor_renew(MDB_txn *txn, MDB_cursor *cursor); + + /** @brief Return the cursor's transaction handle. + * + * @param[in] cursor A cursor handle returned by #mdb_cursor_open() + */ +MDB_txn *mdb_cursor_txn(MDB_cursor *cursor); + + /** @brief Return the cursor's database handle. + * + * @param[in] cursor A cursor handle returned by #mdb_cursor_open() + */ +MDB_dbi mdb_cursor_dbi(MDB_cursor *cursor); + + /** @brief Retrieve by cursor. + * + * This function retrieves key/data pairs from the database. The address and length + * of the key are returned in the object to which \b key refers (except for the + * case of the #MDB_SET option, in which the \b key object is unchanged), and + * the address and length of the data are returned in the object to which \b data + * refers. + * See #mdb_get() for restrictions on using the output values. + * @param[in] cursor A cursor handle returned by #mdb_cursor_open() + * @param[in,out] key The key for a retrieved item + * @param[in,out] data The data of a retrieved item + * @param[in] op A cursor operation #MDB_cursor_op + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>#MDB_NOTFOUND - no matching key found. + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_cursor_get(MDB_cursor *cursor, MDB_val *key, MDB_val *data, + MDB_cursor_op op); + + /** @brief Store by cursor. + * + * This function stores key/data pairs into the database. + * The cursor is positioned at the new item, or on failure usually near it. + * @note Earlier documentation incorrectly said errors would leave the + * state of the cursor unchanged. + * @param[in] cursor A cursor handle returned by #mdb_cursor_open() + * @param[in] key The key operated on. + * @param[in] data The data operated on. + * @param[in] flags Options for this operation. This parameter + * must be set to 0 or one of the values described here. + * <ul> + * <li>#MDB_CURRENT - replace the item at the current cursor position. + * The \b key parameter must still be provided, and must match it. + * If using sorted duplicates (#MDB_DUPSORT) the data item must still + * sort into the same place. This is intended to be used when the + * new data is the same size as the old. Otherwise it will simply + * perform a delete of the old record followed by an insert. + * <li>#MDB_NODUPDATA - enter the new key/data pair only if it does not + * already appear in the database. This flag may only be specified + * if the database was opened with #MDB_DUPSORT. The function will + * return #MDB_KEYEXIST if the key/data pair already appears in the + * database. + * <li>#MDB_NOOVERWRITE - enter the new key/data pair only if the key + * does not already appear in the database. The function will return + * #MDB_KEYEXIST if the key already appears in the database, even if + * the database supports duplicates (#MDB_DUPSORT). + * <li>#MDB_RESERVE - reserve space for data of the given size, but + * don't copy the given data. Instead, return a pointer to the + * reserved space, which the caller can fill in later - before + * the next update operation or the transaction ends. This saves + * an extra memcpy if the data is being generated later. This flag + * must not be specified if the database was opened with #MDB_DUPSORT. + * <li>#MDB_APPEND - append the given key/data pair to the end of the + * database. No key comparisons are performed. This option allows + * fast bulk loading when keys are already known to be in the + * correct order. Loading unsorted keys with this flag will cause + * a #MDB_KEYEXIST error. + * <li>#MDB_APPENDDUP - as above, but for sorted dup data. + * <li>#MDB_MULTIPLE - store multiple contiguous data elements in a + * single request. This flag may only be specified if the database + * was opened with #MDB_DUPFIXED. The \b data argument must be an + * array of two MDB_vals. The mv_size of the first MDB_val must be + * the size of a single data element. The mv_data of the first MDB_val + * must point to the beginning of the array of contiguous data elements. + * The mv_size of the second MDB_val must be the count of the number + * of data elements to store. On return this field will be set to + * the count of the number of elements actually written. The mv_data + * of the second MDB_val is unused. + * </ul> + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>#MDB_MAP_FULL - the database is full, see #mdb_env_set_mapsize(). + * <li>#MDB_TXN_FULL - the transaction has too many dirty pages. + * <li>EACCES - an attempt was made to write in a read-only transaction. + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_cursor_put(MDB_cursor *cursor, MDB_val *key, MDB_val *data, + unsigned int flags); + + /** @brief Delete current key/data pair + * + * This function deletes the key/data pair to which the cursor refers. + * This does not invalidate the cursor, so operations such as MDB_NEXT + * can still be used on it. + * Both MDB_NEXT and MDB_GET_CURRENT will return the same record after + * this operation. + * @param[in] cursor A cursor handle returned by #mdb_cursor_open() + * @param[in] flags Options for this operation. This parameter + * must be set to 0 or one of the values described here. + * <ul> + * <li>#MDB_NODUPDATA - delete all of the data items for the current key. + * This flag may only be specified if the database was opened with #MDB_DUPSORT. + * </ul> + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EACCES - an attempt was made to write in a read-only transaction. + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_cursor_del(MDB_cursor *cursor, unsigned int flags); + + /** @brief Return count of duplicates for current key. + * + * This call is only valid on databases that support sorted duplicate + * data items #MDB_DUPSORT. + * @param[in] cursor A cursor handle returned by #mdb_cursor_open() + * @param[out] countp Address where the count will be stored + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EINVAL - cursor is not initialized, or an invalid parameter was specified. + * </ul> + */ +int mdb_cursor_count(MDB_cursor *cursor, size_t *countp); + + /** @brief Compare two data items according to a particular database. + * + * This returns a comparison as if the two data items were keys in the + * specified database. + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] dbi A database handle returned by #mdb_dbi_open() + * @param[in] a The first item to compare + * @param[in] b The second item to compare + * @return < 0 if a < b, 0 if a == b, > 0 if a > b + */ +int mdb_cmp(MDB_txn *txn, MDB_dbi dbi, const MDB_val *a, const MDB_val *b); + + /** @brief Compare two data items according to a particular database. + * + * This returns a comparison as if the two items were data items of + * the specified database. The database must have the #MDB_DUPSORT flag. + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] dbi A database handle returned by #mdb_dbi_open() + * @param[in] a The first item to compare + * @param[in] b The second item to compare + * @return < 0 if a < b, 0 if a == b, > 0 if a > b + */ +int mdb_dcmp(MDB_txn *txn, MDB_dbi dbi, const MDB_val *a, const MDB_val *b); + + /** @brief A callback function used to print a message from the library. + * + * @param[in] msg The string to be printed. + * @param[in] ctx An arbitrary context pointer for the callback. + * @return < 0 on failure, >= 0 on success. + */ +typedef int (MDB_msg_func)(const char *msg, void *ctx); + + /** @brief Dump the entries in the reader lock table. + * + * @param[in] env An environment handle returned by #mdb_env_create() + * @param[in] func A #MDB_msg_func function + * @param[in] ctx Anything the message function needs + * @return < 0 on failure, >= 0 on success. + */ +int mdb_reader_list(MDB_env *env, MDB_msg_func *func, void *ctx); + + /** @brief Check for stale entries in the reader lock table. + * + * @param[in] env An environment handle returned by #mdb_env_create() + * @param[out] dead Number of stale slots that were cleared + * @return 0 on success, non-zero on failure. + */ +int mdb_reader_check(MDB_env *env, int *dead); +/** @} */ + +#ifdef __cplusplus +} +#endif +/** @page tools LMDB Command Line Tools + The following describes the command line tools that are available for LMDB. + \li \ref mdb_copy_1 + \li \ref mdb_dump_1 + \li \ref mdb_load_1 + \li \ref mdb_stat_1 +*/ + +#endif /* _LMDB_H_ */ diff --git a/libraries/liblmdb/mdb.c b/libraries/liblmdb/mdb.c new file mode 100644 index 0000000..b70a6a5 --- /dev/null +++ b/libraries/liblmdb/mdb.c @@ -0,0 +1,10322 @@ +/** @file mdb.c + * @brief Lightning memory-mapped database library + * + * A Btree-based database management library modeled loosely on the + * BerkeleyDB API, but much simplified. + */ +/* + * Copyright 2011-2020 Howard Chu, Symas Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + * + * This code is derived from btree.c written by Martin Hedenfalk. + * + * Copyright (c) 2009, 2010 Martin Hedenfalk <martin@bzero.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE 1 +#endif +#if defined(__WIN64__) +#define _FILE_OFFSET_BITS 64 +#endif +#ifdef _WIN32 +#include <malloc.h> +#include <windows.h> +#include <wchar.h> /* get wcscpy() */ + +/** getpid() returns int; MinGW defines pid_t but MinGW64 typedefs it + * as int64 which is wrong. MSVC doesn't define it at all, so just + * don't use it. + */ +#define MDB_PID_T int +#define MDB_THR_T DWORD +#include <sys/types.h> +#include <sys/stat.h> +#ifdef __GNUC__ +# include <sys/param.h> +#else +# define LITTLE_ENDIAN 1234 +# define BIG_ENDIAN 4321 +# define BYTE_ORDER LITTLE_ENDIAN +# ifndef SSIZE_MAX +# define SSIZE_MAX INT_MAX +# endif +#endif +#else +#include <sys/types.h> +#include <sys/stat.h> +#define MDB_PID_T pid_t +#define MDB_THR_T pthread_t +#include <sys/param.h> +#include <sys/uio.h> +#include <sys/mman.h> +#ifdef HAVE_SYS_FILE_H +#include <sys/file.h> +#endif +#include <fcntl.h> +#endif + +#if defined(__mips) && defined(__linux) +/* MIPS has cache coherency issues, requires explicit cache control */ +#include <asm/cachectl.h> +extern int cacheflush(char *addr, int nbytes, int cache); +#define CACHEFLUSH(addr, bytes, cache) cacheflush(addr, bytes, cache) +#else +#define CACHEFLUSH(addr, bytes, cache) +#endif + +#if defined(__linux) && !defined(MDB_FDATASYNC_WORKS) +/** fdatasync is broken on ext3/ext4fs on older kernels, see + * description in #mdb_env_open2 comments. You can safely + * define MDB_FDATASYNC_WORKS if this code will only be run + * on kernels 3.6 and newer. + */ +#define BROKEN_FDATASYNC +#endif + +#include <errno.h> +#include <limits.h> +#include <stddef.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#ifdef _MSC_VER +#include <io.h> +typedef SSIZE_T ssize_t; +#else +#include <unistd.h> +#endif + +#if defined(__sun) || defined(ANDROID) +/* Most platforms have posix_memalign, older may only have memalign */ +#define HAVE_MEMALIGN 1 +#include <malloc.h> +/* On Solaris, we need the POSIX sigwait function */ +#if defined (__sun) +# define _POSIX_PTHREAD_SEMANTICS 1 +#endif +#endif + +#if !(defined(BYTE_ORDER) || defined(__BYTE_ORDER)) +#include <netinet/in.h> +#include <resolv.h> /* defines BYTE_ORDER on HPUX and Solaris */ +#endif + +#if defined(__FreeBSD__) && defined(__FreeBSD_version) && __FreeBSD_version >= 1100110 +# define MDB_USE_POSIX_MUTEX 1 +# define MDB_USE_ROBUST 1 +#elif defined(__APPLE__) || defined (BSD) || defined(__FreeBSD_kernel__) +# define MDB_USE_POSIX_SEM 1 +# define MDB_FDATASYNC fsync +#elif defined(ANDROID) +# define MDB_FDATASYNC fsync +#endif + +#ifndef _WIN32 +#include <pthread.h> +#include <signal.h> +#ifdef MDB_USE_POSIX_SEM +# define MDB_USE_HASH 1 +#include <semaphore.h> +#else +#define MDB_USE_POSIX_MUTEX 1 +#endif +#endif + +#if defined(_WIN32) + defined(MDB_USE_POSIX_SEM) \ + + defined(MDB_USE_POSIX_MUTEX) != 1 +# error "Ambiguous shared-lock implementation" +#endif + +#ifdef USE_VALGRIND +#include <valgrind/memcheck.h> +#define VGMEMP_CREATE(h,r,z) VALGRIND_CREATE_MEMPOOL(h,r,z) +#define VGMEMP_ALLOC(h,a,s) VALGRIND_MEMPOOL_ALLOC(h,a,s) +#define VGMEMP_FREE(h,a) VALGRIND_MEMPOOL_FREE(h,a) +#define VGMEMP_DESTROY(h) VALGRIND_DESTROY_MEMPOOL(h) +#define VGMEMP_DEFINED(a,s) VALGRIND_MAKE_MEM_DEFINED(a,s) +#else +#define VGMEMP_CREATE(h,r,z) +#define VGMEMP_ALLOC(h,a,s) +#define VGMEMP_FREE(h,a) +#define VGMEMP_DESTROY(h) +#define VGMEMP_DEFINED(a,s) +#endif + +#ifndef BYTE_ORDER +# if (defined(_LITTLE_ENDIAN) || defined(_BIG_ENDIAN)) && !(defined(_LITTLE_ENDIAN) && defined(_BIG_ENDIAN)) +/* Solaris just defines one or the other */ +# define LITTLE_ENDIAN 1234 +# define BIG_ENDIAN 4321 +# ifdef _LITTLE_ENDIAN +# define BYTE_ORDER LITTLE_ENDIAN +# else +# define BYTE_ORDER BIG_ENDIAN +# endif +# else +# define BYTE_ORDER __BYTE_ORDER +# endif +#endif + +#ifndef LITTLE_ENDIAN +#define LITTLE_ENDIAN __LITTLE_ENDIAN +#endif +#ifndef BIG_ENDIAN +#define BIG_ENDIAN __BIG_ENDIAN +#endif + +#if defined(__i386) || defined(__x86_64) || defined(_M_IX86) +#define MISALIGNED_OK 1 +#endif + +#include "lmdb.h" +#include "midl.h" + +#if (BYTE_ORDER == LITTLE_ENDIAN) == (BYTE_ORDER == BIG_ENDIAN) +# error "Unknown or unsupported endianness (BYTE_ORDER)" +#elif (-6 & 5) || CHAR_BIT != 8 || UINT_MAX < 0xffffffff || ULONG_MAX % 0xFFFF +# error "Two's complement, reasonably sized integer types, please" +#endif + +#ifdef __GNUC__ +/** Put infrequently used env functions in separate section */ +# ifdef __APPLE__ +# define ESECT __attribute__ ((section("__TEXT,text_env"))) +# else +# define ESECT __attribute__ ((section("text_env"))) +# endif +#else +#define ESECT +#endif + +#ifdef _WIN32 +#define CALL_CONV WINAPI +#else +#define CALL_CONV +#endif + +/** @defgroup internal LMDB Internals + * @{ + */ +/** @defgroup compat Compatibility Macros + * A bunch of macros to minimize the amount of platform-specific ifdefs + * needed throughout the rest of the code. When the features this library + * needs are similar enough to POSIX to be hidden in a one-or-two line + * replacement, this macro approach is used. + * @{ + */ + + /** Features under development */ +#ifndef MDB_DEVEL +#define MDB_DEVEL 0 +#endif + + /** Wrapper around __func__, which is a C99 feature */ +#if __STDC_VERSION__ >= 199901L +# define mdb_func_ __func__ +#elif __GNUC__ >= 2 || _MSC_VER >= 1300 +# define mdb_func_ __FUNCTION__ +#else +/* If a debug message says <mdb_unknown>(), update the #if statements above */ +# define mdb_func_ "<mdb_unknown>" +#endif + +/* Internal error codes, not exposed outside liblmdb */ +#define MDB_NO_ROOT (MDB_LAST_ERRCODE + 10) +#ifdef _WIN32 +#define MDB_OWNERDEAD ((int) WAIT_ABANDONED) +#elif defined(MDB_USE_POSIX_MUTEX) && defined(EOWNERDEAD) +#define MDB_OWNERDEAD EOWNERDEAD /**< #LOCK_MUTEX0() result if dead owner */ +#endif + +#ifdef __GLIBC__ +#define GLIBC_VER ((__GLIBC__ << 16 )| __GLIBC_MINOR__) +#endif +/** Some platforms define the EOWNERDEAD error code + * even though they don't support Robust Mutexes. + * Compile with -DMDB_USE_ROBUST=0, or use some other + * mechanism like -DMDB_USE_POSIX_SEM instead of + * -DMDB_USE_POSIX_MUTEX. + * (Posix semaphores are not robust.) + */ +#ifndef MDB_USE_ROBUST +/* Android currently lacks Robust Mutex support. So does glibc < 2.4. */ +# if defined(MDB_USE_POSIX_MUTEX) && (defined(ANDROID) || \ + (defined(__GLIBC__) && GLIBC_VER < 0x020004)) +# define MDB_USE_ROBUST 0 +# else +# define MDB_USE_ROBUST 1 +# endif +#endif /* !MDB_USE_ROBUST */ + +#if defined(MDB_USE_POSIX_MUTEX) && (MDB_USE_ROBUST) +/* glibc < 2.12 only provided _np API */ +# if (defined(__GLIBC__) && GLIBC_VER < 0x02000c) || \ + (defined(PTHREAD_MUTEX_ROBUST_NP) && !defined(PTHREAD_MUTEX_ROBUST)) +# define PTHREAD_MUTEX_ROBUST PTHREAD_MUTEX_ROBUST_NP +# define pthread_mutexattr_setrobust(attr, flag) pthread_mutexattr_setrobust_np(attr, flag) +# define pthread_mutex_consistent(mutex) pthread_mutex_consistent_np(mutex) +# endif +#endif /* MDB_USE_POSIX_MUTEX && MDB_USE_ROBUST */ + +#if defined(MDB_OWNERDEAD) && (MDB_USE_ROBUST) +#define MDB_ROBUST_SUPPORTED 1 +#endif + +#ifdef _WIN32 +#define MDB_USE_HASH 1 +#define MDB_PIDLOCK 0 +#define THREAD_RET DWORD +#define pthread_t HANDLE +#define pthread_mutex_t HANDLE +#define pthread_cond_t HANDLE +typedef HANDLE mdb_mutex_t, mdb_mutexref_t; +#define pthread_key_t DWORD +#define pthread_self() GetCurrentThreadId() +#define pthread_key_create(x,y) \ + ((*(x) = TlsAlloc()) == TLS_OUT_OF_INDEXES ? ErrCode() : 0) +#define pthread_key_delete(x) TlsFree(x) +#define pthread_getspecific(x) TlsGetValue(x) +#define pthread_setspecific(x,y) (TlsSetValue(x,y) ? 0 : ErrCode()) +#define pthread_mutex_unlock(x) ReleaseMutex(*x) +#define pthread_mutex_lock(x) WaitForSingleObject(*x, INFINITE) +#define pthread_cond_signal(x) SetEvent(*x) +#define pthread_cond_wait(cond,mutex) do{SignalObjectAndWait(*mutex, *cond, INFINITE, FALSE); WaitForSingleObject(*mutex, INFINITE);}while(0) +#define THREAD_CREATE(thr,start,arg) \ + (((thr) = CreateThread(NULL, 0, start, arg, 0, NULL)) ? 0 : ErrCode()) +#define THREAD_FINISH(thr) \ + (WaitForSingleObject(thr, INFINITE) ? ErrCode() : 0) +#define LOCK_MUTEX0(mutex) WaitForSingleObject(mutex, INFINITE) +#define UNLOCK_MUTEX(mutex) ReleaseMutex(mutex) +#define mdb_mutex_consistent(mutex) 0 +#define getpid() GetCurrentProcessId() +#define MDB_FDATASYNC(fd) (!FlushFileBuffers(fd)) +#define MDB_MSYNC(addr,len,flags) (!FlushViewOfFile(addr,len)) +#define ErrCode() GetLastError() +#define GET_PAGESIZE(x) {SYSTEM_INFO si; GetSystemInfo(&si); (x) = si.dwPageSize;} +#define close(fd) (CloseHandle(fd) ? 0 : -1) +#define munmap(ptr,len) UnmapViewOfFile(ptr) +#ifdef PROCESS_QUERY_LIMITED_INFORMATION +#define MDB_PROCESS_QUERY_LIMITED_INFORMATION PROCESS_QUERY_LIMITED_INFORMATION +#else +#define MDB_PROCESS_QUERY_LIMITED_INFORMATION 0x1000 +#endif +#define Z "I" +#else +#define THREAD_RET void * +#define THREAD_CREATE(thr,start,arg) pthread_create(&thr,NULL,start,arg) +#define THREAD_FINISH(thr) pthread_join(thr,NULL) +#define Z "z" /**< printf format modifier for size_t */ + + /** For MDB_LOCK_FORMAT: True if readers take a pid lock in the lockfile */ +#define MDB_PIDLOCK 1 + +#ifdef MDB_USE_POSIX_SEM + +typedef sem_t *mdb_mutex_t, *mdb_mutexref_t; +#define LOCK_MUTEX0(mutex) mdb_sem_wait(mutex) +#define UNLOCK_MUTEX(mutex) sem_post(mutex) + +static int +mdb_sem_wait(sem_t *sem) +{ + int rc; + while ((rc = sem_wait(sem)) && (rc = errno) == EINTR) ; + return rc; +} + +#else /* MDB_USE_POSIX_MUTEX: */ + /** Shared mutex/semaphore as the original is stored. + * + * Not for copies. Instead it can be assigned to an #mdb_mutexref_t. + * When mdb_mutexref_t is a pointer and mdb_mutex_t is not, then it + * is array[size 1] so it can be assigned to the pointer. + */ +typedef pthread_mutex_t mdb_mutex_t[1]; + /** Reference to an #mdb_mutex_t */ +typedef pthread_mutex_t *mdb_mutexref_t; + /** Lock the reader or writer mutex. + * Returns 0 or a code to give #mdb_mutex_failed(), as in #LOCK_MUTEX(). + */ +#define LOCK_MUTEX0(mutex) pthread_mutex_lock(mutex) + /** Unlock the reader or writer mutex. + */ +#define UNLOCK_MUTEX(mutex) pthread_mutex_unlock(mutex) + /** Mark mutex-protected data as repaired, after death of previous owner. + */ +#define mdb_mutex_consistent(mutex) pthread_mutex_consistent(mutex) +#endif /* MDB_USE_POSIX_SEM */ + + /** Get the error code for the last failed system function. + */ +#define ErrCode() errno + + /** An abstraction for a file handle. + * On POSIX systems file handles are small integers. On Windows + * they're opaque pointers. + */ +#define HANDLE int + + /** A value for an invalid file handle. + * Mainly used to initialize file variables and signify that they are + * unused. + */ +#define INVALID_HANDLE_VALUE (-1) + + /** Get the size of a memory page for the system. + * This is the basic size that the platform's memory manager uses, and is + * fundamental to the use of memory-mapped files. + */ +#define GET_PAGESIZE(x) ((x) = sysconf(_SC_PAGE_SIZE)) +#endif + +#if defined(_WIN32) || defined(MDB_USE_POSIX_SEM) +#define MNAME_LEN 32 +#else +#define MNAME_LEN (sizeof(pthread_mutex_t)) +#endif + +/** @} */ + +#ifdef MDB_ROBUST_SUPPORTED + /** Lock mutex, handle any error, set rc = result. + * Return 0 on success, nonzero (not rc) on error. + */ +#define LOCK_MUTEX(rc, env, mutex) \ + (((rc) = LOCK_MUTEX0(mutex)) && \ + ((rc) = mdb_mutex_failed(env, mutex, rc))) +static int mdb_mutex_failed(MDB_env *env, mdb_mutexref_t mutex, int rc); +#else +#define LOCK_MUTEX(rc, env, mutex) ((rc) = LOCK_MUTEX0(mutex)) +#define mdb_mutex_failed(env, mutex, rc) (rc) +#endif + +#ifndef _WIN32 +/** A flag for opening a file and requesting synchronous data writes. + * This is only used when writing a meta page. It's not strictly needed; + * we could just do a normal write and then immediately perform a flush. + * But if this flag is available it saves us an extra system call. + * + * @note If O_DSYNC is undefined but exists in /usr/include, + * preferably set some compiler flag to get the definition. + */ +#ifndef MDB_DSYNC +# ifdef O_DSYNC +# define MDB_DSYNC O_DSYNC +# else +# define MDB_DSYNC O_SYNC +# endif +#endif +#endif + +/** Function for flushing the data of a file. Define this to fsync + * if fdatasync() is not supported. + */ +#ifndef MDB_FDATASYNC +# define MDB_FDATASYNC fdatasync +#endif + +#ifndef MDB_MSYNC +# define MDB_MSYNC(addr,len,flags) msync(addr,len,flags) +#endif + +#ifndef MS_SYNC +#define MS_SYNC 1 +#endif + +#ifndef MS_ASYNC +#define MS_ASYNC 0 +#endif + + /** A page number in the database. + * Note that 64 bit page numbers are overkill, since pages themselves + * already represent 12-13 bits of addressable memory, and the OS will + * always limit applications to a maximum of 63 bits of address space. + * + * @note In the #MDB_node structure, we only store 48 bits of this value, + * which thus limits us to only 60 bits of addressable data. + */ +typedef MDB_ID pgno_t; + + /** A transaction ID. + * See struct MDB_txn.mt_txnid for details. + */ +typedef MDB_ID txnid_t; + +/** @defgroup debug Debug Macros + * @{ + */ +#ifndef MDB_DEBUG + /** Enable debug output. Needs variable argument macros (a C99 feature). + * Set this to 1 for copious tracing. Set to 2 to add dumps of all IDLs + * read from and written to the database (used for free space management). + */ +#define MDB_DEBUG 0 +#endif + +#if MDB_DEBUG +static int mdb_debug; +static txnid_t mdb_debug_start; + + /** Print a debug message with printf formatting. + * Requires double parenthesis around 2 or more args. + */ +# define DPRINTF(args) ((void) ((mdb_debug) && DPRINTF0 args)) +# define DPRINTF0(fmt, ...) \ + fprintf(stderr, "%s:%d " fmt "\n", mdb_func_, __LINE__, __VA_ARGS__) +#else +# define DPRINTF(args) ((void) 0) +#endif + /** Print a debug string. + * The string is printed literally, with no format processing. + */ +#define DPUTS(arg) DPRINTF(("%s", arg)) + /** Debuging output value of a cursor DBI: Negative in a sub-cursor. */ +#define DDBI(mc) \ + (((mc)->mc_flags & C_SUB) ? -(int)(mc)->mc_dbi : (int)(mc)->mc_dbi) +/** @} */ + + /** @brief The maximum size of a database page. + * + * It is 32k or 64k, since value-PAGEBASE must fit in + * #MDB_page.%mp_upper. + * + * LMDB will use database pages < OS pages if needed. + * That causes more I/O in write transactions: The OS must + * know (read) the whole page before writing a partial page. + * + * Note that we don't currently support Huge pages. On Linux, + * regular data files cannot use Huge pages, and in general + * Huge pages aren't actually pageable. We rely on the OS + * demand-pager to read our data and page it out when memory + * pressure from other processes is high. So until OSs have + * actual paging support for Huge pages, they're not viable. + */ +#define MAX_PAGESIZE (PAGEBASE ? 0x10000 : 0x8000) + + /** The minimum number of keys required in a database page. + * Setting this to a larger value will place a smaller bound on the + * maximum size of a data item. Data items larger than this size will + * be pushed into overflow pages instead of being stored directly in + * the B-tree node. This value used to default to 4. With a page size + * of 4096 bytes that meant that any item larger than 1024 bytes would + * go into an overflow page. That also meant that on average 2-3KB of + * each overflow page was wasted space. The value cannot be lower than + * 2 because then there would no longer be a tree structure. With this + * value, items larger than 2KB will go into overflow pages, and on + * average only 1KB will be wasted. + */ +#define MDB_MINKEYS 2 + + /** A stamp that identifies a file as an LMDB file. + * There's nothing special about this value other than that it is easily + * recognizable, and it will reflect any byte order mismatches. + */ +#define MDB_MAGIC 0xBEEFC0DE + + /** The version number for a database's datafile format. */ +#define MDB_DATA_VERSION ((MDB_DEVEL) ? 999 : 1) + /** The version number for a database's lockfile format. */ +#define MDB_LOCK_VERSION 1 + + /** @brief The max size of a key we can write, or 0 for computed max. + * + * This macro should normally be left alone or set to 0. + * Note that a database with big keys or dupsort data cannot be + * reliably modified by a liblmdb which uses a smaller max. + * The default is 511 for backwards compat, or 0 when #MDB_DEVEL. + * + * Other values are allowed, for backwards compat. However: + * A value bigger than the computed max can break if you do not + * know what you are doing, and liblmdb <= 0.9.10 can break when + * modifying a DB with keys/dupsort data bigger than its max. + * + * Data items in an #MDB_DUPSORT database are also limited to + * this size, since they're actually keys of a sub-DB. Keys and + * #MDB_DUPSORT data items must fit on a node in a regular page. + */ +#ifndef MDB_MAXKEYSIZE +#define MDB_MAXKEYSIZE ((MDB_DEVEL) ? 0 : 511) +#endif + + /** The maximum size of a key we can write to the environment. */ +#if MDB_MAXKEYSIZE +#define ENV_MAXKEY(env) (MDB_MAXKEYSIZE) +#else +#define ENV_MAXKEY(env) ((env)->me_maxkey) +#endif + + /** @brief The maximum size of a data item. + * + * We only store a 32 bit value for node sizes. + */ +#define MAXDATASIZE 0xffffffffUL + +#if MDB_DEBUG + /** Key size which fits in a #DKBUF. + * @ingroup debug + */ +#define DKBUF_MAXKEYSIZE ((MDB_MAXKEYSIZE) > 0 ? (MDB_MAXKEYSIZE) : 511) + /** A key buffer. + * @ingroup debug + * This is used for printing a hex dump of a key's contents. + */ +#define DKBUF char kbuf[DKBUF_MAXKEYSIZE*2+1] + /** Display a key in hex. + * @ingroup debug + * Invoke a function to display a key in hex. + */ +#define DKEY(x) mdb_dkey(x, kbuf) +#else +#define DKBUF +#define DKEY(x) 0 +#endif + + /** An invalid page number. + * Mainly used to denote an empty tree. + */ +#define P_INVALID (~(pgno_t)0) + + /** Test if the flags \b f are set in a flag word \b w. */ +#define F_ISSET(w, f) (((w) & (f)) == (f)) + + /** Round \b n up to an even number. */ +#define EVEN(n) (((n) + 1U) & -2) /* sign-extending -2 to match n+1U */ + + /** Used for offsets within a single page. + * Since memory pages are typically 4 or 8KB in size, 12-13 bits, + * this is plenty. + */ +typedef uint16_t indx_t; + + /** Default size of memory map. + * This is certainly too small for any actual applications. Apps should always set + * the size explicitly using #mdb_env_set_mapsize(). + */ +#define DEFAULT_MAPSIZE 1048576 + +/** @defgroup readers Reader Lock Table + * Readers don't acquire any locks for their data access. Instead, they + * simply record their transaction ID in the reader table. The reader + * mutex is needed just to find an empty slot in the reader table. The + * slot's address is saved in thread-specific data so that subsequent read + * transactions started by the same thread need no further locking to proceed. + * + * If #MDB_NOTLS is set, the slot address is not saved in thread-specific data. + * + * No reader table is used if the database is on a read-only filesystem, or + * if #MDB_NOLOCK is set. + * + * Since the database uses multi-version concurrency control, readers don't + * actually need any locking. This table is used to keep track of which + * readers are using data from which old transactions, so that we'll know + * when a particular old transaction is no longer in use. Old transactions + * that have discarded any data pages can then have those pages reclaimed + * for use by a later write transaction. + * + * The lock table is constructed such that reader slots are aligned with the + * processor's cache line size. Any slot is only ever used by one thread. + * This alignment guarantees that there will be no contention or cache + * thrashing as threads update their own slot info, and also eliminates + * any need for locking when accessing a slot. + * + * A writer thread will scan every slot in the table to determine the oldest + * outstanding reader transaction. Any freed pages older than this will be + * reclaimed by the writer. The writer doesn't use any locks when scanning + * this table. This means that there's no guarantee that the writer will + * see the most up-to-date reader info, but that's not required for correct + * operation - all we need is to know the upper bound on the oldest reader, + * we don't care at all about the newest reader. So the only consequence of + * reading stale information here is that old pages might hang around a + * while longer before being reclaimed. That's actually good anyway, because + * the longer we delay reclaiming old pages, the more likely it is that a + * string of contiguous pages can be found after coalescing old pages from + * many old transactions together. + * @{ + */ + /** Number of slots in the reader table. + * This value was chosen somewhat arbitrarily. 126 readers plus a + * couple mutexes fit exactly into 8KB on my development machine. + * Applications should set the table size using #mdb_env_set_maxreaders(). + */ +#define DEFAULT_READERS 126 + + /** The size of a CPU cache line in bytes. We want our lock structures + * aligned to this size to avoid false cache line sharing in the + * lock table. + * This value works for most CPUs. For Itanium this should be 128. + */ +#ifndef CACHELINE +#define CACHELINE 64 +#endif + + /** The information we store in a single slot of the reader table. + * In addition to a transaction ID, we also record the process and + * thread ID that owns a slot, so that we can detect stale information, + * e.g. threads or processes that went away without cleaning up. + * @note We currently don't check for stale records. We simply re-init + * the table when we know that we're the only process opening the + * lock file. + */ +typedef struct MDB_rxbody { + /** Current Transaction ID when this transaction began, or (txnid_t)-1. + * Multiple readers that start at the same time will probably have the + * same ID here. Again, it's not important to exclude them from + * anything; all we need to know is which version of the DB they + * started from so we can avoid overwriting any data used in that + * particular version. + */ + volatile txnid_t mrb_txnid; + /** The process ID of the process owning this reader txn. */ + volatile MDB_PID_T mrb_pid; + /** The thread ID of the thread owning this txn. */ + volatile MDB_THR_T mrb_tid; +} MDB_rxbody; + + /** The actual reader record, with cacheline padding. */ +typedef struct MDB_reader { + union { + MDB_rxbody mrx; + /** shorthand for mrb_txnid */ +#define mr_txnid mru.mrx.mrb_txnid +#define mr_pid mru.mrx.mrb_pid +#define mr_tid mru.mrx.mrb_tid + /** cache line alignment */ + char pad[(sizeof(MDB_rxbody)+CACHELINE-1) & ~(CACHELINE-1)]; + } mru; +} MDB_reader; + + /** The header for the reader table. + * The table resides in a memory-mapped file. (This is a different file + * than is used for the main database.) + * + * For POSIX the actual mutexes reside in the shared memory of this + * mapped file. On Windows, mutexes are named objects allocated by the + * kernel; we store the mutex names in this mapped file so that other + * processes can grab them. This same approach is also used on + * MacOSX/Darwin (using named semaphores) since MacOSX doesn't support + * process-shared POSIX mutexes. For these cases where a named object + * is used, the object name is derived from a 64 bit FNV hash of the + * environment pathname. As such, naming collisions are extremely + * unlikely. If a collision occurs, the results are unpredictable. + */ +typedef struct MDB_txbody { + /** Stamp identifying this as an LMDB file. It must be set + * to #MDB_MAGIC. */ + uint32_t mtb_magic; + /** Format of this lock file. Must be set to #MDB_LOCK_FORMAT. */ + uint32_t mtb_format; +#if defined(_WIN32) || defined(MDB_USE_POSIX_SEM) + char mtb_rmname[MNAME_LEN]; +#else + /** Mutex protecting access to this table. + * This is the reader table lock used with LOCK_MUTEX(). + */ + mdb_mutex_t mtb_rmutex; +#endif + /** The ID of the last transaction committed to the database. + * This is recorded here only for convenience; the value can always + * be determined by reading the main database meta pages. + */ + volatile txnid_t mtb_txnid; + /** The number of slots that have been used in the reader table. + * This always records the maximum count, it is not decremented + * when readers release their slots. + */ + volatile unsigned mtb_numreaders; +} MDB_txbody; + + /** The actual reader table definition. */ +typedef struct MDB_txninfo { + union { + MDB_txbody mtb; +#define mti_magic mt1.mtb.mtb_magic +#define mti_format mt1.mtb.mtb_format +#define mti_rmutex mt1.mtb.mtb_rmutex +#define mti_rmname mt1.mtb.mtb_rmname +#define mti_txnid mt1.mtb.mtb_txnid +#define mti_numreaders mt1.mtb.mtb_numreaders + char pad[(sizeof(MDB_txbody)+CACHELINE-1) & ~(CACHELINE-1)]; + } mt1; + union { +#if defined(_WIN32) || defined(MDB_USE_POSIX_SEM) + char mt2_wmname[MNAME_LEN]; +#define mti_wmname mt2.mt2_wmname +#else + mdb_mutex_t mt2_wmutex; +#define mti_wmutex mt2.mt2_wmutex +#endif + char pad[(MNAME_LEN+CACHELINE-1) & ~(CACHELINE-1)]; + } mt2; + MDB_reader mti_readers[1]; +} MDB_txninfo; + + /** Lockfile format signature: version, features and field layout */ +#define MDB_LOCK_FORMAT \ + ((uint32_t) \ + ((MDB_LOCK_VERSION) \ + /* Flags which describe functionality */ \ + + (((MDB_PIDLOCK) != 0) << 16))) +/** @} */ + +/** Common header for all page types. The page type depends on #mp_flags. + * + * #P_BRANCH and #P_LEAF pages have unsorted '#MDB_node's at the end, with + * sorted #mp_ptrs[] entries referring to them. Exception: #P_LEAF2 pages + * omit mp_ptrs and pack sorted #MDB_DUPFIXED values after the page header. + * + * #P_OVERFLOW records occupy one or more contiguous pages where only the + * first has a page header. They hold the real data of #F_BIGDATA nodes. + * + * #P_SUBP sub-pages are small leaf "pages" with duplicate data. + * A node with flag #F_DUPDATA but not #F_SUBDATA contains a sub-page. + * (Duplicate data can also go in sub-databases, which use normal pages.) + * + * #P_META pages contain #MDB_meta, the start point of an LMDB snapshot. + * + * Each non-metapage up to #MDB_meta.%mm_last_pg is reachable exactly once + * in the snapshot: Either used by a database or listed in a freeDB record. + */ +typedef struct MDB_page { +#define mp_pgno mp_p.p_pgno +#define mp_next mp_p.p_next + union { + pgno_t p_pgno; /**< page number */ + struct MDB_page *p_next; /**< for in-memory list of freed pages */ + } mp_p; + uint16_t mp_pad; /**< key size if this is a LEAF2 page */ +/** @defgroup mdb_page Page Flags + * @ingroup internal + * Flags for the page headers. + * @{ + */ +#define P_BRANCH 0x01 /**< branch page */ +#define P_LEAF 0x02 /**< leaf page */ +#define P_OVERFLOW 0x04 /**< overflow page */ +#define P_META 0x08 /**< meta page */ +#define P_DIRTY 0x10 /**< dirty page, also set for #P_SUBP pages */ +#define P_LEAF2 0x20 /**< for #MDB_DUPFIXED records */ +#define P_SUBP 0x40 /**< for #MDB_DUPSORT sub-pages */ +#define P_LOOSE 0x4000 /**< page was dirtied then freed, can be reused */ +#define P_KEEP 0x8000 /**< leave this page alone during spill */ +/** @} */ + uint16_t mp_flags; /**< @ref mdb_page */ +#define mp_lower mp_pb.pb.pb_lower +#define mp_upper mp_pb.pb.pb_upper +#define mp_pages mp_pb.pb_pages + union { + struct { + indx_t pb_lower; /**< lower bound of free space */ + indx_t pb_upper; /**< upper bound of free space */ + } pb; + uint32_t pb_pages; /**< number of overflow pages */ + } mp_pb; + indx_t mp_ptrs[1]; /**< dynamic size */ +} MDB_page; + + /** Size of the page header, excluding dynamic data at the end */ +#define PAGEHDRSZ ((unsigned) offsetof(MDB_page, mp_ptrs)) + + /** Address of first usable data byte in a page, after the header */ +#define METADATA(p) ((void *)((char *)(p) + PAGEHDRSZ)) + + /** ITS#7713, change PAGEBASE to handle 65536 byte pages */ +#define PAGEBASE ((MDB_DEVEL) ? PAGEHDRSZ : 0) + + /** Number of nodes on a page */ +#define NUMKEYS(p) (((p)->mp_lower - (PAGEHDRSZ-PAGEBASE)) >> 1) + + /** The amount of space remaining in the page */ +#define SIZELEFT(p) (indx_t)((p)->mp_upper - (p)->mp_lower) + + /** The percentage of space used in the page, in tenths of a percent. */ +#define PAGEFILL(env, p) (1000L * ((env)->me_psize - PAGEHDRSZ - SIZELEFT(p)) / \ + ((env)->me_psize - PAGEHDRSZ)) + /** The minimum page fill factor, in tenths of a percent. + * Pages emptier than this are candidates for merging. + */ +#define FILL_THRESHOLD 250 + + /** Test if a page is a leaf page */ +#define IS_LEAF(p) F_ISSET((p)->mp_flags, P_LEAF) + /** Test if a page is a LEAF2 page */ +#define IS_LEAF2(p) F_ISSET((p)->mp_flags, P_LEAF2) + /** Test if a page is a branch page */ +#define IS_BRANCH(p) F_ISSET((p)->mp_flags, P_BRANCH) + /** Test if a page is an overflow page */ +#define IS_OVERFLOW(p) F_ISSET((p)->mp_flags, P_OVERFLOW) + /** Test if a page is a sub page */ +#define IS_SUBP(p) F_ISSET((p)->mp_flags, P_SUBP) + + /** The number of overflow pages needed to store the given size. */ +#define OVPAGES(size, psize) ((PAGEHDRSZ-1 + (size)) / (psize) + 1) + + /** Link in #MDB_txn.%mt_loose_pgs list. + * Kept outside the page header, which is needed when reusing the page. + */ +#define NEXT_LOOSE_PAGE(p) (*(MDB_page **)((p) + 2)) + + /** Header for a single key/data pair within a page. + * Used in pages of type #P_BRANCH and #P_LEAF without #P_LEAF2. + * We guarantee 2-byte alignment for 'MDB_node's. + * + * #mn_lo and #mn_hi are used for data size on leaf nodes, and for child + * pgno on branch nodes. On 64 bit platforms, #mn_flags is also used + * for pgno. (Branch nodes have no flags). Lo and hi are in host byte + * order in case some accesses can be optimized to 32-bit word access. + * + * Leaf node flags describe node contents. #F_BIGDATA says the node's + * data part is the page number of an overflow page with actual data. + * #F_DUPDATA and #F_SUBDATA can be combined giving duplicate data in + * a sub-page/sub-database, and named databases (just #F_SUBDATA). + */ +typedef struct MDB_node { + /** part of data size or pgno + * @{ */ +#if BYTE_ORDER == LITTLE_ENDIAN + unsigned short mn_lo, mn_hi; +#else + unsigned short mn_hi, mn_lo; +#endif + /** @} */ +/** @defgroup mdb_node Node Flags + * @ingroup internal + * Flags for node headers. + * @{ + */ +#define F_BIGDATA 0x01 /**< data put on overflow page */ +#define F_SUBDATA 0x02 /**< data is a sub-database */ +#define F_DUPDATA 0x04 /**< data has duplicates */ + +/** valid flags for #mdb_node_add() */ +#define NODE_ADD_FLAGS (F_DUPDATA|F_SUBDATA|MDB_RESERVE|MDB_APPEND) + +/** @} */ + unsigned short mn_flags; /**< @ref mdb_node */ + unsigned short mn_ksize; /**< key size */ + char mn_data[1]; /**< key and data are appended here */ +} MDB_node; + + /** Size of the node header, excluding dynamic data at the end */ +#define NODESIZE offsetof(MDB_node, mn_data) + + /** Bit position of top word in page number, for shifting mn_flags */ +#define PGNO_TOPWORD ((pgno_t)-1 > 0xffffffffu ? 32 : 0) + + /** Size of a node in a branch page with a given key. + * This is just the node header plus the key, there is no data. + */ +#define INDXSIZE(k) (NODESIZE + ((k) == NULL ? 0 : (k)->mv_size)) + + /** Size of a node in a leaf page with a given key and data. + * This is node header plus key plus data size. + */ +#define LEAFSIZE(k, d) (NODESIZE + (k)->mv_size + (d)->mv_size) + + /** Address of node \b i in page \b p */ +#define NODEPTR(p, i) ((MDB_node *)((char *)(p) + (p)->mp_ptrs[i] + PAGEBASE)) + + /** Address of the key for the node */ +#define NODEKEY(node) (void *)((node)->mn_data) + + /** Address of the data for a node */ +#define NODEDATA(node) (void *)((char *)(node)->mn_data + (node)->mn_ksize) + + /** Get the page number pointed to by a branch node */ +#define NODEPGNO(node) \ + ((node)->mn_lo | ((pgno_t) (node)->mn_hi << 16) | \ + (PGNO_TOPWORD ? ((pgno_t) (node)->mn_flags << PGNO_TOPWORD) : 0)) + /** Set the page number in a branch node */ +#define SETPGNO(node,pgno) do { \ + (node)->mn_lo = (pgno) & 0xffff; (node)->mn_hi = (pgno) >> 16; \ + if (PGNO_TOPWORD) (node)->mn_flags = (pgno) >> PGNO_TOPWORD; } while(0) + + /** Get the size of the data in a leaf node */ +#define NODEDSZ(node) ((node)->mn_lo | ((unsigned)(node)->mn_hi << 16)) + /** Set the size of the data for a leaf node */ +#define SETDSZ(node,size) do { \ + (node)->mn_lo = (size) & 0xffff; (node)->mn_hi = (size) >> 16;} while(0) + /** The size of a key in a node */ +#define NODEKSZ(node) ((node)->mn_ksize) + + /** Copy a page number from src to dst */ +#ifdef MISALIGNED_OK +#define COPY_PGNO(dst,src) dst = src +#else +#if SIZE_MAX > 4294967295UL +#define COPY_PGNO(dst,src) do { \ + unsigned short *s, *d; \ + s = (unsigned short *)&(src); \ + d = (unsigned short *)&(dst); \ + *d++ = *s++; \ + *d++ = *s++; \ + *d++ = *s++; \ + *d = *s; \ +} while (0) +#else +#define COPY_PGNO(dst,src) do { \ + unsigned short *s, *d; \ + s = (unsigned short *)&(src); \ + d = (unsigned short *)&(dst); \ + *d++ = *s++; \ + *d = *s; \ +} while (0) +#endif +#endif + /** The address of a key in a LEAF2 page. + * LEAF2 pages are used for #MDB_DUPFIXED sorted-duplicate sub-DBs. + * There are no node headers, keys are stored contiguously. + */ +#define LEAF2KEY(p, i, ks) ((char *)(p) + PAGEHDRSZ + ((i)*(ks))) + + /** Set the \b node's key into \b keyptr, if requested. */ +#define MDB_GET_KEY(node, keyptr) { if ((keyptr) != NULL) { \ + (keyptr)->mv_size = NODEKSZ(node); (keyptr)->mv_data = NODEKEY(node); } } + + /** Set the \b node's key into \b key. */ +#define MDB_GET_KEY2(node, key) { key.mv_size = NODEKSZ(node); key.mv_data = NODEKEY(node); } + + /** Information about a single database in the environment. */ +typedef struct MDB_db { + uint32_t md_pad; /**< also ksize for LEAF2 pages */ + uint16_t md_flags; /**< @ref mdb_dbi_open */ + uint16_t md_depth; /**< depth of this tree */ + pgno_t md_branch_pages; /**< number of internal pages */ + pgno_t md_leaf_pages; /**< number of leaf pages */ + pgno_t md_overflow_pages; /**< number of overflow pages */ + size_t md_entries; /**< number of data items */ + pgno_t md_root; /**< the root page of this tree */ +} MDB_db; + +#define MDB_VALID 0x8000 /**< DB handle is valid, for me_dbflags */ +#define PERSISTENT_FLAGS (0xffff & ~(MDB_VALID)) + /** #mdb_dbi_open() flags */ +#define VALID_FLAGS (MDB_REVERSEKEY|MDB_DUPSORT|MDB_INTEGERKEY|MDB_DUPFIXED|\ + MDB_INTEGERDUP|MDB_REVERSEDUP|MDB_CREATE) + + /** Handle for the DB used to track free pages. */ +#define FREE_DBI 0 + /** Handle for the default DB. */ +#define MAIN_DBI 1 + /** Number of DBs in metapage (free and main) - also hardcoded elsewhere */ +#define CORE_DBS 2 + + /** Number of meta pages - also hardcoded elsewhere */ +#define NUM_METAS 2 + + /** Meta page content. + * A meta page is the start point for accessing a database snapshot. + * Pages 0-1 are meta pages. Transaction N writes meta page #(N % 2). + */ +typedef struct MDB_meta { + /** Stamp identifying this as an LMDB file. It must be set + * to #MDB_MAGIC. */ + uint32_t mm_magic; + /** Version number of this file. Must be set to #MDB_DATA_VERSION. */ + uint32_t mm_version; + void *mm_address; /**< address for fixed mapping */ + size_t mm_mapsize; /**< size of mmap region */ + MDB_db mm_dbs[CORE_DBS]; /**< first is free space, 2nd is main db */ + /** The size of pages used in this DB */ +#define mm_psize mm_dbs[FREE_DBI].md_pad + /** Any persistent environment flags. @ref mdb_env */ +#define mm_flags mm_dbs[FREE_DBI].md_flags + /** Last used page in the datafile. + * Actually the file may be shorter if the freeDB lists the final pages. + */ + pgno_t mm_last_pg; + volatile txnid_t mm_txnid; /**< txnid that committed this page */ +} MDB_meta; + + /** Buffer for a stack-allocated meta page. + * The members define size and alignment, and silence type + * aliasing warnings. They are not used directly; that could + * mean incorrectly using several union members in parallel. + */ +typedef union MDB_metabuf { + MDB_page mb_page; + struct { + char mm_pad[PAGEHDRSZ]; + MDB_meta mm_meta; + } mb_metabuf; +} MDB_metabuf; + + /** Auxiliary DB info. + * The information here is mostly static/read-only. There is + * only a single copy of this record in the environment. + */ +typedef struct MDB_dbx { + MDB_val md_name; /**< name of the database */ + MDB_cmp_func *md_cmp; /**< function for comparing keys */ + MDB_cmp_func *md_dcmp; /**< function for comparing data items */ + MDB_rel_func *md_rel; /**< user relocate function */ + void *md_relctx; /**< user-provided context for md_rel */ +} MDB_dbx; + + /** A database transaction. + * Every operation requires a transaction handle. + */ +struct MDB_txn { + MDB_txn *mt_parent; /**< parent of a nested txn */ + /** Nested txn under this txn, set together with flag #MDB_TXN_HAS_CHILD */ + MDB_txn *mt_child; + pgno_t mt_next_pgno; /**< next unallocated page */ + /** The ID of this transaction. IDs are integers incrementing from 1. + * Only committed write transactions increment the ID. If a transaction + * aborts, the ID may be re-used by the next writer. + */ + txnid_t mt_txnid; + MDB_env *mt_env; /**< the DB environment */ + /** The list of pages that became unused during this transaction. + */ + MDB_IDL mt_free_pgs; + /** The list of loose pages that became unused and may be reused + * in this transaction, linked through #NEXT_LOOSE_PAGE(page). + */ + MDB_page *mt_loose_pgs; + /** Number of loose pages (#mt_loose_pgs) */ + int mt_loose_count; + /** The sorted list of dirty pages we temporarily wrote to disk + * because the dirty list was full. page numbers in here are + * shifted left by 1, deleted slots have the LSB set. + */ + MDB_IDL mt_spill_pgs; + union { + /** For write txns: Modified pages. Sorted when not MDB_WRITEMAP. */ + MDB_ID2L dirty_list; + /** For read txns: This thread/txn's reader table slot, or NULL. */ + MDB_reader *reader; + } mt_u; + /** Array of records for each DB known in the environment. */ + MDB_dbx *mt_dbxs; + /** Array of MDB_db records for each known DB */ + MDB_db *mt_dbs; + /** Array of sequence numbers for each DB handle */ + unsigned int *mt_dbiseqs; +/** @defgroup mt_dbflag Transaction DB Flags + * @ingroup internal + * @{ + */ +#define DB_DIRTY 0x01 /**< DB was written in this txn */ +#define DB_STALE 0x02 /**< Named-DB record is older than txnID */ +#define DB_NEW 0x04 /**< Named-DB handle opened in this txn */ +#define DB_VALID 0x08 /**< DB handle is valid, see also #MDB_VALID */ +#define DB_USRVALID 0x10 /**< As #DB_VALID, but not set for #FREE_DBI */ +#define DB_DUPDATA 0x20 /**< DB is #MDB_DUPSORT data */ +/** @} */ + /** In write txns, array of cursors for each DB */ + MDB_cursor **mt_cursors; + /** Array of flags for each DB */ + unsigned char *mt_dbflags; + /** Number of DB records in use, or 0 when the txn is finished. + * This number only ever increments until the txn finishes; we + * don't decrement it when individual DB handles are closed. + */ + MDB_dbi mt_numdbs; + +/** @defgroup mdb_txn Transaction Flags + * @ingroup internal + * @{ + */ + /** #mdb_txn_begin() flags */ +#define MDB_TXN_BEGIN_FLAGS MDB_RDONLY +#define MDB_TXN_RDONLY MDB_RDONLY /**< read-only transaction */ + /* internal txn flags */ +#define MDB_TXN_WRITEMAP MDB_WRITEMAP /**< copy of #MDB_env flag in writers */ +#define MDB_TXN_FINISHED 0x01 /**< txn is finished or never began */ +#define MDB_TXN_ERROR 0x02 /**< txn is unusable after an error */ +#define MDB_TXN_DIRTY 0x04 /**< must write, even if dirty list is empty */ +#define MDB_TXN_SPILLS 0x08 /**< txn or a parent has spilled pages */ +#define MDB_TXN_HAS_CHILD 0x10 /**< txn has an #MDB_txn.%mt_child */ + /** most operations on the txn are currently illegal */ +#define MDB_TXN_BLOCKED (MDB_TXN_FINISHED|MDB_TXN_ERROR|MDB_TXN_HAS_CHILD) +/** @} */ + unsigned int mt_flags; /**< @ref mdb_txn */ + /** #dirty_list room: Array size - \#dirty pages visible to this txn. + * Includes ancestor txns' dirty pages not hidden by other txns' + * dirty/spilled pages. Thus commit(nested txn) has room to merge + * dirty_list into mt_parent after freeing hidden mt_parent pages. + */ + unsigned int mt_dirty_room; +}; + +/** Enough space for 2^32 nodes with minimum of 2 keys per node. I.e., plenty. + * At 4 keys per node, enough for 2^64 nodes, so there's probably no need to + * raise this on a 64 bit machine. + */ +#define CURSOR_STACK 32 + +struct MDB_xcursor; + + /** Cursors are used for all DB operations. + * A cursor holds a path of (page pointer, key index) from the DB + * root to a position in the DB, plus other state. #MDB_DUPSORT + * cursors include an xcursor to the current data item. Write txns + * track their cursors and keep them up to date when data moves. + * Exception: An xcursor's pointer to a #P_SUBP page can be stale. + * (A node with #F_DUPDATA but no #F_SUBDATA contains a subpage). + */ +struct MDB_cursor { + /** Next cursor on this DB in this txn */ + MDB_cursor *mc_next; + /** Backup of the original cursor if this cursor is a shadow */ + MDB_cursor *mc_backup; + /** Context used for databases with #MDB_DUPSORT, otherwise NULL */ + struct MDB_xcursor *mc_xcursor; + /** The transaction that owns this cursor */ + MDB_txn *mc_txn; + /** The database handle this cursor operates on */ + MDB_dbi mc_dbi; + /** The database record for this cursor */ + MDB_db *mc_db; + /** The database auxiliary record for this cursor */ + MDB_dbx *mc_dbx; + /** The @ref mt_dbflag for this database */ + unsigned char *mc_dbflag; + unsigned short mc_snum; /**< number of pushed pages */ + unsigned short mc_top; /**< index of top page, normally mc_snum-1 */ +/** @defgroup mdb_cursor Cursor Flags + * @ingroup internal + * Cursor state flags. + * @{ + */ +#define C_INITIALIZED 0x01 /**< cursor has been initialized and is valid */ +#define C_EOF 0x02 /**< No more data */ +#define C_SUB 0x04 /**< Cursor is a sub-cursor */ +#define C_DEL 0x08 /**< last op was a cursor_del */ +#define C_UNTRACK 0x40 /**< Un-track cursor when closing */ +/** @} */ + unsigned int mc_flags; /**< @ref mdb_cursor */ + MDB_page *mc_pg[CURSOR_STACK]; /**< stack of pushed pages */ + indx_t mc_ki[CURSOR_STACK]; /**< stack of page indices */ +}; + + /** Context for sorted-dup records. + * We could have gone to a fully recursive design, with arbitrarily + * deep nesting of sub-databases. But for now we only handle these + * levels - main DB, optional sub-DB, sorted-duplicate DB. + */ +typedef struct MDB_xcursor { + /** A sub-cursor for traversing the Dup DB */ + MDB_cursor mx_cursor; + /** The database record for this Dup DB */ + MDB_db mx_db; + /** The auxiliary DB record for this Dup DB */ + MDB_dbx mx_dbx; + /** The @ref mt_dbflag for this Dup DB */ + unsigned char mx_dbflag; +} MDB_xcursor; + + /** Check if there is an inited xcursor */ +#define XCURSOR_INITED(mc) \ + ((mc)->mc_xcursor && ((mc)->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED)) + + /** Update the xcursor's sub-page pointer, if any, in \b mc. Needed + * when the node which contains the sub-page may have moved. Called + * with leaf page \b mp = mc->mc_pg[\b top]. + */ +#define XCURSOR_REFRESH(mc, top, mp) do { \ + MDB_page *xr_pg = (mp); \ + MDB_node *xr_node; \ + if (!XCURSOR_INITED(mc) || (mc)->mc_ki[top] >= NUMKEYS(xr_pg)) break; \ + xr_node = NODEPTR(xr_pg, (mc)->mc_ki[top]); \ + if ((xr_node->mn_flags & (F_DUPDATA|F_SUBDATA)) == F_DUPDATA) \ + (mc)->mc_xcursor->mx_cursor.mc_pg[0] = NODEDATA(xr_node); \ +} while (0) + + /** State of FreeDB old pages, stored in the MDB_env */ +typedef struct MDB_pgstate { + pgno_t *mf_pghead; /**< Reclaimed freeDB pages, or NULL before use */ + txnid_t mf_pglast; /**< ID of last used record, or 0 if !mf_pghead */ +} MDB_pgstate; + + /** The database environment. */ +struct MDB_env { + HANDLE me_fd; /**< The main data file */ + HANDLE me_lfd; /**< The lock file */ + HANDLE me_mfd; /**< For writing and syncing the meta pages */ + /** Failed to update the meta page. Probably an I/O error. */ +#define MDB_FATAL_ERROR 0x80000000U + /** Some fields are initialized. */ +#define MDB_ENV_ACTIVE 0x20000000U + /** me_txkey is set */ +#define MDB_ENV_TXKEY 0x10000000U + /** fdatasync is unreliable */ +#define MDB_FSYNCONLY 0x08000000U + uint32_t me_flags; /**< @ref mdb_env */ + unsigned int me_psize; /**< DB page size, inited from me_os_psize */ + unsigned int me_os_psize; /**< OS page size, from #GET_PAGESIZE */ + unsigned int me_maxreaders; /**< size of the reader table */ + /** Max #MDB_txninfo.%mti_numreaders of interest to #mdb_env_close() */ + volatile int me_close_readers; + MDB_dbi me_numdbs; /**< number of DBs opened */ + MDB_dbi me_maxdbs; /**< size of the DB table */ + MDB_PID_T me_pid; /**< process ID of this env */ + char *me_path; /**< path to the DB files */ + char *me_map; /**< the memory map of the data file */ + MDB_txninfo *me_txns; /**< the memory map of the lock file or NULL */ + MDB_meta *me_metas[NUM_METAS]; /**< pointers to the two meta pages */ + void *me_pbuf; /**< scratch area for DUPSORT put() */ + MDB_txn *me_txn; /**< current write transaction */ + MDB_txn *me_txn0; /**< prealloc'd write transaction */ + size_t me_mapsize; /**< size of the data memory map */ + off_t me_size; /**< current file size */ + pgno_t me_maxpg; /**< me_mapsize / me_psize */ + MDB_dbx *me_dbxs; /**< array of static DB info */ + uint16_t *me_dbflags; /**< array of flags from MDB_db.md_flags */ + unsigned int *me_dbiseqs; /**< array of dbi sequence numbers */ + pthread_key_t me_txkey; /**< thread-key for readers */ + txnid_t me_pgoldest; /**< ID of oldest reader last time we looked */ + MDB_pgstate me_pgstate; /**< state of old pages from freeDB */ +# define me_pglast me_pgstate.mf_pglast +# define me_pghead me_pgstate.mf_pghead + MDB_page *me_dpages; /**< list of malloc'd blocks for re-use */ + /** IDL of pages that became unused in a write txn */ + MDB_IDL me_free_pgs; + /** ID2L of pages written during a write txn. Length MDB_IDL_UM_SIZE. */ + MDB_ID2L me_dirty_list; + /** Max number of freelist items that can fit in a single overflow page */ + int me_maxfree_1pg; + /** Max size of a node on a page */ + unsigned int me_nodemax; +#if !(MDB_MAXKEYSIZE) + unsigned int me_maxkey; /**< max size of a key */ +#endif + int me_live_reader; /**< have liveness lock in reader table */ +#ifdef _WIN32 + int me_pidquery; /**< Used in OpenProcess */ +#endif +#ifdef MDB_USE_POSIX_MUTEX /* Posix mutexes reside in shared mem */ +# define me_rmutex me_txns->mti_rmutex /**< Shared reader lock */ +# define me_wmutex me_txns->mti_wmutex /**< Shared writer lock */ +#else + mdb_mutex_t me_rmutex; + mdb_mutex_t me_wmutex; +#endif + void *me_userctx; /**< User-settable context */ + MDB_assert_func *me_assert_func; /**< Callback for assertion failures */ +}; + + /** Nested transaction */ +typedef struct MDB_ntxn { + MDB_txn mnt_txn; /**< the transaction */ + MDB_pgstate mnt_pgstate; /**< parent transaction's saved freestate */ +} MDB_ntxn; + + /** max number of pages to commit in one writev() call */ +#define MDB_COMMIT_PAGES 64 +#if defined(IOV_MAX) && IOV_MAX < MDB_COMMIT_PAGES +#undef MDB_COMMIT_PAGES +#define MDB_COMMIT_PAGES IOV_MAX +#endif + + /** max bytes to write in one call */ +#define MAX_WRITE (0x40000000U >> (sizeof(ssize_t) == 4)) + + /** Check \b txn and \b dbi arguments to a function */ +#define TXN_DBI_EXIST(txn, dbi, validity) \ + ((txn) && (dbi)<(txn)->mt_numdbs && ((txn)->mt_dbflags[dbi] & (validity))) + + /** Check for misused \b dbi handles */ +#define TXN_DBI_CHANGED(txn, dbi) \ + ((txn)->mt_dbiseqs[dbi] != (txn)->mt_env->me_dbiseqs[dbi]) + +static int mdb_page_alloc(MDB_cursor *mc, int num, MDB_page **mp); +static int mdb_page_new(MDB_cursor *mc, uint32_t flags, int num, MDB_page **mp); +static int mdb_page_touch(MDB_cursor *mc); + +#define MDB_END_NAMES {"committed", "empty-commit", "abort", "reset", \ + "reset-tmp", "fail-begin", "fail-beginchild"} +enum { + /* mdb_txn_end operation number, for logging */ + MDB_END_COMMITTED, MDB_END_EMPTY_COMMIT, MDB_END_ABORT, MDB_END_RESET, + MDB_END_RESET_TMP, MDB_END_FAIL_BEGIN, MDB_END_FAIL_BEGINCHILD +}; +#define MDB_END_OPMASK 0x0F /**< mask for #mdb_txn_end() operation number */ +#define MDB_END_UPDATE 0x10 /**< update env state (DBIs) */ +#define MDB_END_FREE 0x20 /**< free txn unless it is #MDB_env.%me_txn0 */ +#define MDB_END_SLOT MDB_NOTLS /**< release any reader slot if #MDB_NOTLS */ +static void mdb_txn_end(MDB_txn *txn, unsigned mode); + +static int mdb_page_get(MDB_cursor *mc, pgno_t pgno, MDB_page **mp, int *lvl); +static int mdb_page_search_root(MDB_cursor *mc, + MDB_val *key, int modify); +#define MDB_PS_MODIFY 1 +#define MDB_PS_ROOTONLY 2 +#define MDB_PS_FIRST 4 +#define MDB_PS_LAST 8 +static int mdb_page_search(MDB_cursor *mc, + MDB_val *key, int flags); +static int mdb_page_merge(MDB_cursor *csrc, MDB_cursor *cdst); + +#define MDB_SPLIT_REPLACE MDB_APPENDDUP /**< newkey is not new */ +static int mdb_page_split(MDB_cursor *mc, MDB_val *newkey, MDB_val *newdata, + pgno_t newpgno, unsigned int nflags); + +static int mdb_env_read_header(MDB_env *env, MDB_meta *meta); +static MDB_meta *mdb_env_pick_meta(const MDB_env *env); +static int mdb_env_write_meta(MDB_txn *txn); +#if defined(MDB_USE_POSIX_MUTEX) && !defined(MDB_ROBUST_SUPPORTED) /* Drop unused excl arg */ +# define mdb_env_close0(env, excl) mdb_env_close1(env) +#endif +static void mdb_env_close0(MDB_env *env, int excl); + +static MDB_node *mdb_node_search(MDB_cursor *mc, MDB_val *key, int *exactp); +static int mdb_node_add(MDB_cursor *mc, indx_t indx, + MDB_val *key, MDB_val *data, pgno_t pgno, unsigned int flags); +static void mdb_node_del(MDB_cursor *mc, int ksize); +static void mdb_node_shrink(MDB_page *mp, indx_t indx); +static int mdb_node_move(MDB_cursor *csrc, MDB_cursor *cdst, int fromleft); +static int mdb_node_read(MDB_cursor *mc, MDB_node *leaf, MDB_val *data); +static size_t mdb_leaf_size(MDB_env *env, MDB_val *key, MDB_val *data); +static size_t mdb_branch_size(MDB_env *env, MDB_val *key); + +static int mdb_rebalance(MDB_cursor *mc); +static int mdb_update_key(MDB_cursor *mc, MDB_val *key); + +static void mdb_cursor_pop(MDB_cursor *mc); +static int mdb_cursor_push(MDB_cursor *mc, MDB_page *mp); + +static int mdb_cursor_del0(MDB_cursor *mc); +static int mdb_del0(MDB_txn *txn, MDB_dbi dbi, MDB_val *key, MDB_val *data, unsigned flags); +static int mdb_cursor_sibling(MDB_cursor *mc, int move_right); +static int mdb_cursor_next(MDB_cursor *mc, MDB_val *key, MDB_val *data, MDB_cursor_op op); +static int mdb_cursor_prev(MDB_cursor *mc, MDB_val *key, MDB_val *data, MDB_cursor_op op); +static int mdb_cursor_set(MDB_cursor *mc, MDB_val *key, MDB_val *data, MDB_cursor_op op, + int *exactp); +static int mdb_cursor_first(MDB_cursor *mc, MDB_val *key, MDB_val *data); +static int mdb_cursor_last(MDB_cursor *mc, MDB_val *key, MDB_val *data); + +static void mdb_cursor_init(MDB_cursor *mc, MDB_txn *txn, MDB_dbi dbi, MDB_xcursor *mx); +static void mdb_xcursor_init0(MDB_cursor *mc); +static void mdb_xcursor_init1(MDB_cursor *mc, MDB_node *node); +static void mdb_xcursor_init2(MDB_cursor *mc, MDB_xcursor *src_mx, int force); + +static int mdb_drop0(MDB_cursor *mc, int subs); +static void mdb_default_cmp(MDB_txn *txn, MDB_dbi dbi); +static int mdb_reader_check0(MDB_env *env, int rlocked, int *dead); + +/** @cond */ +static MDB_cmp_func mdb_cmp_memn, mdb_cmp_memnr, mdb_cmp_int, mdb_cmp_cint, mdb_cmp_long; +/** @endcond */ + +/** Compare two items pointing at size_t's of unknown alignment. */ +#ifdef MISALIGNED_OK +# define mdb_cmp_clong mdb_cmp_long +#else +# define mdb_cmp_clong mdb_cmp_cint +#endif + +#ifdef _WIN32 +static SECURITY_DESCRIPTOR mdb_null_sd; +static SECURITY_ATTRIBUTES mdb_all_sa; +static int mdb_sec_inited; + +struct MDB_name; +static int utf8_to_utf16(const char *src, struct MDB_name *dst, int xtra); +#endif + +/** Return the library version info. */ +char * ESECT +mdb_version(int *major, int *minor, int *patch) +{ + if (major) *major = MDB_VERSION_MAJOR; + if (minor) *minor = MDB_VERSION_MINOR; + if (patch) *patch = MDB_VERSION_PATCH; + return MDB_VERSION_STRING; +} + +/** Table of descriptions for LMDB @ref errors */ +static char *const mdb_errstr[] = { + "MDB_KEYEXIST: Key/data pair already exists", + "MDB_NOTFOUND: No matching key/data pair found", + "MDB_PAGE_NOTFOUND: Requested page not found", + "MDB_CORRUPTED: Located page was wrong type", + "MDB_PANIC: Update of meta page failed or environment had fatal error", + "MDB_VERSION_MISMATCH: Database environment version mismatch", + "MDB_INVALID: File is not an LMDB file", + "MDB_MAP_FULL: Environment mapsize limit reached", + "MDB_DBS_FULL: Environment maxdbs limit reached", + "MDB_READERS_FULL: Environment maxreaders limit reached", + "MDB_TLS_FULL: Thread-local storage keys full - too many environments open", + "MDB_TXN_FULL: Transaction has too many dirty pages - transaction too big", + "MDB_CURSOR_FULL: Internal error - cursor stack limit reached", + "MDB_PAGE_FULL: Internal error - page has no more space", + "MDB_MAP_RESIZED: Database contents grew beyond environment mapsize", + "MDB_INCOMPATIBLE: Operation and DB incompatible, or DB flags changed", + "MDB_BAD_RSLOT: Invalid reuse of reader locktable slot", + "MDB_BAD_TXN: Transaction must abort, has a child, or is invalid", + "MDB_BAD_VALSIZE: Unsupported size of key/DB name/data, or wrong DUPFIXED size", + "MDB_BAD_DBI: The specified DBI handle was closed/changed unexpectedly", +}; + +char * +mdb_strerror(int err) +{ +#ifdef _WIN32 + /** HACK: pad 4KB on stack over the buf. Return system msgs in buf. + * This works as long as no function between the call to mdb_strerror + * and the actual use of the message uses more than 4K of stack. + */ +#define MSGSIZE 1024 +#define PADSIZE 4096 + char buf[MSGSIZE+PADSIZE], *ptr = buf; +#endif + int i; + if (!err) + return ("Successful return: 0"); + + if (err >= MDB_KEYEXIST && err <= MDB_LAST_ERRCODE) { + i = err - MDB_KEYEXIST; + return mdb_errstr[i]; + } + +#ifdef _WIN32 + /* These are the C-runtime error codes we use. The comment indicates + * their numeric value, and the Win32 error they would correspond to + * if the error actually came from a Win32 API. A major mess, we should + * have used LMDB-specific error codes for everything. + */ + switch(err) { + case ENOENT: /* 2, FILE_NOT_FOUND */ + case EIO: /* 5, ACCESS_DENIED */ + case ENOMEM: /* 12, INVALID_ACCESS */ + case EACCES: /* 13, INVALID_DATA */ + case EBUSY: /* 16, CURRENT_DIRECTORY */ + case EINVAL: /* 22, BAD_COMMAND */ + case ENOSPC: /* 28, OUT_OF_PAPER */ + return strerror(err); + default: + ; + } + buf[0] = 0; + FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, err, 0, ptr, MSGSIZE, (va_list *)buf+MSGSIZE); + return ptr; +#else + return strerror(err); +#endif +} + +/** assert(3) variant in cursor context */ +#define mdb_cassert(mc, expr) mdb_assert0((mc)->mc_txn->mt_env, expr, #expr) +/** assert(3) variant in transaction context */ +#define mdb_tassert(txn, expr) mdb_assert0((txn)->mt_env, expr, #expr) +/** assert(3) variant in environment context */ +#define mdb_eassert(env, expr) mdb_assert0(env, expr, #expr) + +#ifndef NDEBUG +# define mdb_assert0(env, expr, expr_txt) ((expr) ? (void)0 : \ + mdb_assert_fail(env, expr_txt, mdb_func_, __FILE__, __LINE__)) + +static void ESECT +mdb_assert_fail(MDB_env *env, const char *expr_txt, + const char *func, const char *file, int line) +{ + char buf[400]; + sprintf(buf, "%.100s:%d: Assertion '%.200s' failed in %.40s()", + file, line, expr_txt, func); + if (env->me_assert_func) + env->me_assert_func(env, buf); + fprintf(stderr, "%s\n", buf); + abort(); +} +#else +# define mdb_assert0(env, expr, expr_txt) ((void) 0) +#endif /* NDEBUG */ + +#if MDB_DEBUG +/** Return the page number of \b mp which may be sub-page, for debug output */ +static pgno_t +mdb_dbg_pgno(MDB_page *mp) +{ + pgno_t ret; + COPY_PGNO(ret, mp->mp_pgno); + return ret; +} + +/** Display a key in hexadecimal and return the address of the result. + * @param[in] key the key to display + * @param[in] buf the buffer to write into. Should always be #DKBUF. + * @return The key in hexadecimal form. + */ +char * +mdb_dkey(MDB_val *key, char *buf) +{ + char *ptr = buf; + unsigned char *c = key->mv_data; + unsigned int i; + + if (!key) + return ""; + + if (key->mv_size > DKBUF_MAXKEYSIZE) + return "MDB_MAXKEYSIZE"; + /* may want to make this a dynamic check: if the key is mostly + * printable characters, print it as-is instead of converting to hex. + */ +#if 1 + buf[0] = '\0'; + for (i=0; i<key->mv_size; i++) + ptr += sprintf(ptr, "%02x", *c++); +#else + sprintf(buf, "%.*s", key->mv_size, key->mv_data); +#endif + return buf; +} + +static const char * +mdb_leafnode_type(MDB_node *n) +{ + static char *const tp[2][2] = {{"", ": DB"}, {": sub-page", ": sub-DB"}}; + return F_ISSET(n->mn_flags, F_BIGDATA) ? ": overflow page" : + tp[F_ISSET(n->mn_flags, F_DUPDATA)][F_ISSET(n->mn_flags, F_SUBDATA)]; +} + +/** Display all the keys in the page. */ +void +mdb_page_list(MDB_page *mp) +{ + pgno_t pgno = mdb_dbg_pgno(mp); + const char *type, *state = (mp->mp_flags & P_DIRTY) ? ", dirty" : ""; + MDB_node *node; + unsigned int i, nkeys, nsize, total = 0; + MDB_val key; + DKBUF; + + switch (mp->mp_flags & (P_BRANCH|P_LEAF|P_LEAF2|P_META|P_OVERFLOW|P_SUBP)) { + case P_BRANCH: type = "Branch page"; break; + case P_LEAF: type = "Leaf page"; break; + case P_LEAF|P_SUBP: type = "Sub-page"; break; + case P_LEAF|P_LEAF2: type = "LEAF2 page"; break; + case P_LEAF|P_LEAF2|P_SUBP: type = "LEAF2 sub-page"; break; + case P_OVERFLOW: + fprintf(stderr, "Overflow page %"Z"u pages %u%s\n", + pgno, mp->mp_pages, state); + return; + case P_META: + fprintf(stderr, "Meta-page %"Z"u txnid %"Z"u\n", + pgno, ((MDB_meta *)METADATA(mp))->mm_txnid); + return; + default: + fprintf(stderr, "Bad page %"Z"u flags 0x%X\n", pgno, mp->mp_flags); + return; + } + + nkeys = NUMKEYS(mp); + fprintf(stderr, "%s %"Z"u numkeys %d%s\n", type, pgno, nkeys, state); + + for (i=0; i<nkeys; i++) { + if (IS_LEAF2(mp)) { /* LEAF2 pages have no mp_ptrs[] or node headers */ + key.mv_size = nsize = mp->mp_pad; + key.mv_data = LEAF2KEY(mp, i, nsize); + total += nsize; + fprintf(stderr, "key %d: nsize %d, %s\n", i, nsize, DKEY(&key)); + continue; + } + node = NODEPTR(mp, i); + key.mv_size = node->mn_ksize; + key.mv_data = node->mn_data; + nsize = NODESIZE + key.mv_size; + if (IS_BRANCH(mp)) { + fprintf(stderr, "key %d: page %"Z"u, %s\n", i, NODEPGNO(node), + DKEY(&key)); + total += nsize; + } else { + if (F_ISSET(node->mn_flags, F_BIGDATA)) + nsize += sizeof(pgno_t); + else + nsize += NODEDSZ(node); + total += nsize; + nsize += sizeof(indx_t); + fprintf(stderr, "key %d: nsize %d, %s%s\n", + i, nsize, DKEY(&key), mdb_leafnode_type(node)); + } + total = EVEN(total); + } + fprintf(stderr, "Total: header %d + contents %d + unused %d\n", + IS_LEAF2(mp) ? PAGEHDRSZ : PAGEBASE + mp->mp_lower, total, SIZELEFT(mp)); +} + +void +mdb_cursor_chk(MDB_cursor *mc) +{ + unsigned int i; + MDB_node *node; + MDB_page *mp; + + if (!mc->mc_snum || !(mc->mc_flags & C_INITIALIZED)) return; + for (i=0; i<mc->mc_top; i++) { + mp = mc->mc_pg[i]; + node = NODEPTR(mp, mc->mc_ki[i]); + if (NODEPGNO(node) != mc->mc_pg[i+1]->mp_pgno) + printf("oops!\n"); + } + if (mc->mc_ki[i] >= NUMKEYS(mc->mc_pg[i])) + printf("ack!\n"); + if (XCURSOR_INITED(mc)) { + node = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]); + if (((node->mn_flags & (F_DUPDATA|F_SUBDATA)) == F_DUPDATA) && + mc->mc_xcursor->mx_cursor.mc_pg[0] != NODEDATA(node)) { + printf("blah!\n"); + } + } +} +#endif + +#if (MDB_DEBUG) > 2 +/** Count all the pages in each DB and in the freelist + * and make sure it matches the actual number of pages + * being used. + * All named DBs must be open for a correct count. + */ +static void mdb_audit(MDB_txn *txn) +{ + MDB_cursor mc; + MDB_val key, data; + MDB_ID freecount, count; + MDB_dbi i; + int rc; + + freecount = 0; + mdb_cursor_init(&mc, txn, FREE_DBI, NULL); + while ((rc = mdb_cursor_get(&mc, &key, &data, MDB_NEXT)) == 0) + freecount += *(MDB_ID *)data.mv_data; + mdb_tassert(txn, rc == MDB_NOTFOUND); + + count = 0; + for (i = 0; i<txn->mt_numdbs; i++) { + MDB_xcursor mx; + if (!(txn->mt_dbflags[i] & DB_VALID)) + continue; + mdb_cursor_init(&mc, txn, i, &mx); + if (txn->mt_dbs[i].md_root == P_INVALID) + continue; + count += txn->mt_dbs[i].md_branch_pages + + txn->mt_dbs[i].md_leaf_pages + + txn->mt_dbs[i].md_overflow_pages; + if (txn->mt_dbs[i].md_flags & MDB_DUPSORT) { + rc = mdb_page_search(&mc, NULL, MDB_PS_FIRST); + for (; rc == MDB_SUCCESS; rc = mdb_cursor_sibling(&mc, 1)) { + unsigned j; + MDB_page *mp; + mp = mc.mc_pg[mc.mc_top]; + for (j=0; j<NUMKEYS(mp); j++) { + MDB_node *leaf = NODEPTR(mp, j); + if (leaf->mn_flags & F_SUBDATA) { + MDB_db db; + memcpy(&db, NODEDATA(leaf), sizeof(db)); + count += db.md_branch_pages + db.md_leaf_pages + + db.md_overflow_pages; + } + } + } + mdb_tassert(txn, rc == MDB_NOTFOUND); + } + } + if (freecount + count + NUM_METAS != txn->mt_next_pgno) { + fprintf(stderr, "audit: %"Z"u freecount: %"Z"u count: %"Z"u total: %"Z"u next_pgno: %"Z"u\n", + txn->mt_txnid, freecount, count+NUM_METAS, + freecount+count+NUM_METAS, txn->mt_next_pgno); + } +} +#endif + +int +mdb_cmp(MDB_txn *txn, MDB_dbi dbi, const MDB_val *a, const MDB_val *b) +{ + return txn->mt_dbxs[dbi].md_cmp(a, b); +} + +int +mdb_dcmp(MDB_txn *txn, MDB_dbi dbi, const MDB_val *a, const MDB_val *b) +{ + MDB_cmp_func *dcmp = txn->mt_dbxs[dbi].md_dcmp; +#if UINT_MAX < SIZE_MAX + if (dcmp == mdb_cmp_int && a->mv_size == sizeof(size_t)) + dcmp = mdb_cmp_clong; +#endif + return dcmp(a, b); +} + +/** Allocate memory for a page. + * Re-use old malloc'd pages first for singletons, otherwise just malloc. + * Set #MDB_TXN_ERROR on failure. + */ +static MDB_page * +mdb_page_malloc(MDB_txn *txn, unsigned num) +{ + MDB_env *env = txn->mt_env; + MDB_page *ret = env->me_dpages; + size_t psize = env->me_psize, sz = psize, off; + /* For ! #MDB_NOMEMINIT, psize counts how much to init. + * For a single page alloc, we init everything after the page header. + * For multi-page, we init the final page; if the caller needed that + * many pages they will be filling in at least up to the last page. + */ + if (num == 1) { + if (ret) { + VGMEMP_ALLOC(env, ret, sz); + VGMEMP_DEFINED(ret, sizeof(ret->mp_next)); + env->me_dpages = ret->mp_next; + return ret; + } + psize -= off = PAGEHDRSZ; + } else { + sz *= num; + off = sz - psize; + } + if ((ret = malloc(sz)) != NULL) { + VGMEMP_ALLOC(env, ret, sz); + if (!(env->me_flags & MDB_NOMEMINIT)) { + memset((char *)ret + off, 0, psize); + ret->mp_pad = 0; + } + } else { + txn->mt_flags |= MDB_TXN_ERROR; + } + return ret; +} +/** Free a single page. + * Saves single pages to a list, for future reuse. + * (This is not used for multi-page overflow pages.) + */ +static void +mdb_page_free(MDB_env *env, MDB_page *mp) +{ + mp->mp_next = env->me_dpages; + VGMEMP_FREE(env, mp); + env->me_dpages = mp; +} + +/** Free a dirty page */ +static void +mdb_dpage_free(MDB_env *env, MDB_page *dp) +{ + if (!IS_OVERFLOW(dp) || dp->mp_pages == 1) { + mdb_page_free(env, dp); + } else { + /* large pages just get freed directly */ + VGMEMP_FREE(env, dp); + free(dp); + } +} + +/** Return all dirty pages to dpage list */ +static void +mdb_dlist_free(MDB_txn *txn) +{ + MDB_env *env = txn->mt_env; + MDB_ID2L dl = txn->mt_u.dirty_list; + unsigned i, n = dl[0].mid; + + for (i = 1; i <= n; i++) { + mdb_dpage_free(env, dl[i].mptr); + } + dl[0].mid = 0; +} + +/** Loosen or free a single page. + * Saves single pages to a list for future reuse + * in this same txn. It has been pulled from the freeDB + * and already resides on the dirty list, but has been + * deleted. Use these pages first before pulling again + * from the freeDB. + * + * If the page wasn't dirtied in this txn, just add it + * to this txn's free list. + */ +static int +mdb_page_loose(MDB_cursor *mc, MDB_page *mp) +{ + int loose = 0; + pgno_t pgno = mp->mp_pgno; + MDB_txn *txn = mc->mc_txn; + + if ((mp->mp_flags & P_DIRTY) && mc->mc_dbi != FREE_DBI) { + if (txn->mt_parent) { + MDB_ID2 *dl = txn->mt_u.dirty_list; + /* If txn has a parent, make sure the page is in our + * dirty list. + */ + if (dl[0].mid) { + unsigned x = mdb_mid2l_search(dl, pgno); + if (x <= dl[0].mid && dl[x].mid == pgno) { + if (mp != dl[x].mptr) { /* bad cursor? */ + mc->mc_flags &= ~(C_INITIALIZED|C_EOF); + txn->mt_flags |= MDB_TXN_ERROR; + return MDB_CORRUPTED; + } + /* ok, it's ours */ + loose = 1; + } + } + } else { + /* no parent txn, so it's just ours */ + loose = 1; + } + } + if (loose) { + DPRINTF(("loosen db %d page %"Z"u", DDBI(mc), + mp->mp_pgno)); + NEXT_LOOSE_PAGE(mp) = txn->mt_loose_pgs; + txn->mt_loose_pgs = mp; + txn->mt_loose_count++; + mp->mp_flags |= P_LOOSE; + } else { + int rc = mdb_midl_append(&txn->mt_free_pgs, pgno); + if (rc) + return rc; + } + + return MDB_SUCCESS; +} + +/** Set or clear P_KEEP in dirty, non-overflow, non-sub pages watched by txn. + * @param[in] mc A cursor handle for the current operation. + * @param[in] pflags Flags of the pages to update: + * P_DIRTY to set P_KEEP, P_DIRTY|P_KEEP to clear it. + * @param[in] all No shortcuts. Needed except after a full #mdb_page_flush(). + * @return 0 on success, non-zero on failure. + */ +static int +mdb_pages_xkeep(MDB_cursor *mc, unsigned pflags, int all) +{ + enum { Mask = P_SUBP|P_DIRTY|P_LOOSE|P_KEEP }; + MDB_txn *txn = mc->mc_txn; + MDB_cursor *m3, *m0 = mc; + MDB_xcursor *mx; + MDB_page *dp, *mp; + MDB_node *leaf; + unsigned i, j; + int rc = MDB_SUCCESS, level; + + /* Mark pages seen by cursors */ + if (mc->mc_flags & C_UNTRACK) + mc = NULL; /* will find mc in mt_cursors */ + for (i = txn->mt_numdbs;; mc = txn->mt_cursors[--i]) { + for (; mc; mc=mc->mc_next) { + if (!(mc->mc_flags & C_INITIALIZED)) + continue; + for (m3 = mc;; m3 = &mx->mx_cursor) { + mp = NULL; + for (j=0; j<m3->mc_snum; j++) { + mp = m3->mc_pg[j]; + if ((mp->mp_flags & Mask) == pflags) + mp->mp_flags ^= P_KEEP; + } + mx = m3->mc_xcursor; + /* Proceed to mx if it is at a sub-database */ + if (! (mx && (mx->mx_cursor.mc_flags & C_INITIALIZED))) + break; + if (! (mp && (mp->mp_flags & P_LEAF))) + break; + leaf = NODEPTR(mp, m3->mc_ki[j-1]); + if (!(leaf->mn_flags & F_SUBDATA)) + break; + } + } + if (i == 0) + break; + } + + if (all) { + /* Mark dirty root pages */ + for (i=0; i<txn->mt_numdbs; i++) { + if (txn->mt_dbflags[i] & DB_DIRTY) { + pgno_t pgno = txn->mt_dbs[i].md_root; + if (pgno == P_INVALID) + continue; + if ((rc = mdb_page_get(m0, pgno, &dp, &level)) != MDB_SUCCESS) + break; + if ((dp->mp_flags & Mask) == pflags && level <= 1) + dp->mp_flags ^= P_KEEP; + } + } + } + + return rc; +} + +static int mdb_page_flush(MDB_txn *txn, int keep); + +/** Spill pages from the dirty list back to disk. + * This is intended to prevent running into #MDB_TXN_FULL situations, + * but note that they may still occur in a few cases: + * 1) our estimate of the txn size could be too small. Currently this + * seems unlikely, except with a large number of #MDB_MULTIPLE items. + * 2) child txns may run out of space if their parents dirtied a + * lot of pages and never spilled them. TODO: we probably should do + * a preemptive spill during #mdb_txn_begin() of a child txn, if + * the parent's dirty_room is below a given threshold. + * + * Otherwise, if not using nested txns, it is expected that apps will + * not run into #MDB_TXN_FULL any more. The pages are flushed to disk + * the same way as for a txn commit, e.g. their P_DIRTY flag is cleared. + * If the txn never references them again, they can be left alone. + * If the txn only reads them, they can be used without any fuss. + * If the txn writes them again, they can be dirtied immediately without + * going thru all of the work of #mdb_page_touch(). Such references are + * handled by #mdb_page_unspill(). + * + * Also note, we never spill DB root pages, nor pages of active cursors, + * because we'll need these back again soon anyway. And in nested txns, + * we can't spill a page in a child txn if it was already spilled in a + * parent txn. That would alter the parent txns' data even though + * the child hasn't committed yet, and we'd have no way to undo it if + * the child aborted. + * + * @param[in] m0 cursor A cursor handle identifying the transaction and + * database for which we are checking space. + * @param[in] key For a put operation, the key being stored. + * @param[in] data For a put operation, the data being stored. + * @return 0 on success, non-zero on failure. + */ +static int +mdb_page_spill(MDB_cursor *m0, MDB_val *key, MDB_val *data) +{ + MDB_txn *txn = m0->mc_txn; + MDB_page *dp; + MDB_ID2L dl = txn->mt_u.dirty_list; + unsigned int i, j, need; + int rc; + + if (m0->mc_flags & C_SUB) + return MDB_SUCCESS; + + /* Estimate how much space this op will take */ + i = m0->mc_db->md_depth; + /* Named DBs also dirty the main DB */ + if (m0->mc_dbi >= CORE_DBS) + i += txn->mt_dbs[MAIN_DBI].md_depth; + /* For puts, roughly factor in the key+data size */ + if (key) + i += (LEAFSIZE(key, data) + txn->mt_env->me_psize) / txn->mt_env->me_psize; + i += i; /* double it for good measure */ + need = i; + + if (txn->mt_dirty_room > i) + return MDB_SUCCESS; + + if (!txn->mt_spill_pgs) { + txn->mt_spill_pgs = mdb_midl_alloc(MDB_IDL_UM_MAX); + if (!txn->mt_spill_pgs) + return ENOMEM; + } else { + /* purge deleted slots */ + MDB_IDL sl = txn->mt_spill_pgs; + unsigned int num = sl[0]; + j=0; + for (i=1; i<=num; i++) { + if (!(sl[i] & 1)) + sl[++j] = sl[i]; + } + sl[0] = j; + } + + /* Preserve pages which may soon be dirtied again */ + if ((rc = mdb_pages_xkeep(m0, P_DIRTY, 1)) != MDB_SUCCESS) + goto done; + + /* Less aggressive spill - we originally spilled the entire dirty list, + * with a few exceptions for cursor pages and DB root pages. But this + * turns out to be a lot of wasted effort because in a large txn many + * of those pages will need to be used again. So now we spill only 1/8th + * of the dirty pages. Testing revealed this to be a good tradeoff, + * better than 1/2, 1/4, or 1/10. + */ + if (need < MDB_IDL_UM_MAX / 8) + need = MDB_IDL_UM_MAX / 8; + + /* Save the page IDs of all the pages we're flushing */ + /* flush from the tail forward, this saves a lot of shifting later on. */ + for (i=dl[0].mid; i && need; i--) { + MDB_ID pn = dl[i].mid << 1; + dp = dl[i].mptr; + if (dp->mp_flags & (P_LOOSE|P_KEEP)) + continue; + /* Can't spill twice, make sure it's not already in a parent's + * spill list. + */ + if (txn->mt_parent) { + MDB_txn *tx2; + for (tx2 = txn->mt_parent; tx2; tx2 = tx2->mt_parent) { + if (tx2->mt_spill_pgs) { + j = mdb_midl_search(tx2->mt_spill_pgs, pn); + if (j <= tx2->mt_spill_pgs[0] && tx2->mt_spill_pgs[j] == pn) { + dp->mp_flags |= P_KEEP; + break; + } + } + } + if (tx2) + continue; + } + if ((rc = mdb_midl_append(&txn->mt_spill_pgs, pn))) + goto done; + need--; + } + mdb_midl_sort(txn->mt_spill_pgs); + + /* Flush the spilled part of dirty list */ + if ((rc = mdb_page_flush(txn, i)) != MDB_SUCCESS) + goto done; + + /* Reset any dirty pages we kept that page_flush didn't see */ + rc = mdb_pages_xkeep(m0, P_DIRTY|P_KEEP, i); + +done: + txn->mt_flags |= rc ? MDB_TXN_ERROR : MDB_TXN_SPILLS; + return rc; +} + +/** Find oldest txnid still referenced. Expects txn->mt_txnid > 0. */ +static txnid_t +mdb_find_oldest(MDB_txn *txn) +{ + int i; + txnid_t mr, oldest = txn->mt_txnid - 1; + if (txn->mt_env->me_txns) { + MDB_reader *r = txn->mt_env->me_txns->mti_readers; + for (i = txn->mt_env->me_txns->mti_numreaders; --i >= 0; ) { + if (r[i].mr_pid) { + mr = r[i].mr_txnid; + if (oldest > mr) + oldest = mr; + } + } + } + return oldest; +} + +/** Add a page to the txn's dirty list */ +static void +mdb_page_dirty(MDB_txn *txn, MDB_page *mp) +{ + MDB_ID2 mid; + int rc, (*insert)(MDB_ID2L, MDB_ID2 *); + + if (txn->mt_flags & MDB_TXN_WRITEMAP) { + insert = mdb_mid2l_append; + } else { + insert = mdb_mid2l_insert; + } + mid.mid = mp->mp_pgno; + mid.mptr = mp; + rc = insert(txn->mt_u.dirty_list, &mid); + mdb_tassert(txn, rc == 0); + txn->mt_dirty_room--; +} + +/** Allocate page numbers and memory for writing. Maintain me_pglast, + * me_pghead and mt_next_pgno. Set #MDB_TXN_ERROR on failure. + * + * If there are free pages available from older transactions, they + * are re-used first. Otherwise allocate a new page at mt_next_pgno. + * Do not modify the freedB, just merge freeDB records into me_pghead[] + * and move me_pglast to say which records were consumed. Only this + * function can create me_pghead and move me_pglast/mt_next_pgno. + * @param[in] mc cursor A cursor handle identifying the transaction and + * database for which we are allocating. + * @param[in] num the number of pages to allocate. + * @param[out] mp Address of the allocated page(s). Requests for multiple pages + * will always be satisfied by a single contiguous chunk of memory. + * @return 0 on success, non-zero on failure. + */ +static int +mdb_page_alloc(MDB_cursor *mc, int num, MDB_page **mp) +{ +#ifdef MDB_PARANOID /* Seems like we can ignore this now */ + /* Get at most <Max_retries> more freeDB records once me_pghead + * has enough pages. If not enough, use new pages from the map. + * If <Paranoid> and mc is updating the freeDB, only get new + * records if me_pghead is empty. Then the freelist cannot play + * catch-up with itself by growing while trying to save it. + */ + enum { Paranoid = 1, Max_retries = 500 }; +#else + enum { Paranoid = 0, Max_retries = INT_MAX /*infinite*/ }; +#endif + int rc, retry = num * 60; + MDB_txn *txn = mc->mc_txn; + MDB_env *env = txn->mt_env; + pgno_t pgno, *mop = env->me_pghead; + unsigned i, j, mop_len = mop ? mop[0] : 0, n2 = num-1; + MDB_page *np; + txnid_t oldest = 0, last; + MDB_cursor_op op; + MDB_cursor m2; + int found_old = 0; + + /* If there are any loose pages, just use them */ + if (num == 1 && txn->mt_loose_pgs) { + np = txn->mt_loose_pgs; + txn->mt_loose_pgs = NEXT_LOOSE_PAGE(np); + txn->mt_loose_count--; + DPRINTF(("db %d use loose page %"Z"u", DDBI(mc), + np->mp_pgno)); + *mp = np; + return MDB_SUCCESS; + } + + *mp = NULL; + + /* If our dirty list is already full, we can't do anything */ + if (txn->mt_dirty_room == 0) { + rc = MDB_TXN_FULL; + goto fail; + } + + for (op = MDB_FIRST;; op = MDB_NEXT) { + MDB_val key, data; + MDB_node *leaf; + pgno_t *idl; + + /* Seek a big enough contiguous page range. Prefer + * pages at the tail, just truncating the list. + */ + if (mop_len > n2) { + i = mop_len; + do { + pgno = mop[i]; + if (mop[i-n2] == pgno+n2) + goto search_done; + } while (--i > n2); + if (--retry < 0) + break; + } + + if (op == MDB_FIRST) { /* 1st iteration */ + /* Prepare to fetch more and coalesce */ + last = env->me_pglast; + oldest = env->me_pgoldest; + mdb_cursor_init(&m2, txn, FREE_DBI, NULL); + if (last) { + op = MDB_SET_RANGE; + key.mv_data = &last; /* will look up last+1 */ + key.mv_size = sizeof(last); + } + if (Paranoid && mc->mc_dbi == FREE_DBI) + retry = -1; + } + if (Paranoid && retry < 0 && mop_len) + break; + + last++; + /* Do not fetch more if the record will be too recent */ + if (oldest <= last) { + if (!found_old) { + oldest = mdb_find_oldest(txn); + env->me_pgoldest = oldest; + found_old = 1; + } + if (oldest <= last) + break; + } + rc = mdb_cursor_get(&m2, &key, NULL, op); + if (rc) { + if (rc == MDB_NOTFOUND) + break; + goto fail; + } + last = *(txnid_t*)key.mv_data; + if (oldest <= last) { + if (!found_old) { + oldest = mdb_find_oldest(txn); + env->me_pgoldest = oldest; + found_old = 1; + } + if (oldest <= last) + break; + } + np = m2.mc_pg[m2.mc_top]; + leaf = NODEPTR(np, m2.mc_ki[m2.mc_top]); + if ((rc = mdb_node_read(&m2, leaf, &data)) != MDB_SUCCESS) + goto fail; + + idl = (MDB_ID *) data.mv_data; + i = idl[0]; + if (!mop) { + if (!(env->me_pghead = mop = mdb_midl_alloc(i))) { + rc = ENOMEM; + goto fail; + } + } else { + if ((rc = mdb_midl_need(&env->me_pghead, i)) != 0) + goto fail; + mop = env->me_pghead; + } + env->me_pglast = last; +#if (MDB_DEBUG) > 1 + DPRINTF(("IDL read txn %"Z"u root %"Z"u num %u", + last, txn->mt_dbs[FREE_DBI].md_root, i)); + for (j = i; j; j--) + DPRINTF(("IDL %"Z"u", idl[j])); +#endif + /* Merge in descending sorted order */ + mdb_midl_xmerge(mop, idl); + mop_len = mop[0]; + } + + /* Use new pages from the map when nothing suitable in the freeDB */ + i = 0; + pgno = txn->mt_next_pgno; + if (pgno + num >= env->me_maxpg) { + DPUTS("DB size maxed out"); + rc = MDB_MAP_FULL; + goto fail; + } + +search_done: + if (env->me_flags & MDB_WRITEMAP) { + np = (MDB_page *)(env->me_map + env->me_psize * pgno); + } else { + if (!(np = mdb_page_malloc(txn, num))) { + rc = ENOMEM; + goto fail; + } + } + if (i) { + mop[0] = mop_len -= num; + /* Move any stragglers down */ + for (j = i-num; j < mop_len; ) + mop[++j] = mop[++i]; + } else { + txn->mt_next_pgno = pgno + num; + } + np->mp_pgno = pgno; + mdb_page_dirty(txn, np); + *mp = np; + + return MDB_SUCCESS; + +fail: + txn->mt_flags |= MDB_TXN_ERROR; + return rc; +} + +/** Copy the used portions of a non-overflow page. + * @param[in] dst page to copy into + * @param[in] src page to copy from + * @param[in] psize size of a page + */ +static void +mdb_page_copy(MDB_page *dst, MDB_page *src, unsigned int psize) +{ + enum { Align = sizeof(pgno_t) }; + indx_t upper = src->mp_upper, lower = src->mp_lower, unused = upper-lower; + + /* If page isn't full, just copy the used portion. Adjust + * alignment so memcpy may copy words instead of bytes. + */ + if ((unused &= -Align) && !IS_LEAF2(src)) { + upper = (upper + PAGEBASE) & -Align; + memcpy(dst, src, (lower + PAGEBASE + (Align-1)) & -Align); + memcpy((pgno_t *)((char *)dst+upper), (pgno_t *)((char *)src+upper), + psize - upper); + } else { + memcpy(dst, src, psize - unused); + } +} + +/** Pull a page off the txn's spill list, if present. + * If a page being referenced was spilled to disk in this txn, bring + * it back and make it dirty/writable again. + * @param[in] txn the transaction handle. + * @param[in] mp the page being referenced. It must not be dirty. + * @param[out] ret the writable page, if any. ret is unchanged if + * mp wasn't spilled. + */ +static int +mdb_page_unspill(MDB_txn *txn, MDB_page *mp, MDB_page **ret) +{ + MDB_env *env = txn->mt_env; + const MDB_txn *tx2; + unsigned x; + pgno_t pgno = mp->mp_pgno, pn = pgno << 1; + + for (tx2 = txn; tx2; tx2=tx2->mt_parent) { + if (!tx2->mt_spill_pgs) + continue; + x = mdb_midl_search(tx2->mt_spill_pgs, pn); + if (x <= tx2->mt_spill_pgs[0] && tx2->mt_spill_pgs[x] == pn) { + MDB_page *np; + int num; + if (txn->mt_dirty_room == 0) + return MDB_TXN_FULL; + if (IS_OVERFLOW(mp)) + num = mp->mp_pages; + else + num = 1; + if (env->me_flags & MDB_WRITEMAP) { + np = mp; + } else { + np = mdb_page_malloc(txn, num); + if (!np) + return ENOMEM; + if (num > 1) + memcpy(np, mp, num * env->me_psize); + else + mdb_page_copy(np, mp, env->me_psize); + } + if (tx2 == txn) { + /* If in current txn, this page is no longer spilled. + * If it happens to be the last page, truncate the spill list. + * Otherwise mark it as deleted by setting the LSB. + */ + if (x == txn->mt_spill_pgs[0]) + txn->mt_spill_pgs[0]--; + else + txn->mt_spill_pgs[x] |= 1; + } /* otherwise, if belonging to a parent txn, the + * page remains spilled until child commits + */ + + mdb_page_dirty(txn, np); + np->mp_flags |= P_DIRTY; + *ret = np; + break; + } + } + return MDB_SUCCESS; +} + +/** Touch a page: make it dirty and re-insert into tree with updated pgno. + * Set #MDB_TXN_ERROR on failure. + * @param[in] mc cursor pointing to the page to be touched + * @return 0 on success, non-zero on failure. + */ +static int +mdb_page_touch(MDB_cursor *mc) +{ + MDB_page *mp = mc->mc_pg[mc->mc_top], *np; + MDB_txn *txn = mc->mc_txn; + MDB_cursor *m2, *m3; + pgno_t pgno; + int rc; + + if (!F_ISSET(mp->mp_flags, P_DIRTY)) { + if (txn->mt_flags & MDB_TXN_SPILLS) { + np = NULL; + rc = mdb_page_unspill(txn, mp, &np); + if (rc) + goto fail; + if (np) + goto done; + } + if ((rc = mdb_midl_need(&txn->mt_free_pgs, 1)) || + (rc = mdb_page_alloc(mc, 1, &np))) + goto fail; + pgno = np->mp_pgno; + DPRINTF(("touched db %d page %"Z"u -> %"Z"u", DDBI(mc), + mp->mp_pgno, pgno)); + mdb_cassert(mc, mp->mp_pgno != pgno); + mdb_midl_xappend(txn->mt_free_pgs, mp->mp_pgno); + /* Update the parent page, if any, to point to the new page */ + if (mc->mc_top) { + MDB_page *parent = mc->mc_pg[mc->mc_top-1]; + MDB_node *node = NODEPTR(parent, mc->mc_ki[mc->mc_top-1]); + SETPGNO(node, pgno); + } else { + mc->mc_db->md_root = pgno; + } + } else if (txn->mt_parent && !IS_SUBP(mp)) { + MDB_ID2 mid, *dl = txn->mt_u.dirty_list; + pgno = mp->mp_pgno; + /* If txn has a parent, make sure the page is in our + * dirty list. + */ + if (dl[0].mid) { + unsigned x = mdb_mid2l_search(dl, pgno); + if (x <= dl[0].mid && dl[x].mid == pgno) { + if (mp != dl[x].mptr) { /* bad cursor? */ + mc->mc_flags &= ~(C_INITIALIZED|C_EOF); + txn->mt_flags |= MDB_TXN_ERROR; + return MDB_CORRUPTED; + } + return 0; + } + } + mdb_cassert(mc, dl[0].mid < MDB_IDL_UM_MAX); + /* No - copy it */ + np = mdb_page_malloc(txn, 1); + if (!np) + return ENOMEM; + mid.mid = pgno; + mid.mptr = np; + rc = mdb_mid2l_insert(dl, &mid); + mdb_cassert(mc, rc == 0); + } else { + return 0; + } + + mdb_page_copy(np, mp, txn->mt_env->me_psize); + np->mp_pgno = pgno; + np->mp_flags |= P_DIRTY; + +done: + /* Adjust cursors pointing to mp */ + mc->mc_pg[mc->mc_top] = np; + m2 = txn->mt_cursors[mc->mc_dbi]; + if (mc->mc_flags & C_SUB) { + for (; m2; m2=m2->mc_next) { + m3 = &m2->mc_xcursor->mx_cursor; + if (m3->mc_snum < mc->mc_snum) continue; + if (m3->mc_pg[mc->mc_top] == mp) + m3->mc_pg[mc->mc_top] = np; + } + } else { + for (; m2; m2=m2->mc_next) { + if (m2->mc_snum < mc->mc_snum) continue; + if (m2 == mc) continue; + if (m2->mc_pg[mc->mc_top] == mp) { + m2->mc_pg[mc->mc_top] = np; + if (IS_LEAF(np)) + XCURSOR_REFRESH(m2, mc->mc_top, np); + } + } + } + return 0; + +fail: + txn->mt_flags |= MDB_TXN_ERROR; + return rc; +} + +int +mdb_env_sync(MDB_env *env, int force) +{ + int rc = 0; + if (env->me_flags & MDB_RDONLY) + return EACCES; + if (force || !F_ISSET(env->me_flags, MDB_NOSYNC)) { + if (env->me_flags & MDB_WRITEMAP) { + int flags = ((env->me_flags & MDB_MAPASYNC) && !force) + ? MS_ASYNC : MS_SYNC; + if (MDB_MSYNC(env->me_map, env->me_mapsize, flags)) + rc = ErrCode(); +#ifdef _WIN32 + else if (flags == MS_SYNC && MDB_FDATASYNC(env->me_fd)) + rc = ErrCode(); +#endif + } else { +#ifdef BROKEN_FDATASYNC + if (env->me_flags & MDB_FSYNCONLY) { + if (fsync(env->me_fd)) + rc = ErrCode(); + } else +#endif + if (MDB_FDATASYNC(env->me_fd)) + rc = ErrCode(); + } + } + return rc; +} + +/** Back up parent txn's cursors, then grab the originals for tracking */ +static int +mdb_cursor_shadow(MDB_txn *src, MDB_txn *dst) +{ + MDB_cursor *mc, *bk; + MDB_xcursor *mx; + size_t size; + int i; + + for (i = src->mt_numdbs; --i >= 0; ) { + if ((mc = src->mt_cursors[i]) != NULL) { + size = sizeof(MDB_cursor); + if (mc->mc_xcursor) + size += sizeof(MDB_xcursor); + for (; mc; mc = bk->mc_next) { + bk = malloc(size); + if (!bk) + return ENOMEM; + *bk = *mc; + mc->mc_backup = bk; + mc->mc_db = &dst->mt_dbs[i]; + /* Kill pointers into src to reduce abuse: The + * user may not use mc until dst ends. But we need a valid + * txn pointer here for cursor fixups to keep working. + */ + mc->mc_txn = dst; + mc->mc_dbflag = &dst->mt_dbflags[i]; + if ((mx = mc->mc_xcursor) != NULL) { + *(MDB_xcursor *)(bk+1) = *mx; + mx->mx_cursor.mc_txn = dst; + } + mc->mc_next = dst->mt_cursors[i]; + dst->mt_cursors[i] = mc; + } + } + } + return MDB_SUCCESS; +} + +/** Close this write txn's cursors, give parent txn's cursors back to parent. + * @param[in] txn the transaction handle. + * @param[in] merge true to keep changes to parent cursors, false to revert. + * @return 0 on success, non-zero on failure. + */ +static void +mdb_cursors_close(MDB_txn *txn, unsigned merge) +{ + MDB_cursor **cursors = txn->mt_cursors, *mc, *next, *bk; + MDB_xcursor *mx; + int i; + + for (i = txn->mt_numdbs; --i >= 0; ) { + for (mc = cursors[i]; mc; mc = next) { + next = mc->mc_next; + if ((bk = mc->mc_backup) != NULL) { + if (merge) { + /* Commit changes to parent txn */ + mc->mc_next = bk->mc_next; + mc->mc_backup = bk->mc_backup; + mc->mc_txn = bk->mc_txn; + mc->mc_db = bk->mc_db; + mc->mc_dbflag = bk->mc_dbflag; + if ((mx = mc->mc_xcursor) != NULL) + mx->mx_cursor.mc_txn = bk->mc_txn; + } else { + /* Abort nested txn */ + *mc = *bk; + if ((mx = mc->mc_xcursor) != NULL) + *mx = *(MDB_xcursor *)(bk+1); + } + mc = bk; + } + /* Only malloced cursors are permanently tracked. */ + free(mc); + } + cursors[i] = NULL; + } +} + +#if !(MDB_PIDLOCK) /* Currently the same as defined(_WIN32) */ +enum Pidlock_op { + Pidset, Pidcheck +}; +#else +enum Pidlock_op { + Pidset = F_SETLK, Pidcheck = F_GETLK +}; +#endif + +/** Set or check a pid lock. Set returns 0 on success. + * Check returns 0 if the process is certainly dead, nonzero if it may + * be alive (the lock exists or an error happened so we do not know). + * + * On Windows Pidset is a no-op, we merely check for the existence + * of the process with the given pid. On POSIX we use a single byte + * lock on the lockfile, set at an offset equal to the pid. + */ +static int +mdb_reader_pid(MDB_env *env, enum Pidlock_op op, MDB_PID_T pid) +{ +#if !(MDB_PIDLOCK) /* Currently the same as defined(_WIN32) */ + int ret = 0; + HANDLE h; + if (op == Pidcheck) { + h = OpenProcess(env->me_pidquery, FALSE, pid); + /* No documented "no such process" code, but other program use this: */ + if (!h) + return ErrCode() != ERROR_INVALID_PARAMETER; + /* A process exists until all handles to it close. Has it exited? */ + ret = WaitForSingleObject(h, 0) != 0; + CloseHandle(h); + } + return ret; +#else + for (;;) { + int rc; + struct flock lock_info; + memset(&lock_info, 0, sizeof(lock_info)); + lock_info.l_type = F_WRLCK; + lock_info.l_whence = SEEK_SET; + lock_info.l_start = pid; + lock_info.l_len = 1; + if ((rc = fcntl(env->me_lfd, op, &lock_info)) == 0) { + if (op == F_GETLK && lock_info.l_type != F_UNLCK) + rc = -1; + } else if ((rc = ErrCode()) == EINTR) { + continue; + } + return rc; + } +#endif +} + +/** Common code for #mdb_txn_begin() and #mdb_txn_renew(). + * @param[in] txn the transaction handle to initialize + * @return 0 on success, non-zero on failure. + */ +static int +mdb_txn_renew0(MDB_txn *txn) +{ + MDB_env *env = txn->mt_env; + MDB_txninfo *ti = env->me_txns; + MDB_meta *meta; + unsigned int i, nr, flags = txn->mt_flags; + uint16_t x; + int rc, new_notls = 0; + + if ((flags &= MDB_TXN_RDONLY) != 0) { + if (!ti) { + meta = mdb_env_pick_meta(env); + txn->mt_txnid = meta->mm_txnid; + txn->mt_u.reader = NULL; + } else { + MDB_reader *r = (env->me_flags & MDB_NOTLS) ? txn->mt_u.reader : + pthread_getspecific(env->me_txkey); + if (r) { + if (r->mr_pid != env->me_pid || r->mr_txnid != (txnid_t)-1) + return MDB_BAD_RSLOT; + } else { + MDB_PID_T pid = env->me_pid; + MDB_THR_T tid = pthread_self(); + mdb_mutexref_t rmutex = env->me_rmutex; + + if (!env->me_live_reader) { + rc = mdb_reader_pid(env, Pidset, pid); + if (rc) + return rc; + env->me_live_reader = 1; + } + + if (LOCK_MUTEX(rc, env, rmutex)) + return rc; + nr = ti->mti_numreaders; + for (i=0; i<nr; i++) + if (ti->mti_readers[i].mr_pid == 0) + break; + if (i == env->me_maxreaders) { + UNLOCK_MUTEX(rmutex); + return MDB_READERS_FULL; + } + r = &ti->mti_readers[i]; + /* Claim the reader slot, carefully since other code + * uses the reader table un-mutexed: First reset the + * slot, next publish it in mti_numreaders. After + * that, it is safe for mdb_env_close() to touch it. + * When it will be closed, we can finally claim it. + */ + r->mr_pid = 0; + r->mr_txnid = (txnid_t)-1; + r->mr_tid = tid; + if (i == nr) + ti->mti_numreaders = ++nr; + env->me_close_readers = nr; + r->mr_pid = pid; + UNLOCK_MUTEX(rmutex); + + new_notls = (env->me_flags & MDB_NOTLS); + if (!new_notls && (rc=pthread_setspecific(env->me_txkey, r))) { + r->mr_pid = 0; + return rc; + } + } + do /* LY: Retry on a race, ITS#7970. */ + r->mr_txnid = ti->mti_txnid; + while(r->mr_txnid != ti->mti_txnid); + txn->mt_txnid = r->mr_txnid; + txn->mt_u.reader = r; + meta = env->me_metas[txn->mt_txnid & 1]; + } + + } else { + /* Not yet touching txn == env->me_txn0, it may be active */ + if (ti) { + if (LOCK_MUTEX(rc, env, env->me_wmutex)) + return rc; + txn->mt_txnid = ti->mti_txnid; + meta = env->me_metas[txn->mt_txnid & 1]; + } else { + meta = mdb_env_pick_meta(env); + txn->mt_txnid = meta->mm_txnid; + } + txn->mt_txnid++; +#if MDB_DEBUG + if (txn->mt_txnid == mdb_debug_start) + mdb_debug = 1; +#endif + txn->mt_child = NULL; + txn->mt_loose_pgs = NULL; + txn->mt_loose_count = 0; + txn->mt_dirty_room = MDB_IDL_UM_MAX; + txn->mt_u.dirty_list = env->me_dirty_list; + txn->mt_u.dirty_list[0].mid = 0; + txn->mt_free_pgs = env->me_free_pgs; + txn->mt_free_pgs[0] = 0; + txn->mt_spill_pgs = NULL; + env->me_txn = txn; + memcpy(txn->mt_dbiseqs, env->me_dbiseqs, env->me_maxdbs * sizeof(unsigned int)); + } + + /* Copy the DB info and flags */ + memcpy(txn->mt_dbs, meta->mm_dbs, CORE_DBS * sizeof(MDB_db)); + + /* Moved to here to avoid a data race in read TXNs */ + txn->mt_next_pgno = meta->mm_last_pg+1; + + txn->mt_flags = flags; + + /* Setup db info */ + txn->mt_numdbs = env->me_numdbs; + for (i=CORE_DBS; i<txn->mt_numdbs; i++) { + x = env->me_dbflags[i]; + txn->mt_dbs[i].md_flags = x & PERSISTENT_FLAGS; + txn->mt_dbflags[i] = (x & MDB_VALID) ? DB_VALID|DB_USRVALID|DB_STALE : 0; + } + txn->mt_dbflags[MAIN_DBI] = DB_VALID|DB_USRVALID; + txn->mt_dbflags[FREE_DBI] = DB_VALID; + + if (env->me_flags & MDB_FATAL_ERROR) { + DPUTS("environment had fatal error, must shutdown!"); + rc = MDB_PANIC; + } else if (env->me_maxpg < txn->mt_next_pgno) { + rc = MDB_MAP_RESIZED; + } else { + return MDB_SUCCESS; + } + mdb_txn_end(txn, new_notls /*0 or MDB_END_SLOT*/ | MDB_END_FAIL_BEGIN); + return rc; +} + +int +mdb_txn_renew(MDB_txn *txn) +{ + int rc; + + if (!txn || !F_ISSET(txn->mt_flags, MDB_TXN_RDONLY|MDB_TXN_FINISHED)) + return EINVAL; + + rc = mdb_txn_renew0(txn); + if (rc == MDB_SUCCESS) { + DPRINTF(("renew txn %"Z"u%c %p on mdbenv %p, root page %"Z"u", + txn->mt_txnid, (txn->mt_flags & MDB_TXN_RDONLY) ? 'r' : 'w', + (void *)txn, (void *)txn->mt_env, txn->mt_dbs[MAIN_DBI].md_root)); + } + return rc; +} + +int +mdb_txn_begin(MDB_env *env, MDB_txn *parent, unsigned int flags, MDB_txn **ret) +{ + MDB_txn *txn; + MDB_ntxn *ntxn; + int rc, size, tsize; + + flags &= MDB_TXN_BEGIN_FLAGS; + flags |= env->me_flags & MDB_WRITEMAP; + + if (env->me_flags & MDB_RDONLY & ~flags) /* write txn in RDONLY env */ + return EACCES; + + if (parent) { + /* Nested transactions: Max 1 child, write txns only, no writemap */ + flags |= parent->mt_flags; + if (flags & (MDB_RDONLY|MDB_WRITEMAP|MDB_TXN_BLOCKED)) { + return (parent->mt_flags & MDB_TXN_RDONLY) ? EINVAL : MDB_BAD_TXN; + } + /* Child txns save MDB_pgstate and use own copy of cursors */ + size = env->me_maxdbs * (sizeof(MDB_db)+sizeof(MDB_cursor *)+1); + size += tsize = sizeof(MDB_ntxn); + } else if (flags & MDB_RDONLY) { + size = env->me_maxdbs * (sizeof(MDB_db)+1); + size += tsize = sizeof(MDB_txn); + } else { + /* Reuse preallocated write txn. However, do not touch it until + * mdb_txn_renew0() succeeds, since it currently may be active. + */ + txn = env->me_txn0; + goto renew; + } + if ((txn = calloc(1, size)) == NULL) { + DPRINTF(("calloc: %s", strerror(errno))); + return ENOMEM; + } + txn->mt_dbxs = env->me_dbxs; /* static */ + txn->mt_dbs = (MDB_db *) ((char *)txn + tsize); + txn->mt_dbflags = (unsigned char *)txn + size - env->me_maxdbs; + txn->mt_flags = flags; + txn->mt_env = env; + + if (parent) { + unsigned int i; + txn->mt_cursors = (MDB_cursor **)(txn->mt_dbs + env->me_maxdbs); + txn->mt_dbiseqs = parent->mt_dbiseqs; + txn->mt_u.dirty_list = malloc(sizeof(MDB_ID2)*MDB_IDL_UM_SIZE); + if (!txn->mt_u.dirty_list || + !(txn->mt_free_pgs = mdb_midl_alloc(MDB_IDL_UM_MAX))) + { + free(txn->mt_u.dirty_list); + free(txn); + return ENOMEM; + } + txn->mt_txnid = parent->mt_txnid; + txn->mt_dirty_room = parent->mt_dirty_room; + txn->mt_u.dirty_list[0].mid = 0; + txn->mt_spill_pgs = NULL; + txn->mt_next_pgno = parent->mt_next_pgno; + parent->mt_flags |= MDB_TXN_HAS_CHILD; + parent->mt_child = txn; + txn->mt_parent = parent; + txn->mt_numdbs = parent->mt_numdbs; + memcpy(txn->mt_dbs, parent->mt_dbs, txn->mt_numdbs * sizeof(MDB_db)); + /* Copy parent's mt_dbflags, but clear DB_NEW */ + for (i=0; i<txn->mt_numdbs; i++) + txn->mt_dbflags[i] = parent->mt_dbflags[i] & ~DB_NEW; + rc = 0; + ntxn = (MDB_ntxn *)txn; + ntxn->mnt_pgstate = env->me_pgstate; /* save parent me_pghead & co */ + if (env->me_pghead) { + size = MDB_IDL_SIZEOF(env->me_pghead); + env->me_pghead = mdb_midl_alloc(env->me_pghead[0]); + if (env->me_pghead) + memcpy(env->me_pghead, ntxn->mnt_pgstate.mf_pghead, size); + else + rc = ENOMEM; + } + if (!rc) + rc = mdb_cursor_shadow(parent, txn); + if (rc) + mdb_txn_end(txn, MDB_END_FAIL_BEGINCHILD); + } else { /* MDB_RDONLY */ + txn->mt_dbiseqs = env->me_dbiseqs; +renew: + rc = mdb_txn_renew0(txn); + } + if (rc) { + if (txn != env->me_txn0) + free(txn); + } else { + txn->mt_flags |= flags; /* could not change txn=me_txn0 earlier */ + *ret = txn; + DPRINTF(("begin txn %"Z"u%c %p on mdbenv %p, root page %"Z"u", + txn->mt_txnid, (flags & MDB_RDONLY) ? 'r' : 'w', + (void *) txn, (void *) env, txn->mt_dbs[MAIN_DBI].md_root)); + } + + return rc; +} + +MDB_env * +mdb_txn_env(MDB_txn *txn) +{ + if(!txn) return NULL; + return txn->mt_env; +} + +size_t +mdb_txn_id(MDB_txn *txn) +{ + if(!txn) return 0; + return txn->mt_txnid; +} + +/** Export or close DBI handles opened in this txn. */ +static void +mdb_dbis_update(MDB_txn *txn, int keep) +{ + int i; + MDB_dbi n = txn->mt_numdbs; + MDB_env *env = txn->mt_env; + unsigned char *tdbflags = txn->mt_dbflags; + + for (i = n; --i >= CORE_DBS;) { + if (tdbflags[i] & DB_NEW) { + if (keep) { + env->me_dbflags[i] = txn->mt_dbs[i].md_flags | MDB_VALID; + } else { + char *ptr = env->me_dbxs[i].md_name.mv_data; + if (ptr) { + env->me_dbxs[i].md_name.mv_data = NULL; + env->me_dbxs[i].md_name.mv_size = 0; + env->me_dbflags[i] = 0; + env->me_dbiseqs[i]++; + free(ptr); + } + } + } + } + if (keep && env->me_numdbs < n) + env->me_numdbs = n; +} + +/** End a transaction, except successful commit of a nested transaction. + * May be called twice for readonly txns: First reset it, then abort. + * @param[in] txn the transaction handle to end + * @param[in] mode why and how to end the transaction + */ +static void +mdb_txn_end(MDB_txn *txn, unsigned mode) +{ + MDB_env *env = txn->mt_env; +#if MDB_DEBUG + static const char *const names[] = MDB_END_NAMES; +#endif + + /* Export or close DBI handles opened in this txn */ + mdb_dbis_update(txn, mode & MDB_END_UPDATE); + + DPRINTF(("%s txn %"Z"u%c %p on mdbenv %p, root page %"Z"u", + names[mode & MDB_END_OPMASK], + txn->mt_txnid, (txn->mt_flags & MDB_TXN_RDONLY) ? 'r' : 'w', + (void *) txn, (void *)env, txn->mt_dbs[MAIN_DBI].md_root)); + + if (F_ISSET(txn->mt_flags, MDB_TXN_RDONLY)) { + if (txn->mt_u.reader) { + txn->mt_u.reader->mr_txnid = (txnid_t)-1; + if (!(env->me_flags & MDB_NOTLS)) { + txn->mt_u.reader = NULL; /* txn does not own reader */ + } else if (mode & MDB_END_SLOT) { + txn->mt_u.reader->mr_pid = 0; + txn->mt_u.reader = NULL; + } /* else txn owns the slot until it does MDB_END_SLOT */ + } + txn->mt_numdbs = 0; /* prevent further DBI activity */ + txn->mt_flags |= MDB_TXN_FINISHED; + + } else if (!F_ISSET(txn->mt_flags, MDB_TXN_FINISHED)) { + pgno_t *pghead = env->me_pghead; + + if (!(mode & MDB_END_UPDATE)) /* !(already closed cursors) */ + mdb_cursors_close(txn, 0); + if (!(env->me_flags & MDB_WRITEMAP)) { + mdb_dlist_free(txn); + } + + txn->mt_numdbs = 0; + txn->mt_flags = MDB_TXN_FINISHED; + + if (!txn->mt_parent) { + mdb_midl_shrink(&txn->mt_free_pgs); + env->me_free_pgs = txn->mt_free_pgs; + /* me_pgstate: */ + env->me_pghead = NULL; + env->me_pglast = 0; + + env->me_txn = NULL; + mode = 0; /* txn == env->me_txn0, do not free() it */ + + /* The writer mutex was locked in mdb_txn_begin. */ + if (env->me_txns) + UNLOCK_MUTEX(env->me_wmutex); + } else { + txn->mt_parent->mt_child = NULL; + txn->mt_parent->mt_flags &= ~MDB_TXN_HAS_CHILD; + env->me_pgstate = ((MDB_ntxn *)txn)->mnt_pgstate; + mdb_midl_free(txn->mt_free_pgs); + free(txn->mt_u.dirty_list); + } + mdb_midl_free(txn->mt_spill_pgs); + + mdb_midl_free(pghead); + } + + if (mode & MDB_END_FREE) + free(txn); +} + +void +mdb_txn_reset(MDB_txn *txn) +{ + if (txn == NULL) + return; + + /* This call is only valid for read-only txns */ + if (!(txn->mt_flags & MDB_TXN_RDONLY)) + return; + + mdb_txn_end(txn, MDB_END_RESET); +} + +void +mdb_txn_abort(MDB_txn *txn) +{ + if (txn == NULL) + return; + + if (txn->mt_child) + mdb_txn_abort(txn->mt_child); + + mdb_txn_end(txn, MDB_END_ABORT|MDB_END_SLOT|MDB_END_FREE); +} + +/** Save the freelist as of this transaction to the freeDB. + * This changes the freelist. Keep trying until it stabilizes. + */ +static int +mdb_freelist_save(MDB_txn *txn) +{ + /* env->me_pghead[] can grow and shrink during this call. + * env->me_pglast and txn->mt_free_pgs[] can only grow. + * Page numbers cannot disappear from txn->mt_free_pgs[]. + */ + MDB_cursor mc; + MDB_env *env = txn->mt_env; + int rc, maxfree_1pg = env->me_maxfree_1pg, more = 1; + txnid_t pglast = 0, head_id = 0; + pgno_t freecnt = 0, *free_pgs, *mop; + ssize_t head_room = 0, total_room = 0, mop_len, clean_limit; + + mdb_cursor_init(&mc, txn, FREE_DBI, NULL); + + if (env->me_pghead) { + /* Make sure first page of freeDB is touched and on freelist */ + rc = mdb_page_search(&mc, NULL, MDB_PS_FIRST|MDB_PS_MODIFY); + if (rc && rc != MDB_NOTFOUND) + return rc; + } + + if (!env->me_pghead && txn->mt_loose_pgs) { + /* Put loose page numbers in mt_free_pgs, since + * we may be unable to return them to me_pghead. + */ + MDB_page *mp = txn->mt_loose_pgs; + MDB_ID2 *dl = txn->mt_u.dirty_list; + unsigned x; + if ((rc = mdb_midl_need(&txn->mt_free_pgs, txn->mt_loose_count)) != 0) + return rc; + for (; mp; mp = NEXT_LOOSE_PAGE(mp)) { + mdb_midl_xappend(txn->mt_free_pgs, mp->mp_pgno); + /* must also remove from dirty list */ + if (txn->mt_flags & MDB_TXN_WRITEMAP) { + for (x=1; x<=dl[0].mid; x++) + if (dl[x].mid == mp->mp_pgno) + break; + mdb_tassert(txn, x <= dl[0].mid); + } else { + x = mdb_mid2l_search(dl, mp->mp_pgno); + mdb_tassert(txn, dl[x].mid == mp->mp_pgno); + mdb_dpage_free(env, mp); + } + dl[x].mptr = NULL; + } + { + /* squash freed slots out of the dirty list */ + unsigned y; + for (y=1; dl[y].mptr && y <= dl[0].mid; y++); + if (y <= dl[0].mid) { + for(x=y, y++;;) { + while (!dl[y].mptr && y <= dl[0].mid) y++; + if (y > dl[0].mid) break; + dl[x++] = dl[y++]; + } + dl[0].mid = x-1; + } else { + /* all slots freed */ + dl[0].mid = 0; + } + } + txn->mt_loose_pgs = NULL; + txn->mt_loose_count = 0; + } + + /* MDB_RESERVE cancels meminit in ovpage malloc (when no WRITEMAP) */ + clean_limit = (env->me_flags & (MDB_NOMEMINIT|MDB_WRITEMAP)) + ? SSIZE_MAX : maxfree_1pg; + + for (;;) { + /* Come back here after each Put() in case freelist changed */ + MDB_val key, data; + pgno_t *pgs; + ssize_t j; + + /* If using records from freeDB which we have not yet + * deleted, delete them and any we reserved for me_pghead. + */ + while (pglast < env->me_pglast) { + rc = mdb_cursor_first(&mc, &key, NULL); + if (rc) + return rc; + pglast = head_id = *(txnid_t *)key.mv_data; + total_room = head_room = 0; + mdb_tassert(txn, pglast <= env->me_pglast); + rc = mdb_cursor_del(&mc, 0); + if (rc) + return rc; + } + + /* Save the IDL of pages freed by this txn, to a single record */ + if (freecnt < txn->mt_free_pgs[0]) { + if (!freecnt) { + /* Make sure last page of freeDB is touched and on freelist */ + rc = mdb_page_search(&mc, NULL, MDB_PS_LAST|MDB_PS_MODIFY); + if (rc && rc != MDB_NOTFOUND) + return rc; + } + free_pgs = txn->mt_free_pgs; + /* Write to last page of freeDB */ + key.mv_size = sizeof(txn->mt_txnid); + key.mv_data = &txn->mt_txnid; + do { + freecnt = free_pgs[0]; + data.mv_size = MDB_IDL_SIZEOF(free_pgs); + rc = mdb_cursor_put(&mc, &key, &data, MDB_RESERVE); + if (rc) + return rc; + /* Retry if mt_free_pgs[] grew during the Put() */ + free_pgs = txn->mt_free_pgs; + } while (freecnt < free_pgs[0]); + mdb_midl_sort(free_pgs); + memcpy(data.mv_data, free_pgs, data.mv_size); +#if (MDB_DEBUG) > 1 + { + unsigned int i = free_pgs[0]; + DPRINTF(("IDL write txn %"Z"u root %"Z"u num %u", + txn->mt_txnid, txn->mt_dbs[FREE_DBI].md_root, i)); + for (; i; i--) + DPRINTF(("IDL %"Z"u", free_pgs[i])); + } +#endif + continue; + } + + mop = env->me_pghead; + mop_len = (mop ? mop[0] : 0) + txn->mt_loose_count; + + /* Reserve records for me_pghead[]. Split it if multi-page, + * to avoid searching freeDB for a page range. Use keys in + * range [1,me_pglast]: Smaller than txnid of oldest reader. + */ + if (total_room >= mop_len) { + if (total_room == mop_len || --more < 0) + break; + } else if (head_room >= maxfree_1pg && head_id > 1) { + /* Keep current record (overflow page), add a new one */ + head_id--; + head_room = 0; + } + /* (Re)write {key = head_id, IDL length = head_room} */ + total_room -= head_room; + head_room = mop_len - total_room; + if (head_room > maxfree_1pg && head_id > 1) { + /* Overflow multi-page for part of me_pghead */ + head_room /= head_id; /* amortize page sizes */ + head_room += maxfree_1pg - head_room % (maxfree_1pg + 1); + } else if (head_room < 0) { + /* Rare case, not bothering to delete this record */ + head_room = 0; + } + key.mv_size = sizeof(head_id); + key.mv_data = &head_id; + data.mv_size = (head_room + 1) * sizeof(pgno_t); + rc = mdb_cursor_put(&mc, &key, &data, MDB_RESERVE); + if (rc) + return rc; + /* IDL is initially empty, zero out at least the length */ + pgs = (pgno_t *)data.mv_data; + j = head_room > clean_limit ? head_room : 0; + do { + pgs[j] = 0; + } while (--j >= 0); + total_room += head_room; + } + + /* Return loose page numbers to me_pghead, though usually none are + * left at this point. The pages themselves remain in dirty_list. + */ + if (txn->mt_loose_pgs) { + MDB_page *mp = txn->mt_loose_pgs; + unsigned count = txn->mt_loose_count; + MDB_IDL loose; + /* Room for loose pages + temp IDL with same */ + if ((rc = mdb_midl_need(&env->me_pghead, 2*count+1)) != 0) + return rc; + mop = env->me_pghead; + loose = mop + MDB_IDL_ALLOCLEN(mop) - count; + for (count = 0; mp; mp = NEXT_LOOSE_PAGE(mp)) + loose[ ++count ] = mp->mp_pgno; + loose[0] = count; + mdb_midl_sort(loose); + mdb_midl_xmerge(mop, loose); + txn->mt_loose_pgs = NULL; + txn->mt_loose_count = 0; + mop_len = mop[0]; + } + + /* Fill in the reserved me_pghead records */ + rc = MDB_SUCCESS; + if (mop_len) { + MDB_val key, data; + + mop += mop_len; + rc = mdb_cursor_first(&mc, &key, &data); + for (; !rc; rc = mdb_cursor_next(&mc, &key, &data, MDB_NEXT)) { + txnid_t id = *(txnid_t *)key.mv_data; + ssize_t len = (ssize_t)(data.mv_size / sizeof(MDB_ID)) - 1; + MDB_ID save; + + mdb_tassert(txn, len >= 0 && id <= env->me_pglast); + key.mv_data = &id; + if (len > mop_len) { + len = mop_len; + data.mv_size = (len + 1) * sizeof(MDB_ID); + } + data.mv_data = mop -= len; + save = mop[0]; + mop[0] = len; + rc = mdb_cursor_put(&mc, &key, &data, MDB_CURRENT); + mop[0] = save; + if (rc || !(mop_len -= len)) + break; + } + } + return rc; +} + +/** Flush (some) dirty pages to the map, after clearing their dirty flag. + * @param[in] txn the transaction that's being committed + * @param[in] keep number of initial pages in dirty_list to keep dirty. + * @return 0 on success, non-zero on failure. + */ +static int +mdb_page_flush(MDB_txn *txn, int keep) +{ + MDB_env *env = txn->mt_env; + MDB_ID2L dl = txn->mt_u.dirty_list; + unsigned psize = env->me_psize, j; + int i, pagecount = dl[0].mid, rc; + size_t size = 0, pos = 0; + pgno_t pgno = 0; + MDB_page *dp = NULL; +#ifdef _WIN32 + OVERLAPPED ov; +#else + struct iovec iov[MDB_COMMIT_PAGES]; + ssize_t wpos = 0, wsize = 0, wres; + size_t next_pos = 1; /* impossible pos, so pos != next_pos */ + int n = 0; +#endif + + j = i = keep; + + if (env->me_flags & MDB_WRITEMAP) { + /* Clear dirty flags */ + while (++i <= pagecount) { + dp = dl[i].mptr; + /* Don't flush this page yet */ + if (dp->mp_flags & (P_LOOSE|P_KEEP)) { + dp->mp_flags &= ~P_KEEP; + dl[++j] = dl[i]; + continue; + } + dp->mp_flags &= ~P_DIRTY; + } + goto done; + } + + /* Write the pages */ + for (;;) { + if (++i <= pagecount) { + dp = dl[i].mptr; + /* Don't flush this page yet */ + if (dp->mp_flags & (P_LOOSE|P_KEEP)) { + dp->mp_flags &= ~P_KEEP; + dl[i].mid = 0; + continue; + } + pgno = dl[i].mid; + /* clear dirty flag */ + dp->mp_flags &= ~P_DIRTY; + pos = pgno * psize; + size = psize; + if (IS_OVERFLOW(dp)) size *= dp->mp_pages; + } +#ifdef _WIN32 + else break; + + /* Windows actually supports scatter/gather I/O, but only on + * unbuffered file handles. Since we're relying on the OS page + * cache for all our data, that's self-defeating. So we just + * write pages one at a time. We use the ov structure to set + * the write offset, to at least save the overhead of a Seek + * system call. + */ + DPRINTF(("committing page %"Z"u", pgno)); + memset(&ov, 0, sizeof(ov)); + ov.Offset = pos & 0xffffffff; + ov.OffsetHigh = pos >> 16 >> 16; + if (!WriteFile(env->me_fd, dp, size, NULL, &ov)) { + rc = ErrCode(); + DPRINTF(("WriteFile: %d", rc)); + return rc; + } +#else + /* Write up to MDB_COMMIT_PAGES dirty pages at a time. */ + if (pos!=next_pos || n==MDB_COMMIT_PAGES || wsize+size>MAX_WRITE) { + if (n) { +retry_write: + /* Write previous page(s) */ +#ifdef MDB_USE_PWRITEV + wres = pwritev(env->me_fd, iov, n, wpos); +#else + if (n == 1) { + wres = pwrite(env->me_fd, iov[0].iov_base, wsize, wpos); + } else { +retry_seek: + if (lseek(env->me_fd, wpos, SEEK_SET) == -1) { + rc = ErrCode(); + if (rc == EINTR) + goto retry_seek; + DPRINTF(("lseek: %s", strerror(rc))); + return rc; + } + wres = writev(env->me_fd, iov, n); + } +#endif + if (wres != wsize) { + if (wres < 0) { + rc = ErrCode(); + if (rc == EINTR) + goto retry_write; + DPRINTF(("Write error: %s", strerror(rc))); + } else { + rc = EIO; /* TODO: Use which error code? */ + DPUTS("short write, filesystem full?"); + } + return rc; + } + n = 0; + } + if (i > pagecount) + break; + wpos = pos; + wsize = 0; + } + DPRINTF(("committing page %"Z"u", pgno)); + next_pos = pos + size; + iov[n].iov_len = size; + iov[n].iov_base = (char *)dp; + wsize += size; + n++; +#endif /* _WIN32 */ + } + + /* MIPS has cache coherency issues, this is a no-op everywhere else + * Note: for any size >= on-chip cache size, entire on-chip cache is + * flushed. + */ + CACHEFLUSH(env->me_map, txn->mt_next_pgno * env->me_psize, DCACHE); + + for (i = keep; ++i <= pagecount; ) { + dp = dl[i].mptr; + /* This is a page we skipped above */ + if (!dl[i].mid) { + dl[++j] = dl[i]; + dl[j].mid = dp->mp_pgno; + continue; + } + mdb_dpage_free(env, dp); + } + +done: + i--; + txn->mt_dirty_room += i - j; + dl[0].mid = j; + return MDB_SUCCESS; +} + +int +mdb_txn_commit(MDB_txn *txn) +{ + int rc; + unsigned int i, end_mode; + MDB_env *env; + + if (txn == NULL) + return EINVAL; + + /* mdb_txn_end() mode for a commit which writes nothing */ + end_mode = MDB_END_EMPTY_COMMIT|MDB_END_UPDATE|MDB_END_SLOT|MDB_END_FREE; + + if (txn->mt_child) { + rc = mdb_txn_commit(txn->mt_child); + if (rc) + goto fail; + } + + env = txn->mt_env; + + if (F_ISSET(txn->mt_flags, MDB_TXN_RDONLY)) { + goto done; + } + + if (txn->mt_flags & (MDB_TXN_FINISHED|MDB_TXN_ERROR)) { + DPUTS("txn has failed/finished, can't commit"); + if (txn->mt_parent) + txn->mt_parent->mt_flags |= MDB_TXN_ERROR; + rc = MDB_BAD_TXN; + goto fail; + } + + if (txn->mt_parent) { + MDB_txn *parent = txn->mt_parent; + MDB_page **lp; + MDB_ID2L dst, src; + MDB_IDL pspill; + unsigned x, y, len, ps_len; + + /* Append our free list to parent's */ + rc = mdb_midl_append_list(&parent->mt_free_pgs, txn->mt_free_pgs); + if (rc) + goto fail; + mdb_midl_free(txn->mt_free_pgs); + /* Failures after this must either undo the changes + * to the parent or set MDB_TXN_ERROR in the parent. + */ + + parent->mt_next_pgno = txn->mt_next_pgno; + parent->mt_flags = txn->mt_flags; + + /* Merge our cursors into parent's and close them */ + mdb_cursors_close(txn, 1); + + /* Update parent's DB table. */ + memcpy(parent->mt_dbs, txn->mt_dbs, txn->mt_numdbs * sizeof(MDB_db)); + parent->mt_numdbs = txn->mt_numdbs; + parent->mt_dbflags[FREE_DBI] = txn->mt_dbflags[FREE_DBI]; + parent->mt_dbflags[MAIN_DBI] = txn->mt_dbflags[MAIN_DBI]; + for (i=CORE_DBS; i<txn->mt_numdbs; i++) { + /* preserve parent's DB_NEW status */ + x = parent->mt_dbflags[i] & DB_NEW; + parent->mt_dbflags[i] = txn->mt_dbflags[i] | x; + } + + dst = parent->mt_u.dirty_list; + src = txn->mt_u.dirty_list; + /* Remove anything in our dirty list from parent's spill list */ + if ((pspill = parent->mt_spill_pgs) && (ps_len = pspill[0])) { + x = y = ps_len; + pspill[0] = (pgno_t)-1; + /* Mark our dirty pages as deleted in parent spill list */ + for (i=0, len=src[0].mid; ++i <= len; ) { + MDB_ID pn = src[i].mid << 1; + while (pn > pspill[x]) + x--; + if (pn == pspill[x]) { + pspill[x] = 1; + y = --x; + } + } + /* Squash deleted pagenums if we deleted any */ + for (x=y; ++x <= ps_len; ) + if (!(pspill[x] & 1)) + pspill[++y] = pspill[x]; + pspill[0] = y; + } + + /* Remove anything in our spill list from parent's dirty list */ + if (txn->mt_spill_pgs && txn->mt_spill_pgs[0]) { + for (i=1; i<=txn->mt_spill_pgs[0]; i++) { + MDB_ID pn = txn->mt_spill_pgs[i]; + if (pn & 1) + continue; /* deleted spillpg */ + pn >>= 1; + y = mdb_mid2l_search(dst, pn); + if (y <= dst[0].mid && dst[y].mid == pn) { + free(dst[y].mptr); + while (y < dst[0].mid) { + dst[y] = dst[y+1]; + y++; + } + dst[0].mid--; + } + } + } + + /* Find len = length of merging our dirty list with parent's */ + x = dst[0].mid; + dst[0].mid = 0; /* simplify loops */ + if (parent->mt_parent) { + len = x + src[0].mid; + y = mdb_mid2l_search(src, dst[x].mid + 1) - 1; + for (i = x; y && i; y--) { + pgno_t yp = src[y].mid; + while (yp < dst[i].mid) + i--; + if (yp == dst[i].mid) { + i--; + len--; + } + } + } else { /* Simplify the above for single-ancestor case */ + len = MDB_IDL_UM_MAX - txn->mt_dirty_room; + } + /* Merge our dirty list with parent's */ + y = src[0].mid; + for (i = len; y; dst[i--] = src[y--]) { + pgno_t yp = src[y].mid; + while (yp < dst[x].mid) + dst[i--] = dst[x--]; + if (yp == dst[x].mid) + free(dst[x--].mptr); + } + mdb_tassert(txn, i == x); + dst[0].mid = len; + free(txn->mt_u.dirty_list); + parent->mt_dirty_room = txn->mt_dirty_room; + if (txn->mt_spill_pgs) { + if (parent->mt_spill_pgs) { + /* TODO: Prevent failure here, so parent does not fail */ + rc = mdb_midl_append_list(&parent->mt_spill_pgs, txn->mt_spill_pgs); + if (rc) + parent->mt_flags |= MDB_TXN_ERROR; + mdb_midl_free(txn->mt_spill_pgs); + mdb_midl_sort(parent->mt_spill_pgs); + } else { + parent->mt_spill_pgs = txn->mt_spill_pgs; + } + } + + /* Append our loose page list to parent's */ + for (lp = &parent->mt_loose_pgs; *lp; lp = &NEXT_LOOSE_PAGE(*lp)) + ; + *lp = txn->mt_loose_pgs; + parent->mt_loose_count += txn->mt_loose_count; + + parent->mt_child = NULL; + mdb_midl_free(((MDB_ntxn *)txn)->mnt_pgstate.mf_pghead); + free(txn); + return rc; + } + + if (txn != env->me_txn) { + DPUTS("attempt to commit unknown transaction"); + rc = EINVAL; + goto fail; + } + + mdb_cursors_close(txn, 0); + + if (!txn->mt_u.dirty_list[0].mid && + !(txn->mt_flags & (MDB_TXN_DIRTY|MDB_TXN_SPILLS))) + goto done; + + DPRINTF(("committing txn %"Z"u %p on mdbenv %p, root page %"Z"u", + txn->mt_txnid, (void*)txn, (void*)env, txn->mt_dbs[MAIN_DBI].md_root)); + + /* Update DB root pointers */ + if (txn->mt_numdbs > CORE_DBS) { + MDB_cursor mc; + MDB_dbi i; + MDB_val data; + data.mv_size = sizeof(MDB_db); + + mdb_cursor_init(&mc, txn, MAIN_DBI, NULL); + for (i = CORE_DBS; i < txn->mt_numdbs; i++) { + if (txn->mt_dbflags[i] & DB_DIRTY) { + if (TXN_DBI_CHANGED(txn, i)) { + rc = MDB_BAD_DBI; + goto fail; + } + data.mv_data = &txn->mt_dbs[i]; + rc = mdb_cursor_put(&mc, &txn->mt_dbxs[i].md_name, &data, + F_SUBDATA); + if (rc) + goto fail; + } + } + } + + rc = mdb_freelist_save(txn); + if (rc) + goto fail; + + mdb_midl_free(env->me_pghead); + env->me_pghead = NULL; + mdb_midl_shrink(&txn->mt_free_pgs); + +#if (MDB_DEBUG) > 2 + mdb_audit(txn); +#endif + + if ((rc = mdb_page_flush(txn, 0)) || + (rc = mdb_env_sync(env, 0)) || + (rc = mdb_env_write_meta(txn))) + goto fail; + end_mode = MDB_END_COMMITTED|MDB_END_UPDATE; + +done: + mdb_txn_end(txn, end_mode); + return MDB_SUCCESS; + +fail: + mdb_txn_abort(txn); + return rc; +} + +/** Read the environment parameters of a DB environment before + * mapping it into memory. + * @param[in] env the environment handle + * @param[out] meta address of where to store the meta information + * @return 0 on success, non-zero on failure. + */ +static int ESECT +mdb_env_read_header(MDB_env *env, MDB_meta *meta) +{ + MDB_metabuf pbuf; + MDB_page *p; + MDB_meta *m; + int i, rc, off; + enum { Size = sizeof(pbuf) }; + + /* We don't know the page size yet, so use a minimum value. + * Read both meta pages so we can use the latest one. + */ + + for (i=off=0; i<NUM_METAS; i++, off += meta->mm_psize) { +#ifdef _WIN32 + DWORD len; + OVERLAPPED ov; + memset(&ov, 0, sizeof(ov)); + ov.Offset = off; + rc = ReadFile(env->me_fd, &pbuf, Size, &len, &ov) ? (int)len : -1; + if (rc == -1 && ErrCode() == ERROR_HANDLE_EOF) + rc = 0; +#else + rc = pread(env->me_fd, &pbuf, Size, off); +#endif + if (rc != Size) { + if (rc == 0 && off == 0) + return ENOENT; + rc = rc < 0 ? (int) ErrCode() : MDB_INVALID; + DPRINTF(("read: %s", mdb_strerror(rc))); + return rc; + } + + p = (MDB_page *)&pbuf; + + if (!F_ISSET(p->mp_flags, P_META)) { + DPRINTF(("page %"Z"u not a meta page", p->mp_pgno)); + return MDB_INVALID; + } + + m = METADATA(p); + if (m->mm_magic != MDB_MAGIC) { + DPUTS("meta has invalid magic"); + return MDB_INVALID; + } + + if (m->mm_version != MDB_DATA_VERSION) { + DPRINTF(("database is version %u, expected version %u", + m->mm_version, MDB_DATA_VERSION)); + return MDB_VERSION_MISMATCH; + } + + if (off == 0 || m->mm_txnid > meta->mm_txnid) + *meta = *m; + } + return 0; +} + +/** Fill in most of the zeroed #MDB_meta for an empty database environment */ +static void ESECT +mdb_env_init_meta0(MDB_env *env, MDB_meta *meta) +{ + meta->mm_magic = MDB_MAGIC; + meta->mm_version = MDB_DATA_VERSION; + meta->mm_mapsize = env->me_mapsize; + meta->mm_psize = env->me_psize; + meta->mm_last_pg = NUM_METAS-1; + meta->mm_flags = env->me_flags & 0xffff; + meta->mm_flags |= MDB_INTEGERKEY; /* this is mm_dbs[FREE_DBI].md_flags */ + meta->mm_dbs[FREE_DBI].md_root = P_INVALID; + meta->mm_dbs[MAIN_DBI].md_root = P_INVALID; +} + +/** Write the environment parameters of a freshly created DB environment. + * @param[in] env the environment handle + * @param[in] meta the #MDB_meta to write + * @return 0 on success, non-zero on failure. + */ +static int ESECT +mdb_env_init_meta(MDB_env *env, MDB_meta *meta) +{ + MDB_page *p, *q; + int rc; + unsigned int psize; +#ifdef _WIN32 + DWORD len; + OVERLAPPED ov; + memset(&ov, 0, sizeof(ov)); +#define DO_PWRITE(rc, fd, ptr, size, len, pos) do { \ + ov.Offset = pos; \ + rc = WriteFile(fd, ptr, size, &len, &ov); } while(0) +#else + int len; +#define DO_PWRITE(rc, fd, ptr, size, len, pos) do { \ + len = pwrite(fd, ptr, size, pos); \ + if (len == -1 && ErrCode() == EINTR) continue; \ + rc = (len >= 0); break; } while(1) +#endif + + DPUTS("writing new meta page"); + + psize = env->me_psize; + + p = calloc(NUM_METAS, psize); + if (!p) + return ENOMEM; + + p->mp_pgno = 0; + p->mp_flags = P_META; + *(MDB_meta *)METADATA(p) = *meta; + + q = (MDB_page *)((char *)p + psize); + q->mp_pgno = 1; + q->mp_flags = P_META; + *(MDB_meta *)METADATA(q) = *meta; + + DO_PWRITE(rc, env->me_fd, p, psize * NUM_METAS, len, 0); + if (!rc) + rc = ErrCode(); + else if ((unsigned) len == psize * NUM_METAS) + rc = MDB_SUCCESS; + else + rc = ENOSPC; + free(p); + return rc; +} + +/** Update the environment info to commit a transaction. + * @param[in] txn the transaction that's being committed + * @return 0 on success, non-zero on failure. + */ +static int +mdb_env_write_meta(MDB_txn *txn) +{ + MDB_env *env; + MDB_meta meta, metab, *mp; + unsigned flags; + size_t mapsize; + off_t off; + int rc, len, toggle; + char *ptr; + HANDLE mfd; +#ifdef _WIN32 + OVERLAPPED ov; +#else + int r2; +#endif + + toggle = txn->mt_txnid & 1; + DPRINTF(("writing meta page %d for root page %"Z"u", + toggle, txn->mt_dbs[MAIN_DBI].md_root)); + + env = txn->mt_env; + flags = env->me_flags; + mp = env->me_metas[toggle]; + mapsize = env->me_metas[toggle ^ 1]->mm_mapsize; + /* Persist any increases of mapsize config */ + if (mapsize < env->me_mapsize) + mapsize = env->me_mapsize; + + if (flags & MDB_WRITEMAP) { + mp->mm_mapsize = mapsize; + mp->mm_dbs[FREE_DBI] = txn->mt_dbs[FREE_DBI]; + mp->mm_dbs[MAIN_DBI] = txn->mt_dbs[MAIN_DBI]; + mp->mm_last_pg = txn->mt_next_pgno - 1; +#if (__GNUC__ * 100 + __GNUC_MINOR__ >= 404) && /* TODO: portability */ \ + !(defined(__i386__) || defined(__x86_64__)) + /* LY: issue a memory barrier, if not x86. ITS#7969 */ + __sync_synchronize(); +#endif + mp->mm_txnid = txn->mt_txnid; + if (!(flags & (MDB_NOMETASYNC|MDB_NOSYNC))) { + unsigned meta_size = env->me_psize; + rc = (env->me_flags & MDB_MAPASYNC) ? MS_ASYNC : MS_SYNC; + ptr = (char *)mp - PAGEHDRSZ; +#ifndef _WIN32 /* POSIX msync() requires ptr = start of OS page */ + r2 = (ptr - env->me_map) & (env->me_os_psize - 1); + ptr -= r2; + meta_size += r2; +#endif + if (MDB_MSYNC(ptr, meta_size, rc)) { + rc = ErrCode(); + goto fail; + } + } + goto done; + } + metab.mm_txnid = mp->mm_txnid; + metab.mm_last_pg = mp->mm_last_pg; + + meta.mm_mapsize = mapsize; + meta.mm_dbs[FREE_DBI] = txn->mt_dbs[FREE_DBI]; + meta.mm_dbs[MAIN_DBI] = txn->mt_dbs[MAIN_DBI]; + meta.mm_last_pg = txn->mt_next_pgno - 1; + meta.mm_txnid = txn->mt_txnid; + + off = offsetof(MDB_meta, mm_mapsize); + ptr = (char *)&meta + off; + len = sizeof(MDB_meta) - off; + off += (char *)mp - env->me_map; + + /* Write to the SYNC fd unless MDB_NOSYNC/MDB_NOMETASYNC. + * (me_mfd goes to the same file as me_fd, but writing to it + * also syncs to disk. Avoids a separate fdatasync() call.) + */ + mfd = (flags & (MDB_NOSYNC|MDB_NOMETASYNC)) ? env->me_fd : env->me_mfd; +#ifdef _WIN32 + { + memset(&ov, 0, sizeof(ov)); + ov.Offset = off; + if (!WriteFile(mfd, ptr, len, (DWORD *)&rc, &ov)) + rc = -1; + } +#else +retry_write: + rc = pwrite(mfd, ptr, len, off); +#endif + if (rc != len) { + rc = rc < 0 ? ErrCode() : EIO; +#ifndef _WIN32 + if (rc == EINTR) + goto retry_write; +#endif + DPUTS("write failed, disk error?"); + /* On a failure, the pagecache still contains the new data. + * Write some old data back, to prevent it from being used. + * Use the non-SYNC fd; we know it will fail anyway. + */ + meta.mm_last_pg = metab.mm_last_pg; + meta.mm_txnid = metab.mm_txnid; +#ifdef _WIN32 + memset(&ov, 0, sizeof(ov)); + ov.Offset = off; + WriteFile(env->me_fd, ptr, len, NULL, &ov); +#else + r2 = pwrite(env->me_fd, ptr, len, off); + (void)r2; /* Silence warnings. We don't care about pwrite's return value */ +#endif +fail: + env->me_flags |= MDB_FATAL_ERROR; + return rc; + } + /* MIPS has cache coherency issues, this is a no-op everywhere else */ + CACHEFLUSH(env->me_map + off, len, DCACHE); +done: + /* Memory ordering issues are irrelevant; since the entire writer + * is wrapped by wmutex, all of these changes will become visible + * after the wmutex is unlocked. Since the DB is multi-version, + * readers will get consistent data regardless of how fresh or + * how stale their view of these values is. + */ + if (env->me_txns) + env->me_txns->mti_txnid = txn->mt_txnid; + + return MDB_SUCCESS; +} + +/** Check both meta pages to see which one is newer. + * @param[in] env the environment handle + * @return newest #MDB_meta. + */ +static MDB_meta * +mdb_env_pick_meta(const MDB_env *env) +{ + MDB_meta *const *metas = env->me_metas; + return metas[ metas[0]->mm_txnid < metas[1]->mm_txnid ]; +} + +int ESECT +mdb_env_create(MDB_env **env) +{ + MDB_env *e; + + e = calloc(1, sizeof(MDB_env)); + if (!e) + return ENOMEM; + + e->me_maxreaders = DEFAULT_READERS; + e->me_maxdbs = e->me_numdbs = CORE_DBS; + e->me_fd = INVALID_HANDLE_VALUE; + e->me_lfd = INVALID_HANDLE_VALUE; + e->me_mfd = INVALID_HANDLE_VALUE; +#ifdef MDB_USE_POSIX_SEM + e->me_rmutex = SEM_FAILED; + e->me_wmutex = SEM_FAILED; +#endif + e->me_pid = getpid(); + GET_PAGESIZE(e->me_os_psize); + VGMEMP_CREATE(e,0,0); + *env = e; + return MDB_SUCCESS; +} + +static int ESECT +mdb_env_map(MDB_env *env, void *addr) +{ + MDB_page *p; + unsigned int flags = env->me_flags; +#ifdef _WIN32 + int rc; + HANDLE mh; + LONG sizelo, sizehi; + size_t msize; + + if (flags & MDB_RDONLY) { + /* Don't set explicit map size, use whatever exists */ + msize = 0; + sizelo = 0; + sizehi = 0; + } else { + msize = env->me_mapsize; + sizelo = msize & 0xffffffff; + sizehi = msize >> 16 >> 16; /* only needed on Win64 */ + + /* Windows won't create mappings for zero length files. + * and won't map more than the file size. + * Just set the maxsize right now. + */ + if (!(flags & MDB_WRITEMAP) && (SetFilePointer(env->me_fd, sizelo, &sizehi, 0) != (DWORD)sizelo + || !SetEndOfFile(env->me_fd) + || SetFilePointer(env->me_fd, 0, NULL, 0) != 0)) + return ErrCode(); + } + + mh = CreateFileMapping(env->me_fd, NULL, flags & MDB_WRITEMAP ? + PAGE_READWRITE : PAGE_READONLY, + sizehi, sizelo, NULL); + if (!mh) + return ErrCode(); + env->me_map = MapViewOfFileEx(mh, flags & MDB_WRITEMAP ? + FILE_MAP_WRITE : FILE_MAP_READ, + 0, 0, msize, addr); + rc = env->me_map ? 0 : ErrCode(); + CloseHandle(mh); + if (rc) + return rc; +#else + int mmap_flags = MAP_SHARED; + int prot = PROT_READ; +#ifdef MAP_NOSYNC /* Used on FreeBSD */ + if (flags & MDB_NOSYNC) + mmap_flags |= MAP_NOSYNC; +#endif + if (flags & MDB_WRITEMAP) { + prot |= PROT_WRITE; + if (ftruncate(env->me_fd, env->me_mapsize) < 0) + return ErrCode(); + } + env->me_map = mmap(addr, env->me_mapsize, prot, mmap_flags, + env->me_fd, 0); + if (env->me_map == MAP_FAILED) { + env->me_map = NULL; + return ErrCode(); + } + + if (flags & MDB_NORDAHEAD) { + /* Turn off readahead. It's harmful when the DB is larger than RAM. */ +#ifdef MADV_RANDOM + madvise(env->me_map, env->me_mapsize, MADV_RANDOM); +#else +#ifdef POSIX_MADV_RANDOM + posix_madvise(env->me_map, env->me_mapsize, POSIX_MADV_RANDOM); +#endif /* POSIX_MADV_RANDOM */ +#endif /* MADV_RANDOM */ + } +#endif /* _WIN32 */ + + /* Can happen because the address argument to mmap() is just a + * hint. mmap() can pick another, e.g. if the range is in use. + * The MAP_FIXED flag would prevent that, but then mmap could + * instead unmap existing pages to make room for the new map. + */ + if (addr && env->me_map != addr) + return EBUSY; /* TODO: Make a new MDB_* error code? */ + + p = (MDB_page *)env->me_map; + env->me_metas[0] = METADATA(p); + env->me_metas[1] = (MDB_meta *)((char *)env->me_metas[0] + env->me_psize); + + return MDB_SUCCESS; +} + +int ESECT +mdb_env_set_mapsize(MDB_env *env, size_t size) +{ + /* If env is already open, caller is responsible for making + * sure there are no active txns. + */ + if (env->me_map) { + int rc; + MDB_meta *meta; + void *old; + if (env->me_txn) + return EINVAL; + meta = mdb_env_pick_meta(env); + if (!size) + size = meta->mm_mapsize; + { + /* Silently round up to minimum if the size is too small */ + size_t minsize = (meta->mm_last_pg + 1) * env->me_psize; + if (size < minsize) + size = minsize; + } + munmap(env->me_map, env->me_mapsize); + env->me_mapsize = size; + old = (env->me_flags & MDB_FIXEDMAP) ? env->me_map : NULL; + rc = mdb_env_map(env, old); + if (rc) + return rc; + } + env->me_mapsize = size; + if (env->me_psize) + env->me_maxpg = env->me_mapsize / env->me_psize; + return MDB_SUCCESS; +} + +int ESECT +mdb_env_set_maxdbs(MDB_env *env, MDB_dbi dbs) +{ + if (env->me_map) + return EINVAL; + env->me_maxdbs = dbs + CORE_DBS; + return MDB_SUCCESS; +} + +int ESECT +mdb_env_set_maxreaders(MDB_env *env, unsigned int readers) +{ + if (env->me_map || readers < 1) + return EINVAL; + env->me_maxreaders = readers; + return MDB_SUCCESS; +} + +int ESECT +mdb_env_get_maxreaders(MDB_env *env, unsigned int *readers) +{ + if (!env || !readers) + return EINVAL; + *readers = env->me_maxreaders; + return MDB_SUCCESS; +} + +static int ESECT +mdb_fsize(HANDLE fd, size_t *size) +{ +#ifdef _WIN32 + LARGE_INTEGER fsize; + + if (!GetFileSizeEx(fd, &fsize)) + return ErrCode(); + + *size = fsize.QuadPart; +#else + struct stat st; + + if (fstat(fd, &st)) + return ErrCode(); + + *size = st.st_size; +#endif + return MDB_SUCCESS; +} + + +#ifdef _WIN32 +typedef wchar_t mdb_nchar_t; +# define MDB_NAME(str) L##str +# define mdb_name_cpy wcscpy +#else +/** Character type for file names: char on Unix, wchar_t on Windows */ +typedef char mdb_nchar_t; +# define MDB_NAME(str) str /**< #mdb_nchar_t[] string literal */ +# define mdb_name_cpy strcpy /**< Copy name (#mdb_nchar_t string) */ +#endif + +/** Filename - string of #mdb_nchar_t[] */ +typedef struct MDB_name { + int mn_len; /**< Length */ + int mn_alloced; /**< True if #mn_val was malloced */ + mdb_nchar_t *mn_val; /**< Contents */ +} MDB_name; + +/** Filename suffixes [datafile,lockfile][without,with MDB_NOSUBDIR] */ +static const mdb_nchar_t *const mdb_suffixes[2][2] = { + { MDB_NAME("/data.mdb"), MDB_NAME("") }, + { MDB_NAME("/lock.mdb"), MDB_NAME("-lock") } +}; + +#define MDB_SUFFLEN 9 /**< Max string length in #mdb_suffixes[] */ + +/** Set up filename + scratch area for filename suffix, for opening files. + * It should be freed with #mdb_fname_destroy(). + * On Windows, paths are converted from char *UTF-8 to wchar_t *UTF-16. + * + * @param[in] path Pathname for #mdb_env_open(). + * @param[in] envflags Whether a subdir and/or lockfile will be used. + * @param[out] fname Resulting filename, with room for a suffix if necessary. + */ +static int ESECT +mdb_fname_init(const char *path, unsigned envflags, MDB_name *fname) +{ + int no_suffix = F_ISSET(envflags, MDB_NOSUBDIR|MDB_NOLOCK); + fname->mn_alloced = 0; +#ifdef _WIN32 + return utf8_to_utf16(path, fname, no_suffix ? 0 : MDB_SUFFLEN); +#else + fname->mn_len = strlen(path); + if (no_suffix) + fname->mn_val = (char *) path; + else if ((fname->mn_val = malloc(fname->mn_len + MDB_SUFFLEN+1)) != NULL) { + fname->mn_alloced = 1; + strcpy(fname->mn_val, path); + } + else + return ENOMEM; + return MDB_SUCCESS; +#endif +} + +/** Destroy \b fname from #mdb_fname_init() */ +#define mdb_fname_destroy(fname) \ + do { if ((fname).mn_alloced) free((fname).mn_val); } while (0) + +#ifdef O_CLOEXEC /* POSIX.1-2008: Set FD_CLOEXEC atomically at open() */ +# define MDB_CLOEXEC O_CLOEXEC +#else +# define MDB_CLOEXEC 0 +#endif + +/** File type, access mode etc. for #mdb_fopen() */ +enum mdb_fopen_type { +#ifdef _WIN32 + MDB_O_RDONLY, MDB_O_RDWR, MDB_O_META, MDB_O_COPY, MDB_O_LOCKS +#else + /* A comment in mdb_fopen() explains some O_* flag choices. */ + MDB_O_RDONLY= O_RDONLY, /**< for RDONLY me_fd */ + MDB_O_RDWR = O_RDWR |O_CREAT, /**< for me_fd */ + MDB_O_META = O_WRONLY|MDB_DSYNC |MDB_CLOEXEC, /**< for me_mfd */ + MDB_O_COPY = O_WRONLY|O_CREAT|O_EXCL|MDB_CLOEXEC, /**< for #mdb_env_copy() */ + /** Bitmask for open() flags in enum #mdb_fopen_type. The other bits + * distinguish otherwise-equal MDB_O_* constants from each other. + */ + MDB_O_MASK = MDB_O_RDWR|MDB_CLOEXEC | MDB_O_RDONLY|MDB_O_META|MDB_O_COPY, + MDB_O_LOCKS = MDB_O_RDWR|MDB_CLOEXEC | ((MDB_O_MASK+1) & ~MDB_O_MASK) /**< for me_lfd */ +#endif +}; + +/** Open an LMDB file. + * @param[in] env The LMDB environment. + * @param[in,out] fname Path from from #mdb_fname_init(). A suffix is + * appended if necessary to create the filename, without changing mn_len. + * @param[in] which Determines file type, access mode, etc. + * @param[in] mode The Unix permissions for the file, if we create it. + * @param[out] res Resulting file handle. + * @return 0 on success, non-zero on failure. + */ +static int ESECT +mdb_fopen(const MDB_env *env, MDB_name *fname, + enum mdb_fopen_type which, mdb_mode_t mode, + HANDLE *res) +{ + int rc = MDB_SUCCESS; + HANDLE fd; +#ifdef _WIN32 + DWORD acc, share, disp, attrs; +#else + int flags; +#endif + + if (fname->mn_alloced) /* modifiable copy */ + mdb_name_cpy(fname->mn_val + fname->mn_len, + mdb_suffixes[which==MDB_O_LOCKS][F_ISSET(env->me_flags, MDB_NOSUBDIR)]); + + /* The directory must already exist. Usually the file need not. + * MDB_O_META requires the file because we already created it using + * MDB_O_RDWR. MDB_O_COPY must not overwrite an existing file. + * + * With MDB_O_COPY we do not want the OS to cache the writes, since + * the source data is already in the OS cache. + * + * The lockfile needs FD_CLOEXEC (close file descriptor on exec*()) + * to avoid the flock() issues noted under Caveats in lmdb.h. + * Also set it for other filehandles which the user cannot get at + * and close himself, which he may need after fork(). I.e. all but + * me_fd, which programs do use via mdb_env_get_fd(). + */ + +#ifdef _WIN32 + acc = GENERIC_READ|GENERIC_WRITE; + share = FILE_SHARE_READ|FILE_SHARE_WRITE; + disp = OPEN_ALWAYS; + attrs = FILE_ATTRIBUTE_NORMAL; + switch (which) { + case MDB_O_RDONLY: /* read-only datafile */ + acc = GENERIC_READ; + disp = OPEN_EXISTING; + break; + case MDB_O_META: /* for writing metapages */ + acc = GENERIC_WRITE; + disp = OPEN_EXISTING; + attrs = FILE_ATTRIBUTE_NORMAL|FILE_FLAG_WRITE_THROUGH; + break; + case MDB_O_COPY: /* mdb_env_copy() & co */ + acc = GENERIC_WRITE; + share = 0; + disp = CREATE_NEW; + attrs = FILE_FLAG_NO_BUFFERING|FILE_FLAG_WRITE_THROUGH; + break; + default: break; /* silence gcc -Wswitch (not all enum values handled) */ + } + fd = CreateFileW(fname->mn_val, acc, share, NULL, disp, attrs, NULL); +#else + fd = open(fname->mn_val, which & MDB_O_MASK, mode); +#endif + + if (fd == INVALID_HANDLE_VALUE) + rc = ErrCode(); +#ifndef _WIN32 + else { + if (which != MDB_O_RDONLY && which != MDB_O_RDWR) { + /* Set CLOEXEC if we could not pass it to open() */ + if (!MDB_CLOEXEC && (flags = fcntl(fd, F_GETFD)) != -1) + (void) fcntl(fd, F_SETFD, flags | FD_CLOEXEC); + } + if (which == MDB_O_COPY && env->me_psize >= env->me_os_psize) { + /* This may require buffer alignment. There is no portable + * way to ask how much, so we require OS pagesize alignment. + */ +# ifdef F_NOCACHE /* __APPLE__ */ + (void) fcntl(fd, F_NOCACHE, 1); +# elif defined O_DIRECT + /* open(...O_DIRECT...) would break on filesystems without + * O_DIRECT support (ITS#7682). Try to set it here instead. + */ + if ((flags = fcntl(fd, F_GETFL)) != -1) + (void) fcntl(fd, F_SETFL, flags | O_DIRECT); +# endif + } + } +#endif /* !_WIN32 */ + + *res = fd; + return rc; +} + + +#ifdef BROKEN_FDATASYNC +#include <sys/utsname.h> +#include <sys/vfs.h> +#endif + +/** Further setup required for opening an LMDB environment + */ +static int ESECT +mdb_env_open2(MDB_env *env) +{ + unsigned int flags = env->me_flags; + int i, newenv = 0, rc; + MDB_meta meta; + +#ifdef _WIN32 + /* See if we should use QueryLimited */ + rc = GetVersion(); + if ((rc & 0xff) > 5) + env->me_pidquery = MDB_PROCESS_QUERY_LIMITED_INFORMATION; + else + env->me_pidquery = PROCESS_QUERY_INFORMATION; +#endif /* _WIN32 */ + +#ifdef BROKEN_FDATASYNC + /* ext3/ext4 fdatasync is broken on some older Linux kernels. + * https://lkml.org/lkml/2012/9/3/83 + * Kernels after 3.6-rc6 are known good. + * https://lkml.org/lkml/2012/9/10/556 + * See if the DB is on ext3/ext4, then check for new enough kernel + * Kernels 2.6.32.60, 2.6.34.15, 3.2.30, and 3.5.4 are also known + * to be patched. + */ + { + struct statfs st; + fstatfs(env->me_fd, &st); + while (st.f_type == 0xEF53) { + struct utsname uts; + int i; + uname(&uts); + if (uts.release[0] < '3') { + if (!strncmp(uts.release, "2.6.32.", 7)) { + i = atoi(uts.release+7); + if (i >= 60) + break; /* 2.6.32.60 and newer is OK */ + } else if (!strncmp(uts.release, "2.6.34.", 7)) { + i = atoi(uts.release+7); + if (i >= 15) + break; /* 2.6.34.15 and newer is OK */ + } + } else if (uts.release[0] == '3') { + i = atoi(uts.release+2); + if (i > 5) + break; /* 3.6 and newer is OK */ + if (i == 5) { + i = atoi(uts.release+4); + if (i >= 4) + break; /* 3.5.4 and newer is OK */ + } else if (i == 2) { + i = atoi(uts.release+4); + if (i >= 30) + break; /* 3.2.30 and newer is OK */ + } + } else { /* 4.x and newer is OK */ + break; + } + env->me_flags |= MDB_FSYNCONLY; + break; + } + } +#endif + + if ((i = mdb_env_read_header(env, &meta)) != 0) { + if (i != ENOENT) + return i; + DPUTS("new mdbenv"); + newenv = 1; + env->me_psize = env->me_os_psize; + if (env->me_psize > MAX_PAGESIZE) + env->me_psize = MAX_PAGESIZE; + memset(&meta, 0, sizeof(meta)); + mdb_env_init_meta0(env, &meta); + meta.mm_mapsize = DEFAULT_MAPSIZE; + } else { + env->me_psize = meta.mm_psize; + } + + /* Was a mapsize configured? */ + if (!env->me_mapsize) { + env->me_mapsize = meta.mm_mapsize; + } + { + /* Make sure mapsize >= committed data size. Even when using + * mm_mapsize, which could be broken in old files (ITS#7789). + */ + size_t minsize = (meta.mm_last_pg + 1) * meta.mm_psize; + if (env->me_mapsize < minsize) + env->me_mapsize = minsize; + } + meta.mm_mapsize = env->me_mapsize; + + if (newenv && !(flags & MDB_FIXEDMAP)) { + /* mdb_env_map() may grow the datafile. Write the metapages + * first, so the file will be valid if initialization fails. + * Except with FIXEDMAP, since we do not yet know mm_address. + * We could fill in mm_address later, but then a different + * program might end up doing that - one with a memory layout + * and map address which does not suit the main program. + */ + rc = mdb_env_init_meta(env, &meta); + if (rc) + return rc; + newenv = 0; + } + + rc = mdb_env_map(env, (flags & MDB_FIXEDMAP) ? meta.mm_address : NULL); + if (rc) + return rc; + + if (newenv) { + if (flags & MDB_FIXEDMAP) + meta.mm_address = env->me_map; + i = mdb_env_init_meta(env, &meta); + if (i != MDB_SUCCESS) { + return i; + } + } + + env->me_maxfree_1pg = (env->me_psize - PAGEHDRSZ) / sizeof(pgno_t) - 1; + env->me_nodemax = (((env->me_psize - PAGEHDRSZ) / MDB_MINKEYS) & -2) + - sizeof(indx_t); +#if !(MDB_MAXKEYSIZE) + env->me_maxkey = env->me_nodemax - (NODESIZE + sizeof(MDB_db)); +#endif + env->me_maxpg = env->me_mapsize / env->me_psize; + +#if MDB_DEBUG + { + MDB_meta *meta = mdb_env_pick_meta(env); + MDB_db *db = &meta->mm_dbs[MAIN_DBI]; + + DPRINTF(("opened database version %u, pagesize %u", + meta->mm_version, env->me_psize)); + DPRINTF(("using meta page %d", (int) (meta->mm_txnid & 1))); + DPRINTF(("depth: %u", db->md_depth)); + DPRINTF(("entries: %"Z"u", db->md_entries)); + DPRINTF(("branch pages: %"Z"u", db->md_branch_pages)); + DPRINTF(("leaf pages: %"Z"u", db->md_leaf_pages)); + DPRINTF(("overflow pages: %"Z"u", db->md_overflow_pages)); + DPRINTF(("root: %"Z"u", db->md_root)); + } +#endif + + return MDB_SUCCESS; +} + + +/** Release a reader thread's slot in the reader lock table. + * This function is called automatically when a thread exits. + * @param[in] ptr This points to the slot in the reader lock table. + */ +static void +mdb_env_reader_dest(void *ptr) +{ + MDB_reader *reader = ptr; + +#ifndef _WIN32 + if (reader->mr_pid == getpid()) /* catch pthread_exit() in child process */ +#endif + /* We omit the mutex, so do this atomically (i.e. skip mr_txnid) */ + reader->mr_pid = 0; +} + +#ifdef _WIN32 +/** Junk for arranging thread-specific callbacks on Windows. This is + * necessarily platform and compiler-specific. Windows supports up + * to 1088 keys. Let's assume nobody opens more than 64 environments + * in a single process, for now. They can override this if needed. + */ +#ifndef MAX_TLS_KEYS +#define MAX_TLS_KEYS 64 +#endif +static pthread_key_t mdb_tls_keys[MAX_TLS_KEYS]; +static int mdb_tls_nkeys; + +static void NTAPI mdb_tls_callback(PVOID module, DWORD reason, PVOID ptr) +{ + int i; + switch(reason) { + case DLL_PROCESS_ATTACH: break; + case DLL_THREAD_ATTACH: break; + case DLL_THREAD_DETACH: + for (i=0; i<mdb_tls_nkeys; i++) { + MDB_reader *r = pthread_getspecific(mdb_tls_keys[i]); + if (r) { + mdb_env_reader_dest(r); + } + } + break; + case DLL_PROCESS_DETACH: break; + } +} +#ifdef __GNUC__ +#ifdef _WIN64 +const PIMAGE_TLS_CALLBACK mdb_tls_cbp __attribute__((section (".CRT$XLB"))) = mdb_tls_callback; +#else +PIMAGE_TLS_CALLBACK mdb_tls_cbp __attribute__((section (".CRT$XLB"))) = mdb_tls_callback; +#endif +#else +#ifdef _WIN64 +/* Force some symbol references. + * _tls_used forces the linker to create the TLS directory if not already done + * mdb_tls_cbp prevents whole-program-optimizer from dropping the symbol. + */ +#pragma comment(linker, "/INCLUDE:_tls_used") +#pragma comment(linker, "/INCLUDE:mdb_tls_cbp") +#pragma const_seg(".CRT$XLB") +extern const PIMAGE_TLS_CALLBACK mdb_tls_cbp; +const PIMAGE_TLS_CALLBACK mdb_tls_cbp = mdb_tls_callback; +#pragma const_seg() +#else /* _WIN32 */ +#pragma comment(linker, "/INCLUDE:__tls_used") +#pragma comment(linker, "/INCLUDE:_mdb_tls_cbp") +#pragma data_seg(".CRT$XLB") +PIMAGE_TLS_CALLBACK mdb_tls_cbp = mdb_tls_callback; +#pragma data_seg() +#endif /* WIN 32/64 */ +#endif /* !__GNUC__ */ +#endif + +/** Downgrade the exclusive lock on the region back to shared */ +static int ESECT +mdb_env_share_locks(MDB_env *env, int *excl) +{ + int rc = 0; + MDB_meta *meta = mdb_env_pick_meta(env); + + env->me_txns->mti_txnid = meta->mm_txnid; + +#ifdef _WIN32 + { + OVERLAPPED ov; + /* First acquire a shared lock. The Unlock will + * then release the existing exclusive lock. + */ + memset(&ov, 0, sizeof(ov)); + if (!LockFileEx(env->me_lfd, 0, 0, 1, 0, &ov)) { + rc = ErrCode(); + } else { + UnlockFile(env->me_lfd, 0, 0, 1, 0); + *excl = 0; + } + } +#else + { + struct flock lock_info; + /* The shared lock replaces the existing lock */ + memset((void *)&lock_info, 0, sizeof(lock_info)); + lock_info.l_type = F_RDLCK; + lock_info.l_whence = SEEK_SET; + lock_info.l_start = 0; + lock_info.l_len = 1; + while ((rc = fcntl(env->me_lfd, F_SETLK, &lock_info)) && + (rc = ErrCode()) == EINTR) ; + *excl = rc ? -1 : 0; /* error may mean we lost the lock */ + } +#endif + + return rc; +} + +/** Try to get exclusive lock, otherwise shared. + * Maintain *excl = -1: no/unknown lock, 0: shared, 1: exclusive. + */ +static int ESECT +mdb_env_excl_lock(MDB_env *env, int *excl) +{ + int rc = 0; +#ifdef _WIN32 + if (LockFile(env->me_lfd, 0, 0, 1, 0)) { + *excl = 1; + } else { + OVERLAPPED ov; + memset(&ov, 0, sizeof(ov)); + if (LockFileEx(env->me_lfd, 0, 0, 1, 0, &ov)) { + *excl = 0; + } else { + rc = ErrCode(); + } + } +#else + struct flock lock_info; + memset((void *)&lock_info, 0, sizeof(lock_info)); + lock_info.l_type = F_WRLCK; + lock_info.l_whence = SEEK_SET; + lock_info.l_start = 0; + lock_info.l_len = 1; + while ((rc = fcntl(env->me_lfd, F_SETLK, &lock_info)) && + (rc = ErrCode()) == EINTR) ; + if (!rc) { + *excl = 1; + } else +# ifndef MDB_USE_POSIX_MUTEX + if (*excl < 0) /* always true when MDB_USE_POSIX_MUTEX */ +# endif + { + lock_info.l_type = F_RDLCK; + while ((rc = fcntl(env->me_lfd, F_SETLKW, &lock_info)) && + (rc = ErrCode()) == EINTR) ; + if (rc == 0) + *excl = 0; + } +#endif + return rc; +} + +#ifdef MDB_USE_HASH +/* + * hash_64 - 64 bit Fowler/Noll/Vo-0 FNV-1a hash code + * + * @(#) $Revision: 5.1 $ + * @(#) $Id: hash_64a.c,v 5.1 2009/06/30 09:01:38 chongo Exp $ + * @(#) $Source: /usr/local/src/cmd/fnv/RCS/hash_64a.c,v $ + * + * http://www.isthe.com/chongo/tech/comp/fnv/index.html + * + *** + * + * Please do not copyright this code. This code is in the public domain. + * + * LANDON CURT NOLL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO + * EVENT SHALL LANDON CURT NOLL BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + * + * By: + * chongo <Landon Curt Noll> /\oo/\ + * http://www.isthe.com/chongo/ + * + * Share and Enjoy! :-) + */ + +typedef unsigned long long mdb_hash_t; +#define MDB_HASH_INIT ((mdb_hash_t)0xcbf29ce484222325ULL) + +/** perform a 64 bit Fowler/Noll/Vo FNV-1a hash on a buffer + * @param[in] val value to hash + * @param[in] hval initial value for hash + * @return 64 bit hash + * + * NOTE: To use the recommended 64 bit FNV-1a hash, use MDB_HASH_INIT as the + * hval arg on the first call. + */ +static mdb_hash_t +mdb_hash_val(MDB_val *val, mdb_hash_t hval) +{ + unsigned char *s = (unsigned char *)val->mv_data; /* unsigned string */ + unsigned char *end = s + val->mv_size; + /* + * FNV-1a hash each octet of the string + */ + while (s < end) { + /* xor the bottom with the current octet */ + hval ^= (mdb_hash_t)*s++; + + /* multiply by the 64 bit FNV magic prime mod 2^64 */ + hval += (hval << 1) + (hval << 4) + (hval << 5) + + (hval << 7) + (hval << 8) + (hval << 40); + } + /* return our new hash value */ + return hval; +} + +/** Hash the string and output the encoded hash. + * This uses modified RFC1924 Ascii85 encoding to accommodate systems with + * very short name limits. We don't care about the encoding being reversible, + * we just want to preserve as many bits of the input as possible in a + * small printable string. + * @param[in] str string to hash + * @param[out] encbuf an array of 11 chars to hold the hash + */ +static const char mdb_a85[]= "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~"; + +static void ESECT +mdb_pack85(unsigned long l, char *out) +{ + int i; + + for (i=0; i<5; i++) { + *out++ = mdb_a85[l % 85]; + l /= 85; + } +} + +static void ESECT +mdb_hash_enc(MDB_val *val, char *encbuf) +{ + mdb_hash_t h = mdb_hash_val(val, MDB_HASH_INIT); + + mdb_pack85(h, encbuf); + mdb_pack85(h>>32, encbuf+5); + encbuf[10] = '\0'; +} +#endif + +/** Open and/or initialize the lock region for the environment. + * @param[in] env The LMDB environment. + * @param[in] fname Filename + scratch area, from #mdb_fname_init(). + * @param[in] mode The Unix permissions for the file, if we create it. + * @param[in,out] excl In -1, out lock type: -1 none, 0 shared, 1 exclusive + * @return 0 on success, non-zero on failure. + */ +static int ESECT +mdb_env_setup_locks(MDB_env *env, MDB_name *fname, int mode, int *excl) +{ +#ifdef _WIN32 +# define MDB_ERRCODE_ROFS ERROR_WRITE_PROTECT +#else +# define MDB_ERRCODE_ROFS EROFS +#endif + int rc; + off_t size, rsize; + + rc = mdb_fopen(env, fname, MDB_O_LOCKS, mode, &env->me_lfd); + if (rc) { + /* Omit lockfile if read-only env on read-only filesystem */ + if (rc == MDB_ERRCODE_ROFS && (env->me_flags & MDB_RDONLY)) { + return MDB_SUCCESS; + } + goto fail; + } + + if (!(env->me_flags & MDB_NOTLS)) { + rc = pthread_key_create(&env->me_txkey, mdb_env_reader_dest); + if (rc) + goto fail; + env->me_flags |= MDB_ENV_TXKEY; +#ifdef _WIN32 + /* Windows TLS callbacks need help finding their TLS info. */ + if (mdb_tls_nkeys >= MAX_TLS_KEYS) { + rc = MDB_TLS_FULL; + goto fail; + } + mdb_tls_keys[mdb_tls_nkeys++] = env->me_txkey; +#endif + } + + /* Try to get exclusive lock. If we succeed, then + * nobody is using the lock region and we should initialize it. + */ + if ((rc = mdb_env_excl_lock(env, excl))) goto fail; + +#ifdef _WIN32 + size = GetFileSize(env->me_lfd, NULL); +#else + size = lseek(env->me_lfd, 0, SEEK_END); + if (size == -1) goto fail_errno; +#endif + rsize = (env->me_maxreaders-1) * sizeof(MDB_reader) + sizeof(MDB_txninfo); + if (size < rsize && *excl > 0) { +#ifdef _WIN32 + if (SetFilePointer(env->me_lfd, rsize, NULL, FILE_BEGIN) != (DWORD)rsize + || !SetEndOfFile(env->me_lfd)) + goto fail_errno; +#else + if (ftruncate(env->me_lfd, rsize) != 0) goto fail_errno; +#endif + } else { + rsize = size; + size = rsize - sizeof(MDB_txninfo); + env->me_maxreaders = size/sizeof(MDB_reader) + 1; + } + { +#ifdef _WIN32 + HANDLE mh; + mh = CreateFileMapping(env->me_lfd, NULL, PAGE_READWRITE, + 0, 0, NULL); + if (!mh) goto fail_errno; + env->me_txns = MapViewOfFileEx(mh, FILE_MAP_WRITE, 0, 0, rsize, NULL); + CloseHandle(mh); + if (!env->me_txns) goto fail_errno; +#else + void *m = mmap(NULL, rsize, PROT_READ|PROT_WRITE, MAP_SHARED, + env->me_lfd, 0); + if (m == MAP_FAILED) goto fail_errno; + env->me_txns = m; +#endif + } + if (*excl > 0) { +#ifdef _WIN32 + BY_HANDLE_FILE_INFORMATION stbuf; + struct { + DWORD volume; + DWORD nhigh; + DWORD nlow; + } idbuf; + MDB_val val; + char encbuf[11]; + + if (!mdb_sec_inited) { + InitializeSecurityDescriptor(&mdb_null_sd, + SECURITY_DESCRIPTOR_REVISION); + SetSecurityDescriptorDacl(&mdb_null_sd, TRUE, 0, FALSE); + mdb_all_sa.nLength = sizeof(SECURITY_ATTRIBUTES); + mdb_all_sa.bInheritHandle = FALSE; + mdb_all_sa.lpSecurityDescriptor = &mdb_null_sd; + mdb_sec_inited = 1; + } + if (!GetFileInformationByHandle(env->me_lfd, &stbuf)) goto fail_errno; + idbuf.volume = stbuf.dwVolumeSerialNumber; + idbuf.nhigh = stbuf.nFileIndexHigh; + idbuf.nlow = stbuf.nFileIndexLow; + val.mv_data = &idbuf; + val.mv_size = sizeof(idbuf); + mdb_hash_enc(&val, encbuf); + sprintf(env->me_txns->mti_rmname, "Global\\MDBr%s", encbuf); + sprintf(env->me_txns->mti_wmname, "Global\\MDBw%s", encbuf); + env->me_rmutex = CreateMutexA(&mdb_all_sa, FALSE, env->me_txns->mti_rmname); + if (!env->me_rmutex) goto fail_errno; + env->me_wmutex = CreateMutexA(&mdb_all_sa, FALSE, env->me_txns->mti_wmname); + if (!env->me_wmutex) goto fail_errno; +#elif defined(MDB_USE_POSIX_SEM) + struct stat stbuf; + struct { + dev_t dev; + ino_t ino; + } idbuf; + MDB_val val; + char encbuf[11]; + +#if defined(__NetBSD__) +#define MDB_SHORT_SEMNAMES 1 /* limited to 14 chars */ +#endif + if (fstat(env->me_lfd, &stbuf)) goto fail_errno; + idbuf.dev = stbuf.st_dev; + idbuf.ino = stbuf.st_ino; + val.mv_data = &idbuf; + val.mv_size = sizeof(idbuf); + mdb_hash_enc(&val, encbuf); +#ifdef MDB_SHORT_SEMNAMES + encbuf[9] = '\0'; /* drop name from 15 chars to 14 chars */ +#endif + sprintf(env->me_txns->mti_rmname, "/MDBr%s", encbuf); + sprintf(env->me_txns->mti_wmname, "/MDBw%s", encbuf); + /* Clean up after a previous run, if needed: Try to + * remove both semaphores before doing anything else. + */ + sem_unlink(env->me_txns->mti_rmname); + sem_unlink(env->me_txns->mti_wmname); + env->me_rmutex = sem_open(env->me_txns->mti_rmname, + O_CREAT|O_EXCL, mode, 1); + if (env->me_rmutex == SEM_FAILED) goto fail_errno; + env->me_wmutex = sem_open(env->me_txns->mti_wmname, + O_CREAT|O_EXCL, mode, 1); + if (env->me_wmutex == SEM_FAILED) goto fail_errno; +#else /* MDB_USE_POSIX_MUTEX: */ + pthread_mutexattr_t mattr; + + /* Solaris needs this before initing a robust mutex. Otherwise + * it may skip the init and return EBUSY "seems someone already + * inited" or EINVAL "it was inited differently". + */ + memset(env->me_txns->mti_rmutex, 0, sizeof(*env->me_txns->mti_rmutex)); + memset(env->me_txns->mti_wmutex, 0, sizeof(*env->me_txns->mti_wmutex)); + + if ((rc = pthread_mutexattr_init(&mattr))) + goto fail; + + rc = pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED); +#ifdef MDB_ROBUST_SUPPORTED + if (!rc) rc = pthread_mutexattr_setrobust(&mattr, PTHREAD_MUTEX_ROBUST); +#endif + if (!rc) rc = pthread_mutex_init(env->me_txns->mti_rmutex, &mattr); + if (!rc) rc = pthread_mutex_init(env->me_txns->mti_wmutex, &mattr); + pthread_mutexattr_destroy(&mattr); + if (rc) + goto fail; +#endif /* _WIN32 || MDB_USE_POSIX_SEM */ + + env->me_txns->mti_magic = MDB_MAGIC; + env->me_txns->mti_format = MDB_LOCK_FORMAT; + env->me_txns->mti_txnid = 0; + env->me_txns->mti_numreaders = 0; + + } else { + if (env->me_txns->mti_magic != MDB_MAGIC) { + DPUTS("lock region has invalid magic"); + rc = MDB_INVALID; + goto fail; + } + if (env->me_txns->mti_format != MDB_LOCK_FORMAT) { + DPRINTF(("lock region has format+version 0x%x, expected 0x%x", + env->me_txns->mti_format, MDB_LOCK_FORMAT)); + rc = MDB_VERSION_MISMATCH; + goto fail; + } + rc = ErrCode(); + if (rc && rc != EACCES && rc != EAGAIN) { + goto fail; + } +#ifdef _WIN32 + env->me_rmutex = OpenMutexA(SYNCHRONIZE, FALSE, env->me_txns->mti_rmname); + if (!env->me_rmutex) goto fail_errno; + env->me_wmutex = OpenMutexA(SYNCHRONIZE, FALSE, env->me_txns->mti_wmname); + if (!env->me_wmutex) goto fail_errno; +#elif defined(MDB_USE_POSIX_SEM) + env->me_rmutex = sem_open(env->me_txns->mti_rmname, 0); + if (env->me_rmutex == SEM_FAILED) goto fail_errno; + env->me_wmutex = sem_open(env->me_txns->mti_wmname, 0); + if (env->me_wmutex == SEM_FAILED) goto fail_errno; +#endif + } + return MDB_SUCCESS; + +fail_errno: + rc = ErrCode(); +fail: + return rc; +} + + /** Only a subset of the @ref mdb_env flags can be changed + * at runtime. Changing other flags requires closing the + * environment and re-opening it with the new flags. + */ +#define CHANGEABLE (MDB_NOSYNC|MDB_NOMETASYNC|MDB_MAPASYNC|MDB_NOMEMINIT) +#define CHANGELESS (MDB_FIXEDMAP|MDB_NOSUBDIR|MDB_RDONLY| \ + MDB_WRITEMAP|MDB_NOTLS|MDB_NOLOCK|MDB_NORDAHEAD) + +#if VALID_FLAGS & PERSISTENT_FLAGS & (CHANGEABLE|CHANGELESS) +# error "Persistent DB flags & env flags overlap, but both go in mm_flags" +#endif + +int ESECT +mdb_env_open(MDB_env *env, const char *path, unsigned int flags, mdb_mode_t mode) +{ + int rc, excl = -1; + MDB_name fname; + + if (env->me_fd!=INVALID_HANDLE_VALUE || (flags & ~(CHANGEABLE|CHANGELESS))) + return EINVAL; + + flags |= env->me_flags; + + rc = mdb_fname_init(path, flags, &fname); + if (rc) + return rc; + + if (flags & MDB_RDONLY) { + /* silently ignore WRITEMAP when we're only getting read access */ + flags &= ~MDB_WRITEMAP; + } else { + if (!((env->me_free_pgs = mdb_midl_alloc(MDB_IDL_UM_MAX)) && + (env->me_dirty_list = calloc(MDB_IDL_UM_SIZE, sizeof(MDB_ID2))))) + rc = ENOMEM; + } + env->me_flags = flags |= MDB_ENV_ACTIVE; + if (rc) + goto leave; + + env->me_path = strdup(path); + env->me_dbxs = calloc(env->me_maxdbs, sizeof(MDB_dbx)); + env->me_dbflags = calloc(env->me_maxdbs, sizeof(uint16_t)); + env->me_dbiseqs = calloc(env->me_maxdbs, sizeof(unsigned int)); + if (!(env->me_dbxs && env->me_path && env->me_dbflags && env->me_dbiseqs)) { + rc = ENOMEM; + goto leave; + } + env->me_dbxs[FREE_DBI].md_cmp = mdb_cmp_long; /* aligned MDB_INTEGERKEY */ + + /* For RDONLY, get lockfile after we know datafile exists */ + if (!(flags & (MDB_RDONLY|MDB_NOLOCK))) { + rc = mdb_env_setup_locks(env, &fname, mode, &excl); + if (rc) + goto leave; + } + + rc = mdb_fopen(env, &fname, + (flags & MDB_RDONLY) ? MDB_O_RDONLY : MDB_O_RDWR, + mode, &env->me_fd); + if (rc) + goto leave; + + if ((flags & (MDB_RDONLY|MDB_NOLOCK)) == MDB_RDONLY) { + rc = mdb_env_setup_locks(env, &fname, mode, &excl); + if (rc) + goto leave; + } + + if ((rc = mdb_env_open2(env)) == MDB_SUCCESS) { + if (!(flags & (MDB_RDONLY|MDB_WRITEMAP))) { + /* Synchronous fd for meta writes. Needed even with + * MDB_NOSYNC/MDB_NOMETASYNC, in case these get reset. + */ + rc = mdb_fopen(env, &fname, MDB_O_META, mode, &env->me_mfd); + if (rc) + goto leave; + } + DPRINTF(("opened dbenv %p", (void *) env)); + if (excl > 0) { + rc = mdb_env_share_locks(env, &excl); + if (rc) + goto leave; + } + if (!(flags & MDB_RDONLY)) { + MDB_txn *txn; + int tsize = sizeof(MDB_txn), size = tsize + env->me_maxdbs * + (sizeof(MDB_db)+sizeof(MDB_cursor *)+sizeof(unsigned int)+1); + if ((env->me_pbuf = calloc(1, env->me_psize)) && + (txn = calloc(1, size))) + { + txn->mt_dbs = (MDB_db *)((char *)txn + tsize); + txn->mt_cursors = (MDB_cursor **)(txn->mt_dbs + env->me_maxdbs); + txn->mt_dbiseqs = (unsigned int *)(txn->mt_cursors + env->me_maxdbs); + txn->mt_dbflags = (unsigned char *)(txn->mt_dbiseqs + env->me_maxdbs); + txn->mt_env = env; + txn->mt_dbxs = env->me_dbxs; + txn->mt_flags = MDB_TXN_FINISHED; + env->me_txn0 = txn; + } else { + rc = ENOMEM; + } + } + } + +leave: + if (rc) { + mdb_env_close0(env, excl); + } + mdb_fname_destroy(fname); + return rc; +} + +/** Destroy resources from mdb_env_open(), clear our readers & DBIs */ +static void ESECT +mdb_env_close0(MDB_env *env, int excl) +{ + int i; + + if (!(env->me_flags & MDB_ENV_ACTIVE)) + return; + + /* Doing this here since me_dbxs may not exist during mdb_env_close */ + if (env->me_dbxs) { + for (i = env->me_maxdbs; --i >= CORE_DBS; ) + free(env->me_dbxs[i].md_name.mv_data); + free(env->me_dbxs); + } + + free(env->me_pbuf); + free(env->me_dbiseqs); + free(env->me_dbflags); + free(env->me_path); + free(env->me_dirty_list); + free(env->me_txn0); + mdb_midl_free(env->me_free_pgs); + + if (env->me_flags & MDB_ENV_TXKEY) { + pthread_key_delete(env->me_txkey); +#ifdef _WIN32 + /* Delete our key from the global list */ + for (i=0; i<mdb_tls_nkeys; i++) + if (mdb_tls_keys[i] == env->me_txkey) { + mdb_tls_keys[i] = mdb_tls_keys[mdb_tls_nkeys-1]; + mdb_tls_nkeys--; + break; + } +#endif + } + + if (env->me_map) { + munmap(env->me_map, env->me_mapsize); + } + if (env->me_mfd != INVALID_HANDLE_VALUE) + (void) close(env->me_mfd); + if (env->me_fd != INVALID_HANDLE_VALUE) + (void) close(env->me_fd); + if (env->me_txns) { + MDB_PID_T pid = getpid(); + /* Clearing readers is done in this function because + * me_txkey with its destructor must be disabled first. + * + * We skip the the reader mutex, so we touch only + * data owned by this process (me_close_readers and + * our readers), and clear each reader atomically. + */ + for (i = env->me_close_readers; --i >= 0; ) + if (env->me_txns->mti_readers[i].mr_pid == pid) + env->me_txns->mti_readers[i].mr_pid = 0; +#ifdef _WIN32 + if (env->me_rmutex) { + CloseHandle(env->me_rmutex); + if (env->me_wmutex) CloseHandle(env->me_wmutex); + } + /* Windows automatically destroys the mutexes when + * the last handle closes. + */ +#elif defined(MDB_USE_POSIX_SEM) + if (env->me_rmutex != SEM_FAILED) { + sem_close(env->me_rmutex); + if (env->me_wmutex != SEM_FAILED) + sem_close(env->me_wmutex); + /* If we have the filelock: If we are the + * only remaining user, clean up semaphores. + */ + if (excl == 0) + mdb_env_excl_lock(env, &excl); + if (excl > 0) { + sem_unlink(env->me_txns->mti_rmname); + sem_unlink(env->me_txns->mti_wmname); + } + } +#elif defined(MDB_ROBUST_SUPPORTED) + /* If we have the filelock: If we are the + * only remaining user, clean up robust + * mutexes. + */ + if (excl == 0) + mdb_env_excl_lock(env, &excl); + if (excl > 0) { + pthread_mutex_destroy(env->me_txns->mti_rmutex); + pthread_mutex_destroy(env->me_txns->mti_wmutex); + } +#endif + munmap((void *)env->me_txns, (env->me_maxreaders-1)*sizeof(MDB_reader)+sizeof(MDB_txninfo)); + } + if (env->me_lfd != INVALID_HANDLE_VALUE) { +#ifdef _WIN32 + if (excl >= 0) { + /* Unlock the lockfile. Windows would have unlocked it + * after closing anyway, but not necessarily at once. + */ + UnlockFile(env->me_lfd, 0, 0, 1, 0); + } +#endif + (void) close(env->me_lfd); + } + + env->me_flags &= ~(MDB_ENV_ACTIVE|MDB_ENV_TXKEY); +} + +void ESECT +mdb_env_close(MDB_env *env) +{ + MDB_page *dp; + + if (env == NULL) + return; + + VGMEMP_DESTROY(env); + while ((dp = env->me_dpages) != NULL) { + VGMEMP_DEFINED(&dp->mp_next, sizeof(dp->mp_next)); + env->me_dpages = dp->mp_next; + free(dp); + } + + mdb_env_close0(env, 0); + free(env); +} + +/** Compare two items pointing at aligned size_t's */ +static int +mdb_cmp_long(const MDB_val *a, const MDB_val *b) +{ + return (*(size_t *)a->mv_data < *(size_t *)b->mv_data) ? -1 : + *(size_t *)a->mv_data > *(size_t *)b->mv_data; +} + +/** Compare two items pointing at aligned unsigned int's. + * + * This is also set as #MDB_INTEGERDUP|#MDB_DUPFIXED's #MDB_dbx.%md_dcmp, + * but #mdb_cmp_clong() is called instead if the data type is size_t. + */ +static int +mdb_cmp_int(const MDB_val *a, const MDB_val *b) +{ + return (*(unsigned int *)a->mv_data < *(unsigned int *)b->mv_data) ? -1 : + *(unsigned int *)a->mv_data > *(unsigned int *)b->mv_data; +} + +/** Compare two items pointing at unsigned ints of unknown alignment. + * Nodes and keys are guaranteed to be 2-byte aligned. + */ +static int +mdb_cmp_cint(const MDB_val *a, const MDB_val *b) +{ +#if BYTE_ORDER == LITTLE_ENDIAN + unsigned short *u, *c; + int x; + + u = (unsigned short *) ((char *) a->mv_data + a->mv_size); + c = (unsigned short *) ((char *) b->mv_data + a->mv_size); + do { + x = *--u - *--c; + } while(!x && u > (unsigned short *)a->mv_data); + return x; +#else + unsigned short *u, *c, *end; + int x; + + end = (unsigned short *) ((char *) a->mv_data + a->mv_size); + u = (unsigned short *)a->mv_data; + c = (unsigned short *)b->mv_data; + do { + x = *u++ - *c++; + } while(!x && u < end); + return x; +#endif +} + +/** Compare two items lexically */ +static int +mdb_cmp_memn(const MDB_val *a, const MDB_val *b) +{ + int diff; + ssize_t len_diff; + unsigned int len; + + len = a->mv_size; + len_diff = (ssize_t) a->mv_size - (ssize_t) b->mv_size; + if (len_diff > 0) { + len = b->mv_size; + len_diff = 1; + } + + diff = memcmp(a->mv_data, b->mv_data, len); + return diff ? diff : len_diff<0 ? -1 : len_diff; +} + +/** Compare two items in reverse byte order */ +static int +mdb_cmp_memnr(const MDB_val *a, const MDB_val *b) +{ + const unsigned char *p1, *p2, *p1_lim; + ssize_t len_diff; + int diff; + + p1_lim = (const unsigned char *)a->mv_data; + p1 = (const unsigned char *)a->mv_data + a->mv_size; + p2 = (const unsigned char *)b->mv_data + b->mv_size; + + len_diff = (ssize_t) a->mv_size - (ssize_t) b->mv_size; + if (len_diff > 0) { + p1_lim += len_diff; + len_diff = 1; + } + + while (p1 > p1_lim) { + diff = *--p1 - *--p2; + if (diff) + return diff; + } + return len_diff<0 ? -1 : len_diff; +} + +/** Search for key within a page, using binary search. + * Returns the smallest entry larger or equal to the key. + * If exactp is non-null, stores whether the found entry was an exact match + * in *exactp (1 or 0). + * Updates the cursor index with the index of the found entry. + * If no entry larger or equal to the key is found, returns NULL. + */ +static MDB_node * +mdb_node_search(MDB_cursor *mc, MDB_val *key, int *exactp) +{ + unsigned int i = 0, nkeys; + int low, high; + int rc = 0; + MDB_page *mp = mc->mc_pg[mc->mc_top]; + MDB_node *node = NULL; + MDB_val nodekey; + MDB_cmp_func *cmp; + DKBUF; + + nkeys = NUMKEYS(mp); + + DPRINTF(("searching %u keys in %s %spage %"Z"u", + nkeys, IS_LEAF(mp) ? "leaf" : "branch", IS_SUBP(mp) ? "sub-" : "", + mdb_dbg_pgno(mp))); + + low = IS_LEAF(mp) ? 0 : 1; + high = nkeys - 1; + cmp = mc->mc_dbx->md_cmp; + + /* Branch pages have no data, so if using integer keys, + * alignment is guaranteed. Use faster mdb_cmp_int. + */ + if (cmp == mdb_cmp_cint && IS_BRANCH(mp)) { + if (NODEPTR(mp, 1)->mn_ksize == sizeof(size_t)) + cmp = mdb_cmp_long; + else + cmp = mdb_cmp_int; + } + + if (IS_LEAF2(mp)) { + nodekey.mv_size = mc->mc_db->md_pad; + node = NODEPTR(mp, 0); /* fake */ + while (low <= high) { + i = (low + high) >> 1; + nodekey.mv_data = LEAF2KEY(mp, i, nodekey.mv_size); + rc = cmp(key, &nodekey); + DPRINTF(("found leaf index %u [%s], rc = %i", + i, DKEY(&nodekey), rc)); + if (rc == 0) + break; + if (rc > 0) + low = i + 1; + else + high = i - 1; + } + } else { + while (low <= high) { + i = (low + high) >> 1; + + node = NODEPTR(mp, i); + nodekey.mv_size = NODEKSZ(node); + nodekey.mv_data = NODEKEY(node); + + rc = cmp(key, &nodekey); +#if MDB_DEBUG + if (IS_LEAF(mp)) + DPRINTF(("found leaf index %u [%s], rc = %i", + i, DKEY(&nodekey), rc)); + else + DPRINTF(("found branch index %u [%s -> %"Z"u], rc = %i", + i, DKEY(&nodekey), NODEPGNO(node), rc)); +#endif + if (rc == 0) + break; + if (rc > 0) + low = i + 1; + else + high = i - 1; + } + } + + if (rc > 0) { /* Found entry is less than the key. */ + i++; /* Skip to get the smallest entry larger than key. */ + if (!IS_LEAF2(mp)) + node = NODEPTR(mp, i); + } + if (exactp) + *exactp = (rc == 0 && nkeys > 0); + /* store the key index */ + mc->mc_ki[mc->mc_top] = i; + if (i >= nkeys) + /* There is no entry larger or equal to the key. */ + return NULL; + + /* nodeptr is fake for LEAF2 */ + return node; +} + +#if 0 +static void +mdb_cursor_adjust(MDB_cursor *mc, func) +{ + MDB_cursor *m2; + + for (m2 = mc->mc_txn->mt_cursors[mc->mc_dbi]; m2; m2=m2->mc_next) { + if (m2->mc_pg[m2->mc_top] == mc->mc_pg[mc->mc_top]) { + func(mc, m2); + } + } +} +#endif + +/** Pop a page off the top of the cursor's stack. */ +static void +mdb_cursor_pop(MDB_cursor *mc) +{ + if (mc->mc_snum) { + DPRINTF(("popping page %"Z"u off db %d cursor %p", + mc->mc_pg[mc->mc_top]->mp_pgno, DDBI(mc), (void *) mc)); + + mc->mc_snum--; + if (mc->mc_snum) { + mc->mc_top--; + } else { + mc->mc_flags &= ~C_INITIALIZED; + } + } +} + +/** Push a page onto the top of the cursor's stack. + * Set #MDB_TXN_ERROR on failure. + */ +static int +mdb_cursor_push(MDB_cursor *mc, MDB_page *mp) +{ + DPRINTF(("pushing page %"Z"u on db %d cursor %p", mp->mp_pgno, + DDBI(mc), (void *) mc)); + + if (mc->mc_snum >= CURSOR_STACK) { + mc->mc_txn->mt_flags |= MDB_TXN_ERROR; + return MDB_CURSOR_FULL; + } + + mc->mc_top = mc->mc_snum++; + mc->mc_pg[mc->mc_top] = mp; + mc->mc_ki[mc->mc_top] = 0; + + return MDB_SUCCESS; +} + +/** Find the address of the page corresponding to a given page number. + * Set #MDB_TXN_ERROR on failure. + * @param[in] mc the cursor accessing the page. + * @param[in] pgno the page number for the page to retrieve. + * @param[out] ret address of a pointer where the page's address will be stored. + * @param[out] lvl dirty_list inheritance level of found page. 1=current txn, 0=mapped page. + * @return 0 on success, non-zero on failure. + */ +static int +mdb_page_get(MDB_cursor *mc, pgno_t pgno, MDB_page **ret, int *lvl) +{ + MDB_txn *txn = mc->mc_txn; + MDB_env *env = txn->mt_env; + MDB_page *p = NULL; + int level; + + if (! (txn->mt_flags & (MDB_TXN_RDONLY|MDB_TXN_WRITEMAP))) { + MDB_txn *tx2 = txn; + level = 1; + do { + MDB_ID2L dl = tx2->mt_u.dirty_list; + unsigned x; + /* Spilled pages were dirtied in this txn and flushed + * because the dirty list got full. Bring this page + * back in from the map (but don't unspill it here, + * leave that unless page_touch happens again). + */ + if (tx2->mt_spill_pgs) { + MDB_ID pn = pgno << 1; + x = mdb_midl_search(tx2->mt_spill_pgs, pn); + if (x <= tx2->mt_spill_pgs[0] && tx2->mt_spill_pgs[x] == pn) { + p = (MDB_page *)(env->me_map + env->me_psize * pgno); + goto done; + } + } + if (dl[0].mid) { + unsigned x = mdb_mid2l_search(dl, pgno); + if (x <= dl[0].mid && dl[x].mid == pgno) { + p = dl[x].mptr; + goto done; + } + } + level++; + } while ((tx2 = tx2->mt_parent) != NULL); + } + + if (pgno < txn->mt_next_pgno) { + level = 0; + p = (MDB_page *)(env->me_map + env->me_psize * pgno); + } else { + DPRINTF(("page %"Z"u not found", pgno)); + txn->mt_flags |= MDB_TXN_ERROR; + return MDB_PAGE_NOTFOUND; + } + +done: + *ret = p; + if (lvl) + *lvl = level; + return MDB_SUCCESS; +} + +/** Finish #mdb_page_search() / #mdb_page_search_lowest(). + * The cursor is at the root page, set up the rest of it. + */ +static int +mdb_page_search_root(MDB_cursor *mc, MDB_val *key, int flags) +{ + MDB_page *mp = mc->mc_pg[mc->mc_top]; + int rc; + DKBUF; + + while (IS_BRANCH(mp)) { + MDB_node *node; + indx_t i; + + DPRINTF(("branch page %"Z"u has %u keys", mp->mp_pgno, NUMKEYS(mp))); + /* Don't assert on branch pages in the FreeDB. We can get here + * while in the process of rebalancing a FreeDB branch page; we must + * let that proceed. ITS#8336 + */ + mdb_cassert(mc, !mc->mc_dbi || NUMKEYS(mp) > 1); + DPRINTF(("found index 0 to page %"Z"u", NODEPGNO(NODEPTR(mp, 0)))); + + if (flags & (MDB_PS_FIRST|MDB_PS_LAST)) { + i = 0; + if (flags & MDB_PS_LAST) { + i = NUMKEYS(mp) - 1; + /* if already init'd, see if we're already in right place */ + if (mc->mc_flags & C_INITIALIZED) { + if (mc->mc_ki[mc->mc_top] == i) { + mc->mc_top = mc->mc_snum++; + mp = mc->mc_pg[mc->mc_top]; + goto ready; + } + } + } + } else { + int exact; + node = mdb_node_search(mc, key, &exact); + if (node == NULL) + i = NUMKEYS(mp) - 1; + else { + i = mc->mc_ki[mc->mc_top]; + if (!exact) { + mdb_cassert(mc, i > 0); + i--; + } + } + DPRINTF(("following index %u for key [%s]", i, DKEY(key))); + } + + mdb_cassert(mc, i < NUMKEYS(mp)); + node = NODEPTR(mp, i); + + if ((rc = mdb_page_get(mc, NODEPGNO(node), &mp, NULL)) != 0) + return rc; + + mc->mc_ki[mc->mc_top] = i; + if ((rc = mdb_cursor_push(mc, mp))) + return rc; + +ready: + if (flags & MDB_PS_MODIFY) { + if ((rc = mdb_page_touch(mc)) != 0) + return rc; + mp = mc->mc_pg[mc->mc_top]; + } + } + + if (!IS_LEAF(mp)) { + DPRINTF(("internal error, index points to a %02X page!?", + mp->mp_flags)); + mc->mc_txn->mt_flags |= MDB_TXN_ERROR; + return MDB_CORRUPTED; + } + + DPRINTF(("found leaf page %"Z"u for key [%s]", mp->mp_pgno, + key ? DKEY(key) : "null")); + mc->mc_flags |= C_INITIALIZED; + mc->mc_flags &= ~C_EOF; + + return MDB_SUCCESS; +} + +/** Search for the lowest key under the current branch page. + * This just bypasses a NUMKEYS check in the current page + * before calling mdb_page_search_root(), because the callers + * are all in situations where the current page is known to + * be underfilled. + */ +static int +mdb_page_search_lowest(MDB_cursor *mc) +{ + MDB_page *mp = mc->mc_pg[mc->mc_top]; + MDB_node *node = NODEPTR(mp, 0); + int rc; + + if ((rc = mdb_page_get(mc, NODEPGNO(node), &mp, NULL)) != 0) + return rc; + + mc->mc_ki[mc->mc_top] = 0; + if ((rc = mdb_cursor_push(mc, mp))) + return rc; + return mdb_page_search_root(mc, NULL, MDB_PS_FIRST); +} + +/** Search for the page a given key should be in. + * Push it and its parent pages on the cursor stack. + * @param[in,out] mc the cursor for this operation. + * @param[in] key the key to search for, or NULL for first/last page. + * @param[in] flags If MDB_PS_MODIFY is set, visited pages in the DB + * are touched (updated with new page numbers). + * If MDB_PS_FIRST or MDB_PS_LAST is set, find first or last leaf. + * This is used by #mdb_cursor_first() and #mdb_cursor_last(). + * If MDB_PS_ROOTONLY set, just fetch root node, no further lookups. + * @return 0 on success, non-zero on failure. + */ +static int +mdb_page_search(MDB_cursor *mc, MDB_val *key, int flags) +{ + int rc; + pgno_t root; + + /* Make sure the txn is still viable, then find the root from + * the txn's db table and set it as the root of the cursor's stack. + */ + if (mc->mc_txn->mt_flags & MDB_TXN_BLOCKED) { + DPUTS("transaction may not be used now"); + return MDB_BAD_TXN; + } else { + /* Make sure we're using an up-to-date root */ + if (*mc->mc_dbflag & DB_STALE) { + MDB_cursor mc2; + if (TXN_DBI_CHANGED(mc->mc_txn, mc->mc_dbi)) + return MDB_BAD_DBI; + mdb_cursor_init(&mc2, mc->mc_txn, MAIN_DBI, NULL); + rc = mdb_page_search(&mc2, &mc->mc_dbx->md_name, 0); + if (rc) + return rc; + { + MDB_val data; + int exact = 0; + uint16_t flags; + MDB_node *leaf = mdb_node_search(&mc2, + &mc->mc_dbx->md_name, &exact); + if (!exact) + return MDB_NOTFOUND; + if ((leaf->mn_flags & (F_DUPDATA|F_SUBDATA)) != F_SUBDATA) + return MDB_INCOMPATIBLE; /* not a named DB */ + rc = mdb_node_read(&mc2, leaf, &data); + if (rc) + return rc; + memcpy(&flags, ((char *) data.mv_data + offsetof(MDB_db, md_flags)), + sizeof(uint16_t)); + /* The txn may not know this DBI, or another process may + * have dropped and recreated the DB with other flags. + */ + if ((mc->mc_db->md_flags & PERSISTENT_FLAGS) != flags) + return MDB_INCOMPATIBLE; + memcpy(mc->mc_db, data.mv_data, sizeof(MDB_db)); + } + *mc->mc_dbflag &= ~DB_STALE; + } + root = mc->mc_db->md_root; + + if (root == P_INVALID) { /* Tree is empty. */ + DPUTS("tree is empty"); + return MDB_NOTFOUND; + } + } + + mdb_cassert(mc, root > 1); + if (!mc->mc_pg[0] || mc->mc_pg[0]->mp_pgno != root) + if ((rc = mdb_page_get(mc, root, &mc->mc_pg[0], NULL)) != 0) + return rc; + + mc->mc_snum = 1; + mc->mc_top = 0; + + DPRINTF(("db %d root page %"Z"u has flags 0x%X", + DDBI(mc), root, mc->mc_pg[0]->mp_flags)); + + if (flags & MDB_PS_MODIFY) { + if ((rc = mdb_page_touch(mc))) + return rc; + } + + if (flags & MDB_PS_ROOTONLY) + return MDB_SUCCESS; + + return mdb_page_search_root(mc, key, flags); +} + +static int +mdb_ovpage_free(MDB_cursor *mc, MDB_page *mp) +{ + MDB_txn *txn = mc->mc_txn; + pgno_t pg = mp->mp_pgno; + unsigned x = 0, ovpages = mp->mp_pages; + MDB_env *env = txn->mt_env; + MDB_IDL sl = txn->mt_spill_pgs; + MDB_ID pn = pg << 1; + int rc; + + DPRINTF(("free ov page %"Z"u (%d)", pg, ovpages)); + /* If the page is dirty or on the spill list we just acquired it, + * so we should give it back to our current free list, if any. + * Otherwise put it onto the list of pages we freed in this txn. + * + * Won't create me_pghead: me_pglast must be inited along with it. + * Unsupported in nested txns: They would need to hide the page + * range in ancestor txns' dirty and spilled lists. + */ + if (env->me_pghead && + !txn->mt_parent && + ((mp->mp_flags & P_DIRTY) || + (sl && (x = mdb_midl_search(sl, pn)) <= sl[0] && sl[x] == pn))) + { + unsigned i, j; + pgno_t *mop; + MDB_ID2 *dl, ix, iy; + rc = mdb_midl_need(&env->me_pghead, ovpages); + if (rc) + return rc; + if (!(mp->mp_flags & P_DIRTY)) { + /* This page is no longer spilled */ + if (x == sl[0]) + sl[0]--; + else + sl[x] |= 1; + goto release; + } + /* Remove from dirty list */ + dl = txn->mt_u.dirty_list; + x = dl[0].mid--; + for (ix = dl[x]; ix.mptr != mp; ix = iy) { + if (x > 1) { + x--; + iy = dl[x]; + dl[x] = ix; + } else { + mdb_cassert(mc, x > 1); + j = ++(dl[0].mid); + dl[j] = ix; /* Unsorted. OK when MDB_TXN_ERROR. */ + txn->mt_flags |= MDB_TXN_ERROR; + return MDB_CORRUPTED; + } + } + txn->mt_dirty_room++; + if (!(env->me_flags & MDB_WRITEMAP)) + mdb_dpage_free(env, mp); +release: + /* Insert in me_pghead */ + mop = env->me_pghead; + j = mop[0] + ovpages; + for (i = mop[0]; i && mop[i] < pg; i--) + mop[j--] = mop[i]; + while (j>i) + mop[j--] = pg++; + mop[0] += ovpages; + } else { + rc = mdb_midl_append_range(&txn->mt_free_pgs, pg, ovpages); + if (rc) + return rc; + } + mc->mc_db->md_overflow_pages -= ovpages; + return 0; +} + +/** Return the data associated with a given node. + * @param[in] mc The cursor for this operation. + * @param[in] leaf The node being read. + * @param[out] data Updated to point to the node's data. + * @return 0 on success, non-zero on failure. + */ +static int +mdb_node_read(MDB_cursor *mc, MDB_node *leaf, MDB_val *data) +{ + MDB_page *omp; /* overflow page */ + pgno_t pgno; + int rc; + + if (!F_ISSET(leaf->mn_flags, F_BIGDATA)) { + data->mv_size = NODEDSZ(leaf); + data->mv_data = NODEDATA(leaf); + return MDB_SUCCESS; + } + + /* Read overflow data. + */ + data->mv_size = NODEDSZ(leaf); + memcpy(&pgno, NODEDATA(leaf), sizeof(pgno)); + if ((rc = mdb_page_get(mc, pgno, &omp, NULL)) != 0) { + DPRINTF(("read overflow page %"Z"u failed", pgno)); + return rc; + } + data->mv_data = METADATA(omp); + + return MDB_SUCCESS; +} + +int +mdb_get(MDB_txn *txn, MDB_dbi dbi, + MDB_val *key, MDB_val *data) +{ + MDB_cursor mc; + MDB_xcursor mx; + int exact = 0; + DKBUF; + + DPRINTF(("===> get db %u key [%s]", dbi, DKEY(key))); + + if (!key || !data || !TXN_DBI_EXIST(txn, dbi, DB_USRVALID)) + return EINVAL; + + if (txn->mt_flags & MDB_TXN_BLOCKED) + return MDB_BAD_TXN; + + mdb_cursor_init(&mc, txn, dbi, &mx); + return mdb_cursor_set(&mc, key, data, MDB_SET, &exact); +} + +/** Find a sibling for a page. + * Replaces the page at the top of the cursor's stack with the + * specified sibling, if one exists. + * @param[in] mc The cursor for this operation. + * @param[in] move_right Non-zero if the right sibling is requested, + * otherwise the left sibling. + * @return 0 on success, non-zero on failure. + */ +static int +mdb_cursor_sibling(MDB_cursor *mc, int move_right) +{ + int rc; + MDB_node *indx; + MDB_page *mp; + + if (mc->mc_snum < 2) { + return MDB_NOTFOUND; /* root has no siblings */ + } + + mdb_cursor_pop(mc); + DPRINTF(("parent page is page %"Z"u, index %u", + mc->mc_pg[mc->mc_top]->mp_pgno, mc->mc_ki[mc->mc_top])); + + if (move_right ? (mc->mc_ki[mc->mc_top] + 1u >= NUMKEYS(mc->mc_pg[mc->mc_top])) + : (mc->mc_ki[mc->mc_top] == 0)) { + DPRINTF(("no more keys left, moving to %s sibling", + move_right ? "right" : "left")); + if ((rc = mdb_cursor_sibling(mc, move_right)) != MDB_SUCCESS) { + /* undo cursor_pop before returning */ + mc->mc_top++; + mc->mc_snum++; + return rc; + } + } else { + if (move_right) + mc->mc_ki[mc->mc_top]++; + else + mc->mc_ki[mc->mc_top]--; + DPRINTF(("just moving to %s index key %u", + move_right ? "right" : "left", mc->mc_ki[mc->mc_top])); + } + mdb_cassert(mc, IS_BRANCH(mc->mc_pg[mc->mc_top])); + + indx = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]); + if ((rc = mdb_page_get(mc, NODEPGNO(indx), &mp, NULL)) != 0) { + /* mc will be inconsistent if caller does mc_snum++ as above */ + mc->mc_flags &= ~(C_INITIALIZED|C_EOF); + return rc; + } + + mdb_cursor_push(mc, mp); + if (!move_right) + mc->mc_ki[mc->mc_top] = NUMKEYS(mp)-1; + + return MDB_SUCCESS; +} + +/** Move the cursor to the next data item. */ +static int +mdb_cursor_next(MDB_cursor *mc, MDB_val *key, MDB_val *data, MDB_cursor_op op) +{ + MDB_page *mp; + MDB_node *leaf; + int rc; + + if ((mc->mc_flags & C_DEL && op == MDB_NEXT_DUP)) + return MDB_NOTFOUND; + + if (!(mc->mc_flags & C_INITIALIZED)) + return mdb_cursor_first(mc, key, data); + + mp = mc->mc_pg[mc->mc_top]; + + if (mc->mc_flags & C_EOF) { + if (mc->mc_ki[mc->mc_top] >= NUMKEYS(mp)-1) + return MDB_NOTFOUND; + mc->mc_flags ^= C_EOF; + } + + if (mc->mc_db->md_flags & MDB_DUPSORT) { + leaf = NODEPTR(mp, mc->mc_ki[mc->mc_top]); + if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { + if (op == MDB_NEXT || op == MDB_NEXT_DUP) { + rc = mdb_cursor_next(&mc->mc_xcursor->mx_cursor, data, NULL, MDB_NEXT); + if (op != MDB_NEXT || rc != MDB_NOTFOUND) { + if (rc == MDB_SUCCESS) + MDB_GET_KEY(leaf, key); + return rc; + } + } + } else { + mc->mc_xcursor->mx_cursor.mc_flags &= ~(C_INITIALIZED|C_EOF); + if (op == MDB_NEXT_DUP) + return MDB_NOTFOUND; + } + } + + DPRINTF(("cursor_next: top page is %"Z"u in cursor %p", + mdb_dbg_pgno(mp), (void *) mc)); + if (mc->mc_flags & C_DEL) { + mc->mc_flags ^= C_DEL; + goto skip; + } + + if (mc->mc_ki[mc->mc_top] + 1u >= NUMKEYS(mp)) { + DPUTS("=====> move to next sibling page"); + if ((rc = mdb_cursor_sibling(mc, 1)) != MDB_SUCCESS) { + mc->mc_flags |= C_EOF; + return rc; + } + mp = mc->mc_pg[mc->mc_top]; + DPRINTF(("next page is %"Z"u, key index %u", mp->mp_pgno, mc->mc_ki[mc->mc_top])); + } else + mc->mc_ki[mc->mc_top]++; + +skip: + DPRINTF(("==> cursor points to page %"Z"u with %u keys, key index %u", + mdb_dbg_pgno(mp), NUMKEYS(mp), mc->mc_ki[mc->mc_top])); + + if (IS_LEAF2(mp)) { + key->mv_size = mc->mc_db->md_pad; + key->mv_data = LEAF2KEY(mp, mc->mc_ki[mc->mc_top], key->mv_size); + return MDB_SUCCESS; + } + + mdb_cassert(mc, IS_LEAF(mp)); + leaf = NODEPTR(mp, mc->mc_ki[mc->mc_top]); + + if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { + mdb_xcursor_init1(mc, leaf); + rc = mdb_cursor_first(&mc->mc_xcursor->mx_cursor, data, NULL); + if (rc != MDB_SUCCESS) + return rc; + } else if (data) { + if ((rc = mdb_node_read(mc, leaf, data)) != MDB_SUCCESS) + return rc; + } + + MDB_GET_KEY(leaf, key); + return MDB_SUCCESS; +} + +/** Move the cursor to the previous data item. */ +static int +mdb_cursor_prev(MDB_cursor *mc, MDB_val *key, MDB_val *data, MDB_cursor_op op) +{ + MDB_page *mp; + MDB_node *leaf; + int rc; + + if (!(mc->mc_flags & C_INITIALIZED)) { + rc = mdb_cursor_last(mc, key, data); + if (rc) + return rc; + mc->mc_ki[mc->mc_top]++; + } + + mp = mc->mc_pg[mc->mc_top]; + + if ((mc->mc_db->md_flags & MDB_DUPSORT) && + mc->mc_ki[mc->mc_top] < NUMKEYS(mp)) { + leaf = NODEPTR(mp, mc->mc_ki[mc->mc_top]); + if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { + if (op == MDB_PREV || op == MDB_PREV_DUP) { + rc = mdb_cursor_prev(&mc->mc_xcursor->mx_cursor, data, NULL, MDB_PREV); + if (op != MDB_PREV || rc != MDB_NOTFOUND) { + if (rc == MDB_SUCCESS) { + MDB_GET_KEY(leaf, key); + mc->mc_flags &= ~C_EOF; + } + return rc; + } + } + } else { + mc->mc_xcursor->mx_cursor.mc_flags &= ~(C_INITIALIZED|C_EOF); + if (op == MDB_PREV_DUP) + return MDB_NOTFOUND; + } + } + + DPRINTF(("cursor_prev: top page is %"Z"u in cursor %p", + mdb_dbg_pgno(mp), (void *) mc)); + + mc->mc_flags &= ~(C_EOF|C_DEL); + + if (mc->mc_ki[mc->mc_top] == 0) { + DPUTS("=====> move to prev sibling page"); + if ((rc = mdb_cursor_sibling(mc, 0)) != MDB_SUCCESS) { + return rc; + } + mp = mc->mc_pg[mc->mc_top]; + mc->mc_ki[mc->mc_top] = NUMKEYS(mp) - 1; + DPRINTF(("prev page is %"Z"u, key index %u", mp->mp_pgno, mc->mc_ki[mc->mc_top])); + } else + mc->mc_ki[mc->mc_top]--; + + DPRINTF(("==> cursor points to page %"Z"u with %u keys, key index %u", + mdb_dbg_pgno(mp), NUMKEYS(mp), mc->mc_ki[mc->mc_top])); + + if (!IS_LEAF(mp)) + return MDB_CORRUPTED; + + if (IS_LEAF2(mp)) { + key->mv_size = mc->mc_db->md_pad; + key->mv_data = LEAF2KEY(mp, mc->mc_ki[mc->mc_top], key->mv_size); + return MDB_SUCCESS; + } + + leaf = NODEPTR(mp, mc->mc_ki[mc->mc_top]); + + if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { + mdb_xcursor_init1(mc, leaf); + rc = mdb_cursor_last(&mc->mc_xcursor->mx_cursor, data, NULL); + if (rc != MDB_SUCCESS) + return rc; + } else if (data) { + if ((rc = mdb_node_read(mc, leaf, data)) != MDB_SUCCESS) + return rc; + } + + MDB_GET_KEY(leaf, key); + return MDB_SUCCESS; +} + +/** Set the cursor on a specific data item. */ +static int +mdb_cursor_set(MDB_cursor *mc, MDB_val *key, MDB_val *data, + MDB_cursor_op op, int *exactp) +{ + int rc; + MDB_page *mp; + MDB_node *leaf = NULL; + DKBUF; + + if (key->mv_size == 0) + return MDB_BAD_VALSIZE; + + if (mc->mc_xcursor) + mc->mc_xcursor->mx_cursor.mc_flags &= ~(C_INITIALIZED|C_EOF); + + /* See if we're already on the right page */ + if (mc->mc_flags & C_INITIALIZED) { + MDB_val nodekey; + + mp = mc->mc_pg[mc->mc_top]; + if (!NUMKEYS(mp)) { + mc->mc_ki[mc->mc_top] = 0; + return MDB_NOTFOUND; + } + if (mp->mp_flags & P_LEAF2) { + nodekey.mv_size = mc->mc_db->md_pad; + nodekey.mv_data = LEAF2KEY(mp, 0, nodekey.mv_size); + } else { + leaf = NODEPTR(mp, 0); + MDB_GET_KEY2(leaf, nodekey); + } + rc = mc->mc_dbx->md_cmp(key, &nodekey); + if (rc == 0) { + /* Probably happens rarely, but first node on the page + * was the one we wanted. + */ + mc->mc_ki[mc->mc_top] = 0; + if (exactp) + *exactp = 1; + goto set1; + } + if (rc > 0) { + unsigned int i; + unsigned int nkeys = NUMKEYS(mp); + if (nkeys > 1) { + if (mp->mp_flags & P_LEAF2) { + nodekey.mv_data = LEAF2KEY(mp, + nkeys-1, nodekey.mv_size); + } else { + leaf = NODEPTR(mp, nkeys-1); + MDB_GET_KEY2(leaf, nodekey); + } + rc = mc->mc_dbx->md_cmp(key, &nodekey); + if (rc == 0) { + /* last node was the one we wanted */ + mc->mc_ki[mc->mc_top] = nkeys-1; + if (exactp) + *exactp = 1; + goto set1; + } + if (rc < 0) { + if (mc->mc_ki[mc->mc_top] < NUMKEYS(mp)) { + /* This is definitely the right page, skip search_page */ + if (mp->mp_flags & P_LEAF2) { + nodekey.mv_data = LEAF2KEY(mp, + mc->mc_ki[mc->mc_top], nodekey.mv_size); + } else { + leaf = NODEPTR(mp, mc->mc_ki[mc->mc_top]); + MDB_GET_KEY2(leaf, nodekey); + } + rc = mc->mc_dbx->md_cmp(key, &nodekey); + if (rc == 0) { + /* current node was the one we wanted */ + if (exactp) + *exactp = 1; + goto set1; + } + } + rc = 0; + mc->mc_flags &= ~C_EOF; + goto set2; + } + } + /* If any parents have right-sibs, search. + * Otherwise, there's nothing further. + */ + for (i=0; i<mc->mc_top; i++) + if (mc->mc_ki[i] < + NUMKEYS(mc->mc_pg[i])-1) + break; + if (i == mc->mc_top) { + /* There are no other pages */ + mc->mc_ki[mc->mc_top] = nkeys; + return MDB_NOTFOUND; + } + } + if (!mc->mc_top) { + /* There are no other pages */ + mc->mc_ki[mc->mc_top] = 0; + if (op == MDB_SET_RANGE && !exactp) { + rc = 0; + goto set1; + } else + return MDB_NOTFOUND; + } + } else { + mc->mc_pg[0] = 0; + } + + rc = mdb_page_search(mc, key, 0); + if (rc != MDB_SUCCESS) + return rc; + + mp = mc->mc_pg[mc->mc_top]; + mdb_cassert(mc, IS_LEAF(mp)); + +set2: + leaf = mdb_node_search(mc, key, exactp); + if (exactp != NULL && !*exactp) { + /* MDB_SET specified and not an exact match. */ + return MDB_NOTFOUND; + } + + if (leaf == NULL) { + DPUTS("===> inexact leaf not found, goto sibling"); + if ((rc = mdb_cursor_sibling(mc, 1)) != MDB_SUCCESS) { + mc->mc_flags |= C_EOF; + return rc; /* no entries matched */ + } + mp = mc->mc_pg[mc->mc_top]; + mdb_cassert(mc, IS_LEAF(mp)); + leaf = NODEPTR(mp, 0); + } + +set1: + mc->mc_flags |= C_INITIALIZED; + mc->mc_flags &= ~C_EOF; + + if (IS_LEAF2(mp)) { + if (op == MDB_SET_RANGE || op == MDB_SET_KEY) { + key->mv_size = mc->mc_db->md_pad; + key->mv_data = LEAF2KEY(mp, mc->mc_ki[mc->mc_top], key->mv_size); + } + return MDB_SUCCESS; + } + + if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { + mdb_xcursor_init1(mc, leaf); + if (op == MDB_SET || op == MDB_SET_KEY || op == MDB_SET_RANGE) { + rc = mdb_cursor_first(&mc->mc_xcursor->mx_cursor, data, NULL); + } else { + int ex2, *ex2p; + if (op == MDB_GET_BOTH) { + ex2p = &ex2; + ex2 = 0; + } else { + ex2p = NULL; + } + rc = mdb_cursor_set(&mc->mc_xcursor->mx_cursor, data, NULL, MDB_SET_RANGE, ex2p); + if (rc != MDB_SUCCESS) + return rc; + } + } else if (data) { + if (op == MDB_GET_BOTH || op == MDB_GET_BOTH_RANGE) { + MDB_val olddata; + MDB_cmp_func *dcmp; + if ((rc = mdb_node_read(mc, leaf, &olddata)) != MDB_SUCCESS) + return rc; + dcmp = mc->mc_dbx->md_dcmp; +#if UINT_MAX < SIZE_MAX + if (dcmp == mdb_cmp_int && olddata.mv_size == sizeof(size_t)) + dcmp = mdb_cmp_clong; +#endif + rc = dcmp(data, &olddata); + if (rc) { + if (op == MDB_GET_BOTH || rc > 0) + return MDB_NOTFOUND; + rc = 0; + } + *data = olddata; + + } else { + if (mc->mc_xcursor) + mc->mc_xcursor->mx_cursor.mc_flags &= ~(C_INITIALIZED|C_EOF); + if ((rc = mdb_node_read(mc, leaf, data)) != MDB_SUCCESS) + return rc; + } + } + + /* The key already matches in all other cases */ + if (op == MDB_SET_RANGE || op == MDB_SET_KEY) + MDB_GET_KEY(leaf, key); + DPRINTF(("==> cursor placed on key [%s]", DKEY(key))); + + return rc; +} + +/** Move the cursor to the first item in the database. */ +static int +mdb_cursor_first(MDB_cursor *mc, MDB_val *key, MDB_val *data) +{ + int rc; + MDB_node *leaf; + + if (mc->mc_xcursor) + mc->mc_xcursor->mx_cursor.mc_flags &= ~(C_INITIALIZED|C_EOF); + + if (!(mc->mc_flags & C_INITIALIZED) || mc->mc_top) { + rc = mdb_page_search(mc, NULL, MDB_PS_FIRST); + if (rc != MDB_SUCCESS) + return rc; + } + mdb_cassert(mc, IS_LEAF(mc->mc_pg[mc->mc_top])); + + leaf = NODEPTR(mc->mc_pg[mc->mc_top], 0); + mc->mc_flags |= C_INITIALIZED; + mc->mc_flags &= ~C_EOF; + + mc->mc_ki[mc->mc_top] = 0; + + if (IS_LEAF2(mc->mc_pg[mc->mc_top])) { + if ( key ) { + key->mv_size = mc->mc_db->md_pad; + key->mv_data = LEAF2KEY(mc->mc_pg[mc->mc_top], 0, key->mv_size); + } + return MDB_SUCCESS; + } + + if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { + mdb_xcursor_init1(mc, leaf); + rc = mdb_cursor_first(&mc->mc_xcursor->mx_cursor, data, NULL); + if (rc) + return rc; + } else if (data) { + if ((rc = mdb_node_read(mc, leaf, data)) != MDB_SUCCESS) + return rc; + } + + MDB_GET_KEY(leaf, key); + return MDB_SUCCESS; +} + +/** Move the cursor to the last item in the database. */ +static int +mdb_cursor_last(MDB_cursor *mc, MDB_val *key, MDB_val *data) +{ + int rc; + MDB_node *leaf; + + if (mc->mc_xcursor) + mc->mc_xcursor->mx_cursor.mc_flags &= ~(C_INITIALIZED|C_EOF); + + if (!(mc->mc_flags & C_INITIALIZED) || mc->mc_top) { + rc = mdb_page_search(mc, NULL, MDB_PS_LAST); + if (rc != MDB_SUCCESS) + return rc; + } + mdb_cassert(mc, IS_LEAF(mc->mc_pg[mc->mc_top])); + + mc->mc_ki[mc->mc_top] = NUMKEYS(mc->mc_pg[mc->mc_top]) - 1; + mc->mc_flags |= C_INITIALIZED|C_EOF; + leaf = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]); + + if (IS_LEAF2(mc->mc_pg[mc->mc_top])) { + if (key) { + key->mv_size = mc->mc_db->md_pad; + key->mv_data = LEAF2KEY(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top], key->mv_size); + } + return MDB_SUCCESS; + } + + if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { + mdb_xcursor_init1(mc, leaf); + rc = mdb_cursor_last(&mc->mc_xcursor->mx_cursor, data, NULL); + if (rc) + return rc; + } else if (data) { + if ((rc = mdb_node_read(mc, leaf, data)) != MDB_SUCCESS) + return rc; + } + + MDB_GET_KEY(leaf, key); + return MDB_SUCCESS; +} + +int +mdb_cursor_get(MDB_cursor *mc, MDB_val *key, MDB_val *data, + MDB_cursor_op op) +{ + int rc; + int exact = 0; + int (*mfunc)(MDB_cursor *mc, MDB_val *key, MDB_val *data); + + if (mc == NULL) + return EINVAL; + + if (mc->mc_txn->mt_flags & MDB_TXN_BLOCKED) + return MDB_BAD_TXN; + + switch (op) { + case MDB_GET_CURRENT: + if (!(mc->mc_flags & C_INITIALIZED)) { + rc = EINVAL; + } else { + MDB_page *mp = mc->mc_pg[mc->mc_top]; + int nkeys = NUMKEYS(mp); + if (!nkeys || mc->mc_ki[mc->mc_top] >= nkeys) { + mc->mc_ki[mc->mc_top] = nkeys; + rc = MDB_NOTFOUND; + break; + } + rc = MDB_SUCCESS; + if (IS_LEAF2(mp)) { + key->mv_size = mc->mc_db->md_pad; + key->mv_data = LEAF2KEY(mp, mc->mc_ki[mc->mc_top], key->mv_size); + } else { + MDB_node *leaf = NODEPTR(mp, mc->mc_ki[mc->mc_top]); + MDB_GET_KEY(leaf, key); + if (data) { + if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { + rc = mdb_cursor_get(&mc->mc_xcursor->mx_cursor, data, NULL, MDB_GET_CURRENT); + } else { + rc = mdb_node_read(mc, leaf, data); + } + } + } + } + break; + case MDB_GET_BOTH: + case MDB_GET_BOTH_RANGE: + if (data == NULL) { + rc = EINVAL; + break; + } + if (mc->mc_xcursor == NULL) { + rc = MDB_INCOMPATIBLE; + break; + } + /* FALLTHRU */ + case MDB_SET: + case MDB_SET_KEY: + case MDB_SET_RANGE: + if (key == NULL) { + rc = EINVAL; + } else { + rc = mdb_cursor_set(mc, key, data, op, + op == MDB_SET_RANGE ? NULL : &exact); + } + break; + case MDB_GET_MULTIPLE: + if (data == NULL || !(mc->mc_flags & C_INITIALIZED)) { + rc = EINVAL; + break; + } + if (!(mc->mc_db->md_flags & MDB_DUPFIXED)) { + rc = MDB_INCOMPATIBLE; + break; + } + rc = MDB_SUCCESS; + if (!(mc->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED) || + (mc->mc_xcursor->mx_cursor.mc_flags & C_EOF)) + break; + goto fetchm; + case MDB_NEXT_MULTIPLE: + if (data == NULL) { + rc = EINVAL; + break; + } + if (!(mc->mc_db->md_flags & MDB_DUPFIXED)) { + rc = MDB_INCOMPATIBLE; + break; + } + rc = mdb_cursor_next(mc, key, data, MDB_NEXT_DUP); + if (rc == MDB_SUCCESS) { + if (mc->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED) { + MDB_cursor *mx; +fetchm: + mx = &mc->mc_xcursor->mx_cursor; + data->mv_size = NUMKEYS(mx->mc_pg[mx->mc_top]) * + mx->mc_db->md_pad; + data->mv_data = METADATA(mx->mc_pg[mx->mc_top]); + mx->mc_ki[mx->mc_top] = NUMKEYS(mx->mc_pg[mx->mc_top])-1; + } else { + rc = MDB_NOTFOUND; + } + } + break; + case MDB_PREV_MULTIPLE: + if (data == NULL) { + rc = EINVAL; + break; + } + if (!(mc->mc_db->md_flags & MDB_DUPFIXED)) { + rc = MDB_INCOMPATIBLE; + break; + } + if (!(mc->mc_flags & C_INITIALIZED)) + rc = mdb_cursor_last(mc, key, data); + else + rc = MDB_SUCCESS; + if (rc == MDB_SUCCESS) { + MDB_cursor *mx = &mc->mc_xcursor->mx_cursor; + if (mx->mc_flags & C_INITIALIZED) { + rc = mdb_cursor_sibling(mx, 0); + if (rc == MDB_SUCCESS) + goto fetchm; + } else { + rc = MDB_NOTFOUND; + } + } + break; + case MDB_NEXT: + case MDB_NEXT_DUP: + case MDB_NEXT_NODUP: + rc = mdb_cursor_next(mc, key, data, op); + break; + case MDB_PREV: + case MDB_PREV_DUP: + case MDB_PREV_NODUP: + rc = mdb_cursor_prev(mc, key, data, op); + break; + case MDB_FIRST: + rc = mdb_cursor_first(mc, key, data); + break; + case MDB_FIRST_DUP: + mfunc = mdb_cursor_first; + mmove: + if (data == NULL || !(mc->mc_flags & C_INITIALIZED)) { + rc = EINVAL; + break; + } + if (mc->mc_xcursor == NULL) { + rc = MDB_INCOMPATIBLE; + break; + } + if (mc->mc_ki[mc->mc_top] >= NUMKEYS(mc->mc_pg[mc->mc_top])) { + mc->mc_ki[mc->mc_top] = NUMKEYS(mc->mc_pg[mc->mc_top]); + rc = MDB_NOTFOUND; + break; + } + { + MDB_node *leaf = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]); + if (!F_ISSET(leaf->mn_flags, F_DUPDATA)) { + MDB_GET_KEY(leaf, key); + rc = mdb_node_read(mc, leaf, data); + break; + } + } + if (!(mc->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED)) { + rc = EINVAL; + break; + } + rc = mfunc(&mc->mc_xcursor->mx_cursor, data, NULL); + break; + case MDB_LAST: + rc = mdb_cursor_last(mc, key, data); + break; + case MDB_LAST_DUP: + mfunc = mdb_cursor_last; + goto mmove; + default: + DPRINTF(("unhandled/unimplemented cursor operation %u", op)); + rc = EINVAL; + break; + } + + if (mc->mc_flags & C_DEL) + mc->mc_flags ^= C_DEL; + + return rc; +} + +/** Touch all the pages in the cursor stack. Set mc_top. + * Makes sure all the pages are writable, before attempting a write operation. + * @param[in] mc The cursor to operate on. + */ +static int +mdb_cursor_touch(MDB_cursor *mc) +{ + int rc = MDB_SUCCESS; + + if (mc->mc_dbi >= CORE_DBS && !(*mc->mc_dbflag & (DB_DIRTY|DB_DUPDATA))) { + /* Touch DB record of named DB */ + MDB_cursor mc2; + MDB_xcursor mcx; + if (TXN_DBI_CHANGED(mc->mc_txn, mc->mc_dbi)) + return MDB_BAD_DBI; + mdb_cursor_init(&mc2, mc->mc_txn, MAIN_DBI, &mcx); + rc = mdb_page_search(&mc2, &mc->mc_dbx->md_name, MDB_PS_MODIFY); + if (rc) + return rc; + *mc->mc_dbflag |= DB_DIRTY; + } + mc->mc_top = 0; + if (mc->mc_snum) { + do { + rc = mdb_page_touch(mc); + } while (!rc && ++(mc->mc_top) < mc->mc_snum); + mc->mc_top = mc->mc_snum-1; + } + return rc; +} + +/** Do not spill pages to disk if txn is getting full, may fail instead */ +#define MDB_NOSPILL 0x8000 + +int +mdb_cursor_put(MDB_cursor *mc, MDB_val *key, MDB_val *data, + unsigned int flags) +{ + MDB_env *env; + MDB_node *leaf = NULL; + MDB_page *fp, *mp, *sub_root = NULL; + uint16_t fp_flags; + MDB_val xdata, *rdata, dkey, olddata; + MDB_db dummy; + int do_sub = 0, insert_key, insert_data; + unsigned int mcount = 0, dcount = 0, nospill; + size_t nsize; + int rc, rc2; + unsigned int nflags; + DKBUF; + + if (mc == NULL || key == NULL) + return EINVAL; + + env = mc->mc_txn->mt_env; + + /* Check this first so counter will always be zero on any + * early failures. + */ + if (flags & MDB_MULTIPLE) { + dcount = data[1].mv_size; + data[1].mv_size = 0; + if (!F_ISSET(mc->mc_db->md_flags, MDB_DUPFIXED)) + return MDB_INCOMPATIBLE; + } + + nospill = flags & MDB_NOSPILL; + flags &= ~MDB_NOSPILL; + + if (mc->mc_txn->mt_flags & (MDB_TXN_RDONLY|MDB_TXN_BLOCKED)) + return (mc->mc_txn->mt_flags & MDB_TXN_RDONLY) ? EACCES : MDB_BAD_TXN; + + if (key->mv_size-1 >= ENV_MAXKEY(env)) + return MDB_BAD_VALSIZE; + +#if SIZE_MAX > MAXDATASIZE + if (data->mv_size > ((mc->mc_db->md_flags & MDB_DUPSORT) ? ENV_MAXKEY(env) : MAXDATASIZE)) + return MDB_BAD_VALSIZE; +#else + if ((mc->mc_db->md_flags & MDB_DUPSORT) && data->mv_size > ENV_MAXKEY(env)) + return MDB_BAD_VALSIZE; +#endif + + DPRINTF(("==> put db %d key [%s], size %"Z"u, data size %"Z"u", + DDBI(mc), DKEY(key), key ? key->mv_size : 0, data->mv_size)); + + dkey.mv_size = 0; + + if (flags == MDB_CURRENT) { + if (!(mc->mc_flags & C_INITIALIZED)) + return EINVAL; + rc = MDB_SUCCESS; + } else if (mc->mc_db->md_root == P_INVALID) { + /* new database, cursor has nothing to point to */ + mc->mc_snum = 0; + mc->mc_top = 0; + mc->mc_flags &= ~C_INITIALIZED; + rc = MDB_NO_ROOT; + } else { + int exact = 0; + MDB_val d2; + if (flags & MDB_APPEND) { + MDB_val k2; + rc = mdb_cursor_last(mc, &k2, &d2); + if (rc == 0) { + rc = mc->mc_dbx->md_cmp(key, &k2); + if (rc > 0) { + rc = MDB_NOTFOUND; + mc->mc_ki[mc->mc_top]++; + } else { + /* new key is <= last key */ + rc = MDB_KEYEXIST; + } + } + } else { + rc = mdb_cursor_set(mc, key, &d2, MDB_SET, &exact); + } + if ((flags & MDB_NOOVERWRITE) && rc == 0) { + DPRINTF(("duplicate key [%s]", DKEY(key))); + *data = d2; + return MDB_KEYEXIST; + } + if (rc && rc != MDB_NOTFOUND) + return rc; + } + + if (mc->mc_flags & C_DEL) + mc->mc_flags ^= C_DEL; + + /* Cursor is positioned, check for room in the dirty list */ + if (!nospill) { + if (flags & MDB_MULTIPLE) { + rdata = &xdata; + xdata.mv_size = data->mv_size * dcount; + } else { + rdata = data; + } + if ((rc2 = mdb_page_spill(mc, key, rdata))) + return rc2; + } + + if (rc == MDB_NO_ROOT) { + MDB_page *np; + /* new database, write a root leaf page */ + DPUTS("allocating new root leaf page"); + if ((rc2 = mdb_page_new(mc, P_LEAF, 1, &np))) { + return rc2; + } + mdb_cursor_push(mc, np); + mc->mc_db->md_root = np->mp_pgno; + mc->mc_db->md_depth++; + *mc->mc_dbflag |= DB_DIRTY; + if ((mc->mc_db->md_flags & (MDB_DUPSORT|MDB_DUPFIXED)) + == MDB_DUPFIXED) + np->mp_flags |= P_LEAF2; + mc->mc_flags |= C_INITIALIZED; + } else { + /* make sure all cursor pages are writable */ + rc2 = mdb_cursor_touch(mc); + if (rc2) + return rc2; + } + + insert_key = insert_data = rc; + if (insert_key) { + /* The key does not exist */ + DPRINTF(("inserting key at index %i", mc->mc_ki[mc->mc_top])); + if ((mc->mc_db->md_flags & MDB_DUPSORT) && + LEAFSIZE(key, data) > env->me_nodemax) + { + /* Too big for a node, insert in sub-DB. Set up an empty + * "old sub-page" for prep_subDB to expand to a full page. + */ + fp_flags = P_LEAF|P_DIRTY; + fp = env->me_pbuf; + fp->mp_pad = data->mv_size; /* used if MDB_DUPFIXED */ + fp->mp_lower = fp->mp_upper = (PAGEHDRSZ-PAGEBASE); + olddata.mv_size = PAGEHDRSZ; + goto prep_subDB; + } + } else { + /* there's only a key anyway, so this is a no-op */ + if (IS_LEAF2(mc->mc_pg[mc->mc_top])) { + char *ptr; + unsigned int ksize = mc->mc_db->md_pad; + if (key->mv_size != ksize) + return MDB_BAD_VALSIZE; + ptr = LEAF2KEY(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top], ksize); + memcpy(ptr, key->mv_data, ksize); +fix_parent: + /* if overwriting slot 0 of leaf, need to + * update branch key if there is a parent page + */ + if (mc->mc_top && !mc->mc_ki[mc->mc_top]) { + unsigned short dtop = 1; + mc->mc_top--; + /* slot 0 is always an empty key, find real slot */ + while (mc->mc_top && !mc->mc_ki[mc->mc_top]) { + mc->mc_top--; + dtop++; + } + if (mc->mc_ki[mc->mc_top]) + rc2 = mdb_update_key(mc, key); + else + rc2 = MDB_SUCCESS; + mc->mc_top += dtop; + if (rc2) + return rc2; + } + return MDB_SUCCESS; + } + +more: + leaf = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]); + olddata.mv_size = NODEDSZ(leaf); + olddata.mv_data = NODEDATA(leaf); + + /* DB has dups? */ + if (F_ISSET(mc->mc_db->md_flags, MDB_DUPSORT)) { + /* Prepare (sub-)page/sub-DB to accept the new item, + * if needed. fp: old sub-page or a header faking + * it. mp: new (sub-)page. offset: growth in page + * size. xdata: node data with new page or DB. + */ + unsigned i, offset = 0; + mp = fp = xdata.mv_data = env->me_pbuf; + mp->mp_pgno = mc->mc_pg[mc->mc_top]->mp_pgno; + + /* Was a single item before, must convert now */ + if (!F_ISSET(leaf->mn_flags, F_DUPDATA)) { + MDB_cmp_func *dcmp; + /* Just overwrite the current item */ + if (flags == MDB_CURRENT) + goto current; + dcmp = mc->mc_dbx->md_dcmp; +#if UINT_MAX < SIZE_MAX + if (dcmp == mdb_cmp_int && olddata.mv_size == sizeof(size_t)) + dcmp = mdb_cmp_clong; +#endif + /* does data match? */ + if (!dcmp(data, &olddata)) { + if (flags & (MDB_NODUPDATA|MDB_APPENDDUP)) + return MDB_KEYEXIST; + /* overwrite it */ + goto current; + } + + /* Back up original data item */ + dkey.mv_size = olddata.mv_size; + dkey.mv_data = memcpy(fp+1, olddata.mv_data, olddata.mv_size); + + /* Make sub-page header for the dup items, with dummy body */ + fp->mp_flags = P_LEAF|P_DIRTY|P_SUBP; + fp->mp_lower = (PAGEHDRSZ-PAGEBASE); + xdata.mv_size = PAGEHDRSZ + dkey.mv_size + data->mv_size; + if (mc->mc_db->md_flags & MDB_DUPFIXED) { + fp->mp_flags |= P_LEAF2; + fp->mp_pad = data->mv_size; + xdata.mv_size += 2 * data->mv_size; /* leave space for 2 more */ + } else { + xdata.mv_size += 2 * (sizeof(indx_t) + NODESIZE) + + (dkey.mv_size & 1) + (data->mv_size & 1); + } + fp->mp_upper = xdata.mv_size - PAGEBASE; + olddata.mv_size = xdata.mv_size; /* pretend olddata is fp */ + } else if (leaf->mn_flags & F_SUBDATA) { + /* Data is on sub-DB, just store it */ + flags |= F_DUPDATA|F_SUBDATA; + goto put_sub; + } else { + /* Data is on sub-page */ + fp = olddata.mv_data; + switch (flags) { + default: + if (!(mc->mc_db->md_flags & MDB_DUPFIXED)) { + offset = EVEN(NODESIZE + sizeof(indx_t) + + data->mv_size); + break; + } + offset = fp->mp_pad; + if (SIZELEFT(fp) < offset) { + offset *= 4; /* space for 4 more */ + break; + } + /* FALLTHRU */ /* Big enough MDB_DUPFIXED sub-page */ + case MDB_CURRENT: + fp->mp_flags |= P_DIRTY; + COPY_PGNO(fp->mp_pgno, mp->mp_pgno); + mc->mc_xcursor->mx_cursor.mc_pg[0] = fp; + flags |= F_DUPDATA; + goto put_sub; + } + xdata.mv_size = olddata.mv_size + offset; + } + + fp_flags = fp->mp_flags; + if (NODESIZE + NODEKSZ(leaf) + xdata.mv_size > env->me_nodemax) { + /* Too big for a sub-page, convert to sub-DB */ + fp_flags &= ~P_SUBP; +prep_subDB: + if (mc->mc_db->md_flags & MDB_DUPFIXED) { + fp_flags |= P_LEAF2; + dummy.md_pad = fp->mp_pad; + dummy.md_flags = MDB_DUPFIXED; + if (mc->mc_db->md_flags & MDB_INTEGERDUP) + dummy.md_flags |= MDB_INTEGERKEY; + } else { + dummy.md_pad = 0; + dummy.md_flags = 0; + } + dummy.md_depth = 1; + dummy.md_branch_pages = 0; + dummy.md_leaf_pages = 1; + dummy.md_overflow_pages = 0; + dummy.md_entries = NUMKEYS(fp); + xdata.mv_size = sizeof(MDB_db); + xdata.mv_data = &dummy; + if ((rc = mdb_page_alloc(mc, 1, &mp))) + return rc; + offset = env->me_psize - olddata.mv_size; + flags |= F_DUPDATA|F_SUBDATA; + dummy.md_root = mp->mp_pgno; + sub_root = mp; + } + if (mp != fp) { + mp->mp_flags = fp_flags | P_DIRTY; + mp->mp_pad = fp->mp_pad; + mp->mp_lower = fp->mp_lower; + mp->mp_upper = fp->mp_upper + offset; + if (fp_flags & P_LEAF2) { + memcpy(METADATA(mp), METADATA(fp), NUMKEYS(fp) * fp->mp_pad); + } else { + memcpy((char *)mp + mp->mp_upper + PAGEBASE, (char *)fp + fp->mp_upper + PAGEBASE, + olddata.mv_size - fp->mp_upper - PAGEBASE); + memcpy((char *)(&mp->mp_ptrs), (char *)(&fp->mp_ptrs), NUMKEYS(fp) * sizeof(mp->mp_ptrs[0])); + for (i=0; i<NUMKEYS(fp); i++) + mp->mp_ptrs[i] += offset; + } + } + + rdata = &xdata; + flags |= F_DUPDATA; + do_sub = 1; + if (!insert_key) + mdb_node_del(mc, 0); + goto new_sub; + } +current: + /* LMDB passes F_SUBDATA in 'flags' to write a DB record */ + if ((leaf->mn_flags ^ flags) & F_SUBDATA) + return MDB_INCOMPATIBLE; + /* overflow page overwrites need special handling */ + if (F_ISSET(leaf->mn_flags, F_BIGDATA)) { + MDB_page *omp; + pgno_t pg; + int level, ovpages, dpages = OVPAGES(data->mv_size, env->me_psize); + + memcpy(&pg, olddata.mv_data, sizeof(pg)); + if ((rc2 = mdb_page_get(mc, pg, &omp, &level)) != 0) + return rc2; + ovpages = omp->mp_pages; + + /* Is the ov page large enough? */ + if (ovpages >= dpages) { + if (!(omp->mp_flags & P_DIRTY) && + (level || (env->me_flags & MDB_WRITEMAP))) + { + rc = mdb_page_unspill(mc->mc_txn, omp, &omp); + if (rc) + return rc; + level = 0; /* dirty in this txn or clean */ + } + /* Is it dirty? */ + if (omp->mp_flags & P_DIRTY) { + /* yes, overwrite it. Note in this case we don't + * bother to try shrinking the page if the new data + * is smaller than the overflow threshold. + */ + if (level > 1) { + /* It is writable only in a parent txn */ + size_t sz = (size_t) env->me_psize * ovpages, off; + MDB_page *np = mdb_page_malloc(mc->mc_txn, ovpages); + MDB_ID2 id2; + if (!np) + return ENOMEM; + id2.mid = pg; + id2.mptr = np; + /* Note - this page is already counted in parent's dirty_room */ + rc2 = mdb_mid2l_insert(mc->mc_txn->mt_u.dirty_list, &id2); + mdb_cassert(mc, rc2 == 0); + /* Currently we make the page look as with put() in the + * parent txn, in case the user peeks at MDB_RESERVEd + * or unused parts. Some users treat ovpages specially. + */ + if (!(flags & MDB_RESERVE)) { + /* Skip the part where LMDB will put *data. + * Copy end of page, adjusting alignment so + * compiler may copy words instead of bytes. + */ + off = (PAGEHDRSZ + data->mv_size) & -sizeof(size_t); + memcpy((size_t *)((char *)np + off), + (size_t *)((char *)omp + off), sz - off); + sz = PAGEHDRSZ; + } + memcpy(np, omp, sz); /* Copy beginning of page */ + omp = np; + } + SETDSZ(leaf, data->mv_size); + if (F_ISSET(flags, MDB_RESERVE)) + data->mv_data = METADATA(omp); + else + memcpy(METADATA(omp), data->mv_data, data->mv_size); + return MDB_SUCCESS; + } + } + if ((rc2 = mdb_ovpage_free(mc, omp)) != MDB_SUCCESS) + return rc2; + } else if (data->mv_size == olddata.mv_size) { + /* same size, just replace it. Note that we could + * also reuse this node if the new data is smaller, + * but instead we opt to shrink the node in that case. + */ + if (F_ISSET(flags, MDB_RESERVE)) + data->mv_data = olddata.mv_data; + else if (!(mc->mc_flags & C_SUB)) + memcpy(olddata.mv_data, data->mv_data, data->mv_size); + else { + memcpy(NODEKEY(leaf), key->mv_data, key->mv_size); + goto fix_parent; + } + return MDB_SUCCESS; + } + mdb_node_del(mc, 0); + } + + rdata = data; + +new_sub: + nflags = flags & NODE_ADD_FLAGS; + nsize = IS_LEAF2(mc->mc_pg[mc->mc_top]) ? key->mv_size : mdb_leaf_size(env, key, rdata); + if (SIZELEFT(mc->mc_pg[mc->mc_top]) < nsize) { + if (( flags & (F_DUPDATA|F_SUBDATA)) == F_DUPDATA ) + nflags &= ~MDB_APPEND; /* sub-page may need room to grow */ + if (!insert_key) + nflags |= MDB_SPLIT_REPLACE; + rc = mdb_page_split(mc, key, rdata, P_INVALID, nflags); + } else { + /* There is room already in this leaf page. */ + rc = mdb_node_add(mc, mc->mc_ki[mc->mc_top], key, rdata, 0, nflags); + if (rc == 0) { + /* Adjust other cursors pointing to mp */ + MDB_cursor *m2, *m3; + MDB_dbi dbi = mc->mc_dbi; + unsigned i = mc->mc_top; + MDB_page *mp = mc->mc_pg[i]; + + for (m2 = mc->mc_txn->mt_cursors[dbi]; m2; m2=m2->mc_next) { + if (mc->mc_flags & C_SUB) + m3 = &m2->mc_xcursor->mx_cursor; + else + m3 = m2; + if (m3 == mc || m3->mc_snum < mc->mc_snum || m3->mc_pg[i] != mp) continue; + if (m3->mc_ki[i] >= mc->mc_ki[i] && insert_key) { + m3->mc_ki[i]++; + } + XCURSOR_REFRESH(m3, i, mp); + } + } + } + + if (rc == MDB_SUCCESS) { + /* Now store the actual data in the child DB. Note that we're + * storing the user data in the keys field, so there are strict + * size limits on dupdata. The actual data fields of the child + * DB are all zero size. + */ + if (do_sub) { + int xflags, new_dupdata; + size_t ecount; +put_sub: + xdata.mv_size = 0; + xdata.mv_data = ""; + leaf = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]); + if (flags & MDB_CURRENT) { + xflags = MDB_CURRENT|MDB_NOSPILL; + } else { + mdb_xcursor_init1(mc, leaf); + xflags = (flags & MDB_NODUPDATA) ? + MDB_NOOVERWRITE|MDB_NOSPILL : MDB_NOSPILL; + } + if (sub_root) + mc->mc_xcursor->mx_cursor.mc_pg[0] = sub_root; + new_dupdata = (int)dkey.mv_size; + /* converted, write the original data first */ + if (dkey.mv_size) { + rc = mdb_cursor_put(&mc->mc_xcursor->mx_cursor, &dkey, &xdata, xflags); + if (rc) + goto bad_sub; + /* we've done our job */ + dkey.mv_size = 0; + } + if (!(leaf->mn_flags & F_SUBDATA) || sub_root) { + /* Adjust other cursors pointing to mp */ + MDB_cursor *m2; + MDB_xcursor *mx = mc->mc_xcursor; + unsigned i = mc->mc_top; + MDB_page *mp = mc->mc_pg[i]; + + for (m2 = mc->mc_txn->mt_cursors[mc->mc_dbi]; m2; m2=m2->mc_next) { + if (m2 == mc || m2->mc_snum < mc->mc_snum) continue; + if (!(m2->mc_flags & C_INITIALIZED)) continue; + if (m2->mc_pg[i] == mp) { + if (m2->mc_ki[i] == mc->mc_ki[i]) { + mdb_xcursor_init2(m2, mx, new_dupdata); + } else if (!insert_key) { + XCURSOR_REFRESH(m2, i, mp); + } + } + } + } + ecount = mc->mc_xcursor->mx_db.md_entries; + if (flags & MDB_APPENDDUP) + xflags |= MDB_APPEND; + rc = mdb_cursor_put(&mc->mc_xcursor->mx_cursor, data, &xdata, xflags); + if (flags & F_SUBDATA) { + void *db = NODEDATA(leaf); + memcpy(db, &mc->mc_xcursor->mx_db, sizeof(MDB_db)); + } + insert_data = mc->mc_xcursor->mx_db.md_entries - ecount; + } + /* Increment count unless we just replaced an existing item. */ + if (insert_data) + mc->mc_db->md_entries++; + if (insert_key) { + /* Invalidate txn if we created an empty sub-DB */ + if (rc) + goto bad_sub; + /* If we succeeded and the key didn't exist before, + * make sure the cursor is marked valid. + */ + mc->mc_flags |= C_INITIALIZED; + } + if (flags & MDB_MULTIPLE) { + if (!rc) { + mcount++; + /* let caller know how many succeeded, if any */ + data[1].mv_size = mcount; + if (mcount < dcount) { + data[0].mv_data = (char *)data[0].mv_data + data[0].mv_size; + insert_key = insert_data = 0; + goto more; + } + } + } + return rc; +bad_sub: + if (rc == MDB_KEYEXIST) /* should not happen, we deleted that item */ + rc = MDB_CORRUPTED; + } + mc->mc_txn->mt_flags |= MDB_TXN_ERROR; + return rc; +} + +int +mdb_cursor_del(MDB_cursor *mc, unsigned int flags) +{ + MDB_node *leaf; + MDB_page *mp; + int rc; + + if (mc->mc_txn->mt_flags & (MDB_TXN_RDONLY|MDB_TXN_BLOCKED)) + return (mc->mc_txn->mt_flags & MDB_TXN_RDONLY) ? EACCES : MDB_BAD_TXN; + + if (!(mc->mc_flags & C_INITIALIZED)) + return EINVAL; + + if (mc->mc_ki[mc->mc_top] >= NUMKEYS(mc->mc_pg[mc->mc_top])) + return MDB_NOTFOUND; + + if (!(flags & MDB_NOSPILL) && (rc = mdb_page_spill(mc, NULL, NULL))) + return rc; + + rc = mdb_cursor_touch(mc); + if (rc) + return rc; + + mp = mc->mc_pg[mc->mc_top]; + if (!IS_LEAF(mp)) + return MDB_CORRUPTED; + if (IS_LEAF2(mp)) + goto del_key; + leaf = NODEPTR(mp, mc->mc_ki[mc->mc_top]); + + if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { + if (flags & MDB_NODUPDATA) { + /* mdb_cursor_del0() will subtract the final entry */ + mc->mc_db->md_entries -= mc->mc_xcursor->mx_db.md_entries - 1; + mc->mc_xcursor->mx_cursor.mc_flags &= ~C_INITIALIZED; + } else { + if (!F_ISSET(leaf->mn_flags, F_SUBDATA)) { + mc->mc_xcursor->mx_cursor.mc_pg[0] = NODEDATA(leaf); + } + rc = mdb_cursor_del(&mc->mc_xcursor->mx_cursor, MDB_NOSPILL); + if (rc) + return rc; + /* If sub-DB still has entries, we're done */ + if (mc->mc_xcursor->mx_db.md_entries) { + if (leaf->mn_flags & F_SUBDATA) { + /* update subDB info */ + void *db = NODEDATA(leaf); + memcpy(db, &mc->mc_xcursor->mx_db, sizeof(MDB_db)); + } else { + MDB_cursor *m2; + /* shrink fake page */ + mdb_node_shrink(mp, mc->mc_ki[mc->mc_top]); + leaf = NODEPTR(mp, mc->mc_ki[mc->mc_top]); + mc->mc_xcursor->mx_cursor.mc_pg[0] = NODEDATA(leaf); + /* fix other sub-DB cursors pointed at fake pages on this page */ + for (m2 = mc->mc_txn->mt_cursors[mc->mc_dbi]; m2; m2=m2->mc_next) { + if (m2 == mc || m2->mc_snum < mc->mc_snum) continue; + if (!(m2->mc_flags & C_INITIALIZED)) continue; + if (m2->mc_pg[mc->mc_top] == mp) { + XCURSOR_REFRESH(m2, mc->mc_top, mp); + } + } + } + mc->mc_db->md_entries--; + return rc; + } else { + mc->mc_xcursor->mx_cursor.mc_flags &= ~C_INITIALIZED; + } + /* otherwise fall thru and delete the sub-DB */ + } + + if (leaf->mn_flags & F_SUBDATA) { + /* add all the child DB's pages to the free list */ + rc = mdb_drop0(&mc->mc_xcursor->mx_cursor, 0); + if (rc) + goto fail; + } + } + /* LMDB passes F_SUBDATA in 'flags' to delete a DB record */ + else if ((leaf->mn_flags ^ flags) & F_SUBDATA) { + rc = MDB_INCOMPATIBLE; + goto fail; + } + + /* add overflow pages to free list */ + if (F_ISSET(leaf->mn_flags, F_BIGDATA)) { + MDB_page *omp; + pgno_t pg; + + memcpy(&pg, NODEDATA(leaf), sizeof(pg)); + if ((rc = mdb_page_get(mc, pg, &omp, NULL)) || + (rc = mdb_ovpage_free(mc, omp))) + goto fail; + } + +del_key: + return mdb_cursor_del0(mc); + +fail: + mc->mc_txn->mt_flags |= MDB_TXN_ERROR; + return rc; +} + +/** Allocate and initialize new pages for a database. + * Set #MDB_TXN_ERROR on failure. + * @param[in] mc a cursor on the database being added to. + * @param[in] flags flags defining what type of page is being allocated. + * @param[in] num the number of pages to allocate. This is usually 1, + * unless allocating overflow pages for a large record. + * @param[out] mp Address of a page, or NULL on failure. + * @return 0 on success, non-zero on failure. + */ +static int +mdb_page_new(MDB_cursor *mc, uint32_t flags, int num, MDB_page **mp) +{ + MDB_page *np; + int rc; + + if ((rc = mdb_page_alloc(mc, num, &np))) + return rc; + DPRINTF(("allocated new mpage %"Z"u, page size %u", + np->mp_pgno, mc->mc_txn->mt_env->me_psize)); + np->mp_flags = flags | P_DIRTY; + np->mp_lower = (PAGEHDRSZ-PAGEBASE); + np->mp_upper = mc->mc_txn->mt_env->me_psize - PAGEBASE; + + if (IS_BRANCH(np)) + mc->mc_db->md_branch_pages++; + else if (IS_LEAF(np)) + mc->mc_db->md_leaf_pages++; + else if (IS_OVERFLOW(np)) { + mc->mc_db->md_overflow_pages += num; + np->mp_pages = num; + } + *mp = np; + + return 0; +} + +/** Calculate the size of a leaf node. + * The size depends on the environment's page size; if a data item + * is too large it will be put onto an overflow page and the node + * size will only include the key and not the data. Sizes are always + * rounded up to an even number of bytes, to guarantee 2-byte alignment + * of the #MDB_node headers. + * @param[in] env The environment handle. + * @param[in] key The key for the node. + * @param[in] data The data for the node. + * @return The number of bytes needed to store the node. + */ +static size_t +mdb_leaf_size(MDB_env *env, MDB_val *key, MDB_val *data) +{ + size_t sz; + + sz = LEAFSIZE(key, data); + if (sz > env->me_nodemax) { + /* put on overflow page */ + sz -= data->mv_size - sizeof(pgno_t); + } + + return EVEN(sz + sizeof(indx_t)); +} + +/** Calculate the size of a branch node. + * The size should depend on the environment's page size but since + * we currently don't support spilling large keys onto overflow + * pages, it's simply the size of the #MDB_node header plus the + * size of the key. Sizes are always rounded up to an even number + * of bytes, to guarantee 2-byte alignment of the #MDB_node headers. + * @param[in] env The environment handle. + * @param[in] key The key for the node. + * @return The number of bytes needed to store the node. + */ +static size_t +mdb_branch_size(MDB_env *env, MDB_val *key) +{ + size_t sz; + + sz = INDXSIZE(key); + if (sz > env->me_nodemax) { + /* put on overflow page */ + /* not implemented */ + /* sz -= key->size - sizeof(pgno_t); */ + } + + return sz + sizeof(indx_t); +} + +/** Add a node to the page pointed to by the cursor. + * Set #MDB_TXN_ERROR on failure. + * @param[in] mc The cursor for this operation. + * @param[in] indx The index on the page where the new node should be added. + * @param[in] key The key for the new node. + * @param[in] data The data for the new node, if any. + * @param[in] pgno The page number, if adding a branch node. + * @param[in] flags Flags for the node. + * @return 0 on success, non-zero on failure. Possible errors are: + * <ul> + * <li>ENOMEM - failed to allocate overflow pages for the node. + * <li>MDB_PAGE_FULL - there is insufficient room in the page. This error + * should never happen since all callers already calculate the + * page's free space before calling this function. + * </ul> + */ +static int +mdb_node_add(MDB_cursor *mc, indx_t indx, + MDB_val *key, MDB_val *data, pgno_t pgno, unsigned int flags) +{ + unsigned int i; + size_t node_size = NODESIZE; + ssize_t room; + indx_t ofs; + MDB_node *node; + MDB_page *mp = mc->mc_pg[mc->mc_top]; + MDB_page *ofp = NULL; /* overflow page */ + void *ndata; + DKBUF; + + mdb_cassert(mc, mp->mp_upper >= mp->mp_lower); + + DPRINTF(("add to %s %spage %"Z"u index %i, data size %"Z"u key size %"Z"u [%s]", + IS_LEAF(mp) ? "leaf" : "branch", + IS_SUBP(mp) ? "sub-" : "", + mdb_dbg_pgno(mp), indx, data ? data->mv_size : 0, + key ? key->mv_size : 0, key ? DKEY(key) : "null")); + + if (IS_LEAF2(mp)) { + /* Move higher keys up one slot. */ + int ksize = mc->mc_db->md_pad, dif; + char *ptr = LEAF2KEY(mp, indx, ksize); + dif = NUMKEYS(mp) - indx; + if (dif > 0) + memmove(ptr+ksize, ptr, dif*ksize); + /* insert new key */ + memcpy(ptr, key->mv_data, ksize); + + /* Just using these for counting */ + mp->mp_lower += sizeof(indx_t); + mp->mp_upper -= ksize - sizeof(indx_t); + return MDB_SUCCESS; + } + + room = (ssize_t)SIZELEFT(mp) - (ssize_t)sizeof(indx_t); + if (key != NULL) + node_size += key->mv_size; + if (IS_LEAF(mp)) { + mdb_cassert(mc, key && data); + if (F_ISSET(flags, F_BIGDATA)) { + /* Data already on overflow page. */ + node_size += sizeof(pgno_t); + } else if (node_size + data->mv_size > mc->mc_txn->mt_env->me_nodemax) { + int ovpages = OVPAGES(data->mv_size, mc->mc_txn->mt_env->me_psize); + int rc; + /* Put data on overflow page. */ + DPRINTF(("data size is %"Z"u, node would be %"Z"u, put data on overflow page", + data->mv_size, node_size+data->mv_size)); + node_size = EVEN(node_size + sizeof(pgno_t)); + if ((ssize_t)node_size > room) + goto full; + if ((rc = mdb_page_new(mc, P_OVERFLOW, ovpages, &ofp))) + return rc; + DPRINTF(("allocated overflow page %"Z"u", ofp->mp_pgno)); + flags |= F_BIGDATA; + goto update; + } else { + node_size += data->mv_size; + } + } + node_size = EVEN(node_size); + if ((ssize_t)node_size > room) + goto full; + +update: + /* Move higher pointers up one slot. */ + for (i = NUMKEYS(mp); i > indx; i--) + mp->mp_ptrs[i] = mp->mp_ptrs[i - 1]; + + /* Adjust free space offsets. */ + ofs = mp->mp_upper - node_size; + mdb_cassert(mc, ofs >= mp->mp_lower + sizeof(indx_t)); + mp->mp_ptrs[indx] = ofs; + mp->mp_upper = ofs; + mp->mp_lower += sizeof(indx_t); + + /* Write the node data. */ + node = NODEPTR(mp, indx); + node->mn_ksize = (key == NULL) ? 0 : key->mv_size; + node->mn_flags = flags; + if (IS_LEAF(mp)) + SETDSZ(node,data->mv_size); + else + SETPGNO(node,pgno); + + if (key) + memcpy(NODEKEY(node), key->mv_data, key->mv_size); + + if (IS_LEAF(mp)) { + ndata = NODEDATA(node); + if (ofp == NULL) { + if (F_ISSET(flags, F_BIGDATA)) + memcpy(ndata, data->mv_data, sizeof(pgno_t)); + else if (F_ISSET(flags, MDB_RESERVE)) + data->mv_data = ndata; + else + memcpy(ndata, data->mv_data, data->mv_size); + } else { + memcpy(ndata, &ofp->mp_pgno, sizeof(pgno_t)); + ndata = METADATA(ofp); + if (F_ISSET(flags, MDB_RESERVE)) + data->mv_data = ndata; + else + memcpy(ndata, data->mv_data, data->mv_size); + } + } + + return MDB_SUCCESS; + +full: + DPRINTF(("not enough room in page %"Z"u, got %u ptrs", + mdb_dbg_pgno(mp), NUMKEYS(mp))); + DPRINTF(("upper-lower = %u - %u = %"Z"d", mp->mp_upper,mp->mp_lower,room)); + DPRINTF(("node size = %"Z"u", node_size)); + mc->mc_txn->mt_flags |= MDB_TXN_ERROR; + return MDB_PAGE_FULL; +} + +/** Delete the specified node from a page. + * @param[in] mc Cursor pointing to the node to delete. + * @param[in] ksize The size of a node. Only used if the page is + * part of a #MDB_DUPFIXED database. + */ +static void +mdb_node_del(MDB_cursor *mc, int ksize) +{ + MDB_page *mp = mc->mc_pg[mc->mc_top]; + indx_t indx = mc->mc_ki[mc->mc_top]; + unsigned int sz; + indx_t i, j, numkeys, ptr; + MDB_node *node; + char *base; + + DPRINTF(("delete node %u on %s page %"Z"u", indx, + IS_LEAF(mp) ? "leaf" : "branch", mdb_dbg_pgno(mp))); + numkeys = NUMKEYS(mp); + mdb_cassert(mc, indx < numkeys); + + if (IS_LEAF2(mp)) { + int x = numkeys - 1 - indx; + base = LEAF2KEY(mp, indx, ksize); + if (x) + memmove(base, base + ksize, x * ksize); + mp->mp_lower -= sizeof(indx_t); + mp->mp_upper += ksize - sizeof(indx_t); + return; + } + + node = NODEPTR(mp, indx); + sz = NODESIZE + node->mn_ksize; + if (IS_LEAF(mp)) { + if (F_ISSET(node->mn_flags, F_BIGDATA)) + sz += sizeof(pgno_t); + else + sz += NODEDSZ(node); + } + sz = EVEN(sz); + + ptr = mp->mp_ptrs[indx]; + for (i = j = 0; i < numkeys; i++) { + if (i != indx) { + mp->mp_ptrs[j] = mp->mp_ptrs[i]; + if (mp->mp_ptrs[i] < ptr) + mp->mp_ptrs[j] += sz; + j++; + } + } + + base = (char *)mp + mp->mp_upper + PAGEBASE; + memmove(base + sz, base, ptr - mp->mp_upper); + + mp->mp_lower -= sizeof(indx_t); + mp->mp_upper += sz; +} + +/** Compact the main page after deleting a node on a subpage. + * @param[in] mp The main page to operate on. + * @param[in] indx The index of the subpage on the main page. + */ +static void +mdb_node_shrink(MDB_page *mp, indx_t indx) +{ + MDB_node *node; + MDB_page *sp, *xp; + char *base; + indx_t delta, nsize, len, ptr; + int i; + + node = NODEPTR(mp, indx); + sp = (MDB_page *)NODEDATA(node); + delta = SIZELEFT(sp); + nsize = NODEDSZ(node) - delta; + + /* Prepare to shift upward, set len = length(subpage part to shift) */ + if (IS_LEAF2(sp)) { + len = nsize; + if (nsize & 1) + return; /* do not make the node uneven-sized */ + } else { + xp = (MDB_page *)((char *)sp + delta); /* destination subpage */ + for (i = NUMKEYS(sp); --i >= 0; ) + xp->mp_ptrs[i] = sp->mp_ptrs[i] - delta; + len = PAGEHDRSZ; + } + sp->mp_upper = sp->mp_lower; + COPY_PGNO(sp->mp_pgno, mp->mp_pgno); + SETDSZ(node, nsize); + + /* Shift <lower nodes...initial part of subpage> upward */ + base = (char *)mp + mp->mp_upper + PAGEBASE; + memmove(base + delta, base, (char *)sp + len - base); + + ptr = mp->mp_ptrs[indx]; + for (i = NUMKEYS(mp); --i >= 0; ) { + if (mp->mp_ptrs[i] <= ptr) + mp->mp_ptrs[i] += delta; + } + mp->mp_upper += delta; +} + +/** Initial setup of a sorted-dups cursor. + * Sorted duplicates are implemented as a sub-database for the given key. + * The duplicate data items are actually keys of the sub-database. + * Operations on the duplicate data items are performed using a sub-cursor + * initialized when the sub-database is first accessed. This function does + * the preliminary setup of the sub-cursor, filling in the fields that + * depend only on the parent DB. + * @param[in] mc The main cursor whose sorted-dups cursor is to be initialized. + */ +static void +mdb_xcursor_init0(MDB_cursor *mc) +{ + MDB_xcursor *mx = mc->mc_xcursor; + + mx->mx_cursor.mc_xcursor = NULL; + mx->mx_cursor.mc_txn = mc->mc_txn; + mx->mx_cursor.mc_db = &mx->mx_db; + mx->mx_cursor.mc_dbx = &mx->mx_dbx; + mx->mx_cursor.mc_dbi = mc->mc_dbi; + mx->mx_cursor.mc_dbflag = &mx->mx_dbflag; + mx->mx_cursor.mc_snum = 0; + mx->mx_cursor.mc_top = 0; + mx->mx_cursor.mc_flags = C_SUB; + mx->mx_dbx.md_name.mv_size = 0; + mx->mx_dbx.md_name.mv_data = NULL; + mx->mx_dbx.md_cmp = mc->mc_dbx->md_dcmp; + mx->mx_dbx.md_dcmp = NULL; + mx->mx_dbx.md_rel = mc->mc_dbx->md_rel; +} + +/** Final setup of a sorted-dups cursor. + * Sets up the fields that depend on the data from the main cursor. + * @param[in] mc The main cursor whose sorted-dups cursor is to be initialized. + * @param[in] node The data containing the #MDB_db record for the + * sorted-dup database. + */ +static void +mdb_xcursor_init1(MDB_cursor *mc, MDB_node *node) +{ + MDB_xcursor *mx = mc->mc_xcursor; + + if (node->mn_flags & F_SUBDATA) { + memcpy(&mx->mx_db, NODEDATA(node), sizeof(MDB_db)); + mx->mx_cursor.mc_pg[0] = 0; + mx->mx_cursor.mc_snum = 0; + mx->mx_cursor.mc_top = 0; + mx->mx_cursor.mc_flags = C_SUB; + } else { + MDB_page *fp = NODEDATA(node); + mx->mx_db.md_pad = 0; + mx->mx_db.md_flags = 0; + mx->mx_db.md_depth = 1; + mx->mx_db.md_branch_pages = 0; + mx->mx_db.md_leaf_pages = 1; + mx->mx_db.md_overflow_pages = 0; + mx->mx_db.md_entries = NUMKEYS(fp); + COPY_PGNO(mx->mx_db.md_root, fp->mp_pgno); + mx->mx_cursor.mc_snum = 1; + mx->mx_cursor.mc_top = 0; + mx->mx_cursor.mc_flags = C_INITIALIZED|C_SUB; + mx->mx_cursor.mc_pg[0] = fp; + mx->mx_cursor.mc_ki[0] = 0; + if (mc->mc_db->md_flags & MDB_DUPFIXED) { + mx->mx_db.md_flags = MDB_DUPFIXED; + mx->mx_db.md_pad = fp->mp_pad; + if (mc->mc_db->md_flags & MDB_INTEGERDUP) + mx->mx_db.md_flags |= MDB_INTEGERKEY; + } + } + DPRINTF(("Sub-db -%u root page %"Z"u", mx->mx_cursor.mc_dbi, + mx->mx_db.md_root)); + mx->mx_dbflag = DB_VALID|DB_USRVALID|DB_DUPDATA; +#if UINT_MAX < SIZE_MAX + if (mx->mx_dbx.md_cmp == mdb_cmp_int && mx->mx_db.md_pad == sizeof(size_t)) + mx->mx_dbx.md_cmp = mdb_cmp_clong; +#endif +} + + +/** Fixup a sorted-dups cursor due to underlying update. + * Sets up some fields that depend on the data from the main cursor. + * Almost the same as init1, but skips initialization steps if the + * xcursor had already been used. + * @param[in] mc The main cursor whose sorted-dups cursor is to be fixed up. + * @param[in] src_mx The xcursor of an up-to-date cursor. + * @param[in] new_dupdata True if converting from a non-#F_DUPDATA item. + */ +static void +mdb_xcursor_init2(MDB_cursor *mc, MDB_xcursor *src_mx, int new_dupdata) +{ + MDB_xcursor *mx = mc->mc_xcursor; + + if (new_dupdata) { + mx->mx_cursor.mc_snum = 1; + mx->mx_cursor.mc_top = 0; + mx->mx_cursor.mc_flags |= C_INITIALIZED; + mx->mx_cursor.mc_ki[0] = 0; + mx->mx_dbflag = DB_VALID|DB_USRVALID|DB_DUPDATA; +#if UINT_MAX < SIZE_MAX + mx->mx_dbx.md_cmp = src_mx->mx_dbx.md_cmp; +#endif + } else if (!(mx->mx_cursor.mc_flags & C_INITIALIZED)) { + return; + } + mx->mx_db = src_mx->mx_db; + mx->mx_cursor.mc_pg[0] = src_mx->mx_cursor.mc_pg[0]; + DPRINTF(("Sub-db -%u root page %"Z"u", mx->mx_cursor.mc_dbi, + mx->mx_db.md_root)); +} + +/** Initialize a cursor for a given transaction and database. */ +static void +mdb_cursor_init(MDB_cursor *mc, MDB_txn *txn, MDB_dbi dbi, MDB_xcursor *mx) +{ + mc->mc_next = NULL; + mc->mc_backup = NULL; + mc->mc_dbi = dbi; + mc->mc_txn = txn; + mc->mc_db = &txn->mt_dbs[dbi]; + mc->mc_dbx = &txn->mt_dbxs[dbi]; + mc->mc_dbflag = &txn->mt_dbflags[dbi]; + mc->mc_snum = 0; + mc->mc_top = 0; + mc->mc_pg[0] = 0; + mc->mc_ki[0] = 0; + mc->mc_flags = 0; + if (txn->mt_dbs[dbi].md_flags & MDB_DUPSORT) { + mdb_tassert(txn, mx != NULL); + mc->mc_xcursor = mx; + mdb_xcursor_init0(mc); + } else { + mc->mc_xcursor = NULL; + } + if (*mc->mc_dbflag & DB_STALE) { + mdb_page_search(mc, NULL, MDB_PS_ROOTONLY); + } +} + +int +mdb_cursor_open(MDB_txn *txn, MDB_dbi dbi, MDB_cursor **ret) +{ + MDB_cursor *mc; + size_t size = sizeof(MDB_cursor); + + if (!ret || !TXN_DBI_EXIST(txn, dbi, DB_VALID)) + return EINVAL; + + if (txn->mt_flags & MDB_TXN_BLOCKED) + return MDB_BAD_TXN; + + if (dbi == FREE_DBI && !F_ISSET(txn->mt_flags, MDB_TXN_RDONLY)) + return EINVAL; + + if (txn->mt_dbs[dbi].md_flags & MDB_DUPSORT) + size += sizeof(MDB_xcursor); + + if ((mc = malloc(size)) != NULL) { + mdb_cursor_init(mc, txn, dbi, (MDB_xcursor *)(mc + 1)); + if (txn->mt_cursors) { + mc->mc_next = txn->mt_cursors[dbi]; + txn->mt_cursors[dbi] = mc; + mc->mc_flags |= C_UNTRACK; + } + } else { + return ENOMEM; + } + + *ret = mc; + + return MDB_SUCCESS; +} + +int +mdb_cursor_renew(MDB_txn *txn, MDB_cursor *mc) +{ + if (!mc || !TXN_DBI_EXIST(txn, mc->mc_dbi, DB_VALID)) + return EINVAL; + + if ((mc->mc_flags & C_UNTRACK) || txn->mt_cursors) + return EINVAL; + + if (txn->mt_flags & MDB_TXN_BLOCKED) + return MDB_BAD_TXN; + + mdb_cursor_init(mc, txn, mc->mc_dbi, mc->mc_xcursor); + return MDB_SUCCESS; +} + +/* Return the count of duplicate data items for the current key */ +int +mdb_cursor_count(MDB_cursor *mc, size_t *countp) +{ + MDB_node *leaf; + + if (mc == NULL || countp == NULL) + return EINVAL; + + if (mc->mc_xcursor == NULL) + return MDB_INCOMPATIBLE; + + if (mc->mc_txn->mt_flags & MDB_TXN_BLOCKED) + return MDB_BAD_TXN; + + if (!(mc->mc_flags & C_INITIALIZED)) + return EINVAL; + + if (!mc->mc_snum) + return MDB_NOTFOUND; + + if (mc->mc_flags & C_EOF) { + if (mc->mc_ki[mc->mc_top] >= NUMKEYS(mc->mc_pg[mc->mc_top])) + return MDB_NOTFOUND; + mc->mc_flags ^= C_EOF; + } + + leaf = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]); + if (!F_ISSET(leaf->mn_flags, F_DUPDATA)) { + *countp = 1; + } else { + if (!(mc->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED)) + return EINVAL; + + *countp = mc->mc_xcursor->mx_db.md_entries; + } + return MDB_SUCCESS; +} + +void +mdb_cursor_close(MDB_cursor *mc) +{ + if (mc && !mc->mc_backup) { + /* remove from txn, if tracked */ + if ((mc->mc_flags & C_UNTRACK) && mc->mc_txn->mt_cursors) { + MDB_cursor **prev = &mc->mc_txn->mt_cursors[mc->mc_dbi]; + while (*prev && *prev != mc) prev = &(*prev)->mc_next; + if (*prev == mc) + *prev = mc->mc_next; + } + free(mc); + } +} + +MDB_txn * +mdb_cursor_txn(MDB_cursor *mc) +{ + if (!mc) return NULL; + return mc->mc_txn; +} + +MDB_dbi +mdb_cursor_dbi(MDB_cursor *mc) +{ + return mc->mc_dbi; +} + +/** Replace the key for a branch node with a new key. + * Set #MDB_TXN_ERROR on failure. + * @param[in] mc Cursor pointing to the node to operate on. + * @param[in] key The new key to use. + * @return 0 on success, non-zero on failure. + */ +static int +mdb_update_key(MDB_cursor *mc, MDB_val *key) +{ + MDB_page *mp; + MDB_node *node; + char *base; + size_t len; + int delta, ksize, oksize; + indx_t ptr, i, numkeys, indx; + DKBUF; + + indx = mc->mc_ki[mc->mc_top]; + mp = mc->mc_pg[mc->mc_top]; + node = NODEPTR(mp, indx); + ptr = mp->mp_ptrs[indx]; +#if MDB_DEBUG + { + MDB_val k2; + char kbuf2[DKBUF_MAXKEYSIZE*2+1]; + k2.mv_data = NODEKEY(node); + k2.mv_size = node->mn_ksize; + DPRINTF(("update key %u (ofs %u) [%s] to [%s] on page %"Z"u", + indx, ptr, + mdb_dkey(&k2, kbuf2), + DKEY(key), + mp->mp_pgno)); + } +#endif + + /* Sizes must be 2-byte aligned. */ + ksize = EVEN(key->mv_size); + oksize = EVEN(node->mn_ksize); + delta = ksize - oksize; + + /* Shift node contents if EVEN(key length) changed. */ + if (delta) { + if (delta > 0 && SIZELEFT(mp) < delta) { + pgno_t pgno; + /* not enough space left, do a delete and split */ + DPRINTF(("Not enough room, delta = %d, splitting...", delta)); + pgno = NODEPGNO(node); + mdb_node_del(mc, 0); + return mdb_page_split(mc, key, NULL, pgno, MDB_SPLIT_REPLACE); + } + + numkeys = NUMKEYS(mp); + for (i = 0; i < numkeys; i++) { + if (mp->mp_ptrs[i] <= ptr) + mp->mp_ptrs[i] -= delta; + } + + base = (char *)mp + mp->mp_upper + PAGEBASE; + len = ptr - mp->mp_upper + NODESIZE; + memmove(base - delta, base, len); + mp->mp_upper -= delta; + + node = NODEPTR(mp, indx); + } + + /* But even if no shift was needed, update ksize */ + if (node->mn_ksize != key->mv_size) + node->mn_ksize = key->mv_size; + + if (key->mv_size) + memcpy(NODEKEY(node), key->mv_data, key->mv_size); + + return MDB_SUCCESS; +} + +static void +mdb_cursor_copy(const MDB_cursor *csrc, MDB_cursor *cdst); + +/** Perform \b act while tracking temporary cursor \b mn */ +#define WITH_CURSOR_TRACKING(mn, act) do { \ + MDB_cursor dummy, *tracked, **tp = &(mn).mc_txn->mt_cursors[mn.mc_dbi]; \ + if ((mn).mc_flags & C_SUB) { \ + dummy.mc_flags = C_INITIALIZED; \ + dummy.mc_xcursor = (MDB_xcursor *)&(mn); \ + tracked = &dummy; \ + } else { \ + tracked = &(mn); \ + } \ + tracked->mc_next = *tp; \ + *tp = tracked; \ + { act; } \ + *tp = tracked->mc_next; \ +} while (0) + +/** Move a node from csrc to cdst. + */ +static int +mdb_node_move(MDB_cursor *csrc, MDB_cursor *cdst, int fromleft) +{ + MDB_node *srcnode; + MDB_val key, data; + pgno_t srcpg; + MDB_cursor mn; + int rc; + unsigned short flags; + + DKBUF; + + /* Mark src and dst as dirty. */ + if ((rc = mdb_page_touch(csrc)) || + (rc = mdb_page_touch(cdst))) + return rc; + + if (IS_LEAF2(csrc->mc_pg[csrc->mc_top])) { + key.mv_size = csrc->mc_db->md_pad; + key.mv_data = LEAF2KEY(csrc->mc_pg[csrc->mc_top], csrc->mc_ki[csrc->mc_top], key.mv_size); + data.mv_size = 0; + data.mv_data = NULL; + srcpg = 0; + flags = 0; + } else { + srcnode = NODEPTR(csrc->mc_pg[csrc->mc_top], csrc->mc_ki[csrc->mc_top]); + mdb_cassert(csrc, !((size_t)srcnode & 1)); + srcpg = NODEPGNO(srcnode); + flags = srcnode->mn_flags; + if (csrc->mc_ki[csrc->mc_top] == 0 && IS_BRANCH(csrc->mc_pg[csrc->mc_top])) { + unsigned int snum = csrc->mc_snum; + MDB_node *s2; + /* must find the lowest key below src */ + rc = mdb_page_search_lowest(csrc); + if (rc) + return rc; + if (IS_LEAF2(csrc->mc_pg[csrc->mc_top])) { + key.mv_size = csrc->mc_db->md_pad; + key.mv_data = LEAF2KEY(csrc->mc_pg[csrc->mc_top], 0, key.mv_size); + } else { + s2 = NODEPTR(csrc->mc_pg[csrc->mc_top], 0); + key.mv_size = NODEKSZ(s2); + key.mv_data = NODEKEY(s2); + } + csrc->mc_snum = snum--; + csrc->mc_top = snum; + } else { + key.mv_size = NODEKSZ(srcnode); + key.mv_data = NODEKEY(srcnode); + } + data.mv_size = NODEDSZ(srcnode); + data.mv_data = NODEDATA(srcnode); + } + mn.mc_xcursor = NULL; + if (IS_BRANCH(cdst->mc_pg[cdst->mc_top]) && cdst->mc_ki[cdst->mc_top] == 0) { + unsigned int snum = cdst->mc_snum; + MDB_node *s2; + MDB_val bkey; + /* must find the lowest key below dst */ + mdb_cursor_copy(cdst, &mn); + rc = mdb_page_search_lowest(&mn); + if (rc) + return rc; + if (IS_LEAF2(mn.mc_pg[mn.mc_top])) { + bkey.mv_size = mn.mc_db->md_pad; + bkey.mv_data = LEAF2KEY(mn.mc_pg[mn.mc_top], 0, bkey.mv_size); + } else { + s2 = NODEPTR(mn.mc_pg[mn.mc_top], 0); + bkey.mv_size = NODEKSZ(s2); + bkey.mv_data = NODEKEY(s2); + } + mn.mc_snum = snum--; + mn.mc_top = snum; + mn.mc_ki[snum] = 0; + rc = mdb_update_key(&mn, &bkey); + if (rc) + return rc; + } + + DPRINTF(("moving %s node %u [%s] on page %"Z"u to node %u on page %"Z"u", + IS_LEAF(csrc->mc_pg[csrc->mc_top]) ? "leaf" : "branch", + csrc->mc_ki[csrc->mc_top], + DKEY(&key), + csrc->mc_pg[csrc->mc_top]->mp_pgno, + cdst->mc_ki[cdst->mc_top], cdst->mc_pg[cdst->mc_top]->mp_pgno)); + + /* Add the node to the destination page. + */ + rc = mdb_node_add(cdst, cdst->mc_ki[cdst->mc_top], &key, &data, srcpg, flags); + if (rc != MDB_SUCCESS) + return rc; + + /* Delete the node from the source page. + */ + mdb_node_del(csrc, key.mv_size); + + { + /* Adjust other cursors pointing to mp */ + MDB_cursor *m2, *m3; + MDB_dbi dbi = csrc->mc_dbi; + MDB_page *mpd, *mps; + + mps = csrc->mc_pg[csrc->mc_top]; + /* If we're adding on the left, bump others up */ + if (fromleft) { + mpd = cdst->mc_pg[csrc->mc_top]; + for (m2 = csrc->mc_txn->mt_cursors[dbi]; m2; m2=m2->mc_next) { + if (csrc->mc_flags & C_SUB) + m3 = &m2->mc_xcursor->mx_cursor; + else + m3 = m2; + if (!(m3->mc_flags & C_INITIALIZED) || m3->mc_top < csrc->mc_top) + continue; + if (m3 != cdst && + m3->mc_pg[csrc->mc_top] == mpd && + m3->mc_ki[csrc->mc_top] >= cdst->mc_ki[csrc->mc_top]) { + m3->mc_ki[csrc->mc_top]++; + } + if (m3 !=csrc && + m3->mc_pg[csrc->mc_top] == mps && + m3->mc_ki[csrc->mc_top] == csrc->mc_ki[csrc->mc_top]) { + m3->mc_pg[csrc->mc_top] = cdst->mc_pg[cdst->mc_top]; + m3->mc_ki[csrc->mc_top] = cdst->mc_ki[cdst->mc_top]; + m3->mc_ki[csrc->mc_top-1]++; + } + if (IS_LEAF(mps)) + XCURSOR_REFRESH(m3, csrc->mc_top, m3->mc_pg[csrc->mc_top]); + } + } else + /* Adding on the right, bump others down */ + { + for (m2 = csrc->mc_txn->mt_cursors[dbi]; m2; m2=m2->mc_next) { + if (csrc->mc_flags & C_SUB) + m3 = &m2->mc_xcursor->mx_cursor; + else + m3 = m2; + if (m3 == csrc) continue; + if (!(m3->mc_flags & C_INITIALIZED) || m3->mc_top < csrc->mc_top) + continue; + if (m3->mc_pg[csrc->mc_top] == mps) { + if (!m3->mc_ki[csrc->mc_top]) { + m3->mc_pg[csrc->mc_top] = cdst->mc_pg[cdst->mc_top]; + m3->mc_ki[csrc->mc_top] = cdst->mc_ki[cdst->mc_top]; + m3->mc_ki[csrc->mc_top-1]--; + } else { + m3->mc_ki[csrc->mc_top]--; + } + if (IS_LEAF(mps)) + XCURSOR_REFRESH(m3, csrc->mc_top, m3->mc_pg[csrc->mc_top]); + } + } + } + } + + /* Update the parent separators. + */ + if (csrc->mc_ki[csrc->mc_top] == 0) { + if (csrc->mc_ki[csrc->mc_top-1] != 0) { + if (IS_LEAF2(csrc->mc_pg[csrc->mc_top])) { + key.mv_data = LEAF2KEY(csrc->mc_pg[csrc->mc_top], 0, key.mv_size); + } else { + srcnode = NODEPTR(csrc->mc_pg[csrc->mc_top], 0); + key.mv_size = NODEKSZ(srcnode); + key.mv_data = NODEKEY(srcnode); + } + DPRINTF(("update separator for source page %"Z"u to [%s]", + csrc->mc_pg[csrc->mc_top]->mp_pgno, DKEY(&key))); + mdb_cursor_copy(csrc, &mn); + mn.mc_snum--; + mn.mc_top--; + /* We want mdb_rebalance to find mn when doing fixups */ + WITH_CURSOR_TRACKING(mn, + rc = mdb_update_key(&mn, &key)); + if (rc) + return rc; + } + if (IS_BRANCH(csrc->mc_pg[csrc->mc_top])) { + MDB_val nullkey; + indx_t ix = csrc->mc_ki[csrc->mc_top]; + nullkey.mv_size = 0; + csrc->mc_ki[csrc->mc_top] = 0; + rc = mdb_update_key(csrc, &nullkey); + csrc->mc_ki[csrc->mc_top] = ix; + mdb_cassert(csrc, rc == MDB_SUCCESS); + } + } + + if (cdst->mc_ki[cdst->mc_top] == 0) { + if (cdst->mc_ki[cdst->mc_top-1] != 0) { + if (IS_LEAF2(csrc->mc_pg[csrc->mc_top])) { + key.mv_data = LEAF2KEY(cdst->mc_pg[cdst->mc_top], 0, key.mv_size); + } else { + srcnode = NODEPTR(cdst->mc_pg[cdst->mc_top], 0); + key.mv_size = NODEKSZ(srcnode); + key.mv_data = NODEKEY(srcnode); + } + DPRINTF(("update separator for destination page %"Z"u to [%s]", + cdst->mc_pg[cdst->mc_top]->mp_pgno, DKEY(&key))); + mdb_cursor_copy(cdst, &mn); + mn.mc_snum--; + mn.mc_top--; + /* We want mdb_rebalance to find mn when doing fixups */ + WITH_CURSOR_TRACKING(mn, + rc = mdb_update_key(&mn, &key)); + if (rc) + return rc; + } + if (IS_BRANCH(cdst->mc_pg[cdst->mc_top])) { + MDB_val nullkey; + indx_t ix = cdst->mc_ki[cdst->mc_top]; + nullkey.mv_size = 0; + cdst->mc_ki[cdst->mc_top] = 0; + rc = mdb_update_key(cdst, &nullkey); + cdst->mc_ki[cdst->mc_top] = ix; + mdb_cassert(cdst, rc == MDB_SUCCESS); + } + } + + return MDB_SUCCESS; +} + +/** Merge one page into another. + * The nodes from the page pointed to by \b csrc will + * be copied to the page pointed to by \b cdst and then + * the \b csrc page will be freed. + * @param[in] csrc Cursor pointing to the source page. + * @param[in] cdst Cursor pointing to the destination page. + * @return 0 on success, non-zero on failure. + */ +static int +mdb_page_merge(MDB_cursor *csrc, MDB_cursor *cdst) +{ + MDB_page *psrc, *pdst; + MDB_node *srcnode; + MDB_val key, data; + unsigned nkeys; + int rc; + indx_t i, j; + + psrc = csrc->mc_pg[csrc->mc_top]; + pdst = cdst->mc_pg[cdst->mc_top]; + + DPRINTF(("merging page %"Z"u into %"Z"u", psrc->mp_pgno, pdst->mp_pgno)); + + mdb_cassert(csrc, csrc->mc_snum > 1); /* can't merge root page */ + mdb_cassert(csrc, cdst->mc_snum > 1); + + /* Mark dst as dirty. */ + if ((rc = mdb_page_touch(cdst))) + return rc; + + /* get dst page again now that we've touched it. */ + pdst = cdst->mc_pg[cdst->mc_top]; + + /* Move all nodes from src to dst. + */ + j = nkeys = NUMKEYS(pdst); + if (IS_LEAF2(psrc)) { + key.mv_size = csrc->mc_db->md_pad; + key.mv_data = METADATA(psrc); + for (i = 0; i < NUMKEYS(psrc); i++, j++) { + rc = mdb_node_add(cdst, j, &key, NULL, 0, 0); + if (rc != MDB_SUCCESS) + return rc; + key.mv_data = (char *)key.mv_data + key.mv_size; + } + } else { + for (i = 0; i < NUMKEYS(psrc); i++, j++) { + srcnode = NODEPTR(psrc, i); + if (i == 0 && IS_BRANCH(psrc)) { + MDB_cursor mn; + MDB_node *s2; + mdb_cursor_copy(csrc, &mn); + mn.mc_xcursor = NULL; + /* must find the lowest key below src */ + rc = mdb_page_search_lowest(&mn); + if (rc) + return rc; + if (IS_LEAF2(mn.mc_pg[mn.mc_top])) { + key.mv_size = mn.mc_db->md_pad; + key.mv_data = LEAF2KEY(mn.mc_pg[mn.mc_top], 0, key.mv_size); + } else { + s2 = NODEPTR(mn.mc_pg[mn.mc_top], 0); + key.mv_size = NODEKSZ(s2); + key.mv_data = NODEKEY(s2); + } + } else { + key.mv_size = srcnode->mn_ksize; + key.mv_data = NODEKEY(srcnode); + } + + data.mv_size = NODEDSZ(srcnode); + data.mv_data = NODEDATA(srcnode); + rc = mdb_node_add(cdst, j, &key, &data, NODEPGNO(srcnode), srcnode->mn_flags); + if (rc != MDB_SUCCESS) + return rc; + } + } + + DPRINTF(("dst page %"Z"u now has %u keys (%.1f%% filled)", + pdst->mp_pgno, NUMKEYS(pdst), + (float)PAGEFILL(cdst->mc_txn->mt_env, pdst) / 10)); + + /* Unlink the src page from parent and add to free list. + */ + csrc->mc_top--; + mdb_node_del(csrc, 0); + if (csrc->mc_ki[csrc->mc_top] == 0) { + key.mv_size = 0; + rc = mdb_update_key(csrc, &key); + if (rc) { + csrc->mc_top++; + return rc; + } + } + csrc->mc_top++; + + psrc = csrc->mc_pg[csrc->mc_top]; + /* If not operating on FreeDB, allow this page to be reused + * in this txn. Otherwise just add to free list. + */ + rc = mdb_page_loose(csrc, psrc); + if (rc) + return rc; + if (IS_LEAF(psrc)) + csrc->mc_db->md_leaf_pages--; + else + csrc->mc_db->md_branch_pages--; + { + /* Adjust other cursors pointing to mp */ + MDB_cursor *m2, *m3; + MDB_dbi dbi = csrc->mc_dbi; + unsigned int top = csrc->mc_top; + + for (m2 = csrc->mc_txn->mt_cursors[dbi]; m2; m2=m2->mc_next) { + if (csrc->mc_flags & C_SUB) + m3 = &m2->mc_xcursor->mx_cursor; + else + m3 = m2; + if (m3 == csrc) continue; + if (m3->mc_snum < csrc->mc_snum) continue; + if (m3->mc_pg[top] == psrc) { + m3->mc_pg[top] = pdst; + m3->mc_ki[top] += nkeys; + m3->mc_ki[top-1] = cdst->mc_ki[top-1]; + } else if (m3->mc_pg[top-1] == csrc->mc_pg[top-1] && + m3->mc_ki[top-1] > csrc->mc_ki[top-1]) { + m3->mc_ki[top-1]--; + } + if (IS_LEAF(psrc)) + XCURSOR_REFRESH(m3, top, m3->mc_pg[top]); + } + } + { + unsigned int snum = cdst->mc_snum; + uint16_t depth = cdst->mc_db->md_depth; + mdb_cursor_pop(cdst); + rc = mdb_rebalance(cdst); + /* Did the tree height change? */ + if (depth != cdst->mc_db->md_depth) + snum += cdst->mc_db->md_depth - depth; + cdst->mc_snum = snum; + cdst->mc_top = snum-1; + } + return rc; +} + +/** Copy the contents of a cursor. + * @param[in] csrc The cursor to copy from. + * @param[out] cdst The cursor to copy to. + */ +static void +mdb_cursor_copy(const MDB_cursor *csrc, MDB_cursor *cdst) +{ + unsigned int i; + + cdst->mc_txn = csrc->mc_txn; + cdst->mc_dbi = csrc->mc_dbi; + cdst->mc_db = csrc->mc_db; + cdst->mc_dbx = csrc->mc_dbx; + cdst->mc_snum = csrc->mc_snum; + cdst->mc_top = csrc->mc_top; + cdst->mc_flags = csrc->mc_flags; + + for (i=0; i<csrc->mc_snum; i++) { + cdst->mc_pg[i] = csrc->mc_pg[i]; + cdst->mc_ki[i] = csrc->mc_ki[i]; + } +} + +/** Rebalance the tree after a delete operation. + * @param[in] mc Cursor pointing to the page where rebalancing + * should begin. + * @return 0 on success, non-zero on failure. + */ +static int +mdb_rebalance(MDB_cursor *mc) +{ + MDB_node *node; + int rc, fromleft; + unsigned int ptop, minkeys, thresh; + MDB_cursor mn; + indx_t oldki; + + if (IS_BRANCH(mc->mc_pg[mc->mc_top])) { + minkeys = 2; + thresh = 1; + } else { + minkeys = 1; + thresh = FILL_THRESHOLD; + } + DPRINTF(("rebalancing %s page %"Z"u (has %u keys, %.1f%% full)", + IS_LEAF(mc->mc_pg[mc->mc_top]) ? "leaf" : "branch", + mdb_dbg_pgno(mc->mc_pg[mc->mc_top]), NUMKEYS(mc->mc_pg[mc->mc_top]), + (float)PAGEFILL(mc->mc_txn->mt_env, mc->mc_pg[mc->mc_top]) / 10)); + + if (PAGEFILL(mc->mc_txn->mt_env, mc->mc_pg[mc->mc_top]) >= thresh && + NUMKEYS(mc->mc_pg[mc->mc_top]) >= minkeys) { + DPRINTF(("no need to rebalance page %"Z"u, above fill threshold", + mdb_dbg_pgno(mc->mc_pg[mc->mc_top]))); + return MDB_SUCCESS; + } + + if (mc->mc_snum < 2) { + MDB_page *mp = mc->mc_pg[0]; + if (IS_SUBP(mp)) { + DPUTS("Can't rebalance a subpage, ignoring"); + return MDB_SUCCESS; + } + if (NUMKEYS(mp) == 0) { + DPUTS("tree is completely empty"); + mc->mc_db->md_root = P_INVALID; + mc->mc_db->md_depth = 0; + mc->mc_db->md_leaf_pages = 0; + rc = mdb_midl_append(&mc->mc_txn->mt_free_pgs, mp->mp_pgno); + if (rc) + return rc; + /* Adjust cursors pointing to mp */ + mc->mc_snum = 0; + mc->mc_top = 0; + mc->mc_flags &= ~C_INITIALIZED; + { + MDB_cursor *m2, *m3; + MDB_dbi dbi = mc->mc_dbi; + + for (m2 = mc->mc_txn->mt_cursors[dbi]; m2; m2=m2->mc_next) { + if (mc->mc_flags & C_SUB) + m3 = &m2->mc_xcursor->mx_cursor; + else + m3 = m2; + if (!(m3->mc_flags & C_INITIALIZED) || (m3->mc_snum < mc->mc_snum)) + continue; + if (m3->mc_pg[0] == mp) { + m3->mc_snum = 0; + m3->mc_top = 0; + m3->mc_flags &= ~C_INITIALIZED; + } + } + } + } else if (IS_BRANCH(mp) && NUMKEYS(mp) == 1) { + int i; + DPUTS("collapsing root page!"); + rc = mdb_midl_append(&mc->mc_txn->mt_free_pgs, mp->mp_pgno); + if (rc) + return rc; + mc->mc_db->md_root = NODEPGNO(NODEPTR(mp, 0)); + rc = mdb_page_get(mc, mc->mc_db->md_root, &mc->mc_pg[0], NULL); + if (rc) + return rc; + mc->mc_db->md_depth--; + mc->mc_db->md_branch_pages--; + mc->mc_ki[0] = mc->mc_ki[1]; + for (i = 1; i<mc->mc_db->md_depth; i++) { + mc->mc_pg[i] = mc->mc_pg[i+1]; + mc->mc_ki[i] = mc->mc_ki[i+1]; + } + { + /* Adjust other cursors pointing to mp */ + MDB_cursor *m2, *m3; + MDB_dbi dbi = mc->mc_dbi; + + for (m2 = mc->mc_txn->mt_cursors[dbi]; m2; m2=m2->mc_next) { + if (mc->mc_flags & C_SUB) + m3 = &m2->mc_xcursor->mx_cursor; + else + m3 = m2; + if (m3 == mc) continue; + if (!(m3->mc_flags & C_INITIALIZED)) + continue; + if (m3->mc_pg[0] == mp) { + for (i=0; i<mc->mc_db->md_depth; i++) { + m3->mc_pg[i] = m3->mc_pg[i+1]; + m3->mc_ki[i] = m3->mc_ki[i+1]; + } + m3->mc_snum--; + m3->mc_top--; + } + } + } + } else + DPUTS("root page doesn't need rebalancing"); + return MDB_SUCCESS; + } + + /* The parent (branch page) must have at least 2 pointers, + * otherwise the tree is invalid. + */ + ptop = mc->mc_top-1; + mdb_cassert(mc, NUMKEYS(mc->mc_pg[ptop]) > 1); + + /* Leaf page fill factor is below the threshold. + * Try to move keys from left or right neighbor, or + * merge with a neighbor page. + */ + + /* Find neighbors. + */ + mdb_cursor_copy(mc, &mn); + mn.mc_xcursor = NULL; + + oldki = mc->mc_ki[mc->mc_top]; + if (mc->mc_ki[ptop] == 0) { + /* We're the leftmost leaf in our parent. + */ + DPUTS("reading right neighbor"); + mn.mc_ki[ptop]++; + node = NODEPTR(mc->mc_pg[ptop], mn.mc_ki[ptop]); + rc = mdb_page_get(mc, NODEPGNO(node), &mn.mc_pg[mn.mc_top], NULL); + if (rc) + return rc; + mn.mc_ki[mn.mc_top] = 0; + mc->mc_ki[mc->mc_top] = NUMKEYS(mc->mc_pg[mc->mc_top]); + fromleft = 0; + } else { + /* There is at least one neighbor to the left. + */ + DPUTS("reading left neighbor"); + mn.mc_ki[ptop]--; + node = NODEPTR(mc->mc_pg[ptop], mn.mc_ki[ptop]); + rc = mdb_page_get(mc, NODEPGNO(node), &mn.mc_pg[mn.mc_top], NULL); + if (rc) + return rc; + mn.mc_ki[mn.mc_top] = NUMKEYS(mn.mc_pg[mn.mc_top]) - 1; + mc->mc_ki[mc->mc_top] = 0; + fromleft = 1; + } + + DPRINTF(("found neighbor page %"Z"u (%u keys, %.1f%% full)", + mn.mc_pg[mn.mc_top]->mp_pgno, NUMKEYS(mn.mc_pg[mn.mc_top]), + (float)PAGEFILL(mc->mc_txn->mt_env, mn.mc_pg[mn.mc_top]) / 10)); + + /* If the neighbor page is above threshold and has enough keys, + * move one key from it. Otherwise we should try to merge them. + * (A branch page must never have less than 2 keys.) + */ + if (PAGEFILL(mc->mc_txn->mt_env, mn.mc_pg[mn.mc_top]) >= thresh && NUMKEYS(mn.mc_pg[mn.mc_top]) > minkeys) { + rc = mdb_node_move(&mn, mc, fromleft); + if (fromleft) { + /* if we inserted on left, bump position up */ + oldki++; + } + } else { + if (!fromleft) { + rc = mdb_page_merge(&mn, mc); + } else { + oldki += NUMKEYS(mn.mc_pg[mn.mc_top]); + mn.mc_ki[mn.mc_top] += mc->mc_ki[mn.mc_top] + 1; + /* We want mdb_rebalance to find mn when doing fixups */ + WITH_CURSOR_TRACKING(mn, + rc = mdb_page_merge(mc, &mn)); + mdb_cursor_copy(&mn, mc); + } + mc->mc_flags &= ~C_EOF; + } + mc->mc_ki[mc->mc_top] = oldki; + return rc; +} + +/** Complete a delete operation started by #mdb_cursor_del(). */ +static int +mdb_cursor_del0(MDB_cursor *mc) +{ + int rc; + MDB_page *mp; + indx_t ki; + unsigned int nkeys; + MDB_cursor *m2, *m3; + MDB_dbi dbi = mc->mc_dbi; + + ki = mc->mc_ki[mc->mc_top]; + mp = mc->mc_pg[mc->mc_top]; + mdb_node_del(mc, mc->mc_db->md_pad); + mc->mc_db->md_entries--; + { + /* Adjust other cursors pointing to mp */ + for (m2 = mc->mc_txn->mt_cursors[dbi]; m2; m2=m2->mc_next) { + m3 = (mc->mc_flags & C_SUB) ? &m2->mc_xcursor->mx_cursor : m2; + if (! (m2->mc_flags & m3->mc_flags & C_INITIALIZED)) + continue; + if (m3 == mc || m3->mc_snum < mc->mc_snum) + continue; + if (m3->mc_pg[mc->mc_top] == mp) { + if (m3->mc_ki[mc->mc_top] == ki) { + m3->mc_flags |= C_DEL; + if (mc->mc_db->md_flags & MDB_DUPSORT) { + /* Sub-cursor referred into dataset which is gone */ + m3->mc_xcursor->mx_cursor.mc_flags &= ~(C_INITIALIZED|C_EOF); + } + continue; + } else if (m3->mc_ki[mc->mc_top] > ki) { + m3->mc_ki[mc->mc_top]--; + } + XCURSOR_REFRESH(m3, mc->mc_top, mp); + } + } + } + rc = mdb_rebalance(mc); + if (rc) + goto fail; + + /* DB is totally empty now, just bail out. + * Other cursors adjustments were already done + * by mdb_rebalance and aren't needed here. + */ + if (!mc->mc_snum) { + mc->mc_flags |= C_EOF; + return rc; + } + + ki = mc->mc_ki[mc->mc_top]; + mp = mc->mc_pg[mc->mc_top]; + nkeys = NUMKEYS(mp); + + /* Adjust other cursors pointing to mp */ + for (m2 = mc->mc_txn->mt_cursors[dbi]; !rc && m2; m2=m2->mc_next) { + m3 = (mc->mc_flags & C_SUB) ? &m2->mc_xcursor->mx_cursor : m2; + if (!(m2->mc_flags & m3->mc_flags & C_INITIALIZED)) + continue; + if (m3->mc_snum < mc->mc_snum) + continue; + if (m3->mc_pg[mc->mc_top] == mp) { + /* if m3 points past last node in page, find next sibling */ + if (m3->mc_ki[mc->mc_top] >= nkeys) { + rc = mdb_cursor_sibling(m3, 1); + if (rc == MDB_NOTFOUND) { + m3->mc_flags |= C_EOF; + rc = MDB_SUCCESS; + continue; + } + if (rc) + goto fail; + } + if (m3->mc_ki[mc->mc_top] >= ki || + /* moved to right sibling */ m3->mc_pg[mc->mc_top] != mp) { + if (m3->mc_xcursor && !(m3->mc_flags & C_EOF)) { + MDB_node *node = NODEPTR(m3->mc_pg[m3->mc_top], m3->mc_ki[m3->mc_top]); + /* If this node has dupdata, it may need to be reinited + * because its data has moved. + * If the xcursor was not initd it must be reinited. + * Else if node points to a subDB, nothing is needed. + * Else (xcursor was initd, not a subDB) needs mc_pg[0] reset. + */ + if (node->mn_flags & F_DUPDATA) { + if (m3->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED) { + if (!(node->mn_flags & F_SUBDATA)) + m3->mc_xcursor->mx_cursor.mc_pg[0] = NODEDATA(node); + } else { + mdb_xcursor_init1(m3, node); + rc = mdb_cursor_first(&m3->mc_xcursor->mx_cursor, NULL, NULL); + if (rc) + goto fail; + } + } + m3->mc_xcursor->mx_cursor.mc_flags |= C_DEL; + } + m3->mc_flags |= C_DEL; + } + } + } + +fail: + if (rc) + mc->mc_txn->mt_flags |= MDB_TXN_ERROR; + return rc; +} + +int +mdb_del(MDB_txn *txn, MDB_dbi dbi, + MDB_val *key, MDB_val *data) +{ + if (!key || !TXN_DBI_EXIST(txn, dbi, DB_USRVALID)) + return EINVAL; + + if (txn->mt_flags & (MDB_TXN_RDONLY|MDB_TXN_BLOCKED)) + return (txn->mt_flags & MDB_TXN_RDONLY) ? EACCES : MDB_BAD_TXN; + + if (!F_ISSET(txn->mt_dbs[dbi].md_flags, MDB_DUPSORT)) { + /* must ignore any data */ + data = NULL; + } + + return mdb_del0(txn, dbi, key, data, 0); +} + +static int +mdb_del0(MDB_txn *txn, MDB_dbi dbi, + MDB_val *key, MDB_val *data, unsigned flags) +{ + MDB_cursor mc; + MDB_xcursor mx; + MDB_cursor_op op; + MDB_val rdata, *xdata; + int rc, exact = 0; + DKBUF; + + DPRINTF(("====> delete db %u key [%s]", dbi, DKEY(key))); + + mdb_cursor_init(&mc, txn, dbi, &mx); + + if (data) { + op = MDB_GET_BOTH; + rdata = *data; + xdata = &rdata; + } else { + op = MDB_SET; + xdata = NULL; + flags |= MDB_NODUPDATA; + } + rc = mdb_cursor_set(&mc, key, xdata, op, &exact); + if (rc == 0) { + /* let mdb_page_split know about this cursor if needed: + * delete will trigger a rebalance; if it needs to move + * a node from one page to another, it will have to + * update the parent's separator key(s). If the new sepkey + * is larger than the current one, the parent page may + * run out of space, triggering a split. We need this + * cursor to be consistent until the end of the rebalance. + */ + mc.mc_flags |= C_UNTRACK; + mc.mc_next = txn->mt_cursors[dbi]; + txn->mt_cursors[dbi] = &mc; + rc = mdb_cursor_del(&mc, flags); + txn->mt_cursors[dbi] = mc.mc_next; + } + return rc; +} + +/** Split a page and insert a new node. + * Set #MDB_TXN_ERROR on failure. + * @param[in,out] mc Cursor pointing to the page and desired insertion index. + * The cursor will be updated to point to the actual page and index where + * the node got inserted after the split. + * @param[in] newkey The key for the newly inserted node. + * @param[in] newdata The data for the newly inserted node. + * @param[in] newpgno The page number, if the new node is a branch node. + * @param[in] nflags The #NODE_ADD_FLAGS for the new node. + * @return 0 on success, non-zero on failure. + */ +static int +mdb_page_split(MDB_cursor *mc, MDB_val *newkey, MDB_val *newdata, pgno_t newpgno, + unsigned int nflags) +{ + unsigned int flags; + int rc = MDB_SUCCESS, new_root = 0, did_split = 0; + indx_t newindx; + pgno_t pgno = 0; + int i, j, split_indx, nkeys, pmax; + MDB_env *env = mc->mc_txn->mt_env; + MDB_node *node; + MDB_val sepkey, rkey, xdata, *rdata = &xdata; + MDB_page *copy = NULL; + MDB_page *mp, *rp, *pp; + int ptop; + MDB_cursor mn; + DKBUF; + + mp = mc->mc_pg[mc->mc_top]; + newindx = mc->mc_ki[mc->mc_top]; + nkeys = NUMKEYS(mp); + + DPRINTF(("-----> splitting %s page %"Z"u and adding [%s] at index %i/%i", + IS_LEAF(mp) ? "leaf" : "branch", mp->mp_pgno, + DKEY(newkey), mc->mc_ki[mc->mc_top], nkeys)); + + /* Create a right sibling. */ + if ((rc = mdb_page_new(mc, mp->mp_flags, 1, &rp))) + return rc; + rp->mp_pad = mp->mp_pad; + DPRINTF(("new right sibling: page %"Z"u", rp->mp_pgno)); + + /* Usually when splitting the root page, the cursor + * height is 1. But when called from mdb_update_key, + * the cursor height may be greater because it walks + * up the stack while finding the branch slot to update. + */ + if (mc->mc_top < 1) { + if ((rc = mdb_page_new(mc, P_BRANCH, 1, &pp))) + goto done; + /* shift current top to make room for new parent */ + for (i=mc->mc_snum; i>0; i--) { + mc->mc_pg[i] = mc->mc_pg[i-1]; + mc->mc_ki[i] = mc->mc_ki[i-1]; + } + mc->mc_pg[0] = pp; + mc->mc_ki[0] = 0; + mc->mc_db->md_root = pp->mp_pgno; + DPRINTF(("root split! new root = %"Z"u", pp->mp_pgno)); + new_root = mc->mc_db->md_depth++; + + /* Add left (implicit) pointer. */ + if ((rc = mdb_node_add(mc, 0, NULL, NULL, mp->mp_pgno, 0)) != MDB_SUCCESS) { + /* undo the pre-push */ + mc->mc_pg[0] = mc->mc_pg[1]; + mc->mc_ki[0] = mc->mc_ki[1]; + mc->mc_db->md_root = mp->mp_pgno; + mc->mc_db->md_depth--; + goto done; + } + mc->mc_snum++; + mc->mc_top++; + ptop = 0; + } else { + ptop = mc->mc_top-1; + DPRINTF(("parent branch page is %"Z"u", mc->mc_pg[ptop]->mp_pgno)); + } + + mdb_cursor_copy(mc, &mn); + mn.mc_xcursor = NULL; + mn.mc_pg[mn.mc_top] = rp; + mn.mc_ki[ptop] = mc->mc_ki[ptop]+1; + + if (nflags & MDB_APPEND) { + mn.mc_ki[mn.mc_top] = 0; + sepkey = *newkey; + split_indx = newindx; + nkeys = 0; + } else { + + split_indx = (nkeys+1) / 2; + + if (IS_LEAF2(rp)) { + char *split, *ins; + int x; + unsigned int lsize, rsize, ksize; + /* Move half of the keys to the right sibling */ + x = mc->mc_ki[mc->mc_top] - split_indx; + ksize = mc->mc_db->md_pad; + split = LEAF2KEY(mp, split_indx, ksize); + rsize = (nkeys - split_indx) * ksize; + lsize = (nkeys - split_indx) * sizeof(indx_t); + mp->mp_lower -= lsize; + rp->mp_lower += lsize; + mp->mp_upper += rsize - lsize; + rp->mp_upper -= rsize - lsize; + sepkey.mv_size = ksize; + if (newindx == split_indx) { + sepkey.mv_data = newkey->mv_data; + } else { + sepkey.mv_data = split; + } + if (x<0) { + ins = LEAF2KEY(mp, mc->mc_ki[mc->mc_top], ksize); + memcpy(rp->mp_ptrs, split, rsize); + sepkey.mv_data = rp->mp_ptrs; + memmove(ins+ksize, ins, (split_indx - mc->mc_ki[mc->mc_top]) * ksize); + memcpy(ins, newkey->mv_data, ksize); + mp->mp_lower += sizeof(indx_t); + mp->mp_upper -= ksize - sizeof(indx_t); + } else { + if (x) + memcpy(rp->mp_ptrs, split, x * ksize); + ins = LEAF2KEY(rp, x, ksize); + memcpy(ins, newkey->mv_data, ksize); + memcpy(ins+ksize, split + x * ksize, rsize - x * ksize); + rp->mp_lower += sizeof(indx_t); + rp->mp_upper -= ksize - sizeof(indx_t); + mc->mc_ki[mc->mc_top] = x; + } + } else { + int psize, nsize, k; + /* Maximum free space in an empty page */ + pmax = env->me_psize - PAGEHDRSZ; + if (IS_LEAF(mp)) + nsize = mdb_leaf_size(env, newkey, newdata); + else + nsize = mdb_branch_size(env, newkey); + nsize = EVEN(nsize); + + /* grab a page to hold a temporary copy */ + copy = mdb_page_malloc(mc->mc_txn, 1); + if (copy == NULL) { + rc = ENOMEM; + goto done; + } + copy->mp_pgno = mp->mp_pgno; + copy->mp_flags = mp->mp_flags; + copy->mp_lower = (PAGEHDRSZ-PAGEBASE); + copy->mp_upper = env->me_psize - PAGEBASE; + + /* prepare to insert */ + for (i=0, j=0; i<nkeys; i++) { + if (i == newindx) { + copy->mp_ptrs[j++] = 0; + } + copy->mp_ptrs[j++] = mp->mp_ptrs[i]; + } + + /* When items are relatively large the split point needs + * to be checked, because being off-by-one will make the + * difference between success or failure in mdb_node_add. + * + * It's also relevant if a page happens to be laid out + * such that one half of its nodes are all "small" and + * the other half of its nodes are "large." If the new + * item is also "large" and falls on the half with + * "large" nodes, it also may not fit. + * + * As a final tweak, if the new item goes on the last + * spot on the page (and thus, onto the new page), bias + * the split so the new page is emptier than the old page. + * This yields better packing during sequential inserts. + */ + if (nkeys < 32 || nsize > pmax/16 || newindx >= nkeys) { + /* Find split point */ + psize = 0; + if (newindx <= split_indx || newindx >= nkeys) { + i = 0; j = 1; + k = newindx >= nkeys ? nkeys : split_indx+1+IS_LEAF(mp); + } else { + i = nkeys; j = -1; + k = split_indx-1; + } + for (; i!=k; i+=j) { + if (i == newindx) { + psize += nsize; + node = NULL; + } else { + node = (MDB_node *)((char *)mp + copy->mp_ptrs[i] + PAGEBASE); + psize += NODESIZE + NODEKSZ(node) + sizeof(indx_t); + if (IS_LEAF(mp)) { + if (F_ISSET(node->mn_flags, F_BIGDATA)) + psize += sizeof(pgno_t); + else + psize += NODEDSZ(node); + } + psize = EVEN(psize); + } + if (psize > pmax || i == k-j) { + split_indx = i + (j<0); + break; + } + } + } + if (split_indx == newindx) { + sepkey.mv_size = newkey->mv_size; + sepkey.mv_data = newkey->mv_data; + } else { + node = (MDB_node *)((char *)mp + copy->mp_ptrs[split_indx] + PAGEBASE); + sepkey.mv_size = node->mn_ksize; + sepkey.mv_data = NODEKEY(node); + } + } + } + + DPRINTF(("separator is %d [%s]", split_indx, DKEY(&sepkey))); + + /* Copy separator key to the parent. + */ + if (SIZELEFT(mn.mc_pg[ptop]) < mdb_branch_size(env, &sepkey)) { + int snum = mc->mc_snum; + mn.mc_snum--; + mn.mc_top--; + did_split = 1; + /* We want other splits to find mn when doing fixups */ + WITH_CURSOR_TRACKING(mn, + rc = mdb_page_split(&mn, &sepkey, NULL, rp->mp_pgno, 0)); + if (rc) + goto done; + + /* root split? */ + if (mc->mc_snum > snum) { + ptop++; + } + /* Right page might now have changed parent. + * Check if left page also changed parent. + */ + if (mn.mc_pg[ptop] != mc->mc_pg[ptop] && + mc->mc_ki[ptop] >= NUMKEYS(mc->mc_pg[ptop])) { + for (i=0; i<ptop; i++) { + mc->mc_pg[i] = mn.mc_pg[i]; + mc->mc_ki[i] = mn.mc_ki[i]; + } + mc->mc_pg[ptop] = mn.mc_pg[ptop]; + if (mn.mc_ki[ptop]) { + mc->mc_ki[ptop] = mn.mc_ki[ptop] - 1; + } else { + /* find right page's left sibling */ + mc->mc_ki[ptop] = mn.mc_ki[ptop]; + mdb_cursor_sibling(mc, 0); + } + } + } else { + mn.mc_top--; + rc = mdb_node_add(&mn, mn.mc_ki[ptop], &sepkey, NULL, rp->mp_pgno, 0); + mn.mc_top++; + } + if (rc != MDB_SUCCESS) { + goto done; + } + if (nflags & MDB_APPEND) { + mc->mc_pg[mc->mc_top] = rp; + mc->mc_ki[mc->mc_top] = 0; + rc = mdb_node_add(mc, 0, newkey, newdata, newpgno, nflags); + if (rc) + goto done; + for (i=0; i<mc->mc_top; i++) + mc->mc_ki[i] = mn.mc_ki[i]; + } else if (!IS_LEAF2(mp)) { + /* Move nodes */ + mc->mc_pg[mc->mc_top] = rp; + i = split_indx; + j = 0; + do { + if (i == newindx) { + rkey.mv_data = newkey->mv_data; + rkey.mv_size = newkey->mv_size; + if (IS_LEAF(mp)) { + rdata = newdata; + } else + pgno = newpgno; + flags = nflags; + /* Update index for the new key. */ + mc->mc_ki[mc->mc_top] = j; + } else { + node = (MDB_node *)((char *)mp + copy->mp_ptrs[i] + PAGEBASE); + rkey.mv_data = NODEKEY(node); + rkey.mv_size = node->mn_ksize; + if (IS_LEAF(mp)) { + xdata.mv_data = NODEDATA(node); + xdata.mv_size = NODEDSZ(node); + rdata = &xdata; + } else + pgno = NODEPGNO(node); + flags = node->mn_flags; + } + + if (!IS_LEAF(mp) && j == 0) { + /* First branch index doesn't need key data. */ + rkey.mv_size = 0; + } + + rc = mdb_node_add(mc, j, &rkey, rdata, pgno, flags); + if (rc) + goto done; + if (i == nkeys) { + i = 0; + j = 0; + mc->mc_pg[mc->mc_top] = copy; + } else { + i++; + j++; + } + } while (i != split_indx); + + nkeys = NUMKEYS(copy); + for (i=0; i<nkeys; i++) + mp->mp_ptrs[i] = copy->mp_ptrs[i]; + mp->mp_lower = copy->mp_lower; + mp->mp_upper = copy->mp_upper; + memcpy(NODEPTR(mp, nkeys-1), NODEPTR(copy, nkeys-1), + env->me_psize - copy->mp_upper - PAGEBASE); + + /* reset back to original page */ + if (newindx < split_indx) { + mc->mc_pg[mc->mc_top] = mp; + } else { + mc->mc_pg[mc->mc_top] = rp; + mc->mc_ki[ptop]++; + /* Make sure mc_ki is still valid. + */ + if (mn.mc_pg[ptop] != mc->mc_pg[ptop] && + mc->mc_ki[ptop] >= NUMKEYS(mc->mc_pg[ptop])) { + for (i=0; i<=ptop; i++) { + mc->mc_pg[i] = mn.mc_pg[i]; + mc->mc_ki[i] = mn.mc_ki[i]; + } + } + } + if (nflags & MDB_RESERVE) { + node = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]); + if (!(node->mn_flags & F_BIGDATA)) + newdata->mv_data = NODEDATA(node); + } + } else { + if (newindx >= split_indx) { + mc->mc_pg[mc->mc_top] = rp; + mc->mc_ki[ptop]++; + /* Make sure mc_ki is still valid. + */ + if (mn.mc_pg[ptop] != mc->mc_pg[ptop] && + mc->mc_ki[ptop] >= NUMKEYS(mc->mc_pg[ptop])) { + for (i=0; i<=ptop; i++) { + mc->mc_pg[i] = mn.mc_pg[i]; + mc->mc_ki[i] = mn.mc_ki[i]; + } + } + } + } + + { + /* Adjust other cursors pointing to mp */ + MDB_cursor *m2, *m3; + MDB_dbi dbi = mc->mc_dbi; + nkeys = NUMKEYS(mp); + + for (m2 = mc->mc_txn->mt_cursors[dbi]; m2; m2=m2->mc_next) { + if (mc->mc_flags & C_SUB) + m3 = &m2->mc_xcursor->mx_cursor; + else + m3 = m2; + if (m3 == mc) + continue; + if (!(m2->mc_flags & m3->mc_flags & C_INITIALIZED)) + continue; + if (new_root) { + int k; + /* sub cursors may be on different DB */ + if (m3->mc_pg[0] != mp) + continue; + /* root split */ + for (k=new_root; k>=0; k--) { + m3->mc_ki[k+1] = m3->mc_ki[k]; + m3->mc_pg[k+1] = m3->mc_pg[k]; + } + if (m3->mc_ki[0] >= nkeys) { + m3->mc_ki[0] = 1; + } else { + m3->mc_ki[0] = 0; + } + m3->mc_pg[0] = mc->mc_pg[0]; + m3->mc_snum++; + m3->mc_top++; + } + if (m3->mc_top >= mc->mc_top && m3->mc_pg[mc->mc_top] == mp) { + if (m3->mc_ki[mc->mc_top] >= newindx && !(nflags & MDB_SPLIT_REPLACE)) + m3->mc_ki[mc->mc_top]++; + if (m3->mc_ki[mc->mc_top] >= nkeys) { + m3->mc_pg[mc->mc_top] = rp; + m3->mc_ki[mc->mc_top] -= nkeys; + for (i=0; i<mc->mc_top; i++) { + m3->mc_ki[i] = mn.mc_ki[i]; + m3->mc_pg[i] = mn.mc_pg[i]; + } + } + } else if (!did_split && m3->mc_top >= ptop && m3->mc_pg[ptop] == mc->mc_pg[ptop] && + m3->mc_ki[ptop] >= mc->mc_ki[ptop]) { + m3->mc_ki[ptop]++; + } + if (IS_LEAF(mp)) + XCURSOR_REFRESH(m3, mc->mc_top, m3->mc_pg[mc->mc_top]); + } + } + DPRINTF(("mp left: %d, rp left: %d", SIZELEFT(mp), SIZELEFT(rp))); + +done: + if (copy) /* tmp page */ + mdb_page_free(env, copy); + if (rc) + mc->mc_txn->mt_flags |= MDB_TXN_ERROR; + return rc; +} + +int +mdb_put(MDB_txn *txn, MDB_dbi dbi, + MDB_val *key, MDB_val *data, unsigned int flags) +{ + MDB_cursor mc; + MDB_xcursor mx; + int rc; + + if (!key || !data || !TXN_DBI_EXIST(txn, dbi, DB_USRVALID)) + return EINVAL; + + if (flags & ~(MDB_NOOVERWRITE|MDB_NODUPDATA|MDB_RESERVE|MDB_APPEND|MDB_APPENDDUP)) + return EINVAL; + + if (txn->mt_flags & (MDB_TXN_RDONLY|MDB_TXN_BLOCKED)) + return (txn->mt_flags & MDB_TXN_RDONLY) ? EACCES : MDB_BAD_TXN; + + mdb_cursor_init(&mc, txn, dbi, &mx); + mc.mc_next = txn->mt_cursors[dbi]; + txn->mt_cursors[dbi] = &mc; + rc = mdb_cursor_put(&mc, key, data, flags); + txn->mt_cursors[dbi] = mc.mc_next; + return rc; +} + +#ifndef MDB_WBUF +#define MDB_WBUF (1024*1024) +#endif +#define MDB_EOF 0x10 /**< #mdb_env_copyfd1() is done reading */ + + /** State needed for a double-buffering compacting copy. */ +typedef struct mdb_copy { + MDB_env *mc_env; + MDB_txn *mc_txn; + pthread_mutex_t mc_mutex; + pthread_cond_t mc_cond; /**< Condition variable for #mc_new */ + char *mc_wbuf[2]; + char *mc_over[2]; + int mc_wlen[2]; + int mc_olen[2]; + pgno_t mc_next_pgno; + HANDLE mc_fd; + int mc_toggle; /**< Buffer number in provider */ + int mc_new; /**< (0-2 buffers to write) | (#MDB_EOF at end) */ + /** Error code. Never cleared if set. Both threads can set nonzero + * to fail the copy. Not mutex-protected, LMDB expects atomic int. + */ + volatile int mc_error; +} mdb_copy; + + /** Dedicated writer thread for compacting copy. */ +static THREAD_RET ESECT CALL_CONV +mdb_env_copythr(void *arg) +{ + mdb_copy *my = arg; + char *ptr; + int toggle = 0, wsize, rc; +#ifdef _WIN32 + DWORD len; +#define DO_WRITE(rc, fd, ptr, w2, len) rc = WriteFile(fd, ptr, w2, &len, NULL) +#else + int len; +#define DO_WRITE(rc, fd, ptr, w2, len) len = write(fd, ptr, w2); rc = (len >= 0) +#ifdef SIGPIPE + sigset_t set; + sigemptyset(&set); + sigaddset(&set, SIGPIPE); + if ((rc = pthread_sigmask(SIG_BLOCK, &set, NULL)) != 0) + my->mc_error = rc; +#endif +#endif + + pthread_mutex_lock(&my->mc_mutex); + for(;;) { + while (!my->mc_new) + pthread_cond_wait(&my->mc_cond, &my->mc_mutex); + if (my->mc_new == 0 + MDB_EOF) /* 0 buffers, just EOF */ + break; + wsize = my->mc_wlen[toggle]; + ptr = my->mc_wbuf[toggle]; +again: + rc = MDB_SUCCESS; + while (wsize > 0 && !my->mc_error) { + DO_WRITE(rc, my->mc_fd, ptr, wsize, len); + if (!rc) { + rc = ErrCode(); +#if defined(SIGPIPE) && !defined(_WIN32) + if (rc == EPIPE) { + /* Collect the pending SIGPIPE, otherwise at least OS X + * gives it to the process on thread-exit (ITS#8504). + */ + int tmp; + sigwait(&set, &tmp); + } +#endif + break; + } else if (len > 0) { + rc = MDB_SUCCESS; + ptr += len; + wsize -= len; + continue; + } else { + rc = EIO; + break; + } + } + if (rc) { + my->mc_error = rc; + } + /* If there's an overflow page tail, write it too */ + if (my->mc_olen[toggle]) { + wsize = my->mc_olen[toggle]; + ptr = my->mc_over[toggle]; + my->mc_olen[toggle] = 0; + goto again; + } + my->mc_wlen[toggle] = 0; + toggle ^= 1; + /* Return the empty buffer to provider */ + my->mc_new--; + pthread_cond_signal(&my->mc_cond); + } + pthread_mutex_unlock(&my->mc_mutex); + return (THREAD_RET)0; +#undef DO_WRITE +} + + /** Give buffer and/or #MDB_EOF to writer thread, await unused buffer. + * + * @param[in] my control structure. + * @param[in] adjust (1 to hand off 1 buffer) | (MDB_EOF when ending). + */ +static int ESECT +mdb_env_cthr_toggle(mdb_copy *my, int adjust) +{ + pthread_mutex_lock(&my->mc_mutex); + my->mc_new += adjust; + pthread_cond_signal(&my->mc_cond); + while (my->mc_new & 2) /* both buffers in use */ + pthread_cond_wait(&my->mc_cond, &my->mc_mutex); + pthread_mutex_unlock(&my->mc_mutex); + + my->mc_toggle ^= (adjust & 1); + /* Both threads reset mc_wlen, to be safe from threading errors */ + my->mc_wlen[my->mc_toggle] = 0; + return my->mc_error; +} + + /** Depth-first tree traversal for compacting copy. + * @param[in] my control structure. + * @param[in,out] pg database root. + * @param[in] flags includes #F_DUPDATA if it is a sorted-duplicate sub-DB. + */ +static int ESECT +mdb_env_cwalk(mdb_copy *my, pgno_t *pg, int flags) +{ + MDB_cursor mc = {0}; + MDB_node *ni; + MDB_page *mo, *mp, *leaf; + char *buf, *ptr; + int rc, toggle; + unsigned int i; + + /* Empty DB, nothing to do */ + if (*pg == P_INVALID) + return MDB_SUCCESS; + + mc.mc_snum = 1; + mc.mc_txn = my->mc_txn; + + rc = mdb_page_get(&mc, *pg, &mc.mc_pg[0], NULL); + if (rc) + return rc; + rc = mdb_page_search_root(&mc, NULL, MDB_PS_FIRST); + if (rc) + return rc; + + /* Make cursor pages writable */ + buf = ptr = malloc(my->mc_env->me_psize * mc.mc_snum); + if (buf == NULL) + return ENOMEM; + + for (i=0; i<mc.mc_top; i++) { + mdb_page_copy((MDB_page *)ptr, mc.mc_pg[i], my->mc_env->me_psize); + mc.mc_pg[i] = (MDB_page *)ptr; + ptr += my->mc_env->me_psize; + } + + /* This is writable space for a leaf page. Usually not needed. */ + leaf = (MDB_page *)ptr; + + toggle = my->mc_toggle; + while (mc.mc_snum > 0) { + unsigned n; + mp = mc.mc_pg[mc.mc_top]; + n = NUMKEYS(mp); + + if (IS_LEAF(mp)) { + if (!IS_LEAF2(mp) && !(flags & F_DUPDATA)) { + for (i=0; i<n; i++) { + ni = NODEPTR(mp, i); + if (ni->mn_flags & F_BIGDATA) { + MDB_page *omp; + pgno_t pg; + + /* Need writable leaf */ + if (mp != leaf) { + mc.mc_pg[mc.mc_top] = leaf; + mdb_page_copy(leaf, mp, my->mc_env->me_psize); + mp = leaf; + ni = NODEPTR(mp, i); + } + + memcpy(&pg, NODEDATA(ni), sizeof(pg)); + memcpy(NODEDATA(ni), &my->mc_next_pgno, sizeof(pgno_t)); + rc = mdb_page_get(&mc, pg, &omp, NULL); + if (rc) + goto done; + if (my->mc_wlen[toggle] >= MDB_WBUF) { + rc = mdb_env_cthr_toggle(my, 1); + if (rc) + goto done; + toggle = my->mc_toggle; + } + mo = (MDB_page *)(my->mc_wbuf[toggle] + my->mc_wlen[toggle]); + memcpy(mo, omp, my->mc_env->me_psize); + mo->mp_pgno = my->mc_next_pgno; + my->mc_next_pgno += omp->mp_pages; + my->mc_wlen[toggle] += my->mc_env->me_psize; + if (omp->mp_pages > 1) { + my->mc_olen[toggle] = my->mc_env->me_psize * (omp->mp_pages - 1); + my->mc_over[toggle] = (char *)omp + my->mc_env->me_psize; + rc = mdb_env_cthr_toggle(my, 1); + if (rc) + goto done; + toggle = my->mc_toggle; + } + } else if (ni->mn_flags & F_SUBDATA) { + MDB_db db; + + /* Need writable leaf */ + if (mp != leaf) { + mc.mc_pg[mc.mc_top] = leaf; + mdb_page_copy(leaf, mp, my->mc_env->me_psize); + mp = leaf; + ni = NODEPTR(mp, i); + } + + memcpy(&db, NODEDATA(ni), sizeof(db)); + my->mc_toggle = toggle; + rc = mdb_env_cwalk(my, &db.md_root, ni->mn_flags & F_DUPDATA); + if (rc) + goto done; + toggle = my->mc_toggle; + memcpy(NODEDATA(ni), &db, sizeof(db)); + } + } + } + } else { + mc.mc_ki[mc.mc_top]++; + if (mc.mc_ki[mc.mc_top] < n) { + pgno_t pg; +again: + ni = NODEPTR(mp, mc.mc_ki[mc.mc_top]); + pg = NODEPGNO(ni); + rc = mdb_page_get(&mc, pg, &mp, NULL); + if (rc) + goto done; + mc.mc_top++; + mc.mc_snum++; + mc.mc_ki[mc.mc_top] = 0; + if (IS_BRANCH(mp)) { + /* Whenever we advance to a sibling branch page, + * we must proceed all the way down to its first leaf. + */ + mdb_page_copy(mc.mc_pg[mc.mc_top], mp, my->mc_env->me_psize); + goto again; + } else + mc.mc_pg[mc.mc_top] = mp; + continue; + } + } + if (my->mc_wlen[toggle] >= MDB_WBUF) { + rc = mdb_env_cthr_toggle(my, 1); + if (rc) + goto done; + toggle = my->mc_toggle; + } + mo = (MDB_page *)(my->mc_wbuf[toggle] + my->mc_wlen[toggle]); + mdb_page_copy(mo, mp, my->mc_env->me_psize); + mo->mp_pgno = my->mc_next_pgno++; + my->mc_wlen[toggle] += my->mc_env->me_psize; + if (mc.mc_top) { + /* Update parent if there is one */ + ni = NODEPTR(mc.mc_pg[mc.mc_top-1], mc.mc_ki[mc.mc_top-1]); + SETPGNO(ni, mo->mp_pgno); + mdb_cursor_pop(&mc); + } else { + /* Otherwise we're done */ + *pg = mo->mp_pgno; + break; + } + } +done: + free(buf); + return rc; +} + + /** Copy environment with compaction. */ +static int ESECT +mdb_env_copyfd1(MDB_env *env, HANDLE fd) +{ + MDB_meta *mm; + MDB_page *mp; + mdb_copy my = {0}; + MDB_txn *txn = NULL; + pthread_t thr; + pgno_t root, new_root; + int rc = MDB_SUCCESS; + +#ifdef _WIN32 + if (!(my.mc_mutex = CreateMutex(NULL, FALSE, NULL)) || + !(my.mc_cond = CreateEvent(NULL, FALSE, FALSE, NULL))) { + rc = ErrCode(); + goto done; + } + my.mc_wbuf[0] = _aligned_malloc(MDB_WBUF*2, env->me_os_psize); + if (my.mc_wbuf[0] == NULL) { + /* _aligned_malloc() sets errno, but we use Windows error codes */ + rc = ERROR_NOT_ENOUGH_MEMORY; + goto done; + } +#else + if ((rc = pthread_mutex_init(&my.mc_mutex, NULL)) != 0) + return rc; + if ((rc = pthread_cond_init(&my.mc_cond, NULL)) != 0) + goto done2; +#ifdef HAVE_MEMALIGN + my.mc_wbuf[0] = memalign(env->me_os_psize, MDB_WBUF*2); + if (my.mc_wbuf[0] == NULL) { + rc = errno; + goto done; + } +#else + { + void *p; + if ((rc = posix_memalign(&p, env->me_os_psize, MDB_WBUF*2)) != 0) + goto done; + my.mc_wbuf[0] = p; + } +#endif +#endif + memset(my.mc_wbuf[0], 0, MDB_WBUF*2); + my.mc_wbuf[1] = my.mc_wbuf[0] + MDB_WBUF; + my.mc_next_pgno = NUM_METAS; + my.mc_env = env; + my.mc_fd = fd; + rc = THREAD_CREATE(thr, mdb_env_copythr, &my); + if (rc) + goto done; + + rc = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn); + if (rc) + goto finish; + + mp = (MDB_page *)my.mc_wbuf[0]; + memset(mp, 0, NUM_METAS * env->me_psize); + mp->mp_pgno = 0; + mp->mp_flags = P_META; + mm = (MDB_meta *)METADATA(mp); + mdb_env_init_meta0(env, mm); + mm->mm_address = env->me_metas[0]->mm_address; + + mp = (MDB_page *)(my.mc_wbuf[0] + env->me_psize); + mp->mp_pgno = 1; + mp->mp_flags = P_META; + *(MDB_meta *)METADATA(mp) = *mm; + mm = (MDB_meta *)METADATA(mp); + + /* Set metapage 1 with current main DB */ + root = new_root = txn->mt_dbs[MAIN_DBI].md_root; + if (root != P_INVALID) { + /* Count free pages + freeDB pages. Subtract from last_pg + * to find the new last_pg, which also becomes the new root. + */ + MDB_ID freecount = 0; + MDB_cursor mc; + MDB_val key, data; + mdb_cursor_init(&mc, txn, FREE_DBI, NULL); + while ((rc = mdb_cursor_get(&mc, &key, &data, MDB_NEXT)) == 0) + freecount += *(MDB_ID *)data.mv_data; + if (rc != MDB_NOTFOUND) + goto finish; + freecount += txn->mt_dbs[FREE_DBI].md_branch_pages + + txn->mt_dbs[FREE_DBI].md_leaf_pages + + txn->mt_dbs[FREE_DBI].md_overflow_pages; + + new_root = txn->mt_next_pgno - 1 - freecount; + mm->mm_last_pg = new_root; + mm->mm_dbs[MAIN_DBI] = txn->mt_dbs[MAIN_DBI]; + mm->mm_dbs[MAIN_DBI].md_root = new_root; + } else { + /* When the DB is empty, handle it specially to + * fix any breakage like page leaks from ITS#8174. + */ + mm->mm_dbs[MAIN_DBI].md_flags = txn->mt_dbs[MAIN_DBI].md_flags; + } + if (root != P_INVALID || mm->mm_dbs[MAIN_DBI].md_flags) { + mm->mm_txnid = 1; /* use metapage 1 */ + } + + my.mc_wlen[0] = env->me_psize * NUM_METAS; + my.mc_txn = txn; + rc = mdb_env_cwalk(&my, &root, 0); + if (rc == MDB_SUCCESS && root != new_root) { + rc = MDB_INCOMPATIBLE; /* page leak or corrupt DB */ + } + +finish: + if (rc) + my.mc_error = rc; + mdb_env_cthr_toggle(&my, 1 | MDB_EOF); + rc = THREAD_FINISH(thr); + mdb_txn_abort(txn); + +done: +#ifdef _WIN32 + if (my.mc_wbuf[0]) _aligned_free(my.mc_wbuf[0]); + if (my.mc_cond) CloseHandle(my.mc_cond); + if (my.mc_mutex) CloseHandle(my.mc_mutex); +#else + free(my.mc_wbuf[0]); + pthread_cond_destroy(&my.mc_cond); +done2: + pthread_mutex_destroy(&my.mc_mutex); +#endif + return rc ? rc : my.mc_error; +} + + /** Copy environment as-is. */ +static int ESECT +mdb_env_copyfd0(MDB_env *env, HANDLE fd) +{ + MDB_txn *txn = NULL; + mdb_mutexref_t wmutex = NULL; + int rc; + size_t wsize, w3; + char *ptr; +#ifdef _WIN32 + DWORD len, w2; +#define DO_WRITE(rc, fd, ptr, w2, len) rc = WriteFile(fd, ptr, w2, &len, NULL) +#else + ssize_t len; + size_t w2; +#define DO_WRITE(rc, fd, ptr, w2, len) len = write(fd, ptr, w2); rc = (len >= 0) +#endif + + /* Do the lock/unlock of the reader mutex before starting the + * write txn. Otherwise other read txns could block writers. + */ + rc = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn); + if (rc) + return rc; + + if (env->me_txns) { + /* We must start the actual read txn after blocking writers */ + mdb_txn_end(txn, MDB_END_RESET_TMP); + + /* Temporarily block writers until we snapshot the meta pages */ + wmutex = env->me_wmutex; + if (LOCK_MUTEX(rc, env, wmutex)) + goto leave; + + rc = mdb_txn_renew0(txn); + if (rc) { + UNLOCK_MUTEX(wmutex); + goto leave; + } + } + + wsize = env->me_psize * NUM_METAS; + ptr = env->me_map; + w2 = wsize; + while (w2 > 0) { + DO_WRITE(rc, fd, ptr, w2, len); + if (!rc) { + rc = ErrCode(); + break; + } else if (len > 0) { + rc = MDB_SUCCESS; + ptr += len; + w2 -= len; + continue; + } else { + /* Non-blocking or async handles are not supported */ + rc = EIO; + break; + } + } + if (wmutex) + UNLOCK_MUTEX(wmutex); + + if (rc) + goto leave; + + w3 = txn->mt_next_pgno * env->me_psize; + { + size_t fsize = 0; + if ((rc = mdb_fsize(env->me_fd, &fsize))) + goto leave; + if (w3 > fsize) + w3 = fsize; + } + wsize = w3 - wsize; + while (wsize > 0) { + if (wsize > MAX_WRITE) + w2 = MAX_WRITE; + else + w2 = wsize; + DO_WRITE(rc, fd, ptr, w2, len); + if (!rc) { + rc = ErrCode(); + break; + } else if (len > 0) { + rc = MDB_SUCCESS; + ptr += len; + wsize -= len; + continue; + } else { + rc = EIO; + break; + } + } + +leave: + mdb_txn_abort(txn); + return rc; +} + +int ESECT +mdb_env_copyfd2(MDB_env *env, HANDLE fd, unsigned int flags) +{ + if (flags & MDB_CP_COMPACT) + return mdb_env_copyfd1(env, fd); + else + return mdb_env_copyfd0(env, fd); +} + +int ESECT +mdb_env_copyfd(MDB_env *env, HANDLE fd) +{ + return mdb_env_copyfd2(env, fd, 0); +} + +int ESECT +mdb_env_copy2(MDB_env *env, const char *path, unsigned int flags) +{ + int rc; + MDB_name fname; + HANDLE newfd = INVALID_HANDLE_VALUE; + + rc = mdb_fname_init(path, env->me_flags | MDB_NOLOCK, &fname); + if (rc == MDB_SUCCESS) { + rc = mdb_fopen(env, &fname, MDB_O_COPY, 0666, &newfd); + mdb_fname_destroy(fname); + } + if (rc == MDB_SUCCESS) { + rc = mdb_env_copyfd2(env, newfd, flags); + if (close(newfd) < 0 && rc == MDB_SUCCESS) + rc = ErrCode(); + } + return rc; +} + +int ESECT +mdb_env_copy(MDB_env *env, const char *path) +{ + return mdb_env_copy2(env, path, 0); +} + +int ESECT +mdb_env_set_flags(MDB_env *env, unsigned int flag, int onoff) +{ + if (flag & ~CHANGEABLE) + return EINVAL; + if (onoff) + env->me_flags |= flag; + else + env->me_flags &= ~flag; + return MDB_SUCCESS; +} + +int ESECT +mdb_env_get_flags(MDB_env *env, unsigned int *arg) +{ + if (!env || !arg) + return EINVAL; + + *arg = env->me_flags & (CHANGEABLE|CHANGELESS); + return MDB_SUCCESS; +} + +int ESECT +mdb_env_set_userctx(MDB_env *env, void *ctx) +{ + if (!env) + return EINVAL; + env->me_userctx = ctx; + return MDB_SUCCESS; +} + +void * ESECT +mdb_env_get_userctx(MDB_env *env) +{ + return env ? env->me_userctx : NULL; +} + +int ESECT +mdb_env_set_assert(MDB_env *env, MDB_assert_func *func) +{ + if (!env) + return EINVAL; +#ifndef NDEBUG + env->me_assert_func = func; +#endif + return MDB_SUCCESS; +} + +int ESECT +mdb_env_get_path(MDB_env *env, const char **arg) +{ + if (!env || !arg) + return EINVAL; + + *arg = env->me_path; + return MDB_SUCCESS; +} + +int ESECT +mdb_env_get_fd(MDB_env *env, mdb_filehandle_t *arg) +{ + if (!env || !arg) + return EINVAL; + + *arg = env->me_fd; + return MDB_SUCCESS; +} + +/** Common code for #mdb_stat() and #mdb_env_stat(). + * @param[in] env the environment to operate in. + * @param[in] db the #MDB_db record containing the stats to return. + * @param[out] arg the address of an #MDB_stat structure to receive the stats. + * @return 0, this function always succeeds. + */ +static int ESECT +mdb_stat0(MDB_env *env, MDB_db *db, MDB_stat *arg) +{ + arg->ms_psize = env->me_psize; + arg->ms_depth = db->md_depth; + arg->ms_branch_pages = db->md_branch_pages; + arg->ms_leaf_pages = db->md_leaf_pages; + arg->ms_overflow_pages = db->md_overflow_pages; + arg->ms_entries = db->md_entries; + + return MDB_SUCCESS; +} + +int ESECT +mdb_env_stat(MDB_env *env, MDB_stat *arg) +{ + MDB_meta *meta; + + if (env == NULL || arg == NULL) + return EINVAL; + + meta = mdb_env_pick_meta(env); + + return mdb_stat0(env, &meta->mm_dbs[MAIN_DBI], arg); +} + +int ESECT +mdb_env_info(MDB_env *env, MDB_envinfo *arg) +{ + MDB_meta *meta; + + if (env == NULL || arg == NULL) + return EINVAL; + + meta = mdb_env_pick_meta(env); + arg->me_mapaddr = meta->mm_address; + arg->me_last_pgno = meta->mm_last_pg; + arg->me_last_txnid = meta->mm_txnid; + + arg->me_mapsize = env->me_mapsize; + arg->me_maxreaders = env->me_maxreaders; + arg->me_numreaders = env->me_txns ? env->me_txns->mti_numreaders : 0; + return MDB_SUCCESS; +} + +/** Set the default comparison functions for a database. + * Called immediately after a database is opened to set the defaults. + * The user can then override them with #mdb_set_compare() or + * #mdb_set_dupsort(). + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] dbi A database handle returned by #mdb_dbi_open() + */ +static void +mdb_default_cmp(MDB_txn *txn, MDB_dbi dbi) +{ + uint16_t f = txn->mt_dbs[dbi].md_flags; + + txn->mt_dbxs[dbi].md_cmp = + (f & MDB_REVERSEKEY) ? mdb_cmp_memnr : + (f & MDB_INTEGERKEY) ? mdb_cmp_cint : mdb_cmp_memn; + + txn->mt_dbxs[dbi].md_dcmp = + !(f & MDB_DUPSORT) ? 0 : + ((f & MDB_INTEGERDUP) + ? ((f & MDB_DUPFIXED) ? mdb_cmp_int : mdb_cmp_cint) + : ((f & MDB_REVERSEDUP) ? mdb_cmp_memnr : mdb_cmp_memn)); +} + +int mdb_dbi_open(MDB_txn *txn, const char *name, unsigned int flags, MDB_dbi *dbi) +{ + MDB_val key, data; + MDB_dbi i; + MDB_cursor mc; + MDB_db dummy; + int rc, dbflag, exact; + unsigned int unused = 0, seq; + char *namedup; + size_t len; + + if (flags & ~VALID_FLAGS) + return EINVAL; + if (txn->mt_flags & MDB_TXN_BLOCKED) + return MDB_BAD_TXN; + + /* main DB? */ + if (!name) { + *dbi = MAIN_DBI; + if (flags & PERSISTENT_FLAGS) { + uint16_t f2 = flags & PERSISTENT_FLAGS; + /* make sure flag changes get committed */ + if ((txn->mt_dbs[MAIN_DBI].md_flags | f2) != txn->mt_dbs[MAIN_DBI].md_flags) { + txn->mt_dbs[MAIN_DBI].md_flags |= f2; + txn->mt_flags |= MDB_TXN_DIRTY; + } + } + mdb_default_cmp(txn, MAIN_DBI); + return MDB_SUCCESS; + } + + if (txn->mt_dbxs[MAIN_DBI].md_cmp == NULL) { + mdb_default_cmp(txn, MAIN_DBI); + } + + /* Is the DB already open? */ + len = strlen(name); + for (i=CORE_DBS; i<txn->mt_numdbs; i++) { + if (!txn->mt_dbxs[i].md_name.mv_size) { + /* Remember this free slot */ + if (!unused) unused = i; + continue; + } + if (len == txn->mt_dbxs[i].md_name.mv_size && + !strncmp(name, txn->mt_dbxs[i].md_name.mv_data, len)) { + *dbi = i; + return MDB_SUCCESS; + } + } + + /* If no free slot and max hit, fail */ + if (!unused && txn->mt_numdbs >= txn->mt_env->me_maxdbs) + return MDB_DBS_FULL; + + /* Cannot mix named databases with some mainDB flags */ + if (txn->mt_dbs[MAIN_DBI].md_flags & (MDB_DUPSORT|MDB_INTEGERKEY)) + return (flags & MDB_CREATE) ? MDB_INCOMPATIBLE : MDB_NOTFOUND; + + /* Find the DB info */ + dbflag = DB_NEW|DB_VALID|DB_USRVALID; + exact = 0; + key.mv_size = len; + key.mv_data = (void *)name; + mdb_cursor_init(&mc, txn, MAIN_DBI, NULL); + rc = mdb_cursor_set(&mc, &key, &data, MDB_SET, &exact); + if (rc == MDB_SUCCESS) { + /* make sure this is actually a DB */ + MDB_node *node = NODEPTR(mc.mc_pg[mc.mc_top], mc.mc_ki[mc.mc_top]); + if ((node->mn_flags & (F_DUPDATA|F_SUBDATA)) != F_SUBDATA) + return MDB_INCOMPATIBLE; + } else { + if (rc != MDB_NOTFOUND || !(flags & MDB_CREATE)) + return rc; + if (F_ISSET(txn->mt_flags, MDB_TXN_RDONLY)) + return EACCES; + } + + /* Done here so we cannot fail after creating a new DB */ + if ((namedup = strdup(name)) == NULL) + return ENOMEM; + + if (rc) { + /* MDB_NOTFOUND and MDB_CREATE: Create new DB */ + data.mv_size = sizeof(MDB_db); + data.mv_data = &dummy; + memset(&dummy, 0, sizeof(dummy)); + dummy.md_root = P_INVALID; + dummy.md_flags = flags & PERSISTENT_FLAGS; + WITH_CURSOR_TRACKING(mc, + rc = mdb_cursor_put(&mc, &key, &data, F_SUBDATA)); + dbflag |= DB_DIRTY; + } + + if (rc) { + free(namedup); + } else { + /* Got info, register DBI in this txn */ + unsigned int slot = unused ? unused : txn->mt_numdbs; + txn->mt_dbxs[slot].md_name.mv_data = namedup; + txn->mt_dbxs[slot].md_name.mv_size = len; + txn->mt_dbxs[slot].md_rel = NULL; + txn->mt_dbflags[slot] = dbflag; + /* txn-> and env-> are the same in read txns, use + * tmp variable to avoid undefined assignment + */ + seq = ++txn->mt_env->me_dbiseqs[slot]; + txn->mt_dbiseqs[slot] = seq; + + memcpy(&txn->mt_dbs[slot], data.mv_data, sizeof(MDB_db)); + *dbi = slot; + mdb_default_cmp(txn, slot); + if (!unused) { + txn->mt_numdbs++; + } + } + + return rc; +} + +int ESECT +mdb_stat(MDB_txn *txn, MDB_dbi dbi, MDB_stat *arg) +{ + if (!arg || !TXN_DBI_EXIST(txn, dbi, DB_VALID)) + return EINVAL; + + if (txn->mt_flags & MDB_TXN_BLOCKED) + return MDB_BAD_TXN; + + if (txn->mt_dbflags[dbi] & DB_STALE) { + MDB_cursor mc; + MDB_xcursor mx; + /* Stale, must read the DB's root. cursor_init does it for us. */ + mdb_cursor_init(&mc, txn, dbi, &mx); + } + return mdb_stat0(txn->mt_env, &txn->mt_dbs[dbi], arg); +} + +void mdb_dbi_close(MDB_env *env, MDB_dbi dbi) +{ + char *ptr; + if (dbi < CORE_DBS || dbi >= env->me_maxdbs) + return; + ptr = env->me_dbxs[dbi].md_name.mv_data; + /* If there was no name, this was already closed */ + if (ptr) { + env->me_dbxs[dbi].md_name.mv_data = NULL; + env->me_dbxs[dbi].md_name.mv_size = 0; + env->me_dbflags[dbi] = 0; + env->me_dbiseqs[dbi]++; + free(ptr); + } +} + +int mdb_dbi_flags(MDB_txn *txn, MDB_dbi dbi, unsigned int *flags) +{ + /* We could return the flags for the FREE_DBI too but what's the point? */ + if (!TXN_DBI_EXIST(txn, dbi, DB_USRVALID)) + return EINVAL; + *flags = txn->mt_dbs[dbi].md_flags & PERSISTENT_FLAGS; + return MDB_SUCCESS; +} + +/** Add all the DB's pages to the free list. + * @param[in] mc Cursor on the DB to free. + * @param[in] subs non-Zero to check for sub-DBs in this DB. + * @return 0 on success, non-zero on failure. + */ +static int +mdb_drop0(MDB_cursor *mc, int subs) +{ + int rc; + + rc = mdb_page_search(mc, NULL, MDB_PS_FIRST); + if (rc == MDB_SUCCESS) { + MDB_txn *txn = mc->mc_txn; + MDB_node *ni; + MDB_cursor mx; + unsigned int i; + + /* DUPSORT sub-DBs have no ovpages/DBs. Omit scanning leaves. + * This also avoids any P_LEAF2 pages, which have no nodes. + * Also if the DB doesn't have sub-DBs and has no overflow + * pages, omit scanning leaves. + */ + if ((mc->mc_flags & C_SUB) || + (!subs && !mc->mc_db->md_overflow_pages)) + mdb_cursor_pop(mc); + + mdb_cursor_copy(mc, &mx); + while (mc->mc_snum > 0) { + MDB_page *mp = mc->mc_pg[mc->mc_top]; + unsigned n = NUMKEYS(mp); + if (IS_LEAF(mp)) { + for (i=0; i<n; i++) { + ni = NODEPTR(mp, i); + if (ni->mn_flags & F_BIGDATA) { + MDB_page *omp; + pgno_t pg; + memcpy(&pg, NODEDATA(ni), sizeof(pg)); + rc = mdb_page_get(mc, pg, &omp, NULL); + if (rc != 0) + goto done; + mdb_cassert(mc, IS_OVERFLOW(omp)); + rc = mdb_midl_append_range(&txn->mt_free_pgs, + pg, omp->mp_pages); + if (rc) + goto done; + mc->mc_db->md_overflow_pages -= omp->mp_pages; + if (!mc->mc_db->md_overflow_pages && !subs) + break; + } else if (subs && (ni->mn_flags & F_SUBDATA)) { + mdb_xcursor_init1(mc, ni); + rc = mdb_drop0(&mc->mc_xcursor->mx_cursor, 0); + if (rc) + goto done; + } + } + if (!subs && !mc->mc_db->md_overflow_pages) + goto pop; + } else { + if ((rc = mdb_midl_need(&txn->mt_free_pgs, n)) != 0) + goto done; + for (i=0; i<n; i++) { + pgno_t pg; + ni = NODEPTR(mp, i); + pg = NODEPGNO(ni); + /* free it */ + mdb_midl_xappend(txn->mt_free_pgs, pg); + } + } + if (!mc->mc_top) + break; + mc->mc_ki[mc->mc_top] = i; + rc = mdb_cursor_sibling(mc, 1); + if (rc) { + if (rc != MDB_NOTFOUND) + goto done; + /* no more siblings, go back to beginning + * of previous level. + */ +pop: + mdb_cursor_pop(mc); + mc->mc_ki[0] = 0; + for (i=1; i<mc->mc_snum; i++) { + mc->mc_ki[i] = 0; + mc->mc_pg[i] = mx.mc_pg[i]; + } + } + } + /* free it */ + rc = mdb_midl_append(&txn->mt_free_pgs, mc->mc_db->md_root); +done: + if (rc) + txn->mt_flags |= MDB_TXN_ERROR; + } else if (rc == MDB_NOTFOUND) { + rc = MDB_SUCCESS; + } + mc->mc_flags &= ~C_INITIALIZED; + return rc; +} + +int mdb_drop(MDB_txn *txn, MDB_dbi dbi, int del) +{ + MDB_cursor *mc, *m2; + int rc; + + if ((unsigned)del > 1 || !TXN_DBI_EXIST(txn, dbi, DB_USRVALID)) + return EINVAL; + + if (F_ISSET(txn->mt_flags, MDB_TXN_RDONLY)) + return EACCES; + + if (TXN_DBI_CHANGED(txn, dbi)) + return MDB_BAD_DBI; + + rc = mdb_cursor_open(txn, dbi, &mc); + if (rc) + return rc; + + rc = mdb_drop0(mc, mc->mc_db->md_flags & MDB_DUPSORT); + /* Invalidate the dropped DB's cursors */ + for (m2 = txn->mt_cursors[dbi]; m2; m2 = m2->mc_next) + m2->mc_flags &= ~(C_INITIALIZED|C_EOF); + if (rc) + goto leave; + + /* Can't delete the main DB */ + if (del && dbi >= CORE_DBS) { + rc = mdb_del0(txn, MAIN_DBI, &mc->mc_dbx->md_name, NULL, F_SUBDATA); + if (!rc) { + txn->mt_dbflags[dbi] = DB_STALE; + mdb_dbi_close(txn->mt_env, dbi); + } else { + txn->mt_flags |= MDB_TXN_ERROR; + } + } else { + /* reset the DB record, mark it dirty */ + txn->mt_dbflags[dbi] |= DB_DIRTY; + txn->mt_dbs[dbi].md_depth = 0; + txn->mt_dbs[dbi].md_branch_pages = 0; + txn->mt_dbs[dbi].md_leaf_pages = 0; + txn->mt_dbs[dbi].md_overflow_pages = 0; + txn->mt_dbs[dbi].md_entries = 0; + txn->mt_dbs[dbi].md_root = P_INVALID; + + txn->mt_flags |= MDB_TXN_DIRTY; + } +leave: + mdb_cursor_close(mc); + return rc; +} + +int mdb_set_compare(MDB_txn *txn, MDB_dbi dbi, MDB_cmp_func *cmp) +{ + if (!TXN_DBI_EXIST(txn, dbi, DB_USRVALID)) + return EINVAL; + + txn->mt_dbxs[dbi].md_cmp = cmp; + return MDB_SUCCESS; +} + +int mdb_set_dupsort(MDB_txn *txn, MDB_dbi dbi, MDB_cmp_func *cmp) +{ + if (!TXN_DBI_EXIST(txn, dbi, DB_USRVALID)) + return EINVAL; + + txn->mt_dbxs[dbi].md_dcmp = cmp; + return MDB_SUCCESS; +} + +int mdb_set_relfunc(MDB_txn *txn, MDB_dbi dbi, MDB_rel_func *rel) +{ + if (!TXN_DBI_EXIST(txn, dbi, DB_USRVALID)) + return EINVAL; + + txn->mt_dbxs[dbi].md_rel = rel; + return MDB_SUCCESS; +} + +int mdb_set_relctx(MDB_txn *txn, MDB_dbi dbi, void *ctx) +{ + if (!TXN_DBI_EXIST(txn, dbi, DB_USRVALID)) + return EINVAL; + + txn->mt_dbxs[dbi].md_relctx = ctx; + return MDB_SUCCESS; +} + +int ESECT +mdb_env_get_maxkeysize(MDB_env *env) +{ + return ENV_MAXKEY(env); +} + +int ESECT +mdb_reader_list(MDB_env *env, MDB_msg_func *func, void *ctx) +{ + unsigned int i, rdrs; + MDB_reader *mr; + char buf[64]; + int rc = 0, first = 1; + + if (!env || !func) + return -1; + if (!env->me_txns) { + return func("(no reader locks)\n", ctx); + } + rdrs = env->me_txns->mti_numreaders; + mr = env->me_txns->mti_readers; + for (i=0; i<rdrs; i++) { + if (mr[i].mr_pid) { + txnid_t txnid = mr[i].mr_txnid; + sprintf(buf, txnid == (txnid_t)-1 ? + "%10d %"Z"x -\n" : "%10d %"Z"x %"Z"u\n", + (int)mr[i].mr_pid, (size_t)mr[i].mr_tid, txnid); + if (first) { + first = 0; + rc = func(" pid thread txnid\n", ctx); + if (rc < 0) + break; + } + rc = func(buf, ctx); + if (rc < 0) + break; + } + } + if (first) { + rc = func("(no active readers)\n", ctx); + } + return rc; +} + +/** Insert pid into list if not already present. + * return -1 if already present. + */ +static int ESECT +mdb_pid_insert(MDB_PID_T *ids, MDB_PID_T pid) +{ + /* binary search of pid in list */ + unsigned base = 0; + unsigned cursor = 1; + int val = 0; + unsigned n = ids[0]; + + while( 0 < n ) { + unsigned pivot = n >> 1; + cursor = base + pivot + 1; + val = pid - ids[cursor]; + + if( val < 0 ) { + n = pivot; + + } else if ( val > 0 ) { + base = cursor; + n -= pivot + 1; + + } else { + /* found, so it's a duplicate */ + return -1; + } + } + + if( val > 0 ) { + ++cursor; + } + ids[0]++; + for (n = ids[0]; n > cursor; n--) + ids[n] = ids[n-1]; + ids[n] = pid; + return 0; +} + +int ESECT +mdb_reader_check(MDB_env *env, int *dead) +{ + if (!env) + return EINVAL; + if (dead) + *dead = 0; + return env->me_txns ? mdb_reader_check0(env, 0, dead) : MDB_SUCCESS; +} + +/** As #mdb_reader_check(). \b rlocked is set if caller locked #me_rmutex. */ +static int ESECT +mdb_reader_check0(MDB_env *env, int rlocked, int *dead) +{ + mdb_mutexref_t rmutex = rlocked ? NULL : env->me_rmutex; + unsigned int i, j, rdrs; + MDB_reader *mr; + MDB_PID_T *pids, pid; + int rc = MDB_SUCCESS, count = 0; + + rdrs = env->me_txns->mti_numreaders; + pids = malloc((rdrs+1) * sizeof(MDB_PID_T)); + if (!pids) + return ENOMEM; + pids[0] = 0; + mr = env->me_txns->mti_readers; + for (i=0; i<rdrs; i++) { + pid = mr[i].mr_pid; + if (pid && pid != env->me_pid) { + if (mdb_pid_insert(pids, pid) == 0) { + if (!mdb_reader_pid(env, Pidcheck, pid)) { + /* Stale reader found */ + j = i; + if (rmutex) { + if ((rc = LOCK_MUTEX0(rmutex)) != 0) { + if ((rc = mdb_mutex_failed(env, rmutex, rc))) + break; + rdrs = 0; /* the above checked all readers */ + } else { + /* Recheck, a new process may have reused pid */ + if (mdb_reader_pid(env, Pidcheck, pid)) + j = rdrs; + } + } + for (; j<rdrs; j++) + if (mr[j].mr_pid == pid) { + DPRINTF(("clear stale reader pid %u txn %"Z"d", + (unsigned) pid, mr[j].mr_txnid)); + mr[j].mr_pid = 0; + count++; + } + if (rmutex) + UNLOCK_MUTEX(rmutex); + } + } + } + } + free(pids); + if (dead) + *dead = count; + return rc; +} + +#ifdef MDB_ROBUST_SUPPORTED +/** Handle #LOCK_MUTEX0() failure. + * Try to repair the lock file if the mutex owner died. + * @param[in] env the environment handle + * @param[in] mutex LOCK_MUTEX0() mutex + * @param[in] rc LOCK_MUTEX0() error (nonzero) + * @return 0 on success with the mutex locked, or an error code on failure. + */ +static int ESECT +mdb_mutex_failed(MDB_env *env, mdb_mutexref_t mutex, int rc) +{ + int rlocked, rc2; + MDB_meta *meta; + + if (rc == MDB_OWNERDEAD) { + /* We own the mutex. Clean up after dead previous owner. */ + rc = MDB_SUCCESS; + rlocked = (mutex == env->me_rmutex); + if (!rlocked) { + /* Keep mti_txnid updated, otherwise next writer can + * overwrite data which latest meta page refers to. + */ + meta = mdb_env_pick_meta(env); + env->me_txns->mti_txnid = meta->mm_txnid; + /* env is hosed if the dead thread was ours */ + if (env->me_txn) { + env->me_flags |= MDB_FATAL_ERROR; + env->me_txn = NULL; + rc = MDB_PANIC; + } + } + DPRINTF(("%cmutex owner died, %s", (rlocked ? 'r' : 'w'), + (rc ? "this process' env is hosed" : "recovering"))); + rc2 = mdb_reader_check0(env, rlocked, NULL); + if (rc2 == 0) + rc2 = mdb_mutex_consistent(mutex); + if (rc || (rc = rc2)) { + DPRINTF(("LOCK_MUTEX recovery failed, %s", mdb_strerror(rc))); + UNLOCK_MUTEX(mutex); + } + } else { +#ifdef _WIN32 + rc = ErrCode(); +#endif + DPRINTF(("LOCK_MUTEX failed, %s", mdb_strerror(rc))); + } + + return rc; +} +#endif /* MDB_ROBUST_SUPPORTED */ + +#if defined(_WIN32) +/** Convert \b src to new wchar_t[] string with room for \b xtra extra chars */ +static int ESECT +utf8_to_utf16(const char *src, MDB_name *dst, int xtra) +{ + int rc, need = 0; + wchar_t *result = NULL; + for (;;) { /* malloc result, then fill it in */ + need = MultiByteToWideChar(CP_UTF8, 0, src, -1, result, need); + if (!need) { + rc = ErrCode(); + free(result); + return rc; + } + if (!result) { + result = malloc(sizeof(wchar_t) * (need + xtra)); + if (!result) + return ENOMEM; + continue; + } + dst->mn_alloced = 1; + dst->mn_len = need - 1; + dst->mn_val = result; + return MDB_SUCCESS; + } +} +#endif /* defined(_WIN32) */ +/** @} */ diff --git a/libraries/liblmdb/mdb_copy.1 b/libraries/liblmdb/mdb_copy.1 new file mode 100644 index 0000000..606ea66 --- /dev/null +++ b/libraries/liblmdb/mdb_copy.1 @@ -0,0 +1,55 @@ +.TH MDB_COPY 1 "2014/07/01" "LMDB 0.9.14" +.\" Copyright 2012-2020 Howard Chu, Symas Corp. All Rights Reserved. +.\" Copying restrictions apply. See COPYRIGHT/LICENSE. +.SH NAME +mdb_copy \- LMDB environment copy tool +.SH SYNOPSIS +.B mdb_copy +[\c +.BR \-V ] +[\c +.BR \-c ] +[\c +.BR \-n ] +.B srcpath +[\c +.BR dstpath ] +.SH DESCRIPTION +The +.B mdb_copy +utility copies an LMDB environment. The environment can +be copied regardless of whether it is currently in use. +No lockfile is created, since it gets recreated at need. + +If +.I dstpath +is specified it must be the path of an empty directory +for storing the backup. Otherwise, the backup will be +written to stdout. + +.SH OPTIONS +.TP +.BR \-V +Write the library version number to the standard output, and exit. +.TP +.BR \-c +Compact while copying. Only current data pages will be copied; freed +or unused pages will be omitted from the copy. This option will +slow down the backup process as it is more CPU-intensive. +Currently it fails if the environment has suffered a page leak. +.TP +.BR \-n +Open LDMB environment(s) which do not use subdirectories. + +.SH DIAGNOSTICS +Exit status is zero if no errors occur. +Errors result in a non-zero exit status and +a diagnostic message being written to standard error. +.SH CAVEATS +This utility can trigger significant file size growth if run +in parallel with write transactions, because pages which they +free during copying cannot be reused until the copy is done. +.SH "SEE ALSO" +.BR mdb_stat (1) +.SH AUTHOR +Howard Chu of Symas Corporation <http://www.symas.com> diff --git a/libraries/liblmdb/mdb_copy.c b/libraries/liblmdb/mdb_copy.c new file mode 100644 index 0000000..b3eb320 --- /dev/null +++ b/libraries/liblmdb/mdb_copy.c @@ -0,0 +1,82 @@ +/* mdb_copy.c - memory-mapped database backup tool */ +/* + * Copyright 2012-2020 Howard Chu, Symas Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +#ifdef _WIN32 +#include <windows.h> +#define MDB_STDOUT GetStdHandle(STD_OUTPUT_HANDLE) +#else +#define MDB_STDOUT 1 +#endif +#include <stdio.h> +#include <stdlib.h> +#include <signal.h> +#include "lmdb.h" + +static void +sighandle(int sig) +{ +} + +int main(int argc,char * argv[]) +{ + int rc; + MDB_env *env; + const char *progname = argv[0], *act; + unsigned flags = MDB_RDONLY; + unsigned cpflags = 0; + + for (; argc > 1 && argv[1][0] == '-'; argc--, argv++) { + if (argv[1][1] == 'n' && argv[1][2] == '\0') + flags |= MDB_NOSUBDIR; + else if (argv[1][1] == 'c' && argv[1][2] == '\0') + cpflags |= MDB_CP_COMPACT; + else if (argv[1][1] == 'V' && argv[1][2] == '\0') { + printf("%s\n", MDB_VERSION_STRING); + exit(0); + } else + argc = 0; + } + + if (argc<2 || argc>3) { + fprintf(stderr, "usage: %s [-V] [-c] [-n] srcpath [dstpath]\n", progname); + exit(EXIT_FAILURE); + } + +#ifdef SIGPIPE + signal(SIGPIPE, sighandle); +#endif +#ifdef SIGHUP + signal(SIGHUP, sighandle); +#endif + signal(SIGINT, sighandle); + signal(SIGTERM, sighandle); + + act = "opening environment"; + rc = mdb_env_create(&env); + if (rc == MDB_SUCCESS) { + rc = mdb_env_open(env, argv[1], flags, 0600); + } + if (rc == MDB_SUCCESS) { + act = "copying"; + if (argc == 2) + rc = mdb_env_copyfd2(env, MDB_STDOUT, cpflags); + else + rc = mdb_env_copy2(env, argv[2], cpflags); + } + if (rc) + fprintf(stderr, "%s: %s failed, error %d (%s)\n", + progname, act, rc, mdb_strerror(rc)); + mdb_env_close(env); + + return rc ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/libraries/liblmdb/mdb_dump.1 b/libraries/liblmdb/mdb_dump.1 new file mode 100644 index 0000000..08a9f64 --- /dev/null +++ b/libraries/liblmdb/mdb_dump.1 @@ -0,0 +1,75 @@ +.TH MDB_DUMP 1 "2015/09/30" "LMDB 0.9.17" +.\" Copyright 2014-2020 Howard Chu, Symas Corp. All Rights Reserved. +.\" Copying restrictions apply. See COPYRIGHT/LICENSE. +.SH NAME +mdb_dump \- LMDB environment export tool +.SH SYNOPSIS +.B mdb_dump +[\c +.BR \-V ] +[\c +.BI \-f \ file\fR] +[\c +.BR \-l ] +[\c +.BR \-n ] +[\c +.BR \-p ] +[\c +.BR \-a \ | +.BI \-s \ subdb\fR] +.BR \ envpath +.SH DESCRIPTION +The +.B mdb_dump +utility reads a database and writes its contents to the +standard output using a portable flat-text format +understood by the +.BR mdb_load (1) +utility. +.SH OPTIONS +.TP +.BR \-V +Write the library version number to the standard output, and exit. +.TP +.BR \-f \ file +Write to the specified file instead of to the standard output. +.TP +.BR \-l +List the databases stored in the environment. Just the +names will be listed, no data will be output. +.TP +.BR \-n +Dump an LMDB database which does not use subdirectories. +.TP +.BR \-p +If characters in either the key or data items are printing characters (as +defined by isprint(3)), output them directly. This option permits users to +use standard text editors and tools to modify the contents of databases. + +Note: different systems may have different notions about what characters +are considered printing characters, and databases dumped in this manner may +be less portable to external systems. +.TP +.BR \-a +Dump all of the subdatabases in the environment. +.TP +.BR \-s \ subdb +Dump a specific subdatabase. If no database is specified, only the main database is dumped. +.SH DIAGNOSTICS +Exit status is zero if no errors occur. +Errors result in a non-zero exit status and +a diagnostic message being written to standard error. + +Dumping and reloading databases that use user-defined comparison functions +will result in new databases that use the default comparison functions. +\fBIn this case it is quite likely that the reloaded database will be +damaged beyond repair permitting neither record storage nor retrieval.\fP + +The only available workaround is to modify the source for the +.BR mdb_load (1) +utility to load the database using the correct comparison functions. +.SH "SEE ALSO" +.BR mdb_load (1) +.SH AUTHOR +Howard Chu of Symas Corporation <http://www.symas.com> diff --git a/libraries/liblmdb/mdb_dump.c b/libraries/liblmdb/mdb_dump.c new file mode 100644 index 0000000..ee7dbe8 --- /dev/null +++ b/libraries/liblmdb/mdb_dump.c @@ -0,0 +1,319 @@ +/* mdb_dump.c - memory-mapped database dump tool */ +/* + * Copyright 2011-2020 Howard Chu, Symas Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +#include <stdio.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <unistd.h> +#include <signal.h> +#include "lmdb.h" + +#ifdef _WIN32 +#define Z "I" +#else +#define Z "z" +#endif + +#define PRINT 1 +static int mode; + +typedef struct flagbit { + int bit; + char *name; +} flagbit; + +flagbit dbflags[] = { + { MDB_REVERSEKEY, "reversekey" }, + { MDB_DUPSORT, "dupsort" }, + { MDB_INTEGERKEY, "integerkey" }, + { MDB_DUPFIXED, "dupfixed" }, + { MDB_INTEGERDUP, "integerdup" }, + { MDB_REVERSEDUP, "reversedup" }, + { 0, NULL } +}; + +static volatile sig_atomic_t gotsig; + +static void dumpsig( int sig ) +{ + gotsig=1; +} + +static const char hexc[] = "0123456789abcdef"; + +static void hex(unsigned char c) +{ + putchar(hexc[c >> 4]); + putchar(hexc[c & 0xf]); +} + +static void text(MDB_val *v) +{ + unsigned char *c, *end; + + putchar(' '); + c = v->mv_data; + end = c + v->mv_size; + while (c < end) { + if (isprint(*c)) { + if (*c == '\\') + putchar('\\'); + putchar(*c); + } else { + putchar('\\'); + hex(*c); + } + c++; + } + putchar('\n'); +} + +static void byte(MDB_val *v) +{ + unsigned char *c, *end; + + putchar(' '); + c = v->mv_data; + end = c + v->mv_size; + while (c < end) { + hex(*c++); + } + putchar('\n'); +} + +/* Dump in BDB-compatible format */ +static int dumpit(MDB_txn *txn, MDB_dbi dbi, char *name) +{ + MDB_cursor *mc; + MDB_stat ms; + MDB_val key, data; + MDB_envinfo info; + unsigned int flags; + int rc, i; + + rc = mdb_dbi_flags(txn, dbi, &flags); + if (rc) return rc; + + rc = mdb_stat(txn, dbi, &ms); + if (rc) return rc; + + rc = mdb_env_info(mdb_txn_env(txn), &info); + if (rc) return rc; + + printf("VERSION=3\n"); + printf("format=%s\n", mode & PRINT ? "print" : "bytevalue"); + if (name) + printf("database=%s\n", name); + printf("type=btree\n"); + printf("mapsize=%" Z "u\n", info.me_mapsize); + if (info.me_mapaddr) + printf("mapaddr=%p\n", info.me_mapaddr); + printf("maxreaders=%u\n", info.me_maxreaders); + + if (flags & MDB_DUPSORT) + printf("duplicates=1\n"); + + for (i=0; dbflags[i].bit; i++) + if (flags & dbflags[i].bit) + printf("%s=1\n", dbflags[i].name); + + printf("db_pagesize=%d\n", ms.ms_psize); + printf("HEADER=END\n"); + + rc = mdb_cursor_open(txn, dbi, &mc); + if (rc) return rc; + + while ((rc = mdb_cursor_get(mc, &key, &data, MDB_NEXT) == MDB_SUCCESS)) { + if (gotsig) { + rc = EINTR; + break; + } + if (mode & PRINT) { + text(&key); + text(&data); + } else { + byte(&key); + byte(&data); + } + } + printf("DATA=END\n"); + if (rc == MDB_NOTFOUND) + rc = MDB_SUCCESS; + + return rc; +} + +static void usage(char *prog) +{ + fprintf(stderr, "usage: %s [-V] [-f output] [-l] [-n] [-p] [-a|-s subdb] dbpath\n", prog); + exit(EXIT_FAILURE); +} + +int main(int argc, char *argv[]) +{ + int i, rc; + MDB_env *env; + MDB_txn *txn; + MDB_dbi dbi; + char *prog = argv[0]; + char *envname; + char *subname = NULL; + int alldbs = 0, envflags = 0, list = 0; + + if (argc < 2) { + usage(prog); + } + + /* -a: dump main DB and all subDBs + * -s: dump only the named subDB + * -n: use NOSUBDIR flag on env_open + * -p: use printable characters + * -f: write to file instead of stdout + * -V: print version and exit + * (default) dump only the main DB + */ + while ((i = getopt(argc, argv, "af:lnps:V")) != EOF) { + switch(i) { + case 'V': + printf("%s\n", MDB_VERSION_STRING); + exit(0); + break; + case 'l': + list = 1; + /*FALLTHROUGH*/; + case 'a': + if (subname) + usage(prog); + alldbs++; + break; + case 'f': + if (freopen(optarg, "w", stdout) == NULL) { + fprintf(stderr, "%s: %s: reopen: %s\n", + prog, optarg, strerror(errno)); + exit(EXIT_FAILURE); + } + break; + case 'n': + envflags |= MDB_NOSUBDIR; + break; + case 'p': + mode |= PRINT; + break; + case 's': + if (alldbs) + usage(prog); + subname = optarg; + break; + default: + usage(prog); + } + } + + if (optind != argc - 1) + usage(prog); + +#ifdef SIGPIPE + signal(SIGPIPE, dumpsig); +#endif +#ifdef SIGHUP + signal(SIGHUP, dumpsig); +#endif + signal(SIGINT, dumpsig); + signal(SIGTERM, dumpsig); + + envname = argv[optind]; + rc = mdb_env_create(&env); + if (rc) { + fprintf(stderr, "mdb_env_create failed, error %d %s\n", rc, mdb_strerror(rc)); + return EXIT_FAILURE; + } + + if (alldbs || subname) { + mdb_env_set_maxdbs(env, 2); + } + + rc = mdb_env_open(env, envname, envflags | MDB_RDONLY, 0664); + if (rc) { + fprintf(stderr, "mdb_env_open failed, error %d %s\n", rc, mdb_strerror(rc)); + goto env_close; + } + + rc = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn); + if (rc) { + fprintf(stderr, "mdb_txn_begin failed, error %d %s\n", rc, mdb_strerror(rc)); + goto env_close; + } + + rc = mdb_open(txn, subname, 0, &dbi); + if (rc) { + fprintf(stderr, "mdb_open failed, error %d %s\n", rc, mdb_strerror(rc)); + goto txn_abort; + } + + if (alldbs) { + MDB_cursor *cursor; + MDB_val key; + int count = 0; + + rc = mdb_cursor_open(txn, dbi, &cursor); + if (rc) { + fprintf(stderr, "mdb_cursor_open failed, error %d %s\n", rc, mdb_strerror(rc)); + goto txn_abort; + } + while ((rc = mdb_cursor_get(cursor, &key, NULL, MDB_NEXT_NODUP)) == 0) { + char *str; + MDB_dbi db2; + if (memchr(key.mv_data, '\0', key.mv_size)) + continue; + count++; + str = malloc(key.mv_size+1); + memcpy(str, key.mv_data, key.mv_size); + str[key.mv_size] = '\0'; + rc = mdb_open(txn, str, 0, &db2); + if (rc == MDB_SUCCESS) { + if (list) { + printf("%s\n", str); + list++; + } else { + rc = dumpit(txn, db2, str); + if (rc) + break; + } + mdb_close(env, db2); + } + free(str); + if (rc) continue; + } + mdb_cursor_close(cursor); + if (!count) { + fprintf(stderr, "%s: %s does not contain multiple databases\n", prog, envname); + rc = MDB_NOTFOUND; + } else if (rc == MDB_NOTFOUND) { + rc = MDB_SUCCESS; + } + } else { + rc = dumpit(txn, dbi, subname); + } + if (rc && rc != MDB_NOTFOUND) + fprintf(stderr, "%s: %s: %s\n", prog, envname, mdb_strerror(rc)); + + mdb_close(env, dbi); +txn_abort: + mdb_txn_abort(txn); +env_close: + mdb_env_close(env); + + return rc ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/libraries/liblmdb/mdb_load.1 b/libraries/liblmdb/mdb_load.1 new file mode 100644 index 0000000..9c58485 --- /dev/null +++ b/libraries/liblmdb/mdb_load.1 @@ -0,0 +1,77 @@ +.TH MDB_LOAD 1 "2015/09/30" "LMDB 0.9.17" +.\" Copyright 2014-2020 Howard Chu, Symas Corp. All Rights Reserved. +.\" Copying restrictions apply. See COPYRIGHT/LICENSE. +.SH NAME +mdb_load \- LMDB environment import tool +.SH SYNOPSIS +.B mdb_load +[\c +.BR \-V ] +[\c +.BI \-f \ file\fR] +[\c +.BR \-n ] +[\c +.BI \-s \ subdb\fR] +[\c +.BR \-N ] +[\c +.BR \-T ] +.BR \ envpath +.SH DESCRIPTION +The +.B mdb_load +utility reads from the standard input and loads it into the +LMDB environment +.BR envpath . + +The input to +.B mdb_load +must be in the output format specified by the +.BR mdb_dump (1) +utility or as specified by the +.B -T +option below. +.SH OPTIONS +.TP +.BR \-V +Write the library version number to the standard output, and exit. +.TP +.BR \-f \ file +Read from the specified file instead of from the standard input. +.TP +.BR \-n +Load an LMDB database which does not use subdirectories. +.TP +.BR \-s \ subdb +Load a specific subdatabase. If no database is specified, data is loaded into the main database. +.TP +.BR \-N +Don't overwrite existing records when loading into an already existing database; just skip them. +.TP +.BR \-T +Load data from simple text files. The input must be paired lines of text, where the first +line of the pair is the key item, and the second line of the pair is its corresponding +data item. + +A simple escape mechanism, where newline and backslash (\\) characters are special, is +applied to the text input. Newline characters are interpreted as record separators. +Backslash characters in the text will be interpreted in one of two ways: If the backslash +character precedes another backslash character, the pair will be interpreted as a literal +backslash. If the backslash character precedes any other character, the two characters +following the backslash will be interpreted as a hexadecimal specification of a single +character; for example, \\0a is a newline character in the ASCII character set. + +For this reason, any backslash or newline characters that naturally occur in the text +input must be escaped to avoid misinterpretation by +.BR mdb_load . + +.SH DIAGNOSTICS +Exit status is zero if no errors occur. +Errors result in a non-zero exit status and +a diagnostic message being written to standard error. + +.SH "SEE ALSO" +.BR mdb_dump (1) +.SH AUTHOR +Howard Chu of Symas Corporation <http://www.symas.com> diff --git a/libraries/liblmdb/mdb_load.c b/libraries/liblmdb/mdb_load.c new file mode 100644 index 0000000..c4bcfc2 --- /dev/null +++ b/libraries/liblmdb/mdb_load.c @@ -0,0 +1,457 @@ +/* mdb_load.c - memory-mapped database load tool */ +/* + * Copyright 2011-2020 Howard Chu, Symas Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <ctype.h> +#include <unistd.h> +#include "lmdb.h" + +#define PRINT 1 +#define NOHDR 2 +static int mode; + +static char *subname = NULL; + +static size_t lineno; +static int version; + +static int flags; + +static char *prog; + +static int Eof; + +static MDB_envinfo info; + +static MDB_val kbuf, dbuf; + +#ifdef _WIN32 +#define Z "I" +#else +#define Z "z" +#endif + +#define STRLENOF(s) (sizeof(s)-1) + +typedef struct flagbit { + int bit; + char *name; + int len; +} flagbit; + +#define S(s) s, STRLENOF(s) + +flagbit dbflags[] = { + { MDB_REVERSEKEY, S("reversekey") }, + { MDB_DUPSORT, S("dupsort") }, + { MDB_INTEGERKEY, S("integerkey") }, + { MDB_DUPFIXED, S("dupfixed") }, + { MDB_INTEGERDUP, S("integerdup") }, + { MDB_REVERSEDUP, S("reversedup") }, + { 0, NULL, 0 } +}; + +static void readhdr(void) +{ + char *ptr; + + flags = 0; + while (fgets(dbuf.mv_data, dbuf.mv_size, stdin) != NULL) { + lineno++; + if (!strncmp(dbuf.mv_data, "VERSION=", STRLENOF("VERSION="))) { + version=atoi((char *)dbuf.mv_data+STRLENOF("VERSION=")); + if (version > 3) { + fprintf(stderr, "%s: line %" Z "d: unsupported VERSION %d\n", + prog, lineno, version); + exit(EXIT_FAILURE); + } + } else if (!strncmp(dbuf.mv_data, "HEADER=END", STRLENOF("HEADER=END"))) { + break; + } else if (!strncmp(dbuf.mv_data, "format=", STRLENOF("format="))) { + if (!strncmp((char *)dbuf.mv_data+STRLENOF("FORMAT="), "print", STRLENOF("print"))) + mode |= PRINT; + else if (strncmp((char *)dbuf.mv_data+STRLENOF("FORMAT="), "bytevalue", STRLENOF("bytevalue"))) { + fprintf(stderr, "%s: line %" Z "d: unsupported FORMAT %s\n", + prog, lineno, (char *)dbuf.mv_data+STRLENOF("FORMAT=")); + exit(EXIT_FAILURE); + } + } else if (!strncmp(dbuf.mv_data, "database=", STRLENOF("database="))) { + ptr = memchr(dbuf.mv_data, '\n', dbuf.mv_size); + if (ptr) *ptr = '\0'; + if (subname) free(subname); + subname = strdup((char *)dbuf.mv_data+STRLENOF("database=")); + } else if (!strncmp(dbuf.mv_data, "type=", STRLENOF("type="))) { + if (strncmp((char *)dbuf.mv_data+STRLENOF("type="), "btree", STRLENOF("btree"))) { + fprintf(stderr, "%s: line %" Z "d: unsupported type %s\n", + prog, lineno, (char *)dbuf.mv_data+STRLENOF("type=")); + exit(EXIT_FAILURE); + } + } else if (!strncmp(dbuf.mv_data, "mapaddr=", STRLENOF("mapaddr="))) { + int i; + ptr = memchr(dbuf.mv_data, '\n', dbuf.mv_size); + if (ptr) *ptr = '\0'; + i = sscanf((char *)dbuf.mv_data+STRLENOF("mapaddr="), "%p", &info.me_mapaddr); + if (i != 1) { + fprintf(stderr, "%s: line %" Z "d: invalid mapaddr %s\n", + prog, lineno, (char *)dbuf.mv_data+STRLENOF("mapaddr=")); + exit(EXIT_FAILURE); + } + } else if (!strncmp(dbuf.mv_data, "mapsize=", STRLENOF("mapsize="))) { + int i; + ptr = memchr(dbuf.mv_data, '\n', dbuf.mv_size); + if (ptr) *ptr = '\0'; + i = sscanf((char *)dbuf.mv_data+STRLENOF("mapsize="), "%" Z "u", &info.me_mapsize); + if (i != 1) { + fprintf(stderr, "%s: line %" Z "d: invalid mapsize %s\n", + prog, lineno, (char *)dbuf.mv_data+STRLENOF("mapsize=")); + exit(EXIT_FAILURE); + } + } else if (!strncmp(dbuf.mv_data, "maxreaders=", STRLENOF("maxreaders="))) { + int i; + ptr = memchr(dbuf.mv_data, '\n', dbuf.mv_size); + if (ptr) *ptr = '\0'; + i = sscanf((char *)dbuf.mv_data+STRLENOF("maxreaders="), "%u", &info.me_maxreaders); + if (i != 1) { + fprintf(stderr, "%s: line %" Z "d: invalid maxreaders %s\n", + prog, lineno, (char *)dbuf.mv_data+STRLENOF("maxreaders=")); + exit(EXIT_FAILURE); + } + } else { + int i; + for (i=0; dbflags[i].bit; i++) { + if (!strncmp(dbuf.mv_data, dbflags[i].name, dbflags[i].len) && + ((char *)dbuf.mv_data)[dbflags[i].len] == '=') { + flags |= dbflags[i].bit; + break; + } + } + if (!dbflags[i].bit) { + ptr = memchr(dbuf.mv_data, '=', dbuf.mv_size); + if (!ptr) { + fprintf(stderr, "%s: line %" Z "d: unexpected format\n", + prog, lineno); + exit(EXIT_FAILURE); + } else { + *ptr = '\0'; + fprintf(stderr, "%s: line %" Z "d: unrecognized keyword ignored: %s\n", + prog, lineno, (char *)dbuf.mv_data); + } + } + } + } +} + +static void badend(void) +{ + fprintf(stderr, "%s: line %" Z "d: unexpected end of input\n", + prog, lineno); +} + +static int unhex(unsigned char *c2) +{ + int x, c; + x = *c2++ & 0x4f; + if (x & 0x40) + x -= 55; + c = x << 4; + x = *c2 & 0x4f; + if (x & 0x40) + x -= 55; + c |= x; + return c; +} + +static int readline(MDB_val *out, MDB_val *buf) +{ + unsigned char *c1, *c2, *end; + size_t len, l2; + int c; + + if (!(mode & NOHDR)) { + c = fgetc(stdin); + if (c == EOF) { + Eof = 1; + return EOF; + } + if (c != ' ') { + lineno++; + if (fgets(buf->mv_data, buf->mv_size, stdin) == NULL) { +badend: + Eof = 1; + badend(); + return EOF; + } + if (c == 'D' && !strncmp(buf->mv_data, "ATA=END", STRLENOF("ATA=END"))) + return EOF; + goto badend; + } + } + if (fgets(buf->mv_data, buf->mv_size, stdin) == NULL) { + Eof = 1; + return EOF; + } + lineno++; + + c1 = buf->mv_data; + len = strlen((char *)c1); + l2 = len; + + /* Is buffer too short? */ + while (c1[len-1] != '\n') { + buf->mv_data = realloc(buf->mv_data, buf->mv_size*2); + if (!buf->mv_data) { + Eof = 1; + fprintf(stderr, "%s: line %" Z "d: out of memory, line too long\n", + prog, lineno); + return EOF; + } + c1 = buf->mv_data; + c1 += l2; + if (fgets((char *)c1, buf->mv_size+1, stdin) == NULL) { + Eof = 1; + badend(); + return EOF; + } + buf->mv_size *= 2; + len = strlen((char *)c1); + l2 += len; + } + c1 = c2 = buf->mv_data; + len = l2; + c1[--len] = '\0'; + end = c1 + len; + + if (mode & PRINT) { + while (c2 < end) { + if (*c2 == '\\') { + if (c2[1] == '\\') { + *c1++ = *c2; + } else { + if (c2+3 > end || !isxdigit(c2[1]) || !isxdigit(c2[2])) { + Eof = 1; + badend(); + return EOF; + } + *c1++ = unhex(++c2); + } + c2 += 2; + } else { + /* copies are redundant when no escapes were used */ + *c1++ = *c2++; + } + } + } else { + /* odd length not allowed */ + if (len & 1) { + Eof = 1; + badend(); + return EOF; + } + while (c2 < end) { + if (!isxdigit(*c2) || !isxdigit(c2[1])) { + Eof = 1; + badend(); + return EOF; + } + *c1++ = unhex(c2); + c2 += 2; + } + } + c2 = out->mv_data = buf->mv_data; + out->mv_size = c1 - c2; + + return 0; +} + +static void usage(void) +{ + fprintf(stderr, "usage: %s [-V] [-f input] [-n] [-s name] [-N] [-T] dbpath\n", prog); + exit(EXIT_FAILURE); +} + +int main(int argc, char *argv[]) +{ + int i, rc; + MDB_env *env; + MDB_txn *txn; + MDB_cursor *mc; + MDB_dbi dbi; + char *envname; + int envflags = 0, putflags = 0; + int dohdr = 0; + + prog = argv[0]; + + if (argc < 2) { + usage(); + } + + /* -f: load file instead of stdin + * -n: use NOSUBDIR flag on env_open + * -s: load into named subDB + * -N: use NOOVERWRITE on puts + * -T: read plaintext + * -V: print version and exit + */ + while ((i = getopt(argc, argv, "f:ns:NTV")) != EOF) { + switch(i) { + case 'V': + printf("%s\n", MDB_VERSION_STRING); + exit(0); + break; + case 'f': + if (freopen(optarg, "r", stdin) == NULL) { + fprintf(stderr, "%s: %s: reopen: %s\n", + prog, optarg, strerror(errno)); + exit(EXIT_FAILURE); + } + break; + case 'n': + envflags |= MDB_NOSUBDIR; + break; + case 's': + subname = strdup(optarg); + break; + case 'N': + putflags = MDB_NOOVERWRITE|MDB_NODUPDATA; + break; + case 'T': + mode |= NOHDR | PRINT; + break; + default: + usage(); + } + } + + if (optind != argc - 1) + usage(); + + dbuf.mv_size = 4096; + dbuf.mv_data = malloc(dbuf.mv_size); + + if (!(mode & NOHDR)) + readhdr(); + + envname = argv[optind]; + rc = mdb_env_create(&env); + if (rc) { + fprintf(stderr, "mdb_env_create failed, error %d %s\n", rc, mdb_strerror(rc)); + return EXIT_FAILURE; + } + + mdb_env_set_maxdbs(env, 2); + + if (info.me_maxreaders) + mdb_env_set_maxreaders(env, info.me_maxreaders); + + if (info.me_mapsize) + mdb_env_set_mapsize(env, info.me_mapsize); + + if (info.me_mapaddr) + envflags |= MDB_FIXEDMAP; + + rc = mdb_env_open(env, envname, envflags, 0664); + if (rc) { + fprintf(stderr, "mdb_env_open failed, error %d %s\n", rc, mdb_strerror(rc)); + goto env_close; + } + + kbuf.mv_size = mdb_env_get_maxkeysize(env) * 2 + 2; + kbuf.mv_data = malloc(kbuf.mv_size); + + while(!Eof) { + MDB_val key, data; + int batch = 0; + + if (!dohdr) { + dohdr = 1; + } else if (!(mode & NOHDR)) + readhdr(); + + rc = mdb_txn_begin(env, NULL, 0, &txn); + if (rc) { + fprintf(stderr, "mdb_txn_begin failed, error %d %s\n", rc, mdb_strerror(rc)); + goto env_close; + } + + rc = mdb_open(txn, subname, flags|MDB_CREATE, &dbi); + if (rc) { + fprintf(stderr, "mdb_open failed, error %d %s\n", rc, mdb_strerror(rc)); + goto txn_abort; + } + + rc = mdb_cursor_open(txn, dbi, &mc); + if (rc) { + fprintf(stderr, "mdb_cursor_open failed, error %d %s\n", rc, mdb_strerror(rc)); + goto txn_abort; + } + + while(1) { + rc = readline(&key, &kbuf); + if (rc) /* rc == EOF */ + break; + + rc = readline(&data, &dbuf); + if (rc) { + fprintf(stderr, "%s: line %" Z "d: failed to read key value\n", prog, lineno); + goto txn_abort; + } + + rc = mdb_cursor_put(mc, &key, &data, putflags); + if (rc == MDB_KEYEXIST && putflags) + continue; + if (rc) { + fprintf(stderr, "mdb_cursor_put failed, error %d %s\n", rc, mdb_strerror(rc)); + goto txn_abort; + } + batch++; + if (batch == 100) { + rc = mdb_txn_commit(txn); + if (rc) { + fprintf(stderr, "%s: line %" Z "d: txn_commit: %s\n", + prog, lineno, mdb_strerror(rc)); + goto env_close; + } + rc = mdb_txn_begin(env, NULL, 0, &txn); + if (rc) { + fprintf(stderr, "mdb_txn_begin failed, error %d %s\n", rc, mdb_strerror(rc)); + goto env_close; + } + rc = mdb_cursor_open(txn, dbi, &mc); + if (rc) { + fprintf(stderr, "mdb_cursor_open failed, error %d %s\n", rc, mdb_strerror(rc)); + goto txn_abort; + } + batch = 0; + } + } + rc = mdb_txn_commit(txn); + txn = NULL; + if (rc) { + fprintf(stderr, "%s: line %" Z "d: txn_commit: %s\n", + prog, lineno, mdb_strerror(rc)); + goto env_close; + } + mdb_dbi_close(env, dbi); + } + +txn_abort: + mdb_txn_abort(txn); +env_close: + mdb_env_close(env); + + return rc ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/libraries/liblmdb/mdb_stat.1 b/libraries/liblmdb/mdb_stat.1 new file mode 100644 index 0000000..e805bbc --- /dev/null +++ b/libraries/liblmdb/mdb_stat.1 @@ -0,0 +1,64 @@ +.TH MDB_STAT 1 "2015/09/30" "LMDB 0.9.17" +.\" Copyright 2012-2020 Howard Chu, Symas Corp. All Rights Reserved. +.\" Copying restrictions apply. See COPYRIGHT/LICENSE. +.SH NAME +mdb_stat \- LMDB environment status tool +.SH SYNOPSIS +.B mdb_stat +[\c +.BR \-V ] +[\c +.BR \-e ] +[\c +.BR \-f [ f [ f ]]] +[\c +.BR \-n ] +[\c +.BR \-r [ r ]] +[\c +.BR \-a \ | +.BI \-s \ subdb\fR] +.BR \ envpath +.SH DESCRIPTION +The +.B mdb_stat +utility displays the status of an LMDB environment. +.SH OPTIONS +.TP +.BR \-V +Write the library version number to the standard output, and exit. +.TP +.BR \-e +Display information about the database environment. +.TP +.BR \-f +Display information about the environment freelist. +If \fB\-ff\fP is given, summarize each freelist entry. +If \fB\-fff\fP is given, display the full list of page IDs in the freelist. +.TP +.BR \-n +Display the status of an LMDB database which does not use subdirectories. +.TP +.BR \-r +Display information about the environment reader table. +Shows the process ID, thread ID, and transaction ID for each active +reader slot. The process ID and transaction ID are in decimal, the +thread ID is in hexadecimal. The transaction ID is displayed as "-" +if the reader does not currently have a read transaction open. +If \fB\-rr\fP is given, check for stale entries in the reader +table and clear them. The reader table will be printed again +after the check is performed. +.TP +.BR \-a +Display the status of all of the subdatabases in the environment. +.TP +.BR \-s \ subdb +Display the status of a specific subdatabase. +.SH DIAGNOSTICS +Exit status is zero if no errors occur. +Errors result in a non-zero exit status and +a diagnostic message being written to standard error. +.SH "SEE ALSO" +.BR mdb_copy (1) +.SH AUTHOR +Howard Chu of Symas Corporation <http://www.symas.com> diff --git a/libraries/liblmdb/mdb_stat.c b/libraries/liblmdb/mdb_stat.c new file mode 100644 index 0000000..cb73e11 --- /dev/null +++ b/libraries/liblmdb/mdb_stat.c @@ -0,0 +1,263 @@ +/* mdb_stat.c - memory-mapped database status tool */ +/* + * Copyright 2011-2020 Howard Chu, Symas Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include "lmdb.h" + +#ifdef _WIN32 +#define Z "I" +#else +#define Z "z" +#endif + +static void prstat(MDB_stat *ms) +{ +#if 0 + printf(" Page size: %u\n", ms->ms_psize); +#endif + printf(" Tree depth: %u\n", ms->ms_depth); + printf(" Branch pages: %"Z"u\n", ms->ms_branch_pages); + printf(" Leaf pages: %"Z"u\n", ms->ms_leaf_pages); + printf(" Overflow pages: %"Z"u\n", ms->ms_overflow_pages); + printf(" Entries: %"Z"u\n", ms->ms_entries); +} + +static void usage(char *prog) +{ + fprintf(stderr, "usage: %s [-V] [-n] [-e] [-r[r]] [-f[f[f]]] [-a|-s subdb] dbpath\n", prog); + exit(EXIT_FAILURE); +} + +int main(int argc, char *argv[]) +{ + int i, rc; + MDB_env *env; + MDB_txn *txn; + MDB_dbi dbi; + MDB_stat mst; + MDB_envinfo mei; + char *prog = argv[0]; + char *envname; + char *subname = NULL; + int alldbs = 0, envinfo = 0, envflags = 0, freinfo = 0, rdrinfo = 0; + + if (argc < 2) { + usage(prog); + } + + /* -a: print stat of main DB and all subDBs + * -s: print stat of only the named subDB + * -e: print env info + * -f: print freelist info + * -r: print reader info + * -n: use NOSUBDIR flag on env_open + * -V: print version and exit + * (default) print stat of only the main DB + */ + while ((i = getopt(argc, argv, "Vaefnrs:")) != EOF) { + switch(i) { + case 'V': + printf("%s\n", MDB_VERSION_STRING); + exit(0); + break; + case 'a': + if (subname) + usage(prog); + alldbs++; + break; + case 'e': + envinfo++; + break; + case 'f': + freinfo++; + break; + case 'n': + envflags |= MDB_NOSUBDIR; + break; + case 'r': + rdrinfo++; + break; + case 's': + if (alldbs) + usage(prog); + subname = optarg; + break; + default: + usage(prog); + } + } + + if (optind != argc - 1) + usage(prog); + + envname = argv[optind]; + rc = mdb_env_create(&env); + if (rc) { + fprintf(stderr, "mdb_env_create failed, error %d %s\n", rc, mdb_strerror(rc)); + return EXIT_FAILURE; + } + + if (alldbs || subname) { + mdb_env_set_maxdbs(env, 4); + } + + rc = mdb_env_open(env, envname, envflags | MDB_RDONLY, 0664); + if (rc) { + fprintf(stderr, "mdb_env_open failed, error %d %s\n", rc, mdb_strerror(rc)); + goto env_close; + } + + if (envinfo) { + (void)mdb_env_stat(env, &mst); + (void)mdb_env_info(env, &mei); + printf("Environment Info\n"); + printf(" Map address: %p\n", mei.me_mapaddr); + printf(" Map size: %"Z"u\n", mei.me_mapsize); + printf(" Page size: %u\n", mst.ms_psize); + printf(" Max pages: %"Z"u\n", mei.me_mapsize / mst.ms_psize); + printf(" Number of pages used: %"Z"u\n", mei.me_last_pgno+1); + printf(" Last transaction ID: %"Z"u\n", mei.me_last_txnid); + printf(" Max readers: %u\n", mei.me_maxreaders); + printf(" Number of readers used: %u\n", mei.me_numreaders); + } + + if (rdrinfo) { + printf("Reader Table Status\n"); + rc = mdb_reader_list(env, (MDB_msg_func *)fputs, stdout); + if (rdrinfo > 1) { + int dead; + mdb_reader_check(env, &dead); + printf(" %d stale readers cleared.\n", dead); + rc = mdb_reader_list(env, (MDB_msg_func *)fputs, stdout); + } + if (!(subname || alldbs || freinfo)) + goto env_close; + } + + rc = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn); + if (rc) { + fprintf(stderr, "mdb_txn_begin failed, error %d %s\n", rc, mdb_strerror(rc)); + goto env_close; + } + + if (freinfo) { + MDB_cursor *cursor; + MDB_val key, data; + size_t pages = 0, *iptr; + + printf("Freelist Status\n"); + dbi = 0; + rc = mdb_cursor_open(txn, dbi, &cursor); + if (rc) { + fprintf(stderr, "mdb_cursor_open failed, error %d %s\n", rc, mdb_strerror(rc)); + goto txn_abort; + } + rc = mdb_stat(txn, dbi, &mst); + if (rc) { + fprintf(stderr, "mdb_stat failed, error %d %s\n", rc, mdb_strerror(rc)); + goto txn_abort; + } + prstat(&mst); + while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) { + iptr = data.mv_data; + pages += *iptr; + if (freinfo > 1) { + char *bad = ""; + size_t pg, prev; + ssize_t i, j, span = 0; + j = *iptr++; + for (i = j, prev = 1; --i >= 0; ) { + pg = iptr[i]; + if (pg <= prev) + bad = " [bad sequence]"; + prev = pg; + pg += span; + for (; i >= span && iptr[i-span] == pg; span++, pg++) ; + } + printf(" Transaction %"Z"u, %"Z"d pages, maxspan %"Z"d%s\n", + *(size_t *)key.mv_data, j, span, bad); + if (freinfo > 2) { + for (--j; j >= 0; ) { + pg = iptr[j]; + for (span=1; --j >= 0 && iptr[j] == pg+span; span++) ; + printf(span>1 ? " %9"Z"u[%"Z"d]\n" : " %9"Z"u\n", + pg, span); + } + } + } + } + mdb_cursor_close(cursor); + printf(" Free pages: %"Z"u\n", pages); + } + + rc = mdb_open(txn, subname, 0, &dbi); + if (rc) { + fprintf(stderr, "mdb_open failed, error %d %s\n", rc, mdb_strerror(rc)); + goto txn_abort; + } + + rc = mdb_stat(txn, dbi, &mst); + if (rc) { + fprintf(stderr, "mdb_stat failed, error %d %s\n", rc, mdb_strerror(rc)); + goto txn_abort; + } + printf("Status of %s\n", subname ? subname : "Main DB"); + prstat(&mst); + + if (alldbs) { + MDB_cursor *cursor; + MDB_val key; + + rc = mdb_cursor_open(txn, dbi, &cursor); + if (rc) { + fprintf(stderr, "mdb_cursor_open failed, error %d %s\n", rc, mdb_strerror(rc)); + goto txn_abort; + } + while ((rc = mdb_cursor_get(cursor, &key, NULL, MDB_NEXT_NODUP)) == 0) { + char *str; + MDB_dbi db2; + if (memchr(key.mv_data, '\0', key.mv_size)) + continue; + str = malloc(key.mv_size+1); + memcpy(str, key.mv_data, key.mv_size); + str[key.mv_size] = '\0'; + rc = mdb_open(txn, str, 0, &db2); + if (rc == MDB_SUCCESS) + printf("Status of %s\n", str); + free(str); + if (rc) continue; + rc = mdb_stat(txn, db2, &mst); + if (rc) { + fprintf(stderr, "mdb_stat failed, error %d %s\n", rc, mdb_strerror(rc)); + goto txn_abort; + } + prstat(&mst); + mdb_close(env, db2); + } + mdb_cursor_close(cursor); + } + + if (rc == MDB_NOTFOUND) + rc = MDB_SUCCESS; + + mdb_close(env, dbi); +txn_abort: + mdb_txn_abort(txn); +env_close: + mdb_env_close(env); + + return rc ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/libraries/liblmdb/midl.c b/libraries/liblmdb/midl.c new file mode 100644 index 0000000..ab32129 --- /dev/null +++ b/libraries/liblmdb/midl.c @@ -0,0 +1,359 @@ +/** @file midl.c + * @brief ldap bdb back-end ID List functions */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2000-2020 The OpenLDAP Foundation. + * Portions Copyright 2001-2020 Howard Chu, Symas Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include <limits.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/types.h> +#include "midl.h" + +/** @defgroup internal LMDB Internals + * @{ + */ +/** @defgroup idls ID List Management + * @{ + */ +#define CMP(x,y) ( (x) < (y) ? -1 : (x) > (y) ) + +unsigned mdb_midl_search( MDB_IDL ids, MDB_ID id ) +{ + /* + * binary search of id in ids + * if found, returns position of id + * if not found, returns first position greater than id + */ + unsigned base = 0; + unsigned cursor = 1; + int val = 0; + unsigned n = ids[0]; + + while( 0 < n ) { + unsigned pivot = n >> 1; + cursor = base + pivot + 1; + val = CMP( ids[cursor], id ); + + if( val < 0 ) { + n = pivot; + + } else if ( val > 0 ) { + base = cursor; + n -= pivot + 1; + + } else { + return cursor; + } + } + + if( val > 0 ) { + ++cursor; + } + return cursor; +} + +#if 0 /* superseded by append/sort */ +int mdb_midl_insert( MDB_IDL ids, MDB_ID id ) +{ + unsigned x, i; + + x = mdb_midl_search( ids, id ); + assert( x > 0 ); + + if( x < 1 ) { + /* internal error */ + return -2; + } + + if ( x <= ids[0] && ids[x] == id ) { + /* duplicate */ + assert(0); + return -1; + } + + if ( ++ids[0] >= MDB_IDL_DB_MAX ) { + /* no room */ + --ids[0]; + return -2; + + } else { + /* insert id */ + for (i=ids[0]; i>x; i--) + ids[i] = ids[i-1]; + ids[x] = id; + } + + return 0; +} +#endif + +MDB_IDL mdb_midl_alloc(int num) +{ + MDB_IDL ids = malloc((num+2) * sizeof(MDB_ID)); + if (ids) { + *ids++ = num; + *ids = 0; + } + return ids; +} + +void mdb_midl_free(MDB_IDL ids) +{ + if (ids) + free(ids-1); +} + +void mdb_midl_shrink( MDB_IDL *idp ) +{ + MDB_IDL ids = *idp; + if (*(--ids) > MDB_IDL_UM_MAX && + (ids = realloc(ids, (MDB_IDL_UM_MAX+2) * sizeof(MDB_ID)))) + { + *ids++ = MDB_IDL_UM_MAX; + *idp = ids; + } +} + +static int mdb_midl_grow( MDB_IDL *idp, int num ) +{ + MDB_IDL idn = *idp-1; + /* grow it */ + idn = realloc(idn, (*idn + num + 2) * sizeof(MDB_ID)); + if (!idn) + return ENOMEM; + *idn++ += num; + *idp = idn; + return 0; +} + +int mdb_midl_need( MDB_IDL *idp, unsigned num ) +{ + MDB_IDL ids = *idp; + num += ids[0]; + if (num > ids[-1]) { + num = (num + num/4 + (256 + 2)) & -256; + if (!(ids = realloc(ids-1, num * sizeof(MDB_ID)))) + return ENOMEM; + *ids++ = num - 2; + *idp = ids; + } + return 0; +} + +int mdb_midl_append( MDB_IDL *idp, MDB_ID id ) +{ + MDB_IDL ids = *idp; + /* Too big? */ + if (ids[0] >= ids[-1]) { + if (mdb_midl_grow(idp, MDB_IDL_UM_MAX)) + return ENOMEM; + ids = *idp; + } + ids[0]++; + ids[ids[0]] = id; + return 0; +} + +int mdb_midl_append_list( MDB_IDL *idp, MDB_IDL app ) +{ + MDB_IDL ids = *idp; + /* Too big? */ + if (ids[0] + app[0] >= ids[-1]) { + if (mdb_midl_grow(idp, app[0])) + return ENOMEM; + ids = *idp; + } + memcpy(&ids[ids[0]+1], &app[1], app[0] * sizeof(MDB_ID)); + ids[0] += app[0]; + return 0; +} + +int mdb_midl_append_range( MDB_IDL *idp, MDB_ID id, unsigned n ) +{ + MDB_ID *ids = *idp, len = ids[0]; + /* Too big? */ + if (len + n > ids[-1]) { + if (mdb_midl_grow(idp, n | MDB_IDL_UM_MAX)) + return ENOMEM; + ids = *idp; + } + ids[0] = len + n; + ids += len; + while (n) + ids[n--] = id++; + return 0; +} + +void mdb_midl_xmerge( MDB_IDL idl, MDB_IDL merge ) +{ + MDB_ID old_id, merge_id, i = merge[0], j = idl[0], k = i+j, total = k; + idl[0] = (MDB_ID)-1; /* delimiter for idl scan below */ + old_id = idl[j]; + while (i) { + merge_id = merge[i--]; + for (; old_id < merge_id; old_id = idl[--j]) + idl[k--] = old_id; + idl[k--] = merge_id; + } + idl[0] = total; +} + +/* Quicksort + Insertion sort for small arrays */ + +#define SMALL 8 +#define MIDL_SWAP(a,b) { itmp=(a); (a)=(b); (b)=itmp; } + +void +mdb_midl_sort( MDB_IDL ids ) +{ + /* Max possible depth of int-indexed tree * 2 items/level */ + int istack[sizeof(int)*CHAR_BIT * 2]; + int i,j,k,l,ir,jstack; + MDB_ID a, itmp; + + ir = (int)ids[0]; + l = 1; + jstack = 0; + for(;;) { + if (ir - l < SMALL) { /* Insertion sort */ + for (j=l+1;j<=ir;j++) { + a = ids[j]; + for (i=j-1;i>=1;i--) { + if (ids[i] >= a) break; + ids[i+1] = ids[i]; + } + ids[i+1] = a; + } + if (jstack == 0) break; + ir = istack[jstack--]; + l = istack[jstack--]; + } else { + k = (l + ir) >> 1; /* Choose median of left, center, right */ + MIDL_SWAP(ids[k], ids[l+1]); + if (ids[l] < ids[ir]) { + MIDL_SWAP(ids[l], ids[ir]); + } + if (ids[l+1] < ids[ir]) { + MIDL_SWAP(ids[l+1], ids[ir]); + } + if (ids[l] < ids[l+1]) { + MIDL_SWAP(ids[l], ids[l+1]); + } + i = l+1; + j = ir; + a = ids[l+1]; + for(;;) { + do i++; while(ids[i] > a); + do j--; while(ids[j] < a); + if (j < i) break; + MIDL_SWAP(ids[i],ids[j]); + } + ids[l+1] = ids[j]; + ids[j] = a; + jstack += 2; + if (ir-i+1 >= j-l) { + istack[jstack] = ir; + istack[jstack-1] = i; + ir = j-1; + } else { + istack[jstack] = j-1; + istack[jstack-1] = l; + l = i; + } + } + } +} + +unsigned mdb_mid2l_search( MDB_ID2L ids, MDB_ID id ) +{ + /* + * binary search of id in ids + * if found, returns position of id + * if not found, returns first position greater than id + */ + unsigned base = 0; + unsigned cursor = 1; + int val = 0; + unsigned n = (unsigned)ids[0].mid; + + while( 0 < n ) { + unsigned pivot = n >> 1; + cursor = base + pivot + 1; + val = CMP( id, ids[cursor].mid ); + + if( val < 0 ) { + n = pivot; + + } else if ( val > 0 ) { + base = cursor; + n -= pivot + 1; + + } else { + return cursor; + } + } + + if( val > 0 ) { + ++cursor; + } + return cursor; +} + +int mdb_mid2l_insert( MDB_ID2L ids, MDB_ID2 *id ) +{ + unsigned x, i; + + x = mdb_mid2l_search( ids, id->mid ); + + if( x < 1 ) { + /* internal error */ + return -2; + } + + if ( x <= ids[0].mid && ids[x].mid == id->mid ) { + /* duplicate */ + return -1; + } + + if ( ids[0].mid >= MDB_IDL_UM_MAX ) { + /* too big */ + return -2; + + } else { + /* insert id */ + ids[0].mid++; + for (i=(unsigned)ids[0].mid; i>x; i--) + ids[i] = ids[i-1]; + ids[x] = *id; + } + + return 0; +} + +int mdb_mid2l_append( MDB_ID2L ids, MDB_ID2 *id ) +{ + /* Too big? */ + if (ids[0].mid >= MDB_IDL_UM_MAX) { + return -2; + } + ids[0].mid++; + ids[ids[0].mid] = *id; + return 0; +} + +/** @} */ +/** @} */ diff --git a/libraries/liblmdb/midl.h b/libraries/liblmdb/midl.h new file mode 100644 index 0000000..6bb9cf6 --- /dev/null +++ b/libraries/liblmdb/midl.h @@ -0,0 +1,186 @@ +/** @file midl.h + * @brief LMDB ID List header file. + * + * This file was originally part of back-bdb but has been + * modified for use in libmdb. Most of the macros defined + * in this file are unused, just left over from the original. + * + * This file is only used internally in libmdb and its definitions + * are not exposed publicly. + */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2000-2020 The OpenLDAP Foundation. + * Portions Copyright 2001-2020 Howard Chu, Symas Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#ifndef _MDB_MIDL_H_ +#define _MDB_MIDL_H_ + +#include <stddef.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** @defgroup internal LMDB Internals + * @{ + */ + +/** @defgroup idls ID List Management + * @{ + */ + /** A generic unsigned ID number. These were entryIDs in back-bdb. + * Preferably it should have the same size as a pointer. + */ +typedef size_t MDB_ID; + + /** An IDL is an ID List, a sorted array of IDs. The first + * element of the array is a counter for how many actual + * IDs are in the list. In the original back-bdb code, IDLs are + * sorted in ascending order. For libmdb IDLs are sorted in + * descending order. + */ +typedef MDB_ID *MDB_IDL; + +/* IDL sizes - likely should be even bigger + * limiting factors: sizeof(ID), thread stack size + */ +#define MDB_IDL_LOGN 16 /* DB_SIZE is 2^16, UM_SIZE is 2^17 */ +#define MDB_IDL_DB_SIZE (1<<MDB_IDL_LOGN) +#define MDB_IDL_UM_SIZE (1<<(MDB_IDL_LOGN+1)) + +#define MDB_IDL_DB_MAX (MDB_IDL_DB_SIZE-1) +#define MDB_IDL_UM_MAX (MDB_IDL_UM_SIZE-1) + +#define MDB_IDL_SIZEOF(ids) (((ids)[0]+1) * sizeof(MDB_ID)) +#define MDB_IDL_IS_ZERO(ids) ( (ids)[0] == 0 ) +#define MDB_IDL_CPY( dst, src ) (memcpy( dst, src, MDB_IDL_SIZEOF( src ) )) +#define MDB_IDL_FIRST( ids ) ( (ids)[1] ) +#define MDB_IDL_LAST( ids ) ( (ids)[(ids)[0]] ) + + /** Current max length of an #mdb_midl_alloc()ed IDL */ +#define MDB_IDL_ALLOCLEN( ids ) ( (ids)[-1] ) + + /** Append ID to IDL. The IDL must be big enough. */ +#define mdb_midl_xappend(idl, id) do { \ + MDB_ID *xidl = (idl), xlen = ++(xidl[0]); \ + xidl[xlen] = (id); \ + } while (0) + + /** Search for an ID in an IDL. + * @param[in] ids The IDL to search. + * @param[in] id The ID to search for. + * @return The index of the first ID greater than or equal to \b id. + */ +unsigned mdb_midl_search( MDB_IDL ids, MDB_ID id ); + + /** Allocate an IDL. + * Allocates memory for an IDL of the given size. + * @return IDL on success, NULL on failure. + */ +MDB_IDL mdb_midl_alloc(int num); + + /** Free an IDL. + * @param[in] ids The IDL to free. + */ +void mdb_midl_free(MDB_IDL ids); + + /** Shrink an IDL. + * Return the IDL to the default size if it has grown larger. + * @param[in,out] idp Address of the IDL to shrink. + */ +void mdb_midl_shrink(MDB_IDL *idp); + + /** Make room for num additional elements in an IDL. + * @param[in,out] idp Address of the IDL. + * @param[in] num Number of elements to make room for. + * @return 0 on success, ENOMEM on failure. + */ +int mdb_midl_need(MDB_IDL *idp, unsigned num); + + /** Append an ID onto an IDL. + * @param[in,out] idp Address of the IDL to append to. + * @param[in] id The ID to append. + * @return 0 on success, ENOMEM if the IDL is too large. + */ +int mdb_midl_append( MDB_IDL *idp, MDB_ID id ); + + /** Append an IDL onto an IDL. + * @param[in,out] idp Address of the IDL to append to. + * @param[in] app The IDL to append. + * @return 0 on success, ENOMEM if the IDL is too large. + */ +int mdb_midl_append_list( MDB_IDL *idp, MDB_IDL app ); + + /** Append an ID range onto an IDL. + * @param[in,out] idp Address of the IDL to append to. + * @param[in] id The lowest ID to append. + * @param[in] n Number of IDs to append. + * @return 0 on success, ENOMEM if the IDL is too large. + */ +int mdb_midl_append_range( MDB_IDL *idp, MDB_ID id, unsigned n ); + + /** Merge an IDL onto an IDL. The destination IDL must be big enough. + * @param[in] idl The IDL to merge into. + * @param[in] merge The IDL to merge. + */ +void mdb_midl_xmerge( MDB_IDL idl, MDB_IDL merge ); + + /** Sort an IDL. + * @param[in,out] ids The IDL to sort. + */ +void mdb_midl_sort( MDB_IDL ids ); + + /** An ID2 is an ID/pointer pair. + */ +typedef struct MDB_ID2 { + MDB_ID mid; /**< The ID */ + void *mptr; /**< The pointer */ +} MDB_ID2; + + /** An ID2L is an ID2 List, a sorted array of ID2s. + * The first element's \b mid member is a count of how many actual + * elements are in the array. The \b mptr member of the first element is unused. + * The array is sorted in ascending order by \b mid. + */ +typedef MDB_ID2 *MDB_ID2L; + + /** Search for an ID in an ID2L. + * @param[in] ids The ID2L to search. + * @param[in] id The ID to search for. + * @return The index of the first ID2 whose \b mid member is greater than or equal to \b id. + */ +unsigned mdb_mid2l_search( MDB_ID2L ids, MDB_ID id ); + + + /** Insert an ID2 into a ID2L. + * @param[in,out] ids The ID2L to insert into. + * @param[in] id The ID2 to insert. + * @return 0 on success, -1 if the ID was already present in the ID2L. + */ +int mdb_mid2l_insert( MDB_ID2L ids, MDB_ID2 *id ); + + /** Append an ID2 into a ID2L. + * @param[in,out] ids The ID2L to append into. + * @param[in] id The ID2 to append. + * @return 0 on success, -2 if the ID2L is too big. + */ +int mdb_mid2l_append( MDB_ID2L ids, MDB_ID2 *id ); + +/** @} */ +/** @} */ +#ifdef __cplusplus +} +#endif +#endif /* _MDB_MIDL_H_ */ diff --git a/libraries/liblmdb/mtest.c b/libraries/liblmdb/mtest.c new file mode 100644 index 0000000..c1c9abb --- /dev/null +++ b/libraries/liblmdb/mtest.c @@ -0,0 +1,177 @@ +/* mtest.c - memory-mapped database tester/toy */ +/* + * Copyright 2011-2020 Howard Chu, Symas Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include "lmdb.h" + +#define E(expr) CHECK((rc = (expr)) == MDB_SUCCESS, #expr) +#define RES(err, expr) ((rc = expr) == (err) || (CHECK(!rc, #expr), 0)) +#define CHECK(test, msg) ((test) ? (void)0 : ((void)fprintf(stderr, \ + "%s:%d: %s: %s\n", __FILE__, __LINE__, msg, mdb_strerror(rc)), abort())) + +int main(int argc,char * argv[]) +{ + int i = 0, j = 0, rc; + MDB_env *env; + MDB_dbi dbi; + MDB_val key, data; + MDB_txn *txn; + MDB_stat mst; + MDB_cursor *cursor, *cur2; + MDB_cursor_op op; + int count; + int *values; + char sval[32] = ""; + + srand(time(NULL)); + + count = (rand()%384) + 64; + values = (int *)malloc(count*sizeof(int)); + + for(i = 0;i<count;i++) { + values[i] = rand()%1024; + } + + E(mdb_env_create(&env)); + E(mdb_env_set_maxreaders(env, 1)); + E(mdb_env_set_mapsize(env, 10485760)); + E(mdb_env_open(env, "./testdb", MDB_FIXEDMAP /*|MDB_NOSYNC*/, 0664)); + + E(mdb_txn_begin(env, NULL, 0, &txn)); + E(mdb_dbi_open(txn, NULL, 0, &dbi)); + + key.mv_size = sizeof(int); + key.mv_data = sval; + + printf("Adding %d values\n", count); + for (i=0;i<count;i++) { + sprintf(sval, "%03x %d foo bar", values[i], values[i]); + /* Set <data> in each iteration, since MDB_NOOVERWRITE may modify it */ + data.mv_size = sizeof(sval); + data.mv_data = sval; + if (RES(MDB_KEYEXIST, mdb_put(txn, dbi, &key, &data, MDB_NOOVERWRITE))) { + j++; + data.mv_size = sizeof(sval); + data.mv_data = sval; + } + } + if (j) printf("%d duplicates skipped\n", j); + E(mdb_txn_commit(txn)); + E(mdb_env_stat(env, &mst)); + + E(mdb_txn_begin(env, NULL, MDB_RDONLY, &txn)); + E(mdb_cursor_open(txn, dbi, &cursor)); + while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) { + printf("key: %p %.*s, data: %p %.*s\n", + key.mv_data, (int) key.mv_size, (char *) key.mv_data, + data.mv_data, (int) data.mv_size, (char *) data.mv_data); + } + CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get"); + mdb_cursor_close(cursor); + mdb_txn_abort(txn); + + j=0; + key.mv_data = sval; + for (i= count - 1; i > -1; i-= (rand()%5)) { + j++; + txn=NULL; + E(mdb_txn_begin(env, NULL, 0, &txn)); + sprintf(sval, "%03x ", values[i]); + if (RES(MDB_NOTFOUND, mdb_del(txn, dbi, &key, NULL))) { + j--; + mdb_txn_abort(txn); + } else { + E(mdb_txn_commit(txn)); + } + } + free(values); + printf("Deleted %d values\n", j); + + E(mdb_env_stat(env, &mst)); + E(mdb_txn_begin(env, NULL, MDB_RDONLY, &txn)); + E(mdb_cursor_open(txn, dbi, &cursor)); + printf("Cursor next\n"); + while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) { + printf("key: %.*s, data: %.*s\n", + (int) key.mv_size, (char *) key.mv_data, + (int) data.mv_size, (char *) data.mv_data); + } + CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get"); + printf("Cursor last\n"); + E(mdb_cursor_get(cursor, &key, &data, MDB_LAST)); + printf("key: %.*s, data: %.*s\n", + (int) key.mv_size, (char *) key.mv_data, + (int) data.mv_size, (char *) data.mv_data); + printf("Cursor prev\n"); + while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_PREV)) == 0) { + printf("key: %.*s, data: %.*s\n", + (int) key.mv_size, (char *) key.mv_data, + (int) data.mv_size, (char *) data.mv_data); + } + CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get"); + printf("Cursor last/prev\n"); + E(mdb_cursor_get(cursor, &key, &data, MDB_LAST)); + printf("key: %.*s, data: %.*s\n", + (int) key.mv_size, (char *) key.mv_data, + (int) data.mv_size, (char *) data.mv_data); + E(mdb_cursor_get(cursor, &key, &data, MDB_PREV)); + printf("key: %.*s, data: %.*s\n", + (int) key.mv_size, (char *) key.mv_data, + (int) data.mv_size, (char *) data.mv_data); + + mdb_cursor_close(cursor); + mdb_txn_abort(txn); + + printf("Deleting with cursor\n"); + E(mdb_txn_begin(env, NULL, 0, &txn)); + E(mdb_cursor_open(txn, dbi, &cur2)); + for (i=0; i<50; i++) { + if (RES(MDB_NOTFOUND, mdb_cursor_get(cur2, &key, &data, MDB_NEXT))) + break; + printf("key: %p %.*s, data: %p %.*s\n", + key.mv_data, (int) key.mv_size, (char *) key.mv_data, + data.mv_data, (int) data.mv_size, (char *) data.mv_data); + E(mdb_del(txn, dbi, &key, NULL)); + } + + printf("Restarting cursor in txn\n"); + for (op=MDB_FIRST, i=0; i<=32; op=MDB_NEXT, i++) { + if (RES(MDB_NOTFOUND, mdb_cursor_get(cur2, &key, &data, op))) + break; + printf("key: %p %.*s, data: %p %.*s\n", + key.mv_data, (int) key.mv_size, (char *) key.mv_data, + data.mv_data, (int) data.mv_size, (char *) data.mv_data); + } + mdb_cursor_close(cur2); + E(mdb_txn_commit(txn)); + + printf("Restarting cursor outside txn\n"); + E(mdb_txn_begin(env, NULL, 0, &txn)); + E(mdb_cursor_open(txn, dbi, &cursor)); + for (op=MDB_FIRST, i=0; i<=32; op=MDB_NEXT, i++) { + if (RES(MDB_NOTFOUND, mdb_cursor_get(cursor, &key, &data, op))) + break; + printf("key: %p %.*s, data: %p %.*s\n", + key.mv_data, (int) key.mv_size, (char *) key.mv_data, + data.mv_data, (int) data.mv_size, (char *) data.mv_data); + } + mdb_cursor_close(cursor); + mdb_txn_abort(txn); + + mdb_dbi_close(env, dbi); + mdb_env_close(env); + + return 0; +} diff --git a/libraries/liblmdb/mtest2.c b/libraries/liblmdb/mtest2.c new file mode 100644 index 0000000..db32525 --- /dev/null +++ b/libraries/liblmdb/mtest2.c @@ -0,0 +1,124 @@ +/* mtest2.c - memory-mapped database tester/toy */ +/* + * Copyright 2011-2020 Howard Chu, Symas Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +/* Just like mtest.c, but using a subDB instead of the main DB */ + +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include "lmdb.h" + +#define E(expr) CHECK((rc = (expr)) == MDB_SUCCESS, #expr) +#define RES(err, expr) ((rc = expr) == (err) || (CHECK(!rc, #expr), 0)) +#define CHECK(test, msg) ((test) ? (void)0 : ((void)fprintf(stderr, \ + "%s:%d: %s: %s\n", __FILE__, __LINE__, msg, mdb_strerror(rc)), abort())) + +int main(int argc,char * argv[]) +{ + int i = 0, j = 0, rc; + MDB_env *env; + MDB_dbi dbi; + MDB_val key, data; + MDB_txn *txn; + MDB_stat mst; + MDB_cursor *cursor; + int count; + int *values; + char sval[32] = ""; + + srand(time(NULL)); + + count = (rand()%384) + 64; + values = (int *)malloc(count*sizeof(int)); + + for(i = 0;i<count;i++) { + values[i] = rand()%1024; + } + + E(mdb_env_create(&env)); + E(mdb_env_set_maxreaders(env, 1)); + E(mdb_env_set_mapsize(env, 10485760)); + E(mdb_env_set_maxdbs(env, 4)); + E(mdb_env_open(env, "./testdb", MDB_FIXEDMAP|MDB_NOSYNC, 0664)); + + E(mdb_txn_begin(env, NULL, 0, &txn)); + E(mdb_dbi_open(txn, "id1", MDB_CREATE, &dbi)); + + key.mv_size = sizeof(int); + key.mv_data = sval; + + printf("Adding %d values\n", count); + for (i=0;i<count;i++) { + sprintf(sval, "%03x %d foo bar", values[i], values[i]); + data.mv_size = sizeof(sval); + data.mv_data = sval; + if (RES(MDB_KEYEXIST, mdb_put(txn, dbi, &key, &data, MDB_NOOVERWRITE))) + j++; + } + if (j) printf("%d duplicates skipped\n", j); + E(mdb_txn_commit(txn)); + E(mdb_env_stat(env, &mst)); + + E(mdb_txn_begin(env, NULL, MDB_RDONLY, &txn)); + E(mdb_cursor_open(txn, dbi, &cursor)); + while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) { + printf("key: %p %.*s, data: %p %.*s\n", + key.mv_data, (int) key.mv_size, (char *) key.mv_data, + data.mv_data, (int) data.mv_size, (char *) data.mv_data); + } + CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get"); + mdb_cursor_close(cursor); + mdb_txn_abort(txn); + + j=0; + key.mv_data = sval; + for (i= count - 1; i > -1; i-= (rand()%5)) { + j++; + txn=NULL; + E(mdb_txn_begin(env, NULL, 0, &txn)); + sprintf(sval, "%03x ", values[i]); + if (RES(MDB_NOTFOUND, mdb_del(txn, dbi, &key, NULL))) { + j--; + mdb_txn_abort(txn); + } else { + E(mdb_txn_commit(txn)); + } + } + free(values); + printf("Deleted %d values\n", j); + + E(mdb_env_stat(env, &mst)); + E(mdb_txn_begin(env, NULL, MDB_RDONLY, &txn)); + E(mdb_cursor_open(txn, dbi, &cursor)); + printf("Cursor next\n"); + while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) { + printf("key: %.*s, data: %.*s\n", + (int) key.mv_size, (char *) key.mv_data, + (int) data.mv_size, (char *) data.mv_data); + } + CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get"); + printf("Cursor prev\n"); + while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_PREV)) == 0) { + printf("key: %.*s, data: %.*s\n", + (int) key.mv_size, (char *) key.mv_data, + (int) data.mv_size, (char *) data.mv_data); + } + CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get"); + mdb_cursor_close(cursor); + mdb_txn_abort(txn); + + mdb_dbi_close(env, dbi); + mdb_env_close(env); + return 0; +} diff --git a/libraries/liblmdb/mtest3.c b/libraries/liblmdb/mtest3.c new file mode 100644 index 0000000..bc471ee --- /dev/null +++ b/libraries/liblmdb/mtest3.c @@ -0,0 +1,133 @@ +/* mtest3.c - memory-mapped database tester/toy */ +/* + * Copyright 2011-2020 Howard Chu, Symas Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +/* Tests for sorted duplicate DBs */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include "lmdb.h" + +#define E(expr) CHECK((rc = (expr)) == MDB_SUCCESS, #expr) +#define RES(err, expr) ((rc = expr) == (err) || (CHECK(!rc, #expr), 0)) +#define CHECK(test, msg) ((test) ? (void)0 : ((void)fprintf(stderr, \ + "%s:%d: %s: %s\n", __FILE__, __LINE__, msg, mdb_strerror(rc)), abort())) + +int main(int argc,char * argv[]) +{ + int i = 0, j = 0, rc; + MDB_env *env; + MDB_dbi dbi; + MDB_val key, data; + MDB_txn *txn; + MDB_stat mst; + MDB_cursor *cursor; + int count; + int *values; + char sval[32]; + char kval[sizeof(int)]; + + srand(time(NULL)); + + memset(sval, 0, sizeof(sval)); + + count = (rand()%384) + 64; + values = (int *)malloc(count*sizeof(int)); + + for(i = 0;i<count;i++) { + values[i] = rand()%1024; + } + + E(mdb_env_create(&env)); + E(mdb_env_set_mapsize(env, 10485760)); + E(mdb_env_set_maxdbs(env, 4)); + E(mdb_env_open(env, "./testdb", MDB_FIXEDMAP|MDB_NOSYNC, 0664)); + + E(mdb_txn_begin(env, NULL, 0, &txn)); + E(mdb_dbi_open(txn, "id2", MDB_CREATE|MDB_DUPSORT, &dbi)); + + key.mv_size = sizeof(int); + key.mv_data = kval; + data.mv_size = sizeof(sval); + data.mv_data = sval; + + printf("Adding %d values\n", count); + for (i=0;i<count;i++) { + if (!(i & 0x0f)) + sprintf(kval, "%03x", values[i]); + sprintf(sval, "%03x %d foo bar", values[i], values[i]); + if (RES(MDB_KEYEXIST, mdb_put(txn, dbi, &key, &data, MDB_NODUPDATA))) + j++; + } + if (j) printf("%d duplicates skipped\n", j); + E(mdb_txn_commit(txn)); + E(mdb_env_stat(env, &mst)); + + E(mdb_txn_begin(env, NULL, MDB_RDONLY, &txn)); + E(mdb_cursor_open(txn, dbi, &cursor)); + while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) { + printf("key: %p %.*s, data: %p %.*s\n", + key.mv_data, (int) key.mv_size, (char *) key.mv_data, + data.mv_data, (int) data.mv_size, (char *) data.mv_data); + } + CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get"); + mdb_cursor_close(cursor); + mdb_txn_abort(txn); + + j=0; + + for (i= count - 1; i > -1; i-= (rand()%5)) { + j++; + txn=NULL; + E(mdb_txn_begin(env, NULL, 0, &txn)); + sprintf(kval, "%03x", values[i & ~0x0f]); + sprintf(sval, "%03x %d foo bar", values[i], values[i]); + key.mv_size = sizeof(int); + key.mv_data = kval; + data.mv_size = sizeof(sval); + data.mv_data = sval; + if (RES(MDB_NOTFOUND, mdb_del(txn, dbi, &key, &data))) { + j--; + mdb_txn_abort(txn); + } else { + E(mdb_txn_commit(txn)); + } + } + free(values); + printf("Deleted %d values\n", j); + + E(mdb_env_stat(env, &mst)); + E(mdb_txn_begin(env, NULL, MDB_RDONLY, &txn)); + E(mdb_cursor_open(txn, dbi, &cursor)); + printf("Cursor next\n"); + while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) { + printf("key: %.*s, data: %.*s\n", + (int) key.mv_size, (char *) key.mv_data, + (int) data.mv_size, (char *) data.mv_data); + } + CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get"); + printf("Cursor prev\n"); + while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_PREV)) == 0) { + printf("key: %.*s, data: %.*s\n", + (int) key.mv_size, (char *) key.mv_data, + (int) data.mv_size, (char *) data.mv_data); + } + CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get"); + mdb_cursor_close(cursor); + mdb_txn_abort(txn); + + mdb_dbi_close(env, dbi); + mdb_env_close(env); + return 0; +} diff --git a/libraries/liblmdb/mtest4.c b/libraries/liblmdb/mtest4.c new file mode 100644 index 0000000..b753175 --- /dev/null +++ b/libraries/liblmdb/mtest4.c @@ -0,0 +1,168 @@ +/* mtest4.c - memory-mapped database tester/toy */ +/* + * Copyright 2011-2020 Howard Chu, Symas Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +/* Tests for sorted duplicate DBs with fixed-size keys */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include "lmdb.h" + +#define E(expr) CHECK((rc = (expr)) == MDB_SUCCESS, #expr) +#define RES(err, expr) ((rc = expr) == (err) || (CHECK(!rc, #expr), 0)) +#define CHECK(test, msg) ((test) ? (void)0 : ((void)fprintf(stderr, \ + "%s:%d: %s: %s\n", __FILE__, __LINE__, msg, mdb_strerror(rc)), abort())) + +int main(int argc,char * argv[]) +{ + int i = 0, j = 0, rc; + MDB_env *env; + MDB_dbi dbi; + MDB_val key, data; + MDB_txn *txn; + MDB_stat mst; + MDB_cursor *cursor; + int count; + int *values; + char sval[8]; + char kval[sizeof(int)]; + + memset(sval, 0, sizeof(sval)); + + count = 510; + values = (int *)malloc(count*sizeof(int)); + + for(i = 0;i<count;i++) { + values[i] = i*5; + } + + E(mdb_env_create(&env)); + E(mdb_env_set_mapsize(env, 10485760)); + E(mdb_env_set_maxdbs(env, 4)); + E(mdb_env_open(env, "./testdb", MDB_FIXEDMAP|MDB_NOSYNC, 0664)); + + E(mdb_txn_begin(env, NULL, 0, &txn)); + E(mdb_dbi_open(txn, "id4", MDB_CREATE|MDB_DUPSORT|MDB_DUPFIXED, &dbi)); + + key.mv_size = sizeof(int); + key.mv_data = kval; + data.mv_size = sizeof(sval); + data.mv_data = sval; + + printf("Adding %d values\n", count); + strcpy(kval, "001"); + for (i=0;i<count;i++) { + sprintf(sval, "%07x", values[i]); + if (RES(MDB_KEYEXIST, mdb_put(txn, dbi, &key, &data, MDB_NODUPDATA))) + j++; + } + if (j) printf("%d duplicates skipped\n", j); + E(mdb_txn_commit(txn)); + E(mdb_env_stat(env, &mst)); + + /* there should be one full page of dups now. + */ + E(mdb_txn_begin(env, NULL, MDB_RDONLY, &txn)); + E(mdb_cursor_open(txn, dbi, &cursor)); + while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) { + printf("key: %p %.*s, data: %p %.*s\n", + key.mv_data, (int) key.mv_size, (char *) key.mv_data, + data.mv_data, (int) data.mv_size, (char *) data.mv_data); + } + CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get"); + mdb_cursor_close(cursor); + mdb_txn_abort(txn); + + /* test all 3 branches of split code: + * 1: new key in lower half + * 2: new key at split point + * 3: new key in upper half + */ + + key.mv_size = sizeof(int); + key.mv_data = kval; + data.mv_size = sizeof(sval); + data.mv_data = sval; + + sprintf(sval, "%07x", values[3]+1); + E(mdb_txn_begin(env, NULL, 0, &txn)); + (void)RES(MDB_KEYEXIST, mdb_put(txn, dbi, &key, &data, MDB_NODUPDATA)); + mdb_txn_abort(txn); + + sprintf(sval, "%07x", values[255]+1); + E(mdb_txn_begin(env, NULL, 0, &txn)); + (void)RES(MDB_KEYEXIST, mdb_put(txn, dbi, &key, &data, MDB_NODUPDATA)); + mdb_txn_abort(txn); + + sprintf(sval, "%07x", values[500]+1); + E(mdb_txn_begin(env, NULL, 0, &txn)); + (void)RES(MDB_KEYEXIST, mdb_put(txn, dbi, &key, &data, MDB_NODUPDATA)); + E(mdb_txn_commit(txn)); + + /* Try MDB_NEXT_MULTIPLE */ + E(mdb_txn_begin(env, NULL, 0, &txn)); + E(mdb_cursor_open(txn, dbi, &cursor)); + while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT_MULTIPLE)) == 0) { + printf("key: %.*s, data: %.*s\n", + (int) key.mv_size, (char *) key.mv_data, + (int) data.mv_size, (char *) data.mv_data); + } + CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get"); + mdb_cursor_close(cursor); + mdb_txn_abort(txn); + j=0; + + for (i= count - 1; i > -1; i-= (rand()%3)) { + j++; + txn=NULL; + E(mdb_txn_begin(env, NULL, 0, &txn)); + sprintf(sval, "%07x", values[i]); + key.mv_size = sizeof(int); + key.mv_data = kval; + data.mv_size = sizeof(sval); + data.mv_data = sval; + if (RES(MDB_NOTFOUND, mdb_del(txn, dbi, &key, &data))) { + j--; + mdb_txn_abort(txn); + } else { + E(mdb_txn_commit(txn)); + } + } + free(values); + printf("Deleted %d values\n", j); + + E(mdb_env_stat(env, &mst)); + E(mdb_txn_begin(env, NULL, MDB_RDONLY, &txn)); + E(mdb_cursor_open(txn, dbi, &cursor)); + printf("Cursor next\n"); + while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) { + printf("key: %.*s, data: %.*s\n", + (int) key.mv_size, (char *) key.mv_data, + (int) data.mv_size, (char *) data.mv_data); + } + CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get"); + printf("Cursor prev\n"); + while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_PREV)) == 0) { + printf("key: %.*s, data: %.*s\n", + (int) key.mv_size, (char *) key.mv_data, + (int) data.mv_size, (char *) data.mv_data); + } + CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get"); + mdb_cursor_close(cursor); + mdb_txn_abort(txn); + + mdb_dbi_close(env, dbi); + mdb_env_close(env); + return 0; +} diff --git a/libraries/liblmdb/mtest5.c b/libraries/liblmdb/mtest5.c new file mode 100644 index 0000000..d6d1cf9 --- /dev/null +++ b/libraries/liblmdb/mtest5.c @@ -0,0 +1,135 @@ +/* mtest5.c - memory-mapped database tester/toy */ +/* + * Copyright 2011-2020 Howard Chu, Symas Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +/* Tests for sorted duplicate DBs using cursor_put */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include "lmdb.h" + +#define E(expr) CHECK((rc = (expr)) == MDB_SUCCESS, #expr) +#define RES(err, expr) ((rc = expr) == (err) || (CHECK(!rc, #expr), 0)) +#define CHECK(test, msg) ((test) ? (void)0 : ((void)fprintf(stderr, \ + "%s:%d: %s: %s\n", __FILE__, __LINE__, msg, mdb_strerror(rc)), abort())) + +int main(int argc,char * argv[]) +{ + int i = 0, j = 0, rc; + MDB_env *env; + MDB_dbi dbi; + MDB_val key, data; + MDB_txn *txn; + MDB_stat mst; + MDB_cursor *cursor; + int count; + int *values; + char sval[32]; + char kval[sizeof(int)]; + + srand(time(NULL)); + + memset(sval, 0, sizeof(sval)); + + count = (rand()%384) + 64; + values = (int *)malloc(count*sizeof(int)); + + for(i = 0;i<count;i++) { + values[i] = rand()%1024; + } + + E(mdb_env_create(&env)); + E(mdb_env_set_mapsize(env, 10485760)); + E(mdb_env_set_maxdbs(env, 4)); + E(mdb_env_open(env, "./testdb", MDB_FIXEDMAP|MDB_NOSYNC, 0664)); + + E(mdb_txn_begin(env, NULL, 0, &txn)); + E(mdb_dbi_open(txn, "id2", MDB_CREATE|MDB_DUPSORT, &dbi)); + E(mdb_cursor_open(txn, dbi, &cursor)); + + key.mv_size = sizeof(int); + key.mv_data = kval; + data.mv_size = sizeof(sval); + data.mv_data = sval; + + printf("Adding %d values\n", count); + for (i=0;i<count;i++) { + if (!(i & 0x0f)) + sprintf(kval, "%03x", values[i]); + sprintf(sval, "%03x %d foo bar", values[i], values[i]); + if (RES(MDB_KEYEXIST, mdb_cursor_put(cursor, &key, &data, MDB_NODUPDATA))) + j++; + } + if (j) printf("%d duplicates skipped\n", j); + mdb_cursor_close(cursor); + E(mdb_txn_commit(txn)); + E(mdb_env_stat(env, &mst)); + + E(mdb_txn_begin(env, NULL, MDB_RDONLY, &txn)); + E(mdb_cursor_open(txn, dbi, &cursor)); + while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) { + printf("key: %p %.*s, data: %p %.*s\n", + key.mv_data, (int) key.mv_size, (char *) key.mv_data, + data.mv_data, (int) data.mv_size, (char *) data.mv_data); + } + CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get"); + mdb_cursor_close(cursor); + mdb_txn_abort(txn); + + j=0; + + for (i= count - 1; i > -1; i-= (rand()%5)) { + j++; + txn=NULL; + E(mdb_txn_begin(env, NULL, 0, &txn)); + sprintf(kval, "%03x", values[i & ~0x0f]); + sprintf(sval, "%03x %d foo bar", values[i], values[i]); + key.mv_size = sizeof(int); + key.mv_data = kval; + data.mv_size = sizeof(sval); + data.mv_data = sval; + if (RES(MDB_NOTFOUND, mdb_del(txn, dbi, &key, &data))) { + j--; + mdb_txn_abort(txn); + } else { + E(mdb_txn_commit(txn)); + } + } + free(values); + printf("Deleted %d values\n", j); + + E(mdb_env_stat(env, &mst)); + E(mdb_txn_begin(env, NULL, MDB_RDONLY, &txn)); + E(mdb_cursor_open(txn, dbi, &cursor)); + printf("Cursor next\n"); + while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) { + printf("key: %.*s, data: %.*s\n", + (int) key.mv_size, (char *) key.mv_data, + (int) data.mv_size, (char *) data.mv_data); + } + CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get"); + printf("Cursor prev\n"); + while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_PREV)) == 0) { + printf("key: %.*s, data: %.*s\n", + (int) key.mv_size, (char *) key.mv_data, + (int) data.mv_size, (char *) data.mv_data); + } + CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get"); + mdb_cursor_close(cursor); + mdb_txn_abort(txn); + + mdb_dbi_close(env, dbi); + mdb_env_close(env); + return 0; +} diff --git a/libraries/liblmdb/mtest6.c b/libraries/liblmdb/mtest6.c new file mode 100644 index 0000000..e4d4e6b --- /dev/null +++ b/libraries/liblmdb/mtest6.c @@ -0,0 +1,141 @@ +/* mtest6.c - memory-mapped database tester/toy */ +/* + * Copyright 2011-2020 Howard Chu, Symas Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +/* Tests for DB splits and merges */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include "lmdb.h" + +#define E(expr) CHECK((rc = (expr)) == MDB_SUCCESS, #expr) +#define RES(err, expr) ((rc = expr) == (err) || (CHECK(!rc, #expr), 0)) +#define CHECK(test, msg) ((test) ? (void)0 : ((void)fprintf(stderr, \ + "%s:%d: %s: %s\n", __FILE__, __LINE__, msg, mdb_strerror(rc)), abort())) + +char dkbuf[1024]; + +int main(int argc,char * argv[]) +{ + int i = 0, j = 0, rc; + MDB_env *env; + MDB_dbi dbi; + MDB_val key, data, sdata; + MDB_txn *txn; + MDB_stat mst; + MDB_cursor *cursor; + int count; + int *values; + long kval; + char *sval; + + srand(time(NULL)); + + E(mdb_env_create(&env)); + E(mdb_env_set_mapsize(env, 10485760)); + E(mdb_env_set_maxdbs(env, 4)); + E(mdb_env_open(env, "./testdb", MDB_FIXEDMAP|MDB_NOSYNC, 0664)); + + E(mdb_txn_begin(env, NULL, 0, &txn)); + E(mdb_dbi_open(txn, "id6", MDB_CREATE|MDB_INTEGERKEY, &dbi)); + E(mdb_cursor_open(txn, dbi, &cursor)); + E(mdb_stat(txn, dbi, &mst)); + + sval = calloc(1, mst.ms_psize / 4); + key.mv_size = sizeof(long); + key.mv_data = &kval; + sdata.mv_size = mst.ms_psize / 4 - 30; + sdata.mv_data = sval; + + printf("Adding 12 values, should yield 3 splits\n"); + for (i=0;i<12;i++) { + kval = i*5; + sprintf(sval, "%08x", kval); + data = sdata; + (void)RES(MDB_KEYEXIST, mdb_cursor_put(cursor, &key, &data, MDB_NOOVERWRITE)); + } + printf("Adding 12 more values, should yield 3 splits\n"); + for (i=0;i<12;i++) { + kval = i*5+4; + sprintf(sval, "%08x", kval); + data = sdata; + (void)RES(MDB_KEYEXIST, mdb_cursor_put(cursor, &key, &data, MDB_NOOVERWRITE)); + } + printf("Adding 12 more values, should yield 3 splits\n"); + for (i=0;i<12;i++) { + kval = i*5+1; + sprintf(sval, "%08x", kval); + data = sdata; + (void)RES(MDB_KEYEXIST, mdb_cursor_put(cursor, &key, &data, MDB_NOOVERWRITE)); + } + E(mdb_cursor_get(cursor, &key, &data, MDB_FIRST)); + + do { + printf("key: %p %s, data: %p %.*s\n", + key.mv_data, mdb_dkey(&key, dkbuf), + data.mv_data, (int) data.mv_size, (char *) data.mv_data); + } while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0); + CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get"); + mdb_cursor_close(cursor); + mdb_txn_commit(txn); + +#if 0 + j=0; + + for (i= count - 1; i > -1; i-= (rand()%5)) { + j++; + txn=NULL; + E(mdb_txn_begin(env, NULL, 0, &txn)); + sprintf(kval, "%03x", values[i & ~0x0f]); + sprintf(sval, "%03x %d foo bar", values[i], values[i]); + key.mv_size = sizeof(int); + key.mv_data = kval; + data.mv_size = sizeof(sval); + data.mv_data = sval; + if (RES(MDB_NOTFOUND, mdb_del(txn, dbi, &key, &data))) { + j--; + mdb_txn_abort(txn); + } else { + E(mdb_txn_commit(txn)); + } + } + free(values); + printf("Deleted %d values\n", j); + + E(mdb_env_stat(env, &mst)); + E(mdb_txn_begin(env, NULL, MDB_RDONLY, &txn)); + E(mdb_cursor_open(txn, dbi, &cursor)); + printf("Cursor next\n"); + while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) { + printf("key: %.*s, data: %.*s\n", + (int) key.mv_size, (char *) key.mv_data, + (int) data.mv_size, (char *) data.mv_data); + } + CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get"); + printf("Cursor prev\n"); + while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_PREV)) == 0) { + printf("key: %.*s, data: %.*s\n", + (int) key.mv_size, (char *) key.mv_data, + (int) data.mv_size, (char *) data.mv_data); + } + CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get"); + mdb_cursor_close(cursor); + mdb_txn_abort(txn); + + mdb_dbi_close(env, dbi); +#endif + mdb_env_close(env); + + return 0; +} diff --git a/libraries/liblmdb/sample-bdb.txt b/libraries/liblmdb/sample-bdb.txt new file mode 100644 index 0000000..c72078c --- /dev/null +++ b/libraries/liblmdb/sample-bdb.txt @@ -0,0 +1,73 @@ +/* sample-bdb.txt - BerkeleyDB toy/sample + * + * Do a line-by-line comparison of this and sample-mdb.txt + */ +/* + * Copyright 2012-2020 Howard Chu, Symas Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +#include <stdio.h> +#include <string.h> +#include <db.h> + +int main(int argc,char * argv[]) +{ + int rc; + DB_ENV *env; + DB *dbi; + DBT key, data; + DB_TXN *txn; + DBC *cursor; + char sval[32], kval[32]; + + /* Note: Most error checking omitted for simplicity */ + +#define FLAGS (DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_INIT_MPOOL|DB_CREATE|DB_THREAD) + rc = db_env_create(&env, 0); + rc = env->open(env, "./testdb", FLAGS, 0664); + rc = db_create(&dbi, env, 0); + rc = env->txn_begin(env, NULL, &txn, 0); + rc = dbi->open(dbi, txn, "test.bdb", NULL, DB_BTREE, DB_CREATE, 0664); + + memset(&key, 0, sizeof(DBT)); + memset(&data, 0, sizeof(DBT)); + key.size = sizeof(int); + key.data = sval; + data.size = sizeof(sval); + data.data = sval; + + sprintf(sval, "%03x %d foo bar", 32, 3141592); + rc = dbi->put(dbi, txn, &key, &data, 0); + rc = txn->commit(txn, 0); + if (rc) { + fprintf(stderr, "txn->commit: (%d) %s\n", rc, db_strerror(rc)); + goto leave; + } + rc = env->txn_begin(env, NULL, &txn, 0); + rc = dbi->cursor(dbi, txn, &cursor, 0); + key.flags = DB_DBT_USERMEM; + key.data = kval; + key.ulen = sizeof(kval); + data.flags = DB_DBT_USERMEM; + data.data = sval; + data.ulen = sizeof(sval); + while ((rc = cursor->c_get(cursor, &key, &data, DB_NEXT)) == 0) { + printf("key: %p %.*s, data: %p %.*s\n", + key.data, (int) key.size, (char *) key.data, + data.data, (int) data.size, (char *) data.data); + } + rc = cursor->c_close(cursor); + rc = txn->abort(txn); +leave: + rc = dbi->close(dbi, 0); + rc = env->close(env, 0); + return rc; +} diff --git a/libraries/liblmdb/sample-mdb.txt b/libraries/liblmdb/sample-mdb.txt new file mode 100644 index 0000000..e54a847 --- /dev/null +++ b/libraries/liblmdb/sample-mdb.txt @@ -0,0 +1,62 @@ +/* sample-mdb.txt - MDB toy/sample + * + * Do a line-by-line comparison of this and sample-bdb.txt + */ +/* + * Copyright 2012-2020 Howard Chu, Symas Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +#include <stdio.h> +#include "lmdb.h" + +int main(int argc,char * argv[]) +{ + int rc; + MDB_env *env; + MDB_dbi dbi; + MDB_val key, data; + MDB_txn *txn; + MDB_cursor *cursor; + char sval[32]; + + /* Note: Most error checking omitted for simplicity */ + + rc = mdb_env_create(&env); + rc = mdb_env_open(env, "./testdb", 0, 0664); + rc = mdb_txn_begin(env, NULL, 0, &txn); + rc = mdb_dbi_open(txn, NULL, 0, &dbi); + + key.mv_size = sizeof(int); + key.mv_data = sval; + data.mv_size = sizeof(sval); + data.mv_data = sval; + + sprintf(sval, "%03x %d foo bar", 32, 3141592); + rc = mdb_put(txn, dbi, &key, &data, 0); + rc = mdb_txn_commit(txn); + if (rc) { + fprintf(stderr, "mdb_txn_commit: (%d) %s\n", rc, mdb_strerror(rc)); + goto leave; + } + rc = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn); + rc = mdb_cursor_open(txn, dbi, &cursor); + while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) { + printf("key: %p %.*s, data: %p %.*s\n", + key.mv_data, (int) key.mv_size, (char *) key.mv_data, + data.mv_data, (int) data.mv_size, (char *) data.mv_data); + } + mdb_cursor_close(cursor); + mdb_txn_abort(txn); +leave: + mdb_dbi_close(env, dbi); + mdb_env_close(env); + return 0; +} diff --git a/libraries/liblmdb/tooltag b/libraries/liblmdb/tooltag new file mode 100644 index 0000000..229bf16 --- /dev/null +++ b/libraries/liblmdb/tooltag @@ -0,0 +1,22 @@ +<tagfile> + <compound kind="page"> + <name>mdb_copy_1</name> + <title>mdb_copy - environment copy tool</title> + <filename>mdb_copy.1</filename> + </compound> + <compound kind="page"> + <name>mdb_dump_1</name> + <title>mdb_dump - environment export tool</title> + <filename>mdb_dump.1</filename> + </compound> + <compound kind="page"> + <name>mdb_load_1</name> + <title>mdb_load - environment import tool</title> + <filename>mdb_load.1</filename> + </compound> + <compound kind="page"> + <name>mdb_stat_1</name> + <title>mdb_stat - environment status tool</title> + <filename>mdb_stat.1</filename> + </compound> +</tagfile> diff --git a/libraries/liblunicode/CompositionExclusions.txt b/libraries/liblunicode/CompositionExclusions.txt new file mode 100644 index 0000000..07a60b8 --- /dev/null +++ b/libraries/liblunicode/CompositionExclusions.txt @@ -0,0 +1,176 @@ +# CompositionExclusions-3.2.0.txt +# Date: 2002-03-19,23:30:28 GMT [MD] +# +# This file lists the characters from the UAX #15 Composition Exclusion Table. +# +# The format of the comments in this file has been updated since the last version, +# CompositionExclusions-3.txt. The only substantive change to this file between that +# version and this one is the addition of U+2ADC FORKING. +# +# For more information, see +# http://www.unicode.org/unicode/reports/tr15/#Primary Exclusion List Table +# ================================================ + +# (1) Script Specifics +# This list of characters cannot be derived from the UnicodeData file. +# ================================================ + +0958 # DEVANAGARI LETTER QA +0959 # DEVANAGARI LETTER KHHA +095A # DEVANAGARI LETTER GHHA +095B # DEVANAGARI LETTER ZA +095C # DEVANAGARI LETTER DDDHA +095D # DEVANAGARI LETTER RHA +095E # DEVANAGARI LETTER FA +095F # DEVANAGARI LETTER YYA +09DC # BENGALI LETTER RRA +09DD # BENGALI LETTER RHA +09DF # BENGALI LETTER YYA +0A33 # GURMUKHI LETTER LLA +0A36 # GURMUKHI LETTER SHA +0A59 # GURMUKHI LETTER KHHA +0A5A # GURMUKHI LETTER GHHA +0A5B # GURMUKHI LETTER ZA +0A5E # GURMUKHI LETTER FA +0B5C # ORIYA LETTER RRA +0B5D # ORIYA LETTER RHA +0F43 # TIBETAN LETTER GHA +0F4D # TIBETAN LETTER DDHA +0F52 # TIBETAN LETTER DHA +0F57 # TIBETAN LETTER BHA +0F5C # TIBETAN LETTER DZHA +0F69 # TIBETAN LETTER KSSA +0F76 # TIBETAN VOWEL SIGN VOCALIC R +0F78 # TIBETAN VOWEL SIGN VOCALIC L +0F93 # TIBETAN SUBJOINED LETTER GHA +0F9D # TIBETAN SUBJOINED LETTER DDHA +0FA2 # TIBETAN SUBJOINED LETTER DHA +0FA7 # TIBETAN SUBJOINED LETTER BHA +0FAC # TIBETAN SUBJOINED LETTER DZHA +0FB9 # TIBETAN SUBJOINED LETTER KSSA +FB1D # HEBREW LETTER YOD WITH HIRIQ +FB1F # HEBREW LIGATURE YIDDISH YOD YOD PATAH +FB2A # HEBREW LETTER SHIN WITH SHIN DOT +FB2B # HEBREW LETTER SHIN WITH SIN DOT +FB2C # HEBREW LETTER SHIN WITH DAGESH AND SHIN DOT +FB2D # HEBREW LETTER SHIN WITH DAGESH AND SIN DOT +FB2E # HEBREW LETTER ALEF WITH PATAH +FB2F # HEBREW LETTER ALEF WITH QAMATS +FB30 # HEBREW LETTER ALEF WITH MAPIQ +FB31 # HEBREW LETTER BET WITH DAGESH +FB32 # HEBREW LETTER GIMEL WITH DAGESH +FB33 # HEBREW LETTER DALET WITH DAGESH +FB34 # HEBREW LETTER HE WITH MAPIQ +FB35 # HEBREW LETTER VAV WITH DAGESH +FB36 # HEBREW LETTER ZAYIN WITH DAGESH +FB38 # HEBREW LETTER TET WITH DAGESH +FB39 # HEBREW LETTER YOD WITH DAGESH +FB3A # HEBREW LETTER FINAL KAF WITH DAGESH +FB3B # HEBREW LETTER KAF WITH DAGESH +FB3C # HEBREW LETTER LAMED WITH DAGESH +FB3E # HEBREW LETTER MEM WITH DAGESH +FB40 # HEBREW LETTER NUN WITH DAGESH +FB41 # HEBREW LETTER SAMEKH WITH DAGESH +FB43 # HEBREW LETTER FINAL PE WITH DAGESH +FB44 # HEBREW LETTER PE WITH DAGESH +FB46 # HEBREW LETTER TSADI WITH DAGESH +FB47 # HEBREW LETTER QOF WITH DAGESH +FB48 # HEBREW LETTER RESH WITH DAGESH +FB49 # HEBREW LETTER SHIN WITH DAGESH +FB4A # HEBREW LETTER TAV WITH DAGESH +FB4B # HEBREW LETTER VAV WITH HOLAM +FB4C # HEBREW LETTER BET WITH RAFE +FB4D # HEBREW LETTER KAF WITH RAFE +FB4E # HEBREW LETTER PE WITH RAFE + +# Total code points: 67 + +# ================================================ +# (2) Post Composition Version precomposed characters +# These characters cannot be derived solely from the UnicodeData.txt file +# in this version of Unicode. +# ================================================ + +2ADC # FORKING +1D15E # MUSICAL SYMBOL HALF NOTE +1D15F # MUSICAL SYMBOL QUARTER NOTE +1D160 # MUSICAL SYMBOL EIGHTH NOTE +1D161 # MUSICAL SYMBOL SIXTEENTH NOTE +1D162 # MUSICAL SYMBOL THIRTY-SECOND NOTE +1D163 # MUSICAL SYMBOL SIXTY-FOURTH NOTE +1D164 # MUSICAL SYMBOL ONE HUNDRED TWENTY-EIGHTH NOTE +1D1BB # MUSICAL SYMBOL MINIMA +1D1BC # MUSICAL SYMBOL MINIMA BLACK +1D1BD # MUSICAL SYMBOL SEMIMINIMA WHITE +1D1BE # MUSICAL SYMBOL SEMIMINIMA BLACK +1D1BF # MUSICAL SYMBOL FUSA WHITE +1D1C0 # MUSICAL SYMBOL FUSA BLACK + +# Total code points: 14 + +# ================================================ +# (3) Singleton Decompositions +# These characters can be derived from the UnicodeData file +# by including all characters whose canonical decomposition +# consists of a single character. +# These characters are simply quoted here for reference. +# ================================================ + +# 0340..0341 [2] COMBINING GRAVE TONE MARK..COMBINING ACUTE TONE MARK +# 0343 COMBINING GREEK KORONIS +# 0374 GREEK NUMERAL SIGN +# 037E GREEK QUESTION MARK +# 0387 GREEK ANO TELEIA +# 1F71 GREEK SMALL LETTER ALPHA WITH OXIA +# 1F73 GREEK SMALL LETTER EPSILON WITH OXIA +# 1F75 GREEK SMALL LETTER ETA WITH OXIA +# 1F77 GREEK SMALL LETTER IOTA WITH OXIA +# 1F79 GREEK SMALL LETTER OMICRON WITH OXIA +# 1F7B GREEK SMALL LETTER UPSILON WITH OXIA +# 1F7D GREEK SMALL LETTER OMEGA WITH OXIA +# 1FBB GREEK CAPITAL LETTER ALPHA WITH OXIA +# 1FBE GREEK PROSGEGRAMMENI +# 1FC9 GREEK CAPITAL LETTER EPSILON WITH OXIA +# 1FCB GREEK CAPITAL LETTER ETA WITH OXIA +# 1FD3 GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA +# 1FDB GREEK CAPITAL LETTER IOTA WITH OXIA +# 1FE3 GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND OXIA +# 1FEB GREEK CAPITAL LETTER UPSILON WITH OXIA +# 1FEE..1FEF [2] GREEK DIALYTIKA AND OXIA..GREEK VARIA +# 1FF9 GREEK CAPITAL LETTER OMICRON WITH OXIA +# 1FFB GREEK CAPITAL LETTER OMEGA WITH OXIA +# 1FFD GREEK OXIA +# 2000..2001 [2] EN QUAD..EM QUAD +# 2126 OHM SIGN +# 212A..212B [2] KELVIN SIGN..ANGSTROM SIGN +# 2329 LEFT-POINTING ANGLE BRACKET +# 232A RIGHT-POINTING ANGLE BRACKET +# F900..FA0D [270] CJK COMPATIBILITY IDEOGRAPH-F900..CJK COMPATIBILITY IDEOGRAPH-FA0D +# FA10 CJK COMPATIBILITY IDEOGRAPH-FA10 +# FA12 CJK COMPATIBILITY IDEOGRAPH-FA12 +# FA15..FA1E [10] CJK COMPATIBILITY IDEOGRAPH-FA15..CJK COMPATIBILITY IDEOGRAPH-FA1E +# FA20 CJK COMPATIBILITY IDEOGRAPH-FA20 +# FA22 CJK COMPATIBILITY IDEOGRAPH-FA22 +# FA25..FA26 [2] CJK COMPATIBILITY IDEOGRAPH-FA25..CJK COMPATIBILITY IDEOGRAPH-FA26 +# FA2A..FA2D [4] CJK COMPATIBILITY IDEOGRAPH-FA2A..CJK COMPATIBILITY IDEOGRAPH-FA2D +# FA30..FA6A [59] CJK COMPATIBILITY IDEOGRAPH-FA30..CJK COMPATIBILITY IDEOGRAPH-FA6A +# 2F800..2FA1D [542] CJK COMPATIBILITY IDEOGRAPH-2F800..CJK COMPATIBILITY IDEOGRAPH-2FA1D + +# Total code points: 924 + +# ================================================ +# (4) Non-Starter Decompositions +# These characters can be derived from the UnicodeData file +# by including all characters whose canonical decomposition consists +# of a sequence of characters, the first of which has a non-zero +# combining class. +# These characters are simply quoted here for reference. +# ================================================ + +# 0344 COMBINING GREEK DIALYTIKA TONOS +# 0F73 TIBETAN VOWEL SIGN II +# 0F75 TIBETAN VOWEL SIGN UU +# 0F81 TIBETAN VOWEL SIGN REVERSED II + +# Total code points: 4 + diff --git a/libraries/liblunicode/Makefile.in b/libraries/liblunicode/Makefile.in new file mode 100644 index 0000000..deb2130 --- /dev/null +++ b/libraries/liblunicode/Makefile.in @@ -0,0 +1,54 @@ +# Makefile.in for LDAP -llunicode +# $OpenLDAP$ +## This work is part of OpenLDAP Software <http://www.openldap.org/>. +## +## Copyright 1998-2021 The OpenLDAP Foundation. +## All rights reserved. +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted only as authorized by the OpenLDAP +## Public License. +## +## A copy of this license is available in the file LICENSE in the +## top-level directory of the distribution or, alternatively, at +## <http://www.OpenLDAP.org/license.html>. + +LIBRARY = liblunicode.a + +XXDIR = $(srcdir)/ucdata/ +XXHEADERS = ucdata.h ure.h uctable.h + +XXSRCS = ucdata.c ucgendat.c ure.c urestubs.c +SRCS = ucstr.c +OBJS = ucdata.o ure.o urestubs.o ucstr.o + +XLIB = $(LIBRARY) +XLIBS = $(LDAP_LIBLUTIL_A) $(LDAP_LIBLBER_LA) +#PROGRAMS = ucgendat + +LDAP_INCDIR= ../../include +LDAP_LIBDIR= ../../libraries + +uctable.h: $(XXDIR)/uctable.h + +$(XXDIR)/uctable.h: $(XXDIR)/ucgendat.c $(srcdir)/UnicodeData.txt $(srcdir)/CompositionExclusions.txt + $(MAKE) ucgendat + ./ucgendat $(srcdir)/UnicodeData.txt -x $(srcdir)/CompositionExclusions.txt + +ucgendat: $(XLIBS) ucgendat.o + $(LTLINK) -o $@ ucgendat.o $(LIBS) + +.links : + @for i in $(XXSRCS) $(XXHEADERS); do \ + $(RM) $$i ; \ + ii=`find $(srcdir) -name $$i` ; \ + $(LN_S) $$ii . ; \ + done + touch .links + +$(XXSRCS) $(XXHEADERS) : .links + +clean-local: FORCE + @$(RM) *.dat .links $(XXHEADERS) ucgendat + +depend-common: .links diff --git a/libraries/liblunicode/UCD-Terms b/libraries/liblunicode/UCD-Terms new file mode 100644 index 0000000..4ec4da2 --- /dev/null +++ b/libraries/liblunicode/UCD-Terms @@ -0,0 +1,29 @@ +UCD Terms of Use (http://www.unicode.org/Public/UNIDATA/UCD.html) + +Disclaimer + +The Unicode Character Database is provided as is by Unicode, Inc. +No claims are made as to fitness for any particular purpose. No +warranties of any kind are expressed or implied. The recipient +agrees to determine applicability of information provided. If this +file has been purchased on magnetic or optical media from Unicode, +Inc., the sole remedy for any claim will be exchange of defective +media within 90 days of receipt. + +This disclaimer is applicable for all other data files accompanying +the Unicode Character Database, some of which have been compiled +by the Unicode Consortium, and some of which have been supplied by +other sources. + +Limitations on Rights to Redistribute This Data + +Recipient is granted the right to make copies in any form for +internal distribution and to freely use the information supplied +in the creation of products supporting the Unicode (TM) Standard. +The files in the Unicode Character Database can be redistributed +to third parties or other organizations (whether for profit or not) +as long as this notice and the disclaimer notice are retained. +Information can be extracted from these files and used in documentation +or programs, as long as there is an accompanying notice indicating +the source. + diff --git a/libraries/liblunicode/UnicodeData.txt b/libraries/liblunicode/UnicodeData.txt new file mode 100644 index 0000000..125a692 --- /dev/null +++ b/libraries/liblunicode/UnicodeData.txt @@ -0,0 +1,13874 @@ +0000;<control>;Cc;0;BN;;;;;N;NULL;;;; +0001;<control>;Cc;0;BN;;;;;N;START OF HEADING;;;; +0002;<control>;Cc;0;BN;;;;;N;START OF TEXT;;;; +0003;<control>;Cc;0;BN;;;;;N;END OF TEXT;;;; +0004;<control>;Cc;0;BN;;;;;N;END OF TRANSMISSION;;;; +0005;<control>;Cc;0;BN;;;;;N;ENQUIRY;;;; +0006;<control>;Cc;0;BN;;;;;N;ACKNOWLEDGE;;;; +0007;<control>;Cc;0;BN;;;;;N;BELL;;;; +0008;<control>;Cc;0;BN;;;;;N;BACKSPACE;;;; +0009;<control>;Cc;0;S;;;;;N;CHARACTER TABULATION;;;; +000A;<control>;Cc;0;B;;;;;N;LINE FEED (LF);;;; +000B;<control>;Cc;0;S;;;;;N;LINE TABULATION;;;; +000C;<control>;Cc;0;WS;;;;;N;FORM FEED (FF);;;; +000D;<control>;Cc;0;B;;;;;N;CARRIAGE RETURN (CR);;;; +000E;<control>;Cc;0;BN;;;;;N;SHIFT OUT;;;; +000F;<control>;Cc;0;BN;;;;;N;SHIFT IN;;;; +0010;<control>;Cc;0;BN;;;;;N;DATA LINK ESCAPE;;;; +0011;<control>;Cc;0;BN;;;;;N;DEVICE CONTROL ONE;;;; +0012;<control>;Cc;0;BN;;;;;N;DEVICE CONTROL TWO;;;; +0013;<control>;Cc;0;BN;;;;;N;DEVICE CONTROL THREE;;;; +0014;<control>;Cc;0;BN;;;;;N;DEVICE CONTROL FOUR;;;; +0015;<control>;Cc;0;BN;;;;;N;NEGATIVE ACKNOWLEDGE;;;; +0016;<control>;Cc;0;BN;;;;;N;SYNCHRONOUS IDLE;;;; +0017;<control>;Cc;0;BN;;;;;N;END OF TRANSMISSION BLOCK;;;; +0018;<control>;Cc;0;BN;;;;;N;CANCEL;;;; +0019;<control>;Cc;0;BN;;;;;N;END OF MEDIUM;;;; +001A;<control>;Cc;0;BN;;;;;N;SUBSTITUTE;;;; +001B;<control>;Cc;0;BN;;;;;N;ESCAPE;;;; +001C;<control>;Cc;0;B;;;;;N;INFORMATION SEPARATOR FOUR;;;; +001D;<control>;Cc;0;B;;;;;N;INFORMATION SEPARATOR THREE;;;; +001E;<control>;Cc;0;B;;;;;N;INFORMATION SEPARATOR TWO;;;; +001F;<control>;Cc;0;S;;;;;N;INFORMATION SEPARATOR ONE;;;; +0020;SPACE;Zs;0;WS;;;;;N;;;;; +0021;EXCLAMATION MARK;Po;0;ON;;;;;N;;;;; +0022;QUOTATION MARK;Po;0;ON;;;;;N;;;;; +0023;NUMBER SIGN;Po;0;ET;;;;;N;;;;; +0024;DOLLAR SIGN;Sc;0;ET;;;;;N;;;;; +0025;PERCENT SIGN;Po;0;ET;;;;;N;;;;; +0026;AMPERSAND;Po;0;ON;;;;;N;;;;; +0027;APOSTROPHE;Po;0;ON;;;;;N;APOSTROPHE-QUOTE;;;; +0028;LEFT PARENTHESIS;Ps;0;ON;;;;;Y;OPENING PARENTHESIS;;;; +0029;RIGHT PARENTHESIS;Pe;0;ON;;;;;Y;CLOSING PARENTHESIS;;;; +002A;ASTERISK;Po;0;ON;;;;;N;;;;; +002B;PLUS SIGN;Sm;0;ET;;;;;N;;;;; +002C;COMMA;Po;0;CS;;;;;N;;;;; +002D;HYPHEN-MINUS;Pd;0;ET;;;;;N;;;;; +002E;FULL STOP;Po;0;CS;;;;;N;PERIOD;;;; +002F;SOLIDUS;Po;0;ES;;;;;N;SLASH;;;; +0030;DIGIT ZERO;Nd;0;EN;;0;0;0;N;;;;; +0031;DIGIT ONE;Nd;0;EN;;1;1;1;N;;;;; +0032;DIGIT TWO;Nd;0;EN;;2;2;2;N;;;;; +0033;DIGIT THREE;Nd;0;EN;;3;3;3;N;;;;; +0034;DIGIT FOUR;Nd;0;EN;;4;4;4;N;;;;; +0035;DIGIT FIVE;Nd;0;EN;;5;5;5;N;;;;; +0036;DIGIT SIX;Nd;0;EN;;6;6;6;N;;;;; +0037;DIGIT SEVEN;Nd;0;EN;;7;7;7;N;;;;; +0038;DIGIT EIGHT;Nd;0;EN;;8;8;8;N;;;;; +0039;DIGIT NINE;Nd;0;EN;;9;9;9;N;;;;; +003A;COLON;Po;0;CS;;;;;N;;;;; +003B;SEMICOLON;Po;0;ON;;;;;N;;;;; +003C;LESS-THAN SIGN;Sm;0;ON;;;;;Y;;;;; +003D;EQUALS SIGN;Sm;0;ON;;;;;N;;;;; +003E;GREATER-THAN SIGN;Sm;0;ON;;;;;Y;;;;; +003F;QUESTION MARK;Po;0;ON;;;;;N;;;;; +0040;COMMERCIAL AT;Po;0;ON;;;;;N;;;;; +0041;LATIN CAPITAL LETTER A;Lu;0;L;;;;;N;;;;0061; +0042;LATIN CAPITAL LETTER B;Lu;0;L;;;;;N;;;;0062; +0043;LATIN CAPITAL LETTER C;Lu;0;L;;;;;N;;;;0063; +0044;LATIN CAPITAL LETTER D;Lu;0;L;;;;;N;;;;0064; +0045;LATIN CAPITAL LETTER E;Lu;0;L;;;;;N;;;;0065; +0046;LATIN CAPITAL LETTER F;Lu;0;L;;;;;N;;;;0066; +0047;LATIN CAPITAL LETTER G;Lu;0;L;;;;;N;;;;0067; +0048;LATIN CAPITAL LETTER H;Lu;0;L;;;;;N;;;;0068; +0049;LATIN CAPITAL LETTER I;Lu;0;L;;;;;N;;;;0069; +004A;LATIN CAPITAL LETTER J;Lu;0;L;;;;;N;;;;006A; +004B;LATIN CAPITAL LETTER K;Lu;0;L;;;;;N;;;;006B; +004C;LATIN CAPITAL LETTER L;Lu;0;L;;;;;N;;;;006C; +004D;LATIN CAPITAL LETTER M;Lu;0;L;;;;;N;;;;006D; +004E;LATIN CAPITAL LETTER N;Lu;0;L;;;;;N;;;;006E; +004F;LATIN CAPITAL LETTER O;Lu;0;L;;;;;N;;;;006F; +0050;LATIN CAPITAL LETTER P;Lu;0;L;;;;;N;;;;0070; +0051;LATIN CAPITAL LETTER Q;Lu;0;L;;;;;N;;;;0071; +0052;LATIN CAPITAL LETTER R;Lu;0;L;;;;;N;;;;0072; +0053;LATIN CAPITAL LETTER S;Lu;0;L;;;;;N;;;;0073; +0054;LATIN CAPITAL LETTER T;Lu;0;L;;;;;N;;;;0074; +0055;LATIN CAPITAL LETTER U;Lu;0;L;;;;;N;;;;0075; +0056;LATIN CAPITAL LETTER V;Lu;0;L;;;;;N;;;;0076; +0057;LATIN CAPITAL LETTER W;Lu;0;L;;;;;N;;;;0077; +0058;LATIN CAPITAL LETTER X;Lu;0;L;;;;;N;;;;0078; +0059;LATIN CAPITAL LETTER Y;Lu;0;L;;;;;N;;;;0079; +005A;LATIN CAPITAL LETTER Z;Lu;0;L;;;;;N;;;;007A; +005B;LEFT SQUARE BRACKET;Ps;0;ON;;;;;Y;OPENING SQUARE BRACKET;;;; +005C;REVERSE SOLIDUS;Po;0;ON;;;;;N;BACKSLASH;;;; +005D;RIGHT SQUARE BRACKET;Pe;0;ON;;;;;Y;CLOSING SQUARE BRACKET;;;; +005E;CIRCUMFLEX ACCENT;Sk;0;ON;;;;;N;SPACING CIRCUMFLEX;;;; +005F;LOW LINE;Pc;0;ON;;;;;N;SPACING UNDERSCORE;;;; +0060;GRAVE ACCENT;Sk;0;ON;;;;;N;SPACING GRAVE;;;; +0061;LATIN SMALL LETTER A;Ll;0;L;;;;;N;;;0041;;0041 +0062;LATIN SMALL LETTER B;Ll;0;L;;;;;N;;;0042;;0042 +0063;LATIN SMALL LETTER C;Ll;0;L;;;;;N;;;0043;;0043 +0064;LATIN SMALL LETTER D;Ll;0;L;;;;;N;;;0044;;0044 +0065;LATIN SMALL LETTER E;Ll;0;L;;;;;N;;;0045;;0045 +0066;LATIN SMALL LETTER F;Ll;0;L;;;;;N;;;0046;;0046 +0067;LATIN SMALL LETTER G;Ll;0;L;;;;;N;;;0047;;0047 +0068;LATIN SMALL LETTER H;Ll;0;L;;;;;N;;;0048;;0048 +0069;LATIN SMALL LETTER I;Ll;0;L;;;;;N;;;0049;;0049 +006A;LATIN SMALL LETTER J;Ll;0;L;;;;;N;;;004A;;004A +006B;LATIN SMALL LETTER K;Ll;0;L;;;;;N;;;004B;;004B +006C;LATIN SMALL LETTER L;Ll;0;L;;;;;N;;;004C;;004C +006D;LATIN SMALL LETTER M;Ll;0;L;;;;;N;;;004D;;004D +006E;LATIN SMALL LETTER N;Ll;0;L;;;;;N;;;004E;;004E +006F;LATIN SMALL LETTER O;Ll;0;L;;;;;N;;;004F;;004F +0070;LATIN SMALL LETTER P;Ll;0;L;;;;;N;;;0050;;0050 +0071;LATIN SMALL LETTER Q;Ll;0;L;;;;;N;;;0051;;0051 +0072;LATIN SMALL LETTER R;Ll;0;L;;;;;N;;;0052;;0052 +0073;LATIN SMALL LETTER S;Ll;0;L;;;;;N;;;0053;;0053 +0074;LATIN SMALL LETTER T;Ll;0;L;;;;;N;;;0054;;0054 +0075;LATIN SMALL LETTER U;Ll;0;L;;;;;N;;;0055;;0055 +0076;LATIN SMALL LETTER V;Ll;0;L;;;;;N;;;0056;;0056 +0077;LATIN SMALL LETTER W;Ll;0;L;;;;;N;;;0057;;0057 +0078;LATIN SMALL LETTER X;Ll;0;L;;;;;N;;;0058;;0058 +0079;LATIN SMALL LETTER Y;Ll;0;L;;;;;N;;;0059;;0059 +007A;LATIN SMALL LETTER Z;Ll;0;L;;;;;N;;;005A;;005A +007B;LEFT CURLY BRACKET;Ps;0;ON;;;;;Y;OPENING CURLY BRACKET;;;; +007C;VERTICAL LINE;Sm;0;ON;;;;;N;VERTICAL BAR;;;; +007D;RIGHT CURLY BRACKET;Pe;0;ON;;;;;Y;CLOSING CURLY BRACKET;;;; +007E;TILDE;Sm;0;ON;;;;;N;;;;; +007F;<control>;Cc;0;BN;;;;;N;DELETE;;;; +0080;<control>;Cc;0;BN;;;;;N;;;;; +0081;<control>;Cc;0;BN;;;;;N;;;;; +0082;<control>;Cc;0;BN;;;;;N;BREAK PERMITTED HERE;;;; +0083;<control>;Cc;0;BN;;;;;N;NO BREAK HERE;;;; +0084;<control>;Cc;0;BN;;;;;N;;;;; +0085;<control>;Cc;0;B;;;;;N;NEXT LINE (NEL);;;; +0086;<control>;Cc;0;BN;;;;;N;START OF SELECTED AREA;;;; +0087;<control>;Cc;0;BN;;;;;N;END OF SELECTED AREA;;;; +0088;<control>;Cc;0;BN;;;;;N;CHARACTER TABULATION SET;;;; +0089;<control>;Cc;0;BN;;;;;N;CHARACTER TABULATION WITH JUSTIFICATION;;;; +008A;<control>;Cc;0;BN;;;;;N;LINE TABULATION SET;;;; +008B;<control>;Cc;0;BN;;;;;N;PARTIAL LINE FORWARD;;;; +008C;<control>;Cc;0;BN;;;;;N;PARTIAL LINE BACKWARD;;;; +008D;<control>;Cc;0;BN;;;;;N;REVERSE LINE FEED;;;; +008E;<control>;Cc;0;BN;;;;;N;SINGLE SHIFT TWO;;;; +008F;<control>;Cc;0;BN;;;;;N;SINGLE SHIFT THREE;;;; +0090;<control>;Cc;0;BN;;;;;N;DEVICE CONTROL STRING;;;; +0091;<control>;Cc;0;BN;;;;;N;PRIVATE USE ONE;;;; +0092;<control>;Cc;0;BN;;;;;N;PRIVATE USE TWO;;;; +0093;<control>;Cc;0;BN;;;;;N;SET TRANSMIT STATE;;;; +0094;<control>;Cc;0;BN;;;;;N;CANCEL CHARACTER;;;; +0095;<control>;Cc;0;BN;;;;;N;MESSAGE WAITING;;;; +0096;<control>;Cc;0;BN;;;;;N;START OF GUARDED AREA;;;; +0097;<control>;Cc;0;BN;;;;;N;END OF GUARDED AREA;;;; +0098;<control>;Cc;0;BN;;;;;N;START OF STRING;;;; +0099;<control>;Cc;0;BN;;;;;N;;;;; +009A;<control>;Cc;0;BN;;;;;N;SINGLE CHARACTER INTRODUCER;;;; +009B;<control>;Cc;0;BN;;;;;N;CONTROL SEQUENCE INTRODUCER;;;; +009C;<control>;Cc;0;BN;;;;;N;STRING TERMINATOR;;;; +009D;<control>;Cc;0;BN;;;;;N;OPERATING SYSTEM COMMAND;;;; +009E;<control>;Cc;0;BN;;;;;N;PRIVACY MESSAGE;;;; +009F;<control>;Cc;0;BN;;;;;N;APPLICATION PROGRAM COMMAND;;;; +00A0;NO-BREAK SPACE;Zs;0;CS;<noBreak> 0020;;;;N;NON-BREAKING SPACE;;;; +00A1;INVERTED EXCLAMATION MARK;Po;0;ON;;;;;N;;;;; +00A2;CENT SIGN;Sc;0;ET;;;;;N;;;;; +00A3;POUND SIGN;Sc;0;ET;;;;;N;;;;; +00A4;CURRENCY SIGN;Sc;0;ET;;;;;N;;;;; +00A5;YEN SIGN;Sc;0;ET;;;;;N;;;;; +00A6;BROKEN BAR;So;0;ON;;;;;N;BROKEN VERTICAL BAR;;;; +00A7;SECTION SIGN;So;0;ON;;;;;N;;;;; +00A8;DIAERESIS;Sk;0;ON;<compat> 0020 0308;;;;N;SPACING DIAERESIS;;;; +00A9;COPYRIGHT SIGN;So;0;ON;;;;;N;;;;; +00AA;FEMININE ORDINAL INDICATOR;Ll;0;L;<super> 0061;;;;N;;;;; +00AB;LEFT-POINTING DOUBLE ANGLE QUOTATION MARK;Pi;0;ON;;;;;Y;LEFT POINTING GUILLEMET;*;;; +00AC;NOT SIGN;Sm;0;ON;;;;;N;;;;; +00AD;SOFT HYPHEN;Pd;0;ON;;;;;N;;;;; +00AE;REGISTERED SIGN;So;0;ON;;;;;N;REGISTERED TRADE MARK SIGN;;;; +00AF;MACRON;Sk;0;ON;<compat> 0020 0304;;;;N;SPACING MACRON;;;; +00B0;DEGREE SIGN;So;0;ET;;;;;N;;;;; +00B1;PLUS-MINUS SIGN;Sm;0;ET;;;;;N;PLUS-OR-MINUS SIGN;;;; +00B2;SUPERSCRIPT TWO;No;0;EN;<super> 0032;2;2;2;N;SUPERSCRIPT DIGIT TWO;;;; +00B3;SUPERSCRIPT THREE;No;0;EN;<super> 0033;3;3;3;N;SUPERSCRIPT DIGIT THREE;;;; +00B4;ACUTE ACCENT;Sk;0;ON;<compat> 0020 0301;;;;N;SPACING ACUTE;;;; +00B5;MICRO SIGN;Ll;0;L;<compat> 03BC;;;;N;;;039C;;039C +00B6;PILCROW SIGN;So;0;ON;;;;;N;PARAGRAPH SIGN;;;; +00B7;MIDDLE DOT;Po;0;ON;;;;;N;;;;; +00B8;CEDILLA;Sk;0;ON;<compat> 0020 0327;;;;N;SPACING CEDILLA;;;; +00B9;SUPERSCRIPT ONE;No;0;EN;<super> 0031;1;1;1;N;SUPERSCRIPT DIGIT ONE;;;; +00BA;MASCULINE ORDINAL INDICATOR;Ll;0;L;<super> 006F;;;;N;;;;; +00BB;RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK;Pf;0;ON;;;;;Y;RIGHT POINTING GUILLEMET;*;;; +00BC;VULGAR FRACTION ONE QUARTER;No;0;ON;<fraction> 0031 2044 0034;;;1/4;N;FRACTION ONE QUARTER;;;; +00BD;VULGAR FRACTION ONE HALF;No;0;ON;<fraction> 0031 2044 0032;;;1/2;N;FRACTION ONE HALF;;;; +00BE;VULGAR FRACTION THREE QUARTERS;No;0;ON;<fraction> 0033 2044 0034;;;3/4;N;FRACTION THREE QUARTERS;;;; +00BF;INVERTED QUESTION MARK;Po;0;ON;;;;;N;;;;; +00C0;LATIN CAPITAL LETTER A WITH GRAVE;Lu;0;L;0041 0300;;;;N;LATIN CAPITAL LETTER A GRAVE;;;00E0; +00C1;LATIN CAPITAL LETTER A WITH ACUTE;Lu;0;L;0041 0301;;;;N;LATIN CAPITAL LETTER A ACUTE;;;00E1; +00C2;LATIN CAPITAL LETTER A WITH CIRCUMFLEX;Lu;0;L;0041 0302;;;;N;LATIN CAPITAL LETTER A CIRCUMFLEX;;;00E2; +00C3;LATIN CAPITAL LETTER A WITH TILDE;Lu;0;L;0041 0303;;;;N;LATIN CAPITAL LETTER A TILDE;;;00E3; +00C4;LATIN CAPITAL LETTER A WITH DIAERESIS;Lu;0;L;0041 0308;;;;N;LATIN CAPITAL LETTER A DIAERESIS;;;00E4; +00C5;LATIN CAPITAL LETTER A WITH RING ABOVE;Lu;0;L;0041 030A;;;;N;LATIN CAPITAL LETTER A RING;;;00E5; +00C6;LATIN CAPITAL LETTER AE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER A E;ash *;;00E6; +00C7;LATIN CAPITAL LETTER C WITH CEDILLA;Lu;0;L;0043 0327;;;;N;LATIN CAPITAL LETTER C CEDILLA;;;00E7; +00C8;LATIN CAPITAL LETTER E WITH GRAVE;Lu;0;L;0045 0300;;;;N;LATIN CAPITAL LETTER E GRAVE;;;00E8; +00C9;LATIN CAPITAL LETTER E WITH ACUTE;Lu;0;L;0045 0301;;;;N;LATIN CAPITAL LETTER E ACUTE;;;00E9; +00CA;LATIN CAPITAL LETTER E WITH CIRCUMFLEX;Lu;0;L;0045 0302;;;;N;LATIN CAPITAL LETTER E CIRCUMFLEX;;;00EA; +00CB;LATIN CAPITAL LETTER E WITH DIAERESIS;Lu;0;L;0045 0308;;;;N;LATIN CAPITAL LETTER E DIAERESIS;;;00EB; +00CC;LATIN CAPITAL LETTER I WITH GRAVE;Lu;0;L;0049 0300;;;;N;LATIN CAPITAL LETTER I GRAVE;;;00EC; +00CD;LATIN CAPITAL LETTER I WITH ACUTE;Lu;0;L;0049 0301;;;;N;LATIN CAPITAL LETTER I ACUTE;;;00ED; +00CE;LATIN CAPITAL LETTER I WITH CIRCUMFLEX;Lu;0;L;0049 0302;;;;N;LATIN CAPITAL LETTER I CIRCUMFLEX;;;00EE; +00CF;LATIN CAPITAL LETTER I WITH DIAERESIS;Lu;0;L;0049 0308;;;;N;LATIN CAPITAL LETTER I DIAERESIS;;;00EF; +00D0;LATIN CAPITAL LETTER ETH;Lu;0;L;;;;;N;;Icelandic;;00F0; +00D1;LATIN CAPITAL LETTER N WITH TILDE;Lu;0;L;004E 0303;;;;N;LATIN CAPITAL LETTER N TILDE;;;00F1; +00D2;LATIN CAPITAL LETTER O WITH GRAVE;Lu;0;L;004F 0300;;;;N;LATIN CAPITAL LETTER O GRAVE;;;00F2; +00D3;LATIN CAPITAL LETTER O WITH ACUTE;Lu;0;L;004F 0301;;;;N;LATIN CAPITAL LETTER O ACUTE;;;00F3; +00D4;LATIN CAPITAL LETTER O WITH CIRCUMFLEX;Lu;0;L;004F 0302;;;;N;LATIN CAPITAL LETTER O CIRCUMFLEX;;;00F4; +00D5;LATIN CAPITAL LETTER O WITH TILDE;Lu;0;L;004F 0303;;;;N;LATIN CAPITAL LETTER O TILDE;;;00F5; +00D6;LATIN CAPITAL LETTER O WITH DIAERESIS;Lu;0;L;004F 0308;;;;N;LATIN CAPITAL LETTER O DIAERESIS;;;00F6; +00D7;MULTIPLICATION SIGN;Sm;0;ON;;;;;N;;;;; +00D8;LATIN CAPITAL LETTER O WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER O SLASH;;;00F8; +00D9;LATIN CAPITAL LETTER U WITH GRAVE;Lu;0;L;0055 0300;;;;N;LATIN CAPITAL LETTER U GRAVE;;;00F9; +00DA;LATIN CAPITAL LETTER U WITH ACUTE;Lu;0;L;0055 0301;;;;N;LATIN CAPITAL LETTER U ACUTE;;;00FA; +00DB;LATIN CAPITAL LETTER U WITH CIRCUMFLEX;Lu;0;L;0055 0302;;;;N;LATIN CAPITAL LETTER U CIRCUMFLEX;;;00FB; +00DC;LATIN CAPITAL LETTER U WITH DIAERESIS;Lu;0;L;0055 0308;;;;N;LATIN CAPITAL LETTER U DIAERESIS;;;00FC; +00DD;LATIN CAPITAL LETTER Y WITH ACUTE;Lu;0;L;0059 0301;;;;N;LATIN CAPITAL LETTER Y ACUTE;;;00FD; +00DE;LATIN CAPITAL LETTER THORN;Lu;0;L;;;;;N;;Icelandic;;00FE; +00DF;LATIN SMALL LETTER SHARP S;Ll;0;L;;;;;N;;German;;; +00E0;LATIN SMALL LETTER A WITH GRAVE;Ll;0;L;0061 0300;;;;N;LATIN SMALL LETTER A GRAVE;;00C0;;00C0 +00E1;LATIN SMALL LETTER A WITH ACUTE;Ll;0;L;0061 0301;;;;N;LATIN SMALL LETTER A ACUTE;;00C1;;00C1 +00E2;LATIN SMALL LETTER A WITH CIRCUMFLEX;Ll;0;L;0061 0302;;;;N;LATIN SMALL LETTER A CIRCUMFLEX;;00C2;;00C2 +00E3;LATIN SMALL LETTER A WITH TILDE;Ll;0;L;0061 0303;;;;N;LATIN SMALL LETTER A TILDE;;00C3;;00C3 +00E4;LATIN SMALL LETTER A WITH DIAERESIS;Ll;0;L;0061 0308;;;;N;LATIN SMALL LETTER A DIAERESIS;;00C4;;00C4 +00E5;LATIN SMALL LETTER A WITH RING ABOVE;Ll;0;L;0061 030A;;;;N;LATIN SMALL LETTER A RING;;00C5;;00C5 +00E6;LATIN SMALL LETTER AE;Ll;0;L;;;;;N;LATIN SMALL LETTER A E;ash *;00C6;;00C6 +00E7;LATIN SMALL LETTER C WITH CEDILLA;Ll;0;L;0063 0327;;;;N;LATIN SMALL LETTER C CEDILLA;;00C7;;00C7 +00E8;LATIN SMALL LETTER E WITH GRAVE;Ll;0;L;0065 0300;;;;N;LATIN SMALL LETTER E GRAVE;;00C8;;00C8 +00E9;LATIN SMALL LETTER E WITH ACUTE;Ll;0;L;0065 0301;;;;N;LATIN SMALL LETTER E ACUTE;;00C9;;00C9 +00EA;LATIN SMALL LETTER E WITH CIRCUMFLEX;Ll;0;L;0065 0302;;;;N;LATIN SMALL LETTER E CIRCUMFLEX;;00CA;;00CA +00EB;LATIN SMALL LETTER E WITH DIAERESIS;Ll;0;L;0065 0308;;;;N;LATIN SMALL LETTER E DIAERESIS;;00CB;;00CB +00EC;LATIN SMALL LETTER I WITH GRAVE;Ll;0;L;0069 0300;;;;N;LATIN SMALL LETTER I GRAVE;;00CC;;00CC +00ED;LATIN SMALL LETTER I WITH ACUTE;Ll;0;L;0069 0301;;;;N;LATIN SMALL LETTER I ACUTE;;00CD;;00CD +00EE;LATIN SMALL LETTER I WITH CIRCUMFLEX;Ll;0;L;0069 0302;;;;N;LATIN SMALL LETTER I CIRCUMFLEX;;00CE;;00CE +00EF;LATIN SMALL LETTER I WITH DIAERESIS;Ll;0;L;0069 0308;;;;N;LATIN SMALL LETTER I DIAERESIS;;00CF;;00CF +00F0;LATIN SMALL LETTER ETH;Ll;0;L;;;;;N;;Icelandic;00D0;;00D0 +00F1;LATIN SMALL LETTER N WITH TILDE;Ll;0;L;006E 0303;;;;N;LATIN SMALL LETTER N TILDE;;00D1;;00D1 +00F2;LATIN SMALL LETTER O WITH GRAVE;Ll;0;L;006F 0300;;;;N;LATIN SMALL LETTER O GRAVE;;00D2;;00D2 +00F3;LATIN SMALL LETTER O WITH ACUTE;Ll;0;L;006F 0301;;;;N;LATIN SMALL LETTER O ACUTE;;00D3;;00D3 +00F4;LATIN SMALL LETTER O WITH CIRCUMFLEX;Ll;0;L;006F 0302;;;;N;LATIN SMALL LETTER O CIRCUMFLEX;;00D4;;00D4 +00F5;LATIN SMALL LETTER O WITH TILDE;Ll;0;L;006F 0303;;;;N;LATIN SMALL LETTER O TILDE;;00D5;;00D5 +00F6;LATIN SMALL LETTER O WITH DIAERESIS;Ll;0;L;006F 0308;;;;N;LATIN SMALL LETTER O DIAERESIS;;00D6;;00D6 +00F7;DIVISION SIGN;Sm;0;ON;;;;;N;;;;; +00F8;LATIN SMALL LETTER O WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER O SLASH;;00D8;;00D8 +00F9;LATIN SMALL LETTER U WITH GRAVE;Ll;0;L;0075 0300;;;;N;LATIN SMALL LETTER U GRAVE;;00D9;;00D9 +00FA;LATIN SMALL LETTER U WITH ACUTE;Ll;0;L;0075 0301;;;;N;LATIN SMALL LETTER U ACUTE;;00DA;;00DA +00FB;LATIN SMALL LETTER U WITH CIRCUMFLEX;Ll;0;L;0075 0302;;;;N;LATIN SMALL LETTER U CIRCUMFLEX;;00DB;;00DB +00FC;LATIN SMALL LETTER U WITH DIAERESIS;Ll;0;L;0075 0308;;;;N;LATIN SMALL LETTER U DIAERESIS;;00DC;;00DC +00FD;LATIN SMALL LETTER Y WITH ACUTE;Ll;0;L;0079 0301;;;;N;LATIN SMALL LETTER Y ACUTE;;00DD;;00DD +00FE;LATIN SMALL LETTER THORN;Ll;0;L;;;;;N;;Icelandic;00DE;;00DE +00FF;LATIN SMALL LETTER Y WITH DIAERESIS;Ll;0;L;0079 0308;;;;N;LATIN SMALL LETTER Y DIAERESIS;;0178;;0178 +0100;LATIN CAPITAL LETTER A WITH MACRON;Lu;0;L;0041 0304;;;;N;LATIN CAPITAL LETTER A MACRON;;;0101; +0101;LATIN SMALL LETTER A WITH MACRON;Ll;0;L;0061 0304;;;;N;LATIN SMALL LETTER A MACRON;;0100;;0100 +0102;LATIN CAPITAL LETTER A WITH BREVE;Lu;0;L;0041 0306;;;;N;LATIN CAPITAL LETTER A BREVE;;;0103; +0103;LATIN SMALL LETTER A WITH BREVE;Ll;0;L;0061 0306;;;;N;LATIN SMALL LETTER A BREVE;;0102;;0102 +0104;LATIN CAPITAL LETTER A WITH OGONEK;Lu;0;L;0041 0328;;;;N;LATIN CAPITAL LETTER A OGONEK;;;0105; +0105;LATIN SMALL LETTER A WITH OGONEK;Ll;0;L;0061 0328;;;;N;LATIN SMALL LETTER A OGONEK;;0104;;0104 +0106;LATIN CAPITAL LETTER C WITH ACUTE;Lu;0;L;0043 0301;;;;N;LATIN CAPITAL LETTER C ACUTE;;;0107; +0107;LATIN SMALL LETTER C WITH ACUTE;Ll;0;L;0063 0301;;;;N;LATIN SMALL LETTER C ACUTE;;0106;;0106 +0108;LATIN CAPITAL LETTER C WITH CIRCUMFLEX;Lu;0;L;0043 0302;;;;N;LATIN CAPITAL LETTER C CIRCUMFLEX;;;0109; +0109;LATIN SMALL LETTER C WITH CIRCUMFLEX;Ll;0;L;0063 0302;;;;N;LATIN SMALL LETTER C CIRCUMFLEX;;0108;;0108 +010A;LATIN CAPITAL LETTER C WITH DOT ABOVE;Lu;0;L;0043 0307;;;;N;LATIN CAPITAL LETTER C DOT;;;010B; +010B;LATIN SMALL LETTER C WITH DOT ABOVE;Ll;0;L;0063 0307;;;;N;LATIN SMALL LETTER C DOT;;010A;;010A +010C;LATIN CAPITAL LETTER C WITH CARON;Lu;0;L;0043 030C;;;;N;LATIN CAPITAL LETTER C HACEK;;;010D; +010D;LATIN SMALL LETTER C WITH CARON;Ll;0;L;0063 030C;;;;N;LATIN SMALL LETTER C HACEK;;010C;;010C +010E;LATIN CAPITAL LETTER D WITH CARON;Lu;0;L;0044 030C;;;;N;LATIN CAPITAL LETTER D HACEK;;;010F; +010F;LATIN SMALL LETTER D WITH CARON;Ll;0;L;0064 030C;;;;N;LATIN SMALL LETTER D HACEK;;010E;;010E +0110;LATIN CAPITAL LETTER D WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER D BAR;;;0111; +0111;LATIN SMALL LETTER D WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER D BAR;;0110;;0110 +0112;LATIN CAPITAL LETTER E WITH MACRON;Lu;0;L;0045 0304;;;;N;LATIN CAPITAL LETTER E MACRON;;;0113; +0113;LATIN SMALL LETTER E WITH MACRON;Ll;0;L;0065 0304;;;;N;LATIN SMALL LETTER E MACRON;;0112;;0112 +0114;LATIN CAPITAL LETTER E WITH BREVE;Lu;0;L;0045 0306;;;;N;LATIN CAPITAL LETTER E BREVE;;;0115; +0115;LATIN SMALL LETTER E WITH BREVE;Ll;0;L;0065 0306;;;;N;LATIN SMALL LETTER E BREVE;;0114;;0114 +0116;LATIN CAPITAL LETTER E WITH DOT ABOVE;Lu;0;L;0045 0307;;;;N;LATIN CAPITAL LETTER E DOT;;;0117; +0117;LATIN SMALL LETTER E WITH DOT ABOVE;Ll;0;L;0065 0307;;;;N;LATIN SMALL LETTER E DOT;;0116;;0116 +0118;LATIN CAPITAL LETTER E WITH OGONEK;Lu;0;L;0045 0328;;;;N;LATIN CAPITAL LETTER E OGONEK;;;0119; +0119;LATIN SMALL LETTER E WITH OGONEK;Ll;0;L;0065 0328;;;;N;LATIN SMALL LETTER E OGONEK;;0118;;0118 +011A;LATIN CAPITAL LETTER E WITH CARON;Lu;0;L;0045 030C;;;;N;LATIN CAPITAL LETTER E HACEK;;;011B; +011B;LATIN SMALL LETTER E WITH CARON;Ll;0;L;0065 030C;;;;N;LATIN SMALL LETTER E HACEK;;011A;;011A +011C;LATIN CAPITAL LETTER G WITH CIRCUMFLEX;Lu;0;L;0047 0302;;;;N;LATIN CAPITAL LETTER G CIRCUMFLEX;;;011D; +011D;LATIN SMALL LETTER G WITH CIRCUMFLEX;Ll;0;L;0067 0302;;;;N;LATIN SMALL LETTER G CIRCUMFLEX;;011C;;011C +011E;LATIN CAPITAL LETTER G WITH BREVE;Lu;0;L;0047 0306;;;;N;LATIN CAPITAL LETTER G BREVE;;;011F; +011F;LATIN SMALL LETTER G WITH BREVE;Ll;0;L;0067 0306;;;;N;LATIN SMALL LETTER G BREVE;;011E;;011E +0120;LATIN CAPITAL LETTER G WITH DOT ABOVE;Lu;0;L;0047 0307;;;;N;LATIN CAPITAL LETTER G DOT;;;0121; +0121;LATIN SMALL LETTER G WITH DOT ABOVE;Ll;0;L;0067 0307;;;;N;LATIN SMALL LETTER G DOT;;0120;;0120 +0122;LATIN CAPITAL LETTER G WITH CEDILLA;Lu;0;L;0047 0327;;;;N;LATIN CAPITAL LETTER G CEDILLA;;;0123; +0123;LATIN SMALL LETTER G WITH CEDILLA;Ll;0;L;0067 0327;;;;N;LATIN SMALL LETTER G CEDILLA;;0122;;0122 +0124;LATIN CAPITAL LETTER H WITH CIRCUMFLEX;Lu;0;L;0048 0302;;;;N;LATIN CAPITAL LETTER H CIRCUMFLEX;;;0125; +0125;LATIN SMALL LETTER H WITH CIRCUMFLEX;Ll;0;L;0068 0302;;;;N;LATIN SMALL LETTER H CIRCUMFLEX;;0124;;0124 +0126;LATIN CAPITAL LETTER H WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER H BAR;;;0127; +0127;LATIN SMALL LETTER H WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER H BAR;;0126;;0126 +0128;LATIN CAPITAL LETTER I WITH TILDE;Lu;0;L;0049 0303;;;;N;LATIN CAPITAL LETTER I TILDE;;;0129; +0129;LATIN SMALL LETTER I WITH TILDE;Ll;0;L;0069 0303;;;;N;LATIN SMALL LETTER I TILDE;;0128;;0128 +012A;LATIN CAPITAL LETTER I WITH MACRON;Lu;0;L;0049 0304;;;;N;LATIN CAPITAL LETTER I MACRON;;;012B; +012B;LATIN SMALL LETTER I WITH MACRON;Ll;0;L;0069 0304;;;;N;LATIN SMALL LETTER I MACRON;;012A;;012A +012C;LATIN CAPITAL LETTER I WITH BREVE;Lu;0;L;0049 0306;;;;N;LATIN CAPITAL LETTER I BREVE;;;012D; +012D;LATIN SMALL LETTER I WITH BREVE;Ll;0;L;0069 0306;;;;N;LATIN SMALL LETTER I BREVE;;012C;;012C +012E;LATIN CAPITAL LETTER I WITH OGONEK;Lu;0;L;0049 0328;;;;N;LATIN CAPITAL LETTER I OGONEK;;;012F; +012F;LATIN SMALL LETTER I WITH OGONEK;Ll;0;L;0069 0328;;;;N;LATIN SMALL LETTER I OGONEK;;012E;;012E +0130;LATIN CAPITAL LETTER I WITH DOT ABOVE;Lu;0;L;0049 0307;;;;N;LATIN CAPITAL LETTER I DOT;;;0069; +0131;LATIN SMALL LETTER DOTLESS I;Ll;0;L;;;;;N;;;0049;;0049 +0132;LATIN CAPITAL LIGATURE IJ;Lu;0;L;<compat> 0049 004A;;;;N;LATIN CAPITAL LETTER I J;;;0133; +0133;LATIN SMALL LIGATURE IJ;Ll;0;L;<compat> 0069 006A;;;;N;LATIN SMALL LETTER I J;;0132;;0132 +0134;LATIN CAPITAL LETTER J WITH CIRCUMFLEX;Lu;0;L;004A 0302;;;;N;LATIN CAPITAL LETTER J CIRCUMFLEX;;;0135; +0135;LATIN SMALL LETTER J WITH CIRCUMFLEX;Ll;0;L;006A 0302;;;;N;LATIN SMALL LETTER J CIRCUMFLEX;;0134;;0134 +0136;LATIN CAPITAL LETTER K WITH CEDILLA;Lu;0;L;004B 0327;;;;N;LATIN CAPITAL LETTER K CEDILLA;;;0137; +0137;LATIN SMALL LETTER K WITH CEDILLA;Ll;0;L;006B 0327;;;;N;LATIN SMALL LETTER K CEDILLA;;0136;;0136 +0138;LATIN SMALL LETTER KRA;Ll;0;L;;;;;N;;Greenlandic;;; +0139;LATIN CAPITAL LETTER L WITH ACUTE;Lu;0;L;004C 0301;;;;N;LATIN CAPITAL LETTER L ACUTE;;;013A; +013A;LATIN SMALL LETTER L WITH ACUTE;Ll;0;L;006C 0301;;;;N;LATIN SMALL LETTER L ACUTE;;0139;;0139 +013B;LATIN CAPITAL LETTER L WITH CEDILLA;Lu;0;L;004C 0327;;;;N;LATIN CAPITAL LETTER L CEDILLA;;;013C; +013C;LATIN SMALL LETTER L WITH CEDILLA;Ll;0;L;006C 0327;;;;N;LATIN SMALL LETTER L CEDILLA;;013B;;013B +013D;LATIN CAPITAL LETTER L WITH CARON;Lu;0;L;004C 030C;;;;N;LATIN CAPITAL LETTER L HACEK;;;013E; +013E;LATIN SMALL LETTER L WITH CARON;Ll;0;L;006C 030C;;;;N;LATIN SMALL LETTER L HACEK;;013D;;013D +013F;LATIN CAPITAL LETTER L WITH MIDDLE DOT;Lu;0;L;<compat> 004C 00B7;;;;N;;;;0140; +0140;LATIN SMALL LETTER L WITH MIDDLE DOT;Ll;0;L;<compat> 006C 00B7;;;;N;;;013F;;013F +0141;LATIN CAPITAL LETTER L WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER L SLASH;;;0142; +0142;LATIN SMALL LETTER L WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER L SLASH;;0141;;0141 +0143;LATIN CAPITAL LETTER N WITH ACUTE;Lu;0;L;004E 0301;;;;N;LATIN CAPITAL LETTER N ACUTE;;;0144; +0144;LATIN SMALL LETTER N WITH ACUTE;Ll;0;L;006E 0301;;;;N;LATIN SMALL LETTER N ACUTE;;0143;;0143 +0145;LATIN CAPITAL LETTER N WITH CEDILLA;Lu;0;L;004E 0327;;;;N;LATIN CAPITAL LETTER N CEDILLA;;;0146; +0146;LATIN SMALL LETTER N WITH CEDILLA;Ll;0;L;006E 0327;;;;N;LATIN SMALL LETTER N CEDILLA;;0145;;0145 +0147;LATIN CAPITAL LETTER N WITH CARON;Lu;0;L;004E 030C;;;;N;LATIN CAPITAL LETTER N HACEK;;;0148; +0148;LATIN SMALL LETTER N WITH CARON;Ll;0;L;006E 030C;;;;N;LATIN SMALL LETTER N HACEK;;0147;;0147 +0149;LATIN SMALL LETTER N PRECEDED BY APOSTROPHE;Ll;0;L;<compat> 02BC 006E;;;;N;LATIN SMALL LETTER APOSTROPHE N;;;; +014A;LATIN CAPITAL LETTER ENG;Lu;0;L;;;;;N;;Sami;;014B; +014B;LATIN SMALL LETTER ENG;Ll;0;L;;;;;N;;Sami;014A;;014A +014C;LATIN CAPITAL LETTER O WITH MACRON;Lu;0;L;004F 0304;;;;N;LATIN CAPITAL LETTER O MACRON;;;014D; +014D;LATIN SMALL LETTER O WITH MACRON;Ll;0;L;006F 0304;;;;N;LATIN SMALL LETTER O MACRON;;014C;;014C +014E;LATIN CAPITAL LETTER O WITH BREVE;Lu;0;L;004F 0306;;;;N;LATIN CAPITAL LETTER O BREVE;;;014F; +014F;LATIN SMALL LETTER O WITH BREVE;Ll;0;L;006F 0306;;;;N;LATIN SMALL LETTER O BREVE;;014E;;014E +0150;LATIN CAPITAL LETTER O WITH DOUBLE ACUTE;Lu;0;L;004F 030B;;;;N;LATIN CAPITAL LETTER O DOUBLE ACUTE;;;0151; +0151;LATIN SMALL LETTER O WITH DOUBLE ACUTE;Ll;0;L;006F 030B;;;;N;LATIN SMALL LETTER O DOUBLE ACUTE;;0150;;0150 +0152;LATIN CAPITAL LIGATURE OE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER O E;;;0153; +0153;LATIN SMALL LIGATURE OE;Ll;0;L;;;;;N;LATIN SMALL LETTER O E;;0152;;0152 +0154;LATIN CAPITAL LETTER R WITH ACUTE;Lu;0;L;0052 0301;;;;N;LATIN CAPITAL LETTER R ACUTE;;;0155; +0155;LATIN SMALL LETTER R WITH ACUTE;Ll;0;L;0072 0301;;;;N;LATIN SMALL LETTER R ACUTE;;0154;;0154 +0156;LATIN CAPITAL LETTER R WITH CEDILLA;Lu;0;L;0052 0327;;;;N;LATIN CAPITAL LETTER R CEDILLA;;;0157; +0157;LATIN SMALL LETTER R WITH CEDILLA;Ll;0;L;0072 0327;;;;N;LATIN SMALL LETTER R CEDILLA;;0156;;0156 +0158;LATIN CAPITAL LETTER R WITH CARON;Lu;0;L;0052 030C;;;;N;LATIN CAPITAL LETTER R HACEK;;;0159; +0159;LATIN SMALL LETTER R WITH CARON;Ll;0;L;0072 030C;;;;N;LATIN SMALL LETTER R HACEK;;0158;;0158 +015A;LATIN CAPITAL LETTER S WITH ACUTE;Lu;0;L;0053 0301;;;;N;LATIN CAPITAL LETTER S ACUTE;;;015B; +015B;LATIN SMALL LETTER S WITH ACUTE;Ll;0;L;0073 0301;;;;N;LATIN SMALL LETTER S ACUTE;;015A;;015A +015C;LATIN CAPITAL LETTER S WITH CIRCUMFLEX;Lu;0;L;0053 0302;;;;N;LATIN CAPITAL LETTER S CIRCUMFLEX;;;015D; +015D;LATIN SMALL LETTER S WITH CIRCUMFLEX;Ll;0;L;0073 0302;;;;N;LATIN SMALL LETTER S CIRCUMFLEX;;015C;;015C +015E;LATIN CAPITAL LETTER S WITH CEDILLA;Lu;0;L;0053 0327;;;;N;LATIN CAPITAL LETTER S CEDILLA;*;;015F; +015F;LATIN SMALL LETTER S WITH CEDILLA;Ll;0;L;0073 0327;;;;N;LATIN SMALL LETTER S CEDILLA;*;015E;;015E +0160;LATIN CAPITAL LETTER S WITH CARON;Lu;0;L;0053 030C;;;;N;LATIN CAPITAL LETTER S HACEK;;;0161; +0161;LATIN SMALL LETTER S WITH CARON;Ll;0;L;0073 030C;;;;N;LATIN SMALL LETTER S HACEK;;0160;;0160 +0162;LATIN CAPITAL LETTER T WITH CEDILLA;Lu;0;L;0054 0327;;;;N;LATIN CAPITAL LETTER T CEDILLA;*;;0163; +0163;LATIN SMALL LETTER T WITH CEDILLA;Ll;0;L;0074 0327;;;;N;LATIN SMALL LETTER T CEDILLA;*;0162;;0162 +0164;LATIN CAPITAL LETTER T WITH CARON;Lu;0;L;0054 030C;;;;N;LATIN CAPITAL LETTER T HACEK;;;0165; +0165;LATIN SMALL LETTER T WITH CARON;Ll;0;L;0074 030C;;;;N;LATIN SMALL LETTER T HACEK;;0164;;0164 +0166;LATIN CAPITAL LETTER T WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER T BAR;;;0167; +0167;LATIN SMALL LETTER T WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER T BAR;;0166;;0166 +0168;LATIN CAPITAL LETTER U WITH TILDE;Lu;0;L;0055 0303;;;;N;LATIN CAPITAL LETTER U TILDE;;;0169; +0169;LATIN SMALL LETTER U WITH TILDE;Ll;0;L;0075 0303;;;;N;LATIN SMALL LETTER U TILDE;;0168;;0168 +016A;LATIN CAPITAL LETTER U WITH MACRON;Lu;0;L;0055 0304;;;;N;LATIN CAPITAL LETTER U MACRON;;;016B; +016B;LATIN SMALL LETTER U WITH MACRON;Ll;0;L;0075 0304;;;;N;LATIN SMALL LETTER U MACRON;;016A;;016A +016C;LATIN CAPITAL LETTER U WITH BREVE;Lu;0;L;0055 0306;;;;N;LATIN CAPITAL LETTER U BREVE;;;016D; +016D;LATIN SMALL LETTER U WITH BREVE;Ll;0;L;0075 0306;;;;N;LATIN SMALL LETTER U BREVE;;016C;;016C +016E;LATIN CAPITAL LETTER U WITH RING ABOVE;Lu;0;L;0055 030A;;;;N;LATIN CAPITAL LETTER U RING;;;016F; +016F;LATIN SMALL LETTER U WITH RING ABOVE;Ll;0;L;0075 030A;;;;N;LATIN SMALL LETTER U RING;;016E;;016E +0170;LATIN CAPITAL LETTER U WITH DOUBLE ACUTE;Lu;0;L;0055 030B;;;;N;LATIN CAPITAL LETTER U DOUBLE ACUTE;;;0171; +0171;LATIN SMALL LETTER U WITH DOUBLE ACUTE;Ll;0;L;0075 030B;;;;N;LATIN SMALL LETTER U DOUBLE ACUTE;;0170;;0170 +0172;LATIN CAPITAL LETTER U WITH OGONEK;Lu;0;L;0055 0328;;;;N;LATIN CAPITAL LETTER U OGONEK;;;0173; +0173;LATIN SMALL LETTER U WITH OGONEK;Ll;0;L;0075 0328;;;;N;LATIN SMALL LETTER U OGONEK;;0172;;0172 +0174;LATIN CAPITAL LETTER W WITH CIRCUMFLEX;Lu;0;L;0057 0302;;;;N;LATIN CAPITAL LETTER W CIRCUMFLEX;;;0175; +0175;LATIN SMALL LETTER W WITH CIRCUMFLEX;Ll;0;L;0077 0302;;;;N;LATIN SMALL LETTER W CIRCUMFLEX;;0174;;0174 +0176;LATIN CAPITAL LETTER Y WITH CIRCUMFLEX;Lu;0;L;0059 0302;;;;N;LATIN CAPITAL LETTER Y CIRCUMFLEX;;;0177; +0177;LATIN SMALL LETTER Y WITH CIRCUMFLEX;Ll;0;L;0079 0302;;;;N;LATIN SMALL LETTER Y CIRCUMFLEX;;0176;;0176 +0178;LATIN CAPITAL LETTER Y WITH DIAERESIS;Lu;0;L;0059 0308;;;;N;LATIN CAPITAL LETTER Y DIAERESIS;;;00FF; +0179;LATIN CAPITAL LETTER Z WITH ACUTE;Lu;0;L;005A 0301;;;;N;LATIN CAPITAL LETTER Z ACUTE;;;017A; +017A;LATIN SMALL LETTER Z WITH ACUTE;Ll;0;L;007A 0301;;;;N;LATIN SMALL LETTER Z ACUTE;;0179;;0179 +017B;LATIN CAPITAL LETTER Z WITH DOT ABOVE;Lu;0;L;005A 0307;;;;N;LATIN CAPITAL LETTER Z DOT;;;017C; +017C;LATIN SMALL LETTER Z WITH DOT ABOVE;Ll;0;L;007A 0307;;;;N;LATIN SMALL LETTER Z DOT;;017B;;017B +017D;LATIN CAPITAL LETTER Z WITH CARON;Lu;0;L;005A 030C;;;;N;LATIN CAPITAL LETTER Z HACEK;;;017E; +017E;LATIN SMALL LETTER Z WITH CARON;Ll;0;L;007A 030C;;;;N;LATIN SMALL LETTER Z HACEK;;017D;;017D +017F;LATIN SMALL LETTER LONG S;Ll;0;L;<compat> 0073;;;;N;;;0053;;0053 +0180;LATIN SMALL LETTER B WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER B BAR;;;; +0181;LATIN CAPITAL LETTER B WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER B HOOK;;;0253; +0182;LATIN CAPITAL LETTER B WITH TOPBAR;Lu;0;L;;;;;N;LATIN CAPITAL LETTER B TOPBAR;;;0183; +0183;LATIN SMALL LETTER B WITH TOPBAR;Ll;0;L;;;;;N;LATIN SMALL LETTER B TOPBAR;;0182;;0182 +0184;LATIN CAPITAL LETTER TONE SIX;Lu;0;L;;;;;N;;;;0185; +0185;LATIN SMALL LETTER TONE SIX;Ll;0;L;;;;;N;;;0184;;0184 +0186;LATIN CAPITAL LETTER OPEN O;Lu;0;L;;;;;N;;;;0254; +0187;LATIN CAPITAL LETTER C WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER C HOOK;;;0188; +0188;LATIN SMALL LETTER C WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER C HOOK;;0187;;0187 +0189;LATIN CAPITAL LETTER AFRICAN D;Lu;0;L;;;;;N;;*;;0256; +018A;LATIN CAPITAL LETTER D WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER D HOOK;;;0257; +018B;LATIN CAPITAL LETTER D WITH TOPBAR;Lu;0;L;;;;;N;LATIN CAPITAL LETTER D TOPBAR;;;018C; +018C;LATIN SMALL LETTER D WITH TOPBAR;Ll;0;L;;;;;N;LATIN SMALL LETTER D TOPBAR;;018B;;018B +018D;LATIN SMALL LETTER TURNED DELTA;Ll;0;L;;;;;N;;;;; +018E;LATIN CAPITAL LETTER REVERSED E;Lu;0;L;;;;;N;LATIN CAPITAL LETTER TURNED E;;;01DD; +018F;LATIN CAPITAL LETTER SCHWA;Lu;0;L;;;;;N;;;;0259; +0190;LATIN CAPITAL LETTER OPEN E;Lu;0;L;;;;;N;LATIN CAPITAL LETTER EPSILON;;;025B; +0191;LATIN CAPITAL LETTER F WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER F HOOK;;;0192; +0192;LATIN SMALL LETTER F WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER SCRIPT F;;0191;;0191 +0193;LATIN CAPITAL LETTER G WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER G HOOK;;;0260; +0194;LATIN CAPITAL LETTER GAMMA;Lu;0;L;;;;;N;;;;0263; +0195;LATIN SMALL LETTER HV;Ll;0;L;;;;;N;LATIN SMALL LETTER H V;hwair;01F6;;01F6 +0196;LATIN CAPITAL LETTER IOTA;Lu;0;L;;;;;N;;;;0269; +0197;LATIN CAPITAL LETTER I WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER BARRED I;;;0268; +0198;LATIN CAPITAL LETTER K WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER K HOOK;;;0199; +0199;LATIN SMALL LETTER K WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER K HOOK;;0198;;0198 +019A;LATIN SMALL LETTER L WITH BAR;Ll;0;L;;;;;N;LATIN SMALL LETTER BARRED L;;;; +019B;LATIN SMALL LETTER LAMBDA WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER BARRED LAMBDA;;;; +019C;LATIN CAPITAL LETTER TURNED M;Lu;0;L;;;;;N;;;;026F; +019D;LATIN CAPITAL LETTER N WITH LEFT HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER N HOOK;;;0272; +019E;LATIN SMALL LETTER N WITH LONG RIGHT LEG;Ll;0;L;;;;;N;;;0220;;0220 +019F;LATIN CAPITAL LETTER O WITH MIDDLE TILDE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER BARRED O;*;;0275; +01A0;LATIN CAPITAL LETTER O WITH HORN;Lu;0;L;004F 031B;;;;N;LATIN CAPITAL LETTER O HORN;;;01A1; +01A1;LATIN SMALL LETTER O WITH HORN;Ll;0;L;006F 031B;;;;N;LATIN SMALL LETTER O HORN;;01A0;;01A0 +01A2;LATIN CAPITAL LETTER OI;Lu;0;L;;;;;N;LATIN CAPITAL LETTER O I;gha;;01A3; +01A3;LATIN SMALL LETTER OI;Ll;0;L;;;;;N;LATIN SMALL LETTER O I;gha;01A2;;01A2 +01A4;LATIN CAPITAL LETTER P WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER P HOOK;;;01A5; +01A5;LATIN SMALL LETTER P WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER P HOOK;;01A4;;01A4 +01A6;LATIN LETTER YR;Lu;0;L;;;;;N;LATIN LETTER Y R;*;;0280; +01A7;LATIN CAPITAL LETTER TONE TWO;Lu;0;L;;;;;N;;;;01A8; +01A8;LATIN SMALL LETTER TONE TWO;Ll;0;L;;;;;N;;;01A7;;01A7 +01A9;LATIN CAPITAL LETTER ESH;Lu;0;L;;;;;N;;;;0283; +01AA;LATIN LETTER REVERSED ESH LOOP;Ll;0;L;;;;;N;;;;; +01AB;LATIN SMALL LETTER T WITH PALATAL HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER T PALATAL HOOK;;;; +01AC;LATIN CAPITAL LETTER T WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER T HOOK;;;01AD; +01AD;LATIN SMALL LETTER T WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER T HOOK;;01AC;;01AC +01AE;LATIN CAPITAL LETTER T WITH RETROFLEX HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER T RETROFLEX HOOK;;;0288; +01AF;LATIN CAPITAL LETTER U WITH HORN;Lu;0;L;0055 031B;;;;N;LATIN CAPITAL LETTER U HORN;;;01B0; +01B0;LATIN SMALL LETTER U WITH HORN;Ll;0;L;0075 031B;;;;N;LATIN SMALL LETTER U HORN;;01AF;;01AF +01B1;LATIN CAPITAL LETTER UPSILON;Lu;0;L;;;;;N;;;;028A; +01B2;LATIN CAPITAL LETTER V WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER SCRIPT V;;;028B; +01B3;LATIN CAPITAL LETTER Y WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER Y HOOK;;;01B4; +01B4;LATIN SMALL LETTER Y WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER Y HOOK;;01B3;;01B3 +01B5;LATIN CAPITAL LETTER Z WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER Z BAR;;;01B6; +01B6;LATIN SMALL LETTER Z WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER Z BAR;;01B5;;01B5 +01B7;LATIN CAPITAL LETTER EZH;Lu;0;L;;;;;N;LATIN CAPITAL LETTER YOGH;;;0292; +01B8;LATIN CAPITAL LETTER EZH REVERSED;Lu;0;L;;;;;N;LATIN CAPITAL LETTER REVERSED YOGH;;;01B9; +01B9;LATIN SMALL LETTER EZH REVERSED;Ll;0;L;;;;;N;LATIN SMALL LETTER REVERSED YOGH;;01B8;;01B8 +01BA;LATIN SMALL LETTER EZH WITH TAIL;Ll;0;L;;;;;N;LATIN SMALL LETTER YOGH WITH TAIL;;;; +01BB;LATIN LETTER TWO WITH STROKE;Lo;0;L;;;;;N;LATIN LETTER TWO BAR;;;; +01BC;LATIN CAPITAL LETTER TONE FIVE;Lu;0;L;;;;;N;;;;01BD; +01BD;LATIN SMALL LETTER TONE FIVE;Ll;0;L;;;;;N;;;01BC;;01BC +01BE;LATIN LETTER INVERTED GLOTTAL STOP WITH STROKE;Ll;0;L;;;;;N;LATIN LETTER INVERTED GLOTTAL STOP BAR;;;; +01BF;LATIN LETTER WYNN;Ll;0;L;;;;;N;;;01F7;;01F7 +01C0;LATIN LETTER DENTAL CLICK;Lo;0;L;;;;;N;LATIN LETTER PIPE;;;; +01C1;LATIN LETTER LATERAL CLICK;Lo;0;L;;;;;N;LATIN LETTER DOUBLE PIPE;;;; +01C2;LATIN LETTER ALVEOLAR CLICK;Lo;0;L;;;;;N;LATIN LETTER PIPE DOUBLE BAR;;;; +01C3;LATIN LETTER RETROFLEX CLICK;Lo;0;L;;;;;N;LATIN LETTER EXCLAMATION MARK;;;; +01C4;LATIN CAPITAL LETTER DZ WITH CARON;Lu;0;L;<compat> 0044 017D;;;;N;LATIN CAPITAL LETTER D Z HACEK;;;01C6;01C5 +01C5;LATIN CAPITAL LETTER D WITH SMALL LETTER Z WITH CARON;Lt;0;L;<compat> 0044 017E;;;;N;LATIN LETTER CAPITAL D SMALL Z HACEK;;01C4;01C6; +01C6;LATIN SMALL LETTER DZ WITH CARON;Ll;0;L;<compat> 0064 017E;;;;N;LATIN SMALL LETTER D Z HACEK;;01C4;;01C5 +01C7;LATIN CAPITAL LETTER LJ;Lu;0;L;<compat> 004C 004A;;;;N;LATIN CAPITAL LETTER L J;;;01C9;01C8 +01C8;LATIN CAPITAL LETTER L WITH SMALL LETTER J;Lt;0;L;<compat> 004C 006A;;;;N;LATIN LETTER CAPITAL L SMALL J;;01C7;01C9; +01C9;LATIN SMALL LETTER LJ;Ll;0;L;<compat> 006C 006A;;;;N;LATIN SMALL LETTER L J;;01C7;;01C8 +01CA;LATIN CAPITAL LETTER NJ;Lu;0;L;<compat> 004E 004A;;;;N;LATIN CAPITAL LETTER N J;;;01CC;01CB +01CB;LATIN CAPITAL LETTER N WITH SMALL LETTER J;Lt;0;L;<compat> 004E 006A;;;;N;LATIN LETTER CAPITAL N SMALL J;;01CA;01CC; +01CC;LATIN SMALL LETTER NJ;Ll;0;L;<compat> 006E 006A;;;;N;LATIN SMALL LETTER N J;;01CA;;01CB +01CD;LATIN CAPITAL LETTER A WITH CARON;Lu;0;L;0041 030C;;;;N;LATIN CAPITAL LETTER A HACEK;;;01CE; +01CE;LATIN SMALL LETTER A WITH CARON;Ll;0;L;0061 030C;;;;N;LATIN SMALL LETTER A HACEK;;01CD;;01CD +01CF;LATIN CAPITAL LETTER I WITH CARON;Lu;0;L;0049 030C;;;;N;LATIN CAPITAL LETTER I HACEK;;;01D0; +01D0;LATIN SMALL LETTER I WITH CARON;Ll;0;L;0069 030C;;;;N;LATIN SMALL LETTER I HACEK;;01CF;;01CF +01D1;LATIN CAPITAL LETTER O WITH CARON;Lu;0;L;004F 030C;;;;N;LATIN CAPITAL LETTER O HACEK;;;01D2; +01D2;LATIN SMALL LETTER O WITH CARON;Ll;0;L;006F 030C;;;;N;LATIN SMALL LETTER O HACEK;;01D1;;01D1 +01D3;LATIN CAPITAL LETTER U WITH CARON;Lu;0;L;0055 030C;;;;N;LATIN CAPITAL LETTER U HACEK;;;01D4; +01D4;LATIN SMALL LETTER U WITH CARON;Ll;0;L;0075 030C;;;;N;LATIN SMALL LETTER U HACEK;;01D3;;01D3 +01D5;LATIN CAPITAL LETTER U WITH DIAERESIS AND MACRON;Lu;0;L;00DC 0304;;;;N;LATIN CAPITAL LETTER U DIAERESIS MACRON;;;01D6; +01D6;LATIN SMALL LETTER U WITH DIAERESIS AND MACRON;Ll;0;L;00FC 0304;;;;N;LATIN SMALL LETTER U DIAERESIS MACRON;;01D5;;01D5 +01D7;LATIN CAPITAL LETTER U WITH DIAERESIS AND ACUTE;Lu;0;L;00DC 0301;;;;N;LATIN CAPITAL LETTER U DIAERESIS ACUTE;;;01D8; +01D8;LATIN SMALL LETTER U WITH DIAERESIS AND ACUTE;Ll;0;L;00FC 0301;;;;N;LATIN SMALL LETTER U DIAERESIS ACUTE;;01D7;;01D7 +01D9;LATIN CAPITAL LETTER U WITH DIAERESIS AND CARON;Lu;0;L;00DC 030C;;;;N;LATIN CAPITAL LETTER U DIAERESIS HACEK;;;01DA; +01DA;LATIN SMALL LETTER U WITH DIAERESIS AND CARON;Ll;0;L;00FC 030C;;;;N;LATIN SMALL LETTER U DIAERESIS HACEK;;01D9;;01D9 +01DB;LATIN CAPITAL LETTER U WITH DIAERESIS AND GRAVE;Lu;0;L;00DC 0300;;;;N;LATIN CAPITAL LETTER U DIAERESIS GRAVE;;;01DC; +01DC;LATIN SMALL LETTER U WITH DIAERESIS AND GRAVE;Ll;0;L;00FC 0300;;;;N;LATIN SMALL LETTER U DIAERESIS GRAVE;;01DB;;01DB +01DD;LATIN SMALL LETTER TURNED E;Ll;0;L;;;;;N;;;018E;;018E +01DE;LATIN CAPITAL LETTER A WITH DIAERESIS AND MACRON;Lu;0;L;00C4 0304;;;;N;LATIN CAPITAL LETTER A DIAERESIS MACRON;;;01DF; +01DF;LATIN SMALL LETTER A WITH DIAERESIS AND MACRON;Ll;0;L;00E4 0304;;;;N;LATIN SMALL LETTER A DIAERESIS MACRON;;01DE;;01DE +01E0;LATIN CAPITAL LETTER A WITH DOT ABOVE AND MACRON;Lu;0;L;0226 0304;;;;N;LATIN CAPITAL LETTER A DOT MACRON;;;01E1; +01E1;LATIN SMALL LETTER A WITH DOT ABOVE AND MACRON;Ll;0;L;0227 0304;;;;N;LATIN SMALL LETTER A DOT MACRON;;01E0;;01E0 +01E2;LATIN CAPITAL LETTER AE WITH MACRON;Lu;0;L;00C6 0304;;;;N;LATIN CAPITAL LETTER A E MACRON;ash *;;01E3; +01E3;LATIN SMALL LETTER AE WITH MACRON;Ll;0;L;00E6 0304;;;;N;LATIN SMALL LETTER A E MACRON;ash *;01E2;;01E2 +01E4;LATIN CAPITAL LETTER G WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER G BAR;;;01E5; +01E5;LATIN SMALL LETTER G WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER G BAR;;01E4;;01E4 +01E6;LATIN CAPITAL LETTER G WITH CARON;Lu;0;L;0047 030C;;;;N;LATIN CAPITAL LETTER G HACEK;;;01E7; +01E7;LATIN SMALL LETTER G WITH CARON;Ll;0;L;0067 030C;;;;N;LATIN SMALL LETTER G HACEK;;01E6;;01E6 +01E8;LATIN CAPITAL LETTER K WITH CARON;Lu;0;L;004B 030C;;;;N;LATIN CAPITAL LETTER K HACEK;;;01E9; +01E9;LATIN SMALL LETTER K WITH CARON;Ll;0;L;006B 030C;;;;N;LATIN SMALL LETTER K HACEK;;01E8;;01E8 +01EA;LATIN CAPITAL LETTER O WITH OGONEK;Lu;0;L;004F 0328;;;;N;LATIN CAPITAL LETTER O OGONEK;;;01EB; +01EB;LATIN SMALL LETTER O WITH OGONEK;Ll;0;L;006F 0328;;;;N;LATIN SMALL LETTER O OGONEK;;01EA;;01EA +01EC;LATIN CAPITAL LETTER O WITH OGONEK AND MACRON;Lu;0;L;01EA 0304;;;;N;LATIN CAPITAL LETTER O OGONEK MACRON;;;01ED; +01ED;LATIN SMALL LETTER O WITH OGONEK AND MACRON;Ll;0;L;01EB 0304;;;;N;LATIN SMALL LETTER O OGONEK MACRON;;01EC;;01EC +01EE;LATIN CAPITAL LETTER EZH WITH CARON;Lu;0;L;01B7 030C;;;;N;LATIN CAPITAL LETTER YOGH HACEK;;;01EF; +01EF;LATIN SMALL LETTER EZH WITH CARON;Ll;0;L;0292 030C;;;;N;LATIN SMALL LETTER YOGH HACEK;;01EE;;01EE +01F0;LATIN SMALL LETTER J WITH CARON;Ll;0;L;006A 030C;;;;N;LATIN SMALL LETTER J HACEK;;;; +01F1;LATIN CAPITAL LETTER DZ;Lu;0;L;<compat> 0044 005A;;;;N;;;;01F3;01F2 +01F2;LATIN CAPITAL LETTER D WITH SMALL LETTER Z;Lt;0;L;<compat> 0044 007A;;;;N;;;01F1;01F3; +01F3;LATIN SMALL LETTER DZ;Ll;0;L;<compat> 0064 007A;;;;N;;;01F1;;01F2 +01F4;LATIN CAPITAL LETTER G WITH ACUTE;Lu;0;L;0047 0301;;;;N;;;;01F5; +01F5;LATIN SMALL LETTER G WITH ACUTE;Ll;0;L;0067 0301;;;;N;;;01F4;;01F4 +01F6;LATIN CAPITAL LETTER HWAIR;Lu;0;L;;;;;N;;;;0195; +01F7;LATIN CAPITAL LETTER WYNN;Lu;0;L;;;;;N;;;;01BF; +01F8;LATIN CAPITAL LETTER N WITH GRAVE;Lu;0;L;004E 0300;;;;N;;;;01F9; +01F9;LATIN SMALL LETTER N WITH GRAVE;Ll;0;L;006E 0300;;;;N;;;01F8;;01F8 +01FA;LATIN CAPITAL LETTER A WITH RING ABOVE AND ACUTE;Lu;0;L;00C5 0301;;;;N;;;;01FB; +01FB;LATIN SMALL LETTER A WITH RING ABOVE AND ACUTE;Ll;0;L;00E5 0301;;;;N;;;01FA;;01FA +01FC;LATIN CAPITAL LETTER AE WITH ACUTE;Lu;0;L;00C6 0301;;;;N;;ash *;;01FD; +01FD;LATIN SMALL LETTER AE WITH ACUTE;Ll;0;L;00E6 0301;;;;N;;ash *;01FC;;01FC +01FE;LATIN CAPITAL LETTER O WITH STROKE AND ACUTE;Lu;0;L;00D8 0301;;;;N;;;;01FF; +01FF;LATIN SMALL LETTER O WITH STROKE AND ACUTE;Ll;0;L;00F8 0301;;;;N;;;01FE;;01FE +0200;LATIN CAPITAL LETTER A WITH DOUBLE GRAVE;Lu;0;L;0041 030F;;;;N;;;;0201; +0201;LATIN SMALL LETTER A WITH DOUBLE GRAVE;Ll;0;L;0061 030F;;;;N;;;0200;;0200 +0202;LATIN CAPITAL LETTER A WITH INVERTED BREVE;Lu;0;L;0041 0311;;;;N;;;;0203; +0203;LATIN SMALL LETTER A WITH INVERTED BREVE;Ll;0;L;0061 0311;;;;N;;;0202;;0202 +0204;LATIN CAPITAL LETTER E WITH DOUBLE GRAVE;Lu;0;L;0045 030F;;;;N;;;;0205; +0205;LATIN SMALL LETTER E WITH DOUBLE GRAVE;Ll;0;L;0065 030F;;;;N;;;0204;;0204 +0206;LATIN CAPITAL LETTER E WITH INVERTED BREVE;Lu;0;L;0045 0311;;;;N;;;;0207; +0207;LATIN SMALL LETTER E WITH INVERTED BREVE;Ll;0;L;0065 0311;;;;N;;;0206;;0206 +0208;LATIN CAPITAL LETTER I WITH DOUBLE GRAVE;Lu;0;L;0049 030F;;;;N;;;;0209; +0209;LATIN SMALL LETTER I WITH DOUBLE GRAVE;Ll;0;L;0069 030F;;;;N;;;0208;;0208 +020A;LATIN CAPITAL LETTER I WITH INVERTED BREVE;Lu;0;L;0049 0311;;;;N;;;;020B; +020B;LATIN SMALL LETTER I WITH INVERTED BREVE;Ll;0;L;0069 0311;;;;N;;;020A;;020A +020C;LATIN CAPITAL LETTER O WITH DOUBLE GRAVE;Lu;0;L;004F 030F;;;;N;;;;020D; +020D;LATIN SMALL LETTER O WITH DOUBLE GRAVE;Ll;0;L;006F 030F;;;;N;;;020C;;020C +020E;LATIN CAPITAL LETTER O WITH INVERTED BREVE;Lu;0;L;004F 0311;;;;N;;;;020F; +020F;LATIN SMALL LETTER O WITH INVERTED BREVE;Ll;0;L;006F 0311;;;;N;;;020E;;020E +0210;LATIN CAPITAL LETTER R WITH DOUBLE GRAVE;Lu;0;L;0052 030F;;;;N;;;;0211; +0211;LATIN SMALL LETTER R WITH DOUBLE GRAVE;Ll;0;L;0072 030F;;;;N;;;0210;;0210 +0212;LATIN CAPITAL LETTER R WITH INVERTED BREVE;Lu;0;L;0052 0311;;;;N;;;;0213; +0213;LATIN SMALL LETTER R WITH INVERTED BREVE;Ll;0;L;0072 0311;;;;N;;;0212;;0212 +0214;LATIN CAPITAL LETTER U WITH DOUBLE GRAVE;Lu;0;L;0055 030F;;;;N;;;;0215; +0215;LATIN SMALL LETTER U WITH DOUBLE GRAVE;Ll;0;L;0075 030F;;;;N;;;0214;;0214 +0216;LATIN CAPITAL LETTER U WITH INVERTED BREVE;Lu;0;L;0055 0311;;;;N;;;;0217; +0217;LATIN SMALL LETTER U WITH INVERTED BREVE;Ll;0;L;0075 0311;;;;N;;;0216;;0216 +0218;LATIN CAPITAL LETTER S WITH COMMA BELOW;Lu;0;L;0053 0326;;;;N;;*;;0219; +0219;LATIN SMALL LETTER S WITH COMMA BELOW;Ll;0;L;0073 0326;;;;N;;*;0218;;0218 +021A;LATIN CAPITAL LETTER T WITH COMMA BELOW;Lu;0;L;0054 0326;;;;N;;*;;021B; +021B;LATIN SMALL LETTER T WITH COMMA BELOW;Ll;0;L;0074 0326;;;;N;;*;021A;;021A +021C;LATIN CAPITAL LETTER YOGH;Lu;0;L;;;;;N;;;;021D; +021D;LATIN SMALL LETTER YOGH;Ll;0;L;;;;;N;;;021C;;021C +021E;LATIN CAPITAL LETTER H WITH CARON;Lu;0;L;0048 030C;;;;N;;;;021F; +021F;LATIN SMALL LETTER H WITH CARON;Ll;0;L;0068 030C;;;;N;;;021E;;021E +0220;LATIN CAPITAL LETTER N WITH LONG RIGHT LEG;Lu;0;L;;;;;N;;;;019E; +0222;LATIN CAPITAL LETTER OU;Lu;0;L;;;;;N;;;;0223; +0223;LATIN SMALL LETTER OU;Ll;0;L;;;;;N;;;0222;;0222 +0224;LATIN CAPITAL LETTER Z WITH HOOK;Lu;0;L;;;;;N;;;;0225; +0225;LATIN SMALL LETTER Z WITH HOOK;Ll;0;L;;;;;N;;;0224;;0224 +0226;LATIN CAPITAL LETTER A WITH DOT ABOVE;Lu;0;L;0041 0307;;;;N;;;;0227; +0227;LATIN SMALL LETTER A WITH DOT ABOVE;Ll;0;L;0061 0307;;;;N;;;0226;;0226 +0228;LATIN CAPITAL LETTER E WITH CEDILLA;Lu;0;L;0045 0327;;;;N;;;;0229; +0229;LATIN SMALL LETTER E WITH CEDILLA;Ll;0;L;0065 0327;;;;N;;;0228;;0228 +022A;LATIN CAPITAL LETTER O WITH DIAERESIS AND MACRON;Lu;0;L;00D6 0304;;;;N;;;;022B; +022B;LATIN SMALL LETTER O WITH DIAERESIS AND MACRON;Ll;0;L;00F6 0304;;;;N;;;022A;;022A +022C;LATIN CAPITAL LETTER O WITH TILDE AND MACRON;Lu;0;L;00D5 0304;;;;N;;;;022D; +022D;LATIN SMALL LETTER O WITH TILDE AND MACRON;Ll;0;L;00F5 0304;;;;N;;;022C;;022C +022E;LATIN CAPITAL LETTER O WITH DOT ABOVE;Lu;0;L;004F 0307;;;;N;;;;022F; +022F;LATIN SMALL LETTER O WITH DOT ABOVE;Ll;0;L;006F 0307;;;;N;;;022E;;022E +0230;LATIN CAPITAL LETTER O WITH DOT ABOVE AND MACRON;Lu;0;L;022E 0304;;;;N;;;;0231; +0231;LATIN SMALL LETTER O WITH DOT ABOVE AND MACRON;Ll;0;L;022F 0304;;;;N;;;0230;;0230 +0232;LATIN CAPITAL LETTER Y WITH MACRON;Lu;0;L;0059 0304;;;;N;;;;0233; +0233;LATIN SMALL LETTER Y WITH MACRON;Ll;0;L;0079 0304;;;;N;;;0232;;0232 +0250;LATIN SMALL LETTER TURNED A;Ll;0;L;;;;;N;;;;; +0251;LATIN SMALL LETTER ALPHA;Ll;0;L;;;;;N;LATIN SMALL LETTER SCRIPT A;;;; +0252;LATIN SMALL LETTER TURNED ALPHA;Ll;0;L;;;;;N;LATIN SMALL LETTER TURNED SCRIPT A;;;; +0253;LATIN SMALL LETTER B WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER B HOOK;;0181;;0181 +0254;LATIN SMALL LETTER OPEN O;Ll;0;L;;;;;N;;;0186;;0186 +0255;LATIN SMALL LETTER C WITH CURL;Ll;0;L;;;;;N;LATIN SMALL LETTER C CURL;;;; +0256;LATIN SMALL LETTER D WITH TAIL;Ll;0;L;;;;;N;LATIN SMALL LETTER D RETROFLEX HOOK;;0189;;0189 +0257;LATIN SMALL LETTER D WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER D HOOK;;018A;;018A +0258;LATIN SMALL LETTER REVERSED E;Ll;0;L;;;;;N;;;;; +0259;LATIN SMALL LETTER SCHWA;Ll;0;L;;;;;N;;;018F;;018F +025A;LATIN SMALL LETTER SCHWA WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER SCHWA HOOK;;;; +025B;LATIN SMALL LETTER OPEN E;Ll;0;L;;;;;N;LATIN SMALL LETTER EPSILON;;0190;;0190 +025C;LATIN SMALL LETTER REVERSED OPEN E;Ll;0;L;;;;;N;LATIN SMALL LETTER REVERSED EPSILON;;;; +025D;LATIN SMALL LETTER REVERSED OPEN E WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER REVERSED EPSILON HOOK;;;; +025E;LATIN SMALL LETTER CLOSED REVERSED OPEN E;Ll;0;L;;;;;N;LATIN SMALL LETTER CLOSED REVERSED EPSILON;;;; +025F;LATIN SMALL LETTER DOTLESS J WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER DOTLESS J BAR;;;; +0260;LATIN SMALL LETTER G WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER G HOOK;;0193;;0193 +0261;LATIN SMALL LETTER SCRIPT G;Ll;0;L;;;;;N;;;;; +0262;LATIN LETTER SMALL CAPITAL G;Ll;0;L;;;;;N;;;;; +0263;LATIN SMALL LETTER GAMMA;Ll;0;L;;;;;N;;;0194;;0194 +0264;LATIN SMALL LETTER RAMS HORN;Ll;0;L;;;;;N;LATIN SMALL LETTER BABY GAMMA;;;; +0265;LATIN SMALL LETTER TURNED H;Ll;0;L;;;;;N;;;;; +0266;LATIN SMALL LETTER H WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER H HOOK;;;; +0267;LATIN SMALL LETTER HENG WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER HENG HOOK;;;; +0268;LATIN SMALL LETTER I WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER BARRED I;;0197;;0197 +0269;LATIN SMALL LETTER IOTA;Ll;0;L;;;;;N;;;0196;;0196 +026A;LATIN LETTER SMALL CAPITAL I;Ll;0;L;;;;;N;;;;; +026B;LATIN SMALL LETTER L WITH MIDDLE TILDE;Ll;0;L;;;;;N;;;;; +026C;LATIN SMALL LETTER L WITH BELT;Ll;0;L;;;;;N;LATIN SMALL LETTER L BELT;;;; +026D;LATIN SMALL LETTER L WITH RETROFLEX HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER L RETROFLEX HOOK;;;; +026E;LATIN SMALL LETTER LEZH;Ll;0;L;;;;;N;LATIN SMALL LETTER L YOGH;;;; +026F;LATIN SMALL LETTER TURNED M;Ll;0;L;;;;;N;;;019C;;019C +0270;LATIN SMALL LETTER TURNED M WITH LONG LEG;Ll;0;L;;;;;N;;;;; +0271;LATIN SMALL LETTER M WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER M HOOK;;;; +0272;LATIN SMALL LETTER N WITH LEFT HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER N HOOK;;019D;;019D +0273;LATIN SMALL LETTER N WITH RETROFLEX HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER N RETROFLEX HOOK;;;; +0274;LATIN LETTER SMALL CAPITAL N;Ll;0;L;;;;;N;;;;; +0275;LATIN SMALL LETTER BARRED O;Ll;0;L;;;;;N;;;019F;;019F +0276;LATIN LETTER SMALL CAPITAL OE;Ll;0;L;;;;;N;LATIN LETTER SMALL CAPITAL O E;;;; +0277;LATIN SMALL LETTER CLOSED OMEGA;Ll;0;L;;;;;N;;;;; +0278;LATIN SMALL LETTER PHI;Ll;0;L;;;;;N;;;;; +0279;LATIN SMALL LETTER TURNED R;Ll;0;L;;;;;N;;;;; +027A;LATIN SMALL LETTER TURNED R WITH LONG LEG;Ll;0;L;;;;;N;;;;; +027B;LATIN SMALL LETTER TURNED R WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER TURNED R HOOK;;;; +027C;LATIN SMALL LETTER R WITH LONG LEG;Ll;0;L;;;;;N;;;;; +027D;LATIN SMALL LETTER R WITH TAIL;Ll;0;L;;;;;N;LATIN SMALL LETTER R HOOK;;;; +027E;LATIN SMALL LETTER R WITH FISHHOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER FISHHOOK R;;;; +027F;LATIN SMALL LETTER REVERSED R WITH FISHHOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER REVERSED FISHHOOK R;;;; +0280;LATIN LETTER SMALL CAPITAL R;Ll;0;L;;;;;N;;*;01A6;;01A6 +0281;LATIN LETTER SMALL CAPITAL INVERTED R;Ll;0;L;;;;;N;;;;; +0282;LATIN SMALL LETTER S WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER S HOOK;;;; +0283;LATIN SMALL LETTER ESH;Ll;0;L;;;;;N;;;01A9;;01A9 +0284;LATIN SMALL LETTER DOTLESS J WITH STROKE AND HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER DOTLESS J BAR HOOK;;;; +0285;LATIN SMALL LETTER SQUAT REVERSED ESH;Ll;0;L;;;;;N;;;;; +0286;LATIN SMALL LETTER ESH WITH CURL;Ll;0;L;;;;;N;LATIN SMALL LETTER ESH CURL;;;; +0287;LATIN SMALL LETTER TURNED T;Ll;0;L;;;;;N;;;;; +0288;LATIN SMALL LETTER T WITH RETROFLEX HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER T RETROFLEX HOOK;;01AE;;01AE +0289;LATIN SMALL LETTER U BAR;Ll;0;L;;;;;N;;;;; +028A;LATIN SMALL LETTER UPSILON;Ll;0;L;;;;;N;;;01B1;;01B1 +028B;LATIN SMALL LETTER V WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER SCRIPT V;;01B2;;01B2 +028C;LATIN SMALL LETTER TURNED V;Ll;0;L;;;;;N;;;;; +028D;LATIN SMALL LETTER TURNED W;Ll;0;L;;;;;N;;;;; +028E;LATIN SMALL LETTER TURNED Y;Ll;0;L;;;;;N;;;;; +028F;LATIN LETTER SMALL CAPITAL Y;Ll;0;L;;;;;N;;;;; +0290;LATIN SMALL LETTER Z WITH RETROFLEX HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER Z RETROFLEX HOOK;;;; +0291;LATIN SMALL LETTER Z WITH CURL;Ll;0;L;;;;;N;LATIN SMALL LETTER Z CURL;;;; +0292;LATIN SMALL LETTER EZH;Ll;0;L;;;;;N;LATIN SMALL LETTER YOGH;;01B7;;01B7 +0293;LATIN SMALL LETTER EZH WITH CURL;Ll;0;L;;;;;N;LATIN SMALL LETTER YOGH CURL;;;; +0294;LATIN LETTER GLOTTAL STOP;Ll;0;L;;;;;N;;;;; +0295;LATIN LETTER PHARYNGEAL VOICED FRICATIVE;Ll;0;L;;;;;N;LATIN LETTER REVERSED GLOTTAL STOP;;;; +0296;LATIN LETTER INVERTED GLOTTAL STOP;Ll;0;L;;;;;N;;;;; +0297;LATIN LETTER STRETCHED C;Ll;0;L;;;;;N;;;;; +0298;LATIN LETTER BILABIAL CLICK;Ll;0;L;;;;;N;LATIN LETTER BULLSEYE;;;; +0299;LATIN LETTER SMALL CAPITAL B;Ll;0;L;;;;;N;;;;; +029A;LATIN SMALL LETTER CLOSED OPEN E;Ll;0;L;;;;;N;LATIN SMALL LETTER CLOSED EPSILON;;;; +029B;LATIN LETTER SMALL CAPITAL G WITH HOOK;Ll;0;L;;;;;N;LATIN LETTER SMALL CAPITAL G HOOK;;;; +029C;LATIN LETTER SMALL CAPITAL H;Ll;0;L;;;;;N;;;;; +029D;LATIN SMALL LETTER J WITH CROSSED-TAIL;Ll;0;L;;;;;N;LATIN SMALL LETTER CROSSED-TAIL J;;;; +029E;LATIN SMALL LETTER TURNED K;Ll;0;L;;;;;N;;;;; +029F;LATIN LETTER SMALL CAPITAL L;Ll;0;L;;;;;N;;;;; +02A0;LATIN SMALL LETTER Q WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER Q HOOK;;;; +02A1;LATIN LETTER GLOTTAL STOP WITH STROKE;Ll;0;L;;;;;N;LATIN LETTER GLOTTAL STOP BAR;;;; +02A2;LATIN LETTER REVERSED GLOTTAL STOP WITH STROKE;Ll;0;L;;;;;N;LATIN LETTER REVERSED GLOTTAL STOP BAR;;;; +02A3;LATIN SMALL LETTER DZ DIGRAPH;Ll;0;L;;;;;N;LATIN SMALL LETTER D Z;;;; +02A4;LATIN SMALL LETTER DEZH DIGRAPH;Ll;0;L;;;;;N;LATIN SMALL LETTER D YOGH;;;; +02A5;LATIN SMALL LETTER DZ DIGRAPH WITH CURL;Ll;0;L;;;;;N;LATIN SMALL LETTER D Z CURL;;;; +02A6;LATIN SMALL LETTER TS DIGRAPH;Ll;0;L;;;;;N;LATIN SMALL LETTER T S;;;; +02A7;LATIN SMALL LETTER TESH DIGRAPH;Ll;0;L;;;;;N;LATIN SMALL LETTER T ESH;;;; +02A8;LATIN SMALL LETTER TC DIGRAPH WITH CURL;Ll;0;L;;;;;N;LATIN SMALL LETTER T C CURL;;;; +02A9;LATIN SMALL LETTER FENG DIGRAPH;Ll;0;L;;;;;N;;;;; +02AA;LATIN SMALL LETTER LS DIGRAPH;Ll;0;L;;;;;N;;;;; +02AB;LATIN SMALL LETTER LZ DIGRAPH;Ll;0;L;;;;;N;;;;; +02AC;LATIN LETTER BILABIAL PERCUSSIVE;Ll;0;L;;;;;N;;;;; +02AD;LATIN LETTER BIDENTAL PERCUSSIVE;Ll;0;L;;;;;N;;;;; +02B0;MODIFIER LETTER SMALL H;Lm;0;L;<super> 0068;;;;N;;;;; +02B1;MODIFIER LETTER SMALL H WITH HOOK;Lm;0;L;<super> 0266;;;;N;MODIFIER LETTER SMALL H HOOK;;;; +02B2;MODIFIER LETTER SMALL J;Lm;0;L;<super> 006A;;;;N;;;;; +02B3;MODIFIER LETTER SMALL R;Lm;0;L;<super> 0072;;;;N;;;;; +02B4;MODIFIER LETTER SMALL TURNED R;Lm;0;L;<super> 0279;;;;N;;;;; +02B5;MODIFIER LETTER SMALL TURNED R WITH HOOK;Lm;0;L;<super> 027B;;;;N;MODIFIER LETTER SMALL TURNED R HOOK;;;; +02B6;MODIFIER LETTER SMALL CAPITAL INVERTED R;Lm;0;L;<super> 0281;;;;N;;;;; +02B7;MODIFIER LETTER SMALL W;Lm;0;L;<super> 0077;;;;N;;;;; +02B8;MODIFIER LETTER SMALL Y;Lm;0;L;<super> 0079;;;;N;;;;; +02B9;MODIFIER LETTER PRIME;Sk;0;ON;;;;;N;;;;; +02BA;MODIFIER LETTER DOUBLE PRIME;Sk;0;ON;;;;;N;;;;; +02BB;MODIFIER LETTER TURNED COMMA;Lm;0;L;;;;;N;;;;; +02BC;MODIFIER LETTER APOSTROPHE;Lm;0;L;;;;;N;;;;; +02BD;MODIFIER LETTER REVERSED COMMA;Lm;0;L;;;;;N;;;;; +02BE;MODIFIER LETTER RIGHT HALF RING;Lm;0;L;;;;;N;;;;; +02BF;MODIFIER LETTER LEFT HALF RING;Lm;0;L;;;;;N;;;;; +02C0;MODIFIER LETTER GLOTTAL STOP;Lm;0;L;;;;;N;;;;; +02C1;MODIFIER LETTER REVERSED GLOTTAL STOP;Lm;0;L;;;;;N;;;;; +02C2;MODIFIER LETTER LEFT ARROWHEAD;Sk;0;ON;;;;;N;;;;; +02C3;MODIFIER LETTER RIGHT ARROWHEAD;Sk;0;ON;;;;;N;;;;; +02C4;MODIFIER LETTER UP ARROWHEAD;Sk;0;ON;;;;;N;;;;; +02C5;MODIFIER LETTER DOWN ARROWHEAD;Sk;0;ON;;;;;N;;;;; +02C6;MODIFIER LETTER CIRCUMFLEX ACCENT;Sk;0;ON;;;;;N;MODIFIER LETTER CIRCUMFLEX;;;; +02C7;CARON;Sk;0;ON;;;;;N;MODIFIER LETTER HACEK;Mandarin Chinese third tone;;; +02C8;MODIFIER LETTER VERTICAL LINE;Sk;0;ON;;;;;N;;;;; +02C9;MODIFIER LETTER MACRON;Sk;0;ON;;;;;N;;Mandarin Chinese first tone;;; +02CA;MODIFIER LETTER ACUTE ACCENT;Sk;0;ON;;;;;N;MODIFIER LETTER ACUTE;Mandarin Chinese second tone;;; +02CB;MODIFIER LETTER GRAVE ACCENT;Sk;0;ON;;;;;N;MODIFIER LETTER GRAVE;Mandarin Chinese fourth tone;;; +02CC;MODIFIER LETTER LOW VERTICAL LINE;Sk;0;ON;;;;;N;;;;; +02CD;MODIFIER LETTER LOW MACRON;Sk;0;ON;;;;;N;;;;; +02CE;MODIFIER LETTER LOW GRAVE ACCENT;Sk;0;ON;;;;;N;MODIFIER LETTER LOW GRAVE;;;; +02CF;MODIFIER LETTER LOW ACUTE ACCENT;Sk;0;ON;;;;;N;MODIFIER LETTER LOW ACUTE;;;; +02D0;MODIFIER LETTER TRIANGULAR COLON;Lm;0;L;;;;;N;;;;; +02D1;MODIFIER LETTER HALF TRIANGULAR COLON;Lm;0;L;;;;;N;;;;; +02D2;MODIFIER LETTER CENTRED RIGHT HALF RING;Sk;0;ON;;;;;N;MODIFIER LETTER CENTERED RIGHT HALF RING;;;; +02D3;MODIFIER LETTER CENTRED LEFT HALF RING;Sk;0;ON;;;;;N;MODIFIER LETTER CENTERED LEFT HALF RING;;;; +02D4;MODIFIER LETTER UP TACK;Sk;0;ON;;;;;N;;;;; +02D5;MODIFIER LETTER DOWN TACK;Sk;0;ON;;;;;N;;;;; +02D6;MODIFIER LETTER PLUS SIGN;Sk;0;ON;;;;;N;;;;; +02D7;MODIFIER LETTER MINUS SIGN;Sk;0;ON;;;;;N;;;;; +02D8;BREVE;Sk;0;ON;<compat> 0020 0306;;;;N;SPACING BREVE;;;; +02D9;DOT ABOVE;Sk;0;ON;<compat> 0020 0307;;;;N;SPACING DOT ABOVE;Mandarin Chinese light tone;;; +02DA;RING ABOVE;Sk;0;ON;<compat> 0020 030A;;;;N;SPACING RING ABOVE;;;; +02DB;OGONEK;Sk;0;ON;<compat> 0020 0328;;;;N;SPACING OGONEK;;;; +02DC;SMALL TILDE;Sk;0;ON;<compat> 0020 0303;;;;N;SPACING TILDE;;;; +02DD;DOUBLE ACUTE ACCENT;Sk;0;ON;<compat> 0020 030B;;;;N;SPACING DOUBLE ACUTE;;;; +02DE;MODIFIER LETTER RHOTIC HOOK;Sk;0;ON;;;;;N;;;;; +02DF;MODIFIER LETTER CROSS ACCENT;Sk;0;ON;;;;;N;;;;; +02E0;MODIFIER LETTER SMALL GAMMA;Lm;0;L;<super> 0263;;;;N;;;;; +02E1;MODIFIER LETTER SMALL L;Lm;0;L;<super> 006C;;;;N;;;;; +02E2;MODIFIER LETTER SMALL S;Lm;0;L;<super> 0073;;;;N;;;;; +02E3;MODIFIER LETTER SMALL X;Lm;0;L;<super> 0078;;;;N;;;;; +02E4;MODIFIER LETTER SMALL REVERSED GLOTTAL STOP;Lm;0;L;<super> 0295;;;;N;;;;; +02E5;MODIFIER LETTER EXTRA-HIGH TONE BAR;Sk;0;ON;;;;;N;;;;; +02E6;MODIFIER LETTER HIGH TONE BAR;Sk;0;ON;;;;;N;;;;; +02E7;MODIFIER LETTER MID TONE BAR;Sk;0;ON;;;;;N;;;;; +02E8;MODIFIER LETTER LOW TONE BAR;Sk;0;ON;;;;;N;;;;; +02E9;MODIFIER LETTER EXTRA-LOW TONE BAR;Sk;0;ON;;;;;N;;;;; +02EA;MODIFIER LETTER YIN DEPARTING TONE MARK;Sk;0;ON;;;;;N;;;;; +02EB;MODIFIER LETTER YANG DEPARTING TONE MARK;Sk;0;ON;;;;;N;;;;; +02EC;MODIFIER LETTER VOICING;Sk;0;ON;;;;;N;;;;; +02ED;MODIFIER LETTER UNASPIRATED;Sk;0;ON;;;;;N;;;;; +02EE;MODIFIER LETTER DOUBLE APOSTROPHE;Lm;0;L;;;;;N;;;;; +0300;COMBINING GRAVE ACCENT;Mn;230;NSM;;;;;N;NON-SPACING GRAVE;Varia;;; +0301;COMBINING ACUTE ACCENT;Mn;230;NSM;;;;;N;NON-SPACING ACUTE;Oxia, Tonos;;; +0302;COMBINING CIRCUMFLEX ACCENT;Mn;230;NSM;;;;;N;NON-SPACING CIRCUMFLEX;;;; +0303;COMBINING TILDE;Mn;230;NSM;;;;;N;NON-SPACING TILDE;;;; +0304;COMBINING MACRON;Mn;230;NSM;;;;;N;NON-SPACING MACRON;;;; +0305;COMBINING OVERLINE;Mn;230;NSM;;;;;N;NON-SPACING OVERSCORE;;;; +0306;COMBINING BREVE;Mn;230;NSM;;;;;N;NON-SPACING BREVE;Vrachy;;; +0307;COMBINING DOT ABOVE;Mn;230;NSM;;;;;N;NON-SPACING DOT ABOVE;;;; +0308;COMBINING DIAERESIS;Mn;230;NSM;;;;;N;NON-SPACING DIAERESIS;Dialytika;;; +0309;COMBINING HOOK ABOVE;Mn;230;NSM;;;;;N;NON-SPACING HOOK ABOVE;;;; +030A;COMBINING RING ABOVE;Mn;230;NSM;;;;;N;NON-SPACING RING ABOVE;;;; +030B;COMBINING DOUBLE ACUTE ACCENT;Mn;230;NSM;;;;;N;NON-SPACING DOUBLE ACUTE;;;; +030C;COMBINING CARON;Mn;230;NSM;;;;;N;NON-SPACING HACEK;;;; +030D;COMBINING VERTICAL LINE ABOVE;Mn;230;NSM;;;;;N;NON-SPACING VERTICAL LINE ABOVE;;;; +030E;COMBINING DOUBLE VERTICAL LINE ABOVE;Mn;230;NSM;;;;;N;NON-SPACING DOUBLE VERTICAL LINE ABOVE;;;; +030F;COMBINING DOUBLE GRAVE ACCENT;Mn;230;NSM;;;;;N;NON-SPACING DOUBLE GRAVE;;;; +0310;COMBINING CANDRABINDU;Mn;230;NSM;;;;;N;NON-SPACING CANDRABINDU;;;; +0311;COMBINING INVERTED BREVE;Mn;230;NSM;;;;;N;NON-SPACING INVERTED BREVE;;;; +0312;COMBINING TURNED COMMA ABOVE;Mn;230;NSM;;;;;N;NON-SPACING TURNED COMMA ABOVE;;;; +0313;COMBINING COMMA ABOVE;Mn;230;NSM;;;;;N;NON-SPACING COMMA ABOVE;Psili;;; +0314;COMBINING REVERSED COMMA ABOVE;Mn;230;NSM;;;;;N;NON-SPACING REVERSED COMMA ABOVE;Dasia;;; +0315;COMBINING COMMA ABOVE RIGHT;Mn;232;NSM;;;;;N;NON-SPACING COMMA ABOVE RIGHT;;;; +0316;COMBINING GRAVE ACCENT BELOW;Mn;220;NSM;;;;;N;NON-SPACING GRAVE BELOW;;;; +0317;COMBINING ACUTE ACCENT BELOW;Mn;220;NSM;;;;;N;NON-SPACING ACUTE BELOW;;;; +0318;COMBINING LEFT TACK BELOW;Mn;220;NSM;;;;;N;NON-SPACING LEFT TACK BELOW;;;; +0319;COMBINING RIGHT TACK BELOW;Mn;220;NSM;;;;;N;NON-SPACING RIGHT TACK BELOW;;;; +031A;COMBINING LEFT ANGLE ABOVE;Mn;232;NSM;;;;;N;NON-SPACING LEFT ANGLE ABOVE;;;; +031B;COMBINING HORN;Mn;216;NSM;;;;;N;NON-SPACING HORN;;;; +031C;COMBINING LEFT HALF RING BELOW;Mn;220;NSM;;;;;N;NON-SPACING LEFT HALF RING BELOW;;;; +031D;COMBINING UP TACK BELOW;Mn;220;NSM;;;;;N;NON-SPACING UP TACK BELOW;;;; +031E;COMBINING DOWN TACK BELOW;Mn;220;NSM;;;;;N;NON-SPACING DOWN TACK BELOW;;;; +031F;COMBINING PLUS SIGN BELOW;Mn;220;NSM;;;;;N;NON-SPACING PLUS SIGN BELOW;;;; +0320;COMBINING MINUS SIGN BELOW;Mn;220;NSM;;;;;N;NON-SPACING MINUS SIGN BELOW;;;; +0321;COMBINING PALATALIZED HOOK BELOW;Mn;202;NSM;;;;;N;NON-SPACING PALATALIZED HOOK BELOW;;;; +0322;COMBINING RETROFLEX HOOK BELOW;Mn;202;NSM;;;;;N;NON-SPACING RETROFLEX HOOK BELOW;;;; +0323;COMBINING DOT BELOW;Mn;220;NSM;;;;;N;NON-SPACING DOT BELOW;;;; +0324;COMBINING DIAERESIS BELOW;Mn;220;NSM;;;;;N;NON-SPACING DOUBLE DOT BELOW;;;; +0325;COMBINING RING BELOW;Mn;220;NSM;;;;;N;NON-SPACING RING BELOW;;;; +0326;COMBINING COMMA BELOW;Mn;220;NSM;;;;;N;NON-SPACING COMMA BELOW;;;; +0327;COMBINING CEDILLA;Mn;202;NSM;;;;;N;NON-SPACING CEDILLA;;;; +0328;COMBINING OGONEK;Mn;202;NSM;;;;;N;NON-SPACING OGONEK;;;; +0329;COMBINING VERTICAL LINE BELOW;Mn;220;NSM;;;;;N;NON-SPACING VERTICAL LINE BELOW;;;; +032A;COMBINING BRIDGE BELOW;Mn;220;NSM;;;;;N;NON-SPACING BRIDGE BELOW;;;; +032B;COMBINING INVERTED DOUBLE ARCH BELOW;Mn;220;NSM;;;;;N;NON-SPACING INVERTED DOUBLE ARCH BELOW;;;; +032C;COMBINING CARON BELOW;Mn;220;NSM;;;;;N;NON-SPACING HACEK BELOW;;;; +032D;COMBINING CIRCUMFLEX ACCENT BELOW;Mn;220;NSM;;;;;N;NON-SPACING CIRCUMFLEX BELOW;;;; +032E;COMBINING BREVE BELOW;Mn;220;NSM;;;;;N;NON-SPACING BREVE BELOW;;;; +032F;COMBINING INVERTED BREVE BELOW;Mn;220;NSM;;;;;N;NON-SPACING INVERTED BREVE BELOW;;;; +0330;COMBINING TILDE BELOW;Mn;220;NSM;;;;;N;NON-SPACING TILDE BELOW;;;; +0331;COMBINING MACRON BELOW;Mn;220;NSM;;;;;N;NON-SPACING MACRON BELOW;;;; +0332;COMBINING LOW LINE;Mn;220;NSM;;;;;N;NON-SPACING UNDERSCORE;;;; +0333;COMBINING DOUBLE LOW LINE;Mn;220;NSM;;;;;N;NON-SPACING DOUBLE UNDERSCORE;;;; +0334;COMBINING TILDE OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING TILDE OVERLAY;;;; +0335;COMBINING SHORT STROKE OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING SHORT BAR OVERLAY;;;; +0336;COMBINING LONG STROKE OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING LONG BAR OVERLAY;;;; +0337;COMBINING SHORT SOLIDUS OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING SHORT SLASH OVERLAY;;;; +0338;COMBINING LONG SOLIDUS OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING LONG SLASH OVERLAY;;;; +0339;COMBINING RIGHT HALF RING BELOW;Mn;220;NSM;;;;;N;NON-SPACING RIGHT HALF RING BELOW;;;; +033A;COMBINING INVERTED BRIDGE BELOW;Mn;220;NSM;;;;;N;NON-SPACING INVERTED BRIDGE BELOW;;;; +033B;COMBINING SQUARE BELOW;Mn;220;NSM;;;;;N;NON-SPACING SQUARE BELOW;;;; +033C;COMBINING SEAGULL BELOW;Mn;220;NSM;;;;;N;NON-SPACING SEAGULL BELOW;;;; +033D;COMBINING X ABOVE;Mn;230;NSM;;;;;N;NON-SPACING X ABOVE;;;; +033E;COMBINING VERTICAL TILDE;Mn;230;NSM;;;;;N;NON-SPACING VERTICAL TILDE;;;; +033F;COMBINING DOUBLE OVERLINE;Mn;230;NSM;;;;;N;NON-SPACING DOUBLE OVERSCORE;;;; +0340;COMBINING GRAVE TONE MARK;Mn;230;NSM;0300;;;;N;NON-SPACING GRAVE TONE MARK;Vietnamese;;; +0341;COMBINING ACUTE TONE MARK;Mn;230;NSM;0301;;;;N;NON-SPACING ACUTE TONE MARK;Vietnamese;;; +0342;COMBINING GREEK PERISPOMENI;Mn;230;NSM;;;;;N;;;;; +0343;COMBINING GREEK KORONIS;Mn;230;NSM;0313;;;;N;;;;; +0344;COMBINING GREEK DIALYTIKA TONOS;Mn;230;NSM;0308 0301;;;;N;GREEK NON-SPACING DIAERESIS TONOS;;;; +0345;COMBINING GREEK YPOGEGRAMMENI;Mn;240;NSM;;;;;N;GREEK NON-SPACING IOTA BELOW;;0399;;0399 +0346;COMBINING BRIDGE ABOVE;Mn;230;NSM;;;;;N;;;;; +0347;COMBINING EQUALS SIGN BELOW;Mn;220;NSM;;;;;N;;;;; +0348;COMBINING DOUBLE VERTICAL LINE BELOW;Mn;220;NSM;;;;;N;;;;; +0349;COMBINING LEFT ANGLE BELOW;Mn;220;NSM;;;;;N;;;;; +034A;COMBINING NOT TILDE ABOVE;Mn;230;NSM;;;;;N;;;;; +034B;COMBINING HOMOTHETIC ABOVE;Mn;230;NSM;;;;;N;;;;; +034C;COMBINING ALMOST EQUAL TO ABOVE;Mn;230;NSM;;;;;N;;;;; +034D;COMBINING LEFT RIGHT ARROW BELOW;Mn;220;NSM;;;;;N;;;;; +034E;COMBINING UPWARDS ARROW BELOW;Mn;220;NSM;;;;;N;;;;; +034F;COMBINING GRAPHEME JOINER;Mn;0;NSM;;;;;N;;;;; +0360;COMBINING DOUBLE TILDE;Mn;234;NSM;;;;;N;;;;; +0361;COMBINING DOUBLE INVERTED BREVE;Mn;234;NSM;;;;;N;;;;; +0362;COMBINING DOUBLE RIGHTWARDS ARROW BELOW;Mn;233;NSM;;;;;N;;;;; +0363;COMBINING LATIN SMALL LETTER A;Mn;230;NSM;;;;;N;;;;; +0364;COMBINING LATIN SMALL LETTER E;Mn;230;NSM;;;;;N;;;;; +0365;COMBINING LATIN SMALL LETTER I;Mn;230;NSM;;;;;N;;;;; +0366;COMBINING LATIN SMALL LETTER O;Mn;230;NSM;;;;;N;;;;; +0367;COMBINING LATIN SMALL LETTER U;Mn;230;NSM;;;;;N;;;;; +0368;COMBINING LATIN SMALL LETTER C;Mn;230;NSM;;;;;N;;;;; +0369;COMBINING LATIN SMALL LETTER D;Mn;230;NSM;;;;;N;;;;; +036A;COMBINING LATIN SMALL LETTER H;Mn;230;NSM;;;;;N;;;;; +036B;COMBINING LATIN SMALL LETTER M;Mn;230;NSM;;;;;N;;;;; +036C;COMBINING LATIN SMALL LETTER R;Mn;230;NSM;;;;;N;;;;; +036D;COMBINING LATIN SMALL LETTER T;Mn;230;NSM;;;;;N;;;;; +036E;COMBINING LATIN SMALL LETTER V;Mn;230;NSM;;;;;N;;;;; +036F;COMBINING LATIN SMALL LETTER X;Mn;230;NSM;;;;;N;;;;; +0374;GREEK NUMERAL SIGN;Sk;0;ON;02B9;;;;N;GREEK UPPER NUMERAL SIGN;Dexia keraia;;; +0375;GREEK LOWER NUMERAL SIGN;Sk;0;ON;;;;;N;;Aristeri keraia;;; +037A;GREEK YPOGEGRAMMENI;Lm;0;L;<compat> 0020 0345;;;;N;GREEK SPACING IOTA BELOW;;;; +037E;GREEK QUESTION MARK;Po;0;ON;003B;;;;N;;Erotimatiko;;; +0384;GREEK TONOS;Sk;0;ON;<compat> 0020 0301;;;;N;GREEK SPACING TONOS;;;; +0385;GREEK DIALYTIKA TONOS;Sk;0;ON;00A8 0301;;;;N;GREEK SPACING DIAERESIS TONOS;;;; +0386;GREEK CAPITAL LETTER ALPHA WITH TONOS;Lu;0;L;0391 0301;;;;N;GREEK CAPITAL LETTER ALPHA TONOS;;;03AC; +0387;GREEK ANO TELEIA;Po;0;ON;00B7;;;;N;;;;; +0388;GREEK CAPITAL LETTER EPSILON WITH TONOS;Lu;0;L;0395 0301;;;;N;GREEK CAPITAL LETTER EPSILON TONOS;;;03AD; +0389;GREEK CAPITAL LETTER ETA WITH TONOS;Lu;0;L;0397 0301;;;;N;GREEK CAPITAL LETTER ETA TONOS;;;03AE; +038A;GREEK CAPITAL LETTER IOTA WITH TONOS;Lu;0;L;0399 0301;;;;N;GREEK CAPITAL LETTER IOTA TONOS;;;03AF; +038C;GREEK CAPITAL LETTER OMICRON WITH TONOS;Lu;0;L;039F 0301;;;;N;GREEK CAPITAL LETTER OMICRON TONOS;;;03CC; +038E;GREEK CAPITAL LETTER UPSILON WITH TONOS;Lu;0;L;03A5 0301;;;;N;GREEK CAPITAL LETTER UPSILON TONOS;;;03CD; +038F;GREEK CAPITAL LETTER OMEGA WITH TONOS;Lu;0;L;03A9 0301;;;;N;GREEK CAPITAL LETTER OMEGA TONOS;;;03CE; +0390;GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS;Ll;0;L;03CA 0301;;;;N;GREEK SMALL LETTER IOTA DIAERESIS TONOS;;;; +0391;GREEK CAPITAL LETTER ALPHA;Lu;0;L;;;;;N;;;;03B1; +0392;GREEK CAPITAL LETTER BETA;Lu;0;L;;;;;N;;;;03B2; +0393;GREEK CAPITAL LETTER GAMMA;Lu;0;L;;;;;N;;;;03B3; +0394;GREEK CAPITAL LETTER DELTA;Lu;0;L;;;;;N;;;;03B4; +0395;GREEK CAPITAL LETTER EPSILON;Lu;0;L;;;;;N;;;;03B5; +0396;GREEK CAPITAL LETTER ZETA;Lu;0;L;;;;;N;;;;03B6; +0397;GREEK CAPITAL LETTER ETA;Lu;0;L;;;;;N;;;;03B7; +0398;GREEK CAPITAL LETTER THETA;Lu;0;L;;;;;N;;;;03B8; +0399;GREEK CAPITAL LETTER IOTA;Lu;0;L;;;;;N;;;;03B9; +039A;GREEK CAPITAL LETTER KAPPA;Lu;0;L;;;;;N;;;;03BA; +039B;GREEK CAPITAL LETTER LAMDA;Lu;0;L;;;;;N;GREEK CAPITAL LETTER LAMBDA;;;03BB; +039C;GREEK CAPITAL LETTER MU;Lu;0;L;;;;;N;;;;03BC; +039D;GREEK CAPITAL LETTER NU;Lu;0;L;;;;;N;;;;03BD; +039E;GREEK CAPITAL LETTER XI;Lu;0;L;;;;;N;;;;03BE; +039F;GREEK CAPITAL LETTER OMICRON;Lu;0;L;;;;;N;;;;03BF; +03A0;GREEK CAPITAL LETTER PI;Lu;0;L;;;;;N;;;;03C0; +03A1;GREEK CAPITAL LETTER RHO;Lu;0;L;;;;;N;;;;03C1; +03A3;GREEK CAPITAL LETTER SIGMA;Lu;0;L;;;;;N;;;;03C3; +03A4;GREEK CAPITAL LETTER TAU;Lu;0;L;;;;;N;;;;03C4; +03A5;GREEK CAPITAL LETTER UPSILON;Lu;0;L;;;;;N;;;;03C5; +03A6;GREEK CAPITAL LETTER PHI;Lu;0;L;;;;;N;;;;03C6; +03A7;GREEK CAPITAL LETTER CHI;Lu;0;L;;;;;N;;;;03C7; +03A8;GREEK CAPITAL LETTER PSI;Lu;0;L;;;;;N;;;;03C8; +03A9;GREEK CAPITAL LETTER OMEGA;Lu;0;L;;;;;N;;;;03C9; +03AA;GREEK CAPITAL LETTER IOTA WITH DIALYTIKA;Lu;0;L;0399 0308;;;;N;GREEK CAPITAL LETTER IOTA DIAERESIS;;;03CA; +03AB;GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA;Lu;0;L;03A5 0308;;;;N;GREEK CAPITAL LETTER UPSILON DIAERESIS;;;03CB; +03AC;GREEK SMALL LETTER ALPHA WITH TONOS;Ll;0;L;03B1 0301;;;;N;GREEK SMALL LETTER ALPHA TONOS;;0386;;0386 +03AD;GREEK SMALL LETTER EPSILON WITH TONOS;Ll;0;L;03B5 0301;;;;N;GREEK SMALL LETTER EPSILON TONOS;;0388;;0388 +03AE;GREEK SMALL LETTER ETA WITH TONOS;Ll;0;L;03B7 0301;;;;N;GREEK SMALL LETTER ETA TONOS;;0389;;0389 +03AF;GREEK SMALL LETTER IOTA WITH TONOS;Ll;0;L;03B9 0301;;;;N;GREEK SMALL LETTER IOTA TONOS;;038A;;038A +03B0;GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS;Ll;0;L;03CB 0301;;;;N;GREEK SMALL LETTER UPSILON DIAERESIS TONOS;;;; +03B1;GREEK SMALL LETTER ALPHA;Ll;0;L;;;;;N;;;0391;;0391 +03B2;GREEK SMALL LETTER BETA;Ll;0;L;;;;;N;;;0392;;0392 +03B3;GREEK SMALL LETTER GAMMA;Ll;0;L;;;;;N;;;0393;;0393 +03B4;GREEK SMALL LETTER DELTA;Ll;0;L;;;;;N;;;0394;;0394 +03B5;GREEK SMALL LETTER EPSILON;Ll;0;L;;;;;N;;;0395;;0395 +03B6;GREEK SMALL LETTER ZETA;Ll;0;L;;;;;N;;;0396;;0396 +03B7;GREEK SMALL LETTER ETA;Ll;0;L;;;;;N;;;0397;;0397 +03B8;GREEK SMALL LETTER THETA;Ll;0;L;;;;;N;;;0398;;0398 +03B9;GREEK SMALL LETTER IOTA;Ll;0;L;;;;;N;;;0399;;0399 +03BA;GREEK SMALL LETTER KAPPA;Ll;0;L;;;;;N;;;039A;;039A +03BB;GREEK SMALL LETTER LAMDA;Ll;0;L;;;;;N;GREEK SMALL LETTER LAMBDA;;039B;;039B +03BC;GREEK SMALL LETTER MU;Ll;0;L;;;;;N;;;039C;;039C +03BD;GREEK SMALL LETTER NU;Ll;0;L;;;;;N;;;039D;;039D +03BE;GREEK SMALL LETTER XI;Ll;0;L;;;;;N;;;039E;;039E +03BF;GREEK SMALL LETTER OMICRON;Ll;0;L;;;;;N;;;039F;;039F +03C0;GREEK SMALL LETTER PI;Ll;0;L;;;;;N;;;03A0;;03A0 +03C1;GREEK SMALL LETTER RHO;Ll;0;L;;;;;N;;;03A1;;03A1 +03C2;GREEK SMALL LETTER FINAL SIGMA;Ll;0;L;;;;;N;;;03A3;;03A3 +03C3;GREEK SMALL LETTER SIGMA;Ll;0;L;;;;;N;;;03A3;;03A3 +03C4;GREEK SMALL LETTER TAU;Ll;0;L;;;;;N;;;03A4;;03A4 +03C5;GREEK SMALL LETTER UPSILON;Ll;0;L;;;;;N;;;03A5;;03A5 +03C6;GREEK SMALL LETTER PHI;Ll;0;L;;;;;N;;;03A6;;03A6 +03C7;GREEK SMALL LETTER CHI;Ll;0;L;;;;;N;;;03A7;;03A7 +03C8;GREEK SMALL LETTER PSI;Ll;0;L;;;;;N;;;03A8;;03A8 +03C9;GREEK SMALL LETTER OMEGA;Ll;0;L;;;;;N;;;03A9;;03A9 +03CA;GREEK SMALL LETTER IOTA WITH DIALYTIKA;Ll;0;L;03B9 0308;;;;N;GREEK SMALL LETTER IOTA DIAERESIS;;03AA;;03AA +03CB;GREEK SMALL LETTER UPSILON WITH DIALYTIKA;Ll;0;L;03C5 0308;;;;N;GREEK SMALL LETTER UPSILON DIAERESIS;;03AB;;03AB +03CC;GREEK SMALL LETTER OMICRON WITH TONOS;Ll;0;L;03BF 0301;;;;N;GREEK SMALL LETTER OMICRON TONOS;;038C;;038C +03CD;GREEK SMALL LETTER UPSILON WITH TONOS;Ll;0;L;03C5 0301;;;;N;GREEK SMALL LETTER UPSILON TONOS;;038E;;038E +03CE;GREEK SMALL LETTER OMEGA WITH TONOS;Ll;0;L;03C9 0301;;;;N;GREEK SMALL LETTER OMEGA TONOS;;038F;;038F +03D0;GREEK BETA SYMBOL;Ll;0;L;<compat> 03B2;;;;N;GREEK SMALL LETTER CURLED BETA;;0392;;0392 +03D1;GREEK THETA SYMBOL;Ll;0;L;<compat> 03B8;;;;N;GREEK SMALL LETTER SCRIPT THETA;;0398;;0398 +03D2;GREEK UPSILON WITH HOOK SYMBOL;Lu;0;L;<compat> 03A5;;;;N;GREEK CAPITAL LETTER UPSILON HOOK;;;; +03D3;GREEK UPSILON WITH ACUTE AND HOOK SYMBOL;Lu;0;L;03D2 0301;;;;N;GREEK CAPITAL LETTER UPSILON HOOK TONOS;;;; +03D4;GREEK UPSILON WITH DIAERESIS AND HOOK SYMBOL;Lu;0;L;03D2 0308;;;;N;GREEK CAPITAL LETTER UPSILON HOOK DIAERESIS;;;; +03D5;GREEK PHI SYMBOL;Ll;0;L;<compat> 03C6;;;;N;GREEK SMALL LETTER SCRIPT PHI;;03A6;;03A6 +03D6;GREEK PI SYMBOL;Ll;0;L;<compat> 03C0;;;;N;GREEK SMALL LETTER OMEGA PI;;03A0;;03A0 +03D7;GREEK KAI SYMBOL;Ll;0;L;;;;;N;;;;; +03D8;GREEK LETTER ARCHAIC KOPPA;Lu;0;L;;;;;N;;*;;03D9; +03D9;GREEK SMALL LETTER ARCHAIC KOPPA;Ll;0;L;;;;;N;;*;03D8;;03D8 +03DA;GREEK LETTER STIGMA;Lu;0;L;;;;;N;GREEK CAPITAL LETTER STIGMA;;;03DB; +03DB;GREEK SMALL LETTER STIGMA;Ll;0;L;;;;;N;;;03DA;;03DA +03DC;GREEK LETTER DIGAMMA;Lu;0;L;;;;;N;GREEK CAPITAL LETTER DIGAMMA;;;03DD; +03DD;GREEK SMALL LETTER DIGAMMA;Ll;0;L;;;;;N;;;03DC;;03DC +03DE;GREEK LETTER KOPPA;Lu;0;L;;;;;N;GREEK CAPITAL LETTER KOPPA;;;03DF; +03DF;GREEK SMALL LETTER KOPPA;Ll;0;L;;;;;N;;;03DE;;03DE +03E0;GREEK LETTER SAMPI;Lu;0;L;;;;;N;GREEK CAPITAL LETTER SAMPI;;;03E1; +03E1;GREEK SMALL LETTER SAMPI;Ll;0;L;;;;;N;;;03E0;;03E0 +03E2;COPTIC CAPITAL LETTER SHEI;Lu;0;L;;;;;N;GREEK CAPITAL LETTER SHEI;;;03E3; +03E3;COPTIC SMALL LETTER SHEI;Ll;0;L;;;;;N;GREEK SMALL LETTER SHEI;;03E2;;03E2 +03E4;COPTIC CAPITAL LETTER FEI;Lu;0;L;;;;;N;GREEK CAPITAL LETTER FEI;;;03E5; +03E5;COPTIC SMALL LETTER FEI;Ll;0;L;;;;;N;GREEK SMALL LETTER FEI;;03E4;;03E4 +03E6;COPTIC CAPITAL LETTER KHEI;Lu;0;L;;;;;N;GREEK CAPITAL LETTER KHEI;;;03E7; +03E7;COPTIC SMALL LETTER KHEI;Ll;0;L;;;;;N;GREEK SMALL LETTER KHEI;;03E6;;03E6 +03E8;COPTIC CAPITAL LETTER HORI;Lu;0;L;;;;;N;GREEK CAPITAL LETTER HORI;;;03E9; +03E9;COPTIC SMALL LETTER HORI;Ll;0;L;;;;;N;GREEK SMALL LETTER HORI;;03E8;;03E8 +03EA;COPTIC CAPITAL LETTER GANGIA;Lu;0;L;;;;;N;GREEK CAPITAL LETTER GANGIA;;;03EB; +03EB;COPTIC SMALL LETTER GANGIA;Ll;0;L;;;;;N;GREEK SMALL LETTER GANGIA;;03EA;;03EA +03EC;COPTIC CAPITAL LETTER SHIMA;Lu;0;L;;;;;N;GREEK CAPITAL LETTER SHIMA;;;03ED; +03ED;COPTIC SMALL LETTER SHIMA;Ll;0;L;;;;;N;GREEK SMALL LETTER SHIMA;;03EC;;03EC +03EE;COPTIC CAPITAL LETTER DEI;Lu;0;L;;;;;N;GREEK CAPITAL LETTER DEI;;;03EF; +03EF;COPTIC SMALL LETTER DEI;Ll;0;L;;;;;N;GREEK SMALL LETTER DEI;;03EE;;03EE +03F0;GREEK KAPPA SYMBOL;Ll;0;L;<compat> 03BA;;;;N;GREEK SMALL LETTER SCRIPT KAPPA;;039A;;039A +03F1;GREEK RHO SYMBOL;Ll;0;L;<compat> 03C1;;;;N;GREEK SMALL LETTER TAILED RHO;;03A1;;03A1 +03F2;GREEK LUNATE SIGMA SYMBOL;Ll;0;L;<compat> 03C2;;;;N;GREEK SMALL LETTER LUNATE SIGMA;;03A3;;03A3 +03F3;GREEK LETTER YOT;Ll;0;L;;;;;N;;;;; +03F4;GREEK CAPITAL THETA SYMBOL;Lu;0;L;<compat> 0398;;;;N;;;;03B8; +03F5;GREEK LUNATE EPSILON SYMBOL;Ll;0;L;<compat> 03B5;;;;N;;;0395;;0395 +03F6;GREEK REVERSED LUNATE EPSILON SYMBOL;Sm;0;ON;;;;;N;;;;; +0400;CYRILLIC CAPITAL LETTER IE WITH GRAVE;Lu;0;L;0415 0300;;;;N;;;;0450; +0401;CYRILLIC CAPITAL LETTER IO;Lu;0;L;0415 0308;;;;N;;;;0451; +0402;CYRILLIC CAPITAL LETTER DJE;Lu;0;L;;;;;N;;Serbocroatian;;0452; +0403;CYRILLIC CAPITAL LETTER GJE;Lu;0;L;0413 0301;;;;N;;;;0453; +0404;CYRILLIC CAPITAL LETTER UKRAINIAN IE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER E;;;0454; +0405;CYRILLIC CAPITAL LETTER DZE;Lu;0;L;;;;;N;;;;0455; +0406;CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER I;;;0456; +0407;CYRILLIC CAPITAL LETTER YI;Lu;0;L;0406 0308;;;;N;;Ukrainian;;0457; +0408;CYRILLIC CAPITAL LETTER JE;Lu;0;L;;;;;N;;;;0458; +0409;CYRILLIC CAPITAL LETTER LJE;Lu;0;L;;;;;N;;;;0459; +040A;CYRILLIC CAPITAL LETTER NJE;Lu;0;L;;;;;N;;;;045A; +040B;CYRILLIC CAPITAL LETTER TSHE;Lu;0;L;;;;;N;;Serbocroatian;;045B; +040C;CYRILLIC CAPITAL LETTER KJE;Lu;0;L;041A 0301;;;;N;;;;045C; +040D;CYRILLIC CAPITAL LETTER I WITH GRAVE;Lu;0;L;0418 0300;;;;N;;;;045D; +040E;CYRILLIC CAPITAL LETTER SHORT U;Lu;0;L;0423 0306;;;;N;;Byelorussian;;045E; +040F;CYRILLIC CAPITAL LETTER DZHE;Lu;0;L;;;;;N;;;;045F; +0410;CYRILLIC CAPITAL LETTER A;Lu;0;L;;;;;N;;;;0430; +0411;CYRILLIC CAPITAL LETTER BE;Lu;0;L;;;;;N;;;;0431; +0412;CYRILLIC CAPITAL LETTER VE;Lu;0;L;;;;;N;;;;0432; +0413;CYRILLIC CAPITAL LETTER GHE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER GE;;;0433; +0414;CYRILLIC CAPITAL LETTER DE;Lu;0;L;;;;;N;;;;0434; +0415;CYRILLIC CAPITAL LETTER IE;Lu;0;L;;;;;N;;;;0435; +0416;CYRILLIC CAPITAL LETTER ZHE;Lu;0;L;;;;;N;;;;0436; +0417;CYRILLIC CAPITAL LETTER ZE;Lu;0;L;;;;;N;;;;0437; +0418;CYRILLIC CAPITAL LETTER I;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER II;;;0438; +0419;CYRILLIC CAPITAL LETTER SHORT I;Lu;0;L;0418 0306;;;;N;CYRILLIC CAPITAL LETTER SHORT II;;;0439; +041A;CYRILLIC CAPITAL LETTER KA;Lu;0;L;;;;;N;;;;043A; +041B;CYRILLIC CAPITAL LETTER EL;Lu;0;L;;;;;N;;;;043B; +041C;CYRILLIC CAPITAL LETTER EM;Lu;0;L;;;;;N;;;;043C; +041D;CYRILLIC CAPITAL LETTER EN;Lu;0;L;;;;;N;;;;043D; +041E;CYRILLIC CAPITAL LETTER O;Lu;0;L;;;;;N;;;;043E; +041F;CYRILLIC CAPITAL LETTER PE;Lu;0;L;;;;;N;;;;043F; +0420;CYRILLIC CAPITAL LETTER ER;Lu;0;L;;;;;N;;;;0440; +0421;CYRILLIC CAPITAL LETTER ES;Lu;0;L;;;;;N;;;;0441; +0422;CYRILLIC CAPITAL LETTER TE;Lu;0;L;;;;;N;;;;0442; +0423;CYRILLIC CAPITAL LETTER U;Lu;0;L;;;;;N;;;;0443; +0424;CYRILLIC CAPITAL LETTER EF;Lu;0;L;;;;;N;;;;0444; +0425;CYRILLIC CAPITAL LETTER HA;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER KHA;;;0445; +0426;CYRILLIC CAPITAL LETTER TSE;Lu;0;L;;;;;N;;;;0446; +0427;CYRILLIC CAPITAL LETTER CHE;Lu;0;L;;;;;N;;;;0447; +0428;CYRILLIC CAPITAL LETTER SHA;Lu;0;L;;;;;N;;;;0448; +0429;CYRILLIC CAPITAL LETTER SHCHA;Lu;0;L;;;;;N;;;;0449; +042A;CYRILLIC CAPITAL LETTER HARD SIGN;Lu;0;L;;;;;N;;;;044A; +042B;CYRILLIC CAPITAL LETTER YERU;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER YERI;;;044B; +042C;CYRILLIC CAPITAL LETTER SOFT SIGN;Lu;0;L;;;;;N;;;;044C; +042D;CYRILLIC CAPITAL LETTER E;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER REVERSED E;;;044D; +042E;CYRILLIC CAPITAL LETTER YU;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER IU;;;044E; +042F;CYRILLIC CAPITAL LETTER YA;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER IA;;;044F; +0430;CYRILLIC SMALL LETTER A;Ll;0;L;;;;;N;;;0410;;0410 +0431;CYRILLIC SMALL LETTER BE;Ll;0;L;;;;;N;;;0411;;0411 +0432;CYRILLIC SMALL LETTER VE;Ll;0;L;;;;;N;;;0412;;0412 +0433;CYRILLIC SMALL LETTER GHE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER GE;;0413;;0413 +0434;CYRILLIC SMALL LETTER DE;Ll;0;L;;;;;N;;;0414;;0414 +0435;CYRILLIC SMALL LETTER IE;Ll;0;L;;;;;N;;;0415;;0415 +0436;CYRILLIC SMALL LETTER ZHE;Ll;0;L;;;;;N;;;0416;;0416 +0437;CYRILLIC SMALL LETTER ZE;Ll;0;L;;;;;N;;;0417;;0417 +0438;CYRILLIC SMALL LETTER I;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER II;;0418;;0418 +0439;CYRILLIC SMALL LETTER SHORT I;Ll;0;L;0438 0306;;;;N;CYRILLIC SMALL LETTER SHORT II;;0419;;0419 +043A;CYRILLIC SMALL LETTER KA;Ll;0;L;;;;;N;;;041A;;041A +043B;CYRILLIC SMALL LETTER EL;Ll;0;L;;;;;N;;;041B;;041B +043C;CYRILLIC SMALL LETTER EM;Ll;0;L;;;;;N;;;041C;;041C +043D;CYRILLIC SMALL LETTER EN;Ll;0;L;;;;;N;;;041D;;041D +043E;CYRILLIC SMALL LETTER O;Ll;0;L;;;;;N;;;041E;;041E +043F;CYRILLIC SMALL LETTER PE;Ll;0;L;;;;;N;;;041F;;041F +0440;CYRILLIC SMALL LETTER ER;Ll;0;L;;;;;N;;;0420;;0420 +0441;CYRILLIC SMALL LETTER ES;Ll;0;L;;;;;N;;;0421;;0421 +0442;CYRILLIC SMALL LETTER TE;Ll;0;L;;;;;N;;;0422;;0422 +0443;CYRILLIC SMALL LETTER U;Ll;0;L;;;;;N;;;0423;;0423 +0444;CYRILLIC SMALL LETTER EF;Ll;0;L;;;;;N;;;0424;;0424 +0445;CYRILLIC SMALL LETTER HA;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER KHA;;0425;;0425 +0446;CYRILLIC SMALL LETTER TSE;Ll;0;L;;;;;N;;;0426;;0426 +0447;CYRILLIC SMALL LETTER CHE;Ll;0;L;;;;;N;;;0427;;0427 +0448;CYRILLIC SMALL LETTER SHA;Ll;0;L;;;;;N;;;0428;;0428 +0449;CYRILLIC SMALL LETTER SHCHA;Ll;0;L;;;;;N;;;0429;;0429 +044A;CYRILLIC SMALL LETTER HARD SIGN;Ll;0;L;;;;;N;;;042A;;042A +044B;CYRILLIC SMALL LETTER YERU;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER YERI;;042B;;042B +044C;CYRILLIC SMALL LETTER SOFT SIGN;Ll;0;L;;;;;N;;;042C;;042C +044D;CYRILLIC SMALL LETTER E;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER REVERSED E;;042D;;042D +044E;CYRILLIC SMALL LETTER YU;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER IU;;042E;;042E +044F;CYRILLIC SMALL LETTER YA;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER IA;;042F;;042F +0450;CYRILLIC SMALL LETTER IE WITH GRAVE;Ll;0;L;0435 0300;;;;N;;;0400;;0400 +0451;CYRILLIC SMALL LETTER IO;Ll;0;L;0435 0308;;;;N;;;0401;;0401 +0452;CYRILLIC SMALL LETTER DJE;Ll;0;L;;;;;N;;Serbocroatian;0402;;0402 +0453;CYRILLIC SMALL LETTER GJE;Ll;0;L;0433 0301;;;;N;;;0403;;0403 +0454;CYRILLIC SMALL LETTER UKRAINIAN IE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER E;;0404;;0404 +0455;CYRILLIC SMALL LETTER DZE;Ll;0;L;;;;;N;;;0405;;0405 +0456;CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER I;;0406;;0406 +0457;CYRILLIC SMALL LETTER YI;Ll;0;L;0456 0308;;;;N;;Ukrainian;0407;;0407 +0458;CYRILLIC SMALL LETTER JE;Ll;0;L;;;;;N;;;0408;;0408 +0459;CYRILLIC SMALL LETTER LJE;Ll;0;L;;;;;N;;;0409;;0409 +045A;CYRILLIC SMALL LETTER NJE;Ll;0;L;;;;;N;;;040A;;040A +045B;CYRILLIC SMALL LETTER TSHE;Ll;0;L;;;;;N;;Serbocroatian;040B;;040B +045C;CYRILLIC SMALL LETTER KJE;Ll;0;L;043A 0301;;;;N;;;040C;;040C +045D;CYRILLIC SMALL LETTER I WITH GRAVE;Ll;0;L;0438 0300;;;;N;;;040D;;040D +045E;CYRILLIC SMALL LETTER SHORT U;Ll;0;L;0443 0306;;;;N;;Byelorussian;040E;;040E +045F;CYRILLIC SMALL LETTER DZHE;Ll;0;L;;;;;N;;;040F;;040F +0460;CYRILLIC CAPITAL LETTER OMEGA;Lu;0;L;;;;;N;;;;0461; +0461;CYRILLIC SMALL LETTER OMEGA;Ll;0;L;;;;;N;;;0460;;0460 +0462;CYRILLIC CAPITAL LETTER YAT;Lu;0;L;;;;;N;;;;0463; +0463;CYRILLIC SMALL LETTER YAT;Ll;0;L;;;;;N;;;0462;;0462 +0464;CYRILLIC CAPITAL LETTER IOTIFIED E;Lu;0;L;;;;;N;;;;0465; +0465;CYRILLIC SMALL LETTER IOTIFIED E;Ll;0;L;;;;;N;;;0464;;0464 +0466;CYRILLIC CAPITAL LETTER LITTLE YUS;Lu;0;L;;;;;N;;;;0467; +0467;CYRILLIC SMALL LETTER LITTLE YUS;Ll;0;L;;;;;N;;;0466;;0466 +0468;CYRILLIC CAPITAL LETTER IOTIFIED LITTLE YUS;Lu;0;L;;;;;N;;;;0469; +0469;CYRILLIC SMALL LETTER IOTIFIED LITTLE YUS;Ll;0;L;;;;;N;;;0468;;0468 +046A;CYRILLIC CAPITAL LETTER BIG YUS;Lu;0;L;;;;;N;;;;046B; +046B;CYRILLIC SMALL LETTER BIG YUS;Ll;0;L;;;;;N;;;046A;;046A +046C;CYRILLIC CAPITAL LETTER IOTIFIED BIG YUS;Lu;0;L;;;;;N;;;;046D; +046D;CYRILLIC SMALL LETTER IOTIFIED BIG YUS;Ll;0;L;;;;;N;;;046C;;046C +046E;CYRILLIC CAPITAL LETTER KSI;Lu;0;L;;;;;N;;;;046F; +046F;CYRILLIC SMALL LETTER KSI;Ll;0;L;;;;;N;;;046E;;046E +0470;CYRILLIC CAPITAL LETTER PSI;Lu;0;L;;;;;N;;;;0471; +0471;CYRILLIC SMALL LETTER PSI;Ll;0;L;;;;;N;;;0470;;0470 +0472;CYRILLIC CAPITAL LETTER FITA;Lu;0;L;;;;;N;;;;0473; +0473;CYRILLIC SMALL LETTER FITA;Ll;0;L;;;;;N;;;0472;;0472 +0474;CYRILLIC CAPITAL LETTER IZHITSA;Lu;0;L;;;;;N;;;;0475; +0475;CYRILLIC SMALL LETTER IZHITSA;Ll;0;L;;;;;N;;;0474;;0474 +0476;CYRILLIC CAPITAL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT;Lu;0;L;0474 030F;;;;N;CYRILLIC CAPITAL LETTER IZHITSA DOUBLE GRAVE;;;0477; +0477;CYRILLIC SMALL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT;Ll;0;L;0475 030F;;;;N;CYRILLIC SMALL LETTER IZHITSA DOUBLE GRAVE;;0476;;0476 +0478;CYRILLIC CAPITAL LETTER UK;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER UK DIGRAPH;;;0479; +0479;CYRILLIC SMALL LETTER UK;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER UK DIGRAPH;;0478;;0478 +047A;CYRILLIC CAPITAL LETTER ROUND OMEGA;Lu;0;L;;;;;N;;;;047B; +047B;CYRILLIC SMALL LETTER ROUND OMEGA;Ll;0;L;;;;;N;;;047A;;047A +047C;CYRILLIC CAPITAL LETTER OMEGA WITH TITLO;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER OMEGA TITLO;;;047D; +047D;CYRILLIC SMALL LETTER OMEGA WITH TITLO;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER OMEGA TITLO;;047C;;047C +047E;CYRILLIC CAPITAL LETTER OT;Lu;0;L;;;;;N;;;;047F; +047F;CYRILLIC SMALL LETTER OT;Ll;0;L;;;;;N;;;047E;;047E +0480;CYRILLIC CAPITAL LETTER KOPPA;Lu;0;L;;;;;N;;;;0481; +0481;CYRILLIC SMALL LETTER KOPPA;Ll;0;L;;;;;N;;;0480;;0480 +0482;CYRILLIC THOUSANDS SIGN;So;0;L;;;;;N;;;;; +0483;COMBINING CYRILLIC TITLO;Mn;230;NSM;;;;;N;CYRILLIC NON-SPACING TITLO;;;; +0484;COMBINING CYRILLIC PALATALIZATION;Mn;230;NSM;;;;;N;CYRILLIC NON-SPACING PALATALIZATION;;;; +0485;COMBINING CYRILLIC DASIA PNEUMATA;Mn;230;NSM;;;;;N;CYRILLIC NON-SPACING DASIA PNEUMATA;;;; +0486;COMBINING CYRILLIC PSILI PNEUMATA;Mn;230;NSM;;;;;N;CYRILLIC NON-SPACING PSILI PNEUMATA;;;; +0488;COMBINING CYRILLIC HUNDRED THOUSANDS SIGN;Me;0;NSM;;;;;N;;;;; +0489;COMBINING CYRILLIC MILLIONS SIGN;Me;0;NSM;;;;;N;;;;; +048A;CYRILLIC CAPITAL LETTER SHORT I WITH TAIL;Lu;0;L;;;;;N;;;;048B; +048B;CYRILLIC SMALL LETTER SHORT I WITH TAIL;Ll;0;L;;;;;N;;;048A;;048A +048C;CYRILLIC CAPITAL LETTER SEMISOFT SIGN;Lu;0;L;;;;;N;;;;048D; +048D;CYRILLIC SMALL LETTER SEMISOFT SIGN;Ll;0;L;;;;;N;;;048C;;048C +048E;CYRILLIC CAPITAL LETTER ER WITH TICK;Lu;0;L;;;;;N;;;;048F; +048F;CYRILLIC SMALL LETTER ER WITH TICK;Ll;0;L;;;;;N;;;048E;;048E +0490;CYRILLIC CAPITAL LETTER GHE WITH UPTURN;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER GE WITH UPTURN;;;0491; +0491;CYRILLIC SMALL LETTER GHE WITH UPTURN;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER GE WITH UPTURN;;0490;;0490 +0492;CYRILLIC CAPITAL LETTER GHE WITH STROKE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER GE BAR;;;0493; +0493;CYRILLIC SMALL LETTER GHE WITH STROKE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER GE BAR;;0492;;0492 +0494;CYRILLIC CAPITAL LETTER GHE WITH MIDDLE HOOK;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER GE HOOK;;;0495; +0495;CYRILLIC SMALL LETTER GHE WITH MIDDLE HOOK;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER GE HOOK;;0494;;0494 +0496;CYRILLIC CAPITAL LETTER ZHE WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER ZHE WITH RIGHT DESCENDER;;;0497; +0497;CYRILLIC SMALL LETTER ZHE WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER ZHE WITH RIGHT DESCENDER;;0496;;0496 +0498;CYRILLIC CAPITAL LETTER ZE WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER ZE CEDILLA;;;0499; +0499;CYRILLIC SMALL LETTER ZE WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER ZE CEDILLA;;0498;;0498 +049A;CYRILLIC CAPITAL LETTER KA WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER KA WITH RIGHT DESCENDER;;;049B; +049B;CYRILLIC SMALL LETTER KA WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER KA WITH RIGHT DESCENDER;;049A;;049A +049C;CYRILLIC CAPITAL LETTER KA WITH VERTICAL STROKE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER KA VERTICAL BAR;;;049D; +049D;CYRILLIC SMALL LETTER KA WITH VERTICAL STROKE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER KA VERTICAL BAR;;049C;;049C +049E;CYRILLIC CAPITAL LETTER KA WITH STROKE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER KA BAR;;;049F; +049F;CYRILLIC SMALL LETTER KA WITH STROKE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER KA BAR;;049E;;049E +04A0;CYRILLIC CAPITAL LETTER BASHKIR KA;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER REVERSED GE KA;;;04A1; +04A1;CYRILLIC SMALL LETTER BASHKIR KA;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER REVERSED GE KA;;04A0;;04A0 +04A2;CYRILLIC CAPITAL LETTER EN WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER EN WITH RIGHT DESCENDER;;;04A3; +04A3;CYRILLIC SMALL LETTER EN WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER EN WITH RIGHT DESCENDER;;04A2;;04A2 +04A4;CYRILLIC CAPITAL LIGATURE EN GHE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER EN GE;;;04A5; +04A5;CYRILLIC SMALL LIGATURE EN GHE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER EN GE;;04A4;;04A4 +04A6;CYRILLIC CAPITAL LETTER PE WITH MIDDLE HOOK;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER PE HOOK;Abkhasian;;04A7; +04A7;CYRILLIC SMALL LETTER PE WITH MIDDLE HOOK;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER PE HOOK;Abkhasian;04A6;;04A6 +04A8;CYRILLIC CAPITAL LETTER ABKHASIAN HA;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER O HOOK;;;04A9; +04A9;CYRILLIC SMALL LETTER ABKHASIAN HA;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER O HOOK;;04A8;;04A8 +04AA;CYRILLIC CAPITAL LETTER ES WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER ES CEDILLA;;;04AB; +04AB;CYRILLIC SMALL LETTER ES WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER ES CEDILLA;;04AA;;04AA +04AC;CYRILLIC CAPITAL LETTER TE WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER TE WITH RIGHT DESCENDER;;;04AD; +04AD;CYRILLIC SMALL LETTER TE WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER TE WITH RIGHT DESCENDER;;04AC;;04AC +04AE;CYRILLIC CAPITAL LETTER STRAIGHT U;Lu;0;L;;;;;N;;;;04AF; +04AF;CYRILLIC SMALL LETTER STRAIGHT U;Ll;0;L;;;;;N;;;04AE;;04AE +04B0;CYRILLIC CAPITAL LETTER STRAIGHT U WITH STROKE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER STRAIGHT U BAR;;;04B1; +04B1;CYRILLIC SMALL LETTER STRAIGHT U WITH STROKE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER STRAIGHT U BAR;;04B0;;04B0 +04B2;CYRILLIC CAPITAL LETTER HA WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER KHA WITH RIGHT DESCENDER;;;04B3; +04B3;CYRILLIC SMALL LETTER HA WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER KHA WITH RIGHT DESCENDER;;04B2;;04B2 +04B4;CYRILLIC CAPITAL LIGATURE TE TSE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER TE TSE;Abkhasian;;04B5; +04B5;CYRILLIC SMALL LIGATURE TE TSE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER TE TSE;Abkhasian;04B4;;04B4 +04B6;CYRILLIC CAPITAL LETTER CHE WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER CHE WITH RIGHT DESCENDER;;;04B7; +04B7;CYRILLIC SMALL LETTER CHE WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER CHE WITH RIGHT DESCENDER;;04B6;;04B6 +04B8;CYRILLIC CAPITAL LETTER CHE WITH VERTICAL STROKE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER CHE VERTICAL BAR;;;04B9; +04B9;CYRILLIC SMALL LETTER CHE WITH VERTICAL STROKE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER CHE VERTICAL BAR;;04B8;;04B8 +04BA;CYRILLIC CAPITAL LETTER SHHA;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER H;;;04BB; +04BB;CYRILLIC SMALL LETTER SHHA;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER H;;04BA;;04BA +04BC;CYRILLIC CAPITAL LETTER ABKHASIAN CHE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER IE HOOK;;;04BD; +04BD;CYRILLIC SMALL LETTER ABKHASIAN CHE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER IE HOOK;;04BC;;04BC +04BE;CYRILLIC CAPITAL LETTER ABKHASIAN CHE WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER IE HOOK OGONEK;;;04BF; +04BF;CYRILLIC SMALL LETTER ABKHASIAN CHE WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER IE HOOK OGONEK;;04BE;;04BE +04C0;CYRILLIC LETTER PALOCHKA;Lu;0;L;;;;;N;CYRILLIC LETTER I;;;; +04C1;CYRILLIC CAPITAL LETTER ZHE WITH BREVE;Lu;0;L;0416 0306;;;;N;CYRILLIC CAPITAL LETTER SHORT ZHE;;;04C2; +04C2;CYRILLIC SMALL LETTER ZHE WITH BREVE;Ll;0;L;0436 0306;;;;N;CYRILLIC SMALL LETTER SHORT ZHE;;04C1;;04C1 +04C3;CYRILLIC CAPITAL LETTER KA WITH HOOK;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER KA HOOK;;;04C4; +04C4;CYRILLIC SMALL LETTER KA WITH HOOK;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER KA HOOK;;04C3;;04C3 +04C5;CYRILLIC CAPITAL LETTER EL WITH TAIL;Lu;0;L;;;;;N;;;;04C6; +04C6;CYRILLIC SMALL LETTER EL WITH TAIL;Ll;0;L;;;;;N;;;04C5;;04C5 +04C7;CYRILLIC CAPITAL LETTER EN WITH HOOK;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER EN HOOK;;;04C8; +04C8;CYRILLIC SMALL LETTER EN WITH HOOK;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER EN HOOK;;04C7;;04C7 +04C9;CYRILLIC CAPITAL LETTER EN WITH TAIL;Lu;0;L;;;;;N;;;;04CA; +04CA;CYRILLIC SMALL LETTER EN WITH TAIL;Ll;0;L;;;;;N;;;04C9;;04C9 +04CB;CYRILLIC CAPITAL LETTER KHAKASSIAN CHE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER CHE WITH LEFT DESCENDER;;;04CC; +04CC;CYRILLIC SMALL LETTER KHAKASSIAN CHE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER CHE WITH LEFT DESCENDER;;04CB;;04CB +04CD;CYRILLIC CAPITAL LETTER EM WITH TAIL;Lu;0;L;;;;;N;;;;04CE; +04CE;CYRILLIC SMALL LETTER EM WITH TAIL;Ll;0;L;;;;;N;;;04CD;;04CD +04D0;CYRILLIC CAPITAL LETTER A WITH BREVE;Lu;0;L;0410 0306;;;;N;;;;04D1; +04D1;CYRILLIC SMALL LETTER A WITH BREVE;Ll;0;L;0430 0306;;;;N;;;04D0;;04D0 +04D2;CYRILLIC CAPITAL LETTER A WITH DIAERESIS;Lu;0;L;0410 0308;;;;N;;;;04D3; +04D3;CYRILLIC SMALL LETTER A WITH DIAERESIS;Ll;0;L;0430 0308;;;;N;;;04D2;;04D2 +04D4;CYRILLIC CAPITAL LIGATURE A IE;Lu;0;L;;;;;N;;;;04D5; +04D5;CYRILLIC SMALL LIGATURE A IE;Ll;0;L;;;;;N;;;04D4;;04D4 +04D6;CYRILLIC CAPITAL LETTER IE WITH BREVE;Lu;0;L;0415 0306;;;;N;;;;04D7; +04D7;CYRILLIC SMALL LETTER IE WITH BREVE;Ll;0;L;0435 0306;;;;N;;;04D6;;04D6 +04D8;CYRILLIC CAPITAL LETTER SCHWA;Lu;0;L;;;;;N;;;;04D9; +04D9;CYRILLIC SMALL LETTER SCHWA;Ll;0;L;;;;;N;;;04D8;;04D8 +04DA;CYRILLIC CAPITAL LETTER SCHWA WITH DIAERESIS;Lu;0;L;04D8 0308;;;;N;;;;04DB; +04DB;CYRILLIC SMALL LETTER SCHWA WITH DIAERESIS;Ll;0;L;04D9 0308;;;;N;;;04DA;;04DA +04DC;CYRILLIC CAPITAL LETTER ZHE WITH DIAERESIS;Lu;0;L;0416 0308;;;;N;;;;04DD; +04DD;CYRILLIC SMALL LETTER ZHE WITH DIAERESIS;Ll;0;L;0436 0308;;;;N;;;04DC;;04DC +04DE;CYRILLIC CAPITAL LETTER ZE WITH DIAERESIS;Lu;0;L;0417 0308;;;;N;;;;04DF; +04DF;CYRILLIC SMALL LETTER ZE WITH DIAERESIS;Ll;0;L;0437 0308;;;;N;;;04DE;;04DE +04E0;CYRILLIC CAPITAL LETTER ABKHASIAN DZE;Lu;0;L;;;;;N;;;;04E1; +04E1;CYRILLIC SMALL LETTER ABKHASIAN DZE;Ll;0;L;;;;;N;;;04E0;;04E0 +04E2;CYRILLIC CAPITAL LETTER I WITH MACRON;Lu;0;L;0418 0304;;;;N;;;;04E3; +04E3;CYRILLIC SMALL LETTER I WITH MACRON;Ll;0;L;0438 0304;;;;N;;;04E2;;04E2 +04E4;CYRILLIC CAPITAL LETTER I WITH DIAERESIS;Lu;0;L;0418 0308;;;;N;;;;04E5; +04E5;CYRILLIC SMALL LETTER I WITH DIAERESIS;Ll;0;L;0438 0308;;;;N;;;04E4;;04E4 +04E6;CYRILLIC CAPITAL LETTER O WITH DIAERESIS;Lu;0;L;041E 0308;;;;N;;;;04E7; +04E7;CYRILLIC SMALL LETTER O WITH DIAERESIS;Ll;0;L;043E 0308;;;;N;;;04E6;;04E6 +04E8;CYRILLIC CAPITAL LETTER BARRED O;Lu;0;L;;;;;N;;;;04E9; +04E9;CYRILLIC SMALL LETTER BARRED O;Ll;0;L;;;;;N;;;04E8;;04E8 +04EA;CYRILLIC CAPITAL LETTER BARRED O WITH DIAERESIS;Lu;0;L;04E8 0308;;;;N;;;;04EB; +04EB;CYRILLIC SMALL LETTER BARRED O WITH DIAERESIS;Ll;0;L;04E9 0308;;;;N;;;04EA;;04EA +04EC;CYRILLIC CAPITAL LETTER E WITH DIAERESIS;Lu;0;L;042D 0308;;;;N;;;;04ED; +04ED;CYRILLIC SMALL LETTER E WITH DIAERESIS;Ll;0;L;044D 0308;;;;N;;;04EC;;04EC +04EE;CYRILLIC CAPITAL LETTER U WITH MACRON;Lu;0;L;0423 0304;;;;N;;;;04EF; +04EF;CYRILLIC SMALL LETTER U WITH MACRON;Ll;0;L;0443 0304;;;;N;;;04EE;;04EE +04F0;CYRILLIC CAPITAL LETTER U WITH DIAERESIS;Lu;0;L;0423 0308;;;;N;;;;04F1; +04F1;CYRILLIC SMALL LETTER U WITH DIAERESIS;Ll;0;L;0443 0308;;;;N;;;04F0;;04F0 +04F2;CYRILLIC CAPITAL LETTER U WITH DOUBLE ACUTE;Lu;0;L;0423 030B;;;;N;;;;04F3; +04F3;CYRILLIC SMALL LETTER U WITH DOUBLE ACUTE;Ll;0;L;0443 030B;;;;N;;;04F2;;04F2 +04F4;CYRILLIC CAPITAL LETTER CHE WITH DIAERESIS;Lu;0;L;0427 0308;;;;N;;;;04F5; +04F5;CYRILLIC SMALL LETTER CHE WITH DIAERESIS;Ll;0;L;0447 0308;;;;N;;;04F4;;04F4 +04F8;CYRILLIC CAPITAL LETTER YERU WITH DIAERESIS;Lu;0;L;042B 0308;;;;N;;;;04F9; +04F9;CYRILLIC SMALL LETTER YERU WITH DIAERESIS;Ll;0;L;044B 0308;;;;N;;;04F8;;04F8 +0500;CYRILLIC CAPITAL LETTER KOMI DE;Lu;0;L;;;;;N;;;;0501; +0501;CYRILLIC SMALL LETTER KOMI DE;Ll;0;L;;;;;N;;;0500;;0500 +0502;CYRILLIC CAPITAL LETTER KOMI DJE;Lu;0;L;;;;;N;;;;0503; +0503;CYRILLIC SMALL LETTER KOMI DJE;Ll;0;L;;;;;N;;;0502;;0502 +0504;CYRILLIC CAPITAL LETTER KOMI ZJE;Lu;0;L;;;;;N;;;;0505; +0505;CYRILLIC SMALL LETTER KOMI ZJE;Ll;0;L;;;;;N;;;0504;;0504 +0506;CYRILLIC CAPITAL LETTER KOMI DZJE;Lu;0;L;;;;;N;;;;0507; +0507;CYRILLIC SMALL LETTER KOMI DZJE;Ll;0;L;;;;;N;;;0506;;0506 +0508;CYRILLIC CAPITAL LETTER KOMI LJE;Lu;0;L;;;;;N;;;;0509; +0509;CYRILLIC SMALL LETTER KOMI LJE;Ll;0;L;;;;;N;;;0508;;0508 +050A;CYRILLIC CAPITAL LETTER KOMI NJE;Lu;0;L;;;;;N;;;;050B; +050B;CYRILLIC SMALL LETTER KOMI NJE;Ll;0;L;;;;;N;;;050A;;050A +050C;CYRILLIC CAPITAL LETTER KOMI SJE;Lu;0;L;;;;;N;;;;050D; +050D;CYRILLIC SMALL LETTER KOMI SJE;Ll;0;L;;;;;N;;;050C;;050C +050E;CYRILLIC CAPITAL LETTER KOMI TJE;Lu;0;L;;;;;N;;;;050F; +050F;CYRILLIC SMALL LETTER KOMI TJE;Ll;0;L;;;;;N;;;050E;;050E +0531;ARMENIAN CAPITAL LETTER AYB;Lu;0;L;;;;;N;;;;0561; +0532;ARMENIAN CAPITAL LETTER BEN;Lu;0;L;;;;;N;;;;0562; +0533;ARMENIAN CAPITAL LETTER GIM;Lu;0;L;;;;;N;;;;0563; +0534;ARMENIAN CAPITAL LETTER DA;Lu;0;L;;;;;N;;;;0564; +0535;ARMENIAN CAPITAL LETTER ECH;Lu;0;L;;;;;N;;;;0565; +0536;ARMENIAN CAPITAL LETTER ZA;Lu;0;L;;;;;N;;;;0566; +0537;ARMENIAN CAPITAL LETTER EH;Lu;0;L;;;;;N;;;;0567; +0538;ARMENIAN CAPITAL LETTER ET;Lu;0;L;;;;;N;;;;0568; +0539;ARMENIAN CAPITAL LETTER TO;Lu;0;L;;;;;N;;;;0569; +053A;ARMENIAN CAPITAL LETTER ZHE;Lu;0;L;;;;;N;;;;056A; +053B;ARMENIAN CAPITAL LETTER INI;Lu;0;L;;;;;N;;;;056B; +053C;ARMENIAN CAPITAL LETTER LIWN;Lu;0;L;;;;;N;;;;056C; +053D;ARMENIAN CAPITAL LETTER XEH;Lu;0;L;;;;;N;;;;056D; +053E;ARMENIAN CAPITAL LETTER CA;Lu;0;L;;;;;N;;;;056E; +053F;ARMENIAN CAPITAL LETTER KEN;Lu;0;L;;;;;N;;;;056F; +0540;ARMENIAN CAPITAL LETTER HO;Lu;0;L;;;;;N;;;;0570; +0541;ARMENIAN CAPITAL LETTER JA;Lu;0;L;;;;;N;;;;0571; +0542;ARMENIAN CAPITAL LETTER GHAD;Lu;0;L;;;;;N;ARMENIAN CAPITAL LETTER LAD;;;0572; +0543;ARMENIAN CAPITAL LETTER CHEH;Lu;0;L;;;;;N;;;;0573; +0544;ARMENIAN CAPITAL LETTER MEN;Lu;0;L;;;;;N;;;;0574; +0545;ARMENIAN CAPITAL LETTER YI;Lu;0;L;;;;;N;;;;0575; +0546;ARMENIAN CAPITAL LETTER NOW;Lu;0;L;;;;;N;;;;0576; +0547;ARMENIAN CAPITAL LETTER SHA;Lu;0;L;;;;;N;;;;0577; +0548;ARMENIAN CAPITAL LETTER VO;Lu;0;L;;;;;N;;;;0578; +0549;ARMENIAN CAPITAL LETTER CHA;Lu;0;L;;;;;N;;;;0579; +054A;ARMENIAN CAPITAL LETTER PEH;Lu;0;L;;;;;N;;;;057A; +054B;ARMENIAN CAPITAL LETTER JHEH;Lu;0;L;;;;;N;;;;057B; +054C;ARMENIAN CAPITAL LETTER RA;Lu;0;L;;;;;N;;;;057C; +054D;ARMENIAN CAPITAL LETTER SEH;Lu;0;L;;;;;N;;;;057D; +054E;ARMENIAN CAPITAL LETTER VEW;Lu;0;L;;;;;N;;;;057E; +054F;ARMENIAN CAPITAL LETTER TIWN;Lu;0;L;;;;;N;;;;057F; +0550;ARMENIAN CAPITAL LETTER REH;Lu;0;L;;;;;N;;;;0580; +0551;ARMENIAN CAPITAL LETTER CO;Lu;0;L;;;;;N;;;;0581; +0552;ARMENIAN CAPITAL LETTER YIWN;Lu;0;L;;;;;N;;;;0582; +0553;ARMENIAN CAPITAL LETTER PIWR;Lu;0;L;;;;;N;;;;0583; +0554;ARMENIAN CAPITAL LETTER KEH;Lu;0;L;;;;;N;;;;0584; +0555;ARMENIAN CAPITAL LETTER OH;Lu;0;L;;;;;N;;;;0585; +0556;ARMENIAN CAPITAL LETTER FEH;Lu;0;L;;;;;N;;;;0586; +0559;ARMENIAN MODIFIER LETTER LEFT HALF RING;Lm;0;L;;;;;N;;;;; +055A;ARMENIAN APOSTROPHE;Po;0;L;;;;;N;ARMENIAN MODIFIER LETTER RIGHT HALF RING;;;; +055B;ARMENIAN EMPHASIS MARK;Po;0;L;;;;;N;;;;; +055C;ARMENIAN EXCLAMATION MARK;Po;0;L;;;;;N;;;;; +055D;ARMENIAN COMMA;Po;0;L;;;;;N;;;;; +055E;ARMENIAN QUESTION MARK;Po;0;L;;;;;N;;;;; +055F;ARMENIAN ABBREVIATION MARK;Po;0;L;;;;;N;;;;; +0561;ARMENIAN SMALL LETTER AYB;Ll;0;L;;;;;N;;;0531;;0531 +0562;ARMENIAN SMALL LETTER BEN;Ll;0;L;;;;;N;;;0532;;0532 +0563;ARMENIAN SMALL LETTER GIM;Ll;0;L;;;;;N;;;0533;;0533 +0564;ARMENIAN SMALL LETTER DA;Ll;0;L;;;;;N;;;0534;;0534 +0565;ARMENIAN SMALL LETTER ECH;Ll;0;L;;;;;N;;;0535;;0535 +0566;ARMENIAN SMALL LETTER ZA;Ll;0;L;;;;;N;;;0536;;0536 +0567;ARMENIAN SMALL LETTER EH;Ll;0;L;;;;;N;;;0537;;0537 +0568;ARMENIAN SMALL LETTER ET;Ll;0;L;;;;;N;;;0538;;0538 +0569;ARMENIAN SMALL LETTER TO;Ll;0;L;;;;;N;;;0539;;0539 +056A;ARMENIAN SMALL LETTER ZHE;Ll;0;L;;;;;N;;;053A;;053A +056B;ARMENIAN SMALL LETTER INI;Ll;0;L;;;;;N;;;053B;;053B +056C;ARMENIAN SMALL LETTER LIWN;Ll;0;L;;;;;N;;;053C;;053C +056D;ARMENIAN SMALL LETTER XEH;Ll;0;L;;;;;N;;;053D;;053D +056E;ARMENIAN SMALL LETTER CA;Ll;0;L;;;;;N;;;053E;;053E +056F;ARMENIAN SMALL LETTER KEN;Ll;0;L;;;;;N;;;053F;;053F +0570;ARMENIAN SMALL LETTER HO;Ll;0;L;;;;;N;;;0540;;0540 +0571;ARMENIAN SMALL LETTER JA;Ll;0;L;;;;;N;;;0541;;0541 +0572;ARMENIAN SMALL LETTER GHAD;Ll;0;L;;;;;N;ARMENIAN SMALL LETTER LAD;;0542;;0542 +0573;ARMENIAN SMALL LETTER CHEH;Ll;0;L;;;;;N;;;0543;;0543 +0574;ARMENIAN SMALL LETTER MEN;Ll;0;L;;;;;N;;;0544;;0544 +0575;ARMENIAN SMALL LETTER YI;Ll;0;L;;;;;N;;;0545;;0545 +0576;ARMENIAN SMALL LETTER NOW;Ll;0;L;;;;;N;;;0546;;0546 +0577;ARMENIAN SMALL LETTER SHA;Ll;0;L;;;;;N;;;0547;;0547 +0578;ARMENIAN SMALL LETTER VO;Ll;0;L;;;;;N;;;0548;;0548 +0579;ARMENIAN SMALL LETTER CHA;Ll;0;L;;;;;N;;;0549;;0549 +057A;ARMENIAN SMALL LETTER PEH;Ll;0;L;;;;;N;;;054A;;054A +057B;ARMENIAN SMALL LETTER JHEH;Ll;0;L;;;;;N;;;054B;;054B +057C;ARMENIAN SMALL LETTER RA;Ll;0;L;;;;;N;;;054C;;054C +057D;ARMENIAN SMALL LETTER SEH;Ll;0;L;;;;;N;;;054D;;054D +057E;ARMENIAN SMALL LETTER VEW;Ll;0;L;;;;;N;;;054E;;054E +057F;ARMENIAN SMALL LETTER TIWN;Ll;0;L;;;;;N;;;054F;;054F +0580;ARMENIAN SMALL LETTER REH;Ll;0;L;;;;;N;;;0550;;0550 +0581;ARMENIAN SMALL LETTER CO;Ll;0;L;;;;;N;;;0551;;0551 +0582;ARMENIAN SMALL LETTER YIWN;Ll;0;L;;;;;N;;;0552;;0552 +0583;ARMENIAN SMALL LETTER PIWR;Ll;0;L;;;;;N;;;0553;;0553 +0584;ARMENIAN SMALL LETTER KEH;Ll;0;L;;;;;N;;;0554;;0554 +0585;ARMENIAN SMALL LETTER OH;Ll;0;L;;;;;N;;;0555;;0555 +0586;ARMENIAN SMALL LETTER FEH;Ll;0;L;;;;;N;;;0556;;0556 +0587;ARMENIAN SMALL LIGATURE ECH YIWN;Ll;0;L;<compat> 0565 0582;;;;N;;;;; +0589;ARMENIAN FULL STOP;Po;0;L;;;;;N;ARMENIAN PERIOD;;;; +058A;ARMENIAN HYPHEN;Pd;0;ON;;;;;N;;;;; +0591;HEBREW ACCENT ETNAHTA;Mn;220;NSM;;;;;N;;;;; +0592;HEBREW ACCENT SEGOL;Mn;230;NSM;;;;;N;;;;; +0593;HEBREW ACCENT SHALSHELET;Mn;230;NSM;;;;;N;;;;; +0594;HEBREW ACCENT ZAQEF QATAN;Mn;230;NSM;;;;;N;;;;; +0595;HEBREW ACCENT ZAQEF GADOL;Mn;230;NSM;;;;;N;;;;; +0596;HEBREW ACCENT TIPEHA;Mn;220;NSM;;;;;N;;*;;; +0597;HEBREW ACCENT REVIA;Mn;230;NSM;;;;;N;;;;; +0598;HEBREW ACCENT ZARQA;Mn;230;NSM;;;;;N;;*;;; +0599;HEBREW ACCENT PASHTA;Mn;230;NSM;;;;;N;;;;; +059A;HEBREW ACCENT YETIV;Mn;222;NSM;;;;;N;;;;; +059B;HEBREW ACCENT TEVIR;Mn;220;NSM;;;;;N;;;;; +059C;HEBREW ACCENT GERESH;Mn;230;NSM;;;;;N;;;;; +059D;HEBREW ACCENT GERESH MUQDAM;Mn;230;NSM;;;;;N;;;;; +059E;HEBREW ACCENT GERSHAYIM;Mn;230;NSM;;;;;N;;;;; +059F;HEBREW ACCENT QARNEY PARA;Mn;230;NSM;;;;;N;;;;; +05A0;HEBREW ACCENT TELISHA GEDOLA;Mn;230;NSM;;;;;N;;;;; +05A1;HEBREW ACCENT PAZER;Mn;230;NSM;;;;;N;;;;; +05A3;HEBREW ACCENT MUNAH;Mn;220;NSM;;;;;N;;;;; +05A4;HEBREW ACCENT MAHAPAKH;Mn;220;NSM;;;;;N;;;;; +05A5;HEBREW ACCENT MERKHA;Mn;220;NSM;;;;;N;;*;;; +05A6;HEBREW ACCENT MERKHA KEFULA;Mn;220;NSM;;;;;N;;;;; +05A7;HEBREW ACCENT DARGA;Mn;220;NSM;;;;;N;;;;; +05A8;HEBREW ACCENT QADMA;Mn;230;NSM;;;;;N;;*;;; +05A9;HEBREW ACCENT TELISHA QETANA;Mn;230;NSM;;;;;N;;;;; +05AA;HEBREW ACCENT YERAH BEN YOMO;Mn;220;NSM;;;;;N;;*;;; +05AB;HEBREW ACCENT OLE;Mn;230;NSM;;;;;N;;;;; +05AC;HEBREW ACCENT ILUY;Mn;230;NSM;;;;;N;;;;; +05AD;HEBREW ACCENT DEHI;Mn;222;NSM;;;;;N;;;;; +05AE;HEBREW ACCENT ZINOR;Mn;228;NSM;;;;;N;;;;; +05AF;HEBREW MARK MASORA CIRCLE;Mn;230;NSM;;;;;N;;;;; +05B0;HEBREW POINT SHEVA;Mn;10;NSM;;;;;N;;;;; +05B1;HEBREW POINT HATAF SEGOL;Mn;11;NSM;;;;;N;;;;; +05B2;HEBREW POINT HATAF PATAH;Mn;12;NSM;;;;;N;;;;; +05B3;HEBREW POINT HATAF QAMATS;Mn;13;NSM;;;;;N;;;;; +05B4;HEBREW POINT HIRIQ;Mn;14;NSM;;;;;N;;;;; +05B5;HEBREW POINT TSERE;Mn;15;NSM;;;;;N;;;;; +05B6;HEBREW POINT SEGOL;Mn;16;NSM;;;;;N;;;;; +05B7;HEBREW POINT PATAH;Mn;17;NSM;;;;;N;;;;; +05B8;HEBREW POINT QAMATS;Mn;18;NSM;;;;;N;;;;; +05B9;HEBREW POINT HOLAM;Mn;19;NSM;;;;;N;;;;; +05BB;HEBREW POINT QUBUTS;Mn;20;NSM;;;;;N;;;;; +05BC;HEBREW POINT DAGESH OR MAPIQ;Mn;21;NSM;;;;;N;HEBREW POINT DAGESH;or shuruq;;; +05BD;HEBREW POINT METEG;Mn;22;NSM;;;;;N;;*;;; +05BE;HEBREW PUNCTUATION MAQAF;Po;0;R;;;;;N;;;;; +05BF;HEBREW POINT RAFE;Mn;23;NSM;;;;;N;;;;; +05C0;HEBREW PUNCTUATION PASEQ;Po;0;R;;;;;N;HEBREW POINT PASEQ;*;;; +05C1;HEBREW POINT SHIN DOT;Mn;24;NSM;;;;;N;;;;; +05C2;HEBREW POINT SIN DOT;Mn;25;NSM;;;;;N;;;;; +05C3;HEBREW PUNCTUATION SOF PASUQ;Po;0;R;;;;;N;;*;;; +05C4;HEBREW MARK UPPER DOT;Mn;230;NSM;;;;;N;;;;; +05D0;HEBREW LETTER ALEF;Lo;0;R;;;;;N;;;;; +05D1;HEBREW LETTER BET;Lo;0;R;;;;;N;;;;; +05D2;HEBREW LETTER GIMEL;Lo;0;R;;;;;N;;;;; +05D3;HEBREW LETTER DALET;Lo;0;R;;;;;N;;;;; +05D4;HEBREW LETTER HE;Lo;0;R;;;;;N;;;;; +05D5;HEBREW LETTER VAV;Lo;0;R;;;;;N;;;;; +05D6;HEBREW LETTER ZAYIN;Lo;0;R;;;;;N;;;;; +05D7;HEBREW LETTER HET;Lo;0;R;;;;;N;;;;; +05D8;HEBREW LETTER TET;Lo;0;R;;;;;N;;;;; +05D9;HEBREW LETTER YOD;Lo;0;R;;;;;N;;;;; +05DA;HEBREW LETTER FINAL KAF;Lo;0;R;;;;;N;;;;; +05DB;HEBREW LETTER KAF;Lo;0;R;;;;;N;;;;; +05DC;HEBREW LETTER LAMED;Lo;0;R;;;;;N;;;;; +05DD;HEBREW LETTER FINAL MEM;Lo;0;R;;;;;N;;;;; +05DE;HEBREW LETTER MEM;Lo;0;R;;;;;N;;;;; +05DF;HEBREW LETTER FINAL NUN;Lo;0;R;;;;;N;;;;; +05E0;HEBREW LETTER NUN;Lo;0;R;;;;;N;;;;; +05E1;HEBREW LETTER SAMEKH;Lo;0;R;;;;;N;;;;; +05E2;HEBREW LETTER AYIN;Lo;0;R;;;;;N;;;;; +05E3;HEBREW LETTER FINAL PE;Lo;0;R;;;;;N;;;;; +05E4;HEBREW LETTER PE;Lo;0;R;;;;;N;;;;; +05E5;HEBREW LETTER FINAL TSADI;Lo;0;R;;;;;N;;;;; +05E6;HEBREW LETTER TSADI;Lo;0;R;;;;;N;;;;; +05E7;HEBREW LETTER QOF;Lo;0;R;;;;;N;;;;; +05E8;HEBREW LETTER RESH;Lo;0;R;;;;;N;;;;; +05E9;HEBREW LETTER SHIN;Lo;0;R;;;;;N;;;;; +05EA;HEBREW LETTER TAV;Lo;0;R;;;;;N;;;;; +05F0;HEBREW LIGATURE YIDDISH DOUBLE VAV;Lo;0;R;;;;;N;HEBREW LETTER DOUBLE VAV;;;; +05F1;HEBREW LIGATURE YIDDISH VAV YOD;Lo;0;R;;;;;N;HEBREW LETTER VAV YOD;;;; +05F2;HEBREW LIGATURE YIDDISH DOUBLE YOD;Lo;0;R;;;;;N;HEBREW LETTER DOUBLE YOD;;;; +05F3;HEBREW PUNCTUATION GERESH;Po;0;R;;;;;N;;;;; +05F4;HEBREW PUNCTUATION GERSHAYIM;Po;0;R;;;;;N;;;;; +060C;ARABIC COMMA;Po;0;CS;;;;;N;;;;; +061B;ARABIC SEMICOLON;Po;0;AL;;;;;N;;;;; +061F;ARABIC QUESTION MARK;Po;0;AL;;;;;N;;;;; +0621;ARABIC LETTER HAMZA;Lo;0;AL;;;;;N;ARABIC LETTER HAMZAH;;;; +0622;ARABIC LETTER ALEF WITH MADDA ABOVE;Lo;0;AL;0627 0653;;;;N;ARABIC LETTER MADDAH ON ALEF;;;; +0623;ARABIC LETTER ALEF WITH HAMZA ABOVE;Lo;0;AL;0627 0654;;;;N;ARABIC LETTER HAMZAH ON ALEF;;;; +0624;ARABIC LETTER WAW WITH HAMZA ABOVE;Lo;0;AL;0648 0654;;;;N;ARABIC LETTER HAMZAH ON WAW;;;; +0625;ARABIC LETTER ALEF WITH HAMZA BELOW;Lo;0;AL;0627 0655;;;;N;ARABIC LETTER HAMZAH UNDER ALEF;;;; +0626;ARABIC LETTER YEH WITH HAMZA ABOVE;Lo;0;AL;064A 0654;;;;N;ARABIC LETTER HAMZAH ON YA;;;; +0627;ARABIC LETTER ALEF;Lo;0;AL;;;;;N;;;;; +0628;ARABIC LETTER BEH;Lo;0;AL;;;;;N;ARABIC LETTER BAA;;;; +0629;ARABIC LETTER TEH MARBUTA;Lo;0;AL;;;;;N;ARABIC LETTER TAA MARBUTAH;;;; +062A;ARABIC LETTER TEH;Lo;0;AL;;;;;N;ARABIC LETTER TAA;;;; +062B;ARABIC LETTER THEH;Lo;0;AL;;;;;N;ARABIC LETTER THAA;;;; +062C;ARABIC LETTER JEEM;Lo;0;AL;;;;;N;;;;; +062D;ARABIC LETTER HAH;Lo;0;AL;;;;;N;ARABIC LETTER HAA;;;; +062E;ARABIC LETTER KHAH;Lo;0;AL;;;;;N;ARABIC LETTER KHAA;;;; +062F;ARABIC LETTER DAL;Lo;0;AL;;;;;N;;;;; +0630;ARABIC LETTER THAL;Lo;0;AL;;;;;N;;;;; +0631;ARABIC LETTER REH;Lo;0;AL;;;;;N;ARABIC LETTER RA;;;; +0632;ARABIC LETTER ZAIN;Lo;0;AL;;;;;N;;;;; +0633;ARABIC LETTER SEEN;Lo;0;AL;;;;;N;;;;; +0634;ARABIC LETTER SHEEN;Lo;0;AL;;;;;N;;;;; +0635;ARABIC LETTER SAD;Lo;0;AL;;;;;N;;;;; +0636;ARABIC LETTER DAD;Lo;0;AL;;;;;N;;;;; +0637;ARABIC LETTER TAH;Lo;0;AL;;;;;N;;;;; +0638;ARABIC LETTER ZAH;Lo;0;AL;;;;;N;ARABIC LETTER DHAH;;;; +0639;ARABIC LETTER AIN;Lo;0;AL;;;;;N;;;;; +063A;ARABIC LETTER GHAIN;Lo;0;AL;;;;;N;;;;; +0640;ARABIC TATWEEL;Lm;0;AL;;;;;N;;;;; +0641;ARABIC LETTER FEH;Lo;0;AL;;;;;N;ARABIC LETTER FA;;;; +0642;ARABIC LETTER QAF;Lo;0;AL;;;;;N;;;;; +0643;ARABIC LETTER KAF;Lo;0;AL;;;;;N;ARABIC LETTER CAF;;;; +0644;ARABIC LETTER LAM;Lo;0;AL;;;;;N;;;;; +0645;ARABIC LETTER MEEM;Lo;0;AL;;;;;N;;;;; +0646;ARABIC LETTER NOON;Lo;0;AL;;;;;N;;;;; +0647;ARABIC LETTER HEH;Lo;0;AL;;;;;N;ARABIC LETTER HA;;;; +0648;ARABIC LETTER WAW;Lo;0;AL;;;;;N;;;;; +0649;ARABIC LETTER ALEF MAKSURA;Lo;0;AL;;;;;N;ARABIC LETTER ALEF MAQSURAH;;;; +064A;ARABIC LETTER YEH;Lo;0;AL;;;;;N;ARABIC LETTER YA;;;; +064B;ARABIC FATHATAN;Mn;27;NSM;;;;;N;;;;; +064C;ARABIC DAMMATAN;Mn;28;NSM;;;;;N;;;;; +064D;ARABIC KASRATAN;Mn;29;NSM;;;;;N;;;;; +064E;ARABIC FATHA;Mn;30;NSM;;;;;N;ARABIC FATHAH;;;; +064F;ARABIC DAMMA;Mn;31;NSM;;;;;N;ARABIC DAMMAH;;;; +0650;ARABIC KASRA;Mn;32;NSM;;;;;N;ARABIC KASRAH;;;; +0651;ARABIC SHADDA;Mn;33;NSM;;;;;N;ARABIC SHADDAH;;;; +0652;ARABIC SUKUN;Mn;34;NSM;;;;;N;;;;; +0653;ARABIC MADDAH ABOVE;Mn;230;NSM;;;;;N;;;;; +0654;ARABIC HAMZA ABOVE;Mn;230;NSM;;;;;N;;;;; +0655;ARABIC HAMZA BELOW;Mn;220;NSM;;;;;N;;;;; +0660;ARABIC-INDIC DIGIT ZERO;Nd;0;AN;;0;0;0;N;;;;; +0661;ARABIC-INDIC DIGIT ONE;Nd;0;AN;;1;1;1;N;;;;; +0662;ARABIC-INDIC DIGIT TWO;Nd;0;AN;;2;2;2;N;;;;; +0663;ARABIC-INDIC DIGIT THREE;Nd;0;AN;;3;3;3;N;;;;; +0664;ARABIC-INDIC DIGIT FOUR;Nd;0;AN;;4;4;4;N;;;;; +0665;ARABIC-INDIC DIGIT FIVE;Nd;0;AN;;5;5;5;N;;;;; +0666;ARABIC-INDIC DIGIT SIX;Nd;0;AN;;6;6;6;N;;;;; +0667;ARABIC-INDIC DIGIT SEVEN;Nd;0;AN;;7;7;7;N;;;;; +0668;ARABIC-INDIC DIGIT EIGHT;Nd;0;AN;;8;8;8;N;;;;; +0669;ARABIC-INDIC DIGIT NINE;Nd;0;AN;;9;9;9;N;;;;; +066A;ARABIC PERCENT SIGN;Po;0;ET;;;;;N;;;;; +066B;ARABIC DECIMAL SEPARATOR;Po;0;AN;;;;;N;;;;; +066C;ARABIC THOUSANDS SEPARATOR;Po;0;AN;;;;;N;;;;; +066D;ARABIC FIVE POINTED STAR;Po;0;AL;;;;;N;;;;; +066E;ARABIC LETTER DOTLESS BEH;Lo;0;AL;;;;;N;;;;; +066F;ARABIC LETTER DOTLESS QAF;Lo;0;AL;;;;;N;;;;; +0670;ARABIC LETTER SUPERSCRIPT ALEF;Mn;35;NSM;;;;;N;ARABIC ALEF ABOVE;;;; +0671;ARABIC LETTER ALEF WASLA;Lo;0;AL;;;;;N;ARABIC LETTER HAMZAT WASL ON ALEF;;;; +0672;ARABIC LETTER ALEF WITH WAVY HAMZA ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER WAVY HAMZAH ON ALEF;;;; +0673;ARABIC LETTER ALEF WITH WAVY HAMZA BELOW;Lo;0;AL;;;;;N;ARABIC LETTER WAVY HAMZAH UNDER ALEF;;;; +0674;ARABIC LETTER HIGH HAMZA;Lo;0;AL;;;;;N;ARABIC LETTER HIGH HAMZAH;;;; +0675;ARABIC LETTER HIGH HAMZA ALEF;Lo;0;AL;<compat> 0627 0674;;;;N;ARABIC LETTER HIGH HAMZAH ALEF;;;; +0676;ARABIC LETTER HIGH HAMZA WAW;Lo;0;AL;<compat> 0648 0674;;;;N;ARABIC LETTER HIGH HAMZAH WAW;;;; +0677;ARABIC LETTER U WITH HAMZA ABOVE;Lo;0;AL;<compat> 06C7 0674;;;;N;ARABIC LETTER HIGH HAMZAH WAW WITH DAMMAH;;;; +0678;ARABIC LETTER HIGH HAMZA YEH;Lo;0;AL;<compat> 064A 0674;;;;N;ARABIC LETTER HIGH HAMZAH YA;;;; +0679;ARABIC LETTER TTEH;Lo;0;AL;;;;;N;ARABIC LETTER TAA WITH SMALL TAH;;;; +067A;ARABIC LETTER TTEHEH;Lo;0;AL;;;;;N;ARABIC LETTER TAA WITH TWO DOTS VERTICAL ABOVE;;;; +067B;ARABIC LETTER BEEH;Lo;0;AL;;;;;N;ARABIC LETTER BAA WITH TWO DOTS VERTICAL BELOW;;;; +067C;ARABIC LETTER TEH WITH RING;Lo;0;AL;;;;;N;ARABIC LETTER TAA WITH RING;;;; +067D;ARABIC LETTER TEH WITH THREE DOTS ABOVE DOWNWARDS;Lo;0;AL;;;;;N;ARABIC LETTER TAA WITH THREE DOTS ABOVE DOWNWARD;;;; +067E;ARABIC LETTER PEH;Lo;0;AL;;;;;N;ARABIC LETTER TAA WITH THREE DOTS BELOW;;;; +067F;ARABIC LETTER TEHEH;Lo;0;AL;;;;;N;ARABIC LETTER TAA WITH FOUR DOTS ABOVE;;;; +0680;ARABIC LETTER BEHEH;Lo;0;AL;;;;;N;ARABIC LETTER BAA WITH FOUR DOTS BELOW;;;; +0681;ARABIC LETTER HAH WITH HAMZA ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER HAMZAH ON HAA;;;; +0682;ARABIC LETTER HAH WITH TWO DOTS VERTICAL ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER HAA WITH TWO DOTS VERTICAL ABOVE;;;; +0683;ARABIC LETTER NYEH;Lo;0;AL;;;;;N;ARABIC LETTER HAA WITH MIDDLE TWO DOTS;;;; +0684;ARABIC LETTER DYEH;Lo;0;AL;;;;;N;ARABIC LETTER HAA WITH MIDDLE TWO DOTS VERTICAL;;;; +0685;ARABIC LETTER HAH WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER HAA WITH THREE DOTS ABOVE;;;; +0686;ARABIC LETTER TCHEH;Lo;0;AL;;;;;N;ARABIC LETTER HAA WITH MIDDLE THREE DOTS DOWNWARD;;;; +0687;ARABIC LETTER TCHEHEH;Lo;0;AL;;;;;N;ARABIC LETTER HAA WITH MIDDLE FOUR DOTS;;;; +0688;ARABIC LETTER DDAL;Lo;0;AL;;;;;N;ARABIC LETTER DAL WITH SMALL TAH;;;; +0689;ARABIC LETTER DAL WITH RING;Lo;0;AL;;;;;N;;;;; +068A;ARABIC LETTER DAL WITH DOT BELOW;Lo;0;AL;;;;;N;;;;; +068B;ARABIC LETTER DAL WITH DOT BELOW AND SMALL TAH;Lo;0;AL;;;;;N;;;;; +068C;ARABIC LETTER DAHAL;Lo;0;AL;;;;;N;ARABIC LETTER DAL WITH TWO DOTS ABOVE;;;; +068D;ARABIC LETTER DDAHAL;Lo;0;AL;;;;;N;ARABIC LETTER DAL WITH TWO DOTS BELOW;;;; +068E;ARABIC LETTER DUL;Lo;0;AL;;;;;N;ARABIC LETTER DAL WITH THREE DOTS ABOVE;;;; +068F;ARABIC LETTER DAL WITH THREE DOTS ABOVE DOWNWARDS;Lo;0;AL;;;;;N;ARABIC LETTER DAL WITH THREE DOTS ABOVE DOWNWARD;;;; +0690;ARABIC LETTER DAL WITH FOUR DOTS ABOVE;Lo;0;AL;;;;;N;;;;; +0691;ARABIC LETTER RREH;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH SMALL TAH;;;; +0692;ARABIC LETTER REH WITH SMALL V;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH SMALL V;;;; +0693;ARABIC LETTER REH WITH RING;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH RING;;;; +0694;ARABIC LETTER REH WITH DOT BELOW;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH DOT BELOW;;;; +0695;ARABIC LETTER REH WITH SMALL V BELOW;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH SMALL V BELOW;;;; +0696;ARABIC LETTER REH WITH DOT BELOW AND DOT ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH DOT BELOW AND DOT ABOVE;;;; +0697;ARABIC LETTER REH WITH TWO DOTS ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH TWO DOTS ABOVE;;;; +0698;ARABIC LETTER JEH;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH THREE DOTS ABOVE;;;; +0699;ARABIC LETTER REH WITH FOUR DOTS ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH FOUR DOTS ABOVE;;;; +069A;ARABIC LETTER SEEN WITH DOT BELOW AND DOT ABOVE;Lo;0;AL;;;;;N;;;;; +069B;ARABIC LETTER SEEN WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;;;;; +069C;ARABIC LETTER SEEN WITH THREE DOTS BELOW AND THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;; +069D;ARABIC LETTER SAD WITH TWO DOTS BELOW;Lo;0;AL;;;;;N;;;;; +069E;ARABIC LETTER SAD WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;; +069F;ARABIC LETTER TAH WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;; +06A0;ARABIC LETTER AIN WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;; +06A1;ARABIC LETTER DOTLESS FEH;Lo;0;AL;;;;;N;ARABIC LETTER DOTLESS FA;;;; +06A2;ARABIC LETTER FEH WITH DOT MOVED BELOW;Lo;0;AL;;;;;N;ARABIC LETTER FA WITH DOT MOVED BELOW;;;; +06A3;ARABIC LETTER FEH WITH DOT BELOW;Lo;0;AL;;;;;N;ARABIC LETTER FA WITH DOT BELOW;;;; +06A4;ARABIC LETTER VEH;Lo;0;AL;;;;;N;ARABIC LETTER FA WITH THREE DOTS ABOVE;;;; +06A5;ARABIC LETTER FEH WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;ARABIC LETTER FA WITH THREE DOTS BELOW;;;; +06A6;ARABIC LETTER PEHEH;Lo;0;AL;;;;;N;ARABIC LETTER FA WITH FOUR DOTS ABOVE;;;; +06A7;ARABIC LETTER QAF WITH DOT ABOVE;Lo;0;AL;;;;;N;;;;; +06A8;ARABIC LETTER QAF WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;; +06A9;ARABIC LETTER KEHEH;Lo;0;AL;;;;;N;ARABIC LETTER OPEN CAF;;;; +06AA;ARABIC LETTER SWASH KAF;Lo;0;AL;;;;;N;ARABIC LETTER SWASH CAF;;;; +06AB;ARABIC LETTER KAF WITH RING;Lo;0;AL;;;;;N;ARABIC LETTER CAF WITH RING;;;; +06AC;ARABIC LETTER KAF WITH DOT ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER CAF WITH DOT ABOVE;;;; +06AD;ARABIC LETTER NG;Lo;0;AL;;;;;N;ARABIC LETTER CAF WITH THREE DOTS ABOVE;;;; +06AE;ARABIC LETTER KAF WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;ARABIC LETTER CAF WITH THREE DOTS BELOW;;;; +06AF;ARABIC LETTER GAF;Lo;0;AL;;;;;N;;*;;; +06B0;ARABIC LETTER GAF WITH RING;Lo;0;AL;;;;;N;;;;; +06B1;ARABIC LETTER NGOEH;Lo;0;AL;;;;;N;ARABIC LETTER GAF WITH TWO DOTS ABOVE;;;; +06B2;ARABIC LETTER GAF WITH TWO DOTS BELOW;Lo;0;AL;;;;;N;;;;; +06B3;ARABIC LETTER GUEH;Lo;0;AL;;;;;N;ARABIC LETTER GAF WITH TWO DOTS VERTICAL BELOW;;;; +06B4;ARABIC LETTER GAF WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;; +06B5;ARABIC LETTER LAM WITH SMALL V;Lo;0;AL;;;;;N;;;;; +06B6;ARABIC LETTER LAM WITH DOT ABOVE;Lo;0;AL;;;;;N;;;;; +06B7;ARABIC LETTER LAM WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;; +06B8;ARABIC LETTER LAM WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;;;;; +06B9;ARABIC LETTER NOON WITH DOT BELOW;Lo;0;AL;;;;;N;;;;; +06BA;ARABIC LETTER NOON GHUNNA;Lo;0;AL;;;;;N;ARABIC LETTER DOTLESS NOON;;;; +06BB;ARABIC LETTER RNOON;Lo;0;AL;;;;;N;ARABIC LETTER DOTLESS NOON WITH SMALL TAH;;;; +06BC;ARABIC LETTER NOON WITH RING;Lo;0;AL;;;;;N;;;;; +06BD;ARABIC LETTER NOON WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;; +06BE;ARABIC LETTER HEH DOACHASHMEE;Lo;0;AL;;;;;N;ARABIC LETTER KNOTTED HA;;;; +06BF;ARABIC LETTER TCHEH WITH DOT ABOVE;Lo;0;AL;;;;;N;;;;; +06C0;ARABIC LETTER HEH WITH YEH ABOVE;Lo;0;AL;06D5 0654;;;;N;ARABIC LETTER HAMZAH ON HA;;;; +06C1;ARABIC LETTER HEH GOAL;Lo;0;AL;;;;;N;ARABIC LETTER HA GOAL;;;; +06C2;ARABIC LETTER HEH GOAL WITH HAMZA ABOVE;Lo;0;AL;06C1 0654;;;;N;ARABIC LETTER HAMZAH ON HA GOAL;;;; +06C3;ARABIC LETTER TEH MARBUTA GOAL;Lo;0;AL;;;;;N;ARABIC LETTER TAA MARBUTAH GOAL;;;; +06C4;ARABIC LETTER WAW WITH RING;Lo;0;AL;;;;;N;;;;; +06C5;ARABIC LETTER KIRGHIZ OE;Lo;0;AL;;;;;N;ARABIC LETTER WAW WITH BAR;;;; +06C6;ARABIC LETTER OE;Lo;0;AL;;;;;N;ARABIC LETTER WAW WITH SMALL V;;;; +06C7;ARABIC LETTER U;Lo;0;AL;;;;;N;ARABIC LETTER WAW WITH DAMMAH;;;; +06C8;ARABIC LETTER YU;Lo;0;AL;;;;;N;ARABIC LETTER WAW WITH ALEF ABOVE;;;; +06C9;ARABIC LETTER KIRGHIZ YU;Lo;0;AL;;;;;N;ARABIC LETTER WAW WITH INVERTED SMALL V;;;; +06CA;ARABIC LETTER WAW WITH TWO DOTS ABOVE;Lo;0;AL;;;;;N;;;;; +06CB;ARABIC LETTER VE;Lo;0;AL;;;;;N;ARABIC LETTER WAW WITH THREE DOTS ABOVE;;;; +06CC;ARABIC LETTER FARSI YEH;Lo;0;AL;;;;;N;ARABIC LETTER DOTLESS YA;;;; +06CD;ARABIC LETTER YEH WITH TAIL;Lo;0;AL;;;;;N;ARABIC LETTER YA WITH TAIL;;;; +06CE;ARABIC LETTER YEH WITH SMALL V;Lo;0;AL;;;;;N;ARABIC LETTER YA WITH SMALL V;;;; +06CF;ARABIC LETTER WAW WITH DOT ABOVE;Lo;0;AL;;;;;N;;;;; +06D0;ARABIC LETTER E;Lo;0;AL;;;;;N;ARABIC LETTER YA WITH TWO DOTS VERTICAL BELOW;*;;; +06D1;ARABIC LETTER YEH WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;ARABIC LETTER YA WITH THREE DOTS BELOW;;;; +06D2;ARABIC LETTER YEH BARREE;Lo;0;AL;;;;;N;ARABIC LETTER YA BARREE;;;; +06D3;ARABIC LETTER YEH BARREE WITH HAMZA ABOVE;Lo;0;AL;06D2 0654;;;;N;ARABIC LETTER HAMZAH ON YA BARREE;;;; +06D4;ARABIC FULL STOP;Po;0;AL;;;;;N;ARABIC PERIOD;;;; +06D5;ARABIC LETTER AE;Lo;0;AL;;;;;N;;;;; +06D6;ARABIC SMALL HIGH LIGATURE SAD WITH LAM WITH ALEF MAKSURA;Mn;230;NSM;;;;;N;;;;; +06D7;ARABIC SMALL HIGH LIGATURE QAF WITH LAM WITH ALEF MAKSURA;Mn;230;NSM;;;;;N;;;;; +06D8;ARABIC SMALL HIGH MEEM INITIAL FORM;Mn;230;NSM;;;;;N;;;;; +06D9;ARABIC SMALL HIGH LAM ALEF;Mn;230;NSM;;;;;N;;;;; +06DA;ARABIC SMALL HIGH JEEM;Mn;230;NSM;;;;;N;;;;; +06DB;ARABIC SMALL HIGH THREE DOTS;Mn;230;NSM;;;;;N;;;;; +06DC;ARABIC SMALL HIGH SEEN;Mn;230;NSM;;;;;N;;;;; +06DD;ARABIC END OF AYAH;Cf;0;AL;;;;;N;;;;; +06DE;ARABIC START OF RUB EL HIZB;Me;0;NSM;;;;;N;;;;; +06DF;ARABIC SMALL HIGH ROUNDED ZERO;Mn;230;NSM;;;;;N;;;;; +06E0;ARABIC SMALL HIGH UPRIGHT RECTANGULAR ZERO;Mn;230;NSM;;;;;N;;;;; +06E1;ARABIC SMALL HIGH DOTLESS HEAD OF KHAH;Mn;230;NSM;;;;;N;;;;; +06E2;ARABIC SMALL HIGH MEEM ISOLATED FORM;Mn;230;NSM;;;;;N;;;;; +06E3;ARABIC SMALL LOW SEEN;Mn;220;NSM;;;;;N;;;;; +06E4;ARABIC SMALL HIGH MADDA;Mn;230;NSM;;;;;N;;;;; +06E5;ARABIC SMALL WAW;Lm;0;AL;;;;;N;;;;; +06E6;ARABIC SMALL YEH;Lm;0;AL;;;;;N;;;;; +06E7;ARABIC SMALL HIGH YEH;Mn;230;NSM;;;;;N;;;;; +06E8;ARABIC SMALL HIGH NOON;Mn;230;NSM;;;;;N;;;;; +06E9;ARABIC PLACE OF SAJDAH;So;0;ON;;;;;N;;;;; +06EA;ARABIC EMPTY CENTRE LOW STOP;Mn;220;NSM;;;;;N;;;;; +06EB;ARABIC EMPTY CENTRE HIGH STOP;Mn;230;NSM;;;;;N;;;;; +06EC;ARABIC ROUNDED HIGH STOP WITH FILLED CENTRE;Mn;230;NSM;;;;;N;;;;; +06ED;ARABIC SMALL LOW MEEM;Mn;220;NSM;;;;;N;;;;; +06F0;EXTENDED ARABIC-INDIC DIGIT ZERO;Nd;0;EN;;0;0;0;N;EASTERN ARABIC-INDIC DIGIT ZERO;;;; +06F1;EXTENDED ARABIC-INDIC DIGIT ONE;Nd;0;EN;;1;1;1;N;EASTERN ARABIC-INDIC DIGIT ONE;;;; +06F2;EXTENDED ARABIC-INDIC DIGIT TWO;Nd;0;EN;;2;2;2;N;EASTERN ARABIC-INDIC DIGIT TWO;;;; +06F3;EXTENDED ARABIC-INDIC DIGIT THREE;Nd;0;EN;;3;3;3;N;EASTERN ARABIC-INDIC DIGIT THREE;;;; +06F4;EXTENDED ARABIC-INDIC DIGIT FOUR;Nd;0;EN;;4;4;4;N;EASTERN ARABIC-INDIC DIGIT FOUR;;;; +06F5;EXTENDED ARABIC-INDIC DIGIT FIVE;Nd;0;EN;;5;5;5;N;EASTERN ARABIC-INDIC DIGIT FIVE;;;; +06F6;EXTENDED ARABIC-INDIC DIGIT SIX;Nd;0;EN;;6;6;6;N;EASTERN ARABIC-INDIC DIGIT SIX;;;; +06F7;EXTENDED ARABIC-INDIC DIGIT SEVEN;Nd;0;EN;;7;7;7;N;EASTERN ARABIC-INDIC DIGIT SEVEN;;;; +06F8;EXTENDED ARABIC-INDIC DIGIT EIGHT;Nd;0;EN;;8;8;8;N;EASTERN ARABIC-INDIC DIGIT EIGHT;;;; +06F9;EXTENDED ARABIC-INDIC DIGIT NINE;Nd;0;EN;;9;9;9;N;EASTERN ARABIC-INDIC DIGIT NINE;;;; +06FA;ARABIC LETTER SHEEN WITH DOT BELOW;Lo;0;AL;;;;;N;;;;; +06FB;ARABIC LETTER DAD WITH DOT BELOW;Lo;0;AL;;;;;N;;;;; +06FC;ARABIC LETTER GHAIN WITH DOT BELOW;Lo;0;AL;;;;;N;;;;; +06FD;ARABIC SIGN SINDHI AMPERSAND;So;0;AL;;;;;N;;;;; +06FE;ARABIC SIGN SINDHI POSTPOSITION MEN;So;0;AL;;;;;N;;;;; +0700;SYRIAC END OF PARAGRAPH;Po;0;AL;;;;;N;;;;; +0701;SYRIAC SUPRALINEAR FULL STOP;Po;0;AL;;;;;N;;;;; +0702;SYRIAC SUBLINEAR FULL STOP;Po;0;AL;;;;;N;;;;; +0703;SYRIAC SUPRALINEAR COLON;Po;0;AL;;;;;N;;;;; +0704;SYRIAC SUBLINEAR COLON;Po;0;AL;;;;;N;;;;; +0705;SYRIAC HORIZONTAL COLON;Po;0;AL;;;;;N;;;;; +0706;SYRIAC COLON SKEWED LEFT;Po;0;AL;;;;;N;;;;; +0707;SYRIAC COLON SKEWED RIGHT;Po;0;AL;;;;;N;;;;; +0708;SYRIAC SUPRALINEAR COLON SKEWED LEFT;Po;0;AL;;;;;N;;;;; +0709;SYRIAC SUBLINEAR COLON SKEWED RIGHT;Po;0;AL;;;;;N;;;;; +070A;SYRIAC CONTRACTION;Po;0;AL;;;;;N;;;;; +070B;SYRIAC HARKLEAN OBELUS;Po;0;AL;;;;;N;;;;; +070C;SYRIAC HARKLEAN METOBELUS;Po;0;AL;;;;;N;;;;; +070D;SYRIAC HARKLEAN ASTERISCUS;Po;0;AL;;;;;N;;;;; +070F;SYRIAC ABBREVIATION MARK;Cf;0;BN;;;;;N;;;;; +0710;SYRIAC LETTER ALAPH;Lo;0;AL;;;;;N;;;;; +0711;SYRIAC LETTER SUPERSCRIPT ALAPH;Mn;36;NSM;;;;;N;;;;; +0712;SYRIAC LETTER BETH;Lo;0;AL;;;;;N;;;;; +0713;SYRIAC LETTER GAMAL;Lo;0;AL;;;;;N;;;;; +0714;SYRIAC LETTER GAMAL GARSHUNI;Lo;0;AL;;;;;N;;;;; +0715;SYRIAC LETTER DALATH;Lo;0;AL;;;;;N;;;;; +0716;SYRIAC LETTER DOTLESS DALATH RISH;Lo;0;AL;;;;;N;;;;; +0717;SYRIAC LETTER HE;Lo;0;AL;;;;;N;;;;; +0718;SYRIAC LETTER WAW;Lo;0;AL;;;;;N;;;;; +0719;SYRIAC LETTER ZAIN;Lo;0;AL;;;;;N;;;;; +071A;SYRIAC LETTER HETH;Lo;0;AL;;;;;N;;;;; +071B;SYRIAC LETTER TETH;Lo;0;AL;;;;;N;;;;; +071C;SYRIAC LETTER TETH GARSHUNI;Lo;0;AL;;;;;N;;;;; +071D;SYRIAC LETTER YUDH;Lo;0;AL;;;;;N;;;;; +071E;SYRIAC LETTER YUDH HE;Lo;0;AL;;;;;N;;;;; +071F;SYRIAC LETTER KAPH;Lo;0;AL;;;;;N;;;;; +0720;SYRIAC LETTER LAMADH;Lo;0;AL;;;;;N;;;;; +0721;SYRIAC LETTER MIM;Lo;0;AL;;;;;N;;;;; +0722;SYRIAC LETTER NUN;Lo;0;AL;;;;;N;;;;; +0723;SYRIAC LETTER SEMKATH;Lo;0;AL;;;;;N;;;;; +0724;SYRIAC LETTER FINAL SEMKATH;Lo;0;AL;;;;;N;;;;; +0725;SYRIAC LETTER E;Lo;0;AL;;;;;N;;;;; +0726;SYRIAC LETTER PE;Lo;0;AL;;;;;N;;;;; +0727;SYRIAC LETTER REVERSED PE;Lo;0;AL;;;;;N;;;;; +0728;SYRIAC LETTER SADHE;Lo;0;AL;;;;;N;;;;; +0729;SYRIAC LETTER QAPH;Lo;0;AL;;;;;N;;;;; +072A;SYRIAC LETTER RISH;Lo;0;AL;;;;;N;;;;; +072B;SYRIAC LETTER SHIN;Lo;0;AL;;;;;N;;;;; +072C;SYRIAC LETTER TAW;Lo;0;AL;;;;;N;;;;; +0730;SYRIAC PTHAHA ABOVE;Mn;230;NSM;;;;;N;;;;; +0731;SYRIAC PTHAHA BELOW;Mn;220;NSM;;;;;N;;;;; +0732;SYRIAC PTHAHA DOTTED;Mn;230;NSM;;;;;N;;;;; +0733;SYRIAC ZQAPHA ABOVE;Mn;230;NSM;;;;;N;;;;; +0734;SYRIAC ZQAPHA BELOW;Mn;220;NSM;;;;;N;;;;; +0735;SYRIAC ZQAPHA DOTTED;Mn;230;NSM;;;;;N;;;;; +0736;SYRIAC RBASA ABOVE;Mn;230;NSM;;;;;N;;;;; +0737;SYRIAC RBASA BELOW;Mn;220;NSM;;;;;N;;;;; +0738;SYRIAC DOTTED ZLAMA HORIZONTAL;Mn;220;NSM;;;;;N;;;;; +0739;SYRIAC DOTTED ZLAMA ANGULAR;Mn;220;NSM;;;;;N;;;;; +073A;SYRIAC HBASA ABOVE;Mn;230;NSM;;;;;N;;;;; +073B;SYRIAC HBASA BELOW;Mn;220;NSM;;;;;N;;;;; +073C;SYRIAC HBASA-ESASA DOTTED;Mn;220;NSM;;;;;N;;;;; +073D;SYRIAC ESASA ABOVE;Mn;230;NSM;;;;;N;;;;; +073E;SYRIAC ESASA BELOW;Mn;220;NSM;;;;;N;;;;; +073F;SYRIAC RWAHA;Mn;230;NSM;;;;;N;;;;; +0740;SYRIAC FEMININE DOT;Mn;230;NSM;;;;;N;;;;; +0741;SYRIAC QUSHSHAYA;Mn;230;NSM;;;;;N;;;;; +0742;SYRIAC RUKKAKHA;Mn;220;NSM;;;;;N;;;;; +0743;SYRIAC TWO VERTICAL DOTS ABOVE;Mn;230;NSM;;;;;N;;;;; +0744;SYRIAC TWO VERTICAL DOTS BELOW;Mn;220;NSM;;;;;N;;;;; +0745;SYRIAC THREE DOTS ABOVE;Mn;230;NSM;;;;;N;;;;; +0746;SYRIAC THREE DOTS BELOW;Mn;220;NSM;;;;;N;;;;; +0747;SYRIAC OBLIQUE LINE ABOVE;Mn;230;NSM;;;;;N;;;;; +0748;SYRIAC OBLIQUE LINE BELOW;Mn;220;NSM;;;;;N;;;;; +0749;SYRIAC MUSIC;Mn;230;NSM;;;;;N;;;;; +074A;SYRIAC BARREKH;Mn;230;NSM;;;;;N;;;;; +0780;THAANA LETTER HAA;Lo;0;AL;;;;;N;;;;; +0781;THAANA LETTER SHAVIYANI;Lo;0;AL;;;;;N;;;;; +0782;THAANA LETTER NOONU;Lo;0;AL;;;;;N;;;;; +0783;THAANA LETTER RAA;Lo;0;AL;;;;;N;;;;; +0784;THAANA LETTER BAA;Lo;0;AL;;;;;N;;;;; +0785;THAANA LETTER LHAVIYANI;Lo;0;AL;;;;;N;;;;; +0786;THAANA LETTER KAAFU;Lo;0;AL;;;;;N;;;;; +0787;THAANA LETTER ALIFU;Lo;0;AL;;;;;N;;;;; +0788;THAANA LETTER VAAVU;Lo;0;AL;;;;;N;;;;; +0789;THAANA LETTER MEEMU;Lo;0;AL;;;;;N;;;;; +078A;THAANA LETTER FAAFU;Lo;0;AL;;;;;N;;;;; +078B;THAANA LETTER DHAALU;Lo;0;AL;;;;;N;;;;; +078C;THAANA LETTER THAA;Lo;0;AL;;;;;N;;;;; +078D;THAANA LETTER LAAMU;Lo;0;AL;;;;;N;;;;; +078E;THAANA LETTER GAAFU;Lo;0;AL;;;;;N;;;;; +078F;THAANA LETTER GNAVIYANI;Lo;0;AL;;;;;N;;;;; +0790;THAANA LETTER SEENU;Lo;0;AL;;;;;N;;;;; +0791;THAANA LETTER DAVIYANI;Lo;0;AL;;;;;N;;;;; +0792;THAANA LETTER ZAVIYANI;Lo;0;AL;;;;;N;;;;; +0793;THAANA LETTER TAVIYANI;Lo;0;AL;;;;;N;;;;; +0794;THAANA LETTER YAA;Lo;0;AL;;;;;N;;;;; +0795;THAANA LETTER PAVIYANI;Lo;0;AL;;;;;N;;;;; +0796;THAANA LETTER JAVIYANI;Lo;0;AL;;;;;N;;;;; +0797;THAANA LETTER CHAVIYANI;Lo;0;AL;;;;;N;;;;; +0798;THAANA LETTER TTAA;Lo;0;AL;;;;;N;;;;; +0799;THAANA LETTER HHAA;Lo;0;AL;;;;;N;;;;; +079A;THAANA LETTER KHAA;Lo;0;AL;;;;;N;;;;; +079B;THAANA LETTER THAALU;Lo;0;AL;;;;;N;;;;; +079C;THAANA LETTER ZAA;Lo;0;AL;;;;;N;;;;; +079D;THAANA LETTER SHEENU;Lo;0;AL;;;;;N;;;;; +079E;THAANA LETTER SAADHU;Lo;0;AL;;;;;N;;;;; +079F;THAANA LETTER DAADHU;Lo;0;AL;;;;;N;;;;; +07A0;THAANA LETTER TO;Lo;0;AL;;;;;N;;;;; +07A1;THAANA LETTER ZO;Lo;0;AL;;;;;N;;;;; +07A2;THAANA LETTER AINU;Lo;0;AL;;;;;N;;;;; +07A3;THAANA LETTER GHAINU;Lo;0;AL;;;;;N;;;;; +07A4;THAANA LETTER QAAFU;Lo;0;AL;;;;;N;;;;; +07A5;THAANA LETTER WAAVU;Lo;0;AL;;;;;N;;;;; +07A6;THAANA ABAFILI;Mn;0;NSM;;;;;N;;;;; +07A7;THAANA AABAAFILI;Mn;0;NSM;;;;;N;;;;; +07A8;THAANA IBIFILI;Mn;0;NSM;;;;;N;;;;; +07A9;THAANA EEBEEFILI;Mn;0;NSM;;;;;N;;;;; +07AA;THAANA UBUFILI;Mn;0;NSM;;;;;N;;;;; +07AB;THAANA OOBOOFILI;Mn;0;NSM;;;;;N;;;;; +07AC;THAANA EBEFILI;Mn;0;NSM;;;;;N;;;;; +07AD;THAANA EYBEYFILI;Mn;0;NSM;;;;;N;;;;; +07AE;THAANA OBOFILI;Mn;0;NSM;;;;;N;;;;; +07AF;THAANA OABOAFILI;Mn;0;NSM;;;;;N;;;;; +07B0;THAANA SUKUN;Mn;0;NSM;;;;;N;;;;; +07B1;THAANA LETTER NAA;Lo;0;AL;;;;;N;;;;; +0901;DEVANAGARI SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;; +0902;DEVANAGARI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;; +0903;DEVANAGARI SIGN VISARGA;Mc;0;L;;;;;N;;;;; +0905;DEVANAGARI LETTER A;Lo;0;L;;;;;N;;;;; +0906;DEVANAGARI LETTER AA;Lo;0;L;;;;;N;;;;; +0907;DEVANAGARI LETTER I;Lo;0;L;;;;;N;;;;; +0908;DEVANAGARI LETTER II;Lo;0;L;;;;;N;;;;; +0909;DEVANAGARI LETTER U;Lo;0;L;;;;;N;;;;; +090A;DEVANAGARI LETTER UU;Lo;0;L;;;;;N;;;;; +090B;DEVANAGARI LETTER VOCALIC R;Lo;0;L;;;;;N;;;;; +090C;DEVANAGARI LETTER VOCALIC L;Lo;0;L;;;;;N;;;;; +090D;DEVANAGARI LETTER CANDRA E;Lo;0;L;;;;;N;;;;; +090E;DEVANAGARI LETTER SHORT E;Lo;0;L;;;;;N;;;;; +090F;DEVANAGARI LETTER E;Lo;0;L;;;;;N;;;;; +0910;DEVANAGARI LETTER AI;Lo;0;L;;;;;N;;;;; +0911;DEVANAGARI LETTER CANDRA O;Lo;0;L;;;;;N;;;;; +0912;DEVANAGARI LETTER SHORT O;Lo;0;L;;;;;N;;;;; +0913;DEVANAGARI LETTER O;Lo;0;L;;;;;N;;;;; +0914;DEVANAGARI LETTER AU;Lo;0;L;;;;;N;;;;; +0915;DEVANAGARI LETTER KA;Lo;0;L;;;;;N;;;;; +0916;DEVANAGARI LETTER KHA;Lo;0;L;;;;;N;;;;; +0917;DEVANAGARI LETTER GA;Lo;0;L;;;;;N;;;;; +0918;DEVANAGARI LETTER GHA;Lo;0;L;;;;;N;;;;; +0919;DEVANAGARI LETTER NGA;Lo;0;L;;;;;N;;;;; +091A;DEVANAGARI LETTER CA;Lo;0;L;;;;;N;;;;; +091B;DEVANAGARI LETTER CHA;Lo;0;L;;;;;N;;;;; +091C;DEVANAGARI LETTER JA;Lo;0;L;;;;;N;;;;; +091D;DEVANAGARI LETTER JHA;Lo;0;L;;;;;N;;;;; +091E;DEVANAGARI LETTER NYA;Lo;0;L;;;;;N;;;;; +091F;DEVANAGARI LETTER TTA;Lo;0;L;;;;;N;;;;; +0920;DEVANAGARI LETTER TTHA;Lo;0;L;;;;;N;;;;; +0921;DEVANAGARI LETTER DDA;Lo;0;L;;;;;N;;;;; +0922;DEVANAGARI LETTER DDHA;Lo;0;L;;;;;N;;;;; +0923;DEVANAGARI LETTER NNA;Lo;0;L;;;;;N;;;;; +0924;DEVANAGARI LETTER TA;Lo;0;L;;;;;N;;;;; +0925;DEVANAGARI LETTER THA;Lo;0;L;;;;;N;;;;; +0926;DEVANAGARI LETTER DA;Lo;0;L;;;;;N;;;;; +0927;DEVANAGARI LETTER DHA;Lo;0;L;;;;;N;;;;; +0928;DEVANAGARI LETTER NA;Lo;0;L;;;;;N;;;;; +0929;DEVANAGARI LETTER NNNA;Lo;0;L;0928 093C;;;;N;;;;; +092A;DEVANAGARI LETTER PA;Lo;0;L;;;;;N;;;;; +092B;DEVANAGARI LETTER PHA;Lo;0;L;;;;;N;;;;; +092C;DEVANAGARI LETTER BA;Lo;0;L;;;;;N;;;;; +092D;DEVANAGARI LETTER BHA;Lo;0;L;;;;;N;;;;; +092E;DEVANAGARI LETTER MA;Lo;0;L;;;;;N;;;;; +092F;DEVANAGARI LETTER YA;Lo;0;L;;;;;N;;;;; +0930;DEVANAGARI LETTER RA;Lo;0;L;;;;;N;;;;; +0931;DEVANAGARI LETTER RRA;Lo;0;L;0930 093C;;;;N;;;;; +0932;DEVANAGARI LETTER LA;Lo;0;L;;;;;N;;;;; +0933;DEVANAGARI LETTER LLA;Lo;0;L;;;;;N;;;;; +0934;DEVANAGARI LETTER LLLA;Lo;0;L;0933 093C;;;;N;;;;; +0935;DEVANAGARI LETTER VA;Lo;0;L;;;;;N;;;;; +0936;DEVANAGARI LETTER SHA;Lo;0;L;;;;;N;;;;; +0937;DEVANAGARI LETTER SSA;Lo;0;L;;;;;N;;;;; +0938;DEVANAGARI LETTER SA;Lo;0;L;;;;;N;;;;; +0939;DEVANAGARI LETTER HA;Lo;0;L;;;;;N;;;;; +093C;DEVANAGARI SIGN NUKTA;Mn;7;NSM;;;;;N;;;;; +093D;DEVANAGARI SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;; +093E;DEVANAGARI VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; +093F;DEVANAGARI VOWEL SIGN I;Mc;0;L;;;;;N;;;;; +0940;DEVANAGARI VOWEL SIGN II;Mc;0;L;;;;;N;;;;; +0941;DEVANAGARI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; +0942;DEVANAGARI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; +0943;DEVANAGARI VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;; +0944;DEVANAGARI VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;; +0945;DEVANAGARI VOWEL SIGN CANDRA E;Mn;0;NSM;;;;;N;;;;; +0946;DEVANAGARI VOWEL SIGN SHORT E;Mn;0;NSM;;;;;N;;;;; +0947;DEVANAGARI VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;; +0948;DEVANAGARI VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;; +0949;DEVANAGARI VOWEL SIGN CANDRA O;Mc;0;L;;;;;N;;;;; +094A;DEVANAGARI VOWEL SIGN SHORT O;Mc;0;L;;;;;N;;;;; +094B;DEVANAGARI VOWEL SIGN O;Mc;0;L;;;;;N;;;;; +094C;DEVANAGARI VOWEL SIGN AU;Mc;0;L;;;;;N;;;;; +094D;DEVANAGARI SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; +0950;DEVANAGARI OM;Lo;0;L;;;;;N;;;;; +0951;DEVANAGARI STRESS SIGN UDATTA;Mn;230;NSM;;;;;N;;;;; +0952;DEVANAGARI STRESS SIGN ANUDATTA;Mn;220;NSM;;;;;N;;;;; +0953;DEVANAGARI GRAVE ACCENT;Mn;230;NSM;;;;;N;;;;; +0954;DEVANAGARI ACUTE ACCENT;Mn;230;NSM;;;;;N;;;;; +0958;DEVANAGARI LETTER QA;Lo;0;L;0915 093C;;;;N;;;;; +0959;DEVANAGARI LETTER KHHA;Lo;0;L;0916 093C;;;;N;;;;; +095A;DEVANAGARI LETTER GHHA;Lo;0;L;0917 093C;;;;N;;;;; +095B;DEVANAGARI LETTER ZA;Lo;0;L;091C 093C;;;;N;;;;; +095C;DEVANAGARI LETTER DDDHA;Lo;0;L;0921 093C;;;;N;;;;; +095D;DEVANAGARI LETTER RHA;Lo;0;L;0922 093C;;;;N;;;;; +095E;DEVANAGARI LETTER FA;Lo;0;L;092B 093C;;;;N;;;;; +095F;DEVANAGARI LETTER YYA;Lo;0;L;092F 093C;;;;N;;;;; +0960;DEVANAGARI LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;; +0961;DEVANAGARI LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;; +0962;DEVANAGARI VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;; +0963;DEVANAGARI VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;; +0964;DEVANAGARI DANDA;Po;0;L;;;;;N;;;;; +0965;DEVANAGARI DOUBLE DANDA;Po;0;L;;;;;N;;;;; +0966;DEVANAGARI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +0967;DEVANAGARI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +0968;DEVANAGARI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +0969;DEVANAGARI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +096A;DEVANAGARI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +096B;DEVANAGARI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +096C;DEVANAGARI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +096D;DEVANAGARI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +096E;DEVANAGARI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +096F;DEVANAGARI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +0970;DEVANAGARI ABBREVIATION SIGN;Po;0;L;;;;;N;;;;; +0981;BENGALI SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;; +0982;BENGALI SIGN ANUSVARA;Mc;0;L;;;;;N;;;;; +0983;BENGALI SIGN VISARGA;Mc;0;L;;;;;N;;;;; +0985;BENGALI LETTER A;Lo;0;L;;;;;N;;;;; +0986;BENGALI LETTER AA;Lo;0;L;;;;;N;;;;; +0987;BENGALI LETTER I;Lo;0;L;;;;;N;;;;; +0988;BENGALI LETTER II;Lo;0;L;;;;;N;;;;; +0989;BENGALI LETTER U;Lo;0;L;;;;;N;;;;; +098A;BENGALI LETTER UU;Lo;0;L;;;;;N;;;;; +098B;BENGALI LETTER VOCALIC R;Lo;0;L;;;;;N;;;;; +098C;BENGALI LETTER VOCALIC L;Lo;0;L;;;;;N;;;;; +098F;BENGALI LETTER E;Lo;0;L;;;;;N;;;;; +0990;BENGALI LETTER AI;Lo;0;L;;;;;N;;;;; +0993;BENGALI LETTER O;Lo;0;L;;;;;N;;;;; +0994;BENGALI LETTER AU;Lo;0;L;;;;;N;;;;; +0995;BENGALI LETTER KA;Lo;0;L;;;;;N;;;;; +0996;BENGALI LETTER KHA;Lo;0;L;;;;;N;;;;; +0997;BENGALI LETTER GA;Lo;0;L;;;;;N;;;;; +0998;BENGALI LETTER GHA;Lo;0;L;;;;;N;;;;; +0999;BENGALI LETTER NGA;Lo;0;L;;;;;N;;;;; +099A;BENGALI LETTER CA;Lo;0;L;;;;;N;;;;; +099B;BENGALI LETTER CHA;Lo;0;L;;;;;N;;;;; +099C;BENGALI LETTER JA;Lo;0;L;;;;;N;;;;; +099D;BENGALI LETTER JHA;Lo;0;L;;;;;N;;;;; +099E;BENGALI LETTER NYA;Lo;0;L;;;;;N;;;;; +099F;BENGALI LETTER TTA;Lo;0;L;;;;;N;;;;; +09A0;BENGALI LETTER TTHA;Lo;0;L;;;;;N;;;;; +09A1;BENGALI LETTER DDA;Lo;0;L;;;;;N;;;;; +09A2;BENGALI LETTER DDHA;Lo;0;L;;;;;N;;;;; +09A3;BENGALI LETTER NNA;Lo;0;L;;;;;N;;;;; +09A4;BENGALI LETTER TA;Lo;0;L;;;;;N;;;;; +09A5;BENGALI LETTER THA;Lo;0;L;;;;;N;;;;; +09A6;BENGALI LETTER DA;Lo;0;L;;;;;N;;;;; +09A7;BENGALI LETTER DHA;Lo;0;L;;;;;N;;;;; +09A8;BENGALI LETTER NA;Lo;0;L;;;;;N;;;;; +09AA;BENGALI LETTER PA;Lo;0;L;;;;;N;;;;; +09AB;BENGALI LETTER PHA;Lo;0;L;;;;;N;;;;; +09AC;BENGALI LETTER BA;Lo;0;L;;;;;N;;;;; +09AD;BENGALI LETTER BHA;Lo;0;L;;;;;N;;;;; +09AE;BENGALI LETTER MA;Lo;0;L;;;;;N;;;;; +09AF;BENGALI LETTER YA;Lo;0;L;;;;;N;;;;; +09B0;BENGALI LETTER RA;Lo;0;L;;;;;N;;;;; +09B2;BENGALI LETTER LA;Lo;0;L;;;;;N;;;;; +09B6;BENGALI LETTER SHA;Lo;0;L;;;;;N;;;;; +09B7;BENGALI LETTER SSA;Lo;0;L;;;;;N;;;;; +09B8;BENGALI LETTER SA;Lo;0;L;;;;;N;;;;; +09B9;BENGALI LETTER HA;Lo;0;L;;;;;N;;;;; +09BC;BENGALI SIGN NUKTA;Mn;7;NSM;;;;;N;;;;; +09BE;BENGALI VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; +09BF;BENGALI VOWEL SIGN I;Mc;0;L;;;;;N;;;;; +09C0;BENGALI VOWEL SIGN II;Mc;0;L;;;;;N;;;;; +09C1;BENGALI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; +09C2;BENGALI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; +09C3;BENGALI VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;; +09C4;BENGALI VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;; +09C7;BENGALI VOWEL SIGN E;Mc;0;L;;;;;N;;;;; +09C8;BENGALI VOWEL SIGN AI;Mc;0;L;;;;;N;;;;; +09CB;BENGALI VOWEL SIGN O;Mc;0;L;09C7 09BE;;;;N;;;;; +09CC;BENGALI VOWEL SIGN AU;Mc;0;L;09C7 09D7;;;;N;;;;; +09CD;BENGALI SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; +09D7;BENGALI AU LENGTH MARK;Mc;0;L;;;;;N;;;;; +09DC;BENGALI LETTER RRA;Lo;0;L;09A1 09BC;;;;N;;;;; +09DD;BENGALI LETTER RHA;Lo;0;L;09A2 09BC;;;;N;;;;; +09DF;BENGALI LETTER YYA;Lo;0;L;09AF 09BC;;;;N;;;;; +09E0;BENGALI LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;; +09E1;BENGALI LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;; +09E2;BENGALI VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;; +09E3;BENGALI VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;; +09E6;BENGALI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +09E7;BENGALI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +09E8;BENGALI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +09E9;BENGALI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +09EA;BENGALI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +09EB;BENGALI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +09EC;BENGALI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +09ED;BENGALI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +09EE;BENGALI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +09EF;BENGALI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +09F0;BENGALI LETTER RA WITH MIDDLE DIAGONAL;Lo;0;L;;;;;N;;Assamese;;; +09F1;BENGALI LETTER RA WITH LOWER DIAGONAL;Lo;0;L;;;;;N;BENGALI LETTER VA WITH LOWER DIAGONAL;Assamese;;; +09F2;BENGALI RUPEE MARK;Sc;0;ET;;;;;N;;;;; +09F3;BENGALI RUPEE SIGN;Sc;0;ET;;;;;N;;;;; +09F4;BENGALI CURRENCY NUMERATOR ONE;No;0;L;;;;1;N;;;;; +09F5;BENGALI CURRENCY NUMERATOR TWO;No;0;L;;;;2;N;;;;; +09F6;BENGALI CURRENCY NUMERATOR THREE;No;0;L;;;;3;N;;;;; +09F7;BENGALI CURRENCY NUMERATOR FOUR;No;0;L;;;;4;N;;;;; +09F8;BENGALI CURRENCY NUMERATOR ONE LESS THAN THE DENOMINATOR;No;0;L;;;;;N;;;;; +09F9;BENGALI CURRENCY DENOMINATOR SIXTEEN;No;0;L;;;;16;N;;;;; +09FA;BENGALI ISSHAR;So;0;L;;;;;N;;;;; +0A02;GURMUKHI SIGN BINDI;Mn;0;NSM;;;;;N;;;;; +0A05;GURMUKHI LETTER A;Lo;0;L;;;;;N;;;;; +0A06;GURMUKHI LETTER AA;Lo;0;L;;;;;N;;;;; +0A07;GURMUKHI LETTER I;Lo;0;L;;;;;N;;;;; +0A08;GURMUKHI LETTER II;Lo;0;L;;;;;N;;;;; +0A09;GURMUKHI LETTER U;Lo;0;L;;;;;N;;;;; +0A0A;GURMUKHI LETTER UU;Lo;0;L;;;;;N;;;;; +0A0F;GURMUKHI LETTER EE;Lo;0;L;;;;;N;;;;; +0A10;GURMUKHI LETTER AI;Lo;0;L;;;;;N;;;;; +0A13;GURMUKHI LETTER OO;Lo;0;L;;;;;N;;;;; +0A14;GURMUKHI LETTER AU;Lo;0;L;;;;;N;;;;; +0A15;GURMUKHI LETTER KA;Lo;0;L;;;;;N;;;;; +0A16;GURMUKHI LETTER KHA;Lo;0;L;;;;;N;;;;; +0A17;GURMUKHI LETTER GA;Lo;0;L;;;;;N;;;;; +0A18;GURMUKHI LETTER GHA;Lo;0;L;;;;;N;;;;; +0A19;GURMUKHI LETTER NGA;Lo;0;L;;;;;N;;;;; +0A1A;GURMUKHI LETTER CA;Lo;0;L;;;;;N;;;;; +0A1B;GURMUKHI LETTER CHA;Lo;0;L;;;;;N;;;;; +0A1C;GURMUKHI LETTER JA;Lo;0;L;;;;;N;;;;; +0A1D;GURMUKHI LETTER JHA;Lo;0;L;;;;;N;;;;; +0A1E;GURMUKHI LETTER NYA;Lo;0;L;;;;;N;;;;; +0A1F;GURMUKHI LETTER TTA;Lo;0;L;;;;;N;;;;; +0A20;GURMUKHI LETTER TTHA;Lo;0;L;;;;;N;;;;; +0A21;GURMUKHI LETTER DDA;Lo;0;L;;;;;N;;;;; +0A22;GURMUKHI LETTER DDHA;Lo;0;L;;;;;N;;;;; +0A23;GURMUKHI LETTER NNA;Lo;0;L;;;;;N;;;;; +0A24;GURMUKHI LETTER TA;Lo;0;L;;;;;N;;;;; +0A25;GURMUKHI LETTER THA;Lo;0;L;;;;;N;;;;; +0A26;GURMUKHI LETTER DA;Lo;0;L;;;;;N;;;;; +0A27;GURMUKHI LETTER DHA;Lo;0;L;;;;;N;;;;; +0A28;GURMUKHI LETTER NA;Lo;0;L;;;;;N;;;;; +0A2A;GURMUKHI LETTER PA;Lo;0;L;;;;;N;;;;; +0A2B;GURMUKHI LETTER PHA;Lo;0;L;;;;;N;;;;; +0A2C;GURMUKHI LETTER BA;Lo;0;L;;;;;N;;;;; +0A2D;GURMUKHI LETTER BHA;Lo;0;L;;;;;N;;;;; +0A2E;GURMUKHI LETTER MA;Lo;0;L;;;;;N;;;;; +0A2F;GURMUKHI LETTER YA;Lo;0;L;;;;;N;;;;; +0A30;GURMUKHI LETTER RA;Lo;0;L;;;;;N;;;;; +0A32;GURMUKHI LETTER LA;Lo;0;L;;;;;N;;;;; +0A33;GURMUKHI LETTER LLA;Lo;0;L;0A32 0A3C;;;;N;;;;; +0A35;GURMUKHI LETTER VA;Lo;0;L;;;;;N;;;;; +0A36;GURMUKHI LETTER SHA;Lo;0;L;0A38 0A3C;;;;N;;;;; +0A38;GURMUKHI LETTER SA;Lo;0;L;;;;;N;;;;; +0A39;GURMUKHI LETTER HA;Lo;0;L;;;;;N;;;;; +0A3C;GURMUKHI SIGN NUKTA;Mn;7;NSM;;;;;N;;;;; +0A3E;GURMUKHI VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; +0A3F;GURMUKHI VOWEL SIGN I;Mc;0;L;;;;;N;;;;; +0A40;GURMUKHI VOWEL SIGN II;Mc;0;L;;;;;N;;;;; +0A41;GURMUKHI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; +0A42;GURMUKHI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; +0A47;GURMUKHI VOWEL SIGN EE;Mn;0;NSM;;;;;N;;;;; +0A48;GURMUKHI VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;; +0A4B;GURMUKHI VOWEL SIGN OO;Mn;0;NSM;;;;;N;;;;; +0A4C;GURMUKHI VOWEL SIGN AU;Mn;0;NSM;;;;;N;;;;; +0A4D;GURMUKHI SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; +0A59;GURMUKHI LETTER KHHA;Lo;0;L;0A16 0A3C;;;;N;;;;; +0A5A;GURMUKHI LETTER GHHA;Lo;0;L;0A17 0A3C;;;;N;;;;; +0A5B;GURMUKHI LETTER ZA;Lo;0;L;0A1C 0A3C;;;;N;;;;; +0A5C;GURMUKHI LETTER RRA;Lo;0;L;;;;;N;;;;; +0A5E;GURMUKHI LETTER FA;Lo;0;L;0A2B 0A3C;;;;N;;;;; +0A66;GURMUKHI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +0A67;GURMUKHI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +0A68;GURMUKHI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +0A69;GURMUKHI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +0A6A;GURMUKHI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +0A6B;GURMUKHI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +0A6C;GURMUKHI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +0A6D;GURMUKHI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +0A6E;GURMUKHI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +0A6F;GURMUKHI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +0A70;GURMUKHI TIPPI;Mn;0;NSM;;;;;N;;;;; +0A71;GURMUKHI ADDAK;Mn;0;NSM;;;;;N;;;;; +0A72;GURMUKHI IRI;Lo;0;L;;;;;N;;;;; +0A73;GURMUKHI URA;Lo;0;L;;;;;N;;;;; +0A74;GURMUKHI EK ONKAR;Lo;0;L;;;;;N;;;;; +0A81;GUJARATI SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;; +0A82;GUJARATI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;; +0A83;GUJARATI SIGN VISARGA;Mc;0;L;;;;;N;;;;; +0A85;GUJARATI LETTER A;Lo;0;L;;;;;N;;;;; +0A86;GUJARATI LETTER AA;Lo;0;L;;;;;N;;;;; +0A87;GUJARATI LETTER I;Lo;0;L;;;;;N;;;;; +0A88;GUJARATI LETTER II;Lo;0;L;;;;;N;;;;; +0A89;GUJARATI LETTER U;Lo;0;L;;;;;N;;;;; +0A8A;GUJARATI LETTER UU;Lo;0;L;;;;;N;;;;; +0A8B;GUJARATI LETTER VOCALIC R;Lo;0;L;;;;;N;;;;; +0A8D;GUJARATI VOWEL CANDRA E;Lo;0;L;;;;;N;;;;; +0A8F;GUJARATI LETTER E;Lo;0;L;;;;;N;;;;; +0A90;GUJARATI LETTER AI;Lo;0;L;;;;;N;;;;; +0A91;GUJARATI VOWEL CANDRA O;Lo;0;L;;;;;N;;;;; +0A93;GUJARATI LETTER O;Lo;0;L;;;;;N;;;;; +0A94;GUJARATI LETTER AU;Lo;0;L;;;;;N;;;;; +0A95;GUJARATI LETTER KA;Lo;0;L;;;;;N;;;;; +0A96;GUJARATI LETTER KHA;Lo;0;L;;;;;N;;;;; +0A97;GUJARATI LETTER GA;Lo;0;L;;;;;N;;;;; +0A98;GUJARATI LETTER GHA;Lo;0;L;;;;;N;;;;; +0A99;GUJARATI LETTER NGA;Lo;0;L;;;;;N;;;;; +0A9A;GUJARATI LETTER CA;Lo;0;L;;;;;N;;;;; +0A9B;GUJARATI LETTER CHA;Lo;0;L;;;;;N;;;;; +0A9C;GUJARATI LETTER JA;Lo;0;L;;;;;N;;;;; +0A9D;GUJARATI LETTER JHA;Lo;0;L;;;;;N;;;;; +0A9E;GUJARATI LETTER NYA;Lo;0;L;;;;;N;;;;; +0A9F;GUJARATI LETTER TTA;Lo;0;L;;;;;N;;;;; +0AA0;GUJARATI LETTER TTHA;Lo;0;L;;;;;N;;;;; +0AA1;GUJARATI LETTER DDA;Lo;0;L;;;;;N;;;;; +0AA2;GUJARATI LETTER DDHA;Lo;0;L;;;;;N;;;;; +0AA3;GUJARATI LETTER NNA;Lo;0;L;;;;;N;;;;; +0AA4;GUJARATI LETTER TA;Lo;0;L;;;;;N;;;;; +0AA5;GUJARATI LETTER THA;Lo;0;L;;;;;N;;;;; +0AA6;GUJARATI LETTER DA;Lo;0;L;;;;;N;;;;; +0AA7;GUJARATI LETTER DHA;Lo;0;L;;;;;N;;;;; +0AA8;GUJARATI LETTER NA;Lo;0;L;;;;;N;;;;; +0AAA;GUJARATI LETTER PA;Lo;0;L;;;;;N;;;;; +0AAB;GUJARATI LETTER PHA;Lo;0;L;;;;;N;;;;; +0AAC;GUJARATI LETTER BA;Lo;0;L;;;;;N;;;;; +0AAD;GUJARATI LETTER BHA;Lo;0;L;;;;;N;;;;; +0AAE;GUJARATI LETTER MA;Lo;0;L;;;;;N;;;;; +0AAF;GUJARATI LETTER YA;Lo;0;L;;;;;N;;;;; +0AB0;GUJARATI LETTER RA;Lo;0;L;;;;;N;;;;; +0AB2;GUJARATI LETTER LA;Lo;0;L;;;;;N;;;;; +0AB3;GUJARATI LETTER LLA;Lo;0;L;;;;;N;;;;; +0AB5;GUJARATI LETTER VA;Lo;0;L;;;;;N;;;;; +0AB6;GUJARATI LETTER SHA;Lo;0;L;;;;;N;;;;; +0AB7;GUJARATI LETTER SSA;Lo;0;L;;;;;N;;;;; +0AB8;GUJARATI LETTER SA;Lo;0;L;;;;;N;;;;; +0AB9;GUJARATI LETTER HA;Lo;0;L;;;;;N;;;;; +0ABC;GUJARATI SIGN NUKTA;Mn;7;NSM;;;;;N;;;;; +0ABD;GUJARATI SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;; +0ABE;GUJARATI VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; +0ABF;GUJARATI VOWEL SIGN I;Mc;0;L;;;;;N;;;;; +0AC0;GUJARATI VOWEL SIGN II;Mc;0;L;;;;;N;;;;; +0AC1;GUJARATI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; +0AC2;GUJARATI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; +0AC3;GUJARATI VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;; +0AC4;GUJARATI VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;; +0AC5;GUJARATI VOWEL SIGN CANDRA E;Mn;0;NSM;;;;;N;;;;; +0AC7;GUJARATI VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;; +0AC8;GUJARATI VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;; +0AC9;GUJARATI VOWEL SIGN CANDRA O;Mc;0;L;;;;;N;;;;; +0ACB;GUJARATI VOWEL SIGN O;Mc;0;L;;;;;N;;;;; +0ACC;GUJARATI VOWEL SIGN AU;Mc;0;L;;;;;N;;;;; +0ACD;GUJARATI SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; +0AD0;GUJARATI OM;Lo;0;L;;;;;N;;;;; +0AE0;GUJARATI LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;; +0AE6;GUJARATI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +0AE7;GUJARATI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +0AE8;GUJARATI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +0AE9;GUJARATI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +0AEA;GUJARATI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +0AEB;GUJARATI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +0AEC;GUJARATI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +0AED;GUJARATI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +0AEE;GUJARATI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +0AEF;GUJARATI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +0B01;ORIYA SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;; +0B02;ORIYA SIGN ANUSVARA;Mc;0;L;;;;;N;;;;; +0B03;ORIYA SIGN VISARGA;Mc;0;L;;;;;N;;;;; +0B05;ORIYA LETTER A;Lo;0;L;;;;;N;;;;; +0B06;ORIYA LETTER AA;Lo;0;L;;;;;N;;;;; +0B07;ORIYA LETTER I;Lo;0;L;;;;;N;;;;; +0B08;ORIYA LETTER II;Lo;0;L;;;;;N;;;;; +0B09;ORIYA LETTER U;Lo;0;L;;;;;N;;;;; +0B0A;ORIYA LETTER UU;Lo;0;L;;;;;N;;;;; +0B0B;ORIYA LETTER VOCALIC R;Lo;0;L;;;;;N;;;;; +0B0C;ORIYA LETTER VOCALIC L;Lo;0;L;;;;;N;;;;; +0B0F;ORIYA LETTER E;Lo;0;L;;;;;N;;;;; +0B10;ORIYA LETTER AI;Lo;0;L;;;;;N;;;;; +0B13;ORIYA LETTER O;Lo;0;L;;;;;N;;;;; +0B14;ORIYA LETTER AU;Lo;0;L;;;;;N;;;;; +0B15;ORIYA LETTER KA;Lo;0;L;;;;;N;;;;; +0B16;ORIYA LETTER KHA;Lo;0;L;;;;;N;;;;; +0B17;ORIYA LETTER GA;Lo;0;L;;;;;N;;;;; +0B18;ORIYA LETTER GHA;Lo;0;L;;;;;N;;;;; +0B19;ORIYA LETTER NGA;Lo;0;L;;;;;N;;;;; +0B1A;ORIYA LETTER CA;Lo;0;L;;;;;N;;;;; +0B1B;ORIYA LETTER CHA;Lo;0;L;;;;;N;;;;; +0B1C;ORIYA LETTER JA;Lo;0;L;;;;;N;;;;; +0B1D;ORIYA LETTER JHA;Lo;0;L;;;;;N;;;;; +0B1E;ORIYA LETTER NYA;Lo;0;L;;;;;N;;;;; +0B1F;ORIYA LETTER TTA;Lo;0;L;;;;;N;;;;; +0B20;ORIYA LETTER TTHA;Lo;0;L;;;;;N;;;;; +0B21;ORIYA LETTER DDA;Lo;0;L;;;;;N;;;;; +0B22;ORIYA LETTER DDHA;Lo;0;L;;;;;N;;;;; +0B23;ORIYA LETTER NNA;Lo;0;L;;;;;N;;;;; +0B24;ORIYA LETTER TA;Lo;0;L;;;;;N;;;;; +0B25;ORIYA LETTER THA;Lo;0;L;;;;;N;;;;; +0B26;ORIYA LETTER DA;Lo;0;L;;;;;N;;;;; +0B27;ORIYA LETTER DHA;Lo;0;L;;;;;N;;;;; +0B28;ORIYA LETTER NA;Lo;0;L;;;;;N;;;;; +0B2A;ORIYA LETTER PA;Lo;0;L;;;;;N;;;;; +0B2B;ORIYA LETTER PHA;Lo;0;L;;;;;N;;;;; +0B2C;ORIYA LETTER BA;Lo;0;L;;;;;N;;;;; +0B2D;ORIYA LETTER BHA;Lo;0;L;;;;;N;;;;; +0B2E;ORIYA LETTER MA;Lo;0;L;;;;;N;;;;; +0B2F;ORIYA LETTER YA;Lo;0;L;;;;;N;;;;; +0B30;ORIYA LETTER RA;Lo;0;L;;;;;N;;;;; +0B32;ORIYA LETTER LA;Lo;0;L;;;;;N;;;;; +0B33;ORIYA LETTER LLA;Lo;0;L;;;;;N;;;;; +0B36;ORIYA LETTER SHA;Lo;0;L;;;;;N;;;;; +0B37;ORIYA LETTER SSA;Lo;0;L;;;;;N;;;;; +0B38;ORIYA LETTER SA;Lo;0;L;;;;;N;;;;; +0B39;ORIYA LETTER HA;Lo;0;L;;;;;N;;;;; +0B3C;ORIYA SIGN NUKTA;Mn;7;NSM;;;;;N;;;;; +0B3D;ORIYA SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;; +0B3E;ORIYA VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; +0B3F;ORIYA VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;; +0B40;ORIYA VOWEL SIGN II;Mc;0;L;;;;;N;;;;; +0B41;ORIYA VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; +0B42;ORIYA VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; +0B43;ORIYA VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;; +0B47;ORIYA VOWEL SIGN E;Mc;0;L;;;;;N;;;;; +0B48;ORIYA VOWEL SIGN AI;Mc;0;L;0B47 0B56;;;;N;;;;; +0B4B;ORIYA VOWEL SIGN O;Mc;0;L;0B47 0B3E;;;;N;;;;; +0B4C;ORIYA VOWEL SIGN AU;Mc;0;L;0B47 0B57;;;;N;;;;; +0B4D;ORIYA SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; +0B56;ORIYA AI LENGTH MARK;Mn;0;NSM;;;;;N;;;;; +0B57;ORIYA AU LENGTH MARK;Mc;0;L;;;;;N;;;;; +0B5C;ORIYA LETTER RRA;Lo;0;L;0B21 0B3C;;;;N;;;;; +0B5D;ORIYA LETTER RHA;Lo;0;L;0B22 0B3C;;;;N;;;;; +0B5F;ORIYA LETTER YYA;Lo;0;L;;;;;N;;;;; +0B60;ORIYA LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;; +0B61;ORIYA LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;; +0B66;ORIYA DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +0B67;ORIYA DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +0B68;ORIYA DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +0B69;ORIYA DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +0B6A;ORIYA DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +0B6B;ORIYA DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +0B6C;ORIYA DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +0B6D;ORIYA DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +0B6E;ORIYA DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +0B6F;ORIYA DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +0B70;ORIYA ISSHAR;So;0;L;;;;;N;;;;; +0B82;TAMIL SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;; +0B83;TAMIL SIGN VISARGA;Lo;0;L;;;;;N;;;;; +0B85;TAMIL LETTER A;Lo;0;L;;;;;N;;;;; +0B86;TAMIL LETTER AA;Lo;0;L;;;;;N;;;;; +0B87;TAMIL LETTER I;Lo;0;L;;;;;N;;;;; +0B88;TAMIL LETTER II;Lo;0;L;;;;;N;;;;; +0B89;TAMIL LETTER U;Lo;0;L;;;;;N;;;;; +0B8A;TAMIL LETTER UU;Lo;0;L;;;;;N;;;;; +0B8E;TAMIL LETTER E;Lo;0;L;;;;;N;;;;; +0B8F;TAMIL LETTER EE;Lo;0;L;;;;;N;;;;; +0B90;TAMIL LETTER AI;Lo;0;L;;;;;N;;;;; +0B92;TAMIL LETTER O;Lo;0;L;;;;;N;;;;; +0B93;TAMIL LETTER OO;Lo;0;L;;;;;N;;;;; +0B94;TAMIL LETTER AU;Lo;0;L;0B92 0BD7;;;;N;;;;; +0B95;TAMIL LETTER KA;Lo;0;L;;;;;N;;;;; +0B99;TAMIL LETTER NGA;Lo;0;L;;;;;N;;;;; +0B9A;TAMIL LETTER CA;Lo;0;L;;;;;N;;;;; +0B9C;TAMIL LETTER JA;Lo;0;L;;;;;N;;;;; +0B9E;TAMIL LETTER NYA;Lo;0;L;;;;;N;;;;; +0B9F;TAMIL LETTER TTA;Lo;0;L;;;;;N;;;;; +0BA3;TAMIL LETTER NNA;Lo;0;L;;;;;N;;;;; +0BA4;TAMIL LETTER TA;Lo;0;L;;;;;N;;;;; +0BA8;TAMIL LETTER NA;Lo;0;L;;;;;N;;;;; +0BA9;TAMIL LETTER NNNA;Lo;0;L;;;;;N;;;;; +0BAA;TAMIL LETTER PA;Lo;0;L;;;;;N;;;;; +0BAE;TAMIL LETTER MA;Lo;0;L;;;;;N;;;;; +0BAF;TAMIL LETTER YA;Lo;0;L;;;;;N;;;;; +0BB0;TAMIL LETTER RA;Lo;0;L;;;;;N;;;;; +0BB1;TAMIL LETTER RRA;Lo;0;L;;;;;N;;;;; +0BB2;TAMIL LETTER LA;Lo;0;L;;;;;N;;;;; +0BB3;TAMIL LETTER LLA;Lo;0;L;;;;;N;;;;; +0BB4;TAMIL LETTER LLLA;Lo;0;L;;;;;N;;;;; +0BB5;TAMIL LETTER VA;Lo;0;L;;;;;N;;;;; +0BB7;TAMIL LETTER SSA;Lo;0;L;;;;;N;;;;; +0BB8;TAMIL LETTER SA;Lo;0;L;;;;;N;;;;; +0BB9;TAMIL LETTER HA;Lo;0;L;;;;;N;;;;; +0BBE;TAMIL VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; +0BBF;TAMIL VOWEL SIGN I;Mc;0;L;;;;;N;;;;; +0BC0;TAMIL VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;; +0BC1;TAMIL VOWEL SIGN U;Mc;0;L;;;;;N;;;;; +0BC2;TAMIL VOWEL SIGN UU;Mc;0;L;;;;;N;;;;; +0BC6;TAMIL VOWEL SIGN E;Mc;0;L;;;;;N;;;;; +0BC7;TAMIL VOWEL SIGN EE;Mc;0;L;;;;;N;;;;; +0BC8;TAMIL VOWEL SIGN AI;Mc;0;L;;;;;N;;;;; +0BCA;TAMIL VOWEL SIGN O;Mc;0;L;0BC6 0BBE;;;;N;;;;; +0BCB;TAMIL VOWEL SIGN OO;Mc;0;L;0BC7 0BBE;;;;N;;;;; +0BCC;TAMIL VOWEL SIGN AU;Mc;0;L;0BC6 0BD7;;;;N;;;;; +0BCD;TAMIL SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; +0BD7;TAMIL AU LENGTH MARK;Mc;0;L;;;;;N;;;;; +0BE7;TAMIL DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +0BE8;TAMIL DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +0BE9;TAMIL DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +0BEA;TAMIL DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +0BEB;TAMIL DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +0BEC;TAMIL DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +0BED;TAMIL DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +0BEE;TAMIL DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +0BEF;TAMIL DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +0BF0;TAMIL NUMBER TEN;No;0;L;;;;10;N;;;;; +0BF1;TAMIL NUMBER ONE HUNDRED;No;0;L;;;;100;N;;;;; +0BF2;TAMIL NUMBER ONE THOUSAND;No;0;L;;;;1000;N;;;;; +0C01;TELUGU SIGN CANDRABINDU;Mc;0;L;;;;;N;;;;; +0C02;TELUGU SIGN ANUSVARA;Mc;0;L;;;;;N;;;;; +0C03;TELUGU SIGN VISARGA;Mc;0;L;;;;;N;;;;; +0C05;TELUGU LETTER A;Lo;0;L;;;;;N;;;;; +0C06;TELUGU LETTER AA;Lo;0;L;;;;;N;;;;; +0C07;TELUGU LETTER I;Lo;0;L;;;;;N;;;;; +0C08;TELUGU LETTER II;Lo;0;L;;;;;N;;;;; +0C09;TELUGU LETTER U;Lo;0;L;;;;;N;;;;; +0C0A;TELUGU LETTER UU;Lo;0;L;;;;;N;;;;; +0C0B;TELUGU LETTER VOCALIC R;Lo;0;L;;;;;N;;;;; +0C0C;TELUGU LETTER VOCALIC L;Lo;0;L;;;;;N;;;;; +0C0E;TELUGU LETTER E;Lo;0;L;;;;;N;;;;; +0C0F;TELUGU LETTER EE;Lo;0;L;;;;;N;;;;; +0C10;TELUGU LETTER AI;Lo;0;L;;;;;N;;;;; +0C12;TELUGU LETTER O;Lo;0;L;;;;;N;;;;; +0C13;TELUGU LETTER OO;Lo;0;L;;;;;N;;;;; +0C14;TELUGU LETTER AU;Lo;0;L;;;;;N;;;;; +0C15;TELUGU LETTER KA;Lo;0;L;;;;;N;;;;; +0C16;TELUGU LETTER KHA;Lo;0;L;;;;;N;;;;; +0C17;TELUGU LETTER GA;Lo;0;L;;;;;N;;;;; +0C18;TELUGU LETTER GHA;Lo;0;L;;;;;N;;;;; +0C19;TELUGU LETTER NGA;Lo;0;L;;;;;N;;;;; +0C1A;TELUGU LETTER CA;Lo;0;L;;;;;N;;;;; +0C1B;TELUGU LETTER CHA;Lo;0;L;;;;;N;;;;; +0C1C;TELUGU LETTER JA;Lo;0;L;;;;;N;;;;; +0C1D;TELUGU LETTER JHA;Lo;0;L;;;;;N;;;;; +0C1E;TELUGU LETTER NYA;Lo;0;L;;;;;N;;;;; +0C1F;TELUGU LETTER TTA;Lo;0;L;;;;;N;;;;; +0C20;TELUGU LETTER TTHA;Lo;0;L;;;;;N;;;;; +0C21;TELUGU LETTER DDA;Lo;0;L;;;;;N;;;;; +0C22;TELUGU LETTER DDHA;Lo;0;L;;;;;N;;;;; +0C23;TELUGU LETTER NNA;Lo;0;L;;;;;N;;;;; +0C24;TELUGU LETTER TA;Lo;0;L;;;;;N;;;;; +0C25;TELUGU LETTER THA;Lo;0;L;;;;;N;;;;; +0C26;TELUGU LETTER DA;Lo;0;L;;;;;N;;;;; +0C27;TELUGU LETTER DHA;Lo;0;L;;;;;N;;;;; +0C28;TELUGU LETTER NA;Lo;0;L;;;;;N;;;;; +0C2A;TELUGU LETTER PA;Lo;0;L;;;;;N;;;;; +0C2B;TELUGU LETTER PHA;Lo;0;L;;;;;N;;;;; +0C2C;TELUGU LETTER BA;Lo;0;L;;;;;N;;;;; +0C2D;TELUGU LETTER BHA;Lo;0;L;;;;;N;;;;; +0C2E;TELUGU LETTER MA;Lo;0;L;;;;;N;;;;; +0C2F;TELUGU LETTER YA;Lo;0;L;;;;;N;;;;; +0C30;TELUGU LETTER RA;Lo;0;L;;;;;N;;;;; +0C31;TELUGU LETTER RRA;Lo;0;L;;;;;N;;;;; +0C32;TELUGU LETTER LA;Lo;0;L;;;;;N;;;;; +0C33;TELUGU LETTER LLA;Lo;0;L;;;;;N;;;;; +0C35;TELUGU LETTER VA;Lo;0;L;;;;;N;;;;; +0C36;TELUGU LETTER SHA;Lo;0;L;;;;;N;;;;; +0C37;TELUGU LETTER SSA;Lo;0;L;;;;;N;;;;; +0C38;TELUGU LETTER SA;Lo;0;L;;;;;N;;;;; +0C39;TELUGU LETTER HA;Lo;0;L;;;;;N;;;;; +0C3E;TELUGU VOWEL SIGN AA;Mn;0;NSM;;;;;N;;;;; +0C3F;TELUGU VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;; +0C40;TELUGU VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;; +0C41;TELUGU VOWEL SIGN U;Mc;0;L;;;;;N;;;;; +0C42;TELUGU VOWEL SIGN UU;Mc;0;L;;;;;N;;;;; +0C43;TELUGU VOWEL SIGN VOCALIC R;Mc;0;L;;;;;N;;;;; +0C44;TELUGU VOWEL SIGN VOCALIC RR;Mc;0;L;;;;;N;;;;; +0C46;TELUGU VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;; +0C47;TELUGU VOWEL SIGN EE;Mn;0;NSM;;;;;N;;;;; +0C48;TELUGU VOWEL SIGN AI;Mn;0;NSM;0C46 0C56;;;;N;;;;; +0C4A;TELUGU VOWEL SIGN O;Mn;0;NSM;;;;;N;;;;; +0C4B;TELUGU VOWEL SIGN OO;Mn;0;NSM;;;;;N;;;;; +0C4C;TELUGU VOWEL SIGN AU;Mn;0;NSM;;;;;N;;;;; +0C4D;TELUGU SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; +0C55;TELUGU LENGTH MARK;Mn;84;NSM;;;;;N;;;;; +0C56;TELUGU AI LENGTH MARK;Mn;91;NSM;;;;;N;;;;; +0C60;TELUGU LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;; +0C61;TELUGU LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;; +0C66;TELUGU DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +0C67;TELUGU DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +0C68;TELUGU DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +0C69;TELUGU DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +0C6A;TELUGU DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +0C6B;TELUGU DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +0C6C;TELUGU DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +0C6D;TELUGU DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +0C6E;TELUGU DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +0C6F;TELUGU DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +0C82;KANNADA SIGN ANUSVARA;Mc;0;L;;;;;N;;;;; +0C83;KANNADA SIGN VISARGA;Mc;0;L;;;;;N;;;;; +0C85;KANNADA LETTER A;Lo;0;L;;;;;N;;;;; +0C86;KANNADA LETTER AA;Lo;0;L;;;;;N;;;;; +0C87;KANNADA LETTER I;Lo;0;L;;;;;N;;;;; +0C88;KANNADA LETTER II;Lo;0;L;;;;;N;;;;; +0C89;KANNADA LETTER U;Lo;0;L;;;;;N;;;;; +0C8A;KANNADA LETTER UU;Lo;0;L;;;;;N;;;;; +0C8B;KANNADA LETTER VOCALIC R;Lo;0;L;;;;;N;;;;; +0C8C;KANNADA LETTER VOCALIC L;Lo;0;L;;;;;N;;;;; +0C8E;KANNADA LETTER E;Lo;0;L;;;;;N;;;;; +0C8F;KANNADA LETTER EE;Lo;0;L;;;;;N;;;;; +0C90;KANNADA LETTER AI;Lo;0;L;;;;;N;;;;; +0C92;KANNADA LETTER O;Lo;0;L;;;;;N;;;;; +0C93;KANNADA LETTER OO;Lo;0;L;;;;;N;;;;; +0C94;KANNADA LETTER AU;Lo;0;L;;;;;N;;;;; +0C95;KANNADA LETTER KA;Lo;0;L;;;;;N;;;;; +0C96;KANNADA LETTER KHA;Lo;0;L;;;;;N;;;;; +0C97;KANNADA LETTER GA;Lo;0;L;;;;;N;;;;; +0C98;KANNADA LETTER GHA;Lo;0;L;;;;;N;;;;; +0C99;KANNADA LETTER NGA;Lo;0;L;;;;;N;;;;; +0C9A;KANNADA LETTER CA;Lo;0;L;;;;;N;;;;; +0C9B;KANNADA LETTER CHA;Lo;0;L;;;;;N;;;;; +0C9C;KANNADA LETTER JA;Lo;0;L;;;;;N;;;;; +0C9D;KANNADA LETTER JHA;Lo;0;L;;;;;N;;;;; +0C9E;KANNADA LETTER NYA;Lo;0;L;;;;;N;;;;; +0C9F;KANNADA LETTER TTA;Lo;0;L;;;;;N;;;;; +0CA0;KANNADA LETTER TTHA;Lo;0;L;;;;;N;;;;; +0CA1;KANNADA LETTER DDA;Lo;0;L;;;;;N;;;;; +0CA2;KANNADA LETTER DDHA;Lo;0;L;;;;;N;;;;; +0CA3;KANNADA LETTER NNA;Lo;0;L;;;;;N;;;;; +0CA4;KANNADA LETTER TA;Lo;0;L;;;;;N;;;;; +0CA5;KANNADA LETTER THA;Lo;0;L;;;;;N;;;;; +0CA6;KANNADA LETTER DA;Lo;0;L;;;;;N;;;;; +0CA7;KANNADA LETTER DHA;Lo;0;L;;;;;N;;;;; +0CA8;KANNADA LETTER NA;Lo;0;L;;;;;N;;;;; +0CAA;KANNADA LETTER PA;Lo;0;L;;;;;N;;;;; +0CAB;KANNADA LETTER PHA;Lo;0;L;;;;;N;;;;; +0CAC;KANNADA LETTER BA;Lo;0;L;;;;;N;;;;; +0CAD;KANNADA LETTER BHA;Lo;0;L;;;;;N;;;;; +0CAE;KANNADA LETTER MA;Lo;0;L;;;;;N;;;;; +0CAF;KANNADA LETTER YA;Lo;0;L;;;;;N;;;;; +0CB0;KANNADA LETTER RA;Lo;0;L;;;;;N;;;;; +0CB1;KANNADA LETTER RRA;Lo;0;L;;;;;N;;;;; +0CB2;KANNADA LETTER LA;Lo;0;L;;;;;N;;;;; +0CB3;KANNADA LETTER LLA;Lo;0;L;;;;;N;;;;; +0CB5;KANNADA LETTER VA;Lo;0;L;;;;;N;;;;; +0CB6;KANNADA LETTER SHA;Lo;0;L;;;;;N;;;;; +0CB7;KANNADA LETTER SSA;Lo;0;L;;;;;N;;;;; +0CB8;KANNADA LETTER SA;Lo;0;L;;;;;N;;;;; +0CB9;KANNADA LETTER HA;Lo;0;L;;;;;N;;;;; +0CBE;KANNADA VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; +0CBF;KANNADA VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;; +0CC0;KANNADA VOWEL SIGN II;Mc;0;L;0CBF 0CD5;;;;N;;;;; +0CC1;KANNADA VOWEL SIGN U;Mc;0;L;;;;;N;;;;; +0CC2;KANNADA VOWEL SIGN UU;Mc;0;L;;;;;N;;;;; +0CC3;KANNADA VOWEL SIGN VOCALIC R;Mc;0;L;;;;;N;;;;; +0CC4;KANNADA VOWEL SIGN VOCALIC RR;Mc;0;L;;;;;N;;;;; +0CC6;KANNADA VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;; +0CC7;KANNADA VOWEL SIGN EE;Mc;0;L;0CC6 0CD5;;;;N;;;;; +0CC8;KANNADA VOWEL SIGN AI;Mc;0;L;0CC6 0CD6;;;;N;;;;; +0CCA;KANNADA VOWEL SIGN O;Mc;0;L;0CC6 0CC2;;;;N;;;;; +0CCB;KANNADA VOWEL SIGN OO;Mc;0;L;0CCA 0CD5;;;;N;;;;; +0CCC;KANNADA VOWEL SIGN AU;Mn;0;NSM;;;;;N;;;;; +0CCD;KANNADA SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; +0CD5;KANNADA LENGTH MARK;Mc;0;L;;;;;N;;;;; +0CD6;KANNADA AI LENGTH MARK;Mc;0;L;;;;;N;;;;; +0CDE;KANNADA LETTER FA;Lo;0;L;;;;;N;;;;; +0CE0;KANNADA LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;; +0CE1;KANNADA LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;; +0CE6;KANNADA DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +0CE7;KANNADA DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +0CE8;KANNADA DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +0CE9;KANNADA DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +0CEA;KANNADA DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +0CEB;KANNADA DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +0CEC;KANNADA DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +0CED;KANNADA DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +0CEE;KANNADA DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +0CEF;KANNADA DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +0D02;MALAYALAM SIGN ANUSVARA;Mc;0;L;;;;;N;;;;; +0D03;MALAYALAM SIGN VISARGA;Mc;0;L;;;;;N;;;;; +0D05;MALAYALAM LETTER A;Lo;0;L;;;;;N;;;;; +0D06;MALAYALAM LETTER AA;Lo;0;L;;;;;N;;;;; +0D07;MALAYALAM LETTER I;Lo;0;L;;;;;N;;;;; +0D08;MALAYALAM LETTER II;Lo;0;L;;;;;N;;;;; +0D09;MALAYALAM LETTER U;Lo;0;L;;;;;N;;;;; +0D0A;MALAYALAM LETTER UU;Lo;0;L;;;;;N;;;;; +0D0B;MALAYALAM LETTER VOCALIC R;Lo;0;L;;;;;N;;;;; +0D0C;MALAYALAM LETTER VOCALIC L;Lo;0;L;;;;;N;;;;; +0D0E;MALAYALAM LETTER E;Lo;0;L;;;;;N;;;;; +0D0F;MALAYALAM LETTER EE;Lo;0;L;;;;;N;;;;; +0D10;MALAYALAM LETTER AI;Lo;0;L;;;;;N;;;;; +0D12;MALAYALAM LETTER O;Lo;0;L;;;;;N;;;;; +0D13;MALAYALAM LETTER OO;Lo;0;L;;;;;N;;;;; +0D14;MALAYALAM LETTER AU;Lo;0;L;;;;;N;;;;; +0D15;MALAYALAM LETTER KA;Lo;0;L;;;;;N;;;;; +0D16;MALAYALAM LETTER KHA;Lo;0;L;;;;;N;;;;; +0D17;MALAYALAM LETTER GA;Lo;0;L;;;;;N;;;;; +0D18;MALAYALAM LETTER GHA;Lo;0;L;;;;;N;;;;; +0D19;MALAYALAM LETTER NGA;Lo;0;L;;;;;N;;;;; +0D1A;MALAYALAM LETTER CA;Lo;0;L;;;;;N;;;;; +0D1B;MALAYALAM LETTER CHA;Lo;0;L;;;;;N;;;;; +0D1C;MALAYALAM LETTER JA;Lo;0;L;;;;;N;;;;; +0D1D;MALAYALAM LETTER JHA;Lo;0;L;;;;;N;;;;; +0D1E;MALAYALAM LETTER NYA;Lo;0;L;;;;;N;;;;; +0D1F;MALAYALAM LETTER TTA;Lo;0;L;;;;;N;;;;; +0D20;MALAYALAM LETTER TTHA;Lo;0;L;;;;;N;;;;; +0D21;MALAYALAM LETTER DDA;Lo;0;L;;;;;N;;;;; +0D22;MALAYALAM LETTER DDHA;Lo;0;L;;;;;N;;;;; +0D23;MALAYALAM LETTER NNA;Lo;0;L;;;;;N;;;;; +0D24;MALAYALAM LETTER TA;Lo;0;L;;;;;N;;;;; +0D25;MALAYALAM LETTER THA;Lo;0;L;;;;;N;;;;; +0D26;MALAYALAM LETTER DA;Lo;0;L;;;;;N;;;;; +0D27;MALAYALAM LETTER DHA;Lo;0;L;;;;;N;;;;; +0D28;MALAYALAM LETTER NA;Lo;0;L;;;;;N;;;;; +0D2A;MALAYALAM LETTER PA;Lo;0;L;;;;;N;;;;; +0D2B;MALAYALAM LETTER PHA;Lo;0;L;;;;;N;;;;; +0D2C;MALAYALAM LETTER BA;Lo;0;L;;;;;N;;;;; +0D2D;MALAYALAM LETTER BHA;Lo;0;L;;;;;N;;;;; +0D2E;MALAYALAM LETTER MA;Lo;0;L;;;;;N;;;;; +0D2F;MALAYALAM LETTER YA;Lo;0;L;;;;;N;;;;; +0D30;MALAYALAM LETTER RA;Lo;0;L;;;;;N;;;;; +0D31;MALAYALAM LETTER RRA;Lo;0;L;;;;;N;;;;; +0D32;MALAYALAM LETTER LA;Lo;0;L;;;;;N;;;;; +0D33;MALAYALAM LETTER LLA;Lo;0;L;;;;;N;;;;; +0D34;MALAYALAM LETTER LLLA;Lo;0;L;;;;;N;;;;; +0D35;MALAYALAM LETTER VA;Lo;0;L;;;;;N;;;;; +0D36;MALAYALAM LETTER SHA;Lo;0;L;;;;;N;;;;; +0D37;MALAYALAM LETTER SSA;Lo;0;L;;;;;N;;;;; +0D38;MALAYALAM LETTER SA;Lo;0;L;;;;;N;;;;; +0D39;MALAYALAM LETTER HA;Lo;0;L;;;;;N;;;;; +0D3E;MALAYALAM VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; +0D3F;MALAYALAM VOWEL SIGN I;Mc;0;L;;;;;N;;;;; +0D40;MALAYALAM VOWEL SIGN II;Mc;0;L;;;;;N;;;;; +0D41;MALAYALAM VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; +0D42;MALAYALAM VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; +0D43;MALAYALAM VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;; +0D46;MALAYALAM VOWEL SIGN E;Mc;0;L;;;;;N;;;;; +0D47;MALAYALAM VOWEL SIGN EE;Mc;0;L;;;;;N;;;;; +0D48;MALAYALAM VOWEL SIGN AI;Mc;0;L;;;;;N;;;;; +0D4A;MALAYALAM VOWEL SIGN O;Mc;0;L;0D46 0D3E;;;;N;;;;; +0D4B;MALAYALAM VOWEL SIGN OO;Mc;0;L;0D47 0D3E;;;;N;;;;; +0D4C;MALAYALAM VOWEL SIGN AU;Mc;0;L;0D46 0D57;;;;N;;;;; +0D4D;MALAYALAM SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; +0D57;MALAYALAM AU LENGTH MARK;Mc;0;L;;;;;N;;;;; +0D60;MALAYALAM LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;; +0D61;MALAYALAM LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;; +0D66;MALAYALAM DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +0D67;MALAYALAM DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +0D68;MALAYALAM DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +0D69;MALAYALAM DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +0D6A;MALAYALAM DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +0D6B;MALAYALAM DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +0D6C;MALAYALAM DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +0D6D;MALAYALAM DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +0D6E;MALAYALAM DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +0D6F;MALAYALAM DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +0D82;SINHALA SIGN ANUSVARAYA;Mc;0;L;;;;;N;;;;; +0D83;SINHALA SIGN VISARGAYA;Mc;0;L;;;;;N;;;;; +0D85;SINHALA LETTER AYANNA;Lo;0;L;;;;;N;;;;; +0D86;SINHALA LETTER AAYANNA;Lo;0;L;;;;;N;;;;; +0D87;SINHALA LETTER AEYANNA;Lo;0;L;;;;;N;;;;; +0D88;SINHALA LETTER AEEYANNA;Lo;0;L;;;;;N;;;;; +0D89;SINHALA LETTER IYANNA;Lo;0;L;;;;;N;;;;; +0D8A;SINHALA LETTER IIYANNA;Lo;0;L;;;;;N;;;;; +0D8B;SINHALA LETTER UYANNA;Lo;0;L;;;;;N;;;;; +0D8C;SINHALA LETTER UUYANNA;Lo;0;L;;;;;N;;;;; +0D8D;SINHALA LETTER IRUYANNA;Lo;0;L;;;;;N;;;;; +0D8E;SINHALA LETTER IRUUYANNA;Lo;0;L;;;;;N;;;;; +0D8F;SINHALA LETTER ILUYANNA;Lo;0;L;;;;;N;;;;; +0D90;SINHALA LETTER ILUUYANNA;Lo;0;L;;;;;N;;;;; +0D91;SINHALA LETTER EYANNA;Lo;0;L;;;;;N;;;;; +0D92;SINHALA LETTER EEYANNA;Lo;0;L;;;;;N;;;;; +0D93;SINHALA LETTER AIYANNA;Lo;0;L;;;;;N;;;;; +0D94;SINHALA LETTER OYANNA;Lo;0;L;;;;;N;;;;; +0D95;SINHALA LETTER OOYANNA;Lo;0;L;;;;;N;;;;; +0D96;SINHALA LETTER AUYANNA;Lo;0;L;;;;;N;;;;; +0D9A;SINHALA LETTER ALPAPRAANA KAYANNA;Lo;0;L;;;;;N;;;;; +0D9B;SINHALA LETTER MAHAAPRAANA KAYANNA;Lo;0;L;;;;;N;;;;; +0D9C;SINHALA LETTER ALPAPRAANA GAYANNA;Lo;0;L;;;;;N;;;;; +0D9D;SINHALA LETTER MAHAAPRAANA GAYANNA;Lo;0;L;;;;;N;;;;; +0D9E;SINHALA LETTER KANTAJA NAASIKYAYA;Lo;0;L;;;;;N;;;;; +0D9F;SINHALA LETTER SANYAKA GAYANNA;Lo;0;L;;;;;N;;;;; +0DA0;SINHALA LETTER ALPAPRAANA CAYANNA;Lo;0;L;;;;;N;;;;; +0DA1;SINHALA LETTER MAHAAPRAANA CAYANNA;Lo;0;L;;;;;N;;;;; +0DA2;SINHALA LETTER ALPAPRAANA JAYANNA;Lo;0;L;;;;;N;;;;; +0DA3;SINHALA LETTER MAHAAPRAANA JAYANNA;Lo;0;L;;;;;N;;;;; +0DA4;SINHALA LETTER TAALUJA NAASIKYAYA;Lo;0;L;;;;;N;;;;; +0DA5;SINHALA LETTER TAALUJA SANYOOGA NAAKSIKYAYA;Lo;0;L;;;;;N;;;;; +0DA6;SINHALA LETTER SANYAKA JAYANNA;Lo;0;L;;;;;N;;;;; +0DA7;SINHALA LETTER ALPAPRAANA TTAYANNA;Lo;0;L;;;;;N;;;;; +0DA8;SINHALA LETTER MAHAAPRAANA TTAYANNA;Lo;0;L;;;;;N;;;;; +0DA9;SINHALA LETTER ALPAPRAANA DDAYANNA;Lo;0;L;;;;;N;;;;; +0DAA;SINHALA LETTER MAHAAPRAANA DDAYANNA;Lo;0;L;;;;;N;;;;; +0DAB;SINHALA LETTER MUURDHAJA NAYANNA;Lo;0;L;;;;;N;;;;; +0DAC;SINHALA LETTER SANYAKA DDAYANNA;Lo;0;L;;;;;N;;;;; +0DAD;SINHALA LETTER ALPAPRAANA TAYANNA;Lo;0;L;;;;;N;;;;; +0DAE;SINHALA LETTER MAHAAPRAANA TAYANNA;Lo;0;L;;;;;N;;;;; +0DAF;SINHALA LETTER ALPAPRAANA DAYANNA;Lo;0;L;;;;;N;;;;; +0DB0;SINHALA LETTER MAHAAPRAANA DAYANNA;Lo;0;L;;;;;N;;;;; +0DB1;SINHALA LETTER DANTAJA NAYANNA;Lo;0;L;;;;;N;;;;; +0DB3;SINHALA LETTER SANYAKA DAYANNA;Lo;0;L;;;;;N;;;;; +0DB4;SINHALA LETTER ALPAPRAANA PAYANNA;Lo;0;L;;;;;N;;;;; +0DB5;SINHALA LETTER MAHAAPRAANA PAYANNA;Lo;0;L;;;;;N;;;;; +0DB6;SINHALA LETTER ALPAPRAANA BAYANNA;Lo;0;L;;;;;N;;;;; +0DB7;SINHALA LETTER MAHAAPRAANA BAYANNA;Lo;0;L;;;;;N;;;;; +0DB8;SINHALA LETTER MAYANNA;Lo;0;L;;;;;N;;;;; +0DB9;SINHALA LETTER AMBA BAYANNA;Lo;0;L;;;;;N;;;;; +0DBA;SINHALA LETTER YAYANNA;Lo;0;L;;;;;N;;;;; +0DBB;SINHALA LETTER RAYANNA;Lo;0;L;;;;;N;;;;; +0DBD;SINHALA LETTER DANTAJA LAYANNA;Lo;0;L;;;;;N;;;;; +0DC0;SINHALA LETTER VAYANNA;Lo;0;L;;;;;N;;;;; +0DC1;SINHALA LETTER TAALUJA SAYANNA;Lo;0;L;;;;;N;;;;; +0DC2;SINHALA LETTER MUURDHAJA SAYANNA;Lo;0;L;;;;;N;;;;; +0DC3;SINHALA LETTER DANTAJA SAYANNA;Lo;0;L;;;;;N;;;;; +0DC4;SINHALA LETTER HAYANNA;Lo;0;L;;;;;N;;;;; +0DC5;SINHALA LETTER MUURDHAJA LAYANNA;Lo;0;L;;;;;N;;;;; +0DC6;SINHALA LETTER FAYANNA;Lo;0;L;;;;;N;;;;; +0DCA;SINHALA SIGN AL-LAKUNA;Mn;9;NSM;;;;;N;;;;; +0DCF;SINHALA VOWEL SIGN AELA-PILLA;Mc;0;L;;;;;N;;;;; +0DD0;SINHALA VOWEL SIGN KETTI AEDA-PILLA;Mc;0;L;;;;;N;;;;; +0DD1;SINHALA VOWEL SIGN DIGA AEDA-PILLA;Mc;0;L;;;;;N;;;;; +0DD2;SINHALA VOWEL SIGN KETTI IS-PILLA;Mn;0;NSM;;;;;N;;;;; +0DD3;SINHALA VOWEL SIGN DIGA IS-PILLA;Mn;0;NSM;;;;;N;;;;; +0DD4;SINHALA VOWEL SIGN KETTI PAA-PILLA;Mn;0;NSM;;;;;N;;;;; +0DD6;SINHALA VOWEL SIGN DIGA PAA-PILLA;Mn;0;NSM;;;;;N;;;;; +0DD8;SINHALA VOWEL SIGN GAETTA-PILLA;Mc;0;L;;;;;N;;;;; +0DD9;SINHALA VOWEL SIGN KOMBUVA;Mc;0;L;;;;;N;;;;; +0DDA;SINHALA VOWEL SIGN DIGA KOMBUVA;Mc;0;L;0DD9 0DCA;;;;N;;;;; +0DDB;SINHALA VOWEL SIGN KOMBU DEKA;Mc;0;L;;;;;N;;;;; +0DDC;SINHALA VOWEL SIGN KOMBUVA HAA AELA-PILLA;Mc;0;L;0DD9 0DCF;;;;N;;;;; +0DDD;SINHALA VOWEL SIGN KOMBUVA HAA DIGA AELA-PILLA;Mc;0;L;0DDC 0DCA;;;;N;;;;; +0DDE;SINHALA VOWEL SIGN KOMBUVA HAA GAYANUKITTA;Mc;0;L;0DD9 0DDF;;;;N;;;;; +0DDF;SINHALA VOWEL SIGN GAYANUKITTA;Mc;0;L;;;;;N;;;;; +0DF2;SINHALA VOWEL SIGN DIGA GAETTA-PILLA;Mc;0;L;;;;;N;;;;; +0DF3;SINHALA VOWEL SIGN DIGA GAYANUKITTA;Mc;0;L;;;;;N;;;;; +0DF4;SINHALA PUNCTUATION KUNDDALIYA;Po;0;L;;;;;N;;;;; +0E01;THAI CHARACTER KO KAI;Lo;0;L;;;;;N;THAI LETTER KO KAI;;;; +0E02;THAI CHARACTER KHO KHAI;Lo;0;L;;;;;N;THAI LETTER KHO KHAI;;;; +0E03;THAI CHARACTER KHO KHUAT;Lo;0;L;;;;;N;THAI LETTER KHO KHUAT;;;; +0E04;THAI CHARACTER KHO KHWAI;Lo;0;L;;;;;N;THAI LETTER KHO KHWAI;;;; +0E05;THAI CHARACTER KHO KHON;Lo;0;L;;;;;N;THAI LETTER KHO KHON;;;; +0E06;THAI CHARACTER KHO RAKHANG;Lo;0;L;;;;;N;THAI LETTER KHO RAKHANG;;;; +0E07;THAI CHARACTER NGO NGU;Lo;0;L;;;;;N;THAI LETTER NGO NGU;;;; +0E08;THAI CHARACTER CHO CHAN;Lo;0;L;;;;;N;THAI LETTER CHO CHAN;;;; +0E09;THAI CHARACTER CHO CHING;Lo;0;L;;;;;N;THAI LETTER CHO CHING;;;; +0E0A;THAI CHARACTER CHO CHANG;Lo;0;L;;;;;N;THAI LETTER CHO CHANG;;;; +0E0B;THAI CHARACTER SO SO;Lo;0;L;;;;;N;THAI LETTER SO SO;;;; +0E0C;THAI CHARACTER CHO CHOE;Lo;0;L;;;;;N;THAI LETTER CHO CHOE;;;; +0E0D;THAI CHARACTER YO YING;Lo;0;L;;;;;N;THAI LETTER YO YING;;;; +0E0E;THAI CHARACTER DO CHADA;Lo;0;L;;;;;N;THAI LETTER DO CHADA;;;; +0E0F;THAI CHARACTER TO PATAK;Lo;0;L;;;;;N;THAI LETTER TO PATAK;;;; +0E10;THAI CHARACTER THO THAN;Lo;0;L;;;;;N;THAI LETTER THO THAN;;;; +0E11;THAI CHARACTER THO NANGMONTHO;Lo;0;L;;;;;N;THAI LETTER THO NANGMONTHO;;;; +0E12;THAI CHARACTER THO PHUTHAO;Lo;0;L;;;;;N;THAI LETTER THO PHUTHAO;;;; +0E13;THAI CHARACTER NO NEN;Lo;0;L;;;;;N;THAI LETTER NO NEN;;;; +0E14;THAI CHARACTER DO DEK;Lo;0;L;;;;;N;THAI LETTER DO DEK;;;; +0E15;THAI CHARACTER TO TAO;Lo;0;L;;;;;N;THAI LETTER TO TAO;;;; +0E16;THAI CHARACTER THO THUNG;Lo;0;L;;;;;N;THAI LETTER THO THUNG;;;; +0E17;THAI CHARACTER THO THAHAN;Lo;0;L;;;;;N;THAI LETTER THO THAHAN;;;; +0E18;THAI CHARACTER THO THONG;Lo;0;L;;;;;N;THAI LETTER THO THONG;;;; +0E19;THAI CHARACTER NO NU;Lo;0;L;;;;;N;THAI LETTER NO NU;;;; +0E1A;THAI CHARACTER BO BAIMAI;Lo;0;L;;;;;N;THAI LETTER BO BAIMAI;;;; +0E1B;THAI CHARACTER PO PLA;Lo;0;L;;;;;N;THAI LETTER PO PLA;;;; +0E1C;THAI CHARACTER PHO PHUNG;Lo;0;L;;;;;N;THAI LETTER PHO PHUNG;;;; +0E1D;THAI CHARACTER FO FA;Lo;0;L;;;;;N;THAI LETTER FO FA;;;; +0E1E;THAI CHARACTER PHO PHAN;Lo;0;L;;;;;N;THAI LETTER PHO PHAN;;;; +0E1F;THAI CHARACTER FO FAN;Lo;0;L;;;;;N;THAI LETTER FO FAN;;;; +0E20;THAI CHARACTER PHO SAMPHAO;Lo;0;L;;;;;N;THAI LETTER PHO SAMPHAO;;;; +0E21;THAI CHARACTER MO MA;Lo;0;L;;;;;N;THAI LETTER MO MA;;;; +0E22;THAI CHARACTER YO YAK;Lo;0;L;;;;;N;THAI LETTER YO YAK;;;; +0E23;THAI CHARACTER RO RUA;Lo;0;L;;;;;N;THAI LETTER RO RUA;;;; +0E24;THAI CHARACTER RU;Lo;0;L;;;;;N;THAI LETTER RU;;;; +0E25;THAI CHARACTER LO LING;Lo;0;L;;;;;N;THAI LETTER LO LING;;;; +0E26;THAI CHARACTER LU;Lo;0;L;;;;;N;THAI LETTER LU;;;; +0E27;THAI CHARACTER WO WAEN;Lo;0;L;;;;;N;THAI LETTER WO WAEN;;;; +0E28;THAI CHARACTER SO SALA;Lo;0;L;;;;;N;THAI LETTER SO SALA;;;; +0E29;THAI CHARACTER SO RUSI;Lo;0;L;;;;;N;THAI LETTER SO RUSI;;;; +0E2A;THAI CHARACTER SO SUA;Lo;0;L;;;;;N;THAI LETTER SO SUA;;;; +0E2B;THAI CHARACTER HO HIP;Lo;0;L;;;;;N;THAI LETTER HO HIP;;;; +0E2C;THAI CHARACTER LO CHULA;Lo;0;L;;;;;N;THAI LETTER LO CHULA;;;; +0E2D;THAI CHARACTER O ANG;Lo;0;L;;;;;N;THAI LETTER O ANG;;;; +0E2E;THAI CHARACTER HO NOKHUK;Lo;0;L;;;;;N;THAI LETTER HO NOK HUK;;;; +0E2F;THAI CHARACTER PAIYANNOI;Lo;0;L;;;;;N;THAI PAI YAN NOI;paiyan noi;;; +0E30;THAI CHARACTER SARA A;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA A;;;; +0E31;THAI CHARACTER MAI HAN-AKAT;Mn;0;NSM;;;;;N;THAI VOWEL SIGN MAI HAN-AKAT;;;; +0E32;THAI CHARACTER SARA AA;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA AA;;;; +0E33;THAI CHARACTER SARA AM;Lo;0;L;<compat> 0E4D 0E32;;;;N;THAI VOWEL SIGN SARA AM;;;; +0E34;THAI CHARACTER SARA I;Mn;0;NSM;;;;;N;THAI VOWEL SIGN SARA I;;;; +0E35;THAI CHARACTER SARA II;Mn;0;NSM;;;;;N;THAI VOWEL SIGN SARA II;;;; +0E36;THAI CHARACTER SARA UE;Mn;0;NSM;;;;;N;THAI VOWEL SIGN SARA UE;;;; +0E37;THAI CHARACTER SARA UEE;Mn;0;NSM;;;;;N;THAI VOWEL SIGN SARA UEE;sara uue;;; +0E38;THAI CHARACTER SARA U;Mn;103;NSM;;;;;N;THAI VOWEL SIGN SARA U;;;; +0E39;THAI CHARACTER SARA UU;Mn;103;NSM;;;;;N;THAI VOWEL SIGN SARA UU;;;; +0E3A;THAI CHARACTER PHINTHU;Mn;9;NSM;;;;;N;THAI VOWEL SIGN PHINTHU;;;; +0E3F;THAI CURRENCY SYMBOL BAHT;Sc;0;ET;;;;;N;THAI BAHT SIGN;;;; +0E40;THAI CHARACTER SARA E;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA E;;;; +0E41;THAI CHARACTER SARA AE;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA AE;;;; +0E42;THAI CHARACTER SARA O;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA O;;;; +0E43;THAI CHARACTER SARA AI MAIMUAN;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA MAI MUAN;sara ai mai muan;;; +0E44;THAI CHARACTER SARA AI MAIMALAI;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA MAI MALAI;sara ai mai malai;;; +0E45;THAI CHARACTER LAKKHANGYAO;Lo;0;L;;;;;N;THAI LAK KHANG YAO;lakkhang yao;;; +0E46;THAI CHARACTER MAIYAMOK;Lm;0;L;;;;;N;THAI MAI YAMOK;mai yamok;;; +0E47;THAI CHARACTER MAITAIKHU;Mn;0;NSM;;;;;N;THAI VOWEL SIGN MAI TAI KHU;mai taikhu;;; +0E48;THAI CHARACTER MAI EK;Mn;107;NSM;;;;;N;THAI TONE MAI EK;;;; +0E49;THAI CHARACTER MAI THO;Mn;107;NSM;;;;;N;THAI TONE MAI THO;;;; +0E4A;THAI CHARACTER MAI TRI;Mn;107;NSM;;;;;N;THAI TONE MAI TRI;;;; +0E4B;THAI CHARACTER MAI CHATTAWA;Mn;107;NSM;;;;;N;THAI TONE MAI CHATTAWA;;;; +0E4C;THAI CHARACTER THANTHAKHAT;Mn;0;NSM;;;;;N;THAI THANTHAKHAT;;;; +0E4D;THAI CHARACTER NIKHAHIT;Mn;0;NSM;;;;;N;THAI NIKKHAHIT;nikkhahit;;; +0E4E;THAI CHARACTER YAMAKKAN;Mn;0;NSM;;;;;N;THAI YAMAKKAN;;;; +0E4F;THAI CHARACTER FONGMAN;Po;0;L;;;;;N;THAI FONGMAN;;;; +0E50;THAI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +0E51;THAI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +0E52;THAI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +0E53;THAI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +0E54;THAI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +0E55;THAI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +0E56;THAI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +0E57;THAI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +0E58;THAI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +0E59;THAI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +0E5A;THAI CHARACTER ANGKHANKHU;Po;0;L;;;;;N;THAI ANGKHANKHU;;;; +0E5B;THAI CHARACTER KHOMUT;Po;0;L;;;;;N;THAI KHOMUT;;;; +0E81;LAO LETTER KO;Lo;0;L;;;;;N;;;;; +0E82;LAO LETTER KHO SUNG;Lo;0;L;;;;;N;;;;; +0E84;LAO LETTER KHO TAM;Lo;0;L;;;;;N;;;;; +0E87;LAO LETTER NGO;Lo;0;L;;;;;N;;;;; +0E88;LAO LETTER CO;Lo;0;L;;;;;N;;;;; +0E8A;LAO LETTER SO TAM;Lo;0;L;;;;;N;;;;; +0E8D;LAO LETTER NYO;Lo;0;L;;;;;N;;;;; +0E94;LAO LETTER DO;Lo;0;L;;;;;N;;;;; +0E95;LAO LETTER TO;Lo;0;L;;;;;N;;;;; +0E96;LAO LETTER THO SUNG;Lo;0;L;;;;;N;;;;; +0E97;LAO LETTER THO TAM;Lo;0;L;;;;;N;;;;; +0E99;LAO LETTER NO;Lo;0;L;;;;;N;;;;; +0E9A;LAO LETTER BO;Lo;0;L;;;;;N;;;;; +0E9B;LAO LETTER PO;Lo;0;L;;;;;N;;;;; +0E9C;LAO LETTER PHO SUNG;Lo;0;L;;;;;N;;;;; +0E9D;LAO LETTER FO TAM;Lo;0;L;;;;;N;;;;; +0E9E;LAO LETTER PHO TAM;Lo;0;L;;;;;N;;;;; +0E9F;LAO LETTER FO SUNG;Lo;0;L;;;;;N;;;;; +0EA1;LAO LETTER MO;Lo;0;L;;;;;N;;;;; +0EA2;LAO LETTER YO;Lo;0;L;;;;;N;;;;; +0EA3;LAO LETTER LO LING;Lo;0;L;;;;;N;;;;; +0EA5;LAO LETTER LO LOOT;Lo;0;L;;;;;N;;;;; +0EA7;LAO LETTER WO;Lo;0;L;;;;;N;;;;; +0EAA;LAO LETTER SO SUNG;Lo;0;L;;;;;N;;;;; +0EAB;LAO LETTER HO SUNG;Lo;0;L;;;;;N;;;;; +0EAD;LAO LETTER O;Lo;0;L;;;;;N;;;;; +0EAE;LAO LETTER HO TAM;Lo;0;L;;;;;N;;;;; +0EAF;LAO ELLIPSIS;Lo;0;L;;;;;N;;;;; +0EB0;LAO VOWEL SIGN A;Lo;0;L;;;;;N;;;;; +0EB1;LAO VOWEL SIGN MAI KAN;Mn;0;NSM;;;;;N;;;;; +0EB2;LAO VOWEL SIGN AA;Lo;0;L;;;;;N;;;;; +0EB3;LAO VOWEL SIGN AM;Lo;0;L;<compat> 0ECD 0EB2;;;;N;;;;; +0EB4;LAO VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;; +0EB5;LAO VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;; +0EB6;LAO VOWEL SIGN Y;Mn;0;NSM;;;;;N;;;;; +0EB7;LAO VOWEL SIGN YY;Mn;0;NSM;;;;;N;;;;; +0EB8;LAO VOWEL SIGN U;Mn;118;NSM;;;;;N;;;;; +0EB9;LAO VOWEL SIGN UU;Mn;118;NSM;;;;;N;;;;; +0EBB;LAO VOWEL SIGN MAI KON;Mn;0;NSM;;;;;N;;;;; +0EBC;LAO SEMIVOWEL SIGN LO;Mn;0;NSM;;;;;N;;;;; +0EBD;LAO SEMIVOWEL SIGN NYO;Lo;0;L;;;;;N;;;;; +0EC0;LAO VOWEL SIGN E;Lo;0;L;;;;;N;;;;; +0EC1;LAO VOWEL SIGN EI;Lo;0;L;;;;;N;;;;; +0EC2;LAO VOWEL SIGN O;Lo;0;L;;;;;N;;;;; +0EC3;LAO VOWEL SIGN AY;Lo;0;L;;;;;N;;;;; +0EC4;LAO VOWEL SIGN AI;Lo;0;L;;;;;N;;;;; +0EC6;LAO KO LA;Lm;0;L;;;;;N;;;;; +0EC8;LAO TONE MAI EK;Mn;122;NSM;;;;;N;;;;; +0EC9;LAO TONE MAI THO;Mn;122;NSM;;;;;N;;;;; +0ECA;LAO TONE MAI TI;Mn;122;NSM;;;;;N;;;;; +0ECB;LAO TONE MAI CATAWA;Mn;122;NSM;;;;;N;;;;; +0ECC;LAO CANCELLATION MARK;Mn;0;NSM;;;;;N;;;;; +0ECD;LAO NIGGAHITA;Mn;0;NSM;;;;;N;;;;; +0ED0;LAO DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +0ED1;LAO DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +0ED2;LAO DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +0ED3;LAO DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +0ED4;LAO DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +0ED5;LAO DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +0ED6;LAO DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +0ED7;LAO DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +0ED8;LAO DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +0ED9;LAO DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +0EDC;LAO HO NO;Lo;0;L;<compat> 0EAB 0E99;;;;N;;;;; +0EDD;LAO HO MO;Lo;0;L;<compat> 0EAB 0EA1;;;;N;;;;; +0F00;TIBETAN SYLLABLE OM;Lo;0;L;;;;;N;;;;; +0F01;TIBETAN MARK GTER YIG MGO TRUNCATED A;So;0;L;;;;;N;;ter yik go a thung;;; +0F02;TIBETAN MARK GTER YIG MGO -UM RNAM BCAD MA;So;0;L;;;;;N;;ter yik go wum nam chey ma;;; +0F03;TIBETAN MARK GTER YIG MGO -UM GTER TSHEG MA;So;0;L;;;;;N;;ter yik go wum ter tsek ma;;; +0F04;TIBETAN MARK INITIAL YIG MGO MDUN MA;Po;0;L;;;;;N;TIBETAN SINGLE ORNAMENT;yik go dun ma;;; +0F05;TIBETAN MARK CLOSING YIG MGO SGAB MA;Po;0;L;;;;;N;;yik go kab ma;;; +0F06;TIBETAN MARK CARET YIG MGO PHUR SHAD MA;Po;0;L;;;;;N;;yik go pur shey ma;;; +0F07;TIBETAN MARK YIG MGO TSHEG SHAD MA;Po;0;L;;;;;N;;yik go tsek shey ma;;; +0F08;TIBETAN MARK SBRUL SHAD;Po;0;L;;;;;N;TIBETAN RGYANSHAD;drul shey;;; +0F09;TIBETAN MARK BSKUR YIG MGO;Po;0;L;;;;;N;;kur yik go;;; +0F0A;TIBETAN MARK BKA- SHOG YIG MGO;Po;0;L;;;;;N;;ka sho yik go;;; +0F0B;TIBETAN MARK INTERSYLLABIC TSHEG;Po;0;L;;;;;N;TIBETAN TSEG;tsek;;; +0F0C;TIBETAN MARK DELIMITER TSHEG BSTAR;Po;0;L;<noBreak> 0F0B;;;;N;;tsek tar;;; +0F0D;TIBETAN MARK SHAD;Po;0;L;;;;;N;TIBETAN SHAD;shey;;; +0F0E;TIBETAN MARK NYIS SHAD;Po;0;L;;;;;N;TIBETAN DOUBLE SHAD;nyi shey;;; +0F0F;TIBETAN MARK TSHEG SHAD;Po;0;L;;;;;N;;tsek shey;;; +0F10;TIBETAN MARK NYIS TSHEG SHAD;Po;0;L;;;;;N;;nyi tsek shey;;; +0F11;TIBETAN MARK RIN CHEN SPUNGS SHAD;Po;0;L;;;;;N;TIBETAN RINCHANPHUNGSHAD;rinchen pung shey;;; +0F12;TIBETAN MARK RGYA GRAM SHAD;Po;0;L;;;;;N;;gya tram shey;;; +0F13;TIBETAN MARK CARET -DZUD RTAGS ME LONG CAN;So;0;L;;;;;N;;dzu ta me long chen;;; +0F14;TIBETAN MARK GTER TSHEG;So;0;L;;;;;N;TIBETAN COMMA;ter tsek;;; +0F15;TIBETAN LOGOTYPE SIGN CHAD RTAGS;So;0;L;;;;;N;;che ta;;; +0F16;TIBETAN LOGOTYPE SIGN LHAG RTAGS;So;0;L;;;;;N;;hlak ta;;; +0F17;TIBETAN ASTROLOGICAL SIGN SGRA GCAN -CHAR RTAGS;So;0;L;;;;;N;;trachen char ta;;; +0F18;TIBETAN ASTROLOGICAL SIGN -KHYUD PA;Mn;220;NSM;;;;;N;;kyu pa;;; +0F19;TIBETAN ASTROLOGICAL SIGN SDONG TSHUGS;Mn;220;NSM;;;;;N;;dong tsu;;; +0F1A;TIBETAN SIGN RDEL DKAR GCIG;So;0;L;;;;;N;;deka chig;;; +0F1B;TIBETAN SIGN RDEL DKAR GNYIS;So;0;L;;;;;N;;deka nyi;;; +0F1C;TIBETAN SIGN RDEL DKAR GSUM;So;0;L;;;;;N;;deka sum;;; +0F1D;TIBETAN SIGN RDEL NAG GCIG;So;0;L;;;;;N;;dena chig;;; +0F1E;TIBETAN SIGN RDEL NAG GNYIS;So;0;L;;;;;N;;dena nyi;;; +0F1F;TIBETAN SIGN RDEL DKAR RDEL NAG;So;0;L;;;;;N;;deka dena;;; +0F20;TIBETAN DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +0F21;TIBETAN DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +0F22;TIBETAN DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +0F23;TIBETAN DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +0F24;TIBETAN DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +0F25;TIBETAN DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +0F26;TIBETAN DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +0F27;TIBETAN DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +0F28;TIBETAN DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +0F29;TIBETAN DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +0F2A;TIBETAN DIGIT HALF ONE;No;0;L;;;;1/2;N;;;;; +0F2B;TIBETAN DIGIT HALF TWO;No;0;L;;;;3/2;N;;;;; +0F2C;TIBETAN DIGIT HALF THREE;No;0;L;;;;5/2;N;;;;; +0F2D;TIBETAN DIGIT HALF FOUR;No;0;L;;;;7/2;N;;;;; +0F2E;TIBETAN DIGIT HALF FIVE;No;0;L;;;;9/2;N;;;;; +0F2F;TIBETAN DIGIT HALF SIX;No;0;L;;;;11/2;N;;;;; +0F30;TIBETAN DIGIT HALF SEVEN;No;0;L;;;;13/2;N;;;;; +0F31;TIBETAN DIGIT HALF EIGHT;No;0;L;;;;15/2;N;;;;; +0F32;TIBETAN DIGIT HALF NINE;No;0;L;;;;17/2;N;;;;; +0F33;TIBETAN DIGIT HALF ZERO;No;0;L;;;;-1/2;N;;;;; +0F34;TIBETAN MARK BSDUS RTAGS;So;0;L;;;;;N;;du ta;;; +0F35;TIBETAN MARK NGAS BZUNG NYI ZLA;Mn;220;NSM;;;;;N;TIBETAN HONORIFIC UNDER RING;nge zung nyi da;;; +0F36;TIBETAN MARK CARET -DZUD RTAGS BZHI MIG CAN;So;0;L;;;;;N;;dzu ta shi mig chen;;; +0F37;TIBETAN MARK NGAS BZUNG SGOR RTAGS;Mn;220;NSM;;;;;N;TIBETAN UNDER RING;nge zung gor ta;;; +0F38;TIBETAN MARK CHE MGO;So;0;L;;;;;N;;che go;;; +0F39;TIBETAN MARK TSA -PHRU;Mn;216;NSM;;;;;N;TIBETAN LENITION MARK;tsa tru;;; +0F3A;TIBETAN MARK GUG RTAGS GYON;Ps;0;ON;;;;;N;;gug ta yun;;; +0F3B;TIBETAN MARK GUG RTAGS GYAS;Pe;0;ON;;;;;N;;gug ta ye;;; +0F3C;TIBETAN MARK ANG KHANG GYON;Ps;0;ON;;;;;N;TIBETAN LEFT BRACE;ang kang yun;;; +0F3D;TIBETAN MARK ANG KHANG GYAS;Pe;0;ON;;;;;N;TIBETAN RIGHT BRACE;ang kang ye;;; +0F3E;TIBETAN SIGN YAR TSHES;Mc;0;L;;;;;N;;yar tse;;; +0F3F;TIBETAN SIGN MAR TSHES;Mc;0;L;;;;;N;;mar tse;;; +0F40;TIBETAN LETTER KA;Lo;0;L;;;;;N;;;;; +0F41;TIBETAN LETTER KHA;Lo;0;L;;;;;N;;;;; +0F42;TIBETAN LETTER GA;Lo;0;L;;;;;N;;;;; +0F43;TIBETAN LETTER GHA;Lo;0;L;0F42 0FB7;;;;N;;;;; +0F44;TIBETAN LETTER NGA;Lo;0;L;;;;;N;;;;; +0F45;TIBETAN LETTER CA;Lo;0;L;;;;;N;;;;; +0F46;TIBETAN LETTER CHA;Lo;0;L;;;;;N;;;;; +0F47;TIBETAN LETTER JA;Lo;0;L;;;;;N;;;;; +0F49;TIBETAN LETTER NYA;Lo;0;L;;;;;N;;;;; +0F4A;TIBETAN LETTER TTA;Lo;0;L;;;;;N;TIBETAN LETTER REVERSED TA;;;; +0F4B;TIBETAN LETTER TTHA;Lo;0;L;;;;;N;TIBETAN LETTER REVERSED THA;;;; +0F4C;TIBETAN LETTER DDA;Lo;0;L;;;;;N;TIBETAN LETTER REVERSED DA;;;; +0F4D;TIBETAN LETTER DDHA;Lo;0;L;0F4C 0FB7;;;;N;;;;; +0F4E;TIBETAN LETTER NNA;Lo;0;L;;;;;N;TIBETAN LETTER REVERSED NA;;;; +0F4F;TIBETAN LETTER TA;Lo;0;L;;;;;N;;;;; +0F50;TIBETAN LETTER THA;Lo;0;L;;;;;N;;;;; +0F51;TIBETAN LETTER DA;Lo;0;L;;;;;N;;;;; +0F52;TIBETAN LETTER DHA;Lo;0;L;0F51 0FB7;;;;N;;;;; +0F53;TIBETAN LETTER NA;Lo;0;L;;;;;N;;;;; +0F54;TIBETAN LETTER PA;Lo;0;L;;;;;N;;;;; +0F55;TIBETAN LETTER PHA;Lo;0;L;;;;;N;;;;; +0F56;TIBETAN LETTER BA;Lo;0;L;;;;;N;;;;; +0F57;TIBETAN LETTER BHA;Lo;0;L;0F56 0FB7;;;;N;;;;; +0F58;TIBETAN LETTER MA;Lo;0;L;;;;;N;;;;; +0F59;TIBETAN LETTER TSA;Lo;0;L;;;;;N;;;;; +0F5A;TIBETAN LETTER TSHA;Lo;0;L;;;;;N;;;;; +0F5B;TIBETAN LETTER DZA;Lo;0;L;;;;;N;;;;; +0F5C;TIBETAN LETTER DZHA;Lo;0;L;0F5B 0FB7;;;;N;;;;; +0F5D;TIBETAN LETTER WA;Lo;0;L;;;;;N;;;;; +0F5E;TIBETAN LETTER ZHA;Lo;0;L;;;;;N;;;;; +0F5F;TIBETAN LETTER ZA;Lo;0;L;;;;;N;;;;; +0F60;TIBETAN LETTER -A;Lo;0;L;;;;;N;TIBETAN LETTER AA;;;; +0F61;TIBETAN LETTER YA;Lo;0;L;;;;;N;;;;; +0F62;TIBETAN LETTER RA;Lo;0;L;;;;;N;;*;;; +0F63;TIBETAN LETTER LA;Lo;0;L;;;;;N;;;;; +0F64;TIBETAN LETTER SHA;Lo;0;L;;;;;N;;;;; +0F65;TIBETAN LETTER SSA;Lo;0;L;;;;;N;TIBETAN LETTER REVERSED SHA;;;; +0F66;TIBETAN LETTER SA;Lo;0;L;;;;;N;;;;; +0F67;TIBETAN LETTER HA;Lo;0;L;;;;;N;;;;; +0F68;TIBETAN LETTER A;Lo;0;L;;;;;N;;;;; +0F69;TIBETAN LETTER KSSA;Lo;0;L;0F40 0FB5;;;;N;;;;; +0F6A;TIBETAN LETTER FIXED-FORM RA;Lo;0;L;;;;;N;;*;;; +0F71;TIBETAN VOWEL SIGN AA;Mn;129;NSM;;;;;N;;;;; +0F72;TIBETAN VOWEL SIGN I;Mn;130;NSM;;;;;N;;;;; +0F73;TIBETAN VOWEL SIGN II;Mn;0;NSM;0F71 0F72;;;;N;;;;; +0F74;TIBETAN VOWEL SIGN U;Mn;132;NSM;;;;;N;;;;; +0F75;TIBETAN VOWEL SIGN UU;Mn;0;NSM;0F71 0F74;;;;N;;;;; +0F76;TIBETAN VOWEL SIGN VOCALIC R;Mn;0;NSM;0FB2 0F80;;;;N;;;;; +0F77;TIBETAN VOWEL SIGN VOCALIC RR;Mn;0;NSM;<compat> 0FB2 0F81;;;;N;;;;; +0F78;TIBETAN VOWEL SIGN VOCALIC L;Mn;0;NSM;0FB3 0F80;;;;N;;;;; +0F79;TIBETAN VOWEL SIGN VOCALIC LL;Mn;0;NSM;<compat> 0FB3 0F81;;;;N;;;;; +0F7A;TIBETAN VOWEL SIGN E;Mn;130;NSM;;;;;N;;;;; +0F7B;TIBETAN VOWEL SIGN EE;Mn;130;NSM;;;;;N;TIBETAN VOWEL SIGN AI;;;; +0F7C;TIBETAN VOWEL SIGN O;Mn;130;NSM;;;;;N;;;;; +0F7D;TIBETAN VOWEL SIGN OO;Mn;130;NSM;;;;;N;TIBETAN VOWEL SIGN AU;;;; +0F7E;TIBETAN SIGN RJES SU NGA RO;Mn;0;NSM;;;;;N;TIBETAN ANUSVARA;je su nga ro;;; +0F7F;TIBETAN SIGN RNAM BCAD;Mc;0;L;;;;;N;TIBETAN VISARGA;nam chey;;; +0F80;TIBETAN VOWEL SIGN REVERSED I;Mn;130;NSM;;;;;N;TIBETAN VOWEL SIGN SHORT I;;;; +0F81;TIBETAN VOWEL SIGN REVERSED II;Mn;0;NSM;0F71 0F80;;;;N;;;;; +0F82;TIBETAN SIGN NYI ZLA NAA DA;Mn;230;NSM;;;;;N;TIBETAN CANDRABINDU WITH ORNAMENT;nyi da na da;;; +0F83;TIBETAN SIGN SNA LDAN;Mn;230;NSM;;;;;N;TIBETAN CANDRABINDU;nan de;;; +0F84;TIBETAN MARK HALANTA;Mn;9;NSM;;;;;N;TIBETAN VIRAMA;;;; +0F85;TIBETAN MARK PALUTA;Po;0;L;;;;;N;TIBETAN CHUCHENYIGE;;;; +0F86;TIBETAN SIGN LCI RTAGS;Mn;230;NSM;;;;;N;;ji ta;;; +0F87;TIBETAN SIGN YANG RTAGS;Mn;230;NSM;;;;;N;;yang ta;;; +0F88;TIBETAN SIGN LCE TSA CAN;Lo;0;L;;;;;N;;che tsa chen;;; +0F89;TIBETAN SIGN MCHU CAN;Lo;0;L;;;;;N;;chu chen;;; +0F8A;TIBETAN SIGN GRU CAN RGYINGS;Lo;0;L;;;;;N;;tru chen ging;;; +0F8B;TIBETAN SIGN GRU MED RGYINGS;Lo;0;L;;;;;N;;tru me ging;;; +0F90;TIBETAN SUBJOINED LETTER KA;Mn;0;NSM;;;;;N;;;;; +0F91;TIBETAN SUBJOINED LETTER KHA;Mn;0;NSM;;;;;N;;;;; +0F92;TIBETAN SUBJOINED LETTER GA;Mn;0;NSM;;;;;N;;;;; +0F93;TIBETAN SUBJOINED LETTER GHA;Mn;0;NSM;0F92 0FB7;;;;N;;;;; +0F94;TIBETAN SUBJOINED LETTER NGA;Mn;0;NSM;;;;;N;;;;; +0F95;TIBETAN SUBJOINED LETTER CA;Mn;0;NSM;;;;;N;;;;; +0F96;TIBETAN SUBJOINED LETTER CHA;Mn;0;NSM;;;;;N;;;;; +0F97;TIBETAN SUBJOINED LETTER JA;Mn;0;NSM;;;;;N;;;;; +0F99;TIBETAN SUBJOINED LETTER NYA;Mn;0;NSM;;;;;N;;;;; +0F9A;TIBETAN SUBJOINED LETTER TTA;Mn;0;NSM;;;;;N;;;;; +0F9B;TIBETAN SUBJOINED LETTER TTHA;Mn;0;NSM;;;;;N;;;;; +0F9C;TIBETAN SUBJOINED LETTER DDA;Mn;0;NSM;;;;;N;;;;; +0F9D;TIBETAN SUBJOINED LETTER DDHA;Mn;0;NSM;0F9C 0FB7;;;;N;;;;; +0F9E;TIBETAN SUBJOINED LETTER NNA;Mn;0;NSM;;;;;N;;;;; +0F9F;TIBETAN SUBJOINED LETTER TA;Mn;0;NSM;;;;;N;;;;; +0FA0;TIBETAN SUBJOINED LETTER THA;Mn;0;NSM;;;;;N;;;;; +0FA1;TIBETAN SUBJOINED LETTER DA;Mn;0;NSM;;;;;N;;;;; +0FA2;TIBETAN SUBJOINED LETTER DHA;Mn;0;NSM;0FA1 0FB7;;;;N;;;;; +0FA3;TIBETAN SUBJOINED LETTER NA;Mn;0;NSM;;;;;N;;;;; +0FA4;TIBETAN SUBJOINED LETTER PA;Mn;0;NSM;;;;;N;;;;; +0FA5;TIBETAN SUBJOINED LETTER PHA;Mn;0;NSM;;;;;N;;;;; +0FA6;TIBETAN SUBJOINED LETTER BA;Mn;0;NSM;;;;;N;;;;; +0FA7;TIBETAN SUBJOINED LETTER BHA;Mn;0;NSM;0FA6 0FB7;;;;N;;;;; +0FA8;TIBETAN SUBJOINED LETTER MA;Mn;0;NSM;;;;;N;;;;; +0FA9;TIBETAN SUBJOINED LETTER TSA;Mn;0;NSM;;;;;N;;;;; +0FAA;TIBETAN SUBJOINED LETTER TSHA;Mn;0;NSM;;;;;N;;;;; +0FAB;TIBETAN SUBJOINED LETTER DZA;Mn;0;NSM;;;;;N;;;;; +0FAC;TIBETAN SUBJOINED LETTER DZHA;Mn;0;NSM;0FAB 0FB7;;;;N;;;;; +0FAD;TIBETAN SUBJOINED LETTER WA;Mn;0;NSM;;;;;N;;*;;; +0FAE;TIBETAN SUBJOINED LETTER ZHA;Mn;0;NSM;;;;;N;;;;; +0FAF;TIBETAN SUBJOINED LETTER ZA;Mn;0;NSM;;;;;N;;;;; +0FB0;TIBETAN SUBJOINED LETTER -A;Mn;0;NSM;;;;;N;;;;; +0FB1;TIBETAN SUBJOINED LETTER YA;Mn;0;NSM;;;;;N;;*;;; +0FB2;TIBETAN SUBJOINED LETTER RA;Mn;0;NSM;;;;;N;;*;;; +0FB3;TIBETAN SUBJOINED LETTER LA;Mn;0;NSM;;;;;N;;;;; +0FB4;TIBETAN SUBJOINED LETTER SHA;Mn;0;NSM;;;;;N;;;;; +0FB5;TIBETAN SUBJOINED LETTER SSA;Mn;0;NSM;;;;;N;;;;; +0FB6;TIBETAN SUBJOINED LETTER SA;Mn;0;NSM;;;;;N;;;;; +0FB7;TIBETAN SUBJOINED LETTER HA;Mn;0;NSM;;;;;N;;;;; +0FB8;TIBETAN SUBJOINED LETTER A;Mn;0;NSM;;;;;N;;;;; +0FB9;TIBETAN SUBJOINED LETTER KSSA;Mn;0;NSM;0F90 0FB5;;;;N;;;;; +0FBA;TIBETAN SUBJOINED LETTER FIXED-FORM WA;Mn;0;NSM;;;;;N;;*;;; +0FBB;TIBETAN SUBJOINED LETTER FIXED-FORM YA;Mn;0;NSM;;;;;N;;*;;; +0FBC;TIBETAN SUBJOINED LETTER FIXED-FORM RA;Mn;0;NSM;;;;;N;;*;;; +0FBE;TIBETAN KU RU KHA;So;0;L;;;;;N;;kuruka;;; +0FBF;TIBETAN KU RU KHA BZHI MIG CAN;So;0;L;;;;;N;;kuruka shi mik chen;;; +0FC0;TIBETAN CANTILLATION SIGN HEAVY BEAT;So;0;L;;;;;N;;;;; +0FC1;TIBETAN CANTILLATION SIGN LIGHT BEAT;So;0;L;;;;;N;;;;; +0FC2;TIBETAN CANTILLATION SIGN CANG TE-U;So;0;L;;;;;N;;chang tyu;;; +0FC3;TIBETAN CANTILLATION SIGN SBUB -CHAL;So;0;L;;;;;N;;bub chey;;; +0FC4;TIBETAN SYMBOL DRIL BU;So;0;L;;;;;N;;drilbu;;; +0FC5;TIBETAN SYMBOL RDO RJE;So;0;L;;;;;N;;dorje;;; +0FC6;TIBETAN SYMBOL PADMA GDAN;Mn;220;NSM;;;;;N;;pema den;;; +0FC7;TIBETAN SYMBOL RDO RJE RGYA GRAM;So;0;L;;;;;N;;dorje gya dram;;; +0FC8;TIBETAN SYMBOL PHUR PA;So;0;L;;;;;N;;phurba;;; +0FC9;TIBETAN SYMBOL NOR BU;So;0;L;;;;;N;;norbu;;; +0FCA;TIBETAN SYMBOL NOR BU NYIS -KHYIL;So;0;L;;;;;N;;norbu nyi khyi;;; +0FCB;TIBETAN SYMBOL NOR BU GSUM -KHYIL;So;0;L;;;;;N;;norbu sum khyi;;; +0FCC;TIBETAN SYMBOL NOR BU BZHI -KHYIL;So;0;L;;;;;N;;norbu shi khyi;;; +0FCF;TIBETAN SIGN RDEL NAG GSUM;So;0;L;;;;;N;;dena sum;;; +1000;MYANMAR LETTER KA;Lo;0;L;;;;;N;;;;; +1001;MYANMAR LETTER KHA;Lo;0;L;;;;;N;;;;; +1002;MYANMAR LETTER GA;Lo;0;L;;;;;N;;;;; +1003;MYANMAR LETTER GHA;Lo;0;L;;;;;N;;;;; +1004;MYANMAR LETTER NGA;Lo;0;L;;;;;N;;;;; +1005;MYANMAR LETTER CA;Lo;0;L;;;;;N;;;;; +1006;MYANMAR LETTER CHA;Lo;0;L;;;;;N;;;;; +1007;MYANMAR LETTER JA;Lo;0;L;;;;;N;;;;; +1008;MYANMAR LETTER JHA;Lo;0;L;;;;;N;;;;; +1009;MYANMAR LETTER NYA;Lo;0;L;;;;;N;;;;; +100A;MYANMAR LETTER NNYA;Lo;0;L;;;;;N;;;;; +100B;MYANMAR LETTER TTA;Lo;0;L;;;;;N;;;;; +100C;MYANMAR LETTER TTHA;Lo;0;L;;;;;N;;;;; +100D;MYANMAR LETTER DDA;Lo;0;L;;;;;N;;;;; +100E;MYANMAR LETTER DDHA;Lo;0;L;;;;;N;;;;; +100F;MYANMAR LETTER NNA;Lo;0;L;;;;;N;;;;; +1010;MYANMAR LETTER TA;Lo;0;L;;;;;N;;;;; +1011;MYANMAR LETTER THA;Lo;0;L;;;;;N;;;;; +1012;MYANMAR LETTER DA;Lo;0;L;;;;;N;;;;; +1013;MYANMAR LETTER DHA;Lo;0;L;;;;;N;;;;; +1014;MYANMAR LETTER NA;Lo;0;L;;;;;N;;;;; +1015;MYANMAR LETTER PA;Lo;0;L;;;;;N;;;;; +1016;MYANMAR LETTER PHA;Lo;0;L;;;;;N;;;;; +1017;MYANMAR LETTER BA;Lo;0;L;;;;;N;;;;; +1018;MYANMAR LETTER BHA;Lo;0;L;;;;;N;;;;; +1019;MYANMAR LETTER MA;Lo;0;L;;;;;N;;;;; +101A;MYANMAR LETTER YA;Lo;0;L;;;;;N;;;;; +101B;MYANMAR LETTER RA;Lo;0;L;;;;;N;;;;; +101C;MYANMAR LETTER LA;Lo;0;L;;;;;N;;;;; +101D;MYANMAR LETTER WA;Lo;0;L;;;;;N;;;;; +101E;MYANMAR LETTER SA;Lo;0;L;;;;;N;;;;; +101F;MYANMAR LETTER HA;Lo;0;L;;;;;N;;;;; +1020;MYANMAR LETTER LLA;Lo;0;L;;;;;N;;;;; +1021;MYANMAR LETTER A;Lo;0;L;;;;;N;;;;; +1023;MYANMAR LETTER I;Lo;0;L;;;;;N;;;;; +1024;MYANMAR LETTER II;Lo;0;L;;;;;N;;;;; +1025;MYANMAR LETTER U;Lo;0;L;;;;;N;;;;; +1026;MYANMAR LETTER UU;Lo;0;L;1025 102E;;;;N;;;;; +1027;MYANMAR LETTER E;Lo;0;L;;;;;N;;;;; +1029;MYANMAR LETTER O;Lo;0;L;;;;;N;;;;; +102A;MYANMAR LETTER AU;Lo;0;L;;;;;N;;;;; +102C;MYANMAR VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; +102D;MYANMAR VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;; +102E;MYANMAR VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;; +102F;MYANMAR VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; +1030;MYANMAR VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; +1031;MYANMAR VOWEL SIGN E;Mc;0;L;;;;;N;;;;; +1032;MYANMAR VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;; +1036;MYANMAR SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;; +1037;MYANMAR SIGN DOT BELOW;Mn;7;NSM;;;;;N;;;;; +1038;MYANMAR SIGN VISARGA;Mc;0;L;;;;;N;;;;; +1039;MYANMAR SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; +1040;MYANMAR DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +1041;MYANMAR DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +1042;MYANMAR DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +1043;MYANMAR DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +1044;MYANMAR DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +1045;MYANMAR DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +1046;MYANMAR DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +1047;MYANMAR DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +1048;MYANMAR DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +1049;MYANMAR DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +104A;MYANMAR SIGN LITTLE SECTION;Po;0;L;;;;;N;;;;; +104B;MYANMAR SIGN SECTION;Po;0;L;;;;;N;;;;; +104C;MYANMAR SYMBOL LOCATIVE;Po;0;L;;;;;N;;;;; +104D;MYANMAR SYMBOL COMPLETED;Po;0;L;;;;;N;;;;; +104E;MYANMAR SYMBOL AFOREMENTIONED;Po;0;L;;;;;N;;;;; +104F;MYANMAR SYMBOL GENITIVE;Po;0;L;;;;;N;;;;; +1050;MYANMAR LETTER SHA;Lo;0;L;;;;;N;;;;; +1051;MYANMAR LETTER SSA;Lo;0;L;;;;;N;;;;; +1052;MYANMAR LETTER VOCALIC R;Lo;0;L;;;;;N;;;;; +1053;MYANMAR LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;; +1054;MYANMAR LETTER VOCALIC L;Lo;0;L;;;;;N;;;;; +1055;MYANMAR LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;; +1056;MYANMAR VOWEL SIGN VOCALIC R;Mc;0;L;;;;;N;;;;; +1057;MYANMAR VOWEL SIGN VOCALIC RR;Mc;0;L;;;;;N;;;;; +1058;MYANMAR VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;; +1059;MYANMAR VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;; +10A0;GEORGIAN CAPITAL LETTER AN;Lu;0;L;;;;;N;;Khutsuri;;; +10A1;GEORGIAN CAPITAL LETTER BAN;Lu;0;L;;;;;N;;Khutsuri;;; +10A2;GEORGIAN CAPITAL LETTER GAN;Lu;0;L;;;;;N;;Khutsuri;;; +10A3;GEORGIAN CAPITAL LETTER DON;Lu;0;L;;;;;N;;Khutsuri;;; +10A4;GEORGIAN CAPITAL LETTER EN;Lu;0;L;;;;;N;;Khutsuri;;; +10A5;GEORGIAN CAPITAL LETTER VIN;Lu;0;L;;;;;N;;Khutsuri;;; +10A6;GEORGIAN CAPITAL LETTER ZEN;Lu;0;L;;;;;N;;Khutsuri;;; +10A7;GEORGIAN CAPITAL LETTER TAN;Lu;0;L;;;;;N;;Khutsuri;;; +10A8;GEORGIAN CAPITAL LETTER IN;Lu;0;L;;;;;N;;Khutsuri;;; +10A9;GEORGIAN CAPITAL LETTER KAN;Lu;0;L;;;;;N;;Khutsuri;;; +10AA;GEORGIAN CAPITAL LETTER LAS;Lu;0;L;;;;;N;;Khutsuri;;; +10AB;GEORGIAN CAPITAL LETTER MAN;Lu;0;L;;;;;N;;Khutsuri;;; +10AC;GEORGIAN CAPITAL LETTER NAR;Lu;0;L;;;;;N;;Khutsuri;;; +10AD;GEORGIAN CAPITAL LETTER ON;Lu;0;L;;;;;N;;Khutsuri;;; +10AE;GEORGIAN CAPITAL LETTER PAR;Lu;0;L;;;;;N;;Khutsuri;;; +10AF;GEORGIAN CAPITAL LETTER ZHAR;Lu;0;L;;;;;N;;Khutsuri;;; +10B0;GEORGIAN CAPITAL LETTER RAE;Lu;0;L;;;;;N;;Khutsuri;;; +10B1;GEORGIAN CAPITAL LETTER SAN;Lu;0;L;;;;;N;;Khutsuri;;; +10B2;GEORGIAN CAPITAL LETTER TAR;Lu;0;L;;;;;N;;Khutsuri;;; +10B3;GEORGIAN CAPITAL LETTER UN;Lu;0;L;;;;;N;;Khutsuri;;; +10B4;GEORGIAN CAPITAL LETTER PHAR;Lu;0;L;;;;;N;;Khutsuri;;; +10B5;GEORGIAN CAPITAL LETTER KHAR;Lu;0;L;;;;;N;;Khutsuri;;; +10B6;GEORGIAN CAPITAL LETTER GHAN;Lu;0;L;;;;;N;;Khutsuri;;; +10B7;GEORGIAN CAPITAL LETTER QAR;Lu;0;L;;;;;N;;Khutsuri;;; +10B8;GEORGIAN CAPITAL LETTER SHIN;Lu;0;L;;;;;N;;Khutsuri;;; +10B9;GEORGIAN CAPITAL LETTER CHIN;Lu;0;L;;;;;N;;Khutsuri;;; +10BA;GEORGIAN CAPITAL LETTER CAN;Lu;0;L;;;;;N;;Khutsuri;;; +10BB;GEORGIAN CAPITAL LETTER JIL;Lu;0;L;;;;;N;;Khutsuri;;; +10BC;GEORGIAN CAPITAL LETTER CIL;Lu;0;L;;;;;N;;Khutsuri;;; +10BD;GEORGIAN CAPITAL LETTER CHAR;Lu;0;L;;;;;N;;Khutsuri;;; +10BE;GEORGIAN CAPITAL LETTER XAN;Lu;0;L;;;;;N;;Khutsuri;;; +10BF;GEORGIAN CAPITAL LETTER JHAN;Lu;0;L;;;;;N;;Khutsuri;;; +10C0;GEORGIAN CAPITAL LETTER HAE;Lu;0;L;;;;;N;;Khutsuri;;; +10C1;GEORGIAN CAPITAL LETTER HE;Lu;0;L;;;;;N;;Khutsuri;;; +10C2;GEORGIAN CAPITAL LETTER HIE;Lu;0;L;;;;;N;;Khutsuri;;; +10C3;GEORGIAN CAPITAL LETTER WE;Lu;0;L;;;;;N;;Khutsuri;;; +10C4;GEORGIAN CAPITAL LETTER HAR;Lu;0;L;;;;;N;;Khutsuri;;; +10C5;GEORGIAN CAPITAL LETTER HOE;Lu;0;L;;;;;N;;Khutsuri;;; +10D0;GEORGIAN LETTER AN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER AN;;;; +10D1;GEORGIAN LETTER BAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER BAN;;;; +10D2;GEORGIAN LETTER GAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER GAN;;;; +10D3;GEORGIAN LETTER DON;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER DON;;;; +10D4;GEORGIAN LETTER EN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER EN;;;; +10D5;GEORGIAN LETTER VIN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER VIN;;;; +10D6;GEORGIAN LETTER ZEN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER ZEN;;;; +10D7;GEORGIAN LETTER TAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER TAN;;;; +10D8;GEORGIAN LETTER IN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER IN;;;; +10D9;GEORGIAN LETTER KAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER KAN;;;; +10DA;GEORGIAN LETTER LAS;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER LAS;;;; +10DB;GEORGIAN LETTER MAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER MAN;;;; +10DC;GEORGIAN LETTER NAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER NAR;;;; +10DD;GEORGIAN LETTER ON;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER ON;;;; +10DE;GEORGIAN LETTER PAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER PAR;;;; +10DF;GEORGIAN LETTER ZHAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER ZHAR;;;; +10E0;GEORGIAN LETTER RAE;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER RAE;;;; +10E1;GEORGIAN LETTER SAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER SAN;;;; +10E2;GEORGIAN LETTER TAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER TAR;;;; +10E3;GEORGIAN LETTER UN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER UN;;;; +10E4;GEORGIAN LETTER PHAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER PHAR;;;; +10E5;GEORGIAN LETTER KHAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER KHAR;;;; +10E6;GEORGIAN LETTER GHAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER GHAN;;;; +10E7;GEORGIAN LETTER QAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER QAR;;;; +10E8;GEORGIAN LETTER SHIN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER SHIN;;;; +10E9;GEORGIAN LETTER CHIN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER CHIN;;;; +10EA;GEORGIAN LETTER CAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER CAN;;;; +10EB;GEORGIAN LETTER JIL;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER JIL;;;; +10EC;GEORGIAN LETTER CIL;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER CIL;;;; +10ED;GEORGIAN LETTER CHAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER CHAR;;;; +10EE;GEORGIAN LETTER XAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER XAN;;;; +10EF;GEORGIAN LETTER JHAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER JHAN;;;; +10F0;GEORGIAN LETTER HAE;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER HAE;;;; +10F1;GEORGIAN LETTER HE;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER HE;;;; +10F2;GEORGIAN LETTER HIE;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER HIE;;;; +10F3;GEORGIAN LETTER WE;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER WE;;;; +10F4;GEORGIAN LETTER HAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER HAR;;;; +10F5;GEORGIAN LETTER HOE;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER HOE;;;; +10F6;GEORGIAN LETTER FI;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER FI;;;; +10F7;GEORGIAN LETTER YN;Lo;0;L;;;;;N;;;;; +10F8;GEORGIAN LETTER ELIFI;Lo;0;L;;;;;N;;;;; +10FB;GEORGIAN PARAGRAPH SEPARATOR;Po;0;L;;;;;N;;;;; +1100;HANGUL CHOSEONG KIYEOK;Lo;0;L;;;;;N;;g *;;; +1101;HANGUL CHOSEONG SSANGKIYEOK;Lo;0;L;;;;;N;;gg *;;; +1102;HANGUL CHOSEONG NIEUN;Lo;0;L;;;;;N;;n *;;; +1103;HANGUL CHOSEONG TIKEUT;Lo;0;L;;;;;N;;d *;;; +1104;HANGUL CHOSEONG SSANGTIKEUT;Lo;0;L;;;;;N;;dd *;;; +1105;HANGUL CHOSEONG RIEUL;Lo;0;L;;;;;N;;r *;;; +1106;HANGUL CHOSEONG MIEUM;Lo;0;L;;;;;N;;m *;;; +1107;HANGUL CHOSEONG PIEUP;Lo;0;L;;;;;N;;b *;;; +1108;HANGUL CHOSEONG SSANGPIEUP;Lo;0;L;;;;;N;;bb *;;; +1109;HANGUL CHOSEONG SIOS;Lo;0;L;;;;;N;;s *;;; +110A;HANGUL CHOSEONG SSANGSIOS;Lo;0;L;;;;;N;;ss *;;; +110B;HANGUL CHOSEONG IEUNG;Lo;0;L;;;;;N;;;;; +110C;HANGUL CHOSEONG CIEUC;Lo;0;L;;;;;N;;j *;;; +110D;HANGUL CHOSEONG SSANGCIEUC;Lo;0;L;;;;;N;;jj *;;; +110E;HANGUL CHOSEONG CHIEUCH;Lo;0;L;;;;;N;;c *;;; +110F;HANGUL CHOSEONG KHIEUKH;Lo;0;L;;;;;N;;k *;;; +1110;HANGUL CHOSEONG THIEUTH;Lo;0;L;;;;;N;;t *;;; +1111;HANGUL CHOSEONG PHIEUPH;Lo;0;L;;;;;N;;p *;;; +1112;HANGUL CHOSEONG HIEUH;Lo;0;L;;;;;N;;h *;;; +1113;HANGUL CHOSEONG NIEUN-KIYEOK;Lo;0;L;;;;;N;;;;; +1114;HANGUL CHOSEONG SSANGNIEUN;Lo;0;L;;;;;N;;;;; +1115;HANGUL CHOSEONG NIEUN-TIKEUT;Lo;0;L;;;;;N;;;;; +1116;HANGUL CHOSEONG NIEUN-PIEUP;Lo;0;L;;;;;N;;;;; +1117;HANGUL CHOSEONG TIKEUT-KIYEOK;Lo;0;L;;;;;N;;;;; +1118;HANGUL CHOSEONG RIEUL-NIEUN;Lo;0;L;;;;;N;;;;; +1119;HANGUL CHOSEONG SSANGRIEUL;Lo;0;L;;;;;N;;;;; +111A;HANGUL CHOSEONG RIEUL-HIEUH;Lo;0;L;;;;;N;;;;; +111B;HANGUL CHOSEONG KAPYEOUNRIEUL;Lo;0;L;;;;;N;;;;; +111C;HANGUL CHOSEONG MIEUM-PIEUP;Lo;0;L;;;;;N;;;;; +111D;HANGUL CHOSEONG KAPYEOUNMIEUM;Lo;0;L;;;;;N;;;;; +111E;HANGUL CHOSEONG PIEUP-KIYEOK;Lo;0;L;;;;;N;;;;; +111F;HANGUL CHOSEONG PIEUP-NIEUN;Lo;0;L;;;;;N;;;;; +1120;HANGUL CHOSEONG PIEUP-TIKEUT;Lo;0;L;;;;;N;;;;; +1121;HANGUL CHOSEONG PIEUP-SIOS;Lo;0;L;;;;;N;;;;; +1122;HANGUL CHOSEONG PIEUP-SIOS-KIYEOK;Lo;0;L;;;;;N;;;;; +1123;HANGUL CHOSEONG PIEUP-SIOS-TIKEUT;Lo;0;L;;;;;N;;;;; +1124;HANGUL CHOSEONG PIEUP-SIOS-PIEUP;Lo;0;L;;;;;N;;;;; +1125;HANGUL CHOSEONG PIEUP-SSANGSIOS;Lo;0;L;;;;;N;;;;; +1126;HANGUL CHOSEONG PIEUP-SIOS-CIEUC;Lo;0;L;;;;;N;;;;; +1127;HANGUL CHOSEONG PIEUP-CIEUC;Lo;0;L;;;;;N;;;;; +1128;HANGUL CHOSEONG PIEUP-CHIEUCH;Lo;0;L;;;;;N;;;;; +1129;HANGUL CHOSEONG PIEUP-THIEUTH;Lo;0;L;;;;;N;;;;; +112A;HANGUL CHOSEONG PIEUP-PHIEUPH;Lo;0;L;;;;;N;;;;; +112B;HANGUL CHOSEONG KAPYEOUNPIEUP;Lo;0;L;;;;;N;;;;; +112C;HANGUL CHOSEONG KAPYEOUNSSANGPIEUP;Lo;0;L;;;;;N;;;;; +112D;HANGUL CHOSEONG SIOS-KIYEOK;Lo;0;L;;;;;N;;;;; +112E;HANGUL CHOSEONG SIOS-NIEUN;Lo;0;L;;;;;N;;;;; +112F;HANGUL CHOSEONG SIOS-TIKEUT;Lo;0;L;;;;;N;;;;; +1130;HANGUL CHOSEONG SIOS-RIEUL;Lo;0;L;;;;;N;;;;; +1131;HANGUL CHOSEONG SIOS-MIEUM;Lo;0;L;;;;;N;;;;; +1132;HANGUL CHOSEONG SIOS-PIEUP;Lo;0;L;;;;;N;;;;; +1133;HANGUL CHOSEONG SIOS-PIEUP-KIYEOK;Lo;0;L;;;;;N;;;;; +1134;HANGUL CHOSEONG SIOS-SSANGSIOS;Lo;0;L;;;;;N;;;;; +1135;HANGUL CHOSEONG SIOS-IEUNG;Lo;0;L;;;;;N;;;;; +1136;HANGUL CHOSEONG SIOS-CIEUC;Lo;0;L;;;;;N;;;;; +1137;HANGUL CHOSEONG SIOS-CHIEUCH;Lo;0;L;;;;;N;;;;; +1138;HANGUL CHOSEONG SIOS-KHIEUKH;Lo;0;L;;;;;N;;;;; +1139;HANGUL CHOSEONG SIOS-THIEUTH;Lo;0;L;;;;;N;;;;; +113A;HANGUL CHOSEONG SIOS-PHIEUPH;Lo;0;L;;;;;N;;;;; +113B;HANGUL CHOSEONG SIOS-HIEUH;Lo;0;L;;;;;N;;;;; +113C;HANGUL CHOSEONG CHITUEUMSIOS;Lo;0;L;;;;;N;;;;; +113D;HANGUL CHOSEONG CHITUEUMSSANGSIOS;Lo;0;L;;;;;N;;;;; +113E;HANGUL CHOSEONG CEONGCHIEUMSIOS;Lo;0;L;;;;;N;;;;; +113F;HANGUL CHOSEONG CEONGCHIEUMSSANGSIOS;Lo;0;L;;;;;N;;;;; +1140;HANGUL CHOSEONG PANSIOS;Lo;0;L;;;;;N;;;;; +1141;HANGUL CHOSEONG IEUNG-KIYEOK;Lo;0;L;;;;;N;;;;; +1142;HANGUL CHOSEONG IEUNG-TIKEUT;Lo;0;L;;;;;N;;;;; +1143;HANGUL CHOSEONG IEUNG-MIEUM;Lo;0;L;;;;;N;;;;; +1144;HANGUL CHOSEONG IEUNG-PIEUP;Lo;0;L;;;;;N;;;;; +1145;HANGUL CHOSEONG IEUNG-SIOS;Lo;0;L;;;;;N;;;;; +1146;HANGUL CHOSEONG IEUNG-PANSIOS;Lo;0;L;;;;;N;;;;; +1147;HANGUL CHOSEONG SSANGIEUNG;Lo;0;L;;;;;N;;;;; +1148;HANGUL CHOSEONG IEUNG-CIEUC;Lo;0;L;;;;;N;;;;; +1149;HANGUL CHOSEONG IEUNG-CHIEUCH;Lo;0;L;;;;;N;;;;; +114A;HANGUL CHOSEONG IEUNG-THIEUTH;Lo;0;L;;;;;N;;;;; +114B;HANGUL CHOSEONG IEUNG-PHIEUPH;Lo;0;L;;;;;N;;;;; +114C;HANGUL CHOSEONG YESIEUNG;Lo;0;L;;;;;N;;;;; +114D;HANGUL CHOSEONG CIEUC-IEUNG;Lo;0;L;;;;;N;;;;; +114E;HANGUL CHOSEONG CHITUEUMCIEUC;Lo;0;L;;;;;N;;;;; +114F;HANGUL CHOSEONG CHITUEUMSSANGCIEUC;Lo;0;L;;;;;N;;;;; +1150;HANGUL CHOSEONG CEONGCHIEUMCIEUC;Lo;0;L;;;;;N;;;;; +1151;HANGUL CHOSEONG CEONGCHIEUMSSANGCIEUC;Lo;0;L;;;;;N;;;;; +1152;HANGUL CHOSEONG CHIEUCH-KHIEUKH;Lo;0;L;;;;;N;;;;; +1153;HANGUL CHOSEONG CHIEUCH-HIEUH;Lo;0;L;;;;;N;;;;; +1154;HANGUL CHOSEONG CHITUEUMCHIEUCH;Lo;0;L;;;;;N;;;;; +1155;HANGUL CHOSEONG CEONGCHIEUMCHIEUCH;Lo;0;L;;;;;N;;;;; +1156;HANGUL CHOSEONG PHIEUPH-PIEUP;Lo;0;L;;;;;N;;;;; +1157;HANGUL CHOSEONG KAPYEOUNPHIEUPH;Lo;0;L;;;;;N;;;;; +1158;HANGUL CHOSEONG SSANGHIEUH;Lo;0;L;;;;;N;;;;; +1159;HANGUL CHOSEONG YEORINHIEUH;Lo;0;L;;;;;N;;;;; +115F;HANGUL CHOSEONG FILLER;Lo;0;L;;;;;N;;;;; +1160;HANGUL JUNGSEONG FILLER;Lo;0;L;;;;;N;;;;; +1161;HANGUL JUNGSEONG A;Lo;0;L;;;;;N;;;;; +1162;HANGUL JUNGSEONG AE;Lo;0;L;;;;;N;;;;; +1163;HANGUL JUNGSEONG YA;Lo;0;L;;;;;N;;;;; +1164;HANGUL JUNGSEONG YAE;Lo;0;L;;;;;N;;;;; +1165;HANGUL JUNGSEONG EO;Lo;0;L;;;;;N;;;;; +1166;HANGUL JUNGSEONG E;Lo;0;L;;;;;N;;;;; +1167;HANGUL JUNGSEONG YEO;Lo;0;L;;;;;N;;;;; +1168;HANGUL JUNGSEONG YE;Lo;0;L;;;;;N;;;;; +1169;HANGUL JUNGSEONG O;Lo;0;L;;;;;N;;;;; +116A;HANGUL JUNGSEONG WA;Lo;0;L;;;;;N;;;;; +116B;HANGUL JUNGSEONG WAE;Lo;0;L;;;;;N;;;;; +116C;HANGUL JUNGSEONG OE;Lo;0;L;;;;;N;;;;; +116D;HANGUL JUNGSEONG YO;Lo;0;L;;;;;N;;;;; +116E;HANGUL JUNGSEONG U;Lo;0;L;;;;;N;;;;; +116F;HANGUL JUNGSEONG WEO;Lo;0;L;;;;;N;;;;; +1170;HANGUL JUNGSEONG WE;Lo;0;L;;;;;N;;;;; +1171;HANGUL JUNGSEONG WI;Lo;0;L;;;;;N;;;;; +1172;HANGUL JUNGSEONG YU;Lo;0;L;;;;;N;;;;; +1173;HANGUL JUNGSEONG EU;Lo;0;L;;;;;N;;;;; +1174;HANGUL JUNGSEONG YI;Lo;0;L;;;;;N;;;;; +1175;HANGUL JUNGSEONG I;Lo;0;L;;;;;N;;;;; +1176;HANGUL JUNGSEONG A-O;Lo;0;L;;;;;N;;;;; +1177;HANGUL JUNGSEONG A-U;Lo;0;L;;;;;N;;;;; +1178;HANGUL JUNGSEONG YA-O;Lo;0;L;;;;;N;;;;; +1179;HANGUL JUNGSEONG YA-YO;Lo;0;L;;;;;N;;;;; +117A;HANGUL JUNGSEONG EO-O;Lo;0;L;;;;;N;;;;; +117B;HANGUL JUNGSEONG EO-U;Lo;0;L;;;;;N;;;;; +117C;HANGUL JUNGSEONG EO-EU;Lo;0;L;;;;;N;;;;; +117D;HANGUL JUNGSEONG YEO-O;Lo;0;L;;;;;N;;;;; +117E;HANGUL JUNGSEONG YEO-U;Lo;0;L;;;;;N;;;;; +117F;HANGUL JUNGSEONG O-EO;Lo;0;L;;;;;N;;;;; +1180;HANGUL JUNGSEONG O-E;Lo;0;L;;;;;N;;;;; +1181;HANGUL JUNGSEONG O-YE;Lo;0;L;;;;;N;;;;; +1182;HANGUL JUNGSEONG O-O;Lo;0;L;;;;;N;;;;; +1183;HANGUL JUNGSEONG O-U;Lo;0;L;;;;;N;;;;; +1184;HANGUL JUNGSEONG YO-YA;Lo;0;L;;;;;N;;;;; +1185;HANGUL JUNGSEONG YO-YAE;Lo;0;L;;;;;N;;;;; +1186;HANGUL JUNGSEONG YO-YEO;Lo;0;L;;;;;N;;;;; +1187;HANGUL JUNGSEONG YO-O;Lo;0;L;;;;;N;;;;; +1188;HANGUL JUNGSEONG YO-I;Lo;0;L;;;;;N;;;;; +1189;HANGUL JUNGSEONG U-A;Lo;0;L;;;;;N;;;;; +118A;HANGUL JUNGSEONG U-AE;Lo;0;L;;;;;N;;;;; +118B;HANGUL JUNGSEONG U-EO-EU;Lo;0;L;;;;;N;;;;; +118C;HANGUL JUNGSEONG U-YE;Lo;0;L;;;;;N;;;;; +118D;HANGUL JUNGSEONG U-U;Lo;0;L;;;;;N;;;;; +118E;HANGUL JUNGSEONG YU-A;Lo;0;L;;;;;N;;;;; +118F;HANGUL JUNGSEONG YU-EO;Lo;0;L;;;;;N;;;;; +1190;HANGUL JUNGSEONG YU-E;Lo;0;L;;;;;N;;;;; +1191;HANGUL JUNGSEONG YU-YEO;Lo;0;L;;;;;N;;;;; +1192;HANGUL JUNGSEONG YU-YE;Lo;0;L;;;;;N;;;;; +1193;HANGUL JUNGSEONG YU-U;Lo;0;L;;;;;N;;;;; +1194;HANGUL JUNGSEONG YU-I;Lo;0;L;;;;;N;;;;; +1195;HANGUL JUNGSEONG EU-U;Lo;0;L;;;;;N;;;;; +1196;HANGUL JUNGSEONG EU-EU;Lo;0;L;;;;;N;;;;; +1197;HANGUL JUNGSEONG YI-U;Lo;0;L;;;;;N;;;;; +1198;HANGUL JUNGSEONG I-A;Lo;0;L;;;;;N;;;;; +1199;HANGUL JUNGSEONG I-YA;Lo;0;L;;;;;N;;;;; +119A;HANGUL JUNGSEONG I-O;Lo;0;L;;;;;N;;;;; +119B;HANGUL JUNGSEONG I-U;Lo;0;L;;;;;N;;;;; +119C;HANGUL JUNGSEONG I-EU;Lo;0;L;;;;;N;;;;; +119D;HANGUL JUNGSEONG I-ARAEA;Lo;0;L;;;;;N;;;;; +119E;HANGUL JUNGSEONG ARAEA;Lo;0;L;;;;;N;;;;; +119F;HANGUL JUNGSEONG ARAEA-EO;Lo;0;L;;;;;N;;;;; +11A0;HANGUL JUNGSEONG ARAEA-U;Lo;0;L;;;;;N;;;;; +11A1;HANGUL JUNGSEONG ARAEA-I;Lo;0;L;;;;;N;;;;; +11A2;HANGUL JUNGSEONG SSANGARAEA;Lo;0;L;;;;;N;;;;; +11A8;HANGUL JONGSEONG KIYEOK;Lo;0;L;;;;;N;;g *;;; +11A9;HANGUL JONGSEONG SSANGKIYEOK;Lo;0;L;;;;;N;;gg *;;; +11AA;HANGUL JONGSEONG KIYEOK-SIOS;Lo;0;L;;;;;N;;gs *;;; +11AB;HANGUL JONGSEONG NIEUN;Lo;0;L;;;;;N;;n *;;; +11AC;HANGUL JONGSEONG NIEUN-CIEUC;Lo;0;L;;;;;N;;nj *;;; +11AD;HANGUL JONGSEONG NIEUN-HIEUH;Lo;0;L;;;;;N;;nh *;;; +11AE;HANGUL JONGSEONG TIKEUT;Lo;0;L;;;;;N;;d *;;; +11AF;HANGUL JONGSEONG RIEUL;Lo;0;L;;;;;N;;l *;;; +11B0;HANGUL JONGSEONG RIEUL-KIYEOK;Lo;0;L;;;;;N;;lg *;;; +11B1;HANGUL JONGSEONG RIEUL-MIEUM;Lo;0;L;;;;;N;;lm *;;; +11B2;HANGUL JONGSEONG RIEUL-PIEUP;Lo;0;L;;;;;N;;lb *;;; +11B3;HANGUL JONGSEONG RIEUL-SIOS;Lo;0;L;;;;;N;;ls *;;; +11B4;HANGUL JONGSEONG RIEUL-THIEUTH;Lo;0;L;;;;;N;;lt *;;; +11B5;HANGUL JONGSEONG RIEUL-PHIEUPH;Lo;0;L;;;;;N;;lp *;;; +11B6;HANGUL JONGSEONG RIEUL-HIEUH;Lo;0;L;;;;;N;;lh *;;; +11B7;HANGUL JONGSEONG MIEUM;Lo;0;L;;;;;N;;m *;;; +11B8;HANGUL JONGSEONG PIEUP;Lo;0;L;;;;;N;;b *;;; +11B9;HANGUL JONGSEONG PIEUP-SIOS;Lo;0;L;;;;;N;;bs *;;; +11BA;HANGUL JONGSEONG SIOS;Lo;0;L;;;;;N;;s *;;; +11BB;HANGUL JONGSEONG SSANGSIOS;Lo;0;L;;;;;N;;ss *;;; +11BC;HANGUL JONGSEONG IEUNG;Lo;0;L;;;;;N;;ng *;;; +11BD;HANGUL JONGSEONG CIEUC;Lo;0;L;;;;;N;;j *;;; +11BE;HANGUL JONGSEONG CHIEUCH;Lo;0;L;;;;;N;;c *;;; +11BF;HANGUL JONGSEONG KHIEUKH;Lo;0;L;;;;;N;;k *;;; +11C0;HANGUL JONGSEONG THIEUTH;Lo;0;L;;;;;N;;t *;;; +11C1;HANGUL JONGSEONG PHIEUPH;Lo;0;L;;;;;N;;p *;;; +11C2;HANGUL JONGSEONG HIEUH;Lo;0;L;;;;;N;;h *;;; +11C3;HANGUL JONGSEONG KIYEOK-RIEUL;Lo;0;L;;;;;N;;;;; +11C4;HANGUL JONGSEONG KIYEOK-SIOS-KIYEOK;Lo;0;L;;;;;N;;;;; +11C5;HANGUL JONGSEONG NIEUN-KIYEOK;Lo;0;L;;;;;N;;;;; +11C6;HANGUL JONGSEONG NIEUN-TIKEUT;Lo;0;L;;;;;N;;;;; +11C7;HANGUL JONGSEONG NIEUN-SIOS;Lo;0;L;;;;;N;;;;; +11C8;HANGUL JONGSEONG NIEUN-PANSIOS;Lo;0;L;;;;;N;;;;; +11C9;HANGUL JONGSEONG NIEUN-THIEUTH;Lo;0;L;;;;;N;;;;; +11CA;HANGUL JONGSEONG TIKEUT-KIYEOK;Lo;0;L;;;;;N;;;;; +11CB;HANGUL JONGSEONG TIKEUT-RIEUL;Lo;0;L;;;;;N;;;;; +11CC;HANGUL JONGSEONG RIEUL-KIYEOK-SIOS;Lo;0;L;;;;;N;;;;; +11CD;HANGUL JONGSEONG RIEUL-NIEUN;Lo;0;L;;;;;N;;;;; +11CE;HANGUL JONGSEONG RIEUL-TIKEUT;Lo;0;L;;;;;N;;;;; +11CF;HANGUL JONGSEONG RIEUL-TIKEUT-HIEUH;Lo;0;L;;;;;N;;;;; +11D0;HANGUL JONGSEONG SSANGRIEUL;Lo;0;L;;;;;N;;;;; +11D1;HANGUL JONGSEONG RIEUL-MIEUM-KIYEOK;Lo;0;L;;;;;N;;;;; +11D2;HANGUL JONGSEONG RIEUL-MIEUM-SIOS;Lo;0;L;;;;;N;;;;; +11D3;HANGUL JONGSEONG RIEUL-PIEUP-SIOS;Lo;0;L;;;;;N;;;;; +11D4;HANGUL JONGSEONG RIEUL-PIEUP-HIEUH;Lo;0;L;;;;;N;;;;; +11D5;HANGUL JONGSEONG RIEUL-KAPYEOUNPIEUP;Lo;0;L;;;;;N;;;;; +11D6;HANGUL JONGSEONG RIEUL-SSANGSIOS;Lo;0;L;;;;;N;;;;; +11D7;HANGUL JONGSEONG RIEUL-PANSIOS;Lo;0;L;;;;;N;;;;; +11D8;HANGUL JONGSEONG RIEUL-KHIEUKH;Lo;0;L;;;;;N;;;;; +11D9;HANGUL JONGSEONG RIEUL-YEORINHIEUH;Lo;0;L;;;;;N;;;;; +11DA;HANGUL JONGSEONG MIEUM-KIYEOK;Lo;0;L;;;;;N;;;;; +11DB;HANGUL JONGSEONG MIEUM-RIEUL;Lo;0;L;;;;;N;;;;; +11DC;HANGUL JONGSEONG MIEUM-PIEUP;Lo;0;L;;;;;N;;;;; +11DD;HANGUL JONGSEONG MIEUM-SIOS;Lo;0;L;;;;;N;;;;; +11DE;HANGUL JONGSEONG MIEUM-SSANGSIOS;Lo;0;L;;;;;N;;;;; +11DF;HANGUL JONGSEONG MIEUM-PANSIOS;Lo;0;L;;;;;N;;;;; +11E0;HANGUL JONGSEONG MIEUM-CHIEUCH;Lo;0;L;;;;;N;;;;; +11E1;HANGUL JONGSEONG MIEUM-HIEUH;Lo;0;L;;;;;N;;;;; +11E2;HANGUL JONGSEONG KAPYEOUNMIEUM;Lo;0;L;;;;;N;;;;; +11E3;HANGUL JONGSEONG PIEUP-RIEUL;Lo;0;L;;;;;N;;;;; +11E4;HANGUL JONGSEONG PIEUP-PHIEUPH;Lo;0;L;;;;;N;;;;; +11E5;HANGUL JONGSEONG PIEUP-HIEUH;Lo;0;L;;;;;N;;;;; +11E6;HANGUL JONGSEONG KAPYEOUNPIEUP;Lo;0;L;;;;;N;;;;; +11E7;HANGUL JONGSEONG SIOS-KIYEOK;Lo;0;L;;;;;N;;;;; +11E8;HANGUL JONGSEONG SIOS-TIKEUT;Lo;0;L;;;;;N;;;;; +11E9;HANGUL JONGSEONG SIOS-RIEUL;Lo;0;L;;;;;N;;;;; +11EA;HANGUL JONGSEONG SIOS-PIEUP;Lo;0;L;;;;;N;;;;; +11EB;HANGUL JONGSEONG PANSIOS;Lo;0;L;;;;;N;;;;; +11EC;HANGUL JONGSEONG IEUNG-KIYEOK;Lo;0;L;;;;;N;;;;; +11ED;HANGUL JONGSEONG IEUNG-SSANGKIYEOK;Lo;0;L;;;;;N;;;;; +11EE;HANGUL JONGSEONG SSANGIEUNG;Lo;0;L;;;;;N;;;;; +11EF;HANGUL JONGSEONG IEUNG-KHIEUKH;Lo;0;L;;;;;N;;;;; +11F0;HANGUL JONGSEONG YESIEUNG;Lo;0;L;;;;;N;;;;; +11F1;HANGUL JONGSEONG YESIEUNG-SIOS;Lo;0;L;;;;;N;;;;; +11F2;HANGUL JONGSEONG YESIEUNG-PANSIOS;Lo;0;L;;;;;N;;;;; +11F3;HANGUL JONGSEONG PHIEUPH-PIEUP;Lo;0;L;;;;;N;;;;; +11F4;HANGUL JONGSEONG KAPYEOUNPHIEUPH;Lo;0;L;;;;;N;;;;; +11F5;HANGUL JONGSEONG HIEUH-NIEUN;Lo;0;L;;;;;N;;;;; +11F6;HANGUL JONGSEONG HIEUH-RIEUL;Lo;0;L;;;;;N;;;;; +11F7;HANGUL JONGSEONG HIEUH-MIEUM;Lo;0;L;;;;;N;;;;; +11F8;HANGUL JONGSEONG HIEUH-PIEUP;Lo;0;L;;;;;N;;;;; +11F9;HANGUL JONGSEONG YEORINHIEUH;Lo;0;L;;;;;N;;;;; +1200;ETHIOPIC SYLLABLE HA;Lo;0;L;;;;;N;;;;; +1201;ETHIOPIC SYLLABLE HU;Lo;0;L;;;;;N;;;;; +1202;ETHIOPIC SYLLABLE HI;Lo;0;L;;;;;N;;;;; +1203;ETHIOPIC SYLLABLE HAA;Lo;0;L;;;;;N;;;;; +1204;ETHIOPIC SYLLABLE HEE;Lo;0;L;;;;;N;;;;; +1205;ETHIOPIC SYLLABLE HE;Lo;0;L;;;;;N;;;;; +1206;ETHIOPIC SYLLABLE HO;Lo;0;L;;;;;N;;;;; +1208;ETHIOPIC SYLLABLE LA;Lo;0;L;;;;;N;;;;; +1209;ETHIOPIC SYLLABLE LU;Lo;0;L;;;;;N;;;;; +120A;ETHIOPIC SYLLABLE LI;Lo;0;L;;;;;N;;;;; +120B;ETHIOPIC SYLLABLE LAA;Lo;0;L;;;;;N;;;;; +120C;ETHIOPIC SYLLABLE LEE;Lo;0;L;;;;;N;;;;; +120D;ETHIOPIC SYLLABLE LE;Lo;0;L;;;;;N;;;;; +120E;ETHIOPIC SYLLABLE LO;Lo;0;L;;;;;N;;;;; +120F;ETHIOPIC SYLLABLE LWA;Lo;0;L;;;;;N;;;;; +1210;ETHIOPIC SYLLABLE HHA;Lo;0;L;;;;;N;;;;; +1211;ETHIOPIC SYLLABLE HHU;Lo;0;L;;;;;N;;;;; +1212;ETHIOPIC SYLLABLE HHI;Lo;0;L;;;;;N;;;;; +1213;ETHIOPIC SYLLABLE HHAA;Lo;0;L;;;;;N;;;;; +1214;ETHIOPIC SYLLABLE HHEE;Lo;0;L;;;;;N;;;;; +1215;ETHIOPIC SYLLABLE HHE;Lo;0;L;;;;;N;;;;; +1216;ETHIOPIC SYLLABLE HHO;Lo;0;L;;;;;N;;;;; +1217;ETHIOPIC SYLLABLE HHWA;Lo;0;L;;;;;N;;;;; +1218;ETHIOPIC SYLLABLE MA;Lo;0;L;;;;;N;;;;; +1219;ETHIOPIC SYLLABLE MU;Lo;0;L;;;;;N;;;;; +121A;ETHIOPIC SYLLABLE MI;Lo;0;L;;;;;N;;;;; +121B;ETHIOPIC SYLLABLE MAA;Lo;0;L;;;;;N;;;;; +121C;ETHIOPIC SYLLABLE MEE;Lo;0;L;;;;;N;;;;; +121D;ETHIOPIC SYLLABLE ME;Lo;0;L;;;;;N;;;;; +121E;ETHIOPIC SYLLABLE MO;Lo;0;L;;;;;N;;;;; +121F;ETHIOPIC SYLLABLE MWA;Lo;0;L;;;;;N;;;;; +1220;ETHIOPIC SYLLABLE SZA;Lo;0;L;;;;;N;;;;; +1221;ETHIOPIC SYLLABLE SZU;Lo;0;L;;;;;N;;;;; +1222;ETHIOPIC SYLLABLE SZI;Lo;0;L;;;;;N;;;;; +1223;ETHIOPIC SYLLABLE SZAA;Lo;0;L;;;;;N;;;;; +1224;ETHIOPIC SYLLABLE SZEE;Lo;0;L;;;;;N;;;;; +1225;ETHIOPIC SYLLABLE SZE;Lo;0;L;;;;;N;;;;; +1226;ETHIOPIC SYLLABLE SZO;Lo;0;L;;;;;N;;;;; +1227;ETHIOPIC SYLLABLE SZWA;Lo;0;L;;;;;N;;;;; +1228;ETHIOPIC SYLLABLE RA;Lo;0;L;;;;;N;;;;; +1229;ETHIOPIC SYLLABLE RU;Lo;0;L;;;;;N;;;;; +122A;ETHIOPIC SYLLABLE RI;Lo;0;L;;;;;N;;;;; +122B;ETHIOPIC SYLLABLE RAA;Lo;0;L;;;;;N;;;;; +122C;ETHIOPIC SYLLABLE REE;Lo;0;L;;;;;N;;;;; +122D;ETHIOPIC SYLLABLE RE;Lo;0;L;;;;;N;;;;; +122E;ETHIOPIC SYLLABLE RO;Lo;0;L;;;;;N;;;;; +122F;ETHIOPIC SYLLABLE RWA;Lo;0;L;;;;;N;;;;; +1230;ETHIOPIC SYLLABLE SA;Lo;0;L;;;;;N;;;;; +1231;ETHIOPIC SYLLABLE SU;Lo;0;L;;;;;N;;;;; +1232;ETHIOPIC SYLLABLE SI;Lo;0;L;;;;;N;;;;; +1233;ETHIOPIC SYLLABLE SAA;Lo;0;L;;;;;N;;;;; +1234;ETHIOPIC SYLLABLE SEE;Lo;0;L;;;;;N;;;;; +1235;ETHIOPIC SYLLABLE SE;Lo;0;L;;;;;N;;;;; +1236;ETHIOPIC SYLLABLE SO;Lo;0;L;;;;;N;;;;; +1237;ETHIOPIC SYLLABLE SWA;Lo;0;L;;;;;N;;;;; +1238;ETHIOPIC SYLLABLE SHA;Lo;0;L;;;;;N;;;;; +1239;ETHIOPIC SYLLABLE SHU;Lo;0;L;;;;;N;;;;; +123A;ETHIOPIC SYLLABLE SHI;Lo;0;L;;;;;N;;;;; +123B;ETHIOPIC SYLLABLE SHAA;Lo;0;L;;;;;N;;;;; +123C;ETHIOPIC SYLLABLE SHEE;Lo;0;L;;;;;N;;;;; +123D;ETHIOPIC SYLLABLE SHE;Lo;0;L;;;;;N;;;;; +123E;ETHIOPIC SYLLABLE SHO;Lo;0;L;;;;;N;;;;; +123F;ETHIOPIC SYLLABLE SHWA;Lo;0;L;;;;;N;;;;; +1240;ETHIOPIC SYLLABLE QA;Lo;0;L;;;;;N;;;;; +1241;ETHIOPIC SYLLABLE QU;Lo;0;L;;;;;N;;;;; +1242;ETHIOPIC SYLLABLE QI;Lo;0;L;;;;;N;;;;; +1243;ETHIOPIC SYLLABLE QAA;Lo;0;L;;;;;N;;;;; +1244;ETHIOPIC SYLLABLE QEE;Lo;0;L;;;;;N;;;;; +1245;ETHIOPIC SYLLABLE QE;Lo;0;L;;;;;N;;;;; +1246;ETHIOPIC SYLLABLE QO;Lo;0;L;;;;;N;;;;; +1248;ETHIOPIC SYLLABLE QWA;Lo;0;L;;;;;N;;;;; +124A;ETHIOPIC SYLLABLE QWI;Lo;0;L;;;;;N;;;;; +124B;ETHIOPIC SYLLABLE QWAA;Lo;0;L;;;;;N;;;;; +124C;ETHIOPIC SYLLABLE QWEE;Lo;0;L;;;;;N;;;;; +124D;ETHIOPIC SYLLABLE QWE;Lo;0;L;;;;;N;;;;; +1250;ETHIOPIC SYLLABLE QHA;Lo;0;L;;;;;N;;;;; +1251;ETHIOPIC SYLLABLE QHU;Lo;0;L;;;;;N;;;;; +1252;ETHIOPIC SYLLABLE QHI;Lo;0;L;;;;;N;;;;; +1253;ETHIOPIC SYLLABLE QHAA;Lo;0;L;;;;;N;;;;; +1254;ETHIOPIC SYLLABLE QHEE;Lo;0;L;;;;;N;;;;; +1255;ETHIOPIC SYLLABLE QHE;Lo;0;L;;;;;N;;;;; +1256;ETHIOPIC SYLLABLE QHO;Lo;0;L;;;;;N;;;;; +1258;ETHIOPIC SYLLABLE QHWA;Lo;0;L;;;;;N;;;;; +125A;ETHIOPIC SYLLABLE QHWI;Lo;0;L;;;;;N;;;;; +125B;ETHIOPIC SYLLABLE QHWAA;Lo;0;L;;;;;N;;;;; +125C;ETHIOPIC SYLLABLE QHWEE;Lo;0;L;;;;;N;;;;; +125D;ETHIOPIC SYLLABLE QHWE;Lo;0;L;;;;;N;;;;; +1260;ETHIOPIC SYLLABLE BA;Lo;0;L;;;;;N;;;;; +1261;ETHIOPIC SYLLABLE BU;Lo;0;L;;;;;N;;;;; +1262;ETHIOPIC SYLLABLE BI;Lo;0;L;;;;;N;;;;; +1263;ETHIOPIC SYLLABLE BAA;Lo;0;L;;;;;N;;;;; +1264;ETHIOPIC SYLLABLE BEE;Lo;0;L;;;;;N;;;;; +1265;ETHIOPIC SYLLABLE BE;Lo;0;L;;;;;N;;;;; +1266;ETHIOPIC SYLLABLE BO;Lo;0;L;;;;;N;;;;; +1267;ETHIOPIC SYLLABLE BWA;Lo;0;L;;;;;N;;;;; +1268;ETHIOPIC SYLLABLE VA;Lo;0;L;;;;;N;;;;; +1269;ETHIOPIC SYLLABLE VU;Lo;0;L;;;;;N;;;;; +126A;ETHIOPIC SYLLABLE VI;Lo;0;L;;;;;N;;;;; +126B;ETHIOPIC SYLLABLE VAA;Lo;0;L;;;;;N;;;;; +126C;ETHIOPIC SYLLABLE VEE;Lo;0;L;;;;;N;;;;; +126D;ETHIOPIC SYLLABLE VE;Lo;0;L;;;;;N;;;;; +126E;ETHIOPIC SYLLABLE VO;Lo;0;L;;;;;N;;;;; +126F;ETHIOPIC SYLLABLE VWA;Lo;0;L;;;;;N;;;;; +1270;ETHIOPIC SYLLABLE TA;Lo;0;L;;;;;N;;;;; +1271;ETHIOPIC SYLLABLE TU;Lo;0;L;;;;;N;;;;; +1272;ETHIOPIC SYLLABLE TI;Lo;0;L;;;;;N;;;;; +1273;ETHIOPIC SYLLABLE TAA;Lo;0;L;;;;;N;;;;; +1274;ETHIOPIC SYLLABLE TEE;Lo;0;L;;;;;N;;;;; +1275;ETHIOPIC SYLLABLE TE;Lo;0;L;;;;;N;;;;; +1276;ETHIOPIC SYLLABLE TO;Lo;0;L;;;;;N;;;;; +1277;ETHIOPIC SYLLABLE TWA;Lo;0;L;;;;;N;;;;; +1278;ETHIOPIC SYLLABLE CA;Lo;0;L;;;;;N;;;;; +1279;ETHIOPIC SYLLABLE CU;Lo;0;L;;;;;N;;;;; +127A;ETHIOPIC SYLLABLE CI;Lo;0;L;;;;;N;;;;; +127B;ETHIOPIC SYLLABLE CAA;Lo;0;L;;;;;N;;;;; +127C;ETHIOPIC SYLLABLE CEE;Lo;0;L;;;;;N;;;;; +127D;ETHIOPIC SYLLABLE CE;Lo;0;L;;;;;N;;;;; +127E;ETHIOPIC SYLLABLE CO;Lo;0;L;;;;;N;;;;; +127F;ETHIOPIC SYLLABLE CWA;Lo;0;L;;;;;N;;;;; +1280;ETHIOPIC SYLLABLE XA;Lo;0;L;;;;;N;;;;; +1281;ETHIOPIC SYLLABLE XU;Lo;0;L;;;;;N;;;;; +1282;ETHIOPIC SYLLABLE XI;Lo;0;L;;;;;N;;;;; +1283;ETHIOPIC SYLLABLE XAA;Lo;0;L;;;;;N;;;;; +1284;ETHIOPIC SYLLABLE XEE;Lo;0;L;;;;;N;;;;; +1285;ETHIOPIC SYLLABLE XE;Lo;0;L;;;;;N;;;;; +1286;ETHIOPIC SYLLABLE XO;Lo;0;L;;;;;N;;;;; +1288;ETHIOPIC SYLLABLE XWA;Lo;0;L;;;;;N;;;;; +128A;ETHIOPIC SYLLABLE XWI;Lo;0;L;;;;;N;;;;; +128B;ETHIOPIC SYLLABLE XWAA;Lo;0;L;;;;;N;;;;; +128C;ETHIOPIC SYLLABLE XWEE;Lo;0;L;;;;;N;;;;; +128D;ETHIOPIC SYLLABLE XWE;Lo;0;L;;;;;N;;;;; +1290;ETHIOPIC SYLLABLE NA;Lo;0;L;;;;;N;;;;; +1291;ETHIOPIC SYLLABLE NU;Lo;0;L;;;;;N;;;;; +1292;ETHIOPIC SYLLABLE NI;Lo;0;L;;;;;N;;;;; +1293;ETHIOPIC SYLLABLE NAA;Lo;0;L;;;;;N;;;;; +1294;ETHIOPIC SYLLABLE NEE;Lo;0;L;;;;;N;;;;; +1295;ETHIOPIC SYLLABLE NE;Lo;0;L;;;;;N;;;;; +1296;ETHIOPIC SYLLABLE NO;Lo;0;L;;;;;N;;;;; +1297;ETHIOPIC SYLLABLE NWA;Lo;0;L;;;;;N;;;;; +1298;ETHIOPIC SYLLABLE NYA;Lo;0;L;;;;;N;;;;; +1299;ETHIOPIC SYLLABLE NYU;Lo;0;L;;;;;N;;;;; +129A;ETHIOPIC SYLLABLE NYI;Lo;0;L;;;;;N;;;;; +129B;ETHIOPIC SYLLABLE NYAA;Lo;0;L;;;;;N;;;;; +129C;ETHIOPIC SYLLABLE NYEE;Lo;0;L;;;;;N;;;;; +129D;ETHIOPIC SYLLABLE NYE;Lo;0;L;;;;;N;;;;; +129E;ETHIOPIC SYLLABLE NYO;Lo;0;L;;;;;N;;;;; +129F;ETHIOPIC SYLLABLE NYWA;Lo;0;L;;;;;N;;;;; +12A0;ETHIOPIC SYLLABLE GLOTTAL A;Lo;0;L;;;;;N;;;;; +12A1;ETHIOPIC SYLLABLE GLOTTAL U;Lo;0;L;;;;;N;;;;; +12A2;ETHIOPIC SYLLABLE GLOTTAL I;Lo;0;L;;;;;N;;;;; +12A3;ETHIOPIC SYLLABLE GLOTTAL AA;Lo;0;L;;;;;N;;;;; +12A4;ETHIOPIC SYLLABLE GLOTTAL EE;Lo;0;L;;;;;N;;;;; +12A5;ETHIOPIC SYLLABLE GLOTTAL E;Lo;0;L;;;;;N;;;;; +12A6;ETHIOPIC SYLLABLE GLOTTAL O;Lo;0;L;;;;;N;;;;; +12A7;ETHIOPIC SYLLABLE GLOTTAL WA;Lo;0;L;;;;;N;;;;; +12A8;ETHIOPIC SYLLABLE KA;Lo;0;L;;;;;N;;;;; +12A9;ETHIOPIC SYLLABLE KU;Lo;0;L;;;;;N;;;;; +12AA;ETHIOPIC SYLLABLE KI;Lo;0;L;;;;;N;;;;; +12AB;ETHIOPIC SYLLABLE KAA;Lo;0;L;;;;;N;;;;; +12AC;ETHIOPIC SYLLABLE KEE;Lo;0;L;;;;;N;;;;; +12AD;ETHIOPIC SYLLABLE KE;Lo;0;L;;;;;N;;;;; +12AE;ETHIOPIC SYLLABLE KO;Lo;0;L;;;;;N;;;;; +12B0;ETHIOPIC SYLLABLE KWA;Lo;0;L;;;;;N;;;;; +12B2;ETHIOPIC SYLLABLE KWI;Lo;0;L;;;;;N;;;;; +12B3;ETHIOPIC SYLLABLE KWAA;Lo;0;L;;;;;N;;;;; +12B4;ETHIOPIC SYLLABLE KWEE;Lo;0;L;;;;;N;;;;; +12B5;ETHIOPIC SYLLABLE KWE;Lo;0;L;;;;;N;;;;; +12B8;ETHIOPIC SYLLABLE KXA;Lo;0;L;;;;;N;;;;; +12B9;ETHIOPIC SYLLABLE KXU;Lo;0;L;;;;;N;;;;; +12BA;ETHIOPIC SYLLABLE KXI;Lo;0;L;;;;;N;;;;; +12BB;ETHIOPIC SYLLABLE KXAA;Lo;0;L;;;;;N;;;;; +12BC;ETHIOPIC SYLLABLE KXEE;Lo;0;L;;;;;N;;;;; +12BD;ETHIOPIC SYLLABLE KXE;Lo;0;L;;;;;N;;;;; +12BE;ETHIOPIC SYLLABLE KXO;Lo;0;L;;;;;N;;;;; +12C0;ETHIOPIC SYLLABLE KXWA;Lo;0;L;;;;;N;;;;; +12C2;ETHIOPIC SYLLABLE KXWI;Lo;0;L;;;;;N;;;;; +12C3;ETHIOPIC SYLLABLE KXWAA;Lo;0;L;;;;;N;;;;; +12C4;ETHIOPIC SYLLABLE KXWEE;Lo;0;L;;;;;N;;;;; +12C5;ETHIOPIC SYLLABLE KXWE;Lo;0;L;;;;;N;;;;; +12C8;ETHIOPIC SYLLABLE WA;Lo;0;L;;;;;N;;;;; +12C9;ETHIOPIC SYLLABLE WU;Lo;0;L;;;;;N;;;;; +12CA;ETHIOPIC SYLLABLE WI;Lo;0;L;;;;;N;;;;; +12CB;ETHIOPIC SYLLABLE WAA;Lo;0;L;;;;;N;;;;; +12CC;ETHIOPIC SYLLABLE WEE;Lo;0;L;;;;;N;;;;; +12CD;ETHIOPIC SYLLABLE WE;Lo;0;L;;;;;N;;;;; +12CE;ETHIOPIC SYLLABLE WO;Lo;0;L;;;;;N;;;;; +12D0;ETHIOPIC SYLLABLE PHARYNGEAL A;Lo;0;L;;;;;N;;;;; +12D1;ETHIOPIC SYLLABLE PHARYNGEAL U;Lo;0;L;;;;;N;;;;; +12D2;ETHIOPIC SYLLABLE PHARYNGEAL I;Lo;0;L;;;;;N;;;;; +12D3;ETHIOPIC SYLLABLE PHARYNGEAL AA;Lo;0;L;;;;;N;;;;; +12D4;ETHIOPIC SYLLABLE PHARYNGEAL EE;Lo;0;L;;;;;N;;;;; +12D5;ETHIOPIC SYLLABLE PHARYNGEAL E;Lo;0;L;;;;;N;;;;; +12D6;ETHIOPIC SYLLABLE PHARYNGEAL O;Lo;0;L;;;;;N;;;;; +12D8;ETHIOPIC SYLLABLE ZA;Lo;0;L;;;;;N;;;;; +12D9;ETHIOPIC SYLLABLE ZU;Lo;0;L;;;;;N;;;;; +12DA;ETHIOPIC SYLLABLE ZI;Lo;0;L;;;;;N;;;;; +12DB;ETHIOPIC SYLLABLE ZAA;Lo;0;L;;;;;N;;;;; +12DC;ETHIOPIC SYLLABLE ZEE;Lo;0;L;;;;;N;;;;; +12DD;ETHIOPIC SYLLABLE ZE;Lo;0;L;;;;;N;;;;; +12DE;ETHIOPIC SYLLABLE ZO;Lo;0;L;;;;;N;;;;; +12DF;ETHIOPIC SYLLABLE ZWA;Lo;0;L;;;;;N;;;;; +12E0;ETHIOPIC SYLLABLE ZHA;Lo;0;L;;;;;N;;;;; +12E1;ETHIOPIC SYLLABLE ZHU;Lo;0;L;;;;;N;;;;; +12E2;ETHIOPIC SYLLABLE ZHI;Lo;0;L;;;;;N;;;;; +12E3;ETHIOPIC SYLLABLE ZHAA;Lo;0;L;;;;;N;;;;; +12E4;ETHIOPIC SYLLABLE ZHEE;Lo;0;L;;;;;N;;;;; +12E5;ETHIOPIC SYLLABLE ZHE;Lo;0;L;;;;;N;;;;; +12E6;ETHIOPIC SYLLABLE ZHO;Lo;0;L;;;;;N;;;;; +12E7;ETHIOPIC SYLLABLE ZHWA;Lo;0;L;;;;;N;;;;; +12E8;ETHIOPIC SYLLABLE YA;Lo;0;L;;;;;N;;;;; +12E9;ETHIOPIC SYLLABLE YU;Lo;0;L;;;;;N;;;;; +12EA;ETHIOPIC SYLLABLE YI;Lo;0;L;;;;;N;;;;; +12EB;ETHIOPIC SYLLABLE YAA;Lo;0;L;;;;;N;;;;; +12EC;ETHIOPIC SYLLABLE YEE;Lo;0;L;;;;;N;;;;; +12ED;ETHIOPIC SYLLABLE YE;Lo;0;L;;;;;N;;;;; +12EE;ETHIOPIC SYLLABLE YO;Lo;0;L;;;;;N;;;;; +12F0;ETHIOPIC SYLLABLE DA;Lo;0;L;;;;;N;;;;; +12F1;ETHIOPIC SYLLABLE DU;Lo;0;L;;;;;N;;;;; +12F2;ETHIOPIC SYLLABLE DI;Lo;0;L;;;;;N;;;;; +12F3;ETHIOPIC SYLLABLE DAA;Lo;0;L;;;;;N;;;;; +12F4;ETHIOPIC SYLLABLE DEE;Lo;0;L;;;;;N;;;;; +12F5;ETHIOPIC SYLLABLE DE;Lo;0;L;;;;;N;;;;; +12F6;ETHIOPIC SYLLABLE DO;Lo;0;L;;;;;N;;;;; +12F7;ETHIOPIC SYLLABLE DWA;Lo;0;L;;;;;N;;;;; +12F8;ETHIOPIC SYLLABLE DDA;Lo;0;L;;;;;N;;;;; +12F9;ETHIOPIC SYLLABLE DDU;Lo;0;L;;;;;N;;;;; +12FA;ETHIOPIC SYLLABLE DDI;Lo;0;L;;;;;N;;;;; +12FB;ETHIOPIC SYLLABLE DDAA;Lo;0;L;;;;;N;;;;; +12FC;ETHIOPIC SYLLABLE DDEE;Lo;0;L;;;;;N;;;;; +12FD;ETHIOPIC SYLLABLE DDE;Lo;0;L;;;;;N;;;;; +12FE;ETHIOPIC SYLLABLE DDO;Lo;0;L;;;;;N;;;;; +12FF;ETHIOPIC SYLLABLE DDWA;Lo;0;L;;;;;N;;;;; +1300;ETHIOPIC SYLLABLE JA;Lo;0;L;;;;;N;;;;; +1301;ETHIOPIC SYLLABLE JU;Lo;0;L;;;;;N;;;;; +1302;ETHIOPIC SYLLABLE JI;Lo;0;L;;;;;N;;;;; +1303;ETHIOPIC SYLLABLE JAA;Lo;0;L;;;;;N;;;;; +1304;ETHIOPIC SYLLABLE JEE;Lo;0;L;;;;;N;;;;; +1305;ETHIOPIC SYLLABLE JE;Lo;0;L;;;;;N;;;;; +1306;ETHIOPIC SYLLABLE JO;Lo;0;L;;;;;N;;;;; +1307;ETHIOPIC SYLLABLE JWA;Lo;0;L;;;;;N;;;;; +1308;ETHIOPIC SYLLABLE GA;Lo;0;L;;;;;N;;;;; +1309;ETHIOPIC SYLLABLE GU;Lo;0;L;;;;;N;;;;; +130A;ETHIOPIC SYLLABLE GI;Lo;0;L;;;;;N;;;;; +130B;ETHIOPIC SYLLABLE GAA;Lo;0;L;;;;;N;;;;; +130C;ETHIOPIC SYLLABLE GEE;Lo;0;L;;;;;N;;;;; +130D;ETHIOPIC SYLLABLE GE;Lo;0;L;;;;;N;;;;; +130E;ETHIOPIC SYLLABLE GO;Lo;0;L;;;;;N;;;;; +1310;ETHIOPIC SYLLABLE GWA;Lo;0;L;;;;;N;;;;; +1312;ETHIOPIC SYLLABLE GWI;Lo;0;L;;;;;N;;;;; +1313;ETHIOPIC SYLLABLE GWAA;Lo;0;L;;;;;N;;;;; +1314;ETHIOPIC SYLLABLE GWEE;Lo;0;L;;;;;N;;;;; +1315;ETHIOPIC SYLLABLE GWE;Lo;0;L;;;;;N;;;;; +1318;ETHIOPIC SYLLABLE GGA;Lo;0;L;;;;;N;;;;; +1319;ETHIOPIC SYLLABLE GGU;Lo;0;L;;;;;N;;;;; +131A;ETHIOPIC SYLLABLE GGI;Lo;0;L;;;;;N;;;;; +131B;ETHIOPIC SYLLABLE GGAA;Lo;0;L;;;;;N;;;;; +131C;ETHIOPIC SYLLABLE GGEE;Lo;0;L;;;;;N;;;;; +131D;ETHIOPIC SYLLABLE GGE;Lo;0;L;;;;;N;;;;; +131E;ETHIOPIC SYLLABLE GGO;Lo;0;L;;;;;N;;;;; +1320;ETHIOPIC SYLLABLE THA;Lo;0;L;;;;;N;;;;; +1321;ETHIOPIC SYLLABLE THU;Lo;0;L;;;;;N;;;;; +1322;ETHIOPIC SYLLABLE THI;Lo;0;L;;;;;N;;;;; +1323;ETHIOPIC SYLLABLE THAA;Lo;0;L;;;;;N;;;;; +1324;ETHIOPIC SYLLABLE THEE;Lo;0;L;;;;;N;;;;; +1325;ETHIOPIC SYLLABLE THE;Lo;0;L;;;;;N;;;;; +1326;ETHIOPIC SYLLABLE THO;Lo;0;L;;;;;N;;;;; +1327;ETHIOPIC SYLLABLE THWA;Lo;0;L;;;;;N;;;;; +1328;ETHIOPIC SYLLABLE CHA;Lo;0;L;;;;;N;;;;; +1329;ETHIOPIC SYLLABLE CHU;Lo;0;L;;;;;N;;;;; +132A;ETHIOPIC SYLLABLE CHI;Lo;0;L;;;;;N;;;;; +132B;ETHIOPIC SYLLABLE CHAA;Lo;0;L;;;;;N;;;;; +132C;ETHIOPIC SYLLABLE CHEE;Lo;0;L;;;;;N;;;;; +132D;ETHIOPIC SYLLABLE CHE;Lo;0;L;;;;;N;;;;; +132E;ETHIOPIC SYLLABLE CHO;Lo;0;L;;;;;N;;;;; +132F;ETHIOPIC SYLLABLE CHWA;Lo;0;L;;;;;N;;;;; +1330;ETHIOPIC SYLLABLE PHA;Lo;0;L;;;;;N;;;;; +1331;ETHIOPIC SYLLABLE PHU;Lo;0;L;;;;;N;;;;; +1332;ETHIOPIC SYLLABLE PHI;Lo;0;L;;;;;N;;;;; +1333;ETHIOPIC SYLLABLE PHAA;Lo;0;L;;;;;N;;;;; +1334;ETHIOPIC SYLLABLE PHEE;Lo;0;L;;;;;N;;;;; +1335;ETHIOPIC SYLLABLE PHE;Lo;0;L;;;;;N;;;;; +1336;ETHIOPIC SYLLABLE PHO;Lo;0;L;;;;;N;;;;; +1337;ETHIOPIC SYLLABLE PHWA;Lo;0;L;;;;;N;;;;; +1338;ETHIOPIC SYLLABLE TSA;Lo;0;L;;;;;N;;;;; +1339;ETHIOPIC SYLLABLE TSU;Lo;0;L;;;;;N;;;;; +133A;ETHIOPIC SYLLABLE TSI;Lo;0;L;;;;;N;;;;; +133B;ETHIOPIC SYLLABLE TSAA;Lo;0;L;;;;;N;;;;; +133C;ETHIOPIC SYLLABLE TSEE;Lo;0;L;;;;;N;;;;; +133D;ETHIOPIC SYLLABLE TSE;Lo;0;L;;;;;N;;;;; +133E;ETHIOPIC SYLLABLE TSO;Lo;0;L;;;;;N;;;;; +133F;ETHIOPIC SYLLABLE TSWA;Lo;0;L;;;;;N;;;;; +1340;ETHIOPIC SYLLABLE TZA;Lo;0;L;;;;;N;;;;; +1341;ETHIOPIC SYLLABLE TZU;Lo;0;L;;;;;N;;;;; +1342;ETHIOPIC SYLLABLE TZI;Lo;0;L;;;;;N;;;;; +1343;ETHIOPIC SYLLABLE TZAA;Lo;0;L;;;;;N;;;;; +1344;ETHIOPIC SYLLABLE TZEE;Lo;0;L;;;;;N;;;;; +1345;ETHIOPIC SYLLABLE TZE;Lo;0;L;;;;;N;;;;; +1346;ETHIOPIC SYLLABLE TZO;Lo;0;L;;;;;N;;;;; +1348;ETHIOPIC SYLLABLE FA;Lo;0;L;;;;;N;;;;; +1349;ETHIOPIC SYLLABLE FU;Lo;0;L;;;;;N;;;;; +134A;ETHIOPIC SYLLABLE FI;Lo;0;L;;;;;N;;;;; +134B;ETHIOPIC SYLLABLE FAA;Lo;0;L;;;;;N;;;;; +134C;ETHIOPIC SYLLABLE FEE;Lo;0;L;;;;;N;;;;; +134D;ETHIOPIC SYLLABLE FE;Lo;0;L;;;;;N;;;;; +134E;ETHIOPIC SYLLABLE FO;Lo;0;L;;;;;N;;;;; +134F;ETHIOPIC SYLLABLE FWA;Lo;0;L;;;;;N;;;;; +1350;ETHIOPIC SYLLABLE PA;Lo;0;L;;;;;N;;;;; +1351;ETHIOPIC SYLLABLE PU;Lo;0;L;;;;;N;;;;; +1352;ETHIOPIC SYLLABLE PI;Lo;0;L;;;;;N;;;;; +1353;ETHIOPIC SYLLABLE PAA;Lo;0;L;;;;;N;;;;; +1354;ETHIOPIC SYLLABLE PEE;Lo;0;L;;;;;N;;;;; +1355;ETHIOPIC SYLLABLE PE;Lo;0;L;;;;;N;;;;; +1356;ETHIOPIC SYLLABLE PO;Lo;0;L;;;;;N;;;;; +1357;ETHIOPIC SYLLABLE PWA;Lo;0;L;;;;;N;;;;; +1358;ETHIOPIC SYLLABLE RYA;Lo;0;L;;;;;N;;;;; +1359;ETHIOPIC SYLLABLE MYA;Lo;0;L;;;;;N;;;;; +135A;ETHIOPIC SYLLABLE FYA;Lo;0;L;;;;;N;;;;; +1361;ETHIOPIC WORDSPACE;Po;0;L;;;;;N;;;;; +1362;ETHIOPIC FULL STOP;Po;0;L;;;;;N;;;;; +1363;ETHIOPIC COMMA;Po;0;L;;;;;N;;;;; +1364;ETHIOPIC SEMICOLON;Po;0;L;;;;;N;;;;; +1365;ETHIOPIC COLON;Po;0;L;;;;;N;;;;; +1366;ETHIOPIC PREFACE COLON;Po;0;L;;;;;N;;;;; +1367;ETHIOPIC QUESTION MARK;Po;0;L;;;;;N;;;;; +1368;ETHIOPIC PARAGRAPH SEPARATOR;Po;0;L;;;;;N;;;;; +1369;ETHIOPIC DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +136A;ETHIOPIC DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +136B;ETHIOPIC DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +136C;ETHIOPIC DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +136D;ETHIOPIC DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +136E;ETHIOPIC DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +136F;ETHIOPIC DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +1370;ETHIOPIC DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +1371;ETHIOPIC DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +1372;ETHIOPIC NUMBER TEN;No;0;L;;;;10;N;;;;; +1373;ETHIOPIC NUMBER TWENTY;No;0;L;;;;20;N;;;;; +1374;ETHIOPIC NUMBER THIRTY;No;0;L;;;;30;N;;;;; +1375;ETHIOPIC NUMBER FORTY;No;0;L;;;;40;N;;;;; +1376;ETHIOPIC NUMBER FIFTY;No;0;L;;;;50;N;;;;; +1377;ETHIOPIC NUMBER SIXTY;No;0;L;;;;60;N;;;;; +1378;ETHIOPIC NUMBER SEVENTY;No;0;L;;;;70;N;;;;; +1379;ETHIOPIC NUMBER EIGHTY;No;0;L;;;;80;N;;;;; +137A;ETHIOPIC NUMBER NINETY;No;0;L;;;;90;N;;;;; +137B;ETHIOPIC NUMBER HUNDRED;No;0;L;;;;100;N;;;;; +137C;ETHIOPIC NUMBER TEN THOUSAND;No;0;L;;;;10000;N;;;;; +13A0;CHEROKEE LETTER A;Lo;0;L;;;;;N;;;;; +13A1;CHEROKEE LETTER E;Lo;0;L;;;;;N;;;;; +13A2;CHEROKEE LETTER I;Lo;0;L;;;;;N;;;;; +13A3;CHEROKEE LETTER O;Lo;0;L;;;;;N;;;;; +13A4;CHEROKEE LETTER U;Lo;0;L;;;;;N;;;;; +13A5;CHEROKEE LETTER V;Lo;0;L;;;;;N;;;;; +13A6;CHEROKEE LETTER GA;Lo;0;L;;;;;N;;;;; +13A7;CHEROKEE LETTER KA;Lo;0;L;;;;;N;;;;; +13A8;CHEROKEE LETTER GE;Lo;0;L;;;;;N;;;;; +13A9;CHEROKEE LETTER GI;Lo;0;L;;;;;N;;;;; +13AA;CHEROKEE LETTER GO;Lo;0;L;;;;;N;;;;; +13AB;CHEROKEE LETTER GU;Lo;0;L;;;;;N;;;;; +13AC;CHEROKEE LETTER GV;Lo;0;L;;;;;N;;;;; +13AD;CHEROKEE LETTER HA;Lo;0;L;;;;;N;;;;; +13AE;CHEROKEE LETTER HE;Lo;0;L;;;;;N;;;;; +13AF;CHEROKEE LETTER HI;Lo;0;L;;;;;N;;;;; +13B0;CHEROKEE LETTER HO;Lo;0;L;;;;;N;;;;; +13B1;CHEROKEE LETTER HU;Lo;0;L;;;;;N;;;;; +13B2;CHEROKEE LETTER HV;Lo;0;L;;;;;N;;;;; +13B3;CHEROKEE LETTER LA;Lo;0;L;;;;;N;;;;; +13B4;CHEROKEE LETTER LE;Lo;0;L;;;;;N;;;;; +13B5;CHEROKEE LETTER LI;Lo;0;L;;;;;N;;;;; +13B6;CHEROKEE LETTER LO;Lo;0;L;;;;;N;;;;; +13B7;CHEROKEE LETTER LU;Lo;0;L;;;;;N;;;;; +13B8;CHEROKEE LETTER LV;Lo;0;L;;;;;N;;;;; +13B9;CHEROKEE LETTER MA;Lo;0;L;;;;;N;;;;; +13BA;CHEROKEE LETTER ME;Lo;0;L;;;;;N;;;;; +13BB;CHEROKEE LETTER MI;Lo;0;L;;;;;N;;;;; +13BC;CHEROKEE LETTER MO;Lo;0;L;;;;;N;;;;; +13BD;CHEROKEE LETTER MU;Lo;0;L;;;;;N;;;;; +13BE;CHEROKEE LETTER NA;Lo;0;L;;;;;N;;;;; +13BF;CHEROKEE LETTER HNA;Lo;0;L;;;;;N;;;;; +13C0;CHEROKEE LETTER NAH;Lo;0;L;;;;;N;;;;; +13C1;CHEROKEE LETTER NE;Lo;0;L;;;;;N;;;;; +13C2;CHEROKEE LETTER NI;Lo;0;L;;;;;N;;;;; +13C3;CHEROKEE LETTER NO;Lo;0;L;;;;;N;;;;; +13C4;CHEROKEE LETTER NU;Lo;0;L;;;;;N;;;;; +13C5;CHEROKEE LETTER NV;Lo;0;L;;;;;N;;;;; +13C6;CHEROKEE LETTER QUA;Lo;0;L;;;;;N;;;;; +13C7;CHEROKEE LETTER QUE;Lo;0;L;;;;;N;;;;; +13C8;CHEROKEE LETTER QUI;Lo;0;L;;;;;N;;;;; +13C9;CHEROKEE LETTER QUO;Lo;0;L;;;;;N;;;;; +13CA;CHEROKEE LETTER QUU;Lo;0;L;;;;;N;;;;; +13CB;CHEROKEE LETTER QUV;Lo;0;L;;;;;N;;;;; +13CC;CHEROKEE LETTER SA;Lo;0;L;;;;;N;;;;; +13CD;CHEROKEE LETTER S;Lo;0;L;;;;;N;;;;; +13CE;CHEROKEE LETTER SE;Lo;0;L;;;;;N;;;;; +13CF;CHEROKEE LETTER SI;Lo;0;L;;;;;N;;;;; +13D0;CHEROKEE LETTER SO;Lo;0;L;;;;;N;;;;; +13D1;CHEROKEE LETTER SU;Lo;0;L;;;;;N;;;;; +13D2;CHEROKEE LETTER SV;Lo;0;L;;;;;N;;;;; +13D3;CHEROKEE LETTER DA;Lo;0;L;;;;;N;;;;; +13D4;CHEROKEE LETTER TA;Lo;0;L;;;;;N;;;;; +13D5;CHEROKEE LETTER DE;Lo;0;L;;;;;N;;;;; +13D6;CHEROKEE LETTER TE;Lo;0;L;;;;;N;;;;; +13D7;CHEROKEE LETTER DI;Lo;0;L;;;;;N;;;;; +13D8;CHEROKEE LETTER TI;Lo;0;L;;;;;N;;;;; +13D9;CHEROKEE LETTER DO;Lo;0;L;;;;;N;;;;; +13DA;CHEROKEE LETTER DU;Lo;0;L;;;;;N;;;;; +13DB;CHEROKEE LETTER DV;Lo;0;L;;;;;N;;;;; +13DC;CHEROKEE LETTER DLA;Lo;0;L;;;;;N;;;;; +13DD;CHEROKEE LETTER TLA;Lo;0;L;;;;;N;;;;; +13DE;CHEROKEE LETTER TLE;Lo;0;L;;;;;N;;;;; +13DF;CHEROKEE LETTER TLI;Lo;0;L;;;;;N;;;;; +13E0;CHEROKEE LETTER TLO;Lo;0;L;;;;;N;;;;; +13E1;CHEROKEE LETTER TLU;Lo;0;L;;;;;N;;;;; +13E2;CHEROKEE LETTER TLV;Lo;0;L;;;;;N;;;;; +13E3;CHEROKEE LETTER TSA;Lo;0;L;;;;;N;;;;; +13E4;CHEROKEE LETTER TSE;Lo;0;L;;;;;N;;;;; +13E5;CHEROKEE LETTER TSI;Lo;0;L;;;;;N;;;;; +13E6;CHEROKEE LETTER TSO;Lo;0;L;;;;;N;;;;; +13E7;CHEROKEE LETTER TSU;Lo;0;L;;;;;N;;;;; +13E8;CHEROKEE LETTER TSV;Lo;0;L;;;;;N;;;;; +13E9;CHEROKEE LETTER WA;Lo;0;L;;;;;N;;;;; +13EA;CHEROKEE LETTER WE;Lo;0;L;;;;;N;;;;; +13EB;CHEROKEE LETTER WI;Lo;0;L;;;;;N;;;;; +13EC;CHEROKEE LETTER WO;Lo;0;L;;;;;N;;;;; +13ED;CHEROKEE LETTER WU;Lo;0;L;;;;;N;;;;; +13EE;CHEROKEE LETTER WV;Lo;0;L;;;;;N;;;;; +13EF;CHEROKEE LETTER YA;Lo;0;L;;;;;N;;;;; +13F0;CHEROKEE LETTER YE;Lo;0;L;;;;;N;;;;; +13F1;CHEROKEE LETTER YI;Lo;0;L;;;;;N;;;;; +13F2;CHEROKEE LETTER YO;Lo;0;L;;;;;N;;;;; +13F3;CHEROKEE LETTER YU;Lo;0;L;;;;;N;;;;; +13F4;CHEROKEE LETTER YV;Lo;0;L;;;;;N;;;;; +1401;CANADIAN SYLLABICS E;Lo;0;L;;;;;N;;;;; +1402;CANADIAN SYLLABICS AAI;Lo;0;L;;;;;N;;;;; +1403;CANADIAN SYLLABICS I;Lo;0;L;;;;;N;;;;; +1404;CANADIAN SYLLABICS II;Lo;0;L;;;;;N;;;;; +1405;CANADIAN SYLLABICS O;Lo;0;L;;;;;N;;;;; +1406;CANADIAN SYLLABICS OO;Lo;0;L;;;;;N;;;;; +1407;CANADIAN SYLLABICS Y-CREE OO;Lo;0;L;;;;;N;;;;; +1408;CANADIAN SYLLABICS CARRIER EE;Lo;0;L;;;;;N;;;;; +1409;CANADIAN SYLLABICS CARRIER I;Lo;0;L;;;;;N;;;;; +140A;CANADIAN SYLLABICS A;Lo;0;L;;;;;N;;;;; +140B;CANADIAN SYLLABICS AA;Lo;0;L;;;;;N;;;;; +140C;CANADIAN SYLLABICS WE;Lo;0;L;;;;;N;;;;; +140D;CANADIAN SYLLABICS WEST-CREE WE;Lo;0;L;;;;;N;;;;; +140E;CANADIAN SYLLABICS WI;Lo;0;L;;;;;N;;;;; +140F;CANADIAN SYLLABICS WEST-CREE WI;Lo;0;L;;;;;N;;;;; +1410;CANADIAN SYLLABICS WII;Lo;0;L;;;;;N;;;;; +1411;CANADIAN SYLLABICS WEST-CREE WII;Lo;0;L;;;;;N;;;;; +1412;CANADIAN SYLLABICS WO;Lo;0;L;;;;;N;;;;; +1413;CANADIAN SYLLABICS WEST-CREE WO;Lo;0;L;;;;;N;;;;; +1414;CANADIAN SYLLABICS WOO;Lo;0;L;;;;;N;;;;; +1415;CANADIAN SYLLABICS WEST-CREE WOO;Lo;0;L;;;;;N;;;;; +1416;CANADIAN SYLLABICS NASKAPI WOO;Lo;0;L;;;;;N;;;;; +1417;CANADIAN SYLLABICS WA;Lo;0;L;;;;;N;;;;; +1418;CANADIAN SYLLABICS WEST-CREE WA;Lo;0;L;;;;;N;;;;; +1419;CANADIAN SYLLABICS WAA;Lo;0;L;;;;;N;;;;; +141A;CANADIAN SYLLABICS WEST-CREE WAA;Lo;0;L;;;;;N;;;;; +141B;CANADIAN SYLLABICS NASKAPI WAA;Lo;0;L;;;;;N;;;;; +141C;CANADIAN SYLLABICS AI;Lo;0;L;;;;;N;;;;; +141D;CANADIAN SYLLABICS Y-CREE W;Lo;0;L;;;;;N;;;;; +141E;CANADIAN SYLLABICS GLOTTAL STOP;Lo;0;L;;;;;N;;;;; +141F;CANADIAN SYLLABICS FINAL ACUTE;Lo;0;L;;;;;N;;;;; +1420;CANADIAN SYLLABICS FINAL GRAVE;Lo;0;L;;;;;N;;;;; +1421;CANADIAN SYLLABICS FINAL BOTTOM HALF RING;Lo;0;L;;;;;N;;;;; +1422;CANADIAN SYLLABICS FINAL TOP HALF RING;Lo;0;L;;;;;N;;;;; +1423;CANADIAN SYLLABICS FINAL RIGHT HALF RING;Lo;0;L;;;;;N;;;;; +1424;CANADIAN SYLLABICS FINAL RING;Lo;0;L;;;;;N;;;;; +1425;CANADIAN SYLLABICS FINAL DOUBLE ACUTE;Lo;0;L;;;;;N;;;;; +1426;CANADIAN SYLLABICS FINAL DOUBLE SHORT VERTICAL STROKES;Lo;0;L;;;;;N;;;;; +1427;CANADIAN SYLLABICS FINAL MIDDLE DOT;Lo;0;L;;;;;N;;;;; +1428;CANADIAN SYLLABICS FINAL SHORT HORIZONTAL STROKE;Lo;0;L;;;;;N;;;;; +1429;CANADIAN SYLLABICS FINAL PLUS;Lo;0;L;;;;;N;;;;; +142A;CANADIAN SYLLABICS FINAL DOWN TACK;Lo;0;L;;;;;N;;;;; +142B;CANADIAN SYLLABICS EN;Lo;0;L;;;;;N;;;;; +142C;CANADIAN SYLLABICS IN;Lo;0;L;;;;;N;;;;; +142D;CANADIAN SYLLABICS ON;Lo;0;L;;;;;N;;;;; +142E;CANADIAN SYLLABICS AN;Lo;0;L;;;;;N;;;;; +142F;CANADIAN SYLLABICS PE;Lo;0;L;;;;;N;;;;; +1430;CANADIAN SYLLABICS PAAI;Lo;0;L;;;;;N;;;;; +1431;CANADIAN SYLLABICS PI;Lo;0;L;;;;;N;;;;; +1432;CANADIAN SYLLABICS PII;Lo;0;L;;;;;N;;;;; +1433;CANADIAN SYLLABICS PO;Lo;0;L;;;;;N;;;;; +1434;CANADIAN SYLLABICS POO;Lo;0;L;;;;;N;;;;; +1435;CANADIAN SYLLABICS Y-CREE POO;Lo;0;L;;;;;N;;;;; +1436;CANADIAN SYLLABICS CARRIER HEE;Lo;0;L;;;;;N;;;;; +1437;CANADIAN SYLLABICS CARRIER HI;Lo;0;L;;;;;N;;;;; +1438;CANADIAN SYLLABICS PA;Lo;0;L;;;;;N;;;;; +1439;CANADIAN SYLLABICS PAA;Lo;0;L;;;;;N;;;;; +143A;CANADIAN SYLLABICS PWE;Lo;0;L;;;;;N;;;;; +143B;CANADIAN SYLLABICS WEST-CREE PWE;Lo;0;L;;;;;N;;;;; +143C;CANADIAN SYLLABICS PWI;Lo;0;L;;;;;N;;;;; +143D;CANADIAN SYLLABICS WEST-CREE PWI;Lo;0;L;;;;;N;;;;; +143E;CANADIAN SYLLABICS PWII;Lo;0;L;;;;;N;;;;; +143F;CANADIAN SYLLABICS WEST-CREE PWII;Lo;0;L;;;;;N;;;;; +1440;CANADIAN SYLLABICS PWO;Lo;0;L;;;;;N;;;;; +1441;CANADIAN SYLLABICS WEST-CREE PWO;Lo;0;L;;;;;N;;;;; +1442;CANADIAN SYLLABICS PWOO;Lo;0;L;;;;;N;;;;; +1443;CANADIAN SYLLABICS WEST-CREE PWOO;Lo;0;L;;;;;N;;;;; +1444;CANADIAN SYLLABICS PWA;Lo;0;L;;;;;N;;;;; +1445;CANADIAN SYLLABICS WEST-CREE PWA;Lo;0;L;;;;;N;;;;; +1446;CANADIAN SYLLABICS PWAA;Lo;0;L;;;;;N;;;;; +1447;CANADIAN SYLLABICS WEST-CREE PWAA;Lo;0;L;;;;;N;;;;; +1448;CANADIAN SYLLABICS Y-CREE PWAA;Lo;0;L;;;;;N;;;;; +1449;CANADIAN SYLLABICS P;Lo;0;L;;;;;N;;;;; +144A;CANADIAN SYLLABICS WEST-CREE P;Lo;0;L;;;;;N;;;;; +144B;CANADIAN SYLLABICS CARRIER H;Lo;0;L;;;;;N;;;;; +144C;CANADIAN SYLLABICS TE;Lo;0;L;;;;;N;;;;; +144D;CANADIAN SYLLABICS TAAI;Lo;0;L;;;;;N;;;;; +144E;CANADIAN SYLLABICS TI;Lo;0;L;;;;;N;;;;; +144F;CANADIAN SYLLABICS TII;Lo;0;L;;;;;N;;;;; +1450;CANADIAN SYLLABICS TO;Lo;0;L;;;;;N;;;;; +1451;CANADIAN SYLLABICS TOO;Lo;0;L;;;;;N;;;;; +1452;CANADIAN SYLLABICS Y-CREE TOO;Lo;0;L;;;;;N;;;;; +1453;CANADIAN SYLLABICS CARRIER DEE;Lo;0;L;;;;;N;;;;; +1454;CANADIAN SYLLABICS CARRIER DI;Lo;0;L;;;;;N;;;;; +1455;CANADIAN SYLLABICS TA;Lo;0;L;;;;;N;;;;; +1456;CANADIAN SYLLABICS TAA;Lo;0;L;;;;;N;;;;; +1457;CANADIAN SYLLABICS TWE;Lo;0;L;;;;;N;;;;; +1458;CANADIAN SYLLABICS WEST-CREE TWE;Lo;0;L;;;;;N;;;;; +1459;CANADIAN SYLLABICS TWI;Lo;0;L;;;;;N;;;;; +145A;CANADIAN SYLLABICS WEST-CREE TWI;Lo;0;L;;;;;N;;;;; +145B;CANADIAN SYLLABICS TWII;Lo;0;L;;;;;N;;;;; +145C;CANADIAN SYLLABICS WEST-CREE TWII;Lo;0;L;;;;;N;;;;; +145D;CANADIAN SYLLABICS TWO;Lo;0;L;;;;;N;;;;; +145E;CANADIAN SYLLABICS WEST-CREE TWO;Lo;0;L;;;;;N;;;;; +145F;CANADIAN SYLLABICS TWOO;Lo;0;L;;;;;N;;;;; +1460;CANADIAN SYLLABICS WEST-CREE TWOO;Lo;0;L;;;;;N;;;;; +1461;CANADIAN SYLLABICS TWA;Lo;0;L;;;;;N;;;;; +1462;CANADIAN SYLLABICS WEST-CREE TWA;Lo;0;L;;;;;N;;;;; +1463;CANADIAN SYLLABICS TWAA;Lo;0;L;;;;;N;;;;; +1464;CANADIAN SYLLABICS WEST-CREE TWAA;Lo;0;L;;;;;N;;;;; +1465;CANADIAN SYLLABICS NASKAPI TWAA;Lo;0;L;;;;;N;;;;; +1466;CANADIAN SYLLABICS T;Lo;0;L;;;;;N;;;;; +1467;CANADIAN SYLLABICS TTE;Lo;0;L;;;;;N;;;;; +1468;CANADIAN SYLLABICS TTI;Lo;0;L;;;;;N;;;;; +1469;CANADIAN SYLLABICS TTO;Lo;0;L;;;;;N;;;;; +146A;CANADIAN SYLLABICS TTA;Lo;0;L;;;;;N;;;;; +146B;CANADIAN SYLLABICS KE;Lo;0;L;;;;;N;;;;; +146C;CANADIAN SYLLABICS KAAI;Lo;0;L;;;;;N;;;;; +146D;CANADIAN SYLLABICS KI;Lo;0;L;;;;;N;;;;; +146E;CANADIAN SYLLABICS KII;Lo;0;L;;;;;N;;;;; +146F;CANADIAN SYLLABICS KO;Lo;0;L;;;;;N;;;;; +1470;CANADIAN SYLLABICS KOO;Lo;0;L;;;;;N;;;;; +1471;CANADIAN SYLLABICS Y-CREE KOO;Lo;0;L;;;;;N;;;;; +1472;CANADIAN SYLLABICS KA;Lo;0;L;;;;;N;;;;; +1473;CANADIAN SYLLABICS KAA;Lo;0;L;;;;;N;;;;; +1474;CANADIAN SYLLABICS KWE;Lo;0;L;;;;;N;;;;; +1475;CANADIAN SYLLABICS WEST-CREE KWE;Lo;0;L;;;;;N;;;;; +1476;CANADIAN SYLLABICS KWI;Lo;0;L;;;;;N;;;;; +1477;CANADIAN SYLLABICS WEST-CREE KWI;Lo;0;L;;;;;N;;;;; +1478;CANADIAN SYLLABICS KWII;Lo;0;L;;;;;N;;;;; +1479;CANADIAN SYLLABICS WEST-CREE KWII;Lo;0;L;;;;;N;;;;; +147A;CANADIAN SYLLABICS KWO;Lo;0;L;;;;;N;;;;; +147B;CANADIAN SYLLABICS WEST-CREE KWO;Lo;0;L;;;;;N;;;;; +147C;CANADIAN SYLLABICS KWOO;Lo;0;L;;;;;N;;;;; +147D;CANADIAN SYLLABICS WEST-CREE KWOO;Lo;0;L;;;;;N;;;;; +147E;CANADIAN SYLLABICS KWA;Lo;0;L;;;;;N;;;;; +147F;CANADIAN SYLLABICS WEST-CREE KWA;Lo;0;L;;;;;N;;;;; +1480;CANADIAN SYLLABICS KWAA;Lo;0;L;;;;;N;;;;; +1481;CANADIAN SYLLABICS WEST-CREE KWAA;Lo;0;L;;;;;N;;;;; +1482;CANADIAN SYLLABICS NASKAPI KWAA;Lo;0;L;;;;;N;;;;; +1483;CANADIAN SYLLABICS K;Lo;0;L;;;;;N;;;;; +1484;CANADIAN SYLLABICS KW;Lo;0;L;;;;;N;;;;; +1485;CANADIAN SYLLABICS SOUTH-SLAVEY KEH;Lo;0;L;;;;;N;;;;; +1486;CANADIAN SYLLABICS SOUTH-SLAVEY KIH;Lo;0;L;;;;;N;;;;; +1487;CANADIAN SYLLABICS SOUTH-SLAVEY KOH;Lo;0;L;;;;;N;;;;; +1488;CANADIAN SYLLABICS SOUTH-SLAVEY KAH;Lo;0;L;;;;;N;;;;; +1489;CANADIAN SYLLABICS CE;Lo;0;L;;;;;N;;;;; +148A;CANADIAN SYLLABICS CAAI;Lo;0;L;;;;;N;;;;; +148B;CANADIAN SYLLABICS CI;Lo;0;L;;;;;N;;;;; +148C;CANADIAN SYLLABICS CII;Lo;0;L;;;;;N;;;;; +148D;CANADIAN SYLLABICS CO;Lo;0;L;;;;;N;;;;; +148E;CANADIAN SYLLABICS COO;Lo;0;L;;;;;N;;;;; +148F;CANADIAN SYLLABICS Y-CREE COO;Lo;0;L;;;;;N;;;;; +1490;CANADIAN SYLLABICS CA;Lo;0;L;;;;;N;;;;; +1491;CANADIAN SYLLABICS CAA;Lo;0;L;;;;;N;;;;; +1492;CANADIAN SYLLABICS CWE;Lo;0;L;;;;;N;;;;; +1493;CANADIAN SYLLABICS WEST-CREE CWE;Lo;0;L;;;;;N;;;;; +1494;CANADIAN SYLLABICS CWI;Lo;0;L;;;;;N;;;;; +1495;CANADIAN SYLLABICS WEST-CREE CWI;Lo;0;L;;;;;N;;;;; +1496;CANADIAN SYLLABICS CWII;Lo;0;L;;;;;N;;;;; +1497;CANADIAN SYLLABICS WEST-CREE CWII;Lo;0;L;;;;;N;;;;; +1498;CANADIAN SYLLABICS CWO;Lo;0;L;;;;;N;;;;; +1499;CANADIAN SYLLABICS WEST-CREE CWO;Lo;0;L;;;;;N;;;;; +149A;CANADIAN SYLLABICS CWOO;Lo;0;L;;;;;N;;;;; +149B;CANADIAN SYLLABICS WEST-CREE CWOO;Lo;0;L;;;;;N;;;;; +149C;CANADIAN SYLLABICS CWA;Lo;0;L;;;;;N;;;;; +149D;CANADIAN SYLLABICS WEST-CREE CWA;Lo;0;L;;;;;N;;;;; +149E;CANADIAN SYLLABICS CWAA;Lo;0;L;;;;;N;;;;; +149F;CANADIAN SYLLABICS WEST-CREE CWAA;Lo;0;L;;;;;N;;;;; +14A0;CANADIAN SYLLABICS NASKAPI CWAA;Lo;0;L;;;;;N;;;;; +14A1;CANADIAN SYLLABICS C;Lo;0;L;;;;;N;;;;; +14A2;CANADIAN SYLLABICS SAYISI TH;Lo;0;L;;;;;N;;;;; +14A3;CANADIAN SYLLABICS ME;Lo;0;L;;;;;N;;;;; +14A4;CANADIAN SYLLABICS MAAI;Lo;0;L;;;;;N;;;;; +14A5;CANADIAN SYLLABICS MI;Lo;0;L;;;;;N;;;;; +14A6;CANADIAN SYLLABICS MII;Lo;0;L;;;;;N;;;;; +14A7;CANADIAN SYLLABICS MO;Lo;0;L;;;;;N;;;;; +14A8;CANADIAN SYLLABICS MOO;Lo;0;L;;;;;N;;;;; +14A9;CANADIAN SYLLABICS Y-CREE MOO;Lo;0;L;;;;;N;;;;; +14AA;CANADIAN SYLLABICS MA;Lo;0;L;;;;;N;;;;; +14AB;CANADIAN SYLLABICS MAA;Lo;0;L;;;;;N;;;;; +14AC;CANADIAN SYLLABICS MWE;Lo;0;L;;;;;N;;;;; +14AD;CANADIAN SYLLABICS WEST-CREE MWE;Lo;0;L;;;;;N;;;;; +14AE;CANADIAN SYLLABICS MWI;Lo;0;L;;;;;N;;;;; +14AF;CANADIAN SYLLABICS WEST-CREE MWI;Lo;0;L;;;;;N;;;;; +14B0;CANADIAN SYLLABICS MWII;Lo;0;L;;;;;N;;;;; +14B1;CANADIAN SYLLABICS WEST-CREE MWII;Lo;0;L;;;;;N;;;;; +14B2;CANADIAN SYLLABICS MWO;Lo;0;L;;;;;N;;;;; +14B3;CANADIAN SYLLABICS WEST-CREE MWO;Lo;0;L;;;;;N;;;;; +14B4;CANADIAN SYLLABICS MWOO;Lo;0;L;;;;;N;;;;; +14B5;CANADIAN SYLLABICS WEST-CREE MWOO;Lo;0;L;;;;;N;;;;; +14B6;CANADIAN SYLLABICS MWA;Lo;0;L;;;;;N;;;;; +14B7;CANADIAN SYLLABICS WEST-CREE MWA;Lo;0;L;;;;;N;;;;; +14B8;CANADIAN SYLLABICS MWAA;Lo;0;L;;;;;N;;;;; +14B9;CANADIAN SYLLABICS WEST-CREE MWAA;Lo;0;L;;;;;N;;;;; +14BA;CANADIAN SYLLABICS NASKAPI MWAA;Lo;0;L;;;;;N;;;;; +14BB;CANADIAN SYLLABICS M;Lo;0;L;;;;;N;;;;; +14BC;CANADIAN SYLLABICS WEST-CREE M;Lo;0;L;;;;;N;;;;; +14BD;CANADIAN SYLLABICS MH;Lo;0;L;;;;;N;;;;; +14BE;CANADIAN SYLLABICS ATHAPASCAN M;Lo;0;L;;;;;N;;;;; +14BF;CANADIAN SYLLABICS SAYISI M;Lo;0;L;;;;;N;;;;; +14C0;CANADIAN SYLLABICS NE;Lo;0;L;;;;;N;;;;; +14C1;CANADIAN SYLLABICS NAAI;Lo;0;L;;;;;N;;;;; +14C2;CANADIAN SYLLABICS NI;Lo;0;L;;;;;N;;;;; +14C3;CANADIAN SYLLABICS NII;Lo;0;L;;;;;N;;;;; +14C4;CANADIAN SYLLABICS NO;Lo;0;L;;;;;N;;;;; +14C5;CANADIAN SYLLABICS NOO;Lo;0;L;;;;;N;;;;; +14C6;CANADIAN SYLLABICS Y-CREE NOO;Lo;0;L;;;;;N;;;;; +14C7;CANADIAN SYLLABICS NA;Lo;0;L;;;;;N;;;;; +14C8;CANADIAN SYLLABICS NAA;Lo;0;L;;;;;N;;;;; +14C9;CANADIAN SYLLABICS NWE;Lo;0;L;;;;;N;;;;; +14CA;CANADIAN SYLLABICS WEST-CREE NWE;Lo;0;L;;;;;N;;;;; +14CB;CANADIAN SYLLABICS NWA;Lo;0;L;;;;;N;;;;; +14CC;CANADIAN SYLLABICS WEST-CREE NWA;Lo;0;L;;;;;N;;;;; +14CD;CANADIAN SYLLABICS NWAA;Lo;0;L;;;;;N;;;;; +14CE;CANADIAN SYLLABICS WEST-CREE NWAA;Lo;0;L;;;;;N;;;;; +14CF;CANADIAN SYLLABICS NASKAPI NWAA;Lo;0;L;;;;;N;;;;; +14D0;CANADIAN SYLLABICS N;Lo;0;L;;;;;N;;;;; +14D1;CANADIAN SYLLABICS CARRIER NG;Lo;0;L;;;;;N;;;;; +14D2;CANADIAN SYLLABICS NH;Lo;0;L;;;;;N;;;;; +14D3;CANADIAN SYLLABICS LE;Lo;0;L;;;;;N;;;;; +14D4;CANADIAN SYLLABICS LAAI;Lo;0;L;;;;;N;;;;; +14D5;CANADIAN SYLLABICS LI;Lo;0;L;;;;;N;;;;; +14D6;CANADIAN SYLLABICS LII;Lo;0;L;;;;;N;;;;; +14D7;CANADIAN SYLLABICS LO;Lo;0;L;;;;;N;;;;; +14D8;CANADIAN SYLLABICS LOO;Lo;0;L;;;;;N;;;;; +14D9;CANADIAN SYLLABICS Y-CREE LOO;Lo;0;L;;;;;N;;;;; +14DA;CANADIAN SYLLABICS LA;Lo;0;L;;;;;N;;;;; +14DB;CANADIAN SYLLABICS LAA;Lo;0;L;;;;;N;;;;; +14DC;CANADIAN SYLLABICS LWE;Lo;0;L;;;;;N;;;;; +14DD;CANADIAN SYLLABICS WEST-CREE LWE;Lo;0;L;;;;;N;;;;; +14DE;CANADIAN SYLLABICS LWI;Lo;0;L;;;;;N;;;;; +14DF;CANADIAN SYLLABICS WEST-CREE LWI;Lo;0;L;;;;;N;;;;; +14E0;CANADIAN SYLLABICS LWII;Lo;0;L;;;;;N;;;;; +14E1;CANADIAN SYLLABICS WEST-CREE LWII;Lo;0;L;;;;;N;;;;; +14E2;CANADIAN SYLLABICS LWO;Lo;0;L;;;;;N;;;;; +14E3;CANADIAN SYLLABICS WEST-CREE LWO;Lo;0;L;;;;;N;;;;; +14E4;CANADIAN SYLLABICS LWOO;Lo;0;L;;;;;N;;;;; +14E5;CANADIAN SYLLABICS WEST-CREE LWOO;Lo;0;L;;;;;N;;;;; +14E6;CANADIAN SYLLABICS LWA;Lo;0;L;;;;;N;;;;; +14E7;CANADIAN SYLLABICS WEST-CREE LWA;Lo;0;L;;;;;N;;;;; +14E8;CANADIAN SYLLABICS LWAA;Lo;0;L;;;;;N;;;;; +14E9;CANADIAN SYLLABICS WEST-CREE LWAA;Lo;0;L;;;;;N;;;;; +14EA;CANADIAN SYLLABICS L;Lo;0;L;;;;;N;;;;; +14EB;CANADIAN SYLLABICS WEST-CREE L;Lo;0;L;;;;;N;;;;; +14EC;CANADIAN SYLLABICS MEDIAL L;Lo;0;L;;;;;N;;;;; +14ED;CANADIAN SYLLABICS SE;Lo;0;L;;;;;N;;;;; +14EE;CANADIAN SYLLABICS SAAI;Lo;0;L;;;;;N;;;;; +14EF;CANADIAN SYLLABICS SI;Lo;0;L;;;;;N;;;;; +14F0;CANADIAN SYLLABICS SII;Lo;0;L;;;;;N;;;;; +14F1;CANADIAN SYLLABICS SO;Lo;0;L;;;;;N;;;;; +14F2;CANADIAN SYLLABICS SOO;Lo;0;L;;;;;N;;;;; +14F3;CANADIAN SYLLABICS Y-CREE SOO;Lo;0;L;;;;;N;;;;; +14F4;CANADIAN SYLLABICS SA;Lo;0;L;;;;;N;;;;; +14F5;CANADIAN SYLLABICS SAA;Lo;0;L;;;;;N;;;;; +14F6;CANADIAN SYLLABICS SWE;Lo;0;L;;;;;N;;;;; +14F7;CANADIAN SYLLABICS WEST-CREE SWE;Lo;0;L;;;;;N;;;;; +14F8;CANADIAN SYLLABICS SWI;Lo;0;L;;;;;N;;;;; +14F9;CANADIAN SYLLABICS WEST-CREE SWI;Lo;0;L;;;;;N;;;;; +14FA;CANADIAN SYLLABICS SWII;Lo;0;L;;;;;N;;;;; +14FB;CANADIAN SYLLABICS WEST-CREE SWII;Lo;0;L;;;;;N;;;;; +14FC;CANADIAN SYLLABICS SWO;Lo;0;L;;;;;N;;;;; +14FD;CANADIAN SYLLABICS WEST-CREE SWO;Lo;0;L;;;;;N;;;;; +14FE;CANADIAN SYLLABICS SWOO;Lo;0;L;;;;;N;;;;; +14FF;CANADIAN SYLLABICS WEST-CREE SWOO;Lo;0;L;;;;;N;;;;; +1500;CANADIAN SYLLABICS SWA;Lo;0;L;;;;;N;;;;; +1501;CANADIAN SYLLABICS WEST-CREE SWA;Lo;0;L;;;;;N;;;;; +1502;CANADIAN SYLLABICS SWAA;Lo;0;L;;;;;N;;;;; +1503;CANADIAN SYLLABICS WEST-CREE SWAA;Lo;0;L;;;;;N;;;;; +1504;CANADIAN SYLLABICS NASKAPI SWAA;Lo;0;L;;;;;N;;;;; +1505;CANADIAN SYLLABICS S;Lo;0;L;;;;;N;;;;; +1506;CANADIAN SYLLABICS ATHAPASCAN S;Lo;0;L;;;;;N;;;;; +1507;CANADIAN SYLLABICS SW;Lo;0;L;;;;;N;;;;; +1508;CANADIAN SYLLABICS BLACKFOOT S;Lo;0;L;;;;;N;;;;; +1509;CANADIAN SYLLABICS MOOSE-CREE SK;Lo;0;L;;;;;N;;;;; +150A;CANADIAN SYLLABICS NASKAPI SKW;Lo;0;L;;;;;N;;;;; +150B;CANADIAN SYLLABICS NASKAPI S-W;Lo;0;L;;;;;N;;;;; +150C;CANADIAN SYLLABICS NASKAPI SPWA;Lo;0;L;;;;;N;;;;; +150D;CANADIAN SYLLABICS NASKAPI STWA;Lo;0;L;;;;;N;;;;; +150E;CANADIAN SYLLABICS NASKAPI SKWA;Lo;0;L;;;;;N;;;;; +150F;CANADIAN SYLLABICS NASKAPI SCWA;Lo;0;L;;;;;N;;;;; +1510;CANADIAN SYLLABICS SHE;Lo;0;L;;;;;N;;;;; +1511;CANADIAN SYLLABICS SHI;Lo;0;L;;;;;N;;;;; +1512;CANADIAN SYLLABICS SHII;Lo;0;L;;;;;N;;;;; +1513;CANADIAN SYLLABICS SHO;Lo;0;L;;;;;N;;;;; +1514;CANADIAN SYLLABICS SHOO;Lo;0;L;;;;;N;;;;; +1515;CANADIAN SYLLABICS SHA;Lo;0;L;;;;;N;;;;; +1516;CANADIAN SYLLABICS SHAA;Lo;0;L;;;;;N;;;;; +1517;CANADIAN SYLLABICS SHWE;Lo;0;L;;;;;N;;;;; +1518;CANADIAN SYLLABICS WEST-CREE SHWE;Lo;0;L;;;;;N;;;;; +1519;CANADIAN SYLLABICS SHWI;Lo;0;L;;;;;N;;;;; +151A;CANADIAN SYLLABICS WEST-CREE SHWI;Lo;0;L;;;;;N;;;;; +151B;CANADIAN SYLLABICS SHWII;Lo;0;L;;;;;N;;;;; +151C;CANADIAN SYLLABICS WEST-CREE SHWII;Lo;0;L;;;;;N;;;;; +151D;CANADIAN SYLLABICS SHWO;Lo;0;L;;;;;N;;;;; +151E;CANADIAN SYLLABICS WEST-CREE SHWO;Lo;0;L;;;;;N;;;;; +151F;CANADIAN SYLLABICS SHWOO;Lo;0;L;;;;;N;;;;; +1520;CANADIAN SYLLABICS WEST-CREE SHWOO;Lo;0;L;;;;;N;;;;; +1521;CANADIAN SYLLABICS SHWA;Lo;0;L;;;;;N;;;;; +1522;CANADIAN SYLLABICS WEST-CREE SHWA;Lo;0;L;;;;;N;;;;; +1523;CANADIAN SYLLABICS SHWAA;Lo;0;L;;;;;N;;;;; +1524;CANADIAN SYLLABICS WEST-CREE SHWAA;Lo;0;L;;;;;N;;;;; +1525;CANADIAN SYLLABICS SH;Lo;0;L;;;;;N;;;;; +1526;CANADIAN SYLLABICS YE;Lo;0;L;;;;;N;;;;; +1527;CANADIAN SYLLABICS YAAI;Lo;0;L;;;;;N;;;;; +1528;CANADIAN SYLLABICS YI;Lo;0;L;;;;;N;;;;; +1529;CANADIAN SYLLABICS YII;Lo;0;L;;;;;N;;;;; +152A;CANADIAN SYLLABICS YO;Lo;0;L;;;;;N;;;;; +152B;CANADIAN SYLLABICS YOO;Lo;0;L;;;;;N;;;;; +152C;CANADIAN SYLLABICS Y-CREE YOO;Lo;0;L;;;;;N;;;;; +152D;CANADIAN SYLLABICS YA;Lo;0;L;;;;;N;;;;; +152E;CANADIAN SYLLABICS YAA;Lo;0;L;;;;;N;;;;; +152F;CANADIAN SYLLABICS YWE;Lo;0;L;;;;;N;;;;; +1530;CANADIAN SYLLABICS WEST-CREE YWE;Lo;0;L;;;;;N;;;;; +1531;CANADIAN SYLLABICS YWI;Lo;0;L;;;;;N;;;;; +1532;CANADIAN SYLLABICS WEST-CREE YWI;Lo;0;L;;;;;N;;;;; +1533;CANADIAN SYLLABICS YWII;Lo;0;L;;;;;N;;;;; +1534;CANADIAN SYLLABICS WEST-CREE YWII;Lo;0;L;;;;;N;;;;; +1535;CANADIAN SYLLABICS YWO;Lo;0;L;;;;;N;;;;; +1536;CANADIAN SYLLABICS WEST-CREE YWO;Lo;0;L;;;;;N;;;;; +1537;CANADIAN SYLLABICS YWOO;Lo;0;L;;;;;N;;;;; +1538;CANADIAN SYLLABICS WEST-CREE YWOO;Lo;0;L;;;;;N;;;;; +1539;CANADIAN SYLLABICS YWA;Lo;0;L;;;;;N;;;;; +153A;CANADIAN SYLLABICS WEST-CREE YWA;Lo;0;L;;;;;N;;;;; +153B;CANADIAN SYLLABICS YWAA;Lo;0;L;;;;;N;;;;; +153C;CANADIAN SYLLABICS WEST-CREE YWAA;Lo;0;L;;;;;N;;;;; +153D;CANADIAN SYLLABICS NASKAPI YWAA;Lo;0;L;;;;;N;;;;; +153E;CANADIAN SYLLABICS Y;Lo;0;L;;;;;N;;;;; +153F;CANADIAN SYLLABICS BIBLE-CREE Y;Lo;0;L;;;;;N;;;;; +1540;CANADIAN SYLLABICS WEST-CREE Y;Lo;0;L;;;;;N;;;;; +1541;CANADIAN SYLLABICS SAYISI YI;Lo;0;L;;;;;N;;;;; +1542;CANADIAN SYLLABICS RE;Lo;0;L;;;;;N;;;;; +1543;CANADIAN SYLLABICS R-CREE RE;Lo;0;L;;;;;N;;;;; +1544;CANADIAN SYLLABICS WEST-CREE LE;Lo;0;L;;;;;N;;;;; +1545;CANADIAN SYLLABICS RAAI;Lo;0;L;;;;;N;;;;; +1546;CANADIAN SYLLABICS RI;Lo;0;L;;;;;N;;;;; +1547;CANADIAN SYLLABICS RII;Lo;0;L;;;;;N;;;;; +1548;CANADIAN SYLLABICS RO;Lo;0;L;;;;;N;;;;; +1549;CANADIAN SYLLABICS ROO;Lo;0;L;;;;;N;;;;; +154A;CANADIAN SYLLABICS WEST-CREE LO;Lo;0;L;;;;;N;;;;; +154B;CANADIAN SYLLABICS RA;Lo;0;L;;;;;N;;;;; +154C;CANADIAN SYLLABICS RAA;Lo;0;L;;;;;N;;;;; +154D;CANADIAN SYLLABICS WEST-CREE LA;Lo;0;L;;;;;N;;;;; +154E;CANADIAN SYLLABICS RWAA;Lo;0;L;;;;;N;;;;; +154F;CANADIAN SYLLABICS WEST-CREE RWAA;Lo;0;L;;;;;N;;;;; +1550;CANADIAN SYLLABICS R;Lo;0;L;;;;;N;;;;; +1551;CANADIAN SYLLABICS WEST-CREE R;Lo;0;L;;;;;N;;;;; +1552;CANADIAN SYLLABICS MEDIAL R;Lo;0;L;;;;;N;;;;; +1553;CANADIAN SYLLABICS FE;Lo;0;L;;;;;N;;;;; +1554;CANADIAN SYLLABICS FAAI;Lo;0;L;;;;;N;;;;; +1555;CANADIAN SYLLABICS FI;Lo;0;L;;;;;N;;;;; +1556;CANADIAN SYLLABICS FII;Lo;0;L;;;;;N;;;;; +1557;CANADIAN SYLLABICS FO;Lo;0;L;;;;;N;;;;; +1558;CANADIAN SYLLABICS FOO;Lo;0;L;;;;;N;;;;; +1559;CANADIAN SYLLABICS FA;Lo;0;L;;;;;N;;;;; +155A;CANADIAN SYLLABICS FAA;Lo;0;L;;;;;N;;;;; +155B;CANADIAN SYLLABICS FWAA;Lo;0;L;;;;;N;;;;; +155C;CANADIAN SYLLABICS WEST-CREE FWAA;Lo;0;L;;;;;N;;;;; +155D;CANADIAN SYLLABICS F;Lo;0;L;;;;;N;;;;; +155E;CANADIAN SYLLABICS THE;Lo;0;L;;;;;N;;;;; +155F;CANADIAN SYLLABICS N-CREE THE;Lo;0;L;;;;;N;;;;; +1560;CANADIAN SYLLABICS THI;Lo;0;L;;;;;N;;;;; +1561;CANADIAN SYLLABICS N-CREE THI;Lo;0;L;;;;;N;;;;; +1562;CANADIAN SYLLABICS THII;Lo;0;L;;;;;N;;;;; +1563;CANADIAN SYLLABICS N-CREE THII;Lo;0;L;;;;;N;;;;; +1564;CANADIAN SYLLABICS THO;Lo;0;L;;;;;N;;;;; +1565;CANADIAN SYLLABICS THOO;Lo;0;L;;;;;N;;;;; +1566;CANADIAN SYLLABICS THA;Lo;0;L;;;;;N;;;;; +1567;CANADIAN SYLLABICS THAA;Lo;0;L;;;;;N;;;;; +1568;CANADIAN SYLLABICS THWAA;Lo;0;L;;;;;N;;;;; +1569;CANADIAN SYLLABICS WEST-CREE THWAA;Lo;0;L;;;;;N;;;;; +156A;CANADIAN SYLLABICS TH;Lo;0;L;;;;;N;;;;; +156B;CANADIAN SYLLABICS TTHE;Lo;0;L;;;;;N;;;;; +156C;CANADIAN SYLLABICS TTHI;Lo;0;L;;;;;N;;;;; +156D;CANADIAN SYLLABICS TTHO;Lo;0;L;;;;;N;;;;; +156E;CANADIAN SYLLABICS TTHA;Lo;0;L;;;;;N;;;;; +156F;CANADIAN SYLLABICS TTH;Lo;0;L;;;;;N;;;;; +1570;CANADIAN SYLLABICS TYE;Lo;0;L;;;;;N;;;;; +1571;CANADIAN SYLLABICS TYI;Lo;0;L;;;;;N;;;;; +1572;CANADIAN SYLLABICS TYO;Lo;0;L;;;;;N;;;;; +1573;CANADIAN SYLLABICS TYA;Lo;0;L;;;;;N;;;;; +1574;CANADIAN SYLLABICS NUNAVIK HE;Lo;0;L;;;;;N;;;;; +1575;CANADIAN SYLLABICS NUNAVIK HI;Lo;0;L;;;;;N;;;;; +1576;CANADIAN SYLLABICS NUNAVIK HII;Lo;0;L;;;;;N;;;;; +1577;CANADIAN SYLLABICS NUNAVIK HO;Lo;0;L;;;;;N;;;;; +1578;CANADIAN SYLLABICS NUNAVIK HOO;Lo;0;L;;;;;N;;;;; +1579;CANADIAN SYLLABICS NUNAVIK HA;Lo;0;L;;;;;N;;;;; +157A;CANADIAN SYLLABICS NUNAVIK HAA;Lo;0;L;;;;;N;;;;; +157B;CANADIAN SYLLABICS NUNAVIK H;Lo;0;L;;;;;N;;;;; +157C;CANADIAN SYLLABICS NUNAVUT H;Lo;0;L;;;;;N;;;;; +157D;CANADIAN SYLLABICS HK;Lo;0;L;;;;;N;;;;; +157E;CANADIAN SYLLABICS QAAI;Lo;0;L;;;;;N;;;;; +157F;CANADIAN SYLLABICS QI;Lo;0;L;;;;;N;;;;; +1580;CANADIAN SYLLABICS QII;Lo;0;L;;;;;N;;;;; +1581;CANADIAN SYLLABICS QO;Lo;0;L;;;;;N;;;;; +1582;CANADIAN SYLLABICS QOO;Lo;0;L;;;;;N;;;;; +1583;CANADIAN SYLLABICS QA;Lo;0;L;;;;;N;;;;; +1584;CANADIAN SYLLABICS QAA;Lo;0;L;;;;;N;;;;; +1585;CANADIAN SYLLABICS Q;Lo;0;L;;;;;N;;;;; +1586;CANADIAN SYLLABICS TLHE;Lo;0;L;;;;;N;;;;; +1587;CANADIAN SYLLABICS TLHI;Lo;0;L;;;;;N;;;;; +1588;CANADIAN SYLLABICS TLHO;Lo;0;L;;;;;N;;;;; +1589;CANADIAN SYLLABICS TLHA;Lo;0;L;;;;;N;;;;; +158A;CANADIAN SYLLABICS WEST-CREE RE;Lo;0;L;;;;;N;;;;; +158B;CANADIAN SYLLABICS WEST-CREE RI;Lo;0;L;;;;;N;;;;; +158C;CANADIAN SYLLABICS WEST-CREE RO;Lo;0;L;;;;;N;;;;; +158D;CANADIAN SYLLABICS WEST-CREE RA;Lo;0;L;;;;;N;;;;; +158E;CANADIAN SYLLABICS NGAAI;Lo;0;L;;;;;N;;;;; +158F;CANADIAN SYLLABICS NGI;Lo;0;L;;;;;N;;;;; +1590;CANADIAN SYLLABICS NGII;Lo;0;L;;;;;N;;;;; +1591;CANADIAN SYLLABICS NGO;Lo;0;L;;;;;N;;;;; +1592;CANADIAN SYLLABICS NGOO;Lo;0;L;;;;;N;;;;; +1593;CANADIAN SYLLABICS NGA;Lo;0;L;;;;;N;;;;; +1594;CANADIAN SYLLABICS NGAA;Lo;0;L;;;;;N;;;;; +1595;CANADIAN SYLLABICS NG;Lo;0;L;;;;;N;;;;; +1596;CANADIAN SYLLABICS NNG;Lo;0;L;;;;;N;;;;; +1597;CANADIAN SYLLABICS SAYISI SHE;Lo;0;L;;;;;N;;;;; +1598;CANADIAN SYLLABICS SAYISI SHI;Lo;0;L;;;;;N;;;;; +1599;CANADIAN SYLLABICS SAYISI SHO;Lo;0;L;;;;;N;;;;; +159A;CANADIAN SYLLABICS SAYISI SHA;Lo;0;L;;;;;N;;;;; +159B;CANADIAN SYLLABICS WOODS-CREE THE;Lo;0;L;;;;;N;;;;; +159C;CANADIAN SYLLABICS WOODS-CREE THI;Lo;0;L;;;;;N;;;;; +159D;CANADIAN SYLLABICS WOODS-CREE THO;Lo;0;L;;;;;N;;;;; +159E;CANADIAN SYLLABICS WOODS-CREE THA;Lo;0;L;;;;;N;;;;; +159F;CANADIAN SYLLABICS WOODS-CREE TH;Lo;0;L;;;;;N;;;;; +15A0;CANADIAN SYLLABICS LHI;Lo;0;L;;;;;N;;;;; +15A1;CANADIAN SYLLABICS LHII;Lo;0;L;;;;;N;;;;; +15A2;CANADIAN SYLLABICS LHO;Lo;0;L;;;;;N;;;;; +15A3;CANADIAN SYLLABICS LHOO;Lo;0;L;;;;;N;;;;; +15A4;CANADIAN SYLLABICS LHA;Lo;0;L;;;;;N;;;;; +15A5;CANADIAN SYLLABICS LHAA;Lo;0;L;;;;;N;;;;; +15A6;CANADIAN SYLLABICS LH;Lo;0;L;;;;;N;;;;; +15A7;CANADIAN SYLLABICS TH-CREE THE;Lo;0;L;;;;;N;;;;; +15A8;CANADIAN SYLLABICS TH-CREE THI;Lo;0;L;;;;;N;;;;; +15A9;CANADIAN SYLLABICS TH-CREE THII;Lo;0;L;;;;;N;;;;; +15AA;CANADIAN SYLLABICS TH-CREE THO;Lo;0;L;;;;;N;;;;; +15AB;CANADIAN SYLLABICS TH-CREE THOO;Lo;0;L;;;;;N;;;;; +15AC;CANADIAN SYLLABICS TH-CREE THA;Lo;0;L;;;;;N;;;;; +15AD;CANADIAN SYLLABICS TH-CREE THAA;Lo;0;L;;;;;N;;;;; +15AE;CANADIAN SYLLABICS TH-CREE TH;Lo;0;L;;;;;N;;;;; +15AF;CANADIAN SYLLABICS AIVILIK B;Lo;0;L;;;;;N;;;;; +15B0;CANADIAN SYLLABICS BLACKFOOT E;Lo;0;L;;;;;N;;;;; +15B1;CANADIAN SYLLABICS BLACKFOOT I;Lo;0;L;;;;;N;;;;; +15B2;CANADIAN SYLLABICS BLACKFOOT O;Lo;0;L;;;;;N;;;;; +15B3;CANADIAN SYLLABICS BLACKFOOT A;Lo;0;L;;;;;N;;;;; +15B4;CANADIAN SYLLABICS BLACKFOOT WE;Lo;0;L;;;;;N;;;;; +15B5;CANADIAN SYLLABICS BLACKFOOT WI;Lo;0;L;;;;;N;;;;; +15B6;CANADIAN SYLLABICS BLACKFOOT WO;Lo;0;L;;;;;N;;;;; +15B7;CANADIAN SYLLABICS BLACKFOOT WA;Lo;0;L;;;;;N;;;;; +15B8;CANADIAN SYLLABICS BLACKFOOT NE;Lo;0;L;;;;;N;;;;; +15B9;CANADIAN SYLLABICS BLACKFOOT NI;Lo;0;L;;;;;N;;;;; +15BA;CANADIAN SYLLABICS BLACKFOOT NO;Lo;0;L;;;;;N;;;;; +15BB;CANADIAN SYLLABICS BLACKFOOT NA;Lo;0;L;;;;;N;;;;; +15BC;CANADIAN SYLLABICS BLACKFOOT KE;Lo;0;L;;;;;N;;;;; +15BD;CANADIAN SYLLABICS BLACKFOOT KI;Lo;0;L;;;;;N;;;;; +15BE;CANADIAN SYLLABICS BLACKFOOT KO;Lo;0;L;;;;;N;;;;; +15BF;CANADIAN SYLLABICS BLACKFOOT KA;Lo;0;L;;;;;N;;;;; +15C0;CANADIAN SYLLABICS SAYISI HE;Lo;0;L;;;;;N;;;;; +15C1;CANADIAN SYLLABICS SAYISI HI;Lo;0;L;;;;;N;;;;; +15C2;CANADIAN SYLLABICS SAYISI HO;Lo;0;L;;;;;N;;;;; +15C3;CANADIAN SYLLABICS SAYISI HA;Lo;0;L;;;;;N;;;;; +15C4;CANADIAN SYLLABICS CARRIER GHU;Lo;0;L;;;;;N;;;;; +15C5;CANADIAN SYLLABICS CARRIER GHO;Lo;0;L;;;;;N;;;;; +15C6;CANADIAN SYLLABICS CARRIER GHE;Lo;0;L;;;;;N;;;;; +15C7;CANADIAN SYLLABICS CARRIER GHEE;Lo;0;L;;;;;N;;;;; +15C8;CANADIAN SYLLABICS CARRIER GHI;Lo;0;L;;;;;N;;;;; +15C9;CANADIAN SYLLABICS CARRIER GHA;Lo;0;L;;;;;N;;;;; +15CA;CANADIAN SYLLABICS CARRIER RU;Lo;0;L;;;;;N;;;;; +15CB;CANADIAN SYLLABICS CARRIER RO;Lo;0;L;;;;;N;;;;; +15CC;CANADIAN SYLLABICS CARRIER RE;Lo;0;L;;;;;N;;;;; +15CD;CANADIAN SYLLABICS CARRIER REE;Lo;0;L;;;;;N;;;;; +15CE;CANADIAN SYLLABICS CARRIER RI;Lo;0;L;;;;;N;;;;; +15CF;CANADIAN SYLLABICS CARRIER RA;Lo;0;L;;;;;N;;;;; +15D0;CANADIAN SYLLABICS CARRIER WU;Lo;0;L;;;;;N;;;;; +15D1;CANADIAN SYLLABICS CARRIER WO;Lo;0;L;;;;;N;;;;; +15D2;CANADIAN SYLLABICS CARRIER WE;Lo;0;L;;;;;N;;;;; +15D3;CANADIAN SYLLABICS CARRIER WEE;Lo;0;L;;;;;N;;;;; +15D4;CANADIAN SYLLABICS CARRIER WI;Lo;0;L;;;;;N;;;;; +15D5;CANADIAN SYLLABICS CARRIER WA;Lo;0;L;;;;;N;;;;; +15D6;CANADIAN SYLLABICS CARRIER HWU;Lo;0;L;;;;;N;;;;; +15D7;CANADIAN SYLLABICS CARRIER HWO;Lo;0;L;;;;;N;;;;; +15D8;CANADIAN SYLLABICS CARRIER HWE;Lo;0;L;;;;;N;;;;; +15D9;CANADIAN SYLLABICS CARRIER HWEE;Lo;0;L;;;;;N;;;;; +15DA;CANADIAN SYLLABICS CARRIER HWI;Lo;0;L;;;;;N;;;;; +15DB;CANADIAN SYLLABICS CARRIER HWA;Lo;0;L;;;;;N;;;;; +15DC;CANADIAN SYLLABICS CARRIER THU;Lo;0;L;;;;;N;;;;; +15DD;CANADIAN SYLLABICS CARRIER THO;Lo;0;L;;;;;N;;;;; +15DE;CANADIAN SYLLABICS CARRIER THE;Lo;0;L;;;;;N;;;;; +15DF;CANADIAN SYLLABICS CARRIER THEE;Lo;0;L;;;;;N;;;;; +15E0;CANADIAN SYLLABICS CARRIER THI;Lo;0;L;;;;;N;;;;; +15E1;CANADIAN SYLLABICS CARRIER THA;Lo;0;L;;;;;N;;;;; +15E2;CANADIAN SYLLABICS CARRIER TTU;Lo;0;L;;;;;N;;;;; +15E3;CANADIAN SYLLABICS CARRIER TTO;Lo;0;L;;;;;N;;;;; +15E4;CANADIAN SYLLABICS CARRIER TTE;Lo;0;L;;;;;N;;;;; +15E5;CANADIAN SYLLABICS CARRIER TTEE;Lo;0;L;;;;;N;;;;; +15E6;CANADIAN SYLLABICS CARRIER TTI;Lo;0;L;;;;;N;;;;; +15E7;CANADIAN SYLLABICS CARRIER TTA;Lo;0;L;;;;;N;;;;; +15E8;CANADIAN SYLLABICS CARRIER PU;Lo;0;L;;;;;N;;;;; +15E9;CANADIAN SYLLABICS CARRIER PO;Lo;0;L;;;;;N;;;;; +15EA;CANADIAN SYLLABICS CARRIER PE;Lo;0;L;;;;;N;;;;; +15EB;CANADIAN SYLLABICS CARRIER PEE;Lo;0;L;;;;;N;;;;; +15EC;CANADIAN SYLLABICS CARRIER PI;Lo;0;L;;;;;N;;;;; +15ED;CANADIAN SYLLABICS CARRIER PA;Lo;0;L;;;;;N;;;;; +15EE;CANADIAN SYLLABICS CARRIER P;Lo;0;L;;;;;N;;;;; +15EF;CANADIAN SYLLABICS CARRIER GU;Lo;0;L;;;;;N;;;;; +15F0;CANADIAN SYLLABICS CARRIER GO;Lo;0;L;;;;;N;;;;; +15F1;CANADIAN SYLLABICS CARRIER GE;Lo;0;L;;;;;N;;;;; +15F2;CANADIAN SYLLABICS CARRIER GEE;Lo;0;L;;;;;N;;;;; +15F3;CANADIAN SYLLABICS CARRIER GI;Lo;0;L;;;;;N;;;;; +15F4;CANADIAN SYLLABICS CARRIER GA;Lo;0;L;;;;;N;;;;; +15F5;CANADIAN SYLLABICS CARRIER KHU;Lo;0;L;;;;;N;;;;; +15F6;CANADIAN SYLLABICS CARRIER KHO;Lo;0;L;;;;;N;;;;; +15F7;CANADIAN SYLLABICS CARRIER KHE;Lo;0;L;;;;;N;;;;; +15F8;CANADIAN SYLLABICS CARRIER KHEE;Lo;0;L;;;;;N;;;;; +15F9;CANADIAN SYLLABICS CARRIER KHI;Lo;0;L;;;;;N;;;;; +15FA;CANADIAN SYLLABICS CARRIER KHA;Lo;0;L;;;;;N;;;;; +15FB;CANADIAN SYLLABICS CARRIER KKU;Lo;0;L;;;;;N;;;;; +15FC;CANADIAN SYLLABICS CARRIER KKO;Lo;0;L;;;;;N;;;;; +15FD;CANADIAN SYLLABICS CARRIER KKE;Lo;0;L;;;;;N;;;;; +15FE;CANADIAN SYLLABICS CARRIER KKEE;Lo;0;L;;;;;N;;;;; +15FF;CANADIAN SYLLABICS CARRIER KKI;Lo;0;L;;;;;N;;;;; +1600;CANADIAN SYLLABICS CARRIER KKA;Lo;0;L;;;;;N;;;;; +1601;CANADIAN SYLLABICS CARRIER KK;Lo;0;L;;;;;N;;;;; +1602;CANADIAN SYLLABICS CARRIER NU;Lo;0;L;;;;;N;;;;; +1603;CANADIAN SYLLABICS CARRIER NO;Lo;0;L;;;;;N;;;;; +1604;CANADIAN SYLLABICS CARRIER NE;Lo;0;L;;;;;N;;;;; +1605;CANADIAN SYLLABICS CARRIER NEE;Lo;0;L;;;;;N;;;;; +1606;CANADIAN SYLLABICS CARRIER NI;Lo;0;L;;;;;N;;;;; +1607;CANADIAN SYLLABICS CARRIER NA;Lo;0;L;;;;;N;;;;; +1608;CANADIAN SYLLABICS CARRIER MU;Lo;0;L;;;;;N;;;;; +1609;CANADIAN SYLLABICS CARRIER MO;Lo;0;L;;;;;N;;;;; +160A;CANADIAN SYLLABICS CARRIER ME;Lo;0;L;;;;;N;;;;; +160B;CANADIAN SYLLABICS CARRIER MEE;Lo;0;L;;;;;N;;;;; +160C;CANADIAN SYLLABICS CARRIER MI;Lo;0;L;;;;;N;;;;; +160D;CANADIAN SYLLABICS CARRIER MA;Lo;0;L;;;;;N;;;;; +160E;CANADIAN SYLLABICS CARRIER YU;Lo;0;L;;;;;N;;;;; +160F;CANADIAN SYLLABICS CARRIER YO;Lo;0;L;;;;;N;;;;; +1610;CANADIAN SYLLABICS CARRIER YE;Lo;0;L;;;;;N;;;;; +1611;CANADIAN SYLLABICS CARRIER YEE;Lo;0;L;;;;;N;;;;; +1612;CANADIAN SYLLABICS CARRIER YI;Lo;0;L;;;;;N;;;;; +1613;CANADIAN SYLLABICS CARRIER YA;Lo;0;L;;;;;N;;;;; +1614;CANADIAN SYLLABICS CARRIER JU;Lo;0;L;;;;;N;;;;; +1615;CANADIAN SYLLABICS SAYISI JU;Lo;0;L;;;;;N;;;;; +1616;CANADIAN SYLLABICS CARRIER JO;Lo;0;L;;;;;N;;;;; +1617;CANADIAN SYLLABICS CARRIER JE;Lo;0;L;;;;;N;;;;; +1618;CANADIAN SYLLABICS CARRIER JEE;Lo;0;L;;;;;N;;;;; +1619;CANADIAN SYLLABICS CARRIER JI;Lo;0;L;;;;;N;;;;; +161A;CANADIAN SYLLABICS SAYISI JI;Lo;0;L;;;;;N;;;;; +161B;CANADIAN SYLLABICS CARRIER JA;Lo;0;L;;;;;N;;;;; +161C;CANADIAN SYLLABICS CARRIER JJU;Lo;0;L;;;;;N;;;;; +161D;CANADIAN SYLLABICS CARRIER JJO;Lo;0;L;;;;;N;;;;; +161E;CANADIAN SYLLABICS CARRIER JJE;Lo;0;L;;;;;N;;;;; +161F;CANADIAN SYLLABICS CARRIER JJEE;Lo;0;L;;;;;N;;;;; +1620;CANADIAN SYLLABICS CARRIER JJI;Lo;0;L;;;;;N;;;;; +1621;CANADIAN SYLLABICS CARRIER JJA;Lo;0;L;;;;;N;;;;; +1622;CANADIAN SYLLABICS CARRIER LU;Lo;0;L;;;;;N;;;;; +1623;CANADIAN SYLLABICS CARRIER LO;Lo;0;L;;;;;N;;;;; +1624;CANADIAN SYLLABICS CARRIER LE;Lo;0;L;;;;;N;;;;; +1625;CANADIAN SYLLABICS CARRIER LEE;Lo;0;L;;;;;N;;;;; +1626;CANADIAN SYLLABICS CARRIER LI;Lo;0;L;;;;;N;;;;; +1627;CANADIAN SYLLABICS CARRIER LA;Lo;0;L;;;;;N;;;;; +1628;CANADIAN SYLLABICS CARRIER DLU;Lo;0;L;;;;;N;;;;; +1629;CANADIAN SYLLABICS CARRIER DLO;Lo;0;L;;;;;N;;;;; +162A;CANADIAN SYLLABICS CARRIER DLE;Lo;0;L;;;;;N;;;;; +162B;CANADIAN SYLLABICS CARRIER DLEE;Lo;0;L;;;;;N;;;;; +162C;CANADIAN SYLLABICS CARRIER DLI;Lo;0;L;;;;;N;;;;; +162D;CANADIAN SYLLABICS CARRIER DLA;Lo;0;L;;;;;N;;;;; +162E;CANADIAN SYLLABICS CARRIER LHU;Lo;0;L;;;;;N;;;;; +162F;CANADIAN SYLLABICS CARRIER LHO;Lo;0;L;;;;;N;;;;; +1630;CANADIAN SYLLABICS CARRIER LHE;Lo;0;L;;;;;N;;;;; +1631;CANADIAN SYLLABICS CARRIER LHEE;Lo;0;L;;;;;N;;;;; +1632;CANADIAN SYLLABICS CARRIER LHI;Lo;0;L;;;;;N;;;;; +1633;CANADIAN SYLLABICS CARRIER LHA;Lo;0;L;;;;;N;;;;; +1634;CANADIAN SYLLABICS CARRIER TLHU;Lo;0;L;;;;;N;;;;; +1635;CANADIAN SYLLABICS CARRIER TLHO;Lo;0;L;;;;;N;;;;; +1636;CANADIAN SYLLABICS CARRIER TLHE;Lo;0;L;;;;;N;;;;; +1637;CANADIAN SYLLABICS CARRIER TLHEE;Lo;0;L;;;;;N;;;;; +1638;CANADIAN SYLLABICS CARRIER TLHI;Lo;0;L;;;;;N;;;;; +1639;CANADIAN SYLLABICS CARRIER TLHA;Lo;0;L;;;;;N;;;;; +163A;CANADIAN SYLLABICS CARRIER TLU;Lo;0;L;;;;;N;;;;; +163B;CANADIAN SYLLABICS CARRIER TLO;Lo;0;L;;;;;N;;;;; +163C;CANADIAN SYLLABICS CARRIER TLE;Lo;0;L;;;;;N;;;;; +163D;CANADIAN SYLLABICS CARRIER TLEE;Lo;0;L;;;;;N;;;;; +163E;CANADIAN SYLLABICS CARRIER TLI;Lo;0;L;;;;;N;;;;; +163F;CANADIAN SYLLABICS CARRIER TLA;Lo;0;L;;;;;N;;;;; +1640;CANADIAN SYLLABICS CARRIER ZU;Lo;0;L;;;;;N;;;;; +1641;CANADIAN SYLLABICS CARRIER ZO;Lo;0;L;;;;;N;;;;; +1642;CANADIAN SYLLABICS CARRIER ZE;Lo;0;L;;;;;N;;;;; +1643;CANADIAN SYLLABICS CARRIER ZEE;Lo;0;L;;;;;N;;;;; +1644;CANADIAN SYLLABICS CARRIER ZI;Lo;0;L;;;;;N;;;;; +1645;CANADIAN SYLLABICS CARRIER ZA;Lo;0;L;;;;;N;;;;; +1646;CANADIAN SYLLABICS CARRIER Z;Lo;0;L;;;;;N;;;;; +1647;CANADIAN SYLLABICS CARRIER INITIAL Z;Lo;0;L;;;;;N;;;;; +1648;CANADIAN SYLLABICS CARRIER DZU;Lo;0;L;;;;;N;;;;; +1649;CANADIAN SYLLABICS CARRIER DZO;Lo;0;L;;;;;N;;;;; +164A;CANADIAN SYLLABICS CARRIER DZE;Lo;0;L;;;;;N;;;;; +164B;CANADIAN SYLLABICS CARRIER DZEE;Lo;0;L;;;;;N;;;;; +164C;CANADIAN SYLLABICS CARRIER DZI;Lo;0;L;;;;;N;;;;; +164D;CANADIAN SYLLABICS CARRIER DZA;Lo;0;L;;;;;N;;;;; +164E;CANADIAN SYLLABICS CARRIER SU;Lo;0;L;;;;;N;;;;; +164F;CANADIAN SYLLABICS CARRIER SO;Lo;0;L;;;;;N;;;;; +1650;CANADIAN SYLLABICS CARRIER SE;Lo;0;L;;;;;N;;;;; +1651;CANADIAN SYLLABICS CARRIER SEE;Lo;0;L;;;;;N;;;;; +1652;CANADIAN SYLLABICS CARRIER SI;Lo;0;L;;;;;N;;;;; +1653;CANADIAN SYLLABICS CARRIER SA;Lo;0;L;;;;;N;;;;; +1654;CANADIAN SYLLABICS CARRIER SHU;Lo;0;L;;;;;N;;;;; +1655;CANADIAN SYLLABICS CARRIER SHO;Lo;0;L;;;;;N;;;;; +1656;CANADIAN SYLLABICS CARRIER SHE;Lo;0;L;;;;;N;;;;; +1657;CANADIAN SYLLABICS CARRIER SHEE;Lo;0;L;;;;;N;;;;; +1658;CANADIAN SYLLABICS CARRIER SHI;Lo;0;L;;;;;N;;;;; +1659;CANADIAN SYLLABICS CARRIER SHA;Lo;0;L;;;;;N;;;;; +165A;CANADIAN SYLLABICS CARRIER SH;Lo;0;L;;;;;N;;;;; +165B;CANADIAN SYLLABICS CARRIER TSU;Lo;0;L;;;;;N;;;;; +165C;CANADIAN SYLLABICS CARRIER TSO;Lo;0;L;;;;;N;;;;; +165D;CANADIAN SYLLABICS CARRIER TSE;Lo;0;L;;;;;N;;;;; +165E;CANADIAN SYLLABICS CARRIER TSEE;Lo;0;L;;;;;N;;;;; +165F;CANADIAN SYLLABICS CARRIER TSI;Lo;0;L;;;;;N;;;;; +1660;CANADIAN SYLLABICS CARRIER TSA;Lo;0;L;;;;;N;;;;; +1661;CANADIAN SYLLABICS CARRIER CHU;Lo;0;L;;;;;N;;;;; +1662;CANADIAN SYLLABICS CARRIER CHO;Lo;0;L;;;;;N;;;;; +1663;CANADIAN SYLLABICS CARRIER CHE;Lo;0;L;;;;;N;;;;; +1664;CANADIAN SYLLABICS CARRIER CHEE;Lo;0;L;;;;;N;;;;; +1665;CANADIAN SYLLABICS CARRIER CHI;Lo;0;L;;;;;N;;;;; +1666;CANADIAN SYLLABICS CARRIER CHA;Lo;0;L;;;;;N;;;;; +1667;CANADIAN SYLLABICS CARRIER TTSU;Lo;0;L;;;;;N;;;;; +1668;CANADIAN SYLLABICS CARRIER TTSO;Lo;0;L;;;;;N;;;;; +1669;CANADIAN SYLLABICS CARRIER TTSE;Lo;0;L;;;;;N;;;;; +166A;CANADIAN SYLLABICS CARRIER TTSEE;Lo;0;L;;;;;N;;;;; +166B;CANADIAN SYLLABICS CARRIER TTSI;Lo;0;L;;;;;N;;;;; +166C;CANADIAN SYLLABICS CARRIER TTSA;Lo;0;L;;;;;N;;;;; +166D;CANADIAN SYLLABICS CHI SIGN;Po;0;L;;;;;N;;;;; +166E;CANADIAN SYLLABICS FULL STOP;Po;0;L;;;;;N;;;;; +166F;CANADIAN SYLLABICS QAI;Lo;0;L;;;;;N;;;;; +1670;CANADIAN SYLLABICS NGAI;Lo;0;L;;;;;N;;;;; +1671;CANADIAN SYLLABICS NNGI;Lo;0;L;;;;;N;;;;; +1672;CANADIAN SYLLABICS NNGII;Lo;0;L;;;;;N;;;;; +1673;CANADIAN SYLLABICS NNGO;Lo;0;L;;;;;N;;;;; +1674;CANADIAN SYLLABICS NNGOO;Lo;0;L;;;;;N;;;;; +1675;CANADIAN SYLLABICS NNGA;Lo;0;L;;;;;N;;;;; +1676;CANADIAN SYLLABICS NNGAA;Lo;0;L;;;;;N;;;;; +1680;OGHAM SPACE MARK;Zs;0;WS;;;;;N;;;;; +1681;OGHAM LETTER BEITH;Lo;0;L;;;;;N;;;;; +1682;OGHAM LETTER LUIS;Lo;0;L;;;;;N;;;;; +1683;OGHAM LETTER FEARN;Lo;0;L;;;;;N;;;;; +1684;OGHAM LETTER SAIL;Lo;0;L;;;;;N;;;;; +1685;OGHAM LETTER NION;Lo;0;L;;;;;N;;;;; +1686;OGHAM LETTER UATH;Lo;0;L;;;;;N;;;;; +1687;OGHAM LETTER DAIR;Lo;0;L;;;;;N;;;;; +1688;OGHAM LETTER TINNE;Lo;0;L;;;;;N;;;;; +1689;OGHAM LETTER COLL;Lo;0;L;;;;;N;;;;; +168A;OGHAM LETTER CEIRT;Lo;0;L;;;;;N;;;;; +168B;OGHAM LETTER MUIN;Lo;0;L;;;;;N;;;;; +168C;OGHAM LETTER GORT;Lo;0;L;;;;;N;;;;; +168D;OGHAM LETTER NGEADAL;Lo;0;L;;;;;N;;;;; +168E;OGHAM LETTER STRAIF;Lo;0;L;;;;;N;;;;; +168F;OGHAM LETTER RUIS;Lo;0;L;;;;;N;;;;; +1690;OGHAM LETTER AILM;Lo;0;L;;;;;N;;;;; +1691;OGHAM LETTER ONN;Lo;0;L;;;;;N;;;;; +1692;OGHAM LETTER UR;Lo;0;L;;;;;N;;;;; +1693;OGHAM LETTER EADHADH;Lo;0;L;;;;;N;;;;; +1694;OGHAM LETTER IODHADH;Lo;0;L;;;;;N;;;;; +1695;OGHAM LETTER EABHADH;Lo;0;L;;;;;N;;;;; +1696;OGHAM LETTER OR;Lo;0;L;;;;;N;;;;; +1697;OGHAM LETTER UILLEANN;Lo;0;L;;;;;N;;;;; +1698;OGHAM LETTER IFIN;Lo;0;L;;;;;N;;;;; +1699;OGHAM LETTER EAMHANCHOLL;Lo;0;L;;;;;N;;;;; +169A;OGHAM LETTER PEITH;Lo;0;L;;;;;N;;;;; +169B;OGHAM FEATHER MARK;Ps;0;ON;;;;;N;;;;; +169C;OGHAM REVERSED FEATHER MARK;Pe;0;ON;;;;;N;;;;; +16A0;RUNIC LETTER FEHU FEOH FE F;Lo;0;L;;;;;N;;;;; +16A1;RUNIC LETTER V;Lo;0;L;;;;;N;;;;; +16A2;RUNIC LETTER URUZ UR U;Lo;0;L;;;;;N;;;;; +16A3;RUNIC LETTER YR;Lo;0;L;;;;;N;;;;; +16A4;RUNIC LETTER Y;Lo;0;L;;;;;N;;;;; +16A5;RUNIC LETTER W;Lo;0;L;;;;;N;;;;; +16A6;RUNIC LETTER THURISAZ THURS THORN;Lo;0;L;;;;;N;;;;; +16A7;RUNIC LETTER ETH;Lo;0;L;;;;;N;;;;; +16A8;RUNIC LETTER ANSUZ A;Lo;0;L;;;;;N;;;;; +16A9;RUNIC LETTER OS O;Lo;0;L;;;;;N;;;;; +16AA;RUNIC LETTER AC A;Lo;0;L;;;;;N;;;;; +16AB;RUNIC LETTER AESC;Lo;0;L;;;;;N;;;;; +16AC;RUNIC LETTER LONG-BRANCH-OSS O;Lo;0;L;;;;;N;;;;; +16AD;RUNIC LETTER SHORT-TWIG-OSS O;Lo;0;L;;;;;N;;;;; +16AE;RUNIC LETTER O;Lo;0;L;;;;;N;;;;; +16AF;RUNIC LETTER OE;Lo;0;L;;;;;N;;;;; +16B0;RUNIC LETTER ON;Lo;0;L;;;;;N;;;;; +16B1;RUNIC LETTER RAIDO RAD REID R;Lo;0;L;;;;;N;;;;; +16B2;RUNIC LETTER KAUNA;Lo;0;L;;;;;N;;;;; +16B3;RUNIC LETTER CEN;Lo;0;L;;;;;N;;;;; +16B4;RUNIC LETTER KAUN K;Lo;0;L;;;;;N;;;;; +16B5;RUNIC LETTER G;Lo;0;L;;;;;N;;;;; +16B6;RUNIC LETTER ENG;Lo;0;L;;;;;N;;;;; +16B7;RUNIC LETTER GEBO GYFU G;Lo;0;L;;;;;N;;;;; +16B8;RUNIC LETTER GAR;Lo;0;L;;;;;N;;;;; +16B9;RUNIC LETTER WUNJO WYNN W;Lo;0;L;;;;;N;;;;; +16BA;RUNIC LETTER HAGLAZ H;Lo;0;L;;;;;N;;;;; +16BB;RUNIC LETTER HAEGL H;Lo;0;L;;;;;N;;;;; +16BC;RUNIC LETTER LONG-BRANCH-HAGALL H;Lo;0;L;;;;;N;;;;; +16BD;RUNIC LETTER SHORT-TWIG-HAGALL H;Lo;0;L;;;;;N;;;;; +16BE;RUNIC LETTER NAUDIZ NYD NAUD N;Lo;0;L;;;;;N;;;;; +16BF;RUNIC LETTER SHORT-TWIG-NAUD N;Lo;0;L;;;;;N;;;;; +16C0;RUNIC LETTER DOTTED-N;Lo;0;L;;;;;N;;;;; +16C1;RUNIC LETTER ISAZ IS ISS I;Lo;0;L;;;;;N;;;;; +16C2;RUNIC LETTER E;Lo;0;L;;;;;N;;;;; +16C3;RUNIC LETTER JERAN J;Lo;0;L;;;;;N;;;;; +16C4;RUNIC LETTER GER;Lo;0;L;;;;;N;;;;; +16C5;RUNIC LETTER LONG-BRANCH-AR AE;Lo;0;L;;;;;N;;;;; +16C6;RUNIC LETTER SHORT-TWIG-AR A;Lo;0;L;;;;;N;;;;; +16C7;RUNIC LETTER IWAZ EOH;Lo;0;L;;;;;N;;;;; +16C8;RUNIC LETTER PERTHO PEORTH P;Lo;0;L;;;;;N;;;;; +16C9;RUNIC LETTER ALGIZ EOLHX;Lo;0;L;;;;;N;;;;; +16CA;RUNIC LETTER SOWILO S;Lo;0;L;;;;;N;;;;; +16CB;RUNIC LETTER SIGEL LONG-BRANCH-SOL S;Lo;0;L;;;;;N;;;;; +16CC;RUNIC LETTER SHORT-TWIG-SOL S;Lo;0;L;;;;;N;;;;; +16CD;RUNIC LETTER C;Lo;0;L;;;;;N;;;;; +16CE;RUNIC LETTER Z;Lo;0;L;;;;;N;;;;; +16CF;RUNIC LETTER TIWAZ TIR TYR T;Lo;0;L;;;;;N;;;;; +16D0;RUNIC LETTER SHORT-TWIG-TYR T;Lo;0;L;;;;;N;;;;; +16D1;RUNIC LETTER D;Lo;0;L;;;;;N;;;;; +16D2;RUNIC LETTER BERKANAN BEORC BJARKAN B;Lo;0;L;;;;;N;;;;; +16D3;RUNIC LETTER SHORT-TWIG-BJARKAN B;Lo;0;L;;;;;N;;;;; +16D4;RUNIC LETTER DOTTED-P;Lo;0;L;;;;;N;;;;; +16D5;RUNIC LETTER OPEN-P;Lo;0;L;;;;;N;;;;; +16D6;RUNIC LETTER EHWAZ EH E;Lo;0;L;;;;;N;;;;; +16D7;RUNIC LETTER MANNAZ MAN M;Lo;0;L;;;;;N;;;;; +16D8;RUNIC LETTER LONG-BRANCH-MADR M;Lo;0;L;;;;;N;;;;; +16D9;RUNIC LETTER SHORT-TWIG-MADR M;Lo;0;L;;;;;N;;;;; +16DA;RUNIC LETTER LAUKAZ LAGU LOGR L;Lo;0;L;;;;;N;;;;; +16DB;RUNIC LETTER DOTTED-L;Lo;0;L;;;;;N;;;;; +16DC;RUNIC LETTER INGWAZ;Lo;0;L;;;;;N;;;;; +16DD;RUNIC LETTER ING;Lo;0;L;;;;;N;;;;; +16DE;RUNIC LETTER DAGAZ DAEG D;Lo;0;L;;;;;N;;;;; +16DF;RUNIC LETTER OTHALAN ETHEL O;Lo;0;L;;;;;N;;;;; +16E0;RUNIC LETTER EAR;Lo;0;L;;;;;N;;;;; +16E1;RUNIC LETTER IOR;Lo;0;L;;;;;N;;;;; +16E2;RUNIC LETTER CWEORTH;Lo;0;L;;;;;N;;;;; +16E3;RUNIC LETTER CALC;Lo;0;L;;;;;N;;;;; +16E4;RUNIC LETTER CEALC;Lo;0;L;;;;;N;;;;; +16E5;RUNIC LETTER STAN;Lo;0;L;;;;;N;;;;; +16E6;RUNIC LETTER LONG-BRANCH-YR;Lo;0;L;;;;;N;;;;; +16E7;RUNIC LETTER SHORT-TWIG-YR;Lo;0;L;;;;;N;;;;; +16E8;RUNIC LETTER ICELANDIC-YR;Lo;0;L;;;;;N;;;;; +16E9;RUNIC LETTER Q;Lo;0;L;;;;;N;;;;; +16EA;RUNIC LETTER X;Lo;0;L;;;;;N;;;;; +16EB;RUNIC SINGLE PUNCTUATION;Po;0;L;;;;;N;;;;; +16EC;RUNIC MULTIPLE PUNCTUATION;Po;0;L;;;;;N;;;;; +16ED;RUNIC CROSS PUNCTUATION;Po;0;L;;;;;N;;;;; +16EE;RUNIC ARLAUG SYMBOL;Nl;0;L;;;;17;N;;golden number 17;;; +16EF;RUNIC TVIMADUR SYMBOL;Nl;0;L;;;;18;N;;golden number 18;;; +16F0;RUNIC BELGTHOR SYMBOL;Nl;0;L;;;;19;N;;golden number 19;;; +1700;TAGALOG LETTER A;Lo;0;L;;;;;N;;;;; +1701;TAGALOG LETTER I;Lo;0;L;;;;;N;;;;; +1702;TAGALOG LETTER U;Lo;0;L;;;;;N;;;;; +1703;TAGALOG LETTER KA;Lo;0;L;;;;;N;;;;; +1704;TAGALOG LETTER GA;Lo;0;L;;;;;N;;;;; +1705;TAGALOG LETTER NGA;Lo;0;L;;;;;N;;;;; +1706;TAGALOG LETTER TA;Lo;0;L;;;;;N;;;;; +1707;TAGALOG LETTER DA;Lo;0;L;;;;;N;;;;; +1708;TAGALOG LETTER NA;Lo;0;L;;;;;N;;;;; +1709;TAGALOG LETTER PA;Lo;0;L;;;;;N;;;;; +170A;TAGALOG LETTER BA;Lo;0;L;;;;;N;;;;; +170B;TAGALOG LETTER MA;Lo;0;L;;;;;N;;;;; +170C;TAGALOG LETTER YA;Lo;0;L;;;;;N;;;;; +170E;TAGALOG LETTER LA;Lo;0;L;;;;;N;;;;; +170F;TAGALOG LETTER WA;Lo;0;L;;;;;N;;;;; +1710;TAGALOG LETTER SA;Lo;0;L;;;;;N;;;;; +1711;TAGALOG LETTER HA;Lo;0;L;;;;;N;;;;; +1712;TAGALOG VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;; +1713;TAGALOG VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; +1714;TAGALOG SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;; +1720;HANUNOO LETTER A;Lo;0;L;;;;;N;;;;; +1721;HANUNOO LETTER I;Lo;0;L;;;;;N;;;;; +1722;HANUNOO LETTER U;Lo;0;L;;;;;N;;;;; +1723;HANUNOO LETTER KA;Lo;0;L;;;;;N;;;;; +1724;HANUNOO LETTER GA;Lo;0;L;;;;;N;;;;; +1725;HANUNOO LETTER NGA;Lo;0;L;;;;;N;;;;; +1726;HANUNOO LETTER TA;Lo;0;L;;;;;N;;;;; +1727;HANUNOO LETTER DA;Lo;0;L;;;;;N;;;;; +1728;HANUNOO LETTER NA;Lo;0;L;;;;;N;;;;; +1729;HANUNOO LETTER PA;Lo;0;L;;;;;N;;;;; +172A;HANUNOO LETTER BA;Lo;0;L;;;;;N;;;;; +172B;HANUNOO LETTER MA;Lo;0;L;;;;;N;;;;; +172C;HANUNOO LETTER YA;Lo;0;L;;;;;N;;;;; +172D;HANUNOO LETTER RA;Lo;0;L;;;;;N;;;;; +172E;HANUNOO LETTER LA;Lo;0;L;;;;;N;;;;; +172F;HANUNOO LETTER WA;Lo;0;L;;;;;N;;;;; +1730;HANUNOO LETTER SA;Lo;0;L;;;;;N;;;;; +1731;HANUNOO LETTER HA;Lo;0;L;;;;;N;;;;; +1732;HANUNOO VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;; +1733;HANUNOO VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; +1734;HANUNOO SIGN PAMUDPOD;Mn;9;NSM;;;;;N;;;;; +1735;PHILIPPINE SINGLE PUNCTUATION;Po;0;L;;;;;N;;;;; +1736;PHILIPPINE DOUBLE PUNCTUATION;Po;0;L;;;;;N;;;;; +1740;BUHID LETTER A;Lo;0;L;;;;;N;;;;; +1741;BUHID LETTER I;Lo;0;L;;;;;N;;;;; +1742;BUHID LETTER U;Lo;0;L;;;;;N;;;;; +1743;BUHID LETTER KA;Lo;0;L;;;;;N;;;;; +1744;BUHID LETTER GA;Lo;0;L;;;;;N;;;;; +1745;BUHID LETTER NGA;Lo;0;L;;;;;N;;;;; +1746;BUHID LETTER TA;Lo;0;L;;;;;N;;;;; +1747;BUHID LETTER DA;Lo;0;L;;;;;N;;;;; +1748;BUHID LETTER NA;Lo;0;L;;;;;N;;;;; +1749;BUHID LETTER PA;Lo;0;L;;;;;N;;;;; +174A;BUHID LETTER BA;Lo;0;L;;;;;N;;;;; +174B;BUHID LETTER MA;Lo;0;L;;;;;N;;;;; +174C;BUHID LETTER YA;Lo;0;L;;;;;N;;;;; +174D;BUHID LETTER RA;Lo;0;L;;;;;N;;;;; +174E;BUHID LETTER LA;Lo;0;L;;;;;N;;;;; +174F;BUHID LETTER WA;Lo;0;L;;;;;N;;;;; +1750;BUHID LETTER SA;Lo;0;L;;;;;N;;;;; +1751;BUHID LETTER HA;Lo;0;L;;;;;N;;;;; +1752;BUHID VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;; +1753;BUHID VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; +1760;TAGBANWA LETTER A;Lo;0;L;;;;;N;;;;; +1761;TAGBANWA LETTER I;Lo;0;L;;;;;N;;;;; +1762;TAGBANWA LETTER U;Lo;0;L;;;;;N;;;;; +1763;TAGBANWA LETTER KA;Lo;0;L;;;;;N;;;;; +1764;TAGBANWA LETTER GA;Lo;0;L;;;;;N;;;;; +1765;TAGBANWA LETTER NGA;Lo;0;L;;;;;N;;;;; +1766;TAGBANWA LETTER TA;Lo;0;L;;;;;N;;;;; +1767;TAGBANWA LETTER DA;Lo;0;L;;;;;N;;;;; +1768;TAGBANWA LETTER NA;Lo;0;L;;;;;N;;;;; +1769;TAGBANWA LETTER PA;Lo;0;L;;;;;N;;;;; +176A;TAGBANWA LETTER BA;Lo;0;L;;;;;N;;;;; +176B;TAGBANWA LETTER MA;Lo;0;L;;;;;N;;;;; +176C;TAGBANWA LETTER YA;Lo;0;L;;;;;N;;;;; +176E;TAGBANWA LETTER LA;Lo;0;L;;;;;N;;;;; +176F;TAGBANWA LETTER WA;Lo;0;L;;;;;N;;;;; +1770;TAGBANWA LETTER SA;Lo;0;L;;;;;N;;;;; +1772;TAGBANWA VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;; +1773;TAGBANWA VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; +1780;KHMER LETTER KA;Lo;0;L;;;;;N;;;;; +1781;KHMER LETTER KHA;Lo;0;L;;;;;N;;;;; +1782;KHMER LETTER KO;Lo;0;L;;;;;N;;;;; +1783;KHMER LETTER KHO;Lo;0;L;;;;;N;;;;; +1784;KHMER LETTER NGO;Lo;0;L;;;;;N;;;;; +1785;KHMER LETTER CA;Lo;0;L;;;;;N;;;;; +1786;KHMER LETTER CHA;Lo;0;L;;;;;N;;;;; +1787;KHMER LETTER CO;Lo;0;L;;;;;N;;;;; +1788;KHMER LETTER CHO;Lo;0;L;;;;;N;;;;; +1789;KHMER LETTER NYO;Lo;0;L;;;;;N;;;;; +178A;KHMER LETTER DA;Lo;0;L;;;;;N;;;;; +178B;KHMER LETTER TTHA;Lo;0;L;;;;;N;;;;; +178C;KHMER LETTER DO;Lo;0;L;;;;;N;;;;; +178D;KHMER LETTER TTHO;Lo;0;L;;;;;N;;;;; +178E;KHMER LETTER NNO;Lo;0;L;;;;;N;;;;; +178F;KHMER LETTER TA;Lo;0;L;;;;;N;;;;; +1790;KHMER LETTER THA;Lo;0;L;;;;;N;;;;; +1791;KHMER LETTER TO;Lo;0;L;;;;;N;;;;; +1792;KHMER LETTER THO;Lo;0;L;;;;;N;;;;; +1793;KHMER LETTER NO;Lo;0;L;;;;;N;;;;; +1794;KHMER LETTER BA;Lo;0;L;;;;;N;;;;; +1795;KHMER LETTER PHA;Lo;0;L;;;;;N;;;;; +1796;KHMER LETTER PO;Lo;0;L;;;;;N;;;;; +1797;KHMER LETTER PHO;Lo;0;L;;;;;N;;;;; +1798;KHMER LETTER MO;Lo;0;L;;;;;N;;;;; +1799;KHMER LETTER YO;Lo;0;L;;;;;N;;;;; +179A;KHMER LETTER RO;Lo;0;L;;;;;N;;;;; +179B;KHMER LETTER LO;Lo;0;L;;;;;N;;;;; +179C;KHMER LETTER VO;Lo;0;L;;;;;N;;;;; +179D;KHMER LETTER SHA;Lo;0;L;;;;;N;;;;; +179E;KHMER LETTER SSO;Lo;0;L;;;;;N;;;;; +179F;KHMER LETTER SA;Lo;0;L;;;;;N;;;;; +17A0;KHMER LETTER HA;Lo;0;L;;;;;N;;;;; +17A1;KHMER LETTER LA;Lo;0;L;;;;;N;;;;; +17A2;KHMER LETTER QA;Lo;0;L;;;;;N;;;;; +17A3;KHMER INDEPENDENT VOWEL QAQ;Lo;0;L;;;;;N;;;;; +17A4;KHMER INDEPENDENT VOWEL QAA;Lo;0;L;;;;;N;;;;; +17A5;KHMER INDEPENDENT VOWEL QI;Lo;0;L;;;;;N;;;;; +17A6;KHMER INDEPENDENT VOWEL QII;Lo;0;L;;;;;N;;;;; +17A7;KHMER INDEPENDENT VOWEL QU;Lo;0;L;;;;;N;;;;; +17A8;KHMER INDEPENDENT VOWEL QUK;Lo;0;L;;;;;N;;;;; +17A9;KHMER INDEPENDENT VOWEL QUU;Lo;0;L;;;;;N;;;;; +17AA;KHMER INDEPENDENT VOWEL QUUV;Lo;0;L;;;;;N;;;;; +17AB;KHMER INDEPENDENT VOWEL RY;Lo;0;L;;;;;N;;;;; +17AC;KHMER INDEPENDENT VOWEL RYY;Lo;0;L;;;;;N;;;;; +17AD;KHMER INDEPENDENT VOWEL LY;Lo;0;L;;;;;N;;;;; +17AE;KHMER INDEPENDENT VOWEL LYY;Lo;0;L;;;;;N;;;;; +17AF;KHMER INDEPENDENT VOWEL QE;Lo;0;L;;;;;N;;;;; +17B0;KHMER INDEPENDENT VOWEL QAI;Lo;0;L;;;;;N;;;;; +17B1;KHMER INDEPENDENT VOWEL QOO TYPE ONE;Lo;0;L;;;;;N;;;;; +17B2;KHMER INDEPENDENT VOWEL QOO TYPE TWO;Lo;0;L;;;;;N;;;;; +17B3;KHMER INDEPENDENT VOWEL QAU;Lo;0;L;;;;;N;;;;; +17B4;KHMER VOWEL INHERENT AQ;Mc;0;L;;;;;N;;;;; +17B5;KHMER VOWEL INHERENT AA;Mc;0;L;;;;;N;;;;; +17B6;KHMER VOWEL SIGN AA;Mc;0;L;;;;;N;;;;; +17B7;KHMER VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;; +17B8;KHMER VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;; +17B9;KHMER VOWEL SIGN Y;Mn;0;NSM;;;;;N;;;;; +17BA;KHMER VOWEL SIGN YY;Mn;0;NSM;;;;;N;;;;; +17BB;KHMER VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;; +17BC;KHMER VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;; +17BD;KHMER VOWEL SIGN UA;Mn;0;NSM;;;;;N;;;;; +17BE;KHMER VOWEL SIGN OE;Mc;0;L;;;;;N;;;;; +17BF;KHMER VOWEL SIGN YA;Mc;0;L;;;;;N;;;;; +17C0;KHMER VOWEL SIGN IE;Mc;0;L;;;;;N;;;;; +17C1;KHMER VOWEL SIGN E;Mc;0;L;;;;;N;;;;; +17C2;KHMER VOWEL SIGN AE;Mc;0;L;;;;;N;;;;; +17C3;KHMER VOWEL SIGN AI;Mc;0;L;;;;;N;;;;; +17C4;KHMER VOWEL SIGN OO;Mc;0;L;;;;;N;;;;; +17C5;KHMER VOWEL SIGN AU;Mc;0;L;;;;;N;;;;; +17C6;KHMER SIGN NIKAHIT;Mn;0;NSM;;;;;N;;;;; +17C7;KHMER SIGN REAHMUK;Mc;0;L;;;;;N;;;;; +17C8;KHMER SIGN YUUKALEAPINTU;Mc;0;L;;;;;N;;;;; +17C9;KHMER SIGN MUUSIKATOAN;Mn;0;NSM;;;;;N;;;;; +17CA;KHMER SIGN TRIISAP;Mn;0;NSM;;;;;N;;;;; +17CB;KHMER SIGN BANTOC;Mn;0;NSM;;;;;N;;;;; +17CC;KHMER SIGN ROBAT;Mn;0;NSM;;;;;N;;;;; +17CD;KHMER SIGN TOANDAKHIAT;Mn;0;NSM;;;;;N;;;;; +17CE;KHMER SIGN KAKABAT;Mn;0;NSM;;;;;N;;;;; +17CF;KHMER SIGN AHSDA;Mn;0;NSM;;;;;N;;;;; +17D0;KHMER SIGN SAMYOK SANNYA;Mn;0;NSM;;;;;N;;;;; +17D1;KHMER SIGN VIRIAM;Mn;0;NSM;;;;;N;;;;; +17D2;KHMER SIGN COENG;Mn;9;NSM;;;;;N;;;;; +17D3;KHMER SIGN BATHAMASAT;Mn;0;NSM;;;;;N;;;;; +17D4;KHMER SIGN KHAN;Po;0;L;;;;;N;;;;; +17D5;KHMER SIGN BARIYOOSAN;Po;0;L;;;;;N;;;;; +17D6;KHMER SIGN CAMNUC PII KUUH;Po;0;L;;;;;N;;;;; +17D7;KHMER SIGN LEK TOO;Lm;0;L;;;;;N;;;;; +17D8;KHMER SIGN BEYYAL;Po;0;L;;;;;N;;;;; +17D9;KHMER SIGN PHNAEK MUAN;Po;0;L;;;;;N;;;;; +17DA;KHMER SIGN KOOMUUT;Po;0;L;;;;;N;;;;; +17DB;KHMER CURRENCY SYMBOL RIEL;Sc;0;ET;;;;;N;;;;; +17DC;KHMER SIGN AVAKRAHASANYA;Lo;0;L;;;;;N;;;;; +17E0;KHMER DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +17E1;KHMER DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +17E2;KHMER DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +17E3;KHMER DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +17E4;KHMER DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +17E5;KHMER DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +17E6;KHMER DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +17E7;KHMER DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +17E8;KHMER DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +17E9;KHMER DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +1800;MONGOLIAN BIRGA;Po;0;ON;;;;;N;;;;; +1801;MONGOLIAN ELLIPSIS;Po;0;ON;;;;;N;;;;; +1802;MONGOLIAN COMMA;Po;0;ON;;;;;N;;;;; +1803;MONGOLIAN FULL STOP;Po;0;ON;;;;;N;;;;; +1804;MONGOLIAN COLON;Po;0;ON;;;;;N;;;;; +1805;MONGOLIAN FOUR DOTS;Po;0;ON;;;;;N;;;;; +1806;MONGOLIAN TODO SOFT HYPHEN;Pd;0;ON;;;;;N;;;;; +1807;MONGOLIAN SIBE SYLLABLE BOUNDARY MARKER;Po;0;ON;;;;;N;;;;; +1808;MONGOLIAN MANCHU COMMA;Po;0;ON;;;;;N;;;;; +1809;MONGOLIAN MANCHU FULL STOP;Po;0;ON;;;;;N;;;;; +180A;MONGOLIAN NIRUGU;Po;0;ON;;;;;N;;;;; +180B;MONGOLIAN FREE VARIATION SELECTOR ONE;Mn;0;NSM;;;;;N;;;;; +180C;MONGOLIAN FREE VARIATION SELECTOR TWO;Mn;0;NSM;;;;;N;;;;; +180D;MONGOLIAN FREE VARIATION SELECTOR THREE;Mn;0;NSM;;;;;N;;;;; +180E;MONGOLIAN VOWEL SEPARATOR;Cf;0;BN;;;;;N;;;;; +1810;MONGOLIAN DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;; +1811;MONGOLIAN DIGIT ONE;Nd;0;L;;1;1;1;N;;;;; +1812;MONGOLIAN DIGIT TWO;Nd;0;L;;2;2;2;N;;;;; +1813;MONGOLIAN DIGIT THREE;Nd;0;L;;3;3;3;N;;;;; +1814;MONGOLIAN DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;; +1815;MONGOLIAN DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;; +1816;MONGOLIAN DIGIT SIX;Nd;0;L;;6;6;6;N;;;;; +1817;MONGOLIAN DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;; +1818;MONGOLIAN DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;; +1819;MONGOLIAN DIGIT NINE;Nd;0;L;;9;9;9;N;;;;; +1820;MONGOLIAN LETTER A;Lo;0;L;;;;;N;;;;; +1821;MONGOLIAN LETTER E;Lo;0;L;;;;;N;;;;; +1822;MONGOLIAN LETTER I;Lo;0;L;;;;;N;;;;; +1823;MONGOLIAN LETTER O;Lo;0;L;;;;;N;;;;; +1824;MONGOLIAN LETTER U;Lo;0;L;;;;;N;;;;; +1825;MONGOLIAN LETTER OE;Lo;0;L;;;;;N;;;;; +1826;MONGOLIAN LETTER UE;Lo;0;L;;;;;N;;;;; +1827;MONGOLIAN LETTER EE;Lo;0;L;;;;;N;;;;; +1828;MONGOLIAN LETTER NA;Lo;0;L;;;;;N;;;;; +1829;MONGOLIAN LETTER ANG;Lo;0;L;;;;;N;;;;; +182A;MONGOLIAN LETTER BA;Lo;0;L;;;;;N;;;;; +182B;MONGOLIAN LETTER PA;Lo;0;L;;;;;N;;;;; +182C;MONGOLIAN LETTER QA;Lo;0;L;;;;;N;;;;; +182D;MONGOLIAN LETTER GA;Lo;0;L;;;;;N;;;;; +182E;MONGOLIAN LETTER MA;Lo;0;L;;;;;N;;;;; +182F;MONGOLIAN LETTER LA;Lo;0;L;;;;;N;;;;; +1830;MONGOLIAN LETTER SA;Lo;0;L;;;;;N;;;;; +1831;MONGOLIAN LETTER SHA;Lo;0;L;;;;;N;;;;; +1832;MONGOLIAN LETTER TA;Lo;0;L;;;;;N;;;;; +1833;MONGOLIAN LETTER DA;Lo;0;L;;;;;N;;;;; +1834;MONGOLIAN LETTER CHA;Lo;0;L;;;;;N;;;;; +1835;MONGOLIAN LETTER JA;Lo;0;L;;;;;N;;;;; +1836;MONGOLIAN LETTER YA;Lo;0;L;;;;;N;;;;; +1837;MONGOLIAN LETTER RA;Lo;0;L;;;;;N;;;;; +1838;MONGOLIAN LETTER WA;Lo;0;L;;;;;N;;;;; +1839;MONGOLIAN LETTER FA;Lo;0;L;;;;;N;;;;; +183A;MONGOLIAN LETTER KA;Lo;0;L;;;;;N;;;;; +183B;MONGOLIAN LETTER KHA;Lo;0;L;;;;;N;;;;; +183C;MONGOLIAN LETTER TSA;Lo;0;L;;;;;N;;;;; +183D;MONGOLIAN LETTER ZA;Lo;0;L;;;;;N;;;;; +183E;MONGOLIAN LETTER HAA;Lo;0;L;;;;;N;;;;; +183F;MONGOLIAN LETTER ZRA;Lo;0;L;;;;;N;;;;; +1840;MONGOLIAN LETTER LHA;Lo;0;L;;;;;N;;;;; +1841;MONGOLIAN LETTER ZHI;Lo;0;L;;;;;N;;;;; +1842;MONGOLIAN LETTER CHI;Lo;0;L;;;;;N;;;;; +1843;MONGOLIAN LETTER TODO LONG VOWEL SIGN;Lm;0;L;;;;;N;;;;; +1844;MONGOLIAN LETTER TODO E;Lo;0;L;;;;;N;;;;; +1845;MONGOLIAN LETTER TODO I;Lo;0;L;;;;;N;;;;; +1846;MONGOLIAN LETTER TODO O;Lo;0;L;;;;;N;;;;; +1847;MONGOLIAN LETTER TODO U;Lo;0;L;;;;;N;;;;; +1848;MONGOLIAN LETTER TODO OE;Lo;0;L;;;;;N;;;;; +1849;MONGOLIAN LETTER TODO UE;Lo;0;L;;;;;N;;;;; +184A;MONGOLIAN LETTER TODO ANG;Lo;0;L;;;;;N;;;;; +184B;MONGOLIAN LETTER TODO BA;Lo;0;L;;;;;N;;;;; +184C;MONGOLIAN LETTER TODO PA;Lo;0;L;;;;;N;;;;; +184D;MONGOLIAN LETTER TODO QA;Lo;0;L;;;;;N;;;;; +184E;MONGOLIAN LETTER TODO GA;Lo;0;L;;;;;N;;;;; +184F;MONGOLIAN LETTER TODO MA;Lo;0;L;;;;;N;;;;; +1850;MONGOLIAN LETTER TODO TA;Lo;0;L;;;;;N;;;;; +1851;MONGOLIAN LETTER TODO DA;Lo;0;L;;;;;N;;;;; +1852;MONGOLIAN LETTER TODO CHA;Lo;0;L;;;;;N;;;;; +1853;MONGOLIAN LETTER TODO JA;Lo;0;L;;;;;N;;;;; +1854;MONGOLIAN LETTER TODO TSA;Lo;0;L;;;;;N;;;;; +1855;MONGOLIAN LETTER TODO YA;Lo;0;L;;;;;N;;;;; +1856;MONGOLIAN LETTER TODO WA;Lo;0;L;;;;;N;;;;; +1857;MONGOLIAN LETTER TODO KA;Lo;0;L;;;;;N;;;;; +1858;MONGOLIAN LETTER TODO GAA;Lo;0;L;;;;;N;;;;; +1859;MONGOLIAN LETTER TODO HAA;Lo;0;L;;;;;N;;;;; +185A;MONGOLIAN LETTER TODO JIA;Lo;0;L;;;;;N;;;;; +185B;MONGOLIAN LETTER TODO NIA;Lo;0;L;;;;;N;;;;; +185C;MONGOLIAN LETTER TODO DZA;Lo;0;L;;;;;N;;;;; +185D;MONGOLIAN LETTER SIBE E;Lo;0;L;;;;;N;;;;; +185E;MONGOLIAN LETTER SIBE I;Lo;0;L;;;;;N;;;;; +185F;MONGOLIAN LETTER SIBE IY;Lo;0;L;;;;;N;;;;; +1860;MONGOLIAN LETTER SIBE UE;Lo;0;L;;;;;N;;;;; +1861;MONGOLIAN LETTER SIBE U;Lo;0;L;;;;;N;;;;; +1862;MONGOLIAN LETTER SIBE ANG;Lo;0;L;;;;;N;;;;; +1863;MONGOLIAN LETTER SIBE KA;Lo;0;L;;;;;N;;;;; +1864;MONGOLIAN LETTER SIBE GA;Lo;0;L;;;;;N;;;;; +1865;MONGOLIAN LETTER SIBE HA;Lo;0;L;;;;;N;;;;; +1866;MONGOLIAN LETTER SIBE PA;Lo;0;L;;;;;N;;;;; +1867;MONGOLIAN LETTER SIBE SHA;Lo;0;L;;;;;N;;;;; +1868;MONGOLIAN LETTER SIBE TA;Lo;0;L;;;;;N;;;;; +1869;MONGOLIAN LETTER SIBE DA;Lo;0;L;;;;;N;;;;; +186A;MONGOLIAN LETTER SIBE JA;Lo;0;L;;;;;N;;;;; +186B;MONGOLIAN LETTER SIBE FA;Lo;0;L;;;;;N;;;;; +186C;MONGOLIAN LETTER SIBE GAA;Lo;0;L;;;;;N;;;;; +186D;MONGOLIAN LETTER SIBE HAA;Lo;0;L;;;;;N;;;;; +186E;MONGOLIAN LETTER SIBE TSA;Lo;0;L;;;;;N;;;;; +186F;MONGOLIAN LETTER SIBE ZA;Lo;0;L;;;;;N;;;;; +1870;MONGOLIAN LETTER SIBE RAA;Lo;0;L;;;;;N;;;;; +1871;MONGOLIAN LETTER SIBE CHA;Lo;0;L;;;;;N;;;;; +1872;MONGOLIAN LETTER SIBE ZHA;Lo;0;L;;;;;N;;;;; +1873;MONGOLIAN LETTER MANCHU I;Lo;0;L;;;;;N;;;;; +1874;MONGOLIAN LETTER MANCHU KA;Lo;0;L;;;;;N;;;;; +1875;MONGOLIAN LETTER MANCHU RA;Lo;0;L;;;;;N;;;;; +1876;MONGOLIAN LETTER MANCHU FA;Lo;0;L;;;;;N;;;;; +1877;MONGOLIAN LETTER MANCHU ZHA;Lo;0;L;;;;;N;;;;; +1880;MONGOLIAN LETTER ALI GALI ANUSVARA ONE;Lo;0;L;;;;;N;;;;; +1881;MONGOLIAN LETTER ALI GALI VISARGA ONE;Lo;0;L;;;;;N;;;;; +1882;MONGOLIAN LETTER ALI GALI DAMARU;Lo;0;L;;;;;N;;;;; +1883;MONGOLIAN LETTER ALI GALI UBADAMA;Lo;0;L;;;;;N;;;;; +1884;MONGOLIAN LETTER ALI GALI INVERTED UBADAMA;Lo;0;L;;;;;N;;;;; +1885;MONGOLIAN LETTER ALI GALI BALUDA;Lo;0;L;;;;;N;;;;; +1886;MONGOLIAN LETTER ALI GALI THREE BALUDA;Lo;0;L;;;;;N;;;;; +1887;MONGOLIAN LETTER ALI GALI A;Lo;0;L;;;;;N;;;;; +1888;MONGOLIAN LETTER ALI GALI I;Lo;0;L;;;;;N;;;;; +1889;MONGOLIAN LETTER ALI GALI KA;Lo;0;L;;;;;N;;;;; +188A;MONGOLIAN LETTER ALI GALI NGA;Lo;0;L;;;;;N;;;;; +188B;MONGOLIAN LETTER ALI GALI CA;Lo;0;L;;;;;N;;;;; +188C;MONGOLIAN LETTER ALI GALI TTA;Lo;0;L;;;;;N;;;;; +188D;MONGOLIAN LETTER ALI GALI TTHA;Lo;0;L;;;;;N;;;;; +188E;MONGOLIAN LETTER ALI GALI DDA;Lo;0;L;;;;;N;;;;; +188F;MONGOLIAN LETTER ALI GALI NNA;Lo;0;L;;;;;N;;;;; +1890;MONGOLIAN LETTER ALI GALI TA;Lo;0;L;;;;;N;;;;; +1891;MONGOLIAN LETTER ALI GALI DA;Lo;0;L;;;;;N;;;;; +1892;MONGOLIAN LETTER ALI GALI PA;Lo;0;L;;;;;N;;;;; +1893;MONGOLIAN LETTER ALI GALI PHA;Lo;0;L;;;;;N;;;;; +1894;MONGOLIAN LETTER ALI GALI SSA;Lo;0;L;;;;;N;;;;; +1895;MONGOLIAN LETTER ALI GALI ZHA;Lo;0;L;;;;;N;;;;; +1896;MONGOLIAN LETTER ALI GALI ZA;Lo;0;L;;;;;N;;;;; +1897;MONGOLIAN LETTER ALI GALI AH;Lo;0;L;;;;;N;;;;; +1898;MONGOLIAN LETTER TODO ALI GALI TA;Lo;0;L;;;;;N;;;;; +1899;MONGOLIAN LETTER TODO ALI GALI ZHA;Lo;0;L;;;;;N;;;;; +189A;MONGOLIAN LETTER MANCHU ALI GALI GHA;Lo;0;L;;;;;N;;;;; +189B;MONGOLIAN LETTER MANCHU ALI GALI NGA;Lo;0;L;;;;;N;;;;; +189C;MONGOLIAN LETTER MANCHU ALI GALI CA;Lo;0;L;;;;;N;;;;; +189D;MONGOLIAN LETTER MANCHU ALI GALI JHA;Lo;0;L;;;;;N;;;;; +189E;MONGOLIAN LETTER MANCHU ALI GALI TTA;Lo;0;L;;;;;N;;;;; +189F;MONGOLIAN LETTER MANCHU ALI GALI DDHA;Lo;0;L;;;;;N;;;;; +18A0;MONGOLIAN LETTER MANCHU ALI GALI TA;Lo;0;L;;;;;N;;;;; +18A1;MONGOLIAN LETTER MANCHU ALI GALI DHA;Lo;0;L;;;;;N;;;;; +18A2;MONGOLIAN LETTER MANCHU ALI GALI SSA;Lo;0;L;;;;;N;;;;; +18A3;MONGOLIAN LETTER MANCHU ALI GALI CYA;Lo;0;L;;;;;N;;;;; +18A4;MONGOLIAN LETTER MANCHU ALI GALI ZHA;Lo;0;L;;;;;N;;;;; +18A5;MONGOLIAN LETTER MANCHU ALI GALI ZA;Lo;0;L;;;;;N;;;;; +18A6;MONGOLIAN LETTER ALI GALI HALF U;Lo;0;L;;;;;N;;;;; +18A7;MONGOLIAN LETTER ALI GALI HALF YA;Lo;0;L;;;;;N;;;;; +18A8;MONGOLIAN LETTER MANCHU ALI GALI BHA;Lo;0;L;;;;;N;;;;; +18A9;MONGOLIAN LETTER ALI GALI DAGALGA;Mn;228;NSM;;;;;N;;;;; +1E00;LATIN CAPITAL LETTER A WITH RING BELOW;Lu;0;L;0041 0325;;;;N;;;;1E01; +1E01;LATIN SMALL LETTER A WITH RING BELOW;Ll;0;L;0061 0325;;;;N;;;1E00;;1E00 +1E02;LATIN CAPITAL LETTER B WITH DOT ABOVE;Lu;0;L;0042 0307;;;;N;;;;1E03; +1E03;LATIN SMALL LETTER B WITH DOT ABOVE;Ll;0;L;0062 0307;;;;N;;;1E02;;1E02 +1E04;LATIN CAPITAL LETTER B WITH DOT BELOW;Lu;0;L;0042 0323;;;;N;;;;1E05; +1E05;LATIN SMALL LETTER B WITH DOT BELOW;Ll;0;L;0062 0323;;;;N;;;1E04;;1E04 +1E06;LATIN CAPITAL LETTER B WITH LINE BELOW;Lu;0;L;0042 0331;;;;N;;;;1E07; +1E07;LATIN SMALL LETTER B WITH LINE BELOW;Ll;0;L;0062 0331;;;;N;;;1E06;;1E06 +1E08;LATIN CAPITAL LETTER C WITH CEDILLA AND ACUTE;Lu;0;L;00C7 0301;;;;N;;;;1E09; +1E09;LATIN SMALL LETTER C WITH CEDILLA AND ACUTE;Ll;0;L;00E7 0301;;;;N;;;1E08;;1E08 +1E0A;LATIN CAPITAL LETTER D WITH DOT ABOVE;Lu;0;L;0044 0307;;;;N;;;;1E0B; +1E0B;LATIN SMALL LETTER D WITH DOT ABOVE;Ll;0;L;0064 0307;;;;N;;;1E0A;;1E0A +1E0C;LATIN CAPITAL LETTER D WITH DOT BELOW;Lu;0;L;0044 0323;;;;N;;;;1E0D; +1E0D;LATIN SMALL LETTER D WITH DOT BELOW;Ll;0;L;0064 0323;;;;N;;;1E0C;;1E0C +1E0E;LATIN CAPITAL LETTER D WITH LINE BELOW;Lu;0;L;0044 0331;;;;N;;;;1E0F; +1E0F;LATIN SMALL LETTER D WITH LINE BELOW;Ll;0;L;0064 0331;;;;N;;;1E0E;;1E0E +1E10;LATIN CAPITAL LETTER D WITH CEDILLA;Lu;0;L;0044 0327;;;;N;;;;1E11; +1E11;LATIN SMALL LETTER D WITH CEDILLA;Ll;0;L;0064 0327;;;;N;;;1E10;;1E10 +1E12;LATIN CAPITAL LETTER D WITH CIRCUMFLEX BELOW;Lu;0;L;0044 032D;;;;N;;;;1E13; +1E13;LATIN SMALL LETTER D WITH CIRCUMFLEX BELOW;Ll;0;L;0064 032D;;;;N;;;1E12;;1E12 +1E14;LATIN CAPITAL LETTER E WITH MACRON AND GRAVE;Lu;0;L;0112 0300;;;;N;;;;1E15; +1E15;LATIN SMALL LETTER E WITH MACRON AND GRAVE;Ll;0;L;0113 0300;;;;N;;;1E14;;1E14 +1E16;LATIN CAPITAL LETTER E WITH MACRON AND ACUTE;Lu;0;L;0112 0301;;;;N;;;;1E17; +1E17;LATIN SMALL LETTER E WITH MACRON AND ACUTE;Ll;0;L;0113 0301;;;;N;;;1E16;;1E16 +1E18;LATIN CAPITAL LETTER E WITH CIRCUMFLEX BELOW;Lu;0;L;0045 032D;;;;N;;;;1E19; +1E19;LATIN SMALL LETTER E WITH CIRCUMFLEX BELOW;Ll;0;L;0065 032D;;;;N;;;1E18;;1E18 +1E1A;LATIN CAPITAL LETTER E WITH TILDE BELOW;Lu;0;L;0045 0330;;;;N;;;;1E1B; +1E1B;LATIN SMALL LETTER E WITH TILDE BELOW;Ll;0;L;0065 0330;;;;N;;;1E1A;;1E1A +1E1C;LATIN CAPITAL LETTER E WITH CEDILLA AND BREVE;Lu;0;L;0228 0306;;;;N;;;;1E1D; +1E1D;LATIN SMALL LETTER E WITH CEDILLA AND BREVE;Ll;0;L;0229 0306;;;;N;;;1E1C;;1E1C +1E1E;LATIN CAPITAL LETTER F WITH DOT ABOVE;Lu;0;L;0046 0307;;;;N;;;;1E1F; +1E1F;LATIN SMALL LETTER F WITH DOT ABOVE;Ll;0;L;0066 0307;;;;N;;;1E1E;;1E1E +1E20;LATIN CAPITAL LETTER G WITH MACRON;Lu;0;L;0047 0304;;;;N;;;;1E21; +1E21;LATIN SMALL LETTER G WITH MACRON;Ll;0;L;0067 0304;;;;N;;;1E20;;1E20 +1E22;LATIN CAPITAL LETTER H WITH DOT ABOVE;Lu;0;L;0048 0307;;;;N;;;;1E23; +1E23;LATIN SMALL LETTER H WITH DOT ABOVE;Ll;0;L;0068 0307;;;;N;;;1E22;;1E22 +1E24;LATIN CAPITAL LETTER H WITH DOT BELOW;Lu;0;L;0048 0323;;;;N;;;;1E25; +1E25;LATIN SMALL LETTER H WITH DOT BELOW;Ll;0;L;0068 0323;;;;N;;;1E24;;1E24 +1E26;LATIN CAPITAL LETTER H WITH DIAERESIS;Lu;0;L;0048 0308;;;;N;;;;1E27; +1E27;LATIN SMALL LETTER H WITH DIAERESIS;Ll;0;L;0068 0308;;;;N;;;1E26;;1E26 +1E28;LATIN CAPITAL LETTER H WITH CEDILLA;Lu;0;L;0048 0327;;;;N;;;;1E29; +1E29;LATIN SMALL LETTER H WITH CEDILLA;Ll;0;L;0068 0327;;;;N;;;1E28;;1E28 +1E2A;LATIN CAPITAL LETTER H WITH BREVE BELOW;Lu;0;L;0048 032E;;;;N;;;;1E2B; +1E2B;LATIN SMALL LETTER H WITH BREVE BELOW;Ll;0;L;0068 032E;;;;N;;;1E2A;;1E2A +1E2C;LATIN CAPITAL LETTER I WITH TILDE BELOW;Lu;0;L;0049 0330;;;;N;;;;1E2D; +1E2D;LATIN SMALL LETTER I WITH TILDE BELOW;Ll;0;L;0069 0330;;;;N;;;1E2C;;1E2C +1E2E;LATIN CAPITAL LETTER I WITH DIAERESIS AND ACUTE;Lu;0;L;00CF 0301;;;;N;;;;1E2F; +1E2F;LATIN SMALL LETTER I WITH DIAERESIS AND ACUTE;Ll;0;L;00EF 0301;;;;N;;;1E2E;;1E2E +1E30;LATIN CAPITAL LETTER K WITH ACUTE;Lu;0;L;004B 0301;;;;N;;;;1E31; +1E31;LATIN SMALL LETTER K WITH ACUTE;Ll;0;L;006B 0301;;;;N;;;1E30;;1E30 +1E32;LATIN CAPITAL LETTER K WITH DOT BELOW;Lu;0;L;004B 0323;;;;N;;;;1E33; +1E33;LATIN SMALL LETTER K WITH DOT BELOW;Ll;0;L;006B 0323;;;;N;;;1E32;;1E32 +1E34;LATIN CAPITAL LETTER K WITH LINE BELOW;Lu;0;L;004B 0331;;;;N;;;;1E35; +1E35;LATIN SMALL LETTER K WITH LINE BELOW;Ll;0;L;006B 0331;;;;N;;;1E34;;1E34 +1E36;LATIN CAPITAL LETTER L WITH DOT BELOW;Lu;0;L;004C 0323;;;;N;;;;1E37; +1E37;LATIN SMALL LETTER L WITH DOT BELOW;Ll;0;L;006C 0323;;;;N;;;1E36;;1E36 +1E38;LATIN CAPITAL LETTER L WITH DOT BELOW AND MACRON;Lu;0;L;1E36 0304;;;;N;;;;1E39; +1E39;LATIN SMALL LETTER L WITH DOT BELOW AND MACRON;Ll;0;L;1E37 0304;;;;N;;;1E38;;1E38 +1E3A;LATIN CAPITAL LETTER L WITH LINE BELOW;Lu;0;L;004C 0331;;;;N;;;;1E3B; +1E3B;LATIN SMALL LETTER L WITH LINE BELOW;Ll;0;L;006C 0331;;;;N;;;1E3A;;1E3A +1E3C;LATIN CAPITAL LETTER L WITH CIRCUMFLEX BELOW;Lu;0;L;004C 032D;;;;N;;;;1E3D; +1E3D;LATIN SMALL LETTER L WITH CIRCUMFLEX BELOW;Ll;0;L;006C 032D;;;;N;;;1E3C;;1E3C +1E3E;LATIN CAPITAL LETTER M WITH ACUTE;Lu;0;L;004D 0301;;;;N;;;;1E3F; +1E3F;LATIN SMALL LETTER M WITH ACUTE;Ll;0;L;006D 0301;;;;N;;;1E3E;;1E3E +1E40;LATIN CAPITAL LETTER M WITH DOT ABOVE;Lu;0;L;004D 0307;;;;N;;;;1E41; +1E41;LATIN SMALL LETTER M WITH DOT ABOVE;Ll;0;L;006D 0307;;;;N;;;1E40;;1E40 +1E42;LATIN CAPITAL LETTER M WITH DOT BELOW;Lu;0;L;004D 0323;;;;N;;;;1E43; +1E43;LATIN SMALL LETTER M WITH DOT BELOW;Ll;0;L;006D 0323;;;;N;;;1E42;;1E42 +1E44;LATIN CAPITAL LETTER N WITH DOT ABOVE;Lu;0;L;004E 0307;;;;N;;;;1E45; +1E45;LATIN SMALL LETTER N WITH DOT ABOVE;Ll;0;L;006E 0307;;;;N;;;1E44;;1E44 +1E46;LATIN CAPITAL LETTER N WITH DOT BELOW;Lu;0;L;004E 0323;;;;N;;;;1E47; +1E47;LATIN SMALL LETTER N WITH DOT BELOW;Ll;0;L;006E 0323;;;;N;;;1E46;;1E46 +1E48;LATIN CAPITAL LETTER N WITH LINE BELOW;Lu;0;L;004E 0331;;;;N;;;;1E49; +1E49;LATIN SMALL LETTER N WITH LINE BELOW;Ll;0;L;006E 0331;;;;N;;;1E48;;1E48 +1E4A;LATIN CAPITAL LETTER N WITH CIRCUMFLEX BELOW;Lu;0;L;004E 032D;;;;N;;;;1E4B; +1E4B;LATIN SMALL LETTER N WITH CIRCUMFLEX BELOW;Ll;0;L;006E 032D;;;;N;;;1E4A;;1E4A +1E4C;LATIN CAPITAL LETTER O WITH TILDE AND ACUTE;Lu;0;L;00D5 0301;;;;N;;;;1E4D; +1E4D;LATIN SMALL LETTER O WITH TILDE AND ACUTE;Ll;0;L;00F5 0301;;;;N;;;1E4C;;1E4C +1E4E;LATIN CAPITAL LETTER O WITH TILDE AND DIAERESIS;Lu;0;L;00D5 0308;;;;N;;;;1E4F; +1E4F;LATIN SMALL LETTER O WITH TILDE AND DIAERESIS;Ll;0;L;00F5 0308;;;;N;;;1E4E;;1E4E +1E50;LATIN CAPITAL LETTER O WITH MACRON AND GRAVE;Lu;0;L;014C 0300;;;;N;;;;1E51; +1E51;LATIN SMALL LETTER O WITH MACRON AND GRAVE;Ll;0;L;014D 0300;;;;N;;;1E50;;1E50 +1E52;LATIN CAPITAL LETTER O WITH MACRON AND ACUTE;Lu;0;L;014C 0301;;;;N;;;;1E53; +1E53;LATIN SMALL LETTER O WITH MACRON AND ACUTE;Ll;0;L;014D 0301;;;;N;;;1E52;;1E52 +1E54;LATIN CAPITAL LETTER P WITH ACUTE;Lu;0;L;0050 0301;;;;N;;;;1E55; +1E55;LATIN SMALL LETTER P WITH ACUTE;Ll;0;L;0070 0301;;;;N;;;1E54;;1E54 +1E56;LATIN CAPITAL LETTER P WITH DOT ABOVE;Lu;0;L;0050 0307;;;;N;;;;1E57; +1E57;LATIN SMALL LETTER P WITH DOT ABOVE;Ll;0;L;0070 0307;;;;N;;;1E56;;1E56 +1E58;LATIN CAPITAL LETTER R WITH DOT ABOVE;Lu;0;L;0052 0307;;;;N;;;;1E59; +1E59;LATIN SMALL LETTER R WITH DOT ABOVE;Ll;0;L;0072 0307;;;;N;;;1E58;;1E58 +1E5A;LATIN CAPITAL LETTER R WITH DOT BELOW;Lu;0;L;0052 0323;;;;N;;;;1E5B; +1E5B;LATIN SMALL LETTER R WITH DOT BELOW;Ll;0;L;0072 0323;;;;N;;;1E5A;;1E5A +1E5C;LATIN CAPITAL LETTER R WITH DOT BELOW AND MACRON;Lu;0;L;1E5A 0304;;;;N;;;;1E5D; +1E5D;LATIN SMALL LETTER R WITH DOT BELOW AND MACRON;Ll;0;L;1E5B 0304;;;;N;;;1E5C;;1E5C +1E5E;LATIN CAPITAL LETTER R WITH LINE BELOW;Lu;0;L;0052 0331;;;;N;;;;1E5F; +1E5F;LATIN SMALL LETTER R WITH LINE BELOW;Ll;0;L;0072 0331;;;;N;;;1E5E;;1E5E +1E60;LATIN CAPITAL LETTER S WITH DOT ABOVE;Lu;0;L;0053 0307;;;;N;;;;1E61; +1E61;LATIN SMALL LETTER S WITH DOT ABOVE;Ll;0;L;0073 0307;;;;N;;;1E60;;1E60 +1E62;LATIN CAPITAL LETTER S WITH DOT BELOW;Lu;0;L;0053 0323;;;;N;;;;1E63; +1E63;LATIN SMALL LETTER S WITH DOT BELOW;Ll;0;L;0073 0323;;;;N;;;1E62;;1E62 +1E64;LATIN CAPITAL LETTER S WITH ACUTE AND DOT ABOVE;Lu;0;L;015A 0307;;;;N;;;;1E65; +1E65;LATIN SMALL LETTER S WITH ACUTE AND DOT ABOVE;Ll;0;L;015B 0307;;;;N;;;1E64;;1E64 +1E66;LATIN CAPITAL LETTER S WITH CARON AND DOT ABOVE;Lu;0;L;0160 0307;;;;N;;;;1E67; +1E67;LATIN SMALL LETTER S WITH CARON AND DOT ABOVE;Ll;0;L;0161 0307;;;;N;;;1E66;;1E66 +1E68;LATIN CAPITAL LETTER S WITH DOT BELOW AND DOT ABOVE;Lu;0;L;1E62 0307;;;;N;;;;1E69; +1E69;LATIN SMALL LETTER S WITH DOT BELOW AND DOT ABOVE;Ll;0;L;1E63 0307;;;;N;;;1E68;;1E68 +1E6A;LATIN CAPITAL LETTER T WITH DOT ABOVE;Lu;0;L;0054 0307;;;;N;;;;1E6B; +1E6B;LATIN SMALL LETTER T WITH DOT ABOVE;Ll;0;L;0074 0307;;;;N;;;1E6A;;1E6A +1E6C;LATIN CAPITAL LETTER T WITH DOT BELOW;Lu;0;L;0054 0323;;;;N;;;;1E6D; +1E6D;LATIN SMALL LETTER T WITH DOT BELOW;Ll;0;L;0074 0323;;;;N;;;1E6C;;1E6C +1E6E;LATIN CAPITAL LETTER T WITH LINE BELOW;Lu;0;L;0054 0331;;;;N;;;;1E6F; +1E6F;LATIN SMALL LETTER T WITH LINE BELOW;Ll;0;L;0074 0331;;;;N;;;1E6E;;1E6E +1E70;LATIN CAPITAL LETTER T WITH CIRCUMFLEX BELOW;Lu;0;L;0054 032D;;;;N;;;;1E71; +1E71;LATIN SMALL LETTER T WITH CIRCUMFLEX BELOW;Ll;0;L;0074 032D;;;;N;;;1E70;;1E70 +1E72;LATIN CAPITAL LETTER U WITH DIAERESIS BELOW;Lu;0;L;0055 0324;;;;N;;;;1E73; +1E73;LATIN SMALL LETTER U WITH DIAERESIS BELOW;Ll;0;L;0075 0324;;;;N;;;1E72;;1E72 +1E74;LATIN CAPITAL LETTER U WITH TILDE BELOW;Lu;0;L;0055 0330;;;;N;;;;1E75; +1E75;LATIN SMALL LETTER U WITH TILDE BELOW;Ll;0;L;0075 0330;;;;N;;;1E74;;1E74 +1E76;LATIN CAPITAL LETTER U WITH CIRCUMFLEX BELOW;Lu;0;L;0055 032D;;;;N;;;;1E77; +1E77;LATIN SMALL LETTER U WITH CIRCUMFLEX BELOW;Ll;0;L;0075 032D;;;;N;;;1E76;;1E76 +1E78;LATIN CAPITAL LETTER U WITH TILDE AND ACUTE;Lu;0;L;0168 0301;;;;N;;;;1E79; +1E79;LATIN SMALL LETTER U WITH TILDE AND ACUTE;Ll;0;L;0169 0301;;;;N;;;1E78;;1E78 +1E7A;LATIN CAPITAL LETTER U WITH MACRON AND DIAERESIS;Lu;0;L;016A 0308;;;;N;;;;1E7B; +1E7B;LATIN SMALL LETTER U WITH MACRON AND DIAERESIS;Ll;0;L;016B 0308;;;;N;;;1E7A;;1E7A +1E7C;LATIN CAPITAL LETTER V WITH TILDE;Lu;0;L;0056 0303;;;;N;;;;1E7D; +1E7D;LATIN SMALL LETTER V WITH TILDE;Ll;0;L;0076 0303;;;;N;;;1E7C;;1E7C +1E7E;LATIN CAPITAL LETTER V WITH DOT BELOW;Lu;0;L;0056 0323;;;;N;;;;1E7F; +1E7F;LATIN SMALL LETTER V WITH DOT BELOW;Ll;0;L;0076 0323;;;;N;;;1E7E;;1E7E +1E80;LATIN CAPITAL LETTER W WITH GRAVE;Lu;0;L;0057 0300;;;;N;;;;1E81; +1E81;LATIN SMALL LETTER W WITH GRAVE;Ll;0;L;0077 0300;;;;N;;;1E80;;1E80 +1E82;LATIN CAPITAL LETTER W WITH ACUTE;Lu;0;L;0057 0301;;;;N;;;;1E83; +1E83;LATIN SMALL LETTER W WITH ACUTE;Ll;0;L;0077 0301;;;;N;;;1E82;;1E82 +1E84;LATIN CAPITAL LETTER W WITH DIAERESIS;Lu;0;L;0057 0308;;;;N;;;;1E85; +1E85;LATIN SMALL LETTER W WITH DIAERESIS;Ll;0;L;0077 0308;;;;N;;;1E84;;1E84 +1E86;LATIN CAPITAL LETTER W WITH DOT ABOVE;Lu;0;L;0057 0307;;;;N;;;;1E87; +1E87;LATIN SMALL LETTER W WITH DOT ABOVE;Ll;0;L;0077 0307;;;;N;;;1E86;;1E86 +1E88;LATIN CAPITAL LETTER W WITH DOT BELOW;Lu;0;L;0057 0323;;;;N;;;;1E89; +1E89;LATIN SMALL LETTER W WITH DOT BELOW;Ll;0;L;0077 0323;;;;N;;;1E88;;1E88 +1E8A;LATIN CAPITAL LETTER X WITH DOT ABOVE;Lu;0;L;0058 0307;;;;N;;;;1E8B; +1E8B;LATIN SMALL LETTER X WITH DOT ABOVE;Ll;0;L;0078 0307;;;;N;;;1E8A;;1E8A +1E8C;LATIN CAPITAL LETTER X WITH DIAERESIS;Lu;0;L;0058 0308;;;;N;;;;1E8D; +1E8D;LATIN SMALL LETTER X WITH DIAERESIS;Ll;0;L;0078 0308;;;;N;;;1E8C;;1E8C +1E8E;LATIN CAPITAL LETTER Y WITH DOT ABOVE;Lu;0;L;0059 0307;;;;N;;;;1E8F; +1E8F;LATIN SMALL LETTER Y WITH DOT ABOVE;Ll;0;L;0079 0307;;;;N;;;1E8E;;1E8E +1E90;LATIN CAPITAL LETTER Z WITH CIRCUMFLEX;Lu;0;L;005A 0302;;;;N;;;;1E91; +1E91;LATIN SMALL LETTER Z WITH CIRCUMFLEX;Ll;0;L;007A 0302;;;;N;;;1E90;;1E90 +1E92;LATIN CAPITAL LETTER Z WITH DOT BELOW;Lu;0;L;005A 0323;;;;N;;;;1E93; +1E93;LATIN SMALL LETTER Z WITH DOT BELOW;Ll;0;L;007A 0323;;;;N;;;1E92;;1E92 +1E94;LATIN CAPITAL LETTER Z WITH LINE BELOW;Lu;0;L;005A 0331;;;;N;;;;1E95; +1E95;LATIN SMALL LETTER Z WITH LINE BELOW;Ll;0;L;007A 0331;;;;N;;;1E94;;1E94 +1E96;LATIN SMALL LETTER H WITH LINE BELOW;Ll;0;L;0068 0331;;;;N;;;;; +1E97;LATIN SMALL LETTER T WITH DIAERESIS;Ll;0;L;0074 0308;;;;N;;;;; +1E98;LATIN SMALL LETTER W WITH RING ABOVE;Ll;0;L;0077 030A;;;;N;;;;; +1E99;LATIN SMALL LETTER Y WITH RING ABOVE;Ll;0;L;0079 030A;;;;N;;;;; +1E9A;LATIN SMALL LETTER A WITH RIGHT HALF RING;Ll;0;L;<compat> 0061 02BE;;;;N;;;;; +1E9B;LATIN SMALL LETTER LONG S WITH DOT ABOVE;Ll;0;L;017F 0307;;;;N;;;1E60;;1E60 +1EA0;LATIN CAPITAL LETTER A WITH DOT BELOW;Lu;0;L;0041 0323;;;;N;;;;1EA1; +1EA1;LATIN SMALL LETTER A WITH DOT BELOW;Ll;0;L;0061 0323;;;;N;;;1EA0;;1EA0 +1EA2;LATIN CAPITAL LETTER A WITH HOOK ABOVE;Lu;0;L;0041 0309;;;;N;;;;1EA3; +1EA3;LATIN SMALL LETTER A WITH HOOK ABOVE;Ll;0;L;0061 0309;;;;N;;;1EA2;;1EA2 +1EA4;LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND ACUTE;Lu;0;L;00C2 0301;;;;N;;;;1EA5; +1EA5;LATIN SMALL LETTER A WITH CIRCUMFLEX AND ACUTE;Ll;0;L;00E2 0301;;;;N;;;1EA4;;1EA4 +1EA6;LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND GRAVE;Lu;0;L;00C2 0300;;;;N;;;;1EA7; +1EA7;LATIN SMALL LETTER A WITH CIRCUMFLEX AND GRAVE;Ll;0;L;00E2 0300;;;;N;;;1EA6;;1EA6 +1EA8;LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE;Lu;0;L;00C2 0309;;;;N;;;;1EA9; +1EA9;LATIN SMALL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE;Ll;0;L;00E2 0309;;;;N;;;1EA8;;1EA8 +1EAA;LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND TILDE;Lu;0;L;00C2 0303;;;;N;;;;1EAB; +1EAB;LATIN SMALL LETTER A WITH CIRCUMFLEX AND TILDE;Ll;0;L;00E2 0303;;;;N;;;1EAA;;1EAA +1EAC;LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND DOT BELOW;Lu;0;L;1EA0 0302;;;;N;;;;1EAD; +1EAD;LATIN SMALL LETTER A WITH CIRCUMFLEX AND DOT BELOW;Ll;0;L;1EA1 0302;;;;N;;;1EAC;;1EAC +1EAE;LATIN CAPITAL LETTER A WITH BREVE AND ACUTE;Lu;0;L;0102 0301;;;;N;;;;1EAF; +1EAF;LATIN SMALL LETTER A WITH BREVE AND ACUTE;Ll;0;L;0103 0301;;;;N;;;1EAE;;1EAE +1EB0;LATIN CAPITAL LETTER A WITH BREVE AND GRAVE;Lu;0;L;0102 0300;;;;N;;;;1EB1; +1EB1;LATIN SMALL LETTER A WITH BREVE AND GRAVE;Ll;0;L;0103 0300;;;;N;;;1EB0;;1EB0 +1EB2;LATIN CAPITAL LETTER A WITH BREVE AND HOOK ABOVE;Lu;0;L;0102 0309;;;;N;;;;1EB3; +1EB3;LATIN SMALL LETTER A WITH BREVE AND HOOK ABOVE;Ll;0;L;0103 0309;;;;N;;;1EB2;;1EB2 +1EB4;LATIN CAPITAL LETTER A WITH BREVE AND TILDE;Lu;0;L;0102 0303;;;;N;;;;1EB5; +1EB5;LATIN SMALL LETTER A WITH BREVE AND TILDE;Ll;0;L;0103 0303;;;;N;;;1EB4;;1EB4 +1EB6;LATIN CAPITAL LETTER A WITH BREVE AND DOT BELOW;Lu;0;L;1EA0 0306;;;;N;;;;1EB7; +1EB7;LATIN SMALL LETTER A WITH BREVE AND DOT BELOW;Ll;0;L;1EA1 0306;;;;N;;;1EB6;;1EB6 +1EB8;LATIN CAPITAL LETTER E WITH DOT BELOW;Lu;0;L;0045 0323;;;;N;;;;1EB9; +1EB9;LATIN SMALL LETTER E WITH DOT BELOW;Ll;0;L;0065 0323;;;;N;;;1EB8;;1EB8 +1EBA;LATIN CAPITAL LETTER E WITH HOOK ABOVE;Lu;0;L;0045 0309;;;;N;;;;1EBB; +1EBB;LATIN SMALL LETTER E WITH HOOK ABOVE;Ll;0;L;0065 0309;;;;N;;;1EBA;;1EBA +1EBC;LATIN CAPITAL LETTER E WITH TILDE;Lu;0;L;0045 0303;;;;N;;;;1EBD; +1EBD;LATIN SMALL LETTER E WITH TILDE;Ll;0;L;0065 0303;;;;N;;;1EBC;;1EBC +1EBE;LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND ACUTE;Lu;0;L;00CA 0301;;;;N;;;;1EBF; +1EBF;LATIN SMALL LETTER E WITH CIRCUMFLEX AND ACUTE;Ll;0;L;00EA 0301;;;;N;;;1EBE;;1EBE +1EC0;LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND GRAVE;Lu;0;L;00CA 0300;;;;N;;;;1EC1; +1EC1;LATIN SMALL LETTER E WITH CIRCUMFLEX AND GRAVE;Ll;0;L;00EA 0300;;;;N;;;1EC0;;1EC0 +1EC2;LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE;Lu;0;L;00CA 0309;;;;N;;;;1EC3; +1EC3;LATIN SMALL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE;Ll;0;L;00EA 0309;;;;N;;;1EC2;;1EC2 +1EC4;LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND TILDE;Lu;0;L;00CA 0303;;;;N;;;;1EC5; +1EC5;LATIN SMALL LETTER E WITH CIRCUMFLEX AND TILDE;Ll;0;L;00EA 0303;;;;N;;;1EC4;;1EC4 +1EC6;LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND DOT BELOW;Lu;0;L;1EB8 0302;;;;N;;;;1EC7; +1EC7;LATIN SMALL LETTER E WITH CIRCUMFLEX AND DOT BELOW;Ll;0;L;1EB9 0302;;;;N;;;1EC6;;1EC6 +1EC8;LATIN CAPITAL LETTER I WITH HOOK ABOVE;Lu;0;L;0049 0309;;;;N;;;;1EC9; +1EC9;LATIN SMALL LETTER I WITH HOOK ABOVE;Ll;0;L;0069 0309;;;;N;;;1EC8;;1EC8 +1ECA;LATIN CAPITAL LETTER I WITH DOT BELOW;Lu;0;L;0049 0323;;;;N;;;;1ECB; +1ECB;LATIN SMALL LETTER I WITH DOT BELOW;Ll;0;L;0069 0323;;;;N;;;1ECA;;1ECA +1ECC;LATIN CAPITAL LETTER O WITH DOT BELOW;Lu;0;L;004F 0323;;;;N;;;;1ECD; +1ECD;LATIN SMALL LETTER O WITH DOT BELOW;Ll;0;L;006F 0323;;;;N;;;1ECC;;1ECC +1ECE;LATIN CAPITAL LETTER O WITH HOOK ABOVE;Lu;0;L;004F 0309;;;;N;;;;1ECF; +1ECF;LATIN SMALL LETTER O WITH HOOK ABOVE;Ll;0;L;006F 0309;;;;N;;;1ECE;;1ECE +1ED0;LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND ACUTE;Lu;0;L;00D4 0301;;;;N;;;;1ED1; +1ED1;LATIN SMALL LETTER O WITH CIRCUMFLEX AND ACUTE;Ll;0;L;00F4 0301;;;;N;;;1ED0;;1ED0 +1ED2;LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND GRAVE;Lu;0;L;00D4 0300;;;;N;;;;1ED3; +1ED3;LATIN SMALL LETTER O WITH CIRCUMFLEX AND GRAVE;Ll;0;L;00F4 0300;;;;N;;;1ED2;;1ED2 +1ED4;LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE;Lu;0;L;00D4 0309;;;;N;;;;1ED5; +1ED5;LATIN SMALL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE;Ll;0;L;00F4 0309;;;;N;;;1ED4;;1ED4 +1ED6;LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND TILDE;Lu;0;L;00D4 0303;;;;N;;;;1ED7; +1ED7;LATIN SMALL LETTER O WITH CIRCUMFLEX AND TILDE;Ll;0;L;00F4 0303;;;;N;;;1ED6;;1ED6 +1ED8;LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND DOT BELOW;Lu;0;L;1ECC 0302;;;;N;;;;1ED9; +1ED9;LATIN SMALL LETTER O WITH CIRCUMFLEX AND DOT BELOW;Ll;0;L;1ECD 0302;;;;N;;;1ED8;;1ED8 +1EDA;LATIN CAPITAL LETTER O WITH HORN AND ACUTE;Lu;0;L;01A0 0301;;;;N;;;;1EDB; +1EDB;LATIN SMALL LETTER O WITH HORN AND ACUTE;Ll;0;L;01A1 0301;;;;N;;;1EDA;;1EDA +1EDC;LATIN CAPITAL LETTER O WITH HORN AND GRAVE;Lu;0;L;01A0 0300;;;;N;;;;1EDD; +1EDD;LATIN SMALL LETTER O WITH HORN AND GRAVE;Ll;0;L;01A1 0300;;;;N;;;1EDC;;1EDC +1EDE;LATIN CAPITAL LETTER O WITH HORN AND HOOK ABOVE;Lu;0;L;01A0 0309;;;;N;;;;1EDF; +1EDF;LATIN SMALL LETTER O WITH HORN AND HOOK ABOVE;Ll;0;L;01A1 0309;;;;N;;;1EDE;;1EDE +1EE0;LATIN CAPITAL LETTER O WITH HORN AND TILDE;Lu;0;L;01A0 0303;;;;N;;;;1EE1; +1EE1;LATIN SMALL LETTER O WITH HORN AND TILDE;Ll;0;L;01A1 0303;;;;N;;;1EE0;;1EE0 +1EE2;LATIN CAPITAL LETTER O WITH HORN AND DOT BELOW;Lu;0;L;01A0 0323;;;;N;;;;1EE3; +1EE3;LATIN SMALL LETTER O WITH HORN AND DOT BELOW;Ll;0;L;01A1 0323;;;;N;;;1EE2;;1EE2 +1EE4;LATIN CAPITAL LETTER U WITH DOT BELOW;Lu;0;L;0055 0323;;;;N;;;;1EE5; +1EE5;LATIN SMALL LETTER U WITH DOT BELOW;Ll;0;L;0075 0323;;;;N;;;1EE4;;1EE4 +1EE6;LATIN CAPITAL LETTER U WITH HOOK ABOVE;Lu;0;L;0055 0309;;;;N;;;;1EE7; +1EE7;LATIN SMALL LETTER U WITH HOOK ABOVE;Ll;0;L;0075 0309;;;;N;;;1EE6;;1EE6 +1EE8;LATIN CAPITAL LETTER U WITH HORN AND ACUTE;Lu;0;L;01AF 0301;;;;N;;;;1EE9; +1EE9;LATIN SMALL LETTER U WITH HORN AND ACUTE;Ll;0;L;01B0 0301;;;;N;;;1EE8;;1EE8 +1EEA;LATIN CAPITAL LETTER U WITH HORN AND GRAVE;Lu;0;L;01AF 0300;;;;N;;;;1EEB; +1EEB;LATIN SMALL LETTER U WITH HORN AND GRAVE;Ll;0;L;01B0 0300;;;;N;;;1EEA;;1EEA +1EEC;LATIN CAPITAL LETTER U WITH HORN AND HOOK ABOVE;Lu;0;L;01AF 0309;;;;N;;;;1EED; +1EED;LATIN SMALL LETTER U WITH HORN AND HOOK ABOVE;Ll;0;L;01B0 0309;;;;N;;;1EEC;;1EEC +1EEE;LATIN CAPITAL LETTER U WITH HORN AND TILDE;Lu;0;L;01AF 0303;;;;N;;;;1EEF; +1EEF;LATIN SMALL LETTER U WITH HORN AND TILDE;Ll;0;L;01B0 0303;;;;N;;;1EEE;;1EEE +1EF0;LATIN CAPITAL LETTER U WITH HORN AND DOT BELOW;Lu;0;L;01AF 0323;;;;N;;;;1EF1; +1EF1;LATIN SMALL LETTER U WITH HORN AND DOT BELOW;Ll;0;L;01B0 0323;;;;N;;;1EF0;;1EF0 +1EF2;LATIN CAPITAL LETTER Y WITH GRAVE;Lu;0;L;0059 0300;;;;N;;;;1EF3; +1EF3;LATIN SMALL LETTER Y WITH GRAVE;Ll;0;L;0079 0300;;;;N;;;1EF2;;1EF2 +1EF4;LATIN CAPITAL LETTER Y WITH DOT BELOW;Lu;0;L;0059 0323;;;;N;;;;1EF5; +1EF5;LATIN SMALL LETTER Y WITH DOT BELOW;Ll;0;L;0079 0323;;;;N;;;1EF4;;1EF4 +1EF6;LATIN CAPITAL LETTER Y WITH HOOK ABOVE;Lu;0;L;0059 0309;;;;N;;;;1EF7; +1EF7;LATIN SMALL LETTER Y WITH HOOK ABOVE;Ll;0;L;0079 0309;;;;N;;;1EF6;;1EF6 +1EF8;LATIN CAPITAL LETTER Y WITH TILDE;Lu;0;L;0059 0303;;;;N;;;;1EF9; +1EF9;LATIN SMALL LETTER Y WITH TILDE;Ll;0;L;0079 0303;;;;N;;;1EF8;;1EF8 +1F00;GREEK SMALL LETTER ALPHA WITH PSILI;Ll;0;L;03B1 0313;;;;N;;;1F08;;1F08 +1F01;GREEK SMALL LETTER ALPHA WITH DASIA;Ll;0;L;03B1 0314;;;;N;;;1F09;;1F09 +1F02;GREEK SMALL LETTER ALPHA WITH PSILI AND VARIA;Ll;0;L;1F00 0300;;;;N;;;1F0A;;1F0A +1F03;GREEK SMALL LETTER ALPHA WITH DASIA AND VARIA;Ll;0;L;1F01 0300;;;;N;;;1F0B;;1F0B +1F04;GREEK SMALL LETTER ALPHA WITH PSILI AND OXIA;Ll;0;L;1F00 0301;;;;N;;;1F0C;;1F0C +1F05;GREEK SMALL LETTER ALPHA WITH DASIA AND OXIA;Ll;0;L;1F01 0301;;;;N;;;1F0D;;1F0D +1F06;GREEK SMALL LETTER ALPHA WITH PSILI AND PERISPOMENI;Ll;0;L;1F00 0342;;;;N;;;1F0E;;1F0E +1F07;GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI;Ll;0;L;1F01 0342;;;;N;;;1F0F;;1F0F +1F08;GREEK CAPITAL LETTER ALPHA WITH PSILI;Lu;0;L;0391 0313;;;;N;;;;1F00; +1F09;GREEK CAPITAL LETTER ALPHA WITH DASIA;Lu;0;L;0391 0314;;;;N;;;;1F01; +1F0A;GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA;Lu;0;L;1F08 0300;;;;N;;;;1F02; +1F0B;GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA;Lu;0;L;1F09 0300;;;;N;;;;1F03; +1F0C;GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA;Lu;0;L;1F08 0301;;;;N;;;;1F04; +1F0D;GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA;Lu;0;L;1F09 0301;;;;N;;;;1F05; +1F0E;GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI;Lu;0;L;1F08 0342;;;;N;;;;1F06; +1F0F;GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI;Lu;0;L;1F09 0342;;;;N;;;;1F07; +1F10;GREEK SMALL LETTER EPSILON WITH PSILI;Ll;0;L;03B5 0313;;;;N;;;1F18;;1F18 +1F11;GREEK SMALL LETTER EPSILON WITH DASIA;Ll;0;L;03B5 0314;;;;N;;;1F19;;1F19 +1F12;GREEK SMALL LETTER EPSILON WITH PSILI AND VARIA;Ll;0;L;1F10 0300;;;;N;;;1F1A;;1F1A +1F13;GREEK SMALL LETTER EPSILON WITH DASIA AND VARIA;Ll;0;L;1F11 0300;;;;N;;;1F1B;;1F1B +1F14;GREEK SMALL LETTER EPSILON WITH PSILI AND OXIA;Ll;0;L;1F10 0301;;;;N;;;1F1C;;1F1C +1F15;GREEK SMALL LETTER EPSILON WITH DASIA AND OXIA;Ll;0;L;1F11 0301;;;;N;;;1F1D;;1F1D +1F18;GREEK CAPITAL LETTER EPSILON WITH PSILI;Lu;0;L;0395 0313;;;;N;;;;1F10; +1F19;GREEK CAPITAL LETTER EPSILON WITH DASIA;Lu;0;L;0395 0314;;;;N;;;;1F11; +1F1A;GREEK CAPITAL LETTER EPSILON WITH PSILI AND VARIA;Lu;0;L;1F18 0300;;;;N;;;;1F12; +1F1B;GREEK CAPITAL LETTER EPSILON WITH DASIA AND VARIA;Lu;0;L;1F19 0300;;;;N;;;;1F13; +1F1C;GREEK CAPITAL LETTER EPSILON WITH PSILI AND OXIA;Lu;0;L;1F18 0301;;;;N;;;;1F14; +1F1D;GREEK CAPITAL LETTER EPSILON WITH DASIA AND OXIA;Lu;0;L;1F19 0301;;;;N;;;;1F15; +1F20;GREEK SMALL LETTER ETA WITH PSILI;Ll;0;L;03B7 0313;;;;N;;;1F28;;1F28 +1F21;GREEK SMALL LETTER ETA WITH DASIA;Ll;0;L;03B7 0314;;;;N;;;1F29;;1F29 +1F22;GREEK SMALL LETTER ETA WITH PSILI AND VARIA;Ll;0;L;1F20 0300;;;;N;;;1F2A;;1F2A +1F23;GREEK SMALL LETTER ETA WITH DASIA AND VARIA;Ll;0;L;1F21 0300;;;;N;;;1F2B;;1F2B +1F24;GREEK SMALL LETTER ETA WITH PSILI AND OXIA;Ll;0;L;1F20 0301;;;;N;;;1F2C;;1F2C +1F25;GREEK SMALL LETTER ETA WITH DASIA AND OXIA;Ll;0;L;1F21 0301;;;;N;;;1F2D;;1F2D +1F26;GREEK SMALL LETTER ETA WITH PSILI AND PERISPOMENI;Ll;0;L;1F20 0342;;;;N;;;1F2E;;1F2E +1F27;GREEK SMALL LETTER ETA WITH DASIA AND PERISPOMENI;Ll;0;L;1F21 0342;;;;N;;;1F2F;;1F2F +1F28;GREEK CAPITAL LETTER ETA WITH PSILI;Lu;0;L;0397 0313;;;;N;;;;1F20; +1F29;GREEK CAPITAL LETTER ETA WITH DASIA;Lu;0;L;0397 0314;;;;N;;;;1F21; +1F2A;GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA;Lu;0;L;1F28 0300;;;;N;;;;1F22; +1F2B;GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA;Lu;0;L;1F29 0300;;;;N;;;;1F23; +1F2C;GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA;Lu;0;L;1F28 0301;;;;N;;;;1F24; +1F2D;GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA;Lu;0;L;1F29 0301;;;;N;;;;1F25; +1F2E;GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI;Lu;0;L;1F28 0342;;;;N;;;;1F26; +1F2F;GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI;Lu;0;L;1F29 0342;;;;N;;;;1F27; +1F30;GREEK SMALL LETTER IOTA WITH PSILI;Ll;0;L;03B9 0313;;;;N;;;1F38;;1F38 +1F31;GREEK SMALL LETTER IOTA WITH DASIA;Ll;0;L;03B9 0314;;;;N;;;1F39;;1F39 +1F32;GREEK SMALL LETTER IOTA WITH PSILI AND VARIA;Ll;0;L;1F30 0300;;;;N;;;1F3A;;1F3A +1F33;GREEK SMALL LETTER IOTA WITH DASIA AND VARIA;Ll;0;L;1F31 0300;;;;N;;;1F3B;;1F3B +1F34;GREEK SMALL LETTER IOTA WITH PSILI AND OXIA;Ll;0;L;1F30 0301;;;;N;;;1F3C;;1F3C +1F35;GREEK SMALL LETTER IOTA WITH DASIA AND OXIA;Ll;0;L;1F31 0301;;;;N;;;1F3D;;1F3D +1F36;GREEK SMALL LETTER IOTA WITH PSILI AND PERISPOMENI;Ll;0;L;1F30 0342;;;;N;;;1F3E;;1F3E +1F37;GREEK SMALL LETTER IOTA WITH DASIA AND PERISPOMENI;Ll;0;L;1F31 0342;;;;N;;;1F3F;;1F3F +1F38;GREEK CAPITAL LETTER IOTA WITH PSILI;Lu;0;L;0399 0313;;;;N;;;;1F30; +1F39;GREEK CAPITAL LETTER IOTA WITH DASIA;Lu;0;L;0399 0314;;;;N;;;;1F31; +1F3A;GREEK CAPITAL LETTER IOTA WITH PSILI AND VARIA;Lu;0;L;1F38 0300;;;;N;;;;1F32; +1F3B;GREEK CAPITAL LETTER IOTA WITH DASIA AND VARIA;Lu;0;L;1F39 0300;;;;N;;;;1F33; +1F3C;GREEK CAPITAL LETTER IOTA WITH PSILI AND OXIA;Lu;0;L;1F38 0301;;;;N;;;;1F34; +1F3D;GREEK CAPITAL LETTER IOTA WITH DASIA AND OXIA;Lu;0;L;1F39 0301;;;;N;;;;1F35; +1F3E;GREEK CAPITAL LETTER IOTA WITH PSILI AND PERISPOMENI;Lu;0;L;1F38 0342;;;;N;;;;1F36; +1F3F;GREEK CAPITAL LETTER IOTA WITH DASIA AND PERISPOMENI;Lu;0;L;1F39 0342;;;;N;;;;1F37; +1F40;GREEK SMALL LETTER OMICRON WITH PSILI;Ll;0;L;03BF 0313;;;;N;;;1F48;;1F48 +1F41;GREEK SMALL LETTER OMICRON WITH DASIA;Ll;0;L;03BF 0314;;;;N;;;1F49;;1F49 +1F42;GREEK SMALL LETTER OMICRON WITH PSILI AND VARIA;Ll;0;L;1F40 0300;;;;N;;;1F4A;;1F4A +1F43;GREEK SMALL LETTER OMICRON WITH DASIA AND VARIA;Ll;0;L;1F41 0300;;;;N;;;1F4B;;1F4B +1F44;GREEK SMALL LETTER OMICRON WITH PSILI AND OXIA;Ll;0;L;1F40 0301;;;;N;;;1F4C;;1F4C +1F45;GREEK SMALL LETTER OMICRON WITH DASIA AND OXIA;Ll;0;L;1F41 0301;;;;N;;;1F4D;;1F4D +1F48;GREEK CAPITAL LETTER OMICRON WITH PSILI;Lu;0;L;039F 0313;;;;N;;;;1F40; +1F49;GREEK CAPITAL LETTER OMICRON WITH DASIA;Lu;0;L;039F 0314;;;;N;;;;1F41; +1F4A;GREEK CAPITAL LETTER OMICRON WITH PSILI AND VARIA;Lu;0;L;1F48 0300;;;;N;;;;1F42; +1F4B;GREEK CAPITAL LETTER OMICRON WITH DASIA AND VARIA;Lu;0;L;1F49 0300;;;;N;;;;1F43; +1F4C;GREEK CAPITAL LETTER OMICRON WITH PSILI AND OXIA;Lu;0;L;1F48 0301;;;;N;;;;1F44; +1F4D;GREEK CAPITAL LETTER OMICRON WITH DASIA AND OXIA;Lu;0;L;1F49 0301;;;;N;;;;1F45; +1F50;GREEK SMALL LETTER UPSILON WITH PSILI;Ll;0;L;03C5 0313;;;;N;;;;; +1F51;GREEK SMALL LETTER UPSILON WITH DASIA;Ll;0;L;03C5 0314;;;;N;;;1F59;;1F59 +1F52;GREEK SMALL LETTER UPSILON WITH PSILI AND VARIA;Ll;0;L;1F50 0300;;;;N;;;;; +1F53;GREEK SMALL LETTER UPSILON WITH DASIA AND VARIA;Ll;0;L;1F51 0300;;;;N;;;1F5B;;1F5B +1F54;GREEK SMALL LETTER UPSILON WITH PSILI AND OXIA;Ll;0;L;1F50 0301;;;;N;;;;; +1F55;GREEK SMALL LETTER UPSILON WITH DASIA AND OXIA;Ll;0;L;1F51 0301;;;;N;;;1F5D;;1F5D +1F56;GREEK SMALL LETTER UPSILON WITH PSILI AND PERISPOMENI;Ll;0;L;1F50 0342;;;;N;;;;; +1F57;GREEK SMALL LETTER UPSILON WITH DASIA AND PERISPOMENI;Ll;0;L;1F51 0342;;;;N;;;1F5F;;1F5F +1F59;GREEK CAPITAL LETTER UPSILON WITH DASIA;Lu;0;L;03A5 0314;;;;N;;;;1F51; +1F5B;GREEK CAPITAL LETTER UPSILON WITH DASIA AND VARIA;Lu;0;L;1F59 0300;;;;N;;;;1F53; +1F5D;GREEK CAPITAL LETTER UPSILON WITH DASIA AND OXIA;Lu;0;L;1F59 0301;;;;N;;;;1F55; +1F5F;GREEK CAPITAL LETTER UPSILON WITH DASIA AND PERISPOMENI;Lu;0;L;1F59 0342;;;;N;;;;1F57; +1F60;GREEK SMALL LETTER OMEGA WITH PSILI;Ll;0;L;03C9 0313;;;;N;;;1F68;;1F68 +1F61;GREEK SMALL LETTER OMEGA WITH DASIA;Ll;0;L;03C9 0314;;;;N;;;1F69;;1F69 +1F62;GREEK SMALL LETTER OMEGA WITH PSILI AND VARIA;Ll;0;L;1F60 0300;;;;N;;;1F6A;;1F6A +1F63;GREEK SMALL LETTER OMEGA WITH DASIA AND VARIA;Ll;0;L;1F61 0300;;;;N;;;1F6B;;1F6B +1F64;GREEK SMALL LETTER OMEGA WITH PSILI AND OXIA;Ll;0;L;1F60 0301;;;;N;;;1F6C;;1F6C +1F65;GREEK SMALL LETTER OMEGA WITH DASIA AND OXIA;Ll;0;L;1F61 0301;;;;N;;;1F6D;;1F6D +1F66;GREEK SMALL LETTER OMEGA WITH PSILI AND PERISPOMENI;Ll;0;L;1F60 0342;;;;N;;;1F6E;;1F6E +1F67;GREEK SMALL LETTER OMEGA WITH DASIA AND PERISPOMENI;Ll;0;L;1F61 0342;;;;N;;;1F6F;;1F6F +1F68;GREEK CAPITAL LETTER OMEGA WITH PSILI;Lu;0;L;03A9 0313;;;;N;;;;1F60; +1F69;GREEK CAPITAL LETTER OMEGA WITH DASIA;Lu;0;L;03A9 0314;;;;N;;;;1F61; +1F6A;GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA;Lu;0;L;1F68 0300;;;;N;;;;1F62; +1F6B;GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA;Lu;0;L;1F69 0300;;;;N;;;;1F63; +1F6C;GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA;Lu;0;L;1F68 0301;;;;N;;;;1F64; +1F6D;GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA;Lu;0;L;1F69 0301;;;;N;;;;1F65; +1F6E;GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI;Lu;0;L;1F68 0342;;;;N;;;;1F66; +1F6F;GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI;Lu;0;L;1F69 0342;;;;N;;;;1F67; +1F70;GREEK SMALL LETTER ALPHA WITH VARIA;Ll;0;L;03B1 0300;;;;N;;;1FBA;;1FBA +1F71;GREEK SMALL LETTER ALPHA WITH OXIA;Ll;0;L;03AC;;;;N;;;1FBB;;1FBB +1F72;GREEK SMALL LETTER EPSILON WITH VARIA;Ll;0;L;03B5 0300;;;;N;;;1FC8;;1FC8 +1F73;GREEK SMALL LETTER EPSILON WITH OXIA;Ll;0;L;03AD;;;;N;;;1FC9;;1FC9 +1F74;GREEK SMALL LETTER ETA WITH VARIA;Ll;0;L;03B7 0300;;;;N;;;1FCA;;1FCA +1F75;GREEK SMALL LETTER ETA WITH OXIA;Ll;0;L;03AE;;;;N;;;1FCB;;1FCB +1F76;GREEK SMALL LETTER IOTA WITH VARIA;Ll;0;L;03B9 0300;;;;N;;;1FDA;;1FDA +1F77;GREEK SMALL LETTER IOTA WITH OXIA;Ll;0;L;03AF;;;;N;;;1FDB;;1FDB +1F78;GREEK SMALL LETTER OMICRON WITH VARIA;Ll;0;L;03BF 0300;;;;N;;;1FF8;;1FF8 +1F79;GREEK SMALL LETTER OMICRON WITH OXIA;Ll;0;L;03CC;;;;N;;;1FF9;;1FF9 +1F7A;GREEK SMALL LETTER UPSILON WITH VARIA;Ll;0;L;03C5 0300;;;;N;;;1FEA;;1FEA +1F7B;GREEK SMALL LETTER UPSILON WITH OXIA;Ll;0;L;03CD;;;;N;;;1FEB;;1FEB +1F7C;GREEK SMALL LETTER OMEGA WITH VARIA;Ll;0;L;03C9 0300;;;;N;;;1FFA;;1FFA +1F7D;GREEK SMALL LETTER OMEGA WITH OXIA;Ll;0;L;03CE;;;;N;;;1FFB;;1FFB +1F80;GREEK SMALL LETTER ALPHA WITH PSILI AND YPOGEGRAMMENI;Ll;0;L;1F00 0345;;;;N;;;1F88;;1F88 +1F81;GREEK SMALL LETTER ALPHA WITH DASIA AND YPOGEGRAMMENI;Ll;0;L;1F01 0345;;;;N;;;1F89;;1F89 +1F82;GREEK SMALL LETTER ALPHA WITH PSILI AND VARIA AND YPOGEGRAMMENI;Ll;0;L;1F02 0345;;;;N;;;1F8A;;1F8A +1F83;GREEK SMALL LETTER ALPHA WITH DASIA AND VARIA AND YPOGEGRAMMENI;Ll;0;L;1F03 0345;;;;N;;;1F8B;;1F8B +1F84;GREEK SMALL LETTER ALPHA WITH PSILI AND OXIA AND YPOGEGRAMMENI;Ll;0;L;1F04 0345;;;;N;;;1F8C;;1F8C +1F85;GREEK SMALL LETTER ALPHA WITH DASIA AND OXIA AND YPOGEGRAMMENI;Ll;0;L;1F05 0345;;;;N;;;1F8D;;1F8D +1F86;GREEK SMALL LETTER ALPHA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1F06 0345;;;;N;;;1F8E;;1F8E +1F87;GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1F07 0345;;;;N;;;1F8F;;1F8F +1F88;GREEK CAPITAL LETTER ALPHA WITH PSILI AND PROSGEGRAMMENI;Lt;0;L;1F08 0345;;;;N;;;;1F80; +1F89;GREEK CAPITAL LETTER ALPHA WITH DASIA AND PROSGEGRAMMENI;Lt;0;L;1F09 0345;;;;N;;;;1F81; +1F8A;GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA AND PROSGEGRAMMENI;Lt;0;L;1F0A 0345;;;;N;;;;1F82; +1F8B;GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA AND PROSGEGRAMMENI;Lt;0;L;1F0B 0345;;;;N;;;;1F83; +1F8C;GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA AND PROSGEGRAMMENI;Lt;0;L;1F0C 0345;;;;N;;;;1F84; +1F8D;GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA AND PROSGEGRAMMENI;Lt;0;L;1F0D 0345;;;;N;;;;1F85; +1F8E;GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI;Lt;0;L;1F0E 0345;;;;N;;;;1F86; +1F8F;GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI;Lt;0;L;1F0F 0345;;;;N;;;;1F87; +1F90;GREEK SMALL LETTER ETA WITH PSILI AND YPOGEGRAMMENI;Ll;0;L;1F20 0345;;;;N;;;1F98;;1F98 +1F91;GREEK SMALL LETTER ETA WITH DASIA AND YPOGEGRAMMENI;Ll;0;L;1F21 0345;;;;N;;;1F99;;1F99 +1F92;GREEK SMALL LETTER ETA WITH PSILI AND VARIA AND YPOGEGRAMMENI;Ll;0;L;1F22 0345;;;;N;;;1F9A;;1F9A +1F93;GREEK SMALL LETTER ETA WITH DASIA AND VARIA AND YPOGEGRAMMENI;Ll;0;L;1F23 0345;;;;N;;;1F9B;;1F9B +1F94;GREEK SMALL LETTER ETA WITH PSILI AND OXIA AND YPOGEGRAMMENI;Ll;0;L;1F24 0345;;;;N;;;1F9C;;1F9C +1F95;GREEK SMALL LETTER ETA WITH DASIA AND OXIA AND YPOGEGRAMMENI;Ll;0;L;1F25 0345;;;;N;;;1F9D;;1F9D +1F96;GREEK SMALL LETTER ETA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1F26 0345;;;;N;;;1F9E;;1F9E +1F97;GREEK SMALL LETTER ETA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1F27 0345;;;;N;;;1F9F;;1F9F +1F98;GREEK CAPITAL LETTER ETA WITH PSILI AND PROSGEGRAMMENI;Lt;0;L;1F28 0345;;;;N;;;;1F90; +1F99;GREEK CAPITAL LETTER ETA WITH DASIA AND PROSGEGRAMMENI;Lt;0;L;1F29 0345;;;;N;;;;1F91; +1F9A;GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA AND PROSGEGRAMMENI;Lt;0;L;1F2A 0345;;;;N;;;;1F92; +1F9B;GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA AND PROSGEGRAMMENI;Lt;0;L;1F2B 0345;;;;N;;;;1F93; +1F9C;GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA AND PROSGEGRAMMENI;Lt;0;L;1F2C 0345;;;;N;;;;1F94; +1F9D;GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA AND PROSGEGRAMMENI;Lt;0;L;1F2D 0345;;;;N;;;;1F95; +1F9E;GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI;Lt;0;L;1F2E 0345;;;;N;;;;1F96; +1F9F;GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI;Lt;0;L;1F2F 0345;;;;N;;;;1F97; +1FA0;GREEK SMALL LETTER OMEGA WITH PSILI AND YPOGEGRAMMENI;Ll;0;L;1F60 0345;;;;N;;;1FA8;;1FA8 +1FA1;GREEK SMALL LETTER OMEGA WITH DASIA AND YPOGEGRAMMENI;Ll;0;L;1F61 0345;;;;N;;;1FA9;;1FA9 +1FA2;GREEK SMALL LETTER OMEGA WITH PSILI AND VARIA AND YPOGEGRAMMENI;Ll;0;L;1F62 0345;;;;N;;;1FAA;;1FAA +1FA3;GREEK SMALL LETTER OMEGA WITH DASIA AND VARIA AND YPOGEGRAMMENI;Ll;0;L;1F63 0345;;;;N;;;1FAB;;1FAB +1FA4;GREEK SMALL LETTER OMEGA WITH PSILI AND OXIA AND YPOGEGRAMMENI;Ll;0;L;1F64 0345;;;;N;;;1FAC;;1FAC +1FA5;GREEK SMALL LETTER OMEGA WITH DASIA AND OXIA AND YPOGEGRAMMENI;Ll;0;L;1F65 0345;;;;N;;;1FAD;;1FAD +1FA6;GREEK SMALL LETTER OMEGA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1F66 0345;;;;N;;;1FAE;;1FAE +1FA7;GREEK SMALL LETTER OMEGA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1F67 0345;;;;N;;;1FAF;;1FAF +1FA8;GREEK CAPITAL LETTER OMEGA WITH PSILI AND PROSGEGRAMMENI;Lt;0;L;1F68 0345;;;;N;;;;1FA0; +1FA9;GREEK CAPITAL LETTER OMEGA WITH DASIA AND PROSGEGRAMMENI;Lt;0;L;1F69 0345;;;;N;;;;1FA1; +1FAA;GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA AND PROSGEGRAMMENI;Lt;0;L;1F6A 0345;;;;N;;;;1FA2; +1FAB;GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA AND PROSGEGRAMMENI;Lt;0;L;1F6B 0345;;;;N;;;;1FA3; +1FAC;GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA AND PROSGEGRAMMENI;Lt;0;L;1F6C 0345;;;;N;;;;1FA4; +1FAD;GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA AND PROSGEGRAMMENI;Lt;0;L;1F6D 0345;;;;N;;;;1FA5; +1FAE;GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI;Lt;0;L;1F6E 0345;;;;N;;;;1FA6; +1FAF;GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI;Lt;0;L;1F6F 0345;;;;N;;;;1FA7; +1FB0;GREEK SMALL LETTER ALPHA WITH VRACHY;Ll;0;L;03B1 0306;;;;N;;;1FB8;;1FB8 +1FB1;GREEK SMALL LETTER ALPHA WITH MACRON;Ll;0;L;03B1 0304;;;;N;;;1FB9;;1FB9 +1FB2;GREEK SMALL LETTER ALPHA WITH VARIA AND YPOGEGRAMMENI;Ll;0;L;1F70 0345;;;;N;;;;; +1FB3;GREEK SMALL LETTER ALPHA WITH YPOGEGRAMMENI;Ll;0;L;03B1 0345;;;;N;;;1FBC;;1FBC +1FB4;GREEK SMALL LETTER ALPHA WITH OXIA AND YPOGEGRAMMENI;Ll;0;L;03AC 0345;;;;N;;;;; +1FB6;GREEK SMALL LETTER ALPHA WITH PERISPOMENI;Ll;0;L;03B1 0342;;;;N;;;;; +1FB7;GREEK SMALL LETTER ALPHA WITH PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1FB6 0345;;;;N;;;;; +1FB8;GREEK CAPITAL LETTER ALPHA WITH VRACHY;Lu;0;L;0391 0306;;;;N;;;;1FB0; +1FB9;GREEK CAPITAL LETTER ALPHA WITH MACRON;Lu;0;L;0391 0304;;;;N;;;;1FB1; +1FBA;GREEK CAPITAL LETTER ALPHA WITH VARIA;Lu;0;L;0391 0300;;;;N;;;;1F70; +1FBB;GREEK CAPITAL LETTER ALPHA WITH OXIA;Lu;0;L;0386;;;;N;;;;1F71; +1FBC;GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI;Lt;0;L;0391 0345;;;;N;;;;1FB3; +1FBD;GREEK KORONIS;Sk;0;ON;<compat> 0020 0313;;;;N;;;;; +1FBE;GREEK PROSGEGRAMMENI;Ll;0;L;03B9;;;;N;;;0399;;0399 +1FBF;GREEK PSILI;Sk;0;ON;<compat> 0020 0313;;;;N;;;;; +1FC0;GREEK PERISPOMENI;Sk;0;ON;<compat> 0020 0342;;;;N;;;;; +1FC1;GREEK DIALYTIKA AND PERISPOMENI;Sk;0;ON;00A8 0342;;;;N;;;;; +1FC2;GREEK SMALL LETTER ETA WITH VARIA AND YPOGEGRAMMENI;Ll;0;L;1F74 0345;;;;N;;;;; +1FC3;GREEK SMALL LETTER ETA WITH YPOGEGRAMMENI;Ll;0;L;03B7 0345;;;;N;;;1FCC;;1FCC +1FC4;GREEK SMALL LETTER ETA WITH OXIA AND YPOGEGRAMMENI;Ll;0;L;03AE 0345;;;;N;;;;; +1FC6;GREEK SMALL LETTER ETA WITH PERISPOMENI;Ll;0;L;03B7 0342;;;;N;;;;; +1FC7;GREEK SMALL LETTER ETA WITH PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1FC6 0345;;;;N;;;;; +1FC8;GREEK CAPITAL LETTER EPSILON WITH VARIA;Lu;0;L;0395 0300;;;;N;;;;1F72; +1FC9;GREEK CAPITAL LETTER EPSILON WITH OXIA;Lu;0;L;0388;;;;N;;;;1F73; +1FCA;GREEK CAPITAL LETTER ETA WITH VARIA;Lu;0;L;0397 0300;;;;N;;;;1F74; +1FCB;GREEK CAPITAL LETTER ETA WITH OXIA;Lu;0;L;0389;;;;N;;;;1F75; +1FCC;GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI;Lt;0;L;0397 0345;;;;N;;;;1FC3; +1FCD;GREEK PSILI AND VARIA;Sk;0;ON;1FBF 0300;;;;N;;;;; +1FCE;GREEK PSILI AND OXIA;Sk;0;ON;1FBF 0301;;;;N;;;;; +1FCF;GREEK PSILI AND PERISPOMENI;Sk;0;ON;1FBF 0342;;;;N;;;;; +1FD0;GREEK SMALL LETTER IOTA WITH VRACHY;Ll;0;L;03B9 0306;;;;N;;;1FD8;;1FD8 +1FD1;GREEK SMALL LETTER IOTA WITH MACRON;Ll;0;L;03B9 0304;;;;N;;;1FD9;;1FD9 +1FD2;GREEK SMALL LETTER IOTA WITH DIALYTIKA AND VARIA;Ll;0;L;03CA 0300;;;;N;;;;; +1FD3;GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA;Ll;0;L;0390;;;;N;;;;; +1FD6;GREEK SMALL LETTER IOTA WITH PERISPOMENI;Ll;0;L;03B9 0342;;;;N;;;;; +1FD7;GREEK SMALL LETTER IOTA WITH DIALYTIKA AND PERISPOMENI;Ll;0;L;03CA 0342;;;;N;;;;; +1FD8;GREEK CAPITAL LETTER IOTA WITH VRACHY;Lu;0;L;0399 0306;;;;N;;;;1FD0; +1FD9;GREEK CAPITAL LETTER IOTA WITH MACRON;Lu;0;L;0399 0304;;;;N;;;;1FD1; +1FDA;GREEK CAPITAL LETTER IOTA WITH VARIA;Lu;0;L;0399 0300;;;;N;;;;1F76; +1FDB;GREEK CAPITAL LETTER IOTA WITH OXIA;Lu;0;L;038A;;;;N;;;;1F77; +1FDD;GREEK DASIA AND VARIA;Sk;0;ON;1FFE 0300;;;;N;;;;; +1FDE;GREEK DASIA AND OXIA;Sk;0;ON;1FFE 0301;;;;N;;;;; +1FDF;GREEK DASIA AND PERISPOMENI;Sk;0;ON;1FFE 0342;;;;N;;;;; +1FE0;GREEK SMALL LETTER UPSILON WITH VRACHY;Ll;0;L;03C5 0306;;;;N;;;1FE8;;1FE8 +1FE1;GREEK SMALL LETTER UPSILON WITH MACRON;Ll;0;L;03C5 0304;;;;N;;;1FE9;;1FE9 +1FE2;GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND VARIA;Ll;0;L;03CB 0300;;;;N;;;;; +1FE3;GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND OXIA;Ll;0;L;03B0;;;;N;;;;; +1FE4;GREEK SMALL LETTER RHO WITH PSILI;Ll;0;L;03C1 0313;;;;N;;;;; +1FE5;GREEK SMALL LETTER RHO WITH DASIA;Ll;0;L;03C1 0314;;;;N;;;1FEC;;1FEC +1FE6;GREEK SMALL LETTER UPSILON WITH PERISPOMENI;Ll;0;L;03C5 0342;;;;N;;;;; +1FE7;GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND PERISPOMENI;Ll;0;L;03CB 0342;;;;N;;;;; +1FE8;GREEK CAPITAL LETTER UPSILON WITH VRACHY;Lu;0;L;03A5 0306;;;;N;;;;1FE0; +1FE9;GREEK CAPITAL LETTER UPSILON WITH MACRON;Lu;0;L;03A5 0304;;;;N;;;;1FE1; +1FEA;GREEK CAPITAL LETTER UPSILON WITH VARIA;Lu;0;L;03A5 0300;;;;N;;;;1F7A; +1FEB;GREEK CAPITAL LETTER UPSILON WITH OXIA;Lu;0;L;038E;;;;N;;;;1F7B; +1FEC;GREEK CAPITAL LETTER RHO WITH DASIA;Lu;0;L;03A1 0314;;;;N;;;;1FE5; +1FED;GREEK DIALYTIKA AND VARIA;Sk;0;ON;00A8 0300;;;;N;;;;; +1FEE;GREEK DIALYTIKA AND OXIA;Sk;0;ON;0385;;;;N;;;;; +1FEF;GREEK VARIA;Sk;0;ON;0060;;;;N;;;;; +1FF2;GREEK SMALL LETTER OMEGA WITH VARIA AND YPOGEGRAMMENI;Ll;0;L;1F7C 0345;;;;N;;;;; +1FF3;GREEK SMALL LETTER OMEGA WITH YPOGEGRAMMENI;Ll;0;L;03C9 0345;;;;N;;;1FFC;;1FFC +1FF4;GREEK SMALL LETTER OMEGA WITH OXIA AND YPOGEGRAMMENI;Ll;0;L;03CE 0345;;;;N;;;;; +1FF6;GREEK SMALL LETTER OMEGA WITH PERISPOMENI;Ll;0;L;03C9 0342;;;;N;;;;; +1FF7;GREEK SMALL LETTER OMEGA WITH PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1FF6 0345;;;;N;;;;; +1FF8;GREEK CAPITAL LETTER OMICRON WITH VARIA;Lu;0;L;039F 0300;;;;N;;;;1F78; +1FF9;GREEK CAPITAL LETTER OMICRON WITH OXIA;Lu;0;L;038C;;;;N;;;;1F79; +1FFA;GREEK CAPITAL LETTER OMEGA WITH VARIA;Lu;0;L;03A9 0300;;;;N;;;;1F7C; +1FFB;GREEK CAPITAL LETTER OMEGA WITH OXIA;Lu;0;L;038F;;;;N;;;;1F7D; +1FFC;GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI;Lt;0;L;03A9 0345;;;;N;;;;1FF3; +1FFD;GREEK OXIA;Sk;0;ON;00B4;;;;N;;;;; +1FFE;GREEK DASIA;Sk;0;ON;<compat> 0020 0314;;;;N;;;;; +2000;EN QUAD;Zs;0;WS;2002;;;;N;;;;; +2001;EM QUAD;Zs;0;WS;2003;;;;N;;;;; +2002;EN SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;; +2003;EM SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;; +2004;THREE-PER-EM SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;; +2005;FOUR-PER-EM SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;; +2006;SIX-PER-EM SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;; +2007;FIGURE SPACE;Zs;0;WS;<noBreak> 0020;;;;N;;;;; +2008;PUNCTUATION SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;; +2009;THIN SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;; +200A;HAIR SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;; +200B;ZERO WIDTH SPACE;Zs;0;BN;;;;;N;;;;; +200C;ZERO WIDTH NON-JOINER;Cf;0;BN;;;;;N;;;;; +200D;ZERO WIDTH JOINER;Cf;0;BN;;;;;N;;;;; +200E;LEFT-TO-RIGHT MARK;Cf;0;L;;;;;N;;;;; +200F;RIGHT-TO-LEFT MARK;Cf;0;R;;;;;N;;;;; +2010;HYPHEN;Pd;0;ON;;;;;N;;;;; +2011;NON-BREAKING HYPHEN;Pd;0;ON;<noBreak> 2010;;;;N;;;;; +2012;FIGURE DASH;Pd;0;ON;;;;;N;;;;; +2013;EN DASH;Pd;0;ON;;;;;N;;;;; +2014;EM DASH;Pd;0;ON;;;;;N;;;;; +2015;HORIZONTAL BAR;Pd;0;ON;;;;;N;QUOTATION DASH;;;; +2016;DOUBLE VERTICAL LINE;Po;0;ON;;;;;N;DOUBLE VERTICAL BAR;;;; +2017;DOUBLE LOW LINE;Po;0;ON;<compat> 0020 0333;;;;N;SPACING DOUBLE UNDERSCORE;;;; +2018;LEFT SINGLE QUOTATION MARK;Pi;0;ON;;;;;N;SINGLE TURNED COMMA QUOTATION MARK;;;; +2019;RIGHT SINGLE QUOTATION MARK;Pf;0;ON;;;;;N;SINGLE COMMA QUOTATION MARK;;;; +201A;SINGLE LOW-9 QUOTATION MARK;Ps;0;ON;;;;;N;LOW SINGLE COMMA QUOTATION MARK;;;; +201B;SINGLE HIGH-REVERSED-9 QUOTATION MARK;Pi;0;ON;;;;;N;SINGLE REVERSED COMMA QUOTATION MARK;;;; +201C;LEFT DOUBLE QUOTATION MARK;Pi;0;ON;;;;;N;DOUBLE TURNED COMMA QUOTATION MARK;;;; +201D;RIGHT DOUBLE QUOTATION MARK;Pf;0;ON;;;;;N;DOUBLE COMMA QUOTATION MARK;;;; +201E;DOUBLE LOW-9 QUOTATION MARK;Ps;0;ON;;;;;N;LOW DOUBLE COMMA QUOTATION MARK;;;; +201F;DOUBLE HIGH-REVERSED-9 QUOTATION MARK;Pi;0;ON;;;;;N;DOUBLE REVERSED COMMA QUOTATION MARK;;;; +2020;DAGGER;Po;0;ON;;;;;N;;;;; +2021;DOUBLE DAGGER;Po;0;ON;;;;;N;;;;; +2022;BULLET;Po;0;ON;;;;;N;;;;; +2023;TRIANGULAR BULLET;Po;0;ON;;;;;N;;;;; +2024;ONE DOT LEADER;Po;0;ON;<compat> 002E;;;;N;;;;; +2025;TWO DOT LEADER;Po;0;ON;<compat> 002E 002E;;;;N;;;;; +2026;HORIZONTAL ELLIPSIS;Po;0;ON;<compat> 002E 002E 002E;;;;N;;;;; +2027;HYPHENATION POINT;Po;0;ON;;;;;N;;;;; +2028;LINE SEPARATOR;Zl;0;WS;;;;;N;;;;; +2029;PARAGRAPH SEPARATOR;Zp;0;B;;;;;N;;;;; +202A;LEFT-TO-RIGHT EMBEDDING;Cf;0;LRE;;;;;N;;;;; +202B;RIGHT-TO-LEFT EMBEDDING;Cf;0;RLE;;;;;N;;;;; +202C;POP DIRECTIONAL FORMATTING;Cf;0;PDF;;;;;N;;;;; +202D;LEFT-TO-RIGHT OVERRIDE;Cf;0;LRO;;;;;N;;;;; +202E;RIGHT-TO-LEFT OVERRIDE;Cf;0;RLO;;;;;N;;;;; +202F;NARROW NO-BREAK SPACE;Zs;0;WS;<noBreak> 0020;;;;N;;;;; +2030;PER MILLE SIGN;Po;0;ET;;;;;N;;;;; +2031;PER TEN THOUSAND SIGN;Po;0;ET;;;;;N;;;;; +2032;PRIME;Po;0;ET;;;;;N;;;;; +2033;DOUBLE PRIME;Po;0;ET;<compat> 2032 2032;;;;N;;;;; +2034;TRIPLE PRIME;Po;0;ET;<compat> 2032 2032 2032;;;;N;;;;; +2035;REVERSED PRIME;Po;0;ON;;;;;N;;;;; +2036;REVERSED DOUBLE PRIME;Po;0;ON;<compat> 2035 2035;;;;N;;;;; +2037;REVERSED TRIPLE PRIME;Po;0;ON;<compat> 2035 2035 2035;;;;N;;;;; +2038;CARET;Po;0;ON;;;;;N;;;;; +2039;SINGLE LEFT-POINTING ANGLE QUOTATION MARK;Pi;0;ON;;;;;Y;LEFT POINTING SINGLE GUILLEMET;;;; +203A;SINGLE RIGHT-POINTING ANGLE QUOTATION MARK;Pf;0;ON;;;;;Y;RIGHT POINTING SINGLE GUILLEMET;;;; +203B;REFERENCE MARK;Po;0;ON;;;;;N;;;;; +203C;DOUBLE EXCLAMATION MARK;Po;0;ON;<compat> 0021 0021;;;;N;;;;; +203D;INTERROBANG;Po;0;ON;;;;;N;;;;; +203E;OVERLINE;Po;0;ON;<compat> 0020 0305;;;;N;SPACING OVERSCORE;;;; +203F;UNDERTIE;Pc;0;ON;;;;;N;;Enotikon;;; +2040;CHARACTER TIE;Pc;0;ON;;;;;N;;;;; +2041;CARET INSERTION POINT;Po;0;ON;;;;;N;;;;; +2042;ASTERISM;Po;0;ON;;;;;N;;;;; +2043;HYPHEN BULLET;Po;0;ON;;;;;N;;;;; +2044;FRACTION SLASH;Sm;0;ON;;;;;N;;;;; +2045;LEFT SQUARE BRACKET WITH QUILL;Ps;0;ON;;;;;Y;;;;; +2046;RIGHT SQUARE BRACKET WITH QUILL;Pe;0;ON;;;;;Y;;;;; +2047;DOUBLE QUESTION MARK;Po;0;ON;<compat> 003F 003F;;;;N;;;;; +2048;QUESTION EXCLAMATION MARK;Po;0;ON;<compat> 003F 0021;;;;N;;;;; +2049;EXCLAMATION QUESTION MARK;Po;0;ON;<compat> 0021 003F;;;;N;;;;; +204A;TIRONIAN SIGN ET;Po;0;ON;;;;;N;;;;; +204B;REVERSED PILCROW SIGN;Po;0;ON;;;;;N;;;;; +204C;BLACK LEFTWARDS BULLET;Po;0;ON;;;;;N;;;;; +204D;BLACK RIGHTWARDS BULLET;Po;0;ON;;;;;N;;;;; +204E;LOW ASTERISK;Po;0;ON;;;;;N;;;;; +204F;REVERSED SEMICOLON;Po;0;ON;;;;;N;;;;; +2050;CLOSE UP;Po;0;ON;;;;;N;;;;; +2051;TWO ASTERISKS ALIGNED VERTICALLY;Po;0;ON;;;;;N;;;;; +2052;COMMERCIAL MINUS SIGN;Sm;0;ON;;;;;N;;;;; +2057;QUADRUPLE PRIME;Po;0;ON;<compat> 2032 2032 2032 2032;;;;N;;;;; +205F;MEDIUM MATHEMATICAL SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;; +2060;WORD JOINER;Cf;0;BN;;;;;N;;;;; +2061;FUNCTION APPLICATION;Cf;0;BN;;;;;N;;;;; +2062;INVISIBLE TIMES;Cf;0;BN;;;;;N;;;;; +2063;INVISIBLE SEPARATOR;Cf;0;BN;;;;;N;;;;; +206A;INHIBIT SYMMETRIC SWAPPING;Cf;0;BN;;;;;N;;;;; +206B;ACTIVATE SYMMETRIC SWAPPING;Cf;0;BN;;;;;N;;;;; +206C;INHIBIT ARABIC FORM SHAPING;Cf;0;BN;;;;;N;;;;; +206D;ACTIVATE ARABIC FORM SHAPING;Cf;0;BN;;;;;N;;;;; +206E;NATIONAL DIGIT SHAPES;Cf;0;BN;;;;;N;;;;; +206F;NOMINAL DIGIT SHAPES;Cf;0;BN;;;;;N;;;;; +2070;SUPERSCRIPT ZERO;No;0;EN;<super> 0030;0;0;0;N;SUPERSCRIPT DIGIT ZERO;;;; +2071;SUPERSCRIPT LATIN SMALL LETTER I;Ll;0;L;<super> 0069;;;;N;;;;; +2074;SUPERSCRIPT FOUR;No;0;EN;<super> 0034;4;4;4;N;SUPERSCRIPT DIGIT FOUR;;;; +2075;SUPERSCRIPT FIVE;No;0;EN;<super> 0035;5;5;5;N;SUPERSCRIPT DIGIT FIVE;;;; +2076;SUPERSCRIPT SIX;No;0;EN;<super> 0036;6;6;6;N;SUPERSCRIPT DIGIT SIX;;;; +2077;SUPERSCRIPT SEVEN;No;0;EN;<super> 0037;7;7;7;N;SUPERSCRIPT DIGIT SEVEN;;;; +2078;SUPERSCRIPT EIGHT;No;0;EN;<super> 0038;8;8;8;N;SUPERSCRIPT DIGIT EIGHT;;;; +2079;SUPERSCRIPT NINE;No;0;EN;<super> 0039;9;9;9;N;SUPERSCRIPT DIGIT NINE;;;; +207A;SUPERSCRIPT PLUS SIGN;Sm;0;ET;<super> 002B;;;;N;;;;; +207B;SUPERSCRIPT MINUS;Sm;0;ET;<super> 2212;;;;N;SUPERSCRIPT HYPHEN-MINUS;;;; +207C;SUPERSCRIPT EQUALS SIGN;Sm;0;ON;<super> 003D;;;;N;;;;; +207D;SUPERSCRIPT LEFT PARENTHESIS;Ps;0;ON;<super> 0028;;;;Y;SUPERSCRIPT OPENING PARENTHESIS;;;; +207E;SUPERSCRIPT RIGHT PARENTHESIS;Pe;0;ON;<super> 0029;;;;Y;SUPERSCRIPT CLOSING PARENTHESIS;;;; +207F;SUPERSCRIPT LATIN SMALL LETTER N;Ll;0;L;<super> 006E;;;;N;;;;; +2080;SUBSCRIPT ZERO;No;0;EN;<sub> 0030;0;0;0;N;SUBSCRIPT DIGIT ZERO;;;; +2081;SUBSCRIPT ONE;No;0;EN;<sub> 0031;1;1;1;N;SUBSCRIPT DIGIT ONE;;;; +2082;SUBSCRIPT TWO;No;0;EN;<sub> 0032;2;2;2;N;SUBSCRIPT DIGIT TWO;;;; +2083;SUBSCRIPT THREE;No;0;EN;<sub> 0033;3;3;3;N;SUBSCRIPT DIGIT THREE;;;; +2084;SUBSCRIPT FOUR;No;0;EN;<sub> 0034;4;4;4;N;SUBSCRIPT DIGIT FOUR;;;; +2085;SUBSCRIPT FIVE;No;0;EN;<sub> 0035;5;5;5;N;SUBSCRIPT DIGIT FIVE;;;; +2086;SUBSCRIPT SIX;No;0;EN;<sub> 0036;6;6;6;N;SUBSCRIPT DIGIT SIX;;;; +2087;SUBSCRIPT SEVEN;No;0;EN;<sub> 0037;7;7;7;N;SUBSCRIPT DIGIT SEVEN;;;; +2088;SUBSCRIPT EIGHT;No;0;EN;<sub> 0038;8;8;8;N;SUBSCRIPT DIGIT EIGHT;;;; +2089;SUBSCRIPT NINE;No;0;EN;<sub> 0039;9;9;9;N;SUBSCRIPT DIGIT NINE;;;; +208A;SUBSCRIPT PLUS SIGN;Sm;0;ET;<sub> 002B;;;;N;;;;; +208B;SUBSCRIPT MINUS;Sm;0;ET;<sub> 2212;;;;N;SUBSCRIPT HYPHEN-MINUS;;;; +208C;SUBSCRIPT EQUALS SIGN;Sm;0;ON;<sub> 003D;;;;N;;;;; +208D;SUBSCRIPT LEFT PARENTHESIS;Ps;0;ON;<sub> 0028;;;;Y;SUBSCRIPT OPENING PARENTHESIS;;;; +208E;SUBSCRIPT RIGHT PARENTHESIS;Pe;0;ON;<sub> 0029;;;;Y;SUBSCRIPT CLOSING PARENTHESIS;;;; +20A0;EURO-CURRENCY SIGN;Sc;0;ET;;;;;N;;;;; +20A1;COLON SIGN;Sc;0;ET;;;;;N;;;;; +20A2;CRUZEIRO SIGN;Sc;0;ET;;;;;N;;;;; +20A3;FRENCH FRANC SIGN;Sc;0;ET;;;;;N;;;;; +20A4;LIRA SIGN;Sc;0;ET;;;;;N;;;;; +20A5;MILL SIGN;Sc;0;ET;;;;;N;;;;; +20A6;NAIRA SIGN;Sc;0;ET;;;;;N;;;;; +20A7;PESETA SIGN;Sc;0;ET;;;;;N;;;;; +20A8;RUPEE SIGN;Sc;0;ET;<compat> 0052 0073;;;;N;;;;; +20A9;WON SIGN;Sc;0;ET;;;;;N;;;;; +20AA;NEW SHEQEL SIGN;Sc;0;ET;;;;;N;;;;; +20AB;DONG SIGN;Sc;0;ET;;;;;N;;;;; +20AC;EURO SIGN;Sc;0;ET;;;;;N;;;;; +20AD;KIP SIGN;Sc;0;ET;;;;;N;;;;; +20AE;TUGRIK SIGN;Sc;0;ET;;;;;N;;;;; +20AF;DRACHMA SIGN;Sc;0;ET;;;;;N;;;;; +20B0;GERMAN PENNY SIGN;Sc;0;ET;;;;;N;;;;; +20B1;PESO SIGN;Sc;0;ET;;;;;N;;;;; +20D0;COMBINING LEFT HARPOON ABOVE;Mn;230;NSM;;;;;N;NON-SPACING LEFT HARPOON ABOVE;;;; +20D1;COMBINING RIGHT HARPOON ABOVE;Mn;230;NSM;;;;;N;NON-SPACING RIGHT HARPOON ABOVE;;;; +20D2;COMBINING LONG VERTICAL LINE OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING LONG VERTICAL BAR OVERLAY;;;; +20D3;COMBINING SHORT VERTICAL LINE OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING SHORT VERTICAL BAR OVERLAY;;;; +20D4;COMBINING ANTICLOCKWISE ARROW ABOVE;Mn;230;NSM;;;;;N;NON-SPACING ANTICLOCKWISE ARROW ABOVE;;;; +20D5;COMBINING CLOCKWISE ARROW ABOVE;Mn;230;NSM;;;;;N;NON-SPACING CLOCKWISE ARROW ABOVE;;;; +20D6;COMBINING LEFT ARROW ABOVE;Mn;230;NSM;;;;;N;NON-SPACING LEFT ARROW ABOVE;;;; +20D7;COMBINING RIGHT ARROW ABOVE;Mn;230;NSM;;;;;N;NON-SPACING RIGHT ARROW ABOVE;;;; +20D8;COMBINING RING OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING RING OVERLAY;;;; +20D9;COMBINING CLOCKWISE RING OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING CLOCKWISE RING OVERLAY;;;; +20DA;COMBINING ANTICLOCKWISE RING OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING ANTICLOCKWISE RING OVERLAY;;;; +20DB;COMBINING THREE DOTS ABOVE;Mn;230;NSM;;;;;N;NON-SPACING THREE DOTS ABOVE;;;; +20DC;COMBINING FOUR DOTS ABOVE;Mn;230;NSM;;;;;N;NON-SPACING FOUR DOTS ABOVE;;;; +20DD;COMBINING ENCLOSING CIRCLE;Me;0;NSM;;;;;N;ENCLOSING CIRCLE;;;; +20DE;COMBINING ENCLOSING SQUARE;Me;0;NSM;;;;;N;ENCLOSING SQUARE;;;; +20DF;COMBINING ENCLOSING DIAMOND;Me;0;NSM;;;;;N;ENCLOSING DIAMOND;;;; +20E0;COMBINING ENCLOSING CIRCLE BACKSLASH;Me;0;NSM;;;;;N;ENCLOSING CIRCLE SLASH;;;; +20E1;COMBINING LEFT RIGHT ARROW ABOVE;Mn;230;NSM;;;;;N;NON-SPACING LEFT RIGHT ARROW ABOVE;;;; +20E2;COMBINING ENCLOSING SCREEN;Me;0;NSM;;;;;N;;;;; +20E3;COMBINING ENCLOSING KEYCAP;Me;0;NSM;;;;;N;;;;; +20E4;COMBINING ENCLOSING UPWARD POINTING TRIANGLE;Me;0;NSM;;;;;N;;;;; +20E5;COMBINING REVERSE SOLIDUS OVERLAY;Mn;1;NSM;;;;;N;;;;; +20E6;COMBINING DOUBLE VERTICAL STROKE OVERLAY;Mn;1;NSM;;;;;N;;;;; +20E7;COMBINING ANNUITY SYMBOL;Mn;230;NSM;;;;;N;;;;; +20E8;COMBINING TRIPLE UNDERDOT;Mn;220;NSM;;;;;N;;;;; +20E9;COMBINING WIDE BRIDGE ABOVE;Mn;230;NSM;;;;;N;;;;; +20EA;COMBINING LEFTWARDS ARROW OVERLAY;Mn;1;NSM;;;;;N;;;;; +2100;ACCOUNT OF;So;0;ON;<compat> 0061 002F 0063;;;;N;;;;; +2101;ADDRESSED TO THE SUBJECT;So;0;ON;<compat> 0061 002F 0073;;;;N;;;;; +2102;DOUBLE-STRUCK CAPITAL C;Lu;0;L;<font> 0043;;;;N;DOUBLE-STRUCK C;;;; +2103;DEGREE CELSIUS;So;0;ON;<compat> 00B0 0043;;;;N;DEGREES CENTIGRADE;;;; +2104;CENTRE LINE SYMBOL;So;0;ON;;;;;N;C L SYMBOL;;;; +2105;CARE OF;So;0;ON;<compat> 0063 002F 006F;;;;N;;;;; +2106;CADA UNA;So;0;ON;<compat> 0063 002F 0075;;;;N;;;;; +2107;EULER CONSTANT;Lu;0;L;<compat> 0190;;;;N;EULERS;;;; +2108;SCRUPLE;So;0;ON;;;;;N;;;;; +2109;DEGREE FAHRENHEIT;So;0;ON;<compat> 00B0 0046;;;;N;DEGREES FAHRENHEIT;;;; +210A;SCRIPT SMALL G;Ll;0;L;<font> 0067;;;;N;;;;; +210B;SCRIPT CAPITAL H;Lu;0;L;<font> 0048;;;;N;SCRIPT H;;;; +210C;BLACK-LETTER CAPITAL H;Lu;0;L;<font> 0048;;;;N;BLACK-LETTER H;;;; +210D;DOUBLE-STRUCK CAPITAL H;Lu;0;L;<font> 0048;;;;N;DOUBLE-STRUCK H;;;; +210E;PLANCK CONSTANT;Ll;0;L;<font> 0068;;;;N;;;;; +210F;PLANCK CONSTANT OVER TWO PI;Ll;0;L;<font> 0127;;;;N;PLANCK CONSTANT OVER 2 PI;;;; +2110;SCRIPT CAPITAL I;Lu;0;L;<font> 0049;;;;N;SCRIPT I;;;; +2111;BLACK-LETTER CAPITAL I;Lu;0;L;<font> 0049;;;;N;BLACK-LETTER I;;;; +2112;SCRIPT CAPITAL L;Lu;0;L;<font> 004C;;;;N;SCRIPT L;;;; +2113;SCRIPT SMALL L;Ll;0;L;<font> 006C;;;;N;;;;; +2114;L B BAR SYMBOL;So;0;ON;;;;;N;;;;; +2115;DOUBLE-STRUCK CAPITAL N;Lu;0;L;<font> 004E;;;;N;DOUBLE-STRUCK N;;;; +2116;NUMERO SIGN;So;0;ON;<compat> 004E 006F;;;;N;NUMERO;;;; +2117;SOUND RECORDING COPYRIGHT;So;0;ON;;;;;N;;;;; +2118;SCRIPT CAPITAL P;So;0;ON;;;;;N;SCRIPT P;;;; +2119;DOUBLE-STRUCK CAPITAL P;Lu;0;L;<font> 0050;;;;N;DOUBLE-STRUCK P;;;; +211A;DOUBLE-STRUCK CAPITAL Q;Lu;0;L;<font> 0051;;;;N;DOUBLE-STRUCK Q;;;; +211B;SCRIPT CAPITAL R;Lu;0;L;<font> 0052;;;;N;SCRIPT R;;;; +211C;BLACK-LETTER CAPITAL R;Lu;0;L;<font> 0052;;;;N;BLACK-LETTER R;;;; +211D;DOUBLE-STRUCK CAPITAL R;Lu;0;L;<font> 0052;;;;N;DOUBLE-STRUCK R;;;; +211E;PRESCRIPTION TAKE;So;0;ON;;;;;N;;;;; +211F;RESPONSE;So;0;ON;;;;;N;;;;; +2120;SERVICE MARK;So;0;ON;<super> 0053 004D;;;;N;;;;; +2121;TELEPHONE SIGN;So;0;ON;<compat> 0054 0045 004C;;;;N;T E L SYMBOL;;;; +2122;TRADE MARK SIGN;So;0;ON;<super> 0054 004D;;;;N;TRADEMARK;;;; +2123;VERSICLE;So;0;ON;;;;;N;;;;; +2124;DOUBLE-STRUCK CAPITAL Z;Lu;0;L;<font> 005A;;;;N;DOUBLE-STRUCK Z;;;; +2125;OUNCE SIGN;So;0;ON;;;;;N;OUNCE;;;; +2126;OHM SIGN;Lu;0;L;03A9;;;;N;OHM;;;03C9; +2127;INVERTED OHM SIGN;So;0;ON;;;;;N;MHO;;;; +2128;BLACK-LETTER CAPITAL Z;Lu;0;L;<font> 005A;;;;N;BLACK-LETTER Z;;;; +2129;TURNED GREEK SMALL LETTER IOTA;So;0;ON;;;;;N;;;;; +212A;KELVIN SIGN;Lu;0;L;004B;;;;N;DEGREES KELVIN;;;006B; +212B;ANGSTROM SIGN;Lu;0;L;00C5;;;;N;ANGSTROM UNIT;;;00E5; +212C;SCRIPT CAPITAL B;Lu;0;L;<font> 0042;;;;N;SCRIPT B;;;; +212D;BLACK-LETTER CAPITAL C;Lu;0;L;<font> 0043;;;;N;BLACK-LETTER C;;;; +212E;ESTIMATED SYMBOL;So;0;ET;;;;;N;;;;; +212F;SCRIPT SMALL E;Ll;0;L;<font> 0065;;;;N;;;;; +2130;SCRIPT CAPITAL E;Lu;0;L;<font> 0045;;;;N;SCRIPT E;;;; +2131;SCRIPT CAPITAL F;Lu;0;L;<font> 0046;;;;N;SCRIPT F;;;; +2132;TURNED CAPITAL F;So;0;ON;;;;;N;TURNED F;;;; +2133;SCRIPT CAPITAL M;Lu;0;L;<font> 004D;;;;N;SCRIPT M;;;; +2134;SCRIPT SMALL O;Ll;0;L;<font> 006F;;;;N;;;;; +2135;ALEF SYMBOL;Lo;0;L;<compat> 05D0;;;;N;FIRST TRANSFINITE CARDINAL;;;; +2136;BET SYMBOL;Lo;0;L;<compat> 05D1;;;;N;SECOND TRANSFINITE CARDINAL;;;; +2137;GIMEL SYMBOL;Lo;0;L;<compat> 05D2;;;;N;THIRD TRANSFINITE CARDINAL;;;; +2138;DALET SYMBOL;Lo;0;L;<compat> 05D3;;;;N;FOURTH TRANSFINITE CARDINAL;;;; +2139;INFORMATION SOURCE;Ll;0;L;<font> 0069;;;;N;;;;; +213A;ROTATED CAPITAL Q;So;0;ON;;;;;N;;;;; +213D;DOUBLE-STRUCK SMALL GAMMA;Ll;0;L;<font> 03B3;;;;N;;;;; +213E;DOUBLE-STRUCK CAPITAL GAMMA;Lu;0;L;<font> 0393;;;;N;;;;; +213F;DOUBLE-STRUCK CAPITAL PI;Lu;0;L;<font> 03A0;;;;N;;;;; +2140;DOUBLE-STRUCK N-ARY SUMMATION;Sm;0;ON;<font> 2211;;;;Y;;;;; +2141;TURNED SANS-SERIF CAPITAL G;Sm;0;ON;;;;;N;;;;; +2142;TURNED SANS-SERIF CAPITAL L;Sm;0;ON;;;;;N;;;;; +2143;REVERSED SANS-SERIF CAPITAL L;Sm;0;ON;;;;;N;;;;; +2144;TURNED SANS-SERIF CAPITAL Y;Sm;0;ON;;;;;N;;;;; +2145;DOUBLE-STRUCK ITALIC CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;; +2146;DOUBLE-STRUCK ITALIC SMALL D;Ll;0;L;<font> 0064;;;;N;;;;; +2147;DOUBLE-STRUCK ITALIC SMALL E;Ll;0;L;<font> 0065;;;;N;;;;; +2148;DOUBLE-STRUCK ITALIC SMALL I;Ll;0;L;<font> 0069;;;;N;;;;; +2149;DOUBLE-STRUCK ITALIC SMALL J;Ll;0;L;<font> 006A;;;;N;;;;; +214A;PROPERTY LINE;So;0;ON;;;;;N;;;;; +214B;TURNED AMPERSAND;Sm;0;ON;;;;;N;;;;; +2153;VULGAR FRACTION ONE THIRD;No;0;ON;<fraction> 0031 2044 0033;;;1/3;N;FRACTION ONE THIRD;;;; +2154;VULGAR FRACTION TWO THIRDS;No;0;ON;<fraction> 0032 2044 0033;;;2/3;N;FRACTION TWO THIRDS;;;; +2155;VULGAR FRACTION ONE FIFTH;No;0;ON;<fraction> 0031 2044 0035;;;1/5;N;FRACTION ONE FIFTH;;;; +2156;VULGAR FRACTION TWO FIFTHS;No;0;ON;<fraction> 0032 2044 0035;;;2/5;N;FRACTION TWO FIFTHS;;;; +2157;VULGAR FRACTION THREE FIFTHS;No;0;ON;<fraction> 0033 2044 0035;;;3/5;N;FRACTION THREE FIFTHS;;;; +2158;VULGAR FRACTION FOUR FIFTHS;No;0;ON;<fraction> 0034 2044 0035;;;4/5;N;FRACTION FOUR FIFTHS;;;; +2159;VULGAR FRACTION ONE SIXTH;No;0;ON;<fraction> 0031 2044 0036;;;1/6;N;FRACTION ONE SIXTH;;;; +215A;VULGAR FRACTION FIVE SIXTHS;No;0;ON;<fraction> 0035 2044 0036;;;5/6;N;FRACTION FIVE SIXTHS;;;; +215B;VULGAR FRACTION ONE EIGHTH;No;0;ON;<fraction> 0031 2044 0038;;;1/8;N;FRACTION ONE EIGHTH;;;; +215C;VULGAR FRACTION THREE EIGHTHS;No;0;ON;<fraction> 0033 2044 0038;;;3/8;N;FRACTION THREE EIGHTHS;;;; +215D;VULGAR FRACTION FIVE EIGHTHS;No;0;ON;<fraction> 0035 2044 0038;;;5/8;N;FRACTION FIVE EIGHTHS;;;; +215E;VULGAR FRACTION SEVEN EIGHTHS;No;0;ON;<fraction> 0037 2044 0038;;;7/8;N;FRACTION SEVEN EIGHTHS;;;; +215F;FRACTION NUMERATOR ONE;No;0;ON;<fraction> 0031 2044;;;1;N;;;;; +2160;ROMAN NUMERAL ONE;Nl;0;L;<compat> 0049;;;1;N;;;;2170; +2161;ROMAN NUMERAL TWO;Nl;0;L;<compat> 0049 0049;;;2;N;;;;2171; +2162;ROMAN NUMERAL THREE;Nl;0;L;<compat> 0049 0049 0049;;;3;N;;;;2172; +2163;ROMAN NUMERAL FOUR;Nl;0;L;<compat> 0049 0056;;;4;N;;;;2173; +2164;ROMAN NUMERAL FIVE;Nl;0;L;<compat> 0056;;;5;N;;;;2174; +2165;ROMAN NUMERAL SIX;Nl;0;L;<compat> 0056 0049;;;6;N;;;;2175; +2166;ROMAN NUMERAL SEVEN;Nl;0;L;<compat> 0056 0049 0049;;;7;N;;;;2176; +2167;ROMAN NUMERAL EIGHT;Nl;0;L;<compat> 0056 0049 0049 0049;;;8;N;;;;2177; +2168;ROMAN NUMERAL NINE;Nl;0;L;<compat> 0049 0058;;;9;N;;;;2178; +2169;ROMAN NUMERAL TEN;Nl;0;L;<compat> 0058;;;10;N;;;;2179; +216A;ROMAN NUMERAL ELEVEN;Nl;0;L;<compat> 0058 0049;;;11;N;;;;217A; +216B;ROMAN NUMERAL TWELVE;Nl;0;L;<compat> 0058 0049 0049;;;12;N;;;;217B; +216C;ROMAN NUMERAL FIFTY;Nl;0;L;<compat> 004C;;;50;N;;;;217C; +216D;ROMAN NUMERAL ONE HUNDRED;Nl;0;L;<compat> 0043;;;100;N;;;;217D; +216E;ROMAN NUMERAL FIVE HUNDRED;Nl;0;L;<compat> 0044;;;500;N;;;;217E; +216F;ROMAN NUMERAL ONE THOUSAND;Nl;0;L;<compat> 004D;;;1000;N;;;;217F; +2170;SMALL ROMAN NUMERAL ONE;Nl;0;L;<compat> 0069;;;1;N;;;2160;;2160 +2171;SMALL ROMAN NUMERAL TWO;Nl;0;L;<compat> 0069 0069;;;2;N;;;2161;;2161 +2172;SMALL ROMAN NUMERAL THREE;Nl;0;L;<compat> 0069 0069 0069;;;3;N;;;2162;;2162 +2173;SMALL ROMAN NUMERAL FOUR;Nl;0;L;<compat> 0069 0076;;;4;N;;;2163;;2163 +2174;SMALL ROMAN NUMERAL FIVE;Nl;0;L;<compat> 0076;;;5;N;;;2164;;2164 +2175;SMALL ROMAN NUMERAL SIX;Nl;0;L;<compat> 0076 0069;;;6;N;;;2165;;2165 +2176;SMALL ROMAN NUMERAL SEVEN;Nl;0;L;<compat> 0076 0069 0069;;;7;N;;;2166;;2166 +2177;SMALL ROMAN NUMERAL EIGHT;Nl;0;L;<compat> 0076 0069 0069 0069;;;8;N;;;2167;;2167 +2178;SMALL ROMAN NUMERAL NINE;Nl;0;L;<compat> 0069 0078;;;9;N;;;2168;;2168 +2179;SMALL ROMAN NUMERAL TEN;Nl;0;L;<compat> 0078;;;10;N;;;2169;;2169 +217A;SMALL ROMAN NUMERAL ELEVEN;Nl;0;L;<compat> 0078 0069;;;11;N;;;216A;;216A +217B;SMALL ROMAN NUMERAL TWELVE;Nl;0;L;<compat> 0078 0069 0069;;;12;N;;;216B;;216B +217C;SMALL ROMAN NUMERAL FIFTY;Nl;0;L;<compat> 006C;;;50;N;;;216C;;216C +217D;SMALL ROMAN NUMERAL ONE HUNDRED;Nl;0;L;<compat> 0063;;;100;N;;;216D;;216D +217E;SMALL ROMAN NUMERAL FIVE HUNDRED;Nl;0;L;<compat> 0064;;;500;N;;;216E;;216E +217F;SMALL ROMAN NUMERAL ONE THOUSAND;Nl;0;L;<compat> 006D;;;1000;N;;;216F;;216F +2180;ROMAN NUMERAL ONE THOUSAND C D;Nl;0;L;;;;1000;N;;;;; +2181;ROMAN NUMERAL FIVE THOUSAND;Nl;0;L;;;;5000;N;;;;; +2182;ROMAN NUMERAL TEN THOUSAND;Nl;0;L;;;;10000;N;;;;; +2183;ROMAN NUMERAL REVERSED ONE HUNDRED;Nl;0;L;;;;;N;;;;; +2190;LEFTWARDS ARROW;Sm;0;ON;;;;;N;LEFT ARROW;;;; +2191;UPWARDS ARROW;Sm;0;ON;;;;;N;UP ARROW;;;; +2192;RIGHTWARDS ARROW;Sm;0;ON;;;;;N;RIGHT ARROW;;;; +2193;DOWNWARDS ARROW;Sm;0;ON;;;;;N;DOWN ARROW;;;; +2194;LEFT RIGHT ARROW;Sm;0;ON;;;;;N;;;;; +2195;UP DOWN ARROW;So;0;ON;;;;;N;;;;; +2196;NORTH WEST ARROW;So;0;ON;;;;;N;UPPER LEFT ARROW;;;; +2197;NORTH EAST ARROW;So;0;ON;;;;;N;UPPER RIGHT ARROW;;;; +2198;SOUTH EAST ARROW;So;0;ON;;;;;N;LOWER RIGHT ARROW;;;; +2199;SOUTH WEST ARROW;So;0;ON;;;;;N;LOWER LEFT ARROW;;;; +219A;LEFTWARDS ARROW WITH STROKE;Sm;0;ON;2190 0338;;;;N;LEFT ARROW WITH STROKE;;;; +219B;RIGHTWARDS ARROW WITH STROKE;Sm;0;ON;2192 0338;;;;N;RIGHT ARROW WITH STROKE;;;; +219C;LEFTWARDS WAVE ARROW;So;0;ON;;;;;N;LEFT WAVE ARROW;;;; +219D;RIGHTWARDS WAVE ARROW;So;0;ON;;;;;N;RIGHT WAVE ARROW;;;; +219E;LEFTWARDS TWO HEADED ARROW;So;0;ON;;;;;N;LEFT TWO HEADED ARROW;;;; +219F;UPWARDS TWO HEADED ARROW;So;0;ON;;;;;N;UP TWO HEADED ARROW;;;; +21A0;RIGHTWARDS TWO HEADED ARROW;Sm;0;ON;;;;;N;RIGHT TWO HEADED ARROW;;;; +21A1;DOWNWARDS TWO HEADED ARROW;So;0;ON;;;;;N;DOWN TWO HEADED ARROW;;;; +21A2;LEFTWARDS ARROW WITH TAIL;So;0;ON;;;;;N;LEFT ARROW WITH TAIL;;;; +21A3;RIGHTWARDS ARROW WITH TAIL;Sm;0;ON;;;;;N;RIGHT ARROW WITH TAIL;;;; +21A4;LEFTWARDS ARROW FROM BAR;So;0;ON;;;;;N;LEFT ARROW FROM BAR;;;; +21A5;UPWARDS ARROW FROM BAR;So;0;ON;;;;;N;UP ARROW FROM BAR;;;; +21A6;RIGHTWARDS ARROW FROM BAR;Sm;0;ON;;;;;N;RIGHT ARROW FROM BAR;;;; +21A7;DOWNWARDS ARROW FROM BAR;So;0;ON;;;;;N;DOWN ARROW FROM BAR;;;; +21A8;UP DOWN ARROW WITH BASE;So;0;ON;;;;;N;;;;; +21A9;LEFTWARDS ARROW WITH HOOK;So;0;ON;;;;;N;LEFT ARROW WITH HOOK;;;; +21AA;RIGHTWARDS ARROW WITH HOOK;So;0;ON;;;;;N;RIGHT ARROW WITH HOOK;;;; +21AB;LEFTWARDS ARROW WITH LOOP;So;0;ON;;;;;N;LEFT ARROW WITH LOOP;;;; +21AC;RIGHTWARDS ARROW WITH LOOP;So;0;ON;;;;;N;RIGHT ARROW WITH LOOP;;;; +21AD;LEFT RIGHT WAVE ARROW;So;0;ON;;;;;N;;;;; +21AE;LEFT RIGHT ARROW WITH STROKE;Sm;0;ON;2194 0338;;;;N;;;;; +21AF;DOWNWARDS ZIGZAG ARROW;So;0;ON;;;;;N;DOWN ZIGZAG ARROW;;;; +21B0;UPWARDS ARROW WITH TIP LEFTWARDS;So;0;ON;;;;;N;UP ARROW WITH TIP LEFT;;;; +21B1;UPWARDS ARROW WITH TIP RIGHTWARDS;So;0;ON;;;;;N;UP ARROW WITH TIP RIGHT;;;; +21B2;DOWNWARDS ARROW WITH TIP LEFTWARDS;So;0;ON;;;;;N;DOWN ARROW WITH TIP LEFT;;;; +21B3;DOWNWARDS ARROW WITH TIP RIGHTWARDS;So;0;ON;;;;;N;DOWN ARROW WITH TIP RIGHT;;;; +21B4;RIGHTWARDS ARROW WITH CORNER DOWNWARDS;So;0;ON;;;;;N;RIGHT ARROW WITH CORNER DOWN;;;; +21B5;DOWNWARDS ARROW WITH CORNER LEFTWARDS;So;0;ON;;;;;N;DOWN ARROW WITH CORNER LEFT;;;; +21B6;ANTICLOCKWISE TOP SEMICIRCLE ARROW;So;0;ON;;;;;N;;;;; +21B7;CLOCKWISE TOP SEMICIRCLE ARROW;So;0;ON;;;;;N;;;;; +21B8;NORTH WEST ARROW TO LONG BAR;So;0;ON;;;;;N;UPPER LEFT ARROW TO LONG BAR;;;; +21B9;LEFTWARDS ARROW TO BAR OVER RIGHTWARDS ARROW TO BAR;So;0;ON;;;;;N;LEFT ARROW TO BAR OVER RIGHT ARROW TO BAR;;;; +21BA;ANTICLOCKWISE OPEN CIRCLE ARROW;So;0;ON;;;;;N;;;;; +21BB;CLOCKWISE OPEN CIRCLE ARROW;So;0;ON;;;;;N;;;;; +21BC;LEFTWARDS HARPOON WITH BARB UPWARDS;So;0;ON;;;;;N;LEFT HARPOON WITH BARB UP;;;; +21BD;LEFTWARDS HARPOON WITH BARB DOWNWARDS;So;0;ON;;;;;N;LEFT HARPOON WITH BARB DOWN;;;; +21BE;UPWARDS HARPOON WITH BARB RIGHTWARDS;So;0;ON;;;;;N;UP HARPOON WITH BARB RIGHT;;;; +21BF;UPWARDS HARPOON WITH BARB LEFTWARDS;So;0;ON;;;;;N;UP HARPOON WITH BARB LEFT;;;; +21C0;RIGHTWARDS HARPOON WITH BARB UPWARDS;So;0;ON;;;;;N;RIGHT HARPOON WITH BARB UP;;;; +21C1;RIGHTWARDS HARPOON WITH BARB DOWNWARDS;So;0;ON;;;;;N;RIGHT HARPOON WITH BARB DOWN;;;; +21C2;DOWNWARDS HARPOON WITH BARB RIGHTWARDS;So;0;ON;;;;;N;DOWN HARPOON WITH BARB RIGHT;;;; +21C3;DOWNWARDS HARPOON WITH BARB LEFTWARDS;So;0;ON;;;;;N;DOWN HARPOON WITH BARB LEFT;;;; +21C4;RIGHTWARDS ARROW OVER LEFTWARDS ARROW;So;0;ON;;;;;N;RIGHT ARROW OVER LEFT ARROW;;;; +21C5;UPWARDS ARROW LEFTWARDS OF DOWNWARDS ARROW;So;0;ON;;;;;N;UP ARROW LEFT OF DOWN ARROW;;;; +21C6;LEFTWARDS ARROW OVER RIGHTWARDS ARROW;So;0;ON;;;;;N;LEFT ARROW OVER RIGHT ARROW;;;; +21C7;LEFTWARDS PAIRED ARROWS;So;0;ON;;;;;N;LEFT PAIRED ARROWS;;;; +21C8;UPWARDS PAIRED ARROWS;So;0;ON;;;;;N;UP PAIRED ARROWS;;;; +21C9;RIGHTWARDS PAIRED ARROWS;So;0;ON;;;;;N;RIGHT PAIRED ARROWS;;;; +21CA;DOWNWARDS PAIRED ARROWS;So;0;ON;;;;;N;DOWN PAIRED ARROWS;;;; +21CB;LEFTWARDS HARPOON OVER RIGHTWARDS HARPOON;So;0;ON;;;;;N;LEFT HARPOON OVER RIGHT HARPOON;;;; +21CC;RIGHTWARDS HARPOON OVER LEFTWARDS HARPOON;So;0;ON;;;;;N;RIGHT HARPOON OVER LEFT HARPOON;;;; +21CD;LEFTWARDS DOUBLE ARROW WITH STROKE;So;0;ON;21D0 0338;;;;N;LEFT DOUBLE ARROW WITH STROKE;;;; +21CE;LEFT RIGHT DOUBLE ARROW WITH STROKE;Sm;0;ON;21D4 0338;;;;N;;;;; +21CF;RIGHTWARDS DOUBLE ARROW WITH STROKE;Sm;0;ON;21D2 0338;;;;N;RIGHT DOUBLE ARROW WITH STROKE;;;; +21D0;LEFTWARDS DOUBLE ARROW;So;0;ON;;;;;N;LEFT DOUBLE ARROW;;;; +21D1;UPWARDS DOUBLE ARROW;So;0;ON;;;;;N;UP DOUBLE ARROW;;;; +21D2;RIGHTWARDS DOUBLE ARROW;Sm;0;ON;;;;;N;RIGHT DOUBLE ARROW;;;; +21D3;DOWNWARDS DOUBLE ARROW;So;0;ON;;;;;N;DOWN DOUBLE ARROW;;;; +21D4;LEFT RIGHT DOUBLE ARROW;Sm;0;ON;;;;;N;;;;; +21D5;UP DOWN DOUBLE ARROW;So;0;ON;;;;;N;;;;; +21D6;NORTH WEST DOUBLE ARROW;So;0;ON;;;;;N;UPPER LEFT DOUBLE ARROW;;;; +21D7;NORTH EAST DOUBLE ARROW;So;0;ON;;;;;N;UPPER RIGHT DOUBLE ARROW;;;; +21D8;SOUTH EAST DOUBLE ARROW;So;0;ON;;;;;N;LOWER RIGHT DOUBLE ARROW;;;; +21D9;SOUTH WEST DOUBLE ARROW;So;0;ON;;;;;N;LOWER LEFT DOUBLE ARROW;;;; +21DA;LEFTWARDS TRIPLE ARROW;So;0;ON;;;;;N;LEFT TRIPLE ARROW;;;; +21DB;RIGHTWARDS TRIPLE ARROW;So;0;ON;;;;;N;RIGHT TRIPLE ARROW;;;; +21DC;LEFTWARDS SQUIGGLE ARROW;So;0;ON;;;;;N;LEFT SQUIGGLE ARROW;;;; +21DD;RIGHTWARDS SQUIGGLE ARROW;So;0;ON;;;;;N;RIGHT SQUIGGLE ARROW;;;; +21DE;UPWARDS ARROW WITH DOUBLE STROKE;So;0;ON;;;;;N;UP ARROW WITH DOUBLE STROKE;;;; +21DF;DOWNWARDS ARROW WITH DOUBLE STROKE;So;0;ON;;;;;N;DOWN ARROW WITH DOUBLE STROKE;;;; +21E0;LEFTWARDS DASHED ARROW;So;0;ON;;;;;N;LEFT DASHED ARROW;;;; +21E1;UPWARDS DASHED ARROW;So;0;ON;;;;;N;UP DASHED ARROW;;;; +21E2;RIGHTWARDS DASHED ARROW;So;0;ON;;;;;N;RIGHT DASHED ARROW;;;; +21E3;DOWNWARDS DASHED ARROW;So;0;ON;;;;;N;DOWN DASHED ARROW;;;; +21E4;LEFTWARDS ARROW TO BAR;So;0;ON;;;;;N;LEFT ARROW TO BAR;;;; +21E5;RIGHTWARDS ARROW TO BAR;So;0;ON;;;;;N;RIGHT ARROW TO BAR;;;; +21E6;LEFTWARDS WHITE ARROW;So;0;ON;;;;;N;WHITE LEFT ARROW;;;; +21E7;UPWARDS WHITE ARROW;So;0;ON;;;;;N;WHITE UP ARROW;;;; +21E8;RIGHTWARDS WHITE ARROW;So;0;ON;;;;;N;WHITE RIGHT ARROW;;;; +21E9;DOWNWARDS WHITE ARROW;So;0;ON;;;;;N;WHITE DOWN ARROW;;;; +21EA;UPWARDS WHITE ARROW FROM BAR;So;0;ON;;;;;N;WHITE UP ARROW FROM BAR;;;; +21EB;UPWARDS WHITE ARROW ON PEDESTAL;So;0;ON;;;;;N;;;;; +21EC;UPWARDS WHITE ARROW ON PEDESTAL WITH HORIZONTAL BAR;So;0;ON;;;;;N;;;;; +21ED;UPWARDS WHITE ARROW ON PEDESTAL WITH VERTICAL BAR;So;0;ON;;;;;N;;;;; +21EE;UPWARDS WHITE DOUBLE ARROW;So;0;ON;;;;;N;;;;; +21EF;UPWARDS WHITE DOUBLE ARROW ON PEDESTAL;So;0;ON;;;;;N;;;;; +21F0;RIGHTWARDS WHITE ARROW FROM WALL;So;0;ON;;;;;N;;;;; +21F1;NORTH WEST ARROW TO CORNER;So;0;ON;;;;;N;;;;; +21F2;SOUTH EAST ARROW TO CORNER;So;0;ON;;;;;N;;;;; +21F3;UP DOWN WHITE ARROW;So;0;ON;;;;;N;;;;; +21F4;RIGHT ARROW WITH SMALL CIRCLE;Sm;0;ON;;;;;N;;;;; +21F5;DOWNWARDS ARROW LEFTWARDS OF UPWARDS ARROW;Sm;0;ON;;;;;N;;;;; +21F6;THREE RIGHTWARDS ARROWS;Sm;0;ON;;;;;N;;;;; +21F7;LEFTWARDS ARROW WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; +21F8;RIGHTWARDS ARROW WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; +21F9;LEFT RIGHT ARROW WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; +21FA;LEFTWARDS ARROW WITH DOUBLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; +21FB;RIGHTWARDS ARROW WITH DOUBLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; +21FC;LEFT RIGHT ARROW WITH DOUBLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; +21FD;LEFTWARDS OPEN-HEADED ARROW;Sm;0;ON;;;;;N;;;;; +21FE;RIGHTWARDS OPEN-HEADED ARROW;Sm;0;ON;;;;;N;;;;; +21FF;LEFT RIGHT OPEN-HEADED ARROW;Sm;0;ON;;;;;N;;;;; +2200;FOR ALL;Sm;0;ON;;;;;N;;;;; +2201;COMPLEMENT;Sm;0;ON;;;;;Y;;;;; +2202;PARTIAL DIFFERENTIAL;Sm;0;ON;;;;;Y;;;;; +2203;THERE EXISTS;Sm;0;ON;;;;;Y;;;;; +2204;THERE DOES NOT EXIST;Sm;0;ON;2203 0338;;;;Y;;;;; +2205;EMPTY SET;Sm;0;ON;;;;;N;;;;; +2206;INCREMENT;Sm;0;ON;;;;;N;;;;; +2207;NABLA;Sm;0;ON;;;;;N;;;;; +2208;ELEMENT OF;Sm;0;ON;;;;;Y;;;;; +2209;NOT AN ELEMENT OF;Sm;0;ON;2208 0338;;;;Y;;;;; +220A;SMALL ELEMENT OF;Sm;0;ON;;;;;Y;;;;; +220B;CONTAINS AS MEMBER;Sm;0;ON;;;;;Y;;;;; +220C;DOES NOT CONTAIN AS MEMBER;Sm;0;ON;220B 0338;;;;Y;;;;; +220D;SMALL CONTAINS AS MEMBER;Sm;0;ON;;;;;Y;;;;; +220E;END OF PROOF;Sm;0;ON;;;;;N;;;;; +220F;N-ARY PRODUCT;Sm;0;ON;;;;;N;;;;; +2210;N-ARY COPRODUCT;Sm;0;ON;;;;;N;;;;; +2211;N-ARY SUMMATION;Sm;0;ON;;;;;Y;;;;; +2212;MINUS SIGN;Sm;0;ET;;;;;N;;;;; +2213;MINUS-OR-PLUS SIGN;Sm;0;ET;;;;;N;;;;; +2214;DOT PLUS;Sm;0;ON;;;;;N;;;;; +2215;DIVISION SLASH;Sm;0;ON;;;;;Y;;;;; +2216;SET MINUS;Sm;0;ON;;;;;Y;;;;; +2217;ASTERISK OPERATOR;Sm;0;ON;;;;;N;;;;; +2218;RING OPERATOR;Sm;0;ON;;;;;N;;;;; +2219;BULLET OPERATOR;Sm;0;ON;;;;;N;;;;; +221A;SQUARE ROOT;Sm;0;ON;;;;;Y;;;;; +221B;CUBE ROOT;Sm;0;ON;;;;;Y;;;;; +221C;FOURTH ROOT;Sm;0;ON;;;;;Y;;;;; +221D;PROPORTIONAL TO;Sm;0;ON;;;;;Y;;;;; +221E;INFINITY;Sm;0;ON;;;;;N;;;;; +221F;RIGHT ANGLE;Sm;0;ON;;;;;Y;;;;; +2220;ANGLE;Sm;0;ON;;;;;Y;;;;; +2221;MEASURED ANGLE;Sm;0;ON;;;;;Y;;;;; +2222;SPHERICAL ANGLE;Sm;0;ON;;;;;Y;;;;; +2223;DIVIDES;Sm;0;ON;;;;;N;;;;; +2224;DOES NOT DIVIDE;Sm;0;ON;2223 0338;;;;Y;;;;; +2225;PARALLEL TO;Sm;0;ON;;;;;N;;;;; +2226;NOT PARALLEL TO;Sm;0;ON;2225 0338;;;;Y;;;;; +2227;LOGICAL AND;Sm;0;ON;;;;;N;;;;; +2228;LOGICAL OR;Sm;0;ON;;;;;N;;;;; +2229;INTERSECTION;Sm;0;ON;;;;;N;;;;; +222A;UNION;Sm;0;ON;;;;;N;;;;; +222B;INTEGRAL;Sm;0;ON;;;;;Y;;;;; +222C;DOUBLE INTEGRAL;Sm;0;ON;<compat> 222B 222B;;;;Y;;;;; +222D;TRIPLE INTEGRAL;Sm;0;ON;<compat> 222B 222B 222B;;;;Y;;;;; +222E;CONTOUR INTEGRAL;Sm;0;ON;;;;;Y;;;;; +222F;SURFACE INTEGRAL;Sm;0;ON;<compat> 222E 222E;;;;Y;;;;; +2230;VOLUME INTEGRAL;Sm;0;ON;<compat> 222E 222E 222E;;;;Y;;;;; +2231;CLOCKWISE INTEGRAL;Sm;0;ON;;;;;Y;;;;; +2232;CLOCKWISE CONTOUR INTEGRAL;Sm;0;ON;;;;;Y;;;;; +2233;ANTICLOCKWISE CONTOUR INTEGRAL;Sm;0;ON;;;;;Y;;;;; +2234;THEREFORE;Sm;0;ON;;;;;N;;;;; +2235;BECAUSE;Sm;0;ON;;;;;N;;;;; +2236;RATIO;Sm;0;ON;;;;;N;;;;; +2237;PROPORTION;Sm;0;ON;;;;;N;;;;; +2238;DOT MINUS;Sm;0;ON;;;;;N;;;;; +2239;EXCESS;Sm;0;ON;;;;;Y;;;;; +223A;GEOMETRIC PROPORTION;Sm;0;ON;;;;;N;;;;; +223B;HOMOTHETIC;Sm;0;ON;;;;;Y;;;;; +223C;TILDE OPERATOR;Sm;0;ON;;;;;Y;;;;; +223D;REVERSED TILDE;Sm;0;ON;;;;;Y;;lazy S;;; +223E;INVERTED LAZY S;Sm;0;ON;;;;;Y;;;;; +223F;SINE WAVE;Sm;0;ON;;;;;Y;;;;; +2240;WREATH PRODUCT;Sm;0;ON;;;;;Y;;;;; +2241;NOT TILDE;Sm;0;ON;223C 0338;;;;Y;;;;; +2242;MINUS TILDE;Sm;0;ON;;;;;Y;;;;; +2243;ASYMPTOTICALLY EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2244;NOT ASYMPTOTICALLY EQUAL TO;Sm;0;ON;2243 0338;;;;Y;;;;; +2245;APPROXIMATELY EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2246;APPROXIMATELY BUT NOT ACTUALLY EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2247;NEITHER APPROXIMATELY NOR ACTUALLY EQUAL TO;Sm;0;ON;2245 0338;;;;Y;;;;; +2248;ALMOST EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2249;NOT ALMOST EQUAL TO;Sm;0;ON;2248 0338;;;;Y;;;;; +224A;ALMOST EQUAL OR EQUAL TO;Sm;0;ON;;;;;Y;;;;; +224B;TRIPLE TILDE;Sm;0;ON;;;;;Y;;;;; +224C;ALL EQUAL TO;Sm;0;ON;;;;;Y;;;;; +224D;EQUIVALENT TO;Sm;0;ON;;;;;N;;;;; +224E;GEOMETRICALLY EQUIVALENT TO;Sm;0;ON;;;;;N;;;;; +224F;DIFFERENCE BETWEEN;Sm;0;ON;;;;;N;;;;; +2250;APPROACHES THE LIMIT;Sm;0;ON;;;;;N;;;;; +2251;GEOMETRICALLY EQUAL TO;Sm;0;ON;;;;;N;;;;; +2252;APPROXIMATELY EQUAL TO OR THE IMAGE OF;Sm;0;ON;;;;;Y;;;;; +2253;IMAGE OF OR APPROXIMATELY EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2254;COLON EQUALS;Sm;0;ON;;;;;Y;COLON EQUAL;;;; +2255;EQUALS COLON;Sm;0;ON;;;;;Y;EQUAL COLON;;;; +2256;RING IN EQUAL TO;Sm;0;ON;;;;;N;;;;; +2257;RING EQUAL TO;Sm;0;ON;;;;;N;;;;; +2258;CORRESPONDS TO;Sm;0;ON;;;;;N;;;;; +2259;ESTIMATES;Sm;0;ON;;;;;N;;;;; +225A;EQUIANGULAR TO;Sm;0;ON;;;;;N;;;;; +225B;STAR EQUALS;Sm;0;ON;;;;;N;;;;; +225C;DELTA EQUAL TO;Sm;0;ON;;;;;N;;;;; +225D;EQUAL TO BY DEFINITION;Sm;0;ON;;;;;N;;;;; +225E;MEASURED BY;Sm;0;ON;;;;;N;;;;; +225F;QUESTIONED EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2260;NOT EQUAL TO;Sm;0;ON;003D 0338;;;;Y;;;;; +2261;IDENTICAL TO;Sm;0;ON;;;;;N;;;;; +2262;NOT IDENTICAL TO;Sm;0;ON;2261 0338;;;;Y;;;;; +2263;STRICTLY EQUIVALENT TO;Sm;0;ON;;;;;N;;;;; +2264;LESS-THAN OR EQUAL TO;Sm;0;ON;;;;;Y;LESS THAN OR EQUAL TO;;;; +2265;GREATER-THAN OR EQUAL TO;Sm;0;ON;;;;;Y;GREATER THAN OR EQUAL TO;;;; +2266;LESS-THAN OVER EQUAL TO;Sm;0;ON;;;;;Y;LESS THAN OVER EQUAL TO;;;; +2267;GREATER-THAN OVER EQUAL TO;Sm;0;ON;;;;;Y;GREATER THAN OVER EQUAL TO;;;; +2268;LESS-THAN BUT NOT EQUAL TO;Sm;0;ON;;;;;Y;LESS THAN BUT NOT EQUAL TO;;;; +2269;GREATER-THAN BUT NOT EQUAL TO;Sm;0;ON;;;;;Y;GREATER THAN BUT NOT EQUAL TO;;;; +226A;MUCH LESS-THAN;Sm;0;ON;;;;;Y;MUCH LESS THAN;;;; +226B;MUCH GREATER-THAN;Sm;0;ON;;;;;Y;MUCH GREATER THAN;;;; +226C;BETWEEN;Sm;0;ON;;;;;N;;;;; +226D;NOT EQUIVALENT TO;Sm;0;ON;224D 0338;;;;N;;;;; +226E;NOT LESS-THAN;Sm;0;ON;003C 0338;;;;Y;NOT LESS THAN;;;; +226F;NOT GREATER-THAN;Sm;0;ON;003E 0338;;;;Y;NOT GREATER THAN;;;; +2270;NEITHER LESS-THAN NOR EQUAL TO;Sm;0;ON;2264 0338;;;;Y;NEITHER LESS THAN NOR EQUAL TO;;;; +2271;NEITHER GREATER-THAN NOR EQUAL TO;Sm;0;ON;2265 0338;;;;Y;NEITHER GREATER THAN NOR EQUAL TO;;;; +2272;LESS-THAN OR EQUIVALENT TO;Sm;0;ON;;;;;Y;LESS THAN OR EQUIVALENT TO;;;; +2273;GREATER-THAN OR EQUIVALENT TO;Sm;0;ON;;;;;Y;GREATER THAN OR EQUIVALENT TO;;;; +2274;NEITHER LESS-THAN NOR EQUIVALENT TO;Sm;0;ON;2272 0338;;;;Y;NEITHER LESS THAN NOR EQUIVALENT TO;;;; +2275;NEITHER GREATER-THAN NOR EQUIVALENT TO;Sm;0;ON;2273 0338;;;;Y;NEITHER GREATER THAN NOR EQUIVALENT TO;;;; +2276;LESS-THAN OR GREATER-THAN;Sm;0;ON;;;;;Y;LESS THAN OR GREATER THAN;;;; +2277;GREATER-THAN OR LESS-THAN;Sm;0;ON;;;;;Y;GREATER THAN OR LESS THAN;;;; +2278;NEITHER LESS-THAN NOR GREATER-THAN;Sm;0;ON;2276 0338;;;;Y;NEITHER LESS THAN NOR GREATER THAN;;;; +2279;NEITHER GREATER-THAN NOR LESS-THAN;Sm;0;ON;2277 0338;;;;Y;NEITHER GREATER THAN NOR LESS THAN;;;; +227A;PRECEDES;Sm;0;ON;;;;;Y;;;;; +227B;SUCCEEDS;Sm;0;ON;;;;;Y;;;;; +227C;PRECEDES OR EQUAL TO;Sm;0;ON;;;;;Y;;;;; +227D;SUCCEEDS OR EQUAL TO;Sm;0;ON;;;;;Y;;;;; +227E;PRECEDES OR EQUIVALENT TO;Sm;0;ON;;;;;Y;;;;; +227F;SUCCEEDS OR EQUIVALENT TO;Sm;0;ON;;;;;Y;;;;; +2280;DOES NOT PRECEDE;Sm;0;ON;227A 0338;;;;Y;;;;; +2281;DOES NOT SUCCEED;Sm;0;ON;227B 0338;;;;Y;;;;; +2282;SUBSET OF;Sm;0;ON;;;;;Y;;;;; +2283;SUPERSET OF;Sm;0;ON;;;;;Y;;;;; +2284;NOT A SUBSET OF;Sm;0;ON;2282 0338;;;;Y;;;;; +2285;NOT A SUPERSET OF;Sm;0;ON;2283 0338;;;;Y;;;;; +2286;SUBSET OF OR EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2287;SUPERSET OF OR EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2288;NEITHER A SUBSET OF NOR EQUAL TO;Sm;0;ON;2286 0338;;;;Y;;;;; +2289;NEITHER A SUPERSET OF NOR EQUAL TO;Sm;0;ON;2287 0338;;;;Y;;;;; +228A;SUBSET OF WITH NOT EQUAL TO;Sm;0;ON;;;;;Y;SUBSET OF OR NOT EQUAL TO;;;; +228B;SUPERSET OF WITH NOT EQUAL TO;Sm;0;ON;;;;;Y;SUPERSET OF OR NOT EQUAL TO;;;; +228C;MULTISET;Sm;0;ON;;;;;Y;;;;; +228D;MULTISET MULTIPLICATION;Sm;0;ON;;;;;N;;;;; +228E;MULTISET UNION;Sm;0;ON;;;;;N;;;;; +228F;SQUARE IMAGE OF;Sm;0;ON;;;;;Y;;;;; +2290;SQUARE ORIGINAL OF;Sm;0;ON;;;;;Y;;;;; +2291;SQUARE IMAGE OF OR EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2292;SQUARE ORIGINAL OF OR EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2293;SQUARE CAP;Sm;0;ON;;;;;N;;;;; +2294;SQUARE CUP;Sm;0;ON;;;;;N;;;;; +2295;CIRCLED PLUS;Sm;0;ON;;;;;N;;;;; +2296;CIRCLED MINUS;Sm;0;ON;;;;;N;;;;; +2297;CIRCLED TIMES;Sm;0;ON;;;;;N;;;;; +2298;CIRCLED DIVISION SLASH;Sm;0;ON;;;;;Y;;;;; +2299;CIRCLED DOT OPERATOR;Sm;0;ON;;;;;N;;;;; +229A;CIRCLED RING OPERATOR;Sm;0;ON;;;;;N;;;;; +229B;CIRCLED ASTERISK OPERATOR;Sm;0;ON;;;;;N;;;;; +229C;CIRCLED EQUALS;Sm;0;ON;;;;;N;;;;; +229D;CIRCLED DASH;Sm;0;ON;;;;;N;;;;; +229E;SQUARED PLUS;Sm;0;ON;;;;;N;;;;; +229F;SQUARED MINUS;Sm;0;ON;;;;;N;;;;; +22A0;SQUARED TIMES;Sm;0;ON;;;;;N;;;;; +22A1;SQUARED DOT OPERATOR;Sm;0;ON;;;;;N;;;;; +22A2;RIGHT TACK;Sm;0;ON;;;;;Y;;;;; +22A3;LEFT TACK;Sm;0;ON;;;;;Y;;;;; +22A4;DOWN TACK;Sm;0;ON;;;;;N;;;;; +22A5;UP TACK;Sm;0;ON;;;;;N;;;;; +22A6;ASSERTION;Sm;0;ON;;;;;Y;;;;; +22A7;MODELS;Sm;0;ON;;;;;Y;;;;; +22A8;TRUE;Sm;0;ON;;;;;Y;;;;; +22A9;FORCES;Sm;0;ON;;;;;Y;;;;; +22AA;TRIPLE VERTICAL BAR RIGHT TURNSTILE;Sm;0;ON;;;;;Y;;;;; +22AB;DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE;Sm;0;ON;;;;;Y;;;;; +22AC;DOES NOT PROVE;Sm;0;ON;22A2 0338;;;;Y;;;;; +22AD;NOT TRUE;Sm;0;ON;22A8 0338;;;;Y;;;;; +22AE;DOES NOT FORCE;Sm;0;ON;22A9 0338;;;;Y;;;;; +22AF;NEGATED DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE;Sm;0;ON;22AB 0338;;;;Y;;;;; +22B0;PRECEDES UNDER RELATION;Sm;0;ON;;;;;Y;;;;; +22B1;SUCCEEDS UNDER RELATION;Sm;0;ON;;;;;Y;;;;; +22B2;NORMAL SUBGROUP OF;Sm;0;ON;;;;;Y;;;;; +22B3;CONTAINS AS NORMAL SUBGROUP;Sm;0;ON;;;;;Y;;;;; +22B4;NORMAL SUBGROUP OF OR EQUAL TO;Sm;0;ON;;;;;Y;;;;; +22B5;CONTAINS AS NORMAL SUBGROUP OR EQUAL TO;Sm;0;ON;;;;;Y;;;;; +22B6;ORIGINAL OF;Sm;0;ON;;;;;Y;;;;; +22B7;IMAGE OF;Sm;0;ON;;;;;Y;;;;; +22B8;MULTIMAP;Sm;0;ON;;;;;Y;;;;; +22B9;HERMITIAN CONJUGATE MATRIX;Sm;0;ON;;;;;N;;;;; +22BA;INTERCALATE;Sm;0;ON;;;;;N;;;;; +22BB;XOR;Sm;0;ON;;;;;N;;;;; +22BC;NAND;Sm;0;ON;;;;;N;;;;; +22BD;NOR;Sm;0;ON;;;;;N;;;;; +22BE;RIGHT ANGLE WITH ARC;Sm;0;ON;;;;;Y;;;;; +22BF;RIGHT TRIANGLE;Sm;0;ON;;;;;Y;;;;; +22C0;N-ARY LOGICAL AND;Sm;0;ON;;;;;N;;;;; +22C1;N-ARY LOGICAL OR;Sm;0;ON;;;;;N;;;;; +22C2;N-ARY INTERSECTION;Sm;0;ON;;;;;N;;;;; +22C3;N-ARY UNION;Sm;0;ON;;;;;N;;;;; +22C4;DIAMOND OPERATOR;Sm;0;ON;;;;;N;;;;; +22C5;DOT OPERATOR;Sm;0;ON;;;;;N;;;;; +22C6;STAR OPERATOR;Sm;0;ON;;;;;N;;;;; +22C7;DIVISION TIMES;Sm;0;ON;;;;;N;;;;; +22C8;BOWTIE;Sm;0;ON;;;;;N;;;;; +22C9;LEFT NORMAL FACTOR SEMIDIRECT PRODUCT;Sm;0;ON;;;;;Y;;;;; +22CA;RIGHT NORMAL FACTOR SEMIDIRECT PRODUCT;Sm;0;ON;;;;;Y;;;;; +22CB;LEFT SEMIDIRECT PRODUCT;Sm;0;ON;;;;;Y;;;;; +22CC;RIGHT SEMIDIRECT PRODUCT;Sm;0;ON;;;;;Y;;;;; +22CD;REVERSED TILDE EQUALS;Sm;0;ON;;;;;Y;;;;; +22CE;CURLY LOGICAL OR;Sm;0;ON;;;;;N;;;;; +22CF;CURLY LOGICAL AND;Sm;0;ON;;;;;N;;;;; +22D0;DOUBLE SUBSET;Sm;0;ON;;;;;Y;;;;; +22D1;DOUBLE SUPERSET;Sm;0;ON;;;;;Y;;;;; +22D2;DOUBLE INTERSECTION;Sm;0;ON;;;;;N;;;;; +22D3;DOUBLE UNION;Sm;0;ON;;;;;N;;;;; +22D4;PITCHFORK;Sm;0;ON;;;;;N;;;;; +22D5;EQUAL AND PARALLEL TO;Sm;0;ON;;;;;N;;;;; +22D6;LESS-THAN WITH DOT;Sm;0;ON;;;;;Y;LESS THAN WITH DOT;;;; +22D7;GREATER-THAN WITH DOT;Sm;0;ON;;;;;Y;GREATER THAN WITH DOT;;;; +22D8;VERY MUCH LESS-THAN;Sm;0;ON;;;;;Y;VERY MUCH LESS THAN;;;; +22D9;VERY MUCH GREATER-THAN;Sm;0;ON;;;;;Y;VERY MUCH GREATER THAN;;;; +22DA;LESS-THAN EQUAL TO OR GREATER-THAN;Sm;0;ON;;;;;Y;LESS THAN EQUAL TO OR GREATER THAN;;;; +22DB;GREATER-THAN EQUAL TO OR LESS-THAN;Sm;0;ON;;;;;Y;GREATER THAN EQUAL TO OR LESS THAN;;;; +22DC;EQUAL TO OR LESS-THAN;Sm;0;ON;;;;;Y;EQUAL TO OR LESS THAN;;;; +22DD;EQUAL TO OR GREATER-THAN;Sm;0;ON;;;;;Y;EQUAL TO OR GREATER THAN;;;; +22DE;EQUAL TO OR PRECEDES;Sm;0;ON;;;;;Y;;;;; +22DF;EQUAL TO OR SUCCEEDS;Sm;0;ON;;;;;Y;;;;; +22E0;DOES NOT PRECEDE OR EQUAL;Sm;0;ON;227C 0338;;;;Y;;;;; +22E1;DOES NOT SUCCEED OR EQUAL;Sm;0;ON;227D 0338;;;;Y;;;;; +22E2;NOT SQUARE IMAGE OF OR EQUAL TO;Sm;0;ON;2291 0338;;;;Y;;;;; +22E3;NOT SQUARE ORIGINAL OF OR EQUAL TO;Sm;0;ON;2292 0338;;;;Y;;;;; +22E4;SQUARE IMAGE OF OR NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;; +22E5;SQUARE ORIGINAL OF OR NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;; +22E6;LESS-THAN BUT NOT EQUIVALENT TO;Sm;0;ON;;;;;Y;LESS THAN BUT NOT EQUIVALENT TO;;;; +22E7;GREATER-THAN BUT NOT EQUIVALENT TO;Sm;0;ON;;;;;Y;GREATER THAN BUT NOT EQUIVALENT TO;;;; +22E8;PRECEDES BUT NOT EQUIVALENT TO;Sm;0;ON;;;;;Y;;;;; +22E9;SUCCEEDS BUT NOT EQUIVALENT TO;Sm;0;ON;;;;;Y;;;;; +22EA;NOT NORMAL SUBGROUP OF;Sm;0;ON;22B2 0338;;;;Y;;;;; +22EB;DOES NOT CONTAIN AS NORMAL SUBGROUP;Sm;0;ON;22B3 0338;;;;Y;;;;; +22EC;NOT NORMAL SUBGROUP OF OR EQUAL TO;Sm;0;ON;22B4 0338;;;;Y;;;;; +22ED;DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL;Sm;0;ON;22B5 0338;;;;Y;;;;; +22EE;VERTICAL ELLIPSIS;Sm;0;ON;;;;;N;;;;; +22EF;MIDLINE HORIZONTAL ELLIPSIS;Sm;0;ON;;;;;N;;;;; +22F0;UP RIGHT DIAGONAL ELLIPSIS;Sm;0;ON;;;;;Y;;;;; +22F1;DOWN RIGHT DIAGONAL ELLIPSIS;Sm;0;ON;;;;;Y;;;;; +22F2;ELEMENT OF WITH LONG HORIZONTAL STROKE;Sm;0;ON;;;;;Y;;;;; +22F3;ELEMENT OF WITH VERTICAL BAR AT END OF HORIZONTAL STROKE;Sm;0;ON;;;;;Y;;;;; +22F4;SMALL ELEMENT OF WITH VERTICAL BAR AT END OF HORIZONTAL STROKE;Sm;0;ON;;;;;Y;;;;; +22F5;ELEMENT OF WITH DOT ABOVE;Sm;0;ON;;;;;Y;;;;; +22F6;ELEMENT OF WITH OVERBAR;Sm;0;ON;;;;;Y;;;;; +22F7;SMALL ELEMENT OF WITH OVERBAR;Sm;0;ON;;;;;Y;;;;; +22F8;ELEMENT OF WITH UNDERBAR;Sm;0;ON;;;;;Y;;;;; +22F9;ELEMENT OF WITH TWO HORIZONTAL STROKES;Sm;0;ON;;;;;Y;;;;; +22FA;CONTAINS WITH LONG HORIZONTAL STROKE;Sm;0;ON;;;;;Y;;;;; +22FB;CONTAINS WITH VERTICAL BAR AT END OF HORIZONTAL STROKE;Sm;0;ON;;;;;Y;;;;; +22FC;SMALL CONTAINS WITH VERTICAL BAR AT END OF HORIZONTAL STROKE;Sm;0;ON;;;;;Y;;;;; +22FD;CONTAINS WITH OVERBAR;Sm;0;ON;;;;;Y;;;;; +22FE;SMALL CONTAINS WITH OVERBAR;Sm;0;ON;;;;;Y;;;;; +22FF;Z NOTATION BAG MEMBERSHIP;Sm;0;ON;;;;;Y;;;;; +2300;DIAMETER SIGN;So;0;ON;;;;;N;;;;; +2301;ELECTRIC ARROW;So;0;ON;;;;;N;;;;; +2302;HOUSE;So;0;ON;;;;;N;;;;; +2303;UP ARROWHEAD;So;0;ON;;;;;N;;;;; +2304;DOWN ARROWHEAD;So;0;ON;;;;;N;;;;; +2305;PROJECTIVE;So;0;ON;;;;;N;;;;; +2306;PERSPECTIVE;So;0;ON;;;;;N;;;;; +2307;WAVY LINE;So;0;ON;;;;;N;;;;; +2308;LEFT CEILING;Sm;0;ON;;;;;Y;;;;; +2309;RIGHT CEILING;Sm;0;ON;;;;;Y;;;;; +230A;LEFT FLOOR;Sm;0;ON;;;;;Y;;;;; +230B;RIGHT FLOOR;Sm;0;ON;;;;;Y;;;;; +230C;BOTTOM RIGHT CROP;So;0;ON;;;;;N;;;;; +230D;BOTTOM LEFT CROP;So;0;ON;;;;;N;;;;; +230E;TOP RIGHT CROP;So;0;ON;;;;;N;;;;; +230F;TOP LEFT CROP;So;0;ON;;;;;N;;;;; +2310;REVERSED NOT SIGN;So;0;ON;;;;;N;;;;; +2311;SQUARE LOZENGE;So;0;ON;;;;;N;;;;; +2312;ARC;So;0;ON;;;;;N;;;;; +2313;SEGMENT;So;0;ON;;;;;N;;;;; +2314;SECTOR;So;0;ON;;;;;N;;;;; +2315;TELEPHONE RECORDER;So;0;ON;;;;;N;;;;; +2316;POSITION INDICATOR;So;0;ON;;;;;N;;;;; +2317;VIEWDATA SQUARE;So;0;ON;;;;;N;;;;; +2318;PLACE OF INTEREST SIGN;So;0;ON;;;;;N;COMMAND KEY;;;; +2319;TURNED NOT SIGN;So;0;ON;;;;;N;;;;; +231A;WATCH;So;0;ON;;;;;N;;;;; +231B;HOURGLASS;So;0;ON;;;;;N;;;;; +231C;TOP LEFT CORNER;So;0;ON;;;;;N;;;;; +231D;TOP RIGHT CORNER;So;0;ON;;;;;N;;;;; +231E;BOTTOM LEFT CORNER;So;0;ON;;;;;N;;;;; +231F;BOTTOM RIGHT CORNER;So;0;ON;;;;;N;;;;; +2320;TOP HALF INTEGRAL;Sm;0;ON;;;;;Y;;;;; +2321;BOTTOM HALF INTEGRAL;Sm;0;ON;;;;;Y;;;;; +2322;FROWN;So;0;ON;;;;;N;;;;; +2323;SMILE;So;0;ON;;;;;N;;;;; +2324;UP ARROWHEAD BETWEEN TWO HORIZONTAL BARS;So;0;ON;;;;;N;ENTER KEY;;;; +2325;OPTION KEY;So;0;ON;;;;;N;;;;; +2326;ERASE TO THE RIGHT;So;0;ON;;;;;N;DELETE TO THE RIGHT KEY;;;; +2327;X IN A RECTANGLE BOX;So;0;ON;;;;;N;CLEAR KEY;;;; +2328;KEYBOARD;So;0;ON;;;;;N;;;;; +2329;LEFT-POINTING ANGLE BRACKET;Ps;0;ON;3008;;;;Y;BRA;;;; +232A;RIGHT-POINTING ANGLE BRACKET;Pe;0;ON;3009;;;;Y;KET;;;; +232B;ERASE TO THE LEFT;So;0;ON;;;;;N;DELETE TO THE LEFT KEY;;;; +232C;BENZENE RING;So;0;ON;;;;;N;;;;; +232D;CYLINDRICITY;So;0;ON;;;;;N;;;;; +232E;ALL AROUND-PROFILE;So;0;ON;;;;;N;;;;; +232F;SYMMETRY;So;0;ON;;;;;N;;;;; +2330;TOTAL RUNOUT;So;0;ON;;;;;N;;;;; +2331;DIMENSION ORIGIN;So;0;ON;;;;;N;;;;; +2332;CONICAL TAPER;So;0;ON;;;;;N;;;;; +2333;SLOPE;So;0;ON;;;;;N;;;;; +2334;COUNTERBORE;So;0;ON;;;;;N;;;;; +2335;COUNTERSINK;So;0;ON;;;;;N;;;;; +2336;APL FUNCTIONAL SYMBOL I-BEAM;So;0;L;;;;;N;;;;; +2337;APL FUNCTIONAL SYMBOL SQUISH QUAD;So;0;L;;;;;N;;;;; +2338;APL FUNCTIONAL SYMBOL QUAD EQUAL;So;0;L;;;;;N;;;;; +2339;APL FUNCTIONAL SYMBOL QUAD DIVIDE;So;0;L;;;;;N;;;;; +233A;APL FUNCTIONAL SYMBOL QUAD DIAMOND;So;0;L;;;;;N;;;;; +233B;APL FUNCTIONAL SYMBOL QUAD JOT;So;0;L;;;;;N;;;;; +233C;APL FUNCTIONAL SYMBOL QUAD CIRCLE;So;0;L;;;;;N;;;;; +233D;APL FUNCTIONAL SYMBOL CIRCLE STILE;So;0;L;;;;;N;;;;; +233E;APL FUNCTIONAL SYMBOL CIRCLE JOT;So;0;L;;;;;N;;;;; +233F;APL FUNCTIONAL SYMBOL SLASH BAR;So;0;L;;;;;N;;;;; +2340;APL FUNCTIONAL SYMBOL BACKSLASH BAR;So;0;L;;;;;N;;;;; +2341;APL FUNCTIONAL SYMBOL QUAD SLASH;So;0;L;;;;;N;;;;; +2342;APL FUNCTIONAL SYMBOL QUAD BACKSLASH;So;0;L;;;;;N;;;;; +2343;APL FUNCTIONAL SYMBOL QUAD LESS-THAN;So;0;L;;;;;N;;;;; +2344;APL FUNCTIONAL SYMBOL QUAD GREATER-THAN;So;0;L;;;;;N;;;;; +2345;APL FUNCTIONAL SYMBOL LEFTWARDS VANE;So;0;L;;;;;N;;;;; +2346;APL FUNCTIONAL SYMBOL RIGHTWARDS VANE;So;0;L;;;;;N;;;;; +2347;APL FUNCTIONAL SYMBOL QUAD LEFTWARDS ARROW;So;0;L;;;;;N;;;;; +2348;APL FUNCTIONAL SYMBOL QUAD RIGHTWARDS ARROW;So;0;L;;;;;N;;;;; +2349;APL FUNCTIONAL SYMBOL CIRCLE BACKSLASH;So;0;L;;;;;N;;;;; +234A;APL FUNCTIONAL SYMBOL DOWN TACK UNDERBAR;So;0;L;;;;;N;;*;;; +234B;APL FUNCTIONAL SYMBOL DELTA STILE;So;0;L;;;;;N;;;;; +234C;APL FUNCTIONAL SYMBOL QUAD DOWN CARET;So;0;L;;;;;N;;;;; +234D;APL FUNCTIONAL SYMBOL QUAD DELTA;So;0;L;;;;;N;;;;; +234E;APL FUNCTIONAL SYMBOL DOWN TACK JOT;So;0;L;;;;;N;;*;;; +234F;APL FUNCTIONAL SYMBOL UPWARDS VANE;So;0;L;;;;;N;;;;; +2350;APL FUNCTIONAL SYMBOL QUAD UPWARDS ARROW;So;0;L;;;;;N;;;;; +2351;APL FUNCTIONAL SYMBOL UP TACK OVERBAR;So;0;L;;;;;N;;*;;; +2352;APL FUNCTIONAL SYMBOL DEL STILE;So;0;L;;;;;N;;;;; +2353;APL FUNCTIONAL SYMBOL QUAD UP CARET;So;0;L;;;;;N;;;;; +2354;APL FUNCTIONAL SYMBOL QUAD DEL;So;0;L;;;;;N;;;;; +2355;APL FUNCTIONAL SYMBOL UP TACK JOT;So;0;L;;;;;N;;*;;; +2356;APL FUNCTIONAL SYMBOL DOWNWARDS VANE;So;0;L;;;;;N;;;;; +2357;APL FUNCTIONAL SYMBOL QUAD DOWNWARDS ARROW;So;0;L;;;;;N;;;;; +2358;APL FUNCTIONAL SYMBOL QUOTE UNDERBAR;So;0;L;;;;;N;;;;; +2359;APL FUNCTIONAL SYMBOL DELTA UNDERBAR;So;0;L;;;;;N;;;;; +235A;APL FUNCTIONAL SYMBOL DIAMOND UNDERBAR;So;0;L;;;;;N;;;;; +235B;APL FUNCTIONAL SYMBOL JOT UNDERBAR;So;0;L;;;;;N;;;;; +235C;APL FUNCTIONAL SYMBOL CIRCLE UNDERBAR;So;0;L;;;;;N;;;;; +235D;APL FUNCTIONAL SYMBOL UP SHOE JOT;So;0;L;;;;;N;;;;; +235E;APL FUNCTIONAL SYMBOL QUOTE QUAD;So;0;L;;;;;N;;;;; +235F;APL FUNCTIONAL SYMBOL CIRCLE STAR;So;0;L;;;;;N;;;;; +2360;APL FUNCTIONAL SYMBOL QUAD COLON;So;0;L;;;;;N;;;;; +2361;APL FUNCTIONAL SYMBOL UP TACK DIAERESIS;So;0;L;;;;;N;;*;;; +2362;APL FUNCTIONAL SYMBOL DEL DIAERESIS;So;0;L;;;;;N;;;;; +2363;APL FUNCTIONAL SYMBOL STAR DIAERESIS;So;0;L;;;;;N;;;;; +2364;APL FUNCTIONAL SYMBOL JOT DIAERESIS;So;0;L;;;;;N;;;;; +2365;APL FUNCTIONAL SYMBOL CIRCLE DIAERESIS;So;0;L;;;;;N;;;;; +2366;APL FUNCTIONAL SYMBOL DOWN SHOE STILE;So;0;L;;;;;N;;;;; +2367;APL FUNCTIONAL SYMBOL LEFT SHOE STILE;So;0;L;;;;;N;;;;; +2368;APL FUNCTIONAL SYMBOL TILDE DIAERESIS;So;0;L;;;;;N;;;;; +2369;APL FUNCTIONAL SYMBOL GREATER-THAN DIAERESIS;So;0;L;;;;;N;;;;; +236A;APL FUNCTIONAL SYMBOL COMMA BAR;So;0;L;;;;;N;;;;; +236B;APL FUNCTIONAL SYMBOL DEL TILDE;So;0;L;;;;;N;;;;; +236C;APL FUNCTIONAL SYMBOL ZILDE;So;0;L;;;;;N;;;;; +236D;APL FUNCTIONAL SYMBOL STILE TILDE;So;0;L;;;;;N;;;;; +236E;APL FUNCTIONAL SYMBOL SEMICOLON UNDERBAR;So;0;L;;;;;N;;;;; +236F;APL FUNCTIONAL SYMBOL QUAD NOT EQUAL;So;0;L;;;;;N;;;;; +2370;APL FUNCTIONAL SYMBOL QUAD QUESTION;So;0;L;;;;;N;;;;; +2371;APL FUNCTIONAL SYMBOL DOWN CARET TILDE;So;0;L;;;;;N;;;;; +2372;APL FUNCTIONAL SYMBOL UP CARET TILDE;So;0;L;;;;;N;;;;; +2373;APL FUNCTIONAL SYMBOL IOTA;So;0;L;;;;;N;;;;; +2374;APL FUNCTIONAL SYMBOL RHO;So;0;L;;;;;N;;;;; +2375;APL FUNCTIONAL SYMBOL OMEGA;So;0;L;;;;;N;;;;; +2376;APL FUNCTIONAL SYMBOL ALPHA UNDERBAR;So;0;L;;;;;N;;;;; +2377;APL FUNCTIONAL SYMBOL EPSILON UNDERBAR;So;0;L;;;;;N;;;;; +2378;APL FUNCTIONAL SYMBOL IOTA UNDERBAR;So;0;L;;;;;N;;;;; +2379;APL FUNCTIONAL SYMBOL OMEGA UNDERBAR;So;0;L;;;;;N;;;;; +237A;APL FUNCTIONAL SYMBOL ALPHA;So;0;L;;;;;N;;;;; +237B;NOT CHECK MARK;So;0;ON;;;;;N;;;;; +237C;RIGHT ANGLE WITH DOWNWARDS ZIGZAG ARROW;Sm;0;ON;;;;;N;;;;; +237D;SHOULDERED OPEN BOX;So;0;ON;;;;;N;;;;; +237E;BELL SYMBOL;So;0;ON;;;;;N;;;;; +237F;VERTICAL LINE WITH MIDDLE DOT;So;0;ON;;;;;N;;;;; +2380;INSERTION SYMBOL;So;0;ON;;;;;N;;;;; +2381;CONTINUOUS UNDERLINE SYMBOL;So;0;ON;;;;;N;;;;; +2382;DISCONTINUOUS UNDERLINE SYMBOL;So;0;ON;;;;;N;;;;; +2383;EMPHASIS SYMBOL;So;0;ON;;;;;N;;;;; +2384;COMPOSITION SYMBOL;So;0;ON;;;;;N;;;;; +2385;WHITE SQUARE WITH CENTRE VERTICAL LINE;So;0;ON;;;;;N;;;;; +2386;ENTER SYMBOL;So;0;ON;;;;;N;;;;; +2387;ALTERNATIVE KEY SYMBOL;So;0;ON;;;;;N;;;;; +2388;HELM SYMBOL;So;0;ON;;;;;N;;;;; +2389;CIRCLED HORIZONTAL BAR WITH NOTCH;So;0;ON;;;;;N;;pause;;; +238A;CIRCLED TRIANGLE DOWN;So;0;ON;;;;;N;;break;;; +238B;BROKEN CIRCLE WITH NORTHWEST ARROW;So;0;ON;;;;;N;;escape;;; +238C;UNDO SYMBOL;So;0;ON;;;;;N;;;;; +238D;MONOSTABLE SYMBOL;So;0;ON;;;;;N;;;;; +238E;HYSTERESIS SYMBOL;So;0;ON;;;;;N;;;;; +238F;OPEN-CIRCUIT-OUTPUT H-TYPE SYMBOL;So;0;ON;;;;;N;;;;; +2390;OPEN-CIRCUIT-OUTPUT L-TYPE SYMBOL;So;0;ON;;;;;N;;;;; +2391;PASSIVE-PULL-DOWN-OUTPUT SYMBOL;So;0;ON;;;;;N;;;;; +2392;PASSIVE-PULL-UP-OUTPUT SYMBOL;So;0;ON;;;;;N;;;;; +2393;DIRECT CURRENT SYMBOL FORM TWO;So;0;ON;;;;;N;;;;; +2394;SOFTWARE-FUNCTION SYMBOL;So;0;ON;;;;;N;;;;; +2395;APL FUNCTIONAL SYMBOL QUAD;So;0;L;;;;;N;;;;; +2396;DECIMAL SEPARATOR KEY SYMBOL;So;0;ON;;;;;N;;;;; +2397;PREVIOUS PAGE;So;0;ON;;;;;N;;;;; +2398;NEXT PAGE;So;0;ON;;;;;N;;;;; +2399;PRINT SCREEN SYMBOL;So;0;ON;;;;;N;;;;; +239A;CLEAR SCREEN SYMBOL;So;0;ON;;;;;N;;;;; +239B;LEFT PARENTHESIS UPPER HOOK;Sm;0;ON;;;;;N;;;;; +239C;LEFT PARENTHESIS EXTENSION;Sm;0;ON;;;;;N;;;;; +239D;LEFT PARENTHESIS LOWER HOOK;Sm;0;ON;;;;;N;;;;; +239E;RIGHT PARENTHESIS UPPER HOOK;Sm;0;ON;;;;;N;;;;; +239F;RIGHT PARENTHESIS EXTENSION;Sm;0;ON;;;;;N;;;;; +23A0;RIGHT PARENTHESIS LOWER HOOK;Sm;0;ON;;;;;N;;;;; +23A1;LEFT SQUARE BRACKET UPPER CORNER;Sm;0;ON;;;;;N;;;;; +23A2;LEFT SQUARE BRACKET EXTENSION;Sm;0;ON;;;;;N;;;;; +23A3;LEFT SQUARE BRACKET LOWER CORNER;Sm;0;ON;;;;;N;;;;; +23A4;RIGHT SQUARE BRACKET UPPER CORNER;Sm;0;ON;;;;;N;;;;; +23A5;RIGHT SQUARE BRACKET EXTENSION;Sm;0;ON;;;;;N;;;;; +23A6;RIGHT SQUARE BRACKET LOWER CORNER;Sm;0;ON;;;;;N;;;;; +23A7;LEFT CURLY BRACKET UPPER HOOK;Sm;0;ON;;;;;N;;;;; +23A8;LEFT CURLY BRACKET MIDDLE PIECE;Sm;0;ON;;;;;N;;;;; +23A9;LEFT CURLY BRACKET LOWER HOOK;Sm;0;ON;;;;;N;;;;; +23AA;CURLY BRACKET EXTENSION;Sm;0;ON;;;;;N;;;;; +23AB;RIGHT CURLY BRACKET UPPER HOOK;Sm;0;ON;;;;;N;;;;; +23AC;RIGHT CURLY BRACKET MIDDLE PIECE;Sm;0;ON;;;;;N;;;;; +23AD;RIGHT CURLY BRACKET LOWER HOOK;Sm;0;ON;;;;;N;;;;; +23AE;INTEGRAL EXTENSION;Sm;0;ON;;;;;N;;;;; +23AF;HORIZONTAL LINE EXTENSION;Sm;0;ON;;;;;N;;;;; +23B0;UPPER LEFT OR LOWER RIGHT CURLY BRACKET SECTION;Sm;0;ON;;;;;N;;;;; +23B1;UPPER RIGHT OR LOWER LEFT CURLY BRACKET SECTION;Sm;0;ON;;;;;N;;;;; +23B2;SUMMATION TOP;Sm;0;ON;;;;;N;;;;; +23B3;SUMMATION BOTTOM;Sm;0;ON;;;;;N;;;;; +23B4;TOP SQUARE BRACKET;Ps;0;ON;;;;;N;;;;; +23B5;BOTTOM SQUARE BRACKET;Pe;0;ON;;;;;N;;;;; +23B6;BOTTOM SQUARE BRACKET OVER TOP SQUARE BRACKET;Po;0;ON;;;;;N;;;;; +23B7;RADICAL SYMBOL BOTTOM;So;0;ON;;;;;N;;;;; +23B8;LEFT VERTICAL BOX LINE;So;0;ON;;;;;N;;;;; +23B9;RIGHT VERTICAL BOX LINE;So;0;ON;;;;;N;;;;; +23BA;HORIZONTAL SCAN LINE-1;So;0;ON;;;;;N;;;;; +23BB;HORIZONTAL SCAN LINE-3;So;0;ON;;;;;N;;;;; +23BC;HORIZONTAL SCAN LINE-7;So;0;ON;;;;;N;;;;; +23BD;HORIZONTAL SCAN LINE-9;So;0;ON;;;;;N;;;;; +23BE;DENTISTRY SYMBOL LIGHT VERTICAL AND TOP RIGHT;So;0;ON;;;;;N;;;;; +23BF;DENTISTRY SYMBOL LIGHT VERTICAL AND BOTTOM RIGHT;So;0;ON;;;;;N;;;;; +23C0;DENTISTRY SYMBOL LIGHT VERTICAL WITH CIRCLE;So;0;ON;;;;;N;;;;; +23C1;DENTISTRY SYMBOL LIGHT DOWN AND HORIZONTAL WITH CIRCLE;So;0;ON;;;;;N;;;;; +23C2;DENTISTRY SYMBOL LIGHT UP AND HORIZONTAL WITH CIRCLE;So;0;ON;;;;;N;;;;; +23C3;DENTISTRY SYMBOL LIGHT VERTICAL WITH TRIANGLE;So;0;ON;;;;;N;;;;; +23C4;DENTISTRY SYMBOL LIGHT DOWN AND HORIZONTAL WITH TRIANGLE;So;0;ON;;;;;N;;;;; +23C5;DENTISTRY SYMBOL LIGHT UP AND HORIZONTAL WITH TRIANGLE;So;0;ON;;;;;N;;;;; +23C6;DENTISTRY SYMBOL LIGHT VERTICAL AND WAVE;So;0;ON;;;;;N;;;;; +23C7;DENTISTRY SYMBOL LIGHT DOWN AND HORIZONTAL WITH WAVE;So;0;ON;;;;;N;;;;; +23C8;DENTISTRY SYMBOL LIGHT UP AND HORIZONTAL WITH WAVE;So;0;ON;;;;;N;;;;; +23C9;DENTISTRY SYMBOL LIGHT DOWN AND HORIZONTAL;So;0;ON;;;;;N;;;;; +23CA;DENTISTRY SYMBOL LIGHT UP AND HORIZONTAL;So;0;ON;;;;;N;;;;; +23CB;DENTISTRY SYMBOL LIGHT VERTICAL AND TOP LEFT;So;0;ON;;;;;N;;;;; +23CC;DENTISTRY SYMBOL LIGHT VERTICAL AND BOTTOM LEFT;So;0;ON;;;;;N;;;;; +23CD;SQUARE FOOT;So;0;ON;;;;;N;;;;; +23CE;RETURN SYMBOL;So;0;ON;;;;;N;;;;; +2400;SYMBOL FOR NULL;So;0;ON;;;;;N;GRAPHIC FOR NULL;;;; +2401;SYMBOL FOR START OF HEADING;So;0;ON;;;;;N;GRAPHIC FOR START OF HEADING;;;; +2402;SYMBOL FOR START OF TEXT;So;0;ON;;;;;N;GRAPHIC FOR START OF TEXT;;;; +2403;SYMBOL FOR END OF TEXT;So;0;ON;;;;;N;GRAPHIC FOR END OF TEXT;;;; +2404;SYMBOL FOR END OF TRANSMISSION;So;0;ON;;;;;N;GRAPHIC FOR END OF TRANSMISSION;;;; +2405;SYMBOL FOR ENQUIRY;So;0;ON;;;;;N;GRAPHIC FOR ENQUIRY;;;; +2406;SYMBOL FOR ACKNOWLEDGE;So;0;ON;;;;;N;GRAPHIC FOR ACKNOWLEDGE;;;; +2407;SYMBOL FOR BELL;So;0;ON;;;;;N;GRAPHIC FOR BELL;;;; +2408;SYMBOL FOR BACKSPACE;So;0;ON;;;;;N;GRAPHIC FOR BACKSPACE;;;; +2409;SYMBOL FOR HORIZONTAL TABULATION;So;0;ON;;;;;N;GRAPHIC FOR HORIZONTAL TABULATION;;;; +240A;SYMBOL FOR LINE FEED;So;0;ON;;;;;N;GRAPHIC FOR LINE FEED;;;; +240B;SYMBOL FOR VERTICAL TABULATION;So;0;ON;;;;;N;GRAPHIC FOR VERTICAL TABULATION;;;; +240C;SYMBOL FOR FORM FEED;So;0;ON;;;;;N;GRAPHIC FOR FORM FEED;;;; +240D;SYMBOL FOR CARRIAGE RETURN;So;0;ON;;;;;N;GRAPHIC FOR CARRIAGE RETURN;;;; +240E;SYMBOL FOR SHIFT OUT;So;0;ON;;;;;N;GRAPHIC FOR SHIFT OUT;;;; +240F;SYMBOL FOR SHIFT IN;So;0;ON;;;;;N;GRAPHIC FOR SHIFT IN;;;; +2410;SYMBOL FOR DATA LINK ESCAPE;So;0;ON;;;;;N;GRAPHIC FOR DATA LINK ESCAPE;;;; +2411;SYMBOL FOR DEVICE CONTROL ONE;So;0;ON;;;;;N;GRAPHIC FOR DEVICE CONTROL ONE;;;; +2412;SYMBOL FOR DEVICE CONTROL TWO;So;0;ON;;;;;N;GRAPHIC FOR DEVICE CONTROL TWO;;;; +2413;SYMBOL FOR DEVICE CONTROL THREE;So;0;ON;;;;;N;GRAPHIC FOR DEVICE CONTROL THREE;;;; +2414;SYMBOL FOR DEVICE CONTROL FOUR;So;0;ON;;;;;N;GRAPHIC FOR DEVICE CONTROL FOUR;;;; +2415;SYMBOL FOR NEGATIVE ACKNOWLEDGE;So;0;ON;;;;;N;GRAPHIC FOR NEGATIVE ACKNOWLEDGE;;;; +2416;SYMBOL FOR SYNCHRONOUS IDLE;So;0;ON;;;;;N;GRAPHIC FOR SYNCHRONOUS IDLE;;;; +2417;SYMBOL FOR END OF TRANSMISSION BLOCK;So;0;ON;;;;;N;GRAPHIC FOR END OF TRANSMISSION BLOCK;;;; +2418;SYMBOL FOR CANCEL;So;0;ON;;;;;N;GRAPHIC FOR CANCEL;;;; +2419;SYMBOL FOR END OF MEDIUM;So;0;ON;;;;;N;GRAPHIC FOR END OF MEDIUM;;;; +241A;SYMBOL FOR SUBSTITUTE;So;0;ON;;;;;N;GRAPHIC FOR SUBSTITUTE;;;; +241B;SYMBOL FOR ESCAPE;So;0;ON;;;;;N;GRAPHIC FOR ESCAPE;;;; +241C;SYMBOL FOR FILE SEPARATOR;So;0;ON;;;;;N;GRAPHIC FOR FILE SEPARATOR;;;; +241D;SYMBOL FOR GROUP SEPARATOR;So;0;ON;;;;;N;GRAPHIC FOR GROUP SEPARATOR;;;; +241E;SYMBOL FOR RECORD SEPARATOR;So;0;ON;;;;;N;GRAPHIC FOR RECORD SEPARATOR;;;; +241F;SYMBOL FOR UNIT SEPARATOR;So;0;ON;;;;;N;GRAPHIC FOR UNIT SEPARATOR;;;; +2420;SYMBOL FOR SPACE;So;0;ON;;;;;N;GRAPHIC FOR SPACE;;;; +2421;SYMBOL FOR DELETE;So;0;ON;;;;;N;GRAPHIC FOR DELETE;;;; +2422;BLANK SYMBOL;So;0;ON;;;;;N;BLANK;;;; +2423;OPEN BOX;So;0;ON;;;;;N;;;;; +2424;SYMBOL FOR NEWLINE;So;0;ON;;;;;N;GRAPHIC FOR NEWLINE;;;; +2425;SYMBOL FOR DELETE FORM TWO;So;0;ON;;;;;N;;;;; +2426;SYMBOL FOR SUBSTITUTE FORM TWO;So;0;ON;;;;;N;;;;; +2440;OCR HOOK;So;0;ON;;;;;N;;;;; +2441;OCR CHAIR;So;0;ON;;;;;N;;;;; +2442;OCR FORK;So;0;ON;;;;;N;;;;; +2443;OCR INVERTED FORK;So;0;ON;;;;;N;;;;; +2444;OCR BELT BUCKLE;So;0;ON;;;;;N;;;;; +2445;OCR BOW TIE;So;0;ON;;;;;N;;;;; +2446;OCR BRANCH BANK IDENTIFICATION;So;0;ON;;;;;N;;;;; +2447;OCR AMOUNT OF CHECK;So;0;ON;;;;;N;;;;; +2448;OCR DASH;So;0;ON;;;;;N;;;;; +2449;OCR CUSTOMER ACCOUNT NUMBER;So;0;ON;;;;;N;;;;; +244A;OCR DOUBLE BACKSLASH;So;0;ON;;;;;N;;;;; +2460;CIRCLED DIGIT ONE;No;0;EN;<circle> 0031;;1;1;N;;;;; +2461;CIRCLED DIGIT TWO;No;0;EN;<circle> 0032;;2;2;N;;;;; +2462;CIRCLED DIGIT THREE;No;0;EN;<circle> 0033;;3;3;N;;;;; +2463;CIRCLED DIGIT FOUR;No;0;EN;<circle> 0034;;4;4;N;;;;; +2464;CIRCLED DIGIT FIVE;No;0;EN;<circle> 0035;;5;5;N;;;;; +2465;CIRCLED DIGIT SIX;No;0;EN;<circle> 0036;;6;6;N;;;;; +2466;CIRCLED DIGIT SEVEN;No;0;EN;<circle> 0037;;7;7;N;;;;; +2467;CIRCLED DIGIT EIGHT;No;0;EN;<circle> 0038;;8;8;N;;;;; +2468;CIRCLED DIGIT NINE;No;0;EN;<circle> 0039;;9;9;N;;;;; +2469;CIRCLED NUMBER TEN;No;0;EN;<circle> 0031 0030;;;10;N;;;;; +246A;CIRCLED NUMBER ELEVEN;No;0;EN;<circle> 0031 0031;;;11;N;;;;; +246B;CIRCLED NUMBER TWELVE;No;0;EN;<circle> 0031 0032;;;12;N;;;;; +246C;CIRCLED NUMBER THIRTEEN;No;0;EN;<circle> 0031 0033;;;13;N;;;;; +246D;CIRCLED NUMBER FOURTEEN;No;0;EN;<circle> 0031 0034;;;14;N;;;;; +246E;CIRCLED NUMBER FIFTEEN;No;0;EN;<circle> 0031 0035;;;15;N;;;;; +246F;CIRCLED NUMBER SIXTEEN;No;0;EN;<circle> 0031 0036;;;16;N;;;;; +2470;CIRCLED NUMBER SEVENTEEN;No;0;EN;<circle> 0031 0037;;;17;N;;;;; +2471;CIRCLED NUMBER EIGHTEEN;No;0;EN;<circle> 0031 0038;;;18;N;;;;; +2472;CIRCLED NUMBER NINETEEN;No;0;EN;<circle> 0031 0039;;;19;N;;;;; +2473;CIRCLED NUMBER TWENTY;No;0;EN;<circle> 0032 0030;;;20;N;;;;; +2474;PARENTHESIZED DIGIT ONE;No;0;EN;<compat> 0028 0031 0029;;1;1;N;;;;; +2475;PARENTHESIZED DIGIT TWO;No;0;EN;<compat> 0028 0032 0029;;2;2;N;;;;; +2476;PARENTHESIZED DIGIT THREE;No;0;EN;<compat> 0028 0033 0029;;3;3;N;;;;; +2477;PARENTHESIZED DIGIT FOUR;No;0;EN;<compat> 0028 0034 0029;;4;4;N;;;;; +2478;PARENTHESIZED DIGIT FIVE;No;0;EN;<compat> 0028 0035 0029;;5;5;N;;;;; +2479;PARENTHESIZED DIGIT SIX;No;0;EN;<compat> 0028 0036 0029;;6;6;N;;;;; +247A;PARENTHESIZED DIGIT SEVEN;No;0;EN;<compat> 0028 0037 0029;;7;7;N;;;;; +247B;PARENTHESIZED DIGIT EIGHT;No;0;EN;<compat> 0028 0038 0029;;8;8;N;;;;; +247C;PARENTHESIZED DIGIT NINE;No;0;EN;<compat> 0028 0039 0029;;9;9;N;;;;; +247D;PARENTHESIZED NUMBER TEN;No;0;EN;<compat> 0028 0031 0030 0029;;;10;N;;;;; +247E;PARENTHESIZED NUMBER ELEVEN;No;0;EN;<compat> 0028 0031 0031 0029;;;11;N;;;;; +247F;PARENTHESIZED NUMBER TWELVE;No;0;EN;<compat> 0028 0031 0032 0029;;;12;N;;;;; +2480;PARENTHESIZED NUMBER THIRTEEN;No;0;EN;<compat> 0028 0031 0033 0029;;;13;N;;;;; +2481;PARENTHESIZED NUMBER FOURTEEN;No;0;EN;<compat> 0028 0031 0034 0029;;;14;N;;;;; +2482;PARENTHESIZED NUMBER FIFTEEN;No;0;EN;<compat> 0028 0031 0035 0029;;;15;N;;;;; +2483;PARENTHESIZED NUMBER SIXTEEN;No;0;EN;<compat> 0028 0031 0036 0029;;;16;N;;;;; +2484;PARENTHESIZED NUMBER SEVENTEEN;No;0;EN;<compat> 0028 0031 0037 0029;;;17;N;;;;; +2485;PARENTHESIZED NUMBER EIGHTEEN;No;0;EN;<compat> 0028 0031 0038 0029;;;18;N;;;;; +2486;PARENTHESIZED NUMBER NINETEEN;No;0;EN;<compat> 0028 0031 0039 0029;;;19;N;;;;; +2487;PARENTHESIZED NUMBER TWENTY;No;0;EN;<compat> 0028 0032 0030 0029;;;20;N;;;;; +2488;DIGIT ONE FULL STOP;No;0;EN;<compat> 0031 002E;;1;1;N;DIGIT ONE PERIOD;;;; +2489;DIGIT TWO FULL STOP;No;0;EN;<compat> 0032 002E;;2;2;N;DIGIT TWO PERIOD;;;; +248A;DIGIT THREE FULL STOP;No;0;EN;<compat> 0033 002E;;3;3;N;DIGIT THREE PERIOD;;;; +248B;DIGIT FOUR FULL STOP;No;0;EN;<compat> 0034 002E;;4;4;N;DIGIT FOUR PERIOD;;;; +248C;DIGIT FIVE FULL STOP;No;0;EN;<compat> 0035 002E;;5;5;N;DIGIT FIVE PERIOD;;;; +248D;DIGIT SIX FULL STOP;No;0;EN;<compat> 0036 002E;;6;6;N;DIGIT SIX PERIOD;;;; +248E;DIGIT SEVEN FULL STOP;No;0;EN;<compat> 0037 002E;;7;7;N;DIGIT SEVEN PERIOD;;;; +248F;DIGIT EIGHT FULL STOP;No;0;EN;<compat> 0038 002E;;8;8;N;DIGIT EIGHT PERIOD;;;; +2490;DIGIT NINE FULL STOP;No;0;EN;<compat> 0039 002E;;9;9;N;DIGIT NINE PERIOD;;;; +2491;NUMBER TEN FULL STOP;No;0;EN;<compat> 0031 0030 002E;;;10;N;NUMBER TEN PERIOD;;;; +2492;NUMBER ELEVEN FULL STOP;No;0;EN;<compat> 0031 0031 002E;;;11;N;NUMBER ELEVEN PERIOD;;;; +2493;NUMBER TWELVE FULL STOP;No;0;EN;<compat> 0031 0032 002E;;;12;N;NUMBER TWELVE PERIOD;;;; +2494;NUMBER THIRTEEN FULL STOP;No;0;EN;<compat> 0031 0033 002E;;;13;N;NUMBER THIRTEEN PERIOD;;;; +2495;NUMBER FOURTEEN FULL STOP;No;0;EN;<compat> 0031 0034 002E;;;14;N;NUMBER FOURTEEN PERIOD;;;; +2496;NUMBER FIFTEEN FULL STOP;No;0;EN;<compat> 0031 0035 002E;;;15;N;NUMBER FIFTEEN PERIOD;;;; +2497;NUMBER SIXTEEN FULL STOP;No;0;EN;<compat> 0031 0036 002E;;;16;N;NUMBER SIXTEEN PERIOD;;;; +2498;NUMBER SEVENTEEN FULL STOP;No;0;EN;<compat> 0031 0037 002E;;;17;N;NUMBER SEVENTEEN PERIOD;;;; +2499;NUMBER EIGHTEEN FULL STOP;No;0;EN;<compat> 0031 0038 002E;;;18;N;NUMBER EIGHTEEN PERIOD;;;; +249A;NUMBER NINETEEN FULL STOP;No;0;EN;<compat> 0031 0039 002E;;;19;N;NUMBER NINETEEN PERIOD;;;; +249B;NUMBER TWENTY FULL STOP;No;0;EN;<compat> 0032 0030 002E;;;20;N;NUMBER TWENTY PERIOD;;;; +249C;PARENTHESIZED LATIN SMALL LETTER A;So;0;L;<compat> 0028 0061 0029;;;;N;;;;; +249D;PARENTHESIZED LATIN SMALL LETTER B;So;0;L;<compat> 0028 0062 0029;;;;N;;;;; +249E;PARENTHESIZED LATIN SMALL LETTER C;So;0;L;<compat> 0028 0063 0029;;;;N;;;;; +249F;PARENTHESIZED LATIN SMALL LETTER D;So;0;L;<compat> 0028 0064 0029;;;;N;;;;; +24A0;PARENTHESIZED LATIN SMALL LETTER E;So;0;L;<compat> 0028 0065 0029;;;;N;;;;; +24A1;PARENTHESIZED LATIN SMALL LETTER F;So;0;L;<compat> 0028 0066 0029;;;;N;;;;; +24A2;PARENTHESIZED LATIN SMALL LETTER G;So;0;L;<compat> 0028 0067 0029;;;;N;;;;; +24A3;PARENTHESIZED LATIN SMALL LETTER H;So;0;L;<compat> 0028 0068 0029;;;;N;;;;; +24A4;PARENTHESIZED LATIN SMALL LETTER I;So;0;L;<compat> 0028 0069 0029;;;;N;;;;; +24A5;PARENTHESIZED LATIN SMALL LETTER J;So;0;L;<compat> 0028 006A 0029;;;;N;;;;; +24A6;PARENTHESIZED LATIN SMALL LETTER K;So;0;L;<compat> 0028 006B 0029;;;;N;;;;; +24A7;PARENTHESIZED LATIN SMALL LETTER L;So;0;L;<compat> 0028 006C 0029;;;;N;;;;; +24A8;PARENTHESIZED LATIN SMALL LETTER M;So;0;L;<compat> 0028 006D 0029;;;;N;;;;; +24A9;PARENTHESIZED LATIN SMALL LETTER N;So;0;L;<compat> 0028 006E 0029;;;;N;;;;; +24AA;PARENTHESIZED LATIN SMALL LETTER O;So;0;L;<compat> 0028 006F 0029;;;;N;;;;; +24AB;PARENTHESIZED LATIN SMALL LETTER P;So;0;L;<compat> 0028 0070 0029;;;;N;;;;; +24AC;PARENTHESIZED LATIN SMALL LETTER Q;So;0;L;<compat> 0028 0071 0029;;;;N;;;;; +24AD;PARENTHESIZED LATIN SMALL LETTER R;So;0;L;<compat> 0028 0072 0029;;;;N;;;;; +24AE;PARENTHESIZED LATIN SMALL LETTER S;So;0;L;<compat> 0028 0073 0029;;;;N;;;;; +24AF;PARENTHESIZED LATIN SMALL LETTER T;So;0;L;<compat> 0028 0074 0029;;;;N;;;;; +24B0;PARENTHESIZED LATIN SMALL LETTER U;So;0;L;<compat> 0028 0075 0029;;;;N;;;;; +24B1;PARENTHESIZED LATIN SMALL LETTER V;So;0;L;<compat> 0028 0076 0029;;;;N;;;;; +24B2;PARENTHESIZED LATIN SMALL LETTER W;So;0;L;<compat> 0028 0077 0029;;;;N;;;;; +24B3;PARENTHESIZED LATIN SMALL LETTER X;So;0;L;<compat> 0028 0078 0029;;;;N;;;;; +24B4;PARENTHESIZED LATIN SMALL LETTER Y;So;0;L;<compat> 0028 0079 0029;;;;N;;;;; +24B5;PARENTHESIZED LATIN SMALL LETTER Z;So;0;L;<compat> 0028 007A 0029;;;;N;;;;; +24B6;CIRCLED LATIN CAPITAL LETTER A;So;0;L;<circle> 0041;;;;N;;;;24D0; +24B7;CIRCLED LATIN CAPITAL LETTER B;So;0;L;<circle> 0042;;;;N;;;;24D1; +24B8;CIRCLED LATIN CAPITAL LETTER C;So;0;L;<circle> 0043;;;;N;;;;24D2; +24B9;CIRCLED LATIN CAPITAL LETTER D;So;0;L;<circle> 0044;;;;N;;;;24D3; +24BA;CIRCLED LATIN CAPITAL LETTER E;So;0;L;<circle> 0045;;;;N;;;;24D4; +24BB;CIRCLED LATIN CAPITAL LETTER F;So;0;L;<circle> 0046;;;;N;;;;24D5; +24BC;CIRCLED LATIN CAPITAL LETTER G;So;0;L;<circle> 0047;;;;N;;;;24D6; +24BD;CIRCLED LATIN CAPITAL LETTER H;So;0;L;<circle> 0048;;;;N;;;;24D7; +24BE;CIRCLED LATIN CAPITAL LETTER I;So;0;L;<circle> 0049;;;;N;;;;24D8; +24BF;CIRCLED LATIN CAPITAL LETTER J;So;0;L;<circle> 004A;;;;N;;;;24D9; +24C0;CIRCLED LATIN CAPITAL LETTER K;So;0;L;<circle> 004B;;;;N;;;;24DA; +24C1;CIRCLED LATIN CAPITAL LETTER L;So;0;L;<circle> 004C;;;;N;;;;24DB; +24C2;CIRCLED LATIN CAPITAL LETTER M;So;0;L;<circle> 004D;;;;N;;;;24DC; +24C3;CIRCLED LATIN CAPITAL LETTER N;So;0;L;<circle> 004E;;;;N;;;;24DD; +24C4;CIRCLED LATIN CAPITAL LETTER O;So;0;L;<circle> 004F;;;;N;;;;24DE; +24C5;CIRCLED LATIN CAPITAL LETTER P;So;0;L;<circle> 0050;;;;N;;;;24DF; +24C6;CIRCLED LATIN CAPITAL LETTER Q;So;0;L;<circle> 0051;;;;N;;;;24E0; +24C7;CIRCLED LATIN CAPITAL LETTER R;So;0;L;<circle> 0052;;;;N;;;;24E1; +24C8;CIRCLED LATIN CAPITAL LETTER S;So;0;L;<circle> 0053;;;;N;;;;24E2; +24C9;CIRCLED LATIN CAPITAL LETTER T;So;0;L;<circle> 0054;;;;N;;;;24E3; +24CA;CIRCLED LATIN CAPITAL LETTER U;So;0;L;<circle> 0055;;;;N;;;;24E4; +24CB;CIRCLED LATIN CAPITAL LETTER V;So;0;L;<circle> 0056;;;;N;;;;24E5; +24CC;CIRCLED LATIN CAPITAL LETTER W;So;0;L;<circle> 0057;;;;N;;;;24E6; +24CD;CIRCLED LATIN CAPITAL LETTER X;So;0;L;<circle> 0058;;;;N;;;;24E7; +24CE;CIRCLED LATIN CAPITAL LETTER Y;So;0;L;<circle> 0059;;;;N;;;;24E8; +24CF;CIRCLED LATIN CAPITAL LETTER Z;So;0;L;<circle> 005A;;;;N;;;;24E9; +24D0;CIRCLED LATIN SMALL LETTER A;So;0;L;<circle> 0061;;;;N;;;24B6;;24B6 +24D1;CIRCLED LATIN SMALL LETTER B;So;0;L;<circle> 0062;;;;N;;;24B7;;24B7 +24D2;CIRCLED LATIN SMALL LETTER C;So;0;L;<circle> 0063;;;;N;;;24B8;;24B8 +24D3;CIRCLED LATIN SMALL LETTER D;So;0;L;<circle> 0064;;;;N;;;24B9;;24B9 +24D4;CIRCLED LATIN SMALL LETTER E;So;0;L;<circle> 0065;;;;N;;;24BA;;24BA +24D5;CIRCLED LATIN SMALL LETTER F;So;0;L;<circle> 0066;;;;N;;;24BB;;24BB +24D6;CIRCLED LATIN SMALL LETTER G;So;0;L;<circle> 0067;;;;N;;;24BC;;24BC +24D7;CIRCLED LATIN SMALL LETTER H;So;0;L;<circle> 0068;;;;N;;;24BD;;24BD +24D8;CIRCLED LATIN SMALL LETTER I;So;0;L;<circle> 0069;;;;N;;;24BE;;24BE +24D9;CIRCLED LATIN SMALL LETTER J;So;0;L;<circle> 006A;;;;N;;;24BF;;24BF +24DA;CIRCLED LATIN SMALL LETTER K;So;0;L;<circle> 006B;;;;N;;;24C0;;24C0 +24DB;CIRCLED LATIN SMALL LETTER L;So;0;L;<circle> 006C;;;;N;;;24C1;;24C1 +24DC;CIRCLED LATIN SMALL LETTER M;So;0;L;<circle> 006D;;;;N;;;24C2;;24C2 +24DD;CIRCLED LATIN SMALL LETTER N;So;0;L;<circle> 006E;;;;N;;;24C3;;24C3 +24DE;CIRCLED LATIN SMALL LETTER O;So;0;L;<circle> 006F;;;;N;;;24C4;;24C4 +24DF;CIRCLED LATIN SMALL LETTER P;So;0;L;<circle> 0070;;;;N;;;24C5;;24C5 +24E0;CIRCLED LATIN SMALL LETTER Q;So;0;L;<circle> 0071;;;;N;;;24C6;;24C6 +24E1;CIRCLED LATIN SMALL LETTER R;So;0;L;<circle> 0072;;;;N;;;24C7;;24C7 +24E2;CIRCLED LATIN SMALL LETTER S;So;0;L;<circle> 0073;;;;N;;;24C8;;24C8 +24E3;CIRCLED LATIN SMALL LETTER T;So;0;L;<circle> 0074;;;;N;;;24C9;;24C9 +24E4;CIRCLED LATIN SMALL LETTER U;So;0;L;<circle> 0075;;;;N;;;24CA;;24CA +24E5;CIRCLED LATIN SMALL LETTER V;So;0;L;<circle> 0076;;;;N;;;24CB;;24CB +24E6;CIRCLED LATIN SMALL LETTER W;So;0;L;<circle> 0077;;;;N;;;24CC;;24CC +24E7;CIRCLED LATIN SMALL LETTER X;So;0;L;<circle> 0078;;;;N;;;24CD;;24CD +24E8;CIRCLED LATIN SMALL LETTER Y;So;0;L;<circle> 0079;;;;N;;;24CE;;24CE +24E9;CIRCLED LATIN SMALL LETTER Z;So;0;L;<circle> 007A;;;;N;;;24CF;;24CF +24EA;CIRCLED DIGIT ZERO;No;0;EN;<circle> 0030;;0;0;N;;;;; +24EB;NEGATIVE CIRCLED NUMBER ELEVEN;No;0;ON;;;;11;N;;;;; +24EC;NEGATIVE CIRCLED NUMBER TWELVE;No;0;ON;;;;12;N;;;;; +24ED;NEGATIVE CIRCLED NUMBER THIRTEEN;No;0;ON;;;;13;N;;;;; +24EE;NEGATIVE CIRCLED NUMBER FOURTEEN;No;0;ON;;;;14;N;;;;; +24EF;NEGATIVE CIRCLED NUMBER FIFTEEN;No;0;ON;;;;15;N;;;;; +24F0;NEGATIVE CIRCLED NUMBER SIXTEEN;No;0;ON;;;;16;N;;;;; +24F1;NEGATIVE CIRCLED NUMBER SEVENTEEN;No;0;ON;;;;17;N;;;;; +24F2;NEGATIVE CIRCLED NUMBER EIGHTEEN;No;0;ON;;;;18;N;;;;; +24F3;NEGATIVE CIRCLED NUMBER NINETEEN;No;0;ON;;;;19;N;;;;; +24F4;NEGATIVE CIRCLED NUMBER TWENTY;No;0;ON;;;;20;N;;;;; +24F5;DOUBLE CIRCLED DIGIT ONE;No;0;ON;;;1;1;N;;;;; +24F6;DOUBLE CIRCLED DIGIT TWO;No;0;ON;;;2;2;N;;;;; +24F7;DOUBLE CIRCLED DIGIT THREE;No;0;ON;;;3;3;N;;;;; +24F8;DOUBLE CIRCLED DIGIT FOUR;No;0;ON;;;4;4;N;;;;; +24F9;DOUBLE CIRCLED DIGIT FIVE;No;0;ON;;;5;5;N;;;;; +24FA;DOUBLE CIRCLED DIGIT SIX;No;0;ON;;;6;6;N;;;;; +24FB;DOUBLE CIRCLED DIGIT SEVEN;No;0;ON;;;7;7;N;;;;; +24FC;DOUBLE CIRCLED DIGIT EIGHT;No;0;ON;;;8;8;N;;;;; +24FD;DOUBLE CIRCLED DIGIT NINE;No;0;ON;;;9;9;N;;;;; +24FE;DOUBLE CIRCLED NUMBER TEN;No;0;ON;;;;10;N;;;;; +2500;BOX DRAWINGS LIGHT HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT HORIZONTAL;;;; +2501;BOX DRAWINGS HEAVY HORIZONTAL;So;0;ON;;;;;N;FORMS HEAVY HORIZONTAL;;;; +2502;BOX DRAWINGS LIGHT VERTICAL;So;0;ON;;;;;N;FORMS LIGHT VERTICAL;;;; +2503;BOX DRAWINGS HEAVY VERTICAL;So;0;ON;;;;;N;FORMS HEAVY VERTICAL;;;; +2504;BOX DRAWINGS LIGHT TRIPLE DASH HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT TRIPLE DASH HORIZONTAL;;;; +2505;BOX DRAWINGS HEAVY TRIPLE DASH HORIZONTAL;So;0;ON;;;;;N;FORMS HEAVY TRIPLE DASH HORIZONTAL;;;; +2506;BOX DRAWINGS LIGHT TRIPLE DASH VERTICAL;So;0;ON;;;;;N;FORMS LIGHT TRIPLE DASH VERTICAL;;;; +2507;BOX DRAWINGS HEAVY TRIPLE DASH VERTICAL;So;0;ON;;;;;N;FORMS HEAVY TRIPLE DASH VERTICAL;;;; +2508;BOX DRAWINGS LIGHT QUADRUPLE DASH HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT QUADRUPLE DASH HORIZONTAL;;;; +2509;BOX DRAWINGS HEAVY QUADRUPLE DASH HORIZONTAL;So;0;ON;;;;;N;FORMS HEAVY QUADRUPLE DASH HORIZONTAL;;;; +250A;BOX DRAWINGS LIGHT QUADRUPLE DASH VERTICAL;So;0;ON;;;;;N;FORMS LIGHT QUADRUPLE DASH VERTICAL;;;; +250B;BOX DRAWINGS HEAVY QUADRUPLE DASH VERTICAL;So;0;ON;;;;;N;FORMS HEAVY QUADRUPLE DASH VERTICAL;;;; +250C;BOX DRAWINGS LIGHT DOWN AND RIGHT;So;0;ON;;;;;N;FORMS LIGHT DOWN AND RIGHT;;;; +250D;BOX DRAWINGS DOWN LIGHT AND RIGHT HEAVY;So;0;ON;;;;;N;FORMS DOWN LIGHT AND RIGHT HEAVY;;;; +250E;BOX DRAWINGS DOWN HEAVY AND RIGHT LIGHT;So;0;ON;;;;;N;FORMS DOWN HEAVY AND RIGHT LIGHT;;;; +250F;BOX DRAWINGS HEAVY DOWN AND RIGHT;So;0;ON;;;;;N;FORMS HEAVY DOWN AND RIGHT;;;; +2510;BOX DRAWINGS LIGHT DOWN AND LEFT;So;0;ON;;;;;N;FORMS LIGHT DOWN AND LEFT;;;; +2511;BOX DRAWINGS DOWN LIGHT AND LEFT HEAVY;So;0;ON;;;;;N;FORMS DOWN LIGHT AND LEFT HEAVY;;;; +2512;BOX DRAWINGS DOWN HEAVY AND LEFT LIGHT;So;0;ON;;;;;N;FORMS DOWN HEAVY AND LEFT LIGHT;;;; +2513;BOX DRAWINGS HEAVY DOWN AND LEFT;So;0;ON;;;;;N;FORMS HEAVY DOWN AND LEFT;;;; +2514;BOX DRAWINGS LIGHT UP AND RIGHT;So;0;ON;;;;;N;FORMS LIGHT UP AND RIGHT;;;; +2515;BOX DRAWINGS UP LIGHT AND RIGHT HEAVY;So;0;ON;;;;;N;FORMS UP LIGHT AND RIGHT HEAVY;;;; +2516;BOX DRAWINGS UP HEAVY AND RIGHT LIGHT;So;0;ON;;;;;N;FORMS UP HEAVY AND RIGHT LIGHT;;;; +2517;BOX DRAWINGS HEAVY UP AND RIGHT;So;0;ON;;;;;N;FORMS HEAVY UP AND RIGHT;;;; +2518;BOX DRAWINGS LIGHT UP AND LEFT;So;0;ON;;;;;N;FORMS LIGHT UP AND LEFT;;;; +2519;BOX DRAWINGS UP LIGHT AND LEFT HEAVY;So;0;ON;;;;;N;FORMS UP LIGHT AND LEFT HEAVY;;;; +251A;BOX DRAWINGS UP HEAVY AND LEFT LIGHT;So;0;ON;;;;;N;FORMS UP HEAVY AND LEFT LIGHT;;;; +251B;BOX DRAWINGS HEAVY UP AND LEFT;So;0;ON;;;;;N;FORMS HEAVY UP AND LEFT;;;; +251C;BOX DRAWINGS LIGHT VERTICAL AND RIGHT;So;0;ON;;;;;N;FORMS LIGHT VERTICAL AND RIGHT;;;; +251D;BOX DRAWINGS VERTICAL LIGHT AND RIGHT HEAVY;So;0;ON;;;;;N;FORMS VERTICAL LIGHT AND RIGHT HEAVY;;;; +251E;BOX DRAWINGS UP HEAVY AND RIGHT DOWN LIGHT;So;0;ON;;;;;N;FORMS UP HEAVY AND RIGHT DOWN LIGHT;;;; +251F;BOX DRAWINGS DOWN HEAVY AND RIGHT UP LIGHT;So;0;ON;;;;;N;FORMS DOWN HEAVY AND RIGHT UP LIGHT;;;; +2520;BOX DRAWINGS VERTICAL HEAVY AND RIGHT LIGHT;So;0;ON;;;;;N;FORMS VERTICAL HEAVY AND RIGHT LIGHT;;;; +2521;BOX DRAWINGS DOWN LIGHT AND RIGHT UP HEAVY;So;0;ON;;;;;N;FORMS DOWN LIGHT AND RIGHT UP HEAVY;;;; +2522;BOX DRAWINGS UP LIGHT AND RIGHT DOWN HEAVY;So;0;ON;;;;;N;FORMS UP LIGHT AND RIGHT DOWN HEAVY;;;; +2523;BOX DRAWINGS HEAVY VERTICAL AND RIGHT;So;0;ON;;;;;N;FORMS HEAVY VERTICAL AND RIGHT;;;; +2524;BOX DRAWINGS LIGHT VERTICAL AND LEFT;So;0;ON;;;;;N;FORMS LIGHT VERTICAL AND LEFT;;;; +2525;BOX DRAWINGS VERTICAL LIGHT AND LEFT HEAVY;So;0;ON;;;;;N;FORMS VERTICAL LIGHT AND LEFT HEAVY;;;; +2526;BOX DRAWINGS UP HEAVY AND LEFT DOWN LIGHT;So;0;ON;;;;;N;FORMS UP HEAVY AND LEFT DOWN LIGHT;;;; +2527;BOX DRAWINGS DOWN HEAVY AND LEFT UP LIGHT;So;0;ON;;;;;N;FORMS DOWN HEAVY AND LEFT UP LIGHT;;;; +2528;BOX DRAWINGS VERTICAL HEAVY AND LEFT LIGHT;So;0;ON;;;;;N;FORMS VERTICAL HEAVY AND LEFT LIGHT;;;; +2529;BOX DRAWINGS DOWN LIGHT AND LEFT UP HEAVY;So;0;ON;;;;;N;FORMS DOWN LIGHT AND LEFT UP HEAVY;;;; +252A;BOX DRAWINGS UP LIGHT AND LEFT DOWN HEAVY;So;0;ON;;;;;N;FORMS UP LIGHT AND LEFT DOWN HEAVY;;;; +252B;BOX DRAWINGS HEAVY VERTICAL AND LEFT;So;0;ON;;;;;N;FORMS HEAVY VERTICAL AND LEFT;;;; +252C;BOX DRAWINGS LIGHT DOWN AND HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT DOWN AND HORIZONTAL;;;; +252D;BOX DRAWINGS LEFT HEAVY AND RIGHT DOWN LIGHT;So;0;ON;;;;;N;FORMS LEFT HEAVY AND RIGHT DOWN LIGHT;;;; +252E;BOX DRAWINGS RIGHT HEAVY AND LEFT DOWN LIGHT;So;0;ON;;;;;N;FORMS RIGHT HEAVY AND LEFT DOWN LIGHT;;;; +252F;BOX DRAWINGS DOWN LIGHT AND HORIZONTAL HEAVY;So;0;ON;;;;;N;FORMS DOWN LIGHT AND HORIZONTAL HEAVY;;;; +2530;BOX DRAWINGS DOWN HEAVY AND HORIZONTAL LIGHT;So;0;ON;;;;;N;FORMS DOWN HEAVY AND HORIZONTAL LIGHT;;;; +2531;BOX DRAWINGS RIGHT LIGHT AND LEFT DOWN HEAVY;So;0;ON;;;;;N;FORMS RIGHT LIGHT AND LEFT DOWN HEAVY;;;; +2532;BOX DRAWINGS LEFT LIGHT AND RIGHT DOWN HEAVY;So;0;ON;;;;;N;FORMS LEFT LIGHT AND RIGHT DOWN HEAVY;;;; +2533;BOX DRAWINGS HEAVY DOWN AND HORIZONTAL;So;0;ON;;;;;N;FORMS HEAVY DOWN AND HORIZONTAL;;;; +2534;BOX DRAWINGS LIGHT UP AND HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT UP AND HORIZONTAL;;;; +2535;BOX DRAWINGS LEFT HEAVY AND RIGHT UP LIGHT;So;0;ON;;;;;N;FORMS LEFT HEAVY AND RIGHT UP LIGHT;;;; +2536;BOX DRAWINGS RIGHT HEAVY AND LEFT UP LIGHT;So;0;ON;;;;;N;FORMS RIGHT HEAVY AND LEFT UP LIGHT;;;; +2537;BOX DRAWINGS UP LIGHT AND HORIZONTAL HEAVY;So;0;ON;;;;;N;FORMS UP LIGHT AND HORIZONTAL HEAVY;;;; +2538;BOX DRAWINGS UP HEAVY AND HORIZONTAL LIGHT;So;0;ON;;;;;N;FORMS UP HEAVY AND HORIZONTAL LIGHT;;;; +2539;BOX DRAWINGS RIGHT LIGHT AND LEFT UP HEAVY;So;0;ON;;;;;N;FORMS RIGHT LIGHT AND LEFT UP HEAVY;;;; +253A;BOX DRAWINGS LEFT LIGHT AND RIGHT UP HEAVY;So;0;ON;;;;;N;FORMS LEFT LIGHT AND RIGHT UP HEAVY;;;; +253B;BOX DRAWINGS HEAVY UP AND HORIZONTAL;So;0;ON;;;;;N;FORMS HEAVY UP AND HORIZONTAL;;;; +253C;BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT VERTICAL AND HORIZONTAL;;;; +253D;BOX DRAWINGS LEFT HEAVY AND RIGHT VERTICAL LIGHT;So;0;ON;;;;;N;FORMS LEFT HEAVY AND RIGHT VERTICAL LIGHT;;;; +253E;BOX DRAWINGS RIGHT HEAVY AND LEFT VERTICAL LIGHT;So;0;ON;;;;;N;FORMS RIGHT HEAVY AND LEFT VERTICAL LIGHT;;;; +253F;BOX DRAWINGS VERTICAL LIGHT AND HORIZONTAL HEAVY;So;0;ON;;;;;N;FORMS VERTICAL LIGHT AND HORIZONTAL HEAVY;;;; +2540;BOX DRAWINGS UP HEAVY AND DOWN HORIZONTAL LIGHT;So;0;ON;;;;;N;FORMS UP HEAVY AND DOWN HORIZONTAL LIGHT;;;; +2541;BOX DRAWINGS DOWN HEAVY AND UP HORIZONTAL LIGHT;So;0;ON;;;;;N;FORMS DOWN HEAVY AND UP HORIZONTAL LIGHT;;;; +2542;BOX DRAWINGS VERTICAL HEAVY AND HORIZONTAL LIGHT;So;0;ON;;;;;N;FORMS VERTICAL HEAVY AND HORIZONTAL LIGHT;;;; +2543;BOX DRAWINGS LEFT UP HEAVY AND RIGHT DOWN LIGHT;So;0;ON;;;;;N;FORMS LEFT UP HEAVY AND RIGHT DOWN LIGHT;;;; +2544;BOX DRAWINGS RIGHT UP HEAVY AND LEFT DOWN LIGHT;So;0;ON;;;;;N;FORMS RIGHT UP HEAVY AND LEFT DOWN LIGHT;;;; +2545;BOX DRAWINGS LEFT DOWN HEAVY AND RIGHT UP LIGHT;So;0;ON;;;;;N;FORMS LEFT DOWN HEAVY AND RIGHT UP LIGHT;;;; +2546;BOX DRAWINGS RIGHT DOWN HEAVY AND LEFT UP LIGHT;So;0;ON;;;;;N;FORMS RIGHT DOWN HEAVY AND LEFT UP LIGHT;;;; +2547;BOX DRAWINGS DOWN LIGHT AND UP HORIZONTAL HEAVY;So;0;ON;;;;;N;FORMS DOWN LIGHT AND UP HORIZONTAL HEAVY;;;; +2548;BOX DRAWINGS UP LIGHT AND DOWN HORIZONTAL HEAVY;So;0;ON;;;;;N;FORMS UP LIGHT AND DOWN HORIZONTAL HEAVY;;;; +2549;BOX DRAWINGS RIGHT LIGHT AND LEFT VERTICAL HEAVY;So;0;ON;;;;;N;FORMS RIGHT LIGHT AND LEFT VERTICAL HEAVY;;;; +254A;BOX DRAWINGS LEFT LIGHT AND RIGHT VERTICAL HEAVY;So;0;ON;;;;;N;FORMS LEFT LIGHT AND RIGHT VERTICAL HEAVY;;;; +254B;BOX DRAWINGS HEAVY VERTICAL AND HORIZONTAL;So;0;ON;;;;;N;FORMS HEAVY VERTICAL AND HORIZONTAL;;;; +254C;BOX DRAWINGS LIGHT DOUBLE DASH HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT DOUBLE DASH HORIZONTAL;;;; +254D;BOX DRAWINGS HEAVY DOUBLE DASH HORIZONTAL;So;0;ON;;;;;N;FORMS HEAVY DOUBLE DASH HORIZONTAL;;;; +254E;BOX DRAWINGS LIGHT DOUBLE DASH VERTICAL;So;0;ON;;;;;N;FORMS LIGHT DOUBLE DASH VERTICAL;;;; +254F;BOX DRAWINGS HEAVY DOUBLE DASH VERTICAL;So;0;ON;;;;;N;FORMS HEAVY DOUBLE DASH VERTICAL;;;; +2550;BOX DRAWINGS DOUBLE HORIZONTAL;So;0;ON;;;;;N;FORMS DOUBLE HORIZONTAL;;;; +2551;BOX DRAWINGS DOUBLE VERTICAL;So;0;ON;;;;;N;FORMS DOUBLE VERTICAL;;;; +2552;BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE;So;0;ON;;;;;N;FORMS DOWN SINGLE AND RIGHT DOUBLE;;;; +2553;BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE;So;0;ON;;;;;N;FORMS DOWN DOUBLE AND RIGHT SINGLE;;;; +2554;BOX DRAWINGS DOUBLE DOWN AND RIGHT;So;0;ON;;;;;N;FORMS DOUBLE DOWN AND RIGHT;;;; +2555;BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE;So;0;ON;;;;;N;FORMS DOWN SINGLE AND LEFT DOUBLE;;;; +2556;BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE;So;0;ON;;;;;N;FORMS DOWN DOUBLE AND LEFT SINGLE;;;; +2557;BOX DRAWINGS DOUBLE DOWN AND LEFT;So;0;ON;;;;;N;FORMS DOUBLE DOWN AND LEFT;;;; +2558;BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE;So;0;ON;;;;;N;FORMS UP SINGLE AND RIGHT DOUBLE;;;; +2559;BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE;So;0;ON;;;;;N;FORMS UP DOUBLE AND RIGHT SINGLE;;;; +255A;BOX DRAWINGS DOUBLE UP AND RIGHT;So;0;ON;;;;;N;FORMS DOUBLE UP AND RIGHT;;;; +255B;BOX DRAWINGS UP SINGLE AND LEFT DOUBLE;So;0;ON;;;;;N;FORMS UP SINGLE AND LEFT DOUBLE;;;; +255C;BOX DRAWINGS UP DOUBLE AND LEFT SINGLE;So;0;ON;;;;;N;FORMS UP DOUBLE AND LEFT SINGLE;;;; +255D;BOX DRAWINGS DOUBLE UP AND LEFT;So;0;ON;;;;;N;FORMS DOUBLE UP AND LEFT;;;; +255E;BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE;So;0;ON;;;;;N;FORMS VERTICAL SINGLE AND RIGHT DOUBLE;;;; +255F;BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE;So;0;ON;;;;;N;FORMS VERTICAL DOUBLE AND RIGHT SINGLE;;;; +2560;BOX DRAWINGS DOUBLE VERTICAL AND RIGHT;So;0;ON;;;;;N;FORMS DOUBLE VERTICAL AND RIGHT;;;; +2561;BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE;So;0;ON;;;;;N;FORMS VERTICAL SINGLE AND LEFT DOUBLE;;;; +2562;BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE;So;0;ON;;;;;N;FORMS VERTICAL DOUBLE AND LEFT SINGLE;;;; +2563;BOX DRAWINGS DOUBLE VERTICAL AND LEFT;So;0;ON;;;;;N;FORMS DOUBLE VERTICAL AND LEFT;;;; +2564;BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE;So;0;ON;;;;;N;FORMS DOWN SINGLE AND HORIZONTAL DOUBLE;;;; +2565;BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE;So;0;ON;;;;;N;FORMS DOWN DOUBLE AND HORIZONTAL SINGLE;;;; +2566;BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL;So;0;ON;;;;;N;FORMS DOUBLE DOWN AND HORIZONTAL;;;; +2567;BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE;So;0;ON;;;;;N;FORMS UP SINGLE AND HORIZONTAL DOUBLE;;;; +2568;BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE;So;0;ON;;;;;N;FORMS UP DOUBLE AND HORIZONTAL SINGLE;;;; +2569;BOX DRAWINGS DOUBLE UP AND HORIZONTAL;So;0;ON;;;;;N;FORMS DOUBLE UP AND HORIZONTAL;;;; +256A;BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE;So;0;ON;;;;;N;FORMS VERTICAL SINGLE AND HORIZONTAL DOUBLE;;;; +256B;BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE;So;0;ON;;;;;N;FORMS VERTICAL DOUBLE AND HORIZONTAL SINGLE;;;; +256C;BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL;So;0;ON;;;;;N;FORMS DOUBLE VERTICAL AND HORIZONTAL;;;; +256D;BOX DRAWINGS LIGHT ARC DOWN AND RIGHT;So;0;ON;;;;;N;FORMS LIGHT ARC DOWN AND RIGHT;;;; +256E;BOX DRAWINGS LIGHT ARC DOWN AND LEFT;So;0;ON;;;;;N;FORMS LIGHT ARC DOWN AND LEFT;;;; +256F;BOX DRAWINGS LIGHT ARC UP AND LEFT;So;0;ON;;;;;N;FORMS LIGHT ARC UP AND LEFT;;;; +2570;BOX DRAWINGS LIGHT ARC UP AND RIGHT;So;0;ON;;;;;N;FORMS LIGHT ARC UP AND RIGHT;;;; +2571;BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT;So;0;ON;;;;;N;FORMS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT;;;; +2572;BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT;So;0;ON;;;;;N;FORMS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT;;;; +2573;BOX DRAWINGS LIGHT DIAGONAL CROSS;So;0;ON;;;;;N;FORMS LIGHT DIAGONAL CROSS;;;; +2574;BOX DRAWINGS LIGHT LEFT;So;0;ON;;;;;N;FORMS LIGHT LEFT;;;; +2575;BOX DRAWINGS LIGHT UP;So;0;ON;;;;;N;FORMS LIGHT UP;;;; +2576;BOX DRAWINGS LIGHT RIGHT;So;0;ON;;;;;N;FORMS LIGHT RIGHT;;;; +2577;BOX DRAWINGS LIGHT DOWN;So;0;ON;;;;;N;FORMS LIGHT DOWN;;;; +2578;BOX DRAWINGS HEAVY LEFT;So;0;ON;;;;;N;FORMS HEAVY LEFT;;;; +2579;BOX DRAWINGS HEAVY UP;So;0;ON;;;;;N;FORMS HEAVY UP;;;; +257A;BOX DRAWINGS HEAVY RIGHT;So;0;ON;;;;;N;FORMS HEAVY RIGHT;;;; +257B;BOX DRAWINGS HEAVY DOWN;So;0;ON;;;;;N;FORMS HEAVY DOWN;;;; +257C;BOX DRAWINGS LIGHT LEFT AND HEAVY RIGHT;So;0;ON;;;;;N;FORMS LIGHT LEFT AND HEAVY RIGHT;;;; +257D;BOX DRAWINGS LIGHT UP AND HEAVY DOWN;So;0;ON;;;;;N;FORMS LIGHT UP AND HEAVY DOWN;;;; +257E;BOX DRAWINGS HEAVY LEFT AND LIGHT RIGHT;So;0;ON;;;;;N;FORMS HEAVY LEFT AND LIGHT RIGHT;;;; +257F;BOX DRAWINGS HEAVY UP AND LIGHT DOWN;So;0;ON;;;;;N;FORMS HEAVY UP AND LIGHT DOWN;;;; +2580;UPPER HALF BLOCK;So;0;ON;;;;;N;;;;; +2581;LOWER ONE EIGHTH BLOCK;So;0;ON;;;;;N;;;;; +2582;LOWER ONE QUARTER BLOCK;So;0;ON;;;;;N;;;;; +2583;LOWER THREE EIGHTHS BLOCK;So;0;ON;;;;;N;;;;; +2584;LOWER HALF BLOCK;So;0;ON;;;;;N;;;;; +2585;LOWER FIVE EIGHTHS BLOCK;So;0;ON;;;;;N;;;;; +2586;LOWER THREE QUARTERS BLOCK;So;0;ON;;;;;N;LOWER THREE QUARTER BLOCK;;;; +2587;LOWER SEVEN EIGHTHS BLOCK;So;0;ON;;;;;N;;;;; +2588;FULL BLOCK;So;0;ON;;;;;N;;;;; +2589;LEFT SEVEN EIGHTHS BLOCK;So;0;ON;;;;;N;;;;; +258A;LEFT THREE QUARTERS BLOCK;So;0;ON;;;;;N;LEFT THREE QUARTER BLOCK;;;; +258B;LEFT FIVE EIGHTHS BLOCK;So;0;ON;;;;;N;;;;; +258C;LEFT HALF BLOCK;So;0;ON;;;;;N;;;;; +258D;LEFT THREE EIGHTHS BLOCK;So;0;ON;;;;;N;;;;; +258E;LEFT ONE QUARTER BLOCK;So;0;ON;;;;;N;;;;; +258F;LEFT ONE EIGHTH BLOCK;So;0;ON;;;;;N;;;;; +2590;RIGHT HALF BLOCK;So;0;ON;;;;;N;;;;; +2591;LIGHT SHADE;So;0;ON;;;;;N;;;;; +2592;MEDIUM SHADE;So;0;ON;;;;;N;;;;; +2593;DARK SHADE;So;0;ON;;;;;N;;;;; +2594;UPPER ONE EIGHTH BLOCK;So;0;ON;;;;;N;;;;; +2595;RIGHT ONE EIGHTH BLOCK;So;0;ON;;;;;N;;;;; +2596;QUADRANT LOWER LEFT;So;0;ON;;;;;N;;;;; +2597;QUADRANT LOWER RIGHT;So;0;ON;;;;;N;;;;; +2598;QUADRANT UPPER LEFT;So;0;ON;;;;;N;;;;; +2599;QUADRANT UPPER LEFT AND LOWER LEFT AND LOWER RIGHT;So;0;ON;;;;;N;;;;; +259A;QUADRANT UPPER LEFT AND LOWER RIGHT;So;0;ON;;;;;N;;;;; +259B;QUADRANT UPPER LEFT AND UPPER RIGHT AND LOWER LEFT;So;0;ON;;;;;N;;;;; +259C;QUADRANT UPPER LEFT AND UPPER RIGHT AND LOWER RIGHT;So;0;ON;;;;;N;;;;; +259D;QUADRANT UPPER RIGHT;So;0;ON;;;;;N;;;;; +259E;QUADRANT UPPER RIGHT AND LOWER LEFT;So;0;ON;;;;;N;;;;; +259F;QUADRANT UPPER RIGHT AND LOWER LEFT AND LOWER RIGHT;So;0;ON;;;;;N;;;;; +25A0;BLACK SQUARE;So;0;ON;;;;;N;;;;; +25A1;WHITE SQUARE;So;0;ON;;;;;N;;;;; +25A2;WHITE SQUARE WITH ROUNDED CORNERS;So;0;ON;;;;;N;;;;; +25A3;WHITE SQUARE CONTAINING BLACK SMALL SQUARE;So;0;ON;;;;;N;;;;; +25A4;SQUARE WITH HORIZONTAL FILL;So;0;ON;;;;;N;;;;; +25A5;SQUARE WITH VERTICAL FILL;So;0;ON;;;;;N;;;;; +25A6;SQUARE WITH ORTHOGONAL CROSSHATCH FILL;So;0;ON;;;;;N;;;;; +25A7;SQUARE WITH UPPER LEFT TO LOWER RIGHT FILL;So;0;ON;;;;;N;;;;; +25A8;SQUARE WITH UPPER RIGHT TO LOWER LEFT FILL;So;0;ON;;;;;N;;;;; +25A9;SQUARE WITH DIAGONAL CROSSHATCH FILL;So;0;ON;;;;;N;;;;; +25AA;BLACK SMALL SQUARE;So;0;ON;;;;;N;;;;; +25AB;WHITE SMALL SQUARE;So;0;ON;;;;;N;;;;; +25AC;BLACK RECTANGLE;So;0;ON;;;;;N;;;;; +25AD;WHITE RECTANGLE;So;0;ON;;;;;N;;;;; +25AE;BLACK VERTICAL RECTANGLE;So;0;ON;;;;;N;;;;; +25AF;WHITE VERTICAL RECTANGLE;So;0;ON;;;;;N;;;;; +25B0;BLACK PARALLELOGRAM;So;0;ON;;;;;N;;;;; +25B1;WHITE PARALLELOGRAM;So;0;ON;;;;;N;;;;; +25B2;BLACK UP-POINTING TRIANGLE;So;0;ON;;;;;N;BLACK UP POINTING TRIANGLE;;;; +25B3;WHITE UP-POINTING TRIANGLE;So;0;ON;;;;;N;WHITE UP POINTING TRIANGLE;;;; +25B4;BLACK UP-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;BLACK UP POINTING SMALL TRIANGLE;;;; +25B5;WHITE UP-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;WHITE UP POINTING SMALL TRIANGLE;;;; +25B6;BLACK RIGHT-POINTING TRIANGLE;So;0;ON;;;;;N;BLACK RIGHT POINTING TRIANGLE;;;; +25B7;WHITE RIGHT-POINTING TRIANGLE;Sm;0;ON;;;;;N;WHITE RIGHT POINTING TRIANGLE;;;; +25B8;BLACK RIGHT-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;BLACK RIGHT POINTING SMALL TRIANGLE;;;; +25B9;WHITE RIGHT-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;WHITE RIGHT POINTING SMALL TRIANGLE;;;; +25BA;BLACK RIGHT-POINTING POINTER;So;0;ON;;;;;N;BLACK RIGHT POINTING POINTER;;;; +25BB;WHITE RIGHT-POINTING POINTER;So;0;ON;;;;;N;WHITE RIGHT POINTING POINTER;;;; +25BC;BLACK DOWN-POINTING TRIANGLE;So;0;ON;;;;;N;BLACK DOWN POINTING TRIANGLE;;;; +25BD;WHITE DOWN-POINTING TRIANGLE;So;0;ON;;;;;N;WHITE DOWN POINTING TRIANGLE;;;; +25BE;BLACK DOWN-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;BLACK DOWN POINTING SMALL TRIANGLE;;;; +25BF;WHITE DOWN-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;WHITE DOWN POINTING SMALL TRIANGLE;;;; +25C0;BLACK LEFT-POINTING TRIANGLE;So;0;ON;;;;;N;BLACK LEFT POINTING TRIANGLE;;;; +25C1;WHITE LEFT-POINTING TRIANGLE;Sm;0;ON;;;;;N;WHITE LEFT POINTING TRIANGLE;;;; +25C2;BLACK LEFT-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;BLACK LEFT POINTING SMALL TRIANGLE;;;; +25C3;WHITE LEFT-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;WHITE LEFT POINTING SMALL TRIANGLE;;;; +25C4;BLACK LEFT-POINTING POINTER;So;0;ON;;;;;N;BLACK LEFT POINTING POINTER;;;; +25C5;WHITE LEFT-POINTING POINTER;So;0;ON;;;;;N;WHITE LEFT POINTING POINTER;;;; +25C6;BLACK DIAMOND;So;0;ON;;;;;N;;;;; +25C7;WHITE DIAMOND;So;0;ON;;;;;N;;;;; +25C8;WHITE DIAMOND CONTAINING BLACK SMALL DIAMOND;So;0;ON;;;;;N;;;;; +25C9;FISHEYE;So;0;ON;;;;;N;;;;; +25CA;LOZENGE;So;0;ON;;;;;N;;;;; +25CB;WHITE CIRCLE;So;0;ON;;;;;N;;;;; +25CC;DOTTED CIRCLE;So;0;ON;;;;;N;;;;; +25CD;CIRCLE WITH VERTICAL FILL;So;0;ON;;;;;N;;;;; +25CE;BULLSEYE;So;0;ON;;;;;N;;;;; +25CF;BLACK CIRCLE;So;0;ON;;;;;N;;;;; +25D0;CIRCLE WITH LEFT HALF BLACK;So;0;ON;;;;;N;;;;; +25D1;CIRCLE WITH RIGHT HALF BLACK;So;0;ON;;;;;N;;;;; +25D2;CIRCLE WITH LOWER HALF BLACK;So;0;ON;;;;;N;;;;; +25D3;CIRCLE WITH UPPER HALF BLACK;So;0;ON;;;;;N;;;;; +25D4;CIRCLE WITH UPPER RIGHT QUADRANT BLACK;So;0;ON;;;;;N;;;;; +25D5;CIRCLE WITH ALL BUT UPPER LEFT QUADRANT BLACK;So;0;ON;;;;;N;;;;; +25D6;LEFT HALF BLACK CIRCLE;So;0;ON;;;;;N;;;;; +25D7;RIGHT HALF BLACK CIRCLE;So;0;ON;;;;;N;;;;; +25D8;INVERSE BULLET;So;0;ON;;;;;N;;;;; +25D9;INVERSE WHITE CIRCLE;So;0;ON;;;;;N;;;;; +25DA;UPPER HALF INVERSE WHITE CIRCLE;So;0;ON;;;;;N;;;;; +25DB;LOWER HALF INVERSE WHITE CIRCLE;So;0;ON;;;;;N;;;;; +25DC;UPPER LEFT QUADRANT CIRCULAR ARC;So;0;ON;;;;;N;;;;; +25DD;UPPER RIGHT QUADRANT CIRCULAR ARC;So;0;ON;;;;;N;;;;; +25DE;LOWER RIGHT QUADRANT CIRCULAR ARC;So;0;ON;;;;;N;;;;; +25DF;LOWER LEFT QUADRANT CIRCULAR ARC;So;0;ON;;;;;N;;;;; +25E0;UPPER HALF CIRCLE;So;0;ON;;;;;N;;;;; +25E1;LOWER HALF CIRCLE;So;0;ON;;;;;N;;;;; +25E2;BLACK LOWER RIGHT TRIANGLE;So;0;ON;;;;;N;;;;; +25E3;BLACK LOWER LEFT TRIANGLE;So;0;ON;;;;;N;;;;; +25E4;BLACK UPPER LEFT TRIANGLE;So;0;ON;;;;;N;;;;; +25E5;BLACK UPPER RIGHT TRIANGLE;So;0;ON;;;;;N;;;;; +25E6;WHITE BULLET;So;0;ON;;;;;N;;;;; +25E7;SQUARE WITH LEFT HALF BLACK;So;0;ON;;;;;N;;;;; +25E8;SQUARE WITH RIGHT HALF BLACK;So;0;ON;;;;;N;;;;; +25E9;SQUARE WITH UPPER LEFT DIAGONAL HALF BLACK;So;0;ON;;;;;N;;;;; +25EA;SQUARE WITH LOWER RIGHT DIAGONAL HALF BLACK;So;0;ON;;;;;N;;;;; +25EB;WHITE SQUARE WITH VERTICAL BISECTING LINE;So;0;ON;;;;;N;;;;; +25EC;WHITE UP-POINTING TRIANGLE WITH DOT;So;0;ON;;;;;N;WHITE UP POINTING TRIANGLE WITH DOT;;;; +25ED;UP-POINTING TRIANGLE WITH LEFT HALF BLACK;So;0;ON;;;;;N;UP POINTING TRIANGLE WITH LEFT HALF BLACK;;;; +25EE;UP-POINTING TRIANGLE WITH RIGHT HALF BLACK;So;0;ON;;;;;N;UP POINTING TRIANGLE WITH RIGHT HALF BLACK;;;; +25EF;LARGE CIRCLE;So;0;ON;;;;;N;;;;; +25F0;WHITE SQUARE WITH UPPER LEFT QUADRANT;So;0;ON;;;;;N;;;;; +25F1;WHITE SQUARE WITH LOWER LEFT QUADRANT;So;0;ON;;;;;N;;;;; +25F2;WHITE SQUARE WITH LOWER RIGHT QUADRANT;So;0;ON;;;;;N;;;;; +25F3;WHITE SQUARE WITH UPPER RIGHT QUADRANT;So;0;ON;;;;;N;;;;; +25F4;WHITE CIRCLE WITH UPPER LEFT QUADRANT;So;0;ON;;;;;N;;;;; +25F5;WHITE CIRCLE WITH LOWER LEFT QUADRANT;So;0;ON;;;;;N;;;;; +25F6;WHITE CIRCLE WITH LOWER RIGHT QUADRANT;So;0;ON;;;;;N;;;;; +25F7;WHITE CIRCLE WITH UPPER RIGHT QUADRANT;So;0;ON;;;;;N;;;;; +25F8;UPPER LEFT TRIANGLE;Sm;0;ON;;;;;N;;;;; +25F9;UPPER RIGHT TRIANGLE;Sm;0;ON;;;;;N;;;;; +25FA;LOWER LEFT TRIANGLE;Sm;0;ON;;;;;N;;;;; +25FB;WHITE MEDIUM SQUARE;Sm;0;ON;;;;;N;;;;; +25FC;BLACK MEDIUM SQUARE;Sm;0;ON;;;;;N;;;;; +25FD;WHITE MEDIUM SMALL SQUARE;Sm;0;ON;;;;;N;;;;; +25FE;BLACK MEDIUM SMALL SQUARE;Sm;0;ON;;;;;N;;;;; +25FF;LOWER RIGHT TRIANGLE;Sm;0;ON;;;;;N;;;;; +2600;BLACK SUN WITH RAYS;So;0;ON;;;;;N;;;;; +2601;CLOUD;So;0;ON;;;;;N;;;;; +2602;UMBRELLA;So;0;ON;;;;;N;;;;; +2603;SNOWMAN;So;0;ON;;;;;N;;;;; +2604;COMET;So;0;ON;;;;;N;;;;; +2605;BLACK STAR;So;0;ON;;;;;N;;;;; +2606;WHITE STAR;So;0;ON;;;;;N;;;;; +2607;LIGHTNING;So;0;ON;;;;;N;;;;; +2608;THUNDERSTORM;So;0;ON;;;;;N;;;;; +2609;SUN;So;0;ON;;;;;N;;;;; +260A;ASCENDING NODE;So;0;ON;;;;;N;;;;; +260B;DESCENDING NODE;So;0;ON;;;;;N;;;;; +260C;CONJUNCTION;So;0;ON;;;;;N;;;;; +260D;OPPOSITION;So;0;ON;;;;;N;;;;; +260E;BLACK TELEPHONE;So;0;ON;;;;;N;;;;; +260F;WHITE TELEPHONE;So;0;ON;;;;;N;;;;; +2610;BALLOT BOX;So;0;ON;;;;;N;;;;; +2611;BALLOT BOX WITH CHECK;So;0;ON;;;;;N;;;;; +2612;BALLOT BOX WITH X;So;0;ON;;;;;N;;;;; +2613;SALTIRE;So;0;ON;;;;;N;;;;; +2616;WHITE SHOGI PIECE;So;0;ON;;;;;N;;;;; +2617;BLACK SHOGI PIECE;So;0;ON;;;;;N;;;;; +2619;REVERSED ROTATED FLORAL HEART BULLET;So;0;ON;;;;;N;;;;; +261A;BLACK LEFT POINTING INDEX;So;0;ON;;;;;N;;;;; +261B;BLACK RIGHT POINTING INDEX;So;0;ON;;;;;N;;;;; +261C;WHITE LEFT POINTING INDEX;So;0;ON;;;;;N;;;;; +261D;WHITE UP POINTING INDEX;So;0;ON;;;;;N;;;;; +261E;WHITE RIGHT POINTING INDEX;So;0;ON;;;;;N;;;;; +261F;WHITE DOWN POINTING INDEX;So;0;ON;;;;;N;;;;; +2620;SKULL AND CROSSBONES;So;0;ON;;;;;N;;;;; +2621;CAUTION SIGN;So;0;ON;;;;;N;;;;; +2622;RADIOACTIVE SIGN;So;0;ON;;;;;N;;;;; +2623;BIOHAZARD SIGN;So;0;ON;;;;;N;;;;; +2624;CADUCEUS;So;0;ON;;;;;N;;;;; +2625;ANKH;So;0;ON;;;;;N;;;;; +2626;ORTHODOX CROSS;So;0;ON;;;;;N;;;;; +2627;CHI RHO;So;0;ON;;;;;N;;;;; +2628;CROSS OF LORRAINE;So;0;ON;;;;;N;;;;; +2629;CROSS OF JERUSALEM;So;0;ON;;;;;N;;;;; +262A;STAR AND CRESCENT;So;0;ON;;;;;N;;;;; +262B;FARSI SYMBOL;So;0;ON;;;;;N;SYMBOL OF IRAN;;;; +262C;ADI SHAKTI;So;0;ON;;;;;N;;;;; +262D;HAMMER AND SICKLE;So;0;ON;;;;;N;;;;; +262E;PEACE SYMBOL;So;0;ON;;;;;N;;;;; +262F;YIN YANG;So;0;ON;;;;;N;;;;; +2630;TRIGRAM FOR HEAVEN;So;0;ON;;;;;N;;;;; +2631;TRIGRAM FOR LAKE;So;0;ON;;;;;N;;;;; +2632;TRIGRAM FOR FIRE;So;0;ON;;;;;N;;;;; +2633;TRIGRAM FOR THUNDER;So;0;ON;;;;;N;;;;; +2634;TRIGRAM FOR WIND;So;0;ON;;;;;N;;;;; +2635;TRIGRAM FOR WATER;So;0;ON;;;;;N;;;;; +2636;TRIGRAM FOR MOUNTAIN;So;0;ON;;;;;N;;;;; +2637;TRIGRAM FOR EARTH;So;0;ON;;;;;N;;;;; +2638;WHEEL OF DHARMA;So;0;ON;;;;;N;;;;; +2639;WHITE FROWNING FACE;So;0;ON;;;;;N;;;;; +263A;WHITE SMILING FACE;So;0;ON;;;;;N;;;;; +263B;BLACK SMILING FACE;So;0;ON;;;;;N;;;;; +263C;WHITE SUN WITH RAYS;So;0;ON;;;;;N;;;;; +263D;FIRST QUARTER MOON;So;0;ON;;;;;N;;;;; +263E;LAST QUARTER MOON;So;0;ON;;;;;N;;;;; +263F;MERCURY;So;0;ON;;;;;N;;;;; +2640;FEMALE SIGN;So;0;ON;;;;;N;;;;; +2641;EARTH;So;0;ON;;;;;N;;;;; +2642;MALE SIGN;So;0;ON;;;;;N;;;;; +2643;JUPITER;So;0;ON;;;;;N;;;;; +2644;SATURN;So;0;ON;;;;;N;;;;; +2645;URANUS;So;0;ON;;;;;N;;;;; +2646;NEPTUNE;So;0;ON;;;;;N;;;;; +2647;PLUTO;So;0;ON;;;;;N;;;;; +2648;ARIES;So;0;ON;;;;;N;;;;; +2649;TAURUS;So;0;ON;;;;;N;;;;; +264A;GEMINI;So;0;ON;;;;;N;;;;; +264B;CANCER;So;0;ON;;;;;N;;;;; +264C;LEO;So;0;ON;;;;;N;;;;; +264D;VIRGO;So;0;ON;;;;;N;;;;; +264E;LIBRA;So;0;ON;;;;;N;;;;; +264F;SCORPIUS;So;0;ON;;;;;N;;;;; +2650;SAGITTARIUS;So;0;ON;;;;;N;;;;; +2651;CAPRICORN;So;0;ON;;;;;N;;;;; +2652;AQUARIUS;So;0;ON;;;;;N;;;;; +2653;PISCES;So;0;ON;;;;;N;;;;; +2654;WHITE CHESS KING;So;0;ON;;;;;N;;;;; +2655;WHITE CHESS QUEEN;So;0;ON;;;;;N;;;;; +2656;WHITE CHESS ROOK;So;0;ON;;;;;N;;;;; +2657;WHITE CHESS BISHOP;So;0;ON;;;;;N;;;;; +2658;WHITE CHESS KNIGHT;So;0;ON;;;;;N;;;;; +2659;WHITE CHESS PAWN;So;0;ON;;;;;N;;;;; +265A;BLACK CHESS KING;So;0;ON;;;;;N;;;;; +265B;BLACK CHESS QUEEN;So;0;ON;;;;;N;;;;; +265C;BLACK CHESS ROOK;So;0;ON;;;;;N;;;;; +265D;BLACK CHESS BISHOP;So;0;ON;;;;;N;;;;; +265E;BLACK CHESS KNIGHT;So;0;ON;;;;;N;;;;; +265F;BLACK CHESS PAWN;So;0;ON;;;;;N;;;;; +2660;BLACK SPADE SUIT;So;0;ON;;;;;N;;;;; +2661;WHITE HEART SUIT;So;0;ON;;;;;N;;;;; +2662;WHITE DIAMOND SUIT;So;0;ON;;;;;N;;;;; +2663;BLACK CLUB SUIT;So;0;ON;;;;;N;;;;; +2664;WHITE SPADE SUIT;So;0;ON;;;;;N;;;;; +2665;BLACK HEART SUIT;So;0;ON;;;;;N;;;;; +2666;BLACK DIAMOND SUIT;So;0;ON;;;;;N;;;;; +2667;WHITE CLUB SUIT;So;0;ON;;;;;N;;;;; +2668;HOT SPRINGS;So;0;ON;;;;;N;;;;; +2669;QUARTER NOTE;So;0;ON;;;;;N;;;;; +266A;EIGHTH NOTE;So;0;ON;;;;;N;;;;; +266B;BEAMED EIGHTH NOTES;So;0;ON;;;;;N;BARRED EIGHTH NOTES;;;; +266C;BEAMED SIXTEENTH NOTES;So;0;ON;;;;;N;BARRED SIXTEENTH NOTES;;;; +266D;MUSIC FLAT SIGN;So;0;ON;;;;;N;FLAT;;;; +266E;MUSIC NATURAL SIGN;So;0;ON;;;;;N;NATURAL;;;; +266F;MUSIC SHARP SIGN;Sm;0;ON;;;;;N;SHARP;;;; +2670;WEST SYRIAC CROSS;So;0;ON;;;;;N;;;;; +2671;EAST SYRIAC CROSS;So;0;ON;;;;;N;;;;; +2672;UNIVERSAL RECYCLING SYMBOL;So;0;ON;;;;;N;;;;; +2673;RECYCLING SYMBOL FOR TYPE-1 PLASTICS;So;0;ON;;;;;N;;pete;;; +2674;RECYCLING SYMBOL FOR TYPE-2 PLASTICS;So;0;ON;;;;;N;;hdpe;;; +2675;RECYCLING SYMBOL FOR TYPE-3 PLASTICS;So;0;ON;;;;;N;;pvc;;; +2676;RECYCLING SYMBOL FOR TYPE-4 PLASTICS;So;0;ON;;;;;N;;ldpe;;; +2677;RECYCLING SYMBOL FOR TYPE-5 PLASTICS;So;0;ON;;;;;N;;pp;;; +2678;RECYCLING SYMBOL FOR TYPE-6 PLASTICS;So;0;ON;;;;;N;;ps;;; +2679;RECYCLING SYMBOL FOR TYPE-7 PLASTICS;So;0;ON;;;;;N;;other;;; +267A;RECYCLING SYMBOL FOR GENERIC MATERIALS;So;0;ON;;;;;N;;;;; +267B;BLACK UNIVERSAL RECYCLING SYMBOL;So;0;ON;;;;;N;;;;; +267C;RECYCLED PAPER SYMBOL;So;0;ON;;;;;N;;;;; +267D;PARTIALLY-RECYCLED PAPER SYMBOL;So;0;ON;;;;;N;;;;; +2680;DIE FACE-1;So;0;ON;;;;;N;;;;; +2681;DIE FACE-2;So;0;ON;;;;;N;;;;; +2682;DIE FACE-3;So;0;ON;;;;;N;;;;; +2683;DIE FACE-4;So;0;ON;;;;;N;;;;; +2684;DIE FACE-5;So;0;ON;;;;;N;;;;; +2685;DIE FACE-6;So;0;ON;;;;;N;;;;; +2686;WHITE CIRCLE WITH DOT RIGHT;So;0;ON;;;;;N;;;;; +2687;WHITE CIRCLE WITH TWO DOTS;So;0;ON;;;;;N;;;;; +2688;BLACK CIRCLE WITH WHITE DOT RIGHT;So;0;ON;;;;;N;;;;; +2689;BLACK CIRCLE WITH TWO WHITE DOTS;So;0;ON;;;;;N;;;;; +2701;UPPER BLADE SCISSORS;So;0;ON;;;;;N;;;;; +2702;BLACK SCISSORS;So;0;ON;;;;;N;;;;; +2703;LOWER BLADE SCISSORS;So;0;ON;;;;;N;;;;; +2704;WHITE SCISSORS;So;0;ON;;;;;N;;;;; +2706;TELEPHONE LOCATION SIGN;So;0;ON;;;;;N;;;;; +2707;TAPE DRIVE;So;0;ON;;;;;N;;;;; +2708;AIRPLANE;So;0;ON;;;;;N;;;;; +2709;ENVELOPE;So;0;ON;;;;;N;;;;; +270C;VICTORY HAND;So;0;ON;;;;;N;;;;; +270D;WRITING HAND;So;0;ON;;;;;N;;;;; +270E;LOWER RIGHT PENCIL;So;0;ON;;;;;N;;;;; +270F;PENCIL;So;0;ON;;;;;N;;;;; +2710;UPPER RIGHT PENCIL;So;0;ON;;;;;N;;;;; +2711;WHITE NIB;So;0;ON;;;;;N;;;;; +2712;BLACK NIB;So;0;ON;;;;;N;;;;; +2713;CHECK MARK;So;0;ON;;;;;N;;;;; +2714;HEAVY CHECK MARK;So;0;ON;;;;;N;;;;; +2715;MULTIPLICATION X;So;0;ON;;;;;N;;;;; +2716;HEAVY MULTIPLICATION X;So;0;ON;;;;;N;;;;; +2717;BALLOT X;So;0;ON;;;;;N;;;;; +2718;HEAVY BALLOT X;So;0;ON;;;;;N;;;;; +2719;OUTLINED GREEK CROSS;So;0;ON;;;;;N;;;;; +271A;HEAVY GREEK CROSS;So;0;ON;;;;;N;;;;; +271B;OPEN CENTRE CROSS;So;0;ON;;;;;N;OPEN CENTER CROSS;;;; +271C;HEAVY OPEN CENTRE CROSS;So;0;ON;;;;;N;HEAVY OPEN CENTER CROSS;;;; +271D;LATIN CROSS;So;0;ON;;;;;N;;;;; +271E;SHADOWED WHITE LATIN CROSS;So;0;ON;;;;;N;;;;; +271F;OUTLINED LATIN CROSS;So;0;ON;;;;;N;;;;; +2720;MALTESE CROSS;So;0;ON;;;;;N;;;;; +2721;STAR OF DAVID;So;0;ON;;;;;N;;;;; +2722;FOUR TEARDROP-SPOKED ASTERISK;So;0;ON;;;;;N;;;;; +2723;FOUR BALLOON-SPOKED ASTERISK;So;0;ON;;;;;N;;;;; +2724;HEAVY FOUR BALLOON-SPOKED ASTERISK;So;0;ON;;;;;N;;;;; +2725;FOUR CLUB-SPOKED ASTERISK;So;0;ON;;;;;N;;;;; +2726;BLACK FOUR POINTED STAR;So;0;ON;;;;;N;;;;; +2727;WHITE FOUR POINTED STAR;So;0;ON;;;;;N;;;;; +2729;STRESS OUTLINED WHITE STAR;So;0;ON;;;;;N;;;;; +272A;CIRCLED WHITE STAR;So;0;ON;;;;;N;;;;; +272B;OPEN CENTRE BLACK STAR;So;0;ON;;;;;N;OPEN CENTER BLACK STAR;;;; +272C;BLACK CENTRE WHITE STAR;So;0;ON;;;;;N;BLACK CENTER WHITE STAR;;;; +272D;OUTLINED BLACK STAR;So;0;ON;;;;;N;;;;; +272E;HEAVY OUTLINED BLACK STAR;So;0;ON;;;;;N;;;;; +272F;PINWHEEL STAR;So;0;ON;;;;;N;;;;; +2730;SHADOWED WHITE STAR;So;0;ON;;;;;N;;;;; +2731;HEAVY ASTERISK;So;0;ON;;;;;N;;;;; +2732;OPEN CENTRE ASTERISK;So;0;ON;;;;;N;OPEN CENTER ASTERISK;;;; +2733;EIGHT SPOKED ASTERISK;So;0;ON;;;;;N;;;;; +2734;EIGHT POINTED BLACK STAR;So;0;ON;;;;;N;;;;; +2735;EIGHT POINTED PINWHEEL STAR;So;0;ON;;;;;N;;;;; +2736;SIX POINTED BLACK STAR;So;0;ON;;;;;N;;;;; +2737;EIGHT POINTED RECTILINEAR BLACK STAR;So;0;ON;;;;;N;;;;; +2738;HEAVY EIGHT POINTED RECTILINEAR BLACK STAR;So;0;ON;;;;;N;;;;; +2739;TWELVE POINTED BLACK STAR;So;0;ON;;;;;N;;;;; +273A;SIXTEEN POINTED ASTERISK;So;0;ON;;;;;N;;;;; +273B;TEARDROP-SPOKED ASTERISK;So;0;ON;;;;;N;;;;; +273C;OPEN CENTRE TEARDROP-SPOKED ASTERISK;So;0;ON;;;;;N;OPEN CENTER TEARDROP-SPOKED ASTERISK;;;; +273D;HEAVY TEARDROP-SPOKED ASTERISK;So;0;ON;;;;;N;;;;; +273E;SIX PETALLED BLACK AND WHITE FLORETTE;So;0;ON;;;;;N;;;;; +273F;BLACK FLORETTE;So;0;ON;;;;;N;;;;; +2740;WHITE FLORETTE;So;0;ON;;;;;N;;;;; +2741;EIGHT PETALLED OUTLINED BLACK FLORETTE;So;0;ON;;;;;N;;;;; +2742;CIRCLED OPEN CENTRE EIGHT POINTED STAR;So;0;ON;;;;;N;CIRCLED OPEN CENTER EIGHT POINTED STAR;;;; +2743;HEAVY TEARDROP-SPOKED PINWHEEL ASTERISK;So;0;ON;;;;;N;;;;; +2744;SNOWFLAKE;So;0;ON;;;;;N;;;;; +2745;TIGHT TRIFOLIATE SNOWFLAKE;So;0;ON;;;;;N;;;;; +2746;HEAVY CHEVRON SNOWFLAKE;So;0;ON;;;;;N;;;;; +2747;SPARKLE;So;0;ON;;;;;N;;;;; +2748;HEAVY SPARKLE;So;0;ON;;;;;N;;;;; +2749;BALLOON-SPOKED ASTERISK;So;0;ON;;;;;N;;;;; +274A;EIGHT TEARDROP-SPOKED PROPELLER ASTERISK;So;0;ON;;;;;N;;;;; +274B;HEAVY EIGHT TEARDROP-SPOKED PROPELLER ASTERISK;So;0;ON;;;;;N;;;;; +274D;SHADOWED WHITE CIRCLE;So;0;ON;;;;;N;;;;; +274F;LOWER RIGHT DROP-SHADOWED WHITE SQUARE;So;0;ON;;;;;N;;;;; +2750;UPPER RIGHT DROP-SHADOWED WHITE SQUARE;So;0;ON;;;;;N;;;;; +2751;LOWER RIGHT SHADOWED WHITE SQUARE;So;0;ON;;;;;N;;;;; +2752;UPPER RIGHT SHADOWED WHITE SQUARE;So;0;ON;;;;;N;;;;; +2756;BLACK DIAMOND MINUS WHITE X;So;0;ON;;;;;N;;;;; +2758;LIGHT VERTICAL BAR;So;0;ON;;;;;N;;;;; +2759;MEDIUM VERTICAL BAR;So;0;ON;;;;;N;;;;; +275A;HEAVY VERTICAL BAR;So;0;ON;;;;;N;;;;; +275B;HEAVY SINGLE TURNED COMMA QUOTATION MARK ORNAMENT;So;0;ON;;;;;N;;;;; +275C;HEAVY SINGLE COMMA QUOTATION MARK ORNAMENT;So;0;ON;;;;;N;;;;; +275D;HEAVY DOUBLE TURNED COMMA QUOTATION MARK ORNAMENT;So;0;ON;;;;;N;;;;; +275E;HEAVY DOUBLE COMMA QUOTATION MARK ORNAMENT;So;0;ON;;;;;N;;;;; +2761;CURVED STEM PARAGRAPH SIGN ORNAMENT;So;0;ON;;;;;N;;;;; +2762;HEAVY EXCLAMATION MARK ORNAMENT;So;0;ON;;;;;N;;;;; +2763;HEAVY HEART EXCLAMATION MARK ORNAMENT;So;0;ON;;;;;N;;;;; +2764;HEAVY BLACK HEART;So;0;ON;;;;;N;;;;; +2765;ROTATED HEAVY BLACK HEART BULLET;So;0;ON;;;;;N;;;;; +2766;FLORAL HEART;So;0;ON;;;;;N;;;;; +2767;ROTATED FLORAL HEART BULLET;So;0;ON;;;;;N;;;;; +2768;MEDIUM LEFT PARENTHESIS ORNAMENT;Ps;0;ON;;;;;Y;;;;; +2769;MEDIUM RIGHT PARENTHESIS ORNAMENT;Pe;0;ON;;;;;Y;;;;; +276A;MEDIUM FLATTENED LEFT PARENTHESIS ORNAMENT;Ps;0;ON;;;;;Y;;;;; +276B;MEDIUM FLATTENED RIGHT PARENTHESIS ORNAMENT;Pe;0;ON;;;;;Y;;;;; +276C;MEDIUM LEFT-POINTING ANGLE BRACKET ORNAMENT;Ps;0;ON;;;;;Y;;;;; +276D;MEDIUM RIGHT-POINTING ANGLE BRACKET ORNAMENT;Pe;0;ON;;;;;Y;;;;; +276E;HEAVY LEFT-POINTING ANGLE QUOTATION MARK ORNAMENT;Ps;0;ON;;;;;Y;;;;; +276F;HEAVY RIGHT-POINTING ANGLE QUOTATION MARK ORNAMENT;Pe;0;ON;;;;;Y;;;;; +2770;HEAVY LEFT-POINTING ANGLE BRACKET ORNAMENT;Ps;0;ON;;;;;Y;;;;; +2771;HEAVY RIGHT-POINTING ANGLE BRACKET ORNAMENT;Pe;0;ON;;;;;Y;;;;; +2772;LIGHT LEFT TORTOISE SHELL BRACKET ORNAMENT;Ps;0;ON;;;;;Y;;;;; +2773;LIGHT RIGHT TORTOISE SHELL BRACKET ORNAMENT;Pe;0;ON;;;;;Y;;;;; +2774;MEDIUM LEFT CURLY BRACKET ORNAMENT;Ps;0;ON;;;;;Y;;;;; +2775;MEDIUM RIGHT CURLY BRACKET ORNAMENT;Pe;0;ON;;;;;Y;;;;; +2776;DINGBAT NEGATIVE CIRCLED DIGIT ONE;No;0;ON;;;1;1;N;INVERSE CIRCLED DIGIT ONE;;;; +2777;DINGBAT NEGATIVE CIRCLED DIGIT TWO;No;0;ON;;;2;2;N;INVERSE CIRCLED DIGIT TWO;;;; +2778;DINGBAT NEGATIVE CIRCLED DIGIT THREE;No;0;ON;;;3;3;N;INVERSE CIRCLED DIGIT THREE;;;; +2779;DINGBAT NEGATIVE CIRCLED DIGIT FOUR;No;0;ON;;;4;4;N;INVERSE CIRCLED DIGIT FOUR;;;; +277A;DINGBAT NEGATIVE CIRCLED DIGIT FIVE;No;0;ON;;;5;5;N;INVERSE CIRCLED DIGIT FIVE;;;; +277B;DINGBAT NEGATIVE CIRCLED DIGIT SIX;No;0;ON;;;6;6;N;INVERSE CIRCLED DIGIT SIX;;;; +277C;DINGBAT NEGATIVE CIRCLED DIGIT SEVEN;No;0;ON;;;7;7;N;INVERSE CIRCLED DIGIT SEVEN;;;; +277D;DINGBAT NEGATIVE CIRCLED DIGIT EIGHT;No;0;ON;;;8;8;N;INVERSE CIRCLED DIGIT EIGHT;;;; +277E;DINGBAT NEGATIVE CIRCLED DIGIT NINE;No;0;ON;;;9;9;N;INVERSE CIRCLED DIGIT NINE;;;; +277F;DINGBAT NEGATIVE CIRCLED NUMBER TEN;No;0;ON;;;;10;N;INVERSE CIRCLED NUMBER TEN;;;; +2780;DINGBAT CIRCLED SANS-SERIF DIGIT ONE;No;0;ON;;;1;1;N;CIRCLED SANS-SERIF DIGIT ONE;;;; +2781;DINGBAT CIRCLED SANS-SERIF DIGIT TWO;No;0;ON;;;2;2;N;CIRCLED SANS-SERIF DIGIT TWO;;;; +2782;DINGBAT CIRCLED SANS-SERIF DIGIT THREE;No;0;ON;;;3;3;N;CIRCLED SANS-SERIF DIGIT THREE;;;; +2783;DINGBAT CIRCLED SANS-SERIF DIGIT FOUR;No;0;ON;;;4;4;N;CIRCLED SANS-SERIF DIGIT FOUR;;;; +2784;DINGBAT CIRCLED SANS-SERIF DIGIT FIVE;No;0;ON;;;5;5;N;CIRCLED SANS-SERIF DIGIT FIVE;;;; +2785;DINGBAT CIRCLED SANS-SERIF DIGIT SIX;No;0;ON;;;6;6;N;CIRCLED SANS-SERIF DIGIT SIX;;;; +2786;DINGBAT CIRCLED SANS-SERIF DIGIT SEVEN;No;0;ON;;;7;7;N;CIRCLED SANS-SERIF DIGIT SEVEN;;;; +2787;DINGBAT CIRCLED SANS-SERIF DIGIT EIGHT;No;0;ON;;;8;8;N;CIRCLED SANS-SERIF DIGIT EIGHT;;;; +2788;DINGBAT CIRCLED SANS-SERIF DIGIT NINE;No;0;ON;;;9;9;N;CIRCLED SANS-SERIF DIGIT NINE;;;; +2789;DINGBAT CIRCLED SANS-SERIF NUMBER TEN;No;0;ON;;;;10;N;CIRCLED SANS-SERIF NUMBER TEN;;;; +278A;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT ONE;No;0;ON;;;1;1;N;INVERSE CIRCLED SANS-SERIF DIGIT ONE;;;; +278B;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT TWO;No;0;ON;;;2;2;N;INVERSE CIRCLED SANS-SERIF DIGIT TWO;;;; +278C;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT THREE;No;0;ON;;;3;3;N;INVERSE CIRCLED SANS-SERIF DIGIT THREE;;;; +278D;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT FOUR;No;0;ON;;;4;4;N;INVERSE CIRCLED SANS-SERIF DIGIT FOUR;;;; +278E;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT FIVE;No;0;ON;;;5;5;N;INVERSE CIRCLED SANS-SERIF DIGIT FIVE;;;; +278F;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT SIX;No;0;ON;;;6;6;N;INVERSE CIRCLED SANS-SERIF DIGIT SIX;;;; +2790;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT SEVEN;No;0;ON;;;7;7;N;INVERSE CIRCLED SANS-SERIF DIGIT SEVEN;;;; +2791;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT EIGHT;No;0;ON;;;8;8;N;INVERSE CIRCLED SANS-SERIF DIGIT EIGHT;;;; +2792;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT NINE;No;0;ON;;;9;9;N;INVERSE CIRCLED SANS-SERIF DIGIT NINE;;;; +2793;DINGBAT NEGATIVE CIRCLED SANS-SERIF NUMBER TEN;No;0;ON;;;;10;N;INVERSE CIRCLED SANS-SERIF NUMBER TEN;;;; +2794;HEAVY WIDE-HEADED RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY WIDE-HEADED RIGHT ARROW;;;; +2798;HEAVY SOUTH EAST ARROW;So;0;ON;;;;;N;HEAVY LOWER RIGHT ARROW;;;; +2799;HEAVY RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY RIGHT ARROW;;;; +279A;HEAVY NORTH EAST ARROW;So;0;ON;;;;;N;HEAVY UPPER RIGHT ARROW;;;; +279B;DRAFTING POINT RIGHTWARDS ARROW;So;0;ON;;;;;N;DRAFTING POINT RIGHT ARROW;;;; +279C;HEAVY ROUND-TIPPED RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY ROUND-TIPPED RIGHT ARROW;;;; +279D;TRIANGLE-HEADED RIGHTWARDS ARROW;So;0;ON;;;;;N;TRIANGLE-HEADED RIGHT ARROW;;;; +279E;HEAVY TRIANGLE-HEADED RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY TRIANGLE-HEADED RIGHT ARROW;;;; +279F;DASHED TRIANGLE-HEADED RIGHTWARDS ARROW;So;0;ON;;;;;N;DASHED TRIANGLE-HEADED RIGHT ARROW;;;; +27A0;HEAVY DASHED TRIANGLE-HEADED RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY DASHED TRIANGLE-HEADED RIGHT ARROW;;;; +27A1;BLACK RIGHTWARDS ARROW;So;0;ON;;;;;N;BLACK RIGHT ARROW;;;; +27A2;THREE-D TOP-LIGHTED RIGHTWARDS ARROWHEAD;So;0;ON;;;;;N;THREE-D TOP-LIGHTED RIGHT ARROWHEAD;;;; +27A3;THREE-D BOTTOM-LIGHTED RIGHTWARDS ARROWHEAD;So;0;ON;;;;;N;THREE-D BOTTOM-LIGHTED RIGHT ARROWHEAD;;;; +27A4;BLACK RIGHTWARDS ARROWHEAD;So;0;ON;;;;;N;BLACK RIGHT ARROWHEAD;;;; +27A5;HEAVY BLACK CURVED DOWNWARDS AND RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY BLACK CURVED DOWN AND RIGHT ARROW;;;; +27A6;HEAVY BLACK CURVED UPWARDS AND RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY BLACK CURVED UP AND RIGHT ARROW;;;; +27A7;SQUAT BLACK RIGHTWARDS ARROW;So;0;ON;;;;;N;SQUAT BLACK RIGHT ARROW;;;; +27A8;HEAVY CONCAVE-POINTED BLACK RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY CONCAVE-POINTED BLACK RIGHT ARROW;;;; +27A9;RIGHT-SHADED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;RIGHT-SHADED WHITE RIGHT ARROW;;;; +27AA;LEFT-SHADED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;LEFT-SHADED WHITE RIGHT ARROW;;;; +27AB;BACK-TILTED SHADOWED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;BACK-TILTED SHADOWED WHITE RIGHT ARROW;;;; +27AC;FRONT-TILTED SHADOWED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;FRONT-TILTED SHADOWED WHITE RIGHT ARROW;;;; +27AD;HEAVY LOWER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY LOWER RIGHT-SHADOWED WHITE RIGHT ARROW;;;; +27AE;HEAVY UPPER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY UPPER RIGHT-SHADOWED WHITE RIGHT ARROW;;;; +27AF;NOTCHED LOWER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;NOTCHED LOWER RIGHT-SHADOWED WHITE RIGHT ARROW;;;; +27B1;NOTCHED UPPER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;NOTCHED UPPER RIGHT-SHADOWED WHITE RIGHT ARROW;;;; +27B2;CIRCLED HEAVY WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;CIRCLED HEAVY WHITE RIGHT ARROW;;;; +27B3;WHITE-FEATHERED RIGHTWARDS ARROW;So;0;ON;;;;;N;WHITE-FEATHERED RIGHT ARROW;;;; +27B4;BLACK-FEATHERED SOUTH EAST ARROW;So;0;ON;;;;;N;BLACK-FEATHERED LOWER RIGHT ARROW;;;; +27B5;BLACK-FEATHERED RIGHTWARDS ARROW;So;0;ON;;;;;N;BLACK-FEATHERED RIGHT ARROW;;;; +27B6;BLACK-FEATHERED NORTH EAST ARROW;So;0;ON;;;;;N;BLACK-FEATHERED UPPER RIGHT ARROW;;;; +27B7;HEAVY BLACK-FEATHERED SOUTH EAST ARROW;So;0;ON;;;;;N;HEAVY BLACK-FEATHERED LOWER RIGHT ARROW;;;; +27B8;HEAVY BLACK-FEATHERED RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY BLACK-FEATHERED RIGHT ARROW;;;; +27B9;HEAVY BLACK-FEATHERED NORTH EAST ARROW;So;0;ON;;;;;N;HEAVY BLACK-FEATHERED UPPER RIGHT ARROW;;;; +27BA;TEARDROP-BARBED RIGHTWARDS ARROW;So;0;ON;;;;;N;TEARDROP-BARBED RIGHT ARROW;;;; +27BB;HEAVY TEARDROP-SHANKED RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY TEARDROP-SHANKED RIGHT ARROW;;;; +27BC;WEDGE-TAILED RIGHTWARDS ARROW;So;0;ON;;;;;N;WEDGE-TAILED RIGHT ARROW;;;; +27BD;HEAVY WEDGE-TAILED RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY WEDGE-TAILED RIGHT ARROW;;;; +27BE;OPEN-OUTLINED RIGHTWARDS ARROW;So;0;ON;;;;;N;OPEN-OUTLINED RIGHT ARROW;;;; +27D0;WHITE DIAMOND WITH CENTRED DOT;Sm;0;ON;;;;;N;;;;; +27D1;AND WITH DOT;Sm;0;ON;;;;;N;;;;; +27D2;ELEMENT OF OPENING UPWARDS;Sm;0;ON;;;;;N;;;;; +27D3;LOWER RIGHT CORNER WITH DOT;Sm;0;ON;;;;;Y;;;;; +27D4;UPPER LEFT CORNER WITH DOT;Sm;0;ON;;;;;Y;;;;; +27D5;LEFT OUTER JOIN;Sm;0;ON;;;;;Y;;;;; +27D6;RIGHT OUTER JOIN;Sm;0;ON;;;;;Y;;;;; +27D7;FULL OUTER JOIN;Sm;0;ON;;;;;N;;;;; +27D8;LARGE UP TACK;Sm;0;ON;;;;;N;;;;; +27D9;LARGE DOWN TACK;Sm;0;ON;;;;;N;;;;; +27DA;LEFT AND RIGHT DOUBLE TURNSTILE;Sm;0;ON;;;;;N;;;;; +27DB;LEFT AND RIGHT TACK;Sm;0;ON;;;;;N;;;;; +27DC;LEFT MULTIMAP;Sm;0;ON;;;;;Y;;;;; +27DD;LONG RIGHT TACK;Sm;0;ON;;;;;Y;;;;; +27DE;LONG LEFT TACK;Sm;0;ON;;;;;Y;;;;; +27DF;UP TACK WITH CIRCLE ABOVE;Sm;0;ON;;;;;N;;;;; +27E0;LOZENGE DIVIDED BY HORIZONTAL RULE;Sm;0;ON;;;;;N;;;;; +27E1;WHITE CONCAVE-SIDED DIAMOND;Sm;0;ON;;;;;N;;;;; +27E2;WHITE CONCAVE-SIDED DIAMOND WITH LEFTWARDS TICK;Sm;0;ON;;;;;Y;;;;; +27E3;WHITE CONCAVE-SIDED DIAMOND WITH RIGHTWARDS TICK;Sm;0;ON;;;;;Y;;;;; +27E4;WHITE SQUARE WITH LEFTWARDS TICK;Sm;0;ON;;;;;Y;;;;; +27E5;WHITE SQUARE WITH RIGHTWARDS TICK;Sm;0;ON;;;;;Y;;;;; +27E6;MATHEMATICAL LEFT WHITE SQUARE BRACKET;Ps;0;ON;;;;;Y;;;;; +27E7;MATHEMATICAL RIGHT WHITE SQUARE BRACKET;Pe;0;ON;;;;;Y;;;;; +27E8;MATHEMATICAL LEFT ANGLE BRACKET;Ps;0;ON;;;;;Y;;;;; +27E9;MATHEMATICAL RIGHT ANGLE BRACKET;Pe;0;ON;;;;;Y;;;;; +27EA;MATHEMATICAL LEFT DOUBLE ANGLE BRACKET;Ps;0;ON;;;;;Y;;;;; +27EB;MATHEMATICAL RIGHT DOUBLE ANGLE BRACKET;Pe;0;ON;;;;;Y;;;;; +27F0;UPWARDS QUADRUPLE ARROW;Sm;0;ON;;;;;N;;;;; +27F1;DOWNWARDS QUADRUPLE ARROW;Sm;0;ON;;;;;N;;;;; +27F2;ANTICLOCKWISE GAPPED CIRCLE ARROW;Sm;0;ON;;;;;N;;;;; +27F3;CLOCKWISE GAPPED CIRCLE ARROW;Sm;0;ON;;;;;N;;;;; +27F4;RIGHT ARROW WITH CIRCLED PLUS;Sm;0;ON;;;;;N;;;;; +27F5;LONG LEFTWARDS ARROW;Sm;0;ON;;;;;N;;;;; +27F6;LONG RIGHTWARDS ARROW;Sm;0;ON;;;;;N;;;;; +27F7;LONG LEFT RIGHT ARROW;Sm;0;ON;;;;;N;;;;; +27F8;LONG LEFTWARDS DOUBLE ARROW;Sm;0;ON;;;;;N;;;;; +27F9;LONG RIGHTWARDS DOUBLE ARROW;Sm;0;ON;;;;;N;;;;; +27FA;LONG LEFT RIGHT DOUBLE ARROW;Sm;0;ON;;;;;N;;;;; +27FB;LONG LEFTWARDS ARROW FROM BAR;Sm;0;ON;;;;;N;;;;; +27FC;LONG RIGHTWARDS ARROW FROM BAR;Sm;0;ON;;;;;N;;;;; +27FD;LONG LEFTWARDS DOUBLE ARROW FROM BAR;Sm;0;ON;;;;;N;;;;; +27FE;LONG RIGHTWARDS DOUBLE ARROW FROM BAR;Sm;0;ON;;;;;N;;;;; +27FF;LONG RIGHTWARDS SQUIGGLE ARROW;Sm;0;ON;;;;;N;;;;; +2800;BRAILLE PATTERN BLANK;So;0;ON;;;;;N;;;;; +2801;BRAILLE PATTERN DOTS-1;So;0;ON;;;;;N;;;;; +2802;BRAILLE PATTERN DOTS-2;So;0;ON;;;;;N;;;;; +2803;BRAILLE PATTERN DOTS-12;So;0;ON;;;;;N;;;;; +2804;BRAILLE PATTERN DOTS-3;So;0;ON;;;;;N;;;;; +2805;BRAILLE PATTERN DOTS-13;So;0;ON;;;;;N;;;;; +2806;BRAILLE PATTERN DOTS-23;So;0;ON;;;;;N;;;;; +2807;BRAILLE PATTERN DOTS-123;So;0;ON;;;;;N;;;;; +2808;BRAILLE PATTERN DOTS-4;So;0;ON;;;;;N;;;;; +2809;BRAILLE PATTERN DOTS-14;So;0;ON;;;;;N;;;;; +280A;BRAILLE PATTERN DOTS-24;So;0;ON;;;;;N;;;;; +280B;BRAILLE PATTERN DOTS-124;So;0;ON;;;;;N;;;;; +280C;BRAILLE PATTERN DOTS-34;So;0;ON;;;;;N;;;;; +280D;BRAILLE PATTERN DOTS-134;So;0;ON;;;;;N;;;;; +280E;BRAILLE PATTERN DOTS-234;So;0;ON;;;;;N;;;;; +280F;BRAILLE PATTERN DOTS-1234;So;0;ON;;;;;N;;;;; +2810;BRAILLE PATTERN DOTS-5;So;0;ON;;;;;N;;;;; +2811;BRAILLE PATTERN DOTS-15;So;0;ON;;;;;N;;;;; +2812;BRAILLE PATTERN DOTS-25;So;0;ON;;;;;N;;;;; +2813;BRAILLE PATTERN DOTS-125;So;0;ON;;;;;N;;;;; +2814;BRAILLE PATTERN DOTS-35;So;0;ON;;;;;N;;;;; +2815;BRAILLE PATTERN DOTS-135;So;0;ON;;;;;N;;;;; +2816;BRAILLE PATTERN DOTS-235;So;0;ON;;;;;N;;;;; +2817;BRAILLE PATTERN DOTS-1235;So;0;ON;;;;;N;;;;; +2818;BRAILLE PATTERN DOTS-45;So;0;ON;;;;;N;;;;; +2819;BRAILLE PATTERN DOTS-145;So;0;ON;;;;;N;;;;; +281A;BRAILLE PATTERN DOTS-245;So;0;ON;;;;;N;;;;; +281B;BRAILLE PATTERN DOTS-1245;So;0;ON;;;;;N;;;;; +281C;BRAILLE PATTERN DOTS-345;So;0;ON;;;;;N;;;;; +281D;BRAILLE PATTERN DOTS-1345;So;0;ON;;;;;N;;;;; +281E;BRAILLE PATTERN DOTS-2345;So;0;ON;;;;;N;;;;; +281F;BRAILLE PATTERN DOTS-12345;So;0;ON;;;;;N;;;;; +2820;BRAILLE PATTERN DOTS-6;So;0;ON;;;;;N;;;;; +2821;BRAILLE PATTERN DOTS-16;So;0;ON;;;;;N;;;;; +2822;BRAILLE PATTERN DOTS-26;So;0;ON;;;;;N;;;;; +2823;BRAILLE PATTERN DOTS-126;So;0;ON;;;;;N;;;;; +2824;BRAILLE PATTERN DOTS-36;So;0;ON;;;;;N;;;;; +2825;BRAILLE PATTERN DOTS-136;So;0;ON;;;;;N;;;;; +2826;BRAILLE PATTERN DOTS-236;So;0;ON;;;;;N;;;;; +2827;BRAILLE PATTERN DOTS-1236;So;0;ON;;;;;N;;;;; +2828;BRAILLE PATTERN DOTS-46;So;0;ON;;;;;N;;;;; +2829;BRAILLE PATTERN DOTS-146;So;0;ON;;;;;N;;;;; +282A;BRAILLE PATTERN DOTS-246;So;0;ON;;;;;N;;;;; +282B;BRAILLE PATTERN DOTS-1246;So;0;ON;;;;;N;;;;; +282C;BRAILLE PATTERN DOTS-346;So;0;ON;;;;;N;;;;; +282D;BRAILLE PATTERN DOTS-1346;So;0;ON;;;;;N;;;;; +282E;BRAILLE PATTERN DOTS-2346;So;0;ON;;;;;N;;;;; +282F;BRAILLE PATTERN DOTS-12346;So;0;ON;;;;;N;;;;; +2830;BRAILLE PATTERN DOTS-56;So;0;ON;;;;;N;;;;; +2831;BRAILLE PATTERN DOTS-156;So;0;ON;;;;;N;;;;; +2832;BRAILLE PATTERN DOTS-256;So;0;ON;;;;;N;;;;; +2833;BRAILLE PATTERN DOTS-1256;So;0;ON;;;;;N;;;;; +2834;BRAILLE PATTERN DOTS-356;So;0;ON;;;;;N;;;;; +2835;BRAILLE PATTERN DOTS-1356;So;0;ON;;;;;N;;;;; +2836;BRAILLE PATTERN DOTS-2356;So;0;ON;;;;;N;;;;; +2837;BRAILLE PATTERN DOTS-12356;So;0;ON;;;;;N;;;;; +2838;BRAILLE PATTERN DOTS-456;So;0;ON;;;;;N;;;;; +2839;BRAILLE PATTERN DOTS-1456;So;0;ON;;;;;N;;;;; +283A;BRAILLE PATTERN DOTS-2456;So;0;ON;;;;;N;;;;; +283B;BRAILLE PATTERN DOTS-12456;So;0;ON;;;;;N;;;;; +283C;BRAILLE PATTERN DOTS-3456;So;0;ON;;;;;N;;;;; +283D;BRAILLE PATTERN DOTS-13456;So;0;ON;;;;;N;;;;; +283E;BRAILLE PATTERN DOTS-23456;So;0;ON;;;;;N;;;;; +283F;BRAILLE PATTERN DOTS-123456;So;0;ON;;;;;N;;;;; +2840;BRAILLE PATTERN DOTS-7;So;0;ON;;;;;N;;;;; +2841;BRAILLE PATTERN DOTS-17;So;0;ON;;;;;N;;;;; +2842;BRAILLE PATTERN DOTS-27;So;0;ON;;;;;N;;;;; +2843;BRAILLE PATTERN DOTS-127;So;0;ON;;;;;N;;;;; +2844;BRAILLE PATTERN DOTS-37;So;0;ON;;;;;N;;;;; +2845;BRAILLE PATTERN DOTS-137;So;0;ON;;;;;N;;;;; +2846;BRAILLE PATTERN DOTS-237;So;0;ON;;;;;N;;;;; +2847;BRAILLE PATTERN DOTS-1237;So;0;ON;;;;;N;;;;; +2848;BRAILLE PATTERN DOTS-47;So;0;ON;;;;;N;;;;; +2849;BRAILLE PATTERN DOTS-147;So;0;ON;;;;;N;;;;; +284A;BRAILLE PATTERN DOTS-247;So;0;ON;;;;;N;;;;; +284B;BRAILLE PATTERN DOTS-1247;So;0;ON;;;;;N;;;;; +284C;BRAILLE PATTERN DOTS-347;So;0;ON;;;;;N;;;;; +284D;BRAILLE PATTERN DOTS-1347;So;0;ON;;;;;N;;;;; +284E;BRAILLE PATTERN DOTS-2347;So;0;ON;;;;;N;;;;; +284F;BRAILLE PATTERN DOTS-12347;So;0;ON;;;;;N;;;;; +2850;BRAILLE PATTERN DOTS-57;So;0;ON;;;;;N;;;;; +2851;BRAILLE PATTERN DOTS-157;So;0;ON;;;;;N;;;;; +2852;BRAILLE PATTERN DOTS-257;So;0;ON;;;;;N;;;;; +2853;BRAILLE PATTERN DOTS-1257;So;0;ON;;;;;N;;;;; +2854;BRAILLE PATTERN DOTS-357;So;0;ON;;;;;N;;;;; +2855;BRAILLE PATTERN DOTS-1357;So;0;ON;;;;;N;;;;; +2856;BRAILLE PATTERN DOTS-2357;So;0;ON;;;;;N;;;;; +2857;BRAILLE PATTERN DOTS-12357;So;0;ON;;;;;N;;;;; +2858;BRAILLE PATTERN DOTS-457;So;0;ON;;;;;N;;;;; +2859;BRAILLE PATTERN DOTS-1457;So;0;ON;;;;;N;;;;; +285A;BRAILLE PATTERN DOTS-2457;So;0;ON;;;;;N;;;;; +285B;BRAILLE PATTERN DOTS-12457;So;0;ON;;;;;N;;;;; +285C;BRAILLE PATTERN DOTS-3457;So;0;ON;;;;;N;;;;; +285D;BRAILLE PATTERN DOTS-13457;So;0;ON;;;;;N;;;;; +285E;BRAILLE PATTERN DOTS-23457;So;0;ON;;;;;N;;;;; +285F;BRAILLE PATTERN DOTS-123457;So;0;ON;;;;;N;;;;; +2860;BRAILLE PATTERN DOTS-67;So;0;ON;;;;;N;;;;; +2861;BRAILLE PATTERN DOTS-167;So;0;ON;;;;;N;;;;; +2862;BRAILLE PATTERN DOTS-267;So;0;ON;;;;;N;;;;; +2863;BRAILLE PATTERN DOTS-1267;So;0;ON;;;;;N;;;;; +2864;BRAILLE PATTERN DOTS-367;So;0;ON;;;;;N;;;;; +2865;BRAILLE PATTERN DOTS-1367;So;0;ON;;;;;N;;;;; +2866;BRAILLE PATTERN DOTS-2367;So;0;ON;;;;;N;;;;; +2867;BRAILLE PATTERN DOTS-12367;So;0;ON;;;;;N;;;;; +2868;BRAILLE PATTERN DOTS-467;So;0;ON;;;;;N;;;;; +2869;BRAILLE PATTERN DOTS-1467;So;0;ON;;;;;N;;;;; +286A;BRAILLE PATTERN DOTS-2467;So;0;ON;;;;;N;;;;; +286B;BRAILLE PATTERN DOTS-12467;So;0;ON;;;;;N;;;;; +286C;BRAILLE PATTERN DOTS-3467;So;0;ON;;;;;N;;;;; +286D;BRAILLE PATTERN DOTS-13467;So;0;ON;;;;;N;;;;; +286E;BRAILLE PATTERN DOTS-23467;So;0;ON;;;;;N;;;;; +286F;BRAILLE PATTERN DOTS-123467;So;0;ON;;;;;N;;;;; +2870;BRAILLE PATTERN DOTS-567;So;0;ON;;;;;N;;;;; +2871;BRAILLE PATTERN DOTS-1567;So;0;ON;;;;;N;;;;; +2872;BRAILLE PATTERN DOTS-2567;So;0;ON;;;;;N;;;;; +2873;BRAILLE PATTERN DOTS-12567;So;0;ON;;;;;N;;;;; +2874;BRAILLE PATTERN DOTS-3567;So;0;ON;;;;;N;;;;; +2875;BRAILLE PATTERN DOTS-13567;So;0;ON;;;;;N;;;;; +2876;BRAILLE PATTERN DOTS-23567;So;0;ON;;;;;N;;;;; +2877;BRAILLE PATTERN DOTS-123567;So;0;ON;;;;;N;;;;; +2878;BRAILLE PATTERN DOTS-4567;So;0;ON;;;;;N;;;;; +2879;BRAILLE PATTERN DOTS-14567;So;0;ON;;;;;N;;;;; +287A;BRAILLE PATTERN DOTS-24567;So;0;ON;;;;;N;;;;; +287B;BRAILLE PATTERN DOTS-124567;So;0;ON;;;;;N;;;;; +287C;BRAILLE PATTERN DOTS-34567;So;0;ON;;;;;N;;;;; +287D;BRAILLE PATTERN DOTS-134567;So;0;ON;;;;;N;;;;; +287E;BRAILLE PATTERN DOTS-234567;So;0;ON;;;;;N;;;;; +287F;BRAILLE PATTERN DOTS-1234567;So;0;ON;;;;;N;;;;; +2880;BRAILLE PATTERN DOTS-8;So;0;ON;;;;;N;;;;; +2881;BRAILLE PATTERN DOTS-18;So;0;ON;;;;;N;;;;; +2882;BRAILLE PATTERN DOTS-28;So;0;ON;;;;;N;;;;; +2883;BRAILLE PATTERN DOTS-128;So;0;ON;;;;;N;;;;; +2884;BRAILLE PATTERN DOTS-38;So;0;ON;;;;;N;;;;; +2885;BRAILLE PATTERN DOTS-138;So;0;ON;;;;;N;;;;; +2886;BRAILLE PATTERN DOTS-238;So;0;ON;;;;;N;;;;; +2887;BRAILLE PATTERN DOTS-1238;So;0;ON;;;;;N;;;;; +2888;BRAILLE PATTERN DOTS-48;So;0;ON;;;;;N;;;;; +2889;BRAILLE PATTERN DOTS-148;So;0;ON;;;;;N;;;;; +288A;BRAILLE PATTERN DOTS-248;So;0;ON;;;;;N;;;;; +288B;BRAILLE PATTERN DOTS-1248;So;0;ON;;;;;N;;;;; +288C;BRAILLE PATTERN DOTS-348;So;0;ON;;;;;N;;;;; +288D;BRAILLE PATTERN DOTS-1348;So;0;ON;;;;;N;;;;; +288E;BRAILLE PATTERN DOTS-2348;So;0;ON;;;;;N;;;;; +288F;BRAILLE PATTERN DOTS-12348;So;0;ON;;;;;N;;;;; +2890;BRAILLE PATTERN DOTS-58;So;0;ON;;;;;N;;;;; +2891;BRAILLE PATTERN DOTS-158;So;0;ON;;;;;N;;;;; +2892;BRAILLE PATTERN DOTS-258;So;0;ON;;;;;N;;;;; +2893;BRAILLE PATTERN DOTS-1258;So;0;ON;;;;;N;;;;; +2894;BRAILLE PATTERN DOTS-358;So;0;ON;;;;;N;;;;; +2895;BRAILLE PATTERN DOTS-1358;So;0;ON;;;;;N;;;;; +2896;BRAILLE PATTERN DOTS-2358;So;0;ON;;;;;N;;;;; +2897;BRAILLE PATTERN DOTS-12358;So;0;ON;;;;;N;;;;; +2898;BRAILLE PATTERN DOTS-458;So;0;ON;;;;;N;;;;; +2899;BRAILLE PATTERN DOTS-1458;So;0;ON;;;;;N;;;;; +289A;BRAILLE PATTERN DOTS-2458;So;0;ON;;;;;N;;;;; +289B;BRAILLE PATTERN DOTS-12458;So;0;ON;;;;;N;;;;; +289C;BRAILLE PATTERN DOTS-3458;So;0;ON;;;;;N;;;;; +289D;BRAILLE PATTERN DOTS-13458;So;0;ON;;;;;N;;;;; +289E;BRAILLE PATTERN DOTS-23458;So;0;ON;;;;;N;;;;; +289F;BRAILLE PATTERN DOTS-123458;So;0;ON;;;;;N;;;;; +28A0;BRAILLE PATTERN DOTS-68;So;0;ON;;;;;N;;;;; +28A1;BRAILLE PATTERN DOTS-168;So;0;ON;;;;;N;;;;; +28A2;BRAILLE PATTERN DOTS-268;So;0;ON;;;;;N;;;;; +28A3;BRAILLE PATTERN DOTS-1268;So;0;ON;;;;;N;;;;; +28A4;BRAILLE PATTERN DOTS-368;So;0;ON;;;;;N;;;;; +28A5;BRAILLE PATTERN DOTS-1368;So;0;ON;;;;;N;;;;; +28A6;BRAILLE PATTERN DOTS-2368;So;0;ON;;;;;N;;;;; +28A7;BRAILLE PATTERN DOTS-12368;So;0;ON;;;;;N;;;;; +28A8;BRAILLE PATTERN DOTS-468;So;0;ON;;;;;N;;;;; +28A9;BRAILLE PATTERN DOTS-1468;So;0;ON;;;;;N;;;;; +28AA;BRAILLE PATTERN DOTS-2468;So;0;ON;;;;;N;;;;; +28AB;BRAILLE PATTERN DOTS-12468;So;0;ON;;;;;N;;;;; +28AC;BRAILLE PATTERN DOTS-3468;So;0;ON;;;;;N;;;;; +28AD;BRAILLE PATTERN DOTS-13468;So;0;ON;;;;;N;;;;; +28AE;BRAILLE PATTERN DOTS-23468;So;0;ON;;;;;N;;;;; +28AF;BRAILLE PATTERN DOTS-123468;So;0;ON;;;;;N;;;;; +28B0;BRAILLE PATTERN DOTS-568;So;0;ON;;;;;N;;;;; +28B1;BRAILLE PATTERN DOTS-1568;So;0;ON;;;;;N;;;;; +28B2;BRAILLE PATTERN DOTS-2568;So;0;ON;;;;;N;;;;; +28B3;BRAILLE PATTERN DOTS-12568;So;0;ON;;;;;N;;;;; +28B4;BRAILLE PATTERN DOTS-3568;So;0;ON;;;;;N;;;;; +28B5;BRAILLE PATTERN DOTS-13568;So;0;ON;;;;;N;;;;; +28B6;BRAILLE PATTERN DOTS-23568;So;0;ON;;;;;N;;;;; +28B7;BRAILLE PATTERN DOTS-123568;So;0;ON;;;;;N;;;;; +28B8;BRAILLE PATTERN DOTS-4568;So;0;ON;;;;;N;;;;; +28B9;BRAILLE PATTERN DOTS-14568;So;0;ON;;;;;N;;;;; +28BA;BRAILLE PATTERN DOTS-24568;So;0;ON;;;;;N;;;;; +28BB;BRAILLE PATTERN DOTS-124568;So;0;ON;;;;;N;;;;; +28BC;BRAILLE PATTERN DOTS-34568;So;0;ON;;;;;N;;;;; +28BD;BRAILLE PATTERN DOTS-134568;So;0;ON;;;;;N;;;;; +28BE;BRAILLE PATTERN DOTS-234568;So;0;ON;;;;;N;;;;; +28BF;BRAILLE PATTERN DOTS-1234568;So;0;ON;;;;;N;;;;; +28C0;BRAILLE PATTERN DOTS-78;So;0;ON;;;;;N;;;;; +28C1;BRAILLE PATTERN DOTS-178;So;0;ON;;;;;N;;;;; +28C2;BRAILLE PATTERN DOTS-278;So;0;ON;;;;;N;;;;; +28C3;BRAILLE PATTERN DOTS-1278;So;0;ON;;;;;N;;;;; +28C4;BRAILLE PATTERN DOTS-378;So;0;ON;;;;;N;;;;; +28C5;BRAILLE PATTERN DOTS-1378;So;0;ON;;;;;N;;;;; +28C6;BRAILLE PATTERN DOTS-2378;So;0;ON;;;;;N;;;;; +28C7;BRAILLE PATTERN DOTS-12378;So;0;ON;;;;;N;;;;; +28C8;BRAILLE PATTERN DOTS-478;So;0;ON;;;;;N;;;;; +28C9;BRAILLE PATTERN DOTS-1478;So;0;ON;;;;;N;;;;; +28CA;BRAILLE PATTERN DOTS-2478;So;0;ON;;;;;N;;;;; +28CB;BRAILLE PATTERN DOTS-12478;So;0;ON;;;;;N;;;;; +28CC;BRAILLE PATTERN DOTS-3478;So;0;ON;;;;;N;;;;; +28CD;BRAILLE PATTERN DOTS-13478;So;0;ON;;;;;N;;;;; +28CE;BRAILLE PATTERN DOTS-23478;So;0;ON;;;;;N;;;;; +28CF;BRAILLE PATTERN DOTS-123478;So;0;ON;;;;;N;;;;; +28D0;BRAILLE PATTERN DOTS-578;So;0;ON;;;;;N;;;;; +28D1;BRAILLE PATTERN DOTS-1578;So;0;ON;;;;;N;;;;; +28D2;BRAILLE PATTERN DOTS-2578;So;0;ON;;;;;N;;;;; +28D3;BRAILLE PATTERN DOTS-12578;So;0;ON;;;;;N;;;;; +28D4;BRAILLE PATTERN DOTS-3578;So;0;ON;;;;;N;;;;; +28D5;BRAILLE PATTERN DOTS-13578;So;0;ON;;;;;N;;;;; +28D6;BRAILLE PATTERN DOTS-23578;So;0;ON;;;;;N;;;;; +28D7;BRAILLE PATTERN DOTS-123578;So;0;ON;;;;;N;;;;; +28D8;BRAILLE PATTERN DOTS-4578;So;0;ON;;;;;N;;;;; +28D9;BRAILLE PATTERN DOTS-14578;So;0;ON;;;;;N;;;;; +28DA;BRAILLE PATTERN DOTS-24578;So;0;ON;;;;;N;;;;; +28DB;BRAILLE PATTERN DOTS-124578;So;0;ON;;;;;N;;;;; +28DC;BRAILLE PATTERN DOTS-34578;So;0;ON;;;;;N;;;;; +28DD;BRAILLE PATTERN DOTS-134578;So;0;ON;;;;;N;;;;; +28DE;BRAILLE PATTERN DOTS-234578;So;0;ON;;;;;N;;;;; +28DF;BRAILLE PATTERN DOTS-1234578;So;0;ON;;;;;N;;;;; +28E0;BRAILLE PATTERN DOTS-678;So;0;ON;;;;;N;;;;; +28E1;BRAILLE PATTERN DOTS-1678;So;0;ON;;;;;N;;;;; +28E2;BRAILLE PATTERN DOTS-2678;So;0;ON;;;;;N;;;;; +28E3;BRAILLE PATTERN DOTS-12678;So;0;ON;;;;;N;;;;; +28E4;BRAILLE PATTERN DOTS-3678;So;0;ON;;;;;N;;;;; +28E5;BRAILLE PATTERN DOTS-13678;So;0;ON;;;;;N;;;;; +28E6;BRAILLE PATTERN DOTS-23678;So;0;ON;;;;;N;;;;; +28E7;BRAILLE PATTERN DOTS-123678;So;0;ON;;;;;N;;;;; +28E8;BRAILLE PATTERN DOTS-4678;So;0;ON;;;;;N;;;;; +28E9;BRAILLE PATTERN DOTS-14678;So;0;ON;;;;;N;;;;; +28EA;BRAILLE PATTERN DOTS-24678;So;0;ON;;;;;N;;;;; +28EB;BRAILLE PATTERN DOTS-124678;So;0;ON;;;;;N;;;;; +28EC;BRAILLE PATTERN DOTS-34678;So;0;ON;;;;;N;;;;; +28ED;BRAILLE PATTERN DOTS-134678;So;0;ON;;;;;N;;;;; +28EE;BRAILLE PATTERN DOTS-234678;So;0;ON;;;;;N;;;;; +28EF;BRAILLE PATTERN DOTS-1234678;So;0;ON;;;;;N;;;;; +28F0;BRAILLE PATTERN DOTS-5678;So;0;ON;;;;;N;;;;; +28F1;BRAILLE PATTERN DOTS-15678;So;0;ON;;;;;N;;;;; +28F2;BRAILLE PATTERN DOTS-25678;So;0;ON;;;;;N;;;;; +28F3;BRAILLE PATTERN DOTS-125678;So;0;ON;;;;;N;;;;; +28F4;BRAILLE PATTERN DOTS-35678;So;0;ON;;;;;N;;;;; +28F5;BRAILLE PATTERN DOTS-135678;So;0;ON;;;;;N;;;;; +28F6;BRAILLE PATTERN DOTS-235678;So;0;ON;;;;;N;;;;; +28F7;BRAILLE PATTERN DOTS-1235678;So;0;ON;;;;;N;;;;; +28F8;BRAILLE PATTERN DOTS-45678;So;0;ON;;;;;N;;;;; +28F9;BRAILLE PATTERN DOTS-145678;So;0;ON;;;;;N;;;;; +28FA;BRAILLE PATTERN DOTS-245678;So;0;ON;;;;;N;;;;; +28FB;BRAILLE PATTERN DOTS-1245678;So;0;ON;;;;;N;;;;; +28FC;BRAILLE PATTERN DOTS-345678;So;0;ON;;;;;N;;;;; +28FD;BRAILLE PATTERN DOTS-1345678;So;0;ON;;;;;N;;;;; +28FE;BRAILLE PATTERN DOTS-2345678;So;0;ON;;;;;N;;;;; +28FF;BRAILLE PATTERN DOTS-12345678;So;0;ON;;;;;N;;;;; +2900;RIGHTWARDS TWO-HEADED ARROW WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; +2901;RIGHTWARDS TWO-HEADED ARROW WITH DOUBLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; +2902;LEFTWARDS DOUBLE ARROW WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; +2903;RIGHTWARDS DOUBLE ARROW WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; +2904;LEFT RIGHT DOUBLE ARROW WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; +2905;RIGHTWARDS TWO-HEADED ARROW FROM BAR;Sm;0;ON;;;;;N;;;;; +2906;LEFTWARDS DOUBLE ARROW FROM BAR;Sm;0;ON;;;;;N;;;;; +2907;RIGHTWARDS DOUBLE ARROW FROM BAR;Sm;0;ON;;;;;N;;;;; +2908;DOWNWARDS ARROW WITH HORIZONTAL STROKE;Sm;0;ON;;;;;N;;;;; +2909;UPWARDS ARROW WITH HORIZONTAL STROKE;Sm;0;ON;;;;;N;;;;; +290A;UPWARDS TRIPLE ARROW;Sm;0;ON;;;;;N;;;;; +290B;DOWNWARDS TRIPLE ARROW;Sm;0;ON;;;;;N;;;;; +290C;LEFTWARDS DOUBLE DASH ARROW;Sm;0;ON;;;;;N;;;;; +290D;RIGHTWARDS DOUBLE DASH ARROW;Sm;0;ON;;;;;N;;;;; +290E;LEFTWARDS TRIPLE DASH ARROW;Sm;0;ON;;;;;N;;;;; +290F;RIGHTWARDS TRIPLE DASH ARROW;Sm;0;ON;;;;;N;;;;; +2910;RIGHTWARDS TWO-HEADED TRIPLE DASH ARROW;Sm;0;ON;;;;;N;;;;; +2911;RIGHTWARDS ARROW WITH DOTTED STEM;Sm;0;ON;;;;;N;;;;; +2912;UPWARDS ARROW TO BAR;Sm;0;ON;;;;;N;;;;; +2913;DOWNWARDS ARROW TO BAR;Sm;0;ON;;;;;N;;;;; +2914;RIGHTWARDS ARROW WITH TAIL WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; +2915;RIGHTWARDS ARROW WITH TAIL WITH DOUBLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; +2916;RIGHTWARDS TWO-HEADED ARROW WITH TAIL;Sm;0;ON;;;;;N;;;;; +2917;RIGHTWARDS TWO-HEADED ARROW WITH TAIL WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; +2918;RIGHTWARDS TWO-HEADED ARROW WITH TAIL WITH DOUBLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; +2919;LEFTWARDS ARROW-TAIL;Sm;0;ON;;;;;N;;;;; +291A;RIGHTWARDS ARROW-TAIL;Sm;0;ON;;;;;N;;;;; +291B;LEFTWARDS DOUBLE ARROW-TAIL;Sm;0;ON;;;;;N;;;;; +291C;RIGHTWARDS DOUBLE ARROW-TAIL;Sm;0;ON;;;;;N;;;;; +291D;LEFTWARDS ARROW TO BLACK DIAMOND;Sm;0;ON;;;;;N;;;;; +291E;RIGHTWARDS ARROW TO BLACK DIAMOND;Sm;0;ON;;;;;N;;;;; +291F;LEFTWARDS ARROW FROM BAR TO BLACK DIAMOND;Sm;0;ON;;;;;N;;;;; +2920;RIGHTWARDS ARROW FROM BAR TO BLACK DIAMOND;Sm;0;ON;;;;;N;;;;; +2921;NORTH WEST AND SOUTH EAST ARROW;Sm;0;ON;;;;;N;;;;; +2922;NORTH EAST AND SOUTH WEST ARROW;Sm;0;ON;;;;;N;;;;; +2923;NORTH WEST ARROW WITH HOOK;Sm;0;ON;;;;;N;;;;; +2924;NORTH EAST ARROW WITH HOOK;Sm;0;ON;;;;;N;;;;; +2925;SOUTH EAST ARROW WITH HOOK;Sm;0;ON;;;;;N;;;;; +2926;SOUTH WEST ARROW WITH HOOK;Sm;0;ON;;;;;N;;;;; +2927;NORTH WEST ARROW AND NORTH EAST ARROW;Sm;0;ON;;;;;N;;;;; +2928;NORTH EAST ARROW AND SOUTH EAST ARROW;Sm;0;ON;;;;;N;;;;; +2929;SOUTH EAST ARROW AND SOUTH WEST ARROW;Sm;0;ON;;;;;N;;;;; +292A;SOUTH WEST ARROW AND NORTH WEST ARROW;Sm;0;ON;;;;;N;;;;; +292B;RISING DIAGONAL CROSSING FALLING DIAGONAL;Sm;0;ON;;;;;N;;;;; +292C;FALLING DIAGONAL CROSSING RISING DIAGONAL;Sm;0;ON;;;;;N;;;;; +292D;SOUTH EAST ARROW CROSSING NORTH EAST ARROW;Sm;0;ON;;;;;N;;;;; +292E;NORTH EAST ARROW CROSSING SOUTH EAST ARROW;Sm;0;ON;;;;;N;;;;; +292F;FALLING DIAGONAL CROSSING NORTH EAST ARROW;Sm;0;ON;;;;;N;;;;; +2930;RISING DIAGONAL CROSSING SOUTH EAST ARROW;Sm;0;ON;;;;;N;;;;; +2931;NORTH EAST ARROW CROSSING NORTH WEST ARROW;Sm;0;ON;;;;;N;;;;; +2932;NORTH WEST ARROW CROSSING NORTH EAST ARROW;Sm;0;ON;;;;;N;;;;; +2933;WAVE ARROW POINTING DIRECTLY RIGHT;Sm;0;ON;;;;;N;;;;; +2934;ARROW POINTING RIGHTWARDS THEN CURVING UPWARDS;Sm;0;ON;;;;;N;;;;; +2935;ARROW POINTING RIGHTWARDS THEN CURVING DOWNWARDS;Sm;0;ON;;;;;N;;;;; +2936;ARROW POINTING DOWNWARDS THEN CURVING LEFTWARDS;Sm;0;ON;;;;;N;;;;; +2937;ARROW POINTING DOWNWARDS THEN CURVING RIGHTWARDS;Sm;0;ON;;;;;N;;;;; +2938;RIGHT-SIDE ARC CLOCKWISE ARROW;Sm;0;ON;;;;;N;;;;; +2939;LEFT-SIDE ARC ANTICLOCKWISE ARROW;Sm;0;ON;;;;;N;;;;; +293A;TOP ARC ANTICLOCKWISE ARROW;Sm;0;ON;;;;;N;;;;; +293B;BOTTOM ARC ANTICLOCKWISE ARROW;Sm;0;ON;;;;;N;;;;; +293C;TOP ARC CLOCKWISE ARROW WITH MINUS;Sm;0;ON;;;;;N;;;;; +293D;TOP ARC ANTICLOCKWISE ARROW WITH PLUS;Sm;0;ON;;;;;N;;;;; +293E;LOWER RIGHT SEMICIRCULAR CLOCKWISE ARROW;Sm;0;ON;;;;;N;;;;; +293F;LOWER LEFT SEMICIRCULAR ANTICLOCKWISE ARROW;Sm;0;ON;;;;;N;;;;; +2940;ANTICLOCKWISE CLOSED CIRCLE ARROW;Sm;0;ON;;;;;N;;;;; +2941;CLOCKWISE CLOSED CIRCLE ARROW;Sm;0;ON;;;;;N;;;;; +2942;RIGHTWARDS ARROW ABOVE SHORT LEFTWARDS ARROW;Sm;0;ON;;;;;N;;;;; +2943;LEFTWARDS ARROW ABOVE SHORT RIGHTWARDS ARROW;Sm;0;ON;;;;;N;;;;; +2944;SHORT RIGHTWARDS ARROW ABOVE LEFTWARDS ARROW;Sm;0;ON;;;;;N;;;;; +2945;RIGHTWARDS ARROW WITH PLUS BELOW;Sm;0;ON;;;;;N;;;;; +2946;LEFTWARDS ARROW WITH PLUS BELOW;Sm;0;ON;;;;;N;;;;; +2947;RIGHTWARDS ARROW THROUGH X;Sm;0;ON;;;;;N;;;;; +2948;LEFT RIGHT ARROW THROUGH SMALL CIRCLE;Sm;0;ON;;;;;N;;;;; +2949;UPWARDS TWO-HEADED ARROW FROM SMALL CIRCLE;Sm;0;ON;;;;;N;;;;; +294A;LEFT BARB UP RIGHT BARB DOWN HARPOON;Sm;0;ON;;;;;N;;;;; +294B;LEFT BARB DOWN RIGHT BARB UP HARPOON;Sm;0;ON;;;;;N;;;;; +294C;UP BARB RIGHT DOWN BARB LEFT HARPOON;Sm;0;ON;;;;;N;;;;; +294D;UP BARB LEFT DOWN BARB RIGHT HARPOON;Sm;0;ON;;;;;N;;;;; +294E;LEFT BARB UP RIGHT BARB UP HARPOON;Sm;0;ON;;;;;N;;;;; +294F;UP BARB RIGHT DOWN BARB RIGHT HARPOON;Sm;0;ON;;;;;N;;;;; +2950;LEFT BARB DOWN RIGHT BARB DOWN HARPOON;Sm;0;ON;;;;;N;;;;; +2951;UP BARB LEFT DOWN BARB LEFT HARPOON;Sm;0;ON;;;;;N;;;;; +2952;LEFTWARDS HARPOON WITH BARB UP TO BAR;Sm;0;ON;;;;;N;;;;; +2953;RIGHTWARDS HARPOON WITH BARB UP TO BAR;Sm;0;ON;;;;;N;;;;; +2954;UPWARDS HARPOON WITH BARB RIGHT TO BAR;Sm;0;ON;;;;;N;;;;; +2955;DOWNWARDS HARPOON WITH BARB RIGHT TO BAR;Sm;0;ON;;;;;N;;;;; +2956;LEFTWARDS HARPOON WITH BARB DOWN TO BAR;Sm;0;ON;;;;;N;;;;; +2957;RIGHTWARDS HARPOON WITH BARB DOWN TO BAR;Sm;0;ON;;;;;N;;;;; +2958;UPWARDS HARPOON WITH BARB LEFT TO BAR;Sm;0;ON;;;;;N;;;;; +2959;DOWNWARDS HARPOON WITH BARB LEFT TO BAR;Sm;0;ON;;;;;N;;;;; +295A;LEFTWARDS HARPOON WITH BARB UP FROM BAR;Sm;0;ON;;;;;N;;;;; +295B;RIGHTWARDS HARPOON WITH BARB UP FROM BAR;Sm;0;ON;;;;;N;;;;; +295C;UPWARDS HARPOON WITH BARB RIGHT FROM BAR;Sm;0;ON;;;;;N;;;;; +295D;DOWNWARDS HARPOON WITH BARB RIGHT FROM BAR;Sm;0;ON;;;;;N;;;;; +295E;LEFTWARDS HARPOON WITH BARB DOWN FROM BAR;Sm;0;ON;;;;;N;;;;; +295F;RIGHTWARDS HARPOON WITH BARB DOWN FROM BAR;Sm;0;ON;;;;;N;;;;; +2960;UPWARDS HARPOON WITH BARB LEFT FROM BAR;Sm;0;ON;;;;;N;;;;; +2961;DOWNWARDS HARPOON WITH BARB LEFT FROM BAR;Sm;0;ON;;;;;N;;;;; +2962;LEFTWARDS HARPOON WITH BARB UP ABOVE LEFTWARDS HARPOON WITH BARB DOWN;Sm;0;ON;;;;;N;;;;; +2963;UPWARDS HARPOON WITH BARB LEFT BESIDE UPWARDS HARPOON WITH BARB RIGHT;Sm;0;ON;;;;;N;;;;; +2964;RIGHTWARDS HARPOON WITH BARB UP ABOVE RIGHTWARDS HARPOON WITH BARB DOWN;Sm;0;ON;;;;;N;;;;; +2965;DOWNWARDS HARPOON WITH BARB LEFT BESIDE DOWNWARDS HARPOON WITH BARB RIGHT;Sm;0;ON;;;;;N;;;;; +2966;LEFTWARDS HARPOON WITH BARB UP ABOVE RIGHTWARDS HARPOON WITH BARB UP;Sm;0;ON;;;;;N;;;;; +2967;LEFTWARDS HARPOON WITH BARB DOWN ABOVE RIGHTWARDS HARPOON WITH BARB DOWN;Sm;0;ON;;;;;N;;;;; +2968;RIGHTWARDS HARPOON WITH BARB UP ABOVE LEFTWARDS HARPOON WITH BARB UP;Sm;0;ON;;;;;N;;;;; +2969;RIGHTWARDS HARPOON WITH BARB DOWN ABOVE LEFTWARDS HARPOON WITH BARB DOWN;Sm;0;ON;;;;;N;;;;; +296A;LEFTWARDS HARPOON WITH BARB UP ABOVE LONG DASH;Sm;0;ON;;;;;N;;;;; +296B;LEFTWARDS HARPOON WITH BARB DOWN BELOW LONG DASH;Sm;0;ON;;;;;N;;;;; +296C;RIGHTWARDS HARPOON WITH BARB UP ABOVE LONG DASH;Sm;0;ON;;;;;N;;;;; +296D;RIGHTWARDS HARPOON WITH BARB DOWN BELOW LONG DASH;Sm;0;ON;;;;;N;;;;; +296E;UPWARDS HARPOON WITH BARB LEFT BESIDE DOWNWARDS HARPOON WITH BARB RIGHT;Sm;0;ON;;;;;N;;;;; +296F;DOWNWARDS HARPOON WITH BARB LEFT BESIDE UPWARDS HARPOON WITH BARB RIGHT;Sm;0;ON;;;;;N;;;;; +2970;RIGHT DOUBLE ARROW WITH ROUNDED HEAD;Sm;0;ON;;;;;N;;;;; +2971;EQUALS SIGN ABOVE RIGHTWARDS ARROW;Sm;0;ON;;;;;N;;;;; +2972;TILDE OPERATOR ABOVE RIGHTWARDS ARROW;Sm;0;ON;;;;;N;;;;; +2973;LEFTWARDS ARROW ABOVE TILDE OPERATOR;Sm;0;ON;;;;;N;;;;; +2974;RIGHTWARDS ARROW ABOVE TILDE OPERATOR;Sm;0;ON;;;;;N;;;;; +2975;RIGHTWARDS ARROW ABOVE ALMOST EQUAL TO;Sm;0;ON;;;;;N;;;;; +2976;LESS-THAN ABOVE LEFTWARDS ARROW;Sm;0;ON;;;;;N;;;;; +2977;LEFTWARDS ARROW THROUGH LESS-THAN;Sm;0;ON;;;;;N;;;;; +2978;GREATER-THAN ABOVE RIGHTWARDS ARROW;Sm;0;ON;;;;;N;;;;; +2979;SUBSET ABOVE RIGHTWARDS ARROW;Sm;0;ON;;;;;N;;;;; +297A;LEFTWARDS ARROW THROUGH SUBSET;Sm;0;ON;;;;;N;;;;; +297B;SUPERSET ABOVE LEFTWARDS ARROW;Sm;0;ON;;;;;N;;;;; +297C;LEFT FISH TAIL;Sm;0;ON;;;;;N;;;;; +297D;RIGHT FISH TAIL;Sm;0;ON;;;;;N;;;;; +297E;UP FISH TAIL;Sm;0;ON;;;;;N;;;;; +297F;DOWN FISH TAIL;Sm;0;ON;;;;;N;;;;; +2980;TRIPLE VERTICAL BAR DELIMITER;Sm;0;ON;;;;;N;;;;; +2981;Z NOTATION SPOT;Sm;0;ON;;;;;N;;;;; +2982;Z NOTATION TYPE COLON;Sm;0;ON;;;;;N;;;;; +2983;LEFT WHITE CURLY BRACKET;Ps;0;ON;;;;;Y;;;;; +2984;RIGHT WHITE CURLY BRACKET;Pe;0;ON;;;;;Y;;;;; +2985;LEFT WHITE PARENTHESIS;Ps;0;ON;;;;;Y;;;;; +2986;RIGHT WHITE PARENTHESIS;Pe;0;ON;;;;;Y;;;;; +2987;Z NOTATION LEFT IMAGE BRACKET;Ps;0;ON;;;;;Y;;;;; +2988;Z NOTATION RIGHT IMAGE BRACKET;Pe;0;ON;;;;;Y;;;;; +2989;Z NOTATION LEFT BINDING BRACKET;Ps;0;ON;;;;;Y;;;;; +298A;Z NOTATION RIGHT BINDING BRACKET;Pe;0;ON;;;;;Y;;;;; +298B;LEFT SQUARE BRACKET WITH UNDERBAR;Ps;0;ON;;;;;Y;;;;; +298C;RIGHT SQUARE BRACKET WITH UNDERBAR;Pe;0;ON;;;;;Y;;;;; +298D;LEFT SQUARE BRACKET WITH TICK IN TOP CORNER;Ps;0;ON;;;;;Y;;;;; +298E;RIGHT SQUARE BRACKET WITH TICK IN BOTTOM CORNER;Pe;0;ON;;;;;Y;;;;; +298F;LEFT SQUARE BRACKET WITH TICK IN BOTTOM CORNER;Ps;0;ON;;;;;Y;;;;; +2990;RIGHT SQUARE BRACKET WITH TICK IN TOP CORNER;Pe;0;ON;;;;;Y;;;;; +2991;LEFT ANGLE BRACKET WITH DOT;Ps;0;ON;;;;;Y;;;;; +2992;RIGHT ANGLE BRACKET WITH DOT;Pe;0;ON;;;;;Y;;;;; +2993;LEFT ARC LESS-THAN BRACKET;Ps;0;ON;;;;;Y;;;;; +2994;RIGHT ARC GREATER-THAN BRACKET;Pe;0;ON;;;;;Y;;;;; +2995;DOUBLE LEFT ARC GREATER-THAN BRACKET;Ps;0;ON;;;;;Y;;;;; +2996;DOUBLE RIGHT ARC LESS-THAN BRACKET;Pe;0;ON;;;;;Y;;;;; +2997;LEFT BLACK TORTOISE SHELL BRACKET;Ps;0;ON;;;;;Y;;;;; +2998;RIGHT BLACK TORTOISE SHELL BRACKET;Pe;0;ON;;;;;Y;;;;; +2999;DOTTED FENCE;Sm;0;ON;;;;;N;;;;; +299A;VERTICAL ZIGZAG LINE;Sm;0;ON;;;;;N;;;;; +299B;MEASURED ANGLE OPENING LEFT;Sm;0;ON;;;;;Y;;;;; +299C;RIGHT ANGLE VARIANT WITH SQUARE;Sm;0;ON;;;;;Y;;;;; +299D;MEASURED RIGHT ANGLE WITH DOT;Sm;0;ON;;;;;Y;;;;; +299E;ANGLE WITH S INSIDE;Sm;0;ON;;;;;Y;;;;; +299F;ACUTE ANGLE;Sm;0;ON;;;;;Y;;;;; +29A0;SPHERICAL ANGLE OPENING LEFT;Sm;0;ON;;;;;Y;;;;; +29A1;SPHERICAL ANGLE OPENING UP;Sm;0;ON;;;;;Y;;;;; +29A2;TURNED ANGLE;Sm;0;ON;;;;;Y;;;;; +29A3;REVERSED ANGLE;Sm;0;ON;;;;;Y;;;;; +29A4;ANGLE WITH UNDERBAR;Sm;0;ON;;;;;Y;;;;; +29A5;REVERSED ANGLE WITH UNDERBAR;Sm;0;ON;;;;;Y;;;;; +29A6;OBLIQUE ANGLE OPENING UP;Sm;0;ON;;;;;Y;;;;; +29A7;OBLIQUE ANGLE OPENING DOWN;Sm;0;ON;;;;;Y;;;;; +29A8;MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING UP AND RIGHT;Sm;0;ON;;;;;Y;;;;; +29A9;MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING UP AND LEFT;Sm;0;ON;;;;;Y;;;;; +29AA;MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING DOWN AND RIGHT;Sm;0;ON;;;;;Y;;;;; +29AB;MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING DOWN AND LEFT;Sm;0;ON;;;;;Y;;;;; +29AC;MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING RIGHT AND UP;Sm;0;ON;;;;;Y;;;;; +29AD;MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING LEFT AND UP;Sm;0;ON;;;;;Y;;;;; +29AE;MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING RIGHT AND DOWN;Sm;0;ON;;;;;Y;;;;; +29AF;MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING LEFT AND DOWN;Sm;0;ON;;;;;Y;;;;; +29B0;REVERSED EMPTY SET;Sm;0;ON;;;;;N;;;;; +29B1;EMPTY SET WITH OVERBAR;Sm;0;ON;;;;;N;;;;; +29B2;EMPTY SET WITH SMALL CIRCLE ABOVE;Sm;0;ON;;;;;N;;;;; +29B3;EMPTY SET WITH RIGHT ARROW ABOVE;Sm;0;ON;;;;;N;;;;; +29B4;EMPTY SET WITH LEFT ARROW ABOVE;Sm;0;ON;;;;;N;;;;; +29B5;CIRCLE WITH HORIZONTAL BAR;Sm;0;ON;;;;;N;;;;; +29B6;CIRCLED VERTICAL BAR;Sm;0;ON;;;;;N;;;;; +29B7;CIRCLED PARALLEL;Sm;0;ON;;;;;N;;;;; +29B8;CIRCLED REVERSE SOLIDUS;Sm;0;ON;;;;;Y;;;;; +29B9;CIRCLED PERPENDICULAR;Sm;0;ON;;;;;N;;;;; +29BA;CIRCLE DIVIDED BY HORIZONTAL BAR AND TOP HALF DIVIDED BY VERTICAL BAR;Sm;0;ON;;;;;N;;;;; +29BB;CIRCLE WITH SUPERIMPOSED X;Sm;0;ON;;;;;N;;;;; +29BC;CIRCLED ANTICLOCKWISE-ROTATED DIVISION SIGN;Sm;0;ON;;;;;N;;;;; +29BD;UP ARROW THROUGH CIRCLE;Sm;0;ON;;;;;N;;;;; +29BE;CIRCLED WHITE BULLET;Sm;0;ON;;;;;N;;;;; +29BF;CIRCLED BULLET;Sm;0;ON;;;;;N;;;;; +29C0;CIRCLED LESS-THAN;Sm;0;ON;;;;;Y;;;;; +29C1;CIRCLED GREATER-THAN;Sm;0;ON;;;;;Y;;;;; +29C2;CIRCLE WITH SMALL CIRCLE TO THE RIGHT;Sm;0;ON;;;;;Y;;;;; +29C3;CIRCLE WITH TWO HORIZONTAL STROKES TO THE RIGHT;Sm;0;ON;;;;;Y;;;;; +29C4;SQUARED RISING DIAGONAL SLASH;Sm;0;ON;;;;;Y;;;;; +29C5;SQUARED FALLING DIAGONAL SLASH;Sm;0;ON;;;;;Y;;;;; +29C6;SQUARED ASTERISK;Sm;0;ON;;;;;N;;;;; +29C7;SQUARED SMALL CIRCLE;Sm;0;ON;;;;;N;;;;; +29C8;SQUARED SQUARE;Sm;0;ON;;;;;N;;;;; +29C9;TWO JOINED SQUARES;Sm;0;ON;;;;;Y;;;;; +29CA;TRIANGLE WITH DOT ABOVE;Sm;0;ON;;;;;N;;;;; +29CB;TRIANGLE WITH UNDERBAR;Sm;0;ON;;;;;N;;;;; +29CC;S IN TRIANGLE;Sm;0;ON;;;;;N;;;;; +29CD;TRIANGLE WITH SERIFS AT BOTTOM;Sm;0;ON;;;;;N;;;;; +29CE;RIGHT TRIANGLE ABOVE LEFT TRIANGLE;Sm;0;ON;;;;;Y;;;;; +29CF;LEFT TRIANGLE BESIDE VERTICAL BAR;Sm;0;ON;;;;;Y;;;;; +29D0;VERTICAL BAR BESIDE RIGHT TRIANGLE;Sm;0;ON;;;;;Y;;;;; +29D1;BOWTIE WITH LEFT HALF BLACK;Sm;0;ON;;;;;Y;;;;; +29D2;BOWTIE WITH RIGHT HALF BLACK;Sm;0;ON;;;;;Y;;;;; +29D3;BLACK BOWTIE;Sm;0;ON;;;;;N;;;;; +29D4;TIMES WITH LEFT HALF BLACK;Sm;0;ON;;;;;Y;;;;; +29D5;TIMES WITH RIGHT HALF BLACK;Sm;0;ON;;;;;Y;;;;; +29D6;WHITE HOURGLASS;Sm;0;ON;;;;;N;;;;; +29D7;BLACK HOURGLASS;Sm;0;ON;;;;;N;;;;; +29D8;LEFT WIGGLY FENCE;Ps;0;ON;;;;;Y;;;;; +29D9;RIGHT WIGGLY FENCE;Pe;0;ON;;;;;Y;;;;; +29DA;LEFT DOUBLE WIGGLY FENCE;Ps;0;ON;;;;;Y;;;;; +29DB;RIGHT DOUBLE WIGGLY FENCE;Pe;0;ON;;;;;Y;;;;; +29DC;INCOMPLETE INFINITY;Sm;0;ON;;;;;Y;;;;; +29DD;TIE OVER INFINITY;Sm;0;ON;;;;;N;;;;; +29DE;INFINITY NEGATED WITH VERTICAL BAR;Sm;0;ON;;;;;N;;;;; +29DF;DOUBLE-ENDED MULTIMAP;Sm;0;ON;;;;;N;;;;; +29E0;SQUARE WITH CONTOURED OUTLINE;Sm;0;ON;;;;;N;;;;; +29E1;INCREASES AS;Sm;0;ON;;;;;Y;;;;; +29E2;SHUFFLE PRODUCT;Sm;0;ON;;;;;N;;;;; +29E3;EQUALS SIGN AND SLANTED PARALLEL;Sm;0;ON;;;;;Y;;;;; +29E4;EQUALS SIGN AND SLANTED PARALLEL WITH TILDE ABOVE;Sm;0;ON;;;;;Y;;;;; +29E5;IDENTICAL TO AND SLANTED PARALLEL;Sm;0;ON;;;;;Y;;;;; +29E6;GLEICH STARK;Sm;0;ON;;;;;N;;;;; +29E7;THERMODYNAMIC;Sm;0;ON;;;;;N;;;;; +29E8;DOWN-POINTING TRIANGLE WITH LEFT HALF BLACK;Sm;0;ON;;;;;Y;;;;; +29E9;DOWN-POINTING TRIANGLE WITH RIGHT HALF BLACK;Sm;0;ON;;;;;Y;;;;; +29EA;BLACK DIAMOND WITH DOWN ARROW;Sm;0;ON;;;;;N;;;;; +29EB;BLACK LOZENGE;Sm;0;ON;;;;;N;;;;; +29EC;WHITE CIRCLE WITH DOWN ARROW;Sm;0;ON;;;;;N;;;;; +29ED;BLACK CIRCLE WITH DOWN ARROW;Sm;0;ON;;;;;N;;;;; +29EE;ERROR-BARRED WHITE SQUARE;Sm;0;ON;;;;;N;;;;; +29EF;ERROR-BARRED BLACK SQUARE;Sm;0;ON;;;;;N;;;;; +29F0;ERROR-BARRED WHITE DIAMOND;Sm;0;ON;;;;;N;;;;; +29F1;ERROR-BARRED BLACK DIAMOND;Sm;0;ON;;;;;N;;;;; +29F2;ERROR-BARRED WHITE CIRCLE;Sm;0;ON;;;;;N;;;;; +29F3;ERROR-BARRED BLACK CIRCLE;Sm;0;ON;;;;;N;;;;; +29F4;RULE-DELAYED;Sm;0;ON;;;;;Y;;;;; +29F5;REVERSE SOLIDUS OPERATOR;Sm;0;ON;;;;;Y;;;;; +29F6;SOLIDUS WITH OVERBAR;Sm;0;ON;;;;;Y;;;;; +29F7;REVERSE SOLIDUS WITH HORIZONTAL STROKE;Sm;0;ON;;;;;Y;;;;; +29F8;BIG SOLIDUS;Sm;0;ON;;;;;Y;;;;; +29F9;BIG REVERSE SOLIDUS;Sm;0;ON;;;;;Y;;;;; +29FA;DOUBLE PLUS;Sm;0;ON;;;;;N;;;;; +29FB;TRIPLE PLUS;Sm;0;ON;;;;;N;;;;; +29FC;LEFT-POINTING CURVED ANGLE BRACKET;Ps;0;ON;;;;;Y;;;;; +29FD;RIGHT-POINTING CURVED ANGLE BRACKET;Pe;0;ON;;;;;Y;;;;; +29FE;TINY;Sm;0;ON;;;;;N;;;;; +29FF;MINY;Sm;0;ON;;;;;N;;;;; +2A00;N-ARY CIRCLED DOT OPERATOR;Sm;0;ON;;;;;N;;;;; +2A01;N-ARY CIRCLED PLUS OPERATOR;Sm;0;ON;;;;;N;;;;; +2A02;N-ARY CIRCLED TIMES OPERATOR;Sm;0;ON;;;;;N;;;;; +2A03;N-ARY UNION OPERATOR WITH DOT;Sm;0;ON;;;;;N;;;;; +2A04;N-ARY UNION OPERATOR WITH PLUS;Sm;0;ON;;;;;N;;;;; +2A05;N-ARY SQUARE INTERSECTION OPERATOR;Sm;0;ON;;;;;N;;;;; +2A06;N-ARY SQUARE UNION OPERATOR;Sm;0;ON;;;;;N;;;;; +2A07;TWO LOGICAL AND OPERATOR;Sm;0;ON;;;;;N;;;;; +2A08;TWO LOGICAL OR OPERATOR;Sm;0;ON;;;;;N;;;;; +2A09;N-ARY TIMES OPERATOR;Sm;0;ON;;;;;N;;;;; +2A0A;MODULO TWO SUM;Sm;0;ON;;;;;Y;;;;; +2A0B;SUMMATION WITH INTEGRAL;Sm;0;ON;;;;;Y;;;;; +2A0C;QUADRUPLE INTEGRAL OPERATOR;Sm;0;ON;<compat> 222B 222B 222B 222B;;;;Y;;;;; +2A0D;FINITE PART INTEGRAL;Sm;0;ON;;;;;Y;;;;; +2A0E;INTEGRAL WITH DOUBLE STROKE;Sm;0;ON;;;;;Y;;;;; +2A0F;INTEGRAL AVERAGE WITH SLASH;Sm;0;ON;;;;;Y;;;;; +2A10;CIRCULATION FUNCTION;Sm;0;ON;;;;;Y;;;;; +2A11;ANTICLOCKWISE INTEGRATION;Sm;0;ON;;;;;Y;;;;; +2A12;LINE INTEGRATION WITH RECTANGULAR PATH AROUND POLE;Sm;0;ON;;;;;Y;;;;; +2A13;LINE INTEGRATION WITH SEMICIRCULAR PATH AROUND POLE;Sm;0;ON;;;;;Y;;;;; +2A14;LINE INTEGRATION NOT INCLUDING THE POLE;Sm;0;ON;;;;;Y;;;;; +2A15;INTEGRAL AROUND A POINT OPERATOR;Sm;0;ON;;;;;Y;;;;; +2A16;QUATERNION INTEGRAL OPERATOR;Sm;0;ON;;;;;Y;;;;; +2A17;INTEGRAL WITH LEFTWARDS ARROW WITH HOOK;Sm;0;ON;;;;;Y;;;;; +2A18;INTEGRAL WITH TIMES SIGN;Sm;0;ON;;;;;Y;;;;; +2A19;INTEGRAL WITH INTERSECTION;Sm;0;ON;;;;;Y;;;;; +2A1A;INTEGRAL WITH UNION;Sm;0;ON;;;;;Y;;;;; +2A1B;INTEGRAL WITH OVERBAR;Sm;0;ON;;;;;Y;;;;; +2A1C;INTEGRAL WITH UNDERBAR;Sm;0;ON;;;;;Y;;;;; +2A1D;JOIN;Sm;0;ON;;;;;N;;;;; +2A1E;LARGE LEFT TRIANGLE OPERATOR;Sm;0;ON;;;;;Y;;;;; +2A1F;Z NOTATION SCHEMA COMPOSITION;Sm;0;ON;;;;;Y;;;;; +2A20;Z NOTATION SCHEMA PIPING;Sm;0;ON;;;;;Y;;;;; +2A21;Z NOTATION SCHEMA PROJECTION;Sm;0;ON;;;;;Y;;;;; +2A22;PLUS SIGN WITH SMALL CIRCLE ABOVE;Sm;0;ON;;;;;N;;;;; +2A23;PLUS SIGN WITH CIRCUMFLEX ACCENT ABOVE;Sm;0;ON;;;;;N;;;;; +2A24;PLUS SIGN WITH TILDE ABOVE;Sm;0;ON;;;;;Y;;;;; +2A25;PLUS SIGN WITH DOT BELOW;Sm;0;ON;;;;;N;;;;; +2A26;PLUS SIGN WITH TILDE BELOW;Sm;0;ON;;;;;Y;;;;; +2A27;PLUS SIGN WITH SUBSCRIPT TWO;Sm;0;ON;;;;;N;;;;; +2A28;PLUS SIGN WITH BLACK TRIANGLE;Sm;0;ON;;;;;N;;;;; +2A29;MINUS SIGN WITH COMMA ABOVE;Sm;0;ON;;;;;Y;;;;; +2A2A;MINUS SIGN WITH DOT BELOW;Sm;0;ON;;;;;N;;;;; +2A2B;MINUS SIGN WITH FALLING DOTS;Sm;0;ON;;;;;Y;;;;; +2A2C;MINUS SIGN WITH RISING DOTS;Sm;0;ON;;;;;Y;;;;; +2A2D;PLUS SIGN IN LEFT HALF CIRCLE;Sm;0;ON;;;;;Y;;;;; +2A2E;PLUS SIGN IN RIGHT HALF CIRCLE;Sm;0;ON;;;;;Y;;;;; +2A2F;VECTOR OR CROSS PRODUCT;Sm;0;ON;;;;;N;;;;; +2A30;MULTIPLICATION SIGN WITH DOT ABOVE;Sm;0;ON;;;;;N;;;;; +2A31;MULTIPLICATION SIGN WITH UNDERBAR;Sm;0;ON;;;;;N;;;;; +2A32;SEMIDIRECT PRODUCT WITH BOTTOM CLOSED;Sm;0;ON;;;;;N;;;;; +2A33;SMASH PRODUCT;Sm;0;ON;;;;;N;;;;; +2A34;MULTIPLICATION SIGN IN LEFT HALF CIRCLE;Sm;0;ON;;;;;Y;;;;; +2A35;MULTIPLICATION SIGN IN RIGHT HALF CIRCLE;Sm;0;ON;;;;;Y;;;;; +2A36;CIRCLED MULTIPLICATION SIGN WITH CIRCUMFLEX ACCENT;Sm;0;ON;;;;;N;;;;; +2A37;MULTIPLICATION SIGN IN DOUBLE CIRCLE;Sm;0;ON;;;;;N;;;;; +2A38;CIRCLED DIVISION SIGN;Sm;0;ON;;;;;N;;;;; +2A39;PLUS SIGN IN TRIANGLE;Sm;0;ON;;;;;N;;;;; +2A3A;MINUS SIGN IN TRIANGLE;Sm;0;ON;;;;;N;;;;; +2A3B;MULTIPLICATION SIGN IN TRIANGLE;Sm;0;ON;;;;;N;;;;; +2A3C;INTERIOR PRODUCT;Sm;0;ON;;;;;Y;;;;; +2A3D;RIGHTHAND INTERIOR PRODUCT;Sm;0;ON;;;;;Y;;;;; +2A3E;Z NOTATION RELATIONAL COMPOSITION;Sm;0;ON;;;;;Y;;;;; +2A3F;AMALGAMATION OR COPRODUCT;Sm;0;ON;;;;;N;;;;; +2A40;INTERSECTION WITH DOT;Sm;0;ON;;;;;N;;;;; +2A41;UNION WITH MINUS SIGN;Sm;0;ON;;;;;N;;;;; +2A42;UNION WITH OVERBAR;Sm;0;ON;;;;;N;;;;; +2A43;INTERSECTION WITH OVERBAR;Sm;0;ON;;;;;N;;;;; +2A44;INTERSECTION WITH LOGICAL AND;Sm;0;ON;;;;;N;;;;; +2A45;UNION WITH LOGICAL OR;Sm;0;ON;;;;;N;;;;; +2A46;UNION ABOVE INTERSECTION;Sm;0;ON;;;;;N;;;;; +2A47;INTERSECTION ABOVE UNION;Sm;0;ON;;;;;N;;;;; +2A48;UNION ABOVE BAR ABOVE INTERSECTION;Sm;0;ON;;;;;N;;;;; +2A49;INTERSECTION ABOVE BAR ABOVE UNION;Sm;0;ON;;;;;N;;;;; +2A4A;UNION BESIDE AND JOINED WITH UNION;Sm;0;ON;;;;;N;;;;; +2A4B;INTERSECTION BESIDE AND JOINED WITH INTERSECTION;Sm;0;ON;;;;;N;;;;; +2A4C;CLOSED UNION WITH SERIFS;Sm;0;ON;;;;;N;;;;; +2A4D;CLOSED INTERSECTION WITH SERIFS;Sm;0;ON;;;;;N;;;;; +2A4E;DOUBLE SQUARE INTERSECTION;Sm;0;ON;;;;;N;;;;; +2A4F;DOUBLE SQUARE UNION;Sm;0;ON;;;;;N;;;;; +2A50;CLOSED UNION WITH SERIFS AND SMASH PRODUCT;Sm;0;ON;;;;;N;;;;; +2A51;LOGICAL AND WITH DOT ABOVE;Sm;0;ON;;;;;N;;;;; +2A52;LOGICAL OR WITH DOT ABOVE;Sm;0;ON;;;;;N;;;;; +2A53;DOUBLE LOGICAL AND;Sm;0;ON;;;;;N;;;;; +2A54;DOUBLE LOGICAL OR;Sm;0;ON;;;;;N;;;;; +2A55;TWO INTERSECTING LOGICAL AND;Sm;0;ON;;;;;N;;;;; +2A56;TWO INTERSECTING LOGICAL OR;Sm;0;ON;;;;;N;;;;; +2A57;SLOPING LARGE OR;Sm;0;ON;;;;;Y;;;;; +2A58;SLOPING LARGE AND;Sm;0;ON;;;;;Y;;;;; +2A59;LOGICAL OR OVERLAPPING LOGICAL AND;Sm;0;ON;;;;;N;;;;; +2A5A;LOGICAL AND WITH MIDDLE STEM;Sm;0;ON;;;;;N;;;;; +2A5B;LOGICAL OR WITH MIDDLE STEM;Sm;0;ON;;;;;N;;;;; +2A5C;LOGICAL AND WITH HORIZONTAL DASH;Sm;0;ON;;;;;N;;;;; +2A5D;LOGICAL OR WITH HORIZONTAL DASH;Sm;0;ON;;;;;N;;;;; +2A5E;LOGICAL AND WITH DOUBLE OVERBAR;Sm;0;ON;;;;;N;;;;; +2A5F;LOGICAL AND WITH UNDERBAR;Sm;0;ON;;;;;N;;;;; +2A60;LOGICAL AND WITH DOUBLE UNDERBAR;Sm;0;ON;;;;;N;;;;; +2A61;SMALL VEE WITH UNDERBAR;Sm;0;ON;;;;;N;;;;; +2A62;LOGICAL OR WITH DOUBLE OVERBAR;Sm;0;ON;;;;;N;;;;; +2A63;LOGICAL OR WITH DOUBLE UNDERBAR;Sm;0;ON;;;;;N;;;;; +2A64;Z NOTATION DOMAIN ANTIRESTRICTION;Sm;0;ON;;;;;Y;;;;; +2A65;Z NOTATION RANGE ANTIRESTRICTION;Sm;0;ON;;;;;Y;;;;; +2A66;EQUALS SIGN WITH DOT BELOW;Sm;0;ON;;;;;N;;;;; +2A67;IDENTICAL WITH DOT ABOVE;Sm;0;ON;;;;;N;;;;; +2A68;TRIPLE HORIZONTAL BAR WITH DOUBLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; +2A69;TRIPLE HORIZONTAL BAR WITH TRIPLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;; +2A6A;TILDE OPERATOR WITH DOT ABOVE;Sm;0;ON;;;;;Y;;;;; +2A6B;TILDE OPERATOR WITH RISING DOTS;Sm;0;ON;;;;;Y;;;;; +2A6C;SIMILAR MINUS SIMILAR;Sm;0;ON;;;;;Y;;;;; +2A6D;CONGRUENT WITH DOT ABOVE;Sm;0;ON;;;;;Y;;;;; +2A6E;EQUALS WITH ASTERISK;Sm;0;ON;;;;;N;;;;; +2A6F;ALMOST EQUAL TO WITH CIRCUMFLEX ACCENT;Sm;0;ON;;;;;Y;;;;; +2A70;APPROXIMATELY EQUAL OR EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2A71;EQUALS SIGN ABOVE PLUS SIGN;Sm;0;ON;;;;;N;;;;; +2A72;PLUS SIGN ABOVE EQUALS SIGN;Sm;0;ON;;;;;N;;;;; +2A73;EQUALS SIGN ABOVE TILDE OPERATOR;Sm;0;ON;;;;;Y;;;;; +2A74;DOUBLE COLON EQUAL;Sm;0;ON;<compat> 003A 003A 003D;;;;Y;;;;; +2A75;TWO CONSECUTIVE EQUALS SIGNS;Sm;0;ON;<compat> 003D 003D;;;;N;;;;; +2A76;THREE CONSECUTIVE EQUALS SIGNS;Sm;0;ON;<compat> 003D 003D 003D;;;;N;;;;; +2A77;EQUALS SIGN WITH TWO DOTS ABOVE AND TWO DOTS BELOW;Sm;0;ON;;;;;N;;;;; +2A78;EQUIVALENT WITH FOUR DOTS ABOVE;Sm;0;ON;;;;;N;;;;; +2A79;LESS-THAN WITH CIRCLE INSIDE;Sm;0;ON;;;;;Y;;;;; +2A7A;GREATER-THAN WITH CIRCLE INSIDE;Sm;0;ON;;;;;Y;;;;; +2A7B;LESS-THAN WITH QUESTION MARK ABOVE;Sm;0;ON;;;;;Y;;;;; +2A7C;GREATER-THAN WITH QUESTION MARK ABOVE;Sm;0;ON;;;;;Y;;;;; +2A7D;LESS-THAN OR SLANTED EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2A7E;GREATER-THAN OR SLANTED EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2A7F;LESS-THAN OR SLANTED EQUAL TO WITH DOT INSIDE;Sm;0;ON;;;;;Y;;;;; +2A80;GREATER-THAN OR SLANTED EQUAL TO WITH DOT INSIDE;Sm;0;ON;;;;;Y;;;;; +2A81;LESS-THAN OR SLANTED EQUAL TO WITH DOT ABOVE;Sm;0;ON;;;;;Y;;;;; +2A82;GREATER-THAN OR SLANTED EQUAL TO WITH DOT ABOVE;Sm;0;ON;;;;;Y;;;;; +2A83;LESS-THAN OR SLANTED EQUAL TO WITH DOT ABOVE RIGHT;Sm;0;ON;;;;;Y;;;;; +2A84;GREATER-THAN OR SLANTED EQUAL TO WITH DOT ABOVE LEFT;Sm;0;ON;;;;;Y;;;;; +2A85;LESS-THAN OR APPROXIMATE;Sm;0;ON;;;;;Y;;;;; +2A86;GREATER-THAN OR APPROXIMATE;Sm;0;ON;;;;;Y;;;;; +2A87;LESS-THAN AND SINGLE-LINE NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2A88;GREATER-THAN AND SINGLE-LINE NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2A89;LESS-THAN AND NOT APPROXIMATE;Sm;0;ON;;;;;Y;;;;; +2A8A;GREATER-THAN AND NOT APPROXIMATE;Sm;0;ON;;;;;Y;;;;; +2A8B;LESS-THAN ABOVE DOUBLE-LINE EQUAL ABOVE GREATER-THAN;Sm;0;ON;;;;;Y;;;;; +2A8C;GREATER-THAN ABOVE DOUBLE-LINE EQUAL ABOVE LESS-THAN;Sm;0;ON;;;;;Y;;;;; +2A8D;LESS-THAN ABOVE SIMILAR OR EQUAL;Sm;0;ON;;;;;Y;;;;; +2A8E;GREATER-THAN ABOVE SIMILAR OR EQUAL;Sm;0;ON;;;;;Y;;;;; +2A8F;LESS-THAN ABOVE SIMILAR ABOVE GREATER-THAN;Sm;0;ON;;;;;Y;;;;; +2A90;GREATER-THAN ABOVE SIMILAR ABOVE LESS-THAN;Sm;0;ON;;;;;Y;;;;; +2A91;LESS-THAN ABOVE GREATER-THAN ABOVE DOUBLE-LINE EQUAL;Sm;0;ON;;;;;Y;;;;; +2A92;GREATER-THAN ABOVE LESS-THAN ABOVE DOUBLE-LINE EQUAL;Sm;0;ON;;;;;Y;;;;; +2A93;LESS-THAN ABOVE SLANTED EQUAL ABOVE GREATER-THAN ABOVE SLANTED EQUAL;Sm;0;ON;;;;;Y;;;;; +2A94;GREATER-THAN ABOVE SLANTED EQUAL ABOVE LESS-THAN ABOVE SLANTED EQUAL;Sm;0;ON;;;;;Y;;;;; +2A95;SLANTED EQUAL TO OR LESS-THAN;Sm;0;ON;;;;;Y;;;;; +2A96;SLANTED EQUAL TO OR GREATER-THAN;Sm;0;ON;;;;;Y;;;;; +2A97;SLANTED EQUAL TO OR LESS-THAN WITH DOT INSIDE;Sm;0;ON;;;;;Y;;;;; +2A98;SLANTED EQUAL TO OR GREATER-THAN WITH DOT INSIDE;Sm;0;ON;;;;;Y;;;;; +2A99;DOUBLE-LINE EQUAL TO OR LESS-THAN;Sm;0;ON;;;;;Y;;;;; +2A9A;DOUBLE-LINE EQUAL TO OR GREATER-THAN;Sm;0;ON;;;;;Y;;;;; +2A9B;DOUBLE-LINE SLANTED EQUAL TO OR LESS-THAN;Sm;0;ON;;;;;Y;;;;; +2A9C;DOUBLE-LINE SLANTED EQUAL TO OR GREATER-THAN;Sm;0;ON;;;;;Y;;;;; +2A9D;SIMILAR OR LESS-THAN;Sm;0;ON;;;;;Y;;;;; +2A9E;SIMILAR OR GREATER-THAN;Sm;0;ON;;;;;Y;;;;; +2A9F;SIMILAR ABOVE LESS-THAN ABOVE EQUALS SIGN;Sm;0;ON;;;;;Y;;;;; +2AA0;SIMILAR ABOVE GREATER-THAN ABOVE EQUALS SIGN;Sm;0;ON;;;;;Y;;;;; +2AA1;DOUBLE NESTED LESS-THAN;Sm;0;ON;;;;;Y;;;;; +2AA2;DOUBLE NESTED GREATER-THAN;Sm;0;ON;;;;;Y;;;;; +2AA3;DOUBLE NESTED LESS-THAN WITH UNDERBAR;Sm;0;ON;;;;;Y;;;;; +2AA4;GREATER-THAN OVERLAPPING LESS-THAN;Sm;0;ON;;;;;N;;;;; +2AA5;GREATER-THAN BESIDE LESS-THAN;Sm;0;ON;;;;;N;;;;; +2AA6;LESS-THAN CLOSED BY CURVE;Sm;0;ON;;;;;Y;;;;; +2AA7;GREATER-THAN CLOSED BY CURVE;Sm;0;ON;;;;;Y;;;;; +2AA8;LESS-THAN CLOSED BY CURVE ABOVE SLANTED EQUAL;Sm;0;ON;;;;;Y;;;;; +2AA9;GREATER-THAN CLOSED BY CURVE ABOVE SLANTED EQUAL;Sm;0;ON;;;;;Y;;;;; +2AAA;SMALLER THAN;Sm;0;ON;;;;;Y;;;;; +2AAB;LARGER THAN;Sm;0;ON;;;;;Y;;;;; +2AAC;SMALLER THAN OR EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2AAD;LARGER THAN OR EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2AAE;EQUALS SIGN WITH BUMPY ABOVE;Sm;0;ON;;;;;N;;;;; +2AAF;PRECEDES ABOVE SINGLE-LINE EQUALS SIGN;Sm;0;ON;;;;;Y;;;;; +2AB0;SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN;Sm;0;ON;;;;;Y;;;;; +2AB1;PRECEDES ABOVE SINGLE-LINE NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2AB2;SUCCEEDS ABOVE SINGLE-LINE NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2AB3;PRECEDES ABOVE EQUALS SIGN;Sm;0;ON;;;;;Y;;;;; +2AB4;SUCCEEDS ABOVE EQUALS SIGN;Sm;0;ON;;;;;Y;;;;; +2AB5;PRECEDES ABOVE NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2AB6;SUCCEEDS ABOVE NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2AB7;PRECEDES ABOVE ALMOST EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2AB8;SUCCEEDS ABOVE ALMOST EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2AB9;PRECEDES ABOVE NOT ALMOST EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2ABA;SUCCEEDS ABOVE NOT ALMOST EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2ABB;DOUBLE PRECEDES;Sm;0;ON;;;;;Y;;;;; +2ABC;DOUBLE SUCCEEDS;Sm;0;ON;;;;;Y;;;;; +2ABD;SUBSET WITH DOT;Sm;0;ON;;;;;Y;;;;; +2ABE;SUPERSET WITH DOT;Sm;0;ON;;;;;Y;;;;; +2ABF;SUBSET WITH PLUS SIGN BELOW;Sm;0;ON;;;;;Y;;;;; +2AC0;SUPERSET WITH PLUS SIGN BELOW;Sm;0;ON;;;;;Y;;;;; +2AC1;SUBSET WITH MULTIPLICATION SIGN BELOW;Sm;0;ON;;;;;Y;;;;; +2AC2;SUPERSET WITH MULTIPLICATION SIGN BELOW;Sm;0;ON;;;;;Y;;;;; +2AC3;SUBSET OF OR EQUAL TO WITH DOT ABOVE;Sm;0;ON;;;;;Y;;;;; +2AC4;SUPERSET OF OR EQUAL TO WITH DOT ABOVE;Sm;0;ON;;;;;Y;;;;; +2AC5;SUBSET OF ABOVE EQUALS SIGN;Sm;0;ON;;;;;Y;;;;; +2AC6;SUPERSET OF ABOVE EQUALS SIGN;Sm;0;ON;;;;;Y;;;;; +2AC7;SUBSET OF ABOVE TILDE OPERATOR;Sm;0;ON;;;;;Y;;;;; +2AC8;SUPERSET OF ABOVE TILDE OPERATOR;Sm;0;ON;;;;;Y;;;;; +2AC9;SUBSET OF ABOVE ALMOST EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2ACA;SUPERSET OF ABOVE ALMOST EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2ACB;SUBSET OF ABOVE NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2ACC;SUPERSET OF ABOVE NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2ACD;SQUARE LEFT OPEN BOX OPERATOR;Sm;0;ON;;;;;Y;;;;; +2ACE;SQUARE RIGHT OPEN BOX OPERATOR;Sm;0;ON;;;;;Y;;;;; +2ACF;CLOSED SUBSET;Sm;0;ON;;;;;Y;;;;; +2AD0;CLOSED SUPERSET;Sm;0;ON;;;;;Y;;;;; +2AD1;CLOSED SUBSET OR EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2AD2;CLOSED SUPERSET OR EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2AD3;SUBSET ABOVE SUPERSET;Sm;0;ON;;;;;Y;;;;; +2AD4;SUPERSET ABOVE SUBSET;Sm;0;ON;;;;;Y;;;;; +2AD5;SUBSET ABOVE SUBSET;Sm;0;ON;;;;;Y;;;;; +2AD6;SUPERSET ABOVE SUPERSET;Sm;0;ON;;;;;Y;;;;; +2AD7;SUPERSET BESIDE SUBSET;Sm;0;ON;;;;;N;;;;; +2AD8;SUPERSET BESIDE AND JOINED BY DASH WITH SUBSET;Sm;0;ON;;;;;N;;;;; +2AD9;ELEMENT OF OPENING DOWNWARDS;Sm;0;ON;;;;;N;;;;; +2ADA;PITCHFORK WITH TEE TOP;Sm;0;ON;;;;;N;;;;; +2ADB;TRANSVERSAL INTERSECTION;Sm;0;ON;;;;;N;;;;; +2ADC;FORKING;Sm;0;ON;2ADD 0338;;;;Y;;not independent;;; +2ADD;NONFORKING;Sm;0;ON;;;;;N;;independent;;; +2ADE;SHORT LEFT TACK;Sm;0;ON;;;;;Y;;;;; +2ADF;SHORT DOWN TACK;Sm;0;ON;;;;;N;;;;; +2AE0;SHORT UP TACK;Sm;0;ON;;;;;N;;;;; +2AE1;PERPENDICULAR WITH S;Sm;0;ON;;;;;N;;;;; +2AE2;VERTICAL BAR TRIPLE RIGHT TURNSTILE;Sm;0;ON;;;;;Y;;;;; +2AE3;DOUBLE VERTICAL BAR LEFT TURNSTILE;Sm;0;ON;;;;;Y;;;;; +2AE4;VERTICAL BAR DOUBLE LEFT TURNSTILE;Sm;0;ON;;;;;Y;;;;; +2AE5;DOUBLE VERTICAL BAR DOUBLE LEFT TURNSTILE;Sm;0;ON;;;;;Y;;;;; +2AE6;LONG DASH FROM LEFT MEMBER OF DOUBLE VERTICAL;Sm;0;ON;;;;;Y;;;;; +2AE7;SHORT DOWN TACK WITH OVERBAR;Sm;0;ON;;;;;N;;;;; +2AE8;SHORT UP TACK WITH UNDERBAR;Sm;0;ON;;;;;N;;;;; +2AE9;SHORT UP TACK ABOVE SHORT DOWN TACK;Sm;0;ON;;;;;N;;;;; +2AEA;DOUBLE DOWN TACK;Sm;0;ON;;;;;N;;;;; +2AEB;DOUBLE UP TACK;Sm;0;ON;;;;;N;;;;; +2AEC;DOUBLE STROKE NOT SIGN;Sm;0;ON;;;;;Y;;;;; +2AED;REVERSED DOUBLE STROKE NOT SIGN;Sm;0;ON;;;;;Y;;;;; +2AEE;DOES NOT DIVIDE WITH REVERSED NEGATION SLASH;Sm;0;ON;;;;;Y;;;;; +2AEF;VERTICAL LINE WITH CIRCLE ABOVE;Sm;0;ON;;;;;N;;;;; +2AF0;VERTICAL LINE WITH CIRCLE BELOW;Sm;0;ON;;;;;N;;;;; +2AF1;DOWN TACK WITH CIRCLE BELOW;Sm;0;ON;;;;;N;;;;; +2AF2;PARALLEL WITH HORIZONTAL STROKE;Sm;0;ON;;;;;N;;;;; +2AF3;PARALLEL WITH TILDE OPERATOR;Sm;0;ON;;;;;Y;;;;; +2AF4;TRIPLE VERTICAL BAR BINARY RELATION;Sm;0;ON;;;;;N;;;;; +2AF5;TRIPLE VERTICAL BAR WITH HORIZONTAL STROKE;Sm;0;ON;;;;;N;;;;; +2AF6;TRIPLE COLON OPERATOR;Sm;0;ON;;;;;N;;;;; +2AF7;TRIPLE NESTED LESS-THAN;Sm;0;ON;;;;;Y;;;;; +2AF8;TRIPLE NESTED GREATER-THAN;Sm;0;ON;;;;;Y;;;;; +2AF9;DOUBLE-LINE SLANTED LESS-THAN OR EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2AFA;DOUBLE-LINE SLANTED GREATER-THAN OR EQUAL TO;Sm;0;ON;;;;;Y;;;;; +2AFB;TRIPLE SOLIDUS BINARY RELATION;Sm;0;ON;;;;;Y;;;;; +2AFC;LARGE TRIPLE VERTICAL BAR OPERATOR;Sm;0;ON;;;;;N;;;;; +2AFD;DOUBLE SOLIDUS OPERATOR;Sm;0;ON;;;;;Y;;;;; +2AFE;WHITE VERTICAL BAR;Sm;0;ON;;;;;N;;;;; +2AFF;N-ARY WHITE VERTICAL BAR;Sm;0;ON;;;;;N;;;;; +2E80;CJK RADICAL REPEAT;So;0;ON;;;;;N;;;;; +2E81;CJK RADICAL CLIFF;So;0;ON;;;;;N;;;;; +2E82;CJK RADICAL SECOND ONE;So;0;ON;;;;;N;;;;; +2E83;CJK RADICAL SECOND TWO;So;0;ON;;;;;N;;;;; +2E84;CJK RADICAL SECOND THREE;So;0;ON;;;;;N;;;;; +2E85;CJK RADICAL PERSON;So;0;ON;;;;;N;;;;; +2E86;CJK RADICAL BOX;So;0;ON;;;;;N;;;;; +2E87;CJK RADICAL TABLE;So;0;ON;;;;;N;;;;; +2E88;CJK RADICAL KNIFE ONE;So;0;ON;;;;;N;;;;; +2E89;CJK RADICAL KNIFE TWO;So;0;ON;;;;;N;;;;; +2E8A;CJK RADICAL DIVINATION;So;0;ON;;;;;N;;;;; +2E8B;CJK RADICAL SEAL;So;0;ON;;;;;N;;;;; +2E8C;CJK RADICAL SMALL ONE;So;0;ON;;;;;N;;;;; +2E8D;CJK RADICAL SMALL TWO;So;0;ON;;;;;N;;;;; +2E8E;CJK RADICAL LAME ONE;So;0;ON;;;;;N;;;;; +2E8F;CJK RADICAL LAME TWO;So;0;ON;;;;;N;;;;; +2E90;CJK RADICAL LAME THREE;So;0;ON;;;;;N;;;;; +2E91;CJK RADICAL LAME FOUR;So;0;ON;;;;;N;;;;; +2E92;CJK RADICAL SNAKE;So;0;ON;;;;;N;;;;; +2E93;CJK RADICAL THREAD;So;0;ON;;;;;N;;;;; +2E94;CJK RADICAL SNOUT ONE;So;0;ON;;;;;N;;;;; +2E95;CJK RADICAL SNOUT TWO;So;0;ON;;;;;N;;;;; +2E96;CJK RADICAL HEART ONE;So;0;ON;;;;;N;;;;; +2E97;CJK RADICAL HEART TWO;So;0;ON;;;;;N;;;;; +2E98;CJK RADICAL HAND;So;0;ON;;;;;N;;;;; +2E99;CJK RADICAL RAP;So;0;ON;;;;;N;;;;; +2E9B;CJK RADICAL CHOKE;So;0;ON;;;;;N;;;;; +2E9C;CJK RADICAL SUN;So;0;ON;;;;;N;;;;; +2E9D;CJK RADICAL MOON;So;0;ON;;;;;N;;;;; +2E9E;CJK RADICAL DEATH;So;0;ON;;;;;N;;;;; +2E9F;CJK RADICAL MOTHER;So;0;ON;<compat> 6BCD;;;;N;;;;; +2EA0;CJK RADICAL CIVILIAN;So;0;ON;;;;;N;;;;; +2EA1;CJK RADICAL WATER ONE;So;0;ON;;;;;N;;;;; +2EA2;CJK RADICAL WATER TWO;So;0;ON;;;;;N;;;;; +2EA3;CJK RADICAL FIRE;So;0;ON;;;;;N;;;;; +2EA4;CJK RADICAL PAW ONE;So;0;ON;;;;;N;;;;; +2EA5;CJK RADICAL PAW TWO;So;0;ON;;;;;N;;;;; +2EA6;CJK RADICAL SIMPLIFIED HALF TREE TRUNK;So;0;ON;;;;;N;;;;; +2EA7;CJK RADICAL COW;So;0;ON;;;;;N;;;;; +2EA8;CJK RADICAL DOG;So;0;ON;;;;;N;;;;; +2EA9;CJK RADICAL JADE;So;0;ON;;;;;N;;;;; +2EAA;CJK RADICAL BOLT OF CLOTH;So;0;ON;;;;;N;;;;; +2EAB;CJK RADICAL EYE;So;0;ON;;;;;N;;;;; +2EAC;CJK RADICAL SPIRIT ONE;So;0;ON;;;;;N;;;;; +2EAD;CJK RADICAL SPIRIT TWO;So;0;ON;;;;;N;;;;; +2EAE;CJK RADICAL BAMBOO;So;0;ON;;;;;N;;;;; +2EAF;CJK RADICAL SILK;So;0;ON;;;;;N;;;;; +2EB0;CJK RADICAL C-SIMPLIFIED SILK;So;0;ON;;;;;N;;;;; +2EB1;CJK RADICAL NET ONE;So;0;ON;;;;;N;;;;; +2EB2;CJK RADICAL NET TWO;So;0;ON;;;;;N;;;;; +2EB3;CJK RADICAL NET THREE;So;0;ON;;;;;N;;;;; +2EB4;CJK RADICAL NET FOUR;So;0;ON;;;;;N;;;;; +2EB5;CJK RADICAL MESH;So;0;ON;;;;;N;;;;; +2EB6;CJK RADICAL SHEEP;So;0;ON;;;;;N;;;;; +2EB7;CJK RADICAL RAM;So;0;ON;;;;;N;;;;; +2EB8;CJK RADICAL EWE;So;0;ON;;;;;N;;;;; +2EB9;CJK RADICAL OLD;So;0;ON;;;;;N;;;;; +2EBA;CJK RADICAL BRUSH ONE;So;0;ON;;;;;N;;;;; +2EBB;CJK RADICAL BRUSH TWO;So;0;ON;;;;;N;;;;; +2EBC;CJK RADICAL MEAT;So;0;ON;;;;;N;;;;; +2EBD;CJK RADICAL MORTAR;So;0;ON;;;;;N;;;;; +2EBE;CJK RADICAL GRASS ONE;So;0;ON;;;;;N;;;;; +2EBF;CJK RADICAL GRASS TWO;So;0;ON;;;;;N;;;;; +2EC0;CJK RADICAL GRASS THREE;So;0;ON;;;;;N;;;;; +2EC1;CJK RADICAL TIGER;So;0;ON;;;;;N;;;;; +2EC2;CJK RADICAL CLOTHES;So;0;ON;;;;;N;;;;; +2EC3;CJK RADICAL WEST ONE;So;0;ON;;;;;N;;;;; +2EC4;CJK RADICAL WEST TWO;So;0;ON;;;;;N;;;;; +2EC5;CJK RADICAL C-SIMPLIFIED SEE;So;0;ON;;;;;N;;;;; +2EC6;CJK RADICAL SIMPLIFIED HORN;So;0;ON;;;;;N;;;;; +2EC7;CJK RADICAL HORN;So;0;ON;;;;;N;;;;; +2EC8;CJK RADICAL C-SIMPLIFIED SPEECH;So;0;ON;;;;;N;;;;; +2EC9;CJK RADICAL C-SIMPLIFIED SHELL;So;0;ON;;;;;N;;;;; +2ECA;CJK RADICAL FOOT;So;0;ON;;;;;N;;;;; +2ECB;CJK RADICAL C-SIMPLIFIED CART;So;0;ON;;;;;N;;;;; +2ECC;CJK RADICAL SIMPLIFIED WALK;So;0;ON;;;;;N;;;;; +2ECD;CJK RADICAL WALK ONE;So;0;ON;;;;;N;;;;; +2ECE;CJK RADICAL WALK TWO;So;0;ON;;;;;N;;;;; +2ECF;CJK RADICAL CITY;So;0;ON;;;;;N;;;;; +2ED0;CJK RADICAL C-SIMPLIFIED GOLD;So;0;ON;;;;;N;;;;; +2ED1;CJK RADICAL LONG ONE;So;0;ON;;;;;N;;;;; +2ED2;CJK RADICAL LONG TWO;So;0;ON;;;;;N;;;;; +2ED3;CJK RADICAL C-SIMPLIFIED LONG;So;0;ON;;;;;N;;;;; +2ED4;CJK RADICAL C-SIMPLIFIED GATE;So;0;ON;;;;;N;;;;; +2ED5;CJK RADICAL MOUND ONE;So;0;ON;;;;;N;;;;; +2ED6;CJK RADICAL MOUND TWO;So;0;ON;;;;;N;;;;; +2ED7;CJK RADICAL RAIN;So;0;ON;;;;;N;;;;; +2ED8;CJK RADICAL BLUE;So;0;ON;;;;;N;;;;; +2ED9;CJK RADICAL C-SIMPLIFIED TANNED LEATHER;So;0;ON;;;;;N;;;;; +2EDA;CJK RADICAL C-SIMPLIFIED LEAF;So;0;ON;;;;;N;;;;; +2EDB;CJK RADICAL C-SIMPLIFIED WIND;So;0;ON;;;;;N;;;;; +2EDC;CJK RADICAL C-SIMPLIFIED FLY;So;0;ON;;;;;N;;;;; +2EDD;CJK RADICAL EAT ONE;So;0;ON;;;;;N;;;;; +2EDE;CJK RADICAL EAT TWO;So;0;ON;;;;;N;;;;; +2EDF;CJK RADICAL EAT THREE;So;0;ON;;;;;N;;;;; +2EE0;CJK RADICAL C-SIMPLIFIED EAT;So;0;ON;;;;;N;;;;; +2EE1;CJK RADICAL HEAD;So;0;ON;;;;;N;;;;; +2EE2;CJK RADICAL C-SIMPLIFIED HORSE;So;0;ON;;;;;N;;;;; +2EE3;CJK RADICAL BONE;So;0;ON;;;;;N;;;;; +2EE4;CJK RADICAL GHOST;So;0;ON;;;;;N;;;;; +2EE5;CJK RADICAL C-SIMPLIFIED FISH;So;0;ON;;;;;N;;;;; +2EE6;CJK RADICAL C-SIMPLIFIED BIRD;So;0;ON;;;;;N;;;;; +2EE7;CJK RADICAL C-SIMPLIFIED SALT;So;0;ON;;;;;N;;;;; +2EE8;CJK RADICAL SIMPLIFIED WHEAT;So;0;ON;;;;;N;;;;; +2EE9;CJK RADICAL SIMPLIFIED YELLOW;So;0;ON;;;;;N;;;;; +2EEA;CJK RADICAL C-SIMPLIFIED FROG;So;0;ON;;;;;N;;;;; +2EEB;CJK RADICAL J-SIMPLIFIED EVEN;So;0;ON;;;;;N;;;;; +2EEC;CJK RADICAL C-SIMPLIFIED EVEN;So;0;ON;;;;;N;;;;; +2EED;CJK RADICAL J-SIMPLIFIED TOOTH;So;0;ON;;;;;N;;;;; +2EEE;CJK RADICAL C-SIMPLIFIED TOOTH;So;0;ON;;;;;N;;;;; +2EEF;CJK RADICAL J-SIMPLIFIED DRAGON;So;0;ON;;;;;N;;;;; +2EF0;CJK RADICAL C-SIMPLIFIED DRAGON;So;0;ON;;;;;N;;;;; +2EF1;CJK RADICAL TURTLE;So;0;ON;;;;;N;;;;; +2EF2;CJK RADICAL J-SIMPLIFIED TURTLE;So;0;ON;;;;;N;;;;; +2EF3;CJK RADICAL C-SIMPLIFIED TURTLE;So;0;ON;<compat> 9F9F;;;;N;;;;; +2F00;KANGXI RADICAL ONE;So;0;ON;<compat> 4E00;;;;N;;;;; +2F01;KANGXI RADICAL LINE;So;0;ON;<compat> 4E28;;;;N;;;;; +2F02;KANGXI RADICAL DOT;So;0;ON;<compat> 4E36;;;;N;;;;; +2F03;KANGXI RADICAL SLASH;So;0;ON;<compat> 4E3F;;;;N;;;;; +2F04;KANGXI RADICAL SECOND;So;0;ON;<compat> 4E59;;;;N;;;;; +2F05;KANGXI RADICAL HOOK;So;0;ON;<compat> 4E85;;;;N;;;;; +2F06;KANGXI RADICAL TWO;So;0;ON;<compat> 4E8C;;;;N;;;;; +2F07;KANGXI RADICAL LID;So;0;ON;<compat> 4EA0;;;;N;;;;; +2F08;KANGXI RADICAL MAN;So;0;ON;<compat> 4EBA;;;;N;;;;; +2F09;KANGXI RADICAL LEGS;So;0;ON;<compat> 513F;;;;N;;;;; +2F0A;KANGXI RADICAL ENTER;So;0;ON;<compat> 5165;;;;N;;;;; +2F0B;KANGXI RADICAL EIGHT;So;0;ON;<compat> 516B;;;;N;;;;; +2F0C;KANGXI RADICAL DOWN BOX;So;0;ON;<compat> 5182;;;;N;;;;; +2F0D;KANGXI RADICAL COVER;So;0;ON;<compat> 5196;;;;N;;;;; +2F0E;KANGXI RADICAL ICE;So;0;ON;<compat> 51AB;;;;N;;;;; +2F0F;KANGXI RADICAL TABLE;So;0;ON;<compat> 51E0;;;;N;;;;; +2F10;KANGXI RADICAL OPEN BOX;So;0;ON;<compat> 51F5;;;;N;;;;; +2F11;KANGXI RADICAL KNIFE;So;0;ON;<compat> 5200;;;;N;;;;; +2F12;KANGXI RADICAL POWER;So;0;ON;<compat> 529B;;;;N;;;;; +2F13;KANGXI RADICAL WRAP;So;0;ON;<compat> 52F9;;;;N;;;;; +2F14;KANGXI RADICAL SPOON;So;0;ON;<compat> 5315;;;;N;;;;; +2F15;KANGXI RADICAL RIGHT OPEN BOX;So;0;ON;<compat> 531A;;;;N;;;;; +2F16;KANGXI RADICAL HIDING ENCLOSURE;So;0;ON;<compat> 5338;;;;N;;;;; +2F17;KANGXI RADICAL TEN;So;0;ON;<compat> 5341;;;;N;;;;; +2F18;KANGXI RADICAL DIVINATION;So;0;ON;<compat> 535C;;;;N;;;;; +2F19;KANGXI RADICAL SEAL;So;0;ON;<compat> 5369;;;;N;;;;; +2F1A;KANGXI RADICAL CLIFF;So;0;ON;<compat> 5382;;;;N;;;;; +2F1B;KANGXI RADICAL PRIVATE;So;0;ON;<compat> 53B6;;;;N;;;;; +2F1C;KANGXI RADICAL AGAIN;So;0;ON;<compat> 53C8;;;;N;;;;; +2F1D;KANGXI RADICAL MOUTH;So;0;ON;<compat> 53E3;;;;N;;;;; +2F1E;KANGXI RADICAL ENCLOSURE;So;0;ON;<compat> 56D7;;;;N;;;;; +2F1F;KANGXI RADICAL EARTH;So;0;ON;<compat> 571F;;;;N;;;;; +2F20;KANGXI RADICAL SCHOLAR;So;0;ON;<compat> 58EB;;;;N;;;;; +2F21;KANGXI RADICAL GO;So;0;ON;<compat> 5902;;;;N;;;;; +2F22;KANGXI RADICAL GO SLOWLY;So;0;ON;<compat> 590A;;;;N;;;;; +2F23;KANGXI RADICAL EVENING;So;0;ON;<compat> 5915;;;;N;;;;; +2F24;KANGXI RADICAL BIG;So;0;ON;<compat> 5927;;;;N;;;;; +2F25;KANGXI RADICAL WOMAN;So;0;ON;<compat> 5973;;;;N;;;;; +2F26;KANGXI RADICAL CHILD;So;0;ON;<compat> 5B50;;;;N;;;;; +2F27;KANGXI RADICAL ROOF;So;0;ON;<compat> 5B80;;;;N;;;;; +2F28;KANGXI RADICAL INCH;So;0;ON;<compat> 5BF8;;;;N;;;;; +2F29;KANGXI RADICAL SMALL;So;0;ON;<compat> 5C0F;;;;N;;;;; +2F2A;KANGXI RADICAL LAME;So;0;ON;<compat> 5C22;;;;N;;;;; +2F2B;KANGXI RADICAL CORPSE;So;0;ON;<compat> 5C38;;;;N;;;;; +2F2C;KANGXI RADICAL SPROUT;So;0;ON;<compat> 5C6E;;;;N;;;;; +2F2D;KANGXI RADICAL MOUNTAIN;So;0;ON;<compat> 5C71;;;;N;;;;; +2F2E;KANGXI RADICAL RIVER;So;0;ON;<compat> 5DDB;;;;N;;;;; +2F2F;KANGXI RADICAL WORK;So;0;ON;<compat> 5DE5;;;;N;;;;; +2F30;KANGXI RADICAL ONESELF;So;0;ON;<compat> 5DF1;;;;N;;;;; +2F31;KANGXI RADICAL TURBAN;So;0;ON;<compat> 5DFE;;;;N;;;;; +2F32;KANGXI RADICAL DRY;So;0;ON;<compat> 5E72;;;;N;;;;; +2F33;KANGXI RADICAL SHORT THREAD;So;0;ON;<compat> 5E7A;;;;N;;;;; +2F34;KANGXI RADICAL DOTTED CLIFF;So;0;ON;<compat> 5E7F;;;;N;;;;; +2F35;KANGXI RADICAL LONG STRIDE;So;0;ON;<compat> 5EF4;;;;N;;;;; +2F36;KANGXI RADICAL TWO HANDS;So;0;ON;<compat> 5EFE;;;;N;;;;; +2F37;KANGXI RADICAL SHOOT;So;0;ON;<compat> 5F0B;;;;N;;;;; +2F38;KANGXI RADICAL BOW;So;0;ON;<compat> 5F13;;;;N;;;;; +2F39;KANGXI RADICAL SNOUT;So;0;ON;<compat> 5F50;;;;N;;;;; +2F3A;KANGXI RADICAL BRISTLE;So;0;ON;<compat> 5F61;;;;N;;;;; +2F3B;KANGXI RADICAL STEP;So;0;ON;<compat> 5F73;;;;N;;;;; +2F3C;KANGXI RADICAL HEART;So;0;ON;<compat> 5FC3;;;;N;;;;; +2F3D;KANGXI RADICAL HALBERD;So;0;ON;<compat> 6208;;;;N;;;;; +2F3E;KANGXI RADICAL DOOR;So;0;ON;<compat> 6236;;;;N;;;;; +2F3F;KANGXI RADICAL HAND;So;0;ON;<compat> 624B;;;;N;;;;; +2F40;KANGXI RADICAL BRANCH;So;0;ON;<compat> 652F;;;;N;;;;; +2F41;KANGXI RADICAL RAP;So;0;ON;<compat> 6534;;;;N;;;;; +2F42;KANGXI RADICAL SCRIPT;So;0;ON;<compat> 6587;;;;N;;;;; +2F43;KANGXI RADICAL DIPPER;So;0;ON;<compat> 6597;;;;N;;;;; +2F44;KANGXI RADICAL AXE;So;0;ON;<compat> 65A4;;;;N;;;;; +2F45;KANGXI RADICAL SQUARE;So;0;ON;<compat> 65B9;;;;N;;;;; +2F46;KANGXI RADICAL NOT;So;0;ON;<compat> 65E0;;;;N;;;;; +2F47;KANGXI RADICAL SUN;So;0;ON;<compat> 65E5;;;;N;;;;; +2F48;KANGXI RADICAL SAY;So;0;ON;<compat> 66F0;;;;N;;;;; +2F49;KANGXI RADICAL MOON;So;0;ON;<compat> 6708;;;;N;;;;; +2F4A;KANGXI RADICAL TREE;So;0;ON;<compat> 6728;;;;N;;;;; +2F4B;KANGXI RADICAL LACK;So;0;ON;<compat> 6B20;;;;N;;;;; +2F4C;KANGXI RADICAL STOP;So;0;ON;<compat> 6B62;;;;N;;;;; +2F4D;KANGXI RADICAL DEATH;So;0;ON;<compat> 6B79;;;;N;;;;; +2F4E;KANGXI RADICAL WEAPON;So;0;ON;<compat> 6BB3;;;;N;;;;; +2F4F;KANGXI RADICAL DO NOT;So;0;ON;<compat> 6BCB;;;;N;;;;; +2F50;KANGXI RADICAL COMPARE;So;0;ON;<compat> 6BD4;;;;N;;;;; +2F51;KANGXI RADICAL FUR;So;0;ON;<compat> 6BDB;;;;N;;;;; +2F52;KANGXI RADICAL CLAN;So;0;ON;<compat> 6C0F;;;;N;;;;; +2F53;KANGXI RADICAL STEAM;So;0;ON;<compat> 6C14;;;;N;;;;; +2F54;KANGXI RADICAL WATER;So;0;ON;<compat> 6C34;;;;N;;;;; +2F55;KANGXI RADICAL FIRE;So;0;ON;<compat> 706B;;;;N;;;;; +2F56;KANGXI RADICAL CLAW;So;0;ON;<compat> 722A;;;;N;;;;; +2F57;KANGXI RADICAL FATHER;So;0;ON;<compat> 7236;;;;N;;;;; +2F58;KANGXI RADICAL DOUBLE X;So;0;ON;<compat> 723B;;;;N;;;;; +2F59;KANGXI RADICAL HALF TREE TRUNK;So;0;ON;<compat> 723F;;;;N;;;;; +2F5A;KANGXI RADICAL SLICE;So;0;ON;<compat> 7247;;;;N;;;;; +2F5B;KANGXI RADICAL FANG;So;0;ON;<compat> 7259;;;;N;;;;; +2F5C;KANGXI RADICAL COW;So;0;ON;<compat> 725B;;;;N;;;;; +2F5D;KANGXI RADICAL DOG;So;0;ON;<compat> 72AC;;;;N;;;;; +2F5E;KANGXI RADICAL PROFOUND;So;0;ON;<compat> 7384;;;;N;;;;; +2F5F;KANGXI RADICAL JADE;So;0;ON;<compat> 7389;;;;N;;;;; +2F60;KANGXI RADICAL MELON;So;0;ON;<compat> 74DC;;;;N;;;;; +2F61;KANGXI RADICAL TILE;So;0;ON;<compat> 74E6;;;;N;;;;; +2F62;KANGXI RADICAL SWEET;So;0;ON;<compat> 7518;;;;N;;;;; +2F63;KANGXI RADICAL LIFE;So;0;ON;<compat> 751F;;;;N;;;;; +2F64;KANGXI RADICAL USE;So;0;ON;<compat> 7528;;;;N;;;;; +2F65;KANGXI RADICAL FIELD;So;0;ON;<compat> 7530;;;;N;;;;; +2F66;KANGXI RADICAL BOLT OF CLOTH;So;0;ON;<compat> 758B;;;;N;;;;; +2F67;KANGXI RADICAL SICKNESS;So;0;ON;<compat> 7592;;;;N;;;;; +2F68;KANGXI RADICAL DOTTED TENT;So;0;ON;<compat> 7676;;;;N;;;;; +2F69;KANGXI RADICAL WHITE;So;0;ON;<compat> 767D;;;;N;;;;; +2F6A;KANGXI RADICAL SKIN;So;0;ON;<compat> 76AE;;;;N;;;;; +2F6B;KANGXI RADICAL DISH;So;0;ON;<compat> 76BF;;;;N;;;;; +2F6C;KANGXI RADICAL EYE;So;0;ON;<compat> 76EE;;;;N;;;;; +2F6D;KANGXI RADICAL SPEAR;So;0;ON;<compat> 77DB;;;;N;;;;; +2F6E;KANGXI RADICAL ARROW;So;0;ON;<compat> 77E2;;;;N;;;;; +2F6F;KANGXI RADICAL STONE;So;0;ON;<compat> 77F3;;;;N;;;;; +2F70;KANGXI RADICAL SPIRIT;So;0;ON;<compat> 793A;;;;N;;;;; +2F71;KANGXI RADICAL TRACK;So;0;ON;<compat> 79B8;;;;N;;;;; +2F72;KANGXI RADICAL GRAIN;So;0;ON;<compat> 79BE;;;;N;;;;; +2F73;KANGXI RADICAL CAVE;So;0;ON;<compat> 7A74;;;;N;;;;; +2F74;KANGXI RADICAL STAND;So;0;ON;<compat> 7ACB;;;;N;;;;; +2F75;KANGXI RADICAL BAMBOO;So;0;ON;<compat> 7AF9;;;;N;;;;; +2F76;KANGXI RADICAL RICE;So;0;ON;<compat> 7C73;;;;N;;;;; +2F77;KANGXI RADICAL SILK;So;0;ON;<compat> 7CF8;;;;N;;;;; +2F78;KANGXI RADICAL JAR;So;0;ON;<compat> 7F36;;;;N;;;;; +2F79;KANGXI RADICAL NET;So;0;ON;<compat> 7F51;;;;N;;;;; +2F7A;KANGXI RADICAL SHEEP;So;0;ON;<compat> 7F8A;;;;N;;;;; +2F7B;KANGXI RADICAL FEATHER;So;0;ON;<compat> 7FBD;;;;N;;;;; +2F7C;KANGXI RADICAL OLD;So;0;ON;<compat> 8001;;;;N;;;;; +2F7D;KANGXI RADICAL AND;So;0;ON;<compat> 800C;;;;N;;;;; +2F7E;KANGXI RADICAL PLOW;So;0;ON;<compat> 8012;;;;N;;;;; +2F7F;KANGXI RADICAL EAR;So;0;ON;<compat> 8033;;;;N;;;;; +2F80;KANGXI RADICAL BRUSH;So;0;ON;<compat> 807F;;;;N;;;;; +2F81;KANGXI RADICAL MEAT;So;0;ON;<compat> 8089;;;;N;;;;; +2F82;KANGXI RADICAL MINISTER;So;0;ON;<compat> 81E3;;;;N;;;;; +2F83;KANGXI RADICAL SELF;So;0;ON;<compat> 81EA;;;;N;;;;; +2F84;KANGXI RADICAL ARRIVE;So;0;ON;<compat> 81F3;;;;N;;;;; +2F85;KANGXI RADICAL MORTAR;So;0;ON;<compat> 81FC;;;;N;;;;; +2F86;KANGXI RADICAL TONGUE;So;0;ON;<compat> 820C;;;;N;;;;; +2F87;KANGXI RADICAL OPPOSE;So;0;ON;<compat> 821B;;;;N;;;;; +2F88;KANGXI RADICAL BOAT;So;0;ON;<compat> 821F;;;;N;;;;; +2F89;KANGXI RADICAL STOPPING;So;0;ON;<compat> 826E;;;;N;;;;; +2F8A;KANGXI RADICAL COLOR;So;0;ON;<compat> 8272;;;;N;;;;; +2F8B;KANGXI RADICAL GRASS;So;0;ON;<compat> 8278;;;;N;;;;; +2F8C;KANGXI RADICAL TIGER;So;0;ON;<compat> 864D;;;;N;;;;; +2F8D;KANGXI RADICAL INSECT;So;0;ON;<compat> 866B;;;;N;;;;; +2F8E;KANGXI RADICAL BLOOD;So;0;ON;<compat> 8840;;;;N;;;;; +2F8F;KANGXI RADICAL WALK ENCLOSURE;So;0;ON;<compat> 884C;;;;N;;;;; +2F90;KANGXI RADICAL CLOTHES;So;0;ON;<compat> 8863;;;;N;;;;; +2F91;KANGXI RADICAL WEST;So;0;ON;<compat> 897E;;;;N;;;;; +2F92;KANGXI RADICAL SEE;So;0;ON;<compat> 898B;;;;N;;;;; +2F93;KANGXI RADICAL HORN;So;0;ON;<compat> 89D2;;;;N;;;;; +2F94;KANGXI RADICAL SPEECH;So;0;ON;<compat> 8A00;;;;N;;;;; +2F95;KANGXI RADICAL VALLEY;So;0;ON;<compat> 8C37;;;;N;;;;; +2F96;KANGXI RADICAL BEAN;So;0;ON;<compat> 8C46;;;;N;;;;; +2F97;KANGXI RADICAL PIG;So;0;ON;<compat> 8C55;;;;N;;;;; +2F98;KANGXI RADICAL BADGER;So;0;ON;<compat> 8C78;;;;N;;;;; +2F99;KANGXI RADICAL SHELL;So;0;ON;<compat> 8C9D;;;;N;;;;; +2F9A;KANGXI RADICAL RED;So;0;ON;<compat> 8D64;;;;N;;;;; +2F9B;KANGXI RADICAL RUN;So;0;ON;<compat> 8D70;;;;N;;;;; +2F9C;KANGXI RADICAL FOOT;So;0;ON;<compat> 8DB3;;;;N;;;;; +2F9D;KANGXI RADICAL BODY;So;0;ON;<compat> 8EAB;;;;N;;;;; +2F9E;KANGXI RADICAL CART;So;0;ON;<compat> 8ECA;;;;N;;;;; +2F9F;KANGXI RADICAL BITTER;So;0;ON;<compat> 8F9B;;;;N;;;;; +2FA0;KANGXI RADICAL MORNING;So;0;ON;<compat> 8FB0;;;;N;;;;; +2FA1;KANGXI RADICAL WALK;So;0;ON;<compat> 8FB5;;;;N;;;;; +2FA2;KANGXI RADICAL CITY;So;0;ON;<compat> 9091;;;;N;;;;; +2FA3;KANGXI RADICAL WINE;So;0;ON;<compat> 9149;;;;N;;;;; +2FA4;KANGXI RADICAL DISTINGUISH;So;0;ON;<compat> 91C6;;;;N;;;;; +2FA5;KANGXI RADICAL VILLAGE;So;0;ON;<compat> 91CC;;;;N;;;;; +2FA6;KANGXI RADICAL GOLD;So;0;ON;<compat> 91D1;;;;N;;;;; +2FA7;KANGXI RADICAL LONG;So;0;ON;<compat> 9577;;;;N;;;;; +2FA8;KANGXI RADICAL GATE;So;0;ON;<compat> 9580;;;;N;;;;; +2FA9;KANGXI RADICAL MOUND;So;0;ON;<compat> 961C;;;;N;;;;; +2FAA;KANGXI RADICAL SLAVE;So;0;ON;<compat> 96B6;;;;N;;;;; +2FAB;KANGXI RADICAL SHORT TAILED BIRD;So;0;ON;<compat> 96B9;;;;N;;;;; +2FAC;KANGXI RADICAL RAIN;So;0;ON;<compat> 96E8;;;;N;;;;; +2FAD;KANGXI RADICAL BLUE;So;0;ON;<compat> 9751;;;;N;;;;; +2FAE;KANGXI RADICAL WRONG;So;0;ON;<compat> 975E;;;;N;;;;; +2FAF;KANGXI RADICAL FACE;So;0;ON;<compat> 9762;;;;N;;;;; +2FB0;KANGXI RADICAL LEATHER;So;0;ON;<compat> 9769;;;;N;;;;; +2FB1;KANGXI RADICAL TANNED LEATHER;So;0;ON;<compat> 97CB;;;;N;;;;; +2FB2;KANGXI RADICAL LEEK;So;0;ON;<compat> 97ED;;;;N;;;;; +2FB3;KANGXI RADICAL SOUND;So;0;ON;<compat> 97F3;;;;N;;;;; +2FB4;KANGXI RADICAL LEAF;So;0;ON;<compat> 9801;;;;N;;;;; +2FB5;KANGXI RADICAL WIND;So;0;ON;<compat> 98A8;;;;N;;;;; +2FB6;KANGXI RADICAL FLY;So;0;ON;<compat> 98DB;;;;N;;;;; +2FB7;KANGXI RADICAL EAT;So;0;ON;<compat> 98DF;;;;N;;;;; +2FB8;KANGXI RADICAL HEAD;So;0;ON;<compat> 9996;;;;N;;;;; +2FB9;KANGXI RADICAL FRAGRANT;So;0;ON;<compat> 9999;;;;N;;;;; +2FBA;KANGXI RADICAL HORSE;So;0;ON;<compat> 99AC;;;;N;;;;; +2FBB;KANGXI RADICAL BONE;So;0;ON;<compat> 9AA8;;;;N;;;;; +2FBC;KANGXI RADICAL TALL;So;0;ON;<compat> 9AD8;;;;N;;;;; +2FBD;KANGXI RADICAL HAIR;So;0;ON;<compat> 9ADF;;;;N;;;;; +2FBE;KANGXI RADICAL FIGHT;So;0;ON;<compat> 9B25;;;;N;;;;; +2FBF;KANGXI RADICAL SACRIFICIAL WINE;So;0;ON;<compat> 9B2F;;;;N;;;;; +2FC0;KANGXI RADICAL CAULDRON;So;0;ON;<compat> 9B32;;;;N;;;;; +2FC1;KANGXI RADICAL GHOST;So;0;ON;<compat> 9B3C;;;;N;;;;; +2FC2;KANGXI RADICAL FISH;So;0;ON;<compat> 9B5A;;;;N;;;;; +2FC3;KANGXI RADICAL BIRD;So;0;ON;<compat> 9CE5;;;;N;;;;; +2FC4;KANGXI RADICAL SALT;So;0;ON;<compat> 9E75;;;;N;;;;; +2FC5;KANGXI RADICAL DEER;So;0;ON;<compat> 9E7F;;;;N;;;;; +2FC6;KANGXI RADICAL WHEAT;So;0;ON;<compat> 9EA5;;;;N;;;;; +2FC7;KANGXI RADICAL HEMP;So;0;ON;<compat> 9EBB;;;;N;;;;; +2FC8;KANGXI RADICAL YELLOW;So;0;ON;<compat> 9EC3;;;;N;;;;; +2FC9;KANGXI RADICAL MILLET;So;0;ON;<compat> 9ECD;;;;N;;;;; +2FCA;KANGXI RADICAL BLACK;So;0;ON;<compat> 9ED1;;;;N;;;;; +2FCB;KANGXI RADICAL EMBROIDERY;So;0;ON;<compat> 9EF9;;;;N;;;;; +2FCC;KANGXI RADICAL FROG;So;0;ON;<compat> 9EFD;;;;N;;;;; +2FCD;KANGXI RADICAL TRIPOD;So;0;ON;<compat> 9F0E;;;;N;;;;; +2FCE;KANGXI RADICAL DRUM;So;0;ON;<compat> 9F13;;;;N;;;;; +2FCF;KANGXI RADICAL RAT;So;0;ON;<compat> 9F20;;;;N;;;;; +2FD0;KANGXI RADICAL NOSE;So;0;ON;<compat> 9F3B;;;;N;;;;; +2FD1;KANGXI RADICAL EVEN;So;0;ON;<compat> 9F4A;;;;N;;;;; +2FD2;KANGXI RADICAL TOOTH;So;0;ON;<compat> 9F52;;;;N;;;;; +2FD3;KANGXI RADICAL DRAGON;So;0;ON;<compat> 9F8D;;;;N;;;;; +2FD4;KANGXI RADICAL TURTLE;So;0;ON;<compat> 9F9C;;;;N;;;;; +2FD5;KANGXI RADICAL FLUTE;So;0;ON;<compat> 9FA0;;;;N;;;;; +2FF0;IDEOGRAPHIC DESCRIPTION CHARACTER LEFT TO RIGHT;So;0;ON;;;;;N;;;;; +2FF1;IDEOGRAPHIC DESCRIPTION CHARACTER ABOVE TO BELOW;So;0;ON;;;;;N;;;;; +2FF2;IDEOGRAPHIC DESCRIPTION CHARACTER LEFT TO MIDDLE AND RIGHT;So;0;ON;;;;;N;;;;; +2FF3;IDEOGRAPHIC DESCRIPTION CHARACTER ABOVE TO MIDDLE AND BELOW;So;0;ON;;;;;N;;;;; +2FF4;IDEOGRAPHIC DESCRIPTION CHARACTER FULL SURROUND;So;0;ON;;;;;N;;;;; +2FF5;IDEOGRAPHIC DESCRIPTION CHARACTER SURROUND FROM ABOVE;So;0;ON;;;;;N;;;;; +2FF6;IDEOGRAPHIC DESCRIPTION CHARACTER SURROUND FROM BELOW;So;0;ON;;;;;N;;;;; +2FF7;IDEOGRAPHIC DESCRIPTION CHARACTER SURROUND FROM LEFT;So;0;ON;;;;;N;;;;; +2FF8;IDEOGRAPHIC DESCRIPTION CHARACTER SURROUND FROM UPPER LEFT;So;0;ON;;;;;N;;;;; +2FF9;IDEOGRAPHIC DESCRIPTION CHARACTER SURROUND FROM UPPER RIGHT;So;0;ON;;;;;N;;;;; +2FFA;IDEOGRAPHIC DESCRIPTION CHARACTER SURROUND FROM LOWER LEFT;So;0;ON;;;;;N;;;;; +2FFB;IDEOGRAPHIC DESCRIPTION CHARACTER OVERLAID;So;0;ON;;;;;N;;;;; +3000;IDEOGRAPHIC SPACE;Zs;0;WS;<wide> 0020;;;;N;;;;; +3001;IDEOGRAPHIC COMMA;Po;0;ON;;;;;N;;;;; +3002;IDEOGRAPHIC FULL STOP;Po;0;ON;;;;;N;IDEOGRAPHIC PERIOD;;;; +3003;DITTO MARK;Po;0;ON;;;;;N;;;;; +3004;JAPANESE INDUSTRIAL STANDARD SYMBOL;So;0;ON;;;;;N;;;;; +3005;IDEOGRAPHIC ITERATION MARK;Lm;0;L;;;;;N;;;;; +3006;IDEOGRAPHIC CLOSING MARK;Lo;0;L;;;;;N;;;;; +3007;IDEOGRAPHIC NUMBER ZERO;Nl;0;L;;;;0;N;;;;; +3008;LEFT ANGLE BRACKET;Ps;0;ON;;;;;Y;OPENING ANGLE BRACKET;;;; +3009;RIGHT ANGLE BRACKET;Pe;0;ON;;;;;Y;CLOSING ANGLE BRACKET;;;; +300A;LEFT DOUBLE ANGLE BRACKET;Ps;0;ON;;;;;Y;OPENING DOUBLE ANGLE BRACKET;;;; +300B;RIGHT DOUBLE ANGLE BRACKET;Pe;0;ON;;;;;Y;CLOSING DOUBLE ANGLE BRACKET;;;; +300C;LEFT CORNER BRACKET;Ps;0;ON;;;;;Y;OPENING CORNER BRACKET;;;; +300D;RIGHT CORNER BRACKET;Pe;0;ON;;;;;Y;CLOSING CORNER BRACKET;;;; +300E;LEFT WHITE CORNER BRACKET;Ps;0;ON;;;;;Y;OPENING WHITE CORNER BRACKET;;;; +300F;RIGHT WHITE CORNER BRACKET;Pe;0;ON;;;;;Y;CLOSING WHITE CORNER BRACKET;;;; +3010;LEFT BLACK LENTICULAR BRACKET;Ps;0;ON;;;;;Y;OPENING BLACK LENTICULAR BRACKET;;;; +3011;RIGHT BLACK LENTICULAR BRACKET;Pe;0;ON;;;;;Y;CLOSING BLACK LENTICULAR BRACKET;;;; +3012;POSTAL MARK;So;0;ON;;;;;N;;;;; +3013;GETA MARK;So;0;ON;;;;;N;;;;; +3014;LEFT TORTOISE SHELL BRACKET;Ps;0;ON;;;;;Y;OPENING TORTOISE SHELL BRACKET;;;; +3015;RIGHT TORTOISE SHELL BRACKET;Pe;0;ON;;;;;Y;CLOSING TORTOISE SHELL BRACKET;;;; +3016;LEFT WHITE LENTICULAR BRACKET;Ps;0;ON;;;;;Y;OPENING WHITE LENTICULAR BRACKET;;;; +3017;RIGHT WHITE LENTICULAR BRACKET;Pe;0;ON;;;;;Y;CLOSING WHITE LENTICULAR BRACKET;;;; +3018;LEFT WHITE TORTOISE SHELL BRACKET;Ps;0;ON;;;;;Y;OPENING WHITE TORTOISE SHELL BRACKET;;;; +3019;RIGHT WHITE TORTOISE SHELL BRACKET;Pe;0;ON;;;;;Y;CLOSING WHITE TORTOISE SHELL BRACKET;;;; +301A;LEFT WHITE SQUARE BRACKET;Ps;0;ON;;;;;Y;OPENING WHITE SQUARE BRACKET;;;; +301B;RIGHT WHITE SQUARE BRACKET;Pe;0;ON;;;;;Y;CLOSING WHITE SQUARE BRACKET;;;; +301C;WAVE DASH;Pd;0;ON;;;;;N;;;;; +301D;REVERSED DOUBLE PRIME QUOTATION MARK;Ps;0;ON;;;;;N;;;;; +301E;DOUBLE PRIME QUOTATION MARK;Pe;0;ON;;;;;N;;;;; +301F;LOW DOUBLE PRIME QUOTATION MARK;Pe;0;ON;;;;;N;;;;; +3020;POSTAL MARK FACE;So;0;ON;;;;;N;;;;; +3021;HANGZHOU NUMERAL ONE;Nl;0;L;;;;1;N;;;;; +3022;HANGZHOU NUMERAL TWO;Nl;0;L;;;;2;N;;;;; +3023;HANGZHOU NUMERAL THREE;Nl;0;L;;;;3;N;;;;; +3024;HANGZHOU NUMERAL FOUR;Nl;0;L;;;;4;N;;;;; +3025;HANGZHOU NUMERAL FIVE;Nl;0;L;;;;5;N;;;;; +3026;HANGZHOU NUMERAL SIX;Nl;0;L;;;;6;N;;;;; +3027;HANGZHOU NUMERAL SEVEN;Nl;0;L;;;;7;N;;;;; +3028;HANGZHOU NUMERAL EIGHT;Nl;0;L;;;;8;N;;;;; +3029;HANGZHOU NUMERAL NINE;Nl;0;L;;;;9;N;;;;; +302A;IDEOGRAPHIC LEVEL TONE MARK;Mn;218;NSM;;;;;N;;;;; +302B;IDEOGRAPHIC RISING TONE MARK;Mn;228;NSM;;;;;N;;;;; +302C;IDEOGRAPHIC DEPARTING TONE MARK;Mn;232;NSM;;;;;N;;;;; +302D;IDEOGRAPHIC ENTERING TONE MARK;Mn;222;NSM;;;;;N;;;;; +302E;HANGUL SINGLE DOT TONE MARK;Mn;224;NSM;;;;;N;;;;; +302F;HANGUL DOUBLE DOT TONE MARK;Mn;224;NSM;;;;;N;;;;; +3030;WAVY DASH;Pd;0;ON;;;;;N;;;;; +3031;VERTICAL KANA REPEAT MARK;Lm;0;L;;;;;N;;;;; +3032;VERTICAL KANA REPEAT WITH VOICED SOUND MARK;Lm;0;L;;;;;N;;;;; +3033;VERTICAL KANA REPEAT MARK UPPER HALF;Lm;0;L;;;;;N;;;;; +3034;VERTICAL KANA REPEAT WITH VOICED SOUND MARK UPPER HALF;Lm;0;L;;;;;N;;;;; +3035;VERTICAL KANA REPEAT MARK LOWER HALF;Lm;0;L;;;;;N;;;;; +3036;CIRCLED POSTAL MARK;So;0;ON;<compat> 3012;;;;N;;;;; +3037;IDEOGRAPHIC TELEGRAPH LINE FEED SEPARATOR SYMBOL;So;0;ON;;;;;N;;;;; +3038;HANGZHOU NUMERAL TEN;Nl;0;L;<compat> 5341;;;10;N;;;;; +3039;HANGZHOU NUMERAL TWENTY;Nl;0;L;<compat> 5344;;;20;N;;;;; +303A;HANGZHOU NUMERAL THIRTY;Nl;0;L;<compat> 5345;;;30;N;;;;; +303B;VERTICAL IDEOGRAPHIC ITERATION MARK;Lm;0;L;;;;;N;;;;; +303C;MASU MARK;Lo;0;L;;;;;N;;;;; +303D;PART ALTERNATION MARK;Po;0;ON;;;;;N;;;;; +303E;IDEOGRAPHIC VARIATION INDICATOR;So;0;ON;;;;;N;;;;; +303F;IDEOGRAPHIC HALF FILL SPACE;So;0;ON;;;;;N;;;;; +3041;HIRAGANA LETTER SMALL A;Lo;0;L;;;;;N;;;;; +3042;HIRAGANA LETTER A;Lo;0;L;;;;;N;;;;; +3043;HIRAGANA LETTER SMALL I;Lo;0;L;;;;;N;;;;; +3044;HIRAGANA LETTER I;Lo;0;L;;;;;N;;;;; +3045;HIRAGANA LETTER SMALL U;Lo;0;L;;;;;N;;;;; +3046;HIRAGANA LETTER U;Lo;0;L;;;;;N;;;;; +3047;HIRAGANA LETTER SMALL E;Lo;0;L;;;;;N;;;;; +3048;HIRAGANA LETTER E;Lo;0;L;;;;;N;;;;; +3049;HIRAGANA LETTER SMALL O;Lo;0;L;;;;;N;;;;; +304A;HIRAGANA LETTER O;Lo;0;L;;;;;N;;;;; +304B;HIRAGANA LETTER KA;Lo;0;L;;;;;N;;;;; +304C;HIRAGANA LETTER GA;Lo;0;L;304B 3099;;;;N;;;;; +304D;HIRAGANA LETTER KI;Lo;0;L;;;;;N;;;;; +304E;HIRAGANA LETTER GI;Lo;0;L;304D 3099;;;;N;;;;; +304F;HIRAGANA LETTER KU;Lo;0;L;;;;;N;;;;; +3050;HIRAGANA LETTER GU;Lo;0;L;304F 3099;;;;N;;;;; +3051;HIRAGANA LETTER KE;Lo;0;L;;;;;N;;;;; +3052;HIRAGANA LETTER GE;Lo;0;L;3051 3099;;;;N;;;;; +3053;HIRAGANA LETTER KO;Lo;0;L;;;;;N;;;;; +3054;HIRAGANA LETTER GO;Lo;0;L;3053 3099;;;;N;;;;; +3055;HIRAGANA LETTER SA;Lo;0;L;;;;;N;;;;; +3056;HIRAGANA LETTER ZA;Lo;0;L;3055 3099;;;;N;;;;; +3057;HIRAGANA LETTER SI;Lo;0;L;;;;;N;;;;; +3058;HIRAGANA LETTER ZI;Lo;0;L;3057 3099;;;;N;;;;; +3059;HIRAGANA LETTER SU;Lo;0;L;;;;;N;;;;; +305A;HIRAGANA LETTER ZU;Lo;0;L;3059 3099;;;;N;;;;; +305B;HIRAGANA LETTER SE;Lo;0;L;;;;;N;;;;; +305C;HIRAGANA LETTER ZE;Lo;0;L;305B 3099;;;;N;;;;; +305D;HIRAGANA LETTER SO;Lo;0;L;;;;;N;;;;; +305E;HIRAGANA LETTER ZO;Lo;0;L;305D 3099;;;;N;;;;; +305F;HIRAGANA LETTER TA;Lo;0;L;;;;;N;;;;; +3060;HIRAGANA LETTER DA;Lo;0;L;305F 3099;;;;N;;;;; +3061;HIRAGANA LETTER TI;Lo;0;L;;;;;N;;;;; +3062;HIRAGANA LETTER DI;Lo;0;L;3061 3099;;;;N;;;;; +3063;HIRAGANA LETTER SMALL TU;Lo;0;L;;;;;N;;;;; +3064;HIRAGANA LETTER TU;Lo;0;L;;;;;N;;;;; +3065;HIRAGANA LETTER DU;Lo;0;L;3064 3099;;;;N;;;;; +3066;HIRAGANA LETTER TE;Lo;0;L;;;;;N;;;;; +3067;HIRAGANA LETTER DE;Lo;0;L;3066 3099;;;;N;;;;; +3068;HIRAGANA LETTER TO;Lo;0;L;;;;;N;;;;; +3069;HIRAGANA LETTER DO;Lo;0;L;3068 3099;;;;N;;;;; +306A;HIRAGANA LETTER NA;Lo;0;L;;;;;N;;;;; +306B;HIRAGANA LETTER NI;Lo;0;L;;;;;N;;;;; +306C;HIRAGANA LETTER NU;Lo;0;L;;;;;N;;;;; +306D;HIRAGANA LETTER NE;Lo;0;L;;;;;N;;;;; +306E;HIRAGANA LETTER NO;Lo;0;L;;;;;N;;;;; +306F;HIRAGANA LETTER HA;Lo;0;L;;;;;N;;;;; +3070;HIRAGANA LETTER BA;Lo;0;L;306F 3099;;;;N;;;;; +3071;HIRAGANA LETTER PA;Lo;0;L;306F 309A;;;;N;;;;; +3072;HIRAGANA LETTER HI;Lo;0;L;;;;;N;;;;; +3073;HIRAGANA LETTER BI;Lo;0;L;3072 3099;;;;N;;;;; +3074;HIRAGANA LETTER PI;Lo;0;L;3072 309A;;;;N;;;;; +3075;HIRAGANA LETTER HU;Lo;0;L;;;;;N;;;;; +3076;HIRAGANA LETTER BU;Lo;0;L;3075 3099;;;;N;;;;; +3077;HIRAGANA LETTER PU;Lo;0;L;3075 309A;;;;N;;;;; +3078;HIRAGANA LETTER HE;Lo;0;L;;;;;N;;;;; +3079;HIRAGANA LETTER BE;Lo;0;L;3078 3099;;;;N;;;;; +307A;HIRAGANA LETTER PE;Lo;0;L;3078 309A;;;;N;;;;; +307B;HIRAGANA LETTER HO;Lo;0;L;;;;;N;;;;; +307C;HIRAGANA LETTER BO;Lo;0;L;307B 3099;;;;N;;;;; +307D;HIRAGANA LETTER PO;Lo;0;L;307B 309A;;;;N;;;;; +307E;HIRAGANA LETTER MA;Lo;0;L;;;;;N;;;;; +307F;HIRAGANA LETTER MI;Lo;0;L;;;;;N;;;;; +3080;HIRAGANA LETTER MU;Lo;0;L;;;;;N;;;;; +3081;HIRAGANA LETTER ME;Lo;0;L;;;;;N;;;;; +3082;HIRAGANA LETTER MO;Lo;0;L;;;;;N;;;;; +3083;HIRAGANA LETTER SMALL YA;Lo;0;L;;;;;N;;;;; +3084;HIRAGANA LETTER YA;Lo;0;L;;;;;N;;;;; +3085;HIRAGANA LETTER SMALL YU;Lo;0;L;;;;;N;;;;; +3086;HIRAGANA LETTER YU;Lo;0;L;;;;;N;;;;; +3087;HIRAGANA LETTER SMALL YO;Lo;0;L;;;;;N;;;;; +3088;HIRAGANA LETTER YO;Lo;0;L;;;;;N;;;;; +3089;HIRAGANA LETTER RA;Lo;0;L;;;;;N;;;;; +308A;HIRAGANA LETTER RI;Lo;0;L;;;;;N;;;;; +308B;HIRAGANA LETTER RU;Lo;0;L;;;;;N;;;;; +308C;HIRAGANA LETTER RE;Lo;0;L;;;;;N;;;;; +308D;HIRAGANA LETTER RO;Lo;0;L;;;;;N;;;;; +308E;HIRAGANA LETTER SMALL WA;Lo;0;L;;;;;N;;;;; +308F;HIRAGANA LETTER WA;Lo;0;L;;;;;N;;;;; +3090;HIRAGANA LETTER WI;Lo;0;L;;;;;N;;;;; +3091;HIRAGANA LETTER WE;Lo;0;L;;;;;N;;;;; +3092;HIRAGANA LETTER WO;Lo;0;L;;;;;N;;;;; +3093;HIRAGANA LETTER N;Lo;0;L;;;;;N;;;;; +3094;HIRAGANA LETTER VU;Lo;0;L;3046 3099;;;;N;;;;; +3095;HIRAGANA LETTER SMALL KA;Lo;0;L;;;;;N;;;;; +3096;HIRAGANA LETTER SMALL KE;Lo;0;L;;;;;N;;;;; +3099;COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK;Mn;8;NSM;;;;;N;NON-SPACING KATAKANA-HIRAGANA VOICED SOUND MARK;;;; +309A;COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK;Mn;8;NSM;;;;;N;NON-SPACING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK;;;; +309B;KATAKANA-HIRAGANA VOICED SOUND MARK;Sk;0;ON;<compat> 0020 3099;;;;N;;;;; +309C;KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK;Sk;0;ON;<compat> 0020 309A;;;;N;;;;; +309D;HIRAGANA ITERATION MARK;Lm;0;L;;;;;N;;;;; +309E;HIRAGANA VOICED ITERATION MARK;Lm;0;L;309D 3099;;;;N;;;;; +309F;HIRAGANA DIGRAPH YORI;Lo;0;L;<vertical> 3088 308A;;;;N;;;;; +30A0;KATAKANA-HIRAGANA DOUBLE HYPHEN;Pd;0;ON;;;;;N;;;;; +30A1;KATAKANA LETTER SMALL A;Lo;0;L;;;;;N;;;;; +30A2;KATAKANA LETTER A;Lo;0;L;;;;;N;;;;; +30A3;KATAKANA LETTER SMALL I;Lo;0;L;;;;;N;;;;; +30A4;KATAKANA LETTER I;Lo;0;L;;;;;N;;;;; +30A5;KATAKANA LETTER SMALL U;Lo;0;L;;;;;N;;;;; +30A6;KATAKANA LETTER U;Lo;0;L;;;;;N;;;;; +30A7;KATAKANA LETTER SMALL E;Lo;0;L;;;;;N;;;;; +30A8;KATAKANA LETTER E;Lo;0;L;;;;;N;;;;; +30A9;KATAKANA LETTER SMALL O;Lo;0;L;;;;;N;;;;; +30AA;KATAKANA LETTER O;Lo;0;L;;;;;N;;;;; +30AB;KATAKANA LETTER KA;Lo;0;L;;;;;N;;;;; +30AC;KATAKANA LETTER GA;Lo;0;L;30AB 3099;;;;N;;;;; +30AD;KATAKANA LETTER KI;Lo;0;L;;;;;N;;;;; +30AE;KATAKANA LETTER GI;Lo;0;L;30AD 3099;;;;N;;;;; +30AF;KATAKANA LETTER KU;Lo;0;L;;;;;N;;;;; +30B0;KATAKANA LETTER GU;Lo;0;L;30AF 3099;;;;N;;;;; +30B1;KATAKANA LETTER KE;Lo;0;L;;;;;N;;;;; +30B2;KATAKANA LETTER GE;Lo;0;L;30B1 3099;;;;N;;;;; +30B3;KATAKANA LETTER KO;Lo;0;L;;;;;N;;;;; +30B4;KATAKANA LETTER GO;Lo;0;L;30B3 3099;;;;N;;;;; +30B5;KATAKANA LETTER SA;Lo;0;L;;;;;N;;;;; +30B6;KATAKANA LETTER ZA;Lo;0;L;30B5 3099;;;;N;;;;; +30B7;KATAKANA LETTER SI;Lo;0;L;;;;;N;;;;; +30B8;KATAKANA LETTER ZI;Lo;0;L;30B7 3099;;;;N;;;;; +30B9;KATAKANA LETTER SU;Lo;0;L;;;;;N;;;;; +30BA;KATAKANA LETTER ZU;Lo;0;L;30B9 3099;;;;N;;;;; +30BB;KATAKANA LETTER SE;Lo;0;L;;;;;N;;;;; +30BC;KATAKANA LETTER ZE;Lo;0;L;30BB 3099;;;;N;;;;; +30BD;KATAKANA LETTER SO;Lo;0;L;;;;;N;;;;; +30BE;KATAKANA LETTER ZO;Lo;0;L;30BD 3099;;;;N;;;;; +30BF;KATAKANA LETTER TA;Lo;0;L;;;;;N;;;;; +30C0;KATAKANA LETTER DA;Lo;0;L;30BF 3099;;;;N;;;;; +30C1;KATAKANA LETTER TI;Lo;0;L;;;;;N;;;;; +30C2;KATAKANA LETTER DI;Lo;0;L;30C1 3099;;;;N;;;;; +30C3;KATAKANA LETTER SMALL TU;Lo;0;L;;;;;N;;;;; +30C4;KATAKANA LETTER TU;Lo;0;L;;;;;N;;;;; +30C5;KATAKANA LETTER DU;Lo;0;L;30C4 3099;;;;N;;;;; +30C6;KATAKANA LETTER TE;Lo;0;L;;;;;N;;;;; +30C7;KATAKANA LETTER DE;Lo;0;L;30C6 3099;;;;N;;;;; +30C8;KATAKANA LETTER TO;Lo;0;L;;;;;N;;;;; +30C9;KATAKANA LETTER DO;Lo;0;L;30C8 3099;;;;N;;;;; +30CA;KATAKANA LETTER NA;Lo;0;L;;;;;N;;;;; +30CB;KATAKANA LETTER NI;Lo;0;L;;;;;N;;;;; +30CC;KATAKANA LETTER NU;Lo;0;L;;;;;N;;;;; +30CD;KATAKANA LETTER NE;Lo;0;L;;;;;N;;;;; +30CE;KATAKANA LETTER NO;Lo;0;L;;;;;N;;;;; +30CF;KATAKANA LETTER HA;Lo;0;L;;;;;N;;;;; +30D0;KATAKANA LETTER BA;Lo;0;L;30CF 3099;;;;N;;;;; +30D1;KATAKANA LETTER PA;Lo;0;L;30CF 309A;;;;N;;;;; +30D2;KATAKANA LETTER HI;Lo;0;L;;;;;N;;;;; +30D3;KATAKANA LETTER BI;Lo;0;L;30D2 3099;;;;N;;;;; +30D4;KATAKANA LETTER PI;Lo;0;L;30D2 309A;;;;N;;;;; +30D5;KATAKANA LETTER HU;Lo;0;L;;;;;N;;;;; +30D6;KATAKANA LETTER BU;Lo;0;L;30D5 3099;;;;N;;;;; +30D7;KATAKANA LETTER PU;Lo;0;L;30D5 309A;;;;N;;;;; +30D8;KATAKANA LETTER HE;Lo;0;L;;;;;N;;;;; +30D9;KATAKANA LETTER BE;Lo;0;L;30D8 3099;;;;N;;;;; +30DA;KATAKANA LETTER PE;Lo;0;L;30D8 309A;;;;N;;;;; +30DB;KATAKANA LETTER HO;Lo;0;L;;;;;N;;;;; +30DC;KATAKANA LETTER BO;Lo;0;L;30DB 3099;;;;N;;;;; +30DD;KATAKANA LETTER PO;Lo;0;L;30DB 309A;;;;N;;;;; +30DE;KATAKANA LETTER MA;Lo;0;L;;;;;N;;;;; +30DF;KATAKANA LETTER MI;Lo;0;L;;;;;N;;;;; +30E0;KATAKANA LETTER MU;Lo;0;L;;;;;N;;;;; +30E1;KATAKANA LETTER ME;Lo;0;L;;;;;N;;;;; +30E2;KATAKANA LETTER MO;Lo;0;L;;;;;N;;;;; +30E3;KATAKANA LETTER SMALL YA;Lo;0;L;;;;;N;;;;; +30E4;KATAKANA LETTER YA;Lo;0;L;;;;;N;;;;; +30E5;KATAKANA LETTER SMALL YU;Lo;0;L;;;;;N;;;;; +30E6;KATAKANA LETTER YU;Lo;0;L;;;;;N;;;;; +30E7;KATAKANA LETTER SMALL YO;Lo;0;L;;;;;N;;;;; +30E8;KATAKANA LETTER YO;Lo;0;L;;;;;N;;;;; +30E9;KATAKANA LETTER RA;Lo;0;L;;;;;N;;;;; +30EA;KATAKANA LETTER RI;Lo;0;L;;;;;N;;;;; +30EB;KATAKANA LETTER RU;Lo;0;L;;;;;N;;;;; +30EC;KATAKANA LETTER RE;Lo;0;L;;;;;N;;;;; +30ED;KATAKANA LETTER RO;Lo;0;L;;;;;N;;;;; +30EE;KATAKANA LETTER SMALL WA;Lo;0;L;;;;;N;;;;; +30EF;KATAKANA LETTER WA;Lo;0;L;;;;;N;;;;; +30F0;KATAKANA LETTER WI;Lo;0;L;;;;;N;;;;; +30F1;KATAKANA LETTER WE;Lo;0;L;;;;;N;;;;; +30F2;KATAKANA LETTER WO;Lo;0;L;;;;;N;;;;; +30F3;KATAKANA LETTER N;Lo;0;L;;;;;N;;;;; +30F4;KATAKANA LETTER VU;Lo;0;L;30A6 3099;;;;N;;;;; +30F5;KATAKANA LETTER SMALL KA;Lo;0;L;;;;;N;;;;; +30F6;KATAKANA LETTER SMALL KE;Lo;0;L;;;;;N;;;;; +30F7;KATAKANA LETTER VA;Lo;0;L;30EF 3099;;;;N;;;;; +30F8;KATAKANA LETTER VI;Lo;0;L;30F0 3099;;;;N;;;;; +30F9;KATAKANA LETTER VE;Lo;0;L;30F1 3099;;;;N;;;;; +30FA;KATAKANA LETTER VO;Lo;0;L;30F2 3099;;;;N;;;;; +30FB;KATAKANA MIDDLE DOT;Pc;0;ON;;;;;N;;;;; +30FC;KATAKANA-HIRAGANA PROLONGED SOUND MARK;Lm;0;L;;;;;N;;;;; +30FD;KATAKANA ITERATION MARK;Lm;0;L;;;;;N;;;;; +30FE;KATAKANA VOICED ITERATION MARK;Lm;0;L;30FD 3099;;;;N;;;;; +30FF;KATAKANA DIGRAPH KOTO;Lo;0;L;<vertical> 30B3 30C8;;;;N;;;;; +3105;BOPOMOFO LETTER B;Lo;0;L;;;;;N;;;;; +3106;BOPOMOFO LETTER P;Lo;0;L;;;;;N;;;;; +3107;BOPOMOFO LETTER M;Lo;0;L;;;;;N;;;;; +3108;BOPOMOFO LETTER F;Lo;0;L;;;;;N;;;;; +3109;BOPOMOFO LETTER D;Lo;0;L;;;;;N;;;;; +310A;BOPOMOFO LETTER T;Lo;0;L;;;;;N;;;;; +310B;BOPOMOFO LETTER N;Lo;0;L;;;;;N;;;;; +310C;BOPOMOFO LETTER L;Lo;0;L;;;;;N;;;;; +310D;BOPOMOFO LETTER G;Lo;0;L;;;;;N;;;;; +310E;BOPOMOFO LETTER K;Lo;0;L;;;;;N;;;;; +310F;BOPOMOFO LETTER H;Lo;0;L;;;;;N;;;;; +3110;BOPOMOFO LETTER J;Lo;0;L;;;;;N;;;;; +3111;BOPOMOFO LETTER Q;Lo;0;L;;;;;N;;;;; +3112;BOPOMOFO LETTER X;Lo;0;L;;;;;N;;;;; +3113;BOPOMOFO LETTER ZH;Lo;0;L;;;;;N;;;;; +3114;BOPOMOFO LETTER CH;Lo;0;L;;;;;N;;;;; +3115;BOPOMOFO LETTER SH;Lo;0;L;;;;;N;;;;; +3116;BOPOMOFO LETTER R;Lo;0;L;;;;;N;;;;; +3117;BOPOMOFO LETTER Z;Lo;0;L;;;;;N;;;;; +3118;BOPOMOFO LETTER C;Lo;0;L;;;;;N;;;;; +3119;BOPOMOFO LETTER S;Lo;0;L;;;;;N;;;;; +311A;BOPOMOFO LETTER A;Lo;0;L;;;;;N;;;;; +311B;BOPOMOFO LETTER O;Lo;0;L;;;;;N;;;;; +311C;BOPOMOFO LETTER E;Lo;0;L;;;;;N;;;;; +311D;BOPOMOFO LETTER EH;Lo;0;L;;;;;N;;;;; +311E;BOPOMOFO LETTER AI;Lo;0;L;;;;;N;;;;; +311F;BOPOMOFO LETTER EI;Lo;0;L;;;;;N;;;;; +3120;BOPOMOFO LETTER AU;Lo;0;L;;;;;N;;;;; +3121;BOPOMOFO LETTER OU;Lo;0;L;;;;;N;;;;; +3122;BOPOMOFO LETTER AN;Lo;0;L;;;;;N;;;;; +3123;BOPOMOFO LETTER EN;Lo;0;L;;;;;N;;;;; +3124;BOPOMOFO LETTER ANG;Lo;0;L;;;;;N;;;;; +3125;BOPOMOFO LETTER ENG;Lo;0;L;;;;;N;;;;; +3126;BOPOMOFO LETTER ER;Lo;0;L;;;;;N;;;;; +3127;BOPOMOFO LETTER I;Lo;0;L;;;;;N;;;;; +3128;BOPOMOFO LETTER U;Lo;0;L;;;;;N;;;;; +3129;BOPOMOFO LETTER IU;Lo;0;L;;;;;N;;;;; +312A;BOPOMOFO LETTER V;Lo;0;L;;;;;N;;;;; +312B;BOPOMOFO LETTER NG;Lo;0;L;;;;;N;;;;; +312C;BOPOMOFO LETTER GN;Lo;0;L;;;;;N;;;;; +3131;HANGUL LETTER KIYEOK;Lo;0;L;<compat> 1100;;;;N;HANGUL LETTER GIYEOG;;;; +3132;HANGUL LETTER SSANGKIYEOK;Lo;0;L;<compat> 1101;;;;N;HANGUL LETTER SSANG GIYEOG;;;; +3133;HANGUL LETTER KIYEOK-SIOS;Lo;0;L;<compat> 11AA;;;;N;HANGUL LETTER GIYEOG SIOS;;;; +3134;HANGUL LETTER NIEUN;Lo;0;L;<compat> 1102;;;;N;;;;; +3135;HANGUL LETTER NIEUN-CIEUC;Lo;0;L;<compat> 11AC;;;;N;HANGUL LETTER NIEUN JIEUJ;;;; +3136;HANGUL LETTER NIEUN-HIEUH;Lo;0;L;<compat> 11AD;;;;N;HANGUL LETTER NIEUN HIEUH;;;; +3137;HANGUL LETTER TIKEUT;Lo;0;L;<compat> 1103;;;;N;HANGUL LETTER DIGEUD;;;; +3138;HANGUL LETTER SSANGTIKEUT;Lo;0;L;<compat> 1104;;;;N;HANGUL LETTER SSANG DIGEUD;;;; +3139;HANGUL LETTER RIEUL;Lo;0;L;<compat> 1105;;;;N;HANGUL LETTER LIEUL;;;; +313A;HANGUL LETTER RIEUL-KIYEOK;Lo;0;L;<compat> 11B0;;;;N;HANGUL LETTER LIEUL GIYEOG;;;; +313B;HANGUL LETTER RIEUL-MIEUM;Lo;0;L;<compat> 11B1;;;;N;HANGUL LETTER LIEUL MIEUM;;;; +313C;HANGUL LETTER RIEUL-PIEUP;Lo;0;L;<compat> 11B2;;;;N;HANGUL LETTER LIEUL BIEUB;;;; +313D;HANGUL LETTER RIEUL-SIOS;Lo;0;L;<compat> 11B3;;;;N;HANGUL LETTER LIEUL SIOS;;;; +313E;HANGUL LETTER RIEUL-THIEUTH;Lo;0;L;<compat> 11B4;;;;N;HANGUL LETTER LIEUL TIEUT;;;; +313F;HANGUL LETTER RIEUL-PHIEUPH;Lo;0;L;<compat> 11B5;;;;N;HANGUL LETTER LIEUL PIEUP;;;; +3140;HANGUL LETTER RIEUL-HIEUH;Lo;0;L;<compat> 111A;;;;N;HANGUL LETTER LIEUL HIEUH;;;; +3141;HANGUL LETTER MIEUM;Lo;0;L;<compat> 1106;;;;N;;;;; +3142;HANGUL LETTER PIEUP;Lo;0;L;<compat> 1107;;;;N;HANGUL LETTER BIEUB;;;; +3143;HANGUL LETTER SSANGPIEUP;Lo;0;L;<compat> 1108;;;;N;HANGUL LETTER SSANG BIEUB;;;; +3144;HANGUL LETTER PIEUP-SIOS;Lo;0;L;<compat> 1121;;;;N;HANGUL LETTER BIEUB SIOS;;;; +3145;HANGUL LETTER SIOS;Lo;0;L;<compat> 1109;;;;N;;;;; +3146;HANGUL LETTER SSANGSIOS;Lo;0;L;<compat> 110A;;;;N;HANGUL LETTER SSANG SIOS;;;; +3147;HANGUL LETTER IEUNG;Lo;0;L;<compat> 110B;;;;N;;;;; +3148;HANGUL LETTER CIEUC;Lo;0;L;<compat> 110C;;;;N;HANGUL LETTER JIEUJ;;;; +3149;HANGUL LETTER SSANGCIEUC;Lo;0;L;<compat> 110D;;;;N;HANGUL LETTER SSANG JIEUJ;;;; +314A;HANGUL LETTER CHIEUCH;Lo;0;L;<compat> 110E;;;;N;HANGUL LETTER CIEUC;;;; +314B;HANGUL LETTER KHIEUKH;Lo;0;L;<compat> 110F;;;;N;HANGUL LETTER KIYEOK;;;; +314C;HANGUL LETTER THIEUTH;Lo;0;L;<compat> 1110;;;;N;HANGUL LETTER TIEUT;;;; +314D;HANGUL LETTER PHIEUPH;Lo;0;L;<compat> 1111;;;;N;HANGUL LETTER PIEUP;;;; +314E;HANGUL LETTER HIEUH;Lo;0;L;<compat> 1112;;;;N;;;;; +314F;HANGUL LETTER A;Lo;0;L;<compat> 1161;;;;N;;;;; +3150;HANGUL LETTER AE;Lo;0;L;<compat> 1162;;;;N;;;;; +3151;HANGUL LETTER YA;Lo;0;L;<compat> 1163;;;;N;;;;; +3152;HANGUL LETTER YAE;Lo;0;L;<compat> 1164;;;;N;;;;; +3153;HANGUL LETTER EO;Lo;0;L;<compat> 1165;;;;N;;;;; +3154;HANGUL LETTER E;Lo;0;L;<compat> 1166;;;;N;;;;; +3155;HANGUL LETTER YEO;Lo;0;L;<compat> 1167;;;;N;;;;; +3156;HANGUL LETTER YE;Lo;0;L;<compat> 1168;;;;N;;;;; +3157;HANGUL LETTER O;Lo;0;L;<compat> 1169;;;;N;;;;; +3158;HANGUL LETTER WA;Lo;0;L;<compat> 116A;;;;N;;;;; +3159;HANGUL LETTER WAE;Lo;0;L;<compat> 116B;;;;N;;;;; +315A;HANGUL LETTER OE;Lo;0;L;<compat> 116C;;;;N;;;;; +315B;HANGUL LETTER YO;Lo;0;L;<compat> 116D;;;;N;;;;; +315C;HANGUL LETTER U;Lo;0;L;<compat> 116E;;;;N;;;;; +315D;HANGUL LETTER WEO;Lo;0;L;<compat> 116F;;;;N;;;;; +315E;HANGUL LETTER WE;Lo;0;L;<compat> 1170;;;;N;;;;; +315F;HANGUL LETTER WI;Lo;0;L;<compat> 1171;;;;N;;;;; +3160;HANGUL LETTER YU;Lo;0;L;<compat> 1172;;;;N;;;;; +3161;HANGUL LETTER EU;Lo;0;L;<compat> 1173;;;;N;;;;; +3162;HANGUL LETTER YI;Lo;0;L;<compat> 1174;;;;N;;;;; +3163;HANGUL LETTER I;Lo;0;L;<compat> 1175;;;;N;;;;; +3164;HANGUL FILLER;Lo;0;L;<compat> 1160;;;;N;HANGUL CAE OM;;;; +3165;HANGUL LETTER SSANGNIEUN;Lo;0;L;<compat> 1114;;;;N;HANGUL LETTER SSANG NIEUN;;;; +3166;HANGUL LETTER NIEUN-TIKEUT;Lo;0;L;<compat> 1115;;;;N;HANGUL LETTER NIEUN DIGEUD;;;; +3167;HANGUL LETTER NIEUN-SIOS;Lo;0;L;<compat> 11C7;;;;N;HANGUL LETTER NIEUN SIOS;;;; +3168;HANGUL LETTER NIEUN-PANSIOS;Lo;0;L;<compat> 11C8;;;;N;HANGUL LETTER NIEUN BAN CHI EUM;;;; +3169;HANGUL LETTER RIEUL-KIYEOK-SIOS;Lo;0;L;<compat> 11CC;;;;N;HANGUL LETTER LIEUL GIYEOG SIOS;;;; +316A;HANGUL LETTER RIEUL-TIKEUT;Lo;0;L;<compat> 11CE;;;;N;HANGUL LETTER LIEUL DIGEUD;;;; +316B;HANGUL LETTER RIEUL-PIEUP-SIOS;Lo;0;L;<compat> 11D3;;;;N;HANGUL LETTER LIEUL BIEUB SIOS;;;; +316C;HANGUL LETTER RIEUL-PANSIOS;Lo;0;L;<compat> 11D7;;;;N;HANGUL LETTER LIEUL BAN CHI EUM;;;; +316D;HANGUL LETTER RIEUL-YEORINHIEUH;Lo;0;L;<compat> 11D9;;;;N;HANGUL LETTER LIEUL YEOLIN HIEUH;;;; +316E;HANGUL LETTER MIEUM-PIEUP;Lo;0;L;<compat> 111C;;;;N;HANGUL LETTER MIEUM BIEUB;;;; +316F;HANGUL LETTER MIEUM-SIOS;Lo;0;L;<compat> 11DD;;;;N;HANGUL LETTER MIEUM SIOS;;;; +3170;HANGUL LETTER MIEUM-PANSIOS;Lo;0;L;<compat> 11DF;;;;N;HANGUL LETTER BIEUB BAN CHI EUM;;;; +3171;HANGUL LETTER KAPYEOUNMIEUM;Lo;0;L;<compat> 111D;;;;N;HANGUL LETTER MIEUM SUN GYEONG EUM;;;; +3172;HANGUL LETTER PIEUP-KIYEOK;Lo;0;L;<compat> 111E;;;;N;HANGUL LETTER BIEUB GIYEOG;;;; +3173;HANGUL LETTER PIEUP-TIKEUT;Lo;0;L;<compat> 1120;;;;N;HANGUL LETTER BIEUB DIGEUD;;;; +3174;HANGUL LETTER PIEUP-SIOS-KIYEOK;Lo;0;L;<compat> 1122;;;;N;HANGUL LETTER BIEUB SIOS GIYEOG;;;; +3175;HANGUL LETTER PIEUP-SIOS-TIKEUT;Lo;0;L;<compat> 1123;;;;N;HANGUL LETTER BIEUB SIOS DIGEUD;;;; +3176;HANGUL LETTER PIEUP-CIEUC;Lo;0;L;<compat> 1127;;;;N;HANGUL LETTER BIEUB JIEUJ;;;; +3177;HANGUL LETTER PIEUP-THIEUTH;Lo;0;L;<compat> 1129;;;;N;HANGUL LETTER BIEUB TIEUT;;;; +3178;HANGUL LETTER KAPYEOUNPIEUP;Lo;0;L;<compat> 112B;;;;N;HANGUL LETTER BIEUB SUN GYEONG EUM;;;; +3179;HANGUL LETTER KAPYEOUNSSANGPIEUP;Lo;0;L;<compat> 112C;;;;N;HANGUL LETTER SSANG BIEUB SUN GYEONG EUM;;;; +317A;HANGUL LETTER SIOS-KIYEOK;Lo;0;L;<compat> 112D;;;;N;HANGUL LETTER SIOS GIYEOG;;;; +317B;HANGUL LETTER SIOS-NIEUN;Lo;0;L;<compat> 112E;;;;N;HANGUL LETTER SIOS NIEUN;;;; +317C;HANGUL LETTER SIOS-TIKEUT;Lo;0;L;<compat> 112F;;;;N;HANGUL LETTER SIOS DIGEUD;;;; +317D;HANGUL LETTER SIOS-PIEUP;Lo;0;L;<compat> 1132;;;;N;HANGUL LETTER SIOS BIEUB;;;; +317E;HANGUL LETTER SIOS-CIEUC;Lo;0;L;<compat> 1136;;;;N;HANGUL LETTER SIOS JIEUJ;;;; +317F;HANGUL LETTER PANSIOS;Lo;0;L;<compat> 1140;;;;N;HANGUL LETTER BAN CHI EUM;;;; +3180;HANGUL LETTER SSANGIEUNG;Lo;0;L;<compat> 1147;;;;N;HANGUL LETTER SSANG IEUNG;;;; +3181;HANGUL LETTER YESIEUNG;Lo;0;L;<compat> 114C;;;;N;HANGUL LETTER NGIEUNG;;;; +3182;HANGUL LETTER YESIEUNG-SIOS;Lo;0;L;<compat> 11F1;;;;N;HANGUL LETTER NGIEUNG SIOS;;;; +3183;HANGUL LETTER YESIEUNG-PANSIOS;Lo;0;L;<compat> 11F2;;;;N;HANGUL LETTER NGIEUNG BAN CHI EUM;;;; +3184;HANGUL LETTER KAPYEOUNPHIEUPH;Lo;0;L;<compat> 1157;;;;N;HANGUL LETTER PIEUP SUN GYEONG EUM;;;; +3185;HANGUL LETTER SSANGHIEUH;Lo;0;L;<compat> 1158;;;;N;HANGUL LETTER SSANG HIEUH;;;; +3186;HANGUL LETTER YEORINHIEUH;Lo;0;L;<compat> 1159;;;;N;HANGUL LETTER YEOLIN HIEUH;;;; +3187;HANGUL LETTER YO-YA;Lo;0;L;<compat> 1184;;;;N;HANGUL LETTER YOYA;;;; +3188;HANGUL LETTER YO-YAE;Lo;0;L;<compat> 1185;;;;N;HANGUL LETTER YOYAE;;;; +3189;HANGUL LETTER YO-I;Lo;0;L;<compat> 1188;;;;N;HANGUL LETTER YOI;;;; +318A;HANGUL LETTER YU-YEO;Lo;0;L;<compat> 1191;;;;N;HANGUL LETTER YUYEO;;;; +318B;HANGUL LETTER YU-YE;Lo;0;L;<compat> 1192;;;;N;HANGUL LETTER YUYE;;;; +318C;HANGUL LETTER YU-I;Lo;0;L;<compat> 1194;;;;N;HANGUL LETTER YUI;;;; +318D;HANGUL LETTER ARAEA;Lo;0;L;<compat> 119E;;;;N;HANGUL LETTER ALAE A;;;; +318E;HANGUL LETTER ARAEAE;Lo;0;L;<compat> 11A1;;;;N;HANGUL LETTER ALAE AE;;;; +3190;IDEOGRAPHIC ANNOTATION LINKING MARK;So;0;L;;;;;N;KANBUN TATETEN;Kanbun Tateten;;; +3191;IDEOGRAPHIC ANNOTATION REVERSE MARK;So;0;L;;;;;N;KAERITEN RE;Kaeriten;;; +3192;IDEOGRAPHIC ANNOTATION ONE MARK;No;0;L;<super> 4E00;;;1;N;KAERITEN ITI;Kaeriten;;; +3193;IDEOGRAPHIC ANNOTATION TWO MARK;No;0;L;<super> 4E8C;;;2;N;KAERITEN NI;Kaeriten;;; +3194;IDEOGRAPHIC ANNOTATION THREE MARK;No;0;L;<super> 4E09;;;3;N;KAERITEN SAN;Kaeriten;;; +3195;IDEOGRAPHIC ANNOTATION FOUR MARK;No;0;L;<super> 56DB;;;4;N;KAERITEN SI;Kaeriten;;; +3196;IDEOGRAPHIC ANNOTATION TOP MARK;So;0;L;<super> 4E0A;;;;N;KAERITEN ZYOU;Kaeriten;;; +3197;IDEOGRAPHIC ANNOTATION MIDDLE MARK;So;0;L;<super> 4E2D;;;;N;KAERITEN TYUU;Kaeriten;;; +3198;IDEOGRAPHIC ANNOTATION BOTTOM MARK;So;0;L;<super> 4E0B;;;;N;KAERITEN GE;Kaeriten;;; +3199;IDEOGRAPHIC ANNOTATION FIRST MARK;So;0;L;<super> 7532;;;;N;KAERITEN KOU;Kaeriten;;; +319A;IDEOGRAPHIC ANNOTATION SECOND MARK;So;0;L;<super> 4E59;;;;N;KAERITEN OTU;Kaeriten;;; +319B;IDEOGRAPHIC ANNOTATION THIRD MARK;So;0;L;<super> 4E19;;;;N;KAERITEN HEI;Kaeriten;;; +319C;IDEOGRAPHIC ANNOTATION FOURTH MARK;So;0;L;<super> 4E01;;;;N;KAERITEN TEI;Kaeriten;;; +319D;IDEOGRAPHIC ANNOTATION HEAVEN MARK;So;0;L;<super> 5929;;;;N;KAERITEN TEN;Kaeriten;;; +319E;IDEOGRAPHIC ANNOTATION EARTH MARK;So;0;L;<super> 5730;;;;N;KAERITEN TI;Kaeriten;;; +319F;IDEOGRAPHIC ANNOTATION MAN MARK;So;0;L;<super> 4EBA;;;;N;KAERITEN ZIN;Kaeriten;;; +31A0;BOPOMOFO LETTER BU;Lo;0;L;;;;;N;;;;; +31A1;BOPOMOFO LETTER ZI;Lo;0;L;;;;;N;;;;; +31A2;BOPOMOFO LETTER JI;Lo;0;L;;;;;N;;;;; +31A3;BOPOMOFO LETTER GU;Lo;0;L;;;;;N;;;;; +31A4;BOPOMOFO LETTER EE;Lo;0;L;;;;;N;;;;; +31A5;BOPOMOFO LETTER ENN;Lo;0;L;;;;;N;;;;; +31A6;BOPOMOFO LETTER OO;Lo;0;L;;;;;N;;;;; +31A7;BOPOMOFO LETTER ONN;Lo;0;L;;;;;N;;;;; +31A8;BOPOMOFO LETTER IR;Lo;0;L;;;;;N;;;;; +31A9;BOPOMOFO LETTER ANN;Lo;0;L;;;;;N;;;;; +31AA;BOPOMOFO LETTER INN;Lo;0;L;;;;;N;;;;; +31AB;BOPOMOFO LETTER UNN;Lo;0;L;;;;;N;;;;; +31AC;BOPOMOFO LETTER IM;Lo;0;L;;;;;N;;;;; +31AD;BOPOMOFO LETTER NGG;Lo;0;L;;;;;N;;;;; +31AE;BOPOMOFO LETTER AINN;Lo;0;L;;;;;N;;;;; +31AF;BOPOMOFO LETTER AUNN;Lo;0;L;;;;;N;;;;; +31B0;BOPOMOFO LETTER AM;Lo;0;L;;;;;N;;;;; +31B1;BOPOMOFO LETTER OM;Lo;0;L;;;;;N;;;;; +31B2;BOPOMOFO LETTER ONG;Lo;0;L;;;;;N;;;;; +31B3;BOPOMOFO LETTER INNN;Lo;0;L;;;;;N;;;;; +31B4;BOPOMOFO FINAL LETTER P;Lo;0;L;;;;;N;;;;; +31B5;BOPOMOFO FINAL LETTER T;Lo;0;L;;;;;N;;;;; +31B6;BOPOMOFO FINAL LETTER K;Lo;0;L;;;;;N;;;;; +31B7;BOPOMOFO FINAL LETTER H;Lo;0;L;;;;;N;;;;; +31F0;KATAKANA LETTER SMALL KU;Lo;0;L;;;;;N;;;;; +31F1;KATAKANA LETTER SMALL SI;Lo;0;L;;;;;N;;;;; +31F2;KATAKANA LETTER SMALL SU;Lo;0;L;;;;;N;;;;; +31F3;KATAKANA LETTER SMALL TO;Lo;0;L;;;;;N;;;;; +31F4;KATAKANA LETTER SMALL NU;Lo;0;L;;;;;N;;;;; +31F5;KATAKANA LETTER SMALL HA;Lo;0;L;;;;;N;;;;; +31F6;KATAKANA LETTER SMALL HI;Lo;0;L;;;;;N;;;;; +31F7;KATAKANA LETTER SMALL HU;Lo;0;L;;;;;N;;;;; +31F8;KATAKANA LETTER SMALL HE;Lo;0;L;;;;;N;;;;; +31F9;KATAKANA LETTER SMALL HO;Lo;0;L;;;;;N;;;;; +31FA;KATAKANA LETTER SMALL MU;Lo;0;L;;;;;N;;;;; +31FB;KATAKANA LETTER SMALL RA;Lo;0;L;;;;;N;;;;; +31FC;KATAKANA LETTER SMALL RI;Lo;0;L;;;;;N;;;;; +31FD;KATAKANA LETTER SMALL RU;Lo;0;L;;;;;N;;;;; +31FE;KATAKANA LETTER SMALL RE;Lo;0;L;;;;;N;;;;; +31FF;KATAKANA LETTER SMALL RO;Lo;0;L;;;;;N;;;;; +3200;PARENTHESIZED HANGUL KIYEOK;So;0;L;<compat> 0028 1100 0029;;;;N;PARENTHESIZED HANGUL GIYEOG;;;; +3201;PARENTHESIZED HANGUL NIEUN;So;0;L;<compat> 0028 1102 0029;;;;N;;;;; +3202;PARENTHESIZED HANGUL TIKEUT;So;0;L;<compat> 0028 1103 0029;;;;N;PARENTHESIZED HANGUL DIGEUD;;;; +3203;PARENTHESIZED HANGUL RIEUL;So;0;L;<compat> 0028 1105 0029;;;;N;PARENTHESIZED HANGUL LIEUL;;;; +3204;PARENTHESIZED HANGUL MIEUM;So;0;L;<compat> 0028 1106 0029;;;;N;;;;; +3205;PARENTHESIZED HANGUL PIEUP;So;0;L;<compat> 0028 1107 0029;;;;N;PARENTHESIZED HANGUL BIEUB;;;; +3206;PARENTHESIZED HANGUL SIOS;So;0;L;<compat> 0028 1109 0029;;;;N;;;;; +3207;PARENTHESIZED HANGUL IEUNG;So;0;L;<compat> 0028 110B 0029;;;;N;;;;; +3208;PARENTHESIZED HANGUL CIEUC;So;0;L;<compat> 0028 110C 0029;;;;N;PARENTHESIZED HANGUL JIEUJ;;;; +3209;PARENTHESIZED HANGUL CHIEUCH;So;0;L;<compat> 0028 110E 0029;;;;N;PARENTHESIZED HANGUL CIEUC;;;; +320A;PARENTHESIZED HANGUL KHIEUKH;So;0;L;<compat> 0028 110F 0029;;;;N;PARENTHESIZED HANGUL KIYEOK;;;; +320B;PARENTHESIZED HANGUL THIEUTH;So;0;L;<compat> 0028 1110 0029;;;;N;PARENTHESIZED HANGUL TIEUT;;;; +320C;PARENTHESIZED HANGUL PHIEUPH;So;0;L;<compat> 0028 1111 0029;;;;N;PARENTHESIZED HANGUL PIEUP;;;; +320D;PARENTHESIZED HANGUL HIEUH;So;0;L;<compat> 0028 1112 0029;;;;N;;;;; +320E;PARENTHESIZED HANGUL KIYEOK A;So;0;L;<compat> 0028 1100 1161 0029;;;;N;PARENTHESIZED HANGUL GA;;;; +320F;PARENTHESIZED HANGUL NIEUN A;So;0;L;<compat> 0028 1102 1161 0029;;;;N;PARENTHESIZED HANGUL NA;;;; +3210;PARENTHESIZED HANGUL TIKEUT A;So;0;L;<compat> 0028 1103 1161 0029;;;;N;PARENTHESIZED HANGUL DA;;;; +3211;PARENTHESIZED HANGUL RIEUL A;So;0;L;<compat> 0028 1105 1161 0029;;;;N;PARENTHESIZED HANGUL LA;;;; +3212;PARENTHESIZED HANGUL MIEUM A;So;0;L;<compat> 0028 1106 1161 0029;;;;N;PARENTHESIZED HANGUL MA;;;; +3213;PARENTHESIZED HANGUL PIEUP A;So;0;L;<compat> 0028 1107 1161 0029;;;;N;PARENTHESIZED HANGUL BA;;;; +3214;PARENTHESIZED HANGUL SIOS A;So;0;L;<compat> 0028 1109 1161 0029;;;;N;PARENTHESIZED HANGUL SA;;;; +3215;PARENTHESIZED HANGUL IEUNG A;So;0;L;<compat> 0028 110B 1161 0029;;;;N;PARENTHESIZED HANGUL A;;;; +3216;PARENTHESIZED HANGUL CIEUC A;So;0;L;<compat> 0028 110C 1161 0029;;;;N;PARENTHESIZED HANGUL JA;;;; +3217;PARENTHESIZED HANGUL CHIEUCH A;So;0;L;<compat> 0028 110E 1161 0029;;;;N;PARENTHESIZED HANGUL CA;;;; +3218;PARENTHESIZED HANGUL KHIEUKH A;So;0;L;<compat> 0028 110F 1161 0029;;;;N;PARENTHESIZED HANGUL KA;;;; +3219;PARENTHESIZED HANGUL THIEUTH A;So;0;L;<compat> 0028 1110 1161 0029;;;;N;PARENTHESIZED HANGUL TA;;;; +321A;PARENTHESIZED HANGUL PHIEUPH A;So;0;L;<compat> 0028 1111 1161 0029;;;;N;PARENTHESIZED HANGUL PA;;;; +321B;PARENTHESIZED HANGUL HIEUH A;So;0;L;<compat> 0028 1112 1161 0029;;;;N;PARENTHESIZED HANGUL HA;;;; +321C;PARENTHESIZED HANGUL CIEUC U;So;0;L;<compat> 0028 110C 116E 0029;;;;N;PARENTHESIZED HANGUL JU;;;; +3220;PARENTHESIZED IDEOGRAPH ONE;No;0;L;<compat> 0028 4E00 0029;;;1;N;;;;; +3221;PARENTHESIZED IDEOGRAPH TWO;No;0;L;<compat> 0028 4E8C 0029;;;2;N;;;;; +3222;PARENTHESIZED IDEOGRAPH THREE;No;0;L;<compat> 0028 4E09 0029;;;3;N;;;;; +3223;PARENTHESIZED IDEOGRAPH FOUR;No;0;L;<compat> 0028 56DB 0029;;;4;N;;;;; +3224;PARENTHESIZED IDEOGRAPH FIVE;No;0;L;<compat> 0028 4E94 0029;;;5;N;;;;; +3225;PARENTHESIZED IDEOGRAPH SIX;No;0;L;<compat> 0028 516D 0029;;;6;N;;;;; +3226;PARENTHESIZED IDEOGRAPH SEVEN;No;0;L;<compat> 0028 4E03 0029;;;7;N;;;;; +3227;PARENTHESIZED IDEOGRAPH EIGHT;No;0;L;<compat> 0028 516B 0029;;;8;N;;;;; +3228;PARENTHESIZED IDEOGRAPH NINE;No;0;L;<compat> 0028 4E5D 0029;;;9;N;;;;; +3229;PARENTHESIZED IDEOGRAPH TEN;No;0;L;<compat> 0028 5341 0029;;;10;N;;;;; +322A;PARENTHESIZED IDEOGRAPH MOON;So;0;L;<compat> 0028 6708 0029;;;;N;;;;; +322B;PARENTHESIZED IDEOGRAPH FIRE;So;0;L;<compat> 0028 706B 0029;;;;N;;;;; +322C;PARENTHESIZED IDEOGRAPH WATER;So;0;L;<compat> 0028 6C34 0029;;;;N;;;;; +322D;PARENTHESIZED IDEOGRAPH WOOD;So;0;L;<compat> 0028 6728 0029;;;;N;;;;; +322E;PARENTHESIZED IDEOGRAPH METAL;So;0;L;<compat> 0028 91D1 0029;;;;N;;;;; +322F;PARENTHESIZED IDEOGRAPH EARTH;So;0;L;<compat> 0028 571F 0029;;;;N;;;;; +3230;PARENTHESIZED IDEOGRAPH SUN;So;0;L;<compat> 0028 65E5 0029;;;;N;;;;; +3231;PARENTHESIZED IDEOGRAPH STOCK;So;0;L;<compat> 0028 682A 0029;;;;N;;;;; +3232;PARENTHESIZED IDEOGRAPH HAVE;So;0;L;<compat> 0028 6709 0029;;;;N;;;;; +3233;PARENTHESIZED IDEOGRAPH SOCIETY;So;0;L;<compat> 0028 793E 0029;;;;N;;;;; +3234;PARENTHESIZED IDEOGRAPH NAME;So;0;L;<compat> 0028 540D 0029;;;;N;;;;; +3235;PARENTHESIZED IDEOGRAPH SPECIAL;So;0;L;<compat> 0028 7279 0029;;;;N;;;;; +3236;PARENTHESIZED IDEOGRAPH FINANCIAL;So;0;L;<compat> 0028 8CA1 0029;;;;N;;;;; +3237;PARENTHESIZED IDEOGRAPH CONGRATULATION;So;0;L;<compat> 0028 795D 0029;;;;N;;;;; +3238;PARENTHESIZED IDEOGRAPH LABOR;So;0;L;<compat> 0028 52B4 0029;;;;N;;;;; +3239;PARENTHESIZED IDEOGRAPH REPRESENT;So;0;L;<compat> 0028 4EE3 0029;;;;N;;;;; +323A;PARENTHESIZED IDEOGRAPH CALL;So;0;L;<compat> 0028 547C 0029;;;;N;;;;; +323B;PARENTHESIZED IDEOGRAPH STUDY;So;0;L;<compat> 0028 5B66 0029;;;;N;;;;; +323C;PARENTHESIZED IDEOGRAPH SUPERVISE;So;0;L;<compat> 0028 76E3 0029;;;;N;;;;; +323D;PARENTHESIZED IDEOGRAPH ENTERPRISE;So;0;L;<compat> 0028 4F01 0029;;;;N;;;;; +323E;PARENTHESIZED IDEOGRAPH RESOURCE;So;0;L;<compat> 0028 8CC7 0029;;;;N;;;;; +323F;PARENTHESIZED IDEOGRAPH ALLIANCE;So;0;L;<compat> 0028 5354 0029;;;;N;;;;; +3240;PARENTHESIZED IDEOGRAPH FESTIVAL;So;0;L;<compat> 0028 796D 0029;;;;N;;;;; +3241;PARENTHESIZED IDEOGRAPH REST;So;0;L;<compat> 0028 4F11 0029;;;;N;;;;; +3242;PARENTHESIZED IDEOGRAPH SELF;So;0;L;<compat> 0028 81EA 0029;;;;N;;;;; +3243;PARENTHESIZED IDEOGRAPH REACH;So;0;L;<compat> 0028 81F3 0029;;;;N;;;;; +3251;CIRCLED NUMBER TWENTY ONE;No;0;ON;<circle> 0032 0031;;;21;N;;;;; +3252;CIRCLED NUMBER TWENTY TWO;No;0;ON;<circle> 0032 0032;;;22;N;;;;; +3253;CIRCLED NUMBER TWENTY THREE;No;0;ON;<circle> 0032 0033;;;23;N;;;;; +3254;CIRCLED NUMBER TWENTY FOUR;No;0;ON;<circle> 0032 0034;;;24;N;;;;; +3255;CIRCLED NUMBER TWENTY FIVE;No;0;ON;<circle> 0032 0035;;;25;N;;;;; +3256;CIRCLED NUMBER TWENTY SIX;No;0;ON;<circle> 0032 0036;;;26;N;;;;; +3257;CIRCLED NUMBER TWENTY SEVEN;No;0;ON;<circle> 0032 0037;;;27;N;;;;; +3258;CIRCLED NUMBER TWENTY EIGHT;No;0;ON;<circle> 0032 0038;;;28;N;;;;; +3259;CIRCLED NUMBER TWENTY NINE;No;0;ON;<circle> 0032 0039;;;29;N;;;;; +325A;CIRCLED NUMBER THIRTY;No;0;ON;<circle> 0033 0030;;;30;N;;;;; +325B;CIRCLED NUMBER THIRTY ONE;No;0;ON;<circle> 0033 0031;;;31;N;;;;; +325C;CIRCLED NUMBER THIRTY TWO;No;0;ON;<circle> 0033 0032;;;32;N;;;;; +325D;CIRCLED NUMBER THIRTY THREE;No;0;ON;<circle> 0033 0033;;;33;N;;;;; +325E;CIRCLED NUMBER THIRTY FOUR;No;0;ON;<circle> 0033 0034;;;34;N;;;;; +325F;CIRCLED NUMBER THIRTY FIVE;No;0;ON;<circle> 0033 0035;;;35;N;;;;; +3260;CIRCLED HANGUL KIYEOK;So;0;L;<circle> 1100;;;;N;CIRCLED HANGUL GIYEOG;;;; +3261;CIRCLED HANGUL NIEUN;So;0;L;<circle> 1102;;;;N;;;;; +3262;CIRCLED HANGUL TIKEUT;So;0;L;<circle> 1103;;;;N;CIRCLED HANGUL DIGEUD;;;; +3263;CIRCLED HANGUL RIEUL;So;0;L;<circle> 1105;;;;N;CIRCLED HANGUL LIEUL;;;; +3264;CIRCLED HANGUL MIEUM;So;0;L;<circle> 1106;;;;N;;;;; +3265;CIRCLED HANGUL PIEUP;So;0;L;<circle> 1107;;;;N;CIRCLED HANGUL BIEUB;;;; +3266;CIRCLED HANGUL SIOS;So;0;L;<circle> 1109;;;;N;;;;; +3267;CIRCLED HANGUL IEUNG;So;0;L;<circle> 110B;;;;N;;;;; +3268;CIRCLED HANGUL CIEUC;So;0;L;<circle> 110C;;;;N;CIRCLED HANGUL JIEUJ;;;; +3269;CIRCLED HANGUL CHIEUCH;So;0;L;<circle> 110E;;;;N;CIRCLED HANGUL CIEUC;;;; +326A;CIRCLED HANGUL KHIEUKH;So;0;L;<circle> 110F;;;;N;CIRCLED HANGUL KIYEOK;;;; +326B;CIRCLED HANGUL THIEUTH;So;0;L;<circle> 1110;;;;N;CIRCLED HANGUL TIEUT;;;; +326C;CIRCLED HANGUL PHIEUPH;So;0;L;<circle> 1111;;;;N;CIRCLED HANGUL PIEUP;;;; +326D;CIRCLED HANGUL HIEUH;So;0;L;<circle> 1112;;;;N;;;;; +326E;CIRCLED HANGUL KIYEOK A;So;0;L;<circle> 1100 1161;;;;N;CIRCLED HANGUL GA;;;; +326F;CIRCLED HANGUL NIEUN A;So;0;L;<circle> 1102 1161;;;;N;CIRCLED HANGUL NA;;;; +3270;CIRCLED HANGUL TIKEUT A;So;0;L;<circle> 1103 1161;;;;N;CIRCLED HANGUL DA;;;; +3271;CIRCLED HANGUL RIEUL A;So;0;L;<circle> 1105 1161;;;;N;CIRCLED HANGUL LA;;;; +3272;CIRCLED HANGUL MIEUM A;So;0;L;<circle> 1106 1161;;;;N;CIRCLED HANGUL MA;;;; +3273;CIRCLED HANGUL PIEUP A;So;0;L;<circle> 1107 1161;;;;N;CIRCLED HANGUL BA;;;; +3274;CIRCLED HANGUL SIOS A;So;0;L;<circle> 1109 1161;;;;N;CIRCLED HANGUL SA;;;; +3275;CIRCLED HANGUL IEUNG A;So;0;L;<circle> 110B 1161;;;;N;CIRCLED HANGUL A;;;; +3276;CIRCLED HANGUL CIEUC A;So;0;L;<circle> 110C 1161;;;;N;CIRCLED HANGUL JA;;;; +3277;CIRCLED HANGUL CHIEUCH A;So;0;L;<circle> 110E 1161;;;;N;CIRCLED HANGUL CA;;;; +3278;CIRCLED HANGUL KHIEUKH A;So;0;L;<circle> 110F 1161;;;;N;CIRCLED HANGUL KA;;;; +3279;CIRCLED HANGUL THIEUTH A;So;0;L;<circle> 1110 1161;;;;N;CIRCLED HANGUL TA;;;; +327A;CIRCLED HANGUL PHIEUPH A;So;0;L;<circle> 1111 1161;;;;N;CIRCLED HANGUL PA;;;; +327B;CIRCLED HANGUL HIEUH A;So;0;L;<circle> 1112 1161;;;;N;CIRCLED HANGUL HA;;;; +327F;KOREAN STANDARD SYMBOL;So;0;L;;;;;N;;;;; +3280;CIRCLED IDEOGRAPH ONE;No;0;L;<circle> 4E00;;;1;N;;;;; +3281;CIRCLED IDEOGRAPH TWO;No;0;L;<circle> 4E8C;;;2;N;;;;; +3282;CIRCLED IDEOGRAPH THREE;No;0;L;<circle> 4E09;;;3;N;;;;; +3283;CIRCLED IDEOGRAPH FOUR;No;0;L;<circle> 56DB;;;4;N;;;;; +3284;CIRCLED IDEOGRAPH FIVE;No;0;L;<circle> 4E94;;;5;N;;;;; +3285;CIRCLED IDEOGRAPH SIX;No;0;L;<circle> 516D;;;6;N;;;;; +3286;CIRCLED IDEOGRAPH SEVEN;No;0;L;<circle> 4E03;;;7;N;;;;; +3287;CIRCLED IDEOGRAPH EIGHT;No;0;L;<circle> 516B;;;8;N;;;;; +3288;CIRCLED IDEOGRAPH NINE;No;0;L;<circle> 4E5D;;;9;N;;;;; +3289;CIRCLED IDEOGRAPH TEN;No;0;L;<circle> 5341;;;10;N;;;;; +328A;CIRCLED IDEOGRAPH MOON;So;0;L;<circle> 6708;;;;N;;;;; +328B;CIRCLED IDEOGRAPH FIRE;So;0;L;<circle> 706B;;;;N;;;;; +328C;CIRCLED IDEOGRAPH WATER;So;0;L;<circle> 6C34;;;;N;;;;; +328D;CIRCLED IDEOGRAPH WOOD;So;0;L;<circle> 6728;;;;N;;;;; +328E;CIRCLED IDEOGRAPH METAL;So;0;L;<circle> 91D1;;;;N;;;;; +328F;CIRCLED IDEOGRAPH EARTH;So;0;L;<circle> 571F;;;;N;;;;; +3290;CIRCLED IDEOGRAPH SUN;So;0;L;<circle> 65E5;;;;N;;;;; +3291;CIRCLED IDEOGRAPH STOCK;So;0;L;<circle> 682A;;;;N;;;;; +3292;CIRCLED IDEOGRAPH HAVE;So;0;L;<circle> 6709;;;;N;;;;; +3293;CIRCLED IDEOGRAPH SOCIETY;So;0;L;<circle> 793E;;;;N;;;;; +3294;CIRCLED IDEOGRAPH NAME;So;0;L;<circle> 540D;;;;N;;;;; +3295;CIRCLED IDEOGRAPH SPECIAL;So;0;L;<circle> 7279;;;;N;;;;; +3296;CIRCLED IDEOGRAPH FINANCIAL;So;0;L;<circle> 8CA1;;;;N;;;;; +3297;CIRCLED IDEOGRAPH CONGRATULATION;So;0;L;<circle> 795D;;;;N;;;;; +3298;CIRCLED IDEOGRAPH LABOR;So;0;L;<circle> 52B4;;;;N;;;;; +3299;CIRCLED IDEOGRAPH SECRET;So;0;L;<circle> 79D8;;;;N;;;;; +329A;CIRCLED IDEOGRAPH MALE;So;0;L;<circle> 7537;;;;N;;;;; +329B;CIRCLED IDEOGRAPH FEMALE;So;0;L;<circle> 5973;;;;N;;;;; +329C;CIRCLED IDEOGRAPH SUITABLE;So;0;L;<circle> 9069;;;;N;;;;; +329D;CIRCLED IDEOGRAPH EXCELLENT;So;0;L;<circle> 512A;;;;N;;;;; +329E;CIRCLED IDEOGRAPH PRINT;So;0;L;<circle> 5370;;;;N;;;;; +329F;CIRCLED IDEOGRAPH ATTENTION;So;0;L;<circle> 6CE8;;;;N;;;;; +32A0;CIRCLED IDEOGRAPH ITEM;So;0;L;<circle> 9805;;;;N;;;;; +32A1;CIRCLED IDEOGRAPH REST;So;0;L;<circle> 4F11;;;;N;;;;; +32A2;CIRCLED IDEOGRAPH COPY;So;0;L;<circle> 5199;;;;N;;;;; +32A3;CIRCLED IDEOGRAPH CORRECT;So;0;L;<circle> 6B63;;;;N;;;;; +32A4;CIRCLED IDEOGRAPH HIGH;So;0;L;<circle> 4E0A;;;;N;;;;; +32A5;CIRCLED IDEOGRAPH CENTRE;So;0;L;<circle> 4E2D;;;;N;CIRCLED IDEOGRAPH CENTER;;;; +32A6;CIRCLED IDEOGRAPH LOW;So;0;L;<circle> 4E0B;;;;N;;;;; +32A7;CIRCLED IDEOGRAPH LEFT;So;0;L;<circle> 5DE6;;;;N;;;;; +32A8;CIRCLED IDEOGRAPH RIGHT;So;0;L;<circle> 53F3;;;;N;;;;; +32A9;CIRCLED IDEOGRAPH MEDICINE;So;0;L;<circle> 533B;;;;N;;;;; +32AA;CIRCLED IDEOGRAPH RELIGION;So;0;L;<circle> 5B97;;;;N;;;;; +32AB;CIRCLED IDEOGRAPH STUDY;So;0;L;<circle> 5B66;;;;N;;;;; +32AC;CIRCLED IDEOGRAPH SUPERVISE;So;0;L;<circle> 76E3;;;;N;;;;; +32AD;CIRCLED IDEOGRAPH ENTERPRISE;So;0;L;<circle> 4F01;;;;N;;;;; +32AE;CIRCLED IDEOGRAPH RESOURCE;So;0;L;<circle> 8CC7;;;;N;;;;; +32AF;CIRCLED IDEOGRAPH ALLIANCE;So;0;L;<circle> 5354;;;;N;;;;; +32B0;CIRCLED IDEOGRAPH NIGHT;So;0;L;<circle> 591C;;;;N;;;;; +32B1;CIRCLED NUMBER THIRTY SIX;No;0;ON;<circle> 0033 0036;;;36;N;;;;; +32B2;CIRCLED NUMBER THIRTY SEVEN;No;0;ON;<circle> 0033 0037;;;37;N;;;;; +32B3;CIRCLED NUMBER THIRTY EIGHT;No;0;ON;<circle> 0033 0038;;;38;N;;;;; +32B4;CIRCLED NUMBER THIRTY NINE;No;0;ON;<circle> 0033 0039;;;39;N;;;;; +32B5;CIRCLED NUMBER FORTY;No;0;ON;<circle> 0034 0030;;;40;N;;;;; +32B6;CIRCLED NUMBER FORTY ONE;No;0;ON;<circle> 0034 0031;;;41;N;;;;; +32B7;CIRCLED NUMBER FORTY TWO;No;0;ON;<circle> 0034 0032;;;42;N;;;;; +32B8;CIRCLED NUMBER FORTY THREE;No;0;ON;<circle> 0034 0033;;;43;N;;;;; +32B9;CIRCLED NUMBER FORTY FOUR;No;0;ON;<circle> 0034 0034;;;44;N;;;;; +32BA;CIRCLED NUMBER FORTY FIVE;No;0;ON;<circle> 0034 0035;;;45;N;;;;; +32BB;CIRCLED NUMBER FORTY SIX;No;0;ON;<circle> 0034 0036;;;46;N;;;;; +32BC;CIRCLED NUMBER FORTY SEVEN;No;0;ON;<circle> 0034 0037;;;47;N;;;;; +32BD;CIRCLED NUMBER FORTY EIGHT;No;0;ON;<circle> 0034 0038;;;48;N;;;;; +32BE;CIRCLED NUMBER FORTY NINE;No;0;ON;<circle> 0034 0039;;;49;N;;;;; +32BF;CIRCLED NUMBER FIFTY;No;0;ON;<circle> 0035 0030;;;50;N;;;;; +32C0;IDEOGRAPHIC TELEGRAPH SYMBOL FOR JANUARY;So;0;L;<compat> 0031 6708;;;;N;;;;; +32C1;IDEOGRAPHIC TELEGRAPH SYMBOL FOR FEBRUARY;So;0;L;<compat> 0032 6708;;;;N;;;;; +32C2;IDEOGRAPHIC TELEGRAPH SYMBOL FOR MARCH;So;0;L;<compat> 0033 6708;;;;N;;;;; +32C3;IDEOGRAPHIC TELEGRAPH SYMBOL FOR APRIL;So;0;L;<compat> 0034 6708;;;;N;;;;; +32C4;IDEOGRAPHIC TELEGRAPH SYMBOL FOR MAY;So;0;L;<compat> 0035 6708;;;;N;;;;; +32C5;IDEOGRAPHIC TELEGRAPH SYMBOL FOR JUNE;So;0;L;<compat> 0036 6708;;;;N;;;;; +32C6;IDEOGRAPHIC TELEGRAPH SYMBOL FOR JULY;So;0;L;<compat> 0037 6708;;;;N;;;;; +32C7;IDEOGRAPHIC TELEGRAPH SYMBOL FOR AUGUST;So;0;L;<compat> 0038 6708;;;;N;;;;; +32C8;IDEOGRAPHIC TELEGRAPH SYMBOL FOR SEPTEMBER;So;0;L;<compat> 0039 6708;;;;N;;;;; +32C9;IDEOGRAPHIC TELEGRAPH SYMBOL FOR OCTOBER;So;0;L;<compat> 0031 0030 6708;;;;N;;;;; +32CA;IDEOGRAPHIC TELEGRAPH SYMBOL FOR NOVEMBER;So;0;L;<compat> 0031 0031 6708;;;;N;;;;; +32CB;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DECEMBER;So;0;L;<compat> 0031 0032 6708;;;;N;;;;; +32D0;CIRCLED KATAKANA A;So;0;L;<circle> 30A2;;;;N;;;;; +32D1;CIRCLED KATAKANA I;So;0;L;<circle> 30A4;;;;N;;;;; +32D2;CIRCLED KATAKANA U;So;0;L;<circle> 30A6;;;;N;;;;; +32D3;CIRCLED KATAKANA E;So;0;L;<circle> 30A8;;;;N;;;;; +32D4;CIRCLED KATAKANA O;So;0;L;<circle> 30AA;;;;N;;;;; +32D5;CIRCLED KATAKANA KA;So;0;L;<circle> 30AB;;;;N;;;;; +32D6;CIRCLED KATAKANA KI;So;0;L;<circle> 30AD;;;;N;;;;; +32D7;CIRCLED KATAKANA KU;So;0;L;<circle> 30AF;;;;N;;;;; +32D8;CIRCLED KATAKANA KE;So;0;L;<circle> 30B1;;;;N;;;;; +32D9;CIRCLED KATAKANA KO;So;0;L;<circle> 30B3;;;;N;;;;; +32DA;CIRCLED KATAKANA SA;So;0;L;<circle> 30B5;;;;N;;;;; +32DB;CIRCLED KATAKANA SI;So;0;L;<circle> 30B7;;;;N;;;;; +32DC;CIRCLED KATAKANA SU;So;0;L;<circle> 30B9;;;;N;;;;; +32DD;CIRCLED KATAKANA SE;So;0;L;<circle> 30BB;;;;N;;;;; +32DE;CIRCLED KATAKANA SO;So;0;L;<circle> 30BD;;;;N;;;;; +32DF;CIRCLED KATAKANA TA;So;0;L;<circle> 30BF;;;;N;;;;; +32E0;CIRCLED KATAKANA TI;So;0;L;<circle> 30C1;;;;N;;;;; +32E1;CIRCLED KATAKANA TU;So;0;L;<circle> 30C4;;;;N;;;;; +32E2;CIRCLED KATAKANA TE;So;0;L;<circle> 30C6;;;;N;;;;; +32E3;CIRCLED KATAKANA TO;So;0;L;<circle> 30C8;;;;N;;;;; +32E4;CIRCLED KATAKANA NA;So;0;L;<circle> 30CA;;;;N;;;;; +32E5;CIRCLED KATAKANA NI;So;0;L;<circle> 30CB;;;;N;;;;; +32E6;CIRCLED KATAKANA NU;So;0;L;<circle> 30CC;;;;N;;;;; +32E7;CIRCLED KATAKANA NE;So;0;L;<circle> 30CD;;;;N;;;;; +32E8;CIRCLED KATAKANA NO;So;0;L;<circle> 30CE;;;;N;;;;; +32E9;CIRCLED KATAKANA HA;So;0;L;<circle> 30CF;;;;N;;;;; +32EA;CIRCLED KATAKANA HI;So;0;L;<circle> 30D2;;;;N;;;;; +32EB;CIRCLED KATAKANA HU;So;0;L;<circle> 30D5;;;;N;;;;; +32EC;CIRCLED KATAKANA HE;So;0;L;<circle> 30D8;;;;N;;;;; +32ED;CIRCLED KATAKANA HO;So;0;L;<circle> 30DB;;;;N;;;;; +32EE;CIRCLED KATAKANA MA;So;0;L;<circle> 30DE;;;;N;;;;; +32EF;CIRCLED KATAKANA MI;So;0;L;<circle> 30DF;;;;N;;;;; +32F0;CIRCLED KATAKANA MU;So;0;L;<circle> 30E0;;;;N;;;;; +32F1;CIRCLED KATAKANA ME;So;0;L;<circle> 30E1;;;;N;;;;; +32F2;CIRCLED KATAKANA MO;So;0;L;<circle> 30E2;;;;N;;;;; +32F3;CIRCLED KATAKANA YA;So;0;L;<circle> 30E4;;;;N;;;;; +32F4;CIRCLED KATAKANA YU;So;0;L;<circle> 30E6;;;;N;;;;; +32F5;CIRCLED KATAKANA YO;So;0;L;<circle> 30E8;;;;N;;;;; +32F6;CIRCLED KATAKANA RA;So;0;L;<circle> 30E9;;;;N;;;;; +32F7;CIRCLED KATAKANA RI;So;0;L;<circle> 30EA;;;;N;;;;; +32F8;CIRCLED KATAKANA RU;So;0;L;<circle> 30EB;;;;N;;;;; +32F9;CIRCLED KATAKANA RE;So;0;L;<circle> 30EC;;;;N;;;;; +32FA;CIRCLED KATAKANA RO;So;0;L;<circle> 30ED;;;;N;;;;; +32FB;CIRCLED KATAKANA WA;So;0;L;<circle> 30EF;;;;N;;;;; +32FC;CIRCLED KATAKANA WI;So;0;L;<circle> 30F0;;;;N;;;;; +32FD;CIRCLED KATAKANA WE;So;0;L;<circle> 30F1;;;;N;;;;; +32FE;CIRCLED KATAKANA WO;So;0;L;<circle> 30F2;;;;N;;;;; +3300;SQUARE APAATO;So;0;L;<square> 30A2 30D1 30FC 30C8;;;;N;SQUARED APAATO;;;; +3301;SQUARE ARUHUA;So;0;L;<square> 30A2 30EB 30D5 30A1;;;;N;SQUARED ARUHUA;;;; +3302;SQUARE ANPEA;So;0;L;<square> 30A2 30F3 30DA 30A2;;;;N;SQUARED ANPEA;;;; +3303;SQUARE AARU;So;0;L;<square> 30A2 30FC 30EB;;;;N;SQUARED AARU;;;; +3304;SQUARE ININGU;So;0;L;<square> 30A4 30CB 30F3 30B0;;;;N;SQUARED ININGU;;;; +3305;SQUARE INTI;So;0;L;<square> 30A4 30F3 30C1;;;;N;SQUARED INTI;;;; +3306;SQUARE UON;So;0;L;<square> 30A6 30A9 30F3;;;;N;SQUARED UON;;;; +3307;SQUARE ESUKUUDO;So;0;L;<square> 30A8 30B9 30AF 30FC 30C9;;;;N;SQUARED ESUKUUDO;;;; +3308;SQUARE EEKAA;So;0;L;<square> 30A8 30FC 30AB 30FC;;;;N;SQUARED EEKAA;;;; +3309;SQUARE ONSU;So;0;L;<square> 30AA 30F3 30B9;;;;N;SQUARED ONSU;;;; +330A;SQUARE OOMU;So;0;L;<square> 30AA 30FC 30E0;;;;N;SQUARED OOMU;;;; +330B;SQUARE KAIRI;So;0;L;<square> 30AB 30A4 30EA;;;;N;SQUARED KAIRI;;;; +330C;SQUARE KARATTO;So;0;L;<square> 30AB 30E9 30C3 30C8;;;;N;SQUARED KARATTO;;;; +330D;SQUARE KARORII;So;0;L;<square> 30AB 30ED 30EA 30FC;;;;N;SQUARED KARORII;;;; +330E;SQUARE GARON;So;0;L;<square> 30AC 30ED 30F3;;;;N;SQUARED GARON;;;; +330F;SQUARE GANMA;So;0;L;<square> 30AC 30F3 30DE;;;;N;SQUARED GANMA;;;; +3310;SQUARE GIGA;So;0;L;<square> 30AE 30AC;;;;N;SQUARED GIGA;;;; +3311;SQUARE GINII;So;0;L;<square> 30AE 30CB 30FC;;;;N;SQUARED GINII;;;; +3312;SQUARE KYURII;So;0;L;<square> 30AD 30E5 30EA 30FC;;;;N;SQUARED KYURII;;;; +3313;SQUARE GIRUDAA;So;0;L;<square> 30AE 30EB 30C0 30FC;;;;N;SQUARED GIRUDAA;;;; +3314;SQUARE KIRO;So;0;L;<square> 30AD 30ED;;;;N;SQUARED KIRO;;;; +3315;SQUARE KIROGURAMU;So;0;L;<square> 30AD 30ED 30B0 30E9 30E0;;;;N;SQUARED KIROGURAMU;;;; +3316;SQUARE KIROMEETORU;So;0;L;<square> 30AD 30ED 30E1 30FC 30C8 30EB;;;;N;SQUARED KIROMEETORU;;;; +3317;SQUARE KIROWATTO;So;0;L;<square> 30AD 30ED 30EF 30C3 30C8;;;;N;SQUARED KIROWATTO;;;; +3318;SQUARE GURAMU;So;0;L;<square> 30B0 30E9 30E0;;;;N;SQUARED GURAMU;;;; +3319;SQUARE GURAMUTON;So;0;L;<square> 30B0 30E9 30E0 30C8 30F3;;;;N;SQUARED GURAMUTON;;;; +331A;SQUARE KURUZEIRO;So;0;L;<square> 30AF 30EB 30BC 30A4 30ED;;;;N;SQUARED KURUZEIRO;;;; +331B;SQUARE KUROONE;So;0;L;<square> 30AF 30ED 30FC 30CD;;;;N;SQUARED KUROONE;;;; +331C;SQUARE KEESU;So;0;L;<square> 30B1 30FC 30B9;;;;N;SQUARED KEESU;;;; +331D;SQUARE KORUNA;So;0;L;<square> 30B3 30EB 30CA;;;;N;SQUARED KORUNA;;;; +331E;SQUARE KOOPO;So;0;L;<square> 30B3 30FC 30DD;;;;N;SQUARED KOOPO;;;; +331F;SQUARE SAIKURU;So;0;L;<square> 30B5 30A4 30AF 30EB;;;;N;SQUARED SAIKURU;;;; +3320;SQUARE SANTIIMU;So;0;L;<square> 30B5 30F3 30C1 30FC 30E0;;;;N;SQUARED SANTIIMU;;;; +3321;SQUARE SIRINGU;So;0;L;<square> 30B7 30EA 30F3 30B0;;;;N;SQUARED SIRINGU;;;; +3322;SQUARE SENTI;So;0;L;<square> 30BB 30F3 30C1;;;;N;SQUARED SENTI;;;; +3323;SQUARE SENTO;So;0;L;<square> 30BB 30F3 30C8;;;;N;SQUARED SENTO;;;; +3324;SQUARE DAASU;So;0;L;<square> 30C0 30FC 30B9;;;;N;SQUARED DAASU;;;; +3325;SQUARE DESI;So;0;L;<square> 30C7 30B7;;;;N;SQUARED DESI;;;; +3326;SQUARE DORU;So;0;L;<square> 30C9 30EB;;;;N;SQUARED DORU;;;; +3327;SQUARE TON;So;0;L;<square> 30C8 30F3;;;;N;SQUARED TON;;;; +3328;SQUARE NANO;So;0;L;<square> 30CA 30CE;;;;N;SQUARED NANO;;;; +3329;SQUARE NOTTO;So;0;L;<square> 30CE 30C3 30C8;;;;N;SQUARED NOTTO;;;; +332A;SQUARE HAITU;So;0;L;<square> 30CF 30A4 30C4;;;;N;SQUARED HAITU;;;; +332B;SQUARE PAASENTO;So;0;L;<square> 30D1 30FC 30BB 30F3 30C8;;;;N;SQUARED PAASENTO;;;; +332C;SQUARE PAATU;So;0;L;<square> 30D1 30FC 30C4;;;;N;SQUARED PAATU;;;; +332D;SQUARE BAARERU;So;0;L;<square> 30D0 30FC 30EC 30EB;;;;N;SQUARED BAARERU;;;; +332E;SQUARE PIASUTORU;So;0;L;<square> 30D4 30A2 30B9 30C8 30EB;;;;N;SQUARED PIASUTORU;;;; +332F;SQUARE PIKURU;So;0;L;<square> 30D4 30AF 30EB;;;;N;SQUARED PIKURU;;;; +3330;SQUARE PIKO;So;0;L;<square> 30D4 30B3;;;;N;SQUARED PIKO;;;; +3331;SQUARE BIRU;So;0;L;<square> 30D3 30EB;;;;N;SQUARED BIRU;;;; +3332;SQUARE HUARADDO;So;0;L;<square> 30D5 30A1 30E9 30C3 30C9;;;;N;SQUARED HUARADDO;;;; +3333;SQUARE HUIITO;So;0;L;<square> 30D5 30A3 30FC 30C8;;;;N;SQUARED HUIITO;;;; +3334;SQUARE BUSSYERU;So;0;L;<square> 30D6 30C3 30B7 30A7 30EB;;;;N;SQUARED BUSSYERU;;;; +3335;SQUARE HURAN;So;0;L;<square> 30D5 30E9 30F3;;;;N;SQUARED HURAN;;;; +3336;SQUARE HEKUTAARU;So;0;L;<square> 30D8 30AF 30BF 30FC 30EB;;;;N;SQUARED HEKUTAARU;;;; +3337;SQUARE PESO;So;0;L;<square> 30DA 30BD;;;;N;SQUARED PESO;;;; +3338;SQUARE PENIHI;So;0;L;<square> 30DA 30CB 30D2;;;;N;SQUARED PENIHI;;;; +3339;SQUARE HERUTU;So;0;L;<square> 30D8 30EB 30C4;;;;N;SQUARED HERUTU;;;; +333A;SQUARE PENSU;So;0;L;<square> 30DA 30F3 30B9;;;;N;SQUARED PENSU;;;; +333B;SQUARE PEEZI;So;0;L;<square> 30DA 30FC 30B8;;;;N;SQUARED PEEZI;;;; +333C;SQUARE BEETA;So;0;L;<square> 30D9 30FC 30BF;;;;N;SQUARED BEETA;;;; +333D;SQUARE POINTO;So;0;L;<square> 30DD 30A4 30F3 30C8;;;;N;SQUARED POINTO;;;; +333E;SQUARE BORUTO;So;0;L;<square> 30DC 30EB 30C8;;;;N;SQUARED BORUTO;;;; +333F;SQUARE HON;So;0;L;<square> 30DB 30F3;;;;N;SQUARED HON;;;; +3340;SQUARE PONDO;So;0;L;<square> 30DD 30F3 30C9;;;;N;SQUARED PONDO;;;; +3341;SQUARE HOORU;So;0;L;<square> 30DB 30FC 30EB;;;;N;SQUARED HOORU;;;; +3342;SQUARE HOON;So;0;L;<square> 30DB 30FC 30F3;;;;N;SQUARED HOON;;;; +3343;SQUARE MAIKURO;So;0;L;<square> 30DE 30A4 30AF 30ED;;;;N;SQUARED MAIKURO;;;; +3344;SQUARE MAIRU;So;0;L;<square> 30DE 30A4 30EB;;;;N;SQUARED MAIRU;;;; +3345;SQUARE MAHHA;So;0;L;<square> 30DE 30C3 30CF;;;;N;SQUARED MAHHA;;;; +3346;SQUARE MARUKU;So;0;L;<square> 30DE 30EB 30AF;;;;N;SQUARED MARUKU;;;; +3347;SQUARE MANSYON;So;0;L;<square> 30DE 30F3 30B7 30E7 30F3;;;;N;SQUARED MANSYON;;;; +3348;SQUARE MIKURON;So;0;L;<square> 30DF 30AF 30ED 30F3;;;;N;SQUARED MIKURON;;;; +3349;SQUARE MIRI;So;0;L;<square> 30DF 30EA;;;;N;SQUARED MIRI;;;; +334A;SQUARE MIRIBAARU;So;0;L;<square> 30DF 30EA 30D0 30FC 30EB;;;;N;SQUARED MIRIBAARU;;;; +334B;SQUARE MEGA;So;0;L;<square> 30E1 30AC;;;;N;SQUARED MEGA;;;; +334C;SQUARE MEGATON;So;0;L;<square> 30E1 30AC 30C8 30F3;;;;N;SQUARED MEGATON;;;; +334D;SQUARE MEETORU;So;0;L;<square> 30E1 30FC 30C8 30EB;;;;N;SQUARED MEETORU;;;; +334E;SQUARE YAADO;So;0;L;<square> 30E4 30FC 30C9;;;;N;SQUARED YAADO;;;; +334F;SQUARE YAARU;So;0;L;<square> 30E4 30FC 30EB;;;;N;SQUARED YAARU;;;; +3350;SQUARE YUAN;So;0;L;<square> 30E6 30A2 30F3;;;;N;SQUARED YUAN;;;; +3351;SQUARE RITTORU;So;0;L;<square> 30EA 30C3 30C8 30EB;;;;N;SQUARED RITTORU;;;; +3352;SQUARE RIRA;So;0;L;<square> 30EA 30E9;;;;N;SQUARED RIRA;;;; +3353;SQUARE RUPII;So;0;L;<square> 30EB 30D4 30FC;;;;N;SQUARED RUPII;;;; +3354;SQUARE RUUBURU;So;0;L;<square> 30EB 30FC 30D6 30EB;;;;N;SQUARED RUUBURU;;;; +3355;SQUARE REMU;So;0;L;<square> 30EC 30E0;;;;N;SQUARED REMU;;;; +3356;SQUARE RENTOGEN;So;0;L;<square> 30EC 30F3 30C8 30B2 30F3;;;;N;SQUARED RENTOGEN;;;; +3357;SQUARE WATTO;So;0;L;<square> 30EF 30C3 30C8;;;;N;SQUARED WATTO;;;; +3358;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR ZERO;So;0;L;<compat> 0030 70B9;;;;N;;;;; +3359;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR ONE;So;0;L;<compat> 0031 70B9;;;;N;;;;; +335A;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWO;So;0;L;<compat> 0032 70B9;;;;N;;;;; +335B;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR THREE;So;0;L;<compat> 0033 70B9;;;;N;;;;; +335C;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FOUR;So;0;L;<compat> 0034 70B9;;;;N;;;;; +335D;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FIVE;So;0;L;<compat> 0035 70B9;;;;N;;;;; +335E;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SIX;So;0;L;<compat> 0036 70B9;;;;N;;;;; +335F;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SEVEN;So;0;L;<compat> 0037 70B9;;;;N;;;;; +3360;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR EIGHT;So;0;L;<compat> 0038 70B9;;;;N;;;;; +3361;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR NINE;So;0;L;<compat> 0039 70B9;;;;N;;;;; +3362;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TEN;So;0;L;<compat> 0031 0030 70B9;;;;N;;;;; +3363;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR ELEVEN;So;0;L;<compat> 0031 0031 70B9;;;;N;;;;; +3364;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWELVE;So;0;L;<compat> 0031 0032 70B9;;;;N;;;;; +3365;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR THIRTEEN;So;0;L;<compat> 0031 0033 70B9;;;;N;;;;; +3366;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FOURTEEN;So;0;L;<compat> 0031 0034 70B9;;;;N;;;;; +3367;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FIFTEEN;So;0;L;<compat> 0031 0035 70B9;;;;N;;;;; +3368;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SIXTEEN;So;0;L;<compat> 0031 0036 70B9;;;;N;;;;; +3369;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SEVENTEEN;So;0;L;<compat> 0031 0037 70B9;;;;N;;;;; +336A;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR EIGHTEEN;So;0;L;<compat> 0031 0038 70B9;;;;N;;;;; +336B;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR NINETEEN;So;0;L;<compat> 0031 0039 70B9;;;;N;;;;; +336C;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY;So;0;L;<compat> 0032 0030 70B9;;;;N;;;;; +336D;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-ONE;So;0;L;<compat> 0032 0031 70B9;;;;N;;;;; +336E;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-TWO;So;0;L;<compat> 0032 0032 70B9;;;;N;;;;; +336F;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-THREE;So;0;L;<compat> 0032 0033 70B9;;;;N;;;;; +3370;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-FOUR;So;0;L;<compat> 0032 0034 70B9;;;;N;;;;; +3371;SQUARE HPA;So;0;L;<square> 0068 0050 0061;;;;N;;;;; +3372;SQUARE DA;So;0;L;<square> 0064 0061;;;;N;;;;; +3373;SQUARE AU;So;0;L;<square> 0041 0055;;;;N;;;;; +3374;SQUARE BAR;So;0;L;<square> 0062 0061 0072;;;;N;;;;; +3375;SQUARE OV;So;0;L;<square> 006F 0056;;;;N;;;;; +3376;SQUARE PC;So;0;L;<square> 0070 0063;;;;N;;;;; +337B;SQUARE ERA NAME HEISEI;So;0;L;<square> 5E73 6210;;;;N;SQUARED TWO IDEOGRAPHS ERA NAME HEISEI;;;; +337C;SQUARE ERA NAME SYOUWA;So;0;L;<square> 662D 548C;;;;N;SQUARED TWO IDEOGRAPHS ERA NAME SYOUWA;;;; +337D;SQUARE ERA NAME TAISYOU;So;0;L;<square> 5927 6B63;;;;N;SQUARED TWO IDEOGRAPHS ERA NAME TAISYOU;;;; +337E;SQUARE ERA NAME MEIZI;So;0;L;<square> 660E 6CBB;;;;N;SQUARED TWO IDEOGRAPHS ERA NAME MEIZI;;;; +337F;SQUARE CORPORATION;So;0;L;<square> 682A 5F0F 4F1A 793E;;;;N;SQUARED FOUR IDEOGRAPHS CORPORATION;;;; +3380;SQUARE PA AMPS;So;0;L;<square> 0070 0041;;;;N;SQUARED PA AMPS;;;; +3381;SQUARE NA;So;0;L;<square> 006E 0041;;;;N;SQUARED NA;;;; +3382;SQUARE MU A;So;0;L;<square> 03BC 0041;;;;N;SQUARED MU A;;;; +3383;SQUARE MA;So;0;L;<square> 006D 0041;;;;N;SQUARED MA;;;; +3384;SQUARE KA;So;0;L;<square> 006B 0041;;;;N;SQUARED KA;;;; +3385;SQUARE KB;So;0;L;<square> 004B 0042;;;;N;SQUARED KB;;;; +3386;SQUARE MB;So;0;L;<square> 004D 0042;;;;N;SQUARED MB;;;; +3387;SQUARE GB;So;0;L;<square> 0047 0042;;;;N;SQUARED GB;;;; +3388;SQUARE CAL;So;0;L;<square> 0063 0061 006C;;;;N;SQUARED CAL;;;; +3389;SQUARE KCAL;So;0;L;<square> 006B 0063 0061 006C;;;;N;SQUARED KCAL;;;; +338A;SQUARE PF;So;0;L;<square> 0070 0046;;;;N;SQUARED PF;;;; +338B;SQUARE NF;So;0;L;<square> 006E 0046;;;;N;SQUARED NF;;;; +338C;SQUARE MU F;So;0;L;<square> 03BC 0046;;;;N;SQUARED MU F;;;; +338D;SQUARE MU G;So;0;L;<square> 03BC 0067;;;;N;SQUARED MU G;;;; +338E;SQUARE MG;So;0;L;<square> 006D 0067;;;;N;SQUARED MG;;;; +338F;SQUARE KG;So;0;L;<square> 006B 0067;;;;N;SQUARED KG;;;; +3390;SQUARE HZ;So;0;L;<square> 0048 007A;;;;N;SQUARED HZ;;;; +3391;SQUARE KHZ;So;0;L;<square> 006B 0048 007A;;;;N;SQUARED KHZ;;;; +3392;SQUARE MHZ;So;0;L;<square> 004D 0048 007A;;;;N;SQUARED MHZ;;;; +3393;SQUARE GHZ;So;0;L;<square> 0047 0048 007A;;;;N;SQUARED GHZ;;;; +3394;SQUARE THZ;So;0;L;<square> 0054 0048 007A;;;;N;SQUARED THZ;;;; +3395;SQUARE MU L;So;0;L;<square> 03BC 2113;;;;N;SQUARED MU L;;;; +3396;SQUARE ML;So;0;L;<square> 006D 2113;;;;N;SQUARED ML;;;; +3397;SQUARE DL;So;0;L;<square> 0064 2113;;;;N;SQUARED DL;;;; +3398;SQUARE KL;So;0;L;<square> 006B 2113;;;;N;SQUARED KL;;;; +3399;SQUARE FM;So;0;L;<square> 0066 006D;;;;N;SQUARED FM;;;; +339A;SQUARE NM;So;0;L;<square> 006E 006D;;;;N;SQUARED NM;;;; +339B;SQUARE MU M;So;0;L;<square> 03BC 006D;;;;N;SQUARED MU M;;;; +339C;SQUARE MM;So;0;L;<square> 006D 006D;;;;N;SQUARED MM;;;; +339D;SQUARE CM;So;0;L;<square> 0063 006D;;;;N;SQUARED CM;;;; +339E;SQUARE KM;So;0;L;<square> 006B 006D;;;;N;SQUARED KM;;;; +339F;SQUARE MM SQUARED;So;0;L;<square> 006D 006D 00B2;;;;N;SQUARED MM SQUARED;;;; +33A0;SQUARE CM SQUARED;So;0;L;<square> 0063 006D 00B2;;;;N;SQUARED CM SQUARED;;;; +33A1;SQUARE M SQUARED;So;0;L;<square> 006D 00B2;;;;N;SQUARED M SQUARED;;;; +33A2;SQUARE KM SQUARED;So;0;L;<square> 006B 006D 00B2;;;;N;SQUARED KM SQUARED;;;; +33A3;SQUARE MM CUBED;So;0;L;<square> 006D 006D 00B3;;;;N;SQUARED MM CUBED;;;; +33A4;SQUARE CM CUBED;So;0;L;<square> 0063 006D 00B3;;;;N;SQUARED CM CUBED;;;; +33A5;SQUARE M CUBED;So;0;L;<square> 006D 00B3;;;;N;SQUARED M CUBED;;;; +33A6;SQUARE KM CUBED;So;0;L;<square> 006B 006D 00B3;;;;N;SQUARED KM CUBED;;;; +33A7;SQUARE M OVER S;So;0;L;<square> 006D 2215 0073;;;;N;SQUARED M OVER S;;;; +33A8;SQUARE M OVER S SQUARED;So;0;L;<square> 006D 2215 0073 00B2;;;;N;SQUARED M OVER S SQUARED;;;; +33A9;SQUARE PA;So;0;L;<square> 0050 0061;;;;N;SQUARED PA;;;; +33AA;SQUARE KPA;So;0;L;<square> 006B 0050 0061;;;;N;SQUARED KPA;;;; +33AB;SQUARE MPA;So;0;L;<square> 004D 0050 0061;;;;N;SQUARED MPA;;;; +33AC;SQUARE GPA;So;0;L;<square> 0047 0050 0061;;;;N;SQUARED GPA;;;; +33AD;SQUARE RAD;So;0;L;<square> 0072 0061 0064;;;;N;SQUARED RAD;;;; +33AE;SQUARE RAD OVER S;So;0;L;<square> 0072 0061 0064 2215 0073;;;;N;SQUARED RAD OVER S;;;; +33AF;SQUARE RAD OVER S SQUARED;So;0;L;<square> 0072 0061 0064 2215 0073 00B2;;;;N;SQUARED RAD OVER S SQUARED;;;; +33B0;SQUARE PS;So;0;L;<square> 0070 0073;;;;N;SQUARED PS;;;; +33B1;SQUARE NS;So;0;L;<square> 006E 0073;;;;N;SQUARED NS;;;; +33B2;SQUARE MU S;So;0;L;<square> 03BC 0073;;;;N;SQUARED MU S;;;; +33B3;SQUARE MS;So;0;L;<square> 006D 0073;;;;N;SQUARED MS;;;; +33B4;SQUARE PV;So;0;L;<square> 0070 0056;;;;N;SQUARED PV;;;; +33B5;SQUARE NV;So;0;L;<square> 006E 0056;;;;N;SQUARED NV;;;; +33B6;SQUARE MU V;So;0;L;<square> 03BC 0056;;;;N;SQUARED MU V;;;; +33B7;SQUARE MV;So;0;L;<square> 006D 0056;;;;N;SQUARED MV;;;; +33B8;SQUARE KV;So;0;L;<square> 006B 0056;;;;N;SQUARED KV;;;; +33B9;SQUARE MV MEGA;So;0;L;<square> 004D 0056;;;;N;SQUARED MV MEGA;;;; +33BA;SQUARE PW;So;0;L;<square> 0070 0057;;;;N;SQUARED PW;;;; +33BB;SQUARE NW;So;0;L;<square> 006E 0057;;;;N;SQUARED NW;;;; +33BC;SQUARE MU W;So;0;L;<square> 03BC 0057;;;;N;SQUARED MU W;;;; +33BD;SQUARE MW;So;0;L;<square> 006D 0057;;;;N;SQUARED MW;;;; +33BE;SQUARE KW;So;0;L;<square> 006B 0057;;;;N;SQUARED KW;;;; +33BF;SQUARE MW MEGA;So;0;L;<square> 004D 0057;;;;N;SQUARED MW MEGA;;;; +33C0;SQUARE K OHM;So;0;L;<square> 006B 03A9;;;;N;SQUARED K OHM;;;; +33C1;SQUARE M OHM;So;0;L;<square> 004D 03A9;;;;N;SQUARED M OHM;;;; +33C2;SQUARE AM;So;0;L;<square> 0061 002E 006D 002E;;;;N;SQUARED AM;;;; +33C3;SQUARE BQ;So;0;L;<square> 0042 0071;;;;N;SQUARED BQ;;;; +33C4;SQUARE CC;So;0;L;<square> 0063 0063;;;;N;SQUARED CC;;;; +33C5;SQUARE CD;So;0;L;<square> 0063 0064;;;;N;SQUARED CD;;;; +33C6;SQUARE C OVER KG;So;0;L;<square> 0043 2215 006B 0067;;;;N;SQUARED C OVER KG;;;; +33C7;SQUARE CO;So;0;L;<square> 0043 006F 002E;;;;N;SQUARED CO;;;; +33C8;SQUARE DB;So;0;L;<square> 0064 0042;;;;N;SQUARED DB;;;; +33C9;SQUARE GY;So;0;L;<square> 0047 0079;;;;N;SQUARED GY;;;; +33CA;SQUARE HA;So;0;L;<square> 0068 0061;;;;N;SQUARED HA;;;; +33CB;SQUARE HP;So;0;L;<square> 0048 0050;;;;N;SQUARED HP;;;; +33CC;SQUARE IN;So;0;L;<square> 0069 006E;;;;N;SQUARED IN;;;; +33CD;SQUARE KK;So;0;L;<square> 004B 004B;;;;N;SQUARED KK;;;; +33CE;SQUARE KM CAPITAL;So;0;L;<square> 004B 004D;;;;N;SQUARED KM CAPITAL;;;; +33CF;SQUARE KT;So;0;L;<square> 006B 0074;;;;N;SQUARED KT;;;; +33D0;SQUARE LM;So;0;L;<square> 006C 006D;;;;N;SQUARED LM;;;; +33D1;SQUARE LN;So;0;L;<square> 006C 006E;;;;N;SQUARED LN;;;; +33D2;SQUARE LOG;So;0;L;<square> 006C 006F 0067;;;;N;SQUARED LOG;;;; +33D3;SQUARE LX;So;0;L;<square> 006C 0078;;;;N;SQUARED LX;;;; +33D4;SQUARE MB SMALL;So;0;L;<square> 006D 0062;;;;N;SQUARED MB SMALL;;;; +33D5;SQUARE MIL;So;0;L;<square> 006D 0069 006C;;;;N;SQUARED MIL;;;; +33D6;SQUARE MOL;So;0;L;<square> 006D 006F 006C;;;;N;SQUARED MOL;;;; +33D7;SQUARE PH;So;0;L;<square> 0050 0048;;;;N;SQUARED PH;;;; +33D8;SQUARE PM;So;0;L;<square> 0070 002E 006D 002E;;;;N;SQUARED PM;;;; +33D9;SQUARE PPM;So;0;L;<square> 0050 0050 004D;;;;N;SQUARED PPM;;;; +33DA;SQUARE PR;So;0;L;<square> 0050 0052;;;;N;SQUARED PR;;;; +33DB;SQUARE SR;So;0;L;<square> 0073 0072;;;;N;SQUARED SR;;;; +33DC;SQUARE SV;So;0;L;<square> 0053 0076;;;;N;SQUARED SV;;;; +33DD;SQUARE WB;So;0;L;<square> 0057 0062;;;;N;SQUARED WB;;;; +33E0;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY ONE;So;0;L;<compat> 0031 65E5;;;;N;;;;; +33E1;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWO;So;0;L;<compat> 0032 65E5;;;;N;;;;; +33E2;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THREE;So;0;L;<compat> 0033 65E5;;;;N;;;;; +33E3;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FOUR;So;0;L;<compat> 0034 65E5;;;;N;;;;; +33E4;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FIVE;So;0;L;<compat> 0035 65E5;;;;N;;;;; +33E5;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SIX;So;0;L;<compat> 0036 65E5;;;;N;;;;; +33E6;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SEVEN;So;0;L;<compat> 0037 65E5;;;;N;;;;; +33E7;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY EIGHT;So;0;L;<compat> 0038 65E5;;;;N;;;;; +33E8;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY NINE;So;0;L;<compat> 0039 65E5;;;;N;;;;; +33E9;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TEN;So;0;L;<compat> 0031 0030 65E5;;;;N;;;;; +33EA;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY ELEVEN;So;0;L;<compat> 0031 0031 65E5;;;;N;;;;; +33EB;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWELVE;So;0;L;<compat> 0031 0032 65E5;;;;N;;;;; +33EC;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THIRTEEN;So;0;L;<compat> 0031 0033 65E5;;;;N;;;;; +33ED;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FOURTEEN;So;0;L;<compat> 0031 0034 65E5;;;;N;;;;; +33EE;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FIFTEEN;So;0;L;<compat> 0031 0035 65E5;;;;N;;;;; +33EF;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SIXTEEN;So;0;L;<compat> 0031 0036 65E5;;;;N;;;;; +33F0;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SEVENTEEN;So;0;L;<compat> 0031 0037 65E5;;;;N;;;;; +33F1;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY EIGHTEEN;So;0;L;<compat> 0031 0038 65E5;;;;N;;;;; +33F2;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY NINETEEN;So;0;L;<compat> 0031 0039 65E5;;;;N;;;;; +33F3;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY;So;0;L;<compat> 0032 0030 65E5;;;;N;;;;; +33F4;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-ONE;So;0;L;<compat> 0032 0031 65E5;;;;N;;;;; +33F5;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-TWO;So;0;L;<compat> 0032 0032 65E5;;;;N;;;;; +33F6;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-THREE;So;0;L;<compat> 0032 0033 65E5;;;;N;;;;; +33F7;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-FOUR;So;0;L;<compat> 0032 0034 65E5;;;;N;;;;; +33F8;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-FIVE;So;0;L;<compat> 0032 0035 65E5;;;;N;;;;; +33F9;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-SIX;So;0;L;<compat> 0032 0036 65E5;;;;N;;;;; +33FA;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-SEVEN;So;0;L;<compat> 0032 0037 65E5;;;;N;;;;; +33FB;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-EIGHT;So;0;L;<compat> 0032 0038 65E5;;;;N;;;;; +33FC;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-NINE;So;0;L;<compat> 0032 0039 65E5;;;;N;;;;; +33FD;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THIRTY;So;0;L;<compat> 0033 0030 65E5;;;;N;;;;; +33FE;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THIRTY-ONE;So;0;L;<compat> 0033 0031 65E5;;;;N;;;;; +3400;<CJK Ideograph Extension A, First>;Lo;0;L;;;;;N;;;;; +4DB5;<CJK Ideograph Extension A, Last>;Lo;0;L;;;;;N;;;;; +4E00;<CJK Ideograph, First>;Lo;0;L;;;;;N;;;;; +9FA5;<CJK Ideograph, Last>;Lo;0;L;;;;;N;;;;; +A000;YI SYLLABLE IT;Lo;0;L;;;;;N;;;;; +A001;YI SYLLABLE IX;Lo;0;L;;;;;N;;;;; +A002;YI SYLLABLE I;Lo;0;L;;;;;N;;;;; +A003;YI SYLLABLE IP;Lo;0;L;;;;;N;;;;; +A004;YI SYLLABLE IET;Lo;0;L;;;;;N;;;;; +A005;YI SYLLABLE IEX;Lo;0;L;;;;;N;;;;; +A006;YI SYLLABLE IE;Lo;0;L;;;;;N;;;;; +A007;YI SYLLABLE IEP;Lo;0;L;;;;;N;;;;; +A008;YI SYLLABLE AT;Lo;0;L;;;;;N;;;;; +A009;YI SYLLABLE AX;Lo;0;L;;;;;N;;;;; +A00A;YI SYLLABLE A;Lo;0;L;;;;;N;;;;; +A00B;YI SYLLABLE AP;Lo;0;L;;;;;N;;;;; +A00C;YI SYLLABLE UOX;Lo;0;L;;;;;N;;;;; +A00D;YI SYLLABLE UO;Lo;0;L;;;;;N;;;;; +A00E;YI SYLLABLE UOP;Lo;0;L;;;;;N;;;;; +A00F;YI SYLLABLE OT;Lo;0;L;;;;;N;;;;; +A010;YI SYLLABLE OX;Lo;0;L;;;;;N;;;;; +A011;YI SYLLABLE O;Lo;0;L;;;;;N;;;;; +A012;YI SYLLABLE OP;Lo;0;L;;;;;N;;;;; +A013;YI SYLLABLE EX;Lo;0;L;;;;;N;;;;; +A014;YI SYLLABLE E;Lo;0;L;;;;;N;;;;; +A015;YI SYLLABLE WU;Lo;0;L;;;;;N;;;;; +A016;YI SYLLABLE BIT;Lo;0;L;;;;;N;;;;; +A017;YI SYLLABLE BIX;Lo;0;L;;;;;N;;;;; +A018;YI SYLLABLE BI;Lo;0;L;;;;;N;;;;; +A019;YI SYLLABLE BIP;Lo;0;L;;;;;N;;;;; +A01A;YI SYLLABLE BIET;Lo;0;L;;;;;N;;;;; +A01B;YI SYLLABLE BIEX;Lo;0;L;;;;;N;;;;; +A01C;YI SYLLABLE BIE;Lo;0;L;;;;;N;;;;; +A01D;YI SYLLABLE BIEP;Lo;0;L;;;;;N;;;;; +A01E;YI SYLLABLE BAT;Lo;0;L;;;;;N;;;;; +A01F;YI SYLLABLE BAX;Lo;0;L;;;;;N;;;;; +A020;YI SYLLABLE BA;Lo;0;L;;;;;N;;;;; +A021;YI SYLLABLE BAP;Lo;0;L;;;;;N;;;;; +A022;YI SYLLABLE BUOX;Lo;0;L;;;;;N;;;;; +A023;YI SYLLABLE BUO;Lo;0;L;;;;;N;;;;; +A024;YI SYLLABLE BUOP;Lo;0;L;;;;;N;;;;; +A025;YI SYLLABLE BOT;Lo;0;L;;;;;N;;;;; +A026;YI SYLLABLE BOX;Lo;0;L;;;;;N;;;;; +A027;YI SYLLABLE BO;Lo;0;L;;;;;N;;;;; +A028;YI SYLLABLE BOP;Lo;0;L;;;;;N;;;;; +A029;YI SYLLABLE BEX;Lo;0;L;;;;;N;;;;; +A02A;YI SYLLABLE BE;Lo;0;L;;;;;N;;;;; +A02B;YI SYLLABLE BEP;Lo;0;L;;;;;N;;;;; +A02C;YI SYLLABLE BUT;Lo;0;L;;;;;N;;;;; +A02D;YI SYLLABLE BUX;Lo;0;L;;;;;N;;;;; +A02E;YI SYLLABLE BU;Lo;0;L;;;;;N;;;;; +A02F;YI SYLLABLE BUP;Lo;0;L;;;;;N;;;;; +A030;YI SYLLABLE BURX;Lo;0;L;;;;;N;;;;; +A031;YI SYLLABLE BUR;Lo;0;L;;;;;N;;;;; +A032;YI SYLLABLE BYT;Lo;0;L;;;;;N;;;;; +A033;YI SYLLABLE BYX;Lo;0;L;;;;;N;;;;; +A034;YI SYLLABLE BY;Lo;0;L;;;;;N;;;;; +A035;YI SYLLABLE BYP;Lo;0;L;;;;;N;;;;; +A036;YI SYLLABLE BYRX;Lo;0;L;;;;;N;;;;; +A037;YI SYLLABLE BYR;Lo;0;L;;;;;N;;;;; +A038;YI SYLLABLE PIT;Lo;0;L;;;;;N;;;;; +A039;YI SYLLABLE PIX;Lo;0;L;;;;;N;;;;; +A03A;YI SYLLABLE PI;Lo;0;L;;;;;N;;;;; +A03B;YI SYLLABLE PIP;Lo;0;L;;;;;N;;;;; +A03C;YI SYLLABLE PIEX;Lo;0;L;;;;;N;;;;; +A03D;YI SYLLABLE PIE;Lo;0;L;;;;;N;;;;; +A03E;YI SYLLABLE PIEP;Lo;0;L;;;;;N;;;;; +A03F;YI SYLLABLE PAT;Lo;0;L;;;;;N;;;;; +A040;YI SYLLABLE PAX;Lo;0;L;;;;;N;;;;; +A041;YI SYLLABLE PA;Lo;0;L;;;;;N;;;;; +A042;YI SYLLABLE PAP;Lo;0;L;;;;;N;;;;; +A043;YI SYLLABLE PUOX;Lo;0;L;;;;;N;;;;; +A044;YI SYLLABLE PUO;Lo;0;L;;;;;N;;;;; +A045;YI SYLLABLE PUOP;Lo;0;L;;;;;N;;;;; +A046;YI SYLLABLE POT;Lo;0;L;;;;;N;;;;; +A047;YI SYLLABLE POX;Lo;0;L;;;;;N;;;;; +A048;YI SYLLABLE PO;Lo;0;L;;;;;N;;;;; +A049;YI SYLLABLE POP;Lo;0;L;;;;;N;;;;; +A04A;YI SYLLABLE PUT;Lo;0;L;;;;;N;;;;; +A04B;YI SYLLABLE PUX;Lo;0;L;;;;;N;;;;; +A04C;YI SYLLABLE PU;Lo;0;L;;;;;N;;;;; +A04D;YI SYLLABLE PUP;Lo;0;L;;;;;N;;;;; +A04E;YI SYLLABLE PURX;Lo;0;L;;;;;N;;;;; +A04F;YI SYLLABLE PUR;Lo;0;L;;;;;N;;;;; +A050;YI SYLLABLE PYT;Lo;0;L;;;;;N;;;;; +A051;YI SYLLABLE PYX;Lo;0;L;;;;;N;;;;; +A052;YI SYLLABLE PY;Lo;0;L;;;;;N;;;;; +A053;YI SYLLABLE PYP;Lo;0;L;;;;;N;;;;; +A054;YI SYLLABLE PYRX;Lo;0;L;;;;;N;;;;; +A055;YI SYLLABLE PYR;Lo;0;L;;;;;N;;;;; +A056;YI SYLLABLE BBIT;Lo;0;L;;;;;N;;;;; +A057;YI SYLLABLE BBIX;Lo;0;L;;;;;N;;;;; +A058;YI SYLLABLE BBI;Lo;0;L;;;;;N;;;;; +A059;YI SYLLABLE BBIP;Lo;0;L;;;;;N;;;;; +A05A;YI SYLLABLE BBIET;Lo;0;L;;;;;N;;;;; +A05B;YI SYLLABLE BBIEX;Lo;0;L;;;;;N;;;;; +A05C;YI SYLLABLE BBIE;Lo;0;L;;;;;N;;;;; +A05D;YI SYLLABLE BBIEP;Lo;0;L;;;;;N;;;;; +A05E;YI SYLLABLE BBAT;Lo;0;L;;;;;N;;;;; +A05F;YI SYLLABLE BBAX;Lo;0;L;;;;;N;;;;; +A060;YI SYLLABLE BBA;Lo;0;L;;;;;N;;;;; +A061;YI SYLLABLE BBAP;Lo;0;L;;;;;N;;;;; +A062;YI SYLLABLE BBUOX;Lo;0;L;;;;;N;;;;; +A063;YI SYLLABLE BBUO;Lo;0;L;;;;;N;;;;; +A064;YI SYLLABLE BBUOP;Lo;0;L;;;;;N;;;;; +A065;YI SYLLABLE BBOT;Lo;0;L;;;;;N;;;;; +A066;YI SYLLABLE BBOX;Lo;0;L;;;;;N;;;;; +A067;YI SYLLABLE BBO;Lo;0;L;;;;;N;;;;; +A068;YI SYLLABLE BBOP;Lo;0;L;;;;;N;;;;; +A069;YI SYLLABLE BBEX;Lo;0;L;;;;;N;;;;; +A06A;YI SYLLABLE BBE;Lo;0;L;;;;;N;;;;; +A06B;YI SYLLABLE BBEP;Lo;0;L;;;;;N;;;;; +A06C;YI SYLLABLE BBUT;Lo;0;L;;;;;N;;;;; +A06D;YI SYLLABLE BBUX;Lo;0;L;;;;;N;;;;; +A06E;YI SYLLABLE BBU;Lo;0;L;;;;;N;;;;; +A06F;YI SYLLABLE BBUP;Lo;0;L;;;;;N;;;;; +A070;YI SYLLABLE BBURX;Lo;0;L;;;;;N;;;;; +A071;YI SYLLABLE BBUR;Lo;0;L;;;;;N;;;;; +A072;YI SYLLABLE BBYT;Lo;0;L;;;;;N;;;;; +A073;YI SYLLABLE BBYX;Lo;0;L;;;;;N;;;;; +A074;YI SYLLABLE BBY;Lo;0;L;;;;;N;;;;; +A075;YI SYLLABLE BBYP;Lo;0;L;;;;;N;;;;; +A076;YI SYLLABLE NBIT;Lo;0;L;;;;;N;;;;; +A077;YI SYLLABLE NBIX;Lo;0;L;;;;;N;;;;; +A078;YI SYLLABLE NBI;Lo;0;L;;;;;N;;;;; +A079;YI SYLLABLE NBIP;Lo;0;L;;;;;N;;;;; +A07A;YI SYLLABLE NBIEX;Lo;0;L;;;;;N;;;;; +A07B;YI SYLLABLE NBIE;Lo;0;L;;;;;N;;;;; +A07C;YI SYLLABLE NBIEP;Lo;0;L;;;;;N;;;;; +A07D;YI SYLLABLE NBAT;Lo;0;L;;;;;N;;;;; +A07E;YI SYLLABLE NBAX;Lo;0;L;;;;;N;;;;; +A07F;YI SYLLABLE NBA;Lo;0;L;;;;;N;;;;; +A080;YI SYLLABLE NBAP;Lo;0;L;;;;;N;;;;; +A081;YI SYLLABLE NBOT;Lo;0;L;;;;;N;;;;; +A082;YI SYLLABLE NBOX;Lo;0;L;;;;;N;;;;; +A083;YI SYLLABLE NBO;Lo;0;L;;;;;N;;;;; +A084;YI SYLLABLE NBOP;Lo;0;L;;;;;N;;;;; +A085;YI SYLLABLE NBUT;Lo;0;L;;;;;N;;;;; +A086;YI SYLLABLE NBUX;Lo;0;L;;;;;N;;;;; +A087;YI SYLLABLE NBU;Lo;0;L;;;;;N;;;;; +A088;YI SYLLABLE NBUP;Lo;0;L;;;;;N;;;;; +A089;YI SYLLABLE NBURX;Lo;0;L;;;;;N;;;;; +A08A;YI SYLLABLE NBUR;Lo;0;L;;;;;N;;;;; +A08B;YI SYLLABLE NBYT;Lo;0;L;;;;;N;;;;; +A08C;YI SYLLABLE NBYX;Lo;0;L;;;;;N;;;;; +A08D;YI SYLLABLE NBY;Lo;0;L;;;;;N;;;;; +A08E;YI SYLLABLE NBYP;Lo;0;L;;;;;N;;;;; +A08F;YI SYLLABLE NBYRX;Lo;0;L;;;;;N;;;;; +A090;YI SYLLABLE NBYR;Lo;0;L;;;;;N;;;;; +A091;YI SYLLABLE HMIT;Lo;0;L;;;;;N;;;;; +A092;YI SYLLABLE HMIX;Lo;0;L;;;;;N;;;;; +A093;YI SYLLABLE HMI;Lo;0;L;;;;;N;;;;; +A094;YI SYLLABLE HMIP;Lo;0;L;;;;;N;;;;; +A095;YI SYLLABLE HMIEX;Lo;0;L;;;;;N;;;;; +A096;YI SYLLABLE HMIE;Lo;0;L;;;;;N;;;;; +A097;YI SYLLABLE HMIEP;Lo;0;L;;;;;N;;;;; +A098;YI SYLLABLE HMAT;Lo;0;L;;;;;N;;;;; +A099;YI SYLLABLE HMAX;Lo;0;L;;;;;N;;;;; +A09A;YI SYLLABLE HMA;Lo;0;L;;;;;N;;;;; +A09B;YI SYLLABLE HMAP;Lo;0;L;;;;;N;;;;; +A09C;YI SYLLABLE HMUOX;Lo;0;L;;;;;N;;;;; +A09D;YI SYLLABLE HMUO;Lo;0;L;;;;;N;;;;; +A09E;YI SYLLABLE HMUOP;Lo;0;L;;;;;N;;;;; +A09F;YI SYLLABLE HMOT;Lo;0;L;;;;;N;;;;; +A0A0;YI SYLLABLE HMOX;Lo;0;L;;;;;N;;;;; +A0A1;YI SYLLABLE HMO;Lo;0;L;;;;;N;;;;; +A0A2;YI SYLLABLE HMOP;Lo;0;L;;;;;N;;;;; +A0A3;YI SYLLABLE HMUT;Lo;0;L;;;;;N;;;;; +A0A4;YI SYLLABLE HMUX;Lo;0;L;;;;;N;;;;; +A0A5;YI SYLLABLE HMU;Lo;0;L;;;;;N;;;;; +A0A6;YI SYLLABLE HMUP;Lo;0;L;;;;;N;;;;; +A0A7;YI SYLLABLE HMURX;Lo;0;L;;;;;N;;;;; +A0A8;YI SYLLABLE HMUR;Lo;0;L;;;;;N;;;;; +A0A9;YI SYLLABLE HMYX;Lo;0;L;;;;;N;;;;; +A0AA;YI SYLLABLE HMY;Lo;0;L;;;;;N;;;;; +A0AB;YI SYLLABLE HMYP;Lo;0;L;;;;;N;;;;; +A0AC;YI SYLLABLE HMYRX;Lo;0;L;;;;;N;;;;; +A0AD;YI SYLLABLE HMYR;Lo;0;L;;;;;N;;;;; +A0AE;YI SYLLABLE MIT;Lo;0;L;;;;;N;;;;; +A0AF;YI SYLLABLE MIX;Lo;0;L;;;;;N;;;;; +A0B0;YI SYLLABLE MI;Lo;0;L;;;;;N;;;;; +A0B1;YI SYLLABLE MIP;Lo;0;L;;;;;N;;;;; +A0B2;YI SYLLABLE MIEX;Lo;0;L;;;;;N;;;;; +A0B3;YI SYLLABLE MIE;Lo;0;L;;;;;N;;;;; +A0B4;YI SYLLABLE MIEP;Lo;0;L;;;;;N;;;;; +A0B5;YI SYLLABLE MAT;Lo;0;L;;;;;N;;;;; +A0B6;YI SYLLABLE MAX;Lo;0;L;;;;;N;;;;; +A0B7;YI SYLLABLE MA;Lo;0;L;;;;;N;;;;; +A0B8;YI SYLLABLE MAP;Lo;0;L;;;;;N;;;;; +A0B9;YI SYLLABLE MUOT;Lo;0;L;;;;;N;;;;; +A0BA;YI SYLLABLE MUOX;Lo;0;L;;;;;N;;;;; +A0BB;YI SYLLABLE MUO;Lo;0;L;;;;;N;;;;; +A0BC;YI SYLLABLE MUOP;Lo;0;L;;;;;N;;;;; +A0BD;YI SYLLABLE MOT;Lo;0;L;;;;;N;;;;; +A0BE;YI SYLLABLE MOX;Lo;0;L;;;;;N;;;;; +A0BF;YI SYLLABLE MO;Lo;0;L;;;;;N;;;;; +A0C0;YI SYLLABLE MOP;Lo;0;L;;;;;N;;;;; +A0C1;YI SYLLABLE MEX;Lo;0;L;;;;;N;;;;; +A0C2;YI SYLLABLE ME;Lo;0;L;;;;;N;;;;; +A0C3;YI SYLLABLE MUT;Lo;0;L;;;;;N;;;;; +A0C4;YI SYLLABLE MUX;Lo;0;L;;;;;N;;;;; +A0C5;YI SYLLABLE MU;Lo;0;L;;;;;N;;;;; +A0C6;YI SYLLABLE MUP;Lo;0;L;;;;;N;;;;; +A0C7;YI SYLLABLE MURX;Lo;0;L;;;;;N;;;;; +A0C8;YI SYLLABLE MUR;Lo;0;L;;;;;N;;;;; +A0C9;YI SYLLABLE MYT;Lo;0;L;;;;;N;;;;; +A0CA;YI SYLLABLE MYX;Lo;0;L;;;;;N;;;;; +A0CB;YI SYLLABLE MY;Lo;0;L;;;;;N;;;;; +A0CC;YI SYLLABLE MYP;Lo;0;L;;;;;N;;;;; +A0CD;YI SYLLABLE FIT;Lo;0;L;;;;;N;;;;; +A0CE;YI SYLLABLE FIX;Lo;0;L;;;;;N;;;;; +A0CF;YI SYLLABLE FI;Lo;0;L;;;;;N;;;;; +A0D0;YI SYLLABLE FIP;Lo;0;L;;;;;N;;;;; +A0D1;YI SYLLABLE FAT;Lo;0;L;;;;;N;;;;; +A0D2;YI SYLLABLE FAX;Lo;0;L;;;;;N;;;;; +A0D3;YI SYLLABLE FA;Lo;0;L;;;;;N;;;;; +A0D4;YI SYLLABLE FAP;Lo;0;L;;;;;N;;;;; +A0D5;YI SYLLABLE FOX;Lo;0;L;;;;;N;;;;; +A0D6;YI SYLLABLE FO;Lo;0;L;;;;;N;;;;; +A0D7;YI SYLLABLE FOP;Lo;0;L;;;;;N;;;;; +A0D8;YI SYLLABLE FUT;Lo;0;L;;;;;N;;;;; +A0D9;YI SYLLABLE FUX;Lo;0;L;;;;;N;;;;; +A0DA;YI SYLLABLE FU;Lo;0;L;;;;;N;;;;; +A0DB;YI SYLLABLE FUP;Lo;0;L;;;;;N;;;;; +A0DC;YI SYLLABLE FURX;Lo;0;L;;;;;N;;;;; +A0DD;YI SYLLABLE FUR;Lo;0;L;;;;;N;;;;; +A0DE;YI SYLLABLE FYT;Lo;0;L;;;;;N;;;;; +A0DF;YI SYLLABLE FYX;Lo;0;L;;;;;N;;;;; +A0E0;YI SYLLABLE FY;Lo;0;L;;;;;N;;;;; +A0E1;YI SYLLABLE FYP;Lo;0;L;;;;;N;;;;; +A0E2;YI SYLLABLE VIT;Lo;0;L;;;;;N;;;;; +A0E3;YI SYLLABLE VIX;Lo;0;L;;;;;N;;;;; +A0E4;YI SYLLABLE VI;Lo;0;L;;;;;N;;;;; +A0E5;YI SYLLABLE VIP;Lo;0;L;;;;;N;;;;; +A0E6;YI SYLLABLE VIET;Lo;0;L;;;;;N;;;;; +A0E7;YI SYLLABLE VIEX;Lo;0;L;;;;;N;;;;; +A0E8;YI SYLLABLE VIE;Lo;0;L;;;;;N;;;;; +A0E9;YI SYLLABLE VIEP;Lo;0;L;;;;;N;;;;; +A0EA;YI SYLLABLE VAT;Lo;0;L;;;;;N;;;;; +A0EB;YI SYLLABLE VAX;Lo;0;L;;;;;N;;;;; +A0EC;YI SYLLABLE VA;Lo;0;L;;;;;N;;;;; +A0ED;YI SYLLABLE VAP;Lo;0;L;;;;;N;;;;; +A0EE;YI SYLLABLE VOT;Lo;0;L;;;;;N;;;;; +A0EF;YI SYLLABLE VOX;Lo;0;L;;;;;N;;;;; +A0F0;YI SYLLABLE VO;Lo;0;L;;;;;N;;;;; +A0F1;YI SYLLABLE VOP;Lo;0;L;;;;;N;;;;; +A0F2;YI SYLLABLE VEX;Lo;0;L;;;;;N;;;;; +A0F3;YI SYLLABLE VEP;Lo;0;L;;;;;N;;;;; +A0F4;YI SYLLABLE VUT;Lo;0;L;;;;;N;;;;; +A0F5;YI SYLLABLE VUX;Lo;0;L;;;;;N;;;;; +A0F6;YI SYLLABLE VU;Lo;0;L;;;;;N;;;;; +A0F7;YI SYLLABLE VUP;Lo;0;L;;;;;N;;;;; +A0F8;YI SYLLABLE VURX;Lo;0;L;;;;;N;;;;; +A0F9;YI SYLLABLE VUR;Lo;0;L;;;;;N;;;;; +A0FA;YI SYLLABLE VYT;Lo;0;L;;;;;N;;;;; +A0FB;YI SYLLABLE VYX;Lo;0;L;;;;;N;;;;; +A0FC;YI SYLLABLE VY;Lo;0;L;;;;;N;;;;; +A0FD;YI SYLLABLE VYP;Lo;0;L;;;;;N;;;;; +A0FE;YI SYLLABLE VYRX;Lo;0;L;;;;;N;;;;; +A0FF;YI SYLLABLE VYR;Lo;0;L;;;;;N;;;;; +A100;YI SYLLABLE DIT;Lo;0;L;;;;;N;;;;; +A101;YI SYLLABLE DIX;Lo;0;L;;;;;N;;;;; +A102;YI SYLLABLE DI;Lo;0;L;;;;;N;;;;; +A103;YI SYLLABLE DIP;Lo;0;L;;;;;N;;;;; +A104;YI SYLLABLE DIEX;Lo;0;L;;;;;N;;;;; +A105;YI SYLLABLE DIE;Lo;0;L;;;;;N;;;;; +A106;YI SYLLABLE DIEP;Lo;0;L;;;;;N;;;;; +A107;YI SYLLABLE DAT;Lo;0;L;;;;;N;;;;; +A108;YI SYLLABLE DAX;Lo;0;L;;;;;N;;;;; +A109;YI SYLLABLE DA;Lo;0;L;;;;;N;;;;; +A10A;YI SYLLABLE DAP;Lo;0;L;;;;;N;;;;; +A10B;YI SYLLABLE DUOX;Lo;0;L;;;;;N;;;;; +A10C;YI SYLLABLE DUO;Lo;0;L;;;;;N;;;;; +A10D;YI SYLLABLE DOT;Lo;0;L;;;;;N;;;;; +A10E;YI SYLLABLE DOX;Lo;0;L;;;;;N;;;;; +A10F;YI SYLLABLE DO;Lo;0;L;;;;;N;;;;; +A110;YI SYLLABLE DOP;Lo;0;L;;;;;N;;;;; +A111;YI SYLLABLE DEX;Lo;0;L;;;;;N;;;;; +A112;YI SYLLABLE DE;Lo;0;L;;;;;N;;;;; +A113;YI SYLLABLE DEP;Lo;0;L;;;;;N;;;;; +A114;YI SYLLABLE DUT;Lo;0;L;;;;;N;;;;; +A115;YI SYLLABLE DUX;Lo;0;L;;;;;N;;;;; +A116;YI SYLLABLE DU;Lo;0;L;;;;;N;;;;; +A117;YI SYLLABLE DUP;Lo;0;L;;;;;N;;;;; +A118;YI SYLLABLE DURX;Lo;0;L;;;;;N;;;;; +A119;YI SYLLABLE DUR;Lo;0;L;;;;;N;;;;; +A11A;YI SYLLABLE TIT;Lo;0;L;;;;;N;;;;; +A11B;YI SYLLABLE TIX;Lo;0;L;;;;;N;;;;; +A11C;YI SYLLABLE TI;Lo;0;L;;;;;N;;;;; +A11D;YI SYLLABLE TIP;Lo;0;L;;;;;N;;;;; +A11E;YI SYLLABLE TIEX;Lo;0;L;;;;;N;;;;; +A11F;YI SYLLABLE TIE;Lo;0;L;;;;;N;;;;; +A120;YI SYLLABLE TIEP;Lo;0;L;;;;;N;;;;; +A121;YI SYLLABLE TAT;Lo;0;L;;;;;N;;;;; +A122;YI SYLLABLE TAX;Lo;0;L;;;;;N;;;;; +A123;YI SYLLABLE TA;Lo;0;L;;;;;N;;;;; +A124;YI SYLLABLE TAP;Lo;0;L;;;;;N;;;;; +A125;YI SYLLABLE TUOT;Lo;0;L;;;;;N;;;;; +A126;YI SYLLABLE TUOX;Lo;0;L;;;;;N;;;;; +A127;YI SYLLABLE TUO;Lo;0;L;;;;;N;;;;; +A128;YI SYLLABLE TUOP;Lo;0;L;;;;;N;;;;; +A129;YI SYLLABLE TOT;Lo;0;L;;;;;N;;;;; +A12A;YI SYLLABLE TOX;Lo;0;L;;;;;N;;;;; +A12B;YI SYLLABLE TO;Lo;0;L;;;;;N;;;;; +A12C;YI SYLLABLE TOP;Lo;0;L;;;;;N;;;;; +A12D;YI SYLLABLE TEX;Lo;0;L;;;;;N;;;;; +A12E;YI SYLLABLE TE;Lo;0;L;;;;;N;;;;; +A12F;YI SYLLABLE TEP;Lo;0;L;;;;;N;;;;; +A130;YI SYLLABLE TUT;Lo;0;L;;;;;N;;;;; +A131;YI SYLLABLE TUX;Lo;0;L;;;;;N;;;;; +A132;YI SYLLABLE TU;Lo;0;L;;;;;N;;;;; +A133;YI SYLLABLE TUP;Lo;0;L;;;;;N;;;;; +A134;YI SYLLABLE TURX;Lo;0;L;;;;;N;;;;; +A135;YI SYLLABLE TUR;Lo;0;L;;;;;N;;;;; +A136;YI SYLLABLE DDIT;Lo;0;L;;;;;N;;;;; +A137;YI SYLLABLE DDIX;Lo;0;L;;;;;N;;;;; +A138;YI SYLLABLE DDI;Lo;0;L;;;;;N;;;;; +A139;YI SYLLABLE DDIP;Lo;0;L;;;;;N;;;;; +A13A;YI SYLLABLE DDIEX;Lo;0;L;;;;;N;;;;; +A13B;YI SYLLABLE DDIE;Lo;0;L;;;;;N;;;;; +A13C;YI SYLLABLE DDIEP;Lo;0;L;;;;;N;;;;; +A13D;YI SYLLABLE DDAT;Lo;0;L;;;;;N;;;;; +A13E;YI SYLLABLE DDAX;Lo;0;L;;;;;N;;;;; +A13F;YI SYLLABLE DDA;Lo;0;L;;;;;N;;;;; +A140;YI SYLLABLE DDAP;Lo;0;L;;;;;N;;;;; +A141;YI SYLLABLE DDUOX;Lo;0;L;;;;;N;;;;; +A142;YI SYLLABLE DDUO;Lo;0;L;;;;;N;;;;; +A143;YI SYLLABLE DDUOP;Lo;0;L;;;;;N;;;;; +A144;YI SYLLABLE DDOT;Lo;0;L;;;;;N;;;;; +A145;YI SYLLABLE DDOX;Lo;0;L;;;;;N;;;;; +A146;YI SYLLABLE DDO;Lo;0;L;;;;;N;;;;; +A147;YI SYLLABLE DDOP;Lo;0;L;;;;;N;;;;; +A148;YI SYLLABLE DDEX;Lo;0;L;;;;;N;;;;; +A149;YI SYLLABLE DDE;Lo;0;L;;;;;N;;;;; +A14A;YI SYLLABLE DDEP;Lo;0;L;;;;;N;;;;; +A14B;YI SYLLABLE DDUT;Lo;0;L;;;;;N;;;;; +A14C;YI SYLLABLE DDUX;Lo;0;L;;;;;N;;;;; +A14D;YI SYLLABLE DDU;Lo;0;L;;;;;N;;;;; +A14E;YI SYLLABLE DDUP;Lo;0;L;;;;;N;;;;; +A14F;YI SYLLABLE DDURX;Lo;0;L;;;;;N;;;;; +A150;YI SYLLABLE DDUR;Lo;0;L;;;;;N;;;;; +A151;YI SYLLABLE NDIT;Lo;0;L;;;;;N;;;;; +A152;YI SYLLABLE NDIX;Lo;0;L;;;;;N;;;;; +A153;YI SYLLABLE NDI;Lo;0;L;;;;;N;;;;; +A154;YI SYLLABLE NDIP;Lo;0;L;;;;;N;;;;; +A155;YI SYLLABLE NDIEX;Lo;0;L;;;;;N;;;;; +A156;YI SYLLABLE NDIE;Lo;0;L;;;;;N;;;;; +A157;YI SYLLABLE NDAT;Lo;0;L;;;;;N;;;;; +A158;YI SYLLABLE NDAX;Lo;0;L;;;;;N;;;;; +A159;YI SYLLABLE NDA;Lo;0;L;;;;;N;;;;; +A15A;YI SYLLABLE NDAP;Lo;0;L;;;;;N;;;;; +A15B;YI SYLLABLE NDOT;Lo;0;L;;;;;N;;;;; +A15C;YI SYLLABLE NDOX;Lo;0;L;;;;;N;;;;; +A15D;YI SYLLABLE NDO;Lo;0;L;;;;;N;;;;; +A15E;YI SYLLABLE NDOP;Lo;0;L;;;;;N;;;;; +A15F;YI SYLLABLE NDEX;Lo;0;L;;;;;N;;;;; +A160;YI SYLLABLE NDE;Lo;0;L;;;;;N;;;;; +A161;YI SYLLABLE NDEP;Lo;0;L;;;;;N;;;;; +A162;YI SYLLABLE NDUT;Lo;0;L;;;;;N;;;;; +A163;YI SYLLABLE NDUX;Lo;0;L;;;;;N;;;;; +A164;YI SYLLABLE NDU;Lo;0;L;;;;;N;;;;; +A165;YI SYLLABLE NDUP;Lo;0;L;;;;;N;;;;; +A166;YI SYLLABLE NDURX;Lo;0;L;;;;;N;;;;; +A167;YI SYLLABLE NDUR;Lo;0;L;;;;;N;;;;; +A168;YI SYLLABLE HNIT;Lo;0;L;;;;;N;;;;; +A169;YI SYLLABLE HNIX;Lo;0;L;;;;;N;;;;; +A16A;YI SYLLABLE HNI;Lo;0;L;;;;;N;;;;; +A16B;YI SYLLABLE HNIP;Lo;0;L;;;;;N;;;;; +A16C;YI SYLLABLE HNIET;Lo;0;L;;;;;N;;;;; +A16D;YI SYLLABLE HNIEX;Lo;0;L;;;;;N;;;;; +A16E;YI SYLLABLE HNIE;Lo;0;L;;;;;N;;;;; +A16F;YI SYLLABLE HNIEP;Lo;0;L;;;;;N;;;;; +A170;YI SYLLABLE HNAT;Lo;0;L;;;;;N;;;;; +A171;YI SYLLABLE HNAX;Lo;0;L;;;;;N;;;;; +A172;YI SYLLABLE HNA;Lo;0;L;;;;;N;;;;; +A173;YI SYLLABLE HNAP;Lo;0;L;;;;;N;;;;; +A174;YI SYLLABLE HNUOX;Lo;0;L;;;;;N;;;;; +A175;YI SYLLABLE HNUO;Lo;0;L;;;;;N;;;;; +A176;YI SYLLABLE HNOT;Lo;0;L;;;;;N;;;;; +A177;YI SYLLABLE HNOX;Lo;0;L;;;;;N;;;;; +A178;YI SYLLABLE HNOP;Lo;0;L;;;;;N;;;;; +A179;YI SYLLABLE HNEX;Lo;0;L;;;;;N;;;;; +A17A;YI SYLLABLE HNE;Lo;0;L;;;;;N;;;;; +A17B;YI SYLLABLE HNEP;Lo;0;L;;;;;N;;;;; +A17C;YI SYLLABLE HNUT;Lo;0;L;;;;;N;;;;; +A17D;YI SYLLABLE NIT;Lo;0;L;;;;;N;;;;; +A17E;YI SYLLABLE NIX;Lo;0;L;;;;;N;;;;; +A17F;YI SYLLABLE NI;Lo;0;L;;;;;N;;;;; +A180;YI SYLLABLE NIP;Lo;0;L;;;;;N;;;;; +A181;YI SYLLABLE NIEX;Lo;0;L;;;;;N;;;;; +A182;YI SYLLABLE NIE;Lo;0;L;;;;;N;;;;; +A183;YI SYLLABLE NIEP;Lo;0;L;;;;;N;;;;; +A184;YI SYLLABLE NAX;Lo;0;L;;;;;N;;;;; +A185;YI SYLLABLE NA;Lo;0;L;;;;;N;;;;; +A186;YI SYLLABLE NAP;Lo;0;L;;;;;N;;;;; +A187;YI SYLLABLE NUOX;Lo;0;L;;;;;N;;;;; +A188;YI SYLLABLE NUO;Lo;0;L;;;;;N;;;;; +A189;YI SYLLABLE NUOP;Lo;0;L;;;;;N;;;;; +A18A;YI SYLLABLE NOT;Lo;0;L;;;;;N;;;;; +A18B;YI SYLLABLE NOX;Lo;0;L;;;;;N;;;;; +A18C;YI SYLLABLE NO;Lo;0;L;;;;;N;;;;; +A18D;YI SYLLABLE NOP;Lo;0;L;;;;;N;;;;; +A18E;YI SYLLABLE NEX;Lo;0;L;;;;;N;;;;; +A18F;YI SYLLABLE NE;Lo;0;L;;;;;N;;;;; +A190;YI SYLLABLE NEP;Lo;0;L;;;;;N;;;;; +A191;YI SYLLABLE NUT;Lo;0;L;;;;;N;;;;; +A192;YI SYLLABLE NUX;Lo;0;L;;;;;N;;;;; +A193;YI SYLLABLE NU;Lo;0;L;;;;;N;;;;; +A194;YI SYLLABLE NUP;Lo;0;L;;;;;N;;;;; +A195;YI SYLLABLE NURX;Lo;0;L;;;;;N;;;;; +A196;YI SYLLABLE NUR;Lo;0;L;;;;;N;;;;; +A197;YI SYLLABLE HLIT;Lo;0;L;;;;;N;;;;; +A198;YI SYLLABLE HLIX;Lo;0;L;;;;;N;;;;; +A199;YI SYLLABLE HLI;Lo;0;L;;;;;N;;;;; +A19A;YI SYLLABLE HLIP;Lo;0;L;;;;;N;;;;; +A19B;YI SYLLABLE HLIEX;Lo;0;L;;;;;N;;;;; +A19C;YI SYLLABLE HLIE;Lo;0;L;;;;;N;;;;; +A19D;YI SYLLABLE HLIEP;Lo;0;L;;;;;N;;;;; +A19E;YI SYLLABLE HLAT;Lo;0;L;;;;;N;;;;; +A19F;YI SYLLABLE HLAX;Lo;0;L;;;;;N;;;;; +A1A0;YI SYLLABLE HLA;Lo;0;L;;;;;N;;;;; +A1A1;YI SYLLABLE HLAP;Lo;0;L;;;;;N;;;;; +A1A2;YI SYLLABLE HLUOX;Lo;0;L;;;;;N;;;;; +A1A3;YI SYLLABLE HLUO;Lo;0;L;;;;;N;;;;; +A1A4;YI SYLLABLE HLUOP;Lo;0;L;;;;;N;;;;; +A1A5;YI SYLLABLE HLOX;Lo;0;L;;;;;N;;;;; +A1A6;YI SYLLABLE HLO;Lo;0;L;;;;;N;;;;; +A1A7;YI SYLLABLE HLOP;Lo;0;L;;;;;N;;;;; +A1A8;YI SYLLABLE HLEX;Lo;0;L;;;;;N;;;;; +A1A9;YI SYLLABLE HLE;Lo;0;L;;;;;N;;;;; +A1AA;YI SYLLABLE HLEP;Lo;0;L;;;;;N;;;;; +A1AB;YI SYLLABLE HLUT;Lo;0;L;;;;;N;;;;; +A1AC;YI SYLLABLE HLUX;Lo;0;L;;;;;N;;;;; +A1AD;YI SYLLABLE HLU;Lo;0;L;;;;;N;;;;; +A1AE;YI SYLLABLE HLUP;Lo;0;L;;;;;N;;;;; +A1AF;YI SYLLABLE HLURX;Lo;0;L;;;;;N;;;;; +A1B0;YI SYLLABLE HLUR;Lo;0;L;;;;;N;;;;; +A1B1;YI SYLLABLE HLYT;Lo;0;L;;;;;N;;;;; +A1B2;YI SYLLABLE HLYX;Lo;0;L;;;;;N;;;;; +A1B3;YI SYLLABLE HLY;Lo;0;L;;;;;N;;;;; +A1B4;YI SYLLABLE HLYP;Lo;0;L;;;;;N;;;;; +A1B5;YI SYLLABLE HLYRX;Lo;0;L;;;;;N;;;;; +A1B6;YI SYLLABLE HLYR;Lo;0;L;;;;;N;;;;; +A1B7;YI SYLLABLE LIT;Lo;0;L;;;;;N;;;;; +A1B8;YI SYLLABLE LIX;Lo;0;L;;;;;N;;;;; +A1B9;YI SYLLABLE LI;Lo;0;L;;;;;N;;;;; +A1BA;YI SYLLABLE LIP;Lo;0;L;;;;;N;;;;; +A1BB;YI SYLLABLE LIET;Lo;0;L;;;;;N;;;;; +A1BC;YI SYLLABLE LIEX;Lo;0;L;;;;;N;;;;; +A1BD;YI SYLLABLE LIE;Lo;0;L;;;;;N;;;;; +A1BE;YI SYLLABLE LIEP;Lo;0;L;;;;;N;;;;; +A1BF;YI SYLLABLE LAT;Lo;0;L;;;;;N;;;;; +A1C0;YI SYLLABLE LAX;Lo;0;L;;;;;N;;;;; +A1C1;YI SYLLABLE LA;Lo;0;L;;;;;N;;;;; +A1C2;YI SYLLABLE LAP;Lo;0;L;;;;;N;;;;; +A1C3;YI SYLLABLE LUOT;Lo;0;L;;;;;N;;;;; +A1C4;YI SYLLABLE LUOX;Lo;0;L;;;;;N;;;;; +A1C5;YI SYLLABLE LUO;Lo;0;L;;;;;N;;;;; +A1C6;YI SYLLABLE LUOP;Lo;0;L;;;;;N;;;;; +A1C7;YI SYLLABLE LOT;Lo;0;L;;;;;N;;;;; +A1C8;YI SYLLABLE LOX;Lo;0;L;;;;;N;;;;; +A1C9;YI SYLLABLE LO;Lo;0;L;;;;;N;;;;; +A1CA;YI SYLLABLE LOP;Lo;0;L;;;;;N;;;;; +A1CB;YI SYLLABLE LEX;Lo;0;L;;;;;N;;;;; +A1CC;YI SYLLABLE LE;Lo;0;L;;;;;N;;;;; +A1CD;YI SYLLABLE LEP;Lo;0;L;;;;;N;;;;; +A1CE;YI SYLLABLE LUT;Lo;0;L;;;;;N;;;;; +A1CF;YI SYLLABLE LUX;Lo;0;L;;;;;N;;;;; +A1D0;YI SYLLABLE LU;Lo;0;L;;;;;N;;;;; +A1D1;YI SYLLABLE LUP;Lo;0;L;;;;;N;;;;; +A1D2;YI SYLLABLE LURX;Lo;0;L;;;;;N;;;;; +A1D3;YI SYLLABLE LUR;Lo;0;L;;;;;N;;;;; +A1D4;YI SYLLABLE LYT;Lo;0;L;;;;;N;;;;; +A1D5;YI SYLLABLE LYX;Lo;0;L;;;;;N;;;;; +A1D6;YI SYLLABLE LY;Lo;0;L;;;;;N;;;;; +A1D7;YI SYLLABLE LYP;Lo;0;L;;;;;N;;;;; +A1D8;YI SYLLABLE LYRX;Lo;0;L;;;;;N;;;;; +A1D9;YI SYLLABLE LYR;Lo;0;L;;;;;N;;;;; +A1DA;YI SYLLABLE GIT;Lo;0;L;;;;;N;;;;; +A1DB;YI SYLLABLE GIX;Lo;0;L;;;;;N;;;;; +A1DC;YI SYLLABLE GI;Lo;0;L;;;;;N;;;;; +A1DD;YI SYLLABLE GIP;Lo;0;L;;;;;N;;;;; +A1DE;YI SYLLABLE GIET;Lo;0;L;;;;;N;;;;; +A1DF;YI SYLLABLE GIEX;Lo;0;L;;;;;N;;;;; +A1E0;YI SYLLABLE GIE;Lo;0;L;;;;;N;;;;; +A1E1;YI SYLLABLE GIEP;Lo;0;L;;;;;N;;;;; +A1E2;YI SYLLABLE GAT;Lo;0;L;;;;;N;;;;; +A1E3;YI SYLLABLE GAX;Lo;0;L;;;;;N;;;;; +A1E4;YI SYLLABLE GA;Lo;0;L;;;;;N;;;;; +A1E5;YI SYLLABLE GAP;Lo;0;L;;;;;N;;;;; +A1E6;YI SYLLABLE GUOT;Lo;0;L;;;;;N;;;;; +A1E7;YI SYLLABLE GUOX;Lo;0;L;;;;;N;;;;; +A1E8;YI SYLLABLE GUO;Lo;0;L;;;;;N;;;;; +A1E9;YI SYLLABLE GUOP;Lo;0;L;;;;;N;;;;; +A1EA;YI SYLLABLE GOT;Lo;0;L;;;;;N;;;;; +A1EB;YI SYLLABLE GOX;Lo;0;L;;;;;N;;;;; +A1EC;YI SYLLABLE GO;Lo;0;L;;;;;N;;;;; +A1ED;YI SYLLABLE GOP;Lo;0;L;;;;;N;;;;; +A1EE;YI SYLLABLE GET;Lo;0;L;;;;;N;;;;; +A1EF;YI SYLLABLE GEX;Lo;0;L;;;;;N;;;;; +A1F0;YI SYLLABLE GE;Lo;0;L;;;;;N;;;;; +A1F1;YI SYLLABLE GEP;Lo;0;L;;;;;N;;;;; +A1F2;YI SYLLABLE GUT;Lo;0;L;;;;;N;;;;; +A1F3;YI SYLLABLE GUX;Lo;0;L;;;;;N;;;;; +A1F4;YI SYLLABLE GU;Lo;0;L;;;;;N;;;;; +A1F5;YI SYLLABLE GUP;Lo;0;L;;;;;N;;;;; +A1F6;YI SYLLABLE GURX;Lo;0;L;;;;;N;;;;; +A1F7;YI SYLLABLE GUR;Lo;0;L;;;;;N;;;;; +A1F8;YI SYLLABLE KIT;Lo;0;L;;;;;N;;;;; +A1F9;YI SYLLABLE KIX;Lo;0;L;;;;;N;;;;; +A1FA;YI SYLLABLE KI;Lo;0;L;;;;;N;;;;; +A1FB;YI SYLLABLE KIP;Lo;0;L;;;;;N;;;;; +A1FC;YI SYLLABLE KIEX;Lo;0;L;;;;;N;;;;; +A1FD;YI SYLLABLE KIE;Lo;0;L;;;;;N;;;;; +A1FE;YI SYLLABLE KIEP;Lo;0;L;;;;;N;;;;; +A1FF;YI SYLLABLE KAT;Lo;0;L;;;;;N;;;;; +A200;YI SYLLABLE KAX;Lo;0;L;;;;;N;;;;; +A201;YI SYLLABLE KA;Lo;0;L;;;;;N;;;;; +A202;YI SYLLABLE KAP;Lo;0;L;;;;;N;;;;; +A203;YI SYLLABLE KUOX;Lo;0;L;;;;;N;;;;; +A204;YI SYLLABLE KUO;Lo;0;L;;;;;N;;;;; +A205;YI SYLLABLE KUOP;Lo;0;L;;;;;N;;;;; +A206;YI SYLLABLE KOT;Lo;0;L;;;;;N;;;;; +A207;YI SYLLABLE KOX;Lo;0;L;;;;;N;;;;; +A208;YI SYLLABLE KO;Lo;0;L;;;;;N;;;;; +A209;YI SYLLABLE KOP;Lo;0;L;;;;;N;;;;; +A20A;YI SYLLABLE KET;Lo;0;L;;;;;N;;;;; +A20B;YI SYLLABLE KEX;Lo;0;L;;;;;N;;;;; +A20C;YI SYLLABLE KE;Lo;0;L;;;;;N;;;;; +A20D;YI SYLLABLE KEP;Lo;0;L;;;;;N;;;;; +A20E;YI SYLLABLE KUT;Lo;0;L;;;;;N;;;;; +A20F;YI SYLLABLE KUX;Lo;0;L;;;;;N;;;;; +A210;YI SYLLABLE KU;Lo;0;L;;;;;N;;;;; +A211;YI SYLLABLE KUP;Lo;0;L;;;;;N;;;;; +A212;YI SYLLABLE KURX;Lo;0;L;;;;;N;;;;; +A213;YI SYLLABLE KUR;Lo;0;L;;;;;N;;;;; +A214;YI SYLLABLE GGIT;Lo;0;L;;;;;N;;;;; +A215;YI SYLLABLE GGIX;Lo;0;L;;;;;N;;;;; +A216;YI SYLLABLE GGI;Lo;0;L;;;;;N;;;;; +A217;YI SYLLABLE GGIEX;Lo;0;L;;;;;N;;;;; +A218;YI SYLLABLE GGIE;Lo;0;L;;;;;N;;;;; +A219;YI SYLLABLE GGIEP;Lo;0;L;;;;;N;;;;; +A21A;YI SYLLABLE GGAT;Lo;0;L;;;;;N;;;;; +A21B;YI SYLLABLE GGAX;Lo;0;L;;;;;N;;;;; +A21C;YI SYLLABLE GGA;Lo;0;L;;;;;N;;;;; +A21D;YI SYLLABLE GGAP;Lo;0;L;;;;;N;;;;; +A21E;YI SYLLABLE GGUOT;Lo;0;L;;;;;N;;;;; +A21F;YI SYLLABLE GGUOX;Lo;0;L;;;;;N;;;;; +A220;YI SYLLABLE GGUO;Lo;0;L;;;;;N;;;;; +A221;YI SYLLABLE GGUOP;Lo;0;L;;;;;N;;;;; +A222;YI SYLLABLE GGOT;Lo;0;L;;;;;N;;;;; +A223;YI SYLLABLE GGOX;Lo;0;L;;;;;N;;;;; +A224;YI SYLLABLE GGO;Lo;0;L;;;;;N;;;;; +A225;YI SYLLABLE GGOP;Lo;0;L;;;;;N;;;;; +A226;YI SYLLABLE GGET;Lo;0;L;;;;;N;;;;; +A227;YI SYLLABLE GGEX;Lo;0;L;;;;;N;;;;; +A228;YI SYLLABLE GGE;Lo;0;L;;;;;N;;;;; +A229;YI SYLLABLE GGEP;Lo;0;L;;;;;N;;;;; +A22A;YI SYLLABLE GGUT;Lo;0;L;;;;;N;;;;; +A22B;YI SYLLABLE GGUX;Lo;0;L;;;;;N;;;;; +A22C;YI SYLLABLE GGU;Lo;0;L;;;;;N;;;;; +A22D;YI SYLLABLE GGUP;Lo;0;L;;;;;N;;;;; +A22E;YI SYLLABLE GGURX;Lo;0;L;;;;;N;;;;; +A22F;YI SYLLABLE GGUR;Lo;0;L;;;;;N;;;;; +A230;YI SYLLABLE MGIEX;Lo;0;L;;;;;N;;;;; +A231;YI SYLLABLE MGIE;Lo;0;L;;;;;N;;;;; +A232;YI SYLLABLE MGAT;Lo;0;L;;;;;N;;;;; +A233;YI SYLLABLE MGAX;Lo;0;L;;;;;N;;;;; +A234;YI SYLLABLE MGA;Lo;0;L;;;;;N;;;;; +A235;YI SYLLABLE MGAP;Lo;0;L;;;;;N;;;;; +A236;YI SYLLABLE MGUOX;Lo;0;L;;;;;N;;;;; +A237;YI SYLLABLE MGUO;Lo;0;L;;;;;N;;;;; +A238;YI SYLLABLE MGUOP;Lo;0;L;;;;;N;;;;; +A239;YI SYLLABLE MGOT;Lo;0;L;;;;;N;;;;; +A23A;YI SYLLABLE MGOX;Lo;0;L;;;;;N;;;;; +A23B;YI SYLLABLE MGO;Lo;0;L;;;;;N;;;;; +A23C;YI SYLLABLE MGOP;Lo;0;L;;;;;N;;;;; +A23D;YI SYLLABLE MGEX;Lo;0;L;;;;;N;;;;; +A23E;YI SYLLABLE MGE;Lo;0;L;;;;;N;;;;; +A23F;YI SYLLABLE MGEP;Lo;0;L;;;;;N;;;;; +A240;YI SYLLABLE MGUT;Lo;0;L;;;;;N;;;;; +A241;YI SYLLABLE MGUX;Lo;0;L;;;;;N;;;;; +A242;YI SYLLABLE MGU;Lo;0;L;;;;;N;;;;; +A243;YI SYLLABLE MGUP;Lo;0;L;;;;;N;;;;; +A244;YI SYLLABLE MGURX;Lo;0;L;;;;;N;;;;; +A245;YI SYLLABLE MGUR;Lo;0;L;;;;;N;;;;; +A246;YI SYLLABLE HXIT;Lo;0;L;;;;;N;;;;; +A247;YI SYLLABLE HXIX;Lo;0;L;;;;;N;;;;; +A248;YI SYLLABLE HXI;Lo;0;L;;;;;N;;;;; +A249;YI SYLLABLE HXIP;Lo;0;L;;;;;N;;;;; +A24A;YI SYLLABLE HXIET;Lo;0;L;;;;;N;;;;; +A24B;YI SYLLABLE HXIEX;Lo;0;L;;;;;N;;;;; +A24C;YI SYLLABLE HXIE;Lo;0;L;;;;;N;;;;; +A24D;YI SYLLABLE HXIEP;Lo;0;L;;;;;N;;;;; +A24E;YI SYLLABLE HXAT;Lo;0;L;;;;;N;;;;; +A24F;YI SYLLABLE HXAX;Lo;0;L;;;;;N;;;;; +A250;YI SYLLABLE HXA;Lo;0;L;;;;;N;;;;; +A251;YI SYLLABLE HXAP;Lo;0;L;;;;;N;;;;; +A252;YI SYLLABLE HXUOT;Lo;0;L;;;;;N;;;;; +A253;YI SYLLABLE HXUOX;Lo;0;L;;;;;N;;;;; +A254;YI SYLLABLE HXUO;Lo;0;L;;;;;N;;;;; +A255;YI SYLLABLE HXUOP;Lo;0;L;;;;;N;;;;; +A256;YI SYLLABLE HXOT;Lo;0;L;;;;;N;;;;; +A257;YI SYLLABLE HXOX;Lo;0;L;;;;;N;;;;; +A258;YI SYLLABLE HXO;Lo;0;L;;;;;N;;;;; +A259;YI SYLLABLE HXOP;Lo;0;L;;;;;N;;;;; +A25A;YI SYLLABLE HXEX;Lo;0;L;;;;;N;;;;; +A25B;YI SYLLABLE HXE;Lo;0;L;;;;;N;;;;; +A25C;YI SYLLABLE HXEP;Lo;0;L;;;;;N;;;;; +A25D;YI SYLLABLE NGIEX;Lo;0;L;;;;;N;;;;; +A25E;YI SYLLABLE NGIE;Lo;0;L;;;;;N;;;;; +A25F;YI SYLLABLE NGIEP;Lo;0;L;;;;;N;;;;; +A260;YI SYLLABLE NGAT;Lo;0;L;;;;;N;;;;; +A261;YI SYLLABLE NGAX;Lo;0;L;;;;;N;;;;; +A262;YI SYLLABLE NGA;Lo;0;L;;;;;N;;;;; +A263;YI SYLLABLE NGAP;Lo;0;L;;;;;N;;;;; +A264;YI SYLLABLE NGUOT;Lo;0;L;;;;;N;;;;; +A265;YI SYLLABLE NGUOX;Lo;0;L;;;;;N;;;;; +A266;YI SYLLABLE NGUO;Lo;0;L;;;;;N;;;;; +A267;YI SYLLABLE NGOT;Lo;0;L;;;;;N;;;;; +A268;YI SYLLABLE NGOX;Lo;0;L;;;;;N;;;;; +A269;YI SYLLABLE NGO;Lo;0;L;;;;;N;;;;; +A26A;YI SYLLABLE NGOP;Lo;0;L;;;;;N;;;;; +A26B;YI SYLLABLE NGEX;Lo;0;L;;;;;N;;;;; +A26C;YI SYLLABLE NGE;Lo;0;L;;;;;N;;;;; +A26D;YI SYLLABLE NGEP;Lo;0;L;;;;;N;;;;; +A26E;YI SYLLABLE HIT;Lo;0;L;;;;;N;;;;; +A26F;YI SYLLABLE HIEX;Lo;0;L;;;;;N;;;;; +A270;YI SYLLABLE HIE;Lo;0;L;;;;;N;;;;; +A271;YI SYLLABLE HAT;Lo;0;L;;;;;N;;;;; +A272;YI SYLLABLE HAX;Lo;0;L;;;;;N;;;;; +A273;YI SYLLABLE HA;Lo;0;L;;;;;N;;;;; +A274;YI SYLLABLE HAP;Lo;0;L;;;;;N;;;;; +A275;YI SYLLABLE HUOT;Lo;0;L;;;;;N;;;;; +A276;YI SYLLABLE HUOX;Lo;0;L;;;;;N;;;;; +A277;YI SYLLABLE HUO;Lo;0;L;;;;;N;;;;; +A278;YI SYLLABLE HUOP;Lo;0;L;;;;;N;;;;; +A279;YI SYLLABLE HOT;Lo;0;L;;;;;N;;;;; +A27A;YI SYLLABLE HOX;Lo;0;L;;;;;N;;;;; +A27B;YI SYLLABLE HO;Lo;0;L;;;;;N;;;;; +A27C;YI SYLLABLE HOP;Lo;0;L;;;;;N;;;;; +A27D;YI SYLLABLE HEX;Lo;0;L;;;;;N;;;;; +A27E;YI SYLLABLE HE;Lo;0;L;;;;;N;;;;; +A27F;YI SYLLABLE HEP;Lo;0;L;;;;;N;;;;; +A280;YI SYLLABLE WAT;Lo;0;L;;;;;N;;;;; +A281;YI SYLLABLE WAX;Lo;0;L;;;;;N;;;;; +A282;YI SYLLABLE WA;Lo;0;L;;;;;N;;;;; +A283;YI SYLLABLE WAP;Lo;0;L;;;;;N;;;;; +A284;YI SYLLABLE WUOX;Lo;0;L;;;;;N;;;;; +A285;YI SYLLABLE WUO;Lo;0;L;;;;;N;;;;; +A286;YI SYLLABLE WUOP;Lo;0;L;;;;;N;;;;; +A287;YI SYLLABLE WOX;Lo;0;L;;;;;N;;;;; +A288;YI SYLLABLE WO;Lo;0;L;;;;;N;;;;; +A289;YI SYLLABLE WOP;Lo;0;L;;;;;N;;;;; +A28A;YI SYLLABLE WEX;Lo;0;L;;;;;N;;;;; +A28B;YI SYLLABLE WE;Lo;0;L;;;;;N;;;;; +A28C;YI SYLLABLE WEP;Lo;0;L;;;;;N;;;;; +A28D;YI SYLLABLE ZIT;Lo;0;L;;;;;N;;;;; +A28E;YI SYLLABLE ZIX;Lo;0;L;;;;;N;;;;; +A28F;YI SYLLABLE ZI;Lo;0;L;;;;;N;;;;; +A290;YI SYLLABLE ZIP;Lo;0;L;;;;;N;;;;; +A291;YI SYLLABLE ZIEX;Lo;0;L;;;;;N;;;;; +A292;YI SYLLABLE ZIE;Lo;0;L;;;;;N;;;;; +A293;YI SYLLABLE ZIEP;Lo;0;L;;;;;N;;;;; +A294;YI SYLLABLE ZAT;Lo;0;L;;;;;N;;;;; +A295;YI SYLLABLE ZAX;Lo;0;L;;;;;N;;;;; +A296;YI SYLLABLE ZA;Lo;0;L;;;;;N;;;;; +A297;YI SYLLABLE ZAP;Lo;0;L;;;;;N;;;;; +A298;YI SYLLABLE ZUOX;Lo;0;L;;;;;N;;;;; +A299;YI SYLLABLE ZUO;Lo;0;L;;;;;N;;;;; +A29A;YI SYLLABLE ZUOP;Lo;0;L;;;;;N;;;;; +A29B;YI SYLLABLE ZOT;Lo;0;L;;;;;N;;;;; +A29C;YI SYLLABLE ZOX;Lo;0;L;;;;;N;;;;; +A29D;YI SYLLABLE ZO;Lo;0;L;;;;;N;;;;; +A29E;YI SYLLABLE ZOP;Lo;0;L;;;;;N;;;;; +A29F;YI SYLLABLE ZEX;Lo;0;L;;;;;N;;;;; +A2A0;YI SYLLABLE ZE;Lo;0;L;;;;;N;;;;; +A2A1;YI SYLLABLE ZEP;Lo;0;L;;;;;N;;;;; +A2A2;YI SYLLABLE ZUT;Lo;0;L;;;;;N;;;;; +A2A3;YI SYLLABLE ZUX;Lo;0;L;;;;;N;;;;; +A2A4;YI SYLLABLE ZU;Lo;0;L;;;;;N;;;;; +A2A5;YI SYLLABLE ZUP;Lo;0;L;;;;;N;;;;; +A2A6;YI SYLLABLE ZURX;Lo;0;L;;;;;N;;;;; +A2A7;YI SYLLABLE ZUR;Lo;0;L;;;;;N;;;;; +A2A8;YI SYLLABLE ZYT;Lo;0;L;;;;;N;;;;; +A2A9;YI SYLLABLE ZYX;Lo;0;L;;;;;N;;;;; +A2AA;YI SYLLABLE ZY;Lo;0;L;;;;;N;;;;; +A2AB;YI SYLLABLE ZYP;Lo;0;L;;;;;N;;;;; +A2AC;YI SYLLABLE ZYRX;Lo;0;L;;;;;N;;;;; +A2AD;YI SYLLABLE ZYR;Lo;0;L;;;;;N;;;;; +A2AE;YI SYLLABLE CIT;Lo;0;L;;;;;N;;;;; +A2AF;YI SYLLABLE CIX;Lo;0;L;;;;;N;;;;; +A2B0;YI SYLLABLE CI;Lo;0;L;;;;;N;;;;; +A2B1;YI SYLLABLE CIP;Lo;0;L;;;;;N;;;;; +A2B2;YI SYLLABLE CIET;Lo;0;L;;;;;N;;;;; +A2B3;YI SYLLABLE CIEX;Lo;0;L;;;;;N;;;;; +A2B4;YI SYLLABLE CIE;Lo;0;L;;;;;N;;;;; +A2B5;YI SYLLABLE CIEP;Lo;0;L;;;;;N;;;;; +A2B6;YI SYLLABLE CAT;Lo;0;L;;;;;N;;;;; +A2B7;YI SYLLABLE CAX;Lo;0;L;;;;;N;;;;; +A2B8;YI SYLLABLE CA;Lo;0;L;;;;;N;;;;; +A2B9;YI SYLLABLE CAP;Lo;0;L;;;;;N;;;;; +A2BA;YI SYLLABLE CUOX;Lo;0;L;;;;;N;;;;; +A2BB;YI SYLLABLE CUO;Lo;0;L;;;;;N;;;;; +A2BC;YI SYLLABLE CUOP;Lo;0;L;;;;;N;;;;; +A2BD;YI SYLLABLE COT;Lo;0;L;;;;;N;;;;; +A2BE;YI SYLLABLE COX;Lo;0;L;;;;;N;;;;; +A2BF;YI SYLLABLE CO;Lo;0;L;;;;;N;;;;; +A2C0;YI SYLLABLE COP;Lo;0;L;;;;;N;;;;; +A2C1;YI SYLLABLE CEX;Lo;0;L;;;;;N;;;;; +A2C2;YI SYLLABLE CE;Lo;0;L;;;;;N;;;;; +A2C3;YI SYLLABLE CEP;Lo;0;L;;;;;N;;;;; +A2C4;YI SYLLABLE CUT;Lo;0;L;;;;;N;;;;; +A2C5;YI SYLLABLE CUX;Lo;0;L;;;;;N;;;;; +A2C6;YI SYLLABLE CU;Lo;0;L;;;;;N;;;;; +A2C7;YI SYLLABLE CUP;Lo;0;L;;;;;N;;;;; +A2C8;YI SYLLABLE CURX;Lo;0;L;;;;;N;;;;; +A2C9;YI SYLLABLE CUR;Lo;0;L;;;;;N;;;;; +A2CA;YI SYLLABLE CYT;Lo;0;L;;;;;N;;;;; +A2CB;YI SYLLABLE CYX;Lo;0;L;;;;;N;;;;; +A2CC;YI SYLLABLE CY;Lo;0;L;;;;;N;;;;; +A2CD;YI SYLLABLE CYP;Lo;0;L;;;;;N;;;;; +A2CE;YI SYLLABLE CYRX;Lo;0;L;;;;;N;;;;; +A2CF;YI SYLLABLE CYR;Lo;0;L;;;;;N;;;;; +A2D0;YI SYLLABLE ZZIT;Lo;0;L;;;;;N;;;;; +A2D1;YI SYLLABLE ZZIX;Lo;0;L;;;;;N;;;;; +A2D2;YI SYLLABLE ZZI;Lo;0;L;;;;;N;;;;; +A2D3;YI SYLLABLE ZZIP;Lo;0;L;;;;;N;;;;; +A2D4;YI SYLLABLE ZZIET;Lo;0;L;;;;;N;;;;; +A2D5;YI SYLLABLE ZZIEX;Lo;0;L;;;;;N;;;;; +A2D6;YI SYLLABLE ZZIE;Lo;0;L;;;;;N;;;;; +A2D7;YI SYLLABLE ZZIEP;Lo;0;L;;;;;N;;;;; +A2D8;YI SYLLABLE ZZAT;Lo;0;L;;;;;N;;;;; +A2D9;YI SYLLABLE ZZAX;Lo;0;L;;;;;N;;;;; +A2DA;YI SYLLABLE ZZA;Lo;0;L;;;;;N;;;;; +A2DB;YI SYLLABLE ZZAP;Lo;0;L;;;;;N;;;;; +A2DC;YI SYLLABLE ZZOX;Lo;0;L;;;;;N;;;;; +A2DD;YI SYLLABLE ZZO;Lo;0;L;;;;;N;;;;; +A2DE;YI SYLLABLE ZZOP;Lo;0;L;;;;;N;;;;; +A2DF;YI SYLLABLE ZZEX;Lo;0;L;;;;;N;;;;; +A2E0;YI SYLLABLE ZZE;Lo;0;L;;;;;N;;;;; +A2E1;YI SYLLABLE ZZEP;Lo;0;L;;;;;N;;;;; +A2E2;YI SYLLABLE ZZUX;Lo;0;L;;;;;N;;;;; +A2E3;YI SYLLABLE ZZU;Lo;0;L;;;;;N;;;;; +A2E4;YI SYLLABLE ZZUP;Lo;0;L;;;;;N;;;;; +A2E5;YI SYLLABLE ZZURX;Lo;0;L;;;;;N;;;;; +A2E6;YI SYLLABLE ZZUR;Lo;0;L;;;;;N;;;;; +A2E7;YI SYLLABLE ZZYT;Lo;0;L;;;;;N;;;;; +A2E8;YI SYLLABLE ZZYX;Lo;0;L;;;;;N;;;;; +A2E9;YI SYLLABLE ZZY;Lo;0;L;;;;;N;;;;; +A2EA;YI SYLLABLE ZZYP;Lo;0;L;;;;;N;;;;; +A2EB;YI SYLLABLE ZZYRX;Lo;0;L;;;;;N;;;;; +A2EC;YI SYLLABLE ZZYR;Lo;0;L;;;;;N;;;;; +A2ED;YI SYLLABLE NZIT;Lo;0;L;;;;;N;;;;; +A2EE;YI SYLLABLE NZIX;Lo;0;L;;;;;N;;;;; +A2EF;YI SYLLABLE NZI;Lo;0;L;;;;;N;;;;; +A2F0;YI SYLLABLE NZIP;Lo;0;L;;;;;N;;;;; +A2F1;YI SYLLABLE NZIEX;Lo;0;L;;;;;N;;;;; +A2F2;YI SYLLABLE NZIE;Lo;0;L;;;;;N;;;;; +A2F3;YI SYLLABLE NZIEP;Lo;0;L;;;;;N;;;;; +A2F4;YI SYLLABLE NZAT;Lo;0;L;;;;;N;;;;; +A2F5;YI SYLLABLE NZAX;Lo;0;L;;;;;N;;;;; +A2F6;YI SYLLABLE NZA;Lo;0;L;;;;;N;;;;; +A2F7;YI SYLLABLE NZAP;Lo;0;L;;;;;N;;;;; +A2F8;YI SYLLABLE NZUOX;Lo;0;L;;;;;N;;;;; +A2F9;YI SYLLABLE NZUO;Lo;0;L;;;;;N;;;;; +A2FA;YI SYLLABLE NZOX;Lo;0;L;;;;;N;;;;; +A2FB;YI SYLLABLE NZOP;Lo;0;L;;;;;N;;;;; +A2FC;YI SYLLABLE NZEX;Lo;0;L;;;;;N;;;;; +A2FD;YI SYLLABLE NZE;Lo;0;L;;;;;N;;;;; +A2FE;YI SYLLABLE NZUX;Lo;0;L;;;;;N;;;;; +A2FF;YI SYLLABLE NZU;Lo;0;L;;;;;N;;;;; +A300;YI SYLLABLE NZUP;Lo;0;L;;;;;N;;;;; +A301;YI SYLLABLE NZURX;Lo;0;L;;;;;N;;;;; +A302;YI SYLLABLE NZUR;Lo;0;L;;;;;N;;;;; +A303;YI SYLLABLE NZYT;Lo;0;L;;;;;N;;;;; +A304;YI SYLLABLE NZYX;Lo;0;L;;;;;N;;;;; +A305;YI SYLLABLE NZY;Lo;0;L;;;;;N;;;;; +A306;YI SYLLABLE NZYP;Lo;0;L;;;;;N;;;;; +A307;YI SYLLABLE NZYRX;Lo;0;L;;;;;N;;;;; +A308;YI SYLLABLE NZYR;Lo;0;L;;;;;N;;;;; +A309;YI SYLLABLE SIT;Lo;0;L;;;;;N;;;;; +A30A;YI SYLLABLE SIX;Lo;0;L;;;;;N;;;;; +A30B;YI SYLLABLE SI;Lo;0;L;;;;;N;;;;; +A30C;YI SYLLABLE SIP;Lo;0;L;;;;;N;;;;; +A30D;YI SYLLABLE SIEX;Lo;0;L;;;;;N;;;;; +A30E;YI SYLLABLE SIE;Lo;0;L;;;;;N;;;;; +A30F;YI SYLLABLE SIEP;Lo;0;L;;;;;N;;;;; +A310;YI SYLLABLE SAT;Lo;0;L;;;;;N;;;;; +A311;YI SYLLABLE SAX;Lo;0;L;;;;;N;;;;; +A312;YI SYLLABLE SA;Lo;0;L;;;;;N;;;;; +A313;YI SYLLABLE SAP;Lo;0;L;;;;;N;;;;; +A314;YI SYLLABLE SUOX;Lo;0;L;;;;;N;;;;; +A315;YI SYLLABLE SUO;Lo;0;L;;;;;N;;;;; +A316;YI SYLLABLE SUOP;Lo;0;L;;;;;N;;;;; +A317;YI SYLLABLE SOT;Lo;0;L;;;;;N;;;;; +A318;YI SYLLABLE SOX;Lo;0;L;;;;;N;;;;; +A319;YI SYLLABLE SO;Lo;0;L;;;;;N;;;;; +A31A;YI SYLLABLE SOP;Lo;0;L;;;;;N;;;;; +A31B;YI SYLLABLE SEX;Lo;0;L;;;;;N;;;;; +A31C;YI SYLLABLE SE;Lo;0;L;;;;;N;;;;; +A31D;YI SYLLABLE SEP;Lo;0;L;;;;;N;;;;; +A31E;YI SYLLABLE SUT;Lo;0;L;;;;;N;;;;; +A31F;YI SYLLABLE SUX;Lo;0;L;;;;;N;;;;; +A320;YI SYLLABLE SU;Lo;0;L;;;;;N;;;;; +A321;YI SYLLABLE SUP;Lo;0;L;;;;;N;;;;; +A322;YI SYLLABLE SURX;Lo;0;L;;;;;N;;;;; +A323;YI SYLLABLE SUR;Lo;0;L;;;;;N;;;;; +A324;YI SYLLABLE SYT;Lo;0;L;;;;;N;;;;; +A325;YI SYLLABLE SYX;Lo;0;L;;;;;N;;;;; +A326;YI SYLLABLE SY;Lo;0;L;;;;;N;;;;; +A327;YI SYLLABLE SYP;Lo;0;L;;;;;N;;;;; +A328;YI SYLLABLE SYRX;Lo;0;L;;;;;N;;;;; +A329;YI SYLLABLE SYR;Lo;0;L;;;;;N;;;;; +A32A;YI SYLLABLE SSIT;Lo;0;L;;;;;N;;;;; +A32B;YI SYLLABLE SSIX;Lo;0;L;;;;;N;;;;; +A32C;YI SYLLABLE SSI;Lo;0;L;;;;;N;;;;; +A32D;YI SYLLABLE SSIP;Lo;0;L;;;;;N;;;;; +A32E;YI SYLLABLE SSIEX;Lo;0;L;;;;;N;;;;; +A32F;YI SYLLABLE SSIE;Lo;0;L;;;;;N;;;;; +A330;YI SYLLABLE SSIEP;Lo;0;L;;;;;N;;;;; +A331;YI SYLLABLE SSAT;Lo;0;L;;;;;N;;;;; +A332;YI SYLLABLE SSAX;Lo;0;L;;;;;N;;;;; +A333;YI SYLLABLE SSA;Lo;0;L;;;;;N;;;;; +A334;YI SYLLABLE SSAP;Lo;0;L;;;;;N;;;;; +A335;YI SYLLABLE SSOT;Lo;0;L;;;;;N;;;;; +A336;YI SYLLABLE SSOX;Lo;0;L;;;;;N;;;;; +A337;YI SYLLABLE SSO;Lo;0;L;;;;;N;;;;; +A338;YI SYLLABLE SSOP;Lo;0;L;;;;;N;;;;; +A339;YI SYLLABLE SSEX;Lo;0;L;;;;;N;;;;; +A33A;YI SYLLABLE SSE;Lo;0;L;;;;;N;;;;; +A33B;YI SYLLABLE SSEP;Lo;0;L;;;;;N;;;;; +A33C;YI SYLLABLE SSUT;Lo;0;L;;;;;N;;;;; +A33D;YI SYLLABLE SSUX;Lo;0;L;;;;;N;;;;; +A33E;YI SYLLABLE SSU;Lo;0;L;;;;;N;;;;; +A33F;YI SYLLABLE SSUP;Lo;0;L;;;;;N;;;;; +A340;YI SYLLABLE SSYT;Lo;0;L;;;;;N;;;;; +A341;YI SYLLABLE SSYX;Lo;0;L;;;;;N;;;;; +A342;YI SYLLABLE SSY;Lo;0;L;;;;;N;;;;; +A343;YI SYLLABLE SSYP;Lo;0;L;;;;;N;;;;; +A344;YI SYLLABLE SSYRX;Lo;0;L;;;;;N;;;;; +A345;YI SYLLABLE SSYR;Lo;0;L;;;;;N;;;;; +A346;YI SYLLABLE ZHAT;Lo;0;L;;;;;N;;;;; +A347;YI SYLLABLE ZHAX;Lo;0;L;;;;;N;;;;; +A348;YI SYLLABLE ZHA;Lo;0;L;;;;;N;;;;; +A349;YI SYLLABLE ZHAP;Lo;0;L;;;;;N;;;;; +A34A;YI SYLLABLE ZHUOX;Lo;0;L;;;;;N;;;;; +A34B;YI SYLLABLE ZHUO;Lo;0;L;;;;;N;;;;; +A34C;YI SYLLABLE ZHUOP;Lo;0;L;;;;;N;;;;; +A34D;YI SYLLABLE ZHOT;Lo;0;L;;;;;N;;;;; +A34E;YI SYLLABLE ZHOX;Lo;0;L;;;;;N;;;;; +A34F;YI SYLLABLE ZHO;Lo;0;L;;;;;N;;;;; +A350;YI SYLLABLE ZHOP;Lo;0;L;;;;;N;;;;; +A351;YI SYLLABLE ZHET;Lo;0;L;;;;;N;;;;; +A352;YI SYLLABLE ZHEX;Lo;0;L;;;;;N;;;;; +A353;YI SYLLABLE ZHE;Lo;0;L;;;;;N;;;;; +A354;YI SYLLABLE ZHEP;Lo;0;L;;;;;N;;;;; +A355;YI SYLLABLE ZHUT;Lo;0;L;;;;;N;;;;; +A356;YI SYLLABLE ZHUX;Lo;0;L;;;;;N;;;;; +A357;YI SYLLABLE ZHU;Lo;0;L;;;;;N;;;;; +A358;YI SYLLABLE ZHUP;Lo;0;L;;;;;N;;;;; +A359;YI SYLLABLE ZHURX;Lo;0;L;;;;;N;;;;; +A35A;YI SYLLABLE ZHUR;Lo;0;L;;;;;N;;;;; +A35B;YI SYLLABLE ZHYT;Lo;0;L;;;;;N;;;;; +A35C;YI SYLLABLE ZHYX;Lo;0;L;;;;;N;;;;; +A35D;YI SYLLABLE ZHY;Lo;0;L;;;;;N;;;;; +A35E;YI SYLLABLE ZHYP;Lo;0;L;;;;;N;;;;; +A35F;YI SYLLABLE ZHYRX;Lo;0;L;;;;;N;;;;; +A360;YI SYLLABLE ZHYR;Lo;0;L;;;;;N;;;;; +A361;YI SYLLABLE CHAT;Lo;0;L;;;;;N;;;;; +A362;YI SYLLABLE CHAX;Lo;0;L;;;;;N;;;;; +A363;YI SYLLABLE CHA;Lo;0;L;;;;;N;;;;; +A364;YI SYLLABLE CHAP;Lo;0;L;;;;;N;;;;; +A365;YI SYLLABLE CHUOT;Lo;0;L;;;;;N;;;;; +A366;YI SYLLABLE CHUOX;Lo;0;L;;;;;N;;;;; +A367;YI SYLLABLE CHUO;Lo;0;L;;;;;N;;;;; +A368;YI SYLLABLE CHUOP;Lo;0;L;;;;;N;;;;; +A369;YI SYLLABLE CHOT;Lo;0;L;;;;;N;;;;; +A36A;YI SYLLABLE CHOX;Lo;0;L;;;;;N;;;;; +A36B;YI SYLLABLE CHO;Lo;0;L;;;;;N;;;;; +A36C;YI SYLLABLE CHOP;Lo;0;L;;;;;N;;;;; +A36D;YI SYLLABLE CHET;Lo;0;L;;;;;N;;;;; +A36E;YI SYLLABLE CHEX;Lo;0;L;;;;;N;;;;; +A36F;YI SYLLABLE CHE;Lo;0;L;;;;;N;;;;; +A370;YI SYLLABLE CHEP;Lo;0;L;;;;;N;;;;; +A371;YI SYLLABLE CHUX;Lo;0;L;;;;;N;;;;; +A372;YI SYLLABLE CHU;Lo;0;L;;;;;N;;;;; +A373;YI SYLLABLE CHUP;Lo;0;L;;;;;N;;;;; +A374;YI SYLLABLE CHURX;Lo;0;L;;;;;N;;;;; +A375;YI SYLLABLE CHUR;Lo;0;L;;;;;N;;;;; +A376;YI SYLLABLE CHYT;Lo;0;L;;;;;N;;;;; +A377;YI SYLLABLE CHYX;Lo;0;L;;;;;N;;;;; +A378;YI SYLLABLE CHY;Lo;0;L;;;;;N;;;;; +A379;YI SYLLABLE CHYP;Lo;0;L;;;;;N;;;;; +A37A;YI SYLLABLE CHYRX;Lo;0;L;;;;;N;;;;; +A37B;YI SYLLABLE CHYR;Lo;0;L;;;;;N;;;;; +A37C;YI SYLLABLE RRAX;Lo;0;L;;;;;N;;;;; +A37D;YI SYLLABLE RRA;Lo;0;L;;;;;N;;;;; +A37E;YI SYLLABLE RRUOX;Lo;0;L;;;;;N;;;;; +A37F;YI SYLLABLE RRUO;Lo;0;L;;;;;N;;;;; +A380;YI SYLLABLE RROT;Lo;0;L;;;;;N;;;;; +A381;YI SYLLABLE RROX;Lo;0;L;;;;;N;;;;; +A382;YI SYLLABLE RRO;Lo;0;L;;;;;N;;;;; +A383;YI SYLLABLE RROP;Lo;0;L;;;;;N;;;;; +A384;YI SYLLABLE RRET;Lo;0;L;;;;;N;;;;; +A385;YI SYLLABLE RREX;Lo;0;L;;;;;N;;;;; +A386;YI SYLLABLE RRE;Lo;0;L;;;;;N;;;;; +A387;YI SYLLABLE RREP;Lo;0;L;;;;;N;;;;; +A388;YI SYLLABLE RRUT;Lo;0;L;;;;;N;;;;; +A389;YI SYLLABLE RRUX;Lo;0;L;;;;;N;;;;; +A38A;YI SYLLABLE RRU;Lo;0;L;;;;;N;;;;; +A38B;YI SYLLABLE RRUP;Lo;0;L;;;;;N;;;;; +A38C;YI SYLLABLE RRURX;Lo;0;L;;;;;N;;;;; +A38D;YI SYLLABLE RRUR;Lo;0;L;;;;;N;;;;; +A38E;YI SYLLABLE RRYT;Lo;0;L;;;;;N;;;;; +A38F;YI SYLLABLE RRYX;Lo;0;L;;;;;N;;;;; +A390;YI SYLLABLE RRY;Lo;0;L;;;;;N;;;;; +A391;YI SYLLABLE RRYP;Lo;0;L;;;;;N;;;;; +A392;YI SYLLABLE RRYRX;Lo;0;L;;;;;N;;;;; +A393;YI SYLLABLE RRYR;Lo;0;L;;;;;N;;;;; +A394;YI SYLLABLE NRAT;Lo;0;L;;;;;N;;;;; +A395;YI SYLLABLE NRAX;Lo;0;L;;;;;N;;;;; +A396;YI SYLLABLE NRA;Lo;0;L;;;;;N;;;;; +A397;YI SYLLABLE NRAP;Lo;0;L;;;;;N;;;;; +A398;YI SYLLABLE NROX;Lo;0;L;;;;;N;;;;; +A399;YI SYLLABLE NRO;Lo;0;L;;;;;N;;;;; +A39A;YI SYLLABLE NROP;Lo;0;L;;;;;N;;;;; +A39B;YI SYLLABLE NRET;Lo;0;L;;;;;N;;;;; +A39C;YI SYLLABLE NREX;Lo;0;L;;;;;N;;;;; +A39D;YI SYLLABLE NRE;Lo;0;L;;;;;N;;;;; +A39E;YI SYLLABLE NREP;Lo;0;L;;;;;N;;;;; +A39F;YI SYLLABLE NRUT;Lo;0;L;;;;;N;;;;; +A3A0;YI SYLLABLE NRUX;Lo;0;L;;;;;N;;;;; +A3A1;YI SYLLABLE NRU;Lo;0;L;;;;;N;;;;; +A3A2;YI SYLLABLE NRUP;Lo;0;L;;;;;N;;;;; +A3A3;YI SYLLABLE NRURX;Lo;0;L;;;;;N;;;;; +A3A4;YI SYLLABLE NRUR;Lo;0;L;;;;;N;;;;; +A3A5;YI SYLLABLE NRYT;Lo;0;L;;;;;N;;;;; +A3A6;YI SYLLABLE NRYX;Lo;0;L;;;;;N;;;;; +A3A7;YI SYLLABLE NRY;Lo;0;L;;;;;N;;;;; +A3A8;YI SYLLABLE NRYP;Lo;0;L;;;;;N;;;;; +A3A9;YI SYLLABLE NRYRX;Lo;0;L;;;;;N;;;;; +A3AA;YI SYLLABLE NRYR;Lo;0;L;;;;;N;;;;; +A3AB;YI SYLLABLE SHAT;Lo;0;L;;;;;N;;;;; +A3AC;YI SYLLABLE SHAX;Lo;0;L;;;;;N;;;;; +A3AD;YI SYLLABLE SHA;Lo;0;L;;;;;N;;;;; +A3AE;YI SYLLABLE SHAP;Lo;0;L;;;;;N;;;;; +A3AF;YI SYLLABLE SHUOX;Lo;0;L;;;;;N;;;;; +A3B0;YI SYLLABLE SHUO;Lo;0;L;;;;;N;;;;; +A3B1;YI SYLLABLE SHUOP;Lo;0;L;;;;;N;;;;; +A3B2;YI SYLLABLE SHOT;Lo;0;L;;;;;N;;;;; +A3B3;YI SYLLABLE SHOX;Lo;0;L;;;;;N;;;;; +A3B4;YI SYLLABLE SHO;Lo;0;L;;;;;N;;;;; +A3B5;YI SYLLABLE SHOP;Lo;0;L;;;;;N;;;;; +A3B6;YI SYLLABLE SHET;Lo;0;L;;;;;N;;;;; +A3B7;YI SYLLABLE SHEX;Lo;0;L;;;;;N;;;;; +A3B8;YI SYLLABLE SHE;Lo;0;L;;;;;N;;;;; +A3B9;YI SYLLABLE SHEP;Lo;0;L;;;;;N;;;;; +A3BA;YI SYLLABLE SHUT;Lo;0;L;;;;;N;;;;; +A3BB;YI SYLLABLE SHUX;Lo;0;L;;;;;N;;;;; +A3BC;YI SYLLABLE SHU;Lo;0;L;;;;;N;;;;; +A3BD;YI SYLLABLE SHUP;Lo;0;L;;;;;N;;;;; +A3BE;YI SYLLABLE SHURX;Lo;0;L;;;;;N;;;;; +A3BF;YI SYLLABLE SHUR;Lo;0;L;;;;;N;;;;; +A3C0;YI SYLLABLE SHYT;Lo;0;L;;;;;N;;;;; +A3C1;YI SYLLABLE SHYX;Lo;0;L;;;;;N;;;;; +A3C2;YI SYLLABLE SHY;Lo;0;L;;;;;N;;;;; +A3C3;YI SYLLABLE SHYP;Lo;0;L;;;;;N;;;;; +A3C4;YI SYLLABLE SHYRX;Lo;0;L;;;;;N;;;;; +A3C5;YI SYLLABLE SHYR;Lo;0;L;;;;;N;;;;; +A3C6;YI SYLLABLE RAT;Lo;0;L;;;;;N;;;;; +A3C7;YI SYLLABLE RAX;Lo;0;L;;;;;N;;;;; +A3C8;YI SYLLABLE RA;Lo;0;L;;;;;N;;;;; +A3C9;YI SYLLABLE RAP;Lo;0;L;;;;;N;;;;; +A3CA;YI SYLLABLE RUOX;Lo;0;L;;;;;N;;;;; +A3CB;YI SYLLABLE RUO;Lo;0;L;;;;;N;;;;; +A3CC;YI SYLLABLE RUOP;Lo;0;L;;;;;N;;;;; +A3CD;YI SYLLABLE ROT;Lo;0;L;;;;;N;;;;; +A3CE;YI SYLLABLE ROX;Lo;0;L;;;;;N;;;;; +A3CF;YI SYLLABLE RO;Lo;0;L;;;;;N;;;;; +A3D0;YI SYLLABLE ROP;Lo;0;L;;;;;N;;;;; +A3D1;YI SYLLABLE REX;Lo;0;L;;;;;N;;;;; +A3D2;YI SYLLABLE RE;Lo;0;L;;;;;N;;;;; +A3D3;YI SYLLABLE REP;Lo;0;L;;;;;N;;;;; +A3D4;YI SYLLABLE RUT;Lo;0;L;;;;;N;;;;; +A3D5;YI SYLLABLE RUX;Lo;0;L;;;;;N;;;;; +A3D6;YI SYLLABLE RU;Lo;0;L;;;;;N;;;;; +A3D7;YI SYLLABLE RUP;Lo;0;L;;;;;N;;;;; +A3D8;YI SYLLABLE RURX;Lo;0;L;;;;;N;;;;; +A3D9;YI SYLLABLE RUR;Lo;0;L;;;;;N;;;;; +A3DA;YI SYLLABLE RYT;Lo;0;L;;;;;N;;;;; +A3DB;YI SYLLABLE RYX;Lo;0;L;;;;;N;;;;; +A3DC;YI SYLLABLE RY;Lo;0;L;;;;;N;;;;; +A3DD;YI SYLLABLE RYP;Lo;0;L;;;;;N;;;;; +A3DE;YI SYLLABLE RYRX;Lo;0;L;;;;;N;;;;; +A3DF;YI SYLLABLE RYR;Lo;0;L;;;;;N;;;;; +A3E0;YI SYLLABLE JIT;Lo;0;L;;;;;N;;;;; +A3E1;YI SYLLABLE JIX;Lo;0;L;;;;;N;;;;; +A3E2;YI SYLLABLE JI;Lo;0;L;;;;;N;;;;; +A3E3;YI SYLLABLE JIP;Lo;0;L;;;;;N;;;;; +A3E4;YI SYLLABLE JIET;Lo;0;L;;;;;N;;;;; +A3E5;YI SYLLABLE JIEX;Lo;0;L;;;;;N;;;;; +A3E6;YI SYLLABLE JIE;Lo;0;L;;;;;N;;;;; +A3E7;YI SYLLABLE JIEP;Lo;0;L;;;;;N;;;;; +A3E8;YI SYLLABLE JUOT;Lo;0;L;;;;;N;;;;; +A3E9;YI SYLLABLE JUOX;Lo;0;L;;;;;N;;;;; +A3EA;YI SYLLABLE JUO;Lo;0;L;;;;;N;;;;; +A3EB;YI SYLLABLE JUOP;Lo;0;L;;;;;N;;;;; +A3EC;YI SYLLABLE JOT;Lo;0;L;;;;;N;;;;; +A3ED;YI SYLLABLE JOX;Lo;0;L;;;;;N;;;;; +A3EE;YI SYLLABLE JO;Lo;0;L;;;;;N;;;;; +A3EF;YI SYLLABLE JOP;Lo;0;L;;;;;N;;;;; +A3F0;YI SYLLABLE JUT;Lo;0;L;;;;;N;;;;; +A3F1;YI SYLLABLE JUX;Lo;0;L;;;;;N;;;;; +A3F2;YI SYLLABLE JU;Lo;0;L;;;;;N;;;;; +A3F3;YI SYLLABLE JUP;Lo;0;L;;;;;N;;;;; +A3F4;YI SYLLABLE JURX;Lo;0;L;;;;;N;;;;; +A3F5;YI SYLLABLE JUR;Lo;0;L;;;;;N;;;;; +A3F6;YI SYLLABLE JYT;Lo;0;L;;;;;N;;;;; +A3F7;YI SYLLABLE JYX;Lo;0;L;;;;;N;;;;; +A3F8;YI SYLLABLE JY;Lo;0;L;;;;;N;;;;; +A3F9;YI SYLLABLE JYP;Lo;0;L;;;;;N;;;;; +A3FA;YI SYLLABLE JYRX;Lo;0;L;;;;;N;;;;; +A3FB;YI SYLLABLE JYR;Lo;0;L;;;;;N;;;;; +A3FC;YI SYLLABLE QIT;Lo;0;L;;;;;N;;;;; +A3FD;YI SYLLABLE QIX;Lo;0;L;;;;;N;;;;; +A3FE;YI SYLLABLE QI;Lo;0;L;;;;;N;;;;; +A3FF;YI SYLLABLE QIP;Lo;0;L;;;;;N;;;;; +A400;YI SYLLABLE QIET;Lo;0;L;;;;;N;;;;; +A401;YI SYLLABLE QIEX;Lo;0;L;;;;;N;;;;; +A402;YI SYLLABLE QIE;Lo;0;L;;;;;N;;;;; +A403;YI SYLLABLE QIEP;Lo;0;L;;;;;N;;;;; +A404;YI SYLLABLE QUOT;Lo;0;L;;;;;N;;;;; +A405;YI SYLLABLE QUOX;Lo;0;L;;;;;N;;;;; +A406;YI SYLLABLE QUO;Lo;0;L;;;;;N;;;;; +A407;YI SYLLABLE QUOP;Lo;0;L;;;;;N;;;;; +A408;YI SYLLABLE QOT;Lo;0;L;;;;;N;;;;; +A409;YI SYLLABLE QOX;Lo;0;L;;;;;N;;;;; +A40A;YI SYLLABLE QO;Lo;0;L;;;;;N;;;;; +A40B;YI SYLLABLE QOP;Lo;0;L;;;;;N;;;;; +A40C;YI SYLLABLE QUT;Lo;0;L;;;;;N;;;;; +A40D;YI SYLLABLE QUX;Lo;0;L;;;;;N;;;;; +A40E;YI SYLLABLE QU;Lo;0;L;;;;;N;;;;; +A40F;YI SYLLABLE QUP;Lo;0;L;;;;;N;;;;; +A410;YI SYLLABLE QURX;Lo;0;L;;;;;N;;;;; +A411;YI SYLLABLE QUR;Lo;0;L;;;;;N;;;;; +A412;YI SYLLABLE QYT;Lo;0;L;;;;;N;;;;; +A413;YI SYLLABLE QYX;Lo;0;L;;;;;N;;;;; +A414;YI SYLLABLE QY;Lo;0;L;;;;;N;;;;; +A415;YI SYLLABLE QYP;Lo;0;L;;;;;N;;;;; +A416;YI SYLLABLE QYRX;Lo;0;L;;;;;N;;;;; +A417;YI SYLLABLE QYR;Lo;0;L;;;;;N;;;;; +A418;YI SYLLABLE JJIT;Lo;0;L;;;;;N;;;;; +A419;YI SYLLABLE JJIX;Lo;0;L;;;;;N;;;;; +A41A;YI SYLLABLE JJI;Lo;0;L;;;;;N;;;;; +A41B;YI SYLLABLE JJIP;Lo;0;L;;;;;N;;;;; +A41C;YI SYLLABLE JJIET;Lo;0;L;;;;;N;;;;; +A41D;YI SYLLABLE JJIEX;Lo;0;L;;;;;N;;;;; +A41E;YI SYLLABLE JJIE;Lo;0;L;;;;;N;;;;; +A41F;YI SYLLABLE JJIEP;Lo;0;L;;;;;N;;;;; +A420;YI SYLLABLE JJUOX;Lo;0;L;;;;;N;;;;; +A421;YI SYLLABLE JJUO;Lo;0;L;;;;;N;;;;; +A422;YI SYLLABLE JJUOP;Lo;0;L;;;;;N;;;;; +A423;YI SYLLABLE JJOT;Lo;0;L;;;;;N;;;;; +A424;YI SYLLABLE JJOX;Lo;0;L;;;;;N;;;;; +A425;YI SYLLABLE JJO;Lo;0;L;;;;;N;;;;; +A426;YI SYLLABLE JJOP;Lo;0;L;;;;;N;;;;; +A427;YI SYLLABLE JJUT;Lo;0;L;;;;;N;;;;; +A428;YI SYLLABLE JJUX;Lo;0;L;;;;;N;;;;; +A429;YI SYLLABLE JJU;Lo;0;L;;;;;N;;;;; +A42A;YI SYLLABLE JJUP;Lo;0;L;;;;;N;;;;; +A42B;YI SYLLABLE JJURX;Lo;0;L;;;;;N;;;;; +A42C;YI SYLLABLE JJUR;Lo;0;L;;;;;N;;;;; +A42D;YI SYLLABLE JJYT;Lo;0;L;;;;;N;;;;; +A42E;YI SYLLABLE JJYX;Lo;0;L;;;;;N;;;;; +A42F;YI SYLLABLE JJY;Lo;0;L;;;;;N;;;;; +A430;YI SYLLABLE JJYP;Lo;0;L;;;;;N;;;;; +A431;YI SYLLABLE NJIT;Lo;0;L;;;;;N;;;;; +A432;YI SYLLABLE NJIX;Lo;0;L;;;;;N;;;;; +A433;YI SYLLABLE NJI;Lo;0;L;;;;;N;;;;; +A434;YI SYLLABLE NJIP;Lo;0;L;;;;;N;;;;; +A435;YI SYLLABLE NJIET;Lo;0;L;;;;;N;;;;; +A436;YI SYLLABLE NJIEX;Lo;0;L;;;;;N;;;;; +A437;YI SYLLABLE NJIE;Lo;0;L;;;;;N;;;;; +A438;YI SYLLABLE NJIEP;Lo;0;L;;;;;N;;;;; +A439;YI SYLLABLE NJUOX;Lo;0;L;;;;;N;;;;; +A43A;YI SYLLABLE NJUO;Lo;0;L;;;;;N;;;;; +A43B;YI SYLLABLE NJOT;Lo;0;L;;;;;N;;;;; +A43C;YI SYLLABLE NJOX;Lo;0;L;;;;;N;;;;; +A43D;YI SYLLABLE NJO;Lo;0;L;;;;;N;;;;; +A43E;YI SYLLABLE NJOP;Lo;0;L;;;;;N;;;;; +A43F;YI SYLLABLE NJUX;Lo;0;L;;;;;N;;;;; +A440;YI SYLLABLE NJU;Lo;0;L;;;;;N;;;;; +A441;YI SYLLABLE NJUP;Lo;0;L;;;;;N;;;;; +A442;YI SYLLABLE NJURX;Lo;0;L;;;;;N;;;;; +A443;YI SYLLABLE NJUR;Lo;0;L;;;;;N;;;;; +A444;YI SYLLABLE NJYT;Lo;0;L;;;;;N;;;;; +A445;YI SYLLABLE NJYX;Lo;0;L;;;;;N;;;;; +A446;YI SYLLABLE NJY;Lo;0;L;;;;;N;;;;; +A447;YI SYLLABLE NJYP;Lo;0;L;;;;;N;;;;; +A448;YI SYLLABLE NJYRX;Lo;0;L;;;;;N;;;;; +A449;YI SYLLABLE NJYR;Lo;0;L;;;;;N;;;;; +A44A;YI SYLLABLE NYIT;Lo;0;L;;;;;N;;;;; +A44B;YI SYLLABLE NYIX;Lo;0;L;;;;;N;;;;; +A44C;YI SYLLABLE NYI;Lo;0;L;;;;;N;;;;; +A44D;YI SYLLABLE NYIP;Lo;0;L;;;;;N;;;;; +A44E;YI SYLLABLE NYIET;Lo;0;L;;;;;N;;;;; +A44F;YI SYLLABLE NYIEX;Lo;0;L;;;;;N;;;;; +A450;YI SYLLABLE NYIE;Lo;0;L;;;;;N;;;;; +A451;YI SYLLABLE NYIEP;Lo;0;L;;;;;N;;;;; +A452;YI SYLLABLE NYUOX;Lo;0;L;;;;;N;;;;; +A453;YI SYLLABLE NYUO;Lo;0;L;;;;;N;;;;; +A454;YI SYLLABLE NYUOP;Lo;0;L;;;;;N;;;;; +A455;YI SYLLABLE NYOT;Lo;0;L;;;;;N;;;;; +A456;YI SYLLABLE NYOX;Lo;0;L;;;;;N;;;;; +A457;YI SYLLABLE NYO;Lo;0;L;;;;;N;;;;; +A458;YI SYLLABLE NYOP;Lo;0;L;;;;;N;;;;; +A459;YI SYLLABLE NYUT;Lo;0;L;;;;;N;;;;; +A45A;YI SYLLABLE NYUX;Lo;0;L;;;;;N;;;;; +A45B;YI SYLLABLE NYU;Lo;0;L;;;;;N;;;;; +A45C;YI SYLLABLE NYUP;Lo;0;L;;;;;N;;;;; +A45D;YI SYLLABLE XIT;Lo;0;L;;;;;N;;;;; +A45E;YI SYLLABLE XIX;Lo;0;L;;;;;N;;;;; +A45F;YI SYLLABLE XI;Lo;0;L;;;;;N;;;;; +A460;YI SYLLABLE XIP;Lo;0;L;;;;;N;;;;; +A461;YI SYLLABLE XIET;Lo;0;L;;;;;N;;;;; +A462;YI SYLLABLE XIEX;Lo;0;L;;;;;N;;;;; +A463;YI SYLLABLE XIE;Lo;0;L;;;;;N;;;;; +A464;YI SYLLABLE XIEP;Lo;0;L;;;;;N;;;;; +A465;YI SYLLABLE XUOX;Lo;0;L;;;;;N;;;;; +A466;YI SYLLABLE XUO;Lo;0;L;;;;;N;;;;; +A467;YI SYLLABLE XOT;Lo;0;L;;;;;N;;;;; +A468;YI SYLLABLE XOX;Lo;0;L;;;;;N;;;;; +A469;YI SYLLABLE XO;Lo;0;L;;;;;N;;;;; +A46A;YI SYLLABLE XOP;Lo;0;L;;;;;N;;;;; +A46B;YI SYLLABLE XYT;Lo;0;L;;;;;N;;;;; +A46C;YI SYLLABLE XYX;Lo;0;L;;;;;N;;;;; +A46D;YI SYLLABLE XY;Lo;0;L;;;;;N;;;;; +A46E;YI SYLLABLE XYP;Lo;0;L;;;;;N;;;;; +A46F;YI SYLLABLE XYRX;Lo;0;L;;;;;N;;;;; +A470;YI SYLLABLE XYR;Lo;0;L;;;;;N;;;;; +A471;YI SYLLABLE YIT;Lo;0;L;;;;;N;;;;; +A472;YI SYLLABLE YIX;Lo;0;L;;;;;N;;;;; +A473;YI SYLLABLE YI;Lo;0;L;;;;;N;;;;; +A474;YI SYLLABLE YIP;Lo;0;L;;;;;N;;;;; +A475;YI SYLLABLE YIET;Lo;0;L;;;;;N;;;;; +A476;YI SYLLABLE YIEX;Lo;0;L;;;;;N;;;;; +A477;YI SYLLABLE YIE;Lo;0;L;;;;;N;;;;; +A478;YI SYLLABLE YIEP;Lo;0;L;;;;;N;;;;; +A479;YI SYLLABLE YUOT;Lo;0;L;;;;;N;;;;; +A47A;YI SYLLABLE YUOX;Lo;0;L;;;;;N;;;;; +A47B;YI SYLLABLE YUO;Lo;0;L;;;;;N;;;;; +A47C;YI SYLLABLE YUOP;Lo;0;L;;;;;N;;;;; +A47D;YI SYLLABLE YOT;Lo;0;L;;;;;N;;;;; +A47E;YI SYLLABLE YOX;Lo;0;L;;;;;N;;;;; +A47F;YI SYLLABLE YO;Lo;0;L;;;;;N;;;;; +A480;YI SYLLABLE YOP;Lo;0;L;;;;;N;;;;; +A481;YI SYLLABLE YUT;Lo;0;L;;;;;N;;;;; +A482;YI SYLLABLE YUX;Lo;0;L;;;;;N;;;;; +A483;YI SYLLABLE YU;Lo;0;L;;;;;N;;;;; +A484;YI SYLLABLE YUP;Lo;0;L;;;;;N;;;;; +A485;YI SYLLABLE YURX;Lo;0;L;;;;;N;;;;; +A486;YI SYLLABLE YUR;Lo;0;L;;;;;N;;;;; +A487;YI SYLLABLE YYT;Lo;0;L;;;;;N;;;;; +A488;YI SYLLABLE YYX;Lo;0;L;;;;;N;;;;; +A489;YI SYLLABLE YY;Lo;0;L;;;;;N;;;;; +A48A;YI SYLLABLE YYP;Lo;0;L;;;;;N;;;;; +A48B;YI SYLLABLE YYRX;Lo;0;L;;;;;N;;;;; +A48C;YI SYLLABLE YYR;Lo;0;L;;;;;N;;;;; +A490;YI RADICAL QOT;So;0;ON;;;;;N;;;;; +A491;YI RADICAL LI;So;0;ON;;;;;N;;;;; +A492;YI RADICAL KIT;So;0;ON;;;;;N;;;;; +A493;YI RADICAL NYIP;So;0;ON;;;;;N;;;;; +A494;YI RADICAL CYP;So;0;ON;;;;;N;;;;; +A495;YI RADICAL SSI;So;0;ON;;;;;N;;;;; +A496;YI RADICAL GGOP;So;0;ON;;;;;N;;;;; +A497;YI RADICAL GEP;So;0;ON;;;;;N;;;;; +A498;YI RADICAL MI;So;0;ON;;;;;N;;;;; +A499;YI RADICAL HXIT;So;0;ON;;;;;N;;;;; +A49A;YI RADICAL LYR;So;0;ON;;;;;N;;;;; +A49B;YI RADICAL BBUT;So;0;ON;;;;;N;;;;; +A49C;YI RADICAL MOP;So;0;ON;;;;;N;;;;; +A49D;YI RADICAL YO;So;0;ON;;;;;N;;;;; +A49E;YI RADICAL PUT;So;0;ON;;;;;N;;;;; +A49F;YI RADICAL HXUO;So;0;ON;;;;;N;;;;; +A4A0;YI RADICAL TAT;So;0;ON;;;;;N;;;;; +A4A1;YI RADICAL GA;So;0;ON;;;;;N;;;;; +A4A2;YI RADICAL ZUP;So;0;ON;;;;;N;;;;; +A4A3;YI RADICAL CYT;So;0;ON;;;;;N;;;;; +A4A4;YI RADICAL DDUR;So;0;ON;;;;;N;;;;; +A4A5;YI RADICAL BUR;So;0;ON;;;;;N;;;;; +A4A6;YI RADICAL GGUO;So;0;ON;;;;;N;;;;; +A4A7;YI RADICAL NYOP;So;0;ON;;;;;N;;;;; +A4A8;YI RADICAL TU;So;0;ON;;;;;N;;;;; +A4A9;YI RADICAL OP;So;0;ON;;;;;N;;;;; +A4AA;YI RADICAL JJUT;So;0;ON;;;;;N;;;;; +A4AB;YI RADICAL ZOT;So;0;ON;;;;;N;;;;; +A4AC;YI RADICAL PYT;So;0;ON;;;;;N;;;;; +A4AD;YI RADICAL HMO;So;0;ON;;;;;N;;;;; +A4AE;YI RADICAL YIT;So;0;ON;;;;;N;;;;; +A4AF;YI RADICAL VUR;So;0;ON;;;;;N;;;;; +A4B0;YI RADICAL SHY;So;0;ON;;;;;N;;;;; +A4B1;YI RADICAL VEP;So;0;ON;;;;;N;;;;; +A4B2;YI RADICAL ZA;So;0;ON;;;;;N;;;;; +A4B3;YI RADICAL JO;So;0;ON;;;;;N;;;;; +A4B4;YI RADICAL NZUP;So;0;ON;;;;;N;;;;; +A4B5;YI RADICAL JJY;So;0;ON;;;;;N;;;;; +A4B6;YI RADICAL GOT;So;0;ON;;;;;N;;;;; +A4B7;YI RADICAL JJIE;So;0;ON;;;;;N;;;;; +A4B8;YI RADICAL WO;So;0;ON;;;;;N;;;;; +A4B9;YI RADICAL DU;So;0;ON;;;;;N;;;;; +A4BA;YI RADICAL SHUR;So;0;ON;;;;;N;;;;; +A4BB;YI RADICAL LIE;So;0;ON;;;;;N;;;;; +A4BC;YI RADICAL CY;So;0;ON;;;;;N;;;;; +A4BD;YI RADICAL CUOP;So;0;ON;;;;;N;;;;; +A4BE;YI RADICAL CIP;So;0;ON;;;;;N;;;;; +A4BF;YI RADICAL HXOP;So;0;ON;;;;;N;;;;; +A4C0;YI RADICAL SHAT;So;0;ON;;;;;N;;;;; +A4C1;YI RADICAL ZUR;So;0;ON;;;;;N;;;;; +A4C2;YI RADICAL SHOP;So;0;ON;;;;;N;;;;; +A4C3;YI RADICAL CHE;So;0;ON;;;;;N;;;;; +A4C4;YI RADICAL ZZIET;So;0;ON;;;;;N;;;;; +A4C5;YI RADICAL NBIE;So;0;ON;;;;;N;;;;; +A4C6;YI RADICAL KE;So;0;ON;;;;;N;;;;; +AC00;<Hangul Syllable, First>;Lo;0;L;;;;;N;;;;; +D7A3;<Hangul Syllable, Last>;Lo;0;L;;;;;N;;;;; +D800;<Non Private Use High Surrogate, First>;Cs;0;L;;;;;N;;;;; +DB7F;<Non Private Use High Surrogate, Last>;Cs;0;L;;;;;N;;;;; +DB80;<Private Use High Surrogate, First>;Cs;0;L;;;;;N;;;;; +DBFF;<Private Use High Surrogate, Last>;Cs;0;L;;;;;N;;;;; +DC00;<Low Surrogate, First>;Cs;0;L;;;;;N;;;;; +DFFF;<Low Surrogate, Last>;Cs;0;L;;;;;N;;;;; +E000;<Private Use, First>;Co;0;L;;;;;N;;;;; +F8FF;<Private Use, Last>;Co;0;L;;;;;N;;;;; +F900;CJK COMPATIBILITY IDEOGRAPH-F900;Lo;0;L;8C48;;;;N;;;;; +F901;CJK COMPATIBILITY IDEOGRAPH-F901;Lo;0;L;66F4;;;;N;;;;; +F902;CJK COMPATIBILITY IDEOGRAPH-F902;Lo;0;L;8ECA;;;;N;;;;; +F903;CJK COMPATIBILITY IDEOGRAPH-F903;Lo;0;L;8CC8;;;;N;;;;; +F904;CJK COMPATIBILITY IDEOGRAPH-F904;Lo;0;L;6ED1;;;;N;;;;; +F905;CJK COMPATIBILITY IDEOGRAPH-F905;Lo;0;L;4E32;;;;N;;;;; +F906;CJK COMPATIBILITY IDEOGRAPH-F906;Lo;0;L;53E5;;;;N;;;;; +F907;CJK COMPATIBILITY IDEOGRAPH-F907;Lo;0;L;9F9C;;;;N;;;;; +F908;CJK COMPATIBILITY IDEOGRAPH-F908;Lo;0;L;9F9C;;;;N;;;;; +F909;CJK COMPATIBILITY IDEOGRAPH-F909;Lo;0;L;5951;;;;N;;;;; +F90A;CJK COMPATIBILITY IDEOGRAPH-F90A;Lo;0;L;91D1;;;;N;;;;; +F90B;CJK COMPATIBILITY IDEOGRAPH-F90B;Lo;0;L;5587;;;;N;;;;; +F90C;CJK COMPATIBILITY IDEOGRAPH-F90C;Lo;0;L;5948;;;;N;;;;; +F90D;CJK COMPATIBILITY IDEOGRAPH-F90D;Lo;0;L;61F6;;;;N;;;;; +F90E;CJK COMPATIBILITY IDEOGRAPH-F90E;Lo;0;L;7669;;;;N;;;;; +F90F;CJK COMPATIBILITY IDEOGRAPH-F90F;Lo;0;L;7F85;;;;N;;;;; +F910;CJK COMPATIBILITY IDEOGRAPH-F910;Lo;0;L;863F;;;;N;;;;; +F911;CJK COMPATIBILITY IDEOGRAPH-F911;Lo;0;L;87BA;;;;N;;;;; +F912;CJK COMPATIBILITY IDEOGRAPH-F912;Lo;0;L;88F8;;;;N;;;;; +F913;CJK COMPATIBILITY IDEOGRAPH-F913;Lo;0;L;908F;;;;N;;;;; +F914;CJK COMPATIBILITY IDEOGRAPH-F914;Lo;0;L;6A02;;;;N;;;;; +F915;CJK COMPATIBILITY IDEOGRAPH-F915;Lo;0;L;6D1B;;;;N;;;;; +F916;CJK COMPATIBILITY IDEOGRAPH-F916;Lo;0;L;70D9;;;;N;;;;; +F917;CJK COMPATIBILITY IDEOGRAPH-F917;Lo;0;L;73DE;;;;N;;;;; +F918;CJK COMPATIBILITY IDEOGRAPH-F918;Lo;0;L;843D;;;;N;;;;; +F919;CJK COMPATIBILITY IDEOGRAPH-F919;Lo;0;L;916A;;;;N;;;;; +F91A;CJK COMPATIBILITY IDEOGRAPH-F91A;Lo;0;L;99F1;;;;N;;;;; +F91B;CJK COMPATIBILITY IDEOGRAPH-F91B;Lo;0;L;4E82;;;;N;;;;; +F91C;CJK COMPATIBILITY IDEOGRAPH-F91C;Lo;0;L;5375;;;;N;;;;; +F91D;CJK COMPATIBILITY IDEOGRAPH-F91D;Lo;0;L;6B04;;;;N;;;;; +F91E;CJK COMPATIBILITY IDEOGRAPH-F91E;Lo;0;L;721B;;;;N;;;;; +F91F;CJK COMPATIBILITY IDEOGRAPH-F91F;Lo;0;L;862D;;;;N;;;;; +F920;CJK COMPATIBILITY IDEOGRAPH-F920;Lo;0;L;9E1E;;;;N;;;;; +F921;CJK COMPATIBILITY IDEOGRAPH-F921;Lo;0;L;5D50;;;;N;;;;; +F922;CJK COMPATIBILITY IDEOGRAPH-F922;Lo;0;L;6FEB;;;;N;;;;; +F923;CJK COMPATIBILITY IDEOGRAPH-F923;Lo;0;L;85CD;;;;N;;;;; +F924;CJK COMPATIBILITY IDEOGRAPH-F924;Lo;0;L;8964;;;;N;;;;; +F925;CJK COMPATIBILITY IDEOGRAPH-F925;Lo;0;L;62C9;;;;N;;;;; +F926;CJK COMPATIBILITY IDEOGRAPH-F926;Lo;0;L;81D8;;;;N;;;;; +F927;CJK COMPATIBILITY IDEOGRAPH-F927;Lo;0;L;881F;;;;N;;;;; +F928;CJK COMPATIBILITY IDEOGRAPH-F928;Lo;0;L;5ECA;;;;N;;;;; +F929;CJK COMPATIBILITY IDEOGRAPH-F929;Lo;0;L;6717;;;;N;;;;; +F92A;CJK COMPATIBILITY IDEOGRAPH-F92A;Lo;0;L;6D6A;;;;N;;;;; +F92B;CJK COMPATIBILITY IDEOGRAPH-F92B;Lo;0;L;72FC;;;;N;;;;; +F92C;CJK COMPATIBILITY IDEOGRAPH-F92C;Lo;0;L;90CE;;;;N;;;;; +F92D;CJK COMPATIBILITY IDEOGRAPH-F92D;Lo;0;L;4F86;;;;N;;;;; +F92E;CJK COMPATIBILITY IDEOGRAPH-F92E;Lo;0;L;51B7;;;;N;;;;; +F92F;CJK COMPATIBILITY IDEOGRAPH-F92F;Lo;0;L;52DE;;;;N;;;;; +F930;CJK COMPATIBILITY IDEOGRAPH-F930;Lo;0;L;64C4;;;;N;;;;; +F931;CJK COMPATIBILITY IDEOGRAPH-F931;Lo;0;L;6AD3;;;;N;;;;; +F932;CJK COMPATIBILITY IDEOGRAPH-F932;Lo;0;L;7210;;;;N;;;;; +F933;CJK COMPATIBILITY IDEOGRAPH-F933;Lo;0;L;76E7;;;;N;;;;; +F934;CJK COMPATIBILITY IDEOGRAPH-F934;Lo;0;L;8001;;;;N;;;;; +F935;CJK COMPATIBILITY IDEOGRAPH-F935;Lo;0;L;8606;;;;N;;;;; +F936;CJK COMPATIBILITY IDEOGRAPH-F936;Lo;0;L;865C;;;;N;;;;; +F937;CJK COMPATIBILITY IDEOGRAPH-F937;Lo;0;L;8DEF;;;;N;;;;; +F938;CJK COMPATIBILITY IDEOGRAPH-F938;Lo;0;L;9732;;;;N;;;;; +F939;CJK COMPATIBILITY IDEOGRAPH-F939;Lo;0;L;9B6F;;;;N;;;;; +F93A;CJK COMPATIBILITY IDEOGRAPH-F93A;Lo;0;L;9DFA;;;;N;;;;; +F93B;CJK COMPATIBILITY IDEOGRAPH-F93B;Lo;0;L;788C;;;;N;;;;; +F93C;CJK COMPATIBILITY IDEOGRAPH-F93C;Lo;0;L;797F;;;;N;;;;; +F93D;CJK COMPATIBILITY IDEOGRAPH-F93D;Lo;0;L;7DA0;;;;N;;;;; +F93E;CJK COMPATIBILITY IDEOGRAPH-F93E;Lo;0;L;83C9;;;;N;;;;; +F93F;CJK COMPATIBILITY IDEOGRAPH-F93F;Lo;0;L;9304;;;;N;;;;; +F940;CJK COMPATIBILITY IDEOGRAPH-F940;Lo;0;L;9E7F;;;;N;;;;; +F941;CJK COMPATIBILITY IDEOGRAPH-F941;Lo;0;L;8AD6;;;;N;;;;; +F942;CJK COMPATIBILITY IDEOGRAPH-F942;Lo;0;L;58DF;;;;N;;;;; +F943;CJK COMPATIBILITY IDEOGRAPH-F943;Lo;0;L;5F04;;;;N;;;;; +F944;CJK COMPATIBILITY IDEOGRAPH-F944;Lo;0;L;7C60;;;;N;;;;; +F945;CJK COMPATIBILITY IDEOGRAPH-F945;Lo;0;L;807E;;;;N;;;;; +F946;CJK COMPATIBILITY IDEOGRAPH-F946;Lo;0;L;7262;;;;N;;;;; +F947;CJK COMPATIBILITY IDEOGRAPH-F947;Lo;0;L;78CA;;;;N;;;;; +F948;CJK COMPATIBILITY IDEOGRAPH-F948;Lo;0;L;8CC2;;;;N;;;;; +F949;CJK COMPATIBILITY IDEOGRAPH-F949;Lo;0;L;96F7;;;;N;;;;; +F94A;CJK COMPATIBILITY IDEOGRAPH-F94A;Lo;0;L;58D8;;;;N;;;;; +F94B;CJK COMPATIBILITY IDEOGRAPH-F94B;Lo;0;L;5C62;;;;N;;;;; +F94C;CJK COMPATIBILITY IDEOGRAPH-F94C;Lo;0;L;6A13;;;;N;;;;; +F94D;CJK COMPATIBILITY IDEOGRAPH-F94D;Lo;0;L;6DDA;;;;N;;;;; +F94E;CJK COMPATIBILITY IDEOGRAPH-F94E;Lo;0;L;6F0F;;;;N;;;;; +F94F;CJK COMPATIBILITY IDEOGRAPH-F94F;Lo;0;L;7D2F;;;;N;;;;; +F950;CJK COMPATIBILITY IDEOGRAPH-F950;Lo;0;L;7E37;;;;N;;;;; +F951;CJK COMPATIBILITY IDEOGRAPH-F951;Lo;0;L;964B;;;;N;;;;; +F952;CJK COMPATIBILITY IDEOGRAPH-F952;Lo;0;L;52D2;;;;N;;;;; +F953;CJK COMPATIBILITY IDEOGRAPH-F953;Lo;0;L;808B;;;;N;;;;; +F954;CJK COMPATIBILITY IDEOGRAPH-F954;Lo;0;L;51DC;;;;N;;;;; +F955;CJK COMPATIBILITY IDEOGRAPH-F955;Lo;0;L;51CC;;;;N;;;;; +F956;CJK COMPATIBILITY IDEOGRAPH-F956;Lo;0;L;7A1C;;;;N;;;;; +F957;CJK COMPATIBILITY IDEOGRAPH-F957;Lo;0;L;7DBE;;;;N;;;;; +F958;CJK COMPATIBILITY IDEOGRAPH-F958;Lo;0;L;83F1;;;;N;;;;; +F959;CJK COMPATIBILITY IDEOGRAPH-F959;Lo;0;L;9675;;;;N;;;;; +F95A;CJK COMPATIBILITY IDEOGRAPH-F95A;Lo;0;L;8B80;;;;N;;;;; +F95B;CJK COMPATIBILITY IDEOGRAPH-F95B;Lo;0;L;62CF;;;;N;;;;; +F95C;CJK COMPATIBILITY IDEOGRAPH-F95C;Lo;0;L;6A02;;;;N;;;;; +F95D;CJK COMPATIBILITY IDEOGRAPH-F95D;Lo;0;L;8AFE;;;;N;;;;; +F95E;CJK COMPATIBILITY IDEOGRAPH-F95E;Lo;0;L;4E39;;;;N;;;;; +F95F;CJK COMPATIBILITY IDEOGRAPH-F95F;Lo;0;L;5BE7;;;;N;;;;; +F960;CJK COMPATIBILITY IDEOGRAPH-F960;Lo;0;L;6012;;;;N;;;;; +F961;CJK COMPATIBILITY IDEOGRAPH-F961;Lo;0;L;7387;;;;N;;;;; +F962;CJK COMPATIBILITY IDEOGRAPH-F962;Lo;0;L;7570;;;;N;;;;; +F963;CJK COMPATIBILITY IDEOGRAPH-F963;Lo;0;L;5317;;;;N;;;;; +F964;CJK COMPATIBILITY IDEOGRAPH-F964;Lo;0;L;78FB;;;;N;;;;; +F965;CJK COMPATIBILITY IDEOGRAPH-F965;Lo;0;L;4FBF;;;;N;;;;; +F966;CJK COMPATIBILITY IDEOGRAPH-F966;Lo;0;L;5FA9;;;;N;;;;; +F967;CJK COMPATIBILITY IDEOGRAPH-F967;Lo;0;L;4E0D;;;;N;;;;; +F968;CJK COMPATIBILITY IDEOGRAPH-F968;Lo;0;L;6CCC;;;;N;;;;; +F969;CJK COMPATIBILITY IDEOGRAPH-F969;Lo;0;L;6578;;;;N;;;;; +F96A;CJK COMPATIBILITY IDEOGRAPH-F96A;Lo;0;L;7D22;;;;N;;;;; +F96B;CJK COMPATIBILITY IDEOGRAPH-F96B;Lo;0;L;53C3;;;;N;;;;; +F96C;CJK COMPATIBILITY IDEOGRAPH-F96C;Lo;0;L;585E;;;;N;;;;; +F96D;CJK COMPATIBILITY IDEOGRAPH-F96D;Lo;0;L;7701;;;;N;;;;; +F96E;CJK COMPATIBILITY IDEOGRAPH-F96E;Lo;0;L;8449;;;;N;;;;; +F96F;CJK COMPATIBILITY IDEOGRAPH-F96F;Lo;0;L;8AAA;;;;N;;;;; +F970;CJK COMPATIBILITY IDEOGRAPH-F970;Lo;0;L;6BBA;;;;N;;;;; +F971;CJK COMPATIBILITY IDEOGRAPH-F971;Lo;0;L;8FB0;;;;N;;;;; +F972;CJK COMPATIBILITY IDEOGRAPH-F972;Lo;0;L;6C88;;;;N;;;;; +F973;CJK COMPATIBILITY IDEOGRAPH-F973;Lo;0;L;62FE;;;;N;;;;; +F974;CJK COMPATIBILITY IDEOGRAPH-F974;Lo;0;L;82E5;;;;N;;;;; +F975;CJK COMPATIBILITY IDEOGRAPH-F975;Lo;0;L;63A0;;;;N;;;;; +F976;CJK COMPATIBILITY IDEOGRAPH-F976;Lo;0;L;7565;;;;N;;;;; +F977;CJK COMPATIBILITY IDEOGRAPH-F977;Lo;0;L;4EAE;;;;N;;;;; +F978;CJK COMPATIBILITY IDEOGRAPH-F978;Lo;0;L;5169;;;;N;;;;; +F979;CJK COMPATIBILITY IDEOGRAPH-F979;Lo;0;L;51C9;;;;N;;;;; +F97A;CJK COMPATIBILITY IDEOGRAPH-F97A;Lo;0;L;6881;;;;N;;;;; +F97B;CJK COMPATIBILITY IDEOGRAPH-F97B;Lo;0;L;7CE7;;;;N;;;;; +F97C;CJK COMPATIBILITY IDEOGRAPH-F97C;Lo;0;L;826F;;;;N;;;;; +F97D;CJK COMPATIBILITY IDEOGRAPH-F97D;Lo;0;L;8AD2;;;;N;;;;; +F97E;CJK COMPATIBILITY IDEOGRAPH-F97E;Lo;0;L;91CF;;;;N;;;;; +F97F;CJK COMPATIBILITY IDEOGRAPH-F97F;Lo;0;L;52F5;;;;N;;;;; +F980;CJK COMPATIBILITY IDEOGRAPH-F980;Lo;0;L;5442;;;;N;;;;; +F981;CJK COMPATIBILITY IDEOGRAPH-F981;Lo;0;L;5973;;;;N;;;;; +F982;CJK COMPATIBILITY IDEOGRAPH-F982;Lo;0;L;5EEC;;;;N;;;;; +F983;CJK COMPATIBILITY IDEOGRAPH-F983;Lo;0;L;65C5;;;;N;;;;; +F984;CJK COMPATIBILITY IDEOGRAPH-F984;Lo;0;L;6FFE;;;;N;;;;; +F985;CJK COMPATIBILITY IDEOGRAPH-F985;Lo;0;L;792A;;;;N;;;;; +F986;CJK COMPATIBILITY IDEOGRAPH-F986;Lo;0;L;95AD;;;;N;;;;; +F987;CJK COMPATIBILITY IDEOGRAPH-F987;Lo;0;L;9A6A;;;;N;;;;; +F988;CJK COMPATIBILITY IDEOGRAPH-F988;Lo;0;L;9E97;;;;N;;;;; +F989;CJK COMPATIBILITY IDEOGRAPH-F989;Lo;0;L;9ECE;;;;N;;;;; +F98A;CJK COMPATIBILITY IDEOGRAPH-F98A;Lo;0;L;529B;;;;N;;;;; +F98B;CJK COMPATIBILITY IDEOGRAPH-F98B;Lo;0;L;66C6;;;;N;;;;; +F98C;CJK COMPATIBILITY IDEOGRAPH-F98C;Lo;0;L;6B77;;;;N;;;;; +F98D;CJK COMPATIBILITY IDEOGRAPH-F98D;Lo;0;L;8F62;;;;N;;;;; +F98E;CJK COMPATIBILITY IDEOGRAPH-F98E;Lo;0;L;5E74;;;;N;;;;; +F98F;CJK COMPATIBILITY IDEOGRAPH-F98F;Lo;0;L;6190;;;;N;;;;; +F990;CJK COMPATIBILITY IDEOGRAPH-F990;Lo;0;L;6200;;;;N;;;;; +F991;CJK COMPATIBILITY IDEOGRAPH-F991;Lo;0;L;649A;;;;N;;;;; +F992;CJK COMPATIBILITY IDEOGRAPH-F992;Lo;0;L;6F23;;;;N;;;;; +F993;CJK COMPATIBILITY IDEOGRAPH-F993;Lo;0;L;7149;;;;N;;;;; +F994;CJK COMPATIBILITY IDEOGRAPH-F994;Lo;0;L;7489;;;;N;;;;; +F995;CJK COMPATIBILITY IDEOGRAPH-F995;Lo;0;L;79CA;;;;N;;;;; +F996;CJK COMPATIBILITY IDEOGRAPH-F996;Lo;0;L;7DF4;;;;N;;;;; +F997;CJK COMPATIBILITY IDEOGRAPH-F997;Lo;0;L;806F;;;;N;;;;; +F998;CJK COMPATIBILITY IDEOGRAPH-F998;Lo;0;L;8F26;;;;N;;;;; +F999;CJK COMPATIBILITY IDEOGRAPH-F999;Lo;0;L;84EE;;;;N;;;;; +F99A;CJK COMPATIBILITY IDEOGRAPH-F99A;Lo;0;L;9023;;;;N;;;;; +F99B;CJK COMPATIBILITY IDEOGRAPH-F99B;Lo;0;L;934A;;;;N;;;;; +F99C;CJK COMPATIBILITY IDEOGRAPH-F99C;Lo;0;L;5217;;;;N;;;;; +F99D;CJK COMPATIBILITY IDEOGRAPH-F99D;Lo;0;L;52A3;;;;N;;;;; +F99E;CJK COMPATIBILITY IDEOGRAPH-F99E;Lo;0;L;54BD;;;;N;;;;; +F99F;CJK COMPATIBILITY IDEOGRAPH-F99F;Lo;0;L;70C8;;;;N;;;;; +F9A0;CJK COMPATIBILITY IDEOGRAPH-F9A0;Lo;0;L;88C2;;;;N;;;;; +F9A1;CJK COMPATIBILITY IDEOGRAPH-F9A1;Lo;0;L;8AAA;;;;N;;;;; +F9A2;CJK COMPATIBILITY IDEOGRAPH-F9A2;Lo;0;L;5EC9;;;;N;;;;; +F9A3;CJK COMPATIBILITY IDEOGRAPH-F9A3;Lo;0;L;5FF5;;;;N;;;;; +F9A4;CJK COMPATIBILITY IDEOGRAPH-F9A4;Lo;0;L;637B;;;;N;;;;; +F9A5;CJK COMPATIBILITY IDEOGRAPH-F9A5;Lo;0;L;6BAE;;;;N;;;;; +F9A6;CJK COMPATIBILITY IDEOGRAPH-F9A6;Lo;0;L;7C3E;;;;N;;;;; +F9A7;CJK COMPATIBILITY IDEOGRAPH-F9A7;Lo;0;L;7375;;;;N;;;;; +F9A8;CJK COMPATIBILITY IDEOGRAPH-F9A8;Lo;0;L;4EE4;;;;N;;;;; +F9A9;CJK COMPATIBILITY IDEOGRAPH-F9A9;Lo;0;L;56F9;;;;N;;;;; +F9AA;CJK COMPATIBILITY IDEOGRAPH-F9AA;Lo;0;L;5BE7;;;;N;;;;; +F9AB;CJK COMPATIBILITY IDEOGRAPH-F9AB;Lo;0;L;5DBA;;;;N;;;;; +F9AC;CJK COMPATIBILITY IDEOGRAPH-F9AC;Lo;0;L;601C;;;;N;;;;; +F9AD;CJK COMPATIBILITY IDEOGRAPH-F9AD;Lo;0;L;73B2;;;;N;;;;; +F9AE;CJK COMPATIBILITY IDEOGRAPH-F9AE;Lo;0;L;7469;;;;N;;;;; +F9AF;CJK COMPATIBILITY IDEOGRAPH-F9AF;Lo;0;L;7F9A;;;;N;;;;; +F9B0;CJK COMPATIBILITY IDEOGRAPH-F9B0;Lo;0;L;8046;;;;N;;;;; +F9B1;CJK COMPATIBILITY IDEOGRAPH-F9B1;Lo;0;L;9234;;;;N;;;;; +F9B2;CJK COMPATIBILITY IDEOGRAPH-F9B2;Lo;0;L;96F6;;;;N;;;;; +F9B3;CJK COMPATIBILITY IDEOGRAPH-F9B3;Lo;0;L;9748;;;;N;;;;; +F9B4;CJK COMPATIBILITY IDEOGRAPH-F9B4;Lo;0;L;9818;;;;N;;;;; +F9B5;CJK COMPATIBILITY IDEOGRAPH-F9B5;Lo;0;L;4F8B;;;;N;;;;; +F9B6;CJK COMPATIBILITY IDEOGRAPH-F9B6;Lo;0;L;79AE;;;;N;;;;; +F9B7;CJK COMPATIBILITY IDEOGRAPH-F9B7;Lo;0;L;91B4;;;;N;;;;; +F9B8;CJK COMPATIBILITY IDEOGRAPH-F9B8;Lo;0;L;96B8;;;;N;;;;; +F9B9;CJK COMPATIBILITY IDEOGRAPH-F9B9;Lo;0;L;60E1;;;;N;;;;; +F9BA;CJK COMPATIBILITY IDEOGRAPH-F9BA;Lo;0;L;4E86;;;;N;;;;; +F9BB;CJK COMPATIBILITY IDEOGRAPH-F9BB;Lo;0;L;50DA;;;;N;;;;; +F9BC;CJK COMPATIBILITY IDEOGRAPH-F9BC;Lo;0;L;5BEE;;;;N;;;;; +F9BD;CJK COMPATIBILITY IDEOGRAPH-F9BD;Lo;0;L;5C3F;;;;N;;;;; +F9BE;CJK COMPATIBILITY IDEOGRAPH-F9BE;Lo;0;L;6599;;;;N;;;;; +F9BF;CJK COMPATIBILITY IDEOGRAPH-F9BF;Lo;0;L;6A02;;;;N;;;;; +F9C0;CJK COMPATIBILITY IDEOGRAPH-F9C0;Lo;0;L;71CE;;;;N;;;;; +F9C1;CJK COMPATIBILITY IDEOGRAPH-F9C1;Lo;0;L;7642;;;;N;;;;; +F9C2;CJK COMPATIBILITY IDEOGRAPH-F9C2;Lo;0;L;84FC;;;;N;;;;; +F9C3;CJK COMPATIBILITY IDEOGRAPH-F9C3;Lo;0;L;907C;;;;N;;;;; +F9C4;CJK COMPATIBILITY IDEOGRAPH-F9C4;Lo;0;L;9F8D;;;;N;;;;; +F9C5;CJK COMPATIBILITY IDEOGRAPH-F9C5;Lo;0;L;6688;;;;N;;;;; +F9C6;CJK COMPATIBILITY IDEOGRAPH-F9C6;Lo;0;L;962E;;;;N;;;;; +F9C7;CJK COMPATIBILITY IDEOGRAPH-F9C7;Lo;0;L;5289;;;;N;;;;; +F9C8;CJK COMPATIBILITY IDEOGRAPH-F9C8;Lo;0;L;677B;;;;N;;;;; +F9C9;CJK COMPATIBILITY IDEOGRAPH-F9C9;Lo;0;L;67F3;;;;N;;;;; +F9CA;CJK COMPATIBILITY IDEOGRAPH-F9CA;Lo;0;L;6D41;;;;N;;;;; +F9CB;CJK COMPATIBILITY IDEOGRAPH-F9CB;Lo;0;L;6E9C;;;;N;;;;; +F9CC;CJK COMPATIBILITY IDEOGRAPH-F9CC;Lo;0;L;7409;;;;N;;;;; +F9CD;CJK COMPATIBILITY IDEOGRAPH-F9CD;Lo;0;L;7559;;;;N;;;;; +F9CE;CJK COMPATIBILITY IDEOGRAPH-F9CE;Lo;0;L;786B;;;;N;;;;; +F9CF;CJK COMPATIBILITY IDEOGRAPH-F9CF;Lo;0;L;7D10;;;;N;;;;; +F9D0;CJK COMPATIBILITY IDEOGRAPH-F9D0;Lo;0;L;985E;;;;N;;;;; +F9D1;CJK COMPATIBILITY IDEOGRAPH-F9D1;Lo;0;L;516D;;;;N;;;;; +F9D2;CJK COMPATIBILITY IDEOGRAPH-F9D2;Lo;0;L;622E;;;;N;;;;; +F9D3;CJK COMPATIBILITY IDEOGRAPH-F9D3;Lo;0;L;9678;;;;N;;;;; +F9D4;CJK COMPATIBILITY IDEOGRAPH-F9D4;Lo;0;L;502B;;;;N;;;;; +F9D5;CJK COMPATIBILITY IDEOGRAPH-F9D5;Lo;0;L;5D19;;;;N;;;;; +F9D6;CJK COMPATIBILITY IDEOGRAPH-F9D6;Lo;0;L;6DEA;;;;N;;;;; +F9D7;CJK COMPATIBILITY IDEOGRAPH-F9D7;Lo;0;L;8F2A;;;;N;;;;; +F9D8;CJK COMPATIBILITY IDEOGRAPH-F9D8;Lo;0;L;5F8B;;;;N;;;;; +F9D9;CJK COMPATIBILITY IDEOGRAPH-F9D9;Lo;0;L;6144;;;;N;;;;; +F9DA;CJK COMPATIBILITY IDEOGRAPH-F9DA;Lo;0;L;6817;;;;N;;;;; +F9DB;CJK COMPATIBILITY IDEOGRAPH-F9DB;Lo;0;L;7387;;;;N;;;;; +F9DC;CJK COMPATIBILITY IDEOGRAPH-F9DC;Lo;0;L;9686;;;;N;;;;; +F9DD;CJK COMPATIBILITY IDEOGRAPH-F9DD;Lo;0;L;5229;;;;N;;;;; +F9DE;CJK COMPATIBILITY IDEOGRAPH-F9DE;Lo;0;L;540F;;;;N;;;;; +F9DF;CJK COMPATIBILITY IDEOGRAPH-F9DF;Lo;0;L;5C65;;;;N;;;;; +F9E0;CJK COMPATIBILITY IDEOGRAPH-F9E0;Lo;0;L;6613;;;;N;;;;; +F9E1;CJK COMPATIBILITY IDEOGRAPH-F9E1;Lo;0;L;674E;;;;N;;;;; +F9E2;CJK COMPATIBILITY IDEOGRAPH-F9E2;Lo;0;L;68A8;;;;N;;;;; +F9E3;CJK COMPATIBILITY IDEOGRAPH-F9E3;Lo;0;L;6CE5;;;;N;;;;; +F9E4;CJK COMPATIBILITY IDEOGRAPH-F9E4;Lo;0;L;7406;;;;N;;;;; +F9E5;CJK COMPATIBILITY IDEOGRAPH-F9E5;Lo;0;L;75E2;;;;N;;;;; +F9E6;CJK COMPATIBILITY IDEOGRAPH-F9E6;Lo;0;L;7F79;;;;N;;;;; +F9E7;CJK COMPATIBILITY IDEOGRAPH-F9E7;Lo;0;L;88CF;;;;N;;;;; +F9E8;CJK COMPATIBILITY IDEOGRAPH-F9E8;Lo;0;L;88E1;;;;N;;;;; +F9E9;CJK COMPATIBILITY IDEOGRAPH-F9E9;Lo;0;L;91CC;;;;N;;;;; +F9EA;CJK COMPATIBILITY IDEOGRAPH-F9EA;Lo;0;L;96E2;;;;N;;;;; +F9EB;CJK COMPATIBILITY IDEOGRAPH-F9EB;Lo;0;L;533F;;;;N;;;;; +F9EC;CJK COMPATIBILITY IDEOGRAPH-F9EC;Lo;0;L;6EBA;;;;N;;;;; +F9ED;CJK COMPATIBILITY IDEOGRAPH-F9ED;Lo;0;L;541D;;;;N;;;;; +F9EE;CJK COMPATIBILITY IDEOGRAPH-F9EE;Lo;0;L;71D0;;;;N;;;;; +F9EF;CJK COMPATIBILITY IDEOGRAPH-F9EF;Lo;0;L;7498;;;;N;;;;; +F9F0;CJK COMPATIBILITY IDEOGRAPH-F9F0;Lo;0;L;85FA;;;;N;;;;; +F9F1;CJK COMPATIBILITY IDEOGRAPH-F9F1;Lo;0;L;96A3;;;;N;;;;; +F9F2;CJK COMPATIBILITY IDEOGRAPH-F9F2;Lo;0;L;9C57;;;;N;;;;; +F9F3;CJK COMPATIBILITY IDEOGRAPH-F9F3;Lo;0;L;9E9F;;;;N;;;;; +F9F4;CJK COMPATIBILITY IDEOGRAPH-F9F4;Lo;0;L;6797;;;;N;;;;; +F9F5;CJK COMPATIBILITY IDEOGRAPH-F9F5;Lo;0;L;6DCB;;;;N;;;;; +F9F6;CJK COMPATIBILITY IDEOGRAPH-F9F6;Lo;0;L;81E8;;;;N;;;;; +F9F7;CJK COMPATIBILITY IDEOGRAPH-F9F7;Lo;0;L;7ACB;;;;N;;;;; +F9F8;CJK COMPATIBILITY IDEOGRAPH-F9F8;Lo;0;L;7B20;;;;N;;;;; +F9F9;CJK COMPATIBILITY IDEOGRAPH-F9F9;Lo;0;L;7C92;;;;N;;;;; +F9FA;CJK COMPATIBILITY IDEOGRAPH-F9FA;Lo;0;L;72C0;;;;N;;;;; +F9FB;CJK COMPATIBILITY IDEOGRAPH-F9FB;Lo;0;L;7099;;;;N;;;;; +F9FC;CJK COMPATIBILITY IDEOGRAPH-F9FC;Lo;0;L;8B58;;;;N;;;;; +F9FD;CJK COMPATIBILITY IDEOGRAPH-F9FD;Lo;0;L;4EC0;;;;N;;;;; +F9FE;CJK COMPATIBILITY IDEOGRAPH-F9FE;Lo;0;L;8336;;;;N;;;;; +F9FF;CJK COMPATIBILITY IDEOGRAPH-F9FF;Lo;0;L;523A;;;;N;;;;; +FA00;CJK COMPATIBILITY IDEOGRAPH-FA00;Lo;0;L;5207;;;;N;;;;; +FA01;CJK COMPATIBILITY IDEOGRAPH-FA01;Lo;0;L;5EA6;;;;N;;;;; +FA02;CJK COMPATIBILITY IDEOGRAPH-FA02;Lo;0;L;62D3;;;;N;;;;; +FA03;CJK COMPATIBILITY IDEOGRAPH-FA03;Lo;0;L;7CD6;;;;N;;;;; +FA04;CJK COMPATIBILITY IDEOGRAPH-FA04;Lo;0;L;5B85;;;;N;;;;; +FA05;CJK COMPATIBILITY IDEOGRAPH-FA05;Lo;0;L;6D1E;;;;N;;;;; +FA06;CJK COMPATIBILITY IDEOGRAPH-FA06;Lo;0;L;66B4;;;;N;;;;; +FA07;CJK COMPATIBILITY IDEOGRAPH-FA07;Lo;0;L;8F3B;;;;N;;;;; +FA08;CJK COMPATIBILITY IDEOGRAPH-FA08;Lo;0;L;884C;;;;N;;;;; +FA09;CJK COMPATIBILITY IDEOGRAPH-FA09;Lo;0;L;964D;;;;N;;;;; +FA0A;CJK COMPATIBILITY IDEOGRAPH-FA0A;Lo;0;L;898B;;;;N;;;;; +FA0B;CJK COMPATIBILITY IDEOGRAPH-FA0B;Lo;0;L;5ED3;;;;N;;;;; +FA0C;CJK COMPATIBILITY IDEOGRAPH-FA0C;Lo;0;L;5140;;;;N;;;;; +FA0D;CJK COMPATIBILITY IDEOGRAPH-FA0D;Lo;0;L;55C0;;;;N;;;;; +FA0E;CJK COMPATIBILITY IDEOGRAPH-FA0E;Lo;0;L;;;;;N;;;;; +FA0F;CJK COMPATIBILITY IDEOGRAPH-FA0F;Lo;0;L;;;;;N;;;;; +FA10;CJK COMPATIBILITY IDEOGRAPH-FA10;Lo;0;L;585A;;;;N;;;;; +FA11;CJK COMPATIBILITY IDEOGRAPH-FA11;Lo;0;L;;;;;N;;;;; +FA12;CJK COMPATIBILITY IDEOGRAPH-FA12;Lo;0;L;6674;;;;N;;;;; +FA13;CJK COMPATIBILITY IDEOGRAPH-FA13;Lo;0;L;;;;;N;;;;; +FA14;CJK COMPATIBILITY IDEOGRAPH-FA14;Lo;0;L;;;;;N;;;;; +FA15;CJK COMPATIBILITY IDEOGRAPH-FA15;Lo;0;L;51DE;;;;N;;;;; +FA16;CJK COMPATIBILITY IDEOGRAPH-FA16;Lo;0;L;732A;;;;N;;;;; +FA17;CJK COMPATIBILITY IDEOGRAPH-FA17;Lo;0;L;76CA;;;;N;;;;; +FA18;CJK COMPATIBILITY IDEOGRAPH-FA18;Lo;0;L;793C;;;;N;;;;; +FA19;CJK COMPATIBILITY IDEOGRAPH-FA19;Lo;0;L;795E;;;;N;;;;; +FA1A;CJK COMPATIBILITY IDEOGRAPH-FA1A;Lo;0;L;7965;;;;N;;;;; +FA1B;CJK COMPATIBILITY IDEOGRAPH-FA1B;Lo;0;L;798F;;;;N;;;;; +FA1C;CJK COMPATIBILITY IDEOGRAPH-FA1C;Lo;0;L;9756;;;;N;;;;; +FA1D;CJK COMPATIBILITY IDEOGRAPH-FA1D;Lo;0;L;7CBE;;;;N;;;;; +FA1E;CJK COMPATIBILITY IDEOGRAPH-FA1E;Lo;0;L;7FBD;;;;N;;;;; +FA1F;CJK COMPATIBILITY IDEOGRAPH-FA1F;Lo;0;L;;;;;N;;*;;; +FA20;CJK COMPATIBILITY IDEOGRAPH-FA20;Lo;0;L;8612;;;;N;;;;; +FA21;CJK COMPATIBILITY IDEOGRAPH-FA21;Lo;0;L;;;;;N;;;;; +FA22;CJK COMPATIBILITY IDEOGRAPH-FA22;Lo;0;L;8AF8;;;;N;;;;; +FA23;CJK COMPATIBILITY IDEOGRAPH-FA23;Lo;0;L;;;;;N;;*;;; +FA24;CJK COMPATIBILITY IDEOGRAPH-FA24;Lo;0;L;;;;;N;;;;; +FA25;CJK COMPATIBILITY IDEOGRAPH-FA25;Lo;0;L;9038;;;;N;;;;; +FA26;CJK COMPATIBILITY IDEOGRAPH-FA26;Lo;0;L;90FD;;;;N;;;;; +FA27;CJK COMPATIBILITY IDEOGRAPH-FA27;Lo;0;L;;;;;N;;;;; +FA28;CJK COMPATIBILITY IDEOGRAPH-FA28;Lo;0;L;;;;;N;;;;; +FA29;CJK COMPATIBILITY IDEOGRAPH-FA29;Lo;0;L;;;;;N;;;;; +FA2A;CJK COMPATIBILITY IDEOGRAPH-FA2A;Lo;0;L;98EF;;;;N;;;;; +FA2B;CJK COMPATIBILITY IDEOGRAPH-FA2B;Lo;0;L;98FC;;;;N;;;;; +FA2C;CJK COMPATIBILITY IDEOGRAPH-FA2C;Lo;0;L;9928;;;;N;;;;; +FA2D;CJK COMPATIBILITY IDEOGRAPH-FA2D;Lo;0;L;9DB4;;;;N;;;;; +FA30;CJK COMPATIBILITY IDEOGRAPH-FA30;Lo;0;L;4FAE;;;;N;;;;; +FA31;CJK COMPATIBILITY IDEOGRAPH-FA31;Lo;0;L;50E7;;;;N;;;;; +FA32;CJK COMPATIBILITY IDEOGRAPH-FA32;Lo;0;L;514D;;;;N;;;;; +FA33;CJK COMPATIBILITY IDEOGRAPH-FA33;Lo;0;L;52C9;;;;N;;;;; +FA34;CJK COMPATIBILITY IDEOGRAPH-FA34;Lo;0;L;52E4;;;;N;;;;; +FA35;CJK COMPATIBILITY IDEOGRAPH-FA35;Lo;0;L;5351;;;;N;;;;; +FA36;CJK COMPATIBILITY IDEOGRAPH-FA36;Lo;0;L;559D;;;;N;;;;; +FA37;CJK COMPATIBILITY IDEOGRAPH-FA37;Lo;0;L;5606;;;;N;;;;; +FA38;CJK COMPATIBILITY IDEOGRAPH-FA38;Lo;0;L;5668;;;;N;;;;; +FA39;CJK COMPATIBILITY IDEOGRAPH-FA39;Lo;0;L;5840;;;;N;;;;; +FA3A;CJK COMPATIBILITY IDEOGRAPH-FA3A;Lo;0;L;58A8;;;;N;;;;; +FA3B;CJK COMPATIBILITY IDEOGRAPH-FA3B;Lo;0;L;5C64;;;;N;;;;; +FA3C;CJK COMPATIBILITY IDEOGRAPH-FA3C;Lo;0;L;5C6E;;;;N;;;;; +FA3D;CJK COMPATIBILITY IDEOGRAPH-FA3D;Lo;0;L;6094;;;;N;;;;; +FA3E;CJK COMPATIBILITY IDEOGRAPH-FA3E;Lo;0;L;6168;;;;N;;;;; +FA3F;CJK COMPATIBILITY IDEOGRAPH-FA3F;Lo;0;L;618E;;;;N;;;;; +FA40;CJK COMPATIBILITY IDEOGRAPH-FA40;Lo;0;L;61F2;;;;N;;;;; +FA41;CJK COMPATIBILITY IDEOGRAPH-FA41;Lo;0;L;654F;;;;N;;;;; +FA42;CJK COMPATIBILITY IDEOGRAPH-FA42;Lo;0;L;65E2;;;;N;;;;; +FA43;CJK COMPATIBILITY IDEOGRAPH-FA43;Lo;0;L;6691;;;;N;;;;; +FA44;CJK COMPATIBILITY IDEOGRAPH-FA44;Lo;0;L;6885;;;;N;;;;; +FA45;CJK COMPATIBILITY IDEOGRAPH-FA45;Lo;0;L;6D77;;;;N;;;;; +FA46;CJK COMPATIBILITY IDEOGRAPH-FA46;Lo;0;L;6E1A;;;;N;;;;; +FA47;CJK COMPATIBILITY IDEOGRAPH-FA47;Lo;0;L;6F22;;;;N;;;;; +FA48;CJK COMPATIBILITY IDEOGRAPH-FA48;Lo;0;L;716E;;;;N;;;;; +FA49;CJK COMPATIBILITY IDEOGRAPH-FA49;Lo;0;L;722B;;;;N;;;;; +FA4A;CJK COMPATIBILITY IDEOGRAPH-FA4A;Lo;0;L;7422;;;;N;;;;; +FA4B;CJK COMPATIBILITY IDEOGRAPH-FA4B;Lo;0;L;7891;;;;N;;;;; +FA4C;CJK COMPATIBILITY IDEOGRAPH-FA4C;Lo;0;L;793E;;;;N;;;;; +FA4D;CJK COMPATIBILITY IDEOGRAPH-FA4D;Lo;0;L;7949;;;;N;;;;; +FA4E;CJK COMPATIBILITY IDEOGRAPH-FA4E;Lo;0;L;7948;;;;N;;;;; +FA4F;CJK COMPATIBILITY IDEOGRAPH-FA4F;Lo;0;L;7950;;;;N;;;;; +FA50;CJK COMPATIBILITY IDEOGRAPH-FA50;Lo;0;L;7956;;;;N;;;;; +FA51;CJK COMPATIBILITY IDEOGRAPH-FA51;Lo;0;L;795D;;;;N;;;;; +FA52;CJK COMPATIBILITY IDEOGRAPH-FA52;Lo;0;L;798D;;;;N;;;;; +FA53;CJK COMPATIBILITY IDEOGRAPH-FA53;Lo;0;L;798E;;;;N;;;;; +FA54;CJK COMPATIBILITY IDEOGRAPH-FA54;Lo;0;L;7A40;;;;N;;;;; +FA55;CJK COMPATIBILITY IDEOGRAPH-FA55;Lo;0;L;7A81;;;;N;;;;; +FA56;CJK COMPATIBILITY IDEOGRAPH-FA56;Lo;0;L;7BC0;;;;N;;;;; +FA57;CJK COMPATIBILITY IDEOGRAPH-FA57;Lo;0;L;7DF4;;;;N;;;;; +FA58;CJK COMPATIBILITY IDEOGRAPH-FA58;Lo;0;L;7E09;;;;N;;;;; +FA59;CJK COMPATIBILITY IDEOGRAPH-FA59;Lo;0;L;7E41;;;;N;;;;; +FA5A;CJK COMPATIBILITY IDEOGRAPH-FA5A;Lo;0;L;7F72;;;;N;;;;; +FA5B;CJK COMPATIBILITY IDEOGRAPH-FA5B;Lo;0;L;8005;;;;N;;;;; +FA5C;CJK COMPATIBILITY IDEOGRAPH-FA5C;Lo;0;L;81ED;;;;N;;;;; +FA5D;CJK COMPATIBILITY IDEOGRAPH-FA5D;Lo;0;L;8279;;;;N;;;;; +FA5E;CJK COMPATIBILITY IDEOGRAPH-FA5E;Lo;0;L;8279;;;;N;;;;; +FA5F;CJK COMPATIBILITY IDEOGRAPH-FA5F;Lo;0;L;8457;;;;N;;;;; +FA60;CJK COMPATIBILITY IDEOGRAPH-FA60;Lo;0;L;8910;;;;N;;;;; +FA61;CJK COMPATIBILITY IDEOGRAPH-FA61;Lo;0;L;8996;;;;N;;;;; +FA62;CJK COMPATIBILITY IDEOGRAPH-FA62;Lo;0;L;8B01;;;;N;;;;; +FA63;CJK COMPATIBILITY IDEOGRAPH-FA63;Lo;0;L;8B39;;;;N;;;;; +FA64;CJK COMPATIBILITY IDEOGRAPH-FA64;Lo;0;L;8CD3;;;;N;;;;; +FA65;CJK COMPATIBILITY IDEOGRAPH-FA65;Lo;0;L;8D08;;;;N;;;;; +FA66;CJK COMPATIBILITY IDEOGRAPH-FA66;Lo;0;L;8FB6;;;;N;;;;; +FA67;CJK COMPATIBILITY IDEOGRAPH-FA67;Lo;0;L;9038;;;;N;;;;; +FA68;CJK COMPATIBILITY IDEOGRAPH-FA68;Lo;0;L;96E3;;;;N;;;;; +FA69;CJK COMPATIBILITY IDEOGRAPH-FA69;Lo;0;L;97FF;;;;N;;;;; +FA6A;CJK COMPATIBILITY IDEOGRAPH-FA6A;Lo;0;L;983B;;;;N;;;;; +FB00;LATIN SMALL LIGATURE FF;Ll;0;L;<compat> 0066 0066;;;;N;;;;; +FB01;LATIN SMALL LIGATURE FI;Ll;0;L;<compat> 0066 0069;;;;N;;;;; +FB02;LATIN SMALL LIGATURE FL;Ll;0;L;<compat> 0066 006C;;;;N;;;;; +FB03;LATIN SMALL LIGATURE FFI;Ll;0;L;<compat> 0066 0066 0069;;;;N;;;;; +FB04;LATIN SMALL LIGATURE FFL;Ll;0;L;<compat> 0066 0066 006C;;;;N;;;;; +FB05;LATIN SMALL LIGATURE LONG S T;Ll;0;L;<compat> 017F 0074;;;;N;;;;; +FB06;LATIN SMALL LIGATURE ST;Ll;0;L;<compat> 0073 0074;;;;N;;;;; +FB13;ARMENIAN SMALL LIGATURE MEN NOW;Ll;0;L;<compat> 0574 0576;;;;N;;;;; +FB14;ARMENIAN SMALL LIGATURE MEN ECH;Ll;0;L;<compat> 0574 0565;;;;N;;;;; +FB15;ARMENIAN SMALL LIGATURE MEN INI;Ll;0;L;<compat> 0574 056B;;;;N;;;;; +FB16;ARMENIAN SMALL LIGATURE VEW NOW;Ll;0;L;<compat> 057E 0576;;;;N;;;;; +FB17;ARMENIAN SMALL LIGATURE MEN XEH;Ll;0;L;<compat> 0574 056D;;;;N;;;;; +FB1D;HEBREW LETTER YOD WITH HIRIQ;Lo;0;R;05D9 05B4;;;;N;;;;; +FB1E;HEBREW POINT JUDEO-SPANISH VARIKA;Mn;26;NSM;;;;;N;HEBREW POINT VARIKA;;;; +FB1F;HEBREW LIGATURE YIDDISH YOD YOD PATAH;Lo;0;R;05F2 05B7;;;;N;;;;; +FB20;HEBREW LETTER ALTERNATIVE AYIN;Lo;0;R;<font> 05E2;;;;N;;;;; +FB21;HEBREW LETTER WIDE ALEF;Lo;0;R;<font> 05D0;;;;N;;;;; +FB22;HEBREW LETTER WIDE DALET;Lo;0;R;<font> 05D3;;;;N;;;;; +FB23;HEBREW LETTER WIDE HE;Lo;0;R;<font> 05D4;;;;N;;;;; +FB24;HEBREW LETTER WIDE KAF;Lo;0;R;<font> 05DB;;;;N;;;;; +FB25;HEBREW LETTER WIDE LAMED;Lo;0;R;<font> 05DC;;;;N;;;;; +FB26;HEBREW LETTER WIDE FINAL MEM;Lo;0;R;<font> 05DD;;;;N;;;;; +FB27;HEBREW LETTER WIDE RESH;Lo;0;R;<font> 05E8;;;;N;;;;; +FB28;HEBREW LETTER WIDE TAV;Lo;0;R;<font> 05EA;;;;N;;;;; +FB29;HEBREW LETTER ALTERNATIVE PLUS SIGN;Sm;0;ET;<font> 002B;;;;N;;;;; +FB2A;HEBREW LETTER SHIN WITH SHIN DOT;Lo;0;R;05E9 05C1;;;;N;;;;; +FB2B;HEBREW LETTER SHIN WITH SIN DOT;Lo;0;R;05E9 05C2;;;;N;;;;; +FB2C;HEBREW LETTER SHIN WITH DAGESH AND SHIN DOT;Lo;0;R;FB49 05C1;;;;N;;;;; +FB2D;HEBREW LETTER SHIN WITH DAGESH AND SIN DOT;Lo;0;R;FB49 05C2;;;;N;;;;; +FB2E;HEBREW LETTER ALEF WITH PATAH;Lo;0;R;05D0 05B7;;;;N;;;;; +FB2F;HEBREW LETTER ALEF WITH QAMATS;Lo;0;R;05D0 05B8;;;;N;;;;; +FB30;HEBREW LETTER ALEF WITH MAPIQ;Lo;0;R;05D0 05BC;;;;N;;;;; +FB31;HEBREW LETTER BET WITH DAGESH;Lo;0;R;05D1 05BC;;;;N;;;;; +FB32;HEBREW LETTER GIMEL WITH DAGESH;Lo;0;R;05D2 05BC;;;;N;;;;; +FB33;HEBREW LETTER DALET WITH DAGESH;Lo;0;R;05D3 05BC;;;;N;;;;; +FB34;HEBREW LETTER HE WITH MAPIQ;Lo;0;R;05D4 05BC;;;;N;;;;; +FB35;HEBREW LETTER VAV WITH DAGESH;Lo;0;R;05D5 05BC;;;;N;;;;; +FB36;HEBREW LETTER ZAYIN WITH DAGESH;Lo;0;R;05D6 05BC;;;;N;;;;; +FB38;HEBREW LETTER TET WITH DAGESH;Lo;0;R;05D8 05BC;;;;N;;;;; +FB39;HEBREW LETTER YOD WITH DAGESH;Lo;0;R;05D9 05BC;;;;N;;;;; +FB3A;HEBREW LETTER FINAL KAF WITH DAGESH;Lo;0;R;05DA 05BC;;;;N;;;;; +FB3B;HEBREW LETTER KAF WITH DAGESH;Lo;0;R;05DB 05BC;;;;N;;;;; +FB3C;HEBREW LETTER LAMED WITH DAGESH;Lo;0;R;05DC 05BC;;;;N;;;;; +FB3E;HEBREW LETTER MEM WITH DAGESH;Lo;0;R;05DE 05BC;;;;N;;;;; +FB40;HEBREW LETTER NUN WITH DAGESH;Lo;0;R;05E0 05BC;;;;N;;;;; +FB41;HEBREW LETTER SAMEKH WITH DAGESH;Lo;0;R;05E1 05BC;;;;N;;;;; +FB43;HEBREW LETTER FINAL PE WITH DAGESH;Lo;0;R;05E3 05BC;;;;N;;;;; +FB44;HEBREW LETTER PE WITH DAGESH;Lo;0;R;05E4 05BC;;;;N;;;;; +FB46;HEBREW LETTER TSADI WITH DAGESH;Lo;0;R;05E6 05BC;;;;N;;;;; +FB47;HEBREW LETTER QOF WITH DAGESH;Lo;0;R;05E7 05BC;;;;N;;;;; +FB48;HEBREW LETTER RESH WITH DAGESH;Lo;0;R;05E8 05BC;;;;N;;;;; +FB49;HEBREW LETTER SHIN WITH DAGESH;Lo;0;R;05E9 05BC;;;;N;;;;; +FB4A;HEBREW LETTER TAV WITH DAGESH;Lo;0;R;05EA 05BC;;;;N;;;;; +FB4B;HEBREW LETTER VAV WITH HOLAM;Lo;0;R;05D5 05B9;;;;N;;;;; +FB4C;HEBREW LETTER BET WITH RAFE;Lo;0;R;05D1 05BF;;;;N;;;;; +FB4D;HEBREW LETTER KAF WITH RAFE;Lo;0;R;05DB 05BF;;;;N;;;;; +FB4E;HEBREW LETTER PE WITH RAFE;Lo;0;R;05E4 05BF;;;;N;;;;; +FB4F;HEBREW LIGATURE ALEF LAMED;Lo;0;R;<compat> 05D0 05DC;;;;N;;;;; +FB50;ARABIC LETTER ALEF WASLA ISOLATED FORM;Lo;0;AL;<isolated> 0671;;;;N;;;;; +FB51;ARABIC LETTER ALEF WASLA FINAL FORM;Lo;0;AL;<final> 0671;;;;N;;;;; +FB52;ARABIC LETTER BEEH ISOLATED FORM;Lo;0;AL;<isolated> 067B;;;;N;;;;; +FB53;ARABIC LETTER BEEH FINAL FORM;Lo;0;AL;<final> 067B;;;;N;;;;; +FB54;ARABIC LETTER BEEH INITIAL FORM;Lo;0;AL;<initial> 067B;;;;N;;;;; +FB55;ARABIC LETTER BEEH MEDIAL FORM;Lo;0;AL;<medial> 067B;;;;N;;;;; +FB56;ARABIC LETTER PEH ISOLATED FORM;Lo;0;AL;<isolated> 067E;;;;N;;;;; +FB57;ARABIC LETTER PEH FINAL FORM;Lo;0;AL;<final> 067E;;;;N;;;;; +FB58;ARABIC LETTER PEH INITIAL FORM;Lo;0;AL;<initial> 067E;;;;N;;;;; +FB59;ARABIC LETTER PEH MEDIAL FORM;Lo;0;AL;<medial> 067E;;;;N;;;;; +FB5A;ARABIC LETTER BEHEH ISOLATED FORM;Lo;0;AL;<isolated> 0680;;;;N;;;;; +FB5B;ARABIC LETTER BEHEH FINAL FORM;Lo;0;AL;<final> 0680;;;;N;;;;; +FB5C;ARABIC LETTER BEHEH INITIAL FORM;Lo;0;AL;<initial> 0680;;;;N;;;;; +FB5D;ARABIC LETTER BEHEH MEDIAL FORM;Lo;0;AL;<medial> 0680;;;;N;;;;; +FB5E;ARABIC LETTER TTEHEH ISOLATED FORM;Lo;0;AL;<isolated> 067A;;;;N;;;;; +FB5F;ARABIC LETTER TTEHEH FINAL FORM;Lo;0;AL;<final> 067A;;;;N;;;;; +FB60;ARABIC LETTER TTEHEH INITIAL FORM;Lo;0;AL;<initial> 067A;;;;N;;;;; +FB61;ARABIC LETTER TTEHEH MEDIAL FORM;Lo;0;AL;<medial> 067A;;;;N;;;;; +FB62;ARABIC LETTER TEHEH ISOLATED FORM;Lo;0;AL;<isolated> 067F;;;;N;;;;; +FB63;ARABIC LETTER TEHEH FINAL FORM;Lo;0;AL;<final> 067F;;;;N;;;;; +FB64;ARABIC LETTER TEHEH INITIAL FORM;Lo;0;AL;<initial> 067F;;;;N;;;;; +FB65;ARABIC LETTER TEHEH MEDIAL FORM;Lo;0;AL;<medial> 067F;;;;N;;;;; +FB66;ARABIC LETTER TTEH ISOLATED FORM;Lo;0;AL;<isolated> 0679;;;;N;;;;; +FB67;ARABIC LETTER TTEH FINAL FORM;Lo;0;AL;<final> 0679;;;;N;;;;; +FB68;ARABIC LETTER TTEH INITIAL FORM;Lo;0;AL;<initial> 0679;;;;N;;;;; +FB69;ARABIC LETTER TTEH MEDIAL FORM;Lo;0;AL;<medial> 0679;;;;N;;;;; +FB6A;ARABIC LETTER VEH ISOLATED FORM;Lo;0;AL;<isolated> 06A4;;;;N;;;;; +FB6B;ARABIC LETTER VEH FINAL FORM;Lo;0;AL;<final> 06A4;;;;N;;;;; +FB6C;ARABIC LETTER VEH INITIAL FORM;Lo;0;AL;<initial> 06A4;;;;N;;;;; +FB6D;ARABIC LETTER VEH MEDIAL FORM;Lo;0;AL;<medial> 06A4;;;;N;;;;; +FB6E;ARABIC LETTER PEHEH ISOLATED FORM;Lo;0;AL;<isolated> 06A6;;;;N;;;;; +FB6F;ARABIC LETTER PEHEH FINAL FORM;Lo;0;AL;<final> 06A6;;;;N;;;;; +FB70;ARABIC LETTER PEHEH INITIAL FORM;Lo;0;AL;<initial> 06A6;;;;N;;;;; +FB71;ARABIC LETTER PEHEH MEDIAL FORM;Lo;0;AL;<medial> 06A6;;;;N;;;;; +FB72;ARABIC LETTER DYEH ISOLATED FORM;Lo;0;AL;<isolated> 0684;;;;N;;;;; +FB73;ARABIC LETTER DYEH FINAL FORM;Lo;0;AL;<final> 0684;;;;N;;;;; +FB74;ARABIC LETTER DYEH INITIAL FORM;Lo;0;AL;<initial> 0684;;;;N;;;;; +FB75;ARABIC LETTER DYEH MEDIAL FORM;Lo;0;AL;<medial> 0684;;;;N;;;;; +FB76;ARABIC LETTER NYEH ISOLATED FORM;Lo;0;AL;<isolated> 0683;;;;N;;;;; +FB77;ARABIC LETTER NYEH FINAL FORM;Lo;0;AL;<final> 0683;;;;N;;;;; +FB78;ARABIC LETTER NYEH INITIAL FORM;Lo;0;AL;<initial> 0683;;;;N;;;;; +FB79;ARABIC LETTER NYEH MEDIAL FORM;Lo;0;AL;<medial> 0683;;;;N;;;;; +FB7A;ARABIC LETTER TCHEH ISOLATED FORM;Lo;0;AL;<isolated> 0686;;;;N;;;;; +FB7B;ARABIC LETTER TCHEH FINAL FORM;Lo;0;AL;<final> 0686;;;;N;;;;; +FB7C;ARABIC LETTER TCHEH INITIAL FORM;Lo;0;AL;<initial> 0686;;;;N;;;;; +FB7D;ARABIC LETTER TCHEH MEDIAL FORM;Lo;0;AL;<medial> 0686;;;;N;;;;; +FB7E;ARABIC LETTER TCHEHEH ISOLATED FORM;Lo;0;AL;<isolated> 0687;;;;N;;;;; +FB7F;ARABIC LETTER TCHEHEH FINAL FORM;Lo;0;AL;<final> 0687;;;;N;;;;; +FB80;ARABIC LETTER TCHEHEH INITIAL FORM;Lo;0;AL;<initial> 0687;;;;N;;;;; +FB81;ARABIC LETTER TCHEHEH MEDIAL FORM;Lo;0;AL;<medial> 0687;;;;N;;;;; +FB82;ARABIC LETTER DDAHAL ISOLATED FORM;Lo;0;AL;<isolated> 068D;;;;N;;;;; +FB83;ARABIC LETTER DDAHAL FINAL FORM;Lo;0;AL;<final> 068D;;;;N;;;;; +FB84;ARABIC LETTER DAHAL ISOLATED FORM;Lo;0;AL;<isolated> 068C;;;;N;;;;; +FB85;ARABIC LETTER DAHAL FINAL FORM;Lo;0;AL;<final> 068C;;;;N;;;;; +FB86;ARABIC LETTER DUL ISOLATED FORM;Lo;0;AL;<isolated> 068E;;;;N;;;;; +FB87;ARABIC LETTER DUL FINAL FORM;Lo;0;AL;<final> 068E;;;;N;;;;; +FB88;ARABIC LETTER DDAL ISOLATED FORM;Lo;0;AL;<isolated> 0688;;;;N;;;;; +FB89;ARABIC LETTER DDAL FINAL FORM;Lo;0;AL;<final> 0688;;;;N;;;;; +FB8A;ARABIC LETTER JEH ISOLATED FORM;Lo;0;AL;<isolated> 0698;;;;N;;;;; +FB8B;ARABIC LETTER JEH FINAL FORM;Lo;0;AL;<final> 0698;;;;N;;;;; +FB8C;ARABIC LETTER RREH ISOLATED FORM;Lo;0;AL;<isolated> 0691;;;;N;;;;; +FB8D;ARABIC LETTER RREH FINAL FORM;Lo;0;AL;<final> 0691;;;;N;;;;; +FB8E;ARABIC LETTER KEHEH ISOLATED FORM;Lo;0;AL;<isolated> 06A9;;;;N;;;;; +FB8F;ARABIC LETTER KEHEH FINAL FORM;Lo;0;AL;<final> 06A9;;;;N;;;;; +FB90;ARABIC LETTER KEHEH INITIAL FORM;Lo;0;AL;<initial> 06A9;;;;N;;;;; +FB91;ARABIC LETTER KEHEH MEDIAL FORM;Lo;0;AL;<medial> 06A9;;;;N;;;;; +FB92;ARABIC LETTER GAF ISOLATED FORM;Lo;0;AL;<isolated> 06AF;;;;N;;;;; +FB93;ARABIC LETTER GAF FINAL FORM;Lo;0;AL;<final> 06AF;;;;N;;;;; +FB94;ARABIC LETTER GAF INITIAL FORM;Lo;0;AL;<initial> 06AF;;;;N;;;;; +FB95;ARABIC LETTER GAF MEDIAL FORM;Lo;0;AL;<medial> 06AF;;;;N;;;;; +FB96;ARABIC LETTER GUEH ISOLATED FORM;Lo;0;AL;<isolated> 06B3;;;;N;;;;; +FB97;ARABIC LETTER GUEH FINAL FORM;Lo;0;AL;<final> 06B3;;;;N;;;;; +FB98;ARABIC LETTER GUEH INITIAL FORM;Lo;0;AL;<initial> 06B3;;;;N;;;;; +FB99;ARABIC LETTER GUEH MEDIAL FORM;Lo;0;AL;<medial> 06B3;;;;N;;;;; +FB9A;ARABIC LETTER NGOEH ISOLATED FORM;Lo;0;AL;<isolated> 06B1;;;;N;;;;; +FB9B;ARABIC LETTER NGOEH FINAL FORM;Lo;0;AL;<final> 06B1;;;;N;;;;; +FB9C;ARABIC LETTER NGOEH INITIAL FORM;Lo;0;AL;<initial> 06B1;;;;N;;;;; +FB9D;ARABIC LETTER NGOEH MEDIAL FORM;Lo;0;AL;<medial> 06B1;;;;N;;;;; +FB9E;ARABIC LETTER NOON GHUNNA ISOLATED FORM;Lo;0;AL;<isolated> 06BA;;;;N;;;;; +FB9F;ARABIC LETTER NOON GHUNNA FINAL FORM;Lo;0;AL;<final> 06BA;;;;N;;;;; +FBA0;ARABIC LETTER RNOON ISOLATED FORM;Lo;0;AL;<isolated> 06BB;;;;N;;;;; +FBA1;ARABIC LETTER RNOON FINAL FORM;Lo;0;AL;<final> 06BB;;;;N;;;;; +FBA2;ARABIC LETTER RNOON INITIAL FORM;Lo;0;AL;<initial> 06BB;;;;N;;;;; +FBA3;ARABIC LETTER RNOON MEDIAL FORM;Lo;0;AL;<medial> 06BB;;;;N;;;;; +FBA4;ARABIC LETTER HEH WITH YEH ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 06C0;;;;N;;;;; +FBA5;ARABIC LETTER HEH WITH YEH ABOVE FINAL FORM;Lo;0;AL;<final> 06C0;;;;N;;;;; +FBA6;ARABIC LETTER HEH GOAL ISOLATED FORM;Lo;0;AL;<isolated> 06C1;;;;N;;;;; +FBA7;ARABIC LETTER HEH GOAL FINAL FORM;Lo;0;AL;<final> 06C1;;;;N;;;;; +FBA8;ARABIC LETTER HEH GOAL INITIAL FORM;Lo;0;AL;<initial> 06C1;;;;N;;;;; +FBA9;ARABIC LETTER HEH GOAL MEDIAL FORM;Lo;0;AL;<medial> 06C1;;;;N;;;;; +FBAA;ARABIC LETTER HEH DOACHASHMEE ISOLATED FORM;Lo;0;AL;<isolated> 06BE;;;;N;;;;; +FBAB;ARABIC LETTER HEH DOACHASHMEE FINAL FORM;Lo;0;AL;<final> 06BE;;;;N;;;;; +FBAC;ARABIC LETTER HEH DOACHASHMEE INITIAL FORM;Lo;0;AL;<initial> 06BE;;;;N;;;;; +FBAD;ARABIC LETTER HEH DOACHASHMEE MEDIAL FORM;Lo;0;AL;<medial> 06BE;;;;N;;;;; +FBAE;ARABIC LETTER YEH BARREE ISOLATED FORM;Lo;0;AL;<isolated> 06D2;;;;N;;;;; +FBAF;ARABIC LETTER YEH BARREE FINAL FORM;Lo;0;AL;<final> 06D2;;;;N;;;;; +FBB0;ARABIC LETTER YEH BARREE WITH HAMZA ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 06D3;;;;N;;;;; +FBB1;ARABIC LETTER YEH BARREE WITH HAMZA ABOVE FINAL FORM;Lo;0;AL;<final> 06D3;;;;N;;;;; +FBD3;ARABIC LETTER NG ISOLATED FORM;Lo;0;AL;<isolated> 06AD;;;;N;;;;; +FBD4;ARABIC LETTER NG FINAL FORM;Lo;0;AL;<final> 06AD;;;;N;;;;; +FBD5;ARABIC LETTER NG INITIAL FORM;Lo;0;AL;<initial> 06AD;;;;N;;;;; +FBD6;ARABIC LETTER NG MEDIAL FORM;Lo;0;AL;<medial> 06AD;;;;N;;;;; +FBD7;ARABIC LETTER U ISOLATED FORM;Lo;0;AL;<isolated> 06C7;;;;N;;;;; +FBD8;ARABIC LETTER U FINAL FORM;Lo;0;AL;<final> 06C7;;;;N;;;;; +FBD9;ARABIC LETTER OE ISOLATED FORM;Lo;0;AL;<isolated> 06C6;;;;N;;;;; +FBDA;ARABIC LETTER OE FINAL FORM;Lo;0;AL;<final> 06C6;;;;N;;;;; +FBDB;ARABIC LETTER YU ISOLATED FORM;Lo;0;AL;<isolated> 06C8;;;;N;;;;; +FBDC;ARABIC LETTER YU FINAL FORM;Lo;0;AL;<final> 06C8;;;;N;;;;; +FBDD;ARABIC LETTER U WITH HAMZA ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 0677;;;;N;;;;; +FBDE;ARABIC LETTER VE ISOLATED FORM;Lo;0;AL;<isolated> 06CB;;;;N;;;;; +FBDF;ARABIC LETTER VE FINAL FORM;Lo;0;AL;<final> 06CB;;;;N;;;;; +FBE0;ARABIC LETTER KIRGHIZ OE ISOLATED FORM;Lo;0;AL;<isolated> 06C5;;;;N;;;;; +FBE1;ARABIC LETTER KIRGHIZ OE FINAL FORM;Lo;0;AL;<final> 06C5;;;;N;;;;; +FBE2;ARABIC LETTER KIRGHIZ YU ISOLATED FORM;Lo;0;AL;<isolated> 06C9;;;;N;;;;; +FBE3;ARABIC LETTER KIRGHIZ YU FINAL FORM;Lo;0;AL;<final> 06C9;;;;N;;;;; +FBE4;ARABIC LETTER E ISOLATED FORM;Lo;0;AL;<isolated> 06D0;;;;N;;;;; +FBE5;ARABIC LETTER E FINAL FORM;Lo;0;AL;<final> 06D0;;;;N;;;;; +FBE6;ARABIC LETTER E INITIAL FORM;Lo;0;AL;<initial> 06D0;;;;N;;;;; +FBE7;ARABIC LETTER E MEDIAL FORM;Lo;0;AL;<medial> 06D0;;;;N;;;;; +FBE8;ARABIC LETTER UIGHUR KAZAKH KIRGHIZ ALEF MAKSURA INITIAL FORM;Lo;0;AL;<initial> 0649;;;;N;;;;; +FBE9;ARABIC LETTER UIGHUR KAZAKH KIRGHIZ ALEF MAKSURA MEDIAL FORM;Lo;0;AL;<medial> 0649;;;;N;;;;; +FBEA;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF ISOLATED FORM;Lo;0;AL;<isolated> 0626 0627;;;;N;;;;; +FBEB;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF FINAL FORM;Lo;0;AL;<final> 0626 0627;;;;N;;;;; +FBEC;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH AE ISOLATED FORM;Lo;0;AL;<isolated> 0626 06D5;;;;N;;;;; +FBED;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH AE FINAL FORM;Lo;0;AL;<final> 0626 06D5;;;;N;;;;; +FBEE;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH WAW ISOLATED FORM;Lo;0;AL;<isolated> 0626 0648;;;;N;;;;; +FBEF;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH WAW FINAL FORM;Lo;0;AL;<final> 0626 0648;;;;N;;;;; +FBF0;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH U ISOLATED FORM;Lo;0;AL;<isolated> 0626 06C7;;;;N;;;;; +FBF1;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH U FINAL FORM;Lo;0;AL;<final> 0626 06C7;;;;N;;;;; +FBF2;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH OE ISOLATED FORM;Lo;0;AL;<isolated> 0626 06C6;;;;N;;;;; +FBF3;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH OE FINAL FORM;Lo;0;AL;<final> 0626 06C6;;;;N;;;;; +FBF4;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YU ISOLATED FORM;Lo;0;AL;<isolated> 0626 06C8;;;;N;;;;; +FBF5;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YU FINAL FORM;Lo;0;AL;<final> 0626 06C8;;;;N;;;;; +FBF6;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH E ISOLATED FORM;Lo;0;AL;<isolated> 0626 06D0;;;;N;;;;; +FBF7;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH E FINAL FORM;Lo;0;AL;<final> 0626 06D0;;;;N;;;;; +FBF8;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH E INITIAL FORM;Lo;0;AL;<initial> 0626 06D0;;;;N;;;;; +FBF9;ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ABOVE WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0626 0649;;;;N;;;;; +FBFA;ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ABOVE WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0626 0649;;;;N;;;;; +FBFB;ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ABOVE WITH ALEF MAKSURA INITIAL FORM;Lo;0;AL;<initial> 0626 0649;;;;N;;;;; +FBFC;ARABIC LETTER FARSI YEH ISOLATED FORM;Lo;0;AL;<isolated> 06CC;;;;N;;;;; +FBFD;ARABIC LETTER FARSI YEH FINAL FORM;Lo;0;AL;<final> 06CC;;;;N;;;;; +FBFE;ARABIC LETTER FARSI YEH INITIAL FORM;Lo;0;AL;<initial> 06CC;;;;N;;;;; +FBFF;ARABIC LETTER FARSI YEH MEDIAL FORM;Lo;0;AL;<medial> 06CC;;;;N;;;;; +FC00;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0626 062C;;;;N;;;;; +FC01;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0626 062D;;;;N;;;;; +FC02;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0626 0645;;;;N;;;;; +FC03;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0626 0649;;;;N;;;;; +FC04;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0626 064A;;;;N;;;;; +FC05;ARABIC LIGATURE BEH WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0628 062C;;;;N;;;;; +FC06;ARABIC LIGATURE BEH WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0628 062D;;;;N;;;;; +FC07;ARABIC LIGATURE BEH WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0628 062E;;;;N;;;;; +FC08;ARABIC LIGATURE BEH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0628 0645;;;;N;;;;; +FC09;ARABIC LIGATURE BEH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0628 0649;;;;N;;;;; +FC0A;ARABIC LIGATURE BEH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0628 064A;;;;N;;;;; +FC0B;ARABIC LIGATURE TEH WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 062A 062C;;;;N;;;;; +FC0C;ARABIC LIGATURE TEH WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 062A 062D;;;;N;;;;; +FC0D;ARABIC LIGATURE TEH WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 062A 062E;;;;N;;;;; +FC0E;ARABIC LIGATURE TEH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 062A 0645;;;;N;;;;; +FC0F;ARABIC LIGATURE TEH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 062A 0649;;;;N;;;;; +FC10;ARABIC LIGATURE TEH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 062A 064A;;;;N;;;;; +FC11;ARABIC LIGATURE THEH WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 062B 062C;;;;N;;;;; +FC12;ARABIC LIGATURE THEH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 062B 0645;;;;N;;;;; +FC13;ARABIC LIGATURE THEH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 062B 0649;;;;N;;;;; +FC14;ARABIC LIGATURE THEH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 062B 064A;;;;N;;;;; +FC15;ARABIC LIGATURE JEEM WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 062C 062D;;;;N;;;;; +FC16;ARABIC LIGATURE JEEM WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 062C 0645;;;;N;;;;; +FC17;ARABIC LIGATURE HAH WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 062D 062C;;;;N;;;;; +FC18;ARABIC LIGATURE HAH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 062D 0645;;;;N;;;;; +FC19;ARABIC LIGATURE KHAH WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 062E 062C;;;;N;;;;; +FC1A;ARABIC LIGATURE KHAH WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 062E 062D;;;;N;;;;; +FC1B;ARABIC LIGATURE KHAH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 062E 0645;;;;N;;;;; +FC1C;ARABIC LIGATURE SEEN WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0633 062C;;;;N;;;;; +FC1D;ARABIC LIGATURE SEEN WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0633 062D;;;;N;;;;; +FC1E;ARABIC LIGATURE SEEN WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0633 062E;;;;N;;;;; +FC1F;ARABIC LIGATURE SEEN WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0633 0645;;;;N;;;;; +FC20;ARABIC LIGATURE SAD WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0635 062D;;;;N;;;;; +FC21;ARABIC LIGATURE SAD WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0635 0645;;;;N;;;;; +FC22;ARABIC LIGATURE DAD WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0636 062C;;;;N;;;;; +FC23;ARABIC LIGATURE DAD WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0636 062D;;;;N;;;;; +FC24;ARABIC LIGATURE DAD WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0636 062E;;;;N;;;;; +FC25;ARABIC LIGATURE DAD WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0636 0645;;;;N;;;;; +FC26;ARABIC LIGATURE TAH WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0637 062D;;;;N;;;;; +FC27;ARABIC LIGATURE TAH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0637 0645;;;;N;;;;; +FC28;ARABIC LIGATURE ZAH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0638 0645;;;;N;;;;; +FC29;ARABIC LIGATURE AIN WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0639 062C;;;;N;;;;; +FC2A;ARABIC LIGATURE AIN WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0639 0645;;;;N;;;;; +FC2B;ARABIC LIGATURE GHAIN WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 063A 062C;;;;N;;;;; +FC2C;ARABIC LIGATURE GHAIN WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 063A 0645;;;;N;;;;; +FC2D;ARABIC LIGATURE FEH WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0641 062C;;;;N;;;;; +FC2E;ARABIC LIGATURE FEH WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0641 062D;;;;N;;;;; +FC2F;ARABIC LIGATURE FEH WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0641 062E;;;;N;;;;; +FC30;ARABIC LIGATURE FEH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0641 0645;;;;N;;;;; +FC31;ARABIC LIGATURE FEH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0641 0649;;;;N;;;;; +FC32;ARABIC LIGATURE FEH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0641 064A;;;;N;;;;; +FC33;ARABIC LIGATURE QAF WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0642 062D;;;;N;;;;; +FC34;ARABIC LIGATURE QAF WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0642 0645;;;;N;;;;; +FC35;ARABIC LIGATURE QAF WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0642 0649;;;;N;;;;; +FC36;ARABIC LIGATURE QAF WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0642 064A;;;;N;;;;; +FC37;ARABIC LIGATURE KAF WITH ALEF ISOLATED FORM;Lo;0;AL;<isolated> 0643 0627;;;;N;;;;; +FC38;ARABIC LIGATURE KAF WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0643 062C;;;;N;;;;; +FC39;ARABIC LIGATURE KAF WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0643 062D;;;;N;;;;; +FC3A;ARABIC LIGATURE KAF WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0643 062E;;;;N;;;;; +FC3B;ARABIC LIGATURE KAF WITH LAM ISOLATED FORM;Lo;0;AL;<isolated> 0643 0644;;;;N;;;;; +FC3C;ARABIC LIGATURE KAF WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0643 0645;;;;N;;;;; +FC3D;ARABIC LIGATURE KAF WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0643 0649;;;;N;;;;; +FC3E;ARABIC LIGATURE KAF WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0643 064A;;;;N;;;;; +FC3F;ARABIC LIGATURE LAM WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0644 062C;;;;N;;;;; +FC40;ARABIC LIGATURE LAM WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0644 062D;;;;N;;;;; +FC41;ARABIC LIGATURE LAM WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0644 062E;;;;N;;;;; +FC42;ARABIC LIGATURE LAM WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0644 0645;;;;N;;;;; +FC43;ARABIC LIGATURE LAM WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0644 0649;;;;N;;;;; +FC44;ARABIC LIGATURE LAM WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0644 064A;;;;N;;;;; +FC45;ARABIC LIGATURE MEEM WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0645 062C;;;;N;;;;; +FC46;ARABIC LIGATURE MEEM WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0645 062D;;;;N;;;;; +FC47;ARABIC LIGATURE MEEM WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0645 062E;;;;N;;;;; +FC48;ARABIC LIGATURE MEEM WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0645 0645;;;;N;;;;; +FC49;ARABIC LIGATURE MEEM WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0645 0649;;;;N;;;;; +FC4A;ARABIC LIGATURE MEEM WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0645 064A;;;;N;;;;; +FC4B;ARABIC LIGATURE NOON WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0646 062C;;;;N;;;;; +FC4C;ARABIC LIGATURE NOON WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0646 062D;;;;N;;;;; +FC4D;ARABIC LIGATURE NOON WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0646 062E;;;;N;;;;; +FC4E;ARABIC LIGATURE NOON WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0646 0645;;;;N;;;;; +FC4F;ARABIC LIGATURE NOON WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0646 0649;;;;N;;;;; +FC50;ARABIC LIGATURE NOON WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0646 064A;;;;N;;;;; +FC51;ARABIC LIGATURE HEH WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0647 062C;;;;N;;;;; +FC52;ARABIC LIGATURE HEH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0647 0645;;;;N;;;;; +FC53;ARABIC LIGATURE HEH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0647 0649;;;;N;;;;; +FC54;ARABIC LIGATURE HEH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0647 064A;;;;N;;;;; +FC55;ARABIC LIGATURE YEH WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 064A 062C;;;;N;;;;; +FC56;ARABIC LIGATURE YEH WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 064A 062D;;;;N;;;;; +FC57;ARABIC LIGATURE YEH WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 064A 062E;;;;N;;;;; +FC58;ARABIC LIGATURE YEH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 064A 0645;;;;N;;;;; +FC59;ARABIC LIGATURE YEH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 064A 0649;;;;N;;;;; +FC5A;ARABIC LIGATURE YEH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 064A 064A;;;;N;;;;; +FC5B;ARABIC LIGATURE THAL WITH SUPERSCRIPT ALEF ISOLATED FORM;Lo;0;AL;<isolated> 0630 0670;;;;N;;;;; +FC5C;ARABIC LIGATURE REH WITH SUPERSCRIPT ALEF ISOLATED FORM;Lo;0;AL;<isolated> 0631 0670;;;;N;;;;; +FC5D;ARABIC LIGATURE ALEF MAKSURA WITH SUPERSCRIPT ALEF ISOLATED FORM;Lo;0;AL;<isolated> 0649 0670;;;;N;;;;; +FC5E;ARABIC LIGATURE SHADDA WITH DAMMATAN ISOLATED FORM;Lo;0;AL;<isolated> 0020 064C 0651;;;;N;;;;; +FC5F;ARABIC LIGATURE SHADDA WITH KASRATAN ISOLATED FORM;Lo;0;AL;<isolated> 0020 064D 0651;;;;N;;;;; +FC60;ARABIC LIGATURE SHADDA WITH FATHA ISOLATED FORM;Lo;0;AL;<isolated> 0020 064E 0651;;;;N;;;;; +FC61;ARABIC LIGATURE SHADDA WITH DAMMA ISOLATED FORM;Lo;0;AL;<isolated> 0020 064F 0651;;;;N;;;;; +FC62;ARABIC LIGATURE SHADDA WITH KASRA ISOLATED FORM;Lo;0;AL;<isolated> 0020 0650 0651;;;;N;;;;; +FC63;ARABIC LIGATURE SHADDA WITH SUPERSCRIPT ALEF ISOLATED FORM;Lo;0;AL;<isolated> 0020 0651 0670;;;;N;;;;; +FC64;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH REH FINAL FORM;Lo;0;AL;<final> 0626 0631;;;;N;;;;; +FC65;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ZAIN FINAL FORM;Lo;0;AL;<final> 0626 0632;;;;N;;;;; +FC66;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM FINAL FORM;Lo;0;AL;<final> 0626 0645;;;;N;;;;; +FC67;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH NOON FINAL FORM;Lo;0;AL;<final> 0626 0646;;;;N;;;;; +FC68;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0626 0649;;;;N;;;;; +FC69;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YEH FINAL FORM;Lo;0;AL;<final> 0626 064A;;;;N;;;;; +FC6A;ARABIC LIGATURE BEH WITH REH FINAL FORM;Lo;0;AL;<final> 0628 0631;;;;N;;;;; +FC6B;ARABIC LIGATURE BEH WITH ZAIN FINAL FORM;Lo;0;AL;<final> 0628 0632;;;;N;;;;; +FC6C;ARABIC LIGATURE BEH WITH MEEM FINAL FORM;Lo;0;AL;<final> 0628 0645;;;;N;;;;; +FC6D;ARABIC LIGATURE BEH WITH NOON FINAL FORM;Lo;0;AL;<final> 0628 0646;;;;N;;;;; +FC6E;ARABIC LIGATURE BEH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0628 0649;;;;N;;;;; +FC6F;ARABIC LIGATURE BEH WITH YEH FINAL FORM;Lo;0;AL;<final> 0628 064A;;;;N;;;;; +FC70;ARABIC LIGATURE TEH WITH REH FINAL FORM;Lo;0;AL;<final> 062A 0631;;;;N;;;;; +FC71;ARABIC LIGATURE TEH WITH ZAIN FINAL FORM;Lo;0;AL;<final> 062A 0632;;;;N;;;;; +FC72;ARABIC LIGATURE TEH WITH MEEM FINAL FORM;Lo;0;AL;<final> 062A 0645;;;;N;;;;; +FC73;ARABIC LIGATURE TEH WITH NOON FINAL FORM;Lo;0;AL;<final> 062A 0646;;;;N;;;;; +FC74;ARABIC LIGATURE TEH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062A 0649;;;;N;;;;; +FC75;ARABIC LIGATURE TEH WITH YEH FINAL FORM;Lo;0;AL;<final> 062A 064A;;;;N;;;;; +FC76;ARABIC LIGATURE THEH WITH REH FINAL FORM;Lo;0;AL;<final> 062B 0631;;;;N;;;;; +FC77;ARABIC LIGATURE THEH WITH ZAIN FINAL FORM;Lo;0;AL;<final> 062B 0632;;;;N;;;;; +FC78;ARABIC LIGATURE THEH WITH MEEM FINAL FORM;Lo;0;AL;<final> 062B 0645;;;;N;;;;; +FC79;ARABIC LIGATURE THEH WITH NOON FINAL FORM;Lo;0;AL;<final> 062B 0646;;;;N;;;;; +FC7A;ARABIC LIGATURE THEH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062B 0649;;;;N;;;;; +FC7B;ARABIC LIGATURE THEH WITH YEH FINAL FORM;Lo;0;AL;<final> 062B 064A;;;;N;;;;; +FC7C;ARABIC LIGATURE FEH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0641 0649;;;;N;;;;; +FC7D;ARABIC LIGATURE FEH WITH YEH FINAL FORM;Lo;0;AL;<final> 0641 064A;;;;N;;;;; +FC7E;ARABIC LIGATURE QAF WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0642 0649;;;;N;;;;; +FC7F;ARABIC LIGATURE QAF WITH YEH FINAL FORM;Lo;0;AL;<final> 0642 064A;;;;N;;;;; +FC80;ARABIC LIGATURE KAF WITH ALEF FINAL FORM;Lo;0;AL;<final> 0643 0627;;;;N;;;;; +FC81;ARABIC LIGATURE KAF WITH LAM FINAL FORM;Lo;0;AL;<final> 0643 0644;;;;N;;;;; +FC82;ARABIC LIGATURE KAF WITH MEEM FINAL FORM;Lo;0;AL;<final> 0643 0645;;;;N;;;;; +FC83;ARABIC LIGATURE KAF WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0643 0649;;;;N;;;;; +FC84;ARABIC LIGATURE KAF WITH YEH FINAL FORM;Lo;0;AL;<final> 0643 064A;;;;N;;;;; +FC85;ARABIC LIGATURE LAM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0644 0645;;;;N;;;;; +FC86;ARABIC LIGATURE LAM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0644 0649;;;;N;;;;; +FC87;ARABIC LIGATURE LAM WITH YEH FINAL FORM;Lo;0;AL;<final> 0644 064A;;;;N;;;;; +FC88;ARABIC LIGATURE MEEM WITH ALEF FINAL FORM;Lo;0;AL;<final> 0645 0627;;;;N;;;;; +FC89;ARABIC LIGATURE MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0645 0645;;;;N;;;;; +FC8A;ARABIC LIGATURE NOON WITH REH FINAL FORM;Lo;0;AL;<final> 0646 0631;;;;N;;;;; +FC8B;ARABIC LIGATURE NOON WITH ZAIN FINAL FORM;Lo;0;AL;<final> 0646 0632;;;;N;;;;; +FC8C;ARABIC LIGATURE NOON WITH MEEM FINAL FORM;Lo;0;AL;<final> 0646 0645;;;;N;;;;; +FC8D;ARABIC LIGATURE NOON WITH NOON FINAL FORM;Lo;0;AL;<final> 0646 0646;;;;N;;;;; +FC8E;ARABIC LIGATURE NOON WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0646 0649;;;;N;;;;; +FC8F;ARABIC LIGATURE NOON WITH YEH FINAL FORM;Lo;0;AL;<final> 0646 064A;;;;N;;;;; +FC90;ARABIC LIGATURE ALEF MAKSURA WITH SUPERSCRIPT ALEF FINAL FORM;Lo;0;AL;<final> 0649 0670;;;;N;;;;; +FC91;ARABIC LIGATURE YEH WITH REH FINAL FORM;Lo;0;AL;<final> 064A 0631;;;;N;;;;; +FC92;ARABIC LIGATURE YEH WITH ZAIN FINAL FORM;Lo;0;AL;<final> 064A 0632;;;;N;;;;; +FC93;ARABIC LIGATURE YEH WITH MEEM FINAL FORM;Lo;0;AL;<final> 064A 0645;;;;N;;;;; +FC94;ARABIC LIGATURE YEH WITH NOON FINAL FORM;Lo;0;AL;<final> 064A 0646;;;;N;;;;; +FC95;ARABIC LIGATURE YEH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 064A 0649;;;;N;;;;; +FC96;ARABIC LIGATURE YEH WITH YEH FINAL FORM;Lo;0;AL;<final> 064A 064A;;;;N;;;;; +FC97;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0626 062C;;;;N;;;;; +FC98;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0626 062D;;;;N;;;;; +FC99;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0626 062E;;;;N;;;;; +FC9A;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0626 0645;;;;N;;;;; +FC9B;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HEH INITIAL FORM;Lo;0;AL;<initial> 0626 0647;;;;N;;;;; +FC9C;ARABIC LIGATURE BEH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0628 062C;;;;N;;;;; +FC9D;ARABIC LIGATURE BEH WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0628 062D;;;;N;;;;; +FC9E;ARABIC LIGATURE BEH WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0628 062E;;;;N;;;;; +FC9F;ARABIC LIGATURE BEH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0628 0645;;;;N;;;;; +FCA0;ARABIC LIGATURE BEH WITH HEH INITIAL FORM;Lo;0;AL;<initial> 0628 0647;;;;N;;;;; +FCA1;ARABIC LIGATURE TEH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 062A 062C;;;;N;;;;; +FCA2;ARABIC LIGATURE TEH WITH HAH INITIAL FORM;Lo;0;AL;<initial> 062A 062D;;;;N;;;;; +FCA3;ARABIC LIGATURE TEH WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 062A 062E;;;;N;;;;; +FCA4;ARABIC LIGATURE TEH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 062A 0645;;;;N;;;;; +FCA5;ARABIC LIGATURE TEH WITH HEH INITIAL FORM;Lo;0;AL;<initial> 062A 0647;;;;N;;;;; +FCA6;ARABIC LIGATURE THEH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 062B 0645;;;;N;;;;; +FCA7;ARABIC LIGATURE JEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 062C 062D;;;;N;;;;; +FCA8;ARABIC LIGATURE JEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 062C 0645;;;;N;;;;; +FCA9;ARABIC LIGATURE HAH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 062D 062C;;;;N;;;;; +FCAA;ARABIC LIGATURE HAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 062D 0645;;;;N;;;;; +FCAB;ARABIC LIGATURE KHAH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 062E 062C;;;;N;;;;; +FCAC;ARABIC LIGATURE KHAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 062E 0645;;;;N;;;;; +FCAD;ARABIC LIGATURE SEEN WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0633 062C;;;;N;;;;; +FCAE;ARABIC LIGATURE SEEN WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0633 062D;;;;N;;;;; +FCAF;ARABIC LIGATURE SEEN WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0633 062E;;;;N;;;;; +FCB0;ARABIC LIGATURE SEEN WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0633 0645;;;;N;;;;; +FCB1;ARABIC LIGATURE SAD WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0635 062D;;;;N;;;;; +FCB2;ARABIC LIGATURE SAD WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0635 062E;;;;N;;;;; +FCB3;ARABIC LIGATURE SAD WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0635 0645;;;;N;;;;; +FCB4;ARABIC LIGATURE DAD WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0636 062C;;;;N;;;;; +FCB5;ARABIC LIGATURE DAD WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0636 062D;;;;N;;;;; +FCB6;ARABIC LIGATURE DAD WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0636 062E;;;;N;;;;; +FCB7;ARABIC LIGATURE DAD WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0636 0645;;;;N;;;;; +FCB8;ARABIC LIGATURE TAH WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0637 062D;;;;N;;;;; +FCB9;ARABIC LIGATURE ZAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0638 0645;;;;N;;;;; +FCBA;ARABIC LIGATURE AIN WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0639 062C;;;;N;;;;; +FCBB;ARABIC LIGATURE AIN WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0639 0645;;;;N;;;;; +FCBC;ARABIC LIGATURE GHAIN WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 063A 062C;;;;N;;;;; +FCBD;ARABIC LIGATURE GHAIN WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 063A 0645;;;;N;;;;; +FCBE;ARABIC LIGATURE FEH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0641 062C;;;;N;;;;; +FCBF;ARABIC LIGATURE FEH WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0641 062D;;;;N;;;;; +FCC0;ARABIC LIGATURE FEH WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0641 062E;;;;N;;;;; +FCC1;ARABIC LIGATURE FEH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0641 0645;;;;N;;;;; +FCC2;ARABIC LIGATURE QAF WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0642 062D;;;;N;;;;; +FCC3;ARABIC LIGATURE QAF WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0642 0645;;;;N;;;;; +FCC4;ARABIC LIGATURE KAF WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0643 062C;;;;N;;;;; +FCC5;ARABIC LIGATURE KAF WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0643 062D;;;;N;;;;; +FCC6;ARABIC LIGATURE KAF WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0643 062E;;;;N;;;;; +FCC7;ARABIC LIGATURE KAF WITH LAM INITIAL FORM;Lo;0;AL;<initial> 0643 0644;;;;N;;;;; +FCC8;ARABIC LIGATURE KAF WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0643 0645;;;;N;;;;; +FCC9;ARABIC LIGATURE LAM WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0644 062C;;;;N;;;;; +FCCA;ARABIC LIGATURE LAM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0644 062D;;;;N;;;;; +FCCB;ARABIC LIGATURE LAM WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0644 062E;;;;N;;;;; +FCCC;ARABIC LIGATURE LAM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0644 0645;;;;N;;;;; +FCCD;ARABIC LIGATURE LAM WITH HEH INITIAL FORM;Lo;0;AL;<initial> 0644 0647;;;;N;;;;; +FCCE;ARABIC LIGATURE MEEM WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0645 062C;;;;N;;;;; +FCCF;ARABIC LIGATURE MEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0645 062D;;;;N;;;;; +FCD0;ARABIC LIGATURE MEEM WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0645 062E;;;;N;;;;; +FCD1;ARABIC LIGATURE MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0645 0645;;;;N;;;;; +FCD2;ARABIC LIGATURE NOON WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0646 062C;;;;N;;;;; +FCD3;ARABIC LIGATURE NOON WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0646 062D;;;;N;;;;; +FCD4;ARABIC LIGATURE NOON WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0646 062E;;;;N;;;;; +FCD5;ARABIC LIGATURE NOON WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0646 0645;;;;N;;;;; +FCD6;ARABIC LIGATURE NOON WITH HEH INITIAL FORM;Lo;0;AL;<initial> 0646 0647;;;;N;;;;; +FCD7;ARABIC LIGATURE HEH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0647 062C;;;;N;;;;; +FCD8;ARABIC LIGATURE HEH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0647 0645;;;;N;;;;; +FCD9;ARABIC LIGATURE HEH WITH SUPERSCRIPT ALEF INITIAL FORM;Lo;0;AL;<initial> 0647 0670;;;;N;;;;; +FCDA;ARABIC LIGATURE YEH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 064A 062C;;;;N;;;;; +FCDB;ARABIC LIGATURE YEH WITH HAH INITIAL FORM;Lo;0;AL;<initial> 064A 062D;;;;N;;;;; +FCDC;ARABIC LIGATURE YEH WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 064A 062E;;;;N;;;;; +FCDD;ARABIC LIGATURE YEH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 064A 0645;;;;N;;;;; +FCDE;ARABIC LIGATURE YEH WITH HEH INITIAL FORM;Lo;0;AL;<initial> 064A 0647;;;;N;;;;; +FCDF;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0626 0645;;;;N;;;;; +FCE0;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HEH MEDIAL FORM;Lo;0;AL;<medial> 0626 0647;;;;N;;;;; +FCE1;ARABIC LIGATURE BEH WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0628 0645;;;;N;;;;; +FCE2;ARABIC LIGATURE BEH WITH HEH MEDIAL FORM;Lo;0;AL;<medial> 0628 0647;;;;N;;;;; +FCE3;ARABIC LIGATURE TEH WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 062A 0645;;;;N;;;;; +FCE4;ARABIC LIGATURE TEH WITH HEH MEDIAL FORM;Lo;0;AL;<medial> 062A 0647;;;;N;;;;; +FCE5;ARABIC LIGATURE THEH WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 062B 0645;;;;N;;;;; +FCE6;ARABIC LIGATURE THEH WITH HEH MEDIAL FORM;Lo;0;AL;<medial> 062B 0647;;;;N;;;;; +FCE7;ARABIC LIGATURE SEEN WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0633 0645;;;;N;;;;; +FCE8;ARABIC LIGATURE SEEN WITH HEH MEDIAL FORM;Lo;0;AL;<medial> 0633 0647;;;;N;;;;; +FCE9;ARABIC LIGATURE SHEEN WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0634 0645;;;;N;;;;; +FCEA;ARABIC LIGATURE SHEEN WITH HEH MEDIAL FORM;Lo;0;AL;<medial> 0634 0647;;;;N;;;;; +FCEB;ARABIC LIGATURE KAF WITH LAM MEDIAL FORM;Lo;0;AL;<medial> 0643 0644;;;;N;;;;; +FCEC;ARABIC LIGATURE KAF WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0643 0645;;;;N;;;;; +FCED;ARABIC LIGATURE LAM WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0644 0645;;;;N;;;;; +FCEE;ARABIC LIGATURE NOON WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0646 0645;;;;N;;;;; +FCEF;ARABIC LIGATURE NOON WITH HEH MEDIAL FORM;Lo;0;AL;<medial> 0646 0647;;;;N;;;;; +FCF0;ARABIC LIGATURE YEH WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 064A 0645;;;;N;;;;; +FCF1;ARABIC LIGATURE YEH WITH HEH MEDIAL FORM;Lo;0;AL;<medial> 064A 0647;;;;N;;;;; +FCF2;ARABIC LIGATURE SHADDA WITH FATHA MEDIAL FORM;Lo;0;AL;<medial> 0640 064E 0651;;;;N;;;;; +FCF3;ARABIC LIGATURE SHADDA WITH DAMMA MEDIAL FORM;Lo;0;AL;<medial> 0640 064F 0651;;;;N;;;;; +FCF4;ARABIC LIGATURE SHADDA WITH KASRA MEDIAL FORM;Lo;0;AL;<medial> 0640 0650 0651;;;;N;;;;; +FCF5;ARABIC LIGATURE TAH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0637 0649;;;;N;;;;; +FCF6;ARABIC LIGATURE TAH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0637 064A;;;;N;;;;; +FCF7;ARABIC LIGATURE AIN WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0639 0649;;;;N;;;;; +FCF8;ARABIC LIGATURE AIN WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0639 064A;;;;N;;;;; +FCF9;ARABIC LIGATURE GHAIN WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 063A 0649;;;;N;;;;; +FCFA;ARABIC LIGATURE GHAIN WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 063A 064A;;;;N;;;;; +FCFB;ARABIC LIGATURE SEEN WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0633 0649;;;;N;;;;; +FCFC;ARABIC LIGATURE SEEN WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0633 064A;;;;N;;;;; +FCFD;ARABIC LIGATURE SHEEN WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0634 0649;;;;N;;;;; +FCFE;ARABIC LIGATURE SHEEN WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0634 064A;;;;N;;;;; +FCFF;ARABIC LIGATURE HAH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 062D 0649;;;;N;;;;; +FD00;ARABIC LIGATURE HAH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 062D 064A;;;;N;;;;; +FD01;ARABIC LIGATURE JEEM WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 062C 0649;;;;N;;;;; +FD02;ARABIC LIGATURE JEEM WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 062C 064A;;;;N;;;;; +FD03;ARABIC LIGATURE KHAH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 062E 0649;;;;N;;;;; +FD04;ARABIC LIGATURE KHAH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 062E 064A;;;;N;;;;; +FD05;ARABIC LIGATURE SAD WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0635 0649;;;;N;;;;; +FD06;ARABIC LIGATURE SAD WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0635 064A;;;;N;;;;; +FD07;ARABIC LIGATURE DAD WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0636 0649;;;;N;;;;; +FD08;ARABIC LIGATURE DAD WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0636 064A;;;;N;;;;; +FD09;ARABIC LIGATURE SHEEN WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0634 062C;;;;N;;;;; +FD0A;ARABIC LIGATURE SHEEN WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0634 062D;;;;N;;;;; +FD0B;ARABIC LIGATURE SHEEN WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0634 062E;;;;N;;;;; +FD0C;ARABIC LIGATURE SHEEN WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0634 0645;;;;N;;;;; +FD0D;ARABIC LIGATURE SHEEN WITH REH ISOLATED FORM;Lo;0;AL;<isolated> 0634 0631;;;;N;;;;; +FD0E;ARABIC LIGATURE SEEN WITH REH ISOLATED FORM;Lo;0;AL;<isolated> 0633 0631;;;;N;;;;; +FD0F;ARABIC LIGATURE SAD WITH REH ISOLATED FORM;Lo;0;AL;<isolated> 0635 0631;;;;N;;;;; +FD10;ARABIC LIGATURE DAD WITH REH ISOLATED FORM;Lo;0;AL;<isolated> 0636 0631;;;;N;;;;; +FD11;ARABIC LIGATURE TAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0637 0649;;;;N;;;;; +FD12;ARABIC LIGATURE TAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0637 064A;;;;N;;;;; +FD13;ARABIC LIGATURE AIN WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0639 0649;;;;N;;;;; +FD14;ARABIC LIGATURE AIN WITH YEH FINAL FORM;Lo;0;AL;<final> 0639 064A;;;;N;;;;; +FD15;ARABIC LIGATURE GHAIN WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 063A 0649;;;;N;;;;; +FD16;ARABIC LIGATURE GHAIN WITH YEH FINAL FORM;Lo;0;AL;<final> 063A 064A;;;;N;;;;; +FD17;ARABIC LIGATURE SEEN WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0633 0649;;;;N;;;;; +FD18;ARABIC LIGATURE SEEN WITH YEH FINAL FORM;Lo;0;AL;<final> 0633 064A;;;;N;;;;; +FD19;ARABIC LIGATURE SHEEN WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0634 0649;;;;N;;;;; +FD1A;ARABIC LIGATURE SHEEN WITH YEH FINAL FORM;Lo;0;AL;<final> 0634 064A;;;;N;;;;; +FD1B;ARABIC LIGATURE HAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062D 0649;;;;N;;;;; +FD1C;ARABIC LIGATURE HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 062D 064A;;;;N;;;;; +FD1D;ARABIC LIGATURE JEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062C 0649;;;;N;;;;; +FD1E;ARABIC LIGATURE JEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 062C 064A;;;;N;;;;; +FD1F;ARABIC LIGATURE KHAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062E 0649;;;;N;;;;; +FD20;ARABIC LIGATURE KHAH WITH YEH FINAL FORM;Lo;0;AL;<final> 062E 064A;;;;N;;;;; +FD21;ARABIC LIGATURE SAD WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0635 0649;;;;N;;;;; +FD22;ARABIC LIGATURE SAD WITH YEH FINAL FORM;Lo;0;AL;<final> 0635 064A;;;;N;;;;; +FD23;ARABIC LIGATURE DAD WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0636 0649;;;;N;;;;; +FD24;ARABIC LIGATURE DAD WITH YEH FINAL FORM;Lo;0;AL;<final> 0636 064A;;;;N;;;;; +FD25;ARABIC LIGATURE SHEEN WITH JEEM FINAL FORM;Lo;0;AL;<final> 0634 062C;;;;N;;;;; +FD26;ARABIC LIGATURE SHEEN WITH HAH FINAL FORM;Lo;0;AL;<final> 0634 062D;;;;N;;;;; +FD27;ARABIC LIGATURE SHEEN WITH KHAH FINAL FORM;Lo;0;AL;<final> 0634 062E;;;;N;;;;; +FD28;ARABIC LIGATURE SHEEN WITH MEEM FINAL FORM;Lo;0;AL;<final> 0634 0645;;;;N;;;;; +FD29;ARABIC LIGATURE SHEEN WITH REH FINAL FORM;Lo;0;AL;<final> 0634 0631;;;;N;;;;; +FD2A;ARABIC LIGATURE SEEN WITH REH FINAL FORM;Lo;0;AL;<final> 0633 0631;;;;N;;;;; +FD2B;ARABIC LIGATURE SAD WITH REH FINAL FORM;Lo;0;AL;<final> 0635 0631;;;;N;;;;; +FD2C;ARABIC LIGATURE DAD WITH REH FINAL FORM;Lo;0;AL;<final> 0636 0631;;;;N;;;;; +FD2D;ARABIC LIGATURE SHEEN WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0634 062C;;;;N;;;;; +FD2E;ARABIC LIGATURE SHEEN WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0634 062D;;;;N;;;;; +FD2F;ARABIC LIGATURE SHEEN WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0634 062E;;;;N;;;;; +FD30;ARABIC LIGATURE SHEEN WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0634 0645;;;;N;;;;; +FD31;ARABIC LIGATURE SEEN WITH HEH INITIAL FORM;Lo;0;AL;<initial> 0633 0647;;;;N;;;;; +FD32;ARABIC LIGATURE SHEEN WITH HEH INITIAL FORM;Lo;0;AL;<initial> 0634 0647;;;;N;;;;; +FD33;ARABIC LIGATURE TAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0637 0645;;;;N;;;;; +FD34;ARABIC LIGATURE SEEN WITH JEEM MEDIAL FORM;Lo;0;AL;<medial> 0633 062C;;;;N;;;;; +FD35;ARABIC LIGATURE SEEN WITH HAH MEDIAL FORM;Lo;0;AL;<medial> 0633 062D;;;;N;;;;; +FD36;ARABIC LIGATURE SEEN WITH KHAH MEDIAL FORM;Lo;0;AL;<medial> 0633 062E;;;;N;;;;; +FD37;ARABIC LIGATURE SHEEN WITH JEEM MEDIAL FORM;Lo;0;AL;<medial> 0634 062C;;;;N;;;;; +FD38;ARABIC LIGATURE SHEEN WITH HAH MEDIAL FORM;Lo;0;AL;<medial> 0634 062D;;;;N;;;;; +FD39;ARABIC LIGATURE SHEEN WITH KHAH MEDIAL FORM;Lo;0;AL;<medial> 0634 062E;;;;N;;;;; +FD3A;ARABIC LIGATURE TAH WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0637 0645;;;;N;;;;; +FD3B;ARABIC LIGATURE ZAH WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0638 0645;;;;N;;;;; +FD3C;ARABIC LIGATURE ALEF WITH FATHATAN FINAL FORM;Lo;0;AL;<final> 0627 064B;;;;N;;;;; +FD3D;ARABIC LIGATURE ALEF WITH FATHATAN ISOLATED FORM;Lo;0;AL;<isolated> 0627 064B;;;;N;;;;; +FD3E;ORNATE LEFT PARENTHESIS;Ps;0;ON;;;;;N;;;;; +FD3F;ORNATE RIGHT PARENTHESIS;Pe;0;ON;;;;;N;;;;; +FD50;ARABIC LIGATURE TEH WITH JEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 062A 062C 0645;;;;N;;;;; +FD51;ARABIC LIGATURE TEH WITH HAH WITH JEEM FINAL FORM;Lo;0;AL;<final> 062A 062D 062C;;;;N;;;;; +FD52;ARABIC LIGATURE TEH WITH HAH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 062A 062D 062C;;;;N;;;;; +FD53;ARABIC LIGATURE TEH WITH HAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 062A 062D 0645;;;;N;;;;; +FD54;ARABIC LIGATURE TEH WITH KHAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 062A 062E 0645;;;;N;;;;; +FD55;ARABIC LIGATURE TEH WITH MEEM WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 062A 0645 062C;;;;N;;;;; +FD56;ARABIC LIGATURE TEH WITH MEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 062A 0645 062D;;;;N;;;;; +FD57;ARABIC LIGATURE TEH WITH MEEM WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 062A 0645 062E;;;;N;;;;; +FD58;ARABIC LIGATURE JEEM WITH MEEM WITH HAH FINAL FORM;Lo;0;AL;<final> 062C 0645 062D;;;;N;;;;; +FD59;ARABIC LIGATURE JEEM WITH MEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 062C 0645 062D;;;;N;;;;; +FD5A;ARABIC LIGATURE HAH WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 062D 0645 064A;;;;N;;;;; +FD5B;ARABIC LIGATURE HAH WITH MEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062D 0645 0649;;;;N;;;;; +FD5C;ARABIC LIGATURE SEEN WITH HAH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0633 062D 062C;;;;N;;;;; +FD5D;ARABIC LIGATURE SEEN WITH JEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0633 062C 062D;;;;N;;;;; +FD5E;ARABIC LIGATURE SEEN WITH JEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0633 062C 0649;;;;N;;;;; +FD5F;ARABIC LIGATURE SEEN WITH MEEM WITH HAH FINAL FORM;Lo;0;AL;<final> 0633 0645 062D;;;;N;;;;; +FD60;ARABIC LIGATURE SEEN WITH MEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0633 0645 062D;;;;N;;;;; +FD61;ARABIC LIGATURE SEEN WITH MEEM WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0633 0645 062C;;;;N;;;;; +FD62;ARABIC LIGATURE SEEN WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0633 0645 0645;;;;N;;;;; +FD63;ARABIC LIGATURE SEEN WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0633 0645 0645;;;;N;;;;; +FD64;ARABIC LIGATURE SAD WITH HAH WITH HAH FINAL FORM;Lo;0;AL;<final> 0635 062D 062D;;;;N;;;;; +FD65;ARABIC LIGATURE SAD WITH HAH WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0635 062D 062D;;;;N;;;;; +FD66;ARABIC LIGATURE SAD WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0635 0645 0645;;;;N;;;;; +FD67;ARABIC LIGATURE SHEEN WITH HAH WITH MEEM FINAL FORM;Lo;0;AL;<final> 0634 062D 0645;;;;N;;;;; +FD68;ARABIC LIGATURE SHEEN WITH HAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0634 062D 0645;;;;N;;;;; +FD69;ARABIC LIGATURE SHEEN WITH JEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0634 062C 064A;;;;N;;;;; +FD6A;ARABIC LIGATURE SHEEN WITH MEEM WITH KHAH FINAL FORM;Lo;0;AL;<final> 0634 0645 062E;;;;N;;;;; +FD6B;ARABIC LIGATURE SHEEN WITH MEEM WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0634 0645 062E;;;;N;;;;; +FD6C;ARABIC LIGATURE SHEEN WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0634 0645 0645;;;;N;;;;; +FD6D;ARABIC LIGATURE SHEEN WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0634 0645 0645;;;;N;;;;; +FD6E;ARABIC LIGATURE DAD WITH HAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0636 062D 0649;;;;N;;;;; +FD6F;ARABIC LIGATURE DAD WITH KHAH WITH MEEM FINAL FORM;Lo;0;AL;<final> 0636 062E 0645;;;;N;;;;; +FD70;ARABIC LIGATURE DAD WITH KHAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0636 062E 0645;;;;N;;;;; +FD71;ARABIC LIGATURE TAH WITH MEEM WITH HAH FINAL FORM;Lo;0;AL;<final> 0637 0645 062D;;;;N;;;;; +FD72;ARABIC LIGATURE TAH WITH MEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0637 0645 062D;;;;N;;;;; +FD73;ARABIC LIGATURE TAH WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0637 0645 0645;;;;N;;;;; +FD74;ARABIC LIGATURE TAH WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0637 0645 064A;;;;N;;;;; +FD75;ARABIC LIGATURE AIN WITH JEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0639 062C 0645;;;;N;;;;; +FD76;ARABIC LIGATURE AIN WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0639 0645 0645;;;;N;;;;; +FD77;ARABIC LIGATURE AIN WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0639 0645 0645;;;;N;;;;; +FD78;ARABIC LIGATURE AIN WITH MEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0639 0645 0649;;;;N;;;;; +FD79;ARABIC LIGATURE GHAIN WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 063A 0645 0645;;;;N;;;;; +FD7A;ARABIC LIGATURE GHAIN WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 063A 0645 064A;;;;N;;;;; +FD7B;ARABIC LIGATURE GHAIN WITH MEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 063A 0645 0649;;;;N;;;;; +FD7C;ARABIC LIGATURE FEH WITH KHAH WITH MEEM FINAL FORM;Lo;0;AL;<final> 0641 062E 0645;;;;N;;;;; +FD7D;ARABIC LIGATURE FEH WITH KHAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0641 062E 0645;;;;N;;;;; +FD7E;ARABIC LIGATURE QAF WITH MEEM WITH HAH FINAL FORM;Lo;0;AL;<final> 0642 0645 062D;;;;N;;;;; +FD7F;ARABIC LIGATURE QAF WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0642 0645 0645;;;;N;;;;; +FD80;ARABIC LIGATURE LAM WITH HAH WITH MEEM FINAL FORM;Lo;0;AL;<final> 0644 062D 0645;;;;N;;;;; +FD81;ARABIC LIGATURE LAM WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0644 062D 064A;;;;N;;;;; +FD82;ARABIC LIGATURE LAM WITH HAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0644 062D 0649;;;;N;;;;; +FD83;ARABIC LIGATURE LAM WITH JEEM WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0644 062C 062C;;;;N;;;;; +FD84;ARABIC LIGATURE LAM WITH JEEM WITH JEEM FINAL FORM;Lo;0;AL;<final> 0644 062C 062C;;;;N;;;;; +FD85;ARABIC LIGATURE LAM WITH KHAH WITH MEEM FINAL FORM;Lo;0;AL;<final> 0644 062E 0645;;;;N;;;;; +FD86;ARABIC LIGATURE LAM WITH KHAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0644 062E 0645;;;;N;;;;; +FD87;ARABIC LIGATURE LAM WITH MEEM WITH HAH FINAL FORM;Lo;0;AL;<final> 0644 0645 062D;;;;N;;;;; +FD88;ARABIC LIGATURE LAM WITH MEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0644 0645 062D;;;;N;;;;; +FD89;ARABIC LIGATURE MEEM WITH HAH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0645 062D 062C;;;;N;;;;; +FD8A;ARABIC LIGATURE MEEM WITH HAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0645 062D 0645;;;;N;;;;; +FD8B;ARABIC LIGATURE MEEM WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0645 062D 064A;;;;N;;;;; +FD8C;ARABIC LIGATURE MEEM WITH JEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0645 062C 062D;;;;N;;;;; +FD8D;ARABIC LIGATURE MEEM WITH JEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0645 062C 0645;;;;N;;;;; +FD8E;ARABIC LIGATURE MEEM WITH KHAH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0645 062E 062C;;;;N;;;;; +FD8F;ARABIC LIGATURE MEEM WITH KHAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0645 062E 0645;;;;N;;;;; +FD92;ARABIC LIGATURE MEEM WITH JEEM WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0645 062C 062E;;;;N;;;;; +FD93;ARABIC LIGATURE HEH WITH MEEM WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0647 0645 062C;;;;N;;;;; +FD94;ARABIC LIGATURE HEH WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0647 0645 0645;;;;N;;;;; +FD95;ARABIC LIGATURE NOON WITH HAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0646 062D 0645;;;;N;;;;; +FD96;ARABIC LIGATURE NOON WITH HAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0646 062D 0649;;;;N;;;;; +FD97;ARABIC LIGATURE NOON WITH JEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0646 062C 0645;;;;N;;;;; +FD98;ARABIC LIGATURE NOON WITH JEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0646 062C 0645;;;;N;;;;; +FD99;ARABIC LIGATURE NOON WITH JEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0646 062C 0649;;;;N;;;;; +FD9A;ARABIC LIGATURE NOON WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0646 0645 064A;;;;N;;;;; +FD9B;ARABIC LIGATURE NOON WITH MEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0646 0645 0649;;;;N;;;;; +FD9C;ARABIC LIGATURE YEH WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 064A 0645 0645;;;;N;;;;; +FD9D;ARABIC LIGATURE YEH WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 064A 0645 0645;;;;N;;;;; +FD9E;ARABIC LIGATURE BEH WITH KHAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0628 062E 064A;;;;N;;;;; +FD9F;ARABIC LIGATURE TEH WITH JEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 062A 062C 064A;;;;N;;;;; +FDA0;ARABIC LIGATURE TEH WITH JEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062A 062C 0649;;;;N;;;;; +FDA1;ARABIC LIGATURE TEH WITH KHAH WITH YEH FINAL FORM;Lo;0;AL;<final> 062A 062E 064A;;;;N;;;;; +FDA2;ARABIC LIGATURE TEH WITH KHAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062A 062E 0649;;;;N;;;;; +FDA3;ARABIC LIGATURE TEH WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 062A 0645 064A;;;;N;;;;; +FDA4;ARABIC LIGATURE TEH WITH MEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062A 0645 0649;;;;N;;;;; +FDA5;ARABIC LIGATURE JEEM WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 062C 0645 064A;;;;N;;;;; +FDA6;ARABIC LIGATURE JEEM WITH HAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062C 062D 0649;;;;N;;;;; +FDA7;ARABIC LIGATURE JEEM WITH MEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062C 0645 0649;;;;N;;;;; +FDA8;ARABIC LIGATURE SEEN WITH KHAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0633 062E 0649;;;;N;;;;; +FDA9;ARABIC LIGATURE SAD WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0635 062D 064A;;;;N;;;;; +FDAA;ARABIC LIGATURE SHEEN WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0634 062D 064A;;;;N;;;;; +FDAB;ARABIC LIGATURE DAD WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0636 062D 064A;;;;N;;;;; +FDAC;ARABIC LIGATURE LAM WITH JEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0644 062C 064A;;;;N;;;;; +FDAD;ARABIC LIGATURE LAM WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0644 0645 064A;;;;N;;;;; +FDAE;ARABIC LIGATURE YEH WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 064A 062D 064A;;;;N;;;;; +FDAF;ARABIC LIGATURE YEH WITH JEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 064A 062C 064A;;;;N;;;;; +FDB0;ARABIC LIGATURE YEH WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 064A 0645 064A;;;;N;;;;; +FDB1;ARABIC LIGATURE MEEM WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0645 0645 064A;;;;N;;;;; +FDB2;ARABIC LIGATURE QAF WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0642 0645 064A;;;;N;;;;; +FDB3;ARABIC LIGATURE NOON WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0646 062D 064A;;;;N;;;;; +FDB4;ARABIC LIGATURE QAF WITH MEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0642 0645 062D;;;;N;;;;; +FDB5;ARABIC LIGATURE LAM WITH HAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0644 062D 0645;;;;N;;;;; +FDB6;ARABIC LIGATURE AIN WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0639 0645 064A;;;;N;;;;; +FDB7;ARABIC LIGATURE KAF WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0643 0645 064A;;;;N;;;;; +FDB8;ARABIC LIGATURE NOON WITH JEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0646 062C 062D;;;;N;;;;; +FDB9;ARABIC LIGATURE MEEM WITH KHAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0645 062E 064A;;;;N;;;;; +FDBA;ARABIC LIGATURE LAM WITH JEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0644 062C 0645;;;;N;;;;; +FDBB;ARABIC LIGATURE KAF WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0643 0645 0645;;;;N;;;;; +FDBC;ARABIC LIGATURE LAM WITH JEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0644 062C 0645;;;;N;;;;; +FDBD;ARABIC LIGATURE NOON WITH JEEM WITH HAH FINAL FORM;Lo;0;AL;<final> 0646 062C 062D;;;;N;;;;; +FDBE;ARABIC LIGATURE JEEM WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 062C 062D 064A;;;;N;;;;; +FDBF;ARABIC LIGATURE HAH WITH JEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 062D 062C 064A;;;;N;;;;; +FDC0;ARABIC LIGATURE MEEM WITH JEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0645 062C 064A;;;;N;;;;; +FDC1;ARABIC LIGATURE FEH WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0641 0645 064A;;;;N;;;;; +FDC2;ARABIC LIGATURE BEH WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0628 062D 064A;;;;N;;;;; +FDC3;ARABIC LIGATURE KAF WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0643 0645 0645;;;;N;;;;; +FDC4;ARABIC LIGATURE AIN WITH JEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0639 062C 0645;;;;N;;;;; +FDC5;ARABIC LIGATURE SAD WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0635 0645 0645;;;;N;;;;; +FDC6;ARABIC LIGATURE SEEN WITH KHAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0633 062E 064A;;;;N;;;;; +FDC7;ARABIC LIGATURE NOON WITH JEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0646 062C 064A;;;;N;;;;; +FDF0;ARABIC LIGATURE SALLA USED AS KORANIC STOP SIGN ISOLATED FORM;Lo;0;AL;<isolated> 0635 0644 06D2;;;;N;;;;; +FDF1;ARABIC LIGATURE QALA USED AS KORANIC STOP SIGN ISOLATED FORM;Lo;0;AL;<isolated> 0642 0644 06D2;;;;N;;;;; +FDF2;ARABIC LIGATURE ALLAH ISOLATED FORM;Lo;0;AL;<isolated> 0627 0644 0644 0647;;;;N;;;;; +FDF3;ARABIC LIGATURE AKBAR ISOLATED FORM;Lo;0;AL;<isolated> 0627 0643 0628 0631;;;;N;;;;; +FDF4;ARABIC LIGATURE MOHAMMAD ISOLATED FORM;Lo;0;AL;<isolated> 0645 062D 0645 062F;;;;N;;;;; +FDF5;ARABIC LIGATURE SALAM ISOLATED FORM;Lo;0;AL;<isolated> 0635 0644 0639 0645;;;;N;;;;; +FDF6;ARABIC LIGATURE RASOUL ISOLATED FORM;Lo;0;AL;<isolated> 0631 0633 0648 0644;;;;N;;;;; +FDF7;ARABIC LIGATURE ALAYHE ISOLATED FORM;Lo;0;AL;<isolated> 0639 0644 064A 0647;;;;N;;;;; +FDF8;ARABIC LIGATURE WASALLAM ISOLATED FORM;Lo;0;AL;<isolated> 0648 0633 0644 0645;;;;N;;;;; +FDF9;ARABIC LIGATURE SALLA ISOLATED FORM;Lo;0;AL;<isolated> 0635 0644 0649;;;;N;;;;; +FDFA;ARABIC LIGATURE SALLALLAHOU ALAYHE WASALLAM;Lo;0;AL;<isolated> 0635 0644 0649 0020 0627 0644 0644 0647 0020 0639 0644 064A 0647 0020 0648 0633 0644 0645;;;;N;ARABIC LETTER SALLALLAHOU ALAYHE WASALLAM;;;; +FDFB;ARABIC LIGATURE JALLAJALALOUHOU;Lo;0;AL;<isolated> 062C 0644 0020 062C 0644 0627 0644 0647;;;;N;ARABIC LETTER JALLAJALALOUHOU;;;; +FDFC;RIAL SIGN;Sc;0;AL;<isolated> 0631 06CC 0627 0644;;;;N;;;;; +FE00;VARIATION SELECTOR-1;Mn;0;NSM;;;;;N;;;;; +FE01;VARIATION SELECTOR-2;Mn;0;NSM;;;;;N;;;;; +FE02;VARIATION SELECTOR-3;Mn;0;NSM;;;;;N;;;;; +FE03;VARIATION SELECTOR-4;Mn;0;NSM;;;;;N;;;;; +FE04;VARIATION SELECTOR-5;Mn;0;NSM;;;;;N;;;;; +FE05;VARIATION SELECTOR-6;Mn;0;NSM;;;;;N;;;;; +FE06;VARIATION SELECTOR-7;Mn;0;NSM;;;;;N;;;;; +FE07;VARIATION SELECTOR-8;Mn;0;NSM;;;;;N;;;;; +FE08;VARIATION SELECTOR-9;Mn;0;NSM;;;;;N;;;;; +FE09;VARIATION SELECTOR-10;Mn;0;NSM;;;;;N;;;;; +FE0A;VARIATION SELECTOR-11;Mn;0;NSM;;;;;N;;;;; +FE0B;VARIATION SELECTOR-12;Mn;0;NSM;;;;;N;;;;; +FE0C;VARIATION SELECTOR-13;Mn;0;NSM;;;;;N;;;;; +FE0D;VARIATION SELECTOR-14;Mn;0;NSM;;;;;N;;;;; +FE0E;VARIATION SELECTOR-15;Mn;0;NSM;;;;;N;;;;; +FE0F;VARIATION SELECTOR-16;Mn;0;NSM;;;;;N;;;;; +FE20;COMBINING LIGATURE LEFT HALF;Mn;230;NSM;;;;;N;;;;; +FE21;COMBINING LIGATURE RIGHT HALF;Mn;230;NSM;;;;;N;;;;; +FE22;COMBINING DOUBLE TILDE LEFT HALF;Mn;230;NSM;;;;;N;;;;; +FE23;COMBINING DOUBLE TILDE RIGHT HALF;Mn;230;NSM;;;;;N;;;;; +FE30;PRESENTATION FORM FOR VERTICAL TWO DOT LEADER;Po;0;ON;<vertical> 2025;;;;N;GLYPH FOR VERTICAL TWO DOT LEADER;;;; +FE31;PRESENTATION FORM FOR VERTICAL EM DASH;Pd;0;ON;<vertical> 2014;;;;N;GLYPH FOR VERTICAL EM DASH;;;; +FE32;PRESENTATION FORM FOR VERTICAL EN DASH;Pd;0;ON;<vertical> 2013;;;;N;GLYPH FOR VERTICAL EN DASH;;;; +FE33;PRESENTATION FORM FOR VERTICAL LOW LINE;Pc;0;ON;<vertical> 005F;;;;N;GLYPH FOR VERTICAL SPACING UNDERSCORE;;;; +FE34;PRESENTATION FORM FOR VERTICAL WAVY LOW LINE;Pc;0;ON;<vertical> 005F;;;;N;GLYPH FOR VERTICAL SPACING WAVY UNDERSCORE;;;; +FE35;PRESENTATION FORM FOR VERTICAL LEFT PARENTHESIS;Ps;0;ON;<vertical> 0028;;;;N;GLYPH FOR VERTICAL OPENING PARENTHESIS;;;; +FE36;PRESENTATION FORM FOR VERTICAL RIGHT PARENTHESIS;Pe;0;ON;<vertical> 0029;;;;N;GLYPH FOR VERTICAL CLOSING PARENTHESIS;;;; +FE37;PRESENTATION FORM FOR VERTICAL LEFT CURLY BRACKET;Ps;0;ON;<vertical> 007B;;;;N;GLYPH FOR VERTICAL OPENING CURLY BRACKET;;;; +FE38;PRESENTATION FORM FOR VERTICAL RIGHT CURLY BRACKET;Pe;0;ON;<vertical> 007D;;;;N;GLYPH FOR VERTICAL CLOSING CURLY BRACKET;;;; +FE39;PRESENTATION FORM FOR VERTICAL LEFT TORTOISE SHELL BRACKET;Ps;0;ON;<vertical> 3014;;;;N;GLYPH FOR VERTICAL OPENING TORTOISE SHELL BRACKET;;;; +FE3A;PRESENTATION FORM FOR VERTICAL RIGHT TORTOISE SHELL BRACKET;Pe;0;ON;<vertical> 3015;;;;N;GLYPH FOR VERTICAL CLOSING TORTOISE SHELL BRACKET;;;; +FE3B;PRESENTATION FORM FOR VERTICAL LEFT BLACK LENTICULAR BRACKET;Ps;0;ON;<vertical> 3010;;;;N;GLYPH FOR VERTICAL OPENING BLACK LENTICULAR BRACKET;;;; +FE3C;PRESENTATION FORM FOR VERTICAL RIGHT BLACK LENTICULAR BRACKET;Pe;0;ON;<vertical> 3011;;;;N;GLYPH FOR VERTICAL CLOSING BLACK LENTICULAR BRACKET;;;; +FE3D;PRESENTATION FORM FOR VERTICAL LEFT DOUBLE ANGLE BRACKET;Ps;0;ON;<vertical> 300A;;;;N;GLYPH FOR VERTICAL OPENING DOUBLE ANGLE BRACKET;;;; +FE3E;PRESENTATION FORM FOR VERTICAL RIGHT DOUBLE ANGLE BRACKET;Pe;0;ON;<vertical> 300B;;;;N;GLYPH FOR VERTICAL CLOSING DOUBLE ANGLE BRACKET;;;; +FE3F;PRESENTATION FORM FOR VERTICAL LEFT ANGLE BRACKET;Ps;0;ON;<vertical> 3008;;;;N;GLYPH FOR VERTICAL OPENING ANGLE BRACKET;;;; +FE40;PRESENTATION FORM FOR VERTICAL RIGHT ANGLE BRACKET;Pe;0;ON;<vertical> 3009;;;;N;GLYPH FOR VERTICAL CLOSING ANGLE BRACKET;;;; +FE41;PRESENTATION FORM FOR VERTICAL LEFT CORNER BRACKET;Ps;0;ON;<vertical> 300C;;;;N;GLYPH FOR VERTICAL OPENING CORNER BRACKET;;;; +FE42;PRESENTATION FORM FOR VERTICAL RIGHT CORNER BRACKET;Pe;0;ON;<vertical> 300D;;;;N;GLYPH FOR VERTICAL CLOSING CORNER BRACKET;;;; +FE43;PRESENTATION FORM FOR VERTICAL LEFT WHITE CORNER BRACKET;Ps;0;ON;<vertical> 300E;;;;N;GLYPH FOR VERTICAL OPENING WHITE CORNER BRACKET;;;; +FE44;PRESENTATION FORM FOR VERTICAL RIGHT WHITE CORNER BRACKET;Pe;0;ON;<vertical> 300F;;;;N;GLYPH FOR VERTICAL CLOSING WHITE CORNER BRACKET;;;; +FE45;SESAME DOT;Po;0;ON;;;;;N;;;;; +FE46;WHITE SESAME DOT;Po;0;ON;;;;;N;;;;; +FE49;DASHED OVERLINE;Po;0;ON;<compat> 203E;;;;N;SPACING DASHED OVERSCORE;;;; +FE4A;CENTRELINE OVERLINE;Po;0;ON;<compat> 203E;;;;N;SPACING CENTERLINE OVERSCORE;;;; +FE4B;WAVY OVERLINE;Po;0;ON;<compat> 203E;;;;N;SPACING WAVY OVERSCORE;;;; +FE4C;DOUBLE WAVY OVERLINE;Po;0;ON;<compat> 203E;;;;N;SPACING DOUBLE WAVY OVERSCORE;;;; +FE4D;DASHED LOW LINE;Pc;0;ON;<compat> 005F;;;;N;SPACING DASHED UNDERSCORE;;;; +FE4E;CENTRELINE LOW LINE;Pc;0;ON;<compat> 005F;;;;N;SPACING CENTERLINE UNDERSCORE;;;; +FE4F;WAVY LOW LINE;Pc;0;ON;<compat> 005F;;;;N;SPACING WAVY UNDERSCORE;;;; +FE50;SMALL COMMA;Po;0;CS;<small> 002C;;;;N;;;;; +FE51;SMALL IDEOGRAPHIC COMMA;Po;0;ON;<small> 3001;;;;N;;;;; +FE52;SMALL FULL STOP;Po;0;CS;<small> 002E;;;;N;SMALL PERIOD;;;; +FE54;SMALL SEMICOLON;Po;0;ON;<small> 003B;;;;N;;;;; +FE55;SMALL COLON;Po;0;CS;<small> 003A;;;;N;;;;; +FE56;SMALL QUESTION MARK;Po;0;ON;<small> 003F;;;;N;;;;; +FE57;SMALL EXCLAMATION MARK;Po;0;ON;<small> 0021;;;;N;;;;; +FE58;SMALL EM DASH;Pd;0;ON;<small> 2014;;;;N;;;;; +FE59;SMALL LEFT PARENTHESIS;Ps;0;ON;<small> 0028;;;;N;SMALL OPENING PARENTHESIS;;;; +FE5A;SMALL RIGHT PARENTHESIS;Pe;0;ON;<small> 0029;;;;N;SMALL CLOSING PARENTHESIS;;;; +FE5B;SMALL LEFT CURLY BRACKET;Ps;0;ON;<small> 007B;;;;N;SMALL OPENING CURLY BRACKET;;;; +FE5C;SMALL RIGHT CURLY BRACKET;Pe;0;ON;<small> 007D;;;;N;SMALL CLOSING CURLY BRACKET;;;; +FE5D;SMALL LEFT TORTOISE SHELL BRACKET;Ps;0;ON;<small> 3014;;;;N;SMALL OPENING TORTOISE SHELL BRACKET;;;; +FE5E;SMALL RIGHT TORTOISE SHELL BRACKET;Pe;0;ON;<small> 3015;;;;N;SMALL CLOSING TORTOISE SHELL BRACKET;;;; +FE5F;SMALL NUMBER SIGN;Po;0;ET;<small> 0023;;;;N;;;;; +FE60;SMALL AMPERSAND;Po;0;ON;<small> 0026;;;;N;;;;; +FE61;SMALL ASTERISK;Po;0;ON;<small> 002A;;;;N;;;;; +FE62;SMALL PLUS SIGN;Sm;0;ET;<small> 002B;;;;N;;;;; +FE63;SMALL HYPHEN-MINUS;Pd;0;ET;<small> 002D;;;;N;;;;; +FE64;SMALL LESS-THAN SIGN;Sm;0;ON;<small> 003C;;;;N;;;;; +FE65;SMALL GREATER-THAN SIGN;Sm;0;ON;<small> 003E;;;;N;;;;; +FE66;SMALL EQUALS SIGN;Sm;0;ON;<small> 003D;;;;N;;;;; +FE68;SMALL REVERSE SOLIDUS;Po;0;ON;<small> 005C;;;;N;SMALL BACKSLASH;;;; +FE69;SMALL DOLLAR SIGN;Sc;0;ET;<small> 0024;;;;N;;;;; +FE6A;SMALL PERCENT SIGN;Po;0;ET;<small> 0025;;;;N;;;;; +FE6B;SMALL COMMERCIAL AT;Po;0;ON;<small> 0040;;;;N;;;;; +FE70;ARABIC FATHATAN ISOLATED FORM;Lo;0;AL;<isolated> 0020 064B;;;;N;ARABIC SPACING FATHATAN;;;; +FE71;ARABIC TATWEEL WITH FATHATAN ABOVE;Lo;0;AL;<medial> 0640 064B;;;;N;ARABIC FATHATAN ON TATWEEL;;;; +FE72;ARABIC DAMMATAN ISOLATED FORM;Lo;0;AL;<isolated> 0020 064C;;;;N;ARABIC SPACING DAMMATAN;;;; +FE73;ARABIC TAIL FRAGMENT;Lo;0;AL;;;;;N;;;;; +FE74;ARABIC KASRATAN ISOLATED FORM;Lo;0;AL;<isolated> 0020 064D;;;;N;ARABIC SPACING KASRATAN;;;; +FE76;ARABIC FATHA ISOLATED FORM;Lo;0;AL;<isolated> 0020 064E;;;;N;ARABIC SPACING FATHAH;;;; +FE77;ARABIC FATHA MEDIAL FORM;Lo;0;AL;<medial> 0640 064E;;;;N;ARABIC FATHAH ON TATWEEL;;;; +FE78;ARABIC DAMMA ISOLATED FORM;Lo;0;AL;<isolated> 0020 064F;;;;N;ARABIC SPACING DAMMAH;;;; +FE79;ARABIC DAMMA MEDIAL FORM;Lo;0;AL;<medial> 0640 064F;;;;N;ARABIC DAMMAH ON TATWEEL;;;; +FE7A;ARABIC KASRA ISOLATED FORM;Lo;0;AL;<isolated> 0020 0650;;;;N;ARABIC SPACING KASRAH;;;; +FE7B;ARABIC KASRA MEDIAL FORM;Lo;0;AL;<medial> 0640 0650;;;;N;ARABIC KASRAH ON TATWEEL;;;; +FE7C;ARABIC SHADDA ISOLATED FORM;Lo;0;AL;<isolated> 0020 0651;;;;N;ARABIC SPACING SHADDAH;;;; +FE7D;ARABIC SHADDA MEDIAL FORM;Lo;0;AL;<medial> 0640 0651;;;;N;ARABIC SHADDAH ON TATWEEL;;;; +FE7E;ARABIC SUKUN ISOLATED FORM;Lo;0;AL;<isolated> 0020 0652;;;;N;ARABIC SPACING SUKUN;;;; +FE7F;ARABIC SUKUN MEDIAL FORM;Lo;0;AL;<medial> 0640 0652;;;;N;ARABIC SUKUN ON TATWEEL;;;; +FE80;ARABIC LETTER HAMZA ISOLATED FORM;Lo;0;AL;<isolated> 0621;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH;;;; +FE81;ARABIC LETTER ALEF WITH MADDA ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 0622;;;;N;GLYPH FOR ISOLATE ARABIC MADDAH ON ALEF;;;; +FE82;ARABIC LETTER ALEF WITH MADDA ABOVE FINAL FORM;Lo;0;AL;<final> 0622;;;;N;GLYPH FOR FINAL ARABIC MADDAH ON ALEF;;;; +FE83;ARABIC LETTER ALEF WITH HAMZA ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 0623;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH ON ALEF;;;; +FE84;ARABIC LETTER ALEF WITH HAMZA ABOVE FINAL FORM;Lo;0;AL;<final> 0623;;;;N;GLYPH FOR FINAL ARABIC HAMZAH ON ALEF;;;; +FE85;ARABIC LETTER WAW WITH HAMZA ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 0624;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH ON WAW;;;; +FE86;ARABIC LETTER WAW WITH HAMZA ABOVE FINAL FORM;Lo;0;AL;<final> 0624;;;;N;GLYPH FOR FINAL ARABIC HAMZAH ON WAW;;;; +FE87;ARABIC LETTER ALEF WITH HAMZA BELOW ISOLATED FORM;Lo;0;AL;<isolated> 0625;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH UNDER ALEF;;;; +FE88;ARABIC LETTER ALEF WITH HAMZA BELOW FINAL FORM;Lo;0;AL;<final> 0625;;;;N;GLYPH FOR FINAL ARABIC HAMZAH UNDER ALEF;;;; +FE89;ARABIC LETTER YEH WITH HAMZA ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 0626;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH ON YA;;;; +FE8A;ARABIC LETTER YEH WITH HAMZA ABOVE FINAL FORM;Lo;0;AL;<final> 0626;;;;N;GLYPH FOR FINAL ARABIC HAMZAH ON YA;;;; +FE8B;ARABIC LETTER YEH WITH HAMZA ABOVE INITIAL FORM;Lo;0;AL;<initial> 0626;;;;N;GLYPH FOR INITIAL ARABIC HAMZAH ON YA;;;; +FE8C;ARABIC LETTER YEH WITH HAMZA ABOVE MEDIAL FORM;Lo;0;AL;<medial> 0626;;;;N;GLYPH FOR MEDIAL ARABIC HAMZAH ON YA;;;; +FE8D;ARABIC LETTER ALEF ISOLATED FORM;Lo;0;AL;<isolated> 0627;;;;N;GLYPH FOR ISOLATE ARABIC ALEF;;;; +FE8E;ARABIC LETTER ALEF FINAL FORM;Lo;0;AL;<final> 0627;;;;N;GLYPH FOR FINAL ARABIC ALEF;;;; +FE8F;ARABIC LETTER BEH ISOLATED FORM;Lo;0;AL;<isolated> 0628;;;;N;GLYPH FOR ISOLATE ARABIC BAA;;;; +FE90;ARABIC LETTER BEH FINAL FORM;Lo;0;AL;<final> 0628;;;;N;GLYPH FOR FINAL ARABIC BAA;;;; +FE91;ARABIC LETTER BEH INITIAL FORM;Lo;0;AL;<initial> 0628;;;;N;GLYPH FOR INITIAL ARABIC BAA;;;; +FE92;ARABIC LETTER BEH MEDIAL FORM;Lo;0;AL;<medial> 0628;;;;N;GLYPH FOR MEDIAL ARABIC BAA;;;; +FE93;ARABIC LETTER TEH MARBUTA ISOLATED FORM;Lo;0;AL;<isolated> 0629;;;;N;GLYPH FOR ISOLATE ARABIC TAA MARBUTAH;;;; +FE94;ARABIC LETTER TEH MARBUTA FINAL FORM;Lo;0;AL;<final> 0629;;;;N;GLYPH FOR FINAL ARABIC TAA MARBUTAH;;;; +FE95;ARABIC LETTER TEH ISOLATED FORM;Lo;0;AL;<isolated> 062A;;;;N;GLYPH FOR ISOLATE ARABIC TAA;;;; +FE96;ARABIC LETTER TEH FINAL FORM;Lo;0;AL;<final> 062A;;;;N;GLYPH FOR FINAL ARABIC TAA;;;; +FE97;ARABIC LETTER TEH INITIAL FORM;Lo;0;AL;<initial> 062A;;;;N;GLYPH FOR INITIAL ARABIC TAA;;;; +FE98;ARABIC LETTER TEH MEDIAL FORM;Lo;0;AL;<medial> 062A;;;;N;GLYPH FOR MEDIAL ARABIC TAA;;;; +FE99;ARABIC LETTER THEH ISOLATED FORM;Lo;0;AL;<isolated> 062B;;;;N;GLYPH FOR ISOLATE ARABIC THAA;;;; +FE9A;ARABIC LETTER THEH FINAL FORM;Lo;0;AL;<final> 062B;;;;N;GLYPH FOR FINAL ARABIC THAA;;;; +FE9B;ARABIC LETTER THEH INITIAL FORM;Lo;0;AL;<initial> 062B;;;;N;GLYPH FOR INITIAL ARABIC THAA;;;; +FE9C;ARABIC LETTER THEH MEDIAL FORM;Lo;0;AL;<medial> 062B;;;;N;GLYPH FOR MEDIAL ARABIC THAA;;;; +FE9D;ARABIC LETTER JEEM ISOLATED FORM;Lo;0;AL;<isolated> 062C;;;;N;GLYPH FOR ISOLATE ARABIC JEEM;;;; +FE9E;ARABIC LETTER JEEM FINAL FORM;Lo;0;AL;<final> 062C;;;;N;GLYPH FOR FINAL ARABIC JEEM;;;; +FE9F;ARABIC LETTER JEEM INITIAL FORM;Lo;0;AL;<initial> 062C;;;;N;GLYPH FOR INITIAL ARABIC JEEM;;;; +FEA0;ARABIC LETTER JEEM MEDIAL FORM;Lo;0;AL;<medial> 062C;;;;N;GLYPH FOR MEDIAL ARABIC JEEM;;;; +FEA1;ARABIC LETTER HAH ISOLATED FORM;Lo;0;AL;<isolated> 062D;;;;N;GLYPH FOR ISOLATE ARABIC HAA;;;; +FEA2;ARABIC LETTER HAH FINAL FORM;Lo;0;AL;<final> 062D;;;;N;GLYPH FOR FINAL ARABIC HAA;;;; +FEA3;ARABIC LETTER HAH INITIAL FORM;Lo;0;AL;<initial> 062D;;;;N;GLYPH FOR INITIAL ARABIC HAA;;;; +FEA4;ARABIC LETTER HAH MEDIAL FORM;Lo;0;AL;<medial> 062D;;;;N;GLYPH FOR MEDIAL ARABIC HAA;;;; +FEA5;ARABIC LETTER KHAH ISOLATED FORM;Lo;0;AL;<isolated> 062E;;;;N;GLYPH FOR ISOLATE ARABIC KHAA;;;; +FEA6;ARABIC LETTER KHAH FINAL FORM;Lo;0;AL;<final> 062E;;;;N;GLYPH FOR FINAL ARABIC KHAA;;;; +FEA7;ARABIC LETTER KHAH INITIAL FORM;Lo;0;AL;<initial> 062E;;;;N;GLYPH FOR INITIAL ARABIC KHAA;;;; +FEA8;ARABIC LETTER KHAH MEDIAL FORM;Lo;0;AL;<medial> 062E;;;;N;GLYPH FOR MEDIAL ARABIC KHAA;;;; +FEA9;ARABIC LETTER DAL ISOLATED FORM;Lo;0;AL;<isolated> 062F;;;;N;GLYPH FOR ISOLATE ARABIC DAL;;;; +FEAA;ARABIC LETTER DAL FINAL FORM;Lo;0;AL;<final> 062F;;;;N;GLYPH FOR FINAL ARABIC DAL;;;; +FEAB;ARABIC LETTER THAL ISOLATED FORM;Lo;0;AL;<isolated> 0630;;;;N;GLYPH FOR ISOLATE ARABIC THAL;;;; +FEAC;ARABIC LETTER THAL FINAL FORM;Lo;0;AL;<final> 0630;;;;N;GLYPH FOR FINAL ARABIC THAL;;;; +FEAD;ARABIC LETTER REH ISOLATED FORM;Lo;0;AL;<isolated> 0631;;;;N;GLYPH FOR ISOLATE ARABIC RA;;;; +FEAE;ARABIC LETTER REH FINAL FORM;Lo;0;AL;<final> 0631;;;;N;GLYPH FOR FINAL ARABIC RA;;;; +FEAF;ARABIC LETTER ZAIN ISOLATED FORM;Lo;0;AL;<isolated> 0632;;;;N;GLYPH FOR ISOLATE ARABIC ZAIN;;;; +FEB0;ARABIC LETTER ZAIN FINAL FORM;Lo;0;AL;<final> 0632;;;;N;GLYPH FOR FINAL ARABIC ZAIN;;;; +FEB1;ARABIC LETTER SEEN ISOLATED FORM;Lo;0;AL;<isolated> 0633;;;;N;GLYPH FOR ISOLATE ARABIC SEEN;;;; +FEB2;ARABIC LETTER SEEN FINAL FORM;Lo;0;AL;<final> 0633;;;;N;GLYPH FOR FINAL ARABIC SEEN;;;; +FEB3;ARABIC LETTER SEEN INITIAL FORM;Lo;0;AL;<initial> 0633;;;;N;GLYPH FOR INITIAL ARABIC SEEN;;;; +FEB4;ARABIC LETTER SEEN MEDIAL FORM;Lo;0;AL;<medial> 0633;;;;N;GLYPH FOR MEDIAL ARABIC SEEN;;;; +FEB5;ARABIC LETTER SHEEN ISOLATED FORM;Lo;0;AL;<isolated> 0634;;;;N;GLYPH FOR ISOLATE ARABIC SHEEN;;;; +FEB6;ARABIC LETTER SHEEN FINAL FORM;Lo;0;AL;<final> 0634;;;;N;GLYPH FOR FINAL ARABIC SHEEN;;;; +FEB7;ARABIC LETTER SHEEN INITIAL FORM;Lo;0;AL;<initial> 0634;;;;N;GLYPH FOR INITIAL ARABIC SHEEN;;;; +FEB8;ARABIC LETTER SHEEN MEDIAL FORM;Lo;0;AL;<medial> 0634;;;;N;GLYPH FOR MEDIAL ARABIC SHEEN;;;; +FEB9;ARABIC LETTER SAD ISOLATED FORM;Lo;0;AL;<isolated> 0635;;;;N;GLYPH FOR ISOLATE ARABIC SAD;;;; +FEBA;ARABIC LETTER SAD FINAL FORM;Lo;0;AL;<final> 0635;;;;N;GLYPH FOR FINAL ARABIC SAD;;;; +FEBB;ARABIC LETTER SAD INITIAL FORM;Lo;0;AL;<initial> 0635;;;;N;GLYPH FOR INITIAL ARABIC SAD;;;; +FEBC;ARABIC LETTER SAD MEDIAL FORM;Lo;0;AL;<medial> 0635;;;;N;GLYPH FOR MEDIAL ARABIC SAD;;;; +FEBD;ARABIC LETTER DAD ISOLATED FORM;Lo;0;AL;<isolated> 0636;;;;N;GLYPH FOR ISOLATE ARABIC DAD;;;; +FEBE;ARABIC LETTER DAD FINAL FORM;Lo;0;AL;<final> 0636;;;;N;GLYPH FOR FINAL ARABIC DAD;;;; +FEBF;ARABIC LETTER DAD INITIAL FORM;Lo;0;AL;<initial> 0636;;;;N;GLYPH FOR INITIAL ARABIC DAD;;;; +FEC0;ARABIC LETTER DAD MEDIAL FORM;Lo;0;AL;<medial> 0636;;;;N;GLYPH FOR MEDIAL ARABIC DAD;;;; +FEC1;ARABIC LETTER TAH ISOLATED FORM;Lo;0;AL;<isolated> 0637;;;;N;GLYPH FOR ISOLATE ARABIC TAH;;;; +FEC2;ARABIC LETTER TAH FINAL FORM;Lo;0;AL;<final> 0637;;;;N;GLYPH FOR FINAL ARABIC TAH;;;; +FEC3;ARABIC LETTER TAH INITIAL FORM;Lo;0;AL;<initial> 0637;;;;N;GLYPH FOR INITIAL ARABIC TAH;;;; +FEC4;ARABIC LETTER TAH MEDIAL FORM;Lo;0;AL;<medial> 0637;;;;N;GLYPH FOR MEDIAL ARABIC TAH;;;; +FEC5;ARABIC LETTER ZAH ISOLATED FORM;Lo;0;AL;<isolated> 0638;;;;N;GLYPH FOR ISOLATE ARABIC DHAH;;;; +FEC6;ARABIC LETTER ZAH FINAL FORM;Lo;0;AL;<final> 0638;;;;N;GLYPH FOR FINAL ARABIC DHAH;;;; +FEC7;ARABIC LETTER ZAH INITIAL FORM;Lo;0;AL;<initial> 0638;;;;N;GLYPH FOR INITIAL ARABIC DHAH;;;; +FEC8;ARABIC LETTER ZAH MEDIAL FORM;Lo;0;AL;<medial> 0638;;;;N;GLYPH FOR MEDIAL ARABIC DHAH;;;; +FEC9;ARABIC LETTER AIN ISOLATED FORM;Lo;0;AL;<isolated> 0639;;;;N;GLYPH FOR ISOLATE ARABIC AIN;;;; +FECA;ARABIC LETTER AIN FINAL FORM;Lo;0;AL;<final> 0639;;;;N;GLYPH FOR FINAL ARABIC AIN;;;; +FECB;ARABIC LETTER AIN INITIAL FORM;Lo;0;AL;<initial> 0639;;;;N;GLYPH FOR INITIAL ARABIC AIN;;;; +FECC;ARABIC LETTER AIN MEDIAL FORM;Lo;0;AL;<medial> 0639;;;;N;GLYPH FOR MEDIAL ARABIC AIN;;;; +FECD;ARABIC LETTER GHAIN ISOLATED FORM;Lo;0;AL;<isolated> 063A;;;;N;GLYPH FOR ISOLATE ARABIC GHAIN;;;; +FECE;ARABIC LETTER GHAIN FINAL FORM;Lo;0;AL;<final> 063A;;;;N;GLYPH FOR FINAL ARABIC GHAIN;;;; +FECF;ARABIC LETTER GHAIN INITIAL FORM;Lo;0;AL;<initial> 063A;;;;N;GLYPH FOR INITIAL ARABIC GHAIN;;;; +FED0;ARABIC LETTER GHAIN MEDIAL FORM;Lo;0;AL;<medial> 063A;;;;N;GLYPH FOR MEDIAL ARABIC GHAIN;;;; +FED1;ARABIC LETTER FEH ISOLATED FORM;Lo;0;AL;<isolated> 0641;;;;N;GLYPH FOR ISOLATE ARABIC FA;;;; +FED2;ARABIC LETTER FEH FINAL FORM;Lo;0;AL;<final> 0641;;;;N;GLYPH FOR FINAL ARABIC FA;;;; +FED3;ARABIC LETTER FEH INITIAL FORM;Lo;0;AL;<initial> 0641;;;;N;GLYPH FOR INITIAL ARABIC FA;;;; +FED4;ARABIC LETTER FEH MEDIAL FORM;Lo;0;AL;<medial> 0641;;;;N;GLYPH FOR MEDIAL ARABIC FA;;;; +FED5;ARABIC LETTER QAF ISOLATED FORM;Lo;0;AL;<isolated> 0642;;;;N;GLYPH FOR ISOLATE ARABIC QAF;;;; +FED6;ARABIC LETTER QAF FINAL FORM;Lo;0;AL;<final> 0642;;;;N;GLYPH FOR FINAL ARABIC QAF;;;; +FED7;ARABIC LETTER QAF INITIAL FORM;Lo;0;AL;<initial> 0642;;;;N;GLYPH FOR INITIAL ARABIC QAF;;;; +FED8;ARABIC LETTER QAF MEDIAL FORM;Lo;0;AL;<medial> 0642;;;;N;GLYPH FOR MEDIAL ARABIC QAF;;;; +FED9;ARABIC LETTER KAF ISOLATED FORM;Lo;0;AL;<isolated> 0643;;;;N;GLYPH FOR ISOLATE ARABIC CAF;;;; +FEDA;ARABIC LETTER KAF FINAL FORM;Lo;0;AL;<final> 0643;;;;N;GLYPH FOR FINAL ARABIC CAF;;;; +FEDB;ARABIC LETTER KAF INITIAL FORM;Lo;0;AL;<initial> 0643;;;;N;GLYPH FOR INITIAL ARABIC CAF;;;; +FEDC;ARABIC LETTER KAF MEDIAL FORM;Lo;0;AL;<medial> 0643;;;;N;GLYPH FOR MEDIAL ARABIC CAF;;;; +FEDD;ARABIC LETTER LAM ISOLATED FORM;Lo;0;AL;<isolated> 0644;;;;N;GLYPH FOR ISOLATE ARABIC LAM;;;; +FEDE;ARABIC LETTER LAM FINAL FORM;Lo;0;AL;<final> 0644;;;;N;GLYPH FOR FINAL ARABIC LAM;;;; +FEDF;ARABIC LETTER LAM INITIAL FORM;Lo;0;AL;<initial> 0644;;;;N;GLYPH FOR INITIAL ARABIC LAM;;;; +FEE0;ARABIC LETTER LAM MEDIAL FORM;Lo;0;AL;<medial> 0644;;;;N;GLYPH FOR MEDIAL ARABIC LAM;;;; +FEE1;ARABIC LETTER MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0645;;;;N;GLYPH FOR ISOLATE ARABIC MEEM;;;; +FEE2;ARABIC LETTER MEEM FINAL FORM;Lo;0;AL;<final> 0645;;;;N;GLYPH FOR FINAL ARABIC MEEM;;;; +FEE3;ARABIC LETTER MEEM INITIAL FORM;Lo;0;AL;<initial> 0645;;;;N;GLYPH FOR INITIAL ARABIC MEEM;;;; +FEE4;ARABIC LETTER MEEM MEDIAL FORM;Lo;0;AL;<medial> 0645;;;;N;GLYPH FOR MEDIAL ARABIC MEEM;;;; +FEE5;ARABIC LETTER NOON ISOLATED FORM;Lo;0;AL;<isolated> 0646;;;;N;GLYPH FOR ISOLATE ARABIC NOON;;;; +FEE6;ARABIC LETTER NOON FINAL FORM;Lo;0;AL;<final> 0646;;;;N;GLYPH FOR FINAL ARABIC NOON;;;; +FEE7;ARABIC LETTER NOON INITIAL FORM;Lo;0;AL;<initial> 0646;;;;N;GLYPH FOR INITIAL ARABIC NOON;;;; +FEE8;ARABIC LETTER NOON MEDIAL FORM;Lo;0;AL;<medial> 0646;;;;N;GLYPH FOR MEDIAL ARABIC NOON;;;; +FEE9;ARABIC LETTER HEH ISOLATED FORM;Lo;0;AL;<isolated> 0647;;;;N;GLYPH FOR ISOLATE ARABIC HA;;;; +FEEA;ARABIC LETTER HEH FINAL FORM;Lo;0;AL;<final> 0647;;;;N;GLYPH FOR FINAL ARABIC HA;;;; +FEEB;ARABIC LETTER HEH INITIAL FORM;Lo;0;AL;<initial> 0647;;;;N;GLYPH FOR INITIAL ARABIC HA;;;; +FEEC;ARABIC LETTER HEH MEDIAL FORM;Lo;0;AL;<medial> 0647;;;;N;GLYPH FOR MEDIAL ARABIC HA;;;; +FEED;ARABIC LETTER WAW ISOLATED FORM;Lo;0;AL;<isolated> 0648;;;;N;GLYPH FOR ISOLATE ARABIC WAW;;;; +FEEE;ARABIC LETTER WAW FINAL FORM;Lo;0;AL;<final> 0648;;;;N;GLYPH FOR FINAL ARABIC WAW;;;; +FEEF;ARABIC LETTER ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0649;;;;N;GLYPH FOR ISOLATE ARABIC ALEF MAQSURAH;;;; +FEF0;ARABIC LETTER ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0649;;;;N;GLYPH FOR FINAL ARABIC ALEF MAQSURAH;;;; +FEF1;ARABIC LETTER YEH ISOLATED FORM;Lo;0;AL;<isolated> 064A;;;;N;GLYPH FOR ISOLATE ARABIC YA;;;; +FEF2;ARABIC LETTER YEH FINAL FORM;Lo;0;AL;<final> 064A;;;;N;GLYPH FOR FINAL ARABIC YA;;;; +FEF3;ARABIC LETTER YEH INITIAL FORM;Lo;0;AL;<initial> 064A;;;;N;GLYPH FOR INITIAL ARABIC YA;;;; +FEF4;ARABIC LETTER YEH MEDIAL FORM;Lo;0;AL;<medial> 064A;;;;N;GLYPH FOR MEDIAL ARABIC YA;;;; +FEF5;ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 0644 0622;;;;N;GLYPH FOR ISOLATE ARABIC MADDAH ON LIGATURE LAM ALEF;;;; +FEF6;ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE FINAL FORM;Lo;0;AL;<final> 0644 0622;;;;N;GLYPH FOR FINAL ARABIC MADDAH ON LIGATURE LAM ALEF;;;; +FEF7;ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 0644 0623;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH ON LIGATURE LAM ALEF;;;; +FEF8;ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE FINAL FORM;Lo;0;AL;<final> 0644 0623;;;;N;GLYPH FOR FINAL ARABIC HAMZAH ON LIGATURE LAM ALEF;;;; +FEF9;ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW ISOLATED FORM;Lo;0;AL;<isolated> 0644 0625;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH UNDER LIGATURE LAM ALEF;;;; +FEFA;ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW FINAL FORM;Lo;0;AL;<final> 0644 0625;;;;N;GLYPH FOR FINAL ARABIC HAMZAH UNDER LIGATURE LAM ALEF;;;; +FEFB;ARABIC LIGATURE LAM WITH ALEF ISOLATED FORM;Lo;0;AL;<isolated> 0644 0627;;;;N;GLYPH FOR ISOLATE ARABIC LIGATURE LAM ALEF;;;; +FEFC;ARABIC LIGATURE LAM WITH ALEF FINAL FORM;Lo;0;AL;<final> 0644 0627;;;;N;GLYPH FOR FINAL ARABIC LIGATURE LAM ALEF;;;; +FEFF;ZERO WIDTH NO-BREAK SPACE;Cf;0;BN;;;;;N;BYTE ORDER MARK;;;; +FF01;FULLWIDTH EXCLAMATION MARK;Po;0;ON;<wide> 0021;;;;N;;;;; +FF02;FULLWIDTH QUOTATION MARK;Po;0;ON;<wide> 0022;;;;N;;;;; +FF03;FULLWIDTH NUMBER SIGN;Po;0;ET;<wide> 0023;;;;N;;;;; +FF04;FULLWIDTH DOLLAR SIGN;Sc;0;ET;<wide> 0024;;;;N;;;;; +FF05;FULLWIDTH PERCENT SIGN;Po;0;ET;<wide> 0025;;;;N;;;;; +FF06;FULLWIDTH AMPERSAND;Po;0;ON;<wide> 0026;;;;N;;;;; +FF07;FULLWIDTH APOSTROPHE;Po;0;ON;<wide> 0027;;;;N;;;;; +FF08;FULLWIDTH LEFT PARENTHESIS;Ps;0;ON;<wide> 0028;;;;Y;FULLWIDTH OPENING PARENTHESIS;;;; +FF09;FULLWIDTH RIGHT PARENTHESIS;Pe;0;ON;<wide> 0029;;;;Y;FULLWIDTH CLOSING PARENTHESIS;;;; +FF0A;FULLWIDTH ASTERISK;Po;0;ON;<wide> 002A;;;;N;;;;; +FF0B;FULLWIDTH PLUS SIGN;Sm;0;ET;<wide> 002B;;;;N;;;;; +FF0C;FULLWIDTH COMMA;Po;0;CS;<wide> 002C;;;;N;;;;; +FF0D;FULLWIDTH HYPHEN-MINUS;Pd;0;ET;<wide> 002D;;;;N;;;;; +FF0E;FULLWIDTH FULL STOP;Po;0;CS;<wide> 002E;;;;N;FULLWIDTH PERIOD;;;; +FF0F;FULLWIDTH SOLIDUS;Po;0;ES;<wide> 002F;;;;N;FULLWIDTH SLASH;;;; +FF10;FULLWIDTH DIGIT ZERO;Nd;0;EN;<wide> 0030;0;0;0;N;;;;; +FF11;FULLWIDTH DIGIT ONE;Nd;0;EN;<wide> 0031;1;1;1;N;;;;; +FF12;FULLWIDTH DIGIT TWO;Nd;0;EN;<wide> 0032;2;2;2;N;;;;; +FF13;FULLWIDTH DIGIT THREE;Nd;0;EN;<wide> 0033;3;3;3;N;;;;; +FF14;FULLWIDTH DIGIT FOUR;Nd;0;EN;<wide> 0034;4;4;4;N;;;;; +FF15;FULLWIDTH DIGIT FIVE;Nd;0;EN;<wide> 0035;5;5;5;N;;;;; +FF16;FULLWIDTH DIGIT SIX;Nd;0;EN;<wide> 0036;6;6;6;N;;;;; +FF17;FULLWIDTH DIGIT SEVEN;Nd;0;EN;<wide> 0037;7;7;7;N;;;;; +FF18;FULLWIDTH DIGIT EIGHT;Nd;0;EN;<wide> 0038;8;8;8;N;;;;; +FF19;FULLWIDTH DIGIT NINE;Nd;0;EN;<wide> 0039;9;9;9;N;;;;; +FF1A;FULLWIDTH COLON;Po;0;CS;<wide> 003A;;;;N;;;;; +FF1B;FULLWIDTH SEMICOLON;Po;0;ON;<wide> 003B;;;;N;;;;; +FF1C;FULLWIDTH LESS-THAN SIGN;Sm;0;ON;<wide> 003C;;;;Y;;;;; +FF1D;FULLWIDTH EQUALS SIGN;Sm;0;ON;<wide> 003D;;;;N;;;;; +FF1E;FULLWIDTH GREATER-THAN SIGN;Sm;0;ON;<wide> 003E;;;;Y;;;;; +FF1F;FULLWIDTH QUESTION MARK;Po;0;ON;<wide> 003F;;;;N;;;;; +FF20;FULLWIDTH COMMERCIAL AT;Po;0;ON;<wide> 0040;;;;N;;;;; +FF21;FULLWIDTH LATIN CAPITAL LETTER A;Lu;0;L;<wide> 0041;;;;N;;;;FF41; +FF22;FULLWIDTH LATIN CAPITAL LETTER B;Lu;0;L;<wide> 0042;;;;N;;;;FF42; +FF23;FULLWIDTH LATIN CAPITAL LETTER C;Lu;0;L;<wide> 0043;;;;N;;;;FF43; +FF24;FULLWIDTH LATIN CAPITAL LETTER D;Lu;0;L;<wide> 0044;;;;N;;;;FF44; +FF25;FULLWIDTH LATIN CAPITAL LETTER E;Lu;0;L;<wide> 0045;;;;N;;;;FF45; +FF26;FULLWIDTH LATIN CAPITAL LETTER F;Lu;0;L;<wide> 0046;;;;N;;;;FF46; +FF27;FULLWIDTH LATIN CAPITAL LETTER G;Lu;0;L;<wide> 0047;;;;N;;;;FF47; +FF28;FULLWIDTH LATIN CAPITAL LETTER H;Lu;0;L;<wide> 0048;;;;N;;;;FF48; +FF29;FULLWIDTH LATIN CAPITAL LETTER I;Lu;0;L;<wide> 0049;;;;N;;;;FF49; +FF2A;FULLWIDTH LATIN CAPITAL LETTER J;Lu;0;L;<wide> 004A;;;;N;;;;FF4A; +FF2B;FULLWIDTH LATIN CAPITAL LETTER K;Lu;0;L;<wide> 004B;;;;N;;;;FF4B; +FF2C;FULLWIDTH LATIN CAPITAL LETTER L;Lu;0;L;<wide> 004C;;;;N;;;;FF4C; +FF2D;FULLWIDTH LATIN CAPITAL LETTER M;Lu;0;L;<wide> 004D;;;;N;;;;FF4D; +FF2E;FULLWIDTH LATIN CAPITAL LETTER N;Lu;0;L;<wide> 004E;;;;N;;;;FF4E; +FF2F;FULLWIDTH LATIN CAPITAL LETTER O;Lu;0;L;<wide> 004F;;;;N;;;;FF4F; +FF30;FULLWIDTH LATIN CAPITAL LETTER P;Lu;0;L;<wide> 0050;;;;N;;;;FF50; +FF31;FULLWIDTH LATIN CAPITAL LETTER Q;Lu;0;L;<wide> 0051;;;;N;;;;FF51; +FF32;FULLWIDTH LATIN CAPITAL LETTER R;Lu;0;L;<wide> 0052;;;;N;;;;FF52; +FF33;FULLWIDTH LATIN CAPITAL LETTER S;Lu;0;L;<wide> 0053;;;;N;;;;FF53; +FF34;FULLWIDTH LATIN CAPITAL LETTER T;Lu;0;L;<wide> 0054;;;;N;;;;FF54; +FF35;FULLWIDTH LATIN CAPITAL LETTER U;Lu;0;L;<wide> 0055;;;;N;;;;FF55; +FF36;FULLWIDTH LATIN CAPITAL LETTER V;Lu;0;L;<wide> 0056;;;;N;;;;FF56; +FF37;FULLWIDTH LATIN CAPITAL LETTER W;Lu;0;L;<wide> 0057;;;;N;;;;FF57; +FF38;FULLWIDTH LATIN CAPITAL LETTER X;Lu;0;L;<wide> 0058;;;;N;;;;FF58; +FF39;FULLWIDTH LATIN CAPITAL LETTER Y;Lu;0;L;<wide> 0059;;;;N;;;;FF59; +FF3A;FULLWIDTH LATIN CAPITAL LETTER Z;Lu;0;L;<wide> 005A;;;;N;;;;FF5A; +FF3B;FULLWIDTH LEFT SQUARE BRACKET;Ps;0;ON;<wide> 005B;;;;Y;FULLWIDTH OPENING SQUARE BRACKET;;;; +FF3C;FULLWIDTH REVERSE SOLIDUS;Po;0;ON;<wide> 005C;;;;N;FULLWIDTH BACKSLASH;;;; +FF3D;FULLWIDTH RIGHT SQUARE BRACKET;Pe;0;ON;<wide> 005D;;;;Y;FULLWIDTH CLOSING SQUARE BRACKET;;;; +FF3E;FULLWIDTH CIRCUMFLEX ACCENT;Sk;0;ON;<wide> 005E;;;;N;FULLWIDTH SPACING CIRCUMFLEX;;;; +FF3F;FULLWIDTH LOW LINE;Pc;0;ON;<wide> 005F;;;;N;FULLWIDTH SPACING UNDERSCORE;;;; +FF40;FULLWIDTH GRAVE ACCENT;Sk;0;ON;<wide> 0060;;;;N;FULLWIDTH SPACING GRAVE;;;; +FF41;FULLWIDTH LATIN SMALL LETTER A;Ll;0;L;<wide> 0061;;;;N;;;FF21;;FF21 +FF42;FULLWIDTH LATIN SMALL LETTER B;Ll;0;L;<wide> 0062;;;;N;;;FF22;;FF22 +FF43;FULLWIDTH LATIN SMALL LETTER C;Ll;0;L;<wide> 0063;;;;N;;;FF23;;FF23 +FF44;FULLWIDTH LATIN SMALL LETTER D;Ll;0;L;<wide> 0064;;;;N;;;FF24;;FF24 +FF45;FULLWIDTH LATIN SMALL LETTER E;Ll;0;L;<wide> 0065;;;;N;;;FF25;;FF25 +FF46;FULLWIDTH LATIN SMALL LETTER F;Ll;0;L;<wide> 0066;;;;N;;;FF26;;FF26 +FF47;FULLWIDTH LATIN SMALL LETTER G;Ll;0;L;<wide> 0067;;;;N;;;FF27;;FF27 +FF48;FULLWIDTH LATIN SMALL LETTER H;Ll;0;L;<wide> 0068;;;;N;;;FF28;;FF28 +FF49;FULLWIDTH LATIN SMALL LETTER I;Ll;0;L;<wide> 0069;;;;N;;;FF29;;FF29 +FF4A;FULLWIDTH LATIN SMALL LETTER J;Ll;0;L;<wide> 006A;;;;N;;;FF2A;;FF2A +FF4B;FULLWIDTH LATIN SMALL LETTER K;Ll;0;L;<wide> 006B;;;;N;;;FF2B;;FF2B +FF4C;FULLWIDTH LATIN SMALL LETTER L;Ll;0;L;<wide> 006C;;;;N;;;FF2C;;FF2C +FF4D;FULLWIDTH LATIN SMALL LETTER M;Ll;0;L;<wide> 006D;;;;N;;;FF2D;;FF2D +FF4E;FULLWIDTH LATIN SMALL LETTER N;Ll;0;L;<wide> 006E;;;;N;;;FF2E;;FF2E +FF4F;FULLWIDTH LATIN SMALL LETTER O;Ll;0;L;<wide> 006F;;;;N;;;FF2F;;FF2F +FF50;FULLWIDTH LATIN SMALL LETTER P;Ll;0;L;<wide> 0070;;;;N;;;FF30;;FF30 +FF51;FULLWIDTH LATIN SMALL LETTER Q;Ll;0;L;<wide> 0071;;;;N;;;FF31;;FF31 +FF52;FULLWIDTH LATIN SMALL LETTER R;Ll;0;L;<wide> 0072;;;;N;;;FF32;;FF32 +FF53;FULLWIDTH LATIN SMALL LETTER S;Ll;0;L;<wide> 0073;;;;N;;;FF33;;FF33 +FF54;FULLWIDTH LATIN SMALL LETTER T;Ll;0;L;<wide> 0074;;;;N;;;FF34;;FF34 +FF55;FULLWIDTH LATIN SMALL LETTER U;Ll;0;L;<wide> 0075;;;;N;;;FF35;;FF35 +FF56;FULLWIDTH LATIN SMALL LETTER V;Ll;0;L;<wide> 0076;;;;N;;;FF36;;FF36 +FF57;FULLWIDTH LATIN SMALL LETTER W;Ll;0;L;<wide> 0077;;;;N;;;FF37;;FF37 +FF58;FULLWIDTH LATIN SMALL LETTER X;Ll;0;L;<wide> 0078;;;;N;;;FF38;;FF38 +FF59;FULLWIDTH LATIN SMALL LETTER Y;Ll;0;L;<wide> 0079;;;;N;;;FF39;;FF39 +FF5A;FULLWIDTH LATIN SMALL LETTER Z;Ll;0;L;<wide> 007A;;;;N;;;FF3A;;FF3A +FF5B;FULLWIDTH LEFT CURLY BRACKET;Ps;0;ON;<wide> 007B;;;;Y;FULLWIDTH OPENING CURLY BRACKET;;;; +FF5C;FULLWIDTH VERTICAL LINE;Sm;0;ON;<wide> 007C;;;;N;FULLWIDTH VERTICAL BAR;;;; +FF5D;FULLWIDTH RIGHT CURLY BRACKET;Pe;0;ON;<wide> 007D;;;;Y;FULLWIDTH CLOSING CURLY BRACKET;;;; +FF5E;FULLWIDTH TILDE;Sm;0;ON;<wide> 007E;;;;N;FULLWIDTH SPACING TILDE;;;; +FF5F;FULLWIDTH LEFT WHITE PARENTHESIS;Ps;0;ON;<wide> 2985;;;;Y;;*;;; +FF60;FULLWIDTH RIGHT WHITE PARENTHESIS;Pe;0;ON;<wide> 2986;;;;Y;;*;;; +FF61;HALFWIDTH IDEOGRAPHIC FULL STOP;Po;0;ON;<narrow> 3002;;;;N;HALFWIDTH IDEOGRAPHIC PERIOD;;;; +FF62;HALFWIDTH LEFT CORNER BRACKET;Ps;0;ON;<narrow> 300C;;;;Y;HALFWIDTH OPENING CORNER BRACKET;;;; +FF63;HALFWIDTH RIGHT CORNER BRACKET;Pe;0;ON;<narrow> 300D;;;;Y;HALFWIDTH CLOSING CORNER BRACKET;;;; +FF64;HALFWIDTH IDEOGRAPHIC COMMA;Po;0;ON;<narrow> 3001;;;;N;;;;; +FF65;HALFWIDTH KATAKANA MIDDLE DOT;Pc;0;ON;<narrow> 30FB;;;;N;;;;; +FF66;HALFWIDTH KATAKANA LETTER WO;Lo;0;L;<narrow> 30F2;;;;N;;;;; +FF67;HALFWIDTH KATAKANA LETTER SMALL A;Lo;0;L;<narrow> 30A1;;;;N;;;;; +FF68;HALFWIDTH KATAKANA LETTER SMALL I;Lo;0;L;<narrow> 30A3;;;;N;;;;; +FF69;HALFWIDTH KATAKANA LETTER SMALL U;Lo;0;L;<narrow> 30A5;;;;N;;;;; +FF6A;HALFWIDTH KATAKANA LETTER SMALL E;Lo;0;L;<narrow> 30A7;;;;N;;;;; +FF6B;HALFWIDTH KATAKANA LETTER SMALL O;Lo;0;L;<narrow> 30A9;;;;N;;;;; +FF6C;HALFWIDTH KATAKANA LETTER SMALL YA;Lo;0;L;<narrow> 30E3;;;;N;;;;; +FF6D;HALFWIDTH KATAKANA LETTER SMALL YU;Lo;0;L;<narrow> 30E5;;;;N;;;;; +FF6E;HALFWIDTH KATAKANA LETTER SMALL YO;Lo;0;L;<narrow> 30E7;;;;N;;;;; +FF6F;HALFWIDTH KATAKANA LETTER SMALL TU;Lo;0;L;<narrow> 30C3;;;;N;;;;; +FF70;HALFWIDTH KATAKANA-HIRAGANA PROLONGED SOUND MARK;Lm;0;L;<narrow> 30FC;;;;N;;;;; +FF71;HALFWIDTH KATAKANA LETTER A;Lo;0;L;<narrow> 30A2;;;;N;;;;; +FF72;HALFWIDTH KATAKANA LETTER I;Lo;0;L;<narrow> 30A4;;;;N;;;;; +FF73;HALFWIDTH KATAKANA LETTER U;Lo;0;L;<narrow> 30A6;;;;N;;;;; +FF74;HALFWIDTH KATAKANA LETTER E;Lo;0;L;<narrow> 30A8;;;;N;;;;; +FF75;HALFWIDTH KATAKANA LETTER O;Lo;0;L;<narrow> 30AA;;;;N;;;;; +FF76;HALFWIDTH KATAKANA LETTER KA;Lo;0;L;<narrow> 30AB;;;;N;;;;; +FF77;HALFWIDTH KATAKANA LETTER KI;Lo;0;L;<narrow> 30AD;;;;N;;;;; +FF78;HALFWIDTH KATAKANA LETTER KU;Lo;0;L;<narrow> 30AF;;;;N;;;;; +FF79;HALFWIDTH KATAKANA LETTER KE;Lo;0;L;<narrow> 30B1;;;;N;;;;; +FF7A;HALFWIDTH KATAKANA LETTER KO;Lo;0;L;<narrow> 30B3;;;;N;;;;; +FF7B;HALFWIDTH KATAKANA LETTER SA;Lo;0;L;<narrow> 30B5;;;;N;;;;; +FF7C;HALFWIDTH KATAKANA LETTER SI;Lo;0;L;<narrow> 30B7;;;;N;;;;; +FF7D;HALFWIDTH KATAKANA LETTER SU;Lo;0;L;<narrow> 30B9;;;;N;;;;; +FF7E;HALFWIDTH KATAKANA LETTER SE;Lo;0;L;<narrow> 30BB;;;;N;;;;; +FF7F;HALFWIDTH KATAKANA LETTER SO;Lo;0;L;<narrow> 30BD;;;;N;;;;; +FF80;HALFWIDTH KATAKANA LETTER TA;Lo;0;L;<narrow> 30BF;;;;N;;;;; +FF81;HALFWIDTH KATAKANA LETTER TI;Lo;0;L;<narrow> 30C1;;;;N;;;;; +FF82;HALFWIDTH KATAKANA LETTER TU;Lo;0;L;<narrow> 30C4;;;;N;;;;; +FF83;HALFWIDTH KATAKANA LETTER TE;Lo;0;L;<narrow> 30C6;;;;N;;;;; +FF84;HALFWIDTH KATAKANA LETTER TO;Lo;0;L;<narrow> 30C8;;;;N;;;;; +FF85;HALFWIDTH KATAKANA LETTER NA;Lo;0;L;<narrow> 30CA;;;;N;;;;; +FF86;HALFWIDTH KATAKANA LETTER NI;Lo;0;L;<narrow> 30CB;;;;N;;;;; +FF87;HALFWIDTH KATAKANA LETTER NU;Lo;0;L;<narrow> 30CC;;;;N;;;;; +FF88;HALFWIDTH KATAKANA LETTER NE;Lo;0;L;<narrow> 30CD;;;;N;;;;; +FF89;HALFWIDTH KATAKANA LETTER NO;Lo;0;L;<narrow> 30CE;;;;N;;;;; +FF8A;HALFWIDTH KATAKANA LETTER HA;Lo;0;L;<narrow> 30CF;;;;N;;;;; +FF8B;HALFWIDTH KATAKANA LETTER HI;Lo;0;L;<narrow> 30D2;;;;N;;;;; +FF8C;HALFWIDTH KATAKANA LETTER HU;Lo;0;L;<narrow> 30D5;;;;N;;;;; +FF8D;HALFWIDTH KATAKANA LETTER HE;Lo;0;L;<narrow> 30D8;;;;N;;;;; +FF8E;HALFWIDTH KATAKANA LETTER HO;Lo;0;L;<narrow> 30DB;;;;N;;;;; +FF8F;HALFWIDTH KATAKANA LETTER MA;Lo;0;L;<narrow> 30DE;;;;N;;;;; +FF90;HALFWIDTH KATAKANA LETTER MI;Lo;0;L;<narrow> 30DF;;;;N;;;;; +FF91;HALFWIDTH KATAKANA LETTER MU;Lo;0;L;<narrow> 30E0;;;;N;;;;; +FF92;HALFWIDTH KATAKANA LETTER ME;Lo;0;L;<narrow> 30E1;;;;N;;;;; +FF93;HALFWIDTH KATAKANA LETTER MO;Lo;0;L;<narrow> 30E2;;;;N;;;;; +FF94;HALFWIDTH KATAKANA LETTER YA;Lo;0;L;<narrow> 30E4;;;;N;;;;; +FF95;HALFWIDTH KATAKANA LETTER YU;Lo;0;L;<narrow> 30E6;;;;N;;;;; +FF96;HALFWIDTH KATAKANA LETTER YO;Lo;0;L;<narrow> 30E8;;;;N;;;;; +FF97;HALFWIDTH KATAKANA LETTER RA;Lo;0;L;<narrow> 30E9;;;;N;;;;; +FF98;HALFWIDTH KATAKANA LETTER RI;Lo;0;L;<narrow> 30EA;;;;N;;;;; +FF99;HALFWIDTH KATAKANA LETTER RU;Lo;0;L;<narrow> 30EB;;;;N;;;;; +FF9A;HALFWIDTH KATAKANA LETTER RE;Lo;0;L;<narrow> 30EC;;;;N;;;;; +FF9B;HALFWIDTH KATAKANA LETTER RO;Lo;0;L;<narrow> 30ED;;;;N;;;;; +FF9C;HALFWIDTH KATAKANA LETTER WA;Lo;0;L;<narrow> 30EF;;;;N;;;;; +FF9D;HALFWIDTH KATAKANA LETTER N;Lo;0;L;<narrow> 30F3;;;;N;;;;; +FF9E;HALFWIDTH KATAKANA VOICED SOUND MARK;Lm;0;L;<narrow> 3099;;;;N;;halfwidth katakana-hiragana voiced sound mark;;; +FF9F;HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK;Lm;0;L;<narrow> 309A;;;;N;;halfwidth katakana-hiragana semi-voiced sound mark;;; +FFA0;HALFWIDTH HANGUL FILLER;Lo;0;L;<narrow> 3164;;;;N;HALFWIDTH HANGUL CAE OM;;;; +FFA1;HALFWIDTH HANGUL LETTER KIYEOK;Lo;0;L;<narrow> 3131;;;;N;HALFWIDTH HANGUL LETTER GIYEOG;;;; +FFA2;HALFWIDTH HANGUL LETTER SSANGKIYEOK;Lo;0;L;<narrow> 3132;;;;N;HALFWIDTH HANGUL LETTER SSANG GIYEOG;;;; +FFA3;HALFWIDTH HANGUL LETTER KIYEOK-SIOS;Lo;0;L;<narrow> 3133;;;;N;HALFWIDTH HANGUL LETTER GIYEOG SIOS;;;; +FFA4;HALFWIDTH HANGUL LETTER NIEUN;Lo;0;L;<narrow> 3134;;;;N;;;;; +FFA5;HALFWIDTH HANGUL LETTER NIEUN-CIEUC;Lo;0;L;<narrow> 3135;;;;N;HALFWIDTH HANGUL LETTER NIEUN JIEUJ;;;; +FFA6;HALFWIDTH HANGUL LETTER NIEUN-HIEUH;Lo;0;L;<narrow> 3136;;;;N;HALFWIDTH HANGUL LETTER NIEUN HIEUH;;;; +FFA7;HALFWIDTH HANGUL LETTER TIKEUT;Lo;0;L;<narrow> 3137;;;;N;HALFWIDTH HANGUL LETTER DIGEUD;;;; +FFA8;HALFWIDTH HANGUL LETTER SSANGTIKEUT;Lo;0;L;<narrow> 3138;;;;N;HALFWIDTH HANGUL LETTER SSANG DIGEUD;;;; +FFA9;HALFWIDTH HANGUL LETTER RIEUL;Lo;0;L;<narrow> 3139;;;;N;HALFWIDTH HANGUL LETTER LIEUL;;;; +FFAA;HALFWIDTH HANGUL LETTER RIEUL-KIYEOK;Lo;0;L;<narrow> 313A;;;;N;HALFWIDTH HANGUL LETTER LIEUL GIYEOG;;;; +FFAB;HALFWIDTH HANGUL LETTER RIEUL-MIEUM;Lo;0;L;<narrow> 313B;;;;N;HALFWIDTH HANGUL LETTER LIEUL MIEUM;;;; +FFAC;HALFWIDTH HANGUL LETTER RIEUL-PIEUP;Lo;0;L;<narrow> 313C;;;;N;HALFWIDTH HANGUL LETTER LIEUL BIEUB;;;; +FFAD;HALFWIDTH HANGUL LETTER RIEUL-SIOS;Lo;0;L;<narrow> 313D;;;;N;HALFWIDTH HANGUL LETTER LIEUL SIOS;;;; +FFAE;HALFWIDTH HANGUL LETTER RIEUL-THIEUTH;Lo;0;L;<narrow> 313E;;;;N;HALFWIDTH HANGUL LETTER LIEUL TIEUT;;;; +FFAF;HALFWIDTH HANGUL LETTER RIEUL-PHIEUPH;Lo;0;L;<narrow> 313F;;;;N;HALFWIDTH HANGUL LETTER LIEUL PIEUP;;;; +FFB0;HALFWIDTH HANGUL LETTER RIEUL-HIEUH;Lo;0;L;<narrow> 3140;;;;N;HALFWIDTH HANGUL LETTER LIEUL HIEUH;;;; +FFB1;HALFWIDTH HANGUL LETTER MIEUM;Lo;0;L;<narrow> 3141;;;;N;;;;; +FFB2;HALFWIDTH HANGUL LETTER PIEUP;Lo;0;L;<narrow> 3142;;;;N;HALFWIDTH HANGUL LETTER BIEUB;;;; +FFB3;HALFWIDTH HANGUL LETTER SSANGPIEUP;Lo;0;L;<narrow> 3143;;;;N;HALFWIDTH HANGUL LETTER SSANG BIEUB;;;; +FFB4;HALFWIDTH HANGUL LETTER PIEUP-SIOS;Lo;0;L;<narrow> 3144;;;;N;HALFWIDTH HANGUL LETTER BIEUB SIOS;;;; +FFB5;HALFWIDTH HANGUL LETTER SIOS;Lo;0;L;<narrow> 3145;;;;N;;;;; +FFB6;HALFWIDTH HANGUL LETTER SSANGSIOS;Lo;0;L;<narrow> 3146;;;;N;HALFWIDTH HANGUL LETTER SSANG SIOS;;;; +FFB7;HALFWIDTH HANGUL LETTER IEUNG;Lo;0;L;<narrow> 3147;;;;N;;;;; +FFB8;HALFWIDTH HANGUL LETTER CIEUC;Lo;0;L;<narrow> 3148;;;;N;HALFWIDTH HANGUL LETTER JIEUJ;;;; +FFB9;HALFWIDTH HANGUL LETTER SSANGCIEUC;Lo;0;L;<narrow> 3149;;;;N;HALFWIDTH HANGUL LETTER SSANG JIEUJ;;;; +FFBA;HALFWIDTH HANGUL LETTER CHIEUCH;Lo;0;L;<narrow> 314A;;;;N;HALFWIDTH HANGUL LETTER CIEUC;;;; +FFBB;HALFWIDTH HANGUL LETTER KHIEUKH;Lo;0;L;<narrow> 314B;;;;N;HALFWIDTH HANGUL LETTER KIYEOK;;;; +FFBC;HALFWIDTH HANGUL LETTER THIEUTH;Lo;0;L;<narrow> 314C;;;;N;HALFWIDTH HANGUL LETTER TIEUT;;;; +FFBD;HALFWIDTH HANGUL LETTER PHIEUPH;Lo;0;L;<narrow> 314D;;;;N;HALFWIDTH HANGUL LETTER PIEUP;;;; +FFBE;HALFWIDTH HANGUL LETTER HIEUH;Lo;0;L;<narrow> 314E;;;;N;;;;; +FFC2;HALFWIDTH HANGUL LETTER A;Lo;0;L;<narrow> 314F;;;;N;;;;; +FFC3;HALFWIDTH HANGUL LETTER AE;Lo;0;L;<narrow> 3150;;;;N;;;;; +FFC4;HALFWIDTH HANGUL LETTER YA;Lo;0;L;<narrow> 3151;;;;N;;;;; +FFC5;HALFWIDTH HANGUL LETTER YAE;Lo;0;L;<narrow> 3152;;;;N;;;;; +FFC6;HALFWIDTH HANGUL LETTER EO;Lo;0;L;<narrow> 3153;;;;N;;;;; +FFC7;HALFWIDTH HANGUL LETTER E;Lo;0;L;<narrow> 3154;;;;N;;;;; +FFCA;HALFWIDTH HANGUL LETTER YEO;Lo;0;L;<narrow> 3155;;;;N;;;;; +FFCB;HALFWIDTH HANGUL LETTER YE;Lo;0;L;<narrow> 3156;;;;N;;;;; +FFCC;HALFWIDTH HANGUL LETTER O;Lo;0;L;<narrow> 3157;;;;N;;;;; +FFCD;HALFWIDTH HANGUL LETTER WA;Lo;0;L;<narrow> 3158;;;;N;;;;; +FFCE;HALFWIDTH HANGUL LETTER WAE;Lo;0;L;<narrow> 3159;;;;N;;;;; +FFCF;HALFWIDTH HANGUL LETTER OE;Lo;0;L;<narrow> 315A;;;;N;;;;; +FFD2;HALFWIDTH HANGUL LETTER YO;Lo;0;L;<narrow> 315B;;;;N;;;;; +FFD3;HALFWIDTH HANGUL LETTER U;Lo;0;L;<narrow> 315C;;;;N;;;;; +FFD4;HALFWIDTH HANGUL LETTER WEO;Lo;0;L;<narrow> 315D;;;;N;;;;; +FFD5;HALFWIDTH HANGUL LETTER WE;Lo;0;L;<narrow> 315E;;;;N;;;;; +FFD6;HALFWIDTH HANGUL LETTER WI;Lo;0;L;<narrow> 315F;;;;N;;;;; +FFD7;HALFWIDTH HANGUL LETTER YU;Lo;0;L;<narrow> 3160;;;;N;;;;; +FFDA;HALFWIDTH HANGUL LETTER EU;Lo;0;L;<narrow> 3161;;;;N;;;;; +FFDB;HALFWIDTH HANGUL LETTER YI;Lo;0;L;<narrow> 3162;;;;N;;;;; +FFDC;HALFWIDTH HANGUL LETTER I;Lo;0;L;<narrow> 3163;;;;N;;;;; +FFE0;FULLWIDTH CENT SIGN;Sc;0;ET;<wide> 00A2;;;;N;;;;; +FFE1;FULLWIDTH POUND SIGN;Sc;0;ET;<wide> 00A3;;;;N;;;;; +FFE2;FULLWIDTH NOT SIGN;Sm;0;ON;<wide> 00AC;;;;N;;;;; +FFE3;FULLWIDTH MACRON;Sk;0;ON;<wide> 00AF;;;;N;FULLWIDTH SPACING MACRON;*;;; +FFE4;FULLWIDTH BROKEN BAR;So;0;ON;<wide> 00A6;;;;N;FULLWIDTH BROKEN VERTICAL BAR;;;; +FFE5;FULLWIDTH YEN SIGN;Sc;0;ET;<wide> 00A5;;;;N;;;;; +FFE6;FULLWIDTH WON SIGN;Sc;0;ET;<wide> 20A9;;;;N;;;;; +FFE8;HALFWIDTH FORMS LIGHT VERTICAL;So;0;ON;<narrow> 2502;;;;N;;;;; +FFE9;HALFWIDTH LEFTWARDS ARROW;Sm;0;ON;<narrow> 2190;;;;N;;;;; +FFEA;HALFWIDTH UPWARDS ARROW;Sm;0;ON;<narrow> 2191;;;;N;;;;; +FFEB;HALFWIDTH RIGHTWARDS ARROW;Sm;0;ON;<narrow> 2192;;;;N;;;;; +FFEC;HALFWIDTH DOWNWARDS ARROW;Sm;0;ON;<narrow> 2193;;;;N;;;;; +FFED;HALFWIDTH BLACK SQUARE;So;0;ON;<narrow> 25A0;;;;N;;;;; +FFEE;HALFWIDTH WHITE CIRCLE;So;0;ON;<narrow> 25CB;;;;N;;;;; +FFF9;INTERLINEAR ANNOTATION ANCHOR;Cf;0;BN;;;;;N;;;;; +FFFA;INTERLINEAR ANNOTATION SEPARATOR;Cf;0;BN;;;;;N;;;;; +FFFB;INTERLINEAR ANNOTATION TERMINATOR;Cf;0;BN;;;;;N;;;;; +FFFC;OBJECT REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; +FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;; +10300;OLD ITALIC LETTER A;Lo;0;L;;;;;N;;;;; +10301;OLD ITALIC LETTER BE;Lo;0;L;;;;;N;;;;; +10302;OLD ITALIC LETTER KE;Lo;0;L;;;;;N;;;;; +10303;OLD ITALIC LETTER DE;Lo;0;L;;;;;N;;;;; +10304;OLD ITALIC LETTER E;Lo;0;L;;;;;N;;;;; +10305;OLD ITALIC LETTER VE;Lo;0;L;;;;;N;;;;; +10306;OLD ITALIC LETTER ZE;Lo;0;L;;;;;N;;;;; +10307;OLD ITALIC LETTER HE;Lo;0;L;;;;;N;;;;; +10308;OLD ITALIC LETTER THE;Lo;0;L;;;;;N;;;;; +10309;OLD ITALIC LETTER I;Lo;0;L;;;;;N;;;;; +1030A;OLD ITALIC LETTER KA;Lo;0;L;;;;;N;;;;; +1030B;OLD ITALIC LETTER EL;Lo;0;L;;;;;N;;;;; +1030C;OLD ITALIC LETTER EM;Lo;0;L;;;;;N;;;;; +1030D;OLD ITALIC LETTER EN;Lo;0;L;;;;;N;;;;; +1030E;OLD ITALIC LETTER ESH;Lo;0;L;;;;;N;;;;; +1030F;OLD ITALIC LETTER O;Lo;0;L;;;;;N;;Faliscan;;; +10310;OLD ITALIC LETTER PE;Lo;0;L;;;;;N;;;;; +10311;OLD ITALIC LETTER SHE;Lo;0;L;;;;;N;;;;; +10312;OLD ITALIC LETTER KU;Lo;0;L;;;;;N;;;;; +10313;OLD ITALIC LETTER ER;Lo;0;L;;;;;N;;;;; +10314;OLD ITALIC LETTER ES;Lo;0;L;;;;;N;;;;; +10315;OLD ITALIC LETTER TE;Lo;0;L;;;;;N;;;;; +10316;OLD ITALIC LETTER U;Lo;0;L;;;;;N;;;;; +10317;OLD ITALIC LETTER EKS;Lo;0;L;;;;;N;;Faliscan;;; +10318;OLD ITALIC LETTER PHE;Lo;0;L;;;;;N;;;;; +10319;OLD ITALIC LETTER KHE;Lo;0;L;;;;;N;;;;; +1031A;OLD ITALIC LETTER EF;Lo;0;L;;;;;N;;;;; +1031B;OLD ITALIC LETTER ERS;Lo;0;L;;;;;N;;Umbrian;;; +1031C;OLD ITALIC LETTER CHE;Lo;0;L;;;;;N;;Umbrian;;; +1031D;OLD ITALIC LETTER II;Lo;0;L;;;;;N;;Oscan;;; +1031E;OLD ITALIC LETTER UU;Lo;0;L;;;;;N;;Oscan;;; +10320;OLD ITALIC NUMERAL ONE;No;0;L;;;;1;N;;;;; +10321;OLD ITALIC NUMERAL FIVE;No;0;L;;;;5;N;;;;; +10322;OLD ITALIC NUMERAL TEN;No;0;L;;;;10;N;;;;; +10323;OLD ITALIC NUMERAL FIFTY;No;0;L;;;;50;N;;;;; +10330;GOTHIC LETTER AHSA;Lo;0;L;;;;;N;;;;; +10331;GOTHIC LETTER BAIRKAN;Lo;0;L;;;;;N;;;;; +10332;GOTHIC LETTER GIBA;Lo;0;L;;;;;N;;;;; +10333;GOTHIC LETTER DAGS;Lo;0;L;;;;;N;;;;; +10334;GOTHIC LETTER AIHVUS;Lo;0;L;;;;;N;;;;; +10335;GOTHIC LETTER QAIRTHRA;Lo;0;L;;;;;N;;;;; +10336;GOTHIC LETTER IUJA;Lo;0;L;;;;;N;;;;; +10337;GOTHIC LETTER HAGL;Lo;0;L;;;;;N;;;;; +10338;GOTHIC LETTER THIUTH;Lo;0;L;;;;;N;;;;; +10339;GOTHIC LETTER EIS;Lo;0;L;;;;;N;;;;; +1033A;GOTHIC LETTER KUSMA;Lo;0;L;;;;;N;;;;; +1033B;GOTHIC LETTER LAGUS;Lo;0;L;;;;;N;;;;; +1033C;GOTHIC LETTER MANNA;Lo;0;L;;;;;N;;;;; +1033D;GOTHIC LETTER NAUTHS;Lo;0;L;;;;;N;;;;; +1033E;GOTHIC LETTER JER;Lo;0;L;;;;;N;;;;; +1033F;GOTHIC LETTER URUS;Lo;0;L;;;;;N;;;;; +10340;GOTHIC LETTER PAIRTHRA;Lo;0;L;;;;;N;;;;; +10341;GOTHIC LETTER NINETY;Lo;0;L;;;;;N;;;;; +10342;GOTHIC LETTER RAIDA;Lo;0;L;;;;;N;;;;; +10343;GOTHIC LETTER SAUIL;Lo;0;L;;;;;N;;;;; +10344;GOTHIC LETTER TEIWS;Lo;0;L;;;;;N;;;;; +10345;GOTHIC LETTER WINJA;Lo;0;L;;;;;N;;;;; +10346;GOTHIC LETTER FAIHU;Lo;0;L;;;;;N;;;;; +10347;GOTHIC LETTER IGGWS;Lo;0;L;;;;;N;;;;; +10348;GOTHIC LETTER HWAIR;Lo;0;L;;;;;N;;;;; +10349;GOTHIC LETTER OTHAL;Lo;0;L;;;;;N;;;;; +1034A;GOTHIC LETTER NINE HUNDRED;Nl;0;L;;;;;N;;;;; +10400;DESERET CAPITAL LETTER LONG I;Lu;0;L;;;;;N;;;;10428; +10401;DESERET CAPITAL LETTER LONG E;Lu;0;L;;;;;N;;;;10429; +10402;DESERET CAPITAL LETTER LONG A;Lu;0;L;;;;;N;;;;1042A; +10403;DESERET CAPITAL LETTER LONG AH;Lu;0;L;;;;;N;;;;1042B; +10404;DESERET CAPITAL LETTER LONG O;Lu;0;L;;;;;N;;;;1042C; +10405;DESERET CAPITAL LETTER LONG OO;Lu;0;L;;;;;N;;;;1042D; +10406;DESERET CAPITAL LETTER SHORT I;Lu;0;L;;;;;N;;;;1042E; +10407;DESERET CAPITAL LETTER SHORT E;Lu;0;L;;;;;N;;;;1042F; +10408;DESERET CAPITAL LETTER SHORT A;Lu;0;L;;;;;N;;;;10430; +10409;DESERET CAPITAL LETTER SHORT AH;Lu;0;L;;;;;N;;;;10431; +1040A;DESERET CAPITAL LETTER SHORT O;Lu;0;L;;;;;N;;;;10432; +1040B;DESERET CAPITAL LETTER SHORT OO;Lu;0;L;;;;;N;;;;10433; +1040C;DESERET CAPITAL LETTER AY;Lu;0;L;;;;;N;;;;10434; +1040D;DESERET CAPITAL LETTER OW;Lu;0;L;;;;;N;;;;10435; +1040E;DESERET CAPITAL LETTER WU;Lu;0;L;;;;;N;;;;10436; +1040F;DESERET CAPITAL LETTER YEE;Lu;0;L;;;;;N;;;;10437; +10410;DESERET CAPITAL LETTER H;Lu;0;L;;;;;N;;;;10438; +10411;DESERET CAPITAL LETTER PEE;Lu;0;L;;;;;N;;;;10439; +10412;DESERET CAPITAL LETTER BEE;Lu;0;L;;;;;N;;;;1043A; +10413;DESERET CAPITAL LETTER TEE;Lu;0;L;;;;;N;;;;1043B; +10414;DESERET CAPITAL LETTER DEE;Lu;0;L;;;;;N;;;;1043C; +10415;DESERET CAPITAL LETTER CHEE;Lu;0;L;;;;;N;;;;1043D; +10416;DESERET CAPITAL LETTER JEE;Lu;0;L;;;;;N;;;;1043E; +10417;DESERET CAPITAL LETTER KAY;Lu;0;L;;;;;N;;;;1043F; +10418;DESERET CAPITAL LETTER GAY;Lu;0;L;;;;;N;;;;10440; +10419;DESERET CAPITAL LETTER EF;Lu;0;L;;;;;N;;;;10441; +1041A;DESERET CAPITAL LETTER VEE;Lu;0;L;;;;;N;;;;10442; +1041B;DESERET CAPITAL LETTER ETH;Lu;0;L;;;;;N;;;;10443; +1041C;DESERET CAPITAL LETTER THEE;Lu;0;L;;;;;N;;;;10444; +1041D;DESERET CAPITAL LETTER ES;Lu;0;L;;;;;N;;;;10445; +1041E;DESERET CAPITAL LETTER ZEE;Lu;0;L;;;;;N;;;;10446; +1041F;DESERET CAPITAL LETTER ESH;Lu;0;L;;;;;N;;;;10447; +10420;DESERET CAPITAL LETTER ZHEE;Lu;0;L;;;;;N;;;;10448; +10421;DESERET CAPITAL LETTER ER;Lu;0;L;;;;;N;;;;10449; +10422;DESERET CAPITAL LETTER EL;Lu;0;L;;;;;N;;;;1044A; +10423;DESERET CAPITAL LETTER EM;Lu;0;L;;;;;N;;;;1044B; +10424;DESERET CAPITAL LETTER EN;Lu;0;L;;;;;N;;;;1044C; +10425;DESERET CAPITAL LETTER ENG;Lu;0;L;;;;;N;;;;1044D; +10428;DESERET SMALL LETTER LONG I;Ll;0;L;;;;;N;;;10400;;10400 +10429;DESERET SMALL LETTER LONG E;Ll;0;L;;;;;N;;;10401;;10401 +1042A;DESERET SMALL LETTER LONG A;Ll;0;L;;;;;N;;;10402;;10402 +1042B;DESERET SMALL LETTER LONG AH;Ll;0;L;;;;;N;;;10403;;10403 +1042C;DESERET SMALL LETTER LONG O;Ll;0;L;;;;;N;;;10404;;10404 +1042D;DESERET SMALL LETTER LONG OO;Ll;0;L;;;;;N;;;10405;;10405 +1042E;DESERET SMALL LETTER SHORT I;Ll;0;L;;;;;N;;;10406;;10406 +1042F;DESERET SMALL LETTER SHORT E;Ll;0;L;;;;;N;;;10407;;10407 +10430;DESERET SMALL LETTER SHORT A;Ll;0;L;;;;;N;;;10408;;10408 +10431;DESERET SMALL LETTER SHORT AH;Ll;0;L;;;;;N;;;10409;;10409 +10432;DESERET SMALL LETTER SHORT O;Ll;0;L;;;;;N;;;1040A;;1040A +10433;DESERET SMALL LETTER SHORT OO;Ll;0;L;;;;;N;;;1040B;;1040B +10434;DESERET SMALL LETTER AY;Ll;0;L;;;;;N;;;1040C;;1040C +10435;DESERET SMALL LETTER OW;Ll;0;L;;;;;N;;;1040D;;1040D +10436;DESERET SMALL LETTER WU;Ll;0;L;;;;;N;;;1040E;;1040E +10437;DESERET SMALL LETTER YEE;Ll;0;L;;;;;N;;;1040F;;1040F +10438;DESERET SMALL LETTER H;Ll;0;L;;;;;N;;;10410;;10410 +10439;DESERET SMALL LETTER PEE;Ll;0;L;;;;;N;;;10411;;10411 +1043A;DESERET SMALL LETTER BEE;Ll;0;L;;;;;N;;;10412;;10412 +1043B;DESERET SMALL LETTER TEE;Ll;0;L;;;;;N;;;10413;;10413 +1043C;DESERET SMALL LETTER DEE;Ll;0;L;;;;;N;;;10414;;10414 +1043D;DESERET SMALL LETTER CHEE;Ll;0;L;;;;;N;;;10415;;10415 +1043E;DESERET SMALL LETTER JEE;Ll;0;L;;;;;N;;;10416;;10416 +1043F;DESERET SMALL LETTER KAY;Ll;0;L;;;;;N;;;10417;;10417 +10440;DESERET SMALL LETTER GAY;Ll;0;L;;;;;N;;;10418;;10418 +10441;DESERET SMALL LETTER EF;Ll;0;L;;;;;N;;;10419;;10419 +10442;DESERET SMALL LETTER VEE;Ll;0;L;;;;;N;;;1041A;;1041A +10443;DESERET SMALL LETTER ETH;Ll;0;L;;;;;N;;;1041B;;1041B +10444;DESERET SMALL LETTER THEE;Ll;0;L;;;;;N;;;1041C;;1041C +10445;DESERET SMALL LETTER ES;Ll;0;L;;;;;N;;;1041D;;1041D +10446;DESERET SMALL LETTER ZEE;Ll;0;L;;;;;N;;;1041E;;1041E +10447;DESERET SMALL LETTER ESH;Ll;0;L;;;;;N;;;1041F;;1041F +10448;DESERET SMALL LETTER ZHEE;Ll;0;L;;;;;N;;;10420;;10420 +10449;DESERET SMALL LETTER ER;Ll;0;L;;;;;N;;;10421;;10421 +1044A;DESERET SMALL LETTER EL;Ll;0;L;;;;;N;;;10422;;10422 +1044B;DESERET SMALL LETTER EM;Ll;0;L;;;;;N;;;10423;;10423 +1044C;DESERET SMALL LETTER EN;Ll;0;L;;;;;N;;;10424;;10424 +1044D;DESERET SMALL LETTER ENG;Ll;0;L;;;;;N;;;10425;;10425 +1D000;BYZANTINE MUSICAL SYMBOL PSILI;So;0;L;;;;;N;;;;; +1D001;BYZANTINE MUSICAL SYMBOL DASEIA;So;0;L;;;;;N;;;;; +1D002;BYZANTINE MUSICAL SYMBOL PERISPOMENI;So;0;L;;;;;N;;;;; +1D003;BYZANTINE MUSICAL SYMBOL OXEIA EKFONITIKON;So;0;L;;;;;N;;;;; +1D004;BYZANTINE MUSICAL SYMBOL OXEIA DIPLI;So;0;L;;;;;N;;;;; +1D005;BYZANTINE MUSICAL SYMBOL VAREIA EKFONITIKON;So;0;L;;;;;N;;;;; +1D006;BYZANTINE MUSICAL SYMBOL VAREIA DIPLI;So;0;L;;;;;N;;;;; +1D007;BYZANTINE MUSICAL SYMBOL KATHISTI;So;0;L;;;;;N;;;;; +1D008;BYZANTINE MUSICAL SYMBOL SYRMATIKI;So;0;L;;;;;N;;;;; +1D009;BYZANTINE MUSICAL SYMBOL PARAKLITIKI;So;0;L;;;;;N;;;;; +1D00A;BYZANTINE MUSICAL SYMBOL YPOKRISIS;So;0;L;;;;;N;;;;; +1D00B;BYZANTINE MUSICAL SYMBOL YPOKRISIS DIPLI;So;0;L;;;;;N;;;;; +1D00C;BYZANTINE MUSICAL SYMBOL KREMASTI;So;0;L;;;;;N;;;;; +1D00D;BYZANTINE MUSICAL SYMBOL APESO EKFONITIKON;So;0;L;;;;;N;;;;; +1D00E;BYZANTINE MUSICAL SYMBOL EXO EKFONITIKON;So;0;L;;;;;N;;;;; +1D00F;BYZANTINE MUSICAL SYMBOL TELEIA;So;0;L;;;;;N;;;;; +1D010;BYZANTINE MUSICAL SYMBOL KENTIMATA;So;0;L;;;;;N;;;;; +1D011;BYZANTINE MUSICAL SYMBOL APOSTROFOS;So;0;L;;;;;N;;;;; +1D012;BYZANTINE MUSICAL SYMBOL APOSTROFOS DIPLI;So;0;L;;;;;N;;;;; +1D013;BYZANTINE MUSICAL SYMBOL SYNEVMA;So;0;L;;;;;N;;;;; +1D014;BYZANTINE MUSICAL SYMBOL THITA;So;0;L;;;;;N;;;;; +1D015;BYZANTINE MUSICAL SYMBOL OLIGON ARCHAION;So;0;L;;;;;N;;;;; +1D016;BYZANTINE MUSICAL SYMBOL GORGON ARCHAION;So;0;L;;;;;N;;;;; +1D017;BYZANTINE MUSICAL SYMBOL PSILON;So;0;L;;;;;N;;;;; +1D018;BYZANTINE MUSICAL SYMBOL CHAMILON;So;0;L;;;;;N;;;;; +1D019;BYZANTINE MUSICAL SYMBOL VATHY;So;0;L;;;;;N;;;;; +1D01A;BYZANTINE MUSICAL SYMBOL ISON ARCHAION;So;0;L;;;;;N;;;;; +1D01B;BYZANTINE MUSICAL SYMBOL KENTIMA ARCHAION;So;0;L;;;;;N;;;;; +1D01C;BYZANTINE MUSICAL SYMBOL KENTIMATA ARCHAION;So;0;L;;;;;N;;;;; +1D01D;BYZANTINE MUSICAL SYMBOL SAXIMATA;So;0;L;;;;;N;;;;; +1D01E;BYZANTINE MUSICAL SYMBOL PARICHON;So;0;L;;;;;N;;;;; +1D01F;BYZANTINE MUSICAL SYMBOL STAVROS APODEXIA;So;0;L;;;;;N;;;;; +1D020;BYZANTINE MUSICAL SYMBOL OXEIAI ARCHAION;So;0;L;;;;;N;;;;; +1D021;BYZANTINE MUSICAL SYMBOL VAREIAI ARCHAION;So;0;L;;;;;N;;;;; +1D022;BYZANTINE MUSICAL SYMBOL APODERMA ARCHAION;So;0;L;;;;;N;;;;; +1D023;BYZANTINE MUSICAL SYMBOL APOTHEMA;So;0;L;;;;;N;;;;; +1D024;BYZANTINE MUSICAL SYMBOL KLASMA;So;0;L;;;;;N;;;;; +1D025;BYZANTINE MUSICAL SYMBOL REVMA;So;0;L;;;;;N;;;;; +1D026;BYZANTINE MUSICAL SYMBOL PIASMA ARCHAION;So;0;L;;;;;N;;;;; +1D027;BYZANTINE MUSICAL SYMBOL TINAGMA;So;0;L;;;;;N;;;;; +1D028;BYZANTINE MUSICAL SYMBOL ANATRICHISMA;So;0;L;;;;;N;;;;; +1D029;BYZANTINE MUSICAL SYMBOL SEISMA;So;0;L;;;;;N;;;;; +1D02A;BYZANTINE MUSICAL SYMBOL SYNAGMA ARCHAION;So;0;L;;;;;N;;;;; +1D02B;BYZANTINE MUSICAL SYMBOL SYNAGMA META STAVROU;So;0;L;;;;;N;;;;; +1D02C;BYZANTINE MUSICAL SYMBOL OYRANISMA ARCHAION;So;0;L;;;;;N;;;;; +1D02D;BYZANTINE MUSICAL SYMBOL THEMA;So;0;L;;;;;N;;;;; +1D02E;BYZANTINE MUSICAL SYMBOL LEMOI;So;0;L;;;;;N;;;;; +1D02F;BYZANTINE MUSICAL SYMBOL DYO;So;0;L;;;;;N;;;;; +1D030;BYZANTINE MUSICAL SYMBOL TRIA;So;0;L;;;;;N;;;;; +1D031;BYZANTINE MUSICAL SYMBOL TESSERA;So;0;L;;;;;N;;;;; +1D032;BYZANTINE MUSICAL SYMBOL KRATIMATA;So;0;L;;;;;N;;;;; +1D033;BYZANTINE MUSICAL SYMBOL APESO EXO NEO;So;0;L;;;;;N;;;;; +1D034;BYZANTINE MUSICAL SYMBOL FTHORA ARCHAION;So;0;L;;;;;N;;;;; +1D035;BYZANTINE MUSICAL SYMBOL IMIFTHORA;So;0;L;;;;;N;;;;; +1D036;BYZANTINE MUSICAL SYMBOL TROMIKON ARCHAION;So;0;L;;;;;N;;;;; +1D037;BYZANTINE MUSICAL SYMBOL KATAVA TROMIKON;So;0;L;;;;;N;;;;; +1D038;BYZANTINE MUSICAL SYMBOL PELASTON;So;0;L;;;;;N;;;;; +1D039;BYZANTINE MUSICAL SYMBOL PSIFISTON;So;0;L;;;;;N;;;;; +1D03A;BYZANTINE MUSICAL SYMBOL KONTEVMA;So;0;L;;;;;N;;;;; +1D03B;BYZANTINE MUSICAL SYMBOL CHOREVMA ARCHAION;So;0;L;;;;;N;;;;; +1D03C;BYZANTINE MUSICAL SYMBOL RAPISMA;So;0;L;;;;;N;;;;; +1D03D;BYZANTINE MUSICAL SYMBOL PARAKALESMA ARCHAION;So;0;L;;;;;N;;;;; +1D03E;BYZANTINE MUSICAL SYMBOL PARAKLITIKI ARCHAION;So;0;L;;;;;N;;;;; +1D03F;BYZANTINE MUSICAL SYMBOL ICHADIN;So;0;L;;;;;N;;;;; +1D040;BYZANTINE MUSICAL SYMBOL NANA;So;0;L;;;;;N;;;;; +1D041;BYZANTINE MUSICAL SYMBOL PETASMA;So;0;L;;;;;N;;;;; +1D042;BYZANTINE MUSICAL SYMBOL KONTEVMA ALLO;So;0;L;;;;;N;;;;; +1D043;BYZANTINE MUSICAL SYMBOL TROMIKON ALLO;So;0;L;;;;;N;;;;; +1D044;BYZANTINE MUSICAL SYMBOL STRAGGISMATA;So;0;L;;;;;N;;;;; +1D045;BYZANTINE MUSICAL SYMBOL GRONTHISMATA;So;0;L;;;;;N;;;;; +1D046;BYZANTINE MUSICAL SYMBOL ISON NEO;So;0;L;;;;;N;;;;; +1D047;BYZANTINE MUSICAL SYMBOL OLIGON NEO;So;0;L;;;;;N;;;;; +1D048;BYZANTINE MUSICAL SYMBOL OXEIA NEO;So;0;L;;;;;N;;;;; +1D049;BYZANTINE MUSICAL SYMBOL PETASTI;So;0;L;;;;;N;;;;; +1D04A;BYZANTINE MUSICAL SYMBOL KOUFISMA;So;0;L;;;;;N;;;;; +1D04B;BYZANTINE MUSICAL SYMBOL PETASTOKOUFISMA;So;0;L;;;;;N;;;;; +1D04C;BYZANTINE MUSICAL SYMBOL KRATIMOKOUFISMA;So;0;L;;;;;N;;;;; +1D04D;BYZANTINE MUSICAL SYMBOL PELASTON NEO;So;0;L;;;;;N;;;;; +1D04E;BYZANTINE MUSICAL SYMBOL KENTIMATA NEO ANO;So;0;L;;;;;N;;;;; +1D04F;BYZANTINE MUSICAL SYMBOL KENTIMA NEO ANO;So;0;L;;;;;N;;;;; +1D050;BYZANTINE MUSICAL SYMBOL YPSILI;So;0;L;;;;;N;;;;; +1D051;BYZANTINE MUSICAL SYMBOL APOSTROFOS NEO;So;0;L;;;;;N;;;;; +1D052;BYZANTINE MUSICAL SYMBOL APOSTROFOI SYNDESMOS NEO;So;0;L;;;;;N;;;;; +1D053;BYZANTINE MUSICAL SYMBOL YPORROI;So;0;L;;;;;N;;;;; +1D054;BYZANTINE MUSICAL SYMBOL KRATIMOYPORROON;So;0;L;;;;;N;;;;; +1D055;BYZANTINE MUSICAL SYMBOL ELAFRON;So;0;L;;;;;N;;;;; +1D056;BYZANTINE MUSICAL SYMBOL CHAMILI;So;0;L;;;;;N;;;;; +1D057;BYZANTINE MUSICAL SYMBOL MIKRON ISON;So;0;L;;;;;N;;;;; +1D058;BYZANTINE MUSICAL SYMBOL VAREIA NEO;So;0;L;;;;;N;;;;; +1D059;BYZANTINE MUSICAL SYMBOL PIASMA NEO;So;0;L;;;;;N;;;;; +1D05A;BYZANTINE MUSICAL SYMBOL PSIFISTON NEO;So;0;L;;;;;N;;;;; +1D05B;BYZANTINE MUSICAL SYMBOL OMALON;So;0;L;;;;;N;;;;; +1D05C;BYZANTINE MUSICAL SYMBOL ANTIKENOMA;So;0;L;;;;;N;;;;; +1D05D;BYZANTINE MUSICAL SYMBOL LYGISMA;So;0;L;;;;;N;;;;; +1D05E;BYZANTINE MUSICAL SYMBOL PARAKLITIKI NEO;So;0;L;;;;;N;;;;; +1D05F;BYZANTINE MUSICAL SYMBOL PARAKALESMA NEO;So;0;L;;;;;N;;;;; +1D060;BYZANTINE MUSICAL SYMBOL ETERON PARAKALESMA;So;0;L;;;;;N;;;;; +1D061;BYZANTINE MUSICAL SYMBOL KYLISMA;So;0;L;;;;;N;;;;; +1D062;BYZANTINE MUSICAL SYMBOL ANTIKENOKYLISMA;So;0;L;;;;;N;;;;; +1D063;BYZANTINE MUSICAL SYMBOL TROMIKON NEO;So;0;L;;;;;N;;;;; +1D064;BYZANTINE MUSICAL SYMBOL EKSTREPTON;So;0;L;;;;;N;;;;; +1D065;BYZANTINE MUSICAL SYMBOL SYNAGMA NEO;So;0;L;;;;;N;;;;; +1D066;BYZANTINE MUSICAL SYMBOL SYRMA;So;0;L;;;;;N;;;;; +1D067;BYZANTINE MUSICAL SYMBOL CHOREVMA NEO;So;0;L;;;;;N;;;;; +1D068;BYZANTINE MUSICAL SYMBOL EPEGERMA;So;0;L;;;;;N;;;;; +1D069;BYZANTINE MUSICAL SYMBOL SEISMA NEO;So;0;L;;;;;N;;;;; +1D06A;BYZANTINE MUSICAL SYMBOL XIRON KLASMA;So;0;L;;;;;N;;;;; +1D06B;BYZANTINE MUSICAL SYMBOL TROMIKOPSIFISTON;So;0;L;;;;;N;;;;; +1D06C;BYZANTINE MUSICAL SYMBOL PSIFISTOLYGISMA;So;0;L;;;;;N;;;;; +1D06D;BYZANTINE MUSICAL SYMBOL TROMIKOLYGISMA;So;0;L;;;;;N;;;;; +1D06E;BYZANTINE MUSICAL SYMBOL TROMIKOPARAKALESMA;So;0;L;;;;;N;;;;; +1D06F;BYZANTINE MUSICAL SYMBOL PSIFISTOPARAKALESMA;So;0;L;;;;;N;;;;; +1D070;BYZANTINE MUSICAL SYMBOL TROMIKOSYNAGMA;So;0;L;;;;;N;;;;; +1D071;BYZANTINE MUSICAL SYMBOL PSIFISTOSYNAGMA;So;0;L;;;;;N;;;;; +1D072;BYZANTINE MUSICAL SYMBOL GORGOSYNTHETON;So;0;L;;;;;N;;;;; +1D073;BYZANTINE MUSICAL SYMBOL ARGOSYNTHETON;So;0;L;;;;;N;;;;; +1D074;BYZANTINE MUSICAL SYMBOL ETERON ARGOSYNTHETON;So;0;L;;;;;N;;;;; +1D075;BYZANTINE MUSICAL SYMBOL OYRANISMA NEO;So;0;L;;;;;N;;;;; +1D076;BYZANTINE MUSICAL SYMBOL THEMATISMOS ESO;So;0;L;;;;;N;;;;; +1D077;BYZANTINE MUSICAL SYMBOL THEMATISMOS EXO;So;0;L;;;;;N;;;;; +1D078;BYZANTINE MUSICAL SYMBOL THEMA APLOUN;So;0;L;;;;;N;;;;; +1D079;BYZANTINE MUSICAL SYMBOL THES KAI APOTHES;So;0;L;;;;;N;;;;; +1D07A;BYZANTINE MUSICAL SYMBOL KATAVASMA;So;0;L;;;;;N;;;;; +1D07B;BYZANTINE MUSICAL SYMBOL ENDOFONON;So;0;L;;;;;N;;;;; +1D07C;BYZANTINE MUSICAL SYMBOL YFEN KATO;So;0;L;;;;;N;;;;; +1D07D;BYZANTINE MUSICAL SYMBOL YFEN ANO;So;0;L;;;;;N;;;;; +1D07E;BYZANTINE MUSICAL SYMBOL STAVROS;So;0;L;;;;;N;;;;; +1D07F;BYZANTINE MUSICAL SYMBOL KLASMA ANO;So;0;L;;;;;N;;;;; +1D080;BYZANTINE MUSICAL SYMBOL DIPLI ARCHAION;So;0;L;;;;;N;;;;; +1D081;BYZANTINE MUSICAL SYMBOL KRATIMA ARCHAION;So;0;L;;;;;N;;;;; +1D082;BYZANTINE MUSICAL SYMBOL KRATIMA ALLO;So;0;L;;;;;N;;;;; +1D083;BYZANTINE MUSICAL SYMBOL KRATIMA NEO;So;0;L;;;;;N;;;;; +1D084;BYZANTINE MUSICAL SYMBOL APODERMA NEO;So;0;L;;;;;N;;;;; +1D085;BYZANTINE MUSICAL SYMBOL APLI;So;0;L;;;;;N;;;;; +1D086;BYZANTINE MUSICAL SYMBOL DIPLI;So;0;L;;;;;N;;;;; +1D087;BYZANTINE MUSICAL SYMBOL TRIPLI;So;0;L;;;;;N;;;;; +1D088;BYZANTINE MUSICAL SYMBOL TETRAPLI;So;0;L;;;;;N;;;;; +1D089;BYZANTINE MUSICAL SYMBOL KORONIS;So;0;L;;;;;N;;;;; +1D08A;BYZANTINE MUSICAL SYMBOL LEIMMA ENOS CHRONOU;So;0;L;;;;;N;;;;; +1D08B;BYZANTINE MUSICAL SYMBOL LEIMMA DYO CHRONON;So;0;L;;;;;N;;;;; +1D08C;BYZANTINE MUSICAL SYMBOL LEIMMA TRION CHRONON;So;0;L;;;;;N;;;;; +1D08D;BYZANTINE MUSICAL SYMBOL LEIMMA TESSARON CHRONON;So;0;L;;;;;N;;;;; +1D08E;BYZANTINE MUSICAL SYMBOL LEIMMA IMISEOS CHRONOU;So;0;L;;;;;N;;;;; +1D08F;BYZANTINE MUSICAL SYMBOL GORGON NEO ANO;So;0;L;;;;;N;;;;; +1D090;BYZANTINE MUSICAL SYMBOL GORGON PARESTIGMENON ARISTERA;So;0;L;;;;;N;;;;; +1D091;BYZANTINE MUSICAL SYMBOL GORGON PARESTIGMENON DEXIA;So;0;L;;;;;N;;;;; +1D092;BYZANTINE MUSICAL SYMBOL DIGORGON;So;0;L;;;;;N;;;;; +1D093;BYZANTINE MUSICAL SYMBOL DIGORGON PARESTIGMENON ARISTERA KATO;So;0;L;;;;;N;;;;; +1D094;BYZANTINE MUSICAL SYMBOL DIGORGON PARESTIGMENON ARISTERA ANO;So;0;L;;;;;N;;;;; +1D095;BYZANTINE MUSICAL SYMBOL DIGORGON PARESTIGMENON DEXIA;So;0;L;;;;;N;;;;; +1D096;BYZANTINE MUSICAL SYMBOL TRIGORGON;So;0;L;;;;;N;;;;; +1D097;BYZANTINE MUSICAL SYMBOL ARGON;So;0;L;;;;;N;;;;; +1D098;BYZANTINE MUSICAL SYMBOL IMIDIARGON;So;0;L;;;;;N;;;;; +1D099;BYZANTINE MUSICAL SYMBOL DIARGON;So;0;L;;;;;N;;;;; +1D09A;BYZANTINE MUSICAL SYMBOL AGOGI POLI ARGI;So;0;L;;;;;N;;;;; +1D09B;BYZANTINE MUSICAL SYMBOL AGOGI ARGOTERI;So;0;L;;;;;N;;;;; +1D09C;BYZANTINE MUSICAL SYMBOL AGOGI ARGI;So;0;L;;;;;N;;;;; +1D09D;BYZANTINE MUSICAL SYMBOL AGOGI METRIA;So;0;L;;;;;N;;;;; +1D09E;BYZANTINE MUSICAL SYMBOL AGOGI MESI;So;0;L;;;;;N;;;;; +1D09F;BYZANTINE MUSICAL SYMBOL AGOGI GORGI;So;0;L;;;;;N;;;;; +1D0A0;BYZANTINE MUSICAL SYMBOL AGOGI GORGOTERI;So;0;L;;;;;N;;;;; +1D0A1;BYZANTINE MUSICAL SYMBOL AGOGI POLI GORGI;So;0;L;;;;;N;;;;; +1D0A2;BYZANTINE MUSICAL SYMBOL MARTYRIA PROTOS ICHOS;So;0;L;;;;;N;;;;; +1D0A3;BYZANTINE MUSICAL SYMBOL MARTYRIA ALLI PROTOS ICHOS;So;0;L;;;;;N;;;;; +1D0A4;BYZANTINE MUSICAL SYMBOL MARTYRIA DEYTEROS ICHOS;So;0;L;;;;;N;;;;; +1D0A5;BYZANTINE MUSICAL SYMBOL MARTYRIA ALLI DEYTEROS ICHOS;So;0;L;;;;;N;;;;; +1D0A6;BYZANTINE MUSICAL SYMBOL MARTYRIA TRITOS ICHOS;So;0;L;;;;;N;;;;; +1D0A7;BYZANTINE MUSICAL SYMBOL MARTYRIA TRIFONIAS;So;0;L;;;;;N;;;;; +1D0A8;BYZANTINE MUSICAL SYMBOL MARTYRIA TETARTOS ICHOS;So;0;L;;;;;N;;;;; +1D0A9;BYZANTINE MUSICAL SYMBOL MARTYRIA TETARTOS LEGETOS ICHOS;So;0;L;;;;;N;;;;; +1D0AA;BYZANTINE MUSICAL SYMBOL MARTYRIA LEGETOS ICHOS;So;0;L;;;;;N;;;;; +1D0AB;BYZANTINE MUSICAL SYMBOL MARTYRIA PLAGIOS ICHOS;So;0;L;;;;;N;;;;; +1D0AC;BYZANTINE MUSICAL SYMBOL ISAKIA TELOUS ICHIMATOS;So;0;L;;;;;N;;;;; +1D0AD;BYZANTINE MUSICAL SYMBOL APOSTROFOI TELOUS ICHIMATOS;So;0;L;;;;;N;;;;; +1D0AE;BYZANTINE MUSICAL SYMBOL FANEROSIS TETRAFONIAS;So;0;L;;;;;N;;;;; +1D0AF;BYZANTINE MUSICAL SYMBOL FANEROSIS MONOFONIAS;So;0;L;;;;;N;;;;; +1D0B0;BYZANTINE MUSICAL SYMBOL FANEROSIS DIFONIAS;So;0;L;;;;;N;;;;; +1D0B1;BYZANTINE MUSICAL SYMBOL MARTYRIA VARYS ICHOS;So;0;L;;;;;N;;;;; +1D0B2;BYZANTINE MUSICAL SYMBOL MARTYRIA PROTOVARYS ICHOS;So;0;L;;;;;N;;;;; +1D0B3;BYZANTINE MUSICAL SYMBOL MARTYRIA PLAGIOS TETARTOS ICHOS;So;0;L;;;;;N;;;;; +1D0B4;BYZANTINE MUSICAL SYMBOL GORTHMIKON N APLOUN;So;0;L;;;;;N;;;;; +1D0B5;BYZANTINE MUSICAL SYMBOL GORTHMIKON N DIPLOUN;So;0;L;;;;;N;;;;; +1D0B6;BYZANTINE MUSICAL SYMBOL ENARXIS KAI FTHORA VOU;So;0;L;;;;;N;;;;; +1D0B7;BYZANTINE MUSICAL SYMBOL IMIFONON;So;0;L;;;;;N;;;;; +1D0B8;BYZANTINE MUSICAL SYMBOL IMIFTHORON;So;0;L;;;;;N;;;;; +1D0B9;BYZANTINE MUSICAL SYMBOL FTHORA ARCHAION DEYTEROU ICHOU;So;0;L;;;;;N;;;;; +1D0BA;BYZANTINE MUSICAL SYMBOL FTHORA DIATONIKI PA;So;0;L;;;;;N;;;;; +1D0BB;BYZANTINE MUSICAL SYMBOL FTHORA DIATONIKI NANA;So;0;L;;;;;N;;;;; +1D0BC;BYZANTINE MUSICAL SYMBOL FTHORA NAOS ICHOS;So;0;L;;;;;N;;;;; +1D0BD;BYZANTINE MUSICAL SYMBOL FTHORA DIATONIKI DI;So;0;L;;;;;N;;;;; +1D0BE;BYZANTINE MUSICAL SYMBOL FTHORA SKLIRON DIATONON DI;So;0;L;;;;;N;;;;; +1D0BF;BYZANTINE MUSICAL SYMBOL FTHORA DIATONIKI KE;So;0;L;;;;;N;;;;; +1D0C0;BYZANTINE MUSICAL SYMBOL FTHORA DIATONIKI ZO;So;0;L;;;;;N;;;;; +1D0C1;BYZANTINE MUSICAL SYMBOL FTHORA DIATONIKI NI KATO;So;0;L;;;;;N;;;;; +1D0C2;BYZANTINE MUSICAL SYMBOL FTHORA DIATONIKI NI ANO;So;0;L;;;;;N;;;;; +1D0C3;BYZANTINE MUSICAL SYMBOL FTHORA MALAKON CHROMA DIFONIAS;So;0;L;;;;;N;;;;; +1D0C4;BYZANTINE MUSICAL SYMBOL FTHORA MALAKON CHROMA MONOFONIAS;So;0;L;;;;;N;;;;; +1D0C5;BYZANTINE MUSICAL SYMBOL FHTORA SKLIRON CHROMA VASIS;So;0;L;;;;;N;;;;; +1D0C6;BYZANTINE MUSICAL SYMBOL FTHORA SKLIRON CHROMA SYNAFI;So;0;L;;;;;N;;;;; +1D0C7;BYZANTINE MUSICAL SYMBOL FTHORA NENANO;So;0;L;;;;;N;;;;; +1D0C8;BYZANTINE MUSICAL SYMBOL CHROA ZYGOS;So;0;L;;;;;N;;;;; +1D0C9;BYZANTINE MUSICAL SYMBOL CHROA KLITON;So;0;L;;;;;N;;;;; +1D0CA;BYZANTINE MUSICAL SYMBOL CHROA SPATHI;So;0;L;;;;;N;;;;; +1D0CB;BYZANTINE MUSICAL SYMBOL FTHORA I YFESIS TETARTIMORION;So;0;L;;;;;N;;;;; +1D0CC;BYZANTINE MUSICAL SYMBOL FTHORA ENARMONIOS ANTIFONIA;So;0;L;;;;;N;;;;; +1D0CD;BYZANTINE MUSICAL SYMBOL YFESIS TRITIMORION;So;0;L;;;;;N;;;;; +1D0CE;BYZANTINE MUSICAL SYMBOL DIESIS TRITIMORION;So;0;L;;;;;N;;;;; +1D0CF;BYZANTINE MUSICAL SYMBOL DIESIS TETARTIMORION;So;0;L;;;;;N;;;;; +1D0D0;BYZANTINE MUSICAL SYMBOL DIESIS APLI DYO DODEKATA;So;0;L;;;;;N;;;;; +1D0D1;BYZANTINE MUSICAL SYMBOL DIESIS MONOGRAMMOS TESSERA DODEKATA;So;0;L;;;;;N;;;;; +1D0D2;BYZANTINE MUSICAL SYMBOL DIESIS DIGRAMMOS EX DODEKATA;So;0;L;;;;;N;;;;; +1D0D3;BYZANTINE MUSICAL SYMBOL DIESIS TRIGRAMMOS OKTO DODEKATA;So;0;L;;;;;N;;;;; +1D0D4;BYZANTINE MUSICAL SYMBOL YFESIS APLI DYO DODEKATA;So;0;L;;;;;N;;;;; +1D0D5;BYZANTINE MUSICAL SYMBOL YFESIS MONOGRAMMOS TESSERA DODEKATA;So;0;L;;;;;N;;;;; +1D0D6;BYZANTINE MUSICAL SYMBOL YFESIS DIGRAMMOS EX DODEKATA;So;0;L;;;;;N;;;;; +1D0D7;BYZANTINE MUSICAL SYMBOL YFESIS TRIGRAMMOS OKTO DODEKATA;So;0;L;;;;;N;;;;; +1D0D8;BYZANTINE MUSICAL SYMBOL GENIKI DIESIS;So;0;L;;;;;N;;;;; +1D0D9;BYZANTINE MUSICAL SYMBOL GENIKI YFESIS;So;0;L;;;;;N;;;;; +1D0DA;BYZANTINE MUSICAL SYMBOL DIASTOLI APLI MIKRI;So;0;L;;;;;N;;;;; +1D0DB;BYZANTINE MUSICAL SYMBOL DIASTOLI APLI MEGALI;So;0;L;;;;;N;;;;; +1D0DC;BYZANTINE MUSICAL SYMBOL DIASTOLI DIPLI;So;0;L;;;;;N;;;;; +1D0DD;BYZANTINE MUSICAL SYMBOL DIASTOLI THESEOS;So;0;L;;;;;N;;;;; +1D0DE;BYZANTINE MUSICAL SYMBOL SIMANSIS THESEOS;So;0;L;;;;;N;;;;; +1D0DF;BYZANTINE MUSICAL SYMBOL SIMANSIS THESEOS DISIMOU;So;0;L;;;;;N;;;;; +1D0E0;BYZANTINE MUSICAL SYMBOL SIMANSIS THESEOS TRISIMOU;So;0;L;;;;;N;;;;; +1D0E1;BYZANTINE MUSICAL SYMBOL SIMANSIS THESEOS TETRASIMOU;So;0;L;;;;;N;;;;; +1D0E2;BYZANTINE MUSICAL SYMBOL SIMANSIS ARSEOS;So;0;L;;;;;N;;;;; +1D0E3;BYZANTINE MUSICAL SYMBOL SIMANSIS ARSEOS DISIMOU;So;0;L;;;;;N;;;;; +1D0E4;BYZANTINE MUSICAL SYMBOL SIMANSIS ARSEOS TRISIMOU;So;0;L;;;;;N;;;;; +1D0E5;BYZANTINE MUSICAL SYMBOL SIMANSIS ARSEOS TETRASIMOU;So;0;L;;;;;N;;;;; +1D0E6;BYZANTINE MUSICAL SYMBOL DIGRAMMA GG;So;0;L;;;;;N;;;;; +1D0E7;BYZANTINE MUSICAL SYMBOL DIFTOGGOS OU;So;0;L;;;;;N;;;;; +1D0E8;BYZANTINE MUSICAL SYMBOL STIGMA;So;0;L;;;;;N;;;;; +1D0E9;BYZANTINE MUSICAL SYMBOL ARKTIKO PA;So;0;L;;;;;N;;;;; +1D0EA;BYZANTINE MUSICAL SYMBOL ARKTIKO VOU;So;0;L;;;;;N;;;;; +1D0EB;BYZANTINE MUSICAL SYMBOL ARKTIKO GA;So;0;L;;;;;N;;;;; +1D0EC;BYZANTINE MUSICAL SYMBOL ARKTIKO DI;So;0;L;;;;;N;;;;; +1D0ED;BYZANTINE MUSICAL SYMBOL ARKTIKO KE;So;0;L;;;;;N;;;;; +1D0EE;BYZANTINE MUSICAL SYMBOL ARKTIKO ZO;So;0;L;;;;;N;;;;; +1D0EF;BYZANTINE MUSICAL SYMBOL ARKTIKO NI;So;0;L;;;;;N;;;;; +1D0F0;BYZANTINE MUSICAL SYMBOL KENTIMATA NEO MESO;So;0;L;;;;;N;;;;; +1D0F1;BYZANTINE MUSICAL SYMBOL KENTIMA NEO MESO;So;0;L;;;;;N;;;;; +1D0F2;BYZANTINE MUSICAL SYMBOL KENTIMATA NEO KATO;So;0;L;;;;;N;;;;; +1D0F3;BYZANTINE MUSICAL SYMBOL KENTIMA NEO KATO;So;0;L;;;;;N;;;;; +1D0F4;BYZANTINE MUSICAL SYMBOL KLASMA KATO;So;0;L;;;;;N;;;;; +1D0F5;BYZANTINE MUSICAL SYMBOL GORGON NEO KATO;So;0;L;;;;;N;;;;; +1D100;MUSICAL SYMBOL SINGLE BARLINE;So;0;L;;;;;N;;;;; +1D101;MUSICAL SYMBOL DOUBLE BARLINE;So;0;L;;;;;N;;;;; +1D102;MUSICAL SYMBOL FINAL BARLINE;So;0;L;;;;;N;;;;; +1D103;MUSICAL SYMBOL REVERSE FINAL BARLINE;So;0;L;;;;;N;;;;; +1D104;MUSICAL SYMBOL DASHED BARLINE;So;0;L;;;;;N;;;;; +1D105;MUSICAL SYMBOL SHORT BARLINE;So;0;L;;;;;N;;;;; +1D106;MUSICAL SYMBOL LEFT REPEAT SIGN;So;0;L;;;;;N;;;;; +1D107;MUSICAL SYMBOL RIGHT REPEAT SIGN;So;0;L;;;;;N;;;;; +1D108;MUSICAL SYMBOL REPEAT DOTS;So;0;L;;;;;N;;;;; +1D109;MUSICAL SYMBOL DAL SEGNO;So;0;L;;;;;N;;;;; +1D10A;MUSICAL SYMBOL DA CAPO;So;0;L;;;;;N;;;;; +1D10B;MUSICAL SYMBOL SEGNO;So;0;L;;;;;N;;;;; +1D10C;MUSICAL SYMBOL CODA;So;0;L;;;;;N;;;;; +1D10D;MUSICAL SYMBOL REPEATED FIGURE-1;So;0;L;;;;;N;;;;; +1D10E;MUSICAL SYMBOL REPEATED FIGURE-2;So;0;L;;;;;N;;;;; +1D10F;MUSICAL SYMBOL REPEATED FIGURE-3;So;0;L;;;;;N;;;;; +1D110;MUSICAL SYMBOL FERMATA;So;0;L;;;;;N;;;;; +1D111;MUSICAL SYMBOL FERMATA BELOW;So;0;L;;;;;N;;;;; +1D112;MUSICAL SYMBOL BREATH MARK;So;0;L;;;;;N;;;;; +1D113;MUSICAL SYMBOL CAESURA;So;0;L;;;;;N;;;;; +1D114;MUSICAL SYMBOL BRACE;So;0;L;;;;;N;;;;; +1D115;MUSICAL SYMBOL BRACKET;So;0;L;;;;;N;;;;; +1D116;MUSICAL SYMBOL ONE-LINE STAFF;So;0;L;;;;;N;;;;; +1D117;MUSICAL SYMBOL TWO-LINE STAFF;So;0;L;;;;;N;;;;; +1D118;MUSICAL SYMBOL THREE-LINE STAFF;So;0;L;;;;;N;;;;; +1D119;MUSICAL SYMBOL FOUR-LINE STAFF;So;0;L;;;;;N;;;;; +1D11A;MUSICAL SYMBOL FIVE-LINE STAFF;So;0;L;;;;;N;;;;; +1D11B;MUSICAL SYMBOL SIX-LINE STAFF;So;0;L;;;;;N;;;;; +1D11C;MUSICAL SYMBOL SIX-STRING FRETBOARD;So;0;L;;;;;N;;;;; +1D11D;MUSICAL SYMBOL FOUR-STRING FRETBOARD;So;0;L;;;;;N;;;;; +1D11E;MUSICAL SYMBOL G CLEF;So;0;L;;;;;N;;;;; +1D11F;MUSICAL SYMBOL G CLEF OTTAVA ALTA;So;0;L;;;;;N;;;;; +1D120;MUSICAL SYMBOL G CLEF OTTAVA BASSA;So;0;L;;;;;N;;;;; +1D121;MUSICAL SYMBOL C CLEF;So;0;L;;;;;N;;;;; +1D122;MUSICAL SYMBOL F CLEF;So;0;L;;;;;N;;;;; +1D123;MUSICAL SYMBOL F CLEF OTTAVA ALTA;So;0;L;;;;;N;;;;; +1D124;MUSICAL SYMBOL F CLEF OTTAVA BASSA;So;0;L;;;;;N;;;;; +1D125;MUSICAL SYMBOL DRUM CLEF-1;So;0;L;;;;;N;;;;; +1D126;MUSICAL SYMBOL DRUM CLEF-2;So;0;L;;;;;N;;;;; +1D12A;MUSICAL SYMBOL DOUBLE SHARP;So;0;L;;;;;N;;;;; +1D12B;MUSICAL SYMBOL DOUBLE FLAT;So;0;L;;;;;N;;;;; +1D12C;MUSICAL SYMBOL FLAT UP;So;0;L;;;;;N;;;;; +1D12D;MUSICAL SYMBOL FLAT DOWN;So;0;L;;;;;N;;;;; +1D12E;MUSICAL SYMBOL NATURAL UP;So;0;L;;;;;N;;;;; +1D12F;MUSICAL SYMBOL NATURAL DOWN;So;0;L;;;;;N;;;;; +1D130;MUSICAL SYMBOL SHARP UP;So;0;L;;;;;N;;;;; +1D131;MUSICAL SYMBOL SHARP DOWN;So;0;L;;;;;N;;;;; +1D132;MUSICAL SYMBOL QUARTER TONE SHARP;So;0;L;;;;;N;;;;; +1D133;MUSICAL SYMBOL QUARTER TONE FLAT;So;0;L;;;;;N;;;;; +1D134;MUSICAL SYMBOL COMMON TIME;So;0;L;;;;;N;;;;; +1D135;MUSICAL SYMBOL CUT TIME;So;0;L;;;;;N;;;;; +1D136;MUSICAL SYMBOL OTTAVA ALTA;So;0;L;;;;;N;;;;; +1D137;MUSICAL SYMBOL OTTAVA BASSA;So;0;L;;;;;N;;;;; +1D138;MUSICAL SYMBOL QUINDICESIMA ALTA;So;0;L;;;;;N;;;;; +1D139;MUSICAL SYMBOL QUINDICESIMA BASSA;So;0;L;;;;;N;;;;; +1D13A;MUSICAL SYMBOL MULTI REST;So;0;L;;;;;N;;;;; +1D13B;MUSICAL SYMBOL WHOLE REST;So;0;L;;;;;N;;;;; +1D13C;MUSICAL SYMBOL HALF REST;So;0;L;;;;;N;;;;; +1D13D;MUSICAL SYMBOL QUARTER REST;So;0;L;;;;;N;;;;; +1D13E;MUSICAL SYMBOL EIGHTH REST;So;0;L;;;;;N;;;;; +1D13F;MUSICAL SYMBOL SIXTEENTH REST;So;0;L;;;;;N;;;;; +1D140;MUSICAL SYMBOL THIRTY-SECOND REST;So;0;L;;;;;N;;;;; +1D141;MUSICAL SYMBOL SIXTY-FOURTH REST;So;0;L;;;;;N;;;;; +1D142;MUSICAL SYMBOL ONE HUNDRED TWENTY-EIGHTH REST;So;0;L;;;;;N;;;;; +1D143;MUSICAL SYMBOL X NOTEHEAD;So;0;L;;;;;N;;;;; +1D144;MUSICAL SYMBOL PLUS NOTEHEAD;So;0;L;;;;;N;;;;; +1D145;MUSICAL SYMBOL CIRCLE X NOTEHEAD;So;0;L;;;;;N;;;;; +1D146;MUSICAL SYMBOL SQUARE NOTEHEAD WHITE;So;0;L;;;;;N;;;;; +1D147;MUSICAL SYMBOL SQUARE NOTEHEAD BLACK;So;0;L;;;;;N;;;;; +1D148;MUSICAL SYMBOL TRIANGLE NOTEHEAD UP WHITE;So;0;L;;;;;N;;;;; +1D149;MUSICAL SYMBOL TRIANGLE NOTEHEAD UP BLACK;So;0;L;;;;;N;;;;; +1D14A;MUSICAL SYMBOL TRIANGLE NOTEHEAD LEFT WHITE;So;0;L;;;;;N;;;;; +1D14B;MUSICAL SYMBOL TRIANGLE NOTEHEAD LEFT BLACK;So;0;L;;;;;N;;;;; +1D14C;MUSICAL SYMBOL TRIANGLE NOTEHEAD RIGHT WHITE;So;0;L;;;;;N;;;;; +1D14D;MUSICAL SYMBOL TRIANGLE NOTEHEAD RIGHT BLACK;So;0;L;;;;;N;;;;; +1D14E;MUSICAL SYMBOL TRIANGLE NOTEHEAD DOWN WHITE;So;0;L;;;;;N;;;;; +1D14F;MUSICAL SYMBOL TRIANGLE NOTEHEAD DOWN BLACK;So;0;L;;;;;N;;;;; +1D150;MUSICAL SYMBOL TRIANGLE NOTEHEAD UP RIGHT WHITE;So;0;L;;;;;N;;;;; +1D151;MUSICAL SYMBOL TRIANGLE NOTEHEAD UP RIGHT BLACK;So;0;L;;;;;N;;;;; +1D152;MUSICAL SYMBOL MOON NOTEHEAD WHITE;So;0;L;;;;;N;;;;; +1D153;MUSICAL SYMBOL MOON NOTEHEAD BLACK;So;0;L;;;;;N;;;;; +1D154;MUSICAL SYMBOL TRIANGLE-ROUND NOTEHEAD DOWN WHITE;So;0;L;;;;;N;;;;; +1D155;MUSICAL SYMBOL TRIANGLE-ROUND NOTEHEAD DOWN BLACK;So;0;L;;;;;N;;;;; +1D156;MUSICAL SYMBOL PARENTHESIS NOTEHEAD;So;0;L;;;;;N;;;;; +1D157;MUSICAL SYMBOL VOID NOTEHEAD;So;0;L;;;;;N;;;;; +1D158;MUSICAL SYMBOL NOTEHEAD BLACK;So;0;L;;;;;N;;;;; +1D159;MUSICAL SYMBOL NULL NOTEHEAD;So;0;L;;;;;N;;;;; +1D15A;MUSICAL SYMBOL CLUSTER NOTEHEAD WHITE;So;0;L;;;;;N;;;;; +1D15B;MUSICAL SYMBOL CLUSTER NOTEHEAD BLACK;So;0;L;;;;;N;;;;; +1D15C;MUSICAL SYMBOL BREVE;So;0;L;;;;;N;;;;; +1D15D;MUSICAL SYMBOL WHOLE NOTE;So;0;L;;;;;N;;;;; +1D15E;MUSICAL SYMBOL HALF NOTE;So;0;L;1D157 1D165;;;;N;;;;; +1D15F;MUSICAL SYMBOL QUARTER NOTE;So;0;L;1D158 1D165;;;;N;;;;; +1D160;MUSICAL SYMBOL EIGHTH NOTE;So;0;L;1D15F 1D16E;;;;N;;;;; +1D161;MUSICAL SYMBOL SIXTEENTH NOTE;So;0;L;1D15F 1D16F;;;;N;;;;; +1D162;MUSICAL SYMBOL THIRTY-SECOND NOTE;So;0;L;1D15F 1D170;;;;N;;;;; +1D163;MUSICAL SYMBOL SIXTY-FOURTH NOTE;So;0;L;1D15F 1D171;;;;N;;;;; +1D164;MUSICAL SYMBOL ONE HUNDRED TWENTY-EIGHTH NOTE;So;0;L;1D15F 1D172;;;;N;;;;; +1D165;MUSICAL SYMBOL COMBINING STEM;Mc;216;L;;;;;N;;;;; +1D166;MUSICAL SYMBOL COMBINING SPRECHGESANG STEM;Mc;216;L;;;;;N;;;;; +1D167;MUSICAL SYMBOL COMBINING TREMOLO-1;Mn;1;NSM;;;;;N;;;;; +1D168;MUSICAL SYMBOL COMBINING TREMOLO-2;Mn;1;NSM;;;;;N;;;;; +1D169;MUSICAL SYMBOL COMBINING TREMOLO-3;Mn;1;NSM;;;;;N;;;;; +1D16A;MUSICAL SYMBOL FINGERED TREMOLO-1;So;0;L;;;;;N;;;;; +1D16B;MUSICAL SYMBOL FINGERED TREMOLO-2;So;0;L;;;;;N;;;;; +1D16C;MUSICAL SYMBOL FINGERED TREMOLO-3;So;0;L;;;;;N;;;;; +1D16D;MUSICAL SYMBOL COMBINING AUGMENTATION DOT;Mc;226;L;;;;;N;;;;; +1D16E;MUSICAL SYMBOL COMBINING FLAG-1;Mc;216;L;;;;;N;;;;; +1D16F;MUSICAL SYMBOL COMBINING FLAG-2;Mc;216;L;;;;;N;;;;; +1D170;MUSICAL SYMBOL COMBINING FLAG-3;Mc;216;L;;;;;N;;;;; +1D171;MUSICAL SYMBOL COMBINING FLAG-4;Mc;216;L;;;;;N;;;;; +1D172;MUSICAL SYMBOL COMBINING FLAG-5;Mc;216;L;;;;;N;;;;; +1D173;MUSICAL SYMBOL BEGIN BEAM;Cf;0;BN;;;;;N;;;;; +1D174;MUSICAL SYMBOL END BEAM;Cf;0;BN;;;;;N;;;;; +1D175;MUSICAL SYMBOL BEGIN TIE;Cf;0;BN;;;;;N;;;;; +1D176;MUSICAL SYMBOL END TIE;Cf;0;BN;;;;;N;;;;; +1D177;MUSICAL SYMBOL BEGIN SLUR;Cf;0;BN;;;;;N;;;;; +1D178;MUSICAL SYMBOL END SLUR;Cf;0;BN;;;;;N;;;;; +1D179;MUSICAL SYMBOL BEGIN PHRASE;Cf;0;BN;;;;;N;;;;; +1D17A;MUSICAL SYMBOL END PHRASE;Cf;0;BN;;;;;N;;;;; +1D17B;MUSICAL SYMBOL COMBINING ACCENT;Mn;220;NSM;;;;;N;;;;; +1D17C;MUSICAL SYMBOL COMBINING STACCATO;Mn;220;NSM;;;;;N;;;;; +1D17D;MUSICAL SYMBOL COMBINING TENUTO;Mn;220;NSM;;;;;N;;;;; +1D17E;MUSICAL SYMBOL COMBINING STACCATISSIMO;Mn;220;NSM;;;;;N;;;;; +1D17F;MUSICAL SYMBOL COMBINING MARCATO;Mn;220;NSM;;;;;N;;;;; +1D180;MUSICAL SYMBOL COMBINING MARCATO-STACCATO;Mn;220;NSM;;;;;N;;;;; +1D181;MUSICAL SYMBOL COMBINING ACCENT-STACCATO;Mn;220;NSM;;;;;N;;;;; +1D182;MUSICAL SYMBOL COMBINING LOURE;Mn;220;NSM;;;;;N;;;;; +1D183;MUSICAL SYMBOL ARPEGGIATO UP;So;0;L;;;;;N;;;;; +1D184;MUSICAL SYMBOL ARPEGGIATO DOWN;So;0;L;;;;;N;;;;; +1D185;MUSICAL SYMBOL COMBINING DOIT;Mn;230;NSM;;;;;N;;;;; +1D186;MUSICAL SYMBOL COMBINING RIP;Mn;230;NSM;;;;;N;;;;; +1D187;MUSICAL SYMBOL COMBINING FLIP;Mn;230;NSM;;;;;N;;;;; +1D188;MUSICAL SYMBOL COMBINING SMEAR;Mn;230;NSM;;;;;N;;;;; +1D189;MUSICAL SYMBOL COMBINING BEND;Mn;230;NSM;;;;;N;;;;; +1D18A;MUSICAL SYMBOL COMBINING DOUBLE TONGUE;Mn;220;NSM;;;;;N;;;;; +1D18B;MUSICAL SYMBOL COMBINING TRIPLE TONGUE;Mn;220;NSM;;;;;N;;;;; +1D18C;MUSICAL SYMBOL RINFORZANDO;So;0;L;;;;;N;;;;; +1D18D;MUSICAL SYMBOL SUBITO;So;0;L;;;;;N;;;;; +1D18E;MUSICAL SYMBOL Z;So;0;L;;;;;N;;;;; +1D18F;MUSICAL SYMBOL PIANO;So;0;L;;;;;N;;;;; +1D190;MUSICAL SYMBOL MEZZO;So;0;L;;;;;N;;;;; +1D191;MUSICAL SYMBOL FORTE;So;0;L;;;;;N;;;;; +1D192;MUSICAL SYMBOL CRESCENDO;So;0;L;;;;;N;;;;; +1D193;MUSICAL SYMBOL DECRESCENDO;So;0;L;;;;;N;;;;; +1D194;MUSICAL SYMBOL GRACE NOTE SLASH;So;0;L;;;;;N;;;;; +1D195;MUSICAL SYMBOL GRACE NOTE NO SLASH;So;0;L;;;;;N;;;;; +1D196;MUSICAL SYMBOL TR;So;0;L;;;;;N;;;;; +1D197;MUSICAL SYMBOL TURN;So;0;L;;;;;N;;;;; +1D198;MUSICAL SYMBOL INVERTED TURN;So;0;L;;;;;N;;;;; +1D199;MUSICAL SYMBOL TURN SLASH;So;0;L;;;;;N;;;;; +1D19A;MUSICAL SYMBOL TURN UP;So;0;L;;;;;N;;;;; +1D19B;MUSICAL SYMBOL ORNAMENT STROKE-1;So;0;L;;;;;N;;;;; +1D19C;MUSICAL SYMBOL ORNAMENT STROKE-2;So;0;L;;;;;N;;;;; +1D19D;MUSICAL SYMBOL ORNAMENT STROKE-3;So;0;L;;;;;N;;;;; +1D19E;MUSICAL SYMBOL ORNAMENT STROKE-4;So;0;L;;;;;N;;;;; +1D19F;MUSICAL SYMBOL ORNAMENT STROKE-5;So;0;L;;;;;N;;;;; +1D1A0;MUSICAL SYMBOL ORNAMENT STROKE-6;So;0;L;;;;;N;;;;; +1D1A1;MUSICAL SYMBOL ORNAMENT STROKE-7;So;0;L;;;;;N;;;;; +1D1A2;MUSICAL SYMBOL ORNAMENT STROKE-8;So;0;L;;;;;N;;;;; +1D1A3;MUSICAL SYMBOL ORNAMENT STROKE-9;So;0;L;;;;;N;;;;; +1D1A4;MUSICAL SYMBOL ORNAMENT STROKE-10;So;0;L;;;;;N;;;;; +1D1A5;MUSICAL SYMBOL ORNAMENT STROKE-11;So;0;L;;;;;N;;;;; +1D1A6;MUSICAL SYMBOL HAUPTSTIMME;So;0;L;;;;;N;;;;; +1D1A7;MUSICAL SYMBOL NEBENSTIMME;So;0;L;;;;;N;;;;; +1D1A8;MUSICAL SYMBOL END OF STIMME;So;0;L;;;;;N;;;;; +1D1A9;MUSICAL SYMBOL DEGREE SLASH;So;0;L;;;;;N;;;;; +1D1AA;MUSICAL SYMBOL COMBINING DOWN BOW;Mn;230;NSM;;;;;N;;;;; +1D1AB;MUSICAL SYMBOL COMBINING UP BOW;Mn;230;NSM;;;;;N;;;;; +1D1AC;MUSICAL SYMBOL COMBINING HARMONIC;Mn;230;NSM;;;;;N;;;;; +1D1AD;MUSICAL SYMBOL COMBINING SNAP PIZZICATO;Mn;230;NSM;;;;;N;;;;; +1D1AE;MUSICAL SYMBOL PEDAL MARK;So;0;L;;;;;N;;;;; +1D1AF;MUSICAL SYMBOL PEDAL UP MARK;So;0;L;;;;;N;;;;; +1D1B0;MUSICAL SYMBOL HALF PEDAL MARK;So;0;L;;;;;N;;;;; +1D1B1;MUSICAL SYMBOL GLISSANDO UP;So;0;L;;;;;N;;;;; +1D1B2;MUSICAL SYMBOL GLISSANDO DOWN;So;0;L;;;;;N;;;;; +1D1B3;MUSICAL SYMBOL WITH FINGERNAILS;So;0;L;;;;;N;;;;; +1D1B4;MUSICAL SYMBOL DAMP;So;0;L;;;;;N;;;;; +1D1B5;MUSICAL SYMBOL DAMP ALL;So;0;L;;;;;N;;;;; +1D1B6;MUSICAL SYMBOL MAXIMA;So;0;L;;;;;N;;;;; +1D1B7;MUSICAL SYMBOL LONGA;So;0;L;;;;;N;;;;; +1D1B8;MUSICAL SYMBOL BREVIS;So;0;L;;;;;N;;;;; +1D1B9;MUSICAL SYMBOL SEMIBREVIS WHITE;So;0;L;;;;;N;;;;; +1D1BA;MUSICAL SYMBOL SEMIBREVIS BLACK;So;0;L;;;;;N;;;;; +1D1BB;MUSICAL SYMBOL MINIMA;So;0;L;1D1B9 1D165;;;;N;;;;; +1D1BC;MUSICAL SYMBOL MINIMA BLACK;So;0;L;1D1BA 1D165;;;;N;;;;; +1D1BD;MUSICAL SYMBOL SEMIMINIMA WHITE;So;0;L;1D1BB 1D16E;;;;N;;;;; +1D1BE;MUSICAL SYMBOL SEMIMINIMA BLACK;So;0;L;1D1BC 1D16E;;;;N;;;;; +1D1BF;MUSICAL SYMBOL FUSA WHITE;So;0;L;1D1BB 1D16F;;;;N;;;;; +1D1C0;MUSICAL SYMBOL FUSA BLACK;So;0;L;1D1BC 1D16F;;;;N;;;;; +1D1C1;MUSICAL SYMBOL LONGA PERFECTA REST;So;0;L;;;;;N;;;;; +1D1C2;MUSICAL SYMBOL LONGA IMPERFECTA REST;So;0;L;;;;;N;;;;; +1D1C3;MUSICAL SYMBOL BREVIS REST;So;0;L;;;;;N;;;;; +1D1C4;MUSICAL SYMBOL SEMIBREVIS REST;So;0;L;;;;;N;;;;; +1D1C5;MUSICAL SYMBOL MINIMA REST;So;0;L;;;;;N;;;;; +1D1C6;MUSICAL SYMBOL SEMIMINIMA REST;So;0;L;;;;;N;;;;; +1D1C7;MUSICAL SYMBOL TEMPUS PERFECTUM CUM PROLATIONE PERFECTA;So;0;L;;;;;N;;;;; +1D1C8;MUSICAL SYMBOL TEMPUS PERFECTUM CUM PROLATIONE IMPERFECTA;So;0;L;;;;;N;;;;; +1D1C9;MUSICAL SYMBOL TEMPUS PERFECTUM CUM PROLATIONE PERFECTA DIMINUTION-1;So;0;L;;;;;N;;;;; +1D1CA;MUSICAL SYMBOL TEMPUS IMPERFECTUM CUM PROLATIONE PERFECTA;So;0;L;;;;;N;;;;; +1D1CB;MUSICAL SYMBOL TEMPUS IMPERFECTUM CUM PROLATIONE IMPERFECTA;So;0;L;;;;;N;;;;; +1D1CC;MUSICAL SYMBOL TEMPUS IMPERFECTUM CUM PROLATIONE IMPERFECTA DIMINUTION-1;So;0;L;;;;;N;;;;; +1D1CD;MUSICAL SYMBOL TEMPUS IMPERFECTUM CUM PROLATIONE IMPERFECTA DIMINUTION-2;So;0;L;;;;;N;;;;; +1D1CE;MUSICAL SYMBOL TEMPUS IMPERFECTUM CUM PROLATIONE IMPERFECTA DIMINUTION-3;So;0;L;;;;;N;;;;; +1D1CF;MUSICAL SYMBOL CROIX;So;0;L;;;;;N;;;;; +1D1D0;MUSICAL SYMBOL GREGORIAN C CLEF;So;0;L;;;;;N;;;;; +1D1D1;MUSICAL SYMBOL GREGORIAN F CLEF;So;0;L;;;;;N;;;;; +1D1D2;MUSICAL SYMBOL SQUARE B;So;0;L;;;;;N;;;;; +1D1D3;MUSICAL SYMBOL VIRGA;So;0;L;;;;;N;;;;; +1D1D4;MUSICAL SYMBOL PODATUS;So;0;L;;;;;N;;;;; +1D1D5;MUSICAL SYMBOL CLIVIS;So;0;L;;;;;N;;;;; +1D1D6;MUSICAL SYMBOL SCANDICUS;So;0;L;;;;;N;;;;; +1D1D7;MUSICAL SYMBOL CLIMACUS;So;0;L;;;;;N;;;;; +1D1D8;MUSICAL SYMBOL TORCULUS;So;0;L;;;;;N;;;;; +1D1D9;MUSICAL SYMBOL PORRECTUS;So;0;L;;;;;N;;;;; +1D1DA;MUSICAL SYMBOL PORRECTUS FLEXUS;So;0;L;;;;;N;;;;; +1D1DB;MUSICAL SYMBOL SCANDICUS FLEXUS;So;0;L;;;;;N;;;;; +1D1DC;MUSICAL SYMBOL TORCULUS RESUPINUS;So;0;L;;;;;N;;;;; +1D1DD;MUSICAL SYMBOL PES SUBPUNCTIS;So;0;L;;;;;N;;;;; +1D400;MATHEMATICAL BOLD CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;; +1D401;MATHEMATICAL BOLD CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;; +1D402;MATHEMATICAL BOLD CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;; +1D403;MATHEMATICAL BOLD CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;; +1D404;MATHEMATICAL BOLD CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;; +1D405;MATHEMATICAL BOLD CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;; +1D406;MATHEMATICAL BOLD CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;; +1D407;MATHEMATICAL BOLD CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;; +1D408;MATHEMATICAL BOLD CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;; +1D409;MATHEMATICAL BOLD CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;; +1D40A;MATHEMATICAL BOLD CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;; +1D40B;MATHEMATICAL BOLD CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;; +1D40C;MATHEMATICAL BOLD CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;; +1D40D;MATHEMATICAL BOLD CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;; +1D40E;MATHEMATICAL BOLD CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;; +1D40F;MATHEMATICAL BOLD CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;; +1D410;MATHEMATICAL BOLD CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;; +1D411;MATHEMATICAL BOLD CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;; +1D412;MATHEMATICAL BOLD CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;; +1D413;MATHEMATICAL BOLD CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;; +1D414;MATHEMATICAL BOLD CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;; +1D415;MATHEMATICAL BOLD CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;; +1D416;MATHEMATICAL BOLD CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;; +1D417;MATHEMATICAL BOLD CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;; +1D418;MATHEMATICAL BOLD CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;; +1D419;MATHEMATICAL BOLD CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;; +1D41A;MATHEMATICAL BOLD SMALL A;Ll;0;L;<font> 0061;;;;N;;;;; +1D41B;MATHEMATICAL BOLD SMALL B;Ll;0;L;<font> 0062;;;;N;;;;; +1D41C;MATHEMATICAL BOLD SMALL C;Ll;0;L;<font> 0063;;;;N;;;;; +1D41D;MATHEMATICAL BOLD SMALL D;Ll;0;L;<font> 0064;;;;N;;;;; +1D41E;MATHEMATICAL BOLD SMALL E;Ll;0;L;<font> 0065;;;;N;;;;; +1D41F;MATHEMATICAL BOLD SMALL F;Ll;0;L;<font> 0066;;;;N;;;;; +1D420;MATHEMATICAL BOLD SMALL G;Ll;0;L;<font> 0067;;;;N;;;;; +1D421;MATHEMATICAL BOLD SMALL H;Ll;0;L;<font> 0068;;;;N;;;;; +1D422;MATHEMATICAL BOLD SMALL I;Ll;0;L;<font> 0069;;;;N;;;;; +1D423;MATHEMATICAL BOLD SMALL J;Ll;0;L;<font> 006A;;;;N;;;;; +1D424;MATHEMATICAL BOLD SMALL K;Ll;0;L;<font> 006B;;;;N;;;;; +1D425;MATHEMATICAL BOLD SMALL L;Ll;0;L;<font> 006C;;;;N;;;;; +1D426;MATHEMATICAL BOLD SMALL M;Ll;0;L;<font> 006D;;;;N;;;;; +1D427;MATHEMATICAL BOLD SMALL N;Ll;0;L;<font> 006E;;;;N;;;;; +1D428;MATHEMATICAL BOLD SMALL O;Ll;0;L;<font> 006F;;;;N;;;;; +1D429;MATHEMATICAL BOLD SMALL P;Ll;0;L;<font> 0070;;;;N;;;;; +1D42A;MATHEMATICAL BOLD SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;; +1D42B;MATHEMATICAL BOLD SMALL R;Ll;0;L;<font> 0072;;;;N;;;;; +1D42C;MATHEMATICAL BOLD SMALL S;Ll;0;L;<font> 0073;;;;N;;;;; +1D42D;MATHEMATICAL BOLD SMALL T;Ll;0;L;<font> 0074;;;;N;;;;; +1D42E;MATHEMATICAL BOLD SMALL U;Ll;0;L;<font> 0075;;;;N;;;;; +1D42F;MATHEMATICAL BOLD SMALL V;Ll;0;L;<font> 0076;;;;N;;;;; +1D430;MATHEMATICAL BOLD SMALL W;Ll;0;L;<font> 0077;;;;N;;;;; +1D431;MATHEMATICAL BOLD SMALL X;Ll;0;L;<font> 0078;;;;N;;;;; +1D432;MATHEMATICAL BOLD SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;; +1D433;MATHEMATICAL BOLD SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;; +1D434;MATHEMATICAL ITALIC CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;; +1D435;MATHEMATICAL ITALIC CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;; +1D436;MATHEMATICAL ITALIC CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;; +1D437;MATHEMATICAL ITALIC CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;; +1D438;MATHEMATICAL ITALIC CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;; +1D439;MATHEMATICAL ITALIC CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;; +1D43A;MATHEMATICAL ITALIC CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;; +1D43B;MATHEMATICAL ITALIC CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;; +1D43C;MATHEMATICAL ITALIC CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;; +1D43D;MATHEMATICAL ITALIC CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;; +1D43E;MATHEMATICAL ITALIC CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;; +1D43F;MATHEMATICAL ITALIC CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;; +1D440;MATHEMATICAL ITALIC CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;; +1D441;MATHEMATICAL ITALIC CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;; +1D442;MATHEMATICAL ITALIC CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;; +1D443;MATHEMATICAL ITALIC CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;; +1D444;MATHEMATICAL ITALIC CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;; +1D445;MATHEMATICAL ITALIC CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;; +1D446;MATHEMATICAL ITALIC CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;; +1D447;MATHEMATICAL ITALIC CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;; +1D448;MATHEMATICAL ITALIC CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;; +1D449;MATHEMATICAL ITALIC CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;; +1D44A;MATHEMATICAL ITALIC CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;; +1D44B;MATHEMATICAL ITALIC CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;; +1D44C;MATHEMATICAL ITALIC CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;; +1D44D;MATHEMATICAL ITALIC CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;; +1D44E;MATHEMATICAL ITALIC SMALL A;Ll;0;L;<font> 0061;;;;N;;;;; +1D44F;MATHEMATICAL ITALIC SMALL B;Ll;0;L;<font> 0062;;;;N;;;;; +1D450;MATHEMATICAL ITALIC SMALL C;Ll;0;L;<font> 0063;;;;N;;;;; +1D451;MATHEMATICAL ITALIC SMALL D;Ll;0;L;<font> 0064;;;;N;;;;; +1D452;MATHEMATICAL ITALIC SMALL E;Ll;0;L;<font> 0065;;;;N;;;;; +1D453;MATHEMATICAL ITALIC SMALL F;Ll;0;L;<font> 0066;;;;N;;;;; +1D454;MATHEMATICAL ITALIC SMALL G;Ll;0;L;<font> 0067;;;;N;;;;; +1D456;MATHEMATICAL ITALIC SMALL I;Ll;0;L;<font> 0069;;;;N;;;;; +1D457;MATHEMATICAL ITALIC SMALL J;Ll;0;L;<font> 006A;;;;N;;;;; +1D458;MATHEMATICAL ITALIC SMALL K;Ll;0;L;<font> 006B;;;;N;;;;; +1D459;MATHEMATICAL ITALIC SMALL L;Ll;0;L;<font> 006C;;;;N;;;;; +1D45A;MATHEMATICAL ITALIC SMALL M;Ll;0;L;<font> 006D;;;;N;;;;; +1D45B;MATHEMATICAL ITALIC SMALL N;Ll;0;L;<font> 006E;;;;N;;;;; +1D45C;MATHEMATICAL ITALIC SMALL O;Ll;0;L;<font> 006F;;;;N;;;;; +1D45D;MATHEMATICAL ITALIC SMALL P;Ll;0;L;<font> 0070;;;;N;;;;; +1D45E;MATHEMATICAL ITALIC SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;; +1D45F;MATHEMATICAL ITALIC SMALL R;Ll;0;L;<font> 0072;;;;N;;;;; +1D460;MATHEMATICAL ITALIC SMALL S;Ll;0;L;<font> 0073;;;;N;;;;; +1D461;MATHEMATICAL ITALIC SMALL T;Ll;0;L;<font> 0074;;;;N;;;;; +1D462;MATHEMATICAL ITALIC SMALL U;Ll;0;L;<font> 0075;;;;N;;;;; +1D463;MATHEMATICAL ITALIC SMALL V;Ll;0;L;<font> 0076;;;;N;;;;; +1D464;MATHEMATICAL ITALIC SMALL W;Ll;0;L;<font> 0077;;;;N;;;;; +1D465;MATHEMATICAL ITALIC SMALL X;Ll;0;L;<font> 0078;;;;N;;;;; +1D466;MATHEMATICAL ITALIC SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;; +1D467;MATHEMATICAL ITALIC SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;; +1D468;MATHEMATICAL BOLD ITALIC CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;; +1D469;MATHEMATICAL BOLD ITALIC CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;; +1D46A;MATHEMATICAL BOLD ITALIC CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;; +1D46B;MATHEMATICAL BOLD ITALIC CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;; +1D46C;MATHEMATICAL BOLD ITALIC CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;; +1D46D;MATHEMATICAL BOLD ITALIC CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;; +1D46E;MATHEMATICAL BOLD ITALIC CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;; +1D46F;MATHEMATICAL BOLD ITALIC CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;; +1D470;MATHEMATICAL BOLD ITALIC CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;; +1D471;MATHEMATICAL BOLD ITALIC CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;; +1D472;MATHEMATICAL BOLD ITALIC CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;; +1D473;MATHEMATICAL BOLD ITALIC CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;; +1D474;MATHEMATICAL BOLD ITALIC CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;; +1D475;MATHEMATICAL BOLD ITALIC CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;; +1D476;MATHEMATICAL BOLD ITALIC CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;; +1D477;MATHEMATICAL BOLD ITALIC CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;; +1D478;MATHEMATICAL BOLD ITALIC CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;; +1D479;MATHEMATICAL BOLD ITALIC CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;; +1D47A;MATHEMATICAL BOLD ITALIC CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;; +1D47B;MATHEMATICAL BOLD ITALIC CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;; +1D47C;MATHEMATICAL BOLD ITALIC CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;; +1D47D;MATHEMATICAL BOLD ITALIC CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;; +1D47E;MATHEMATICAL BOLD ITALIC CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;; +1D47F;MATHEMATICAL BOLD ITALIC CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;; +1D480;MATHEMATICAL BOLD ITALIC CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;; +1D481;MATHEMATICAL BOLD ITALIC CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;; +1D482;MATHEMATICAL BOLD ITALIC SMALL A;Ll;0;L;<font> 0061;;;;N;;;;; +1D483;MATHEMATICAL BOLD ITALIC SMALL B;Ll;0;L;<font> 0062;;;;N;;;;; +1D484;MATHEMATICAL BOLD ITALIC SMALL C;Ll;0;L;<font> 0063;;;;N;;;;; +1D485;MATHEMATICAL BOLD ITALIC SMALL D;Ll;0;L;<font> 0064;;;;N;;;;; +1D486;MATHEMATICAL BOLD ITALIC SMALL E;Ll;0;L;<font> 0065;;;;N;;;;; +1D487;MATHEMATICAL BOLD ITALIC SMALL F;Ll;0;L;<font> 0066;;;;N;;;;; +1D488;MATHEMATICAL BOLD ITALIC SMALL G;Ll;0;L;<font> 0067;;;;N;;;;; +1D489;MATHEMATICAL BOLD ITALIC SMALL H;Ll;0;L;<font> 0068;;;;N;;;;; +1D48A;MATHEMATICAL BOLD ITALIC SMALL I;Ll;0;L;<font> 0069;;;;N;;;;; +1D48B;MATHEMATICAL BOLD ITALIC SMALL J;Ll;0;L;<font> 006A;;;;N;;;;; +1D48C;MATHEMATICAL BOLD ITALIC SMALL K;Ll;0;L;<font> 006B;;;;N;;;;; +1D48D;MATHEMATICAL BOLD ITALIC SMALL L;Ll;0;L;<font> 006C;;;;N;;;;; +1D48E;MATHEMATICAL BOLD ITALIC SMALL M;Ll;0;L;<font> 006D;;;;N;;;;; +1D48F;MATHEMATICAL BOLD ITALIC SMALL N;Ll;0;L;<font> 006E;;;;N;;;;; +1D490;MATHEMATICAL BOLD ITALIC SMALL O;Ll;0;L;<font> 006F;;;;N;;;;; +1D491;MATHEMATICAL BOLD ITALIC SMALL P;Ll;0;L;<font> 0070;;;;N;;;;; +1D492;MATHEMATICAL BOLD ITALIC SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;; +1D493;MATHEMATICAL BOLD ITALIC SMALL R;Ll;0;L;<font> 0072;;;;N;;;;; +1D494;MATHEMATICAL BOLD ITALIC SMALL S;Ll;0;L;<font> 0073;;;;N;;;;; +1D495;MATHEMATICAL BOLD ITALIC SMALL T;Ll;0;L;<font> 0074;;;;N;;;;; +1D496;MATHEMATICAL BOLD ITALIC SMALL U;Ll;0;L;<font> 0075;;;;N;;;;; +1D497;MATHEMATICAL BOLD ITALIC SMALL V;Ll;0;L;<font> 0076;;;;N;;;;; +1D498;MATHEMATICAL BOLD ITALIC SMALL W;Ll;0;L;<font> 0077;;;;N;;;;; +1D499;MATHEMATICAL BOLD ITALIC SMALL X;Ll;0;L;<font> 0078;;;;N;;;;; +1D49A;MATHEMATICAL BOLD ITALIC SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;; +1D49B;MATHEMATICAL BOLD ITALIC SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;; +1D49C;MATHEMATICAL SCRIPT CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;; +1D49E;MATHEMATICAL SCRIPT CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;; +1D49F;MATHEMATICAL SCRIPT CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;; +1D4A2;MATHEMATICAL SCRIPT CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;; +1D4A5;MATHEMATICAL SCRIPT CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;; +1D4A6;MATHEMATICAL SCRIPT CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;; +1D4A9;MATHEMATICAL SCRIPT CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;; +1D4AA;MATHEMATICAL SCRIPT CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;; +1D4AB;MATHEMATICAL SCRIPT CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;; +1D4AC;MATHEMATICAL SCRIPT CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;; +1D4AE;MATHEMATICAL SCRIPT CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;; +1D4AF;MATHEMATICAL SCRIPT CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;; +1D4B0;MATHEMATICAL SCRIPT CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;; +1D4B1;MATHEMATICAL SCRIPT CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;; +1D4B2;MATHEMATICAL SCRIPT CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;; +1D4B3;MATHEMATICAL SCRIPT CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;; +1D4B4;MATHEMATICAL SCRIPT CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;; +1D4B5;MATHEMATICAL SCRIPT CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;; +1D4B6;MATHEMATICAL SCRIPT SMALL A;Ll;0;L;<font> 0061;;;;N;;;;; +1D4B7;MATHEMATICAL SCRIPT SMALL B;Ll;0;L;<font> 0062;;;;N;;;;; +1D4B8;MATHEMATICAL SCRIPT SMALL C;Ll;0;L;<font> 0063;;;;N;;;;; +1D4B9;MATHEMATICAL SCRIPT SMALL D;Ll;0;L;<font> 0064;;;;N;;;;; +1D4BB;MATHEMATICAL SCRIPT SMALL F;Ll;0;L;<font> 0066;;;;N;;;;; +1D4BD;MATHEMATICAL SCRIPT SMALL H;Ll;0;L;<font> 0068;;;;N;;;;; +1D4BE;MATHEMATICAL SCRIPT SMALL I;Ll;0;L;<font> 0069;;;;N;;;;; +1D4BF;MATHEMATICAL SCRIPT SMALL J;Ll;0;L;<font> 006A;;;;N;;;;; +1D4C0;MATHEMATICAL SCRIPT SMALL K;Ll;0;L;<font> 006B;;;;N;;;;; +1D4C2;MATHEMATICAL SCRIPT SMALL M;Ll;0;L;<font> 006D;;;;N;;;;; +1D4C3;MATHEMATICAL SCRIPT SMALL N;Ll;0;L;<font> 006E;;;;N;;;;; +1D4C5;MATHEMATICAL SCRIPT SMALL P;Ll;0;L;<font> 0070;;;;N;;;;; +1D4C6;MATHEMATICAL SCRIPT SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;; +1D4C7;MATHEMATICAL SCRIPT SMALL R;Ll;0;L;<font> 0072;;;;N;;;;; +1D4C8;MATHEMATICAL SCRIPT SMALL S;Ll;0;L;<font> 0073;;;;N;;;;; +1D4C9;MATHEMATICAL SCRIPT SMALL T;Ll;0;L;<font> 0074;;;;N;;;;; +1D4CA;MATHEMATICAL SCRIPT SMALL U;Ll;0;L;<font> 0075;;;;N;;;;; +1D4CB;MATHEMATICAL SCRIPT SMALL V;Ll;0;L;<font> 0076;;;;N;;;;; +1D4CC;MATHEMATICAL SCRIPT SMALL W;Ll;0;L;<font> 0077;;;;N;;;;; +1D4CD;MATHEMATICAL SCRIPT SMALL X;Ll;0;L;<font> 0078;;;;N;;;;; +1D4CE;MATHEMATICAL SCRIPT SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;; +1D4CF;MATHEMATICAL SCRIPT SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;; +1D4D0;MATHEMATICAL BOLD SCRIPT CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;; +1D4D1;MATHEMATICAL BOLD SCRIPT CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;; +1D4D2;MATHEMATICAL BOLD SCRIPT CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;; +1D4D3;MATHEMATICAL BOLD SCRIPT CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;; +1D4D4;MATHEMATICAL BOLD SCRIPT CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;; +1D4D5;MATHEMATICAL BOLD SCRIPT CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;; +1D4D6;MATHEMATICAL BOLD SCRIPT CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;; +1D4D7;MATHEMATICAL BOLD SCRIPT CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;; +1D4D8;MATHEMATICAL BOLD SCRIPT CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;; +1D4D9;MATHEMATICAL BOLD SCRIPT CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;; +1D4DA;MATHEMATICAL BOLD SCRIPT CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;; +1D4DB;MATHEMATICAL BOLD SCRIPT CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;; +1D4DC;MATHEMATICAL BOLD SCRIPT CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;; +1D4DD;MATHEMATICAL BOLD SCRIPT CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;; +1D4DE;MATHEMATICAL BOLD SCRIPT CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;; +1D4DF;MATHEMATICAL BOLD SCRIPT CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;; +1D4E0;MATHEMATICAL BOLD SCRIPT CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;; +1D4E1;MATHEMATICAL BOLD SCRIPT CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;; +1D4E2;MATHEMATICAL BOLD SCRIPT CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;; +1D4E3;MATHEMATICAL BOLD SCRIPT CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;; +1D4E4;MATHEMATICAL BOLD SCRIPT CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;; +1D4E5;MATHEMATICAL BOLD SCRIPT CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;; +1D4E6;MATHEMATICAL BOLD SCRIPT CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;; +1D4E7;MATHEMATICAL BOLD SCRIPT CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;; +1D4E8;MATHEMATICAL BOLD SCRIPT CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;; +1D4E9;MATHEMATICAL BOLD SCRIPT CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;; +1D4EA;MATHEMATICAL BOLD SCRIPT SMALL A;Ll;0;L;<font> 0061;;;;N;;;;; +1D4EB;MATHEMATICAL BOLD SCRIPT SMALL B;Ll;0;L;<font> 0062;;;;N;;;;; +1D4EC;MATHEMATICAL BOLD SCRIPT SMALL C;Ll;0;L;<font> 0063;;;;N;;;;; +1D4ED;MATHEMATICAL BOLD SCRIPT SMALL D;Ll;0;L;<font> 0064;;;;N;;;;; +1D4EE;MATHEMATICAL BOLD SCRIPT SMALL E;Ll;0;L;<font> 0065;;;;N;;;;; +1D4EF;MATHEMATICAL BOLD SCRIPT SMALL F;Ll;0;L;<font> 0066;;;;N;;;;; +1D4F0;MATHEMATICAL BOLD SCRIPT SMALL G;Ll;0;L;<font> 0067;;;;N;;;;; +1D4F1;MATHEMATICAL BOLD SCRIPT SMALL H;Ll;0;L;<font> 0068;;;;N;;;;; +1D4F2;MATHEMATICAL BOLD SCRIPT SMALL I;Ll;0;L;<font> 0069;;;;N;;;;; +1D4F3;MATHEMATICAL BOLD SCRIPT SMALL J;Ll;0;L;<font> 006A;;;;N;;;;; +1D4F4;MATHEMATICAL BOLD SCRIPT SMALL K;Ll;0;L;<font> 006B;;;;N;;;;; +1D4F5;MATHEMATICAL BOLD SCRIPT SMALL L;Ll;0;L;<font> 006C;;;;N;;;;; +1D4F6;MATHEMATICAL BOLD SCRIPT SMALL M;Ll;0;L;<font> 006D;;;;N;;;;; +1D4F7;MATHEMATICAL BOLD SCRIPT SMALL N;Ll;0;L;<font> 006E;;;;N;;;;; +1D4F8;MATHEMATICAL BOLD SCRIPT SMALL O;Ll;0;L;<font> 006F;;;;N;;;;; +1D4F9;MATHEMATICAL BOLD SCRIPT SMALL P;Ll;0;L;<font> 0070;;;;N;;;;; +1D4FA;MATHEMATICAL BOLD SCRIPT SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;; +1D4FB;MATHEMATICAL BOLD SCRIPT SMALL R;Ll;0;L;<font> 0072;;;;N;;;;; +1D4FC;MATHEMATICAL BOLD SCRIPT SMALL S;Ll;0;L;<font> 0073;;;;N;;;;; +1D4FD;MATHEMATICAL BOLD SCRIPT SMALL T;Ll;0;L;<font> 0074;;;;N;;;;; +1D4FE;MATHEMATICAL BOLD SCRIPT SMALL U;Ll;0;L;<font> 0075;;;;N;;;;; +1D4FF;MATHEMATICAL BOLD SCRIPT SMALL V;Ll;0;L;<font> 0076;;;;N;;;;; +1D500;MATHEMATICAL BOLD SCRIPT SMALL W;Ll;0;L;<font> 0077;;;;N;;;;; +1D501;MATHEMATICAL BOLD SCRIPT SMALL X;Ll;0;L;<font> 0078;;;;N;;;;; +1D502;MATHEMATICAL BOLD SCRIPT SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;; +1D503;MATHEMATICAL BOLD SCRIPT SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;; +1D504;MATHEMATICAL FRAKTUR CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;; +1D505;MATHEMATICAL FRAKTUR CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;; +1D507;MATHEMATICAL FRAKTUR CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;; +1D508;MATHEMATICAL FRAKTUR CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;; +1D509;MATHEMATICAL FRAKTUR CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;; +1D50A;MATHEMATICAL FRAKTUR CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;; +1D50D;MATHEMATICAL FRAKTUR CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;; +1D50E;MATHEMATICAL FRAKTUR CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;; +1D50F;MATHEMATICAL FRAKTUR CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;; +1D510;MATHEMATICAL FRAKTUR CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;; +1D511;MATHEMATICAL FRAKTUR CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;; +1D512;MATHEMATICAL FRAKTUR CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;; +1D513;MATHEMATICAL FRAKTUR CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;; +1D514;MATHEMATICAL FRAKTUR CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;; +1D516;MATHEMATICAL FRAKTUR CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;; +1D517;MATHEMATICAL FRAKTUR CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;; +1D518;MATHEMATICAL FRAKTUR CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;; +1D519;MATHEMATICAL FRAKTUR CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;; +1D51A;MATHEMATICAL FRAKTUR CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;; +1D51B;MATHEMATICAL FRAKTUR CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;; +1D51C;MATHEMATICAL FRAKTUR CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;; +1D51E;MATHEMATICAL FRAKTUR SMALL A;Ll;0;L;<font> 0061;;;;N;;;;; +1D51F;MATHEMATICAL FRAKTUR SMALL B;Ll;0;L;<font> 0062;;;;N;;;;; +1D520;MATHEMATICAL FRAKTUR SMALL C;Ll;0;L;<font> 0063;;;;N;;;;; +1D521;MATHEMATICAL FRAKTUR SMALL D;Ll;0;L;<font> 0064;;;;N;;;;; +1D522;MATHEMATICAL FRAKTUR SMALL E;Ll;0;L;<font> 0065;;;;N;;;;; +1D523;MATHEMATICAL FRAKTUR SMALL F;Ll;0;L;<font> 0066;;;;N;;;;; +1D524;MATHEMATICAL FRAKTUR SMALL G;Ll;0;L;<font> 0067;;;;N;;;;; +1D525;MATHEMATICAL FRAKTUR SMALL H;Ll;0;L;<font> 0068;;;;N;;;;; +1D526;MATHEMATICAL FRAKTUR SMALL I;Ll;0;L;<font> 0069;;;;N;;;;; +1D527;MATHEMATICAL FRAKTUR SMALL J;Ll;0;L;<font> 006A;;;;N;;;;; +1D528;MATHEMATICAL FRAKTUR SMALL K;Ll;0;L;<font> 006B;;;;N;;;;; +1D529;MATHEMATICAL FRAKTUR SMALL L;Ll;0;L;<font> 006C;;;;N;;;;; +1D52A;MATHEMATICAL FRAKTUR SMALL M;Ll;0;L;<font> 006D;;;;N;;;;; +1D52B;MATHEMATICAL FRAKTUR SMALL N;Ll;0;L;<font> 006E;;;;N;;;;; +1D52C;MATHEMATICAL FRAKTUR SMALL O;Ll;0;L;<font> 006F;;;;N;;;;; +1D52D;MATHEMATICAL FRAKTUR SMALL P;Ll;0;L;<font> 0070;;;;N;;;;; +1D52E;MATHEMATICAL FRAKTUR SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;; +1D52F;MATHEMATICAL FRAKTUR SMALL R;Ll;0;L;<font> 0072;;;;N;;;;; +1D530;MATHEMATICAL FRAKTUR SMALL S;Ll;0;L;<font> 0073;;;;N;;;;; +1D531;MATHEMATICAL FRAKTUR SMALL T;Ll;0;L;<font> 0074;;;;N;;;;; +1D532;MATHEMATICAL FRAKTUR SMALL U;Ll;0;L;<font> 0075;;;;N;;;;; +1D533;MATHEMATICAL FRAKTUR SMALL V;Ll;0;L;<font> 0076;;;;N;;;;; +1D534;MATHEMATICAL FRAKTUR SMALL W;Ll;0;L;<font> 0077;;;;N;;;;; +1D535;MATHEMATICAL FRAKTUR SMALL X;Ll;0;L;<font> 0078;;;;N;;;;; +1D536;MATHEMATICAL FRAKTUR SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;; +1D537;MATHEMATICAL FRAKTUR SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;; +1D538;MATHEMATICAL DOUBLE-STRUCK CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;; +1D539;MATHEMATICAL DOUBLE-STRUCK CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;; +1D53B;MATHEMATICAL DOUBLE-STRUCK CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;; +1D53C;MATHEMATICAL DOUBLE-STRUCK CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;; +1D53D;MATHEMATICAL DOUBLE-STRUCK CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;; +1D53E;MATHEMATICAL DOUBLE-STRUCK CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;; +1D540;MATHEMATICAL DOUBLE-STRUCK CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;; +1D541;MATHEMATICAL DOUBLE-STRUCK CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;; +1D542;MATHEMATICAL DOUBLE-STRUCK CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;; +1D543;MATHEMATICAL DOUBLE-STRUCK CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;; +1D544;MATHEMATICAL DOUBLE-STRUCK CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;; +1D546;MATHEMATICAL DOUBLE-STRUCK CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;; +1D54A;MATHEMATICAL DOUBLE-STRUCK CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;; +1D54B;MATHEMATICAL DOUBLE-STRUCK CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;; +1D54C;MATHEMATICAL DOUBLE-STRUCK CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;; +1D54D;MATHEMATICAL DOUBLE-STRUCK CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;; +1D54E;MATHEMATICAL DOUBLE-STRUCK CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;; +1D54F;MATHEMATICAL DOUBLE-STRUCK CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;; +1D550;MATHEMATICAL DOUBLE-STRUCK CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;; +1D552;MATHEMATICAL DOUBLE-STRUCK SMALL A;Ll;0;L;<font> 0061;;;;N;;;;; +1D553;MATHEMATICAL DOUBLE-STRUCK SMALL B;Ll;0;L;<font> 0062;;;;N;;;;; +1D554;MATHEMATICAL DOUBLE-STRUCK SMALL C;Ll;0;L;<font> 0063;;;;N;;;;; +1D555;MATHEMATICAL DOUBLE-STRUCK SMALL D;Ll;0;L;<font> 0064;;;;N;;;;; +1D556;MATHEMATICAL DOUBLE-STRUCK SMALL E;Ll;0;L;<font> 0065;;;;N;;;;; +1D557;MATHEMATICAL DOUBLE-STRUCK SMALL F;Ll;0;L;<font> 0066;;;;N;;;;; +1D558;MATHEMATICAL DOUBLE-STRUCK SMALL G;Ll;0;L;<font> 0067;;;;N;;;;; +1D559;MATHEMATICAL DOUBLE-STRUCK SMALL H;Ll;0;L;<font> 0068;;;;N;;;;; +1D55A;MATHEMATICAL DOUBLE-STRUCK SMALL I;Ll;0;L;<font> 0069;;;;N;;;;; +1D55B;MATHEMATICAL DOUBLE-STRUCK SMALL J;Ll;0;L;<font> 006A;;;;N;;;;; +1D55C;MATHEMATICAL DOUBLE-STRUCK SMALL K;Ll;0;L;<font> 006B;;;;N;;;;; +1D55D;MATHEMATICAL DOUBLE-STRUCK SMALL L;Ll;0;L;<font> 006C;;;;N;;;;; +1D55E;MATHEMATICAL DOUBLE-STRUCK SMALL M;Ll;0;L;<font> 006D;;;;N;;;;; +1D55F;MATHEMATICAL DOUBLE-STRUCK SMALL N;Ll;0;L;<font> 006E;;;;N;;;;; +1D560;MATHEMATICAL DOUBLE-STRUCK SMALL O;Ll;0;L;<font> 006F;;;;N;;;;; +1D561;MATHEMATICAL DOUBLE-STRUCK SMALL P;Ll;0;L;<font> 0070;;;;N;;;;; +1D562;MATHEMATICAL DOUBLE-STRUCK SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;; +1D563;MATHEMATICAL DOUBLE-STRUCK SMALL R;Ll;0;L;<font> 0072;;;;N;;;;; +1D564;MATHEMATICAL DOUBLE-STRUCK SMALL S;Ll;0;L;<font> 0073;;;;N;;;;; +1D565;MATHEMATICAL DOUBLE-STRUCK SMALL T;Ll;0;L;<font> 0074;;;;N;;;;; +1D566;MATHEMATICAL DOUBLE-STRUCK SMALL U;Ll;0;L;<font> 0075;;;;N;;;;; +1D567;MATHEMATICAL DOUBLE-STRUCK SMALL V;Ll;0;L;<font> 0076;;;;N;;;;; +1D568;MATHEMATICAL DOUBLE-STRUCK SMALL W;Ll;0;L;<font> 0077;;;;N;;;;; +1D569;MATHEMATICAL DOUBLE-STRUCK SMALL X;Ll;0;L;<font> 0078;;;;N;;;;; +1D56A;MATHEMATICAL DOUBLE-STRUCK SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;; +1D56B;MATHEMATICAL DOUBLE-STRUCK SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;; +1D56C;MATHEMATICAL BOLD FRAKTUR CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;; +1D56D;MATHEMATICAL BOLD FRAKTUR CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;; +1D56E;MATHEMATICAL BOLD FRAKTUR CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;; +1D56F;MATHEMATICAL BOLD FRAKTUR CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;; +1D570;MATHEMATICAL BOLD FRAKTUR CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;; +1D571;MATHEMATICAL BOLD FRAKTUR CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;; +1D572;MATHEMATICAL BOLD FRAKTUR CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;; +1D573;MATHEMATICAL BOLD FRAKTUR CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;; +1D574;MATHEMATICAL BOLD FRAKTUR CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;; +1D575;MATHEMATICAL BOLD FRAKTUR CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;; +1D576;MATHEMATICAL BOLD FRAKTUR CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;; +1D577;MATHEMATICAL BOLD FRAKTUR CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;; +1D578;MATHEMATICAL BOLD FRAKTUR CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;; +1D579;MATHEMATICAL BOLD FRAKTUR CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;; +1D57A;MATHEMATICAL BOLD FRAKTUR CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;; +1D57B;MATHEMATICAL BOLD FRAKTUR CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;; +1D57C;MATHEMATICAL BOLD FRAKTUR CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;; +1D57D;MATHEMATICAL BOLD FRAKTUR CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;; +1D57E;MATHEMATICAL BOLD FRAKTUR CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;; +1D57F;MATHEMATICAL BOLD FRAKTUR CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;; +1D580;MATHEMATICAL BOLD FRAKTUR CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;; +1D581;MATHEMATICAL BOLD FRAKTUR CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;; +1D582;MATHEMATICAL BOLD FRAKTUR CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;; +1D583;MATHEMATICAL BOLD FRAKTUR CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;; +1D584;MATHEMATICAL BOLD FRAKTUR CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;; +1D585;MATHEMATICAL BOLD FRAKTUR CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;; +1D586;MATHEMATICAL BOLD FRAKTUR SMALL A;Ll;0;L;<font> 0061;;;;N;;;;; +1D587;MATHEMATICAL BOLD FRAKTUR SMALL B;Ll;0;L;<font> 0062;;;;N;;;;; +1D588;MATHEMATICAL BOLD FRAKTUR SMALL C;Ll;0;L;<font> 0063;;;;N;;;;; +1D589;MATHEMATICAL BOLD FRAKTUR SMALL D;Ll;0;L;<font> 0064;;;;N;;;;; +1D58A;MATHEMATICAL BOLD FRAKTUR SMALL E;Ll;0;L;<font> 0065;;;;N;;;;; +1D58B;MATHEMATICAL BOLD FRAKTUR SMALL F;Ll;0;L;<font> 0066;;;;N;;;;; +1D58C;MATHEMATICAL BOLD FRAKTUR SMALL G;Ll;0;L;<font> 0067;;;;N;;;;; +1D58D;MATHEMATICAL BOLD FRAKTUR SMALL H;Ll;0;L;<font> 0068;;;;N;;;;; +1D58E;MATHEMATICAL BOLD FRAKTUR SMALL I;Ll;0;L;<font> 0069;;;;N;;;;; +1D58F;MATHEMATICAL BOLD FRAKTUR SMALL J;Ll;0;L;<font> 006A;;;;N;;;;; +1D590;MATHEMATICAL BOLD FRAKTUR SMALL K;Ll;0;L;<font> 006B;;;;N;;;;; +1D591;MATHEMATICAL BOLD FRAKTUR SMALL L;Ll;0;L;<font> 006C;;;;N;;;;; +1D592;MATHEMATICAL BOLD FRAKTUR SMALL M;Ll;0;L;<font> 006D;;;;N;;;;; +1D593;MATHEMATICAL BOLD FRAKTUR SMALL N;Ll;0;L;<font> 006E;;;;N;;;;; +1D594;MATHEMATICAL BOLD FRAKTUR SMALL O;Ll;0;L;<font> 006F;;;;N;;;;; +1D595;MATHEMATICAL BOLD FRAKTUR SMALL P;Ll;0;L;<font> 0070;;;;N;;;;; +1D596;MATHEMATICAL BOLD FRAKTUR SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;; +1D597;MATHEMATICAL BOLD FRAKTUR SMALL R;Ll;0;L;<font> 0072;;;;N;;;;; +1D598;MATHEMATICAL BOLD FRAKTUR SMALL S;Ll;0;L;<font> 0073;;;;N;;;;; +1D599;MATHEMATICAL BOLD FRAKTUR SMALL T;Ll;0;L;<font> 0074;;;;N;;;;; +1D59A;MATHEMATICAL BOLD FRAKTUR SMALL U;Ll;0;L;<font> 0075;;;;N;;;;; +1D59B;MATHEMATICAL BOLD FRAKTUR SMALL V;Ll;0;L;<font> 0076;;;;N;;;;; +1D59C;MATHEMATICAL BOLD FRAKTUR SMALL W;Ll;0;L;<font> 0077;;;;N;;;;; +1D59D;MATHEMATICAL BOLD FRAKTUR SMALL X;Ll;0;L;<font> 0078;;;;N;;;;; +1D59E;MATHEMATICAL BOLD FRAKTUR SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;; +1D59F;MATHEMATICAL BOLD FRAKTUR SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;; +1D5A0;MATHEMATICAL SANS-SERIF CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;; +1D5A1;MATHEMATICAL SANS-SERIF CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;; +1D5A2;MATHEMATICAL SANS-SERIF CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;; +1D5A3;MATHEMATICAL SANS-SERIF CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;; +1D5A4;MATHEMATICAL SANS-SERIF CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;; +1D5A5;MATHEMATICAL SANS-SERIF CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;; +1D5A6;MATHEMATICAL SANS-SERIF CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;; +1D5A7;MATHEMATICAL SANS-SERIF CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;; +1D5A8;MATHEMATICAL SANS-SERIF CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;; +1D5A9;MATHEMATICAL SANS-SERIF CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;; +1D5AA;MATHEMATICAL SANS-SERIF CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;; +1D5AB;MATHEMATICAL SANS-SERIF CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;; +1D5AC;MATHEMATICAL SANS-SERIF CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;; +1D5AD;MATHEMATICAL SANS-SERIF CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;; +1D5AE;MATHEMATICAL SANS-SERIF CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;; +1D5AF;MATHEMATICAL SANS-SERIF CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;; +1D5B0;MATHEMATICAL SANS-SERIF CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;; +1D5B1;MATHEMATICAL SANS-SERIF CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;; +1D5B2;MATHEMATICAL SANS-SERIF CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;; +1D5B3;MATHEMATICAL SANS-SERIF CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;; +1D5B4;MATHEMATICAL SANS-SERIF CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;; +1D5B5;MATHEMATICAL SANS-SERIF CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;; +1D5B6;MATHEMATICAL SANS-SERIF CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;; +1D5B7;MATHEMATICAL SANS-SERIF CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;; +1D5B8;MATHEMATICAL SANS-SERIF CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;; +1D5B9;MATHEMATICAL SANS-SERIF CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;; +1D5BA;MATHEMATICAL SANS-SERIF SMALL A;Ll;0;L;<font> 0061;;;;N;;;;; +1D5BB;MATHEMATICAL SANS-SERIF SMALL B;Ll;0;L;<font> 0062;;;;N;;;;; +1D5BC;MATHEMATICAL SANS-SERIF SMALL C;Ll;0;L;<font> 0063;;;;N;;;;; +1D5BD;MATHEMATICAL SANS-SERIF SMALL D;Ll;0;L;<font> 0064;;;;N;;;;; +1D5BE;MATHEMATICAL SANS-SERIF SMALL E;Ll;0;L;<font> 0065;;;;N;;;;; +1D5BF;MATHEMATICAL SANS-SERIF SMALL F;Ll;0;L;<font> 0066;;;;N;;;;; +1D5C0;MATHEMATICAL SANS-SERIF SMALL G;Ll;0;L;<font> 0067;;;;N;;;;; +1D5C1;MATHEMATICAL SANS-SERIF SMALL H;Ll;0;L;<font> 0068;;;;N;;;;; +1D5C2;MATHEMATICAL SANS-SERIF SMALL I;Ll;0;L;<font> 0069;;;;N;;;;; +1D5C3;MATHEMATICAL SANS-SERIF SMALL J;Ll;0;L;<font> 006A;;;;N;;;;; +1D5C4;MATHEMATICAL SANS-SERIF SMALL K;Ll;0;L;<font> 006B;;;;N;;;;; +1D5C5;MATHEMATICAL SANS-SERIF SMALL L;Ll;0;L;<font> 006C;;;;N;;;;; +1D5C6;MATHEMATICAL SANS-SERIF SMALL M;Ll;0;L;<font> 006D;;;;N;;;;; +1D5C7;MATHEMATICAL SANS-SERIF SMALL N;Ll;0;L;<font> 006E;;;;N;;;;; +1D5C8;MATHEMATICAL SANS-SERIF SMALL O;Ll;0;L;<font> 006F;;;;N;;;;; +1D5C9;MATHEMATICAL SANS-SERIF SMALL P;Ll;0;L;<font> 0070;;;;N;;;;; +1D5CA;MATHEMATICAL SANS-SERIF SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;; +1D5CB;MATHEMATICAL SANS-SERIF SMALL R;Ll;0;L;<font> 0072;;;;N;;;;; +1D5CC;MATHEMATICAL SANS-SERIF SMALL S;Ll;0;L;<font> 0073;;;;N;;;;; +1D5CD;MATHEMATICAL SANS-SERIF SMALL T;Ll;0;L;<font> 0074;;;;N;;;;; +1D5CE;MATHEMATICAL SANS-SERIF SMALL U;Ll;0;L;<font> 0075;;;;N;;;;; +1D5CF;MATHEMATICAL SANS-SERIF SMALL V;Ll;0;L;<font> 0076;;;;N;;;;; +1D5D0;MATHEMATICAL SANS-SERIF SMALL W;Ll;0;L;<font> 0077;;;;N;;;;; +1D5D1;MATHEMATICAL SANS-SERIF SMALL X;Ll;0;L;<font> 0078;;;;N;;;;; +1D5D2;MATHEMATICAL SANS-SERIF SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;; +1D5D3;MATHEMATICAL SANS-SERIF SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;; +1D5D4;MATHEMATICAL SANS-SERIF BOLD CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;; +1D5D5;MATHEMATICAL SANS-SERIF BOLD CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;; +1D5D6;MATHEMATICAL SANS-SERIF BOLD CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;; +1D5D7;MATHEMATICAL SANS-SERIF BOLD CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;; +1D5D8;MATHEMATICAL SANS-SERIF BOLD CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;; +1D5D9;MATHEMATICAL SANS-SERIF BOLD CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;; +1D5DA;MATHEMATICAL SANS-SERIF BOLD CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;; +1D5DB;MATHEMATICAL SANS-SERIF BOLD CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;; +1D5DC;MATHEMATICAL SANS-SERIF BOLD CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;; +1D5DD;MATHEMATICAL SANS-SERIF BOLD CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;; +1D5DE;MATHEMATICAL SANS-SERIF BOLD CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;; +1D5DF;MATHEMATICAL SANS-SERIF BOLD CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;; +1D5E0;MATHEMATICAL SANS-SERIF BOLD CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;; +1D5E1;MATHEMATICAL SANS-SERIF BOLD CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;; +1D5E2;MATHEMATICAL SANS-SERIF BOLD CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;; +1D5E3;MATHEMATICAL SANS-SERIF BOLD CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;; +1D5E4;MATHEMATICAL SANS-SERIF BOLD CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;; +1D5E5;MATHEMATICAL SANS-SERIF BOLD CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;; +1D5E6;MATHEMATICAL SANS-SERIF BOLD CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;; +1D5E7;MATHEMATICAL SANS-SERIF BOLD CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;; +1D5E8;MATHEMATICAL SANS-SERIF BOLD CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;; +1D5E9;MATHEMATICAL SANS-SERIF BOLD CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;; +1D5EA;MATHEMATICAL SANS-SERIF BOLD CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;; +1D5EB;MATHEMATICAL SANS-SERIF BOLD CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;; +1D5EC;MATHEMATICAL SANS-SERIF BOLD CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;; +1D5ED;MATHEMATICAL SANS-SERIF BOLD CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;; +1D5EE;MATHEMATICAL SANS-SERIF BOLD SMALL A;Ll;0;L;<font> 0061;;;;N;;;;; +1D5EF;MATHEMATICAL SANS-SERIF BOLD SMALL B;Ll;0;L;<font> 0062;;;;N;;;;; +1D5F0;MATHEMATICAL SANS-SERIF BOLD SMALL C;Ll;0;L;<font> 0063;;;;N;;;;; +1D5F1;MATHEMATICAL SANS-SERIF BOLD SMALL D;Ll;0;L;<font> 0064;;;;N;;;;; +1D5F2;MATHEMATICAL SANS-SERIF BOLD SMALL E;Ll;0;L;<font> 0065;;;;N;;;;; +1D5F3;MATHEMATICAL SANS-SERIF BOLD SMALL F;Ll;0;L;<font> 0066;;;;N;;;;; +1D5F4;MATHEMATICAL SANS-SERIF BOLD SMALL G;Ll;0;L;<font> 0067;;;;N;;;;; +1D5F5;MATHEMATICAL SANS-SERIF BOLD SMALL H;Ll;0;L;<font> 0068;;;;N;;;;; +1D5F6;MATHEMATICAL SANS-SERIF BOLD SMALL I;Ll;0;L;<font> 0069;;;;N;;;;; +1D5F7;MATHEMATICAL SANS-SERIF BOLD SMALL J;Ll;0;L;<font> 006A;;;;N;;;;; +1D5F8;MATHEMATICAL SANS-SERIF BOLD SMALL K;Ll;0;L;<font> 006B;;;;N;;;;; +1D5F9;MATHEMATICAL SANS-SERIF BOLD SMALL L;Ll;0;L;<font> 006C;;;;N;;;;; +1D5FA;MATHEMATICAL SANS-SERIF BOLD SMALL M;Ll;0;L;<font> 006D;;;;N;;;;; +1D5FB;MATHEMATICAL SANS-SERIF BOLD SMALL N;Ll;0;L;<font> 006E;;;;N;;;;; +1D5FC;MATHEMATICAL SANS-SERIF BOLD SMALL O;Ll;0;L;<font> 006F;;;;N;;;;; +1D5FD;MATHEMATICAL SANS-SERIF BOLD SMALL P;Ll;0;L;<font> 0070;;;;N;;;;; +1D5FE;MATHEMATICAL SANS-SERIF BOLD SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;; +1D5FF;MATHEMATICAL SANS-SERIF BOLD SMALL R;Ll;0;L;<font> 0072;;;;N;;;;; +1D600;MATHEMATICAL SANS-SERIF BOLD SMALL S;Ll;0;L;<font> 0073;;;;N;;;;; +1D601;MATHEMATICAL SANS-SERIF BOLD SMALL T;Ll;0;L;<font> 0074;;;;N;;;;; +1D602;MATHEMATICAL SANS-SERIF BOLD SMALL U;Ll;0;L;<font> 0075;;;;N;;;;; +1D603;MATHEMATICAL SANS-SERIF BOLD SMALL V;Ll;0;L;<font> 0076;;;;N;;;;; +1D604;MATHEMATICAL SANS-SERIF BOLD SMALL W;Ll;0;L;<font> 0077;;;;N;;;;; +1D605;MATHEMATICAL SANS-SERIF BOLD SMALL X;Ll;0;L;<font> 0078;;;;N;;;;; +1D606;MATHEMATICAL SANS-SERIF BOLD SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;; +1D607;MATHEMATICAL SANS-SERIF BOLD SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;; +1D608;MATHEMATICAL SANS-SERIF ITALIC CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;; +1D609;MATHEMATICAL SANS-SERIF ITALIC CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;; +1D60A;MATHEMATICAL SANS-SERIF ITALIC CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;; +1D60B;MATHEMATICAL SANS-SERIF ITALIC CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;; +1D60C;MATHEMATICAL SANS-SERIF ITALIC CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;; +1D60D;MATHEMATICAL SANS-SERIF ITALIC CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;; +1D60E;MATHEMATICAL SANS-SERIF ITALIC CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;; +1D60F;MATHEMATICAL SANS-SERIF ITALIC CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;; +1D610;MATHEMATICAL SANS-SERIF ITALIC CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;; +1D611;MATHEMATICAL SANS-SERIF ITALIC CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;; +1D612;MATHEMATICAL SANS-SERIF ITALIC CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;; +1D613;MATHEMATICAL SANS-SERIF ITALIC CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;; +1D614;MATHEMATICAL SANS-SERIF ITALIC CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;; +1D615;MATHEMATICAL SANS-SERIF ITALIC CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;; +1D616;MATHEMATICAL SANS-SERIF ITALIC CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;; +1D617;MATHEMATICAL SANS-SERIF ITALIC CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;; +1D618;MATHEMATICAL SANS-SERIF ITALIC CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;; +1D619;MATHEMATICAL SANS-SERIF ITALIC CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;; +1D61A;MATHEMATICAL SANS-SERIF ITALIC CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;; +1D61B;MATHEMATICAL SANS-SERIF ITALIC CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;; +1D61C;MATHEMATICAL SANS-SERIF ITALIC CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;; +1D61D;MATHEMATICAL SANS-SERIF ITALIC CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;; +1D61E;MATHEMATICAL SANS-SERIF ITALIC CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;; +1D61F;MATHEMATICAL SANS-SERIF ITALIC CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;; +1D620;MATHEMATICAL SANS-SERIF ITALIC CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;; +1D621;MATHEMATICAL SANS-SERIF ITALIC CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;; +1D622;MATHEMATICAL SANS-SERIF ITALIC SMALL A;Ll;0;L;<font> 0061;;;;N;;;;; +1D623;MATHEMATICAL SANS-SERIF ITALIC SMALL B;Ll;0;L;<font> 0062;;;;N;;;;; +1D624;MATHEMATICAL SANS-SERIF ITALIC SMALL C;Ll;0;L;<font> 0063;;;;N;;;;; +1D625;MATHEMATICAL SANS-SERIF ITALIC SMALL D;Ll;0;L;<font> 0064;;;;N;;;;; +1D626;MATHEMATICAL SANS-SERIF ITALIC SMALL E;Ll;0;L;<font> 0065;;;;N;;;;; +1D627;MATHEMATICAL SANS-SERIF ITALIC SMALL F;Ll;0;L;<font> 0066;;;;N;;;;; +1D628;MATHEMATICAL SANS-SERIF ITALIC SMALL G;Ll;0;L;<font> 0067;;;;N;;;;; +1D629;MATHEMATICAL SANS-SERIF ITALIC SMALL H;Ll;0;L;<font> 0068;;;;N;;;;; +1D62A;MATHEMATICAL SANS-SERIF ITALIC SMALL I;Ll;0;L;<font> 0069;;;;N;;;;; +1D62B;MATHEMATICAL SANS-SERIF ITALIC SMALL J;Ll;0;L;<font> 006A;;;;N;;;;; +1D62C;MATHEMATICAL SANS-SERIF ITALIC SMALL K;Ll;0;L;<font> 006B;;;;N;;;;; +1D62D;MATHEMATICAL SANS-SERIF ITALIC SMALL L;Ll;0;L;<font> 006C;;;;N;;;;; +1D62E;MATHEMATICAL SANS-SERIF ITALIC SMALL M;Ll;0;L;<font> 006D;;;;N;;;;; +1D62F;MATHEMATICAL SANS-SERIF ITALIC SMALL N;Ll;0;L;<font> 006E;;;;N;;;;; +1D630;MATHEMATICAL SANS-SERIF ITALIC SMALL O;Ll;0;L;<font> 006F;;;;N;;;;; +1D631;MATHEMATICAL SANS-SERIF ITALIC SMALL P;Ll;0;L;<font> 0070;;;;N;;;;; +1D632;MATHEMATICAL SANS-SERIF ITALIC SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;; +1D633;MATHEMATICAL SANS-SERIF ITALIC SMALL R;Ll;0;L;<font> 0072;;;;N;;;;; +1D634;MATHEMATICAL SANS-SERIF ITALIC SMALL S;Ll;0;L;<font> 0073;;;;N;;;;; +1D635;MATHEMATICAL SANS-SERIF ITALIC SMALL T;Ll;0;L;<font> 0074;;;;N;;;;; +1D636;MATHEMATICAL SANS-SERIF ITALIC SMALL U;Ll;0;L;<font> 0075;;;;N;;;;; +1D637;MATHEMATICAL SANS-SERIF ITALIC SMALL V;Ll;0;L;<font> 0076;;;;N;;;;; +1D638;MATHEMATICAL SANS-SERIF ITALIC SMALL W;Ll;0;L;<font> 0077;;;;N;;;;; +1D639;MATHEMATICAL SANS-SERIF ITALIC SMALL X;Ll;0;L;<font> 0078;;;;N;;;;; +1D63A;MATHEMATICAL SANS-SERIF ITALIC SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;; +1D63B;MATHEMATICAL SANS-SERIF ITALIC SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;; +1D63C;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;; +1D63D;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;; +1D63E;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;; +1D63F;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;; +1D640;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;; +1D641;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;; +1D642;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;; +1D643;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;; +1D644;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;; +1D645;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;; +1D646;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;; +1D647;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;; +1D648;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;; +1D649;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;; +1D64A;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;; +1D64B;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;; +1D64C;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;; +1D64D;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;; +1D64E;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;; +1D64F;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;; +1D650;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;; +1D651;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;; +1D652;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;; +1D653;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;; +1D654;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;; +1D655;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;; +1D656;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL A;Ll;0;L;<font> 0061;;;;N;;;;; +1D657;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL B;Ll;0;L;<font> 0062;;;;N;;;;; +1D658;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL C;Ll;0;L;<font> 0063;;;;N;;;;; +1D659;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL D;Ll;0;L;<font> 0064;;;;N;;;;; +1D65A;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL E;Ll;0;L;<font> 0065;;;;N;;;;; +1D65B;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL F;Ll;0;L;<font> 0066;;;;N;;;;; +1D65C;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL G;Ll;0;L;<font> 0067;;;;N;;;;; +1D65D;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL H;Ll;0;L;<font> 0068;;;;N;;;;; +1D65E;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL I;Ll;0;L;<font> 0069;;;;N;;;;; +1D65F;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL J;Ll;0;L;<font> 006A;;;;N;;;;; +1D660;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL K;Ll;0;L;<font> 006B;;;;N;;;;; +1D661;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL L;Ll;0;L;<font> 006C;;;;N;;;;; +1D662;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL M;Ll;0;L;<font> 006D;;;;N;;;;; +1D663;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL N;Ll;0;L;<font> 006E;;;;N;;;;; +1D664;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL O;Ll;0;L;<font> 006F;;;;N;;;;; +1D665;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL P;Ll;0;L;<font> 0070;;;;N;;;;; +1D666;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;; +1D667;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL R;Ll;0;L;<font> 0072;;;;N;;;;; +1D668;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL S;Ll;0;L;<font> 0073;;;;N;;;;; +1D669;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL T;Ll;0;L;<font> 0074;;;;N;;;;; +1D66A;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL U;Ll;0;L;<font> 0075;;;;N;;;;; +1D66B;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL V;Ll;0;L;<font> 0076;;;;N;;;;; +1D66C;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL W;Ll;0;L;<font> 0077;;;;N;;;;; +1D66D;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL X;Ll;0;L;<font> 0078;;;;N;;;;; +1D66E;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;; +1D66F;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;; +1D670;MATHEMATICAL MONOSPACE CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;; +1D671;MATHEMATICAL MONOSPACE CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;; +1D672;MATHEMATICAL MONOSPACE CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;; +1D673;MATHEMATICAL MONOSPACE CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;; +1D674;MATHEMATICAL MONOSPACE CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;; +1D675;MATHEMATICAL MONOSPACE CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;; +1D676;MATHEMATICAL MONOSPACE CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;; +1D677;MATHEMATICAL MONOSPACE CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;; +1D678;MATHEMATICAL MONOSPACE CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;; +1D679;MATHEMATICAL MONOSPACE CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;; +1D67A;MATHEMATICAL MONOSPACE CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;; +1D67B;MATHEMATICAL MONOSPACE CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;; +1D67C;MATHEMATICAL MONOSPACE CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;; +1D67D;MATHEMATICAL MONOSPACE CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;; +1D67E;MATHEMATICAL MONOSPACE CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;; +1D67F;MATHEMATICAL MONOSPACE CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;; +1D680;MATHEMATICAL MONOSPACE CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;; +1D681;MATHEMATICAL MONOSPACE CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;; +1D682;MATHEMATICAL MONOSPACE CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;; +1D683;MATHEMATICAL MONOSPACE CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;; +1D684;MATHEMATICAL MONOSPACE CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;; +1D685;MATHEMATICAL MONOSPACE CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;; +1D686;MATHEMATICAL MONOSPACE CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;; +1D687;MATHEMATICAL MONOSPACE CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;; +1D688;MATHEMATICAL MONOSPACE CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;; +1D689;MATHEMATICAL MONOSPACE CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;; +1D68A;MATHEMATICAL MONOSPACE SMALL A;Ll;0;L;<font> 0061;;;;N;;;;; +1D68B;MATHEMATICAL MONOSPACE SMALL B;Ll;0;L;<font> 0062;;;;N;;;;; +1D68C;MATHEMATICAL MONOSPACE SMALL C;Ll;0;L;<font> 0063;;;;N;;;;; +1D68D;MATHEMATICAL MONOSPACE SMALL D;Ll;0;L;<font> 0064;;;;N;;;;; +1D68E;MATHEMATICAL MONOSPACE SMALL E;Ll;0;L;<font> 0065;;;;N;;;;; +1D68F;MATHEMATICAL MONOSPACE SMALL F;Ll;0;L;<font> 0066;;;;N;;;;; +1D690;MATHEMATICAL MONOSPACE SMALL G;Ll;0;L;<font> 0067;;;;N;;;;; +1D691;MATHEMATICAL MONOSPACE SMALL H;Ll;0;L;<font> 0068;;;;N;;;;; +1D692;MATHEMATICAL MONOSPACE SMALL I;Ll;0;L;<font> 0069;;;;N;;;;; +1D693;MATHEMATICAL MONOSPACE SMALL J;Ll;0;L;<font> 006A;;;;N;;;;; +1D694;MATHEMATICAL MONOSPACE SMALL K;Ll;0;L;<font> 006B;;;;N;;;;; +1D695;MATHEMATICAL MONOSPACE SMALL L;Ll;0;L;<font> 006C;;;;N;;;;; +1D696;MATHEMATICAL MONOSPACE SMALL M;Ll;0;L;<font> 006D;;;;N;;;;; +1D697;MATHEMATICAL MONOSPACE SMALL N;Ll;0;L;<font> 006E;;;;N;;;;; +1D698;MATHEMATICAL MONOSPACE SMALL O;Ll;0;L;<font> 006F;;;;N;;;;; +1D699;MATHEMATICAL MONOSPACE SMALL P;Ll;0;L;<font> 0070;;;;N;;;;; +1D69A;MATHEMATICAL MONOSPACE SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;; +1D69B;MATHEMATICAL MONOSPACE SMALL R;Ll;0;L;<font> 0072;;;;N;;;;; +1D69C;MATHEMATICAL MONOSPACE SMALL S;Ll;0;L;<font> 0073;;;;N;;;;; +1D69D;MATHEMATICAL MONOSPACE SMALL T;Ll;0;L;<font> 0074;;;;N;;;;; +1D69E;MATHEMATICAL MONOSPACE SMALL U;Ll;0;L;<font> 0075;;;;N;;;;; +1D69F;MATHEMATICAL MONOSPACE SMALL V;Ll;0;L;<font> 0076;;;;N;;;;; +1D6A0;MATHEMATICAL MONOSPACE SMALL W;Ll;0;L;<font> 0077;;;;N;;;;; +1D6A1;MATHEMATICAL MONOSPACE SMALL X;Ll;0;L;<font> 0078;;;;N;;;;; +1D6A2;MATHEMATICAL MONOSPACE SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;; +1D6A3;MATHEMATICAL MONOSPACE SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;; +1D6A8;MATHEMATICAL BOLD CAPITAL ALPHA;Lu;0;L;<font> 0391;;;;N;;;;; +1D6A9;MATHEMATICAL BOLD CAPITAL BETA;Lu;0;L;<font> 0392;;;;N;;;;; +1D6AA;MATHEMATICAL BOLD CAPITAL GAMMA;Lu;0;L;<font> 0393;;;;N;;;;; +1D6AB;MATHEMATICAL BOLD CAPITAL DELTA;Lu;0;L;<font> 0394;;;;N;;;;; +1D6AC;MATHEMATICAL BOLD CAPITAL EPSILON;Lu;0;L;<font> 0395;;;;N;;;;; +1D6AD;MATHEMATICAL BOLD CAPITAL ZETA;Lu;0;L;<font> 0396;;;;N;;;;; +1D6AE;MATHEMATICAL BOLD CAPITAL ETA;Lu;0;L;<font> 0397;;;;N;;;;; +1D6AF;MATHEMATICAL BOLD CAPITAL THETA;Lu;0;L;<font> 0398;;;;N;;;;; +1D6B0;MATHEMATICAL BOLD CAPITAL IOTA;Lu;0;L;<font> 0399;;;;N;;;;; +1D6B1;MATHEMATICAL BOLD CAPITAL KAPPA;Lu;0;L;<font> 039A;;;;N;;;;; +1D6B2;MATHEMATICAL BOLD CAPITAL LAMDA;Lu;0;L;<font> 039B;;;;N;;;;; +1D6B3;MATHEMATICAL BOLD CAPITAL MU;Lu;0;L;<font> 039C;;;;N;;;;; +1D6B4;MATHEMATICAL BOLD CAPITAL NU;Lu;0;L;<font> 039D;;;;N;;;;; +1D6B5;MATHEMATICAL BOLD CAPITAL XI;Lu;0;L;<font> 039E;;;;N;;;;; +1D6B6;MATHEMATICAL BOLD CAPITAL OMICRON;Lu;0;L;<font> 039F;;;;N;;;;; +1D6B7;MATHEMATICAL BOLD CAPITAL PI;Lu;0;L;<font> 03A0;;;;N;;;;; +1D6B8;MATHEMATICAL BOLD CAPITAL RHO;Lu;0;L;<font> 03A1;;;;N;;;;; +1D6B9;MATHEMATICAL BOLD CAPITAL THETA SYMBOL;Lu;0;L;<font> 03F4;;;;N;;;;; +1D6BA;MATHEMATICAL BOLD CAPITAL SIGMA;Lu;0;L;<font> 03A3;;;;N;;;;; +1D6BB;MATHEMATICAL BOLD CAPITAL TAU;Lu;0;L;<font> 03A4;;;;N;;;;; +1D6BC;MATHEMATICAL BOLD CAPITAL UPSILON;Lu;0;L;<font> 03A5;;;;N;;;;; +1D6BD;MATHEMATICAL BOLD CAPITAL PHI;Lu;0;L;<font> 03A6;;;;N;;;;; +1D6BE;MATHEMATICAL BOLD CAPITAL CHI;Lu;0;L;<font> 03A7;;;;N;;;;; +1D6BF;MATHEMATICAL BOLD CAPITAL PSI;Lu;0;L;<font> 03A8;;;;N;;;;; +1D6C0;MATHEMATICAL BOLD CAPITAL OMEGA;Lu;0;L;<font> 03A9;;;;N;;;;; +1D6C1;MATHEMATICAL BOLD NABLA;Sm;0;L;<font> 2207;;;;N;;;;; +1D6C2;MATHEMATICAL BOLD SMALL ALPHA;Ll;0;L;<font> 03B1;;;;N;;;;; +1D6C3;MATHEMATICAL BOLD SMALL BETA;Ll;0;L;<font> 03B2;;;;N;;;;; +1D6C4;MATHEMATICAL BOLD SMALL GAMMA;Ll;0;L;<font> 03B3;;;;N;;;;; +1D6C5;MATHEMATICAL BOLD SMALL DELTA;Ll;0;L;<font> 03B4;;;;N;;;;; +1D6C6;MATHEMATICAL BOLD SMALL EPSILON;Ll;0;L;<font> 03B5;;;;N;;;;; +1D6C7;MATHEMATICAL BOLD SMALL ZETA;Ll;0;L;<font> 03B6;;;;N;;;;; +1D6C8;MATHEMATICAL BOLD SMALL ETA;Ll;0;L;<font> 03B7;;;;N;;;;; +1D6C9;MATHEMATICAL BOLD SMALL THETA;Ll;0;L;<font> 03B8;;;;N;;;;; +1D6CA;MATHEMATICAL BOLD SMALL IOTA;Ll;0;L;<font> 03B9;;;;N;;;;; +1D6CB;MATHEMATICAL BOLD SMALL KAPPA;Ll;0;L;<font> 03BA;;;;N;;;;; +1D6CC;MATHEMATICAL BOLD SMALL LAMDA;Ll;0;L;<font> 03BB;;;;N;;;;; +1D6CD;MATHEMATICAL BOLD SMALL MU;Ll;0;L;<font> 03BC;;;;N;;;;; +1D6CE;MATHEMATICAL BOLD SMALL NU;Ll;0;L;<font> 03BD;;;;N;;;;; +1D6CF;MATHEMATICAL BOLD SMALL XI;Ll;0;L;<font> 03BE;;;;N;;;;; +1D6D0;MATHEMATICAL BOLD SMALL OMICRON;Ll;0;L;<font> 03BF;;;;N;;;;; +1D6D1;MATHEMATICAL BOLD SMALL PI;Ll;0;L;<font> 03C0;;;;N;;;;; +1D6D2;MATHEMATICAL BOLD SMALL RHO;Ll;0;L;<font> 03C1;;;;N;;;;; +1D6D3;MATHEMATICAL BOLD SMALL FINAL SIGMA;Ll;0;L;<font> 03C2;;;;N;;;;; +1D6D4;MATHEMATICAL BOLD SMALL SIGMA;Ll;0;L;<font> 03C3;;;;N;;;;; +1D6D5;MATHEMATICAL BOLD SMALL TAU;Ll;0;L;<font> 03C4;;;;N;;;;; +1D6D6;MATHEMATICAL BOLD SMALL UPSILON;Ll;0;L;<font> 03C5;;;;N;;;;; +1D6D7;MATHEMATICAL BOLD SMALL PHI;Ll;0;L;<font> 03C6;;;;N;;;;; +1D6D8;MATHEMATICAL BOLD SMALL CHI;Ll;0;L;<font> 03C7;;;;N;;;;; +1D6D9;MATHEMATICAL BOLD SMALL PSI;Ll;0;L;<font> 03C8;;;;N;;;;; +1D6DA;MATHEMATICAL BOLD SMALL OMEGA;Ll;0;L;<font> 03C9;;;;N;;;;; +1D6DB;MATHEMATICAL BOLD PARTIAL DIFFERENTIAL;Sm;0;L;<font> 2202;;;;N;;;;; +1D6DC;MATHEMATICAL BOLD EPSILON SYMBOL;Ll;0;L;<font> 03F5;;;;N;;;;; +1D6DD;MATHEMATICAL BOLD THETA SYMBOL;Ll;0;L;<font> 03D1;;;;N;;;;; +1D6DE;MATHEMATICAL BOLD KAPPA SYMBOL;Ll;0;L;<font> 03F0;;;;N;;;;; +1D6DF;MATHEMATICAL BOLD PHI SYMBOL;Ll;0;L;<font> 03D5;;;;N;;;;; +1D6E0;MATHEMATICAL BOLD RHO SYMBOL;Ll;0;L;<font> 03F1;;;;N;;;;; +1D6E1;MATHEMATICAL BOLD PI SYMBOL;Ll;0;L;<font> 03D6;;;;N;;;;; +1D6E2;MATHEMATICAL ITALIC CAPITAL ALPHA;Lu;0;L;<font> 0391;;;;N;;;;; +1D6E3;MATHEMATICAL ITALIC CAPITAL BETA;Lu;0;L;<font> 0392;;;;N;;;;; +1D6E4;MATHEMATICAL ITALIC CAPITAL GAMMA;Lu;0;L;<font> 0393;;;;N;;;;; +1D6E5;MATHEMATICAL ITALIC CAPITAL DELTA;Lu;0;L;<font> 0394;;;;N;;;;; +1D6E6;MATHEMATICAL ITALIC CAPITAL EPSILON;Lu;0;L;<font> 0395;;;;N;;;;; +1D6E7;MATHEMATICAL ITALIC CAPITAL ZETA;Lu;0;L;<font> 0396;;;;N;;;;; +1D6E8;MATHEMATICAL ITALIC CAPITAL ETA;Lu;0;L;<font> 0397;;;;N;;;;; +1D6E9;MATHEMATICAL ITALIC CAPITAL THETA;Lu;0;L;<font> 0398;;;;N;;;;; +1D6EA;MATHEMATICAL ITALIC CAPITAL IOTA;Lu;0;L;<font> 0399;;;;N;;;;; +1D6EB;MATHEMATICAL ITALIC CAPITAL KAPPA;Lu;0;L;<font> 039A;;;;N;;;;; +1D6EC;MATHEMATICAL ITALIC CAPITAL LAMDA;Lu;0;L;<font> 039B;;;;N;;;;; +1D6ED;MATHEMATICAL ITALIC CAPITAL MU;Lu;0;L;<font> 039C;;;;N;;;;; +1D6EE;MATHEMATICAL ITALIC CAPITAL NU;Lu;0;L;<font> 039D;;;;N;;;;; +1D6EF;MATHEMATICAL ITALIC CAPITAL XI;Lu;0;L;<font> 039E;;;;N;;;;; +1D6F0;MATHEMATICAL ITALIC CAPITAL OMICRON;Lu;0;L;<font> 039F;;;;N;;;;; +1D6F1;MATHEMATICAL ITALIC CAPITAL PI;Lu;0;L;<font> 03A0;;;;N;;;;; +1D6F2;MATHEMATICAL ITALIC CAPITAL RHO;Lu;0;L;<font> 03A1;;;;N;;;;; +1D6F3;MATHEMATICAL ITALIC CAPITAL THETA SYMBOL;Lu;0;L;<font> 03F4;;;;N;;;;; +1D6F4;MATHEMATICAL ITALIC CAPITAL SIGMA;Lu;0;L;<font> 03A3;;;;N;;;;; +1D6F5;MATHEMATICAL ITALIC CAPITAL TAU;Lu;0;L;<font> 03A4;;;;N;;;;; +1D6F6;MATHEMATICAL ITALIC CAPITAL UPSILON;Lu;0;L;<font> 03A5;;;;N;;;;; +1D6F7;MATHEMATICAL ITALIC CAPITAL PHI;Lu;0;L;<font> 03A6;;;;N;;;;; +1D6F8;MATHEMATICAL ITALIC CAPITAL CHI;Lu;0;L;<font> 03A7;;;;N;;;;; +1D6F9;MATHEMATICAL ITALIC CAPITAL PSI;Lu;0;L;<font> 03A8;;;;N;;;;; +1D6FA;MATHEMATICAL ITALIC CAPITAL OMEGA;Lu;0;L;<font> 03A9;;;;N;;;;; +1D6FB;MATHEMATICAL ITALIC NABLA;Sm;0;L;<font> 2207;;;;N;;;;; +1D6FC;MATHEMATICAL ITALIC SMALL ALPHA;Ll;0;L;<font> 03B1;;;;N;;;;; +1D6FD;MATHEMATICAL ITALIC SMALL BETA;Ll;0;L;<font> 03B2;;;;N;;;;; +1D6FE;MATHEMATICAL ITALIC SMALL GAMMA;Ll;0;L;<font> 03B3;;;;N;;;;; +1D6FF;MATHEMATICAL ITALIC SMALL DELTA;Ll;0;L;<font> 03B4;;;;N;;;;; +1D700;MATHEMATICAL ITALIC SMALL EPSILON;Ll;0;L;<font> 03B5;;;;N;;;;; +1D701;MATHEMATICAL ITALIC SMALL ZETA;Ll;0;L;<font> 03B6;;;;N;;;;; +1D702;MATHEMATICAL ITALIC SMALL ETA;Ll;0;L;<font> 03B7;;;;N;;;;; +1D703;MATHEMATICAL ITALIC SMALL THETA;Ll;0;L;<font> 03B8;;;;N;;;;; +1D704;MATHEMATICAL ITALIC SMALL IOTA;Ll;0;L;<font> 03B9;;;;N;;;;; +1D705;MATHEMATICAL ITALIC SMALL KAPPA;Ll;0;L;<font> 03BA;;;;N;;;;; +1D706;MATHEMATICAL ITALIC SMALL LAMDA;Ll;0;L;<font> 03BB;;;;N;;;;; +1D707;MATHEMATICAL ITALIC SMALL MU;Ll;0;L;<font> 03BC;;;;N;;;;; +1D708;MATHEMATICAL ITALIC SMALL NU;Ll;0;L;<font> 03BD;;;;N;;;;; +1D709;MATHEMATICAL ITALIC SMALL XI;Ll;0;L;<font> 03BE;;;;N;;;;; +1D70A;MATHEMATICAL ITALIC SMALL OMICRON;Ll;0;L;<font> 03BF;;;;N;;;;; +1D70B;MATHEMATICAL ITALIC SMALL PI;Ll;0;L;<font> 03C0;;;;N;;;;; +1D70C;MATHEMATICAL ITALIC SMALL RHO;Ll;0;L;<font> 03C1;;;;N;;;;; +1D70D;MATHEMATICAL ITALIC SMALL FINAL SIGMA;Ll;0;L;<font> 03C2;;;;N;;;;; +1D70E;MATHEMATICAL ITALIC SMALL SIGMA;Ll;0;L;<font> 03C3;;;;N;;;;; +1D70F;MATHEMATICAL ITALIC SMALL TAU;Ll;0;L;<font> 03C4;;;;N;;;;; +1D710;MATHEMATICAL ITALIC SMALL UPSILON;Ll;0;L;<font> 03C5;;;;N;;;;; +1D711;MATHEMATICAL ITALIC SMALL PHI;Ll;0;L;<font> 03C6;;;;N;;;;; +1D712;MATHEMATICAL ITALIC SMALL CHI;Ll;0;L;<font> 03C7;;;;N;;;;; +1D713;MATHEMATICAL ITALIC SMALL PSI;Ll;0;L;<font> 03C8;;;;N;;;;; +1D714;MATHEMATICAL ITALIC SMALL OMEGA;Ll;0;L;<font> 03C9;;;;N;;;;; +1D715;MATHEMATICAL ITALIC PARTIAL DIFFERENTIAL;Sm;0;L;<font> 2202;;;;N;;;;; +1D716;MATHEMATICAL ITALIC EPSILON SYMBOL;Ll;0;L;<font> 03F5;;;;N;;;;; +1D717;MATHEMATICAL ITALIC THETA SYMBOL;Ll;0;L;<font> 03D1;;;;N;;;;; +1D718;MATHEMATICAL ITALIC KAPPA SYMBOL;Ll;0;L;<font> 03F0;;;;N;;;;; +1D719;MATHEMATICAL ITALIC PHI SYMBOL;Ll;0;L;<font> 03D5;;;;N;;;;; +1D71A;MATHEMATICAL ITALIC RHO SYMBOL;Ll;0;L;<font> 03F1;;;;N;;;;; +1D71B;MATHEMATICAL ITALIC PI SYMBOL;Ll;0;L;<font> 03D6;;;;N;;;;; +1D71C;MATHEMATICAL BOLD ITALIC CAPITAL ALPHA;Lu;0;L;<font> 0391;;;;N;;;;; +1D71D;MATHEMATICAL BOLD ITALIC CAPITAL BETA;Lu;0;L;<font> 0392;;;;N;;;;; +1D71E;MATHEMATICAL BOLD ITALIC CAPITAL GAMMA;Lu;0;L;<font> 0393;;;;N;;;;; +1D71F;MATHEMATICAL BOLD ITALIC CAPITAL DELTA;Lu;0;L;<font> 0394;;;;N;;;;; +1D720;MATHEMATICAL BOLD ITALIC CAPITAL EPSILON;Lu;0;L;<font> 0395;;;;N;;;;; +1D721;MATHEMATICAL BOLD ITALIC CAPITAL ZETA;Lu;0;L;<font> 0396;;;;N;;;;; +1D722;MATHEMATICAL BOLD ITALIC CAPITAL ETA;Lu;0;L;<font> 0397;;;;N;;;;; +1D723;MATHEMATICAL BOLD ITALIC CAPITAL THETA;Lu;0;L;<font> 0398;;;;N;;;;; +1D724;MATHEMATICAL BOLD ITALIC CAPITAL IOTA;Lu;0;L;<font> 0399;;;;N;;;;; +1D725;MATHEMATICAL BOLD ITALIC CAPITAL KAPPA;Lu;0;L;<font> 039A;;;;N;;;;; +1D726;MATHEMATICAL BOLD ITALIC CAPITAL LAMDA;Lu;0;L;<font> 039B;;;;N;;;;; +1D727;MATHEMATICAL BOLD ITALIC CAPITAL MU;Lu;0;L;<font> 039C;;;;N;;;;; +1D728;MATHEMATICAL BOLD ITALIC CAPITAL NU;Lu;0;L;<font> 039D;;;;N;;;;; +1D729;MATHEMATICAL BOLD ITALIC CAPITAL XI;Lu;0;L;<font> 039E;;;;N;;;;; +1D72A;MATHEMATICAL BOLD ITALIC CAPITAL OMICRON;Lu;0;L;<font> 039F;;;;N;;;;; +1D72B;MATHEMATICAL BOLD ITALIC CAPITAL PI;Lu;0;L;<font> 03A0;;;;N;;;;; +1D72C;MATHEMATICAL BOLD ITALIC CAPITAL RHO;Lu;0;L;<font> 03A1;;;;N;;;;; +1D72D;MATHEMATICAL BOLD ITALIC CAPITAL THETA SYMBOL;Lu;0;L;<font> 03F4;;;;N;;;;; +1D72E;MATHEMATICAL BOLD ITALIC CAPITAL SIGMA;Lu;0;L;<font> 03A3;;;;N;;;;; +1D72F;MATHEMATICAL BOLD ITALIC CAPITAL TAU;Lu;0;L;<font> 03A4;;;;N;;;;; +1D730;MATHEMATICAL BOLD ITALIC CAPITAL UPSILON;Lu;0;L;<font> 03A5;;;;N;;;;; +1D731;MATHEMATICAL BOLD ITALIC CAPITAL PHI;Lu;0;L;<font> 03A6;;;;N;;;;; +1D732;MATHEMATICAL BOLD ITALIC CAPITAL CHI;Lu;0;L;<font> 03A7;;;;N;;;;; +1D733;MATHEMATICAL BOLD ITALIC CAPITAL PSI;Lu;0;L;<font> 03A8;;;;N;;;;; +1D734;MATHEMATICAL BOLD ITALIC CAPITAL OMEGA;Lu;0;L;<font> 03A9;;;;N;;;;; +1D735;MATHEMATICAL BOLD ITALIC NABLA;Sm;0;L;<font> 2207;;;;N;;;;; +1D736;MATHEMATICAL BOLD ITALIC SMALL ALPHA;Ll;0;L;<font> 03B1;;;;N;;;;; +1D737;MATHEMATICAL BOLD ITALIC SMALL BETA;Ll;0;L;<font> 03B2;;;;N;;;;; +1D738;MATHEMATICAL BOLD ITALIC SMALL GAMMA;Ll;0;L;<font> 03B3;;;;N;;;;; +1D739;MATHEMATICAL BOLD ITALIC SMALL DELTA;Ll;0;L;<font> 03B4;;;;N;;;;; +1D73A;MATHEMATICAL BOLD ITALIC SMALL EPSILON;Ll;0;L;<font> 03B5;;;;N;;;;; +1D73B;MATHEMATICAL BOLD ITALIC SMALL ZETA;Ll;0;L;<font> 03B6;;;;N;;;;; +1D73C;MATHEMATICAL BOLD ITALIC SMALL ETA;Ll;0;L;<font> 03B7;;;;N;;;;; +1D73D;MATHEMATICAL BOLD ITALIC SMALL THETA;Ll;0;L;<font> 03B8;;;;N;;;;; +1D73E;MATHEMATICAL BOLD ITALIC SMALL IOTA;Ll;0;L;<font> 03B9;;;;N;;;;; +1D73F;MATHEMATICAL BOLD ITALIC SMALL KAPPA;Ll;0;L;<font> 03BA;;;;N;;;;; +1D740;MATHEMATICAL BOLD ITALIC SMALL LAMDA;Ll;0;L;<font> 03BB;;;;N;;;;; +1D741;MATHEMATICAL BOLD ITALIC SMALL MU;Ll;0;L;<font> 03BC;;;;N;;;;; +1D742;MATHEMATICAL BOLD ITALIC SMALL NU;Ll;0;L;<font> 03BD;;;;N;;;;; +1D743;MATHEMATICAL BOLD ITALIC SMALL XI;Ll;0;L;<font> 03BE;;;;N;;;;; +1D744;MATHEMATICAL BOLD ITALIC SMALL OMICRON;Ll;0;L;<font> 03BF;;;;N;;;;; +1D745;MATHEMATICAL BOLD ITALIC SMALL PI;Ll;0;L;<font> 03C0;;;;N;;;;; +1D746;MATHEMATICAL BOLD ITALIC SMALL RHO;Ll;0;L;<font> 03C1;;;;N;;;;; +1D747;MATHEMATICAL BOLD ITALIC SMALL FINAL SIGMA;Ll;0;L;<font> 03C2;;;;N;;;;; +1D748;MATHEMATICAL BOLD ITALIC SMALL SIGMA;Ll;0;L;<font> 03C3;;;;N;;;;; +1D749;MATHEMATICAL BOLD ITALIC SMALL TAU;Ll;0;L;<font> 03C4;;;;N;;;;; +1D74A;MATHEMATICAL BOLD ITALIC SMALL UPSILON;Ll;0;L;<font> 03C5;;;;N;;;;; +1D74B;MATHEMATICAL BOLD ITALIC SMALL PHI;Ll;0;L;<font> 03C6;;;;N;;;;; +1D74C;MATHEMATICAL BOLD ITALIC SMALL CHI;Ll;0;L;<font> 03C7;;;;N;;;;; +1D74D;MATHEMATICAL BOLD ITALIC SMALL PSI;Ll;0;L;<font> 03C8;;;;N;;;;; +1D74E;MATHEMATICAL BOLD ITALIC SMALL OMEGA;Ll;0;L;<font> 03C9;;;;N;;;;; +1D74F;MATHEMATICAL BOLD ITALIC PARTIAL DIFFERENTIAL;Sm;0;L;<font> 2202;;;;N;;;;; +1D750;MATHEMATICAL BOLD ITALIC EPSILON SYMBOL;Ll;0;L;<font> 03F5;;;;N;;;;; +1D751;MATHEMATICAL BOLD ITALIC THETA SYMBOL;Ll;0;L;<font> 03D1;;;;N;;;;; +1D752;MATHEMATICAL BOLD ITALIC KAPPA SYMBOL;Ll;0;L;<font> 03F0;;;;N;;;;; +1D753;MATHEMATICAL BOLD ITALIC PHI SYMBOL;Ll;0;L;<font> 03D5;;;;N;;;;; +1D754;MATHEMATICAL BOLD ITALIC RHO SYMBOL;Ll;0;L;<font> 03F1;;;;N;;;;; +1D755;MATHEMATICAL BOLD ITALIC PI SYMBOL;Ll;0;L;<font> 03D6;;;;N;;;;; +1D756;MATHEMATICAL SANS-SERIF BOLD CAPITAL ALPHA;Lu;0;L;<font> 0391;;;;N;;;;; +1D757;MATHEMATICAL SANS-SERIF BOLD CAPITAL BETA;Lu;0;L;<font> 0392;;;;N;;;;; +1D758;MATHEMATICAL SANS-SERIF BOLD CAPITAL GAMMA;Lu;0;L;<font> 0393;;;;N;;;;; +1D759;MATHEMATICAL SANS-SERIF BOLD CAPITAL DELTA;Lu;0;L;<font> 0394;;;;N;;;;; +1D75A;MATHEMATICAL SANS-SERIF BOLD CAPITAL EPSILON;Lu;0;L;<font> 0395;;;;N;;;;; +1D75B;MATHEMATICAL SANS-SERIF BOLD CAPITAL ZETA;Lu;0;L;<font> 0396;;;;N;;;;; +1D75C;MATHEMATICAL SANS-SERIF BOLD CAPITAL ETA;Lu;0;L;<font> 0397;;;;N;;;;; +1D75D;MATHEMATICAL SANS-SERIF BOLD CAPITAL THETA;Lu;0;L;<font> 0398;;;;N;;;;; +1D75E;MATHEMATICAL SANS-SERIF BOLD CAPITAL IOTA;Lu;0;L;<font> 0399;;;;N;;;;; +1D75F;MATHEMATICAL SANS-SERIF BOLD CAPITAL KAPPA;Lu;0;L;<font> 039A;;;;N;;;;; +1D760;MATHEMATICAL SANS-SERIF BOLD CAPITAL LAMDA;Lu;0;L;<font> 039B;;;;N;;;;; +1D761;MATHEMATICAL SANS-SERIF BOLD CAPITAL MU;Lu;0;L;<font> 039C;;;;N;;;;; +1D762;MATHEMATICAL SANS-SERIF BOLD CAPITAL NU;Lu;0;L;<font> 039D;;;;N;;;;; +1D763;MATHEMATICAL SANS-SERIF BOLD CAPITAL XI;Lu;0;L;<font> 039E;;;;N;;;;; +1D764;MATHEMATICAL SANS-SERIF BOLD CAPITAL OMICRON;Lu;0;L;<font> 039F;;;;N;;;;; +1D765;MATHEMATICAL SANS-SERIF BOLD CAPITAL PI;Lu;0;L;<font> 03A0;;;;N;;;;; +1D766;MATHEMATICAL SANS-SERIF BOLD CAPITAL RHO;Lu;0;L;<font> 03A1;;;;N;;;;; +1D767;MATHEMATICAL SANS-SERIF BOLD CAPITAL THETA SYMBOL;Lu;0;L;<font> 03F4;;;;N;;;;; +1D768;MATHEMATICAL SANS-SERIF BOLD CAPITAL SIGMA;Lu;0;L;<font> 03A3;;;;N;;;;; +1D769;MATHEMATICAL SANS-SERIF BOLD CAPITAL TAU;Lu;0;L;<font> 03A4;;;;N;;;;; +1D76A;MATHEMATICAL SANS-SERIF BOLD CAPITAL UPSILON;Lu;0;L;<font> 03A5;;;;N;;;;; +1D76B;MATHEMATICAL SANS-SERIF BOLD CAPITAL PHI;Lu;0;L;<font> 03A6;;;;N;;;;; +1D76C;MATHEMATICAL SANS-SERIF BOLD CAPITAL CHI;Lu;0;L;<font> 03A7;;;;N;;;;; +1D76D;MATHEMATICAL SANS-SERIF BOLD CAPITAL PSI;Lu;0;L;<font> 03A8;;;;N;;;;; +1D76E;MATHEMATICAL SANS-SERIF BOLD CAPITAL OMEGA;Lu;0;L;<font> 03A9;;;;N;;;;; +1D76F;MATHEMATICAL SANS-SERIF BOLD NABLA;Sm;0;L;<font> 2207;;;;N;;;;; +1D770;MATHEMATICAL SANS-SERIF BOLD SMALL ALPHA;Ll;0;L;<font> 03B1;;;;N;;;;; +1D771;MATHEMATICAL SANS-SERIF BOLD SMALL BETA;Ll;0;L;<font> 03B2;;;;N;;;;; +1D772;MATHEMATICAL SANS-SERIF BOLD SMALL GAMMA;Ll;0;L;<font> 03B3;;;;N;;;;; +1D773;MATHEMATICAL SANS-SERIF BOLD SMALL DELTA;Ll;0;L;<font> 03B4;;;;N;;;;; +1D774;MATHEMATICAL SANS-SERIF BOLD SMALL EPSILON;Ll;0;L;<font> 03B5;;;;N;;;;; +1D775;MATHEMATICAL SANS-SERIF BOLD SMALL ZETA;Ll;0;L;<font> 03B6;;;;N;;;;; +1D776;MATHEMATICAL SANS-SERIF BOLD SMALL ETA;Ll;0;L;<font> 03B7;;;;N;;;;; +1D777;MATHEMATICAL SANS-SERIF BOLD SMALL THETA;Ll;0;L;<font> 03B8;;;;N;;;;; +1D778;MATHEMATICAL SANS-SERIF BOLD SMALL IOTA;Ll;0;L;<font> 03B9;;;;N;;;;; +1D779;MATHEMATICAL SANS-SERIF BOLD SMALL KAPPA;Ll;0;L;<font> 03BA;;;;N;;;;; +1D77A;MATHEMATICAL SANS-SERIF BOLD SMALL LAMDA;Ll;0;L;<font> 03BB;;;;N;;;;; +1D77B;MATHEMATICAL SANS-SERIF BOLD SMALL MU;Ll;0;L;<font> 03BC;;;;N;;;;; +1D77C;MATHEMATICAL SANS-SERIF BOLD SMALL NU;Ll;0;L;<font> 03BD;;;;N;;;;; +1D77D;MATHEMATICAL SANS-SERIF BOLD SMALL XI;Ll;0;L;<font> 03BE;;;;N;;;;; +1D77E;MATHEMATICAL SANS-SERIF BOLD SMALL OMICRON;Ll;0;L;<font> 03BF;;;;N;;;;; +1D77F;MATHEMATICAL SANS-SERIF BOLD SMALL PI;Ll;0;L;<font> 03C0;;;;N;;;;; +1D780;MATHEMATICAL SANS-SERIF BOLD SMALL RHO;Ll;0;L;<font> 03C1;;;;N;;;;; +1D781;MATHEMATICAL SANS-SERIF BOLD SMALL FINAL SIGMA;Ll;0;L;<font> 03C2;;;;N;;;;; +1D782;MATHEMATICAL SANS-SERIF BOLD SMALL SIGMA;Ll;0;L;<font> 03C3;;;;N;;;;; +1D783;MATHEMATICAL SANS-SERIF BOLD SMALL TAU;Ll;0;L;<font> 03C4;;;;N;;;;; +1D784;MATHEMATICAL SANS-SERIF BOLD SMALL UPSILON;Ll;0;L;<font> 03C5;;;;N;;;;; +1D785;MATHEMATICAL SANS-SERIF BOLD SMALL PHI;Ll;0;L;<font> 03C6;;;;N;;;;; +1D786;MATHEMATICAL SANS-SERIF BOLD SMALL CHI;Ll;0;L;<font> 03C7;;;;N;;;;; +1D787;MATHEMATICAL SANS-SERIF BOLD SMALL PSI;Ll;0;L;<font> 03C8;;;;N;;;;; +1D788;MATHEMATICAL SANS-SERIF BOLD SMALL OMEGA;Ll;0;L;<font> 03C9;;;;N;;;;; +1D789;MATHEMATICAL SANS-SERIF BOLD PARTIAL DIFFERENTIAL;Sm;0;L;<font> 2202;;;;N;;;;; +1D78A;MATHEMATICAL SANS-SERIF BOLD EPSILON SYMBOL;Ll;0;L;<font> 03F5;;;;N;;;;; +1D78B;MATHEMATICAL SANS-SERIF BOLD THETA SYMBOL;Ll;0;L;<font> 03D1;;;;N;;;;; +1D78C;MATHEMATICAL SANS-SERIF BOLD KAPPA SYMBOL;Ll;0;L;<font> 03F0;;;;N;;;;; +1D78D;MATHEMATICAL SANS-SERIF BOLD PHI SYMBOL;Ll;0;L;<font> 03D5;;;;N;;;;; +1D78E;MATHEMATICAL SANS-SERIF BOLD RHO SYMBOL;Ll;0;L;<font> 03F1;;;;N;;;;; +1D78F;MATHEMATICAL SANS-SERIF BOLD PI SYMBOL;Ll;0;L;<font> 03D6;;;;N;;;;; +1D790;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL ALPHA;Lu;0;L;<font> 0391;;;;N;;;;; +1D791;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL BETA;Lu;0;L;<font> 0392;;;;N;;;;; +1D792;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL GAMMA;Lu;0;L;<font> 0393;;;;N;;;;; +1D793;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL DELTA;Lu;0;L;<font> 0394;;;;N;;;;; +1D794;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL EPSILON;Lu;0;L;<font> 0395;;;;N;;;;; +1D795;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL ZETA;Lu;0;L;<font> 0396;;;;N;;;;; +1D796;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL ETA;Lu;0;L;<font> 0397;;;;N;;;;; +1D797;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL THETA;Lu;0;L;<font> 0398;;;;N;;;;; +1D798;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL IOTA;Lu;0;L;<font> 0399;;;;N;;;;; +1D799;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL KAPPA;Lu;0;L;<font> 039A;;;;N;;;;; +1D79A;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL LAMDA;Lu;0;L;<font> 039B;;;;N;;;;; +1D79B;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL MU;Lu;0;L;<font> 039C;;;;N;;;;; +1D79C;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL NU;Lu;0;L;<font> 039D;;;;N;;;;; +1D79D;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL XI;Lu;0;L;<font> 039E;;;;N;;;;; +1D79E;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL OMICRON;Lu;0;L;<font> 039F;;;;N;;;;; +1D79F;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL PI;Lu;0;L;<font> 03A0;;;;N;;;;; +1D7A0;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL RHO;Lu;0;L;<font> 03A1;;;;N;;;;; +1D7A1;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL THETA SYMBOL;Lu;0;L;<font> 03F4;;;;N;;;;; +1D7A2;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL SIGMA;Lu;0;L;<font> 03A3;;;;N;;;;; +1D7A3;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL TAU;Lu;0;L;<font> 03A4;;;;N;;;;; +1D7A4;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL UPSILON;Lu;0;L;<font> 03A5;;;;N;;;;; +1D7A5;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL PHI;Lu;0;L;<font> 03A6;;;;N;;;;; +1D7A6;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL CHI;Lu;0;L;<font> 03A7;;;;N;;;;; +1D7A7;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL PSI;Lu;0;L;<font> 03A8;;;;N;;;;; +1D7A8;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL OMEGA;Lu;0;L;<font> 03A9;;;;N;;;;; +1D7A9;MATHEMATICAL SANS-SERIF BOLD ITALIC NABLA;Sm;0;L;<font> 2207;;;;N;;;;; +1D7AA;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL ALPHA;Ll;0;L;<font> 03B1;;;;N;;;;; +1D7AB;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL BETA;Ll;0;L;<font> 03B2;;;;N;;;;; +1D7AC;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL GAMMA;Ll;0;L;<font> 03B3;;;;N;;;;; +1D7AD;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL DELTA;Ll;0;L;<font> 03B4;;;;N;;;;; +1D7AE;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL EPSILON;Ll;0;L;<font> 03B5;;;;N;;;;; +1D7AF;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL ZETA;Ll;0;L;<font> 03B6;;;;N;;;;; +1D7B0;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL ETA;Ll;0;L;<font> 03B7;;;;N;;;;; +1D7B1;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL THETA;Ll;0;L;<font> 03B8;;;;N;;;;; +1D7B2;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL IOTA;Ll;0;L;<font> 03B9;;;;N;;;;; +1D7B3;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL KAPPA;Ll;0;L;<font> 03BA;;;;N;;;;; +1D7B4;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL LAMDA;Ll;0;L;<font> 03BB;;;;N;;;;; +1D7B5;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL MU;Ll;0;L;<font> 03BC;;;;N;;;;; +1D7B6;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL NU;Ll;0;L;<font> 03BD;;;;N;;;;; +1D7B7;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL XI;Ll;0;L;<font> 03BE;;;;N;;;;; +1D7B8;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL OMICRON;Ll;0;L;<font> 03BF;;;;N;;;;; +1D7B9;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL PI;Ll;0;L;<font> 03C0;;;;N;;;;; +1D7BA;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL RHO;Ll;0;L;<font> 03C1;;;;N;;;;; +1D7BB;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL FINAL SIGMA;Ll;0;L;<font> 03C2;;;;N;;;;; +1D7BC;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL SIGMA;Ll;0;L;<font> 03C3;;;;N;;;;; +1D7BD;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL TAU;Ll;0;L;<font> 03C4;;;;N;;;;; +1D7BE;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL UPSILON;Ll;0;L;<font> 03C5;;;;N;;;;; +1D7BF;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL PHI;Ll;0;L;<font> 03C6;;;;N;;;;; +1D7C0;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL CHI;Ll;0;L;<font> 03C7;;;;N;;;;; +1D7C1;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL PSI;Ll;0;L;<font> 03C8;;;;N;;;;; +1D7C2;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL OMEGA;Ll;0;L;<font> 03C9;;;;N;;;;; +1D7C3;MATHEMATICAL SANS-SERIF BOLD ITALIC PARTIAL DIFFERENTIAL;Sm;0;L;<font> 2202;;;;N;;;;; +1D7C4;MATHEMATICAL SANS-SERIF BOLD ITALIC EPSILON SYMBOL;Ll;0;L;<font> 03F5;;;;N;;;;; +1D7C5;MATHEMATICAL SANS-SERIF BOLD ITALIC THETA SYMBOL;Ll;0;L;<font> 03D1;;;;N;;;;; +1D7C6;MATHEMATICAL SANS-SERIF BOLD ITALIC KAPPA SYMBOL;Ll;0;L;<font> 03F0;;;;N;;;;; +1D7C7;MATHEMATICAL SANS-SERIF BOLD ITALIC PHI SYMBOL;Ll;0;L;<font> 03D5;;;;N;;;;; +1D7C8;MATHEMATICAL SANS-SERIF BOLD ITALIC RHO SYMBOL;Ll;0;L;<font> 03F1;;;;N;;;;; +1D7C9;MATHEMATICAL SANS-SERIF BOLD ITALIC PI SYMBOL;Ll;0;L;<font> 03D6;;;;N;;;;; +1D7CE;MATHEMATICAL BOLD DIGIT ZERO;Nd;0;EN;<font> 0030;0;0;0;N;;;;; +1D7CF;MATHEMATICAL BOLD DIGIT ONE;Nd;0;EN;<font> 0031;1;1;1;N;;;;; +1D7D0;MATHEMATICAL BOLD DIGIT TWO;Nd;0;EN;<font> 0032;2;2;2;N;;;;; +1D7D1;MATHEMATICAL BOLD DIGIT THREE;Nd;0;EN;<font> 0033;3;3;3;N;;;;; +1D7D2;MATHEMATICAL BOLD DIGIT FOUR;Nd;0;EN;<font> 0034;4;4;4;N;;;;; +1D7D3;MATHEMATICAL BOLD DIGIT FIVE;Nd;0;EN;<font> 0035;5;5;5;N;;;;; +1D7D4;MATHEMATICAL BOLD DIGIT SIX;Nd;0;EN;<font> 0036;6;6;6;N;;;;; +1D7D5;MATHEMATICAL BOLD DIGIT SEVEN;Nd;0;EN;<font> 0037;7;7;7;N;;;;; +1D7D6;MATHEMATICAL BOLD DIGIT EIGHT;Nd;0;EN;<font> 0038;8;8;8;N;;;;; +1D7D7;MATHEMATICAL BOLD DIGIT NINE;Nd;0;EN;<font> 0039;9;9;9;N;;;;; +1D7D8;MATHEMATICAL DOUBLE-STRUCK DIGIT ZERO;Nd;0;EN;<font> 0030;0;0;0;N;;;;; +1D7D9;MATHEMATICAL DOUBLE-STRUCK DIGIT ONE;Nd;0;EN;<font> 0031;1;1;1;N;;;;; +1D7DA;MATHEMATICAL DOUBLE-STRUCK DIGIT TWO;Nd;0;EN;<font> 0032;2;2;2;N;;;;; +1D7DB;MATHEMATICAL DOUBLE-STRUCK DIGIT THREE;Nd;0;EN;<font> 0033;3;3;3;N;;;;; +1D7DC;MATHEMATICAL DOUBLE-STRUCK DIGIT FOUR;Nd;0;EN;<font> 0034;4;4;4;N;;;;; +1D7DD;MATHEMATICAL DOUBLE-STRUCK DIGIT FIVE;Nd;0;EN;<font> 0035;5;5;5;N;;;;; +1D7DE;MATHEMATICAL DOUBLE-STRUCK DIGIT SIX;Nd;0;EN;<font> 0036;6;6;6;N;;;;; +1D7DF;MATHEMATICAL DOUBLE-STRUCK DIGIT SEVEN;Nd;0;EN;<font> 0037;7;7;7;N;;;;; +1D7E0;MATHEMATICAL DOUBLE-STRUCK DIGIT EIGHT;Nd;0;EN;<font> 0038;8;8;8;N;;;;; +1D7E1;MATHEMATICAL DOUBLE-STRUCK DIGIT NINE;Nd;0;EN;<font> 0039;9;9;9;N;;;;; +1D7E2;MATHEMATICAL SANS-SERIF DIGIT ZERO;Nd;0;EN;<font> 0030;0;0;0;N;;;;; +1D7E3;MATHEMATICAL SANS-SERIF DIGIT ONE;Nd;0;EN;<font> 0031;1;1;1;N;;;;; +1D7E4;MATHEMATICAL SANS-SERIF DIGIT TWO;Nd;0;EN;<font> 0032;2;2;2;N;;;;; +1D7E5;MATHEMATICAL SANS-SERIF DIGIT THREE;Nd;0;EN;<font> 0033;3;3;3;N;;;;; +1D7E6;MATHEMATICAL SANS-SERIF DIGIT FOUR;Nd;0;EN;<font> 0034;4;4;4;N;;;;; +1D7E7;MATHEMATICAL SANS-SERIF DIGIT FIVE;Nd;0;EN;<font> 0035;5;5;5;N;;;;; +1D7E8;MATHEMATICAL SANS-SERIF DIGIT SIX;Nd;0;EN;<font> 0036;6;6;6;N;;;;; +1D7E9;MATHEMATICAL SANS-SERIF DIGIT SEVEN;Nd;0;EN;<font> 0037;7;7;7;N;;;;; +1D7EA;MATHEMATICAL SANS-SERIF DIGIT EIGHT;Nd;0;EN;<font> 0038;8;8;8;N;;;;; +1D7EB;MATHEMATICAL SANS-SERIF DIGIT NINE;Nd;0;EN;<font> 0039;9;9;9;N;;;;; +1D7EC;MATHEMATICAL SANS-SERIF BOLD DIGIT ZERO;Nd;0;EN;<font> 0030;0;0;0;N;;;;; +1D7ED;MATHEMATICAL SANS-SERIF BOLD DIGIT ONE;Nd;0;EN;<font> 0031;1;1;1;N;;;;; +1D7EE;MATHEMATICAL SANS-SERIF BOLD DIGIT TWO;Nd;0;EN;<font> 0032;2;2;2;N;;;;; +1D7EF;MATHEMATICAL SANS-SERIF BOLD DIGIT THREE;Nd;0;EN;<font> 0033;3;3;3;N;;;;; +1D7F0;MATHEMATICAL SANS-SERIF BOLD DIGIT FOUR;Nd;0;EN;<font> 0034;4;4;4;N;;;;; +1D7F1;MATHEMATICAL SANS-SERIF BOLD DIGIT FIVE;Nd;0;EN;<font> 0035;5;5;5;N;;;;; +1D7F2;MATHEMATICAL SANS-SERIF BOLD DIGIT SIX;Nd;0;EN;<font> 0036;6;6;6;N;;;;; +1D7F3;MATHEMATICAL SANS-SERIF BOLD DIGIT SEVEN;Nd;0;EN;<font> 0037;7;7;7;N;;;;; +1D7F4;MATHEMATICAL SANS-SERIF BOLD DIGIT EIGHT;Nd;0;EN;<font> 0038;8;8;8;N;;;;; +1D7F5;MATHEMATICAL SANS-SERIF BOLD DIGIT NINE;Nd;0;EN;<font> 0039;9;9;9;N;;;;; +1D7F6;MATHEMATICAL MONOSPACE DIGIT ZERO;Nd;0;EN;<font> 0030;0;0;0;N;;;;; +1D7F7;MATHEMATICAL MONOSPACE DIGIT ONE;Nd;0;EN;<font> 0031;1;1;1;N;;;;; +1D7F8;MATHEMATICAL MONOSPACE DIGIT TWO;Nd;0;EN;<font> 0032;2;2;2;N;;;;; +1D7F9;MATHEMATICAL MONOSPACE DIGIT THREE;Nd;0;EN;<font> 0033;3;3;3;N;;;;; +1D7FA;MATHEMATICAL MONOSPACE DIGIT FOUR;Nd;0;EN;<font> 0034;4;4;4;N;;;;; +1D7FB;MATHEMATICAL MONOSPACE DIGIT FIVE;Nd;0;EN;<font> 0035;5;5;5;N;;;;; +1D7FC;MATHEMATICAL MONOSPACE DIGIT SIX;Nd;0;EN;<font> 0036;6;6;6;N;;;;; +1D7FD;MATHEMATICAL MONOSPACE DIGIT SEVEN;Nd;0;EN;<font> 0037;7;7;7;N;;;;; +1D7FE;MATHEMATICAL MONOSPACE DIGIT EIGHT;Nd;0;EN;<font> 0038;8;8;8;N;;;;; +1D7FF;MATHEMATICAL MONOSPACE DIGIT NINE;Nd;0;EN;<font> 0039;9;9;9;N;;;;; +20000;<CJK Ideograph Extension B, First>;Lo;0;L;;;;;N;;;;; +2A6D6;<CJK Ideograph Extension B, Last>;Lo;0;L;;;;;N;;;;; +2F800;CJK COMPATIBILITY IDEOGRAPH-2F800;Lo;0;L;4E3D;;;;N;;;;; +2F801;CJK COMPATIBILITY IDEOGRAPH-2F801;Lo;0;L;4E38;;;;N;;;;; +2F802;CJK COMPATIBILITY IDEOGRAPH-2F802;Lo;0;L;4E41;;;;N;;;;; +2F803;CJK COMPATIBILITY IDEOGRAPH-2F803;Lo;0;L;20122;;;;N;;;;; +2F804;CJK COMPATIBILITY IDEOGRAPH-2F804;Lo;0;L;4F60;;;;N;;;;; +2F805;CJK COMPATIBILITY IDEOGRAPH-2F805;Lo;0;L;4FAE;;;;N;;;;; +2F806;CJK COMPATIBILITY IDEOGRAPH-2F806;Lo;0;L;4FBB;;;;N;;;;; +2F807;CJK COMPATIBILITY IDEOGRAPH-2F807;Lo;0;L;5002;;;;N;;;;; +2F808;CJK COMPATIBILITY IDEOGRAPH-2F808;Lo;0;L;507A;;;;N;;;;; +2F809;CJK COMPATIBILITY IDEOGRAPH-2F809;Lo;0;L;5099;;;;N;;;;; +2F80A;CJK COMPATIBILITY IDEOGRAPH-2F80A;Lo;0;L;50E7;;;;N;;;;; +2F80B;CJK COMPATIBILITY IDEOGRAPH-2F80B;Lo;0;L;50CF;;;;N;;;;; +2F80C;CJK COMPATIBILITY IDEOGRAPH-2F80C;Lo;0;L;349E;;;;N;;;;; +2F80D;CJK COMPATIBILITY IDEOGRAPH-2F80D;Lo;0;L;2063A;;;;N;;;;; +2F80E;CJK COMPATIBILITY IDEOGRAPH-2F80E;Lo;0;L;514D;;;;N;;;;; +2F80F;CJK COMPATIBILITY IDEOGRAPH-2F80F;Lo;0;L;5154;;;;N;;;;; +2F810;CJK COMPATIBILITY IDEOGRAPH-2F810;Lo;0;L;5164;;;;N;;;;; +2F811;CJK COMPATIBILITY IDEOGRAPH-2F811;Lo;0;L;5177;;;;N;;;;; +2F812;CJK COMPATIBILITY IDEOGRAPH-2F812;Lo;0;L;2051C;;;;N;;;;; +2F813;CJK COMPATIBILITY IDEOGRAPH-2F813;Lo;0;L;34B9;;;;N;;;;; +2F814;CJK COMPATIBILITY IDEOGRAPH-2F814;Lo;0;L;5167;;;;N;;;;; +2F815;CJK COMPATIBILITY IDEOGRAPH-2F815;Lo;0;L;518D;;;;N;;;;; +2F816;CJK COMPATIBILITY IDEOGRAPH-2F816;Lo;0;L;2054B;;;;N;;;;; +2F817;CJK COMPATIBILITY IDEOGRAPH-2F817;Lo;0;L;5197;;;;N;;;;; +2F818;CJK COMPATIBILITY IDEOGRAPH-2F818;Lo;0;L;51A4;;;;N;;;;; +2F819;CJK COMPATIBILITY IDEOGRAPH-2F819;Lo;0;L;4ECC;;;;N;;;;; +2F81A;CJK COMPATIBILITY IDEOGRAPH-2F81A;Lo;0;L;51AC;;;;N;;;;; +2F81B;CJK COMPATIBILITY IDEOGRAPH-2F81B;Lo;0;L;51B5;;;;N;;;;; +2F81C;CJK COMPATIBILITY IDEOGRAPH-2F81C;Lo;0;L;291DF;;;;N;;;;; +2F81D;CJK COMPATIBILITY IDEOGRAPH-2F81D;Lo;0;L;51F5;;;;N;;;;; +2F81E;CJK COMPATIBILITY IDEOGRAPH-2F81E;Lo;0;L;5203;;;;N;;;;; +2F81F;CJK COMPATIBILITY IDEOGRAPH-2F81F;Lo;0;L;34DF;;;;N;;;;; +2F820;CJK COMPATIBILITY IDEOGRAPH-2F820;Lo;0;L;523B;;;;N;;;;; +2F821;CJK COMPATIBILITY IDEOGRAPH-2F821;Lo;0;L;5246;;;;N;;;;; +2F822;CJK COMPATIBILITY IDEOGRAPH-2F822;Lo;0;L;5272;;;;N;;;;; +2F823;CJK COMPATIBILITY IDEOGRAPH-2F823;Lo;0;L;5277;;;;N;;;;; +2F824;CJK COMPATIBILITY IDEOGRAPH-2F824;Lo;0;L;3515;;;;N;;;;; +2F825;CJK COMPATIBILITY IDEOGRAPH-2F825;Lo;0;L;52C7;;;;N;;;;; +2F826;CJK COMPATIBILITY IDEOGRAPH-2F826;Lo;0;L;52C9;;;;N;;;;; +2F827;CJK COMPATIBILITY IDEOGRAPH-2F827;Lo;0;L;52E4;;;;N;;;;; +2F828;CJK COMPATIBILITY IDEOGRAPH-2F828;Lo;0;L;52FA;;;;N;;;;; +2F829;CJK COMPATIBILITY IDEOGRAPH-2F829;Lo;0;L;5305;;;;N;;;;; +2F82A;CJK COMPATIBILITY IDEOGRAPH-2F82A;Lo;0;L;5306;;;;N;;;;; +2F82B;CJK COMPATIBILITY IDEOGRAPH-2F82B;Lo;0;L;5317;;;;N;;;;; +2F82C;CJK COMPATIBILITY IDEOGRAPH-2F82C;Lo;0;L;5349;;;;N;;;;; +2F82D;CJK COMPATIBILITY IDEOGRAPH-2F82D;Lo;0;L;5351;;;;N;;;;; +2F82E;CJK COMPATIBILITY IDEOGRAPH-2F82E;Lo;0;L;535A;;;;N;;;;; +2F82F;CJK COMPATIBILITY IDEOGRAPH-2F82F;Lo;0;L;5373;;;;N;;;;; +2F830;CJK COMPATIBILITY IDEOGRAPH-2F830;Lo;0;L;537D;;;;N;;;;; +2F831;CJK COMPATIBILITY IDEOGRAPH-2F831;Lo;0;L;537F;;;;N;;;;; +2F832;CJK COMPATIBILITY IDEOGRAPH-2F832;Lo;0;L;537F;;;;N;;;;; +2F833;CJK COMPATIBILITY IDEOGRAPH-2F833;Lo;0;L;537F;;;;N;;;;; +2F834;CJK COMPATIBILITY IDEOGRAPH-2F834;Lo;0;L;20A2C;;;;N;;;;; +2F835;CJK COMPATIBILITY IDEOGRAPH-2F835;Lo;0;L;7070;;;;N;;;;; +2F836;CJK COMPATIBILITY IDEOGRAPH-2F836;Lo;0;L;53CA;;;;N;;;;; +2F837;CJK COMPATIBILITY IDEOGRAPH-2F837;Lo;0;L;53DF;;;;N;;;;; +2F838;CJK COMPATIBILITY IDEOGRAPH-2F838;Lo;0;L;20B63;;;;N;;;;; +2F839;CJK COMPATIBILITY IDEOGRAPH-2F839;Lo;0;L;53EB;;;;N;;;;; +2F83A;CJK COMPATIBILITY IDEOGRAPH-2F83A;Lo;0;L;53F1;;;;N;;;;; +2F83B;CJK COMPATIBILITY IDEOGRAPH-2F83B;Lo;0;L;5406;;;;N;;;;; +2F83C;CJK COMPATIBILITY IDEOGRAPH-2F83C;Lo;0;L;549E;;;;N;;;;; +2F83D;CJK COMPATIBILITY IDEOGRAPH-2F83D;Lo;0;L;5438;;;;N;;;;; +2F83E;CJK COMPATIBILITY IDEOGRAPH-2F83E;Lo;0;L;5448;;;;N;;;;; +2F83F;CJK COMPATIBILITY IDEOGRAPH-2F83F;Lo;0;L;5468;;;;N;;;;; +2F840;CJK COMPATIBILITY IDEOGRAPH-2F840;Lo;0;L;54A2;;;;N;;;;; +2F841;CJK COMPATIBILITY IDEOGRAPH-2F841;Lo;0;L;54F6;;;;N;;;;; +2F842;CJK COMPATIBILITY IDEOGRAPH-2F842;Lo;0;L;5510;;;;N;;;;; +2F843;CJK COMPATIBILITY IDEOGRAPH-2F843;Lo;0;L;5553;;;;N;;;;; +2F844;CJK COMPATIBILITY IDEOGRAPH-2F844;Lo;0;L;5563;;;;N;;;;; +2F845;CJK COMPATIBILITY IDEOGRAPH-2F845;Lo;0;L;5584;;;;N;;;;; +2F846;CJK COMPATIBILITY IDEOGRAPH-2F846;Lo;0;L;5584;;;;N;;;;; +2F847;CJK COMPATIBILITY IDEOGRAPH-2F847;Lo;0;L;5599;;;;N;;;;; +2F848;CJK COMPATIBILITY IDEOGRAPH-2F848;Lo;0;L;55AB;;;;N;;;;; +2F849;CJK COMPATIBILITY IDEOGRAPH-2F849;Lo;0;L;55B3;;;;N;;;;; +2F84A;CJK COMPATIBILITY IDEOGRAPH-2F84A;Lo;0;L;55C2;;;;N;;;;; +2F84B;CJK COMPATIBILITY IDEOGRAPH-2F84B;Lo;0;L;5716;;;;N;;;;; +2F84C;CJK COMPATIBILITY IDEOGRAPH-2F84C;Lo;0;L;5606;;;;N;;;;; +2F84D;CJK COMPATIBILITY IDEOGRAPH-2F84D;Lo;0;L;5717;;;;N;;;;; +2F84E;CJK COMPATIBILITY IDEOGRAPH-2F84E;Lo;0;L;5651;;;;N;;;;; +2F84F;CJK COMPATIBILITY IDEOGRAPH-2F84F;Lo;0;L;5674;;;;N;;;;; +2F850;CJK COMPATIBILITY IDEOGRAPH-2F850;Lo;0;L;5207;;;;N;;;;; +2F851;CJK COMPATIBILITY IDEOGRAPH-2F851;Lo;0;L;58EE;;;;N;;;;; +2F852;CJK COMPATIBILITY IDEOGRAPH-2F852;Lo;0;L;57CE;;;;N;;;;; +2F853;CJK COMPATIBILITY IDEOGRAPH-2F853;Lo;0;L;57F4;;;;N;;;;; +2F854;CJK COMPATIBILITY IDEOGRAPH-2F854;Lo;0;L;580D;;;;N;;;;; +2F855;CJK COMPATIBILITY IDEOGRAPH-2F855;Lo;0;L;578B;;;;N;;;;; +2F856;CJK COMPATIBILITY IDEOGRAPH-2F856;Lo;0;L;5832;;;;N;;;;; +2F857;CJK COMPATIBILITY IDEOGRAPH-2F857;Lo;0;L;5831;;;;N;;;;; +2F858;CJK COMPATIBILITY IDEOGRAPH-2F858;Lo;0;L;58AC;;;;N;;;;; +2F859;CJK COMPATIBILITY IDEOGRAPH-2F859;Lo;0;L;214E4;;;;N;;;;; +2F85A;CJK COMPATIBILITY IDEOGRAPH-2F85A;Lo;0;L;58F2;;;;N;;;;; +2F85B;CJK COMPATIBILITY IDEOGRAPH-2F85B;Lo;0;L;58F7;;;;N;;;;; +2F85C;CJK COMPATIBILITY IDEOGRAPH-2F85C;Lo;0;L;5906;;;;N;;;;; +2F85D;CJK COMPATIBILITY IDEOGRAPH-2F85D;Lo;0;L;591A;;;;N;;;;; +2F85E;CJK COMPATIBILITY IDEOGRAPH-2F85E;Lo;0;L;5922;;;;N;;;;; +2F85F;CJK COMPATIBILITY IDEOGRAPH-2F85F;Lo;0;L;5962;;;;N;;;;; +2F860;CJK COMPATIBILITY IDEOGRAPH-2F860;Lo;0;L;216A8;;;;N;;;;; +2F861;CJK COMPATIBILITY IDEOGRAPH-2F861;Lo;0;L;216EA;;;;N;;;;; +2F862;CJK COMPATIBILITY IDEOGRAPH-2F862;Lo;0;L;59EC;;;;N;;;;; +2F863;CJK COMPATIBILITY IDEOGRAPH-2F863;Lo;0;L;5A1B;;;;N;;;;; +2F864;CJK COMPATIBILITY IDEOGRAPH-2F864;Lo;0;L;5A27;;;;N;;;;; +2F865;CJK COMPATIBILITY IDEOGRAPH-2F865;Lo;0;L;59D8;;;;N;;;;; +2F866;CJK COMPATIBILITY IDEOGRAPH-2F866;Lo;0;L;5A66;;;;N;;;;; +2F867;CJK COMPATIBILITY IDEOGRAPH-2F867;Lo;0;L;36EE;;;;N;;;;; +2F868;CJK COMPATIBILITY IDEOGRAPH-2F868;Lo;0;L;2136A;;;;N;;;;; +2F869;CJK COMPATIBILITY IDEOGRAPH-2F869;Lo;0;L;5B08;;;;N;;;;; +2F86A;CJK COMPATIBILITY IDEOGRAPH-2F86A;Lo;0;L;5B3E;;;;N;;;;; +2F86B;CJK COMPATIBILITY IDEOGRAPH-2F86B;Lo;0;L;5B3E;;;;N;;;;; +2F86C;CJK COMPATIBILITY IDEOGRAPH-2F86C;Lo;0;L;219C8;;;;N;;;;; +2F86D;CJK COMPATIBILITY IDEOGRAPH-2F86D;Lo;0;L;5BC3;;;;N;;;;; +2F86E;CJK COMPATIBILITY IDEOGRAPH-2F86E;Lo;0;L;5BD8;;;;N;;;;; +2F86F;CJK COMPATIBILITY IDEOGRAPH-2F86F;Lo;0;L;5BE7;;;;N;;;;; +2F870;CJK COMPATIBILITY IDEOGRAPH-2F870;Lo;0;L;5BF3;;;;N;;;;; +2F871;CJK COMPATIBILITY IDEOGRAPH-2F871;Lo;0;L;21B18;;;;N;;;;; +2F872;CJK COMPATIBILITY IDEOGRAPH-2F872;Lo;0;L;5BFF;;;;N;;;;; +2F873;CJK COMPATIBILITY IDEOGRAPH-2F873;Lo;0;L;5C06;;;;N;;;;; +2F874;CJK COMPATIBILITY IDEOGRAPH-2F874;Lo;0;L;5F33;;;;N;;;;; +2F875;CJK COMPATIBILITY IDEOGRAPH-2F875;Lo;0;L;5C22;;;;N;;;;; +2F876;CJK COMPATIBILITY IDEOGRAPH-2F876;Lo;0;L;3781;;;;N;;;;; +2F877;CJK COMPATIBILITY IDEOGRAPH-2F877;Lo;0;L;5C60;;;;N;;;;; +2F878;CJK COMPATIBILITY IDEOGRAPH-2F878;Lo;0;L;5C6E;;;;N;;;;; +2F879;CJK COMPATIBILITY IDEOGRAPH-2F879;Lo;0;L;5CC0;;;;N;;;;; +2F87A;CJK COMPATIBILITY IDEOGRAPH-2F87A;Lo;0;L;5C8D;;;;N;;;;; +2F87B;CJK COMPATIBILITY IDEOGRAPH-2F87B;Lo;0;L;21DE4;;;;N;;;;; +2F87C;CJK COMPATIBILITY IDEOGRAPH-2F87C;Lo;0;L;5D43;;;;N;;;;; +2F87D;CJK COMPATIBILITY IDEOGRAPH-2F87D;Lo;0;L;21DE6;;;;N;;;;; +2F87E;CJK COMPATIBILITY IDEOGRAPH-2F87E;Lo;0;L;5D6E;;;;N;;;;; +2F87F;CJK COMPATIBILITY IDEOGRAPH-2F87F;Lo;0;L;5D6B;;;;N;;;;; +2F880;CJK COMPATIBILITY IDEOGRAPH-2F880;Lo;0;L;5D7C;;;;N;;;;; +2F881;CJK COMPATIBILITY IDEOGRAPH-2F881;Lo;0;L;5DE1;;;;N;;;;; +2F882;CJK COMPATIBILITY IDEOGRAPH-2F882;Lo;0;L;5DE2;;;;N;;;;; +2F883;CJK COMPATIBILITY IDEOGRAPH-2F883;Lo;0;L;382F;;;;N;;;;; +2F884;CJK COMPATIBILITY IDEOGRAPH-2F884;Lo;0;L;5DFD;;;;N;;;;; +2F885;CJK COMPATIBILITY IDEOGRAPH-2F885;Lo;0;L;5E28;;;;N;;;;; +2F886;CJK COMPATIBILITY IDEOGRAPH-2F886;Lo;0;L;5E3D;;;;N;;;;; +2F887;CJK COMPATIBILITY IDEOGRAPH-2F887;Lo;0;L;5E69;;;;N;;;;; +2F888;CJK COMPATIBILITY IDEOGRAPH-2F888;Lo;0;L;3862;;;;N;;;;; +2F889;CJK COMPATIBILITY IDEOGRAPH-2F889;Lo;0;L;22183;;;;N;;;;; +2F88A;CJK COMPATIBILITY IDEOGRAPH-2F88A;Lo;0;L;387C;;;;N;;;;; +2F88B;CJK COMPATIBILITY IDEOGRAPH-2F88B;Lo;0;L;5EB0;;;;N;;;;; +2F88C;CJK COMPATIBILITY IDEOGRAPH-2F88C;Lo;0;L;5EB3;;;;N;;;;; +2F88D;CJK COMPATIBILITY IDEOGRAPH-2F88D;Lo;0;L;5EB6;;;;N;;;;; +2F88E;CJK COMPATIBILITY IDEOGRAPH-2F88E;Lo;0;L;5ECA;;;;N;;;;; +2F88F;CJK COMPATIBILITY IDEOGRAPH-2F88F;Lo;0;L;2A392;;;;N;;;;; +2F890;CJK COMPATIBILITY IDEOGRAPH-2F890;Lo;0;L;5EFE;;;;N;;;;; +2F891;CJK COMPATIBILITY IDEOGRAPH-2F891;Lo;0;L;22331;;;;N;;;;; +2F892;CJK COMPATIBILITY IDEOGRAPH-2F892;Lo;0;L;22331;;;;N;;;;; +2F893;CJK COMPATIBILITY IDEOGRAPH-2F893;Lo;0;L;8201;;;;N;;;;; +2F894;CJK COMPATIBILITY IDEOGRAPH-2F894;Lo;0;L;5F22;;;;N;;;;; +2F895;CJK COMPATIBILITY IDEOGRAPH-2F895;Lo;0;L;5F22;;;;N;;;;; +2F896;CJK COMPATIBILITY IDEOGRAPH-2F896;Lo;0;L;38C7;;;;N;;;;; +2F897;CJK COMPATIBILITY IDEOGRAPH-2F897;Lo;0;L;232B8;;;;N;;;;; +2F898;CJK COMPATIBILITY IDEOGRAPH-2F898;Lo;0;L;261DA;;;;N;;;;; +2F899;CJK COMPATIBILITY IDEOGRAPH-2F899;Lo;0;L;5F62;;;;N;;;;; +2F89A;CJK COMPATIBILITY IDEOGRAPH-2F89A;Lo;0;L;5F6B;;;;N;;;;; +2F89B;CJK COMPATIBILITY IDEOGRAPH-2F89B;Lo;0;L;38E3;;;;N;;;;; +2F89C;CJK COMPATIBILITY IDEOGRAPH-2F89C;Lo;0;L;5F9A;;;;N;;;;; +2F89D;CJK COMPATIBILITY IDEOGRAPH-2F89D;Lo;0;L;5FCD;;;;N;;;;; +2F89E;CJK COMPATIBILITY IDEOGRAPH-2F89E;Lo;0;L;5FD7;;;;N;;;;; +2F89F;CJK COMPATIBILITY IDEOGRAPH-2F89F;Lo;0;L;5FF9;;;;N;;;;; +2F8A0;CJK COMPATIBILITY IDEOGRAPH-2F8A0;Lo;0;L;6081;;;;N;;;;; +2F8A1;CJK COMPATIBILITY IDEOGRAPH-2F8A1;Lo;0;L;393A;;;;N;;;;; +2F8A2;CJK COMPATIBILITY IDEOGRAPH-2F8A2;Lo;0;L;391C;;;;N;;;;; +2F8A3;CJK COMPATIBILITY IDEOGRAPH-2F8A3;Lo;0;L;6094;;;;N;;;;; +2F8A4;CJK COMPATIBILITY IDEOGRAPH-2F8A4;Lo;0;L;226D4;;;;N;;;;; +2F8A5;CJK COMPATIBILITY IDEOGRAPH-2F8A5;Lo;0;L;60C7;;;;N;;;;; +2F8A6;CJK COMPATIBILITY IDEOGRAPH-2F8A6;Lo;0;L;6148;;;;N;;;;; +2F8A7;CJK COMPATIBILITY IDEOGRAPH-2F8A7;Lo;0;L;614C;;;;N;;;;; +2F8A8;CJK COMPATIBILITY IDEOGRAPH-2F8A8;Lo;0;L;614E;;;;N;;;;; +2F8A9;CJK COMPATIBILITY IDEOGRAPH-2F8A9;Lo;0;L;614C;;;;N;;;;; +2F8AA;CJK COMPATIBILITY IDEOGRAPH-2F8AA;Lo;0;L;617A;;;;N;;;;; +2F8AB;CJK COMPATIBILITY IDEOGRAPH-2F8AB;Lo;0;L;618E;;;;N;;;;; +2F8AC;CJK COMPATIBILITY IDEOGRAPH-2F8AC;Lo;0;L;61B2;;;;N;;;;; +2F8AD;CJK COMPATIBILITY IDEOGRAPH-2F8AD;Lo;0;L;61A4;;;;N;;;;; +2F8AE;CJK COMPATIBILITY IDEOGRAPH-2F8AE;Lo;0;L;61AF;;;;N;;;;; +2F8AF;CJK COMPATIBILITY IDEOGRAPH-2F8AF;Lo;0;L;61DE;;;;N;;;;; +2F8B0;CJK COMPATIBILITY IDEOGRAPH-2F8B0;Lo;0;L;61F2;;;;N;;;;; +2F8B1;CJK COMPATIBILITY IDEOGRAPH-2F8B1;Lo;0;L;61F6;;;;N;;;;; +2F8B2;CJK COMPATIBILITY IDEOGRAPH-2F8B2;Lo;0;L;6210;;;;N;;;;; +2F8B3;CJK COMPATIBILITY IDEOGRAPH-2F8B3;Lo;0;L;621B;;;;N;;;;; +2F8B4;CJK COMPATIBILITY IDEOGRAPH-2F8B4;Lo;0;L;625D;;;;N;;;;; +2F8B5;CJK COMPATIBILITY IDEOGRAPH-2F8B5;Lo;0;L;62B1;;;;N;;;;; +2F8B6;CJK COMPATIBILITY IDEOGRAPH-2F8B6;Lo;0;L;62D4;;;;N;;;;; +2F8B7;CJK COMPATIBILITY IDEOGRAPH-2F8B7;Lo;0;L;6350;;;;N;;;;; +2F8B8;CJK COMPATIBILITY IDEOGRAPH-2F8B8;Lo;0;L;22B0C;;;;N;;;;; +2F8B9;CJK COMPATIBILITY IDEOGRAPH-2F8B9;Lo;0;L;633D;;;;N;;;;; +2F8BA;CJK COMPATIBILITY IDEOGRAPH-2F8BA;Lo;0;L;62FC;;;;N;;;;; +2F8BB;CJK COMPATIBILITY IDEOGRAPH-2F8BB;Lo;0;L;6368;;;;N;;;;; +2F8BC;CJK COMPATIBILITY IDEOGRAPH-2F8BC;Lo;0;L;6383;;;;N;;;;; +2F8BD;CJK COMPATIBILITY IDEOGRAPH-2F8BD;Lo;0;L;63E4;;;;N;;;;; +2F8BE;CJK COMPATIBILITY IDEOGRAPH-2F8BE;Lo;0;L;22BF1;;;;N;;;;; +2F8BF;CJK COMPATIBILITY IDEOGRAPH-2F8BF;Lo;0;L;6422;;;;N;;;;; +2F8C0;CJK COMPATIBILITY IDEOGRAPH-2F8C0;Lo;0;L;63C5;;;;N;;;;; +2F8C1;CJK COMPATIBILITY IDEOGRAPH-2F8C1;Lo;0;L;63A9;;;;N;;;;; +2F8C2;CJK COMPATIBILITY IDEOGRAPH-2F8C2;Lo;0;L;3A2E;;;;N;;;;; +2F8C3;CJK COMPATIBILITY IDEOGRAPH-2F8C3;Lo;0;L;6469;;;;N;;;;; +2F8C4;CJK COMPATIBILITY IDEOGRAPH-2F8C4;Lo;0;L;647E;;;;N;;;;; +2F8C5;CJK COMPATIBILITY IDEOGRAPH-2F8C5;Lo;0;L;649D;;;;N;;;;; +2F8C6;CJK COMPATIBILITY IDEOGRAPH-2F8C6;Lo;0;L;6477;;;;N;;;;; +2F8C7;CJK COMPATIBILITY IDEOGRAPH-2F8C7;Lo;0;L;3A6C;;;;N;;;;; +2F8C8;CJK COMPATIBILITY IDEOGRAPH-2F8C8;Lo;0;L;654F;;;;N;;;;; +2F8C9;CJK COMPATIBILITY IDEOGRAPH-2F8C9;Lo;0;L;656C;;;;N;;;;; +2F8CA;CJK COMPATIBILITY IDEOGRAPH-2F8CA;Lo;0;L;2300A;;;;N;;;;; +2F8CB;CJK COMPATIBILITY IDEOGRAPH-2F8CB;Lo;0;L;65E3;;;;N;;;;; +2F8CC;CJK COMPATIBILITY IDEOGRAPH-2F8CC;Lo;0;L;66F8;;;;N;;;;; +2F8CD;CJK COMPATIBILITY IDEOGRAPH-2F8CD;Lo;0;L;6649;;;;N;;;;; +2F8CE;CJK COMPATIBILITY IDEOGRAPH-2F8CE;Lo;0;L;3B19;;;;N;;;;; +2F8CF;CJK COMPATIBILITY IDEOGRAPH-2F8CF;Lo;0;L;6691;;;;N;;;;; +2F8D0;CJK COMPATIBILITY IDEOGRAPH-2F8D0;Lo;0;L;3B08;;;;N;;;;; +2F8D1;CJK COMPATIBILITY IDEOGRAPH-2F8D1;Lo;0;L;3AE4;;;;N;;;;; +2F8D2;CJK COMPATIBILITY IDEOGRAPH-2F8D2;Lo;0;L;5192;;;;N;;;;; +2F8D3;CJK COMPATIBILITY IDEOGRAPH-2F8D3;Lo;0;L;5195;;;;N;;;;; +2F8D4;CJK COMPATIBILITY IDEOGRAPH-2F8D4;Lo;0;L;6700;;;;N;;;;; +2F8D5;CJK COMPATIBILITY IDEOGRAPH-2F8D5;Lo;0;L;669C;;;;N;;;;; +2F8D6;CJK COMPATIBILITY IDEOGRAPH-2F8D6;Lo;0;L;80AD;;;;N;;;;; +2F8D7;CJK COMPATIBILITY IDEOGRAPH-2F8D7;Lo;0;L;43D9;;;;N;;;;; +2F8D8;CJK COMPATIBILITY IDEOGRAPH-2F8D8;Lo;0;L;6717;;;;N;;;;; +2F8D9;CJK COMPATIBILITY IDEOGRAPH-2F8D9;Lo;0;L;671B;;;;N;;;;; +2F8DA;CJK COMPATIBILITY IDEOGRAPH-2F8DA;Lo;0;L;6721;;;;N;;;;; +2F8DB;CJK COMPATIBILITY IDEOGRAPH-2F8DB;Lo;0;L;675E;;;;N;;;;; +2F8DC;CJK COMPATIBILITY IDEOGRAPH-2F8DC;Lo;0;L;6753;;;;N;;;;; +2F8DD;CJK COMPATIBILITY IDEOGRAPH-2F8DD;Lo;0;L;233C3;;;;N;;;;; +2F8DE;CJK COMPATIBILITY IDEOGRAPH-2F8DE;Lo;0;L;3B49;;;;N;;;;; +2F8DF;CJK COMPATIBILITY IDEOGRAPH-2F8DF;Lo;0;L;67FA;;;;N;;;;; +2F8E0;CJK COMPATIBILITY IDEOGRAPH-2F8E0;Lo;0;L;6785;;;;N;;;;; +2F8E1;CJK COMPATIBILITY IDEOGRAPH-2F8E1;Lo;0;L;6852;;;;N;;;;; +2F8E2;CJK COMPATIBILITY IDEOGRAPH-2F8E2;Lo;0;L;6885;;;;N;;;;; +2F8E3;CJK COMPATIBILITY IDEOGRAPH-2F8E3;Lo;0;L;2346D;;;;N;;;;; +2F8E4;CJK COMPATIBILITY IDEOGRAPH-2F8E4;Lo;0;L;688E;;;;N;;;;; +2F8E5;CJK COMPATIBILITY IDEOGRAPH-2F8E5;Lo;0;L;681F;;;;N;;;;; +2F8E6;CJK COMPATIBILITY IDEOGRAPH-2F8E6;Lo;0;L;6914;;;;N;;;;; +2F8E7;CJK COMPATIBILITY IDEOGRAPH-2F8E7;Lo;0;L;3B9D;;;;N;;;;; +2F8E8;CJK COMPATIBILITY IDEOGRAPH-2F8E8;Lo;0;L;6942;;;;N;;;;; +2F8E9;CJK COMPATIBILITY IDEOGRAPH-2F8E9;Lo;0;L;69A3;;;;N;;;;; +2F8EA;CJK COMPATIBILITY IDEOGRAPH-2F8EA;Lo;0;L;69EA;;;;N;;;;; +2F8EB;CJK COMPATIBILITY IDEOGRAPH-2F8EB;Lo;0;L;6AA8;;;;N;;;;; +2F8EC;CJK COMPATIBILITY IDEOGRAPH-2F8EC;Lo;0;L;236A3;;;;N;;;;; +2F8ED;CJK COMPATIBILITY IDEOGRAPH-2F8ED;Lo;0;L;6ADB;;;;N;;;;; +2F8EE;CJK COMPATIBILITY IDEOGRAPH-2F8EE;Lo;0;L;3C18;;;;N;;;;; +2F8EF;CJK COMPATIBILITY IDEOGRAPH-2F8EF;Lo;0;L;6B21;;;;N;;;;; +2F8F0;CJK COMPATIBILITY IDEOGRAPH-2F8F0;Lo;0;L;238A7;;;;N;;;;; +2F8F1;CJK COMPATIBILITY IDEOGRAPH-2F8F1;Lo;0;L;6B54;;;;N;;;;; +2F8F2;CJK COMPATIBILITY IDEOGRAPH-2F8F2;Lo;0;L;3C4E;;;;N;;;;; +2F8F3;CJK COMPATIBILITY IDEOGRAPH-2F8F3;Lo;0;L;6B72;;;;N;;;;; +2F8F4;CJK COMPATIBILITY IDEOGRAPH-2F8F4;Lo;0;L;6B9F;;;;N;;;;; +2F8F5;CJK COMPATIBILITY IDEOGRAPH-2F8F5;Lo;0;L;6BBA;;;;N;;;;; +2F8F6;CJK COMPATIBILITY IDEOGRAPH-2F8F6;Lo;0;L;6BBB;;;;N;;;;; +2F8F7;CJK COMPATIBILITY IDEOGRAPH-2F8F7;Lo;0;L;23A8D;;;;N;;;;; +2F8F8;CJK COMPATIBILITY IDEOGRAPH-2F8F8;Lo;0;L;21D0B;;;;N;;;;; +2F8F9;CJK COMPATIBILITY IDEOGRAPH-2F8F9;Lo;0;L;23AFA;;;;N;;;;; +2F8FA;CJK COMPATIBILITY IDEOGRAPH-2F8FA;Lo;0;L;6C4E;;;;N;;;;; +2F8FB;CJK COMPATIBILITY IDEOGRAPH-2F8FB;Lo;0;L;23CBC;;;;N;;;;; +2F8FC;CJK COMPATIBILITY IDEOGRAPH-2F8FC;Lo;0;L;6CBF;;;;N;;;;; +2F8FD;CJK COMPATIBILITY IDEOGRAPH-2F8FD;Lo;0;L;6CCD;;;;N;;;;; +2F8FE;CJK COMPATIBILITY IDEOGRAPH-2F8FE;Lo;0;L;6C67;;;;N;;;;; +2F8FF;CJK COMPATIBILITY IDEOGRAPH-2F8FF;Lo;0;L;6D16;;;;N;;;;; +2F900;CJK COMPATIBILITY IDEOGRAPH-2F900;Lo;0;L;6D3E;;;;N;;;;; +2F901;CJK COMPATIBILITY IDEOGRAPH-2F901;Lo;0;L;6D77;;;;N;;;;; +2F902;CJK COMPATIBILITY IDEOGRAPH-2F902;Lo;0;L;6D41;;;;N;;;;; +2F903;CJK COMPATIBILITY IDEOGRAPH-2F903;Lo;0;L;6D69;;;;N;;;;; +2F904;CJK COMPATIBILITY IDEOGRAPH-2F904;Lo;0;L;6D78;;;;N;;;;; +2F905;CJK COMPATIBILITY IDEOGRAPH-2F905;Lo;0;L;6D85;;;;N;;;;; +2F906;CJK COMPATIBILITY IDEOGRAPH-2F906;Lo;0;L;23D1E;;;;N;;;;; +2F907;CJK COMPATIBILITY IDEOGRAPH-2F907;Lo;0;L;6D34;;;;N;;;;; +2F908;CJK COMPATIBILITY IDEOGRAPH-2F908;Lo;0;L;6E2F;;;;N;;;;; +2F909;CJK COMPATIBILITY IDEOGRAPH-2F909;Lo;0;L;6E6E;;;;N;;;;; +2F90A;CJK COMPATIBILITY IDEOGRAPH-2F90A;Lo;0;L;3D33;;;;N;;;;; +2F90B;CJK COMPATIBILITY IDEOGRAPH-2F90B;Lo;0;L;6ECB;;;;N;;;;; +2F90C;CJK COMPATIBILITY IDEOGRAPH-2F90C;Lo;0;L;6EC7;;;;N;;;;; +2F90D;CJK COMPATIBILITY IDEOGRAPH-2F90D;Lo;0;L;23ED1;;;;N;;;;; +2F90E;CJK COMPATIBILITY IDEOGRAPH-2F90E;Lo;0;L;6DF9;;;;N;;;;; +2F90F;CJK COMPATIBILITY IDEOGRAPH-2F90F;Lo;0;L;6F6E;;;;N;;;;; +2F910;CJK COMPATIBILITY IDEOGRAPH-2F910;Lo;0;L;23F5E;;;;N;;;;; +2F911;CJK COMPATIBILITY IDEOGRAPH-2F911;Lo;0;L;23F8E;;;;N;;;;; +2F912;CJK COMPATIBILITY IDEOGRAPH-2F912;Lo;0;L;6FC6;;;;N;;;;; +2F913;CJK COMPATIBILITY IDEOGRAPH-2F913;Lo;0;L;7039;;;;N;;;;; +2F914;CJK COMPATIBILITY IDEOGRAPH-2F914;Lo;0;L;701E;;;;N;;;;; +2F915;CJK COMPATIBILITY IDEOGRAPH-2F915;Lo;0;L;701B;;;;N;;;;; +2F916;CJK COMPATIBILITY IDEOGRAPH-2F916;Lo;0;L;3D96;;;;N;;;;; +2F917;CJK COMPATIBILITY IDEOGRAPH-2F917;Lo;0;L;704A;;;;N;;;;; +2F918;CJK COMPATIBILITY IDEOGRAPH-2F918;Lo;0;L;707D;;;;N;;;;; +2F919;CJK COMPATIBILITY IDEOGRAPH-2F919;Lo;0;L;7077;;;;N;;;;; +2F91A;CJK COMPATIBILITY IDEOGRAPH-2F91A;Lo;0;L;70AD;;;;N;;;;; +2F91B;CJK COMPATIBILITY IDEOGRAPH-2F91B;Lo;0;L;20525;;;;N;;;;; +2F91C;CJK COMPATIBILITY IDEOGRAPH-2F91C;Lo;0;L;7145;;;;N;;;;; +2F91D;CJK COMPATIBILITY IDEOGRAPH-2F91D;Lo;0;L;24263;;;;N;;;;; +2F91E;CJK COMPATIBILITY IDEOGRAPH-2F91E;Lo;0;L;719C;;;;N;;;;; +2F91F;CJK COMPATIBILITY IDEOGRAPH-2F91F;Lo;0;L;43AB;;;;N;;;;; +2F920;CJK COMPATIBILITY IDEOGRAPH-2F920;Lo;0;L;7228;;;;N;;;;; +2F921;CJK COMPATIBILITY IDEOGRAPH-2F921;Lo;0;L;7235;;;;N;;;;; +2F922;CJK COMPATIBILITY IDEOGRAPH-2F922;Lo;0;L;7250;;;;N;;;;; +2F923;CJK COMPATIBILITY IDEOGRAPH-2F923;Lo;0;L;24608;;;;N;;;;; +2F924;CJK COMPATIBILITY IDEOGRAPH-2F924;Lo;0;L;7280;;;;N;;;;; +2F925;CJK COMPATIBILITY IDEOGRAPH-2F925;Lo;0;L;7295;;;;N;;;;; +2F926;CJK COMPATIBILITY IDEOGRAPH-2F926;Lo;0;L;24735;;;;N;;;;; +2F927;CJK COMPATIBILITY IDEOGRAPH-2F927;Lo;0;L;24814;;;;N;;;;; +2F928;CJK COMPATIBILITY IDEOGRAPH-2F928;Lo;0;L;737A;;;;N;;;;; +2F929;CJK COMPATIBILITY IDEOGRAPH-2F929;Lo;0;L;738B;;;;N;;;;; +2F92A;CJK COMPATIBILITY IDEOGRAPH-2F92A;Lo;0;L;3EAC;;;;N;;;;; +2F92B;CJK COMPATIBILITY IDEOGRAPH-2F92B;Lo;0;L;73A5;;;;N;;;;; +2F92C;CJK COMPATIBILITY IDEOGRAPH-2F92C;Lo;0;L;3EB8;;;;N;;;;; +2F92D;CJK COMPATIBILITY IDEOGRAPH-2F92D;Lo;0;L;3EB8;;;;N;;;;; +2F92E;CJK COMPATIBILITY IDEOGRAPH-2F92E;Lo;0;L;7447;;;;N;;;;; +2F92F;CJK COMPATIBILITY IDEOGRAPH-2F92F;Lo;0;L;745C;;;;N;;;;; +2F930;CJK COMPATIBILITY IDEOGRAPH-2F930;Lo;0;L;7471;;;;N;;;;; +2F931;CJK COMPATIBILITY IDEOGRAPH-2F931;Lo;0;L;7485;;;;N;;;;; +2F932;CJK COMPATIBILITY IDEOGRAPH-2F932;Lo;0;L;74CA;;;;N;;;;; +2F933;CJK COMPATIBILITY IDEOGRAPH-2F933;Lo;0;L;3F1B;;;;N;;;;; +2F934;CJK COMPATIBILITY IDEOGRAPH-2F934;Lo;0;L;7524;;;;N;;;;; +2F935;CJK COMPATIBILITY IDEOGRAPH-2F935;Lo;0;L;24C36;;;;N;;;;; +2F936;CJK COMPATIBILITY IDEOGRAPH-2F936;Lo;0;L;753E;;;;N;;;;; +2F937;CJK COMPATIBILITY IDEOGRAPH-2F937;Lo;0;L;24C92;;;;N;;;;; +2F938;CJK COMPATIBILITY IDEOGRAPH-2F938;Lo;0;L;7570;;;;N;;;;; +2F939;CJK COMPATIBILITY IDEOGRAPH-2F939;Lo;0;L;2219F;;;;N;;;;; +2F93A;CJK COMPATIBILITY IDEOGRAPH-2F93A;Lo;0;L;7610;;;;N;;;;; +2F93B;CJK COMPATIBILITY IDEOGRAPH-2F93B;Lo;0;L;24FA1;;;;N;;;;; +2F93C;CJK COMPATIBILITY IDEOGRAPH-2F93C;Lo;0;L;24FB8;;;;N;;;;; +2F93D;CJK COMPATIBILITY IDEOGRAPH-2F93D;Lo;0;L;25044;;;;N;;;;; +2F93E;CJK COMPATIBILITY IDEOGRAPH-2F93E;Lo;0;L;3FFC;;;;N;;;;; +2F93F;CJK COMPATIBILITY IDEOGRAPH-2F93F;Lo;0;L;4008;;;;N;;;;; +2F940;CJK COMPATIBILITY IDEOGRAPH-2F940;Lo;0;L;76F4;;;;N;;;;; +2F941;CJK COMPATIBILITY IDEOGRAPH-2F941;Lo;0;L;250F3;;;;N;;;;; +2F942;CJK COMPATIBILITY IDEOGRAPH-2F942;Lo;0;L;250F2;;;;N;;;;; +2F943;CJK COMPATIBILITY IDEOGRAPH-2F943;Lo;0;L;25119;;;;N;;;;; +2F944;CJK COMPATIBILITY IDEOGRAPH-2F944;Lo;0;L;25133;;;;N;;;;; +2F945;CJK COMPATIBILITY IDEOGRAPH-2F945;Lo;0;L;771E;;;;N;;;;; +2F946;CJK COMPATIBILITY IDEOGRAPH-2F946;Lo;0;L;771F;;;;N;;;;; +2F947;CJK COMPATIBILITY IDEOGRAPH-2F947;Lo;0;L;771F;;;;N;;;;; +2F948;CJK COMPATIBILITY IDEOGRAPH-2F948;Lo;0;L;774A;;;;N;;;;; +2F949;CJK COMPATIBILITY IDEOGRAPH-2F949;Lo;0;L;4039;;;;N;;;;; +2F94A;CJK COMPATIBILITY IDEOGRAPH-2F94A;Lo;0;L;778B;;;;N;;;;; +2F94B;CJK COMPATIBILITY IDEOGRAPH-2F94B;Lo;0;L;4046;;;;N;;;;; +2F94C;CJK COMPATIBILITY IDEOGRAPH-2F94C;Lo;0;L;4096;;;;N;;;;; +2F94D;CJK COMPATIBILITY IDEOGRAPH-2F94D;Lo;0;L;2541D;;;;N;;;;; +2F94E;CJK COMPATIBILITY IDEOGRAPH-2F94E;Lo;0;L;784E;;;;N;;;;; +2F94F;CJK COMPATIBILITY IDEOGRAPH-2F94F;Lo;0;L;788C;;;;N;;;;; +2F950;CJK COMPATIBILITY IDEOGRAPH-2F950;Lo;0;L;78CC;;;;N;;;;; +2F951;CJK COMPATIBILITY IDEOGRAPH-2F951;Lo;0;L;40E3;;;;N;;;;; +2F952;CJK COMPATIBILITY IDEOGRAPH-2F952;Lo;0;L;25626;;;;N;;;;; +2F953;CJK COMPATIBILITY IDEOGRAPH-2F953;Lo;0;L;7956;;;;N;;;;; +2F954;CJK COMPATIBILITY IDEOGRAPH-2F954;Lo;0;L;2569A;;;;N;;;;; +2F955;CJK COMPATIBILITY IDEOGRAPH-2F955;Lo;0;L;256C5;;;;N;;;;; +2F956;CJK COMPATIBILITY IDEOGRAPH-2F956;Lo;0;L;798F;;;;N;;;;; +2F957;CJK COMPATIBILITY IDEOGRAPH-2F957;Lo;0;L;79EB;;;;N;;;;; +2F958;CJK COMPATIBILITY IDEOGRAPH-2F958;Lo;0;L;412F;;;;N;;;;; +2F959;CJK COMPATIBILITY IDEOGRAPH-2F959;Lo;0;L;7A40;;;;N;;;;; +2F95A;CJK COMPATIBILITY IDEOGRAPH-2F95A;Lo;0;L;7A4A;;;;N;;;;; +2F95B;CJK COMPATIBILITY IDEOGRAPH-2F95B;Lo;0;L;7A4F;;;;N;;;;; +2F95C;CJK COMPATIBILITY IDEOGRAPH-2F95C;Lo;0;L;2597C;;;;N;;;;; +2F95D;CJK COMPATIBILITY IDEOGRAPH-2F95D;Lo;0;L;25AA7;;;;N;;;;; +2F95E;CJK COMPATIBILITY IDEOGRAPH-2F95E;Lo;0;L;25AA7;;;;N;;;;; +2F95F;CJK COMPATIBILITY IDEOGRAPH-2F95F;Lo;0;L;7AAE;;;;N;;;;; +2F960;CJK COMPATIBILITY IDEOGRAPH-2F960;Lo;0;L;4202;;;;N;;;;; +2F961;CJK COMPATIBILITY IDEOGRAPH-2F961;Lo;0;L;25BAB;;;;N;;;;; +2F962;CJK COMPATIBILITY IDEOGRAPH-2F962;Lo;0;L;7BC6;;;;N;;;;; +2F963;CJK COMPATIBILITY IDEOGRAPH-2F963;Lo;0;L;7BC9;;;;N;;;;; +2F964;CJK COMPATIBILITY IDEOGRAPH-2F964;Lo;0;L;4227;;;;N;;;;; +2F965;CJK COMPATIBILITY IDEOGRAPH-2F965;Lo;0;L;25C80;;;;N;;;;; +2F966;CJK COMPATIBILITY IDEOGRAPH-2F966;Lo;0;L;7CD2;;;;N;;;;; +2F967;CJK COMPATIBILITY IDEOGRAPH-2F967;Lo;0;L;42A0;;;;N;;;;; +2F968;CJK COMPATIBILITY IDEOGRAPH-2F968;Lo;0;L;7CE8;;;;N;;;;; +2F969;CJK COMPATIBILITY IDEOGRAPH-2F969;Lo;0;L;7CE3;;;;N;;;;; +2F96A;CJK COMPATIBILITY IDEOGRAPH-2F96A;Lo;0;L;7D00;;;;N;;;;; +2F96B;CJK COMPATIBILITY IDEOGRAPH-2F96B;Lo;0;L;25F86;;;;N;;;;; +2F96C;CJK COMPATIBILITY IDEOGRAPH-2F96C;Lo;0;L;7D63;;;;N;;;;; +2F96D;CJK COMPATIBILITY IDEOGRAPH-2F96D;Lo;0;L;4301;;;;N;;;;; +2F96E;CJK COMPATIBILITY IDEOGRAPH-2F96E;Lo;0;L;7DC7;;;;N;;;;; +2F96F;CJK COMPATIBILITY IDEOGRAPH-2F96F;Lo;0;L;7E02;;;;N;;;;; +2F970;CJK COMPATIBILITY IDEOGRAPH-2F970;Lo;0;L;7E45;;;;N;;;;; +2F971;CJK COMPATIBILITY IDEOGRAPH-2F971;Lo;0;L;4334;;;;N;;;;; +2F972;CJK COMPATIBILITY IDEOGRAPH-2F972;Lo;0;L;26228;;;;N;;;;; +2F973;CJK COMPATIBILITY IDEOGRAPH-2F973;Lo;0;L;26247;;;;N;;;;; +2F974;CJK COMPATIBILITY IDEOGRAPH-2F974;Lo;0;L;4359;;;;N;;;;; +2F975;CJK COMPATIBILITY IDEOGRAPH-2F975;Lo;0;L;262D9;;;;N;;;;; +2F976;CJK COMPATIBILITY IDEOGRAPH-2F976;Lo;0;L;7F7A;;;;N;;;;; +2F977;CJK COMPATIBILITY IDEOGRAPH-2F977;Lo;0;L;2633E;;;;N;;;;; +2F978;CJK COMPATIBILITY IDEOGRAPH-2F978;Lo;0;L;7F95;;;;N;;;;; +2F979;CJK COMPATIBILITY IDEOGRAPH-2F979;Lo;0;L;7FFA;;;;N;;;;; +2F97A;CJK COMPATIBILITY IDEOGRAPH-2F97A;Lo;0;L;8005;;;;N;;;;; +2F97B;CJK COMPATIBILITY IDEOGRAPH-2F97B;Lo;0;L;264DA;;;;N;;;;; +2F97C;CJK COMPATIBILITY IDEOGRAPH-2F97C;Lo;0;L;26523;;;;N;;;;; +2F97D;CJK COMPATIBILITY IDEOGRAPH-2F97D;Lo;0;L;8060;;;;N;;;;; +2F97E;CJK COMPATIBILITY IDEOGRAPH-2F97E;Lo;0;L;265A8;;;;N;;;;; +2F97F;CJK COMPATIBILITY IDEOGRAPH-2F97F;Lo;0;L;8070;;;;N;;;;; +2F980;CJK COMPATIBILITY IDEOGRAPH-2F980;Lo;0;L;2335F;;;;N;;;;; +2F981;CJK COMPATIBILITY IDEOGRAPH-2F981;Lo;0;L;43D5;;;;N;;;;; +2F982;CJK COMPATIBILITY IDEOGRAPH-2F982;Lo;0;L;80B2;;;;N;;;;; +2F983;CJK COMPATIBILITY IDEOGRAPH-2F983;Lo;0;L;8103;;;;N;;;;; +2F984;CJK COMPATIBILITY IDEOGRAPH-2F984;Lo;0;L;440B;;;;N;;;;; +2F985;CJK COMPATIBILITY IDEOGRAPH-2F985;Lo;0;L;813E;;;;N;;;;; +2F986;CJK COMPATIBILITY IDEOGRAPH-2F986;Lo;0;L;5AB5;;;;N;;;;; +2F987;CJK COMPATIBILITY IDEOGRAPH-2F987;Lo;0;L;267A7;;;;N;;;;; +2F988;CJK COMPATIBILITY IDEOGRAPH-2F988;Lo;0;L;267B5;;;;N;;;;; +2F989;CJK COMPATIBILITY IDEOGRAPH-2F989;Lo;0;L;23393;;;;N;;;;; +2F98A;CJK COMPATIBILITY IDEOGRAPH-2F98A;Lo;0;L;2339C;;;;N;;;;; +2F98B;CJK COMPATIBILITY IDEOGRAPH-2F98B;Lo;0;L;8201;;;;N;;;;; +2F98C;CJK COMPATIBILITY IDEOGRAPH-2F98C;Lo;0;L;8204;;;;N;;;;; +2F98D;CJK COMPATIBILITY IDEOGRAPH-2F98D;Lo;0;L;8F9E;;;;N;;;;; +2F98E;CJK COMPATIBILITY IDEOGRAPH-2F98E;Lo;0;L;446B;;;;N;;;;; +2F98F;CJK COMPATIBILITY IDEOGRAPH-2F98F;Lo;0;L;8291;;;;N;;;;; +2F990;CJK COMPATIBILITY IDEOGRAPH-2F990;Lo;0;L;828B;;;;N;;;;; +2F991;CJK COMPATIBILITY IDEOGRAPH-2F991;Lo;0;L;829D;;;;N;;;;; +2F992;CJK COMPATIBILITY IDEOGRAPH-2F992;Lo;0;L;52B3;;;;N;;;;; +2F993;CJK COMPATIBILITY IDEOGRAPH-2F993;Lo;0;L;82B1;;;;N;;;;; +2F994;CJK COMPATIBILITY IDEOGRAPH-2F994;Lo;0;L;82B3;;;;N;;;;; +2F995;CJK COMPATIBILITY IDEOGRAPH-2F995;Lo;0;L;82BD;;;;N;;;;; +2F996;CJK COMPATIBILITY IDEOGRAPH-2F996;Lo;0;L;82E6;;;;N;;;;; +2F997;CJK COMPATIBILITY IDEOGRAPH-2F997;Lo;0;L;26B3C;;;;N;;;;; +2F998;CJK COMPATIBILITY IDEOGRAPH-2F998;Lo;0;L;82E5;;;;N;;;;; +2F999;CJK COMPATIBILITY IDEOGRAPH-2F999;Lo;0;L;831D;;;;N;;;;; +2F99A;CJK COMPATIBILITY IDEOGRAPH-2F99A;Lo;0;L;8363;;;;N;;;;; +2F99B;CJK COMPATIBILITY IDEOGRAPH-2F99B;Lo;0;L;83AD;;;;N;;;;; +2F99C;CJK COMPATIBILITY IDEOGRAPH-2F99C;Lo;0;L;8323;;;;N;;;;; +2F99D;CJK COMPATIBILITY IDEOGRAPH-2F99D;Lo;0;L;83BD;;;;N;;;;; +2F99E;CJK COMPATIBILITY IDEOGRAPH-2F99E;Lo;0;L;83E7;;;;N;;;;; +2F99F;CJK COMPATIBILITY IDEOGRAPH-2F99F;Lo;0;L;8457;;;;N;;;;; +2F9A0;CJK COMPATIBILITY IDEOGRAPH-2F9A0;Lo;0;L;8353;;;;N;;;;; +2F9A1;CJK COMPATIBILITY IDEOGRAPH-2F9A1;Lo;0;L;83CA;;;;N;;;;; +2F9A2;CJK COMPATIBILITY IDEOGRAPH-2F9A2;Lo;0;L;83CC;;;;N;;;;; +2F9A3;CJK COMPATIBILITY IDEOGRAPH-2F9A3;Lo;0;L;83DC;;;;N;;;;; +2F9A4;CJK COMPATIBILITY IDEOGRAPH-2F9A4;Lo;0;L;26C36;;;;N;;;;; +2F9A5;CJK COMPATIBILITY IDEOGRAPH-2F9A5;Lo;0;L;26D6B;;;;N;;;;; +2F9A6;CJK COMPATIBILITY IDEOGRAPH-2F9A6;Lo;0;L;26CD5;;;;N;;;;; +2F9A7;CJK COMPATIBILITY IDEOGRAPH-2F9A7;Lo;0;L;452B;;;;N;;;;; +2F9A8;CJK COMPATIBILITY IDEOGRAPH-2F9A8;Lo;0;L;84F1;;;;N;;;;; +2F9A9;CJK COMPATIBILITY IDEOGRAPH-2F9A9;Lo;0;L;84F3;;;;N;;;;; +2F9AA;CJK COMPATIBILITY IDEOGRAPH-2F9AA;Lo;0;L;8516;;;;N;;;;; +2F9AB;CJK COMPATIBILITY IDEOGRAPH-2F9AB;Lo;0;L;273CA;;;;N;;;;; +2F9AC;CJK COMPATIBILITY IDEOGRAPH-2F9AC;Lo;0;L;8564;;;;N;;;;; +2F9AD;CJK COMPATIBILITY IDEOGRAPH-2F9AD;Lo;0;L;26F2C;;;;N;;;;; +2F9AE;CJK COMPATIBILITY IDEOGRAPH-2F9AE;Lo;0;L;455D;;;;N;;;;; +2F9AF;CJK COMPATIBILITY IDEOGRAPH-2F9AF;Lo;0;L;4561;;;;N;;;;; +2F9B0;CJK COMPATIBILITY IDEOGRAPH-2F9B0;Lo;0;L;26FB1;;;;N;;;;; +2F9B1;CJK COMPATIBILITY IDEOGRAPH-2F9B1;Lo;0;L;270D2;;;;N;;;;; +2F9B2;CJK COMPATIBILITY IDEOGRAPH-2F9B2;Lo;0;L;456B;;;;N;;;;; +2F9B3;CJK COMPATIBILITY IDEOGRAPH-2F9B3;Lo;0;L;8650;;;;N;;;;; +2F9B4;CJK COMPATIBILITY IDEOGRAPH-2F9B4;Lo;0;L;865C;;;;N;;;;; +2F9B5;CJK COMPATIBILITY IDEOGRAPH-2F9B5;Lo;0;L;8667;;;;N;;;;; +2F9B6;CJK COMPATIBILITY IDEOGRAPH-2F9B6;Lo;0;L;8669;;;;N;;;;; +2F9B7;CJK COMPATIBILITY IDEOGRAPH-2F9B7;Lo;0;L;86A9;;;;N;;;;; +2F9B8;CJK COMPATIBILITY IDEOGRAPH-2F9B8;Lo;0;L;8688;;;;N;;;;; +2F9B9;CJK COMPATIBILITY IDEOGRAPH-2F9B9;Lo;0;L;870E;;;;N;;;;; +2F9BA;CJK COMPATIBILITY IDEOGRAPH-2F9BA;Lo;0;L;86E2;;;;N;;;;; +2F9BB;CJK COMPATIBILITY IDEOGRAPH-2F9BB;Lo;0;L;8779;;;;N;;;;; +2F9BC;CJK COMPATIBILITY IDEOGRAPH-2F9BC;Lo;0;L;8728;;;;N;;;;; +2F9BD;CJK COMPATIBILITY IDEOGRAPH-2F9BD;Lo;0;L;876B;;;;N;;;;; +2F9BE;CJK COMPATIBILITY IDEOGRAPH-2F9BE;Lo;0;L;8786;;;;N;;;;; +2F9BF;CJK COMPATIBILITY IDEOGRAPH-2F9BF;Lo;0;L;4D57;;;;N;;;;; +2F9C0;CJK COMPATIBILITY IDEOGRAPH-2F9C0;Lo;0;L;87E1;;;;N;;;;; +2F9C1;CJK COMPATIBILITY IDEOGRAPH-2F9C1;Lo;0;L;8801;;;;N;;;;; +2F9C2;CJK COMPATIBILITY IDEOGRAPH-2F9C2;Lo;0;L;45F9;;;;N;;;;; +2F9C3;CJK COMPATIBILITY IDEOGRAPH-2F9C3;Lo;0;L;8860;;;;N;;;;; +2F9C4;CJK COMPATIBILITY IDEOGRAPH-2F9C4;Lo;0;L;8863;;;;N;;;;; +2F9C5;CJK COMPATIBILITY IDEOGRAPH-2F9C5;Lo;0;L;27667;;;;N;;;;; +2F9C6;CJK COMPATIBILITY IDEOGRAPH-2F9C6;Lo;0;L;88D7;;;;N;;;;; +2F9C7;CJK COMPATIBILITY IDEOGRAPH-2F9C7;Lo;0;L;88DE;;;;N;;;;; +2F9C8;CJK COMPATIBILITY IDEOGRAPH-2F9C8;Lo;0;L;4635;;;;N;;;;; +2F9C9;CJK COMPATIBILITY IDEOGRAPH-2F9C9;Lo;0;L;88FA;;;;N;;;;; +2F9CA;CJK COMPATIBILITY IDEOGRAPH-2F9CA;Lo;0;L;34BB;;;;N;;;;; +2F9CB;CJK COMPATIBILITY IDEOGRAPH-2F9CB;Lo;0;L;278AE;;;;N;;;;; +2F9CC;CJK COMPATIBILITY IDEOGRAPH-2F9CC;Lo;0;L;27966;;;;N;;;;; +2F9CD;CJK COMPATIBILITY IDEOGRAPH-2F9CD;Lo;0;L;46BE;;;;N;;;;; +2F9CE;CJK COMPATIBILITY IDEOGRAPH-2F9CE;Lo;0;L;46C7;;;;N;;;;; +2F9CF;CJK COMPATIBILITY IDEOGRAPH-2F9CF;Lo;0;L;8AA0;;;;N;;;;; +2F9D0;CJK COMPATIBILITY IDEOGRAPH-2F9D0;Lo;0;L;8AED;;;;N;;;;; +2F9D1;CJK COMPATIBILITY IDEOGRAPH-2F9D1;Lo;0;L;8B8A;;;;N;;;;; +2F9D2;CJK COMPATIBILITY IDEOGRAPH-2F9D2;Lo;0;L;8C55;;;;N;;;;; +2F9D3;CJK COMPATIBILITY IDEOGRAPH-2F9D3;Lo;0;L;27CA8;;;;N;;;;; +2F9D4;CJK COMPATIBILITY IDEOGRAPH-2F9D4;Lo;0;L;8CAB;;;;N;;;;; +2F9D5;CJK COMPATIBILITY IDEOGRAPH-2F9D5;Lo;0;L;8CC1;;;;N;;;;; +2F9D6;CJK COMPATIBILITY IDEOGRAPH-2F9D6;Lo;0;L;8D1B;;;;N;;;;; +2F9D7;CJK COMPATIBILITY IDEOGRAPH-2F9D7;Lo;0;L;8D77;;;;N;;;;; +2F9D8;CJK COMPATIBILITY IDEOGRAPH-2F9D8;Lo;0;L;27F2F;;;;N;;;;; +2F9D9;CJK COMPATIBILITY IDEOGRAPH-2F9D9;Lo;0;L;20804;;;;N;;;;; +2F9DA;CJK COMPATIBILITY IDEOGRAPH-2F9DA;Lo;0;L;8DCB;;;;N;;;;; +2F9DB;CJK COMPATIBILITY IDEOGRAPH-2F9DB;Lo;0;L;8DBC;;;;N;;;;; +2F9DC;CJK COMPATIBILITY IDEOGRAPH-2F9DC;Lo;0;L;8DF0;;;;N;;;;; +2F9DD;CJK COMPATIBILITY IDEOGRAPH-2F9DD;Lo;0;L;208DE;;;;N;;;;; +2F9DE;CJK COMPATIBILITY IDEOGRAPH-2F9DE;Lo;0;L;8ED4;;;;N;;;;; +2F9DF;CJK COMPATIBILITY IDEOGRAPH-2F9DF;Lo;0;L;8F38;;;;N;;;;; +2F9E0;CJK COMPATIBILITY IDEOGRAPH-2F9E0;Lo;0;L;285D2;;;;N;;;;; +2F9E1;CJK COMPATIBILITY IDEOGRAPH-2F9E1;Lo;0;L;285ED;;;;N;;;;; +2F9E2;CJK COMPATIBILITY IDEOGRAPH-2F9E2;Lo;0;L;9094;;;;N;;;;; +2F9E3;CJK COMPATIBILITY IDEOGRAPH-2F9E3;Lo;0;L;90F1;;;;N;;;;; +2F9E4;CJK COMPATIBILITY IDEOGRAPH-2F9E4;Lo;0;L;9111;;;;N;;;;; +2F9E5;CJK COMPATIBILITY IDEOGRAPH-2F9E5;Lo;0;L;2872E;;;;N;;;;; +2F9E6;CJK COMPATIBILITY IDEOGRAPH-2F9E6;Lo;0;L;911B;;;;N;;;;; +2F9E7;CJK COMPATIBILITY IDEOGRAPH-2F9E7;Lo;0;L;9238;;;;N;;;;; +2F9E8;CJK COMPATIBILITY IDEOGRAPH-2F9E8;Lo;0;L;92D7;;;;N;;;;; +2F9E9;CJK COMPATIBILITY IDEOGRAPH-2F9E9;Lo;0;L;92D8;;;;N;;;;; +2F9EA;CJK COMPATIBILITY IDEOGRAPH-2F9EA;Lo;0;L;927C;;;;N;;;;; +2F9EB;CJK COMPATIBILITY IDEOGRAPH-2F9EB;Lo;0;L;93F9;;;;N;;;;; +2F9EC;CJK COMPATIBILITY IDEOGRAPH-2F9EC;Lo;0;L;9415;;;;N;;;;; +2F9ED;CJK COMPATIBILITY IDEOGRAPH-2F9ED;Lo;0;L;28BFA;;;;N;;;;; +2F9EE;CJK COMPATIBILITY IDEOGRAPH-2F9EE;Lo;0;L;958B;;;;N;;;;; +2F9EF;CJK COMPATIBILITY IDEOGRAPH-2F9EF;Lo;0;L;4995;;;;N;;;;; +2F9F0;CJK COMPATIBILITY IDEOGRAPH-2F9F0;Lo;0;L;95B7;;;;N;;;;; +2F9F1;CJK COMPATIBILITY IDEOGRAPH-2F9F1;Lo;0;L;28D77;;;;N;;;;; +2F9F2;CJK COMPATIBILITY IDEOGRAPH-2F9F2;Lo;0;L;49E6;;;;N;;;;; +2F9F3;CJK COMPATIBILITY IDEOGRAPH-2F9F3;Lo;0;L;96C3;;;;N;;;;; +2F9F4;CJK COMPATIBILITY IDEOGRAPH-2F9F4;Lo;0;L;5DB2;;;;N;;;;; +2F9F5;CJK COMPATIBILITY IDEOGRAPH-2F9F5;Lo;0;L;9723;;;;N;;;;; +2F9F6;CJK COMPATIBILITY IDEOGRAPH-2F9F6;Lo;0;L;29145;;;;N;;;;; +2F9F7;CJK COMPATIBILITY IDEOGRAPH-2F9F7;Lo;0;L;2921A;;;;N;;;;; +2F9F8;CJK COMPATIBILITY IDEOGRAPH-2F9F8;Lo;0;L;4A6E;;;;N;;;;; +2F9F9;CJK COMPATIBILITY IDEOGRAPH-2F9F9;Lo;0;L;4A76;;;;N;;;;; +2F9FA;CJK COMPATIBILITY IDEOGRAPH-2F9FA;Lo;0;L;97E0;;;;N;;;;; +2F9FB;CJK COMPATIBILITY IDEOGRAPH-2F9FB;Lo;0;L;2940A;;;;N;;;;; +2F9FC;CJK COMPATIBILITY IDEOGRAPH-2F9FC;Lo;0;L;4AB2;;;;N;;;;; +2F9FD;CJK COMPATIBILITY IDEOGRAPH-2F9FD;Lo;0;L;29496;;;;N;;;;; +2F9FE;CJK COMPATIBILITY IDEOGRAPH-2F9FE;Lo;0;L;980B;;;;N;;;;; +2F9FF;CJK COMPATIBILITY IDEOGRAPH-2F9FF;Lo;0;L;980B;;;;N;;;;; +2FA00;CJK COMPATIBILITY IDEOGRAPH-2FA00;Lo;0;L;9829;;;;N;;;;; +2FA01;CJK COMPATIBILITY IDEOGRAPH-2FA01;Lo;0;L;295B6;;;;N;;;;; +2FA02;CJK COMPATIBILITY IDEOGRAPH-2FA02;Lo;0;L;98E2;;;;N;;;;; +2FA03;CJK COMPATIBILITY IDEOGRAPH-2FA03;Lo;0;L;4B33;;;;N;;;;; +2FA04;CJK COMPATIBILITY IDEOGRAPH-2FA04;Lo;0;L;9929;;;;N;;;;; +2FA05;CJK COMPATIBILITY IDEOGRAPH-2FA05;Lo;0;L;99A7;;;;N;;;;; +2FA06;CJK COMPATIBILITY IDEOGRAPH-2FA06;Lo;0;L;99C2;;;;N;;;;; +2FA07;CJK COMPATIBILITY IDEOGRAPH-2FA07;Lo;0;L;99FE;;;;N;;;;; +2FA08;CJK COMPATIBILITY IDEOGRAPH-2FA08;Lo;0;L;4BCE;;;;N;;;;; +2FA09;CJK COMPATIBILITY IDEOGRAPH-2FA09;Lo;0;L;29B30;;;;N;;;;; +2FA0A;CJK COMPATIBILITY IDEOGRAPH-2FA0A;Lo;0;L;9B12;;;;N;;;;; +2FA0B;CJK COMPATIBILITY IDEOGRAPH-2FA0B;Lo;0;L;9C40;;;;N;;;;; +2FA0C;CJK COMPATIBILITY IDEOGRAPH-2FA0C;Lo;0;L;9CFD;;;;N;;;;; +2FA0D;CJK COMPATIBILITY IDEOGRAPH-2FA0D;Lo;0;L;4CCE;;;;N;;;;; +2FA0E;CJK COMPATIBILITY IDEOGRAPH-2FA0E;Lo;0;L;4CED;;;;N;;;;; +2FA0F;CJK COMPATIBILITY IDEOGRAPH-2FA0F;Lo;0;L;9D67;;;;N;;;;; +2FA10;CJK COMPATIBILITY IDEOGRAPH-2FA10;Lo;0;L;2A0CE;;;;N;;;;; +2FA11;CJK COMPATIBILITY IDEOGRAPH-2FA11;Lo;0;L;4CF8;;;;N;;;;; +2FA12;CJK COMPATIBILITY IDEOGRAPH-2FA12;Lo;0;L;2A105;;;;N;;;;; +2FA13;CJK COMPATIBILITY IDEOGRAPH-2FA13;Lo;0;L;2A20E;;;;N;;;;; +2FA14;CJK COMPATIBILITY IDEOGRAPH-2FA14;Lo;0;L;2A291;;;;N;;;;; +2FA15;CJK COMPATIBILITY IDEOGRAPH-2FA15;Lo;0;L;9EBB;;;;N;;;;; +2FA16;CJK COMPATIBILITY IDEOGRAPH-2FA16;Lo;0;L;4D56;;;;N;;;;; +2FA17;CJK COMPATIBILITY IDEOGRAPH-2FA17;Lo;0;L;9EF9;;;;N;;;;; +2FA18;CJK COMPATIBILITY IDEOGRAPH-2FA18;Lo;0;L;9EFE;;;;N;;;;; +2FA19;CJK COMPATIBILITY IDEOGRAPH-2FA19;Lo;0;L;9F05;;;;N;;;;; +2FA1A;CJK COMPATIBILITY IDEOGRAPH-2FA1A;Lo;0;L;9F0F;;;;N;;;;; +2FA1B;CJK COMPATIBILITY IDEOGRAPH-2FA1B;Lo;0;L;9F16;;;;N;;;;; +2FA1C;CJK COMPATIBILITY IDEOGRAPH-2FA1C;Lo;0;L;9F3B;;;;N;;;;; +2FA1D;CJK COMPATIBILITY IDEOGRAPH-2FA1D;Lo;0;L;2A600;;;;N;;;;; +E0001;LANGUAGE TAG;Cf;0;BN;;;;;N;;;;; +E0020;TAG SPACE;Cf;0;BN;;;;;N;;;;; +E0021;TAG EXCLAMATION MARK;Cf;0;BN;;;;;N;;;;; +E0022;TAG QUOTATION MARK;Cf;0;BN;;;;;N;;;;; +E0023;TAG NUMBER SIGN;Cf;0;BN;;;;;N;;;;; +E0024;TAG DOLLAR SIGN;Cf;0;BN;;;;;N;;;;; +E0025;TAG PERCENT SIGN;Cf;0;BN;;;;;N;;;;; +E0026;TAG AMPERSAND;Cf;0;BN;;;;;N;;;;; +E0027;TAG APOSTROPHE;Cf;0;BN;;;;;N;;;;; +E0028;TAG LEFT PARENTHESIS;Cf;0;BN;;;;;N;;;;; +E0029;TAG RIGHT PARENTHESIS;Cf;0;BN;;;;;N;;;;; +E002A;TAG ASTERISK;Cf;0;BN;;;;;N;;;;; +E002B;TAG PLUS SIGN;Cf;0;BN;;;;;N;;;;; +E002C;TAG COMMA;Cf;0;BN;;;;;N;;;;; +E002D;TAG HYPHEN-MINUS;Cf;0;BN;;;;;N;;;;; +E002E;TAG FULL STOP;Cf;0;BN;;;;;N;;;;; +E002F;TAG SOLIDUS;Cf;0;BN;;;;;N;;;;; +E0030;TAG DIGIT ZERO;Cf;0;BN;;;;;N;;;;; +E0031;TAG DIGIT ONE;Cf;0;BN;;;;;N;;;;; +E0032;TAG DIGIT TWO;Cf;0;BN;;;;;N;;;;; +E0033;TAG DIGIT THREE;Cf;0;BN;;;;;N;;;;; +E0034;TAG DIGIT FOUR;Cf;0;BN;;;;;N;;;;; +E0035;TAG DIGIT FIVE;Cf;0;BN;;;;;N;;;;; +E0036;TAG DIGIT SIX;Cf;0;BN;;;;;N;;;;; +E0037;TAG DIGIT SEVEN;Cf;0;BN;;;;;N;;;;; +E0038;TAG DIGIT EIGHT;Cf;0;BN;;;;;N;;;;; +E0039;TAG DIGIT NINE;Cf;0;BN;;;;;N;;;;; +E003A;TAG COLON;Cf;0;BN;;;;;N;;;;; +E003B;TAG SEMICOLON;Cf;0;BN;;;;;N;;;;; +E003C;TAG LESS-THAN SIGN;Cf;0;BN;;;;;N;;;;; +E003D;TAG EQUALS SIGN;Cf;0;BN;;;;;N;;;;; +E003E;TAG GREATER-THAN SIGN;Cf;0;BN;;;;;N;;;;; +E003F;TAG QUESTION MARK;Cf;0;BN;;;;;N;;;;; +E0040;TAG COMMERCIAL AT;Cf;0;BN;;;;;N;;;;; +E0041;TAG LATIN CAPITAL LETTER A;Cf;0;BN;;;;;N;;;;; +E0042;TAG LATIN CAPITAL LETTER B;Cf;0;BN;;;;;N;;;;; +E0043;TAG LATIN CAPITAL LETTER C;Cf;0;BN;;;;;N;;;;; +E0044;TAG LATIN CAPITAL LETTER D;Cf;0;BN;;;;;N;;;;; +E0045;TAG LATIN CAPITAL LETTER E;Cf;0;BN;;;;;N;;;;; +E0046;TAG LATIN CAPITAL LETTER F;Cf;0;BN;;;;;N;;;;; +E0047;TAG LATIN CAPITAL LETTER G;Cf;0;BN;;;;;N;;;;; +E0048;TAG LATIN CAPITAL LETTER H;Cf;0;BN;;;;;N;;;;; +E0049;TAG LATIN CAPITAL LETTER I;Cf;0;BN;;;;;N;;;;; +E004A;TAG LATIN CAPITAL LETTER J;Cf;0;BN;;;;;N;;;;; +E004B;TAG LATIN CAPITAL LETTER K;Cf;0;BN;;;;;N;;;;; +E004C;TAG LATIN CAPITAL LETTER L;Cf;0;BN;;;;;N;;;;; +E004D;TAG LATIN CAPITAL LETTER M;Cf;0;BN;;;;;N;;;;; +E004E;TAG LATIN CAPITAL LETTER N;Cf;0;BN;;;;;N;;;;; +E004F;TAG LATIN CAPITAL LETTER O;Cf;0;BN;;;;;N;;;;; +E0050;TAG LATIN CAPITAL LETTER P;Cf;0;BN;;;;;N;;;;; +E0051;TAG LATIN CAPITAL LETTER Q;Cf;0;BN;;;;;N;;;;; +E0052;TAG LATIN CAPITAL LETTER R;Cf;0;BN;;;;;N;;;;; +E0053;TAG LATIN CAPITAL LETTER S;Cf;0;BN;;;;;N;;;;; +E0054;TAG LATIN CAPITAL LETTER T;Cf;0;BN;;;;;N;;;;; +E0055;TAG LATIN CAPITAL LETTER U;Cf;0;BN;;;;;N;;;;; +E0056;TAG LATIN CAPITAL LETTER V;Cf;0;BN;;;;;N;;;;; +E0057;TAG LATIN CAPITAL LETTER W;Cf;0;BN;;;;;N;;;;; +E0058;TAG LATIN CAPITAL LETTER X;Cf;0;BN;;;;;N;;;;; +E0059;TAG LATIN CAPITAL LETTER Y;Cf;0;BN;;;;;N;;;;; +E005A;TAG LATIN CAPITAL LETTER Z;Cf;0;BN;;;;;N;;;;; +E005B;TAG LEFT SQUARE BRACKET;Cf;0;BN;;;;;N;;;;; +E005C;TAG REVERSE SOLIDUS;Cf;0;BN;;;;;N;;;;; +E005D;TAG RIGHT SQUARE BRACKET;Cf;0;BN;;;;;N;;;;; +E005E;TAG CIRCUMFLEX ACCENT;Cf;0;BN;;;;;N;;;;; +E005F;TAG LOW LINE;Cf;0;BN;;;;;N;;;;; +E0060;TAG GRAVE ACCENT;Cf;0;BN;;;;;N;;;;; +E0061;TAG LATIN SMALL LETTER A;Cf;0;BN;;;;;N;;;;; +E0062;TAG LATIN SMALL LETTER B;Cf;0;BN;;;;;N;;;;; +E0063;TAG LATIN SMALL LETTER C;Cf;0;BN;;;;;N;;;;; +E0064;TAG LATIN SMALL LETTER D;Cf;0;BN;;;;;N;;;;; +E0065;TAG LATIN SMALL LETTER E;Cf;0;BN;;;;;N;;;;; +E0066;TAG LATIN SMALL LETTER F;Cf;0;BN;;;;;N;;;;; +E0067;TAG LATIN SMALL LETTER G;Cf;0;BN;;;;;N;;;;; +E0068;TAG LATIN SMALL LETTER H;Cf;0;BN;;;;;N;;;;; +E0069;TAG LATIN SMALL LETTER I;Cf;0;BN;;;;;N;;;;; +E006A;TAG LATIN SMALL LETTER J;Cf;0;BN;;;;;N;;;;; +E006B;TAG LATIN SMALL LETTER K;Cf;0;BN;;;;;N;;;;; +E006C;TAG LATIN SMALL LETTER L;Cf;0;BN;;;;;N;;;;; +E006D;TAG LATIN SMALL LETTER M;Cf;0;BN;;;;;N;;;;; +E006E;TAG LATIN SMALL LETTER N;Cf;0;BN;;;;;N;;;;; +E006F;TAG LATIN SMALL LETTER O;Cf;0;BN;;;;;N;;;;; +E0070;TAG LATIN SMALL LETTER P;Cf;0;BN;;;;;N;;;;; +E0071;TAG LATIN SMALL LETTER Q;Cf;0;BN;;;;;N;;;;; +E0072;TAG LATIN SMALL LETTER R;Cf;0;BN;;;;;N;;;;; +E0073;TAG LATIN SMALL LETTER S;Cf;0;BN;;;;;N;;;;; +E0074;TAG LATIN SMALL LETTER T;Cf;0;BN;;;;;N;;;;; +E0075;TAG LATIN SMALL LETTER U;Cf;0;BN;;;;;N;;;;; +E0076;TAG LATIN SMALL LETTER V;Cf;0;BN;;;;;N;;;;; +E0077;TAG LATIN SMALL LETTER W;Cf;0;BN;;;;;N;;;;; +E0078;TAG LATIN SMALL LETTER X;Cf;0;BN;;;;;N;;;;; +E0079;TAG LATIN SMALL LETTER Y;Cf;0;BN;;;;;N;;;;; +E007A;TAG LATIN SMALL LETTER Z;Cf;0;BN;;;;;N;;;;; +E007B;TAG LEFT CURLY BRACKET;Cf;0;BN;;;;;N;;;;; +E007C;TAG VERTICAL LINE;Cf;0;BN;;;;;N;;;;; +E007D;TAG RIGHT CURLY BRACKET;Cf;0;BN;;;;;N;;;;; +E007E;TAG TILDE;Cf;0;BN;;;;;N;;;;; +E007F;CANCEL TAG;Cf;0;BN;;;;;N;;;;; +F0000;<Plane 15 Private Use, First>;Co;0;L;;;;;N;;;;; +FFFFD;<Plane 15 Private Use, Last>;Co;0;L;;;;;N;;;;; +100000;<Plane 16 Private Use, First>;Co;0;L;;;;;N;;;;; +10FFFD;<Plane 16 Private Use, Last>;Co;0;L;;;;;N;;;;; diff --git a/libraries/liblunicode/ucdata/MUTTUCData.txt b/libraries/liblunicode/ucdata/MUTTUCData.txt new file mode 100644 index 0000000..82c4659 --- /dev/null +++ b/libraries/liblunicode/ucdata/MUTTUCData.txt @@ -0,0 +1,303 @@ +# +# $Id: MUTTUCData.txt,v 1.3 1999/10/29 00:04:35 mleisher Exp $ +# +# Copyright 1999 Computing Research Labs, New Mexico State University +# +# 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 COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY 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. +# +# +# Implementation specific character properties. +# +# +# Space, other. +# +0009;;Ss;;;;;;;;;;;; +000A;;Ss;;;;;;;;;;;; +000B;;Ss;;;;;;;;;;;; +000C;;Ss;;;;;;;;;;;; +000D;;Ss;;;;;;;;;;;; +# +# Non-breaking. +# +00A0;;Nb;;;;;;;;;;;; +2007;;Nb;;;;;;;;;;;; +2011;;Nb;;;;;;;;;;;; +FEFF;;Nb;;;;;;;;;;;; +# +# Symmetric. +# +0028;;Sy;;;;;;;;;;;; +0029;;Sy;;;;;;;;;;;; +005B;;Sy;;;;;;;;;;;; +005D;;Sy;;;;;;;;;;;; +007B;;Sy;;;;;;;;;;;; +007D;;Sy;;;;;;;;;;;; +00AB;;Sy;;;;;;;;;;;; +00BB;;Sy;;;;;;;;;;;; +0F3A;;Sy;;;;;;;;;;;; +0F3B;;Sy;;;;;;;;;;;; +0F3C;;Sy;;;;;;;;;;;; +0F3D;;Sy;;;;;;;;;;;; +0F3E;;Sy;;;;;;;;;;;; +0F3F;;Sy;;;;;;;;;;;; +2018;;Sy;;;;;;;;;;;; +2019;;Sy;;;;;;;;;;;; +201A;;Sy;;;;;;;;;;;; +201B;;Sy;;;;;;;;;;;; +201C;;Sy;;;;;;;;;;;; +201D;;Sy;;;;;;;;;;;; +201E;;Sy;;;;;;;;;;;; +201F;;Sy;;;;;;;;;;;; +2039;;Sy;;;;;;;;;;;; +203A;;Sy;;;;;;;;;;;; +2045;;Sy;;;;;;;;;;;; +2046;;Sy;;;;;;;;;;;; +207D;;Sy;;;;;;;;;;;; +207E;;Sy;;;;;;;;;;;; +208D;;Sy;;;;;;;;;;;; +208E;;Sy;;;;;;;;;;;; +2329;;Sy;;;;;;;;;;;; +232A;;Sy;;;;;;;;;;;; +3008;;Sy;;;;;;;;;;;; +3009;;Sy;;;;;;;;;;;; +300A;;Sy;;;;;;;;;;;; +300B;;Sy;;;;;;;;;;;; +300C;;Sy;;;;;;;;;;;; +300D;;Sy;;;;;;;;;;;; +300E;;Sy;;;;;;;;;;;; +300F;;Sy;;;;;;;;;;;; +3010;;Sy;;;;;;;;;;;; +3011;;Sy;;;;;;;;;;;; +3014;;Sy;;;;;;;;;;;; +3015;;Sy;;;;;;;;;;;; +3016;;Sy;;;;;;;;;;;; +3017;;Sy;;;;;;;;;;;; +3018;;Sy;;;;;;;;;;;; +3019;;Sy;;;;;;;;;;;; +301A;;Sy;;;;;;;;;;;; +301B;;Sy;;;;;;;;;;;; +301D;;Sy;;;;;;;;;;;; +301E;;Sy;;;;;;;;;;;; +301F;;Sy;;;;;;;;;;;; +FD3E;;Sy;;;;;;;;;;;; +FD3F;;Sy;;;;;;;;;;;; +FE35;;Sy;;;;;;;;;;;; +FE36;;Sy;;;;;;;;;;;; +FE37;;Sy;;;;;;;;;;;; +FE38;;Sy;;;;;;;;;;;; +FE39;;Sy;;;;;;;;;;;; +FE3A;;Sy;;;;;;;;;;;; +FE3B;;Sy;;;;;;;;;;;; +FE3C;;Sy;;;;;;;;;;;; +FE3D;;Sy;;;;;;;;;;;; +FE3E;;Sy;;;;;;;;;;;; +FE3F;;Sy;;;;;;;;;;;; +FE40;;Sy;;;;;;;;;;;; +FE41;;Sy;;;;;;;;;;;; +FE42;;Sy;;;;;;;;;;;; +FE43;;Sy;;;;;;;;;;;; +FE44;;Sy;;;;;;;;;;;; +FE59;;Sy;;;;;;;;;;;; +FE5A;;Sy;;;;;;;;;;;; +FE5B;;Sy;;;;;;;;;;;; +FE5C;;Sy;;;;;;;;;;;; +FE5D;;Sy;;;;;;;;;;;; +FE5E;;Sy;;;;;;;;;;;; +FF08;;Sy;;;;;;;;;;;; +FF09;;Sy;;;;;;;;;;;; +FF3B;;Sy;;;;;;;;;;;; +FF3D;;Sy;;;;;;;;;;;; +FF5B;;Sy;;;;;;;;;;;; +FF5D;;Sy;;;;;;;;;;;; +FF62;;Sy;;;;;;;;;;;; +FF63;;Sy;;;;;;;;;;;; +# +# Hex digit. +# +0030;;Hd;;;;;;;;;;;; +0031;;Hd;;;;;;;;;;;; +0032;;Hd;;;;;;;;;;;; +0033;;Hd;;;;;;;;;;;; +0034;;Hd;;;;;;;;;;;; +0035;;Hd;;;;;;;;;;;; +0036;;Hd;;;;;;;;;;;; +0037;;Hd;;;;;;;;;;;; +0038;;Hd;;;;;;;;;;;; +0039;;Hd;;;;;;;;;;;; +0041;;Hd;;;;;;;;;;;; +0042;;Hd;;;;;;;;;;;; +0043;;Hd;;;;;;;;;;;; +0044;;Hd;;;;;;;;;;;; +0045;;Hd;;;;;;;;;;;; +0046;;Hd;;;;;;;;;;;; +0061;;Hd;;;;;;;;;;;; +0062;;Hd;;;;;;;;;;;; +0063;;Hd;;;;;;;;;;;; +0064;;Hd;;;;;;;;;;;; +0065;;Hd;;;;;;;;;;;; +0066;;Hd;;;;;;;;;;;; +FF10;;Hd;;;;;;;;;;;; +FF11;;Hd;;;;;;;;;;;; +FF12;;Hd;;;;;;;;;;;; +FF13;;Hd;;;;;;;;;;;; +FF14;;Hd;;;;;;;;;;;; +FF15;;Hd;;;;;;;;;;;; +FF16;;Hd;;;;;;;;;;;; +FF17;;Hd;;;;;;;;;;;; +FF18;;Hd;;;;;;;;;;;; +FF19;;Hd;;;;;;;;;;;; +FF21;;Hd;;;;;;;;;;;; +FF22;;Hd;;;;;;;;;;;; +FF23;;Hd;;;;;;;;;;;; +FF24;;Hd;;;;;;;;;;;; +FF25;;Hd;;;;;;;;;;;; +FF26;;Hd;;;;;;;;;;;; +FF41;;Hd;;;;;;;;;;;; +FF42;;Hd;;;;;;;;;;;; +FF43;;Hd;;;;;;;;;;;; +FF44;;Hd;;;;;;;;;;;; +FF45;;Hd;;;;;;;;;;;; +FF46;;Hd;;;;;;;;;;;; +# +# Quote marks. +# +0022;;Qm;;;;;;;;;;;; +0027;;Qm;;;;;;;;;;;; +00AB;;Qm;;;;;;;;;;;; +00BB;;Qm;;;;;;;;;;;; +2018;;Qm;;;;;;;;;;;; +2019;;Qm;;;;;;;;;;;; +201A;;Qm;;;;;;;;;;;; +201B;;Qm;;;;;;;;;;;; +201C;;Qm;;;;;;;;;;;; +201D;;Qm;;;;;;;;;;;; +201E;;Qm;;;;;;;;;;;; +201F;;Qm;;;;;;;;;;;; +2039;;Qm;;;;;;;;;;;; +203A;;Qm;;;;;;;;;;;; +300C;;Qm;;;;;;;;;;;; +300D;;Qm;;;;;;;;;;;; +300E;;Qm;;;;;;;;;;;; +300F;;Qm;;;;;;;;;;;; +301D;;Qm;;;;;;;;;;;; +301E;;Qm;;;;;;;;;;;; +301F;;Qm;;;;;;;;;;;; +FE41;;Qm;;;;;;;;;;;; +FE42;;Qm;;;;;;;;;;;; +FE43;;Qm;;;;;;;;;;;; +FE44;;Qm;;;;;;;;;;;; +FF02;;Qm;;;;;;;;;;;; +FF07;;Qm;;;;;;;;;;;; +FF62;;Qm;;;;;;;;;;;; +FF63;;Qm;;;;;;;;;;;; +# +# Special Devanagari forms +# +E900;DEVANAGARI KSHA LIGATURE;Lo;0;L;0915 094D 0937;;;;N;;;;; +E901;DEVANAGARI GNYA LIGATURE;Lo;0;L;091C 094D 091E;;;;N;;;;; +E902;DEVANAGARI TTA LIGATURE;Lo;0;L;0924 094D 0924;;;;N;;;;; +E903;DEVANAGARI TRA LIGATURE;Lo;0;L;0924 094D 0930;;;;N;;;;; +E904;DEVANAGARI SHCHA LIGATURE;Lo;0;L;0936 094D 091B;;;;N;;;;; +E905;DEVANAGARI SHRA LIGATURE;Lo;0;L;0936 094D 0930;;;;N;;;;; +E906;DEVANAGARI SHVA LIGATURE;Lo;0;L;0936 094D 0935;;;;N;;;;; +E907;DEVANAGARI KRA LIGATURE;Lo;0;L;;;;;N;;;;; +E908;DEVANAGARI JRA LIGATURE;Lo;0;L;;;;;N;;;;; +E909;DEVANAGARI ZRA LIGATURE;Lo;0;L;;;;;N;;;;; +E90A;DEVANAGARI PHRA LIGATURE;Lo;0;L;;;;;N;;;;; +E90B;DEVANAGARI FRA LIGATURE;Lo;0;L;;;;;N;;;;; +E90C;DEVANAGARI PRA LIGATURE;Lo;0;L;;;;;N;;;;; +E90D;DEVANAGARI SRA LIGATURE;Lo;0;L;;;;;N;;;;; +E90E;DEVANAGARI RU LIGATURE;Lo;0;L;;;;;N;;;;; +E90F;DEVANAGARI RUU LIGATURE;Lo;0;L;;;;;N;;;;; +E915;DEVANAGARI HALF LETTER KA;Lo;0;L;;;;;N;;;;; +E916;DEVANAGARI HALF LETTER KHA;Lo;0;L;;;;;N;;;;; +E917;DEVANAGARI HALF LETTER GA;Lo;0;L;;;;;N;;;;; +E918;DEVANAGARI HALF LETTER GHA;Lo;0;L;;;;;N;;;;; +E919;DEVANAGARI HALF LETTER NGA;Lo;0;L;;;;;N;;;;; +E91A;DEVANAGARI HALF LETTER CA;Lo;0;L;;;;;N;;;;; +E91B;DEVANAGARI HALF LETTER CHA;Lo;0;L;;;;;N;;;;; +E91C;DEVANAGARI HALF LETTER JA;Lo;0;L;;;;;N;;;;; +E91D;DEVANAGARI HALF LETTER JHA;Lo;0;L;;;;;N;;;;; +E91E;DEVANAGARI HALF LETTER NYA;Lo;0;L;;;;;N;;;;; +E91F;DEVANAGARI HALF LETTER TTA;Lo;0;L;;;;;N;;;;; +E920;DEVANAGARI HALF LETTER TTHA;Lo;0;L;;;;;N;;;;; +E921;DEVANAGARI HALF LETTER DDA;Lo;0;L;;;;;N;;;;; +E922;DEVANAGARI HALF LETTER DDHA;Lo;0;L;;;;;N;;;;; +E923;DEVANAGARI HALF LETTER NNA;Lo;0;L;;;;;N;;;;; +E924;DEVANAGARI HALF LETTER TA;Lo;0;L;;;;;N;;;;; +E925;DEVANAGARI HALF LETTER THA;Lo;0;L;;;;;N;;;;; +E926;DEVANAGARI HALF LETTER DA;Lo;0;L;;;;;N;;;;; +E927;DEVANAGARI HALF LETTER DHA;Lo;0;L;;;;;N;;;;; +E928;DEVANAGARI HALF LETTER NA;Lo;0;L;;;;;N;;;;; +E929;DEVANAGARI HALF LETTER NNNA;Lo;0;L;0928 093C;;;;N;;;;; +E92A;DEVANAGARI HALF LETTER PA;Lo;0;L;;;;;N;;;;; +E92B;DEVANAGARI HALF LETTER PHA;Lo;0;L;;;;;N;;;;; +E92C;DEVANAGARI HALF LETTER BA;Lo;0;L;;;;;N;;;;; +E92D;DEVANAGARI HALF LETTER BHA;Lo;0;L;;;;;N;;;;; +E92E;DEVANAGARI HALF LETTER MA;Lo;0;L;;;;;N;;;;; +E92F;DEVANAGARI HALF LETTER YA;Lo;0;L;;;;;N;;;;; +E930;DEVANAGARI HALF LETTER RA;Lo;0;L;;;;;N;;;;; +E931;DEVANAGARI HALF LETTER RRA;Lo;0;L;0930 093C;;;;N;;;;; +E932;DEVANAGARI HALF LETTER LA;Lo;0;L;;;;;N;;;;; +E933;DEVANAGARI HALF LETTER LLA;Lo;0;L;;;;;N;;;;; +E934;DEVANAGARI HALF LETTER LLLA;Lo;0;L;0933 093C;;;;N;;;;; +E935;DEVANAGARI HALF LETTER VA;Lo;0;L;;;;;N;;;;; +E936;DEVANAGARI HALF LETTER SHA;Lo;0;L;;;;;N;;;;; +E937;DEVANAGARI HALF LETTER SSA;Lo;0;L;;;;;N;;;;; +E938;DEVANAGARI HALF LETTER SA;Lo;0;L;;;;;N;;;;; +E939;DEVANAGARI HALF LETTER HA;Lo;0;L;;;;;N;;;;; +E940;DEVANAGARI KKA LIGATURE;Lo;0;L;0915 094D 0915;;;;N;;;;; +E941;DEVANAGARI KTA LIGATURE;Lo;0;L;0915 094D 0924;;;;N;;;;; +E942;DEVANAGARI NGKA LIGATURE;Lo;0;L;0919 094D 0915;;;;N;;;;; +E943;DEVANAGARI NGKHA LIGATURE;Lo;0;L;0919 094D 0916;;;;N;;;;; +E944;DEVANAGARI NGGA LIGATURE;Lo;0;L;0919 094D 0917;;;;N;;;;; +E945;DEVANAGARI NGGHA LIGATURE;Lo;0;L;0919 094D 0918;;;;N;;;;; +E946;DEVANAGARI NYJA LIGATURE;Lo;0;L;091E 094D 091C;;;;N;;;;; +E947;DEVANAGARI DGHA LIGATURE;Lo;0;L;0926 094D 0918;;;;N;;;;; +E948;DEVANAGARI DDA LIGATURE;Lo;0;L;0926 094D 0926;;;;N;;;;; +E949;DEVANAGARI DDHA LIGATURE;Lo;0;L;0926 094D 0927;;;;N;;;;; +E94A;DEVANAGARI DBA LIGATURE;Lo;0;L;0926 094D 092C;;;;N;;;;; +E94B;DEVANAGARI DBHA LIGATURE;Lo;0;L;0926 094D 092D;;;;N;;;;; +E94C;DEVANAGARI DMA LIGATURE;Lo;0;L;0926 094D 092E;;;;N;;;;; +E94D;DEVANAGARI DYA LIGATURE;Lo;0;L;0926 094D 092F;;;;N;;;;; +E94E;DEVANAGARI DVA LIGATURE;Lo;0;L;0926 094D 0935;;;;N;;;;; +E94F;DEVANAGARI TT-TTA LIGATURE;Lo;0;L;091F 094D 091F;;;;N;;;;; +E950;DEVANAGARI TT-TTHA LIGATURE;Lo;0;L;091F 094D 0920;;;;N;;;;; +E951;DEVANAGARI TTH-TTHA LIGATURE;Lo;0;L;0920 094D 0920;;;;N;;;;; +E952;DEVANAGARI DD-GA LIGATURE;Lo;0;L;0921 094D 0917;;;;N;;;;; +E953;DEVANAGARI DD-DDA LIGATURE;Lo;0;L;0921 094D 0921;;;;N;;;;; +E954;DEVANAGARI DD-DDHA LIGATURE;Lo;0;L;0921 094D 0922;;;;N;;;;; +E955;DEVANAGARI NNA LIGATURE;Lo;0;L;0928 094D 0928;;;;N;;;;; +E956;DEVANAGARI HMA LIGATURE;Lo;0;L;0939 094D 092E;;;;N;;;;; +E957;DEVANAGARI HYA LIGATURE;Lo;0;L;0939 094D 092F;;;;N;;;;; +E958;DEVANAGARI HLA LIGATURE;Lo;0;L;0939 094D 0932;;;;N;;;;; +E959;DEVANAGARI HVA LIGATURE;Lo;0;L;0939 094D 0935;;;;N;;;;; +E95A;DEVANAGARI STRA LIGATURE;Lo;0;L;0938 094D 0924 094D 0930;;;;N;;;;; +E970;DEVANAGARI HALF KSHA LIGATURE;Lo;0;L;0915 094D 0937;;;;N;;;;; +E971;DEVANAGARI HALF GNYA LIGATURE;Lo;0;L;091C 094D 091E;;;;N;;;;; +E972;DEVANAGARI HALF TTA LIGATURE;Lo;0;L;0924 094D 0924;;;;N;;;;; +E973;DEVANAGARI HALF TRA LIGATURE;Lo;0;L;0924 094D 0930;;;;N;;;;; +E974;DEVANAGARI HALF SHCHA LIGATURE;Lo;0;L;0936 094D 091B;;;;N;;;;; +E975;DEVANAGARI HALF SHRA LIGATURE;Lo;0;L;0936 094D 0930;;;;N;;;;; +E976;DEVANAGARI HALF SHVA LIGATURE;Lo;0;L;0936 094D 0935;;;;N;;;;; +E97B;DEVANAGARI SIGN RRA-REPHA;Mn;36;L;;;;;N;;;;; +E97C;DEVANAGARI HAR LIGATURE;Lo;0;L;0939 0943;;;;N;;;;; +E97D;DEVANAGARI SIGN EYELASH RA;Lo;0;L;;;;;N;;;;; +E97E;DEVANAGARI SIGN REPHA;Mn;36;L;;;;;N;;;;; +E97F;DEVANAGARI SIGN SUBJOINED RA;Mn;36;L;;;;;N;;;;; diff --git a/libraries/liblunicode/ucdata/README b/libraries/liblunicode/ucdata/README new file mode 100644 index 0000000..6a02cc1 --- /dev/null +++ b/libraries/liblunicode/ucdata/README @@ -0,0 +1,313 @@ +# +# $Id: README,v 1.33 2001/01/02 18:46:19 mleisher Exp $ +# + + MUTT UCData Package 2.5 + ----------------------- + +This is a package that supports ctype-like operations for Unicode UCS-2 text +(and surrogates), case mapping, decomposition lookup, and provides a +bidirectional reordering algorithm. To use it, you will need to get the +latest "UnicodeData-*.txt" (or later) file from the Unicode Web or FTP site. + +The character information portion of the package consists of three parts: + + 1. A program called "ucgendat" which generates five data files from the + UnicodeData-*.txt file. The files are: + + A. case.dat - the case mappings. + B. ctype.dat - the character property tables. + C. comp.dat - the character composition pairs. + D. decomp.dat - the character decompositions. + E. cmbcl.dat - the non-zero combining classes. + F. num.dat - the codes representing numbers. + + 2. The "ucdata.[ch]" files which implement the functions needed to + check to see if a character matches groups of properties, to map between + upper, lower, and title case, to look up the decomposition of a + character, look up the combining class of a character, and get the number + value of a character. + + 3. The UCData.java class which provides the same API (with minor changes for + the numbers) and loads the same binary data files as the C code. + +A short reference to the functions available is in the "api.txt" file. + +Techie Details +============== + +The "ucgendat" program parses files from the command line which are all in the +Unicode Character Database (UCDB) format. An additional properties file, +"MUTTUCData.txt", provides some extra properties for some characters. + +The program looks for the two character properties fields (2 and 4), the +combining class field (3), the decomposition field (5), the numeric value +field (8), and the case mapping fields (12, 13, and 14). The decompositions +are recursively expanded before being written out. + +The decomposition table contains all the canonical decompositions. This means +all decompositions that do not have tags such as "<compat>" or "<font>". + +The data is almost all stored as unsigned longs (32-bits assumed) and the +routines that load the data take care of endian swaps when necessary. This +also means that supplementary characters (>= 0x10000) can be placed in the +data files the "ucgendat" program parses. + +The data is written as external files and broken into six parts so it can be +selectively updated at runtime if necessary. + +The data files currently generated from the "ucgendat" program total about 56K +in size all together. + +The format of the binary data files is documented in the "format.txt" file. + +========================================================================== + + The "Pretty Good Bidi Algorithm" + -------------------------------- + +This routine provides an alternative to the Unicode Bidi algorithm. The +difference is that this version of the PGBA does not handle the explicit +directional codes (LRE, RLE, LRO, RLO, PDF). It should now produce the same +results as the Unicode BiDi algorithm for implicit reordering. Included are +functions for doing cursor motion in both logical and visual order. + +This implementation is provided to demonstrate an effective alternate method +for implicit reordering. To make this useful for an application, it probably +needs some changes to the memory allocation and deallocation, as well as data +structure additions for rendering. + +Mark Leisher <mleisher@crl.nmsu.edu> +19 November 1999 + +----------------------------------------------------------------------------- + +CHANGES +======= +Version 2.5 +----------- +1. Changed the number lookup to set the denominator to 1 in cases of digits. + This restores functional compatibility with John Cowan's UCType package. + +2. Added support for the AL property. + +3. Modified load and reload functions to return error codes. + +Version 2.4 +----------- +1. Improved some bidi algorithm documentation in the code. + +2. Fixed a code mixup that produced a non-working version. + +Version 2.3 +----------- +1. Fixed a misspelling in the ucpgba.h header file. + +2. Fixed a bug which caused trailing weak non-digit sequences to be left out of + the reordered string in the bidi algorithm. + +3. Fixed a problem with weak sequences containing non-spacing marks in the + bidi algorithm. + +4. Fixed a problem with text runs of the opposite direction of the string + surrounding a weak + neutral text run appearing in the wrong order in the + bidi algorithm. + +5. Added a default overall direction parameter to the reordering function for + cases of strings with no strong directional characters in the bidi + algorithm. + +6. The bidi API documentation was improved. + +7. Added a man page for the bidi API. + +Version 2.2 +----------- +1. Fixed a problem with the bidi algorithm locating directional section + boundaries. + +2. Fixed a problem with the bidi algorithm starting the reordering correctly. + +3. Fixed a problem with the bidi algorithm determining end boundaries for LTR + segments. + +4. Fixed a problem with the bidi algorithm reordering weak (digits and number + separators) segments. + +5. Added automatic switching of symmetrically paired characters when + reversing RTL segments. + +6. Added a missing symmetric character to the extra character properties in + MUTTUCData.txt. + +7. Added support for doing logical and visual cursor traversal. + +Version 2.1 +----------- +1. Updated the ucgendat program to handle the Unicode 3.0 character database + properties. The AL and BM bidi properties gets marked as strong RTL and + Other Neutral, the NSM, LRE, RLE, PDF, LRO, and RLO controls all get marked + as Other Neutral. + +2. Fixed some problems with testing against signed values in the UCData.java + code and some minor cleanup. + +3. Added the "Pretty Good Bidi Algorithm." + +Version 2.0 +----------- +1. Removed the old Java stuff for a new class that loads directly from the + same data files as the C code does. + +2. Fixed a problem with choosing the correct field when mapping case. + +3. Adjust some search routines to start their search in the correct position. + +4. Moved the copyright year to 1999. + +Version 1.9 +----------- +1. Fixed a problem with an incorrect amount of storage being allocated for the + combining class nodes. + +2. Fixed an invalid initialization in the number code. + +3. Changed the Java template file formatting a bit. + +4. Added tables and function for getting decompositions in the Java class. + +Version 1.8 +----------- +1. Fixed a problem with adding certain ranges. + +2. Added two more macros for testing for identifiers. + +3. Tested with the UnicodeData-2.1.5.txt file. + +Version 1.7 +----------- +1. Fixed a problem with looking up decompositions in "ucgendat." + +Version 1.6 +----------- +1. Added two new properties introduced with UnicodeData-2.1.4.txt. + +2. Changed the "ucgendat.c" program a little to automatically align the + property data on a 4-byte boundary when new properties are added. + +3. Changed the "ucgendat.c" programs to only generate canonical + decompositions. + +4. Added two new macros ucisinitialpunct() and ucisfinalpunct() to check for + initial and final punctuation characters. + +5. Minor additions and changes to the documentation. + +Version 1.5 +----------- +1. Changed all file open calls to include binary mode with "b" for DOS/WIN + platforms. + +2. Wrapped the unistd.h include so it won't be included when compiled under + Win32. + +3. Fixed a bad range check for hex digits in ucgendat.c. + +4. Fixed a bad endian swap for combining classes. + +5. Added code to make a number table and associated lookup functions. + Functions added are ucnumber(), ucdigit(), and ucgetnumber(). The last + function is to maintain compatibility with John Cowan's "uctype" package. + +Version 1.4 +----------- +1. Fixed a bug with adding a range. + +2. Fixed a bug with inserting a range in order. + +3. Fixed incorrectly specified ucisdefined() and ucisundefined() macros. + +4. Added the missing unload for the combining class data. + +5. Fixed a bad macro placement in ucisweak(). + +Version 1.3 +----------- +1. Bug with case mapping calculations fixed. + +2. Bug with empty character property entries fixed. + +3. Bug with incorrect type in the combining class lookup fixed. + +4. Some corrections done to api.txt. + +5. Bug in certain character property lookups fixed. + +6. Added a character property table that records the defined characters. + +7. Replaced ucisunknown() with ucisdefined() and ucisundefined(). + +Version 1.2 +----------- +1. Added code to ucgendat to generate a combining class table. + +2. Fixed an endian problem with the byte count of decompositions. + +3. Fixed some minor problems in the "format.txt" file. + +4. Removed some bogus "Ss" values from MUTTUCData.txt file. + +5. Added API function to get combining class. + +6. Changed the open mode to "rb" so binary data files will be opened correctly + on DOS/WIN as well as other platforms. + +7. Added the "api.txt" file. + +Version 1.1 +----------- +1. Added ucisxdigit() which I overlooked. + +2. Added UC_LT to the ucisalpha() macro which I overlooked. + +3. Change uciscntrl() to include UC_CF. + +4. Added ucisocntrl() and ucfntcntrl() macros. + +5. Added a ucisblank() which I overlooked. + +6. Added missing properties to ucissymbol() and ucisnumber(). + +7. Added ucisgraph() and ucisprint(). + +8. Changed the "Mr" property to "Sy" to mark this subset of mirroring + characters as symmetric to avoid trampling the Unicode/ISO10646 sense of + mirroring. + +9. Added another property called "Ss" which includes control characters + traditionally seen as spaces in the isspace() macro. + +10. Added a bunch of macros to be API compatible with John Cowan's package. + +ACKNOWLEDGEMENTS +================ + +Thanks go to John Cowan <cowan@locke.ccil.org> for pointing out lots of +missing things and giving me stuff, particularly a bunch of new macros. + +Thanks go to Bob Verbrugge <bob_verbrugge@nl.compuware.com> for pointing out +various bugs. + +Thanks go to Christophe Pierret <cpierret@businessobjects.com> for pointing +out that file modes need to have "b" for DOS/WIN machines, pointing out +unistd.h is not a Win 32 header, and pointing out a problem with ucisalnum(). + +Thanks go to Kent Johnson <kent@pondview.mv.com> for finding a bug that caused +incomplete decompositions to be generated by the "ucgendat" program. + +Thanks go to Valeriy E. Ushakov <uwe@ptc.spbu.ru> for spotting an allocation +error and an initialization error. + +Thanks go to Stig Venaas <Stig.Venaas@uninett.no> for providing a patch to +support return types on load and reload, and for major updates to handle +canonical composition and decomposition. diff --git a/libraries/liblunicode/ucdata/api.txt b/libraries/liblunicode/ucdata/api.txt new file mode 100644 index 0000000..59170ba --- /dev/null +++ b/libraries/liblunicode/ucdata/api.txt @@ -0,0 +1,401 @@ +# +# $Id: api.txt,v 1.3 2001/01/02 18:46:20 mleisher Exp $ +# + + The MUTT UCData API + ------------------- + + +#### +NOTE: This library has been customized for use with OpenLDAP. The character +data tables are hardcoded into the library and the load/unload/reload +functions are no-ops. Also, the MUTT API claimed to be compatible with +John Cowan's library but its ucnumber behavior was broken. This has been +fixed in the OpenLDAP release. + +By default, the implementation specific properties in MUTTUCData.txt are +not incorporated into the OpenLDAP build. You can supply them to ucgendat +and recreate uctable.h if you need them. + -- hyc@openldap.org +#### + + +----------------------------------------------------------------------------- + +Macros that combine to select data tables for ucdata_load(), ucdata_unload(), +and ucdata_reload(). + +#define UCDATA_CASE 0x01 +#define UCDATA_CTYPE 0x02 +#define UCDATA_DECOMP 0x04 +#define UCDATA_CMBCL 0x08 +#define UCDATA_NUM 0x10 +#define UCDATA_COMP 0x20 +#define UCATA_ALL (UCDATA_CASE|UCDATA_CTYPE|UCDATA_DECOMP|\ + UCDATA_CMBCL|UCDATA_NUM|UCDATA_COMP) +----------------------------------------------------------------------------- + +void ucdata_load(char *paths, int masks) + + This function initializes the UCData library by locating the data files in + one of the colon-separated directories in the `paths' parameter. The data + files to be loaded are specified in the `masks' parameter as a bitwise + combination of the macros listed above. + + This should be called before using any of the other functions. + + NOTE: the ucdata_setup(char *paths) function is now a macro that expands + into this function at compile time. + +----------------------------------------------------------------------------- + +void ucdata_unload(int masks) + + This function unloads the data tables specified in the `masks' parameter. + + This function should be called when the application is done using the UCData + package. + + NOTE: the ucdata_cleanup() function is now a macro that expands into this + function at compile time. + +----------------------------------------------------------------------------- + +void ucdata_reload(char *paths, int masks) + + This function reloads the data files from one of the colon-separated + directories in the `paths' parameter. The data files to be reloaded are + specified in the `masks' parameter as a bitwise combination of the macros + listed above. + + If the data files have already been loaded, they are unloaded before the + data files are loaded again. + +----------------------------------------------------------------------------- + +int ucdecomp(unsigned long code, unsigned long *num, unsigned long **decomp) + + This function determines if a character has a decomposition and returns the + decomposition information if it exists. + + If a zero is returned, there is no decomposition. If a non-zero is + returned, then the `num' and `decomp' variables are filled in with the + appropriate values. + + Example call: + + unsigned long i, num, *decomp; + + if (ucdecomp(0x1d5, &num, &decomp) != 0) { + for (i = 0; i < num; i++) + printf("0x%08lX,", decomp[i]); + putchar('\n'); + } + +int uccanondecomp(const unsigned long *in, int inlen, unsigned long **out, + int *outlen) + + This function decomposes an input string and does canonical reordering of + the characters at the same time. + + If a -1 is returned, memory allocation was not successful. If a zero is + returned, no decomposition occured. Any other value means the output string + contains the fully decomposed string in canonical order. + + If the "outlen" parameter comes back with a value > 0, then the string + returned in the "out" parameter needs to be deallocated by the caller. + +----------------------------------------------------------------------------- + +int ucdecomp_hangul(unsigned long code, unsigned long *num, + unsigned long decomp[]) + + This function determines if a Hangul syllable has a decomposition and + returns the decomposition information. + + An array of at least size 3 should be passed to the function for the + decomposition of the syllable. + + If a zero is returned, the character is not a Hangul syllable. If a + non-zero is returned, the `num' field will be 2 or 3 and the syllable will + be decomposed into the `decomp' array arithmetically. + + Example call: + + unsigned long i, num, decomp[3]; + + if (ucdecomp_hangul(0xb1ba, &num, &decomp) != 0) { + for (i = 0; i < num; i++) + printf("0x%08lX,", decomp[i]); + putchar('\n'); + } + +----------------------------------------------------------------------------- + +int uccomp(unsigned long ch1, unsigned long ch2, unsigned long *comp) + + This function takes a pair of characters and determines if they combine to + form another character. + + If a zero is returned, no composition is formed by the character pair. Any + other value indicates the "comp" parameter has a value. + +int uccomp_hangul(unsigned long *str, int len) + + This function composes the Hangul Jamo in the string. The composition is + done in-place. + + The return value provides the new length of the string. This will be + smaller than "len" if compositions occured. + +int uccanoncomp(unsigned long *str, int len) + + This function does a canonical composition of characters in the string. + + The return value is the new length of the string. + +----------------------------------------------------------------------------- + +struct ucnumber { + int numerator; + int denominator; +}; + +int ucnumber_lookup(unsigned long code, struct ucnumber *num) + + This function determines if the code is a number and fills in the `num' + field with the numerator and denominator. If the code happens to be a + single digit, the denominator field will be 1. + +#### +The original code would set numerator = denominator for regular digits. +However, the Readme also claimed to be compatible with John Cowan's uctype +library, but this behavior is both nonsensical and incompatible with the +Cowan library. As such, it has been fixed here as described above. + -- hyc@openldap.org +#### + + If the function returns 0, the code is not a number. Any other return + value means the code is a number. + +int ucdigit_lookup(unsigned long code, int *digit) + + This function determines if the code is a digit and fills in the `digit' + field with the digit value. + + If the function returns 0, the code is not a number. Any other return + value means the code is a number. + +struct ucnumber ucgetnumber(unsigned long code) + + This is a compatibility function with John Cowan's "uctype" package. It + uses ucnumber_lookup(). + +int ucgetdigit(unsigned long code) + + This is a compatibility function with John Cowan's "uctype" package. It + uses ucdigit_lookup(). + +----------------------------------------------------------------------------- + +unsigned long uctoupper(unsigned long code) + + This function returns the code unchanged if it is already upper case or has + no upper case equivalent. Otherwise the upper case equivalent is returned. + +----------------------------------------------------------------------------- + +unsigned long uctolower(unsigned long code) + + This function returns the code unchanged if it is already lower case or has + no lower case equivalent. Otherwise the lower case equivalent is returned. + +----------------------------------------------------------------------------- + +unsigned long uctotitle(unsigned long code) + + This function returns the code unchanged if it is already title case or has + no title case equivalent. Otherwise the title case equivalent is returned. + +----------------------------------------------------------------------------- + +int ucisalpha(unsigned long code) +int ucisalnum(unsigned long code) +int ucisdigit(unsigned long code) +int uciscntrl(unsigned long code) +int ucisspace(unsigned long code) +int ucisblank(unsigned long code) +int ucispunct(unsigned long code) +int ucisgraph(unsigned long code) +int ucisprint(unsigned long code) +int ucisxdigit(unsigned long code) + +int ucisupper(unsigned long code) +int ucislower(unsigned long code) +int ucistitle(unsigned long code) + + These functions (actually macros) determine if a character has these + properties. These behave in a fashion very similar to the venerable ctype + package. + +----------------------------------------------------------------------------- + +int ucisisocntrl(unsigned long code) + + Is the character a C0 control character (< 32) ? + +int ucisfmtcntrl(unsigned long code) + + Is the character a format control character? + +int ucissymbol(unsigned long code) + + Is the character a symbol? + +int ucisnumber(unsigned long code) + + Is the character a number or digit? + +int ucisnonspacing(unsigned long code) + + Is the character non-spacing? + +int ucisopenpunct(unsigned long code) + + Is the character an open/left punctuation (i.e. '[') + +int ucisclosepunct(unsigned long code) + + Is the character an close/right punctuation (i.e. ']') + +int ucisinitialpunct(unsigned long code) + + Is the character an initial punctuation (i.e. U+2018 LEFT SINGLE QUOTATION + MARK) + +int ucisfinalpunct(unsigned long code) + + Is the character a final punctuation (i.e. U+2019 RIGHT SINGLE QUOTATION + MARK) + +int uciscomposite(unsigned long code) + + Can the character be decomposed into a set of other characters? + +int ucisquote(unsigned long code) + + Is the character one of the many quotation marks? + +int ucissymmetric(unsigned long code) + + Is the character one that has an opposite form (i.e. <>) + +int ucismirroring(unsigned long code) + + Is the character mirroring (superset of symmetric)? + +int ucisnonbreaking(unsigned long code) + + Is the character non-breaking (i.e. non-breaking space)? + +int ucisrtl(unsigned long code) + + Does the character have strong right-to-left directionality (i.e. Arabic + letters)? + +int ucisltr(unsigned long code) + + Does the character have strong left-to-right directionality (i.e. Latin + letters)? + +int ucisstrong(unsigned long code) + + Does the character have strong directionality? + +int ucisweak(unsigned long code) + + Does the character have weak directionality (i.e. numbers)? + +int ucisneutral(unsigned long code) + + Does the character have neutral directionality (i.e. whitespace)? + +int ucisseparator(unsigned long code) + + Is the character a block or segment separator? + +int ucislsep(unsigned long code) + + Is the character a line separator? + +int ucispsep(unsigned long code) + + Is the character a paragraph separator? + +int ucismark(unsigned long code) + + Is the character a mark of some kind? + +int ucisnsmark(unsigned long code) + + Is the character a non-spacing mark? + +int ucisspmark(unsigned long code) + + Is the character a spacing mark? + +int ucismodif(unsigned long code) + + Is the character a modifier letter? + +int ucismodifsymbol(unsigned long code) + + Is the character a modifier symbol? + +int ucisletnum(unsigned long code) + + Is the character a number represented by a letter? + +int ucisconnect(unsigned long code) + + Is the character connecting punctuation? + +int ucisdash(unsigned long code) + + Is the character dash punctuation? + +int ucismath(unsigned long code) + + Is the character a math character? + +int uciscurrency(unsigned long code) + + Is the character a currency character? + +int ucisenclosing(unsigned long code) + + Is the character enclosing (i.e. enclosing box)? + +int ucisprivate(unsigned long code) + + Is the character from the Private Use Area? + +int ucissurrogate(unsigned long code) + + Is the character one of the surrogate codes? + +int ucisdefined(unsigned long code) + + Is the character defined (appeared in one of the data files)? + +int ucisundefined(unsigned long code) + + Is the character not defined (non-Unicode)? + +int ucishan(unsigned long code) + + Is the character a Han ideograph? + +int ucishangul(unsigned long code) + + Is the character a pre-composed Hangul syllable? diff --git a/libraries/liblunicode/ucdata/bidiapi.txt b/libraries/liblunicode/ucdata/bidiapi.txt new file mode 100644 index 0000000..dffd12e --- /dev/null +++ b/libraries/liblunicode/ucdata/bidiapi.txt @@ -0,0 +1,84 @@ +# +# $Id: bidiapi.txt,v 1.2 1999/11/19 15:24:29 mleisher Exp $ +# + + "Pretty Good Bidi Algorithm" API + +The PGBA (Pretty Good Bidi Algorithm) is an effective alternative to the +Unicode BiDi algorithm. It currently provides only implicit reordering and +does not yet support explicit reordering codes that the Unicode BiDi algorithm +supports. In addition to reordering, the PGBA includes cursor movement +support for both visual and logical navigation. + +----------------------------------------------------------------------------- + +#define UCPGBA_LTR 0 +#define UCPGBA_RTL 1 + + These macros appear in the `direction' field of the data structures. + +#define UCPGBA_CURSOR_VISUAL 0 +#define UCPGBA_CURSOR_LOGICAL 1 + + These macros are used to set the cursor movement for each reordered string. + +----------------------------------------------------------------------------- + +ucstring_t *ucstring_create(unsigned long *source, unsigned long start, + unsigned long end, int default_direction, + int cursor_motion) + + This function will create a reordered string by using the implicit + directionality of the characters in the specified substring. + + The `default_direction' parameter should be one of UCPGBA_LTR or UCPGBA_RTL + and is used only in cases where a string contains no characters with strong + directionality. + + The `cursor_motion' parameter should be one of UCPGBA_CURSOR_VISUAL or + UCPGBA_CURSOR_LOGICAL, and is used to specify the initial cursor motion + behavior. This behavior can be switched at any time using + ustring_set_cursor_motion(). + +----------------------------------------------------------------------------- + +void ucstring_free(ucstring_t *string) + + This function will deallocate the memory used by the string, incuding the + string itself. + +----------------------------------------------------------------------------- + +void ucstring_cursor_info(ustring_t *string, int *direction, + unsigned long *position) + + This function will return the text position of the internal cursor and the + directionality of the text at that position. The position returned is the + original text position of the character. + +----------------------------------------------------------------------------- + +int ucstring_set_cursor_motion(ucstring_t *string, int cursor_motion) + + This function will change the cursor motion type and return the previous + cursor motion type. + +----------------------------------------------------------------------------- + +int ucstring_cursor_right(ucstring_t *string, int count) + + This function will move the internal cursor to the right according to the + type of cursor motion set for the string. + + If no cursor motion is performed, it returns 0. Otherwise it will return a + 1. + +----------------------------------------------------------------------------- + +int ucstring_cursor_left(ucstring_t *string, int count) + + This function will move the internal cursor to the left according to the + type of cursor motion set for the string. + + If no cursor motion is performed, it returns 0. Otherwise it will return a + 1. diff --git a/libraries/liblunicode/ucdata/format.txt b/libraries/liblunicode/ucdata/format.txt new file mode 100644 index 0000000..e285b39 --- /dev/null +++ b/libraries/liblunicode/ucdata/format.txt @@ -0,0 +1,267 @@ +# +# $Id: format.txt,v 1.2 2001/01/02 18:46:20 mleisher Exp $ +# + +CHARACTER DATA +============== + +This package generates some data files that contain character properties useful +for text processing. + +CHARACTER PROPERTIES +==================== + +The first data file is called "ctype.dat" and contains a compressed form of +the character properties found in the Unicode Character Database (UCDB). +Additional properties can be specified in limited UCDB format in another file +to avoid modifying the original UCDB. + +The following is a property name and code table to be used with the character +data: + +NAME CODE DESCRIPTION +--------------------- +Mn 0 Mark, Non-Spacing +Mc 1 Mark, Spacing Combining +Me 2 Mark, Enclosing +Nd 3 Number, Decimal Digit +Nl 4 Number, Letter +No 5 Number, Other +Zs 6 Separator, Space +Zl 7 Separator, Line +Zp 8 Separator, Paragraph +Cc 9 Other, Control +Cf 10 Other, Format +Cs 11 Other, Surrogate +Co 12 Other, Private Use +Cn 13 Other, Not Assigned +Lu 14 Letter, Uppercase +Ll 15 Letter, Lowercase +Lt 16 Letter, Titlecase +Lm 17 Letter, Modifier +Lo 18 Letter, Other +Pc 19 Punctuation, Connector +Pd 20 Punctuation, Dash +Ps 21 Punctuation, Open +Pe 22 Punctuation, Close +Po 23 Punctuation, Other +Sm 24 Symbol, Math +Sc 25 Symbol, Currency +Sk 26 Symbol, Modifier +So 27 Symbol, Other +L 28 Left-To-Right +R 29 Right-To-Left +EN 30 European Number +ES 31 European Number Separator +ET 32 European Number Terminator +AN 33 Arabic Number +CS 34 Common Number Separator +B 35 Block Separator +S 36 Segment Separator +WS 37 Whitespace +ON 38 Other Neutrals +Pi 47 Punctuation, Initial +Pf 48 Punctuation, Final +# +# Implementation specific properties. +# +Cm 39 Composite +Nb 40 Non-Breaking +Sy 41 Symmetric (characters which are part of open/close pairs) +Hd 42 Hex Digit +Qm 43 Quote Mark +Mr 44 Mirroring +Ss 45 Space, Other (controls viewed as spaces in ctype isspace()) +Cp 46 Defined character + +The actual binary data is formatted as follows: + + Assumptions: unsigned short is at least 16-bits in size and unsigned long + is at least 32-bits in size. + + unsigned short ByteOrderMark + unsigned short OffsetArraySize + unsigned long Bytes + unsigned short Offsets[OffsetArraySize + 1] + unsigned long Ranges[N], N = value of Offsets[OffsetArraySize] + + The Bytes field provides the total byte count used for the Offsets[] and + Ranges[] arrays. The Offsets[] array is aligned on a 4-byte boundary and + there is always one extra node on the end to hold the final index of the + Ranges[] array. The Ranges[] array contains pairs of 4-byte values + representing a range of Unicode characters. The pairs are arranged in + increasing order by the first character code in the range. + + Determining if a particular character is in the property list requires a + simple binary search to determine if a character is in any of the ranges + for the property. + + If the ByteOrderMark is equal to 0xFFFE, then the data was generated on a + machine with a different endian order and the values must be byte-swapped. + + To swap a 16-bit value: + c = (c >> 8) | ((c & 0xff) << 8) + + To swap a 32-bit value: + c = ((c & 0xff) << 24) | (((c >> 8) & 0xff) << 16) | + (((c >> 16) & 0xff) << 8) | (c >> 24) + +CASE MAPPINGS +============= + +The next data file is called "case.dat" and contains three case mapping tables +in the following order: upper, lower, and title case. Each table is in +increasing order by character code and each mapping contains 3 unsigned longs +which represent the possible mappings. + +The format for the binary form of these tables is: + + unsigned short ByteOrderMark + unsigned short NumMappingNodes, count of all mapping nodes + unsigned short CaseTableSizes[2], upper and lower mapping node counts + unsigned long CaseTables[NumMappingNodes] + + The starting indexes of the case tables are calculated as following: + + UpperIndex = 0; + LowerIndex = CaseTableSizes[0] * 3; + TitleIndex = LowerIndex + CaseTableSizes[1] * 3; + + The order of the fields for the three tables are: + + Upper case + ---------- + unsigned long upper; + unsigned long lower; + unsigned long title; + + Lower case + ---------- + unsigned long lower; + unsigned long upper; + unsigned long title; + + Title case + ---------- + unsigned long title; + unsigned long upper; + unsigned long lower; + + If the ByteOrderMark is equal to 0xFFFE, endian swapping is required in the + same way as described in the CHARACTER PROPERTIES section. + + Because the tables are in increasing order by character code, locating a + mapping requires a simple binary search on one of the 3 codes that make up + each node. + + It is important to note that there can only be 65536 mapping nodes which + divided into 3 portions allows 21845 nodes for each case mapping table. The + distribution of mappings may be more or less than 21845 per table, but only + 65536 are allowed. + +COMPOSITIONS +============ + +This data file is called "comp.dat" and contains data that tracks character +pairs that have a single Unicode value representing the combination of the two +characters. + +The format for the binary form of this table is: + + unsigned short ByteOrderMark + unsigned short NumCompositionNodes, count of composition nodes + unsigned long Bytes, total number of bytes used for composition nodes + unsigned long CompositionNodes[NumCompositionNodes * 4] + + If the ByteOrderMark is equal to 0xFFFE, endian swapping is required in the + same way as described in the CHARACTER PROPERTIES section. + + The CompositionNodes[] array consists of groups of 4 unsigned longs. The + first of these is the character code representing the combination of two + other character codes, the second records the number of character codes that + make up the composition (not currently used), and the last two are the pair + of character codes whose combination is represented by the character code in + the first field. + +DECOMPOSITIONS +============== + +The next data file is called "decomp.dat" and contains the decomposition data +for all characters with decompositions containing more than one character and +are *not* compatibility decompositions. Compatibility decompositions are +signaled in the UCDB format by the use of the <compat> tag in the +decomposition field. Each list of character codes represents a full +decomposition of a composite character. The nodes are arranged in increasing +order by character code. + +The format for the binary form of this table is: + + unsigned short ByteOrderMark + unsigned short NumDecompNodes, count of all decomposition nodes + unsigned long Bytes + unsigned long DecompNodes[(NumDecompNodes * 2) + 1] + unsigned long Decomp[N], N = sum of all counts in DecompNodes[] + + If the ByteOrderMark is equal to 0xFFFE, endian swapping is required in the + same way as described in the CHARACTER PROPERTIES section. + + The DecompNodes[] array consists of pairs of unsigned longs, the first of + which is the character code and the second is the initial index of the list + of character codes representing the decomposition. + + Locating the decomposition of a composite character requires a binary search + for a character code in the DecompNodes[] array and using its index to + locate the start of the decomposition. The length of the decomposition list + is the index in the following element in DecompNode[] minus the current + index. + +COMBINING CLASSES +================= + +The fourth data file is called "cmbcl.dat" and contains the characters with +non-zero combining classes. + +The format for the binary form of this table is: + + unsigned short ByteOrderMark + unsigned short NumCCLNodes + unsigned long Bytes + unsigned long CCLNodes[NumCCLNodes * 3] + + If the ByteOrderMark is equal to 0xFFFE, endian swapping is required in the + same way as described in the CHARACTER PROPERTIES section. + + The CCLNodes[] array consists of groups of three unsigned longs. The first + and second are the beginning and ending of a range and the third is the + combining class of that range. + + If a character is not found in this table, then the combining class is + assumed to be 0. + + It is important to note that only 65536 distinct ranges plus combining class + can be specified because the NumCCLNodes is usually a 16-bit number. + +NUMBER TABLE +============ + +The final data file is called "num.dat" and contains the characters that have +a numeric value associated with them. + +The format for the binary form of the table is: + + unsigned short ByteOrderMark + unsigned short NumNumberNodes + unsigned long Bytes + unsigned long NumberNodes[NumNumberNodes] + unsigned short ValueNodes[(Bytes - (NumNumberNodes * sizeof(unsigned long))) + / sizeof(short)] + + If the ByteOrderMark is equal to 0xFFFE, endian swapping is required in the + same way as described in the CHARACTER PROPERTIES section. + + The NumberNodes array contains pairs of values, the first of which is the + character code and the second an index into the ValueNodes array. The + ValueNodes array contains pairs of integers which represent the numerator + and denominator of the numeric value of the character. If the character + happens to map to an integer, both the values in ValueNodes will be the + same. diff --git a/libraries/liblunicode/ucdata/ucdata.c b/libraries/liblunicode/ucdata/ucdata.c new file mode 100644 index 0000000..abe70c2 --- /dev/null +++ b/libraries/liblunicode/ucdata/ucdata.c @@ -0,0 +1,1501 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Copyright 2001 Computing Research Labs, New Mexico State University + * + * 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 COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY 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. + */ +/* $Id: ucdata.c,v 1.4 2001/01/02 18:46:20 mleisher Exp $" */ + +#include "portable.h" +#include "ldap_config.h" + +#include <stdio.h> +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/unistd.h> + +#include <ac/bytes.h> + +#include "lber_pvt.h" +#include "ucdata.h" + +#ifndef HARDCODE_DATA +#define HARDCODE_DATA 1 +#endif + +#if HARDCODE_DATA +#include "uctable.h" +#endif + +/************************************************************************** + * + * Miscellaneous types, data, and support functions. + * + **************************************************************************/ + +typedef struct { + ac_uint2 bom; + ac_uint2 cnt; + union { + ac_uint4 bytes; + ac_uint2 len[2]; + } size; +} _ucheader_t; + +/* + * A simple array of 32-bit masks for lookup. + */ +static ac_uint4 masks32[32] = { + 0x00000001UL, 0x00000002UL, 0x00000004UL, 0x00000008UL, + 0x00000010UL, 0x00000020UL, 0x00000040UL, 0x00000080UL, + 0x00000100UL, 0x00000200UL, 0x00000400UL, 0x00000800UL, + 0x00001000UL, 0x00002000UL, 0x00004000UL, 0x00008000UL, + 0x00010000UL, 0x00020000UL, 0x00040000UL, 0x00080000UL, + 0x00100000UL, 0x00200000UL, 0x00400000UL, 0x00800000UL, + 0x01000000UL, 0x02000000UL, 0x04000000UL, 0x08000000UL, + 0x10000000UL, 0x20000000UL, 0x40000000UL, 0x80000000UL +}; + +#define endian_short(cc) (((cc) >> 8) | (((cc) & 0xff) << 8)) +#define endian_long(cc) ((((cc) & 0xff) << 24)|((((cc) >> 8) & 0xff) << 16)|\ + ((((cc) >> 16) & 0xff) << 8)|((cc) >> 24)) + +#if !HARDCODE_DATA +static FILE * +_ucopenfile(char *paths, char *filename, char *mode) +{ + FILE *f; + char *fp, *dp, *pp, path[BUFSIZ]; + + if (filename == 0 || *filename == 0) + return 0; + + dp = paths; + while (dp && *dp) { + pp = path; + while (*dp && *dp != ':') + *pp++ = *dp++; + *pp++ = *LDAP_DIRSEP; + + fp = filename; + while (*fp) + *pp++ = *fp++; + *pp = 0; + + if ((f = fopen(path, mode)) != 0) + return f; + + if (*dp == ':') + dp++; + } + + return 0; +} +#endif + +/************************************************************************** + * + * Support for the character properties. + * + **************************************************************************/ + +#if !HARDCODE_DATA + +static ac_uint4 _ucprop_size; +static ac_uint2 *_ucprop_offsets; +static ac_uint4 *_ucprop_ranges; + +/* + * Return -1 on error, 0 if okay + */ +static int +_ucprop_load(char *paths, int reload) +{ + FILE *in; + ac_uint4 size, i; + _ucheader_t hdr; + + if (_ucprop_size > 0) { + if (!reload) + /* + * The character properties have already been loaded. + */ + return 0; + + /* + * Unload the current character property data in preparation for + * loading a new copy. Only the first array has to be deallocated + * because all the memory for the arrays is allocated as a single + * block. + */ + free((char *) _ucprop_offsets); + _ucprop_size = 0; + } + + if ((in = _ucopenfile(paths, "ctype.dat", "rb")) == 0) + return -1; + + /* + * Load the header. + */ + fread((char *) &hdr, sizeof(_ucheader_t), 1, in); + + if (hdr.bom == 0xfffe) { + hdr.cnt = endian_short(hdr.cnt); + hdr.size.bytes = endian_long(hdr.size.bytes); + } + + if ((_ucprop_size = hdr.cnt) == 0) { + fclose(in); + return -1; + } + + /* + * Allocate all the storage needed for the lookup table. + */ + _ucprop_offsets = (ac_uint2 *) malloc(hdr.size.bytes); + + /* + * Calculate the offset into the storage for the ranges. The offsets + * array is on a 4-byte boundary and one larger than the value provided in + * the header count field. This means the offset to the ranges must be + * calculated after aligning the count to a 4-byte boundary. + */ + if ((size = ((hdr.cnt + 1) * sizeof(ac_uint2))) & 3) + size += 4 - (size & 3); + size >>= 1; + _ucprop_ranges = (ac_uint4 *) (_ucprop_offsets + size); + + /* + * Load the offset array. + */ + fread((char *) _ucprop_offsets, sizeof(ac_uint2), size, in); + + /* + * Do an endian swap if necessary. Don't forget there is an extra node on + * the end with the final index. + */ + if (hdr.bom == 0xfffe) { + for (i = 0; i <= _ucprop_size; i++) + _ucprop_offsets[i] = endian_short(_ucprop_offsets[i]); + } + + /* + * Load the ranges. The number of elements is in the last array position + * of the offsets. + */ + fread((char *) _ucprop_ranges, sizeof(ac_uint4), + _ucprop_offsets[_ucprop_size], in); + + fclose(in); + + /* + * Do an endian swap if necessary. + */ + if (hdr.bom == 0xfffe) { + for (i = 0; i < _ucprop_offsets[_ucprop_size]; i++) + _ucprop_ranges[i] = endian_long(_ucprop_ranges[i]); + } + return 0; +} + +static void +_ucprop_unload(void) +{ + if (_ucprop_size == 0) + return; + + /* + * Only need to free the offsets because the memory is allocated as a + * single block. + */ + free((char *) _ucprop_offsets); + _ucprop_size = 0; +} +#endif + +static int +_ucprop_lookup(ac_uint4 code, ac_uint4 n) +{ + long l, r, m; + + if (_ucprop_size == 0) + return 0; + + /* + * There is an extra node on the end of the offsets to allow this routine + * to work right. If the index is 0xffff, then there are no nodes for the + * property. + */ + if ((l = _ucprop_offsets[n]) == 0xffff) + return 0; + + /* + * Locate the next offset that is not 0xffff. The sentinel at the end of + * the array is the max index value. + */ + for (m = 1; + n + m < _ucprop_size && _ucprop_offsets[n + m] == 0xffff; m++) ; + + r = _ucprop_offsets[n + m] - 1; + + while (l <= r) { + /* + * Determine a "mid" point and adjust to make sure the mid point is at + * the beginning of a range pair. + */ + m = (l + r) >> 1; + m -= (m & 1); + if (code > _ucprop_ranges[m + 1]) + l = m + 2; + else if (code < _ucprop_ranges[m]) + r = m - 2; + else if (code >= _ucprop_ranges[m] && code <= _ucprop_ranges[m + 1]) + return 1; + } + return 0; +} + +int +ucisprop(ac_uint4 code, ac_uint4 mask1, ac_uint4 mask2) +{ + ac_uint4 i; + + if (mask1 == 0 && mask2 == 0) + return 0; + + for (i = 0; mask1 && i < 32; i++) { + if ((mask1 & masks32[i]) && _ucprop_lookup(code, i)) + return 1; + } + + for (i = 32; mask2 && i < _ucprop_size; i++) { + if ((mask2 & masks32[i & 31]) && _ucprop_lookup(code, i)) + return 1; + } + + return 0; +} + +/************************************************************************** + * + * Support for case mapping. + * + **************************************************************************/ + +#if !HARDCODE_DATA + +/* These record the number of slots in the map. + * There are 3 words per slot. + */ +static ac_uint4 _uccase_size; +static ac_uint2 _uccase_len[2]; +static ac_uint4 *_uccase_map; + +/* + * Return -1 on error, 0 if okay + */ +static int +_uccase_load(char *paths, int reload) +{ + FILE *in; + ac_uint4 i; + _ucheader_t hdr; + + if (_uccase_size > 0) { + if (!reload) + /* + * The case mappings have already been loaded. + */ + return 0; + + free((char *) _uccase_map); + _uccase_size = 0; + } + + if ((in = _ucopenfile(paths, "case.dat", "rb")) == 0) + return -1; + + /* + * Load the header. + */ + fread((char *) &hdr, sizeof(_ucheader_t), 1, in); + + if (hdr.bom == 0xfffe) { + hdr.cnt = endian_short(hdr.cnt); + hdr.size.len[0] = endian_short(hdr.size.len[0]); + hdr.size.len[1] = endian_short(hdr.size.len[1]); + } + + /* + * Set the node count and lengths of the upper and lower case mapping + * tables. + */ + _uccase_size = hdr.cnt; + _uccase_len[0] = hdr.size.len[0]; + _uccase_len[1] = hdr.size.len[1]; + + _uccase_map = (ac_uint4 *) + malloc(_uccase_size * 3 * sizeof(ac_uint4)); + + /* + * Load the case mapping table. + */ + fread((char *) _uccase_map, sizeof(ac_uint4), _uccase_size * 3, in); + + /* + * Do an endian swap if necessary. + */ + if (hdr.bom == 0xfffe) { + for (i = 0; i < _uccase_size * 3; i++) + _uccase_map[i] = endian_long(_uccase_map[i]); + } + fclose(in); + return 0; +} + +static void +_uccase_unload(void) +{ + if (_uccase_size == 0) + return; + + free((char *) _uccase_map); + _uccase_size = 0; +} +#endif + +static ac_uint4 +_uccase_lookup(ac_uint4 code, long l, long r, int field) +{ + long m; + const ac_uint4 *tmp; + + /* + * Do the binary search. + */ + while (l <= r) { + /* + * Determine a "mid" point and adjust to make sure the mid point is at + * the beginning of a case mapping triple. + */ + m = (l + r) >> 1; + tmp = &_uccase_map[m*3]; + if (code > *tmp) + l = m + 1; + else if (code < *tmp) + r = m - 1; + else if (code == *tmp) + return tmp[field]; + } + + return code; +} + +ac_uint4 +uctoupper(ac_uint4 code) +{ + int field; + long l, r; + + if (ucisupper(code)) + return code; + + if (ucislower(code)) { + /* + * The character is lower case. + */ + field = 2; + l = _uccase_len[0]; + r = (l + _uccase_len[1]) - 1; + } else { + /* + * The character is title case. + */ + field = 1; + l = _uccase_len[0] + _uccase_len[1]; + r = _uccase_size - 1; + } + return _uccase_lookup(code, l, r, field); +} + +ac_uint4 +uctolower(ac_uint4 code) +{ + int field; + long l, r; + + if (ucislower(code)) + return code; + + if (ucisupper(code)) { + /* + * The character is upper case. + */ + field = 1; + l = 0; + r = _uccase_len[0] - 1; + } else { + /* + * The character is title case. + */ + field = 2; + l = _uccase_len[0] + _uccase_len[1]; + r = _uccase_size - 1; + } + return _uccase_lookup(code, l, r, field); +} + +ac_uint4 +uctotitle(ac_uint4 code) +{ + int field; + long l, r; + + if (ucistitle(code)) + return code; + + /* + * The offset will always be the same for converting to title case. + */ + field = 2; + + if (ucisupper(code)) { + /* + * The character is upper case. + */ + l = 0; + r = _uccase_len[0] - 1; + } else { + /* + * The character is lower case. + */ + l = _uccase_len[0]; + r = (l + _uccase_len[1]) - 1; + } + return _uccase_lookup(code, l, r, field); +} + +/************************************************************************** + * + * Support for compositions. + * + **************************************************************************/ + +#if !HARDCODE_DATA + +static ac_uint4 _uccomp_size; +static ac_uint4 *_uccomp_data; + +/* + * Return -1 on error, 0 if okay + */ +static int +_uccomp_load(char *paths, int reload) +{ + FILE *in; + ac_uint4 size, i; + _ucheader_t hdr; + + if (_uccomp_size > 0) { + if (!reload) + /* + * The compositions have already been loaded. + */ + return 0; + + free((char *) _uccomp_data); + _uccomp_size = 0; + } + + if ((in = _ucopenfile(paths, "comp.dat", "rb")) == 0) + return -1; + + /* + * Load the header. + */ + fread((char *) &hdr, sizeof(_ucheader_t), 1, in); + + if (hdr.bom == 0xfffe) { + hdr.cnt = endian_short(hdr.cnt); + hdr.size.bytes = endian_long(hdr.size.bytes); + } + + _uccomp_size = hdr.cnt; + _uccomp_data = (ac_uint4 *) malloc(hdr.size.bytes); + + /* + * Read the composition data in. + */ + size = hdr.size.bytes / sizeof(ac_uint4); + fread((char *) _uccomp_data, sizeof(ac_uint4), size, in); + + /* + * Do an endian swap if necessary. + */ + if (hdr.bom == 0xfffe) { + for (i = 0; i < size; i++) + _uccomp_data[i] = endian_long(_uccomp_data[i]); + } + + /* + * Assume that the data is ordered on count, so that all compositions + * of length 2 come first. Only handling length 2 for now. + */ + for (i = 1; i < size; i += 4) + if (_uccomp_data[i] != 2) + break; + _uccomp_size = i - 1; + + fclose(in); + return 0; +} + +static void +_uccomp_unload(void) +{ + if (_uccomp_size == 0) + return; + + free((char *) _uccomp_data); + _uccomp_size = 0; +} +#endif + +int +uccomp(ac_uint4 node1, ac_uint4 node2, ac_uint4 *comp) +{ + int l, r, m; + + l = 0; + r = _uccomp_size - 1; + + while (l <= r) { + m = ((r + l) >> 1); + m -= m & 3; + if (node1 > _uccomp_data[m+2]) + l = m + 4; + else if (node1 < _uccomp_data[m+2]) + r = m - 4; + else if (node2 > _uccomp_data[m+3]) + l = m + 4; + else if (node2 < _uccomp_data[m+3]) + r = m - 4; + else { + *comp = _uccomp_data[m]; + return 1; + } + } + return 0; +} + +int +uccomp_hangul(ac_uint4 *str, int len) +{ + const int SBase = 0xAC00, LBase = 0x1100, + VBase = 0x1161, TBase = 0x11A7, + LCount = 19, VCount = 21, TCount = 28, + NCount = VCount * TCount, /* 588 */ + SCount = LCount * NCount; /* 11172 */ + + int i, rlen; + ac_uint4 ch, last, lindex, sindex; + + last = str[0]; + rlen = 1; + for ( i = 1; i < len; i++ ) { + ch = str[i]; + + /* check if two current characters are L and V */ + lindex = last - LBase; + if (lindex < (ac_uint4) LCount) { + ac_uint4 vindex = ch - VBase; + if (vindex < (ac_uint4) VCount) { + /* make syllable of form LV */ + last = SBase + (lindex * VCount + vindex) * TCount; + str[rlen-1] = last; /* reset last */ + continue; + } + } + + /* check if two current characters are LV and T */ + sindex = last - SBase; + if (sindex < (ac_uint4) SCount + && (sindex % TCount) == 0) + { + ac_uint4 tindex = ch - TBase; + if (tindex <= (ac_uint4) TCount) { + /* make syllable of form LVT */ + last += tindex; + str[rlen-1] = last; /* reset last */ + continue; + } + } + + /* if neither case was true, just add the character */ + last = ch; + str[rlen] = ch; + rlen++; + } + return rlen; +} + +int +uccanoncomp(ac_uint4 *str, int len) +{ + int i, stpos, copos; + ac_uint4 cl, prevcl, st, ch, co; + + st = str[0]; + stpos = 0; + copos = 1; + prevcl = uccombining_class(st) == 0 ? 0 : 256; + + for (i = 1; i < len; i++) { + ch = str[i]; + cl = uccombining_class(ch); + if (uccomp(st, ch, &co) && (prevcl < cl || prevcl == 0)) + st = str[stpos] = co; + else { + if (cl == 0) { + stpos = copos; + st = ch; + } + prevcl = cl; + str[copos++] = ch; + } + } + + return uccomp_hangul(str, copos); +} + +/************************************************************************** + * + * Support for decompositions. + * + **************************************************************************/ + +#if !HARDCODE_DATA + +static ac_uint4 _ucdcmp_size; +static ac_uint4 *_ucdcmp_nodes; +static ac_uint4 *_ucdcmp_decomp; + +static ac_uint4 _uckdcmp_size; +static ac_uint4 *_uckdcmp_nodes; +static ac_uint4 *_uckdcmp_decomp; + +/* + * Return -1 on error, 0 if okay + */ +static int +_ucdcmp_load(char *paths, int reload) +{ + FILE *in; + ac_uint4 size, i; + _ucheader_t hdr; + + if (_ucdcmp_size > 0) { + if (!reload) + /* + * The decompositions have already been loaded. + */ + return 0; + + free((char *) _ucdcmp_nodes); + _ucdcmp_size = 0; + } + + if ((in = _ucopenfile(paths, "decomp.dat", "rb")) == 0) + return -1; + + /* + * Load the header. + */ + fread((char *) &hdr, sizeof(_ucheader_t), 1, in); + + if (hdr.bom == 0xfffe) { + hdr.cnt = endian_short(hdr.cnt); + hdr.size.bytes = endian_long(hdr.size.bytes); + } + + _ucdcmp_size = hdr.cnt << 1; + _ucdcmp_nodes = (ac_uint4 *) malloc(hdr.size.bytes); + _ucdcmp_decomp = _ucdcmp_nodes + (_ucdcmp_size + 1); + + /* + * Read the decomposition data in. + */ + size = hdr.size.bytes / sizeof(ac_uint4); + fread((char *) _ucdcmp_nodes, sizeof(ac_uint4), size, in); + + /* + * Do an endian swap if necessary. + */ + if (hdr.bom == 0xfffe) { + for (i = 0; i < size; i++) + _ucdcmp_nodes[i] = endian_long(_ucdcmp_nodes[i]); + } + fclose(in); + return 0; +} + +/* + * Return -1 on error, 0 if okay + */ +static int +_uckdcmp_load(char *paths, int reload) +{ + FILE *in; + ac_uint4 size, i; + _ucheader_t hdr; + + if (_uckdcmp_size > 0) { + if (!reload) + /* + * The decompositions have already been loaded. + */ + return 0; + + free((char *) _uckdcmp_nodes); + _uckdcmp_size = 0; + } + + if ((in = _ucopenfile(paths, "kdecomp.dat", "rb")) == 0) + return -1; + + /* + * Load the header. + */ + fread((char *) &hdr, sizeof(_ucheader_t), 1, in); + + if (hdr.bom == 0xfffe) { + hdr.cnt = endian_short(hdr.cnt); + hdr.size.bytes = endian_long(hdr.size.bytes); + } + + _uckdcmp_size = hdr.cnt << 1; + _uckdcmp_nodes = (ac_uint4 *) malloc(hdr.size.bytes); + _uckdcmp_decomp = _uckdcmp_nodes + (_uckdcmp_size + 1); + + /* + * Read the decomposition data in. + */ + size = hdr.size.bytes / sizeof(ac_uint4); + fread((char *) _uckdcmp_nodes, sizeof(ac_uint4), size, in); + + /* + * Do an endian swap if necessary. + */ + if (hdr.bom == 0xfffe) { + for (i = 0; i < size; i++) + _uckdcmp_nodes[i] = endian_long(_uckdcmp_nodes[i]); + } + fclose(in); + return 0; +} + +static void +_ucdcmp_unload(void) +{ + if (_ucdcmp_size == 0) + return; + + /* + * Only need to free the offsets because the memory is allocated as a + * single block. + */ + free((char *) _ucdcmp_nodes); + _ucdcmp_size = 0; +} + +static void +_uckdcmp_unload(void) +{ + if (_uckdcmp_size == 0) + return; + + /* + * Only need to free the offsets because the memory is allocated as a + * single block. + */ + free((char *) _uckdcmp_nodes); + _uckdcmp_size = 0; +} +#endif + +int +ucdecomp(ac_uint4 code, ac_uint4 *num, ac_uint4 **decomp) +{ + long l, r, m; + + if (code < _ucdcmp_nodes[0]) { + return 0; + } + + l = 0; + r = _ucdcmp_nodes[_ucdcmp_size] - 1; + + while (l <= r) { + /* + * Determine a "mid" point and adjust to make sure the mid point is at + * the beginning of a code+offset pair. + */ + m = (l + r) >> 1; + m -= (m & 1); + if (code > _ucdcmp_nodes[m]) + l = m + 2; + else if (code < _ucdcmp_nodes[m]) + r = m - 2; + else if (code == _ucdcmp_nodes[m]) { + *num = _ucdcmp_nodes[m + 3] - _ucdcmp_nodes[m + 1]; + *decomp = (ac_uint4*)&_ucdcmp_decomp[_ucdcmp_nodes[m + 1]]; + return 1; + } + } + return 0; +} + +int +uckdecomp(ac_uint4 code, ac_uint4 *num, ac_uint4 **decomp) +{ + long l, r, m; + + if (code < _uckdcmp_nodes[0]) { + return 0; + } + + l = 0; + r = _uckdcmp_nodes[_uckdcmp_size] - 1; + + while (l <= r) { + /* + * Determine a "mid" point and adjust to make sure the mid point is at + * the beginning of a code+offset pair. + */ + m = (l + r) >> 1; + m -= (m & 1); + if (code > _uckdcmp_nodes[m]) + l = m + 2; + else if (code < _uckdcmp_nodes[m]) + r = m - 2; + else if (code == _uckdcmp_nodes[m]) { + *num = _uckdcmp_nodes[m + 3] - _uckdcmp_nodes[m + 1]; + *decomp = (ac_uint4*)&_uckdcmp_decomp[_uckdcmp_nodes[m + 1]]; + return 1; + } + } + return 0; +} + +int +ucdecomp_hangul(ac_uint4 code, ac_uint4 *num, ac_uint4 decomp[]) +{ + if (!ucishangul(code)) + return 0; + + code -= 0xac00; + decomp[0] = 0x1100 + (ac_uint4) (code / 588); + decomp[1] = 0x1161 + (ac_uint4) ((code % 588) / 28); + decomp[2] = 0x11a7 + (ac_uint4) (code % 28); + *num = (decomp[2] != 0x11a7) ? 3 : 2; + + return 1; +} + +/* mode == 0 for canonical, mode == 1 for compatibility */ +static int +uccanoncompatdecomp(const ac_uint4 *in, int inlen, + ac_uint4 **out, int *outlen, short mode, void *ctx) +{ + int l, size; + unsigned i, j, k; + ac_uint4 num, class, *decomp, hangdecomp[3]; + + size = inlen * 2; + *out = (ac_uint4 *) ber_memalloc_x(size * sizeof(**out), ctx); + if (*out == NULL) + return *outlen = -1; + + i = 0; + for (j = 0; j < (unsigned) inlen; j++) { + if (mode ? uckdecomp(in[j], &num, &decomp) : ucdecomp(in[j], &num, &decomp)) { + if ( size - i < num) { + size = inlen + i - j + num - 1; + *out = (ac_uint4 *) ber_memrealloc_x(*out, size * sizeof(**out), ctx ); + if (*out == NULL) + return *outlen = -1; + } + for (k = 0; k < num; k++) { + class = uccombining_class(decomp[k]); + if (class == 0) { + (*out)[i] = decomp[k]; + } else { + for (l = i; l > 0; l--) + if (class >= uccombining_class((*out)[l-1])) + break; + AC_MEMCPY(*out + l + 1, *out + l, (i - l) * sizeof(**out)); + (*out)[l] = decomp[k]; + } + i++; + } + } else if (ucdecomp_hangul(in[j], &num, hangdecomp)) { + if (size - i < num) { + size = inlen + i - j + num - 1; + *out = (ac_uint4 *) ber_memrealloc_x(*out, size * sizeof(**out), ctx); + if (*out == NULL) + return *outlen = -1; + } + for (k = 0; k < num; k++) { + (*out)[i] = hangdecomp[k]; + i++; + } + } else { + if (size - i < 1) { + size = inlen + i - j; + *out = (ac_uint4 *) ber_memrealloc_x(*out, size * sizeof(**out), ctx); + if (*out == NULL) + return *outlen = -1; + } + class = uccombining_class(in[j]); + if (class == 0) { + (*out)[i] = in[j]; + } else { + for (l = i; l > 0; l--) + if (class >= uccombining_class((*out)[l-1])) + break; + AC_MEMCPY(*out + l + 1, *out + l, (i - l) * sizeof(**out)); + (*out)[l] = in[j]; + } + i++; + } + } + return *outlen = i; +} + +int +uccanondecomp(const ac_uint4 *in, int inlen, + ac_uint4 **out, int *outlen, void *ctx) +{ + return uccanoncompatdecomp(in, inlen, out, outlen, 0, ctx); +} + +int +uccompatdecomp(const ac_uint4 *in, int inlen, + ac_uint4 **out, int *outlen, void *ctx) +{ + return uccanoncompatdecomp(in, inlen, out, outlen, 1, ctx); +} + +/************************************************************************** + * + * Support for combining classes. + * + **************************************************************************/ + +#if !HARDCODE_DATA +static ac_uint4 _uccmcl_size; +static ac_uint4 *_uccmcl_nodes; + +/* + * Return -1 on error, 0 if okay + */ +static int +_uccmcl_load(char *paths, int reload) +{ + FILE *in; + ac_uint4 i; + _ucheader_t hdr; + + if (_uccmcl_size > 0) { + if (!reload) + /* + * The combining classes have already been loaded. + */ + return 0; + + free((char *) _uccmcl_nodes); + _uccmcl_size = 0; + } + + if ((in = _ucopenfile(paths, "cmbcl.dat", "rb")) == 0) + return -1; + + /* + * Load the header. + */ + fread((char *) &hdr, sizeof(_ucheader_t), 1, in); + + if (hdr.bom == 0xfffe) { + hdr.cnt = endian_short(hdr.cnt); + hdr.size.bytes = endian_long(hdr.size.bytes); + } + + _uccmcl_size = hdr.cnt * 3; + _uccmcl_nodes = (ac_uint4 *) malloc(hdr.size.bytes); + + /* + * Read the combining classes in. + */ + fread((char *) _uccmcl_nodes, sizeof(ac_uint4), _uccmcl_size, in); + + /* + * Do an endian swap if necessary. + */ + if (hdr.bom == 0xfffe) { + for (i = 0; i < _uccmcl_size; i++) + _uccmcl_nodes[i] = endian_long(_uccmcl_nodes[i]); + } + fclose(in); + return 0; +} + +static void +_uccmcl_unload(void) +{ + if (_uccmcl_size == 0) + return; + + free((char *) _uccmcl_nodes); + _uccmcl_size = 0; +} +#endif + +ac_uint4 +uccombining_class(ac_uint4 code) +{ + long l, r, m; + + l = 0; + r = _uccmcl_size - 1; + + while (l <= r) { + m = (l + r) >> 1; + m -= (m % 3); + if (code > _uccmcl_nodes[m + 1]) + l = m + 3; + else if (code < _uccmcl_nodes[m]) + r = m - 3; + else if (code >= _uccmcl_nodes[m] && code <= _uccmcl_nodes[m + 1]) + return _uccmcl_nodes[m + 2]; + } + return 0; +} + +/************************************************************************** + * + * Support for numeric values. + * + **************************************************************************/ + +#if !HARDCODE_DATA +static ac_uint4 *_ucnum_nodes; +static ac_uint4 _ucnum_size; +static short *_ucnum_vals; + +/* + * Return -1 on error, 0 if okay + */ +static int +_ucnumb_load(char *paths, int reload) +{ + FILE *in; + ac_uint4 size, i; + _ucheader_t hdr; + + if (_ucnum_size > 0) { + if (!reload) + /* + * The numbers have already been loaded. + */ + return 0; + + free((char *) _ucnum_nodes); + _ucnum_size = 0; + } + + if ((in = _ucopenfile(paths, "num.dat", "rb")) == 0) + return -1; + + /* + * Load the header. + */ + fread((char *) &hdr, sizeof(_ucheader_t), 1, in); + + if (hdr.bom == 0xfffe) { + hdr.cnt = endian_short(hdr.cnt); + hdr.size.bytes = endian_long(hdr.size.bytes); + } + + _ucnum_size = hdr.cnt; + _ucnum_nodes = (ac_uint4 *) malloc(hdr.size.bytes); + _ucnum_vals = (short *) (_ucnum_nodes + _ucnum_size); + + /* + * Read the combining classes in. + */ + fread((char *) _ucnum_nodes, sizeof(unsigned char), hdr.size.bytes, in); + + /* + * Do an endian swap if necessary. + */ + if (hdr.bom == 0xfffe) { + for (i = 0; i < _ucnum_size; i++) + _ucnum_nodes[i] = endian_long(_ucnum_nodes[i]); + + /* + * Determine the number of values that have to be adjusted. + */ + size = (hdr.size.bytes - + (_ucnum_size * (sizeof(ac_uint4) << 1))) / + sizeof(short); + + for (i = 0; i < size; i++) + _ucnum_vals[i] = endian_short(_ucnum_vals[i]); + } + fclose(in); + return 0; +} + +static void +_ucnumb_unload(void) +{ + if (_ucnum_size == 0) + return; + + free((char *) _ucnum_nodes); + _ucnum_size = 0; +} +#endif + +int +ucnumber_lookup(ac_uint4 code, struct ucnumber *num) +{ + long l, r, m; + short *vp; + + l = 0; + r = _ucnum_size - 1; + while (l <= r) { + /* + * Determine a "mid" point and adjust to make sure the mid point is at + * the beginning of a code+offset pair. + */ + m = (l + r) >> 1; + m -= (m & 1); + if (code > _ucnum_nodes[m]) + l = m + 2; + else if (code < _ucnum_nodes[m]) + r = m - 2; + else { + vp = (short *)_ucnum_vals + _ucnum_nodes[m + 1]; + num->numerator = (int) *vp++; + num->denominator = (int) *vp; + return 1; + } + } + return 0; +} + +int +ucdigit_lookup(ac_uint4 code, int *digit) +{ + long l, r, m; + short *vp; + + l = 0; + r = _ucnum_size - 1; + while (l <= r) { + /* + * Determine a "mid" point and adjust to make sure the mid point is at + * the beginning of a code+offset pair. + */ + m = (l + r) >> 1; + m -= (m & 1); + if (code > _ucnum_nodes[m]) + l = m + 2; + else if (code < _ucnum_nodes[m]) + r = m - 2; + else { + vp = (short *)_ucnum_vals + _ucnum_nodes[m + 1]; + if (*vp == *(vp + 1)) { + *digit = *vp; + return 1; + } + return 0; + } + } + return 0; +} + +struct ucnumber +ucgetnumber(ac_uint4 code) +{ + struct ucnumber num; + + /* + * Initialize with some arbitrary value, because the caller simply cannot + * tell for sure if the code is a number without calling the ucisnumber() + * macro before calling this function. + */ + num.numerator = num.denominator = -111; + + (void) ucnumber_lookup(code, &num); + + return num; +} + +int +ucgetdigit(ac_uint4 code) +{ + int dig; + + /* + * Initialize with some arbitrary value, because the caller simply cannot + * tell for sure if the code is a number without calling the ucisdigit() + * macro before calling this function. + */ + dig = -111; + + (void) ucdigit_lookup(code, &dig); + + return dig; +} + +/************************************************************************** + * + * Setup and cleanup routines. + * + **************************************************************************/ + +#if HARDCODE_DATA +int ucdata_load(char *paths, int masks) { return 0; } +void ucdata_unload(int masks) { } +int ucdata_reload(char *paths, int masks) { return 0; } +#else +/* + * Return 0 if okay, negative on error + */ +int +ucdata_load(char *paths, int masks) +{ + int error = 0; + + if (masks & UCDATA_CTYPE) + error |= _ucprop_load(paths, 0) < 0 ? UCDATA_CTYPE : 0; + if (masks & UCDATA_CASE) + error |= _uccase_load(paths, 0) < 0 ? UCDATA_CASE : 0; + if (masks & UCDATA_DECOMP) + error |= _ucdcmp_load(paths, 0) < 0 ? UCDATA_DECOMP : 0; + if (masks & UCDATA_CMBCL) + error |= _uccmcl_load(paths, 0) < 0 ? UCDATA_CMBCL : 0; + if (masks & UCDATA_NUM) + error |= _ucnumb_load(paths, 0) < 0 ? UCDATA_NUM : 0; + if (masks & UCDATA_COMP) + error |= _uccomp_load(paths, 0) < 0 ? UCDATA_COMP : 0; + if (masks & UCDATA_KDECOMP) + error |= _uckdcmp_load(paths, 0) < 0 ? UCDATA_KDECOMP : 0; + + return -error; +} + +void +ucdata_unload(int masks) +{ + if (masks & UCDATA_CTYPE) + _ucprop_unload(); + if (masks & UCDATA_CASE) + _uccase_unload(); + if (masks & UCDATA_DECOMP) + _ucdcmp_unload(); + if (masks & UCDATA_CMBCL) + _uccmcl_unload(); + if (masks & UCDATA_NUM) + _ucnumb_unload(); + if (masks & UCDATA_COMP) + _uccomp_unload(); + if (masks & UCDATA_KDECOMP) + _uckdcmp_unload(); +} + +/* + * Return 0 if okay, negative on error + */ +int +ucdata_reload(char *paths, int masks) +{ + int error = 0; + + if (masks & UCDATA_CTYPE) + error |= _ucprop_load(paths, 1) < 0 ? UCDATA_CTYPE : 0; + if (masks & UCDATA_CASE) + error |= _uccase_load(paths, 1) < 0 ? UCDATA_CASE : 0; + if (masks & UCDATA_DECOMP) + error |= _ucdcmp_load(paths, 1) < 0 ? UCDATA_DECOMP : 0; + if (masks & UCDATA_CMBCL) + error |= _uccmcl_load(paths, 1) < 0 ? UCDATA_CMBCL : 0; + if (masks & UCDATA_NUM) + error |= _ucnumb_load(paths, 1) < 0 ? UCDATA_NUM : 0; + if (masks & UCDATA_COMP) + error |= _uccomp_load(paths, 1) < 0 ? UCDATA_COMP : 0; + if (masks & UCDATA_KDECOMP) + error |= _uckdcmp_load(paths, 1) < 0 ? UCDATA_KDECOMP : 0; + + return -error; +} +#endif + +#ifdef TEST + +void +main(void) +{ + int dig; + ac_uint4 i, lo, *dec; + struct ucnumber num; + +/* ucdata_setup("."); */ + + if (ucisweak(0x30)) + printf("WEAK\n"); + else + printf("NOT WEAK\n"); + + printf("LOWER 0x%04lX\n", uctolower(0xff3a)); + printf("UPPER 0x%04lX\n", uctoupper(0xff5a)); + + if (ucisalpha(0x1d5)) + printf("ALPHA\n"); + else + printf("NOT ALPHA\n"); + + if (ucisupper(0x1d5)) { + printf("UPPER\n"); + lo = uctolower(0x1d5); + printf("0x%04lx\n", lo); + lo = uctotitle(0x1d5); + printf("0x%04lx\n", lo); + } else + printf("NOT UPPER\n"); + + if (ucistitle(0x1d5)) + printf("TITLE\n"); + else + printf("NOT TITLE\n"); + + if (uciscomposite(0x1d5)) + printf("COMPOSITE\n"); + else + printf("NOT COMPOSITE\n"); + + if (ucdecomp(0x1d5, &lo, &dec)) { + for (i = 0; i < lo; i++) + printf("0x%04lx ", dec[i]); + putchar('\n'); + } + + if ((lo = uccombining_class(0x41)) != 0) + printf("0x41 CCL %ld\n", lo); + + if (ucisxdigit(0xfeff)) + printf("0xFEFF HEX DIGIT\n"); + else + printf("0xFEFF NOT HEX DIGIT\n"); + + if (ucisdefined(0x10000)) + printf("0x10000 DEFINED\n"); + else + printf("0x10000 NOT DEFINED\n"); + + if (ucnumber_lookup(0x30, &num)) { + if (num.denominator != 1) + printf("UCNUMBER: 0x30 = %d/%d\n", num.numerator, num.denominator); + else + printf("UCNUMBER: 0x30 = %d\n", num.numerator); + } else + printf("UCNUMBER: 0x30 NOT A NUMBER\n"); + + if (ucnumber_lookup(0xbc, &num)) { + if (num.denominator != 1) + printf("UCNUMBER: 0xbc = %d/%d\n", num.numerator, num.denominator); + else + printf("UCNUMBER: 0xbc = %d\n", num.numerator); + } else + printf("UCNUMBER: 0xbc NOT A NUMBER\n"); + + + if (ucnumber_lookup(0xff19, &num)) { + if (num.denominator != 1) + printf("UCNUMBER: 0xff19 = %d/%d\n", num.numerator, num.denominator); + else + printf("UCNUMBER: 0xff19 = %d\n", num.numerator); + } else + printf("UCNUMBER: 0xff19 NOT A NUMBER\n"); + + if (ucnumber_lookup(0x4e00, &num)) { + if (num.denominator != 1) + printf("UCNUMBER: 0x4e00 = %d/%d\n", num.numerator, num.denominator); + else + printf("UCNUMBER: 0x4e00 = %d\n", num.numerator); + } else + printf("UCNUMBER: 0x4e00 NOT A NUMBER\n"); + + if (ucdigit_lookup(0x06f9, &dig)) + printf("UCDIGIT: 0x6f9 = %d\n", dig); + else + printf("UCDIGIT: 0x6f9 NOT A NUMBER\n"); + + dig = ucgetdigit(0x0969); + printf("UCGETDIGIT: 0x969 = %d\n", dig); + + num = ucgetnumber(0x30); + if (num.denominator != 1) + printf("UCGETNUMBER: 0x30 = %d/%d\n", num.numerator, num.denominator); + else + printf("UCGETNUMBER: 0x30 = %d\n", num.numerator); + + num = ucgetnumber(0xbc); + if (num.denominator != 1) + printf("UCGETNUMBER: 0xbc = %d/%d\n", num.numerator, num.denominator); + else + printf("UCGETNUMBER: 0xbc = %d\n", num.numerator); + + num = ucgetnumber(0xff19); + if (num.denominator != 1) + printf("UCGETNUMBER: 0xff19 = %d/%d\n", num.numerator, num.denominator); + else + printf("UCGETNUMBER: 0xff19 = %d\n", num.numerator); + +/* ucdata_cleanup(); */ + exit(0); +} + +#endif /* TEST */ diff --git a/libraries/liblunicode/ucdata/ucdata.h b/libraries/liblunicode/ucdata/ucdata.h new file mode 100644 index 0000000..69c592a --- /dev/null +++ b/libraries/liblunicode/ucdata/ucdata.h @@ -0,0 +1,364 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Copyright 2001 Computing Research Labs, New Mexico State University + * + * 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 COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY 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. + */ +/* $Id: ucdata.h,v 1.6 2001/01/02 18:46:20 mleisher Exp $ */ + +#ifndef _h_ucdata +#define _h_ucdata + +LDAP_BEGIN_DECL + +#define UCDATA_VERSION "2.4" + +/************************************************************************** + * + * Masks and macros for character properties. + * + **************************************************************************/ + +/* + * Values that can appear in the `mask1' parameter of the ucisprop() + * function. + */ +#define UC_MN 0x00000001 /* Mark, Non-Spacing */ +#define UC_MC 0x00000002 /* Mark, Spacing Combining */ +#define UC_ME 0x00000004 /* Mark, Enclosing */ +#define UC_ND 0x00000008 /* Number, Decimal Digit */ +#define UC_NL 0x00000010 /* Number, Letter */ +#define UC_NO 0x00000020 /* Number, Other */ +#define UC_ZS 0x00000040 /* Separator, Space */ +#define UC_ZL 0x00000080 /* Separator, Line */ +#define UC_ZP 0x00000100 /* Separator, Paragraph */ +#define UC_CC 0x00000200 /* Other, Control */ +#define UC_CF 0x00000400 /* Other, Format */ +#define UC_OS 0x00000800 /* Other, Surrogate */ +#define UC_CO 0x00001000 /* Other, Private Use */ +#define UC_CN 0x00002000 /* Other, Not Assigned */ +#define UC_LU 0x00004000 /* Letter, Uppercase */ +#define UC_LL 0x00008000 /* Letter, Lowercase */ +#define UC_LT 0x00010000 /* Letter, Titlecase */ +#define UC_LM 0x00020000 /* Letter, Modifier */ +#define UC_LO 0x00040000 /* Letter, Other */ +#define UC_PC 0x00080000 /* Punctuation, Connector */ +#define UC_PD 0x00100000 /* Punctuation, Dash */ +#define UC_PS 0x00200000 /* Punctuation, Open */ +#define UC_PE 0x00400000 /* Punctuation, Close */ +#define UC_PO 0x00800000 /* Punctuation, Other */ +#define UC_SM 0x01000000 /* Symbol, Math */ +#define UC_SC 0x02000000 /* Symbol, Currency */ +#define UC_SK 0x04000000 /* Symbol, Modifier */ +#define UC_SO 0x08000000 /* Symbol, Other */ +#define UC_L 0x10000000 /* Left-To-Right */ +#define UC_R 0x20000000 /* Right-To-Left */ +#define UC_EN 0x40000000 /* European Number */ +#define UC_ES 0x80000000 /* European Number Separator */ + +/* + * Values that can appear in the `mask2' parameter of the ucisprop() + * function. + */ +#define UC_ET 0x00000001 /* European Number Terminator */ +#define UC_AN 0x00000002 /* Arabic Number */ +#define UC_CS 0x00000004 /* Common Number Separator */ +#define UC_B 0x00000008 /* Block Separator */ +#define UC_S 0x00000010 /* Segment Separator */ +#define UC_WS 0x00000020 /* Whitespace */ +#define UC_ON 0x00000040 /* Other Neutrals */ +/* + * Implementation specific character properties. + */ +#define UC_CM 0x00000080 /* Composite */ +#define UC_NB 0x00000100 /* Non-Breaking */ +#define UC_SY 0x00000200 /* Symmetric */ +#define UC_HD 0x00000400 /* Hex Digit */ +#define UC_QM 0x00000800 /* Quote Mark */ +#define UC_MR 0x00001000 /* Mirroring */ +#define UC_SS 0x00002000 /* Space, other */ + +#define UC_CP 0x00004000 /* Defined */ + +/* + * Added for UnicodeData-2.1.3. + */ +#define UC_PI 0x00008000 /* Punctuation, Initial */ +#define UC_PF 0x00010000 /* Punctuation, Final */ + +/* + * This is the primary function for testing to see if a character has some set + * of properties. The macros that test for various character properties all + * call this function with some set of masks. + */ +LDAP_LUNICODE_F (int) +ucisprop LDAP_P((ac_uint4 code, ac_uint4 mask1, ac_uint4 mask2)); + +#define ucisalpha(cc) ucisprop(cc, UC_LU|UC_LL|UC_LM|UC_LO|UC_LT, 0) +#define ucisdigit(cc) ucisprop(cc, UC_ND, 0) +#define ucisalnum(cc) ucisprop(cc, UC_LU|UC_LL|UC_LM|UC_LO|UC_LT|UC_ND, 0) +#define uciscntrl(cc) ucisprop(cc, UC_CC|UC_CF, 0) +#define ucisspace(cc) ucisprop(cc, UC_ZS|UC_SS, 0) +#define ucisblank(cc) ucisprop(cc, UC_ZS, 0) +#define ucispunct(cc) ucisprop(cc, UC_PD|UC_PS|UC_PE|UC_PO, UC_PI|UC_PF) +#define ucisgraph(cc) ucisprop(cc, UC_MN|UC_MC|UC_ME|UC_ND|UC_NL|UC_NO|\ + UC_LU|UC_LL|UC_LT|UC_LM|UC_LO|UC_PC|UC_PD|\ + UC_PS|UC_PE|UC_PO|UC_SM|UC_SM|UC_SC|UC_SK|\ + UC_SO, UC_PI|UC_PF) +#define ucisprint(cc) ucisprop(cc, UC_MN|UC_MC|UC_ME|UC_ND|UC_NL|UC_NO|\ + UC_LU|UC_LL|UC_LT|UC_LM|UC_LO|UC_PC|UC_PD|\ + UC_PS|UC_PE|UC_PO|UC_SM|UC_SM|UC_SC|UC_SK|\ + UC_SO|UC_ZS, UC_PI|UC_PF) +#define ucisupper(cc) ucisprop(cc, UC_LU, 0) +#define ucislower(cc) ucisprop(cc, UC_LL, 0) +#define ucistitle(cc) ucisprop(cc, UC_LT, 0) +#define ucisxdigit(cc) ucisprop(cc, 0, UC_HD) + +#define ucisisocntrl(cc) ucisprop(cc, UC_CC, 0) +#define ucisfmtcntrl(cc) ucisprop(cc, UC_CF, 0) + +#define ucissymbol(cc) ucisprop(cc, UC_SM|UC_SC|UC_SO|UC_SK, 0) +#define ucisnumber(cc) ucisprop(cc, UC_ND|UC_NO|UC_NL, 0) +#define ucisnonspacing(cc) ucisprop(cc, UC_MN, 0) +#define ucisopenpunct(cc) ucisprop(cc, UC_PS, 0) +#define ucisclosepunct(cc) ucisprop(cc, UC_PE, 0) +#define ucisinitialpunct(cc) ucisprop(cc, 0, UC_PI) +#define ucisfinalpunct(cc) ucisprop(cc, 0, UC_PF) + +#define uciscomposite(cc) ucisprop(cc, 0, UC_CM) +#define ucishex(cc) ucisprop(cc, 0, UC_HD) +#define ucisquote(cc) ucisprop(cc, 0, UC_QM) +#define ucissymmetric(cc) ucisprop(cc, 0, UC_SY) +#define ucismirroring(cc) ucisprop(cc, 0, UC_MR) +#define ucisnonbreaking(cc) ucisprop(cc, 0, UC_NB) + +/* + * Directionality macros. + */ +#define ucisrtl(cc) ucisprop(cc, UC_R, 0) +#define ucisltr(cc) ucisprop(cc, UC_L, 0) +#define ucisstrong(cc) ucisprop(cc, UC_L|UC_R, 0) +#define ucisweak(cc) ucisprop(cc, UC_EN|UC_ES, UC_ET|UC_AN|UC_CS) +#define ucisneutral(cc) ucisprop(cc, 0, UC_B|UC_S|UC_WS|UC_ON) +#define ucisseparator(cc) ucisprop(cc, 0, UC_B|UC_S) + +/* + * Other macros inspired by John Cowan. + */ +#define ucismark(cc) ucisprop(cc, UC_MN|UC_MC|UC_ME, 0) +#define ucismodif(cc) ucisprop(cc, UC_LM, 0) +#define ucisletnum(cc) ucisprop(cc, UC_NL, 0) +#define ucisconnect(cc) ucisprop(cc, UC_PC, 0) +#define ucisdash(cc) ucisprop(cc, UC_PD, 0) +#define ucismath(cc) ucisprop(cc, UC_SM, 0) +#define uciscurrency(cc) ucisprop(cc, UC_SC, 0) +#define ucismodifsymbol(cc) ucisprop(cc, UC_SK, 0) +#define ucisnsmark(cc) ucisprop(cc, UC_MN, 0) +#define ucisspmark(cc) ucisprop(cc, UC_MC, 0) +#define ucisenclosing(cc) ucisprop(cc, UC_ME, 0) +#define ucisprivate(cc) ucisprop(cc, UC_CO, 0) +#define ucissurrogate(cc) ucisprop(cc, UC_OS, 0) +#define ucislsep(cc) ucisprop(cc, UC_ZL, 0) +#define ucispsep(cc) ucisprop(cc, UC_ZP, 0) + +#define ucisidentstart(cc) ucisprop(cc, UC_LU|UC_LL|UC_LT|UC_LO|UC_NL, 0) +#define ucisidentpart(cc) ucisprop(cc, UC_LU|UC_LL|UC_LT|UC_LO|UC_NL|\ + UC_MN|UC_MC|UC_ND|UC_PC|UC_CF, 0) + +#define ucisdefined(cc) ucisprop(cc, 0, UC_CP) +#define ucisundefined(cc) !ucisprop(cc, 0, UC_CP) + +/* + * Other miscellaneous character property macros. + */ +#define ucishan(cc) (((cc) >= 0x4e00 && (cc) <= 0x9fff) ||\ + ((cc) >= 0xf900 && (cc) <= 0xfaff)) +#define ucishangul(cc) ((cc) >= 0xac00 && (cc) <= 0xd7ff) + +/************************************************************************** + * + * Functions for case conversion. + * + **************************************************************************/ + +LDAP_LUNICODE_F (ac_uint4) uctoupper LDAP_P((ac_uint4 code)); +LDAP_LUNICODE_F (ac_uint4) uctolower LDAP_P((ac_uint4 code)); +LDAP_LUNICODE_F (ac_uint4) uctotitle LDAP_P((ac_uint4 code)); + +/************************************************************************** + * + * Functions for getting compositions. + * + **************************************************************************/ + +/* + * This routine determines if there exists a composition of node1 and node2. + * If it returns 0, there is no composition. Any other value indicates a + * composition was returned in comp. + */ +LDAP_LUNICODE_F (int) uccomp LDAP_P((ac_uint4 node1, ac_uint4 node2, + ac_uint4 *comp)); + +/* + * Does Hangul composition on the string str with length len, and returns + * the length of the composed string. + */ +LDAP_LUNICODE_F (int) uccomp_hangul LDAP_P((ac_uint4 *str, int len)); + +/* + * Does canonical composition on the string str with length len, and returns + * the length of the composed string. + */ +LDAP_LUNICODE_F (int) uccanoncomp LDAP_P((ac_uint4 *str, int len)); + +/************************************************************************** + * + * Functions for getting decompositions. + * + **************************************************************************/ + +/* + * This routine determines if the code has a decomposition. If it returns 0, + * there is no decomposition. Any other value indicates a decomposition was + * returned. + */ +LDAP_LUNICODE_F (int) +ucdecomp LDAP_P((ac_uint4 code, ac_uint4 *num, + ac_uint4 **decomp)); + +/* + * Equivalent to ucdecomp() except that it includes compatibility + * decompositions. + */ +LDAP_LUNICODE_F (int) +uckdecomp LDAP_P((ac_uint4 code, ac_uint4 *num, + ac_uint4 **decomp)); + +/* + * If the code is a Hangul syllable, this routine decomposes it into the array + * passed. The array size should be at least 3. + */ +LDAP_LUNICODE_F (int) +ucdecomp_hangul LDAP_P((ac_uint4 code, ac_uint4 *num, + ac_uint4 decomp[])); + +/* + * This routine does canonical decomposition of the string in of length + * inlen, and returns the decomposed string in out with length outlen. + * The memory for out is allocated by this routine. It returns the length + * of the decomposed string if okay, and -1 on error. + */ +LDAP_LUNICODE_F (int) +uccanondecomp LDAP_P((const ac_uint4 *in, int inlen, + ac_uint4 **out, int *outlen, void *ctx)); + +/* + * Equivalent to uccanondecomp() except that it includes compatibility + * decompositions. + */ +LDAP_LUNICODE_F (int) +uccompatdecomp LDAP_P((const ac_uint4 *in, int inlen, + ac_uint4 **out, int *outlen, void *ctx)); + +/************************************************************************** + * + * Functions for getting combining classes. + * + **************************************************************************/ + +/* + * This will return the combining class for a character to be used with the + * Canonical Ordering algorithm. + */ +LDAP_LUNICODE_F (ac_uint4) uccombining_class LDAP_P((ac_uint4 code)); + +/************************************************************************** + * + * Functions for getting numbers and digits. + * + **************************************************************************/ + +struct ucnumber { + int numerator; + int denominator; +}; + +LDAP_LUNICODE_F (int) +ucnumber_lookup LDAP_P((ac_uint4 code, struct ucnumber *num)); + +LDAP_LUNICODE_F (int) +ucdigit_lookup LDAP_P((ac_uint4 code, int *digit)); + +/* + * For compatibility with John Cowan's "uctype" package. + */ +LDAP_LUNICODE_F (struct ucnumber) ucgetnumber LDAP_P((ac_uint4 code)); +LDAP_LUNICODE_F (int) ucgetdigit LDAP_P((ac_uint4 code)); + +/************************************************************************** + * + * Functions library initialization and cleanup. + * + **************************************************************************/ + +/* + * Macros for specifying the data tables to be loaded, unloaded, or reloaded + * by the ucdata_load(), ucdata_unload(), and ucdata_reload() routines. + */ +#define UCDATA_CASE 0x01 +#define UCDATA_CTYPE 0x02 +#define UCDATA_DECOMP 0x04 +#define UCDATA_CMBCL 0x08 +#define UCDATA_NUM 0x10 +#define UCDATA_COMP 0x20 +#define UCDATA_KDECOMP 0x40 + +#define UCDATA_ALL (UCDATA_CASE|UCDATA_CTYPE|UCDATA_DECOMP|\ + UCDATA_CMBCL|UCDATA_NUM|UCDATA_COMP|UCDATA_KDECOMP) + +/* + * Functions to load, unload, and reload specific data files. + */ +LDAP_LUNICODE_F (int) ucdata_load LDAP_P((char *paths, int mask)); +LDAP_LUNICODE_F (void) ucdata_unload LDAP_P((int mask)); +LDAP_LUNICODE_F (int) ucdata_reload LDAP_P((char *paths, int mask)); + +#ifdef UCDATA_DEPRECATED +/* + * Deprecated functions, now just compatibility macros. + */ +#define ucdata_setup(p) ucdata_load(p, UCDATA_ALL) +#define ucdata_cleanup() ucdata_unload(UCDATA_ALL) +#endif + +LDAP_END_DECL + +#endif /* _h_ucdata */ diff --git a/libraries/liblunicode/ucdata/ucdata.man b/libraries/liblunicode/ucdata/ucdata.man new file mode 100644 index 0000000..54df484 --- /dev/null +++ b/libraries/liblunicode/ucdata/ucdata.man @@ -0,0 +1,504 @@ +.\" +.\" $Id: ucdata.man,v 1.5 2001/01/02 18:46:20 mleisher Exp $ +.\" +.TH ucdata 3 "03 January 2001" +.SH NAME +ucdata \- package for providing Unicode/ISO10646 character information + +.SH SYNOPSIS +#include <ucdata.h> +.sp +void ucdata_load(char * paths, int masks) +.sp +void ucdata_unload(int masks) +.sp +void ucdata_reload(char * paths, int masks) +.sp +int ucdecomp(unsigned long code, unsigned long *num, unsigned long **decomp) +.sp +int uccanondecomp(const unsigned long *in, int inlen, unsigned long **out, +int *outlen) +.sp +int ucdecomp_hangul(unsigned long code, unsigned long *num, +unsigned long decomp[]) +.sp +int uccomp(unsigned long ch1, unsigned long ch2, unsigned long *comp) +.sp +int uccomp_hangul(unsigned long *str, int len) +.sp +int uccanoncomp(unsiged long *str, int len) +.nf +struct ucnumber { + int numerator; + int denominator; +}; +.sp +int ucnumber_lookup(unsigned long code, struct ucnumber *num) +.sp +int ucdigit_lookup(unsigned long code, int *digit) +.sp +struct ucnumber ucgetnumber(unsigned long code) +.sp +int ucgetdigit(unsigned long code) +.sp +unsigned long uctoupper(unsigned long code) +.sp +unsigned long uctolower(unsigned long code) +.sp +unsigned long uctotitle(unsigned long code) +.sp +int ucisalpha(unsigned long code) +.sp +int ucisalnum(unsigned long code) +.sp +int ucisdigit(unsigned long code) +.sp +int uciscntrl(unsigned long code) +.sp +int ucisspace(unsigned long code) +.sp +int ucisblank(unsigned long code) +.sp +int ucispunct(unsigned long code) +.sp +int ucisgraph(unsigned long code) +.sp +int ucisprint(unsigned long code) +.sp +int ucisxdigit(unsigned long code) +.sp +int ucisupper(unsigned long code) +.sp +int ucislower(unsigned long code) +.sp +int ucistitle(unsigned long code) +.sp +int ucisisocntrl(unsigned long code) +.sp +int ucisfmtcntrl(unsigned long code) +.sp +int ucissymbol(unsigned long code) +.sp +int ucisnumber(unsigned long code) +.sp +int ucisnonspacing(unsigned long code) +.sp +int ucisopenpunct(unsigned long code) +.sp +int ucisclosepunct(unsigned long code) +.sp +int ucisinitialpunct(unsigned long code) +.sp +int ucisfinalpunct(unsigned long code) +.sp +int uciscomposite(unsigned long code) +.sp +int ucisquote(unsigned long code) +.sp +int ucissymmetric(unsigned long code) +.sp +int ucismirroring(unsigned long code) +.sp +int ucisnonbreaking(unsigned long code) +.sp +int ucisrtl(unsigned long code) +.sp +int ucisltr(unsigned long code) +.sp +int ucisstrong(unsigned long code) +.sp +int ucisweak(unsigned long code) +.sp +int ucisneutral(unsigned long code) +.sp +int ucisseparator(unsigned long code) +.sp +int ucislsep(unsigned long code) +.sp +int ucispsep(unsigned long code) +.sp +int ucismark(unsigned long code) +.sp +int ucisnsmark(unsigned long code) +.sp +int ucisspmark(unsigned long code) +.sp +int ucismodif(unsigned long code) +.sp +int ucismodifsymbol(unsigned long code) +.sp +int ucisletnum(unsigned long code) +.sp +int ucisconnect(unsigned long code) +.sp +int ucisdash(unsigned long code) +.sp +int ucismath(unsigned long code) +.sp +int uciscurrency(unsigned long code) +.sp +int ucisenclosing(unsigned long code) +.sp +int ucisprivate(unsigned long code) +.sp +int ucissurrogate(unsigned long code) +.sp +int ucisidentstart(unsigned long code) +.sp +int ucisidentpart(unsigned long code) +.sp +int ucisdefined(unsigned long code) +.sp +int ucisundefined(unsigned long code) +.sp +int ucishan(unsigned long code) +.sp +int ucishangul(unsigned long code) + +.SH DESCRIPTION +.TP 4 +.BR Macros +.br +UCDATA_CASE +.br +UCDATA_CTYPE +.br +UCDATA_DECOMP +.br +UCDATA_CMBCL +.br +UCDATA_NUM +.br +UCDATA_ALL +.br +.TP 4 +.BR ucdata_load() +This function initializes the UCData library by locating the data files in one +of the colon-separated directories in the `paths' parameter. The data files +to be loaded are specified in the `masks' parameter as a bitwise combination +of the macros listed above. +.sp +This should be called before using any of the other functions. +.TP 4 +.BR ucdata_unload() +This function unloads the data tables specified in the `masks' parameter. +.sp +This function should be called when the application is done using the UCData +package. +.TP 4 +.BR ucdata_reload() +This function reloads the data files from one of the colon-separated +directories in the `paths' parameter. The data files to be reloaded are +specified in the `masks' parameter as a bitwise combination of the macros +listed above. +.TP 4 +.BR ucdecomp() +This function determines if a character has a decomposition and returns the +decomposition information if it exists. +.sp +If a zero is returned, there is no decomposition. If a non-zero is +returned, then the `num' and `decomp' variables are filled in with the +appropriate values. +.sp +Example call: +.sp +.nf + unsigned long i, num, *decomp; + + if (ucdecomp(0x1d5, &num, &decomp) != 0) { + for (i = 0; i < num; i++) + printf("0x%08lX,", decomp[i]); + putchar('\n'); + } +.TP 4 +.BR uccanondecomp() +This function will decompose a string, insuring the characters are in +canonical order for comparison. +.sp +If a decomposed string is returned, the caller is responsible for deallocating +the string. +.sp +If a -1 is returned, memory allocation failed. If a zero is returned, no +decomposition was done. Any other value means a decomposition string was +created and the values returned in the `out' and `outlen' parameters. +.TP 4 +.BR ucdecomp_hangul() +This function determines if a Hangul syllable has a +decomposition and returns the decomposition information. +.sp +An array of at least size 3 should be passed to the function +for the decomposition of the syllable. +.sp +If a zero is returned, the character is not a Hangul +syllable. If a non-zero is returned, the `num' field +will be 2 or 3 and the syllable will be decomposed into +the `decomp' array arithmetically. +.sp +Example call: +.sp +.nf + unsigned long i, num, decomp[3]; + + if (ucdecomp_hangul(0xb1ba, &num, &decomp) != 0) { + for (i = 0; i < num; i++) + printf("0x%08lX,", decomp[i]); + putchar('\n'); + } +.TP 4 +.BR uccomp() +This function determines if a pair of characters have a composition, and +returns that composition if one exists. +.sp +A zero is returned is no composition exists for the character pair. Any other +value indicates the `comp' field holds the character code representing the +composition of the two character codes. +.TP 4 +.BR uccomp_hangul() +This composes the Hangul Jamo in-place in the string. +.sp +The returned value is the new length of the string. +.TP 4 +.BR uccanoncomp() +This function does a full composition in-place in the string, including the +Hangul composition. +.sp +The returned value is the new length of the string. +.TP 4 +.BR ucnumber_lookup() +This function determines if the code is a number and +fills in the `num' field with the numerator and +denominator. If the code happens to be a single digit, +the numerator and denominator fields will be the same. +.sp +If the function returns 0, the code is not a number. +Any other return value means the code is a number. +.TP 4 +.BR ucdigit_lookup() +This function determines if the code is a digit and +fills in the `digit' field with the digit value. +.sp +If the function returns 0, the code is not a number. +Any other return value means the code is a number. +.TP 4 +.BR ucgetnumber() +This is a compatibility function with John Cowan's +"uctype" package. It uses ucnumber_lookup(). +.TP 4 +.BR ucgetdigit() +This is a compatibility function with John Cowan's +"uctype" package. It uses ucdigit_lookup(). +.TP 4 +.BR uctoupper() +This function returns the code unchanged if it is +already upper case or has no upper case equivalent. +Otherwise the upper case equivalent is returned. +.TP 4 +.BR uctolower() +This function returns the code unchanged if it is +already lower case or has no lower case equivalent. +Otherwise the lower case equivalent is returned. +.TP 4 +.BR uctotitle() +This function returns the code unchanged if it is +already title case or has no title case equivalent. +Otherwise the title case equivalent is returned. +.TP 4 +.BR ucisalpha() +Test if \fIcode\fR is an alpha character. +.TP 4 +.BR ucisalnum() +Test if \fIcode\fR is an alpha or digit character. +.TP 4 +.BR ucisdigit() +Test if \fIcode\fR is a digit character. +.TP 4 +.BR uciscntrl() +Test if \fIcode\fR is a control character. +.TP 4 +.BR ucisspace() +Test if \fIcode\fR is a space character. +.TP 4 +.BR ucisblank() +Test if \fIcode\fR is a blank character. +.TP 4 +.BR ucispunct() +Test if \fIcode\fR is a punctuation character. +.TP 4 +.BR ucisgraph() +Test if \fIcode\fR is a graphical (visible) character. +.TP 4 +.BR ucisprint() +Test if \fIcode\fR is a printable character. +.TP 4 +.BR ucisxdigit() +Test if \fIcode\fR is a hexadecimal digit character. +.TP 4 +.BR ucisupper() +Test if \fIcode\fR is an upper case character. +.TP 4 +.BR ucislower() +Test if \fIcode\fR is a lower case character. +.TP 4 +.BR ucistitle() +Test if \fIcode\fR is a title case character. +.TP 4 +.BR ucisisocntrl() +Is the character a C0 control character (< 32)? +.TP 4 +.BR ucisfmtcntrl() +Is the character a format control character? +.TP 4 +.BR ucissymbol() +Is the character a symbol? +.TP 4 +.BR ucisnumber() +Is the character a number or digit? +.TP 4 +.BR ucisnonspacing() +Is the character non-spacing? +.TP 4 +.BR ucisopenpunct() +Is the character an open/left punctuation (i.e. '[') +.TP 4 +.BR ucisclosepunct() +Is the character an close/right punctuation (i.e. ']') +.TP 4 +.BR ucisinitialpunct() +Is the character an initial punctuation (i.e. U+2018 LEFT +SINGLE QUOTATION MARK) +.TP 4 +.BR ucisfinalpunct() +Is the character a final punctuation (i.e. U+2019 RIGHT +SINGLE QUOTATION MARK) +.TP 4 +.BR uciscomposite() +Can the character be decomposed into a set of other +characters? +.TP 4 +.BR ucisquote() +Is the character one of the many quotation marks? +.TP 4 +.BR ucissymmetric() +Is the character one that has an opposite form +(i.e. <>) +.TP 4 +.BR ucismirroring() +Is the character mirroring (superset of symmetric)? +.TP 4 +.BR ucisnonbreaking() +Is the character non-breaking (i.e. non-breaking +space)? +.TP 4 +.BR ucisrtl() +Does the character have strong right-to-left +directionality (i.e. Arabic letters)? +.TP 4 +.BR ucisltr() +Does the character have strong left-to-right +directionality (i.e. Latin letters)? +.TP 4 +.BR ucisstrong() +Does the character have strong directionality? +.TP 4 +.BR ucisweak() +Does the character have weak directionality +(i.e. numbers)? +.TP 4 +.BR ucisneutral() +Does the character have neutral directionality +(i.e. whitespace)? +.TP 4 +.BR ucisseparator() +Is the character a block or segment separator? +.TP 4 +.BR ucislsep() +Is the character a line separator? +.TP 4 +.BR ucispsep() +Is the character a paragraph separator? +.TP 4 +.BR ucismark() +Is the character a mark of some kind? +.TP 4 +.BR ucisnsmark() +Is the character a non-spacing mark? +.TP 4 +.BR ucisspmark() +Is the character a spacing mark? +.TP 4 +.BR ucismodif() +Is the character a modifier letter? +.TP 4 +.BR ucismodifsymbol() +Is the character a modifier symbol? +.TP 4 +.BR ucisletnum() +Is the character a number represented by a letter? +.TP 4 +.BR ucisconnect() +Is the character connecting punctuation? +.TP 4 +.BR ucisdash() +Is the character dash punctuation? +.TP 4 +.BR ucismath() +Is the character a math character? +.TP 4 +.BR uciscurrency() +Is the character a currency character? +.TP 4 +.BR ucisenclosing() +Is the character enclosing (i.e. enclosing box)? +.TP 4 +.BR ucisprivate() +Is the character from the Private Use Area? +.TP 4 +.BR ucissurrogate() +Is the character one of the surrogate codes? +.TP 4 +.BR ucisidentstart() +Is the character a legal initial character of an identifier? +.TP 4 +.BR ucisidentpart() +Is the character a legal identifier character? +.TP 4 +.BR ucisdefined() +Is the character defined (appeared in one of the data +files)? +.TP 4 +.BR ucisundefined() +Is the character not defined (non-Unicode)? +.TP 4 +.BR ucishan() +Is the character a Han ideograph? +.TP 4 +.BR ucishangul() +Is the character a pre-composed Hangul syllable? + +.SH "SEE ALSO" +ctype(3) + +.SH ACKNOWLEDGMENTS +These are people who have helped with patches or +alerted me about problems. +.sp +John Cowan <cowan@locke.ccil.org> +.br +Bob Verbrugge <bob_verbrugge@nl.compuware.com> +.br +Christophe Pierret <cpierret@businessobjects.com> +.br +Kent Johnson <kent@pondview.mv.com> +.br +Valeriy E. Ushakov <uwe@ptc.spbu.ru> +.br +Stig Venaas <Stig.Venaas@uninett.no> + +.SH AUTHOR +Mark Leisher +.br +Computing Research Lab +.br +New Mexico State University +.br +Email: mleisher@crl.nmsu.edu diff --git a/libraries/liblunicode/ucdata/ucgendat.c b/libraries/liblunicode/ucdata/ucgendat.c new file mode 100644 index 0000000..c1efe87 --- /dev/null +++ b/libraries/liblunicode/ucdata/ucgendat.c @@ -0,0 +1,1960 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Copyright 2001 Computing Research Labs, New Mexico State University + * + * 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 COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY 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. + */ +/* $Id: ucgendat.c,v 1.4 2001/01/02 18:46:20 mleisher Exp $" */ + +#include "portable.h" +#include "ldap_config.h" + +#include <stdio.h> +#include <ac/ctype.h> +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/unistd.h> + +#include <ac/bytes.h> + +#include <lutil.h> + +#ifndef HARDCODE_DATA +#define HARDCODE_DATA 1 +#endif + +#undef ishdigit +#define ishdigit(cc) (((cc) >= '0' && (cc) <= '9') ||\ + ((cc) >= 'A' && (cc) <= 'F') ||\ + ((cc) >= 'a' && (cc) <= 'f')) + +/* + * A header written to the output file with the byte-order-mark and the number + * of property nodes. + */ +static ac_uint2 hdr[2] = {0xfeff, 0}; + +#define NUMPROPS 50 +#define NEEDPROPS (NUMPROPS + (4 - (NUMPROPS & 3))) + +typedef struct { + char *name; + int len; +} _prop_t; + +/* + * List of properties expected to be found in the Unicode Character Database + * including some implementation specific properties. + * + * The implementation specific properties are: + * Cm = Composed (can be decomposed) + * Nb = Non-breaking + * Sy = Symmetric (has left and right forms) + * Hd = Hex digit + * Qm = Quote marks + * Mr = Mirroring + * Ss = Space, other + * Cp = Defined character + */ +static _prop_t props[NUMPROPS] = { + {"Mn", 2}, {"Mc", 2}, {"Me", 2}, {"Nd", 2}, {"Nl", 2}, {"No", 2}, + {"Zs", 2}, {"Zl", 2}, {"Zp", 2}, {"Cc", 2}, {"Cf", 2}, {"Cs", 2}, + {"Co", 2}, {"Cn", 2}, {"Lu", 2}, {"Ll", 2}, {"Lt", 2}, {"Lm", 2}, + {"Lo", 2}, {"Pc", 2}, {"Pd", 2}, {"Ps", 2}, {"Pe", 2}, {"Po", 2}, + {"Sm", 2}, {"Sc", 2}, {"Sk", 2}, {"So", 2}, {"L", 1}, {"R", 1}, + {"EN", 2}, {"ES", 2}, {"ET", 2}, {"AN", 2}, {"CS", 2}, {"B", 1}, + {"S", 1}, {"WS", 2}, {"ON", 2}, + {"Cm", 2}, {"Nb", 2}, {"Sy", 2}, {"Hd", 2}, {"Qm", 2}, {"Mr", 2}, + {"Ss", 2}, {"Cp", 2}, {"Pi", 2}, {"Pf", 2}, {"AL", 2} +}; + +typedef struct { + ac_uint4 *ranges; + ac_uint2 used; + ac_uint2 size; +} _ranges_t; + +static _ranges_t proptbl[NUMPROPS]; + +/* + * Make sure this array is sized to be on a 4-byte boundary at compile time. + */ +static ac_uint2 propcnt[NEEDPROPS]; + +/* + * Array used to collect a decomposition before adding it to the decomposition + * table. + */ +static ac_uint4 dectmp[64]; +static ac_uint4 dectmp_size; + +typedef struct { + ac_uint4 code; + ac_uint2 size; + ac_uint2 used; + ac_uint4 *decomp; +} _decomp_t; + +/* + * List of decomposition. Created and expanded in order as the characters are + * encountered. First list contains canonical mappings, second also includes + * compatibility mappings. + */ +static _decomp_t *decomps; +static ac_uint4 decomps_used; +static ac_uint4 decomps_size; + +static _decomp_t *kdecomps; +static ac_uint4 kdecomps_used; +static ac_uint4 kdecomps_size; + +/* + * Composition exclusion table stuff. + */ +#define COMPEX_SET(c) (compexs[(c) >> 5] |= (1 << ((c) & 31))) +#define COMPEX_TEST(c) (compexs[(c) >> 5] & (1 << ((c) & 31))) +static ac_uint4 compexs[8192]; + +/* + * Struct for holding a composition pair, and array of composition pairs + */ +typedef struct { + ac_uint4 comp; + ac_uint4 count; + ac_uint4 code1; + ac_uint4 code2; +} _comp_t; + +static _comp_t *comps; +static ac_uint4 comps_used; + +/* + * Types and lists for handling lists of case mappings. + */ +typedef struct { + ac_uint4 key; + ac_uint4 other1; + ac_uint4 other2; +} _case_t; + +static _case_t *upper; +static _case_t *lower; +static _case_t *title; +static ac_uint4 upper_used; +static ac_uint4 upper_size; +static ac_uint4 lower_used; +static ac_uint4 lower_size; +static ac_uint4 title_used; +static ac_uint4 title_size; + +/* + * Array used to collect case mappings before adding them to a list. + */ +static ac_uint4 cases[3]; + +/* + * An array to hold ranges for combining classes. + */ +static ac_uint4 *ccl; +static ac_uint4 ccl_used; +static ac_uint4 ccl_size; + +/* + * Structures for handling numbers. + */ +typedef struct { + ac_uint4 code; + ac_uint4 idx; +} _codeidx_t; + +typedef struct { + short numerator; + short denominator; +} _num_t; + +/* + * Arrays to hold the mapping of codes to numbers. + */ +static _codeidx_t *ncodes; +static ac_uint4 ncodes_used; +static ac_uint4 ncodes_size; + +static _num_t *nums; +static ac_uint4 nums_used; +static ac_uint4 nums_size; + +/* + * Array for holding numbers. + */ +static _num_t *nums; +static ac_uint4 nums_used; +static ac_uint4 nums_size; + +static void +add_range(ac_uint4 start, ac_uint4 end, char *p1, char *p2) +{ + int i, j, k, len; + _ranges_t *rlp; + char *name; + + for (k = 0; k < 2; k++) { + if (k == 0) { + name = p1; + len = 2; + } else { + if (p2 == 0) + break; + + name = p2; + len = 1; + } + + for (i = 0; i < NUMPROPS; i++) { + if (props[i].len == len && memcmp(props[i].name, name, len) == 0) + break; + } + + if (i == NUMPROPS) + continue; + + rlp = &proptbl[i]; + + /* + * Resize the range list if necessary. + */ + if (rlp->used == rlp->size) { + if (rlp->size == 0) + rlp->ranges = (ac_uint4 *) + malloc(sizeof(ac_uint4) << 3); + else + rlp->ranges = (ac_uint4 *) + realloc((char *) rlp->ranges, + sizeof(ac_uint4) * (rlp->size + 8)); + rlp->size += 8; + } + + /* + * If this is the first code for this property list, just add it + * and return. + */ + if (rlp->used == 0) { + rlp->ranges[0] = start; + rlp->ranges[1] = end; + rlp->used += 2; + continue; + } + + /* + * Optimize the case of adding the range to the end. + */ + j = rlp->used - 1; + if (start > rlp->ranges[j]) { + j = rlp->used; + rlp->ranges[j++] = start; + rlp->ranges[j++] = end; + rlp->used = j; + continue; + } + + /* + * Need to locate the insertion point. + */ + for (i = 0; + i < rlp->used && start > rlp->ranges[i + 1] + 1; i += 2) ; + + /* + * If the start value lies in the current range, then simply set the + * new end point of the range to the end value passed as a parameter. + */ + if (rlp->ranges[i] <= start && start <= rlp->ranges[i + 1] + 1) { + rlp->ranges[i + 1] = end; + return; + } + + /* + * Shift following values up by two. + */ + for (j = rlp->used; j > i; j -= 2) { + rlp->ranges[j] = rlp->ranges[j - 2]; + rlp->ranges[j + 1] = rlp->ranges[j - 1]; + } + + /* + * Add the new range at the insertion point. + */ + rlp->ranges[i] = start; + rlp->ranges[i + 1] = end; + rlp->used += 2; + } +} + +static void +ordered_range_insert(ac_uint4 c, char *name, int len) +{ + int i, j; + ac_uint4 s, e; + _ranges_t *rlp; + + if (len == 0) + return; + + /* + * Deal with directionality codes introduced in Unicode 3.0. + */ + if ((len == 2 && memcmp(name, "BN", 2) == 0) || + (len == 3 && + (memcmp(name, "NSM", 3) == 0 || memcmp(name, "PDF", 3) == 0 || + memcmp(name, "LRE", 3) == 0 || memcmp(name, "LRO", 3) == 0 || + memcmp(name, "RLE", 3) == 0 || memcmp(name, "RLO", 3) == 0))) { + /* + * Mark all of these as Other Neutral to preserve compatibility with + * older versions. + */ + len = 2; + name = "ON"; + } + + for (i = 0; i < NUMPROPS; i++) { + if (props[i].len == len && memcmp(props[i].name, name, len) == 0) + break; + } + + if (i == NUMPROPS) + return; + + /* + * Have a match, so insert the code in order. + */ + rlp = &proptbl[i]; + + /* + * Resize the range list if necessary. + */ + if (rlp->used == rlp->size) { + if (rlp->size == 0) + rlp->ranges = (ac_uint4 *) + malloc(sizeof(ac_uint4) << 3); + else + rlp->ranges = (ac_uint4 *) + realloc((char *) rlp->ranges, + sizeof(ac_uint4) * (rlp->size + 8)); + rlp->size += 8; + } + + /* + * If this is the first code for this property list, just add it + * and return. + */ + if (rlp->used == 0) { + rlp->ranges[0] = rlp->ranges[1] = c; + rlp->used += 2; + return; + } + + /* + * Optimize the cases of extending the last range and adding new ranges to + * the end. + */ + j = rlp->used - 1; + e = rlp->ranges[j]; + s = rlp->ranges[j - 1]; + + if (c == e + 1) { + /* + * Extend the last range. + */ + rlp->ranges[j] = c; + return; + } + + if (c > e + 1) { + /* + * Start another range on the end. + */ + j = rlp->used; + rlp->ranges[j] = rlp->ranges[j + 1] = c; + rlp->used += 2; + return; + } + + if (c >= s) + /* + * The code is a duplicate of a code in the last range, so just return. + */ + return; + + /* + * The code should be inserted somewhere before the last range in the + * list. Locate the insertion point. + */ + for (i = 0; + i < rlp->used && c > rlp->ranges[i + 1] + 1; i += 2) ; + + s = rlp->ranges[i]; + e = rlp->ranges[i + 1]; + + if (c == e + 1) + /* + * Simply extend the current range. + */ + rlp->ranges[i + 1] = c; + else if (c < s) { + /* + * Add a new entry before the current location. Shift all entries + * before the current one up by one to make room. + */ + for (j = rlp->used; j > i; j -= 2) { + rlp->ranges[j] = rlp->ranges[j - 2]; + rlp->ranges[j + 1] = rlp->ranges[j - 1]; + } + rlp->ranges[i] = rlp->ranges[i + 1] = c; + + rlp->used += 2; + } +} + +static void +add_decomp(ac_uint4 code, short compat) +{ + ac_uint4 i, j, size; + _decomp_t **pdecomps; + ac_uint4 *pdecomps_used; + ac_uint4 *pdecomps_size; + + if (compat) { + pdecomps = &kdecomps; + pdecomps_used = &kdecomps_used; + pdecomps_size = &kdecomps_size; + } else { + pdecomps = &decomps; + pdecomps_used = &decomps_used; + pdecomps_size = &decomps_size; + } + + /* + * Add the code to the composite property. + */ + if (!compat) { + ordered_range_insert(code, "Cm", 2); + } + + /* + * Locate the insertion point for the code. + */ + for (i = 0; i < *pdecomps_used && code > (*pdecomps)[i].code; i++) ; + + /* + * Allocate space for a new decomposition. + */ + if (*pdecomps_used == *pdecomps_size) { + if (*pdecomps_size == 0) + *pdecomps = (_decomp_t *) malloc(sizeof(_decomp_t) << 3); + else + *pdecomps = (_decomp_t *) + realloc((char *) *pdecomps, + sizeof(_decomp_t) * (*pdecomps_size + 8)); + (void) memset((char *) (*pdecomps + *pdecomps_size), '\0', + sizeof(_decomp_t) << 3); + *pdecomps_size += 8; + } + + if (i < *pdecomps_used && code != (*pdecomps)[i].code) { + /* + * Shift the decomps up by one if the codes don't match. + */ + for (j = *pdecomps_used; j > i; j--) + (void) AC_MEMCPY((char *) &(*pdecomps)[j], (char *) &(*pdecomps)[j - 1], + sizeof(_decomp_t)); + } + + /* + * Insert or replace a decomposition. + */ + size = dectmp_size + (4 - (dectmp_size & 3)); + if ((*pdecomps)[i].size < size) { + if ((*pdecomps)[i].size == 0) + (*pdecomps)[i].decomp = (ac_uint4 *) + malloc(sizeof(ac_uint4) * size); + else + (*pdecomps)[i].decomp = (ac_uint4 *) + realloc((char *) (*pdecomps)[i].decomp, + sizeof(ac_uint4) * size); + (*pdecomps)[i].size = size; + } + + if ((*pdecomps)[i].code != code) + (*pdecomps_used)++; + + (*pdecomps)[i].code = code; + (*pdecomps)[i].used = dectmp_size; + (void) AC_MEMCPY((char *) (*pdecomps)[i].decomp, (char *) dectmp, + sizeof(ac_uint4) * dectmp_size); + + /* + * NOTICE: This needs changing later so it is more general than simply + * pairs. This calculation is done here to simplify allocation elsewhere. + */ + if (!compat && dectmp_size == 2) + comps_used++; +} + +static void +add_title(ac_uint4 code) +{ + ac_uint4 i, j; + + /* + * Always map the code to itself. + */ + cases[2] = code; + + /* + * If the upper case character is not present, then make it the same as + * the title case. + */ + if (cases[0] == 0) + cases[0] = code; + + if (title_used == title_size) { + if (title_size == 0) + title = (_case_t *) malloc(sizeof(_case_t) << 3); + else + title = (_case_t *) realloc((char *) title, + sizeof(_case_t) * (title_size + 8)); + title_size += 8; + } + + /* + * Locate the insertion point. + */ + for (i = 0; i < title_used && code > title[i].key; i++) ; + + if (i < title_used) { + /* + * Shift the array up by one. + */ + for (j = title_used; j > i; j--) + (void) AC_MEMCPY((char *) &title[j], (char *) &title[j - 1], + sizeof(_case_t)); + } + + title[i].key = cases[2]; /* Title */ + title[i].other1 = cases[0]; /* Upper */ + title[i].other2 = cases[1]; /* Lower */ + + title_used++; +} + +static void +add_upper(ac_uint4 code) +{ + ac_uint4 i, j; + + /* + * Always map the code to itself. + */ + cases[0] = code; + + /* + * If the title case character is not present, then make it the same as + * the upper case. + */ + if (cases[2] == 0) + cases[2] = code; + + if (upper_used == upper_size) { + if (upper_size == 0) + upper = (_case_t *) malloc(sizeof(_case_t) << 3); + else + upper = (_case_t *) realloc((char *) upper, + sizeof(_case_t) * (upper_size + 8)); + upper_size += 8; + } + + /* + * Locate the insertion point. + */ + for (i = 0; i < upper_used && code > upper[i].key; i++) ; + + if (i < upper_used) { + /* + * Shift the array up by one. + */ + for (j = upper_used; j > i; j--) + (void) AC_MEMCPY((char *) &upper[j], (char *) &upper[j - 1], + sizeof(_case_t)); + } + + upper[i].key = cases[0]; /* Upper */ + upper[i].other1 = cases[1]; /* Lower */ + upper[i].other2 = cases[2]; /* Title */ + + upper_used++; +} + +static void +add_lower(ac_uint4 code) +{ + ac_uint4 i, j; + + /* + * Always map the code to itself. + */ + cases[1] = code; + + /* + * If the title case character is empty, then make it the same as the + * upper case. + */ + if (cases[2] == 0) + cases[2] = cases[0]; + + if (lower_used == lower_size) { + if (lower_size == 0) + lower = (_case_t *) malloc(sizeof(_case_t) << 3); + else + lower = (_case_t *) realloc((char *) lower, + sizeof(_case_t) * (lower_size + 8)); + lower_size += 8; + } + + /* + * Locate the insertion point. + */ + for (i = 0; i < lower_used && code > lower[i].key; i++) ; + + if (i < lower_used) { + /* + * Shift the array up by one. + */ + for (j = lower_used; j > i; j--) + (void) AC_MEMCPY((char *) &lower[j], (char *) &lower[j - 1], + sizeof(_case_t)); + } + + lower[i].key = cases[1]; /* Lower */ + lower[i].other1 = cases[0]; /* Upper */ + lower[i].other2 = cases[2]; /* Title */ + + lower_used++; +} + +static void +ordered_ccl_insert(ac_uint4 c, ac_uint4 ccl_code) +{ + ac_uint4 i, j; + + if (ccl_used == ccl_size) { + if (ccl_size == 0) + ccl = (ac_uint4 *) malloc(sizeof(ac_uint4) * 24); + else + ccl = (ac_uint4 *) + realloc((char *) ccl, sizeof(ac_uint4) * (ccl_size + 24)); + ccl_size += 24; + } + + /* + * Optimize adding the first item. + */ + if (ccl_used == 0) { + ccl[0] = ccl[1] = c; + ccl[2] = ccl_code; + ccl_used += 3; + return; + } + + /* + * Handle the special case of extending the range on the end. This + * requires that the combining class codes are the same. + */ + if (ccl_code == ccl[ccl_used - 1] && c == ccl[ccl_used - 2] + 1) { + ccl[ccl_used - 2] = c; + return; + } + + /* + * Handle the special case of adding another range on the end. + */ + if (c > ccl[ccl_used - 2] + 1 || + (c == ccl[ccl_used - 2] + 1 && ccl_code != ccl[ccl_used - 1])) { + ccl[ccl_used++] = c; + ccl[ccl_used++] = c; + ccl[ccl_used++] = ccl_code; + return; + } + + /* + * Locate either the insertion point or range for the code. + */ + for (i = 0; i < ccl_used && c > ccl[i + 1] + 1; i += 3) ; + + if (ccl_code == ccl[i + 2] && c == ccl[i + 1] + 1) { + /* + * Extend an existing range. + */ + ccl[i + 1] = c; + return; + } else if (c < ccl[i]) { + /* + * Start a new range before the current location. + */ + for (j = ccl_used; j > i; j -= 3) { + ccl[j] = ccl[j - 3]; + ccl[j - 1] = ccl[j - 4]; + ccl[j - 2] = ccl[j - 5]; + } + ccl[i] = ccl[i + 1] = c; + ccl[i + 2] = ccl_code; + } +} + +/* + * Adds a number if it does not already exist and returns an index value + * multiplied by 2. + */ +static ac_uint4 +make_number(short num, short denom) +{ + ac_uint4 n; + + /* + * Determine if the number already exists. + */ + for (n = 0; n < nums_used; n++) { + if (nums[n].numerator == num && nums[n].denominator == denom) + return n << 1; + } + + if (nums_used == nums_size) { + if (nums_size == 0) + nums = (_num_t *) malloc(sizeof(_num_t) << 3); + else + nums = (_num_t *) realloc((char *) nums, + sizeof(_num_t) * (nums_size + 8)); + nums_size += 8; + } + + n = nums_used++; + nums[n].numerator = num; + nums[n].denominator = denom; + + return n << 1; +} + +static void +add_number(ac_uint4 code, short num, short denom) +{ + ac_uint4 i, j; + + /* + * Insert the code in order. + */ + for (i = 0; i < ncodes_used && code > ncodes[i].code; i++) ; + + /* + * Handle the case of the codes matching and simply replace the number + * that was there before. + */ + if (i < ncodes_used && code == ncodes[i].code) { + ncodes[i].idx = make_number(num, denom); + return; + } + + /* + * Resize the array if necessary. + */ + if (ncodes_used == ncodes_size) { + if (ncodes_size == 0) + ncodes = (_codeidx_t *) malloc(sizeof(_codeidx_t) << 3); + else + ncodes = (_codeidx_t *) + realloc((char *) ncodes, sizeof(_codeidx_t) * (ncodes_size + 8)); + + ncodes_size += 8; + } + + /* + * Shift things around to insert the code if necessary. + */ + if (i < ncodes_used) { + for (j = ncodes_used; j > i; j--) { + ncodes[j].code = ncodes[j - 1].code; + ncodes[j].idx = ncodes[j - 1].idx; + } + } + ncodes[i].code = code; + ncodes[i].idx = make_number(num, denom); + + ncodes_used++; +} + +/* + * This routine assumes that the line is a valid Unicode Character Database + * entry. + */ +static void +read_cdata(FILE *in) +{ + ac_uint4 i, lineno, skip, code, ccl_code; + short wnum, neg, number[2], compat; + char line[512], *s, *e, *first_prop; + + lineno = skip = 0; + while (fgets(line, sizeof(line), in)) { + if( (s=strchr(line, '\n')) ) *s = '\0'; + lineno++; + + /* + * Skip blank lines and lines that start with a '#'. + */ + if (line[0] == 0 || line[0] == '#') + continue; + + /* + * If lines need to be skipped, do it here. + */ + if (skip) { + skip--; + continue; + } + + /* + * Collect the code. The code can be up to 6 hex digits in length to + * allow surrogates to be specified. + */ + for (s = line, i = code = 0; *s != ';' && i < 6; i++, s++) { + code <<= 4; + if (*s >= '0' && *s <= '9') + code += *s - '0'; + else if (*s >= 'A' && *s <= 'F') + code += (*s - 'A') + 10; + else if (*s >= 'a' && *s <= 'f') + code += (*s - 'a') + 10; + } + + /* + * Handle the following special cases: + * 1. 4E00-9FA5 CJK Ideographs. + * 2. AC00-D7A3 Hangul Syllables. + * 3. D800-DFFF Surrogates. + * 4. E000-F8FF Private Use Area. + * 5. F900-FA2D Han compatibility. + * ...Plus additional ranges in newer Unicode versions... + */ + switch (code) { + case 0x3400: + /* CJK Ideograph Extension A */ + add_range(0x3400, 0x4db5, "Lo", "L"); + + add_range(0x3400, 0x4db5, "Cp", 0); + + skip = 1; + break; + case 0x4e00: + /* + * The Han ideographs. + */ + add_range(0x4e00, 0x9fff, "Lo", "L"); + + /* + * Add the characters to the defined category. + */ + add_range(0x4e00, 0x9fa5, "Cp", 0); + + skip = 1; + break; + case 0xac00: + /* + * The Hangul syllables. + */ + add_range(0xac00, 0xd7a3, "Lo", "L"); + + /* + * Add the characters to the defined category. + */ + add_range(0xac00, 0xd7a3, "Cp", 0); + + skip = 1; + break; + case 0xd800: + /* + * Make a range of all surrogates and assume some default + * properties. + */ + add_range(0x010000, 0x10ffff, "Cs", "L"); + skip = 5; + break; + case 0xe000: + /* + * The Private Use area. Add with a default set of properties. + */ + add_range(0xe000, 0xf8ff, "Co", "L"); + skip = 1; + break; + case 0xf900: + /* + * The CJK compatibility area. + */ + add_range(0xf900, 0xfaff, "Lo", "L"); + + /* + * Add the characters to the defined category. + */ + add_range(0xf900, 0xfaff, "Cp", 0); + + skip = 1; + break; + case 0x20000: + /* CJK Ideograph Extension B */ + add_range(0x20000, 0x2a6d6, "Lo", "L"); + + add_range(0x20000, 0x2a6d6, "Cp", 0); + + skip = 1; + break; + case 0xf0000: + /* Plane 15 private use */ + add_range(0xf0000, 0xffffd, "Co", "L"); + skip = 1; + break; + + case 0x100000: + /* Plane 16 private use */ + add_range(0x100000, 0x10fffd, "Co", "L"); + skip = 1; + break; + } + + if (skip) + continue; + + /* + * Add the code to the defined category. + */ + ordered_range_insert(code, "Cp", 2); + + /* + * Locate the first character property field. + */ + for (i = 0; *s != 0 && i < 2; s++) { + if (*s == ';') + i++; + } + for (e = s; *e && *e != ';'; e++) ; + + first_prop = s; + + ordered_range_insert(code, s, e - s); + + /* + * Locate the combining class code. + */ + for (s = e; *s != 0 && i < 3; s++) { + if (*s == ';') + i++; + } + + /* + * Convert the combining class code from decimal. + */ + for (ccl_code = 0, e = s; *e && *e != ';'; e++) + ccl_code = (ccl_code * 10) + (*e - '0'); + + /* + * Add the code if it not 0. + */ + if (ccl_code != 0) + ordered_ccl_insert(code, ccl_code); + + /* + * Locate the second character property field. + */ + for (s = e; *s != 0 && i < 4; s++) { + if (*s == ';') + i++; + } + for (e = s; *e && *e != ';'; e++) ; + + ordered_range_insert(code, s, e - s); + + /* + * Check for a decomposition. + */ + s = ++e; + if (*s != ';') { + compat = *s == '<'; + if (compat) { + /* + * Skip compatibility formatting tag. + */ + while (*s++ != '>'); + } + /* + * Collect the codes of the decomposition. + */ + for (dectmp_size = 0; *s != ';'; ) { + /* + * Skip all leading non-hex digits. + */ + while (!ishdigit(*s)) + s++; + + for (dectmp[dectmp_size] = 0; ishdigit(*s); s++) { + dectmp[dectmp_size] <<= 4; + if (*s >= '0' && *s <= '9') + dectmp[dectmp_size] += *s - '0'; + else if (*s >= 'A' && *s <= 'F') + dectmp[dectmp_size] += (*s - 'A') + 10; + else if (*s >= 'a' && *s <= 'f') + dectmp[dectmp_size] += (*s - 'a') + 10; + } + dectmp_size++; + } + + /* + * If there are any codes in the temporary decomposition array, + * then add the character with its decomposition. + */ + if (dectmp_size > 0) { + if (!compat) { + add_decomp(code, 0); + } + add_decomp(code, 1); + } + } + + /* + * Skip to the number field. + */ + for (i = 0; i < 3 && *s; s++) { + if (*s == ';') + i++; + } + + /* + * Scan the number in. + */ + number[0] = number[1] = 0; + for (e = s, neg = wnum = 0; *e && *e != ';'; e++) { + if (*e == '-') { + neg = 1; + continue; + } + + if (*e == '/') { + /* + * Move the the denominator of the fraction. + */ + if (neg) + number[wnum] *= -1; + neg = 0; + e++; + wnum++; + } + number[wnum] = (number[wnum] * 10) + (*e - '0'); + } + + if (e > s) { + /* + * Adjust the denominator in case of integers and add the number. + */ + if (wnum == 0) + number[1] = 1; + + add_number(code, number[0], number[1]); + } + + /* + * Skip to the start of the possible case mappings. + */ + for (s = e, i = 0; i < 4 && *s; s++) { + if (*s == ';') + i++; + } + + /* + * Collect the case mappings. + */ + cases[0] = cases[1] = cases[2] = 0; + for (i = 0; i < 3; i++) { + while (ishdigit(*s)) { + cases[i] <<= 4; + if (*s >= '0' && *s <= '9') + cases[i] += *s - '0'; + else if (*s >= 'A' && *s <= 'F') + cases[i] += (*s - 'A') + 10; + else if (*s >= 'a' && *s <= 'f') + cases[i] += (*s - 'a') + 10; + s++; + } + if (*s == ';') + s++; + } + if (!strncmp(first_prop,"Lt",2) && (cases[0] || cases[1])) + /* + * Add the upper and lower mappings for a title case character. + */ + add_title(code); + else if (cases[1]) + /* + * Add the lower and title case mappings for the upper case + * character. + */ + add_upper(code); + else if (cases[0]) + /* + * Add the upper and title case mappings for the lower case + * character. + */ + add_lower(code); + } +} + +static _decomp_t * +find_decomp(ac_uint4 code, short compat) +{ + long l, r, m; + _decomp_t *decs; + + l = 0; + r = (compat ? kdecomps_used : decomps_used) - 1; + decs = compat ? kdecomps : decomps; + while (l <= r) { + m = (l + r) >> 1; + if (code > decs[m].code) + l = m + 1; + else if (code < decs[m].code) + r = m - 1; + else + return &decs[m]; + } + return 0; +} + +static void +decomp_it(_decomp_t *d, short compat) +{ + ac_uint4 i; + _decomp_t *dp; + + for (i = 0; i < d->used; i++) { + if ((dp = find_decomp(d->decomp[i], compat)) != 0) + decomp_it(dp, compat); + else + dectmp[dectmp_size++] = d->decomp[i]; + } +} + +/* + * Expand all decompositions by recursively decomposing each character + * in the decomposition. + */ +static void +expand_decomp(void) +{ + ac_uint4 i; + + for (i = 0; i < decomps_used; i++) { + dectmp_size = 0; + decomp_it(&decomps[i], 0); + if (dectmp_size > 0) + add_decomp(decomps[i].code, 0); + } + + for (i = 0; i < kdecomps_used; i++) { + dectmp_size = 0; + decomp_it(&kdecomps[i], 1); + if (dectmp_size > 0) + add_decomp(kdecomps[i].code, 1); + } +} + +static int +cmpcomps(const void *v_comp1, const void *v_comp2) +{ + const _comp_t *comp1 = v_comp1, *comp2 = v_comp2; + long diff = comp1->code1 - comp2->code1; + + if (!diff) + diff = comp1->code2 - comp2->code2; + return (int) diff; +} + +/* + * Load composition exclusion data + */ +static void +read_compexdata(FILE *in) +{ + ac_uint2 i; + ac_uint4 code; + char line[512], *s; + + (void) memset((char *) compexs, 0, sizeof(compexs)); + + while (fgets(line, sizeof(line), in)) { + if( (s=strchr(line, '\n')) ) *s = '\0'; + /* + * Skip blank lines and lines that start with a '#'. + */ + if (line[0] == 0 || line[0] == '#') + continue; + + /* + * Collect the code. Assume max 6 digits + */ + + for (s = line, i = code = 0; *s != '#' && i < 6; i++, s++) { + if (isspace((unsigned char)*s)) break; + code <<= 4; + if (*s >= '0' && *s <= '9') + code += *s - '0'; + else if (*s >= 'A' && *s <= 'F') + code += (*s - 'A') + 10; + else if (*s >= 'a' && *s <= 'f') + code += (*s - 'a') + 10; + } + COMPEX_SET(code); + } +} + +/* + * Creates array of compositions from decomposition array + */ +static void +create_comps(void) +{ + ac_uint4 i, cu; + + comps = (_comp_t *) malloc(comps_used * sizeof(_comp_t)); + + for (i = cu = 0; i < decomps_used; i++) { + if (decomps[i].used != 2 || COMPEX_TEST(decomps[i].code)) + continue; + comps[cu].comp = decomps[i].code; + comps[cu].count = 2; + comps[cu].code1 = decomps[i].decomp[0]; + comps[cu].code2 = decomps[i].decomp[1]; + cu++; + } + comps_used = cu; + qsort(comps, comps_used, sizeof(_comp_t), cmpcomps); +} + +#if HARDCODE_DATA +static void +write_case(FILE *out, _case_t *tab, int num, int first) +{ + int i; + + for (i=0; i<num; i++) { + if (first) first = 0; + else fprintf(out, ","); + fprintf(out, "\n\t0x%08lx, 0x%08lx, 0x%08lx", + (unsigned long) tab[i].key, (unsigned long) tab[i].other1, + (unsigned long) tab[i].other2); + } +} + +#define PREF "static const " + +#endif + +static void +write_cdata(char *opath) +{ + FILE *out; + ac_uint4 bytes; + ac_uint4 i, idx, nprops; +#if !(HARDCODE_DATA) + ac_uint2 casecnt[2]; +#endif + char path[BUFSIZ]; +#if HARDCODE_DATA + int j, k; + + /***************************************************************** + * + * Generate the ctype data. + * + *****************************************************************/ + + /* + * Open the output file. + */ + snprintf(path, sizeof path, "%s" LDAP_DIRSEP "uctable.h", opath); + if ((out = fopen(path, "w")) == 0) + return; +#else + /* + * Open the ctype.dat file. + */ + snprintf(path, sizeof path, "%s" LDAP_DIRSEP "ctype.dat", opath); + if ((out = fopen(path, "wb")) == 0) + return; +#endif + + /* + * Collect the offsets for the properties. The offsets array is + * on a 4-byte boundary to keep things efficient for architectures + * that need such a thing. + */ + for (i = idx = 0; i < NUMPROPS; i++) { + propcnt[i] = (proptbl[i].used != 0) ? idx : 0xffff; + idx += proptbl[i].used; + } + + /* + * Add the sentinel index which is used by the binary search as the upper + * bound for a search. + */ + propcnt[i] = idx; + + /* + * Record the actual number of property lists. This may be different than + * the number of offsets actually written because of aligning on a 4-byte + * boundary. + */ + hdr[1] = NUMPROPS; + + /* + * Calculate the byte count needed and pad the property counts array to a + * 4-byte boundary. + */ + if ((bytes = sizeof(ac_uint2) * (NUMPROPS + 1)) & 3) + bytes += 4 - (bytes & 3); + nprops = bytes / sizeof(ac_uint2); + bytes += sizeof(ac_uint4) * idx; + +#if HARDCODE_DATA + fprintf(out, PREF "ac_uint4 _ucprop_size = %d;\n\n", NUMPROPS); + + fprintf(out, PREF "ac_uint2 _ucprop_offsets[] = {"); + + for (i = 0; i<nprops; i++) { + if (i) fprintf(out, ","); + if (!(i&7)) fprintf(out, "\n\t"); + else fprintf(out, " "); + fprintf(out, "0x%04x", propcnt[i]); + } + fprintf(out, "\n};\n\n"); + + fprintf(out, PREF "ac_uint4 _ucprop_ranges[] = {"); + + k = 0; + for (i = 0; i < NUMPROPS; i++) { + if (proptbl[i].used > 0) { + for (j=0; j<proptbl[i].used; j++) { + if (k) fprintf(out, ","); + if (!(k&3)) fprintf(out,"\n\t"); + else fprintf(out, " "); + k++; + fprintf(out, "0x%08lx", (unsigned long) proptbl[i].ranges[j]); + } + } + } + fprintf(out, "\n};\n\n"); +#else + /* + * Write the header. + */ + fwrite((char *) hdr, sizeof(ac_uint2), 2, out); + + /* + * Write the byte count. + */ + fwrite((char *) &bytes, sizeof(ac_uint4), 1, out); + + /* + * Write the property list counts. + */ + fwrite((char *) propcnt, sizeof(ac_uint2), nprops, out); + + /* + * Write the property lists. + */ + for (i = 0; i < NUMPROPS; i++) { + if (proptbl[i].used > 0) + fwrite((char *) proptbl[i].ranges, sizeof(ac_uint4), + proptbl[i].used, out); + } + + fclose(out); +#endif + + /***************************************************************** + * + * Generate the case mapping data. + * + *****************************************************************/ + +#if HARDCODE_DATA + fprintf(out, PREF "ac_uint4 _uccase_size = %ld;\n\n", + (long) (upper_used + lower_used + title_used)); + + fprintf(out, PREF "ac_uint2 _uccase_len[2] = {%ld, %ld};\n\n", + (long) upper_used, (long) lower_used); + fprintf(out, PREF "ac_uint4 _uccase_map[] = {"); + + if (upper_used > 0) + /* + * Write the upper case table. + */ + write_case(out, upper, upper_used, 1); + + if (lower_used > 0) + /* + * Write the lower case table. + */ + write_case(out, lower, lower_used, !upper_used); + + if (title_used > 0) + /* + * Write the title case table. + */ + write_case(out, title, title_used, !(upper_used||lower_used)); + + if (!(upper_used || lower_used || title_used)) + fprintf(out, "\t0"); + + fprintf(out, "\n};\n\n"); +#else + /* + * Open the case.dat file. + */ + snprintf(path, sizeof path, "%s" LDAP_DIRSEP "case.dat", opath); + if ((out = fopen(path, "wb")) == 0) + return; + + /* + * Write the case mapping tables. + */ + hdr[1] = upper_used + lower_used + title_used; + casecnt[0] = upper_used; + casecnt[1] = lower_used; + + /* + * Write the header. + */ + fwrite((char *) hdr, sizeof(ac_uint2), 2, out); + + /* + * Write the upper and lower case table sizes. + */ + fwrite((char *) casecnt, sizeof(ac_uint2), 2, out); + + if (upper_used > 0) + /* + * Write the upper case table. + */ + fwrite((char *) upper, sizeof(_case_t), upper_used, out); + + if (lower_used > 0) + /* + * Write the lower case table. + */ + fwrite((char *) lower, sizeof(_case_t), lower_used, out); + + if (title_used > 0) + /* + * Write the title case table. + */ + fwrite((char *) title, sizeof(_case_t), title_used, out); + + fclose(out); +#endif + + /***************************************************************** + * + * Generate the composition data. + * + *****************************************************************/ + + /* + * Create compositions from decomposition data + */ + create_comps(); + +#if HARDCODE_DATA + fprintf(out, PREF "ac_uint4 _uccomp_size = %ld;\n\n", + comps_used * 4L); + + fprintf(out, PREF "ac_uint4 _uccomp_data[] = {"); + + /* + * Now, if comps exist, write them out. + */ + if (comps_used > 0) { + for (i=0; i<comps_used; i++) { + if (i) fprintf(out, ","); + fprintf(out, "\n\t0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx", + (unsigned long) comps[i].comp, (unsigned long) comps[i].count, + (unsigned long) comps[i].code1, (unsigned long) comps[i].code2); + } + } else { + fprintf(out, "\t0"); + } + fprintf(out, "\n};\n\n"); +#else + /* + * Open the comp.dat file. + */ + snprintf(path, sizeof path, "%s" LDAP_DIRSEP "comp.dat", opath); + if ((out = fopen(path, "wb")) == 0) + return; + + /* + * Write the header. + */ + hdr[1] = (ac_uint2) comps_used * 4; + fwrite((char *) hdr, sizeof(ac_uint2), 2, out); + + /* + * Write out the byte count to maintain header size. + */ + bytes = comps_used * sizeof(_comp_t); + fwrite((char *) &bytes, sizeof(ac_uint4), 1, out); + + /* + * Now, if comps exist, write them out. + */ + if (comps_used > 0) + fwrite((char *) comps, sizeof(_comp_t), comps_used, out); + + fclose(out); +#endif + + /***************************************************************** + * + * Generate the decomposition data. + * + *****************************************************************/ + + /* + * Fully expand all decompositions before generating the output file. + */ + expand_decomp(); + +#if HARDCODE_DATA + fprintf(out, PREF "ac_uint4 _ucdcmp_size = %ld;\n\n", + decomps_used * 2L); + + fprintf(out, PREF "ac_uint4 _ucdcmp_nodes[] = {"); + + if (decomps_used) { + /* + * Write the list of decomp nodes. + */ + for (i = idx = 0; i < decomps_used; i++) { + fprintf(out, "\n\t0x%08lx, 0x%08lx,", + (unsigned long) decomps[i].code, (unsigned long) idx); + idx += decomps[i].used; + } + + /* + * Write the sentinel index as the last decomp node. + */ + fprintf(out, "\n\t0x%08lx\n};\n\n", (unsigned long) idx); + + fprintf(out, PREF "ac_uint4 _ucdcmp_decomp[] = {"); + /* + * Write the decompositions themselves. + */ + k = 0; + for (i = 0; i < decomps_used; i++) + for (j=0; j<decomps[i].used; j++) { + if (k) fprintf(out, ","); + if (!(k&3)) fprintf(out,"\n\t"); + else fprintf(out, " "); + k++; + fprintf(out, "0x%08lx", (unsigned long) decomps[i].decomp[j]); + } + fprintf(out, "\n};\n\n"); + } +#else + /* + * Open the decomp.dat file. + */ + snprintf(path, sizeof path, "%s" LDAP_DIRSEP "decomp.dat", opath); + if ((out = fopen(path, "wb")) == 0) + return; + + hdr[1] = decomps_used; + + /* + * Write the header. + */ + fwrite((char *) hdr, sizeof(ac_uint2), 2, out); + + /* + * Write a temporary byte count which will be calculated as the + * decompositions are written out. + */ + bytes = 0; + fwrite((char *) &bytes, sizeof(ac_uint4), 1, out); + + if (decomps_used) { + /* + * Write the list of decomp nodes. + */ + for (i = idx = 0; i < decomps_used; i++) { + fwrite((char *) &decomps[i].code, sizeof(ac_uint4), 1, out); + fwrite((char *) &idx, sizeof(ac_uint4), 1, out); + idx += decomps[i].used; + } + + /* + * Write the sentinel index as the last decomp node. + */ + fwrite((char *) &idx, sizeof(ac_uint4), 1, out); + + /* + * Write the decompositions themselves. + */ + for (i = 0; i < decomps_used; i++) + fwrite((char *) decomps[i].decomp, sizeof(ac_uint4), + decomps[i].used, out); + + /* + * Seek back to the beginning and write the byte count. + */ + bytes = (sizeof(ac_uint4) * idx) + + (sizeof(ac_uint4) * ((hdr[1] << 1) + 1)); + fseek(out, sizeof(ac_uint2) << 1, 0L); + fwrite((char *) &bytes, sizeof(ac_uint4), 1, out); + + fclose(out); + } +#endif + +#ifdef HARDCODE_DATA + fprintf(out, PREF "ac_uint4 _uckdcmp_size = %ld;\n\n", + kdecomps_used * 2L); + + fprintf(out, PREF "ac_uint4 _uckdcmp_nodes[] = {"); + + if (kdecomps_used) { + /* + * Write the list of kdecomp nodes. + */ + for (i = idx = 0; i < kdecomps_used; i++) { + fprintf(out, "\n\t0x%08lx, 0x%08lx,", + (unsigned long) kdecomps[i].code, (unsigned long) idx); + idx += kdecomps[i].used; + } + + /* + * Write the sentinel index as the last decomp node. + */ + fprintf(out, "\n\t0x%08lx\n};\n\n", (unsigned long) idx); + + fprintf(out, PREF "ac_uint4 _uckdcmp_decomp[] = {"); + + /* + * Write the decompositions themselves. + */ + k = 0; + for (i = 0; i < kdecomps_used; i++) + for (j=0; j<kdecomps[i].used; j++) { + if (k) fprintf(out, ","); + if (!(k&3)) fprintf(out,"\n\t"); + else fprintf(out, " "); + k++; + fprintf(out, "0x%08lx", (unsigned long) kdecomps[i].decomp[j]); + } + fprintf(out, "\n};\n\n"); + } +#else + /* + * Open the kdecomp.dat file. + */ + snprintf(path, sizeof path, "%s" LDAP_DIRSEP "kdecomp.dat", opath); + if ((out = fopen(path, "wb")) == 0) + return; + + hdr[1] = kdecomps_used; + + /* + * Write the header. + */ + fwrite((char *) hdr, sizeof(ac_uint2), 2, out); + + /* + * Write a temporary byte count which will be calculated as the + * decompositions are written out. + */ + bytes = 0; + fwrite((char *) &bytes, sizeof(ac_uint4), 1, out); + + if (kdecomps_used) { + /* + * Write the list of kdecomp nodes. + */ + for (i = idx = 0; i < kdecomps_used; i++) { + fwrite((char *) &kdecomps[i].code, sizeof(ac_uint4), 1, out); + fwrite((char *) &idx, sizeof(ac_uint4), 1, out); + idx += kdecomps[i].used; + } + + /* + * Write the sentinel index as the last decomp node. + */ + fwrite((char *) &idx, sizeof(ac_uint4), 1, out); + + /* + * Write the decompositions themselves. + */ + for (i = 0; i < kdecomps_used; i++) + fwrite((char *) kdecomps[i].decomp, sizeof(ac_uint4), + kdecomps[i].used, out); + + /* + * Seek back to the beginning and write the byte count. + */ + bytes = (sizeof(ac_uint4) * idx) + + (sizeof(ac_uint4) * ((hdr[1] << 1) + 1)); + fseek(out, sizeof(ac_uint2) << 1, 0L); + fwrite((char *) &bytes, sizeof(ac_uint4), 1, out); + + fclose(out); + } +#endif + + /***************************************************************** + * + * Generate the combining class data. + * + *****************************************************************/ +#ifdef HARDCODE_DATA + fprintf(out, PREF "ac_uint4 _uccmcl_size = %ld;\n\n", (long) ccl_used); + + fprintf(out, PREF "ac_uint4 _uccmcl_nodes[] = {"); + + if (ccl_used > 0) { + /* + * Write the combining class ranges out. + */ + for (i = 0; i<ccl_used; i++) { + if (i) fprintf(out, ","); + if (!(i&3)) fprintf(out, "\n\t"); + else fprintf(out, " "); + fprintf(out, "0x%08lx", (unsigned long) ccl[i]); + } + } else { + fprintf(out, "\t0"); + } + fprintf(out, "\n};\n\n"); +#else + /* + * Open the cmbcl.dat file. + */ + snprintf(path, sizeof path, "%s" LDAP_DIRSEP "cmbcl.dat", opath); + if ((out = fopen(path, "wb")) == 0) + return; + + /* + * Set the number of ranges used. Each range has a combining class which + * means each entry is a 3-tuple. + */ + hdr[1] = ccl_used / 3; + + /* + * Write the header. + */ + fwrite((char *) hdr, sizeof(ac_uint2), 2, out); + + /* + * Write out the byte count to maintain header size. + */ + bytes = ccl_used * sizeof(ac_uint4); + fwrite((char *) &bytes, sizeof(ac_uint4), 1, out); + + if (ccl_used > 0) + /* + * Write the combining class ranges out. + */ + fwrite((char *) ccl, sizeof(ac_uint4), ccl_used, out); + + fclose(out); +#endif + + /***************************************************************** + * + * Generate the number data. + * + *****************************************************************/ + +#if HARDCODE_DATA + fprintf(out, PREF "ac_uint4 _ucnum_size = %lu;\n\n", + (unsigned long)ncodes_used<<1); + + fprintf(out, PREF "ac_uint4 _ucnum_nodes[] = {"); + + /* + * Now, if number mappings exist, write them out. + */ + if (ncodes_used > 0) { + for (i = 0; i<ncodes_used; i++) { + if (i) fprintf(out, ","); + if (!(i&1)) fprintf(out, "\n\t"); + else fprintf(out, " "); + fprintf(out, "0x%08lx, 0x%08lx", + (unsigned long) ncodes[i].code, (unsigned long) ncodes[i].idx); + } + fprintf(out, "\n};\n\n"); + + fprintf(out, PREF "short _ucnum_vals[] = {"); + for (i = 0; i<nums_used; i++) { + if (i) fprintf(out, ","); + if (!(i&3)) fprintf(out, "\n\t"); + else fprintf(out, " "); + if (nums[i].numerator < 0) { + fprintf(out, "%6d, 0x%04x", + nums[i].numerator, nums[i].denominator); + } else { + fprintf(out, "0x%04x, 0x%04x", + nums[i].numerator, nums[i].denominator); + } + } + fprintf(out, "\n};\n\n"); + } +#else + /* + * Open the num.dat file. + */ + snprintf(path, sizeof path, "%s" LDAP_DIRSEP "num.dat", opath); + if ((out = fopen(path, "wb")) == 0) + return; + + /* + * The count part of the header will be the total number of codes that + * have numbers. + */ + hdr[1] = (ac_uint2) (ncodes_used << 1); + bytes = (ncodes_used * sizeof(_codeidx_t)) + (nums_used * sizeof(_num_t)); + + /* + * Write the header. + */ + fwrite((char *) hdr, sizeof(ac_uint2), 2, out); + + /* + * Write out the byte count to maintain header size. + */ + fwrite((char *) &bytes, sizeof(ac_uint4), 1, out); + + /* + * Now, if number mappings exist, write them out. + */ + if (ncodes_used > 0) { + fwrite((char *) ncodes, sizeof(_codeidx_t), ncodes_used, out); + fwrite((char *) nums, sizeof(_num_t), nums_used, out); + } +#endif + + fclose(out); +} + +static void +usage(char *prog) +{ + fprintf(stderr, + "Usage: %s [-o output-directory|-x composition-exclusions]", prog); + fprintf(stderr, " datafile1 datafile2 ...\n\n"); + fprintf(stderr, + "-o output-directory\n\t\tWrite the output files to a different"); + fprintf(stderr, " directory (default: .).\n"); + fprintf(stderr, + "-x composition-exclusion\n\t\tFile of composition codes"); + fprintf(stderr, " that should be excluded.\n"); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + FILE *in; + char *prog, *opath; + + prog = lutil_progname( "ucgendat", argc, argv ); + + opath = 0; + in = stdin; + + argc--; + argv++; + + while (argc > 0) { + if (argv[0][0] == '-') { + switch (argv[0][1]) { + case 'o': + argc--; + argv++; + opath = argv[0]; + break; + case 'x': + argc--; + argv++; + if ((in = fopen(argv[0], "r")) == 0) + fprintf(stderr, + "%s: unable to open composition exclusion file %s\n", + prog, argv[0]); + else { + read_compexdata(in); + fclose(in); + in = 0; + } + break; + default: + usage(prog); + } + } else { + if (in != stdin && in != NULL) + fclose(in); + if ((in = fopen(argv[0], "r")) == 0) + fprintf(stderr, "%s: unable to open ctype file %s\n", + prog, argv[0]); + else { + read_cdata(in); + fclose(in); + in = 0; + } + } + argc--; + argv++; + } + + if (opath == 0) + opath = "."; + write_cdata(opath); + + return 0; +} diff --git a/libraries/liblunicode/ucdata/ucpgba.c b/libraries/liblunicode/ucdata/ucpgba.c new file mode 100644 index 0000000..30afb56 --- /dev/null +++ b/libraries/liblunicode/ucdata/ucpgba.c @@ -0,0 +1,750 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Copyright 2001 Computing Research Labs, New Mexico State University + * + * 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 COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY 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. + */ +/* $Id: ucpgba.c,v 1.5 2001/01/02 18:46:20 mleisher Exp $ */ + +#include "portable.h" + +#include <stdio.h> +#include <stdlib.h> + +#include "ucdata.h" +#include "ucpgba.h" + +/* + * These macros are used while reordering of RTL runs of text for the + * special case of non-spacing characters being in runs of weakly + * directional text. They check for weak and non-spacing, and digits and + * non-spacing. + */ +#define ISWEAKSPECIAL(cc) ucisprop(cc, UC_EN|UC_ES|UC_MN, UC_ET|UC_AN|UC_CS) +#define ISDIGITSPECIAL(cc) ucisprop(cc, UC_ND|UC_MN, 0) + +/* + * These macros are used while breaking a string into runs of text in + * different directions. Descriptions: + * + * ISLTR_LTR - Test for members of an LTR run in an LTR context. This looks + * for characters with ltr, non-spacing, weak, and neutral + * properties. + * + * ISRTL_RTL - Test for members of an RTL run in an RTL context. This looks + * for characters with rtl, non-spacing, weak, and neutral + * properties. + * + * ISRTL_NEUTRAL - Test for RTL or neutral characters. + * + * ISWEAK_NEUTRAL - Test for weak or neutral characters. + */ +#define ISLTR_LTR(cc) ucisprop(cc, UC_L|UC_MN|UC_EN|UC_ES,\ + UC_ET|UC_CS|UC_B|UC_S|UC_WS|UC_ON) + +#define ISRTL_RTL(cc) ucisprop(cc, UC_R|UC_MN|UC_EN|UC_ES,\ + UC_ET|UC_AN|UC_CS|UC_B|UC_S|UC_WS|UC_ON) + +#define ISRTL_NEUTRAL(cc) ucisprop(cc, UC_R, UC_B|UC_S|UC_WS|UC_ON) +#define ISWEAK_NEUTRAL(cc) ucisprop(cc, UC_EN|UC_ES, \ + UC_B|UC_S|UC_WS|UC_ON|UC_ET|UC_AN|UC_CS) + +/* + * This table is temporarily hard-coded here until it can be constructed + * automatically somehow. + */ +static unsigned long _symmetric_pairs[] = { + 0x0028, 0x0029, 0x0029, 0x0028, 0x003C, 0x003E, 0x003E, 0x003C, + 0x005B, 0x005D, 0x005D, 0x005B, 0x007B, 0x007D, 0x007D, 0x007B, + 0x2045, 0x2046, 0x2046, 0x2045, 0x207D, 0x207E, 0x207E, 0x207D, + 0x208D, 0x208E, 0x208E, 0x208D, 0x3008, 0x3009, 0x3009, 0x3008, + 0x300A, 0x300B, 0x300B, 0x300A, 0x300C, 0x300D, 0x300D, 0x300C, + 0x300E, 0x300F, 0x300F, 0x300E, 0x3010, 0x3011, 0x3011, 0x3010, + 0x3014, 0x3015, 0x3015, 0x3014, 0x3016, 0x3017, 0x3017, 0x3016, + 0x3018, 0x3019, 0x3019, 0x3018, 0x301A, 0x301B, 0x301B, 0x301A, + 0xFD3E, 0xFD3F, 0xFD3F, 0xFD3E, 0xFE59, 0xFE5A, 0xFE5A, 0xFE59, + 0xFE5B, 0xFE5C, 0xFE5C, 0xFE5B, 0xFE5D, 0xFE5E, 0xFE5E, 0xFE5D, + 0xFF08, 0xFF09, 0xFF09, 0xFF08, 0xFF3B, 0xFF3D, 0xFF3D, 0xFF3B, + 0xFF5B, 0xFF5D, 0xFF5D, 0xFF5B, 0xFF62, 0xFF63, 0xFF63, 0xFF62, +}; + +static int _symmetric_pairs_size = +sizeof(_symmetric_pairs)/sizeof(_symmetric_pairs[0]); + +/* + * This routine looks up the other form of a symmetric pair. + */ +static unsigned long +_ucsymmetric_pair(unsigned long c) +{ + int i; + + for (i = 0; i < _symmetric_pairs_size; i += 2) { + if (_symmetric_pairs[i] == c) + return _symmetric_pairs[i+1]; + } + return c; +} + +/* + * This routine creates a new run, copies the text into it, links it into the + * logical text order chain and returns it to the caller to be linked into + * the visual text order chain. + */ +static ucrun_t * +_add_run(ucstring_t *str, unsigned long *src, + unsigned long start, unsigned long end, int direction) +{ + long i, t; + ucrun_t *run; + + run = (ucrun_t *) malloc(sizeof(ucrun_t)); + run->visual_next = run->visual_prev = 0; + run->direction = direction; + + run->cursor = ~0; + + run->chars = (unsigned long *) + malloc(sizeof(unsigned long) * ((end - start) << 1)); + run->positions = run->chars + (end - start); + + run->source = src; + run->start = start; + run->end = end; + + if (direction == UCPGBA_RTL) { + /* + * Copy the source text into the run in reverse order and select + * replacements for the pairwise punctuation and the <> characters. + */ + for (i = 0, t = end - 1; start < end; start++, t--, i++) { + run->positions[i] = t; + if (ucissymmetric(src[t]) || src[t] == '<' || src[t] == '>') + run->chars[i] = _ucsymmetric_pair(src[t]); + else + run->chars[i] = src[t]; + } + } else { + /* + * Copy the source text into the run directly. + */ + for (i = start; i < end; i++) { + run->positions[i - start] = i; + run->chars[i - start] = src[i]; + } + } + + /* + * Add the run to the logical list for cursor traversal. + */ + if (str->logical_first == 0) + str->logical_first = str->logical_last = run; + else { + run->logical_prev = str->logical_last; + str->logical_last->logical_next = run; + str->logical_last = run; + } + + return run; +} + +static void +_ucadd_rtl_segment(ucstring_t *str, unsigned long *source, unsigned long start, + unsigned long end) +{ + unsigned long s, e; + ucrun_t *run, *lrun; + + /* + * This is used to splice runs into strings with overall LTR direction. + * The `lrun' variable will never be NULL because at least one LTR run was + * added before this RTL run. + */ + lrun = str->visual_last; + + for (e = s = start; s < end;) { + for (; e < end && ISRTL_NEUTRAL(source[e]); e++) ; + + if (e > s) { + run = _add_run(str, source, s, e, UCPGBA_RTL); + + /* + * Add the run to the visual list for cursor traversal. + */ + if (str->visual_first != 0) { + if (str->direction == UCPGBA_LTR) { + run->visual_prev = lrun; + run->visual_next = lrun->visual_next; + if (lrun->visual_next != 0) + lrun->visual_next->visual_prev = run; + lrun->visual_next = run; + if (lrun == str->visual_last) + str->visual_last = run; + } else { + run->visual_next = str->visual_first; + str->visual_first->visual_prev = run; + str->visual_first = run; + } + } else + str->visual_first = str->visual_last = run; + } + + /* + * Handle digits in a special way. This makes sure the weakly + * directional characters appear on the expected sides of a number + * depending on whether that number is Arabic or not. + */ + for (s = e; e < end && ISWEAKSPECIAL(source[e]); e++) { + if (!ISDIGITSPECIAL(source[e]) && + (e + 1 == end || !ISDIGITSPECIAL(source[e + 1]))) + break; + } + + if (e > s) { + run = _add_run(str, source, s, e, UCPGBA_LTR); + + /* + * Add the run to the visual list for cursor traversal. + */ + if (str->visual_first != 0) { + if (str->direction == UCPGBA_LTR) { + run->visual_prev = lrun; + run->visual_next = lrun->visual_next; + if (lrun->visual_next != 0) + lrun->visual_next->visual_prev = run; + lrun->visual_next = run; + if (lrun == str->visual_last) + str->visual_last = run; + } else { + run->visual_next = str->visual_first; + str->visual_first->visual_prev = run; + str->visual_first = run; + } + } else + str->visual_first = str->visual_last = run; + } + + /* + * Collect all weak non-digit sequences for an RTL segment. These + * will appear as part of the next RTL segment or will be added as + * an RTL segment by themselves. + */ + for (s = e; e < end && ucisweak(source[e]) && !ucisdigit(source[e]); + e++) ; + } + + /* + * Capture any weak non-digit sequences that occur at the end of the RTL + * run. + */ + if (e > s) { + run = _add_run(str, source, s, e, UCPGBA_RTL); + + /* + * Add the run to the visual list for cursor traversal. + */ + if (str->visual_first != 0) { + if (str->direction == UCPGBA_LTR) { + run->visual_prev = lrun; + run->visual_next = lrun->visual_next; + if (lrun->visual_next != 0) + lrun->visual_next->visual_prev = run; + lrun->visual_next = run; + if (lrun == str->visual_last) + str->visual_last = run; + } else { + run->visual_next = str->visual_first; + str->visual_first->visual_prev = run; + str->visual_first = run; + } + } else + str->visual_first = str->visual_last = run; + } +} + +static void +_ucadd_ltr_segment(ucstring_t *str, unsigned long *source, unsigned long start, + unsigned long end) +{ + ucrun_t *run; + + run = _add_run(str, source, start, end, UCPGBA_LTR); + + /* + * Add the run to the visual list for cursor traversal. + */ + if (str->visual_first != 0) { + if (str->direction == UCPGBA_LTR) { + run->visual_prev = str->visual_last; + str->visual_last->visual_next = run; + str->visual_last = run; + } else { + run->visual_next = str->visual_first; + str->visual_first->visual_prev = run; + str->visual_first = run; + } + } else + str->visual_first = str->visual_last = run; +} + +ucstring_t * +ucstring_create(unsigned long *source, unsigned long start, unsigned long end, + int default_direction, int cursor_motion) +{ + int rtl_first; + unsigned long s, e, ld; + ucstring_t *str; + + str = (ucstring_t *) malloc(sizeof(ucstring_t)); + + /* + * Set the initial values. + */ + str->cursor_motion = cursor_motion; + str->logical_first = str->logical_last = 0; + str->visual_first = str->visual_last = str->cursor = 0; + str->source = source; + str->start = start; + str->end = end; + + /* + * If the length of the string is 0, then just return it at this point. + */ + if (start == end) + return str; + + /* + * This flag indicates whether the collection loop for RTL is called + * before the LTR loop the first time. + */ + rtl_first = 0; + + /* + * Look for the first character in the string that has strong + * directionality. + */ + for (s = start; s < end && !ucisstrong(source[s]); s++) ; + + if (s == end) + /* + * If the string contains no characters with strong directionality, use + * the default direction. + */ + str->direction = default_direction; + else + str->direction = ucisrtl(source[s]) ? UCPGBA_RTL : UCPGBA_LTR; + + if (str->direction == UCPGBA_RTL) + /* + * Set the flag that causes the RTL collection loop to run first. + */ + rtl_first = 1; + + /* + * This loop now separates the string into runs based on directionality. + */ + for (s = e = 0; s < end; s = e) { + if (!rtl_first) { + /* + * Determine the next run of LTR text. + */ + + ld = s; + while (e < end && ISLTR_LTR(source[e])) { + if (ucisdigit(source[e]) && + !(0x660 <= source[e] && source[e] <= 0x669)) + ld = e; + e++; + } + if (str->direction != UCPGBA_LTR) { + while (e > ld && ISWEAK_NEUTRAL(source[e - 1])) + e--; + } + + /* + * Add the LTR segment to the string. + */ + if (e > s) + _ucadd_ltr_segment(str, source, s, e); + } + + /* + * Determine the next run of RTL text. + */ + ld = s = e; + while (e < end && ISRTL_RTL(source[e])) { + if (ucisdigit(source[e]) && + !(0x660 <= source[e] && source[e] <= 0x669)) + ld = e; + e++; + } + if (str->direction != UCPGBA_RTL) { + while (e > ld && ISWEAK_NEUTRAL(source[e - 1])) + e--; + } + + /* + * Add the RTL segment to the string. + */ + if (e > s) + _ucadd_rtl_segment(str, source, s, e); + + /* + * Clear the flag that allowed the RTL collection loop to run first + * for strings with overall RTL directionality. + */ + rtl_first = 0; + } + + /* + * Set up the initial cursor run. + */ + str->cursor = str->logical_first; + if (str != 0) + str->cursor->cursor = (str->cursor->direction == UCPGBA_RTL) ? + str->cursor->end - str->cursor->start : 0; + + return str; +} + +void +ucstring_free(ucstring_t *s) +{ + ucrun_t *l, *r; + + if (s == 0) + return; + + for (l = 0, r = s->visual_first; r != 0; r = r->visual_next) { + if (r->end > r->start) + free((char *) r->chars); + if (l) + free((char *) l); + l = r; + } + if (l) + free((char *) l); + + free((char *) s); +} + +int +ucstring_set_cursor_motion(ucstring_t *str, int cursor_motion) +{ + int n; + + if (str == 0) + return -1; + + n = str->cursor_motion; + str->cursor_motion = cursor_motion; + return n; +} + +static int +_ucstring_visual_cursor_right(ucstring_t *str, int count) +{ + int cnt = count; + unsigned long size; + ucrun_t *cursor; + + if (str == 0) + return 0; + + cursor = str->cursor; + while (cnt > 0) { + size = cursor->end - cursor->start; + if ((cursor->direction == UCPGBA_RTL && cursor->cursor + 1 == size) || + cursor->cursor + 1 > size) { + /* + * If the next run is NULL, then the cursor is already on the + * far right end already. + */ + if (cursor->visual_next == 0) + /* + * If movement occured, then report it. + */ + return (cnt != count); + + /* + * Move to the next run. + */ + str->cursor = cursor = cursor->visual_next; + cursor->cursor = (cursor->direction == UCPGBA_RTL) ? -1 : 0; + size = cursor->end - cursor->start; + } else + cursor->cursor++; + cnt--; + } + return 1; +} + +static int +_ucstring_logical_cursor_right(ucstring_t *str, int count) +{ + int cnt = count; + unsigned long size; + ucrun_t *cursor; + + if (str == 0) + return 0; + + cursor = str->cursor; + while (cnt > 0) { + size = cursor->end - cursor->start; + if (str->direction == UCPGBA_RTL) { + if (cursor->direction == UCPGBA_RTL) { + if (cursor->cursor + 1 == size) { + if (cursor == str->logical_first) + /* + * Already at the beginning of the string. + */ + return (cnt != count); + + str->cursor = cursor = cursor->logical_prev; + size = cursor->end - cursor->start; + cursor->cursor = (cursor->direction == UCPGBA_LTR) ? + size : 0; + } else + cursor->cursor++; + } else { + if (cursor->cursor == 0) { + if (cursor == str->logical_first) + /* + * At the beginning of the string already. + */ + return (cnt != count); + + str->cursor = cursor = cursor->logical_prev; + size = cursor->end - cursor->start; + cursor->cursor = (cursor->direction == UCPGBA_LTR) ? + size : 0; + } else + cursor->cursor--; + } + } else { + if (cursor->direction == UCPGBA_RTL) { + if (cursor->cursor == 0) { + if (cursor == str->logical_last) + /* + * Already at the end of the string. + */ + return (cnt != count); + + str->cursor = cursor = cursor->logical_next; + size = cursor->end - cursor->start; + cursor->cursor = (cursor->direction == UCPGBA_LTR) ? + 0 : size - 1; + } else + cursor->cursor--; + } else { + if (cursor->cursor + 1 > size) { + if (cursor == str->logical_last) + /* + * Already at the end of the string. + */ + return (cnt != count); + + str->cursor = cursor = cursor->logical_next; + cursor->cursor = (cursor->direction == UCPGBA_LTR) ? + 0 : size - 1; + } else + cursor->cursor++; + } + } + cnt--; + } + return 1; +} + +int +ucstring_cursor_right(ucstring_t *str, int count) +{ + if (str == 0) + return 0; + return (str->cursor_motion == UCPGBA_CURSOR_VISUAL) ? + _ucstring_visual_cursor_right(str, count) : + _ucstring_logical_cursor_right(str, count); +} + +static int +_ucstring_visual_cursor_left(ucstring_t *str, int count) +{ + int cnt = count; + unsigned long size; + ucrun_t *cursor; + + if (str == 0) + return 0; + + cursor = str->cursor; + while (cnt > 0) { + size = cursor->end - cursor->start; + if ((cursor->direction == UCPGBA_LTR && cursor->cursor == 0) || + cursor->cursor - 1 < -1) { + /* + * If the preceding run is NULL, then the cursor is already on the + * far left end already. + */ + if (cursor->visual_prev == 0) + /* + * If movement occured, then report it. + */ + return (cnt != count); + + /* + * Move to the previous run. + */ + str->cursor = cursor = cursor->visual_prev; + size = cursor->end - cursor->start; + cursor->cursor = (cursor->direction == UCPGBA_RTL) ? + size : size - 1; + } else + cursor->cursor--; + cnt--; + } + return 1; +} + +static int +_ucstring_logical_cursor_left(ucstring_t *str, int count) +{ + int cnt = count; + unsigned long size; + ucrun_t *cursor; + + if (str == 0) + return 0; + + cursor = str->cursor; + while (cnt > 0) { + size = cursor->end - cursor->start; + if (str->direction == UCPGBA_RTL) { + if (cursor->direction == UCPGBA_RTL) { + if (cursor->cursor == -1) { + if (cursor == str->logical_last) + /* + * Already at the end of the string. + */ + return (cnt != count); + + str->cursor = cursor = cursor->logical_next; + size = cursor->end - cursor->start; + cursor->cursor = (cursor->direction == UCPGBA_LTR) ? + 0 : size - 1; + } else + cursor->cursor--; + } else { + if (cursor->cursor + 1 > size) { + if (cursor == str->logical_last) + /* + * At the end of the string already. + */ + return (cnt != count); + + str->cursor = cursor = cursor->logical_next; + size = cursor->end - cursor->start; + cursor->cursor = (cursor->direction == UCPGBA_LTR) ? + 0 : size - 1; + } else + cursor->cursor++; + } + } else { + if (cursor->direction == UCPGBA_RTL) { + if (cursor->cursor + 1 == size) { + if (cursor == str->logical_first) + /* + * Already at the beginning of the string. + */ + return (cnt != count); + + str->cursor = cursor = cursor->logical_prev; + size = cursor->end - cursor->start; + cursor->cursor = (cursor->direction == UCPGBA_LTR) ? + size : 0; + } else + cursor->cursor++; + } else { + if (cursor->cursor == 0) { + if (cursor == str->logical_first) + /* + * Already at the beginning of the string. + */ + return (cnt != count); + + str->cursor = cursor = cursor->logical_prev; + cursor->cursor = (cursor->direction == UCPGBA_LTR) ? + size : 0; + } else + cursor->cursor--; + } + } + cnt--; + } + return 1; +} + +int +ucstring_cursor_left(ucstring_t *str, int count) +{ + if (str == 0) + return 0; + return (str->cursor_motion == UCPGBA_CURSOR_VISUAL) ? + _ucstring_visual_cursor_left(str, count) : + _ucstring_logical_cursor_left(str, count); +} + +void +ucstring_cursor_info(ucstring_t *str, int *direction, unsigned long *position) +{ + long c; + unsigned long size; + ucrun_t *cursor; + + if (str == 0 || direction == 0 || position == 0) + return; + + cursor = str->cursor; + + *direction = cursor->direction; + + c = cursor->cursor; + size = cursor->end - cursor->start; + + if (c == size) + *position = (cursor->direction == UCPGBA_RTL) ? + cursor->start : cursor->positions[c - 1]; + else if (c == -1) + *position = (cursor->direction == UCPGBA_RTL) ? + cursor->end : cursor->start; + else + *position = cursor->positions[c]; +} diff --git a/libraries/liblunicode/ucdata/ucpgba.h b/libraries/liblunicode/ucdata/ucpgba.h new file mode 100644 index 0000000..05ff54e --- /dev/null +++ b/libraries/liblunicode/ucdata/ucpgba.h @@ -0,0 +1,167 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Copyright 1999 Computing Research Labs, New Mexico State University + * + * 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 COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY 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. + */ +/* $Id: ucpgba.h,v 1.4 1999/11/19 15:24:30 mleisher Exp $ */ + +#ifndef _h_ucpgba +#define _h_ucpgba + +#include "portable.h" + +LDAP_BEGIN_DECL + +/*************************************************************************** + * + * Macros and types. + * + ***************************************************************************/ + +/* + * These are the direction values that can appear in render runs and render + * strings. + */ +#define UCPGBA_LTR 0 +#define UCPGBA_RTL 1 + +/* + * These are the flags for cursor motion. + */ +#define UCPGBA_CURSOR_VISUAL 0 +#define UCPGBA_CURSOR_LOGICAL 1 + +/* + * This structure is used to contain runs of text in a particular direction. + */ +typedef struct _ucrun_t { + struct _ucrun_t *visual_prev; /* Pointer to the previous visual run. */ + struct _ucrun_t *visual_next; /* Pointer to the next visual run. */ + + struct _ucrun_t *logical_prev; /* Pointer to the previous logical run. */ + struct _ucrun_t *logical_next; /* Pointer to the next logical run. */ + + int direction; /* Direction of the run. */ + + long cursor; /* Position of "cursor" in the string. */ + + unsigned long *chars; /* List of characters for the run. */ + unsigned long *positions; /* List of original positions in source. */ + + unsigned long *source; /* The source string. */ + unsigned long start; /* Beginning offset in the source string. */ + unsigned long end; /* Ending offset in the source string. */ +} ucrun_t; + +/* + * This represents a string of runs rendered up to a point that is not + * platform specific. + */ +typedef struct _ucstring_t { + int direction; /* Overall direction of the string. */ + + int cursor_motion; /* Logical or visual cursor motion flag. */ + + ucrun_t *cursor; /* The run containing the "cursor." */ + + ucrun_t *logical_first; /* First run in the logical order. */ + ucrun_t *logical_last; /* Last run in the logical order. */ + + ucrun_t *visual_first; /* First run in the visual order. */ + ucrun_t *visual_last; /* Last run in the visual order. */ + + unsigned long *source; /* The source string. */ + unsigned long start; /* The beginning offset in the source. */ + unsigned long end; /* The ending offset in the source. */ +} ucstring_t; + +/*************************************************************************** + * + * API + * + ***************************************************************************/ + +/* + * This creates and reorders the specified substring using the + * "Pretty Good Bidi Algorithm." A default direction is provided for cases + * of a string containing no strong direction characters and the default + * cursor motion should be provided. + */ +LDAP_LUNICODE_F (ucstring_t *) +ucstring_create LDAP_P((unsigned long *source, + unsigned long start, + unsigned long end, + int default_direction, + int cursor_motion)); +/* + * This releases the string. + */ +LDAP_LUNICODE_F (void) ucstring_free LDAP_P((ucstring_t *string)); + +/* + * This changes the cursor motion flag for the string. + */ +LDAP_LUNICODE_F (int) +ucstring_set_cursor_motion LDAP_P((ucstring_t *string, + int cursor_motion)); + +/* + * This function will move the cursor to the right depending on the + * type of cursor motion that was specified for the string. + * + * A 0 is returned if no cursor motion is performed, otherwise a + * 1 is returned. + */ +LDAP_LUNICODE_F (int) +ucstring_cursor_right LDAP_P((ucstring_t *string, int count)); + +/* + * This function will move the cursor to the left depending on the + * type of cursor motion that was specified for the string. + * + * A 0 is returned if no cursor motion is performed, otherwise a + * 1 is returned. + */ +LDAP_LUNICODE_F (int) +ucstring_cursor_left LDAP_P((ucstring_t *string, int count)); + +/* + * This routine retrieves the direction of the run containing the cursor + * and the actual position in the original text string. + */ +LDAP_LUNICODE_F (void) +ucstring_cursor_info LDAP_P((ucstring_t *string, int *direction, + unsigned long *position)); + +LDAP_END_DECL + +#endif /* _h_ucpgba */ diff --git a/libraries/liblunicode/ucdata/ucpgba.man b/libraries/liblunicode/ucdata/ucpgba.man new file mode 100644 index 0000000..4486509 --- /dev/null +++ b/libraries/liblunicode/ucdata/ucpgba.man @@ -0,0 +1,97 @@ +.\" +.\" $Id: ucpgba.man,v 1.1 1999/11/19 16:08:34 mleisher Exp $ +.\" +.TH ucpgba 3 "19 November 1999" +.SH NAME +ucpgba \- functions for doing bidirectional reordering of Unicode text and +logical and visual cursor motion + +.SH SYNOPSIS +.nf +#include <ucdata.h> +#include <ucpgba.h> + +ucstring_t *ucstring_create(unsigned long *source, unsigned long start, + unsigned long end, int default_direction, + int cursor_motion) +.sp +void ucstring_free(ucstring_t *string) +.sp +int ucstring_set_cursor_motion(ucstring_t *string, int cursor_motion) +.sp +int ucstring_cursor_right(ucstring_t *string, int count) +.sp +int ucstring_cursor_left(ucstring_t *string, int count) +.sp +void ucstring_cursor_info(ucstring_t *string, int *direction, + unsigned long *position) + +.SH DESCRIPTION +.TP 4 +.BR Macros +UCPGBA_LTR +.br +UCPGBA_RTL +.br +UCPGBA_CURSOR_VISUAL +.br +UCPGBA_CURSOR_LOGICAL + +.TP 4 +.BR ucstring_create() +This function will create a reordered string by using the implicit +directionality of the characters in the specified substring. +.sp +The `default_direction' parameter should be one of UCPGBA_LTR or UCPGBA_RTL +and is used only in cases where a string contains no characters with strong +directionality. +.sp +The `cursor_motion' parameter should be one of UCPGBA_CURSOR_VISUAL or +UCPGBA_CURSOR_LOGICAL, and is used to specify the initial cursor motion +behavior. This behavior can be switched at any time using +ustring_set_cursor_motion(). + +.TP 4 +.BR ucstring_free() +This function will deallocate the memory used by the string, incuding the +string itself. + +.TP 4 +.BR ucstring_cursor_info() +This function will return the text position of the internal cursor and the +directionality of the text at that position. The position returned is the +original text position of the character. + +.TP 4 +.BR ucstring_set_cursor_motion() +This function will change the cursor motion type and return the previous +cursor motion type. + +.TP 4 +.BR ucstring_cursor_right() +This function will move the internal cursor to the right according to the +type of cursor motion set for the string. +.sp +If no cursor motion is performed, it returns 0. Otherwise it will return a 1. + +.TP 4 +.BR ucstring_cursor_left() +This function will move the internal cursor to the left according to the +type of cursor motion set for the string. +.sp +If no cursor motion is performed, it returns 0. Otherwise it will return a 1. + +.SH "SEE ALSO" +ucdata(3) + +.SH ACKNOWLEDGMENTS +These are people who have helped with patches or alerted me about problems. + +.SH AUTHOR +Mark Leisher +.br +Computing Research Lab +.br +New Mexico State University +.br +Email: mleisher@crl.nmsu.edu diff --git a/libraries/liblunicode/ucdata/uctable.h b/libraries/liblunicode/ucdata/uctable.h new file mode 100644 index 0000000..f6c06e9 --- /dev/null +++ b/libraries/liblunicode/ucdata/uctable.h @@ -0,0 +1,14306 @@ +static const ac_uint4 _ucprop_size = 50; + +static const ac_uint2 _ucprop_offsets[] = { + 0x0000, 0x00d0, 0x0138, 0x0140, 0x016a, 0x0176, 0x019e, 0x01ac, + 0x01ae, 0x01b0, 0x01b4, 0x01cc, 0x01ce, 0xffff, 0x01d4, 0x051a, + 0x0862, 0x0876, 0x089e, 0x0a32, 0x0a40, 0x0a58, 0x0ad8, 0x0b54, + 0x0be0, 0x0c54, 0x0c6a, 0x0c96, 0x0d66, 0x0fee, 0x100a, 0x1020, + 0x1024, 0x1054, 0x1058, 0x106e, 0x1078, 0x107e, 0x108e, 0x1240, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x13e8, 0x16e4, + 0x16ee, 0x16f6, 0x1720, 0x0000 +}; + +static const ac_uint4 _ucprop_ranges[] = { + 0x00000300, 0x0000034f, 0x00000360, 0x0000036f, + 0x00000483, 0x00000486, 0x00000591, 0x000005a1, + 0x000005a3, 0x000005b9, 0x000005bb, 0x000005bd, + 0x000005bf, 0x000005bf, 0x000005c1, 0x000005c2, + 0x000005c4, 0x000005c4, 0x0000064b, 0x00000655, + 0x00000670, 0x00000670, 0x000006d6, 0x000006dc, + 0x000006df, 0x000006e4, 0x000006e7, 0x000006e8, + 0x000006ea, 0x000006ed, 0x00000711, 0x00000711, + 0x00000730, 0x0000074a, 0x000007a6, 0x000007b0, + 0x00000901, 0x00000902, 0x0000093c, 0x0000093c, + 0x00000941, 0x00000948, 0x0000094d, 0x0000094d, + 0x00000951, 0x00000954, 0x00000962, 0x00000963, + 0x00000981, 0x00000981, 0x000009bc, 0x000009bc, + 0x000009c1, 0x000009c4, 0x000009cd, 0x000009cd, + 0x000009e2, 0x000009e3, 0x00000a02, 0x00000a02, + 0x00000a3c, 0x00000a3c, 0x00000a41, 0x00000a42, + 0x00000a47, 0x00000a48, 0x00000a4b, 0x00000a4d, + 0x00000a70, 0x00000a71, 0x00000a81, 0x00000a82, + 0x00000abc, 0x00000abc, 0x00000ac1, 0x00000ac5, + 0x00000ac7, 0x00000ac8, 0x00000acd, 0x00000acd, + 0x00000b01, 0x00000b01, 0x00000b3c, 0x00000b3c, + 0x00000b3f, 0x00000b3f, 0x00000b41, 0x00000b43, + 0x00000b4d, 0x00000b4d, 0x00000b56, 0x00000b56, + 0x00000b82, 0x00000b82, 0x00000bc0, 0x00000bc0, + 0x00000bcd, 0x00000bcd, 0x00000c3e, 0x00000c40, + 0x00000c46, 0x00000c48, 0x00000c4a, 0x00000c4d, + 0x00000c55, 0x00000c56, 0x00000cbf, 0x00000cbf, + 0x00000cc6, 0x00000cc6, 0x00000ccc, 0x00000ccd, + 0x00000d41, 0x00000d43, 0x00000d4d, 0x00000d4d, + 0x00000dca, 0x00000dca, 0x00000dd2, 0x00000dd4, + 0x00000dd6, 0x00000dd6, 0x00000e31, 0x00000e31, + 0x00000e34, 0x00000e3a, 0x00000e47, 0x00000e4e, + 0x00000eb1, 0x00000eb1, 0x00000eb4, 0x00000eb9, + 0x00000ebb, 0x00000ebc, 0x00000ec8, 0x00000ecd, + 0x00000f18, 0x00000f19, 0x00000f35, 0x00000f35, + 0x00000f37, 0x00000f37, 0x00000f39, 0x00000f39, + 0x00000f71, 0x00000f7e, 0x00000f80, 0x00000f84, + 0x00000f86, 0x00000f87, 0x00000f90, 0x00000f97, + 0x00000f99, 0x00000fbc, 0x00000fc6, 0x00000fc6, + 0x0000102d, 0x00001030, 0x00001032, 0x00001032, + 0x00001036, 0x00001037, 0x00001039, 0x00001039, + 0x00001058, 0x00001059, 0x00001712, 0x00001714, + 0x00001732, 0x00001734, 0x00001752, 0x00001753, + 0x00001772, 0x00001773, 0x000017b7, 0x000017bd, + 0x000017c6, 0x000017c6, 0x000017c9, 0x000017d3, + 0x0000180b, 0x0000180d, 0x000018a9, 0x000018a9, + 0x000020d0, 0x000020dc, 0x000020e1, 0x000020e1, + 0x000020e5, 0x000020ea, 0x0000302a, 0x0000302f, + 0x00003099, 0x0000309a, 0x0000fb1e, 0x0000fb1e, + 0x0000fe00, 0x0000fe0f, 0x0000fe20, 0x0000fe23, + 0x0001d167, 0x0001d169, 0x0001d17b, 0x0001d182, + 0x0001d185, 0x0001d18b, 0x0001d1aa, 0x0001d1ad, + 0x00000903, 0x00000903, 0x0000093e, 0x00000940, + 0x00000949, 0x0000094c, 0x00000982, 0x00000983, + 0x000009be, 0x000009c0, 0x000009c7, 0x000009c8, + 0x000009cb, 0x000009cc, 0x000009d7, 0x000009d7, + 0x00000a3e, 0x00000a40, 0x00000a83, 0x00000a83, + 0x00000abe, 0x00000ac0, 0x00000ac9, 0x00000ac9, + 0x00000acb, 0x00000acc, 0x00000b02, 0x00000b03, + 0x00000b3e, 0x00000b3e, 0x00000b40, 0x00000b40, + 0x00000b47, 0x00000b48, 0x00000b4b, 0x00000b4c, + 0x00000b57, 0x00000b57, 0x00000bbe, 0x00000bbf, + 0x00000bc1, 0x00000bc2, 0x00000bc6, 0x00000bc8, + 0x00000bca, 0x00000bcc, 0x00000bd7, 0x00000bd7, + 0x00000c01, 0x00000c03, 0x00000c41, 0x00000c44, + 0x00000c82, 0x00000c83, 0x00000cbe, 0x00000cbe, + 0x00000cc0, 0x00000cc4, 0x00000cc7, 0x00000cc8, + 0x00000cca, 0x00000ccb, 0x00000cd5, 0x00000cd6, + 0x00000d02, 0x00000d03, 0x00000d3e, 0x00000d40, + 0x00000d46, 0x00000d48, 0x00000d4a, 0x00000d4c, + 0x00000d57, 0x00000d57, 0x00000d82, 0x00000d83, + 0x00000dcf, 0x00000dd1, 0x00000dd8, 0x00000ddf, + 0x00000df2, 0x00000df3, 0x00000f3e, 0x00000f3f, + 0x00000f7f, 0x00000f7f, 0x0000102c, 0x0000102c, + 0x00001031, 0x00001031, 0x00001038, 0x00001038, + 0x00001056, 0x00001057, 0x000017b4, 0x000017b6, + 0x000017be, 0x000017c5, 0x000017c7, 0x000017c8, + 0x0001d165, 0x0001d166, 0x0001d16d, 0x0001d172, + 0x00000488, 0x00000489, 0x000006de, 0x000006de, + 0x000020dd, 0x000020e0, 0x000020e2, 0x000020e4, + 0x00000030, 0x00000039, 0x00000660, 0x00000669, + 0x000006f0, 0x000006f9, 0x00000966, 0x0000096f, + 0x000009e6, 0x000009ef, 0x00000a66, 0x00000a6f, + 0x00000ae6, 0x00000aef, 0x00000b66, 0x00000b6f, + 0x00000be7, 0x00000bef, 0x00000c66, 0x00000c6f, + 0x00000ce6, 0x00000cef, 0x00000d66, 0x00000d6f, + 0x00000e50, 0x00000e59, 0x00000ed0, 0x00000ed9, + 0x00000f20, 0x00000f29, 0x00001040, 0x00001049, + 0x00001369, 0x00001371, 0x000017e0, 0x000017e9, + 0x00001810, 0x00001819, 0x0000ff10, 0x0000ff19, + 0x0001d7ce, 0x0001d7ff, 0x000016ee, 0x000016f0, + 0x00002160, 0x00002183, 0x00003007, 0x00003007, + 0x00003021, 0x00003029, 0x00003038, 0x0000303a, + 0x0001034a, 0x0001034a, 0x000000b2, 0x000000b3, + 0x000000b9, 0x000000b9, 0x000000bc, 0x000000be, + 0x000009f4, 0x000009f9, 0x00000bf0, 0x00000bf2, + 0x00000f2a, 0x00000f33, 0x00001372, 0x0000137c, + 0x00002070, 0x00002070, 0x00002074, 0x00002079, + 0x00002080, 0x00002089, 0x00002153, 0x0000215f, + 0x00002460, 0x0000249b, 0x000024ea, 0x000024fe, + 0x00002776, 0x00002793, 0x00003192, 0x00003195, + 0x00003220, 0x00003229, 0x00003251, 0x0000325f, + 0x00003280, 0x00003289, 0x000032b1, 0x000032bf, + 0x00010320, 0x00010323, 0x00000020, 0x00000020, + 0x000000a0, 0x000000a0, 0x00001680, 0x00001680, + 0x00002000, 0x0000200b, 0x0000202f, 0x0000202f, + 0x0000205f, 0x0000205f, 0x00003000, 0x00003000, + 0x00002028, 0x00002028, 0x00002029, 0x00002029, + 0x00000000, 0x0000001f, 0x0000007f, 0x0000009f, + 0x000006dd, 0x000006dd, 0x0000070f, 0x0000070f, + 0x0000180e, 0x0000180e, 0x0000200c, 0x0000200f, + 0x0000202a, 0x0000202e, 0x00002060, 0x00002063, + 0x0000206a, 0x0000206f, 0x0000feff, 0x0000feff, + 0x0000fff9, 0x0000fffb, 0x0001d173, 0x0001d17a, + 0x000e0001, 0x000e0001, 0x000e0020, 0x000e007f, + 0x00010000, 0x0010ffff, 0x0000e000, 0x0000f8ff, + 0x000f0000, 0x000ffffd, 0x00100000, 0x0010fffd, + 0x00000041, 0x0000005a, 0x000000c0, 0x000000d6, + 0x000000d8, 0x000000de, 0x00000100, 0x00000100, + 0x00000102, 0x00000102, 0x00000104, 0x00000104, + 0x00000106, 0x00000106, 0x00000108, 0x00000108, + 0x0000010a, 0x0000010a, 0x0000010c, 0x0000010c, + 0x0000010e, 0x0000010e, 0x00000110, 0x00000110, + 0x00000112, 0x00000112, 0x00000114, 0x00000114, + 0x00000116, 0x00000116, 0x00000118, 0x00000118, + 0x0000011a, 0x0000011a, 0x0000011c, 0x0000011c, + 0x0000011e, 0x0000011e, 0x00000120, 0x00000120, + 0x00000122, 0x00000122, 0x00000124, 0x00000124, + 0x00000126, 0x00000126, 0x00000128, 0x00000128, + 0x0000012a, 0x0000012a, 0x0000012c, 0x0000012c, + 0x0000012e, 0x0000012e, 0x00000130, 0x00000130, + 0x00000132, 0x00000132, 0x00000134, 0x00000134, + 0x00000136, 0x00000136, 0x00000139, 0x00000139, + 0x0000013b, 0x0000013b, 0x0000013d, 0x0000013d, + 0x0000013f, 0x0000013f, 0x00000141, 0x00000141, + 0x00000143, 0x00000143, 0x00000145, 0x00000145, + 0x00000147, 0x00000147, 0x0000014a, 0x0000014a, + 0x0000014c, 0x0000014c, 0x0000014e, 0x0000014e, + 0x00000150, 0x00000150, 0x00000152, 0x00000152, + 0x00000154, 0x00000154, 0x00000156, 0x00000156, + 0x00000158, 0x00000158, 0x0000015a, 0x0000015a, + 0x0000015c, 0x0000015c, 0x0000015e, 0x0000015e, + 0x00000160, 0x00000160, 0x00000162, 0x00000162, + 0x00000164, 0x00000164, 0x00000166, 0x00000166, + 0x00000168, 0x00000168, 0x0000016a, 0x0000016a, + 0x0000016c, 0x0000016c, 0x0000016e, 0x0000016e, + 0x00000170, 0x00000170, 0x00000172, 0x00000172, + 0x00000174, 0x00000174, 0x00000176, 0x00000176, + 0x00000178, 0x00000179, 0x0000017b, 0x0000017b, + 0x0000017d, 0x0000017d, 0x00000181, 0x00000182, + 0x00000184, 0x00000184, 0x00000186, 0x00000187, + 0x00000189, 0x0000018b, 0x0000018e, 0x00000191, + 0x00000193, 0x00000194, 0x00000196, 0x00000198, + 0x0000019c, 0x0000019d, 0x0000019f, 0x000001a0, + 0x000001a2, 0x000001a2, 0x000001a4, 0x000001a4, + 0x000001a6, 0x000001a7, 0x000001a9, 0x000001a9, + 0x000001ac, 0x000001ac, 0x000001ae, 0x000001af, + 0x000001b1, 0x000001b3, 0x000001b5, 0x000001b5, + 0x000001b7, 0x000001b8, 0x000001bc, 0x000001bc, + 0x000001c4, 0x000001c4, 0x000001c7, 0x000001c7, + 0x000001ca, 0x000001ca, 0x000001cd, 0x000001cd, + 0x000001cf, 0x000001cf, 0x000001d1, 0x000001d1, + 0x000001d3, 0x000001d3, 0x000001d5, 0x000001d5, + 0x000001d7, 0x000001d7, 0x000001d9, 0x000001d9, + 0x000001db, 0x000001db, 0x000001de, 0x000001de, + 0x000001e0, 0x000001e0, 0x000001e2, 0x000001e2, + 0x000001e4, 0x000001e4, 0x000001e6, 0x000001e6, + 0x000001e8, 0x000001e8, 0x000001ea, 0x000001ea, + 0x000001ec, 0x000001ec, 0x000001ee, 0x000001ee, + 0x000001f1, 0x000001f1, 0x000001f4, 0x000001f4, + 0x000001f6, 0x000001f8, 0x000001fa, 0x000001fa, + 0x000001fc, 0x000001fc, 0x000001fe, 0x000001fe, + 0x00000200, 0x00000200, 0x00000202, 0x00000202, + 0x00000204, 0x00000204, 0x00000206, 0x00000206, + 0x00000208, 0x00000208, 0x0000020a, 0x0000020a, + 0x0000020c, 0x0000020c, 0x0000020e, 0x0000020e, + 0x00000210, 0x00000210, 0x00000212, 0x00000212, + 0x00000214, 0x00000214, 0x00000216, 0x00000216, + 0x00000218, 0x00000218, 0x0000021a, 0x0000021a, + 0x0000021c, 0x0000021c, 0x0000021e, 0x0000021e, + 0x00000220, 0x00000220, 0x00000222, 0x00000222, + 0x00000224, 0x00000224, 0x00000226, 0x00000226, + 0x00000228, 0x00000228, 0x0000022a, 0x0000022a, + 0x0000022c, 0x0000022c, 0x0000022e, 0x0000022e, + 0x00000230, 0x00000230, 0x00000232, 0x00000232, + 0x00000386, 0x00000386, 0x00000388, 0x0000038a, + 0x0000038c, 0x0000038c, 0x0000038e, 0x0000038f, + 0x00000391, 0x000003a1, 0x000003a3, 0x000003ab, + 0x000003d2, 0x000003d4, 0x000003d8, 0x000003d8, + 0x000003da, 0x000003da, 0x000003dc, 0x000003dc, + 0x000003de, 0x000003de, 0x000003e0, 0x000003e0, + 0x000003e2, 0x000003e2, 0x000003e4, 0x000003e4, + 0x000003e6, 0x000003e6, 0x000003e8, 0x000003e8, + 0x000003ea, 0x000003ea, 0x000003ec, 0x000003ec, + 0x000003ee, 0x000003ee, 0x000003f4, 0x000003f4, + 0x00000400, 0x0000042f, 0x00000460, 0x00000460, + 0x00000462, 0x00000462, 0x00000464, 0x00000464, + 0x00000466, 0x00000466, 0x00000468, 0x00000468, + 0x0000046a, 0x0000046a, 0x0000046c, 0x0000046c, + 0x0000046e, 0x0000046e, 0x00000470, 0x00000470, + 0x00000472, 0x00000472, 0x00000474, 0x00000474, + 0x00000476, 0x00000476, 0x00000478, 0x00000478, + 0x0000047a, 0x0000047a, 0x0000047c, 0x0000047c, + 0x0000047e, 0x0000047e, 0x00000480, 0x00000480, + 0x0000048a, 0x0000048a, 0x0000048c, 0x0000048c, + 0x0000048e, 0x0000048e, 0x00000490, 0x00000490, + 0x00000492, 0x00000492, 0x00000494, 0x00000494, + 0x00000496, 0x00000496, 0x00000498, 0x00000498, + 0x0000049a, 0x0000049a, 0x0000049c, 0x0000049c, + 0x0000049e, 0x0000049e, 0x000004a0, 0x000004a0, + 0x000004a2, 0x000004a2, 0x000004a4, 0x000004a4, + 0x000004a6, 0x000004a6, 0x000004a8, 0x000004a8, + 0x000004aa, 0x000004aa, 0x000004ac, 0x000004ac, + 0x000004ae, 0x000004ae, 0x000004b0, 0x000004b0, + 0x000004b2, 0x000004b2, 0x000004b4, 0x000004b4, + 0x000004b6, 0x000004b6, 0x000004b8, 0x000004b8, + 0x000004ba, 0x000004ba, 0x000004bc, 0x000004bc, + 0x000004be, 0x000004be, 0x000004c0, 0x000004c1, + 0x000004c3, 0x000004c3, 0x000004c5, 0x000004c5, + 0x000004c7, 0x000004c7, 0x000004c9, 0x000004c9, + 0x000004cb, 0x000004cb, 0x000004cd, 0x000004cd, + 0x000004d0, 0x000004d0, 0x000004d2, 0x000004d2, + 0x000004d4, 0x000004d4, 0x000004d6, 0x000004d6, + 0x000004d8, 0x000004d8, 0x000004da, 0x000004da, + 0x000004dc, 0x000004dc, 0x000004de, 0x000004de, + 0x000004e0, 0x000004e0, 0x000004e2, 0x000004e2, + 0x000004e4, 0x000004e4, 0x000004e6, 0x000004e6, + 0x000004e8, 0x000004e8, 0x000004ea, 0x000004ea, + 0x000004ec, 0x000004ec, 0x000004ee, 0x000004ee, + 0x000004f0, 0x000004f0, 0x000004f2, 0x000004f2, + 0x000004f4, 0x000004f4, 0x000004f8, 0x000004f8, + 0x00000500, 0x00000500, 0x00000502, 0x00000502, + 0x00000504, 0x00000504, 0x00000506, 0x00000506, + 0x00000508, 0x00000508, 0x0000050a, 0x0000050a, + 0x0000050c, 0x0000050c, 0x0000050e, 0x0000050e, + 0x00000531, 0x00000556, 0x000010a0, 0x000010c5, + 0x00001e00, 0x00001e00, 0x00001e02, 0x00001e02, + 0x00001e04, 0x00001e04, 0x00001e06, 0x00001e06, + 0x00001e08, 0x00001e08, 0x00001e0a, 0x00001e0a, + 0x00001e0c, 0x00001e0c, 0x00001e0e, 0x00001e0e, + 0x00001e10, 0x00001e10, 0x00001e12, 0x00001e12, + 0x00001e14, 0x00001e14, 0x00001e16, 0x00001e16, + 0x00001e18, 0x00001e18, 0x00001e1a, 0x00001e1a, + 0x00001e1c, 0x00001e1c, 0x00001e1e, 0x00001e1e, + 0x00001e20, 0x00001e20, 0x00001e22, 0x00001e22, + 0x00001e24, 0x00001e24, 0x00001e26, 0x00001e26, + 0x00001e28, 0x00001e28, 0x00001e2a, 0x00001e2a, + 0x00001e2c, 0x00001e2c, 0x00001e2e, 0x00001e2e, + 0x00001e30, 0x00001e30, 0x00001e32, 0x00001e32, + 0x00001e34, 0x00001e34, 0x00001e36, 0x00001e36, + 0x00001e38, 0x00001e38, 0x00001e3a, 0x00001e3a, + 0x00001e3c, 0x00001e3c, 0x00001e3e, 0x00001e3e, + 0x00001e40, 0x00001e40, 0x00001e42, 0x00001e42, + 0x00001e44, 0x00001e44, 0x00001e46, 0x00001e46, + 0x00001e48, 0x00001e48, 0x00001e4a, 0x00001e4a, + 0x00001e4c, 0x00001e4c, 0x00001e4e, 0x00001e4e, + 0x00001e50, 0x00001e50, 0x00001e52, 0x00001e52, + 0x00001e54, 0x00001e54, 0x00001e56, 0x00001e56, + 0x00001e58, 0x00001e58, 0x00001e5a, 0x00001e5a, + 0x00001e5c, 0x00001e5c, 0x00001e5e, 0x00001e5e, + 0x00001e60, 0x00001e60, 0x00001e62, 0x00001e62, + 0x00001e64, 0x00001e64, 0x00001e66, 0x00001e66, + 0x00001e68, 0x00001e68, 0x00001e6a, 0x00001e6a, + 0x00001e6c, 0x00001e6c, 0x00001e6e, 0x00001e6e, + 0x00001e70, 0x00001e70, 0x00001e72, 0x00001e72, + 0x00001e74, 0x00001e74, 0x00001e76, 0x00001e76, + 0x00001e78, 0x00001e78, 0x00001e7a, 0x00001e7a, + 0x00001e7c, 0x00001e7c, 0x00001e7e, 0x00001e7e, + 0x00001e80, 0x00001e80, 0x00001e82, 0x00001e82, + 0x00001e84, 0x00001e84, 0x00001e86, 0x00001e86, + 0x00001e88, 0x00001e88, 0x00001e8a, 0x00001e8a, + 0x00001e8c, 0x00001e8c, 0x00001e8e, 0x00001e8e, + 0x00001e90, 0x00001e90, 0x00001e92, 0x00001e92, + 0x00001e94, 0x00001e94, 0x00001ea0, 0x00001ea0, + 0x00001ea2, 0x00001ea2, 0x00001ea4, 0x00001ea4, + 0x00001ea6, 0x00001ea6, 0x00001ea8, 0x00001ea8, + 0x00001eaa, 0x00001eaa, 0x00001eac, 0x00001eac, + 0x00001eae, 0x00001eae, 0x00001eb0, 0x00001eb0, + 0x00001eb2, 0x00001eb2, 0x00001eb4, 0x00001eb4, + 0x00001eb6, 0x00001eb6, 0x00001eb8, 0x00001eb8, + 0x00001eba, 0x00001eba, 0x00001ebc, 0x00001ebc, + 0x00001ebe, 0x00001ebe, 0x00001ec0, 0x00001ec0, + 0x00001ec2, 0x00001ec2, 0x00001ec4, 0x00001ec4, + 0x00001ec6, 0x00001ec6, 0x00001ec8, 0x00001ec8, + 0x00001eca, 0x00001eca, 0x00001ecc, 0x00001ecc, + 0x00001ece, 0x00001ece, 0x00001ed0, 0x00001ed0, + 0x00001ed2, 0x00001ed2, 0x00001ed4, 0x00001ed4, + 0x00001ed6, 0x00001ed6, 0x00001ed8, 0x00001ed8, + 0x00001eda, 0x00001eda, 0x00001edc, 0x00001edc, + 0x00001ede, 0x00001ede, 0x00001ee0, 0x00001ee0, + 0x00001ee2, 0x00001ee2, 0x00001ee4, 0x00001ee4, + 0x00001ee6, 0x00001ee6, 0x00001ee8, 0x00001ee8, + 0x00001eea, 0x00001eea, 0x00001eec, 0x00001eec, + 0x00001eee, 0x00001eee, 0x00001ef0, 0x00001ef0, + 0x00001ef2, 0x00001ef2, 0x00001ef4, 0x00001ef4, + 0x00001ef6, 0x00001ef6, 0x00001ef8, 0x00001ef8, + 0x00001f08, 0x00001f0f, 0x00001f18, 0x00001f1d, + 0x00001f28, 0x00001f2f, 0x00001f38, 0x00001f3f, + 0x00001f48, 0x00001f4d, 0x00001f59, 0x00001f59, + 0x00001f5b, 0x00001f5b, 0x00001f5d, 0x00001f5d, + 0x00001f5f, 0x00001f5f, 0x00001f68, 0x00001f6f, + 0x00001fb8, 0x00001fbb, 0x00001fc8, 0x00001fcb, + 0x00001fd8, 0x00001fdb, 0x00001fe8, 0x00001fec, + 0x00001ff8, 0x00001ffb, 0x00002102, 0x00002102, + 0x00002107, 0x00002107, 0x0000210b, 0x0000210d, + 0x00002110, 0x00002112, 0x00002115, 0x00002115, + 0x00002119, 0x0000211d, 0x00002124, 0x00002124, + 0x00002126, 0x00002126, 0x00002128, 0x00002128, + 0x0000212a, 0x0000212d, 0x00002130, 0x00002131, + 0x00002133, 0x00002133, 0x0000213e, 0x0000213f, + 0x00002145, 0x00002145, 0x0000ff21, 0x0000ff3a, + 0x00010400, 0x00010425, 0x0001d400, 0x0001d419, + 0x0001d434, 0x0001d44d, 0x0001d468, 0x0001d481, + 0x0001d49c, 0x0001d49c, 0x0001d49e, 0x0001d49f, + 0x0001d4a2, 0x0001d4a2, 0x0001d4a5, 0x0001d4a6, + 0x0001d4a9, 0x0001d4ac, 0x0001d4ae, 0x0001d4b5, + 0x0001d4d0, 0x0001d4e9, 0x0001d504, 0x0001d505, + 0x0001d507, 0x0001d50a, 0x0001d50d, 0x0001d514, + 0x0001d516, 0x0001d51c, 0x0001d538, 0x0001d539, + 0x0001d53b, 0x0001d53e, 0x0001d540, 0x0001d544, + 0x0001d546, 0x0001d546, 0x0001d54a, 0x0001d550, + 0x0001d56c, 0x0001d585, 0x0001d5a0, 0x0001d5b9, + 0x0001d5d4, 0x0001d5ed, 0x0001d608, 0x0001d621, + 0x0001d63c, 0x0001d655, 0x0001d670, 0x0001d689, + 0x0001d6a8, 0x0001d6c0, 0x0001d6e2, 0x0001d6fa, + 0x0001d71c, 0x0001d734, 0x0001d756, 0x0001d76e, + 0x0001d790, 0x0001d7a8, 0x00000061, 0x0000007a, + 0x000000aa, 0x000000aa, 0x000000b5, 0x000000b5, + 0x000000ba, 0x000000ba, 0x000000df, 0x000000f6, + 0x000000f8, 0x000000ff, 0x00000101, 0x00000101, + 0x00000103, 0x00000103, 0x00000105, 0x00000105, + 0x00000107, 0x00000107, 0x00000109, 0x00000109, + 0x0000010b, 0x0000010b, 0x0000010d, 0x0000010d, + 0x0000010f, 0x0000010f, 0x00000111, 0x00000111, + 0x00000113, 0x00000113, 0x00000115, 0x00000115, + 0x00000117, 0x00000117, 0x00000119, 0x00000119, + 0x0000011b, 0x0000011b, 0x0000011d, 0x0000011d, + 0x0000011f, 0x0000011f, 0x00000121, 0x00000121, + 0x00000123, 0x00000123, 0x00000125, 0x00000125, + 0x00000127, 0x00000127, 0x00000129, 0x00000129, + 0x0000012b, 0x0000012b, 0x0000012d, 0x0000012d, + 0x0000012f, 0x0000012f, 0x00000131, 0x00000131, + 0x00000133, 0x00000133, 0x00000135, 0x00000135, + 0x00000137, 0x00000138, 0x0000013a, 0x0000013a, + 0x0000013c, 0x0000013c, 0x0000013e, 0x0000013e, + 0x00000140, 0x00000140, 0x00000142, 0x00000142, + 0x00000144, 0x00000144, 0x00000146, 0x00000146, + 0x00000148, 0x00000149, 0x0000014b, 0x0000014b, + 0x0000014d, 0x0000014d, 0x0000014f, 0x0000014f, + 0x00000151, 0x00000151, 0x00000153, 0x00000153, + 0x00000155, 0x00000155, 0x00000157, 0x00000157, + 0x00000159, 0x00000159, 0x0000015b, 0x0000015b, + 0x0000015d, 0x0000015d, 0x0000015f, 0x0000015f, + 0x00000161, 0x00000161, 0x00000163, 0x00000163, + 0x00000165, 0x00000165, 0x00000167, 0x00000167, + 0x00000169, 0x00000169, 0x0000016b, 0x0000016b, + 0x0000016d, 0x0000016d, 0x0000016f, 0x0000016f, + 0x00000171, 0x00000171, 0x00000173, 0x00000173, + 0x00000175, 0x00000175, 0x00000177, 0x00000177, + 0x0000017a, 0x0000017a, 0x0000017c, 0x0000017c, + 0x0000017e, 0x00000180, 0x00000183, 0x00000183, + 0x00000185, 0x00000185, 0x00000188, 0x00000188, + 0x0000018c, 0x0000018d, 0x00000192, 0x00000192, + 0x00000195, 0x00000195, 0x00000199, 0x0000019b, + 0x0000019e, 0x0000019e, 0x000001a1, 0x000001a1, + 0x000001a3, 0x000001a3, 0x000001a5, 0x000001a5, + 0x000001a8, 0x000001a8, 0x000001aa, 0x000001ab, + 0x000001ad, 0x000001ad, 0x000001b0, 0x000001b0, + 0x000001b4, 0x000001b4, 0x000001b6, 0x000001b6, + 0x000001b9, 0x000001ba, 0x000001bd, 0x000001bf, + 0x000001c6, 0x000001c6, 0x000001c9, 0x000001c9, + 0x000001cc, 0x000001cc, 0x000001ce, 0x000001ce, + 0x000001d0, 0x000001d0, 0x000001d2, 0x000001d2, + 0x000001d4, 0x000001d4, 0x000001d6, 0x000001d6, + 0x000001d8, 0x000001d8, 0x000001da, 0x000001da, + 0x000001dc, 0x000001dd, 0x000001df, 0x000001df, + 0x000001e1, 0x000001e1, 0x000001e3, 0x000001e3, + 0x000001e5, 0x000001e5, 0x000001e7, 0x000001e7, + 0x000001e9, 0x000001e9, 0x000001eb, 0x000001eb, + 0x000001ed, 0x000001ed, 0x000001ef, 0x000001f0, + 0x000001f3, 0x000001f3, 0x000001f5, 0x000001f5, + 0x000001f9, 0x000001f9, 0x000001fb, 0x000001fb, + 0x000001fd, 0x000001fd, 0x000001ff, 0x000001ff, + 0x00000201, 0x00000201, 0x00000203, 0x00000203, + 0x00000205, 0x00000205, 0x00000207, 0x00000207, + 0x00000209, 0x00000209, 0x0000020b, 0x0000020b, + 0x0000020d, 0x0000020d, 0x0000020f, 0x0000020f, + 0x00000211, 0x00000211, 0x00000213, 0x00000213, + 0x00000215, 0x00000215, 0x00000217, 0x00000217, + 0x00000219, 0x00000219, 0x0000021b, 0x0000021b, + 0x0000021d, 0x0000021d, 0x0000021f, 0x0000021f, + 0x00000223, 0x00000223, 0x00000225, 0x00000225, + 0x00000227, 0x00000227, 0x00000229, 0x00000229, + 0x0000022b, 0x0000022b, 0x0000022d, 0x0000022d, + 0x0000022f, 0x0000022f, 0x00000231, 0x00000231, + 0x00000233, 0x00000233, 0x00000250, 0x000002ad, + 0x00000390, 0x00000390, 0x000003ac, 0x000003ce, + 0x000003d0, 0x000003d1, 0x000003d5, 0x000003d7, + 0x000003d9, 0x000003d9, 0x000003db, 0x000003db, + 0x000003dd, 0x000003dd, 0x000003df, 0x000003df, + 0x000003e1, 0x000003e1, 0x000003e3, 0x000003e3, + 0x000003e5, 0x000003e5, 0x000003e7, 0x000003e7, + 0x000003e9, 0x000003e9, 0x000003eb, 0x000003eb, + 0x000003ed, 0x000003ed, 0x000003ef, 0x000003f3, + 0x000003f5, 0x000003f5, 0x00000430, 0x0000045f, + 0x00000461, 0x00000461, 0x00000463, 0x00000463, + 0x00000465, 0x00000465, 0x00000467, 0x00000467, + 0x00000469, 0x00000469, 0x0000046b, 0x0000046b, + 0x0000046d, 0x0000046d, 0x0000046f, 0x0000046f, + 0x00000471, 0x00000471, 0x00000473, 0x00000473, + 0x00000475, 0x00000475, 0x00000477, 0x00000477, + 0x00000479, 0x00000479, 0x0000047b, 0x0000047b, + 0x0000047d, 0x0000047d, 0x0000047f, 0x0000047f, + 0x00000481, 0x00000481, 0x0000048b, 0x0000048b, + 0x0000048d, 0x0000048d, 0x0000048f, 0x0000048f, + 0x00000491, 0x00000491, 0x00000493, 0x00000493, + 0x00000495, 0x00000495, 0x00000497, 0x00000497, + 0x00000499, 0x00000499, 0x0000049b, 0x0000049b, + 0x0000049d, 0x0000049d, 0x0000049f, 0x0000049f, + 0x000004a1, 0x000004a1, 0x000004a3, 0x000004a3, + 0x000004a5, 0x000004a5, 0x000004a7, 0x000004a7, + 0x000004a9, 0x000004a9, 0x000004ab, 0x000004ab, + 0x000004ad, 0x000004ad, 0x000004af, 0x000004af, + 0x000004b1, 0x000004b1, 0x000004b3, 0x000004b3, + 0x000004b5, 0x000004b5, 0x000004b7, 0x000004b7, + 0x000004b9, 0x000004b9, 0x000004bb, 0x000004bb, + 0x000004bd, 0x000004bd, 0x000004bf, 0x000004bf, + 0x000004c2, 0x000004c2, 0x000004c4, 0x000004c4, + 0x000004c6, 0x000004c6, 0x000004c8, 0x000004c8, + 0x000004ca, 0x000004ca, 0x000004cc, 0x000004cc, + 0x000004ce, 0x000004ce, 0x000004d1, 0x000004d1, + 0x000004d3, 0x000004d3, 0x000004d5, 0x000004d5, + 0x000004d7, 0x000004d7, 0x000004d9, 0x000004d9, + 0x000004db, 0x000004db, 0x000004dd, 0x000004dd, + 0x000004df, 0x000004df, 0x000004e1, 0x000004e1, + 0x000004e3, 0x000004e3, 0x000004e5, 0x000004e5, + 0x000004e7, 0x000004e7, 0x000004e9, 0x000004e9, + 0x000004eb, 0x000004eb, 0x000004ed, 0x000004ed, + 0x000004ef, 0x000004ef, 0x000004f1, 0x000004f1, + 0x000004f3, 0x000004f3, 0x000004f5, 0x000004f5, + 0x000004f9, 0x000004f9, 0x00000501, 0x00000501, + 0x00000503, 0x00000503, 0x00000505, 0x00000505, + 0x00000507, 0x00000507, 0x00000509, 0x00000509, + 0x0000050b, 0x0000050b, 0x0000050d, 0x0000050d, + 0x0000050f, 0x0000050f, 0x00000561, 0x00000587, + 0x00001e01, 0x00001e01, 0x00001e03, 0x00001e03, + 0x00001e05, 0x00001e05, 0x00001e07, 0x00001e07, + 0x00001e09, 0x00001e09, 0x00001e0b, 0x00001e0b, + 0x00001e0d, 0x00001e0d, 0x00001e0f, 0x00001e0f, + 0x00001e11, 0x00001e11, 0x00001e13, 0x00001e13, + 0x00001e15, 0x00001e15, 0x00001e17, 0x00001e17, + 0x00001e19, 0x00001e19, 0x00001e1b, 0x00001e1b, + 0x00001e1d, 0x00001e1d, 0x00001e1f, 0x00001e1f, + 0x00001e21, 0x00001e21, 0x00001e23, 0x00001e23, + 0x00001e25, 0x00001e25, 0x00001e27, 0x00001e27, + 0x00001e29, 0x00001e29, 0x00001e2b, 0x00001e2b, + 0x00001e2d, 0x00001e2d, 0x00001e2f, 0x00001e2f, + 0x00001e31, 0x00001e31, 0x00001e33, 0x00001e33, + 0x00001e35, 0x00001e35, 0x00001e37, 0x00001e37, + 0x00001e39, 0x00001e39, 0x00001e3b, 0x00001e3b, + 0x00001e3d, 0x00001e3d, 0x00001e3f, 0x00001e3f, + 0x00001e41, 0x00001e41, 0x00001e43, 0x00001e43, + 0x00001e45, 0x00001e45, 0x00001e47, 0x00001e47, + 0x00001e49, 0x00001e49, 0x00001e4b, 0x00001e4b, + 0x00001e4d, 0x00001e4d, 0x00001e4f, 0x00001e4f, + 0x00001e51, 0x00001e51, 0x00001e53, 0x00001e53, + 0x00001e55, 0x00001e55, 0x00001e57, 0x00001e57, + 0x00001e59, 0x00001e59, 0x00001e5b, 0x00001e5b, + 0x00001e5d, 0x00001e5d, 0x00001e5f, 0x00001e5f, + 0x00001e61, 0x00001e61, 0x00001e63, 0x00001e63, + 0x00001e65, 0x00001e65, 0x00001e67, 0x00001e67, + 0x00001e69, 0x00001e69, 0x00001e6b, 0x00001e6b, + 0x00001e6d, 0x00001e6d, 0x00001e6f, 0x00001e6f, + 0x00001e71, 0x00001e71, 0x00001e73, 0x00001e73, + 0x00001e75, 0x00001e75, 0x00001e77, 0x00001e77, + 0x00001e79, 0x00001e79, 0x00001e7b, 0x00001e7b, + 0x00001e7d, 0x00001e7d, 0x00001e7f, 0x00001e7f, + 0x00001e81, 0x00001e81, 0x00001e83, 0x00001e83, + 0x00001e85, 0x00001e85, 0x00001e87, 0x00001e87, + 0x00001e89, 0x00001e89, 0x00001e8b, 0x00001e8b, + 0x00001e8d, 0x00001e8d, 0x00001e8f, 0x00001e8f, + 0x00001e91, 0x00001e91, 0x00001e93, 0x00001e93, + 0x00001e95, 0x00001e9b, 0x00001ea1, 0x00001ea1, + 0x00001ea3, 0x00001ea3, 0x00001ea5, 0x00001ea5, + 0x00001ea7, 0x00001ea7, 0x00001ea9, 0x00001ea9, + 0x00001eab, 0x00001eab, 0x00001ead, 0x00001ead, + 0x00001eaf, 0x00001eaf, 0x00001eb1, 0x00001eb1, + 0x00001eb3, 0x00001eb3, 0x00001eb5, 0x00001eb5, + 0x00001eb7, 0x00001eb7, 0x00001eb9, 0x00001eb9, + 0x00001ebb, 0x00001ebb, 0x00001ebd, 0x00001ebd, + 0x00001ebf, 0x00001ebf, 0x00001ec1, 0x00001ec1, + 0x00001ec3, 0x00001ec3, 0x00001ec5, 0x00001ec5, + 0x00001ec7, 0x00001ec7, 0x00001ec9, 0x00001ec9, + 0x00001ecb, 0x00001ecb, 0x00001ecd, 0x00001ecd, + 0x00001ecf, 0x00001ecf, 0x00001ed1, 0x00001ed1, + 0x00001ed3, 0x00001ed3, 0x00001ed5, 0x00001ed5, + 0x00001ed7, 0x00001ed7, 0x00001ed9, 0x00001ed9, + 0x00001edb, 0x00001edb, 0x00001edd, 0x00001edd, + 0x00001edf, 0x00001edf, 0x00001ee1, 0x00001ee1, + 0x00001ee3, 0x00001ee3, 0x00001ee5, 0x00001ee5, + 0x00001ee7, 0x00001ee7, 0x00001ee9, 0x00001ee9, + 0x00001eeb, 0x00001eeb, 0x00001eed, 0x00001eed, + 0x00001eef, 0x00001eef, 0x00001ef1, 0x00001ef1, + 0x00001ef3, 0x00001ef3, 0x00001ef5, 0x00001ef5, + 0x00001ef7, 0x00001ef7, 0x00001ef9, 0x00001ef9, + 0x00001f00, 0x00001f07, 0x00001f10, 0x00001f15, + 0x00001f20, 0x00001f27, 0x00001f30, 0x00001f37, + 0x00001f40, 0x00001f45, 0x00001f50, 0x00001f57, + 0x00001f60, 0x00001f67, 0x00001f70, 0x00001f7d, + 0x00001f80, 0x00001f87, 0x00001f90, 0x00001f97, + 0x00001fa0, 0x00001fa7, 0x00001fb0, 0x00001fb4, + 0x00001fb6, 0x00001fb7, 0x00001fbe, 0x00001fbe, + 0x00001fc2, 0x00001fc4, 0x00001fc6, 0x00001fc7, + 0x00001fd0, 0x00001fd3, 0x00001fd6, 0x00001fd7, + 0x00001fe0, 0x00001fe7, 0x00001ff2, 0x00001ff4, + 0x00001ff6, 0x00001ff7, 0x00002071, 0x00002071, + 0x0000207f, 0x0000207f, 0x0000210a, 0x0000210a, + 0x0000210e, 0x0000210f, 0x00002113, 0x00002113, + 0x0000212f, 0x0000212f, 0x00002134, 0x00002134, + 0x00002139, 0x00002139, 0x0000213d, 0x0000213d, + 0x00002146, 0x00002149, 0x0000fb00, 0x0000fb06, + 0x0000fb13, 0x0000fb17, 0x0000ff41, 0x0000ff5a, + 0x00010428, 0x0001044d, 0x0001d41a, 0x0001d433, + 0x0001d44e, 0x0001d454, 0x0001d456, 0x0001d467, + 0x0001d482, 0x0001d49b, 0x0001d4b6, 0x0001d4b9, + 0x0001d4bb, 0x0001d4bb, 0x0001d4bd, 0x0001d4c0, + 0x0001d4c2, 0x0001d4c3, 0x0001d4c5, 0x0001d4cf, + 0x0001d4ea, 0x0001d503, 0x0001d51e, 0x0001d537, + 0x0001d552, 0x0001d56b, 0x0001d586, 0x0001d59f, + 0x0001d5ba, 0x0001d5d3, 0x0001d5ee, 0x0001d607, + 0x0001d622, 0x0001d63b, 0x0001d656, 0x0001d66f, + 0x0001d68a, 0x0001d6a3, 0x0001d6c2, 0x0001d6da, + 0x0001d6dc, 0x0001d6e1, 0x0001d6fc, 0x0001d714, + 0x0001d716, 0x0001d71b, 0x0001d736, 0x0001d74e, + 0x0001d750, 0x0001d755, 0x0001d770, 0x0001d788, + 0x0001d78a, 0x0001d78f, 0x0001d7aa, 0x0001d7c2, + 0x0001d7c4, 0x0001d7c9, 0x000001c5, 0x000001c5, + 0x000001c8, 0x000001c8, 0x000001cb, 0x000001cb, + 0x000001f2, 0x000001f2, 0x00001f88, 0x00001f8f, + 0x00001f98, 0x00001f9f, 0x00001fa8, 0x00001faf, + 0x00001fbc, 0x00001fbc, 0x00001fcc, 0x00001fcc, + 0x00001ffc, 0x00001ffc, 0x000002b0, 0x000002b8, + 0x000002bb, 0x000002c1, 0x000002d0, 0x000002d1, + 0x000002e0, 0x000002e4, 0x000002ee, 0x000002ee, + 0x0000037a, 0x0000037a, 0x00000559, 0x00000559, + 0x00000640, 0x00000640, 0x000006e5, 0x000006e6, + 0x00000e46, 0x00000e46, 0x00000ec6, 0x00000ec6, + 0x000017d7, 0x000017d7, 0x00001843, 0x00001843, + 0x00003005, 0x00003005, 0x00003031, 0x00003035, + 0x0000303b, 0x0000303b, 0x0000309d, 0x0000309e, + 0x000030fc, 0x000030fe, 0x0000ff70, 0x0000ff70, + 0x0000ff9e, 0x0000ff9f, 0x000001bb, 0x000001bb, + 0x000001c0, 0x000001c3, 0x000005d0, 0x000005ea, + 0x000005f0, 0x000005f2, 0x00000621, 0x0000063a, + 0x00000641, 0x0000064a, 0x0000066e, 0x0000066f, + 0x00000671, 0x000006d3, 0x000006d5, 0x000006d5, + 0x000006fa, 0x000006fc, 0x00000710, 0x00000710, + 0x00000712, 0x0000072c, 0x00000780, 0x000007a5, + 0x000007b1, 0x000007b1, 0x00000905, 0x00000939, + 0x0000093d, 0x0000093d, 0x00000950, 0x00000950, + 0x00000958, 0x00000961, 0x00000985, 0x0000098c, + 0x0000098f, 0x00000990, 0x00000993, 0x000009a8, + 0x000009aa, 0x000009b0, 0x000009b2, 0x000009b2, + 0x000009b6, 0x000009b9, 0x000009dc, 0x000009dd, + 0x000009df, 0x000009e1, 0x000009f0, 0x000009f1, + 0x00000a05, 0x00000a0a, 0x00000a0f, 0x00000a10, + 0x00000a13, 0x00000a28, 0x00000a2a, 0x00000a30, + 0x00000a32, 0x00000a33, 0x00000a35, 0x00000a36, + 0x00000a38, 0x00000a39, 0x00000a59, 0x00000a5c, + 0x00000a5e, 0x00000a5e, 0x00000a72, 0x00000a74, + 0x00000a85, 0x00000a8b, 0x00000a8d, 0x00000a8d, + 0x00000a8f, 0x00000a91, 0x00000a93, 0x00000aa8, + 0x00000aaa, 0x00000ab0, 0x00000ab2, 0x00000ab3, + 0x00000ab5, 0x00000ab9, 0x00000abd, 0x00000abd, + 0x00000ad0, 0x00000ad0, 0x00000ae0, 0x00000ae0, + 0x00000b05, 0x00000b0c, 0x00000b0f, 0x00000b10, + 0x00000b13, 0x00000b28, 0x00000b2a, 0x00000b30, + 0x00000b32, 0x00000b33, 0x00000b36, 0x00000b39, + 0x00000b3d, 0x00000b3d, 0x00000b5c, 0x00000b5d, + 0x00000b5f, 0x00000b61, 0x00000b83, 0x00000b83, + 0x00000b85, 0x00000b8a, 0x00000b8e, 0x00000b90, + 0x00000b92, 0x00000b95, 0x00000b99, 0x00000b9a, + 0x00000b9c, 0x00000b9c, 0x00000b9e, 0x00000b9f, + 0x00000ba3, 0x00000ba4, 0x00000ba8, 0x00000baa, + 0x00000bae, 0x00000bb5, 0x00000bb7, 0x00000bb9, + 0x00000c05, 0x00000c0c, 0x00000c0e, 0x00000c10, + 0x00000c12, 0x00000c28, 0x00000c2a, 0x00000c33, + 0x00000c35, 0x00000c39, 0x00000c60, 0x00000c61, + 0x00000c85, 0x00000c8c, 0x00000c8e, 0x00000c90, + 0x00000c92, 0x00000ca8, 0x00000caa, 0x00000cb3, + 0x00000cb5, 0x00000cb9, 0x00000cde, 0x00000cde, + 0x00000ce0, 0x00000ce1, 0x00000d05, 0x00000d0c, + 0x00000d0e, 0x00000d10, 0x00000d12, 0x00000d28, + 0x00000d2a, 0x00000d39, 0x00000d60, 0x00000d61, + 0x00000d85, 0x00000d96, 0x00000d9a, 0x00000db1, + 0x00000db3, 0x00000dbb, 0x00000dbd, 0x00000dbd, + 0x00000dc0, 0x00000dc6, 0x00000e01, 0x00000e30, + 0x00000e32, 0x00000e33, 0x00000e40, 0x00000e45, + 0x00000e81, 0x00000e82, 0x00000e84, 0x00000e84, + 0x00000e87, 0x00000e88, 0x00000e8a, 0x00000e8a, + 0x00000e8d, 0x00000e8d, 0x00000e94, 0x00000e97, + 0x00000e99, 0x00000e9f, 0x00000ea1, 0x00000ea3, + 0x00000ea5, 0x00000ea5, 0x00000ea7, 0x00000ea7, + 0x00000eaa, 0x00000eab, 0x00000ead, 0x00000eb0, + 0x00000eb2, 0x00000eb3, 0x00000ebd, 0x00000ebd, + 0x00000ec0, 0x00000ec4, 0x00000edc, 0x00000edd, + 0x00000f00, 0x00000f00, 0x00000f40, 0x00000f47, + 0x00000f49, 0x00000f6a, 0x00000f88, 0x00000f8b, + 0x00001000, 0x00001021, 0x00001023, 0x00001027, + 0x00001029, 0x0000102a, 0x00001050, 0x00001055, + 0x000010d0, 0x000010f8, 0x00001100, 0x00001159, + 0x0000115f, 0x000011a2, 0x000011a8, 0x000011f9, + 0x00001200, 0x00001206, 0x00001208, 0x00001246, + 0x00001248, 0x00001248, 0x0000124a, 0x0000124d, + 0x00001250, 0x00001256, 0x00001258, 0x00001258, + 0x0000125a, 0x0000125d, 0x00001260, 0x00001286, + 0x00001288, 0x00001288, 0x0000128a, 0x0000128d, + 0x00001290, 0x000012ae, 0x000012b0, 0x000012b0, + 0x000012b2, 0x000012b5, 0x000012b8, 0x000012be, + 0x000012c0, 0x000012c0, 0x000012c2, 0x000012c5, + 0x000012c8, 0x000012ce, 0x000012d0, 0x000012d6, + 0x000012d8, 0x000012ee, 0x000012f0, 0x0000130e, + 0x00001310, 0x00001310, 0x00001312, 0x00001315, + 0x00001318, 0x0000131e, 0x00001320, 0x00001346, + 0x00001348, 0x0000135a, 0x000013a0, 0x000013f4, + 0x00001401, 0x0000166c, 0x0000166f, 0x00001676, + 0x00001681, 0x0000169a, 0x000016a0, 0x000016ea, + 0x00001700, 0x0000170c, 0x0000170e, 0x00001711, + 0x00001720, 0x00001731, 0x00001740, 0x00001751, + 0x00001760, 0x0000176c, 0x0000176e, 0x00001770, + 0x00001780, 0x000017b3, 0x000017dc, 0x000017dc, + 0x00001820, 0x00001842, 0x00001844, 0x00001877, + 0x00001880, 0x000018a8, 0x00002135, 0x00002138, + 0x00003006, 0x00003006, 0x0000303c, 0x0000303c, + 0x00003041, 0x00003096, 0x0000309f, 0x0000309f, + 0x000030a1, 0x000030fa, 0x000030ff, 0x000030ff, + 0x00003105, 0x0000312c, 0x00003131, 0x0000318e, + 0x000031a0, 0x000031b7, 0x000031f0, 0x000031ff, + 0x00003400, 0x00004db5, 0x00004e00, 0x0000a48c, + 0x0000ac00, 0x0000d7a3, 0x0000f900, 0x0000faff, + 0x0000fb1d, 0x0000fb1d, 0x0000fb1f, 0x0000fb28, + 0x0000fb2a, 0x0000fb36, 0x0000fb38, 0x0000fb3c, + 0x0000fb3e, 0x0000fb3e, 0x0000fb40, 0x0000fb41, + 0x0000fb43, 0x0000fb44, 0x0000fb46, 0x0000fbb1, + 0x0000fbd3, 0x0000fd3d, 0x0000fd50, 0x0000fd8f, + 0x0000fd92, 0x0000fdc7, 0x0000fdf0, 0x0000fdfb, + 0x0000fe70, 0x0000fe74, 0x0000fe76, 0x0000fefc, + 0x0000ff66, 0x0000ff6f, 0x0000ff71, 0x0000ff9d, + 0x0000ffa0, 0x0000ffbe, 0x0000ffc2, 0x0000ffc7, + 0x0000ffca, 0x0000ffcf, 0x0000ffd2, 0x0000ffd7, + 0x0000ffda, 0x0000ffdc, 0x00010300, 0x0001031e, + 0x00010330, 0x00010349, 0x00020000, 0x0002a6d6, + 0x0002f800, 0x0002fa1d, 0x0000005f, 0x0000005f, + 0x0000203f, 0x00002040, 0x000030fb, 0x000030fb, + 0x0000fe33, 0x0000fe34, 0x0000fe4d, 0x0000fe4f, + 0x0000ff3f, 0x0000ff3f, 0x0000ff65, 0x0000ff65, + 0x0000002d, 0x0000002d, 0x000000ad, 0x000000ad, + 0x0000058a, 0x0000058a, 0x00001806, 0x00001806, + 0x00002010, 0x00002015, 0x0000301c, 0x0000301c, + 0x00003030, 0x00003030, 0x000030a0, 0x000030a0, + 0x0000fe31, 0x0000fe32, 0x0000fe58, 0x0000fe58, + 0x0000fe63, 0x0000fe63, 0x0000ff0d, 0x0000ff0d, + 0x00000028, 0x00000028, 0x0000005b, 0x0000005b, + 0x0000007b, 0x0000007b, 0x00000f3a, 0x00000f3a, + 0x00000f3c, 0x00000f3c, 0x0000169b, 0x0000169b, + 0x0000201a, 0x0000201a, 0x0000201e, 0x0000201e, + 0x00002045, 0x00002045, 0x0000207d, 0x0000207d, + 0x0000208d, 0x0000208d, 0x00002329, 0x00002329, + 0x000023b4, 0x000023b4, 0x00002768, 0x00002768, + 0x0000276a, 0x0000276a, 0x0000276c, 0x0000276c, + 0x0000276e, 0x0000276e, 0x00002770, 0x00002770, + 0x00002772, 0x00002772, 0x00002774, 0x00002774, + 0x000027e6, 0x000027e6, 0x000027e8, 0x000027e8, + 0x000027ea, 0x000027ea, 0x00002983, 0x00002983, + 0x00002985, 0x00002985, 0x00002987, 0x00002987, + 0x00002989, 0x00002989, 0x0000298b, 0x0000298b, + 0x0000298d, 0x0000298d, 0x0000298f, 0x0000298f, + 0x00002991, 0x00002991, 0x00002993, 0x00002993, + 0x00002995, 0x00002995, 0x00002997, 0x00002997, + 0x000029d8, 0x000029d8, 0x000029da, 0x000029da, + 0x000029fc, 0x000029fc, 0x00003008, 0x00003008, + 0x0000300a, 0x0000300a, 0x0000300c, 0x0000300c, + 0x0000300e, 0x0000300e, 0x00003010, 0x00003010, + 0x00003014, 0x00003014, 0x00003016, 0x00003016, + 0x00003018, 0x00003018, 0x0000301a, 0x0000301a, + 0x0000301d, 0x0000301d, 0x0000fd3e, 0x0000fd3e, + 0x0000fe35, 0x0000fe35, 0x0000fe37, 0x0000fe37, + 0x0000fe39, 0x0000fe39, 0x0000fe3b, 0x0000fe3b, + 0x0000fe3d, 0x0000fe3d, 0x0000fe3f, 0x0000fe3f, + 0x0000fe41, 0x0000fe41, 0x0000fe43, 0x0000fe43, + 0x0000fe59, 0x0000fe59, 0x0000fe5b, 0x0000fe5b, + 0x0000fe5d, 0x0000fe5d, 0x0000ff08, 0x0000ff08, + 0x0000ff3b, 0x0000ff3b, 0x0000ff5b, 0x0000ff5b, + 0x0000ff5f, 0x0000ff5f, 0x0000ff62, 0x0000ff62, + 0x00000029, 0x00000029, 0x0000005d, 0x0000005d, + 0x0000007d, 0x0000007d, 0x00000f3b, 0x00000f3b, + 0x00000f3d, 0x00000f3d, 0x0000169c, 0x0000169c, + 0x00002046, 0x00002046, 0x0000207e, 0x0000207e, + 0x0000208e, 0x0000208e, 0x0000232a, 0x0000232a, + 0x000023b5, 0x000023b5, 0x00002769, 0x00002769, + 0x0000276b, 0x0000276b, 0x0000276d, 0x0000276d, + 0x0000276f, 0x0000276f, 0x00002771, 0x00002771, + 0x00002773, 0x00002773, 0x00002775, 0x00002775, + 0x000027e7, 0x000027e7, 0x000027e9, 0x000027e9, + 0x000027eb, 0x000027eb, 0x00002984, 0x00002984, + 0x00002986, 0x00002986, 0x00002988, 0x00002988, + 0x0000298a, 0x0000298a, 0x0000298c, 0x0000298c, + 0x0000298e, 0x0000298e, 0x00002990, 0x00002990, + 0x00002992, 0x00002992, 0x00002994, 0x00002994, + 0x00002996, 0x00002996, 0x00002998, 0x00002998, + 0x000029d9, 0x000029d9, 0x000029db, 0x000029db, + 0x000029fd, 0x000029fd, 0x00003009, 0x00003009, + 0x0000300b, 0x0000300b, 0x0000300d, 0x0000300d, + 0x0000300f, 0x0000300f, 0x00003011, 0x00003011, + 0x00003015, 0x00003015, 0x00003017, 0x00003017, + 0x00003019, 0x00003019, 0x0000301b, 0x0000301b, + 0x0000301e, 0x0000301f, 0x0000fd3f, 0x0000fd3f, + 0x0000fe36, 0x0000fe36, 0x0000fe38, 0x0000fe38, + 0x0000fe3a, 0x0000fe3a, 0x0000fe3c, 0x0000fe3c, + 0x0000fe3e, 0x0000fe3e, 0x0000fe40, 0x0000fe40, + 0x0000fe42, 0x0000fe42, 0x0000fe44, 0x0000fe44, + 0x0000fe5a, 0x0000fe5a, 0x0000fe5c, 0x0000fe5c, + 0x0000fe5e, 0x0000fe5e, 0x0000ff09, 0x0000ff09, + 0x0000ff3d, 0x0000ff3d, 0x0000ff5d, 0x0000ff5d, + 0x0000ff60, 0x0000ff60, 0x0000ff63, 0x0000ff63, + 0x00000021, 0x00000023, 0x00000025, 0x00000027, + 0x0000002a, 0x0000002a, 0x0000002c, 0x0000002c, + 0x0000002e, 0x0000002f, 0x0000003a, 0x0000003b, + 0x0000003f, 0x00000040, 0x0000005c, 0x0000005c, + 0x000000a1, 0x000000a1, 0x000000b7, 0x000000b7, + 0x000000bf, 0x000000bf, 0x0000037e, 0x0000037e, + 0x00000387, 0x00000387, 0x0000055a, 0x0000055f, + 0x00000589, 0x00000589, 0x000005be, 0x000005be, + 0x000005c0, 0x000005c0, 0x000005c3, 0x000005c3, + 0x000005f3, 0x000005f4, 0x0000060c, 0x0000060c, + 0x0000061b, 0x0000061b, 0x0000061f, 0x0000061f, + 0x0000066a, 0x0000066d, 0x000006d4, 0x000006d4, + 0x00000700, 0x0000070d, 0x00000964, 0x00000965, + 0x00000970, 0x00000970, 0x00000df4, 0x00000df4, + 0x00000e4f, 0x00000e4f, 0x00000e5a, 0x00000e5b, + 0x00000f04, 0x00000f12, 0x00000f85, 0x00000f85, + 0x0000104a, 0x0000104f, 0x000010fb, 0x000010fb, + 0x00001361, 0x00001368, 0x0000166d, 0x0000166e, + 0x000016eb, 0x000016ed, 0x00001735, 0x00001736, + 0x000017d4, 0x000017d6, 0x000017d8, 0x000017da, + 0x00001800, 0x00001805, 0x00001807, 0x0000180a, + 0x00002016, 0x00002017, 0x00002020, 0x00002027, + 0x00002030, 0x00002038, 0x0000203b, 0x0000203e, + 0x00002041, 0x00002043, 0x00002047, 0x00002051, + 0x00002057, 0x00002057, 0x000023b6, 0x000023b6, + 0x00003001, 0x00003003, 0x0000303d, 0x0000303d, + 0x0000fe30, 0x0000fe30, 0x0000fe45, 0x0000fe46, + 0x0000fe49, 0x0000fe4c, 0x0000fe50, 0x0000fe52, + 0x0000fe54, 0x0000fe57, 0x0000fe5f, 0x0000fe61, + 0x0000fe68, 0x0000fe68, 0x0000fe6a, 0x0000fe6b, + 0x0000ff01, 0x0000ff03, 0x0000ff05, 0x0000ff07, + 0x0000ff0a, 0x0000ff0a, 0x0000ff0c, 0x0000ff0c, + 0x0000ff0e, 0x0000ff0f, 0x0000ff1a, 0x0000ff1b, + 0x0000ff1f, 0x0000ff20, 0x0000ff3c, 0x0000ff3c, + 0x0000ff61, 0x0000ff61, 0x0000ff64, 0x0000ff64, + 0x0000002b, 0x0000002b, 0x0000003c, 0x0000003e, + 0x0000007c, 0x0000007c, 0x0000007e, 0x0000007e, + 0x000000ac, 0x000000ac, 0x000000b1, 0x000000b1, + 0x000000d7, 0x000000d7, 0x000000f7, 0x000000f7, + 0x000003f6, 0x000003f6, 0x00002044, 0x00002044, + 0x00002052, 0x00002052, 0x0000207a, 0x0000207c, + 0x0000208a, 0x0000208c, 0x00002140, 0x00002144, + 0x0000214b, 0x0000214b, 0x00002190, 0x00002194, + 0x0000219a, 0x0000219b, 0x000021a0, 0x000021a0, + 0x000021a3, 0x000021a3, 0x000021a6, 0x000021a6, + 0x000021ae, 0x000021ae, 0x000021ce, 0x000021cf, + 0x000021d2, 0x000021d2, 0x000021d4, 0x000021d4, + 0x000021f4, 0x000022ff, 0x00002308, 0x0000230b, + 0x00002320, 0x00002321, 0x0000237c, 0x0000237c, + 0x0000239b, 0x000023b3, 0x000025b7, 0x000025b7, + 0x000025c1, 0x000025c1, 0x000025f8, 0x000025ff, + 0x0000266f, 0x0000266f, 0x000027d0, 0x000027e5, + 0x000027f0, 0x000027ff, 0x00002900, 0x00002982, + 0x00002999, 0x000029d7, 0x000029dc, 0x000029fb, + 0x000029fe, 0x00002aff, 0x0000fb29, 0x0000fb29, + 0x0000fe62, 0x0000fe62, 0x0000fe64, 0x0000fe66, + 0x0000ff0b, 0x0000ff0b, 0x0000ff1c, 0x0000ff1e, + 0x0000ff5c, 0x0000ff5c, 0x0000ff5e, 0x0000ff5e, + 0x0000ffe2, 0x0000ffe2, 0x0000ffe9, 0x0000ffec, + 0x0001d6c1, 0x0001d6c1, 0x0001d6db, 0x0001d6db, + 0x0001d6fb, 0x0001d6fb, 0x0001d715, 0x0001d715, + 0x0001d735, 0x0001d735, 0x0001d74f, 0x0001d74f, + 0x0001d76f, 0x0001d76f, 0x0001d789, 0x0001d789, + 0x0001d7a9, 0x0001d7a9, 0x0001d7c3, 0x0001d7c3, + 0x00000024, 0x00000024, 0x000000a2, 0x000000a5, + 0x000009f2, 0x000009f3, 0x00000e3f, 0x00000e3f, + 0x000017db, 0x000017db, 0x000020a0, 0x000020b1, + 0x0000fdfc, 0x0000fdfc, 0x0000fe69, 0x0000fe69, + 0x0000ff04, 0x0000ff04, 0x0000ffe0, 0x0000ffe1, + 0x0000ffe5, 0x0000ffe6, 0x0000005e, 0x0000005e, + 0x00000060, 0x00000060, 0x000000a8, 0x000000a8, + 0x000000af, 0x000000af, 0x000000b4, 0x000000b4, + 0x000000b8, 0x000000b8, 0x000002b9, 0x000002ba, + 0x000002c2, 0x000002cf, 0x000002d2, 0x000002df, + 0x000002e5, 0x000002ed, 0x00000374, 0x00000375, + 0x00000384, 0x00000385, 0x00001fbd, 0x00001fbd, + 0x00001fbf, 0x00001fc1, 0x00001fcd, 0x00001fcf, + 0x00001fdd, 0x00001fdf, 0x00001fed, 0x00001fef, + 0x00001ffd, 0x00001ffe, 0x0000309b, 0x0000309c, + 0x0000ff3e, 0x0000ff3e, 0x0000ff40, 0x0000ff40, + 0x0000ffe3, 0x0000ffe3, 0x000000a6, 0x000000a7, + 0x000000a9, 0x000000a9, 0x000000ae, 0x000000ae, + 0x000000b0, 0x000000b0, 0x000000b6, 0x000000b6, + 0x00000482, 0x00000482, 0x000006e9, 0x000006e9, + 0x000006fd, 0x000006fe, 0x000009fa, 0x000009fa, + 0x00000b70, 0x00000b70, 0x00000f01, 0x00000f03, + 0x00000f13, 0x00000f17, 0x00000f1a, 0x00000f1f, + 0x00000f34, 0x00000f34, 0x00000f36, 0x00000f36, + 0x00000f38, 0x00000f38, 0x00000fbe, 0x00000fc5, + 0x00000fc7, 0x00000fcc, 0x00000fcf, 0x00000fcf, + 0x00002100, 0x00002101, 0x00002103, 0x00002106, + 0x00002108, 0x00002109, 0x00002114, 0x00002114, + 0x00002116, 0x00002118, 0x0000211e, 0x00002123, + 0x00002125, 0x00002125, 0x00002127, 0x00002127, + 0x00002129, 0x00002129, 0x0000212e, 0x0000212e, + 0x00002132, 0x00002132, 0x0000213a, 0x0000213a, + 0x0000214a, 0x0000214a, 0x00002195, 0x00002199, + 0x0000219c, 0x0000219f, 0x000021a1, 0x000021a2, + 0x000021a4, 0x000021a5, 0x000021a7, 0x000021ad, + 0x000021af, 0x000021cd, 0x000021d0, 0x000021d1, + 0x000021d3, 0x000021d3, 0x000021d5, 0x000021f3, + 0x00002300, 0x00002307, 0x0000230c, 0x0000231f, + 0x00002322, 0x00002328, 0x0000232b, 0x0000237b, + 0x0000237d, 0x0000239a, 0x000023b7, 0x000023ce, + 0x00002400, 0x00002426, 0x00002440, 0x0000244a, + 0x0000249c, 0x000024e9, 0x00002500, 0x000025b6, + 0x000025b8, 0x000025c0, 0x000025c2, 0x000025f7, + 0x00002600, 0x00002613, 0x00002616, 0x00002617, + 0x00002619, 0x0000266e, 0x00002670, 0x0000267d, + 0x00002680, 0x00002689, 0x00002701, 0x00002704, + 0x00002706, 0x00002709, 0x0000270c, 0x00002727, + 0x00002729, 0x0000274b, 0x0000274d, 0x0000274d, + 0x0000274f, 0x00002752, 0x00002756, 0x00002756, + 0x00002758, 0x0000275e, 0x00002761, 0x00002767, + 0x00002794, 0x00002794, 0x00002798, 0x000027af, + 0x000027b1, 0x000027be, 0x00002800, 0x000028ff, + 0x00002e80, 0x00002e99, 0x00002e9b, 0x00002ef3, + 0x00002f00, 0x00002fd5, 0x00002ff0, 0x00002ffb, + 0x00003004, 0x00003004, 0x00003012, 0x00003013, + 0x00003020, 0x00003020, 0x00003036, 0x00003037, + 0x0000303e, 0x0000303f, 0x00003190, 0x00003191, + 0x00003196, 0x0000319f, 0x00003200, 0x0000321c, + 0x0000322a, 0x00003243, 0x00003260, 0x0000327b, + 0x0000327f, 0x0000327f, 0x0000328a, 0x000032b0, + 0x000032c0, 0x000032cb, 0x000032d0, 0x000032fe, + 0x00003300, 0x00003376, 0x0000337b, 0x000033dd, + 0x000033e0, 0x000033fe, 0x0000a490, 0x0000a4c6, + 0x0000ffe4, 0x0000ffe4, 0x0000ffe8, 0x0000ffe8, + 0x0000ffed, 0x0000ffee, 0x0000fffc, 0x0000fffd, + 0x0001d000, 0x0001d0f5, 0x0001d100, 0x0001d126, + 0x0001d12a, 0x0001d164, 0x0001d16a, 0x0001d16c, + 0x0001d183, 0x0001d184, 0x0001d18c, 0x0001d1a9, + 0x0001d1ae, 0x0001d1dd, 0x00000041, 0x0000005a, + 0x00000061, 0x0000007a, 0x000000aa, 0x000000aa, + 0x000000b5, 0x000000b5, 0x000000ba, 0x000000ba, + 0x000000c0, 0x000000d6, 0x000000d8, 0x000000f6, + 0x000000f8, 0x00000220, 0x00000222, 0x00000233, + 0x00000250, 0x000002ad, 0x000002b0, 0x000002b8, + 0x000002bb, 0x000002c1, 0x000002d0, 0x000002d1, + 0x000002e0, 0x000002e4, 0x000002ee, 0x000002ee, + 0x0000037a, 0x0000037a, 0x00000386, 0x00000386, + 0x00000388, 0x0000038a, 0x0000038c, 0x0000038c, + 0x0000038e, 0x000003a1, 0x000003a3, 0x000003ce, + 0x000003d0, 0x000003f5, 0x00000400, 0x00000482, + 0x0000048a, 0x000004ce, 0x000004d0, 0x000004f5, + 0x000004f8, 0x000004f9, 0x00000500, 0x0000050f, + 0x00000531, 0x00000556, 0x00000559, 0x0000055f, + 0x00000561, 0x00000587, 0x00000589, 0x00000589, + 0x00000903, 0x00000903, 0x00000905, 0x00000939, + 0x0000093d, 0x00000940, 0x00000949, 0x0000094c, + 0x00000950, 0x00000950, 0x00000958, 0x00000961, + 0x00000964, 0x00000970, 0x00000982, 0x00000983, + 0x00000985, 0x0000098c, 0x0000098f, 0x00000990, + 0x00000993, 0x000009a8, 0x000009aa, 0x000009b0, + 0x000009b2, 0x000009b2, 0x000009b6, 0x000009b9, + 0x000009be, 0x000009c0, 0x000009c7, 0x000009c8, + 0x000009cb, 0x000009cc, 0x000009d7, 0x000009d7, + 0x000009dc, 0x000009dd, 0x000009df, 0x000009e1, + 0x000009e6, 0x000009f1, 0x000009f4, 0x000009fa, + 0x00000a05, 0x00000a0a, 0x00000a0f, 0x00000a10, + 0x00000a13, 0x00000a28, 0x00000a2a, 0x00000a30, + 0x00000a32, 0x00000a33, 0x00000a35, 0x00000a36, + 0x00000a38, 0x00000a39, 0x00000a3e, 0x00000a40, + 0x00000a59, 0x00000a5c, 0x00000a5e, 0x00000a5e, + 0x00000a66, 0x00000a6f, 0x00000a72, 0x00000a74, + 0x00000a83, 0x00000a83, 0x00000a85, 0x00000a8b, + 0x00000a8d, 0x00000a8d, 0x00000a8f, 0x00000a91, + 0x00000a93, 0x00000aa8, 0x00000aaa, 0x00000ab0, + 0x00000ab2, 0x00000ab3, 0x00000ab5, 0x00000ab9, + 0x00000abd, 0x00000ac0, 0x00000ac9, 0x00000ac9, + 0x00000acb, 0x00000acc, 0x00000ad0, 0x00000ad0, + 0x00000ae0, 0x00000ae0, 0x00000ae6, 0x00000aef, + 0x00000b02, 0x00000b03, 0x00000b05, 0x00000b0c, + 0x00000b0f, 0x00000b10, 0x00000b13, 0x00000b28, + 0x00000b2a, 0x00000b30, 0x00000b32, 0x00000b33, + 0x00000b36, 0x00000b39, 0x00000b3d, 0x00000b3e, + 0x00000b40, 0x00000b40, 0x00000b47, 0x00000b48, + 0x00000b4b, 0x00000b4c, 0x00000b57, 0x00000b57, + 0x00000b5c, 0x00000b5d, 0x00000b5f, 0x00000b61, + 0x00000b66, 0x00000b70, 0x00000b83, 0x00000b83, + 0x00000b85, 0x00000b8a, 0x00000b8e, 0x00000b90, + 0x00000b92, 0x00000b95, 0x00000b99, 0x00000b9a, + 0x00000b9c, 0x00000b9c, 0x00000b9e, 0x00000b9f, + 0x00000ba3, 0x00000ba4, 0x00000ba8, 0x00000baa, + 0x00000bae, 0x00000bb5, 0x00000bb7, 0x00000bb9, + 0x00000bbe, 0x00000bbf, 0x00000bc1, 0x00000bc2, + 0x00000bc6, 0x00000bc8, 0x00000bca, 0x00000bcc, + 0x00000bd7, 0x00000bd7, 0x00000be7, 0x00000bf2, + 0x00000c01, 0x00000c03, 0x00000c05, 0x00000c0c, + 0x00000c0e, 0x00000c10, 0x00000c12, 0x00000c28, + 0x00000c2a, 0x00000c33, 0x00000c35, 0x00000c39, + 0x00000c41, 0x00000c44, 0x00000c60, 0x00000c61, + 0x00000c66, 0x00000c6f, 0x00000c82, 0x00000c83, + 0x00000c85, 0x00000c8c, 0x00000c8e, 0x00000c90, + 0x00000c92, 0x00000ca8, 0x00000caa, 0x00000cb3, + 0x00000cb5, 0x00000cb9, 0x00000cbe, 0x00000cbe, + 0x00000cc0, 0x00000cc4, 0x00000cc7, 0x00000cc8, + 0x00000cca, 0x00000ccb, 0x00000cd5, 0x00000cd6, + 0x00000cde, 0x00000cde, 0x00000ce0, 0x00000ce1, + 0x00000ce6, 0x00000cef, 0x00000d02, 0x00000d03, + 0x00000d05, 0x00000d0c, 0x00000d0e, 0x00000d10, + 0x00000d12, 0x00000d28, 0x00000d2a, 0x00000d39, + 0x00000d3e, 0x00000d40, 0x00000d46, 0x00000d48, + 0x00000d4a, 0x00000d4c, 0x00000d57, 0x00000d57, + 0x00000d60, 0x00000d61, 0x00000d66, 0x00000d6f, + 0x00000d82, 0x00000d83, 0x00000d85, 0x00000d96, + 0x00000d9a, 0x00000db1, 0x00000db3, 0x00000dbb, + 0x00000dbd, 0x00000dbd, 0x00000dc0, 0x00000dc6, + 0x00000dcf, 0x00000dd1, 0x00000dd8, 0x00000ddf, + 0x00000df2, 0x00000df4, 0x00000e01, 0x00000e30, + 0x00000e32, 0x00000e33, 0x00000e40, 0x00000e46, + 0x00000e4f, 0x00000e5b, 0x00000e81, 0x00000e82, + 0x00000e84, 0x00000e84, 0x00000e87, 0x00000e88, + 0x00000e8a, 0x00000e8a, 0x00000e8d, 0x00000e8d, + 0x00000e94, 0x00000e97, 0x00000e99, 0x00000e9f, + 0x00000ea1, 0x00000ea3, 0x00000ea5, 0x00000ea5, + 0x00000ea7, 0x00000ea7, 0x00000eaa, 0x00000eab, + 0x00000ead, 0x00000eb0, 0x00000eb2, 0x00000eb3, + 0x00000ebd, 0x00000ebd, 0x00000ec0, 0x00000ec4, + 0x00000ec6, 0x00000ec6, 0x00000ed0, 0x00000ed9, + 0x00000edc, 0x00000edd, 0x00000f00, 0x00000f17, + 0x00000f1a, 0x00000f34, 0x00000f36, 0x00000f36, + 0x00000f38, 0x00000f38, 0x00000f3e, 0x00000f47, + 0x00000f49, 0x00000f6a, 0x00000f7f, 0x00000f7f, + 0x00000f85, 0x00000f85, 0x00000f88, 0x00000f8b, + 0x00000fbe, 0x00000fc5, 0x00000fc7, 0x00000fcc, + 0x00000fcf, 0x00000fcf, 0x00001000, 0x00001021, + 0x00001023, 0x00001027, 0x00001029, 0x0000102a, + 0x0000102c, 0x0000102c, 0x00001031, 0x00001031, + 0x00001038, 0x00001038, 0x00001040, 0x00001057, + 0x000010a0, 0x000010c5, 0x000010d0, 0x000010f8, + 0x000010fb, 0x000010fb, 0x00001100, 0x00001159, + 0x0000115f, 0x000011a2, 0x000011a8, 0x000011f9, + 0x00001200, 0x00001206, 0x00001208, 0x00001246, + 0x00001248, 0x00001248, 0x0000124a, 0x0000124d, + 0x00001250, 0x00001256, 0x00001258, 0x00001258, + 0x0000125a, 0x0000125d, 0x00001260, 0x00001286, + 0x00001288, 0x00001288, 0x0000128a, 0x0000128d, + 0x00001290, 0x000012ae, 0x000012b0, 0x000012b0, + 0x000012b2, 0x000012b5, 0x000012b8, 0x000012be, + 0x000012c0, 0x000012c0, 0x000012c2, 0x000012c5, + 0x000012c8, 0x000012ce, 0x000012d0, 0x000012d6, + 0x000012d8, 0x000012ee, 0x000012f0, 0x0000130e, + 0x00001310, 0x00001310, 0x00001312, 0x00001315, + 0x00001318, 0x0000131e, 0x00001320, 0x00001346, + 0x00001348, 0x0000135a, 0x00001361, 0x0000137c, + 0x000013a0, 0x000013f4, 0x00001401, 0x00001676, + 0x00001681, 0x0000169a, 0x000016a0, 0x000016f0, + 0x00001700, 0x0000170c, 0x0000170e, 0x00001711, + 0x00001720, 0x00001731, 0x00001735, 0x00001736, + 0x00001740, 0x00001751, 0x00001760, 0x0000176c, + 0x0000176e, 0x00001770, 0x00001780, 0x000017b6, + 0x000017be, 0x000017c5, 0x000017c7, 0x000017c8, + 0x000017d4, 0x000017da, 0x000017dc, 0x000017dc, + 0x000017e0, 0x000017e9, 0x00001810, 0x00001819, + 0x00001820, 0x00001877, 0x00001880, 0x000018a8, + 0x00001e00, 0x00001e9b, 0x00001ea0, 0x00001ef9, + 0x00001f00, 0x00001f15, 0x00001f18, 0x00001f1d, + 0x00001f20, 0x00001f45, 0x00001f48, 0x00001f4d, + 0x00001f50, 0x00001f57, 0x00001f59, 0x00001f59, + 0x00001f5b, 0x00001f5b, 0x00001f5d, 0x00001f5d, + 0x00001f5f, 0x00001f7d, 0x00001f80, 0x00001fb4, + 0x00001fb6, 0x00001fbc, 0x00001fbe, 0x00001fbe, + 0x00001fc2, 0x00001fc4, 0x00001fc6, 0x00001fcc, + 0x00001fd0, 0x00001fd3, 0x00001fd6, 0x00001fdb, + 0x00001fe0, 0x00001fec, 0x00001ff2, 0x00001ff4, + 0x00001ff6, 0x00001ffc, 0x0000200e, 0x0000200e, + 0x00002071, 0x00002071, 0x0000207f, 0x0000207f, + 0x00002102, 0x00002102, 0x00002107, 0x00002107, + 0x0000210a, 0x00002113, 0x00002115, 0x00002115, + 0x00002119, 0x0000211d, 0x00002124, 0x00002124, + 0x00002126, 0x00002126, 0x00002128, 0x00002128, + 0x0000212a, 0x0000212d, 0x0000212f, 0x00002131, + 0x00002133, 0x00002139, 0x0000213d, 0x0000213f, + 0x00002145, 0x00002149, 0x00002160, 0x00002183, + 0x00002336, 0x0000237a, 0x00002395, 0x00002395, + 0x0000249c, 0x000024e9, 0x00003005, 0x00003007, + 0x00003021, 0x00003029, 0x00003031, 0x00003035, + 0x00003038, 0x0000303c, 0x00003041, 0x00003096, + 0x0000309d, 0x0000309f, 0x000030a1, 0x000030fa, + 0x000030fc, 0x000030ff, 0x00003105, 0x0000312c, + 0x00003131, 0x0000318e, 0x00003190, 0x000031b7, + 0x000031f0, 0x0000321c, 0x00003220, 0x00003243, + 0x00003260, 0x0000327b, 0x0000327f, 0x000032b0, + 0x000032c0, 0x000032cb, 0x000032d0, 0x000032fe, + 0x00003300, 0x00003376, 0x0000337b, 0x000033dd, + 0x000033e0, 0x000033fe, 0x00003400, 0x00004db5, + 0x00004e00, 0x0000a48c, 0x0000ac00, 0x0000d7a3, + 0x0000e000, 0x0000fb06, 0x0000fb13, 0x0000fb17, + 0x0000ff21, 0x0000ff3a, 0x0000ff41, 0x0000ff5a, + 0x0000ff66, 0x0000ffbe, 0x0000ffc2, 0x0000ffc7, + 0x0000ffca, 0x0000ffcf, 0x0000ffd2, 0x0000ffd7, + 0x0000ffda, 0x0000ffdc, 0x00010000, 0x0002a6d6, + 0x0002f800, 0x0002fa1d, 0x000f0000, 0x000ffffd, + 0x00100000, 0x0010fffd, 0x000005be, 0x000005be, + 0x000005c0, 0x000005c0, 0x000005c3, 0x000005c3, + 0x000005d0, 0x000005ea, 0x000005f0, 0x000005f4, + 0x0000200f, 0x0000200f, 0x0000fb1d, 0x0000fb1d, + 0x0000fb1f, 0x0000fb28, 0x0000fb2a, 0x0000fb36, + 0x0000fb38, 0x0000fb3c, 0x0000fb3e, 0x0000fb3e, + 0x0000fb40, 0x0000fb41, 0x0000fb43, 0x0000fb44, + 0x0000fb46, 0x0000fb4f, 0x00000030, 0x00000039, + 0x000000b2, 0x000000b3, 0x000000b9, 0x000000b9, + 0x000006f0, 0x000006f9, 0x00002070, 0x00002070, + 0x00002074, 0x00002079, 0x00002080, 0x00002089, + 0x00002460, 0x0000249b, 0x000024ea, 0x000024ea, + 0x0000ff10, 0x0000ff19, 0x0001d7ce, 0x0001d7ff, + 0x0000002f, 0x0000002f, 0x0000ff0f, 0x0000ff0f, + 0x00000023, 0x00000025, 0x0000002b, 0x0000002b, + 0x0000002d, 0x0000002d, 0x000000a2, 0x000000a5, + 0x000000b0, 0x000000b1, 0x0000066a, 0x0000066a, + 0x000009f2, 0x000009f3, 0x00000e3f, 0x00000e3f, + 0x000017db, 0x000017db, 0x00002030, 0x00002034, + 0x0000207a, 0x0000207b, 0x0000208a, 0x0000208b, + 0x000020a0, 0x000020b1, 0x0000212e, 0x0000212e, + 0x00002212, 0x00002213, 0x0000fb29, 0x0000fb29, + 0x0000fe5f, 0x0000fe5f, 0x0000fe62, 0x0000fe63, + 0x0000fe69, 0x0000fe6a, 0x0000ff03, 0x0000ff05, + 0x0000ff0b, 0x0000ff0b, 0x0000ff0d, 0x0000ff0d, + 0x0000ffe0, 0x0000ffe1, 0x0000ffe5, 0x0000ffe6, + 0x00000660, 0x00000669, 0x0000066b, 0x0000066c, + 0x0000002c, 0x0000002c, 0x0000002e, 0x0000002e, + 0x0000003a, 0x0000003a, 0x000000a0, 0x000000a0, + 0x0000060c, 0x0000060c, 0x0000fe50, 0x0000fe50, + 0x0000fe52, 0x0000fe52, 0x0000fe55, 0x0000fe55, + 0x0000ff0c, 0x0000ff0c, 0x0000ff0e, 0x0000ff0e, + 0x0000ff1a, 0x0000ff1a, 0x0000000a, 0x0000000a, + 0x0000000d, 0x0000000d, 0x0000001c, 0x0000001e, + 0x00000085, 0x00000085, 0x00002029, 0x00002029, + 0x00000009, 0x00000009, 0x0000000b, 0x0000000b, + 0x0000001f, 0x0000001f, 0x0000000c, 0x0000000c, + 0x00000020, 0x00000020, 0x00001680, 0x00001680, + 0x00002000, 0x0000200a, 0x00002028, 0x00002028, + 0x0000202f, 0x0000202f, 0x0000205f, 0x0000205f, + 0x00003000, 0x00003000, 0x00000000, 0x00000008, + 0x0000000e, 0x0000001b, 0x00000021, 0x00000022, + 0x00000026, 0x0000002a, 0x0000003b, 0x00000040, + 0x0000005b, 0x00000060, 0x0000007b, 0x00000084, + 0x00000086, 0x0000009f, 0x000000a1, 0x000000a1, + 0x000000a6, 0x000000a9, 0x000000ab, 0x000000af, + 0x000000b4, 0x000000b4, 0x000000b6, 0x000000b8, + 0x000000bb, 0x000000bf, 0x000000d7, 0x000000d7, + 0x000000f7, 0x000000f7, 0x000002b9, 0x000002ba, + 0x000002c2, 0x000002cf, 0x000002d2, 0x000002df, + 0x000002e5, 0x000002ed, 0x00000300, 0x0000034f, + 0x00000360, 0x0000036f, 0x00000374, 0x00000375, + 0x0000037e, 0x0000037e, 0x00000384, 0x00000385, + 0x00000387, 0x00000387, 0x000003f6, 0x000003f6, + 0x00000483, 0x00000486, 0x00000488, 0x00000489, + 0x0000058a, 0x0000058a, 0x00000591, 0x000005a1, + 0x000005a3, 0x000005b9, 0x000005bb, 0x000005bd, + 0x000005bf, 0x000005bf, 0x000005c1, 0x000005c2, + 0x000005c4, 0x000005c4, 0x0000064b, 0x00000655, + 0x00000670, 0x00000670, 0x000006d6, 0x000006dc, + 0x000006de, 0x000006e4, 0x000006e7, 0x000006ed, + 0x0000070f, 0x0000070f, 0x00000711, 0x00000711, + 0x00000730, 0x0000074a, 0x000007a6, 0x000007b0, + 0x00000901, 0x00000902, 0x0000093c, 0x0000093c, + 0x00000941, 0x00000948, 0x0000094d, 0x0000094d, + 0x00000951, 0x00000954, 0x00000962, 0x00000963, + 0x00000981, 0x00000981, 0x000009bc, 0x000009bc, + 0x000009c1, 0x000009c4, 0x000009cd, 0x000009cd, + 0x000009e2, 0x000009e3, 0x00000a02, 0x00000a02, + 0x00000a3c, 0x00000a3c, 0x00000a41, 0x00000a42, + 0x00000a47, 0x00000a48, 0x00000a4b, 0x00000a4d, + 0x00000a70, 0x00000a71, 0x00000a81, 0x00000a82, + 0x00000abc, 0x00000abc, 0x00000ac1, 0x00000ac5, + 0x00000ac7, 0x00000ac8, 0x00000acd, 0x00000acd, + 0x00000b01, 0x00000b01, 0x00000b3c, 0x00000b3c, + 0x00000b3f, 0x00000b3f, 0x00000b41, 0x00000b43, + 0x00000b4d, 0x00000b4d, 0x00000b56, 0x00000b56, + 0x00000b82, 0x00000b82, 0x00000bc0, 0x00000bc0, + 0x00000bcd, 0x00000bcd, 0x00000c3e, 0x00000c40, + 0x00000c46, 0x00000c48, 0x00000c4a, 0x00000c4d, + 0x00000c55, 0x00000c56, 0x00000cbf, 0x00000cbf, + 0x00000cc6, 0x00000cc6, 0x00000ccc, 0x00000ccd, + 0x00000d41, 0x00000d43, 0x00000d4d, 0x00000d4d, + 0x00000dca, 0x00000dca, 0x00000dd2, 0x00000dd4, + 0x00000dd6, 0x00000dd6, 0x00000e31, 0x00000e31, + 0x00000e34, 0x00000e3a, 0x00000e47, 0x00000e4e, + 0x00000eb1, 0x00000eb1, 0x00000eb4, 0x00000eb9, + 0x00000ebb, 0x00000ebc, 0x00000ec8, 0x00000ecd, + 0x00000f18, 0x00000f19, 0x00000f35, 0x00000f35, + 0x00000f37, 0x00000f37, 0x00000f39, 0x00000f3d, + 0x00000f71, 0x00000f7e, 0x00000f80, 0x00000f84, + 0x00000f86, 0x00000f87, 0x00000f90, 0x00000f97, + 0x00000f99, 0x00000fbc, 0x00000fc6, 0x00000fc6, + 0x0000102d, 0x00001030, 0x00001032, 0x00001032, + 0x00001036, 0x00001037, 0x00001039, 0x00001039, + 0x00001058, 0x00001059, 0x0000169b, 0x0000169c, + 0x00001712, 0x00001714, 0x00001732, 0x00001734, + 0x00001752, 0x00001753, 0x00001772, 0x00001773, + 0x000017b7, 0x000017bd, 0x000017c6, 0x000017c6, + 0x000017c9, 0x000017d3, 0x00001800, 0x0000180e, + 0x000018a9, 0x000018a9, 0x00001fbd, 0x00001fbd, + 0x00001fbf, 0x00001fc1, 0x00001fcd, 0x00001fcf, + 0x00001fdd, 0x00001fdf, 0x00001fed, 0x00001fef, + 0x00001ffd, 0x00001ffe, 0x0000200b, 0x0000200d, + 0x00002010, 0x00002027, 0x0000202a, 0x0000202e, + 0x00002035, 0x00002052, 0x00002057, 0x00002057, + 0x00002060, 0x00002063, 0x0000206a, 0x0000206f, + 0x0000207c, 0x0000207e, 0x0000208c, 0x0000208e, + 0x000020d0, 0x000020ea, 0x00002100, 0x00002101, + 0x00002103, 0x00002106, 0x00002108, 0x00002109, + 0x00002114, 0x00002114, 0x00002116, 0x00002118, + 0x0000211e, 0x00002123, 0x00002125, 0x00002125, + 0x00002127, 0x00002127, 0x00002129, 0x00002129, + 0x00002132, 0x00002132, 0x0000213a, 0x0000213a, + 0x00002140, 0x00002144, 0x0000214a, 0x0000214b, + 0x00002153, 0x0000215f, 0x00002190, 0x00002211, + 0x00002214, 0x00002335, 0x0000237b, 0x00002394, + 0x00002396, 0x000023ce, 0x00002400, 0x00002426, + 0x00002440, 0x0000244a, 0x000024eb, 0x000024fe, + 0x00002500, 0x00002613, 0x00002616, 0x00002617, + 0x00002619, 0x0000267d, 0x00002680, 0x00002689, + 0x00002701, 0x00002704, 0x00002706, 0x00002709, + 0x0000270c, 0x00002727, 0x00002729, 0x0000274b, + 0x0000274d, 0x0000274d, 0x0000274f, 0x00002752, + 0x00002756, 0x00002756, 0x00002758, 0x0000275e, + 0x00002761, 0x00002794, 0x00002798, 0x000027af, + 0x000027b1, 0x000027be, 0x000027d0, 0x000027eb, + 0x000027f0, 0x00002aff, 0x00002e80, 0x00002e99, + 0x00002e9b, 0x00002ef3, 0x00002f00, 0x00002fd5, + 0x00002ff0, 0x00002ffb, 0x00003001, 0x00003004, + 0x00003008, 0x00003020, 0x0000302a, 0x00003030, + 0x00003036, 0x00003037, 0x0000303d, 0x0000303f, + 0x00003099, 0x0000309c, 0x000030a0, 0x000030a0, + 0x000030fb, 0x000030fb, 0x00003251, 0x0000325f, + 0x000032b1, 0x000032bf, 0x0000a490, 0x0000a4c6, + 0x0000fb1e, 0x0000fb1e, 0x0000fd3e, 0x0000fd3f, + 0x0000fe00, 0x0000fe0f, 0x0000fe20, 0x0000fe23, + 0x0000fe30, 0x0000fe46, 0x0000fe49, 0x0000fe4f, + 0x0000fe51, 0x0000fe51, 0x0000fe54, 0x0000fe54, + 0x0000fe56, 0x0000fe5e, 0x0000fe60, 0x0000fe61, + 0x0000fe64, 0x0000fe66, 0x0000fe68, 0x0000fe68, + 0x0000fe6b, 0x0000fe6b, 0x0000feff, 0x0000feff, + 0x0000ff01, 0x0000ff02, 0x0000ff06, 0x0000ff0a, + 0x0000ff1b, 0x0000ff20, 0x0000ff3b, 0x0000ff40, + 0x0000ff5b, 0x0000ff65, 0x0000ffe2, 0x0000ffe4, + 0x0000ffe8, 0x0000ffee, 0x0000fff9, 0x0000fffd, + 0x0001d167, 0x0001d169, 0x0001d173, 0x0001d182, + 0x0001d185, 0x0001d18b, 0x0001d1aa, 0x0001d1ad, + 0x000e0001, 0x000e0001, 0x000e0020, 0x000e007f, + 0x000000c0, 0x000000c5, 0x000000c7, 0x000000cf, + 0x000000d1, 0x000000d6, 0x000000d9, 0x000000dd, + 0x000000e0, 0x000000e5, 0x000000e7, 0x000000ef, + 0x000000f1, 0x000000f6, 0x000000f9, 0x000000fd, + 0x000000ff, 0x0000010f, 0x00000112, 0x00000125, + 0x00000128, 0x00000130, 0x00000134, 0x00000137, + 0x00000139, 0x0000013e, 0x00000143, 0x00000148, + 0x0000014c, 0x00000151, 0x00000154, 0x00000165, + 0x00000168, 0x0000017e, 0x000001a0, 0x000001a1, + 0x000001af, 0x000001b0, 0x000001cd, 0x000001dc, + 0x000001de, 0x000001e3, 0x000001e6, 0x000001f0, + 0x000001f4, 0x000001f5, 0x000001f8, 0x0000021b, + 0x0000021e, 0x0000021f, 0x00000226, 0x00000233, + 0x00000340, 0x00000341, 0x00000343, 0x00000344, + 0x00000374, 0x00000374, 0x0000037e, 0x0000037e, + 0x00000385, 0x0000038a, 0x0000038c, 0x0000038c, + 0x0000038e, 0x00000390, 0x000003aa, 0x000003b0, + 0x000003ca, 0x000003ce, 0x000003d3, 0x000003d4, + 0x00000400, 0x00000401, 0x00000403, 0x00000403, + 0x00000407, 0x00000407, 0x0000040c, 0x0000040e, + 0x00000419, 0x00000419, 0x00000439, 0x00000439, + 0x00000450, 0x00000451, 0x00000453, 0x00000453, + 0x00000457, 0x00000457, 0x0000045c, 0x0000045e, + 0x00000476, 0x00000477, 0x000004c1, 0x000004c2, + 0x000004d0, 0x000004d3, 0x000004d6, 0x000004d7, + 0x000004da, 0x000004df, 0x000004e2, 0x000004e7, + 0x000004ea, 0x000004f5, 0x000004f8, 0x000004f9, + 0x00000622, 0x00000626, 0x000006c0, 0x000006c0, + 0x000006c2, 0x000006c2, 0x000006d3, 0x000006d3, + 0x00000929, 0x00000929, 0x00000931, 0x00000931, + 0x00000934, 0x00000934, 0x00000958, 0x0000095f, + 0x000009cb, 0x000009cc, 0x000009dc, 0x000009dd, + 0x000009df, 0x000009df, 0x00000a33, 0x00000a33, + 0x00000a36, 0x00000a36, 0x00000a59, 0x00000a5b, + 0x00000a5e, 0x00000a5e, 0x00000b48, 0x00000b48, + 0x00000b4b, 0x00000b4c, 0x00000b5c, 0x00000b5d, + 0x00000b94, 0x00000b94, 0x00000bca, 0x00000bcc, + 0x00000c48, 0x00000c48, 0x00000cc0, 0x00000cc0, + 0x00000cc7, 0x00000cc8, 0x00000cca, 0x00000ccb, + 0x00000d4a, 0x00000d4c, 0x00000dda, 0x00000dda, + 0x00000ddc, 0x00000dde, 0x00000f43, 0x00000f43, + 0x00000f4d, 0x00000f4d, 0x00000f52, 0x00000f52, + 0x00000f57, 0x00000f57, 0x00000f5c, 0x00000f5c, + 0x00000f69, 0x00000f69, 0x00000f73, 0x00000f73, + 0x00000f75, 0x00000f76, 0x00000f78, 0x00000f78, + 0x00000f81, 0x00000f81, 0x00000f93, 0x00000f93, + 0x00000f9d, 0x00000f9d, 0x00000fa2, 0x00000fa2, + 0x00000fa7, 0x00000fa7, 0x00000fac, 0x00000fac, + 0x00000fb9, 0x00000fb9, 0x00001026, 0x00001026, + 0x00001e00, 0x00001e99, 0x00001e9b, 0x00001e9b, + 0x00001ea0, 0x00001ef9, 0x00001f00, 0x00001f15, + 0x00001f18, 0x00001f1d, 0x00001f20, 0x00001f45, + 0x00001f48, 0x00001f4d, 0x00001f50, 0x00001f57, + 0x00001f59, 0x00001f59, 0x00001f5b, 0x00001f5b, + 0x00001f5d, 0x00001f5d, 0x00001f5f, 0x00001f7d, + 0x00001f80, 0x00001fb4, 0x00001fb6, 0x00001fbc, + 0x00001fbe, 0x00001fbe, 0x00001fc1, 0x00001fc4, + 0x00001fc6, 0x00001fd3, 0x00001fd6, 0x00001fdb, + 0x00001fdd, 0x00001fef, 0x00001ff2, 0x00001ff4, + 0x00001ff6, 0x00001ffd, 0x00002000, 0x00002001, + 0x00002126, 0x00002126, 0x0000212a, 0x0000212b, + 0x0000219a, 0x0000219b, 0x000021ae, 0x000021ae, + 0x000021cd, 0x000021cf, 0x00002204, 0x00002204, + 0x00002209, 0x00002209, 0x0000220c, 0x0000220c, + 0x00002224, 0x00002224, 0x00002226, 0x00002226, + 0x00002241, 0x00002241, 0x00002244, 0x00002244, + 0x00002247, 0x00002247, 0x00002249, 0x00002249, + 0x00002260, 0x00002260, 0x00002262, 0x00002262, + 0x0000226d, 0x00002271, 0x00002274, 0x00002275, + 0x00002278, 0x00002279, 0x00002280, 0x00002281, + 0x00002284, 0x00002285, 0x00002288, 0x00002289, + 0x000022ac, 0x000022af, 0x000022e0, 0x000022e3, + 0x000022ea, 0x000022ed, 0x00002329, 0x0000232a, + 0x00002adc, 0x00002adc, 0x0000304c, 0x0000304c, + 0x0000304e, 0x0000304e, 0x00003050, 0x00003050, + 0x00003052, 0x00003052, 0x00003054, 0x00003054, + 0x00003056, 0x00003056, 0x00003058, 0x00003058, + 0x0000305a, 0x0000305a, 0x0000305c, 0x0000305c, + 0x0000305e, 0x0000305e, 0x00003060, 0x00003060, + 0x00003062, 0x00003062, 0x00003065, 0x00003065, + 0x00003067, 0x00003067, 0x00003069, 0x00003069, + 0x00003070, 0x00003071, 0x00003073, 0x00003074, + 0x00003076, 0x00003077, 0x00003079, 0x0000307a, + 0x0000307c, 0x0000307d, 0x00003094, 0x00003094, + 0x0000309e, 0x0000309e, 0x000030ac, 0x000030ac, + 0x000030ae, 0x000030ae, 0x000030b0, 0x000030b0, + 0x000030b2, 0x000030b2, 0x000030b4, 0x000030b4, + 0x000030b6, 0x000030b6, 0x000030b8, 0x000030b8, + 0x000030ba, 0x000030ba, 0x000030bc, 0x000030bc, + 0x000030be, 0x000030be, 0x000030c0, 0x000030c0, + 0x000030c2, 0x000030c2, 0x000030c5, 0x000030c5, + 0x000030c7, 0x000030c7, 0x000030c9, 0x000030c9, + 0x000030d0, 0x000030d1, 0x000030d3, 0x000030d4, + 0x000030d6, 0x000030d7, 0x000030d9, 0x000030da, + 0x000030dc, 0x000030dd, 0x000030f4, 0x000030f4, + 0x000030f7, 0x000030fa, 0x000030fe, 0x000030fe, + 0x0000f902, 0x0000fa0d, 0x0000fa10, 0x0000fa10, + 0x0000fa12, 0x0000fa12, 0x0000fa15, 0x0000fa1e, + 0x0000fa20, 0x0000fa20, 0x0000fa22, 0x0000fa22, + 0x0000fa25, 0x0000fa26, 0x0000fa2a, 0x0000fa2d, + 0x0000fa30, 0x0000fa6a, 0x0000fb1d, 0x0000fb1d, + 0x0000fb1f, 0x0000fb1f, 0x0000fb2a, 0x0000fb36, + 0x0000fb38, 0x0000fb3c, 0x0000fb3e, 0x0000fb3e, + 0x0000fb40, 0x0000fb41, 0x0000fb43, 0x0000fb44, + 0x0000fb46, 0x0000fb4e, 0x0001d15e, 0x0001d164, + 0x0001d1bb, 0x0001d1c0, 0x0002f800, 0x0002fa1d, + 0x00000000, 0x00000220, 0x00000222, 0x00000233, + 0x00000250, 0x000002ad, 0x000002b0, 0x000002ee, + 0x00000300, 0x0000034f, 0x00000360, 0x0000036f, + 0x00000374, 0x00000375, 0x0000037a, 0x0000037a, + 0x0000037e, 0x0000037e, 0x00000384, 0x0000038a, + 0x0000038c, 0x0000038c, 0x0000038e, 0x000003a1, + 0x000003a3, 0x000003ce, 0x000003d0, 0x000003f6, + 0x00000400, 0x00000486, 0x00000488, 0x000004ce, + 0x000004d0, 0x000004f5, 0x000004f8, 0x000004f9, + 0x00000500, 0x0000050f, 0x00000531, 0x00000556, + 0x00000559, 0x0000055f, 0x00000561, 0x00000587, + 0x00000589, 0x0000058a, 0x00000591, 0x000005a1, + 0x000005a3, 0x000005b9, 0x000005bb, 0x000005c4, + 0x000005d0, 0x000005ea, 0x000005f0, 0x000005f4, + 0x0000060c, 0x0000060c, 0x0000061b, 0x0000061b, + 0x0000061f, 0x0000061f, 0x00000621, 0x0000063a, + 0x00000640, 0x00000655, 0x00000660, 0x000006ed, + 0x000006f0, 0x000006fe, 0x00000700, 0x0000070d, + 0x0000070f, 0x0000072c, 0x00000730, 0x0000074a, + 0x00000780, 0x000007b1, 0x00000901, 0x00000903, + 0x00000905, 0x00000939, 0x0000093c, 0x0000094d, + 0x00000950, 0x00000954, 0x00000958, 0x00000970, + 0x00000981, 0x00000983, 0x00000985, 0x0000098c, + 0x0000098f, 0x00000990, 0x00000993, 0x000009a8, + 0x000009aa, 0x000009b0, 0x000009b2, 0x000009b2, + 0x000009b6, 0x000009b9, 0x000009bc, 0x000009bc, + 0x000009be, 0x000009c4, 0x000009c7, 0x000009c8, + 0x000009cb, 0x000009cd, 0x000009d7, 0x000009d7, + 0x000009dc, 0x000009dd, 0x000009df, 0x000009e3, + 0x000009e6, 0x000009fa, 0x00000a02, 0x00000a02, + 0x00000a05, 0x00000a0a, 0x00000a0f, 0x00000a10, + 0x00000a13, 0x00000a28, 0x00000a2a, 0x00000a30, + 0x00000a32, 0x00000a33, 0x00000a35, 0x00000a36, + 0x00000a38, 0x00000a39, 0x00000a3c, 0x00000a3c, + 0x00000a3e, 0x00000a42, 0x00000a47, 0x00000a48, + 0x00000a4b, 0x00000a4d, 0x00000a59, 0x00000a5c, + 0x00000a5e, 0x00000a5e, 0x00000a66, 0x00000a74, + 0x00000a81, 0x00000a83, 0x00000a85, 0x00000a8b, + 0x00000a8d, 0x00000a8d, 0x00000a8f, 0x00000a91, + 0x00000a93, 0x00000aa8, 0x00000aaa, 0x00000ab0, + 0x00000ab2, 0x00000ab3, 0x00000ab5, 0x00000ab9, + 0x00000abc, 0x00000ac5, 0x00000ac7, 0x00000ac9, + 0x00000acb, 0x00000acd, 0x00000ad0, 0x00000ad0, + 0x00000ae0, 0x00000ae0, 0x00000ae6, 0x00000aef, + 0x00000b01, 0x00000b03, 0x00000b05, 0x00000b0c, + 0x00000b0f, 0x00000b10, 0x00000b13, 0x00000b28, + 0x00000b2a, 0x00000b30, 0x00000b32, 0x00000b33, + 0x00000b36, 0x00000b39, 0x00000b3c, 0x00000b43, + 0x00000b47, 0x00000b48, 0x00000b4b, 0x00000b4d, + 0x00000b56, 0x00000b57, 0x00000b5c, 0x00000b5d, + 0x00000b5f, 0x00000b61, 0x00000b66, 0x00000b70, + 0x00000b82, 0x00000b83, 0x00000b85, 0x00000b8a, + 0x00000b8e, 0x00000b90, 0x00000b92, 0x00000b95, + 0x00000b99, 0x00000b9a, 0x00000b9c, 0x00000b9c, + 0x00000b9e, 0x00000b9f, 0x00000ba3, 0x00000ba4, + 0x00000ba8, 0x00000baa, 0x00000bae, 0x00000bb5, + 0x00000bb7, 0x00000bb9, 0x00000bbe, 0x00000bc2, + 0x00000bc6, 0x00000bc8, 0x00000bca, 0x00000bcd, + 0x00000bd7, 0x00000bd7, 0x00000be7, 0x00000bf2, + 0x00000c01, 0x00000c03, 0x00000c05, 0x00000c0c, + 0x00000c0e, 0x00000c10, 0x00000c12, 0x00000c28, + 0x00000c2a, 0x00000c33, 0x00000c35, 0x00000c39, + 0x00000c3e, 0x00000c44, 0x00000c46, 0x00000c48, + 0x00000c4a, 0x00000c4d, 0x00000c55, 0x00000c56, + 0x00000c60, 0x00000c61, 0x00000c66, 0x00000c6f, + 0x00000c82, 0x00000c83, 0x00000c85, 0x00000c8c, + 0x00000c8e, 0x00000c90, 0x00000c92, 0x00000ca8, + 0x00000caa, 0x00000cb3, 0x00000cb5, 0x00000cb9, + 0x00000cbe, 0x00000cc4, 0x00000cc6, 0x00000cc8, + 0x00000cca, 0x00000ccd, 0x00000cd5, 0x00000cd6, + 0x00000cde, 0x00000cde, 0x00000ce0, 0x00000ce1, + 0x00000ce6, 0x00000cef, 0x00000d02, 0x00000d03, + 0x00000d05, 0x00000d0c, 0x00000d0e, 0x00000d10, + 0x00000d12, 0x00000d28, 0x00000d2a, 0x00000d39, + 0x00000d3e, 0x00000d43, 0x00000d46, 0x00000d48, + 0x00000d4a, 0x00000d4d, 0x00000d57, 0x00000d57, + 0x00000d60, 0x00000d61, 0x00000d66, 0x00000d6f, + 0x00000d82, 0x00000d83, 0x00000d85, 0x00000d96, + 0x00000d9a, 0x00000db1, 0x00000db3, 0x00000dbb, + 0x00000dbd, 0x00000dbd, 0x00000dc0, 0x00000dc6, + 0x00000dca, 0x00000dca, 0x00000dcf, 0x00000dd4, + 0x00000dd6, 0x00000dd6, 0x00000dd8, 0x00000ddf, + 0x00000df2, 0x00000df4, 0x00000e01, 0x00000e3a, + 0x00000e3f, 0x00000e5b, 0x00000e81, 0x00000e82, + 0x00000e84, 0x00000e84, 0x00000e87, 0x00000e88, + 0x00000e8a, 0x00000e8a, 0x00000e8d, 0x00000e8d, + 0x00000e94, 0x00000e97, 0x00000e99, 0x00000e9f, + 0x00000ea1, 0x00000ea3, 0x00000ea5, 0x00000ea5, + 0x00000ea7, 0x00000ea7, 0x00000eaa, 0x00000eab, + 0x00000ead, 0x00000eb9, 0x00000ebb, 0x00000ebd, + 0x00000ec0, 0x00000ec4, 0x00000ec6, 0x00000ec6, + 0x00000ec8, 0x00000ecd, 0x00000ed0, 0x00000ed9, + 0x00000edc, 0x00000edd, 0x00000f00, 0x00000f47, + 0x00000f49, 0x00000f6a, 0x00000f71, 0x00000f8b, + 0x00000f90, 0x00000f97, 0x00000f99, 0x00000fbc, + 0x00000fbe, 0x00000fcc, 0x00000fcf, 0x00000fcf, + 0x00001000, 0x00001021, 0x00001023, 0x00001027, + 0x00001029, 0x0000102a, 0x0000102c, 0x00001032, + 0x00001036, 0x00001039, 0x00001040, 0x00001059, + 0x000010a0, 0x000010c5, 0x000010d0, 0x000010f8, + 0x000010fb, 0x000010fb, 0x00001100, 0x00001159, + 0x0000115f, 0x000011a2, 0x000011a8, 0x000011f9, + 0x00001200, 0x00001206, 0x00001208, 0x00001246, + 0x00001248, 0x00001248, 0x0000124a, 0x0000124d, + 0x00001250, 0x00001256, 0x00001258, 0x00001258, + 0x0000125a, 0x0000125d, 0x00001260, 0x00001286, + 0x00001288, 0x00001288, 0x0000128a, 0x0000128d, + 0x00001290, 0x000012ae, 0x000012b0, 0x000012b0, + 0x000012b2, 0x000012b5, 0x000012b8, 0x000012be, + 0x000012c0, 0x000012c0, 0x000012c2, 0x000012c5, + 0x000012c8, 0x000012ce, 0x000012d0, 0x000012d6, + 0x000012d8, 0x000012ee, 0x000012f0, 0x0000130e, + 0x00001310, 0x00001310, 0x00001312, 0x00001315, + 0x00001318, 0x0000131e, 0x00001320, 0x00001346, + 0x00001348, 0x0000135a, 0x00001361, 0x0000137c, + 0x000013a0, 0x000013f4, 0x00001401, 0x00001676, + 0x00001680, 0x0000169c, 0x000016a0, 0x000016f0, + 0x00001700, 0x0000170c, 0x0000170e, 0x00001714, + 0x00001720, 0x00001736, 0x00001740, 0x00001753, + 0x00001760, 0x0000176c, 0x0000176e, 0x00001770, + 0x00001772, 0x00001773, 0x00001780, 0x000017dc, + 0x000017e0, 0x000017e9, 0x00001800, 0x0000180e, + 0x00001810, 0x00001819, 0x00001820, 0x00001877, + 0x00001880, 0x000018a9, 0x00001e00, 0x00001e9b, + 0x00001ea0, 0x00001ef9, 0x00001f00, 0x00001f15, + 0x00001f18, 0x00001f1d, 0x00001f20, 0x00001f45, + 0x00001f48, 0x00001f4d, 0x00001f50, 0x00001f57, + 0x00001f59, 0x00001f59, 0x00001f5b, 0x00001f5b, + 0x00001f5d, 0x00001f5d, 0x00001f5f, 0x00001f7d, + 0x00001f80, 0x00001fb4, 0x00001fb6, 0x00001fc4, + 0x00001fc6, 0x00001fd3, 0x00001fd6, 0x00001fdb, + 0x00001fdd, 0x00001fef, 0x00001ff2, 0x00001ff4, + 0x00001ff6, 0x00001ffe, 0x00002000, 0x00002052, + 0x00002057, 0x00002057, 0x0000205f, 0x00002063, + 0x0000206a, 0x00002071, 0x00002074, 0x0000208e, + 0x000020a0, 0x000020b1, 0x000020d0, 0x000020ea, + 0x00002100, 0x0000213a, 0x0000213d, 0x0000214b, + 0x00002153, 0x00002183, 0x00002190, 0x000023ce, + 0x00002400, 0x00002426, 0x00002440, 0x0000244a, + 0x00002460, 0x000024fe, 0x00002500, 0x00002613, + 0x00002616, 0x00002617, 0x00002619, 0x0000267d, + 0x00002680, 0x00002689, 0x00002701, 0x00002704, + 0x00002706, 0x00002709, 0x0000270c, 0x00002727, + 0x00002729, 0x0000274b, 0x0000274d, 0x0000274d, + 0x0000274f, 0x00002752, 0x00002756, 0x00002756, + 0x00002758, 0x0000275e, 0x00002761, 0x00002794, + 0x00002798, 0x000027af, 0x000027b1, 0x000027be, + 0x000027d0, 0x000027eb, 0x000027f0, 0x00002aff, + 0x00002e80, 0x00002e99, 0x00002e9b, 0x00002ef3, + 0x00002f00, 0x00002fd5, 0x00002ff0, 0x00002ffb, + 0x00003000, 0x0000303f, 0x00003041, 0x00003096, + 0x00003099, 0x000030ff, 0x00003105, 0x0000312c, + 0x00003131, 0x0000318e, 0x00003190, 0x000031b7, + 0x000031f0, 0x0000321c, 0x00003220, 0x00003243, + 0x00003251, 0x0000327b, 0x0000327f, 0x000032cb, + 0x000032d0, 0x000032fe, 0x00003300, 0x00003376, + 0x0000337b, 0x000033dd, 0x000033e0, 0x000033fe, + 0x00003400, 0x00004db5, 0x00004e00, 0x00009fa5, + 0x0000a000, 0x0000a48c, 0x0000a490, 0x0000a4c6, + 0x0000ac00, 0x0000d7a3, 0x0000f900, 0x0000fb06, + 0x0000fb13, 0x0000fb17, 0x0000fb1d, 0x0000fb36, + 0x0000fb38, 0x0000fb3c, 0x0000fb3e, 0x0000fb3e, + 0x0000fb40, 0x0000fb41, 0x0000fb43, 0x0000fb44, + 0x0000fb46, 0x0000fbb1, 0x0000fbd3, 0x0000fd3f, + 0x0000fd50, 0x0000fd8f, 0x0000fd92, 0x0000fdc7, + 0x0000fdf0, 0x0000fdfc, 0x0000fe00, 0x0000fe0f, + 0x0000fe20, 0x0000fe23, 0x0000fe30, 0x0000fe46, + 0x0000fe49, 0x0000fe52, 0x0000fe54, 0x0000fe66, + 0x0000fe68, 0x0000fe6b, 0x0000fe70, 0x0000fe74, + 0x0000fe76, 0x0000fefc, 0x0000feff, 0x0000feff, + 0x0000ff01, 0x0000ffbe, 0x0000ffc2, 0x0000ffc7, + 0x0000ffca, 0x0000ffcf, 0x0000ffd2, 0x0000ffd7, + 0x0000ffda, 0x0000ffdc, 0x0000ffe0, 0x0000ffe6, + 0x0000ffe8, 0x0000ffee, 0x0000fff9, 0x0000fffd, + 0x00010300, 0x0001031e, 0x00010320, 0x00010323, + 0x00010330, 0x0001034a, 0x00010400, 0x00010425, + 0x00010428, 0x0001044d, 0x0001d000, 0x0001d0f5, + 0x0001d100, 0x0001d126, 0x0001d12a, 0x0001d1dd, + 0x0001d400, 0x0001d454, 0x0001d456, 0x0001d49c, + 0x0001d49e, 0x0001d49f, 0x0001d4a2, 0x0001d4a2, + 0x0001d4a5, 0x0001d4a6, 0x0001d4a9, 0x0001d4ac, + 0x0001d4ae, 0x0001d4b9, 0x0001d4bb, 0x0001d4bb, + 0x0001d4bd, 0x0001d4c0, 0x0001d4c2, 0x0001d4c3, + 0x0001d4c5, 0x0001d505, 0x0001d507, 0x0001d50a, + 0x0001d50d, 0x0001d514, 0x0001d516, 0x0001d51c, + 0x0001d51e, 0x0001d539, 0x0001d53b, 0x0001d53e, + 0x0001d540, 0x0001d544, 0x0001d546, 0x0001d546, + 0x0001d54a, 0x0001d550, 0x0001d552, 0x0001d6a3, + 0x0001d6a8, 0x0001d7c9, 0x0001d7ce, 0x0001d7ff, + 0x00020000, 0x0002a6d6, 0x0002f800, 0x0002fa1d, + 0x000e0001, 0x000e0001, 0x000e0020, 0x000e007f, + 0x000000ab, 0x000000ab, 0x00002018, 0x00002018, + 0x0000201b, 0x0000201c, 0x0000201f, 0x0000201f, + 0x00002039, 0x00002039, 0x000000bb, 0x000000bb, + 0x00002019, 0x00002019, 0x0000201d, 0x0000201d, + 0x0000203a, 0x0000203a, 0x0000061b, 0x0000061b, + 0x0000061f, 0x0000061f, 0x00000621, 0x0000063a, + 0x00000640, 0x0000064a, 0x0000066d, 0x0000066f, + 0x00000671, 0x000006d5, 0x000006dd, 0x000006dd, + 0x000006e5, 0x000006e6, 0x000006fa, 0x000006fe, + 0x00000700, 0x0000070d, 0x00000710, 0x00000710, + 0x00000712, 0x0000072c, 0x00000780, 0x000007a5, + 0x000007b1, 0x000007b1, 0x0000fb50, 0x0000fbb1, + 0x0000fbd3, 0x0000fd3d, 0x0000fd50, 0x0000fd8f, + 0x0000fd92, 0x0000fdc7, 0x0000fdf0, 0x0000fdfc, + 0x0000fe70, 0x0000fe74, 0x0000fe76, 0x0000fefc +}; + +static const ac_uint4 _uccase_size = 1504; + +static const ac_uint2 _uccase_len[2] = {718, 755}; + +static const ac_uint4 _uccase_map[] = { + 0x00000041, 0x00000061, 0x00000041, + 0x00000042, 0x00000062, 0x00000042, + 0x00000043, 0x00000063, 0x00000043, + 0x00000044, 0x00000064, 0x00000044, + 0x00000045, 0x00000065, 0x00000045, + 0x00000046, 0x00000066, 0x00000046, + 0x00000047, 0x00000067, 0x00000047, + 0x00000048, 0x00000068, 0x00000048, + 0x00000049, 0x00000069, 0x00000049, + 0x0000004a, 0x0000006a, 0x0000004a, + 0x0000004b, 0x0000006b, 0x0000004b, + 0x0000004c, 0x0000006c, 0x0000004c, + 0x0000004d, 0x0000006d, 0x0000004d, + 0x0000004e, 0x0000006e, 0x0000004e, + 0x0000004f, 0x0000006f, 0x0000004f, + 0x00000050, 0x00000070, 0x00000050, + 0x00000051, 0x00000071, 0x00000051, + 0x00000052, 0x00000072, 0x00000052, + 0x00000053, 0x00000073, 0x00000053, + 0x00000054, 0x00000074, 0x00000054, + 0x00000055, 0x00000075, 0x00000055, + 0x00000056, 0x00000076, 0x00000056, + 0x00000057, 0x00000077, 0x00000057, + 0x00000058, 0x00000078, 0x00000058, + 0x00000059, 0x00000079, 0x00000059, + 0x0000005a, 0x0000007a, 0x0000005a, + 0x000000c0, 0x000000e0, 0x000000c0, + 0x000000c1, 0x000000e1, 0x000000c1, + 0x000000c2, 0x000000e2, 0x000000c2, + 0x000000c3, 0x000000e3, 0x000000c3, + 0x000000c4, 0x000000e4, 0x000000c4, + 0x000000c5, 0x000000e5, 0x000000c5, + 0x000000c6, 0x000000e6, 0x000000c6, + 0x000000c7, 0x000000e7, 0x000000c7, + 0x000000c8, 0x000000e8, 0x000000c8, + 0x000000c9, 0x000000e9, 0x000000c9, + 0x000000ca, 0x000000ea, 0x000000ca, + 0x000000cb, 0x000000eb, 0x000000cb, + 0x000000cc, 0x000000ec, 0x000000cc, + 0x000000cd, 0x000000ed, 0x000000cd, + 0x000000ce, 0x000000ee, 0x000000ce, + 0x000000cf, 0x000000ef, 0x000000cf, + 0x000000d0, 0x000000f0, 0x000000d0, + 0x000000d1, 0x000000f1, 0x000000d1, + 0x000000d2, 0x000000f2, 0x000000d2, + 0x000000d3, 0x000000f3, 0x000000d3, + 0x000000d4, 0x000000f4, 0x000000d4, + 0x000000d5, 0x000000f5, 0x000000d5, + 0x000000d6, 0x000000f6, 0x000000d6, + 0x000000d8, 0x000000f8, 0x000000d8, + 0x000000d9, 0x000000f9, 0x000000d9, + 0x000000da, 0x000000fa, 0x000000da, + 0x000000db, 0x000000fb, 0x000000db, + 0x000000dc, 0x000000fc, 0x000000dc, + 0x000000dd, 0x000000fd, 0x000000dd, + 0x000000de, 0x000000fe, 0x000000de, + 0x00000100, 0x00000101, 0x00000100, + 0x00000102, 0x00000103, 0x00000102, + 0x00000104, 0x00000105, 0x00000104, + 0x00000106, 0x00000107, 0x00000106, + 0x00000108, 0x00000109, 0x00000108, + 0x0000010a, 0x0000010b, 0x0000010a, + 0x0000010c, 0x0000010d, 0x0000010c, + 0x0000010e, 0x0000010f, 0x0000010e, + 0x00000110, 0x00000111, 0x00000110, + 0x00000112, 0x00000113, 0x00000112, + 0x00000114, 0x00000115, 0x00000114, + 0x00000116, 0x00000117, 0x00000116, + 0x00000118, 0x00000119, 0x00000118, + 0x0000011a, 0x0000011b, 0x0000011a, + 0x0000011c, 0x0000011d, 0x0000011c, + 0x0000011e, 0x0000011f, 0x0000011e, + 0x00000120, 0x00000121, 0x00000120, + 0x00000122, 0x00000123, 0x00000122, + 0x00000124, 0x00000125, 0x00000124, + 0x00000126, 0x00000127, 0x00000126, + 0x00000128, 0x00000129, 0x00000128, + 0x0000012a, 0x0000012b, 0x0000012a, + 0x0000012c, 0x0000012d, 0x0000012c, + 0x0000012e, 0x0000012f, 0x0000012e, + 0x00000130, 0x00000069, 0x00000130, + 0x00000132, 0x00000133, 0x00000132, + 0x00000134, 0x00000135, 0x00000134, + 0x00000136, 0x00000137, 0x00000136, + 0x00000139, 0x0000013a, 0x00000139, + 0x0000013b, 0x0000013c, 0x0000013b, + 0x0000013d, 0x0000013e, 0x0000013d, + 0x0000013f, 0x00000140, 0x0000013f, + 0x00000141, 0x00000142, 0x00000141, + 0x00000143, 0x00000144, 0x00000143, + 0x00000145, 0x00000146, 0x00000145, + 0x00000147, 0x00000148, 0x00000147, + 0x0000014a, 0x0000014b, 0x0000014a, + 0x0000014c, 0x0000014d, 0x0000014c, + 0x0000014e, 0x0000014f, 0x0000014e, + 0x00000150, 0x00000151, 0x00000150, + 0x00000152, 0x00000153, 0x00000152, + 0x00000154, 0x00000155, 0x00000154, + 0x00000156, 0x00000157, 0x00000156, + 0x00000158, 0x00000159, 0x00000158, + 0x0000015a, 0x0000015b, 0x0000015a, + 0x0000015c, 0x0000015d, 0x0000015c, + 0x0000015e, 0x0000015f, 0x0000015e, + 0x00000160, 0x00000161, 0x00000160, + 0x00000162, 0x00000163, 0x00000162, + 0x00000164, 0x00000165, 0x00000164, + 0x00000166, 0x00000167, 0x00000166, + 0x00000168, 0x00000169, 0x00000168, + 0x0000016a, 0x0000016b, 0x0000016a, + 0x0000016c, 0x0000016d, 0x0000016c, + 0x0000016e, 0x0000016f, 0x0000016e, + 0x00000170, 0x00000171, 0x00000170, + 0x00000172, 0x00000173, 0x00000172, + 0x00000174, 0x00000175, 0x00000174, + 0x00000176, 0x00000177, 0x00000176, + 0x00000178, 0x000000ff, 0x00000178, + 0x00000179, 0x0000017a, 0x00000179, + 0x0000017b, 0x0000017c, 0x0000017b, + 0x0000017d, 0x0000017e, 0x0000017d, + 0x00000181, 0x00000253, 0x00000181, + 0x00000182, 0x00000183, 0x00000182, + 0x00000184, 0x00000185, 0x00000184, + 0x00000186, 0x00000254, 0x00000186, + 0x00000187, 0x00000188, 0x00000187, + 0x00000189, 0x00000256, 0x00000189, + 0x0000018a, 0x00000257, 0x0000018a, + 0x0000018b, 0x0000018c, 0x0000018b, + 0x0000018e, 0x000001dd, 0x0000018e, + 0x0000018f, 0x00000259, 0x0000018f, + 0x00000190, 0x0000025b, 0x00000190, + 0x00000191, 0x00000192, 0x00000191, + 0x00000193, 0x00000260, 0x00000193, + 0x00000194, 0x00000263, 0x00000194, + 0x00000196, 0x00000269, 0x00000196, + 0x00000197, 0x00000268, 0x00000197, + 0x00000198, 0x00000199, 0x00000198, + 0x0000019c, 0x0000026f, 0x0000019c, + 0x0000019d, 0x00000272, 0x0000019d, + 0x0000019f, 0x00000275, 0x0000019f, + 0x000001a0, 0x000001a1, 0x000001a0, + 0x000001a2, 0x000001a3, 0x000001a2, + 0x000001a4, 0x000001a5, 0x000001a4, + 0x000001a6, 0x00000280, 0x000001a6, + 0x000001a7, 0x000001a8, 0x000001a7, + 0x000001a9, 0x00000283, 0x000001a9, + 0x000001ac, 0x000001ad, 0x000001ac, + 0x000001ae, 0x00000288, 0x000001ae, + 0x000001af, 0x000001b0, 0x000001af, + 0x000001b1, 0x0000028a, 0x000001b1, + 0x000001b2, 0x0000028b, 0x000001b2, + 0x000001b3, 0x000001b4, 0x000001b3, + 0x000001b5, 0x000001b6, 0x000001b5, + 0x000001b7, 0x00000292, 0x000001b7, + 0x000001b8, 0x000001b9, 0x000001b8, + 0x000001bc, 0x000001bd, 0x000001bc, + 0x000001c4, 0x000001c6, 0x000001c5, + 0x000001c7, 0x000001c9, 0x000001c8, + 0x000001ca, 0x000001cc, 0x000001cb, + 0x000001cd, 0x000001ce, 0x000001cd, + 0x000001cf, 0x000001d0, 0x000001cf, + 0x000001d1, 0x000001d2, 0x000001d1, + 0x000001d3, 0x000001d4, 0x000001d3, + 0x000001d5, 0x000001d6, 0x000001d5, + 0x000001d7, 0x000001d8, 0x000001d7, + 0x000001d9, 0x000001da, 0x000001d9, + 0x000001db, 0x000001dc, 0x000001db, + 0x000001de, 0x000001df, 0x000001de, + 0x000001e0, 0x000001e1, 0x000001e0, + 0x000001e2, 0x000001e3, 0x000001e2, + 0x000001e4, 0x000001e5, 0x000001e4, + 0x000001e6, 0x000001e7, 0x000001e6, + 0x000001e8, 0x000001e9, 0x000001e8, + 0x000001ea, 0x000001eb, 0x000001ea, + 0x000001ec, 0x000001ed, 0x000001ec, + 0x000001ee, 0x000001ef, 0x000001ee, + 0x000001f1, 0x000001f3, 0x000001f2, + 0x000001f4, 0x000001f5, 0x000001f4, + 0x000001f6, 0x00000195, 0x000001f6, + 0x000001f7, 0x000001bf, 0x000001f7, + 0x000001f8, 0x000001f9, 0x000001f8, + 0x000001fa, 0x000001fb, 0x000001fa, + 0x000001fc, 0x000001fd, 0x000001fc, + 0x000001fe, 0x000001ff, 0x000001fe, + 0x00000200, 0x00000201, 0x00000200, + 0x00000202, 0x00000203, 0x00000202, + 0x00000204, 0x00000205, 0x00000204, + 0x00000206, 0x00000207, 0x00000206, + 0x00000208, 0x00000209, 0x00000208, + 0x0000020a, 0x0000020b, 0x0000020a, + 0x0000020c, 0x0000020d, 0x0000020c, + 0x0000020e, 0x0000020f, 0x0000020e, + 0x00000210, 0x00000211, 0x00000210, + 0x00000212, 0x00000213, 0x00000212, + 0x00000214, 0x00000215, 0x00000214, + 0x00000216, 0x00000217, 0x00000216, + 0x00000218, 0x00000219, 0x00000218, + 0x0000021a, 0x0000021b, 0x0000021a, + 0x0000021c, 0x0000021d, 0x0000021c, + 0x0000021e, 0x0000021f, 0x0000021e, + 0x00000220, 0x0000019e, 0x00000220, + 0x00000222, 0x00000223, 0x00000222, + 0x00000224, 0x00000225, 0x00000224, + 0x00000226, 0x00000227, 0x00000226, + 0x00000228, 0x00000229, 0x00000228, + 0x0000022a, 0x0000022b, 0x0000022a, + 0x0000022c, 0x0000022d, 0x0000022c, + 0x0000022e, 0x0000022f, 0x0000022e, + 0x00000230, 0x00000231, 0x00000230, + 0x00000232, 0x00000233, 0x00000232, + 0x00000386, 0x000003ac, 0x00000386, + 0x00000388, 0x000003ad, 0x00000388, + 0x00000389, 0x000003ae, 0x00000389, + 0x0000038a, 0x000003af, 0x0000038a, + 0x0000038c, 0x000003cc, 0x0000038c, + 0x0000038e, 0x000003cd, 0x0000038e, + 0x0000038f, 0x000003ce, 0x0000038f, + 0x00000391, 0x000003b1, 0x00000391, + 0x00000392, 0x000003b2, 0x00000392, + 0x00000393, 0x000003b3, 0x00000393, + 0x00000394, 0x000003b4, 0x00000394, + 0x00000395, 0x000003b5, 0x00000395, + 0x00000396, 0x000003b6, 0x00000396, + 0x00000397, 0x000003b7, 0x00000397, + 0x00000398, 0x000003b8, 0x00000398, + 0x00000399, 0x000003b9, 0x00000399, + 0x0000039a, 0x000003ba, 0x0000039a, + 0x0000039b, 0x000003bb, 0x0000039b, + 0x0000039c, 0x000003bc, 0x0000039c, + 0x0000039d, 0x000003bd, 0x0000039d, + 0x0000039e, 0x000003be, 0x0000039e, + 0x0000039f, 0x000003bf, 0x0000039f, + 0x000003a0, 0x000003c0, 0x000003a0, + 0x000003a1, 0x000003c1, 0x000003a1, + 0x000003a3, 0x000003c3, 0x000003a3, + 0x000003a4, 0x000003c4, 0x000003a4, + 0x000003a5, 0x000003c5, 0x000003a5, + 0x000003a6, 0x000003c6, 0x000003a6, + 0x000003a7, 0x000003c7, 0x000003a7, + 0x000003a8, 0x000003c8, 0x000003a8, + 0x000003a9, 0x000003c9, 0x000003a9, + 0x000003aa, 0x000003ca, 0x000003aa, + 0x000003ab, 0x000003cb, 0x000003ab, + 0x000003d8, 0x000003d9, 0x000003d8, + 0x000003da, 0x000003db, 0x000003da, + 0x000003dc, 0x000003dd, 0x000003dc, + 0x000003de, 0x000003df, 0x000003de, + 0x000003e0, 0x000003e1, 0x000003e0, + 0x000003e2, 0x000003e3, 0x000003e2, + 0x000003e4, 0x000003e5, 0x000003e4, + 0x000003e6, 0x000003e7, 0x000003e6, + 0x000003e8, 0x000003e9, 0x000003e8, + 0x000003ea, 0x000003eb, 0x000003ea, + 0x000003ec, 0x000003ed, 0x000003ec, + 0x000003ee, 0x000003ef, 0x000003ee, + 0x000003f4, 0x000003b8, 0x000003f4, + 0x00000400, 0x00000450, 0x00000400, + 0x00000401, 0x00000451, 0x00000401, + 0x00000402, 0x00000452, 0x00000402, + 0x00000403, 0x00000453, 0x00000403, + 0x00000404, 0x00000454, 0x00000404, + 0x00000405, 0x00000455, 0x00000405, + 0x00000406, 0x00000456, 0x00000406, + 0x00000407, 0x00000457, 0x00000407, + 0x00000408, 0x00000458, 0x00000408, + 0x00000409, 0x00000459, 0x00000409, + 0x0000040a, 0x0000045a, 0x0000040a, + 0x0000040b, 0x0000045b, 0x0000040b, + 0x0000040c, 0x0000045c, 0x0000040c, + 0x0000040d, 0x0000045d, 0x0000040d, + 0x0000040e, 0x0000045e, 0x0000040e, + 0x0000040f, 0x0000045f, 0x0000040f, + 0x00000410, 0x00000430, 0x00000410, + 0x00000411, 0x00000431, 0x00000411, + 0x00000412, 0x00000432, 0x00000412, + 0x00000413, 0x00000433, 0x00000413, + 0x00000414, 0x00000434, 0x00000414, + 0x00000415, 0x00000435, 0x00000415, + 0x00000416, 0x00000436, 0x00000416, + 0x00000417, 0x00000437, 0x00000417, + 0x00000418, 0x00000438, 0x00000418, + 0x00000419, 0x00000439, 0x00000419, + 0x0000041a, 0x0000043a, 0x0000041a, + 0x0000041b, 0x0000043b, 0x0000041b, + 0x0000041c, 0x0000043c, 0x0000041c, + 0x0000041d, 0x0000043d, 0x0000041d, + 0x0000041e, 0x0000043e, 0x0000041e, + 0x0000041f, 0x0000043f, 0x0000041f, + 0x00000420, 0x00000440, 0x00000420, + 0x00000421, 0x00000441, 0x00000421, + 0x00000422, 0x00000442, 0x00000422, + 0x00000423, 0x00000443, 0x00000423, + 0x00000424, 0x00000444, 0x00000424, + 0x00000425, 0x00000445, 0x00000425, + 0x00000426, 0x00000446, 0x00000426, + 0x00000427, 0x00000447, 0x00000427, + 0x00000428, 0x00000448, 0x00000428, + 0x00000429, 0x00000449, 0x00000429, + 0x0000042a, 0x0000044a, 0x0000042a, + 0x0000042b, 0x0000044b, 0x0000042b, + 0x0000042c, 0x0000044c, 0x0000042c, + 0x0000042d, 0x0000044d, 0x0000042d, + 0x0000042e, 0x0000044e, 0x0000042e, + 0x0000042f, 0x0000044f, 0x0000042f, + 0x00000460, 0x00000461, 0x00000460, + 0x00000462, 0x00000463, 0x00000462, + 0x00000464, 0x00000465, 0x00000464, + 0x00000466, 0x00000467, 0x00000466, + 0x00000468, 0x00000469, 0x00000468, + 0x0000046a, 0x0000046b, 0x0000046a, + 0x0000046c, 0x0000046d, 0x0000046c, + 0x0000046e, 0x0000046f, 0x0000046e, + 0x00000470, 0x00000471, 0x00000470, + 0x00000472, 0x00000473, 0x00000472, + 0x00000474, 0x00000475, 0x00000474, + 0x00000476, 0x00000477, 0x00000476, + 0x00000478, 0x00000479, 0x00000478, + 0x0000047a, 0x0000047b, 0x0000047a, + 0x0000047c, 0x0000047d, 0x0000047c, + 0x0000047e, 0x0000047f, 0x0000047e, + 0x00000480, 0x00000481, 0x00000480, + 0x0000048a, 0x0000048b, 0x0000048a, + 0x0000048c, 0x0000048d, 0x0000048c, + 0x0000048e, 0x0000048f, 0x0000048e, + 0x00000490, 0x00000491, 0x00000490, + 0x00000492, 0x00000493, 0x00000492, + 0x00000494, 0x00000495, 0x00000494, + 0x00000496, 0x00000497, 0x00000496, + 0x00000498, 0x00000499, 0x00000498, + 0x0000049a, 0x0000049b, 0x0000049a, + 0x0000049c, 0x0000049d, 0x0000049c, + 0x0000049e, 0x0000049f, 0x0000049e, + 0x000004a0, 0x000004a1, 0x000004a0, + 0x000004a2, 0x000004a3, 0x000004a2, + 0x000004a4, 0x000004a5, 0x000004a4, + 0x000004a6, 0x000004a7, 0x000004a6, + 0x000004a8, 0x000004a9, 0x000004a8, + 0x000004aa, 0x000004ab, 0x000004aa, + 0x000004ac, 0x000004ad, 0x000004ac, + 0x000004ae, 0x000004af, 0x000004ae, + 0x000004b0, 0x000004b1, 0x000004b0, + 0x000004b2, 0x000004b3, 0x000004b2, + 0x000004b4, 0x000004b5, 0x000004b4, + 0x000004b6, 0x000004b7, 0x000004b6, + 0x000004b8, 0x000004b9, 0x000004b8, + 0x000004ba, 0x000004bb, 0x000004ba, + 0x000004bc, 0x000004bd, 0x000004bc, + 0x000004be, 0x000004bf, 0x000004be, + 0x000004c1, 0x000004c2, 0x000004c1, + 0x000004c3, 0x000004c4, 0x000004c3, + 0x000004c5, 0x000004c6, 0x000004c5, + 0x000004c7, 0x000004c8, 0x000004c7, + 0x000004c9, 0x000004ca, 0x000004c9, + 0x000004cb, 0x000004cc, 0x000004cb, + 0x000004cd, 0x000004ce, 0x000004cd, + 0x000004d0, 0x000004d1, 0x000004d0, + 0x000004d2, 0x000004d3, 0x000004d2, + 0x000004d4, 0x000004d5, 0x000004d4, + 0x000004d6, 0x000004d7, 0x000004d6, + 0x000004d8, 0x000004d9, 0x000004d8, + 0x000004da, 0x000004db, 0x000004da, + 0x000004dc, 0x000004dd, 0x000004dc, + 0x000004de, 0x000004df, 0x000004de, + 0x000004e0, 0x000004e1, 0x000004e0, + 0x000004e2, 0x000004e3, 0x000004e2, + 0x000004e4, 0x000004e5, 0x000004e4, + 0x000004e6, 0x000004e7, 0x000004e6, + 0x000004e8, 0x000004e9, 0x000004e8, + 0x000004ea, 0x000004eb, 0x000004ea, + 0x000004ec, 0x000004ed, 0x000004ec, + 0x000004ee, 0x000004ef, 0x000004ee, + 0x000004f0, 0x000004f1, 0x000004f0, + 0x000004f2, 0x000004f3, 0x000004f2, + 0x000004f4, 0x000004f5, 0x000004f4, + 0x000004f8, 0x000004f9, 0x000004f8, + 0x00000500, 0x00000501, 0x00000500, + 0x00000502, 0x00000503, 0x00000502, + 0x00000504, 0x00000505, 0x00000504, + 0x00000506, 0x00000507, 0x00000506, + 0x00000508, 0x00000509, 0x00000508, + 0x0000050a, 0x0000050b, 0x0000050a, + 0x0000050c, 0x0000050d, 0x0000050c, + 0x0000050e, 0x0000050f, 0x0000050e, + 0x00000531, 0x00000561, 0x00000531, + 0x00000532, 0x00000562, 0x00000532, + 0x00000533, 0x00000563, 0x00000533, + 0x00000534, 0x00000564, 0x00000534, + 0x00000535, 0x00000565, 0x00000535, + 0x00000536, 0x00000566, 0x00000536, + 0x00000537, 0x00000567, 0x00000537, + 0x00000538, 0x00000568, 0x00000538, + 0x00000539, 0x00000569, 0x00000539, + 0x0000053a, 0x0000056a, 0x0000053a, + 0x0000053b, 0x0000056b, 0x0000053b, + 0x0000053c, 0x0000056c, 0x0000053c, + 0x0000053d, 0x0000056d, 0x0000053d, + 0x0000053e, 0x0000056e, 0x0000053e, + 0x0000053f, 0x0000056f, 0x0000053f, + 0x00000540, 0x00000570, 0x00000540, + 0x00000541, 0x00000571, 0x00000541, + 0x00000542, 0x00000572, 0x00000542, + 0x00000543, 0x00000573, 0x00000543, + 0x00000544, 0x00000574, 0x00000544, + 0x00000545, 0x00000575, 0x00000545, + 0x00000546, 0x00000576, 0x00000546, + 0x00000547, 0x00000577, 0x00000547, + 0x00000548, 0x00000578, 0x00000548, + 0x00000549, 0x00000579, 0x00000549, + 0x0000054a, 0x0000057a, 0x0000054a, + 0x0000054b, 0x0000057b, 0x0000054b, + 0x0000054c, 0x0000057c, 0x0000054c, + 0x0000054d, 0x0000057d, 0x0000054d, + 0x0000054e, 0x0000057e, 0x0000054e, + 0x0000054f, 0x0000057f, 0x0000054f, + 0x00000550, 0x00000580, 0x00000550, + 0x00000551, 0x00000581, 0x00000551, + 0x00000552, 0x00000582, 0x00000552, + 0x00000553, 0x00000583, 0x00000553, + 0x00000554, 0x00000584, 0x00000554, + 0x00000555, 0x00000585, 0x00000555, + 0x00000556, 0x00000586, 0x00000556, + 0x00001e00, 0x00001e01, 0x00001e00, + 0x00001e02, 0x00001e03, 0x00001e02, + 0x00001e04, 0x00001e05, 0x00001e04, + 0x00001e06, 0x00001e07, 0x00001e06, + 0x00001e08, 0x00001e09, 0x00001e08, + 0x00001e0a, 0x00001e0b, 0x00001e0a, + 0x00001e0c, 0x00001e0d, 0x00001e0c, + 0x00001e0e, 0x00001e0f, 0x00001e0e, + 0x00001e10, 0x00001e11, 0x00001e10, + 0x00001e12, 0x00001e13, 0x00001e12, + 0x00001e14, 0x00001e15, 0x00001e14, + 0x00001e16, 0x00001e17, 0x00001e16, + 0x00001e18, 0x00001e19, 0x00001e18, + 0x00001e1a, 0x00001e1b, 0x00001e1a, + 0x00001e1c, 0x00001e1d, 0x00001e1c, + 0x00001e1e, 0x00001e1f, 0x00001e1e, + 0x00001e20, 0x00001e21, 0x00001e20, + 0x00001e22, 0x00001e23, 0x00001e22, + 0x00001e24, 0x00001e25, 0x00001e24, + 0x00001e26, 0x00001e27, 0x00001e26, + 0x00001e28, 0x00001e29, 0x00001e28, + 0x00001e2a, 0x00001e2b, 0x00001e2a, + 0x00001e2c, 0x00001e2d, 0x00001e2c, + 0x00001e2e, 0x00001e2f, 0x00001e2e, + 0x00001e30, 0x00001e31, 0x00001e30, + 0x00001e32, 0x00001e33, 0x00001e32, + 0x00001e34, 0x00001e35, 0x00001e34, + 0x00001e36, 0x00001e37, 0x00001e36, + 0x00001e38, 0x00001e39, 0x00001e38, + 0x00001e3a, 0x00001e3b, 0x00001e3a, + 0x00001e3c, 0x00001e3d, 0x00001e3c, + 0x00001e3e, 0x00001e3f, 0x00001e3e, + 0x00001e40, 0x00001e41, 0x00001e40, + 0x00001e42, 0x00001e43, 0x00001e42, + 0x00001e44, 0x00001e45, 0x00001e44, + 0x00001e46, 0x00001e47, 0x00001e46, + 0x00001e48, 0x00001e49, 0x00001e48, + 0x00001e4a, 0x00001e4b, 0x00001e4a, + 0x00001e4c, 0x00001e4d, 0x00001e4c, + 0x00001e4e, 0x00001e4f, 0x00001e4e, + 0x00001e50, 0x00001e51, 0x00001e50, + 0x00001e52, 0x00001e53, 0x00001e52, + 0x00001e54, 0x00001e55, 0x00001e54, + 0x00001e56, 0x00001e57, 0x00001e56, + 0x00001e58, 0x00001e59, 0x00001e58, + 0x00001e5a, 0x00001e5b, 0x00001e5a, + 0x00001e5c, 0x00001e5d, 0x00001e5c, + 0x00001e5e, 0x00001e5f, 0x00001e5e, + 0x00001e60, 0x00001e61, 0x00001e60, + 0x00001e62, 0x00001e63, 0x00001e62, + 0x00001e64, 0x00001e65, 0x00001e64, + 0x00001e66, 0x00001e67, 0x00001e66, + 0x00001e68, 0x00001e69, 0x00001e68, + 0x00001e6a, 0x00001e6b, 0x00001e6a, + 0x00001e6c, 0x00001e6d, 0x00001e6c, + 0x00001e6e, 0x00001e6f, 0x00001e6e, + 0x00001e70, 0x00001e71, 0x00001e70, + 0x00001e72, 0x00001e73, 0x00001e72, + 0x00001e74, 0x00001e75, 0x00001e74, + 0x00001e76, 0x00001e77, 0x00001e76, + 0x00001e78, 0x00001e79, 0x00001e78, + 0x00001e7a, 0x00001e7b, 0x00001e7a, + 0x00001e7c, 0x00001e7d, 0x00001e7c, + 0x00001e7e, 0x00001e7f, 0x00001e7e, + 0x00001e80, 0x00001e81, 0x00001e80, + 0x00001e82, 0x00001e83, 0x00001e82, + 0x00001e84, 0x00001e85, 0x00001e84, + 0x00001e86, 0x00001e87, 0x00001e86, + 0x00001e88, 0x00001e89, 0x00001e88, + 0x00001e8a, 0x00001e8b, 0x00001e8a, + 0x00001e8c, 0x00001e8d, 0x00001e8c, + 0x00001e8e, 0x00001e8f, 0x00001e8e, + 0x00001e90, 0x00001e91, 0x00001e90, + 0x00001e92, 0x00001e93, 0x00001e92, + 0x00001e94, 0x00001e95, 0x00001e94, + 0x00001ea0, 0x00001ea1, 0x00001ea0, + 0x00001ea2, 0x00001ea3, 0x00001ea2, + 0x00001ea4, 0x00001ea5, 0x00001ea4, + 0x00001ea6, 0x00001ea7, 0x00001ea6, + 0x00001ea8, 0x00001ea9, 0x00001ea8, + 0x00001eaa, 0x00001eab, 0x00001eaa, + 0x00001eac, 0x00001ead, 0x00001eac, + 0x00001eae, 0x00001eaf, 0x00001eae, + 0x00001eb0, 0x00001eb1, 0x00001eb0, + 0x00001eb2, 0x00001eb3, 0x00001eb2, + 0x00001eb4, 0x00001eb5, 0x00001eb4, + 0x00001eb6, 0x00001eb7, 0x00001eb6, + 0x00001eb8, 0x00001eb9, 0x00001eb8, + 0x00001eba, 0x00001ebb, 0x00001eba, + 0x00001ebc, 0x00001ebd, 0x00001ebc, + 0x00001ebe, 0x00001ebf, 0x00001ebe, + 0x00001ec0, 0x00001ec1, 0x00001ec0, + 0x00001ec2, 0x00001ec3, 0x00001ec2, + 0x00001ec4, 0x00001ec5, 0x00001ec4, + 0x00001ec6, 0x00001ec7, 0x00001ec6, + 0x00001ec8, 0x00001ec9, 0x00001ec8, + 0x00001eca, 0x00001ecb, 0x00001eca, + 0x00001ecc, 0x00001ecd, 0x00001ecc, + 0x00001ece, 0x00001ecf, 0x00001ece, + 0x00001ed0, 0x00001ed1, 0x00001ed0, + 0x00001ed2, 0x00001ed3, 0x00001ed2, + 0x00001ed4, 0x00001ed5, 0x00001ed4, + 0x00001ed6, 0x00001ed7, 0x00001ed6, + 0x00001ed8, 0x00001ed9, 0x00001ed8, + 0x00001eda, 0x00001edb, 0x00001eda, + 0x00001edc, 0x00001edd, 0x00001edc, + 0x00001ede, 0x00001edf, 0x00001ede, + 0x00001ee0, 0x00001ee1, 0x00001ee0, + 0x00001ee2, 0x00001ee3, 0x00001ee2, + 0x00001ee4, 0x00001ee5, 0x00001ee4, + 0x00001ee6, 0x00001ee7, 0x00001ee6, + 0x00001ee8, 0x00001ee9, 0x00001ee8, + 0x00001eea, 0x00001eeb, 0x00001eea, + 0x00001eec, 0x00001eed, 0x00001eec, + 0x00001eee, 0x00001eef, 0x00001eee, + 0x00001ef0, 0x00001ef1, 0x00001ef0, + 0x00001ef2, 0x00001ef3, 0x00001ef2, + 0x00001ef4, 0x00001ef5, 0x00001ef4, + 0x00001ef6, 0x00001ef7, 0x00001ef6, + 0x00001ef8, 0x00001ef9, 0x00001ef8, + 0x00001f08, 0x00001f00, 0x00001f08, + 0x00001f09, 0x00001f01, 0x00001f09, + 0x00001f0a, 0x00001f02, 0x00001f0a, + 0x00001f0b, 0x00001f03, 0x00001f0b, + 0x00001f0c, 0x00001f04, 0x00001f0c, + 0x00001f0d, 0x00001f05, 0x00001f0d, + 0x00001f0e, 0x00001f06, 0x00001f0e, + 0x00001f0f, 0x00001f07, 0x00001f0f, + 0x00001f18, 0x00001f10, 0x00001f18, + 0x00001f19, 0x00001f11, 0x00001f19, + 0x00001f1a, 0x00001f12, 0x00001f1a, + 0x00001f1b, 0x00001f13, 0x00001f1b, + 0x00001f1c, 0x00001f14, 0x00001f1c, + 0x00001f1d, 0x00001f15, 0x00001f1d, + 0x00001f28, 0x00001f20, 0x00001f28, + 0x00001f29, 0x00001f21, 0x00001f29, + 0x00001f2a, 0x00001f22, 0x00001f2a, + 0x00001f2b, 0x00001f23, 0x00001f2b, + 0x00001f2c, 0x00001f24, 0x00001f2c, + 0x00001f2d, 0x00001f25, 0x00001f2d, + 0x00001f2e, 0x00001f26, 0x00001f2e, + 0x00001f2f, 0x00001f27, 0x00001f2f, + 0x00001f38, 0x00001f30, 0x00001f38, + 0x00001f39, 0x00001f31, 0x00001f39, + 0x00001f3a, 0x00001f32, 0x00001f3a, + 0x00001f3b, 0x00001f33, 0x00001f3b, + 0x00001f3c, 0x00001f34, 0x00001f3c, + 0x00001f3d, 0x00001f35, 0x00001f3d, + 0x00001f3e, 0x00001f36, 0x00001f3e, + 0x00001f3f, 0x00001f37, 0x00001f3f, + 0x00001f48, 0x00001f40, 0x00001f48, + 0x00001f49, 0x00001f41, 0x00001f49, + 0x00001f4a, 0x00001f42, 0x00001f4a, + 0x00001f4b, 0x00001f43, 0x00001f4b, + 0x00001f4c, 0x00001f44, 0x00001f4c, + 0x00001f4d, 0x00001f45, 0x00001f4d, + 0x00001f59, 0x00001f51, 0x00001f59, + 0x00001f5b, 0x00001f53, 0x00001f5b, + 0x00001f5d, 0x00001f55, 0x00001f5d, + 0x00001f5f, 0x00001f57, 0x00001f5f, + 0x00001f68, 0x00001f60, 0x00001f68, + 0x00001f69, 0x00001f61, 0x00001f69, + 0x00001f6a, 0x00001f62, 0x00001f6a, + 0x00001f6b, 0x00001f63, 0x00001f6b, + 0x00001f6c, 0x00001f64, 0x00001f6c, + 0x00001f6d, 0x00001f65, 0x00001f6d, + 0x00001f6e, 0x00001f66, 0x00001f6e, + 0x00001f6f, 0x00001f67, 0x00001f6f, + 0x00001fb8, 0x00001fb0, 0x00001fb8, + 0x00001fb9, 0x00001fb1, 0x00001fb9, + 0x00001fba, 0x00001f70, 0x00001fba, + 0x00001fbb, 0x00001f71, 0x00001fbb, + 0x00001fc8, 0x00001f72, 0x00001fc8, + 0x00001fc9, 0x00001f73, 0x00001fc9, + 0x00001fca, 0x00001f74, 0x00001fca, + 0x00001fcb, 0x00001f75, 0x00001fcb, + 0x00001fd8, 0x00001fd0, 0x00001fd8, + 0x00001fd9, 0x00001fd1, 0x00001fd9, + 0x00001fda, 0x00001f76, 0x00001fda, + 0x00001fdb, 0x00001f77, 0x00001fdb, + 0x00001fe8, 0x00001fe0, 0x00001fe8, + 0x00001fe9, 0x00001fe1, 0x00001fe9, + 0x00001fea, 0x00001f7a, 0x00001fea, + 0x00001feb, 0x00001f7b, 0x00001feb, + 0x00001fec, 0x00001fe5, 0x00001fec, + 0x00001ff8, 0x00001f78, 0x00001ff8, + 0x00001ff9, 0x00001f79, 0x00001ff9, + 0x00001ffa, 0x00001f7c, 0x00001ffa, + 0x00001ffb, 0x00001f7d, 0x00001ffb, + 0x00002126, 0x000003c9, 0x00002126, + 0x0000212a, 0x0000006b, 0x0000212a, + 0x0000212b, 0x000000e5, 0x0000212b, + 0x00002160, 0x00002170, 0x00002160, + 0x00002161, 0x00002171, 0x00002161, + 0x00002162, 0x00002172, 0x00002162, + 0x00002163, 0x00002173, 0x00002163, + 0x00002164, 0x00002174, 0x00002164, + 0x00002165, 0x00002175, 0x00002165, + 0x00002166, 0x00002176, 0x00002166, + 0x00002167, 0x00002177, 0x00002167, + 0x00002168, 0x00002178, 0x00002168, + 0x00002169, 0x00002179, 0x00002169, + 0x0000216a, 0x0000217a, 0x0000216a, + 0x0000216b, 0x0000217b, 0x0000216b, + 0x0000216c, 0x0000217c, 0x0000216c, + 0x0000216d, 0x0000217d, 0x0000216d, + 0x0000216e, 0x0000217e, 0x0000216e, + 0x0000216f, 0x0000217f, 0x0000216f, + 0x000024b6, 0x000024d0, 0x000024b6, + 0x000024b7, 0x000024d1, 0x000024b7, + 0x000024b8, 0x000024d2, 0x000024b8, + 0x000024b9, 0x000024d3, 0x000024b9, + 0x000024ba, 0x000024d4, 0x000024ba, + 0x000024bb, 0x000024d5, 0x000024bb, + 0x000024bc, 0x000024d6, 0x000024bc, + 0x000024bd, 0x000024d7, 0x000024bd, + 0x000024be, 0x000024d8, 0x000024be, + 0x000024bf, 0x000024d9, 0x000024bf, + 0x000024c0, 0x000024da, 0x000024c0, + 0x000024c1, 0x000024db, 0x000024c1, + 0x000024c2, 0x000024dc, 0x000024c2, + 0x000024c3, 0x000024dd, 0x000024c3, + 0x000024c4, 0x000024de, 0x000024c4, + 0x000024c5, 0x000024df, 0x000024c5, + 0x000024c6, 0x000024e0, 0x000024c6, + 0x000024c7, 0x000024e1, 0x000024c7, + 0x000024c8, 0x000024e2, 0x000024c8, + 0x000024c9, 0x000024e3, 0x000024c9, + 0x000024ca, 0x000024e4, 0x000024ca, + 0x000024cb, 0x000024e5, 0x000024cb, + 0x000024cc, 0x000024e6, 0x000024cc, + 0x000024cd, 0x000024e7, 0x000024cd, + 0x000024ce, 0x000024e8, 0x000024ce, + 0x000024cf, 0x000024e9, 0x000024cf, + 0x0000ff21, 0x0000ff41, 0x0000ff21, + 0x0000ff22, 0x0000ff42, 0x0000ff22, + 0x0000ff23, 0x0000ff43, 0x0000ff23, + 0x0000ff24, 0x0000ff44, 0x0000ff24, + 0x0000ff25, 0x0000ff45, 0x0000ff25, + 0x0000ff26, 0x0000ff46, 0x0000ff26, + 0x0000ff27, 0x0000ff47, 0x0000ff27, + 0x0000ff28, 0x0000ff48, 0x0000ff28, + 0x0000ff29, 0x0000ff49, 0x0000ff29, + 0x0000ff2a, 0x0000ff4a, 0x0000ff2a, + 0x0000ff2b, 0x0000ff4b, 0x0000ff2b, + 0x0000ff2c, 0x0000ff4c, 0x0000ff2c, + 0x0000ff2d, 0x0000ff4d, 0x0000ff2d, + 0x0000ff2e, 0x0000ff4e, 0x0000ff2e, + 0x0000ff2f, 0x0000ff4f, 0x0000ff2f, + 0x0000ff30, 0x0000ff50, 0x0000ff30, + 0x0000ff31, 0x0000ff51, 0x0000ff31, + 0x0000ff32, 0x0000ff52, 0x0000ff32, + 0x0000ff33, 0x0000ff53, 0x0000ff33, + 0x0000ff34, 0x0000ff54, 0x0000ff34, + 0x0000ff35, 0x0000ff55, 0x0000ff35, + 0x0000ff36, 0x0000ff56, 0x0000ff36, + 0x0000ff37, 0x0000ff57, 0x0000ff37, + 0x0000ff38, 0x0000ff58, 0x0000ff38, + 0x0000ff39, 0x0000ff59, 0x0000ff39, + 0x0000ff3a, 0x0000ff5a, 0x0000ff3a, + 0x00010400, 0x00010428, 0x00010400, + 0x00010401, 0x00010429, 0x00010401, + 0x00010402, 0x0001042a, 0x00010402, + 0x00010403, 0x0001042b, 0x00010403, + 0x00010404, 0x0001042c, 0x00010404, + 0x00010405, 0x0001042d, 0x00010405, + 0x00010406, 0x0001042e, 0x00010406, + 0x00010407, 0x0001042f, 0x00010407, + 0x00010408, 0x00010430, 0x00010408, + 0x00010409, 0x00010431, 0x00010409, + 0x0001040a, 0x00010432, 0x0001040a, + 0x0001040b, 0x00010433, 0x0001040b, + 0x0001040c, 0x00010434, 0x0001040c, + 0x0001040d, 0x00010435, 0x0001040d, + 0x0001040e, 0x00010436, 0x0001040e, + 0x0001040f, 0x00010437, 0x0001040f, + 0x00010410, 0x00010438, 0x00010410, + 0x00010411, 0x00010439, 0x00010411, + 0x00010412, 0x0001043a, 0x00010412, + 0x00010413, 0x0001043b, 0x00010413, + 0x00010414, 0x0001043c, 0x00010414, + 0x00010415, 0x0001043d, 0x00010415, + 0x00010416, 0x0001043e, 0x00010416, + 0x00010417, 0x0001043f, 0x00010417, + 0x00010418, 0x00010440, 0x00010418, + 0x00010419, 0x00010441, 0x00010419, + 0x0001041a, 0x00010442, 0x0001041a, + 0x0001041b, 0x00010443, 0x0001041b, + 0x0001041c, 0x00010444, 0x0001041c, + 0x0001041d, 0x00010445, 0x0001041d, + 0x0001041e, 0x00010446, 0x0001041e, + 0x0001041f, 0x00010447, 0x0001041f, + 0x00010420, 0x00010448, 0x00010420, + 0x00010421, 0x00010449, 0x00010421, + 0x00010422, 0x0001044a, 0x00010422, + 0x00010423, 0x0001044b, 0x00010423, + 0x00010424, 0x0001044c, 0x00010424, + 0x00010425, 0x0001044d, 0x00010425, + 0x00000061, 0x00000041, 0x00000041, + 0x00000062, 0x00000042, 0x00000042, + 0x00000063, 0x00000043, 0x00000043, + 0x00000064, 0x00000044, 0x00000044, + 0x00000065, 0x00000045, 0x00000045, + 0x00000066, 0x00000046, 0x00000046, + 0x00000067, 0x00000047, 0x00000047, + 0x00000068, 0x00000048, 0x00000048, + 0x00000069, 0x00000049, 0x00000049, + 0x0000006a, 0x0000004a, 0x0000004a, + 0x0000006b, 0x0000004b, 0x0000004b, + 0x0000006c, 0x0000004c, 0x0000004c, + 0x0000006d, 0x0000004d, 0x0000004d, + 0x0000006e, 0x0000004e, 0x0000004e, + 0x0000006f, 0x0000004f, 0x0000004f, + 0x00000070, 0x00000050, 0x00000050, + 0x00000071, 0x00000051, 0x00000051, + 0x00000072, 0x00000052, 0x00000052, + 0x00000073, 0x00000053, 0x00000053, + 0x00000074, 0x00000054, 0x00000054, + 0x00000075, 0x00000055, 0x00000055, + 0x00000076, 0x00000056, 0x00000056, + 0x00000077, 0x00000057, 0x00000057, + 0x00000078, 0x00000058, 0x00000058, + 0x00000079, 0x00000059, 0x00000059, + 0x0000007a, 0x0000005a, 0x0000005a, + 0x000000b5, 0x0000039c, 0x0000039c, + 0x000000e0, 0x000000c0, 0x000000c0, + 0x000000e1, 0x000000c1, 0x000000c1, + 0x000000e2, 0x000000c2, 0x000000c2, + 0x000000e3, 0x000000c3, 0x000000c3, + 0x000000e4, 0x000000c4, 0x000000c4, + 0x000000e5, 0x000000c5, 0x000000c5, + 0x000000e6, 0x000000c6, 0x000000c6, + 0x000000e7, 0x000000c7, 0x000000c7, + 0x000000e8, 0x000000c8, 0x000000c8, + 0x000000e9, 0x000000c9, 0x000000c9, + 0x000000ea, 0x000000ca, 0x000000ca, + 0x000000eb, 0x000000cb, 0x000000cb, + 0x000000ec, 0x000000cc, 0x000000cc, + 0x000000ed, 0x000000cd, 0x000000cd, + 0x000000ee, 0x000000ce, 0x000000ce, + 0x000000ef, 0x000000cf, 0x000000cf, + 0x000000f0, 0x000000d0, 0x000000d0, + 0x000000f1, 0x000000d1, 0x000000d1, + 0x000000f2, 0x000000d2, 0x000000d2, + 0x000000f3, 0x000000d3, 0x000000d3, + 0x000000f4, 0x000000d4, 0x000000d4, + 0x000000f5, 0x000000d5, 0x000000d5, + 0x000000f6, 0x000000d6, 0x000000d6, + 0x000000f8, 0x000000d8, 0x000000d8, + 0x000000f9, 0x000000d9, 0x000000d9, + 0x000000fa, 0x000000da, 0x000000da, + 0x000000fb, 0x000000db, 0x000000db, + 0x000000fc, 0x000000dc, 0x000000dc, + 0x000000fd, 0x000000dd, 0x000000dd, + 0x000000fe, 0x000000de, 0x000000de, + 0x000000ff, 0x00000178, 0x00000178, + 0x00000101, 0x00000100, 0x00000100, + 0x00000103, 0x00000102, 0x00000102, + 0x00000105, 0x00000104, 0x00000104, + 0x00000107, 0x00000106, 0x00000106, + 0x00000109, 0x00000108, 0x00000108, + 0x0000010b, 0x0000010a, 0x0000010a, + 0x0000010d, 0x0000010c, 0x0000010c, + 0x0000010f, 0x0000010e, 0x0000010e, + 0x00000111, 0x00000110, 0x00000110, + 0x00000113, 0x00000112, 0x00000112, + 0x00000115, 0x00000114, 0x00000114, + 0x00000117, 0x00000116, 0x00000116, + 0x00000119, 0x00000118, 0x00000118, + 0x0000011b, 0x0000011a, 0x0000011a, + 0x0000011d, 0x0000011c, 0x0000011c, + 0x0000011f, 0x0000011e, 0x0000011e, + 0x00000121, 0x00000120, 0x00000120, + 0x00000123, 0x00000122, 0x00000122, + 0x00000125, 0x00000124, 0x00000124, + 0x00000127, 0x00000126, 0x00000126, + 0x00000129, 0x00000128, 0x00000128, + 0x0000012b, 0x0000012a, 0x0000012a, + 0x0000012d, 0x0000012c, 0x0000012c, + 0x0000012f, 0x0000012e, 0x0000012e, + 0x00000131, 0x00000049, 0x00000049, + 0x00000133, 0x00000132, 0x00000132, + 0x00000135, 0x00000134, 0x00000134, + 0x00000137, 0x00000136, 0x00000136, + 0x0000013a, 0x00000139, 0x00000139, + 0x0000013c, 0x0000013b, 0x0000013b, + 0x0000013e, 0x0000013d, 0x0000013d, + 0x00000140, 0x0000013f, 0x0000013f, + 0x00000142, 0x00000141, 0x00000141, + 0x00000144, 0x00000143, 0x00000143, + 0x00000146, 0x00000145, 0x00000145, + 0x00000148, 0x00000147, 0x00000147, + 0x0000014b, 0x0000014a, 0x0000014a, + 0x0000014d, 0x0000014c, 0x0000014c, + 0x0000014f, 0x0000014e, 0x0000014e, + 0x00000151, 0x00000150, 0x00000150, + 0x00000153, 0x00000152, 0x00000152, + 0x00000155, 0x00000154, 0x00000154, + 0x00000157, 0x00000156, 0x00000156, + 0x00000159, 0x00000158, 0x00000158, + 0x0000015b, 0x0000015a, 0x0000015a, + 0x0000015d, 0x0000015c, 0x0000015c, + 0x0000015f, 0x0000015e, 0x0000015e, + 0x00000161, 0x00000160, 0x00000160, + 0x00000163, 0x00000162, 0x00000162, + 0x00000165, 0x00000164, 0x00000164, + 0x00000167, 0x00000166, 0x00000166, + 0x00000169, 0x00000168, 0x00000168, + 0x0000016b, 0x0000016a, 0x0000016a, + 0x0000016d, 0x0000016c, 0x0000016c, + 0x0000016f, 0x0000016e, 0x0000016e, + 0x00000171, 0x00000170, 0x00000170, + 0x00000173, 0x00000172, 0x00000172, + 0x00000175, 0x00000174, 0x00000174, + 0x00000177, 0x00000176, 0x00000176, + 0x0000017a, 0x00000179, 0x00000179, + 0x0000017c, 0x0000017b, 0x0000017b, + 0x0000017e, 0x0000017d, 0x0000017d, + 0x0000017f, 0x00000053, 0x00000053, + 0x00000183, 0x00000182, 0x00000182, + 0x00000185, 0x00000184, 0x00000184, + 0x00000188, 0x00000187, 0x00000187, + 0x0000018c, 0x0000018b, 0x0000018b, + 0x00000192, 0x00000191, 0x00000191, + 0x00000195, 0x000001f6, 0x000001f6, + 0x00000199, 0x00000198, 0x00000198, + 0x0000019e, 0x00000220, 0x00000220, + 0x000001a1, 0x000001a0, 0x000001a0, + 0x000001a3, 0x000001a2, 0x000001a2, + 0x000001a5, 0x000001a4, 0x000001a4, + 0x000001a8, 0x000001a7, 0x000001a7, + 0x000001ad, 0x000001ac, 0x000001ac, + 0x000001b0, 0x000001af, 0x000001af, + 0x000001b4, 0x000001b3, 0x000001b3, + 0x000001b6, 0x000001b5, 0x000001b5, + 0x000001b9, 0x000001b8, 0x000001b8, + 0x000001bd, 0x000001bc, 0x000001bc, + 0x000001bf, 0x000001f7, 0x000001f7, + 0x000001c6, 0x000001c4, 0x000001c5, + 0x000001c9, 0x000001c7, 0x000001c8, + 0x000001cc, 0x000001ca, 0x000001cb, + 0x000001ce, 0x000001cd, 0x000001cd, + 0x000001d0, 0x000001cf, 0x000001cf, + 0x000001d2, 0x000001d1, 0x000001d1, + 0x000001d4, 0x000001d3, 0x000001d3, + 0x000001d6, 0x000001d5, 0x000001d5, + 0x000001d8, 0x000001d7, 0x000001d7, + 0x000001da, 0x000001d9, 0x000001d9, + 0x000001dc, 0x000001db, 0x000001db, + 0x000001dd, 0x0000018e, 0x0000018e, + 0x000001df, 0x000001de, 0x000001de, + 0x000001e1, 0x000001e0, 0x000001e0, + 0x000001e3, 0x000001e2, 0x000001e2, + 0x000001e5, 0x000001e4, 0x000001e4, + 0x000001e7, 0x000001e6, 0x000001e6, + 0x000001e9, 0x000001e8, 0x000001e8, + 0x000001eb, 0x000001ea, 0x000001ea, + 0x000001ed, 0x000001ec, 0x000001ec, + 0x000001ef, 0x000001ee, 0x000001ee, + 0x000001f3, 0x000001f1, 0x000001f2, + 0x000001f5, 0x000001f4, 0x000001f4, + 0x000001f9, 0x000001f8, 0x000001f8, + 0x000001fb, 0x000001fa, 0x000001fa, + 0x000001fd, 0x000001fc, 0x000001fc, + 0x000001ff, 0x000001fe, 0x000001fe, + 0x00000201, 0x00000200, 0x00000200, + 0x00000203, 0x00000202, 0x00000202, + 0x00000205, 0x00000204, 0x00000204, + 0x00000207, 0x00000206, 0x00000206, + 0x00000209, 0x00000208, 0x00000208, + 0x0000020b, 0x0000020a, 0x0000020a, + 0x0000020d, 0x0000020c, 0x0000020c, + 0x0000020f, 0x0000020e, 0x0000020e, + 0x00000211, 0x00000210, 0x00000210, + 0x00000213, 0x00000212, 0x00000212, + 0x00000215, 0x00000214, 0x00000214, + 0x00000217, 0x00000216, 0x00000216, + 0x00000219, 0x00000218, 0x00000218, + 0x0000021b, 0x0000021a, 0x0000021a, + 0x0000021d, 0x0000021c, 0x0000021c, + 0x0000021f, 0x0000021e, 0x0000021e, + 0x00000223, 0x00000222, 0x00000222, + 0x00000225, 0x00000224, 0x00000224, + 0x00000227, 0x00000226, 0x00000226, + 0x00000229, 0x00000228, 0x00000228, + 0x0000022b, 0x0000022a, 0x0000022a, + 0x0000022d, 0x0000022c, 0x0000022c, + 0x0000022f, 0x0000022e, 0x0000022e, + 0x00000231, 0x00000230, 0x00000230, + 0x00000233, 0x00000232, 0x00000232, + 0x00000253, 0x00000181, 0x00000181, + 0x00000254, 0x00000186, 0x00000186, + 0x00000256, 0x00000189, 0x00000189, + 0x00000257, 0x0000018a, 0x0000018a, + 0x00000259, 0x0000018f, 0x0000018f, + 0x0000025b, 0x00000190, 0x00000190, + 0x00000260, 0x00000193, 0x00000193, + 0x00000263, 0x00000194, 0x00000194, + 0x00000268, 0x00000197, 0x00000197, + 0x00000269, 0x00000196, 0x00000196, + 0x0000026f, 0x0000019c, 0x0000019c, + 0x00000272, 0x0000019d, 0x0000019d, + 0x00000275, 0x0000019f, 0x0000019f, + 0x00000280, 0x000001a6, 0x000001a6, + 0x00000283, 0x000001a9, 0x000001a9, + 0x00000288, 0x000001ae, 0x000001ae, + 0x0000028a, 0x000001b1, 0x000001b1, + 0x0000028b, 0x000001b2, 0x000001b2, + 0x00000292, 0x000001b7, 0x000001b7, + 0x00000345, 0x00000399, 0x00000399, + 0x000003ac, 0x00000386, 0x00000386, + 0x000003ad, 0x00000388, 0x00000388, + 0x000003ae, 0x00000389, 0x00000389, + 0x000003af, 0x0000038a, 0x0000038a, + 0x000003b1, 0x00000391, 0x00000391, + 0x000003b2, 0x00000392, 0x00000392, + 0x000003b3, 0x00000393, 0x00000393, + 0x000003b4, 0x00000394, 0x00000394, + 0x000003b5, 0x00000395, 0x00000395, + 0x000003b6, 0x00000396, 0x00000396, + 0x000003b7, 0x00000397, 0x00000397, + 0x000003b8, 0x00000398, 0x00000398, + 0x000003b9, 0x00000399, 0x00000399, + 0x000003ba, 0x0000039a, 0x0000039a, + 0x000003bb, 0x0000039b, 0x0000039b, + 0x000003bc, 0x0000039c, 0x0000039c, + 0x000003bd, 0x0000039d, 0x0000039d, + 0x000003be, 0x0000039e, 0x0000039e, + 0x000003bf, 0x0000039f, 0x0000039f, + 0x000003c0, 0x000003a0, 0x000003a0, + 0x000003c1, 0x000003a1, 0x000003a1, + 0x000003c2, 0x000003a3, 0x000003a3, + 0x000003c3, 0x000003a3, 0x000003a3, + 0x000003c4, 0x000003a4, 0x000003a4, + 0x000003c5, 0x000003a5, 0x000003a5, + 0x000003c6, 0x000003a6, 0x000003a6, + 0x000003c7, 0x000003a7, 0x000003a7, + 0x000003c8, 0x000003a8, 0x000003a8, + 0x000003c9, 0x000003a9, 0x000003a9, + 0x000003ca, 0x000003aa, 0x000003aa, + 0x000003cb, 0x000003ab, 0x000003ab, + 0x000003cc, 0x0000038c, 0x0000038c, + 0x000003cd, 0x0000038e, 0x0000038e, + 0x000003ce, 0x0000038f, 0x0000038f, + 0x000003d0, 0x00000392, 0x00000392, + 0x000003d1, 0x00000398, 0x00000398, + 0x000003d5, 0x000003a6, 0x000003a6, + 0x000003d6, 0x000003a0, 0x000003a0, + 0x000003d9, 0x000003d8, 0x000003d8, + 0x000003db, 0x000003da, 0x000003da, + 0x000003dd, 0x000003dc, 0x000003dc, + 0x000003df, 0x000003de, 0x000003de, + 0x000003e1, 0x000003e0, 0x000003e0, + 0x000003e3, 0x000003e2, 0x000003e2, + 0x000003e5, 0x000003e4, 0x000003e4, + 0x000003e7, 0x000003e6, 0x000003e6, + 0x000003e9, 0x000003e8, 0x000003e8, + 0x000003eb, 0x000003ea, 0x000003ea, + 0x000003ed, 0x000003ec, 0x000003ec, + 0x000003ef, 0x000003ee, 0x000003ee, + 0x000003f0, 0x0000039a, 0x0000039a, + 0x000003f1, 0x000003a1, 0x000003a1, + 0x000003f2, 0x000003a3, 0x000003a3, + 0x000003f5, 0x00000395, 0x00000395, + 0x00000430, 0x00000410, 0x00000410, + 0x00000431, 0x00000411, 0x00000411, + 0x00000432, 0x00000412, 0x00000412, + 0x00000433, 0x00000413, 0x00000413, + 0x00000434, 0x00000414, 0x00000414, + 0x00000435, 0x00000415, 0x00000415, + 0x00000436, 0x00000416, 0x00000416, + 0x00000437, 0x00000417, 0x00000417, + 0x00000438, 0x00000418, 0x00000418, + 0x00000439, 0x00000419, 0x00000419, + 0x0000043a, 0x0000041a, 0x0000041a, + 0x0000043b, 0x0000041b, 0x0000041b, + 0x0000043c, 0x0000041c, 0x0000041c, + 0x0000043d, 0x0000041d, 0x0000041d, + 0x0000043e, 0x0000041e, 0x0000041e, + 0x0000043f, 0x0000041f, 0x0000041f, + 0x00000440, 0x00000420, 0x00000420, + 0x00000441, 0x00000421, 0x00000421, + 0x00000442, 0x00000422, 0x00000422, + 0x00000443, 0x00000423, 0x00000423, + 0x00000444, 0x00000424, 0x00000424, + 0x00000445, 0x00000425, 0x00000425, + 0x00000446, 0x00000426, 0x00000426, + 0x00000447, 0x00000427, 0x00000427, + 0x00000448, 0x00000428, 0x00000428, + 0x00000449, 0x00000429, 0x00000429, + 0x0000044a, 0x0000042a, 0x0000042a, + 0x0000044b, 0x0000042b, 0x0000042b, + 0x0000044c, 0x0000042c, 0x0000042c, + 0x0000044d, 0x0000042d, 0x0000042d, + 0x0000044e, 0x0000042e, 0x0000042e, + 0x0000044f, 0x0000042f, 0x0000042f, + 0x00000450, 0x00000400, 0x00000400, + 0x00000451, 0x00000401, 0x00000401, + 0x00000452, 0x00000402, 0x00000402, + 0x00000453, 0x00000403, 0x00000403, + 0x00000454, 0x00000404, 0x00000404, + 0x00000455, 0x00000405, 0x00000405, + 0x00000456, 0x00000406, 0x00000406, + 0x00000457, 0x00000407, 0x00000407, + 0x00000458, 0x00000408, 0x00000408, + 0x00000459, 0x00000409, 0x00000409, + 0x0000045a, 0x0000040a, 0x0000040a, + 0x0000045b, 0x0000040b, 0x0000040b, + 0x0000045c, 0x0000040c, 0x0000040c, + 0x0000045d, 0x0000040d, 0x0000040d, + 0x0000045e, 0x0000040e, 0x0000040e, + 0x0000045f, 0x0000040f, 0x0000040f, + 0x00000461, 0x00000460, 0x00000460, + 0x00000463, 0x00000462, 0x00000462, + 0x00000465, 0x00000464, 0x00000464, + 0x00000467, 0x00000466, 0x00000466, + 0x00000469, 0x00000468, 0x00000468, + 0x0000046b, 0x0000046a, 0x0000046a, + 0x0000046d, 0x0000046c, 0x0000046c, + 0x0000046f, 0x0000046e, 0x0000046e, + 0x00000471, 0x00000470, 0x00000470, + 0x00000473, 0x00000472, 0x00000472, + 0x00000475, 0x00000474, 0x00000474, + 0x00000477, 0x00000476, 0x00000476, + 0x00000479, 0x00000478, 0x00000478, + 0x0000047b, 0x0000047a, 0x0000047a, + 0x0000047d, 0x0000047c, 0x0000047c, + 0x0000047f, 0x0000047e, 0x0000047e, + 0x00000481, 0x00000480, 0x00000480, + 0x0000048b, 0x0000048a, 0x0000048a, + 0x0000048d, 0x0000048c, 0x0000048c, + 0x0000048f, 0x0000048e, 0x0000048e, + 0x00000491, 0x00000490, 0x00000490, + 0x00000493, 0x00000492, 0x00000492, + 0x00000495, 0x00000494, 0x00000494, + 0x00000497, 0x00000496, 0x00000496, + 0x00000499, 0x00000498, 0x00000498, + 0x0000049b, 0x0000049a, 0x0000049a, + 0x0000049d, 0x0000049c, 0x0000049c, + 0x0000049f, 0x0000049e, 0x0000049e, + 0x000004a1, 0x000004a0, 0x000004a0, + 0x000004a3, 0x000004a2, 0x000004a2, + 0x000004a5, 0x000004a4, 0x000004a4, + 0x000004a7, 0x000004a6, 0x000004a6, + 0x000004a9, 0x000004a8, 0x000004a8, + 0x000004ab, 0x000004aa, 0x000004aa, + 0x000004ad, 0x000004ac, 0x000004ac, + 0x000004af, 0x000004ae, 0x000004ae, + 0x000004b1, 0x000004b0, 0x000004b0, + 0x000004b3, 0x000004b2, 0x000004b2, + 0x000004b5, 0x000004b4, 0x000004b4, + 0x000004b7, 0x000004b6, 0x000004b6, + 0x000004b9, 0x000004b8, 0x000004b8, + 0x000004bb, 0x000004ba, 0x000004ba, + 0x000004bd, 0x000004bc, 0x000004bc, + 0x000004bf, 0x000004be, 0x000004be, + 0x000004c2, 0x000004c1, 0x000004c1, + 0x000004c4, 0x000004c3, 0x000004c3, + 0x000004c6, 0x000004c5, 0x000004c5, + 0x000004c8, 0x000004c7, 0x000004c7, + 0x000004ca, 0x000004c9, 0x000004c9, + 0x000004cc, 0x000004cb, 0x000004cb, + 0x000004ce, 0x000004cd, 0x000004cd, + 0x000004d1, 0x000004d0, 0x000004d0, + 0x000004d3, 0x000004d2, 0x000004d2, + 0x000004d5, 0x000004d4, 0x000004d4, + 0x000004d7, 0x000004d6, 0x000004d6, + 0x000004d9, 0x000004d8, 0x000004d8, + 0x000004db, 0x000004da, 0x000004da, + 0x000004dd, 0x000004dc, 0x000004dc, + 0x000004df, 0x000004de, 0x000004de, + 0x000004e1, 0x000004e0, 0x000004e0, + 0x000004e3, 0x000004e2, 0x000004e2, + 0x000004e5, 0x000004e4, 0x000004e4, + 0x000004e7, 0x000004e6, 0x000004e6, + 0x000004e9, 0x000004e8, 0x000004e8, + 0x000004eb, 0x000004ea, 0x000004ea, + 0x000004ed, 0x000004ec, 0x000004ec, + 0x000004ef, 0x000004ee, 0x000004ee, + 0x000004f1, 0x000004f0, 0x000004f0, + 0x000004f3, 0x000004f2, 0x000004f2, + 0x000004f5, 0x000004f4, 0x000004f4, + 0x000004f9, 0x000004f8, 0x000004f8, + 0x00000501, 0x00000500, 0x00000500, + 0x00000503, 0x00000502, 0x00000502, + 0x00000505, 0x00000504, 0x00000504, + 0x00000507, 0x00000506, 0x00000506, + 0x00000509, 0x00000508, 0x00000508, + 0x0000050b, 0x0000050a, 0x0000050a, + 0x0000050d, 0x0000050c, 0x0000050c, + 0x0000050f, 0x0000050e, 0x0000050e, + 0x00000561, 0x00000531, 0x00000531, + 0x00000562, 0x00000532, 0x00000532, + 0x00000563, 0x00000533, 0x00000533, + 0x00000564, 0x00000534, 0x00000534, + 0x00000565, 0x00000535, 0x00000535, + 0x00000566, 0x00000536, 0x00000536, + 0x00000567, 0x00000537, 0x00000537, + 0x00000568, 0x00000538, 0x00000538, + 0x00000569, 0x00000539, 0x00000539, + 0x0000056a, 0x0000053a, 0x0000053a, + 0x0000056b, 0x0000053b, 0x0000053b, + 0x0000056c, 0x0000053c, 0x0000053c, + 0x0000056d, 0x0000053d, 0x0000053d, + 0x0000056e, 0x0000053e, 0x0000053e, + 0x0000056f, 0x0000053f, 0x0000053f, + 0x00000570, 0x00000540, 0x00000540, + 0x00000571, 0x00000541, 0x00000541, + 0x00000572, 0x00000542, 0x00000542, + 0x00000573, 0x00000543, 0x00000543, + 0x00000574, 0x00000544, 0x00000544, + 0x00000575, 0x00000545, 0x00000545, + 0x00000576, 0x00000546, 0x00000546, + 0x00000577, 0x00000547, 0x00000547, + 0x00000578, 0x00000548, 0x00000548, + 0x00000579, 0x00000549, 0x00000549, + 0x0000057a, 0x0000054a, 0x0000054a, + 0x0000057b, 0x0000054b, 0x0000054b, + 0x0000057c, 0x0000054c, 0x0000054c, + 0x0000057d, 0x0000054d, 0x0000054d, + 0x0000057e, 0x0000054e, 0x0000054e, + 0x0000057f, 0x0000054f, 0x0000054f, + 0x00000580, 0x00000550, 0x00000550, + 0x00000581, 0x00000551, 0x00000551, + 0x00000582, 0x00000552, 0x00000552, + 0x00000583, 0x00000553, 0x00000553, + 0x00000584, 0x00000554, 0x00000554, + 0x00000585, 0x00000555, 0x00000555, + 0x00000586, 0x00000556, 0x00000556, + 0x00001e01, 0x00001e00, 0x00001e00, + 0x00001e03, 0x00001e02, 0x00001e02, + 0x00001e05, 0x00001e04, 0x00001e04, + 0x00001e07, 0x00001e06, 0x00001e06, + 0x00001e09, 0x00001e08, 0x00001e08, + 0x00001e0b, 0x00001e0a, 0x00001e0a, + 0x00001e0d, 0x00001e0c, 0x00001e0c, + 0x00001e0f, 0x00001e0e, 0x00001e0e, + 0x00001e11, 0x00001e10, 0x00001e10, + 0x00001e13, 0x00001e12, 0x00001e12, + 0x00001e15, 0x00001e14, 0x00001e14, + 0x00001e17, 0x00001e16, 0x00001e16, + 0x00001e19, 0x00001e18, 0x00001e18, + 0x00001e1b, 0x00001e1a, 0x00001e1a, + 0x00001e1d, 0x00001e1c, 0x00001e1c, + 0x00001e1f, 0x00001e1e, 0x00001e1e, + 0x00001e21, 0x00001e20, 0x00001e20, + 0x00001e23, 0x00001e22, 0x00001e22, + 0x00001e25, 0x00001e24, 0x00001e24, + 0x00001e27, 0x00001e26, 0x00001e26, + 0x00001e29, 0x00001e28, 0x00001e28, + 0x00001e2b, 0x00001e2a, 0x00001e2a, + 0x00001e2d, 0x00001e2c, 0x00001e2c, + 0x00001e2f, 0x00001e2e, 0x00001e2e, + 0x00001e31, 0x00001e30, 0x00001e30, + 0x00001e33, 0x00001e32, 0x00001e32, + 0x00001e35, 0x00001e34, 0x00001e34, + 0x00001e37, 0x00001e36, 0x00001e36, + 0x00001e39, 0x00001e38, 0x00001e38, + 0x00001e3b, 0x00001e3a, 0x00001e3a, + 0x00001e3d, 0x00001e3c, 0x00001e3c, + 0x00001e3f, 0x00001e3e, 0x00001e3e, + 0x00001e41, 0x00001e40, 0x00001e40, + 0x00001e43, 0x00001e42, 0x00001e42, + 0x00001e45, 0x00001e44, 0x00001e44, + 0x00001e47, 0x00001e46, 0x00001e46, + 0x00001e49, 0x00001e48, 0x00001e48, + 0x00001e4b, 0x00001e4a, 0x00001e4a, + 0x00001e4d, 0x00001e4c, 0x00001e4c, + 0x00001e4f, 0x00001e4e, 0x00001e4e, + 0x00001e51, 0x00001e50, 0x00001e50, + 0x00001e53, 0x00001e52, 0x00001e52, + 0x00001e55, 0x00001e54, 0x00001e54, + 0x00001e57, 0x00001e56, 0x00001e56, + 0x00001e59, 0x00001e58, 0x00001e58, + 0x00001e5b, 0x00001e5a, 0x00001e5a, + 0x00001e5d, 0x00001e5c, 0x00001e5c, + 0x00001e5f, 0x00001e5e, 0x00001e5e, + 0x00001e61, 0x00001e60, 0x00001e60, + 0x00001e63, 0x00001e62, 0x00001e62, + 0x00001e65, 0x00001e64, 0x00001e64, + 0x00001e67, 0x00001e66, 0x00001e66, + 0x00001e69, 0x00001e68, 0x00001e68, + 0x00001e6b, 0x00001e6a, 0x00001e6a, + 0x00001e6d, 0x00001e6c, 0x00001e6c, + 0x00001e6f, 0x00001e6e, 0x00001e6e, + 0x00001e71, 0x00001e70, 0x00001e70, + 0x00001e73, 0x00001e72, 0x00001e72, + 0x00001e75, 0x00001e74, 0x00001e74, + 0x00001e77, 0x00001e76, 0x00001e76, + 0x00001e79, 0x00001e78, 0x00001e78, + 0x00001e7b, 0x00001e7a, 0x00001e7a, + 0x00001e7d, 0x00001e7c, 0x00001e7c, + 0x00001e7f, 0x00001e7e, 0x00001e7e, + 0x00001e81, 0x00001e80, 0x00001e80, + 0x00001e83, 0x00001e82, 0x00001e82, + 0x00001e85, 0x00001e84, 0x00001e84, + 0x00001e87, 0x00001e86, 0x00001e86, + 0x00001e89, 0x00001e88, 0x00001e88, + 0x00001e8b, 0x00001e8a, 0x00001e8a, + 0x00001e8d, 0x00001e8c, 0x00001e8c, + 0x00001e8f, 0x00001e8e, 0x00001e8e, + 0x00001e91, 0x00001e90, 0x00001e90, + 0x00001e93, 0x00001e92, 0x00001e92, + 0x00001e95, 0x00001e94, 0x00001e94, + 0x00001e9b, 0x00001e60, 0x00001e60, + 0x00001ea1, 0x00001ea0, 0x00001ea0, + 0x00001ea3, 0x00001ea2, 0x00001ea2, + 0x00001ea5, 0x00001ea4, 0x00001ea4, + 0x00001ea7, 0x00001ea6, 0x00001ea6, + 0x00001ea9, 0x00001ea8, 0x00001ea8, + 0x00001eab, 0x00001eaa, 0x00001eaa, + 0x00001ead, 0x00001eac, 0x00001eac, + 0x00001eaf, 0x00001eae, 0x00001eae, + 0x00001eb1, 0x00001eb0, 0x00001eb0, + 0x00001eb3, 0x00001eb2, 0x00001eb2, + 0x00001eb5, 0x00001eb4, 0x00001eb4, + 0x00001eb7, 0x00001eb6, 0x00001eb6, + 0x00001eb9, 0x00001eb8, 0x00001eb8, + 0x00001ebb, 0x00001eba, 0x00001eba, + 0x00001ebd, 0x00001ebc, 0x00001ebc, + 0x00001ebf, 0x00001ebe, 0x00001ebe, + 0x00001ec1, 0x00001ec0, 0x00001ec0, + 0x00001ec3, 0x00001ec2, 0x00001ec2, + 0x00001ec5, 0x00001ec4, 0x00001ec4, + 0x00001ec7, 0x00001ec6, 0x00001ec6, + 0x00001ec9, 0x00001ec8, 0x00001ec8, + 0x00001ecb, 0x00001eca, 0x00001eca, + 0x00001ecd, 0x00001ecc, 0x00001ecc, + 0x00001ecf, 0x00001ece, 0x00001ece, + 0x00001ed1, 0x00001ed0, 0x00001ed0, + 0x00001ed3, 0x00001ed2, 0x00001ed2, + 0x00001ed5, 0x00001ed4, 0x00001ed4, + 0x00001ed7, 0x00001ed6, 0x00001ed6, + 0x00001ed9, 0x00001ed8, 0x00001ed8, + 0x00001edb, 0x00001eda, 0x00001eda, + 0x00001edd, 0x00001edc, 0x00001edc, + 0x00001edf, 0x00001ede, 0x00001ede, + 0x00001ee1, 0x00001ee0, 0x00001ee0, + 0x00001ee3, 0x00001ee2, 0x00001ee2, + 0x00001ee5, 0x00001ee4, 0x00001ee4, + 0x00001ee7, 0x00001ee6, 0x00001ee6, + 0x00001ee9, 0x00001ee8, 0x00001ee8, + 0x00001eeb, 0x00001eea, 0x00001eea, + 0x00001eed, 0x00001eec, 0x00001eec, + 0x00001eef, 0x00001eee, 0x00001eee, + 0x00001ef1, 0x00001ef0, 0x00001ef0, + 0x00001ef3, 0x00001ef2, 0x00001ef2, + 0x00001ef5, 0x00001ef4, 0x00001ef4, + 0x00001ef7, 0x00001ef6, 0x00001ef6, + 0x00001ef9, 0x00001ef8, 0x00001ef8, + 0x00001f00, 0x00001f08, 0x00001f08, + 0x00001f01, 0x00001f09, 0x00001f09, + 0x00001f02, 0x00001f0a, 0x00001f0a, + 0x00001f03, 0x00001f0b, 0x00001f0b, + 0x00001f04, 0x00001f0c, 0x00001f0c, + 0x00001f05, 0x00001f0d, 0x00001f0d, + 0x00001f06, 0x00001f0e, 0x00001f0e, + 0x00001f07, 0x00001f0f, 0x00001f0f, + 0x00001f10, 0x00001f18, 0x00001f18, + 0x00001f11, 0x00001f19, 0x00001f19, + 0x00001f12, 0x00001f1a, 0x00001f1a, + 0x00001f13, 0x00001f1b, 0x00001f1b, + 0x00001f14, 0x00001f1c, 0x00001f1c, + 0x00001f15, 0x00001f1d, 0x00001f1d, + 0x00001f20, 0x00001f28, 0x00001f28, + 0x00001f21, 0x00001f29, 0x00001f29, + 0x00001f22, 0x00001f2a, 0x00001f2a, + 0x00001f23, 0x00001f2b, 0x00001f2b, + 0x00001f24, 0x00001f2c, 0x00001f2c, + 0x00001f25, 0x00001f2d, 0x00001f2d, + 0x00001f26, 0x00001f2e, 0x00001f2e, + 0x00001f27, 0x00001f2f, 0x00001f2f, + 0x00001f30, 0x00001f38, 0x00001f38, + 0x00001f31, 0x00001f39, 0x00001f39, + 0x00001f32, 0x00001f3a, 0x00001f3a, + 0x00001f33, 0x00001f3b, 0x00001f3b, + 0x00001f34, 0x00001f3c, 0x00001f3c, + 0x00001f35, 0x00001f3d, 0x00001f3d, + 0x00001f36, 0x00001f3e, 0x00001f3e, + 0x00001f37, 0x00001f3f, 0x00001f3f, + 0x00001f40, 0x00001f48, 0x00001f48, + 0x00001f41, 0x00001f49, 0x00001f49, + 0x00001f42, 0x00001f4a, 0x00001f4a, + 0x00001f43, 0x00001f4b, 0x00001f4b, + 0x00001f44, 0x00001f4c, 0x00001f4c, + 0x00001f45, 0x00001f4d, 0x00001f4d, + 0x00001f51, 0x00001f59, 0x00001f59, + 0x00001f53, 0x00001f5b, 0x00001f5b, + 0x00001f55, 0x00001f5d, 0x00001f5d, + 0x00001f57, 0x00001f5f, 0x00001f5f, + 0x00001f60, 0x00001f68, 0x00001f68, + 0x00001f61, 0x00001f69, 0x00001f69, + 0x00001f62, 0x00001f6a, 0x00001f6a, + 0x00001f63, 0x00001f6b, 0x00001f6b, + 0x00001f64, 0x00001f6c, 0x00001f6c, + 0x00001f65, 0x00001f6d, 0x00001f6d, + 0x00001f66, 0x00001f6e, 0x00001f6e, + 0x00001f67, 0x00001f6f, 0x00001f6f, + 0x00001f70, 0x00001fba, 0x00001fba, + 0x00001f71, 0x00001fbb, 0x00001fbb, + 0x00001f72, 0x00001fc8, 0x00001fc8, + 0x00001f73, 0x00001fc9, 0x00001fc9, + 0x00001f74, 0x00001fca, 0x00001fca, + 0x00001f75, 0x00001fcb, 0x00001fcb, + 0x00001f76, 0x00001fda, 0x00001fda, + 0x00001f77, 0x00001fdb, 0x00001fdb, + 0x00001f78, 0x00001ff8, 0x00001ff8, + 0x00001f79, 0x00001ff9, 0x00001ff9, + 0x00001f7a, 0x00001fea, 0x00001fea, + 0x00001f7b, 0x00001feb, 0x00001feb, + 0x00001f7c, 0x00001ffa, 0x00001ffa, + 0x00001f7d, 0x00001ffb, 0x00001ffb, + 0x00001f80, 0x00001f88, 0x00001f88, + 0x00001f81, 0x00001f89, 0x00001f89, + 0x00001f82, 0x00001f8a, 0x00001f8a, + 0x00001f83, 0x00001f8b, 0x00001f8b, + 0x00001f84, 0x00001f8c, 0x00001f8c, + 0x00001f85, 0x00001f8d, 0x00001f8d, + 0x00001f86, 0x00001f8e, 0x00001f8e, + 0x00001f87, 0x00001f8f, 0x00001f8f, + 0x00001f90, 0x00001f98, 0x00001f98, + 0x00001f91, 0x00001f99, 0x00001f99, + 0x00001f92, 0x00001f9a, 0x00001f9a, + 0x00001f93, 0x00001f9b, 0x00001f9b, + 0x00001f94, 0x00001f9c, 0x00001f9c, + 0x00001f95, 0x00001f9d, 0x00001f9d, + 0x00001f96, 0x00001f9e, 0x00001f9e, + 0x00001f97, 0x00001f9f, 0x00001f9f, + 0x00001fa0, 0x00001fa8, 0x00001fa8, + 0x00001fa1, 0x00001fa9, 0x00001fa9, + 0x00001fa2, 0x00001faa, 0x00001faa, + 0x00001fa3, 0x00001fab, 0x00001fab, + 0x00001fa4, 0x00001fac, 0x00001fac, + 0x00001fa5, 0x00001fad, 0x00001fad, + 0x00001fa6, 0x00001fae, 0x00001fae, + 0x00001fa7, 0x00001faf, 0x00001faf, + 0x00001fb0, 0x00001fb8, 0x00001fb8, + 0x00001fb1, 0x00001fb9, 0x00001fb9, + 0x00001fb3, 0x00001fbc, 0x00001fbc, + 0x00001fbe, 0x00000399, 0x00000399, + 0x00001fc3, 0x00001fcc, 0x00001fcc, + 0x00001fd0, 0x00001fd8, 0x00001fd8, + 0x00001fd1, 0x00001fd9, 0x00001fd9, + 0x00001fe0, 0x00001fe8, 0x00001fe8, + 0x00001fe1, 0x00001fe9, 0x00001fe9, + 0x00001fe5, 0x00001fec, 0x00001fec, + 0x00001ff3, 0x00001ffc, 0x00001ffc, + 0x00002170, 0x00002160, 0x00002160, + 0x00002171, 0x00002161, 0x00002161, + 0x00002172, 0x00002162, 0x00002162, + 0x00002173, 0x00002163, 0x00002163, + 0x00002174, 0x00002164, 0x00002164, + 0x00002175, 0x00002165, 0x00002165, + 0x00002176, 0x00002166, 0x00002166, + 0x00002177, 0x00002167, 0x00002167, + 0x00002178, 0x00002168, 0x00002168, + 0x00002179, 0x00002169, 0x00002169, + 0x0000217a, 0x0000216a, 0x0000216a, + 0x0000217b, 0x0000216b, 0x0000216b, + 0x0000217c, 0x0000216c, 0x0000216c, + 0x0000217d, 0x0000216d, 0x0000216d, + 0x0000217e, 0x0000216e, 0x0000216e, + 0x0000217f, 0x0000216f, 0x0000216f, + 0x000024d0, 0x000024b6, 0x000024b6, + 0x000024d1, 0x000024b7, 0x000024b7, + 0x000024d2, 0x000024b8, 0x000024b8, + 0x000024d3, 0x000024b9, 0x000024b9, + 0x000024d4, 0x000024ba, 0x000024ba, + 0x000024d5, 0x000024bb, 0x000024bb, + 0x000024d6, 0x000024bc, 0x000024bc, + 0x000024d7, 0x000024bd, 0x000024bd, + 0x000024d8, 0x000024be, 0x000024be, + 0x000024d9, 0x000024bf, 0x000024bf, + 0x000024da, 0x000024c0, 0x000024c0, + 0x000024db, 0x000024c1, 0x000024c1, + 0x000024dc, 0x000024c2, 0x000024c2, + 0x000024dd, 0x000024c3, 0x000024c3, + 0x000024de, 0x000024c4, 0x000024c4, + 0x000024df, 0x000024c5, 0x000024c5, + 0x000024e0, 0x000024c6, 0x000024c6, + 0x000024e1, 0x000024c7, 0x000024c7, + 0x000024e2, 0x000024c8, 0x000024c8, + 0x000024e3, 0x000024c9, 0x000024c9, + 0x000024e4, 0x000024ca, 0x000024ca, + 0x000024e5, 0x000024cb, 0x000024cb, + 0x000024e6, 0x000024cc, 0x000024cc, + 0x000024e7, 0x000024cd, 0x000024cd, + 0x000024e8, 0x000024ce, 0x000024ce, + 0x000024e9, 0x000024cf, 0x000024cf, + 0x0000ff41, 0x0000ff21, 0x0000ff21, + 0x0000ff42, 0x0000ff22, 0x0000ff22, + 0x0000ff43, 0x0000ff23, 0x0000ff23, + 0x0000ff44, 0x0000ff24, 0x0000ff24, + 0x0000ff45, 0x0000ff25, 0x0000ff25, + 0x0000ff46, 0x0000ff26, 0x0000ff26, + 0x0000ff47, 0x0000ff27, 0x0000ff27, + 0x0000ff48, 0x0000ff28, 0x0000ff28, + 0x0000ff49, 0x0000ff29, 0x0000ff29, + 0x0000ff4a, 0x0000ff2a, 0x0000ff2a, + 0x0000ff4b, 0x0000ff2b, 0x0000ff2b, + 0x0000ff4c, 0x0000ff2c, 0x0000ff2c, + 0x0000ff4d, 0x0000ff2d, 0x0000ff2d, + 0x0000ff4e, 0x0000ff2e, 0x0000ff2e, + 0x0000ff4f, 0x0000ff2f, 0x0000ff2f, + 0x0000ff50, 0x0000ff30, 0x0000ff30, + 0x0000ff51, 0x0000ff31, 0x0000ff31, + 0x0000ff52, 0x0000ff32, 0x0000ff32, + 0x0000ff53, 0x0000ff33, 0x0000ff33, + 0x0000ff54, 0x0000ff34, 0x0000ff34, + 0x0000ff55, 0x0000ff35, 0x0000ff35, + 0x0000ff56, 0x0000ff36, 0x0000ff36, + 0x0000ff57, 0x0000ff37, 0x0000ff37, + 0x0000ff58, 0x0000ff38, 0x0000ff38, + 0x0000ff59, 0x0000ff39, 0x0000ff39, + 0x0000ff5a, 0x0000ff3a, 0x0000ff3a, + 0x00010428, 0x00010400, 0x00010400, + 0x00010429, 0x00010401, 0x00010401, + 0x0001042a, 0x00010402, 0x00010402, + 0x0001042b, 0x00010403, 0x00010403, + 0x0001042c, 0x00010404, 0x00010404, + 0x0001042d, 0x00010405, 0x00010405, + 0x0001042e, 0x00010406, 0x00010406, + 0x0001042f, 0x00010407, 0x00010407, + 0x00010430, 0x00010408, 0x00010408, + 0x00010431, 0x00010409, 0x00010409, + 0x00010432, 0x0001040a, 0x0001040a, + 0x00010433, 0x0001040b, 0x0001040b, + 0x00010434, 0x0001040c, 0x0001040c, + 0x00010435, 0x0001040d, 0x0001040d, + 0x00010436, 0x0001040e, 0x0001040e, + 0x00010437, 0x0001040f, 0x0001040f, + 0x00010438, 0x00010410, 0x00010410, + 0x00010439, 0x00010411, 0x00010411, + 0x0001043a, 0x00010412, 0x00010412, + 0x0001043b, 0x00010413, 0x00010413, + 0x0001043c, 0x00010414, 0x00010414, + 0x0001043d, 0x00010415, 0x00010415, + 0x0001043e, 0x00010416, 0x00010416, + 0x0001043f, 0x00010417, 0x00010417, + 0x00010440, 0x00010418, 0x00010418, + 0x00010441, 0x00010419, 0x00010419, + 0x00010442, 0x0001041a, 0x0001041a, + 0x00010443, 0x0001041b, 0x0001041b, + 0x00010444, 0x0001041c, 0x0001041c, + 0x00010445, 0x0001041d, 0x0001041d, + 0x00010446, 0x0001041e, 0x0001041e, + 0x00010447, 0x0001041f, 0x0001041f, + 0x00010448, 0x00010420, 0x00010420, + 0x00010449, 0x00010421, 0x00010421, + 0x0001044a, 0x00010422, 0x00010422, + 0x0001044b, 0x00010423, 0x00010423, + 0x0001044c, 0x00010424, 0x00010424, + 0x0001044d, 0x00010425, 0x00010425, + 0x000001c5, 0x000001c4, 0x000001c6, + 0x000001c8, 0x000001c7, 0x000001c9, + 0x000001cb, 0x000001ca, 0x000001cc, + 0x000001f2, 0x000001f1, 0x000001f3, + 0x00001f88, 0x00001f88, 0x00001f80, + 0x00001f89, 0x00001f89, 0x00001f81, + 0x00001f8a, 0x00001f8a, 0x00001f82, + 0x00001f8b, 0x00001f8b, 0x00001f83, + 0x00001f8c, 0x00001f8c, 0x00001f84, + 0x00001f8d, 0x00001f8d, 0x00001f85, + 0x00001f8e, 0x00001f8e, 0x00001f86, + 0x00001f8f, 0x00001f8f, 0x00001f87, + 0x00001f98, 0x00001f98, 0x00001f90, + 0x00001f99, 0x00001f99, 0x00001f91, + 0x00001f9a, 0x00001f9a, 0x00001f92, + 0x00001f9b, 0x00001f9b, 0x00001f93, + 0x00001f9c, 0x00001f9c, 0x00001f94, + 0x00001f9d, 0x00001f9d, 0x00001f95, + 0x00001f9e, 0x00001f9e, 0x00001f96, + 0x00001f9f, 0x00001f9f, 0x00001f97, + 0x00001fa8, 0x00001fa8, 0x00001fa0, + 0x00001fa9, 0x00001fa9, 0x00001fa1, + 0x00001faa, 0x00001faa, 0x00001fa2, + 0x00001fab, 0x00001fab, 0x00001fa3, + 0x00001fac, 0x00001fac, 0x00001fa4, + 0x00001fad, 0x00001fad, 0x00001fa5, + 0x00001fae, 0x00001fae, 0x00001fa6, + 0x00001faf, 0x00001faf, 0x00001fa7, + 0x00001fbc, 0x00001fbc, 0x00001fb3, + 0x00001fcc, 0x00001fcc, 0x00001fc3, + 0x00001ffc, 0x00001ffc, 0x00001ff3 +}; + +static const ac_uint4 _uccomp_size = 3684; + +static const ac_uint4 _uccomp_data[] = { + 0x0000226e, 0x00000002, 0x0000003c, 0x00000338, + 0x00002260, 0x00000002, 0x0000003d, 0x00000338, + 0x0000226f, 0x00000002, 0x0000003e, 0x00000338, + 0x000000c0, 0x00000002, 0x00000041, 0x00000300, + 0x000000c1, 0x00000002, 0x00000041, 0x00000301, + 0x000000c2, 0x00000002, 0x00000041, 0x00000302, + 0x000000c3, 0x00000002, 0x00000041, 0x00000303, + 0x00000100, 0x00000002, 0x00000041, 0x00000304, + 0x00000102, 0x00000002, 0x00000041, 0x00000306, + 0x00000226, 0x00000002, 0x00000041, 0x00000307, + 0x000000c4, 0x00000002, 0x00000041, 0x00000308, + 0x00001ea2, 0x00000002, 0x00000041, 0x00000309, + 0x000000c5, 0x00000002, 0x00000041, 0x0000030a, + 0x000001cd, 0x00000002, 0x00000041, 0x0000030c, + 0x00000200, 0x00000002, 0x00000041, 0x0000030f, + 0x00000202, 0x00000002, 0x00000041, 0x00000311, + 0x00001ea0, 0x00000002, 0x00000041, 0x00000323, + 0x00001e00, 0x00000002, 0x00000041, 0x00000325, + 0x00000104, 0x00000002, 0x00000041, 0x00000328, + 0x00001e02, 0x00000002, 0x00000042, 0x00000307, + 0x00001e04, 0x00000002, 0x00000042, 0x00000323, + 0x00001e06, 0x00000002, 0x00000042, 0x00000331, + 0x00000106, 0x00000002, 0x00000043, 0x00000301, + 0x00000108, 0x00000002, 0x00000043, 0x00000302, + 0x0000010a, 0x00000002, 0x00000043, 0x00000307, + 0x0000010c, 0x00000002, 0x00000043, 0x0000030c, + 0x000000c7, 0x00000002, 0x00000043, 0x00000327, + 0x00001e0a, 0x00000002, 0x00000044, 0x00000307, + 0x0000010e, 0x00000002, 0x00000044, 0x0000030c, + 0x00001e0c, 0x00000002, 0x00000044, 0x00000323, + 0x00001e10, 0x00000002, 0x00000044, 0x00000327, + 0x00001e12, 0x00000002, 0x00000044, 0x0000032d, + 0x00001e0e, 0x00000002, 0x00000044, 0x00000331, + 0x000000c8, 0x00000002, 0x00000045, 0x00000300, + 0x000000c9, 0x00000002, 0x00000045, 0x00000301, + 0x000000ca, 0x00000002, 0x00000045, 0x00000302, + 0x00001ebc, 0x00000002, 0x00000045, 0x00000303, + 0x00000112, 0x00000002, 0x00000045, 0x00000304, + 0x00000114, 0x00000002, 0x00000045, 0x00000306, + 0x00000116, 0x00000002, 0x00000045, 0x00000307, + 0x000000cb, 0x00000002, 0x00000045, 0x00000308, + 0x00001eba, 0x00000002, 0x00000045, 0x00000309, + 0x0000011a, 0x00000002, 0x00000045, 0x0000030c, + 0x00000204, 0x00000002, 0x00000045, 0x0000030f, + 0x00000206, 0x00000002, 0x00000045, 0x00000311, + 0x00001eb8, 0x00000002, 0x00000045, 0x00000323, + 0x00000228, 0x00000002, 0x00000045, 0x00000327, + 0x00000118, 0x00000002, 0x00000045, 0x00000328, + 0x00001e18, 0x00000002, 0x00000045, 0x0000032d, + 0x00001e1a, 0x00000002, 0x00000045, 0x00000330, + 0x00001e1e, 0x00000002, 0x00000046, 0x00000307, + 0x000001f4, 0x00000002, 0x00000047, 0x00000301, + 0x0000011c, 0x00000002, 0x00000047, 0x00000302, + 0x00001e20, 0x00000002, 0x00000047, 0x00000304, + 0x0000011e, 0x00000002, 0x00000047, 0x00000306, + 0x00000120, 0x00000002, 0x00000047, 0x00000307, + 0x000001e6, 0x00000002, 0x00000047, 0x0000030c, + 0x00000122, 0x00000002, 0x00000047, 0x00000327, + 0x00000124, 0x00000002, 0x00000048, 0x00000302, + 0x00001e22, 0x00000002, 0x00000048, 0x00000307, + 0x00001e26, 0x00000002, 0x00000048, 0x00000308, + 0x0000021e, 0x00000002, 0x00000048, 0x0000030c, + 0x00001e24, 0x00000002, 0x00000048, 0x00000323, + 0x00001e28, 0x00000002, 0x00000048, 0x00000327, + 0x00001e2a, 0x00000002, 0x00000048, 0x0000032e, + 0x000000cc, 0x00000002, 0x00000049, 0x00000300, + 0x000000cd, 0x00000002, 0x00000049, 0x00000301, + 0x000000ce, 0x00000002, 0x00000049, 0x00000302, + 0x00000128, 0x00000002, 0x00000049, 0x00000303, + 0x0000012a, 0x00000002, 0x00000049, 0x00000304, + 0x0000012c, 0x00000002, 0x00000049, 0x00000306, + 0x00000130, 0x00000002, 0x00000049, 0x00000307, + 0x000000cf, 0x00000002, 0x00000049, 0x00000308, + 0x00001ec8, 0x00000002, 0x00000049, 0x00000309, + 0x000001cf, 0x00000002, 0x00000049, 0x0000030c, + 0x00000208, 0x00000002, 0x00000049, 0x0000030f, + 0x0000020a, 0x00000002, 0x00000049, 0x00000311, + 0x00001eca, 0x00000002, 0x00000049, 0x00000323, + 0x0000012e, 0x00000002, 0x00000049, 0x00000328, + 0x00001e2c, 0x00000002, 0x00000049, 0x00000330, + 0x00000134, 0x00000002, 0x0000004a, 0x00000302, + 0x00001e30, 0x00000002, 0x0000004b, 0x00000301, + 0x000001e8, 0x00000002, 0x0000004b, 0x0000030c, + 0x00001e32, 0x00000002, 0x0000004b, 0x00000323, + 0x00000136, 0x00000002, 0x0000004b, 0x00000327, + 0x00001e34, 0x00000002, 0x0000004b, 0x00000331, + 0x00000139, 0x00000002, 0x0000004c, 0x00000301, + 0x0000013d, 0x00000002, 0x0000004c, 0x0000030c, + 0x00001e36, 0x00000002, 0x0000004c, 0x00000323, + 0x0000013b, 0x00000002, 0x0000004c, 0x00000327, + 0x00001e3c, 0x00000002, 0x0000004c, 0x0000032d, + 0x00001e3a, 0x00000002, 0x0000004c, 0x00000331, + 0x00001e3e, 0x00000002, 0x0000004d, 0x00000301, + 0x00001e40, 0x00000002, 0x0000004d, 0x00000307, + 0x00001e42, 0x00000002, 0x0000004d, 0x00000323, + 0x000001f8, 0x00000002, 0x0000004e, 0x00000300, + 0x00000143, 0x00000002, 0x0000004e, 0x00000301, + 0x000000d1, 0x00000002, 0x0000004e, 0x00000303, + 0x00001e44, 0x00000002, 0x0000004e, 0x00000307, + 0x00000147, 0x00000002, 0x0000004e, 0x0000030c, + 0x00001e46, 0x00000002, 0x0000004e, 0x00000323, + 0x00000145, 0x00000002, 0x0000004e, 0x00000327, + 0x00001e4a, 0x00000002, 0x0000004e, 0x0000032d, + 0x00001e48, 0x00000002, 0x0000004e, 0x00000331, + 0x000000d2, 0x00000002, 0x0000004f, 0x00000300, + 0x000000d3, 0x00000002, 0x0000004f, 0x00000301, + 0x000000d4, 0x00000002, 0x0000004f, 0x00000302, + 0x000000d5, 0x00000002, 0x0000004f, 0x00000303, + 0x0000014c, 0x00000002, 0x0000004f, 0x00000304, + 0x0000014e, 0x00000002, 0x0000004f, 0x00000306, + 0x0000022e, 0x00000002, 0x0000004f, 0x00000307, + 0x000000d6, 0x00000002, 0x0000004f, 0x00000308, + 0x00001ece, 0x00000002, 0x0000004f, 0x00000309, + 0x00000150, 0x00000002, 0x0000004f, 0x0000030b, + 0x000001d1, 0x00000002, 0x0000004f, 0x0000030c, + 0x0000020c, 0x00000002, 0x0000004f, 0x0000030f, + 0x0000020e, 0x00000002, 0x0000004f, 0x00000311, + 0x000001a0, 0x00000002, 0x0000004f, 0x0000031b, + 0x00001ecc, 0x00000002, 0x0000004f, 0x00000323, + 0x000001ea, 0x00000002, 0x0000004f, 0x00000328, + 0x00001e54, 0x00000002, 0x00000050, 0x00000301, + 0x00001e56, 0x00000002, 0x00000050, 0x00000307, + 0x00000154, 0x00000002, 0x00000052, 0x00000301, + 0x00001e58, 0x00000002, 0x00000052, 0x00000307, + 0x00000158, 0x00000002, 0x00000052, 0x0000030c, + 0x00000210, 0x00000002, 0x00000052, 0x0000030f, + 0x00000212, 0x00000002, 0x00000052, 0x00000311, + 0x00001e5a, 0x00000002, 0x00000052, 0x00000323, + 0x00000156, 0x00000002, 0x00000052, 0x00000327, + 0x00001e5e, 0x00000002, 0x00000052, 0x00000331, + 0x0000015a, 0x00000002, 0x00000053, 0x00000301, + 0x0000015c, 0x00000002, 0x00000053, 0x00000302, + 0x00001e60, 0x00000002, 0x00000053, 0x00000307, + 0x00000160, 0x00000002, 0x00000053, 0x0000030c, + 0x00001e62, 0x00000002, 0x00000053, 0x00000323, + 0x00000218, 0x00000002, 0x00000053, 0x00000326, + 0x0000015e, 0x00000002, 0x00000053, 0x00000327, + 0x00001e6a, 0x00000002, 0x00000054, 0x00000307, + 0x00000164, 0x00000002, 0x00000054, 0x0000030c, + 0x00001e6c, 0x00000002, 0x00000054, 0x00000323, + 0x0000021a, 0x00000002, 0x00000054, 0x00000326, + 0x00000162, 0x00000002, 0x00000054, 0x00000327, + 0x00001e70, 0x00000002, 0x00000054, 0x0000032d, + 0x00001e6e, 0x00000002, 0x00000054, 0x00000331, + 0x000000d9, 0x00000002, 0x00000055, 0x00000300, + 0x000000da, 0x00000002, 0x00000055, 0x00000301, + 0x000000db, 0x00000002, 0x00000055, 0x00000302, + 0x00000168, 0x00000002, 0x00000055, 0x00000303, + 0x0000016a, 0x00000002, 0x00000055, 0x00000304, + 0x0000016c, 0x00000002, 0x00000055, 0x00000306, + 0x000000dc, 0x00000002, 0x00000055, 0x00000308, + 0x00001ee6, 0x00000002, 0x00000055, 0x00000309, + 0x0000016e, 0x00000002, 0x00000055, 0x0000030a, + 0x00000170, 0x00000002, 0x00000055, 0x0000030b, + 0x000001d3, 0x00000002, 0x00000055, 0x0000030c, + 0x00000214, 0x00000002, 0x00000055, 0x0000030f, + 0x00000216, 0x00000002, 0x00000055, 0x00000311, + 0x000001af, 0x00000002, 0x00000055, 0x0000031b, + 0x00001ee4, 0x00000002, 0x00000055, 0x00000323, + 0x00001e72, 0x00000002, 0x00000055, 0x00000324, + 0x00000172, 0x00000002, 0x00000055, 0x00000328, + 0x00001e76, 0x00000002, 0x00000055, 0x0000032d, + 0x00001e74, 0x00000002, 0x00000055, 0x00000330, + 0x00001e7c, 0x00000002, 0x00000056, 0x00000303, + 0x00001e7e, 0x00000002, 0x00000056, 0x00000323, + 0x00001e80, 0x00000002, 0x00000057, 0x00000300, + 0x00001e82, 0x00000002, 0x00000057, 0x00000301, + 0x00000174, 0x00000002, 0x00000057, 0x00000302, + 0x00001e86, 0x00000002, 0x00000057, 0x00000307, + 0x00001e84, 0x00000002, 0x00000057, 0x00000308, + 0x00001e88, 0x00000002, 0x00000057, 0x00000323, + 0x00001e8a, 0x00000002, 0x00000058, 0x00000307, + 0x00001e8c, 0x00000002, 0x00000058, 0x00000308, + 0x00001ef2, 0x00000002, 0x00000059, 0x00000300, + 0x000000dd, 0x00000002, 0x00000059, 0x00000301, + 0x00000176, 0x00000002, 0x00000059, 0x00000302, + 0x00001ef8, 0x00000002, 0x00000059, 0x00000303, + 0x00000232, 0x00000002, 0x00000059, 0x00000304, + 0x00001e8e, 0x00000002, 0x00000059, 0x00000307, + 0x00000178, 0x00000002, 0x00000059, 0x00000308, + 0x00001ef6, 0x00000002, 0x00000059, 0x00000309, + 0x00001ef4, 0x00000002, 0x00000059, 0x00000323, + 0x00000179, 0x00000002, 0x0000005a, 0x00000301, + 0x00001e90, 0x00000002, 0x0000005a, 0x00000302, + 0x0000017b, 0x00000002, 0x0000005a, 0x00000307, + 0x0000017d, 0x00000002, 0x0000005a, 0x0000030c, + 0x00001e92, 0x00000002, 0x0000005a, 0x00000323, + 0x00001e94, 0x00000002, 0x0000005a, 0x00000331, + 0x000000e0, 0x00000002, 0x00000061, 0x00000300, + 0x000000e1, 0x00000002, 0x00000061, 0x00000301, + 0x000000e2, 0x00000002, 0x00000061, 0x00000302, + 0x000000e3, 0x00000002, 0x00000061, 0x00000303, + 0x00000101, 0x00000002, 0x00000061, 0x00000304, + 0x00000103, 0x00000002, 0x00000061, 0x00000306, + 0x00000227, 0x00000002, 0x00000061, 0x00000307, + 0x000000e4, 0x00000002, 0x00000061, 0x00000308, + 0x00001ea3, 0x00000002, 0x00000061, 0x00000309, + 0x000000e5, 0x00000002, 0x00000061, 0x0000030a, + 0x000001ce, 0x00000002, 0x00000061, 0x0000030c, + 0x00000201, 0x00000002, 0x00000061, 0x0000030f, + 0x00000203, 0x00000002, 0x00000061, 0x00000311, + 0x00001ea1, 0x00000002, 0x00000061, 0x00000323, + 0x00001e01, 0x00000002, 0x00000061, 0x00000325, + 0x00000105, 0x00000002, 0x00000061, 0x00000328, + 0x00001e03, 0x00000002, 0x00000062, 0x00000307, + 0x00001e05, 0x00000002, 0x00000062, 0x00000323, + 0x00001e07, 0x00000002, 0x00000062, 0x00000331, + 0x00000107, 0x00000002, 0x00000063, 0x00000301, + 0x00000109, 0x00000002, 0x00000063, 0x00000302, + 0x0000010b, 0x00000002, 0x00000063, 0x00000307, + 0x0000010d, 0x00000002, 0x00000063, 0x0000030c, + 0x000000e7, 0x00000002, 0x00000063, 0x00000327, + 0x00001e0b, 0x00000002, 0x00000064, 0x00000307, + 0x0000010f, 0x00000002, 0x00000064, 0x0000030c, + 0x00001e0d, 0x00000002, 0x00000064, 0x00000323, + 0x00001e11, 0x00000002, 0x00000064, 0x00000327, + 0x00001e13, 0x00000002, 0x00000064, 0x0000032d, + 0x00001e0f, 0x00000002, 0x00000064, 0x00000331, + 0x000000e8, 0x00000002, 0x00000065, 0x00000300, + 0x000000e9, 0x00000002, 0x00000065, 0x00000301, + 0x000000ea, 0x00000002, 0x00000065, 0x00000302, + 0x00001ebd, 0x00000002, 0x00000065, 0x00000303, + 0x00000113, 0x00000002, 0x00000065, 0x00000304, + 0x00000115, 0x00000002, 0x00000065, 0x00000306, + 0x00000117, 0x00000002, 0x00000065, 0x00000307, + 0x000000eb, 0x00000002, 0x00000065, 0x00000308, + 0x00001ebb, 0x00000002, 0x00000065, 0x00000309, + 0x0000011b, 0x00000002, 0x00000065, 0x0000030c, + 0x00000205, 0x00000002, 0x00000065, 0x0000030f, + 0x00000207, 0x00000002, 0x00000065, 0x00000311, + 0x00001eb9, 0x00000002, 0x00000065, 0x00000323, + 0x00000229, 0x00000002, 0x00000065, 0x00000327, + 0x00000119, 0x00000002, 0x00000065, 0x00000328, + 0x00001e19, 0x00000002, 0x00000065, 0x0000032d, + 0x00001e1b, 0x00000002, 0x00000065, 0x00000330, + 0x00001e1f, 0x00000002, 0x00000066, 0x00000307, + 0x000001f5, 0x00000002, 0x00000067, 0x00000301, + 0x0000011d, 0x00000002, 0x00000067, 0x00000302, + 0x00001e21, 0x00000002, 0x00000067, 0x00000304, + 0x0000011f, 0x00000002, 0x00000067, 0x00000306, + 0x00000121, 0x00000002, 0x00000067, 0x00000307, + 0x000001e7, 0x00000002, 0x00000067, 0x0000030c, + 0x00000123, 0x00000002, 0x00000067, 0x00000327, + 0x00000125, 0x00000002, 0x00000068, 0x00000302, + 0x00001e23, 0x00000002, 0x00000068, 0x00000307, + 0x00001e27, 0x00000002, 0x00000068, 0x00000308, + 0x0000021f, 0x00000002, 0x00000068, 0x0000030c, + 0x00001e25, 0x00000002, 0x00000068, 0x00000323, + 0x00001e29, 0x00000002, 0x00000068, 0x00000327, + 0x00001e2b, 0x00000002, 0x00000068, 0x0000032e, + 0x00001e96, 0x00000002, 0x00000068, 0x00000331, + 0x000000ec, 0x00000002, 0x00000069, 0x00000300, + 0x000000ed, 0x00000002, 0x00000069, 0x00000301, + 0x000000ee, 0x00000002, 0x00000069, 0x00000302, + 0x00000129, 0x00000002, 0x00000069, 0x00000303, + 0x0000012b, 0x00000002, 0x00000069, 0x00000304, + 0x0000012d, 0x00000002, 0x00000069, 0x00000306, + 0x000000ef, 0x00000002, 0x00000069, 0x00000308, + 0x00001ec9, 0x00000002, 0x00000069, 0x00000309, + 0x000001d0, 0x00000002, 0x00000069, 0x0000030c, + 0x00000209, 0x00000002, 0x00000069, 0x0000030f, + 0x0000020b, 0x00000002, 0x00000069, 0x00000311, + 0x00001ecb, 0x00000002, 0x00000069, 0x00000323, + 0x0000012f, 0x00000002, 0x00000069, 0x00000328, + 0x00001e2d, 0x00000002, 0x00000069, 0x00000330, + 0x00000135, 0x00000002, 0x0000006a, 0x00000302, + 0x000001f0, 0x00000002, 0x0000006a, 0x0000030c, + 0x00001e31, 0x00000002, 0x0000006b, 0x00000301, + 0x000001e9, 0x00000002, 0x0000006b, 0x0000030c, + 0x00001e33, 0x00000002, 0x0000006b, 0x00000323, + 0x00000137, 0x00000002, 0x0000006b, 0x00000327, + 0x00001e35, 0x00000002, 0x0000006b, 0x00000331, + 0x0000013a, 0x00000002, 0x0000006c, 0x00000301, + 0x0000013e, 0x00000002, 0x0000006c, 0x0000030c, + 0x00001e37, 0x00000002, 0x0000006c, 0x00000323, + 0x0000013c, 0x00000002, 0x0000006c, 0x00000327, + 0x00001e3d, 0x00000002, 0x0000006c, 0x0000032d, + 0x00001e3b, 0x00000002, 0x0000006c, 0x00000331, + 0x00001e3f, 0x00000002, 0x0000006d, 0x00000301, + 0x00001e41, 0x00000002, 0x0000006d, 0x00000307, + 0x00001e43, 0x00000002, 0x0000006d, 0x00000323, + 0x000001f9, 0x00000002, 0x0000006e, 0x00000300, + 0x00000144, 0x00000002, 0x0000006e, 0x00000301, + 0x000000f1, 0x00000002, 0x0000006e, 0x00000303, + 0x00001e45, 0x00000002, 0x0000006e, 0x00000307, + 0x00000148, 0x00000002, 0x0000006e, 0x0000030c, + 0x00001e47, 0x00000002, 0x0000006e, 0x00000323, + 0x00000146, 0x00000002, 0x0000006e, 0x00000327, + 0x00001e4b, 0x00000002, 0x0000006e, 0x0000032d, + 0x00001e49, 0x00000002, 0x0000006e, 0x00000331, + 0x000000f2, 0x00000002, 0x0000006f, 0x00000300, + 0x000000f3, 0x00000002, 0x0000006f, 0x00000301, + 0x000000f4, 0x00000002, 0x0000006f, 0x00000302, + 0x000000f5, 0x00000002, 0x0000006f, 0x00000303, + 0x0000014d, 0x00000002, 0x0000006f, 0x00000304, + 0x0000014f, 0x00000002, 0x0000006f, 0x00000306, + 0x0000022f, 0x00000002, 0x0000006f, 0x00000307, + 0x000000f6, 0x00000002, 0x0000006f, 0x00000308, + 0x00001ecf, 0x00000002, 0x0000006f, 0x00000309, + 0x00000151, 0x00000002, 0x0000006f, 0x0000030b, + 0x000001d2, 0x00000002, 0x0000006f, 0x0000030c, + 0x0000020d, 0x00000002, 0x0000006f, 0x0000030f, + 0x0000020f, 0x00000002, 0x0000006f, 0x00000311, + 0x000001a1, 0x00000002, 0x0000006f, 0x0000031b, + 0x00001ecd, 0x00000002, 0x0000006f, 0x00000323, + 0x000001eb, 0x00000002, 0x0000006f, 0x00000328, + 0x00001e55, 0x00000002, 0x00000070, 0x00000301, + 0x00001e57, 0x00000002, 0x00000070, 0x00000307, + 0x00000155, 0x00000002, 0x00000072, 0x00000301, + 0x00001e59, 0x00000002, 0x00000072, 0x00000307, + 0x00000159, 0x00000002, 0x00000072, 0x0000030c, + 0x00000211, 0x00000002, 0x00000072, 0x0000030f, + 0x00000213, 0x00000002, 0x00000072, 0x00000311, + 0x00001e5b, 0x00000002, 0x00000072, 0x00000323, + 0x00000157, 0x00000002, 0x00000072, 0x00000327, + 0x00001e5f, 0x00000002, 0x00000072, 0x00000331, + 0x0000015b, 0x00000002, 0x00000073, 0x00000301, + 0x0000015d, 0x00000002, 0x00000073, 0x00000302, + 0x00001e61, 0x00000002, 0x00000073, 0x00000307, + 0x00000161, 0x00000002, 0x00000073, 0x0000030c, + 0x00001e63, 0x00000002, 0x00000073, 0x00000323, + 0x00000219, 0x00000002, 0x00000073, 0x00000326, + 0x0000015f, 0x00000002, 0x00000073, 0x00000327, + 0x00001e6b, 0x00000002, 0x00000074, 0x00000307, + 0x00001e97, 0x00000002, 0x00000074, 0x00000308, + 0x00000165, 0x00000002, 0x00000074, 0x0000030c, + 0x00001e6d, 0x00000002, 0x00000074, 0x00000323, + 0x0000021b, 0x00000002, 0x00000074, 0x00000326, + 0x00000163, 0x00000002, 0x00000074, 0x00000327, + 0x00001e71, 0x00000002, 0x00000074, 0x0000032d, + 0x00001e6f, 0x00000002, 0x00000074, 0x00000331, + 0x000000f9, 0x00000002, 0x00000075, 0x00000300, + 0x000000fa, 0x00000002, 0x00000075, 0x00000301, + 0x000000fb, 0x00000002, 0x00000075, 0x00000302, + 0x00000169, 0x00000002, 0x00000075, 0x00000303, + 0x0000016b, 0x00000002, 0x00000075, 0x00000304, + 0x0000016d, 0x00000002, 0x00000075, 0x00000306, + 0x000000fc, 0x00000002, 0x00000075, 0x00000308, + 0x00001ee7, 0x00000002, 0x00000075, 0x00000309, + 0x0000016f, 0x00000002, 0x00000075, 0x0000030a, + 0x00000171, 0x00000002, 0x00000075, 0x0000030b, + 0x000001d4, 0x00000002, 0x00000075, 0x0000030c, + 0x00000215, 0x00000002, 0x00000075, 0x0000030f, + 0x00000217, 0x00000002, 0x00000075, 0x00000311, + 0x000001b0, 0x00000002, 0x00000075, 0x0000031b, + 0x00001ee5, 0x00000002, 0x00000075, 0x00000323, + 0x00001e73, 0x00000002, 0x00000075, 0x00000324, + 0x00000173, 0x00000002, 0x00000075, 0x00000328, + 0x00001e77, 0x00000002, 0x00000075, 0x0000032d, + 0x00001e75, 0x00000002, 0x00000075, 0x00000330, + 0x00001e7d, 0x00000002, 0x00000076, 0x00000303, + 0x00001e7f, 0x00000002, 0x00000076, 0x00000323, + 0x00001e81, 0x00000002, 0x00000077, 0x00000300, + 0x00001e83, 0x00000002, 0x00000077, 0x00000301, + 0x00000175, 0x00000002, 0x00000077, 0x00000302, + 0x00001e87, 0x00000002, 0x00000077, 0x00000307, + 0x00001e85, 0x00000002, 0x00000077, 0x00000308, + 0x00001e98, 0x00000002, 0x00000077, 0x0000030a, + 0x00001e89, 0x00000002, 0x00000077, 0x00000323, + 0x00001e8b, 0x00000002, 0x00000078, 0x00000307, + 0x00001e8d, 0x00000002, 0x00000078, 0x00000308, + 0x00001ef3, 0x00000002, 0x00000079, 0x00000300, + 0x000000fd, 0x00000002, 0x00000079, 0x00000301, + 0x00000177, 0x00000002, 0x00000079, 0x00000302, + 0x00001ef9, 0x00000002, 0x00000079, 0x00000303, + 0x00000233, 0x00000002, 0x00000079, 0x00000304, + 0x00001e8f, 0x00000002, 0x00000079, 0x00000307, + 0x000000ff, 0x00000002, 0x00000079, 0x00000308, + 0x00001ef7, 0x00000002, 0x00000079, 0x00000309, + 0x00001e99, 0x00000002, 0x00000079, 0x0000030a, + 0x00001ef5, 0x00000002, 0x00000079, 0x00000323, + 0x0000017a, 0x00000002, 0x0000007a, 0x00000301, + 0x00001e91, 0x00000002, 0x0000007a, 0x00000302, + 0x0000017c, 0x00000002, 0x0000007a, 0x00000307, + 0x0000017e, 0x00000002, 0x0000007a, 0x0000030c, + 0x00001e93, 0x00000002, 0x0000007a, 0x00000323, + 0x00001e95, 0x00000002, 0x0000007a, 0x00000331, + 0x00001fed, 0x00000002, 0x000000a8, 0x00000300, + 0x00000385, 0x00000002, 0x000000a8, 0x00000301, + 0x00001fc1, 0x00000002, 0x000000a8, 0x00000342, + 0x00001ea6, 0x00000002, 0x000000c2, 0x00000300, + 0x00001ea4, 0x00000002, 0x000000c2, 0x00000301, + 0x00001eaa, 0x00000002, 0x000000c2, 0x00000303, + 0x00001ea8, 0x00000002, 0x000000c2, 0x00000309, + 0x000001de, 0x00000002, 0x000000c4, 0x00000304, + 0x000001fa, 0x00000002, 0x000000c5, 0x00000301, + 0x000001fc, 0x00000002, 0x000000c6, 0x00000301, + 0x000001e2, 0x00000002, 0x000000c6, 0x00000304, + 0x00001e08, 0x00000002, 0x000000c7, 0x00000301, + 0x00001ec0, 0x00000002, 0x000000ca, 0x00000300, + 0x00001ebe, 0x00000002, 0x000000ca, 0x00000301, + 0x00001ec4, 0x00000002, 0x000000ca, 0x00000303, + 0x00001ec2, 0x00000002, 0x000000ca, 0x00000309, + 0x00001e2e, 0x00000002, 0x000000cf, 0x00000301, + 0x00001ed2, 0x00000002, 0x000000d4, 0x00000300, + 0x00001ed0, 0x00000002, 0x000000d4, 0x00000301, + 0x00001ed6, 0x00000002, 0x000000d4, 0x00000303, + 0x00001ed4, 0x00000002, 0x000000d4, 0x00000309, + 0x00001e4c, 0x00000002, 0x000000d5, 0x00000301, + 0x0000022c, 0x00000002, 0x000000d5, 0x00000304, + 0x00001e4e, 0x00000002, 0x000000d5, 0x00000308, + 0x0000022a, 0x00000002, 0x000000d6, 0x00000304, + 0x000001fe, 0x00000002, 0x000000d8, 0x00000301, + 0x000001db, 0x00000002, 0x000000dc, 0x00000300, + 0x000001d7, 0x00000002, 0x000000dc, 0x00000301, + 0x000001d5, 0x00000002, 0x000000dc, 0x00000304, + 0x000001d9, 0x00000002, 0x000000dc, 0x0000030c, + 0x00001ea7, 0x00000002, 0x000000e2, 0x00000300, + 0x00001ea5, 0x00000002, 0x000000e2, 0x00000301, + 0x00001eab, 0x00000002, 0x000000e2, 0x00000303, + 0x00001ea9, 0x00000002, 0x000000e2, 0x00000309, + 0x000001df, 0x00000002, 0x000000e4, 0x00000304, + 0x000001fb, 0x00000002, 0x000000e5, 0x00000301, + 0x000001fd, 0x00000002, 0x000000e6, 0x00000301, + 0x000001e3, 0x00000002, 0x000000e6, 0x00000304, + 0x00001e09, 0x00000002, 0x000000e7, 0x00000301, + 0x00001ec1, 0x00000002, 0x000000ea, 0x00000300, + 0x00001ebf, 0x00000002, 0x000000ea, 0x00000301, + 0x00001ec5, 0x00000002, 0x000000ea, 0x00000303, + 0x00001ec3, 0x00000002, 0x000000ea, 0x00000309, + 0x00001e2f, 0x00000002, 0x000000ef, 0x00000301, + 0x00001ed3, 0x00000002, 0x000000f4, 0x00000300, + 0x00001ed1, 0x00000002, 0x000000f4, 0x00000301, + 0x00001ed7, 0x00000002, 0x000000f4, 0x00000303, + 0x00001ed5, 0x00000002, 0x000000f4, 0x00000309, + 0x00001e4d, 0x00000002, 0x000000f5, 0x00000301, + 0x0000022d, 0x00000002, 0x000000f5, 0x00000304, + 0x00001e4f, 0x00000002, 0x000000f5, 0x00000308, + 0x0000022b, 0x00000002, 0x000000f6, 0x00000304, + 0x000001ff, 0x00000002, 0x000000f8, 0x00000301, + 0x000001dc, 0x00000002, 0x000000fc, 0x00000300, + 0x000001d8, 0x00000002, 0x000000fc, 0x00000301, + 0x000001d6, 0x00000002, 0x000000fc, 0x00000304, + 0x000001da, 0x00000002, 0x000000fc, 0x0000030c, + 0x00001eb0, 0x00000002, 0x00000102, 0x00000300, + 0x00001eae, 0x00000002, 0x00000102, 0x00000301, + 0x00001eb4, 0x00000002, 0x00000102, 0x00000303, + 0x00001eb2, 0x00000002, 0x00000102, 0x00000309, + 0x00001eb1, 0x00000002, 0x00000103, 0x00000300, + 0x00001eaf, 0x00000002, 0x00000103, 0x00000301, + 0x00001eb5, 0x00000002, 0x00000103, 0x00000303, + 0x00001eb3, 0x00000002, 0x00000103, 0x00000309, + 0x00001e14, 0x00000002, 0x00000112, 0x00000300, + 0x00001e16, 0x00000002, 0x00000112, 0x00000301, + 0x00001e15, 0x00000002, 0x00000113, 0x00000300, + 0x00001e17, 0x00000002, 0x00000113, 0x00000301, + 0x00001e50, 0x00000002, 0x0000014c, 0x00000300, + 0x00001e52, 0x00000002, 0x0000014c, 0x00000301, + 0x00001e51, 0x00000002, 0x0000014d, 0x00000300, + 0x00001e53, 0x00000002, 0x0000014d, 0x00000301, + 0x00001e64, 0x00000002, 0x0000015a, 0x00000307, + 0x00001e65, 0x00000002, 0x0000015b, 0x00000307, + 0x00001e66, 0x00000002, 0x00000160, 0x00000307, + 0x00001e67, 0x00000002, 0x00000161, 0x00000307, + 0x00001e78, 0x00000002, 0x00000168, 0x00000301, + 0x00001e79, 0x00000002, 0x00000169, 0x00000301, + 0x00001e7a, 0x00000002, 0x0000016a, 0x00000308, + 0x00001e7b, 0x00000002, 0x0000016b, 0x00000308, + 0x00001e9b, 0x00000002, 0x0000017f, 0x00000307, + 0x00001edc, 0x00000002, 0x000001a0, 0x00000300, + 0x00001eda, 0x00000002, 0x000001a0, 0x00000301, + 0x00001ee0, 0x00000002, 0x000001a0, 0x00000303, + 0x00001ede, 0x00000002, 0x000001a0, 0x00000309, + 0x00001ee2, 0x00000002, 0x000001a0, 0x00000323, + 0x00001edd, 0x00000002, 0x000001a1, 0x00000300, + 0x00001edb, 0x00000002, 0x000001a1, 0x00000301, + 0x00001ee1, 0x00000002, 0x000001a1, 0x00000303, + 0x00001edf, 0x00000002, 0x000001a1, 0x00000309, + 0x00001ee3, 0x00000002, 0x000001a1, 0x00000323, + 0x00001eea, 0x00000002, 0x000001af, 0x00000300, + 0x00001ee8, 0x00000002, 0x000001af, 0x00000301, + 0x00001eee, 0x00000002, 0x000001af, 0x00000303, + 0x00001eec, 0x00000002, 0x000001af, 0x00000309, + 0x00001ef0, 0x00000002, 0x000001af, 0x00000323, + 0x00001eeb, 0x00000002, 0x000001b0, 0x00000300, + 0x00001ee9, 0x00000002, 0x000001b0, 0x00000301, + 0x00001eef, 0x00000002, 0x000001b0, 0x00000303, + 0x00001eed, 0x00000002, 0x000001b0, 0x00000309, + 0x00001ef1, 0x00000002, 0x000001b0, 0x00000323, + 0x000001ee, 0x00000002, 0x000001b7, 0x0000030c, + 0x000001ec, 0x00000002, 0x000001ea, 0x00000304, + 0x000001ed, 0x00000002, 0x000001eb, 0x00000304, + 0x000001e0, 0x00000002, 0x00000226, 0x00000304, + 0x000001e1, 0x00000002, 0x00000227, 0x00000304, + 0x00001e1c, 0x00000002, 0x00000228, 0x00000306, + 0x00001e1d, 0x00000002, 0x00000229, 0x00000306, + 0x00000230, 0x00000002, 0x0000022e, 0x00000304, + 0x00000231, 0x00000002, 0x0000022f, 0x00000304, + 0x000001ef, 0x00000002, 0x00000292, 0x0000030c, + 0x00000344, 0x00000002, 0x00000308, 0x00000301, + 0x00001fba, 0x00000002, 0x00000391, 0x00000300, + 0x00000386, 0x00000002, 0x00000391, 0x00000301, + 0x00001fb9, 0x00000002, 0x00000391, 0x00000304, + 0x00001fb8, 0x00000002, 0x00000391, 0x00000306, + 0x00001f08, 0x00000002, 0x00000391, 0x00000313, + 0x00001f09, 0x00000002, 0x00000391, 0x00000314, + 0x00001fbc, 0x00000002, 0x00000391, 0x00000345, + 0x00001fc8, 0x00000002, 0x00000395, 0x00000300, + 0x00000388, 0x00000002, 0x00000395, 0x00000301, + 0x00001f18, 0x00000002, 0x00000395, 0x00000313, + 0x00001f19, 0x00000002, 0x00000395, 0x00000314, + 0x00001fca, 0x00000002, 0x00000397, 0x00000300, + 0x00000389, 0x00000002, 0x00000397, 0x00000301, + 0x00001f28, 0x00000002, 0x00000397, 0x00000313, + 0x00001f29, 0x00000002, 0x00000397, 0x00000314, + 0x00001fcc, 0x00000002, 0x00000397, 0x00000345, + 0x00001fda, 0x00000002, 0x00000399, 0x00000300, + 0x0000038a, 0x00000002, 0x00000399, 0x00000301, + 0x00001fd9, 0x00000002, 0x00000399, 0x00000304, + 0x00001fd8, 0x00000002, 0x00000399, 0x00000306, + 0x000003aa, 0x00000002, 0x00000399, 0x00000308, + 0x00001f38, 0x00000002, 0x00000399, 0x00000313, + 0x00001f39, 0x00000002, 0x00000399, 0x00000314, + 0x00001ff8, 0x00000002, 0x0000039f, 0x00000300, + 0x0000038c, 0x00000002, 0x0000039f, 0x00000301, + 0x00001f48, 0x00000002, 0x0000039f, 0x00000313, + 0x00001f49, 0x00000002, 0x0000039f, 0x00000314, + 0x00001fec, 0x00000002, 0x000003a1, 0x00000314, + 0x00001fea, 0x00000002, 0x000003a5, 0x00000300, + 0x0000038e, 0x00000002, 0x000003a5, 0x00000301, + 0x00001fe9, 0x00000002, 0x000003a5, 0x00000304, + 0x00001fe8, 0x00000002, 0x000003a5, 0x00000306, + 0x000003ab, 0x00000002, 0x000003a5, 0x00000308, + 0x00001f59, 0x00000002, 0x000003a5, 0x00000314, + 0x00001ffa, 0x00000002, 0x000003a9, 0x00000300, + 0x0000038f, 0x00000002, 0x000003a9, 0x00000301, + 0x00001f68, 0x00000002, 0x000003a9, 0x00000313, + 0x00001f69, 0x00000002, 0x000003a9, 0x00000314, + 0x00001ffc, 0x00000002, 0x000003a9, 0x00000345, + 0x00001fb4, 0x00000002, 0x000003ac, 0x00000345, + 0x00001fc4, 0x00000002, 0x000003ae, 0x00000345, + 0x00001f70, 0x00000002, 0x000003b1, 0x00000300, + 0x000003ac, 0x00000002, 0x000003b1, 0x00000301, + 0x00001fb1, 0x00000002, 0x000003b1, 0x00000304, + 0x00001fb0, 0x00000002, 0x000003b1, 0x00000306, + 0x00001f00, 0x00000002, 0x000003b1, 0x00000313, + 0x00001f01, 0x00000002, 0x000003b1, 0x00000314, + 0x00001fb6, 0x00000002, 0x000003b1, 0x00000342, + 0x00001fb3, 0x00000002, 0x000003b1, 0x00000345, + 0x00001f72, 0x00000002, 0x000003b5, 0x00000300, + 0x000003ad, 0x00000002, 0x000003b5, 0x00000301, + 0x00001f10, 0x00000002, 0x000003b5, 0x00000313, + 0x00001f11, 0x00000002, 0x000003b5, 0x00000314, + 0x00001f74, 0x00000002, 0x000003b7, 0x00000300, + 0x000003ae, 0x00000002, 0x000003b7, 0x00000301, + 0x00001f20, 0x00000002, 0x000003b7, 0x00000313, + 0x00001f21, 0x00000002, 0x000003b7, 0x00000314, + 0x00001fc6, 0x00000002, 0x000003b7, 0x00000342, + 0x00001fc3, 0x00000002, 0x000003b7, 0x00000345, + 0x00001f76, 0x00000002, 0x000003b9, 0x00000300, + 0x000003af, 0x00000002, 0x000003b9, 0x00000301, + 0x00001fd1, 0x00000002, 0x000003b9, 0x00000304, + 0x00001fd0, 0x00000002, 0x000003b9, 0x00000306, + 0x000003ca, 0x00000002, 0x000003b9, 0x00000308, + 0x00001f30, 0x00000002, 0x000003b9, 0x00000313, + 0x00001f31, 0x00000002, 0x000003b9, 0x00000314, + 0x00001fd6, 0x00000002, 0x000003b9, 0x00000342, + 0x00001f78, 0x00000002, 0x000003bf, 0x00000300, + 0x000003cc, 0x00000002, 0x000003bf, 0x00000301, + 0x00001f40, 0x00000002, 0x000003bf, 0x00000313, + 0x00001f41, 0x00000002, 0x000003bf, 0x00000314, + 0x00001fe4, 0x00000002, 0x000003c1, 0x00000313, + 0x00001fe5, 0x00000002, 0x000003c1, 0x00000314, + 0x00001f7a, 0x00000002, 0x000003c5, 0x00000300, + 0x000003cd, 0x00000002, 0x000003c5, 0x00000301, + 0x00001fe1, 0x00000002, 0x000003c5, 0x00000304, + 0x00001fe0, 0x00000002, 0x000003c5, 0x00000306, + 0x000003cb, 0x00000002, 0x000003c5, 0x00000308, + 0x00001f50, 0x00000002, 0x000003c5, 0x00000313, + 0x00001f51, 0x00000002, 0x000003c5, 0x00000314, + 0x00001fe6, 0x00000002, 0x000003c5, 0x00000342, + 0x00001f7c, 0x00000002, 0x000003c9, 0x00000300, + 0x000003ce, 0x00000002, 0x000003c9, 0x00000301, + 0x00001f60, 0x00000002, 0x000003c9, 0x00000313, + 0x00001f61, 0x00000002, 0x000003c9, 0x00000314, + 0x00001ff6, 0x00000002, 0x000003c9, 0x00000342, + 0x00001ff3, 0x00000002, 0x000003c9, 0x00000345, + 0x00001fd2, 0x00000002, 0x000003ca, 0x00000300, + 0x00000390, 0x00000002, 0x000003ca, 0x00000301, + 0x00001fd7, 0x00000002, 0x000003ca, 0x00000342, + 0x00001fe2, 0x00000002, 0x000003cb, 0x00000300, + 0x000003b0, 0x00000002, 0x000003cb, 0x00000301, + 0x00001fe7, 0x00000002, 0x000003cb, 0x00000342, + 0x00001ff4, 0x00000002, 0x000003ce, 0x00000345, + 0x000003d3, 0x00000002, 0x000003d2, 0x00000301, + 0x000003d4, 0x00000002, 0x000003d2, 0x00000308, + 0x00000407, 0x00000002, 0x00000406, 0x00000308, + 0x000004d0, 0x00000002, 0x00000410, 0x00000306, + 0x000004d2, 0x00000002, 0x00000410, 0x00000308, + 0x00000403, 0x00000002, 0x00000413, 0x00000301, + 0x00000400, 0x00000002, 0x00000415, 0x00000300, + 0x000004d6, 0x00000002, 0x00000415, 0x00000306, + 0x00000401, 0x00000002, 0x00000415, 0x00000308, + 0x000004c1, 0x00000002, 0x00000416, 0x00000306, + 0x000004dc, 0x00000002, 0x00000416, 0x00000308, + 0x000004de, 0x00000002, 0x00000417, 0x00000308, + 0x0000040d, 0x00000002, 0x00000418, 0x00000300, + 0x000004e2, 0x00000002, 0x00000418, 0x00000304, + 0x00000419, 0x00000002, 0x00000418, 0x00000306, + 0x000004e4, 0x00000002, 0x00000418, 0x00000308, + 0x0000040c, 0x00000002, 0x0000041a, 0x00000301, + 0x000004e6, 0x00000002, 0x0000041e, 0x00000308, + 0x000004ee, 0x00000002, 0x00000423, 0x00000304, + 0x0000040e, 0x00000002, 0x00000423, 0x00000306, + 0x000004f0, 0x00000002, 0x00000423, 0x00000308, + 0x000004f2, 0x00000002, 0x00000423, 0x0000030b, + 0x000004f4, 0x00000002, 0x00000427, 0x00000308, + 0x000004f8, 0x00000002, 0x0000042b, 0x00000308, + 0x000004ec, 0x00000002, 0x0000042d, 0x00000308, + 0x000004d1, 0x00000002, 0x00000430, 0x00000306, + 0x000004d3, 0x00000002, 0x00000430, 0x00000308, + 0x00000453, 0x00000002, 0x00000433, 0x00000301, + 0x00000450, 0x00000002, 0x00000435, 0x00000300, + 0x000004d7, 0x00000002, 0x00000435, 0x00000306, + 0x00000451, 0x00000002, 0x00000435, 0x00000308, + 0x000004c2, 0x00000002, 0x00000436, 0x00000306, + 0x000004dd, 0x00000002, 0x00000436, 0x00000308, + 0x000004df, 0x00000002, 0x00000437, 0x00000308, + 0x0000045d, 0x00000002, 0x00000438, 0x00000300, + 0x000004e3, 0x00000002, 0x00000438, 0x00000304, + 0x00000439, 0x00000002, 0x00000438, 0x00000306, + 0x000004e5, 0x00000002, 0x00000438, 0x00000308, + 0x0000045c, 0x00000002, 0x0000043a, 0x00000301, + 0x000004e7, 0x00000002, 0x0000043e, 0x00000308, + 0x000004ef, 0x00000002, 0x00000443, 0x00000304, + 0x0000045e, 0x00000002, 0x00000443, 0x00000306, + 0x000004f1, 0x00000002, 0x00000443, 0x00000308, + 0x000004f3, 0x00000002, 0x00000443, 0x0000030b, + 0x000004f5, 0x00000002, 0x00000447, 0x00000308, + 0x000004f9, 0x00000002, 0x0000044b, 0x00000308, + 0x000004ed, 0x00000002, 0x0000044d, 0x00000308, + 0x00000457, 0x00000002, 0x00000456, 0x00000308, + 0x00000476, 0x00000002, 0x00000474, 0x0000030f, + 0x00000477, 0x00000002, 0x00000475, 0x0000030f, + 0x000004da, 0x00000002, 0x000004d8, 0x00000308, + 0x000004db, 0x00000002, 0x000004d9, 0x00000308, + 0x000004ea, 0x00000002, 0x000004e8, 0x00000308, + 0x000004eb, 0x00000002, 0x000004e9, 0x00000308, + 0x00000622, 0x00000002, 0x00000627, 0x00000653, + 0x00000623, 0x00000002, 0x00000627, 0x00000654, + 0x00000625, 0x00000002, 0x00000627, 0x00000655, + 0x00000624, 0x00000002, 0x00000648, 0x00000654, + 0x00000626, 0x00000002, 0x0000064a, 0x00000654, + 0x000006c2, 0x00000002, 0x000006c1, 0x00000654, + 0x000006d3, 0x00000002, 0x000006d2, 0x00000654, + 0x000006c0, 0x00000002, 0x000006d5, 0x00000654, + 0x00000929, 0x00000002, 0x00000928, 0x0000093c, + 0x00000931, 0x00000002, 0x00000930, 0x0000093c, + 0x00000934, 0x00000002, 0x00000933, 0x0000093c, + 0x000009cb, 0x00000002, 0x000009c7, 0x000009be, + 0x000009cc, 0x00000002, 0x000009c7, 0x000009d7, + 0x00000b4b, 0x00000002, 0x00000b47, 0x00000b3e, + 0x00000b48, 0x00000002, 0x00000b47, 0x00000b56, + 0x00000b4c, 0x00000002, 0x00000b47, 0x00000b57, + 0x00000b94, 0x00000002, 0x00000b92, 0x00000bd7, + 0x00000bca, 0x00000002, 0x00000bc6, 0x00000bbe, + 0x00000bcc, 0x00000002, 0x00000bc6, 0x00000bd7, + 0x00000bcb, 0x00000002, 0x00000bc7, 0x00000bbe, + 0x00000c48, 0x00000002, 0x00000c46, 0x00000c56, + 0x00000cc0, 0x00000002, 0x00000cbf, 0x00000cd5, + 0x00000cca, 0x00000002, 0x00000cc6, 0x00000cc2, + 0x00000cc7, 0x00000002, 0x00000cc6, 0x00000cd5, + 0x00000cc8, 0x00000002, 0x00000cc6, 0x00000cd6, + 0x00000ccb, 0x00000002, 0x00000cca, 0x00000cd5, + 0x00000d4a, 0x00000002, 0x00000d46, 0x00000d3e, + 0x00000d4c, 0x00000002, 0x00000d46, 0x00000d57, + 0x00000d4b, 0x00000002, 0x00000d47, 0x00000d3e, + 0x00000dda, 0x00000002, 0x00000dd9, 0x00000dca, + 0x00000ddc, 0x00000002, 0x00000dd9, 0x00000dcf, + 0x00000dde, 0x00000002, 0x00000dd9, 0x00000ddf, + 0x00000ddd, 0x00000002, 0x00000ddc, 0x00000dca, + 0x00000f73, 0x00000002, 0x00000f71, 0x00000f72, + 0x00000f75, 0x00000002, 0x00000f71, 0x00000f74, + 0x00000f81, 0x00000002, 0x00000f71, 0x00000f80, + 0x00001026, 0x00000002, 0x00001025, 0x0000102e, + 0x00001e38, 0x00000002, 0x00001e36, 0x00000304, + 0x00001e39, 0x00000002, 0x00001e37, 0x00000304, + 0x00001e5c, 0x00000002, 0x00001e5a, 0x00000304, + 0x00001e5d, 0x00000002, 0x00001e5b, 0x00000304, + 0x00001e68, 0x00000002, 0x00001e62, 0x00000307, + 0x00001e69, 0x00000002, 0x00001e63, 0x00000307, + 0x00001eac, 0x00000002, 0x00001ea0, 0x00000302, + 0x00001eb6, 0x00000002, 0x00001ea0, 0x00000306, + 0x00001ead, 0x00000002, 0x00001ea1, 0x00000302, + 0x00001eb7, 0x00000002, 0x00001ea1, 0x00000306, + 0x00001ec6, 0x00000002, 0x00001eb8, 0x00000302, + 0x00001ec7, 0x00000002, 0x00001eb9, 0x00000302, + 0x00001ed8, 0x00000002, 0x00001ecc, 0x00000302, + 0x00001ed9, 0x00000002, 0x00001ecd, 0x00000302, + 0x00001f02, 0x00000002, 0x00001f00, 0x00000300, + 0x00001f04, 0x00000002, 0x00001f00, 0x00000301, + 0x00001f06, 0x00000002, 0x00001f00, 0x00000342, + 0x00001f80, 0x00000002, 0x00001f00, 0x00000345, + 0x00001f03, 0x00000002, 0x00001f01, 0x00000300, + 0x00001f05, 0x00000002, 0x00001f01, 0x00000301, + 0x00001f07, 0x00000002, 0x00001f01, 0x00000342, + 0x00001f81, 0x00000002, 0x00001f01, 0x00000345, + 0x00001f82, 0x00000002, 0x00001f02, 0x00000345, + 0x00001f83, 0x00000002, 0x00001f03, 0x00000345, + 0x00001f84, 0x00000002, 0x00001f04, 0x00000345, + 0x00001f85, 0x00000002, 0x00001f05, 0x00000345, + 0x00001f86, 0x00000002, 0x00001f06, 0x00000345, + 0x00001f87, 0x00000002, 0x00001f07, 0x00000345, + 0x00001f0a, 0x00000002, 0x00001f08, 0x00000300, + 0x00001f0c, 0x00000002, 0x00001f08, 0x00000301, + 0x00001f0e, 0x00000002, 0x00001f08, 0x00000342, + 0x00001f88, 0x00000002, 0x00001f08, 0x00000345, + 0x00001f0b, 0x00000002, 0x00001f09, 0x00000300, + 0x00001f0d, 0x00000002, 0x00001f09, 0x00000301, + 0x00001f0f, 0x00000002, 0x00001f09, 0x00000342, + 0x00001f89, 0x00000002, 0x00001f09, 0x00000345, + 0x00001f8a, 0x00000002, 0x00001f0a, 0x00000345, + 0x00001f8b, 0x00000002, 0x00001f0b, 0x00000345, + 0x00001f8c, 0x00000002, 0x00001f0c, 0x00000345, + 0x00001f8d, 0x00000002, 0x00001f0d, 0x00000345, + 0x00001f8e, 0x00000002, 0x00001f0e, 0x00000345, + 0x00001f8f, 0x00000002, 0x00001f0f, 0x00000345, + 0x00001f12, 0x00000002, 0x00001f10, 0x00000300, + 0x00001f14, 0x00000002, 0x00001f10, 0x00000301, + 0x00001f13, 0x00000002, 0x00001f11, 0x00000300, + 0x00001f15, 0x00000002, 0x00001f11, 0x00000301, + 0x00001f1a, 0x00000002, 0x00001f18, 0x00000300, + 0x00001f1c, 0x00000002, 0x00001f18, 0x00000301, + 0x00001f1b, 0x00000002, 0x00001f19, 0x00000300, + 0x00001f1d, 0x00000002, 0x00001f19, 0x00000301, + 0x00001f22, 0x00000002, 0x00001f20, 0x00000300, + 0x00001f24, 0x00000002, 0x00001f20, 0x00000301, + 0x00001f26, 0x00000002, 0x00001f20, 0x00000342, + 0x00001f90, 0x00000002, 0x00001f20, 0x00000345, + 0x00001f23, 0x00000002, 0x00001f21, 0x00000300, + 0x00001f25, 0x00000002, 0x00001f21, 0x00000301, + 0x00001f27, 0x00000002, 0x00001f21, 0x00000342, + 0x00001f91, 0x00000002, 0x00001f21, 0x00000345, + 0x00001f92, 0x00000002, 0x00001f22, 0x00000345, + 0x00001f93, 0x00000002, 0x00001f23, 0x00000345, + 0x00001f94, 0x00000002, 0x00001f24, 0x00000345, + 0x00001f95, 0x00000002, 0x00001f25, 0x00000345, + 0x00001f96, 0x00000002, 0x00001f26, 0x00000345, + 0x00001f97, 0x00000002, 0x00001f27, 0x00000345, + 0x00001f2a, 0x00000002, 0x00001f28, 0x00000300, + 0x00001f2c, 0x00000002, 0x00001f28, 0x00000301, + 0x00001f2e, 0x00000002, 0x00001f28, 0x00000342, + 0x00001f98, 0x00000002, 0x00001f28, 0x00000345, + 0x00001f2b, 0x00000002, 0x00001f29, 0x00000300, + 0x00001f2d, 0x00000002, 0x00001f29, 0x00000301, + 0x00001f2f, 0x00000002, 0x00001f29, 0x00000342, + 0x00001f99, 0x00000002, 0x00001f29, 0x00000345, + 0x00001f9a, 0x00000002, 0x00001f2a, 0x00000345, + 0x00001f9b, 0x00000002, 0x00001f2b, 0x00000345, + 0x00001f9c, 0x00000002, 0x00001f2c, 0x00000345, + 0x00001f9d, 0x00000002, 0x00001f2d, 0x00000345, + 0x00001f9e, 0x00000002, 0x00001f2e, 0x00000345, + 0x00001f9f, 0x00000002, 0x00001f2f, 0x00000345, + 0x00001f32, 0x00000002, 0x00001f30, 0x00000300, + 0x00001f34, 0x00000002, 0x00001f30, 0x00000301, + 0x00001f36, 0x00000002, 0x00001f30, 0x00000342, + 0x00001f33, 0x00000002, 0x00001f31, 0x00000300, + 0x00001f35, 0x00000002, 0x00001f31, 0x00000301, + 0x00001f37, 0x00000002, 0x00001f31, 0x00000342, + 0x00001f3a, 0x00000002, 0x00001f38, 0x00000300, + 0x00001f3c, 0x00000002, 0x00001f38, 0x00000301, + 0x00001f3e, 0x00000002, 0x00001f38, 0x00000342, + 0x00001f3b, 0x00000002, 0x00001f39, 0x00000300, + 0x00001f3d, 0x00000002, 0x00001f39, 0x00000301, + 0x00001f3f, 0x00000002, 0x00001f39, 0x00000342, + 0x00001f42, 0x00000002, 0x00001f40, 0x00000300, + 0x00001f44, 0x00000002, 0x00001f40, 0x00000301, + 0x00001f43, 0x00000002, 0x00001f41, 0x00000300, + 0x00001f45, 0x00000002, 0x00001f41, 0x00000301, + 0x00001f4a, 0x00000002, 0x00001f48, 0x00000300, + 0x00001f4c, 0x00000002, 0x00001f48, 0x00000301, + 0x00001f4b, 0x00000002, 0x00001f49, 0x00000300, + 0x00001f4d, 0x00000002, 0x00001f49, 0x00000301, + 0x00001f52, 0x00000002, 0x00001f50, 0x00000300, + 0x00001f54, 0x00000002, 0x00001f50, 0x00000301, + 0x00001f56, 0x00000002, 0x00001f50, 0x00000342, + 0x00001f53, 0x00000002, 0x00001f51, 0x00000300, + 0x00001f55, 0x00000002, 0x00001f51, 0x00000301, + 0x00001f57, 0x00000002, 0x00001f51, 0x00000342, + 0x00001f5b, 0x00000002, 0x00001f59, 0x00000300, + 0x00001f5d, 0x00000002, 0x00001f59, 0x00000301, + 0x00001f5f, 0x00000002, 0x00001f59, 0x00000342, + 0x00001f62, 0x00000002, 0x00001f60, 0x00000300, + 0x00001f64, 0x00000002, 0x00001f60, 0x00000301, + 0x00001f66, 0x00000002, 0x00001f60, 0x00000342, + 0x00001fa0, 0x00000002, 0x00001f60, 0x00000345, + 0x00001f63, 0x00000002, 0x00001f61, 0x00000300, + 0x00001f65, 0x00000002, 0x00001f61, 0x00000301, + 0x00001f67, 0x00000002, 0x00001f61, 0x00000342, + 0x00001fa1, 0x00000002, 0x00001f61, 0x00000345, + 0x00001fa2, 0x00000002, 0x00001f62, 0x00000345, + 0x00001fa3, 0x00000002, 0x00001f63, 0x00000345, + 0x00001fa4, 0x00000002, 0x00001f64, 0x00000345, + 0x00001fa5, 0x00000002, 0x00001f65, 0x00000345, + 0x00001fa6, 0x00000002, 0x00001f66, 0x00000345, + 0x00001fa7, 0x00000002, 0x00001f67, 0x00000345, + 0x00001f6a, 0x00000002, 0x00001f68, 0x00000300, + 0x00001f6c, 0x00000002, 0x00001f68, 0x00000301, + 0x00001f6e, 0x00000002, 0x00001f68, 0x00000342, + 0x00001fa8, 0x00000002, 0x00001f68, 0x00000345, + 0x00001f6b, 0x00000002, 0x00001f69, 0x00000300, + 0x00001f6d, 0x00000002, 0x00001f69, 0x00000301, + 0x00001f6f, 0x00000002, 0x00001f69, 0x00000342, + 0x00001fa9, 0x00000002, 0x00001f69, 0x00000345, + 0x00001faa, 0x00000002, 0x00001f6a, 0x00000345, + 0x00001fab, 0x00000002, 0x00001f6b, 0x00000345, + 0x00001fac, 0x00000002, 0x00001f6c, 0x00000345, + 0x00001fad, 0x00000002, 0x00001f6d, 0x00000345, + 0x00001fae, 0x00000002, 0x00001f6e, 0x00000345, + 0x00001faf, 0x00000002, 0x00001f6f, 0x00000345, + 0x00001fb2, 0x00000002, 0x00001f70, 0x00000345, + 0x00001fc2, 0x00000002, 0x00001f74, 0x00000345, + 0x00001ff2, 0x00000002, 0x00001f7c, 0x00000345, + 0x00001fb7, 0x00000002, 0x00001fb6, 0x00000345, + 0x00001fcd, 0x00000002, 0x00001fbf, 0x00000300, + 0x00001fce, 0x00000002, 0x00001fbf, 0x00000301, + 0x00001fcf, 0x00000002, 0x00001fbf, 0x00000342, + 0x00001fc7, 0x00000002, 0x00001fc6, 0x00000345, + 0x00001ff7, 0x00000002, 0x00001ff6, 0x00000345, + 0x00001fdd, 0x00000002, 0x00001ffe, 0x00000300, + 0x00001fde, 0x00000002, 0x00001ffe, 0x00000301, + 0x00001fdf, 0x00000002, 0x00001ffe, 0x00000342, + 0x0000219a, 0x00000002, 0x00002190, 0x00000338, + 0x0000219b, 0x00000002, 0x00002192, 0x00000338, + 0x000021ae, 0x00000002, 0x00002194, 0x00000338, + 0x000021cd, 0x00000002, 0x000021d0, 0x00000338, + 0x000021cf, 0x00000002, 0x000021d2, 0x00000338, + 0x000021ce, 0x00000002, 0x000021d4, 0x00000338, + 0x00002204, 0x00000002, 0x00002203, 0x00000338, + 0x00002209, 0x00000002, 0x00002208, 0x00000338, + 0x0000220c, 0x00000002, 0x0000220b, 0x00000338, + 0x00002224, 0x00000002, 0x00002223, 0x00000338, + 0x00002226, 0x00000002, 0x00002225, 0x00000338, + 0x00002241, 0x00000002, 0x0000223c, 0x00000338, + 0x00002244, 0x00000002, 0x00002243, 0x00000338, + 0x00002247, 0x00000002, 0x00002245, 0x00000338, + 0x00002249, 0x00000002, 0x00002248, 0x00000338, + 0x0000226d, 0x00000002, 0x0000224d, 0x00000338, + 0x00002262, 0x00000002, 0x00002261, 0x00000338, + 0x00002270, 0x00000002, 0x00002264, 0x00000338, + 0x00002271, 0x00000002, 0x00002265, 0x00000338, + 0x00002274, 0x00000002, 0x00002272, 0x00000338, + 0x00002275, 0x00000002, 0x00002273, 0x00000338, + 0x00002278, 0x00000002, 0x00002276, 0x00000338, + 0x00002279, 0x00000002, 0x00002277, 0x00000338, + 0x00002280, 0x00000002, 0x0000227a, 0x00000338, + 0x00002281, 0x00000002, 0x0000227b, 0x00000338, + 0x000022e0, 0x00000002, 0x0000227c, 0x00000338, + 0x000022e1, 0x00000002, 0x0000227d, 0x00000338, + 0x00002284, 0x00000002, 0x00002282, 0x00000338, + 0x00002285, 0x00000002, 0x00002283, 0x00000338, + 0x00002288, 0x00000002, 0x00002286, 0x00000338, + 0x00002289, 0x00000002, 0x00002287, 0x00000338, + 0x000022e2, 0x00000002, 0x00002291, 0x00000338, + 0x000022e3, 0x00000002, 0x00002292, 0x00000338, + 0x000022ac, 0x00000002, 0x000022a2, 0x00000338, + 0x000022ad, 0x00000002, 0x000022a8, 0x00000338, + 0x000022ae, 0x00000002, 0x000022a9, 0x00000338, + 0x000022af, 0x00000002, 0x000022ab, 0x00000338, + 0x000022ea, 0x00000002, 0x000022b2, 0x00000338, + 0x000022eb, 0x00000002, 0x000022b3, 0x00000338, + 0x000022ec, 0x00000002, 0x000022b4, 0x00000338, + 0x000022ed, 0x00000002, 0x000022b5, 0x00000338, + 0x00003094, 0x00000002, 0x00003046, 0x00003099, + 0x0000304c, 0x00000002, 0x0000304b, 0x00003099, + 0x0000304e, 0x00000002, 0x0000304d, 0x00003099, + 0x00003050, 0x00000002, 0x0000304f, 0x00003099, + 0x00003052, 0x00000002, 0x00003051, 0x00003099, + 0x00003054, 0x00000002, 0x00003053, 0x00003099, + 0x00003056, 0x00000002, 0x00003055, 0x00003099, + 0x00003058, 0x00000002, 0x00003057, 0x00003099, + 0x0000305a, 0x00000002, 0x00003059, 0x00003099, + 0x0000305c, 0x00000002, 0x0000305b, 0x00003099, + 0x0000305e, 0x00000002, 0x0000305d, 0x00003099, + 0x00003060, 0x00000002, 0x0000305f, 0x00003099, + 0x00003062, 0x00000002, 0x00003061, 0x00003099, + 0x00003065, 0x00000002, 0x00003064, 0x00003099, + 0x00003067, 0x00000002, 0x00003066, 0x00003099, + 0x00003069, 0x00000002, 0x00003068, 0x00003099, + 0x00003070, 0x00000002, 0x0000306f, 0x00003099, + 0x00003071, 0x00000002, 0x0000306f, 0x0000309a, + 0x00003073, 0x00000002, 0x00003072, 0x00003099, + 0x00003074, 0x00000002, 0x00003072, 0x0000309a, + 0x00003076, 0x00000002, 0x00003075, 0x00003099, + 0x00003077, 0x00000002, 0x00003075, 0x0000309a, + 0x00003079, 0x00000002, 0x00003078, 0x00003099, + 0x0000307a, 0x00000002, 0x00003078, 0x0000309a, + 0x0000307c, 0x00000002, 0x0000307b, 0x00003099, + 0x0000307d, 0x00000002, 0x0000307b, 0x0000309a, + 0x0000309e, 0x00000002, 0x0000309d, 0x00003099, + 0x000030f4, 0x00000002, 0x000030a6, 0x00003099, + 0x000030ac, 0x00000002, 0x000030ab, 0x00003099, + 0x000030ae, 0x00000002, 0x000030ad, 0x00003099, + 0x000030b0, 0x00000002, 0x000030af, 0x00003099, + 0x000030b2, 0x00000002, 0x000030b1, 0x00003099, + 0x000030b4, 0x00000002, 0x000030b3, 0x00003099, + 0x000030b6, 0x00000002, 0x000030b5, 0x00003099, + 0x000030b8, 0x00000002, 0x000030b7, 0x00003099, + 0x000030ba, 0x00000002, 0x000030b9, 0x00003099, + 0x000030bc, 0x00000002, 0x000030bb, 0x00003099, + 0x000030be, 0x00000002, 0x000030bd, 0x00003099, + 0x000030c0, 0x00000002, 0x000030bf, 0x00003099, + 0x000030c2, 0x00000002, 0x000030c1, 0x00003099, + 0x000030c5, 0x00000002, 0x000030c4, 0x00003099, + 0x000030c7, 0x00000002, 0x000030c6, 0x00003099, + 0x000030c9, 0x00000002, 0x000030c8, 0x00003099, + 0x000030d0, 0x00000002, 0x000030cf, 0x00003099, + 0x000030d1, 0x00000002, 0x000030cf, 0x0000309a, + 0x000030d3, 0x00000002, 0x000030d2, 0x00003099, + 0x000030d4, 0x00000002, 0x000030d2, 0x0000309a, + 0x000030d6, 0x00000002, 0x000030d5, 0x00003099, + 0x000030d7, 0x00000002, 0x000030d5, 0x0000309a, + 0x000030d9, 0x00000002, 0x000030d8, 0x00003099, + 0x000030da, 0x00000002, 0x000030d8, 0x0000309a, + 0x000030dc, 0x00000002, 0x000030db, 0x00003099, + 0x000030dd, 0x00000002, 0x000030db, 0x0000309a, + 0x000030f7, 0x00000002, 0x000030ef, 0x00003099, + 0x000030f8, 0x00000002, 0x000030f0, 0x00003099, + 0x000030f9, 0x00000002, 0x000030f1, 0x00003099, + 0x000030fa, 0x00000002, 0x000030f2, 0x00003099, + 0x000030fe, 0x00000002, 0x000030fd, 0x00003099 +}; + +static const ac_uint4 _ucdcmp_size = 3848; + +static const ac_uint4 _ucdcmp_nodes[] = { + 0x000000c0, 0x00000000, + 0x000000c1, 0x00000002, + 0x000000c2, 0x00000004, + 0x000000c3, 0x00000006, + 0x000000c4, 0x00000008, + 0x000000c5, 0x0000000a, + 0x000000c7, 0x0000000c, + 0x000000c8, 0x0000000e, + 0x000000c9, 0x00000010, + 0x000000ca, 0x00000012, + 0x000000cb, 0x00000014, + 0x000000cc, 0x00000016, + 0x000000cd, 0x00000018, + 0x000000ce, 0x0000001a, + 0x000000cf, 0x0000001c, + 0x000000d1, 0x0000001e, + 0x000000d2, 0x00000020, + 0x000000d3, 0x00000022, + 0x000000d4, 0x00000024, + 0x000000d5, 0x00000026, + 0x000000d6, 0x00000028, + 0x000000d9, 0x0000002a, + 0x000000da, 0x0000002c, + 0x000000db, 0x0000002e, + 0x000000dc, 0x00000030, + 0x000000dd, 0x00000032, + 0x000000e0, 0x00000034, + 0x000000e1, 0x00000036, + 0x000000e2, 0x00000038, + 0x000000e3, 0x0000003a, + 0x000000e4, 0x0000003c, + 0x000000e5, 0x0000003e, + 0x000000e7, 0x00000040, + 0x000000e8, 0x00000042, + 0x000000e9, 0x00000044, + 0x000000ea, 0x00000046, + 0x000000eb, 0x00000048, + 0x000000ec, 0x0000004a, + 0x000000ed, 0x0000004c, + 0x000000ee, 0x0000004e, + 0x000000ef, 0x00000050, + 0x000000f1, 0x00000052, + 0x000000f2, 0x00000054, + 0x000000f3, 0x00000056, + 0x000000f4, 0x00000058, + 0x000000f5, 0x0000005a, + 0x000000f6, 0x0000005c, + 0x000000f9, 0x0000005e, + 0x000000fa, 0x00000060, + 0x000000fb, 0x00000062, + 0x000000fc, 0x00000064, + 0x000000fd, 0x00000066, + 0x000000ff, 0x00000068, + 0x00000100, 0x0000006a, + 0x00000101, 0x0000006c, + 0x00000102, 0x0000006e, + 0x00000103, 0x00000070, + 0x00000104, 0x00000072, + 0x00000105, 0x00000074, + 0x00000106, 0x00000076, + 0x00000107, 0x00000078, + 0x00000108, 0x0000007a, + 0x00000109, 0x0000007c, + 0x0000010a, 0x0000007e, + 0x0000010b, 0x00000080, + 0x0000010c, 0x00000082, + 0x0000010d, 0x00000084, + 0x0000010e, 0x00000086, + 0x0000010f, 0x00000088, + 0x00000112, 0x0000008a, + 0x00000113, 0x0000008c, + 0x00000114, 0x0000008e, + 0x00000115, 0x00000090, + 0x00000116, 0x00000092, + 0x00000117, 0x00000094, + 0x00000118, 0x00000096, + 0x00000119, 0x00000098, + 0x0000011a, 0x0000009a, + 0x0000011b, 0x0000009c, + 0x0000011c, 0x0000009e, + 0x0000011d, 0x000000a0, + 0x0000011e, 0x000000a2, + 0x0000011f, 0x000000a4, + 0x00000120, 0x000000a6, + 0x00000121, 0x000000a8, + 0x00000122, 0x000000aa, + 0x00000123, 0x000000ac, + 0x00000124, 0x000000ae, + 0x00000125, 0x000000b0, + 0x00000128, 0x000000b2, + 0x00000129, 0x000000b4, + 0x0000012a, 0x000000b6, + 0x0000012b, 0x000000b8, + 0x0000012c, 0x000000ba, + 0x0000012d, 0x000000bc, + 0x0000012e, 0x000000be, + 0x0000012f, 0x000000c0, + 0x00000130, 0x000000c2, + 0x00000134, 0x000000c4, + 0x00000135, 0x000000c6, + 0x00000136, 0x000000c8, + 0x00000137, 0x000000ca, + 0x00000139, 0x000000cc, + 0x0000013a, 0x000000ce, + 0x0000013b, 0x000000d0, + 0x0000013c, 0x000000d2, + 0x0000013d, 0x000000d4, + 0x0000013e, 0x000000d6, + 0x00000143, 0x000000d8, + 0x00000144, 0x000000da, + 0x00000145, 0x000000dc, + 0x00000146, 0x000000de, + 0x00000147, 0x000000e0, + 0x00000148, 0x000000e2, + 0x0000014c, 0x000000e4, + 0x0000014d, 0x000000e6, + 0x0000014e, 0x000000e8, + 0x0000014f, 0x000000ea, + 0x00000150, 0x000000ec, + 0x00000151, 0x000000ee, + 0x00000154, 0x000000f0, + 0x00000155, 0x000000f2, + 0x00000156, 0x000000f4, + 0x00000157, 0x000000f6, + 0x00000158, 0x000000f8, + 0x00000159, 0x000000fa, + 0x0000015a, 0x000000fc, + 0x0000015b, 0x000000fe, + 0x0000015c, 0x00000100, + 0x0000015d, 0x00000102, + 0x0000015e, 0x00000104, + 0x0000015f, 0x00000106, + 0x00000160, 0x00000108, + 0x00000161, 0x0000010a, + 0x00000162, 0x0000010c, + 0x00000163, 0x0000010e, + 0x00000164, 0x00000110, + 0x00000165, 0x00000112, + 0x00000168, 0x00000114, + 0x00000169, 0x00000116, + 0x0000016a, 0x00000118, + 0x0000016b, 0x0000011a, + 0x0000016c, 0x0000011c, + 0x0000016d, 0x0000011e, + 0x0000016e, 0x00000120, + 0x0000016f, 0x00000122, + 0x00000170, 0x00000124, + 0x00000171, 0x00000126, + 0x00000172, 0x00000128, + 0x00000173, 0x0000012a, + 0x00000174, 0x0000012c, + 0x00000175, 0x0000012e, + 0x00000176, 0x00000130, + 0x00000177, 0x00000132, + 0x00000178, 0x00000134, + 0x00000179, 0x00000136, + 0x0000017a, 0x00000138, + 0x0000017b, 0x0000013a, + 0x0000017c, 0x0000013c, + 0x0000017d, 0x0000013e, + 0x0000017e, 0x00000140, + 0x000001a0, 0x00000142, + 0x000001a1, 0x00000144, + 0x000001af, 0x00000146, + 0x000001b0, 0x00000148, + 0x000001cd, 0x0000014a, + 0x000001ce, 0x0000014c, + 0x000001cf, 0x0000014e, + 0x000001d0, 0x00000150, + 0x000001d1, 0x00000152, + 0x000001d2, 0x00000154, + 0x000001d3, 0x00000156, + 0x000001d4, 0x00000158, + 0x000001d5, 0x0000015a, + 0x000001d6, 0x0000015d, + 0x000001d7, 0x00000160, + 0x000001d8, 0x00000163, + 0x000001d9, 0x00000166, + 0x000001da, 0x00000169, + 0x000001db, 0x0000016c, + 0x000001dc, 0x0000016f, + 0x000001de, 0x00000172, + 0x000001df, 0x00000175, + 0x000001e0, 0x00000178, + 0x000001e1, 0x0000017b, + 0x000001e2, 0x0000017e, + 0x000001e3, 0x00000180, + 0x000001e6, 0x00000182, + 0x000001e7, 0x00000184, + 0x000001e8, 0x00000186, + 0x000001e9, 0x00000188, + 0x000001ea, 0x0000018a, + 0x000001eb, 0x0000018c, + 0x000001ec, 0x0000018e, + 0x000001ed, 0x00000191, + 0x000001ee, 0x00000194, + 0x000001ef, 0x00000196, + 0x000001f0, 0x00000198, + 0x000001f4, 0x0000019a, + 0x000001f5, 0x0000019c, + 0x000001f8, 0x0000019e, + 0x000001f9, 0x000001a0, + 0x000001fa, 0x000001a2, + 0x000001fb, 0x000001a5, + 0x000001fc, 0x000001a8, + 0x000001fd, 0x000001aa, + 0x000001fe, 0x000001ac, + 0x000001ff, 0x000001ae, + 0x00000200, 0x000001b0, + 0x00000201, 0x000001b2, + 0x00000202, 0x000001b4, + 0x00000203, 0x000001b6, + 0x00000204, 0x000001b8, + 0x00000205, 0x000001ba, + 0x00000206, 0x000001bc, + 0x00000207, 0x000001be, + 0x00000208, 0x000001c0, + 0x00000209, 0x000001c2, + 0x0000020a, 0x000001c4, + 0x0000020b, 0x000001c6, + 0x0000020c, 0x000001c8, + 0x0000020d, 0x000001ca, + 0x0000020e, 0x000001cc, + 0x0000020f, 0x000001ce, + 0x00000210, 0x000001d0, + 0x00000211, 0x000001d2, + 0x00000212, 0x000001d4, + 0x00000213, 0x000001d6, + 0x00000214, 0x000001d8, + 0x00000215, 0x000001da, + 0x00000216, 0x000001dc, + 0x00000217, 0x000001de, + 0x00000218, 0x000001e0, + 0x00000219, 0x000001e2, + 0x0000021a, 0x000001e4, + 0x0000021b, 0x000001e6, + 0x0000021e, 0x000001e8, + 0x0000021f, 0x000001ea, + 0x00000226, 0x000001ec, + 0x00000227, 0x000001ee, + 0x00000228, 0x000001f0, + 0x00000229, 0x000001f2, + 0x0000022a, 0x000001f4, + 0x0000022b, 0x000001f7, + 0x0000022c, 0x000001fa, + 0x0000022d, 0x000001fd, + 0x0000022e, 0x00000200, + 0x0000022f, 0x00000202, + 0x00000230, 0x00000204, + 0x00000231, 0x00000207, + 0x00000232, 0x0000020a, + 0x00000233, 0x0000020c, + 0x00000340, 0x0000020e, + 0x00000341, 0x0000020f, + 0x00000343, 0x00000210, + 0x00000344, 0x00000211, + 0x00000374, 0x00000213, + 0x0000037e, 0x00000214, + 0x00000385, 0x00000215, + 0x00000386, 0x00000217, + 0x00000387, 0x00000219, + 0x00000388, 0x0000021a, + 0x00000389, 0x0000021c, + 0x0000038a, 0x0000021e, + 0x0000038c, 0x00000220, + 0x0000038e, 0x00000222, + 0x0000038f, 0x00000224, + 0x00000390, 0x00000226, + 0x000003aa, 0x00000229, + 0x000003ab, 0x0000022b, + 0x000003ac, 0x0000022d, + 0x000003ad, 0x0000022f, + 0x000003ae, 0x00000231, + 0x000003af, 0x00000233, + 0x000003b0, 0x00000235, + 0x000003ca, 0x00000238, + 0x000003cb, 0x0000023a, + 0x000003cc, 0x0000023c, + 0x000003cd, 0x0000023e, + 0x000003ce, 0x00000240, + 0x000003d3, 0x00000242, + 0x000003d4, 0x00000244, + 0x00000400, 0x00000246, + 0x00000401, 0x00000248, + 0x00000403, 0x0000024a, + 0x00000407, 0x0000024c, + 0x0000040c, 0x0000024e, + 0x0000040d, 0x00000250, + 0x0000040e, 0x00000252, + 0x00000419, 0x00000254, + 0x00000439, 0x00000256, + 0x00000450, 0x00000258, + 0x00000451, 0x0000025a, + 0x00000453, 0x0000025c, + 0x00000457, 0x0000025e, + 0x0000045c, 0x00000260, + 0x0000045d, 0x00000262, + 0x0000045e, 0x00000264, + 0x00000476, 0x00000266, + 0x00000477, 0x00000268, + 0x000004c1, 0x0000026a, + 0x000004c2, 0x0000026c, + 0x000004d0, 0x0000026e, + 0x000004d1, 0x00000270, + 0x000004d2, 0x00000272, + 0x000004d3, 0x00000274, + 0x000004d6, 0x00000276, + 0x000004d7, 0x00000278, + 0x000004da, 0x0000027a, + 0x000004db, 0x0000027c, + 0x000004dc, 0x0000027e, + 0x000004dd, 0x00000280, + 0x000004de, 0x00000282, + 0x000004df, 0x00000284, + 0x000004e2, 0x00000286, + 0x000004e3, 0x00000288, + 0x000004e4, 0x0000028a, + 0x000004e5, 0x0000028c, + 0x000004e6, 0x0000028e, + 0x000004e7, 0x00000290, + 0x000004ea, 0x00000292, + 0x000004eb, 0x00000294, + 0x000004ec, 0x00000296, + 0x000004ed, 0x00000298, + 0x000004ee, 0x0000029a, + 0x000004ef, 0x0000029c, + 0x000004f0, 0x0000029e, + 0x000004f1, 0x000002a0, + 0x000004f2, 0x000002a2, + 0x000004f3, 0x000002a4, + 0x000004f4, 0x000002a6, + 0x000004f5, 0x000002a8, + 0x000004f8, 0x000002aa, + 0x000004f9, 0x000002ac, + 0x00000622, 0x000002ae, + 0x00000623, 0x000002b0, + 0x00000624, 0x000002b2, + 0x00000625, 0x000002b4, + 0x00000626, 0x000002b6, + 0x000006c0, 0x000002b8, + 0x000006c2, 0x000002ba, + 0x000006d3, 0x000002bc, + 0x00000929, 0x000002be, + 0x00000931, 0x000002c0, + 0x00000934, 0x000002c2, + 0x00000958, 0x000002c4, + 0x00000959, 0x000002c6, + 0x0000095a, 0x000002c8, + 0x0000095b, 0x000002ca, + 0x0000095c, 0x000002cc, + 0x0000095d, 0x000002ce, + 0x0000095e, 0x000002d0, + 0x0000095f, 0x000002d2, + 0x000009cb, 0x000002d4, + 0x000009cc, 0x000002d6, + 0x000009dc, 0x000002d8, + 0x000009dd, 0x000002da, + 0x000009df, 0x000002dc, + 0x00000a33, 0x000002de, + 0x00000a36, 0x000002e0, + 0x00000a59, 0x000002e2, + 0x00000a5a, 0x000002e4, + 0x00000a5b, 0x000002e6, + 0x00000a5e, 0x000002e8, + 0x00000b48, 0x000002ea, + 0x00000b4b, 0x000002ec, + 0x00000b4c, 0x000002ee, + 0x00000b5c, 0x000002f0, + 0x00000b5d, 0x000002f2, + 0x00000b94, 0x000002f4, + 0x00000bca, 0x000002f6, + 0x00000bcb, 0x000002f8, + 0x00000bcc, 0x000002fa, + 0x00000c48, 0x000002fc, + 0x00000cc0, 0x000002fe, + 0x00000cc7, 0x00000300, + 0x00000cc8, 0x00000302, + 0x00000cca, 0x00000304, + 0x00000ccb, 0x00000306, + 0x00000d4a, 0x00000309, + 0x00000d4b, 0x0000030b, + 0x00000d4c, 0x0000030d, + 0x00000dda, 0x0000030f, + 0x00000ddc, 0x00000311, + 0x00000ddd, 0x00000313, + 0x00000dde, 0x00000316, + 0x00000f43, 0x00000318, + 0x00000f4d, 0x0000031a, + 0x00000f52, 0x0000031c, + 0x00000f57, 0x0000031e, + 0x00000f5c, 0x00000320, + 0x00000f69, 0x00000322, + 0x00000f73, 0x00000324, + 0x00000f75, 0x00000326, + 0x00000f76, 0x00000328, + 0x00000f78, 0x0000032a, + 0x00000f81, 0x0000032c, + 0x00000f93, 0x0000032e, + 0x00000f9d, 0x00000330, + 0x00000fa2, 0x00000332, + 0x00000fa7, 0x00000334, + 0x00000fac, 0x00000336, + 0x00000fb9, 0x00000338, + 0x00001026, 0x0000033a, + 0x00001e00, 0x0000033c, + 0x00001e01, 0x0000033e, + 0x00001e02, 0x00000340, + 0x00001e03, 0x00000342, + 0x00001e04, 0x00000344, + 0x00001e05, 0x00000346, + 0x00001e06, 0x00000348, + 0x00001e07, 0x0000034a, + 0x00001e08, 0x0000034c, + 0x00001e09, 0x0000034f, + 0x00001e0a, 0x00000352, + 0x00001e0b, 0x00000354, + 0x00001e0c, 0x00000356, + 0x00001e0d, 0x00000358, + 0x00001e0e, 0x0000035a, + 0x00001e0f, 0x0000035c, + 0x00001e10, 0x0000035e, + 0x00001e11, 0x00000360, + 0x00001e12, 0x00000362, + 0x00001e13, 0x00000364, + 0x00001e14, 0x00000366, + 0x00001e15, 0x00000369, + 0x00001e16, 0x0000036c, + 0x00001e17, 0x0000036f, + 0x00001e18, 0x00000372, + 0x00001e19, 0x00000374, + 0x00001e1a, 0x00000376, + 0x00001e1b, 0x00000378, + 0x00001e1c, 0x0000037a, + 0x00001e1d, 0x0000037d, + 0x00001e1e, 0x00000380, + 0x00001e1f, 0x00000382, + 0x00001e20, 0x00000384, + 0x00001e21, 0x00000386, + 0x00001e22, 0x00000388, + 0x00001e23, 0x0000038a, + 0x00001e24, 0x0000038c, + 0x00001e25, 0x0000038e, + 0x00001e26, 0x00000390, + 0x00001e27, 0x00000392, + 0x00001e28, 0x00000394, + 0x00001e29, 0x00000396, + 0x00001e2a, 0x00000398, + 0x00001e2b, 0x0000039a, + 0x00001e2c, 0x0000039c, + 0x00001e2d, 0x0000039e, + 0x00001e2e, 0x000003a0, + 0x00001e2f, 0x000003a3, + 0x00001e30, 0x000003a6, + 0x00001e31, 0x000003a8, + 0x00001e32, 0x000003aa, + 0x00001e33, 0x000003ac, + 0x00001e34, 0x000003ae, + 0x00001e35, 0x000003b0, + 0x00001e36, 0x000003b2, + 0x00001e37, 0x000003b4, + 0x00001e38, 0x000003b6, + 0x00001e39, 0x000003b9, + 0x00001e3a, 0x000003bc, + 0x00001e3b, 0x000003be, + 0x00001e3c, 0x000003c0, + 0x00001e3d, 0x000003c2, + 0x00001e3e, 0x000003c4, + 0x00001e3f, 0x000003c6, + 0x00001e40, 0x000003c8, + 0x00001e41, 0x000003ca, + 0x00001e42, 0x000003cc, + 0x00001e43, 0x000003ce, + 0x00001e44, 0x000003d0, + 0x00001e45, 0x000003d2, + 0x00001e46, 0x000003d4, + 0x00001e47, 0x000003d6, + 0x00001e48, 0x000003d8, + 0x00001e49, 0x000003da, + 0x00001e4a, 0x000003dc, + 0x00001e4b, 0x000003de, + 0x00001e4c, 0x000003e0, + 0x00001e4d, 0x000003e3, + 0x00001e4e, 0x000003e6, + 0x00001e4f, 0x000003e9, + 0x00001e50, 0x000003ec, + 0x00001e51, 0x000003ef, + 0x00001e52, 0x000003f2, + 0x00001e53, 0x000003f5, + 0x00001e54, 0x000003f8, + 0x00001e55, 0x000003fa, + 0x00001e56, 0x000003fc, + 0x00001e57, 0x000003fe, + 0x00001e58, 0x00000400, + 0x00001e59, 0x00000402, + 0x00001e5a, 0x00000404, + 0x00001e5b, 0x00000406, + 0x00001e5c, 0x00000408, + 0x00001e5d, 0x0000040b, + 0x00001e5e, 0x0000040e, + 0x00001e5f, 0x00000410, + 0x00001e60, 0x00000412, + 0x00001e61, 0x00000414, + 0x00001e62, 0x00000416, + 0x00001e63, 0x00000418, + 0x00001e64, 0x0000041a, + 0x00001e65, 0x0000041d, + 0x00001e66, 0x00000420, + 0x00001e67, 0x00000423, + 0x00001e68, 0x00000426, + 0x00001e69, 0x00000429, + 0x00001e6a, 0x0000042c, + 0x00001e6b, 0x0000042e, + 0x00001e6c, 0x00000430, + 0x00001e6d, 0x00000432, + 0x00001e6e, 0x00000434, + 0x00001e6f, 0x00000436, + 0x00001e70, 0x00000438, + 0x00001e71, 0x0000043a, + 0x00001e72, 0x0000043c, + 0x00001e73, 0x0000043e, + 0x00001e74, 0x00000440, + 0x00001e75, 0x00000442, + 0x00001e76, 0x00000444, + 0x00001e77, 0x00000446, + 0x00001e78, 0x00000448, + 0x00001e79, 0x0000044b, + 0x00001e7a, 0x0000044e, + 0x00001e7b, 0x00000451, + 0x00001e7c, 0x00000454, + 0x00001e7d, 0x00000456, + 0x00001e7e, 0x00000458, + 0x00001e7f, 0x0000045a, + 0x00001e80, 0x0000045c, + 0x00001e81, 0x0000045e, + 0x00001e82, 0x00000460, + 0x00001e83, 0x00000462, + 0x00001e84, 0x00000464, + 0x00001e85, 0x00000466, + 0x00001e86, 0x00000468, + 0x00001e87, 0x0000046a, + 0x00001e88, 0x0000046c, + 0x00001e89, 0x0000046e, + 0x00001e8a, 0x00000470, + 0x00001e8b, 0x00000472, + 0x00001e8c, 0x00000474, + 0x00001e8d, 0x00000476, + 0x00001e8e, 0x00000478, + 0x00001e8f, 0x0000047a, + 0x00001e90, 0x0000047c, + 0x00001e91, 0x0000047e, + 0x00001e92, 0x00000480, + 0x00001e93, 0x00000482, + 0x00001e94, 0x00000484, + 0x00001e95, 0x00000486, + 0x00001e96, 0x00000488, + 0x00001e97, 0x0000048a, + 0x00001e98, 0x0000048c, + 0x00001e99, 0x0000048e, + 0x00001e9b, 0x00000490, + 0x00001ea0, 0x00000492, + 0x00001ea1, 0x00000494, + 0x00001ea2, 0x00000496, + 0x00001ea3, 0x00000498, + 0x00001ea4, 0x0000049a, + 0x00001ea5, 0x0000049d, + 0x00001ea6, 0x000004a0, + 0x00001ea7, 0x000004a3, + 0x00001ea8, 0x000004a6, + 0x00001ea9, 0x000004a9, + 0x00001eaa, 0x000004ac, + 0x00001eab, 0x000004af, + 0x00001eac, 0x000004b2, + 0x00001ead, 0x000004b5, + 0x00001eae, 0x000004b8, + 0x00001eaf, 0x000004bb, + 0x00001eb0, 0x000004be, + 0x00001eb1, 0x000004c1, + 0x00001eb2, 0x000004c4, + 0x00001eb3, 0x000004c7, + 0x00001eb4, 0x000004ca, + 0x00001eb5, 0x000004cd, + 0x00001eb6, 0x000004d0, + 0x00001eb7, 0x000004d3, + 0x00001eb8, 0x000004d6, + 0x00001eb9, 0x000004d8, + 0x00001eba, 0x000004da, + 0x00001ebb, 0x000004dc, + 0x00001ebc, 0x000004de, + 0x00001ebd, 0x000004e0, + 0x00001ebe, 0x000004e2, + 0x00001ebf, 0x000004e5, + 0x00001ec0, 0x000004e8, + 0x00001ec1, 0x000004eb, + 0x00001ec2, 0x000004ee, + 0x00001ec3, 0x000004f1, + 0x00001ec4, 0x000004f4, + 0x00001ec5, 0x000004f7, + 0x00001ec6, 0x000004fa, + 0x00001ec7, 0x000004fd, + 0x00001ec8, 0x00000500, + 0x00001ec9, 0x00000502, + 0x00001eca, 0x00000504, + 0x00001ecb, 0x00000506, + 0x00001ecc, 0x00000508, + 0x00001ecd, 0x0000050a, + 0x00001ece, 0x0000050c, + 0x00001ecf, 0x0000050e, + 0x00001ed0, 0x00000510, + 0x00001ed1, 0x00000513, + 0x00001ed2, 0x00000516, + 0x00001ed3, 0x00000519, + 0x00001ed4, 0x0000051c, + 0x00001ed5, 0x0000051f, + 0x00001ed6, 0x00000522, + 0x00001ed7, 0x00000525, + 0x00001ed8, 0x00000528, + 0x00001ed9, 0x0000052b, + 0x00001eda, 0x0000052e, + 0x00001edb, 0x00000531, + 0x00001edc, 0x00000534, + 0x00001edd, 0x00000537, + 0x00001ede, 0x0000053a, + 0x00001edf, 0x0000053d, + 0x00001ee0, 0x00000540, + 0x00001ee1, 0x00000543, + 0x00001ee2, 0x00000546, + 0x00001ee3, 0x00000549, + 0x00001ee4, 0x0000054c, + 0x00001ee5, 0x0000054e, + 0x00001ee6, 0x00000550, + 0x00001ee7, 0x00000552, + 0x00001ee8, 0x00000554, + 0x00001ee9, 0x00000557, + 0x00001eea, 0x0000055a, + 0x00001eeb, 0x0000055d, + 0x00001eec, 0x00000560, + 0x00001eed, 0x00000563, + 0x00001eee, 0x00000566, + 0x00001eef, 0x00000569, + 0x00001ef0, 0x0000056c, + 0x00001ef1, 0x0000056f, + 0x00001ef2, 0x00000572, + 0x00001ef3, 0x00000574, + 0x00001ef4, 0x00000576, + 0x00001ef5, 0x00000578, + 0x00001ef6, 0x0000057a, + 0x00001ef7, 0x0000057c, + 0x00001ef8, 0x0000057e, + 0x00001ef9, 0x00000580, + 0x00001f00, 0x00000582, + 0x00001f01, 0x00000584, + 0x00001f02, 0x00000586, + 0x00001f03, 0x00000589, + 0x00001f04, 0x0000058c, + 0x00001f05, 0x0000058f, + 0x00001f06, 0x00000592, + 0x00001f07, 0x00000595, + 0x00001f08, 0x00000598, + 0x00001f09, 0x0000059a, + 0x00001f0a, 0x0000059c, + 0x00001f0b, 0x0000059f, + 0x00001f0c, 0x000005a2, + 0x00001f0d, 0x000005a5, + 0x00001f0e, 0x000005a8, + 0x00001f0f, 0x000005ab, + 0x00001f10, 0x000005ae, + 0x00001f11, 0x000005b0, + 0x00001f12, 0x000005b2, + 0x00001f13, 0x000005b5, + 0x00001f14, 0x000005b8, + 0x00001f15, 0x000005bb, + 0x00001f18, 0x000005be, + 0x00001f19, 0x000005c0, + 0x00001f1a, 0x000005c2, + 0x00001f1b, 0x000005c5, + 0x00001f1c, 0x000005c8, + 0x00001f1d, 0x000005cb, + 0x00001f20, 0x000005ce, + 0x00001f21, 0x000005d0, + 0x00001f22, 0x000005d2, + 0x00001f23, 0x000005d5, + 0x00001f24, 0x000005d8, + 0x00001f25, 0x000005db, + 0x00001f26, 0x000005de, + 0x00001f27, 0x000005e1, + 0x00001f28, 0x000005e4, + 0x00001f29, 0x000005e6, + 0x00001f2a, 0x000005e8, + 0x00001f2b, 0x000005eb, + 0x00001f2c, 0x000005ee, + 0x00001f2d, 0x000005f1, + 0x00001f2e, 0x000005f4, + 0x00001f2f, 0x000005f7, + 0x00001f30, 0x000005fa, + 0x00001f31, 0x000005fc, + 0x00001f32, 0x000005fe, + 0x00001f33, 0x00000601, + 0x00001f34, 0x00000604, + 0x00001f35, 0x00000607, + 0x00001f36, 0x0000060a, + 0x00001f37, 0x0000060d, + 0x00001f38, 0x00000610, + 0x00001f39, 0x00000612, + 0x00001f3a, 0x00000614, + 0x00001f3b, 0x00000617, + 0x00001f3c, 0x0000061a, + 0x00001f3d, 0x0000061d, + 0x00001f3e, 0x00000620, + 0x00001f3f, 0x00000623, + 0x00001f40, 0x00000626, + 0x00001f41, 0x00000628, + 0x00001f42, 0x0000062a, + 0x00001f43, 0x0000062d, + 0x00001f44, 0x00000630, + 0x00001f45, 0x00000633, + 0x00001f48, 0x00000636, + 0x00001f49, 0x00000638, + 0x00001f4a, 0x0000063a, + 0x00001f4b, 0x0000063d, + 0x00001f4c, 0x00000640, + 0x00001f4d, 0x00000643, + 0x00001f50, 0x00000646, + 0x00001f51, 0x00000648, + 0x00001f52, 0x0000064a, + 0x00001f53, 0x0000064d, + 0x00001f54, 0x00000650, + 0x00001f55, 0x00000653, + 0x00001f56, 0x00000656, + 0x00001f57, 0x00000659, + 0x00001f59, 0x0000065c, + 0x00001f5b, 0x0000065e, + 0x00001f5d, 0x00000661, + 0x00001f5f, 0x00000664, + 0x00001f60, 0x00000667, + 0x00001f61, 0x00000669, + 0x00001f62, 0x0000066b, + 0x00001f63, 0x0000066e, + 0x00001f64, 0x00000671, + 0x00001f65, 0x00000674, + 0x00001f66, 0x00000677, + 0x00001f67, 0x0000067a, + 0x00001f68, 0x0000067d, + 0x00001f69, 0x0000067f, + 0x00001f6a, 0x00000681, + 0x00001f6b, 0x00000684, + 0x00001f6c, 0x00000687, + 0x00001f6d, 0x0000068a, + 0x00001f6e, 0x0000068d, + 0x00001f6f, 0x00000690, + 0x00001f70, 0x00000693, + 0x00001f71, 0x00000695, + 0x00001f72, 0x00000697, + 0x00001f73, 0x00000699, + 0x00001f74, 0x0000069b, + 0x00001f75, 0x0000069d, + 0x00001f76, 0x0000069f, + 0x00001f77, 0x000006a1, + 0x00001f78, 0x000006a3, + 0x00001f79, 0x000006a5, + 0x00001f7a, 0x000006a7, + 0x00001f7b, 0x000006a9, + 0x00001f7c, 0x000006ab, + 0x00001f7d, 0x000006ad, + 0x00001f80, 0x000006af, + 0x00001f81, 0x000006b2, + 0x00001f82, 0x000006b5, + 0x00001f83, 0x000006b9, + 0x00001f84, 0x000006bd, + 0x00001f85, 0x000006c1, + 0x00001f86, 0x000006c5, + 0x00001f87, 0x000006c9, + 0x00001f88, 0x000006cd, + 0x00001f89, 0x000006d0, + 0x00001f8a, 0x000006d3, + 0x00001f8b, 0x000006d7, + 0x00001f8c, 0x000006db, + 0x00001f8d, 0x000006df, + 0x00001f8e, 0x000006e3, + 0x00001f8f, 0x000006e7, + 0x00001f90, 0x000006eb, + 0x00001f91, 0x000006ee, + 0x00001f92, 0x000006f1, + 0x00001f93, 0x000006f5, + 0x00001f94, 0x000006f9, + 0x00001f95, 0x000006fd, + 0x00001f96, 0x00000701, + 0x00001f97, 0x00000705, + 0x00001f98, 0x00000709, + 0x00001f99, 0x0000070c, + 0x00001f9a, 0x0000070f, + 0x00001f9b, 0x00000713, + 0x00001f9c, 0x00000717, + 0x00001f9d, 0x0000071b, + 0x00001f9e, 0x0000071f, + 0x00001f9f, 0x00000723, + 0x00001fa0, 0x00000727, + 0x00001fa1, 0x0000072a, + 0x00001fa2, 0x0000072d, + 0x00001fa3, 0x00000731, + 0x00001fa4, 0x00000735, + 0x00001fa5, 0x00000739, + 0x00001fa6, 0x0000073d, + 0x00001fa7, 0x00000741, + 0x00001fa8, 0x00000745, + 0x00001fa9, 0x00000748, + 0x00001faa, 0x0000074b, + 0x00001fab, 0x0000074f, + 0x00001fac, 0x00000753, + 0x00001fad, 0x00000757, + 0x00001fae, 0x0000075b, + 0x00001faf, 0x0000075f, + 0x00001fb0, 0x00000763, + 0x00001fb1, 0x00000765, + 0x00001fb2, 0x00000767, + 0x00001fb3, 0x0000076a, + 0x00001fb4, 0x0000076c, + 0x00001fb6, 0x0000076f, + 0x00001fb7, 0x00000771, + 0x00001fb8, 0x00000774, + 0x00001fb9, 0x00000776, + 0x00001fba, 0x00000778, + 0x00001fbb, 0x0000077a, + 0x00001fbc, 0x0000077c, + 0x00001fbe, 0x0000077e, + 0x00001fc1, 0x0000077f, + 0x00001fc2, 0x00000781, + 0x00001fc3, 0x00000784, + 0x00001fc4, 0x00000786, + 0x00001fc6, 0x00000789, + 0x00001fc7, 0x0000078b, + 0x00001fc8, 0x0000078e, + 0x00001fc9, 0x00000790, + 0x00001fca, 0x00000792, + 0x00001fcb, 0x00000794, + 0x00001fcc, 0x00000796, + 0x00001fcd, 0x00000798, + 0x00001fce, 0x0000079a, + 0x00001fcf, 0x0000079c, + 0x00001fd0, 0x0000079e, + 0x00001fd1, 0x000007a0, + 0x00001fd2, 0x000007a2, + 0x00001fd3, 0x000007a5, + 0x00001fd6, 0x000007a8, + 0x00001fd7, 0x000007aa, + 0x00001fd8, 0x000007ad, + 0x00001fd9, 0x000007af, + 0x00001fda, 0x000007b1, + 0x00001fdb, 0x000007b3, + 0x00001fdd, 0x000007b5, + 0x00001fde, 0x000007b7, + 0x00001fdf, 0x000007b9, + 0x00001fe0, 0x000007bb, + 0x00001fe1, 0x000007bd, + 0x00001fe2, 0x000007bf, + 0x00001fe3, 0x000007c2, + 0x00001fe4, 0x000007c5, + 0x00001fe5, 0x000007c7, + 0x00001fe6, 0x000007c9, + 0x00001fe7, 0x000007cb, + 0x00001fe8, 0x000007ce, + 0x00001fe9, 0x000007d0, + 0x00001fea, 0x000007d2, + 0x00001feb, 0x000007d4, + 0x00001fec, 0x000007d6, + 0x00001fed, 0x000007d8, + 0x00001fee, 0x000007da, + 0x00001fef, 0x000007dc, + 0x00001ff2, 0x000007dd, + 0x00001ff3, 0x000007e0, + 0x00001ff4, 0x000007e2, + 0x00001ff6, 0x000007e5, + 0x00001ff7, 0x000007e7, + 0x00001ff8, 0x000007ea, + 0x00001ff9, 0x000007ec, + 0x00001ffa, 0x000007ee, + 0x00001ffb, 0x000007f0, + 0x00001ffc, 0x000007f2, + 0x00001ffd, 0x000007f4, + 0x00002000, 0x000007f5, + 0x00002001, 0x000007f6, + 0x00002126, 0x000007f7, + 0x0000212a, 0x000007f8, + 0x0000212b, 0x000007f9, + 0x0000219a, 0x000007fb, + 0x0000219b, 0x000007fd, + 0x000021ae, 0x000007ff, + 0x000021cd, 0x00000801, + 0x000021ce, 0x00000803, + 0x000021cf, 0x00000805, + 0x00002204, 0x00000807, + 0x00002209, 0x00000809, + 0x0000220c, 0x0000080b, + 0x00002224, 0x0000080d, + 0x00002226, 0x0000080f, + 0x00002241, 0x00000811, + 0x00002244, 0x00000813, + 0x00002247, 0x00000815, + 0x00002249, 0x00000817, + 0x00002260, 0x00000819, + 0x00002262, 0x0000081b, + 0x0000226d, 0x0000081d, + 0x0000226e, 0x0000081f, + 0x0000226f, 0x00000821, + 0x00002270, 0x00000823, + 0x00002271, 0x00000825, + 0x00002274, 0x00000827, + 0x00002275, 0x00000829, + 0x00002278, 0x0000082b, + 0x00002279, 0x0000082d, + 0x00002280, 0x0000082f, + 0x00002281, 0x00000831, + 0x00002284, 0x00000833, + 0x00002285, 0x00000835, + 0x00002288, 0x00000837, + 0x00002289, 0x00000839, + 0x000022ac, 0x0000083b, + 0x000022ad, 0x0000083d, + 0x000022ae, 0x0000083f, + 0x000022af, 0x00000841, + 0x000022e0, 0x00000843, + 0x000022e1, 0x00000845, + 0x000022e2, 0x00000847, + 0x000022e3, 0x00000849, + 0x000022ea, 0x0000084b, + 0x000022eb, 0x0000084d, + 0x000022ec, 0x0000084f, + 0x000022ed, 0x00000851, + 0x00002329, 0x00000853, + 0x0000232a, 0x00000854, + 0x00002adc, 0x00000855, + 0x0000304c, 0x00000857, + 0x0000304e, 0x00000859, + 0x00003050, 0x0000085b, + 0x00003052, 0x0000085d, + 0x00003054, 0x0000085f, + 0x00003056, 0x00000861, + 0x00003058, 0x00000863, + 0x0000305a, 0x00000865, + 0x0000305c, 0x00000867, + 0x0000305e, 0x00000869, + 0x00003060, 0x0000086b, + 0x00003062, 0x0000086d, + 0x00003065, 0x0000086f, + 0x00003067, 0x00000871, + 0x00003069, 0x00000873, + 0x00003070, 0x00000875, + 0x00003071, 0x00000877, + 0x00003073, 0x00000879, + 0x00003074, 0x0000087b, + 0x00003076, 0x0000087d, + 0x00003077, 0x0000087f, + 0x00003079, 0x00000881, + 0x0000307a, 0x00000883, + 0x0000307c, 0x00000885, + 0x0000307d, 0x00000887, + 0x00003094, 0x00000889, + 0x0000309e, 0x0000088b, + 0x000030ac, 0x0000088d, + 0x000030ae, 0x0000088f, + 0x000030b0, 0x00000891, + 0x000030b2, 0x00000893, + 0x000030b4, 0x00000895, + 0x000030b6, 0x00000897, + 0x000030b8, 0x00000899, + 0x000030ba, 0x0000089b, + 0x000030bc, 0x0000089d, + 0x000030be, 0x0000089f, + 0x000030c0, 0x000008a1, + 0x000030c2, 0x000008a3, + 0x000030c5, 0x000008a5, + 0x000030c7, 0x000008a7, + 0x000030c9, 0x000008a9, + 0x000030d0, 0x000008ab, + 0x000030d1, 0x000008ad, + 0x000030d3, 0x000008af, + 0x000030d4, 0x000008b1, + 0x000030d6, 0x000008b3, + 0x000030d7, 0x000008b5, + 0x000030d9, 0x000008b7, + 0x000030da, 0x000008b9, + 0x000030dc, 0x000008bb, + 0x000030dd, 0x000008bd, + 0x000030f4, 0x000008bf, + 0x000030f7, 0x000008c1, + 0x000030f8, 0x000008c3, + 0x000030f9, 0x000008c5, + 0x000030fa, 0x000008c7, + 0x000030fe, 0x000008c9, + 0x0000f902, 0x000008cb, + 0x0000f903, 0x000008cc, + 0x0000f904, 0x000008cd, + 0x0000f905, 0x000008ce, + 0x0000f906, 0x000008cf, + 0x0000f907, 0x000008d0, + 0x0000f908, 0x000008d1, + 0x0000f909, 0x000008d2, + 0x0000f90a, 0x000008d3, + 0x0000f90b, 0x000008d4, + 0x0000f90c, 0x000008d5, + 0x0000f90d, 0x000008d6, + 0x0000f90e, 0x000008d7, + 0x0000f90f, 0x000008d8, + 0x0000f910, 0x000008d9, + 0x0000f911, 0x000008da, + 0x0000f912, 0x000008db, + 0x0000f913, 0x000008dc, + 0x0000f914, 0x000008dd, + 0x0000f915, 0x000008de, + 0x0000f916, 0x000008df, + 0x0000f917, 0x000008e0, + 0x0000f918, 0x000008e1, + 0x0000f919, 0x000008e2, + 0x0000f91a, 0x000008e3, + 0x0000f91b, 0x000008e4, + 0x0000f91c, 0x000008e5, + 0x0000f91d, 0x000008e6, + 0x0000f91e, 0x000008e7, + 0x0000f91f, 0x000008e8, + 0x0000f920, 0x000008e9, + 0x0000f921, 0x000008ea, + 0x0000f922, 0x000008eb, + 0x0000f923, 0x000008ec, + 0x0000f924, 0x000008ed, + 0x0000f925, 0x000008ee, + 0x0000f926, 0x000008ef, + 0x0000f927, 0x000008f0, + 0x0000f928, 0x000008f1, + 0x0000f929, 0x000008f2, + 0x0000f92a, 0x000008f3, + 0x0000f92b, 0x000008f4, + 0x0000f92c, 0x000008f5, + 0x0000f92d, 0x000008f6, + 0x0000f92e, 0x000008f7, + 0x0000f92f, 0x000008f8, + 0x0000f930, 0x000008f9, + 0x0000f931, 0x000008fa, + 0x0000f932, 0x000008fb, + 0x0000f933, 0x000008fc, + 0x0000f934, 0x000008fd, + 0x0000f935, 0x000008fe, + 0x0000f936, 0x000008ff, + 0x0000f937, 0x00000900, + 0x0000f938, 0x00000901, + 0x0000f939, 0x00000902, + 0x0000f93a, 0x00000903, + 0x0000f93b, 0x00000904, + 0x0000f93c, 0x00000905, + 0x0000f93d, 0x00000906, + 0x0000f93e, 0x00000907, + 0x0000f93f, 0x00000908, + 0x0000f940, 0x00000909, + 0x0000f941, 0x0000090a, + 0x0000f942, 0x0000090b, + 0x0000f943, 0x0000090c, + 0x0000f944, 0x0000090d, + 0x0000f945, 0x0000090e, + 0x0000f946, 0x0000090f, + 0x0000f947, 0x00000910, + 0x0000f948, 0x00000911, + 0x0000f949, 0x00000912, + 0x0000f94a, 0x00000913, + 0x0000f94b, 0x00000914, + 0x0000f94c, 0x00000915, + 0x0000f94d, 0x00000916, + 0x0000f94e, 0x00000917, + 0x0000f94f, 0x00000918, + 0x0000f950, 0x00000919, + 0x0000f951, 0x0000091a, + 0x0000f952, 0x0000091b, + 0x0000f953, 0x0000091c, + 0x0000f954, 0x0000091d, + 0x0000f955, 0x0000091e, + 0x0000f956, 0x0000091f, + 0x0000f957, 0x00000920, + 0x0000f958, 0x00000921, + 0x0000f959, 0x00000922, + 0x0000f95a, 0x00000923, + 0x0000f95b, 0x00000924, + 0x0000f95c, 0x00000925, + 0x0000f95d, 0x00000926, + 0x0000f95e, 0x00000927, + 0x0000f95f, 0x00000928, + 0x0000f960, 0x00000929, + 0x0000f961, 0x0000092a, + 0x0000f962, 0x0000092b, + 0x0000f963, 0x0000092c, + 0x0000f964, 0x0000092d, + 0x0000f965, 0x0000092e, + 0x0000f966, 0x0000092f, + 0x0000f967, 0x00000930, + 0x0000f968, 0x00000931, + 0x0000f969, 0x00000932, + 0x0000f96a, 0x00000933, + 0x0000f96b, 0x00000934, + 0x0000f96c, 0x00000935, + 0x0000f96d, 0x00000936, + 0x0000f96e, 0x00000937, + 0x0000f96f, 0x00000938, + 0x0000f970, 0x00000939, + 0x0000f971, 0x0000093a, + 0x0000f972, 0x0000093b, + 0x0000f973, 0x0000093c, + 0x0000f974, 0x0000093d, + 0x0000f975, 0x0000093e, + 0x0000f976, 0x0000093f, + 0x0000f977, 0x00000940, + 0x0000f978, 0x00000941, + 0x0000f979, 0x00000942, + 0x0000f97a, 0x00000943, + 0x0000f97b, 0x00000944, + 0x0000f97c, 0x00000945, + 0x0000f97d, 0x00000946, + 0x0000f97e, 0x00000947, + 0x0000f97f, 0x00000948, + 0x0000f980, 0x00000949, + 0x0000f981, 0x0000094a, + 0x0000f982, 0x0000094b, + 0x0000f983, 0x0000094c, + 0x0000f984, 0x0000094d, + 0x0000f985, 0x0000094e, + 0x0000f986, 0x0000094f, + 0x0000f987, 0x00000950, + 0x0000f988, 0x00000951, + 0x0000f989, 0x00000952, + 0x0000f98a, 0x00000953, + 0x0000f98b, 0x00000954, + 0x0000f98c, 0x00000955, + 0x0000f98d, 0x00000956, + 0x0000f98e, 0x00000957, + 0x0000f98f, 0x00000958, + 0x0000f990, 0x00000959, + 0x0000f991, 0x0000095a, + 0x0000f992, 0x0000095b, + 0x0000f993, 0x0000095c, + 0x0000f994, 0x0000095d, + 0x0000f995, 0x0000095e, + 0x0000f996, 0x0000095f, + 0x0000f997, 0x00000960, + 0x0000f998, 0x00000961, + 0x0000f999, 0x00000962, + 0x0000f99a, 0x00000963, + 0x0000f99b, 0x00000964, + 0x0000f99c, 0x00000965, + 0x0000f99d, 0x00000966, + 0x0000f99e, 0x00000967, + 0x0000f99f, 0x00000968, + 0x0000f9a0, 0x00000969, + 0x0000f9a1, 0x0000096a, + 0x0000f9a2, 0x0000096b, + 0x0000f9a3, 0x0000096c, + 0x0000f9a4, 0x0000096d, + 0x0000f9a5, 0x0000096e, + 0x0000f9a6, 0x0000096f, + 0x0000f9a7, 0x00000970, + 0x0000f9a8, 0x00000971, + 0x0000f9a9, 0x00000972, + 0x0000f9aa, 0x00000973, + 0x0000f9ab, 0x00000974, + 0x0000f9ac, 0x00000975, + 0x0000f9ad, 0x00000976, + 0x0000f9ae, 0x00000977, + 0x0000f9af, 0x00000978, + 0x0000f9b0, 0x00000979, + 0x0000f9b1, 0x0000097a, + 0x0000f9b2, 0x0000097b, + 0x0000f9b3, 0x0000097c, + 0x0000f9b4, 0x0000097d, + 0x0000f9b5, 0x0000097e, + 0x0000f9b6, 0x0000097f, + 0x0000f9b7, 0x00000980, + 0x0000f9b8, 0x00000981, + 0x0000f9b9, 0x00000982, + 0x0000f9ba, 0x00000983, + 0x0000f9bb, 0x00000984, + 0x0000f9bc, 0x00000985, + 0x0000f9bd, 0x00000986, + 0x0000f9be, 0x00000987, + 0x0000f9bf, 0x00000988, + 0x0000f9c0, 0x00000989, + 0x0000f9c1, 0x0000098a, + 0x0000f9c2, 0x0000098b, + 0x0000f9c3, 0x0000098c, + 0x0000f9c4, 0x0000098d, + 0x0000f9c5, 0x0000098e, + 0x0000f9c6, 0x0000098f, + 0x0000f9c7, 0x00000990, + 0x0000f9c8, 0x00000991, + 0x0000f9c9, 0x00000992, + 0x0000f9ca, 0x00000993, + 0x0000f9cb, 0x00000994, + 0x0000f9cc, 0x00000995, + 0x0000f9cd, 0x00000996, + 0x0000f9ce, 0x00000997, + 0x0000f9cf, 0x00000998, + 0x0000f9d0, 0x00000999, + 0x0000f9d1, 0x0000099a, + 0x0000f9d2, 0x0000099b, + 0x0000f9d3, 0x0000099c, + 0x0000f9d4, 0x0000099d, + 0x0000f9d5, 0x0000099e, + 0x0000f9d6, 0x0000099f, + 0x0000f9d7, 0x000009a0, + 0x0000f9d8, 0x000009a1, + 0x0000f9d9, 0x000009a2, + 0x0000f9da, 0x000009a3, + 0x0000f9db, 0x000009a4, + 0x0000f9dc, 0x000009a5, + 0x0000f9dd, 0x000009a6, + 0x0000f9de, 0x000009a7, + 0x0000f9df, 0x000009a8, + 0x0000f9e0, 0x000009a9, + 0x0000f9e1, 0x000009aa, + 0x0000f9e2, 0x000009ab, + 0x0000f9e3, 0x000009ac, + 0x0000f9e4, 0x000009ad, + 0x0000f9e5, 0x000009ae, + 0x0000f9e6, 0x000009af, + 0x0000f9e7, 0x000009b0, + 0x0000f9e8, 0x000009b1, + 0x0000f9e9, 0x000009b2, + 0x0000f9ea, 0x000009b3, + 0x0000f9eb, 0x000009b4, + 0x0000f9ec, 0x000009b5, + 0x0000f9ed, 0x000009b6, + 0x0000f9ee, 0x000009b7, + 0x0000f9ef, 0x000009b8, + 0x0000f9f0, 0x000009b9, + 0x0000f9f1, 0x000009ba, + 0x0000f9f2, 0x000009bb, + 0x0000f9f3, 0x000009bc, + 0x0000f9f4, 0x000009bd, + 0x0000f9f5, 0x000009be, + 0x0000f9f6, 0x000009bf, + 0x0000f9f7, 0x000009c0, + 0x0000f9f8, 0x000009c1, + 0x0000f9f9, 0x000009c2, + 0x0000f9fa, 0x000009c3, + 0x0000f9fb, 0x000009c4, + 0x0000f9fc, 0x000009c5, + 0x0000f9fd, 0x000009c6, + 0x0000f9fe, 0x000009c7, + 0x0000f9ff, 0x000009c8, + 0x0000fa00, 0x000009c9, + 0x0000fa01, 0x000009ca, + 0x0000fa02, 0x000009cb, + 0x0000fa03, 0x000009cc, + 0x0000fa04, 0x000009cd, + 0x0000fa05, 0x000009ce, + 0x0000fa06, 0x000009cf, + 0x0000fa07, 0x000009d0, + 0x0000fa08, 0x000009d1, + 0x0000fa09, 0x000009d2, + 0x0000fa0a, 0x000009d3, + 0x0000fa0b, 0x000009d4, + 0x0000fa0c, 0x000009d5, + 0x0000fa0d, 0x000009d6, + 0x0000fa10, 0x000009d7, + 0x0000fa12, 0x000009d8, + 0x0000fa15, 0x000009d9, + 0x0000fa16, 0x000009da, + 0x0000fa17, 0x000009db, + 0x0000fa18, 0x000009dc, + 0x0000fa19, 0x000009dd, + 0x0000fa1a, 0x000009de, + 0x0000fa1b, 0x000009df, + 0x0000fa1c, 0x000009e0, + 0x0000fa1d, 0x000009e1, + 0x0000fa1e, 0x000009e2, + 0x0000fa20, 0x000009e3, + 0x0000fa22, 0x000009e4, + 0x0000fa25, 0x000009e5, + 0x0000fa26, 0x000009e6, + 0x0000fa2a, 0x000009e7, + 0x0000fa2b, 0x000009e8, + 0x0000fa2c, 0x000009e9, + 0x0000fa2d, 0x000009ea, + 0x0000fa30, 0x000009eb, + 0x0000fa31, 0x000009ec, + 0x0000fa32, 0x000009ed, + 0x0000fa33, 0x000009ee, + 0x0000fa34, 0x000009ef, + 0x0000fa35, 0x000009f0, + 0x0000fa36, 0x000009f1, + 0x0000fa37, 0x000009f2, + 0x0000fa38, 0x000009f3, + 0x0000fa39, 0x000009f4, + 0x0000fa3a, 0x000009f5, + 0x0000fa3b, 0x000009f6, + 0x0000fa3c, 0x000009f7, + 0x0000fa3d, 0x000009f8, + 0x0000fa3e, 0x000009f9, + 0x0000fa3f, 0x000009fa, + 0x0000fa40, 0x000009fb, + 0x0000fa41, 0x000009fc, + 0x0000fa42, 0x000009fd, + 0x0000fa43, 0x000009fe, + 0x0000fa44, 0x000009ff, + 0x0000fa45, 0x00000a00, + 0x0000fa46, 0x00000a01, + 0x0000fa47, 0x00000a02, + 0x0000fa48, 0x00000a03, + 0x0000fa49, 0x00000a04, + 0x0000fa4a, 0x00000a05, + 0x0000fa4b, 0x00000a06, + 0x0000fa4c, 0x00000a07, + 0x0000fa4d, 0x00000a08, + 0x0000fa4e, 0x00000a09, + 0x0000fa4f, 0x00000a0a, + 0x0000fa50, 0x00000a0b, + 0x0000fa51, 0x00000a0c, + 0x0000fa52, 0x00000a0d, + 0x0000fa53, 0x00000a0e, + 0x0000fa54, 0x00000a0f, + 0x0000fa55, 0x00000a10, + 0x0000fa56, 0x00000a11, + 0x0000fa57, 0x00000a12, + 0x0000fa58, 0x00000a13, + 0x0000fa59, 0x00000a14, + 0x0000fa5a, 0x00000a15, + 0x0000fa5b, 0x00000a16, + 0x0000fa5c, 0x00000a17, + 0x0000fa5d, 0x00000a18, + 0x0000fa5e, 0x00000a19, + 0x0000fa5f, 0x00000a1a, + 0x0000fa60, 0x00000a1b, + 0x0000fa61, 0x00000a1c, + 0x0000fa62, 0x00000a1d, + 0x0000fa63, 0x00000a1e, + 0x0000fa64, 0x00000a1f, + 0x0000fa65, 0x00000a20, + 0x0000fa66, 0x00000a21, + 0x0000fa67, 0x00000a22, + 0x0000fa68, 0x00000a23, + 0x0000fa69, 0x00000a24, + 0x0000fa6a, 0x00000a25, + 0x0000fb1d, 0x00000a26, + 0x0000fb1f, 0x00000a28, + 0x0000fb2a, 0x00000a2a, + 0x0000fb2b, 0x00000a2c, + 0x0000fb2c, 0x00000a2e, + 0x0000fb2d, 0x00000a31, + 0x0000fb2e, 0x00000a34, + 0x0000fb2f, 0x00000a36, + 0x0000fb30, 0x00000a38, + 0x0000fb31, 0x00000a3a, + 0x0000fb32, 0x00000a3c, + 0x0000fb33, 0x00000a3e, + 0x0000fb34, 0x00000a40, + 0x0000fb35, 0x00000a42, + 0x0000fb36, 0x00000a44, + 0x0000fb38, 0x00000a46, + 0x0000fb39, 0x00000a48, + 0x0000fb3a, 0x00000a4a, + 0x0000fb3b, 0x00000a4c, + 0x0000fb3c, 0x00000a4e, + 0x0000fb3e, 0x00000a50, + 0x0000fb40, 0x00000a52, + 0x0000fb41, 0x00000a54, + 0x0000fb43, 0x00000a56, + 0x0000fb44, 0x00000a58, + 0x0000fb46, 0x00000a5a, + 0x0000fb47, 0x00000a5c, + 0x0000fb48, 0x00000a5e, + 0x0000fb49, 0x00000a60, + 0x0000fb4a, 0x00000a62, + 0x0000fb4b, 0x00000a64, + 0x0000fb4c, 0x00000a66, + 0x0000fb4d, 0x00000a68, + 0x0000fb4e, 0x00000a6a, + 0x0001d15e, 0x00000a6c, + 0x0001d15f, 0x00000a6e, + 0x0001d160, 0x00000a70, + 0x0001d161, 0x00000a73, + 0x0001d162, 0x00000a76, + 0x0001d163, 0x00000a79, + 0x0001d164, 0x00000a7c, + 0x0001d1bb, 0x00000a7f, + 0x0001d1bc, 0x00000a81, + 0x0001d1bd, 0x00000a83, + 0x0001d1be, 0x00000a86, + 0x0001d1bf, 0x00000a89, + 0x0001d1c0, 0x00000a8c, + 0x0002f800, 0x00000a8f, + 0x0002f801, 0x00000a90, + 0x0002f802, 0x00000a91, + 0x0002f803, 0x00000a92, + 0x0002f804, 0x00000a93, + 0x0002f805, 0x00000a94, + 0x0002f806, 0x00000a95, + 0x0002f807, 0x00000a96, + 0x0002f808, 0x00000a97, + 0x0002f809, 0x00000a98, + 0x0002f80a, 0x00000a99, + 0x0002f80b, 0x00000a9a, + 0x0002f80c, 0x00000a9b, + 0x0002f80d, 0x00000a9c, + 0x0002f80e, 0x00000a9d, + 0x0002f80f, 0x00000a9e, + 0x0002f810, 0x00000a9f, + 0x0002f811, 0x00000aa0, + 0x0002f812, 0x00000aa1, + 0x0002f813, 0x00000aa2, + 0x0002f814, 0x00000aa3, + 0x0002f815, 0x00000aa4, + 0x0002f816, 0x00000aa5, + 0x0002f817, 0x00000aa6, + 0x0002f818, 0x00000aa7, + 0x0002f819, 0x00000aa8, + 0x0002f81a, 0x00000aa9, + 0x0002f81b, 0x00000aaa, + 0x0002f81c, 0x00000aab, + 0x0002f81d, 0x00000aac, + 0x0002f81e, 0x00000aad, + 0x0002f81f, 0x00000aae, + 0x0002f820, 0x00000aaf, + 0x0002f821, 0x00000ab0, + 0x0002f822, 0x00000ab1, + 0x0002f823, 0x00000ab2, + 0x0002f824, 0x00000ab3, + 0x0002f825, 0x00000ab4, + 0x0002f826, 0x00000ab5, + 0x0002f827, 0x00000ab6, + 0x0002f828, 0x00000ab7, + 0x0002f829, 0x00000ab8, + 0x0002f82a, 0x00000ab9, + 0x0002f82b, 0x00000aba, + 0x0002f82c, 0x00000abb, + 0x0002f82d, 0x00000abc, + 0x0002f82e, 0x00000abd, + 0x0002f82f, 0x00000abe, + 0x0002f830, 0x00000abf, + 0x0002f831, 0x00000ac0, + 0x0002f832, 0x00000ac1, + 0x0002f833, 0x00000ac2, + 0x0002f834, 0x00000ac3, + 0x0002f835, 0x00000ac4, + 0x0002f836, 0x00000ac5, + 0x0002f837, 0x00000ac6, + 0x0002f838, 0x00000ac7, + 0x0002f839, 0x00000ac8, + 0x0002f83a, 0x00000ac9, + 0x0002f83b, 0x00000aca, + 0x0002f83c, 0x00000acb, + 0x0002f83d, 0x00000acc, + 0x0002f83e, 0x00000acd, + 0x0002f83f, 0x00000ace, + 0x0002f840, 0x00000acf, + 0x0002f841, 0x00000ad0, + 0x0002f842, 0x00000ad1, + 0x0002f843, 0x00000ad2, + 0x0002f844, 0x00000ad3, + 0x0002f845, 0x00000ad4, + 0x0002f846, 0x00000ad5, + 0x0002f847, 0x00000ad6, + 0x0002f848, 0x00000ad7, + 0x0002f849, 0x00000ad8, + 0x0002f84a, 0x00000ad9, + 0x0002f84b, 0x00000ada, + 0x0002f84c, 0x00000adb, + 0x0002f84d, 0x00000adc, + 0x0002f84e, 0x00000add, + 0x0002f84f, 0x00000ade, + 0x0002f850, 0x00000adf, + 0x0002f851, 0x00000ae0, + 0x0002f852, 0x00000ae1, + 0x0002f853, 0x00000ae2, + 0x0002f854, 0x00000ae3, + 0x0002f855, 0x00000ae4, + 0x0002f856, 0x00000ae5, + 0x0002f857, 0x00000ae6, + 0x0002f858, 0x00000ae7, + 0x0002f859, 0x00000ae8, + 0x0002f85a, 0x00000ae9, + 0x0002f85b, 0x00000aea, + 0x0002f85c, 0x00000aeb, + 0x0002f85d, 0x00000aec, + 0x0002f85e, 0x00000aed, + 0x0002f85f, 0x00000aee, + 0x0002f860, 0x00000aef, + 0x0002f861, 0x00000af0, + 0x0002f862, 0x00000af1, + 0x0002f863, 0x00000af2, + 0x0002f864, 0x00000af3, + 0x0002f865, 0x00000af4, + 0x0002f866, 0x00000af5, + 0x0002f867, 0x00000af6, + 0x0002f868, 0x00000af7, + 0x0002f869, 0x00000af8, + 0x0002f86a, 0x00000af9, + 0x0002f86b, 0x00000afa, + 0x0002f86c, 0x00000afb, + 0x0002f86d, 0x00000afc, + 0x0002f86e, 0x00000afd, + 0x0002f86f, 0x00000afe, + 0x0002f870, 0x00000aff, + 0x0002f871, 0x00000b00, + 0x0002f872, 0x00000b01, + 0x0002f873, 0x00000b02, + 0x0002f874, 0x00000b03, + 0x0002f875, 0x00000b04, + 0x0002f876, 0x00000b05, + 0x0002f877, 0x00000b06, + 0x0002f878, 0x00000b07, + 0x0002f879, 0x00000b08, + 0x0002f87a, 0x00000b09, + 0x0002f87b, 0x00000b0a, + 0x0002f87c, 0x00000b0b, + 0x0002f87d, 0x00000b0c, + 0x0002f87e, 0x00000b0d, + 0x0002f87f, 0x00000b0e, + 0x0002f880, 0x00000b0f, + 0x0002f881, 0x00000b10, + 0x0002f882, 0x00000b11, + 0x0002f883, 0x00000b12, + 0x0002f884, 0x00000b13, + 0x0002f885, 0x00000b14, + 0x0002f886, 0x00000b15, + 0x0002f887, 0x00000b16, + 0x0002f888, 0x00000b17, + 0x0002f889, 0x00000b18, + 0x0002f88a, 0x00000b19, + 0x0002f88b, 0x00000b1a, + 0x0002f88c, 0x00000b1b, + 0x0002f88d, 0x00000b1c, + 0x0002f88e, 0x00000b1d, + 0x0002f88f, 0x00000b1e, + 0x0002f890, 0x00000b1f, + 0x0002f891, 0x00000b20, + 0x0002f892, 0x00000b21, + 0x0002f893, 0x00000b22, + 0x0002f894, 0x00000b23, + 0x0002f895, 0x00000b24, + 0x0002f896, 0x00000b25, + 0x0002f897, 0x00000b26, + 0x0002f898, 0x00000b27, + 0x0002f899, 0x00000b28, + 0x0002f89a, 0x00000b29, + 0x0002f89b, 0x00000b2a, + 0x0002f89c, 0x00000b2b, + 0x0002f89d, 0x00000b2c, + 0x0002f89e, 0x00000b2d, + 0x0002f89f, 0x00000b2e, + 0x0002f8a0, 0x00000b2f, + 0x0002f8a1, 0x00000b30, + 0x0002f8a2, 0x00000b31, + 0x0002f8a3, 0x00000b32, + 0x0002f8a4, 0x00000b33, + 0x0002f8a5, 0x00000b34, + 0x0002f8a6, 0x00000b35, + 0x0002f8a7, 0x00000b36, + 0x0002f8a8, 0x00000b37, + 0x0002f8a9, 0x00000b38, + 0x0002f8aa, 0x00000b39, + 0x0002f8ab, 0x00000b3a, + 0x0002f8ac, 0x00000b3b, + 0x0002f8ad, 0x00000b3c, + 0x0002f8ae, 0x00000b3d, + 0x0002f8af, 0x00000b3e, + 0x0002f8b0, 0x00000b3f, + 0x0002f8b1, 0x00000b40, + 0x0002f8b2, 0x00000b41, + 0x0002f8b3, 0x00000b42, + 0x0002f8b4, 0x00000b43, + 0x0002f8b5, 0x00000b44, + 0x0002f8b6, 0x00000b45, + 0x0002f8b7, 0x00000b46, + 0x0002f8b8, 0x00000b47, + 0x0002f8b9, 0x00000b48, + 0x0002f8ba, 0x00000b49, + 0x0002f8bb, 0x00000b4a, + 0x0002f8bc, 0x00000b4b, + 0x0002f8bd, 0x00000b4c, + 0x0002f8be, 0x00000b4d, + 0x0002f8bf, 0x00000b4e, + 0x0002f8c0, 0x00000b4f, + 0x0002f8c1, 0x00000b50, + 0x0002f8c2, 0x00000b51, + 0x0002f8c3, 0x00000b52, + 0x0002f8c4, 0x00000b53, + 0x0002f8c5, 0x00000b54, + 0x0002f8c6, 0x00000b55, + 0x0002f8c7, 0x00000b56, + 0x0002f8c8, 0x00000b57, + 0x0002f8c9, 0x00000b58, + 0x0002f8ca, 0x00000b59, + 0x0002f8cb, 0x00000b5a, + 0x0002f8cc, 0x00000b5b, + 0x0002f8cd, 0x00000b5c, + 0x0002f8ce, 0x00000b5d, + 0x0002f8cf, 0x00000b5e, + 0x0002f8d0, 0x00000b5f, + 0x0002f8d1, 0x00000b60, + 0x0002f8d2, 0x00000b61, + 0x0002f8d3, 0x00000b62, + 0x0002f8d4, 0x00000b63, + 0x0002f8d5, 0x00000b64, + 0x0002f8d6, 0x00000b65, + 0x0002f8d7, 0x00000b66, + 0x0002f8d8, 0x00000b67, + 0x0002f8d9, 0x00000b68, + 0x0002f8da, 0x00000b69, + 0x0002f8db, 0x00000b6a, + 0x0002f8dc, 0x00000b6b, + 0x0002f8dd, 0x00000b6c, + 0x0002f8de, 0x00000b6d, + 0x0002f8df, 0x00000b6e, + 0x0002f8e0, 0x00000b6f, + 0x0002f8e1, 0x00000b70, + 0x0002f8e2, 0x00000b71, + 0x0002f8e3, 0x00000b72, + 0x0002f8e4, 0x00000b73, + 0x0002f8e5, 0x00000b74, + 0x0002f8e6, 0x00000b75, + 0x0002f8e7, 0x00000b76, + 0x0002f8e8, 0x00000b77, + 0x0002f8e9, 0x00000b78, + 0x0002f8ea, 0x00000b79, + 0x0002f8eb, 0x00000b7a, + 0x0002f8ec, 0x00000b7b, + 0x0002f8ed, 0x00000b7c, + 0x0002f8ee, 0x00000b7d, + 0x0002f8ef, 0x00000b7e, + 0x0002f8f0, 0x00000b7f, + 0x0002f8f1, 0x00000b80, + 0x0002f8f2, 0x00000b81, + 0x0002f8f3, 0x00000b82, + 0x0002f8f4, 0x00000b83, + 0x0002f8f5, 0x00000b84, + 0x0002f8f6, 0x00000b85, + 0x0002f8f7, 0x00000b86, + 0x0002f8f8, 0x00000b87, + 0x0002f8f9, 0x00000b88, + 0x0002f8fa, 0x00000b89, + 0x0002f8fb, 0x00000b8a, + 0x0002f8fc, 0x00000b8b, + 0x0002f8fd, 0x00000b8c, + 0x0002f8fe, 0x00000b8d, + 0x0002f8ff, 0x00000b8e, + 0x0002f900, 0x00000b8f, + 0x0002f901, 0x00000b90, + 0x0002f902, 0x00000b91, + 0x0002f903, 0x00000b92, + 0x0002f904, 0x00000b93, + 0x0002f905, 0x00000b94, + 0x0002f906, 0x00000b95, + 0x0002f907, 0x00000b96, + 0x0002f908, 0x00000b97, + 0x0002f909, 0x00000b98, + 0x0002f90a, 0x00000b99, + 0x0002f90b, 0x00000b9a, + 0x0002f90c, 0x00000b9b, + 0x0002f90d, 0x00000b9c, + 0x0002f90e, 0x00000b9d, + 0x0002f90f, 0x00000b9e, + 0x0002f910, 0x00000b9f, + 0x0002f911, 0x00000ba0, + 0x0002f912, 0x00000ba1, + 0x0002f913, 0x00000ba2, + 0x0002f914, 0x00000ba3, + 0x0002f915, 0x00000ba4, + 0x0002f916, 0x00000ba5, + 0x0002f917, 0x00000ba6, + 0x0002f918, 0x00000ba7, + 0x0002f919, 0x00000ba8, + 0x0002f91a, 0x00000ba9, + 0x0002f91b, 0x00000baa, + 0x0002f91c, 0x00000bab, + 0x0002f91d, 0x00000bac, + 0x0002f91e, 0x00000bad, + 0x0002f91f, 0x00000bae, + 0x0002f920, 0x00000baf, + 0x0002f921, 0x00000bb0, + 0x0002f922, 0x00000bb1, + 0x0002f923, 0x00000bb2, + 0x0002f924, 0x00000bb3, + 0x0002f925, 0x00000bb4, + 0x0002f926, 0x00000bb5, + 0x0002f927, 0x00000bb6, + 0x0002f928, 0x00000bb7, + 0x0002f929, 0x00000bb8, + 0x0002f92a, 0x00000bb9, + 0x0002f92b, 0x00000bba, + 0x0002f92c, 0x00000bbb, + 0x0002f92d, 0x00000bbc, + 0x0002f92e, 0x00000bbd, + 0x0002f92f, 0x00000bbe, + 0x0002f930, 0x00000bbf, + 0x0002f931, 0x00000bc0, + 0x0002f932, 0x00000bc1, + 0x0002f933, 0x00000bc2, + 0x0002f934, 0x00000bc3, + 0x0002f935, 0x00000bc4, + 0x0002f936, 0x00000bc5, + 0x0002f937, 0x00000bc6, + 0x0002f938, 0x00000bc7, + 0x0002f939, 0x00000bc8, + 0x0002f93a, 0x00000bc9, + 0x0002f93b, 0x00000bca, + 0x0002f93c, 0x00000bcb, + 0x0002f93d, 0x00000bcc, + 0x0002f93e, 0x00000bcd, + 0x0002f93f, 0x00000bce, + 0x0002f940, 0x00000bcf, + 0x0002f941, 0x00000bd0, + 0x0002f942, 0x00000bd1, + 0x0002f943, 0x00000bd2, + 0x0002f944, 0x00000bd3, + 0x0002f945, 0x00000bd4, + 0x0002f946, 0x00000bd5, + 0x0002f947, 0x00000bd6, + 0x0002f948, 0x00000bd7, + 0x0002f949, 0x00000bd8, + 0x0002f94a, 0x00000bd9, + 0x0002f94b, 0x00000bda, + 0x0002f94c, 0x00000bdb, + 0x0002f94d, 0x00000bdc, + 0x0002f94e, 0x00000bdd, + 0x0002f94f, 0x00000bde, + 0x0002f950, 0x00000bdf, + 0x0002f951, 0x00000be0, + 0x0002f952, 0x00000be1, + 0x0002f953, 0x00000be2, + 0x0002f954, 0x00000be3, + 0x0002f955, 0x00000be4, + 0x0002f956, 0x00000be5, + 0x0002f957, 0x00000be6, + 0x0002f958, 0x00000be7, + 0x0002f959, 0x00000be8, + 0x0002f95a, 0x00000be9, + 0x0002f95b, 0x00000bea, + 0x0002f95c, 0x00000beb, + 0x0002f95d, 0x00000bec, + 0x0002f95e, 0x00000bed, + 0x0002f95f, 0x00000bee, + 0x0002f960, 0x00000bef, + 0x0002f961, 0x00000bf0, + 0x0002f962, 0x00000bf1, + 0x0002f963, 0x00000bf2, + 0x0002f964, 0x00000bf3, + 0x0002f965, 0x00000bf4, + 0x0002f966, 0x00000bf5, + 0x0002f967, 0x00000bf6, + 0x0002f968, 0x00000bf7, + 0x0002f969, 0x00000bf8, + 0x0002f96a, 0x00000bf9, + 0x0002f96b, 0x00000bfa, + 0x0002f96c, 0x00000bfb, + 0x0002f96d, 0x00000bfc, + 0x0002f96e, 0x00000bfd, + 0x0002f96f, 0x00000bfe, + 0x0002f970, 0x00000bff, + 0x0002f971, 0x00000c00, + 0x0002f972, 0x00000c01, + 0x0002f973, 0x00000c02, + 0x0002f974, 0x00000c03, + 0x0002f975, 0x00000c04, + 0x0002f976, 0x00000c05, + 0x0002f977, 0x00000c06, + 0x0002f978, 0x00000c07, + 0x0002f979, 0x00000c08, + 0x0002f97a, 0x00000c09, + 0x0002f97b, 0x00000c0a, + 0x0002f97c, 0x00000c0b, + 0x0002f97d, 0x00000c0c, + 0x0002f97e, 0x00000c0d, + 0x0002f97f, 0x00000c0e, + 0x0002f980, 0x00000c0f, + 0x0002f981, 0x00000c10, + 0x0002f982, 0x00000c11, + 0x0002f983, 0x00000c12, + 0x0002f984, 0x00000c13, + 0x0002f985, 0x00000c14, + 0x0002f986, 0x00000c15, + 0x0002f987, 0x00000c16, + 0x0002f988, 0x00000c17, + 0x0002f989, 0x00000c18, + 0x0002f98a, 0x00000c19, + 0x0002f98b, 0x00000c1a, + 0x0002f98c, 0x00000c1b, + 0x0002f98d, 0x00000c1c, + 0x0002f98e, 0x00000c1d, + 0x0002f98f, 0x00000c1e, + 0x0002f990, 0x00000c1f, + 0x0002f991, 0x00000c20, + 0x0002f992, 0x00000c21, + 0x0002f993, 0x00000c22, + 0x0002f994, 0x00000c23, + 0x0002f995, 0x00000c24, + 0x0002f996, 0x00000c25, + 0x0002f997, 0x00000c26, + 0x0002f998, 0x00000c27, + 0x0002f999, 0x00000c28, + 0x0002f99a, 0x00000c29, + 0x0002f99b, 0x00000c2a, + 0x0002f99c, 0x00000c2b, + 0x0002f99d, 0x00000c2c, + 0x0002f99e, 0x00000c2d, + 0x0002f99f, 0x00000c2e, + 0x0002f9a0, 0x00000c2f, + 0x0002f9a1, 0x00000c30, + 0x0002f9a2, 0x00000c31, + 0x0002f9a3, 0x00000c32, + 0x0002f9a4, 0x00000c33, + 0x0002f9a5, 0x00000c34, + 0x0002f9a6, 0x00000c35, + 0x0002f9a7, 0x00000c36, + 0x0002f9a8, 0x00000c37, + 0x0002f9a9, 0x00000c38, + 0x0002f9aa, 0x00000c39, + 0x0002f9ab, 0x00000c3a, + 0x0002f9ac, 0x00000c3b, + 0x0002f9ad, 0x00000c3c, + 0x0002f9ae, 0x00000c3d, + 0x0002f9af, 0x00000c3e, + 0x0002f9b0, 0x00000c3f, + 0x0002f9b1, 0x00000c40, + 0x0002f9b2, 0x00000c41, + 0x0002f9b3, 0x00000c42, + 0x0002f9b4, 0x00000c43, + 0x0002f9b5, 0x00000c44, + 0x0002f9b6, 0x00000c45, + 0x0002f9b7, 0x00000c46, + 0x0002f9b8, 0x00000c47, + 0x0002f9b9, 0x00000c48, + 0x0002f9ba, 0x00000c49, + 0x0002f9bb, 0x00000c4a, + 0x0002f9bc, 0x00000c4b, + 0x0002f9bd, 0x00000c4c, + 0x0002f9be, 0x00000c4d, + 0x0002f9bf, 0x00000c4e, + 0x0002f9c0, 0x00000c4f, + 0x0002f9c1, 0x00000c50, + 0x0002f9c2, 0x00000c51, + 0x0002f9c3, 0x00000c52, + 0x0002f9c4, 0x00000c53, + 0x0002f9c5, 0x00000c54, + 0x0002f9c6, 0x00000c55, + 0x0002f9c7, 0x00000c56, + 0x0002f9c8, 0x00000c57, + 0x0002f9c9, 0x00000c58, + 0x0002f9ca, 0x00000c59, + 0x0002f9cb, 0x00000c5a, + 0x0002f9cc, 0x00000c5b, + 0x0002f9cd, 0x00000c5c, + 0x0002f9ce, 0x00000c5d, + 0x0002f9cf, 0x00000c5e, + 0x0002f9d0, 0x00000c5f, + 0x0002f9d1, 0x00000c60, + 0x0002f9d2, 0x00000c61, + 0x0002f9d3, 0x00000c62, + 0x0002f9d4, 0x00000c63, + 0x0002f9d5, 0x00000c64, + 0x0002f9d6, 0x00000c65, + 0x0002f9d7, 0x00000c66, + 0x0002f9d8, 0x00000c67, + 0x0002f9d9, 0x00000c68, + 0x0002f9da, 0x00000c69, + 0x0002f9db, 0x00000c6a, + 0x0002f9dc, 0x00000c6b, + 0x0002f9dd, 0x00000c6c, + 0x0002f9de, 0x00000c6d, + 0x0002f9df, 0x00000c6e, + 0x0002f9e0, 0x00000c6f, + 0x0002f9e1, 0x00000c70, + 0x0002f9e2, 0x00000c71, + 0x0002f9e3, 0x00000c72, + 0x0002f9e4, 0x00000c73, + 0x0002f9e5, 0x00000c74, + 0x0002f9e6, 0x00000c75, + 0x0002f9e7, 0x00000c76, + 0x0002f9e8, 0x00000c77, + 0x0002f9e9, 0x00000c78, + 0x0002f9ea, 0x00000c79, + 0x0002f9eb, 0x00000c7a, + 0x0002f9ec, 0x00000c7b, + 0x0002f9ed, 0x00000c7c, + 0x0002f9ee, 0x00000c7d, + 0x0002f9ef, 0x00000c7e, + 0x0002f9f0, 0x00000c7f, + 0x0002f9f1, 0x00000c80, + 0x0002f9f2, 0x00000c81, + 0x0002f9f3, 0x00000c82, + 0x0002f9f4, 0x00000c83, + 0x0002f9f5, 0x00000c84, + 0x0002f9f6, 0x00000c85, + 0x0002f9f7, 0x00000c86, + 0x0002f9f8, 0x00000c87, + 0x0002f9f9, 0x00000c88, + 0x0002f9fa, 0x00000c89, + 0x0002f9fb, 0x00000c8a, + 0x0002f9fc, 0x00000c8b, + 0x0002f9fd, 0x00000c8c, + 0x0002f9fe, 0x00000c8d, + 0x0002f9ff, 0x00000c8e, + 0x0002fa00, 0x00000c8f, + 0x0002fa01, 0x00000c90, + 0x0002fa02, 0x00000c91, + 0x0002fa03, 0x00000c92, + 0x0002fa04, 0x00000c93, + 0x0002fa05, 0x00000c94, + 0x0002fa06, 0x00000c95, + 0x0002fa07, 0x00000c96, + 0x0002fa08, 0x00000c97, + 0x0002fa09, 0x00000c98, + 0x0002fa0a, 0x00000c99, + 0x0002fa0b, 0x00000c9a, + 0x0002fa0c, 0x00000c9b, + 0x0002fa0d, 0x00000c9c, + 0x0002fa0e, 0x00000c9d, + 0x0002fa0f, 0x00000c9e, + 0x0002fa10, 0x00000c9f, + 0x0002fa11, 0x00000ca0, + 0x0002fa12, 0x00000ca1, + 0x0002fa13, 0x00000ca2, + 0x0002fa14, 0x00000ca3, + 0x0002fa15, 0x00000ca4, + 0x0002fa16, 0x00000ca5, + 0x0002fa17, 0x00000ca6, + 0x0002fa18, 0x00000ca7, + 0x0002fa19, 0x00000ca8, + 0x0002fa1a, 0x00000ca9, + 0x0002fa1b, 0x00000caa, + 0x0002fa1c, 0x00000cab, + 0x0002fa1d, 0x00000cac, + 0x00000cad +}; + +static const ac_uint4 _ucdcmp_decomp[] = { + 0x00000041, 0x00000300, 0x00000041, 0x00000301, + 0x00000041, 0x00000302, 0x00000041, 0x00000303, + 0x00000041, 0x00000308, 0x00000041, 0x0000030a, + 0x00000043, 0x00000327, 0x00000045, 0x00000300, + 0x00000045, 0x00000301, 0x00000045, 0x00000302, + 0x00000045, 0x00000308, 0x00000049, 0x00000300, + 0x00000049, 0x00000301, 0x00000049, 0x00000302, + 0x00000049, 0x00000308, 0x0000004e, 0x00000303, + 0x0000004f, 0x00000300, 0x0000004f, 0x00000301, + 0x0000004f, 0x00000302, 0x0000004f, 0x00000303, + 0x0000004f, 0x00000308, 0x00000055, 0x00000300, + 0x00000055, 0x00000301, 0x00000055, 0x00000302, + 0x00000055, 0x00000308, 0x00000059, 0x00000301, + 0x00000061, 0x00000300, 0x00000061, 0x00000301, + 0x00000061, 0x00000302, 0x00000061, 0x00000303, + 0x00000061, 0x00000308, 0x00000061, 0x0000030a, + 0x00000063, 0x00000327, 0x00000065, 0x00000300, + 0x00000065, 0x00000301, 0x00000065, 0x00000302, + 0x00000065, 0x00000308, 0x00000069, 0x00000300, + 0x00000069, 0x00000301, 0x00000069, 0x00000302, + 0x00000069, 0x00000308, 0x0000006e, 0x00000303, + 0x0000006f, 0x00000300, 0x0000006f, 0x00000301, + 0x0000006f, 0x00000302, 0x0000006f, 0x00000303, + 0x0000006f, 0x00000308, 0x00000075, 0x00000300, + 0x00000075, 0x00000301, 0x00000075, 0x00000302, + 0x00000075, 0x00000308, 0x00000079, 0x00000301, + 0x00000079, 0x00000308, 0x00000041, 0x00000304, + 0x00000061, 0x00000304, 0x00000041, 0x00000306, + 0x00000061, 0x00000306, 0x00000041, 0x00000328, + 0x00000061, 0x00000328, 0x00000043, 0x00000301, + 0x00000063, 0x00000301, 0x00000043, 0x00000302, + 0x00000063, 0x00000302, 0x00000043, 0x00000307, + 0x00000063, 0x00000307, 0x00000043, 0x0000030c, + 0x00000063, 0x0000030c, 0x00000044, 0x0000030c, + 0x00000064, 0x0000030c, 0x00000045, 0x00000304, + 0x00000065, 0x00000304, 0x00000045, 0x00000306, + 0x00000065, 0x00000306, 0x00000045, 0x00000307, + 0x00000065, 0x00000307, 0x00000045, 0x00000328, + 0x00000065, 0x00000328, 0x00000045, 0x0000030c, + 0x00000065, 0x0000030c, 0x00000047, 0x00000302, + 0x00000067, 0x00000302, 0x00000047, 0x00000306, + 0x00000067, 0x00000306, 0x00000047, 0x00000307, + 0x00000067, 0x00000307, 0x00000047, 0x00000327, + 0x00000067, 0x00000327, 0x00000048, 0x00000302, + 0x00000068, 0x00000302, 0x00000049, 0x00000303, + 0x00000069, 0x00000303, 0x00000049, 0x00000304, + 0x00000069, 0x00000304, 0x00000049, 0x00000306, + 0x00000069, 0x00000306, 0x00000049, 0x00000328, + 0x00000069, 0x00000328, 0x00000049, 0x00000307, + 0x0000004a, 0x00000302, 0x0000006a, 0x00000302, + 0x0000004b, 0x00000327, 0x0000006b, 0x00000327, + 0x0000004c, 0x00000301, 0x0000006c, 0x00000301, + 0x0000004c, 0x00000327, 0x0000006c, 0x00000327, + 0x0000004c, 0x0000030c, 0x0000006c, 0x0000030c, + 0x0000004e, 0x00000301, 0x0000006e, 0x00000301, + 0x0000004e, 0x00000327, 0x0000006e, 0x00000327, + 0x0000004e, 0x0000030c, 0x0000006e, 0x0000030c, + 0x0000004f, 0x00000304, 0x0000006f, 0x00000304, + 0x0000004f, 0x00000306, 0x0000006f, 0x00000306, + 0x0000004f, 0x0000030b, 0x0000006f, 0x0000030b, + 0x00000052, 0x00000301, 0x00000072, 0x00000301, + 0x00000052, 0x00000327, 0x00000072, 0x00000327, + 0x00000052, 0x0000030c, 0x00000072, 0x0000030c, + 0x00000053, 0x00000301, 0x00000073, 0x00000301, + 0x00000053, 0x00000302, 0x00000073, 0x00000302, + 0x00000053, 0x00000327, 0x00000073, 0x00000327, + 0x00000053, 0x0000030c, 0x00000073, 0x0000030c, + 0x00000054, 0x00000327, 0x00000074, 0x00000327, + 0x00000054, 0x0000030c, 0x00000074, 0x0000030c, + 0x00000055, 0x00000303, 0x00000075, 0x00000303, + 0x00000055, 0x00000304, 0x00000075, 0x00000304, + 0x00000055, 0x00000306, 0x00000075, 0x00000306, + 0x00000055, 0x0000030a, 0x00000075, 0x0000030a, + 0x00000055, 0x0000030b, 0x00000075, 0x0000030b, + 0x00000055, 0x00000328, 0x00000075, 0x00000328, + 0x00000057, 0x00000302, 0x00000077, 0x00000302, + 0x00000059, 0x00000302, 0x00000079, 0x00000302, + 0x00000059, 0x00000308, 0x0000005a, 0x00000301, + 0x0000007a, 0x00000301, 0x0000005a, 0x00000307, + 0x0000007a, 0x00000307, 0x0000005a, 0x0000030c, + 0x0000007a, 0x0000030c, 0x0000004f, 0x0000031b, + 0x0000006f, 0x0000031b, 0x00000055, 0x0000031b, + 0x00000075, 0x0000031b, 0x00000041, 0x0000030c, + 0x00000061, 0x0000030c, 0x00000049, 0x0000030c, + 0x00000069, 0x0000030c, 0x0000004f, 0x0000030c, + 0x0000006f, 0x0000030c, 0x00000055, 0x0000030c, + 0x00000075, 0x0000030c, 0x00000055, 0x00000308, + 0x00000304, 0x00000075, 0x00000308, 0x00000304, + 0x00000055, 0x00000308, 0x00000301, 0x00000075, + 0x00000308, 0x00000301, 0x00000055, 0x00000308, + 0x0000030c, 0x00000075, 0x00000308, 0x0000030c, + 0x00000055, 0x00000308, 0x00000300, 0x00000075, + 0x00000308, 0x00000300, 0x00000041, 0x00000308, + 0x00000304, 0x00000061, 0x00000308, 0x00000304, + 0x00000041, 0x00000307, 0x00000304, 0x00000061, + 0x00000307, 0x00000304, 0x000000c6, 0x00000304, + 0x000000e6, 0x00000304, 0x00000047, 0x0000030c, + 0x00000067, 0x0000030c, 0x0000004b, 0x0000030c, + 0x0000006b, 0x0000030c, 0x0000004f, 0x00000328, + 0x0000006f, 0x00000328, 0x0000004f, 0x00000328, + 0x00000304, 0x0000006f, 0x00000328, 0x00000304, + 0x000001b7, 0x0000030c, 0x00000292, 0x0000030c, + 0x0000006a, 0x0000030c, 0x00000047, 0x00000301, + 0x00000067, 0x00000301, 0x0000004e, 0x00000300, + 0x0000006e, 0x00000300, 0x00000041, 0x0000030a, + 0x00000301, 0x00000061, 0x0000030a, 0x00000301, + 0x000000c6, 0x00000301, 0x000000e6, 0x00000301, + 0x000000d8, 0x00000301, 0x000000f8, 0x00000301, + 0x00000041, 0x0000030f, 0x00000061, 0x0000030f, + 0x00000041, 0x00000311, 0x00000061, 0x00000311, + 0x00000045, 0x0000030f, 0x00000065, 0x0000030f, + 0x00000045, 0x00000311, 0x00000065, 0x00000311, + 0x00000049, 0x0000030f, 0x00000069, 0x0000030f, + 0x00000049, 0x00000311, 0x00000069, 0x00000311, + 0x0000004f, 0x0000030f, 0x0000006f, 0x0000030f, + 0x0000004f, 0x00000311, 0x0000006f, 0x00000311, + 0x00000052, 0x0000030f, 0x00000072, 0x0000030f, + 0x00000052, 0x00000311, 0x00000072, 0x00000311, + 0x00000055, 0x0000030f, 0x00000075, 0x0000030f, + 0x00000055, 0x00000311, 0x00000075, 0x00000311, + 0x00000053, 0x00000326, 0x00000073, 0x00000326, + 0x00000054, 0x00000326, 0x00000074, 0x00000326, + 0x00000048, 0x0000030c, 0x00000068, 0x0000030c, + 0x00000041, 0x00000307, 0x00000061, 0x00000307, + 0x00000045, 0x00000327, 0x00000065, 0x00000327, + 0x0000004f, 0x00000308, 0x00000304, 0x0000006f, + 0x00000308, 0x00000304, 0x0000004f, 0x00000303, + 0x00000304, 0x0000006f, 0x00000303, 0x00000304, + 0x0000004f, 0x00000307, 0x0000006f, 0x00000307, + 0x0000004f, 0x00000307, 0x00000304, 0x0000006f, + 0x00000307, 0x00000304, 0x00000059, 0x00000304, + 0x00000079, 0x00000304, 0x00000300, 0x00000301, + 0x00000313, 0x00000308, 0x00000301, 0x000002b9, + 0x0000003b, 0x000000a8, 0x00000301, 0x00000391, + 0x00000301, 0x000000b7, 0x00000395, 0x00000301, + 0x00000397, 0x00000301, 0x00000399, 0x00000301, + 0x0000039f, 0x00000301, 0x000003a5, 0x00000301, + 0x000003a9, 0x00000301, 0x000003b9, 0x00000308, + 0x00000301, 0x00000399, 0x00000308, 0x000003a5, + 0x00000308, 0x000003b1, 0x00000301, 0x000003b5, + 0x00000301, 0x000003b7, 0x00000301, 0x000003b9, + 0x00000301, 0x000003c5, 0x00000308, 0x00000301, + 0x000003b9, 0x00000308, 0x000003c5, 0x00000308, + 0x000003bf, 0x00000301, 0x000003c5, 0x00000301, + 0x000003c9, 0x00000301, 0x000003d2, 0x00000301, + 0x000003d2, 0x00000308, 0x00000415, 0x00000300, + 0x00000415, 0x00000308, 0x00000413, 0x00000301, + 0x00000406, 0x00000308, 0x0000041a, 0x00000301, + 0x00000418, 0x00000300, 0x00000423, 0x00000306, + 0x00000418, 0x00000306, 0x00000438, 0x00000306, + 0x00000435, 0x00000300, 0x00000435, 0x00000308, + 0x00000433, 0x00000301, 0x00000456, 0x00000308, + 0x0000043a, 0x00000301, 0x00000438, 0x00000300, + 0x00000443, 0x00000306, 0x00000474, 0x0000030f, + 0x00000475, 0x0000030f, 0x00000416, 0x00000306, + 0x00000436, 0x00000306, 0x00000410, 0x00000306, + 0x00000430, 0x00000306, 0x00000410, 0x00000308, + 0x00000430, 0x00000308, 0x00000415, 0x00000306, + 0x00000435, 0x00000306, 0x000004d8, 0x00000308, + 0x000004d9, 0x00000308, 0x00000416, 0x00000308, + 0x00000436, 0x00000308, 0x00000417, 0x00000308, + 0x00000437, 0x00000308, 0x00000418, 0x00000304, + 0x00000438, 0x00000304, 0x00000418, 0x00000308, + 0x00000438, 0x00000308, 0x0000041e, 0x00000308, + 0x0000043e, 0x00000308, 0x000004e8, 0x00000308, + 0x000004e9, 0x00000308, 0x0000042d, 0x00000308, + 0x0000044d, 0x00000308, 0x00000423, 0x00000304, + 0x00000443, 0x00000304, 0x00000423, 0x00000308, + 0x00000443, 0x00000308, 0x00000423, 0x0000030b, + 0x00000443, 0x0000030b, 0x00000427, 0x00000308, + 0x00000447, 0x00000308, 0x0000042b, 0x00000308, + 0x0000044b, 0x00000308, 0x00000627, 0x00000653, + 0x00000627, 0x00000654, 0x00000648, 0x00000654, + 0x00000627, 0x00000655, 0x0000064a, 0x00000654, + 0x000006d5, 0x00000654, 0x000006c1, 0x00000654, + 0x000006d2, 0x00000654, 0x00000928, 0x0000093c, + 0x00000930, 0x0000093c, 0x00000933, 0x0000093c, + 0x00000915, 0x0000093c, 0x00000916, 0x0000093c, + 0x00000917, 0x0000093c, 0x0000091c, 0x0000093c, + 0x00000921, 0x0000093c, 0x00000922, 0x0000093c, + 0x0000092b, 0x0000093c, 0x0000092f, 0x0000093c, + 0x000009c7, 0x000009be, 0x000009c7, 0x000009d7, + 0x000009a1, 0x000009bc, 0x000009a2, 0x000009bc, + 0x000009af, 0x000009bc, 0x00000a32, 0x00000a3c, + 0x00000a38, 0x00000a3c, 0x00000a16, 0x00000a3c, + 0x00000a17, 0x00000a3c, 0x00000a1c, 0x00000a3c, + 0x00000a2b, 0x00000a3c, 0x00000b47, 0x00000b56, + 0x00000b47, 0x00000b3e, 0x00000b47, 0x00000b57, + 0x00000b21, 0x00000b3c, 0x00000b22, 0x00000b3c, + 0x00000b92, 0x00000bd7, 0x00000bc6, 0x00000bbe, + 0x00000bc7, 0x00000bbe, 0x00000bc6, 0x00000bd7, + 0x00000c46, 0x00000c56, 0x00000cbf, 0x00000cd5, + 0x00000cc6, 0x00000cd5, 0x00000cc6, 0x00000cd6, + 0x00000cc6, 0x00000cc2, 0x00000cc6, 0x00000cc2, + 0x00000cd5, 0x00000d46, 0x00000d3e, 0x00000d47, + 0x00000d3e, 0x00000d46, 0x00000d57, 0x00000dd9, + 0x00000dca, 0x00000dd9, 0x00000dcf, 0x00000dd9, + 0x00000dcf, 0x00000dca, 0x00000dd9, 0x00000ddf, + 0x00000f42, 0x00000fb7, 0x00000f4c, 0x00000fb7, + 0x00000f51, 0x00000fb7, 0x00000f56, 0x00000fb7, + 0x00000f5b, 0x00000fb7, 0x00000f40, 0x00000fb5, + 0x00000f71, 0x00000f72, 0x00000f71, 0x00000f74, + 0x00000fb2, 0x00000f80, 0x00000fb3, 0x00000f80, + 0x00000f71, 0x00000f80, 0x00000f92, 0x00000fb7, + 0x00000f9c, 0x00000fb7, 0x00000fa1, 0x00000fb7, + 0x00000fa6, 0x00000fb7, 0x00000fab, 0x00000fb7, + 0x00000f90, 0x00000fb5, 0x00001025, 0x0000102e, + 0x00000041, 0x00000325, 0x00000061, 0x00000325, + 0x00000042, 0x00000307, 0x00000062, 0x00000307, + 0x00000042, 0x00000323, 0x00000062, 0x00000323, + 0x00000042, 0x00000331, 0x00000062, 0x00000331, + 0x00000043, 0x00000327, 0x00000301, 0x00000063, + 0x00000327, 0x00000301, 0x00000044, 0x00000307, + 0x00000064, 0x00000307, 0x00000044, 0x00000323, + 0x00000064, 0x00000323, 0x00000044, 0x00000331, + 0x00000064, 0x00000331, 0x00000044, 0x00000327, + 0x00000064, 0x00000327, 0x00000044, 0x0000032d, + 0x00000064, 0x0000032d, 0x00000045, 0x00000304, + 0x00000300, 0x00000065, 0x00000304, 0x00000300, + 0x00000045, 0x00000304, 0x00000301, 0x00000065, + 0x00000304, 0x00000301, 0x00000045, 0x0000032d, + 0x00000065, 0x0000032d, 0x00000045, 0x00000330, + 0x00000065, 0x00000330, 0x00000045, 0x00000327, + 0x00000306, 0x00000065, 0x00000327, 0x00000306, + 0x00000046, 0x00000307, 0x00000066, 0x00000307, + 0x00000047, 0x00000304, 0x00000067, 0x00000304, + 0x00000048, 0x00000307, 0x00000068, 0x00000307, + 0x00000048, 0x00000323, 0x00000068, 0x00000323, + 0x00000048, 0x00000308, 0x00000068, 0x00000308, + 0x00000048, 0x00000327, 0x00000068, 0x00000327, + 0x00000048, 0x0000032e, 0x00000068, 0x0000032e, + 0x00000049, 0x00000330, 0x00000069, 0x00000330, + 0x00000049, 0x00000308, 0x00000301, 0x00000069, + 0x00000308, 0x00000301, 0x0000004b, 0x00000301, + 0x0000006b, 0x00000301, 0x0000004b, 0x00000323, + 0x0000006b, 0x00000323, 0x0000004b, 0x00000331, + 0x0000006b, 0x00000331, 0x0000004c, 0x00000323, + 0x0000006c, 0x00000323, 0x0000004c, 0x00000323, + 0x00000304, 0x0000006c, 0x00000323, 0x00000304, + 0x0000004c, 0x00000331, 0x0000006c, 0x00000331, + 0x0000004c, 0x0000032d, 0x0000006c, 0x0000032d, + 0x0000004d, 0x00000301, 0x0000006d, 0x00000301, + 0x0000004d, 0x00000307, 0x0000006d, 0x00000307, + 0x0000004d, 0x00000323, 0x0000006d, 0x00000323, + 0x0000004e, 0x00000307, 0x0000006e, 0x00000307, + 0x0000004e, 0x00000323, 0x0000006e, 0x00000323, + 0x0000004e, 0x00000331, 0x0000006e, 0x00000331, + 0x0000004e, 0x0000032d, 0x0000006e, 0x0000032d, + 0x0000004f, 0x00000303, 0x00000301, 0x0000006f, + 0x00000303, 0x00000301, 0x0000004f, 0x00000303, + 0x00000308, 0x0000006f, 0x00000303, 0x00000308, + 0x0000004f, 0x00000304, 0x00000300, 0x0000006f, + 0x00000304, 0x00000300, 0x0000004f, 0x00000304, + 0x00000301, 0x0000006f, 0x00000304, 0x00000301, + 0x00000050, 0x00000301, 0x00000070, 0x00000301, + 0x00000050, 0x00000307, 0x00000070, 0x00000307, + 0x00000052, 0x00000307, 0x00000072, 0x00000307, + 0x00000052, 0x00000323, 0x00000072, 0x00000323, + 0x00000052, 0x00000323, 0x00000304, 0x00000072, + 0x00000323, 0x00000304, 0x00000052, 0x00000331, + 0x00000072, 0x00000331, 0x00000053, 0x00000307, + 0x00000073, 0x00000307, 0x00000053, 0x00000323, + 0x00000073, 0x00000323, 0x00000053, 0x00000301, + 0x00000307, 0x00000073, 0x00000301, 0x00000307, + 0x00000053, 0x0000030c, 0x00000307, 0x00000073, + 0x0000030c, 0x00000307, 0x00000053, 0x00000323, + 0x00000307, 0x00000073, 0x00000323, 0x00000307, + 0x00000054, 0x00000307, 0x00000074, 0x00000307, + 0x00000054, 0x00000323, 0x00000074, 0x00000323, + 0x00000054, 0x00000331, 0x00000074, 0x00000331, + 0x00000054, 0x0000032d, 0x00000074, 0x0000032d, + 0x00000055, 0x00000324, 0x00000075, 0x00000324, + 0x00000055, 0x00000330, 0x00000075, 0x00000330, + 0x00000055, 0x0000032d, 0x00000075, 0x0000032d, + 0x00000055, 0x00000303, 0x00000301, 0x00000075, + 0x00000303, 0x00000301, 0x00000055, 0x00000304, + 0x00000308, 0x00000075, 0x00000304, 0x00000308, + 0x00000056, 0x00000303, 0x00000076, 0x00000303, + 0x00000056, 0x00000323, 0x00000076, 0x00000323, + 0x00000057, 0x00000300, 0x00000077, 0x00000300, + 0x00000057, 0x00000301, 0x00000077, 0x00000301, + 0x00000057, 0x00000308, 0x00000077, 0x00000308, + 0x00000057, 0x00000307, 0x00000077, 0x00000307, + 0x00000057, 0x00000323, 0x00000077, 0x00000323, + 0x00000058, 0x00000307, 0x00000078, 0x00000307, + 0x00000058, 0x00000308, 0x00000078, 0x00000308, + 0x00000059, 0x00000307, 0x00000079, 0x00000307, + 0x0000005a, 0x00000302, 0x0000007a, 0x00000302, + 0x0000005a, 0x00000323, 0x0000007a, 0x00000323, + 0x0000005a, 0x00000331, 0x0000007a, 0x00000331, + 0x00000068, 0x00000331, 0x00000074, 0x00000308, + 0x00000077, 0x0000030a, 0x00000079, 0x0000030a, + 0x0000017f, 0x00000307, 0x00000041, 0x00000323, + 0x00000061, 0x00000323, 0x00000041, 0x00000309, + 0x00000061, 0x00000309, 0x00000041, 0x00000302, + 0x00000301, 0x00000061, 0x00000302, 0x00000301, + 0x00000041, 0x00000302, 0x00000300, 0x00000061, + 0x00000302, 0x00000300, 0x00000041, 0x00000302, + 0x00000309, 0x00000061, 0x00000302, 0x00000309, + 0x00000041, 0x00000302, 0x00000303, 0x00000061, + 0x00000302, 0x00000303, 0x00000041, 0x00000323, + 0x00000302, 0x00000061, 0x00000323, 0x00000302, + 0x00000041, 0x00000306, 0x00000301, 0x00000061, + 0x00000306, 0x00000301, 0x00000041, 0x00000306, + 0x00000300, 0x00000061, 0x00000306, 0x00000300, + 0x00000041, 0x00000306, 0x00000309, 0x00000061, + 0x00000306, 0x00000309, 0x00000041, 0x00000306, + 0x00000303, 0x00000061, 0x00000306, 0x00000303, + 0x00000041, 0x00000323, 0x00000306, 0x00000061, + 0x00000323, 0x00000306, 0x00000045, 0x00000323, + 0x00000065, 0x00000323, 0x00000045, 0x00000309, + 0x00000065, 0x00000309, 0x00000045, 0x00000303, + 0x00000065, 0x00000303, 0x00000045, 0x00000302, + 0x00000301, 0x00000065, 0x00000302, 0x00000301, + 0x00000045, 0x00000302, 0x00000300, 0x00000065, + 0x00000302, 0x00000300, 0x00000045, 0x00000302, + 0x00000309, 0x00000065, 0x00000302, 0x00000309, + 0x00000045, 0x00000302, 0x00000303, 0x00000065, + 0x00000302, 0x00000303, 0x00000045, 0x00000323, + 0x00000302, 0x00000065, 0x00000323, 0x00000302, + 0x00000049, 0x00000309, 0x00000069, 0x00000309, + 0x00000049, 0x00000323, 0x00000069, 0x00000323, + 0x0000004f, 0x00000323, 0x0000006f, 0x00000323, + 0x0000004f, 0x00000309, 0x0000006f, 0x00000309, + 0x0000004f, 0x00000302, 0x00000301, 0x0000006f, + 0x00000302, 0x00000301, 0x0000004f, 0x00000302, + 0x00000300, 0x0000006f, 0x00000302, 0x00000300, + 0x0000004f, 0x00000302, 0x00000309, 0x0000006f, + 0x00000302, 0x00000309, 0x0000004f, 0x00000302, + 0x00000303, 0x0000006f, 0x00000302, 0x00000303, + 0x0000004f, 0x00000323, 0x00000302, 0x0000006f, + 0x00000323, 0x00000302, 0x0000004f, 0x0000031b, + 0x00000301, 0x0000006f, 0x0000031b, 0x00000301, + 0x0000004f, 0x0000031b, 0x00000300, 0x0000006f, + 0x0000031b, 0x00000300, 0x0000004f, 0x0000031b, + 0x00000309, 0x0000006f, 0x0000031b, 0x00000309, + 0x0000004f, 0x0000031b, 0x00000303, 0x0000006f, + 0x0000031b, 0x00000303, 0x0000004f, 0x0000031b, + 0x00000323, 0x0000006f, 0x0000031b, 0x00000323, + 0x00000055, 0x00000323, 0x00000075, 0x00000323, + 0x00000055, 0x00000309, 0x00000075, 0x00000309, + 0x00000055, 0x0000031b, 0x00000301, 0x00000075, + 0x0000031b, 0x00000301, 0x00000055, 0x0000031b, + 0x00000300, 0x00000075, 0x0000031b, 0x00000300, + 0x00000055, 0x0000031b, 0x00000309, 0x00000075, + 0x0000031b, 0x00000309, 0x00000055, 0x0000031b, + 0x00000303, 0x00000075, 0x0000031b, 0x00000303, + 0x00000055, 0x0000031b, 0x00000323, 0x00000075, + 0x0000031b, 0x00000323, 0x00000059, 0x00000300, + 0x00000079, 0x00000300, 0x00000059, 0x00000323, + 0x00000079, 0x00000323, 0x00000059, 0x00000309, + 0x00000079, 0x00000309, 0x00000059, 0x00000303, + 0x00000079, 0x00000303, 0x000003b1, 0x00000313, + 0x000003b1, 0x00000314, 0x000003b1, 0x00000313, + 0x00000300, 0x000003b1, 0x00000314, 0x00000300, + 0x000003b1, 0x00000313, 0x00000301, 0x000003b1, + 0x00000314, 0x00000301, 0x000003b1, 0x00000313, + 0x00000342, 0x000003b1, 0x00000314, 0x00000342, + 0x00000391, 0x00000313, 0x00000391, 0x00000314, + 0x00000391, 0x00000313, 0x00000300, 0x00000391, + 0x00000314, 0x00000300, 0x00000391, 0x00000313, + 0x00000301, 0x00000391, 0x00000314, 0x00000301, + 0x00000391, 0x00000313, 0x00000342, 0x00000391, + 0x00000314, 0x00000342, 0x000003b5, 0x00000313, + 0x000003b5, 0x00000314, 0x000003b5, 0x00000313, + 0x00000300, 0x000003b5, 0x00000314, 0x00000300, + 0x000003b5, 0x00000313, 0x00000301, 0x000003b5, + 0x00000314, 0x00000301, 0x00000395, 0x00000313, + 0x00000395, 0x00000314, 0x00000395, 0x00000313, + 0x00000300, 0x00000395, 0x00000314, 0x00000300, + 0x00000395, 0x00000313, 0x00000301, 0x00000395, + 0x00000314, 0x00000301, 0x000003b7, 0x00000313, + 0x000003b7, 0x00000314, 0x000003b7, 0x00000313, + 0x00000300, 0x000003b7, 0x00000314, 0x00000300, + 0x000003b7, 0x00000313, 0x00000301, 0x000003b7, + 0x00000314, 0x00000301, 0x000003b7, 0x00000313, + 0x00000342, 0x000003b7, 0x00000314, 0x00000342, + 0x00000397, 0x00000313, 0x00000397, 0x00000314, + 0x00000397, 0x00000313, 0x00000300, 0x00000397, + 0x00000314, 0x00000300, 0x00000397, 0x00000313, + 0x00000301, 0x00000397, 0x00000314, 0x00000301, + 0x00000397, 0x00000313, 0x00000342, 0x00000397, + 0x00000314, 0x00000342, 0x000003b9, 0x00000313, + 0x000003b9, 0x00000314, 0x000003b9, 0x00000313, + 0x00000300, 0x000003b9, 0x00000314, 0x00000300, + 0x000003b9, 0x00000313, 0x00000301, 0x000003b9, + 0x00000314, 0x00000301, 0x000003b9, 0x00000313, + 0x00000342, 0x000003b9, 0x00000314, 0x00000342, + 0x00000399, 0x00000313, 0x00000399, 0x00000314, + 0x00000399, 0x00000313, 0x00000300, 0x00000399, + 0x00000314, 0x00000300, 0x00000399, 0x00000313, + 0x00000301, 0x00000399, 0x00000314, 0x00000301, + 0x00000399, 0x00000313, 0x00000342, 0x00000399, + 0x00000314, 0x00000342, 0x000003bf, 0x00000313, + 0x000003bf, 0x00000314, 0x000003bf, 0x00000313, + 0x00000300, 0x000003bf, 0x00000314, 0x00000300, + 0x000003bf, 0x00000313, 0x00000301, 0x000003bf, + 0x00000314, 0x00000301, 0x0000039f, 0x00000313, + 0x0000039f, 0x00000314, 0x0000039f, 0x00000313, + 0x00000300, 0x0000039f, 0x00000314, 0x00000300, + 0x0000039f, 0x00000313, 0x00000301, 0x0000039f, + 0x00000314, 0x00000301, 0x000003c5, 0x00000313, + 0x000003c5, 0x00000314, 0x000003c5, 0x00000313, + 0x00000300, 0x000003c5, 0x00000314, 0x00000300, + 0x000003c5, 0x00000313, 0x00000301, 0x000003c5, + 0x00000314, 0x00000301, 0x000003c5, 0x00000313, + 0x00000342, 0x000003c5, 0x00000314, 0x00000342, + 0x000003a5, 0x00000314, 0x000003a5, 0x00000314, + 0x00000300, 0x000003a5, 0x00000314, 0x00000301, + 0x000003a5, 0x00000314, 0x00000342, 0x000003c9, + 0x00000313, 0x000003c9, 0x00000314, 0x000003c9, + 0x00000313, 0x00000300, 0x000003c9, 0x00000314, + 0x00000300, 0x000003c9, 0x00000313, 0x00000301, + 0x000003c9, 0x00000314, 0x00000301, 0x000003c9, + 0x00000313, 0x00000342, 0x000003c9, 0x00000314, + 0x00000342, 0x000003a9, 0x00000313, 0x000003a9, + 0x00000314, 0x000003a9, 0x00000313, 0x00000300, + 0x000003a9, 0x00000314, 0x00000300, 0x000003a9, + 0x00000313, 0x00000301, 0x000003a9, 0x00000314, + 0x00000301, 0x000003a9, 0x00000313, 0x00000342, + 0x000003a9, 0x00000314, 0x00000342, 0x000003b1, + 0x00000300, 0x000003b1, 0x00000301, 0x000003b5, + 0x00000300, 0x000003b5, 0x00000301, 0x000003b7, + 0x00000300, 0x000003b7, 0x00000301, 0x000003b9, + 0x00000300, 0x000003b9, 0x00000301, 0x000003bf, + 0x00000300, 0x000003bf, 0x00000301, 0x000003c5, + 0x00000300, 0x000003c5, 0x00000301, 0x000003c9, + 0x00000300, 0x000003c9, 0x00000301, 0x000003b1, + 0x00000313, 0x00000345, 0x000003b1, 0x00000314, + 0x00000345, 0x000003b1, 0x00000313, 0x00000300, + 0x00000345, 0x000003b1, 0x00000314, 0x00000300, + 0x00000345, 0x000003b1, 0x00000313, 0x00000301, + 0x00000345, 0x000003b1, 0x00000314, 0x00000301, + 0x00000345, 0x000003b1, 0x00000313, 0x00000342, + 0x00000345, 0x000003b1, 0x00000314, 0x00000342, + 0x00000345, 0x00000391, 0x00000313, 0x00000345, + 0x00000391, 0x00000314, 0x00000345, 0x00000391, + 0x00000313, 0x00000300, 0x00000345, 0x00000391, + 0x00000314, 0x00000300, 0x00000345, 0x00000391, + 0x00000313, 0x00000301, 0x00000345, 0x00000391, + 0x00000314, 0x00000301, 0x00000345, 0x00000391, + 0x00000313, 0x00000342, 0x00000345, 0x00000391, + 0x00000314, 0x00000342, 0x00000345, 0x000003b7, + 0x00000313, 0x00000345, 0x000003b7, 0x00000314, + 0x00000345, 0x000003b7, 0x00000313, 0x00000300, + 0x00000345, 0x000003b7, 0x00000314, 0x00000300, + 0x00000345, 0x000003b7, 0x00000313, 0x00000301, + 0x00000345, 0x000003b7, 0x00000314, 0x00000301, + 0x00000345, 0x000003b7, 0x00000313, 0x00000342, + 0x00000345, 0x000003b7, 0x00000314, 0x00000342, + 0x00000345, 0x00000397, 0x00000313, 0x00000345, + 0x00000397, 0x00000314, 0x00000345, 0x00000397, + 0x00000313, 0x00000300, 0x00000345, 0x00000397, + 0x00000314, 0x00000300, 0x00000345, 0x00000397, + 0x00000313, 0x00000301, 0x00000345, 0x00000397, + 0x00000314, 0x00000301, 0x00000345, 0x00000397, + 0x00000313, 0x00000342, 0x00000345, 0x00000397, + 0x00000314, 0x00000342, 0x00000345, 0x000003c9, + 0x00000313, 0x00000345, 0x000003c9, 0x00000314, + 0x00000345, 0x000003c9, 0x00000313, 0x00000300, + 0x00000345, 0x000003c9, 0x00000314, 0x00000300, + 0x00000345, 0x000003c9, 0x00000313, 0x00000301, + 0x00000345, 0x000003c9, 0x00000314, 0x00000301, + 0x00000345, 0x000003c9, 0x00000313, 0x00000342, + 0x00000345, 0x000003c9, 0x00000314, 0x00000342, + 0x00000345, 0x000003a9, 0x00000313, 0x00000345, + 0x000003a9, 0x00000314, 0x00000345, 0x000003a9, + 0x00000313, 0x00000300, 0x00000345, 0x000003a9, + 0x00000314, 0x00000300, 0x00000345, 0x000003a9, + 0x00000313, 0x00000301, 0x00000345, 0x000003a9, + 0x00000314, 0x00000301, 0x00000345, 0x000003a9, + 0x00000313, 0x00000342, 0x00000345, 0x000003a9, + 0x00000314, 0x00000342, 0x00000345, 0x000003b1, + 0x00000306, 0x000003b1, 0x00000304, 0x000003b1, + 0x00000300, 0x00000345, 0x000003b1, 0x00000345, + 0x000003b1, 0x00000301, 0x00000345, 0x000003b1, + 0x00000342, 0x000003b1, 0x00000342, 0x00000345, + 0x00000391, 0x00000306, 0x00000391, 0x00000304, + 0x00000391, 0x00000300, 0x00000391, 0x00000301, + 0x00000391, 0x00000345, 0x000003b9, 0x000000a8, + 0x00000342, 0x000003b7, 0x00000300, 0x00000345, + 0x000003b7, 0x00000345, 0x000003b7, 0x00000301, + 0x00000345, 0x000003b7, 0x00000342, 0x000003b7, + 0x00000342, 0x00000345, 0x00000395, 0x00000300, + 0x00000395, 0x00000301, 0x00000397, 0x00000300, + 0x00000397, 0x00000301, 0x00000397, 0x00000345, + 0x00001fbf, 0x00000300, 0x00001fbf, 0x00000301, + 0x00001fbf, 0x00000342, 0x000003b9, 0x00000306, + 0x000003b9, 0x00000304, 0x000003b9, 0x00000308, + 0x00000300, 0x000003b9, 0x00000308, 0x00000301, + 0x000003b9, 0x00000342, 0x000003b9, 0x00000308, + 0x00000342, 0x00000399, 0x00000306, 0x00000399, + 0x00000304, 0x00000399, 0x00000300, 0x00000399, + 0x00000301, 0x00001ffe, 0x00000300, 0x00001ffe, + 0x00000301, 0x00001ffe, 0x00000342, 0x000003c5, + 0x00000306, 0x000003c5, 0x00000304, 0x000003c5, + 0x00000308, 0x00000300, 0x000003c5, 0x00000308, + 0x00000301, 0x000003c1, 0x00000313, 0x000003c1, + 0x00000314, 0x000003c5, 0x00000342, 0x000003c5, + 0x00000308, 0x00000342, 0x000003a5, 0x00000306, + 0x000003a5, 0x00000304, 0x000003a5, 0x00000300, + 0x000003a5, 0x00000301, 0x000003a1, 0x00000314, + 0x000000a8, 0x00000300, 0x000000a8, 0x00000301, + 0x00000060, 0x000003c9, 0x00000300, 0x00000345, + 0x000003c9, 0x00000345, 0x000003c9, 0x00000301, + 0x00000345, 0x000003c9, 0x00000342, 0x000003c9, + 0x00000342, 0x00000345, 0x0000039f, 0x00000300, + 0x0000039f, 0x00000301, 0x000003a9, 0x00000300, + 0x000003a9, 0x00000301, 0x000003a9, 0x00000345, + 0x000000b4, 0x00002002, 0x00002003, 0x000003a9, + 0x0000004b, 0x00000041, 0x0000030a, 0x00002190, + 0x00000338, 0x00002192, 0x00000338, 0x00002194, + 0x00000338, 0x000021d0, 0x00000338, 0x000021d4, + 0x00000338, 0x000021d2, 0x00000338, 0x00002203, + 0x00000338, 0x00002208, 0x00000338, 0x0000220b, + 0x00000338, 0x00002223, 0x00000338, 0x00002225, + 0x00000338, 0x0000223c, 0x00000338, 0x00002243, + 0x00000338, 0x00002245, 0x00000338, 0x00002248, + 0x00000338, 0x0000003d, 0x00000338, 0x00002261, + 0x00000338, 0x0000224d, 0x00000338, 0x0000003c, + 0x00000338, 0x0000003e, 0x00000338, 0x00002264, + 0x00000338, 0x00002265, 0x00000338, 0x00002272, + 0x00000338, 0x00002273, 0x00000338, 0x00002276, + 0x00000338, 0x00002277, 0x00000338, 0x0000227a, + 0x00000338, 0x0000227b, 0x00000338, 0x00002282, + 0x00000338, 0x00002283, 0x00000338, 0x00002286, + 0x00000338, 0x00002287, 0x00000338, 0x000022a2, + 0x00000338, 0x000022a8, 0x00000338, 0x000022a9, + 0x00000338, 0x000022ab, 0x00000338, 0x0000227c, + 0x00000338, 0x0000227d, 0x00000338, 0x00002291, + 0x00000338, 0x00002292, 0x00000338, 0x000022b2, + 0x00000338, 0x000022b3, 0x00000338, 0x000022b4, + 0x00000338, 0x000022b5, 0x00000338, 0x00003008, + 0x00003009, 0x00002add, 0x00000338, 0x0000304b, + 0x00003099, 0x0000304d, 0x00003099, 0x0000304f, + 0x00003099, 0x00003051, 0x00003099, 0x00003053, + 0x00003099, 0x00003055, 0x00003099, 0x00003057, + 0x00003099, 0x00003059, 0x00003099, 0x0000305b, + 0x00003099, 0x0000305d, 0x00003099, 0x0000305f, + 0x00003099, 0x00003061, 0x00003099, 0x00003064, + 0x00003099, 0x00003066, 0x00003099, 0x00003068, + 0x00003099, 0x0000306f, 0x00003099, 0x0000306f, + 0x0000309a, 0x00003072, 0x00003099, 0x00003072, + 0x0000309a, 0x00003075, 0x00003099, 0x00003075, + 0x0000309a, 0x00003078, 0x00003099, 0x00003078, + 0x0000309a, 0x0000307b, 0x00003099, 0x0000307b, + 0x0000309a, 0x00003046, 0x00003099, 0x0000309d, + 0x00003099, 0x000030ab, 0x00003099, 0x000030ad, + 0x00003099, 0x000030af, 0x00003099, 0x000030b1, + 0x00003099, 0x000030b3, 0x00003099, 0x000030b5, + 0x00003099, 0x000030b7, 0x00003099, 0x000030b9, + 0x00003099, 0x000030bb, 0x00003099, 0x000030bd, + 0x00003099, 0x000030bf, 0x00003099, 0x000030c1, + 0x00003099, 0x000030c4, 0x00003099, 0x000030c6, + 0x00003099, 0x000030c8, 0x00003099, 0x000030cf, + 0x00003099, 0x000030cf, 0x0000309a, 0x000030d2, + 0x00003099, 0x000030d2, 0x0000309a, 0x000030d5, + 0x00003099, 0x000030d5, 0x0000309a, 0x000030d8, + 0x00003099, 0x000030d8, 0x0000309a, 0x000030db, + 0x00003099, 0x000030db, 0x0000309a, 0x000030a6, + 0x00003099, 0x000030ef, 0x00003099, 0x000030f0, + 0x00003099, 0x000030f1, 0x00003099, 0x000030f2, + 0x00003099, 0x000030fd, 0x00003099, 0x00008eca, + 0x00008cc8, 0x00006ed1, 0x00004e32, 0x000053e5, + 0x00009f9c, 0x00009f9c, 0x00005951, 0x000091d1, + 0x00005587, 0x00005948, 0x000061f6, 0x00007669, + 0x00007f85, 0x0000863f, 0x000087ba, 0x000088f8, + 0x0000908f, 0x00006a02, 0x00006d1b, 0x000070d9, + 0x000073de, 0x0000843d, 0x0000916a, 0x000099f1, + 0x00004e82, 0x00005375, 0x00006b04, 0x0000721b, + 0x0000862d, 0x00009e1e, 0x00005d50, 0x00006feb, + 0x000085cd, 0x00008964, 0x000062c9, 0x000081d8, + 0x0000881f, 0x00005eca, 0x00006717, 0x00006d6a, + 0x000072fc, 0x000090ce, 0x00004f86, 0x000051b7, + 0x000052de, 0x000064c4, 0x00006ad3, 0x00007210, + 0x000076e7, 0x00008001, 0x00008606, 0x0000865c, + 0x00008def, 0x00009732, 0x00009b6f, 0x00009dfa, + 0x0000788c, 0x0000797f, 0x00007da0, 0x000083c9, + 0x00009304, 0x00009e7f, 0x00008ad6, 0x000058df, + 0x00005f04, 0x00007c60, 0x0000807e, 0x00007262, + 0x000078ca, 0x00008cc2, 0x000096f7, 0x000058d8, + 0x00005c62, 0x00006a13, 0x00006dda, 0x00006f0f, + 0x00007d2f, 0x00007e37, 0x0000964b, 0x000052d2, + 0x0000808b, 0x000051dc, 0x000051cc, 0x00007a1c, + 0x00007dbe, 0x000083f1, 0x00009675, 0x00008b80, + 0x000062cf, 0x00006a02, 0x00008afe, 0x00004e39, + 0x00005be7, 0x00006012, 0x00007387, 0x00007570, + 0x00005317, 0x000078fb, 0x00004fbf, 0x00005fa9, + 0x00004e0d, 0x00006ccc, 0x00006578, 0x00007d22, + 0x000053c3, 0x0000585e, 0x00007701, 0x00008449, + 0x00008aaa, 0x00006bba, 0x00008fb0, 0x00006c88, + 0x000062fe, 0x000082e5, 0x000063a0, 0x00007565, + 0x00004eae, 0x00005169, 0x000051c9, 0x00006881, + 0x00007ce7, 0x0000826f, 0x00008ad2, 0x000091cf, + 0x000052f5, 0x00005442, 0x00005973, 0x00005eec, + 0x000065c5, 0x00006ffe, 0x0000792a, 0x000095ad, + 0x00009a6a, 0x00009e97, 0x00009ece, 0x0000529b, + 0x000066c6, 0x00006b77, 0x00008f62, 0x00005e74, + 0x00006190, 0x00006200, 0x0000649a, 0x00006f23, + 0x00007149, 0x00007489, 0x000079ca, 0x00007df4, + 0x0000806f, 0x00008f26, 0x000084ee, 0x00009023, + 0x0000934a, 0x00005217, 0x000052a3, 0x000054bd, + 0x000070c8, 0x000088c2, 0x00008aaa, 0x00005ec9, + 0x00005ff5, 0x0000637b, 0x00006bae, 0x00007c3e, + 0x00007375, 0x00004ee4, 0x000056f9, 0x00005be7, + 0x00005dba, 0x0000601c, 0x000073b2, 0x00007469, + 0x00007f9a, 0x00008046, 0x00009234, 0x000096f6, + 0x00009748, 0x00009818, 0x00004f8b, 0x000079ae, + 0x000091b4, 0x000096b8, 0x000060e1, 0x00004e86, + 0x000050da, 0x00005bee, 0x00005c3f, 0x00006599, + 0x00006a02, 0x000071ce, 0x00007642, 0x000084fc, + 0x0000907c, 0x00009f8d, 0x00006688, 0x0000962e, + 0x00005289, 0x0000677b, 0x000067f3, 0x00006d41, + 0x00006e9c, 0x00007409, 0x00007559, 0x0000786b, + 0x00007d10, 0x0000985e, 0x0000516d, 0x0000622e, + 0x00009678, 0x0000502b, 0x00005d19, 0x00006dea, + 0x00008f2a, 0x00005f8b, 0x00006144, 0x00006817, + 0x00007387, 0x00009686, 0x00005229, 0x0000540f, + 0x00005c65, 0x00006613, 0x0000674e, 0x000068a8, + 0x00006ce5, 0x00007406, 0x000075e2, 0x00007f79, + 0x000088cf, 0x000088e1, 0x000091cc, 0x000096e2, + 0x0000533f, 0x00006eba, 0x0000541d, 0x000071d0, + 0x00007498, 0x000085fa, 0x000096a3, 0x00009c57, + 0x00009e9f, 0x00006797, 0x00006dcb, 0x000081e8, + 0x00007acb, 0x00007b20, 0x00007c92, 0x000072c0, + 0x00007099, 0x00008b58, 0x00004ec0, 0x00008336, + 0x0000523a, 0x00005207, 0x00005ea6, 0x000062d3, + 0x00007cd6, 0x00005b85, 0x00006d1e, 0x000066b4, + 0x00008f3b, 0x0000884c, 0x0000964d, 0x0000898b, + 0x00005ed3, 0x00005140, 0x000055c0, 0x0000585a, + 0x00006674, 0x000051de, 0x0000732a, 0x000076ca, + 0x0000793c, 0x0000795e, 0x00007965, 0x0000798f, + 0x00009756, 0x00007cbe, 0x00007fbd, 0x00008612, + 0x00008af8, 0x00009038, 0x000090fd, 0x000098ef, + 0x000098fc, 0x00009928, 0x00009db4, 0x00004fae, + 0x000050e7, 0x0000514d, 0x000052c9, 0x000052e4, + 0x00005351, 0x0000559d, 0x00005606, 0x00005668, + 0x00005840, 0x000058a8, 0x00005c64, 0x00005c6e, + 0x00006094, 0x00006168, 0x0000618e, 0x000061f2, + 0x0000654f, 0x000065e2, 0x00006691, 0x00006885, + 0x00006d77, 0x00006e1a, 0x00006f22, 0x0000716e, + 0x0000722b, 0x00007422, 0x00007891, 0x0000793e, + 0x00007949, 0x00007948, 0x00007950, 0x00007956, + 0x0000795d, 0x0000798d, 0x0000798e, 0x00007a40, + 0x00007a81, 0x00007bc0, 0x00007df4, 0x00007e09, + 0x00007e41, 0x00007f72, 0x00008005, 0x000081ed, + 0x00008279, 0x00008279, 0x00008457, 0x00008910, + 0x00008996, 0x00008b01, 0x00008b39, 0x00008cd3, + 0x00008d08, 0x00008fb6, 0x00009038, 0x000096e3, + 0x000097ff, 0x0000983b, 0x000005d9, 0x000005b4, + 0x000005f2, 0x000005b7, 0x000005e9, 0x000005c1, + 0x000005e9, 0x000005c2, 0x000005e9, 0x000005bc, + 0x000005c1, 0x000005e9, 0x000005bc, 0x000005c2, + 0x000005d0, 0x000005b7, 0x000005d0, 0x000005b8, + 0x000005d0, 0x000005bc, 0x000005d1, 0x000005bc, + 0x000005d2, 0x000005bc, 0x000005d3, 0x000005bc, + 0x000005d4, 0x000005bc, 0x000005d5, 0x000005bc, + 0x000005d6, 0x000005bc, 0x000005d8, 0x000005bc, + 0x000005d9, 0x000005bc, 0x000005da, 0x000005bc, + 0x000005db, 0x000005bc, 0x000005dc, 0x000005bc, + 0x000005de, 0x000005bc, 0x000005e0, 0x000005bc, + 0x000005e1, 0x000005bc, 0x000005e3, 0x000005bc, + 0x000005e4, 0x000005bc, 0x000005e6, 0x000005bc, + 0x000005e7, 0x000005bc, 0x000005e8, 0x000005bc, + 0x000005e9, 0x000005bc, 0x000005ea, 0x000005bc, + 0x000005d5, 0x000005b9, 0x000005d1, 0x000005bf, + 0x000005db, 0x000005bf, 0x000005e4, 0x000005bf, + 0x0001d157, 0x0001d165, 0x0001d158, 0x0001d165, + 0x0001d158, 0x0001d165, 0x0001d16e, 0x0001d158, + 0x0001d165, 0x0001d16f, 0x0001d158, 0x0001d165, + 0x0001d170, 0x0001d158, 0x0001d165, 0x0001d171, + 0x0001d158, 0x0001d165, 0x0001d172, 0x0001d1b9, + 0x0001d165, 0x0001d1ba, 0x0001d165, 0x0001d1b9, + 0x0001d165, 0x0001d16e, 0x0001d1ba, 0x0001d165, + 0x0001d16e, 0x0001d1b9, 0x0001d165, 0x0001d16f, + 0x0001d1ba, 0x0001d165, 0x0001d16f, 0x00004e3d, + 0x00004e38, 0x00004e41, 0x00020122, 0x00004f60, + 0x00004fae, 0x00004fbb, 0x00005002, 0x0000507a, + 0x00005099, 0x000050e7, 0x000050cf, 0x0000349e, + 0x0002063a, 0x0000514d, 0x00005154, 0x00005164, + 0x00005177, 0x0002051c, 0x000034b9, 0x00005167, + 0x0000518d, 0x0002054b, 0x00005197, 0x000051a4, + 0x00004ecc, 0x000051ac, 0x000051b5, 0x000291df, + 0x000051f5, 0x00005203, 0x000034df, 0x0000523b, + 0x00005246, 0x00005272, 0x00005277, 0x00003515, + 0x000052c7, 0x000052c9, 0x000052e4, 0x000052fa, + 0x00005305, 0x00005306, 0x00005317, 0x00005349, + 0x00005351, 0x0000535a, 0x00005373, 0x0000537d, + 0x0000537f, 0x0000537f, 0x0000537f, 0x00020a2c, + 0x00007070, 0x000053ca, 0x000053df, 0x00020b63, + 0x000053eb, 0x000053f1, 0x00005406, 0x0000549e, + 0x00005438, 0x00005448, 0x00005468, 0x000054a2, + 0x000054f6, 0x00005510, 0x00005553, 0x00005563, + 0x00005584, 0x00005584, 0x00005599, 0x000055ab, + 0x000055b3, 0x000055c2, 0x00005716, 0x00005606, + 0x00005717, 0x00005651, 0x00005674, 0x00005207, + 0x000058ee, 0x000057ce, 0x000057f4, 0x0000580d, + 0x0000578b, 0x00005832, 0x00005831, 0x000058ac, + 0x000214e4, 0x000058f2, 0x000058f7, 0x00005906, + 0x0000591a, 0x00005922, 0x00005962, 0x000216a8, + 0x000216ea, 0x000059ec, 0x00005a1b, 0x00005a27, + 0x000059d8, 0x00005a66, 0x000036ee, 0x0002136a, + 0x00005b08, 0x00005b3e, 0x00005b3e, 0x000219c8, + 0x00005bc3, 0x00005bd8, 0x00005be7, 0x00005bf3, + 0x00021b18, 0x00005bff, 0x00005c06, 0x00005f33, + 0x00005c22, 0x00003781, 0x00005c60, 0x00005c6e, + 0x00005cc0, 0x00005c8d, 0x00021de4, 0x00005d43, + 0x00021de6, 0x00005d6e, 0x00005d6b, 0x00005d7c, + 0x00005de1, 0x00005de2, 0x0000382f, 0x00005dfd, + 0x00005e28, 0x00005e3d, 0x00005e69, 0x00003862, + 0x00022183, 0x0000387c, 0x00005eb0, 0x00005eb3, + 0x00005eb6, 0x00005eca, 0x0002a392, 0x00005efe, + 0x00022331, 0x00022331, 0x00008201, 0x00005f22, + 0x00005f22, 0x000038c7, 0x000232b8, 0x000261da, + 0x00005f62, 0x00005f6b, 0x000038e3, 0x00005f9a, + 0x00005fcd, 0x00005fd7, 0x00005ff9, 0x00006081, + 0x0000393a, 0x0000391c, 0x00006094, 0x000226d4, + 0x000060c7, 0x00006148, 0x0000614c, 0x0000614e, + 0x0000614c, 0x0000617a, 0x0000618e, 0x000061b2, + 0x000061a4, 0x000061af, 0x000061de, 0x000061f2, + 0x000061f6, 0x00006210, 0x0000621b, 0x0000625d, + 0x000062b1, 0x000062d4, 0x00006350, 0x00022b0c, + 0x0000633d, 0x000062fc, 0x00006368, 0x00006383, + 0x000063e4, 0x00022bf1, 0x00006422, 0x000063c5, + 0x000063a9, 0x00003a2e, 0x00006469, 0x0000647e, + 0x0000649d, 0x00006477, 0x00003a6c, 0x0000654f, + 0x0000656c, 0x0002300a, 0x000065e3, 0x000066f8, + 0x00006649, 0x00003b19, 0x00006691, 0x00003b08, + 0x00003ae4, 0x00005192, 0x00005195, 0x00006700, + 0x0000669c, 0x000080ad, 0x000043d9, 0x00006717, + 0x0000671b, 0x00006721, 0x0000675e, 0x00006753, + 0x000233c3, 0x00003b49, 0x000067fa, 0x00006785, + 0x00006852, 0x00006885, 0x0002346d, 0x0000688e, + 0x0000681f, 0x00006914, 0x00003b9d, 0x00006942, + 0x000069a3, 0x000069ea, 0x00006aa8, 0x000236a3, + 0x00006adb, 0x00003c18, 0x00006b21, 0x000238a7, + 0x00006b54, 0x00003c4e, 0x00006b72, 0x00006b9f, + 0x00006bba, 0x00006bbb, 0x00023a8d, 0x00021d0b, + 0x00023afa, 0x00006c4e, 0x00023cbc, 0x00006cbf, + 0x00006ccd, 0x00006c67, 0x00006d16, 0x00006d3e, + 0x00006d77, 0x00006d41, 0x00006d69, 0x00006d78, + 0x00006d85, 0x00023d1e, 0x00006d34, 0x00006e2f, + 0x00006e6e, 0x00003d33, 0x00006ecb, 0x00006ec7, + 0x00023ed1, 0x00006df9, 0x00006f6e, 0x00023f5e, + 0x00023f8e, 0x00006fc6, 0x00007039, 0x0000701e, + 0x0000701b, 0x00003d96, 0x0000704a, 0x0000707d, + 0x00007077, 0x000070ad, 0x00020525, 0x00007145, + 0x00024263, 0x0000719c, 0x000043ab, 0x00007228, + 0x00007235, 0x00007250, 0x00024608, 0x00007280, + 0x00007295, 0x00024735, 0x00024814, 0x0000737a, + 0x0000738b, 0x00003eac, 0x000073a5, 0x00003eb8, + 0x00003eb8, 0x00007447, 0x0000745c, 0x00007471, + 0x00007485, 0x000074ca, 0x00003f1b, 0x00007524, + 0x00024c36, 0x0000753e, 0x00024c92, 0x00007570, + 0x0002219f, 0x00007610, 0x00024fa1, 0x00024fb8, + 0x00025044, 0x00003ffc, 0x00004008, 0x000076f4, + 0x000250f3, 0x000250f2, 0x00025119, 0x00025133, + 0x0000771e, 0x0000771f, 0x0000771f, 0x0000774a, + 0x00004039, 0x0000778b, 0x00004046, 0x00004096, + 0x0002541d, 0x0000784e, 0x0000788c, 0x000078cc, + 0x000040e3, 0x00025626, 0x00007956, 0x0002569a, + 0x000256c5, 0x0000798f, 0x000079eb, 0x0000412f, + 0x00007a40, 0x00007a4a, 0x00007a4f, 0x0002597c, + 0x00025aa7, 0x00025aa7, 0x00007aae, 0x00004202, + 0x00025bab, 0x00007bc6, 0x00007bc9, 0x00004227, + 0x00025c80, 0x00007cd2, 0x000042a0, 0x00007ce8, + 0x00007ce3, 0x00007d00, 0x00025f86, 0x00007d63, + 0x00004301, 0x00007dc7, 0x00007e02, 0x00007e45, + 0x00004334, 0x00026228, 0x00026247, 0x00004359, + 0x000262d9, 0x00007f7a, 0x0002633e, 0x00007f95, + 0x00007ffa, 0x00008005, 0x000264da, 0x00026523, + 0x00008060, 0x000265a8, 0x00008070, 0x0002335f, + 0x000043d5, 0x000080b2, 0x00008103, 0x0000440b, + 0x0000813e, 0x00005ab5, 0x000267a7, 0x000267b5, + 0x00023393, 0x0002339c, 0x00008201, 0x00008204, + 0x00008f9e, 0x0000446b, 0x00008291, 0x0000828b, + 0x0000829d, 0x000052b3, 0x000082b1, 0x000082b3, + 0x000082bd, 0x000082e6, 0x00026b3c, 0x000082e5, + 0x0000831d, 0x00008363, 0x000083ad, 0x00008323, + 0x000083bd, 0x000083e7, 0x00008457, 0x00008353, + 0x000083ca, 0x000083cc, 0x000083dc, 0x00026c36, + 0x00026d6b, 0x00026cd5, 0x0000452b, 0x000084f1, + 0x000084f3, 0x00008516, 0x000273ca, 0x00008564, + 0x00026f2c, 0x0000455d, 0x00004561, 0x00026fb1, + 0x000270d2, 0x0000456b, 0x00008650, 0x0000865c, + 0x00008667, 0x00008669, 0x000086a9, 0x00008688, + 0x0000870e, 0x000086e2, 0x00008779, 0x00008728, + 0x0000876b, 0x00008786, 0x00004d57, 0x000087e1, + 0x00008801, 0x000045f9, 0x00008860, 0x00008863, + 0x00027667, 0x000088d7, 0x000088de, 0x00004635, + 0x000088fa, 0x000034bb, 0x000278ae, 0x00027966, + 0x000046be, 0x000046c7, 0x00008aa0, 0x00008aed, + 0x00008b8a, 0x00008c55, 0x00027ca8, 0x00008cab, + 0x00008cc1, 0x00008d1b, 0x00008d77, 0x00027f2f, + 0x00020804, 0x00008dcb, 0x00008dbc, 0x00008df0, + 0x000208de, 0x00008ed4, 0x00008f38, 0x000285d2, + 0x000285ed, 0x00009094, 0x000090f1, 0x00009111, + 0x0002872e, 0x0000911b, 0x00009238, 0x000092d7, + 0x000092d8, 0x0000927c, 0x000093f9, 0x00009415, + 0x00028bfa, 0x0000958b, 0x00004995, 0x000095b7, + 0x00028d77, 0x000049e6, 0x000096c3, 0x00005db2, + 0x00009723, 0x00029145, 0x0002921a, 0x00004a6e, + 0x00004a76, 0x000097e0, 0x0002940a, 0x00004ab2, + 0x00029496, 0x0000980b, 0x0000980b, 0x00009829, + 0x000295b6, 0x000098e2, 0x00004b33, 0x00009929, + 0x000099a7, 0x000099c2, 0x000099fe, 0x00004bce, + 0x00029b30, 0x00009b12, 0x00009c40, 0x00009cfd, + 0x00004cce, 0x00004ced, 0x00009d67, 0x0002a0ce, + 0x00004cf8, 0x0002a105, 0x0002a20e, 0x0002a291, + 0x00009ebb, 0x00004d56, 0x00009ef9, 0x00009efe, + 0x00009f05, 0x00009f0f, 0x00009f16, 0x00009f3b, + 0x0002a600 +}; + +static const ac_uint4 _uckdcmp_size = 10282; + +static const ac_uint4 _uckdcmp_nodes[] = { + 0x000000a0, 0x00000000, + 0x000000a8, 0x00000001, + 0x000000aa, 0x00000003, + 0x000000af, 0x00000004, + 0x000000b2, 0x00000006, + 0x000000b3, 0x00000007, + 0x000000b4, 0x00000008, + 0x000000b5, 0x0000000a, + 0x000000b8, 0x0000000b, + 0x000000b9, 0x0000000d, + 0x000000ba, 0x0000000e, + 0x000000bc, 0x0000000f, + 0x000000bd, 0x00000012, + 0x000000be, 0x00000015, + 0x000000c0, 0x00000018, + 0x000000c1, 0x0000001a, + 0x000000c2, 0x0000001c, + 0x000000c3, 0x0000001e, + 0x000000c4, 0x00000020, + 0x000000c5, 0x00000022, + 0x000000c7, 0x00000024, + 0x000000c8, 0x00000026, + 0x000000c9, 0x00000028, + 0x000000ca, 0x0000002a, + 0x000000cb, 0x0000002c, + 0x000000cc, 0x0000002e, + 0x000000cd, 0x00000030, + 0x000000ce, 0x00000032, + 0x000000cf, 0x00000034, + 0x000000d1, 0x00000036, + 0x000000d2, 0x00000038, + 0x000000d3, 0x0000003a, + 0x000000d4, 0x0000003c, + 0x000000d5, 0x0000003e, + 0x000000d6, 0x00000040, + 0x000000d9, 0x00000042, + 0x000000da, 0x00000044, + 0x000000db, 0x00000046, + 0x000000dc, 0x00000048, + 0x000000dd, 0x0000004a, + 0x000000e0, 0x0000004c, + 0x000000e1, 0x0000004e, + 0x000000e2, 0x00000050, + 0x000000e3, 0x00000052, + 0x000000e4, 0x00000054, + 0x000000e5, 0x00000056, + 0x000000e7, 0x00000058, + 0x000000e8, 0x0000005a, + 0x000000e9, 0x0000005c, + 0x000000ea, 0x0000005e, + 0x000000eb, 0x00000060, + 0x000000ec, 0x00000062, + 0x000000ed, 0x00000064, + 0x000000ee, 0x00000066, + 0x000000ef, 0x00000068, + 0x000000f1, 0x0000006a, + 0x000000f2, 0x0000006c, + 0x000000f3, 0x0000006e, + 0x000000f4, 0x00000070, + 0x000000f5, 0x00000072, + 0x000000f6, 0x00000074, + 0x000000f9, 0x00000076, + 0x000000fa, 0x00000078, + 0x000000fb, 0x0000007a, + 0x000000fc, 0x0000007c, + 0x000000fd, 0x0000007e, + 0x000000ff, 0x00000080, + 0x00000100, 0x00000082, + 0x00000101, 0x00000084, + 0x00000102, 0x00000086, + 0x00000103, 0x00000088, + 0x00000104, 0x0000008a, + 0x00000105, 0x0000008c, + 0x00000106, 0x0000008e, + 0x00000107, 0x00000090, + 0x00000108, 0x00000092, + 0x00000109, 0x00000094, + 0x0000010a, 0x00000096, + 0x0000010b, 0x00000098, + 0x0000010c, 0x0000009a, + 0x0000010d, 0x0000009c, + 0x0000010e, 0x0000009e, + 0x0000010f, 0x000000a0, + 0x00000112, 0x000000a2, + 0x00000113, 0x000000a4, + 0x00000114, 0x000000a6, + 0x00000115, 0x000000a8, + 0x00000116, 0x000000aa, + 0x00000117, 0x000000ac, + 0x00000118, 0x000000ae, + 0x00000119, 0x000000b0, + 0x0000011a, 0x000000b2, + 0x0000011b, 0x000000b4, + 0x0000011c, 0x000000b6, + 0x0000011d, 0x000000b8, + 0x0000011e, 0x000000ba, + 0x0000011f, 0x000000bc, + 0x00000120, 0x000000be, + 0x00000121, 0x000000c0, + 0x00000122, 0x000000c2, + 0x00000123, 0x000000c4, + 0x00000124, 0x000000c6, + 0x00000125, 0x000000c8, + 0x00000128, 0x000000ca, + 0x00000129, 0x000000cc, + 0x0000012a, 0x000000ce, + 0x0000012b, 0x000000d0, + 0x0000012c, 0x000000d2, + 0x0000012d, 0x000000d4, + 0x0000012e, 0x000000d6, + 0x0000012f, 0x000000d8, + 0x00000130, 0x000000da, + 0x00000132, 0x000000dc, + 0x00000133, 0x000000de, + 0x00000134, 0x000000e0, + 0x00000135, 0x000000e2, + 0x00000136, 0x000000e4, + 0x00000137, 0x000000e6, + 0x00000139, 0x000000e8, + 0x0000013a, 0x000000ea, + 0x0000013b, 0x000000ec, + 0x0000013c, 0x000000ee, + 0x0000013d, 0x000000f0, + 0x0000013e, 0x000000f2, + 0x0000013f, 0x000000f4, + 0x00000140, 0x000000f6, + 0x00000143, 0x000000f8, + 0x00000144, 0x000000fa, + 0x00000145, 0x000000fc, + 0x00000146, 0x000000fe, + 0x00000147, 0x00000100, + 0x00000148, 0x00000102, + 0x00000149, 0x00000104, + 0x0000014c, 0x00000106, + 0x0000014d, 0x00000108, + 0x0000014e, 0x0000010a, + 0x0000014f, 0x0000010c, + 0x00000150, 0x0000010e, + 0x00000151, 0x00000110, + 0x00000154, 0x00000112, + 0x00000155, 0x00000114, + 0x00000156, 0x00000116, + 0x00000157, 0x00000118, + 0x00000158, 0x0000011a, + 0x00000159, 0x0000011c, + 0x0000015a, 0x0000011e, + 0x0000015b, 0x00000120, + 0x0000015c, 0x00000122, + 0x0000015d, 0x00000124, + 0x0000015e, 0x00000126, + 0x0000015f, 0x00000128, + 0x00000160, 0x0000012a, + 0x00000161, 0x0000012c, + 0x00000162, 0x0000012e, + 0x00000163, 0x00000130, + 0x00000164, 0x00000132, + 0x00000165, 0x00000134, + 0x00000168, 0x00000136, + 0x00000169, 0x00000138, + 0x0000016a, 0x0000013a, + 0x0000016b, 0x0000013c, + 0x0000016c, 0x0000013e, + 0x0000016d, 0x00000140, + 0x0000016e, 0x00000142, + 0x0000016f, 0x00000144, + 0x00000170, 0x00000146, + 0x00000171, 0x00000148, + 0x00000172, 0x0000014a, + 0x00000173, 0x0000014c, + 0x00000174, 0x0000014e, + 0x00000175, 0x00000150, + 0x00000176, 0x00000152, + 0x00000177, 0x00000154, + 0x00000178, 0x00000156, + 0x00000179, 0x00000158, + 0x0000017a, 0x0000015a, + 0x0000017b, 0x0000015c, + 0x0000017c, 0x0000015e, + 0x0000017d, 0x00000160, + 0x0000017e, 0x00000162, + 0x0000017f, 0x00000164, + 0x000001a0, 0x00000165, + 0x000001a1, 0x00000167, + 0x000001af, 0x00000169, + 0x000001b0, 0x0000016b, + 0x000001c4, 0x0000016d, + 0x000001c5, 0x00000170, + 0x000001c6, 0x00000173, + 0x000001c7, 0x00000176, + 0x000001c8, 0x00000178, + 0x000001c9, 0x0000017a, + 0x000001ca, 0x0000017c, + 0x000001cb, 0x0000017e, + 0x000001cc, 0x00000180, + 0x000001cd, 0x00000182, + 0x000001ce, 0x00000184, + 0x000001cf, 0x00000186, + 0x000001d0, 0x00000188, + 0x000001d1, 0x0000018a, + 0x000001d2, 0x0000018c, + 0x000001d3, 0x0000018e, + 0x000001d4, 0x00000190, + 0x000001d5, 0x00000192, + 0x000001d6, 0x00000195, + 0x000001d7, 0x00000198, + 0x000001d8, 0x0000019b, + 0x000001d9, 0x0000019e, + 0x000001da, 0x000001a1, + 0x000001db, 0x000001a4, + 0x000001dc, 0x000001a7, + 0x000001de, 0x000001aa, + 0x000001df, 0x000001ad, + 0x000001e0, 0x000001b0, + 0x000001e1, 0x000001b3, + 0x000001e2, 0x000001b6, + 0x000001e3, 0x000001b8, + 0x000001e6, 0x000001ba, + 0x000001e7, 0x000001bc, + 0x000001e8, 0x000001be, + 0x000001e9, 0x000001c0, + 0x000001ea, 0x000001c2, + 0x000001eb, 0x000001c4, + 0x000001ec, 0x000001c6, + 0x000001ed, 0x000001c9, + 0x000001ee, 0x000001cc, + 0x000001ef, 0x000001ce, + 0x000001f0, 0x000001d0, + 0x000001f1, 0x000001d2, + 0x000001f2, 0x000001d4, + 0x000001f3, 0x000001d6, + 0x000001f4, 0x000001d8, + 0x000001f5, 0x000001da, + 0x000001f8, 0x000001dc, + 0x000001f9, 0x000001de, + 0x000001fa, 0x000001e0, + 0x000001fb, 0x000001e3, + 0x000001fc, 0x000001e6, + 0x000001fd, 0x000001e8, + 0x000001fe, 0x000001ea, + 0x000001ff, 0x000001ec, + 0x00000200, 0x000001ee, + 0x00000201, 0x000001f0, + 0x00000202, 0x000001f2, + 0x00000203, 0x000001f4, + 0x00000204, 0x000001f6, + 0x00000205, 0x000001f8, + 0x00000206, 0x000001fa, + 0x00000207, 0x000001fc, + 0x00000208, 0x000001fe, + 0x00000209, 0x00000200, + 0x0000020a, 0x00000202, + 0x0000020b, 0x00000204, + 0x0000020c, 0x00000206, + 0x0000020d, 0x00000208, + 0x0000020e, 0x0000020a, + 0x0000020f, 0x0000020c, + 0x00000210, 0x0000020e, + 0x00000211, 0x00000210, + 0x00000212, 0x00000212, + 0x00000213, 0x00000214, + 0x00000214, 0x00000216, + 0x00000215, 0x00000218, + 0x00000216, 0x0000021a, + 0x00000217, 0x0000021c, + 0x00000218, 0x0000021e, + 0x00000219, 0x00000220, + 0x0000021a, 0x00000222, + 0x0000021b, 0x00000224, + 0x0000021e, 0x00000226, + 0x0000021f, 0x00000228, + 0x00000226, 0x0000022a, + 0x00000227, 0x0000022c, + 0x00000228, 0x0000022e, + 0x00000229, 0x00000230, + 0x0000022a, 0x00000232, + 0x0000022b, 0x00000235, + 0x0000022c, 0x00000238, + 0x0000022d, 0x0000023b, + 0x0000022e, 0x0000023e, + 0x0000022f, 0x00000240, + 0x00000230, 0x00000242, + 0x00000231, 0x00000245, + 0x00000232, 0x00000248, + 0x00000233, 0x0000024a, + 0x000002b0, 0x0000024c, + 0x000002b1, 0x0000024d, + 0x000002b2, 0x0000024e, + 0x000002b3, 0x0000024f, + 0x000002b4, 0x00000250, + 0x000002b5, 0x00000251, + 0x000002b6, 0x00000252, + 0x000002b7, 0x00000253, + 0x000002b8, 0x00000254, + 0x000002d8, 0x00000255, + 0x000002d9, 0x00000257, + 0x000002da, 0x00000259, + 0x000002db, 0x0000025b, + 0x000002dc, 0x0000025d, + 0x000002dd, 0x0000025f, + 0x000002e0, 0x00000261, + 0x000002e1, 0x00000262, + 0x000002e2, 0x00000263, + 0x000002e3, 0x00000264, + 0x000002e4, 0x00000265, + 0x00000340, 0x00000266, + 0x00000341, 0x00000267, + 0x00000343, 0x00000268, + 0x00000344, 0x00000269, + 0x00000374, 0x0000026b, + 0x0000037a, 0x0000026c, + 0x0000037e, 0x0000026e, + 0x00000384, 0x0000026f, + 0x00000385, 0x00000271, + 0x00000386, 0x00000274, + 0x00000387, 0x00000276, + 0x00000388, 0x00000277, + 0x00000389, 0x00000279, + 0x0000038a, 0x0000027b, + 0x0000038c, 0x0000027d, + 0x0000038e, 0x0000027f, + 0x0000038f, 0x00000281, + 0x00000390, 0x00000283, + 0x000003aa, 0x00000286, + 0x000003ab, 0x00000288, + 0x000003ac, 0x0000028a, + 0x000003ad, 0x0000028c, + 0x000003ae, 0x0000028e, + 0x000003af, 0x00000290, + 0x000003b0, 0x00000292, + 0x000003ca, 0x00000295, + 0x000003cb, 0x00000297, + 0x000003cc, 0x00000299, + 0x000003cd, 0x0000029b, + 0x000003ce, 0x0000029d, + 0x000003d0, 0x0000029f, + 0x000003d1, 0x000002a0, + 0x000003d2, 0x000002a1, + 0x000003d3, 0x000002a2, + 0x000003d4, 0x000002a4, + 0x000003d5, 0x000002a6, + 0x000003d6, 0x000002a7, + 0x000003f0, 0x000002a8, + 0x000003f1, 0x000002a9, + 0x000003f2, 0x000002aa, + 0x000003f4, 0x000002ab, + 0x000003f5, 0x000002ac, + 0x00000400, 0x000002ad, + 0x00000401, 0x000002af, + 0x00000403, 0x000002b1, + 0x00000407, 0x000002b3, + 0x0000040c, 0x000002b5, + 0x0000040d, 0x000002b7, + 0x0000040e, 0x000002b9, + 0x00000419, 0x000002bb, + 0x00000439, 0x000002bd, + 0x00000450, 0x000002bf, + 0x00000451, 0x000002c1, + 0x00000453, 0x000002c3, + 0x00000457, 0x000002c5, + 0x0000045c, 0x000002c7, + 0x0000045d, 0x000002c9, + 0x0000045e, 0x000002cb, + 0x00000476, 0x000002cd, + 0x00000477, 0x000002cf, + 0x000004c1, 0x000002d1, + 0x000004c2, 0x000002d3, + 0x000004d0, 0x000002d5, + 0x000004d1, 0x000002d7, + 0x000004d2, 0x000002d9, + 0x000004d3, 0x000002db, + 0x000004d6, 0x000002dd, + 0x000004d7, 0x000002df, + 0x000004da, 0x000002e1, + 0x000004db, 0x000002e3, + 0x000004dc, 0x000002e5, + 0x000004dd, 0x000002e7, + 0x000004de, 0x000002e9, + 0x000004df, 0x000002eb, + 0x000004e2, 0x000002ed, + 0x000004e3, 0x000002ef, + 0x000004e4, 0x000002f1, + 0x000004e5, 0x000002f3, + 0x000004e6, 0x000002f5, + 0x000004e7, 0x000002f7, + 0x000004ea, 0x000002f9, + 0x000004eb, 0x000002fb, + 0x000004ec, 0x000002fd, + 0x000004ed, 0x000002ff, + 0x000004ee, 0x00000301, + 0x000004ef, 0x00000303, + 0x000004f0, 0x00000305, + 0x000004f1, 0x00000307, + 0x000004f2, 0x00000309, + 0x000004f3, 0x0000030b, + 0x000004f4, 0x0000030d, + 0x000004f5, 0x0000030f, + 0x000004f8, 0x00000311, + 0x000004f9, 0x00000313, + 0x00000587, 0x00000315, + 0x00000622, 0x00000317, + 0x00000623, 0x00000319, + 0x00000624, 0x0000031b, + 0x00000625, 0x0000031d, + 0x00000626, 0x0000031f, + 0x00000675, 0x00000321, + 0x00000676, 0x00000323, + 0x00000677, 0x00000325, + 0x00000678, 0x00000327, + 0x000006c0, 0x00000329, + 0x000006c2, 0x0000032b, + 0x000006d3, 0x0000032d, + 0x00000929, 0x0000032f, + 0x00000931, 0x00000331, + 0x00000934, 0x00000333, + 0x00000958, 0x00000335, + 0x00000959, 0x00000337, + 0x0000095a, 0x00000339, + 0x0000095b, 0x0000033b, + 0x0000095c, 0x0000033d, + 0x0000095d, 0x0000033f, + 0x0000095e, 0x00000341, + 0x0000095f, 0x00000343, + 0x000009cb, 0x00000345, + 0x000009cc, 0x00000347, + 0x000009dc, 0x00000349, + 0x000009dd, 0x0000034b, + 0x000009df, 0x0000034d, + 0x00000a33, 0x0000034f, + 0x00000a36, 0x00000351, + 0x00000a59, 0x00000353, + 0x00000a5a, 0x00000355, + 0x00000a5b, 0x00000357, + 0x00000a5e, 0x00000359, + 0x00000b48, 0x0000035b, + 0x00000b4b, 0x0000035d, + 0x00000b4c, 0x0000035f, + 0x00000b5c, 0x00000361, + 0x00000b5d, 0x00000363, + 0x00000b94, 0x00000365, + 0x00000bca, 0x00000367, + 0x00000bcb, 0x00000369, + 0x00000bcc, 0x0000036b, + 0x00000c48, 0x0000036d, + 0x00000cc0, 0x0000036f, + 0x00000cc7, 0x00000371, + 0x00000cc8, 0x00000373, + 0x00000cca, 0x00000375, + 0x00000ccb, 0x00000377, + 0x00000d4a, 0x0000037a, + 0x00000d4b, 0x0000037c, + 0x00000d4c, 0x0000037e, + 0x00000dda, 0x00000380, + 0x00000ddc, 0x00000382, + 0x00000ddd, 0x00000384, + 0x00000dde, 0x00000387, + 0x00000e33, 0x00000389, + 0x00000eb3, 0x0000038b, + 0x00000edc, 0x0000038d, + 0x00000edd, 0x0000038f, + 0x00000f0c, 0x00000391, + 0x00000f43, 0x00000392, + 0x00000f4d, 0x00000394, + 0x00000f52, 0x00000396, + 0x00000f57, 0x00000398, + 0x00000f5c, 0x0000039a, + 0x00000f69, 0x0000039c, + 0x00000f73, 0x0000039e, + 0x00000f75, 0x000003a0, + 0x00000f76, 0x000003a2, + 0x00000f77, 0x000003a4, + 0x00000f78, 0x000003a7, + 0x00000f79, 0x000003a9, + 0x00000f81, 0x000003ac, + 0x00000f93, 0x000003ae, + 0x00000f9d, 0x000003b0, + 0x00000fa2, 0x000003b2, + 0x00000fa7, 0x000003b4, + 0x00000fac, 0x000003b6, + 0x00000fb9, 0x000003b8, + 0x00001026, 0x000003ba, + 0x00001e00, 0x000003bc, + 0x00001e01, 0x000003be, + 0x00001e02, 0x000003c0, + 0x00001e03, 0x000003c2, + 0x00001e04, 0x000003c4, + 0x00001e05, 0x000003c6, + 0x00001e06, 0x000003c8, + 0x00001e07, 0x000003ca, + 0x00001e08, 0x000003cc, + 0x00001e09, 0x000003cf, + 0x00001e0a, 0x000003d2, + 0x00001e0b, 0x000003d4, + 0x00001e0c, 0x000003d6, + 0x00001e0d, 0x000003d8, + 0x00001e0e, 0x000003da, + 0x00001e0f, 0x000003dc, + 0x00001e10, 0x000003de, + 0x00001e11, 0x000003e0, + 0x00001e12, 0x000003e2, + 0x00001e13, 0x000003e4, + 0x00001e14, 0x000003e6, + 0x00001e15, 0x000003e9, + 0x00001e16, 0x000003ec, + 0x00001e17, 0x000003ef, + 0x00001e18, 0x000003f2, + 0x00001e19, 0x000003f4, + 0x00001e1a, 0x000003f6, + 0x00001e1b, 0x000003f8, + 0x00001e1c, 0x000003fa, + 0x00001e1d, 0x000003fd, + 0x00001e1e, 0x00000400, + 0x00001e1f, 0x00000402, + 0x00001e20, 0x00000404, + 0x00001e21, 0x00000406, + 0x00001e22, 0x00000408, + 0x00001e23, 0x0000040a, + 0x00001e24, 0x0000040c, + 0x00001e25, 0x0000040e, + 0x00001e26, 0x00000410, + 0x00001e27, 0x00000412, + 0x00001e28, 0x00000414, + 0x00001e29, 0x00000416, + 0x00001e2a, 0x00000418, + 0x00001e2b, 0x0000041a, + 0x00001e2c, 0x0000041c, + 0x00001e2d, 0x0000041e, + 0x00001e2e, 0x00000420, + 0x00001e2f, 0x00000423, + 0x00001e30, 0x00000426, + 0x00001e31, 0x00000428, + 0x00001e32, 0x0000042a, + 0x00001e33, 0x0000042c, + 0x00001e34, 0x0000042e, + 0x00001e35, 0x00000430, + 0x00001e36, 0x00000432, + 0x00001e37, 0x00000434, + 0x00001e38, 0x00000436, + 0x00001e39, 0x00000439, + 0x00001e3a, 0x0000043c, + 0x00001e3b, 0x0000043e, + 0x00001e3c, 0x00000440, + 0x00001e3d, 0x00000442, + 0x00001e3e, 0x00000444, + 0x00001e3f, 0x00000446, + 0x00001e40, 0x00000448, + 0x00001e41, 0x0000044a, + 0x00001e42, 0x0000044c, + 0x00001e43, 0x0000044e, + 0x00001e44, 0x00000450, + 0x00001e45, 0x00000452, + 0x00001e46, 0x00000454, + 0x00001e47, 0x00000456, + 0x00001e48, 0x00000458, + 0x00001e49, 0x0000045a, + 0x00001e4a, 0x0000045c, + 0x00001e4b, 0x0000045e, + 0x00001e4c, 0x00000460, + 0x00001e4d, 0x00000463, + 0x00001e4e, 0x00000466, + 0x00001e4f, 0x00000469, + 0x00001e50, 0x0000046c, + 0x00001e51, 0x0000046f, + 0x00001e52, 0x00000472, + 0x00001e53, 0x00000475, + 0x00001e54, 0x00000478, + 0x00001e55, 0x0000047a, + 0x00001e56, 0x0000047c, + 0x00001e57, 0x0000047e, + 0x00001e58, 0x00000480, + 0x00001e59, 0x00000482, + 0x00001e5a, 0x00000484, + 0x00001e5b, 0x00000486, + 0x00001e5c, 0x00000488, + 0x00001e5d, 0x0000048b, + 0x00001e5e, 0x0000048e, + 0x00001e5f, 0x00000490, + 0x00001e60, 0x00000492, + 0x00001e61, 0x00000494, + 0x00001e62, 0x00000496, + 0x00001e63, 0x00000498, + 0x00001e64, 0x0000049a, + 0x00001e65, 0x0000049d, + 0x00001e66, 0x000004a0, + 0x00001e67, 0x000004a3, + 0x00001e68, 0x000004a6, + 0x00001e69, 0x000004a9, + 0x00001e6a, 0x000004ac, + 0x00001e6b, 0x000004ae, + 0x00001e6c, 0x000004b0, + 0x00001e6d, 0x000004b2, + 0x00001e6e, 0x000004b4, + 0x00001e6f, 0x000004b6, + 0x00001e70, 0x000004b8, + 0x00001e71, 0x000004ba, + 0x00001e72, 0x000004bc, + 0x00001e73, 0x000004be, + 0x00001e74, 0x000004c0, + 0x00001e75, 0x000004c2, + 0x00001e76, 0x000004c4, + 0x00001e77, 0x000004c6, + 0x00001e78, 0x000004c8, + 0x00001e79, 0x000004cb, + 0x00001e7a, 0x000004ce, + 0x00001e7b, 0x000004d1, + 0x00001e7c, 0x000004d4, + 0x00001e7d, 0x000004d6, + 0x00001e7e, 0x000004d8, + 0x00001e7f, 0x000004da, + 0x00001e80, 0x000004dc, + 0x00001e81, 0x000004de, + 0x00001e82, 0x000004e0, + 0x00001e83, 0x000004e2, + 0x00001e84, 0x000004e4, + 0x00001e85, 0x000004e6, + 0x00001e86, 0x000004e8, + 0x00001e87, 0x000004ea, + 0x00001e88, 0x000004ec, + 0x00001e89, 0x000004ee, + 0x00001e8a, 0x000004f0, + 0x00001e8b, 0x000004f2, + 0x00001e8c, 0x000004f4, + 0x00001e8d, 0x000004f6, + 0x00001e8e, 0x000004f8, + 0x00001e8f, 0x000004fa, + 0x00001e90, 0x000004fc, + 0x00001e91, 0x000004fe, + 0x00001e92, 0x00000500, + 0x00001e93, 0x00000502, + 0x00001e94, 0x00000504, + 0x00001e95, 0x00000506, + 0x00001e96, 0x00000508, + 0x00001e97, 0x0000050a, + 0x00001e98, 0x0000050c, + 0x00001e99, 0x0000050e, + 0x00001e9a, 0x00000510, + 0x00001e9b, 0x00000512, + 0x00001ea0, 0x00000514, + 0x00001ea1, 0x00000516, + 0x00001ea2, 0x00000518, + 0x00001ea3, 0x0000051a, + 0x00001ea4, 0x0000051c, + 0x00001ea5, 0x0000051f, + 0x00001ea6, 0x00000522, + 0x00001ea7, 0x00000525, + 0x00001ea8, 0x00000528, + 0x00001ea9, 0x0000052b, + 0x00001eaa, 0x0000052e, + 0x00001eab, 0x00000531, + 0x00001eac, 0x00000534, + 0x00001ead, 0x00000537, + 0x00001eae, 0x0000053a, + 0x00001eaf, 0x0000053d, + 0x00001eb0, 0x00000540, + 0x00001eb1, 0x00000543, + 0x00001eb2, 0x00000546, + 0x00001eb3, 0x00000549, + 0x00001eb4, 0x0000054c, + 0x00001eb5, 0x0000054f, + 0x00001eb6, 0x00000552, + 0x00001eb7, 0x00000555, + 0x00001eb8, 0x00000558, + 0x00001eb9, 0x0000055a, + 0x00001eba, 0x0000055c, + 0x00001ebb, 0x0000055e, + 0x00001ebc, 0x00000560, + 0x00001ebd, 0x00000562, + 0x00001ebe, 0x00000564, + 0x00001ebf, 0x00000567, + 0x00001ec0, 0x0000056a, + 0x00001ec1, 0x0000056d, + 0x00001ec2, 0x00000570, + 0x00001ec3, 0x00000573, + 0x00001ec4, 0x00000576, + 0x00001ec5, 0x00000579, + 0x00001ec6, 0x0000057c, + 0x00001ec7, 0x0000057f, + 0x00001ec8, 0x00000582, + 0x00001ec9, 0x00000584, + 0x00001eca, 0x00000586, + 0x00001ecb, 0x00000588, + 0x00001ecc, 0x0000058a, + 0x00001ecd, 0x0000058c, + 0x00001ece, 0x0000058e, + 0x00001ecf, 0x00000590, + 0x00001ed0, 0x00000592, + 0x00001ed1, 0x00000595, + 0x00001ed2, 0x00000598, + 0x00001ed3, 0x0000059b, + 0x00001ed4, 0x0000059e, + 0x00001ed5, 0x000005a1, + 0x00001ed6, 0x000005a4, + 0x00001ed7, 0x000005a7, + 0x00001ed8, 0x000005aa, + 0x00001ed9, 0x000005ad, + 0x00001eda, 0x000005b0, + 0x00001edb, 0x000005b3, + 0x00001edc, 0x000005b6, + 0x00001edd, 0x000005b9, + 0x00001ede, 0x000005bc, + 0x00001edf, 0x000005bf, + 0x00001ee0, 0x000005c2, + 0x00001ee1, 0x000005c5, + 0x00001ee2, 0x000005c8, + 0x00001ee3, 0x000005cb, + 0x00001ee4, 0x000005ce, + 0x00001ee5, 0x000005d0, + 0x00001ee6, 0x000005d2, + 0x00001ee7, 0x000005d4, + 0x00001ee8, 0x000005d6, + 0x00001ee9, 0x000005d9, + 0x00001eea, 0x000005dc, + 0x00001eeb, 0x000005df, + 0x00001eec, 0x000005e2, + 0x00001eed, 0x000005e5, + 0x00001eee, 0x000005e8, + 0x00001eef, 0x000005eb, + 0x00001ef0, 0x000005ee, + 0x00001ef1, 0x000005f1, + 0x00001ef2, 0x000005f4, + 0x00001ef3, 0x000005f6, + 0x00001ef4, 0x000005f8, + 0x00001ef5, 0x000005fa, + 0x00001ef6, 0x000005fc, + 0x00001ef7, 0x000005fe, + 0x00001ef8, 0x00000600, + 0x00001ef9, 0x00000602, + 0x00001f00, 0x00000604, + 0x00001f01, 0x00000606, + 0x00001f02, 0x00000608, + 0x00001f03, 0x0000060b, + 0x00001f04, 0x0000060e, + 0x00001f05, 0x00000611, + 0x00001f06, 0x00000614, + 0x00001f07, 0x00000617, + 0x00001f08, 0x0000061a, + 0x00001f09, 0x0000061c, + 0x00001f0a, 0x0000061e, + 0x00001f0b, 0x00000621, + 0x00001f0c, 0x00000624, + 0x00001f0d, 0x00000627, + 0x00001f0e, 0x0000062a, + 0x00001f0f, 0x0000062d, + 0x00001f10, 0x00000630, + 0x00001f11, 0x00000632, + 0x00001f12, 0x00000634, + 0x00001f13, 0x00000637, + 0x00001f14, 0x0000063a, + 0x00001f15, 0x0000063d, + 0x00001f18, 0x00000640, + 0x00001f19, 0x00000642, + 0x00001f1a, 0x00000644, + 0x00001f1b, 0x00000647, + 0x00001f1c, 0x0000064a, + 0x00001f1d, 0x0000064d, + 0x00001f20, 0x00000650, + 0x00001f21, 0x00000652, + 0x00001f22, 0x00000654, + 0x00001f23, 0x00000657, + 0x00001f24, 0x0000065a, + 0x00001f25, 0x0000065d, + 0x00001f26, 0x00000660, + 0x00001f27, 0x00000663, + 0x00001f28, 0x00000666, + 0x00001f29, 0x00000668, + 0x00001f2a, 0x0000066a, + 0x00001f2b, 0x0000066d, + 0x00001f2c, 0x00000670, + 0x00001f2d, 0x00000673, + 0x00001f2e, 0x00000676, + 0x00001f2f, 0x00000679, + 0x00001f30, 0x0000067c, + 0x00001f31, 0x0000067e, + 0x00001f32, 0x00000680, + 0x00001f33, 0x00000683, + 0x00001f34, 0x00000686, + 0x00001f35, 0x00000689, + 0x00001f36, 0x0000068c, + 0x00001f37, 0x0000068f, + 0x00001f38, 0x00000692, + 0x00001f39, 0x00000694, + 0x00001f3a, 0x00000696, + 0x00001f3b, 0x00000699, + 0x00001f3c, 0x0000069c, + 0x00001f3d, 0x0000069f, + 0x00001f3e, 0x000006a2, + 0x00001f3f, 0x000006a5, + 0x00001f40, 0x000006a8, + 0x00001f41, 0x000006aa, + 0x00001f42, 0x000006ac, + 0x00001f43, 0x000006af, + 0x00001f44, 0x000006b2, + 0x00001f45, 0x000006b5, + 0x00001f48, 0x000006b8, + 0x00001f49, 0x000006ba, + 0x00001f4a, 0x000006bc, + 0x00001f4b, 0x000006bf, + 0x00001f4c, 0x000006c2, + 0x00001f4d, 0x000006c5, + 0x00001f50, 0x000006c8, + 0x00001f51, 0x000006ca, + 0x00001f52, 0x000006cc, + 0x00001f53, 0x000006cf, + 0x00001f54, 0x000006d2, + 0x00001f55, 0x000006d5, + 0x00001f56, 0x000006d8, + 0x00001f57, 0x000006db, + 0x00001f59, 0x000006de, + 0x00001f5b, 0x000006e0, + 0x00001f5d, 0x000006e3, + 0x00001f5f, 0x000006e6, + 0x00001f60, 0x000006e9, + 0x00001f61, 0x000006eb, + 0x00001f62, 0x000006ed, + 0x00001f63, 0x000006f0, + 0x00001f64, 0x000006f3, + 0x00001f65, 0x000006f6, + 0x00001f66, 0x000006f9, + 0x00001f67, 0x000006fc, + 0x00001f68, 0x000006ff, + 0x00001f69, 0x00000701, + 0x00001f6a, 0x00000703, + 0x00001f6b, 0x00000706, + 0x00001f6c, 0x00000709, + 0x00001f6d, 0x0000070c, + 0x00001f6e, 0x0000070f, + 0x00001f6f, 0x00000712, + 0x00001f70, 0x00000715, + 0x00001f71, 0x00000717, + 0x00001f72, 0x00000719, + 0x00001f73, 0x0000071b, + 0x00001f74, 0x0000071d, + 0x00001f75, 0x0000071f, + 0x00001f76, 0x00000721, + 0x00001f77, 0x00000723, + 0x00001f78, 0x00000725, + 0x00001f79, 0x00000727, + 0x00001f7a, 0x00000729, + 0x00001f7b, 0x0000072b, + 0x00001f7c, 0x0000072d, + 0x00001f7d, 0x0000072f, + 0x00001f80, 0x00000731, + 0x00001f81, 0x00000734, + 0x00001f82, 0x00000737, + 0x00001f83, 0x0000073b, + 0x00001f84, 0x0000073f, + 0x00001f85, 0x00000743, + 0x00001f86, 0x00000747, + 0x00001f87, 0x0000074b, + 0x00001f88, 0x0000074f, + 0x00001f89, 0x00000752, + 0x00001f8a, 0x00000755, + 0x00001f8b, 0x00000759, + 0x00001f8c, 0x0000075d, + 0x00001f8d, 0x00000761, + 0x00001f8e, 0x00000765, + 0x00001f8f, 0x00000769, + 0x00001f90, 0x0000076d, + 0x00001f91, 0x00000770, + 0x00001f92, 0x00000773, + 0x00001f93, 0x00000777, + 0x00001f94, 0x0000077b, + 0x00001f95, 0x0000077f, + 0x00001f96, 0x00000783, + 0x00001f97, 0x00000787, + 0x00001f98, 0x0000078b, + 0x00001f99, 0x0000078e, + 0x00001f9a, 0x00000791, + 0x00001f9b, 0x00000795, + 0x00001f9c, 0x00000799, + 0x00001f9d, 0x0000079d, + 0x00001f9e, 0x000007a1, + 0x00001f9f, 0x000007a5, + 0x00001fa0, 0x000007a9, + 0x00001fa1, 0x000007ac, + 0x00001fa2, 0x000007af, + 0x00001fa3, 0x000007b3, + 0x00001fa4, 0x000007b7, + 0x00001fa5, 0x000007bb, + 0x00001fa6, 0x000007bf, + 0x00001fa7, 0x000007c3, + 0x00001fa8, 0x000007c7, + 0x00001fa9, 0x000007ca, + 0x00001faa, 0x000007cd, + 0x00001fab, 0x000007d1, + 0x00001fac, 0x000007d5, + 0x00001fad, 0x000007d9, + 0x00001fae, 0x000007dd, + 0x00001faf, 0x000007e1, + 0x00001fb0, 0x000007e5, + 0x00001fb1, 0x000007e7, + 0x00001fb2, 0x000007e9, + 0x00001fb3, 0x000007ec, + 0x00001fb4, 0x000007ee, + 0x00001fb6, 0x000007f1, + 0x00001fb7, 0x000007f3, + 0x00001fb8, 0x000007f6, + 0x00001fb9, 0x000007f8, + 0x00001fba, 0x000007fa, + 0x00001fbb, 0x000007fc, + 0x00001fbc, 0x000007fe, + 0x00001fbd, 0x00000800, + 0x00001fbe, 0x00000802, + 0x00001fbf, 0x00000803, + 0x00001fc0, 0x00000805, + 0x00001fc1, 0x00000807, + 0x00001fc2, 0x0000080a, + 0x00001fc3, 0x0000080d, + 0x00001fc4, 0x0000080f, + 0x00001fc6, 0x00000812, + 0x00001fc7, 0x00000814, + 0x00001fc8, 0x00000817, + 0x00001fc9, 0x00000819, + 0x00001fca, 0x0000081b, + 0x00001fcb, 0x0000081d, + 0x00001fcc, 0x0000081f, + 0x00001fcd, 0x00000821, + 0x00001fce, 0x00000824, + 0x00001fcf, 0x00000827, + 0x00001fd0, 0x0000082a, + 0x00001fd1, 0x0000082c, + 0x00001fd2, 0x0000082e, + 0x00001fd3, 0x00000831, + 0x00001fd6, 0x00000834, + 0x00001fd7, 0x00000836, + 0x00001fd8, 0x00000839, + 0x00001fd9, 0x0000083b, + 0x00001fda, 0x0000083d, + 0x00001fdb, 0x0000083f, + 0x00001fdd, 0x00000841, + 0x00001fde, 0x00000844, + 0x00001fdf, 0x00000847, + 0x00001fe0, 0x0000084a, + 0x00001fe1, 0x0000084c, + 0x00001fe2, 0x0000084e, + 0x00001fe3, 0x00000851, + 0x00001fe4, 0x00000854, + 0x00001fe5, 0x00000856, + 0x00001fe6, 0x00000858, + 0x00001fe7, 0x0000085a, + 0x00001fe8, 0x0000085d, + 0x00001fe9, 0x0000085f, + 0x00001fea, 0x00000861, + 0x00001feb, 0x00000863, + 0x00001fec, 0x00000865, + 0x00001fed, 0x00000867, + 0x00001fee, 0x0000086a, + 0x00001fef, 0x0000086d, + 0x00001ff2, 0x0000086e, + 0x00001ff3, 0x00000871, + 0x00001ff4, 0x00000873, + 0x00001ff6, 0x00000876, + 0x00001ff7, 0x00000878, + 0x00001ff8, 0x0000087b, + 0x00001ff9, 0x0000087d, + 0x00001ffa, 0x0000087f, + 0x00001ffb, 0x00000881, + 0x00001ffc, 0x00000883, + 0x00001ffd, 0x00000885, + 0x00001ffe, 0x00000887, + 0x00002000, 0x00000889, + 0x00002001, 0x0000088a, + 0x00002002, 0x0000088b, + 0x00002003, 0x0000088c, + 0x00002004, 0x0000088d, + 0x00002005, 0x0000088e, + 0x00002006, 0x0000088f, + 0x00002007, 0x00000890, + 0x00002008, 0x00000891, + 0x00002009, 0x00000892, + 0x0000200a, 0x00000893, + 0x00002011, 0x00000894, + 0x00002017, 0x00000895, + 0x00002024, 0x00000897, + 0x00002025, 0x00000898, + 0x00002026, 0x0000089a, + 0x0000202f, 0x0000089d, + 0x00002033, 0x0000089e, + 0x00002034, 0x000008a0, + 0x00002036, 0x000008a3, + 0x00002037, 0x000008a5, + 0x0000203c, 0x000008a8, + 0x0000203e, 0x000008aa, + 0x00002047, 0x000008ac, + 0x00002048, 0x000008ae, + 0x00002049, 0x000008b0, + 0x00002057, 0x000008b2, + 0x0000205f, 0x000008b6, + 0x00002070, 0x000008b7, + 0x00002071, 0x000008b8, + 0x00002074, 0x000008b9, + 0x00002075, 0x000008ba, + 0x00002076, 0x000008bb, + 0x00002077, 0x000008bc, + 0x00002078, 0x000008bd, + 0x00002079, 0x000008be, + 0x0000207a, 0x000008bf, + 0x0000207b, 0x000008c0, + 0x0000207c, 0x000008c1, + 0x0000207d, 0x000008c2, + 0x0000207e, 0x000008c3, + 0x0000207f, 0x000008c4, + 0x00002080, 0x000008c5, + 0x00002081, 0x000008c6, + 0x00002082, 0x000008c7, + 0x00002083, 0x000008c8, + 0x00002084, 0x000008c9, + 0x00002085, 0x000008ca, + 0x00002086, 0x000008cb, + 0x00002087, 0x000008cc, + 0x00002088, 0x000008cd, + 0x00002089, 0x000008ce, + 0x0000208a, 0x000008cf, + 0x0000208b, 0x000008d0, + 0x0000208c, 0x000008d1, + 0x0000208d, 0x000008d2, + 0x0000208e, 0x000008d3, + 0x000020a8, 0x000008d4, + 0x00002100, 0x000008d6, + 0x00002101, 0x000008d9, + 0x00002102, 0x000008dc, + 0x00002103, 0x000008dd, + 0x00002105, 0x000008df, + 0x00002106, 0x000008e2, + 0x00002107, 0x000008e5, + 0x00002109, 0x000008e6, + 0x0000210a, 0x000008e8, + 0x0000210b, 0x000008e9, + 0x0000210c, 0x000008ea, + 0x0000210d, 0x000008eb, + 0x0000210e, 0x000008ec, + 0x0000210f, 0x000008ed, + 0x00002110, 0x000008ee, + 0x00002111, 0x000008ef, + 0x00002112, 0x000008f0, + 0x00002113, 0x000008f1, + 0x00002115, 0x000008f2, + 0x00002116, 0x000008f3, + 0x00002119, 0x000008f5, + 0x0000211a, 0x000008f6, + 0x0000211b, 0x000008f7, + 0x0000211c, 0x000008f8, + 0x0000211d, 0x000008f9, + 0x00002120, 0x000008fa, + 0x00002121, 0x000008fc, + 0x00002122, 0x000008ff, + 0x00002124, 0x00000901, + 0x00002126, 0x00000902, + 0x00002128, 0x00000903, + 0x0000212a, 0x00000904, + 0x0000212b, 0x00000905, + 0x0000212c, 0x00000907, + 0x0000212d, 0x00000908, + 0x0000212f, 0x00000909, + 0x00002130, 0x0000090a, + 0x00002131, 0x0000090b, + 0x00002133, 0x0000090c, + 0x00002134, 0x0000090d, + 0x00002135, 0x0000090e, + 0x00002136, 0x0000090f, + 0x00002137, 0x00000910, + 0x00002138, 0x00000911, + 0x00002139, 0x00000912, + 0x0000213d, 0x00000913, + 0x0000213e, 0x00000914, + 0x0000213f, 0x00000915, + 0x00002140, 0x00000916, + 0x00002145, 0x00000917, + 0x00002146, 0x00000918, + 0x00002147, 0x00000919, + 0x00002148, 0x0000091a, + 0x00002149, 0x0000091b, + 0x00002153, 0x0000091c, + 0x00002154, 0x0000091f, + 0x00002155, 0x00000922, + 0x00002156, 0x00000925, + 0x00002157, 0x00000928, + 0x00002158, 0x0000092b, + 0x00002159, 0x0000092e, + 0x0000215a, 0x00000931, + 0x0000215b, 0x00000934, + 0x0000215c, 0x00000937, + 0x0000215d, 0x0000093a, + 0x0000215e, 0x0000093d, + 0x0000215f, 0x00000940, + 0x00002160, 0x00000942, + 0x00002161, 0x00000943, + 0x00002162, 0x00000945, + 0x00002163, 0x00000948, + 0x00002164, 0x0000094a, + 0x00002165, 0x0000094b, + 0x00002166, 0x0000094d, + 0x00002167, 0x00000950, + 0x00002168, 0x00000954, + 0x00002169, 0x00000956, + 0x0000216a, 0x00000957, + 0x0000216b, 0x00000959, + 0x0000216c, 0x0000095c, + 0x0000216d, 0x0000095d, + 0x0000216e, 0x0000095e, + 0x0000216f, 0x0000095f, + 0x00002170, 0x00000960, + 0x00002171, 0x00000961, + 0x00002172, 0x00000963, + 0x00002173, 0x00000966, + 0x00002174, 0x00000968, + 0x00002175, 0x00000969, + 0x00002176, 0x0000096b, + 0x00002177, 0x0000096e, + 0x00002178, 0x00000972, + 0x00002179, 0x00000974, + 0x0000217a, 0x00000975, + 0x0000217b, 0x00000977, + 0x0000217c, 0x0000097a, + 0x0000217d, 0x0000097b, + 0x0000217e, 0x0000097c, + 0x0000217f, 0x0000097d, + 0x0000219a, 0x0000097e, + 0x0000219b, 0x00000980, + 0x000021ae, 0x00000982, + 0x000021cd, 0x00000984, + 0x000021ce, 0x00000986, + 0x000021cf, 0x00000988, + 0x00002204, 0x0000098a, + 0x00002209, 0x0000098c, + 0x0000220c, 0x0000098e, + 0x00002224, 0x00000990, + 0x00002226, 0x00000992, + 0x0000222c, 0x00000994, + 0x0000222d, 0x00000996, + 0x0000222f, 0x00000999, + 0x00002230, 0x0000099b, + 0x00002241, 0x0000099e, + 0x00002244, 0x000009a0, + 0x00002247, 0x000009a2, + 0x00002249, 0x000009a4, + 0x00002260, 0x000009a6, + 0x00002262, 0x000009a8, + 0x0000226d, 0x000009aa, + 0x0000226e, 0x000009ac, + 0x0000226f, 0x000009ae, + 0x00002270, 0x000009b0, + 0x00002271, 0x000009b2, + 0x00002274, 0x000009b4, + 0x00002275, 0x000009b6, + 0x00002278, 0x000009b8, + 0x00002279, 0x000009ba, + 0x00002280, 0x000009bc, + 0x00002281, 0x000009be, + 0x00002284, 0x000009c0, + 0x00002285, 0x000009c2, + 0x00002288, 0x000009c4, + 0x00002289, 0x000009c6, + 0x000022ac, 0x000009c8, + 0x000022ad, 0x000009ca, + 0x000022ae, 0x000009cc, + 0x000022af, 0x000009ce, + 0x000022e0, 0x000009d0, + 0x000022e1, 0x000009d2, + 0x000022e2, 0x000009d4, + 0x000022e3, 0x000009d6, + 0x000022ea, 0x000009d8, + 0x000022eb, 0x000009da, + 0x000022ec, 0x000009dc, + 0x000022ed, 0x000009de, + 0x00002329, 0x000009e0, + 0x0000232a, 0x000009e1, + 0x00002460, 0x000009e2, + 0x00002461, 0x000009e3, + 0x00002462, 0x000009e4, + 0x00002463, 0x000009e5, + 0x00002464, 0x000009e6, + 0x00002465, 0x000009e7, + 0x00002466, 0x000009e8, + 0x00002467, 0x000009e9, + 0x00002468, 0x000009ea, + 0x00002469, 0x000009eb, + 0x0000246a, 0x000009ed, + 0x0000246b, 0x000009ef, + 0x0000246c, 0x000009f1, + 0x0000246d, 0x000009f3, + 0x0000246e, 0x000009f5, + 0x0000246f, 0x000009f7, + 0x00002470, 0x000009f9, + 0x00002471, 0x000009fb, + 0x00002472, 0x000009fd, + 0x00002473, 0x000009ff, + 0x00002474, 0x00000a01, + 0x00002475, 0x00000a04, + 0x00002476, 0x00000a07, + 0x00002477, 0x00000a0a, + 0x00002478, 0x00000a0d, + 0x00002479, 0x00000a10, + 0x0000247a, 0x00000a13, + 0x0000247b, 0x00000a16, + 0x0000247c, 0x00000a19, + 0x0000247d, 0x00000a1c, + 0x0000247e, 0x00000a20, + 0x0000247f, 0x00000a24, + 0x00002480, 0x00000a28, + 0x00002481, 0x00000a2c, + 0x00002482, 0x00000a30, + 0x00002483, 0x00000a34, + 0x00002484, 0x00000a38, + 0x00002485, 0x00000a3c, + 0x00002486, 0x00000a40, + 0x00002487, 0x00000a44, + 0x00002488, 0x00000a48, + 0x00002489, 0x00000a4a, + 0x0000248a, 0x00000a4c, + 0x0000248b, 0x00000a4e, + 0x0000248c, 0x00000a50, + 0x0000248d, 0x00000a52, + 0x0000248e, 0x00000a54, + 0x0000248f, 0x00000a56, + 0x00002490, 0x00000a58, + 0x00002491, 0x00000a5a, + 0x00002492, 0x00000a5d, + 0x00002493, 0x00000a60, + 0x00002494, 0x00000a63, + 0x00002495, 0x00000a66, + 0x00002496, 0x00000a69, + 0x00002497, 0x00000a6c, + 0x00002498, 0x00000a6f, + 0x00002499, 0x00000a72, + 0x0000249a, 0x00000a75, + 0x0000249b, 0x00000a78, + 0x0000249c, 0x00000a7b, + 0x0000249d, 0x00000a7e, + 0x0000249e, 0x00000a81, + 0x0000249f, 0x00000a84, + 0x000024a0, 0x00000a87, + 0x000024a1, 0x00000a8a, + 0x000024a2, 0x00000a8d, + 0x000024a3, 0x00000a90, + 0x000024a4, 0x00000a93, + 0x000024a5, 0x00000a96, + 0x000024a6, 0x00000a99, + 0x000024a7, 0x00000a9c, + 0x000024a8, 0x00000a9f, + 0x000024a9, 0x00000aa2, + 0x000024aa, 0x00000aa5, + 0x000024ab, 0x00000aa8, + 0x000024ac, 0x00000aab, + 0x000024ad, 0x00000aae, + 0x000024ae, 0x00000ab1, + 0x000024af, 0x00000ab4, + 0x000024b0, 0x00000ab7, + 0x000024b1, 0x00000aba, + 0x000024b2, 0x00000abd, + 0x000024b3, 0x00000ac0, + 0x000024b4, 0x00000ac3, + 0x000024b5, 0x00000ac6, + 0x000024b6, 0x00000ac9, + 0x000024b7, 0x00000aca, + 0x000024b8, 0x00000acb, + 0x000024b9, 0x00000acc, + 0x000024ba, 0x00000acd, + 0x000024bb, 0x00000ace, + 0x000024bc, 0x00000acf, + 0x000024bd, 0x00000ad0, + 0x000024be, 0x00000ad1, + 0x000024bf, 0x00000ad2, + 0x000024c0, 0x00000ad3, + 0x000024c1, 0x00000ad4, + 0x000024c2, 0x00000ad5, + 0x000024c3, 0x00000ad6, + 0x000024c4, 0x00000ad7, + 0x000024c5, 0x00000ad8, + 0x000024c6, 0x00000ad9, + 0x000024c7, 0x00000ada, + 0x000024c8, 0x00000adb, + 0x000024c9, 0x00000adc, + 0x000024ca, 0x00000add, + 0x000024cb, 0x00000ade, + 0x000024cc, 0x00000adf, + 0x000024cd, 0x00000ae0, + 0x000024ce, 0x00000ae1, + 0x000024cf, 0x00000ae2, + 0x000024d0, 0x00000ae3, + 0x000024d1, 0x00000ae4, + 0x000024d2, 0x00000ae5, + 0x000024d3, 0x00000ae6, + 0x000024d4, 0x00000ae7, + 0x000024d5, 0x00000ae8, + 0x000024d6, 0x00000ae9, + 0x000024d7, 0x00000aea, + 0x000024d8, 0x00000aeb, + 0x000024d9, 0x00000aec, + 0x000024da, 0x00000aed, + 0x000024db, 0x00000aee, + 0x000024dc, 0x00000aef, + 0x000024dd, 0x00000af0, + 0x000024de, 0x00000af1, + 0x000024df, 0x00000af2, + 0x000024e0, 0x00000af3, + 0x000024e1, 0x00000af4, + 0x000024e2, 0x00000af5, + 0x000024e3, 0x00000af6, + 0x000024e4, 0x00000af7, + 0x000024e5, 0x00000af8, + 0x000024e6, 0x00000af9, + 0x000024e7, 0x00000afa, + 0x000024e8, 0x00000afb, + 0x000024e9, 0x00000afc, + 0x000024ea, 0x00000afd, + 0x00002a0c, 0x00000afe, + 0x00002a74, 0x00000b02, + 0x00002a75, 0x00000b05, + 0x00002a76, 0x00000b07, + 0x00002adc, 0x00000b0a, + 0x00002e9f, 0x00000b0c, + 0x00002ef3, 0x00000b0d, + 0x00002f00, 0x00000b0e, + 0x00002f01, 0x00000b0f, + 0x00002f02, 0x00000b10, + 0x00002f03, 0x00000b11, + 0x00002f04, 0x00000b12, + 0x00002f05, 0x00000b13, + 0x00002f06, 0x00000b14, + 0x00002f07, 0x00000b15, + 0x00002f08, 0x00000b16, + 0x00002f09, 0x00000b17, + 0x00002f0a, 0x00000b18, + 0x00002f0b, 0x00000b19, + 0x00002f0c, 0x00000b1a, + 0x00002f0d, 0x00000b1b, + 0x00002f0e, 0x00000b1c, + 0x00002f0f, 0x00000b1d, + 0x00002f10, 0x00000b1e, + 0x00002f11, 0x00000b1f, + 0x00002f12, 0x00000b20, + 0x00002f13, 0x00000b21, + 0x00002f14, 0x00000b22, + 0x00002f15, 0x00000b23, + 0x00002f16, 0x00000b24, + 0x00002f17, 0x00000b25, + 0x00002f18, 0x00000b26, + 0x00002f19, 0x00000b27, + 0x00002f1a, 0x00000b28, + 0x00002f1b, 0x00000b29, + 0x00002f1c, 0x00000b2a, + 0x00002f1d, 0x00000b2b, + 0x00002f1e, 0x00000b2c, + 0x00002f1f, 0x00000b2d, + 0x00002f20, 0x00000b2e, + 0x00002f21, 0x00000b2f, + 0x00002f22, 0x00000b30, + 0x00002f23, 0x00000b31, + 0x00002f24, 0x00000b32, + 0x00002f25, 0x00000b33, + 0x00002f26, 0x00000b34, + 0x00002f27, 0x00000b35, + 0x00002f28, 0x00000b36, + 0x00002f29, 0x00000b37, + 0x00002f2a, 0x00000b38, + 0x00002f2b, 0x00000b39, + 0x00002f2c, 0x00000b3a, + 0x00002f2d, 0x00000b3b, + 0x00002f2e, 0x00000b3c, + 0x00002f2f, 0x00000b3d, + 0x00002f30, 0x00000b3e, + 0x00002f31, 0x00000b3f, + 0x00002f32, 0x00000b40, + 0x00002f33, 0x00000b41, + 0x00002f34, 0x00000b42, + 0x00002f35, 0x00000b43, + 0x00002f36, 0x00000b44, + 0x00002f37, 0x00000b45, + 0x00002f38, 0x00000b46, + 0x00002f39, 0x00000b47, + 0x00002f3a, 0x00000b48, + 0x00002f3b, 0x00000b49, + 0x00002f3c, 0x00000b4a, + 0x00002f3d, 0x00000b4b, + 0x00002f3e, 0x00000b4c, + 0x00002f3f, 0x00000b4d, + 0x00002f40, 0x00000b4e, + 0x00002f41, 0x00000b4f, + 0x00002f42, 0x00000b50, + 0x00002f43, 0x00000b51, + 0x00002f44, 0x00000b52, + 0x00002f45, 0x00000b53, + 0x00002f46, 0x00000b54, + 0x00002f47, 0x00000b55, + 0x00002f48, 0x00000b56, + 0x00002f49, 0x00000b57, + 0x00002f4a, 0x00000b58, + 0x00002f4b, 0x00000b59, + 0x00002f4c, 0x00000b5a, + 0x00002f4d, 0x00000b5b, + 0x00002f4e, 0x00000b5c, + 0x00002f4f, 0x00000b5d, + 0x00002f50, 0x00000b5e, + 0x00002f51, 0x00000b5f, + 0x00002f52, 0x00000b60, + 0x00002f53, 0x00000b61, + 0x00002f54, 0x00000b62, + 0x00002f55, 0x00000b63, + 0x00002f56, 0x00000b64, + 0x00002f57, 0x00000b65, + 0x00002f58, 0x00000b66, + 0x00002f59, 0x00000b67, + 0x00002f5a, 0x00000b68, + 0x00002f5b, 0x00000b69, + 0x00002f5c, 0x00000b6a, + 0x00002f5d, 0x00000b6b, + 0x00002f5e, 0x00000b6c, + 0x00002f5f, 0x00000b6d, + 0x00002f60, 0x00000b6e, + 0x00002f61, 0x00000b6f, + 0x00002f62, 0x00000b70, + 0x00002f63, 0x00000b71, + 0x00002f64, 0x00000b72, + 0x00002f65, 0x00000b73, + 0x00002f66, 0x00000b74, + 0x00002f67, 0x00000b75, + 0x00002f68, 0x00000b76, + 0x00002f69, 0x00000b77, + 0x00002f6a, 0x00000b78, + 0x00002f6b, 0x00000b79, + 0x00002f6c, 0x00000b7a, + 0x00002f6d, 0x00000b7b, + 0x00002f6e, 0x00000b7c, + 0x00002f6f, 0x00000b7d, + 0x00002f70, 0x00000b7e, + 0x00002f71, 0x00000b7f, + 0x00002f72, 0x00000b80, + 0x00002f73, 0x00000b81, + 0x00002f74, 0x00000b82, + 0x00002f75, 0x00000b83, + 0x00002f76, 0x00000b84, + 0x00002f77, 0x00000b85, + 0x00002f78, 0x00000b86, + 0x00002f79, 0x00000b87, + 0x00002f7a, 0x00000b88, + 0x00002f7b, 0x00000b89, + 0x00002f7c, 0x00000b8a, + 0x00002f7d, 0x00000b8b, + 0x00002f7e, 0x00000b8c, + 0x00002f7f, 0x00000b8d, + 0x00002f80, 0x00000b8e, + 0x00002f81, 0x00000b8f, + 0x00002f82, 0x00000b90, + 0x00002f83, 0x00000b91, + 0x00002f84, 0x00000b92, + 0x00002f85, 0x00000b93, + 0x00002f86, 0x00000b94, + 0x00002f87, 0x00000b95, + 0x00002f88, 0x00000b96, + 0x00002f89, 0x00000b97, + 0x00002f8a, 0x00000b98, + 0x00002f8b, 0x00000b99, + 0x00002f8c, 0x00000b9a, + 0x00002f8d, 0x00000b9b, + 0x00002f8e, 0x00000b9c, + 0x00002f8f, 0x00000b9d, + 0x00002f90, 0x00000b9e, + 0x00002f91, 0x00000b9f, + 0x00002f92, 0x00000ba0, + 0x00002f93, 0x00000ba1, + 0x00002f94, 0x00000ba2, + 0x00002f95, 0x00000ba3, + 0x00002f96, 0x00000ba4, + 0x00002f97, 0x00000ba5, + 0x00002f98, 0x00000ba6, + 0x00002f99, 0x00000ba7, + 0x00002f9a, 0x00000ba8, + 0x00002f9b, 0x00000ba9, + 0x00002f9c, 0x00000baa, + 0x00002f9d, 0x00000bab, + 0x00002f9e, 0x00000bac, + 0x00002f9f, 0x00000bad, + 0x00002fa0, 0x00000bae, + 0x00002fa1, 0x00000baf, + 0x00002fa2, 0x00000bb0, + 0x00002fa3, 0x00000bb1, + 0x00002fa4, 0x00000bb2, + 0x00002fa5, 0x00000bb3, + 0x00002fa6, 0x00000bb4, + 0x00002fa7, 0x00000bb5, + 0x00002fa8, 0x00000bb6, + 0x00002fa9, 0x00000bb7, + 0x00002faa, 0x00000bb8, + 0x00002fab, 0x00000bb9, + 0x00002fac, 0x00000bba, + 0x00002fad, 0x00000bbb, + 0x00002fae, 0x00000bbc, + 0x00002faf, 0x00000bbd, + 0x00002fb0, 0x00000bbe, + 0x00002fb1, 0x00000bbf, + 0x00002fb2, 0x00000bc0, + 0x00002fb3, 0x00000bc1, + 0x00002fb4, 0x00000bc2, + 0x00002fb5, 0x00000bc3, + 0x00002fb6, 0x00000bc4, + 0x00002fb7, 0x00000bc5, + 0x00002fb8, 0x00000bc6, + 0x00002fb9, 0x00000bc7, + 0x00002fba, 0x00000bc8, + 0x00002fbb, 0x00000bc9, + 0x00002fbc, 0x00000bca, + 0x00002fbd, 0x00000bcb, + 0x00002fbe, 0x00000bcc, + 0x00002fbf, 0x00000bcd, + 0x00002fc0, 0x00000bce, + 0x00002fc1, 0x00000bcf, + 0x00002fc2, 0x00000bd0, + 0x00002fc3, 0x00000bd1, + 0x00002fc4, 0x00000bd2, + 0x00002fc5, 0x00000bd3, + 0x00002fc6, 0x00000bd4, + 0x00002fc7, 0x00000bd5, + 0x00002fc8, 0x00000bd6, + 0x00002fc9, 0x00000bd7, + 0x00002fca, 0x00000bd8, + 0x00002fcb, 0x00000bd9, + 0x00002fcc, 0x00000bda, + 0x00002fcd, 0x00000bdb, + 0x00002fce, 0x00000bdc, + 0x00002fcf, 0x00000bdd, + 0x00002fd0, 0x00000bde, + 0x00002fd1, 0x00000bdf, + 0x00002fd2, 0x00000be0, + 0x00002fd3, 0x00000be1, + 0x00002fd4, 0x00000be2, + 0x00002fd5, 0x00000be3, + 0x00003000, 0x00000be4, + 0x00003036, 0x00000be5, + 0x00003038, 0x00000be6, + 0x00003039, 0x00000be7, + 0x0000303a, 0x00000be8, + 0x0000304c, 0x00000be9, + 0x0000304e, 0x00000beb, + 0x00003050, 0x00000bed, + 0x00003052, 0x00000bef, + 0x00003054, 0x00000bf1, + 0x00003056, 0x00000bf3, + 0x00003058, 0x00000bf5, + 0x0000305a, 0x00000bf7, + 0x0000305c, 0x00000bf9, + 0x0000305e, 0x00000bfb, + 0x00003060, 0x00000bfd, + 0x00003062, 0x00000bff, + 0x00003065, 0x00000c01, + 0x00003067, 0x00000c03, + 0x00003069, 0x00000c05, + 0x00003070, 0x00000c07, + 0x00003071, 0x00000c09, + 0x00003073, 0x00000c0b, + 0x00003074, 0x00000c0d, + 0x00003076, 0x00000c0f, + 0x00003077, 0x00000c11, + 0x00003079, 0x00000c13, + 0x0000307a, 0x00000c15, + 0x0000307c, 0x00000c17, + 0x0000307d, 0x00000c19, + 0x00003094, 0x00000c1b, + 0x0000309b, 0x00000c1d, + 0x0000309c, 0x00000c1f, + 0x0000309e, 0x00000c21, + 0x0000309f, 0x00000c23, + 0x000030ac, 0x00000c25, + 0x000030ae, 0x00000c27, + 0x000030b0, 0x00000c29, + 0x000030b2, 0x00000c2b, + 0x000030b4, 0x00000c2d, + 0x000030b6, 0x00000c2f, + 0x000030b8, 0x00000c31, + 0x000030ba, 0x00000c33, + 0x000030bc, 0x00000c35, + 0x000030be, 0x00000c37, + 0x000030c0, 0x00000c39, + 0x000030c2, 0x00000c3b, + 0x000030c5, 0x00000c3d, + 0x000030c7, 0x00000c3f, + 0x000030c9, 0x00000c41, + 0x000030d0, 0x00000c43, + 0x000030d1, 0x00000c45, + 0x000030d3, 0x00000c47, + 0x000030d4, 0x00000c49, + 0x000030d6, 0x00000c4b, + 0x000030d7, 0x00000c4d, + 0x000030d9, 0x00000c4f, + 0x000030da, 0x00000c51, + 0x000030dc, 0x00000c53, + 0x000030dd, 0x00000c55, + 0x000030f4, 0x00000c57, + 0x000030f7, 0x00000c59, + 0x000030f8, 0x00000c5b, + 0x000030f9, 0x00000c5d, + 0x000030fa, 0x00000c5f, + 0x000030fe, 0x00000c61, + 0x000030ff, 0x00000c63, + 0x00003131, 0x00000c65, + 0x00003132, 0x00000c66, + 0x00003133, 0x00000c67, + 0x00003134, 0x00000c68, + 0x00003135, 0x00000c69, + 0x00003136, 0x00000c6a, + 0x00003137, 0x00000c6b, + 0x00003138, 0x00000c6c, + 0x00003139, 0x00000c6d, + 0x0000313a, 0x00000c6e, + 0x0000313b, 0x00000c6f, + 0x0000313c, 0x00000c70, + 0x0000313d, 0x00000c71, + 0x0000313e, 0x00000c72, + 0x0000313f, 0x00000c73, + 0x00003140, 0x00000c74, + 0x00003141, 0x00000c75, + 0x00003142, 0x00000c76, + 0x00003143, 0x00000c77, + 0x00003144, 0x00000c78, + 0x00003145, 0x00000c79, + 0x00003146, 0x00000c7a, + 0x00003147, 0x00000c7b, + 0x00003148, 0x00000c7c, + 0x00003149, 0x00000c7d, + 0x0000314a, 0x00000c7e, + 0x0000314b, 0x00000c7f, + 0x0000314c, 0x00000c80, + 0x0000314d, 0x00000c81, + 0x0000314e, 0x00000c82, + 0x0000314f, 0x00000c83, + 0x00003150, 0x00000c84, + 0x00003151, 0x00000c85, + 0x00003152, 0x00000c86, + 0x00003153, 0x00000c87, + 0x00003154, 0x00000c88, + 0x00003155, 0x00000c89, + 0x00003156, 0x00000c8a, + 0x00003157, 0x00000c8b, + 0x00003158, 0x00000c8c, + 0x00003159, 0x00000c8d, + 0x0000315a, 0x00000c8e, + 0x0000315b, 0x00000c8f, + 0x0000315c, 0x00000c90, + 0x0000315d, 0x00000c91, + 0x0000315e, 0x00000c92, + 0x0000315f, 0x00000c93, + 0x00003160, 0x00000c94, + 0x00003161, 0x00000c95, + 0x00003162, 0x00000c96, + 0x00003163, 0x00000c97, + 0x00003164, 0x00000c98, + 0x00003165, 0x00000c99, + 0x00003166, 0x00000c9a, + 0x00003167, 0x00000c9b, + 0x00003168, 0x00000c9c, + 0x00003169, 0x00000c9d, + 0x0000316a, 0x00000c9e, + 0x0000316b, 0x00000c9f, + 0x0000316c, 0x00000ca0, + 0x0000316d, 0x00000ca1, + 0x0000316e, 0x00000ca2, + 0x0000316f, 0x00000ca3, + 0x00003170, 0x00000ca4, + 0x00003171, 0x00000ca5, + 0x00003172, 0x00000ca6, + 0x00003173, 0x00000ca7, + 0x00003174, 0x00000ca8, + 0x00003175, 0x00000ca9, + 0x00003176, 0x00000caa, + 0x00003177, 0x00000cab, + 0x00003178, 0x00000cac, + 0x00003179, 0x00000cad, + 0x0000317a, 0x00000cae, + 0x0000317b, 0x00000caf, + 0x0000317c, 0x00000cb0, + 0x0000317d, 0x00000cb1, + 0x0000317e, 0x00000cb2, + 0x0000317f, 0x00000cb3, + 0x00003180, 0x00000cb4, + 0x00003181, 0x00000cb5, + 0x00003182, 0x00000cb6, + 0x00003183, 0x00000cb7, + 0x00003184, 0x00000cb8, + 0x00003185, 0x00000cb9, + 0x00003186, 0x00000cba, + 0x00003187, 0x00000cbb, + 0x00003188, 0x00000cbc, + 0x00003189, 0x00000cbd, + 0x0000318a, 0x00000cbe, + 0x0000318b, 0x00000cbf, + 0x0000318c, 0x00000cc0, + 0x0000318d, 0x00000cc1, + 0x0000318e, 0x00000cc2, + 0x00003192, 0x00000cc3, + 0x00003193, 0x00000cc4, + 0x00003194, 0x00000cc5, + 0x00003195, 0x00000cc6, + 0x00003196, 0x00000cc7, + 0x00003197, 0x00000cc8, + 0x00003198, 0x00000cc9, + 0x00003199, 0x00000cca, + 0x0000319a, 0x00000ccb, + 0x0000319b, 0x00000ccc, + 0x0000319c, 0x00000ccd, + 0x0000319d, 0x00000cce, + 0x0000319e, 0x00000ccf, + 0x0000319f, 0x00000cd0, + 0x00003200, 0x00000cd1, + 0x00003201, 0x00000cd4, + 0x00003202, 0x00000cd7, + 0x00003203, 0x00000cda, + 0x00003204, 0x00000cdd, + 0x00003205, 0x00000ce0, + 0x00003206, 0x00000ce3, + 0x00003207, 0x00000ce6, + 0x00003208, 0x00000ce9, + 0x00003209, 0x00000cec, + 0x0000320a, 0x00000cef, + 0x0000320b, 0x00000cf2, + 0x0000320c, 0x00000cf5, + 0x0000320d, 0x00000cf8, + 0x0000320e, 0x00000cfb, + 0x0000320f, 0x00000cff, + 0x00003210, 0x00000d03, + 0x00003211, 0x00000d07, + 0x00003212, 0x00000d0b, + 0x00003213, 0x00000d0f, + 0x00003214, 0x00000d13, + 0x00003215, 0x00000d17, + 0x00003216, 0x00000d1b, + 0x00003217, 0x00000d1f, + 0x00003218, 0x00000d23, + 0x00003219, 0x00000d27, + 0x0000321a, 0x00000d2b, + 0x0000321b, 0x00000d2f, + 0x0000321c, 0x00000d33, + 0x00003220, 0x00000d37, + 0x00003221, 0x00000d3a, + 0x00003222, 0x00000d3d, + 0x00003223, 0x00000d40, + 0x00003224, 0x00000d43, + 0x00003225, 0x00000d46, + 0x00003226, 0x00000d49, + 0x00003227, 0x00000d4c, + 0x00003228, 0x00000d4f, + 0x00003229, 0x00000d52, + 0x0000322a, 0x00000d55, + 0x0000322b, 0x00000d58, + 0x0000322c, 0x00000d5b, + 0x0000322d, 0x00000d5e, + 0x0000322e, 0x00000d61, + 0x0000322f, 0x00000d64, + 0x00003230, 0x00000d67, + 0x00003231, 0x00000d6a, + 0x00003232, 0x00000d6d, + 0x00003233, 0x00000d70, + 0x00003234, 0x00000d73, + 0x00003235, 0x00000d76, + 0x00003236, 0x00000d79, + 0x00003237, 0x00000d7c, + 0x00003238, 0x00000d7f, + 0x00003239, 0x00000d82, + 0x0000323a, 0x00000d85, + 0x0000323b, 0x00000d88, + 0x0000323c, 0x00000d8b, + 0x0000323d, 0x00000d8e, + 0x0000323e, 0x00000d91, + 0x0000323f, 0x00000d94, + 0x00003240, 0x00000d97, + 0x00003241, 0x00000d9a, + 0x00003242, 0x00000d9d, + 0x00003243, 0x00000da0, + 0x00003251, 0x00000da3, + 0x00003252, 0x00000da5, + 0x00003253, 0x00000da7, + 0x00003254, 0x00000da9, + 0x00003255, 0x00000dab, + 0x00003256, 0x00000dad, + 0x00003257, 0x00000daf, + 0x00003258, 0x00000db1, + 0x00003259, 0x00000db3, + 0x0000325a, 0x00000db5, + 0x0000325b, 0x00000db7, + 0x0000325c, 0x00000db9, + 0x0000325d, 0x00000dbb, + 0x0000325e, 0x00000dbd, + 0x0000325f, 0x00000dbf, + 0x00003260, 0x00000dc1, + 0x00003261, 0x00000dc2, + 0x00003262, 0x00000dc3, + 0x00003263, 0x00000dc4, + 0x00003264, 0x00000dc5, + 0x00003265, 0x00000dc6, + 0x00003266, 0x00000dc7, + 0x00003267, 0x00000dc8, + 0x00003268, 0x00000dc9, + 0x00003269, 0x00000dca, + 0x0000326a, 0x00000dcb, + 0x0000326b, 0x00000dcc, + 0x0000326c, 0x00000dcd, + 0x0000326d, 0x00000dce, + 0x0000326e, 0x00000dcf, + 0x0000326f, 0x00000dd1, + 0x00003270, 0x00000dd3, + 0x00003271, 0x00000dd5, + 0x00003272, 0x00000dd7, + 0x00003273, 0x00000dd9, + 0x00003274, 0x00000ddb, + 0x00003275, 0x00000ddd, + 0x00003276, 0x00000ddf, + 0x00003277, 0x00000de1, + 0x00003278, 0x00000de3, + 0x00003279, 0x00000de5, + 0x0000327a, 0x00000de7, + 0x0000327b, 0x00000de9, + 0x00003280, 0x00000deb, + 0x00003281, 0x00000dec, + 0x00003282, 0x00000ded, + 0x00003283, 0x00000dee, + 0x00003284, 0x00000def, + 0x00003285, 0x00000df0, + 0x00003286, 0x00000df1, + 0x00003287, 0x00000df2, + 0x00003288, 0x00000df3, + 0x00003289, 0x00000df4, + 0x0000328a, 0x00000df5, + 0x0000328b, 0x00000df6, + 0x0000328c, 0x00000df7, + 0x0000328d, 0x00000df8, + 0x0000328e, 0x00000df9, + 0x0000328f, 0x00000dfa, + 0x00003290, 0x00000dfb, + 0x00003291, 0x00000dfc, + 0x00003292, 0x00000dfd, + 0x00003293, 0x00000dfe, + 0x00003294, 0x00000dff, + 0x00003295, 0x00000e00, + 0x00003296, 0x00000e01, + 0x00003297, 0x00000e02, + 0x00003298, 0x00000e03, + 0x00003299, 0x00000e04, + 0x0000329a, 0x00000e05, + 0x0000329b, 0x00000e06, + 0x0000329c, 0x00000e07, + 0x0000329d, 0x00000e08, + 0x0000329e, 0x00000e09, + 0x0000329f, 0x00000e0a, + 0x000032a0, 0x00000e0b, + 0x000032a1, 0x00000e0c, + 0x000032a2, 0x00000e0d, + 0x000032a3, 0x00000e0e, + 0x000032a4, 0x00000e0f, + 0x000032a5, 0x00000e10, + 0x000032a6, 0x00000e11, + 0x000032a7, 0x00000e12, + 0x000032a8, 0x00000e13, + 0x000032a9, 0x00000e14, + 0x000032aa, 0x00000e15, + 0x000032ab, 0x00000e16, + 0x000032ac, 0x00000e17, + 0x000032ad, 0x00000e18, + 0x000032ae, 0x00000e19, + 0x000032af, 0x00000e1a, + 0x000032b0, 0x00000e1b, + 0x000032b1, 0x00000e1c, + 0x000032b2, 0x00000e1e, + 0x000032b3, 0x00000e20, + 0x000032b4, 0x00000e22, + 0x000032b5, 0x00000e24, + 0x000032b6, 0x00000e26, + 0x000032b7, 0x00000e28, + 0x000032b8, 0x00000e2a, + 0x000032b9, 0x00000e2c, + 0x000032ba, 0x00000e2e, + 0x000032bb, 0x00000e30, + 0x000032bc, 0x00000e32, + 0x000032bd, 0x00000e34, + 0x000032be, 0x00000e36, + 0x000032bf, 0x00000e38, + 0x000032c0, 0x00000e3a, + 0x000032c1, 0x00000e3c, + 0x000032c2, 0x00000e3e, + 0x000032c3, 0x00000e40, + 0x000032c4, 0x00000e42, + 0x000032c5, 0x00000e44, + 0x000032c6, 0x00000e46, + 0x000032c7, 0x00000e48, + 0x000032c8, 0x00000e4a, + 0x000032c9, 0x00000e4c, + 0x000032ca, 0x00000e4f, + 0x000032cb, 0x00000e52, + 0x000032d0, 0x00000e55, + 0x000032d1, 0x00000e56, + 0x000032d2, 0x00000e57, + 0x000032d3, 0x00000e58, + 0x000032d4, 0x00000e59, + 0x000032d5, 0x00000e5a, + 0x000032d6, 0x00000e5b, + 0x000032d7, 0x00000e5c, + 0x000032d8, 0x00000e5d, + 0x000032d9, 0x00000e5e, + 0x000032da, 0x00000e5f, + 0x000032db, 0x00000e60, + 0x000032dc, 0x00000e61, + 0x000032dd, 0x00000e62, + 0x000032de, 0x00000e63, + 0x000032df, 0x00000e64, + 0x000032e0, 0x00000e65, + 0x000032e1, 0x00000e66, + 0x000032e2, 0x00000e67, + 0x000032e3, 0x00000e68, + 0x000032e4, 0x00000e69, + 0x000032e5, 0x00000e6a, + 0x000032e6, 0x00000e6b, + 0x000032e7, 0x00000e6c, + 0x000032e8, 0x00000e6d, + 0x000032e9, 0x00000e6e, + 0x000032ea, 0x00000e6f, + 0x000032eb, 0x00000e70, + 0x000032ec, 0x00000e71, + 0x000032ed, 0x00000e72, + 0x000032ee, 0x00000e73, + 0x000032ef, 0x00000e74, + 0x000032f0, 0x00000e75, + 0x000032f1, 0x00000e76, + 0x000032f2, 0x00000e77, + 0x000032f3, 0x00000e78, + 0x000032f4, 0x00000e79, + 0x000032f5, 0x00000e7a, + 0x000032f6, 0x00000e7b, + 0x000032f7, 0x00000e7c, + 0x000032f8, 0x00000e7d, + 0x000032f9, 0x00000e7e, + 0x000032fa, 0x00000e7f, + 0x000032fb, 0x00000e80, + 0x000032fc, 0x00000e81, + 0x000032fd, 0x00000e82, + 0x000032fe, 0x00000e83, + 0x00003300, 0x00000e84, + 0x00003301, 0x00000e89, + 0x00003302, 0x00000e8d, + 0x00003303, 0x00000e92, + 0x00003304, 0x00000e95, + 0x00003305, 0x00000e9a, + 0x00003306, 0x00000e9d, + 0x00003307, 0x00000ea0, + 0x00003308, 0x00000ea6, + 0x00003309, 0x00000eaa, + 0x0000330a, 0x00000ead, + 0x0000330b, 0x00000eb0, + 0x0000330c, 0x00000eb3, + 0x0000330d, 0x00000eb7, + 0x0000330e, 0x00000ebb, + 0x0000330f, 0x00000ebf, + 0x00003310, 0x00000ec3, + 0x00003311, 0x00000ec7, + 0x00003312, 0x00000ecb, + 0x00003313, 0x00000ecf, + 0x00003314, 0x00000ed5, + 0x00003315, 0x00000ed7, + 0x00003316, 0x00000edd, + 0x00003317, 0x00000ee3, + 0x00003318, 0x00000ee8, + 0x00003319, 0x00000eec, + 0x0000331a, 0x00000ef2, + 0x0000331b, 0x00000ef8, + 0x0000331c, 0x00000efc, + 0x0000331d, 0x00000eff, + 0x0000331e, 0x00000f02, + 0x0000331f, 0x00000f06, + 0x00003320, 0x00000f0a, + 0x00003321, 0x00000f0f, + 0x00003322, 0x00000f14, + 0x00003323, 0x00000f17, + 0x00003324, 0x00000f1a, + 0x00003325, 0x00000f1e, + 0x00003326, 0x00000f21, + 0x00003327, 0x00000f24, + 0x00003328, 0x00000f26, + 0x00003329, 0x00000f28, + 0x0000332a, 0x00000f2b, + 0x0000332b, 0x00000f2e, + 0x0000332c, 0x00000f34, + 0x0000332d, 0x00000f38, + 0x0000332e, 0x00000f3d, + 0x0000332f, 0x00000f43, + 0x00003330, 0x00000f47, + 0x00003331, 0x00000f4a, + 0x00003332, 0x00000f4d, + 0x00003333, 0x00000f53, + 0x00003334, 0x00000f57, + 0x00003335, 0x00000f5d, + 0x00003336, 0x00000f60, + 0x00003337, 0x00000f65, + 0x00003338, 0x00000f68, + 0x00003339, 0x00000f6c, + 0x0000333a, 0x00000f6f, + 0x0000333b, 0x00000f73, + 0x0000333c, 0x00000f78, + 0x0000333d, 0x00000f7c, + 0x0000333e, 0x00000f81, + 0x0000333f, 0x00000f85, + 0x00003340, 0x00000f87, + 0x00003341, 0x00000f8c, + 0x00003342, 0x00000f8f, + 0x00003343, 0x00000f92, + 0x00003344, 0x00000f96, + 0x00003345, 0x00000f99, + 0x00003346, 0x00000f9c, + 0x00003347, 0x00000f9f, + 0x00003348, 0x00000fa4, + 0x00003349, 0x00000fa8, + 0x0000334a, 0x00000faa, + 0x0000334b, 0x00000fb0, + 0x0000334c, 0x00000fb3, + 0x0000334d, 0x00000fb8, + 0x0000334e, 0x00000fbc, + 0x0000334f, 0x00000fc0, + 0x00003350, 0x00000fc3, + 0x00003351, 0x00000fc6, + 0x00003352, 0x00000fca, + 0x00003353, 0x00000fcc, + 0x00003354, 0x00000fd0, + 0x00003355, 0x00000fd5, + 0x00003356, 0x00000fd7, + 0x00003357, 0x00000fdd, + 0x00003358, 0x00000fe0, + 0x00003359, 0x00000fe2, + 0x0000335a, 0x00000fe4, + 0x0000335b, 0x00000fe6, + 0x0000335c, 0x00000fe8, + 0x0000335d, 0x00000fea, + 0x0000335e, 0x00000fec, + 0x0000335f, 0x00000fee, + 0x00003360, 0x00000ff0, + 0x00003361, 0x00000ff2, + 0x00003362, 0x00000ff4, + 0x00003363, 0x00000ff7, + 0x00003364, 0x00000ffa, + 0x00003365, 0x00000ffd, + 0x00003366, 0x00001000, + 0x00003367, 0x00001003, + 0x00003368, 0x00001006, + 0x00003369, 0x00001009, + 0x0000336a, 0x0000100c, + 0x0000336b, 0x0000100f, + 0x0000336c, 0x00001012, + 0x0000336d, 0x00001015, + 0x0000336e, 0x00001018, + 0x0000336f, 0x0000101b, + 0x00003370, 0x0000101e, + 0x00003371, 0x00001021, + 0x00003372, 0x00001024, + 0x00003373, 0x00001026, + 0x00003374, 0x00001028, + 0x00003375, 0x0000102b, + 0x00003376, 0x0000102d, + 0x0000337b, 0x0000102f, + 0x0000337c, 0x00001031, + 0x0000337d, 0x00001033, + 0x0000337e, 0x00001035, + 0x0000337f, 0x00001037, + 0x00003380, 0x0000103b, + 0x00003381, 0x0000103d, + 0x00003382, 0x0000103f, + 0x00003383, 0x00001041, + 0x00003384, 0x00001043, + 0x00003385, 0x00001045, + 0x00003386, 0x00001047, + 0x00003387, 0x00001049, + 0x00003388, 0x0000104b, + 0x00003389, 0x0000104e, + 0x0000338a, 0x00001052, + 0x0000338b, 0x00001054, + 0x0000338c, 0x00001056, + 0x0000338d, 0x00001058, + 0x0000338e, 0x0000105a, + 0x0000338f, 0x0000105c, + 0x00003390, 0x0000105e, + 0x00003391, 0x00001060, + 0x00003392, 0x00001063, + 0x00003393, 0x00001066, + 0x00003394, 0x00001069, + 0x00003395, 0x0000106c, + 0x00003396, 0x0000106e, + 0x00003397, 0x00001070, + 0x00003398, 0x00001072, + 0x00003399, 0x00001074, + 0x0000339a, 0x00001076, + 0x0000339b, 0x00001078, + 0x0000339c, 0x0000107a, + 0x0000339d, 0x0000107c, + 0x0000339e, 0x0000107e, + 0x0000339f, 0x00001080, + 0x000033a0, 0x00001083, + 0x000033a1, 0x00001086, + 0x000033a2, 0x00001088, + 0x000033a3, 0x0000108b, + 0x000033a4, 0x0000108e, + 0x000033a5, 0x00001091, + 0x000033a6, 0x00001093, + 0x000033a7, 0x00001096, + 0x000033a8, 0x00001099, + 0x000033a9, 0x0000109d, + 0x000033aa, 0x0000109f, + 0x000033ab, 0x000010a2, + 0x000033ac, 0x000010a5, + 0x000033ad, 0x000010a8, + 0x000033ae, 0x000010ab, + 0x000033af, 0x000010b0, + 0x000033b0, 0x000010b6, + 0x000033b1, 0x000010b8, + 0x000033b2, 0x000010ba, + 0x000033b3, 0x000010bc, + 0x000033b4, 0x000010be, + 0x000033b5, 0x000010c0, + 0x000033b6, 0x000010c2, + 0x000033b7, 0x000010c4, + 0x000033b8, 0x000010c6, + 0x000033b9, 0x000010c8, + 0x000033ba, 0x000010ca, + 0x000033bb, 0x000010cc, + 0x000033bc, 0x000010ce, + 0x000033bd, 0x000010d0, + 0x000033be, 0x000010d2, + 0x000033bf, 0x000010d4, + 0x000033c0, 0x000010d6, + 0x000033c1, 0x000010d8, + 0x000033c2, 0x000010da, + 0x000033c3, 0x000010de, + 0x000033c4, 0x000010e0, + 0x000033c5, 0x000010e2, + 0x000033c6, 0x000010e4, + 0x000033c7, 0x000010e8, + 0x000033c8, 0x000010eb, + 0x000033c9, 0x000010ed, + 0x000033ca, 0x000010ef, + 0x000033cb, 0x000010f1, + 0x000033cc, 0x000010f3, + 0x000033cd, 0x000010f5, + 0x000033ce, 0x000010f7, + 0x000033cf, 0x000010f9, + 0x000033d0, 0x000010fb, + 0x000033d1, 0x000010fd, + 0x000033d2, 0x000010ff, + 0x000033d3, 0x00001102, + 0x000033d4, 0x00001104, + 0x000033d5, 0x00001106, + 0x000033d6, 0x00001109, + 0x000033d7, 0x0000110c, + 0x000033d8, 0x0000110e, + 0x000033d9, 0x00001112, + 0x000033da, 0x00001115, + 0x000033db, 0x00001117, + 0x000033dc, 0x00001119, + 0x000033dd, 0x0000111b, + 0x000033e0, 0x0000111d, + 0x000033e1, 0x0000111f, + 0x000033e2, 0x00001121, + 0x000033e3, 0x00001123, + 0x000033e4, 0x00001125, + 0x000033e5, 0x00001127, + 0x000033e6, 0x00001129, + 0x000033e7, 0x0000112b, + 0x000033e8, 0x0000112d, + 0x000033e9, 0x0000112f, + 0x000033ea, 0x00001132, + 0x000033eb, 0x00001135, + 0x000033ec, 0x00001138, + 0x000033ed, 0x0000113b, + 0x000033ee, 0x0000113e, + 0x000033ef, 0x00001141, + 0x000033f0, 0x00001144, + 0x000033f1, 0x00001147, + 0x000033f2, 0x0000114a, + 0x000033f3, 0x0000114d, + 0x000033f4, 0x00001150, + 0x000033f5, 0x00001153, + 0x000033f6, 0x00001156, + 0x000033f7, 0x00001159, + 0x000033f8, 0x0000115c, + 0x000033f9, 0x0000115f, + 0x000033fa, 0x00001162, + 0x000033fb, 0x00001165, + 0x000033fc, 0x00001168, + 0x000033fd, 0x0000116b, + 0x000033fe, 0x0000116e, + 0x0000f902, 0x00001171, + 0x0000f903, 0x00001172, + 0x0000f904, 0x00001173, + 0x0000f905, 0x00001174, + 0x0000f906, 0x00001175, + 0x0000f907, 0x00001176, + 0x0000f908, 0x00001177, + 0x0000f909, 0x00001178, + 0x0000f90a, 0x00001179, + 0x0000f90b, 0x0000117a, + 0x0000f90c, 0x0000117b, + 0x0000f90d, 0x0000117c, + 0x0000f90e, 0x0000117d, + 0x0000f90f, 0x0000117e, + 0x0000f910, 0x0000117f, + 0x0000f911, 0x00001180, + 0x0000f912, 0x00001181, + 0x0000f913, 0x00001182, + 0x0000f914, 0x00001183, + 0x0000f915, 0x00001184, + 0x0000f916, 0x00001185, + 0x0000f917, 0x00001186, + 0x0000f918, 0x00001187, + 0x0000f919, 0x00001188, + 0x0000f91a, 0x00001189, + 0x0000f91b, 0x0000118a, + 0x0000f91c, 0x0000118b, + 0x0000f91d, 0x0000118c, + 0x0000f91e, 0x0000118d, + 0x0000f91f, 0x0000118e, + 0x0000f920, 0x0000118f, + 0x0000f921, 0x00001190, + 0x0000f922, 0x00001191, + 0x0000f923, 0x00001192, + 0x0000f924, 0x00001193, + 0x0000f925, 0x00001194, + 0x0000f926, 0x00001195, + 0x0000f927, 0x00001196, + 0x0000f928, 0x00001197, + 0x0000f929, 0x00001198, + 0x0000f92a, 0x00001199, + 0x0000f92b, 0x0000119a, + 0x0000f92c, 0x0000119b, + 0x0000f92d, 0x0000119c, + 0x0000f92e, 0x0000119d, + 0x0000f92f, 0x0000119e, + 0x0000f930, 0x0000119f, + 0x0000f931, 0x000011a0, + 0x0000f932, 0x000011a1, + 0x0000f933, 0x000011a2, + 0x0000f934, 0x000011a3, + 0x0000f935, 0x000011a4, + 0x0000f936, 0x000011a5, + 0x0000f937, 0x000011a6, + 0x0000f938, 0x000011a7, + 0x0000f939, 0x000011a8, + 0x0000f93a, 0x000011a9, + 0x0000f93b, 0x000011aa, + 0x0000f93c, 0x000011ab, + 0x0000f93d, 0x000011ac, + 0x0000f93e, 0x000011ad, + 0x0000f93f, 0x000011ae, + 0x0000f940, 0x000011af, + 0x0000f941, 0x000011b0, + 0x0000f942, 0x000011b1, + 0x0000f943, 0x000011b2, + 0x0000f944, 0x000011b3, + 0x0000f945, 0x000011b4, + 0x0000f946, 0x000011b5, + 0x0000f947, 0x000011b6, + 0x0000f948, 0x000011b7, + 0x0000f949, 0x000011b8, + 0x0000f94a, 0x000011b9, + 0x0000f94b, 0x000011ba, + 0x0000f94c, 0x000011bb, + 0x0000f94d, 0x000011bc, + 0x0000f94e, 0x000011bd, + 0x0000f94f, 0x000011be, + 0x0000f950, 0x000011bf, + 0x0000f951, 0x000011c0, + 0x0000f952, 0x000011c1, + 0x0000f953, 0x000011c2, + 0x0000f954, 0x000011c3, + 0x0000f955, 0x000011c4, + 0x0000f956, 0x000011c5, + 0x0000f957, 0x000011c6, + 0x0000f958, 0x000011c7, + 0x0000f959, 0x000011c8, + 0x0000f95a, 0x000011c9, + 0x0000f95b, 0x000011ca, + 0x0000f95c, 0x000011cb, + 0x0000f95d, 0x000011cc, + 0x0000f95e, 0x000011cd, + 0x0000f95f, 0x000011ce, + 0x0000f960, 0x000011cf, + 0x0000f961, 0x000011d0, + 0x0000f962, 0x000011d1, + 0x0000f963, 0x000011d2, + 0x0000f964, 0x000011d3, + 0x0000f965, 0x000011d4, + 0x0000f966, 0x000011d5, + 0x0000f967, 0x000011d6, + 0x0000f968, 0x000011d7, + 0x0000f969, 0x000011d8, + 0x0000f96a, 0x000011d9, + 0x0000f96b, 0x000011da, + 0x0000f96c, 0x000011db, + 0x0000f96d, 0x000011dc, + 0x0000f96e, 0x000011dd, + 0x0000f96f, 0x000011de, + 0x0000f970, 0x000011df, + 0x0000f971, 0x000011e0, + 0x0000f972, 0x000011e1, + 0x0000f973, 0x000011e2, + 0x0000f974, 0x000011e3, + 0x0000f975, 0x000011e4, + 0x0000f976, 0x000011e5, + 0x0000f977, 0x000011e6, + 0x0000f978, 0x000011e7, + 0x0000f979, 0x000011e8, + 0x0000f97a, 0x000011e9, + 0x0000f97b, 0x000011ea, + 0x0000f97c, 0x000011eb, + 0x0000f97d, 0x000011ec, + 0x0000f97e, 0x000011ed, + 0x0000f97f, 0x000011ee, + 0x0000f980, 0x000011ef, + 0x0000f981, 0x000011f0, + 0x0000f982, 0x000011f1, + 0x0000f983, 0x000011f2, + 0x0000f984, 0x000011f3, + 0x0000f985, 0x000011f4, + 0x0000f986, 0x000011f5, + 0x0000f987, 0x000011f6, + 0x0000f988, 0x000011f7, + 0x0000f989, 0x000011f8, + 0x0000f98a, 0x000011f9, + 0x0000f98b, 0x000011fa, + 0x0000f98c, 0x000011fb, + 0x0000f98d, 0x000011fc, + 0x0000f98e, 0x000011fd, + 0x0000f98f, 0x000011fe, + 0x0000f990, 0x000011ff, + 0x0000f991, 0x00001200, + 0x0000f992, 0x00001201, + 0x0000f993, 0x00001202, + 0x0000f994, 0x00001203, + 0x0000f995, 0x00001204, + 0x0000f996, 0x00001205, + 0x0000f997, 0x00001206, + 0x0000f998, 0x00001207, + 0x0000f999, 0x00001208, + 0x0000f99a, 0x00001209, + 0x0000f99b, 0x0000120a, + 0x0000f99c, 0x0000120b, + 0x0000f99d, 0x0000120c, + 0x0000f99e, 0x0000120d, + 0x0000f99f, 0x0000120e, + 0x0000f9a0, 0x0000120f, + 0x0000f9a1, 0x00001210, + 0x0000f9a2, 0x00001211, + 0x0000f9a3, 0x00001212, + 0x0000f9a4, 0x00001213, + 0x0000f9a5, 0x00001214, + 0x0000f9a6, 0x00001215, + 0x0000f9a7, 0x00001216, + 0x0000f9a8, 0x00001217, + 0x0000f9a9, 0x00001218, + 0x0000f9aa, 0x00001219, + 0x0000f9ab, 0x0000121a, + 0x0000f9ac, 0x0000121b, + 0x0000f9ad, 0x0000121c, + 0x0000f9ae, 0x0000121d, + 0x0000f9af, 0x0000121e, + 0x0000f9b0, 0x0000121f, + 0x0000f9b1, 0x00001220, + 0x0000f9b2, 0x00001221, + 0x0000f9b3, 0x00001222, + 0x0000f9b4, 0x00001223, + 0x0000f9b5, 0x00001224, + 0x0000f9b6, 0x00001225, + 0x0000f9b7, 0x00001226, + 0x0000f9b8, 0x00001227, + 0x0000f9b9, 0x00001228, + 0x0000f9ba, 0x00001229, + 0x0000f9bb, 0x0000122a, + 0x0000f9bc, 0x0000122b, + 0x0000f9bd, 0x0000122c, + 0x0000f9be, 0x0000122d, + 0x0000f9bf, 0x0000122e, + 0x0000f9c0, 0x0000122f, + 0x0000f9c1, 0x00001230, + 0x0000f9c2, 0x00001231, + 0x0000f9c3, 0x00001232, + 0x0000f9c4, 0x00001233, + 0x0000f9c5, 0x00001234, + 0x0000f9c6, 0x00001235, + 0x0000f9c7, 0x00001236, + 0x0000f9c8, 0x00001237, + 0x0000f9c9, 0x00001238, + 0x0000f9ca, 0x00001239, + 0x0000f9cb, 0x0000123a, + 0x0000f9cc, 0x0000123b, + 0x0000f9cd, 0x0000123c, + 0x0000f9ce, 0x0000123d, + 0x0000f9cf, 0x0000123e, + 0x0000f9d0, 0x0000123f, + 0x0000f9d1, 0x00001240, + 0x0000f9d2, 0x00001241, + 0x0000f9d3, 0x00001242, + 0x0000f9d4, 0x00001243, + 0x0000f9d5, 0x00001244, + 0x0000f9d6, 0x00001245, + 0x0000f9d7, 0x00001246, + 0x0000f9d8, 0x00001247, + 0x0000f9d9, 0x00001248, + 0x0000f9da, 0x00001249, + 0x0000f9db, 0x0000124a, + 0x0000f9dc, 0x0000124b, + 0x0000f9dd, 0x0000124c, + 0x0000f9de, 0x0000124d, + 0x0000f9df, 0x0000124e, + 0x0000f9e0, 0x0000124f, + 0x0000f9e1, 0x00001250, + 0x0000f9e2, 0x00001251, + 0x0000f9e3, 0x00001252, + 0x0000f9e4, 0x00001253, + 0x0000f9e5, 0x00001254, + 0x0000f9e6, 0x00001255, + 0x0000f9e7, 0x00001256, + 0x0000f9e8, 0x00001257, + 0x0000f9e9, 0x00001258, + 0x0000f9ea, 0x00001259, + 0x0000f9eb, 0x0000125a, + 0x0000f9ec, 0x0000125b, + 0x0000f9ed, 0x0000125c, + 0x0000f9ee, 0x0000125d, + 0x0000f9ef, 0x0000125e, + 0x0000f9f0, 0x0000125f, + 0x0000f9f1, 0x00001260, + 0x0000f9f2, 0x00001261, + 0x0000f9f3, 0x00001262, + 0x0000f9f4, 0x00001263, + 0x0000f9f5, 0x00001264, + 0x0000f9f6, 0x00001265, + 0x0000f9f7, 0x00001266, + 0x0000f9f8, 0x00001267, + 0x0000f9f9, 0x00001268, + 0x0000f9fa, 0x00001269, + 0x0000f9fb, 0x0000126a, + 0x0000f9fc, 0x0000126b, + 0x0000f9fd, 0x0000126c, + 0x0000f9fe, 0x0000126d, + 0x0000f9ff, 0x0000126e, + 0x0000fa00, 0x0000126f, + 0x0000fa01, 0x00001270, + 0x0000fa02, 0x00001271, + 0x0000fa03, 0x00001272, + 0x0000fa04, 0x00001273, + 0x0000fa05, 0x00001274, + 0x0000fa06, 0x00001275, + 0x0000fa07, 0x00001276, + 0x0000fa08, 0x00001277, + 0x0000fa09, 0x00001278, + 0x0000fa0a, 0x00001279, + 0x0000fa0b, 0x0000127a, + 0x0000fa0c, 0x0000127b, + 0x0000fa0d, 0x0000127c, + 0x0000fa10, 0x0000127d, + 0x0000fa12, 0x0000127e, + 0x0000fa15, 0x0000127f, + 0x0000fa16, 0x00001280, + 0x0000fa17, 0x00001281, + 0x0000fa18, 0x00001282, + 0x0000fa19, 0x00001283, + 0x0000fa1a, 0x00001284, + 0x0000fa1b, 0x00001285, + 0x0000fa1c, 0x00001286, + 0x0000fa1d, 0x00001287, + 0x0000fa1e, 0x00001288, + 0x0000fa20, 0x00001289, + 0x0000fa22, 0x0000128a, + 0x0000fa25, 0x0000128b, + 0x0000fa26, 0x0000128c, + 0x0000fa2a, 0x0000128d, + 0x0000fa2b, 0x0000128e, + 0x0000fa2c, 0x0000128f, + 0x0000fa2d, 0x00001290, + 0x0000fa30, 0x00001291, + 0x0000fa31, 0x00001292, + 0x0000fa32, 0x00001293, + 0x0000fa33, 0x00001294, + 0x0000fa34, 0x00001295, + 0x0000fa35, 0x00001296, + 0x0000fa36, 0x00001297, + 0x0000fa37, 0x00001298, + 0x0000fa38, 0x00001299, + 0x0000fa39, 0x0000129a, + 0x0000fa3a, 0x0000129b, + 0x0000fa3b, 0x0000129c, + 0x0000fa3c, 0x0000129d, + 0x0000fa3d, 0x0000129e, + 0x0000fa3e, 0x0000129f, + 0x0000fa3f, 0x000012a0, + 0x0000fa40, 0x000012a1, + 0x0000fa41, 0x000012a2, + 0x0000fa42, 0x000012a3, + 0x0000fa43, 0x000012a4, + 0x0000fa44, 0x000012a5, + 0x0000fa45, 0x000012a6, + 0x0000fa46, 0x000012a7, + 0x0000fa47, 0x000012a8, + 0x0000fa48, 0x000012a9, + 0x0000fa49, 0x000012aa, + 0x0000fa4a, 0x000012ab, + 0x0000fa4b, 0x000012ac, + 0x0000fa4c, 0x000012ad, + 0x0000fa4d, 0x000012ae, + 0x0000fa4e, 0x000012af, + 0x0000fa4f, 0x000012b0, + 0x0000fa50, 0x000012b1, + 0x0000fa51, 0x000012b2, + 0x0000fa52, 0x000012b3, + 0x0000fa53, 0x000012b4, + 0x0000fa54, 0x000012b5, + 0x0000fa55, 0x000012b6, + 0x0000fa56, 0x000012b7, + 0x0000fa57, 0x000012b8, + 0x0000fa58, 0x000012b9, + 0x0000fa59, 0x000012ba, + 0x0000fa5a, 0x000012bb, + 0x0000fa5b, 0x000012bc, + 0x0000fa5c, 0x000012bd, + 0x0000fa5d, 0x000012be, + 0x0000fa5e, 0x000012bf, + 0x0000fa5f, 0x000012c0, + 0x0000fa60, 0x000012c1, + 0x0000fa61, 0x000012c2, + 0x0000fa62, 0x000012c3, + 0x0000fa63, 0x000012c4, + 0x0000fa64, 0x000012c5, + 0x0000fa65, 0x000012c6, + 0x0000fa66, 0x000012c7, + 0x0000fa67, 0x000012c8, + 0x0000fa68, 0x000012c9, + 0x0000fa69, 0x000012ca, + 0x0000fa6a, 0x000012cb, + 0x0000fb00, 0x000012cc, + 0x0000fb01, 0x000012ce, + 0x0000fb02, 0x000012d0, + 0x0000fb03, 0x000012d2, + 0x0000fb04, 0x000012d5, + 0x0000fb05, 0x000012d8, + 0x0000fb06, 0x000012da, + 0x0000fb13, 0x000012dc, + 0x0000fb14, 0x000012de, + 0x0000fb15, 0x000012e0, + 0x0000fb16, 0x000012e2, + 0x0000fb17, 0x000012e4, + 0x0000fb1d, 0x000012e6, + 0x0000fb1f, 0x000012e8, + 0x0000fb20, 0x000012ea, + 0x0000fb21, 0x000012eb, + 0x0000fb22, 0x000012ec, + 0x0000fb23, 0x000012ed, + 0x0000fb24, 0x000012ee, + 0x0000fb25, 0x000012ef, + 0x0000fb26, 0x000012f0, + 0x0000fb27, 0x000012f1, + 0x0000fb28, 0x000012f2, + 0x0000fb29, 0x000012f3, + 0x0000fb2a, 0x000012f4, + 0x0000fb2b, 0x000012f6, + 0x0000fb2c, 0x000012f8, + 0x0000fb2d, 0x000012fb, + 0x0000fb2e, 0x000012fe, + 0x0000fb2f, 0x00001300, + 0x0000fb30, 0x00001302, + 0x0000fb31, 0x00001304, + 0x0000fb32, 0x00001306, + 0x0000fb33, 0x00001308, + 0x0000fb34, 0x0000130a, + 0x0000fb35, 0x0000130c, + 0x0000fb36, 0x0000130e, + 0x0000fb38, 0x00001310, + 0x0000fb39, 0x00001312, + 0x0000fb3a, 0x00001314, + 0x0000fb3b, 0x00001316, + 0x0000fb3c, 0x00001318, + 0x0000fb3e, 0x0000131a, + 0x0000fb40, 0x0000131c, + 0x0000fb41, 0x0000131e, + 0x0000fb43, 0x00001320, + 0x0000fb44, 0x00001322, + 0x0000fb46, 0x00001324, + 0x0000fb47, 0x00001326, + 0x0000fb48, 0x00001328, + 0x0000fb49, 0x0000132a, + 0x0000fb4a, 0x0000132c, + 0x0000fb4b, 0x0000132e, + 0x0000fb4c, 0x00001330, + 0x0000fb4d, 0x00001332, + 0x0000fb4e, 0x00001334, + 0x0000fb4f, 0x00001336, + 0x0000fb50, 0x00001338, + 0x0000fb51, 0x00001339, + 0x0000fb52, 0x0000133a, + 0x0000fb53, 0x0000133b, + 0x0000fb54, 0x0000133c, + 0x0000fb55, 0x0000133d, + 0x0000fb56, 0x0000133e, + 0x0000fb57, 0x0000133f, + 0x0000fb58, 0x00001340, + 0x0000fb59, 0x00001341, + 0x0000fb5a, 0x00001342, + 0x0000fb5b, 0x00001343, + 0x0000fb5c, 0x00001344, + 0x0000fb5d, 0x00001345, + 0x0000fb5e, 0x00001346, + 0x0000fb5f, 0x00001347, + 0x0000fb60, 0x00001348, + 0x0000fb61, 0x00001349, + 0x0000fb62, 0x0000134a, + 0x0000fb63, 0x0000134b, + 0x0000fb64, 0x0000134c, + 0x0000fb65, 0x0000134d, + 0x0000fb66, 0x0000134e, + 0x0000fb67, 0x0000134f, + 0x0000fb68, 0x00001350, + 0x0000fb69, 0x00001351, + 0x0000fb6a, 0x00001352, + 0x0000fb6b, 0x00001353, + 0x0000fb6c, 0x00001354, + 0x0000fb6d, 0x00001355, + 0x0000fb6e, 0x00001356, + 0x0000fb6f, 0x00001357, + 0x0000fb70, 0x00001358, + 0x0000fb71, 0x00001359, + 0x0000fb72, 0x0000135a, + 0x0000fb73, 0x0000135b, + 0x0000fb74, 0x0000135c, + 0x0000fb75, 0x0000135d, + 0x0000fb76, 0x0000135e, + 0x0000fb77, 0x0000135f, + 0x0000fb78, 0x00001360, + 0x0000fb79, 0x00001361, + 0x0000fb7a, 0x00001362, + 0x0000fb7b, 0x00001363, + 0x0000fb7c, 0x00001364, + 0x0000fb7d, 0x00001365, + 0x0000fb7e, 0x00001366, + 0x0000fb7f, 0x00001367, + 0x0000fb80, 0x00001368, + 0x0000fb81, 0x00001369, + 0x0000fb82, 0x0000136a, + 0x0000fb83, 0x0000136b, + 0x0000fb84, 0x0000136c, + 0x0000fb85, 0x0000136d, + 0x0000fb86, 0x0000136e, + 0x0000fb87, 0x0000136f, + 0x0000fb88, 0x00001370, + 0x0000fb89, 0x00001371, + 0x0000fb8a, 0x00001372, + 0x0000fb8b, 0x00001373, + 0x0000fb8c, 0x00001374, + 0x0000fb8d, 0x00001375, + 0x0000fb8e, 0x00001376, + 0x0000fb8f, 0x00001377, + 0x0000fb90, 0x00001378, + 0x0000fb91, 0x00001379, + 0x0000fb92, 0x0000137a, + 0x0000fb93, 0x0000137b, + 0x0000fb94, 0x0000137c, + 0x0000fb95, 0x0000137d, + 0x0000fb96, 0x0000137e, + 0x0000fb97, 0x0000137f, + 0x0000fb98, 0x00001380, + 0x0000fb99, 0x00001381, + 0x0000fb9a, 0x00001382, + 0x0000fb9b, 0x00001383, + 0x0000fb9c, 0x00001384, + 0x0000fb9d, 0x00001385, + 0x0000fb9e, 0x00001386, + 0x0000fb9f, 0x00001387, + 0x0000fba0, 0x00001388, + 0x0000fba1, 0x00001389, + 0x0000fba2, 0x0000138a, + 0x0000fba3, 0x0000138b, + 0x0000fba4, 0x0000138c, + 0x0000fba5, 0x0000138e, + 0x0000fba6, 0x00001390, + 0x0000fba7, 0x00001391, + 0x0000fba8, 0x00001392, + 0x0000fba9, 0x00001393, + 0x0000fbaa, 0x00001394, + 0x0000fbab, 0x00001395, + 0x0000fbac, 0x00001396, + 0x0000fbad, 0x00001397, + 0x0000fbae, 0x00001398, + 0x0000fbaf, 0x00001399, + 0x0000fbb0, 0x0000139a, + 0x0000fbb1, 0x0000139c, + 0x0000fbd3, 0x0000139e, + 0x0000fbd4, 0x0000139f, + 0x0000fbd5, 0x000013a0, + 0x0000fbd6, 0x000013a1, + 0x0000fbd7, 0x000013a2, + 0x0000fbd8, 0x000013a3, + 0x0000fbd9, 0x000013a4, + 0x0000fbda, 0x000013a5, + 0x0000fbdb, 0x000013a6, + 0x0000fbdc, 0x000013a7, + 0x0000fbdd, 0x000013a8, + 0x0000fbde, 0x000013aa, + 0x0000fbdf, 0x000013ab, + 0x0000fbe0, 0x000013ac, + 0x0000fbe1, 0x000013ad, + 0x0000fbe2, 0x000013ae, + 0x0000fbe3, 0x000013af, + 0x0000fbe4, 0x000013b0, + 0x0000fbe5, 0x000013b1, + 0x0000fbe6, 0x000013b2, + 0x0000fbe7, 0x000013b3, + 0x0000fbe8, 0x000013b4, + 0x0000fbe9, 0x000013b5, + 0x0000fbea, 0x000013b6, + 0x0000fbeb, 0x000013b9, + 0x0000fbec, 0x000013bc, + 0x0000fbed, 0x000013bf, + 0x0000fbee, 0x000013c2, + 0x0000fbef, 0x000013c5, + 0x0000fbf0, 0x000013c8, + 0x0000fbf1, 0x000013cb, + 0x0000fbf2, 0x000013ce, + 0x0000fbf3, 0x000013d1, + 0x0000fbf4, 0x000013d4, + 0x0000fbf5, 0x000013d7, + 0x0000fbf6, 0x000013da, + 0x0000fbf7, 0x000013dd, + 0x0000fbf8, 0x000013e0, + 0x0000fbf9, 0x000013e3, + 0x0000fbfa, 0x000013e6, + 0x0000fbfb, 0x000013e9, + 0x0000fbfc, 0x000013ec, + 0x0000fbfd, 0x000013ed, + 0x0000fbfe, 0x000013ee, + 0x0000fbff, 0x000013ef, + 0x0000fc00, 0x000013f0, + 0x0000fc01, 0x000013f3, + 0x0000fc02, 0x000013f6, + 0x0000fc03, 0x000013f9, + 0x0000fc04, 0x000013fc, + 0x0000fc05, 0x000013ff, + 0x0000fc06, 0x00001401, + 0x0000fc07, 0x00001403, + 0x0000fc08, 0x00001405, + 0x0000fc09, 0x00001407, + 0x0000fc0a, 0x00001409, + 0x0000fc0b, 0x0000140b, + 0x0000fc0c, 0x0000140d, + 0x0000fc0d, 0x0000140f, + 0x0000fc0e, 0x00001411, + 0x0000fc0f, 0x00001413, + 0x0000fc10, 0x00001415, + 0x0000fc11, 0x00001417, + 0x0000fc12, 0x00001419, + 0x0000fc13, 0x0000141b, + 0x0000fc14, 0x0000141d, + 0x0000fc15, 0x0000141f, + 0x0000fc16, 0x00001421, + 0x0000fc17, 0x00001423, + 0x0000fc18, 0x00001425, + 0x0000fc19, 0x00001427, + 0x0000fc1a, 0x00001429, + 0x0000fc1b, 0x0000142b, + 0x0000fc1c, 0x0000142d, + 0x0000fc1d, 0x0000142f, + 0x0000fc1e, 0x00001431, + 0x0000fc1f, 0x00001433, + 0x0000fc20, 0x00001435, + 0x0000fc21, 0x00001437, + 0x0000fc22, 0x00001439, + 0x0000fc23, 0x0000143b, + 0x0000fc24, 0x0000143d, + 0x0000fc25, 0x0000143f, + 0x0000fc26, 0x00001441, + 0x0000fc27, 0x00001443, + 0x0000fc28, 0x00001445, + 0x0000fc29, 0x00001447, + 0x0000fc2a, 0x00001449, + 0x0000fc2b, 0x0000144b, + 0x0000fc2c, 0x0000144d, + 0x0000fc2d, 0x0000144f, + 0x0000fc2e, 0x00001451, + 0x0000fc2f, 0x00001453, + 0x0000fc30, 0x00001455, + 0x0000fc31, 0x00001457, + 0x0000fc32, 0x00001459, + 0x0000fc33, 0x0000145b, + 0x0000fc34, 0x0000145d, + 0x0000fc35, 0x0000145f, + 0x0000fc36, 0x00001461, + 0x0000fc37, 0x00001463, + 0x0000fc38, 0x00001465, + 0x0000fc39, 0x00001467, + 0x0000fc3a, 0x00001469, + 0x0000fc3b, 0x0000146b, + 0x0000fc3c, 0x0000146d, + 0x0000fc3d, 0x0000146f, + 0x0000fc3e, 0x00001471, + 0x0000fc3f, 0x00001473, + 0x0000fc40, 0x00001475, + 0x0000fc41, 0x00001477, + 0x0000fc42, 0x00001479, + 0x0000fc43, 0x0000147b, + 0x0000fc44, 0x0000147d, + 0x0000fc45, 0x0000147f, + 0x0000fc46, 0x00001481, + 0x0000fc47, 0x00001483, + 0x0000fc48, 0x00001485, + 0x0000fc49, 0x00001487, + 0x0000fc4a, 0x00001489, + 0x0000fc4b, 0x0000148b, + 0x0000fc4c, 0x0000148d, + 0x0000fc4d, 0x0000148f, + 0x0000fc4e, 0x00001491, + 0x0000fc4f, 0x00001493, + 0x0000fc50, 0x00001495, + 0x0000fc51, 0x00001497, + 0x0000fc52, 0x00001499, + 0x0000fc53, 0x0000149b, + 0x0000fc54, 0x0000149d, + 0x0000fc55, 0x0000149f, + 0x0000fc56, 0x000014a1, + 0x0000fc57, 0x000014a3, + 0x0000fc58, 0x000014a5, + 0x0000fc59, 0x000014a7, + 0x0000fc5a, 0x000014a9, + 0x0000fc5b, 0x000014ab, + 0x0000fc5c, 0x000014ad, + 0x0000fc5d, 0x000014af, + 0x0000fc5e, 0x000014b1, + 0x0000fc5f, 0x000014b4, + 0x0000fc60, 0x000014b7, + 0x0000fc61, 0x000014ba, + 0x0000fc62, 0x000014bd, + 0x0000fc63, 0x000014c0, + 0x0000fc64, 0x000014c3, + 0x0000fc65, 0x000014c6, + 0x0000fc66, 0x000014c9, + 0x0000fc67, 0x000014cc, + 0x0000fc68, 0x000014cf, + 0x0000fc69, 0x000014d2, + 0x0000fc6a, 0x000014d5, + 0x0000fc6b, 0x000014d7, + 0x0000fc6c, 0x000014d9, + 0x0000fc6d, 0x000014db, + 0x0000fc6e, 0x000014dd, + 0x0000fc6f, 0x000014df, + 0x0000fc70, 0x000014e1, + 0x0000fc71, 0x000014e3, + 0x0000fc72, 0x000014e5, + 0x0000fc73, 0x000014e7, + 0x0000fc74, 0x000014e9, + 0x0000fc75, 0x000014eb, + 0x0000fc76, 0x000014ed, + 0x0000fc77, 0x000014ef, + 0x0000fc78, 0x000014f1, + 0x0000fc79, 0x000014f3, + 0x0000fc7a, 0x000014f5, + 0x0000fc7b, 0x000014f7, + 0x0000fc7c, 0x000014f9, + 0x0000fc7d, 0x000014fb, + 0x0000fc7e, 0x000014fd, + 0x0000fc7f, 0x000014ff, + 0x0000fc80, 0x00001501, + 0x0000fc81, 0x00001503, + 0x0000fc82, 0x00001505, + 0x0000fc83, 0x00001507, + 0x0000fc84, 0x00001509, + 0x0000fc85, 0x0000150b, + 0x0000fc86, 0x0000150d, + 0x0000fc87, 0x0000150f, + 0x0000fc88, 0x00001511, + 0x0000fc89, 0x00001513, + 0x0000fc8a, 0x00001515, + 0x0000fc8b, 0x00001517, + 0x0000fc8c, 0x00001519, + 0x0000fc8d, 0x0000151b, + 0x0000fc8e, 0x0000151d, + 0x0000fc8f, 0x0000151f, + 0x0000fc90, 0x00001521, + 0x0000fc91, 0x00001523, + 0x0000fc92, 0x00001525, + 0x0000fc93, 0x00001527, + 0x0000fc94, 0x00001529, + 0x0000fc95, 0x0000152b, + 0x0000fc96, 0x0000152d, + 0x0000fc97, 0x0000152f, + 0x0000fc98, 0x00001532, + 0x0000fc99, 0x00001535, + 0x0000fc9a, 0x00001538, + 0x0000fc9b, 0x0000153b, + 0x0000fc9c, 0x0000153e, + 0x0000fc9d, 0x00001540, + 0x0000fc9e, 0x00001542, + 0x0000fc9f, 0x00001544, + 0x0000fca0, 0x00001546, + 0x0000fca1, 0x00001548, + 0x0000fca2, 0x0000154a, + 0x0000fca3, 0x0000154c, + 0x0000fca4, 0x0000154e, + 0x0000fca5, 0x00001550, + 0x0000fca6, 0x00001552, + 0x0000fca7, 0x00001554, + 0x0000fca8, 0x00001556, + 0x0000fca9, 0x00001558, + 0x0000fcaa, 0x0000155a, + 0x0000fcab, 0x0000155c, + 0x0000fcac, 0x0000155e, + 0x0000fcad, 0x00001560, + 0x0000fcae, 0x00001562, + 0x0000fcaf, 0x00001564, + 0x0000fcb0, 0x00001566, + 0x0000fcb1, 0x00001568, + 0x0000fcb2, 0x0000156a, + 0x0000fcb3, 0x0000156c, + 0x0000fcb4, 0x0000156e, + 0x0000fcb5, 0x00001570, + 0x0000fcb6, 0x00001572, + 0x0000fcb7, 0x00001574, + 0x0000fcb8, 0x00001576, + 0x0000fcb9, 0x00001578, + 0x0000fcba, 0x0000157a, + 0x0000fcbb, 0x0000157c, + 0x0000fcbc, 0x0000157e, + 0x0000fcbd, 0x00001580, + 0x0000fcbe, 0x00001582, + 0x0000fcbf, 0x00001584, + 0x0000fcc0, 0x00001586, + 0x0000fcc1, 0x00001588, + 0x0000fcc2, 0x0000158a, + 0x0000fcc3, 0x0000158c, + 0x0000fcc4, 0x0000158e, + 0x0000fcc5, 0x00001590, + 0x0000fcc6, 0x00001592, + 0x0000fcc7, 0x00001594, + 0x0000fcc8, 0x00001596, + 0x0000fcc9, 0x00001598, + 0x0000fcca, 0x0000159a, + 0x0000fccb, 0x0000159c, + 0x0000fccc, 0x0000159e, + 0x0000fccd, 0x000015a0, + 0x0000fcce, 0x000015a2, + 0x0000fccf, 0x000015a4, + 0x0000fcd0, 0x000015a6, + 0x0000fcd1, 0x000015a8, + 0x0000fcd2, 0x000015aa, + 0x0000fcd3, 0x000015ac, + 0x0000fcd4, 0x000015ae, + 0x0000fcd5, 0x000015b0, + 0x0000fcd6, 0x000015b2, + 0x0000fcd7, 0x000015b4, + 0x0000fcd8, 0x000015b6, + 0x0000fcd9, 0x000015b8, + 0x0000fcda, 0x000015ba, + 0x0000fcdb, 0x000015bc, + 0x0000fcdc, 0x000015be, + 0x0000fcdd, 0x000015c0, + 0x0000fcde, 0x000015c2, + 0x0000fcdf, 0x000015c4, + 0x0000fce0, 0x000015c7, + 0x0000fce1, 0x000015ca, + 0x0000fce2, 0x000015cc, + 0x0000fce3, 0x000015ce, + 0x0000fce4, 0x000015d0, + 0x0000fce5, 0x000015d2, + 0x0000fce6, 0x000015d4, + 0x0000fce7, 0x000015d6, + 0x0000fce8, 0x000015d8, + 0x0000fce9, 0x000015da, + 0x0000fcea, 0x000015dc, + 0x0000fceb, 0x000015de, + 0x0000fcec, 0x000015e0, + 0x0000fced, 0x000015e2, + 0x0000fcee, 0x000015e4, + 0x0000fcef, 0x000015e6, + 0x0000fcf0, 0x000015e8, + 0x0000fcf1, 0x000015ea, + 0x0000fcf2, 0x000015ec, + 0x0000fcf3, 0x000015ef, + 0x0000fcf4, 0x000015f2, + 0x0000fcf5, 0x000015f5, + 0x0000fcf6, 0x000015f7, + 0x0000fcf7, 0x000015f9, + 0x0000fcf8, 0x000015fb, + 0x0000fcf9, 0x000015fd, + 0x0000fcfa, 0x000015ff, + 0x0000fcfb, 0x00001601, + 0x0000fcfc, 0x00001603, + 0x0000fcfd, 0x00001605, + 0x0000fcfe, 0x00001607, + 0x0000fcff, 0x00001609, + 0x0000fd00, 0x0000160b, + 0x0000fd01, 0x0000160d, + 0x0000fd02, 0x0000160f, + 0x0000fd03, 0x00001611, + 0x0000fd04, 0x00001613, + 0x0000fd05, 0x00001615, + 0x0000fd06, 0x00001617, + 0x0000fd07, 0x00001619, + 0x0000fd08, 0x0000161b, + 0x0000fd09, 0x0000161d, + 0x0000fd0a, 0x0000161f, + 0x0000fd0b, 0x00001621, + 0x0000fd0c, 0x00001623, + 0x0000fd0d, 0x00001625, + 0x0000fd0e, 0x00001627, + 0x0000fd0f, 0x00001629, + 0x0000fd10, 0x0000162b, + 0x0000fd11, 0x0000162d, + 0x0000fd12, 0x0000162f, + 0x0000fd13, 0x00001631, + 0x0000fd14, 0x00001633, + 0x0000fd15, 0x00001635, + 0x0000fd16, 0x00001637, + 0x0000fd17, 0x00001639, + 0x0000fd18, 0x0000163b, + 0x0000fd19, 0x0000163d, + 0x0000fd1a, 0x0000163f, + 0x0000fd1b, 0x00001641, + 0x0000fd1c, 0x00001643, + 0x0000fd1d, 0x00001645, + 0x0000fd1e, 0x00001647, + 0x0000fd1f, 0x00001649, + 0x0000fd20, 0x0000164b, + 0x0000fd21, 0x0000164d, + 0x0000fd22, 0x0000164f, + 0x0000fd23, 0x00001651, + 0x0000fd24, 0x00001653, + 0x0000fd25, 0x00001655, + 0x0000fd26, 0x00001657, + 0x0000fd27, 0x00001659, + 0x0000fd28, 0x0000165b, + 0x0000fd29, 0x0000165d, + 0x0000fd2a, 0x0000165f, + 0x0000fd2b, 0x00001661, + 0x0000fd2c, 0x00001663, + 0x0000fd2d, 0x00001665, + 0x0000fd2e, 0x00001667, + 0x0000fd2f, 0x00001669, + 0x0000fd30, 0x0000166b, + 0x0000fd31, 0x0000166d, + 0x0000fd32, 0x0000166f, + 0x0000fd33, 0x00001671, + 0x0000fd34, 0x00001673, + 0x0000fd35, 0x00001675, + 0x0000fd36, 0x00001677, + 0x0000fd37, 0x00001679, + 0x0000fd38, 0x0000167b, + 0x0000fd39, 0x0000167d, + 0x0000fd3a, 0x0000167f, + 0x0000fd3b, 0x00001681, + 0x0000fd3c, 0x00001683, + 0x0000fd3d, 0x00001685, + 0x0000fd50, 0x00001687, + 0x0000fd51, 0x0000168a, + 0x0000fd52, 0x0000168d, + 0x0000fd53, 0x00001690, + 0x0000fd54, 0x00001693, + 0x0000fd55, 0x00001696, + 0x0000fd56, 0x00001699, + 0x0000fd57, 0x0000169c, + 0x0000fd58, 0x0000169f, + 0x0000fd59, 0x000016a2, + 0x0000fd5a, 0x000016a5, + 0x0000fd5b, 0x000016a8, + 0x0000fd5c, 0x000016ab, + 0x0000fd5d, 0x000016ae, + 0x0000fd5e, 0x000016b1, + 0x0000fd5f, 0x000016b4, + 0x0000fd60, 0x000016b7, + 0x0000fd61, 0x000016ba, + 0x0000fd62, 0x000016bd, + 0x0000fd63, 0x000016c0, + 0x0000fd64, 0x000016c3, + 0x0000fd65, 0x000016c6, + 0x0000fd66, 0x000016c9, + 0x0000fd67, 0x000016cc, + 0x0000fd68, 0x000016cf, + 0x0000fd69, 0x000016d2, + 0x0000fd6a, 0x000016d5, + 0x0000fd6b, 0x000016d8, + 0x0000fd6c, 0x000016db, + 0x0000fd6d, 0x000016de, + 0x0000fd6e, 0x000016e1, + 0x0000fd6f, 0x000016e4, + 0x0000fd70, 0x000016e7, + 0x0000fd71, 0x000016ea, + 0x0000fd72, 0x000016ed, + 0x0000fd73, 0x000016f0, + 0x0000fd74, 0x000016f3, + 0x0000fd75, 0x000016f6, + 0x0000fd76, 0x000016f9, + 0x0000fd77, 0x000016fc, + 0x0000fd78, 0x000016ff, + 0x0000fd79, 0x00001702, + 0x0000fd7a, 0x00001705, + 0x0000fd7b, 0x00001708, + 0x0000fd7c, 0x0000170b, + 0x0000fd7d, 0x0000170e, + 0x0000fd7e, 0x00001711, + 0x0000fd7f, 0x00001714, + 0x0000fd80, 0x00001717, + 0x0000fd81, 0x0000171a, + 0x0000fd82, 0x0000171d, + 0x0000fd83, 0x00001720, + 0x0000fd84, 0x00001723, + 0x0000fd85, 0x00001726, + 0x0000fd86, 0x00001729, + 0x0000fd87, 0x0000172c, + 0x0000fd88, 0x0000172f, + 0x0000fd89, 0x00001732, + 0x0000fd8a, 0x00001735, + 0x0000fd8b, 0x00001738, + 0x0000fd8c, 0x0000173b, + 0x0000fd8d, 0x0000173e, + 0x0000fd8e, 0x00001741, + 0x0000fd8f, 0x00001744, + 0x0000fd92, 0x00001747, + 0x0000fd93, 0x0000174a, + 0x0000fd94, 0x0000174d, + 0x0000fd95, 0x00001750, + 0x0000fd96, 0x00001753, + 0x0000fd97, 0x00001756, + 0x0000fd98, 0x00001759, + 0x0000fd99, 0x0000175c, + 0x0000fd9a, 0x0000175f, + 0x0000fd9b, 0x00001762, + 0x0000fd9c, 0x00001765, + 0x0000fd9d, 0x00001768, + 0x0000fd9e, 0x0000176b, + 0x0000fd9f, 0x0000176e, + 0x0000fda0, 0x00001771, + 0x0000fda1, 0x00001774, + 0x0000fda2, 0x00001777, + 0x0000fda3, 0x0000177a, + 0x0000fda4, 0x0000177d, + 0x0000fda5, 0x00001780, + 0x0000fda6, 0x00001783, + 0x0000fda7, 0x00001786, + 0x0000fda8, 0x00001789, + 0x0000fda9, 0x0000178c, + 0x0000fdaa, 0x0000178f, + 0x0000fdab, 0x00001792, + 0x0000fdac, 0x00001795, + 0x0000fdad, 0x00001798, + 0x0000fdae, 0x0000179b, + 0x0000fdaf, 0x0000179e, + 0x0000fdb0, 0x000017a1, + 0x0000fdb1, 0x000017a4, + 0x0000fdb2, 0x000017a7, + 0x0000fdb3, 0x000017aa, + 0x0000fdb4, 0x000017ad, + 0x0000fdb5, 0x000017b0, + 0x0000fdb6, 0x000017b3, + 0x0000fdb7, 0x000017b6, + 0x0000fdb8, 0x000017b9, + 0x0000fdb9, 0x000017bc, + 0x0000fdba, 0x000017bf, + 0x0000fdbb, 0x000017c2, + 0x0000fdbc, 0x000017c5, + 0x0000fdbd, 0x000017c8, + 0x0000fdbe, 0x000017cb, + 0x0000fdbf, 0x000017ce, + 0x0000fdc0, 0x000017d1, + 0x0000fdc1, 0x000017d4, + 0x0000fdc2, 0x000017d7, + 0x0000fdc3, 0x000017da, + 0x0000fdc4, 0x000017dd, + 0x0000fdc5, 0x000017e0, + 0x0000fdc6, 0x000017e3, + 0x0000fdc7, 0x000017e6, + 0x0000fdf0, 0x000017e9, + 0x0000fdf1, 0x000017ec, + 0x0000fdf2, 0x000017ef, + 0x0000fdf3, 0x000017f3, + 0x0000fdf4, 0x000017f7, + 0x0000fdf5, 0x000017fb, + 0x0000fdf6, 0x000017ff, + 0x0000fdf7, 0x00001803, + 0x0000fdf8, 0x00001807, + 0x0000fdf9, 0x0000180b, + 0x0000fdfa, 0x0000180e, + 0x0000fdfb, 0x00001820, + 0x0000fdfc, 0x00001828, + 0x0000fe30, 0x0000182c, + 0x0000fe31, 0x0000182e, + 0x0000fe32, 0x0000182f, + 0x0000fe33, 0x00001830, + 0x0000fe34, 0x00001831, + 0x0000fe35, 0x00001832, + 0x0000fe36, 0x00001833, + 0x0000fe37, 0x00001834, + 0x0000fe38, 0x00001835, + 0x0000fe39, 0x00001836, + 0x0000fe3a, 0x00001837, + 0x0000fe3b, 0x00001838, + 0x0000fe3c, 0x00001839, + 0x0000fe3d, 0x0000183a, + 0x0000fe3e, 0x0000183b, + 0x0000fe3f, 0x0000183c, + 0x0000fe40, 0x0000183d, + 0x0000fe41, 0x0000183e, + 0x0000fe42, 0x0000183f, + 0x0000fe43, 0x00001840, + 0x0000fe44, 0x00001841, + 0x0000fe49, 0x00001842, + 0x0000fe4a, 0x00001844, + 0x0000fe4b, 0x00001846, + 0x0000fe4c, 0x00001848, + 0x0000fe4d, 0x0000184a, + 0x0000fe4e, 0x0000184b, + 0x0000fe4f, 0x0000184c, + 0x0000fe50, 0x0000184d, + 0x0000fe51, 0x0000184e, + 0x0000fe52, 0x0000184f, + 0x0000fe54, 0x00001850, + 0x0000fe55, 0x00001851, + 0x0000fe56, 0x00001852, + 0x0000fe57, 0x00001853, + 0x0000fe58, 0x00001854, + 0x0000fe59, 0x00001855, + 0x0000fe5a, 0x00001856, + 0x0000fe5b, 0x00001857, + 0x0000fe5c, 0x00001858, + 0x0000fe5d, 0x00001859, + 0x0000fe5e, 0x0000185a, + 0x0000fe5f, 0x0000185b, + 0x0000fe60, 0x0000185c, + 0x0000fe61, 0x0000185d, + 0x0000fe62, 0x0000185e, + 0x0000fe63, 0x0000185f, + 0x0000fe64, 0x00001860, + 0x0000fe65, 0x00001861, + 0x0000fe66, 0x00001862, + 0x0000fe68, 0x00001863, + 0x0000fe69, 0x00001864, + 0x0000fe6a, 0x00001865, + 0x0000fe6b, 0x00001866, + 0x0000fe70, 0x00001867, + 0x0000fe71, 0x00001869, + 0x0000fe72, 0x0000186b, + 0x0000fe74, 0x0000186d, + 0x0000fe76, 0x0000186f, + 0x0000fe77, 0x00001871, + 0x0000fe78, 0x00001873, + 0x0000fe79, 0x00001875, + 0x0000fe7a, 0x00001877, + 0x0000fe7b, 0x00001879, + 0x0000fe7c, 0x0000187b, + 0x0000fe7d, 0x0000187d, + 0x0000fe7e, 0x0000187f, + 0x0000fe7f, 0x00001881, + 0x0000fe80, 0x00001883, + 0x0000fe81, 0x00001884, + 0x0000fe82, 0x00001886, + 0x0000fe83, 0x00001888, + 0x0000fe84, 0x0000188a, + 0x0000fe85, 0x0000188c, + 0x0000fe86, 0x0000188e, + 0x0000fe87, 0x00001890, + 0x0000fe88, 0x00001892, + 0x0000fe89, 0x00001894, + 0x0000fe8a, 0x00001896, + 0x0000fe8b, 0x00001898, + 0x0000fe8c, 0x0000189a, + 0x0000fe8d, 0x0000189c, + 0x0000fe8e, 0x0000189d, + 0x0000fe8f, 0x0000189e, + 0x0000fe90, 0x0000189f, + 0x0000fe91, 0x000018a0, + 0x0000fe92, 0x000018a1, + 0x0000fe93, 0x000018a2, + 0x0000fe94, 0x000018a3, + 0x0000fe95, 0x000018a4, + 0x0000fe96, 0x000018a5, + 0x0000fe97, 0x000018a6, + 0x0000fe98, 0x000018a7, + 0x0000fe99, 0x000018a8, + 0x0000fe9a, 0x000018a9, + 0x0000fe9b, 0x000018aa, + 0x0000fe9c, 0x000018ab, + 0x0000fe9d, 0x000018ac, + 0x0000fe9e, 0x000018ad, + 0x0000fe9f, 0x000018ae, + 0x0000fea0, 0x000018af, + 0x0000fea1, 0x000018b0, + 0x0000fea2, 0x000018b1, + 0x0000fea3, 0x000018b2, + 0x0000fea4, 0x000018b3, + 0x0000fea5, 0x000018b4, + 0x0000fea6, 0x000018b5, + 0x0000fea7, 0x000018b6, + 0x0000fea8, 0x000018b7, + 0x0000fea9, 0x000018b8, + 0x0000feaa, 0x000018b9, + 0x0000feab, 0x000018ba, + 0x0000feac, 0x000018bb, + 0x0000fead, 0x000018bc, + 0x0000feae, 0x000018bd, + 0x0000feaf, 0x000018be, + 0x0000feb0, 0x000018bf, + 0x0000feb1, 0x000018c0, + 0x0000feb2, 0x000018c1, + 0x0000feb3, 0x000018c2, + 0x0000feb4, 0x000018c3, + 0x0000feb5, 0x000018c4, + 0x0000feb6, 0x000018c5, + 0x0000feb7, 0x000018c6, + 0x0000feb8, 0x000018c7, + 0x0000feb9, 0x000018c8, + 0x0000feba, 0x000018c9, + 0x0000febb, 0x000018ca, + 0x0000febc, 0x000018cb, + 0x0000febd, 0x000018cc, + 0x0000febe, 0x000018cd, + 0x0000febf, 0x000018ce, + 0x0000fec0, 0x000018cf, + 0x0000fec1, 0x000018d0, + 0x0000fec2, 0x000018d1, + 0x0000fec3, 0x000018d2, + 0x0000fec4, 0x000018d3, + 0x0000fec5, 0x000018d4, + 0x0000fec6, 0x000018d5, + 0x0000fec7, 0x000018d6, + 0x0000fec8, 0x000018d7, + 0x0000fec9, 0x000018d8, + 0x0000feca, 0x000018d9, + 0x0000fecb, 0x000018da, + 0x0000fecc, 0x000018db, + 0x0000fecd, 0x000018dc, + 0x0000fece, 0x000018dd, + 0x0000fecf, 0x000018de, + 0x0000fed0, 0x000018df, + 0x0000fed1, 0x000018e0, + 0x0000fed2, 0x000018e1, + 0x0000fed3, 0x000018e2, + 0x0000fed4, 0x000018e3, + 0x0000fed5, 0x000018e4, + 0x0000fed6, 0x000018e5, + 0x0000fed7, 0x000018e6, + 0x0000fed8, 0x000018e7, + 0x0000fed9, 0x000018e8, + 0x0000feda, 0x000018e9, + 0x0000fedb, 0x000018ea, + 0x0000fedc, 0x000018eb, + 0x0000fedd, 0x000018ec, + 0x0000fede, 0x000018ed, + 0x0000fedf, 0x000018ee, + 0x0000fee0, 0x000018ef, + 0x0000fee1, 0x000018f0, + 0x0000fee2, 0x000018f1, + 0x0000fee3, 0x000018f2, + 0x0000fee4, 0x000018f3, + 0x0000fee5, 0x000018f4, + 0x0000fee6, 0x000018f5, + 0x0000fee7, 0x000018f6, + 0x0000fee8, 0x000018f7, + 0x0000fee9, 0x000018f8, + 0x0000feea, 0x000018f9, + 0x0000feeb, 0x000018fa, + 0x0000feec, 0x000018fb, + 0x0000feed, 0x000018fc, + 0x0000feee, 0x000018fd, + 0x0000feef, 0x000018fe, + 0x0000fef0, 0x000018ff, + 0x0000fef1, 0x00001900, + 0x0000fef2, 0x00001901, + 0x0000fef3, 0x00001902, + 0x0000fef4, 0x00001903, + 0x0000fef5, 0x00001904, + 0x0000fef6, 0x00001907, + 0x0000fef7, 0x0000190a, + 0x0000fef8, 0x0000190d, + 0x0000fef9, 0x00001910, + 0x0000fefa, 0x00001913, + 0x0000fefb, 0x00001916, + 0x0000fefc, 0x00001918, + 0x0000ff01, 0x0000191a, + 0x0000ff02, 0x0000191b, + 0x0000ff03, 0x0000191c, + 0x0000ff04, 0x0000191d, + 0x0000ff05, 0x0000191e, + 0x0000ff06, 0x0000191f, + 0x0000ff07, 0x00001920, + 0x0000ff08, 0x00001921, + 0x0000ff09, 0x00001922, + 0x0000ff0a, 0x00001923, + 0x0000ff0b, 0x00001924, + 0x0000ff0c, 0x00001925, + 0x0000ff0d, 0x00001926, + 0x0000ff0e, 0x00001927, + 0x0000ff0f, 0x00001928, + 0x0000ff10, 0x00001929, + 0x0000ff11, 0x0000192a, + 0x0000ff12, 0x0000192b, + 0x0000ff13, 0x0000192c, + 0x0000ff14, 0x0000192d, + 0x0000ff15, 0x0000192e, + 0x0000ff16, 0x0000192f, + 0x0000ff17, 0x00001930, + 0x0000ff18, 0x00001931, + 0x0000ff19, 0x00001932, + 0x0000ff1a, 0x00001933, + 0x0000ff1b, 0x00001934, + 0x0000ff1c, 0x00001935, + 0x0000ff1d, 0x00001936, + 0x0000ff1e, 0x00001937, + 0x0000ff1f, 0x00001938, + 0x0000ff20, 0x00001939, + 0x0000ff21, 0x0000193a, + 0x0000ff22, 0x0000193b, + 0x0000ff23, 0x0000193c, + 0x0000ff24, 0x0000193d, + 0x0000ff25, 0x0000193e, + 0x0000ff26, 0x0000193f, + 0x0000ff27, 0x00001940, + 0x0000ff28, 0x00001941, + 0x0000ff29, 0x00001942, + 0x0000ff2a, 0x00001943, + 0x0000ff2b, 0x00001944, + 0x0000ff2c, 0x00001945, + 0x0000ff2d, 0x00001946, + 0x0000ff2e, 0x00001947, + 0x0000ff2f, 0x00001948, + 0x0000ff30, 0x00001949, + 0x0000ff31, 0x0000194a, + 0x0000ff32, 0x0000194b, + 0x0000ff33, 0x0000194c, + 0x0000ff34, 0x0000194d, + 0x0000ff35, 0x0000194e, + 0x0000ff36, 0x0000194f, + 0x0000ff37, 0x00001950, + 0x0000ff38, 0x00001951, + 0x0000ff39, 0x00001952, + 0x0000ff3a, 0x00001953, + 0x0000ff3b, 0x00001954, + 0x0000ff3c, 0x00001955, + 0x0000ff3d, 0x00001956, + 0x0000ff3e, 0x00001957, + 0x0000ff3f, 0x00001958, + 0x0000ff40, 0x00001959, + 0x0000ff41, 0x0000195a, + 0x0000ff42, 0x0000195b, + 0x0000ff43, 0x0000195c, + 0x0000ff44, 0x0000195d, + 0x0000ff45, 0x0000195e, + 0x0000ff46, 0x0000195f, + 0x0000ff47, 0x00001960, + 0x0000ff48, 0x00001961, + 0x0000ff49, 0x00001962, + 0x0000ff4a, 0x00001963, + 0x0000ff4b, 0x00001964, + 0x0000ff4c, 0x00001965, + 0x0000ff4d, 0x00001966, + 0x0000ff4e, 0x00001967, + 0x0000ff4f, 0x00001968, + 0x0000ff50, 0x00001969, + 0x0000ff51, 0x0000196a, + 0x0000ff52, 0x0000196b, + 0x0000ff53, 0x0000196c, + 0x0000ff54, 0x0000196d, + 0x0000ff55, 0x0000196e, + 0x0000ff56, 0x0000196f, + 0x0000ff57, 0x00001970, + 0x0000ff58, 0x00001971, + 0x0000ff59, 0x00001972, + 0x0000ff5a, 0x00001973, + 0x0000ff5b, 0x00001974, + 0x0000ff5c, 0x00001975, + 0x0000ff5d, 0x00001976, + 0x0000ff5e, 0x00001977, + 0x0000ff5f, 0x00001978, + 0x0000ff60, 0x00001979, + 0x0000ff61, 0x0000197a, + 0x0000ff62, 0x0000197b, + 0x0000ff63, 0x0000197c, + 0x0000ff64, 0x0000197d, + 0x0000ff65, 0x0000197e, + 0x0000ff66, 0x0000197f, + 0x0000ff67, 0x00001980, + 0x0000ff68, 0x00001981, + 0x0000ff69, 0x00001982, + 0x0000ff6a, 0x00001983, + 0x0000ff6b, 0x00001984, + 0x0000ff6c, 0x00001985, + 0x0000ff6d, 0x00001986, + 0x0000ff6e, 0x00001987, + 0x0000ff6f, 0x00001988, + 0x0000ff70, 0x00001989, + 0x0000ff71, 0x0000198a, + 0x0000ff72, 0x0000198b, + 0x0000ff73, 0x0000198c, + 0x0000ff74, 0x0000198d, + 0x0000ff75, 0x0000198e, + 0x0000ff76, 0x0000198f, + 0x0000ff77, 0x00001990, + 0x0000ff78, 0x00001991, + 0x0000ff79, 0x00001992, + 0x0000ff7a, 0x00001993, + 0x0000ff7b, 0x00001994, + 0x0000ff7c, 0x00001995, + 0x0000ff7d, 0x00001996, + 0x0000ff7e, 0x00001997, + 0x0000ff7f, 0x00001998, + 0x0000ff80, 0x00001999, + 0x0000ff81, 0x0000199a, + 0x0000ff82, 0x0000199b, + 0x0000ff83, 0x0000199c, + 0x0000ff84, 0x0000199d, + 0x0000ff85, 0x0000199e, + 0x0000ff86, 0x0000199f, + 0x0000ff87, 0x000019a0, + 0x0000ff88, 0x000019a1, + 0x0000ff89, 0x000019a2, + 0x0000ff8a, 0x000019a3, + 0x0000ff8b, 0x000019a4, + 0x0000ff8c, 0x000019a5, + 0x0000ff8d, 0x000019a6, + 0x0000ff8e, 0x000019a7, + 0x0000ff8f, 0x000019a8, + 0x0000ff90, 0x000019a9, + 0x0000ff91, 0x000019aa, + 0x0000ff92, 0x000019ab, + 0x0000ff93, 0x000019ac, + 0x0000ff94, 0x000019ad, + 0x0000ff95, 0x000019ae, + 0x0000ff96, 0x000019af, + 0x0000ff97, 0x000019b0, + 0x0000ff98, 0x000019b1, + 0x0000ff99, 0x000019b2, + 0x0000ff9a, 0x000019b3, + 0x0000ff9b, 0x000019b4, + 0x0000ff9c, 0x000019b5, + 0x0000ff9d, 0x000019b6, + 0x0000ff9e, 0x000019b7, + 0x0000ff9f, 0x000019b8, + 0x0000ffa0, 0x000019b9, + 0x0000ffa1, 0x000019ba, + 0x0000ffa2, 0x000019bb, + 0x0000ffa3, 0x000019bc, + 0x0000ffa4, 0x000019bd, + 0x0000ffa5, 0x000019be, + 0x0000ffa6, 0x000019bf, + 0x0000ffa7, 0x000019c0, + 0x0000ffa8, 0x000019c1, + 0x0000ffa9, 0x000019c2, + 0x0000ffaa, 0x000019c3, + 0x0000ffab, 0x000019c4, + 0x0000ffac, 0x000019c5, + 0x0000ffad, 0x000019c6, + 0x0000ffae, 0x000019c7, + 0x0000ffaf, 0x000019c8, + 0x0000ffb0, 0x000019c9, + 0x0000ffb1, 0x000019ca, + 0x0000ffb2, 0x000019cb, + 0x0000ffb3, 0x000019cc, + 0x0000ffb4, 0x000019cd, + 0x0000ffb5, 0x000019ce, + 0x0000ffb6, 0x000019cf, + 0x0000ffb7, 0x000019d0, + 0x0000ffb8, 0x000019d1, + 0x0000ffb9, 0x000019d2, + 0x0000ffba, 0x000019d3, + 0x0000ffbb, 0x000019d4, + 0x0000ffbc, 0x000019d5, + 0x0000ffbd, 0x000019d6, + 0x0000ffbe, 0x000019d7, + 0x0000ffc2, 0x000019d8, + 0x0000ffc3, 0x000019d9, + 0x0000ffc4, 0x000019da, + 0x0000ffc5, 0x000019db, + 0x0000ffc6, 0x000019dc, + 0x0000ffc7, 0x000019dd, + 0x0000ffca, 0x000019de, + 0x0000ffcb, 0x000019df, + 0x0000ffcc, 0x000019e0, + 0x0000ffcd, 0x000019e1, + 0x0000ffce, 0x000019e2, + 0x0000ffcf, 0x000019e3, + 0x0000ffd2, 0x000019e4, + 0x0000ffd3, 0x000019e5, + 0x0000ffd4, 0x000019e6, + 0x0000ffd5, 0x000019e7, + 0x0000ffd6, 0x000019e8, + 0x0000ffd7, 0x000019e9, + 0x0000ffda, 0x000019ea, + 0x0000ffdb, 0x000019eb, + 0x0000ffdc, 0x000019ec, + 0x0000ffe0, 0x000019ed, + 0x0000ffe1, 0x000019ee, + 0x0000ffe2, 0x000019ef, + 0x0000ffe3, 0x000019f0, + 0x0000ffe4, 0x000019f2, + 0x0000ffe5, 0x000019f3, + 0x0000ffe6, 0x000019f4, + 0x0000ffe8, 0x000019f5, + 0x0000ffe9, 0x000019f6, + 0x0000ffea, 0x000019f7, + 0x0000ffeb, 0x000019f8, + 0x0000ffec, 0x000019f9, + 0x0000ffed, 0x000019fa, + 0x0000ffee, 0x000019fb, + 0x0001d15e, 0x000019fc, + 0x0001d15f, 0x000019fe, + 0x0001d160, 0x00001a00, + 0x0001d161, 0x00001a03, + 0x0001d162, 0x00001a06, + 0x0001d163, 0x00001a09, + 0x0001d164, 0x00001a0c, + 0x0001d1bb, 0x00001a0f, + 0x0001d1bc, 0x00001a11, + 0x0001d1bd, 0x00001a13, + 0x0001d1be, 0x00001a16, + 0x0001d1bf, 0x00001a19, + 0x0001d1c0, 0x00001a1c, + 0x0001d400, 0x00001a1f, + 0x0001d401, 0x00001a20, + 0x0001d402, 0x00001a21, + 0x0001d403, 0x00001a22, + 0x0001d404, 0x00001a23, + 0x0001d405, 0x00001a24, + 0x0001d406, 0x00001a25, + 0x0001d407, 0x00001a26, + 0x0001d408, 0x00001a27, + 0x0001d409, 0x00001a28, + 0x0001d40a, 0x00001a29, + 0x0001d40b, 0x00001a2a, + 0x0001d40c, 0x00001a2b, + 0x0001d40d, 0x00001a2c, + 0x0001d40e, 0x00001a2d, + 0x0001d40f, 0x00001a2e, + 0x0001d410, 0x00001a2f, + 0x0001d411, 0x00001a30, + 0x0001d412, 0x00001a31, + 0x0001d413, 0x00001a32, + 0x0001d414, 0x00001a33, + 0x0001d415, 0x00001a34, + 0x0001d416, 0x00001a35, + 0x0001d417, 0x00001a36, + 0x0001d418, 0x00001a37, + 0x0001d419, 0x00001a38, + 0x0001d41a, 0x00001a39, + 0x0001d41b, 0x00001a3a, + 0x0001d41c, 0x00001a3b, + 0x0001d41d, 0x00001a3c, + 0x0001d41e, 0x00001a3d, + 0x0001d41f, 0x00001a3e, + 0x0001d420, 0x00001a3f, + 0x0001d421, 0x00001a40, + 0x0001d422, 0x00001a41, + 0x0001d423, 0x00001a42, + 0x0001d424, 0x00001a43, + 0x0001d425, 0x00001a44, + 0x0001d426, 0x00001a45, + 0x0001d427, 0x00001a46, + 0x0001d428, 0x00001a47, + 0x0001d429, 0x00001a48, + 0x0001d42a, 0x00001a49, + 0x0001d42b, 0x00001a4a, + 0x0001d42c, 0x00001a4b, + 0x0001d42d, 0x00001a4c, + 0x0001d42e, 0x00001a4d, + 0x0001d42f, 0x00001a4e, + 0x0001d430, 0x00001a4f, + 0x0001d431, 0x00001a50, + 0x0001d432, 0x00001a51, + 0x0001d433, 0x00001a52, + 0x0001d434, 0x00001a53, + 0x0001d435, 0x00001a54, + 0x0001d436, 0x00001a55, + 0x0001d437, 0x00001a56, + 0x0001d438, 0x00001a57, + 0x0001d439, 0x00001a58, + 0x0001d43a, 0x00001a59, + 0x0001d43b, 0x00001a5a, + 0x0001d43c, 0x00001a5b, + 0x0001d43d, 0x00001a5c, + 0x0001d43e, 0x00001a5d, + 0x0001d43f, 0x00001a5e, + 0x0001d440, 0x00001a5f, + 0x0001d441, 0x00001a60, + 0x0001d442, 0x00001a61, + 0x0001d443, 0x00001a62, + 0x0001d444, 0x00001a63, + 0x0001d445, 0x00001a64, + 0x0001d446, 0x00001a65, + 0x0001d447, 0x00001a66, + 0x0001d448, 0x00001a67, + 0x0001d449, 0x00001a68, + 0x0001d44a, 0x00001a69, + 0x0001d44b, 0x00001a6a, + 0x0001d44c, 0x00001a6b, + 0x0001d44d, 0x00001a6c, + 0x0001d44e, 0x00001a6d, + 0x0001d44f, 0x00001a6e, + 0x0001d450, 0x00001a6f, + 0x0001d451, 0x00001a70, + 0x0001d452, 0x00001a71, + 0x0001d453, 0x00001a72, + 0x0001d454, 0x00001a73, + 0x0001d456, 0x00001a74, + 0x0001d457, 0x00001a75, + 0x0001d458, 0x00001a76, + 0x0001d459, 0x00001a77, + 0x0001d45a, 0x00001a78, + 0x0001d45b, 0x00001a79, + 0x0001d45c, 0x00001a7a, + 0x0001d45d, 0x00001a7b, + 0x0001d45e, 0x00001a7c, + 0x0001d45f, 0x00001a7d, + 0x0001d460, 0x00001a7e, + 0x0001d461, 0x00001a7f, + 0x0001d462, 0x00001a80, + 0x0001d463, 0x00001a81, + 0x0001d464, 0x00001a82, + 0x0001d465, 0x00001a83, + 0x0001d466, 0x00001a84, + 0x0001d467, 0x00001a85, + 0x0001d468, 0x00001a86, + 0x0001d469, 0x00001a87, + 0x0001d46a, 0x00001a88, + 0x0001d46b, 0x00001a89, + 0x0001d46c, 0x00001a8a, + 0x0001d46d, 0x00001a8b, + 0x0001d46e, 0x00001a8c, + 0x0001d46f, 0x00001a8d, + 0x0001d470, 0x00001a8e, + 0x0001d471, 0x00001a8f, + 0x0001d472, 0x00001a90, + 0x0001d473, 0x00001a91, + 0x0001d474, 0x00001a92, + 0x0001d475, 0x00001a93, + 0x0001d476, 0x00001a94, + 0x0001d477, 0x00001a95, + 0x0001d478, 0x00001a96, + 0x0001d479, 0x00001a97, + 0x0001d47a, 0x00001a98, + 0x0001d47b, 0x00001a99, + 0x0001d47c, 0x00001a9a, + 0x0001d47d, 0x00001a9b, + 0x0001d47e, 0x00001a9c, + 0x0001d47f, 0x00001a9d, + 0x0001d480, 0x00001a9e, + 0x0001d481, 0x00001a9f, + 0x0001d482, 0x00001aa0, + 0x0001d483, 0x00001aa1, + 0x0001d484, 0x00001aa2, + 0x0001d485, 0x00001aa3, + 0x0001d486, 0x00001aa4, + 0x0001d487, 0x00001aa5, + 0x0001d488, 0x00001aa6, + 0x0001d489, 0x00001aa7, + 0x0001d48a, 0x00001aa8, + 0x0001d48b, 0x00001aa9, + 0x0001d48c, 0x00001aaa, + 0x0001d48d, 0x00001aab, + 0x0001d48e, 0x00001aac, + 0x0001d48f, 0x00001aad, + 0x0001d490, 0x00001aae, + 0x0001d491, 0x00001aaf, + 0x0001d492, 0x00001ab0, + 0x0001d493, 0x00001ab1, + 0x0001d494, 0x00001ab2, + 0x0001d495, 0x00001ab3, + 0x0001d496, 0x00001ab4, + 0x0001d497, 0x00001ab5, + 0x0001d498, 0x00001ab6, + 0x0001d499, 0x00001ab7, + 0x0001d49a, 0x00001ab8, + 0x0001d49b, 0x00001ab9, + 0x0001d49c, 0x00001aba, + 0x0001d49e, 0x00001abb, + 0x0001d49f, 0x00001abc, + 0x0001d4a2, 0x00001abd, + 0x0001d4a5, 0x00001abe, + 0x0001d4a6, 0x00001abf, + 0x0001d4a9, 0x00001ac0, + 0x0001d4aa, 0x00001ac1, + 0x0001d4ab, 0x00001ac2, + 0x0001d4ac, 0x00001ac3, + 0x0001d4ae, 0x00001ac4, + 0x0001d4af, 0x00001ac5, + 0x0001d4b0, 0x00001ac6, + 0x0001d4b1, 0x00001ac7, + 0x0001d4b2, 0x00001ac8, + 0x0001d4b3, 0x00001ac9, + 0x0001d4b4, 0x00001aca, + 0x0001d4b5, 0x00001acb, + 0x0001d4b6, 0x00001acc, + 0x0001d4b7, 0x00001acd, + 0x0001d4b8, 0x00001ace, + 0x0001d4b9, 0x00001acf, + 0x0001d4bb, 0x00001ad0, + 0x0001d4bd, 0x00001ad1, + 0x0001d4be, 0x00001ad2, + 0x0001d4bf, 0x00001ad3, + 0x0001d4c0, 0x00001ad4, + 0x0001d4c2, 0x00001ad5, + 0x0001d4c3, 0x00001ad6, + 0x0001d4c5, 0x00001ad7, + 0x0001d4c6, 0x00001ad8, + 0x0001d4c7, 0x00001ad9, + 0x0001d4c8, 0x00001ada, + 0x0001d4c9, 0x00001adb, + 0x0001d4ca, 0x00001adc, + 0x0001d4cb, 0x00001add, + 0x0001d4cc, 0x00001ade, + 0x0001d4cd, 0x00001adf, + 0x0001d4ce, 0x00001ae0, + 0x0001d4cf, 0x00001ae1, + 0x0001d4d0, 0x00001ae2, + 0x0001d4d1, 0x00001ae3, + 0x0001d4d2, 0x00001ae4, + 0x0001d4d3, 0x00001ae5, + 0x0001d4d4, 0x00001ae6, + 0x0001d4d5, 0x00001ae7, + 0x0001d4d6, 0x00001ae8, + 0x0001d4d7, 0x00001ae9, + 0x0001d4d8, 0x00001aea, + 0x0001d4d9, 0x00001aeb, + 0x0001d4da, 0x00001aec, + 0x0001d4db, 0x00001aed, + 0x0001d4dc, 0x00001aee, + 0x0001d4dd, 0x00001aef, + 0x0001d4de, 0x00001af0, + 0x0001d4df, 0x00001af1, + 0x0001d4e0, 0x00001af2, + 0x0001d4e1, 0x00001af3, + 0x0001d4e2, 0x00001af4, + 0x0001d4e3, 0x00001af5, + 0x0001d4e4, 0x00001af6, + 0x0001d4e5, 0x00001af7, + 0x0001d4e6, 0x00001af8, + 0x0001d4e7, 0x00001af9, + 0x0001d4e8, 0x00001afa, + 0x0001d4e9, 0x00001afb, + 0x0001d4ea, 0x00001afc, + 0x0001d4eb, 0x00001afd, + 0x0001d4ec, 0x00001afe, + 0x0001d4ed, 0x00001aff, + 0x0001d4ee, 0x00001b00, + 0x0001d4ef, 0x00001b01, + 0x0001d4f0, 0x00001b02, + 0x0001d4f1, 0x00001b03, + 0x0001d4f2, 0x00001b04, + 0x0001d4f3, 0x00001b05, + 0x0001d4f4, 0x00001b06, + 0x0001d4f5, 0x00001b07, + 0x0001d4f6, 0x00001b08, + 0x0001d4f7, 0x00001b09, + 0x0001d4f8, 0x00001b0a, + 0x0001d4f9, 0x00001b0b, + 0x0001d4fa, 0x00001b0c, + 0x0001d4fb, 0x00001b0d, + 0x0001d4fc, 0x00001b0e, + 0x0001d4fd, 0x00001b0f, + 0x0001d4fe, 0x00001b10, + 0x0001d4ff, 0x00001b11, + 0x0001d500, 0x00001b12, + 0x0001d501, 0x00001b13, + 0x0001d502, 0x00001b14, + 0x0001d503, 0x00001b15, + 0x0001d504, 0x00001b16, + 0x0001d505, 0x00001b17, + 0x0001d507, 0x00001b18, + 0x0001d508, 0x00001b19, + 0x0001d509, 0x00001b1a, + 0x0001d50a, 0x00001b1b, + 0x0001d50d, 0x00001b1c, + 0x0001d50e, 0x00001b1d, + 0x0001d50f, 0x00001b1e, + 0x0001d510, 0x00001b1f, + 0x0001d511, 0x00001b20, + 0x0001d512, 0x00001b21, + 0x0001d513, 0x00001b22, + 0x0001d514, 0x00001b23, + 0x0001d516, 0x00001b24, + 0x0001d517, 0x00001b25, + 0x0001d518, 0x00001b26, + 0x0001d519, 0x00001b27, + 0x0001d51a, 0x00001b28, + 0x0001d51b, 0x00001b29, + 0x0001d51c, 0x00001b2a, + 0x0001d51e, 0x00001b2b, + 0x0001d51f, 0x00001b2c, + 0x0001d520, 0x00001b2d, + 0x0001d521, 0x00001b2e, + 0x0001d522, 0x00001b2f, + 0x0001d523, 0x00001b30, + 0x0001d524, 0x00001b31, + 0x0001d525, 0x00001b32, + 0x0001d526, 0x00001b33, + 0x0001d527, 0x00001b34, + 0x0001d528, 0x00001b35, + 0x0001d529, 0x00001b36, + 0x0001d52a, 0x00001b37, + 0x0001d52b, 0x00001b38, + 0x0001d52c, 0x00001b39, + 0x0001d52d, 0x00001b3a, + 0x0001d52e, 0x00001b3b, + 0x0001d52f, 0x00001b3c, + 0x0001d530, 0x00001b3d, + 0x0001d531, 0x00001b3e, + 0x0001d532, 0x00001b3f, + 0x0001d533, 0x00001b40, + 0x0001d534, 0x00001b41, + 0x0001d535, 0x00001b42, + 0x0001d536, 0x00001b43, + 0x0001d537, 0x00001b44, + 0x0001d538, 0x00001b45, + 0x0001d539, 0x00001b46, + 0x0001d53b, 0x00001b47, + 0x0001d53c, 0x00001b48, + 0x0001d53d, 0x00001b49, + 0x0001d53e, 0x00001b4a, + 0x0001d540, 0x00001b4b, + 0x0001d541, 0x00001b4c, + 0x0001d542, 0x00001b4d, + 0x0001d543, 0x00001b4e, + 0x0001d544, 0x00001b4f, + 0x0001d546, 0x00001b50, + 0x0001d54a, 0x00001b51, + 0x0001d54b, 0x00001b52, + 0x0001d54c, 0x00001b53, + 0x0001d54d, 0x00001b54, + 0x0001d54e, 0x00001b55, + 0x0001d54f, 0x00001b56, + 0x0001d550, 0x00001b57, + 0x0001d552, 0x00001b58, + 0x0001d553, 0x00001b59, + 0x0001d554, 0x00001b5a, + 0x0001d555, 0x00001b5b, + 0x0001d556, 0x00001b5c, + 0x0001d557, 0x00001b5d, + 0x0001d558, 0x00001b5e, + 0x0001d559, 0x00001b5f, + 0x0001d55a, 0x00001b60, + 0x0001d55b, 0x00001b61, + 0x0001d55c, 0x00001b62, + 0x0001d55d, 0x00001b63, + 0x0001d55e, 0x00001b64, + 0x0001d55f, 0x00001b65, + 0x0001d560, 0x00001b66, + 0x0001d561, 0x00001b67, + 0x0001d562, 0x00001b68, + 0x0001d563, 0x00001b69, + 0x0001d564, 0x00001b6a, + 0x0001d565, 0x00001b6b, + 0x0001d566, 0x00001b6c, + 0x0001d567, 0x00001b6d, + 0x0001d568, 0x00001b6e, + 0x0001d569, 0x00001b6f, + 0x0001d56a, 0x00001b70, + 0x0001d56b, 0x00001b71, + 0x0001d56c, 0x00001b72, + 0x0001d56d, 0x00001b73, + 0x0001d56e, 0x00001b74, + 0x0001d56f, 0x00001b75, + 0x0001d570, 0x00001b76, + 0x0001d571, 0x00001b77, + 0x0001d572, 0x00001b78, + 0x0001d573, 0x00001b79, + 0x0001d574, 0x00001b7a, + 0x0001d575, 0x00001b7b, + 0x0001d576, 0x00001b7c, + 0x0001d577, 0x00001b7d, + 0x0001d578, 0x00001b7e, + 0x0001d579, 0x00001b7f, + 0x0001d57a, 0x00001b80, + 0x0001d57b, 0x00001b81, + 0x0001d57c, 0x00001b82, + 0x0001d57d, 0x00001b83, + 0x0001d57e, 0x00001b84, + 0x0001d57f, 0x00001b85, + 0x0001d580, 0x00001b86, + 0x0001d581, 0x00001b87, + 0x0001d582, 0x00001b88, + 0x0001d583, 0x00001b89, + 0x0001d584, 0x00001b8a, + 0x0001d585, 0x00001b8b, + 0x0001d586, 0x00001b8c, + 0x0001d587, 0x00001b8d, + 0x0001d588, 0x00001b8e, + 0x0001d589, 0x00001b8f, + 0x0001d58a, 0x00001b90, + 0x0001d58b, 0x00001b91, + 0x0001d58c, 0x00001b92, + 0x0001d58d, 0x00001b93, + 0x0001d58e, 0x00001b94, + 0x0001d58f, 0x00001b95, + 0x0001d590, 0x00001b96, + 0x0001d591, 0x00001b97, + 0x0001d592, 0x00001b98, + 0x0001d593, 0x00001b99, + 0x0001d594, 0x00001b9a, + 0x0001d595, 0x00001b9b, + 0x0001d596, 0x00001b9c, + 0x0001d597, 0x00001b9d, + 0x0001d598, 0x00001b9e, + 0x0001d599, 0x00001b9f, + 0x0001d59a, 0x00001ba0, + 0x0001d59b, 0x00001ba1, + 0x0001d59c, 0x00001ba2, + 0x0001d59d, 0x00001ba3, + 0x0001d59e, 0x00001ba4, + 0x0001d59f, 0x00001ba5, + 0x0001d5a0, 0x00001ba6, + 0x0001d5a1, 0x00001ba7, + 0x0001d5a2, 0x00001ba8, + 0x0001d5a3, 0x00001ba9, + 0x0001d5a4, 0x00001baa, + 0x0001d5a5, 0x00001bab, + 0x0001d5a6, 0x00001bac, + 0x0001d5a7, 0x00001bad, + 0x0001d5a8, 0x00001bae, + 0x0001d5a9, 0x00001baf, + 0x0001d5aa, 0x00001bb0, + 0x0001d5ab, 0x00001bb1, + 0x0001d5ac, 0x00001bb2, + 0x0001d5ad, 0x00001bb3, + 0x0001d5ae, 0x00001bb4, + 0x0001d5af, 0x00001bb5, + 0x0001d5b0, 0x00001bb6, + 0x0001d5b1, 0x00001bb7, + 0x0001d5b2, 0x00001bb8, + 0x0001d5b3, 0x00001bb9, + 0x0001d5b4, 0x00001bba, + 0x0001d5b5, 0x00001bbb, + 0x0001d5b6, 0x00001bbc, + 0x0001d5b7, 0x00001bbd, + 0x0001d5b8, 0x00001bbe, + 0x0001d5b9, 0x00001bbf, + 0x0001d5ba, 0x00001bc0, + 0x0001d5bb, 0x00001bc1, + 0x0001d5bc, 0x00001bc2, + 0x0001d5bd, 0x00001bc3, + 0x0001d5be, 0x00001bc4, + 0x0001d5bf, 0x00001bc5, + 0x0001d5c0, 0x00001bc6, + 0x0001d5c1, 0x00001bc7, + 0x0001d5c2, 0x00001bc8, + 0x0001d5c3, 0x00001bc9, + 0x0001d5c4, 0x00001bca, + 0x0001d5c5, 0x00001bcb, + 0x0001d5c6, 0x00001bcc, + 0x0001d5c7, 0x00001bcd, + 0x0001d5c8, 0x00001bce, + 0x0001d5c9, 0x00001bcf, + 0x0001d5ca, 0x00001bd0, + 0x0001d5cb, 0x00001bd1, + 0x0001d5cc, 0x00001bd2, + 0x0001d5cd, 0x00001bd3, + 0x0001d5ce, 0x00001bd4, + 0x0001d5cf, 0x00001bd5, + 0x0001d5d0, 0x00001bd6, + 0x0001d5d1, 0x00001bd7, + 0x0001d5d2, 0x00001bd8, + 0x0001d5d3, 0x00001bd9, + 0x0001d5d4, 0x00001bda, + 0x0001d5d5, 0x00001bdb, + 0x0001d5d6, 0x00001bdc, + 0x0001d5d7, 0x00001bdd, + 0x0001d5d8, 0x00001bde, + 0x0001d5d9, 0x00001bdf, + 0x0001d5da, 0x00001be0, + 0x0001d5db, 0x00001be1, + 0x0001d5dc, 0x00001be2, + 0x0001d5dd, 0x00001be3, + 0x0001d5de, 0x00001be4, + 0x0001d5df, 0x00001be5, + 0x0001d5e0, 0x00001be6, + 0x0001d5e1, 0x00001be7, + 0x0001d5e2, 0x00001be8, + 0x0001d5e3, 0x00001be9, + 0x0001d5e4, 0x00001bea, + 0x0001d5e5, 0x00001beb, + 0x0001d5e6, 0x00001bec, + 0x0001d5e7, 0x00001bed, + 0x0001d5e8, 0x00001bee, + 0x0001d5e9, 0x00001bef, + 0x0001d5ea, 0x00001bf0, + 0x0001d5eb, 0x00001bf1, + 0x0001d5ec, 0x00001bf2, + 0x0001d5ed, 0x00001bf3, + 0x0001d5ee, 0x00001bf4, + 0x0001d5ef, 0x00001bf5, + 0x0001d5f0, 0x00001bf6, + 0x0001d5f1, 0x00001bf7, + 0x0001d5f2, 0x00001bf8, + 0x0001d5f3, 0x00001bf9, + 0x0001d5f4, 0x00001bfa, + 0x0001d5f5, 0x00001bfb, + 0x0001d5f6, 0x00001bfc, + 0x0001d5f7, 0x00001bfd, + 0x0001d5f8, 0x00001bfe, + 0x0001d5f9, 0x00001bff, + 0x0001d5fa, 0x00001c00, + 0x0001d5fb, 0x00001c01, + 0x0001d5fc, 0x00001c02, + 0x0001d5fd, 0x00001c03, + 0x0001d5fe, 0x00001c04, + 0x0001d5ff, 0x00001c05, + 0x0001d600, 0x00001c06, + 0x0001d601, 0x00001c07, + 0x0001d602, 0x00001c08, + 0x0001d603, 0x00001c09, + 0x0001d604, 0x00001c0a, + 0x0001d605, 0x00001c0b, + 0x0001d606, 0x00001c0c, + 0x0001d607, 0x00001c0d, + 0x0001d608, 0x00001c0e, + 0x0001d609, 0x00001c0f, + 0x0001d60a, 0x00001c10, + 0x0001d60b, 0x00001c11, + 0x0001d60c, 0x00001c12, + 0x0001d60d, 0x00001c13, + 0x0001d60e, 0x00001c14, + 0x0001d60f, 0x00001c15, + 0x0001d610, 0x00001c16, + 0x0001d611, 0x00001c17, + 0x0001d612, 0x00001c18, + 0x0001d613, 0x00001c19, + 0x0001d614, 0x00001c1a, + 0x0001d615, 0x00001c1b, + 0x0001d616, 0x00001c1c, + 0x0001d617, 0x00001c1d, + 0x0001d618, 0x00001c1e, + 0x0001d619, 0x00001c1f, + 0x0001d61a, 0x00001c20, + 0x0001d61b, 0x00001c21, + 0x0001d61c, 0x00001c22, + 0x0001d61d, 0x00001c23, + 0x0001d61e, 0x00001c24, + 0x0001d61f, 0x00001c25, + 0x0001d620, 0x00001c26, + 0x0001d621, 0x00001c27, + 0x0001d622, 0x00001c28, + 0x0001d623, 0x00001c29, + 0x0001d624, 0x00001c2a, + 0x0001d625, 0x00001c2b, + 0x0001d626, 0x00001c2c, + 0x0001d627, 0x00001c2d, + 0x0001d628, 0x00001c2e, + 0x0001d629, 0x00001c2f, + 0x0001d62a, 0x00001c30, + 0x0001d62b, 0x00001c31, + 0x0001d62c, 0x00001c32, + 0x0001d62d, 0x00001c33, + 0x0001d62e, 0x00001c34, + 0x0001d62f, 0x00001c35, + 0x0001d630, 0x00001c36, + 0x0001d631, 0x00001c37, + 0x0001d632, 0x00001c38, + 0x0001d633, 0x00001c39, + 0x0001d634, 0x00001c3a, + 0x0001d635, 0x00001c3b, + 0x0001d636, 0x00001c3c, + 0x0001d637, 0x00001c3d, + 0x0001d638, 0x00001c3e, + 0x0001d639, 0x00001c3f, + 0x0001d63a, 0x00001c40, + 0x0001d63b, 0x00001c41, + 0x0001d63c, 0x00001c42, + 0x0001d63d, 0x00001c43, + 0x0001d63e, 0x00001c44, + 0x0001d63f, 0x00001c45, + 0x0001d640, 0x00001c46, + 0x0001d641, 0x00001c47, + 0x0001d642, 0x00001c48, + 0x0001d643, 0x00001c49, + 0x0001d644, 0x00001c4a, + 0x0001d645, 0x00001c4b, + 0x0001d646, 0x00001c4c, + 0x0001d647, 0x00001c4d, + 0x0001d648, 0x00001c4e, + 0x0001d649, 0x00001c4f, + 0x0001d64a, 0x00001c50, + 0x0001d64b, 0x00001c51, + 0x0001d64c, 0x00001c52, + 0x0001d64d, 0x00001c53, + 0x0001d64e, 0x00001c54, + 0x0001d64f, 0x00001c55, + 0x0001d650, 0x00001c56, + 0x0001d651, 0x00001c57, + 0x0001d652, 0x00001c58, + 0x0001d653, 0x00001c59, + 0x0001d654, 0x00001c5a, + 0x0001d655, 0x00001c5b, + 0x0001d656, 0x00001c5c, + 0x0001d657, 0x00001c5d, + 0x0001d658, 0x00001c5e, + 0x0001d659, 0x00001c5f, + 0x0001d65a, 0x00001c60, + 0x0001d65b, 0x00001c61, + 0x0001d65c, 0x00001c62, + 0x0001d65d, 0x00001c63, + 0x0001d65e, 0x00001c64, + 0x0001d65f, 0x00001c65, + 0x0001d660, 0x00001c66, + 0x0001d661, 0x00001c67, + 0x0001d662, 0x00001c68, + 0x0001d663, 0x00001c69, + 0x0001d664, 0x00001c6a, + 0x0001d665, 0x00001c6b, + 0x0001d666, 0x00001c6c, + 0x0001d667, 0x00001c6d, + 0x0001d668, 0x00001c6e, + 0x0001d669, 0x00001c6f, + 0x0001d66a, 0x00001c70, + 0x0001d66b, 0x00001c71, + 0x0001d66c, 0x00001c72, + 0x0001d66d, 0x00001c73, + 0x0001d66e, 0x00001c74, + 0x0001d66f, 0x00001c75, + 0x0001d670, 0x00001c76, + 0x0001d671, 0x00001c77, + 0x0001d672, 0x00001c78, + 0x0001d673, 0x00001c79, + 0x0001d674, 0x00001c7a, + 0x0001d675, 0x00001c7b, + 0x0001d676, 0x00001c7c, + 0x0001d677, 0x00001c7d, + 0x0001d678, 0x00001c7e, + 0x0001d679, 0x00001c7f, + 0x0001d67a, 0x00001c80, + 0x0001d67b, 0x00001c81, + 0x0001d67c, 0x00001c82, + 0x0001d67d, 0x00001c83, + 0x0001d67e, 0x00001c84, + 0x0001d67f, 0x00001c85, + 0x0001d680, 0x00001c86, + 0x0001d681, 0x00001c87, + 0x0001d682, 0x00001c88, + 0x0001d683, 0x00001c89, + 0x0001d684, 0x00001c8a, + 0x0001d685, 0x00001c8b, + 0x0001d686, 0x00001c8c, + 0x0001d687, 0x00001c8d, + 0x0001d688, 0x00001c8e, + 0x0001d689, 0x00001c8f, + 0x0001d68a, 0x00001c90, + 0x0001d68b, 0x00001c91, + 0x0001d68c, 0x00001c92, + 0x0001d68d, 0x00001c93, + 0x0001d68e, 0x00001c94, + 0x0001d68f, 0x00001c95, + 0x0001d690, 0x00001c96, + 0x0001d691, 0x00001c97, + 0x0001d692, 0x00001c98, + 0x0001d693, 0x00001c99, + 0x0001d694, 0x00001c9a, + 0x0001d695, 0x00001c9b, + 0x0001d696, 0x00001c9c, + 0x0001d697, 0x00001c9d, + 0x0001d698, 0x00001c9e, + 0x0001d699, 0x00001c9f, + 0x0001d69a, 0x00001ca0, + 0x0001d69b, 0x00001ca1, + 0x0001d69c, 0x00001ca2, + 0x0001d69d, 0x00001ca3, + 0x0001d69e, 0x00001ca4, + 0x0001d69f, 0x00001ca5, + 0x0001d6a0, 0x00001ca6, + 0x0001d6a1, 0x00001ca7, + 0x0001d6a2, 0x00001ca8, + 0x0001d6a3, 0x00001ca9, + 0x0001d6a8, 0x00001caa, + 0x0001d6a9, 0x00001cab, + 0x0001d6aa, 0x00001cac, + 0x0001d6ab, 0x00001cad, + 0x0001d6ac, 0x00001cae, + 0x0001d6ad, 0x00001caf, + 0x0001d6ae, 0x00001cb0, + 0x0001d6af, 0x00001cb1, + 0x0001d6b0, 0x00001cb2, + 0x0001d6b1, 0x00001cb3, + 0x0001d6b2, 0x00001cb4, + 0x0001d6b3, 0x00001cb5, + 0x0001d6b4, 0x00001cb6, + 0x0001d6b5, 0x00001cb7, + 0x0001d6b6, 0x00001cb8, + 0x0001d6b7, 0x00001cb9, + 0x0001d6b8, 0x00001cba, + 0x0001d6b9, 0x00001cbb, + 0x0001d6ba, 0x00001cbc, + 0x0001d6bb, 0x00001cbd, + 0x0001d6bc, 0x00001cbe, + 0x0001d6bd, 0x00001cbf, + 0x0001d6be, 0x00001cc0, + 0x0001d6bf, 0x00001cc1, + 0x0001d6c0, 0x00001cc2, + 0x0001d6c1, 0x00001cc3, + 0x0001d6c2, 0x00001cc4, + 0x0001d6c3, 0x00001cc5, + 0x0001d6c4, 0x00001cc6, + 0x0001d6c5, 0x00001cc7, + 0x0001d6c6, 0x00001cc8, + 0x0001d6c7, 0x00001cc9, + 0x0001d6c8, 0x00001cca, + 0x0001d6c9, 0x00001ccb, + 0x0001d6ca, 0x00001ccc, + 0x0001d6cb, 0x00001ccd, + 0x0001d6cc, 0x00001cce, + 0x0001d6cd, 0x00001ccf, + 0x0001d6ce, 0x00001cd0, + 0x0001d6cf, 0x00001cd1, + 0x0001d6d0, 0x00001cd2, + 0x0001d6d1, 0x00001cd3, + 0x0001d6d2, 0x00001cd4, + 0x0001d6d3, 0x00001cd5, + 0x0001d6d4, 0x00001cd6, + 0x0001d6d5, 0x00001cd7, + 0x0001d6d6, 0x00001cd8, + 0x0001d6d7, 0x00001cd9, + 0x0001d6d8, 0x00001cda, + 0x0001d6d9, 0x00001cdb, + 0x0001d6da, 0x00001cdc, + 0x0001d6db, 0x00001cdd, + 0x0001d6dc, 0x00001cde, + 0x0001d6dd, 0x00001cdf, + 0x0001d6de, 0x00001ce0, + 0x0001d6df, 0x00001ce1, + 0x0001d6e0, 0x00001ce2, + 0x0001d6e1, 0x00001ce3, + 0x0001d6e2, 0x00001ce4, + 0x0001d6e3, 0x00001ce5, + 0x0001d6e4, 0x00001ce6, + 0x0001d6e5, 0x00001ce7, + 0x0001d6e6, 0x00001ce8, + 0x0001d6e7, 0x00001ce9, + 0x0001d6e8, 0x00001cea, + 0x0001d6e9, 0x00001ceb, + 0x0001d6ea, 0x00001cec, + 0x0001d6eb, 0x00001ced, + 0x0001d6ec, 0x00001cee, + 0x0001d6ed, 0x00001cef, + 0x0001d6ee, 0x00001cf0, + 0x0001d6ef, 0x00001cf1, + 0x0001d6f0, 0x00001cf2, + 0x0001d6f1, 0x00001cf3, + 0x0001d6f2, 0x00001cf4, + 0x0001d6f3, 0x00001cf5, + 0x0001d6f4, 0x00001cf6, + 0x0001d6f5, 0x00001cf7, + 0x0001d6f6, 0x00001cf8, + 0x0001d6f7, 0x00001cf9, + 0x0001d6f8, 0x00001cfa, + 0x0001d6f9, 0x00001cfb, + 0x0001d6fa, 0x00001cfc, + 0x0001d6fb, 0x00001cfd, + 0x0001d6fc, 0x00001cfe, + 0x0001d6fd, 0x00001cff, + 0x0001d6fe, 0x00001d00, + 0x0001d6ff, 0x00001d01, + 0x0001d700, 0x00001d02, + 0x0001d701, 0x00001d03, + 0x0001d702, 0x00001d04, + 0x0001d703, 0x00001d05, + 0x0001d704, 0x00001d06, + 0x0001d705, 0x00001d07, + 0x0001d706, 0x00001d08, + 0x0001d707, 0x00001d09, + 0x0001d708, 0x00001d0a, + 0x0001d709, 0x00001d0b, + 0x0001d70a, 0x00001d0c, + 0x0001d70b, 0x00001d0d, + 0x0001d70c, 0x00001d0e, + 0x0001d70d, 0x00001d0f, + 0x0001d70e, 0x00001d10, + 0x0001d70f, 0x00001d11, + 0x0001d710, 0x00001d12, + 0x0001d711, 0x00001d13, + 0x0001d712, 0x00001d14, + 0x0001d713, 0x00001d15, + 0x0001d714, 0x00001d16, + 0x0001d715, 0x00001d17, + 0x0001d716, 0x00001d18, + 0x0001d717, 0x00001d19, + 0x0001d718, 0x00001d1a, + 0x0001d719, 0x00001d1b, + 0x0001d71a, 0x00001d1c, + 0x0001d71b, 0x00001d1d, + 0x0001d71c, 0x00001d1e, + 0x0001d71d, 0x00001d1f, + 0x0001d71e, 0x00001d20, + 0x0001d71f, 0x00001d21, + 0x0001d720, 0x00001d22, + 0x0001d721, 0x00001d23, + 0x0001d722, 0x00001d24, + 0x0001d723, 0x00001d25, + 0x0001d724, 0x00001d26, + 0x0001d725, 0x00001d27, + 0x0001d726, 0x00001d28, + 0x0001d727, 0x00001d29, + 0x0001d728, 0x00001d2a, + 0x0001d729, 0x00001d2b, + 0x0001d72a, 0x00001d2c, + 0x0001d72b, 0x00001d2d, + 0x0001d72c, 0x00001d2e, + 0x0001d72d, 0x00001d2f, + 0x0001d72e, 0x00001d30, + 0x0001d72f, 0x00001d31, + 0x0001d730, 0x00001d32, + 0x0001d731, 0x00001d33, + 0x0001d732, 0x00001d34, + 0x0001d733, 0x00001d35, + 0x0001d734, 0x00001d36, + 0x0001d735, 0x00001d37, + 0x0001d736, 0x00001d38, + 0x0001d737, 0x00001d39, + 0x0001d738, 0x00001d3a, + 0x0001d739, 0x00001d3b, + 0x0001d73a, 0x00001d3c, + 0x0001d73b, 0x00001d3d, + 0x0001d73c, 0x00001d3e, + 0x0001d73d, 0x00001d3f, + 0x0001d73e, 0x00001d40, + 0x0001d73f, 0x00001d41, + 0x0001d740, 0x00001d42, + 0x0001d741, 0x00001d43, + 0x0001d742, 0x00001d44, + 0x0001d743, 0x00001d45, + 0x0001d744, 0x00001d46, + 0x0001d745, 0x00001d47, + 0x0001d746, 0x00001d48, + 0x0001d747, 0x00001d49, + 0x0001d748, 0x00001d4a, + 0x0001d749, 0x00001d4b, + 0x0001d74a, 0x00001d4c, + 0x0001d74b, 0x00001d4d, + 0x0001d74c, 0x00001d4e, + 0x0001d74d, 0x00001d4f, + 0x0001d74e, 0x00001d50, + 0x0001d74f, 0x00001d51, + 0x0001d750, 0x00001d52, + 0x0001d751, 0x00001d53, + 0x0001d752, 0x00001d54, + 0x0001d753, 0x00001d55, + 0x0001d754, 0x00001d56, + 0x0001d755, 0x00001d57, + 0x0001d756, 0x00001d58, + 0x0001d757, 0x00001d59, + 0x0001d758, 0x00001d5a, + 0x0001d759, 0x00001d5b, + 0x0001d75a, 0x00001d5c, + 0x0001d75b, 0x00001d5d, + 0x0001d75c, 0x00001d5e, + 0x0001d75d, 0x00001d5f, + 0x0001d75e, 0x00001d60, + 0x0001d75f, 0x00001d61, + 0x0001d760, 0x00001d62, + 0x0001d761, 0x00001d63, + 0x0001d762, 0x00001d64, + 0x0001d763, 0x00001d65, + 0x0001d764, 0x00001d66, + 0x0001d765, 0x00001d67, + 0x0001d766, 0x00001d68, + 0x0001d767, 0x00001d69, + 0x0001d768, 0x00001d6a, + 0x0001d769, 0x00001d6b, + 0x0001d76a, 0x00001d6c, + 0x0001d76b, 0x00001d6d, + 0x0001d76c, 0x00001d6e, + 0x0001d76d, 0x00001d6f, + 0x0001d76e, 0x00001d70, + 0x0001d76f, 0x00001d71, + 0x0001d770, 0x00001d72, + 0x0001d771, 0x00001d73, + 0x0001d772, 0x00001d74, + 0x0001d773, 0x00001d75, + 0x0001d774, 0x00001d76, + 0x0001d775, 0x00001d77, + 0x0001d776, 0x00001d78, + 0x0001d777, 0x00001d79, + 0x0001d778, 0x00001d7a, + 0x0001d779, 0x00001d7b, + 0x0001d77a, 0x00001d7c, + 0x0001d77b, 0x00001d7d, + 0x0001d77c, 0x00001d7e, + 0x0001d77d, 0x00001d7f, + 0x0001d77e, 0x00001d80, + 0x0001d77f, 0x00001d81, + 0x0001d780, 0x00001d82, + 0x0001d781, 0x00001d83, + 0x0001d782, 0x00001d84, + 0x0001d783, 0x00001d85, + 0x0001d784, 0x00001d86, + 0x0001d785, 0x00001d87, + 0x0001d786, 0x00001d88, + 0x0001d787, 0x00001d89, + 0x0001d788, 0x00001d8a, + 0x0001d789, 0x00001d8b, + 0x0001d78a, 0x00001d8c, + 0x0001d78b, 0x00001d8d, + 0x0001d78c, 0x00001d8e, + 0x0001d78d, 0x00001d8f, + 0x0001d78e, 0x00001d90, + 0x0001d78f, 0x00001d91, + 0x0001d790, 0x00001d92, + 0x0001d791, 0x00001d93, + 0x0001d792, 0x00001d94, + 0x0001d793, 0x00001d95, + 0x0001d794, 0x00001d96, + 0x0001d795, 0x00001d97, + 0x0001d796, 0x00001d98, + 0x0001d797, 0x00001d99, + 0x0001d798, 0x00001d9a, + 0x0001d799, 0x00001d9b, + 0x0001d79a, 0x00001d9c, + 0x0001d79b, 0x00001d9d, + 0x0001d79c, 0x00001d9e, + 0x0001d79d, 0x00001d9f, + 0x0001d79e, 0x00001da0, + 0x0001d79f, 0x00001da1, + 0x0001d7a0, 0x00001da2, + 0x0001d7a1, 0x00001da3, + 0x0001d7a2, 0x00001da4, + 0x0001d7a3, 0x00001da5, + 0x0001d7a4, 0x00001da6, + 0x0001d7a5, 0x00001da7, + 0x0001d7a6, 0x00001da8, + 0x0001d7a7, 0x00001da9, + 0x0001d7a8, 0x00001daa, + 0x0001d7a9, 0x00001dab, + 0x0001d7aa, 0x00001dac, + 0x0001d7ab, 0x00001dad, + 0x0001d7ac, 0x00001dae, + 0x0001d7ad, 0x00001daf, + 0x0001d7ae, 0x00001db0, + 0x0001d7af, 0x00001db1, + 0x0001d7b0, 0x00001db2, + 0x0001d7b1, 0x00001db3, + 0x0001d7b2, 0x00001db4, + 0x0001d7b3, 0x00001db5, + 0x0001d7b4, 0x00001db6, + 0x0001d7b5, 0x00001db7, + 0x0001d7b6, 0x00001db8, + 0x0001d7b7, 0x00001db9, + 0x0001d7b8, 0x00001dba, + 0x0001d7b9, 0x00001dbb, + 0x0001d7ba, 0x00001dbc, + 0x0001d7bb, 0x00001dbd, + 0x0001d7bc, 0x00001dbe, + 0x0001d7bd, 0x00001dbf, + 0x0001d7be, 0x00001dc0, + 0x0001d7bf, 0x00001dc1, + 0x0001d7c0, 0x00001dc2, + 0x0001d7c1, 0x00001dc3, + 0x0001d7c2, 0x00001dc4, + 0x0001d7c3, 0x00001dc5, + 0x0001d7c4, 0x00001dc6, + 0x0001d7c5, 0x00001dc7, + 0x0001d7c6, 0x00001dc8, + 0x0001d7c7, 0x00001dc9, + 0x0001d7c8, 0x00001dca, + 0x0001d7c9, 0x00001dcb, + 0x0001d7ce, 0x00001dcc, + 0x0001d7cf, 0x00001dcd, + 0x0001d7d0, 0x00001dce, + 0x0001d7d1, 0x00001dcf, + 0x0001d7d2, 0x00001dd0, + 0x0001d7d3, 0x00001dd1, + 0x0001d7d4, 0x00001dd2, + 0x0001d7d5, 0x00001dd3, + 0x0001d7d6, 0x00001dd4, + 0x0001d7d7, 0x00001dd5, + 0x0001d7d8, 0x00001dd6, + 0x0001d7d9, 0x00001dd7, + 0x0001d7da, 0x00001dd8, + 0x0001d7db, 0x00001dd9, + 0x0001d7dc, 0x00001dda, + 0x0001d7dd, 0x00001ddb, + 0x0001d7de, 0x00001ddc, + 0x0001d7df, 0x00001ddd, + 0x0001d7e0, 0x00001dde, + 0x0001d7e1, 0x00001ddf, + 0x0001d7e2, 0x00001de0, + 0x0001d7e3, 0x00001de1, + 0x0001d7e4, 0x00001de2, + 0x0001d7e5, 0x00001de3, + 0x0001d7e6, 0x00001de4, + 0x0001d7e7, 0x00001de5, + 0x0001d7e8, 0x00001de6, + 0x0001d7e9, 0x00001de7, + 0x0001d7ea, 0x00001de8, + 0x0001d7eb, 0x00001de9, + 0x0001d7ec, 0x00001dea, + 0x0001d7ed, 0x00001deb, + 0x0001d7ee, 0x00001dec, + 0x0001d7ef, 0x00001ded, + 0x0001d7f0, 0x00001dee, + 0x0001d7f1, 0x00001def, + 0x0001d7f2, 0x00001df0, + 0x0001d7f3, 0x00001df1, + 0x0001d7f4, 0x00001df2, + 0x0001d7f5, 0x00001df3, + 0x0001d7f6, 0x00001df4, + 0x0001d7f7, 0x00001df5, + 0x0001d7f8, 0x00001df6, + 0x0001d7f9, 0x00001df7, + 0x0001d7fa, 0x00001df8, + 0x0001d7fb, 0x00001df9, + 0x0001d7fc, 0x00001dfa, + 0x0001d7fd, 0x00001dfb, + 0x0001d7fe, 0x00001dfc, + 0x0001d7ff, 0x00001dfd, + 0x0002f800, 0x00001dfe, + 0x0002f801, 0x00001dff, + 0x0002f802, 0x00001e00, + 0x0002f803, 0x00001e01, + 0x0002f804, 0x00001e02, + 0x0002f805, 0x00001e03, + 0x0002f806, 0x00001e04, + 0x0002f807, 0x00001e05, + 0x0002f808, 0x00001e06, + 0x0002f809, 0x00001e07, + 0x0002f80a, 0x00001e08, + 0x0002f80b, 0x00001e09, + 0x0002f80c, 0x00001e0a, + 0x0002f80d, 0x00001e0b, + 0x0002f80e, 0x00001e0c, + 0x0002f80f, 0x00001e0d, + 0x0002f810, 0x00001e0e, + 0x0002f811, 0x00001e0f, + 0x0002f812, 0x00001e10, + 0x0002f813, 0x00001e11, + 0x0002f814, 0x00001e12, + 0x0002f815, 0x00001e13, + 0x0002f816, 0x00001e14, + 0x0002f817, 0x00001e15, + 0x0002f818, 0x00001e16, + 0x0002f819, 0x00001e17, + 0x0002f81a, 0x00001e18, + 0x0002f81b, 0x00001e19, + 0x0002f81c, 0x00001e1a, + 0x0002f81d, 0x00001e1b, + 0x0002f81e, 0x00001e1c, + 0x0002f81f, 0x00001e1d, + 0x0002f820, 0x00001e1e, + 0x0002f821, 0x00001e1f, + 0x0002f822, 0x00001e20, + 0x0002f823, 0x00001e21, + 0x0002f824, 0x00001e22, + 0x0002f825, 0x00001e23, + 0x0002f826, 0x00001e24, + 0x0002f827, 0x00001e25, + 0x0002f828, 0x00001e26, + 0x0002f829, 0x00001e27, + 0x0002f82a, 0x00001e28, + 0x0002f82b, 0x00001e29, + 0x0002f82c, 0x00001e2a, + 0x0002f82d, 0x00001e2b, + 0x0002f82e, 0x00001e2c, + 0x0002f82f, 0x00001e2d, + 0x0002f830, 0x00001e2e, + 0x0002f831, 0x00001e2f, + 0x0002f832, 0x00001e30, + 0x0002f833, 0x00001e31, + 0x0002f834, 0x00001e32, + 0x0002f835, 0x00001e33, + 0x0002f836, 0x00001e34, + 0x0002f837, 0x00001e35, + 0x0002f838, 0x00001e36, + 0x0002f839, 0x00001e37, + 0x0002f83a, 0x00001e38, + 0x0002f83b, 0x00001e39, + 0x0002f83c, 0x00001e3a, + 0x0002f83d, 0x00001e3b, + 0x0002f83e, 0x00001e3c, + 0x0002f83f, 0x00001e3d, + 0x0002f840, 0x00001e3e, + 0x0002f841, 0x00001e3f, + 0x0002f842, 0x00001e40, + 0x0002f843, 0x00001e41, + 0x0002f844, 0x00001e42, + 0x0002f845, 0x00001e43, + 0x0002f846, 0x00001e44, + 0x0002f847, 0x00001e45, + 0x0002f848, 0x00001e46, + 0x0002f849, 0x00001e47, + 0x0002f84a, 0x00001e48, + 0x0002f84b, 0x00001e49, + 0x0002f84c, 0x00001e4a, + 0x0002f84d, 0x00001e4b, + 0x0002f84e, 0x00001e4c, + 0x0002f84f, 0x00001e4d, + 0x0002f850, 0x00001e4e, + 0x0002f851, 0x00001e4f, + 0x0002f852, 0x00001e50, + 0x0002f853, 0x00001e51, + 0x0002f854, 0x00001e52, + 0x0002f855, 0x00001e53, + 0x0002f856, 0x00001e54, + 0x0002f857, 0x00001e55, + 0x0002f858, 0x00001e56, + 0x0002f859, 0x00001e57, + 0x0002f85a, 0x00001e58, + 0x0002f85b, 0x00001e59, + 0x0002f85c, 0x00001e5a, + 0x0002f85d, 0x00001e5b, + 0x0002f85e, 0x00001e5c, + 0x0002f85f, 0x00001e5d, + 0x0002f860, 0x00001e5e, + 0x0002f861, 0x00001e5f, + 0x0002f862, 0x00001e60, + 0x0002f863, 0x00001e61, + 0x0002f864, 0x00001e62, + 0x0002f865, 0x00001e63, + 0x0002f866, 0x00001e64, + 0x0002f867, 0x00001e65, + 0x0002f868, 0x00001e66, + 0x0002f869, 0x00001e67, + 0x0002f86a, 0x00001e68, + 0x0002f86b, 0x00001e69, + 0x0002f86c, 0x00001e6a, + 0x0002f86d, 0x00001e6b, + 0x0002f86e, 0x00001e6c, + 0x0002f86f, 0x00001e6d, + 0x0002f870, 0x00001e6e, + 0x0002f871, 0x00001e6f, + 0x0002f872, 0x00001e70, + 0x0002f873, 0x00001e71, + 0x0002f874, 0x00001e72, + 0x0002f875, 0x00001e73, + 0x0002f876, 0x00001e74, + 0x0002f877, 0x00001e75, + 0x0002f878, 0x00001e76, + 0x0002f879, 0x00001e77, + 0x0002f87a, 0x00001e78, + 0x0002f87b, 0x00001e79, + 0x0002f87c, 0x00001e7a, + 0x0002f87d, 0x00001e7b, + 0x0002f87e, 0x00001e7c, + 0x0002f87f, 0x00001e7d, + 0x0002f880, 0x00001e7e, + 0x0002f881, 0x00001e7f, + 0x0002f882, 0x00001e80, + 0x0002f883, 0x00001e81, + 0x0002f884, 0x00001e82, + 0x0002f885, 0x00001e83, + 0x0002f886, 0x00001e84, + 0x0002f887, 0x00001e85, + 0x0002f888, 0x00001e86, + 0x0002f889, 0x00001e87, + 0x0002f88a, 0x00001e88, + 0x0002f88b, 0x00001e89, + 0x0002f88c, 0x00001e8a, + 0x0002f88d, 0x00001e8b, + 0x0002f88e, 0x00001e8c, + 0x0002f88f, 0x00001e8d, + 0x0002f890, 0x00001e8e, + 0x0002f891, 0x00001e8f, + 0x0002f892, 0x00001e90, + 0x0002f893, 0x00001e91, + 0x0002f894, 0x00001e92, + 0x0002f895, 0x00001e93, + 0x0002f896, 0x00001e94, + 0x0002f897, 0x00001e95, + 0x0002f898, 0x00001e96, + 0x0002f899, 0x00001e97, + 0x0002f89a, 0x00001e98, + 0x0002f89b, 0x00001e99, + 0x0002f89c, 0x00001e9a, + 0x0002f89d, 0x00001e9b, + 0x0002f89e, 0x00001e9c, + 0x0002f89f, 0x00001e9d, + 0x0002f8a0, 0x00001e9e, + 0x0002f8a1, 0x00001e9f, + 0x0002f8a2, 0x00001ea0, + 0x0002f8a3, 0x00001ea1, + 0x0002f8a4, 0x00001ea2, + 0x0002f8a5, 0x00001ea3, + 0x0002f8a6, 0x00001ea4, + 0x0002f8a7, 0x00001ea5, + 0x0002f8a8, 0x00001ea6, + 0x0002f8a9, 0x00001ea7, + 0x0002f8aa, 0x00001ea8, + 0x0002f8ab, 0x00001ea9, + 0x0002f8ac, 0x00001eaa, + 0x0002f8ad, 0x00001eab, + 0x0002f8ae, 0x00001eac, + 0x0002f8af, 0x00001ead, + 0x0002f8b0, 0x00001eae, + 0x0002f8b1, 0x00001eaf, + 0x0002f8b2, 0x00001eb0, + 0x0002f8b3, 0x00001eb1, + 0x0002f8b4, 0x00001eb2, + 0x0002f8b5, 0x00001eb3, + 0x0002f8b6, 0x00001eb4, + 0x0002f8b7, 0x00001eb5, + 0x0002f8b8, 0x00001eb6, + 0x0002f8b9, 0x00001eb7, + 0x0002f8ba, 0x00001eb8, + 0x0002f8bb, 0x00001eb9, + 0x0002f8bc, 0x00001eba, + 0x0002f8bd, 0x00001ebb, + 0x0002f8be, 0x00001ebc, + 0x0002f8bf, 0x00001ebd, + 0x0002f8c0, 0x00001ebe, + 0x0002f8c1, 0x00001ebf, + 0x0002f8c2, 0x00001ec0, + 0x0002f8c3, 0x00001ec1, + 0x0002f8c4, 0x00001ec2, + 0x0002f8c5, 0x00001ec3, + 0x0002f8c6, 0x00001ec4, + 0x0002f8c7, 0x00001ec5, + 0x0002f8c8, 0x00001ec6, + 0x0002f8c9, 0x00001ec7, + 0x0002f8ca, 0x00001ec8, + 0x0002f8cb, 0x00001ec9, + 0x0002f8cc, 0x00001eca, + 0x0002f8cd, 0x00001ecb, + 0x0002f8ce, 0x00001ecc, + 0x0002f8cf, 0x00001ecd, + 0x0002f8d0, 0x00001ece, + 0x0002f8d1, 0x00001ecf, + 0x0002f8d2, 0x00001ed0, + 0x0002f8d3, 0x00001ed1, + 0x0002f8d4, 0x00001ed2, + 0x0002f8d5, 0x00001ed3, + 0x0002f8d6, 0x00001ed4, + 0x0002f8d7, 0x00001ed5, + 0x0002f8d8, 0x00001ed6, + 0x0002f8d9, 0x00001ed7, + 0x0002f8da, 0x00001ed8, + 0x0002f8db, 0x00001ed9, + 0x0002f8dc, 0x00001eda, + 0x0002f8dd, 0x00001edb, + 0x0002f8de, 0x00001edc, + 0x0002f8df, 0x00001edd, + 0x0002f8e0, 0x00001ede, + 0x0002f8e1, 0x00001edf, + 0x0002f8e2, 0x00001ee0, + 0x0002f8e3, 0x00001ee1, + 0x0002f8e4, 0x00001ee2, + 0x0002f8e5, 0x00001ee3, + 0x0002f8e6, 0x00001ee4, + 0x0002f8e7, 0x00001ee5, + 0x0002f8e8, 0x00001ee6, + 0x0002f8e9, 0x00001ee7, + 0x0002f8ea, 0x00001ee8, + 0x0002f8eb, 0x00001ee9, + 0x0002f8ec, 0x00001eea, + 0x0002f8ed, 0x00001eeb, + 0x0002f8ee, 0x00001eec, + 0x0002f8ef, 0x00001eed, + 0x0002f8f0, 0x00001eee, + 0x0002f8f1, 0x00001eef, + 0x0002f8f2, 0x00001ef0, + 0x0002f8f3, 0x00001ef1, + 0x0002f8f4, 0x00001ef2, + 0x0002f8f5, 0x00001ef3, + 0x0002f8f6, 0x00001ef4, + 0x0002f8f7, 0x00001ef5, + 0x0002f8f8, 0x00001ef6, + 0x0002f8f9, 0x00001ef7, + 0x0002f8fa, 0x00001ef8, + 0x0002f8fb, 0x00001ef9, + 0x0002f8fc, 0x00001efa, + 0x0002f8fd, 0x00001efb, + 0x0002f8fe, 0x00001efc, + 0x0002f8ff, 0x00001efd, + 0x0002f900, 0x00001efe, + 0x0002f901, 0x00001eff, + 0x0002f902, 0x00001f00, + 0x0002f903, 0x00001f01, + 0x0002f904, 0x00001f02, + 0x0002f905, 0x00001f03, + 0x0002f906, 0x00001f04, + 0x0002f907, 0x00001f05, + 0x0002f908, 0x00001f06, + 0x0002f909, 0x00001f07, + 0x0002f90a, 0x00001f08, + 0x0002f90b, 0x00001f09, + 0x0002f90c, 0x00001f0a, + 0x0002f90d, 0x00001f0b, + 0x0002f90e, 0x00001f0c, + 0x0002f90f, 0x00001f0d, + 0x0002f910, 0x00001f0e, + 0x0002f911, 0x00001f0f, + 0x0002f912, 0x00001f10, + 0x0002f913, 0x00001f11, + 0x0002f914, 0x00001f12, + 0x0002f915, 0x00001f13, + 0x0002f916, 0x00001f14, + 0x0002f917, 0x00001f15, + 0x0002f918, 0x00001f16, + 0x0002f919, 0x00001f17, + 0x0002f91a, 0x00001f18, + 0x0002f91b, 0x00001f19, + 0x0002f91c, 0x00001f1a, + 0x0002f91d, 0x00001f1b, + 0x0002f91e, 0x00001f1c, + 0x0002f91f, 0x00001f1d, + 0x0002f920, 0x00001f1e, + 0x0002f921, 0x00001f1f, + 0x0002f922, 0x00001f20, + 0x0002f923, 0x00001f21, + 0x0002f924, 0x00001f22, + 0x0002f925, 0x00001f23, + 0x0002f926, 0x00001f24, + 0x0002f927, 0x00001f25, + 0x0002f928, 0x00001f26, + 0x0002f929, 0x00001f27, + 0x0002f92a, 0x00001f28, + 0x0002f92b, 0x00001f29, + 0x0002f92c, 0x00001f2a, + 0x0002f92d, 0x00001f2b, + 0x0002f92e, 0x00001f2c, + 0x0002f92f, 0x00001f2d, + 0x0002f930, 0x00001f2e, + 0x0002f931, 0x00001f2f, + 0x0002f932, 0x00001f30, + 0x0002f933, 0x00001f31, + 0x0002f934, 0x00001f32, + 0x0002f935, 0x00001f33, + 0x0002f936, 0x00001f34, + 0x0002f937, 0x00001f35, + 0x0002f938, 0x00001f36, + 0x0002f939, 0x00001f37, + 0x0002f93a, 0x00001f38, + 0x0002f93b, 0x00001f39, + 0x0002f93c, 0x00001f3a, + 0x0002f93d, 0x00001f3b, + 0x0002f93e, 0x00001f3c, + 0x0002f93f, 0x00001f3d, + 0x0002f940, 0x00001f3e, + 0x0002f941, 0x00001f3f, + 0x0002f942, 0x00001f40, + 0x0002f943, 0x00001f41, + 0x0002f944, 0x00001f42, + 0x0002f945, 0x00001f43, + 0x0002f946, 0x00001f44, + 0x0002f947, 0x00001f45, + 0x0002f948, 0x00001f46, + 0x0002f949, 0x00001f47, + 0x0002f94a, 0x00001f48, + 0x0002f94b, 0x00001f49, + 0x0002f94c, 0x00001f4a, + 0x0002f94d, 0x00001f4b, + 0x0002f94e, 0x00001f4c, + 0x0002f94f, 0x00001f4d, + 0x0002f950, 0x00001f4e, + 0x0002f951, 0x00001f4f, + 0x0002f952, 0x00001f50, + 0x0002f953, 0x00001f51, + 0x0002f954, 0x00001f52, + 0x0002f955, 0x00001f53, + 0x0002f956, 0x00001f54, + 0x0002f957, 0x00001f55, + 0x0002f958, 0x00001f56, + 0x0002f959, 0x00001f57, + 0x0002f95a, 0x00001f58, + 0x0002f95b, 0x00001f59, + 0x0002f95c, 0x00001f5a, + 0x0002f95d, 0x00001f5b, + 0x0002f95e, 0x00001f5c, + 0x0002f95f, 0x00001f5d, + 0x0002f960, 0x00001f5e, + 0x0002f961, 0x00001f5f, + 0x0002f962, 0x00001f60, + 0x0002f963, 0x00001f61, + 0x0002f964, 0x00001f62, + 0x0002f965, 0x00001f63, + 0x0002f966, 0x00001f64, + 0x0002f967, 0x00001f65, + 0x0002f968, 0x00001f66, + 0x0002f969, 0x00001f67, + 0x0002f96a, 0x00001f68, + 0x0002f96b, 0x00001f69, + 0x0002f96c, 0x00001f6a, + 0x0002f96d, 0x00001f6b, + 0x0002f96e, 0x00001f6c, + 0x0002f96f, 0x00001f6d, + 0x0002f970, 0x00001f6e, + 0x0002f971, 0x00001f6f, + 0x0002f972, 0x00001f70, + 0x0002f973, 0x00001f71, + 0x0002f974, 0x00001f72, + 0x0002f975, 0x00001f73, + 0x0002f976, 0x00001f74, + 0x0002f977, 0x00001f75, + 0x0002f978, 0x00001f76, + 0x0002f979, 0x00001f77, + 0x0002f97a, 0x00001f78, + 0x0002f97b, 0x00001f79, + 0x0002f97c, 0x00001f7a, + 0x0002f97d, 0x00001f7b, + 0x0002f97e, 0x00001f7c, + 0x0002f97f, 0x00001f7d, + 0x0002f980, 0x00001f7e, + 0x0002f981, 0x00001f7f, + 0x0002f982, 0x00001f80, + 0x0002f983, 0x00001f81, + 0x0002f984, 0x00001f82, + 0x0002f985, 0x00001f83, + 0x0002f986, 0x00001f84, + 0x0002f987, 0x00001f85, + 0x0002f988, 0x00001f86, + 0x0002f989, 0x00001f87, + 0x0002f98a, 0x00001f88, + 0x0002f98b, 0x00001f89, + 0x0002f98c, 0x00001f8a, + 0x0002f98d, 0x00001f8b, + 0x0002f98e, 0x00001f8c, + 0x0002f98f, 0x00001f8d, + 0x0002f990, 0x00001f8e, + 0x0002f991, 0x00001f8f, + 0x0002f992, 0x00001f90, + 0x0002f993, 0x00001f91, + 0x0002f994, 0x00001f92, + 0x0002f995, 0x00001f93, + 0x0002f996, 0x00001f94, + 0x0002f997, 0x00001f95, + 0x0002f998, 0x00001f96, + 0x0002f999, 0x00001f97, + 0x0002f99a, 0x00001f98, + 0x0002f99b, 0x00001f99, + 0x0002f99c, 0x00001f9a, + 0x0002f99d, 0x00001f9b, + 0x0002f99e, 0x00001f9c, + 0x0002f99f, 0x00001f9d, + 0x0002f9a0, 0x00001f9e, + 0x0002f9a1, 0x00001f9f, + 0x0002f9a2, 0x00001fa0, + 0x0002f9a3, 0x00001fa1, + 0x0002f9a4, 0x00001fa2, + 0x0002f9a5, 0x00001fa3, + 0x0002f9a6, 0x00001fa4, + 0x0002f9a7, 0x00001fa5, + 0x0002f9a8, 0x00001fa6, + 0x0002f9a9, 0x00001fa7, + 0x0002f9aa, 0x00001fa8, + 0x0002f9ab, 0x00001fa9, + 0x0002f9ac, 0x00001faa, + 0x0002f9ad, 0x00001fab, + 0x0002f9ae, 0x00001fac, + 0x0002f9af, 0x00001fad, + 0x0002f9b0, 0x00001fae, + 0x0002f9b1, 0x00001faf, + 0x0002f9b2, 0x00001fb0, + 0x0002f9b3, 0x00001fb1, + 0x0002f9b4, 0x00001fb2, + 0x0002f9b5, 0x00001fb3, + 0x0002f9b6, 0x00001fb4, + 0x0002f9b7, 0x00001fb5, + 0x0002f9b8, 0x00001fb6, + 0x0002f9b9, 0x00001fb7, + 0x0002f9ba, 0x00001fb8, + 0x0002f9bb, 0x00001fb9, + 0x0002f9bc, 0x00001fba, + 0x0002f9bd, 0x00001fbb, + 0x0002f9be, 0x00001fbc, + 0x0002f9bf, 0x00001fbd, + 0x0002f9c0, 0x00001fbe, + 0x0002f9c1, 0x00001fbf, + 0x0002f9c2, 0x00001fc0, + 0x0002f9c3, 0x00001fc1, + 0x0002f9c4, 0x00001fc2, + 0x0002f9c5, 0x00001fc3, + 0x0002f9c6, 0x00001fc4, + 0x0002f9c7, 0x00001fc5, + 0x0002f9c8, 0x00001fc6, + 0x0002f9c9, 0x00001fc7, + 0x0002f9ca, 0x00001fc8, + 0x0002f9cb, 0x00001fc9, + 0x0002f9cc, 0x00001fca, + 0x0002f9cd, 0x00001fcb, + 0x0002f9ce, 0x00001fcc, + 0x0002f9cf, 0x00001fcd, + 0x0002f9d0, 0x00001fce, + 0x0002f9d1, 0x00001fcf, + 0x0002f9d2, 0x00001fd0, + 0x0002f9d3, 0x00001fd1, + 0x0002f9d4, 0x00001fd2, + 0x0002f9d5, 0x00001fd3, + 0x0002f9d6, 0x00001fd4, + 0x0002f9d7, 0x00001fd5, + 0x0002f9d8, 0x00001fd6, + 0x0002f9d9, 0x00001fd7, + 0x0002f9da, 0x00001fd8, + 0x0002f9db, 0x00001fd9, + 0x0002f9dc, 0x00001fda, + 0x0002f9dd, 0x00001fdb, + 0x0002f9de, 0x00001fdc, + 0x0002f9df, 0x00001fdd, + 0x0002f9e0, 0x00001fde, + 0x0002f9e1, 0x00001fdf, + 0x0002f9e2, 0x00001fe0, + 0x0002f9e3, 0x00001fe1, + 0x0002f9e4, 0x00001fe2, + 0x0002f9e5, 0x00001fe3, + 0x0002f9e6, 0x00001fe4, + 0x0002f9e7, 0x00001fe5, + 0x0002f9e8, 0x00001fe6, + 0x0002f9e9, 0x00001fe7, + 0x0002f9ea, 0x00001fe8, + 0x0002f9eb, 0x00001fe9, + 0x0002f9ec, 0x00001fea, + 0x0002f9ed, 0x00001feb, + 0x0002f9ee, 0x00001fec, + 0x0002f9ef, 0x00001fed, + 0x0002f9f0, 0x00001fee, + 0x0002f9f1, 0x00001fef, + 0x0002f9f2, 0x00001ff0, + 0x0002f9f3, 0x00001ff1, + 0x0002f9f4, 0x00001ff2, + 0x0002f9f5, 0x00001ff3, + 0x0002f9f6, 0x00001ff4, + 0x0002f9f7, 0x00001ff5, + 0x0002f9f8, 0x00001ff6, + 0x0002f9f9, 0x00001ff7, + 0x0002f9fa, 0x00001ff8, + 0x0002f9fb, 0x00001ff9, + 0x0002f9fc, 0x00001ffa, + 0x0002f9fd, 0x00001ffb, + 0x0002f9fe, 0x00001ffc, + 0x0002f9ff, 0x00001ffd, + 0x0002fa00, 0x00001ffe, + 0x0002fa01, 0x00001fff, + 0x0002fa02, 0x00002000, + 0x0002fa03, 0x00002001, + 0x0002fa04, 0x00002002, + 0x0002fa05, 0x00002003, + 0x0002fa06, 0x00002004, + 0x0002fa07, 0x00002005, + 0x0002fa08, 0x00002006, + 0x0002fa09, 0x00002007, + 0x0002fa0a, 0x00002008, + 0x0002fa0b, 0x00002009, + 0x0002fa0c, 0x0000200a, + 0x0002fa0d, 0x0000200b, + 0x0002fa0e, 0x0000200c, + 0x0002fa0f, 0x0000200d, + 0x0002fa10, 0x0000200e, + 0x0002fa11, 0x0000200f, + 0x0002fa12, 0x00002010, + 0x0002fa13, 0x00002011, + 0x0002fa14, 0x00002012, + 0x0002fa15, 0x00002013, + 0x0002fa16, 0x00002014, + 0x0002fa17, 0x00002015, + 0x0002fa18, 0x00002016, + 0x0002fa19, 0x00002017, + 0x0002fa1a, 0x00002018, + 0x0002fa1b, 0x00002019, + 0x0002fa1c, 0x0000201a, + 0x0002fa1d, 0x0000201b, + 0x0000201c +}; + +static const ac_uint4 _uckdcmp_decomp[] = { + 0x00000020, 0x00000020, 0x00000308, 0x00000061, + 0x00000020, 0x00000304, 0x00000032, 0x00000033, + 0x00000020, 0x00000301, 0x000003bc, 0x00000020, + 0x00000327, 0x00000031, 0x0000006f, 0x00000031, + 0x00002044, 0x00000034, 0x00000031, 0x00002044, + 0x00000032, 0x00000033, 0x00002044, 0x00000034, + 0x00000041, 0x00000300, 0x00000041, 0x00000301, + 0x00000041, 0x00000302, 0x00000041, 0x00000303, + 0x00000041, 0x00000308, 0x00000041, 0x0000030a, + 0x00000043, 0x00000327, 0x00000045, 0x00000300, + 0x00000045, 0x00000301, 0x00000045, 0x00000302, + 0x00000045, 0x00000308, 0x00000049, 0x00000300, + 0x00000049, 0x00000301, 0x00000049, 0x00000302, + 0x00000049, 0x00000308, 0x0000004e, 0x00000303, + 0x0000004f, 0x00000300, 0x0000004f, 0x00000301, + 0x0000004f, 0x00000302, 0x0000004f, 0x00000303, + 0x0000004f, 0x00000308, 0x00000055, 0x00000300, + 0x00000055, 0x00000301, 0x00000055, 0x00000302, + 0x00000055, 0x00000308, 0x00000059, 0x00000301, + 0x00000061, 0x00000300, 0x00000061, 0x00000301, + 0x00000061, 0x00000302, 0x00000061, 0x00000303, + 0x00000061, 0x00000308, 0x00000061, 0x0000030a, + 0x00000063, 0x00000327, 0x00000065, 0x00000300, + 0x00000065, 0x00000301, 0x00000065, 0x00000302, + 0x00000065, 0x00000308, 0x00000069, 0x00000300, + 0x00000069, 0x00000301, 0x00000069, 0x00000302, + 0x00000069, 0x00000308, 0x0000006e, 0x00000303, + 0x0000006f, 0x00000300, 0x0000006f, 0x00000301, + 0x0000006f, 0x00000302, 0x0000006f, 0x00000303, + 0x0000006f, 0x00000308, 0x00000075, 0x00000300, + 0x00000075, 0x00000301, 0x00000075, 0x00000302, + 0x00000075, 0x00000308, 0x00000079, 0x00000301, + 0x00000079, 0x00000308, 0x00000041, 0x00000304, + 0x00000061, 0x00000304, 0x00000041, 0x00000306, + 0x00000061, 0x00000306, 0x00000041, 0x00000328, + 0x00000061, 0x00000328, 0x00000043, 0x00000301, + 0x00000063, 0x00000301, 0x00000043, 0x00000302, + 0x00000063, 0x00000302, 0x00000043, 0x00000307, + 0x00000063, 0x00000307, 0x00000043, 0x0000030c, + 0x00000063, 0x0000030c, 0x00000044, 0x0000030c, + 0x00000064, 0x0000030c, 0x00000045, 0x00000304, + 0x00000065, 0x00000304, 0x00000045, 0x00000306, + 0x00000065, 0x00000306, 0x00000045, 0x00000307, + 0x00000065, 0x00000307, 0x00000045, 0x00000328, + 0x00000065, 0x00000328, 0x00000045, 0x0000030c, + 0x00000065, 0x0000030c, 0x00000047, 0x00000302, + 0x00000067, 0x00000302, 0x00000047, 0x00000306, + 0x00000067, 0x00000306, 0x00000047, 0x00000307, + 0x00000067, 0x00000307, 0x00000047, 0x00000327, + 0x00000067, 0x00000327, 0x00000048, 0x00000302, + 0x00000068, 0x00000302, 0x00000049, 0x00000303, + 0x00000069, 0x00000303, 0x00000049, 0x00000304, + 0x00000069, 0x00000304, 0x00000049, 0x00000306, + 0x00000069, 0x00000306, 0x00000049, 0x00000328, + 0x00000069, 0x00000328, 0x00000049, 0x00000307, + 0x00000049, 0x0000004a, 0x00000069, 0x0000006a, + 0x0000004a, 0x00000302, 0x0000006a, 0x00000302, + 0x0000004b, 0x00000327, 0x0000006b, 0x00000327, + 0x0000004c, 0x00000301, 0x0000006c, 0x00000301, + 0x0000004c, 0x00000327, 0x0000006c, 0x00000327, + 0x0000004c, 0x0000030c, 0x0000006c, 0x0000030c, + 0x0000004c, 0x000000b7, 0x0000006c, 0x000000b7, + 0x0000004e, 0x00000301, 0x0000006e, 0x00000301, + 0x0000004e, 0x00000327, 0x0000006e, 0x00000327, + 0x0000004e, 0x0000030c, 0x0000006e, 0x0000030c, + 0x000002bc, 0x0000006e, 0x0000004f, 0x00000304, + 0x0000006f, 0x00000304, 0x0000004f, 0x00000306, + 0x0000006f, 0x00000306, 0x0000004f, 0x0000030b, + 0x0000006f, 0x0000030b, 0x00000052, 0x00000301, + 0x00000072, 0x00000301, 0x00000052, 0x00000327, + 0x00000072, 0x00000327, 0x00000052, 0x0000030c, + 0x00000072, 0x0000030c, 0x00000053, 0x00000301, + 0x00000073, 0x00000301, 0x00000053, 0x00000302, + 0x00000073, 0x00000302, 0x00000053, 0x00000327, + 0x00000073, 0x00000327, 0x00000053, 0x0000030c, + 0x00000073, 0x0000030c, 0x00000054, 0x00000327, + 0x00000074, 0x00000327, 0x00000054, 0x0000030c, + 0x00000074, 0x0000030c, 0x00000055, 0x00000303, + 0x00000075, 0x00000303, 0x00000055, 0x00000304, + 0x00000075, 0x00000304, 0x00000055, 0x00000306, + 0x00000075, 0x00000306, 0x00000055, 0x0000030a, + 0x00000075, 0x0000030a, 0x00000055, 0x0000030b, + 0x00000075, 0x0000030b, 0x00000055, 0x00000328, + 0x00000075, 0x00000328, 0x00000057, 0x00000302, + 0x00000077, 0x00000302, 0x00000059, 0x00000302, + 0x00000079, 0x00000302, 0x00000059, 0x00000308, + 0x0000005a, 0x00000301, 0x0000007a, 0x00000301, + 0x0000005a, 0x00000307, 0x0000007a, 0x00000307, + 0x0000005a, 0x0000030c, 0x0000007a, 0x0000030c, + 0x00000073, 0x0000004f, 0x0000031b, 0x0000006f, + 0x0000031b, 0x00000055, 0x0000031b, 0x00000075, + 0x0000031b, 0x00000044, 0x0000005a, 0x0000030c, + 0x00000044, 0x0000007a, 0x0000030c, 0x00000064, + 0x0000007a, 0x0000030c, 0x0000004c, 0x0000004a, + 0x0000004c, 0x0000006a, 0x0000006c, 0x0000006a, + 0x0000004e, 0x0000004a, 0x0000004e, 0x0000006a, + 0x0000006e, 0x0000006a, 0x00000041, 0x0000030c, + 0x00000061, 0x0000030c, 0x00000049, 0x0000030c, + 0x00000069, 0x0000030c, 0x0000004f, 0x0000030c, + 0x0000006f, 0x0000030c, 0x00000055, 0x0000030c, + 0x00000075, 0x0000030c, 0x00000055, 0x00000308, + 0x00000304, 0x00000075, 0x00000308, 0x00000304, + 0x00000055, 0x00000308, 0x00000301, 0x00000075, + 0x00000308, 0x00000301, 0x00000055, 0x00000308, + 0x0000030c, 0x00000075, 0x00000308, 0x0000030c, + 0x00000055, 0x00000308, 0x00000300, 0x00000075, + 0x00000308, 0x00000300, 0x00000041, 0x00000308, + 0x00000304, 0x00000061, 0x00000308, 0x00000304, + 0x00000041, 0x00000307, 0x00000304, 0x00000061, + 0x00000307, 0x00000304, 0x000000c6, 0x00000304, + 0x000000e6, 0x00000304, 0x00000047, 0x0000030c, + 0x00000067, 0x0000030c, 0x0000004b, 0x0000030c, + 0x0000006b, 0x0000030c, 0x0000004f, 0x00000328, + 0x0000006f, 0x00000328, 0x0000004f, 0x00000328, + 0x00000304, 0x0000006f, 0x00000328, 0x00000304, + 0x000001b7, 0x0000030c, 0x00000292, 0x0000030c, + 0x0000006a, 0x0000030c, 0x00000044, 0x0000005a, + 0x00000044, 0x0000007a, 0x00000064, 0x0000007a, + 0x00000047, 0x00000301, 0x00000067, 0x00000301, + 0x0000004e, 0x00000300, 0x0000006e, 0x00000300, + 0x00000041, 0x0000030a, 0x00000301, 0x00000061, + 0x0000030a, 0x00000301, 0x000000c6, 0x00000301, + 0x000000e6, 0x00000301, 0x000000d8, 0x00000301, + 0x000000f8, 0x00000301, 0x00000041, 0x0000030f, + 0x00000061, 0x0000030f, 0x00000041, 0x00000311, + 0x00000061, 0x00000311, 0x00000045, 0x0000030f, + 0x00000065, 0x0000030f, 0x00000045, 0x00000311, + 0x00000065, 0x00000311, 0x00000049, 0x0000030f, + 0x00000069, 0x0000030f, 0x00000049, 0x00000311, + 0x00000069, 0x00000311, 0x0000004f, 0x0000030f, + 0x0000006f, 0x0000030f, 0x0000004f, 0x00000311, + 0x0000006f, 0x00000311, 0x00000052, 0x0000030f, + 0x00000072, 0x0000030f, 0x00000052, 0x00000311, + 0x00000072, 0x00000311, 0x00000055, 0x0000030f, + 0x00000075, 0x0000030f, 0x00000055, 0x00000311, + 0x00000075, 0x00000311, 0x00000053, 0x00000326, + 0x00000073, 0x00000326, 0x00000054, 0x00000326, + 0x00000074, 0x00000326, 0x00000048, 0x0000030c, + 0x00000068, 0x0000030c, 0x00000041, 0x00000307, + 0x00000061, 0x00000307, 0x00000045, 0x00000327, + 0x00000065, 0x00000327, 0x0000004f, 0x00000308, + 0x00000304, 0x0000006f, 0x00000308, 0x00000304, + 0x0000004f, 0x00000303, 0x00000304, 0x0000006f, + 0x00000303, 0x00000304, 0x0000004f, 0x00000307, + 0x0000006f, 0x00000307, 0x0000004f, 0x00000307, + 0x00000304, 0x0000006f, 0x00000307, 0x00000304, + 0x00000059, 0x00000304, 0x00000079, 0x00000304, + 0x00000068, 0x00000266, 0x0000006a, 0x00000072, + 0x00000279, 0x0000027b, 0x00000281, 0x00000077, + 0x00000079, 0x00000020, 0x00000306, 0x00000020, + 0x00000307, 0x00000020, 0x0000030a, 0x00000020, + 0x00000328, 0x00000020, 0x00000303, 0x00000020, + 0x0000030b, 0x00000263, 0x0000006c, 0x00000073, + 0x00000078, 0x00000295, 0x00000300, 0x00000301, + 0x00000313, 0x00000308, 0x00000301, 0x000002b9, + 0x00000020, 0x00000345, 0x0000003b, 0x00000020, + 0x00000301, 0x00000020, 0x00000308, 0x00000301, + 0x00000391, 0x00000301, 0x000000b7, 0x00000395, + 0x00000301, 0x00000397, 0x00000301, 0x00000399, + 0x00000301, 0x0000039f, 0x00000301, 0x000003a5, + 0x00000301, 0x000003a9, 0x00000301, 0x000003b9, + 0x00000308, 0x00000301, 0x00000399, 0x00000308, + 0x000003a5, 0x00000308, 0x000003b1, 0x00000301, + 0x000003b5, 0x00000301, 0x000003b7, 0x00000301, + 0x000003b9, 0x00000301, 0x000003c5, 0x00000308, + 0x00000301, 0x000003b9, 0x00000308, 0x000003c5, + 0x00000308, 0x000003bf, 0x00000301, 0x000003c5, + 0x00000301, 0x000003c9, 0x00000301, 0x000003b2, + 0x000003b8, 0x000003a5, 0x000003a5, 0x00000301, + 0x000003a5, 0x00000308, 0x000003c6, 0x000003c0, + 0x000003ba, 0x000003c1, 0x000003c2, 0x00000398, + 0x000003b5, 0x00000415, 0x00000300, 0x00000415, + 0x00000308, 0x00000413, 0x00000301, 0x00000406, + 0x00000308, 0x0000041a, 0x00000301, 0x00000418, + 0x00000300, 0x00000423, 0x00000306, 0x00000418, + 0x00000306, 0x00000438, 0x00000306, 0x00000435, + 0x00000300, 0x00000435, 0x00000308, 0x00000433, + 0x00000301, 0x00000456, 0x00000308, 0x0000043a, + 0x00000301, 0x00000438, 0x00000300, 0x00000443, + 0x00000306, 0x00000474, 0x0000030f, 0x00000475, + 0x0000030f, 0x00000416, 0x00000306, 0x00000436, + 0x00000306, 0x00000410, 0x00000306, 0x00000430, + 0x00000306, 0x00000410, 0x00000308, 0x00000430, + 0x00000308, 0x00000415, 0x00000306, 0x00000435, + 0x00000306, 0x000004d8, 0x00000308, 0x000004d9, + 0x00000308, 0x00000416, 0x00000308, 0x00000436, + 0x00000308, 0x00000417, 0x00000308, 0x00000437, + 0x00000308, 0x00000418, 0x00000304, 0x00000438, + 0x00000304, 0x00000418, 0x00000308, 0x00000438, + 0x00000308, 0x0000041e, 0x00000308, 0x0000043e, + 0x00000308, 0x000004e8, 0x00000308, 0x000004e9, + 0x00000308, 0x0000042d, 0x00000308, 0x0000044d, + 0x00000308, 0x00000423, 0x00000304, 0x00000443, + 0x00000304, 0x00000423, 0x00000308, 0x00000443, + 0x00000308, 0x00000423, 0x0000030b, 0x00000443, + 0x0000030b, 0x00000427, 0x00000308, 0x00000447, + 0x00000308, 0x0000042b, 0x00000308, 0x0000044b, + 0x00000308, 0x00000565, 0x00000582, 0x00000627, + 0x00000653, 0x00000627, 0x00000654, 0x00000648, + 0x00000654, 0x00000627, 0x00000655, 0x0000064a, + 0x00000654, 0x00000627, 0x00000674, 0x00000648, + 0x00000674, 0x000006c7, 0x00000674, 0x0000064a, + 0x00000674, 0x000006d5, 0x00000654, 0x000006c1, + 0x00000654, 0x000006d2, 0x00000654, 0x00000928, + 0x0000093c, 0x00000930, 0x0000093c, 0x00000933, + 0x0000093c, 0x00000915, 0x0000093c, 0x00000916, + 0x0000093c, 0x00000917, 0x0000093c, 0x0000091c, + 0x0000093c, 0x00000921, 0x0000093c, 0x00000922, + 0x0000093c, 0x0000092b, 0x0000093c, 0x0000092f, + 0x0000093c, 0x000009c7, 0x000009be, 0x000009c7, + 0x000009d7, 0x000009a1, 0x000009bc, 0x000009a2, + 0x000009bc, 0x000009af, 0x000009bc, 0x00000a32, + 0x00000a3c, 0x00000a38, 0x00000a3c, 0x00000a16, + 0x00000a3c, 0x00000a17, 0x00000a3c, 0x00000a1c, + 0x00000a3c, 0x00000a2b, 0x00000a3c, 0x00000b47, + 0x00000b56, 0x00000b47, 0x00000b3e, 0x00000b47, + 0x00000b57, 0x00000b21, 0x00000b3c, 0x00000b22, + 0x00000b3c, 0x00000b92, 0x00000bd7, 0x00000bc6, + 0x00000bbe, 0x00000bc7, 0x00000bbe, 0x00000bc6, + 0x00000bd7, 0x00000c46, 0x00000c56, 0x00000cbf, + 0x00000cd5, 0x00000cc6, 0x00000cd5, 0x00000cc6, + 0x00000cd6, 0x00000cc6, 0x00000cc2, 0x00000cc6, + 0x00000cc2, 0x00000cd5, 0x00000d46, 0x00000d3e, + 0x00000d47, 0x00000d3e, 0x00000d46, 0x00000d57, + 0x00000dd9, 0x00000dca, 0x00000dd9, 0x00000dcf, + 0x00000dd9, 0x00000dcf, 0x00000dca, 0x00000dd9, + 0x00000ddf, 0x00000e4d, 0x00000e32, 0x00000ecd, + 0x00000eb2, 0x00000eab, 0x00000e99, 0x00000eab, + 0x00000ea1, 0x00000f0b, 0x00000f42, 0x00000fb7, + 0x00000f4c, 0x00000fb7, 0x00000f51, 0x00000fb7, + 0x00000f56, 0x00000fb7, 0x00000f5b, 0x00000fb7, + 0x00000f40, 0x00000fb5, 0x00000f71, 0x00000f72, + 0x00000f71, 0x00000f74, 0x00000fb2, 0x00000f80, + 0x00000fb2, 0x00000f71, 0x00000f80, 0x00000fb3, + 0x00000f80, 0x00000fb3, 0x00000f71, 0x00000f80, + 0x00000f71, 0x00000f80, 0x00000f92, 0x00000fb7, + 0x00000f9c, 0x00000fb7, 0x00000fa1, 0x00000fb7, + 0x00000fa6, 0x00000fb7, 0x00000fab, 0x00000fb7, + 0x00000f90, 0x00000fb5, 0x00001025, 0x0000102e, + 0x00000041, 0x00000325, 0x00000061, 0x00000325, + 0x00000042, 0x00000307, 0x00000062, 0x00000307, + 0x00000042, 0x00000323, 0x00000062, 0x00000323, + 0x00000042, 0x00000331, 0x00000062, 0x00000331, + 0x00000043, 0x00000327, 0x00000301, 0x00000063, + 0x00000327, 0x00000301, 0x00000044, 0x00000307, + 0x00000064, 0x00000307, 0x00000044, 0x00000323, + 0x00000064, 0x00000323, 0x00000044, 0x00000331, + 0x00000064, 0x00000331, 0x00000044, 0x00000327, + 0x00000064, 0x00000327, 0x00000044, 0x0000032d, + 0x00000064, 0x0000032d, 0x00000045, 0x00000304, + 0x00000300, 0x00000065, 0x00000304, 0x00000300, + 0x00000045, 0x00000304, 0x00000301, 0x00000065, + 0x00000304, 0x00000301, 0x00000045, 0x0000032d, + 0x00000065, 0x0000032d, 0x00000045, 0x00000330, + 0x00000065, 0x00000330, 0x00000045, 0x00000327, + 0x00000306, 0x00000065, 0x00000327, 0x00000306, + 0x00000046, 0x00000307, 0x00000066, 0x00000307, + 0x00000047, 0x00000304, 0x00000067, 0x00000304, + 0x00000048, 0x00000307, 0x00000068, 0x00000307, + 0x00000048, 0x00000323, 0x00000068, 0x00000323, + 0x00000048, 0x00000308, 0x00000068, 0x00000308, + 0x00000048, 0x00000327, 0x00000068, 0x00000327, + 0x00000048, 0x0000032e, 0x00000068, 0x0000032e, + 0x00000049, 0x00000330, 0x00000069, 0x00000330, + 0x00000049, 0x00000308, 0x00000301, 0x00000069, + 0x00000308, 0x00000301, 0x0000004b, 0x00000301, + 0x0000006b, 0x00000301, 0x0000004b, 0x00000323, + 0x0000006b, 0x00000323, 0x0000004b, 0x00000331, + 0x0000006b, 0x00000331, 0x0000004c, 0x00000323, + 0x0000006c, 0x00000323, 0x0000004c, 0x00000323, + 0x00000304, 0x0000006c, 0x00000323, 0x00000304, + 0x0000004c, 0x00000331, 0x0000006c, 0x00000331, + 0x0000004c, 0x0000032d, 0x0000006c, 0x0000032d, + 0x0000004d, 0x00000301, 0x0000006d, 0x00000301, + 0x0000004d, 0x00000307, 0x0000006d, 0x00000307, + 0x0000004d, 0x00000323, 0x0000006d, 0x00000323, + 0x0000004e, 0x00000307, 0x0000006e, 0x00000307, + 0x0000004e, 0x00000323, 0x0000006e, 0x00000323, + 0x0000004e, 0x00000331, 0x0000006e, 0x00000331, + 0x0000004e, 0x0000032d, 0x0000006e, 0x0000032d, + 0x0000004f, 0x00000303, 0x00000301, 0x0000006f, + 0x00000303, 0x00000301, 0x0000004f, 0x00000303, + 0x00000308, 0x0000006f, 0x00000303, 0x00000308, + 0x0000004f, 0x00000304, 0x00000300, 0x0000006f, + 0x00000304, 0x00000300, 0x0000004f, 0x00000304, + 0x00000301, 0x0000006f, 0x00000304, 0x00000301, + 0x00000050, 0x00000301, 0x00000070, 0x00000301, + 0x00000050, 0x00000307, 0x00000070, 0x00000307, + 0x00000052, 0x00000307, 0x00000072, 0x00000307, + 0x00000052, 0x00000323, 0x00000072, 0x00000323, + 0x00000052, 0x00000323, 0x00000304, 0x00000072, + 0x00000323, 0x00000304, 0x00000052, 0x00000331, + 0x00000072, 0x00000331, 0x00000053, 0x00000307, + 0x00000073, 0x00000307, 0x00000053, 0x00000323, + 0x00000073, 0x00000323, 0x00000053, 0x00000301, + 0x00000307, 0x00000073, 0x00000301, 0x00000307, + 0x00000053, 0x0000030c, 0x00000307, 0x00000073, + 0x0000030c, 0x00000307, 0x00000053, 0x00000323, + 0x00000307, 0x00000073, 0x00000323, 0x00000307, + 0x00000054, 0x00000307, 0x00000074, 0x00000307, + 0x00000054, 0x00000323, 0x00000074, 0x00000323, + 0x00000054, 0x00000331, 0x00000074, 0x00000331, + 0x00000054, 0x0000032d, 0x00000074, 0x0000032d, + 0x00000055, 0x00000324, 0x00000075, 0x00000324, + 0x00000055, 0x00000330, 0x00000075, 0x00000330, + 0x00000055, 0x0000032d, 0x00000075, 0x0000032d, + 0x00000055, 0x00000303, 0x00000301, 0x00000075, + 0x00000303, 0x00000301, 0x00000055, 0x00000304, + 0x00000308, 0x00000075, 0x00000304, 0x00000308, + 0x00000056, 0x00000303, 0x00000076, 0x00000303, + 0x00000056, 0x00000323, 0x00000076, 0x00000323, + 0x00000057, 0x00000300, 0x00000077, 0x00000300, + 0x00000057, 0x00000301, 0x00000077, 0x00000301, + 0x00000057, 0x00000308, 0x00000077, 0x00000308, + 0x00000057, 0x00000307, 0x00000077, 0x00000307, + 0x00000057, 0x00000323, 0x00000077, 0x00000323, + 0x00000058, 0x00000307, 0x00000078, 0x00000307, + 0x00000058, 0x00000308, 0x00000078, 0x00000308, + 0x00000059, 0x00000307, 0x00000079, 0x00000307, + 0x0000005a, 0x00000302, 0x0000007a, 0x00000302, + 0x0000005a, 0x00000323, 0x0000007a, 0x00000323, + 0x0000005a, 0x00000331, 0x0000007a, 0x00000331, + 0x00000068, 0x00000331, 0x00000074, 0x00000308, + 0x00000077, 0x0000030a, 0x00000079, 0x0000030a, + 0x00000061, 0x000002be, 0x00000073, 0x00000307, + 0x00000041, 0x00000323, 0x00000061, 0x00000323, + 0x00000041, 0x00000309, 0x00000061, 0x00000309, + 0x00000041, 0x00000302, 0x00000301, 0x00000061, + 0x00000302, 0x00000301, 0x00000041, 0x00000302, + 0x00000300, 0x00000061, 0x00000302, 0x00000300, + 0x00000041, 0x00000302, 0x00000309, 0x00000061, + 0x00000302, 0x00000309, 0x00000041, 0x00000302, + 0x00000303, 0x00000061, 0x00000302, 0x00000303, + 0x00000041, 0x00000323, 0x00000302, 0x00000061, + 0x00000323, 0x00000302, 0x00000041, 0x00000306, + 0x00000301, 0x00000061, 0x00000306, 0x00000301, + 0x00000041, 0x00000306, 0x00000300, 0x00000061, + 0x00000306, 0x00000300, 0x00000041, 0x00000306, + 0x00000309, 0x00000061, 0x00000306, 0x00000309, + 0x00000041, 0x00000306, 0x00000303, 0x00000061, + 0x00000306, 0x00000303, 0x00000041, 0x00000323, + 0x00000306, 0x00000061, 0x00000323, 0x00000306, + 0x00000045, 0x00000323, 0x00000065, 0x00000323, + 0x00000045, 0x00000309, 0x00000065, 0x00000309, + 0x00000045, 0x00000303, 0x00000065, 0x00000303, + 0x00000045, 0x00000302, 0x00000301, 0x00000065, + 0x00000302, 0x00000301, 0x00000045, 0x00000302, + 0x00000300, 0x00000065, 0x00000302, 0x00000300, + 0x00000045, 0x00000302, 0x00000309, 0x00000065, + 0x00000302, 0x00000309, 0x00000045, 0x00000302, + 0x00000303, 0x00000065, 0x00000302, 0x00000303, + 0x00000045, 0x00000323, 0x00000302, 0x00000065, + 0x00000323, 0x00000302, 0x00000049, 0x00000309, + 0x00000069, 0x00000309, 0x00000049, 0x00000323, + 0x00000069, 0x00000323, 0x0000004f, 0x00000323, + 0x0000006f, 0x00000323, 0x0000004f, 0x00000309, + 0x0000006f, 0x00000309, 0x0000004f, 0x00000302, + 0x00000301, 0x0000006f, 0x00000302, 0x00000301, + 0x0000004f, 0x00000302, 0x00000300, 0x0000006f, + 0x00000302, 0x00000300, 0x0000004f, 0x00000302, + 0x00000309, 0x0000006f, 0x00000302, 0x00000309, + 0x0000004f, 0x00000302, 0x00000303, 0x0000006f, + 0x00000302, 0x00000303, 0x0000004f, 0x00000323, + 0x00000302, 0x0000006f, 0x00000323, 0x00000302, + 0x0000004f, 0x0000031b, 0x00000301, 0x0000006f, + 0x0000031b, 0x00000301, 0x0000004f, 0x0000031b, + 0x00000300, 0x0000006f, 0x0000031b, 0x00000300, + 0x0000004f, 0x0000031b, 0x00000309, 0x0000006f, + 0x0000031b, 0x00000309, 0x0000004f, 0x0000031b, + 0x00000303, 0x0000006f, 0x0000031b, 0x00000303, + 0x0000004f, 0x0000031b, 0x00000323, 0x0000006f, + 0x0000031b, 0x00000323, 0x00000055, 0x00000323, + 0x00000075, 0x00000323, 0x00000055, 0x00000309, + 0x00000075, 0x00000309, 0x00000055, 0x0000031b, + 0x00000301, 0x00000075, 0x0000031b, 0x00000301, + 0x00000055, 0x0000031b, 0x00000300, 0x00000075, + 0x0000031b, 0x00000300, 0x00000055, 0x0000031b, + 0x00000309, 0x00000075, 0x0000031b, 0x00000309, + 0x00000055, 0x0000031b, 0x00000303, 0x00000075, + 0x0000031b, 0x00000303, 0x00000055, 0x0000031b, + 0x00000323, 0x00000075, 0x0000031b, 0x00000323, + 0x00000059, 0x00000300, 0x00000079, 0x00000300, + 0x00000059, 0x00000323, 0x00000079, 0x00000323, + 0x00000059, 0x00000309, 0x00000079, 0x00000309, + 0x00000059, 0x00000303, 0x00000079, 0x00000303, + 0x000003b1, 0x00000313, 0x000003b1, 0x00000314, + 0x000003b1, 0x00000313, 0x00000300, 0x000003b1, + 0x00000314, 0x00000300, 0x000003b1, 0x00000313, + 0x00000301, 0x000003b1, 0x00000314, 0x00000301, + 0x000003b1, 0x00000313, 0x00000342, 0x000003b1, + 0x00000314, 0x00000342, 0x00000391, 0x00000313, + 0x00000391, 0x00000314, 0x00000391, 0x00000313, + 0x00000300, 0x00000391, 0x00000314, 0x00000300, + 0x00000391, 0x00000313, 0x00000301, 0x00000391, + 0x00000314, 0x00000301, 0x00000391, 0x00000313, + 0x00000342, 0x00000391, 0x00000314, 0x00000342, + 0x000003b5, 0x00000313, 0x000003b5, 0x00000314, + 0x000003b5, 0x00000313, 0x00000300, 0x000003b5, + 0x00000314, 0x00000300, 0x000003b5, 0x00000313, + 0x00000301, 0x000003b5, 0x00000314, 0x00000301, + 0x00000395, 0x00000313, 0x00000395, 0x00000314, + 0x00000395, 0x00000313, 0x00000300, 0x00000395, + 0x00000314, 0x00000300, 0x00000395, 0x00000313, + 0x00000301, 0x00000395, 0x00000314, 0x00000301, + 0x000003b7, 0x00000313, 0x000003b7, 0x00000314, + 0x000003b7, 0x00000313, 0x00000300, 0x000003b7, + 0x00000314, 0x00000300, 0x000003b7, 0x00000313, + 0x00000301, 0x000003b7, 0x00000314, 0x00000301, + 0x000003b7, 0x00000313, 0x00000342, 0x000003b7, + 0x00000314, 0x00000342, 0x00000397, 0x00000313, + 0x00000397, 0x00000314, 0x00000397, 0x00000313, + 0x00000300, 0x00000397, 0x00000314, 0x00000300, + 0x00000397, 0x00000313, 0x00000301, 0x00000397, + 0x00000314, 0x00000301, 0x00000397, 0x00000313, + 0x00000342, 0x00000397, 0x00000314, 0x00000342, + 0x000003b9, 0x00000313, 0x000003b9, 0x00000314, + 0x000003b9, 0x00000313, 0x00000300, 0x000003b9, + 0x00000314, 0x00000300, 0x000003b9, 0x00000313, + 0x00000301, 0x000003b9, 0x00000314, 0x00000301, + 0x000003b9, 0x00000313, 0x00000342, 0x000003b9, + 0x00000314, 0x00000342, 0x00000399, 0x00000313, + 0x00000399, 0x00000314, 0x00000399, 0x00000313, + 0x00000300, 0x00000399, 0x00000314, 0x00000300, + 0x00000399, 0x00000313, 0x00000301, 0x00000399, + 0x00000314, 0x00000301, 0x00000399, 0x00000313, + 0x00000342, 0x00000399, 0x00000314, 0x00000342, + 0x000003bf, 0x00000313, 0x000003bf, 0x00000314, + 0x000003bf, 0x00000313, 0x00000300, 0x000003bf, + 0x00000314, 0x00000300, 0x000003bf, 0x00000313, + 0x00000301, 0x000003bf, 0x00000314, 0x00000301, + 0x0000039f, 0x00000313, 0x0000039f, 0x00000314, + 0x0000039f, 0x00000313, 0x00000300, 0x0000039f, + 0x00000314, 0x00000300, 0x0000039f, 0x00000313, + 0x00000301, 0x0000039f, 0x00000314, 0x00000301, + 0x000003c5, 0x00000313, 0x000003c5, 0x00000314, + 0x000003c5, 0x00000313, 0x00000300, 0x000003c5, + 0x00000314, 0x00000300, 0x000003c5, 0x00000313, + 0x00000301, 0x000003c5, 0x00000314, 0x00000301, + 0x000003c5, 0x00000313, 0x00000342, 0x000003c5, + 0x00000314, 0x00000342, 0x000003a5, 0x00000314, + 0x000003a5, 0x00000314, 0x00000300, 0x000003a5, + 0x00000314, 0x00000301, 0x000003a5, 0x00000314, + 0x00000342, 0x000003c9, 0x00000313, 0x000003c9, + 0x00000314, 0x000003c9, 0x00000313, 0x00000300, + 0x000003c9, 0x00000314, 0x00000300, 0x000003c9, + 0x00000313, 0x00000301, 0x000003c9, 0x00000314, + 0x00000301, 0x000003c9, 0x00000313, 0x00000342, + 0x000003c9, 0x00000314, 0x00000342, 0x000003a9, + 0x00000313, 0x000003a9, 0x00000314, 0x000003a9, + 0x00000313, 0x00000300, 0x000003a9, 0x00000314, + 0x00000300, 0x000003a9, 0x00000313, 0x00000301, + 0x000003a9, 0x00000314, 0x00000301, 0x000003a9, + 0x00000313, 0x00000342, 0x000003a9, 0x00000314, + 0x00000342, 0x000003b1, 0x00000300, 0x000003b1, + 0x00000301, 0x000003b5, 0x00000300, 0x000003b5, + 0x00000301, 0x000003b7, 0x00000300, 0x000003b7, + 0x00000301, 0x000003b9, 0x00000300, 0x000003b9, + 0x00000301, 0x000003bf, 0x00000300, 0x000003bf, + 0x00000301, 0x000003c5, 0x00000300, 0x000003c5, + 0x00000301, 0x000003c9, 0x00000300, 0x000003c9, + 0x00000301, 0x000003b1, 0x00000313, 0x00000345, + 0x000003b1, 0x00000314, 0x00000345, 0x000003b1, + 0x00000313, 0x00000300, 0x00000345, 0x000003b1, + 0x00000314, 0x00000300, 0x00000345, 0x000003b1, + 0x00000313, 0x00000301, 0x00000345, 0x000003b1, + 0x00000314, 0x00000301, 0x00000345, 0x000003b1, + 0x00000313, 0x00000342, 0x00000345, 0x000003b1, + 0x00000314, 0x00000342, 0x00000345, 0x00000391, + 0x00000313, 0x00000345, 0x00000391, 0x00000314, + 0x00000345, 0x00000391, 0x00000313, 0x00000300, + 0x00000345, 0x00000391, 0x00000314, 0x00000300, + 0x00000345, 0x00000391, 0x00000313, 0x00000301, + 0x00000345, 0x00000391, 0x00000314, 0x00000301, + 0x00000345, 0x00000391, 0x00000313, 0x00000342, + 0x00000345, 0x00000391, 0x00000314, 0x00000342, + 0x00000345, 0x000003b7, 0x00000313, 0x00000345, + 0x000003b7, 0x00000314, 0x00000345, 0x000003b7, + 0x00000313, 0x00000300, 0x00000345, 0x000003b7, + 0x00000314, 0x00000300, 0x00000345, 0x000003b7, + 0x00000313, 0x00000301, 0x00000345, 0x000003b7, + 0x00000314, 0x00000301, 0x00000345, 0x000003b7, + 0x00000313, 0x00000342, 0x00000345, 0x000003b7, + 0x00000314, 0x00000342, 0x00000345, 0x00000397, + 0x00000313, 0x00000345, 0x00000397, 0x00000314, + 0x00000345, 0x00000397, 0x00000313, 0x00000300, + 0x00000345, 0x00000397, 0x00000314, 0x00000300, + 0x00000345, 0x00000397, 0x00000313, 0x00000301, + 0x00000345, 0x00000397, 0x00000314, 0x00000301, + 0x00000345, 0x00000397, 0x00000313, 0x00000342, + 0x00000345, 0x00000397, 0x00000314, 0x00000342, + 0x00000345, 0x000003c9, 0x00000313, 0x00000345, + 0x000003c9, 0x00000314, 0x00000345, 0x000003c9, + 0x00000313, 0x00000300, 0x00000345, 0x000003c9, + 0x00000314, 0x00000300, 0x00000345, 0x000003c9, + 0x00000313, 0x00000301, 0x00000345, 0x000003c9, + 0x00000314, 0x00000301, 0x00000345, 0x000003c9, + 0x00000313, 0x00000342, 0x00000345, 0x000003c9, + 0x00000314, 0x00000342, 0x00000345, 0x000003a9, + 0x00000313, 0x00000345, 0x000003a9, 0x00000314, + 0x00000345, 0x000003a9, 0x00000313, 0x00000300, + 0x00000345, 0x000003a9, 0x00000314, 0x00000300, + 0x00000345, 0x000003a9, 0x00000313, 0x00000301, + 0x00000345, 0x000003a9, 0x00000314, 0x00000301, + 0x00000345, 0x000003a9, 0x00000313, 0x00000342, + 0x00000345, 0x000003a9, 0x00000314, 0x00000342, + 0x00000345, 0x000003b1, 0x00000306, 0x000003b1, + 0x00000304, 0x000003b1, 0x00000300, 0x00000345, + 0x000003b1, 0x00000345, 0x000003b1, 0x00000301, + 0x00000345, 0x000003b1, 0x00000342, 0x000003b1, + 0x00000342, 0x00000345, 0x00000391, 0x00000306, + 0x00000391, 0x00000304, 0x00000391, 0x00000300, + 0x00000391, 0x00000301, 0x00000391, 0x00000345, + 0x00000020, 0x00000313, 0x000003b9, 0x00000020, + 0x00000313, 0x00000020, 0x00000342, 0x00000020, + 0x00000308, 0x00000342, 0x000003b7, 0x00000300, + 0x00000345, 0x000003b7, 0x00000345, 0x000003b7, + 0x00000301, 0x00000345, 0x000003b7, 0x00000342, + 0x000003b7, 0x00000342, 0x00000345, 0x00000395, + 0x00000300, 0x00000395, 0x00000301, 0x00000397, + 0x00000300, 0x00000397, 0x00000301, 0x00000397, + 0x00000345, 0x00000020, 0x00000313, 0x00000300, + 0x00000020, 0x00000313, 0x00000301, 0x00000020, + 0x00000313, 0x00000342, 0x000003b9, 0x00000306, + 0x000003b9, 0x00000304, 0x000003b9, 0x00000308, + 0x00000300, 0x000003b9, 0x00000308, 0x00000301, + 0x000003b9, 0x00000342, 0x000003b9, 0x00000308, + 0x00000342, 0x00000399, 0x00000306, 0x00000399, + 0x00000304, 0x00000399, 0x00000300, 0x00000399, + 0x00000301, 0x00000020, 0x00000314, 0x00000300, + 0x00000020, 0x00000314, 0x00000301, 0x00000020, + 0x00000314, 0x00000342, 0x000003c5, 0x00000306, + 0x000003c5, 0x00000304, 0x000003c5, 0x00000308, + 0x00000300, 0x000003c5, 0x00000308, 0x00000301, + 0x000003c1, 0x00000313, 0x000003c1, 0x00000314, + 0x000003c5, 0x00000342, 0x000003c5, 0x00000308, + 0x00000342, 0x000003a5, 0x00000306, 0x000003a5, + 0x00000304, 0x000003a5, 0x00000300, 0x000003a5, + 0x00000301, 0x000003a1, 0x00000314, 0x00000020, + 0x00000308, 0x00000300, 0x00000020, 0x00000308, + 0x00000301, 0x00000060, 0x000003c9, 0x00000300, + 0x00000345, 0x000003c9, 0x00000345, 0x000003c9, + 0x00000301, 0x00000345, 0x000003c9, 0x00000342, + 0x000003c9, 0x00000342, 0x00000345, 0x0000039f, + 0x00000300, 0x0000039f, 0x00000301, 0x000003a9, + 0x00000300, 0x000003a9, 0x00000301, 0x000003a9, + 0x00000345, 0x00000020, 0x00000301, 0x00000020, + 0x00000314, 0x00000020, 0x00000020, 0x00000020, + 0x00000020, 0x00000020, 0x00000020, 0x00000020, + 0x00000020, 0x00000020, 0x00000020, 0x00000020, + 0x00002010, 0x00000020, 0x00000333, 0x0000002e, + 0x0000002e, 0x0000002e, 0x0000002e, 0x0000002e, + 0x0000002e, 0x00000020, 0x00002032, 0x00002032, + 0x00002032, 0x00002032, 0x00002032, 0x00002035, + 0x00002035, 0x00002035, 0x00002035, 0x00002035, + 0x00000021, 0x00000021, 0x00000020, 0x00000305, + 0x0000003f, 0x0000003f, 0x0000003f, 0x00000021, + 0x00000021, 0x0000003f, 0x00002032, 0x00002032, + 0x00002032, 0x00002032, 0x00000020, 0x00000030, + 0x00000069, 0x00000034, 0x00000035, 0x00000036, + 0x00000037, 0x00000038, 0x00000039, 0x0000002b, + 0x00002212, 0x0000003d, 0x00000028, 0x00000029, + 0x0000006e, 0x00000030, 0x00000031, 0x00000032, + 0x00000033, 0x00000034, 0x00000035, 0x00000036, + 0x00000037, 0x00000038, 0x00000039, 0x0000002b, + 0x00002212, 0x0000003d, 0x00000028, 0x00000029, + 0x00000052, 0x00000073, 0x00000061, 0x0000002f, + 0x00000063, 0x00000061, 0x0000002f, 0x00000073, + 0x00000043, 0x000000b0, 0x00000043, 0x00000063, + 0x0000002f, 0x0000006f, 0x00000063, 0x0000002f, + 0x00000075, 0x00000190, 0x000000b0, 0x00000046, + 0x00000067, 0x00000048, 0x00000048, 0x00000048, + 0x00000068, 0x00000127, 0x00000049, 0x00000049, + 0x0000004c, 0x0000006c, 0x0000004e, 0x0000004e, + 0x0000006f, 0x00000050, 0x00000051, 0x00000052, + 0x00000052, 0x00000052, 0x00000053, 0x0000004d, + 0x00000054, 0x00000045, 0x0000004c, 0x00000054, + 0x0000004d, 0x0000005a, 0x000003a9, 0x0000005a, + 0x0000004b, 0x00000041, 0x0000030a, 0x00000042, + 0x00000043, 0x00000065, 0x00000045, 0x00000046, + 0x0000004d, 0x0000006f, 0x000005d0, 0x000005d1, + 0x000005d2, 0x000005d3, 0x00000069, 0x000003b3, + 0x00000393, 0x000003a0, 0x00002211, 0x00000044, + 0x00000064, 0x00000065, 0x00000069, 0x0000006a, + 0x00000031, 0x00002044, 0x00000033, 0x00000032, + 0x00002044, 0x00000033, 0x00000031, 0x00002044, + 0x00000035, 0x00000032, 0x00002044, 0x00000035, + 0x00000033, 0x00002044, 0x00000035, 0x00000034, + 0x00002044, 0x00000035, 0x00000031, 0x00002044, + 0x00000036, 0x00000035, 0x00002044, 0x00000036, + 0x00000031, 0x00002044, 0x00000038, 0x00000033, + 0x00002044, 0x00000038, 0x00000035, 0x00002044, + 0x00000038, 0x00000037, 0x00002044, 0x00000038, + 0x00000031, 0x00002044, 0x00000049, 0x00000049, + 0x00000049, 0x00000049, 0x00000049, 0x00000049, + 0x00000049, 0x00000056, 0x00000056, 0x00000056, + 0x00000049, 0x00000056, 0x00000049, 0x00000049, + 0x00000056, 0x00000049, 0x00000049, 0x00000049, + 0x00000049, 0x00000058, 0x00000058, 0x00000058, + 0x00000049, 0x00000058, 0x00000049, 0x00000049, + 0x0000004c, 0x00000043, 0x00000044, 0x0000004d, + 0x00000069, 0x00000069, 0x00000069, 0x00000069, + 0x00000069, 0x00000069, 0x00000069, 0x00000076, + 0x00000076, 0x00000076, 0x00000069, 0x00000076, + 0x00000069, 0x00000069, 0x00000076, 0x00000069, + 0x00000069, 0x00000069, 0x00000069, 0x00000078, + 0x00000078, 0x00000078, 0x00000069, 0x00000078, + 0x00000069, 0x00000069, 0x0000006c, 0x00000063, + 0x00000064, 0x0000006d, 0x00002190, 0x00000338, + 0x00002192, 0x00000338, 0x00002194, 0x00000338, + 0x000021d0, 0x00000338, 0x000021d4, 0x00000338, + 0x000021d2, 0x00000338, 0x00002203, 0x00000338, + 0x00002208, 0x00000338, 0x0000220b, 0x00000338, + 0x00002223, 0x00000338, 0x00002225, 0x00000338, + 0x0000222b, 0x0000222b, 0x0000222b, 0x0000222b, + 0x0000222b, 0x0000222e, 0x0000222e, 0x0000222e, + 0x0000222e, 0x0000222e, 0x0000223c, 0x00000338, + 0x00002243, 0x00000338, 0x00002245, 0x00000338, + 0x00002248, 0x00000338, 0x0000003d, 0x00000338, + 0x00002261, 0x00000338, 0x0000224d, 0x00000338, + 0x0000003c, 0x00000338, 0x0000003e, 0x00000338, + 0x00002264, 0x00000338, 0x00002265, 0x00000338, + 0x00002272, 0x00000338, 0x00002273, 0x00000338, + 0x00002276, 0x00000338, 0x00002277, 0x00000338, + 0x0000227a, 0x00000338, 0x0000227b, 0x00000338, + 0x00002282, 0x00000338, 0x00002283, 0x00000338, + 0x00002286, 0x00000338, 0x00002287, 0x00000338, + 0x000022a2, 0x00000338, 0x000022a8, 0x00000338, + 0x000022a9, 0x00000338, 0x000022ab, 0x00000338, + 0x0000227c, 0x00000338, 0x0000227d, 0x00000338, + 0x00002291, 0x00000338, 0x00002292, 0x00000338, + 0x000022b2, 0x00000338, 0x000022b3, 0x00000338, + 0x000022b4, 0x00000338, 0x000022b5, 0x00000338, + 0x00003008, 0x00003009, 0x00000031, 0x00000032, + 0x00000033, 0x00000034, 0x00000035, 0x00000036, + 0x00000037, 0x00000038, 0x00000039, 0x00000031, + 0x00000030, 0x00000031, 0x00000031, 0x00000031, + 0x00000032, 0x00000031, 0x00000033, 0x00000031, + 0x00000034, 0x00000031, 0x00000035, 0x00000031, + 0x00000036, 0x00000031, 0x00000037, 0x00000031, + 0x00000038, 0x00000031, 0x00000039, 0x00000032, + 0x00000030, 0x00000028, 0x00000031, 0x00000029, + 0x00000028, 0x00000032, 0x00000029, 0x00000028, + 0x00000033, 0x00000029, 0x00000028, 0x00000034, + 0x00000029, 0x00000028, 0x00000035, 0x00000029, + 0x00000028, 0x00000036, 0x00000029, 0x00000028, + 0x00000037, 0x00000029, 0x00000028, 0x00000038, + 0x00000029, 0x00000028, 0x00000039, 0x00000029, + 0x00000028, 0x00000031, 0x00000030, 0x00000029, + 0x00000028, 0x00000031, 0x00000031, 0x00000029, + 0x00000028, 0x00000031, 0x00000032, 0x00000029, + 0x00000028, 0x00000031, 0x00000033, 0x00000029, + 0x00000028, 0x00000031, 0x00000034, 0x00000029, + 0x00000028, 0x00000031, 0x00000035, 0x00000029, + 0x00000028, 0x00000031, 0x00000036, 0x00000029, + 0x00000028, 0x00000031, 0x00000037, 0x00000029, + 0x00000028, 0x00000031, 0x00000038, 0x00000029, + 0x00000028, 0x00000031, 0x00000039, 0x00000029, + 0x00000028, 0x00000032, 0x00000030, 0x00000029, + 0x00000031, 0x0000002e, 0x00000032, 0x0000002e, + 0x00000033, 0x0000002e, 0x00000034, 0x0000002e, + 0x00000035, 0x0000002e, 0x00000036, 0x0000002e, + 0x00000037, 0x0000002e, 0x00000038, 0x0000002e, + 0x00000039, 0x0000002e, 0x00000031, 0x00000030, + 0x0000002e, 0x00000031, 0x00000031, 0x0000002e, + 0x00000031, 0x00000032, 0x0000002e, 0x00000031, + 0x00000033, 0x0000002e, 0x00000031, 0x00000034, + 0x0000002e, 0x00000031, 0x00000035, 0x0000002e, + 0x00000031, 0x00000036, 0x0000002e, 0x00000031, + 0x00000037, 0x0000002e, 0x00000031, 0x00000038, + 0x0000002e, 0x00000031, 0x00000039, 0x0000002e, + 0x00000032, 0x00000030, 0x0000002e, 0x00000028, + 0x00000061, 0x00000029, 0x00000028, 0x00000062, + 0x00000029, 0x00000028, 0x00000063, 0x00000029, + 0x00000028, 0x00000064, 0x00000029, 0x00000028, + 0x00000065, 0x00000029, 0x00000028, 0x00000066, + 0x00000029, 0x00000028, 0x00000067, 0x00000029, + 0x00000028, 0x00000068, 0x00000029, 0x00000028, + 0x00000069, 0x00000029, 0x00000028, 0x0000006a, + 0x00000029, 0x00000028, 0x0000006b, 0x00000029, + 0x00000028, 0x0000006c, 0x00000029, 0x00000028, + 0x0000006d, 0x00000029, 0x00000028, 0x0000006e, + 0x00000029, 0x00000028, 0x0000006f, 0x00000029, + 0x00000028, 0x00000070, 0x00000029, 0x00000028, + 0x00000071, 0x00000029, 0x00000028, 0x00000072, + 0x00000029, 0x00000028, 0x00000073, 0x00000029, + 0x00000028, 0x00000074, 0x00000029, 0x00000028, + 0x00000075, 0x00000029, 0x00000028, 0x00000076, + 0x00000029, 0x00000028, 0x00000077, 0x00000029, + 0x00000028, 0x00000078, 0x00000029, 0x00000028, + 0x00000079, 0x00000029, 0x00000028, 0x0000007a, + 0x00000029, 0x00000041, 0x00000042, 0x00000043, + 0x00000044, 0x00000045, 0x00000046, 0x00000047, + 0x00000048, 0x00000049, 0x0000004a, 0x0000004b, + 0x0000004c, 0x0000004d, 0x0000004e, 0x0000004f, + 0x00000050, 0x00000051, 0x00000052, 0x00000053, + 0x00000054, 0x00000055, 0x00000056, 0x00000057, + 0x00000058, 0x00000059, 0x0000005a, 0x00000061, + 0x00000062, 0x00000063, 0x00000064, 0x00000065, + 0x00000066, 0x00000067, 0x00000068, 0x00000069, + 0x0000006a, 0x0000006b, 0x0000006c, 0x0000006d, + 0x0000006e, 0x0000006f, 0x00000070, 0x00000071, + 0x00000072, 0x00000073, 0x00000074, 0x00000075, + 0x00000076, 0x00000077, 0x00000078, 0x00000079, + 0x0000007a, 0x00000030, 0x0000222b, 0x0000222b, + 0x0000222b, 0x0000222b, 0x0000003a, 0x0000003a, + 0x0000003d, 0x0000003d, 0x0000003d, 0x0000003d, + 0x0000003d, 0x0000003d, 0x00002add, 0x00000338, + 0x00006bcd, 0x00009f9f, 0x00004e00, 0x00004e28, + 0x00004e36, 0x00004e3f, 0x00004e59, 0x00004e85, + 0x00004e8c, 0x00004ea0, 0x00004eba, 0x0000513f, + 0x00005165, 0x0000516b, 0x00005182, 0x00005196, + 0x000051ab, 0x000051e0, 0x000051f5, 0x00005200, + 0x0000529b, 0x000052f9, 0x00005315, 0x0000531a, + 0x00005338, 0x00005341, 0x0000535c, 0x00005369, + 0x00005382, 0x000053b6, 0x000053c8, 0x000053e3, + 0x000056d7, 0x0000571f, 0x000058eb, 0x00005902, + 0x0000590a, 0x00005915, 0x00005927, 0x00005973, + 0x00005b50, 0x00005b80, 0x00005bf8, 0x00005c0f, + 0x00005c22, 0x00005c38, 0x00005c6e, 0x00005c71, + 0x00005ddb, 0x00005de5, 0x00005df1, 0x00005dfe, + 0x00005e72, 0x00005e7a, 0x00005e7f, 0x00005ef4, + 0x00005efe, 0x00005f0b, 0x00005f13, 0x00005f50, + 0x00005f61, 0x00005f73, 0x00005fc3, 0x00006208, + 0x00006236, 0x0000624b, 0x0000652f, 0x00006534, + 0x00006587, 0x00006597, 0x000065a4, 0x000065b9, + 0x000065e0, 0x000065e5, 0x000066f0, 0x00006708, + 0x00006728, 0x00006b20, 0x00006b62, 0x00006b79, + 0x00006bb3, 0x00006bcb, 0x00006bd4, 0x00006bdb, + 0x00006c0f, 0x00006c14, 0x00006c34, 0x0000706b, + 0x0000722a, 0x00007236, 0x0000723b, 0x0000723f, + 0x00007247, 0x00007259, 0x0000725b, 0x000072ac, + 0x00007384, 0x00007389, 0x000074dc, 0x000074e6, + 0x00007518, 0x0000751f, 0x00007528, 0x00007530, + 0x0000758b, 0x00007592, 0x00007676, 0x0000767d, + 0x000076ae, 0x000076bf, 0x000076ee, 0x000077db, + 0x000077e2, 0x000077f3, 0x0000793a, 0x000079b8, + 0x000079be, 0x00007a74, 0x00007acb, 0x00007af9, + 0x00007c73, 0x00007cf8, 0x00007f36, 0x00007f51, + 0x00007f8a, 0x00007fbd, 0x00008001, 0x0000800c, + 0x00008012, 0x00008033, 0x0000807f, 0x00008089, + 0x000081e3, 0x000081ea, 0x000081f3, 0x000081fc, + 0x0000820c, 0x0000821b, 0x0000821f, 0x0000826e, + 0x00008272, 0x00008278, 0x0000864d, 0x0000866b, + 0x00008840, 0x0000884c, 0x00008863, 0x0000897e, + 0x0000898b, 0x000089d2, 0x00008a00, 0x00008c37, + 0x00008c46, 0x00008c55, 0x00008c78, 0x00008c9d, + 0x00008d64, 0x00008d70, 0x00008db3, 0x00008eab, + 0x00008eca, 0x00008f9b, 0x00008fb0, 0x00008fb5, + 0x00009091, 0x00009149, 0x000091c6, 0x000091cc, + 0x000091d1, 0x00009577, 0x00009580, 0x0000961c, + 0x000096b6, 0x000096b9, 0x000096e8, 0x00009751, + 0x0000975e, 0x00009762, 0x00009769, 0x000097cb, + 0x000097ed, 0x000097f3, 0x00009801, 0x000098a8, + 0x000098db, 0x000098df, 0x00009996, 0x00009999, + 0x000099ac, 0x00009aa8, 0x00009ad8, 0x00009adf, + 0x00009b25, 0x00009b2f, 0x00009b32, 0x00009b3c, + 0x00009b5a, 0x00009ce5, 0x00009e75, 0x00009e7f, + 0x00009ea5, 0x00009ebb, 0x00009ec3, 0x00009ecd, + 0x00009ed1, 0x00009ef9, 0x00009efd, 0x00009f0e, + 0x00009f13, 0x00009f20, 0x00009f3b, 0x00009f4a, + 0x00009f52, 0x00009f8d, 0x00009f9c, 0x00009fa0, + 0x00000020, 0x00003012, 0x00005341, 0x00005344, + 0x00005345, 0x0000304b, 0x00003099, 0x0000304d, + 0x00003099, 0x0000304f, 0x00003099, 0x00003051, + 0x00003099, 0x00003053, 0x00003099, 0x00003055, + 0x00003099, 0x00003057, 0x00003099, 0x00003059, + 0x00003099, 0x0000305b, 0x00003099, 0x0000305d, + 0x00003099, 0x0000305f, 0x00003099, 0x00003061, + 0x00003099, 0x00003064, 0x00003099, 0x00003066, + 0x00003099, 0x00003068, 0x00003099, 0x0000306f, + 0x00003099, 0x0000306f, 0x0000309a, 0x00003072, + 0x00003099, 0x00003072, 0x0000309a, 0x00003075, + 0x00003099, 0x00003075, 0x0000309a, 0x00003078, + 0x00003099, 0x00003078, 0x0000309a, 0x0000307b, + 0x00003099, 0x0000307b, 0x0000309a, 0x00003046, + 0x00003099, 0x00000020, 0x00003099, 0x00000020, + 0x0000309a, 0x0000309d, 0x00003099, 0x00003088, + 0x0000308a, 0x000030ab, 0x00003099, 0x000030ad, + 0x00003099, 0x000030af, 0x00003099, 0x000030b1, + 0x00003099, 0x000030b3, 0x00003099, 0x000030b5, + 0x00003099, 0x000030b7, 0x00003099, 0x000030b9, + 0x00003099, 0x000030bb, 0x00003099, 0x000030bd, + 0x00003099, 0x000030bf, 0x00003099, 0x000030c1, + 0x00003099, 0x000030c4, 0x00003099, 0x000030c6, + 0x00003099, 0x000030c8, 0x00003099, 0x000030cf, + 0x00003099, 0x000030cf, 0x0000309a, 0x000030d2, + 0x00003099, 0x000030d2, 0x0000309a, 0x000030d5, + 0x00003099, 0x000030d5, 0x0000309a, 0x000030d8, + 0x00003099, 0x000030d8, 0x0000309a, 0x000030db, + 0x00003099, 0x000030db, 0x0000309a, 0x000030a6, + 0x00003099, 0x000030ef, 0x00003099, 0x000030f0, + 0x00003099, 0x000030f1, 0x00003099, 0x000030f2, + 0x00003099, 0x000030fd, 0x00003099, 0x000030b3, + 0x000030c8, 0x00001100, 0x00001101, 0x000011aa, + 0x00001102, 0x000011ac, 0x000011ad, 0x00001103, + 0x00001104, 0x00001105, 0x000011b0, 0x000011b1, + 0x000011b2, 0x000011b3, 0x000011b4, 0x000011b5, + 0x0000111a, 0x00001106, 0x00001107, 0x00001108, + 0x00001121, 0x00001109, 0x0000110a, 0x0000110b, + 0x0000110c, 0x0000110d, 0x0000110e, 0x0000110f, + 0x00001110, 0x00001111, 0x00001112, 0x00001161, + 0x00001162, 0x00001163, 0x00001164, 0x00001165, + 0x00001166, 0x00001167, 0x00001168, 0x00001169, + 0x0000116a, 0x0000116b, 0x0000116c, 0x0000116d, + 0x0000116e, 0x0000116f, 0x00001170, 0x00001171, + 0x00001172, 0x00001173, 0x00001174, 0x00001175, + 0x00001160, 0x00001114, 0x00001115, 0x000011c7, + 0x000011c8, 0x000011cc, 0x000011ce, 0x000011d3, + 0x000011d7, 0x000011d9, 0x0000111c, 0x000011dd, + 0x000011df, 0x0000111d, 0x0000111e, 0x00001120, + 0x00001122, 0x00001123, 0x00001127, 0x00001129, + 0x0000112b, 0x0000112c, 0x0000112d, 0x0000112e, + 0x0000112f, 0x00001132, 0x00001136, 0x00001140, + 0x00001147, 0x0000114c, 0x000011f1, 0x000011f2, + 0x00001157, 0x00001158, 0x00001159, 0x00001184, + 0x00001185, 0x00001188, 0x00001191, 0x00001192, + 0x00001194, 0x0000119e, 0x000011a1, 0x00004e00, + 0x00004e8c, 0x00004e09, 0x000056db, 0x00004e0a, + 0x00004e2d, 0x00004e0b, 0x00007532, 0x00004e59, + 0x00004e19, 0x00004e01, 0x00005929, 0x00005730, + 0x00004eba, 0x00000028, 0x00001100, 0x00000029, + 0x00000028, 0x00001102, 0x00000029, 0x00000028, + 0x00001103, 0x00000029, 0x00000028, 0x00001105, + 0x00000029, 0x00000028, 0x00001106, 0x00000029, + 0x00000028, 0x00001107, 0x00000029, 0x00000028, + 0x00001109, 0x00000029, 0x00000028, 0x0000110b, + 0x00000029, 0x00000028, 0x0000110c, 0x00000029, + 0x00000028, 0x0000110e, 0x00000029, 0x00000028, + 0x0000110f, 0x00000029, 0x00000028, 0x00001110, + 0x00000029, 0x00000028, 0x00001111, 0x00000029, + 0x00000028, 0x00001112, 0x00000029, 0x00000028, + 0x00001100, 0x00001161, 0x00000029, 0x00000028, + 0x00001102, 0x00001161, 0x00000029, 0x00000028, + 0x00001103, 0x00001161, 0x00000029, 0x00000028, + 0x00001105, 0x00001161, 0x00000029, 0x00000028, + 0x00001106, 0x00001161, 0x00000029, 0x00000028, + 0x00001107, 0x00001161, 0x00000029, 0x00000028, + 0x00001109, 0x00001161, 0x00000029, 0x00000028, + 0x0000110b, 0x00001161, 0x00000029, 0x00000028, + 0x0000110c, 0x00001161, 0x00000029, 0x00000028, + 0x0000110e, 0x00001161, 0x00000029, 0x00000028, + 0x0000110f, 0x00001161, 0x00000029, 0x00000028, + 0x00001110, 0x00001161, 0x00000029, 0x00000028, + 0x00001111, 0x00001161, 0x00000029, 0x00000028, + 0x00001112, 0x00001161, 0x00000029, 0x00000028, + 0x0000110c, 0x0000116e, 0x00000029, 0x00000028, + 0x00004e00, 0x00000029, 0x00000028, 0x00004e8c, + 0x00000029, 0x00000028, 0x00004e09, 0x00000029, + 0x00000028, 0x000056db, 0x00000029, 0x00000028, + 0x00004e94, 0x00000029, 0x00000028, 0x0000516d, + 0x00000029, 0x00000028, 0x00004e03, 0x00000029, + 0x00000028, 0x0000516b, 0x00000029, 0x00000028, + 0x00004e5d, 0x00000029, 0x00000028, 0x00005341, + 0x00000029, 0x00000028, 0x00006708, 0x00000029, + 0x00000028, 0x0000706b, 0x00000029, 0x00000028, + 0x00006c34, 0x00000029, 0x00000028, 0x00006728, + 0x00000029, 0x00000028, 0x000091d1, 0x00000029, + 0x00000028, 0x0000571f, 0x00000029, 0x00000028, + 0x000065e5, 0x00000029, 0x00000028, 0x0000682a, + 0x00000029, 0x00000028, 0x00006709, 0x00000029, + 0x00000028, 0x0000793e, 0x00000029, 0x00000028, + 0x0000540d, 0x00000029, 0x00000028, 0x00007279, + 0x00000029, 0x00000028, 0x00008ca1, 0x00000029, + 0x00000028, 0x0000795d, 0x00000029, 0x00000028, + 0x000052b4, 0x00000029, 0x00000028, 0x00004ee3, + 0x00000029, 0x00000028, 0x0000547c, 0x00000029, + 0x00000028, 0x00005b66, 0x00000029, 0x00000028, + 0x000076e3, 0x00000029, 0x00000028, 0x00004f01, + 0x00000029, 0x00000028, 0x00008cc7, 0x00000029, + 0x00000028, 0x00005354, 0x00000029, 0x00000028, + 0x0000796d, 0x00000029, 0x00000028, 0x00004f11, + 0x00000029, 0x00000028, 0x000081ea, 0x00000029, + 0x00000028, 0x000081f3, 0x00000029, 0x00000032, + 0x00000031, 0x00000032, 0x00000032, 0x00000032, + 0x00000033, 0x00000032, 0x00000034, 0x00000032, + 0x00000035, 0x00000032, 0x00000036, 0x00000032, + 0x00000037, 0x00000032, 0x00000038, 0x00000032, + 0x00000039, 0x00000033, 0x00000030, 0x00000033, + 0x00000031, 0x00000033, 0x00000032, 0x00000033, + 0x00000033, 0x00000033, 0x00000034, 0x00000033, + 0x00000035, 0x00001100, 0x00001102, 0x00001103, + 0x00001105, 0x00001106, 0x00001107, 0x00001109, + 0x0000110b, 0x0000110c, 0x0000110e, 0x0000110f, + 0x00001110, 0x00001111, 0x00001112, 0x00001100, + 0x00001161, 0x00001102, 0x00001161, 0x00001103, + 0x00001161, 0x00001105, 0x00001161, 0x00001106, + 0x00001161, 0x00001107, 0x00001161, 0x00001109, + 0x00001161, 0x0000110b, 0x00001161, 0x0000110c, + 0x00001161, 0x0000110e, 0x00001161, 0x0000110f, + 0x00001161, 0x00001110, 0x00001161, 0x00001111, + 0x00001161, 0x00001112, 0x00001161, 0x00004e00, + 0x00004e8c, 0x00004e09, 0x000056db, 0x00004e94, + 0x0000516d, 0x00004e03, 0x0000516b, 0x00004e5d, + 0x00005341, 0x00006708, 0x0000706b, 0x00006c34, + 0x00006728, 0x000091d1, 0x0000571f, 0x000065e5, + 0x0000682a, 0x00006709, 0x0000793e, 0x0000540d, + 0x00007279, 0x00008ca1, 0x0000795d, 0x000052b4, + 0x000079d8, 0x00007537, 0x00005973, 0x00009069, + 0x0000512a, 0x00005370, 0x00006ce8, 0x00009805, + 0x00004f11, 0x00005199, 0x00006b63, 0x00004e0a, + 0x00004e2d, 0x00004e0b, 0x00005de6, 0x000053f3, + 0x0000533b, 0x00005b97, 0x00005b66, 0x000076e3, + 0x00004f01, 0x00008cc7, 0x00005354, 0x0000591c, + 0x00000033, 0x00000036, 0x00000033, 0x00000037, + 0x00000033, 0x00000038, 0x00000033, 0x00000039, + 0x00000034, 0x00000030, 0x00000034, 0x00000031, + 0x00000034, 0x00000032, 0x00000034, 0x00000033, + 0x00000034, 0x00000034, 0x00000034, 0x00000035, + 0x00000034, 0x00000036, 0x00000034, 0x00000037, + 0x00000034, 0x00000038, 0x00000034, 0x00000039, + 0x00000035, 0x00000030, 0x00000031, 0x00006708, + 0x00000032, 0x00006708, 0x00000033, 0x00006708, + 0x00000034, 0x00006708, 0x00000035, 0x00006708, + 0x00000036, 0x00006708, 0x00000037, 0x00006708, + 0x00000038, 0x00006708, 0x00000039, 0x00006708, + 0x00000031, 0x00000030, 0x00006708, 0x00000031, + 0x00000031, 0x00006708, 0x00000031, 0x00000032, + 0x00006708, 0x000030a2, 0x000030a4, 0x000030a6, + 0x000030a8, 0x000030aa, 0x000030ab, 0x000030ad, + 0x000030af, 0x000030b1, 0x000030b3, 0x000030b5, + 0x000030b7, 0x000030b9, 0x000030bb, 0x000030bd, + 0x000030bf, 0x000030c1, 0x000030c4, 0x000030c6, + 0x000030c8, 0x000030ca, 0x000030cb, 0x000030cc, + 0x000030cd, 0x000030ce, 0x000030cf, 0x000030d2, + 0x000030d5, 0x000030d8, 0x000030db, 0x000030de, + 0x000030df, 0x000030e0, 0x000030e1, 0x000030e2, + 0x000030e4, 0x000030e6, 0x000030e8, 0x000030e9, + 0x000030ea, 0x000030eb, 0x000030ec, 0x000030ed, + 0x000030ef, 0x000030f0, 0x000030f1, 0x000030f2, + 0x000030a2, 0x000030cf, 0x0000309a, 0x000030fc, + 0x000030c8, 0x000030a2, 0x000030eb, 0x000030d5, + 0x000030a1, 0x000030a2, 0x000030f3, 0x000030d8, + 0x0000309a, 0x000030a2, 0x000030a2, 0x000030fc, + 0x000030eb, 0x000030a4, 0x000030cb, 0x000030f3, + 0x000030af, 0x00003099, 0x000030a4, 0x000030f3, + 0x000030c1, 0x000030a6, 0x000030a9, 0x000030f3, + 0x000030a8, 0x000030b9, 0x000030af, 0x000030fc, + 0x000030c8, 0x00003099, 0x000030a8, 0x000030fc, + 0x000030ab, 0x000030fc, 0x000030aa, 0x000030f3, + 0x000030b9, 0x000030aa, 0x000030fc, 0x000030e0, + 0x000030ab, 0x000030a4, 0x000030ea, 0x000030ab, + 0x000030e9, 0x000030c3, 0x000030c8, 0x000030ab, + 0x000030ed, 0x000030ea, 0x000030fc, 0x000030ab, + 0x00003099, 0x000030ed, 0x000030f3, 0x000030ab, + 0x00003099, 0x000030f3, 0x000030de, 0x000030ad, + 0x00003099, 0x000030ab, 0x00003099, 0x000030ad, + 0x00003099, 0x000030cb, 0x000030fc, 0x000030ad, + 0x000030e5, 0x000030ea, 0x000030fc, 0x000030ad, + 0x00003099, 0x000030eb, 0x000030bf, 0x00003099, + 0x000030fc, 0x000030ad, 0x000030ed, 0x000030ad, + 0x000030ed, 0x000030af, 0x00003099, 0x000030e9, + 0x000030e0, 0x000030ad, 0x000030ed, 0x000030e1, + 0x000030fc, 0x000030c8, 0x000030eb, 0x000030ad, + 0x000030ed, 0x000030ef, 0x000030c3, 0x000030c8, + 0x000030af, 0x00003099, 0x000030e9, 0x000030e0, + 0x000030af, 0x00003099, 0x000030e9, 0x000030e0, + 0x000030c8, 0x000030f3, 0x000030af, 0x000030eb, + 0x000030bb, 0x00003099, 0x000030a4, 0x000030ed, + 0x000030af, 0x000030ed, 0x000030fc, 0x000030cd, + 0x000030b1, 0x000030fc, 0x000030b9, 0x000030b3, + 0x000030eb, 0x000030ca, 0x000030b3, 0x000030fc, + 0x000030db, 0x0000309a, 0x000030b5, 0x000030a4, + 0x000030af, 0x000030eb, 0x000030b5, 0x000030f3, + 0x000030c1, 0x000030fc, 0x000030e0, 0x000030b7, + 0x000030ea, 0x000030f3, 0x000030af, 0x00003099, + 0x000030bb, 0x000030f3, 0x000030c1, 0x000030bb, + 0x000030f3, 0x000030c8, 0x000030bf, 0x00003099, + 0x000030fc, 0x000030b9, 0x000030c6, 0x00003099, + 0x000030b7, 0x000030c8, 0x00003099, 0x000030eb, + 0x000030c8, 0x000030f3, 0x000030ca, 0x000030ce, + 0x000030ce, 0x000030c3, 0x000030c8, 0x000030cf, + 0x000030a4, 0x000030c4, 0x000030cf, 0x0000309a, + 0x000030fc, 0x000030bb, 0x000030f3, 0x000030c8, + 0x000030cf, 0x0000309a, 0x000030fc, 0x000030c4, + 0x000030cf, 0x00003099, 0x000030fc, 0x000030ec, + 0x000030eb, 0x000030d2, 0x0000309a, 0x000030a2, + 0x000030b9, 0x000030c8, 0x000030eb, 0x000030d2, + 0x0000309a, 0x000030af, 0x000030eb, 0x000030d2, + 0x0000309a, 0x000030b3, 0x000030d2, 0x00003099, + 0x000030eb, 0x000030d5, 0x000030a1, 0x000030e9, + 0x000030c3, 0x000030c8, 0x00003099, 0x000030d5, + 0x000030a3, 0x000030fc, 0x000030c8, 0x000030d5, + 0x00003099, 0x000030c3, 0x000030b7, 0x000030a7, + 0x000030eb, 0x000030d5, 0x000030e9, 0x000030f3, + 0x000030d8, 0x000030af, 0x000030bf, 0x000030fc, + 0x000030eb, 0x000030d8, 0x0000309a, 0x000030bd, + 0x000030d8, 0x0000309a, 0x000030cb, 0x000030d2, + 0x000030d8, 0x000030eb, 0x000030c4, 0x000030d8, + 0x0000309a, 0x000030f3, 0x000030b9, 0x000030d8, + 0x0000309a, 0x000030fc, 0x000030b7, 0x00003099, + 0x000030d8, 0x00003099, 0x000030fc, 0x000030bf, + 0x000030db, 0x0000309a, 0x000030a4, 0x000030f3, + 0x000030c8, 0x000030db, 0x00003099, 0x000030eb, + 0x000030c8, 0x000030db, 0x000030f3, 0x000030db, + 0x0000309a, 0x000030f3, 0x000030c8, 0x00003099, + 0x000030db, 0x000030fc, 0x000030eb, 0x000030db, + 0x000030fc, 0x000030f3, 0x000030de, 0x000030a4, + 0x000030af, 0x000030ed, 0x000030de, 0x000030a4, + 0x000030eb, 0x000030de, 0x000030c3, 0x000030cf, + 0x000030de, 0x000030eb, 0x000030af, 0x000030de, + 0x000030f3, 0x000030b7, 0x000030e7, 0x000030f3, + 0x000030df, 0x000030af, 0x000030ed, 0x000030f3, + 0x000030df, 0x000030ea, 0x000030df, 0x000030ea, + 0x000030cf, 0x00003099, 0x000030fc, 0x000030eb, + 0x000030e1, 0x000030ab, 0x00003099, 0x000030e1, + 0x000030ab, 0x00003099, 0x000030c8, 0x000030f3, + 0x000030e1, 0x000030fc, 0x000030c8, 0x000030eb, + 0x000030e4, 0x000030fc, 0x000030c8, 0x00003099, + 0x000030e4, 0x000030fc, 0x000030eb, 0x000030e6, + 0x000030a2, 0x000030f3, 0x000030ea, 0x000030c3, + 0x000030c8, 0x000030eb, 0x000030ea, 0x000030e9, + 0x000030eb, 0x000030d2, 0x0000309a, 0x000030fc, + 0x000030eb, 0x000030fc, 0x000030d5, 0x00003099, + 0x000030eb, 0x000030ec, 0x000030e0, 0x000030ec, + 0x000030f3, 0x000030c8, 0x000030b1, 0x00003099, + 0x000030f3, 0x000030ef, 0x000030c3, 0x000030c8, + 0x00000030, 0x000070b9, 0x00000031, 0x000070b9, + 0x00000032, 0x000070b9, 0x00000033, 0x000070b9, + 0x00000034, 0x000070b9, 0x00000035, 0x000070b9, + 0x00000036, 0x000070b9, 0x00000037, 0x000070b9, + 0x00000038, 0x000070b9, 0x00000039, 0x000070b9, + 0x00000031, 0x00000030, 0x000070b9, 0x00000031, + 0x00000031, 0x000070b9, 0x00000031, 0x00000032, + 0x000070b9, 0x00000031, 0x00000033, 0x000070b9, + 0x00000031, 0x00000034, 0x000070b9, 0x00000031, + 0x00000035, 0x000070b9, 0x00000031, 0x00000036, + 0x000070b9, 0x00000031, 0x00000037, 0x000070b9, + 0x00000031, 0x00000038, 0x000070b9, 0x00000031, + 0x00000039, 0x000070b9, 0x00000032, 0x00000030, + 0x000070b9, 0x00000032, 0x00000031, 0x000070b9, + 0x00000032, 0x00000032, 0x000070b9, 0x00000032, + 0x00000033, 0x000070b9, 0x00000032, 0x00000034, + 0x000070b9, 0x00000068, 0x00000050, 0x00000061, + 0x00000064, 0x00000061, 0x00000041, 0x00000055, + 0x00000062, 0x00000061, 0x00000072, 0x0000006f, + 0x00000056, 0x00000070, 0x00000063, 0x00005e73, + 0x00006210, 0x0000662d, 0x0000548c, 0x00005927, + 0x00006b63, 0x0000660e, 0x00006cbb, 0x0000682a, + 0x00005f0f, 0x00004f1a, 0x0000793e, 0x00000070, + 0x00000041, 0x0000006e, 0x00000041, 0x000003bc, + 0x00000041, 0x0000006d, 0x00000041, 0x0000006b, + 0x00000041, 0x0000004b, 0x00000042, 0x0000004d, + 0x00000042, 0x00000047, 0x00000042, 0x00000063, + 0x00000061, 0x0000006c, 0x0000006b, 0x00000063, + 0x00000061, 0x0000006c, 0x00000070, 0x00000046, + 0x0000006e, 0x00000046, 0x000003bc, 0x00000046, + 0x000003bc, 0x00000067, 0x0000006d, 0x00000067, + 0x0000006b, 0x00000067, 0x00000048, 0x0000007a, + 0x0000006b, 0x00000048, 0x0000007a, 0x0000004d, + 0x00000048, 0x0000007a, 0x00000047, 0x00000048, + 0x0000007a, 0x00000054, 0x00000048, 0x0000007a, + 0x000003bc, 0x0000006c, 0x0000006d, 0x0000006c, + 0x00000064, 0x0000006c, 0x0000006b, 0x0000006c, + 0x00000066, 0x0000006d, 0x0000006e, 0x0000006d, + 0x000003bc, 0x0000006d, 0x0000006d, 0x0000006d, + 0x00000063, 0x0000006d, 0x0000006b, 0x0000006d, + 0x0000006d, 0x0000006d, 0x00000032, 0x00000063, + 0x0000006d, 0x00000032, 0x0000006d, 0x00000032, + 0x0000006b, 0x0000006d, 0x00000032, 0x0000006d, + 0x0000006d, 0x00000033, 0x00000063, 0x0000006d, + 0x00000033, 0x0000006d, 0x00000033, 0x0000006b, + 0x0000006d, 0x00000033, 0x0000006d, 0x00002215, + 0x00000073, 0x0000006d, 0x00002215, 0x00000073, + 0x00000032, 0x00000050, 0x00000061, 0x0000006b, + 0x00000050, 0x00000061, 0x0000004d, 0x00000050, + 0x00000061, 0x00000047, 0x00000050, 0x00000061, + 0x00000072, 0x00000061, 0x00000064, 0x00000072, + 0x00000061, 0x00000064, 0x00002215, 0x00000073, + 0x00000072, 0x00000061, 0x00000064, 0x00002215, + 0x00000073, 0x00000032, 0x00000070, 0x00000073, + 0x0000006e, 0x00000073, 0x000003bc, 0x00000073, + 0x0000006d, 0x00000073, 0x00000070, 0x00000056, + 0x0000006e, 0x00000056, 0x000003bc, 0x00000056, + 0x0000006d, 0x00000056, 0x0000006b, 0x00000056, + 0x0000004d, 0x00000056, 0x00000070, 0x00000057, + 0x0000006e, 0x00000057, 0x000003bc, 0x00000057, + 0x0000006d, 0x00000057, 0x0000006b, 0x00000057, + 0x0000004d, 0x00000057, 0x0000006b, 0x000003a9, + 0x0000004d, 0x000003a9, 0x00000061, 0x0000002e, + 0x0000006d, 0x0000002e, 0x00000042, 0x00000071, + 0x00000063, 0x00000063, 0x00000063, 0x00000064, + 0x00000043, 0x00002215, 0x0000006b, 0x00000067, + 0x00000043, 0x0000006f, 0x0000002e, 0x00000064, + 0x00000042, 0x00000047, 0x00000079, 0x00000068, + 0x00000061, 0x00000048, 0x00000050, 0x00000069, + 0x0000006e, 0x0000004b, 0x0000004b, 0x0000004b, + 0x0000004d, 0x0000006b, 0x00000074, 0x0000006c, + 0x0000006d, 0x0000006c, 0x0000006e, 0x0000006c, + 0x0000006f, 0x00000067, 0x0000006c, 0x00000078, + 0x0000006d, 0x00000062, 0x0000006d, 0x00000069, + 0x0000006c, 0x0000006d, 0x0000006f, 0x0000006c, + 0x00000050, 0x00000048, 0x00000070, 0x0000002e, + 0x0000006d, 0x0000002e, 0x00000050, 0x00000050, + 0x0000004d, 0x00000050, 0x00000052, 0x00000073, + 0x00000072, 0x00000053, 0x00000076, 0x00000057, + 0x00000062, 0x00000031, 0x000065e5, 0x00000032, + 0x000065e5, 0x00000033, 0x000065e5, 0x00000034, + 0x000065e5, 0x00000035, 0x000065e5, 0x00000036, + 0x000065e5, 0x00000037, 0x000065e5, 0x00000038, + 0x000065e5, 0x00000039, 0x000065e5, 0x00000031, + 0x00000030, 0x000065e5, 0x00000031, 0x00000031, + 0x000065e5, 0x00000031, 0x00000032, 0x000065e5, + 0x00000031, 0x00000033, 0x000065e5, 0x00000031, + 0x00000034, 0x000065e5, 0x00000031, 0x00000035, + 0x000065e5, 0x00000031, 0x00000036, 0x000065e5, + 0x00000031, 0x00000037, 0x000065e5, 0x00000031, + 0x00000038, 0x000065e5, 0x00000031, 0x00000039, + 0x000065e5, 0x00000032, 0x00000030, 0x000065e5, + 0x00000032, 0x00000031, 0x000065e5, 0x00000032, + 0x00000032, 0x000065e5, 0x00000032, 0x00000033, + 0x000065e5, 0x00000032, 0x00000034, 0x000065e5, + 0x00000032, 0x00000035, 0x000065e5, 0x00000032, + 0x00000036, 0x000065e5, 0x00000032, 0x00000037, + 0x000065e5, 0x00000032, 0x00000038, 0x000065e5, + 0x00000032, 0x00000039, 0x000065e5, 0x00000033, + 0x00000030, 0x000065e5, 0x00000033, 0x00000031, + 0x000065e5, 0x00008eca, 0x00008cc8, 0x00006ed1, + 0x00004e32, 0x000053e5, 0x00009f9c, 0x00009f9c, + 0x00005951, 0x000091d1, 0x00005587, 0x00005948, + 0x000061f6, 0x00007669, 0x00007f85, 0x0000863f, + 0x000087ba, 0x000088f8, 0x0000908f, 0x00006a02, + 0x00006d1b, 0x000070d9, 0x000073de, 0x0000843d, + 0x0000916a, 0x000099f1, 0x00004e82, 0x00005375, + 0x00006b04, 0x0000721b, 0x0000862d, 0x00009e1e, + 0x00005d50, 0x00006feb, 0x000085cd, 0x00008964, + 0x000062c9, 0x000081d8, 0x0000881f, 0x00005eca, + 0x00006717, 0x00006d6a, 0x000072fc, 0x000090ce, + 0x00004f86, 0x000051b7, 0x000052de, 0x000064c4, + 0x00006ad3, 0x00007210, 0x000076e7, 0x00008001, + 0x00008606, 0x0000865c, 0x00008def, 0x00009732, + 0x00009b6f, 0x00009dfa, 0x0000788c, 0x0000797f, + 0x00007da0, 0x000083c9, 0x00009304, 0x00009e7f, + 0x00008ad6, 0x000058df, 0x00005f04, 0x00007c60, + 0x0000807e, 0x00007262, 0x000078ca, 0x00008cc2, + 0x000096f7, 0x000058d8, 0x00005c62, 0x00006a13, + 0x00006dda, 0x00006f0f, 0x00007d2f, 0x00007e37, + 0x0000964b, 0x000052d2, 0x0000808b, 0x000051dc, + 0x000051cc, 0x00007a1c, 0x00007dbe, 0x000083f1, + 0x00009675, 0x00008b80, 0x000062cf, 0x00006a02, + 0x00008afe, 0x00004e39, 0x00005be7, 0x00006012, + 0x00007387, 0x00007570, 0x00005317, 0x000078fb, + 0x00004fbf, 0x00005fa9, 0x00004e0d, 0x00006ccc, + 0x00006578, 0x00007d22, 0x000053c3, 0x0000585e, + 0x00007701, 0x00008449, 0x00008aaa, 0x00006bba, + 0x00008fb0, 0x00006c88, 0x000062fe, 0x000082e5, + 0x000063a0, 0x00007565, 0x00004eae, 0x00005169, + 0x000051c9, 0x00006881, 0x00007ce7, 0x0000826f, + 0x00008ad2, 0x000091cf, 0x000052f5, 0x00005442, + 0x00005973, 0x00005eec, 0x000065c5, 0x00006ffe, + 0x0000792a, 0x000095ad, 0x00009a6a, 0x00009e97, + 0x00009ece, 0x0000529b, 0x000066c6, 0x00006b77, + 0x00008f62, 0x00005e74, 0x00006190, 0x00006200, + 0x0000649a, 0x00006f23, 0x00007149, 0x00007489, + 0x000079ca, 0x00007df4, 0x0000806f, 0x00008f26, + 0x000084ee, 0x00009023, 0x0000934a, 0x00005217, + 0x000052a3, 0x000054bd, 0x000070c8, 0x000088c2, + 0x00008aaa, 0x00005ec9, 0x00005ff5, 0x0000637b, + 0x00006bae, 0x00007c3e, 0x00007375, 0x00004ee4, + 0x000056f9, 0x00005be7, 0x00005dba, 0x0000601c, + 0x000073b2, 0x00007469, 0x00007f9a, 0x00008046, + 0x00009234, 0x000096f6, 0x00009748, 0x00009818, + 0x00004f8b, 0x000079ae, 0x000091b4, 0x000096b8, + 0x000060e1, 0x00004e86, 0x000050da, 0x00005bee, + 0x00005c3f, 0x00006599, 0x00006a02, 0x000071ce, + 0x00007642, 0x000084fc, 0x0000907c, 0x00009f8d, + 0x00006688, 0x0000962e, 0x00005289, 0x0000677b, + 0x000067f3, 0x00006d41, 0x00006e9c, 0x00007409, + 0x00007559, 0x0000786b, 0x00007d10, 0x0000985e, + 0x0000516d, 0x0000622e, 0x00009678, 0x0000502b, + 0x00005d19, 0x00006dea, 0x00008f2a, 0x00005f8b, + 0x00006144, 0x00006817, 0x00007387, 0x00009686, + 0x00005229, 0x0000540f, 0x00005c65, 0x00006613, + 0x0000674e, 0x000068a8, 0x00006ce5, 0x00007406, + 0x000075e2, 0x00007f79, 0x000088cf, 0x000088e1, + 0x000091cc, 0x000096e2, 0x0000533f, 0x00006eba, + 0x0000541d, 0x000071d0, 0x00007498, 0x000085fa, + 0x000096a3, 0x00009c57, 0x00009e9f, 0x00006797, + 0x00006dcb, 0x000081e8, 0x00007acb, 0x00007b20, + 0x00007c92, 0x000072c0, 0x00007099, 0x00008b58, + 0x00004ec0, 0x00008336, 0x0000523a, 0x00005207, + 0x00005ea6, 0x000062d3, 0x00007cd6, 0x00005b85, + 0x00006d1e, 0x000066b4, 0x00008f3b, 0x0000884c, + 0x0000964d, 0x0000898b, 0x00005ed3, 0x00005140, + 0x000055c0, 0x0000585a, 0x00006674, 0x000051de, + 0x0000732a, 0x000076ca, 0x0000793c, 0x0000795e, + 0x00007965, 0x0000798f, 0x00009756, 0x00007cbe, + 0x00007fbd, 0x00008612, 0x00008af8, 0x00009038, + 0x000090fd, 0x000098ef, 0x000098fc, 0x00009928, + 0x00009db4, 0x00004fae, 0x000050e7, 0x0000514d, + 0x000052c9, 0x000052e4, 0x00005351, 0x0000559d, + 0x00005606, 0x00005668, 0x00005840, 0x000058a8, + 0x00005c64, 0x00005c6e, 0x00006094, 0x00006168, + 0x0000618e, 0x000061f2, 0x0000654f, 0x000065e2, + 0x00006691, 0x00006885, 0x00006d77, 0x00006e1a, + 0x00006f22, 0x0000716e, 0x0000722b, 0x00007422, + 0x00007891, 0x0000793e, 0x00007949, 0x00007948, + 0x00007950, 0x00007956, 0x0000795d, 0x0000798d, + 0x0000798e, 0x00007a40, 0x00007a81, 0x00007bc0, + 0x00007df4, 0x00007e09, 0x00007e41, 0x00007f72, + 0x00008005, 0x000081ed, 0x00008279, 0x00008279, + 0x00008457, 0x00008910, 0x00008996, 0x00008b01, + 0x00008b39, 0x00008cd3, 0x00008d08, 0x00008fb6, + 0x00009038, 0x000096e3, 0x000097ff, 0x0000983b, + 0x00000066, 0x00000066, 0x00000066, 0x00000069, + 0x00000066, 0x0000006c, 0x00000066, 0x00000066, + 0x00000069, 0x00000066, 0x00000066, 0x0000006c, + 0x00000073, 0x00000074, 0x00000073, 0x00000074, + 0x00000574, 0x00000576, 0x00000574, 0x00000565, + 0x00000574, 0x0000056b, 0x0000057e, 0x00000576, + 0x00000574, 0x0000056d, 0x000005d9, 0x000005b4, + 0x000005f2, 0x000005b7, 0x000005e2, 0x000005d0, + 0x000005d3, 0x000005d4, 0x000005db, 0x000005dc, + 0x000005dd, 0x000005e8, 0x000005ea, 0x0000002b, + 0x000005e9, 0x000005c1, 0x000005e9, 0x000005c2, + 0x000005e9, 0x000005bc, 0x000005c1, 0x000005e9, + 0x000005bc, 0x000005c2, 0x000005d0, 0x000005b7, + 0x000005d0, 0x000005b8, 0x000005d0, 0x000005bc, + 0x000005d1, 0x000005bc, 0x000005d2, 0x000005bc, + 0x000005d3, 0x000005bc, 0x000005d4, 0x000005bc, + 0x000005d5, 0x000005bc, 0x000005d6, 0x000005bc, + 0x000005d8, 0x000005bc, 0x000005d9, 0x000005bc, + 0x000005da, 0x000005bc, 0x000005db, 0x000005bc, + 0x000005dc, 0x000005bc, 0x000005de, 0x000005bc, + 0x000005e0, 0x000005bc, 0x000005e1, 0x000005bc, + 0x000005e3, 0x000005bc, 0x000005e4, 0x000005bc, + 0x000005e6, 0x000005bc, 0x000005e7, 0x000005bc, + 0x000005e8, 0x000005bc, 0x000005e9, 0x000005bc, + 0x000005ea, 0x000005bc, 0x000005d5, 0x000005b9, + 0x000005d1, 0x000005bf, 0x000005db, 0x000005bf, + 0x000005e4, 0x000005bf, 0x000005d0, 0x000005dc, + 0x00000671, 0x00000671, 0x0000067b, 0x0000067b, + 0x0000067b, 0x0000067b, 0x0000067e, 0x0000067e, + 0x0000067e, 0x0000067e, 0x00000680, 0x00000680, + 0x00000680, 0x00000680, 0x0000067a, 0x0000067a, + 0x0000067a, 0x0000067a, 0x0000067f, 0x0000067f, + 0x0000067f, 0x0000067f, 0x00000679, 0x00000679, + 0x00000679, 0x00000679, 0x000006a4, 0x000006a4, + 0x000006a4, 0x000006a4, 0x000006a6, 0x000006a6, + 0x000006a6, 0x000006a6, 0x00000684, 0x00000684, + 0x00000684, 0x00000684, 0x00000683, 0x00000683, + 0x00000683, 0x00000683, 0x00000686, 0x00000686, + 0x00000686, 0x00000686, 0x00000687, 0x00000687, + 0x00000687, 0x00000687, 0x0000068d, 0x0000068d, + 0x0000068c, 0x0000068c, 0x0000068e, 0x0000068e, + 0x00000688, 0x00000688, 0x00000698, 0x00000698, + 0x00000691, 0x00000691, 0x000006a9, 0x000006a9, + 0x000006a9, 0x000006a9, 0x000006af, 0x000006af, + 0x000006af, 0x000006af, 0x000006b3, 0x000006b3, + 0x000006b3, 0x000006b3, 0x000006b1, 0x000006b1, + 0x000006b1, 0x000006b1, 0x000006ba, 0x000006ba, + 0x000006bb, 0x000006bb, 0x000006bb, 0x000006bb, + 0x000006d5, 0x00000654, 0x000006d5, 0x00000654, + 0x000006c1, 0x000006c1, 0x000006c1, 0x000006c1, + 0x000006be, 0x000006be, 0x000006be, 0x000006be, + 0x000006d2, 0x000006d2, 0x000006d2, 0x00000654, + 0x000006d2, 0x00000654, 0x000006ad, 0x000006ad, + 0x000006ad, 0x000006ad, 0x000006c7, 0x000006c7, + 0x000006c6, 0x000006c6, 0x000006c8, 0x000006c8, + 0x000006c7, 0x00000674, 0x000006cb, 0x000006cb, + 0x000006c5, 0x000006c5, 0x000006c9, 0x000006c9, + 0x000006d0, 0x000006d0, 0x000006d0, 0x000006d0, + 0x00000649, 0x00000649, 0x0000064a, 0x00000654, + 0x00000627, 0x0000064a, 0x00000654, 0x00000627, + 0x0000064a, 0x00000654, 0x000006d5, 0x0000064a, + 0x00000654, 0x000006d5, 0x0000064a, 0x00000654, + 0x00000648, 0x0000064a, 0x00000654, 0x00000648, + 0x0000064a, 0x00000654, 0x000006c7, 0x0000064a, + 0x00000654, 0x000006c7, 0x0000064a, 0x00000654, + 0x000006c6, 0x0000064a, 0x00000654, 0x000006c6, + 0x0000064a, 0x00000654, 0x000006c8, 0x0000064a, + 0x00000654, 0x000006c8, 0x0000064a, 0x00000654, + 0x000006d0, 0x0000064a, 0x00000654, 0x000006d0, + 0x0000064a, 0x00000654, 0x000006d0, 0x0000064a, + 0x00000654, 0x00000649, 0x0000064a, 0x00000654, + 0x00000649, 0x0000064a, 0x00000654, 0x00000649, + 0x000006cc, 0x000006cc, 0x000006cc, 0x000006cc, + 0x0000064a, 0x00000654, 0x0000062c, 0x0000064a, + 0x00000654, 0x0000062d, 0x0000064a, 0x00000654, + 0x00000645, 0x0000064a, 0x00000654, 0x00000649, + 0x0000064a, 0x00000654, 0x0000064a, 0x00000628, + 0x0000062c, 0x00000628, 0x0000062d, 0x00000628, + 0x0000062e, 0x00000628, 0x00000645, 0x00000628, + 0x00000649, 0x00000628, 0x0000064a, 0x0000062a, + 0x0000062c, 0x0000062a, 0x0000062d, 0x0000062a, + 0x0000062e, 0x0000062a, 0x00000645, 0x0000062a, + 0x00000649, 0x0000062a, 0x0000064a, 0x0000062b, + 0x0000062c, 0x0000062b, 0x00000645, 0x0000062b, + 0x00000649, 0x0000062b, 0x0000064a, 0x0000062c, + 0x0000062d, 0x0000062c, 0x00000645, 0x0000062d, + 0x0000062c, 0x0000062d, 0x00000645, 0x0000062e, + 0x0000062c, 0x0000062e, 0x0000062d, 0x0000062e, + 0x00000645, 0x00000633, 0x0000062c, 0x00000633, + 0x0000062d, 0x00000633, 0x0000062e, 0x00000633, + 0x00000645, 0x00000635, 0x0000062d, 0x00000635, + 0x00000645, 0x00000636, 0x0000062c, 0x00000636, + 0x0000062d, 0x00000636, 0x0000062e, 0x00000636, + 0x00000645, 0x00000637, 0x0000062d, 0x00000637, + 0x00000645, 0x00000638, 0x00000645, 0x00000639, + 0x0000062c, 0x00000639, 0x00000645, 0x0000063a, + 0x0000062c, 0x0000063a, 0x00000645, 0x00000641, + 0x0000062c, 0x00000641, 0x0000062d, 0x00000641, + 0x0000062e, 0x00000641, 0x00000645, 0x00000641, + 0x00000649, 0x00000641, 0x0000064a, 0x00000642, + 0x0000062d, 0x00000642, 0x00000645, 0x00000642, + 0x00000649, 0x00000642, 0x0000064a, 0x00000643, + 0x00000627, 0x00000643, 0x0000062c, 0x00000643, + 0x0000062d, 0x00000643, 0x0000062e, 0x00000643, + 0x00000644, 0x00000643, 0x00000645, 0x00000643, + 0x00000649, 0x00000643, 0x0000064a, 0x00000644, + 0x0000062c, 0x00000644, 0x0000062d, 0x00000644, + 0x0000062e, 0x00000644, 0x00000645, 0x00000644, + 0x00000649, 0x00000644, 0x0000064a, 0x00000645, + 0x0000062c, 0x00000645, 0x0000062d, 0x00000645, + 0x0000062e, 0x00000645, 0x00000645, 0x00000645, + 0x00000649, 0x00000645, 0x0000064a, 0x00000646, + 0x0000062c, 0x00000646, 0x0000062d, 0x00000646, + 0x0000062e, 0x00000646, 0x00000645, 0x00000646, + 0x00000649, 0x00000646, 0x0000064a, 0x00000647, + 0x0000062c, 0x00000647, 0x00000645, 0x00000647, + 0x00000649, 0x00000647, 0x0000064a, 0x0000064a, + 0x0000062c, 0x0000064a, 0x0000062d, 0x0000064a, + 0x0000062e, 0x0000064a, 0x00000645, 0x0000064a, + 0x00000649, 0x0000064a, 0x0000064a, 0x00000630, + 0x00000670, 0x00000631, 0x00000670, 0x00000649, + 0x00000670, 0x00000020, 0x0000064c, 0x00000651, + 0x00000020, 0x0000064d, 0x00000651, 0x00000020, + 0x0000064e, 0x00000651, 0x00000020, 0x0000064f, + 0x00000651, 0x00000020, 0x00000650, 0x00000651, + 0x00000020, 0x00000651, 0x00000670, 0x0000064a, + 0x00000654, 0x00000631, 0x0000064a, 0x00000654, + 0x00000632, 0x0000064a, 0x00000654, 0x00000645, + 0x0000064a, 0x00000654, 0x00000646, 0x0000064a, + 0x00000654, 0x00000649, 0x0000064a, 0x00000654, + 0x0000064a, 0x00000628, 0x00000631, 0x00000628, + 0x00000632, 0x00000628, 0x00000645, 0x00000628, + 0x00000646, 0x00000628, 0x00000649, 0x00000628, + 0x0000064a, 0x0000062a, 0x00000631, 0x0000062a, + 0x00000632, 0x0000062a, 0x00000645, 0x0000062a, + 0x00000646, 0x0000062a, 0x00000649, 0x0000062a, + 0x0000064a, 0x0000062b, 0x00000631, 0x0000062b, + 0x00000632, 0x0000062b, 0x00000645, 0x0000062b, + 0x00000646, 0x0000062b, 0x00000649, 0x0000062b, + 0x0000064a, 0x00000641, 0x00000649, 0x00000641, + 0x0000064a, 0x00000642, 0x00000649, 0x00000642, + 0x0000064a, 0x00000643, 0x00000627, 0x00000643, + 0x00000644, 0x00000643, 0x00000645, 0x00000643, + 0x00000649, 0x00000643, 0x0000064a, 0x00000644, + 0x00000645, 0x00000644, 0x00000649, 0x00000644, + 0x0000064a, 0x00000645, 0x00000627, 0x00000645, + 0x00000645, 0x00000646, 0x00000631, 0x00000646, + 0x00000632, 0x00000646, 0x00000645, 0x00000646, + 0x00000646, 0x00000646, 0x00000649, 0x00000646, + 0x0000064a, 0x00000649, 0x00000670, 0x0000064a, + 0x00000631, 0x0000064a, 0x00000632, 0x0000064a, + 0x00000645, 0x0000064a, 0x00000646, 0x0000064a, + 0x00000649, 0x0000064a, 0x0000064a, 0x0000064a, + 0x00000654, 0x0000062c, 0x0000064a, 0x00000654, + 0x0000062d, 0x0000064a, 0x00000654, 0x0000062e, + 0x0000064a, 0x00000654, 0x00000645, 0x0000064a, + 0x00000654, 0x00000647, 0x00000628, 0x0000062c, + 0x00000628, 0x0000062d, 0x00000628, 0x0000062e, + 0x00000628, 0x00000645, 0x00000628, 0x00000647, + 0x0000062a, 0x0000062c, 0x0000062a, 0x0000062d, + 0x0000062a, 0x0000062e, 0x0000062a, 0x00000645, + 0x0000062a, 0x00000647, 0x0000062b, 0x00000645, + 0x0000062c, 0x0000062d, 0x0000062c, 0x00000645, + 0x0000062d, 0x0000062c, 0x0000062d, 0x00000645, + 0x0000062e, 0x0000062c, 0x0000062e, 0x00000645, + 0x00000633, 0x0000062c, 0x00000633, 0x0000062d, + 0x00000633, 0x0000062e, 0x00000633, 0x00000645, + 0x00000635, 0x0000062d, 0x00000635, 0x0000062e, + 0x00000635, 0x00000645, 0x00000636, 0x0000062c, + 0x00000636, 0x0000062d, 0x00000636, 0x0000062e, + 0x00000636, 0x00000645, 0x00000637, 0x0000062d, + 0x00000638, 0x00000645, 0x00000639, 0x0000062c, + 0x00000639, 0x00000645, 0x0000063a, 0x0000062c, + 0x0000063a, 0x00000645, 0x00000641, 0x0000062c, + 0x00000641, 0x0000062d, 0x00000641, 0x0000062e, + 0x00000641, 0x00000645, 0x00000642, 0x0000062d, + 0x00000642, 0x00000645, 0x00000643, 0x0000062c, + 0x00000643, 0x0000062d, 0x00000643, 0x0000062e, + 0x00000643, 0x00000644, 0x00000643, 0x00000645, + 0x00000644, 0x0000062c, 0x00000644, 0x0000062d, + 0x00000644, 0x0000062e, 0x00000644, 0x00000645, + 0x00000644, 0x00000647, 0x00000645, 0x0000062c, + 0x00000645, 0x0000062d, 0x00000645, 0x0000062e, + 0x00000645, 0x00000645, 0x00000646, 0x0000062c, + 0x00000646, 0x0000062d, 0x00000646, 0x0000062e, + 0x00000646, 0x00000645, 0x00000646, 0x00000647, + 0x00000647, 0x0000062c, 0x00000647, 0x00000645, + 0x00000647, 0x00000670, 0x0000064a, 0x0000062c, + 0x0000064a, 0x0000062d, 0x0000064a, 0x0000062e, + 0x0000064a, 0x00000645, 0x0000064a, 0x00000647, + 0x0000064a, 0x00000654, 0x00000645, 0x0000064a, + 0x00000654, 0x00000647, 0x00000628, 0x00000645, + 0x00000628, 0x00000647, 0x0000062a, 0x00000645, + 0x0000062a, 0x00000647, 0x0000062b, 0x00000645, + 0x0000062b, 0x00000647, 0x00000633, 0x00000645, + 0x00000633, 0x00000647, 0x00000634, 0x00000645, + 0x00000634, 0x00000647, 0x00000643, 0x00000644, + 0x00000643, 0x00000645, 0x00000644, 0x00000645, + 0x00000646, 0x00000645, 0x00000646, 0x00000647, + 0x0000064a, 0x00000645, 0x0000064a, 0x00000647, + 0x00000640, 0x0000064e, 0x00000651, 0x00000640, + 0x0000064f, 0x00000651, 0x00000640, 0x00000650, + 0x00000651, 0x00000637, 0x00000649, 0x00000637, + 0x0000064a, 0x00000639, 0x00000649, 0x00000639, + 0x0000064a, 0x0000063a, 0x00000649, 0x0000063a, + 0x0000064a, 0x00000633, 0x00000649, 0x00000633, + 0x0000064a, 0x00000634, 0x00000649, 0x00000634, + 0x0000064a, 0x0000062d, 0x00000649, 0x0000062d, + 0x0000064a, 0x0000062c, 0x00000649, 0x0000062c, + 0x0000064a, 0x0000062e, 0x00000649, 0x0000062e, + 0x0000064a, 0x00000635, 0x00000649, 0x00000635, + 0x0000064a, 0x00000636, 0x00000649, 0x00000636, + 0x0000064a, 0x00000634, 0x0000062c, 0x00000634, + 0x0000062d, 0x00000634, 0x0000062e, 0x00000634, + 0x00000645, 0x00000634, 0x00000631, 0x00000633, + 0x00000631, 0x00000635, 0x00000631, 0x00000636, + 0x00000631, 0x00000637, 0x00000649, 0x00000637, + 0x0000064a, 0x00000639, 0x00000649, 0x00000639, + 0x0000064a, 0x0000063a, 0x00000649, 0x0000063a, + 0x0000064a, 0x00000633, 0x00000649, 0x00000633, + 0x0000064a, 0x00000634, 0x00000649, 0x00000634, + 0x0000064a, 0x0000062d, 0x00000649, 0x0000062d, + 0x0000064a, 0x0000062c, 0x00000649, 0x0000062c, + 0x0000064a, 0x0000062e, 0x00000649, 0x0000062e, + 0x0000064a, 0x00000635, 0x00000649, 0x00000635, + 0x0000064a, 0x00000636, 0x00000649, 0x00000636, + 0x0000064a, 0x00000634, 0x0000062c, 0x00000634, + 0x0000062d, 0x00000634, 0x0000062e, 0x00000634, + 0x00000645, 0x00000634, 0x00000631, 0x00000633, + 0x00000631, 0x00000635, 0x00000631, 0x00000636, + 0x00000631, 0x00000634, 0x0000062c, 0x00000634, + 0x0000062d, 0x00000634, 0x0000062e, 0x00000634, + 0x00000645, 0x00000633, 0x00000647, 0x00000634, + 0x00000647, 0x00000637, 0x00000645, 0x00000633, + 0x0000062c, 0x00000633, 0x0000062d, 0x00000633, + 0x0000062e, 0x00000634, 0x0000062c, 0x00000634, + 0x0000062d, 0x00000634, 0x0000062e, 0x00000637, + 0x00000645, 0x00000638, 0x00000645, 0x00000627, + 0x0000064b, 0x00000627, 0x0000064b, 0x0000062a, + 0x0000062c, 0x00000645, 0x0000062a, 0x0000062d, + 0x0000062c, 0x0000062a, 0x0000062d, 0x0000062c, + 0x0000062a, 0x0000062d, 0x00000645, 0x0000062a, + 0x0000062e, 0x00000645, 0x0000062a, 0x00000645, + 0x0000062c, 0x0000062a, 0x00000645, 0x0000062d, + 0x0000062a, 0x00000645, 0x0000062e, 0x0000062c, + 0x00000645, 0x0000062d, 0x0000062c, 0x00000645, + 0x0000062d, 0x0000062d, 0x00000645, 0x0000064a, + 0x0000062d, 0x00000645, 0x00000649, 0x00000633, + 0x0000062d, 0x0000062c, 0x00000633, 0x0000062c, + 0x0000062d, 0x00000633, 0x0000062c, 0x00000649, + 0x00000633, 0x00000645, 0x0000062d, 0x00000633, + 0x00000645, 0x0000062d, 0x00000633, 0x00000645, + 0x0000062c, 0x00000633, 0x00000645, 0x00000645, + 0x00000633, 0x00000645, 0x00000645, 0x00000635, + 0x0000062d, 0x0000062d, 0x00000635, 0x0000062d, + 0x0000062d, 0x00000635, 0x00000645, 0x00000645, + 0x00000634, 0x0000062d, 0x00000645, 0x00000634, + 0x0000062d, 0x00000645, 0x00000634, 0x0000062c, + 0x0000064a, 0x00000634, 0x00000645, 0x0000062e, + 0x00000634, 0x00000645, 0x0000062e, 0x00000634, + 0x00000645, 0x00000645, 0x00000634, 0x00000645, + 0x00000645, 0x00000636, 0x0000062d, 0x00000649, + 0x00000636, 0x0000062e, 0x00000645, 0x00000636, + 0x0000062e, 0x00000645, 0x00000637, 0x00000645, + 0x0000062d, 0x00000637, 0x00000645, 0x0000062d, + 0x00000637, 0x00000645, 0x00000645, 0x00000637, + 0x00000645, 0x0000064a, 0x00000639, 0x0000062c, + 0x00000645, 0x00000639, 0x00000645, 0x00000645, + 0x00000639, 0x00000645, 0x00000645, 0x00000639, + 0x00000645, 0x00000649, 0x0000063a, 0x00000645, + 0x00000645, 0x0000063a, 0x00000645, 0x0000064a, + 0x0000063a, 0x00000645, 0x00000649, 0x00000641, + 0x0000062e, 0x00000645, 0x00000641, 0x0000062e, + 0x00000645, 0x00000642, 0x00000645, 0x0000062d, + 0x00000642, 0x00000645, 0x00000645, 0x00000644, + 0x0000062d, 0x00000645, 0x00000644, 0x0000062d, + 0x0000064a, 0x00000644, 0x0000062d, 0x00000649, + 0x00000644, 0x0000062c, 0x0000062c, 0x00000644, + 0x0000062c, 0x0000062c, 0x00000644, 0x0000062e, + 0x00000645, 0x00000644, 0x0000062e, 0x00000645, + 0x00000644, 0x00000645, 0x0000062d, 0x00000644, + 0x00000645, 0x0000062d, 0x00000645, 0x0000062d, + 0x0000062c, 0x00000645, 0x0000062d, 0x00000645, + 0x00000645, 0x0000062d, 0x0000064a, 0x00000645, + 0x0000062c, 0x0000062d, 0x00000645, 0x0000062c, + 0x00000645, 0x00000645, 0x0000062e, 0x0000062c, + 0x00000645, 0x0000062e, 0x00000645, 0x00000645, + 0x0000062c, 0x0000062e, 0x00000647, 0x00000645, + 0x0000062c, 0x00000647, 0x00000645, 0x00000645, + 0x00000646, 0x0000062d, 0x00000645, 0x00000646, + 0x0000062d, 0x00000649, 0x00000646, 0x0000062c, + 0x00000645, 0x00000646, 0x0000062c, 0x00000645, + 0x00000646, 0x0000062c, 0x00000649, 0x00000646, + 0x00000645, 0x0000064a, 0x00000646, 0x00000645, + 0x00000649, 0x0000064a, 0x00000645, 0x00000645, + 0x0000064a, 0x00000645, 0x00000645, 0x00000628, + 0x0000062e, 0x0000064a, 0x0000062a, 0x0000062c, + 0x0000064a, 0x0000062a, 0x0000062c, 0x00000649, + 0x0000062a, 0x0000062e, 0x0000064a, 0x0000062a, + 0x0000062e, 0x00000649, 0x0000062a, 0x00000645, + 0x0000064a, 0x0000062a, 0x00000645, 0x00000649, + 0x0000062c, 0x00000645, 0x0000064a, 0x0000062c, + 0x0000062d, 0x00000649, 0x0000062c, 0x00000645, + 0x00000649, 0x00000633, 0x0000062e, 0x00000649, + 0x00000635, 0x0000062d, 0x0000064a, 0x00000634, + 0x0000062d, 0x0000064a, 0x00000636, 0x0000062d, + 0x0000064a, 0x00000644, 0x0000062c, 0x0000064a, + 0x00000644, 0x00000645, 0x0000064a, 0x0000064a, + 0x0000062d, 0x0000064a, 0x0000064a, 0x0000062c, + 0x0000064a, 0x0000064a, 0x00000645, 0x0000064a, + 0x00000645, 0x00000645, 0x0000064a, 0x00000642, + 0x00000645, 0x0000064a, 0x00000646, 0x0000062d, + 0x0000064a, 0x00000642, 0x00000645, 0x0000062d, + 0x00000644, 0x0000062d, 0x00000645, 0x00000639, + 0x00000645, 0x0000064a, 0x00000643, 0x00000645, + 0x0000064a, 0x00000646, 0x0000062c, 0x0000062d, + 0x00000645, 0x0000062e, 0x0000064a, 0x00000644, + 0x0000062c, 0x00000645, 0x00000643, 0x00000645, + 0x00000645, 0x00000644, 0x0000062c, 0x00000645, + 0x00000646, 0x0000062c, 0x0000062d, 0x0000062c, + 0x0000062d, 0x0000064a, 0x0000062d, 0x0000062c, + 0x0000064a, 0x00000645, 0x0000062c, 0x0000064a, + 0x00000641, 0x00000645, 0x0000064a, 0x00000628, + 0x0000062d, 0x0000064a, 0x00000643, 0x00000645, + 0x00000645, 0x00000639, 0x0000062c, 0x00000645, + 0x00000635, 0x00000645, 0x00000645, 0x00000633, + 0x0000062e, 0x0000064a, 0x00000646, 0x0000062c, + 0x0000064a, 0x00000635, 0x00000644, 0x000006d2, + 0x00000642, 0x00000644, 0x000006d2, 0x00000627, + 0x00000644, 0x00000644, 0x00000647, 0x00000627, + 0x00000643, 0x00000628, 0x00000631, 0x00000645, + 0x0000062d, 0x00000645, 0x0000062f, 0x00000635, + 0x00000644, 0x00000639, 0x00000645, 0x00000631, + 0x00000633, 0x00000648, 0x00000644, 0x00000639, + 0x00000644, 0x0000064a, 0x00000647, 0x00000648, + 0x00000633, 0x00000644, 0x00000645, 0x00000635, + 0x00000644, 0x00000649, 0x00000635, 0x00000644, + 0x00000649, 0x00000020, 0x00000627, 0x00000644, + 0x00000644, 0x00000647, 0x00000020, 0x00000639, + 0x00000644, 0x0000064a, 0x00000647, 0x00000020, + 0x00000648, 0x00000633, 0x00000644, 0x00000645, + 0x0000062c, 0x00000644, 0x00000020, 0x0000062c, + 0x00000644, 0x00000627, 0x00000644, 0x00000647, + 0x00000631, 0x000006cc, 0x00000627, 0x00000644, + 0x0000002e, 0x0000002e, 0x00002014, 0x00002013, + 0x0000005f, 0x0000005f, 0x00000028, 0x00000029, + 0x0000007b, 0x0000007d, 0x00003014, 0x00003015, + 0x00003010, 0x00003011, 0x0000300a, 0x0000300b, + 0x00003008, 0x00003009, 0x0000300c, 0x0000300d, + 0x0000300e, 0x0000300f, 0x00000020, 0x00000305, + 0x00000020, 0x00000305, 0x00000020, 0x00000305, + 0x00000020, 0x00000305, 0x0000005f, 0x0000005f, + 0x0000005f, 0x0000002c, 0x00003001, 0x0000002e, + 0x0000003b, 0x0000003a, 0x0000003f, 0x00000021, + 0x00002014, 0x00000028, 0x00000029, 0x0000007b, + 0x0000007d, 0x00003014, 0x00003015, 0x00000023, + 0x00000026, 0x0000002a, 0x0000002b, 0x0000002d, + 0x0000003c, 0x0000003e, 0x0000003d, 0x0000005c, + 0x00000024, 0x00000025, 0x00000040, 0x00000020, + 0x0000064b, 0x00000640, 0x0000064b, 0x00000020, + 0x0000064c, 0x00000020, 0x0000064d, 0x00000020, + 0x0000064e, 0x00000640, 0x0000064e, 0x00000020, + 0x0000064f, 0x00000640, 0x0000064f, 0x00000020, + 0x00000650, 0x00000640, 0x00000650, 0x00000020, + 0x00000651, 0x00000640, 0x00000651, 0x00000020, + 0x00000652, 0x00000640, 0x00000652, 0x00000621, + 0x00000627, 0x00000653, 0x00000627, 0x00000653, + 0x00000627, 0x00000654, 0x00000627, 0x00000654, + 0x00000648, 0x00000654, 0x00000648, 0x00000654, + 0x00000627, 0x00000655, 0x00000627, 0x00000655, + 0x0000064a, 0x00000654, 0x0000064a, 0x00000654, + 0x0000064a, 0x00000654, 0x0000064a, 0x00000654, + 0x00000627, 0x00000627, 0x00000628, 0x00000628, + 0x00000628, 0x00000628, 0x00000629, 0x00000629, + 0x0000062a, 0x0000062a, 0x0000062a, 0x0000062a, + 0x0000062b, 0x0000062b, 0x0000062b, 0x0000062b, + 0x0000062c, 0x0000062c, 0x0000062c, 0x0000062c, + 0x0000062d, 0x0000062d, 0x0000062d, 0x0000062d, + 0x0000062e, 0x0000062e, 0x0000062e, 0x0000062e, + 0x0000062f, 0x0000062f, 0x00000630, 0x00000630, + 0x00000631, 0x00000631, 0x00000632, 0x00000632, + 0x00000633, 0x00000633, 0x00000633, 0x00000633, + 0x00000634, 0x00000634, 0x00000634, 0x00000634, + 0x00000635, 0x00000635, 0x00000635, 0x00000635, + 0x00000636, 0x00000636, 0x00000636, 0x00000636, + 0x00000637, 0x00000637, 0x00000637, 0x00000637, + 0x00000638, 0x00000638, 0x00000638, 0x00000638, + 0x00000639, 0x00000639, 0x00000639, 0x00000639, + 0x0000063a, 0x0000063a, 0x0000063a, 0x0000063a, + 0x00000641, 0x00000641, 0x00000641, 0x00000641, + 0x00000642, 0x00000642, 0x00000642, 0x00000642, + 0x00000643, 0x00000643, 0x00000643, 0x00000643, + 0x00000644, 0x00000644, 0x00000644, 0x00000644, + 0x00000645, 0x00000645, 0x00000645, 0x00000645, + 0x00000646, 0x00000646, 0x00000646, 0x00000646, + 0x00000647, 0x00000647, 0x00000647, 0x00000647, + 0x00000648, 0x00000648, 0x00000649, 0x00000649, + 0x0000064a, 0x0000064a, 0x0000064a, 0x0000064a, + 0x00000644, 0x00000627, 0x00000653, 0x00000644, + 0x00000627, 0x00000653, 0x00000644, 0x00000627, + 0x00000654, 0x00000644, 0x00000627, 0x00000654, + 0x00000644, 0x00000627, 0x00000655, 0x00000644, + 0x00000627, 0x00000655, 0x00000644, 0x00000627, + 0x00000644, 0x00000627, 0x00000021, 0x00000022, + 0x00000023, 0x00000024, 0x00000025, 0x00000026, + 0x00000027, 0x00000028, 0x00000029, 0x0000002a, + 0x0000002b, 0x0000002c, 0x0000002d, 0x0000002e, + 0x0000002f, 0x00000030, 0x00000031, 0x00000032, + 0x00000033, 0x00000034, 0x00000035, 0x00000036, + 0x00000037, 0x00000038, 0x00000039, 0x0000003a, + 0x0000003b, 0x0000003c, 0x0000003d, 0x0000003e, + 0x0000003f, 0x00000040, 0x00000041, 0x00000042, + 0x00000043, 0x00000044, 0x00000045, 0x00000046, + 0x00000047, 0x00000048, 0x00000049, 0x0000004a, + 0x0000004b, 0x0000004c, 0x0000004d, 0x0000004e, + 0x0000004f, 0x00000050, 0x00000051, 0x00000052, + 0x00000053, 0x00000054, 0x00000055, 0x00000056, + 0x00000057, 0x00000058, 0x00000059, 0x0000005a, + 0x0000005b, 0x0000005c, 0x0000005d, 0x0000005e, + 0x0000005f, 0x00000060, 0x00000061, 0x00000062, + 0x00000063, 0x00000064, 0x00000065, 0x00000066, + 0x00000067, 0x00000068, 0x00000069, 0x0000006a, + 0x0000006b, 0x0000006c, 0x0000006d, 0x0000006e, + 0x0000006f, 0x00000070, 0x00000071, 0x00000072, + 0x00000073, 0x00000074, 0x00000075, 0x00000076, + 0x00000077, 0x00000078, 0x00000079, 0x0000007a, + 0x0000007b, 0x0000007c, 0x0000007d, 0x0000007e, + 0x00002985, 0x00002986, 0x00003002, 0x0000300c, + 0x0000300d, 0x00003001, 0x000030fb, 0x000030f2, + 0x000030a1, 0x000030a3, 0x000030a5, 0x000030a7, + 0x000030a9, 0x000030e3, 0x000030e5, 0x000030e7, + 0x000030c3, 0x000030fc, 0x000030a2, 0x000030a4, + 0x000030a6, 0x000030a8, 0x000030aa, 0x000030ab, + 0x000030ad, 0x000030af, 0x000030b1, 0x000030b3, + 0x000030b5, 0x000030b7, 0x000030b9, 0x000030bb, + 0x000030bd, 0x000030bf, 0x000030c1, 0x000030c4, + 0x000030c6, 0x000030c8, 0x000030ca, 0x000030cb, + 0x000030cc, 0x000030cd, 0x000030ce, 0x000030cf, + 0x000030d2, 0x000030d5, 0x000030d8, 0x000030db, + 0x000030de, 0x000030df, 0x000030e0, 0x000030e1, + 0x000030e2, 0x000030e4, 0x000030e6, 0x000030e8, + 0x000030e9, 0x000030ea, 0x000030eb, 0x000030ec, + 0x000030ed, 0x000030ef, 0x000030f3, 0x00003099, + 0x0000309a, 0x00001160, 0x00001100, 0x00001101, + 0x000011aa, 0x00001102, 0x000011ac, 0x000011ad, + 0x00001103, 0x00001104, 0x00001105, 0x000011b0, + 0x000011b1, 0x000011b2, 0x000011b3, 0x000011b4, + 0x000011b5, 0x0000111a, 0x00001106, 0x00001107, + 0x00001108, 0x00001121, 0x00001109, 0x0000110a, + 0x0000110b, 0x0000110c, 0x0000110d, 0x0000110e, + 0x0000110f, 0x00001110, 0x00001111, 0x00001112, + 0x00001161, 0x00001162, 0x00001163, 0x00001164, + 0x00001165, 0x00001166, 0x00001167, 0x00001168, + 0x00001169, 0x0000116a, 0x0000116b, 0x0000116c, + 0x0000116d, 0x0000116e, 0x0000116f, 0x00001170, + 0x00001171, 0x00001172, 0x00001173, 0x00001174, + 0x00001175, 0x000000a2, 0x000000a3, 0x000000ac, + 0x00000020, 0x00000304, 0x000000a6, 0x000000a5, + 0x000020a9, 0x00002502, 0x00002190, 0x00002191, + 0x00002192, 0x00002193, 0x000025a0, 0x000025cb, + 0x0001d157, 0x0001d165, 0x0001d158, 0x0001d165, + 0x0001d158, 0x0001d165, 0x0001d16e, 0x0001d158, + 0x0001d165, 0x0001d16f, 0x0001d158, 0x0001d165, + 0x0001d170, 0x0001d158, 0x0001d165, 0x0001d171, + 0x0001d158, 0x0001d165, 0x0001d172, 0x0001d1b9, + 0x0001d165, 0x0001d1ba, 0x0001d165, 0x0001d1b9, + 0x0001d165, 0x0001d16e, 0x0001d1ba, 0x0001d165, + 0x0001d16e, 0x0001d1b9, 0x0001d165, 0x0001d16f, + 0x0001d1ba, 0x0001d165, 0x0001d16f, 0x00000041, + 0x00000042, 0x00000043, 0x00000044, 0x00000045, + 0x00000046, 0x00000047, 0x00000048, 0x00000049, + 0x0000004a, 0x0000004b, 0x0000004c, 0x0000004d, + 0x0000004e, 0x0000004f, 0x00000050, 0x00000051, + 0x00000052, 0x00000053, 0x00000054, 0x00000055, + 0x00000056, 0x00000057, 0x00000058, 0x00000059, + 0x0000005a, 0x00000061, 0x00000062, 0x00000063, + 0x00000064, 0x00000065, 0x00000066, 0x00000067, + 0x00000068, 0x00000069, 0x0000006a, 0x0000006b, + 0x0000006c, 0x0000006d, 0x0000006e, 0x0000006f, + 0x00000070, 0x00000071, 0x00000072, 0x00000073, + 0x00000074, 0x00000075, 0x00000076, 0x00000077, + 0x00000078, 0x00000079, 0x0000007a, 0x00000041, + 0x00000042, 0x00000043, 0x00000044, 0x00000045, + 0x00000046, 0x00000047, 0x00000048, 0x00000049, + 0x0000004a, 0x0000004b, 0x0000004c, 0x0000004d, + 0x0000004e, 0x0000004f, 0x00000050, 0x00000051, + 0x00000052, 0x00000053, 0x00000054, 0x00000055, + 0x00000056, 0x00000057, 0x00000058, 0x00000059, + 0x0000005a, 0x00000061, 0x00000062, 0x00000063, + 0x00000064, 0x00000065, 0x00000066, 0x00000067, + 0x00000069, 0x0000006a, 0x0000006b, 0x0000006c, + 0x0000006d, 0x0000006e, 0x0000006f, 0x00000070, + 0x00000071, 0x00000072, 0x00000073, 0x00000074, + 0x00000075, 0x00000076, 0x00000077, 0x00000078, + 0x00000079, 0x0000007a, 0x00000041, 0x00000042, + 0x00000043, 0x00000044, 0x00000045, 0x00000046, + 0x00000047, 0x00000048, 0x00000049, 0x0000004a, + 0x0000004b, 0x0000004c, 0x0000004d, 0x0000004e, + 0x0000004f, 0x00000050, 0x00000051, 0x00000052, + 0x00000053, 0x00000054, 0x00000055, 0x00000056, + 0x00000057, 0x00000058, 0x00000059, 0x0000005a, + 0x00000061, 0x00000062, 0x00000063, 0x00000064, + 0x00000065, 0x00000066, 0x00000067, 0x00000068, + 0x00000069, 0x0000006a, 0x0000006b, 0x0000006c, + 0x0000006d, 0x0000006e, 0x0000006f, 0x00000070, + 0x00000071, 0x00000072, 0x00000073, 0x00000074, + 0x00000075, 0x00000076, 0x00000077, 0x00000078, + 0x00000079, 0x0000007a, 0x00000041, 0x00000043, + 0x00000044, 0x00000047, 0x0000004a, 0x0000004b, + 0x0000004e, 0x0000004f, 0x00000050, 0x00000051, + 0x00000053, 0x00000054, 0x00000055, 0x00000056, + 0x00000057, 0x00000058, 0x00000059, 0x0000005a, + 0x00000061, 0x00000062, 0x00000063, 0x00000064, + 0x00000066, 0x00000068, 0x00000069, 0x0000006a, + 0x0000006b, 0x0000006d, 0x0000006e, 0x00000070, + 0x00000071, 0x00000072, 0x00000073, 0x00000074, + 0x00000075, 0x00000076, 0x00000077, 0x00000078, + 0x00000079, 0x0000007a, 0x00000041, 0x00000042, + 0x00000043, 0x00000044, 0x00000045, 0x00000046, + 0x00000047, 0x00000048, 0x00000049, 0x0000004a, + 0x0000004b, 0x0000004c, 0x0000004d, 0x0000004e, + 0x0000004f, 0x00000050, 0x00000051, 0x00000052, + 0x00000053, 0x00000054, 0x00000055, 0x00000056, + 0x00000057, 0x00000058, 0x00000059, 0x0000005a, + 0x00000061, 0x00000062, 0x00000063, 0x00000064, + 0x00000065, 0x00000066, 0x00000067, 0x00000068, + 0x00000069, 0x0000006a, 0x0000006b, 0x0000006c, + 0x0000006d, 0x0000006e, 0x0000006f, 0x00000070, + 0x00000071, 0x00000072, 0x00000073, 0x00000074, + 0x00000075, 0x00000076, 0x00000077, 0x00000078, + 0x00000079, 0x0000007a, 0x00000041, 0x00000042, + 0x00000044, 0x00000045, 0x00000046, 0x00000047, + 0x0000004a, 0x0000004b, 0x0000004c, 0x0000004d, + 0x0000004e, 0x0000004f, 0x00000050, 0x00000051, + 0x00000053, 0x00000054, 0x00000055, 0x00000056, + 0x00000057, 0x00000058, 0x00000059, 0x00000061, + 0x00000062, 0x00000063, 0x00000064, 0x00000065, + 0x00000066, 0x00000067, 0x00000068, 0x00000069, + 0x0000006a, 0x0000006b, 0x0000006c, 0x0000006d, + 0x0000006e, 0x0000006f, 0x00000070, 0x00000071, + 0x00000072, 0x00000073, 0x00000074, 0x00000075, + 0x00000076, 0x00000077, 0x00000078, 0x00000079, + 0x0000007a, 0x00000041, 0x00000042, 0x00000044, + 0x00000045, 0x00000046, 0x00000047, 0x00000049, + 0x0000004a, 0x0000004b, 0x0000004c, 0x0000004d, + 0x0000004f, 0x00000053, 0x00000054, 0x00000055, + 0x00000056, 0x00000057, 0x00000058, 0x00000059, + 0x00000061, 0x00000062, 0x00000063, 0x00000064, + 0x00000065, 0x00000066, 0x00000067, 0x00000068, + 0x00000069, 0x0000006a, 0x0000006b, 0x0000006c, + 0x0000006d, 0x0000006e, 0x0000006f, 0x00000070, + 0x00000071, 0x00000072, 0x00000073, 0x00000074, + 0x00000075, 0x00000076, 0x00000077, 0x00000078, + 0x00000079, 0x0000007a, 0x00000041, 0x00000042, + 0x00000043, 0x00000044, 0x00000045, 0x00000046, + 0x00000047, 0x00000048, 0x00000049, 0x0000004a, + 0x0000004b, 0x0000004c, 0x0000004d, 0x0000004e, + 0x0000004f, 0x00000050, 0x00000051, 0x00000052, + 0x00000053, 0x00000054, 0x00000055, 0x00000056, + 0x00000057, 0x00000058, 0x00000059, 0x0000005a, + 0x00000061, 0x00000062, 0x00000063, 0x00000064, + 0x00000065, 0x00000066, 0x00000067, 0x00000068, + 0x00000069, 0x0000006a, 0x0000006b, 0x0000006c, + 0x0000006d, 0x0000006e, 0x0000006f, 0x00000070, + 0x00000071, 0x00000072, 0x00000073, 0x00000074, + 0x00000075, 0x00000076, 0x00000077, 0x00000078, + 0x00000079, 0x0000007a, 0x00000041, 0x00000042, + 0x00000043, 0x00000044, 0x00000045, 0x00000046, + 0x00000047, 0x00000048, 0x00000049, 0x0000004a, + 0x0000004b, 0x0000004c, 0x0000004d, 0x0000004e, + 0x0000004f, 0x00000050, 0x00000051, 0x00000052, + 0x00000053, 0x00000054, 0x00000055, 0x00000056, + 0x00000057, 0x00000058, 0x00000059, 0x0000005a, + 0x00000061, 0x00000062, 0x00000063, 0x00000064, + 0x00000065, 0x00000066, 0x00000067, 0x00000068, + 0x00000069, 0x0000006a, 0x0000006b, 0x0000006c, + 0x0000006d, 0x0000006e, 0x0000006f, 0x00000070, + 0x00000071, 0x00000072, 0x00000073, 0x00000074, + 0x00000075, 0x00000076, 0x00000077, 0x00000078, + 0x00000079, 0x0000007a, 0x00000041, 0x00000042, + 0x00000043, 0x00000044, 0x00000045, 0x00000046, + 0x00000047, 0x00000048, 0x00000049, 0x0000004a, + 0x0000004b, 0x0000004c, 0x0000004d, 0x0000004e, + 0x0000004f, 0x00000050, 0x00000051, 0x00000052, + 0x00000053, 0x00000054, 0x00000055, 0x00000056, + 0x00000057, 0x00000058, 0x00000059, 0x0000005a, + 0x00000061, 0x00000062, 0x00000063, 0x00000064, + 0x00000065, 0x00000066, 0x00000067, 0x00000068, + 0x00000069, 0x0000006a, 0x0000006b, 0x0000006c, + 0x0000006d, 0x0000006e, 0x0000006f, 0x00000070, + 0x00000071, 0x00000072, 0x00000073, 0x00000074, + 0x00000075, 0x00000076, 0x00000077, 0x00000078, + 0x00000079, 0x0000007a, 0x00000041, 0x00000042, + 0x00000043, 0x00000044, 0x00000045, 0x00000046, + 0x00000047, 0x00000048, 0x00000049, 0x0000004a, + 0x0000004b, 0x0000004c, 0x0000004d, 0x0000004e, + 0x0000004f, 0x00000050, 0x00000051, 0x00000052, + 0x00000053, 0x00000054, 0x00000055, 0x00000056, + 0x00000057, 0x00000058, 0x00000059, 0x0000005a, + 0x00000061, 0x00000062, 0x00000063, 0x00000064, + 0x00000065, 0x00000066, 0x00000067, 0x00000068, + 0x00000069, 0x0000006a, 0x0000006b, 0x0000006c, + 0x0000006d, 0x0000006e, 0x0000006f, 0x00000070, + 0x00000071, 0x00000072, 0x00000073, 0x00000074, + 0x00000075, 0x00000076, 0x00000077, 0x00000078, + 0x00000079, 0x0000007a, 0x00000041, 0x00000042, + 0x00000043, 0x00000044, 0x00000045, 0x00000046, + 0x00000047, 0x00000048, 0x00000049, 0x0000004a, + 0x0000004b, 0x0000004c, 0x0000004d, 0x0000004e, + 0x0000004f, 0x00000050, 0x00000051, 0x00000052, + 0x00000053, 0x00000054, 0x00000055, 0x00000056, + 0x00000057, 0x00000058, 0x00000059, 0x0000005a, + 0x00000061, 0x00000062, 0x00000063, 0x00000064, + 0x00000065, 0x00000066, 0x00000067, 0x00000068, + 0x00000069, 0x0000006a, 0x0000006b, 0x0000006c, + 0x0000006d, 0x0000006e, 0x0000006f, 0x00000070, + 0x00000071, 0x00000072, 0x00000073, 0x00000074, + 0x00000075, 0x00000076, 0x00000077, 0x00000078, + 0x00000079, 0x0000007a, 0x00000041, 0x00000042, + 0x00000043, 0x00000044, 0x00000045, 0x00000046, + 0x00000047, 0x00000048, 0x00000049, 0x0000004a, + 0x0000004b, 0x0000004c, 0x0000004d, 0x0000004e, + 0x0000004f, 0x00000050, 0x00000051, 0x00000052, + 0x00000053, 0x00000054, 0x00000055, 0x00000056, + 0x00000057, 0x00000058, 0x00000059, 0x0000005a, + 0x00000061, 0x00000062, 0x00000063, 0x00000064, + 0x00000065, 0x00000066, 0x00000067, 0x00000068, + 0x00000069, 0x0000006a, 0x0000006b, 0x0000006c, + 0x0000006d, 0x0000006e, 0x0000006f, 0x00000070, + 0x00000071, 0x00000072, 0x00000073, 0x00000074, + 0x00000075, 0x00000076, 0x00000077, 0x00000078, + 0x00000079, 0x0000007a, 0x00000391, 0x00000392, + 0x00000393, 0x00000394, 0x00000395, 0x00000396, + 0x00000397, 0x00000398, 0x00000399, 0x0000039a, + 0x0000039b, 0x0000039c, 0x0000039d, 0x0000039e, + 0x0000039f, 0x000003a0, 0x000003a1, 0x00000398, + 0x000003a3, 0x000003a4, 0x000003a5, 0x000003a6, + 0x000003a7, 0x000003a8, 0x000003a9, 0x00002207, + 0x000003b1, 0x000003b2, 0x000003b3, 0x000003b4, + 0x000003b5, 0x000003b6, 0x000003b7, 0x000003b8, + 0x000003b9, 0x000003ba, 0x000003bb, 0x000003bc, + 0x000003bd, 0x000003be, 0x000003bf, 0x000003c0, + 0x000003c1, 0x000003c2, 0x000003c3, 0x000003c4, + 0x000003c5, 0x000003c6, 0x000003c7, 0x000003c8, + 0x000003c9, 0x00002202, 0x000003b5, 0x000003b8, + 0x000003ba, 0x000003c6, 0x000003c1, 0x000003c0, + 0x00000391, 0x00000392, 0x00000393, 0x00000394, + 0x00000395, 0x00000396, 0x00000397, 0x00000398, + 0x00000399, 0x0000039a, 0x0000039b, 0x0000039c, + 0x0000039d, 0x0000039e, 0x0000039f, 0x000003a0, + 0x000003a1, 0x00000398, 0x000003a3, 0x000003a4, + 0x000003a5, 0x000003a6, 0x000003a7, 0x000003a8, + 0x000003a9, 0x00002207, 0x000003b1, 0x000003b2, + 0x000003b3, 0x000003b4, 0x000003b5, 0x000003b6, + 0x000003b7, 0x000003b8, 0x000003b9, 0x000003ba, + 0x000003bb, 0x000003bc, 0x000003bd, 0x000003be, + 0x000003bf, 0x000003c0, 0x000003c1, 0x000003c2, + 0x000003c3, 0x000003c4, 0x000003c5, 0x000003c6, + 0x000003c7, 0x000003c8, 0x000003c9, 0x00002202, + 0x000003b5, 0x000003b8, 0x000003ba, 0x000003c6, + 0x000003c1, 0x000003c0, 0x00000391, 0x00000392, + 0x00000393, 0x00000394, 0x00000395, 0x00000396, + 0x00000397, 0x00000398, 0x00000399, 0x0000039a, + 0x0000039b, 0x0000039c, 0x0000039d, 0x0000039e, + 0x0000039f, 0x000003a0, 0x000003a1, 0x00000398, + 0x000003a3, 0x000003a4, 0x000003a5, 0x000003a6, + 0x000003a7, 0x000003a8, 0x000003a9, 0x00002207, + 0x000003b1, 0x000003b2, 0x000003b3, 0x000003b4, + 0x000003b5, 0x000003b6, 0x000003b7, 0x000003b8, + 0x000003b9, 0x000003ba, 0x000003bb, 0x000003bc, + 0x000003bd, 0x000003be, 0x000003bf, 0x000003c0, + 0x000003c1, 0x000003c2, 0x000003c3, 0x000003c4, + 0x000003c5, 0x000003c6, 0x000003c7, 0x000003c8, + 0x000003c9, 0x00002202, 0x000003b5, 0x000003b8, + 0x000003ba, 0x000003c6, 0x000003c1, 0x000003c0, + 0x00000391, 0x00000392, 0x00000393, 0x00000394, + 0x00000395, 0x00000396, 0x00000397, 0x00000398, + 0x00000399, 0x0000039a, 0x0000039b, 0x0000039c, + 0x0000039d, 0x0000039e, 0x0000039f, 0x000003a0, + 0x000003a1, 0x00000398, 0x000003a3, 0x000003a4, + 0x000003a5, 0x000003a6, 0x000003a7, 0x000003a8, + 0x000003a9, 0x00002207, 0x000003b1, 0x000003b2, + 0x000003b3, 0x000003b4, 0x000003b5, 0x000003b6, + 0x000003b7, 0x000003b8, 0x000003b9, 0x000003ba, + 0x000003bb, 0x000003bc, 0x000003bd, 0x000003be, + 0x000003bf, 0x000003c0, 0x000003c1, 0x000003c2, + 0x000003c3, 0x000003c4, 0x000003c5, 0x000003c6, + 0x000003c7, 0x000003c8, 0x000003c9, 0x00002202, + 0x000003b5, 0x000003b8, 0x000003ba, 0x000003c6, + 0x000003c1, 0x000003c0, 0x00000391, 0x00000392, + 0x00000393, 0x00000394, 0x00000395, 0x00000396, + 0x00000397, 0x00000398, 0x00000399, 0x0000039a, + 0x0000039b, 0x0000039c, 0x0000039d, 0x0000039e, + 0x0000039f, 0x000003a0, 0x000003a1, 0x00000398, + 0x000003a3, 0x000003a4, 0x000003a5, 0x000003a6, + 0x000003a7, 0x000003a8, 0x000003a9, 0x00002207, + 0x000003b1, 0x000003b2, 0x000003b3, 0x000003b4, + 0x000003b5, 0x000003b6, 0x000003b7, 0x000003b8, + 0x000003b9, 0x000003ba, 0x000003bb, 0x000003bc, + 0x000003bd, 0x000003be, 0x000003bf, 0x000003c0, + 0x000003c1, 0x000003c2, 0x000003c3, 0x000003c4, + 0x000003c5, 0x000003c6, 0x000003c7, 0x000003c8, + 0x000003c9, 0x00002202, 0x000003b5, 0x000003b8, + 0x000003ba, 0x000003c6, 0x000003c1, 0x000003c0, + 0x00000030, 0x00000031, 0x00000032, 0x00000033, + 0x00000034, 0x00000035, 0x00000036, 0x00000037, + 0x00000038, 0x00000039, 0x00000030, 0x00000031, + 0x00000032, 0x00000033, 0x00000034, 0x00000035, + 0x00000036, 0x00000037, 0x00000038, 0x00000039, + 0x00000030, 0x00000031, 0x00000032, 0x00000033, + 0x00000034, 0x00000035, 0x00000036, 0x00000037, + 0x00000038, 0x00000039, 0x00000030, 0x00000031, + 0x00000032, 0x00000033, 0x00000034, 0x00000035, + 0x00000036, 0x00000037, 0x00000038, 0x00000039, + 0x00000030, 0x00000031, 0x00000032, 0x00000033, + 0x00000034, 0x00000035, 0x00000036, 0x00000037, + 0x00000038, 0x00000039, 0x00004e3d, 0x00004e38, + 0x00004e41, 0x00020122, 0x00004f60, 0x00004fae, + 0x00004fbb, 0x00005002, 0x0000507a, 0x00005099, + 0x000050e7, 0x000050cf, 0x0000349e, 0x0002063a, + 0x0000514d, 0x00005154, 0x00005164, 0x00005177, + 0x0002051c, 0x000034b9, 0x00005167, 0x0000518d, + 0x0002054b, 0x00005197, 0x000051a4, 0x00004ecc, + 0x000051ac, 0x000051b5, 0x000291df, 0x000051f5, + 0x00005203, 0x000034df, 0x0000523b, 0x00005246, + 0x00005272, 0x00005277, 0x00003515, 0x000052c7, + 0x000052c9, 0x000052e4, 0x000052fa, 0x00005305, + 0x00005306, 0x00005317, 0x00005349, 0x00005351, + 0x0000535a, 0x00005373, 0x0000537d, 0x0000537f, + 0x0000537f, 0x0000537f, 0x00020a2c, 0x00007070, + 0x000053ca, 0x000053df, 0x00020b63, 0x000053eb, + 0x000053f1, 0x00005406, 0x0000549e, 0x00005438, + 0x00005448, 0x00005468, 0x000054a2, 0x000054f6, + 0x00005510, 0x00005553, 0x00005563, 0x00005584, + 0x00005584, 0x00005599, 0x000055ab, 0x000055b3, + 0x000055c2, 0x00005716, 0x00005606, 0x00005717, + 0x00005651, 0x00005674, 0x00005207, 0x000058ee, + 0x000057ce, 0x000057f4, 0x0000580d, 0x0000578b, + 0x00005832, 0x00005831, 0x000058ac, 0x000214e4, + 0x000058f2, 0x000058f7, 0x00005906, 0x0000591a, + 0x00005922, 0x00005962, 0x000216a8, 0x000216ea, + 0x000059ec, 0x00005a1b, 0x00005a27, 0x000059d8, + 0x00005a66, 0x000036ee, 0x0002136a, 0x00005b08, + 0x00005b3e, 0x00005b3e, 0x000219c8, 0x00005bc3, + 0x00005bd8, 0x00005be7, 0x00005bf3, 0x00021b18, + 0x00005bff, 0x00005c06, 0x00005f33, 0x00005c22, + 0x00003781, 0x00005c60, 0x00005c6e, 0x00005cc0, + 0x00005c8d, 0x00021de4, 0x00005d43, 0x00021de6, + 0x00005d6e, 0x00005d6b, 0x00005d7c, 0x00005de1, + 0x00005de2, 0x0000382f, 0x00005dfd, 0x00005e28, + 0x00005e3d, 0x00005e69, 0x00003862, 0x00022183, + 0x0000387c, 0x00005eb0, 0x00005eb3, 0x00005eb6, + 0x00005eca, 0x0002a392, 0x00005efe, 0x00022331, + 0x00022331, 0x00008201, 0x00005f22, 0x00005f22, + 0x000038c7, 0x000232b8, 0x000261da, 0x00005f62, + 0x00005f6b, 0x000038e3, 0x00005f9a, 0x00005fcd, + 0x00005fd7, 0x00005ff9, 0x00006081, 0x0000393a, + 0x0000391c, 0x00006094, 0x000226d4, 0x000060c7, + 0x00006148, 0x0000614c, 0x0000614e, 0x0000614c, + 0x0000617a, 0x0000618e, 0x000061b2, 0x000061a4, + 0x000061af, 0x000061de, 0x000061f2, 0x000061f6, + 0x00006210, 0x0000621b, 0x0000625d, 0x000062b1, + 0x000062d4, 0x00006350, 0x00022b0c, 0x0000633d, + 0x000062fc, 0x00006368, 0x00006383, 0x000063e4, + 0x00022bf1, 0x00006422, 0x000063c5, 0x000063a9, + 0x00003a2e, 0x00006469, 0x0000647e, 0x0000649d, + 0x00006477, 0x00003a6c, 0x0000654f, 0x0000656c, + 0x0002300a, 0x000065e3, 0x000066f8, 0x00006649, + 0x00003b19, 0x00006691, 0x00003b08, 0x00003ae4, + 0x00005192, 0x00005195, 0x00006700, 0x0000669c, + 0x000080ad, 0x000043d9, 0x00006717, 0x0000671b, + 0x00006721, 0x0000675e, 0x00006753, 0x000233c3, + 0x00003b49, 0x000067fa, 0x00006785, 0x00006852, + 0x00006885, 0x0002346d, 0x0000688e, 0x0000681f, + 0x00006914, 0x00003b9d, 0x00006942, 0x000069a3, + 0x000069ea, 0x00006aa8, 0x000236a3, 0x00006adb, + 0x00003c18, 0x00006b21, 0x000238a7, 0x00006b54, + 0x00003c4e, 0x00006b72, 0x00006b9f, 0x00006bba, + 0x00006bbb, 0x00023a8d, 0x00021d0b, 0x00023afa, + 0x00006c4e, 0x00023cbc, 0x00006cbf, 0x00006ccd, + 0x00006c67, 0x00006d16, 0x00006d3e, 0x00006d77, + 0x00006d41, 0x00006d69, 0x00006d78, 0x00006d85, + 0x00023d1e, 0x00006d34, 0x00006e2f, 0x00006e6e, + 0x00003d33, 0x00006ecb, 0x00006ec7, 0x00023ed1, + 0x00006df9, 0x00006f6e, 0x00023f5e, 0x00023f8e, + 0x00006fc6, 0x00007039, 0x0000701e, 0x0000701b, + 0x00003d96, 0x0000704a, 0x0000707d, 0x00007077, + 0x000070ad, 0x00020525, 0x00007145, 0x00024263, + 0x0000719c, 0x000043ab, 0x00007228, 0x00007235, + 0x00007250, 0x00024608, 0x00007280, 0x00007295, + 0x00024735, 0x00024814, 0x0000737a, 0x0000738b, + 0x00003eac, 0x000073a5, 0x00003eb8, 0x00003eb8, + 0x00007447, 0x0000745c, 0x00007471, 0x00007485, + 0x000074ca, 0x00003f1b, 0x00007524, 0x00024c36, + 0x0000753e, 0x00024c92, 0x00007570, 0x0002219f, + 0x00007610, 0x00024fa1, 0x00024fb8, 0x00025044, + 0x00003ffc, 0x00004008, 0x000076f4, 0x000250f3, + 0x000250f2, 0x00025119, 0x00025133, 0x0000771e, + 0x0000771f, 0x0000771f, 0x0000774a, 0x00004039, + 0x0000778b, 0x00004046, 0x00004096, 0x0002541d, + 0x0000784e, 0x0000788c, 0x000078cc, 0x000040e3, + 0x00025626, 0x00007956, 0x0002569a, 0x000256c5, + 0x0000798f, 0x000079eb, 0x0000412f, 0x00007a40, + 0x00007a4a, 0x00007a4f, 0x0002597c, 0x00025aa7, + 0x00025aa7, 0x00007aae, 0x00004202, 0x00025bab, + 0x00007bc6, 0x00007bc9, 0x00004227, 0x00025c80, + 0x00007cd2, 0x000042a0, 0x00007ce8, 0x00007ce3, + 0x00007d00, 0x00025f86, 0x00007d63, 0x00004301, + 0x00007dc7, 0x00007e02, 0x00007e45, 0x00004334, + 0x00026228, 0x00026247, 0x00004359, 0x000262d9, + 0x00007f7a, 0x0002633e, 0x00007f95, 0x00007ffa, + 0x00008005, 0x000264da, 0x00026523, 0x00008060, + 0x000265a8, 0x00008070, 0x0002335f, 0x000043d5, + 0x000080b2, 0x00008103, 0x0000440b, 0x0000813e, + 0x00005ab5, 0x000267a7, 0x000267b5, 0x00023393, + 0x0002339c, 0x00008201, 0x00008204, 0x00008f9e, + 0x0000446b, 0x00008291, 0x0000828b, 0x0000829d, + 0x000052b3, 0x000082b1, 0x000082b3, 0x000082bd, + 0x000082e6, 0x00026b3c, 0x000082e5, 0x0000831d, + 0x00008363, 0x000083ad, 0x00008323, 0x000083bd, + 0x000083e7, 0x00008457, 0x00008353, 0x000083ca, + 0x000083cc, 0x000083dc, 0x00026c36, 0x00026d6b, + 0x00026cd5, 0x0000452b, 0x000084f1, 0x000084f3, + 0x00008516, 0x000273ca, 0x00008564, 0x00026f2c, + 0x0000455d, 0x00004561, 0x00026fb1, 0x000270d2, + 0x0000456b, 0x00008650, 0x0000865c, 0x00008667, + 0x00008669, 0x000086a9, 0x00008688, 0x0000870e, + 0x000086e2, 0x00008779, 0x00008728, 0x0000876b, + 0x00008786, 0x00004d57, 0x000087e1, 0x00008801, + 0x000045f9, 0x00008860, 0x00008863, 0x00027667, + 0x000088d7, 0x000088de, 0x00004635, 0x000088fa, + 0x000034bb, 0x000278ae, 0x00027966, 0x000046be, + 0x000046c7, 0x00008aa0, 0x00008aed, 0x00008b8a, + 0x00008c55, 0x00027ca8, 0x00008cab, 0x00008cc1, + 0x00008d1b, 0x00008d77, 0x00027f2f, 0x00020804, + 0x00008dcb, 0x00008dbc, 0x00008df0, 0x000208de, + 0x00008ed4, 0x00008f38, 0x000285d2, 0x000285ed, + 0x00009094, 0x000090f1, 0x00009111, 0x0002872e, + 0x0000911b, 0x00009238, 0x000092d7, 0x000092d8, + 0x0000927c, 0x000093f9, 0x00009415, 0x00028bfa, + 0x0000958b, 0x00004995, 0x000095b7, 0x00028d77, + 0x000049e6, 0x000096c3, 0x00005db2, 0x00009723, + 0x00029145, 0x0002921a, 0x00004a6e, 0x00004a76, + 0x000097e0, 0x0002940a, 0x00004ab2, 0x00029496, + 0x0000980b, 0x0000980b, 0x00009829, 0x000295b6, + 0x000098e2, 0x00004b33, 0x00009929, 0x000099a7, + 0x000099c2, 0x000099fe, 0x00004bce, 0x00029b30, + 0x00009b12, 0x00009c40, 0x00009cfd, 0x00004cce, + 0x00004ced, 0x00009d67, 0x0002a0ce, 0x00004cf8, + 0x0002a105, 0x0002a20e, 0x0002a291, 0x00009ebb, + 0x00004d56, 0x00009ef9, 0x00009efe, 0x00009f05, + 0x00009f0f, 0x00009f16, 0x00009f3b, 0x0002a600 +}; + +static const ac_uint4 _uccmcl_size = 489; + +static const ac_uint4 _uccmcl_nodes[] = { + 0x00000300, 0x00000314, 0x000000e6, 0x00000315, + 0x00000315, 0x000000e8, 0x00000316, 0x00000319, + 0x000000dc, 0x0000031a, 0x0000031a, 0x000000e8, + 0x0000031b, 0x0000031b, 0x000000d8, 0x0000031c, + 0x00000320, 0x000000dc, 0x00000321, 0x00000322, + 0x000000ca, 0x00000323, 0x00000326, 0x000000dc, + 0x00000327, 0x00000328, 0x000000ca, 0x00000329, + 0x00000333, 0x000000dc, 0x00000334, 0x00000338, + 0x00000001, 0x00000339, 0x0000033c, 0x000000dc, + 0x0000033d, 0x00000344, 0x000000e6, 0x00000345, + 0x00000345, 0x000000f0, 0x00000346, 0x00000346, + 0x000000e6, 0x00000347, 0x00000349, 0x000000dc, + 0x0000034a, 0x0000034c, 0x000000e6, 0x0000034d, + 0x0000034e, 0x000000dc, 0x00000360, 0x00000361, + 0x000000ea, 0x00000362, 0x00000362, 0x000000e9, + 0x00000363, 0x0000036f, 0x000000e6, 0x00000483, + 0x00000486, 0x000000e6, 0x00000591, 0x00000591, + 0x000000dc, 0x00000592, 0x00000595, 0x000000e6, + 0x00000596, 0x00000596, 0x000000dc, 0x00000597, + 0x00000599, 0x000000e6, 0x0000059a, 0x0000059a, + 0x000000de, 0x0000059b, 0x0000059b, 0x000000dc, + 0x0000059c, 0x000005a1, 0x000000e6, 0x000005a3, + 0x000005a7, 0x000000dc, 0x000005a8, 0x000005a9, + 0x000000e6, 0x000005aa, 0x000005aa, 0x000000dc, + 0x000005ab, 0x000005ac, 0x000000e6, 0x000005ad, + 0x000005ad, 0x000000de, 0x000005ae, 0x000005ae, + 0x000000e4, 0x000005af, 0x000005af, 0x000000e6, + 0x000005b0, 0x000005b0, 0x0000000a, 0x000005b1, + 0x000005b1, 0x0000000b, 0x000005b2, 0x000005b2, + 0x0000000c, 0x000005b3, 0x000005b3, 0x0000000d, + 0x000005b4, 0x000005b4, 0x0000000e, 0x000005b5, + 0x000005b5, 0x0000000f, 0x000005b6, 0x000005b6, + 0x00000010, 0x000005b7, 0x000005b7, 0x00000011, + 0x000005b8, 0x000005b8, 0x00000012, 0x000005b9, + 0x000005b9, 0x00000013, 0x000005bb, 0x000005bb, + 0x00000014, 0x000005bc, 0x000005bc, 0x00000015, + 0x000005bd, 0x000005bd, 0x00000016, 0x000005bf, + 0x000005bf, 0x00000017, 0x000005c1, 0x000005c1, + 0x00000018, 0x000005c2, 0x000005c2, 0x00000019, + 0x000005c4, 0x000005c4, 0x000000e6, 0x0000064b, + 0x0000064b, 0x0000001b, 0x0000064c, 0x0000064c, + 0x0000001c, 0x0000064d, 0x0000064d, 0x0000001d, + 0x0000064e, 0x0000064e, 0x0000001e, 0x0000064f, + 0x0000064f, 0x0000001f, 0x00000650, 0x00000650, + 0x00000020, 0x00000651, 0x00000651, 0x00000021, + 0x00000652, 0x00000652, 0x00000022, 0x00000653, + 0x00000654, 0x000000e6, 0x00000655, 0x00000655, + 0x000000dc, 0x00000670, 0x00000670, 0x00000023, + 0x000006d6, 0x000006dc, 0x000000e6, 0x000006df, + 0x000006e2, 0x000000e6, 0x000006e3, 0x000006e3, + 0x000000dc, 0x000006e4, 0x000006e4, 0x000000e6, + 0x000006e7, 0x000006e8, 0x000000e6, 0x000006ea, + 0x000006ea, 0x000000dc, 0x000006eb, 0x000006ec, + 0x000000e6, 0x000006ed, 0x000006ed, 0x000000dc, + 0x00000711, 0x00000711, 0x00000024, 0x00000730, + 0x00000730, 0x000000e6, 0x00000731, 0x00000731, + 0x000000dc, 0x00000732, 0x00000733, 0x000000e6, + 0x00000734, 0x00000734, 0x000000dc, 0x00000735, + 0x00000736, 0x000000e6, 0x00000737, 0x00000739, + 0x000000dc, 0x0000073a, 0x0000073a, 0x000000e6, + 0x0000073b, 0x0000073c, 0x000000dc, 0x0000073d, + 0x0000073d, 0x000000e6, 0x0000073e, 0x0000073e, + 0x000000dc, 0x0000073f, 0x00000741, 0x000000e6, + 0x00000742, 0x00000742, 0x000000dc, 0x00000743, + 0x00000743, 0x000000e6, 0x00000744, 0x00000744, + 0x000000dc, 0x00000745, 0x00000745, 0x000000e6, + 0x00000746, 0x00000746, 0x000000dc, 0x00000747, + 0x00000747, 0x000000e6, 0x00000748, 0x00000748, + 0x000000dc, 0x00000749, 0x0000074a, 0x000000e6, + 0x0000093c, 0x0000093c, 0x00000007, 0x0000094d, + 0x0000094d, 0x00000009, 0x00000951, 0x00000951, + 0x000000e6, 0x00000952, 0x00000952, 0x000000dc, + 0x00000953, 0x00000954, 0x000000e6, 0x000009bc, + 0x000009bc, 0x00000007, 0x000009cd, 0x000009cd, + 0x00000009, 0x00000a3c, 0x00000a3c, 0x00000007, + 0x00000a4d, 0x00000a4d, 0x00000009, 0x00000abc, + 0x00000abc, 0x00000007, 0x00000acd, 0x00000acd, + 0x00000009, 0x00000b3c, 0x00000b3c, 0x00000007, + 0x00000b4d, 0x00000b4d, 0x00000009, 0x00000bcd, + 0x00000bcd, 0x00000009, 0x00000c4d, 0x00000c4d, + 0x00000009, 0x00000c55, 0x00000c55, 0x00000054, + 0x00000c56, 0x00000c56, 0x0000005b, 0x00000ccd, + 0x00000ccd, 0x00000009, 0x00000d4d, 0x00000d4d, + 0x00000009, 0x00000dca, 0x00000dca, 0x00000009, + 0x00000e38, 0x00000e39, 0x00000067, 0x00000e3a, + 0x00000e3a, 0x00000009, 0x00000e48, 0x00000e4b, + 0x0000006b, 0x00000eb8, 0x00000eb9, 0x00000076, + 0x00000ec8, 0x00000ecb, 0x0000007a, 0x00000f18, + 0x00000f19, 0x000000dc, 0x00000f35, 0x00000f35, + 0x000000dc, 0x00000f37, 0x00000f37, 0x000000dc, + 0x00000f39, 0x00000f39, 0x000000d8, 0x00000f71, + 0x00000f71, 0x00000081, 0x00000f72, 0x00000f72, + 0x00000082, 0x00000f74, 0x00000f74, 0x00000084, + 0x00000f7a, 0x00000f7d, 0x00000082, 0x00000f80, + 0x00000f80, 0x00000082, 0x00000f82, 0x00000f83, + 0x000000e6, 0x00000f84, 0x00000f84, 0x00000009, + 0x00000f86, 0x00000f87, 0x000000e6, 0x00000fc6, + 0x00000fc6, 0x000000dc, 0x00001037, 0x00001037, + 0x00000007, 0x00001039, 0x00001039, 0x00000009, + 0x00001714, 0x00001714, 0x00000009, 0x00001734, + 0x00001734, 0x00000009, 0x000017d2, 0x000017d2, + 0x00000009, 0x000018a9, 0x000018a9, 0x000000e4, + 0x000020d0, 0x000020d1, 0x000000e6, 0x000020d2, + 0x000020d3, 0x00000001, 0x000020d4, 0x000020d7, + 0x000000e6, 0x000020d8, 0x000020da, 0x00000001, + 0x000020db, 0x000020dc, 0x000000e6, 0x000020e1, + 0x000020e1, 0x000000e6, 0x000020e5, 0x000020e6, + 0x00000001, 0x000020e7, 0x000020e7, 0x000000e6, + 0x000020e8, 0x000020e8, 0x000000dc, 0x000020e9, + 0x000020e9, 0x000000e6, 0x000020ea, 0x000020ea, + 0x00000001, 0x0000302a, 0x0000302a, 0x000000da, + 0x0000302b, 0x0000302b, 0x000000e4, 0x0000302c, + 0x0000302c, 0x000000e8, 0x0000302d, 0x0000302d, + 0x000000de, 0x0000302e, 0x0000302f, 0x000000e0, + 0x00003099, 0x0000309a, 0x00000008, 0x0000fb1e, + 0x0000fb1e, 0x0000001a, 0x0000fe20, 0x0000fe23, + 0x000000e6, 0x0001d165, 0x0001d166, 0x000000d8, + 0x0001d167, 0x0001d169, 0x00000001, 0x0001d16d, + 0x0001d16d, 0x000000e2, 0x0001d16e, 0x0001d172, + 0x000000d8, 0x0001d17b, 0x0001d182, 0x000000dc, + 0x0001d185, 0x0001d189, 0x000000e6, 0x0001d18a, + 0x0001d18b, 0x000000dc, 0x0001d1aa, 0x0001d1ad, + 0x000000e6 +}; + +static const ac_uint4 _ucnum_size = 1066; + +static const ac_uint4 _ucnum_nodes[] = { + 0x00000030, 0x00000000, 0x00000031, 0x00000002, + 0x00000032, 0x00000004, 0x00000033, 0x00000006, + 0x00000034, 0x00000008, 0x00000035, 0x0000000a, + 0x00000036, 0x0000000c, 0x00000037, 0x0000000e, + 0x00000038, 0x00000010, 0x00000039, 0x00000012, + 0x000000b2, 0x00000004, 0x000000b3, 0x00000006, + 0x000000b9, 0x00000002, 0x000000bc, 0x00000014, + 0x000000bd, 0x00000016, 0x000000be, 0x00000018, + 0x00000660, 0x00000000, 0x00000661, 0x00000002, + 0x00000662, 0x00000004, 0x00000663, 0x00000006, + 0x00000664, 0x00000008, 0x00000665, 0x0000000a, + 0x00000666, 0x0000000c, 0x00000667, 0x0000000e, + 0x00000668, 0x00000010, 0x00000669, 0x00000012, + 0x000006f0, 0x00000000, 0x000006f1, 0x00000002, + 0x000006f2, 0x00000004, 0x000006f3, 0x00000006, + 0x000006f4, 0x00000008, 0x000006f5, 0x0000000a, + 0x000006f6, 0x0000000c, 0x000006f7, 0x0000000e, + 0x000006f8, 0x00000010, 0x000006f9, 0x00000012, + 0x00000966, 0x00000000, 0x00000967, 0x00000002, + 0x00000968, 0x00000004, 0x00000969, 0x00000006, + 0x0000096a, 0x00000008, 0x0000096b, 0x0000000a, + 0x0000096c, 0x0000000c, 0x0000096d, 0x0000000e, + 0x0000096e, 0x00000010, 0x0000096f, 0x00000012, + 0x000009e6, 0x00000000, 0x000009e7, 0x00000002, + 0x000009e8, 0x00000004, 0x000009e9, 0x00000006, + 0x000009ea, 0x00000008, 0x000009eb, 0x0000000a, + 0x000009ec, 0x0000000c, 0x000009ed, 0x0000000e, + 0x000009ee, 0x00000010, 0x000009ef, 0x00000012, + 0x000009f4, 0x00000002, 0x000009f5, 0x00000004, + 0x000009f6, 0x00000006, 0x000009f7, 0x00000008, + 0x000009f9, 0x0000001a, 0x00000a66, 0x00000000, + 0x00000a67, 0x00000002, 0x00000a68, 0x00000004, + 0x00000a69, 0x00000006, 0x00000a6a, 0x00000008, + 0x00000a6b, 0x0000000a, 0x00000a6c, 0x0000000c, + 0x00000a6d, 0x0000000e, 0x00000a6e, 0x00000010, + 0x00000a6f, 0x00000012, 0x00000ae6, 0x00000000, + 0x00000ae7, 0x00000002, 0x00000ae8, 0x00000004, + 0x00000ae9, 0x00000006, 0x00000aea, 0x00000008, + 0x00000aeb, 0x0000000a, 0x00000aec, 0x0000000c, + 0x00000aed, 0x0000000e, 0x00000aee, 0x00000010, + 0x00000aef, 0x00000012, 0x00000b66, 0x00000000, + 0x00000b67, 0x00000002, 0x00000b68, 0x00000004, + 0x00000b69, 0x00000006, 0x00000b6a, 0x00000008, + 0x00000b6b, 0x0000000a, 0x00000b6c, 0x0000000c, + 0x00000b6d, 0x0000000e, 0x00000b6e, 0x00000010, + 0x00000b6f, 0x00000012, 0x00000be7, 0x00000002, + 0x00000be8, 0x00000004, 0x00000be9, 0x00000006, + 0x00000bea, 0x00000008, 0x00000beb, 0x0000000a, + 0x00000bec, 0x0000000c, 0x00000bed, 0x0000000e, + 0x00000bee, 0x00000010, 0x00000bef, 0x00000012, + 0x00000bf0, 0x0000001c, 0x00000bf1, 0x0000001e, + 0x00000bf2, 0x00000020, 0x00000c66, 0x00000000, + 0x00000c67, 0x00000002, 0x00000c68, 0x00000004, + 0x00000c69, 0x00000006, 0x00000c6a, 0x00000008, + 0x00000c6b, 0x0000000a, 0x00000c6c, 0x0000000c, + 0x00000c6d, 0x0000000e, 0x00000c6e, 0x00000010, + 0x00000c6f, 0x00000012, 0x00000ce6, 0x00000000, + 0x00000ce7, 0x00000002, 0x00000ce8, 0x00000004, + 0x00000ce9, 0x00000006, 0x00000cea, 0x00000008, + 0x00000ceb, 0x0000000a, 0x00000cec, 0x0000000c, + 0x00000ced, 0x0000000e, 0x00000cee, 0x00000010, + 0x00000cef, 0x00000012, 0x00000d66, 0x00000000, + 0x00000d67, 0x00000002, 0x00000d68, 0x00000004, + 0x00000d69, 0x00000006, 0x00000d6a, 0x00000008, + 0x00000d6b, 0x0000000a, 0x00000d6c, 0x0000000c, + 0x00000d6d, 0x0000000e, 0x00000d6e, 0x00000010, + 0x00000d6f, 0x00000012, 0x00000e50, 0x00000000, + 0x00000e51, 0x00000002, 0x00000e52, 0x00000004, + 0x00000e53, 0x00000006, 0x00000e54, 0x00000008, + 0x00000e55, 0x0000000a, 0x00000e56, 0x0000000c, + 0x00000e57, 0x0000000e, 0x00000e58, 0x00000010, + 0x00000e59, 0x00000012, 0x00000ed0, 0x00000000, + 0x00000ed1, 0x00000002, 0x00000ed2, 0x00000004, + 0x00000ed3, 0x00000006, 0x00000ed4, 0x00000008, + 0x00000ed5, 0x0000000a, 0x00000ed6, 0x0000000c, + 0x00000ed7, 0x0000000e, 0x00000ed8, 0x00000010, + 0x00000ed9, 0x00000012, 0x00000f20, 0x00000000, + 0x00000f21, 0x00000002, 0x00000f22, 0x00000004, + 0x00000f23, 0x00000006, 0x00000f24, 0x00000008, + 0x00000f25, 0x0000000a, 0x00000f26, 0x0000000c, + 0x00000f27, 0x0000000e, 0x00000f28, 0x00000010, + 0x00000f29, 0x00000012, 0x00000f2a, 0x00000016, + 0x00000f2b, 0x00000022, 0x00000f2c, 0x00000024, + 0x00000f2d, 0x00000026, 0x00000f2e, 0x00000028, + 0x00000f2f, 0x0000002a, 0x00000f30, 0x0000002c, + 0x00000f31, 0x0000002e, 0x00000f32, 0x00000030, + 0x00000f33, 0x00000032, 0x00001040, 0x00000000, + 0x00001041, 0x00000002, 0x00001042, 0x00000004, + 0x00001043, 0x00000006, 0x00001044, 0x00000008, + 0x00001045, 0x0000000a, 0x00001046, 0x0000000c, + 0x00001047, 0x0000000e, 0x00001048, 0x00000010, + 0x00001049, 0x00000012, 0x00001369, 0x00000002, + 0x0000136a, 0x00000004, 0x0000136b, 0x00000006, + 0x0000136c, 0x00000008, 0x0000136d, 0x0000000a, + 0x0000136e, 0x0000000c, 0x0000136f, 0x0000000e, + 0x00001370, 0x00000010, 0x00001371, 0x00000012, + 0x00001372, 0x0000001c, 0x00001373, 0x00000034, + 0x00001374, 0x00000036, 0x00001375, 0x00000038, + 0x00001376, 0x0000003a, 0x00001377, 0x0000003c, + 0x00001378, 0x0000003e, 0x00001379, 0x00000040, + 0x0000137a, 0x00000042, 0x0000137b, 0x0000001e, + 0x0000137c, 0x00000044, 0x000016ee, 0x00000046, + 0x000016ef, 0x00000048, 0x000016f0, 0x0000004a, + 0x000017e0, 0x00000000, 0x000017e1, 0x00000002, + 0x000017e2, 0x00000004, 0x000017e3, 0x00000006, + 0x000017e4, 0x00000008, 0x000017e5, 0x0000000a, + 0x000017e6, 0x0000000c, 0x000017e7, 0x0000000e, + 0x000017e8, 0x00000010, 0x000017e9, 0x00000012, + 0x00001810, 0x00000000, 0x00001811, 0x00000002, + 0x00001812, 0x00000004, 0x00001813, 0x00000006, + 0x00001814, 0x00000008, 0x00001815, 0x0000000a, + 0x00001816, 0x0000000c, 0x00001817, 0x0000000e, + 0x00001818, 0x00000010, 0x00001819, 0x00000012, + 0x00002070, 0x00000000, 0x00002074, 0x00000008, + 0x00002075, 0x0000000a, 0x00002076, 0x0000000c, + 0x00002077, 0x0000000e, 0x00002078, 0x00000010, + 0x00002079, 0x00000012, 0x00002080, 0x00000000, + 0x00002081, 0x00000002, 0x00002082, 0x00000004, + 0x00002083, 0x00000006, 0x00002084, 0x00000008, + 0x00002085, 0x0000000a, 0x00002086, 0x0000000c, + 0x00002087, 0x0000000e, 0x00002088, 0x00000010, + 0x00002089, 0x00000012, 0x00002153, 0x0000004c, + 0x00002154, 0x0000004e, 0x00002155, 0x00000050, + 0x00002156, 0x00000052, 0x00002157, 0x00000054, + 0x00002158, 0x00000056, 0x00002159, 0x00000058, + 0x0000215a, 0x0000005a, 0x0000215b, 0x0000005c, + 0x0000215c, 0x0000005e, 0x0000215d, 0x00000060, + 0x0000215e, 0x00000062, 0x0000215f, 0x00000002, + 0x00002160, 0x00000002, 0x00002161, 0x00000004, + 0x00002162, 0x00000006, 0x00002163, 0x00000008, + 0x00002164, 0x0000000a, 0x00002165, 0x0000000c, + 0x00002166, 0x0000000e, 0x00002167, 0x00000010, + 0x00002168, 0x00000012, 0x00002169, 0x0000001c, + 0x0000216a, 0x00000064, 0x0000216b, 0x00000066, + 0x0000216c, 0x0000003a, 0x0000216d, 0x0000001e, + 0x0000216e, 0x00000068, 0x0000216f, 0x00000020, + 0x00002170, 0x00000002, 0x00002171, 0x00000004, + 0x00002172, 0x00000006, 0x00002173, 0x00000008, + 0x00002174, 0x0000000a, 0x00002175, 0x0000000c, + 0x00002176, 0x0000000e, 0x00002177, 0x00000010, + 0x00002178, 0x00000012, 0x00002179, 0x0000001c, + 0x0000217a, 0x00000064, 0x0000217b, 0x00000066, + 0x0000217c, 0x0000003a, 0x0000217d, 0x0000001e, + 0x0000217e, 0x00000068, 0x0000217f, 0x00000020, + 0x00002180, 0x00000020, 0x00002181, 0x0000006a, + 0x00002182, 0x00000044, 0x00002460, 0x00000002, + 0x00002461, 0x00000004, 0x00002462, 0x00000006, + 0x00002463, 0x00000008, 0x00002464, 0x0000000a, + 0x00002465, 0x0000000c, 0x00002466, 0x0000000e, + 0x00002467, 0x00000010, 0x00002468, 0x00000012, + 0x00002469, 0x0000001c, 0x0000246a, 0x00000064, + 0x0000246b, 0x00000066, 0x0000246c, 0x0000006c, + 0x0000246d, 0x0000006e, 0x0000246e, 0x00000070, + 0x0000246f, 0x0000001a, 0x00002470, 0x00000046, + 0x00002471, 0x00000048, 0x00002472, 0x0000004a, + 0x00002473, 0x00000034, 0x00002474, 0x00000002, + 0x00002475, 0x00000004, 0x00002476, 0x00000006, + 0x00002477, 0x00000008, 0x00002478, 0x0000000a, + 0x00002479, 0x0000000c, 0x0000247a, 0x0000000e, + 0x0000247b, 0x00000010, 0x0000247c, 0x00000012, + 0x0000247d, 0x0000001c, 0x0000247e, 0x00000064, + 0x0000247f, 0x00000066, 0x00002480, 0x0000006c, + 0x00002481, 0x0000006e, 0x00002482, 0x00000070, + 0x00002483, 0x0000001a, 0x00002484, 0x00000046, + 0x00002485, 0x00000048, 0x00002486, 0x0000004a, + 0x00002487, 0x00000034, 0x00002488, 0x00000002, + 0x00002489, 0x00000004, 0x0000248a, 0x00000006, + 0x0000248b, 0x00000008, 0x0000248c, 0x0000000a, + 0x0000248d, 0x0000000c, 0x0000248e, 0x0000000e, + 0x0000248f, 0x00000010, 0x00002490, 0x00000012, + 0x00002491, 0x0000001c, 0x00002492, 0x00000064, + 0x00002493, 0x00000066, 0x00002494, 0x0000006c, + 0x00002495, 0x0000006e, 0x00002496, 0x00000070, + 0x00002497, 0x0000001a, 0x00002498, 0x00000046, + 0x00002499, 0x00000048, 0x0000249a, 0x0000004a, + 0x0000249b, 0x00000034, 0x000024ea, 0x00000000, + 0x000024eb, 0x00000064, 0x000024ec, 0x00000066, + 0x000024ed, 0x0000006c, 0x000024ee, 0x0000006e, + 0x000024ef, 0x00000070, 0x000024f0, 0x0000001a, + 0x000024f1, 0x00000046, 0x000024f2, 0x00000048, + 0x000024f3, 0x0000004a, 0x000024f4, 0x00000034, + 0x000024f5, 0x00000002, 0x000024f6, 0x00000004, + 0x000024f7, 0x00000006, 0x000024f8, 0x00000008, + 0x000024f9, 0x0000000a, 0x000024fa, 0x0000000c, + 0x000024fb, 0x0000000e, 0x000024fc, 0x00000010, + 0x000024fd, 0x00000012, 0x000024fe, 0x0000001c, + 0x00002776, 0x00000002, 0x00002777, 0x00000004, + 0x00002778, 0x00000006, 0x00002779, 0x00000008, + 0x0000277a, 0x0000000a, 0x0000277b, 0x0000000c, + 0x0000277c, 0x0000000e, 0x0000277d, 0x00000010, + 0x0000277e, 0x00000012, 0x0000277f, 0x0000001c, + 0x00002780, 0x00000002, 0x00002781, 0x00000004, + 0x00002782, 0x00000006, 0x00002783, 0x00000008, + 0x00002784, 0x0000000a, 0x00002785, 0x0000000c, + 0x00002786, 0x0000000e, 0x00002787, 0x00000010, + 0x00002788, 0x00000012, 0x00002789, 0x0000001c, + 0x0000278a, 0x00000002, 0x0000278b, 0x00000004, + 0x0000278c, 0x00000006, 0x0000278d, 0x00000008, + 0x0000278e, 0x0000000a, 0x0000278f, 0x0000000c, + 0x00002790, 0x0000000e, 0x00002791, 0x00000010, + 0x00002792, 0x00000012, 0x00002793, 0x0000001c, + 0x00003007, 0x00000000, 0x00003021, 0x00000002, + 0x00003022, 0x00000004, 0x00003023, 0x00000006, + 0x00003024, 0x00000008, 0x00003025, 0x0000000a, + 0x00003026, 0x0000000c, 0x00003027, 0x0000000e, + 0x00003028, 0x00000010, 0x00003029, 0x00000012, + 0x00003038, 0x0000001c, 0x00003039, 0x00000034, + 0x0000303a, 0x00000036, 0x00003192, 0x00000002, + 0x00003193, 0x00000004, 0x00003194, 0x00000006, + 0x00003195, 0x00000008, 0x00003220, 0x00000002, + 0x00003221, 0x00000004, 0x00003222, 0x00000006, + 0x00003223, 0x00000008, 0x00003224, 0x0000000a, + 0x00003225, 0x0000000c, 0x00003226, 0x0000000e, + 0x00003227, 0x00000010, 0x00003228, 0x00000012, + 0x00003229, 0x0000001c, 0x00003251, 0x00000072, + 0x00003252, 0x00000074, 0x00003253, 0x00000076, + 0x00003254, 0x00000078, 0x00003255, 0x0000007a, + 0x00003256, 0x0000007c, 0x00003257, 0x0000007e, + 0x00003258, 0x00000080, 0x00003259, 0x00000082, + 0x0000325a, 0x00000036, 0x0000325b, 0x00000084, + 0x0000325c, 0x00000086, 0x0000325d, 0x00000088, + 0x0000325e, 0x0000008a, 0x0000325f, 0x0000008c, + 0x00003280, 0x00000002, 0x00003281, 0x00000004, + 0x00003282, 0x00000006, 0x00003283, 0x00000008, + 0x00003284, 0x0000000a, 0x00003285, 0x0000000c, + 0x00003286, 0x0000000e, 0x00003287, 0x00000010, + 0x00003288, 0x00000012, 0x00003289, 0x0000001c, + 0x000032b1, 0x0000008e, 0x000032b2, 0x00000090, + 0x000032b3, 0x00000092, 0x000032b4, 0x00000094, + 0x000032b5, 0x00000038, 0x000032b6, 0x00000096, + 0x000032b7, 0x00000098, 0x000032b8, 0x0000009a, + 0x000032b9, 0x0000009c, 0x000032ba, 0x0000009e, + 0x000032bb, 0x000000a0, 0x000032bc, 0x000000a2, + 0x000032bd, 0x000000a4, 0x000032be, 0x000000a6, + 0x000032bf, 0x0000003a, 0x0000ff10, 0x00000000, + 0x0000ff11, 0x00000002, 0x0000ff12, 0x00000004, + 0x0000ff13, 0x00000006, 0x0000ff14, 0x00000008, + 0x0000ff15, 0x0000000a, 0x0000ff16, 0x0000000c, + 0x0000ff17, 0x0000000e, 0x0000ff18, 0x00000010, + 0x0000ff19, 0x00000012, 0x00010320, 0x00000002, + 0x00010321, 0x0000000a, 0x00010322, 0x0000001c, + 0x00010323, 0x0000003a, 0x0001d7ce, 0x00000000, + 0x0001d7cf, 0x00000002, 0x0001d7d0, 0x00000004, + 0x0001d7d1, 0x00000006, 0x0001d7d2, 0x00000008, + 0x0001d7d3, 0x0000000a, 0x0001d7d4, 0x0000000c, + 0x0001d7d5, 0x0000000e, 0x0001d7d6, 0x00000010, + 0x0001d7d7, 0x00000012, 0x0001d7d8, 0x00000000, + 0x0001d7d9, 0x00000002, 0x0001d7da, 0x00000004, + 0x0001d7db, 0x00000006, 0x0001d7dc, 0x00000008, + 0x0001d7dd, 0x0000000a, 0x0001d7de, 0x0000000c, + 0x0001d7df, 0x0000000e, 0x0001d7e0, 0x00000010, + 0x0001d7e1, 0x00000012, 0x0001d7e2, 0x00000000, + 0x0001d7e3, 0x00000002, 0x0001d7e4, 0x00000004, + 0x0001d7e5, 0x00000006, 0x0001d7e6, 0x00000008, + 0x0001d7e7, 0x0000000a, 0x0001d7e8, 0x0000000c, + 0x0001d7e9, 0x0000000e, 0x0001d7ea, 0x00000010, + 0x0001d7eb, 0x00000012, 0x0001d7ec, 0x00000000, + 0x0001d7ed, 0x00000002, 0x0001d7ee, 0x00000004, + 0x0001d7ef, 0x00000006, 0x0001d7f0, 0x00000008, + 0x0001d7f1, 0x0000000a, 0x0001d7f2, 0x0000000c, + 0x0001d7f3, 0x0000000e, 0x0001d7f4, 0x00000010, + 0x0001d7f5, 0x00000012, 0x0001d7f6, 0x00000000, + 0x0001d7f7, 0x00000002, 0x0001d7f8, 0x00000004, + 0x0001d7f9, 0x00000006, 0x0001d7fa, 0x00000008, + 0x0001d7fb, 0x0000000a, 0x0001d7fc, 0x0000000c, + 0x0001d7fd, 0x0000000e, 0x0001d7fe, 0x00000010, + 0x0001d7ff, 0x00000012 +}; + +static const short _ucnum_vals[] = { + 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0001, 0x0003, 0x0001, + 0x0004, 0x0001, 0x0005, 0x0001, 0x0006, 0x0001, 0x0007, 0x0001, + 0x0008, 0x0001, 0x0009, 0x0001, 0x0001, 0x0004, 0x0001, 0x0002, + 0x0003, 0x0004, 0x0010, 0x0001, 0x000a, 0x0001, 0x0064, 0x0001, + 0x03e8, 0x0001, 0x0003, 0x0002, 0x0005, 0x0002, 0x0007, 0x0002, + 0x0009, 0x0002, 0x000b, 0x0002, 0x000d, 0x0002, 0x000f, 0x0002, + 0x0011, 0x0002, -1, 0x0002, 0x0014, 0x0001, 0x001e, 0x0001, + 0x0028, 0x0001, 0x0032, 0x0001, 0x003c, 0x0001, 0x0046, 0x0001, + 0x0050, 0x0001, 0x005a, 0x0001, 0x2710, 0x0001, 0x0011, 0x0001, + 0x0012, 0x0001, 0x0013, 0x0001, 0x0001, 0x0003, 0x0002, 0x0003, + 0x0001, 0x0005, 0x0002, 0x0005, 0x0003, 0x0005, 0x0004, 0x0005, + 0x0001, 0x0006, 0x0005, 0x0006, 0x0001, 0x0008, 0x0003, 0x0008, + 0x0005, 0x0008, 0x0007, 0x0008, 0x000b, 0x0001, 0x000c, 0x0001, + 0x01f4, 0x0001, 0x1388, 0x0001, 0x000d, 0x0001, 0x000e, 0x0001, + 0x000f, 0x0001, 0x0015, 0x0001, 0x0016, 0x0001, 0x0017, 0x0001, + 0x0018, 0x0001, 0x0019, 0x0001, 0x001a, 0x0001, 0x001b, 0x0001, + 0x001c, 0x0001, 0x001d, 0x0001, 0x001f, 0x0001, 0x0020, 0x0001, + 0x0021, 0x0001, 0x0022, 0x0001, 0x0023, 0x0001, 0x0024, 0x0001, + 0x0025, 0x0001, 0x0026, 0x0001, 0x0027, 0x0001, 0x0029, 0x0001, + 0x002a, 0x0001, 0x002b, 0x0001, 0x002c, 0x0001, 0x002d, 0x0001, + 0x002e, 0x0001, 0x002f, 0x0001, 0x0030, 0x0001, 0x0031, 0x0001 +}; + diff --git a/libraries/liblunicode/ucstr.c b/libraries/liblunicode/ucstr.c new file mode 100644 index 0000000..b60e222 --- /dev/null +++ b/libraries/liblunicode/ucstr.c @@ -0,0 +1,459 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <ac/bytes.h> +#include <ac/ctype.h> +#include <ac/string.h> +#include <ac/stdlib.h> + +#include <lber_pvt.h> + +#include <ldap_utf8.h> +#include <ldap_pvt_uc.h> + +#define malloc(x) ber_memalloc_x(x,ctx) +#define realloc(x,y) ber_memrealloc_x(x,y,ctx) +#define free(x) ber_memfree_x(x,ctx) + +int ucstrncmp( + const ldap_unicode_t *u1, + const ldap_unicode_t *u2, + ber_len_t n ) +{ + for(; 0 < n; ++u1, ++u2, --n ) { + if( *u1 != *u2 ) { + return *u1 < *u2 ? -1 : +1; + } + if ( *u1 == 0 ) { + return 0; + } + } + return 0; +} + +int ucstrncasecmp( + const ldap_unicode_t *u1, + const ldap_unicode_t *u2, + ber_len_t n ) +{ + for(; 0 < n; ++u1, ++u2, --n ) { + ldap_unicode_t uu1 = uctolower( *u1 ); + ldap_unicode_t uu2 = uctolower( *u2 ); + + if( uu1 != uu2 ) { + return uu1 < uu2 ? -1 : +1; + } + if ( uu1 == 0 ) { + return 0; + } + } + return 0; +} + +ldap_unicode_t * ucstrnchr( + const ldap_unicode_t *u, + ber_len_t n, + ldap_unicode_t c ) +{ + for(; 0 < n; ++u, --n ) { + if( *u == c ) { + return (ldap_unicode_t *) u; + } + } + + return NULL; +} + +ldap_unicode_t * ucstrncasechr( + const ldap_unicode_t *u, + ber_len_t n, + ldap_unicode_t c ) +{ + c = uctolower( c ); + for(; 0 < n; ++u, --n ) { + if( uctolower( *u ) == c ) { + return (ldap_unicode_t *) u; + } + } + + return NULL; +} + +void ucstr2upper( + ldap_unicode_t *u, + ber_len_t n ) +{ + for(; 0 < n; ++u, --n ) { + *u = uctoupper( *u ); + } +} + +struct berval * UTF8bvnormalize( + struct berval *bv, + struct berval *newbv, + unsigned flags, + void *ctx ) +{ + int i, j, len, clen, outpos, ucsoutlen, outsize, last; + int didnewbv = 0; + char *out, *outtmp, *s; + ac_uint4 *ucs, *p, *ucsout; + + static unsigned char mask[] = { + 0, 0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01 }; + + unsigned casefold = flags & LDAP_UTF8_CASEFOLD; + unsigned approx = flags & LDAP_UTF8_APPROX; + + if ( bv == NULL ) { + return NULL; + } + + s = bv->bv_val; + len = bv->bv_len; + + if ( len == 0 ) { + return ber_dupbv_x( newbv, bv, ctx ); + } + + if ( !newbv ) { + newbv = ber_memalloc_x( sizeof(struct berval), ctx ); + if ( !newbv ) return NULL; + didnewbv = 1; + } + + /* Should first check to see if string is already in proper + * normalized form. This is almost as time consuming as + * the normalization though. + */ + + /* finish off everything up to character before first non-ascii */ + if ( LDAP_UTF8_ISASCII( s ) ) { + if ( casefold ) { + outsize = len + 7; + out = (char *) ber_memalloc_x( outsize, ctx ); + if ( out == NULL ) { +fail: + if ( didnewbv ) + ber_memfree_x( newbv, ctx ); + return NULL; + } + outpos = 0; + + for ( i = 1; (i < len) && LDAP_UTF8_ISASCII(s + i); i++ ) { + out[outpos++] = TOLOWER( s[i-1] ); + } + if ( i == len ) { + out[outpos++] = TOLOWER( s[len-1] ); + out[outpos] = '\0'; + newbv->bv_val = out; + newbv->bv_len = outpos; + return newbv; + } + } else { + for ( i = 1; (i < len) && LDAP_UTF8_ISASCII(s + i); i++ ) { + /* empty */ + } + + if ( i == len ) { + return ber_str2bv_x( s, len, 1, newbv, ctx ); + } + + outsize = len + 7; + out = (char *) ber_memalloc_x( outsize, ctx ); + if ( out == NULL ) { + goto fail; + } + outpos = i - 1; + memcpy(out, s, outpos); + } + } else { + outsize = len + 7; + out = (char *) ber_memalloc_x( outsize, ctx ); + if ( out == NULL ) { + goto fail; + } + outpos = 0; + i = 0; + } + + p = ucs = ber_memalloc_x( len * sizeof(*ucs), ctx ); + if ( ucs == NULL ) { + ber_memfree_x(out, ctx); + goto fail; + } + + /* convert character before first non-ascii to ucs-4 */ + if ( i > 0 ) { + *p = casefold ? TOLOWER( s[i-1] ) : s[i-1]; + p++; + } + + /* s[i] is now first non-ascii character */ + for (;;) { + /* s[i] is non-ascii */ + /* convert everything up to next ascii to ucs-4 */ + while ( i < len ) { + clen = LDAP_UTF8_CHARLEN2( s + i, clen ); + if ( clen == 0 ) { + ber_memfree_x( ucs, ctx ); + ber_memfree_x( out, ctx ); + goto fail; + } + if ( clen == 1 ) { + /* ascii */ + break; + } + *p = s[i] & mask[clen]; + i++; + for( j = 1; j < clen; j++ ) { + if ( (s[i] & 0xc0) != 0x80 ) { + ber_memfree_x( ucs, ctx ); + ber_memfree_x( out, ctx ); + goto fail; + } + *p <<= 6; + *p |= s[i] & 0x3f; + i++; + } + if ( casefold ) { + *p = uctolower( *p ); + } + p++; + } + /* normalize ucs of length p - ucs */ + uccompatdecomp( ucs, p - ucs, &ucsout, &ucsoutlen, ctx ); + if ( approx ) { + for ( j = 0; j < ucsoutlen; j++ ) { + if ( ucsout[j] < 0x80 ) { + out[outpos++] = ucsout[j]; + } + } + } else { + ucsoutlen = uccanoncomp( ucsout, ucsoutlen ); + /* convert ucs to utf-8 and store in out */ + for ( j = 0; j < ucsoutlen; j++ ) { + /* allocate more space if not enough room for + 6 bytes and terminator */ + if ( outsize - outpos < 7 ) { + outsize = ucsoutlen - j + outpos + 6; + outtmp = (char *) ber_memrealloc_x( out, outsize, ctx ); + if ( outtmp == NULL ) { + ber_memfree_x( ucsout, ctx ); + ber_memfree_x( ucs, ctx ); + ber_memfree_x( out, ctx ); + goto fail; + } + out = outtmp; + } + outpos += ldap_x_ucs4_to_utf8( ucsout[j], &out[outpos] ); + } + } + + ber_memfree_x( ucsout, ctx ); + ucsout = NULL; + + if ( i == len ) { + break; + } + + last = i; + + /* Allocate more space in out if necessary */ + if (len - i >= outsize - outpos) { + outsize += 1 + ((len - i) - (outsize - outpos)); + outtmp = (char *) ber_memrealloc_x(out, outsize, ctx); + if (outtmp == NULL) { + ber_memfree_x( ucs, ctx ); + ber_memfree_x( out, ctx ); + goto fail; + } + out = outtmp; + } + + /* s[i] is ascii */ + /* finish off everything up to char before next non-ascii */ + for ( i++; (i < len) && LDAP_UTF8_ISASCII(s + i); i++ ) { + out[outpos++] = casefold ? TOLOWER( s[i-1] ) : s[i-1]; + } + if ( i == len ) { + out[outpos++] = casefold ? TOLOWER( s[len-1] ) : s[len-1]; + break; + } + + /* convert character before next non-ascii to ucs-4 */ + *ucs = casefold ? TOLOWER( s[i-1] ) : s[i-1]; + p = ucs + 1; + } + + ber_memfree_x( ucs, ctx ); + out[outpos] = '\0'; + newbv->bv_val = out; + newbv->bv_len = outpos; + return newbv; +} + +/* compare UTF8-strings, optionally ignore casing */ +/* slow, should be optimized */ +int UTF8bvnormcmp( + struct berval *bv1, + struct berval *bv2, + unsigned flags, + void *ctx ) +{ + int i, l1, l2, len, ulen, res = 0; + char *s1, *s2, *done; + ac_uint4 *ucs, *ucsout1, *ucsout2; + + unsigned casefold = flags & LDAP_UTF8_CASEFOLD; + unsigned norm1 = flags & LDAP_UTF8_ARG1NFC; + unsigned norm2 = flags & LDAP_UTF8_ARG2NFC; + + if (bv1 == NULL) { + return bv2 == NULL ? 0 : -1; + + } else if (bv2 == NULL) { + return 1; + } + + l1 = bv1->bv_len; + l2 = bv2->bv_len; + + len = (l1 < l2) ? l1 : l2; + if (len == 0) { + return l1 == 0 ? (l2 == 0 ? 0 : -1) : 1; + } + + s1 = bv1->bv_val; + s2 = bv2->bv_val; + done = s1 + len; + + while ( (s1 < done) && LDAP_UTF8_ISASCII(s1) && LDAP_UTF8_ISASCII(s2) ) { + if (casefold) { + char c1 = TOLOWER(*s1); + char c2 = TOLOWER(*s2); + res = c1 - c2; + } else { + res = *s1 - *s2; + } + s1++; + s2++; + if (res) { + /* done unless next character in s1 or s2 is non-ascii */ + if (s1 < done) { + if (!LDAP_UTF8_ISASCII(s1) || !LDAP_UTF8_ISASCII(s2)) { + break; + } + } else if (((len < l1) && !LDAP_UTF8_ISASCII(s1)) || + ((len < l2) && !LDAP_UTF8_ISASCII(s2))) + { + break; + } + return res; + } + } + + /* We have encountered non-ascii or strings equal up to len */ + + /* set i to number of iterations */ + i = s1 - done + len; + /* passed through loop at least once? */ + if (i > 0) { + if (!res && (s1 == done) && + ((len == l1) || LDAP_UTF8_ISASCII(s1)) && + ((len == l2) || LDAP_UTF8_ISASCII(s2))) { + /* all ascii and equal up to len */ + return l1 - l2; + } + + /* rewind one char, and do normalized compare from there */ + s1--; + s2--; + l1 -= i - 1; + l2 -= i - 1; + } + + /* Should first check to see if strings are already in + * proper normalized form. + */ + ucs = malloc( ( ( norm1 || l1 > l2 ) ? l1 : l2 ) * sizeof(*ucs) ); + if ( ucs == NULL ) { + return l1 > l2 ? 1 : -1; /* what to do??? */ + } + + /* + * XXYYZ: we convert to ucs4 even though -llunicode + * expects ucs2 in an ac_uint4 + */ + + /* convert and normalize 1st string */ + for ( i = 0, ulen = 0; i < l1; i += len, ulen++ ) { + ucs[ulen] = ldap_x_utf8_to_ucs4( s1 + i ); + if ( ucs[ulen] == LDAP_UCS4_INVALID ) { + free( ucs ); + return -1; /* what to do??? */ + } + len = LDAP_UTF8_CHARLEN( s1 + i ); + } + + if ( norm1 ) { + ucsout1 = ucs; + l1 = ulen; + ucs = malloc( l2 * sizeof(*ucs) ); + if ( ucs == NULL ) { + free( ucsout1 ); + return l1 > l2 ? 1 : -1; /* what to do??? */ + } + } else { + uccompatdecomp( ucs, ulen, &ucsout1, &l1, ctx ); + l1 = uccanoncomp( ucsout1, l1 ); + } + + /* convert and normalize 2nd string */ + for ( i = 0, ulen = 0; i < l2; i += len, ulen++ ) { + ucs[ulen] = ldap_x_utf8_to_ucs4( s2 + i ); + if ( ucs[ulen] == LDAP_UCS4_INVALID ) { + free( ucsout1 ); + free( ucs ); + return 1; /* what to do??? */ + } + len = LDAP_UTF8_CHARLEN( s2 + i ); + } + + if ( norm2 ) { + ucsout2 = ucs; + l2 = ulen; + } else { + uccompatdecomp( ucs, ulen, &ucsout2, &l2, ctx ); + l2 = uccanoncomp( ucsout2, l2 ); + free( ucs ); + } + + res = casefold + ? ucstrncasecmp( ucsout1, ucsout2, l1 < l2 ? l1 : l2 ) + : ucstrncmp( ucsout1, ucsout2, l1 < l2 ? l1 : l2 ); + free( ucsout1 ); + free( ucsout2 ); + + if ( res != 0 ) { + return res; + } + if ( l1 == l2 ) { + return 0; + } + return l1 > l2 ? 1 : -1; +} diff --git a/libraries/liblunicode/ure/README b/libraries/liblunicode/ure/README new file mode 100644 index 0000000..c9918f5 --- /dev/null +++ b/libraries/liblunicode/ure/README @@ -0,0 +1,212 @@ +# +# $Id: README,v 1.3 1999/09/21 15:47:43 mleisher Exp $ +# +# Copyright 1997, 1998, 1999 Computing Research Labs, +# New Mexico State University +# +# 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 COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY 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. +# + + + Unicode and Regular Expressions + Version 0.5 + +This is a simple regular expression package for matching against Unicode text +in UCS2 form. The implementation of this URE package is a variation on the +RE->DFA algorithm done by Mark Hopkins (markh@csd4.csd.uwm.edu). Mark +Hopkins' algorithm had the virtue of being very simple, so it was used as a +model. + +--------------------------------------------------------------------------- + +Assumptions: + + o Regular expression and text already normalized. + + o Conversion to lower case assumes a 1-1 mapping. + +Definitions: + + Separator - any one of U+2028, U+2029, '\n', '\r'. + +Operators: + . - match any character. + * - match zero or more of the last subexpression. + + - match one or more of the last subexpression. + ? - match zero or one of the last subexpression. + () - subexpression grouping. + + Notes: + + o The "." operator normally does not match separators, but a flag is + available for the ure_exec() function that will allow this operator to + match a separator. + +Literals and Constants: + + c - literal UCS2 character. + \x.... - hexadecimal number of up to 4 digits. + \X.... - hexadecimal number of up to 4 digits. + \u.... - hexadecimal number of up to 4 digits. + \U.... - hexadecimal number of up to 4 digits. + +Character classes: + + [...] - Character class. + [^...] - Negated character class. + \pN1,N2,...,Nn - Character properties class. + \PN1,N2,...,Nn - Negated character properties class. + + POSIX character classes recognized: + + :alnum: + :alpha: + :cntrl: + :digit: + :graph: + :lower: + :print: + :punct: + :space: + :upper: + :xdigit: + + Notes: + + o Character property classes are \p or \P followed by a comma separated + list of integers between 1 and 32. These integers are references to + the following character properties: + + N Character Property + -------------------------- + 1 _URE_NONSPACING + 2 _URE_COMBINING + 3 _URE_NUMDIGIT + 4 _URE_NUMOTHER + 5 _URE_SPACESEP + 6 _URE_LINESEP + 7 _URE_PARASEP + 8 _URE_CNTRL + 9 _URE_PUA + 10 _URE_UPPER + 11 _URE_LOWER + 12 _URE_TITLE + 13 _URE_MODIFIER + 14 _URE_OTHERLETTER + 15 _URE_DASHPUNCT + 16 _URE_OPENPUNCT + 17 _URE_CLOSEPUNCT + 18 _URE_OTHERPUNCT + 19 _URE_MATHSYM + 20 _URE_CURRENCYSYM + 21 _URE_OTHERSYM + 22 _URE_LTR + 23 _URE_RTL + 24 _URE_EURONUM + 25 _URE_EURONUMSEP + 26 _URE_EURONUMTERM + 27 _URE_ARABNUM + 28 _URE_COMMONSEP + 29 _URE_BLOCKSEP + 30 _URE_SEGMENTSEP + 31 _URE_WHITESPACE + 32 _URE_OTHERNEUT + + o Character classes can contain literals, constants, and character + property classes. Example: + + [abc\U10A\p1,3,4] + +--------------------------------------------------------------------------- + +Before using URE +---------------- +Before URE is used, two functions need to be created. One to check if a +character matches a set of URE character properties, and one to convert a +character to lower case. + +Stubs for these function are located in the urestubs.c file. + +Using URE +--------- + +Sample pseudo-code fragment. + + ure_buffer_t rebuf; + ure_dfa_t dfa; + ucs2_t *re, *text; + unsigned long relen, textlen; + unsigned long match_start, match_end; + + /* + * Allocate the dynamic storage needed to compile regular expressions. + */ + rebuf = ure_buffer_create(); + + for each regular expression in a list { + re = next regular expression; + relen = length(re); + + /* + * Compile the regular expression with the case insensitive flag + * turned on. + */ + dfa = ure_compile(re, relen, 1, rebuf); + + /* + * Look for the first match in some text. The matching will be done + * in a case insensitive manner because the expression was compiled + * with the case insensitive flag on. + */ + if (ure_exec(dfa, 0, text, textlen, &match_start, &match_end)) + printf("MATCH: %ld %ld\n", match_start, match_end); + + /* + * Look for the first match in some text, ignoring non-spacing + * characters. + */ + if (ure_exec(dfa, URE_IGNORE_NONSPACING, text, textlen, + &match_start, &match_end)) + printf("MATCH: %ld %ld\n", match_start, match_end); + + /* + * Free the DFA. + */ + ure_free_dfa(dfa); + } + + /* + * Free the dynamic storage used for compiling the expressions. + */ + ure_free_buffer(rebuf); + +--------------------------------------------------------------------------- + +Mark Leisher <mleisher@crl.nmsu.edu> +29 March 1997 + +=========================================================================== + +CHANGES +------- + +Version: 0.5 +Date : 21 September 1999 +========================== + 1. Added copyright stuff and put in CVS. diff --git a/libraries/liblunicode/ure/ure.c b/libraries/liblunicode/ure/ure.c new file mode 100644 index 0000000..12f77e7 --- /dev/null +++ b/libraries/liblunicode/ure/ure.c @@ -0,0 +1,2131 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Copyright 1997, 1998, 1999 Computing Research Labs, + * New Mexico State University + * + * 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 COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY 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. + */ +/* $Id: ure.c,v 1.2 1999/09/21 15:47:43 mleisher Exp $" */ + +#include "portable.h" + +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/unistd.h> + +#include "ure.h" + +/* + * Flags used internally in the DFA. + */ +#define _URE_DFA_CASEFOLD 0x01 +#define _URE_DFA_BLANKLINE 0x02 + +static unsigned long cclass_flags[] = { + 0, + _URE_NONSPACING, + _URE_COMBINING, + _URE_NUMDIGIT, + _URE_NUMOTHER, + _URE_SPACESEP, + _URE_LINESEP, + _URE_PARASEP, + _URE_CNTRL, + _URE_PUA, + _URE_UPPER, + _URE_LOWER, + _URE_TITLE, + _URE_MODIFIER, + _URE_OTHERLETTER, + _URE_DASHPUNCT, + _URE_OPENPUNCT, + _URE_CLOSEPUNCT, + _URE_OTHERPUNCT, + _URE_MATHSYM, + _URE_CURRENCYSYM, + _URE_OTHERSYM, + _URE_LTR, + _URE_RTL, + _URE_EURONUM, + _URE_EURONUMSEP, + _URE_EURONUMTERM, + _URE_ARABNUM, + _URE_COMMONSEP, + _URE_BLOCKSEP, + _URE_SEGMENTSEP, + _URE_WHITESPACE, + _URE_OTHERNEUT, +}; + +/* + * Symbol types for the DFA. + */ +#define _URE_ANY_CHAR 1 +#define _URE_CHAR 2 +#define _URE_CCLASS 3 +#define _URE_NCCLASS 4 +#define _URE_BOL_ANCHOR 5 +#define _URE_EOL_ANCHOR 6 + +/* + * Op codes for converting the NFA to a DFA. + */ +#define _URE_SYMBOL 10 +#define _URE_PAREN 11 +#define _URE_QUEST 12 +#define _URE_STAR 13 +#define _URE_PLUS 14 +#define _URE_ONE 15 +#define _URE_AND 16 +#define _URE_OR 17 + +#define _URE_NOOP 0xffff + +#define _URE_REGSTART 0x8000 +#define _URE_REGEND 0x4000 + +/* + * Structure used to handle a compacted range of characters. + */ +typedef struct { + ucs4_t min_code; + ucs4_t max_code; +} _ure_range_t; + +typedef struct { + _ure_range_t *ranges; + ucs2_t ranges_used; + ucs2_t ranges_size; +} _ure_ccl_t; + +typedef union { + ucs4_t chr; + _ure_ccl_t ccl; +} _ure_sym_t; + +/* + * This is a general element structure used for expressions and stack + * elements. + */ +typedef struct { + ucs2_t reg; + ucs2_t onstack; + ucs2_t type; + ucs2_t lhs; + ucs2_t rhs; +} _ure_elt_t; + +/* + * This is a structure used to track a list or a stack of states. + */ +typedef struct { + ucs2_t *slist; + ucs2_t slist_size; + ucs2_t slist_used; +} _ure_stlist_t; + +/* + * Structure to track the list of unique states for a symbol + * during reduction. + */ +typedef struct { + ucs2_t id; + ucs2_t type; + unsigned long mods; + unsigned long props; + _ure_sym_t sym; + _ure_stlist_t states; +} _ure_symtab_t; + +/* + * Structure to hold a single state. + */ +typedef struct { + ucs2_t id; + ucs2_t accepting; + ucs2_t pad; + _ure_stlist_t st; + _ure_elt_t *trans; + ucs2_t trans_size; + ucs2_t trans_used; +} _ure_state_t; + +/* + * Structure used for keeping lists of states. + */ +typedef struct { + _ure_state_t *states; + ucs2_t states_size; + ucs2_t states_used; +} _ure_statetable_t; + +/* + * Structure to track pairs of DFA states when equivalent states are + * merged. + */ +typedef struct { + ucs2_t l; + ucs2_t r; +} _ure_equiv_t; + +/* + * Structure used for constructing the NFA and reducing to a minimal DFA. + */ +typedef struct _ure_buffer_t { + int reducing; + int error; + unsigned long flags; + + _ure_stlist_t stack; + + /* + * Table of unique symbols encountered. + */ + _ure_symtab_t *symtab; + ucs2_t symtab_size; + ucs2_t symtab_used; + + /* + * Tracks the unique expressions generated for the NFA and when the NFA is + * reduced. + */ + _ure_elt_t *expr; + ucs2_t expr_used; + ucs2_t expr_size; + + /* + * The reduced table of unique groups of NFA states. + */ + _ure_statetable_t states; + + /* + * Tracks states when equivalent states are merged. + */ + _ure_equiv_t *equiv; + ucs2_t equiv_used; + ucs2_t equiv_size; +} _ure_buffer_t; + +typedef struct { + ucs2_t symbol; + ucs2_t next_state; +} _ure_trans_t; + +typedef struct { + ucs2_t accepting; + ucs2_t ntrans; + _ure_trans_t *trans; +} _ure_dstate_t; + +typedef struct _ure_dfa_t { + unsigned long flags; + + _ure_symtab_t *syms; + ucs2_t nsyms; + + _ure_dstate_t *states; + ucs2_t nstates; + + _ure_trans_t *trans; + ucs2_t ntrans; +} _ure_dfa_t; + +/************************************************************************* + * + * Functions. + * + *************************************************************************/ + +static void +_ure_memmove(char *dest, char *src, unsigned long bytes) +{ + long i, j; + + i = (long) bytes; + j = i & 7; + i = (i + 7) >> 3; + + /* + * Do a memmove using Ye Olde Duff's Device for efficiency. + */ + if (src < dest) { + src += bytes; + dest += bytes; + + switch (j) { + case 0: do { + *--dest = *--src; + case 7: *--dest = *--src; + case 6: *--dest = *--src; + case 5: *--dest = *--src; + case 4: *--dest = *--src; + case 3: *--dest = *--src; + case 2: *--dest = *--src; + case 1: *--dest = *--src; + } while (--i > 0); + } + } else if (src > dest) { + switch (j) { + case 0: do { + *dest++ = *src++; + case 7: *dest++ = *src++; + case 6: *dest++ = *src++; + case 5: *dest++ = *src++; + case 4: *dest++ = *src++; + case 3: *dest++ = *src++; + case 2: *dest++ = *src++; + case 1: *dest++ = *src++; + } while (--i > 0); + } + } +} + +static void +_ure_push(ucs2_t v, _ure_buffer_t *b) +{ + _ure_stlist_t *s; + + if (b == 0) + return; + + /* + * If the `reducing' parameter is non-zero, check to see if the value + * passed is already on the stack. + */ + if (b->reducing != 0 && b->expr[v].onstack != 0) + return; + + s = &b->stack; + if (s->slist_used == s->slist_size) { + if (s->slist_size == 0) + s->slist = (ucs2_t *) malloc(sizeof(ucs2_t) << 3); + else + s->slist = (ucs2_t *) realloc((char *) s->slist, + sizeof(ucs2_t) * (s->slist_size + 8)); + s->slist_size += 8; + } + s->slist[s->slist_used++] = v; + + /* + * If the `reducing' parameter is non-zero, flag the element as being on + * the stack. + */ + if (b->reducing != 0) + b->expr[v].onstack = 1; +} + +static ucs2_t +_ure_peek(_ure_buffer_t *b) +{ + if (b == 0 || b->stack.slist_used == 0) + return _URE_NOOP; + + return b->stack.slist[b->stack.slist_used - 1]; +} + +static ucs2_t +_ure_pop(_ure_buffer_t *b) +{ + ucs2_t v; + + if (b == 0 || b->stack.slist_used == 0) + return _URE_NOOP; + + v = b->stack.slist[--b->stack.slist_used]; + if (b->reducing) + b->expr[v].onstack = 0; + + return v; +} + +/************************************************************************* + * + * Start symbol parse functions. + * + *************************************************************************/ + +/* + * Parse a comma-separated list of integers that represent character + * properties. Combine them into a mask that is returned in the `mask' + * variable, and return the number of characters consumed. + */ +static unsigned long +_ure_prop_list(ucs2_t *pp, unsigned long limit, unsigned long *mask, + _ure_buffer_t *b) +{ + unsigned long n, m; + ucs2_t *sp, *ep; + + sp = pp; + ep = sp + limit; + + for (m = n = 0; b->error == _URE_OK && sp < ep; sp++) { + if (*sp == ',') { + /* + * Encountered a comma, so select the next character property flag + * and reset the number. + */ + m |= cclass_flags[n]; + n = 0; + } else if (*sp >= '0' && *sp <= '9') + /* + * Encountered a digit, so start or continue building the cardinal + * that represents the character property flag. + */ + n = (n * 10) + (*sp - '0'); + else + /* + * Encountered something that is not part of the property list. + * Indicate that we are done. + */ + break; + + /* + * If a property number greater than 32 occurs, then there is a + * problem. Most likely a missing comma separator. + */ + if (n > 32) + b->error = _URE_INVALID_PROPERTY; + } + + if (b->error == _URE_OK && n != 0) + m |= cclass_flags[n]; + + /* + * Set the mask that represents the group of character properties. + */ + *mask = m; + + /* + * Return the number of characters consumed. + */ + return sp - pp; +} + +/* + * Collect a hex number with 1 to 4 digits and return the number + * of characters used. + */ +static unsigned long +_ure_hex(ucs2_t *np, unsigned long limit, ucs4_t *n) +{ + ucs2_t i; + ucs2_t *sp, *ep; + ucs4_t nn; + + sp = np; + ep = sp + limit; + + for (nn = 0, i = 0; i < 4 && sp < ep; i++, sp++) { + if (*sp >= '0' && *sp <= '9') + nn = (nn << 4) + (*sp - '0'); + else if (*sp >= 'A' && *sp <= 'F') + nn = (nn << 4) + ((*sp - 'A') + 10); + else if (*sp >= 'a' && *sp <= 'f') + nn = (nn << 4) + ((*sp - 'a') + 10); + else + /* + * Encountered something that is not a hex digit. + */ + break; + } + + /* + * Assign the character code collected and return the number of + * characters used. + */ + *n = nn; + + return sp - np; +} + +/* + * Insert a range into a character class, removing duplicates and ordering + * them in increasing range-start order. + */ +static void +_ure_add_range(_ure_ccl_t *ccl, _ure_range_t *r, _ure_buffer_t *b) +{ + ucs2_t i; + ucs4_t tmp; + _ure_range_t *rp; + + /* + * If the `casefold' flag is set, then make sure both endpoints of the + * range are converted to lower case. + */ + if (b->flags & _URE_DFA_CASEFOLD) { + r->min_code = _ure_tolower(r->min_code); + r->max_code = _ure_tolower(r->max_code); + } + + /* + * Swap the range endpoints if they are not in increasing order. + */ + if (r->min_code > r->max_code) { + tmp = r->min_code; + r->min_code = r->max_code; + r->max_code = tmp; + } + + for (i = 0, rp = ccl->ranges; + i < ccl->ranges_used && r->min_code < rp->min_code; i++, rp++) ; + + /* + * Check for a duplicate. + */ + if (i < ccl->ranges_used && + r->min_code == rp->min_code && r->max_code == rp->max_code) + return; + + if (ccl->ranges_used == ccl->ranges_size) { + if (ccl->ranges_size == 0) + ccl->ranges = (_ure_range_t *) malloc(sizeof(_ure_range_t) << 3); + else + ccl->ranges = (_ure_range_t *) + realloc((char *) ccl->ranges, + sizeof(_ure_range_t) * (ccl->ranges_size + 8)); + ccl->ranges_size += 8; + } + + rp = ccl->ranges + ccl->ranges_used; + + if (i < ccl->ranges_used) + _ure_memmove((char *) (rp + 1), (char *) rp, + sizeof(_ure_range_t) * (ccl->ranges_used - i)); + + ccl->ranges_used++; + rp->min_code = r->min_code; + rp->max_code = r->max_code; +} + +#define _URE_ALPHA_MASK (_URE_UPPER|_URE_LOWER|_URE_OTHERLETTER|\ +_URE_MODIFIER|_URE_TITLE|_URE_NONSPACING|_URE_COMBINING) +#define _URE_ALNUM_MASK (_URE_ALPHA_MASK|_URE_NUMDIGIT) +#define _URE_PUNCT_MASK (_URE_DASHPUNCT|_URE_OPENPUNCT|_URE_CLOSEPUNCT|\ +_URE_OTHERPUNCT) +#define _URE_GRAPH_MASK (_URE_NUMDIGIT|_URE_NUMOTHER|_URE_ALPHA_MASK|\ +_URE_MATHSYM|_URE_CURRENCYSYM|_URE_OTHERSYM) +#define _URE_PRINT_MASK (_URE_GRAPH_MASK|_URE_SPACESEP) +#define _URE_SPACE_MASK (_URE_SPACESEP|_URE_LINESEP|_URE_PARASEP) + +typedef void (*_ure_cclsetup_t)( + _ure_symtab_t *sym, + unsigned long mask, + _ure_buffer_t *b +); + +typedef struct { + ucs2_t key; + unsigned long len; + unsigned long next; + _ure_cclsetup_t func; + unsigned long mask; +} _ure_trie_t; + +static void +_ure_ccl_setup(_ure_symtab_t *sym, unsigned long mask, _ure_buffer_t *b) +{ + sym->props |= mask; +} + +static void +_ure_space_setup(_ure_symtab_t *sym, unsigned long mask, _ure_buffer_t *b) +{ + _ure_range_t range; + + sym->props |= mask; + + /* + * Add the additional characters needed for handling isspace(). + */ + range.min_code = range.max_code = '\t'; + _ure_add_range(&sym->sym.ccl, &range, b); + range.min_code = range.max_code = '\r'; + _ure_add_range(&sym->sym.ccl, &range, b); + range.min_code = range.max_code = '\n'; + _ure_add_range(&sym->sym.ccl, &range, b); + range.min_code = range.max_code = '\f'; + _ure_add_range(&sym->sym.ccl, &range, b); + range.min_code = range.max_code = 0xfeff; + _ure_add_range(&sym->sym.ccl, &range, b); +} + +static void +_ure_xdigit_setup(_ure_symtab_t *sym, unsigned long mask, _ure_buffer_t *b) +{ + _ure_range_t range; + + /* + * Add the additional characters needed for handling isxdigit(). + */ + range.min_code = '0'; + range.max_code = '9'; + _ure_add_range(&sym->sym.ccl, &range, b); + range.min_code = 'A'; + range.max_code = 'F'; + _ure_add_range(&sym->sym.ccl, &range, b); + range.min_code = 'a'; + range.max_code = 'f'; + _ure_add_range(&sym->sym.ccl, &range, b); +} + +static _ure_trie_t cclass_trie[] = { + {0x003a, 1, 1, 0, 0}, + {0x0061, 9, 10, 0, 0}, + {0x0063, 8, 19, 0, 0}, + {0x0064, 7, 24, 0, 0}, + {0x0067, 6, 29, 0, 0}, + {0x006c, 5, 34, 0, 0}, + {0x0070, 4, 39, 0, 0}, + {0x0073, 3, 49, 0, 0}, + {0x0075, 2, 54, 0, 0}, + {0x0078, 1, 59, 0, 0}, + {0x006c, 1, 11, 0, 0}, + {0x006e, 2, 13, 0, 0}, + {0x0070, 1, 16, 0, 0}, + {0x0075, 1, 14, 0, 0}, + {0x006d, 1, 15, 0, 0}, + {0x003a, 1, 16, _ure_ccl_setup, _URE_ALNUM_MASK}, + {0x0068, 1, 17, 0, 0}, + {0x0061, 1, 18, 0, 0}, + {0x003a, 1, 19, _ure_ccl_setup, _URE_ALPHA_MASK}, + {0x006e, 1, 20, 0, 0}, + {0x0074, 1, 21, 0, 0}, + {0x0072, 1, 22, 0, 0}, + {0x006c, 1, 23, 0, 0}, + {0x003a, 1, 24, _ure_ccl_setup, _URE_CNTRL}, + {0x0069, 1, 25, 0, 0}, + {0x0067, 1, 26, 0, 0}, + {0x0069, 1, 27, 0, 0}, + {0x0074, 1, 28, 0, 0}, + {0x003a, 1, 29, _ure_ccl_setup, _URE_NUMDIGIT}, + {0x0072, 1, 30, 0, 0}, + {0x0061, 1, 31, 0, 0}, + {0x0070, 1, 32, 0, 0}, + {0x0068, 1, 33, 0, 0}, + {0x003a, 1, 34, _ure_ccl_setup, _URE_GRAPH_MASK}, + {0x006f, 1, 35, 0, 0}, + {0x0077, 1, 36, 0, 0}, + {0x0065, 1, 37, 0, 0}, + {0x0072, 1, 38, 0, 0}, + {0x003a, 1, 39, _ure_ccl_setup, _URE_LOWER}, + {0x0072, 2, 41, 0, 0}, + {0x0075, 1, 45, 0, 0}, + {0x0069, 1, 42, 0, 0}, + {0x006e, 1, 43, 0, 0}, + {0x0074, 1, 44, 0, 0}, + {0x003a, 1, 45, _ure_ccl_setup, _URE_PRINT_MASK}, + {0x006e, 1, 46, 0, 0}, + {0x0063, 1, 47, 0, 0}, + {0x0074, 1, 48, 0, 0}, + {0x003a, 1, 49, _ure_ccl_setup, _URE_PUNCT_MASK}, + {0x0070, 1, 50, 0, 0}, + {0x0061, 1, 51, 0, 0}, + {0x0063, 1, 52, 0, 0}, + {0x0065, 1, 53, 0, 0}, + {0x003a, 1, 54, _ure_space_setup, _URE_SPACE_MASK}, + {0x0070, 1, 55, 0, 0}, + {0x0070, 1, 56, 0, 0}, + {0x0065, 1, 57, 0, 0}, + {0x0072, 1, 58, 0, 0}, + {0x003a, 1, 59, _ure_ccl_setup, _URE_UPPER}, + {0x0064, 1, 60, 0, 0}, + {0x0069, 1, 61, 0, 0}, + {0x0067, 1, 62, 0, 0}, + {0x0069, 1, 63, 0, 0}, + {0x0074, 1, 64, 0, 0}, + {0x003a, 1, 65, _ure_xdigit_setup, 0}, +}; + +/* + * Probe for one of the POSIX colon delimited character classes in the static + * trie. + */ +static unsigned long +_ure_posix_ccl(ucs2_t *cp, unsigned long limit, _ure_symtab_t *sym, + _ure_buffer_t *b) +{ + int i; + unsigned long n; + _ure_trie_t *tp; + ucs2_t *sp, *ep; + + /* + * If the number of characters left is less than 7, then this cannot be + * interpreted as one of the colon delimited classes. + */ + if (limit < 7) + return 0; + + sp = cp; + ep = sp + limit; + tp = cclass_trie; + for (i = 0; sp < ep && i < 8; i++, sp++) { + n = tp->len; + + for (; n > 0 && tp->key != *sp; tp++, n--) ; + + if (n == 0) + return 0; + + if (*sp == ':' && (i == 6 || i == 7)) { + sp++; + break; + } + if (sp + 1 < ep) + tp = cclass_trie + tp->next; + } + if (tp->func == 0) + return 0; + + (*tp->func)(sym, tp->mask, b); + + return sp - cp; +} + +/* + * Construct a list of ranges and return the number of characters consumed. + */ +static unsigned long +_ure_cclass(ucs2_t *cp, unsigned long limit, _ure_symtab_t *symp, + _ure_buffer_t *b) +{ + int range_end; + unsigned long n; + ucs2_t *sp, *ep; + ucs4_t c, last; + _ure_ccl_t *cclp; + _ure_range_t range; + + sp = cp; + ep = sp + limit; + + if (*sp == '^') { + symp->type = _URE_NCCLASS; + sp++; + } else + symp->type = _URE_CCLASS; + + for (last = 0, range_end = 0; + b->error == _URE_OK && sp < ep && *sp != ']'; ) { + c = *sp++; + if (c == '\\') { + if (sp == ep) { + /* + * The EOS was encountered when expecting the reverse solidus + * to be followed by the character it is escaping. Set an + * error code and return the number of characters consumed up + * to this point. + */ + b->error = _URE_UNEXPECTED_EOS; + return sp - cp; + } + + c = *sp++; + switch (c) { + case 'a': + c = 0x07; + break; + case 'b': + c = 0x08; + break; + case 'f': + c = 0x0c; + break; + case 'n': + c = 0x0a; + break; + case 'r': + c = 0x0d; + break; + case 't': + c = 0x09; + break; + case 'v': + c = 0x0b; + break; + case 'p': + case 'P': + sp += _ure_prop_list(sp, ep - sp, &symp->props, b); + /* + * Invert the bit mask of the properties if this is a negated + * character class or if 'P' is used to specify a list of + * character properties that should *not* match in a + * character class. + */ + if (c == 'P') + symp->props = ~symp->props; + continue; + break; + case 'x': + case 'X': + case 'u': + case 'U': + if (sp < ep && + ((*sp >= '0' && *sp <= '9') || + (*sp >= 'A' && *sp <= 'F') || + (*sp >= 'a' && *sp <= 'f'))) + sp += _ure_hex(sp, ep - sp, &c); + } + } else if (c == ':') { + /* + * Probe for a POSIX colon delimited character class. + */ + sp--; + if ((n = _ure_posix_ccl(sp, ep - sp, symp, b)) == 0) + sp++; + else { + sp += n; + continue; + } + } + + cclp = &symp->sym.ccl; + + /* + * Check to see if the current character is a low surrogate that needs + * to be combined with a preceding high surrogate. + */ + if (last != 0) { + if (c >= 0xdc00 && c <= 0xdfff) + /* + * Construct the UTF16 character code. + */ + c = 0x10000 + (((last & 0x03ff) << 10) | (c & 0x03ff)); + else { + /* + * Add the isolated high surrogate to the range. + */ + if (range_end == 1) + range.max_code = last & 0xffff; + else + range.min_code = range.max_code = last & 0xffff; + + _ure_add_range(cclp, &range, b); + range_end = 0; + } + } + + /* + * Clear the last character code. + */ + last = 0; + + /* + * This slightly awkward code handles the different cases needed to + * construct a range. + */ + if (c >= 0xd800 && c <= 0xdbff) { + /* + * If the high surrogate is followed by a range indicator, simply + * add it as the range start. Otherwise, save it in case the next + * character is a low surrogate. + */ + if (*sp == '-') { + sp++; + range.min_code = c; + range_end = 1; + } else + last = c; + } else if (range_end == 1) { + range.max_code = c; + _ure_add_range(cclp, &range, b); + range_end = 0; + } else { + range.min_code = range.max_code = c; + if (*sp == '-') { + sp++; + range_end = 1; + } else + _ure_add_range(cclp, &range, b); + } + } + + if (sp < ep && *sp == ']') + sp++; + else + /* + * The parse was not terminated by the character class close symbol + * (']'), so set an error code. + */ + b->error = _URE_CCLASS_OPEN; + + return sp - cp; +} + +/* + * Probe for a low surrogate hex code. + */ +static unsigned long +_ure_probe_ls(ucs2_t *ls, unsigned long limit, ucs4_t *c) +{ + ucs4_t i, code; + ucs2_t *sp, *ep; + + for (i = code = 0, sp = ls, ep = sp + limit; i < 4 && sp < ep; sp++) { + if (*sp >= '0' && *sp <= '9') + code = (code << 4) + (*sp - '0'); + else if (*sp >= 'A' && *sp <= 'F') + code = (code << 4) + ((*sp - 'A') + 10); + else if (*sp >= 'a' && *sp <= 'f') + code = (code << 4) + ((*sp - 'a') + 10); + else + break; + } + + *c = code; + return (0xdc00 <= code && code <= 0xdfff) ? sp - ls : 0; +} + +static unsigned long +_ure_compile_symbol(ucs2_t *sym, unsigned long limit, _ure_symtab_t *symp, + _ure_buffer_t *b) +{ + ucs4_t c; + ucs2_t *sp, *ep; + + sp = sym; + ep = sym + limit; + + if ((c = *sp++) == '\\') { + + if (sp == ep) { + /* + * The EOS was encountered when expecting the reverse solidus to + * be followed by the character it is escaping. Set an error code + * and return the number of characters consumed up to this point. + */ + b->error = _URE_UNEXPECTED_EOS; + return sp - sym; + } + + c = *sp++; + switch (c) { + case 'p': + case 'P': + symp->type = (c == 'p') ? _URE_CCLASS : _URE_NCCLASS; + sp += _ure_prop_list(sp, ep - sp, &symp->props, b); + break; + case 'a': + symp->type = _URE_CHAR; + symp->sym.chr = 0x07; + break; + case 'b': + symp->type = _URE_CHAR; + symp->sym.chr = 0x08; + break; + case 'f': + symp->type = _URE_CHAR; + symp->sym.chr = 0x0c; + break; + case 'n': + symp->type = _URE_CHAR; + symp->sym.chr = 0x0a; + break; + case 'r': + symp->type = _URE_CHAR; + symp->sym.chr = 0x0d; + break; + case 't': + symp->type = _URE_CHAR; + symp->sym.chr = 0x09; + break; + case 'v': + symp->type = _URE_CHAR; + symp->sym.chr = 0x0b; + break; + case 'x': + case 'X': + case 'u': + case 'U': + /* + * Collect between 1 and 4 digits representing a UCS2 code. Fall + * through to the next case. + */ + if (sp < ep && + ((*sp >= '0' && *sp <= '9') || + (*sp >= 'A' && *sp <= 'F') || + (*sp >= 'a' && *sp <= 'f'))) + sp += _ure_hex(sp, ep - sp, &c); + /* FALLTHROUGH */ + default: + /* + * Simply add an escaped character here. + */ + symp->type = _URE_CHAR; + symp->sym.chr = c; + } + } else if (c == '^' || c == '$') + /* + * Handle the BOL and EOL anchors. This actually consists simply of + * setting a flag that indicates that the user supplied anchor match + * function should be called. This needs to be done instead of simply + * matching line/paragraph separators because beginning-of-text and + * end-of-text tests are needed as well. + */ + symp->type = (c == '^') ? _URE_BOL_ANCHOR : _URE_EOL_ANCHOR; + else if (c == '[') + /* + * Construct a character class. + */ + sp += _ure_cclass(sp, ep - sp, symp, b); + else if (c == '.') + symp->type = _URE_ANY_CHAR; + else { + symp->type = _URE_CHAR; + symp->sym.chr = c; + } + + /* + * If the symbol type happens to be a character and is a high surrogate, + * then probe forward to see if it is followed by a low surrogate that + * needs to be added. + */ + if (sp < ep && symp->type == _URE_CHAR && + 0xd800 <= symp->sym.chr && symp->sym.chr <= 0xdbff) { + + if (0xdc00 <= *sp && *sp <= 0xdfff) { + symp->sym.chr = 0x10000 + (((symp->sym.chr & 0x03ff) << 10) | + (*sp & 0x03ff)); + sp++; + } else if (*sp == '\\' && (*(sp + 1) == 'x' || *(sp + 1) == 'X' || + *(sp + 1) == 'u' || *(sp + 1) == 'U')) { + sp += _ure_probe_ls(sp + 2, ep - (sp + 2), &c); + if (0xdc00 <= c && c <= 0xdfff) { + /* + * Take into account the \[xu] in front of the hex code. + */ + sp += 2; + symp->sym.chr = 0x10000 + (((symp->sym.chr & 0x03ff) << 10) | + (c & 0x03ff)); + } + } + } + + /* + * Last, make sure any _URE_CHAR type symbols are changed to lower case if + * the `casefold' flag is set. + */ + if ((b->flags & _URE_DFA_CASEFOLD) && symp->type == _URE_CHAR) + symp->sym.chr = _ure_tolower(symp->sym.chr); + + /* + * If the symbol constructed is anything other than one of the anchors, + * make sure the _URE_DFA_BLANKLINE flag is removed. + */ + if (symp->type != _URE_BOL_ANCHOR && symp->type != _URE_EOL_ANCHOR) + b->flags &= ~_URE_DFA_BLANKLINE; + + /* + * Return the number of characters consumed. + */ + return sp - sym; +} + +static int +_ure_sym_neq(_ure_symtab_t *a, _ure_symtab_t *b) +{ + if (a->type != b->type || a->mods != b->mods || a->props != b->props) + return 1; + + if (a->type == _URE_CCLASS || a->type == _URE_NCCLASS) { + if (a->sym.ccl.ranges_used != b->sym.ccl.ranges_used) + return 1; + if (a->sym.ccl.ranges_used > 0 && + memcmp((char *) a->sym.ccl.ranges, (char *) b->sym.ccl.ranges, + sizeof(_ure_range_t) * a->sym.ccl.ranges_used) != 0) + return 1; + } else if (a->type == _URE_CHAR && a->sym.chr != b->sym.chr) + return 1; + return 0; +} + +/* + * Construct a symbol, but only keep unique symbols. + */ +static ucs2_t +_ure_make_symbol(ucs2_t *sym, unsigned long limit, unsigned long *consumed, + _ure_buffer_t *b) +{ + ucs2_t i; + _ure_symtab_t *sp, symbol; + + /* + * Build the next symbol so we can test to see if it is already in the + * symbol table. + */ + (void) memset((char *) &symbol, '\0', sizeof(_ure_symtab_t)); + *consumed = _ure_compile_symbol(sym, limit, &symbol, b); + + /* + * Check to see if the symbol exists. + */ + for (i = 0, sp = b->symtab; + i < b->symtab_used && _ure_sym_neq(&symbol, sp); i++, sp++) ; + + if (i < b->symtab_used) { + /* + * Free up any ranges used for the symbol. + */ + if ((symbol.type == _URE_CCLASS || symbol.type == _URE_NCCLASS) && + symbol.sym.ccl.ranges_size > 0) + free((char *) symbol.sym.ccl.ranges); + + return b->symtab[i].id; + } + + /* + * Need to add the new symbol. + */ + if (b->symtab_used == b->symtab_size) { + if (b->symtab_size == 0) + b->symtab = (_ure_symtab_t *) malloc(sizeof(_ure_symtab_t) << 3); + else + b->symtab = (_ure_symtab_t *) + realloc((char *) b->symtab, + sizeof(_ure_symtab_t) * (b->symtab_size + 8)); + sp = b->symtab + b->symtab_size; + (void) memset((char *) sp, '\0', sizeof(_ure_symtab_t) << 3); + b->symtab_size += 8; + } + + symbol.id = b->symtab_used++; + (void) AC_MEMCPY((char *) &b->symtab[symbol.id], (char *) &symbol, + sizeof(_ure_symtab_t)); + + return symbol.id; +} + +/************************************************************************* + * + * End symbol parse functions. + * + *************************************************************************/ + +static ucs2_t +_ure_make_expr(ucs2_t type, ucs2_t lhs, ucs2_t rhs, _ure_buffer_t *b) +{ + ucs2_t i; + + if (b == 0) + return _URE_NOOP; + + /* + * Determine if the expression already exists or not. + */ + for (i = 0; i < b->expr_used; i++) { + if (b->expr[i].type == type && b->expr[i].lhs == lhs && + b->expr[i].rhs == rhs) + break; + } + if (i < b->expr_used) + return i; + + /* + * Need to add a new expression. + */ + if (b->expr_used == b->expr_size) { + if (b->expr_size == 0) + b->expr = (_ure_elt_t *) malloc(sizeof(_ure_elt_t) << 3); + else + b->expr = (_ure_elt_t *) + realloc((char *) b->expr, + sizeof(_ure_elt_t) * (b->expr_size + 8)); + b->expr_size += 8; + } + + b->expr[b->expr_used].onstack = 0; + b->expr[b->expr_used].type = type; + b->expr[b->expr_used].lhs = lhs; + b->expr[b->expr_used].rhs = rhs; + + return b->expr_used++; +} + +static unsigned char spmap[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +#define _ure_isspecial(cc) ((cc) > 0x20 && (cc) < 0x7f && \ + (spmap[(cc) >> 3] & (1 << ((cc) & 7)))) + +/* + * Convert the regular expression into an NFA in a form that will be easy to + * reduce to a DFA. The starting state for the reduction will be returned. + */ +static ucs2_t +_ure_re2nfa(ucs2_t *re, unsigned long relen, _ure_buffer_t *b) +{ + ucs2_t c, state, top, sym, *sp, *ep; + unsigned long used; + + state = _URE_NOOP; + + sp = re; + ep = sp + relen; + while (b->error == _URE_OK && sp < ep) { + c = *sp++; + switch (c) { + case '(': + _ure_push(_URE_PAREN, b); + break; + case ')': + /* + * Check for the case of too many close parentheses. + */ + if (_ure_peek(b) == _URE_NOOP) { + b->error = _URE_UNBALANCED_GROUP; + break; + } + + while ((top = _ure_peek(b)) == _URE_AND || top == _URE_OR) + /* + * Make an expression with the AND or OR operator and its right + * hand side. + */ + state = _ure_make_expr(_ure_pop(b), _ure_pop(b), state, b); + + /* + * Remove the _URE_PAREN off the stack. + */ + (void) _ure_pop(b); + break; + case '*': + state = _ure_make_expr(_URE_STAR, state, _URE_NOOP, b); + break; + case '+': + state = _ure_make_expr(_URE_PLUS, state, _URE_NOOP, b); + break; + case '?': + state = _ure_make_expr(_URE_QUEST, state, _URE_NOOP, b); + break; + case '|': + while ((top = _ure_peek(b)) == _URE_AND || top == _URE_OR) + /* + * Make an expression with the AND or OR operator and its right + * hand side. + */ + state = _ure_make_expr(_ure_pop(b), _ure_pop(b), state, b); + + _ure_push(state, b); + _ure_push(_URE_OR, b); + break; + default: + sp--; + sym = _ure_make_symbol(sp, ep - sp, &used, b); + sp += used; + state = _ure_make_expr(_URE_SYMBOL, sym, _URE_NOOP, b); + break; + } + + if (c != '(' && c != '|' && sp < ep && + (!_ure_isspecial(*sp) || *sp == '(')) { + _ure_push(state, b); + _ure_push(_URE_AND, b); + } + } + while ((top = _ure_peek(b)) == _URE_AND || top == _URE_OR) + /* + * Make an expression with the AND or OR operator and its right + * hand side. + */ + state = _ure_make_expr(_ure_pop(b), _ure_pop(b), state, b); + + if (b->stack.slist_used > 0) + b->error = _URE_UNBALANCED_GROUP; + + return (b->error == _URE_OK) ? state : _URE_NOOP; +} + +static void +_ure_add_symstate(ucs2_t sym, ucs2_t state, _ure_buffer_t *b) +{ + ucs2_t i, *stp; + _ure_symtab_t *sp; + + /* + * Locate the symbol in the symbol table so the state can be added. + * If the symbol doesn't exist, then a real problem exists. + */ + for (i = 0, sp = b->symtab; i < b->symtab_used && sym != sp->id; + i++, sp++) ; + + /* + * Now find out if the state exists in the symbol's state list. + */ + for (i = 0, stp = sp->states.slist; + i < sp->states.slist_used && state > *stp; i++, stp++) ; + + if (i == sp->states.slist_used || state < *stp) { + /* + * Need to add the state in order. + */ + if (sp->states.slist_used == sp->states.slist_size) { + if (sp->states.slist_size == 0) + sp->states.slist = (ucs2_t *) malloc(sizeof(ucs2_t) << 3); + else + sp->states.slist = (ucs2_t *) + realloc((char *) sp->states.slist, + sizeof(ucs2_t) * (sp->states.slist_size + 8)); + sp->states.slist_size += 8; + } + if (i < sp->states.slist_used) + (void) _ure_memmove((char *) (sp->states.slist + i + 1), + (char *) (sp->states.slist + i), + sizeof(ucs2_t) * (sp->states.slist_used - i)); + sp->states.slist[i] = state; + sp->states.slist_used++; + } +} + +static ucs2_t +_ure_add_state(ucs2_t nstates, ucs2_t *states, _ure_buffer_t *b) +{ + ucs2_t i; + _ure_state_t *sp; + + for (i = 0, sp = b->states.states; i < b->states.states_used; i++, sp++) { + if (sp->st.slist_used == nstates && + memcmp((char *) states, (char *) sp->st.slist, + sizeof(ucs2_t) * nstates) == 0) + break; + } + + if (i == b->states.states_used) { + /* + * Need to add a new DFA state (set of NFA states). + */ + if (b->states.states_used == b->states.states_size) { + if (b->states.states_size == 0) + b->states.states = (_ure_state_t *) + malloc(sizeof(_ure_state_t) << 3); + else + b->states.states = (_ure_state_t *) + realloc((char *) b->states.states, + sizeof(_ure_state_t) * (b->states.states_size + 8)); + sp = b->states.states + b->states.states_size; + (void) memset((char *) sp, '\0', sizeof(_ure_state_t) << 3); + b->states.states_size += 8; + } + + sp = b->states.states + b->states.states_used++; + sp->id = i; + + if (sp->st.slist_used + nstates > sp->st.slist_size) { + if (sp->st.slist_size == 0) + sp->st.slist = (ucs2_t *) + malloc(sizeof(ucs2_t) * (sp->st.slist_used + nstates)); + else + sp->st.slist = (ucs2_t *) + realloc((char *) sp->st.slist, + sizeof(ucs2_t) * (sp->st.slist_used + nstates)); + sp->st.slist_size = sp->st.slist_used + nstates; + } + sp->st.slist_used = nstates; + (void) AC_MEMCPY((char *) sp->st.slist, (char *) states, + sizeof(ucs2_t) * nstates); + } + + /* + * Return the ID of the DFA state representing a group of NFA states. + */ + return i; +} + +static void +_ure_reduce(ucs2_t start, _ure_buffer_t *b) +{ + ucs2_t i, j, state, eval, syms, rhs; + ucs2_t s1, s2, ns1, ns2; + _ure_state_t *sp; + _ure_symtab_t *smp; + + b->reducing = 1; + + /* + * Add the starting state for the reduction. + */ + _ure_add_state(1, &start, b); + + /* + * Process each set of NFA states that get created. + */ + for (i = 0; i < b->states.states_used; i++) { + sp = b->states.states + i; + + /* + * Push the current states on the stack. + */ + for (j = 0; j < sp->st.slist_used; j++) + _ure_push(sp->st.slist[j], b); + + /* + * Reduce the NFA states. + */ + for (j = sp->accepting = syms = 0; j < b->stack.slist_used; j++) { + state = b->stack.slist[j]; + eval = 1; + + /* + * This inner loop is the iterative equivalent of recursively + * reducing subexpressions generated as a result of a reduction. + */ + while (eval) { + switch (b->expr[state].type) { + case _URE_SYMBOL: + ns1 = _ure_make_expr(_URE_ONE, _URE_NOOP, _URE_NOOP, b); + _ure_add_symstate(b->expr[state].lhs, ns1, b); + syms++; + eval = 0; + break; + case _URE_ONE: + sp->accepting = 1; + eval = 0; + break; + case _URE_QUEST: + s1 = b->expr[state].lhs; + ns1 = _ure_make_expr(_URE_ONE, _URE_NOOP, _URE_NOOP, b); + state = _ure_make_expr(_URE_OR, ns1, s1, b); + break; + case _URE_PLUS: + s1 = b->expr[state].lhs; + ns1 = _ure_make_expr(_URE_STAR, s1, _URE_NOOP, b); + state = _ure_make_expr(_URE_AND, s1, ns1, b); + break; + case _URE_STAR: + s1 = b->expr[state].lhs; + ns1 = _ure_make_expr(_URE_ONE, _URE_NOOP, _URE_NOOP, b); + ns2 = _ure_make_expr(_URE_PLUS, s1, _URE_NOOP, b); + state = _ure_make_expr(_URE_OR, ns1, ns2, b); + break; + case _URE_OR: + s1 = b->expr[state].lhs; + s2 = b->expr[state].rhs; + _ure_push(s1, b); + _ure_push(s2, b); + eval = 0; + break; + case _URE_AND: + s1 = b->expr[state].lhs; + s2 = b->expr[state].rhs; + switch (b->expr[s1].type) { + case _URE_SYMBOL: + _ure_add_symstate(b->expr[s1].lhs, s2, b); + syms++; + eval = 0; + break; + case _URE_ONE: + state = s2; + break; + case _URE_QUEST: + ns1 = b->expr[s1].lhs; + ns2 = _ure_make_expr(_URE_AND, ns1, s2, b); + state = _ure_make_expr(_URE_OR, s2, ns2, b); + break; + case _URE_PLUS: + ns1 = b->expr[s1].lhs; + ns2 = _ure_make_expr(_URE_OR, s2, state, b); + state = _ure_make_expr(_URE_AND, ns1, ns2, b); + break; + case _URE_STAR: + ns1 = b->expr[s1].lhs; + ns2 = _ure_make_expr(_URE_AND, ns1, state, b); + state = _ure_make_expr(_URE_OR, s2, ns2, b); + break; + case _URE_OR: + ns1 = b->expr[s1].lhs; + ns2 = b->expr[s1].rhs; + ns1 = _ure_make_expr(_URE_AND, ns1, s2, b); + ns2 = _ure_make_expr(_URE_AND, ns2, s2, b); + state = _ure_make_expr(_URE_OR, ns1, ns2, b); + break; + case _URE_AND: + ns1 = b->expr[s1].lhs; + ns2 = b->expr[s1].rhs; + ns2 = _ure_make_expr(_URE_AND, ns2, s2, b); + state = _ure_make_expr(_URE_AND, ns1, ns2, b); + break; + } + } + } + } + + /* + * Clear the state stack. + */ + while (_ure_pop(b) != _URE_NOOP) ; + + /* + * Reset the state pointer because the reduction may have moved it + * during a reallocation. + */ + sp = b->states.states + i; + + /* + * Generate the DFA states for the symbols collected during the + * current reduction. + */ + if (sp->trans_used + syms > sp->trans_size) { + if (sp->trans_size == 0) + sp->trans = (_ure_elt_t *) + malloc(sizeof(_ure_elt_t) * (sp->trans_used + syms)); + else + sp->trans = (_ure_elt_t *) + realloc((char *) sp->trans, + sizeof(_ure_elt_t) * (sp->trans_used + syms)); + sp->trans_size = sp->trans_used + syms; + } + + /* + * Go through the symbol table and generate the DFA state transitions + * for each symbol that has collected NFA states. + */ + for (j = syms = 0, smp = b->symtab; j < b->symtab_used; j++, smp++) { + sp = b->states.states + i; + + if (smp->states.slist_used > 0) { + sp->trans[syms].lhs = smp->id; + rhs = _ure_add_state(smp->states.slist_used, + smp->states.slist, b); + /* + * Reset the state pointer in case the reallocation moves it + * in memory. + */ + sp = b->states.states + i; + sp->trans[syms].rhs = rhs; + + smp->states.slist_used = 0; + syms++; + } + } + + /* + * Set the number of transitions actually used. + */ + sp->trans_used = syms; + } + b->reducing = 0; +} + +static void +_ure_add_equiv(ucs2_t l, ucs2_t r, _ure_buffer_t *b) +{ + ucs2_t tmp; + + l = b->states.states[l].id; + r = b->states.states[r].id; + + if (l == r) + return; + + if (l > r) { + tmp = l; + l = r; + r = tmp; + } + + /* + * Check to see if the equivalence pair already exists. + */ + for (tmp = 0; tmp < b->equiv_used && + (b->equiv[tmp].l != l || b->equiv[tmp].r != r); + tmp++) ; + + if (tmp < b->equiv_used) + return; + + if (b->equiv_used == b->equiv_size) { + if (b->equiv_size == 0) + b->equiv = (_ure_equiv_t *) malloc(sizeof(_ure_equiv_t) << 3); + else + b->equiv = (_ure_equiv_t *) realloc((char *) b->equiv, + sizeof(_ure_equiv_t) * + (b->equiv_size + 8)); + b->equiv_size += 8; + } + b->equiv[b->equiv_used].l = l; + b->equiv[b->equiv_used].r = r; + b->equiv_used++; +} + +/* + * Merge the DFA states that are equivalent. + */ +static void +_ure_merge_equiv(_ure_buffer_t *b) +{ + ucs2_t i, j, k, eq, done; + _ure_state_t *sp1, *sp2, *ls, *rs; + + for (i = 0; i < b->states.states_used; i++) { + sp1 = b->states.states + i; + if (sp1->id != i) + continue; + for (j = 0; j < i; j++) { + sp2 = b->states.states + j; + if (sp2->id != j) + continue; + b->equiv_used = 0; + _ure_add_equiv(i, j, b); + for (eq = 0, done = 0; eq < b->equiv_used; eq++) { + ls = b->states.states + b->equiv[eq].l; + rs = b->states.states + b->equiv[eq].r; + if (ls->accepting != rs->accepting || + ls->trans_used != rs->trans_used) { + done = 1; + break; + } + for (k = 0; k < ls->trans_used && + ls->trans[k].lhs == rs->trans[k].lhs; k++) ; + if (k < ls->trans_used) { + done = 1; + break; + } + + for (k = 0; k < ls->trans_used; k++) + _ure_add_equiv(ls->trans[k].rhs, rs->trans[k].rhs, b); + } + if (done == 0) + break; + } + for (eq = 0; j < i && eq < b->equiv_used; eq++) + b->states.states[b->equiv[eq].r].id = + b->states.states[b->equiv[eq].l].id; + } + + /* + * Renumber the states appropriately. + */ + for (i = eq = 0, sp1 = b->states.states; i < b->states.states_used; + sp1++, i++) + sp1->id = (sp1->id == i) ? eq++ : b->states.states[sp1->id].id; +} + +/************************************************************************* + * + * API. + * + *************************************************************************/ + +ure_buffer_t +ure_buffer_create(void) +{ + ure_buffer_t b; + + b = (ure_buffer_t) calloc(1, sizeof(_ure_buffer_t)); + + return b; +} + +void +ure_buffer_free(ure_buffer_t buf) +{ + unsigned long i; + + if (buf == 0) + return; + + if (buf->stack.slist_size > 0) + free((char *) buf->stack.slist); + + if (buf->expr_size > 0) + free((char *) buf->expr); + + for (i = 0; i < buf->symtab_size; i++) { + if (buf->symtab[i].states.slist_size > 0) + free((char *) buf->symtab[i].states.slist); + } + + if (buf->symtab_size > 0) + free((char *) buf->symtab); + + for (i = 0; i < buf->states.states_size; i++) { + if (buf->states.states[i].trans_size > 0) + free((char *) buf->states.states[i].trans); + if (buf->states.states[i].st.slist_size > 0) + free((char *) buf->states.states[i].st.slist); + } + + if (buf->states.states_size > 0) + free((char *) buf->states.states); + + if (buf->equiv_size > 0) + free((char *) buf->equiv); + + free((char *) buf); +} + +ure_dfa_t +ure_compile(ucs2_t *re, unsigned long relen, int casefold, ure_buffer_t buf) +{ + ucs2_t i, j, state; + _ure_state_t *sp; + _ure_dstate_t *dsp; + _ure_trans_t *tp; + ure_dfa_t dfa; + + if (re == 0 || *re == 0 || relen == 0 || buf == 0) + return 0; + + /* + * Reset the various fields of the compilation buffer. Default the flags + * to indicate the presense of the "^$" pattern. If any other pattern + * occurs, then this flag will be removed. This is done to catch this + * special pattern and handle it specially when matching. + */ + buf->flags = _URE_DFA_BLANKLINE | ((casefold) ? _URE_DFA_CASEFOLD : 0); + buf->reducing = 0; + buf->stack.slist_used = 0; + buf->expr_used = 0; + + for (i = 0; i < buf->symtab_used; i++) + buf->symtab[i].states.slist_used = 0; + buf->symtab_used = 0; + + for (i = 0; i < buf->states.states_used; i++) { + buf->states.states[i].st.slist_used = 0; + buf->states.states[i].trans_used = 0; + } + buf->states.states_used = 0; + + /* + * Construct the NFA. If this stage returns a 0, then an error occured or + * an empty expression was passed. + */ + if ((state = _ure_re2nfa(re, relen, buf)) == _URE_NOOP) + return 0; + + /* + * Do the expression reduction to get the initial DFA. + */ + _ure_reduce(state, buf); + + /* + * Merge all the equivalent DFA states. + */ + _ure_merge_equiv(buf); + + /* + * Construct the minimal DFA. + */ + dfa = (ure_dfa_t) malloc(sizeof(_ure_dfa_t)); + (void) memset((char *) dfa, '\0', sizeof(_ure_dfa_t)); + + dfa->flags = buf->flags & (_URE_DFA_CASEFOLD|_URE_DFA_BLANKLINE); + + /* + * Free up the NFA state groups and transfer the symbols from the buffer + * to the DFA. + */ + for (i = 0; i < buf->symtab_size; i++) { + if (buf->symtab[i].states.slist_size > 0) + free((char *) buf->symtab[i].states.slist); + } + dfa->syms = buf->symtab; + dfa->nsyms = buf->symtab_used; + + buf->symtab_used = buf->symtab_size = 0; + + /* + * Collect the total number of states and transitions needed for the DFA. + */ + for (i = state = 0, sp = buf->states.states; i < buf->states.states_used; + i++, sp++) { + if (sp->id == state) { + dfa->nstates++; + dfa->ntrans += sp->trans_used; + state++; + } + } + + /* + * Allocate enough space for the states and transitions. + */ + dfa->states = (_ure_dstate_t *) malloc(sizeof(_ure_dstate_t) * + dfa->nstates); + dfa->trans = (_ure_trans_t *) malloc(sizeof(_ure_trans_t) * dfa->ntrans); + + /* + * Actually transfer the DFA states from the buffer. + */ + dsp = dfa->states; + tp = dfa->trans; + for (i = state = 0, sp = buf->states.states; i < buf->states.states_used; + i++, sp++) { + if (sp->id == state) { + dsp->trans = tp; + dsp->ntrans = sp->trans_used; + dsp->accepting = sp->accepting; + + /* + * Add the transitions for the state. + */ + for (j = 0; j < dsp->ntrans; j++, tp++) { + tp->symbol = sp->trans[j].lhs; + tp->next_state = buf->states.states[sp->trans[j].rhs].id; + } + + dsp++; + state++; + } + } + + return dfa; +} + +void +ure_dfa_free(ure_dfa_t dfa) +{ + ucs2_t i; + + if (dfa == 0) + return; + + for (i = 0; i < dfa->nsyms; i++) { + if ((dfa->syms[i].type == _URE_CCLASS || + dfa->syms[i].type == _URE_NCCLASS) && + dfa->syms[i].sym.ccl.ranges_size > 0) + free((char *) dfa->syms[i].sym.ccl.ranges); + } + if (dfa->nsyms > 0) + free((char *) dfa->syms); + + if (dfa->nstates > 0) + free((char *) dfa->states); + if (dfa->ntrans > 0) + free((char *) dfa->trans); + free((char *) dfa); +} + +void +ure_write_dfa(ure_dfa_t dfa, FILE *out) +{ + ucs2_t i, j, k, h, l; + _ure_dstate_t *sp; + _ure_symtab_t *sym; + _ure_range_t *rp; + + if (dfa == 0 || out == 0) + return; + + /* + * Write all the different character classes. + */ + for (i = 0, sym = dfa->syms; i < dfa->nsyms; i++, sym++) { + if (sym->type == _URE_CCLASS || sym->type == _URE_NCCLASS) { + fprintf(out, "C%hd = ", sym->id); + if (sym->sym.ccl.ranges_used > 0) { + putc('[', out); + if (sym->type == _URE_NCCLASS) + putc('^', out); + } + if (sym->props != 0) { + if (sym->type == _URE_NCCLASS) + fprintf(out, "\\P"); + else + fprintf(out, "\\p"); + for (k = h = 0; k < 32; k++) { + if (sym->props & (1 << k)) { + if (h != 0) + putc(',', out); + fprintf(out, "%hd", k + 1); + h = 1; + } + } + } + /* + * Dump the ranges. + */ + for (k = 0, rp = sym->sym.ccl.ranges; + k < sym->sym.ccl.ranges_used; k++, rp++) { + /* + * Check for UTF16 characters. + */ + if (0x10000 <= rp->min_code && + rp->min_code <= 0x10ffff) { + h = (ucs2_t) (((rp->min_code - 0x10000) >> 10) + 0xd800); + l = (ucs2_t) (((rp->min_code - 0x10000) & 1023) + 0xdc00); + fprintf(out, "\\x%04hX\\x%04hX", h, l); + } else + fprintf(out, "\\x%04lX", rp->min_code & 0xffff); + if (rp->max_code != rp->min_code) { + putc('-', out); + if (rp->max_code >= 0x10000 && + rp->max_code <= 0x10ffff) { + h = (ucs2_t) (((rp->max_code - 0x10000) >> 10) + 0xd800); + l = (ucs2_t) (((rp->max_code - 0x10000) & 1023) + 0xdc00); + fprintf(out, "\\x%04hX\\x%04hX", h, l); + } else + fprintf(out, "\\x%04lX", rp->max_code & 0xffff); + } + } + if (sym->sym.ccl.ranges_used > 0) + putc(']', out); + putc('\n', out); + } + } + + for (i = 0, sp = dfa->states; i < dfa->nstates; i++, sp++) { + fprintf(out, "S%hd = ", i); + if (sp->accepting) { + fprintf(out, "1 "); + if (sp->ntrans) + fprintf(out, "| "); + } + for (j = 0; j < sp->ntrans; j++) { + if (j > 0) + fprintf(out, "| "); + + sym = dfa->syms + sp->trans[j].symbol; + switch (sym->type) { + case _URE_CHAR: + if (0x10000 <= sym->sym.chr && sym->sym.chr <= 0x10ffff) { + /* + * Take care of UTF16 characters. + */ + h = (ucs2_t) (((sym->sym.chr - 0x10000) >> 10) + 0xd800); + l = (ucs2_t) (((sym->sym.chr - 0x10000) & 1023) + 0xdc00); + fprintf(out, "\\x%04hX\\x%04hX ", h, l); + } else + fprintf(out, "\\x%04lX ", sym->sym.chr & 0xffff); + break; + case _URE_ANY_CHAR: + fprintf(out, "<any> "); + break; + case _URE_BOL_ANCHOR: + fprintf(out, "<bol-anchor> "); + break; + case _URE_EOL_ANCHOR: + fprintf(out, "<eol-anchor> "); + break; + case _URE_CCLASS: + case _URE_NCCLASS: + fprintf(out, "[C%hd] ", sym->id); + break; + } + fprintf(out, "S%hd", sp->trans[j].next_state); + if (j + 1 < sp->ntrans) + putc(' ', out); + } + putc('\n', out); + } +} + +#define _ure_issep(cc) ((cc) == '\n' || (cc) == '\r' || (cc) == 0x2028 ||\ + (cc) == 0x2029) + +int +ure_exec(ure_dfa_t dfa, int flags, ucs2_t *text, unsigned long textlen, + unsigned long *match_start, unsigned long *match_end) +{ + int i, j, matched, found, skip; + unsigned long ms, me; + ucs4_t c; + ucs2_t *sp, *ep, *lp; + _ure_dstate_t *stp; + _ure_symtab_t *sym; + _ure_range_t *rp; + + if (dfa == 0 || text == 0) + return 0; + + /* + * Handle the special case of an empty string matching the "^$" pattern. + */ + if (textlen == 0 && (dfa->flags & _URE_DFA_BLANKLINE)) { + *match_start = *match_end = 0; + return 1; + } + + sp = text; + ep = sp + textlen; + + ms = me = ~0; + + stp = dfa->states; + + for (found = skip = 0; found == 0 && sp < ep; ) { + lp = sp; + c = *sp++; + + /* + * Check to see if this is a high surrogate that should be + * combined with a following low surrogate. + */ + if (sp < ep && 0xd800 <= c && c <= 0xdbff && + 0xdc00 <= *sp && *sp <= 0xdfff) + c = 0x10000 + (((c & 0x03ff) << 10) | (*sp++ & 0x03ff)); + + /* + * Determine if the character is non-spacing and should be skipped. + */ + if (_ure_matches_properties(_URE_NONSPACING, c) && + (flags & URE_IGNORE_NONSPACING)) { + sp++; + continue; + } + + if (dfa->flags & _URE_DFA_CASEFOLD) + c = _ure_tolower(c); + + /* + * See if one of the transitions matches. + */ + for (i = 0, matched = 0; matched == 0 && i < stp->ntrans; i++) { + sym = dfa->syms + stp->trans[i].symbol; + switch (sym->type) { + case _URE_ANY_CHAR: + if ((flags & URE_DOT_MATCHES_SEPARATORS) || + !_ure_issep(c)) + matched = 1; + break; + case _URE_CHAR: + if (c == sym->sym.chr) + matched = 1; + break; + case _URE_BOL_ANCHOR: + if (lp == text) { + sp = lp; + matched = 1; + } else if (_ure_issep(c)) { + if (c == '\r' && sp < ep && *sp == '\n') + sp++; + lp = sp; + matched = 1; + } + break; + case _URE_EOL_ANCHOR: + if (_ure_issep(c)) { + /* + * Put the pointer back before the separator so the match + * end position will be correct. This case will also + * cause the `sp' pointer to be advanced over the current + * separator once the match end point has been recorded. + */ + sp = lp; + matched = 1; + } + break; + case _URE_CCLASS: + case _URE_NCCLASS: + if (sym->props != 0) + matched = _ure_matches_properties(sym->props, c); + for (j = 0, rp = sym->sym.ccl.ranges; + j < sym->sym.ccl.ranges_used; j++, rp++) { + if (rp->min_code <= c && c <= rp->max_code) + matched = 1; + } + if (sym->type == _URE_NCCLASS) + matched = !matched; + break; + } + + if (matched) { + if (ms == ~0UL) + ms = lp - text; + else + me = sp - text; + stp = dfa->states + stp->trans[i].next_state; + + /* + * If the match was an EOL anchor, adjust the pointer past the + * separator that caused the match. The correct match + * position has been recorded already. + */ + if (sym->type == _URE_EOL_ANCHOR) { + /* + * Skip the character that caused the match. + */ + sp++; + + /* + * Handle the infamous CRLF situation. + */ + if (sp < ep && c == '\r' && *sp == '\n') + sp++; + } + } + } + + if (matched == 0) { + if (stp->accepting == 0) { + /* + * If the last state was not accepting, then reset + * and start over. + */ + stp = dfa->states; + ms = me = ~0; + } else + /* + * The last state was accepting, so terminate the matching + * loop to avoid more work. + */ + found = 1; + } else if (sp == ep) { + if (!stp->accepting) { + /* + * This ugly hack is to make sure the end-of-line anchors + * match when the source text hits the end. This is only done + * if the last subexpression matches. + */ + for (i = 0; found == 0 && i < stp->ntrans; i++) { + sym = dfa->syms + stp->trans[i].symbol; + if (sym->type ==_URE_EOL_ANCHOR) { + stp = dfa->states + stp->trans[i].next_state; + if (stp->accepting) { + me = sp - text; + found = 1; + } else + break; + } + } + } else { + /* + * Make sure any conditions that match all the way to the end + * of the string match. + */ + found = 1; + me = sp - text; + } + } + } + + if (found == 0) + ms = me = ~0; + + *match_start = ms; + *match_end = me; + + return (ms != ~0UL) ? 1 : 0; +} diff --git a/libraries/liblunicode/ure/ure.h b/libraries/liblunicode/ure/ure.h new file mode 100644 index 0000000..cc23694 --- /dev/null +++ b/libraries/liblunicode/ure/ure.h @@ -0,0 +1,154 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Copyright 1997, 1998, 1999 Computing Research Labs, + * New Mexico State University + * + * 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 COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY 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. + */ +/* $Id: ure.h,v 1.2 1999/09/21 15:47:44 mleisher Exp $ */ + +#ifndef _h_ure +#define _h_ure + +#include "portable.h" + + +#include <stdio.h> + +LDAP_BEGIN_DECL + +/* + * Set of character class flags. + */ +#define _URE_NONSPACING 0x00000001 +#define _URE_COMBINING 0x00000002 +#define _URE_NUMDIGIT 0x00000004 +#define _URE_NUMOTHER 0x00000008 +#define _URE_SPACESEP 0x00000010 +#define _URE_LINESEP 0x00000020 +#define _URE_PARASEP 0x00000040 +#define _URE_CNTRL 0x00000080 +#define _URE_PUA 0x00000100 + +#define _URE_UPPER 0x00000200 +#define _URE_LOWER 0x00000400 +#define _URE_TITLE 0x00000800 +#define _URE_MODIFIER 0x00001000 +#define _URE_OTHERLETTER 0x00002000 +#define _URE_DASHPUNCT 0x00004000 +#define _URE_OPENPUNCT 0x00008000 +#define _URE_CLOSEPUNCT 0x00010000 +#define _URE_OTHERPUNCT 0x00020000 +#define _URE_MATHSYM 0x00040000 +#define _URE_CURRENCYSYM 0x00080000 +#define _URE_OTHERSYM 0x00100000 + +#define _URE_LTR 0x00200000 +#define _URE_RTL 0x00400000 + +#define _URE_EURONUM 0x00800000 +#define _URE_EURONUMSEP 0x01000000 +#define _URE_EURONUMTERM 0x02000000 +#define _URE_ARABNUM 0x04000000 +#define _URE_COMMONSEP 0x08000000 + +#define _URE_BLOCKSEP 0x10000000 +#define _URE_SEGMENTSEP 0x20000000 + +#define _URE_WHITESPACE 0x40000000 +#define _URE_OTHERNEUT 0x80000000 + +/* + * Error codes. + */ +#define _URE_OK 0 +#define _URE_UNEXPECTED_EOS -1 +#define _URE_CCLASS_OPEN -2 +#define _URE_UNBALANCED_GROUP -3 +#define _URE_INVALID_PROPERTY -4 + +/* + * Options that can be combined for searching. + */ +#define URE_IGNORE_NONSPACING 0x01 +#define URE_DOT_MATCHES_SEPARATORS 0x02 + +typedef unsigned long ucs4_t; +typedef unsigned short ucs2_t; + +/* + * Opaque type for memory used when compiling expressions. + */ +typedef struct _ure_buffer_t *ure_buffer_t; + +/* + * Opaque type for the minimal DFA used when matching. + */ +typedef struct _ure_dfa_t *ure_dfa_t; + +/************************************************************************* + * + * API. + * + *************************************************************************/ + +LDAP_LUNICODE_F (ure_buffer_t) ure_buffer_create LDAP_P((void)); + +LDAP_LUNICODE_F (void) ure_buffer_free LDAP_P((ure_buffer_t buf)); + +LDAP_LUNICODE_F (ure_dfa_t) +ure_compile LDAP_P((ucs2_t *re, unsigned long relen, + int casefold, ure_buffer_t buf)); + +LDAP_LUNICODE_F (void) ure_dfa_free LDAP_P((ure_dfa_t dfa)); + +LDAP_LUNICODE_F (void) ure_write_dfa LDAP_P((ure_dfa_t dfa, FILE *out)); + +LDAP_LUNICODE_F (int) +ure_exec LDAP_P((ure_dfa_t dfa, int flags, ucs2_t *text, + unsigned long textlen, unsigned long *match_start, + unsigned long *match_end)); + +/************************************************************************* + * + * Prototypes for stub functions used for URE. These need to be rewritten to + * use the Unicode support available on the system. + * + *************************************************************************/ + +LDAP_LUNICODE_F (ucs4_t) _ure_tolower LDAP_P((ucs4_t c)); + +LDAP_LUNICODE_F (int) +_ure_matches_properties LDAP_P((unsigned long props, ucs4_t c)); + +LDAP_END_DECL + +#endif /* _h_ure */ diff --git a/libraries/liblunicode/ure/urestubs.c b/libraries/liblunicode/ure/urestubs.c new file mode 100644 index 0000000..0444fb8 --- /dev/null +++ b/libraries/liblunicode/ure/urestubs.c @@ -0,0 +1,127 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* + * Copyright 1997, 1998, 1999 Computing Research Labs, + * New Mexico State University + * + * 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 COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY 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. + */ +/* $Id: urestubs.c,v 1.2 1999/09/21 15:47:44 mleisher Exp $" */ + +#include "portable.h" +#include <ac/bytes.h> + +#include "ure.h" + +#ifdef _MSC_VER +# include "../ucdata/ucdata.h" +#else +# include "ucdata.h" +#endif + +/* + * This file contains stub routines needed by the URE package to test + * character properties and other Unicode implementation specific details. + */ + +/* + * This routine should return the lower case equivalent for the character or, + * if there is no lower case quivalent, the character itself. + */ +ucs4_t _ure_tolower(ucs4_t c) +{ + return uctoupper(c); +} + +static struct ucmaskmap { + unsigned long mask1; + unsigned long mask2; +} masks[32] = { + { UC_MN, 0 }, /* _URE_NONSPACING */ + { UC_MC, 0 }, /* _URE_COMBINING */ + { UC_ND, 0 }, /* _URE_NUMDIGIT */ + { UC_NL|UC_NO, 0 }, /* _URE_NUMOTHER */ + { UC_ZS, 0 }, /* _URE_SPACESEP */ + { UC_ZL, 0 }, /* _URE_LINESEP */ + { UC_ZP, 0 }, /* _URE_PARASEP */ + { UC_CC, 0 }, /* _URE_CNTRL */ + { UC_CO, 0 }, /* _URE_PUA */ + + { UC_LU, 0 }, /* _URE_UPPER */ + { UC_LL, 0 }, /* _URE_LOWER */ + { UC_LT, 0 }, /* _URE_TITLE */ + { UC_LM, 0 }, /* _URE_MODIFIER */ + { UC_LO, 0 }, /* _URE_OTHERLETTER */ + { UC_PD, 0 }, /* _URE_DASHPUNCT */ + { UC_PS, 0 }, /* _URE_OPENPUNCT */ + { UC_PC, 0 }, /* _URE_CLOSEPUNCT */ + { UC_PO, 0 }, /* _URE_OTHERPUNCT */ + { UC_SM, 0 }, /* _URE_MATHSYM */ + { UC_SC, 0 }, /* _URE_CURRENCYSYM */ + { UC_SO, 0 }, /* _URE_OTHERSYM */ + + { UC_L, 0 }, /* _URE_LTR */ + { UC_R, 0 }, /* _URE_RTL */ + + { 0, UC_EN }, /* _URE_EURONUM */ + { 0, UC_ES }, /* _URE_EURONUMSEP */ + { 0, UC_ET }, /* _URE_EURONUMTERM */ + { 0, UC_AN }, /* _URE_ARABNUM */ + { 0, UC_CS }, /* _URE_COMMONSEP */ + + { 0, UC_B }, /* _URE_BLOCKSEP */ + { 0, UC_S }, /* _URE_SEGMENTSEP */ + + { 0, UC_WS }, /* _URE_WHITESPACE */ + { 0, UC_ON } /* _URE_OTHERNEUT */ +}; + + +/* + * This routine takes a set of URE character property flags (see ure.h) along + * with a character and tests to see if the character has one or more of those + * properties. + */ +int +_ure_matches_properties(unsigned long props, ucs4_t c) +{ + int i; + unsigned long mask1=0, mask2=0; + + for( i=0; i<32; i++ ) { + if( props & (1 << i) ) { + mask1 |= masks[i].mask1; + mask2 |= masks[i].mask2; + } + } + + return ucisprop( c, mask1, mask2 ); +} diff --git a/libraries/liblunicode/utbm/README b/libraries/liblunicode/utbm/README new file mode 100644 index 0000000..8c0212d --- /dev/null +++ b/libraries/liblunicode/utbm/README @@ -0,0 +1,121 @@ +# +# $Id: README,v 1.1 1999/09/21 15:45:17 mleisher Exp $ +# +# Copyright 1997, 1998, 1999 Computing Research Labs, +# New Mexico State University +# +# 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 COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY 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. +# + + Unicode and Boyer-Moore Searching + Version 0.2 + +UTBM (Unicode Tuned Boyer-Moore) is a simple package that provides tuned +Boyer-Moore searches on Unicode UCS2 text (handles high and low surrogates). + +--------------------------------------------------------------------------- + +Assumptions: + + o Search pattern and text already normalized in some fasion. + + o Upper, lower, and title case conversions are one-to-one. + + o For conversions between upper, lower, and title case, UCS2 characters + always convert to other UCS2 characters, and UTF-16 characters always + convert to other UTF-16 characters. + +Flags: + + UTBM provides three processing flags: + + o UTBM_CASEFOLD - search in a case-insensitive manner. + + o UTBM_IGNORE_NONSPACING - ignore non-spacing characters in the pattern and + the text. + + o UTBM_SPACE_COMPRESS - view as a *single space*, sequential groups of + U+2028, U+2029, '\n', '\r', '\t', and any + character identified as a space by the Unicode + support on the platform. + + This flag also causes all characters identified + as control by the Unicode support on the + platform to be ignored (except for '\n', '\r', + and '\t'). + +--------------------------------------------------------------------------- + +Before using UTBM +----------------- +Before UTBM is used, some functions need to be created. The "utbmstub.c" file +contains stubs that need to be rewritten so they work with the Unicode support +on the platform on which this package is being used. + +Using UTBM +---------- + +Sample pseudo-code fragment. + + utbm_pattern_t pat; + ucs2_t *pattern, *text; + unsigned long patternlen, textlen; + unsigned long flags, match_start, match_end; + + /* + * Allocate the dynamic storage needed for a search pattern. + */ + pat = utbm_create_pattern(); + + /* + * Set the search flags desired. + */ + flags = UTBM_CASEFOLD|UTBM_IGNORE_NONSPACING; + + /* + * Compile the search pattern. + */ + utbm_compile(pattern, patternlen, flags, pat); + + /* + * Find the first occurance of the search pattern in the text. + */ + if (utbm_exec(pat, text, textlen, &match_start, &match_end)) + printf("MATCH: %ld %ld\n", match_start, match_end); + + /* + * Free the dynamic storage used for the search pattern. + */ + ure_free_pattern(pat); + +--------------------------------------------------------------------------- + +Mark Leisher <mleisher@crl.nmsu.edu> +2 May 1997 + +=========================================================================== + +CHANGES +------- + +Version: 0.2 +Date : 21 September 1999 +========================== + 1. Added copyright stuff and put in CVS. + diff --git a/libraries/liblunicode/utbm/utbm.c b/libraries/liblunicode/utbm/utbm.c new file mode 100644 index 0000000..ef24746 --- /dev/null +++ b/libraries/liblunicode/utbm/utbm.c @@ -0,0 +1,472 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Copyright 1997, 1998, 1999 Computing Research Labs, + * New Mexico State University + * + * 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 COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY 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. + */ +/* $Id: utbm.c,v 1.1 1999/09/21 15:45:17 mleisher Exp $ */ + +/* + * Assumptions: + * 1. Case conversions of UTF-16 characters must also be UTF-16 characters. + * 2. Case conversions are all one-to-one. + * 3. Text and pattern have already been normalized in some fashion. + */ + +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include "utbm.h" + +/* + * Single pattern character. + */ +typedef struct { + ucs4_t lc; + ucs4_t uc; + ucs4_t tc; +} _utbm_char_t; + +typedef struct { + _utbm_char_t *ch; + unsigned long skip; +} _utbm_skip_t; + +typedef struct _utbm_pattern_t { + unsigned long flags; + + _utbm_char_t *pat; + unsigned long pat_used; + unsigned long pat_size; + unsigned long patlen; + + _utbm_skip_t *skip; + unsigned long skip_used; + unsigned long skip_size; + + unsigned long md4; +} _utbm_pattern_t; + +/************************************************************************* + * + * Support functions. + * + *************************************************************************/ + +/* + * Routine to look up the skip value for a character. + */ +static unsigned long +_utbm_skip(utbm_pattern_t p, ucs2_t *start, ucs2_t *end) +{ + unsigned long i; + ucs4_t c1, c2; + _utbm_skip_t *sp; + + if (start >= end) + return 0; + + c1 = *start; + c2 = (start + 1 < end) ? *(start + 1) : ~0; + if (0xd800 <= c1 && c1 <= 0xdbff && 0xdc00 <= c2 && c2 <= 0xdfff) + c1 = 0x10000 + (((c1 & 0x03ff) << 10) | (c2 & 0x03ff)); + + for (i = 0, sp = p->skip; i < p->skip_used; i++, sp++) { + if (!((c1 ^ sp->ch->uc) & (c1 ^ sp->ch->lc) & (c1 ^ sp->ch->tc))) { + return ((unsigned long) (end - start) < sp->skip) ? + end - start : sp->skip; + } + } + return p->patlen; +} + +static int +_utbm_match(utbm_pattern_t pat, ucs2_t *text, ucs2_t *start, ucs2_t *end, + unsigned long *match_start, unsigned long *match_end) +{ + int check_space; + ucs4_t c1, c2; + unsigned long count; + _utbm_char_t *cp; + + /* + * Set the potential match endpoint first. + */ + *match_end = (start - text) + 1; + + c1 = *start; + c2 = (start + 1 < end) ? *(start + 1) : ~0; + if (0xd800 <= c1 && c1 <= 0xdbff && 0xdc00 <= c2 && c2 <= 0xdfff) { + c1 = 0x10000 + (((c1 & 0x03ff) << 10) | (c2 & 0x03ff)); + /* + * Adjust the match end point to occur after the UTF-16 character. + */ + *match_end = *match_end + 1; + } + + if (pat->pat_used == 1) { + *match_start = start - text; + return 1; + } + + /* + * Compare backward. + */ + cp = pat->pat + (pat->pat_used - 1); + + for (count = pat->patlen; start > text && count > 0;) { + /* + * Ignore non-spacing characters if indicated. + */ + if (pat->flags & UTBM_IGNORE_NONSPACING) { + while (start > text && _utbm_nonspacing(c1)) { + c2 = *--start; + c1 = (start - 1 > text) ? *(start - 1) : ~0; + if (0xdc00 <= c2 && c2 <= 0xdfff && + 0xd800 <= c1 && c1 <= 0xdbff) { + c1 = 0x10000 + (((c1 & 0x03ff) << 10) | (c2 & 0x03ff)); + start--; + } else + c1 = c2; + } + } + + /* + * Handle space compression if indicated. + */ + if (pat->flags & UTBM_SPACE_COMPRESS) { + check_space = 0; + while (start > text && + (_utbm_isspace(c1, 1) || _utbm_iscntrl(c1))) { + check_space = _utbm_isspace(c1, 1); + c2 = *--start; + c1 = (start - 1 > text) ? *(start - 1) : ~0; + if (0xdc00 <= c2 && c2 <= 0xdfff && + 0xd800 <= c1 && c1 <= 0xdbff) { + c1 = 0x10000 + (((c1 & 0x03ff) << 10) | (c2 & 0x03ff)); + start--; + } else + c1 = c2; + } + /* + * Handle things if space compression was indicated and one or + * more member characters were found. + */ + if (check_space) { + if (cp->uc != ' ') + return 0; + cp--; + count--; + } + } + + /* + * Handle the normal comparison cases. + */ + if (count > 0 && ((c1 ^ cp->uc) & (c1 ^ cp->lc) & (c1 ^ cp->tc))) + return 0; + + count -= (c1 >= 0x10000) ? 2 : 1; + if (count > 0) { + cp--; + + /* + * Get the next preceding character. + */ + if (start > text) { + c2 = *--start; + c1 = (start - 1 > text) ? *(start - 1) : ~0; + if (0xdc00 <= c2 && c2 <= 0xdfff && + 0xd800 <= c1 && c1 <= 0xdbff) { + c1 = 0x10000 + (((c1 & 0x03ff) << 10) | (c2 & 0x03ff)); + start--; + } else + c1 = c2; + } + } + } + + /* + * Set the match start position. + */ + *match_start = start - text; + return 1; +} + +/************************************************************************* + * + * API. + * + *************************************************************************/ + +utbm_pattern_t +utbm_create_pattern(void) +{ + utbm_pattern_t p; + + p = (utbm_pattern_t) malloc(sizeof(_utbm_pattern_t)); + (void) memset((char *) p, '\0', sizeof(_utbm_pattern_t)); + return p; +} + +void +utbm_free_pattern(utbm_pattern_t pattern) +{ + if (pattern == 0) + return; + + if (pattern->pat_size > 0) + free((char *) pattern->pat); + + if (pattern->skip_size > 0) + free((char *) pattern->skip); + + free((char *) pattern); +} + +void +utbm_compile(ucs2_t *pat, unsigned long patlen, unsigned long flags, + utbm_pattern_t p) +{ + int have_space; + unsigned long i, j, k, slen; + _utbm_char_t *cp; + _utbm_skip_t *sp; + ucs4_t c1, c2, sentinel; + + if (p == 0 || pat == 0 || *pat == 0 || patlen == 0) + return; + + /* + * Reset the pattern buffer. + */ + p->patlen = p->pat_used = p->skip_used = 0; + + /* + * Set the flags. + */ + p->flags = flags; + + /* + * Initialize the extra skip flag. + */ + p->md4 = 1; + + /* + * Allocate more storage if necessary. + */ + if (patlen > p->pat_size) { + if (p->pat_size == 0) { + p->pat = (_utbm_char_t *) malloc(sizeof(_utbm_char_t) * patlen); + p->skip = (_utbm_skip_t *) malloc(sizeof(_utbm_skip_t) * patlen); + } else { + p->pat = (_utbm_char_t *) + realloc((char *) p->pat, sizeof(_utbm_char_t) * patlen); + p->skip = (_utbm_skip_t *) + realloc((char *) p->skip, sizeof(_utbm_skip_t) * patlen); + } + p->pat_size = p->skip_size = patlen; + } + + /* + * Preprocess the pattern to remove controls (if specified) and determine + * case. + */ + for (have_space = 0, cp = p->pat, i = 0; i < patlen; i++) { + c1 = pat[i]; + c2 = (i + 1 < patlen) ? pat[i + 1] : ~0; + if (0xd800 <= c1 && c1 <= 0xdbff && 0xdc00 <= c2 && c2 <= 0xdfff) + c1 = 0x10000 + (((c1 & 0x03ff) << 10) | (c2 & 0x03ff)); + + /* + * Make sure the `have_space' flag is turned off if the character + * is not an appropriate one. + */ + if (!_utbm_isspace(c1, flags & UTBM_SPACE_COMPRESS)) + have_space = 0; + + /* + * If non-spacing characters should be ignored, do it here. + */ + if ((flags & UTBM_IGNORE_NONSPACING) && _utbm_nonspacing(c1)) + continue; + + /* + * Check if spaces and controls need to be compressed. + */ + if (flags & UTBM_SPACE_COMPRESS) { + if (_utbm_isspace(c1, 1)) { + if (!have_space) { + /* + * Add a space and set the flag. + */ + cp->uc = cp->lc = cp->tc = ' '; + cp++; + + /* + * Increase the real pattern length. + */ + p->patlen++; + sentinel = ' '; + have_space = 1; + } + continue; + } + + /* + * Ignore all control characters. + */ + if (_utbm_iscntrl(c1)) + continue; + } + + /* + * Add the character. + */ + if (flags & UTBM_CASEFOLD) { + cp->uc = _utbm_toupper(c1); + cp->lc = _utbm_tolower(c1); + cp->tc = _utbm_totitle(c1); + } else + cp->uc = cp->lc = cp->tc = c1; + + /* + * Set the sentinel character. + */ + sentinel = cp->uc; + + /* + * Move to the next character. + */ + cp++; + + /* + * Increase the real pattern length appropriately. + */ + p->patlen += (c1 >= 0x10000) ? 2 : 1; + + /* + * Increment the loop index for UTF-16 characters. + */ + i += (c1 >= 0x10000) ? 1 : 0; + + } + + /* + * Set the number of characters actually used. + */ + p->pat_used = cp - p->pat; + + /* + * Go through and construct the skip array and determine the actual length + * of the pattern in UCS2 terms. + */ + slen = p->patlen - 1; + cp = p->pat; + for (i = k = 0; i < p->pat_used; i++, cp++) { + /* + * Locate the character in the skip array. + */ + for (sp = p->skip, j = 0; + j < p->skip_used && sp->ch->uc != cp->uc; j++, sp++) ; + + /* + * If the character is not found, set the new skip element and + * increase the number of skip elements. + */ + if (j == p->skip_used) { + sp->ch = cp; + p->skip_used++; + } + + /* + * Set the updated skip value. If the character is UTF-16 and is + * not the last one in the pattern, add one to its skip value. + */ + sp->skip = slen - k; + if (cp->uc >= 0x10000 && k + 2 < slen) + sp->skip++; + + /* + * Set the new extra skip for the sentinel character. + */ + if (((cp->uc >= 0x10000 && k + 2 <= slen) || k + 1 <= slen) && + cp->uc == sentinel) + p->md4 = slen - k; + + /* + * Increase the actual index. + */ + k += (cp->uc >= 0x10000) ? 2 : 1; + } +} + +int +utbm_exec(utbm_pattern_t pat, ucs2_t *text, unsigned long textlen, + unsigned long *match_start, unsigned long *match_end) +{ + unsigned long k; + ucs2_t *start, *end; + + if (pat == 0 || pat->pat_used == 0 || text == 0 || textlen == 0 || + textlen < pat->patlen) + return 0; + + start = text + pat->patlen; + end = text + textlen; + + /* + * Adjust the start point if it points to a low surrogate. + */ + if (0xdc00 <= *start && *start <= 0xdfff && + 0xd800 <= *(start - 1) && *(start - 1) <= 0xdbff) + start--; + + while (start < end) { + while ((k = _utbm_skip(pat, start, end))) { + start += k; + if (start < end && 0xdc00 <= *start && *start <= 0xdfff && + 0xd800 <= *(start - 1) && *(start - 1) <= 0xdbff) + start--; + } + + if (start < end && + _utbm_match(pat, text, start, end, match_start, match_end)) + return 1; + + start += pat->md4; + if (start < end && 0xdc00 <= *start && *start <= 0xdfff && + 0xd800 <= *(start - 1) && *(start - 1) <= 0xdbff) + start--; + } + return 0; +} diff --git a/libraries/liblunicode/utbm/utbm.h b/libraries/liblunicode/utbm/utbm.h new file mode 100644 index 0000000..59bc206 --- /dev/null +++ b/libraries/liblunicode/utbm/utbm.h @@ -0,0 +1,114 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Copyright 1997, 1998, 1999 Computing Research Labs, + * New Mexico State University + * + * 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 COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY 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. + */ +/* $Id: utbm.h,v 1.1 1999/09/21 15:45:18 mleisher Exp $ */ + +#ifndef _h_utbm +#define _h_utbm + +#include "portable.h" + +LDAP_BEGIN_DECL + +/************************************************************************* + * + * Types. + * + *************************************************************************/ + +/* + * Fundamental character types. + */ +typedef unsigned long ucs4_t; +typedef unsigned short ucs2_t; + +/* + * An opaque type used for the search pattern. + */ +typedef struct _utbm_pattern_t *utbm_pattern_t; + +/************************************************************************* + * + * Flags. + * + *************************************************************************/ + +#define UTBM_CASEFOLD 0x01 +#define UTBM_IGNORE_NONSPACING 0x02 +#define UTBM_SPACE_COMPRESS 0x04 + +/************************************************************************* + * + * API. + * + *************************************************************************/ + +LDAP_LUNICODE_F (utbm_pattern_t) utbm_create_pattern LDAP_P((void)); + +LDAP_LUNICODE_F (void) utbm_free_pattern LDAP_P((utbm_pattern_t pattern)); + +LDAP_LUNICODE_F (void) +utbm_compile LDAP_P((ucs2_t *pat, unsigned long patlen, + unsigned long flags, utbm_pattern_t pattern)); + +LDAP_LUNICODE_F (int) +utbm_exec LDAP_P((utbm_pattern_t pat, ucs2_t *text, + unsigned long textlen, unsigned long *match_start, + unsigned long *match_end)); + +/************************************************************************* + * + * Prototypes for the stub functions needed. + * + *************************************************************************/ + +LDAP_LUNICODE_F (int) _utbm_isspace LDAP_P((ucs4_t c, int compress)); + +LDAP_LUNICODE_F (int) _utbm_iscntrl LDAP_P((ucs4_t c)); + +LDAP_LUNICODE_F (int) _utbm_nonspacing LDAP_P((ucs4_t c)); + +LDAP_LUNICODE_F (ucs4_t) _utbm_tolower LDAP_P((ucs4_t c)); + +LDAP_LUNICODE_F (ucs4_t) _utbm_toupper LDAP_P((ucs4_t c)); + +LDAP_LUNICODE_F (ucs4_t) _utbm_totitle LDAP_P((ucs4_t c)); + +LDAP_END_DECL + +#endif + + +#endif /* _h_utbm */ diff --git a/libraries/liblunicode/utbm/utbmstub.c b/libraries/liblunicode/utbm/utbmstub.c new file mode 100644 index 0000000..2b5bd6d --- /dev/null +++ b/libraries/liblunicode/utbm/utbmstub.c @@ -0,0 +1,105 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Copyright 1997, 1998, 1999 Computing Research Labs, + * New Mexico State University + * + * 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 COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY 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. + */ +/* $Id: utbmstub.c,v 1.1 1999/09/21 15:45:18 mleisher Exp $ */ + +#include "utbm.h" + +/* + * This should be redefined to use the `isspace' function available in the + * Unicode support on the platform where this is being used. + */ +#define _platform_isspace(x) 0 + +/* + * Return non-zero for any character that should be considered the equivalent + * of a space character. Return zero otherwise. + */ +int +_utbm_isspace(ucs4_t c, int compress) +{ + if (compress) + return (c == 0x09 || c == 0x0a || c == 0x0d || + c == 0x2028 || c == 0x2029 || _platform_isspace(c)) ? 1 : 0; + + return _platform_isspace(c); + +} + +/* + * Return non-zero if the character is a control character, or zero otherwise. + */ +int +_utbm_iscntrl(ucs4_t c) +{ + return 0; +} + +/* + * Return non-zero if the character is a non-spacing character, or zero + * otherwise. + */ +int +_utbm_nonspacing(ucs4_t c) +{ + return 0; +} + +/* + * Convert a character to lower case. + */ +ucs4_t +_utbm_tolower(ucs4_t c) +{ + return c; +} + +/* + * Convert a character to upper case. + */ +ucs4_t +_utbm_toupper(ucs4_t c) +{ + return c; +} + +/* + * Convert a character to title case. + */ +ucs4_t +_utbm_totitle(ucs4_t c) +{ + return c; +} diff --git a/libraries/liblutil/Makefile.in b/libraries/liblutil/Makefile.in new file mode 100644 index 0000000..c8306de --- /dev/null +++ b/libraries/liblutil/Makefile.in @@ -0,0 +1,61 @@ +# Makefile for -llutil +# $OpenLDAP$ +## This work is part of OpenLDAP Software <http://www.openldap.org/>. +## +## Copyright 1998-2021 The OpenLDAP Foundation. +## All rights reserved. +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted only as authorized by the OpenLDAP +## Public License. +## +## A copy of this license is available in the file LICENSE in the +## top-level directory of the distribution or, alternatively, at +## <http://www.OpenLDAP.org/license.html>. + +LIBRARY = liblutil.a +PROGRAM = testavl + +LDAP_INCDIR= ../../include +LDAP_LIBDIR= ../../libraries + +NT_SRCS = ntservice.c +NT_OBJS = ntservice.o slapdmsg.res + +UNIX_SRCS = detach.c +UNIX_OBJS = detach.o + +XLIBS = $(LIBRARY) $(LDAP_LIBLBER_LA) + +SRCS = base64.c entropy.c sasl.c signal.c hash.c passfile.c \ + md5.c passwd.c sha1.c getpass.c lockf.c utils.c uuid.c sockpair.c \ + avl.c tavl.c \ + testavl.c \ + meter.c \ + @LIBSRCS@ $(@PLAT@_SRCS) + +OBJS = base64.o entropy.o sasl.o signal.o hash.o passfile.o \ + md5.o passwd.o sha1.o getpass.o lockf.o utils.o uuid.o sockpair.o \ + avl.o tavl.o \ + meter.o \ + @LIBOBJS@ $(@PLAT@_OBJS) + +testavl: $(XLIBS) testavl.o + $(LTLINK) -o $@ testavl.o $(LIBS) + +testtavl: $(XLIBS) testtavl.o + $(LTLINK) -o $@ testtavl.o $(LIBS) + +# These rules are for a Mingw32 build, specifically. +# It's ok for them to be here because the clean rule is harmless, and +# slapdmsg.res won't get built unless it's declared in OBJS. + +slapdmsg.bin: FORCE + @if [ ! -f $@ ]; then cp $(srcdir)/$@ .; fi + +slapdmsg.res: slapdmsg.rc slapdmsg.bin + windres $< -O coff -o $@ + +clean-local: + $(RM) *.res + diff --git a/libraries/liblutil/avl.c b/libraries/liblutil/avl.c new file mode 100644 index 0000000..4105e88 --- /dev/null +++ b/libraries/liblutil/avl.c @@ -0,0 +1,669 @@ +/* avl.c - routines to implement an avl tree */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1993 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ +/* ACKNOWLEDGEMENTS: + * This work was originally developed by the University of Michigan + * (as part of U-MICH LDAP). Additional significant contributors + * include: + * Howard Y. Chu + * Hallvard B. Furuseth + * Kurt D. Zeilenga + */ + +#include "portable.h" + +#include <limits.h> +#include <stdio.h> +#include <ac/stdlib.h> + +#ifdef CSRIMALLOC +#define ber_memalloc malloc +#define ber_memrealloc realloc +#define ber_memfree free +#else +#include "lber.h" +#endif + +#define AVL_INTERNAL +#include "avl.h" + +/* Maximum tree depth this host's address space could support */ +#define MAX_TREE_DEPTH (sizeof(void *) * CHAR_BIT) + +static const int avl_bfs[] = {LH, RH}; + +/* + * avl_insert -- insert a node containing data data into the avl tree + * with root root. fcmp is a function to call to compare the data portion + * of two nodes. it should take two arguments and return <, >, or == 0, + * depending on whether its first argument is <, >, or == its second + * argument (like strcmp, e.g.). fdup is a function to call when a duplicate + * node is inserted. it should return 0, or -1 and its return value + * will be the return value from avl_insert in the case of a duplicate node. + * the function will be called with the original node's data as its first + * argument and with the incoming duplicate node's data as its second + * argument. this could be used, for example, to keep a count with each + * node. + * + * NOTE: this routine may malloc memory + */ +int +avl_insert( Avlnode ** root, void *data, AVL_CMP fcmp, AVL_DUP fdup ) +{ + Avlnode *t, *p, *s, *q, *r; + int a, cmp, ncmp; + + if ( *root == NULL ) { + if (( r = (Avlnode *) ber_memalloc( sizeof( Avlnode ))) == NULL ) { + return( -1 ); + } + r->avl_link[0] = r->avl_link[1] = NULL; + r->avl_data = data; + r->avl_bf = EH; + *root = r; + + return( 0 ); + } + + t = NULL; + s = p = *root; + + /* find insertion point */ + while (1) { + cmp = fcmp( data, p->avl_data ); + if ( cmp == 0 ) + return (*fdup)( p->avl_data, data ); + + cmp = (cmp > 0); + q = p->avl_link[cmp]; + if (q == NULL) { + /* insert */ + if (( q = (Avlnode *) ber_memalloc( sizeof( Avlnode ))) == NULL ) { + return( -1 ); + } + q->avl_link[0] = q->avl_link[1] = NULL; + q->avl_data = data; + q->avl_bf = EH; + + p->avl_link[cmp] = q; + break; + } else if ( q->avl_bf ) { + t = p; + s = q; + } + p = q; + } + + /* adjust balance factors */ + cmp = fcmp( data, s->avl_data ) > 0; + r = p = s->avl_link[cmp]; + a = avl_bfs[cmp]; + + while ( p != q ) { + cmp = fcmp( data, p->avl_data ) > 0; + p->avl_bf = avl_bfs[cmp]; + p = p->avl_link[cmp]; + } + + /* checks and balances */ + + if ( s->avl_bf == EH ) { + s->avl_bf = a; + return 0; + } else if ( s->avl_bf == -a ) { + s->avl_bf = EH; + return 0; + } else if ( s->avl_bf == a ) { + cmp = (a > 0); + ncmp = !cmp; + if ( r->avl_bf == a ) { + /* single rotation */ + p = r; + s->avl_link[cmp] = r->avl_link[ncmp]; + r->avl_link[ncmp] = s; + s->avl_bf = 0; + r->avl_bf = 0; + } else if ( r->avl_bf == -a ) { + /* double rotation */ + p = r->avl_link[ncmp]; + r->avl_link[ncmp] = p->avl_link[cmp]; + p->avl_link[cmp] = r; + s->avl_link[cmp] = p->avl_link[ncmp]; + p->avl_link[ncmp] = s; + + if ( p->avl_bf == a ) { + s->avl_bf = -a; + r->avl_bf = 0; + } else if ( p->avl_bf == -a ) { + s->avl_bf = 0; + r->avl_bf = a; + } else { + s->avl_bf = 0; + r->avl_bf = 0; + } + p->avl_bf = 0; + } + /* Update parent */ + if ( t == NULL ) + *root = p; + else if ( s == t->avl_right ) + t->avl_right = p; + else + t->avl_left = p; + } + + return 0; +} + +void* +avl_delete( Avlnode **root, void* data, AVL_CMP fcmp ) +{ + Avlnode *p, *q, *r, *top; + int side, side_bf, shorter, nside; + + /* parent stack */ + Avlnode *pptr[MAX_TREE_DEPTH]; + unsigned char pdir[MAX_TREE_DEPTH]; + int depth = 0; + + if ( *root == NULL ) + return NULL; + + p = *root; + + while (1) { + side = fcmp( data, p->avl_data ); + if ( !side ) + break; + side = ( side > 0 ); + pdir[depth] = side; + pptr[depth++] = p; + + p = p->avl_link[side]; + if ( p == NULL ) + return p; + } + data = p->avl_data; + + /* If this node has two children, swap so we are deleting a node with + * at most one child. + */ + if ( p->avl_link[0] && p->avl_link[1] ) { + + /* find the immediate predecessor <q> */ + q = p->avl_link[0]; + side = depth; + pdir[depth++] = 0; + while (q->avl_link[1]) { + pdir[depth] = 1; + pptr[depth++] = q; + q = q->avl_link[1]; + } + /* swap links */ + r = p->avl_link[0]; + p->avl_link[0] = q->avl_link[0]; + q->avl_link[0] = r; + + q->avl_link[1] = p->avl_link[1]; + p->avl_link[1] = NULL; + + q->avl_bf = p->avl_bf; + + /* fix stack positions: old parent of p points to q */ + pptr[side] = q; + if ( side ) { + r = pptr[side-1]; + r->avl_link[pdir[side-1]] = q; + } else { + *root = q; + } + /* new parent of p points to p */ + if ( depth-side > 1 ) { + r = pptr[depth-1]; + r->avl_link[1] = p; + } else { + q->avl_link[0] = p; + } + } + + /* now <p> has at most one child, get it */ + q = p->avl_link[0] ? p->avl_link[0] : p->avl_link[1]; + + ber_memfree( p ); + + if ( !depth ) { + *root = q; + return data; + } + + /* set the child into p's parent */ + depth--; + p = pptr[depth]; + side = pdir[depth]; + p->avl_link[side] = q; + + top = NULL; + shorter = 1; + + while ( shorter ) { + p = pptr[depth]; + side = pdir[depth]; + nside = !side; + side_bf = avl_bfs[side]; + + /* case 1: height unchanged */ + if ( p->avl_bf == EH ) { + /* Tree is now heavier on opposite side */ + p->avl_bf = avl_bfs[nside]; + shorter = 0; + + } else if ( p->avl_bf == side_bf ) { + /* case 2: taller subtree shortened, height reduced */ + p->avl_bf = EH; + } else { + /* case 3: shorter subtree shortened */ + if ( depth ) + top = pptr[depth-1]; /* p->parent; */ + else + top = NULL; + /* set <q> to the taller of the two subtrees of <p> */ + q = p->avl_link[nside]; + if ( q->avl_bf == EH ) { + /* case 3a: height unchanged, single rotate */ + p->avl_link[nside] = q->avl_link[side]; + q->avl_link[side] = p; + shorter = 0; + q->avl_bf = side_bf; + p->avl_bf = (- side_bf); + + } else if ( q->avl_bf == p->avl_bf ) { + /* case 3b: height reduced, single rotate */ + p->avl_link[nside] = q->avl_link[side]; + q->avl_link[side] = p; + shorter = 1; + q->avl_bf = EH; + p->avl_bf = EH; + + } else { + /* case 3c: height reduced, balance factors opposite */ + r = q->avl_link[side]; + q->avl_link[side] = r->avl_link[nside]; + r->avl_link[nside] = q; + + p->avl_link[nside] = r->avl_link[side]; + r->avl_link[side] = p; + + if ( r->avl_bf == side_bf ) { + q->avl_bf = (- side_bf); + p->avl_bf = EH; + } else if ( r->avl_bf == (- side_bf)) { + q->avl_bf = EH; + p->avl_bf = side_bf; + } else { + q->avl_bf = EH; + p->avl_bf = EH; + } + r->avl_bf = EH; + q = r; + } + /* a rotation has caused <q> (or <r> in case 3c) to become + * the root. let <p>'s former parent know this. + */ + if ( top == NULL ) { + *root = q; + } else if (top->avl_link[0] == p) { + top->avl_link[0] = q; + } else { + top->avl_link[1] = q; + } + /* end case 3 */ + p = q; + } + if ( !depth ) + break; + depth--; + } /* end while(shorter) */ + + return data; +} + +static int +avl_inapply( Avlnode *root, AVL_APPLY fn, void* arg, int stopflag ) +{ + if ( root == 0 ) + return( AVL_NOMORE ); + + if ( root->avl_left != 0 ) + if ( avl_inapply( root->avl_left, fn, arg, stopflag ) + == stopflag ) + return( stopflag ); + + if ( (*fn)( root->avl_data, arg ) == stopflag ) + return( stopflag ); + + if ( root->avl_right == 0 ) + return( AVL_NOMORE ); + else + return( avl_inapply( root->avl_right, fn, arg, stopflag ) ); +} + +static int +avl_postapply( Avlnode *root, AVL_APPLY fn, void* arg, int stopflag ) +{ + if ( root == 0 ) + return( AVL_NOMORE ); + + if ( root->avl_left != 0 ) + if ( avl_postapply( root->avl_left, fn, arg, stopflag ) + == stopflag ) + return( stopflag ); + + if ( root->avl_right != 0 ) + if ( avl_postapply( root->avl_right, fn, arg, stopflag ) + == stopflag ) + return( stopflag ); + + return( (*fn)( root->avl_data, arg ) ); +} + +static int +avl_preapply( Avlnode *root, AVL_APPLY fn, void* arg, int stopflag ) +{ + if ( root == 0 ) + return( AVL_NOMORE ); + + if ( (*fn)( root->avl_data, arg ) == stopflag ) + return( stopflag ); + + if ( root->avl_left != 0 ) + if ( avl_preapply( root->avl_left, fn, arg, stopflag ) + == stopflag ) + return( stopflag ); + + if ( root->avl_right == 0 ) + return( AVL_NOMORE ); + else + return( avl_preapply( root->avl_right, fn, arg, stopflag ) ); +} + +/* + * avl_apply -- avl tree root is traversed, function fn is called with + * arguments arg and the data portion of each node. if fn returns stopflag, + * the traversal is cut short, otherwise it continues. Do not use -6 as + * a stopflag, as this is what is used to indicate the traversal ran out + * of nodes. + */ + +int +avl_apply( Avlnode *root, AVL_APPLY fn, void* arg, int stopflag, int type ) +{ + switch ( type ) { + case AVL_INORDER: + return( avl_inapply( root, fn, arg, stopflag ) ); + case AVL_PREORDER: + return( avl_preapply( root, fn, arg, stopflag ) ); + case AVL_POSTORDER: + return( avl_postapply( root, fn, arg, stopflag ) ); + default: + fprintf( stderr, "Invalid traversal type %d\n", type ); + return( -1 ); + } + + /* NOTREACHED */ +} + +/* + * avl_prefixapply - traverse avl tree root, applying function fprefix + * to any nodes that match. fcmp is called with data as its first arg + * and the current node's data as its second arg. it should return + * 0 if they match, < 0 if data is less, and > 0 if data is greater. + * the idea is to efficiently find all nodes that are prefixes of + * some key... Like avl_apply, this routine also takes a stopflag + * and will return prematurely if fmatch returns this value. Otherwise, + * AVL_NOMORE is returned. + */ + +int +avl_prefixapply( + Avlnode *root, + void* data, + AVL_CMP fmatch, + void* marg, + AVL_CMP fcmp, + void* carg, + int stopflag +) +{ + int cmp; + + if ( root == 0 ) + return( AVL_NOMORE ); + + cmp = (*fcmp)( data, root->avl_data /* , carg */); + if ( cmp == 0 ) { + if ( (*fmatch)( root->avl_data, marg ) == stopflag ) + return( stopflag ); + + if ( root->avl_left != 0 ) + if ( avl_prefixapply( root->avl_left, data, fmatch, + marg, fcmp, carg, stopflag ) == stopflag ) + return( stopflag ); + + if ( root->avl_right != 0 ) + return( avl_prefixapply( root->avl_right, data, fmatch, + marg, fcmp, carg, stopflag ) ); + else + return( AVL_NOMORE ); + + } else if ( cmp < 0 ) { + if ( root->avl_left != 0 ) + return( avl_prefixapply( root->avl_left, data, fmatch, + marg, fcmp, carg, stopflag ) ); + } else { + if ( root->avl_right != 0 ) + return( avl_prefixapply( root->avl_right, data, fmatch, + marg, fcmp, carg, stopflag ) ); + } + + return( AVL_NOMORE ); +} + +/* + * avl_free -- traverse avltree root, freeing the memory it is using. + * the dfree() is called to free the data portion of each node. The + * number of items actually freed is returned. + */ + +int +avl_free( Avlnode *root, AVL_FREE dfree ) +{ + int nleft, nright; + + if ( root == 0 ) + return( 0 ); + + nleft = nright = 0; + if ( root->avl_left != 0 ) + nleft = avl_free( root->avl_left, dfree ); + + if ( root->avl_right != 0 ) + nright = avl_free( root->avl_right, dfree ); + + if ( dfree ) + (*dfree)( root->avl_data ); + ber_memfree( root ); + + return( nleft + nright + 1 ); +} + +/* + * avl_find -- search avltree root for a node with data data. the function + * cmp is used to compare things. it is called with data as its first arg + * and the current node data as its second. it should return 0 if they match, + * < 0 if arg1 is less than arg2 and > 0 if arg1 is greater than arg2. + */ + +Avlnode * +avl_find2( Avlnode *root, const void *data, AVL_CMP fcmp ) +{ + int cmp; + + while ( root != 0 && (cmp = (*fcmp)( data, root->avl_data )) != 0 ) { + cmp = cmp > 0; + root = root->avl_link[cmp]; + } + return root; +} + +void* +avl_find( Avlnode *root, const void* data, AVL_CMP fcmp ) +{ + int cmp; + + while ( root != 0 && (cmp = (*fcmp)( data, root->avl_data )) != 0 ) { + cmp = cmp > 0; + root = root->avl_link[cmp]; + } + + return( root ? root->avl_data : 0 ); +} + +/* + * avl_find_lin -- search avltree root linearly for a node with data data. + * the function cmp is used to compare things. it is called with data as its + * first arg and the current node data as its second. it should return 0 if + * they match, non-zero otherwise. + */ + +void* +avl_find_lin( Avlnode *root, const void* data, AVL_CMP fcmp ) +{ + void* res; + + if ( root == 0 ) + return( NULL ); + + if ( (*fcmp)( data, root->avl_data ) == 0 ) + return( root->avl_data ); + + if ( root->avl_left != 0 ) + if ( (res = avl_find_lin( root->avl_left, data, fcmp )) + != NULL ) + return( res ); + + if ( root->avl_right == 0 ) + return( NULL ); + else + return( avl_find_lin( root->avl_right, data, fcmp ) ); +} + +/* NON-REENTRANT INTERFACE */ + +static void* *avl_list; +static int avl_maxlist; +static int avl_nextlist; + +#define AVL_GRABSIZE 100 + +/* ARGSUSED */ +static int +avl_buildlist( void* data, void* arg ) +{ + static int slots; + + if ( avl_list == (void* *) 0 ) { + avl_list = (void* *) ber_memalloc(AVL_GRABSIZE * sizeof(void*)); + slots = AVL_GRABSIZE; + avl_maxlist = 0; + } else if ( avl_maxlist == slots ) { + slots += AVL_GRABSIZE; + avl_list = (void* *) ber_memrealloc( (char *) avl_list, + (unsigned) slots * sizeof(void*)); + } + + avl_list[ avl_maxlist++ ] = data; + + return( 0 ); +} + +/* + * avl_getfirst() and avl_getnext() are provided as alternate tree + * traversal methods, to be used when a single function cannot be + * provided to be called with every node in the tree. avl_getfirst() + * traverses the tree and builds a linear list of all the nodes, + * returning the first node. avl_getnext() returns the next thing + * on the list built by avl_getfirst(). This means that avl_getfirst() + * can take a while, and that the tree should not be messed with while + * being traversed in this way, and that multiple traversals (even of + * different trees) cannot be active at once. + */ + +void* +avl_getfirst( Avlnode *root ) +{ + if ( avl_list ) { + ber_memfree( (char *) avl_list); + avl_list = (void* *) 0; + } + avl_maxlist = 0; + avl_nextlist = 0; + + if ( root == 0 ) + return( 0 ); + + (void) avl_apply( root, avl_buildlist, (void*) 0, -1, AVL_INORDER ); + + return( avl_list[ avl_nextlist++ ] ); +} + +void* +avl_getnext( void ) +{ + if ( avl_list == 0 ) + return( 0 ); + + if ( avl_nextlist == avl_maxlist ) { + ber_memfree( (void*) avl_list); + avl_list = (void* *) 0; + return( 0 ); + } + + return( avl_list[ avl_nextlist++ ] ); +} + +/* end non-reentrant code */ + + +int +avl_dup_error( void* left, void* right ) +{ + return( -1 ); +} + +int +avl_dup_ok( void* left, void* right ) +{ + return( 0 ); +} diff --git a/libraries/liblutil/base64.c b/libraries/liblutil/base64.c new file mode 100644 index 0000000..780e964 --- /dev/null +++ b/libraries/liblutil/base64.c @@ -0,0 +1,308 @@ +/* base64.c -- routines to encode/decode base64 data */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * Portions Copyright 1998-2003 Kurt D. Zeilenga. + * Portions Copyright 1995 IBM Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1996, 1998 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ +/* This work is based upon Base64 routines (developed by IBM) found + * Berkeley Internet Name Daemon (BIND) as distributed by ISC. They + * were adapted for inclusion in OpenLDAP Software by Kurt D. Zeilenga. + */ + +#include "portable.h" + +#include <ac/assert.h> +#include <ac/stdlib.h> +#include <ac/ctype.h> +#include <ac/string.h> + +/* include socket.h to get sys/types.h and/or winsock2.h */ +#include <ac/socket.h> + +#include "lutil.h" + +static const char Base64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static const char Pad64 = '='; + +/* (From RFC1521 and draft-ietf-dnssec-secext-03.txt) + The following encoding technique is taken from RFC 1521 by Borenstein + and Freed. It is reproduced here in a slightly edited form for + convenience. + + A 65-character subset of US-ASCII is used, enabling 6 bits to be + represented per printable character. (The extra 65th character, "=", + is used to signify a special processing function.) + + The encoding process represents 24-bit groups of input bits as output + strings of 4 encoded characters. Proceeding from left to right, a + 24-bit input group is formed by concatenating 3 8-bit input groups. + These 24 bits are then treated as 4 concatenated 6-bit groups, each + of which is translated into a single digit in the base64 alphabet. + + Each 6-bit group is used as an index into an array of 64 printable + characters. The character referenced by the index is placed in the + output string. + + Table 1: The Base64 Alphabet + + Value Encoding Value Encoding Value Encoding Value Encoding + 0 A 17 R 34 i 51 z + 1 B 18 S 35 j 52 0 + 2 C 19 T 36 k 53 1 + 3 D 20 U 37 l 54 2 + 4 E 21 V 38 m 55 3 + 5 F 22 W 39 n 56 4 + 6 G 23 X 40 o 57 5 + 7 H 24 Y 41 p 58 6 + 8 I 25 Z 42 q 59 7 + 9 J 26 a 43 r 60 8 + 10 K 27 b 44 s 61 9 + 11 L 28 c 45 t 62 + + 12 M 29 d 46 u 63 / + 13 N 30 e 47 v + 14 O 31 f 48 w (pad) = + 15 P 32 g 49 x + 16 Q 33 h 50 y + + Special processing is performed if fewer than 24 bits are available + at the end of the data being encoded. A full encoding quantum is + always completed at the end of a quantity. When fewer than 24 input + bits are available in an input group, zero bits are added (on the + right) to form an integral number of 6-bit groups. Padding at the + end of the data is performed using the '=' character. + + Since all base64 input is an integral number of octets, only the + ------------------------------------------------- + following cases can arise: + + (1) the final quantum of encoding input is an integral + multiple of 24 bits; here, the final unit of encoded + output will be an integral multiple of 4 characters + with no "=" padding, + (2) the final quantum of encoding input is exactly 8 bits; + here, the final unit of encoded output will be two + characters followed by two "=" padding characters, or + (3) the final quantum of encoding input is exactly 16 bits; + here, the final unit of encoded output will be three + characters followed by one "=" padding character. + */ + +int +lutil_b64_ntop( + u_char const *src, + size_t srclength, + char *target, + size_t targsize) +{ + size_t datalength = 0; + u_char input[3]; + u_char output[4]; + size_t i; + + while (2 < srclength) { + input[0] = *src++; + input[1] = *src++; + input[2] = *src++; + srclength -= 3; + + output[0] = input[0] >> 2; + output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); + output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); + output[3] = input[2] & 0x3f; + assert(output[0] < 64); + assert(output[1] < 64); + assert(output[2] < 64); + assert(output[3] < 64); + + if (datalength + 4 > targsize) + return (-1); + target[datalength++] = Base64[output[0]]; + target[datalength++] = Base64[output[1]]; + target[datalength++] = Base64[output[2]]; + target[datalength++] = Base64[output[3]]; + } + + /* Now we worry about padding. */ + if (0 != srclength) { + /* Get what's left. */ + input[0] = input[1] = input[2] = '\0'; + for (i = 0; i < srclength; i++) + input[i] = *src++; + + output[0] = input[0] >> 2; + output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); + output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); + assert(output[0] < 64); + assert(output[1] < 64); + assert(output[2] < 64); + + if (datalength + 4 > targsize) + return (-1); + target[datalength++] = Base64[output[0]]; + target[datalength++] = Base64[output[1]]; + if (srclength == 1) + target[datalength++] = Pad64; + else + target[datalength++] = Base64[output[2]]; + target[datalength++] = Pad64; + } + if (datalength >= targsize) + return (-1); + target[datalength] = '\0'; /* Returned value doesn't count \0. */ + return (datalength); +} + +/* skips all whitespace anywhere. + converts characters, four at a time, starting at (or after) + src from base - 64 numbers into three 8 bit bytes in the target area. + it returns the number of data bytes stored at the target, or -1 on error. + */ + +int +lutil_b64_pton( + char const *src, + u_char *target, + size_t targsize) +{ + int tarindex, state, ch; + char *pos; + + state = 0; + tarindex = 0; + + while ((ch = *src++) != '\0') { + if (isascii(ch) && isspace(ch)) /* Skip whitespace anywhere. */ + continue; + + if (ch == Pad64) + break; + + pos = strchr(Base64, ch); + if (pos == 0) /* A non-base64 character. */ + return (-1); + + switch (state) { + case 0: + if (target) { + if ((size_t)tarindex >= targsize) + return (-1); + target[tarindex] = (pos - Base64) << 2; + } + state = 1; + break; + case 1: + if (target) { + if ((size_t)tarindex + 1 >= targsize) + return (-1); + target[tarindex] |= (pos - Base64) >> 4; + target[tarindex+1] = ((pos - Base64) & 0x0f) + << 4 ; + } + tarindex++; + state = 2; + break; + case 2: + if (target) { + if ((size_t)tarindex + 1 >= targsize) + return (-1); + target[tarindex] |= (pos - Base64) >> 2; + target[tarindex+1] = ((pos - Base64) & 0x03) + << 6; + } + tarindex++; + state = 3; + break; + case 3: + if (target) { + if ((size_t)tarindex >= targsize) + return (-1); + target[tarindex] |= (pos - Base64); + } + tarindex++; + state = 0; + break; + default: + abort(); + } + } + + /* + * We are done decoding Base-64 chars. Let's see if we ended + * on a byte boundary, and/or with erroneous trailing characters. + */ + + if (ch == Pad64) { /* We got a pad char. */ + ch = *src++; /* Skip it, get next. */ + switch (state) { + case 0: /* Invalid = in first position */ + case 1: /* Invalid = in second position */ + return (-1); + + case 2: /* Valid, means one byte of info */ + /* Skip any number of spaces. */ + for ((void)NULL; ch != '\0'; ch = *src++) + if (! (isascii(ch) && isspace(ch))) + break; + /* Make sure there is another trailing = sign. */ + if (ch != Pad64) + return (-1); + ch = *src++; /* Skip the = */ + /* Fall through to "single trailing =" case. */ + /* FALLTHROUGH */ + + case 3: /* Valid, means two bytes of info */ + /* + * We know this char is an =. Is there anything but + * whitespace after it? + */ + for ((void)NULL; ch != '\0'; ch = *src++) + if (! (isascii(ch) && isspace(ch))) + return (-1); + + /* + * Now make sure for cases 2 and 3 that the "extra" + * bits that slopped past the last full byte were + * zeros. If we don't check them, they become a + * subliminal channel. + */ + if (target && target[tarindex] != 0) + return (-1); + } + } else { + /* + * We ended by seeing the end of the string. Make sure we + * have no partial bytes lying around. + */ + if (state != 0) + return (-1); + } + + return (tarindex); +} diff --git a/libraries/liblutil/detach.c b/libraries/liblutil/detach.c new file mode 100644 index 0000000..b2ce7c0 --- /dev/null +++ b/libraries/liblutil/detach.c @@ -0,0 +1,144 @@ +/* detach.c -- routines to daemonize a process */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* + * Copyright (c) 1990, 1994 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ +/* This work was originally developed by the University of Michigan + * and distributed as part of U-MICH LDAP. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> +#include <ac/signal.h> +#include <ac/socket.h> +#include <ac/unistd.h> + +#include <sys/stat.h> +#include <fcntl.h> + +#ifdef HAVE_SYS_FILE_H +#include <sys/file.h> +#endif +#ifdef HAVE_SYS_IOCTL_H +#include <sys/ioctl.h> +#endif + +#include "lutil.h" + +int +lutil_detach( int debug, int do_close ) +{ + int i, sd, nbits, pid; + +#ifdef HAVE_SYSCONF + nbits = sysconf( _SC_OPEN_MAX ); +#elif defined(HAVE_GETDTABLESIZE) + nbits = getdtablesize(); +#else + nbits = FD_SETSIZE; +#endif + +#ifdef FD_SETSIZE + if ( nbits > FD_SETSIZE ) { + nbits = FD_SETSIZE; + } +#endif /* FD_SETSIZE */ + + if ( debug == 0 ) { + for ( i = 0; i < 5; i++ ) { +#ifdef HAVE_THR + pid = fork1(); +#else + pid = fork(); +#endif + switch ( pid ) + { + case -1: + sleep( 5 ); + continue; + + case 0: + break; + + default: + return pid; + } + break; + } + + if ( (sd = open( "/dev/null", O_RDWR )) == -1 && + (sd = open( "/dev/null", O_RDONLY )) == -1 && + /* Panic -- open *something* */ + (sd = open( "/", O_RDONLY )) == -1 ) { + perror("/dev/null"); + } else { + /* redirect stdin, stdout, stderr to /dev/null */ + dup2( sd, STDIN_FILENO ); + dup2( sd, STDOUT_FILENO ); + dup2( sd, STDERR_FILENO ); + + switch( sd ) { + default: + close( sd ); + case STDIN_FILENO: + case STDOUT_FILENO: + case STDERR_FILENO: + break; + } + } + + if ( do_close ) { + /* close everything else */ + for ( i = 0; i < nbits; i++ ) { + if( i != STDIN_FILENO && + i != STDOUT_FILENO && + i != STDERR_FILENO ) + { + close( i ); + } + } + } + +#ifdef CHDIR_TO_ROOT + (void) chdir( "/" ); +#endif + +#ifdef HAVE_SETSID + (void) setsid(); +#elif defined(TIOCNOTTY) + if ( (sd = open( "/dev/tty", O_RDWR )) != -1 ) { + (void) ioctl( sd, TIOCNOTTY, NULL ); + (void) close( sd ); + } +#endif + } + +#ifdef SIGPIPE + (void) SIGNAL( SIGPIPE, SIG_IGN ); +#endif + return 0; +} diff --git a/libraries/liblutil/entropy.c b/libraries/liblutil/entropy.c new file mode 100644 index 0000000..eb1d740 --- /dev/null +++ b/libraries/liblutil/entropy.c @@ -0,0 +1,170 @@ +/* entropy.c -- routines for providing pseudo-random data */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1999-2021 The OpenLDAP Foundation. + * Portions Copyright 1999-2003 Kurt D. Zeilenga. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* This work was initially developed by Kurt D. Zeilenga for + * inclusion in OpenLDAP Software based, in part, on publically + * available works (as noted below). + */ + +#include "portable.h" + +#include <ac/string.h> +#include <ac/time.h> +#include <ac/unistd.h> + +#ifdef HAVE_PROCESS_H +#include <process.h> +#endif + +#include <fcntl.h> + +#include <lutil.h> +#include <lutil_md5.h> + +/* + * lutil_entropy() provides nbytes of entropy in buf. + * Quality offerred is suitable for one-time uses, such as "once" keys. + * Values may not be suitable for multi-time uses. + * + * Note: Callers are encouraged to provide additional bytes of + * of entropy in the buf argument. This information is used in + * fallback mode to improve the quality of bytes returned. + * + * This routinue should be extended to support additional sources + * of entropy. + */ +int lutil_entropy( unsigned char *buf, ber_len_t nbytes ) +{ + if( nbytes == 0 ) return 0; + +#ifdef URANDOM_DEVICE +#define URANDOM_NREADS 4 + /* Linux and *BSD offer a urandom device */ + { + int rc, fd, n=0; + + fd = open( URANDOM_DEVICE, O_RDONLY ); + + if( fd < 0 ) return -1; + + do { + rc = read( fd, buf, nbytes ); + if( rc <= 0 ) break; + + buf+=rc; + nbytes-=rc; + + if( ++n >= URANDOM_NREADS ) break; + } while( nbytes > 0 ); + + close(fd); + return nbytes > 0 ? -1 : 0; + } +#elif defined(PROV_RSA_FULL) + { + /* Not used since _WIN32_WINNT not set... */ + HCRYPTPROV hProv = 0; + + /* Get handle to user default provider */ + if(!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 0)) { + return -1; + } + + /* Generate random initialization vector */ + if(!CryptGenRandom(hProv, (DWORD) nbytes, (BYTE *) buf)) { + return -1; + } + + /* Release provider handle */ + if(hProv != 0) CryptReleaseContext(hProv, 0); + + return 0; + } +#else + { + /* based upon Phil Karn's "practical randomness" idea + * but implementation 100% OpenLDAP. So don't blame Phil. + * + * Worse case is that this is a MD5 hash of a counter, if + * MD5 is a strong cryptographic hash, this should be fairly + * resistant to attack + */ + + /* + * the caller may need to provide external synchronization OR + * provide entropy (in buf) to ensure quality results as + * access to this counter may not be atomic. + */ + static int counter = 0; + ber_len_t n; + + struct rdata_s { + int counter; + + unsigned char *buf; + struct rdata_s *stack; + + pid_t pid; + +#ifdef HAVE_GETTIMEOFDAY + struct timeval tv; +#else + time_t time; +#endif + + unsigned long junk; /* purposely not initialized */ + } rdata; + + /* make sure rdata differs for each process */ + rdata.pid = getpid(); + + /* make sure rdata differs for each program */ + rdata.buf = buf; + rdata.stack = &rdata; + + for( n = 0; n < nbytes; n += 16 ) { + struct lutil_MD5Context ctx; + unsigned char digest[16]; + + /* poor resolution */ +#ifdef HAVE_GETTIMEOFDAY + (void) gettimeofday( &rdata.tv, NULL ); +#else + (void) time( &rdata.time ); +#endif + + /* make sure rdata differs */ + rdata.counter = ++counter; + rdata.pid++; + rdata.junk++; + + lutil_MD5Init( &ctx ); + lutil_MD5Update( &ctx, (unsigned char *) &rdata, sizeof( rdata ) ); + + /* allow caller to provided additional entropy */ + lutil_MD5Update( &ctx, buf, nbytes ); + + lutil_MD5Final( digest, &ctx ); + + AC_MEMCPY( &buf[n], digest, + nbytes - n >= 16 ? 16 : nbytes - n ); + } + + return 0; + } +#endif + return -1; +} diff --git a/libraries/liblutil/getopt.c b/libraries/liblutil/getopt.c new file mode 100644 index 0000000..5fc94e3 --- /dev/null +++ b/libraries/liblutil/getopt.c @@ -0,0 +1,136 @@ +/* getopt.c -- replacement getopt(3) routines */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * Portions Copyright 1998-2003 Kurt D. Zeilenga. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* This work is based upon the public-domain getopt(3) routines + * developed by AT&T. Modified by Kurt D. Zeilenga for inclusion + * into OpenLDAP Software. Significant contributors include: + * Howard Chu + */ + +#include "portable.h" + +#ifndef HAVE_GETOPT + +#include <stdio.h> + +#include <ac/string.h> +#include <ac/unistd.h> + +#ifdef HAVE_IO_H +#include <io.h> +#endif + +#include "lutil.h" + +#ifndef STDERR_FILENO +#define STDERR_FILENO 2 +#endif + +int opterr = 1; +int optind = 1; +int optopt; +char * optarg; + +#ifdef HAVE_EBCDIC +extern int _trans_argv; +#endif + +static void ERR (char * const argv[], const char * s, char c) +{ +#ifdef DF_TRACE_DEBUG +printf("DF_TRACE_DEBUG: static void ERR () in getopt.c\n"); +#endif + if (opterr) + { + char *ptr, outbuf[4096]; + + ptr = lutil_strncopy(outbuf, argv[0], sizeof(outbuf) - 2); + ptr = lutil_strncopy(ptr, s, sizeof(outbuf)-2 -(ptr-outbuf)); + *ptr++ = c; + *ptr++ = '\n'; +#ifdef HAVE_EBCDIC + __atoe_l(outbuf, ptr - outbuf); +#endif + (void) write(STDERR_FILENO,outbuf,ptr - outbuf); + } +} + +int getopt (int argc, char * const argv [], const char * opts) +{ + static int sp = 1, error = (int) '?'; + static char sw = '-', eos = '\0', arg = ':'; + register char c, * cp; + +#ifdef DF_TRACE_DEBUG +printf("DF_TRACE_DEBUG: int getopt () in getopt.c\n"); +#endif + +#ifdef HAVE_EBCDIC + if (_trans_argv) { + int i; + for (i=0; i<argc; i++) __etoa(argv[i]); + _trans_argv = 0; + } +#endif + if (sp == 1) + { + if (optind >= argc || argv[optind][0] != sw + || argv[optind][1] == eos) + return EOF; + else if (strcmp(argv[optind],"--") == 0) + { + optind++; + return EOF; + } + } + c = argv[optind][sp]; + optopt = (int) c; + if (c == arg || (cp = strchr(opts,c)) == NULL) + { + ERR(argv,_(": illegal option--"),c); + if (argv[optind][++sp] == eos) + { + optind++; + sp = 1; + } + return error; + } + else if (*++cp == arg) + { + if (argv[optind][sp + 1] != eos) + optarg = &argv[optind++][sp + 1]; + else if (++optind >= argc) + { + ERR(argv,_(": option requires an argument--"),c); + sp = 1; + return error; + } + else + optarg = argv[optind++]; + sp = 1; + } + else + { + if (argv[optind][++sp] == eos) + { + sp = 1; + optind++; + } + optarg = NULL; + } + return (int) c; +} +#endif /* HAVE_GETOPT */ diff --git a/libraries/liblutil/getpass.c b/libraries/liblutil/getpass.c new file mode 100644 index 0000000..7f75f6f --- /dev/null +++ b/libraries/liblutil/getpass.c @@ -0,0 +1,130 @@ +/* getpass.c -- get password from user */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * Portions Copyright 1998-2003 Kurt D. Zeilenga. + * Portions Copyright 2009 Howard Chu. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1992, 1993 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ +/* This work was originally developed by the University of Michigan + * and distributed as part of U-MICH LDAP. It was adapted for use in + * -llutil by Kurt D. Zeilenga and subsequently rewritten by Howard Chu. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/ctype.h> +#include <ac/signal.h> +#include <ac/string.h> +#include <ac/termios.h> +#include <ac/time.h> +#include <ac/unistd.h> + +#ifndef HAVE_GETPASSPHRASE + +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif + +#ifdef HAVE_CONIO_H +#include <conio.h> +#endif + +#include <lber.h> +#include <ldap.h> + +#include "ldap_defaults.h" + +#define PBUF 512 + +#ifdef HAVE_WINSOCK +#define TTY "con:" +#else +#define TTY "/dev/tty" +#endif + +char * +lutil_getpass( const char *prompt ) +{ + static char pbuf[PBUF]; + FILE *fi; + int c; + unsigned i; +#if defined(HAVE_TERMIOS_H) || defined(HAVE_SGTTY_H) + TERMIO_TYPE ttyb; + TERMFLAG_TYPE flags; + RETSIGTYPE (*sig)( int sig ); +#endif + + if( prompt == NULL ) prompt = _("Password: "); + +#ifdef DEBUG + if (debug & D_TRACE) + printf("->getpass(%s)\n", prompt); +#endif + +#if defined(HAVE_TERMIOS_H) || defined(HAVE_SGTTY_H) + if ((fi = fopen(TTY, "r")) == NULL) + fi = stdin; + else + setbuf(fi, (char *)NULL); + if (fi != stdin) { + if (GETATTR(fileno(fi), &ttyb) < 0) + perror("GETATTR"); + sig = SIGNAL (SIGINT, SIG_IGN); + flags = GETFLAGS( ttyb ); + SETFLAGS( ttyb, flags & ~ECHO ); + if (SETATTR(fileno(fi), &ttyb) < 0) + perror("SETATTR"); + } +#else + fi = stdin; +#endif + fprintf(stderr, "%s", prompt); + fflush(stderr); + i = 0; + while ( (c = getc(fi)) != EOF && c != '\n' && c != '\r' ) + if ( i < (sizeof(pbuf)-1) ) + pbuf[i++] = c; +#if defined(HAVE_TERMIOS_H) || defined(HAVE_SGTTY_H) + /* tidy up */ + if (fi != stdin) { + fprintf(stderr, "\n"); + fflush(stderr); + SETFLAGS( ttyb, flags ); + if (SETATTR(fileno(fi), &ttyb) < 0) + perror("SETATTR"); + (void) SIGNAL (SIGINT, sig); + (void) fclose(fi); + } +#endif + if ( c == EOF ) + return( NULL ); + pbuf[i] = '\0'; + return (pbuf); +} + +#endif /* !NEED_GETPASSPHRASE */ diff --git a/libraries/liblutil/getpeereid.c b/libraries/liblutil/getpeereid.c new file mode 100644 index 0000000..e87f6df --- /dev/null +++ b/libraries/liblutil/getpeereid.c @@ -0,0 +1,220 @@ +/* getpeereid.c */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2000-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE 1 /* Needed for glibc struct ucred */ +#endif + +#include "portable.h" + +#ifndef HAVE_GETPEEREID + +#include <sys/types.h> +#include <ac/unistd.h> + +#include <ac/socket.h> +#include <ac/errno.h> + +#ifdef HAVE_GETPEERUCRED +#include <ucred.h> +#endif + +#ifdef LDAP_PF_LOCAL_SENDMSG +#include <lber.h> +#ifdef HAVE_SYS_UIO_H +#include <sys/uio.h> +#endif +#include <sys/stat.h> +#endif + +#ifdef HAVE_SYS_UCRED_H +#ifdef HAVE_GRP_H +#include <grp.h> /* for NGROUPS on Tru64 5.1 */ +#endif +#include <sys/ucred.h> +#endif + +#include <stdlib.h> + +int lutil_getpeereid( int s, uid_t *euid, gid_t *egid +#ifdef LDAP_PF_LOCAL_SENDMSG + , struct berval *peerbv +#endif + ) +{ +#ifdef LDAP_PF_LOCAL +#if defined( HAVE_GETPEERUCRED ) + ucred_t *uc = NULL; + if( getpeerucred( s, &uc ) == 0 ) { + *euid = ucred_geteuid( uc ); + *egid = ucred_getegid( uc ); + ucred_free( uc ); + return 0; + } + +#elif defined( SO_PEERCRED ) + struct ucred peercred; + ber_socklen_t peercredlen = sizeof peercred; + + if(( getsockopt( s, SOL_SOCKET, SO_PEERCRED, + (void *)&peercred, &peercredlen ) == 0 ) + && ( peercredlen == sizeof peercred )) + { + *euid = peercred.uid; + *egid = peercred.gid; + return 0; + } + +#elif defined( LOCAL_PEERCRED ) + struct xucred peercred; + ber_socklen_t peercredlen = sizeof peercred; + + if(( getsockopt( s, LOCAL_PEERCRED, 1, + (void *)&peercred, &peercredlen ) == 0 ) + && ( peercred.cr_version == XUCRED_VERSION )) + { + *euid = peercred.cr_uid; + *egid = peercred.cr_gid; + return 0; + } +#elif defined( LDAP_PF_LOCAL_SENDMSG ) && defined( MSG_WAITALL ) + int err, fd; + struct iovec iov; + struct msghdr msg = {0}; +# ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL +# ifndef CMSG_SPACE +# define CMSG_SPACE(len) (_CMSG_ALIGN(sizeof(struct cmsghdr)) + _CMSG_ALIGN(len)) +# endif +# ifndef CMSG_LEN +# define CMSG_LEN(len) (_CMSG_ALIGN(sizeof(struct cmsghdr)) + (len)) +# endif + struct { + struct cmsghdr cm; + int fd; + } control_st; + struct cmsghdr *cmsg; +# endif /* HAVE_STRUCT_MSGHDR_MSG_CONTROL */ + struct stat st; + struct sockaddr_un lname, rname; + ber_socklen_t llen, rlen; + + rlen = sizeof(rname); + llen = sizeof(lname); + memset( &lname, 0, sizeof( lname )); + getsockname(s, (struct sockaddr *)&lname, &llen); + + iov.iov_base = peerbv->bv_val; + iov.iov_len = peerbv->bv_len; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + peerbv->bv_len = 0; + +# ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL + msg.msg_control = &control_st; + msg.msg_controllen = sizeof( struct cmsghdr ) + sizeof( int ); /* no padding! */ + + cmsg = CMSG_FIRSTHDR( &msg ); +# else + msg.msg_accrights = (char *)&fd; + msg.msg_accrightslen = sizeof(fd); +# endif + + /* + * AIX returns a bogus file descriptor if recvmsg() is + * called with MSG_PEEK (is this a bug?). Hence we need + * to receive the Abandon PDU. + */ + err = recvmsg( s, &msg, MSG_WAITALL ); + if( err >= 0 && +# ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL + cmsg->cmsg_len == CMSG_LEN( sizeof(int) ) && + cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_RIGHTS +# else + msg.msg_accrightslen == sizeof(int) +# endif /* HAVE_STRUCT_MSGHDR_MSG_CONTROL*/ + ) { + int mode = S_IFIFO|S_ISUID|S_IRWXU; + + /* We must receive a valid descriptor, it must be a pipe, + * it must only be accessible by its owner, and it must + * have the name of our socket written on it. + */ + peerbv->bv_len = err; +# ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL + fd = (*(int *)CMSG_DATA( cmsg )); +# endif + err = fstat( fd, &st ); + if ( err == 0 ) + rlen = read(fd, &rname, rlen); + close(fd); + if( err == 0 && st.st_mode == mode && + llen == rlen && !memcmp(&lname, &rname, llen)) + { + *euid = st.st_uid; + *egid = st.st_gid; + return 0; + } + } +#elif defined(SOCKCREDSIZE) + struct msghdr msg; + ber_socklen_t crmsgsize; + void *crmsg; + struct cmsghdr *cmp; + struct sockcred *sc; + + memset(&msg, 0, sizeof msg); + crmsgsize = CMSG_SPACE(SOCKCREDSIZE(NGROUPS)); + if (crmsgsize == 0) goto sc_err; + crmsg = malloc(crmsgsize); + if (crmsg == NULL) goto sc_err; + memset(crmsg, 0, crmsgsize); + + msg.msg_control = crmsg; + msg.msg_controllen = crmsgsize; + + if (recvmsg(s, &msg, 0) < 0) { + free(crmsg); + goto sc_err; + } + + if (msg.msg_controllen == 0 || (msg.msg_flags & MSG_CTRUNC) != 0) { + free(crmsg); + goto sc_err; + } + + cmp = CMSG_FIRSTHDR(&msg); + if (cmp->cmsg_level != SOL_SOCKET || cmp->cmsg_type != SCM_CREDS) { + printf("nocreds\n"); + goto sc_err; + } + + sc = (struct sockcred *)(void *)CMSG_DATA(cmp); + + *euid = sc->sc_euid; + *egid = sc->sc_egid; + + free(crmsg); + return 0; + +sc_err: +#endif +#endif /* LDAP_PF_LOCAL */ + + return -1; +} + +#endif /* HAVE_GETPEEREID */ diff --git a/libraries/liblutil/hash.c b/libraries/liblutil/hash.c new file mode 100644 index 0000000..6b3abb2 --- /dev/null +++ b/libraries/liblutil/hash.c @@ -0,0 +1,77 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2000-2021 The OpenLDAP Foundation. + * Portions Copyright 2000-2003 Kurt D. Zeilenga. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +/* This implements the Fowler / Noll / Vo (FNV-1) hash algorithm. + * A summary of the algorithm can be found at: + * http://www.isthe.com/chongo/tech/comp/fnv/index.html + */ + +#include "portable.h" + +#include <lutil_hash.h> + +/* offset and prime for 32-bit FNV-1 */ +#define HASH_OFFSET 0x811c9dc5U +#define HASH_PRIME 16777619 + + +/* + * Initialize context + */ +void +lutil_HASHInit( struct lutil_HASHContext *ctx ) +{ + ctx->hash = HASH_OFFSET; +} + +/* + * Update hash + */ +void +lutil_HASHUpdate( + struct lutil_HASHContext *ctx, + const unsigned char *buf, + ber_len_t len ) +{ + const unsigned char *p, *e; + ber_uint_t h; + + p = buf; + e = &buf[len]; + + h = ctx->hash; + + while( p < e ) { + h *= HASH_PRIME; + h ^= *p++; + } + + ctx->hash = h; +} + +/* + * Save hash + */ +void +lutil_HASHFinal( unsigned char *digest, struct lutil_HASHContext *ctx ) +{ + ber_uint_t h = ctx->hash; + + digest[0] = h & 0xffU; + digest[1] = (h>>8) & 0xffU; + digest[2] = (h>>16) & 0xffU; + digest[3] = (h>>24) & 0xffU; +} diff --git a/libraries/liblutil/lockf.c b/libraries/liblutil/lockf.c new file mode 100644 index 0000000..8bdb1fe --- /dev/null +++ b/libraries/liblutil/lockf.c @@ -0,0 +1,118 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +/* + * File Locking Routines + * + * Implementations (in order of preference) + * - lockf + * - fcntl + * - flock + * + * Other implementations will be added as needed. + * + * NOTE: lutil_lockf() MUST block until an exclusive lock is acquired. + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/unistd.h> + +#undef LOCK_API + +#if defined(HAVE_LOCKF) && defined(F_LOCK) +# define USE_LOCKF 1 +# define LOCK_API "lockf" +#endif + +#if !defined(LOCK_API) && defined(HAVE_FCNTL) +# ifdef HAVE_FCNTL_H +# include <fcntl.h> +# endif +# ifdef F_WRLCK +# define USE_FCNTL 1 +# define LOCK_API "fcntl" +# endif +#endif + +#if !defined(LOCK_API) && defined(HAVE_FLOCK) +# ifdef HAVE_SYS_FILE_H +# include <sys/file.h> +# endif +# define USE_FLOCK 1 +# define LOCK_API "flock" +#endif + +#if !defined(USE_LOCKF) && !defined(USE_FCNTL) && !defined(USE_FLOCK) +int lutil_lockf ( int fd ) { + fd = fd; + return 0; +} + +int lutil_unlockf ( int fd ) { + fd = fd; + return 0; +} +#endif + +#ifdef USE_LOCKF +int lutil_lockf ( int fd ) { + /* use F_LOCK instead of F_TLOCK, ie: block */ + return lockf( fd, F_LOCK, 0 ); +} + +int lutil_unlockf ( int fd ) { + return lockf( fd, F_ULOCK, 0 ); +} +#endif + +#ifdef USE_FCNTL +int lutil_lockf ( int fd ) { + struct flock file_lock; + + memset( &file_lock, '\0', sizeof( file_lock ) ); + file_lock.l_type = F_WRLCK; + file_lock.l_whence = SEEK_SET; + file_lock.l_start = 0; + file_lock.l_len = 0; + + /* use F_SETLKW instead of F_SETLK, ie: block */ + return( fcntl( fd, F_SETLKW, &file_lock ) ); +} + +int lutil_unlockf ( int fd ) { + struct flock file_lock; + + memset( &file_lock, '\0', sizeof( file_lock ) ); + file_lock.l_type = F_UNLCK; + file_lock.l_whence = SEEK_SET; + file_lock.l_start = 0; + file_lock.l_len = 0; + + return( fcntl ( fd, F_SETLKW, &file_lock ) ); +} +#endif + +#ifdef USE_FLOCK +int lutil_lockf ( int fd ) { + /* use LOCK_EX instead of LOCK_EX|LOCK_NB, ie: block */ + return flock( fd, LOCK_EX ); +} + +int lutil_unlockf ( int fd ) { + return flock( fd, LOCK_UN ); +} +#endif diff --git a/libraries/liblutil/md5.c b/libraries/liblutil/md5.c new file mode 100644 index 0000000..f66ae80 --- /dev/null +++ b/libraries/liblutil/md5.c @@ -0,0 +1,332 @@ +/* md5.c -- MD5 message-digest algorithm */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* This work was adapted for inclusion in OpenLDAP Software by + * Kurt D. Zeilenga based upon code developed by Colin Plumb + * and subsequently modified by Jim Kingdon. + */ + +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ + +/* This code was modified in 1997 by Jim Kingdon of Cyclic Software to + not require an integer type which is exactly 32 bits. This work + draws on the changes for the same purpose by Tatu Ylonen + <ylo@cs.hut.fi> as part of SSH, but since I didn't actually use + that code, there is no copyright issue. I hereby disclaim + copyright in any changes I have made; this code remains in the + public domain. */ + +#include "portable.h" + +#include <ac/string.h> + +/* include socket.h to get sys/types.h and/or winsock2.h */ +#include <ac/socket.h> + +#include <lutil_md5.h> + +/* Little-endian byte-swapping routines. Note that these do not + depend on the size of datatypes such as ber_uint_t, nor do they require + us to detect the endianness of the machine we are running on. It + is possible they should be macros for speed, but I would be + surprised if they were a performance bottleneck for MD5. */ + +static ber_uint_t +getu32( const unsigned char *addr ) +{ + return (((((unsigned long)addr[3] << 8) | addr[2]) << 8) + | addr[1]) << 8 | addr[0]; +} + +static void +putu32( ber_uint_t data, unsigned char *addr ) +{ + addr[0] = (unsigned char)data; + addr[1] = (unsigned char)(data >> 8); + addr[2] = (unsigned char)(data >> 16); + addr[3] = (unsigned char)(data >> 24); +} + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void +lutil_MD5Init( struct lutil_MD5Context *ctx ) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void +lutil_MD5Update( + struct lutil_MD5Context *ctx, + const unsigned char *buf, + ber_len_t len +) +{ + ber_uint_t t; + + /* Update bitcount */ + + t = ctx->bits[0]; + if ((ctx->bits[0] = (t + ((ber_uint_t)len << 3)) & 0xffffffff) < t) + ctx->bits[1]++; /* Carry from low to high */ + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if ( t ) { + unsigned char *p = ctx->in + t; + + t = 64-t; + if (len < t) { + AC_MEMCPY(p, buf, len); + return; + } + AC_MEMCPY(p, buf, t); + lutil_MD5Transform(ctx->buf, ctx->in); + buf += t; + len -= t; + } + + /* Process data in 64-byte chunks */ + + while (len >= 64) { + AC_MEMCPY(ctx->in, buf, 64); + lutil_MD5Transform(ctx->buf, ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + + AC_MEMCPY(ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void +lutil_MD5Final( unsigned char *digest, struct lutil_MD5Context *ctx ) +{ + unsigned count; + unsigned char *p; + + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = ctx->in + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset(p, '\0', count); + lutil_MD5Transform(ctx->buf, ctx->in); + + /* Now fill the next block with 56 bytes */ + memset(ctx->in, '\0', 56); + } else { + /* Pad block to 56 bytes */ + memset(p, '\0', count-8); + } + + /* Append length in bits and transform */ + putu32(ctx->bits[0], ctx->in + 56); + putu32(ctx->bits[1], ctx->in + 60); + + lutil_MD5Transform(ctx->buf, ctx->in); + putu32(ctx->buf[0], digest); + putu32(ctx->buf[1], digest + 4); + putu32(ctx->buf[2], digest + 8); + putu32(ctx->buf[3], digest + 12); + memset(ctx, '\0', sizeof(*ctx)); /* In case it's sensitive */ +} + +#ifndef ASM_MD5 + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w &= 0xffffffff, w = w<<s | w>>(32-s), w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +void +lutil_MD5Transform( ber_uint_t *buf, const unsigned char *inraw ) +{ + register ber_uint_t a, b, c, d; + ber_uint_t in[16]; + int i; + + for (i = 0; i < 16; ++i) + in[i] = getu32 (inraw + 4 * i); + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12]+0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10]+0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} +#endif + +#ifdef TEST +/* Simple test program. Can use it to manually run the tests from + RFC1321 for example. */ +#include <stdio.h> + +int +main (int argc, char **argv ) +{ + struct lutil_MD5Context context; + unsigned char checksum[LUTIL_MD5_BYTES]; + int i; + int j; + + if (argc < 2) + { + fprintf (stderr, "usage: %s string-to-hash\n", argv[0]); + return EXIT_FAILURE; + } + for (j = 1; j < argc; ++j) + { + printf ("MD5 (\"%s\") = ", argv[j]); + lutil_MD5Init (&context); + lutil_MD5Update (&context, argv[j], strlen (argv[j])); + lutil_MD5Final (checksum, &context); + for (i = 0; i < LUTIL_MD5_BYTES; i++) + { + printf ("%02x", (unsigned int) checksum[i]); + } + printf ("\n"); + } + return EXIT_SUCCESS; +} +#endif /* TEST */ diff --git a/libraries/liblutil/memcmp.c b/libraries/liblutil/memcmp.c new file mode 100644 index 0000000..c96a525 --- /dev/null +++ b/libraries/liblutil/memcmp.c @@ -0,0 +1,33 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <ac/string.h> + +/* + * Memory Compare + */ +int +(lutil_memcmp)(const void *v1, const void *v2, size_t n) +{ + if (n != 0) { + const unsigned char *s1=v1, *s2=v2; + do { + if (*s1++ != *s2++) return *--s1 - *--s2; + } while (--n != 0); + } + return 0; +} diff --git a/libraries/liblutil/meter.c b/libraries/liblutil/meter.c new file mode 100644 index 0000000..7b5543c --- /dev/null +++ b/libraries/liblutil/meter.c @@ -0,0 +1,386 @@ +/* meter.c - lutil_meter meters */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright (c) 2009 by Emily Backes, Symas Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: + * This work was initially developed by Emily Backes for inclusion + * in OpenLDAP software. + */ + +#include "portable.h" +#include "lutil_meter.h" + +#include <ac/assert.h> +#include <ac/string.h> + +int +lutil_time_string ( + char *dest, + int duration, + int max_terms) +{ + static const int time_div[] = {31556952, + 604800, + 86400, + 3600, + 60, + 1, + 0}; + const int * time_divp = time_div; + static const char * time_name_ch = "ywdhms"; + const char * time_name_chp = time_name_ch; + int term_count = 0; + char *buf = dest; + int time_quot; + + assert ( max_terms >= 2 ); /* room for "none" message */ + + if ( duration < 0 ) { + *dest = '\0'; + return 1; + } + if ( duration == 0 ) { + strcpy( dest, "none" ); + return 0; + } + while ( term_count < max_terms && duration > 0 ) { + if (duration > *time_divp) { + time_quot = duration / *time_divp; + duration %= *time_divp; + if (time_quot > 99) { + return 1; + } else { + *(buf++) = time_quot / 10 + '0'; + *(buf++) = time_quot % 10 + '0'; + *(buf++) = *time_name_chp; + ++term_count; + } + } + if ( *(++time_divp) == 0) duration = 0; + ++time_name_chp; + } + *buf = '\0'; + return 0; +} + +int +lutil_get_now (double *now) +{ +#ifdef HAVE_GETTIMEOFDAY + struct timeval tv; + + assert( now ); + gettimeofday( &tv, NULL ); + *now = ((double) tv.tv_sec) + (((double) tv.tv_usec) / 1000000.0); + return 0; +#else + time_t tm; + + assert( now ); + time( &tm ); + *now = (double) tm; + return 0; +#endif +} + +int +lutil_meter_open ( + lutil_meter_t *meter, + const lutil_meter_display_t *display, + const lutil_meter_estimator_t *estimator, + size_t goal_value) +{ + int rc; + + assert( meter != NULL ); + assert( display != NULL ); + assert( estimator != NULL ); + + if (goal_value < 1) return -1; + + memset( (void*) meter, 0, sizeof( lutil_meter_t )); + meter->display = display; + meter->estimator = estimator; + lutil_get_now( &meter->start_time ); + meter->last_update = meter->start_time; + meter->goal_value = goal_value; + meter->last_position = 0; + + rc = meter->display->display_open( &meter->display_data ); + if( rc != 0 ) return rc; + + rc = meter->estimator->estimator_open( &meter->estimator_data ); + if( rc != 0 ) { + meter->display->display_close( &meter->display_data ); + return rc; + } + + return 0; +} + +int +lutil_meter_update ( + lutil_meter_t *meter, + size_t position, + int force) +{ + static const double display_rate = 0.5; + double frac, cycle_length, speed, now; + time_t remaining_time, elapsed; + int rc; + + assert( meter != NULL ); + + lutil_get_now( &now ); + + if ( !force && now - meter->last_update < display_rate ) return 0; + + frac = ((double)position) / ((double) meter->goal_value); + elapsed = now - meter->start_time; + if (frac <= 0.0) return 0; + if (frac >= 1.0) { + rc = meter->display->display_update( + &meter->display_data, + 1.0, + 0, + (time_t) elapsed, + ((double)position) / elapsed); + } else { + rc = meter->estimator->estimator_update( + &meter->estimator_data, + meter->start_time, + frac, + &remaining_time ); + if ( rc == 0 ) { + cycle_length = now - meter->last_update; + speed = cycle_length > 0.0 ? + ((double)(position - meter->last_position)) + / cycle_length : + 0.0; + rc = meter->display->display_update( + &meter->display_data, + frac, + remaining_time, + (time_t) elapsed, + speed); + if ( rc == 0 ) { + meter->last_update = now; + meter->last_position = position; + } + } + } + + return rc; +} + +int +lutil_meter_close (lutil_meter_t *meter) +{ + meter->estimator->estimator_close( &meter->estimator_data ); + meter->display->display_close( &meter->display_data ); + + return 0; +} + +/* Default display and estimator */ +typedef struct { + int buffer_length; + char * buffer; + int need_eol; + int phase; + FILE *output; +} text_display_state_t; + +static int +text_open (void ** display_datap) +{ + static const int default_buffer_length = 81; + text_display_state_t *data; + + assert( display_datap != NULL ); + data = calloc( 1, sizeof( text_display_state_t )); + assert( data != NULL ); + data->buffer_length = default_buffer_length; + data->buffer = calloc( 1, default_buffer_length ); + assert( data->buffer != NULL ); + data->output = stderr; + *display_datap = data; + return 0; +} + +static int +text_update ( + void **display_datap, + double frac, + time_t remaining_time, + time_t elapsed, + double byte_rate) +{ + text_display_state_t *data; + char *buf, *buf_end; + + assert( display_datap != NULL ); + assert( *display_datap != NULL ); + data = (text_display_state_t*) *display_datap; + + if ( data->output == NULL ) return 1; + + buf = data->buffer; + buf_end = buf + data->buffer_length - 1; + +/* |#################### 100.00% eta 1d19h elapsed 23w 7d23h15m12s spd nnnn.n M/s */ + + { + /* spinner */ + static const int phase_mod = 8; + static const char phase_char[] = "_.-*\"*-."; + *buf++ = phase_char[data->phase % phase_mod]; + data->phase++; + } + + { + /* bar */ + static const int bar_length = 20; + static const double bar_lengthd = 20.0; + static const char fill_char = '#'; + static const char blank_char = ' '; + char *bar_end = buf + bar_length; + char *bar_pos = frac < 0.0 ? + buf : + frac < 1.0 ? + buf + (int) (bar_lengthd * frac) : + bar_end; + + assert( (buf_end - buf) > bar_length ); + while ( buf < bar_end ) { + *buf = buf < bar_pos ? + fill_char : blank_char; + ++buf; + } + } + + { + /* percent */ + (void) snprintf( buf, buf_end-buf, "%7.2f%%", 100.0*frac ); + buf += 8; + } + + { + /* eta and elapsed */ + char time_buffer[19]; + int rc; + rc = lutil_time_string( time_buffer, remaining_time, 2); + if (rc == 0) + snprintf( buf, buf_end-buf, " eta %6s", time_buffer ); + buf += 5+6; + rc = lutil_time_string( time_buffer, elapsed, 5); + if (rc == 0) + snprintf( buf, buf_end-buf, " elapsed %15s", + time_buffer ); + buf += 9+15; + } + + { + /* speed */ + static const char prefixes[] = " kMGTPEZY"; + const char *prefix_chp = prefixes; + + while (*prefix_chp && byte_rate >= 1024.0) { + byte_rate /= 1024.0; + ++prefix_chp; + } + if ( byte_rate >= 1024.0 ) { + snprintf( buf, buf_end-buf, " fast!" ); + buf += 6; + } else { + snprintf( buf, buf_end-buf, " spd %5.1f %c/s", + byte_rate, + *prefix_chp); + buf += 5+6+4; + } + } + + (void) fprintf( data->output, + "\r%-79s", + data->buffer ); + data->need_eol = 1; + return 0; +} + +static int +text_close (void ** display_datap) +{ + text_display_state_t *data; + + if (display_datap) { + if (*display_datap) { + data = (text_display_state_t*) *display_datap; + if (data->output && data->need_eol) + fputs ("\n", data->output); + if (data->buffer) + free( data->buffer ); + free( data ); + } + *display_datap = NULL; + } + return 0; +} + +static int +null_open_close (void **datap) +{ + assert( datap ); + *datap = NULL; + return 0; +} + +static int +linear_update ( + void **estimator_datap, + double start, + double frac, + time_t *remaining) +{ + double now; + double elapsed; + + assert( estimator_datap != NULL ); + assert( *estimator_datap == NULL ); + assert( start > 0.0 ); + assert( frac >= 0.0 ); + assert( frac <= 1.0 ); + assert( remaining != NULL ); + lutil_get_now( &now ); + + elapsed = now-start; + assert( elapsed >= 0.0 ); + + if ( frac == 0.0 ) { + return 1; + } else if ( frac >= 1.0 ) { + *remaining = 0; + return 0; + } else { + *remaining = (time_t) (elapsed/frac-elapsed+0.5); + return 0; + } +} + +const lutil_meter_display_t lutil_meter_text_display = { + text_open, text_update, text_close +}; + +const lutil_meter_estimator_t lutil_meter_linear_estimator = { + null_open_close, linear_update, null_open_close +}; diff --git a/libraries/liblutil/ntservice.c b/libraries/liblutil/ntservice.c new file mode 100644 index 0000000..48c139d --- /dev/null +++ b/libraries/liblutil/ntservice.c @@ -0,0 +1,509 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +/* + * NT Service manager utilities for OpenLDAP services + */ + +#include "portable.h" + +#ifdef HAVE_NT_SERVICE_MANAGER + +#include <ac/stdlib.h> +#include <ac/string.h> + +#include <stdio.h> + +#include <windows.h> +#include <winsvc.h> + +#include <ldap.h> + +#include "ldap_pvt_thread.h" + +#include "ldap_defaults.h" + +#include "slapdmsg.h" + +#define SCM_NOTIFICATION_INTERVAL 5000 +#define THIRTY_SECONDS (30 * 1000) + +int is_NT_Service; /* is this is an NT service? */ + +SERVICE_STATUS lutil_ServiceStatus; +SERVICE_STATUS_HANDLE hlutil_ServiceStatus; + +ldap_pvt_thread_cond_t started_event, stopped_event; +ldap_pvt_thread_t start_status_tid, stop_status_tid; + +void (*stopfunc)(int); + +static char *GetLastErrorString( void ); + +int lutil_srv_install(LPCTSTR lpszServiceName, LPCTSTR lpszDisplayName, + LPCTSTR lpszBinaryPathName, int auto_start) +{ + HKEY hKey; + DWORD dwValue, dwDisposition; + SC_HANDLE schSCManager, schService; + char *sp = strrchr( lpszBinaryPathName, '\\'); + + if ( sp ) sp = strchr(sp, ' '); + if ( sp ) *sp = '\0'; + fprintf( stderr, "The install path is %s.\n", lpszBinaryPathName ); + if ( sp ) *sp = ' '; + if ((schSCManager = OpenSCManager( NULL, NULL, SC_MANAGER_CONNECT|SC_MANAGER_CREATE_SERVICE ) ) != NULL ) + { + if ((schService = CreateService( + schSCManager, + lpszServiceName, + lpszDisplayName, + SERVICE_ALL_ACCESS, + SERVICE_WIN32_OWN_PROCESS, + auto_start ? SERVICE_AUTO_START : SERVICE_DEMAND_START, + SERVICE_ERROR_NORMAL, + lpszBinaryPathName, + NULL, NULL, NULL, NULL, NULL)) != NULL) + { + char regpath[132]; + CloseServiceHandle(schService); + CloseServiceHandle(schSCManager); + + snprintf( regpath, sizeof regpath, + "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\%s", + lpszServiceName ); + /* Create the registry key for event logging to the Windows NT event log. */ + if ( RegCreateKeyEx(HKEY_LOCAL_MACHINE, + regpath, 0, + "REG_SZ", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, + &dwDisposition) != ERROR_SUCCESS) + { + fprintf( stderr, "RegCreateKeyEx() failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() ); + RegCloseKey(hKey); + return(0); + } + if ( sp ) *sp = '\0'; + if ( RegSetValueEx(hKey, "EventMessageFile", 0, REG_EXPAND_SZ, lpszBinaryPathName, strlen(lpszBinaryPathName) + 1) != ERROR_SUCCESS) + { + fprintf( stderr, "RegSetValueEx(EventMessageFile) failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() ); + RegCloseKey(hKey); + return(0); + } + + dwValue = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE; + if ( RegSetValueEx(hKey, "TypesSupported", 0, REG_DWORD, (LPBYTE) &dwValue, sizeof(DWORD)) != ERROR_SUCCESS) + { + fprintf( stderr, "RegCreateKeyEx(TypesSupported) failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() ); + RegCloseKey(hKey); + return(0); + } + RegCloseKey(hKey); + return(1); + } + else + { + fprintf( stderr, "CreateService() failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() ); + CloseServiceHandle(schSCManager); + return(0); + } + } + else + fprintf( stderr, "OpenSCManager() failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() ); + return(0); +} + + +int lutil_srv_remove(LPCTSTR lpszServiceName, LPCTSTR lpszBinaryPathName) +{ + SC_HANDLE schSCManager, schService; + + fprintf( stderr, "The installed path is %s.\n", lpszBinaryPathName ); + if ((schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT|SC_MANAGER_CREATE_SERVICE)) != NULL ) + { + if ((schService = OpenService(schSCManager, lpszServiceName, DELETE)) != NULL) + { + if ( DeleteService(schService) == TRUE) + { + CloseServiceHandle(schService); + CloseServiceHandle(schSCManager); + return(1); + } else { + fprintf( stderr, "DeleteService() failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() ); + fprintf( stderr, "The %s service has not been removed.\n", lpszBinaryPathName); + CloseServiceHandle(schService); + CloseServiceHandle(schSCManager); + return(0); + } + } else { + fprintf( stderr, "OpenService() failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() ); + CloseServiceHandle(schSCManager); + return(0); + } + } + else + fprintf( stderr, "OpenSCManager() failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() ); + return(0); +} + + +#if 0 /* unused */ +DWORD +svc_installed (LPTSTR lpszServiceName, LPTSTR lpszBinaryPathName) +{ + char buf[256]; + HKEY key; + DWORD rc; + DWORD type; + long len; + + strcpy(buf, TEXT("SYSTEM\\CurrentControlSet\\Services\\")); + strcat(buf, lpszServiceName); + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, buf, 0, KEY_QUERY_VALUE, &key) != ERROR_SUCCESS) + return(-1); + + rc = 0; + if (lpszBinaryPathName) { + len = sizeof(buf); + if (RegQueryValueEx(key, "ImagePath", NULL, &type, buf, &len) == ERROR_SUCCESS) { + if (strcmp(lpszBinaryPathName, buf)) + rc = -1; + } + } + RegCloseKey(key); + return(rc); +} + + +DWORD +svc_running (LPTSTR lpszServiceName) +{ + SC_HANDLE service; + SC_HANDLE scm; + DWORD rc; + SERVICE_STATUS ss; + + if (!(scm = OpenSCManager(NULL, NULL, GENERIC_READ))) + return(GetLastError()); + + rc = 1; + service = OpenService(scm, lpszServiceName, SERVICE_QUERY_STATUS); + if (service) { + if (!QueryServiceStatus(service, &ss)) + rc = GetLastError(); + else if (ss.dwCurrentState != SERVICE_STOPPED) + rc = 0; + CloseServiceHandle(service); + } + CloseServiceHandle(scm); + return(rc); +} +#endif + +static void *start_status_routine( void *ptr ) +{ + DWORD wait_result; + int done = 0; + + while ( !done ) + { + wait_result = WaitForSingleObject( started_event, SCM_NOTIFICATION_INTERVAL ); + switch ( wait_result ) + { + case WAIT_ABANDONED: + case WAIT_OBJECT_0: + /* the object that we were waiting for has been destroyed (ABANDONED) or + * signalled (TIMEOUT_0). We can assume that the startup process is + * complete and tell the Service Control Manager that we are now runnng */ + lutil_ServiceStatus.dwCurrentState = SERVICE_RUNNING; + lutil_ServiceStatus.dwWin32ExitCode = NO_ERROR; + lutil_ServiceStatus.dwCheckPoint++; + lutil_ServiceStatus.dwWaitHint = 1000; + SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus); + done = 1; + break; + case WAIT_TIMEOUT: + /* We've waited for the required time, so send an update to the Service Control + * Manager saying to wait again. */ + lutil_ServiceStatus.dwCheckPoint++; + lutil_ServiceStatus.dwWaitHint = SCM_NOTIFICATION_INTERVAL * 2; + SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus); + break; + case WAIT_FAILED: + /* theres been some problem with WaitForSingleObject so tell the Service + * Control Manager to wait 30 seconds before deploying its assasin and + * then leave the thread. */ + lutil_ServiceStatus.dwCheckPoint++; + lutil_ServiceStatus.dwWaitHint = THIRTY_SECONDS; + SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus); + done = 1; + break; + } + } + ldap_pvt_thread_exit(NULL); + return NULL; +} + + + +static void *stop_status_routine( void *ptr ) +{ + DWORD wait_result; + int done = 0; + + while ( !done ) + { + wait_result = WaitForSingleObject( stopped_event, SCM_NOTIFICATION_INTERVAL ); + switch ( wait_result ) + { + case WAIT_ABANDONED: + case WAIT_OBJECT_0: + /* the object that we were waiting for has been destroyed (ABANDONED) or + * signalled (TIMEOUT_0). The shutting down process is therefore complete + * and the final SERVICE_STOPPED message will be sent to the service control + * manager prior to the process terminating. */ + done = 1; + break; + case WAIT_TIMEOUT: + /* We've waited for the required time, so send an update to the Service Control + * Manager saying to wait again. */ + lutil_ServiceStatus.dwCheckPoint++; + lutil_ServiceStatus.dwWaitHint = SCM_NOTIFICATION_INTERVAL * 2; + SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus); + break; + case WAIT_FAILED: + /* theres been some problem with WaitForSingleObject so tell the Service + * Control Manager to wait 30 seconds before deploying its assasin and + * then leave the thread. */ + lutil_ServiceStatus.dwCheckPoint++; + lutil_ServiceStatus.dwWaitHint = THIRTY_SECONDS; + SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus); + done = 1; + break; + } + } + ldap_pvt_thread_exit(NULL); + return NULL; +} + + + +static void WINAPI lutil_ServiceCtrlHandler( IN DWORD Opcode) +{ + switch (Opcode) + { + case SERVICE_CONTROL_STOP: + case SERVICE_CONTROL_SHUTDOWN: + + lutil_ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING; + lutil_ServiceStatus.dwCheckPoint++; + lutil_ServiceStatus.dwWaitHint = SCM_NOTIFICATION_INTERVAL * 2; + SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus); + + ldap_pvt_thread_cond_init( &stopped_event ); + if ( stopped_event == NULL ) + { + /* the event was not created. We will ask the service control manager for 30 + * seconds to shutdown */ + lutil_ServiceStatus.dwCheckPoint++; + lutil_ServiceStatus.dwWaitHint = THIRTY_SECONDS; + SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus); + } + else + { + /* start a thread to report the progress to the service control manager + * until the stopped_event is fired. */ + if ( ldap_pvt_thread_create( &stop_status_tid, 0, stop_status_routine, NULL ) == 0 ) + { + + } + else { + /* failed to create the thread that tells the Service Control Manager that the + * service stopping is proceeding. + * tell the Service Control Manager to wait another 30 seconds before deploying its + * assasin. */ + lutil_ServiceStatus.dwCheckPoint++; + lutil_ServiceStatus.dwWaitHint = THIRTY_SECONDS; + SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus); + } + } + stopfunc( -1 ); + break; + + case SERVICE_CONTROL_INTERROGATE: + SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus); + break; + } + return; +} + +void *lutil_getRegParam( char *svc, char *value ) +{ + HKEY hkey; + char path[255]; + DWORD vType; + static char vValue[1024]; + DWORD valLen = sizeof( vValue ); + + if ( svc != NULL ) + snprintf ( path, sizeof path, "SOFTWARE\\%s", svc ); + else + snprintf ( path, sizeof path, "SOFTWARE\\OpenLDAP\\Parameters" ); + + if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE, path, 0, KEY_READ, &hkey ) != ERROR_SUCCESS ) + { + return NULL; + } + + if ( RegQueryValueEx( hkey, value, NULL, &vType, vValue, &valLen ) != ERROR_SUCCESS ) + { + RegCloseKey( hkey ); + return NULL; + } + RegCloseKey( hkey ); + + switch ( vType ) + { + case REG_BINARY: + case REG_DWORD: + return (void*)&vValue; + case REG_SZ: + return (void*)&vValue; + } + return (void*)NULL; +} + +void lutil_LogStartedEvent( char *svc, int slap_debug, char *configfile, char *urls ) +{ + char *Inserts[5]; + WORD i = 0, j; + HANDLE hEventLog; + + hEventLog = RegisterEventSource( NULL, svc ); + + Inserts[i] = (char *)malloc( 20 ); + itoa( slap_debug, Inserts[i++], 10 ); + Inserts[i++] = strdup( configfile ); + Inserts[i++] = strdup( urls ? urls : "ldap:///" ); + + ReportEvent( hEventLog, EVENTLOG_INFORMATION_TYPE, 0, + MSG_SVC_STARTED, NULL, i, 0, (LPCSTR *) Inserts, NULL ); + + for ( j = 0; j < i; j++ ) + ldap_memfree( Inserts[j] ); + DeregisterEventSource( hEventLog ); +} + + + +void lutil_LogStoppedEvent( char *svc ) +{ + HANDLE hEventLog; + + hEventLog = RegisterEventSource( NULL, svc ); + ReportEvent( hEventLog, EVENTLOG_INFORMATION_TYPE, 0, + MSG_SVC_STOPPED, NULL, 0, 0, NULL, NULL ); + DeregisterEventSource( hEventLog ); +} + + +void lutil_CommenceStartupProcessing( char *lpszServiceName, + void (*stopper)(int) ) +{ + hlutil_ServiceStatus = RegisterServiceCtrlHandler( lpszServiceName, (LPHANDLER_FUNCTION)lutil_ServiceCtrlHandler); + + stopfunc = stopper; + + /* initialize the Service Status structure */ + lutil_ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + lutil_ServiceStatus.dwCurrentState = SERVICE_START_PENDING; + lutil_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; + lutil_ServiceStatus.dwWin32ExitCode = NO_ERROR; + lutil_ServiceStatus.dwServiceSpecificExitCode = 0; + lutil_ServiceStatus.dwCheckPoint = 1; + lutil_ServiceStatus.dwWaitHint = SCM_NOTIFICATION_INTERVAL * 2; + + SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus); + + /* start up a thread to keep sending SERVICE_START_PENDING to the Service Control Manager + * until the slapd listener is completed and listening. Only then should we send + * SERVICE_RUNNING to the Service Control Manager. */ + ldap_pvt_thread_cond_init( &started_event ); + if ( started_event == NULL) + { + /* failed to create the event to determine when the startup process is complete so + * tell the Service Control Manager to wait another 30 seconds before deploying its + * assasin */ + lutil_ServiceStatus.dwCheckPoint++; + lutil_ServiceStatus.dwWaitHint = THIRTY_SECONDS; + SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus); + } + else + { + /* start a thread to report the progress to the service control manager + * until the started_event is fired. */ + if ( ldap_pvt_thread_create( &start_status_tid, 0, start_status_routine, NULL ) == 0 ) + { + + } + else { + /* failed to create the thread that tells the Service Control Manager that the + * service startup is proceeding. + * tell the Service Control Manager to wait another 30 seconds before deploying its + * assasin. */ + lutil_ServiceStatus.dwCheckPoint++; + lutil_ServiceStatus.dwWaitHint = THIRTY_SECONDS; + SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus); + } + } +} + +void lutil_ReportShutdownComplete( ) +{ + if ( is_NT_Service ) + { + /* stop sending SERVICE_STOP_PENDING messages to the Service Control Manager */ + ldap_pvt_thread_cond_signal( &stopped_event ); + ldap_pvt_thread_cond_destroy( &stopped_event ); + + /* wait for the thread sending the SERVICE_STOP_PENDING messages to the Service Control Manager to die. + * if the wait fails then put ourselves to sleep for half the Service Control Manager update interval */ + if (ldap_pvt_thread_join( stop_status_tid, (void *) NULL ) == -1) + ldap_pvt_thread_sleep( SCM_NOTIFICATION_INTERVAL / 2 ); + + lutil_ServiceStatus.dwCurrentState = SERVICE_STOPPED; + lutil_ServiceStatus.dwCheckPoint++; + lutil_ServiceStatus.dwWaitHint = SCM_NOTIFICATION_INTERVAL; + SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus); + } +} + +static char *GetErrorString( int err ) +{ + static char msgBuf[1024]; + + FormatMessage( + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + msgBuf, 1024, NULL ); + + return msgBuf; +} + +static char *GetLastErrorString( void ) +{ + return GetErrorString( GetLastError() ); +} +#endif diff --git a/libraries/liblutil/passfile.c b/libraries/liblutil/passfile.c new file mode 100644 index 0000000..2930a69 --- /dev/null +++ b/libraries/liblutil/passfile.c @@ -0,0 +1,110 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> +#include <ac/ctype.h> +#include <ac/string.h> + +#ifdef HAVE_FSTAT +#include <sys/types.h> +#include <sys/stat.h> +#endif /* HAVE_FSTAT */ + +#include <lber.h> +#include <lutil.h> + +/* Get a password from a file. */ +int +lutil_get_filed_password( + const char *filename, + struct berval *passwd ) +{ + size_t nread, nleft, nr; + FILE *f = fopen( filename, "r" ); + + if( f == NULL ) { + perror( filename ); + return -1; + } + + passwd->bv_val = NULL; + passwd->bv_len = 4096; + +#ifdef HAVE_FSTAT + { + struct stat sb; + if ( fstat( fileno( f ), &sb ) == 0 ) { + if( sb.st_mode & 006 ) { + fprintf( stderr, _("Warning: Password file %s" + " is publicly readable/writeable\n"), + filename ); + } + + if ( sb.st_size ) + passwd->bv_len = sb.st_size; + } + } +#endif /* HAVE_FSTAT */ + + passwd->bv_val = (char *) ber_memalloc( passwd->bv_len + 1 ); + if( passwd->bv_val == NULL ) { + perror( filename ); + fclose( f ); + return -1; + } + + nread = 0; + nleft = passwd->bv_len; + do { + if( nleft == 0 ) { + /* double the buffer size */ + char *p = (char *) ber_memrealloc( passwd->bv_val, + 2 * passwd->bv_len + 1 ); + if( p == NULL ) { + ber_memfree( passwd->bv_val ); + passwd->bv_val = NULL; + passwd->bv_len = 0; + fclose( f ); + return -1; + } + nleft = passwd->bv_len; + passwd->bv_len *= 2; + passwd->bv_val = p; + } + + nr = fread( &passwd->bv_val[nread], 1, nleft, f ); + + if( nr < nleft && ferror( f ) ) { + ber_memfree( passwd->bv_val ); + passwd->bv_val = NULL; + passwd->bv_len = 0; + fclose( f ); + return -1; + } + + nread += nr; + nleft -= nr; + } while ( !feof(f) ); + + passwd->bv_len = nread; + passwd->bv_val[nread] = '\0'; + + fclose( f ); + return 0; +} diff --git a/libraries/liblutil/passwd.c b/libraries/liblutil/passwd.c new file mode 100644 index 0000000..8fa4a08 --- /dev/null +++ b/libraries/liblutil/passwd.c @@ -0,0 +1,1263 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +/* + * int lutil_passwd( + * const struct berval *passwd, + * const struct berval *cred, + * const char **schemes ) + * + * Returns true if user supplied credentials (cred) matches + * the stored password (passwd). + * + * Due to the use of the crypt(3) function + * this routine is NOT thread-safe. + */ + +#include "portable.h" + +#include <stdio.h> +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/unistd.h> + +#if defined(SLAPD_LMHASH) +#if defined(HAVE_OPENSSL) +# include <openssl/des.h> + + +typedef DES_cblock des_key; +typedef DES_cblock des_data_block; +typedef DES_key_schedule des_context[1]; +#define des_failed(encrypted) 0 +#define des_finish(key, schedule) + +#elif defined(HAVE_MOZNSS) +/* + hack hack hack + We need to define this here so that nspr/obsolete/protypes.h will not be included + if that file is included, it will create a uint32 typedef that will cause the + one in lutil_sha1.h to blow up +*/ +#define PROTYPES_H 1 +# include <nss/pk11pub.h> +typedef PK11SymKey *des_key; +typedef unsigned char des_data_block[8]; +typedef PK11Context *des_context[1]; +#define DES_ENCRYPT CKA_ENCRYPT + +#endif + +#endif /* SLAPD_LMHASH */ + +#include <ac/param.h> + +#ifdef SLAPD_CRYPT +# include <ac/crypt.h> + +# if defined( HAVE_GETPWNAM ) && defined( HAVE_STRUCT_PASSWD_PW_PASSWD ) +# ifdef HAVE_SHADOW_H +# include <shadow.h> +# endif +# ifdef HAVE_PWD_H +# include <pwd.h> +# endif +# ifdef HAVE_AIX_SECURITY +# include <userpw.h> +# endif +# endif +#endif + +#include <lber.h> + +#include "ldap_pvt.h" +#include "lber_pvt.h" + +#include "lutil_md5.h" +#include "lutil_sha1.h" +#include "lutil.h" + +static const unsigned char crypt64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890./"; + +#ifdef SLAPD_CRYPT +static char *salt_format = NULL; +static lutil_cryptfunc lutil_crypt; +lutil_cryptfunc *lutil_cryptptr = lutil_crypt; +#endif + +/* KLUDGE: + * chk_fn is NULL iff name is {CLEARTEXT} + * otherwise, things will break + */ +struct pw_scheme { + struct berval name; + LUTIL_PASSWD_CHK_FUNC *chk_fn; + LUTIL_PASSWD_HASH_FUNC *hash_fn; +}; + +struct pw_slist { + struct pw_slist *next; + struct pw_scheme s; +}; + +/* password check routines */ + +#define SALT_SIZE 4 + +static LUTIL_PASSWD_CHK_FUNC chk_md5; +static LUTIL_PASSWD_CHK_FUNC chk_smd5; +static LUTIL_PASSWD_HASH_FUNC hash_smd5; +static LUTIL_PASSWD_HASH_FUNC hash_md5; + + +#ifdef LUTIL_SHA1_BYTES +static LUTIL_PASSWD_CHK_FUNC chk_ssha1; +static LUTIL_PASSWD_CHK_FUNC chk_sha1; +static LUTIL_PASSWD_HASH_FUNC hash_sha1; +static LUTIL_PASSWD_HASH_FUNC hash_ssha1; +#endif + +#ifdef SLAPD_LMHASH +static LUTIL_PASSWD_CHK_FUNC chk_lanman; +static LUTIL_PASSWD_HASH_FUNC hash_lanman; +#endif + +#ifdef SLAPD_CRYPT +static LUTIL_PASSWD_CHK_FUNC chk_crypt; +static LUTIL_PASSWD_HASH_FUNC hash_crypt; + +#if defined( HAVE_GETPWNAM ) && defined( HAVE_STRUCT_PASSWD_PW_PASSWD ) +static LUTIL_PASSWD_CHK_FUNC chk_unix; +#endif +#endif + +/* password hash routines */ + +#ifdef SLAPD_CLEARTEXT +static LUTIL_PASSWD_HASH_FUNC hash_clear; +#endif + +static struct pw_slist *pw_schemes; +static int pw_inited; + +static const struct pw_scheme pw_schemes_default[] = +{ +#ifdef LUTIL_SHA1_BYTES + { BER_BVC("{SSHA}"), chk_ssha1, hash_ssha1 }, + { BER_BVC("{SHA}"), chk_sha1, hash_sha1 }, +#endif + + { BER_BVC("{SMD5}"), chk_smd5, hash_smd5 }, + { BER_BVC("{MD5}"), chk_md5, hash_md5 }, + +#ifdef SLAPD_LMHASH + { BER_BVC("{LANMAN}"), chk_lanman, hash_lanman }, +#endif /* SLAPD_LMHASH */ + +#ifdef SLAPD_CRYPT + { BER_BVC("{CRYPT}"), chk_crypt, hash_crypt }, +# if defined( HAVE_GETPWNAM ) && defined( HAVE_STRUCT_PASSWD_PW_PASSWD ) + { BER_BVC("{UNIX}"), chk_unix, NULL }, +# endif +#endif + +#ifdef SLAPD_CLEARTEXT + /* pseudo scheme */ + { BER_BVC("{CLEARTEXT}"), NULL, hash_clear }, +#endif + + { BER_BVNULL, NULL, NULL } +}; + +int lutil_passwd_add( + struct berval *scheme, + LUTIL_PASSWD_CHK_FUNC *chk, + LUTIL_PASSWD_HASH_FUNC *hash ) +{ + struct pw_slist *ptr; + + if (!pw_inited) lutil_passwd_init(); + + ptr = ber_memalloc( sizeof( struct pw_slist )); + if (!ptr) return -1; + ptr->next = pw_schemes; + ptr->s.name = *scheme; + ptr->s.chk_fn = chk; + ptr->s.hash_fn = hash; + pw_schemes = ptr; + return 0; +} + +void lutil_passwd_init() +{ + struct pw_scheme *s; + + pw_inited = 1; + + for( s=(struct pw_scheme *)pw_schemes_default; s->name.bv_val; s++) { + if ( lutil_passwd_add( &s->name, s->chk_fn, s->hash_fn ) ) break; + } +} + +void lutil_passwd_destroy() +{ + struct pw_slist *ptr, *next; + + for( ptr=pw_schemes; ptr; ptr=next ) { + next = ptr->next; + ber_memfree( ptr ); + } +} + +static const struct pw_scheme *get_scheme( + const char* scheme ) +{ + struct pw_slist *pws; + struct berval bv; + + if (!pw_inited) lutil_passwd_init(); + + bv.bv_val = strchr( scheme, '}' ); + if ( !bv.bv_val ) + return NULL; + + bv.bv_len = bv.bv_val - scheme + 1; + bv.bv_val = (char *) scheme; + + for( pws=pw_schemes; pws; pws=pws->next ) { + if ( ber_bvstrcasecmp(&bv, &pws->s.name ) == 0 ) { + return &(pws->s); + } + } + + return NULL; +} + +int lutil_passwd_scheme( + const char* scheme ) +{ + if( scheme == NULL ) { + return 0; + } + + return get_scheme(scheme) != NULL; +} + + +static int is_allowed_scheme( + const char* scheme, + const char** schemes ) +{ + int i; + + if( schemes == NULL ) return 1; + + for( i=0; schemes[i] != NULL; i++ ) { + if( strcasecmp( scheme, schemes[i] ) == 0 ) { + return 1; + } + } + return 0; +} + +static struct berval *passwd_scheme( + const struct pw_scheme *scheme, + const struct berval * passwd, + struct berval *bv, + const char** allowed ) +{ + if( !is_allowed_scheme( scheme->name.bv_val, allowed ) ) { + return NULL; + } + + if( passwd->bv_len >= scheme->name.bv_len ) { + if( strncasecmp( passwd->bv_val, scheme->name.bv_val, scheme->name.bv_len ) == 0 ) { + bv->bv_val = &passwd->bv_val[scheme->name.bv_len]; + bv->bv_len = passwd->bv_len - scheme->name.bv_len; + + return bv; + } + } + + return NULL; +} + +/* + * Return 0 if creds are good. + */ +int +lutil_passwd( + const struct berval *passwd, /* stored passwd */ + const struct berval *cred, /* user cred */ + const char **schemes, + const char **text ) +{ + struct pw_slist *pws; + + if ( text ) *text = NULL; + + if (cred == NULL || cred->bv_len == 0 || + passwd == NULL || passwd->bv_len == 0 ) + { + return -1; + } + + if (!pw_inited) lutil_passwd_init(); + + for( pws=pw_schemes; pws; pws=pws->next ) { + if( pws->s.chk_fn ) { + struct berval x; + struct berval *p = passwd_scheme( &(pws->s), + passwd, &x, schemes ); + + if( p != NULL ) { + return (pws->s.chk_fn)( &(pws->s.name), p, cred, text ); + } + } + } + +#ifdef SLAPD_CLEARTEXT + /* Do we think there is a scheme specifier here that we + * didn't recognize? Assume a scheme name is at least 1 character. + */ + if (( passwd->bv_val[0] == '{' ) && + ( ber_bvchr( passwd, '}' ) > passwd->bv_val+1 )) + { + return 1; + } + if( is_allowed_scheme("{CLEARTEXT}", schemes ) ) { + return ( passwd->bv_len == cred->bv_len ) ? + memcmp( passwd->bv_val, cred->bv_val, passwd->bv_len ) + : 1; + } +#endif + return 1; +} + +int lutil_passwd_generate( struct berval *pw, ber_len_t len ) +{ + + if( len < 1 ) return -1; + + pw->bv_len = len; + pw->bv_val = ber_memalloc( len + 1 ); + + if( pw->bv_val == NULL ) { + return -1; + } + + if( lutil_entropy( (unsigned char *) pw->bv_val, pw->bv_len) < 0 ) { + return -1; + } + + for( len = 0; len < pw->bv_len; len++ ) { + pw->bv_val[len] = crypt64[ + pw->bv_val[len] % (sizeof(crypt64)-1) ]; + } + + pw->bv_val[len] = '\0'; + + return 0; +} + +int lutil_passwd_hash( + const struct berval * passwd, + const char * method, + struct berval *hash, + const char **text ) +{ + const struct pw_scheme *sc = get_scheme( method ); + + hash->bv_val = NULL; + hash->bv_len = 0; + + if( sc == NULL ) { + if( text ) *text = "scheme not recognized"; + return -1; + } + + if( ! sc->hash_fn ) { + if( text ) *text = "scheme provided no hash function"; + return -1; + } + + if( text ) *text = NULL; + + return (sc->hash_fn)( &sc->name, passwd, hash, text ); +} + +/* pw_string is only called when SLAPD_LMHASH or SLAPD_CRYPT is defined */ +#if defined(SLAPD_LMHASH) || defined(SLAPD_CRYPT) +static int pw_string( + const struct berval *sc, + struct berval *passwd ) +{ + struct berval pw; + + pw.bv_len = sc->bv_len + passwd->bv_len; + pw.bv_val = ber_memalloc( pw.bv_len + 1 ); + + if( pw.bv_val == NULL ) { + return LUTIL_PASSWD_ERR; + } + + AC_MEMCPY( pw.bv_val, sc->bv_val, sc->bv_len ); + AC_MEMCPY( &pw.bv_val[sc->bv_len], passwd->bv_val, passwd->bv_len ); + + pw.bv_val[pw.bv_len] = '\0'; + *passwd = pw; + + return LUTIL_PASSWD_OK; +} +#endif /* SLAPD_LMHASH || SLAPD_CRYPT */ + +int lutil_passwd_string64( + const struct berval *sc, + const struct berval *hash, + struct berval *b64, + const struct berval *salt ) +{ + int rc; + struct berval string; + size_t b64len; + + if( salt ) { + /* need to base64 combined string */ + string.bv_len = hash->bv_len + salt->bv_len; + string.bv_val = ber_memalloc( string.bv_len + 1 ); + + if( string.bv_val == NULL ) { + return LUTIL_PASSWD_ERR; + } + + AC_MEMCPY( string.bv_val, hash->bv_val, + hash->bv_len ); + AC_MEMCPY( &string.bv_val[hash->bv_len], salt->bv_val, + salt->bv_len ); + string.bv_val[string.bv_len] = '\0'; + + } else { + string = *hash; + } + + b64len = LUTIL_BASE64_ENCODE_LEN( string.bv_len ) + 1; + b64->bv_len = b64len + sc->bv_len; + b64->bv_val = ber_memalloc( b64->bv_len + 1 ); + + if( b64->bv_val == NULL ) { + if( salt ) ber_memfree( string.bv_val ); + return LUTIL_PASSWD_ERR; + } + + AC_MEMCPY(b64->bv_val, sc->bv_val, sc->bv_len); + + rc = lutil_b64_ntop( + (unsigned char *) string.bv_val, string.bv_len, + &b64->bv_val[sc->bv_len], b64len ); + + if( salt ) ber_memfree( string.bv_val ); + + if( rc < 0 ) { + return LUTIL_PASSWD_ERR; + } + + /* recompute length */ + b64->bv_len = sc->bv_len + rc; + assert( strlen(b64->bv_val) == b64->bv_len ); + return LUTIL_PASSWD_OK; +} + +/* PASSWORD CHECK ROUTINES */ + +#ifdef LUTIL_SHA1_BYTES +static int chk_ssha1( + const struct berval *sc, + const struct berval * passwd, + const struct berval * cred, + const char **text ) +{ + lutil_SHA1_CTX SHA1context; + unsigned char SHA1digest[LUTIL_SHA1_BYTES]; + int rc; + unsigned char *orig_pass = NULL; + size_t decode_len = LUTIL_BASE64_DECODE_LEN(passwd->bv_len); + + /* safety check -- must have some salt */ + if (decode_len <= sizeof(SHA1digest)) { + return LUTIL_PASSWD_ERR; + } + + /* decode base64 password */ + orig_pass = (unsigned char *) ber_memalloc(decode_len + 1); + + if( orig_pass == NULL ) return LUTIL_PASSWD_ERR; + + rc = lutil_b64_pton(passwd->bv_val, orig_pass, decode_len); + + /* safety check -- must have some salt */ + if (rc <= (int)(sizeof(SHA1digest))) { + ber_memfree(orig_pass); + return LUTIL_PASSWD_ERR; + } + + /* hash credentials with salt */ + lutil_SHA1Init(&SHA1context); + lutil_SHA1Update(&SHA1context, + (const unsigned char *) cred->bv_val, cred->bv_len); + lutil_SHA1Update(&SHA1context, + (const unsigned char *) &orig_pass[sizeof(SHA1digest)], + rc - sizeof(SHA1digest)); + lutil_SHA1Final(SHA1digest, &SHA1context); + + /* compare */ + rc = memcmp((char *)orig_pass, (char *)SHA1digest, sizeof(SHA1digest)); + ber_memfree(orig_pass); + return rc ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK; +} + +static int chk_sha1( + const struct berval *sc, + const struct berval * passwd, + const struct berval * cred, + const char **text ) +{ + lutil_SHA1_CTX SHA1context; + unsigned char SHA1digest[LUTIL_SHA1_BYTES]; + int rc; + unsigned char *orig_pass = NULL; + size_t decode_len = LUTIL_BASE64_DECODE_LEN(passwd->bv_len); + + /* safety check */ + if (decode_len < sizeof(SHA1digest)) { + return LUTIL_PASSWD_ERR; + } + + /* base64 un-encode password */ + orig_pass = (unsigned char *) ber_memalloc(decode_len + 1); + + if( orig_pass == NULL ) return LUTIL_PASSWD_ERR; + + rc = lutil_b64_pton(passwd->bv_val, orig_pass, decode_len); + + if( rc != sizeof(SHA1digest) ) { + ber_memfree(orig_pass); + return LUTIL_PASSWD_ERR; + } + + /* hash credentials with salt */ + lutil_SHA1Init(&SHA1context); + lutil_SHA1Update(&SHA1context, + (const unsigned char *) cred->bv_val, cred->bv_len); + lutil_SHA1Final(SHA1digest, &SHA1context); + + /* compare */ + rc = memcmp((char *)orig_pass, (char *)SHA1digest, sizeof(SHA1digest)); + ber_memfree(orig_pass); + return rc ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK; +} +#endif + +static int chk_smd5( + const struct berval *sc, + const struct berval * passwd, + const struct berval * cred, + const char **text ) +{ + lutil_MD5_CTX MD5context; + unsigned char MD5digest[LUTIL_MD5_BYTES]; + int rc; + unsigned char *orig_pass = NULL; + size_t decode_len = LUTIL_BASE64_DECODE_LEN(passwd->bv_len); + + /* safety check */ + if (decode_len <= sizeof(MD5digest)) { + return LUTIL_PASSWD_ERR; + } + + /* base64 un-encode password */ + orig_pass = (unsigned char *) ber_memalloc(decode_len + 1); + + if( orig_pass == NULL ) return LUTIL_PASSWD_ERR; + + rc = lutil_b64_pton(passwd->bv_val, orig_pass, decode_len); + + if (rc <= (int)(sizeof(MD5digest))) { + ber_memfree(orig_pass); + return LUTIL_PASSWD_ERR; + } + + /* hash credentials with salt */ + lutil_MD5Init(&MD5context); + lutil_MD5Update(&MD5context, + (const unsigned char *) cred->bv_val, + cred->bv_len ); + lutil_MD5Update(&MD5context, + &orig_pass[sizeof(MD5digest)], + rc - sizeof(MD5digest)); + lutil_MD5Final(MD5digest, &MD5context); + + /* compare */ + rc = memcmp((char *)orig_pass, (char *)MD5digest, sizeof(MD5digest)); + ber_memfree(orig_pass); + return rc ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK; +} + +static int chk_md5( + const struct berval *sc, + const struct berval * passwd, + const struct berval * cred, + const char **text ) +{ + lutil_MD5_CTX MD5context; + unsigned char MD5digest[LUTIL_MD5_BYTES]; + int rc; + unsigned char *orig_pass = NULL; + size_t decode_len = LUTIL_BASE64_DECODE_LEN(passwd->bv_len); + + /* safety check */ + if (decode_len < sizeof(MD5digest)) { + return LUTIL_PASSWD_ERR; + } + + /* base64 un-encode password */ + orig_pass = (unsigned char *) ber_memalloc(decode_len + 1); + + if( orig_pass == NULL ) return LUTIL_PASSWD_ERR; + + rc = lutil_b64_pton(passwd->bv_val, orig_pass, decode_len); + if ( rc != sizeof(MD5digest) ) { + ber_memfree(orig_pass); + return LUTIL_PASSWD_ERR; + } + + /* hash credentials with salt */ + lutil_MD5Init(&MD5context); + lutil_MD5Update(&MD5context, + (const unsigned char *) cred->bv_val, + cred->bv_len ); + lutil_MD5Final(MD5digest, &MD5context); + + /* compare */ + rc = memcmp((char *)orig_pass, (char *)MD5digest, sizeof(MD5digest)); + ber_memfree(orig_pass); + return rc ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK; +} + +#ifdef SLAPD_LMHASH + +#if defined(HAVE_OPENSSL) + +/* + * abstract away setting the parity. + */ +static void +des_set_key_and_parity( des_key *key, unsigned char *keyData) +{ + memcpy(key, keyData, 8); + DES_set_odd_parity( key ); +} + + +#elif defined(HAVE_MOZNSS) + +/* + * implement MozNSS wrappers for the openSSL calls + */ +static void +des_set_key_and_parity( des_key *key, unsigned char *keyData) +{ + SECItem keyDataItem; + PK11SlotInfo *slot; + *key = NULL; + + keyDataItem.data = keyData; + keyDataItem.len = 8; + + slot = PK11_GetBestSlot(CKM_DES_ECB, NULL); + if (slot == NULL) { + return; + } + + /* NOTE: this will not work in FIPS mode. In order to make lmhash + * work in fips mode we need to define a LMHASH pbe mechanism and + * do the fulll key derivation inside the token */ + *key = PK11_ImportSymKey(slot, CKM_DES_ECB, PK11_OriginGenerated, + CKA_ENCRYPT, &keyDataItem, NULL); +} + +static void +DES_set_key_unchecked( des_key *key, des_context ctxt ) +{ + ctxt[0] = NULL; + + /* handle error conditions from previous call */ + if (!*key) { + return; + } + + ctxt[0] = PK11_CreateContextBySymKey(CKM_DES_ECB, CKA_ENCRYPT, *key, NULL); +} + +static void +DES_ecb_encrypt( des_data_block *plain, des_data_block *encrypted, + des_context ctxt, int op) +{ + SECStatus rv; + int size; + + if (ctxt[0] == NULL) { + /* need to fail here... */ + memset(encrypted, 0, sizeof(des_data_block)); + return; + } + rv = PK11_CipherOp(ctxt[0], (unsigned char *)&encrypted[0], + &size, sizeof(des_data_block), + (unsigned char *)&plain[0], sizeof(des_data_block)); + if (rv != SECSuccess) { + /* signal failure */ + memset(encrypted, 0, sizeof(des_data_block)); + return; + } + return; +} + +static int +des_failed(des_data_block *encrypted) +{ + static const des_data_block zero = { 0 }; + return memcmp(encrypted, zero, sizeof(zero)) == 0; +} + +static void +des_finish(des_key *key, des_context ctxt) +{ + if (*key) { + PK11_FreeSymKey(*key); + *key = NULL; + } + if (ctxt[0]) { + PK11_Finalize(ctxt[0]); + PK11_DestroyContext(ctxt[0], PR_TRUE); + ctxt[0] = NULL; + } +} + +#endif + +/* pseudocode from RFC2433 + * A.2 LmPasswordHash() + * + * LmPasswordHash( + * IN 0-to-14-oem-char Password, + * OUT 16-octet PasswordHash ) + * { + * Set UcasePassword to the uppercased Password + * Zero pad UcasePassword to 14 characters + * + * DesHash( 1st 7-octets of UcasePassword, + * giving 1st 8-octets of PasswordHash ) + * + * DesHash( 2nd 7-octets of UcasePassword, + * giving 2nd 8-octets of PasswordHash ) + * } + * + * + * A.3 DesHash() + * + * DesHash( + * IN 7-octet Clear, + * OUT 8-octet Cypher ) + * { + * * + * * Make Cypher an irreversibly encrypted form of Clear by + * * encrypting known text using Clear as the secret key. + * * The known text consists of the string + * * + * * KGS!@#$% + * * + * + * Set StdText to "KGS!@#$%" + * DesEncrypt( StdText, Clear, giving Cypher ) + * } + * + * + * A.4 DesEncrypt() + * + * DesEncrypt( + * IN 8-octet Clear, + * IN 7-octet Key, + * OUT 8-octet Cypher ) + * { + * * + * * Use the DES encryption algorithm [4] in ECB mode [9] + * * to encrypt Clear into Cypher such that Cypher can + * * only be decrypted back to Clear by providing Key. + * * Note that the DES algorithm takes as input a 64-bit + * * stream where the 8th, 16th, 24th, etc. bits are + * * parity bits ignored by the encrypting algorithm. + * * Unless you write your own DES to accept 56-bit input + * * without parity, you will need to insert the parity bits + * * yourself. + * * + * } + */ + +static void lmPasswd_to_key( + const char *lmPasswd, + des_key *key) +{ + const unsigned char *lpw = (const unsigned char *) lmPasswd; + unsigned char k[8]; + + /* make room for parity bits */ + k[0] = lpw[0]; + k[1] = ((lpw[0] & 0x01) << 7) | (lpw[1] >> 1); + k[2] = ((lpw[1] & 0x03) << 6) | (lpw[2] >> 2); + k[3] = ((lpw[2] & 0x07) << 5) | (lpw[3] >> 3); + k[4] = ((lpw[3] & 0x0F) << 4) | (lpw[4] >> 4); + k[5] = ((lpw[4] & 0x1F) << 3) | (lpw[5] >> 5); + k[6] = ((lpw[5] & 0x3F) << 2) | (lpw[6] >> 6); + k[7] = ((lpw[6] & 0x7F) << 1); + + des_set_key_and_parity( key, k ); +} + +static int chk_lanman( + const struct berval *scheme, + const struct berval *passwd, + const struct berval *cred, + const char **text ) +{ + ber_len_t i; + char UcasePassword[15]; + des_key key; + des_context schedule; + des_data_block StdText = "KGS!@#$%"; + des_data_block PasswordHash1, PasswordHash2; + char PasswordHash[33], storedPasswordHash[33]; + + for( i=0; i<cred->bv_len; i++) { + if(cred->bv_val[i] == '\0') { + return LUTIL_PASSWD_ERR; /* NUL character in password */ + } + } + + if( cred->bv_val[i] != '\0' ) { + return LUTIL_PASSWD_ERR; /* passwd must behave like a string */ + } + + strncpy( UcasePassword, cred->bv_val, 14 ); + UcasePassword[14] = '\0'; + ldap_pvt_str2upper( UcasePassword ); + + lmPasswd_to_key( UcasePassword, &key ); + DES_set_key_unchecked( &key, schedule ); + DES_ecb_encrypt( &StdText, &PasswordHash1, schedule , DES_ENCRYPT ); + + if (des_failed(&PasswordHash1)) { + return LUTIL_PASSWD_ERR; + } + + lmPasswd_to_key( &UcasePassword[7], &key ); + DES_set_key_unchecked( &key, schedule ); + DES_ecb_encrypt( &StdText, &PasswordHash2, schedule , DES_ENCRYPT ); + if (des_failed(&PasswordHash2)) { + return LUTIL_PASSWD_ERR; + } + + des_finish( &key, schedule ); + + sprintf( PasswordHash, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + PasswordHash1[0],PasswordHash1[1],PasswordHash1[2],PasswordHash1[3], + PasswordHash1[4],PasswordHash1[5],PasswordHash1[6],PasswordHash1[7], + PasswordHash2[0],PasswordHash2[1],PasswordHash2[2],PasswordHash2[3], + PasswordHash2[4],PasswordHash2[5],PasswordHash2[6],PasswordHash2[7] ); + + /* as a precaution convert stored password hash to lower case */ + strncpy( storedPasswordHash, passwd->bv_val, 32 ); + storedPasswordHash[32] = '\0'; + ldap_pvt_str2lower( storedPasswordHash ); + + return memcmp( PasswordHash, storedPasswordHash, 32) ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK; +} +#endif /* SLAPD_LMHASH */ + +#ifdef SLAPD_CRYPT +static int lutil_crypt( + const char *key, + const char *salt, + char **hash ) +{ + char *cr = crypt( key, salt ); + int rc; + + if( cr == NULL || cr[0] == '\0' ) { + /* salt must have been invalid */ + rc = LUTIL_PASSWD_ERR; + } else { + if ( hash ) { + *hash = ber_strdup( cr ); + rc = LUTIL_PASSWD_OK; + } else { + rc = strcmp( salt, cr ) ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK; + } + } + return rc; +} + +static int chk_crypt( + const struct berval *sc, + const struct berval * passwd, + const struct berval * cred, + const char **text ) +{ + unsigned int i; + + for( i=0; i<cred->bv_len; i++) { + if(cred->bv_val[i] == '\0') { + return LUTIL_PASSWD_ERR; /* NUL character in password */ + } + } + + if( cred->bv_val[i] != '\0' ) { + return LUTIL_PASSWD_ERR; /* cred must behave like a string */ + } + + if( passwd->bv_len < 2 ) { + return LUTIL_PASSWD_ERR; /* passwd must be at least two characters long */ + } + + for( i=0; i<passwd->bv_len; i++) { + if(passwd->bv_val[i] == '\0') { + return LUTIL_PASSWD_ERR; /* NUL character in password */ + } + } + + if( passwd->bv_val[i] != '\0' ) { + return LUTIL_PASSWD_ERR; /* passwd must behave like a string */ + } + + return lutil_cryptptr( cred->bv_val, passwd->bv_val, NULL ); +} + +# if defined( HAVE_GETPWNAM ) && defined( HAVE_STRUCT_PASSWD_PW_PASSWD ) +static int chk_unix( + const struct berval *sc, + const struct berval * passwd, + const struct berval * cred, + const char **text ) +{ + unsigned int i; + char *pw; + + for( i=0; i<cred->bv_len; i++) { + if(cred->bv_val[i] == '\0') { + return LUTIL_PASSWD_ERR; /* NUL character in password */ + } + } + if( cred->bv_val[i] != '\0' ) { + return LUTIL_PASSWD_ERR; /* cred must behave like a string */ + } + + for( i=0; i<passwd->bv_len; i++) { + if(passwd->bv_val[i] == '\0') { + return LUTIL_PASSWD_ERR; /* NUL character in password */ + } + } + + if( passwd->bv_val[i] != '\0' ) { + return LUTIL_PASSWD_ERR; /* passwd must behave like a string */ + } + + { + struct passwd *pwd = getpwnam(passwd->bv_val); + + if(pwd == NULL) { + return LUTIL_PASSWD_ERR; /* not found */ + } + + pw = pwd->pw_passwd; + } +# ifdef HAVE_GETSPNAM + { + struct spwd *spwd = getspnam(passwd->bv_val); + + if(spwd != NULL) { + pw = spwd->sp_pwdp; + } + } +# endif +# ifdef HAVE_AIX_SECURITY + { + struct userpw *upw = getuserpw(passwd->bv_val); + + if (upw != NULL) { + pw = upw->upw_passwd; + } + } +# endif + + if( pw == NULL || pw[0] == '\0' || pw[1] == '\0' ) { + /* password must must be at least two characters long */ + return LUTIL_PASSWD_ERR; + } + + return lutil_cryptptr( cred->bv_val, pw, NULL ); +} +# endif +#endif + +/* PASSWORD GENERATION ROUTINES */ + +#ifdef LUTIL_SHA1_BYTES +static int hash_ssha1( + const struct berval *scheme, + const struct berval *passwd, + struct berval *hash, + const char **text ) +{ + lutil_SHA1_CTX SHA1context; + unsigned char SHA1digest[LUTIL_SHA1_BYTES]; + char saltdata[SALT_SIZE]; + struct berval digest; + struct berval salt; + + digest.bv_val = (char *) SHA1digest; + digest.bv_len = sizeof(SHA1digest); + salt.bv_val = saltdata; + salt.bv_len = sizeof(saltdata); + + if( lutil_entropy( (unsigned char *) salt.bv_val, salt.bv_len) < 0 ) { + return LUTIL_PASSWD_ERR; + } + + lutil_SHA1Init( &SHA1context ); + lutil_SHA1Update( &SHA1context, + (const unsigned char *)passwd->bv_val, passwd->bv_len ); + lutil_SHA1Update( &SHA1context, + (const unsigned char *)salt.bv_val, salt.bv_len ); + lutil_SHA1Final( SHA1digest, &SHA1context ); + + return lutil_passwd_string64( scheme, &digest, hash, &salt); +} + +static int hash_sha1( + const struct berval *scheme, + const struct berval *passwd, + struct berval *hash, + const char **text ) +{ + lutil_SHA1_CTX SHA1context; + unsigned char SHA1digest[LUTIL_SHA1_BYTES]; + struct berval digest; + digest.bv_val = (char *) SHA1digest; + digest.bv_len = sizeof(SHA1digest); + + lutil_SHA1Init( &SHA1context ); + lutil_SHA1Update( &SHA1context, + (const unsigned char *)passwd->bv_val, passwd->bv_len ); + lutil_SHA1Final( SHA1digest, &SHA1context ); + + return lutil_passwd_string64( scheme, &digest, hash, NULL); +} +#endif + +static int hash_smd5( + const struct berval *scheme, + const struct berval *passwd, + struct berval *hash, + const char **text ) +{ + lutil_MD5_CTX MD5context; + unsigned char MD5digest[LUTIL_MD5_BYTES]; + char saltdata[SALT_SIZE]; + struct berval digest; + struct berval salt; + + digest.bv_val = (char *) MD5digest; + digest.bv_len = sizeof(MD5digest); + salt.bv_val = saltdata; + salt.bv_len = sizeof(saltdata); + + if( lutil_entropy( (unsigned char *) salt.bv_val, salt.bv_len) < 0 ) { + return LUTIL_PASSWD_ERR; + } + + lutil_MD5Init( &MD5context ); + lutil_MD5Update( &MD5context, + (const unsigned char *) passwd->bv_val, passwd->bv_len ); + lutil_MD5Update( &MD5context, + (const unsigned char *) salt.bv_val, salt.bv_len ); + lutil_MD5Final( MD5digest, &MD5context ); + + return lutil_passwd_string64( scheme, &digest, hash, &salt ); +} + +static int hash_md5( + const struct berval *scheme, + const struct berval *passwd, + struct berval *hash, + const char **text ) +{ + lutil_MD5_CTX MD5context; + unsigned char MD5digest[LUTIL_MD5_BYTES]; + + struct berval digest; + + digest.bv_val = (char *) MD5digest; + digest.bv_len = sizeof(MD5digest); + + lutil_MD5Init( &MD5context ); + lutil_MD5Update( &MD5context, + (const unsigned char *) passwd->bv_val, passwd->bv_len ); + lutil_MD5Final( MD5digest, &MD5context ); + + return lutil_passwd_string64( scheme, &digest, hash, NULL ); +; +} + +#ifdef SLAPD_LMHASH +static int hash_lanman( + const struct berval *scheme, + const struct berval *passwd, + struct berval *hash, + const char **text ) +{ + + ber_len_t i; + char UcasePassword[15]; + des_key key; + des_context schedule; + des_data_block StdText = "KGS!@#$%"; + des_data_block PasswordHash1, PasswordHash2; + char PasswordHash[33]; + + for( i=0; i<passwd->bv_len; i++) { + if(passwd->bv_val[i] == '\0') { + return LUTIL_PASSWD_ERR; /* NUL character in password */ + } + } + + if( passwd->bv_val[i] != '\0' ) { + return LUTIL_PASSWD_ERR; /* passwd must behave like a string */ + } + + strncpy( UcasePassword, passwd->bv_val, 14 ); + UcasePassword[14] = '\0'; + ldap_pvt_str2upper( UcasePassword ); + + lmPasswd_to_key( UcasePassword, &key ); + DES_set_key_unchecked( &key, schedule ); + DES_ecb_encrypt( &StdText, &PasswordHash1, schedule , DES_ENCRYPT ); + + lmPasswd_to_key( &UcasePassword[7], &key ); + DES_set_key_unchecked( &key, schedule ); + DES_ecb_encrypt( &StdText, &PasswordHash2, schedule , DES_ENCRYPT ); + + sprintf( PasswordHash, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + PasswordHash1[0],PasswordHash1[1],PasswordHash1[2],PasswordHash1[3], + PasswordHash1[4],PasswordHash1[5],PasswordHash1[6],PasswordHash1[7], + PasswordHash2[0],PasswordHash2[1],PasswordHash2[2],PasswordHash2[3], + PasswordHash2[4],PasswordHash2[5],PasswordHash2[6],PasswordHash2[7] ); + + hash->bv_val = PasswordHash; + hash->bv_len = 32; + + return pw_string( scheme, hash ); +} +#endif /* SLAPD_LMHASH */ + +#ifdef SLAPD_CRYPT +static int hash_crypt( + const struct berval *scheme, + const struct berval *passwd, + struct berval *hash, + const char **text ) +{ + unsigned char salt[32]; /* salt suitable for most anything */ + unsigned int i; + char *save; + int rc; + + for( i=0; i<passwd->bv_len; i++) { + if(passwd->bv_val[i] == '\0') { + return LUTIL_PASSWD_ERR; /* NUL character in password */ + } + } + + if( passwd->bv_val[i] != '\0' ) { + return LUTIL_PASSWD_ERR; /* passwd must behave like a string */ + } + + if( lutil_entropy( salt, sizeof( salt ) ) < 0 ) { + return LUTIL_PASSWD_ERR; + } + + for( i=0; i< ( sizeof(salt) - 1 ); i++ ) { + salt[i] = crypt64[ salt[i] % (sizeof(crypt64)-1) ]; + } + salt[sizeof( salt ) - 1 ] = '\0'; + + if( salt_format != NULL ) { + /* copy the salt we made into entropy before snprintfing + it back into the salt */ + char entropy[sizeof(salt)]; + strcpy( entropy, (char *) salt ); + snprintf( (char *) salt, sizeof(entropy), salt_format, entropy ); + } + + rc = lutil_cryptptr( passwd->bv_val, (char *) salt, &hash->bv_val ); + if ( rc != LUTIL_PASSWD_OK ) return rc; + + if( hash->bv_val == NULL ) return -1; + + hash->bv_len = strlen( hash->bv_val ); + + save = hash->bv_val; + + if( hash->bv_len == 0 ) { + rc = LUTIL_PASSWD_ERR; + } else { + rc = pw_string( scheme, hash ); + } + ber_memfree( save ); + return rc; +} +#endif + +int lutil_salt_format(const char *format) +{ +#ifdef SLAPD_CRYPT + ber_memfree( salt_format ); + + salt_format = format != NULL ? ber_strdup( format ) : NULL; +#endif + + return 0; +} + +#ifdef SLAPD_CLEARTEXT +static int hash_clear( + const struct berval *scheme, + const struct berval *passwd, + struct berval *hash, + const char **text ) +{ + ber_dupbv( hash, (struct berval *)passwd ); + return LUTIL_PASSWD_OK; +} +#endif + diff --git a/libraries/liblutil/ptest.c b/libraries/liblutil/ptest.c new file mode 100644 index 0000000..d2983c4 --- /dev/null +++ b/libraries/liblutil/ptest.c @@ -0,0 +1,112 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/ctype.h> +#include <ac/signal.h> +#include <ac/socket.h> +#include <ac/string.h> +#include <ac/time.h> + +#include <lber.h> + +#include "lutil.h" + +/* + * Password Test Program + */ + +static char *hash[] = { +#ifdef SLAP_AUTHPASSWD + "SHA1", "MD5", +#else +#ifdef SLAPD_CRYPT + "{CRYPT}", +#endif + "{SSHA}", "{SMD5}", + "{SHA}", "{MD5}", + "{BOGUS}", +#endif + NULL +}; + +static struct berval pw[] = { + { sizeof("secret")-1, "secret" }, + { sizeof("binary\0secret")-1, "binary\0secret" }, + { 0, NULL } +}; + +int +main( int argc, char *argv[] ) +{ + int i, j, rc; + struct berval *passwd; +#ifdef SLAP_AUTHPASSWD + struct berval *salt; +#endif + struct berval bad; + bad.bv_val = "bad password"; + bad.bv_len = sizeof("bad password")-1; + + for( i= 0; hash[i]; i++ ) { + for( j = 0; pw[j].bv_len; j++ ) { +#ifdef SLAP_AUTHPASSWD + rc = lutil_authpasswd_hash( &pw[j], + &passwd, &salt, hash[i] ); + + if( rc ) +#else + passwd = lutil_passwd_hash( &pw[j], hash[i] ); + + if( passwd == NULL ) +#endif + { + printf("%s generate fail: %s (%d)\n", + hash[i], pw[j].bv_val, pw[j].bv_len ); + continue; + } + + +#ifdef SLAP_AUTHPASSWD + rc = lutil_authpasswd( &pw[j], passwd, salt, NULL ); +#else + rc = lutil_passwd( passwd, &pw[j], NULL ); +#endif + + printf("%s (%d): %s (%d)\t(%d) %s\n", + pw[j].bv_val, pw[j].bv_len, passwd->bv_val, passwd->bv_len, + rc, rc == 0 ? "OKAY" : "BAD" ); + +#ifdef SLAP_AUTHPASSWD + rc = lutil_authpasswd( passwd, salt, &bad, NULL ); +#else + rc = lutil_passwd( passwd, &bad, NULL ); +#endif + + printf("%s (%d): %s (%d)\t(%d) %s\n", + bad.bv_val, bad.bv_len, passwd->bv_val, passwd->bv_len, + rc, rc != 0 ? "OKAY" : "BAD" ); + } + + printf("\n"); + } + + return EXIT_SUCCESS; +} diff --git a/libraries/liblutil/sasl.c b/libraries/liblutil/sasl.c new file mode 100644 index 0000000..59819b6 --- /dev/null +++ b/libraries/liblutil/sasl.c @@ -0,0 +1,234 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#ifdef HAVE_CYRUS_SASL + +#include <stdio.h> +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/unistd.h> + +#ifdef HAVE_SASL_SASL_H +#include <sasl/sasl.h> +#else +#include <sasl.h> +#endif + +#include <ldap.h> +#include "ldap_pvt.h" +#include "lutil_ldap.h" + + +typedef struct lutil_sasl_defaults_s { + char *mech; + char *realm; + char *authcid; + char *passwd; + char *authzid; + char **resps; + int nresps; +} lutilSASLdefaults; + + +void +lutil_sasl_freedefs( + void *defaults ) +{ + lutilSASLdefaults *defs = defaults; + + assert( defs != NULL ); + + if (defs->mech) ber_memfree(defs->mech); + if (defs->realm) ber_memfree(defs->realm); + if (defs->authcid) ber_memfree(defs->authcid); + if (defs->passwd) ber_memfree(defs->passwd); + if (defs->authzid) ber_memfree(defs->authzid); + if (defs->resps) ldap_charray_free(defs->resps); + + ber_memfree(defs); +} + +void * +lutil_sasl_defaults( + LDAP *ld, + char *mech, + char *realm, + char *authcid, + char *passwd, + char *authzid ) +{ + lutilSASLdefaults *defaults; + + defaults = ber_memalloc( sizeof( lutilSASLdefaults ) ); + + if( defaults == NULL ) return NULL; + + defaults->mech = mech ? ber_strdup(mech) : NULL; + defaults->realm = realm ? ber_strdup(realm) : NULL; + defaults->authcid = authcid ? ber_strdup(authcid) : NULL; + defaults->passwd = passwd ? ber_strdup(passwd) : NULL; + defaults->authzid = authzid ? ber_strdup(authzid) : NULL; + + if( defaults->mech == NULL ) { + ldap_get_option( ld, LDAP_OPT_X_SASL_MECH, &defaults->mech ); + } + if( defaults->realm == NULL ) { + ldap_get_option( ld, LDAP_OPT_X_SASL_REALM, &defaults->realm ); + } + if( defaults->authcid == NULL ) { + ldap_get_option( ld, LDAP_OPT_X_SASL_AUTHCID, &defaults->authcid ); + } + if( defaults->authzid == NULL ) { + ldap_get_option( ld, LDAP_OPT_X_SASL_AUTHZID, &defaults->authzid ); + } + defaults->resps = NULL; + defaults->nresps = 0; + + return defaults; +} + +static int interaction( + unsigned flags, + sasl_interact_t *interact, + lutilSASLdefaults *defaults ) +{ + const char *dflt = interact->defresult; + char input[1024]; + + int noecho=0; + int challenge=0; + + switch( interact->id ) { + case SASL_CB_GETREALM: + if( defaults ) dflt = defaults->realm; + break; + case SASL_CB_AUTHNAME: + if( defaults ) dflt = defaults->authcid; + break; + case SASL_CB_PASS: + if( defaults ) dflt = defaults->passwd; + noecho = 1; + break; + case SASL_CB_USER: + if( defaults ) dflt = defaults->authzid; + break; + case SASL_CB_NOECHOPROMPT: + noecho = 1; + challenge = 1; + break; + case SASL_CB_ECHOPROMPT: + challenge = 1; + break; + } + + if( dflt && !*dflt ) dflt = NULL; + + if( flags != LDAP_SASL_INTERACTIVE && + ( dflt || interact->id == SASL_CB_USER ) ) + { + goto use_default; + } + + if( flags == LDAP_SASL_QUIET ) { + /* don't prompt */ + return LDAP_OTHER; + } + + if( challenge ) { + if( interact->challenge ) { + fprintf( stderr, _("Challenge: %s\n"), interact->challenge ); + } + } + + if( dflt ) { + fprintf( stderr, _("Default: %s\n"), dflt ); + } + + snprintf( input, sizeof input, "%s: ", + interact->prompt ? interact->prompt : _("Interact") ); + + if( noecho ) { + interact->result = (char *) getpassphrase( input ); + interact->len = interact->result + ? strlen( interact->result ) : 0; + + } else { + /* prompt user */ + fputs( input, stderr ); + + /* get input */ + interact->result = fgets( input, sizeof(input), stdin ); + + if( interact->result == NULL ) { + interact->len = 0; + return LDAP_UNAVAILABLE; + } + + /* len of input */ + interact->len = strlen(input); + + if( interact->len > 0 && input[interact->len - 1] == '\n' ) { + /* input includes '\n', trim it */ + interact->len--; + input[interact->len] = '\0'; + } + } + + + if( interact->len > 0 ) { + /* duplicate */ + char *p = (char *)interact->result; + ldap_charray_add(&defaults->resps, interact->result); + interact->result = defaults->resps[defaults->nresps++]; + + /* zap */ + memset( p, '\0', interact->len ); + + } else { +use_default: + /* input must be empty */ + interact->result = (dflt && *dflt) ? dflt : ""; + interact->len = strlen( interact->result ); + } + + return LDAP_SUCCESS; +} + +int lutil_sasl_interact( + LDAP *ld, + unsigned flags, + void *defaults, + void *in ) +{ + sasl_interact_t *interact = in; + + if( ld == NULL ) return LDAP_PARAM_ERROR; + + if( flags == LDAP_SASL_INTERACTIVE ) { + fputs( _("SASL Interaction\n"), stderr ); + } + + while( interact->id != SASL_CB_LIST_END ) { + int rc = interaction( flags, interact, defaults ); + + if( rc ) return rc; + interact++; + } + + return LDAP_SUCCESS; +} +#endif diff --git a/libraries/liblutil/setproctitle.c b/libraries/liblutil/setproctitle.c new file mode 100644 index 0000000..2215210 --- /dev/null +++ b/libraries/liblutil/setproctitle.c @@ -0,0 +1,78 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1990,1991 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ + +#include "portable.h" + +#ifndef HAVE_SETPROCTITLE + +#include <stdio.h> + +#include <ac/stdlib.h> + +#include <ac/setproctitle.h> +#include <ac/string.h> +#include <ac/stdarg.h> + +char **Argv; /* pointer to original (main's) argv */ +int Argc; /* original argc */ + +/* + * takes a printf-style format string (fmt) and up to three parameters (a,b,c) + * this clobbers the original argv... + */ + +/* VARARGS */ +void setproctitle( const char *fmt, ... ) +{ + static char *endargv = (char *)0; + char *s; + int i; + char buf[ 1024 ]; + va_list ap; + + va_start(ap, fmt); + + buf[sizeof(buf) - 1] = '\0'; + vsnprintf( buf, sizeof(buf)-1, fmt, ap ); + + va_end(ap); + + if ( endargv == (char *)0 ) { + /* set pointer to end of original argv */ + endargv = Argv[ Argc-1 ] + strlen( Argv[ Argc-1 ] ); + } + /* make ps print "([prog name])" */ + s = Argv[0]; + *s++ = '-'; + i = strlen( buf ); + if ( i > endargv - s - 2 ) { + i = endargv - s - 2; + buf[ i ] = '\0'; + } + strcpy( s, buf ); + s += i; + while ( s < endargv ) *s++ = ' '; +} +#endif /* NOSETPROCTITLE */ diff --git a/libraries/liblutil/sha1.c b/libraries/liblutil/sha1.c new file mode 100644 index 0000000..ae153c2 --- /dev/null +++ b/libraries/liblutil/sha1.c @@ -0,0 +1,288 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* This work was derived from code developed by Steve Reid and + * adapted for use in OpenLDAP by Kurt D. Zeilenga. + */ + + +/* Acquired from: + * $OpenBSD: sha1.c,v 1.9 1997/07/23 21:12:32 kstailey Exp $ */ +/* + * SHA-1 in C + * By Steve Reid <steve@edmweb.com> + * 100% Public Domain + * + * Test Vectors (from FIPS PUB 180-1) + * "abc" + * A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D + * "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + * 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 + * A million repetitions of "a" + * 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F + */ +/* + * This code assumes uint32 is 32 bits and char is 8 bits + */ + +#include "portable.h" +#include <ac/param.h> +#include <ac/string.h> +#include <ac/socket.h> +#include <ac/bytes.h> + +#include "lutil_sha1.h" + +#ifdef LUTIL_SHA1_BYTES + +/* undefining this will cause pointer alignment errors */ +#define SHA1HANDSOFF /* Copies data before messing with it. */ +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +/* + * blk0() and blk() perform the initial expand. + * I got the idea of expanding during the round function from SSLeay + */ +#if BYTE_ORDER == LITTLE_ENDIAN +# define blk0(i) (block[i] = (rol(block[i],24)&0xFF00FF00) \ + |(rol(block[i],8)&0x00FF00FF)) +#else +# define blk0(i) block[i] +#endif +#define blk(i) (block[i&15] = rol(block[(i+13)&15]^block[(i+8)&15] \ + ^block[(i+2)&15]^block[i&15],1)) + +/* + * (R0+R1), R2, R3, R4 are the different operations (rounds) used in SHA1 + */ +#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); +#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); +#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); + + +/* + * Hash a single 512-bit block. This is the core of the algorithm. + */ +void +lutil_SHA1Transform( uint32 *state, const unsigned char *buffer ) +{ + uint32 a, b, c, d, e; + +#ifdef SHA1HANDSOFF + uint32 block[16]; + (void)AC_MEMCPY(block, buffer, 64); +#else + uint32 *block = (u_int32 *) buffer; +#endif + + /* Copy context->state[] to working vars */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + + /* 4 rounds of 20 operations each. Loop unrolled. */ + R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); + R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); + R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); + R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); + + /* Add the working vars back into context.state[] */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + + /* Wipe variables */ + a = b = c = d = e = 0; +} + + +/* + * lutil_SHA1Init - Initialize new context + */ +void +lutil_SHA1Init( lutil_SHA1_CTX *context ) +{ + + /* SHA1 initialization constants */ + context->state[0] = 0x67452301; + context->state[1] = 0xEFCDAB89; + context->state[2] = 0x98BADCFE; + context->state[3] = 0x10325476; + context->state[4] = 0xC3D2E1F0; + context->count[0] = context->count[1] = 0; +} + + +/* + * Run your data through this. + */ +void +lutil_SHA1Update( + lutil_SHA1_CTX *context, + const unsigned char *data, + uint32 len +) +{ + u_int i, j; + + j = context->count[0]; + if ((context->count[0] += len << 3) < j) + context->count[1] += (len>>29)+1; + j = (j >> 3) & 63; + if ((j + len) > 63) { + (void)AC_MEMCPY(&context->buffer[j], data, (i = 64-j)); + lutil_SHA1Transform(context->state, context->buffer); + for ( ; i + 63 < len; i += 64) + lutil_SHA1Transform(context->state, &data[i]); + j = 0; + } else { + i = 0; + } + (void)AC_MEMCPY(&context->buffer[j], &data[i], len - i); +} + + +/* + * Add padding and return the message digest. + */ +void +lutil_SHA1Final( unsigned char *digest, lutil_SHA1_CTX *context ) +{ + u_int i; + unsigned char finalcount[8]; + + for (i = 0; i < 8; i++) { + finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] + >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ + } + lutil_SHA1Update(context, (unsigned char *)"\200", 1); + while ((context->count[0] & 504) != 448) + lutil_SHA1Update(context, (unsigned char *)"\0", 1); + lutil_SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */ + + if (digest) { + for (i = 0; i < 20; i++) + digest[i] = (unsigned char) + ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); + } +} + + +/* sha1hl.c + * ---------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * <phk@login.dkuug.dk> wrote this file. As long as you retain this notice you + * can do whatever you want with this stuff. If we meet some day, and you think + * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp + * ---------------------------------------------------------------------------- + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char rcsid[] = "$OpenBSD: sha1hl.c,v 1.1 1997/07/12 20:06:03 millert Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#include <stdio.h> +#include <ac/stdlib.h> + +#include <ac/errno.h> +#include <ac/unistd.h> + +#ifdef HAVE_SYS_FILE_H +#include <sys/file.h> +#endif + +#ifdef HAVE_IO_H +#include <io.h> +#endif + +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif + + +/* ARGSUSED */ +char * +lutil_SHA1End( lutil_SHA1_CTX *ctx, char *buf ) +{ + int i; + char *p = buf; + unsigned char digest[20]; + static const char hex[]="0123456789abcdef"; + + if (p == NULL && (p = malloc(41)) == NULL) + return 0; + + lutil_SHA1Final(digest,ctx); + for (i = 0; i < 20; i++) { + p[i + i] = hex[digest[i] >> 4]; + p[i + i + 1] = hex[digest[i] & 0x0f]; + } + p[i + i] = '\0'; + return(p); +} + +char * +lutil_SHA1File( char *filename, char *buf ) +{ + unsigned char buffer[BUFSIZ]; + lutil_SHA1_CTX ctx; + int fd, num, oerrno; + + lutil_SHA1Init(&ctx); + + if ((fd = open(filename,O_RDONLY)) < 0) + return(0); + + while ((num = read(fd, buffer, sizeof(buffer))) > 0) + lutil_SHA1Update(&ctx, buffer, num); + + oerrno = errno; + close(fd); + errno = oerrno; + return(num < 0 ? 0 : lutil_SHA1End(&ctx, buf)); +} + +char * +lutil_SHA1Data( const unsigned char *data, size_t len, char *buf ) +{ + lutil_SHA1_CTX ctx; + + lutil_SHA1Init(&ctx); + lutil_SHA1Update(&ctx, data, len); + return(lutil_SHA1End(&ctx, buf)); +} + +#endif diff --git a/libraries/liblutil/signal.c b/libraries/liblutil/signal.c new file mode 100644 index 0000000..a73e311 --- /dev/null +++ b/libraries/liblutil/signal.c @@ -0,0 +1,41 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#ifdef HAVE_SIGACTION +#include <ac/string.h> +#include <ac/signal.h> + +lutil_sig_t +lutil_sigaction(int sig, lutil_sig_t func) +{ + struct sigaction action, oaction; + + memset( &action, '\0', sizeof(action) ); + + action.sa_handler = func; + sigemptyset( &action.sa_mask ); +#ifdef SA_RESTART + action.sa_flags |= SA_RESTART; +#endif + + if( sigaction( sig, &action, &oaction ) != 0 ) { + return NULL; + } + + return oaction.sa_handler; +} +#endif diff --git a/libraries/liblutil/slapdmsg.bin b/libraries/liblutil/slapdmsg.bin Binary files differnew file mode 100644 index 0000000..d8ca806 --- /dev/null +++ b/libraries/liblutil/slapdmsg.bin diff --git a/libraries/liblutil/slapdmsg.h b/libraries/liblutil/slapdmsg.h new file mode 100644 index 0000000..21a84fb --- /dev/null +++ b/libraries/liblutil/slapdmsg.h @@ -0,0 +1,65 @@ +// +// This file contains message strings for the OpenLDAP slapd service. +// +// This file should be compiled as follows +// mc -v slapdmsg.mc -r $(IntDir) +// rc /v /r $(IntDir)\slapdmsg.rc +// The mc (message compiler) command generates the .rc and .h files from this file. The +// rc (resource compiler) takes the .rc file and produces a .res file that can be linked +// with the final executable application. The application is then registered as a message +// source with by creating the appropriate entries in the system registry. +// +// +// Values are 32 bit values layed out as follows: +// +// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 +// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +// +---+-+-+-----------------------+-------------------------------+ +// |Sev|C|R| Facility | Code | +// +---+-+-+-----------------------+-------------------------------+ +// +// where +// +// Sev - is the severity code +// +// 00 - Success +// 01 - Informational +// 10 - Warning +// 11 - Error +// +// C - is the Customer code flag +// +// R - is a reserved bit +// +// Facility - is the facility code +// +// Code - is the facility's status code +// +// +// Define the facility codes +// + + +// +// Define the severity codes +// + + +// +// MessageId: MSG_SVC_STARTED +// +// MessageText: +// +// OpenLDAP service started. debuglevel=%1, conffile=%2, urls=%3 +// +#define MSG_SVC_STARTED 0x40000500L + +// +// MessageId: MSG_SVC_STOPPED +// +// MessageText: +// +// OpenLDAP service stopped. +// +#define MSG_SVC_STOPPED 0x40000501L + diff --git a/libraries/liblutil/slapdmsg.mc b/libraries/liblutil/slapdmsg.mc new file mode 100644 index 0000000..53401f0 --- /dev/null +++ b/libraries/liblutil/slapdmsg.mc @@ -0,0 +1,28 @@ +;// +;// This file contains message strings for the OpenLDAP slapd service. +;// +;// This file should be compiled as follows +;// mc -v slapdmsg.mc -r $(IntDir) +;// rc /v /r $(IntDir)\slapdmsg.rc +;// The mc (message compiler) command generates the .rc and .h files from this file. The +;// rc (resource compiler) takes the .rc file and produces a .res file that can be linked +;// with the final executable application. The application is then registered as a message +;// source with by creating the appropriate entries in the system registry. +;// + +MessageID=0x500 +Severity=Informational +SymbolicName=MSG_SVC_STARTED +Facility=Application +Language=English +OpenLDAP service started. debuglevel=%1, conffile=%2, urls=%3 +. + + +MessageID=0x501 +Severity=Informational +SymbolicName=MSG_SVC_STOPPED +Facility=Application +Language=English +OpenLDAP service stopped. +. diff --git a/libraries/liblutil/slapdmsg.rc b/libraries/liblutil/slapdmsg.rc new file mode 100644 index 0000000..f967de2 --- /dev/null +++ b/libraries/liblutil/slapdmsg.rc @@ -0,0 +1,2 @@ +LANGUAGE 0x9,0x1 +1 11 slapdmsg.bin diff --git a/libraries/liblutil/sockpair.c b/libraries/liblutil/sockpair.c new file mode 100644 index 0000000..ecb9140 --- /dev/null +++ b/libraries/liblutil/sockpair.c @@ -0,0 +1,78 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" +#include <ac/socket.h> +#include <ac/unistd.h> + +#include <lutil.h> + +/* Return a pair of socket descriptors that are connected to each other. + * The returned descriptors are suitable for use with select(). The two + * descriptors may or may not be identical; the function may return + * the same descriptor number in both slots. It is guaranteed that + * data written on sds[1] will be readable on sds[0]. The returned + * descriptors may be datagram oriented, so data should be written + * in reasonably small pieces and read all at once. On Unix systems + * this function is best implemented using a single pipe() call. + */ + +int lutil_pair( ber_socket_t sds[2] ) +{ +#ifdef USE_PIPE + return pipe( sds ); +#else + struct sockaddr_in si; + int rc; + ber_socklen_t len = sizeof(si); + ber_socket_t sd; + + sd = socket( AF_INET, SOCK_DGRAM, 0 ); + if ( sd == AC_SOCKET_INVALID ) { + return sd; + } + + (void) memset( (void*) &si, '\0', len ); + si.sin_family = AF_INET; + si.sin_port = 0; + si.sin_addr.s_addr = htonl( INADDR_LOOPBACK ); + + rc = bind( sd, (struct sockaddr *)&si, len ); + if ( rc == AC_SOCKET_ERROR ) { + tcp_close(sd); + return rc; + } + + rc = getsockname( sd, (struct sockaddr *)&si, &len ); + if ( rc == AC_SOCKET_ERROR ) { + tcp_close(sd); + return rc; + } + + rc = connect( sd, (struct sockaddr *)&si, len ); + if ( rc == AC_SOCKET_ERROR ) { + tcp_close(sd); + return rc; + } + + sds[0] = sd; +#if !HAVE_WINSOCK + sds[1] = dup( sds[0] ); +#else + sds[1] = sds[0]; +#endif + return 0; +#endif +} diff --git a/libraries/liblutil/tavl.c b/libraries/liblutil/tavl.c new file mode 100644 index 0000000..ed12019 --- /dev/null +++ b/libraries/liblutil/tavl.c @@ -0,0 +1,523 @@ +/* avl.c - routines to implement an avl tree */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2005-2021 The OpenLDAP Foundation. + * Portions Copyright (c) 2005 by Howard Chu, Symas Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENTS: + * This work was initially developed by Howard Chu for inclusion + * in OpenLDAP software. + */ + +#include "portable.h" + +#include <limits.h> +#include <stdio.h> +#include <ac/stdlib.h> + +#ifdef CSRIMALLOC +#define ber_memalloc malloc +#define ber_memrealloc realloc +#define ber_memfree free +#else +#include "lber.h" +#endif + +#define AVL_INTERNAL +#include "avl.h" + +/* Maximum tree depth this host's address space could support */ +#define MAX_TREE_DEPTH (sizeof(void *) * CHAR_BIT) + +static const int avl_bfs[] = {LH, RH}; + +/* + * Threaded AVL trees - for fast in-order traversal of nodes. + */ +/* + * tavl_insert -- insert a node containing data data into the avl tree + * with root root. fcmp is a function to call to compare the data portion + * of two nodes. it should take two arguments and return <, >, or == 0, + * depending on whether its first argument is <, >, or == its second + * argument (like strcmp, e.g.). fdup is a function to call when a duplicate + * node is inserted. it should return 0, or -1 and its return value + * will be the return value from avl_insert in the case of a duplicate node. + * the function will be called with the original node's data as its first + * argument and with the incoming duplicate node's data as its second + * argument. this could be used, for example, to keep a count with each + * node. + * + * NOTE: this routine may malloc memory + */ +int +tavl_insert( Avlnode ** root, void *data, AVL_CMP fcmp, AVL_DUP fdup ) +{ + Avlnode *t, *p, *s, *q, *r; + int a, cmp, ncmp; + + if ( *root == NULL ) { + if (( r = (Avlnode *) ber_memalloc( sizeof( Avlnode ))) == NULL ) { + return( -1 ); + } + r->avl_link[0] = r->avl_link[1] = NULL; + r->avl_data = data; + r->avl_bf = EH; + r->avl_bits[0] = r->avl_bits[1] = AVL_THREAD; + *root = r; + + return( 0 ); + } + + t = NULL; + s = p = *root; + + /* find insertion point */ + while (1) { + cmp = fcmp( data, p->avl_data ); + if ( cmp == 0 ) + return (*fdup)( p->avl_data, data ); + + cmp = (cmp > 0); + q = avl_child( p, cmp ); + if (q == NULL) { + /* insert */ + if (( q = (Avlnode *) ber_memalloc( sizeof( Avlnode ))) == NULL ) { + return( -1 ); + } + q->avl_link[cmp] = p->avl_link[cmp]; + q->avl_link[!cmp] = p; + q->avl_data = data; + q->avl_bf = EH; + q->avl_bits[0] = q->avl_bits[1] = AVL_THREAD; + + p->avl_link[cmp] = q; + p->avl_bits[cmp] = AVL_CHILD; + break; + } else if ( q->avl_bf ) { + t = p; + s = q; + } + p = q; + } + + /* adjust balance factors */ + cmp = fcmp( data, s->avl_data ) > 0; + r = p = s->avl_link[cmp]; + a = avl_bfs[cmp]; + + while ( p != q ) { + cmp = fcmp( data, p->avl_data ) > 0; + p->avl_bf = avl_bfs[cmp]; + p = p->avl_link[cmp]; + } + + /* checks and balances */ + + if ( s->avl_bf == EH ) { + s->avl_bf = a; + return 0; + } else if ( s->avl_bf == -a ) { + s->avl_bf = EH; + return 0; + } else if ( s->avl_bf == a ) { + cmp = (a > 0); + ncmp = !cmp; + if ( r->avl_bf == a ) { + /* single rotation */ + p = r; + if ( r->avl_bits[ncmp] == AVL_THREAD ) { + r->avl_bits[ncmp] = AVL_CHILD; + s->avl_bits[cmp] = AVL_THREAD; + } else { + s->avl_link[cmp] = r->avl_link[ncmp]; + r->avl_link[ncmp] = s; + } + s->avl_bf = 0; + r->avl_bf = 0; + } else if ( r->avl_bf == -a ) { + /* double rotation */ + p = r->avl_link[ncmp]; + if ( p->avl_bits[cmp] == AVL_THREAD ) { + p->avl_bits[cmp] = AVL_CHILD; + r->avl_bits[ncmp] = AVL_THREAD; + } else { + r->avl_link[ncmp] = p->avl_link[cmp]; + p->avl_link[cmp] = r; + } + if ( p->avl_bits[ncmp] == AVL_THREAD ) { + p->avl_bits[ncmp] = AVL_CHILD; + s->avl_link[cmp] = p; + s->avl_bits[cmp] = AVL_THREAD; + } else { + s->avl_link[cmp] = p->avl_link[ncmp]; + p->avl_link[ncmp] = s; + } + if ( p->avl_bf == a ) { + s->avl_bf = -a; + r->avl_bf = 0; + } else if ( p->avl_bf == -a ) { + s->avl_bf = 0; + r->avl_bf = a; + } else { + s->avl_bf = 0; + r->avl_bf = 0; + } + p->avl_bf = 0; + } + /* Update parent */ + if ( t == NULL ) + *root = p; + else if ( s == t->avl_right ) + t->avl_right = p; + else + t->avl_left = p; + } + + return 0; +} + +void* +tavl_delete( Avlnode **root, void* data, AVL_CMP fcmp ) +{ + Avlnode *p, *q, *r, *top; + int side, side_bf, shorter, nside = -1; + + /* parent stack */ + Avlnode *pptr[MAX_TREE_DEPTH]; + unsigned char pdir[MAX_TREE_DEPTH]; + int depth = 0; + + if ( *root == NULL ) + return NULL; + + p = *root; + + while (1) { + side = fcmp( data, p->avl_data ); + if ( !side ) + break; + side = ( side > 0 ); + pdir[depth] = side; + pptr[depth++] = p; + + if ( p->avl_bits[side] == AVL_THREAD ) + return NULL; + p = p->avl_link[side]; + } + data = p->avl_data; + + /* If this node has two children, swap so we are deleting a node with + * at most one child. + */ + if ( p->avl_bits[0] == AVL_CHILD && p->avl_bits[1] == AVL_CHILD && + p->avl_link[0] && p->avl_link[1] ) { + + /* find the immediate predecessor <q> */ + q = p->avl_link[0]; + side = depth; + pdir[depth++] = 0; + while (q->avl_bits[1] == AVL_CHILD && q->avl_link[1]) { + pdir[depth] = 1; + pptr[depth++] = q; + q = q->avl_link[1]; + } + /* swap links */ + r = p->avl_link[0]; + p->avl_link[0] = q->avl_link[0]; + q->avl_link[0] = r; + + q->avl_link[1] = p->avl_link[1]; + p->avl_link[1] = q; + + p->avl_bits[0] = q->avl_bits[0]; + p->avl_bits[1] = q->avl_bits[1]; + q->avl_bits[0] = q->avl_bits[1] = AVL_CHILD; + + q->avl_bf = p->avl_bf; + + /* fix stack positions: old parent of p points to q */ + pptr[side] = q; + if ( side ) { + r = pptr[side-1]; + r->avl_link[pdir[side-1]] = q; + } else { + *root = q; + } + /* new parent of p points to p */ + if ( depth-side > 1 ) { + r = pptr[depth-1]; + r->avl_link[1] = p; + } else { + q->avl_link[0] = p; + } + + /* fix right subtree: successor of p points to q */ + r = q->avl_link[1]; + while ( r->avl_bits[0] == AVL_CHILD && r->avl_link[0] ) + r = r->avl_link[0]; + r->avl_link[0] = q; + } + + /* now <p> has at most one child, get it */ + if ( p->avl_link[0] && p->avl_bits[0] == AVL_CHILD ) { + q = p->avl_link[0]; + /* Preserve thread continuity */ + r = p->avl_link[1]; + nside = 1; + } else if ( p->avl_link[1] && p->avl_bits[1] == AVL_CHILD ) { + q = p->avl_link[1]; + r = p->avl_link[0]; + nside = 0; + } else { + q = NULL; + if ( depth > 0 ) + r = p->avl_link[pdir[depth-1]]; + else + r = NULL; + } + + ber_memfree( p ); + + /* Update child thread */ + if ( q ) { + for ( ; q->avl_bits[nside] == AVL_CHILD && q->avl_link[nside]; + q = q->avl_link[nside] ) ; + q->avl_link[nside] = r; + } + + if ( !depth ) { + *root = q; + return data; + } + + /* set the child into p's parent */ + depth--; + p = pptr[depth]; + side = pdir[depth]; + p->avl_link[side] = q; + + if ( !q ) { + p->avl_bits[side] = AVL_THREAD; + p->avl_link[side] = r; + } + + top = NULL; + shorter = 1; + + while ( shorter ) { + p = pptr[depth]; + side = pdir[depth]; + nside = !side; + side_bf = avl_bfs[side]; + + /* case 1: height unchanged */ + if ( p->avl_bf == EH ) { + /* Tree is now heavier on opposite side */ + p->avl_bf = avl_bfs[nside]; + shorter = 0; + + } else if ( p->avl_bf == side_bf ) { + /* case 2: taller subtree shortened, height reduced */ + p->avl_bf = EH; + } else { + /* case 3: shorter subtree shortened */ + if ( depth ) + top = pptr[depth-1]; /* p->parent; */ + else + top = NULL; + /* set <q> to the taller of the two subtrees of <p> */ + q = p->avl_link[nside]; + if ( q->avl_bf == EH ) { + /* case 3a: height unchanged, single rotate */ + if ( q->avl_bits[side] == AVL_THREAD ) { + q->avl_bits[side] = AVL_CHILD; + p->avl_bits[nside] = AVL_THREAD; + } else { + p->avl_link[nside] = q->avl_link[side]; + q->avl_link[side] = p; + } + shorter = 0; + q->avl_bf = side_bf; + p->avl_bf = (- side_bf); + + } else if ( q->avl_bf == p->avl_bf ) { + /* case 3b: height reduced, single rotate */ + if ( q->avl_bits[side] == AVL_THREAD ) { + q->avl_bits[side] = AVL_CHILD; + p->avl_bits[nside] = AVL_THREAD; + } else { + p->avl_link[nside] = q->avl_link[side]; + q->avl_link[side] = p; + } + shorter = 1; + q->avl_bf = EH; + p->avl_bf = EH; + + } else { + /* case 3c: height reduced, balance factors opposite */ + r = q->avl_link[side]; + if ( r->avl_bits[nside] == AVL_THREAD ) { + r->avl_bits[nside] = AVL_CHILD; + q->avl_bits[side] = AVL_THREAD; + } else { + q->avl_link[side] = r->avl_link[nside]; + r->avl_link[nside] = q; + } + + if ( r->avl_bits[side] == AVL_THREAD ) { + r->avl_bits[side] = AVL_CHILD; + p->avl_bits[nside] = AVL_THREAD; + p->avl_link[nside] = r; + } else { + p->avl_link[nside] = r->avl_link[side]; + r->avl_link[side] = p; + } + + if ( r->avl_bf == side_bf ) { + q->avl_bf = (- side_bf); + p->avl_bf = EH; + } else if ( r->avl_bf == (- side_bf)) { + q->avl_bf = EH; + p->avl_bf = side_bf; + } else { + q->avl_bf = EH; + p->avl_bf = EH; + } + r->avl_bf = EH; + q = r; + } + /* a rotation has caused <q> (or <r> in case 3c) to become + * the root. let <p>'s former parent know this. + */ + if ( top == NULL ) { + *root = q; + } else if (top->avl_link[0] == p) { + top->avl_link[0] = q; + } else { + top->avl_link[1] = q; + } + /* end case 3 */ + p = q; + } + if ( !depth ) + break; + depth--; + } /* end while(shorter) */ + + return data; +} + +/* + * tavl_free -- traverse avltree root, freeing the memory it is using. + * the dfree() is called to free the data portion of each node. The + * number of items actually freed is returned. + */ + +int +tavl_free( Avlnode *root, AVL_FREE dfree ) +{ + int nleft, nright; + + if ( root == 0 ) + return( 0 ); + + nleft = tavl_free( avl_lchild( root ), dfree ); + + nright = tavl_free( avl_rchild( root ), dfree ); + + if ( dfree ) + (*dfree)( root->avl_data ); + ber_memfree( root ); + + return( nleft + nright + 1 ); +} + +/* + * tavl_find -- search avltree root for a node with data data. the function + * cmp is used to compare things. it is called with data as its first arg + * and the current node data as its second. it should return 0 if they match, + * < 0 if arg1 is less than arg2 and > 0 if arg1 is greater than arg2. + */ + +/* + * tavl_find2 - returns Avlnode instead of data pointer. + * tavl_find3 - as above, but returns Avlnode even if no match is found. + * also set *ret = last comparison result, or -1 if root == NULL. + */ +Avlnode * +tavl_find3( Avlnode *root, const void *data, AVL_CMP fcmp, int *ret ) +{ + int cmp = -1, dir; + Avlnode *prev = root; + + while ( root != 0 && (cmp = (*fcmp)( data, root->avl_data )) != 0 ) { + prev = root; + dir = cmp > 0; + root = avl_child( root, dir ); + } + *ret = cmp; + return root ? root : prev; +} + +Avlnode * +tavl_find2( Avlnode *root, const void *data, AVL_CMP fcmp ) +{ + int cmp; + + while ( root != 0 && (cmp = (*fcmp)( data, root->avl_data )) != 0 ) { + cmp = cmp > 0; + root = avl_child( root, cmp ); + } + return root; +} + +void* +tavl_find( Avlnode *root, const void* data, AVL_CMP fcmp ) +{ + int cmp; + + while ( root != 0 && (cmp = (*fcmp)( data, root->avl_data )) != 0 ) { + cmp = cmp > 0; + root = avl_child( root, cmp ); + } + + return( root ? root->avl_data : 0 ); +} + +/* Return the leftmost or rightmost node in the tree */ +Avlnode * +tavl_end( Avlnode *root, int dir ) +{ + if ( root ) { + while ( root->avl_bits[dir] == AVL_CHILD ) + root = root->avl_link[dir]; + } + return root; +} + +/* Return the next node in the given direction */ +Avlnode * +tavl_next( Avlnode *root, int dir ) +{ + if ( root ) { + int c = root->avl_bits[dir]; + + root = root->avl_link[dir]; + if ( c == AVL_CHILD ) { + dir ^= 1; + while ( root->avl_bits[dir] == AVL_CHILD ) + root = root->avl_link[dir]; + } + } + return root; +} diff --git a/libraries/liblutil/testavl.c b/libraries/liblutil/testavl.c new file mode 100644 index 0000000..0473fbf --- /dev/null +++ b/libraries/liblutil/testavl.c @@ -0,0 +1,150 @@ +/* testavl.c - Test Tim Howes AVL code */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1993 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ +/* ACKNOWLEDGEMENTS: + * This work was originally developed by the University of Michigan + * (as part of U-MICH LDAP). + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> +#include <ac/string.h> + +#define AVL_INTERNAL +#define AVL_NONREENTRANT +#include "avl.h" + +static void ravl_print LDAP_P(( Avlnode *root, int depth )); +static void myprint LDAP_P(( Avlnode *root )); +static int avl_strcmp LDAP_P(( const void *s, const void *t )); + +int +main( int argc, char **argv ) +{ + Avlnode *tree = NULL; + char command[ 10 ]; + char name[ 80 ]; + char *p; + + printf( "> " ); + while ( fgets( command, sizeof( command ), stdin ) != NULL ) { + switch( *command ) { + case 'n': /* new tree */ + ( void ) avl_free( tree, free ); + tree = NULL; + break; + case 'p': /* print */ + ( void ) myprint( tree ); + break; + case 't': /* traverse with first, next */ +#ifdef AVL_NONREENTRANT + printf( "***\n" ); + for ( p = (char * ) avl_getfirst( tree ); + p != NULL; + p = (char *) avl_getnext()) + printf( "%s\n", p ); + printf( "***\n" ); +#else + printf( "*** reentrant interface not implemented ***" ); +#endif + break; + case 'f': /* find */ + printf( "data? " ); + if ( fgets( name, sizeof( name ), stdin ) == NULL ) + exit( EXIT_SUCCESS ); + name[ strlen( name ) - 1 ] = '\0'; + if ( (p = (char *) avl_find( tree, name, avl_strcmp )) + == NULL ) + printf( "Not found.\n\n" ); + else + printf( "%s\n\n", p ); + break; + case 'i': /* insert */ + printf( "data? " ); + if ( fgets( name, sizeof( name ), stdin ) == NULL ) + exit( EXIT_SUCCESS ); + name[ strlen( name ) - 1 ] = '\0'; + if ( avl_insert( &tree, strdup( name ), avl_strcmp, + avl_dup_error ) != 0 ) + printf( "\nNot inserted!\n" ); + break; + case 'd': /* delete */ + printf( "data? " ); + if ( fgets( name, sizeof( name ), stdin ) == NULL ) + exit( EXIT_SUCCESS ); + name[ strlen( name ) - 1 ] = '\0'; + if ( avl_delete( &tree, name, avl_strcmp ) == NULL ) + printf( "\nNot found!\n" ); + break; + case 'q': /* quit */ + exit( EXIT_SUCCESS ); + break; + case '\n': + break; + default: + printf("Commands: insert, delete, print, new, quit\n"); + } + + printf( "> " ); + } + + return( 0 ); +} + +static void ravl_print( Avlnode *root, int depth ) +{ + int i; + + if ( root == 0 ) + return; + + ravl_print( root->avl_right, depth+1 ); + + for ( i = 0; i < depth; i++ ) + printf( " " ); + printf( "%s %d\n", (char *) root->avl_data, root->avl_bf ); + + ravl_print( root->avl_left, depth+1 ); +} + +static void myprint( Avlnode *root ) +{ + printf( "********\n" ); + + if ( root == 0 ) + printf( "\tNULL\n" ); + else + ravl_print( root, 0 ); + + printf( "********\n" ); +} + +static int avl_strcmp( const void *s, const void *t ) +{ + return strcmp( s, t ); +} diff --git a/libraries/liblutil/testtavl.c b/libraries/liblutil/testtavl.c new file mode 100644 index 0000000..330ed36 --- /dev/null +++ b/libraries/liblutil/testtavl.c @@ -0,0 +1,158 @@ +/* testavl.c - Test Tim Howes AVL code */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright (c) 1993 Regents of the University of Michigan. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of Michigan at Ann Arbor. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ +/* ACKNOWLEDGEMENTS: + * This work was originally developed by the University of Michigan + * (as part of U-MICH LDAP). Additional contributors include + * Howard Chu + */ + +#include "portable.h" + +#include <stdio.h> + +#include <ac/stdlib.h> +#include <ac/string.h> + +#define AVL_INTERNAL +#include "avl.h" + +static void ravl_print LDAP_P(( Avlnode *root, int depth, int thread )); +static void myprint LDAP_P(( Avlnode *root )); +static int avl_strcmp LDAP_P(( const void *s, const void *t )); + +int +main( int argc, char **argv ) +{ + Avlnode *tree = NULL, *n; + char command[ 10 ]; + char name[ 80 ]; + char *p; + + printf( "> " ); + while ( fgets( command, sizeof( command ), stdin ) != NULL ) { + switch( *command ) { + case 'n': /* new tree */ + ( void ) tavl_free( tree, free ); + tree = NULL; + break; + case 'p': /* print */ + ( void ) myprint( tree ); + break; + case 't': /* traverse with first, next */ + printf( "***\n" ); + for ( n = tavl_end( tree, TAVL_DIR_LEFT ); + n != NULL; + n = tavl_next( n, TAVL_DIR_RIGHT )) + printf( "%s\n", n->avl_data ); + printf( "***\n" ); + break; + case 'f': /* find */ + printf( "data? " ); + if ( fgets( name, sizeof( name ), stdin ) == NULL ) + exit( EXIT_SUCCESS ); + name[ strlen( name ) - 1 ] = '\0'; + if ( (p = (char *) tavl_find( tree, name, avl_strcmp )) + == NULL ) + printf( "Not found.\n\n" ); + else + printf( "%s\n\n", p ); + break; + case 'i': /* insert */ + printf( "data? " ); + if ( fgets( name, sizeof( name ), stdin ) == NULL ) + exit( EXIT_SUCCESS ); + name[ strlen( name ) - 1 ] = '\0'; + if ( tavl_insert( &tree, strdup( name ), avl_strcmp, + avl_dup_error ) != 0 ) + printf( "\nNot inserted!\n" ); + break; + case 'd': /* delete */ + printf( "data? " ); + if ( fgets( name, sizeof( name ), stdin ) == NULL ) + exit( EXIT_SUCCESS ); + name[ strlen( name ) - 1 ] = '\0'; + if ( tavl_delete( &tree, name, avl_strcmp ) == NULL ) + printf( "\nNot found!\n" ); + break; + case 'q': /* quit */ + exit( EXIT_SUCCESS ); + break; + case '\n': + break; + default: + printf("Commands: insert, delete, print, new, quit\n"); + } + + printf( "> " ); + } + + return( 0 ); +} + +static const char bfc_array[] = "\\-/"; +static const char *bfcs = bfc_array+1; + +static void ravl_print( Avlnode *root, int depth, int thread ) +{ + int i; + + if ( root && !thread ) + ravl_print( root->avl_link[1], depth+1, root->avl_bits[1] == AVL_THREAD ); + + for ( i = 0; i < depth; i++ ) + printf( " " ); + if ( thread ) + printf( "~" ); + else if ( root ) + printf( "%c", bfcs[root->avl_bf] ); + else + printf( " " ); + if ( !root) { + printf( ".\n" ); + return; + } + printf( "%s\n", (char *) root->avl_data ); + + if ( !thread ) + ravl_print( root->avl_link[0], depth+1, root->avl_bits[0] == AVL_THREAD ); +} + +static void myprint( Avlnode *root ) +{ + printf( "********\n" ); + + if ( root == 0 ) + printf( "\tNULL\n" ); + else + ravl_print( root, 0, 0 ); + + printf( "********\n" ); +} + +static int avl_strcmp( const void *s, const void *t ) +{ + return strcmp( s, t ); +} diff --git a/libraries/liblutil/utils.c b/libraries/liblutil/utils.c new file mode 100644 index 0000000..fb3b9ea --- /dev/null +++ b/libraries/liblutil/utils.c @@ -0,0 +1,987 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 1998-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "portable.h" + +#include <limits.h> +#include <stdio.h> +#include <ac/stdlib.h> +#include <ac/stdarg.h> +#include <ac/string.h> +#include <ac/ctype.h> +#include <ac/unistd.h> +#include <ac/time.h> +#include <ac/errno.h> +#ifdef HAVE_IO_H +#include <io.h> +#endif +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif +#ifdef _WIN32 +#include <windows.h> +#endif + +#include "lutil.h" +#include "ldap_defaults.h" +#include "ldap_pvt.h" +#include "lber_pvt.h" + +#ifdef HAVE_EBCDIC +int _trans_argv = 1; +#endif + +#ifdef _WIN32 +/* Some Windows versions accept both forward and backslashes in + * directory paths, but we always use backslashes when generating + * and parsing... + */ +void lutil_slashpath( char *path ) +{ + char *c, *p; + + p = path; + while (( c=strchr( p, '/' ))) { + *c++ = '\\'; + p = c; + } +} +#endif + +char* lutil_progname( const char* name, int argc, char *argv[] ) +{ + char *progname; + + if(argc == 0) { + return (char *)name; + } + +#ifdef HAVE_EBCDIC + if (_trans_argv) { + int i; + for (i=0; i<argc; i++) __etoa(argv[i]); + _trans_argv = 0; + } +#endif + LUTIL_SLASHPATH( argv[0] ); + progname = strrchr ( argv[0], *LDAP_DIRSEP ); + progname = progname ? &progname[1] : argv[0]; +#ifdef _WIN32 + { + size_t len = strlen( progname ); + if ( len > 4 && strcasecmp( &progname[len - 4], ".exe" ) == 0 ) + progname[len - 4] = '\0'; + } +#endif + return progname; +} + +#if 0 +size_t lutil_gentime( char *s, size_t smax, const struct tm *tm ) +{ + size_t ret; +#ifdef HAVE_EBCDIC +/* We've been compiling in ASCII so far, but we want EBCDIC now since + * strftime only understands EBCDIC input. + */ +#pragma convlit(suspend) +#endif + ret = strftime( s, smax, "%Y%m%d%H%M%SZ", tm ); +#ifdef HAVE_EBCDIC +#pragma convlit(resume) + __etoa( s ); +#endif + return ret; +} +#endif + +size_t lutil_localtime( char *s, size_t smax, const struct tm *tm, long delta ) +{ + size_t ret; + char *p; + + if ( smax < 16 ) { /* YYYYmmddHHMMSSZ */ + return 0; + } + +#ifdef HAVE_EBCDIC +/* We've been compiling in ASCII so far, but we want EBCDIC now since + * strftime only understands EBCDIC input. + */ +#pragma convlit(suspend) +#endif + ret = strftime( s, smax, "%Y%m%d%H%M%SZ", tm ); +#ifdef HAVE_EBCDIC +#pragma convlit(resume) + __etoa( s ); +#endif + if ( delta == 0 || ret == 0 ) { + return ret; + } + + if ( smax < 20 ) { /* YYYYmmddHHMMSS+HHMM */ + return 0; + } + + p = s + 14; + + if ( delta < 0 ) { + p[ 0 ] = '-'; + delta = -delta; + } else { + p[ 0 ] = '+'; + } + p++; + + snprintf( p, smax - 15, "%02ld%02ld", delta / 3600, + ( delta % 3600 ) / 60 ); + + return ret + 4; +} + +int lutil_tm2time( struct lutil_tm *tm, struct lutil_timet *tt ) +{ + static int moffset[12] = { + 0, 31, 59, 90, 120, + 151, 181, 212, 243, + 273, 304, 334 }; + int sec; + + tt->tt_usec = tm->tm_usec; + + /* special case 0000/01/01+00:00:00 is returned as zero */ + if ( tm->tm_year == -1900 && tm->tm_mon == 0 && tm->tm_mday == 1 && + tm->tm_hour == 0 && tm->tm_min == 0 && tm->tm_sec == 0 ) { + tt->tt_sec = 0; + tt->tt_gsec = 0; + return 0; + } + + /* tm->tm_year is years since 1900 */ + /* calculate days from years since 1970 (epoch) */ + tt->tt_sec = tm->tm_year - 70; + tt->tt_sec *= 365L; + + /* count leap days in preceding years */ + tt->tt_sec += ((tm->tm_year -69) >> 2); + + /* calculate days from months */ + tt->tt_sec += moffset[tm->tm_mon]; + + /* add in this year's leap day, if any */ + if (((tm->tm_year & 3) == 0) && (tm->tm_mon > 1)) { + tt->tt_sec ++; + } + + /* add in days in this month */ + tt->tt_sec += (tm->tm_mday - 1); + + /* this function can handle a range of about 17408 years... */ + /* 86400 seconds in a day, divided by 128 = 675 */ + tt->tt_sec *= 675; + + /* move high 7 bits into tt_gsec */ + tt->tt_gsec = tt->tt_sec >> 25; + tt->tt_sec -= tt->tt_gsec << 25; + + /* get hours */ + sec = tm->tm_hour; + + /* convert to minutes */ + sec *= 60L; + sec += tm->tm_min; + + /* convert to seconds */ + sec *= 60L; + sec += tm->tm_sec; + + /* add remaining seconds */ + tt->tt_sec <<= 7; + tt->tt_sec += sec; + + /* return success */ + return 0; +} + +int lutil_parsetime( char *atm, struct lutil_tm *tm ) +{ + while (atm && tm) { + char *ptr = atm; + unsigned i, fracs; + + /* Is the stamp reasonably long? */ + for (i=0; isdigit((unsigned char) atm[i]); i++); + if (i < sizeof("00000101000000")-1) + break; + + /* + * parse the time into a struct tm + */ + /* 4 digit year to year - 1900 */ + tm->tm_year = *ptr++ - '0'; + tm->tm_year *= 10; tm->tm_year += *ptr++ - '0'; + tm->tm_year *= 10; tm->tm_year += *ptr++ - '0'; + tm->tm_year *= 10; tm->tm_year += *ptr++ - '0'; + tm->tm_year -= 1900; + /* month 01-12 to 0-11 */ + tm->tm_mon = *ptr++ - '0'; + tm->tm_mon *=10; tm->tm_mon += *ptr++ - '0'; + if (tm->tm_mon < 1 || tm->tm_mon > 12) break; + tm->tm_mon--; + + /* day of month 01-31 */ + tm->tm_mday = *ptr++ - '0'; + tm->tm_mday *=10; tm->tm_mday += *ptr++ - '0'; + if (tm->tm_mday < 1 || tm->tm_mday > 31) break; + + /* Hour 00-23 */ + tm->tm_hour = *ptr++ - '0'; + tm->tm_hour *=10; tm->tm_hour += *ptr++ - '0'; + if (tm->tm_hour < 0 || tm->tm_hour > 23) break; + + /* Minute 00-59 */ + tm->tm_min = *ptr++ - '0'; + tm->tm_min *=10; tm->tm_min += *ptr++ - '0'; + if (tm->tm_min < 0 || tm->tm_min > 59) break; + + /* Second 00-61 */ + tm->tm_sec = *ptr++ - '0'; + tm->tm_sec *=10; tm->tm_sec += *ptr++ - '0'; + if (tm->tm_sec < 0 || tm->tm_sec > 61) break; + + /* Fractions of seconds */ + if ( *ptr == '.' ) { + ptr++; + for (i = 0, fracs = 0; isdigit((unsigned char) *ptr); ) { + i*=10; i+= *ptr++ - '0'; + fracs++; + } + tm->tm_usec = i; + if (i) { + for (i = fracs; i<6; i++) + tm->tm_usec *= 10; + } + } + + /* Must be UTC */ + if (*ptr != 'Z') break; + + return 0; + } + return -1; +} + +/* strcopy is like strcpy except it returns a pointer to the trailing NUL of + * the result string. This allows fast construction of catenated strings + * without the overhead of strlen/strcat. + */ +char * +lutil_strcopy( + char *a, + const char *b +) +{ + if (!a || !b) + return a; + + while ((*a++ = *b++)) ; + return a-1; +} + +/* strncopy is like strcpy except it returns a pointer to the trailing NUL of + * the result string. This allows fast construction of catenated strings + * without the overhead of strlen/strcat. + */ +char * +lutil_strncopy( + char *a, + const char *b, + size_t n +) +{ + if (!a || !b || n == 0) + return a; + + while ((*a++ = *b++) && n-- > 0) ; + return a-1; +} + +/* memcopy is like memcpy except it returns a pointer to the byte past + * the end of the result buffer, set to NULL. This allows fast construction + * of catenated buffers. Provided for API consistency with lutil_str*copy(). + */ +char * +lutil_memcopy( + char *a, + const char *b, + size_t n +) +{ + AC_MEMCPY(a, b, n); + return a + n; +} + +#ifndef HAVE_MKSTEMP +int mkstemp( char * template ) +{ +#ifdef HAVE_MKTEMP + return open ( mktemp ( template ), O_RDWR|O_CREAT|O_EXCL, 0600 ); +#else + return -1; +#endif +} +#endif + +#ifdef _MSC_VER +/* Equivalent of MS CRT's _dosmaperr(). + * @param lastError[in] Result of GetLastError(). + */ +static errno_t win2errno(DWORD lastError) +{ + const struct { + DWORD windows_code; + errno_t errno_code; + } WIN2ERRNO_TABLE[] = { + { ERROR_SUCCESS, 0 }, + { ERROR_FILE_NOT_FOUND, ENOENT }, + { ERROR_PATH_NOT_FOUND, ENOENT }, + { ERROR_TOO_MANY_OPEN_FILES, EMFILE }, + { ERROR_ACCESS_DENIED, EACCES }, + { ERROR_INVALID_HANDLE, EBADF }, + { ERROR_NOT_ENOUGH_MEMORY, ENOMEM }, + { ERROR_LOCK_VIOLATION, EACCES }, + { ERROR_FILE_EXISTS, EEXIST }, + { ERROR_INVALID_PARAMETER, EINVAL }, + { ERROR_FILENAME_EXCED_RANGE, ENAMETOOLONG }, + }; + const unsigned int WIN2ERRNO_TABLE_SIZE = sizeof(WIN2ERRNO_TABLE) / +sizeof(WIN2ERRNO_TABLE[0]); + const errno_t DEFAULT_ERRNO_ERROR = -1; + unsigned int i; + + for (i = 0; i < WIN2ERRNO_TABLE_SIZE; ++i) { + if (WIN2ERRNO_TABLE[i].windows_code == lastError) { + return WIN2ERRNO_TABLE[i].errno_code; + } + } + return DEFAULT_ERRNO_ERROR; +} + +struct dirent { + char *d_name; +}; +typedef struct DIR { + HANDLE dir; + struct dirent data; + int first; + char buf[MAX_PATH+1]; +} DIR; +DIR *opendir( char *path ) +{ + char tmp[32768]; + int len = strlen(path); + DIR *d; + HANDLE h; + WIN32_FIND_DATA data; + + if (len+3 >= sizeof(tmp)) { + errno = ENAMETOOLONG; + return NULL; + } + + strcpy(tmp, path); + tmp[len++] = '\\'; + tmp[len++] = '*'; + tmp[len] = '\0'; + + h = FindFirstFile( tmp, &data ); + + if ( h == INVALID_HANDLE_VALUE ) { + errno = win2errno( GetLastError()); + return NULL; + } + + d = ber_memalloc( sizeof(DIR) ); + if ( !d ) + return NULL; + d->dir = h; + d->data.d_name = d->buf; + d->first = 1; + strcpy(d->data.d_name, data.cFileName); + return d; +} +struct dirent *readdir(DIR *dir) +{ + WIN32_FIND_DATA data; + + if (dir->first) { + dir->first = 0; + } else { + if (!FindNextFile(dir->dir, &data)) + return NULL; + strcpy(dir->data.d_name, data.cFileName); + } + return &dir->data; +} +int closedir(DIR *dir) +{ + (void) FindClose(dir->dir); + ber_memfree(dir); + return 0; +} +#endif + +/* + * Memory Reverse Search + */ +void * +(lutil_memrchr)(const void *b, int c, size_t n) +{ + if (n != 0) { + const unsigned char *s, *bb = b, cc = c; + + for ( s = bb + n; s > bb; ) { + if ( *--s == cc ) { + return (void *) s; + } + } + } + + return NULL; +} + +int +lutil_atoix( int *v, const char *s, int x ) +{ + char *next; + long i; + + assert( s != NULL ); + assert( v != NULL ); + + i = strtol( s, &next, x ); + if ( next == s || next[ 0 ] != '\0' ) { + return -1; + } + + if ( (long)(int)i != i ) { + return 1; + } + + *v = (int)i; + + return 0; +} + +int +lutil_atoux( unsigned *v, const char *s, int x ) +{ + char *next; + unsigned long u; + + assert( s != NULL ); + assert( v != NULL ); + + /* strtoul() has an odd interface */ + if ( s[ 0 ] == '-' ) { + return -1; + } + + u = strtoul( s, &next, x ); + if ( next == s || next[ 0 ] != '\0' ) { + return -1; + } + + if ( (unsigned long)(unsigned)u != u ) { + return 1; + } + + *v = u; + + return 0; +} + +int +lutil_atolx( long *v, const char *s, int x ) +{ + char *next; + long l; + int save_errno; + + assert( s != NULL ); + assert( v != NULL ); + + if ( isspace( s[ 0 ] ) ) { + return -1; + } + + errno = 0; + l = strtol( s, &next, x ); + save_errno = errno; + if ( next == s || next[ 0 ] != '\0' ) { + return -1; + } + + if ( ( l == LONG_MIN || l == LONG_MAX ) && save_errno != 0 ) { + return -1; + } + + *v = l; + + return 0; +} + +int +lutil_atoulx( unsigned long *v, const char *s, int x ) +{ + char *next; + unsigned long ul; + int save_errno; + + assert( s != NULL ); + assert( v != NULL ); + + /* strtoul() has an odd interface */ + if ( s[ 0 ] == '-' || isspace( s[ 0 ] ) ) { + return -1; + } + + errno = 0; + ul = strtoul( s, &next, x ); + save_errno = errno; + if ( next == s || next[ 0 ] != '\0' ) { + return -1; + } + + if ( ( ul == 0 || ul == ULONG_MAX ) && save_errno != 0 ) { + return -1; + } + + *v = ul; + + return 0; +} + +#ifdef HAVE_LONG_LONG +#if defined(HAVE_STRTOLL) || defined(HAVE_STRTOQ) +int +lutil_atollx( long long *v, const char *s, int x ) +{ + char *next; + long long ll; + int save_errno; + + assert( s != NULL ); + assert( v != NULL ); + + if ( isspace( s[ 0 ] ) ) { + return -1; + } + + errno = 0; +#ifdef HAVE_STRTOLL + ll = strtoll( s, &next, x ); +#else /* HAVE_STRTOQ */ + ll = (unsigned long long)strtoq( s, &next, x ); +#endif /* HAVE_STRTOQ */ + save_errno = errno; + if ( next == s || next[ 0 ] != '\0' ) { + return -1; + } + + /* LLONG_MIN, LLONG_MAX are C99 only */ +#if defined (LLONG_MIN) && defined(LLONG_MAX) + if ( ( ll == LLONG_MIN || ll == LLONG_MAX ) && save_errno != 0 ) { + return -1; + } +#endif /* LLONG_MIN && LLONG_MAX */ + + *v = ll; + + return 0; +} +#endif /* HAVE_STRTOLL || HAVE_STRTOQ */ + +#if defined(HAVE_STRTOULL) || defined(HAVE_STRTOUQ) +int +lutil_atoullx( unsigned long long *v, const char *s, int x ) +{ + char *next; + unsigned long long ull; + int save_errno; + + assert( s != NULL ); + assert( v != NULL ); + + /* strtoull() has an odd interface */ + if ( s[ 0 ] == '-' || isspace( s[ 0 ] ) ) { + return -1; + } + + errno = 0; +#ifdef HAVE_STRTOULL + ull = strtoull( s, &next, x ); +#else /* HAVE_STRTOUQ */ + ull = (unsigned long long)strtouq( s, &next, x ); +#endif /* HAVE_STRTOUQ */ + save_errno = errno; + if ( next == s || next[ 0 ] != '\0' ) { + return -1; + } + + /* ULLONG_MAX is C99 only */ +#if defined(ULLONG_MAX) + if ( ( ull == 0 || ull == ULLONG_MAX ) && save_errno != 0 ) { + return -1; + } +#endif /* ULLONG_MAX */ + + *v = ull; + + return 0; +} +#endif /* HAVE_STRTOULL || HAVE_STRTOUQ */ +#endif /* HAVE_LONG_LONG */ + +/* Multiply an integer by 100000000 and add new */ +typedef struct lutil_int_decnum { + unsigned char *buf; + int bufsiz; + int beg; + int len; +} lutil_int_decnum; + +#define FACTOR1 (100000000&0xffff) +#define FACTOR2 (100000000>>16) + +static void +scale( int new, lutil_int_decnum *prev, unsigned char *tmp ) +{ + int i, j; + unsigned char *in = prev->buf+prev->beg; + unsigned int part; + unsigned char *out = tmp + prev->bufsiz - prev->len; + + memset( tmp, 0, prev->bufsiz ); + if ( prev->len ) { + for ( i = prev->len-1; i>=0; i-- ) { + part = in[i] * FACTOR1; + for ( j = i; part; j-- ) { + part += out[j]; + out[j] = part & 0xff; + part >>= 8; + } + part = in[i] * FACTOR2; + for ( j = i-2; part; j-- ) { + part += out[j]; + out[j] = part & 0xff; + part >>= 8; + } + } + j++; + prev->beg += j; + prev->len -= j; + } + + out = tmp + prev->bufsiz; + i = 0; + do { + i--; + new += out[i]; + out[i] = new & 0xff; + new >>= 8; + } while ( new ); + i = -i; + if ( prev->len < i ) { + prev->beg = prev->bufsiz - i; + prev->len = i; + } + AC_MEMCPY( prev->buf+prev->beg, tmp+prev->beg, prev->len ); +} + +/* Convert unlimited length decimal or hex string to binary. + * Output buffer must be provided, bv_len must indicate buffer size + * Hex input can be "0x1234" or "'1234'H" + * + * Note: High bit of binary form is always the sign bit. If the number + * is supposed to be positive but has the high bit set, a zero byte + * is prepended. It is assumed that this has already been handled on + * any hex input. + */ +int +lutil_str2bin( struct berval *in, struct berval *out, void *ctx ) +{ + char *pin, *pout; + char *end; + int i, chunk, len, rc = 0, hex = 0; + if ( !out || !out->bv_val || out->bv_len < in->bv_len ) + return -1; + + pout = out->bv_val; + /* Leading "0x" for hex input */ + if ( in->bv_len > 2 && in->bv_val[0] == '0' && + ( in->bv_val[1] == 'x' || in->bv_val[1] == 'X' ) ) + { + len = in->bv_len - 2; + pin = in->bv_val + 2; + hex = 1; + } else if ( in->bv_len > 3 && in->bv_val[0] == '\'' && + in->bv_val[in->bv_len-2] == '\'' && + in->bv_val[in->bv_len-1] == 'H' ) + { + len = in->bv_len - 3; + pin = in->bv_val + 1; + hex = 1; + } + if ( hex ) { +#define HEXMAX (2 * sizeof(long)) + unsigned long l; + char tbuf[HEXMAX+1]; + + /* Convert a longword at a time, but handle leading + * odd bytes first + */ + chunk = len % HEXMAX; + if ( !chunk ) + chunk = HEXMAX; + + while ( len ) { + int ochunk; + memcpy( tbuf, pin, chunk ); + tbuf[chunk] = '\0'; + errno = 0; + l = strtoul( tbuf, &end, 16 ); + if ( errno ) + return -1; + ochunk = (chunk + 1)/2; + for ( i = ochunk - 1; i >= 0; i-- ) { + pout[i] = l & 0xff; + l >>= 8; + } + pin += chunk; + pout += ochunk; + len -= chunk; + chunk = HEXMAX; + } + out->bv_len = pout - out->bv_val; + } else { + /* Decimal */ +#define DECMAX 8 /* 8 digits at a time */ + char tmpbuf[64], *tmp; + lutil_int_decnum num; + int neg = 0; + long l; + char tbuf[DECMAX+1]; + + len = in->bv_len; + pin = in->bv_val; + num.buf = (unsigned char *)out->bv_val; + num.bufsiz = out->bv_len; + num.beg = num.bufsiz-1; + num.len = 0; + if ( pin[0] == '-' ) { + neg = 0xff; + len--; + pin++; + } + + /* tmp must be at least as large as outbuf */ + if ( out->bv_len > sizeof(tmpbuf)) { + tmp = ber_memalloc_x( out->bv_len, ctx ); + } else { + tmp = tmpbuf; + } + chunk = len & (DECMAX-1); + if ( !chunk ) + chunk = DECMAX; + + while ( len ) { + memcpy( tbuf, pin, chunk ); + tbuf[chunk] = '\0'; + errno = 0; + l = strtol( tbuf, &end, 10 ); + if ( errno ) { + rc = -1; + goto decfail; + } + scale( l, &num, (unsigned char *)tmp ); + pin += chunk; + len -= chunk; + chunk = DECMAX; + } + /* Negate the result */ + if ( neg ) { + unsigned char *ptr; + + ptr = num.buf+num.beg; + + /* flip all bits */ + for ( i=0; i<num.len; i++ ) + ptr[i] ^= 0xff; + + /* add 1, with carry - overflow handled below */ + while ( i-- && ! (ptr[i] = (ptr[i] + 1) & 0xff )) ; + } + /* Prepend sign byte if wrong sign bit */ + if (( num.buf[num.beg] ^ neg ) & 0x80 ) { + num.beg--; + num.len++; + num.buf[num.beg] = neg; + } + if ( num.beg ) + AC_MEMCPY( num.buf, num.buf+num.beg, num.len ); + out->bv_len = num.len; +decfail: + if ( tmp != tmpbuf ) { + ber_memfree_x( tmp, ctx ); + } + } + return rc; +} + +static char time_unit[] = "dhms"; + +/* Used to parse and unparse time intervals, not timestamps */ +int +lutil_parse_time( + const char *in, + unsigned long *tp ) +{ + unsigned long t = 0; + char *s, + *next; + int sofar = -1, + scale[] = { 86400, 3600, 60, 1 }; + + *tp = 0; + + for ( s = (char *)in; s[ 0 ] != '\0'; ) { + unsigned long u; + char *what; + + /* strtoul() has an odd interface */ + if ( s[ 0 ] == '-' ) { + return -1; + } + + u = strtoul( s, &next, 10 ); + if ( next == s ) { + return -1; + } + + if ( next[ 0 ] == '\0' ) { + /* assume seconds */ + t += u; + break; + } + + what = strchr( time_unit, next[ 0 ] ); + if ( what == NULL ) { + return -1; + } + + if ( what - time_unit <= sofar ) { + return -1; + } + + sofar = what - time_unit; + t += u * scale[ sofar ]; + + s = &next[ 1 ]; + } + + *tp = t; + return 0; +} + +int +lutil_unparse_time( + char *buf, + size_t buflen, + unsigned long t ) +{ + int len, i; + unsigned long v[ 4 ]; + char *ptr = buf; + + v[ 0 ] = t/86400; + v[ 1 ] = (t%86400)/3600; + v[ 2 ] = (t%3600)/60; + v[ 3 ] = t%60; + + for ( i = 0; i < 4; i++ ) { + if ( v[i] > 0 || ( i == 3 && ptr == buf ) ) { + len = snprintf( ptr, buflen, "%lu%c", v[ i ], time_unit[ i ] ); + if ( len < 0 || (unsigned)len >= buflen ) { + return -1; + } + buflen -= len; + ptr += len; + } + } + + return 0; +} + +/* + * formatted print to string + * + * - if return code < 0, the error code returned by vsnprintf(3) is returned + * + * - if return code > 0, the buffer was not long enough; + * - if next is not NULL, *next will be set to buf + bufsize - 1 + * - if len is not NULL, *len will contain the required buffer length + * + * - if return code == 0, the buffer was long enough; + * - if next is not NULL, *next will point to the end of the string printed so far + * - if len is not NULL, *len will contain the length of the string printed so far + */ +int +lutil_snprintf( char *buf, ber_len_t bufsize, char **next, ber_len_t *len, LDAP_CONST char *fmt, ... ) +{ + va_list ap; + int ret; + + assert( buf != NULL ); + assert( bufsize > 0 ); + assert( fmt != NULL ); + + va_start( ap, fmt ); + ret = vsnprintf( buf, bufsize, fmt, ap ); + va_end( ap ); + + if ( ret < 0 ) { + return ret; + } + + if ( len ) { + *len = ret; + } + + if ( (unsigned) ret >= bufsize ) { + if ( next ) { + *next = &buf[ bufsize - 1 ]; + } + + return 1; + } + + if ( next ) { + *next = &buf[ ret ]; + } + + return 0; +} + diff --git a/libraries/liblutil/uuid.c b/libraries/liblutil/uuid.c new file mode 100644 index 0000000..c98174b --- /dev/null +++ b/libraries/liblutil/uuid.c @@ -0,0 +1,460 @@ +/* uuid.c -- Universally Unique Identifier routines */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2000-2021 The OpenLDAP Foundation. + * Portions Copyright 2000-2003 Kurt D. Zeilenga. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* Portions Copyright 2000, John E. Schimmel, All rights reserved. + * This software is not subject to any license of Mirapoint, Inc. + * + * This is free software; you can redistribute and use it + * under the same terms as OpenLDAP itself. + */ +/* This work was initially developed by John E. Schimmel and adapted + * for inclusion in OpenLDAP Software by Kurt D. Zeilenga. + */ + +/* + * Sorry this file is so scary, but it needs to run on a wide range of + * platforms. The only exported routine is lutil_uuidstr() which is all + * that LDAP cares about. It generates a new uuid and returns it in + * in string form. + */ +#include "portable.h" + +#include <limits.h> +#include <stdio.h> +#include <sys/types.h> + +#include <ac/stdlib.h> +#include <ac/string.h> /* get memcmp() */ + +#ifdef HAVE_UUID_TO_STR +# include <sys/uuid.h> +#elif defined( HAVE_UUID_GENERATE ) +# include <uuid/uuid.h> +#elif defined( _WIN32 ) +# include <rpc.h> +#else +# include <ac/socket.h> +# include <ac/time.h> +# ifdef HAVE_SYS_SYSCTL_H +# include <net/if.h> +# include <sys/sysctl.h> +# include <net/route.h> +# endif +#endif + +#include <lutil.h> + +/* not needed for Windows */ +#if !defined(HAVE_UUID_TO_STR) && !defined(HAVE_UUID_GENERATE) && !defined(_WIN32) +static unsigned char * +lutil_eaddr( void ) +{ + static unsigned char zero[6]; + static unsigned char eaddr[6]; + +#ifdef HAVE_SYS_SYSCTL_H + size_t needed; + int mib[6]; + char *buf, *next, *lim; + struct if_msghdr *ifm; + struct sockaddr_dl *sdl; + + if (memcmp(eaddr, zero, sizeof(eaddr))) { + return eaddr; + } + + mib[0] = CTL_NET; + mib[1] = PF_ROUTE; + mib[3] = 0; + mib[3] = 0; + mib[4] = NET_RT_IFLIST; + mib[5] = 0; + + if (sysctl(mib, sizeof(mib), NULL, &needed, NULL, 0) < 0) { + return NULL; + } + + buf = malloc(needed); + if( buf == NULL ) return NULL; + + if (sysctl(mib, sizeof(mib), buf, &needed, NULL, 0) < 0) { + free(buf); + return NULL; + } + + lim = buf + needed; + for (next = buf; next < lim; next += ifm->ifm_msglen) { + ifm = (struct if_msghdr *)next; + sdl = (struct sockaddr_dl *)(ifm + 1); + + if ( sdl->sdl_family != AF_LINK || sdl->sdl_alen == 6 ) { + AC_MEMCPY(eaddr, + (unsigned char *)sdl->sdl_data + sdl->sdl_nlen, + sizeof(eaddr)); + free(buf); + return eaddr; + } + } + + free(buf); + return NULL; + +#elif defined( SIOCGIFADDR ) && defined( AFLINK ) + char buf[sizeof(struct ifreq) * 32]; + struct ifconf ifc; + struct ifreq *ifr; + struct sockaddr *sa; + struct sockaddr_dl *sdl; + unsigned char *p; + int s, i; + + if (memcmp(eaddr, zero, sizeof(eaddr))) { + return eaddr; + } + + s = socket( AF_INET, SOCK_DGRAM, 0 ); + if ( s < 0 ) { + return NULL; + } + + ifc.ifc_len = sizeof( buf ); + ifc.ifc_buf = buf; + memset( buf, 0, sizeof( buf ) ); + + i = ioctl( s, SIOCGIFCONF, (char *)&ifc ); + close( s ); + + if( i < 0 ) { + return NULL; + } + + for ( i = 0; i < ifc.ifc_len; ) { + ifr = (struct ifreq *)&ifc.ifc_buf[i]; + sa = &ifr->ifr_addr; + + if ( sa->sa_len > sizeof( ifr->ifr_addr ) ) { + i += sizeof( ifr->ifr_name ) + sa->sa_len; + } else { + i += sizeof( *ifr ); + } + + if ( sa->sa_family != AF_LINK ) { + continue; + } + + sdl = (struct sockaddr_dl *)sa; + + if ( sdl->sdl_alen == 6 ) { + AC_MEMCPY(eaddr, + (unsigned char *)sdl->sdl_data + sdl->sdl_nlen, + sizeof(eaddr)); + return eaddr; + } + } + + return NULL; + +#else + if (memcmp(eaddr, zero, sizeof(eaddr)) == 0) { + /* XXX - who knows? */ + lutil_entropy( eaddr, sizeof(eaddr) ); + eaddr[0] |= 0x01; /* turn it into a multicast address */ + } + + return eaddr; +#endif +} + +#if (ULONG_MAX >> 31 >> 31) > 1 || defined HAVE_LONG_LONG + +#if (ULONG_MAX >> 31 >> 31) > 1 + typedef unsigned long UI64; + /* 100 usec intervals from 10/10/1582 to 1/1/1970 */ +# define UUID_TPLUS 0x01B21DD2138140ul +#else + typedef unsigned long long UI64; +# define UUID_TPLUS 0x01B21DD2138140ull +#endif + +#define high32(i) ((unsigned long) ((i) >> 32)) +#define low32(i) ((unsigned long) (i) & 0xFFFFFFFFul) +#define set_add64(res, i) ((res) += (i)) +#define set_add64l(res, i) ((res) += (i)) +#define mul64ll(i1, i2) ((UI64) (i1) * (i2)) + +#else /* ! (ULONG_MAX >= 64 bits || HAVE_LONG_LONG) */ + +typedef struct { + unsigned long high, low; +} UI64; + +static const UI64 UUID_TPLUS = { 0x01B21Dul, 0xD2138140ul }; + +#define high32(i) ((i).high) +#define low32(i) ((i).low) + +/* res += ui64 */ +#define set_add64(res, ui64) \ +{ \ + res.high += ui64.high; \ + res.low = (res.low + ui64.low) & 0xFFFFFFFFul; \ + if (res.low < ui64.low) res.high++; \ +} + +/* res += ul32 */ +#define set_add64l(res, ul32) \ +{ \ + res.low = (res.low + ul32) & 0xFFFFFFFFul; \ + if (res.low < ul32) res.high++; \ +} + +/* compute i1 * i2 */ +static UI64 +mul64ll(unsigned long i1, unsigned long i2) +{ + const unsigned int high1 = (i1 >> 16), low1 = (i1 & 0xffff); + const unsigned int high2 = (i2 >> 16), low2 = (i2 & 0xffff); + + UI64 res; + unsigned long tmp; + + res.high = (unsigned long) high1 * high2; + res.low = (unsigned long) low1 * low2; + + tmp = (unsigned long) low1 * high2; + res.high += (tmp >> 16); + tmp = (tmp << 16) & 0xFFFFFFFFul; + res.low = (res.low + tmp) & 0xFFFFFFFFul; + if (res.low < tmp) + res.high++; + + tmp = (unsigned long) low2 * high1; + res.high += (tmp >> 16); + tmp = (tmp << 16) & 0xFFFFFFFFul; + res.low = (res.low + tmp) & 0xFFFFFFFFul; + if (res.low < tmp) + res.high++; + + return res; +} + +#endif /* ULONG_MAX >= 64 bits || HAVE_LONG_LONG */ + +#endif /* !HAVE_UUID_TO_STR && !HAVE_UUID_GENERATE && !_WIN32 */ + +/* +** All we really care about is an ISO UUID string. The format of a UUID is: +** field octet note +** time_low 0-3 low field of the timestamp +** time_mid 4-5 middle field of timestamp +** time_hi_and_version 6-7 high field of timestamp and +** version number +** clock_seq_hi_and_resv 8 high field of clock sequence +** and variant +** clock_seq_low 9 low field of clock sequence +** node 10-15 spacially unique identifier +** +** We use DCE version one, and the DCE variant. Our unique identifier is +** the first ethernet address on the system. +*/ +size_t +lutil_uuidstr( char *buf, size_t len ) +{ +#ifdef HAVE_UUID_TO_STR + uuid_t uu = {0}; + unsigned rc; + char *s; + size_t l; + + uuid_create( &uu, &rc ); + if ( rc != uuid_s_ok ) { + return 0; + } + + uuid_to_str( &uu, &s, &rc ); + if ( rc != uuid_s_ok ) { + return 0; + } + + l = strlen( s ); + if ( l >= len ) { + free( s ); + return 0; + } + + strncpy( buf, s, len ); + free( s ); + + return l; + +#elif defined( HAVE_UUID_GENERATE ) + uuid_t uu; + + uuid_generate( uu ); + uuid_unparse_lower( uu, buf ); + return strlen( buf ); + +#elif defined( _WIN32 ) + UUID uuid; + unsigned char *uuidstr; + size_t uuidlen; + + if( UuidCreate( &uuid ) != RPC_S_OK ) { + return 0; + } + + if( UuidToString( &uuid, &uuidstr ) != RPC_S_OK ) { + return 0; + } + + uuidlen = strlen( uuidstr ); + if( uuidlen >= len ) { + return 0; + } + + strncpy( buf, uuidstr, len ); + RpcStringFree( &uuidstr ); + + return uuidlen; + +#else + struct timeval tv; + UI64 tl; + unsigned char *nl; + unsigned short t2, t3, s1; + unsigned long t1, tl_high; + unsigned int rc; + + /* + * Theoretically we should delay if seq wraps within 100usec but for now + * systems are not fast enough to worry about it. + */ + static int inited = 0; + static unsigned short seq; + + if (!inited) { + lutil_entropy( (unsigned char *) &seq, sizeof(seq) ); + inited++; + } + +#ifdef HAVE_GETTIMEOFDAY + gettimeofday( &tv, 0 ); +#else + time( &tv.tv_sec ); + tv.tv_usec = 0; +#endif + + tl = mul64ll(tv.tv_sec, 10000000UL); + set_add64l(tl, tv.tv_usec * 10UL); + set_add64(tl, UUID_TPLUS); + + nl = lutil_eaddr(); + + t1 = low32(tl); /* time_low */ + tl_high = high32(tl); + t2 = tl_high & 0xffff; /* time_mid */ + t3 = ((tl_high >> 16) & 0x0fff) | 0x1000; /* time_hi_and_version */ + s1 = ( ++seq & 0x1fff ) | 0x8000; /* clock_seq_and_reserved */ + + rc = snprintf( buf, len, + "%08lx-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x", + t1, (unsigned) t2, (unsigned) t3, (unsigned) s1, + (unsigned) nl[0], (unsigned) nl[1], + (unsigned) nl[2], (unsigned) nl[3], + (unsigned) nl[4], (unsigned) nl[5] ); + + return rc < len ? rc : 0; +#endif +} + +int +lutil_uuidstr_from_normalized( + char *uuid, + size_t uuidlen, + char *buf, + size_t buflen ) +{ + unsigned char nibble; + int i, d = 0; + + assert( uuid != NULL ); + assert( buf != NULL ); + + if ( uuidlen != 16 ) return -1; + if ( buflen < 36 ) return -1; + + for ( i = 0; i < 16; i++ ) { + if ( i == 4 || i == 6 || i == 8 || i == 10 ) { + buf[(i<<1)+d] = '-'; + d += 1; + } + + nibble = (uuid[i] >> 4) & 0xF; + if ( nibble < 10 ) { + buf[(i<<1)+d] = nibble + '0'; + } else { + buf[(i<<1)+d] = nibble - 10 + 'a'; + } + + nibble = (uuid[i]) & 0xF; + if ( nibble < 10 ) { + buf[(i<<1)+d+1] = nibble + '0'; + } else { + buf[(i<<1)+d+1] = nibble - 10 + 'a'; + } + } + + if ( buflen > 36 ) buf[36] = '\0'; + return 36; +} + +#ifdef TEST +int +main(int argc, char **argv) +{ + char buf1[8], buf2[64]; + +#ifndef HAVE_UUID_TO_STR + unsigned char *p = lutil_eaddr(); + + if( p ) { + printf( "Ethernet Address: %02x:%02x:%02x:%02x:%02x:%02x\n", + (unsigned) p[0], (unsigned) p[1], (unsigned) p[2], + (unsigned) p[3], (unsigned) p[4], (unsigned) p[5]); + } +#endif + + if ( lutil_uuidstr( buf1, sizeof( buf1 ) ) ) { + printf( "UUID: %s\n", buf1 ); + } else { + fprintf( stderr, "too short: %ld\n", (long) sizeof( buf1 ) ); + } + + if ( lutil_uuidstr( buf2, sizeof( buf2 ) ) ) { + printf( "UUID: %s\n", buf2 ); + } else { + fprintf( stderr, "too short: %ld\n", (long) sizeof( buf2 ) ); + } + + if ( lutil_uuidstr( buf2, sizeof( buf2 ) ) ) { + printf( "UUID: %s\n", buf2 ); + } else { + fprintf( stderr, "too short: %ld\n", (long) sizeof( buf2 ) ); + } + + return 0; +} +#endif diff --git a/libraries/librewrite/Copyright b/libraries/librewrite/Copyright new file mode 100644 index 0000000..64a25f5 --- /dev/null +++ b/libraries/librewrite/Copyright @@ -0,0 +1,23 @@ +/****************************************************************************** + * + * Copyright (C) 2000 Pierangelo Masarati, <ando@sys-net.it> + * All rights reserved. + * + * Permission is granted to anyone to use this software for any purpose + * on any computer system, and to alter it and redistribute it, subject + * to the following restrictions: + * + * 1. The author is not responsible for the consequences of use of this + * software, no matter how awful, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either by + * explicit claim or by omission. Since few users ever read sources, + * credits should appear in the documentation. + * + * 3. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software. Since few users + * ever read sources, credits should appear in the documentation. + * + * 4. This notice may not be removed or altered. + * + ******************************************************************************/ diff --git a/libraries/librewrite/Makefile.in b/libraries/librewrite/Makefile.in new file mode 100644 index 0000000..dba291c --- /dev/null +++ b/libraries/librewrite/Makefile.in @@ -0,0 +1,37 @@ +# LIBREWRITE +# $OpenLDAP$ +## This work is part of OpenLDAP Software <http://www.openldap.org/>. +## +## Copyright 1998-2021 The OpenLDAP Foundation. +## All rights reserved. +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted only as authorized by the OpenLDAP +## Public License. +## +## A copy of this license is available in the file LICENSE in the +## top-level directory of the distribution or, alternatively, at +## <http://www.OpenLDAP.org/license.html>. +## +## Copyright 2000-2001 Pierangelo Masarati <ando@sys-net.it> +## + +SRCS = config.c context.c info.c ldapmap.c map.c params.c rule.c \ + session.c subst.c var.c xmap.c \ + parse.c rewrite.c +XSRCS = version.c +OBJS = config.o context.o info.o ldapmap.o map.o params.o rule.o \ + session.o subst.o var.o xmap.o + +LDAP_INCDIR= ../../include +LDAP_LIBDIR= ../../libraries + +LIBRARY = librewrite.a +PROGRAMS = rewrite +XLIBS = $(LIBRARY) $(LDAP_LIBLUTIL_A) \ + $(LDAP_LIBLDAP_R_LA) $(LDAP_LIBLBER_LA) +XXLIBS = $(SECURITY_LIBS) $(LUTIL_LIBS) +XXXLIBS = $(LTHREAD_LIBS) + +rewrite: $(XLIBS) rewrite.o parse.o + $(LTLINK) -o $@ rewrite.o parse.o $(LIBS) diff --git a/libraries/librewrite/RATIONALE b/libraries/librewrite/RATIONALE new file mode 100644 index 0000000..c8fa386 --- /dev/null +++ b/libraries/librewrite/RATIONALE @@ -0,0 +1,2 @@ +The workings of the rewrite library are described in the +REWRITING section of the slapd-meta(5) manual page. diff --git a/libraries/librewrite/config.c b/libraries/librewrite/config.c new file mode 100644 index 0000000..bf3d85d --- /dev/null +++ b/libraries/librewrite/config.c @@ -0,0 +1,441 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2000-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENT: + * This work was initially developed by Pierangelo Masarati for + * inclusion in OpenLDAP Software. + */ + +#include <portable.h> + +#include "rewrite-int.h" +#include "rewrite-map.h" + +/* + * Parses a plugin map + */ +static int +rewrite_parse_builtin_map( + struct rewrite_info *info, + const char *fname, + int lineno, + int argc, + char **argv +); + +/* + * Parses a config line and takes actions to fit content in rewrite structure; + * lines handled are of the form: + * + * rewriteEngine {on|off} + * rewriteMaxPasses numPasses [numPassesPerRule] + * rewriteContext contextName [alias aliasedContextName] + * rewriteRule pattern substPattern [ruleFlags] + * rewriteMap mapType mapName [mapArgs] + * rewriteParam paramName paramValue + */ +int +rewrite_parse( + struct rewrite_info *info, + const char *fname, + int lineno, + int argc, + char **argv +) +{ + int rc = -1; + + assert( info != NULL ); + assert( fname != NULL ); + assert( argv != NULL ); + assert( argc > 0 ); + + /* + * Switch on the rewrite engine + */ + if ( strcasecmp( argv[ 0 ], "rewriteEngine" ) == 0 ) { + if ( argc < 2 ) { + Debug( LDAP_DEBUG_ANY, + "[%s:%d] rewriteEngine needs 'state'\n%s", + fname, lineno, "" ); + return -1; + + } else if ( argc > 2 ) { + Debug( LDAP_DEBUG_ANY, + "[%s:%d] extra fields in rewriteEngine" + " will be discarded\n%s", + fname, lineno, "" ); + } + + if ( strcasecmp( argv[ 1 ], "on" ) == 0 ) { + info->li_state = REWRITE_ON; + + } else if ( strcasecmp( argv[ 1 ], "off" ) == 0 ) { + info->li_state = REWRITE_OFF; + + } else { + Debug( LDAP_DEBUG_ANY, + "[%s:%d] unknown 'state' in rewriteEngine;" + " assuming 'on'\n%s", + fname, lineno, "" ); + info->li_state = REWRITE_ON; + } + rc = REWRITE_SUCCESS; + + /* + * Alter max passes + */ + } else if ( strcasecmp( argv[ 0 ], "rewriteMaxPasses" ) == 0 ) { + if ( argc < 2 ) { + Debug( LDAP_DEBUG_ANY, + "[%s:%d] rewriteMaxPasses needs 'value'\n%s", + fname, lineno, "" ); + return -1; + } + + if ( lutil_atoi( &info->li_max_passes, argv[ 1 ] ) != 0 ) { + Debug( LDAP_DEBUG_ANY, + "[%s:%d] unable to parse rewriteMaxPasses=\"%s\"\n", + fname, lineno, argv[ 1 ] ); + return -1; + } + + if ( info->li_max_passes <= 0 ) { + Debug( LDAP_DEBUG_ANY, + "[%s:%d] negative or null rewriteMaxPasses\n", + fname, lineno, 0 ); + return -1; + } + + if ( argc > 2 ) { + if ( lutil_atoi( &info->li_max_passes_per_rule, argv[ 2 ] ) != 0 ) { + Debug( LDAP_DEBUG_ANY, + "[%s:%d] unable to parse rewriteMaxPassesPerRule=\"%s\"\n", + fname, lineno, argv[ 2 ] ); + return -1; + } + + if ( info->li_max_passes_per_rule <= 0 ) { + Debug( LDAP_DEBUG_ANY, + "[%s:%d] negative or null rewriteMaxPassesPerRule\n", + fname, lineno, 0 ); + return -1; + } + + } else { + info->li_max_passes_per_rule = info->li_max_passes; + } + rc = REWRITE_SUCCESS; + + /* + * Start a new rewrite context and set current context + */ + } else if ( strcasecmp( argv[ 0 ], "rewriteContext" ) == 0 ) { + if ( argc < 2 ) { + Debug( LDAP_DEBUG_ANY, + "[%s:%d] rewriteContext needs 'name'\n%s", + fname, lineno, "" ); + return -1; + } + + /* + * Checks for existence (lots of contexts should be + * available by default ...) + */ + rewrite_int_curr_context = rewrite_context_find( info, argv[ 1 ] ); + if ( rewrite_int_curr_context == NULL ) { + rewrite_int_curr_context = rewrite_context_create( info, + argv[ 1 ] ); + } + if ( rewrite_int_curr_context == NULL ) { + return -1; + } + + if ( argc > 2 ) { + + /* + * A context can alias another (e.g., the `builtin' + * contexts for backend operations, if not defined, + * alias the `default' rewrite context (with the + * notable exception of the searchResult context, + * which can be undefined) + */ + if ( strcasecmp( argv[ 2 ], "alias" ) == 0 ) { + struct rewrite_context *aliased; + + if ( argc == 3 ) { + Debug( LDAP_DEBUG_ANY, + "[%s:%d] rewriteContext" + " needs 'name' after" + " 'alias'\n%s", + fname, lineno, "" ); + return -1; + + } else if ( argc > 4 ) { + Debug( LDAP_DEBUG_ANY, + "[%s:%d] extra fields in" + " rewriteContext" + " after aliased name" + " will be" + " discarded\n%s", + fname, lineno, "" ); + } + + aliased = rewrite_context_find( info, + argv[ 3 ] ); + if ( aliased == NULL ) { + Debug( LDAP_DEBUG_ANY, + "[%s:%d] aliased" + " rewriteContext '%s'" + " does not exists\n", + fname, lineno, + argv[ 3 ] ); + return -1; + } + + rewrite_int_curr_context->lc_alias = aliased; + rewrite_int_curr_context = aliased; + + } else { + Debug( LDAP_DEBUG_ANY, + "[%s:%d] extra fields" + " in rewriteContext" + " will be discarded\n%s", + fname, lineno, "" ); + } + } + rc = REWRITE_SUCCESS; + + /* + * Compile a rule in current context + */ + } else if ( strcasecmp( argv[ 0 ], "rewriteRule" ) == 0 ) { + if ( argc < 3 ) { + Debug( LDAP_DEBUG_ANY, + "[%s:%d] rewriteRule needs 'pattern'" + " 'subst' ['flags']\n%s", + fname, lineno, "" ); + return -1; + + } else if ( argc > 4 ) { + Debug( LDAP_DEBUG_ANY, + "[%s:%d] extra fields in rewriteRule" + " will be discarded\n%s", + fname, lineno, "" ); + } + + if ( rewrite_int_curr_context == NULL ) { + Debug( LDAP_DEBUG_ANY, + "[%s:%d] rewriteRule outside a" + " context; will add to default\n%s", + fname, lineno, "" ); + rewrite_int_curr_context = rewrite_context_find( info, + REWRITE_DEFAULT_CONTEXT ); + + /* + * Default context MUST exist in a properly initialized + * struct rewrite_info + */ + assert( rewrite_int_curr_context != NULL ); + } + + rc = rewrite_rule_compile( info, rewrite_int_curr_context, argv[ 1 ], + argv[ 2 ], ( argc == 4 ? argv[ 3 ] : "" ) ); + + /* + * Add a plugin map to the map tree + */ + } else if ( strcasecmp( argv[ 0 ], "rewriteMap" ) == 0 ) { + if ( argc < 3 ) { + Debug( LDAP_DEBUG_ANY, + "[%s:%d] rewriteMap needs at least 'type'" + " and 'name' ['args']\n%s", + fname, lineno, "" ); + return -1; + } + + rc = rewrite_parse_builtin_map( info, fname, lineno, + argc, argv ); + + /* + * Set the value of a global scope parameter + */ + } else if ( strcasecmp( argv[ 0 ], "rewriteParam" ) == 0 ) { + if ( argc < 3 ) { + Debug( LDAP_DEBUG_ANY, + "[%s:%d] rewriteParam needs 'name'" + " and 'value'\n%s", + fname, lineno, "" ); + return -1; + } + + rc = rewrite_param_set( info, argv[ 1 ], argv[ 2 ] ); + + /* + * Error + */ + } else { + Debug( LDAP_DEBUG_ANY, + "[%s:%d] unknown command '%s'\n", + fname, lineno, "" ); + return -1; + } + + return rc; +} + +/* + * Compares two maps + */ +static int +rewrite_builtin_map_cmp( + const void *c1, + const void *c2 +) +{ + const struct rewrite_builtin_map *m1, *m2; + + m1 = ( const struct rewrite_builtin_map * )c1; + m2 = ( const struct rewrite_builtin_map * )c2; + + assert( m1 != NULL ); + assert( m2 != NULL ); + assert( m1->lb_name != NULL ); + assert( m2->lb_name != NULL ); + + return strcasecmp( m1->lb_name, m2->lb_name ); +} + +/* + * Duplicate map ? + */ +static int +rewrite_builtin_map_dup( + void *c1, + void *c2 +) +{ + struct rewrite_builtin_map *m1, *m2; + + m1 = ( struct rewrite_builtin_map * )c1; + m2 = ( struct rewrite_builtin_map * )c2; + + assert( m1 != NULL ); + assert( m2 != NULL ); + assert( m1->lb_name != NULL ); + assert( m2->lb_name != NULL ); + + return ( strcasecmp( m1->lb_name, m2->lb_name ) == 0 ? -1 : 0 ); +} + +/* + * Adds a map to the info map tree + */ +static int +rewrite_builtin_map_insert( + struct rewrite_info *info, + struct rewrite_builtin_map *map +) +{ + /* + * May need a mutex? + */ + return avl_insert( &info->li_maps, ( caddr_t )map, + rewrite_builtin_map_cmp, + rewrite_builtin_map_dup ); +} + +/* + * Retrieves a map + */ +struct rewrite_builtin_map * +rewrite_builtin_map_find( + struct rewrite_info *info, + const char *name +) +{ + struct rewrite_builtin_map tmp; + + assert( info != NULL ); + assert( name != NULL ); + + tmp.lb_name = ( char * )name; + + return ( struct rewrite_builtin_map * )avl_find( info->li_maps, + ( caddr_t )&tmp, rewrite_builtin_map_cmp ); +} + +/* + * Parses a plugin map + */ +static int +rewrite_parse_builtin_map( + struct rewrite_info *info, + const char *fname, + int lineno, + int argc, + char **argv +) +{ + struct rewrite_builtin_map *map; + +#define MAP_TYPE 1 +#define MAP_NAME 2 + + assert( info != NULL ); + assert( fname != NULL ); + assert( argc > 2 ); + assert( argv != NULL ); + assert( strcasecmp( argv[ 0 ], "rewriteMap" ) == 0 ); + + map = calloc( sizeof( struct rewrite_builtin_map ), 1 ); + if ( map == NULL ) { + return REWRITE_ERR; + } + + map->lb_name = strdup( argv[ MAP_NAME ] ); + if ( map->lb_name == NULL ) { + free( map ); + return REWRITE_ERR; + } + + /* + * Built-in ldap map + */ + if (( map->lb_mapper = rewrite_mapper_find( argv[ MAP_TYPE ] ))) { + map->lb_type = REWRITE_BUILTIN_MAP; + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + if ( ldap_pvt_thread_mutex_init( & map->lb_mutex ) ) { + free( map->lb_name ); + free( map ); + return REWRITE_ERR; + } +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + map->lb_private = map->lb_mapper->rm_config( fname, lineno, + argc - 3, argv + 3 ); + + /* + * Error + */ + } else { + free( map ); + Debug( LDAP_DEBUG_ANY, "[%s:%d] unknown map type\n%s", + fname, lineno, "" ); + return -1; + } + + return rewrite_builtin_map_insert( info, map ); +} diff --git a/libraries/librewrite/context.c b/libraries/librewrite/context.c new file mode 100644 index 0000000..cc7854c --- /dev/null +++ b/libraries/librewrite/context.c @@ -0,0 +1,474 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2000-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENT: + * This work was initially developed by Pierangelo Masarati for + * inclusion in OpenLDAP Software. + */ + +#include <portable.h> + +#include "rewrite-int.h" + +/* + * Compares two struct rewrite_context based on the name; + * used by avl stuff + */ +static int +rewrite_context_cmp( + const void *c1, + const void *c2 +) +{ + const struct rewrite_context *lc1, *lc2; + + lc1 = (const struct rewrite_context *)c1; + lc2 = (const struct rewrite_context *)c2; + + assert( c1 != NULL ); + assert( c2 != NULL ); + assert( lc1->lc_name != NULL ); + assert( lc2->lc_name != NULL ); + + return strcasecmp( lc1->lc_name, lc2->lc_name ); +} + +/* + * Returns -1 in case a duplicate struct rewrite_context + * has been inserted; used by avl stuff + */ +static int +rewrite_context_dup( + void *c1, + void *c2 + ) +{ + struct rewrite_context *lc1, *lc2; + + lc1 = (struct rewrite_context *)c1; + lc2 = (struct rewrite_context *)c2; + + assert( c1 != NULL ); + assert( c2 != NULL ); + assert( lc1->lc_name != NULL ); + assert( lc2->lc_name != NULL ); + + return( strcasecmp( lc1->lc_name, lc2->lc_name) == 0 ? -1 : 0 ); +} + +/* + * Finds the context named rewriteContext in the context tree + */ +struct rewrite_context * +rewrite_context_find( + struct rewrite_info *info, + const char *rewriteContext +) +{ + struct rewrite_context *context, c; + + assert( info != NULL ); + assert( rewriteContext != NULL ); + + /* + * Fetches the required rewrite context + */ + c.lc_name = (char *)rewriteContext; + context = (struct rewrite_context *)avl_find( info->li_context, + (caddr_t)&c, rewrite_context_cmp ); + if ( context == NULL ) { + return NULL; + } + + /* + * De-aliases the context if required + */ + if ( context->lc_alias ) { + return context->lc_alias; + } + + return context; +} + +/* + * Creates a new context called rewriteContext and stores in into the tree + */ +struct rewrite_context * +rewrite_context_create( + struct rewrite_info *info, + const char *rewriteContext +) +{ + struct rewrite_context *context; + int rc; + + assert( info != NULL ); + assert( rewriteContext != NULL ); + + context = calloc( sizeof( struct rewrite_context ), 1 ); + if ( context == NULL ) { + return NULL; + } + + /* + * Context name + */ + context->lc_name = strdup( rewriteContext ); + if ( context->lc_name == NULL ) { + free( context ); + return NULL; + } + + /* + * The first, empty rule + */ + context->lc_rule = calloc( sizeof( struct rewrite_rule ), 1 ); + if ( context->lc_rule == NULL ) { + free( context->lc_name ); + free( context ); + return NULL; + } + memset( context->lc_rule, 0, sizeof( struct rewrite_rule ) ); + + /* + * Add context to tree + */ + rc = avl_insert( &info->li_context, (caddr_t)context, + rewrite_context_cmp, rewrite_context_dup ); + if ( rc == -1 ) { + free( context->lc_rule ); + free( context->lc_name ); + free( context ); + return NULL; + } + + return context; +} + +/* + * Finds the next rule according to a goto action statement, + * or null in case of error. + * Helper for rewrite_context_apply. + */ +static struct rewrite_rule * +rewrite_action_goto( + struct rewrite_action *action, + struct rewrite_rule *rule +) +{ + int n; + + assert( action != NULL ); + assert( action->la_args != NULL ); + assert( rule != NULL ); + + n = ((int *)action->la_args)[ 0 ]; + + if ( n > 0 ) { + for ( ; n > 1 && rule != NULL ; n-- ) { + rule = rule->lr_next; + } + } else if ( n <= 0 ) { + for ( ; n < 1 && rule != NULL ; n++ ) { + rule = rule->lr_prev; + } + } + + return rule; +} + +/* + * Rewrites string according to context; may return: + * OK: fine; if *result != NULL rule matched and rewrite succeeded. + * STOP: fine, rule matched; stop processing following rules + * UNWILL: rule matched; force 'unwilling to perform' + */ +int +rewrite_context_apply( + struct rewrite_info *info, + struct rewrite_op *op, + struct rewrite_context *context, + const char *string, + char **result +) +{ + struct rewrite_rule *rule; + char *s, *res = NULL; + int return_code = REWRITE_REGEXEC_OK; + + assert( info != NULL ); + assert( op != NULL ); + assert( context != NULL ); + assert( context->lc_rule != NULL ); + assert( string != NULL ); + assert( result != NULL ); + + op->lo_depth++; + + Debug( LDAP_DEBUG_TRACE, "==> rewrite_context_apply" + " [depth=%d] string='%s'\n", + op->lo_depth, string, 0 ); + assert( op->lo_depth > 0 ); + + s = (char *)string; + + for ( rule = context->lc_rule->lr_next; + rule != NULL && op->lo_num_passes < info->li_max_passes; + rule = rule->lr_next, op->lo_num_passes++ ) { + int rc; + + /* + * Apply a single rule + */ + rc = rewrite_rule_apply( info, op, rule, s, &res ); + + /* + * A rule may return: + * OK with result != NULL if matched + * ERR if anything was wrong + * UNWILLING if the server should drop the request + * the latter case in honored immediately; + * the other two may require some special actions to take + * place. + */ + switch ( rc ) { + + case REWRITE_REGEXEC_ERR: + Debug( LDAP_DEBUG_ANY, "==> rewrite_context_apply" + " error ...\n", 0, 0, 0); + + /* + * Checks for special actions to be taken + * in case of error ... + */ + if ( rule->lr_action != NULL ) { + struct rewrite_action *action; + int do_continue = 0; + + for ( action = rule->lr_action; + action != NULL; + action = action->la_next ) { + switch ( action->la_type ) { + + /* + * This action takes precedence + * over the others in case of failure + */ + case REWRITE_ACTION_IGNORE_ERR: + Debug( LDAP_DEBUG_ANY, + "==> rewrite_context_apply" + " ignoring error ...\n", 0, 0, 0 ); + do_continue = 1; + break; + + /* + * Goto is honored only if it comes + * after ignore error + */ + case REWRITE_ACTION_GOTO: + if ( do_continue ) { + rule = rewrite_action_goto( action, rule ); + if ( rule == NULL ) { + return_code = REWRITE_REGEXEC_ERR; + goto rc_end_of_context; + } + } + break; + + /* + * Other actions are ignored + */ + default: + break; + } + } + + if ( do_continue ) { + if ( rule->lr_next == NULL ) { + res = s; + } + goto rc_continue; + } + } + + /* + * Default behavior is to bail out ... + */ + return_code = REWRITE_REGEXEC_ERR; + goto rc_end_of_context; + + /* + * OK means there were no errors or special return codes; + * if res is defined, it means the rule matched and we + * got a sucessful rewriting + */ + case REWRITE_REGEXEC_OK: + + /* + * It matched! Check for actions ... + */ + if ( res != NULL ) { + struct rewrite_action *action; + + if ( s != string && s != res ) { + free( s ); + } + s = res; + + for ( action = rule->lr_action; + action != NULL; + action = action->la_next ) { + + switch ( action->la_type ) { + + /* + * This ends the rewrite context + * successfully + */ + case REWRITE_ACTION_STOP: + goto rc_end_of_context; + + /* + * This instructs the server to return + * an `unwilling to perform' error + * message + */ + case REWRITE_ACTION_UNWILLING: + return_code = REWRITE_REGEXEC_UNWILLING; + goto rc_end_of_context; + + /* + * This causes the processing to + * jump n rules back and forth + */ + case REWRITE_ACTION_GOTO: + rule = rewrite_action_goto( action, rule ); + if ( rule == NULL ) { + return_code = REWRITE_REGEXEC_ERR; + goto rc_end_of_context; + } + break; + + /* + * This ends the rewrite context + * and returns a user-defined + * error code + */ + case REWRITE_ACTION_USER: + return_code = ((int *)action->la_args)[ 0 ]; + goto rc_end_of_context; + + default: + /* ... */ + break; + } + } + + /* + * If result was OK and string didn't match, + * in case of last rule we need to set the + * result back to the string + */ + } else if ( rule->lr_next == NULL ) { + res = s; + } + + break; + + /* + * A STOP has propagated ... + */ + case REWRITE_REGEXEC_STOP: + goto rc_end_of_context; + + /* + * This will instruct the server to return + * an `unwilling to perform' error message + */ + case REWRITE_REGEXEC_UNWILLING: + return_code = REWRITE_REGEXEC_UNWILLING; + goto rc_end_of_context; + + /* + * A user-defined error code has propagated ... + */ + default: + assert( rc >= REWRITE_REGEXEC_USER ); + goto rc_end_of_context; + + } + +rc_continue:; /* sent here by actions that require to continue */ + + } + +rc_end_of_context:; + *result = res; + + Debug( LDAP_DEBUG_TRACE, "==> rewrite_context_apply" + " [depth=%d] res={%d,'%s'}\n", + op->lo_depth, return_code, ( res ? res : "NULL" ) ); + + assert( op->lo_depth > 0 ); + op->lo_depth--; + + return return_code; +} + +void +rewrite_context_free( + void *tmp +) +{ + struct rewrite_context *context = (struct rewrite_context *)tmp; + + assert( tmp != NULL ); + + rewrite_context_destroy( &context ); +} + +int +rewrite_context_destroy( + struct rewrite_context **pcontext +) +{ + struct rewrite_context *context; + struct rewrite_rule *r; + + assert( pcontext != NULL ); + assert( *pcontext != NULL ); + + context = *pcontext; + + assert( context->lc_rule != NULL ); + + for ( r = context->lc_rule->lr_next; r; ) { + struct rewrite_rule *cr = r; + + r = r->lr_next; + rewrite_rule_destroy( &cr ); + } + + free( context->lc_rule ); + context->lc_rule = NULL; + + assert( context->lc_name != NULL ); + free( context->lc_name ); + context->lc_name = NULL; + + free( context ); + *pcontext = NULL; + + return 0; +} diff --git a/libraries/librewrite/info.c b/libraries/librewrite/info.c new file mode 100644 index 0000000..3952e97 --- /dev/null +++ b/libraries/librewrite/info.c @@ -0,0 +1,284 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2000-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENT: + * This work was initially developed by Pierangelo Masarati for + * inclusion in OpenLDAP Software. + */ + +#include <portable.h> + +#include "rewrite-int.h" + +/* + * Global data + */ + +/* + * This becomes the running context for subsequent calls to + * rewrite_parse; it can be altered only by a + * rewriteContext config line or by a change in info. + */ +struct rewrite_context *rewrite_int_curr_context = NULL; + +/* + * Inits the info + */ +struct rewrite_info * +rewrite_info_init( + int mode +) +{ + struct rewrite_info *info; + struct rewrite_context *context; + + switch ( mode ) { + case REWRITE_MODE_ERR: + case REWRITE_MODE_OK: + case REWRITE_MODE_COPY_INPUT: + case REWRITE_MODE_USE_DEFAULT: + break; + default: + mode = REWRITE_MODE_USE_DEFAULT; + break; + /* return NULL */ + } + + /* + * Resets the running context for parsing ... + */ + rewrite_int_curr_context = NULL; + + info = calloc( sizeof( struct rewrite_info ), 1 ); + if ( info == NULL ) { + return NULL; + } + + info->li_state = REWRITE_DEFAULT; + info->li_max_passes = REWRITE_MAX_PASSES; + info->li_max_passes_per_rule = REWRITE_MAX_PASSES; + info->li_rewrite_mode = mode; + + /* + * Add the default (empty) rule + */ + context = rewrite_context_create( info, REWRITE_DEFAULT_CONTEXT ); + if ( context == NULL ) { + free( info ); + return NULL; + } + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + if ( ldap_pvt_thread_rdwr_init( &info->li_cookies_mutex ) ) { + avl_free( info->li_context, rewrite_context_free ); + free( info ); + return NULL; + } + if ( ldap_pvt_thread_rdwr_init( &info->li_params_mutex ) ) { + ldap_pvt_thread_rdwr_destroy( &info->li_cookies_mutex ); + avl_free( info->li_context, rewrite_context_free ); + free( info ); + return NULL; + } +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + return info; +} + +/* + * Cleans up the info structure + */ +int +rewrite_info_delete( + struct rewrite_info **pinfo +) +{ + struct rewrite_info *info; + + assert( pinfo != NULL ); + assert( *pinfo != NULL ); + + info = *pinfo; + + if ( info->li_context ) { + avl_free( info->li_context, rewrite_context_free ); + } + info->li_context = NULL; + + if ( info->li_maps ) { + avl_free( info->li_maps, rewrite_builtin_map_free ); + } + info->li_maps = NULL; + + rewrite_session_destroy( info ); + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_destroy( &info->li_cookies_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + rewrite_param_destroy( info ); + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_destroy( &info->li_params_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + free( info ); + *pinfo = NULL; + + return REWRITE_SUCCESS; +} + +/* + * Rewrites a string according to context. + * If the engine is off, OK is returned, but the return string will be NULL. + * In case of 'unwilling to perform', UNWILLING is returned, and the + * return string will also be null. The same in case of error. + * Otherwise, OK is returned, and result will hold a newly allocated string + * with the rewriting. + * + * What to do in case of non-existing rewrite context is still an issue. + * Four possibilities: + * - error, + * - ok with NULL result, + * - ok with copy of string as result, + * - use the default rewrite context. + */ +int +rewrite( + struct rewrite_info *info, + const char *rewriteContext, + const char *string, + char **result +) +{ + return rewrite_session( info, rewriteContext, + string, NULL, result ); +} + +int +rewrite_session( + struct rewrite_info *info, + const char *rewriteContext, + const char *string, + const void *cookie, + char **result +) +{ + struct rewrite_context *context; + struct rewrite_op op = { 0, 0, NULL, NULL, NULL }; + int rc; + + assert( info != NULL ); + assert( rewriteContext != NULL ); + assert( string != NULL ); + assert( result != NULL ); + + /* + * cookie can be null; means: don't care about session stuff + */ + + *result = NULL; + op.lo_cookie = cookie; + + /* + * Engine not on means no failure, but explicit no rewriting + */ + if ( info->li_state != REWRITE_ON ) { + rc = REWRITE_REGEXEC_OK; + goto rc_return; + } + + /* + * Undefined context means no rewriting also + * (conservative, are we sure it's what we want?) + */ + context = rewrite_context_find( info, rewriteContext ); + if ( context == NULL ) { + switch ( info->li_rewrite_mode ) { + case REWRITE_MODE_ERR: + rc = REWRITE_REGEXEC_ERR; + goto rc_return; + + case REWRITE_MODE_OK: + rc = REWRITE_REGEXEC_OK; + goto rc_return; + + case REWRITE_MODE_COPY_INPUT: + *result = strdup( string ); + rc = ( *result != NULL ) ? REWRITE_REGEXEC_OK : REWRITE_REGEXEC_ERR; + goto rc_return; + + case REWRITE_MODE_USE_DEFAULT: + context = rewrite_context_find( info, + REWRITE_DEFAULT_CONTEXT ); + break; + } + } + +#if 0 /* FIXME: not used anywhere! (debug? then, why strdup?) */ + op.lo_string = strdup( string ); + if ( op.lo_string == NULL ) { + rc = REWRITE_REGEXEC_ERR; + goto rc_return; + } +#endif + + /* + * Applies rewrite context + */ + rc = rewrite_context_apply( info, &op, context, string, result ); + assert( op.lo_depth == 0 ); + +#if 0 /* FIXME: not used anywhere! (debug? then, why strdup?) */ + free( op.lo_string ); +#endif + + switch ( rc ) { + /* + * Success + */ + case REWRITE_REGEXEC_OK: + case REWRITE_REGEXEC_STOP: + /* + * If rewrite succeeded return OK regardless of how + * the successful rewriting was obtained! + */ + rc = REWRITE_REGEXEC_OK; + break; + + + /* + * Internal or forced error, return = NULL; rc already OK. + */ + case REWRITE_REGEXEC_UNWILLING: + case REWRITE_REGEXEC_ERR: + if ( *result != NULL ) { + if ( *result != string ) { + free( *result ); + } + *result = NULL; + } + + default: + break; + } + +rc_return:; + if ( op.lo_vars ) { + rewrite_var_delete( op.lo_vars ); + } + + return rc; +} + diff --git a/libraries/librewrite/ldapmap.c b/libraries/librewrite/ldapmap.c new file mode 100644 index 0000000..fc7eeac --- /dev/null +++ b/libraries/librewrite/ldapmap.c @@ -0,0 +1,454 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2000-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENT: + * This work was initially developed by Pierangelo Masarati for + * inclusion in OpenLDAP Software. + */ + +#include <portable.h> + +#define LDAP_DEPRECATED 1 +#include "rewrite-int.h" +#include "rewrite-map.h" + +typedef enum { + MAP_LDAP_UNKNOWN, + MAP_LDAP_EVERYTIME, + MAP_LDAP_NOW, + MAP_LDAP_LATER +} bindwhen_t; + +/* + * LDAP map data structure + */ +struct ldap_map_data { + char *lm_url; + LDAPURLDesc *lm_lud; + int lm_version; + char *lm_binddn; + struct berval lm_cred; + + bindwhen_t lm_when; + + LDAP *lm_ld; + + int lm_wantdn; + char *lm_attrs[ 2 ]; + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_mutex_t lm_mutex; +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ +}; + +static void +map_ldap_free( + struct ldap_map_data *data +) +{ + assert( data != NULL ); + + if ( data->lm_url != NULL ) { + free( data->lm_url ); + } + + if ( data->lm_lud != NULL ) { + ldap_free_urldesc( data->lm_lud ); + } + + if ( data->lm_binddn != NULL ) { + free( data->lm_binddn ); + } + + if ( data->lm_cred.bv_val != NULL ) { + memset( data->lm_cred.bv_val, 0, data->lm_cred.bv_len ); + free( data->lm_cred.bv_val ); + data->lm_cred.bv_val = NULL; + data->lm_cred.bv_len = 0; + } + + if ( data->lm_when != MAP_LDAP_EVERYTIME && data->lm_ld != NULL ) { + ldap_unbind_ext( data->lm_ld, NULL, NULL ); + } + + free( data ); +} + +static void * +map_ldap_parse( + const char *fname, + int lineno, + int argc, + char **argv +) +{ + struct ldap_map_data *data; + char *p, *uri; + + assert( fname != NULL ); + assert( argv != NULL ); + + data = calloc( sizeof( struct ldap_map_data ), 1 ); + if ( data == NULL ) { + return NULL; + } + + if ( argc < 1 ) { + Debug( LDAP_DEBUG_ANY, + "[%s:%d] ldap map needs URI\n%s", + fname, lineno, "" ); + free( data ); + return NULL; + } + + uri = argv[ 0 ]; + if ( strncasecmp( uri, "uri=", STRLENOF( "uri=" ) ) == 0 ) { + uri += STRLENOF( "uri=" ); + } + + data->lm_url = strdup( uri ); + if ( data->lm_url == NULL ) { + map_ldap_free( data ); + return NULL; + } + + if ( ldap_url_parse( uri, &data->lm_lud ) != REWRITE_SUCCESS ) { + Debug( LDAP_DEBUG_ANY, + "[%s:%d] illegal URI '%s'\n", + fname, lineno, argv[ 0 ] ); + map_ldap_free( data ); + return NULL; + } + + /* trim everything after [host][:port] */ + p = strchr( data->lm_url, '/' ); + assert( p[ 1 ] == '/' ); + if ( ( p = strchr( p + 2, '/' ) ) != NULL ) { + p[ 0 ] = '\0'; + } + + if ( data->lm_lud->lud_attrs == NULL ) { + data->lm_attrs[ 0 ] = LDAP_NO_ATTRS; + data->lm_wantdn = 1; + + } else { + if ( data->lm_lud->lud_attrs[ 1 ] != NULL ) { + Debug( LDAP_DEBUG_ANY, + "[%s:%d] only one attribute allowed in URI\n", + fname, lineno, 0 ); + map_ldap_free( data ); + return NULL; + } + + if ( strcasecmp( data->lm_lud->lud_attrs[ 0 ], "dn" ) == 0 + || strcasecmp( data->lm_lud->lud_attrs[ 0 ], "entryDN" ) == 0 ) + { + ldap_memfree( data->lm_lud->lud_attrs[ 0 ] ); + ldap_memfree( data->lm_lud->lud_attrs ); + data->lm_lud->lud_attrs = NULL; + data->lm_attrs[ 0 ] = LDAP_NO_ATTRS; + data->lm_wantdn = 1; + + } else { + data->lm_attrs[ 0 ] = data->lm_lud->lud_attrs[ 0 ]; + } + } + + data->lm_attrs[ 1 ] = NULL; + + /* safe defaults */ + data->lm_version = LDAP_VERSION3; + + for ( argc--, argv++; argc > 0; argc--, argv++ ) { + if ( strncasecmp( argv[ 0 ], "binddn=", STRLENOF( "binddn=" ) ) == 0 ) { + char *p = argv[ 0 ] + STRLENOF( "binddn=" ); + int l; + + if ( p[ 0 ] == '\"' || p [ 0 ] == '\'' ) { + l = strlen( p ) - 2; + p++; + if ( p[ l ] != p[ 0 ] ) { + map_ldap_free( data ); + return NULL; + } + } else { + l = strlen( p ); + } + + data->lm_binddn = strdup( p ); + if ( data->lm_binddn == NULL ) { + map_ldap_free( data ); + return NULL; + } + + if ( data->lm_binddn[ l ] == '\"' + || data->lm_binddn[ l ] == '\'' ) { + data->lm_binddn[ l ] = '\0'; + } + + /* deprecated */ + } else if ( strncasecmp( argv[ 0 ], "bindpw=", STRLENOF( "bindpw=" ) ) == 0 ) { + ber_str2bv( argv[ 0 ] + STRLENOF( "bindpw=" ), 0, 1, &data->lm_cred ); + if ( data->lm_cred.bv_val == NULL ) { + map_ldap_free( data ); + return NULL; + } + + } else if ( strncasecmp( argv[ 0 ], "credentials=", STRLENOF( "credentials=" ) ) == 0 ) { + ber_str2bv( argv[ 0 ] + STRLENOF( "credentials=" ), 0, 1, &data->lm_cred ); + if ( data->lm_cred.bv_val == NULL ) { + map_ldap_free( data ); + return NULL; + } + + } else if ( strncasecmp( argv[ 0 ], "bindwhen=", STRLENOF( "bindwhen=" ) ) == 0 ) { + char *p = argv[ 0 ] + STRLENOF( "bindwhen=" ); + + if ( strcasecmp( p, "now" ) == 0 ) { + int rc; + + data->lm_when = MAP_LDAP_NOW; + + /* + * Init LDAP handler ... + */ + rc = ldap_initialize( &data->lm_ld, data->lm_url ); + if ( rc != LDAP_SUCCESS ) { + map_ldap_free( data ); + return NULL; + } + + ldap_set_option( data->lm_ld, + LDAP_OPT_PROTOCOL_VERSION, + (void *)&data->lm_version ); + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_mutex_init( &data->lm_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + } else if ( strcasecmp( p, "later" ) == 0 ) { + data->lm_when = MAP_LDAP_LATER; + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_mutex_init( &data->lm_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + } else if ( strcasecmp( p, "everytime" ) == 0 ) { + data->lm_when = MAP_LDAP_EVERYTIME; + } else { + /* ignore ... */ + } + + } else if ( strncasecmp( argv[ 0 ], "version=", STRLENOF( "version=" ) ) == 0 ) { + if ( lutil_atoi( &data->lm_version, argv[ 0 ] + STRLENOF( "version=" ) ) ) { + map_ldap_free( data ); + return NULL; + } + + switch ( data->lm_version ) { + case LDAP_VERSION2: + case LDAP_VERSION3: + break; + + default: + Debug( LDAP_DEBUG_ANY, + "[%s:%d] unknown version %s\n", + fname, lineno, p ); + map_ldap_free( data ); + return NULL; + } + + } else { + Debug( LDAP_DEBUG_ANY, + "[%s:%d] unknown option %s (ignored)\n", + fname, lineno, argv[0] ); + } + } + + if ( data->lm_when == MAP_LDAP_UNKNOWN ) { + data->lm_when = MAP_LDAP_EVERYTIME; + } + + return ( void * )data; +} + +static int +map_ldap_apply( + void *private, + const char *filter, + struct berval *val + +) +{ + LDAP *ld; + LDAPMessage *res = NULL, *entry; + int rc; + struct ldap_map_data *data = private; + LDAPURLDesc *lud = data->lm_lud; + + int first_try = 1, set_version = 0; + + assert( private != NULL ); + assert( filter != NULL ); + assert( val != NULL ); + + val->bv_val = NULL; + val->bv_len = 0; + + if ( data->lm_when == MAP_LDAP_EVERYTIME ) { + rc = ldap_initialize( &ld, data->lm_url ); + set_version = 1; + + } else { +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_mutex_lock( &data->lm_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + rc = LDAP_SUCCESS; + + if ( data->lm_when == MAP_LDAP_LATER && data->lm_ld == NULL ) { + rc = ldap_initialize( &data->lm_ld, data->lm_url ); + set_version = 1; + } + + ld = data->lm_ld; + } + + if ( rc != LDAP_SUCCESS ) { + rc = REWRITE_ERR; + goto rc_return; + } + +do_bind:; + if ( set_version ) { + ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, + (void *)&data->lm_version ); + set_version = 0; + } + + if ( data->lm_binddn != NULL ) { + rc = ldap_sasl_bind_s( ld, data->lm_binddn, + LDAP_SASL_SIMPLE, &data->lm_cred, + NULL, NULL, NULL ); + if ( rc == LDAP_SERVER_DOWN && first_try ) { + first_try = 0; + if ( ldap_initialize( &ld, data->lm_url ) != LDAP_SUCCESS ) { + rc = REWRITE_ERR; + goto rc_return; + } + set_version = 1; + goto do_bind; + + } else if ( rc != REWRITE_SUCCESS ) { + rc = REWRITE_ERR; + goto rc_return; + } + } + + rc = ldap_search_ext_s( ld, lud->lud_dn, lud->lud_scope, ( char * )filter, + data->lm_attrs, 0, NULL, NULL, NULL, 1, &res ); + if ( rc == LDAP_SERVER_DOWN && first_try ) { + first_try = 0; + if ( ldap_initialize( &ld, data->lm_url ) != LDAP_SUCCESS ) { + rc = REWRITE_ERR; + goto rc_return; + } + set_version = 1; + goto do_bind; + + } else if ( rc != LDAP_SUCCESS ) { + rc = REWRITE_ERR; + goto rc_return; + } + + if ( ldap_count_entries( ld, res ) != 1 ) { + ldap_msgfree( res ); + rc = REWRITE_ERR; + goto rc_return; + } + + entry = ldap_first_entry( ld, res ); + assert( entry != NULL ); + + if ( data->lm_wantdn == 1 ) { + /* + * dn is newly allocated, so there's no need to strdup it + */ + val->bv_val = ldap_get_dn( ld, entry ); + val->bv_len = strlen( val->bv_val ); + + } else { + struct berval **values; + + values = ldap_get_values_len( ld, entry, data->lm_attrs[ 0 ] ); + if ( values != NULL ) { + if ( values[ 0 ] != NULL && values[ 0 ]->bv_val != NULL ) { +#if 0 + /* NOTE: in principle, multiple values + * should not be acceptable according + * to the current API; ignore by now */ + if ( values[ 1 ] != NULL ) { + /* error */ + } +#endif + ber_dupbv( val, values[ 0 ] ); + } + ldap_value_free_len( values ); + } + } + + ldap_msgfree( res ); + + if ( val->bv_val == NULL ) { + rc = REWRITE_ERR; + goto rc_return; + } + +rc_return:; + if ( data->lm_when == MAP_LDAP_EVERYTIME ) { + if ( ld != NULL ) { + ldap_unbind_ext( ld, NULL, NULL ); + } + + } else { + data->lm_ld = ld; +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_mutex_unlock( &data->lm_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + } + + return rc; +} + +static int +map_ldap_destroy( + void *private +) +{ + struct ldap_map_data *data = private; + + assert( private != NULL ); + + map_ldap_free( data ); + + return 0; +} + +const rewrite_mapper rewrite_ldap_mapper = { + "ldap", + map_ldap_parse, + map_ldap_apply, + map_ldap_destroy +}; + diff --git a/libraries/librewrite/map.c b/libraries/librewrite/map.c new file mode 100644 index 0000000..43341e3 --- /dev/null +++ b/libraries/librewrite/map.c @@ -0,0 +1,583 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2000-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENT: + * This work was initially developed by Pierangelo Masarati for + * inclusion in OpenLDAP Software. + */ + +#include <portable.h> + +#include <stdio.h> + +#ifdef HAVE_PWD_H +#include <pwd.h> +#endif + +#include "rewrite-int.h" +#include "rewrite-map.h" + +static int num_mappers; +static const rewrite_mapper **mappers; +#define MAPPER_ALLOC 8 + +struct rewrite_map * +rewrite_map_parse( + struct rewrite_info *info, + const char *string, + const char **currpos +) +{ + struct rewrite_map *map = NULL; + struct rewrite_subst *subst = NULL; + char *s, *begin = NULL, *end; + const char *p; + int l, cnt, mtx = 0, rc = 0; + + assert( info != NULL ); + assert( string != NULL ); + assert( currpos != NULL ); + + *currpos = NULL; + + /* + * Go to the end of the map invocation (the right closing brace) + */ + for ( p = string, cnt = 1; p[ 0 ] != '\0' && cnt > 0; p++ ) { + if ( IS_REWRITE_SUBMATCH_ESCAPE( p[ 0 ] ) ) { + /* + * '%' marks the beginning of a new map + */ + if ( p[ 1 ] == '{' ) { + cnt++; + /* + * '%' followed by a digit may mark the beginning + * of an old map + */ + } else if ( isdigit( (unsigned char) p[ 1 ] ) && p[ 2 ] == '{' ) { + cnt++; + p++; + } + + if ( p[ 1 ] != '\0' ) { + p++; + } + + } else if ( p[ 0 ] == '}' ) { + cnt--; + } + } + if ( cnt != 0 ) { + return NULL; + } + *currpos = p; + + /* + * Copy the map invocation + */ + l = p - string - 1; + s = calloc( sizeof( char ), l + 1 ); + if ( s == NULL ) { + return NULL; + } + AC_MEMCPY( s, string, l ); + s[ l ] = 0; + + /* + * Isolate the map name (except for variable deref) + */ + switch ( s[ 0 ] ) { + case REWRITE_OPERATOR_VARIABLE_GET: + case REWRITE_OPERATOR_PARAM_GET: + break; + + default: + begin = strchr( s, '(' ); + if ( begin == NULL ) { + rc = -1; + goto cleanup; + } + begin[ 0 ] = '\0'; + begin++; + break; + } + + /* + * Check for special map types + */ + p = s; + switch ( p[ 0 ] ) { + case REWRITE_OPERATOR_SUBCONTEXT: + case REWRITE_OPERATOR_COMMAND: + case REWRITE_OPERATOR_VARIABLE_SET: + case REWRITE_OPERATOR_VARIABLE_GET: + case REWRITE_OPERATOR_PARAM_GET: + p++; + break; + } + + /* + * Variable set and get may be repeated to indicate session-wide + * instead of operation-wide variables + */ + switch ( p[ 0 ] ) { + case REWRITE_OPERATOR_VARIABLE_SET: + case REWRITE_OPERATOR_VARIABLE_GET: + p++; + break; + } + + /* + * Variable get token can be appended to variable set to mean store + * AND rewrite + */ + if ( p[ 0 ] == REWRITE_OPERATOR_VARIABLE_GET ) { + p++; + } + + /* + * Check the syntax of the variable name + */ + if ( !isalpha( (unsigned char) p[ 0 ] ) ) { + rc = -1; + goto cleanup; + } + for ( p++; p[ 0 ] != '\0'; p++ ) { + if ( !isalnum( (unsigned char) p[ 0 ] ) ) { + rc = -1; + goto cleanup; + } + } + + /* + * Isolate the argument of the map (except for variable deref) + */ + switch ( s[ 0 ] ) { + case REWRITE_OPERATOR_VARIABLE_GET: + case REWRITE_OPERATOR_PARAM_GET: + break; + + default: + end = strrchr( begin, ')' ); + if ( end == NULL ) { + rc = -1; + goto cleanup; + } + end[ 0 ] = '\0'; + + /* + * Compile the substitution pattern of the map argument + */ + subst = rewrite_subst_compile( info, begin ); + if ( subst == NULL ) { + rc = -1; + goto cleanup; + } + break; + } + + /* + * Create the map + */ + map = calloc( sizeof( struct rewrite_map ), 1 ); + if ( map == NULL ) { + rc = -1; + goto cleanup; + } + memset( map, 0, sizeof( struct rewrite_map ) ); + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + if ( ldap_pvt_thread_mutex_init( &map->lm_mutex ) ) { + rc = -1; + goto cleanup; + } + ++mtx; +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + /* + * No subst for variable deref + */ + switch ( s[ 0 ] ) { + case REWRITE_OPERATOR_VARIABLE_GET: + case REWRITE_OPERATOR_PARAM_GET: + break; + + default: + map->lm_subst = subst; + break; + } + + /* + * Parses special map types + */ + switch ( s[ 0 ] ) { + + /* + * Subcontext + */ + case REWRITE_OPERATOR_SUBCONTEXT: /* '>' */ + + /* + * Fetch the rewrite context + * it MUST have been defined previously + */ + map->lm_type = REWRITE_MAP_SUBCONTEXT; + map->lm_name = strdup( s + 1 ); + if ( map->lm_name == NULL ) { + rc = -1; + goto cleanup; + } + map->lm_data = rewrite_context_find( info, s + 1 ); + if ( map->lm_data == NULL ) { + rc = -1; + goto cleanup; + } + break; + + /* + * External command (not implemented yet) + */ + case REWRITE_OPERATOR_COMMAND: /* '|' */ + rc = -1; + goto cleanup; + + /* + * Variable set + */ + case REWRITE_OPERATOR_VARIABLE_SET: /* '&' */ + if ( s[ 1 ] == REWRITE_OPERATOR_VARIABLE_SET ) { + if ( s[ 2 ] == REWRITE_OPERATOR_VARIABLE_GET ) { + map->lm_type = REWRITE_MAP_SETW_SESN_VAR; + map->lm_name = strdup( s + 3 ); + } else { + map->lm_type = REWRITE_MAP_SET_SESN_VAR; + map->lm_name = strdup( s + 2 ); + } + } else { + if ( s[ 1 ] == REWRITE_OPERATOR_VARIABLE_GET ) { + map->lm_type = REWRITE_MAP_SETW_OP_VAR; + map->lm_name = strdup( s + 2 ); + } else { + map->lm_type = REWRITE_MAP_SET_OP_VAR; + map->lm_name = strdup( s + 1 ); + } + } + if ( map->lm_name == NULL ) { + rc = -1; + goto cleanup; + } + break; + + /* + * Variable dereference + */ + case REWRITE_OPERATOR_VARIABLE_GET: /* '*' */ + if ( s[ 1 ] == REWRITE_OPERATOR_VARIABLE_GET ) { + map->lm_type = REWRITE_MAP_GET_SESN_VAR; + map->lm_name = strdup( s + 2 ); + } else { + map->lm_type = REWRITE_MAP_GET_OP_VAR; + map->lm_name = strdup( s + 1 ); + } + if ( map->lm_name == NULL ) { + rc = -1; + goto cleanup; + } + break; + + /* + * Parameter + */ + case REWRITE_OPERATOR_PARAM_GET: /* '$' */ + map->lm_type = REWRITE_MAP_GET_PARAM; + map->lm_name = strdup( s + 1 ); + if ( map->lm_name == NULL ) { + rc = -1; + goto cleanup; + } + break; + + /* + * Built-in map + */ + default: + map->lm_type = REWRITE_MAP_BUILTIN; + map->lm_name = strdup( s ); + if ( map->lm_name == NULL ) { + rc = -1; + goto cleanup; + } + map->lm_data = rewrite_builtin_map_find( info, s ); + if ( map->lm_data == NULL ) { + rc = -1; + goto cleanup; + } + break; + + } + +cleanup: + free( s ); + if ( rc ) { + if ( subst != NULL ) { + free( subst ); + } + if ( map ) { +#ifdef USE_REWRITE_LDAP_PVT_THREADS + if ( mtx ) { + ldap_pvt_thread_mutex_destroy( &map->lm_mutex ); + } +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + if ( map->lm_name ) { + free( map->lm_name ); + map->lm_name = NULL; + } + free( map ); + map = NULL; + } + } + + return map; +} + +/* + * Applies the new map type + */ +int +rewrite_map_apply( + struct rewrite_info *info, + struct rewrite_op *op, + struct rewrite_map *map, + struct berval *key, + struct berval *val +) +{ + int rc = REWRITE_SUCCESS; + + assert( info != NULL ); + assert( op != NULL ); + assert( map != NULL ); + assert( key != NULL ); + assert( val != NULL ); + + val->bv_val = NULL; + val->bv_len = 0; + + switch ( map->lm_type ) { + case REWRITE_MAP_SUBCONTEXT: + rc = rewrite_context_apply( info, op, + ( struct rewrite_context * )map->lm_data, + key->bv_val, &val->bv_val ); + if ( val->bv_val != NULL ) { + if ( val->bv_val == key->bv_val ) { + val->bv_len = key->bv_len; + key->bv_val = NULL; + } else { + val->bv_len = strlen( val->bv_val ); + } + } + break; + + case REWRITE_MAP_SET_OP_VAR: + case REWRITE_MAP_SETW_OP_VAR: + rc = rewrite_var_set( &op->lo_vars, map->lm_name, + key->bv_val, 1 ) + ? REWRITE_SUCCESS : REWRITE_ERR; + if ( rc == REWRITE_SUCCESS ) { + if ( map->lm_type == REWRITE_MAP_SET_OP_VAR ) { + val->bv_val = strdup( "" ); + } else { + val->bv_val = strdup( key->bv_val ); + val->bv_len = key->bv_len; + } + if ( val->bv_val == NULL ) { + rc = REWRITE_ERR; + } + } + break; + + case REWRITE_MAP_GET_OP_VAR: { + struct rewrite_var *var; + + var = rewrite_var_find( op->lo_vars, map->lm_name ); + if ( var == NULL ) { + rc = REWRITE_ERR; + } else { + val->bv_val = strdup( var->lv_value.bv_val ); + val->bv_len = var->lv_value.bv_len; + if ( val->bv_val == NULL ) { + rc = REWRITE_ERR; + } + } + break; + } + + case REWRITE_MAP_SET_SESN_VAR: + case REWRITE_MAP_SETW_SESN_VAR: + if ( op->lo_cookie == NULL ) { + rc = REWRITE_ERR; + break; + } + rc = rewrite_session_var_set( info, op->lo_cookie, + map->lm_name, key->bv_val ); + if ( rc == REWRITE_SUCCESS ) { + if ( map->lm_type == REWRITE_MAP_SET_SESN_VAR ) { + val->bv_val = strdup( "" ); + } else { + val->bv_val = strdup( key->bv_val ); + val->bv_len = key->bv_len; + } + if ( val->bv_val == NULL ) { + rc = REWRITE_ERR; + } + } + break; + + case REWRITE_MAP_GET_SESN_VAR: + rc = rewrite_session_var_get( info, op->lo_cookie, + map->lm_name, val ); + break; + + case REWRITE_MAP_GET_PARAM: + rc = rewrite_param_get( info, map->lm_name, val ); + break; + + case REWRITE_MAP_BUILTIN: { + struct rewrite_builtin_map *bmap = map->lm_data; + + if ( bmap->lb_mapper && bmap->lb_mapper->rm_apply ) + rc = bmap->lb_mapper->rm_apply( bmap->lb_private, key->bv_val, + val ); + else + rc = REWRITE_ERR; + break; + break; + } + + default: + rc = REWRITE_ERR; + break; + } + + return rc; +} + +void +rewrite_builtin_map_free( + void *tmp +) +{ + struct rewrite_builtin_map *map = ( struct rewrite_builtin_map * )tmp; + + assert( map != NULL ); + + if ( map->lb_mapper && map->lb_mapper->rm_destroy ) + map->lb_mapper->rm_destroy( map->lb_private ); + + free( map->lb_name ); + free( map ); +} + +int +rewrite_map_destroy( + struct rewrite_map **pmap +) +{ + struct rewrite_map *map; + + assert( pmap != NULL ); + assert( *pmap != NULL ); + + map = *pmap; + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_mutex_lock( &map->lm_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + if ( map->lm_name ) { + free( map->lm_name ); + map->lm_name = NULL; + } + + if ( map->lm_subst ) { + rewrite_subst_destroy( &map->lm_subst ); + } + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_mutex_unlock( &map->lm_mutex ); + ldap_pvt_thread_mutex_destroy( &map->lm_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + free( map ); + *pmap = NULL; + + return 0; +} + +/* ldapmap.c */ +extern const rewrite_mapper rewrite_ldap_mapper; + +const rewrite_mapper * +rewrite_mapper_find( + const char *name +) +{ + int i; + + if ( !strcasecmp( name, "ldap" )) + return &rewrite_ldap_mapper; + + for (i=0; i<num_mappers; i++) + if ( !strcasecmp( name, mappers[i]->rm_name )) + return mappers[i]; + return NULL; +} + +int +rewrite_mapper_register( + const rewrite_mapper *map +) +{ + if ( num_mappers % MAPPER_ALLOC == 0 ) { + const rewrite_mapper **mnew; + mnew = realloc( mappers, (num_mappers + MAPPER_ALLOC) * + sizeof( rewrite_mapper * )); + if ( mnew ) + mappers = mnew; + else + return -1; + } + mappers[num_mappers++] = map; + return 0; +} + +int +rewrite_mapper_unregister( + const rewrite_mapper *map +) +{ + int i; + + for (i = 0; i<num_mappers; i++) { + if ( mappers[i] == map ) { + num_mappers--; + mappers[i] = mappers[num_mappers]; + mappers[num_mappers] = NULL; + return 0; + } + } + /* not found */ + return -1; +} diff --git a/libraries/librewrite/params.c b/libraries/librewrite/params.c new file mode 100644 index 0000000..1a7f555 --- /dev/null +++ b/libraries/librewrite/params.c @@ -0,0 +1,149 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2000-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENT: + * This work was initially developed by Pierangelo Masarati for + * inclusion in OpenLDAP Software. + */ + +#include <portable.h> + +#include "rewrite-int.h" + +/* + * Defines and inits a variable with global scope + */ +int +rewrite_param_set( + struct rewrite_info *info, + const char *name, + const char *value +) +{ + struct rewrite_var *var; + int rc = REWRITE_SUCCESS; + + assert( info != NULL ); + assert( name != NULL ); + assert( value != NULL ); + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_wlock( &info->li_params_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + var = rewrite_var_find( info->li_params, name ); + if ( var != NULL ) { + assert( var->lv_value.bv_val != NULL ); + free( var->lv_value.bv_val ); + var->lv_value.bv_val = strdup( value ); + var->lv_value.bv_len = strlen( value ); + + } else { + var = rewrite_var_insert( &info->li_params, name, value ); + } + + if ( var == NULL || var->lv_value.bv_val == NULL ) { + rc = REWRITE_ERR; + } + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_wunlock( &info->li_params_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + return rc; +} + +/* + * Gets a var with global scope + */ +int +rewrite_param_get( + struct rewrite_info *info, + const char *name, + struct berval *value +) +{ + struct rewrite_var *var; + int rc = REWRITE_SUCCESS; + + assert( info != NULL ); + assert( name != NULL ); + assert( value != NULL ); + + value->bv_val = NULL; + value->bv_len = 0; + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_rlock( &info->li_params_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + var = rewrite_var_find( info->li_params, name ); + if ( var != NULL ) { + value->bv_val = strdup( var->lv_value.bv_val ); + value->bv_len = var->lv_value.bv_len; + } + + if ( var == NULL || value->bv_val == NULL ) { + rc = REWRITE_ERR; + } + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_runlock( &info->li_params_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + return REWRITE_SUCCESS; +} + +static void +rewrite_param_free( + void *tmp +) +{ + struct rewrite_var *var = ( struct rewrite_var * )tmp; + assert( var != NULL ); + + assert( var->lv_name != NULL ); + assert( var->lv_value.bv_val != NULL ); + + free( var->lv_name ); + free( var->lv_value.bv_val ); + free( var ); +} + +/* + * Destroys the parameter tree + */ +int +rewrite_param_destroy( + struct rewrite_info *info +) +{ + int count; + + assert( info != NULL ); + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_wlock( &info->li_params_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + count = avl_free( info->li_params, rewrite_param_free ); + info->li_params = NULL; + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_wunlock( &info->li_params_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + return REWRITE_SUCCESS; +} + diff --git a/libraries/librewrite/parse.c b/libraries/librewrite/parse.c new file mode 100644 index 0000000..0950499 --- /dev/null +++ b/libraries/librewrite/parse.c @@ -0,0 +1,124 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2000-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENT: + * This work was initially developed by Pierangelo Masarati for + * inclusion in OpenLDAP Software. + */ + +#include <portable.h> + +#include <stdio.h> + +#include "rewrite-int.h" + +static int +parse_line( + char **argv, + int *argc, + int maxargs, + char *buf +) +{ + char *p, *begin; + int in_quoted_field = 0, cnt = 0; + char quote = '\0'; + + for ( p = buf; isspace( (unsigned char) p[ 0 ] ); p++ ); + + if ( p[ 0 ] == '#' ) { + return 0; + } + + for ( begin = p; p[ 0 ] != '\0'; p++ ) { + if ( p[ 0 ] == '\\' && p[ 1 ] != '\0' ) { + p++; + } else if ( p[ 0 ] == '\'' || p[ 0 ] == '\"') { + if ( in_quoted_field && p[ 0 ] == quote ) { + in_quoted_field = 1 - in_quoted_field; + quote = '\0'; + p[ 0 ] = '\0'; + argv[ cnt ] = begin; + if ( ++cnt == maxargs ) { + *argc = cnt; + return 1; + } + for ( p++; isspace( (unsigned char) p[ 0 ] ); p++ ); + begin = p; + p--; + + } else if ( !in_quoted_field ) { + if ( p != begin ) { + return -1; + } + begin++; + in_quoted_field = 1 - in_quoted_field; + quote = p[ 0 ]; + } + } else if ( isspace( (unsigned char) p[ 0 ] ) && !in_quoted_field ) { + p[ 0 ] = '\0'; + argv[ cnt ] = begin; + + if ( ++cnt == maxargs ) { + *argc = cnt; + return 1; + } + + for ( p++; isspace( (unsigned char) p[ 0 ] ); p++ ); + begin = p; + p--; + } + } + + *argc = cnt; + + return 1; +} + +int +rewrite_read( + FILE *fin, + struct rewrite_info *info +) +{ + char buf[ 1024 ]; + char *argv[11]; + int argc, lineno; + + /* + * Empty rule at the beginning of the context + */ + + for ( lineno = 0; fgets( buf, sizeof( buf ), fin ); lineno++ ) { + switch ( parse_line( argv, &argc, sizeof( argv ) - 1, buf ) ) { + case -1: + return REWRITE_ERR; + case 0: + break; + case 1: + if ( strncasecmp( argv[ 0 ], "rewrite", 7 ) == 0 ) { + int rc; + rc = rewrite_parse( info, "file", lineno, + argc, argv ); + if ( rc != REWRITE_SUCCESS ) { + return rc; + } + } + break; + } + } + + return REWRITE_SUCCESS; +} + diff --git a/libraries/librewrite/rewrite-int.h b/libraries/librewrite/rewrite-int.h new file mode 100644 index 0000000..4c7bf56 --- /dev/null +++ b/libraries/librewrite/rewrite-int.h @@ -0,0 +1,627 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2000-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENT: + * This work was initially developed by Pierangelo Masarati for + * inclusion in OpenLDAP Software. + */ + +#ifndef REWRITE_INT_H +#define REWRITE_INT_H + +/* + * These are required by every file of the library, so they're included here + */ +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/syslog.h> +#include <ac/regex.h> +#include <ac/socket.h> +#include <ac/unistd.h> +#include <ac/ctype.h> + +#include <lber.h> +#include <ldap.h> +#define LDAP_DEFINE_LDAP_DEBUG +#include <ldap_log.h> +#include <lutil.h> +#include <avl.h> + +#include <rewrite.h> + +#define malloc(x) ber_memalloc(x) +#define calloc(x,y) ber_memcalloc(x,y) +#define realloc(x,y) ber_memrealloc(x,y) +#define free(x) ber_memfree(x) +#undef strdup +#define strdup(x) ber_strdup(x) + +/* Uncomment to use ldap pvt threads */ +#define USE_REWRITE_LDAP_PVT_THREADS +#include <ldap_pvt_thread.h> + +/* + * For details, see RATIONALE. + */ + +#define REWRITE_MAX_MATCH 11 /* 0: overall string; 1-9: submatches */ +#define REWRITE_MAX_PASSES 100 + +/* + * Submatch escape char + */ +/* the '\' conflicts with slapd.conf parsing */ +/* #define REWRITE_SUBMATCH_ESCAPE '\\' */ +#define REWRITE_SUBMATCH_ESCAPE_ORIG '%' +#define REWRITE_SUBMATCH_ESCAPE '$' +#define IS_REWRITE_SUBMATCH_ESCAPE(c) \ + ((c) == REWRITE_SUBMATCH_ESCAPE || (c) == REWRITE_SUBMATCH_ESCAPE_ORIG) + +/* + * REGEX flags + */ + +#define REWRITE_FLAG_HONORCASE 'C' +#define REWRITE_FLAG_BASICREGEX 'R' + +/* + * Action flags + */ +#define REWRITE_FLAG_EXECONCE ':' +#define REWRITE_FLAG_STOP '@' +#define REWRITE_FLAG_UNWILLING '#' +#define REWRITE_FLAG_GOTO 'G' /* requires an arg */ +#define REWRITE_FLAG_USER 'U' /* requires an arg */ +#define REWRITE_FLAG_MAX_PASSES 'M' /* requires an arg */ +#define REWRITE_FLAG_IGNORE_ERR 'I' + +/* + * Map operators + */ +#define REWRITE_OPERATOR_SUBCONTEXT '>' +#define REWRITE_OPERATOR_COMMAND '|' +#define REWRITE_OPERATOR_VARIABLE_SET '&' +#define REWRITE_OPERATOR_VARIABLE_GET '*' +#define REWRITE_OPERATOR_PARAM_GET '$' + + +/*********** + * PRIVATE * + ***********/ + +/* + * Action + */ +struct rewrite_action { + struct rewrite_action *la_next; + +#define REWRITE_ACTION_STOP 0x0001 +#define REWRITE_ACTION_UNWILLING 0x0002 +#define REWRITE_ACTION_GOTO 0x0003 +#define REWRITE_ACTION_IGNORE_ERR 0x0004 +#define REWRITE_ACTION_USER 0x0005 + int la_type; + void *la_args; +}; + +/* + * Map + */ +struct rewrite_map { + + /* + * Legacy stuff + */ +#define REWRITE_MAP_XFILEMAP 0x0001 /* Rough implementation! */ +#define REWRITE_MAP_XPWDMAP 0x0002 /* uid -> gecos */ +#define REWRITE_MAP_XLDAPMAP 0x0003 /* Not implemented yet! */ + + /* + * Maps with args + */ +#define REWRITE_MAP_SUBCONTEXT 0x0101 + +#define REWRITE_MAP_SET_OP_VAR 0x0102 +#define REWRITE_MAP_SETW_OP_VAR 0x0103 +#define REWRITE_MAP_GET_OP_VAR 0x0104 +#define REWRITE_MAP_SET_SESN_VAR 0x0105 +#define REWRITE_MAP_SETW_SESN_VAR 0x0106 +#define REWRITE_MAP_GET_SESN_VAR 0x0107 +#define REWRITE_MAP_GET_PARAM 0x0108 +#define REWRITE_MAP_BUILTIN 0x0109 + int lm_type; + + char *lm_name; + void *lm_data; + + /* + * Old maps store private data in _lm_args; + * new maps store the substitution pattern in _lm_subst + */ + union { + void *_lm_args; + struct rewrite_subst *_lm_subst; + } lm_union; +#define lm_args lm_union._lm_args +#define lm_subst lm_union._lm_subst + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_mutex_t lm_mutex; +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ +}; + +/* + * Builtin maps + */ +struct rewrite_builtin_map { +#define REWRITE_BUILTIN_MAP 0x0200 + int lb_type; + char *lb_name; + void *lb_private; + const rewrite_mapper *lb_mapper; + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_mutex_t lb_mutex; +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ +}; + +/* + * Submatch substitution + */ +struct rewrite_submatch { +#define REWRITE_SUBMATCH_ASIS 0x0000 +#define REWRITE_SUBMATCH_XMAP 0x0001 +#define REWRITE_SUBMATCH_MAP_W_ARG 0x0002 + int ls_type; + struct rewrite_map *ls_map; + int ls_submatch; + /* + * The first one represents the index of the submatch in case + * the map has single submatch as argument; + * the latter represents the map argument scheme in case + * the map has substitution string argument form + */ +}; + +/* + * Pattern substitution + */ +struct rewrite_subst { + size_t lt_subs_len; + struct berval *lt_subs; + + int lt_num_submatch; + struct rewrite_submatch *lt_submatch; +}; + +/* + * Rule + */ +struct rewrite_rule { + struct rewrite_rule *lr_next; + struct rewrite_rule *lr_prev; + + char *lr_pattern; + char *lr_subststring; + char *lr_flagstring; + regex_t lr_regex; + + /* + * I was thinking about some kind of per-rule mutex, but there's + * probably no need, because rules after compilation are only read; + * however, I need to check whether regexec is reentrant ... + */ + + struct rewrite_subst *lr_subst; + +#define REWRITE_REGEX_ICASE REG_ICASE +#define REWRITE_REGEX_EXTENDED REG_EXTENDED + int lr_flags; + +#define REWRITE_RECURSE 0x0001 +#define REWRITE_EXEC_ONCE 0x0002 + int lr_mode; + int lr_max_passes; + + struct rewrite_action *lr_action; +}; + +/* + * Rewrite Context (set of rules) + */ +struct rewrite_context { + char *lc_name; + struct rewrite_context *lc_alias; + struct rewrite_rule *lc_rule; +}; + +/* + * Session + */ +struct rewrite_session { + void *ls_cookie; + Avlnode *ls_vars; +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_t ls_vars_mutex; + ldap_pvt_thread_mutex_t ls_mutex; +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + int ls_count; +}; + +/* + * Variable + */ +struct rewrite_var { + char *lv_name; + int lv_flags; + struct berval lv_value; +}; + +/* + * Operation + */ +struct rewrite_op { + int lo_num_passes; + int lo_depth; +#if 0 /* FIXME: not used anywhere! (debug? then, why strdup?) */ + char *lo_string; +#endif + char *lo_result; + Avlnode *lo_vars; + const void *lo_cookie; +}; + + +/********** + * PUBLIC * + **********/ + +/* + * Rewrite info + */ +struct rewrite_info { + Avlnode *li_context; + Avlnode *li_maps; + /* + * No global mutex because maps are read only at + * config time + */ + Avlnode *li_params; + Avlnode *li_cookies; + int li_num_cookies; + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_t li_params_mutex; + ldap_pvt_thread_rdwr_t li_cookies_mutex; +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + /* + * Default to `off'; + * use `rewriteEngine {on|off}' directive to alter + */ + int li_state; + + /* + * Defaults to REWRITE_MAXPASSES; + * use `rewriteMaxPasses numPasses' directive to alter + */ +#define REWRITE_MAXPASSES 100 + int li_max_passes; + int li_max_passes_per_rule; + + /* + * Behavior in case a NULL or non-existent context is required + */ + int li_rewrite_mode; +}; + +/*********** + * PRIVATE * + ***********/ + +LDAP_REWRITE_V (struct rewrite_context*) rewrite_int_curr_context; + +/* + * Maps + */ + +/* + * Parses a map (also in legacy 'x' version) + */ +LDAP_REWRITE_F (struct rewrite_map *) +rewrite_map_parse( + struct rewrite_info *info, + const char *s, + const char **end +); + +LDAP_REWRITE_F (struct rewrite_map *) +rewrite_xmap_parse( + struct rewrite_info *info, + const char *s, + const char **end +); + +/* + * Resolves key in val by means of map (also in legacy 'x' version) + */ +LDAP_REWRITE_F (int) +rewrite_map_apply( + struct rewrite_info *info, + struct rewrite_op *op, + struct rewrite_map *map, + struct berval *key, + struct berval *val +); + +LDAP_REWRITE_F (int) +rewrite_xmap_apply( + struct rewrite_info *info, + struct rewrite_op *op, + struct rewrite_map *map, + struct berval *key, + struct berval *val +); + +LDAP_REWRITE_F (int) +rewrite_map_destroy( + struct rewrite_map **map +); + +LDAP_REWRITE_F (int) +rewrite_xmap_destroy( + struct rewrite_map **map +); + +LDAP_REWRITE_F (void) +rewrite_builtin_map_free( + void *map +); +/* + * Submatch substitution + */ + +/* + * Compiles a substitution pattern + */ +LDAP_REWRITE_F (struct rewrite_subst *) +rewrite_subst_compile( + struct rewrite_info *info, + const char *result +); + +/* + * Substitutes a portion of rewritten string according to substitution + * pattern using submatches + */ +LDAP_REWRITE_F (int) +rewrite_subst_apply( + struct rewrite_info *info, + struct rewrite_op *op, + struct rewrite_subst *subst, + const char *string, + const regmatch_t *match, + struct berval *val +); + +LDAP_REWRITE_F (int) +rewrite_subst_destroy( + struct rewrite_subst **subst +); + + +/* + * Rules + */ + +/* + * Compiles the rule and appends it at the running context + */ +LDAP_REWRITE_F (int) +rewrite_rule_compile( + struct rewrite_info *info, + struct rewrite_context *context, + const char *pattern, + const char *result, + const char *flagstring +); + +/* + * Rewrites string according to rule; may return: + * REWRITE_REGEXEC_OK: fine; if *result != NULL rule matched + * and rewrite succeeded. + * REWRITE_REGEXEC_STOP: fine, rule matched; stop processing + * following rules + * REWRITE_REGEXEC_UNWILL: rule matched; force 'unwilling to perform' + * REWRITE_REGEXEC_ERR: an error occurred + */ +LDAP_REWRITE_F (int) +rewrite_rule_apply( + struct rewrite_info *info, + struct rewrite_op *op, + struct rewrite_rule *rule, + const char *string, + char **result +); + +LDAP_REWRITE_F (int) +rewrite_rule_destroy( + struct rewrite_rule **rule +); + +/* + * Sessions + */ + +/* + * Fetches a struct rewrite_session + */ +LDAP_REWRITE_F (struct rewrite_session *) +rewrite_session_find( + struct rewrite_info *info, + const void *cookie +); + +/* + * Defines and inits a variable with session scope + */ +LDAP_REWRITE_F (int) +rewrite_session_var_set_f( + struct rewrite_info *info, + const void *cookie, + const char *name, + const char *value, + int flags +); + +/* + * Gets a var with session scope + */ +LDAP_REWRITE_F (int) +rewrite_session_var_get( + struct rewrite_info *info, + const void *cookie, + const char *name, + struct berval *val +); + +/* + * Deletes a session + */ +LDAP_REWRITE_F (int) +rewrite_session_delete( + struct rewrite_info *info, + const void *cookie +); + +/* + * Destroys the cookie tree + */ +LDAP_REWRITE_F (int) +rewrite_session_destroy( + struct rewrite_info *info +); + + +/* + * Vars + */ + +/* + * Finds a var + */ +LDAP_REWRITE_F (struct rewrite_var *) +rewrite_var_find( + Avlnode *tree, + const char *name +); + +/* + * Replaces the value of a variable + */ +LDAP_REWRITE_F (int) +rewrite_var_replace( + struct rewrite_var *var, + const char *value, + int flags +); + +/* + * Inserts a newly created var + */ +LDAP_REWRITE_F (struct rewrite_var *) +rewrite_var_insert_f( + Avlnode **tree, + const char *name, + const char *value, + int flags +); + +#define rewrite_var_insert(tree, name, value) \ + rewrite_var_insert_f((tree), (name), (value), \ + REWRITE_VAR_UPDATE|REWRITE_VAR_COPY_NAME|REWRITE_VAR_COPY_VALUE) + +/* + * Sets/inserts a var + */ +LDAP_REWRITE_F (struct rewrite_var *) +rewrite_var_set_f( + Avlnode **tree, + const char *name, + const char *value, + int flags +); + +#define rewrite_var_set(tree, name, value, insert) \ + rewrite_var_set_f((tree), (name), (value), \ + REWRITE_VAR_UPDATE|REWRITE_VAR_COPY_NAME|REWRITE_VAR_COPY_VALUE|((insert)? REWRITE_VAR_INSERT : 0)) + +/* + * Deletes a var tree + */ +LDAP_REWRITE_F (int) +rewrite_var_delete( + Avlnode *tree +); + + +/* + * Contexts + */ + +/* + * Finds the context named rewriteContext in the context tree + */ +LDAP_REWRITE_F (struct rewrite_context *) +rewrite_context_find( + struct rewrite_info *info, + const char *rewriteContext +); + +/* + * Creates a new context called rewriteContext and stores in into the tree + */ +LDAP_REWRITE_F (struct rewrite_context *) +rewrite_context_create( + struct rewrite_info *info, + const char *rewriteContext +); + +/* + * Rewrites string according to context; may return: + * OK: fine; if *result != NULL rule matched and rewrite succeeded. + * STOP: fine, rule matched; stop processing following rules + * UNWILL: rule matched; force 'unwilling to perform' + */ +LDAP_REWRITE_F (int) +rewrite_context_apply( + struct rewrite_info *info, + struct rewrite_op *op, + struct rewrite_context *context, + const char *string, + char **result +); + +LDAP_REWRITE_F (int) +rewrite_context_destroy( + struct rewrite_context **context +); + +LDAP_REWRITE_F (void) +rewrite_context_free( + void *tmp +); + +#endif /* REWRITE_INT_H */ + diff --git a/libraries/librewrite/rewrite-map.h b/libraries/librewrite/rewrite-map.h new file mode 100644 index 0000000..93bc38b --- /dev/null +++ b/libraries/librewrite/rewrite-map.h @@ -0,0 +1,32 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2000-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENT: + * This work was initially developed by Pierangelo Masarati for + * inclusion in OpenLDAP Software. + */ + +#ifndef MAP_H +#define MAP_H + +/* + * Retrieves a builtin map + */ +LDAP_REWRITE_F (struct rewrite_builtin_map *) +rewrite_builtin_map_find( + struct rewrite_info *info, + const char *name +); + +#endif /* MAP_H */ diff --git a/libraries/librewrite/rewrite.c b/libraries/librewrite/rewrite.c new file mode 100644 index 0000000..4c14b7f --- /dev/null +++ b/libraries/librewrite/rewrite.c @@ -0,0 +1,195 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2000-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENT: + * This work was initially developed by Pierangelo Masarati for + * inclusion in OpenLDAP Software. + */ + +#include <portable.h> + +#include <ac/stdlib.h> +#include <ac/string.h> +#include <ac/syslog.h> +#include <ac/regex.h> +#include <ac/socket.h> +#include <ac/unistd.h> +#include <ac/ctype.h> +#include <ac/string.h> +#include <stdio.h> + +#include <rewrite.h> +#include <lutil.h> +#include <ldap.h> + +int ldap_debug; +int ldap_syslog; +int ldap_syslog_level; + +static void +apply( + FILE *fin, + const char *rewriteContext, + const char *arg +) +{ + struct rewrite_info *info; + char *string, *sep, *result = NULL; + int rc; + void *cookie = &info; + + info = rewrite_info_init( REWRITE_MODE_ERR ); + + if ( rewrite_read( fin, info ) != 0 ) { + exit( EXIT_FAILURE ); + } + + rewrite_param_set( info, "prog", "rewrite" ); + + rewrite_session_init( info, cookie ); + + string = (char *)arg; + for ( sep = strchr( rewriteContext, ',' ); + rewriteContext != NULL; + rewriteContext = sep, + sep ? sep = strchr( rewriteContext, ',' ) : NULL ) + { + char *errmsg = ""; + + if ( sep != NULL ) { + sep[ 0 ] = '\0'; + sep++; + } + /* rc = rewrite( info, rewriteContext, string, &result ); */ + rc = rewrite_session( info, rewriteContext, string, + cookie, &result ); + + switch ( rc ) { + case REWRITE_REGEXEC_OK: + errmsg = "ok"; + break; + + case REWRITE_REGEXEC_ERR: + errmsg = "error"; + break; + + case REWRITE_REGEXEC_STOP: + errmsg = "stop"; + break; + + case REWRITE_REGEXEC_UNWILLING: + errmsg = "unwilling to perform"; + break; + + default: + if (rc >= REWRITE_REGEXEC_USER) { + errmsg = "user-defined"; + } else { + errmsg = "unknown"; + } + break; + } + + fprintf( stdout, "%s -> %s [%d:%s]\n", string, + ( result ? result : "(null)" ), + rc, errmsg ); + if ( result == NULL ) { + break; + } + if ( string != arg && string != result ) { + free( string ); + } + string = result; + } + + if ( result && result != arg ) { + free( result ); + } + + rewrite_session_delete( info, cookie ); + + rewrite_info_delete( &info ); +} + +int +main( int argc, char *argv[] ) +{ + FILE *fin = NULL; + char *rewriteContext = REWRITE_DEFAULT_CONTEXT; + int debug = 0; + + while ( 1 ) { + int opt = getopt( argc, argv, "d:f:hr:" ); + + if ( opt == EOF ) { + break; + } + + switch ( opt ) { + case 'd': + if ( lutil_atoi( &debug, optarg ) != 0 ) { + fprintf( stderr, "illegal log level '%s'\n", + optarg ); + exit( EXIT_FAILURE ); + } + break; + + case 'f': + fin = fopen( optarg, "r" ); + if ( fin == NULL ) { + fprintf( stderr, "unable to open file '%s'\n", + optarg ); + exit( EXIT_FAILURE ); + } + break; + + case 'h': + fprintf( stderr, + "usage: rewrite [options] string\n" + "\n" + "\t\t-f file\t\tconfiguration file\n" + "\t\t-r rule[s]\tlist of comma-separated rules\n" + "\n" + "\tsyntax:\n" + "\t\trewriteEngine\t{on|off}\n" + "\t\trewriteContext\tcontextName [alias aliasedContextName]\n" + "\t\trewriteRule\tpattern subst [flags]\n" + "\n" + ); + exit( EXIT_SUCCESS ); + + case 'r': + rewriteContext = optarg; + break; + } + } + + if ( debug != 0 ) { + ber_set_option(NULL, LBER_OPT_DEBUG_LEVEL, &debug); + ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, &debug); + } + + if ( optind >= argc ) { + return -1; + } + + apply( ( fin ? fin : stdin ), rewriteContext, argv[ optind ] ); + + if ( fin ) { + fclose( fin ); + } + + return 0; +} + diff --git a/libraries/librewrite/rule.c b/libraries/librewrite/rule.c new file mode 100644 index 0000000..c9eb2b4 --- /dev/null +++ b/libraries/librewrite/rule.c @@ -0,0 +1,510 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2000-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENT: + * This work was initially developed by Pierangelo Masarati for + * inclusion in OpenLDAP Software. + */ + +#include <portable.h> + +#include "rewrite-int.h" + +/* + * Appends a rule to the double linked list of rules + * Helper for rewrite_rule_compile + */ +static int +append_rule( + struct rewrite_context *context, + struct rewrite_rule *rule +) +{ + struct rewrite_rule *r; + + assert( context != NULL ); + assert( context->lc_rule != NULL ); + assert( rule != NULL ); + + for ( r = context->lc_rule; r->lr_next != NULL; r = r->lr_next ); + r->lr_next = rule; + rule->lr_prev = r; + + return REWRITE_SUCCESS; +} + +/* + * Appends an action to the linked list of actions + * Helper for rewrite_rule_compile + */ +static int +append_action( + struct rewrite_action **pbase, + struct rewrite_action *action +) +{ + struct rewrite_action **pa; + + assert( pbase != NULL ); + assert( action != NULL ); + + for ( pa = pbase; *pa != NULL; pa = &(*pa)->la_next ); + *pa = action; + + return REWRITE_SUCCESS; +} + +static int +destroy_action( + struct rewrite_action **paction +) +{ + struct rewrite_action *action; + + assert( paction != NULL ); + assert( *paction != NULL ); + + action = *paction; + + /* do something */ + switch ( action->la_type ) { + case REWRITE_FLAG_GOTO: + case REWRITE_FLAG_USER: { + int *pi = (int *)action->la_args; + + if ( pi ) { + free( pi ); + } + break; + } + + default: + break; + } + + free( action ); + *paction = NULL; + + return 0; +} + +static void +destroy_actions( + struct rewrite_action *paction +) +{ + struct rewrite_action *next; + + for (; paction; paction = next) { + next = paction->la_next; + destroy_action( &paction ); + } +} + +/* + */ +int +rewrite_rule_compile( + struct rewrite_info *info, + struct rewrite_context *context, + const char *pattern, + const char *result, + const char *flagstring +) +{ + int flags = REWRITE_REGEX_EXTENDED | REWRITE_REGEX_ICASE; + int mode = REWRITE_RECURSE; + int max_passes; + + struct rewrite_rule *rule = NULL; + struct rewrite_subst *subst = NULL; + struct rewrite_action *action = NULL, *first_action = NULL; + + const char *p; + + assert( info != NULL ); + assert( context != NULL ); + assert( pattern != NULL ); + assert( result != NULL ); + /* + * A null flagstring should be allowed + */ + + max_passes = info->li_max_passes_per_rule; + + /* + * Take care of substitution string + */ + subst = rewrite_subst_compile( info, result ); + if ( subst == NULL ) { + return REWRITE_ERR; + } + + /* + * Take care of flags + */ + for ( p = flagstring; p[ 0 ] != '\0'; p++ ) { + switch( p[ 0 ] ) { + + /* + * REGEX flags + */ + case REWRITE_FLAG_HONORCASE: /* 'C' */ + /* + * Honor case (default is case insensitive) + */ + flags &= ~REWRITE_REGEX_ICASE; + break; + + case REWRITE_FLAG_BASICREGEX: /* 'R' */ + /* + * Use POSIX Basic Regular Expression syntax + * instead of POSIX Extended Regular Expression + * syntax (default) + */ + flags &= ~REWRITE_REGEX_EXTENDED; + break; + + /* + * Execution mode flags + */ + case REWRITE_FLAG_EXECONCE: /* ':' */ + /* + * Apply rule once only + */ + mode &= ~REWRITE_RECURSE; + mode |= REWRITE_EXEC_ONCE; + break; + + /* + * Special action flags + */ + case REWRITE_FLAG_STOP: /* '@' */ + /* + * Bail out after applying rule + */ + action = calloc( sizeof( struct rewrite_action ), 1 ); + if ( action == NULL ) { + goto fail; + } + + action->la_type = REWRITE_ACTION_STOP; + break; + + case REWRITE_FLAG_UNWILLING: /* '#' */ + /* + * Matching objs will be marked as gone! + */ + action = calloc( sizeof( struct rewrite_action ), 1 ); + if ( action == NULL ) { + goto fail; + } + + mode &= ~REWRITE_RECURSE; + mode |= REWRITE_EXEC_ONCE; + action->la_type = REWRITE_ACTION_UNWILLING; + break; + + case REWRITE_FLAG_GOTO: /* 'G' */ + /* + * After applying rule, jump N rules + */ + + case REWRITE_FLAG_USER: { /* 'U' */ + /* + * After applying rule, return user-defined + * error code + */ + char *next = NULL; + int *d; + + if ( p[ 1 ] != '{' ) { + goto fail; + } + + d = malloc( sizeof( int ) ); + if ( d == NULL ) { + goto fail; + } + + d[ 0 ] = strtol( &p[ 2 ], &next, 0 ); + if ( next == &p[ 2 ] || next[0] != '}' ) { + free( d ); + goto fail; + } + + action = calloc( sizeof( struct rewrite_action ), 1 ); + if ( action == NULL ) { + free( d ); + goto fail; + } + switch ( p[ 0 ] ) { + case REWRITE_FLAG_GOTO: + action->la_type = REWRITE_ACTION_GOTO; + break; + + case REWRITE_FLAG_USER: + action->la_type = REWRITE_ACTION_USER; + break; + + default: + assert(0); + } + + action->la_args = (void *)d; + + p = next; /* p is incremented by the for ... */ + + break; + } + + case REWRITE_FLAG_MAX_PASSES: { /* 'U' */ + /* + * Set the number of max passes per rule + */ + char *next = NULL; + + if ( p[ 1 ] != '{' ) { + goto fail; + } + + max_passes = strtol( &p[ 2 ], &next, 0 ); + if ( next == &p[ 2 ] || next[0] != '}' ) { + goto fail; + } + + if ( max_passes < 1 ) { + /* FIXME: nonsense ... */ + max_passes = 1; + } + + p = next; /* p is incremented by the for ... */ + + break; + } + + case REWRITE_FLAG_IGNORE_ERR: /* 'I' */ + /* + * Ignore errors! + */ + action = calloc( sizeof( struct rewrite_action ), 1 ); + if ( action == NULL ) { + goto fail; + } + + action->la_type = REWRITE_ACTION_IGNORE_ERR; + break; + + /* + * Other flags ... + */ + default: + /* + * Unimplemented feature (complain only) + */ + break; + } + + /* + * Stupid way to append to a list ... + */ + if ( action != NULL ) { + append_action( &first_action, action ); + action = NULL; + } + } + + /* + * Finally, rule allocation + */ + rule = calloc( sizeof( struct rewrite_rule ), 1 ); + if ( rule == NULL ) { + goto fail; + } + + /* + * REGEX compilation (luckily I don't need to take care of this ...) + */ + if ( regcomp( &rule->lr_regex, ( char * )pattern, flags ) != 0 ) { + goto fail; + } + + /* + * Just to remember them ... + */ + rule->lr_pattern = strdup( pattern ); + rule->lr_subststring = strdup( result ); + rule->lr_flagstring = strdup( flagstring ); + if ( rule->lr_pattern == NULL + || rule->lr_subststring == NULL + || rule->lr_flagstring == NULL ) + { + goto fail; + } + + /* + * Load compiled data into rule + */ + rule->lr_subst = subst; + + /* + * Set various parameters + */ + rule->lr_flags = flags; /* don't really need any longer ... */ + rule->lr_mode = mode; + rule->lr_max_passes = max_passes; + rule->lr_action = first_action; + + /* + * Append rule at the end of the rewrite context + */ + append_rule( context, rule ); + + return REWRITE_SUCCESS; + +fail: + if ( rule ) { + if ( rule->lr_pattern ) free( rule->lr_pattern ); + if ( rule->lr_subststring ) free( rule->lr_subststring ); + if ( rule->lr_flagstring ) free( rule->lr_flagstring ); + free( rule ); + } + destroy_actions( first_action ); + free( subst ); + return REWRITE_ERR; +} + +/* + * Rewrites string according to rule; may return: + * OK: fine; if *result != NULL rule matched and rewrite succeeded. + * STOP: fine, rule matched; stop processing following rules + * UNWILL: rule matched; force 'unwilling to perform' + */ +int +rewrite_rule_apply( + struct rewrite_info *info, + struct rewrite_op *op, + struct rewrite_rule *rule, + const char *arg, + char **result + ) +{ + size_t nmatch = REWRITE_MAX_MATCH; + regmatch_t match[ REWRITE_MAX_MATCH ]; + + int rc = REWRITE_SUCCESS; + + char *string; + int strcnt = 0; + struct berval val = { 0, NULL }; + + assert( info != NULL ); + assert( op != NULL ); + assert( rule != NULL ); + assert( arg != NULL ); + assert( result != NULL ); + + *result = NULL; + + string = (char *)arg; + + /* + * In case recursive match is required (default) + */ +recurse:; + + Debug( LDAP_DEBUG_TRACE, "==> rewrite_rule_apply" + " rule='%s' string='%s' [%d pass(es)]\n", + rule->lr_pattern, string, strcnt + 1 ); + + op->lo_num_passes++; + + rc = regexec( &rule->lr_regex, string, nmatch, match, 0 ); + if ( rc != 0 ) { + if ( *result == NULL && string != arg ) { + free( string ); + } + + /* + * No match is OK; *result = NULL means no match + */ + return REWRITE_REGEXEC_OK; + } + + rc = rewrite_subst_apply( info, op, rule->lr_subst, string, + match, &val ); + + *result = val.bv_val; + val.bv_val = NULL; + if ( string != arg ) { + free( string ); + string = NULL; + } + + if ( rc != REWRITE_REGEXEC_OK ) { + return rc; + } + + if ( ( rule->lr_mode & REWRITE_RECURSE ) == REWRITE_RECURSE + && op->lo_num_passes < info->li_max_passes + && ++strcnt < rule->lr_max_passes ) { + string = *result; + + goto recurse; + } + + return REWRITE_REGEXEC_OK; +} + +int +rewrite_rule_destroy( + struct rewrite_rule **prule + ) +{ + struct rewrite_rule *rule; + + assert( prule != NULL ); + assert( *prule != NULL ); + + rule = *prule; + + if ( rule->lr_pattern ) { + free( rule->lr_pattern ); + rule->lr_pattern = NULL; + } + + if ( rule->lr_subststring ) { + free( rule->lr_subststring ); + rule->lr_subststring = NULL; + } + + if ( rule->lr_flagstring ) { + free( rule->lr_flagstring ); + rule->lr_flagstring = NULL; + } + + if ( rule->lr_subst ) { + rewrite_subst_destroy( &rule->lr_subst ); + } + + regfree( &rule->lr_regex ); + + destroy_actions( rule->lr_action ); + + free( rule ); + *prule = NULL; + + return 0; +} + diff --git a/libraries/librewrite/session.c b/libraries/librewrite/session.c new file mode 100644 index 0000000..6e2e048 --- /dev/null +++ b/libraries/librewrite/session.c @@ -0,0 +1,423 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2000-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENT: + * This work was initially developed by Pierangelo Masarati for + * inclusion in OpenLDAP Software. + */ + +#include <portable.h> + +#include "rewrite-int.h" + +/* + * Compares two cookies + */ +static int +rewrite_cookie_cmp( + const void *c1, + const void *c2 +) +{ + const struct rewrite_session *s1, *s2; + + s1 = ( const struct rewrite_session * )c1; + s2 = ( const struct rewrite_session * )c2; + + assert( s1 != NULL ); + assert( s2 != NULL ); + assert( s1->ls_cookie != NULL ); + assert( s2->ls_cookie != NULL ); + + return ( ( s1->ls_cookie < s2->ls_cookie ) ? -1 : + ( ( s1->ls_cookie > s2->ls_cookie ) ? 1 : 0 ) ); +} + +/* + * Duplicate cookies? + */ +static int +rewrite_cookie_dup( + void *c1, + void *c2 +) +{ + struct rewrite_session *s1, *s2; + + s1 = ( struct rewrite_session * )c1; + s2 = ( struct rewrite_session * )c2; + + assert( s1 != NULL ); + assert( s2 != NULL ); + assert( s1->ls_cookie != NULL ); + assert( s2->ls_cookie != NULL ); + + assert( s1->ls_cookie != s2->ls_cookie ); + + return ( ( s1->ls_cookie == s2->ls_cookie ) ? -1 : 0 ); +} + +/* + * Inits a session + */ +struct rewrite_session * +rewrite_session_init( + struct rewrite_info *info, + const void *cookie +) +{ + struct rewrite_session *session, tmp; + int rc; + + assert( info != NULL ); + assert( cookie != NULL ); + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_wlock( &info->li_cookies_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + tmp.ls_cookie = ( void * )cookie; + session = ( struct rewrite_session * )avl_find( info->li_cookies, + ( caddr_t )&tmp, rewrite_cookie_cmp ); + if ( session ) { + session->ls_count++; +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_wunlock( &info->li_cookies_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + return session; + } + + session = calloc( sizeof( struct rewrite_session ), 1 ); + if ( session == NULL ) { +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_wunlock( &info->li_cookies_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + return NULL; + } + session->ls_cookie = ( void * )cookie; + session->ls_count = 1; + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + if ( ldap_pvt_thread_mutex_init( &session->ls_mutex ) ) { + free( session ); + ldap_pvt_thread_rdwr_wunlock( &info->li_cookies_mutex ); + return NULL; + } + if ( ldap_pvt_thread_rdwr_init( &session->ls_vars_mutex ) ) { + ldap_pvt_thread_mutex_destroy( &session->ls_mutex ); + free( session ); + ldap_pvt_thread_rdwr_wunlock( &info->li_cookies_mutex ); + return NULL; + } +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + rc = avl_insert( &info->li_cookies, ( caddr_t )session, + rewrite_cookie_cmp, rewrite_cookie_dup ); + info->li_num_cookies++; + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_wunlock( &info->li_cookies_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + if ( rc != 0 ) { +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_destroy( &session->ls_vars_mutex ); + ldap_pvt_thread_mutex_destroy( &session->ls_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + free( session ); + return NULL; + } + + return session; +} + +/* + * Fetches a session + */ +struct rewrite_session * +rewrite_session_find( + struct rewrite_info *info, + const void *cookie +) +{ + struct rewrite_session *session, tmp; + + assert( info != NULL ); + assert( cookie != NULL ); + + tmp.ls_cookie = ( void * )cookie; +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_rlock( &info->li_cookies_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + session = ( struct rewrite_session * )avl_find( info->li_cookies, + ( caddr_t )&tmp, rewrite_cookie_cmp ); +#ifdef USE_REWRITE_LDAP_PVT_THREADS + if ( session ) { + ldap_pvt_thread_mutex_lock( &session->ls_mutex ); + session->ls_count++; + } + ldap_pvt_thread_rdwr_runlock( &info->li_cookies_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + return session; +} + +/* + * Returns a session + */ +void +rewrite_session_return( + struct rewrite_info *info, + struct rewrite_session *session +) +{ + assert( session != NULL ); + session->ls_count--; + ldap_pvt_thread_mutex_unlock( &session->ls_mutex ); +} + +/* + * Defines and inits a var with session scope + */ +int +rewrite_session_var_set_f( + struct rewrite_info *info, + const void *cookie, + const char *name, + const char *value, + int flags +) +{ + struct rewrite_session *session; + struct rewrite_var *var; + + assert( info != NULL ); + assert( cookie != NULL ); + assert( name != NULL ); + assert( value != NULL ); + + session = rewrite_session_find( info, cookie ); + if ( session == NULL ) { + session = rewrite_session_init( info, cookie ); + if ( session == NULL ) { + return REWRITE_ERR; + } + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_mutex_lock( &session->ls_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + } + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_wlock( &session->ls_vars_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + var = rewrite_var_find( session->ls_vars, name ); + if ( var != NULL ) { + assert( var->lv_value.bv_val != NULL ); + + (void)rewrite_var_replace( var, value, flags ); + + } else { + var = rewrite_var_insert_f( &session->ls_vars, name, value, flags ); + if ( var == NULL ) { +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_wunlock( &session->ls_vars_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + rewrite_session_return( info, session ); + return REWRITE_ERR; + } + } + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_wunlock( &session->ls_vars_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + rewrite_session_return( info, session ); + + return REWRITE_SUCCESS; +} + +/* + * Gets a var with session scope + */ +int +rewrite_session_var_get( + struct rewrite_info *info, + const void *cookie, + const char *name, + struct berval *value +) +{ + struct rewrite_session *session; + struct rewrite_var *var; + int rc = REWRITE_SUCCESS; + + assert( info != NULL ); + assert( cookie != NULL ); + assert( name != NULL ); + assert( value != NULL ); + + value->bv_val = NULL; + value->bv_len = 0; + + if ( cookie == NULL ) { + return REWRITE_ERR; + } + + session = rewrite_session_find( info, cookie ); + if ( session == NULL ) { + return REWRITE_ERR; + } + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_rlock( &session->ls_vars_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + var = rewrite_var_find( session->ls_vars, name ); + if ( var != NULL ) { + value->bv_val = strdup( var->lv_value.bv_val ); + value->bv_len = var->lv_value.bv_len; + } + + if ( var == NULL || value->bv_val == NULL ) { + rc = REWRITE_ERR; + } + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_runlock( &session->ls_vars_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + rewrite_session_return( info, session ); + + return rc; +} + +static void +rewrite_session_clean( void *v_session ) +{ + struct rewrite_session *session = (struct rewrite_session *)v_session; + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_wlock( &session->ls_vars_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + rewrite_var_delete( session->ls_vars ); + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_wunlock( &session->ls_vars_mutex ); + ldap_pvt_thread_rdwr_destroy( &session->ls_vars_mutex ); + ldap_pvt_thread_mutex_unlock( &session->ls_mutex ); + ldap_pvt_thread_mutex_destroy( &session->ls_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ +} + +static void +rewrite_session_free( void *v_session ) +{ + struct rewrite_session *session = (struct rewrite_session *)v_session; + + ldap_pvt_thread_mutex_lock( &session->ls_mutex ); + rewrite_session_clean( v_session ); + free( v_session ); +} + +/* + * Deletes a session + */ +int +rewrite_session_delete( + struct rewrite_info *info, + const void *cookie +) +{ + struct rewrite_session *session, tmp = { 0 }; + + assert( info != NULL ); + assert( cookie != NULL ); + + session = rewrite_session_find( info, cookie ); + + if ( session == NULL ) { + return REWRITE_SUCCESS; + } + + if ( --session->ls_count > 0 ) { + rewrite_session_return( info, session ); + return REWRITE_SUCCESS; + } + + rewrite_session_clean( session ); + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_wlock( &info->li_cookies_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + assert( info->li_num_cookies > 0 ); + info->li_num_cookies--; + + /* + * There is nothing to delete in the return value + */ + tmp.ls_cookie = ( void * )cookie; + avl_delete( &info->li_cookies, ( caddr_t )&tmp, rewrite_cookie_cmp ); + + free( session ); + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_wunlock( &info->li_cookies_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + return REWRITE_SUCCESS; +} + +/* + * Destroys the cookie tree + */ +int +rewrite_session_destroy( + struct rewrite_info *info +) +{ + int count; + + assert( info != NULL ); + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_wlock( &info->li_cookies_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + /* + * Should call per-session destruction routine ... + */ + + count = avl_free( info->li_cookies, rewrite_session_free ); + info->li_cookies = NULL; + +#if 0 + fprintf( stderr, "count = %d; num_cookies = %d\n", + count, info->li_num_cookies ); +#endif + + assert( count == info->li_num_cookies ); + info->li_num_cookies = 0; + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_rdwr_wunlock( &info->li_cookies_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + return REWRITE_SUCCESS; +} + diff --git a/libraries/librewrite/subst.c b/libraries/librewrite/subst.c new file mode 100644 index 0000000..ac7f586 --- /dev/null +++ b/libraries/librewrite/subst.c @@ -0,0 +1,513 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2000-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENT: + * This work was initially developed by Pierangelo Masarati for + * inclusion in OpenLDAP Software. + */ + +#include <portable.h> + +#include "rewrite-int.h" + +/* + * Compiles a substitution pattern + */ +struct rewrite_subst * +rewrite_subst_compile( + struct rewrite_info *info, + const char *str +) +{ + size_t subs_len; + struct berval *subs = NULL, *tmps; + struct rewrite_submatch *submatch = NULL, *tmpsm; + + struct rewrite_subst *s = NULL; + + char *result, *begin, *p; + int nsub = 0, l; + + assert( info != NULL ); + assert( str != NULL ); + + result = strdup( str ); + if ( result == NULL ) { + return NULL; + } + + /* + * Take care of substitution string + */ + for ( p = begin = result, subs_len = 0; p[ 0 ] != '\0'; p++ ) { + + /* + * Keep only single escapes '%' + */ + if ( !IS_REWRITE_SUBMATCH_ESCAPE( p[ 0 ] ) ) { + continue; + } + + if ( IS_REWRITE_SUBMATCH_ESCAPE( p[ 1 ] ) ) { + /* Pull &p[1] over p, including the trailing '\0' */ + AC_MEMCPY((char *)p, &p[ 1 ], strlen( p ) ); + continue; + } + + tmps = ( struct berval * )realloc( subs, + sizeof( struct berval )*( nsub + 1 ) ); + if ( tmps == NULL ) { + goto cleanup; + } + subs = tmps; + subs[ nsub ].bv_val = NULL; + + tmpsm = ( struct rewrite_submatch * )realloc( submatch, + sizeof( struct rewrite_submatch )*( nsub + 1 ) ); + if ( tmpsm == NULL ) { + goto cleanup; + } + submatch = tmpsm; + submatch[ nsub ].ls_map = NULL; + + /* + * I think an `if l > 0' at runtime is better outside than + * inside a function call ... + */ + l = p - begin; + if ( l > 0 ) { + subs_len += l; + subs[ nsub ].bv_len = l; + subs[ nsub ].bv_val = malloc( l + 1 ); + if ( subs[ nsub ].bv_val == NULL ) { + goto cleanup; + } + AC_MEMCPY( subs[ nsub ].bv_val, begin, l ); + subs[ nsub ].bv_val[ l ] = '\0'; + } else { + subs[ nsub ].bv_val = NULL; + subs[ nsub ].bv_len = 0; + } + + /* + * Substitution pattern + */ + if ( isdigit( (unsigned char) p[ 1 ] ) ) { + int d = p[ 1 ] - '0'; + + /* + * Add a new value substitution scheme + */ + + submatch[ nsub ].ls_submatch = d; + + /* + * If there is no argument, use default + * (substitute substring as is) + */ + if ( p[ 2 ] != '{' ) { + submatch[ nsub ].ls_type = + REWRITE_SUBMATCH_ASIS; + submatch[ nsub ].ls_map = NULL; + begin = ++p + 1; + + } else { + struct rewrite_map *map; + + submatch[ nsub ].ls_type = + REWRITE_SUBMATCH_XMAP; + + map = rewrite_xmap_parse( info, + p + 3, (const char **)&begin ); + if ( map == NULL ) { + goto cleanup; + } + submatch[ nsub ].ls_map = map; + p = begin - 1; + } + + /* + * Map with args ... + */ + } else if ( p[ 1 ] == '{' ) { + struct rewrite_map *map; + + map = rewrite_map_parse( info, p + 2, + (const char **)&begin ); + if ( map == NULL ) { + goto cleanup; + } + p = begin - 1; + + /* + * Add a new value substitution scheme + */ + submatch[ nsub ].ls_type = + REWRITE_SUBMATCH_MAP_W_ARG; + submatch[ nsub ].ls_map = map; + + /* + * Escape '%' ... + */ + } else if ( p[ 1 ] == '%' ) { + AC_MEMCPY( &p[ 1 ], &p[ 2 ], strlen( &p[ 1 ] ) ); + continue; + + } else { + goto cleanup; + } + + nsub++; + } + + /* + * Last part of string + */ + tmps = (struct berval * )realloc( subs, sizeof( struct berval )*( nsub + 1 ) ); + if ( tmps == NULL ) { + /* + * XXX need to free the value subst stuff! + */ + free( subs ); + goto cleanup; + } + subs = tmps; + l = p - begin; + if ( l > 0 ) { + subs_len += l; + subs[ nsub ].bv_len = l; + subs[ nsub ].bv_val = malloc( l + 1 ); + if ( subs[ nsub ].bv_val == NULL ) { + goto cleanup; + } + AC_MEMCPY( subs[ nsub ].bv_val, begin, l ); + subs[ nsub ].bv_val[ l ] = '\0'; + } else { + subs[ nsub ].bv_val = NULL; + subs[ nsub ].bv_len = 0; + } + + s = calloc( sizeof( struct rewrite_subst ), 1 ); + if ( s == NULL ) { + goto cleanup; + } + + s->lt_subs_len = subs_len; + s->lt_subs = subs; + s->lt_num_submatch = nsub; + s->lt_submatch = submatch; + subs = NULL; + submatch = NULL; + +cleanup:; + if ( subs ) { + for ( l=0; l<nsub; l++ ) { + free( subs[nsub].bv_val ); + } + free( subs ); + } + if ( submatch ) { + for ( l=0; l<nsub; l++ ) { + free( submatch[nsub].ls_map ); + } + free( submatch ); + } + free( result ); + + return s; +} + +/* + * Copies the match referred to by submatch and fetched in string by match. + * Helper for rewrite_rule_apply. + */ +static int +submatch_copy( + struct rewrite_submatch *submatch, + const char *string, + const regmatch_t *match, + struct berval *val +) +{ + int c, l; + const char *s; + + assert( submatch != NULL ); + assert( submatch->ls_type == REWRITE_SUBMATCH_ASIS + || submatch->ls_type == REWRITE_SUBMATCH_XMAP ); + assert( string != NULL ); + assert( match != NULL ); + assert( val != NULL ); + assert( val->bv_val == NULL ); + + c = submatch->ls_submatch; + s = string + match[ c ].rm_so; + l = match[ c ].rm_eo - match[ c ].rm_so; + + val->bv_len = l; + val->bv_val = malloc( l + 1 ); + if ( val->bv_val == NULL ) { + return REWRITE_ERR; + } + + AC_MEMCPY( val->bv_val, s, l ); + val->bv_val[ l ] = '\0'; + + return REWRITE_SUCCESS; +} + +/* + * Substitutes a portion of rewritten string according to substitution + * pattern using submatches + */ +int +rewrite_subst_apply( + struct rewrite_info *info, + struct rewrite_op *op, + struct rewrite_subst *subst, + const char *string, + const regmatch_t *match, + struct berval *val +) +{ + struct berval *submatch = NULL; + char *res = NULL; + int n = 0, l, cl; + int rc = REWRITE_REGEXEC_OK; + + assert( info != NULL ); + assert( op != NULL ); + assert( subst != NULL ); + assert( string != NULL ); + assert( match != NULL ); + assert( val != NULL ); + + assert( val->bv_val == NULL ); + + val->bv_val = NULL; + val->bv_len = 0; + + /* + * Prepare room for submatch expansion + */ + if ( subst->lt_num_submatch > 0 ) { + submatch = calloc( sizeof( struct berval ), + subst->lt_num_submatch ); + if ( submatch == NULL ) { + return REWRITE_REGEXEC_ERR; + } + } + + /* + * Resolve submatches (simple subst, map expansion and so). + */ + for ( n = 0, l = 0; n < subst->lt_num_submatch; n++ ) { + struct berval key = { 0, NULL }; + + submatch[ n ].bv_val = NULL; + + /* + * Get key + */ + switch ( subst->lt_submatch[ n ].ls_type ) { + case REWRITE_SUBMATCH_ASIS: + case REWRITE_SUBMATCH_XMAP: + rc = submatch_copy( &subst->lt_submatch[ n ], + string, match, &key ); + if ( rc != REWRITE_SUCCESS ) { + rc = REWRITE_REGEXEC_ERR; + goto cleanup; + } + break; + + case REWRITE_SUBMATCH_MAP_W_ARG: + switch ( subst->lt_submatch[ n ].ls_map->lm_type ) { + case REWRITE_MAP_GET_OP_VAR: + case REWRITE_MAP_GET_SESN_VAR: + case REWRITE_MAP_GET_PARAM: + rc = REWRITE_SUCCESS; + break; + + default: + rc = rewrite_subst_apply( info, op, + subst->lt_submatch[ n ].ls_map->lm_subst, + string, match, &key); + } + + if ( rc != REWRITE_SUCCESS ) { + goto cleanup; + } + break; + + default: + Debug( LDAP_DEBUG_ANY, "Not Implemented\n", 0, 0, 0 ); + rc = REWRITE_ERR; + break; + } + + if ( rc != REWRITE_SUCCESS ) { + rc = REWRITE_REGEXEC_ERR; + goto cleanup; + } + + /* + * Resolve key + */ + switch ( subst->lt_submatch[ n ].ls_type ) { + case REWRITE_SUBMATCH_ASIS: + submatch[ n ] = key; + rc = REWRITE_SUCCESS; + break; + + case REWRITE_SUBMATCH_XMAP: + rc = rewrite_xmap_apply( info, op, + subst->lt_submatch[ n ].ls_map, + &key, &submatch[ n ] ); + free( key.bv_val ); + key.bv_val = NULL; + break; + + case REWRITE_SUBMATCH_MAP_W_ARG: + rc = rewrite_map_apply( info, op, + subst->lt_submatch[ n ].ls_map, + &key, &submatch[ n ] ); + free( key.bv_val ); + key.bv_val = NULL; + break; + + default: + /* + * When implemented, this might return the + * exit status of a rewrite context, + * which may include a stop, or an + * unwilling to perform + */ + rc = REWRITE_ERR; + break; + } + + if ( rc != REWRITE_SUCCESS ) { + rc = REWRITE_REGEXEC_ERR; + goto cleanup; + } + + /* + * Increment the length of the resulting string + */ + l += submatch[ n ].bv_len; + } + + /* + * Alloc result buffer + */ + l += subst->lt_subs_len; + res = malloc( l + 1 ); + if ( res == NULL ) { + rc = REWRITE_REGEXEC_ERR; + goto cleanup; + } + + /* + * Apply submatches (possibly resolved thru maps) + */ + for ( n = 0, cl = 0; n < subst->lt_num_submatch; n++ ) { + if ( subst->lt_subs[ n ].bv_val != NULL ) { + AC_MEMCPY( res + cl, subst->lt_subs[ n ].bv_val, + subst->lt_subs[ n ].bv_len ); + cl += subst->lt_subs[ n ].bv_len; + } + AC_MEMCPY( res + cl, submatch[ n ].bv_val, + submatch[ n ].bv_len ); + cl += submatch[ n ].bv_len; + } + if ( subst->lt_subs[ n ].bv_val != NULL ) { + AC_MEMCPY( res + cl, subst->lt_subs[ n ].bv_val, + subst->lt_subs[ n ].bv_len ); + cl += subst->lt_subs[ n ].bv_len; + } + res[ cl ] = '\0'; + + val->bv_val = res; + val->bv_len = l; + +cleanup:; + if ( submatch ) { + for ( ; --n >= 0; ) { + if ( submatch[ n ].bv_val ) { + free( submatch[ n ].bv_val ); + } + } + free( submatch ); + } + + return rc; +} + +/* + * frees data + */ +int +rewrite_subst_destroy( + struct rewrite_subst **psubst +) +{ + int n; + struct rewrite_subst *subst; + + assert( psubst != NULL ); + assert( *psubst != NULL ); + + subst = *psubst; + + for ( n = 0; n < subst->lt_num_submatch; n++ ) { + if ( subst->lt_subs[ n ].bv_val ) { + free( subst->lt_subs[ n ].bv_val ); + subst->lt_subs[ n ].bv_val = NULL; + } + + switch ( subst->lt_submatch[ n ].ls_type ) { + case REWRITE_SUBMATCH_ASIS: + break; + + case REWRITE_SUBMATCH_XMAP: + rewrite_xmap_destroy( &subst->lt_submatch[ n ].ls_map ); + break; + + case REWRITE_SUBMATCH_MAP_W_ARG: + rewrite_map_destroy( &subst->lt_submatch[ n ].ls_map ); + break; + + default: + break; + } + } + + free( subst->lt_submatch ); + subst->lt_submatch = NULL; + + /* last one */ + if ( subst->lt_subs[ n ].bv_val ) { + free( subst->lt_subs[ n ].bv_val ); + subst->lt_subs[ n ].bv_val = NULL; + } + + free( subst->lt_subs ); + subst->lt_subs = NULL; + + free( subst ); + *psubst = NULL; + + return 0; +} + diff --git a/libraries/librewrite/var.c b/libraries/librewrite/var.c new file mode 100644 index 0000000..61adc57 --- /dev/null +++ b/libraries/librewrite/var.c @@ -0,0 +1,273 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2000-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENT: + * This work was initially developed by Pierangelo Masarati for + * inclusion in OpenLDAP Software. + */ + +#include <portable.h> + +#include "rewrite-int.h" + +/* + * Compares two vars + */ +static int +rewrite_var_cmp( + const void *c1, + const void *c2 +) +{ + const struct rewrite_var *v1, *v2; + + v1 = ( const struct rewrite_var * )c1; + v2 = ( const struct rewrite_var * )c2; + + assert( v1 != NULL ); + assert( v2 != NULL ); + assert( v1->lv_name != NULL ); + assert( v2->lv_name != NULL ); + + return strcasecmp( v1->lv_name, v2->lv_name ); +} + +/* + * Duplicate var ? + */ +static int +rewrite_var_dup( + void *c1, + void *c2 +) +{ + struct rewrite_var *v1, *v2; + + v1 = ( struct rewrite_var * )c1; + v2 = ( struct rewrite_var * )c2; + + assert( v1 != NULL ); + assert( v2 != NULL ); + assert( v1->lv_name != NULL ); + assert( v2->lv_name != NULL ); + + return ( strcasecmp( v1->lv_name, v2->lv_name ) == 0 ? -1 : 0 ); +} + +/* + * Frees a var + */ +static void +rewrite_var_free( + void *v_var +) +{ + struct rewrite_var *var = v_var; + assert( var != NULL ); + + assert( var->lv_name != NULL ); + assert( var->lv_value.bv_val != NULL ); + + if ( var->lv_flags & REWRITE_VAR_COPY_NAME ) + free( var->lv_name ); + if ( var->lv_flags & REWRITE_VAR_COPY_VALUE ) + free( var->lv_value.bv_val ); + free( var ); +} + +/* + * Deletes a var tree + */ +int +rewrite_var_delete( + Avlnode *tree +) +{ + avl_free( tree, rewrite_var_free ); + return REWRITE_SUCCESS; +} + +/* + * Finds a var + */ +struct rewrite_var * +rewrite_var_find( + Avlnode *tree, + const char *name +) +{ + struct rewrite_var var; + + assert( name != NULL ); + + var.lv_name = ( char * )name; + return ( struct rewrite_var * )avl_find( tree, + ( caddr_t )&var, rewrite_var_cmp ); +} + +int +rewrite_var_replace( + struct rewrite_var *var, + const char *value, + int flags +) +{ + ber_len_t len; + + assert( value != NULL ); + + len = strlen( value ); + + if ( var->lv_flags & REWRITE_VAR_COPY_VALUE ) { + if ( flags & REWRITE_VAR_COPY_VALUE ) { + if ( len <= var->lv_value.bv_len ) { + AC_MEMCPY(var->lv_value.bv_val, value, len + 1); + + } else { + free( var->lv_value.bv_val ); + var->lv_value.bv_val = strdup( value ); + } + + } else { + free( var->lv_value.bv_val ); + var->lv_value.bv_val = (char *)value; + var->lv_flags &= ~REWRITE_VAR_COPY_VALUE; + } + + } else { + if ( flags & REWRITE_VAR_COPY_VALUE ) { + var->lv_value.bv_val = strdup( value ); + var->lv_flags |= REWRITE_VAR_COPY_VALUE; + + } else { + var->lv_value.bv_val = (char *)value; + } + } + + if ( var->lv_value.bv_val == NULL ) { + return -1; + } + + var->lv_value.bv_len = len; + + return 0; +} + +/* + * Inserts a newly created var + */ +struct rewrite_var * +rewrite_var_insert_f( + Avlnode **tree, + const char *name, + const char *value, + int flags +) +{ + struct rewrite_var *var; + int rc = 0; + + assert( tree != NULL ); + assert( name != NULL ); + assert( value != NULL ); + + var = rewrite_var_find( *tree, name ); + if ( var != NULL ) { + if ( flags & REWRITE_VAR_UPDATE ) { + (void)rewrite_var_replace( var, value, flags ); + goto cleanup; + } + rc = -1; + goto cleanup; + } + + var = calloc( sizeof( struct rewrite_var ), 1 ); + if ( var == NULL ) { + return NULL; + } + + memset( var, 0, sizeof( struct rewrite_var ) ); + + if ( flags & REWRITE_VAR_COPY_NAME ) { + var->lv_name = strdup( name ); + if ( var->lv_name == NULL ) { + rc = -1; + goto cleanup; + } + var->lv_flags |= REWRITE_VAR_COPY_NAME; + + } else { + var->lv_name = (char *)name; + } + + if ( flags & REWRITE_VAR_COPY_VALUE ) { + var->lv_value.bv_val = strdup( value ); + if ( var->lv_value.bv_val == NULL ) { + rc = -1; + goto cleanup; + } + var->lv_flags |= REWRITE_VAR_COPY_VALUE; + + } else { + var->lv_value.bv_val = (char *)value; + } + var->lv_value.bv_len = strlen( value ); + rc = avl_insert( tree, ( caddr_t )var, + rewrite_var_cmp, rewrite_var_dup ); + +cleanup:; + if ( rc != 0 && var ) { + avl_delete( tree, ( caddr_t )var, rewrite_var_cmp ); + rewrite_var_free( var ); + var = NULL; + } + + return var; +} + +/* + * Sets/inserts a var + */ +struct rewrite_var * +rewrite_var_set_f( + Avlnode **tree, + const char *name, + const char *value, + int flags +) +{ + struct rewrite_var *var; + + assert( tree != NULL ); + assert( name != NULL ); + assert( value != NULL ); + + var = rewrite_var_find( *tree, name ); + if ( var == NULL ) { + if ( flags & REWRITE_VAR_INSERT ) { + return rewrite_var_insert_f( tree, name, value, flags ); + + } else { + return NULL; + } + + } else { + assert( var->lv_value.bv_val != NULL ); + + (void)rewrite_var_replace( var, value, flags ); + } + + return var; +} + diff --git a/libraries/librewrite/xmap.c b/libraries/librewrite/xmap.c new file mode 100644 index 0000000..1ce0d44 --- /dev/null +++ b/libraries/librewrite/xmap.c @@ -0,0 +1,506 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2000-2021 The OpenLDAP Foundation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ +/* ACKNOWLEDGEMENT: + * This work was initially developed by Pierangelo Masarati for + * inclusion in OpenLDAP Software. + */ + +#include <portable.h> + +#include <stdio.h> + +#ifdef HAVE_PWD_H +#include <pwd.h> +#endif + +#define LDAP_DEPRECATED 1 +#include "rewrite-int.h" +#include "rewrite-map.h" + +/* + * Global data + */ +#ifdef USE_REWRITE_LDAP_PVT_THREADS +ldap_pvt_thread_mutex_t xpasswd_mutex; +static int xpasswd_mutex_init = 0; +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + +/* + * Map parsing + * NOTE: these are old-fashion maps; new maps will be parsed on separate + * config lines, and referred by name. + */ +struct rewrite_map * +rewrite_xmap_parse( + struct rewrite_info *info, + const char *s, + const char **currpos +) +{ + struct rewrite_map *map; + + assert( info != NULL ); + assert( s != NULL ); + assert( currpos != NULL ); + + Debug( LDAP_DEBUG_ARGS, "rewrite_xmap_parse: %s\n%s%s", + s, "", "" ); + + *currpos = NULL; + + map = calloc( sizeof( struct rewrite_map ), 1 ); + if ( map == NULL ) { + Debug( LDAP_DEBUG_ANY, "rewrite_xmap_parse:" + " calloc failed\n%s%s%s", "", "", "" ); + return NULL; + } + + /* + * Experimental passwd map: + * replaces the uid with the matching gecos from /etc/passwd file + */ + if ( strncasecmp(s, "xpasswd", 7 ) == 0 ) { + map->lm_type = REWRITE_MAP_XPWDMAP; + map->lm_name = strdup( "xpasswd" ); + if ( map->lm_name == NULL ) { + free( map ); + return NULL; + } + + assert( s[7] == '}' ); + *currpos = s + 8; + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + if ( !xpasswd_mutex_init ) { + if ( ldap_pvt_thread_mutex_init( &xpasswd_mutex ) ) { + free( map ); + return NULL; + } + } + ++xpasswd_mutex_init; +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + /* Don't really care if fails */ + return map; + + /* + * Experimental file map: + * looks up key in a `key value' ascii file + */ + } else if ( strncasecmp( s, "xfile", 5 ) == 0 ) { + char *filename; + const char *p; + int l; + int c = 5; + + map->lm_type = REWRITE_MAP_XFILEMAP; + + if ( s[ c ] != '(' ) { + free( map ); + return NULL; + } + + /* Must start with '/' for security concerns */ + c++; + if ( s[ c ] != '/' ) { + free( map ); + return NULL; + } + + for ( p = s + c; p[ 0 ] != '\0' && p[ 0 ] != ')'; p++ ); + if ( p[ 0 ] != ')' ) { + free( map ); + return NULL; + } + + l = p - s - c; + filename = calloc( sizeof( char ), l + 1 ); + if ( filename == NULL ) { + free( map ); + return NULL; + } + AC_MEMCPY( filename, s + c, l ); + filename[ l ] = '\0'; + + map->lm_args = ( void * )fopen( filename, "r" ); + free( filename ); + + if ( map->lm_args == NULL ) { + free( map ); + return NULL; + } + + *currpos = p + 1; + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + if ( ldap_pvt_thread_mutex_init( &map->lm_mutex ) ) { + fclose( ( FILE * )map->lm_args ); + free( map ); + return NULL; + } +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + return map; + + /* + * Experimental ldap map: + * looks up key on the fly (not implemented!) + */ + } else if ( strncasecmp(s, "xldap", 5 ) == 0 ) { + char *p; + char *url; + int l, rc; + int c = 5; + LDAPURLDesc *lud; + + if ( s[ c ] != '(' ) { + free( map ); + return NULL; + } + c++; + + p = strchr( s, '}' ); + if ( p == NULL ) { + free( map ); + return NULL; + } + p--; + + *currpos = p + 2; + + /* + * Add two bytes for urlencoding of '%s' + */ + l = p - s - c; + url = calloc( sizeof( char ), l + 3 ); + if ( url == NULL ) { + free( map ); + return NULL; + } + AC_MEMCPY( url, s + c, l ); + url[ l ] = '\0'; + + /* + * Urlencodes the '%s' for ldap_url_parse + */ + p = strchr( url, '%' ); + if ( p != NULL ) { + AC_MEMCPY( p + 3, p + 1, strlen( p + 1 ) + 1 ); + p[ 1 ] = '2'; + p[ 2 ] = '5'; + } + + rc = ldap_url_parse( url, &lud ); + free( url ); + + if ( rc != LDAP_SUCCESS ) { + free( map ); + return NULL; + } + assert( lud != NULL ); + + map->lm_args = ( void * )lud; + map->lm_type = REWRITE_MAP_XLDAPMAP; + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + if ( ldap_pvt_thread_mutex_init( &map->lm_mutex ) ) { + ldap_free_urldesc( lud ); + free( map ); + return NULL; + } +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + return map; + + /* Unhandled map */ + } + + free( map ); + return NULL; +} + +/* + * Map key -> value resolution + * NOTE: these are old-fashion maps; new maps will be parsed on separate + * config lines, and referred by name. + */ +int +rewrite_xmap_apply( + struct rewrite_info *info, + struct rewrite_op *op, + struct rewrite_map *map, + struct berval *key, + struct berval *val +) +{ + int rc = REWRITE_SUCCESS; + + assert( info != NULL ); + assert( op != NULL ); + assert( map != NULL ); + assert( key != NULL ); + assert( val != NULL ); + + val->bv_val = NULL; + val->bv_len = 0; + + switch ( map->lm_type ) { +#ifdef HAVE_GETPWNAM + case REWRITE_MAP_XPWDMAP: { + struct passwd *pwd; + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_mutex_lock( &xpasswd_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + pwd = getpwnam( key->bv_val ); + if ( pwd == NULL ) { + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_mutex_unlock( &xpasswd_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + rc = LDAP_NO_SUCH_OBJECT; + break; + } + +#ifdef HAVE_STRUCT_PASSWD_PW_GECOS + if ( pwd->pw_gecos != NULL && pwd->pw_gecos[0] != '\0' ) { + int l = strlen( pwd->pw_gecos ); + + val->bv_val = strdup( pwd->pw_gecos ); + val->bv_len = l; + } else +#endif /* HAVE_STRUCT_PASSWD_PW_GECOS */ + { + val->bv_val = strdup( key->bv_val ); + val->bv_len = key->bv_len; + } + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_mutex_unlock( &xpasswd_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + if ( val->bv_val == NULL ) { + rc = REWRITE_ERR; + } + break; + } +#endif /* HAVE_GETPWNAM*/ + + case REWRITE_MAP_XFILEMAP: { + char buf[1024]; + + if ( map->lm_args == NULL ) { + rc = REWRITE_ERR; + break; + } + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_mutex_lock( &map->lm_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + rewind( ( FILE * )map->lm_args ); + + while ( fgets( buf, sizeof( buf ), ( FILE * )map->lm_args ) ) { + char *p; + int blen; + + blen = strlen( buf ); + if ( buf[ blen - 1 ] == '\n' ) { + buf[ blen - 1 ] = '\0'; + } + + p = strtok( buf, " " ); + if ( p == NULL ) { +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_mutex_unlock( &map->lm_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + rc = REWRITE_ERR; + goto rc_return; + } + if ( strcasecmp( p, key->bv_val ) == 0 + && ( p = strtok( NULL, "" ) ) ) { + val->bv_val = strdup( p ); + if ( val->bv_val == NULL ) { +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_mutex_unlock( &map->lm_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + rc = REWRITE_ERR; + goto rc_return; + } + + val->bv_len = strlen( p ); + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_mutex_unlock( &map->lm_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + goto rc_return; + } + } + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_mutex_unlock( &map->lm_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + rc = REWRITE_ERR; + + break; + } + + case REWRITE_MAP_XLDAPMAP: { + LDAP *ld; + char filter[1024]; + LDAPMessage *res = NULL, *entry; + LDAPURLDesc *lud = ( LDAPURLDesc * )map->lm_args; + int attrsonly = 0; + char **values; + + assert( lud != NULL ); + + /* + * No mutex because there is no write on the map data + */ + + ld = ldap_init( lud->lud_host, lud->lud_port ); + if ( ld == NULL ) { + rc = REWRITE_ERR; + goto rc_return; + } + + snprintf( filter, sizeof( filter ), lud->lud_filter, + key->bv_val ); + + if ( strcasecmp( lud->lud_attrs[ 0 ], "dn" ) == 0 ) { + attrsonly = 1; + } + rc = ldap_search_s( ld, lud->lud_dn, lud->lud_scope, + filter, lud->lud_attrs, attrsonly, &res ); + if ( rc != LDAP_SUCCESS ) { + ldap_unbind( ld ); + rc = REWRITE_ERR; + goto rc_return; + } + + if ( ldap_count_entries( ld, res ) != 1 ) { + ldap_unbind( ld ); + rc = REWRITE_ERR; + goto rc_return; + } + + entry = ldap_first_entry( ld, res ); + if ( entry == NULL ) { + ldap_msgfree( res ); + ldap_unbind( ld ); + rc = REWRITE_ERR; + goto rc_return; + } + if ( attrsonly == 1 ) { + val->bv_val = ldap_get_dn( ld, entry ); + + } else { + values = ldap_get_values( ld, entry, + lud->lud_attrs[0] ); + if ( values != NULL ) { + val->bv_val = strdup( values[ 0 ] ); + ldap_value_free( values ); + } + } + + ldap_msgfree( res ); + ldap_unbind( ld ); + + if ( val->bv_val == NULL ) { + rc = REWRITE_ERR; + goto rc_return; + } + val->bv_len = strlen( val->bv_val ); + + rc = REWRITE_SUCCESS; + } break; + } + +rc_return:; + return rc; +} + +int +rewrite_xmap_destroy( + struct rewrite_map **pmap +) +{ + struct rewrite_map *map; + + assert( pmap != NULL ); + assert( *pmap != NULL ); + + map = *pmap; + + switch ( map->lm_type ) { + case REWRITE_MAP_XPWDMAP: +#ifdef USE_REWRITE_LDAP_PVT_THREADS + --xpasswd_mutex_init; + if ( !xpasswd_mutex_init ) { + ldap_pvt_thread_mutex_destroy( &xpasswd_mutex ); + } +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + break; + + case REWRITE_MAP_XFILEMAP: +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_mutex_lock( &map->lm_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + if ( map->lm_args ) { + fclose( ( FILE * )map->lm_args ); + map->lm_args = NULL; + } + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_mutex_unlock( &map->lm_mutex ); + ldap_pvt_thread_mutex_destroy( &map->lm_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + break; + + case REWRITE_MAP_XLDAPMAP: +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_mutex_lock( &map->lm_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + + if ( map->lm_args ) { + ldap_free_urldesc( ( LDAPURLDesc * )map->lm_args ); + map->lm_args = NULL; + } + +#ifdef USE_REWRITE_LDAP_PVT_THREADS + ldap_pvt_thread_mutex_unlock( &map->lm_mutex ); + ldap_pvt_thread_mutex_destroy( &map->lm_mutex ); +#endif /* USE_REWRITE_LDAP_PVT_THREADS */ + break; + + default: + break; + + } + + free( map->lm_name ); + free( map ); + *pmap = NULL; + + return 0; +} + |