+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;
+}
+
+static char *
+find_wildcard( char *s )
+{
+ for( ; *s != '\0' ; s++ ) {
+ switch( *s ) {
+ case '*': /* found wildcard */
+ return s;
+
+ case '\\':
+ s++; /* skip over escape */
+ if ( *s == '\0' )
+ return NULL; /* escape at end of string */
+ if( hex2value( s[0] ) >= 0 && hex2value( s[1] ) >= 0 ) {
+ /* skip over lead digit of two hex digit code */
+ s++;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/* unescape filter value */
+/* support both LDAP v2 and v3 escapes */
+/* output can include nul characters */
+static long
+filter_value_unescape( char *fval )
+{
+ long r, v;
+ int v1, v2;
+
+ for( r=v=0; fval[v] != '\0'; v++ ) {
+ switch( fval[v] ) {
+ 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 */
+ fval[r++] = fval[v];
+ }
+
+ break;
+
+ default:
+ fval[r++] = fval[v];
+ }
+ }
+
+ fval[r] = '\0';
+ return r;
+}
+