diff --git a/clients/maildap/main.c b/clients/maildap/main.c
new file mode 100644
index 0000000000000000000000000000000000000000..345c8f4256853b9cc02df766d1e89b3cd15ea8d8
--- /dev/null
+++ b/clients/maildap/main.c
@@ -0,0 +1,2074 @@
+/* $OpenLDAP$ */
+/*
+ * 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.
+ *
+ * Copyright 1998-2002 The OpenLDAP Foundation
+ * COPYING RESTRICTIONS APPLY.  See COPYRIGHT File in top level directory
+ * of this package for details.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/ctype.h>
+#include <ac/param.h>
+#include <ac/signal.h>
+#include <ac/string.h>
+#include <ac/sysexits.h>
+#include <ac/syslog.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+#include <ac/wait.h>
+
+#include <sys/stat.h>
+
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
+
+#include <ldap.h>
+
+#include "ldap_defaults.h"
+
+#ifndef MAIL500_BOUNCEFROM
+#define MAIL500_BOUNCEFROM "<>"
+#endif
+
+#define USER		0x01
+#define GROUP_ERRORS	0x02
+#define GROUP_REQUEST	0x04
+#define GROUP_MEMBERS	0x08
+#define GROUP_OWNER	0x10
+
+#define ERROR		"error"
+#define ERRORS		"errors"
+#define REQUEST		"request"
+#define REQUESTS	"requests"
+#define MEMBERS		"members"
+#define OWNER		"owner"
+#define OWNERS		"owners"
+
+LDAP	*ld;
+char	*vacationhost = NULL;
+char	*errorsfrom = MAIL500_BOUNCEFROM;
+char	*mailfrom = NULL;
+char	*host = NULL;
+char	*ldaphost = NULL;
+int	hostlen = 0;
+int	debug;
+
+typedef struct errs {
+	int		e_code;
+#define E_USERUNKNOWN		1
+#define E_AMBIGUOUS		2
+#define E_NOEMAIL		3
+#define E_NOREQUEST		4
+#define E_NOERRORS		5
+#define E_BADMEMBER		6
+#define E_JOINMEMBERNOEMAIL	7
+#define E_MEMBERNOEMAIL		8
+#define E_LOOP			9
+#define E_NOMEMBERS		10
+#define	E_NOOWNER		11
+#define E_GROUPUNKNOWN		12
+#define E_NOOWNADDRESS		13
+	char		*e_addr;
+	union e_union_u {
+		char		*e_u_loop;
+		LDAPMessage	*e_u_msg;
+	} e_union;
+#define e_msg	e_union.e_u_msg
+#define e_loop	e_union.e_u_loop
+} Error;
+
+typedef struct groupto {
+	char	*g_dn;
+	char	*g_errorsto;
+	char	**g_members;
+	int	g_nmembers;
+} Group;
+
+typedef struct baseinfo {
+	char	*b_url;
+	int	b_m_entries;
+	char	b_rdnpref;	/* give rdn's preference when searching? */
+	int	b_search;	/* ORed with the type of thing the address */
+				/*  looks like (USER, GROUP_ERRORS, etc.)  */
+				/*  to see if this should be searched	   */
+} Base;
+
+Base	**base = NULL;
+
+char	*sendmailargs[] = { MAIL500_SENDMAIL, "-oMrLDAP", "-odi", "-oi", "-f", NULL, NULL };
+
+typedef struct attr_semantics {
+	char	*as_name;
+	int	as_m_valued;	/* Is multivalued? */
+	int	as_priority;	/* Priority level of this attribut type */
+	int	as_syntax;	/* How to interpret values */
+	int	as_m_entries;	/* Can resolve to several entries? */
+	int	as_kind;	/* Recipient, sender, etc. */
+	char	*as_param;	/* Extra info for filters and things alike */
+} AttrSemantics;
+
+#define AS_SYNTAX_UNKNOWN	0
+#define AS_SYNTAX_NATIVE_MB	1	/* Unqualified mailbox name */
+#define AS_SYNTAX_RFC822	2	/* RFC822 mail address */
+#define AS_SYNTAX_HOST		3
+#define AS_SYNTAX_DN		4	/* A directory entry */
+#define AS_SYNTAX_RFC822_EXT	5
+#define AS_SYNTAX_URL		6	/* mailto: or ldap: URL */
+#define AS_SYNTAX_BOOL_FILTER	7	/* For joinable, filter in as_param */
+#define AS_SYNTAX_PRESENT	8	/* Value irrelevant, only presence is
+					 * considered. */
+
+#define AS_KIND_UNKNOWN		0
+#define AS_KIND_RECIPIENT	1
+#define AS_KIND_ERRORS		2	/* For ErrorsTo and similar */
+#define AS_KIND_REQUEST		3
+#define AS_KIND_OWNER		4
+#define AS_KIND_ROUTE_TO_HOST	5	/* Expand at some other host */
+#define AS_KIND_ALLOWED_SENDER	6	/* Can send to group */
+#define AS_KIND_MODERATOR	7
+#define AS_KIND_ROUTE_TO_ADDR	8	/* Rewrite recipient address as */
+#define AS_KIND_OWN_ADDR	9	/* RFC822 name of this entry */
+#define AS_KIND_DELIVERY_TYPE	10	/* How to deliver mail to this entry */
+
+AttrSemantics **attr_semantics = NULL;
+int current_priority = 0;
+
+typedef struct subst {
+	char	sub_char;
+	char	*sub_value;
+} Subst;
+
+char	**groupclasses = NULL;
+char	**def_attr = NULL;
+char	**myhosts = NULL;		/* FQDNs not to route elsewhere */
+char	**mydomains = NULL;		/* If an RFC822 address points to one
+					   of these domains, search it in the
+					   directory instead of returning it
+					   to hte MTA */
+
+static void load_config( char *filespec );
+static void split_address( char *address, char **localpart, char **domainpart);
+static int entry_engine( LDAPMessage *e, char *dn, char *address, char ***to, int *nto, Group ***togroups, int *ngroups, Error **err, int *nerr, int type );
+static void do_address( char *name, char ***to, int *nto, Group ***togroups, int *ngroups, Error **err, int *nerr, int type );
+static void send_message( char **to );
+static void send_errors( Error *err, int nerr );
+static void do_noemail( FILE *fp, Error *err, int namelen );
+static void do_ambiguous( FILE *fp, Error *err, int namelen );
+static int count_values( char **list );
+static void add_to( char ***list, int *nlist, char **new );
+static void add_single_to( char ***list, char *new );
+static int  isgroup( LDAPMessage *e );
+static void add_error( Error **err, int *nerr, int code, char *addr, LDAPMessage *msg );
+static void unbind_and_exit( int rc ) LDAP_GCCATTR((noreturn));
+static void send_group( Group **group, int ngroup );
+
+static int  connect_to_x500( void );
+
+
+int
+main ( int argc, char **argv )
+{
+	char		*myname;
+	char		**tolist;
+	Error		*errlist;
+	Group		**togroups;
+	int		numto, ngroups, numerr, nargs;
+	int		i, j;
+	char		*conffile = NULL;
+
+	if ( (myname = strrchr( argv[0], *LDAP_DIRSEP )) == NULL )
+		myname = strdup( argv[0] );
+	else
+		myname = strdup( myname + 1 );
+
+#ifdef SIGPIPE
+	(void) SIGNAL( SIGPIPE, SIG_IGN );
+#endif
+
+#ifdef LOG_MAIL
+	openlog( myname, OPENLOG_OPTIONS, LOG_MAIL );
+#elif LOG_DEBUG
+	openlog( myname, OPENLOG_OPTIONS );
+#endif
+
+	while ( (i = getopt( argc, argv, "d:C:f:h:l:m:v:" )) != EOF ) {
+		switch( i ) {
+		case 'd':	/* turn on debugging */
+			debug |= atoi( optarg );
+			break;
+
+		case 'C':	/* path to configuration file */
+			conffile = strdup( optarg );
+			break;
+
+		case 'f':	/* who it's from & where errors should go */
+			mailfrom = strdup( optarg );
+			/* Deal with <> */
+			if ( mailfrom[0] == '\0' ) {
+				free( mailfrom );
+				mailfrom = strdup( "<>" );
+			}
+			for ( j = 0; sendmailargs[j] != NULL; j++ ) {
+				if ( strcmp( sendmailargs[j], "-f" ) == 0 ) {
+					sendmailargs[j+1] = mailfrom;
+					break;
+				}
+			}
+			break;
+
+		case 'h':	/* hostname */
+			host = strdup( optarg );
+			hostlen = strlen(host);
+			break;
+
+		case 'l':	/* ldap host */
+			ldaphost = strdup( optarg );
+			break;
+
+				/* mailer-daemon address - who we should */
+		case 'm':	/* say errors come from */
+			errorsfrom = strdup( optarg );
+			break;
+
+		case 'v':	/* vacation host */
+			vacationhost = strdup( optarg );
+			break;
+
+		default:
+			syslog( LOG_ALERT, "unknown option" );
+			break;
+		}
+	}
+
+	if ( mailfrom == NULL ) {
+		syslog( LOG_ALERT, "required argument -f not present" );
+		exit( EX_TEMPFAIL );
+	}
+	if ( errorsfrom == NULL ) {
+		syslog( LOG_ALERT, "required argument -m not present" );
+		exit( EX_TEMPFAIL );
+	}
+/*  	if ( host == NULL ) { */
+/*  		syslog( LOG_ALERT, "required argument -h not present" ); */
+/*  		exit( EX_TEMPFAIL ); */
+/*  	} */
+	if ( conffile == NULL ) {
+		syslog( LOG_ALERT, "required argument -C not present" );
+		exit( EX_TEMPFAIL );
+	}
+
+	load_config( conffile );
+
+	if ( connect_to_x500() != 0 )
+		exit( EX_TEMPFAIL );
+
+	setuid( geteuid() );
+
+	if ( debug ) {
+		char	buf[1024];
+		int	i;
+
+		syslog( LOG_ALERT, "running as %d", geteuid() );
+		strcpy( buf, argv[0] );
+		for ( i = 1; i < argc; i++ ) {
+			strcat( buf, " " );
+			strcat( buf, argv[i] );
+		}
+
+		syslog( LOG_ALERT, "args: (%s)", buf );
+	}
+
+	tolist = NULL;
+	numto = 0;
+	add_to( &tolist, &numto, sendmailargs );
+	nargs = numto;
+	ngroups = numerr = 0;
+	togroups = NULL;
+	errlist = NULL;
+	for ( i = optind; i < argc; i++ ) {
+		char	*s;
+		int	type;
+		char	*localpart = NULL, *domainpart = NULL;
+		char	address[1024];
+
+		type = USER;
+		split_address( argv[i], &localpart, &domainpart );
+		if ( (s = strrchr( localpart, '-' )) != NULL ) {
+			s++;
+
+			if ((strcasecmp(s, ERROR) == 0) ||
+				(strcasecmp(s, ERRORS) == 0)) {
+				type = GROUP_ERRORS;
+				*(--s) = '\0';
+			} else if ((strcasecmp(s, REQUEST) == 0) ||
+				(strcasecmp(s, REQUESTS) == 0)) {
+				type = GROUP_REQUEST;
+				*(--s) = '\0';
+			} else if ( strcasecmp( s, MEMBERS ) == 0 ) {
+				type = GROUP_MEMBERS;
+				*(--s) = '\0';
+			} else if ((strcasecmp(s, OWNER) == 0) ||
+				(strcasecmp(s, OWNERS) == 0)) {
+				type = GROUP_OWNER;
+				*(--s) = '\0';
+			}
+		}
+
+		if ( domainpart ) {
+			sprintf( address, "%s@%s", localpart, domainpart );
+			free( localpart );
+			free( domainpart );
+		} else {
+			sprintf( address, "%s", localpart );
+			free( localpart );
+		}
+		do_address( address, &tolist, &numto, &togroups, &ngroups,
+		    &errlist, &numerr, type );
+	}
+
+	/*
+	 * If we have both errors and successful deliveries to make or if
+	 * if there are any groups to deliver to, we basically need to read
+	 * the message twice.  So, we have to put it in a tmp file.
+	 */
+
+	if ( numerr > 0 && numto > nargs || ngroups > 0 ) {
+		FILE	*fp;
+		char	buf[BUFSIZ];
+
+		umask( 077 );
+		if ( (fp = tmpfile()) == NULL ) {
+			syslog( LOG_ALERT, "could not open tmp file" );
+			unbind_and_exit( EX_TEMPFAIL );
+		}
+
+		/* copy the message to a temp file */
+		while ( fgets( buf, sizeof(buf), stdin ) != NULL ) {
+			if ( fputs( buf, fp ) == EOF ) {
+				syslog( LOG_ALERT, "error writing tmpfile" );
+				unbind_and_exit( EX_TEMPFAIL );
+			}
+		}
+
+		if ( dup2( fileno( fp ), 0 ) == -1 ) {
+			syslog( LOG_ALERT, "could not dup2 tmpfile" );
+			unbind_and_exit( EX_TEMPFAIL );
+		}
+
+		fclose( fp );
+	}
+
+	/* deal with errors */
+	if ( numerr > 0 ) {
+		if ( debug ) {
+			syslog( LOG_ALERT, "sending errors" );
+		}
+		(void) rewind( stdin );
+		send_errors( errlist, numerr );
+	}
+
+	(void) ldap_unbind( ld );
+
+	/* send to groups with errorsTo */
+	if ( ngroups > 0 ) {
+		if ( debug ) {
+			syslog( LOG_ALERT, "sending to groups with errorsto" );
+		}
+		(void) rewind( stdin );
+		send_group( togroups, ngroups );
+	}
+
+	/* send to expanded aliases and groups w/o errorsTo */
+	if ( numto > nargs ) {
+		if ( debug ) {
+			syslog( LOG_ALERT, "sending to aliases and groups" );
+		}
+		(void) rewind( stdin );
+		send_message( tolist );
+	}
+
+	return( EX_OK );
+}
+
+static char *
+get_config_line( FILE *cf, int *lineno)
+{
+	static char	buf[2048];
+	int		len;
+	int		pos;
+	int		room;
+
+	pos = 0;
+	room = sizeof( buf );
+	while ( fgets( &buf[pos], room, cf ) ) {
+		(*lineno)++;
+		if ( pos > 0 ) {
+			/* Delete whitespace at the beginning of new data */
+			if ( isspace( (unsigned char) buf[pos] ) ) {
+				char *s, *d;
+				for ( s = buf+pos; isspace((unsigned char) *s); s++ )
+					;
+				for ( d = buf+pos; *s; s++, d++ ) {
+					*d = *s;
+				}
+				*d = *s;
+			}
+		}
+		len = strlen( buf );
+		if ( buf[len-1] != '\n' ) {
+			syslog( LOG_ALERT, "Definition too long at line %d",
+				*lineno );
+			exit( EX_TEMPFAIL );
+		}
+		if ( buf[0] == '#' )
+			continue;
+		if ( strspn( buf, " \t\n" ) == len )
+			continue;
+		if ( len >= 2 && buf[len-2] == '\\' ) {
+			pos = len - 2;
+			room = sizeof(buf) - pos;
+			continue;
+		}
+		/* We have a real line, we will exit the loop */
+		buf[len-1] = '\0';
+		return( buf );
+	}
+	return( NULL );
+}
+
+static void
+add_url ( char *url, int rdnpref, int typemask )
+{
+	Base		**list_temp;
+	int		size;
+	Base		*b;
+
+	b = calloc(1, sizeof(Base));
+	if ( !b ) {
+		syslog( LOG_ALERT, "Out of memory" );
+		exit( EX_TEMPFAIL );
+	}
+	b->b_url = strdup( url );
+	b->b_rdnpref = rdnpref;
+	b->b_search   = typemask;
+
+	if ( base == NULL ) {
+		base = calloc(2, sizeof(LDAPURLDesc *));
+		if ( !base ) {
+			syslog( LOG_ALERT, "Out of memory" );
+			exit( EX_TEMPFAIL );
+		}
+		base[0] = b;
+	} else {
+		for ( size = 0; base[size]; size++ )
+			;
+		size += 2;
+		list_temp = realloc( base, size*sizeof(LDAPURLDesc *) );
+		if ( !list_temp ) {
+			syslog( LOG_ALERT, "Out of memory" );
+			exit( EX_TEMPFAIL );
+		}
+		base = list_temp;
+		base[size-2] = b;
+		base[size-1] = NULL;
+	}
+}
+
+static void
+add_def_attr( char *s )
+{
+	char *p, *q;
+
+	p = s;
+	while ( *p ) {
+		p += strspn( p, "\t," );
+		q = strpbrk( p, " \t," );
+		if ( q ) {
+			*q = '\0';
+			add_single_to( &def_attr, p );
+		} else {
+			add_single_to( &def_attr, p );
+			break;
+		}
+		p = q + 1;
+	}
+}
+
+static void
+add_attr_semantics( char *s )
+{
+	char *p, *q;
+	AttrSemantics *as;
+
+	as = calloc( 1, sizeof( AttrSemantics ) );
+	as->as_priority = current_priority;
+	p = s;
+	while ( isspace ( (unsigned char) *p ) )
+		p++;
+	q = p;
+	while ( !isspace ( (unsigned char) *q ) && *q != '\0' )
+		q++;
+	*q = '\0';
+	as->as_name = strdup( p );
+	p = q + 1;
+
+	while ( *p ) {
+		while ( isspace ( (unsigned char) *p ) )
+			p++;
+		q = p;
+		while ( !isspace ( (unsigned char) *q ) && *q != '\0' )
+			q++;
+		*q = '\0';
+		if ( !strcasecmp( p, "multivalued" ) ) {
+			as->as_m_valued = 1;
+		} else if ( !strcasecmp( p, "multiple-entries" ) ) {
+			as->as_m_entries = 1;
+		} else if ( !strcasecmp( p, "local-native-mailbox" ) ) {
+			as->as_syntax = AS_SYNTAX_NATIVE_MB;
+		} else if ( !strcasecmp( p, "rfc822" ) ) {
+			as->as_syntax = AS_SYNTAX_RFC822;
+		} else if ( !strcasecmp( p, "rfc822-extended" ) ) {
+			as->as_syntax = AS_SYNTAX_RFC822_EXT;
+		} else if ( !strcasecmp( p, "dn" ) ) {
+			as->as_syntax = AS_SYNTAX_DN;
+		} else if ( !strcasecmp( p, "url" ) ) {
+			as->as_syntax = AS_SYNTAX_URL;
+		} else if ( !strcasecmp( p, "search-with-filter" ) ) {
+			as->as_syntax = AS_SYNTAX_BOOL_FILTER;
+		} else if ( !strncasecmp( p, "param=", 6 ) ) {
+			q = strchr( p, '=' );
+			if ( q ) {
+				p = q + 1;
+				while ( *q && !isspace( (unsigned char) *q ) ) {
+					q++;
+				}
+				if ( *q ) {
+					*q = '\0';
+					as->as_param = strdup( p );
+					p = q + 1;
+				} else {
+					as->as_param = strdup( p );
+					p = q;
+				}
+			}
+		} else if ( !strcasecmp( p, "host" ) ) {
+			as->as_kind = AS_SYNTAX_HOST;
+		} else if ( !strcasecmp( p, "present" ) ) {
+			as->as_kind = AS_SYNTAX_PRESENT;
+		} else if ( !strcasecmp( p, "route-to-host" ) ) {
+			as->as_kind = AS_KIND_ROUTE_TO_HOST;
+		} else if ( !strcasecmp( p, "route-to-address" ) ) {
+			as->as_kind = AS_KIND_ROUTE_TO_ADDR;
+		} else if ( !strcasecmp( p, "own-address" ) ) {
+			as->as_kind = AS_KIND_OWN_ADDR;
+		} else if ( !strcasecmp( p, "recipient" ) ) {
+			as->as_kind = AS_KIND_RECIPIENT;
+		} else if ( !strcasecmp( p, "errors" ) ) {
+			as->as_kind = AS_KIND_ERRORS;
+		} else if ( !strcasecmp( p, "request" ) ) {
+			as->as_kind = AS_KIND_REQUEST;
+		} else if ( !strcasecmp( p, "owner" ) ) {
+			as->as_kind = AS_KIND_OWNER;
+		} else if ( !strcasecmp( p, "delivery-type" ) ) {
+			as->as_kind = AS_KIND_DELIVERY_TYPE;
+		} else {
+			syslog( LOG_ALERT,
+				"Unknown semantics word %s", p );
+			exit( EX_TEMPFAIL );
+		}
+		p = q + 1;
+	}
+	if ( attr_semantics == NULL ) {
+		attr_semantics = calloc(2, sizeof(AttrSemantics *));
+		if ( !attr_semantics ) {
+			syslog( LOG_ALERT, "Out of memory" );
+			exit( EX_TEMPFAIL );
+		}
+		attr_semantics[0] = as;
+	} else {
+		int size;
+		AttrSemantics **list_temp;
+		for ( size = 0; attr_semantics[size]; size++ )
+			;
+		size += 2;
+		list_temp = realloc( attr_semantics,
+				     size*sizeof(AttrSemantics *) );
+		if ( !list_temp ) {
+			syslog( LOG_ALERT, "Out of memory" );
+			exit( EX_TEMPFAIL );
+		}
+		attr_semantics = list_temp;
+		attr_semantics[size-2] = as;
+		attr_semantics[size-1] = NULL;
+	}
+}
+
+static void
+load_config( char *filespec )
+{
+	FILE		*cf;
+	char		*line;
+	int		lineno = 0;
+	char		*p;
+	int		rdnpref;
+	int		typemask;
+
+	cf = fopen( filespec, "r" );
+	if ( !cf ) {
+		perror( "Opening config file" );
+		exit( EX_TEMPFAIL );
+	}
+
+	while ( ( line = get_config_line( cf,&lineno ) ) ) {
+		p = strpbrk( line, " \t" );
+		if ( !p ) {
+			syslog( LOG_ALERT,
+				"Missing space at line %d", lineno );
+			exit( EX_TEMPFAIL );
+		}
+		if ( !strncmp( line, "search", p-line ) ) {
+			p += strspn( p, " \t" );
+			/* TBC, get these */
+			rdnpref = 0;
+			typemask = 0xFF;
+			add_url( p, rdnpref, typemask );
+		} else if ( !strncmp(line, "attribute", p-line) ) {
+			p += strspn(p, " \t");
+			add_attr_semantics( p );
+		} else if ( !strncmp(line, "default-attributes", p-line) ) {
+			p += strspn(p, " \t");
+			add_def_attr( p );
+		} else if ( !strncmp(line, "group-classes", p-line) ) {
+			p += strspn(p, " \t");
+			add_single_to( &groupclasses, p );
+		} else if ( !strncmp(line, "priority", p-line) ) {
+			p += strspn(p, " \t");
+			current_priority = atoi(p);
+		} else if ( !strncmp(line, "domain", p-line) ) {
+			p += strspn(p, " \t");
+			add_single_to( &mydomains, p );
+		} else if ( !strncmp(line, "host", p-line) ) {
+			p += strspn(p, " \t");
+			add_single_to( &myhosts, p );
+		} else {
+			syslog( LOG_ALERT,
+				"Unparseable config definition at line %d",
+				lineno );
+			exit( EX_TEMPFAIL );
+		}
+	}
+	fclose( cf );
+}
+
+static int
+connect_to_x500( void )
+{
+	int opt;
+
+	if ( (ld = ldap_init( ldaphost, 0 )) == NULL ) {
+		syslog( LOG_ALERT, "ldap_init failed" );
+		return( -1 );
+	}
+
+	/*  TBC: Set this only when it makes sense
+	opt = MAIL500_MAXAMBIGUOUS;
+	ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &opt);
+	*/
+	opt = LDAP_DEREF_ALWAYS;
+	ldap_set_option(ld, LDAP_OPT_DEREF, &opt);
+
+	if ( ldap_simple_bind_s( ld, NULL, NULL ) != LDAP_SUCCESS ) {
+		syslog( LOG_ALERT, "ldap_simple_bind_s failed" );
+		return( -1 );
+	}
+
+	return( 0 );
+}
+
+static Group *
+new_group( char *dn, Group ***list, int *nlist )
+{
+	int	i;
+	Group	*this_group;
+
+	for ( i = 0; i < *nlist; i++ ) {
+		if ( strcasecmp( dn, (*list)[i]->g_dn ) == 0 ) {
+			syslog( LOG_ALERT, "group loop 2 detected (%s)", dn );
+			return NULL;
+		}
+	}
+
+	this_group = (Group *) malloc( sizeof(Group) );
+
+	if ( *nlist == 0 ) {
+		*list = (Group **) malloc( sizeof(Group *) );
+	} else {
+		*list = (Group **) realloc( *list, (*nlist + 1) *
+		    sizeof(Group *) );
+	}
+
+	this_group->g_errorsto = NULL;
+	this_group->g_members = NULL;
+	this_group->g_nmembers = 0;
+	/* save the group's dn so we can check for loops above */
+	this_group->g_dn = strdup( dn );
+
+	(*list)[*nlist] = this_group;
+	(*nlist)++;
+
+	return( this_group );
+}
+
+static void
+split_address(
+	char	*address,
+	char	**localpart,
+	char	**domainpart
+)
+{
+	char		*p;
+
+	if ( ( p = strrchr( address, '@' ) ) == NULL ) {
+		*localpart = strdup( address );
+		*domainpart = NULL;
+	} else {
+		*localpart = malloc( p - address + 1 );
+		strncpy( *localpart, address, p - address );
+		(*localpart)[p - address] = '\0';
+		p++;
+		*domainpart = strdup( p );
+	}
+}
+
+static int
+dn_search(
+	char	**dnlist, 
+	char	*address,
+	char	***to,
+	int	*nto,
+	Group	***togroups,
+	int	*ngroups,
+	Error	**err,
+	int	*nerr
+)
+{
+	int		rc;
+	int		i;
+	int		resolved = 0;
+	LDAPMessage	*res, *e;
+	struct timeval	timeout;
+
+	timeout.tv_sec = MAIL500_TIMEOUT;
+	timeout.tv_usec = 0;
+	for ( i = 0; dnlist[i]; i++ ) {
+		if ( (rc = ldap_search_st( ld, dnlist[i], LDAP_SCOPE_BASE,
+			NULL, def_attr, 0,
+			 &timeout, &res )) != LDAP_SUCCESS ) {
+			if ( rc == LDAP_NO_SUCH_OBJECT ) {
+				add_error( err, nerr, E_BADMEMBER, dnlist[i], NULL );
+				continue;
+			} else {
+				syslog( LOG_ALERT, "member search return 0x%x", rc );
+
+				unbind_and_exit( EX_TEMPFAIL );
+			}
+		} else {
+			if ( (e = ldap_first_entry( ld, res )) == NULL ) {
+				syslog( LOG_ALERT, "member search error parsing entry" );
+				unbind_and_exit( EX_TEMPFAIL );
+			}
+			if ( entry_engine( e, dnlist[i], address, to, nto,
+					   togroups, ngroups, err, nerr,
+					   USER | GROUP_MEMBERS ) ) {
+				resolved = 1;
+			}
+		}
+	}
+	return( resolved );
+}
+
+static int
+search_ldap_url(
+	char	*url,
+	Subst	*substs,
+	char	*address,
+	int	rdnpref,
+	int	multi_entry,
+	char	***to,
+	int	*nto,
+	Group	***togroups,
+	int	*ngroups,
+	Error	**err,
+	int	*nerr,
+	int	type
+)
+{
+	LDAPURLDesc	*ludp;
+	char		*p, *s, *d;
+	int		i;
+	char		filter[1024];
+	LDAPMessage	*e, *res;
+	int		rc;
+	char		**attrlist;
+	struct timeval	timeout;
+	int		match;
+	int		resolved = 0;
+	char		*dn;
+
+	timeout.tv_sec = MAIL500_TIMEOUT;
+	timeout.tv_usec = 0;
+
+	rc = ldap_url_parse( url, &ludp );
+	if ( rc ) {
+		switch ( rc ) {
+		case LDAP_URL_ERR_BADSCHEME:
+			syslog( LOG_ALERT,
+				"Not an LDAP URL: %s", url );
+			break;
+		case LDAP_URL_ERR_BADENCLOSURE:
+			syslog( LOG_ALERT,
+				"Bad Enclosure in URL: %s", url );
+			break;
+		case LDAP_URL_ERR_BADURL:
+			syslog( LOG_ALERT,
+				"Bad URL: %s", url );
+			break;
+		case LDAP_URL_ERR_BADHOST:
+			syslog( LOG_ALERT,
+				"Host is invalid in URL: %s", url );
+			break;
+		case LDAP_URL_ERR_BADATTRS:
+			syslog( LOG_ALERT,
+				"Attributes are invalid in URL: %s", url );
+			break;
+		case LDAP_URL_ERR_BADSCOPE:
+			syslog( LOG_ALERT,
+				"Scope is invalid in URL: %s", url );
+			break;
+		case LDAP_URL_ERR_BADFILTER:
+			syslog( LOG_ALERT,
+				"Filter is invalid in URL: %s", url );
+			break;
+		case LDAP_URL_ERR_BADEXTS:
+			syslog( LOG_ALERT,
+				"Extensions are invalid in URL: %s", url );
+			break;
+		case LDAP_URL_ERR_MEM:
+			syslog( LOG_ALERT,
+				"Out of memory parsing URL: %s", url );
+			break;
+		case LDAP_URL_ERR_PARAM:
+			syslog( LOG_ALERT,
+				"bad parameter parsing URL: %s", url );
+			break;
+		default:
+			syslog( LOG_ALERT,
+				"Unknown error %d parsing URL: %s",
+				rc, url );
+			break;
+		}
+		add_error( err, nerr, E_BADMEMBER,
+			   url, NULL );
+		return 0;
+	}
+
+	if ( substs ) {
+		for ( s = ludp->lud_filter, d = filter; *s; s++,d++ ) {
+			if ( *s == '%' ) {
+				s++;
+				if ( *s == '%' ) {
+					*d = '%';
+					continue;
+				}
+				for ( i = 0; substs[i].sub_char != '\0';
+				      i++ ) {
+					if ( *s == substs[i].sub_char ) {
+						for ( p = substs[i].sub_value;
+						      *p; p++,d++ ) {
+							*d = *p;
+						}
+						d--;
+						break;
+					}
+				}
+				if ( substs[i].sub_char == '\0' ) {
+					syslog( LOG_ALERT,
+						"unknown format %c", *s );
+				}
+			} else {
+				*d = *s;
+			}
+		}
+		*d = *s;
+	} else {
+		strncpy( filter, ludp->lud_filter, sizeof( filter ) - 1 );
+		filter[ sizeof( filter ) - 1 ] = '\0';
+	}
+
+	if ( ludp->lud_attrs ) {
+		attrlist = ludp->lud_attrs;
+	} else {
+		attrlist = def_attr;
+	}
+	res = NULL;
+	/* TBC: we don't read the host, dammit */
+	rc = ldap_search_st( ld, ludp->lud_dn, ludp->lud_scope,
+			     filter, attrlist, 0,
+			     &timeout, &res );
+
+	/* some other trouble - try again later */
+	if ( rc != LDAP_SUCCESS &&
+	     rc != LDAP_SIZELIMIT_EXCEEDED ) {
+		syslog( LOG_ALERT, "return 0x%x from X.500",
+			rc );
+		unbind_and_exit( EX_TEMPFAIL );
+	}
+
+	match = ldap_count_entries( ld, res );
+
+	/* trouble - try again later */
+	if ( match == -1 ) {
+		syslog( LOG_ALERT, "error parsing result from X.500" );
+		unbind_and_exit( EX_TEMPFAIL );
+	}
+
+	if ( match == 1 || multi_entry ) {
+		for ( e = ldap_first_entry( ld, res ); e != NULL;
+		      e = ldap_next_entry( ld, e ) ) {
+			dn = ldap_get_dn( ld, e );
+			resolved = entry_engine( e, dn, address, to, nto,
+						 togroups, ngroups,
+						 err, nerr, type );
+			if ( !resolved ) {
+				add_error( err, nerr, E_NOEMAIL, address, res );
+			}
+		}
+		return ( resolved );
+	}
+
+	/* more than one match - bounce with ambiguous user? */
+	if ( match > 1 ) {
+		LDAPMessage	*next, *tmpres = NULL;
+		char		*dn;
+		char		**xdn;
+
+		/* not giving rdn preference - bounce with ambiguous user */
+		if ( rdnpref == 0 ) {
+			add_error( err, nerr, E_AMBIGUOUS, address, res );
+			return 0;
+		}
+
+		/*
+		 * giving rdn preference - see if any entries were matched
+		 * because of their rdn.  If so, collect them to deal with
+		 * later (== 1 we deliver, > 1 we bounce).
+		 */
+
+		for ( e = ldap_first_entry( ld, res ); e != NULL; e = next ) {
+			next = ldap_next_entry( ld, e );
+			dn = ldap_get_dn( ld, e );
+			xdn = ldap_explode_dn( dn, 1 );
+
+			/* XXX bad, but how else can we do it? XXX */
+			if ( strcasecmp( xdn[0], address ) == 0 ) {
+				ldap_delete_result_entry( &res, e );
+				ldap_add_result_entry( &tmpres, e );
+			}
+
+			ldap_value_free( xdn );
+			free( dn );
+		}
+
+		/* nothing matched by rdn - go ahead and bounce */
+		if ( tmpres == NULL ) {
+			add_error( err, nerr, E_AMBIGUOUS, address, res );
+			return 0;
+
+		/* more than one matched by rdn - bounce with rdn matches */
+		} else if ( (match = ldap_count_entries( ld, tmpres )) > 1 ) {
+			add_error( err, nerr, E_AMBIGUOUS, address, tmpres );
+			return 0;
+
+		/* trouble... */
+		} else if ( match < 0 ) {
+			syslog( LOG_ALERT, "error parsing result from X.500" );
+			unbind_and_exit( EX_TEMPFAIL );
+		}
+
+		/* otherwise one matched by rdn - send to it */
+		ldap_msgfree( res );
+		res = tmpres;
+
+		/* trouble */
+		if ( (e = ldap_first_entry( ld, res )) == NULL ) {
+			syslog( LOG_ALERT, "error parsing entry from X.500" );
+			unbind_and_exit( EX_TEMPFAIL );
+		}
+
+		dn = ldap_get_dn( ld, e );
+
+		resolved = entry_engine( e, dn, address, to, nto,
+					 togroups, ngroups,
+					 err, nerr, type );
+		if ( !resolved ) {
+			add_error( err, nerr, E_NOEMAIL, address, res );
+			/* Don't free res if we passed it to add_error */
+		} else {
+			ldap_msgfree( res );
+		}
+	}
+	return( resolved );
+}
+
+static int
+url_list_search(
+	char	**urllist, 
+	char	*address,
+	int	multi_entry,
+	char	***to,
+	int	*nto,
+	Group	***togroups,
+	int	*ngroups,
+	Error	**err,
+	int	*nerr,
+	int	type
+)
+{
+	int		i;
+	int		resolved = 0;
+
+	for ( i = 0; urllist[i]; i++ ) {
+
+		if ( !strncasecmp( urllist[i], "mail:", 5 ) ) {
+			char	*vals[2];
+
+			vals[0] = urllist[i] + 5;
+			vals[1] = NULL;
+			add_to( to, nto, vals );
+			resolved = 1;
+
+		} else if ( ldap_is_ldap_url( urllist[i] ) ) {
+
+			resolved = search_ldap_url( urllist[i], NULL,
+						    address, 0, multi_entry,
+						    to, nto, togroups, ngroups,
+						    err, nerr, type );
+		} else {
+			/* Produce some sensible error here */
+			resolved = 0;
+		}
+	}
+	return( resolved );
+}
+
+/*
+ * We should probably take MX records into account to cover all bases,
+ * but really, routing belongs in the MTA.
+ */
+static int
+is_my_host(
+	char * host
+)
+{
+	char **d;
+
+	if ( myhosts == NULL )
+		return 0;
+	for ( d = myhosts; *d; d++ ) {
+		if ( !strcasecmp(*d,host) ) {
+			return 1;
+		}
+	}
+	return 0;
+}
+
+static int
+is_my_domain(
+	char * address
+)
+{
+	char **d;
+	char *p;
+
+	if ( mydomains == NULL )
+		return 0;
+	p = strchr( address, '@' );
+	if ( p == NULL)
+		return 0;
+	for ( d = mydomains; *d; d++ ) {
+		if ( !strcasecmp(*d,p+1) ) {
+			return 1;
+		}
+	}
+	return 0;
+}
+
+static void
+do_addresses(
+	char	**addresses,
+	char	***to,
+	int	*nto,
+	Group	***togroups,
+	int	*ngroups,
+	Error	**err,
+	int	*nerr,
+	int	type
+)
+{
+	int	i, j;
+	int	n;
+
+	/*
+	 * Well, this is tricky, every address in my_addresses will be
+	 * removed from the list while we shift the other values down
+	 * and we do it in a single scan of the address list and
+	 * without using additional memory.  We are going to be
+	 * modifying the value list in a way that the later
+	 * ldap_value_free works.
+	 */
+	j = 0;
+	for ( i = 0; addresses[i]; i++ ) {
+		if ( is_my_domain(addresses[i]) ) {
+			do_address( addresses[i], to, nto, togroups, ngroups,
+				    err, nerr, type );
+			ldap_memfree( addresses[i] );
+		} else {
+			if ( j < i ) {
+				addresses[j] = addresses[i];
+			}
+			j++;
+		}
+	}
+	addresses[j] = NULL;
+	if ( addresses[0] ) {
+		add_to( to, nto, addresses );
+	}
+}
+
+/*
+ * The entry engine processes an entry.  Normally, each entry will resolve
+ * to one or more values that will be added to the 'to' argument.  This
+ * argument needs not be the global 'to' list, it may be the g_to field
+ * in a group.  Groups have no special treatment, unless they require
+ * a special sender.
+ */
+
+static int
+entry_engine(
+	LDAPMessage *e,
+	char	*dn,
+	char	*address,
+	char	***to,
+	int	*nto,
+	Group	***togroups,
+	int	*ngroups,
+	Error	**err,
+	int	*nerr,
+	int	type
+)
+{
+	char	**vals;
+	int	i;
+	int	resolved = 0;
+	char	***current_to = to;
+	int	*current_nto = nto;
+	Group	*current_group = NULL;
+	char	buf[1024];
+	char	*localpart = NULL, *domainpart = NULL;
+	Subst	substs[2];
+	int	cur_priority = 0;
+	char	*route_to_host = NULL;
+	char	*route_to_address = NULL;
+	int	needs_mta_routing = 0;
+	char	**own_addresses = NULL;
+	int	own_addresses_total = 0;
+	char	**delivery_types = NULL;
+	int	delivery_types_total = 0;
+	char	*nvals[2];
+
+	for ( i=0; attr_semantics[i] != NULL; i++ ) {
+		AttrSemantics	*as = attr_semantics[i];
+		int		nent;
+		int		j;
+
+		if ( as->as_priority < cur_priority ) {
+			/*
+			 * We already got higher priority information,
+			 * so no further work to do, ignore the rest.
+			 */
+			break;
+		}
+		vals = ldap_get_values( ld, e, as->as_name );
+		if ( !vals || vals[0] == NULL ) {
+			continue;
+		}
+		nent = count_values( vals );
+		if ( nent > 1 && !as->as_m_valued ) {
+			add_error( err, nerr, E_AMBIGUOUS, address, e );
+			return( 0 );
+		}
+		switch ( as->as_kind ) {
+		case AS_KIND_RECIPIENT:
+			cur_priority = as->as_priority;
+			if ( ! ( type & ( USER | GROUP_MEMBERS ) ) )
+				break;
+			switch ( as->as_syntax ) {
+			case AS_SYNTAX_RFC822:
+				do_addresses( vals, current_to, current_nto,
+					      togroups, ngroups, err, nerr,
+					      USER );
+				resolved = 1;
+				break;
+			case AS_SYNTAX_RFC822_EXT:
+				do_addresses( vals, current_to, current_nto,
+					      togroups, ngroups, err, nerr,
+					      USER );
+				resolved = 1;
+				break;
+			case AS_SYNTAX_NATIVE_MB:
+				/* We used to concatenate mailHost if set here */
+				/*
+				 * We used to send a copy to the vacation host
+				 * if onVacation to uid@vacationhost
+				 */
+				if ( as->as_param ) {
+					for ( j=0; j<delivery_types_total; j++ ) {
+						if ( !strcasecmp( as->as_param, delivery_types[j] ) ) {
+							add_to( current_to, current_nto, vals );
+							resolved = 1;
+							break;
+						}
+					}
+				} else {
+					add_to( current_to, current_nto, vals );
+					resolved = 1;
+				}
+				break;
+
+			case AS_SYNTAX_DN:
+				if ( dn_search( vals, address,
+						current_to, current_nto,
+						togroups, ngroups,
+						err, nerr ) ) {
+					resolved = 1;
+				}
+				break;
+
+			case AS_SYNTAX_URL:
+				if ( url_list_search( vals, address,
+						 as->as_m_entries,
+						 current_to, current_nto,
+						 togroups, ngroups,
+						 err, nerr, type ) ) {
+					resolved = 1;
+				}
+				break;
+
+			case AS_SYNTAX_BOOL_FILTER:
+				if ( strcasecmp( vals[0], "true" ) ) {
+					break;
+				}
+				substs[0].sub_char = 'D';
+				substs[0].sub_value = dn;
+				substs[1].sub_char = '\0';
+				substs[1].sub_value = NULL;
+				if ( url_list_search( vals, address,
+						 as->as_m_entries,
+						 current_to, current_nto,
+						 togroups, ngroups,
+						 err, nerr, type ) ) {
+					resolved = 1;
+				}
+				break;
+
+			default:
+				syslog( LOG_ALERT,
+					"Invalid syntax %d for kind %d",
+					as->as_syntax, as->as_kind );
+				break;
+			}
+			break;
+
+		case AS_KIND_ERRORS:
+			cur_priority = as->as_priority;
+			/* This is a group with special processing */
+			if ( type & GROUP_ERRORS ) {
+				switch (as->as_kind) {
+				case AS_SYNTAX_RFC822:
+					add_to( current_to, current_nto, vals );
+					resolved = 1;
+					break;
+				case AS_SYNTAX_URL:
+				default:
+					syslog( LOG_ALERT,
+						"Invalid syntax %d for kind %d",
+						as->as_syntax, as->as_kind );
+				}
+			} else {
+				current_group = new_group( dn, togroups,
+							   ngroups );
+				if ( ! current_group )
+					/*
+					 * We have already considered
+					 * this group, so we just
+					 * return resolved.
+					 */
+					return 1;
+				current_to = &current_group->g_members;
+				current_nto = &current_group->g_nmembers;
+				split_address( address,
+					       &localpart, &domainpart );
+				if ( domainpart ) {
+					sprintf( buf, "%s-%s@%s",
+						 localpart, ERRORS,
+						 domainpart );
+					free( localpart );
+					free( domainpart );
+				} else {
+					sprintf( buf, "%s-%s@%s",
+						 localpart, ERRORS,
+						 host );
+					free( localpart );
+				}
+				current_group->g_errorsto = strdup( buf );
+			}
+			break;
+
+		case AS_KIND_REQUEST:
+			cur_priority = as->as_priority;
+			/* This is a group with special processing */
+			if ( type & GROUP_REQUEST ) {
+				add_to( current_to, current_nto, vals );
+				resolved = 1;
+			}
+			break;
+
+		case AS_KIND_OWNER:
+			cur_priority = as->as_priority;
+			/* This is a group with special processing */
+			if ( type & GROUP_REQUEST ) {
+				add_to( current_to, current_nto, vals );
+				resolved = 1;
+			}
+			break;
+
+		case AS_KIND_ROUTE_TO_HOST:
+			if ( !is_my_host( vals[0] ) ) {
+				cur_priority = as->as_priority;
+				if ( as->as_syntax == AS_SYNTAX_PRESENT ) {
+					needs_mta_routing = 1;
+				} else {
+					route_to_host = strdup( vals[0] );
+				}
+			}
+			break;
+
+		case AS_KIND_ROUTE_TO_ADDR:
+			for ( j=0; j<own_addresses_total; j++ ) {
+				if ( strcasecmp( vals[0], own_addresses[j] ) ) {
+					cur_priority = as->as_priority;
+					if ( as->as_syntax == AS_SYNTAX_PRESENT ) {
+						needs_mta_routing = 1;
+					} else {
+						route_to_address = strdup( vals[0] );
+					}
+				}
+				break;
+			}
+
+		case AS_KIND_OWN_ADDR:
+			add_to( &own_addresses, &own_addresses_total, vals );
+			cur_priority = as->as_priority;
+			break;
+
+		case AS_KIND_DELIVERY_TYPE:
+			add_to( &delivery_types, &delivery_types_total, vals );
+			cur_priority = as->as_priority;
+			break;
+
+		default:
+			syslog( LOG_ALERT,
+				"Invalid kind %d", as->as_kind );
+			/* Error, TBC */
+		}
+		ldap_value_free( vals );
+	}
+	/*
+	 * Now check if we are dealing with mail routing.  We support
+	 * two modes.
+	 *
+	 * The first mode and by far the most robust method is doing
+	 * routing at the MTA.  In this case, we just checked if the
+	 * routing attributes were present and did not seem like
+	 * pointing to ourselves.  The only thing we have to do here
+	 * is adding to the recipient list any of the RFC822 addresses
+	 * of this entry.  That means we needed to retrieve them from
+	 * the entry itself because we might have arrived here through
+	 * some directory search.  The address received as argument is
+	 * not the address of the entry we are processing, but rather
+	 * the RFC822 address we are expanding now.  Unfortunately,
+	 * this requires an MTA that understands LDAP routing.
+	 * Sendmail 8.10.0 does, if compiled properly.
+	 *
+	 * The second method, that is most emphatically not recommended
+	 * is routing in maildap.  This is going to require using the
+	 * percent hack.  Moreover, this may occasionally loop.
+	 */
+	if ( needs_mta_routing ) {
+		if ( !own_addresses ) {
+			add_error( err, nerr, E_NOOWNADDRESS, address, e );
+			return( 0 );
+		}
+		nvals[0] = own_addresses[0];	/* Anyone will do */
+		nvals[1] = NULL;
+		add_to( current_to, current_nto, nvals );
+		resolved = 1;
+	} else if ( route_to_host ) {
+		char *p;
+		if ( !route_to_address ) {
+			if ( !own_addresses ) {
+				add_error( err, nerr, E_NOOWNADDRESS, address, e );
+				return( 0 );
+			}
+			route_to_address = strdup( own_addresses[0] );
+		}
+		/* This makes use of the percent hack, but there's no choice */
+		p = strchr( route_to_address, '@' );
+		if ( p ) {
+			*p = '%';
+		}
+		sprintf( buf, "%s@%s", route_to_address, route_to_host );
+		nvals[0] = buf;
+		nvals[1] = NULL;
+		add_to( current_to, current_nto, nvals );
+		resolved = 1;
+		free( route_to_host );
+		free( route_to_address );
+	} else if ( route_to_address ) {
+		nvals[0] = route_to_address;
+		nvals[1] = NULL;
+		add_to( current_to, current_nto, nvals );
+		resolved = 1;
+		free( route_to_address );
+	}
+	if ( own_addresses ) {
+		ldap_value_free( own_addresses );
+	}
+	if ( delivery_types ) {
+		ldap_value_free( delivery_types );
+	}
+		  
+	return( resolved );
+}
+
+static int
+search_bases(
+	char	*filter,
+	Subst	*substs,
+	char	*name,
+	char	***to,
+	int	*nto,
+	Group	***togroups,
+	int	*ngroups,
+	Error	**err,
+	int	*nerr,
+	int	type
+)
+{
+	int		b, resolved = 0;
+
+	for ( b = 0; base[b] != NULL; b++ ) {
+
+		if ( ! (base[b]->b_search & type) ) {
+			continue;
+		}
+
+		resolved = search_ldap_url( base[b]->b_url, substs, name,
+					    base[b]->b_rdnpref,
+					    base[b]->b_m_entries,
+					    to, nto, togroups, ngroups,
+					    err, nerr, type );
+		if ( resolved )
+			break;
+	}
+	return( resolved );
+}
+
+static void
+do_address(
+	char	*name,
+	char	***to,
+	int	*nto,
+	Group	***togroups,
+	int	*ngroups,
+	Error	**err,
+	int	*nerr,
+	int	type
+)
+{
+	char		*localpart = NULL, *domainpart = NULL;
+	char		*synthname = NULL;
+	int		resolved;
+	int		i;
+	Subst		substs[6];
+
+	/*
+	 * Look up the name in X.500, add the appropriate addresses found
+	 * to the to list, or to the err list in case of error.  Groups are
+	 * handled by the do_group routine, individuals are handled here.
+	 * When looking up name, we follow the bases hierarchy, looking
+	 * in base[0] first, then base[1], etc.  For each base, there is
+	 * a set of search filters to try, in order.  If something goes
+	 * wrong here trying to contact X.500, we exit with EX_TEMPFAIL.
+	 * If the b_rdnpref flag is set, then we give preference to entries
+	 * that matched name because it's their rdn, otherwise not.
+	 */
+
+	split_address( name, &localpart, &domainpart );
+	synthname = strdup( localpart );
+	for ( i = 0; synthname[i] != '\0'; i++ ) {
+		if ( synthname[i] == '.' || synthname[i] == '_' )
+			synthname[i] = ' ';
+	}
+	substs[0].sub_char = 'm';
+	substs[0].sub_value = name;
+	substs[1].sub_char = 'h';
+	substs[1].sub_value = host;
+	substs[2].sub_char = 'l';
+	substs[2].sub_value = localpart;
+	substs[3].sub_char = 'd';
+	substs[3].sub_value = domainpart;
+	substs[4].sub_char = 's';
+	substs[4].sub_value = synthname;
+	substs[5].sub_char = '\0';
+	substs[5].sub_value = NULL;
+
+	resolved = search_bases( NULL, substs, name,
+				 to, nto, togroups, ngroups,
+				 err, nerr, type );
+
+	if ( localpart ) {
+		free( localpart );
+	}
+	if ( domainpart ) {
+		free( domainpart );
+	}
+	if ( synthname ) {
+		free( synthname );
+	}
+
+	if ( !resolved ) {
+		/* not resolved - bounce with user unknown */
+		if ( type == USER ) {
+			add_error( err, nerr, E_USERUNKNOWN, name, NULL );
+		} else {
+			add_error( err, nerr, E_GROUPUNKNOWN, name, NULL );
+		}
+	}
+}
+
+static void
+send_message( char **to )
+{
+	int	pid;
+#ifndef HAVE_WAITPID
+	WAITSTATUSTYPE	status;
+#endif
+
+	if ( debug ) {
+		char	buf[1024];
+		int	i;
+
+		strcpy( buf, to[0] );
+		for ( i = 1; to[i] != NULL; i++ ) {
+			strcat( buf, " " );
+			strcat( buf, to[i] );
+		}
+
+		syslog( LOG_ALERT, "send_message execing sendmail: (%s)", buf );
+	}
+
+	/* parent */
+	if ( (pid = fork()) != 0 ) {
+#ifdef HAVE_WAITPID
+		waitpid( pid, (int *) NULL, 0 );
+#else
+		wait4( pid, &status, WAIT_FLAGS, 0 );
+#endif
+	/* child */
+	} else {
+		/* to includes sendmailargs */
+		execv( MAIL500_SENDMAIL, to );
+
+		syslog( LOG_ALERT, "execv failed" );
+
+		exit( EX_TEMPFAIL );
+	}
+}
+
+static void
+send_group( Group **group, int ngroup )
+{
+	int	i, pid;
+	char	**argv;
+	int	argc;
+	char	*iargv[7];
+#ifndef HAVE_WAITPID
+	WAITSTATUSTYPE	status;
+#endif
+
+	for ( i = 0; i < ngroup; i++ ) {
+		(void) rewind( stdin );
+
+		iargv[0] = MAIL500_SENDMAIL;
+		iargv[1] = "-f";
+		iargv[2] = group[i]->g_errorsto;
+		iargv[3] = "-oMrX.500";
+		iargv[4] = "-odi";
+		iargv[5] = "-oi";
+		iargv[6] = NULL;
+
+		argv = NULL;
+		argc = 0;
+		add_to( &argv, &argc, iargv );
+		add_to( &argv, &argc, group[i]->g_members );
+
+		if ( debug ) {
+			char	buf[1024];
+			int	i;
+
+			strcpy( buf, argv[0] );
+			for ( i = 1; i < argc; i++ ) {
+				strcat( buf, " " );
+				strcat( buf, argv[i] );
+			}
+
+			syslog( LOG_ALERT, "execing sendmail: (%s)", buf );
+		}
+
+		/* parent */
+		if ( (pid = fork()) != 0 ) {
+#ifdef HAVE_WAITPID
+			waitpid( pid, (int *) NULL, 0 );
+#else
+			wait4( pid, &status, WAIT_FLAGS, 0 );
+#endif
+		/* child */
+		} else {
+			execv( MAIL500_SENDMAIL, argv );
+
+			syslog( LOG_ALERT, "execv failed" );
+
+			exit( EX_TEMPFAIL );
+		}
+	}
+}
+
+static void
+send_errors( Error *err, int nerr )
+{
+	int	pid, i, namelen;
+	FILE	*fp;
+	int	fd[2];
+	char	*argv[8];
+	char	buf[1024];
+#ifndef HAVE_WAITPID
+	WAITSTATUSTYPE	status;
+#endif
+
+	if ( strcmp( MAIL500_BOUNCEFROM, mailfrom ) == 0 ) {
+	    mailfrom = errorsfrom;
+	}
+
+	argv[0] = MAIL500_SENDMAIL;
+	argv[1] = "-oMrX.500";
+	argv[2] = "-odi";
+	argv[3] = "-oi";
+	argv[4] = "-f";
+	argv[5] = MAIL500_BOUNCEFROM;
+	argv[6] = mailfrom;
+	argv[7] = NULL;
+
+	if ( debug ) {
+		int	i;
+
+		strcpy( buf, argv[0] );
+		for ( i = 1; argv[i] != NULL; i++ ) {
+			strcat( buf, " " );
+			strcat( buf, argv[i] );
+		}
+
+		syslog( LOG_ALERT, "execing sendmail: (%s)", buf );
+	}
+
+	if ( pipe( fd ) == -1 ) {
+		syslog( LOG_ALERT, "cannot create pipe" );
+		exit( EX_TEMPFAIL );
+	}
+
+	if ( (pid = fork()) != 0 ) {
+		if ( (fp = fdopen( fd[1], "w" )) == NULL ) {
+			syslog( LOG_ALERT, "cannot fdopen pipe" );
+			exit( EX_TEMPFAIL );
+		}
+
+		fprintf( fp, "To: %s\n", mailfrom );
+		fprintf( fp, "From: %s\n", errorsfrom );
+		fprintf( fp, "Subject: undeliverable mail\n" );
+		fprintf( fp, "\n" );
+		fprintf( fp, "The following errors occurred when trying to deliver the attached mail:\n" );
+		for ( i = 0; i < nerr; i++ ) {
+			namelen = strlen( err[i].e_addr );
+			fprintf( fp, "\n" );
+
+			switch ( err[i].e_code ) {
+			case E_USERUNKNOWN:
+				fprintf( fp, "%s: User unknown\n", err[i].e_addr );
+				break;
+
+			case E_GROUPUNKNOWN:
+				fprintf( fp, "%s: Group unknown\n", err[i].e_addr );
+				break;
+
+			case E_BADMEMBER:
+				fprintf( fp, "%s: Group member does not exist\n",
+				    err[i].e_addr );
+				fprintf( fp, "This could be because the distinguished name of the person has changed\n" );
+				fprintf( fp, "If this is the case, the problem can be solved by removing and\n" );
+				fprintf( fp, "then re-adding the person to the group.\n" );
+				break;
+
+			case E_NOREQUEST:
+				fprintf( fp, "%s: Group exists but has no request address\n",
+				    err[i].e_addr );
+				break;
+
+			case E_NOERRORS:
+				fprintf( fp, "%s: Group exists but has no errors-to address\n",
+				    err[i].e_addr );
+				break;
+
+			case E_NOOWNER:
+				fprintf( fp, "%s: Group exists but has no owner\n",
+				    err[i].e_addr );
+				break;
+
+			case E_AMBIGUOUS:
+				do_ambiguous( fp, &err[i], namelen );
+				break;
+
+			case E_NOEMAIL:
+				do_noemail( fp, &err[i], namelen );
+				break;
+
+			case E_MEMBERNOEMAIL:
+				fprintf( fp, "%s: Group member exists but does not have an email address\n",
+				    err[i].e_addr );
+				break;
+
+			case E_JOINMEMBERNOEMAIL:
+				fprintf( fp, "%s: User has joined group but does not have an email address\n",
+				    err[i].e_addr );
+				break;
+
+			case E_LOOP:
+				fprintf( fp, "%s: User has created a mail loop by adding address %s to their X.500 entry\n",
+				    err[i].e_addr, err[i].e_loop );
+				break;
+
+			case E_NOMEMBERS:
+				fprintf( fp, "%s: Group has no members\n",
+				    err[i].e_addr );
+				break;
+
+			case E_NOOWNADDRESS:
+				fprintf( fp, "%s: Not enough information to perform required routing\n",
+				    err[i].e_addr );
+				break;
+
+			default:
+				syslog( LOG_ALERT, "unknown error %d", err[i].e_code );
+				unbind_and_exit( EX_TEMPFAIL );
+				break;
+			}
+		}
+
+		fprintf( fp, "\n------- The original message sent:\n\n" );
+
+		while ( fgets( buf, sizeof(buf), stdin ) != NULL ) {
+			fputs( buf, fp );
+		}
+		fclose( fp );
+
+#ifdef HAVE_WAITPID
+		waitpid( pid, (int *) NULL, 0 );
+#else
+		wait4( pid, &status, WAIT_FLAGS, 0 );
+#endif
+	} else {
+		dup2( fd[0], 0 );
+
+		execv( MAIL500_SENDMAIL, argv );
+
+		syslog( LOG_ALERT, "execv failed" );
+
+		exit( EX_TEMPFAIL );
+	}
+}
+
+static void
+do_noemail( FILE *fp, Error *err, int namelen )
+{
+	int		i, last;
+	char		*dn, *rdn;
+	char		**ufn, **vals;
+
+	fprintf(fp, "%s: User has no email address registered.\n",
+	    err->e_addr );
+	fprintf( fp, "%*s  Name, title, postal address and phone for '%s':\n\n",
+	    namelen, " ", err->e_addr );
+
+	/* name */
+	dn = ldap_get_dn( ld, err->e_msg );
+	ufn = ldap_explode_dn( dn, 1 );
+	rdn = strdup( ufn[0] );
+	if ( strcasecmp( rdn, err->e_addr ) == 0 ) {
+		if ( (vals = ldap_get_values( ld, err->e_msg, "cn" ))
+		    != NULL ) {
+			for ( i = 0; vals[i]; i++ ) {
+				last = strlen( vals[i] ) - 1;
+				if ( isdigit((unsigned char) vals[i][last]) ) {
+					rdn = strdup( vals[i] );
+					break;
+				}
+			}
+
+			ldap_value_free( vals );
+		}
+	}
+	fprintf( fp, "%*s  %s\n", namelen, " ", rdn );
+	free( dn );
+	free( rdn );
+	ldap_value_free( ufn );
+
+	/* titles or descriptions */
+	if ( (vals = ldap_get_values( ld, err->e_msg, "title" )) == NULL &&
+	    (vals = ldap_get_values( ld, err->e_msg, "description" ))
+	    == NULL ) {
+		fprintf( fp, "%*s  No title or description registered\n",
+		    namelen, " " );
+	} else {
+		for ( i = 0; vals[i] != NULL; i++ ) {
+			fprintf( fp, "%*s  %s\n", namelen, " ", vals[i] );
+		}
+
+		ldap_value_free( vals );
+	}
+
+	/* postal address */
+	if ( (vals = ldap_get_values( ld, err->e_msg, "postalAddress" ))
+	    == NULL ) {
+		fprintf( fp, "%*s  No postal address registered\n", namelen,
+		    " " );
+	} else {
+		fprintf( fp, "%*s  ", namelen, " " );
+		for ( i = 0; vals[0][i] != '\0'; i++ ) {
+			if ( vals[0][i] == '$' ) {
+				fprintf( fp, "\n%*s  ", namelen, " " );
+				while ( isspace((unsigned char) vals[0][i+1]) )
+					i++;
+			} else {
+				fprintf( fp, "%c", vals[0][i] );
+			}
+		}
+		fprintf( fp, "\n" );
+
+		ldap_value_free( vals );
+	}
+
+	/* telephone number */
+	if ( (vals = ldap_get_values( ld, err->e_msg, "telephoneNumber" ))
+	    == NULL ) {
+		fprintf( fp, "%*s  No phone number registered\n", namelen,
+		    " " );
+	} else {
+		for ( i = 0; vals[i] != NULL; i++ ) {
+			fprintf( fp, "%*s  %s\n", namelen, " ", vals[i] );
+		}
+
+		ldap_value_free( vals );
+	}
+}
+
+/* ARGSUSED */
+static void
+do_ambiguous( FILE *fp, Error *err, int namelen )
+{
+	int		i, last;
+	char		*dn, *rdn;
+	char		**ufn, **vals;
+	LDAPMessage	*e;
+
+	i = ldap_result2error( ld, err->e_msg, 0 );
+
+	fprintf( fp, "%s: Ambiguous user.  %s%d matches found:\n\n",
+	    err->e_addr, i == LDAP_SIZELIMIT_EXCEEDED ? "First " : "",
+	    ldap_count_entries( ld, err->e_msg ) );
+
+	for ( e = ldap_first_entry( ld, err->e_msg ); e != NULL;
+	    e = ldap_next_entry( ld, e ) ) {
+		dn = ldap_get_dn( ld, e );
+		ufn = ldap_explode_dn( dn, 1 );
+		rdn = strdup( ufn[0] );
+		if ( strcasecmp( rdn, err->e_addr ) == 0 ) {
+			if ( (vals = ldap_get_values( ld, e, "cn" )) != NULL ) {
+				for ( i = 0; vals[i]; i++ ) {
+					last = strlen( vals[i] ) - 1;
+					if (isdigit((unsigned char) vals[i][last])) {
+						rdn = strdup( vals[i] );
+						break;
+					}
+				}
+
+				ldap_value_free( vals );
+			}
+		}
+
+		/* 
+		if ( isgroup( e ) ) {
+			vals = ldap_get_values( ld, e, "description" );
+		} else {
+			vals = ldap_get_values( ld, e, "title" );
+		}
+		*/
+		vals = ldap_get_values( ld, e, "description" );
+
+		fprintf( fp, "    %-20s %s\n", rdn, vals ? vals[0] : "" );
+		for ( i = 1; vals && vals[i] != NULL; i++ ) {
+			fprintf( fp, "                         %s\n", vals[i] );
+		}
+
+		free( dn );
+		free( rdn );
+		ldap_value_free( ufn );
+		if ( vals != NULL )
+			ldap_value_free( vals );
+	}
+}
+
+static int
+count_values( char **list )
+{
+	int	i;
+
+	for ( i = 0; list && list[i] != NULL; i++ )
+		;	/* NULL */
+
+	return( i );
+}
+
+static void
+add_to( char ***list, int *nlist, char **new )
+{
+	int	i, nnew, oldnlist;
+
+	nnew = count_values( new );
+
+	oldnlist = *nlist;
+	if ( *list == NULL || *nlist == 0 ) {
+		*list = (char **) malloc( (nnew + 1) * sizeof(char *) );
+		*nlist = nnew;
+	} else {
+		*list = (char **) realloc( *list, *nlist * sizeof(char *) +
+		    nnew * sizeof(char *) + sizeof(char *) );
+		*nlist += nnew;
+	}
+
+	for ( i = 0; i < nnew; i++ )
+		(*list)[i + oldnlist] = strdup( new[i] );
+	(*list)[*nlist] = NULL;
+}
+
+static void
+add_single_to( char ***list, char *new )
+{
+	int	nlist;
+
+	if ( *list == NULL ) {
+		nlist = 0;
+		*list = (char **) malloc( 2 * sizeof(char *) );
+	} else {
+		nlist = count_values( *list );
+		*list = (char **) realloc( *list,
+					   ( nlist + 2 ) * sizeof(char *) );
+	}
+
+	(*list)[nlist] = strdup( new );
+	(*list)[nlist+1] = NULL;
+}
+
+static int
+isgroup( LDAPMessage *e )
+{
+	int	i, j;
+	char	**oclist;
+
+	if ( !groupclasses ) {
+		return( 0 );
+	}
+
+	oclist = ldap_get_values( ld, e, "objectClass" );
+
+	for ( i = 0; oclist[i] != NULL; i++ ) {
+		for ( j = 0; groupclasses[j] != NULL; j++ ) {
+			if ( strcasecmp( oclist[i], groupclasses[j] ) == 0 ) {
+				ldap_value_free( oclist );
+				return( 1 );
+			}
+		}
+	}
+	ldap_value_free( oclist );
+
+	return( 0 );
+}
+
+static void
+add_error( Error **err, int *nerr, int code, char *addr, LDAPMessage *msg )
+{
+	if ( *nerr == 0 ) {
+		*err = (Error *) malloc( sizeof(Error) );
+	} else {
+		*err = (Error *) realloc( *err, (*nerr + 1) * sizeof(Error) );
+	}
+
+	(*err)[*nerr].e_code = code;
+	(*err)[*nerr].e_addr = strdup( addr );
+	(*err)[*nerr].e_msg = msg;
+	(*nerr)++;
+}
+
+static void
+unbind_and_exit( int rc )
+{
+	int	i;
+
+	if ( (i = ldap_unbind( ld )) != LDAP_SUCCESS )
+		syslog( LOG_ALERT, "ldap_unbind failed %d\n", i );
+
+	exit( rc );
+}
diff --git a/clients/tools/ldapmodify.c b/clients/tools/ldapmodify.c
index d09b9ad8398b4ece77746f25347a9f5b25db4f40..f55cb80aa15d2bc2bf83bfc057d768979e58e4b8 100644
--- a/clients/tools/ldapmodify.c
+++ b/clients/tools/ldapmodify.c
@@ -1,40 +1,61 @@
+/* $OpenLDAP$ */
+/*
+ * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
 /* ldapmodify.c - generic program to modify or add entries using LDAP */
 
+#include "portable.h"
+
 #include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <sys/types.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/ctype.h>
+#include <ac/signal.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+
+#ifdef HAVE_SYS_STAT_H
 #include <sys/stat.h>
+#endif
+
+#ifdef HAVE_SYS_FILE_H
 #include <sys/file.h>
+#endif
+#ifdef HAVE_FCNTL_H
 #include <fcntl.h>
-#ifndef VMS
-#include <unistd.h>
-#endif /* VMS */
-#include <lber.h>
+#endif
+
 #include <ldap.h>
-#include <ldif.h>
 
-#include "ldapconfig.h"
+#include "lutil_ldap.h"
+#include "ldif.h"
+#include "ldap_defaults.h"
+#include "ldap_log.h"
 
 static char	*prog;
-static char	*binddn = LDAPMODIFY_BINDDN;
-static char	*passwd = NULL;
-static char	*ldaphost = LDAPHOST;
-static int	ldapport = LDAP_PORT;
-static int	new, replace, not, verbose, contoper, force, valsfromfiles;
-static LDAP	*ld;
-
-#ifdef LDAP_DEBUG
-extern int ldap_debug, lber_debug;
-#endif /* LDAP_DEBUG */
-
-#define safe_realloc( ptr, size )	( ptr == NULL ? malloc( size ) : \
-					 realloc( ptr, size ))
+static char	*binddn = NULL;
+static struct berval passwd = { 0, NULL };
+static char *ldapuri = NULL;
+static char	*ldaphost = NULL;
+static int	ldapport = 0;
+#ifdef HAVE_CYRUS_SASL
+static unsigned sasl_flags = LDAP_SASL_AUTOMATIC;
+static char *sasl_realm = NULL;
+static char	*sasl_authc_id = NULL;
+static char	*sasl_authz_id = NULL;
+static char	*sasl_mech = NULL;
+static char	*sasl_secprops = NULL;
+#endif
+static int	use_tls = 0;
+static int	ldapadd, not, verbose, contoper, force;
+static LDAP	*ld = NULL;
 
 #define LDAPMOD_MAXLINE		4096
 
 /* strings found in replog/LDIF entries (mostly lifted from slurpd/slurp.h) */
+#define T_VERSION_STR		"version"
 #define T_REPLICA_STR		"replica"
 #define T_DN_STR		"dn"
 #define T_CHANGETYPESTR         "changetype"
@@ -42,228 +63,700 @@ extern int ldap_debug, lber_debug;
 #define T_MODIFYCTSTR		"modify"
 #define T_DELETECTSTR		"delete"
 #define T_MODRDNCTSTR		"modrdn"
+#define T_MODDNCTSTR		"moddn"
+#define T_RENAMECTSTR		"rename"
 #define T_MODOPADDSTR		"add"
 #define T_MODOPREPLACESTR	"replace"
 #define T_MODOPDELETESTR	"delete"
 #define T_MODSEPSTR		"-"
 #define T_NEWRDNSTR		"newrdn"
 #define T_DELETEOLDRDNSTR	"deleteoldrdn"
+#define T_NEWSUPSTR		"newsuperior"
+
+
+static void usage LDAP_P(( const char *prog )) LDAP_GCCATTR((noreturn));
+static int process_ldif_rec LDAP_P(( char *rbuf, int count ));
+static void addmodifyop LDAP_P((
+	LDAPMod ***pmodsp, int modop,
+	const char *attr,
+	struct berval *value ));
+static int domodify LDAP_P((
+	const char *dn,
+	LDAPMod **pmods,
+	int newentry ));
+static int dodelete LDAP_P((
+	const char *dn ));
+static int dorename LDAP_P((
+	const char *dn,
+	const char *newrdn,
+	const char *newsup,
+	int deleteoldrdn ));
+static char *read_one_record LDAP_P(( FILE *fp ));
+
+static void
+usage( const char *prog )
+{
+    fprintf( stderr,
+"Add or modify entries from an LDAP server\n\n"
+"usage: %s [options]\n"
+"	The list of desired operations are read from stdin or from the file\n"
+"	specified by \"-f file\".\n"
+"Add or modify options:\n"
+"  -a         add values (default%s)\n"
+"  -F         force all changes records to be used\n"
+
+"Common options:\n"
+"  -d level   set LDAP debugging level to `level'\n"
+"  -D binddn  bind DN\n"
+"  -f file    read operations from `file'\n"
+"  -h host    LDAP server\n"
+"  -H URI     LDAP Uniform Resource Indentifier(s)\n"
+"  -I         use SASL Interactive mode\n"
+"  -k         use Kerberos authentication\n"
+"  -K         like -k, but do only step 1 of the Kerberos bind\n"
+"  -M         enable Manage DSA IT control (-MM to make critical)\n"
+"  -n         show what would be done but don't actually update\n"
+"  -O props   SASL security properties\n"
+"  -p port    port on LDAP server\n"
+"  -P version procotol version (default: 3)\n"
+"  -Q         use SASL Quiet mode\n"
+"  -R realm   SASL realm\n"
+"  -U authcid SASL authentication identity\n"
+"  -v         run in verbose mode (diagnostics to standard output)\n"
+"  -w passwd  bind passwd (for simple authentication)\n"
+"  -W         prompt for bind passwd\n"
+"  -x         Simple authentication\n"
+"  -X authzid SASL authorization identity (\"dn:<dn>\" or \"u:<user>\")\n"
+"  -Y mech    SASL mechanism\n"
+"  -Z         Start TLS request (-ZZ to require successful response)\n"
+	     , prog, (strcmp( prog, "ldapadd" ) ? " is to replace" : "") );
+
+    exit( EXIT_FAILURE );
+}
 
 
-#ifdef NEEDPROTOS
-static int process_ldapmod_rec( char *rbuf );
-static int process_ldif_rec( char *rbuf );
-static void addmodifyop( LDAPMod ***pmodsp, int modop, char *attr,
-	char *value, int vlen );
-static int domodify( char *dn, LDAPMod **pmods, int newentry );
-static int dodelete( char *dn );
-static int domodrdn( char *dn, char *newrdn, int deleteoldrdn );
-static void freepmods( LDAPMod **pmods );
-static int fromfile( char *path, struct berval *bv );
-static char *read_one_record( FILE *fp );
-#else /* NEEDPROTOS */
-static int process_ldapmod_rec();
-static int process_ldif_rec();
-static void addmodifyop();
-static int domodify();
-static int dodelete();
-static int domodrdn();
-static void freepmods();
-static int fromfile();
-static char *read_one_record();
-#endif /* NEEDPROTOS */
-
-
-main( argc, argv )
-    int		argc;
-    char	**argv;
+int
+main( int argc, char **argv )
 {
-    char		*infile, *rbuf, *start, *p, *q;
+    char		*infile, *rbuf, *start;
     FILE		*fp;
-    int			rc, i, kerberos, use_ldif, authmethod;
-    char		*usage = "usage: %s [-abcknrvF] [-d debug-level] [-h ldaphost] [-p ldapport] [-D binddn] [-w passwd] [ -f file | < entryfile ]\n";
+	int		rc, i, authmethod, version, want_bindpw, debug, manageDSAit, referrals;
+	int count;
 
-    extern char	*optarg;
-    extern int	optind;
-
-    if (( prog = strrchr( argv[ 0 ], '/' )) == NULL ) {
+    if (( prog = strrchr( argv[ 0 ], *LDAP_DIRSEP )) == NULL ) {
 	prog = argv[ 0 ];
     } else {
 	++prog;
     }
-    new = ( strcmp( prog, "ldapadd" ) == 0 );
+
+    /* Print usage when no parameters */
+    if( argc < 2 ) usage( prog );
+
+	/* strncmp instead of strcmp since NT binaries carry .exe extension */
+    ldapadd = ( strncmp( prog, "ldapadd", sizeof("ldapadd")-1 ) == 0 );
 
     infile = NULL;
-    kerberos = not = verbose = valsfromfiles = 0;
+    not = verbose = want_bindpw = debug = manageDSAit = referrals = 0;
+    authmethod = -1;
+	version = -1;
 
-    while (( i = getopt( argc, argv, "FabckKnrtvh:p:D:w:d:f:" )) != EOF ) {
+    while (( i = getopt( argc, argv, "acrf:F"
+		"Cd:D:h:H:IkKMnO:p:P:QR:U:vw:WxX:Y:Z" )) != EOF )
+	{
 	switch( i ) {
+	/* Modify Options */
 	case 'a':	/* add */
-	    new = 1;
-	    break;
-	case 'b':	/* read values from files (for binary attributes) */
-	    valsfromfiles = 1;
+	    ldapadd = 1;
 	    break;
 	case 'c':	/* continuous operation */
 	    contoper = 1;
 	    break;
-	case 'r':	/* default is to replace rather than add values */
-	    replace = 1;
-	    break;
-	case 'k':	/* kerberos bind */
-	    kerberos = 2;
-	    break;
-	case 'K':	/* kerberos bind, part 1 only */
-	    kerberos = 1;
+	case 'f':	/* read from file */
+		if( infile != NULL ) {
+			fprintf( stderr, "%s: -f previously specified\n", prog );
+			return EXIT_FAILURE;
+		}
+	    infile = strdup( optarg );
 	    break;
 	case 'F':	/* force all changes records to be used */
 	    force = 1;
 	    break;
-	case 'h':	/* ldap host */
-	    ldaphost = strdup( optarg );
+
+	/* Common Options */
+	case 'C':
+		referrals++;
+		break;
+	case 'd':
+	    debug |= atoi( optarg );
 	    break;
 	case 'D':	/* bind DN */
+		if( binddn != NULL ) {
+			fprintf( stderr, "%s: -D previously specified\n", prog );
+			return EXIT_FAILURE;
+		}
 	    binddn = strdup( optarg );
 	    break;
-	case 'w':	/* password */
-	    passwd = strdup( optarg );
+	case 'h':	/* ldap host */
+		if( ldapuri != NULL ) {
+			fprintf( stderr, "%s: -h incompatible with -H\n", prog );
+			return EXIT_FAILURE;
+		}
+		if( ldaphost != NULL ) {
+			fprintf( stderr, "%s: -h previously specified\n", prog );
+			return EXIT_FAILURE;
+		}
+	    ldaphost = strdup( optarg );
 	    break;
-	case 'd':
-#ifdef LDAP_DEBUG
-	    ldap_debug = lber_debug = atoi( optarg );	/* */
-#else /* LDAP_DEBUG */
-	    fprintf( stderr, "%s: compile with -DLDAP_DEBUG for debugging\n",
-		    prog );
-#endif /* LDAP_DEBUG */
+	case 'H':	/* ldap URI */
+		if( ldaphost != NULL ) {
+			fprintf( stderr, "%s: -H incompatible with -h\n", prog );
+			return EXIT_FAILURE;
+		}
+		if( ldapport ) {
+			fprintf( stderr, "%s: -H incompatible with -p\n", prog );
+			return EXIT_FAILURE;
+		}
+		if( ldapuri != NULL ) {
+			fprintf( stderr, "%s: -H previously specified\n", prog );
+			return EXIT_FAILURE;
+		}
+	    ldapuri = strdup( optarg );
 	    break;
-	case 'f':	/* read from file */
-	    infile = strdup( optarg );
+	case 'I':
+#ifdef HAVE_CYRUS_SASL
+		if( version == LDAP_VERSION2 ) {
+			fprintf( stderr, "%s: -I incompatible with version %d\n",
+				prog, version );
+			return EXIT_FAILURE;
+		}
+		if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
+			fprintf( stderr, "%s: incompatible previous "
+				"authentication choice\n",
+				prog );
+			return EXIT_FAILURE;
+		}
+		authmethod = LDAP_AUTH_SASL;
+		version = LDAP_VERSION3;
+		sasl_flags = LDAP_SASL_INTERACTIVE;
+		break;
+#else
+		fprintf( stderr, "%s: was not compiled with SASL support\n",
+			prog );
+		return( EXIT_FAILURE );
+#endif
+	case 'k':	/* kerberos bind */
+#ifdef LDAP_API_FEATURE_X_OPENLDAP_V2_KBIND
+		if( version > LDAP_VERSION2 ) {
+			fprintf( stderr, "%s: -k incompatible with LDAPv%d\n",
+				prog, version );
+			return EXIT_FAILURE;
+		}
+
+		if( authmethod != -1 ) {
+			fprintf( stderr, "%s: -k incompatible with previous "
+				"authentication choice\n", prog );
+			return EXIT_FAILURE;
+		}
+			
+		authmethod = LDAP_AUTH_KRBV4;
+#else
+		fprintf( stderr, "%s: not compiled with Kerberos support\n", prog );
+		return EXIT_FAILURE;
+#endif
 	    break;
-	case 'p':
-	    ldapport = atoi( optarg );
+	case 'K':	/* kerberos bind, part one only */
+#ifdef LDAP_API_FEATURE_X_OPENLDAP_V2_KBIND
+		if( version > LDAP_VERSION2 ) {
+			fprintf( stderr, "%s: -k incompatible with LDAPv%d\n",
+				prog, version );
+			return EXIT_FAILURE;
+		}
+		if( authmethod != -1 ) {
+			fprintf( stderr, "%s: incompatible with previous "
+				"authentication choice\n", prog );
+			return EXIT_FAILURE;
+		}
+
+		authmethod = LDAP_AUTH_KRBV41;
+#else
+		fprintf( stderr, "%s: not compiled with Kerberos support\n", prog );
+		return( EXIT_FAILURE );
+#endif
 	    break;
-	case 'n':	/* print adds, don't actually do them */
+	case 'M':
+		/* enable Manage DSA IT */
+		if( version == LDAP_VERSION2 ) {
+			fprintf( stderr, "%s: -M incompatible with LDAPv%d\n",
+				prog, version );
+			return EXIT_FAILURE;
+		}
+		manageDSAit++;
+		version = LDAP_VERSION3;
+		break;
+	case 'n':	/* print deletes, don't actually do them */
 	    ++not;
 	    break;
+	case 'O':
+#ifdef HAVE_CYRUS_SASL
+		if( sasl_secprops != NULL ) {
+			fprintf( stderr, "%s: -O previously specified\n", prog );
+			return EXIT_FAILURE;
+		}
+		if( version == LDAP_VERSION2 ) {
+			fprintf( stderr, "%s: -O incompatible with LDAPv%d\n",
+				prog, version );
+			return EXIT_FAILURE;
+		}
+		if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
+			fprintf( stderr, "%s: incompatible previous "
+				"authentication choice\n", prog );
+			return EXIT_FAILURE;
+		}
+		authmethod = LDAP_AUTH_SASL;
+		version = LDAP_VERSION3;
+		sasl_secprops = strdup( optarg );
+#else
+		fprintf( stderr, "%s: not compiled with SASL support\n",
+			prog );
+		return( EXIT_FAILURE );
+#endif
+		break;
+	case 'p':
+		if( ldapport ) {
+			fprintf( stderr, "%s: -p previously specified\n", prog );
+			return EXIT_FAILURE;
+		}
+	    ldapport = atoi( optarg );
+	    break;
+	case 'P':
+		switch( atoi(optarg) ) {
+		case 2:
+			if( version == LDAP_VERSION3 ) {
+				fprintf( stderr, "%s: -P 2 incompatible with version %d\n",
+					prog, version );
+				return EXIT_FAILURE;
+			}
+			version = LDAP_VERSION2;
+			break;
+		case 3:
+			if( version == LDAP_VERSION2 ) {
+				fprintf( stderr, "%s: -P 2 incompatible with version %d\n",
+					prog, version );
+				return EXIT_FAILURE;
+			}
+			version = LDAP_VERSION3;
+			break;
+		default:
+			fprintf( stderr, "%s: protocol version should be 2 or 3\n",
+				prog );
+			usage( prog );
+			return( EXIT_FAILURE );
+		} break;
+	case 'Q':
+#ifdef HAVE_CYRUS_SASL
+		if( version == LDAP_VERSION2 ) {
+			fprintf( stderr, "%s: -Q incompatible with version %d\n",
+				prog, version );
+			return EXIT_FAILURE;
+		}
+		if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
+			fprintf( stderr, "%s: incompatible previous "
+				"authentication choice\n",
+				prog );
+			return EXIT_FAILURE;
+		}
+		authmethod = LDAP_AUTH_SASL;
+		version = LDAP_VERSION3;
+		sasl_flags = LDAP_SASL_QUIET;
+		break;
+#else
+		fprintf( stderr, "%s: not compiled with SASL support\n",
+			prog );
+		return( EXIT_FAILURE );
+#endif
+	case 'r':	/* replace (obsolete) */
+		break;
+
+	case 'R':
+#ifdef HAVE_CYRUS_SASL
+		if( sasl_realm != NULL ) {
+			fprintf( stderr, "%s: -R previously specified\n", prog );
+			return EXIT_FAILURE;
+		}
+		if( version == LDAP_VERSION2 ) {
+			fprintf( stderr, "%s: -R incompatible with version %d\n",
+				prog, version );
+			return EXIT_FAILURE;
+		}
+		if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
+			fprintf( stderr, "%s: incompatible previous "
+				"authentication choice\n",
+				prog );
+			return EXIT_FAILURE;
+		}
+		authmethod = LDAP_AUTH_SASL;
+		version = LDAP_VERSION3;
+		sasl_realm = strdup( optarg );
+#else
+		fprintf( stderr, "%s: not compiled with SASL support\n",
+			prog );
+		return( EXIT_FAILURE );
+#endif
+		break;
+	case 'U':
+#ifdef HAVE_CYRUS_SASL
+		if( sasl_authc_id != NULL ) {
+			fprintf( stderr, "%s: -U previously specified\n", prog );
+			return EXIT_FAILURE;
+		}
+		if( version == LDAP_VERSION2 ) {
+			fprintf( stderr, "%s: -U incompatible with version %d\n",
+				prog, version );
+			return EXIT_FAILURE;
+		}
+		if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
+			fprintf( stderr, "%s: incompatible previous "
+				"authentication choice\n",
+				prog );
+			return EXIT_FAILURE;
+		}
+		authmethod = LDAP_AUTH_SASL;
+		version = LDAP_VERSION3;
+		sasl_authc_id = strdup( optarg );
+#else
+		fprintf( stderr, "%s: not compiled with SASL support\n",
+			prog );
+		return( EXIT_FAILURE );
+#endif
+		break;
 	case 'v':	/* verbose mode */
 	    verbose++;
 	    break;
+	case 'w':	/* password */
+	    passwd.bv_val = strdup( optarg );
+		{
+			char* p;
+
+			for( p = optarg; *p != '\0'; p++ ) {
+				*p = '\0';
+			}
+		}
+		passwd.bv_len = strlen( passwd.bv_val );
+	    break;
+	case 'W':
+		want_bindpw++;
+		break;
+	case 'Y':
+#ifdef HAVE_CYRUS_SASL
+		if( sasl_mech != NULL ) {
+			fprintf( stderr, "%s: -Y previously specified\n", prog );
+			return EXIT_FAILURE;
+		}
+		if( version == LDAP_VERSION2 ) {
+			fprintf( stderr, "%s: -Y incompatible with version %d\n",
+				prog, version );
+			return EXIT_FAILURE;
+		}
+		if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
+			fprintf( stderr, "%s: incompatible with authentication choice\n", prog );
+			return EXIT_FAILURE;
+		}
+		authmethod = LDAP_AUTH_SASL;
+		version = LDAP_VERSION3;
+		sasl_mech = strdup( optarg );
+#else
+		fprintf( stderr, "%s: not compiled with SASL support\n",
+			prog );
+		return( EXIT_FAILURE );
+#endif
+		break;
+	case 'x':
+		if( authmethod != -1 && authmethod != LDAP_AUTH_SIMPLE ) {
+			fprintf( stderr, "%s: incompatible with previous "
+				"authentication choice\n", prog );
+			return EXIT_FAILURE;
+		}
+		authmethod = LDAP_AUTH_SIMPLE;
+		break;
+	case 'X':
+#ifdef HAVE_CYRUS_SASL
+		if( sasl_authz_id != NULL ) {
+			fprintf( stderr, "%s: -X previously specified\n", prog );
+			return EXIT_FAILURE;
+		}
+		if( version == LDAP_VERSION2 ) {
+			fprintf( stderr, "%s: -X incompatible with LDAPv%d\n",
+				prog, version );
+			return EXIT_FAILURE;
+		}
+		if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
+			fprintf( stderr, "%s: -X incompatible with "
+				"authentication choice\n", prog );
+			return EXIT_FAILURE;
+		}
+		authmethod = LDAP_AUTH_SASL;
+		version = LDAP_VERSION3;
+		sasl_authz_id = strdup( optarg );
+#else
+		fprintf( stderr, "%s: not compiled with SASL support\n",
+			prog );
+		return( EXIT_FAILURE );
+#endif
+		break;
+	case 'Z':
+#ifdef HAVE_TLS
+		if( version == LDAP_VERSION2 ) {
+			fprintf( stderr, "%s: -Z incompatible with version %d\n",
+				prog, version );
+			return EXIT_FAILURE;
+		}
+		version = LDAP_VERSION3;
+		use_tls++;
+#else
+		fprintf( stderr, "%s: not compiled with TLS support\n",
+			prog );
+		return( EXIT_FAILURE );
+#endif
+		break;
 	default:
-	    fprintf( stderr, usage, prog );
-	    exit( 1 );
+		fprintf( stderr, "%s: unrecognized option -%c\n",
+			prog, optopt );
+	    usage( prog );
 	}
     }
 
-    if ( argc - optind != 0 ) {
-	fprintf( stderr, usage, prog );
-	exit( 1 );
-    }
+	if (version == -1) {
+		version = LDAP_VERSION3;
+	}
+	if (authmethod == -1 && version > LDAP_VERSION2) {
+#ifdef HAVE_CYRUS_SASL
+		authmethod = LDAP_AUTH_SASL;
+#else
+		authmethod = LDAP_AUTH_SIMPLE;
+#endif
+	}
+
+	if ( argc != optind )
+	usage( prog );
 
     if ( infile != NULL ) {
 	if (( fp = fopen( infile, "r" )) == NULL ) {
 	    perror( infile );
-	    exit( 1 );
+	    return( EXIT_FAILURE );
 	}
     } else {
 	fp = stdin;
     }
 
+	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 );
+		}
+		ldif_debug = debug;
+	}
+
+#ifdef SIGPIPE
+	(void) SIGNAL( SIGPIPE, SIG_IGN );
+#endif
+
+#ifdef NEW_LOGGING
+    lutil_log_initialize( argc, argv );
+#endif
 
     if ( !not ) {
-	if (( ld = ldap_open( ldaphost, ldapport )) == NULL ) {
-	    perror( "ldap_open" );
-	    exit( 1 );
-	}
+	if( ( ldaphost != NULL || ldapport ) && ( ldapuri == NULL ) ) {
+		if ( verbose ) {
+			fprintf( stderr, "ldap_init( %s, %d )\n",
+				ldaphost != NULL ? ldaphost : "<DEFAULT>",
+				ldapport );
+		}
 
-	ld->ld_deref = LDAP_DEREF_NEVER;	/* this seems prudent */
+		ld = ldap_init( ldaphost, ldapport );
+		if( ld == NULL ) {
+			perror("ldapmodify: ldap_init");
+			return EXIT_FAILURE;
+		}
 
-	if ( !kerberos ) {
-	    authmethod = LDAP_AUTH_SIMPLE;
-	} else if ( kerberos == 1 ) {
-	    authmethod = LDAP_AUTH_KRBV41;
 	} else {
-	    authmethod = LDAP_AUTH_KRBV4;
+		if ( verbose ) {
+			fprintf( stderr, "ldap_initialize( %s )\n",
+				ldapuri != NULL ? ldapuri : "<DEFAULT>" );
+		}
+
+		rc = ldap_initialize( &ld, ldapuri );
+		if( rc != LDAP_SUCCESS ) {
+			fprintf( stderr, "Could not create LDAP session handle (%d): %s\n",
+				rc, ldap_err2string(rc) );
+			return EXIT_FAILURE;
+		}
+	}
+
+	/* referrals */
+	if( ldap_set_option( ld, LDAP_OPT_REFERRALS,
+		referrals ? LDAP_OPT_ON : LDAP_OPT_OFF ) != LDAP_OPT_SUCCESS )
+	{
+		fprintf( stderr, "Could not set LDAP_OPT_REFERRALS %s\n",
+			referrals ? "on" : "off" );
+		return EXIT_FAILURE;
+	}
+
+
+	if (version == -1 ) {
+		version = LDAP_VERSION3;
+	}
+
+	if( ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version )
+		!= LDAP_OPT_SUCCESS )
+	{
+		fprintf( stderr, "Could not set LDAP_OPT_PROTOCOL_VERSION %d\n",
+			version );
+		return EXIT_FAILURE;
 	}
-	if ( ldap_bind_s( ld, binddn, passwd, authmethod ) != LDAP_SUCCESS ) {
-	    ldap_perror( ld, "ldap_bind" );
-	    exit( 1 );
+
+	if ( use_tls && ( ldap_start_tls_s( ld, NULL, NULL ) != LDAP_SUCCESS )) {
+		ldap_perror( ld, "ldap_start_tls" );
+		if ( use_tls > 1 ) {
+			return( EXIT_FAILURE );
+		}
+	}
+
+	if (want_bindpw) {
+		passwd.bv_val = getpassphrase("Enter LDAP Password: ");
+		passwd.bv_len = passwd.bv_val ? strlen( passwd.bv_val ) : 0;
+	}
+
+	if ( authmethod == LDAP_AUTH_SASL ) {
+#ifdef HAVE_CYRUS_SASL
+		void *defaults;
+
+		if( sasl_secprops != NULL ) {
+			rc = ldap_set_option( ld, LDAP_OPT_X_SASL_SECPROPS,
+				(void *) sasl_secprops );
+			
+			if( rc != LDAP_OPT_SUCCESS ) {
+				fprintf( stderr,
+					"Could not set LDAP_OPT_X_SASL_SECPROPS: %s\n",
+					sasl_secprops );
+				return( EXIT_FAILURE );
+			}
+		}
+		
+		defaults = lutil_sasl_defaults( ld,
+			sasl_mech,
+			sasl_realm,
+			sasl_authc_id,
+			passwd.bv_val,
+			sasl_authz_id );
+
+		rc = ldap_sasl_interactive_bind_s( ld, binddn,
+			sasl_mech, NULL, NULL,
+			sasl_flags, lutil_sasl_interact, defaults );
+
+		if( rc != LDAP_SUCCESS ) {
+			ldap_perror( ld, "ldap_sasl_interactive_bind_s" );
+			return( EXIT_FAILURE );
+		}
+#else
+		fprintf( stderr, "%s: not compiled with SASL support\n",
+			prog );
+		return( EXIT_FAILURE );
+#endif
 	}
+	else {
+		if ( ldap_bind_s( ld, binddn, passwd.bv_val, authmethod )
+				!= LDAP_SUCCESS ) {
+			ldap_perror( ld, "ldap_bind" );
+			return( EXIT_FAILURE );
+		}
+	}
+
     }
 
     rc = 0;
 
+	if ( manageDSAit ) {
+		int err;
+		LDAPControl c;
+		LDAPControl *ctrls[2];
+		ctrls[0] = &c;
+		ctrls[1] = NULL;
+
+		c.ldctl_oid = LDAP_CONTROL_MANAGEDSAIT;
+		c.ldctl_value.bv_val = NULL;
+		c.ldctl_value.bv_len = 0;
+		c.ldctl_iscritical = manageDSAit > 1;
+
+		err = ldap_set_option( ld, LDAP_OPT_SERVER_CONTROLS, ctrls );
+
+		if( err != LDAP_OPT_SUCCESS ) {
+			fprintf( stderr, "Could not set ManageDSAit %scontrol\n",
+				c.ldctl_iscritical ? "critical " : "" );
+			if( c.ldctl_iscritical ) {
+				exit( EXIT_FAILURE );
+			}
+		}
+	}
+
+	count = 0;
     while (( rc == 0 || contoper ) &&
 		( rbuf = read_one_record( fp )) != NULL ) {
-	/*
-	 * we assume record is ldif/slapd.replog if the first line
-	 * has a colon that appears to the left of any equal signs, OR
-	 * if the first line consists entirely of digits (an entry id)
-	 */
-	use_ldif = ( p = strchr( rbuf, ':' )) != NULL &&
-		( q = strchr( rbuf, '\n' )) != NULL && p < q &&
-		(( q = strchr( rbuf, '=' )) == NULL || p < q );
+	count++;
 
 	start = rbuf;
 
-	if ( !use_ldif && ( q = strchr( rbuf, '\n' )) != NULL ) {
-	    for ( p = rbuf; p < q; ++p ) {
-		if ( !isdigit( *p )) {
-		    break;
-		}
-	    }
-	    if ( p >= q ) {
-		use_ldif = 1;
-		start = q + 1;
-	    }
-	}
-
-	if ( use_ldif ) {
-	    rc = process_ldif_rec( start );
-	} else {
-	    rc = process_ldapmod_rec( start );
-	}
+    rc = process_ldif_rec( start, count );
 
-	free( rbuf );
+	if( rc )
+		fprintf( stderr, "ldif_record() = %d\n", rc );
+		free( rbuf );
     }
 
     if ( !not ) {
-	ldap_unbind( ld );
+		ldap_unbind( ld );
     }
 
-    exit( rc );
+	return( rc );
 }
 
 
 static int
-process_ldif_rec( char *rbuf )
+process_ldif_rec( char *rbuf, int count )
 {
-    char	*line, *dn, *type, *value, *newrdn, *p;
-    int		rc, linenum, vlen, modop, replicaport;
-    int		expect_modop, expect_sep, expect_ct, expect_newrdn;
+    char	*line, *dn, *type, *newrdn, *newsup, *p;
+    int		rc, linenum, modop, replicaport;
+    int		expect_modop, expect_sep, expect_ct, expect_newrdn, expect_newsup;
     int		expect_deleteoldrdn, deleteoldrdn;
     int		saw_replica, use_record, new_entry, delete_entry, got_all;
     LDAPMod	**pmods;
+	int version;
+	struct berval val;
 
-    new_entry = new;
+    new_entry = ldapadd;
 
-    rc = got_all = saw_replica = delete_entry = expect_modop = 0;
-    expect_deleteoldrdn = expect_newrdn = expect_sep = expect_ct = 0;
+    rc = got_all = saw_replica = delete_entry = modop = expect_modop = 0;
+    expect_deleteoldrdn = expect_newrdn = expect_newsup = 0;
+	expect_sep = expect_ct = 0;
     linenum = 0;
+	version = 0;
     deleteoldrdn = 1;
     use_record = force;
     pmods = NULL;
-    dn = newrdn = NULL;
+    dn = newrdn = newsup = NULL;
 
-    while ( rc == 0 && ( line = str_getline( &rbuf )) != NULL ) {
+    while ( rc == 0 && ( line = ldif_getline( &rbuf )) != NULL ) {
 	++linenum;
+
 	if ( expect_sep && strcasecmp( line, T_MODSEPSTR ) == 0 ) {
 	    expect_sep = 0;
 	    expect_ct = 1;
 	    continue;
 	}
 	
-	if ( str_parse_line( line, &type, &value, &vlen ) < 0 ) {
-	    fprintf( stderr, "%s: invalid format (line %d of entry: %s\n",
+	if ( ldif_parse_line( line, &type, &val.bv_val, &val.bv_len ) < 0 ) {
+	    fprintf( stderr, "%s: invalid format (line %d) entry: \"%s\"\n",
 		    prog, linenum, dn == NULL ? "" : dn );
 	    rc = LDAP_PARAM_ERROR;
 	    break;
@@ -272,53 +765,85 @@ process_ldif_rec( char *rbuf )
 	if ( dn == NULL ) {
 	    if ( !use_record && strcasecmp( type, T_REPLICA_STR ) == 0 ) {
 		++saw_replica;
-		if (( p = strchr( value, ':' )) == NULL ) {
-		    replicaport = LDAP_PORT;
+		if (( p = strchr( val.bv_val, ':' )) == NULL ) {
+		    replicaport = 0;
 		} else {
 		    *p++ = '\0';
 		    replicaport = atoi( p );
 		}
-		if ( strcasecmp( value, ldaphost ) == 0 &&
+		if ( ldaphost != NULL && strcasecmp( val.bv_val, ldaphost ) == 0 &&
 			replicaport == ldapport ) {
 		    use_record = 1;
 		}
+	    } else if ( count == 1 && linenum == 1 && 
+			strcasecmp( type, T_VERSION_STR ) == 0 )
+		{
+			if( val.bv_len == 0 || atoi(val.bv_val) != 1 ) {
+		    	fprintf( stderr, "%s: invalid version %s, line %d (ignored)\n",
+			   	prog, val.bv_val == NULL ? "(null)" : val.bv_val, linenum );
+			}
+			version++;
+
 	    } else if ( strcasecmp( type, T_DN_STR ) == 0 ) {
-		if (( dn = strdup( value )) == NULL ) {
+		if (( dn = strdup( val.bv_val ? val.bv_val : "" )) == NULL ) {
 		    perror( "strdup" );
-		    exit( 1 );
+		    exit( EXIT_FAILURE );
 		}
 		expect_ct = 1;
 	    }
-	    continue;	/* skip all lines until we see "dn:" */
+	    goto end_line;	/* skip all lines until we see "dn:" */
 	}
 
 	if ( expect_ct ) {
 	    expect_ct = 0;
 	    if ( !use_record && saw_replica ) {
-		printf( "%s: skipping change record for entry: %s\n\t(LDAP host/port does not match replica: lines)\n",
+		printf( "%s: skipping change record for entry: %s\n"
+			"\t(LDAP host/port does not match replica: lines)\n",
 			prog, dn );
 		free( dn );
+		ber_memfree( type );
+		ber_memfree( val.bv_val );
 		return( 0 );
 	    }
 
 	    if ( strcasecmp( type, T_CHANGETYPESTR ) == 0 ) {
-		if ( strcasecmp( value, T_MODIFYCTSTR ) == 0 ) {
+#ifdef LIBERAL_CHANGETYPE_MODOP
+		/* trim trailing spaces (and log warning ...) */
+
+		int icnt;
+		for ( icnt = val.bv_len; --icnt > 0; ) {
+		    if ( !isspace( (unsigned char) val.bv_val[icnt] ) ) {
+			break;
+		    }
+		}
+
+		if ( ++icnt != val.bv_len ) {
+		    fprintf( stderr, "%s: illegal trailing space after \"%s: %s\" trimmed (line %d of entry \"%s\")\n",
+			    prog, T_CHANGETYPESTR, val.bv_val, linenum, dn );
+		    val.bv_val[icnt] = '\0';
+		}
+#endif /* LIBERAL_CHANGETYPE_MODOP */
+
+		if ( strcasecmp( val.bv_val, T_MODIFYCTSTR ) == 0 ) {
 			new_entry = 0;
 			expect_modop = 1;
-		} else if ( strcasecmp( value, T_ADDCTSTR ) == 0 ) {
+		} else if ( strcasecmp( val.bv_val, T_ADDCTSTR ) == 0 ) {
 			new_entry = 1;
-		} else if ( strcasecmp( value, T_MODRDNCTSTR ) == 0 ) {
+		} else if ( strcasecmp( val.bv_val, T_MODRDNCTSTR ) == 0
+			|| strcasecmp( val.bv_val, T_MODDNCTSTR ) == 0
+			|| strcasecmp( val.bv_val, T_RENAMECTSTR ) == 0)
+		{
 		    expect_newrdn = 1;
-		} else if ( strcasecmp( value, T_DELETECTSTR ) == 0 ) {
+		} else if ( strcasecmp( val.bv_val, T_DELETECTSTR ) == 0 ) {
 		    got_all = delete_entry = 1;
 		} else {
 		    fprintf( stderr,
-			    "%s:  unknown %s \"%s\" (line %d of entry: %s)\n",
-			    prog, T_CHANGETYPESTR, value, linenum, dn );
+			    "%s:  unknown %s \"%s\" (line %d of entry \"%s\")\n",
+			    prog, T_CHANGETYPESTR, val.bv_val, linenum, dn );
 		    rc = LDAP_PARAM_ERROR;
 		}
-		continue;
-	    } else if ( new ) {		/*  missing changetype => add */
+		goto end_line;
+	    } else if ( ldapadd ) {		/*  missing changetype => add */
 		new_entry = 1;
 		modop = LDAP_MOD_ADD;
 	    } else {
@@ -327,60 +852,104 @@ process_ldif_rec( char *rbuf )
 	}
 
 	if ( expect_modop ) {
+#ifdef LIBERAL_CHANGETYPE_MODOP
+	    /* trim trailing spaces (and log warning ...) */
+	    
+	    int icnt;
+	    for ( icnt = val.bv_len; --icnt > 0; ) {
+		if ( !isspace( (unsigned char) val.bv_val[icnt] ) ) {
+		    break;
+		}
+	    }
+	    
+	    if ( ++icnt != val.bv_len ) {
+		fprintf( stderr, "%s: illegal trailing space after \"%s: %s\" trimmed (line %d of entry \"%s\")\n",
+    			prog, type, val.bv_val, linenum, dn );
+		val.bv_val[icnt] = '\0';
+	    }
+#endif /* LIBERAL_CHANGETYPE_MODOP */
+
 	    expect_modop = 0;
 	    expect_sep = 1;
 	    if ( strcasecmp( type, T_MODOPADDSTR ) == 0 ) {
 		modop = LDAP_MOD_ADD;
-		continue;
+		goto end_line;
 	    } else if ( strcasecmp( type, T_MODOPREPLACESTR ) == 0 ) {
 		modop = LDAP_MOD_REPLACE;
-		continue;
+		addmodifyop( &pmods, modop, val.bv_val, NULL );
+		goto end_line;
 	    } else if ( strcasecmp( type, T_MODOPDELETESTR ) == 0 ) {
 		modop = LDAP_MOD_DELETE;
-		addmodifyop( &pmods, modop, value, NULL, 0 );
-		continue;
+		addmodifyop( &pmods, modop, val.bv_val, NULL );
+		goto end_line;
 	    } else {	/* no modify op:  use default */
-		modop = replace ? LDAP_MOD_REPLACE : LDAP_MOD_ADD;
+		modop = ldapadd ? LDAP_MOD_ADD : LDAP_MOD_REPLACE;
 	    }
 	}
 
 	if ( expect_newrdn ) {
 	    if ( strcasecmp( type, T_NEWRDNSTR ) == 0 ) {
-		if (( newrdn = strdup( value )) == NULL ) {
+			if (( newrdn = strdup( val.bv_val ? val.bv_val : "" )) == NULL ) {
 		    perror( "strdup" );
-		    exit( 1 );
+		    exit( EXIT_FAILURE );
 		}
 		expect_deleteoldrdn = 1;
 		expect_newrdn = 0;
 	    } else {
-		fprintf( stderr, "%s: expecting \"%s:\" but saw \"%s:\" (line %d of entry %s)\n",
+		fprintf( stderr, "%s: expecting \"%s:\" but saw \"%s:\" (line %d of entry \"%s\")\n",
 			prog, T_NEWRDNSTR, type, linenum, dn );
 		rc = LDAP_PARAM_ERROR;
 	    }
 	} else if ( expect_deleteoldrdn ) {
 	    if ( strcasecmp( type, T_DELETEOLDRDNSTR ) == 0 ) {
-		deleteoldrdn = ( *value == '0' ) ? 0 : 1;
+		deleteoldrdn = ( *val.bv_val == '0' ) ? 0 : 1;
+		expect_deleteoldrdn = 0;
+		expect_newsup = 1;
 		got_all = 1;
 	    } else {
-		fprintf( stderr, "%s: expecting \"%s:\" but saw \"%s:\" (line %d of entry %s)\n",
+		fprintf( stderr, "%s: expecting \"%s:\" but saw \"%s:\" (line %d of entry \"%s\")\n",
 			prog, T_DELETEOLDRDNSTR, type, linenum, dn );
 		rc = LDAP_PARAM_ERROR;
 	    }
+	} else if ( expect_newsup ) {
+	    if ( strcasecmp( type, T_NEWSUPSTR ) == 0 ) {
+		if (( newsup = strdup( val.bv_val ? val.bv_val : "" )) == NULL ) {
+		    perror( "strdup" );
+		    exit( EXIT_FAILURE );
+		}
+		expect_newsup = 0;
+	    } else {
+		fprintf( stderr, "%s: expecting \"%s:\" but saw \"%s:\" (line %d of entry \"%s\")\n",
+			prog, T_NEWSUPSTR, type, linenum, dn );
+		rc = LDAP_PARAM_ERROR;
+	    }
 	} else if ( got_all ) {
 	    fprintf( stderr,
-		    "%s: extra lines at end (line %d of entry %s)\n",
+		    "%s: extra lines at end (line %d of entry \"%s\")\n",
 		    prog, linenum, dn );
 	    rc = LDAP_PARAM_ERROR;
 	} else {
-	    addmodifyop( &pmods, modop, type, value, vlen );
+		addmodifyop( &pmods, modop, type, val.bv_val == NULL ? NULL : &val );
 	}
+
+end_line:
+	ber_memfree( type );
+	ber_memfree( val.bv_val );
     }
 
+	if( linenum == 0 ) {
+		return 0;
+	}
+
+	if( version && linenum == 1 ) {
+		return 0;
+	}
+
     if ( rc == 0 ) {
 	if ( delete_entry ) {
 	    rc = dodelete( dn );
 	} else if ( newrdn != NULL ) {
-	    rc = domodrdn( dn, newrdn, deleteoldrdn );
+	    rc = dorename( dn, newrdn, newsup, deleteoldrdn );
 	} else {
 	    rc = domodify( dn, pmods, new_entry );
 	}
@@ -397,209 +966,110 @@ process_ldif_rec( char *rbuf )
 	free( newrdn );
     }
     if ( pmods != NULL ) {
-	freepmods( pmods );
+	ldap_mods_free( pmods, 1 );
     }
 
     return( rc );
 }
 
 
-static int
-process_ldapmod_rec( char *rbuf )
+static void
+addmodifyop(
+	LDAPMod ***pmodsp,
+	int modop,
+	const char *attr,
+	struct berval *val )
 {
-    char	*line, *dn, *p, *q, *attr, *value;
-    int		rc, linenum, modop;
-    LDAPMod	**pmods;
-
-    pmods = NULL;
-    dn = NULL;
-    linenum = 0;
-    line = rbuf;
-    rc = 0;
-
-    while ( rc == 0 && rbuf != NULL && *rbuf != '\0' ) {
-	++linenum;
-	if (( p = strchr( rbuf, '\n' )) == NULL ) {
-	    rbuf = NULL;
-	} else {
-	    if ( *(p-1) == '\\' ) {	/* lines ending in '\' are continued */
-		strcpy( p - 1, p );
-		rbuf = p;
-		continue;
-	    }
-	    *p++ = '\0';
-	    rbuf = p;
+	LDAPMod		**pmods;
+	int			i, j;
+
+	pmods = *pmodsp;
+	modop |= LDAP_MOD_BVALUES;
+
+	i = 0;
+	if ( pmods != NULL ) {
+		for ( ; pmods[ i ] != NULL; ++i ) {
+			if ( strcasecmp( pmods[ i ]->mod_type, attr ) == 0 &&
+				pmods[ i ]->mod_op == modop )
+			{
+				break;
+			}
+		}
 	}
 
-	if ( dn == NULL ) {	/* first line contains DN */
-	    if (( dn = strdup( line )) == NULL ) {
-		perror( "strdup" );
-		exit( 1 );
-	    }
-	} else {
-	    if (( p = strchr( line, '=' )) == NULL ) {
-		value = NULL;
-		p = line + strlen( line );
-	    } else {
-		*p++ = '\0';
-		value = p;
-	    }
-
-	    for ( attr = line; *attr != '\0' && isspace( *attr ); ++attr ) {
-		;	/* skip attribute leading white space */
-	    }
-
-	    for ( q = p - 1; q > attr && isspace( *q ); --q ) {
-		*q = '\0';	/* remove attribute trailing white space */
-	    }
-
-	    if ( value != NULL ) {
-		while ( isspace( *value )) {
-		    ++value;		/* skip value leading white space */
-		}
-		for ( q = value + strlen( value ) - 1; q > value &&
-			isspace( *q ); --q ) {
-		    *q = '\0';	/* remove value trailing white space */
-		}
-		if ( *value == '\0' ) {
-		    value = NULL;
+	if ( pmods == NULL || pmods[ i ] == NULL ) {
+		if (( pmods = (LDAPMod **)ber_memrealloc( pmods, (i + 2) *
+			sizeof( LDAPMod * ))) == NULL )
+		{
+			perror( "realloc" );
+			exit( EXIT_FAILURE );
 		}
 
-	    }
+		*pmodsp = pmods;
+		pmods[ i + 1 ] = NULL;
 
-	    if ( value == NULL && new ) {
-		fprintf( stderr, "%s: missing value on line %d (attr is %s)\n",
-			prog, linenum, attr );
-		rc = LDAP_PARAM_ERROR;
-	    } else {
-		 switch ( *attr ) {
-		case '-':
-		    modop = LDAP_MOD_DELETE;
-		    ++attr;
-		    break;
-		case '+':
-		    modop = LDAP_MOD_ADD;
-		    ++attr;
-		    break;
-		default:
-		    modop = replace ? LDAP_MOD_REPLACE : LDAP_MOD_ADD;
+		pmods[ i ] = (LDAPMod *)ber_memcalloc( 1, sizeof( LDAPMod ));
+		if ( pmods[ i ] == NULL ) {
+			perror( "calloc" );
+			exit( EXIT_FAILURE );
 		}
 
-		addmodifyop( &pmods, modop, attr, value,
-			( value == NULL ) ? 0 : strlen( value ));
-	    }
-	}
-
-	line = rbuf;
-    }
-
-    if ( rc == 0 ) {
-	if ( dn == NULL ) {
-	    rc = LDAP_PARAM_ERROR;
-	} else if (( rc = domodify( dn, pmods, new )) == LDAP_SUCCESS ) {
-	    rc = 0;
-	}
-    }
-
-    if ( pmods != NULL ) {
-	freepmods( pmods );
-    }
-    if ( dn != NULL ) {
-	free( dn );
-    }
-
-    return( rc );
-}
-
-
-static void
-addmodifyop( LDAPMod ***pmodsp, int modop, char *attr, char *value, int vlen )
-{
-    LDAPMod		**pmods;
-    int			i, j;
-    struct berval	*bvp;
-
-    pmods = *pmodsp;
-    modop |= LDAP_MOD_BVALUES;
-
-    i = 0;
-    if ( pmods != NULL ) {
-	for ( ; pmods[ i ] != NULL; ++i ) {
-	    if ( strcasecmp( pmods[ i ]->mod_type, attr ) == 0 &&
-		    pmods[ i ]->mod_op == modop ) {
-		break;
-	    }
+		pmods[ i ]->mod_op = modop;
+		pmods[ i ]->mod_type = ber_strdup( attr );
+		if ( pmods[ i ]->mod_type == NULL ) {
+			perror( "strdup" );
+			exit( EXIT_FAILURE );
+		}
 	}
-    }
 
-    if ( pmods == NULL || pmods[ i ] == NULL ) {
-	if (( pmods = (LDAPMod **)safe_realloc( pmods, (i + 2) *
-		sizeof( LDAPMod * ))) == NULL ) {
-	    perror( "safe_realloc" );
-	    exit( 1 );
-	}
-	*pmodsp = pmods;
-	pmods[ i + 1 ] = NULL;
-	if (( pmods[ i ] = (LDAPMod *)calloc( 1, sizeof( LDAPMod )))
-		== NULL ) {
-	    perror( "calloc" );
-	    exit( 1 );
-	}
-	pmods[ i ]->mod_op = modop;
-	if (( pmods[ i ]->mod_type = strdup( attr )) == NULL ) {
-	    perror( "strdup" );
-	    exit( 1 );
-	}
-    }
+	if ( val != NULL ) {
+		j = 0;
+		if ( pmods[ i ]->mod_bvalues != NULL ) {
+			for ( ; pmods[ i ]->mod_bvalues[ j ] != NULL; ++j ) {
+				/* Empty */;
+			}
+		}
 
-    if ( value != NULL ) {
-	j = 0;
-	if ( pmods[ i ]->mod_bvalues != NULL ) {
-	    for ( ; pmods[ i ]->mod_bvalues[ j ] != NULL; ++j ) {
-		;
-	    }
-	}
-	if (( pmods[ i ]->mod_bvalues =
-		(struct berval **)safe_realloc( pmods[ i ]->mod_bvalues,
-		(j + 2) * sizeof( struct berval * ))) == NULL ) {
-	    perror( "safe_realloc" );
-	    exit( 1 );
-	}
-	pmods[ i ]->mod_bvalues[ j + 1 ] = NULL;
-	if (( bvp = (struct berval *)malloc( sizeof( struct berval )))
-		== NULL ) {
-	    perror( "malloc" );
-	    exit( 1 );
-	}
-	pmods[ i ]->mod_bvalues[ j ] = bvp;
+		pmods[ i ]->mod_bvalues = (struct berval **) ber_memrealloc(
+			pmods[ i ]->mod_bvalues, (j + 2) * sizeof( struct berval * ));
+		if ( pmods[ i ]->mod_bvalues == NULL ) {
+			perror( "ber_realloc" );
+			exit( EXIT_FAILURE );
+		}
 
-	if ( valsfromfiles && *value == '/' ) {	/* get value from file */
-	    if ( fromfile( value, bvp ) < 0 ) {
-		exit( 1 );
-	    }
-	} else {
-	    bvp->bv_len = vlen;
-	    if (( bvp->bv_val = (char *)malloc( vlen + 1 )) == NULL ) {
-		perror( "malloc" );
-		exit( 1 );
-	    }
-	    SAFEMEMCPY( bvp->bv_val, value, vlen );
-	    bvp->bv_val[ vlen ] = '\0';
+		pmods[ i ]->mod_bvalues[ j + 1 ] = NULL;
+		pmods[ i ]->mod_bvalues[ j ] = ber_bvdup( val );
+		if ( pmods[ i ]->mod_bvalues[ j ] == NULL ) {
+			perror( "ber_bvdup" );
+			exit( EXIT_FAILURE );
+		}
 	}
-    }
 }
 
 
 static int
-domodify( char *dn, LDAPMod **pmods, int newentry )
+domodify(
+	const char *dn,
+	LDAPMod **pmods,
+	int newentry )
 {
     int			i, j, k, notascii, op;
     struct berval	*bvp;
 
     if ( pmods == NULL ) {
-	fprintf( stderr, "%s: no attributes to change or add (entry %s)\n",
+	fprintf( stderr, "%s: no attributes to change or add (entry=\"%s\")\n",
 		prog, dn );
 	return( LDAP_PARAM_ERROR );
+    } 
+
+    for ( i = 0; pmods[ i ] != NULL; ++i ) {
+	op = pmods[ i ]->mod_op & ~LDAP_MOD_BVALUES;
+	if( op == LDAP_MOD_ADD && ( pmods[i]->mod_bvalues == NULL )) {
+		fprintf( stderr,
+			"%s: attribute \"%s\" has no values (entry=\"%s\")\n",
+			prog, pmods[i]->mod_type, dn );
+		return LDAP_PARAM_ERROR;
+	}
     }
 
     if ( verbose ) {
@@ -612,7 +1082,7 @@ domodify( char *dn, LDAPMod **pmods, int newentry )
 		for ( j = 0; pmods[ i ]->mod_bvalues[ j ] != NULL; ++j ) {
 		    bvp = pmods[ i ]->mod_bvalues[ j ];
 		    notascii = 0;
-		    for ( k = 0; k < bvp->bv_len; ++k ) {
+		    for ( k = 0; (unsigned long) k < bvp->bv_len; ++k ) {
 			if ( !isascii( bvp->bv_val[ k ] )) {
 			    notascii = 1;
 			    break;
@@ -629,9 +1099,9 @@ domodify( char *dn, LDAPMod **pmods, int newentry )
     }
 
     if ( newentry ) {
-	printf( "%sadding new entry %s\n", not ? "!" : "", dn );
+	printf( "%sadding new entry \"%s\"\n", not ? "!" : "", dn );
     } else {
-	printf( "%smodifying entry %s\n", not ? "!" : "", dn );
+	printf( "%smodifying entry \"%s\"\n", not ? "!" : "", dn );
     }
 
     if ( !not ) {
@@ -641,7 +1111,9 @@ domodify( char *dn, LDAPMod **pmods, int newentry )
 	    i = ldap_modify_s( ld, dn, pmods );
 	}
 	if ( i != LDAP_SUCCESS ) {
-	    ldap_perror( ld, newentry ? "ldap_add" : "ldap_modify" );
+		/* print error message about failed update including DN */
+		fprintf( stderr, "%s: update failed: %s\n", prog, dn );
+		ldap_perror( ld, newentry ? "ldap_add" : "ldap_modify" );
 	} else if ( verbose ) {
 	    printf( "modify complete\n" );
 	}
@@ -656,14 +1128,16 @@ domodify( char *dn, LDAPMod **pmods, int newentry )
 
 
 static int
-dodelete( char *dn )
+dodelete(
+	const char *dn )
 {
     int	rc;
 
-    printf( "%sdeleting entry %s\n", not ? "!" : "", dn );
+    printf( "%sdeleting entry \"%s\"\n", not ? "!" : "", dn );
     if ( !not ) {
 	if (( rc = ldap_delete_s( ld, dn )) != LDAP_SUCCESS ) {
-	    ldap_perror( ld, "ldap_delete" );
+		fprintf( stderr, "%s: delete failed: %s\n", prog, dn );
+		ldap_perror( ld, "ldap_delete" );
 	} else if ( verbose ) {
 	    printf( "delete complete" );
 	}
@@ -678,20 +1152,26 @@ dodelete( char *dn )
 
 
 static int
-domodrdn( char *dn, char *newrdn, int deleteoldrdn )
+dorename(
+	const char *dn,
+	const char *newrdn,
+	const char* newsup,
+	int deleteoldrdn )
 {
     int	rc;
 
+
+    printf( "%smodifying rdn of entry \"%s\"\n", not ? "!" : "", dn );
     if ( verbose ) {
-	printf( "new RDN: %s (%skeep existing values)\n",
+	printf( "\tnew RDN: \"%s\" (%skeep existing values)\n",
 		newrdn, deleteoldrdn ? "do not " : "" );
     }
-
-    printf( "%smodifying rdn of entry %s\n", not ? "!" : "", dn );
     if ( !not ) {
-	if (( rc = ldap_modrdn2_s( ld, dn, newrdn, deleteoldrdn ))
-		!= LDAP_SUCCESS ) {
-	    ldap_perror( ld, "ldap_modrdn" );
+	if (( rc = ldap_rename2_s( ld, dn, newrdn, newsup, deleteoldrdn ))
+		!= LDAP_SUCCESS )
+	{
+		fprintf( stderr, "%s: rename failed: %s\n", prog, dn );
+		ldap_perror( ld, "ldap_modrdn" );
 	} else {
 	    printf( "modrdn completed\n" );
 	}
@@ -705,93 +1185,38 @@ domodrdn( char *dn, char *newrdn, int deleteoldrdn )
 }
 
 
-
-static void
-freepmods( LDAPMod **pmods )
-{
-    int	i;
-
-    for ( i = 0; pmods[ i ] != NULL; ++i ) {
-	if ( pmods[ i ]->mod_bvalues != NULL ) {
-	    ber_bvecfree( pmods[ i ]->mod_bvalues );
-	}
-	if ( pmods[ i ]->mod_type != NULL ) {
-	    free( pmods[ i ]->mod_type );
-	}
-	free( pmods[ i ] );
-    }
-    free( pmods );
-}
-
-
-static int
-fromfile( char *path, struct berval *bv )
-{
-	FILE		*fp;
-	long		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 ( rlen != bv->bv_len ) {
-		perror( path );
-		free( bv->bv_val );
-		return( -1 );
-	}
-
-	return( bv->bv_len );
-}
-
-
 static char *
 read_one_record( FILE *fp )
 {
-    int         len;
     char        *buf, line[ LDAPMOD_MAXLINE ];
     int		lcur, lmax;
 
     lcur = lmax = 0;
     buf = NULL;
 
-    while (( fgets( line, sizeof(line), fp ) != NULL ) &&
-            (( len = strlen( line )) > 1 )) {
-        if ( lcur + len + 1 > lmax ) {
-            lmax = LDAPMOD_MAXLINE
-		    * (( lcur + len + 1 ) / LDAPMOD_MAXLINE + 1 );
-	    if (( buf = (char *)safe_realloc( buf, lmax )) == NULL ) {
-		perror( "safe_realloc" );
-		exit( 1 );
-	    }
-        }
-        strcpy( buf + lcur, line );
-        lcur += len;
+    while ( fgets( line, sizeof(line), fp ) != NULL ) {
+    	int len = strlen( line );
+
+		if( len < 2 || ( len == 3 && *line == '\r' )) {
+			if( buf == NULL ) {
+				continue;
+			} else {
+				break;
+			}
+		}
+
+		if ( lcur + len + 1 > lmax ) {
+			lmax = LDAPMOD_MAXLINE
+				* (( lcur + len + 1 ) / LDAPMOD_MAXLINE + 1 );
+
+			if (( buf = (char *)realloc( buf, lmax )) == NULL ) {
+				perror( "realloc" );
+				exit( EXIT_FAILURE );
+			}
+		}
+
+		strcpy( buf + lcur, line );
+		lcur += len;
     }
 
     return( buf );
diff --git a/doc/man/man3/ldap_schema.3 b/doc/man/man3/ldap_schema.3
new file mode 100644
index 0000000000000000000000000000000000000000..11a7bfc739e3d7ee2e7ef9b67bf431bf949068a9
--- /dev/null
+++ b/doc/man/man3/ldap_schema.3
@@ -0,0 +1,329 @@
+.TH LDAP_SCHEMA 3 "4 June 2000" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 2000-2002 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply.  See COPYRIGHT/LICENSE.
+.SH NAME
+ldap_str2syntax, ldap_syntax2str, ldap_syntax2name, ldap_syntax_free,
+ldap_str2matchingrule, ldap_matchingrule2str, ldap_matchingrule2name,
+ldap_matchingrule_free,
+ldap_str2attributetype, ldap_attributetype2str,
+ldap_attributetype2name, ldap_attributetype_free,
+ldap_str2objectclass, ldap_objectclass2str, ldap_objectclass2name,
+ldap_objectclass_free,
+ldap_scherr2str \- Schema definition handling routines
+.SH SYNOPSIS
+.nf
+.ft B
+#include <ldap.h>
+#include <ldap_schema.h>
+.LP
+.ft B
+LDAPSyntax * ldap_str2syntax(s, code, errp, flags)
+.ft
+const char * s;
+int * code;
+const char ** errp;
+const int flags;
+.LP
+.ft B
+char * ldap_syntax2str(syn)
+.ft
+const LDAPSyntax * syn;
+.LP
+.ft B
+const char * ldap_syntax2name(syn)
+.ft
+LDAPSyntax * syn;
+.LP
+.ft B
+ldap_syntax_free(syn)
+.ft
+LDAPSyntax * syn;
+.LP
+.ft B
+LDAPMatchingRule * ldap_str2matchingrule(s, code, errp, flags)
+.ft
+const char * s;
+int * code;
+const char ** errp;
+const int flags;
+.LP
+.ft B
+char * ldap_matchingrule2str(mr);
+.ft
+const LDAPMatchingRule * mr;
+.LP
+.ft B
+const char * ldap_matchingrule2name(mr)
+.ft
+LDAPMatchingRule * mr;
+.LP
+.ft B
+ldap_matchingrule_free(mr)
+.ft
+LDAPMatchingRule * mr;
+.LP
+.ft B
+LDAPAttributeType * ldap_str2attributetype(s, code, errp, flags)
+.ft
+const char * s;
+int * code;
+const char ** errp;
+const int flags;
+.LP
+.ft B
+char * ldap_attributetype2str(at)
+.ft
+const LDAPAttributeType * at;
+.LP
+.ft B
+const char * ldap_attributetype2name(at)
+.ft
+LDAPAttributeType * at;
+.LP
+.ft B
+ldap_attributetype_free(at)
+.ft
+LDAPAttributeType * at;
+.LP
+.ft B
+LDAPObjectClass * ldap_str2objectclass(s, code, errp, flags)
+.ft
+const char * s;
+int * code;
+const char ** errp;
+const int flags;
+.LP
+.ft B
+char * ldap_objectclass2str(oc)
+.ft
+const LDAPObjectClass * oc;
+.LP
+.ft B
+const char * ldap_objectclass2name(oc)
+.ft
+LDAPObjectClass * oc;
+.LP
+.ft B
+ldap_objectclass_free(oc)
+.ft
+LDAPObjectClass * oc;
+.LP
+.ft B
+char * ldap_scherr2str(code)
+.ft
+int code;
+.SH DESCRIPTION
+These routines are used to parse schema definitions in the syntax
+defined in RFC 2252 into structs and handle these structs.  These
+routines handle four kinds of definitions: syntaxes, matching rules,
+attribute types and objectclasses.  For each definition kind, four
+routines are provided.
+.LP
+.B ldap_str2xxx()
+takes a definition in RFC 2252 format in argument
+.IR s
+as a NUL-terminated string and returns, if possible, a pointer to a
+newly allocated struct of the appropriate kind.  The caller is
+responsible for freeing the struct by calling
+.B ldap_xxx_free()
+when not needed any longer.  The routine returns NULL if some problem
+happened.  In this case, the integer pointed at by argument
+.IR code
+will receive an error code (see below the description of
+.B ldap_scherr2str()
+for an explanation of the values) and a pointer to a NUL-terminated
+string will be placed where requested by argument
+.IR errp
+, indicating where in argument
+.IR s
+the error happened, so it must not be freed by the caller.  Argument
+.IR flags
+is a bit mask of parsing options controlling the relaxation of the
+syntax recognized.  The following values are defined:
+.TP
+.B LDAP_SCHEMA_ALLOW_NONE
+strict parsing according to RFC 2252.
+.TP
+.B LDAP_SCHEMA_ALLOW_NO_OID
+permit definitions that do not contain an initial OID.
+.TP
+.B LDAP_SCHEMA_ALLOW_QUOTED
+permit quotes around some items that should not have them.
+.TP
+.B LDAP_SCHEMA_ALLOW_DESCR
+permit a
+.B descr
+instead of a numeric OID in places where the syntax expect the latter.
+.TP
+.B LDAP_SCHEMA_ALLOW_DESCR_PREFIX
+permit that the initial numeric OID contains a prefix in
+.B descr
+format.
+.TP
+.B LDAP_SCHEMA_ALLOW_ALL
+be very liberal, include all options.
+.LP
+The structures returned are as follows:
+.sp
+.RS
+.nf
+.ne 7
+.ta 8n 16n 32n
+typedef struct ldap_schema_extension_item {
+	char *lsei_name;	/* Extension name */
+	char **lsei_values;	/* Extension values */
+} LDAPSchemaExtensionItem;
+
+typedef struct ldap_syntax {
+	char *syn_oid;		/* OID */
+	char **syn_names;	/* Names */
+	char *syn_desc;		/* Description */
+	LDAPSchemaExtensionItem **syn_extensions; /* Extension */
+} LDAPSyntax;
+
+typedef struct ldap_matchingrule {
+	char *mr_oid;		/* OID */
+	char **mr_names;	/* Names */
+	char *mr_desc;		/* Description */
+	int  mr_obsolete;	/* Is obsolete? */
+	char *mr_syntax_oid;	/* Syntax of asserted values */
+	LDAPSchemaExtensionItem **mr_extensions; /* Extensions */
+} LDAPMatchingRule;
+
+typedef struct ldap_attributetype {
+	char *at_oid;		/* OID */
+	char **at_names;	/* Names */
+	char *at_desc;		/* Description */
+	int  at_obsolete;	/* Is obsolete? */
+	char *at_sup_oid;	/* OID of superior type */
+	char *at_equality_oid;	/* OID of equality matching rule */
+	char *at_ordering_oid;	/* OID of ordering matching rule */
+	char *at_substr_oid;	/* OID of substrings matching rule */
+	char *at_syntax_oid;	/* OID of syntax of values */
+	int  at_syntax_len;	/* Suggested minimum maximum length */
+	int  at_single_value;	/* Is single-valued?  */
+	int  at_collective;	/* Is collective? */
+	int  at_no_user_mod;	/* Are changes forbidden through LDAP? */
+	int  at_usage;		/* Usage, see below */
+	LDAPSchemaExtensionItem **at_extensions; /* Extensions */
+} LDAPAttributeType;
+
+typedef struct ldap_objectclass {
+	char *oc_oid;		/* OID */
+	char **oc_names;	/* Names */
+	char *oc_desc;		/* Description */
+	int  oc_obsolete;	/* Is obsolete? */
+	char **oc_sup_oids;	/* OIDs of superior classes */
+	int  oc_kind;		/* Kind, see below */
+	char **oc_at_oids_must;	/* OIDs of required attribute types */
+	char **oc_at_oids_may;	/* OIDs of optional attribute types */
+	LDAPSchemaExtensionItem **oc_extensions; /* Extensions */
+} LDAPObjectClass;
+.ta
+.fi
+.RE
+.PP
+Some integer fields (those described with a question mark) have a
+truth value, for these fields the possible values are:
+.TP
+.B LDAP_SCHEMA_NO
+The answer to the question is no.
+.TP
+.B LDAP_SCHEMA_YES
+The answer to the question is yes.
+.LP
+For attribute types, the following usages are possible:
+.TP
+.B LDAP_SCHEMA_USER_APPLICATIONS
+the attribute type is non-operational.
+.TP
+.B LDAP_SCHEMA_DIRECTORY_OPERATION
+the attribute type is operational and is pertinent to the directory
+itself, i.e. it has the same value on all servers that master the
+entry containing this attribute type.
+.TP
+.B LDAP_SCHEMA_DISTRIBUTED_OPERATION
+the attribute type is operational and is pertinent to replication,
+shadowing or other distributed directory aspect.  TBC.
+.TP
+.B LDAP_SCHEMA_DSA_OPERATION
+the attribute type is operational and is pertinent to the directory
+server itself, i.e. it may have different values for the same entry
+when retrieved from different servers that master the entry.
+.LP
+Object classes can be of three kinds:
+.TP
+.B LDAP_SCHEMA_ABSTRACT
+the object class is abstract, i.e. there cannot be entries of this
+class alone.
+.TP
+.B LDAP_SCHEMA_STRUCTURAL
+the object class is structural, i.e. it describes the main role of the
+entry.  On some servers, once the entry is created the set of
+structural object classes assigned cannot be changed: none of those
+present can be removed and none other can be added.
+.TP
+.B LDAP_SCHEMA_AUXILIARY
+the object class is auxiliary, i.e. it is intended to go with other,
+structural, object classes.  These can be added or removed at any time
+if attribute types are added or removed at the same time as needed by
+the set of object classes resulting from the operation.
+.LP
+Routines
+.B ldap_xxx2name()
+return a canonical name for the definition.
+.LP
+Routines
+.B ldap_xxx2str()
+return a string representation in the format described by RFC 2252 of
+the struct passed in the argument.  The string is a newly allocated
+string that must be freed by the caller.  These routines may return
+NULL if no memory can be allocated for the string.
+.LP
+.B ldap_scherr2str()
+returns a NUL-terminated string with a text description of the error
+found.  This is a pointer to a static area, so it must not be freed by
+the caller.  The argument
+.IR code
+comes from one of the parsing routines and can adopt the following
+values:
+.TP
+.B LDAP_SCHERR_OUTOFMEM
+Out of memory.
+.TP
+.B LDAP_SCHERR_UNEXPTOKEN
+Unexpected token.
+.TP
+.B LDAP_SCHERR_NOLEFTPAREN
+Missing opening parenthesis.
+.TP
+.B LDAP_SCHERR_NORIGHTPAREN
+Missing closing parenthesis.
+.TP
+.B LDAP_SCHERR_NODIGIT
+Expecting digit.
+.TP
+.B LDAP_SCHERR_BADNAME
+Expecting a name.
+.TP
+.B LDAP_SCHERR_BADDESC
+Bad description.
+.TP
+.B LDAP_SCHERR_BADSUP
+Bad superiors.
+.TP
+.B LDAP_SCHERR_DUPOPT
+Duplicate option.
+.TP
+.B LDAP_SCHERR_EMPTY
+Unexpected end of data.
+
+.SH SEE ALSO
+.BR ldap (3),
+.SH ACKNOWLEDGEMENTS
+.B	OpenLDAP
+is developed and maintained by The OpenLDAP Project (http://www.openldap.org/).
+.B	OpenLDAP
+is derived from University of Michigan LDAP 3.3 Release.  
+
diff --git a/doc/man/man8/slapd.8 b/doc/man/man8/slapd.8
index 949f866e68e8176f92f3843e0057aa77df310fe6..4a92d7aad4c324feb46f136c7ca0dd7fdd54b09a 100644
--- a/doc/man/man8/slapd.8
+++ b/doc/man/man8/slapd.8
@@ -6,12 +6,12 @@
 slapd \- Stand-alone LDAP Daemon
 .SH SYNOPSIS
 .B LIBEXECDIR/slapd 
+.B [\-d debug\-level]
 .B [\-f slapd\-config\-file]
 .B [\-h URLs]
-.B [\-d debug\-level]
 .B [\-n service\-name] [\-s syslog\-level] [\-l syslog\-local\-user]
 .B [\-r directory]
-.B [\-u user] [\-g group]
+.B [\-u user] [\-g group] [\-t]
 .B 
 .SH DESCRIPTION
 .LP
@@ -144,6 +144,12 @@ will run with the specified group name or id.
 Note that on some systems, running as a non-privileged user will prevent
 passwd back-ends from accessing the encrypted passwords.  Note also that
 any shell back-ends will run as the specified non-privileged user.
+.TP
+.BI \-t
+.B slapd
+will read the configuration file (the default if none is given with the 
+\fI\-f\fP switch) and check its syntax, without opening any listener 
+or database.
 .SH EXAMPLES
 To start 
 .I slapd
@@ -167,6 +173,14 @@ on voluminous debugging which will be printed on standard error, type:
 .ft
 .fi
 .LP
+To test whether the configuration file is correct or not, type:
+.LP
+.nf
+.ft tt
+	LIBEXECDIR/slapd -t
+.ft
+.fi
+.LP
 .SH "SEE ALSO"
 .BR ldap (3),
 .BR slapd.conf (5),
diff --git a/libraries/liblber/bprint.c b/libraries/liblber/bprint.c
index a6ae8ce7ed68337196edc622c1382071d440ba36..23ebf88853e05261216084530eb2f2a0e4ebdffd 100644
--- a/libraries/liblber/bprint.c
+++ b/libraries/liblber/bprint.c
@@ -285,7 +285,7 @@ int ber_output_dump(
         
         off = BP_GRAPH + n + ((n >= 8)?1:0);
         
-        if ( isprint( data[i] )) {
+        if ( isprint( (unsigned char) data[i] )) {
             line[ BP_GRAPH + n ] = data[i];
         } else {
             line[ BP_GRAPH + n ] = '.';
diff --git a/libraries/libldap/cyrus.c b/libraries/libldap/cyrus.c
index 5f128098d353f079f95da282ee06beb5c46ecb95..d14e893c058357e21e0bffac8a99350001a696cc 100644
--- a/libraries/libldap/cyrus.c
+++ b/libraries/libldap/cyrus.c
@@ -846,7 +846,7 @@ int ldap_pvt_sasl_secprops(
 		} else if( !strncasecmp(props[i],
 			"minssf=", sizeof("minssf")) )
 		{
-			if( isdigit( props[i][sizeof("minssf")] ) ) {
+			if( isdigit( (unsigned char) props[i][sizeof("minssf")] ) ) {
 				got_min_ssf++;
 				min_ssf = atoi( &props[i][sizeof("minssf")] );
 			} else {
@@ -856,7 +856,7 @@ int ldap_pvt_sasl_secprops(
 		} else if( !strncasecmp(props[i],
 			"maxssf=", sizeof("maxssf")) )
 		{
-			if( isdigit( props[i][sizeof("maxssf")] ) ) {
+			if( isdigit( (unsigned char) props[i][sizeof("maxssf")] ) ) {
 				got_max_ssf++;
 				max_ssf = atoi( &props[i][sizeof("maxssf")] );
 			} else {
@@ -866,7 +866,7 @@ int ldap_pvt_sasl_secprops(
 		} else if( !strncasecmp(props[i],
 			"maxbufsize=", sizeof("maxbufsize")) )
 		{
-			if( isdigit( props[i][sizeof("maxbufsize")] ) ) {
+			if( isdigit( (unsigned char) props[i][sizeof("maxbufsize")] ) ) {
 				got_maxbufsize++;
 				maxbufsize = atoi( &props[i][sizeof("maxbufsize")] );
 			} else {
diff --git a/libraries/libldap/result.c b/libraries/libldap/result.c
index c99e2a84ff93a43036a1e537d1d7b30116392ca9..5905f43afa1417870e1c8397fd238b1fb052599e 100644
--- a/libraries/libldap/result.c
+++ b/libraries/libldap/result.c
@@ -647,7 +647,8 @@ try_read1msg(
 		{
 			tmpber = *ber;	/* struct copy */
 			if ( v3ref == 1 ) {
-				; /* V3 search reference or V3 referral sucessfully chased */
+				/* V3 search reference or V3 referral successfully chased */
+				refer_cnt = 0;
 			} else if ( ber_scanf( &tmpber, "{iaa}", &lderr,
 			    &lr->lr_res_matched, &lr->lr_res_error )
 			    != LBER_ERROR ) {
diff --git a/libraries/liblunicode/ucdata/ucdata.c b/libraries/liblunicode/ucdata/ucdata.c
new file mode 100644
index 0000000000000000000000000000000000000000..e4ded225fae9c60db866aa19c14febb81e6f7c62
--- /dev/null
+++ b/libraries/liblunicode/ucdata/ucdata.c
@@ -0,0 +1,1325 @@
+/* $OpenLDAP$ */
+/*
+ * Copyright 2000-2002 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+/*
+ * 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 "ucdata.h"
+
+/**************************************************************************
+ *
+ * Miscellaneous types, data, and support functions.
+ *
+ **************************************************************************/
+
+typedef struct {
+    unsigned short bom;
+    unsigned short cnt;
+    union {
+        unsigned long bytes;
+        unsigned short len[2];
+    } size;
+} _ucheader_t;
+
+/*
+ * A simple array of 32-bit masks for lookup.
+ */
+static unsigned long 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))
+
+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;
+}
+
+/**************************************************************************
+ *
+ * Support for the character properties.
+ *
+ **************************************************************************/
+
+static unsigned long  _ucprop_size;
+static unsigned short *_ucprop_offsets;
+static unsigned long  *_ucprop_ranges;
+
+/*
+ * Return -1 on error, 0 if okay
+ */
+static int
+_ucprop_load(char *paths, int reload)
+{
+    FILE *in;
+    unsigned long 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 = (unsigned short *) 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(unsigned short))) & 3)
+      size += 4 - (size & 3);
+    size >>= 1;
+    _ucprop_ranges = (unsigned long *) (_ucprop_offsets + size);
+
+    /*
+     * Load the offset array.
+     */
+    fread((char *) _ucprop_offsets, sizeof(unsigned short), 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(unsigned long),
+          _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;
+}
+
+static int
+_ucprop_lookup(unsigned long code, unsigned long 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(unsigned long code, unsigned long mask1, unsigned long mask2)
+{
+    unsigned long 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.
+ *
+ **************************************************************************/
+
+static unsigned long _uccase_size;
+static unsigned short _uccase_len[2];
+static unsigned long *_uccase_map;
+
+/*
+ * Return -1 on error, 0 if okay
+ */
+static int
+_uccase_load(char *paths, int reload)
+{
+    FILE *in;
+    unsigned long 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 * 3;
+    _uccase_len[0] = hdr.size.len[0] * 3;
+    _uccase_len[1] = hdr.size.len[1] * 3;
+
+    _uccase_map = (unsigned long *)
+        malloc(_uccase_size * sizeof(unsigned long));
+
+    /*
+     * Load the case mapping table.
+     */
+    fread((char *) _uccase_map, sizeof(unsigned long), _uccase_size, in);
+
+    /*
+     * Do an endian swap if necessary.
+     */
+    if (hdr.bom == 0xfffe) {
+        for (i = 0; i < _uccase_size; 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;
+}
+
+static unsigned long
+_uccase_lookup(unsigned long code, long l, long r, int field)
+{
+    long m;
+
+    /*
+     * 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;
+        m -= (m % 3);
+        if (code > _uccase_map[m])
+          l = m + 3;
+        else if (code < _uccase_map[m])
+          r = m - 3;
+        else if (code == _uccase_map[m])
+          return _uccase_map[m + field];
+    }
+
+    return code;
+}
+
+unsigned long
+uctoupper(unsigned long 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]) - 3;
+    } else {
+        /*
+         * The character is title case.
+         */
+        field = 1;
+        l = _uccase_len[0] + _uccase_len[1];
+        r = _uccase_size - 3;
+    }
+    return _uccase_lookup(code, l, r, field);
+}
+
+unsigned long
+uctolower(unsigned long 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] - 3;
+    } else {
+        /*
+         * The character is title case.
+         */
+        field = 2;
+        l = _uccase_len[0] + _uccase_len[1];
+        r = _uccase_size - 3;
+    }
+    return _uccase_lookup(code, l, r, field);
+}
+
+unsigned long
+uctotitle(unsigned long 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] - 3;
+    } else {
+        /*
+         * The character is lower case.
+         */
+        l = _uccase_len[0];
+        r = (l + _uccase_len[1]) - 3;
+    }
+    return _uccase_lookup(code, l, r, field);
+}
+
+/**************************************************************************
+ *
+ * Support for compositions.
+ *
+ **************************************************************************/
+
+static unsigned long  _uccomp_size;
+static unsigned long *_uccomp_data;
+
+/*
+ * Return -1 on error, 0 if okay
+ */
+static int
+_uccomp_load(char *paths, int reload)
+{
+    FILE *in;
+    unsigned long 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 = (unsigned long *) malloc(hdr.size.bytes);
+
+    /*
+     * Read the composition data in.
+     */
+    size = hdr.size.bytes / sizeof(unsigned long);
+    fread((char *) _uccomp_data, sizeof(unsigned long), 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;
+}
+
+int
+uccomp(unsigned long node1, unsigned long node2, unsigned long *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(unsigned long *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;
+    unsigned long 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 < (unsigned long) LCount) {
+            unsigned long vindex = ch - VBase;
+            if (vindex < (unsigned long) 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 < (unsigned long) SCount
+			&& (sindex % TCount) == 0)
+		{
+            unsigned long tindex = ch - TBase;
+            if (tindex <= (unsigned long) 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(unsigned long *str, int len)
+{
+    int i, stpos, copos;
+    unsigned long 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.
+ *
+ **************************************************************************/
+
+static unsigned long  _ucdcmp_size;
+static unsigned long *_ucdcmp_nodes;
+static unsigned long *_ucdcmp_decomp;
+
+/*
+ * Return -1 on error, 0 if okay
+ */
+static int
+_ucdcmp_load(char *paths, int reload)
+{
+    FILE *in;
+    unsigned long 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 = (unsigned long *) malloc(hdr.size.bytes);
+    _ucdcmp_decomp = _ucdcmp_nodes + (_ucdcmp_size + 1);
+
+    /*
+     * Read the decomposition data in.
+     */
+    size = hdr.size.bytes / sizeof(unsigned long);
+    fread((char *) _ucdcmp_nodes, sizeof(unsigned long), 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;
+}
+
+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;
+}
+
+int
+ucdecomp(unsigned long code, unsigned long *num, unsigned long **decomp)
+{
+    long l, r, m;
+
+    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 = &_ucdcmp_decomp[_ucdcmp_nodes[m + 1]];
+            return 1;
+        }
+    }
+    return 0;
+}
+
+int
+ucdecomp_hangul(unsigned long code, unsigned long *num, unsigned long decomp[])
+{
+    if (!ucishangul(code))
+      return 0;
+
+    code -= 0xac00;
+    decomp[0] = 0x1100 + (unsigned long) (code / 588);
+    decomp[1] = 0x1161 + (unsigned long) ((code % 588) / 28);
+    decomp[2] = 0x11a7 + (unsigned long) (code % 28);
+    *num = (decomp[2] != 0x11a7) ? 3 : 2;
+
+    return 1;
+}
+
+int
+uccanondecomp(const unsigned long *in, int inlen,
+              unsigned long **out, int *outlen)
+{
+    int l, size;
+	unsigned i, j, k;
+    unsigned long num, class, *decomp, hangdecomp[3];
+
+    size = inlen;
+    *out = (unsigned long *) malloc(size * sizeof(**out));
+    if (*out == NULL)
+        return *outlen = -1;
+
+    i = 0;
+    for (j = 0; j < (unsigned) inlen; j++) {
+        if (ucdecomp(in[j], &num, &decomp)) {
+            if ( size - i < num) {
+                size = inlen + i - j + num - 1;
+                *out = (unsigned long *) realloc(*out, size * sizeof(**out));
+                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 = (unsigned long *) realloc(*out, size * sizeof(**out));
+                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 = (unsigned long *) realloc(*out, size * sizeof(**out));
+                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;
+}
+
+/**************************************************************************
+ *
+ * Support for combining classes.
+ *
+ **************************************************************************/
+
+static unsigned long  _uccmcl_size;
+static unsigned long *_uccmcl_nodes;
+
+/*
+ * Return -1 on error, 0 if okay
+ */
+static int
+_uccmcl_load(char *paths, int reload)
+{
+    FILE *in;
+    unsigned long 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 = (unsigned long *) malloc(hdr.size.bytes);
+
+    /*
+     * Read the combining classes in.
+     */
+    fread((char *) _uccmcl_nodes, sizeof(unsigned long), _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;
+}
+
+unsigned long
+uccombining_class(unsigned long 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.
+ *
+ **************************************************************************/
+
+static unsigned long *_ucnum_nodes;
+static unsigned long _ucnum_size;
+static short *_ucnum_vals;
+
+/*
+ * Return -1 on error, 0 if okay
+ */
+static int
+_ucnumb_load(char *paths, int reload)
+{
+    FILE *in;
+    unsigned long 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 = (unsigned long *) 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(unsigned long) << 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;
+}
+
+int
+ucnumber_lookup(unsigned long 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 = _ucnum_vals + _ucnum_nodes[m + 1];
+            num->numerator = (int) *vp++;
+            num->denominator = (int) *vp;
+            return 1;
+        }
+    }
+    return 0;
+}
+
+int
+ucdigit_lookup(unsigned long 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 = _ucnum_vals + _ucnum_nodes[m + 1];
+            if (*vp == *(vp + 1)) {
+              *digit = *vp;
+              return 1;
+            }
+            return 0;
+        }
+    }
+    return 0;
+}
+
+struct ucnumber
+ucgetnumber(unsigned long 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(unsigned long 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.
+ *
+ **************************************************************************/
+
+/*
+ * 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;
+
+    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();
+}
+
+/*
+ * 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;
+
+    return -error;
+}
+
+#ifdef TEST
+
+void
+main(void)
+{
+    int dig;
+    unsigned long 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.numerator != num.denominator)
+          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.numerator != num.denominator)
+          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.numerator != num.denominator)
+          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.numerator != num.denominator)
+          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.numerator != num.denominator)
+      printf("UCGETNUMBER: 0x30 = %d/%d\n", num.numerator, num.denominator);
+    else
+      printf("UCGETNUMBER: 0x30 = %d\n", num.numerator);
+
+    num = ucgetnumber(0xbc);
+    if (num.numerator != num.denominator)
+      printf("UCGETNUMBER: 0xbc = %d/%d\n", num.numerator, num.denominator);
+    else
+      printf("UCGETNUMBER: 0xbc = %d\n", num.numerator);
+
+    num = ucgetnumber(0xff19);
+    if (num.numerator != num.denominator)
+      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/ucgendat.c b/libraries/liblunicode/ucdata/ucgendat.c
new file mode 100644
index 0000000000000000000000000000000000000000..2b6712231621c7510099f1acb072212f8cccc7a9
--- /dev/null
+++ b/libraries/liblunicode/ucdata/ucgendat.c
@@ -0,0 +1,1561 @@
+/* $OpenLDAP$ */
+/*
+ * Copyright 2000-2002 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+/*
+ * 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/stdlib.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+
+#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 unsigned short 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 {
+    unsigned long *ranges;
+    unsigned short used;
+    unsigned short 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 unsigned short propcnt[NEEDPROPS];
+
+/*
+ * Array used to collect a decomposition before adding it to the decomposition
+ * table.
+ */
+static unsigned long dectmp[64];
+static unsigned long dectmp_size;
+
+typedef struct {
+    unsigned long code;
+    unsigned short size;
+    unsigned short used;
+    unsigned long *decomp;
+} _decomp_t;
+
+/*
+ * List of decomposition.  Created and expanded in order as the characters are
+ * encountered.
+ */
+static _decomp_t *decomps;
+static unsigned long decomps_used;
+static unsigned long decomps_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 unsigned long compexs[2048];
+
+/*
+ * Struct for holding a composition pair, and array of composition pairs
+ */
+typedef struct {
+    unsigned long comp;
+    unsigned long count;
+    unsigned long code1;
+    unsigned long code2;
+} _comp_t;
+
+static _comp_t *comps;
+static unsigned long comps_used;
+
+/*
+ * Types and lists for handling lists of case mappings.
+ */
+typedef struct {
+    unsigned long key;
+    unsigned long other1;
+    unsigned long other2;
+} _case_t;
+
+static _case_t *upper;
+static _case_t *lower;
+static _case_t *title;
+static unsigned long upper_used;
+static unsigned long upper_size;
+static unsigned long lower_used;
+static unsigned long lower_size;
+static unsigned long title_used;
+static unsigned long title_size;
+
+/*
+ * Array used to collect case mappings before adding them to a list.
+ */
+static unsigned long cases[3];
+
+/*
+ * An array to hold ranges for combining classes.
+ */
+static unsigned long *ccl;
+static unsigned long ccl_used;
+static unsigned long ccl_size;
+
+/*
+ * Structures for handling numbers.
+ */
+typedef struct {
+    unsigned long code;
+    unsigned long 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 unsigned long ncodes_used;
+static unsigned long ncodes_size;
+
+static _num_t *nums;
+static unsigned long nums_used;
+static unsigned long nums_size;
+
+/*
+ * Array for holding numbers.
+ */
+static _num_t *nums;
+static unsigned long nums_used;
+static unsigned long nums_size;
+
+static void
+add_range(unsigned long start, unsigned long 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 = (unsigned long *)
+                  malloc(sizeof(unsigned long) << 3);
+            else
+              rlp->ranges = (unsigned long *)
+                  realloc((char *) rlp->ranges,
+                          sizeof(unsigned long) * (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(unsigned long c, char *name, int len)
+{
+    int i, j;
+    unsigned long 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 = (unsigned long *)
+              malloc(sizeof(unsigned long) << 3);
+        else
+          rlp->ranges = (unsigned long *)
+              realloc((char *) rlp->ranges,
+                      sizeof(unsigned long) * (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(unsigned long code)
+{
+    unsigned long i, j, size;
+
+    /*
+     * Add the code to the composite property.
+     */
+    ordered_range_insert(code, "Cm", 2);
+
+    /*
+     * Locate the insertion point for the code.
+     */
+    for (i = 0; i < decomps_used && code > decomps[i].code; i++) ;
+
+    /*
+     * Allocate space for a new decomposition.
+     */
+    if (decomps_used == decomps_size) {
+        if (decomps_size == 0)
+          decomps = (_decomp_t *) malloc(sizeof(_decomp_t) << 3);
+        else
+          decomps = (_decomp_t *)
+              realloc((char *) decomps,
+                      sizeof(_decomp_t) * (decomps_size + 8));
+        (void) memset((char *) (decomps + decomps_size), '\0',
+                      sizeof(_decomp_t) << 3);
+        decomps_size += 8;
+    }
+
+    if (i < decomps_used && code != decomps[i].code) {
+        /*
+         * Shift the decomps up by one if the codes don't match.
+         */
+        for (j = decomps_used; j > i; j--)
+          (void) AC_MEMCPY((char *) &decomps[j], (char *) &decomps[j - 1],
+                        sizeof(_decomp_t));
+    }
+
+    /*
+     * Insert or replace a decomposition.
+     */
+    size = dectmp_size + (4 - (dectmp_size & 3));
+    if (decomps[i].size < size) {
+        if (decomps[i].size == 0)
+          decomps[i].decomp = (unsigned long *)
+              malloc(sizeof(unsigned long) * size);
+        else
+          decomps[i].decomp = (unsigned long *)
+              realloc((char *) decomps[i].decomp,
+                      sizeof(unsigned long) * size);
+        decomps[i].size = size;
+    }
+
+    if (decomps[i].code != code)
+      decomps_used++;
+
+    decomps[i].code = code;
+    decomps[i].used = dectmp_size;
+    (void) AC_MEMCPY((char *) decomps[i].decomp, (char *) dectmp,
+                  sizeof(unsigned long) * 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 (dectmp_size == 2)
+      comps_used++;
+}
+
+static void
+add_title(unsigned long code)
+{
+    unsigned long i, j;
+
+    /*
+     * Always map the code to itself.
+     */
+    cases[2] = 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(unsigned long code)
+{
+    unsigned long 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(unsigned long code)
+{
+    unsigned long 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(unsigned long c, unsigned long ccl_code)
+{
+    unsigned long i, j;
+
+    if (ccl_used == ccl_size) {
+        if (ccl_size == 0)
+          ccl = (unsigned long *) malloc(sizeof(unsigned long) * 24);
+        else
+          ccl = (unsigned long *)
+              realloc((char *) ccl, sizeof(unsigned long) * (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 unsigned long
+make_number(short num, short denom)
+{
+    unsigned long 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(unsigned long code, short num, short denom)
+{
+    unsigned long 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)
+{
+    unsigned long i, lineno, skip, code, ccl_code;
+    short wnum, neg, number[2];
+    char line[512], *s, *e;
+
+    lineno = skip = 0;
+    while (fscanf(in, "%[^\n]\n", line) != EOF) {
+        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.
+         */
+        switch (code) {
+          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;
+        }
+
+        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++) ;
+    
+        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 != ';' && *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)
+              add_decomp(code);
+        }
+
+        /*
+         * 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] = number[0];
+
+            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 (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(unsigned long code)
+{
+    long l, r, m;
+
+    l = 0;
+    r = decomps_used - 1;
+    while (l <= r) {
+        m = (l + r) >> 1;
+        if (code > decomps[m].code)
+          l = m + 1;
+        else if (code < decomps[m].code)
+          r = m - 1;
+        else
+          return &decomps[m];
+    }
+    return 0;
+}
+
+static void
+decomp_it(_decomp_t *d)
+{
+    unsigned long i;
+    _decomp_t *dp;
+
+    for (i = 0; i < d->used; i++) {
+        if ((dp = find_decomp(d->decomp[i])) != 0)
+          decomp_it(dp);
+        else
+          dectmp[dectmp_size++] = d->decomp[i];
+    }
+}
+
+/*
+ * Expand all decompositions by recursively decomposing each character
+ * in the decomposition.
+ */
+static void
+expand_decomp(void)
+{
+    unsigned long i;
+
+    for (i = 0; i < decomps_used; i++) {
+        dectmp_size = 0;
+        decomp_it(&decomps[i]);
+        if (dectmp_size > 0)
+          add_decomp(decomps[i].code);
+    }
+}
+
+static int
+cmpcomps(_comp_t *comp1, _comp_t *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)
+{
+    unsigned short i, code;
+    char line[512], *s;
+
+    (void) memset((char *) compexs, 0, sizeof(unsigned long) << 11);
+
+    while (fscanf(in, "%[^\n]\n", line) != EOF) {
+        /*
+         * Skip blank lines and lines that start with a '#'.
+         */
+        if (line[0] == 0 || line[0] == '#')
+	    continue;
+
+	/*
+         * Collect the code.  Assume max 4 digits
+         */
+
+	for (s = line, i = code = 0; *s != '#' && i < 4; 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;
+        }
+        COMPEX_SET(code);
+    }
+}
+
+/*
+ * Creates array of compositions from decomposition array
+ */
+static void
+create_comps(void)
+{
+    unsigned long 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),
+	  (int (*)(const void *, const void *)) cmpcomps);
+}
+
+static void
+write_cdata(char *opath)
+{
+    FILE *out;
+    unsigned long i, idx, bytes, nprops;
+    unsigned short casecnt[2];
+    char path[BUFSIZ];
+
+    /*****************************************************************
+     *
+     * Generate the ctype data.
+     *
+     *****************************************************************/
+
+    /*
+     * Open the ctype.dat file.
+     */
+    sprintf(path, "%s%sctype.dat", opath, LDAP_DIRSEP);
+    if ((out = fopen(path, "wb")) == 0)
+      return;
+
+    /*
+     * 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(unsigned short) * (NUMPROPS + 1)) & 3)
+      bytes += 4 - (bytes & 3);
+    nprops = bytes / sizeof(unsigned short);
+    bytes += sizeof(unsigned long) * idx;
+        
+    /*
+     * Write the header.
+     */
+    fwrite((char *) hdr, sizeof(unsigned short), 2, out);
+
+    /*
+     * Write the byte count.
+     */
+    fwrite((char *) &bytes, sizeof(unsigned long), 1, out);
+
+    /*
+     * Write the property list counts.
+     */
+    fwrite((char *) propcnt, sizeof(unsigned short), nprops, out);
+
+    /*
+     * Write the property lists.
+     */
+    for (i = 0; i < NUMPROPS; i++) {
+        if (proptbl[i].used > 0)
+          fwrite((char *) proptbl[i].ranges, sizeof(unsigned long),
+                 proptbl[i].used, out);
+    }
+
+    fclose(out);
+
+    /*****************************************************************
+     *
+     * Generate the case mapping data.
+     *
+     *****************************************************************/
+
+    /*
+     * Open the case.dat file.
+     */
+    sprintf(path, "%s%scase.dat", opath, LDAP_DIRSEP);
+    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(unsigned short), 2, out);
+
+    /*
+     * Write the upper and lower case table sizes.
+     */
+    fwrite((char *) casecnt, sizeof(unsigned short), 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);
+
+    /*****************************************************************
+     *
+     * Generate the composition data.
+     *
+     *****************************************************************/
+    
+    /*
+     * Create compositions from decomposition data
+     */
+    create_comps();
+    
+    /*
+     * Open the comp.dat file.
+     */
+    sprintf(path, "%s%scomp.dat", opath, LDAP_DIRSEP);
+    if ((out = fopen(path, "wb")) == 0)
+	return;
+    
+    /*
+     * Write the header.
+     */
+    hdr[1] = (unsigned short) comps_used * 4;
+    fwrite((char *) hdr, sizeof(unsigned short), 2, out);
+    
+    /*
+     * Write out the byte count to maintain header size.
+     */
+    bytes = comps_used * sizeof(_comp_t);
+    fwrite((char *) &bytes, sizeof(unsigned long), 1, out);
+    
+    /*
+     * Now, if comps exist, write them out.
+     */
+    if (comps_used > 0)
+        fwrite((char *) comps, sizeof(_comp_t), comps_used, out);
+    
+    fclose(out);
+    
+    /*****************************************************************
+     *
+     * Generate the decomposition data.
+     *
+     *****************************************************************/
+
+    /*
+     * Fully expand all decompositions before generating the output file.
+     */
+    expand_decomp();
+
+    /*
+     * Open the decomp.dat file.
+     */
+    sprintf(path, "%s%sdecomp.dat", opath, LDAP_DIRSEP);
+    if ((out = fopen(path, "wb")) == 0)
+      return;
+
+    hdr[1] = decomps_used;
+
+    /*
+     * Write the header.
+     */
+    fwrite((char *) hdr, sizeof(unsigned short), 2, out);
+
+    /*
+     * Write a temporary byte count which will be calculated as the
+     * decompositions are written out.
+     */
+    bytes = 0;
+    fwrite((char *) &bytes, sizeof(unsigned long), 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(unsigned long), 1, out);
+            fwrite((char *) &idx, sizeof(unsigned long), 1, out);
+            idx += decomps[i].used;
+        }
+
+        /*
+         * Write the sentinel index as the last decomp node.
+         */
+        fwrite((char *) &idx, sizeof(unsigned long), 1, out);
+
+        /*
+         * Write the decompositions themselves.
+         */
+        for (i = 0; i < decomps_used; i++)
+          fwrite((char *) decomps[i].decomp, sizeof(unsigned long),
+                 decomps[i].used, out);
+
+        /*
+         * Seek back to the beginning and write the byte count.
+         */
+        bytes = (sizeof(unsigned long) * idx) +
+            (sizeof(unsigned long) * ((hdr[1] << 1) + 1));
+        fseek(out, sizeof(unsigned short) << 1, 0L);
+        fwrite((char *) &bytes, sizeof(unsigned long), 1, out);
+
+        fclose(out);
+    }
+
+    /*****************************************************************
+     *
+     * Generate the combining class data.
+     *
+     *****************************************************************/
+
+    /*
+     * Open the cmbcl.dat file.
+     */
+    sprintf(path, "%s%scmbcl.dat", opath, LDAP_DIRSEP);
+    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(unsigned short), 2, out);
+
+    /*
+     * Write out the byte count to maintain header size.
+     */
+    bytes = ccl_used * sizeof(unsigned long);
+    fwrite((char *) &bytes, sizeof(unsigned long), 1, out);
+
+    if (ccl_used > 0)
+      /*
+       * Write the combining class ranges out.
+       */
+      fwrite((char *) ccl, sizeof(unsigned long), ccl_used, out);
+
+    fclose(out);
+
+    /*****************************************************************
+     *
+     * Generate the number data.
+     *
+     *****************************************************************/
+
+    /*
+     * Open the num.dat file.
+     */
+    sprintf(path, "%s%snum.dat", opath, LDAP_DIRSEP);
+    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] = (unsigned short) (ncodes_used << 1);
+    bytes = (ncodes_used * sizeof(_codeidx_t)) + (nums_used * sizeof(_num_t));
+
+    /*
+     * Write the header.
+     */
+    fwrite((char *) hdr, sizeof(unsigned short), 2, out);
+
+    /*
+     * Write out the byte count to maintain header size.
+     */
+    fwrite((char *) &bytes, sizeof(unsigned long), 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);
+    }
+
+    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;
+
+    if ((prog = strrchr(argv[0], *LDAP_DIRSEP)) != 0)
+      prog++;
+    else
+      prog = argv[0];
+
+    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], "rb")) == 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], "rb")) == 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/ucstr.c b/libraries/liblunicode/ucstr.c
index f0cc6033221fdedb51faf08ce53657652c9f0abf..18a882111d7506ce6be17d181ad5b0a6bfb09713 100644
--- a/libraries/liblunicode/ucstr.c
+++ b/libraries/liblunicode/ucstr.c
@@ -167,7 +167,7 @@ struct berval * UTF8bvnormalize(
 		i = 0;
 	}
 
-	p = ucs = (long *) malloc( len * sizeof(*ucs) );
+	p = ucs = malloc( len * sizeof(*ucs) );
 	if ( ucs == NULL ) {
 		free(out);
 		return NULL;
@@ -342,7 +342,7 @@ int UTF8bvnormcmp(
 	 * proper normalized form.
 	 */
 
-	ucs = (long *) malloc( ( ( norm1 || l1 > l2 ) ? l1 : l2 ) * sizeof(*ucs) );
+	ucs = malloc( ( ( norm1 || l1 > l2 ) ? l1 : l2 ) * sizeof(*ucs) );
 	if ( ucs == NULL ) {
 		return l1 > l2 ? 1 : -1; /* what to do??? */
 	}
@@ -365,7 +365,7 @@ int UTF8bvnormcmp(
 	if ( norm1 ) {
 		ucsout1 = ucs;
 		l1 = ulen;
-		ucs = (long *) malloc( l2 * sizeof(*ucs) );
+		ucs = malloc( l2 * sizeof(*ucs) );
 		if ( ucs == NULL ) {
 			return l1 > l2 ? 1 : -1; /* what to do??? */
 		}
diff --git a/libraries/liblutil/passwd.c b/libraries/liblutil/passwd.c
index ee9282310cb85f6302e15753d021a00776fdd159..dba06c9b2b1d8e5901726477d4a9755de7e5c901 100644
--- a/libraries/liblutil/passwd.c
+++ b/libraries/liblutil/passwd.c
@@ -28,7 +28,11 @@
 #endif /* SLAPD_LMHASH */
 
 #ifdef SLAPD_SPASSWD
-#	include <sasl.h>
+#	ifdef HAVE_SASL_SASL_H
+#		include <sasl/sasl.h>
+#	else
+#		include <sasl.h>
+#	endif
 #endif
 
 #ifdef SLAPD_KPASSWD
@@ -659,14 +663,18 @@ static int chk_sasl(
 
 #ifdef HAVE_CYRUS_SASL
 	if( lutil_passwd_sasl_conn != NULL ) {
-		const char *errstr = NULL;
 		int sc;
-
+# if SASL_VERSION_MAJOR < 2
+		const char *errstr = NULL;
 		sc = sasl_checkpass( lutil_passwd_sasl_conn,
 			passwd->bv_val, passwd->bv_len,
 			cred->bv_val, cred->bv_len,
 			&errstr );
-
+# else
+		sc = sasl_checkpass( lutil_passwd_sasl_conn,
+			passwd->bv_val, passwd->bv_len,
+			cred->bv_val, cred->bv_len );
+# endif
 		rtn = ( sc != SASL_OK );
 	}
 #endif
diff --git a/libraries/librewrite/map.c b/libraries/librewrite/map.c
index 2180d8907757164816ad4c23e19b8babe54a5fe2..8db54af13711b89701804bb6fdcf5a214cf20012 100644
--- a/libraries/librewrite/map.c
+++ b/libraries/librewrite/map.c
@@ -245,19 +245,20 @@ rewrite_map_parse(
 	for ( p = string, cnt = 1; p[ 0 ] != '\0' && cnt > 0; p++ ) {
 		if ( p[ 0 ] == REWRITE_SUBMATCH_ESCAPE ) {
 			/*
-			 * '\' marks the beginning of a new map
+			 * '%' marks the beginning of a new map
 			 */
 			if ( p[ 1 ] == '{' ) {
 				cnt++;
 			/*
-			 * '\' followed by a digit may mark the beginning
+			 * '%' followed by a digit may mark the beginning
 			 * of an old map
 			 */
 			} else if ( isdigit( (unsigned char) p[ 1 ] ) && p[ 2 ] == '{' ) {
 				cnt++;
 				p++;
 			}
-			p++;
+			if ( p[ 1 ] != '\0' )
+				p++;
 		} else if ( p[ 0 ] == '}' ) {
 			cnt--;
 		}
diff --git a/libraries/librewrite/parse.c b/libraries/librewrite/parse.c
index a8129c41aed5ecfd83b2bce50d2961282eff3beb..bc83e0594e575a6a0cc61c4ce12ec07ef05e0452 100644
--- a/libraries/librewrite/parse.c
+++ b/libraries/librewrite/parse.c
@@ -45,7 +45,7 @@ parse_line(
 	}
 	
 	for ( begin = p;  p[ 0 ] != '\0'; p++ ) {
-		if ( p[ 0 ] == '\\' ) {
+		if ( p[ 0 ] == '\\' && p[ 1 ] != '\0' ) {
 			p++;
 		} else if ( p[ 0 ] == '\'' || p[ 0 ] == '\"') {
 			if ( in_quoted_field && p[ 0 ] == quote ) {
diff --git a/libraries/librewrite/subst.c b/libraries/librewrite/subst.c
index 1024517463519212239e7985e28a8f1b6cd81b61..4c0bc411e0fa627c0d77084e2a116da33c635f06 100644
--- a/libraries/librewrite/subst.c
+++ b/libraries/librewrite/subst.c
@@ -53,7 +53,7 @@ rewrite_subst_compile(
 	for ( p = begin = result, subs_len = 0; p[ 0 ] != '\0'; p++ ) {
 		
 		/*
-		 * Keep only single escapes '\'
+		 * Keep only single escapes '%'
 		 */
 		if ( p[ 0 ] != REWRITE_SUBMATCH_ESCAPE ) {
 			continue;
diff --git a/servers/slapd/aclparse.c b/servers/slapd/aclparse.c
index 95397c2668eb71b7d04639b4e679df4f75d082bc..f53d273aa6373e5edbb946b996a252555962bea1 100644
--- a/servers/slapd/aclparse.c
+++ b/servers/slapd/aclparse.c
@@ -22,14 +22,14 @@ static void		split(char *line, int splitchar, char **left, char **right);
 static void		access_append(Access **l, Access *a);
 static void		acl_usage(void) LDAP_GCCATTR((noreturn));
 
-static void 		acl_regex_normalized_dn(struct berval *pattern);
+static void		acl_regex_normalized_dn(const char *src, struct berval *pat);
 
 #ifdef LDAP_DEBUG
 static void		print_acl(Backend *be, AccessControl *a);
 static void		print_access(Access *b);
 #endif
 
-static int
+static void
 regtest(const char *fname, int lineno, char *pat) {
 	int e;
 	regex_t re;
@@ -79,10 +79,8 @@ regtest(const char *fname, int lineno, char *pat) {
 			"%s: line %d: regular expression \"%s\" bad because of %s\n",
 			fname, lineno, pat, error );
 		acl_usage();
-		return(0);
 	}
 	regfree(&re);
-	return(1);
 }
 
 void
@@ -166,7 +164,7 @@ parse_acl(
 							|| strcmp(right, ".*") == 0 
 							|| strcmp(right, ".*$") == 0 
 							|| strcmp(right, "^.*") == 0 
-							|| strcmp(right, "^.*$$") == 0
+							|| strcmp(right, "^.*$") == 0
 							|| strcmp(right, ".*$$") == 0 
 							|| strcmp(right, "^.*$$") == 0 )
 						{
@@ -174,8 +172,7 @@ parse_acl(
 							a->acl_dn_pat.bv_len = sizeof("*")-1;
 
 						} else {
-							a->acl_dn_pat.bv_val = right;
-							acl_regex_normalized_dn( &a->acl_dn_pat );
+							acl_regex_normalized_dn( right, &a->acl_dn_pat );
 						}
 					} else if ( strcasecmp( style, "base" ) == 0 ) {
 						a->acl_dn_style = ACL_STYLE_BASE;
@@ -236,7 +233,13 @@ parse_acl(
 			if( a->acl_dn_pat.bv_len != 0 ) {
 				if ( a->acl_dn_style != ACL_STYLE_REGEX ) {
 					struct berval bv;
-					dnNormalize2( NULL, &a->acl_dn_pat, &bv);
+					rc = dnNormalize2( NULL, &a->acl_dn_pat, &bv);
+					if ( rc != LDAP_SUCCESS ) {
+						fprintf( stderr,
+							"%s: line %d: bad DN \"%s\"\n",
+							fname, lineno, a->acl_dn_pat.bv_val );
+						acl_usage();
+					}
 					free( a->acl_dn_pat.bv_val );
 					a->acl_dn_pat = bv;
 				} else {
@@ -373,8 +376,7 @@ parse_acl(
 								1, &bv);
 
 						} else {
-							bv.bv_val = right;
-							acl_regex_normalized_dn( &bv );
+							acl_regex_normalized_dn( right, &bv );
 							if ( !ber_bvccmp( &bv, '*' ) ) {
 								regtest(fname, lineno, bv.bv_val);
 							}
@@ -402,7 +404,13 @@ parse_acl(
 					}
 
 					if ( sty != ACL_STYLE_REGEX && expand == 0 ) {
-						dnNormalize2(NULL, &bv, &b->a_dn_pat);
+						rc = dnNormalize2(NULL, &bv, &b->a_dn_pat);
+						if ( rc != LDAP_SUCCESS ) {
+							fprintf( stderr,
+								"%s: line %d: bad DN \"%s\"\n",
+								fname, lineno, bv.bv_val );
+							acl_usage();
+						}
 						free(bv.bv_val);
 					} else {
 						b->a_dn_pat = bv;
@@ -490,15 +498,20 @@ parse_acl(
 
 					b->a_group_style = sty;
 					if (sty == ACL_STYLE_REGEX) {
-						bv.bv_val = right;
-						acl_regex_normalized_dn( &bv );
+						acl_regex_normalized_dn( right, &bv );
 						if ( !ber_bvccmp( &bv, '*' ) ) {
 							regtest(fname, lineno, bv.bv_val);
 						}
 						b->a_group_pat = bv;
 					} else {
 						ber_str2bv( right, 0, 0, &bv );
-						dnNormalize2( NULL, &bv, &b->a_group_pat );
+						rc = dnNormalize2( NULL, &bv, &b->a_group_pat );
+						if ( rc != LDAP_SUCCESS ) {
+							fprintf( stderr,
+								"%s: line %d: bad DN \"%s\"\n",
+								fname, lineno, right );
+							acl_usage();
+						}
 					}
 
 					if (value && *value) {
@@ -625,8 +638,7 @@ parse_acl(
 
 					b->a_peername_style = sty;
 					if (sty == ACL_STYLE_REGEX) {
-						bv.bv_val = right;
-						acl_regex_normalized_dn( &bv );
+						acl_regex_normalized_dn( right, &bv );
 						if ( !ber_bvccmp( &bv, '*' ) ) {
 							regtest(fname, lineno, bv.bv_val);
 						}
@@ -661,8 +673,7 @@ parse_acl(
 
 					b->a_sockname_style = sty;
 					if (sty == ACL_STYLE_REGEX) {
-						bv.bv_val = right;
-						acl_regex_normalized_dn( &bv );
+						acl_regex_normalized_dn( right, &bv );
 						if ( !ber_bvccmp( &bv, '*' ) ) {
 							regtest(fname, lineno, bv.bv_val);
 						}
@@ -704,8 +715,7 @@ parse_acl(
 					b->a_domain_style = sty;
 					b->a_domain_expand = expand;
 					if (sty == ACL_STYLE_REGEX) {
-						bv.bv_val = right;
-						acl_regex_normalized_dn( &bv );
+						acl_regex_normalized_dn( right, &bv );
 						if ( !ber_bvccmp( &bv, '*' ) ) {
 							regtest(fname, lineno, bv.bv_val);
 						}
@@ -740,8 +750,7 @@ parse_acl(
 
 					b->a_sockurl_style = sty;
 					if (sty == ACL_STYLE_REGEX) {
-						bv.bv_val = right;
-						acl_regex_normalized_dn( &bv );
+						acl_regex_normalized_dn( right, &bv );
 						if ( !ber_bvccmp( &bv, '*' ) ) {
 							regtest(fname, lineno, bv.bv_val);
 						}
@@ -1191,19 +1200,19 @@ str2accessmask( const char *str )
 		}
 
 		for( i=1; str[i] != '\0'; i++ ) {
-			if( TOLOWER(str[i]) == 'w' ) {
+			if( TOLOWER((unsigned char) str[i]) == 'w' ) {
 				ACL_PRIV_SET(mask, ACL_PRIV_WRITE);
 
-			} else if( TOLOWER(str[i]) == 'r' ) {
+			} else if( TOLOWER((unsigned char) str[i]) == 'r' ) {
 				ACL_PRIV_SET(mask, ACL_PRIV_READ);
 
-			} else if( TOLOWER(str[i]) == 's' ) {
+			} else if( TOLOWER((unsigned char) str[i]) == 's' ) {
 				ACL_PRIV_SET(mask, ACL_PRIV_SEARCH);
 
-			} else if( TOLOWER(str[i]) == 'c' ) {
+			} else if( TOLOWER((unsigned char) str[i]) == 'c' ) {
 				ACL_PRIV_SET(mask, ACL_PRIV_COMPARE);
 
-			} else if( TOLOWER(str[i]) == 'x' ) {
+			} else if( TOLOWER((unsigned char) str[i]) == 'x' ) {
 				ACL_PRIV_SET(mask, ACL_PRIV_AUTH);
 
 			} else if( str[i] != '0' ) {
@@ -1270,25 +1279,26 @@ acl_usage( void )
 }
 
 /*
+ * Set pattern to a "normalized" DN from src.
  * At present it simply eats the (optional) space after 
  * a RDN separator (,)
  * Eventually will evolve in a more complete normalization
- *
- * Note that the input berval only needs bv_val, it ignores
- * the input bv_len and sets it on return.
  */
 static void
 acl_regex_normalized_dn(
+	const char *src,
 	struct berval *pattern
 )
 {
 	char *str, *p;
+	ber_len_t len;
 
-	str = ch_strdup( pattern->bv_val );
+	str = ch_strdup( src );
+	len = strlen( src );
 
 	for ( p = str; p && p[ 0 ]; p++ ) {
 		/* escape */
-		if ( p[ 0 ] == '\\' ) {
+		if ( p[ 0 ] == '\\' && p[ 1 ] ) {
 			/* 
 			 * if escaping a hex pair we should
 			 * increment p twice; however, in that 
@@ -1309,7 +1319,7 @@ acl_regex_normalized_dn(
 				for ( q = &p[ 2 ]; q[ 0 ] == ' '; q++ ) {
 					/* DO NOTHING */ ;
 				}
-				AC_MEMCPY( p+1, q, pattern->bv_len-(q-str)+1);
+				AC_MEMCPY( p+1, q, len-(q-str)+1);
 			}
 		}
 	}
diff --git a/servers/slapd/back-bdb/cache.c b/servers/slapd/back-bdb/cache.c
index 1dd74f1cd8e873509b565e0b7f21a135aeee19a0..5038db4bc799bacf7dbe6f7982e621f379a1a0db 100644
--- a/servers/slapd/back-bdb/cache.c
+++ b/servers/slapd/back-bdb/cache.c
@@ -167,7 +167,7 @@ bdb_cache_return_entry_rw( Cache *cache, Entry *e, int rw )
 	ID id;
 	int refcnt, freeit = 1;
 
-	/* set cache mutex */
+	/* set cache write lock */
 	ldap_pvt_thread_rdwr_wlock( &cache->c_rwlock );
 
 	assert( e->e_private );
@@ -183,8 +183,10 @@ bdb_cache_return_entry_rw( Cache *cache, Entry *e, int rw )
 	 * for instance)
 	 */
 	if (  BEI(e)->bei_state == CACHE_ENTRY_CREATING ) {
+		/* set lru mutex */
 		ldap_pvt_thread_mutex_lock( &cache->lru_mutex );
 		bdb_cache_delete_entry_internal( cache, e );
+		/* free lru mutex */
 		ldap_pvt_thread_mutex_unlock( &cache->lru_mutex );
 		freeit = 0;
 		/* now the entry is in DELETED state */
@@ -193,7 +195,7 @@ bdb_cache_return_entry_rw( Cache *cache, Entry *e, int rw )
 	if ( BEI(e)->bei_state == CACHE_ENTRY_COMMITTED ) {
 		BEI(e)->bei_state = CACHE_ENTRY_READY;
 
-		/* free cache mutex */
+		/* free cache write lock */
 		ldap_pvt_thread_rdwr_wunlock( &cache->c_rwlock );
 
 #ifdef NEW_LOGGING
@@ -209,7 +211,7 @@ bdb_cache_return_entry_rw( Cache *cache, Entry *e, int rw )
 
 	} else if ( BEI(e)->bei_state == CACHE_ENTRY_DELETED ) {
 		if( refcnt > 0 ) {
-			/* free cache mutex */
+			/* free cache write lock */
 			ldap_pvt_thread_rdwr_wunlock( &cache->c_rwlock );
 
 #ifdef NEW_LOGGING
@@ -228,7 +230,7 @@ bdb_cache_return_entry_rw( Cache *cache, Entry *e, int rw )
 				bdb_entry_return( e );
 			}
 
-			/* free cache mutex */
+			/* free cache write lock */
 			ldap_pvt_thread_rdwr_wunlock( &cache->c_rwlock );
 
 #ifdef NEW_LOGGING
@@ -243,7 +245,7 @@ bdb_cache_return_entry_rw( Cache *cache, Entry *e, int rw )
 		}
 
 	} else {
-		/* free cache mutex */
+		/* free cache write lock */
 		ldap_pvt_thread_rdwr_wunlock( &cache->c_rwlock );
 
 #ifdef NEW_LOGGING
@@ -304,14 +306,14 @@ bdb_cache_add_entry_rw(
 		   "bdb_cache_add_entry_rw: add (%s):%s to cache\n",
 		   e->e_dn, rw ? "w" : "r" ));
 #endif
-	/* set cache mutex */
+	/* set cache write lock */
 	ldap_pvt_thread_rdwr_wlock( &cache->c_rwlock );
 
 	assert( e->e_private == NULL );
 
 	if( bdb_cache_entry_private_init(e) != 0 ) {
-		/* free cache mutex */
-		ldap_pvt_thread_mutex_unlock( &cache->lru_mutex );
+		/* free cache write lock */
+		ldap_pvt_thread_rdwr_wunlock( &cache->c_rwlock );
 
 #ifdef NEW_LOGGING
 		LDAP_LOG(( "cache", LDAP_LEVEL_ERR,
@@ -330,7 +332,7 @@ bdb_cache_add_entry_rw(
 	if ( avl_insert( &cache->c_dntree, (caddr_t) e,
 		(AVL_CMP) entry_dn_cmp, avl_dup_error ) != 0 )
 	{
-		/* free cache mutex */
+		/* free cache write lock */
 		ldap_pvt_thread_rdwr_wunlock( &cache->c_rwlock );
 
 #ifdef NEW_LOGGING
@@ -378,7 +380,7 @@ bdb_cache_add_entry_rw(
 
 		bdb_cache_entry_private_destroy(e);
 
-		/* free cache mutex */
+		/* free cache write lock */
 		ldap_pvt_thread_rdwr_wunlock( &cache->c_rwlock );
 		return( -1 );
 	}
@@ -390,6 +392,7 @@ bdb_cache_add_entry_rw(
 	BEI(e)->bei_state = CACHE_ENTRY_CREATING;
 	BEI(e)->bei_refcnt = 1;
 
+	/* set lru mutex */
 	ldap_pvt_thread_mutex_lock( &cache->lru_mutex );
 	/* lru */
 	LRU_ADD( cache, e );
@@ -429,8 +432,9 @@ bdb_cache_add_entry_rw(
 		}
 	}
 
-	/* free cache mutex */
+	/* free lru mutex */
 	ldap_pvt_thread_mutex_unlock( &cache->lru_mutex );
+	/* free cache write lock */
 	ldap_pvt_thread_rdwr_wunlock( &cache->c_rwlock );
 	return( 0 );
 }
@@ -450,7 +454,7 @@ bdb_cache_update_entry(
 	int	i, rc;
 	Entry	*ee;
 
-	/* set cache mutex */
+	/* set cache write lock */
 	ldap_pvt_thread_rdwr_wlock( &cache->c_rwlock );
 
 	assert( e->e_private );
@@ -468,7 +472,7 @@ bdb_cache_update_entry(
 		    e->e_id, e->e_dn, 0 );
 #endif
 
-		/* free cache mutex */
+		/* free cache write lock */
 		ldap_pvt_thread_rdwr_wunlock( &cache->c_rwlock );
 		return( 1 );
 	}
@@ -501,7 +505,7 @@ bdb_cache_update_entry(
 #endif
 		}
 
-		/* free cache mutex */
+		/* free cache write lock */
 		ldap_pvt_thread_rdwr_wunlock( &cache->c_rwlock );
 		return( -1 );
 	}
@@ -511,6 +515,7 @@ bdb_cache_update_entry(
 	/* will be marked after when entry is returned */
 	BEI(e)->bei_state = CACHE_ENTRY_CREATING;
 
+	/* set lru mutex */
 	ldap_pvt_thread_mutex_lock( &cache->lru_mutex );
 	/* lru */
 	LRU_ADD( cache, e );
@@ -550,8 +555,9 @@ bdb_cache_update_entry(
 		}
 	}
 
-	/* free cache mutex */
+	/* free lru mutex */
 	ldap_pvt_thread_mutex_unlock( &cache->lru_mutex );
+	/* free cache write lock */
 	ldap_pvt_thread_rdwr_wunlock( &cache->c_rwlock );
 	return( 0 );
 }
@@ -571,7 +577,7 @@ bdb_cache_find_entry_ndn2id(
 	e.e_nname = *ndn;
 
 try_again:
-	/* set cache mutex */
+	/* set cache read lock */
 	ldap_pvt_thread_rdwr_rlock( &cache->c_rwlock );
 
 	if ( (ep = (Entry *) avl_find( cache->c_dntree, (caddr_t) &e,
@@ -598,7 +604,7 @@ try_again:
 		if ( state != CACHE_ENTRY_READY ) {
 			assert(state != CACHE_ENTRY_UNDEFINED);
 
-			/* free cache mutex */
+			/* free cache read lock */
 			ldap_pvt_thread_rdwr_runlock( &cache->c_rwlock );
 
 #ifdef NEW_LOGGING
@@ -616,15 +622,17 @@ try_again:
 			goto try_again;
 		}
 
+		/* free cache read lock */
 		ldap_pvt_thread_rdwr_runlock( &cache->c_rwlock );
 
+		/* set lru mutex */
 		ldap_pvt_thread_mutex_lock( &cache->lru_mutex );
 
 		/* lru */
 		LRU_DELETE( cache, ep );
 		LRU_ADD( cache, ep );
 		
-		/* free cache mutex */
+		/* free lru mutex */
 		ldap_pvt_thread_mutex_unlock( &cache->lru_mutex );
 
 #ifdef NEW_LOGGING
@@ -638,7 +646,7 @@ try_again:
 #endif
 
 	} else {
-		/* free cache mutex */
+		/* free cache read lock */
 		ldap_pvt_thread_rdwr_runlock( &cache->c_rwlock );
 
 		id = NOID;
@@ -665,7 +673,7 @@ bdb_cache_find_entry_id(
 	e.e_id = id;
 
 try_again:
-	/* set cache mutex */
+	/* set cache read lock */
 	ldap_pvt_thread_rdwr_rlock( &cache->c_rwlock );
 
 	if ( (ep = (Entry *) avl_find( cache->c_idtree, (caddr_t) &e,
@@ -688,7 +696,7 @@ try_again:
 
 			assert(state != CACHE_ENTRY_UNDEFINED);
 
-			/* free cache mutex */
+			/* free cache read lock */
 			ldap_pvt_thread_rdwr_runlock( &cache->c_rwlock );
 
 #ifdef NEW_LOGGING
@@ -713,7 +721,7 @@ try_again:
 			 * so, unlock the cache, yield, and try again.
 			 */
 
-			/* free cache mutex */
+			/* free cache read lock */
 			ldap_pvt_thread_rdwr_runlock( &cache->c_rwlock );
 
 #ifdef NEW_LOGGING
@@ -730,7 +738,9 @@ try_again:
 			goto try_again;
 		}
 
+		/* free cache read lock */
 		ldap_pvt_thread_rdwr_runlock( &cache->c_rwlock );
+		/* set lru mutex */
 		ldap_pvt_thread_mutex_lock( &cache->lru_mutex );
 		/* lru */
 		LRU_DELETE( cache, ep );
@@ -738,7 +748,7 @@ try_again:
 		
 		BEI(ep)->bei_refcnt++;
 
-		/* free cache mutex */
+		/* free lru mutex */
 		ldap_pvt_thread_mutex_unlock( &cache->lru_mutex );
 
 #ifdef NEW_LOGGING
@@ -755,7 +765,7 @@ try_again:
 		return( ep );
 	}
 
-	/* free cache mutex */
+	/* free cache read lock */
 	ldap_pvt_thread_rdwr_runlock( &cache->c_rwlock );
 
 	return( NULL );
@@ -780,7 +790,7 @@ bdb_cache_delete_entry(
 {
 	int	rc;
 
-	/* set cache mutex */
+	/* set cache write lock */
 	ldap_pvt_thread_rdwr_wlock( &cache->c_rwlock );
 
 	assert( e->e_private );
@@ -793,11 +803,13 @@ bdb_cache_delete_entry(
 		e->e_id, 0, 0 );
 #endif
 
+	/* set lru mutex */
 	ldap_pvt_thread_mutex_lock( &cache->lru_mutex );
 	rc = bdb_cache_delete_entry_internal( cache, e );
+	/* free lru mutex */
 	ldap_pvt_thread_mutex_unlock( &cache->lru_mutex );
 
-	/* free cache mutex */
+	/* free cache write lock */
 	ldap_pvt_thread_rdwr_wunlock( &cache->c_rwlock );
 	return( rc );
 }
@@ -846,8 +858,9 @@ bdb_cache_release_all( Cache *cache )
 	Entry *e;
 	int rc;
 
-	/* set cache mutex */
+	/* set cache write lock */
 	ldap_pvt_thread_rdwr_wlock( &cache->c_rwlock );
+	/* set lru mutex */
 	ldap_pvt_thread_mutex_lock( &cache->lru_mutex );
 
 #ifdef NEW_LOGGING
@@ -879,8 +892,9 @@ bdb_cache_release_all( Cache *cache )
 
 	}
 
-	/* free cache mutex */
+	/* free lru mutex */
 	ldap_pvt_thread_mutex_unlock( &cache->lru_mutex );
+	/* free cache write lock */
 	ldap_pvt_thread_rdwr_wunlock( &cache->c_rwlock );
 }
 
diff --git a/servers/slapd/back-bdb/init.c b/servers/slapd/back-bdb/init.c
index d0c27ba340abb03ddcbce38eb1d1d22cf8948d6a..0283e3170bbb1debb74491ca8df931ab5e6ec59b 100644
--- a/servers/slapd/back-bdb/init.c
+++ b/servers/slapd/back-bdb/init.c
@@ -443,6 +443,14 @@ bdb_db_destroy( BackendDB *be )
 		}
 	}
 
+#ifdef BDB_HIER
+	ldap_pvt_thread_rdwr_destroy( &bdb->bi_tree_rdwr );
+#endif
+	ldap_pvt_thread_rdwr_destroy ( &bdb->bi_cache.c_rwlock );
+	ldap_pvt_thread_mutex_destroy( &bdb->bi_cache.lru_mutex );
+	ldap_pvt_thread_mutex_destroy( &bdb->bi_lastid_mutex );
+	ldap_pvt_thread_mutex_destroy( &bdb->bi_database_mutex );
+
 	return 0;
 }
 
diff --git a/servers/slapd/back-ldap/suffixmassage.c b/servers/slapd/back-ldap/suffixmassage.c
new file mode 100644
index 0000000000000000000000000000000000000000..0847740f02fbcc1c09008ca63a45d733134f55c9
--- /dev/null
+++ b/servers/slapd/back-ldap/suffixmassage.c
@@ -0,0 +1,115 @@
+/* suffixmassage.c - massages ldap backend dns */
+/* $OpenLDAP$ */
+
+/* 
+ * Copyright 1999, Howard Chu, All rights reserved. <hyc@highlandsun.com>
+ * Copyright 2000, Pierangelo Masarati, All rights reserved. <ando@sys-net.it>
+ * 
+ * Module back-ldap, originally developed by Howard Chu
+ *
+ * has been modified by Pierangelo Masarati. The original copyright
+ * notice has been maintained.
+ * 
+ * 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.
+ */
+
+#include "portable.h"
+
+#ifndef ENABLE_REWRITE
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "back-ldap.h"
+
+/*
+ * ldap_back_dn_massage
+ * 
+ * Aliases the suffix; based on suffix_alias (servers/slapd/suffixalias.c).
+ */
+void
+ldap_back_dn_massage(
+	struct ldapinfo *li,
+	struct berval *dn,
+	struct berval *res,
+	int normalized,
+	int tofrom
+)
+{
+	int     i, src, dst;
+
+        if ( dn == NULL ) {
+		res->bv_val = NULL;
+		res->bv_len = 0;
+		return;
+	}
+        if ( li == NULL || li->suffix_massage == NULL ) {
+		*res = *dn;
+		return;
+	}
+
+	if ( tofrom ) {
+		src = 0 + normalized;
+		dst = 2 + normalized;
+	} else {
+		src = 2 + normalized;
+		dst = 0 + normalized;
+	}
+
+        for ( i = 0;
+                li->suffix_massage[i] != NULL;
+                i += 4 ) {
+                int aliasLength = li->suffix_massage[i+src]->bv_len;
+                int diff = dn->bv_len - aliasLength;
+
+                if ( diff < 0 ) {
+                        /* alias is longer than dn */
+                        continue;
+										                } else if ( diff > 0 ) {
+                        if ( normalized && ( ! DN_SEPARATOR(dn->bv_val[diff-1]) ) ) {
+                                /* boundary is not at a DN separator */
+                                continue;
+			}
+                        /* At a DN Separator */
+                        /* XXX or an escaped separator... oh well */
+                }
+
+                if ( !strcmp( li->suffix_massage[i+src]->bv_val, &dn->bv_val[diff] ) ) {
+			res->bv_len = diff + li->suffix_massage[i+dst]->bv_len;
+                        res->bv_val = ch_malloc( res->bv_len + 1 );
+                        strncpy( res->bv_val, dn->bv_val, diff );
+                        strcpy( &res->bv_val[diff], li->suffix_massage[i+dst]->bv_val );
+#ifdef NEW_LOGGING
+					LDAP_LOG (( "suffixmassage", LDAP_LEVEL_ARGS,
+						"ldap_back_dn_massage: converted \"%s\" to \"%s\"\n",
+						dn->bv_val, res->bv_val ));
+#else
+                        Debug( LDAP_DEBUG_ARGS,
+                                "ldap_back_dn_massage:"
+				" converted \"%s\" to \"%s\"\n",
+                                dn->bv_val, res->bv_val, 0 );
+#endif
+                        break;
+                }
+        }
+
+        return;
+}
+#endif /* !ENABLE_REWRITE */
diff --git a/servers/slapd/back-ldap/unbind.c b/servers/slapd/back-ldap/unbind.c
new file mode 100644
index 0000000000000000000000000000000000000000..f3f5617d5d49d614d81e51470d855d4bed0075b0
--- /dev/null
+++ b/servers/slapd/back-ldap/unbind.c
@@ -0,0 +1,106 @@
+/* unbind.c - ldap backend unbind function */
+/* $OpenLDAP$ */
+/*
+ * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+/* This is an altered version */
+/*
+ * Copyright 1999, Howard Chu, All rights reserved. <hyc@highlandsun.com>
+ * 
+ * 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.
+ *
+ *
+ *
+ * Copyright 2000, Pierangelo Masarati, All rights reserved. <ando@sys-net.it>
+ *
+ * This software is being modified by Pierangelo Masarati.
+ * The previously reported conditions apply to the modified code as well.
+ * Changes in the original code are highlighted where required.
+ * Credits for the original code go to the author, Howard Chu.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "back-ldap.h"
+
+int
+ldap_back_conn_destroy(
+    Backend		*be,
+    Connection		*conn
+)
+{
+	struct ldapinfo	*li = (struct ldapinfo *) be->be_private;
+	struct ldapconn *lc, lc_curr;
+
+#ifdef NEW_LOGGING
+	LDAP_LOG(( "backend", LDAP_LEVEL_INFO,
+			"ldap_back_conn_destroy: fetching conn %ld\n",
+			conn->c_connid ));
+#else /* !NEW_LOGGING */
+	Debug( LDAP_DEBUG_TRACE,
+		"=>ldap_back_conn_destroy: fetching conn %ld\n",
+		conn->c_connid, 0, 0 );
+#endif /* !NEW_LOGGING */
+
+	lc_curr.conn = conn;
+	
+	ldap_pvt_thread_mutex_lock( &li->conn_mutex );
+	lc = avl_delete( &li->conntree, (caddr_t)&lc_curr, ldap_back_conn_cmp );
+	ldap_pvt_thread_mutex_unlock( &li->conn_mutex );
+
+	if (lc) {
+#ifdef NEW_LOGGING
+		LDAP_LOG(( "backend", LDAP_LEVEL_DETAIL1,
+			"ldap_back_conn_destroy: destroying conn %ld\n",
+			conn->c_connid ));
+#else /* !NEW_LOGGING */
+		Debug( LDAP_DEBUG_TRACE,
+			"=>ldap_back_conn_destroy: destroying conn %ld\n",
+			lc->conn->c_connid, 0, 0 );
+#endif
+
+#ifdef ENABLE_REWRITE
+		/*
+		 * Cleanup rewrite session
+		 */
+		rewrite_session_delete( li->rwinfo, conn );
+#endif /* ENABLE_REWRITE */
+
+		/*
+		 * Needs a test because the handler may be corrupted,
+		 * and calling ldap_unbind on a corrupted header results
+		 * in a segmentation fault
+		 */
+		ldap_unbind(lc->ld);
+		if ( lc->bound_dn.bv_val ) {
+			ch_free( lc->bound_dn.bv_val );
+		}
+		ch_free( lc );
+	}
+
+	/* no response to unbind */
+
+	return 0;
+}
diff --git a/servers/slapd/back-ldbm/config.c b/servers/slapd/back-ldbm/config.c
index 3c8475024e5621e81739513579404b4ef4e2c1ca..2cb64aa675aa263749c569475003c23ccc26427c 100644
--- a/servers/slapd/back-ldbm/config.c
+++ b/servers/slapd/back-ldbm/config.c
@@ -177,7 +177,7 @@ ldbm_back_db_config(
 
 #else
 #ifdef NEW_LOGGING
-		LDAP_LOG (( "config",LDAP_LEVEL_ERR, "ldbm_back_db_config: "\"dbsync\""
+		LDAP_LOG (( "config", LDAP_LEVEL_ERR, "ldbm_back_db_config: \"dbsync\""
 			" policies not supported in non-threaded environments\n" ));
 #else	
 		Debug( LDAP_DEBUG_ANY,
diff --git a/servers/slapd/back-passwd/search.c b/servers/slapd/back-passwd/search.c
index 2cdeb4c9cf1671c2b1566d3a70f98805ebee3e1f..83db8ae5fea8d83d801f597dda78ab2e0aa9b024 100644
--- a/servers/slapd/back-passwd/search.c
+++ b/servers/slapd/back-passwd/search.c
@@ -325,7 +325,7 @@ pw2entry( Backend *be, struct passwd *pw, const char **text )
 			strncpy(buf, vals[0].bv_val, i);
 			s = buf+i;
 			strcpy(s, pw->pw_name);
-			*s = TOUPPER(*s);
+			*s = TOUPPER((unsigned char)*s);
 			strcat(s, vals[0].bv_val+i+1);
 			vals[0].bv_val = buf;
 		}
diff --git a/servers/slapd/back-perl/SampleLDAP.pm b/servers/slapd/back-perl/SampleLDAP.pm
new file mode 100644
index 0000000000000000000000000000000000000000..6bbcd0e5a375709548aa5d74b91a2a43f4c13de1
--- /dev/null
+++ b/servers/slapd/back-perl/SampleLDAP.pm
@@ -0,0 +1,300 @@
+
+=head1 Introduction
+
+This is a sample Perl module for the OpenLDAP server slapd.
+It also contains the documentation that you will need to
+get up and going.
+
+WARNING: the interfaces of this backen to the perl module
+MAY change.  Any suggestions would greatly be appreciated.
+
+
+=head1 Overview
+
+The Perl back end works by embedding a Perl interpreter into
+the slapd backend. Then when the configuration file indicates
+that we are going to be using a Perl backend it will get an
+option that tells it what module to use.  It then creates a 
+new Perl object that handles all the request for that particular
+instance of the back end.
+
+
+=head1 Interface
+
+You will need to create a method for each one of the
+following actions that you wish to handle.
+
+   * new        # Creates a new object.
+   * search     # Performs the ldap search
+   * compare    # does a compare
+   * modify     # modify's and entry
+   * add        # adds an entry to back end
+   * modrdn     # modifies a an entries rdn
+   * delete     # deletes an ldap entry
+   * config     # process unknown config file lines
+   * init       # called after backend is initialized
+
+=head2 new
+
+This method is called when the config file encounters a 
+B<perlmod> line. The module in that line is then effectively
+used into the perl interpreter, then the new method is called
+to create a new object.  Note that multiple instances of that
+object may be instantiated, as with any perl object.
+
+The new method doesn't receive any arguments other than the
+class name.
+
+RETURN: 
+
+=head2 search
+
+This method is called when a search request comes from a client.
+It arguments are as follow.
+
+  * obj reference
+  * base DN
+  * scope
+  * alias deferencing policy
+  * size limit
+  * time limit
+  * filter string
+  * attributes only flag ( 1 for yes )
+  * list of attributes that are to be returned. (could be empty)
+
+RETURN:
+
+=head2 compare
+
+This method is called when a compare request comes from a client.
+Its arguments are as follows.
+
+  * obj reference
+  * dn
+  * attribute assertion string
+
+RETURN:
+
+=head2 modify
+
+This method is called when a modify request comes from a client.
+Its arguments are as follows.
+
+  * obj reference
+  * dn
+  * lists formatted as follows
+   { ADD | DELETE | REPLACE }, key, value
+
+RETURN:
+
+=head2 add
+
+This method is called when a add request comes from a client.
+Its arguments are as follows.
+
+  * obj reference
+  * entry in string format.
+
+RETURN:
+
+=head2 modrdn
+
+This method is called when a modrdn request comes from a client.
+Its arguments are as follows.
+
+  * obj reference
+  * dn
+  * new rdn
+  * delete old dn flage ( 1 means yes )
+
+RETURN:
+
+=head2 delete
+
+This method is called when a delete request comes from a client.
+Its arguments are as follows.
+
+  * obj reference
+  * dn
+
+RETURN:
+
+=head2 config
+
+  * obj reference
+  * arrray of arguments on line
+
+RETURN: non zero value if this is not a valid option.
+
+=head2 init
+
+  * obj reference
+
+RETURN: non zero value if initialization failed.
+
+=head1 Configuration
+
+The perl section of the config file recognizes the following 
+options.  It should also be noted that any option not recoginized
+will be sent to the B<config> method of the perl module as noted
+above.
+
+  database perl         # startn section for the perl database
+
+  suffix          "o=AnyOrg, c=US"
+
+  perlModulePath /path/to/libs  # addes the path to @INC variable same
+                             # as "use lib '/path/to/libs'"
+
+  perlModule ModName       # use the module name ModName from ModName.pm
+
+  filterSearchResults      # search results are candidates that need to be
+                           # filtered, rather than search results to be 
+                           # returned directly to the client
+
+=cut
+
+package SampleLDAP;
+
+use POSIX;
+
+sub new
+{
+	my $class = shift;
+
+	my $this = {};
+	bless $this, $class;
+        print STDERR "Here in new\n";
+	print STDERR "Posix Var " . BUFSIZ . " and " . FILENAME_MAX . "\n";
+	return $this;
+}
+
+sub search
+{
+	my $this = shift;
+	my($base, $scope, $deref, $sizeLim, $timeLim, $filterStr, $attrOnly, @attrs ) = @_;
+        print STDERR "====$filterStr====\n";
+	$filterStr =~ s/\(|\)//g;
+	$filterStr =~ s/=/: /;
+
+	my @match_dn = ();
+	foreach my $dn ( keys %$this ) {
+		if ( $this->{ $dn } =~ /$filterStr/im ) {
+			push @match_dn, $dn;
+			last if ( scalar @match_dn == $sizeLim );
+
+		}
+	}
+
+	my @match_entries = ();
+	
+	foreach my $dn ( @match_dn )  {
+		push @match_entries, $this->{ $dn };
+	}
+
+	return ( 0 , @match_entries );
+
+}
+
+sub compare
+{
+	my $this = shift;
+	my ( $dn, $avaStr ) = @_;
+	my $rc = 5; # LDAP_COMPARE_FALSE
+
+	$avaStr =~ s/=/: /;
+
+	if ( $this->{ $dn } =~ /$avaStr/im ) {
+		$rc = 6; # LDAP_COMPARE_TRUE
+	}
+
+	return $rc;
+}
+
+sub modify
+{
+	my $this = shift;
+
+	my ( $dn, @list ) = @_;
+
+	while ( @list > 0 ) {
+		my $action = shift @list;
+		my $key    = shift @list;
+		my $value  = shift @list;
+
+		if( $action eq "ADD" ) {
+			$this->{ $dn } .= "$key: $value\n";
+
+		}
+		elsif( $action eq "DELETE" ) {
+			$this->{ $dn } =~ s/^$key:\s*$value\n//mi ;
+
+		}
+		elsif( $action eq "REPLACE" ) {
+			$this->{ $dn } =~ s/$key: .*$/$key: $value/im ;
+		}
+	}
+
+	return 0;
+}
+
+sub add
+{
+	my $this = shift;
+
+	my ( $entryStr ) = @_;
+
+	my ( $dn ) = ( $entryStr =~ /dn:\s(.*)$/m );
+
+	#
+	# This needs to be here untill a normalize dn is
+	# passed to this routine.
+	#
+	$dn = uc( $dn );
+	$dn =~ s/\s*//g;
+
+
+	$this->{$dn} = $entryStr;
+
+	return 0;
+}
+
+sub modrdn
+{
+	my $this = shift;
+
+	my ( $dn, $newdn, $delFlag ) = @_;
+
+	$this->{ $newdn } = $this->{ $dn };
+
+	if( $delFlag ) {
+		delete $this->{ $dn };
+	}
+	return 0;
+
+}
+
+sub delete
+{
+	my $this = shift;
+
+	my ( $dn ) = @_;
+	
+        print STDERR "XXXXXX $dn XXXXXXX\n";
+	delete $this->{$dn};
+}
+
+sub config
+{
+	my $this = shift;
+
+	my ( @args ) = @_;
+        local $, = " - ";
+        print STDERR @args;
+        print STDERR "\n";
+	return 0;
+}
+
+1;
+
+
diff --git a/servers/slapd/back-perl/add.c b/servers/slapd/back-perl/add.c
new file mode 100644
index 0000000000000000000000000000000000000000..c4a54ef5fcd8c26a0f13d269fa6987fff54456a4
--- /dev/null
+++ b/servers/slapd/back-perl/add.c
@@ -0,0 +1,77 @@
+/* $OpenLDAP$ */
+/*
+ *	 Copyright 1999, John C. Quillan, All rights reserved.
+ *	 Portions Copyright 2002, myinternet Limited. All rights reserved.
+ *
+ *	 Redistribution and use in source and binary forms are permitted only
+ *	 as authorized by the OpenLDAP Public License.	A copy of this
+ *	 license is available at http://www.OpenLDAP.org/license.html or
+ *	 in file LICENSE in the top-level directory of the distribution.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include "slap.h"
+#ifdef HAVE_WIN32_ASPERL
+#include "asperl_undefs.h"
+#endif
+
+#include <EXTERN.h>
+#include <perl.h>
+
+#include "perl_back.h"
+
+int
+perl_back_add(
+	Backend	*be,
+	Connection	*conn,
+	Operation	*op,
+	Entry	*e
+)
+{
+	int len;
+	int count;
+	int return_code;
+
+	PerlBackend *perl_back = (PerlBackend *) be->be_private;
+
+	ldap_pvt_thread_mutex_lock( &perl_interpreter_mutex );
+	ldap_pvt_thread_mutex_lock( &entry2str_mutex );
+
+	{
+		dSP; ENTER; SAVETMPS;
+
+		PUSHMARK(sp);
+		XPUSHs( perl_back->pb_obj_ref );
+		XPUSHs(sv_2mortal(newSVpv( entry2str( e, &len ), 0 )));
+
+		PUTBACK;
+
+#ifdef PERL_IS_5_6
+		count = call_method("add", G_SCALAR);
+#else
+		count = perl_call_method("add", G_SCALAR);
+#endif
+
+		SPAGAIN;
+
+		if (count != 1) {
+			croak("Big trouble in back_add\n");
+		}
+							 
+		return_code = POPi;
+
+		PUTBACK; FREETMPS; LEAVE;
+	}
+
+	ldap_pvt_thread_mutex_unlock( &entry2str_mutex );
+	ldap_pvt_thread_mutex_unlock( &perl_interpreter_mutex );	
+
+	send_ldap_result( conn, op, return_code,
+		NULL, NULL, NULL, NULL );
+
+	Debug( LDAP_DEBUG_ANY, "Perl ADD\n", 0, 0, 0 );
+	return( 0 );
+}
diff --git a/servers/slapd/back-perl/bind.c b/servers/slapd/back-perl/bind.c
new file mode 100644
index 0000000000000000000000000000000000000000..a286224563303ee2cc860d91f867fd3a52bebac9
--- /dev/null
+++ b/servers/slapd/back-perl/bind.c
@@ -0,0 +1,91 @@
+/* $OpenLDAP$ */
+/*
+ *	 Copyright 1999, John C. Quillan, All rights reserved.
+ *	 Portions Copyright 2002, myinternet Limited. All rights reserved.
+ *
+ *	 Redistribution and use in source and binary forms are permitted only
+ *	 as authorized by the OpenLDAP Public License.	A copy of this
+ *	 license is available at http://www.OpenLDAP.org/license.html or
+ *	 in file LICENSE in the top-level directory of the distribution.
+ */
+
+#include "portable.h"
+/* init.c - initialize Perl backend */
+	
+#include <stdio.h>
+
+#include "slap.h"
+#ifdef HAVE_WIN32_ASPERL
+#include "asperl_undefs.h"
+#endif
+
+#include <EXTERN.h>
+#include <XSUB.h>
+#include <perl.h>
+
+#include "perl_back.h"
+
+
+/**********************************************************
+ *
+ * Bind
+ *
+ **********************************************************/
+int
+perl_back_bind(
+	Backend *be,
+	Connection *conn,
+	Operation *op,
+	struct berval *dn,
+	struct berval *ndn,
+	int method,
+	struct berval *cred,
+	struct berval *edn
+)
+{
+	int return_code;
+	int count;
+
+	PerlBackend *perl_back = (PerlBackend *) be->be_private;
+
+#ifdef HAVE_WIN32_ASPERL
+	PERL_SET_CONTEXT( PERL_INTERPRETER );
+#endif
+
+	ldap_pvt_thread_mutex_lock( &perl_interpreter_mutex );	
+
+	{
+		dSP; ENTER; SAVETMPS;
+
+		PUSHMARK(SP);
+		XPUSHs( perl_back->pb_obj_ref );
+		XPUSHs(sv_2mortal(newSVpv( dn->bv_val , 0)));
+		XPUSHs(sv_2mortal(newSVpv( cred->bv_val , cred->bv_len)));
+		PUTBACK;
+
+#ifdef PERL_IS_5_6
+		count = call_method("bind", G_SCALAR);
+#else
+		count = perl_call_method("bind", G_SCALAR);
+#endif
+
+		SPAGAIN;
+
+		if (count != 1) {
+			croak("Big trouble in back_bind\n");
+		}
+
+		return_code = POPi;
+							 
+
+		PUTBACK; FREETMPS; LEAVE;
+	}
+
+	ldap_pvt_thread_mutex_unlock( &perl_interpreter_mutex );	
+
+	Debug( LDAP_DEBUG_ANY, "Perl BIND returned 0x%04x\n", return_code, 0, 0 );
+
+	send_ldap_result( conn, op, return_code, NULL, NULL, NULL, NULL );
+
+	return ( return_code );
+}
diff --git a/servers/slapd/back-perl/close.c b/servers/slapd/back-perl/close.c
new file mode 100644
index 0000000000000000000000000000000000000000..f16478b3f361ff1bbab898f38102eaaa245abca1
--- /dev/null
+++ b/servers/slapd/back-perl/close.c
@@ -0,0 +1,69 @@
+/* $OpenLDAP$ */
+/*
+ *	 Copyright 1999, John C. Quillan, All rights reserved.
+ *	 Portions Copyright 2002, myinternet Limited. All rights reserved.
+ *
+ *	 Redistribution and use in source and binary forms are permitted only
+ *	 as authorized by the OpenLDAP Public License.	A copy of this
+ *	 license is available at http://www.OpenLDAP.org/license.html or
+ *	 in file LICENSE in the top-level directory of the distribution.
+ */
+
+#include "portable.h"
+/* init.c - initialize shell backend */
+	
+#include <stdio.h>
+
+#include "slap.h"
+#ifdef HAVE_WIN32_ASPERL
+#include "asperl_undefs.h"
+#endif
+
+#include <EXTERN.h>
+#include <perl.h>
+
+#include "perl_back.h"
+
+/**********************************************************
+ *
+ * Close
+ *
+ **********************************************************/
+
+int
+perl_back_close(
+	BackendInfo *bd
+)
+{
+	ldap_pvt_thread_mutex_lock( &perl_interpreter_mutex );	
+
+	perl_destruct(PERL_INTERPRETER);
+
+	ldap_pvt_thread_mutex_unlock( &perl_interpreter_mutex );	
+
+	return 0;
+}
+
+int
+perl_back_destroy(
+	BackendInfo *bd
+)
+{
+	perl_free(PERL_INTERPRETER);
+	PERL_INTERPRETER = NULL;
+
+	ldap_pvt_thread_mutex_destroy( &perl_interpreter_mutex );	
+
+	return 0;
+}
+
+int
+perl_back_db_destroy(
+	BackendDB *be
+)
+{
+	free( be->be_private );
+	be->be_private = NULL;
+
+	return 0;
+}
diff --git a/servers/slapd/back-perl/compare.c b/servers/slapd/back-perl/compare.c
new file mode 100644
index 0000000000000000000000000000000000000000..cf167b3b31273130fbcee2e01acb0002128f5e99
--- /dev/null
+++ b/servers/slapd/back-perl/compare.c
@@ -0,0 +1,94 @@
+/* $OpenLDAP$ */
+/*
+ *	 Copyright 1999, John C. Quillan, All rights reserved.
+ *	 Portions Copyright 2002, myinternet Limited. All rights reserved.
+ *
+ *	 Redistribution and use in source and binary forms are permitted only
+ *	 as authorized by the OpenLDAP Public License.	A copy of this
+ *	 license is available at http://www.OpenLDAP.org/license.html or
+ *	 in file LICENSE in the top-level directory of the distribution.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include "slap.h"
+#ifdef HAVE_WIN32_ASPERL
+#include "asperl_undefs.h"
+#endif
+
+#include <EXTERN.h>
+#include <perl.h>
+
+#include "perl_back.h"
+
+/**********************************************************
+ *
+ * Compare
+ *
+ **********************************************************/
+
+int
+perl_back_compare(
+	Backend	*be,
+	Connection	*conn,
+	Operation	*op,
+	struct berval	*dn,
+	struct berval	*ndn,
+	AttributeAssertion		*ava
+)
+{
+	int return_code;
+	int count;
+	char *avastr, *ptr;
+
+	PerlBackend *perl_back = (PerlBackend *)be->be_private;
+
+	avastr = ch_malloc( ava->aa_desc->ad_cname.bv_len + 1 +
+		ava->aa_value.bv_len + 1 );
+	
+	slap_strcopy( slap_strcopy( slap_strcopy( avastr,
+		ava->aa_desc->ad_cname.bv_val ), "=" ),
+		ava->aa_value.bv_val );
+
+	ldap_pvt_thread_mutex_lock( &perl_interpreter_mutex );	
+
+	{
+		dSP; ENTER; SAVETMPS;
+
+		PUSHMARK(sp);
+		XPUSHs( perl_back->pb_obj_ref );
+		XPUSHs(sv_2mortal(newSVpv( dn->bv_val , 0)));
+		XPUSHs(sv_2mortal(newSVpv( avastr , 0)));
+		PUTBACK;
+
+#ifdef PERL_IS_5_6
+		count = call_method("compare", G_SCALAR);
+#else
+		count = perl_call_method("compare", G_SCALAR);
+#endif
+
+		SPAGAIN;
+
+		if (count != 1) {
+			croak("Big trouble in back_compare\n");
+		}
+
+		return_code = POPi;
+							 
+		PUTBACK; FREETMPS; LEAVE;
+	}
+
+	ldap_pvt_thread_mutex_unlock( &perl_interpreter_mutex );	
+
+	ch_free( avastr );
+
+	send_ldap_result( conn, op, return_code,
+		NULL, NULL, NULL, NULL );
+
+	Debug( LDAP_DEBUG_ANY, "Perl COMPARE\n", 0, 0, 0 );
+
+	return (0);
+}
+
diff --git a/servers/slapd/back-perl/config.c b/servers/slapd/back-perl/config.c
new file mode 100644
index 0000000000000000000000000000000000000000..da4d839d3e3bf1cad7e805b0a7561db3d26757ce
--- /dev/null
+++ b/servers/slapd/back-perl/config.c
@@ -0,0 +1,161 @@
+/* $OpenLDAP$ */
+/*
+ *	 Copyright 1999, John C. Quillan, All rights reserved.
+ *	 Portions Copyright 2002, myinternet Limited. All rights reserved.
+ *
+ *	 Redistribution and use in source and binary forms are permitted only
+ *	 as authorized by the OpenLDAP Public License.	A copy of this
+ *	 license is available at http://www.OpenLDAP.org/license.html or
+ *	 in file LICENSE in the top-level directory of the distribution.
+ */
+
+#include "portable.h"
+	
+#include <stdio.h>
+
+#include "slap.h"
+#ifdef HAVE_WIN32_ASPERL
+#include "asperl_undefs.h"
+#endif
+
+#include <EXTERN.h>
+#include <perl.h>
+
+#include "perl_back.h"
+
+
+/**********************************************************
+ *
+ * Config
+ *
+ **********************************************************/
+int
+perl_back_db_config(
+	 BackendDB *be,
+	 const char *fname,
+	 int lineno,
+	 int argc,
+	 char **argv
+)
+{
+	SV* loc_sv;
+	PerlBackend *perl_back = (PerlBackend *) be->be_private;
+	char eval_str[EVAL_BUF_SIZE];
+	int count ;
+	int args;
+	int return_code;
+	
+
+	if ( strcasecmp( argv[0], "perlModule" ) == 0 ) {
+		if ( argc < 2 ) {
+			Debug( LDAP_DEBUG_ANY,
+				 "%s.pm: line %d: missing module in \"perlModule <module>\" line\n",
+				fname, lineno, 0 );
+			return( 1 );
+		}
+
+#ifdef PERL_IS_5_6
+		snprintf( eval_str, EVAL_BUF_SIZE, "use %s;", argv[1] );
+		eval_pv( eval_str, 0 );
+
+		if (SvTRUE(ERRSV)) {
+			STRLEN n_a;
+
+			fprintf(stderr , "Error %s\n", SvPV(ERRSV, n_a)) ;
+		}
+#else
+		snprintf( eval_str, EVAL_BUF_SIZE, "%s.pm", argv[1] );
+		perl_require_pv( eval_str );
+
+		if (SvTRUE(GvSV(errgv))) {
+			fprintf(stderr , "Error %s\n", SvPV(GvSV(errgv), na)) ;
+		}
+#endif /* PERL_IS_5_6 */
+		else {
+			dSP; ENTER; SAVETMPS;
+			PUSHMARK(sp);
+			XPUSHs(sv_2mortal(newSVpv(argv[1], 0)));
+			PUTBACK;
+
+#ifdef PERL_IS_5_6
+			count = call_method("new", G_SCALAR);
+#else
+			count = perl_call_method("new", G_SCALAR);
+#endif
+
+			SPAGAIN;
+
+			if (count != 1) {
+				croak("Big trouble in config\n") ;
+			}
+
+			perl_back->pb_obj_ref = newSVsv(POPs);
+
+			PUTBACK; FREETMPS; LEAVE ;
+		}
+
+	} else if ( strcasecmp( argv[0], "perlModulePath" ) == 0 ) {
+		if ( argc < 2 ) {
+			fprintf( stderr,
+				"%s: line %d: missing module in \"PerlModulePath <module>\" line\n",
+				fname, lineno );
+			return( 1 );
+		}
+
+		snprintf( eval_str, EVAL_BUF_SIZE, "push @INC, '%s';", argv[1] );
+#ifdef PERL_IS_5_6
+		loc_sv = eval_pv( eval_str, 0 );
+#else
+		loc_sv = perl_eval_pv( eval_str, 0 );
+#endif
+
+		/* XXX loc_sv return value is ignored. */
+
+	} else if ( strcasecmp( argv[0], "filterSearchResults" ) == 0 ) {
+		perl_back->pb_filter_search_results = 1;
+	} else {
+		/*
+		 * Pass it to Perl module if defined
+		 */
+
+		{
+			dSP ;  ENTER ; SAVETMPS;
+
+			PUSHMARK(sp) ;
+			XPUSHs( perl_back->pb_obj_ref );
+
+			/* Put all arguments on the perl stack */
+			for( args = 0; args < argc; args++ ) {
+				XPUSHs(sv_2mortal(newSVpv(argv[args], 0)));
+			}
+
+			PUTBACK ;
+
+#ifdef PERL_IS_5_6
+			count = call_method("config", G_SCALAR);
+#else
+			count = perl_call_method("config", G_SCALAR);
+#endif
+
+			SPAGAIN ;
+
+			if (count != 1) {
+				croak("Big trouble in config\n") ;
+			}
+
+			return_code = POPi;
+
+			PUTBACK ; FREETMPS ;  LEAVE ;
+
+		}
+
+		/* if the module rejected it then we should reject it */
+		if ( return_code != 0 ) {
+			fprintf( stderr,
+				 "Unknown perl backend config: %s\n", argv[0]);
+			exit( EXIT_FAILURE );
+		}
+	}
+
+	return 0;
+}
diff --git a/servers/slapd/back-perl/delete.c b/servers/slapd/back-perl/delete.c
new file mode 100644
index 0000000000000000000000000000000000000000..e07d3abf18368a932dec48537ef713f49a341f52
--- /dev/null
+++ b/servers/slapd/back-perl/delete.c
@@ -0,0 +1,76 @@
+/* $OpenLDAP$ */
+/*
+ *	 Copyright 1999, John C. Quillan, All rights reserved.
+ *	 Portions Copyright 2002, myinternet Limited. All rights reserved.
+ *
+ *	 Redistribution and use in source and binary forms are permitted only
+ *	 as authorized by the OpenLDAP Public License.	A copy of this
+ *	 license is available at http://www.OpenLDAP.org/license.html or
+ *	 in file LICENSE in the top-level directory of the distribution.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include "slap.h"
+#ifdef HAVE_WIN32_ASPERL
+#include "asperl_undefs.h"
+#endif
+
+#include <EXTERN.h>
+#include <perl.h>
+
+#include "perl_back.h"
+
+int
+perl_back_delete(
+	Backend	*be,
+	Connection	*conn,
+	Operation	*op,
+	struct berval	*dn,
+	struct berval	*ndn
+)
+{
+	int len;
+	int count;
+	int return_code;
+
+	PerlBackend *perl_back = (PerlBackend *) be->be_private;
+
+	ldap_pvt_thread_mutex_lock( &perl_interpreter_mutex );	
+
+	{
+		dSP; ENTER; SAVETMPS;
+
+		PUSHMARK(sp);
+		XPUSHs( perl_back->pb_obj_ref );
+		XPUSHs(sv_2mortal(newSVpv( dn->bv_val , 0 )));
+
+		PUTBACK;
+
+#ifdef PERL_IS_5_6
+		count = call_method("delete", G_SCALAR);
+#else
+		count = perl_call_method("delete", G_SCALAR);
+#endif
+
+		SPAGAIN;
+
+		if (count != 1) {
+			croak("Big trouble in perl-back_delete\n");
+		}
+							 
+		return_code = POPi;
+
+		PUTBACK; FREETMPS; LEAVE;
+	}
+
+	ldap_pvt_thread_mutex_unlock( &perl_interpreter_mutex );	
+
+	send_ldap_result( conn, op, return_code,
+			NULL, NULL, NULL, NULL );
+
+	Debug( LDAP_DEBUG_ANY, "Perl DELETE\n", 0, 0, 0 );
+	return( 0 );
+}
diff --git a/servers/slapd/back-perl/external.h b/servers/slapd/back-perl/external.h
new file mode 100644
index 0000000000000000000000000000000000000000..f95fe171e43a0735994a5768a904b0c10ae43d10
--- /dev/null
+++ b/servers/slapd/back-perl/external.h
@@ -0,0 +1,34 @@
+/* $OpenLDAP$ */
+#ifndef _PERL_EXTERNAL_H
+#define _PERL_EXTERNAL_H
+
+LDAP_BEGIN_DECL
+
+extern BI_init	perl_back_initialize;
+extern BI_open	perl_back_open;
+extern BI_close	perl_back_close;
+extern BI_destroy	perl_back_destroy;
+
+extern BI_db_init	perl_back_db_init;
+extern BI_db_open	perl_back_db_open;
+extern BI_db_destroy	perl_back_db_destroy;
+
+extern BI_db_config	perl_back_db_config;
+
+extern BI_op_bind	perl_back_bind;
+
+extern BI_op_search	perl_back_search;
+
+extern BI_op_compare	perl_back_compare;
+
+extern BI_op_modify	perl_back_modify;
+
+extern BI_op_modrdn	perl_back_modrdn;
+
+extern BI_op_add	perl_back_add;
+
+extern BI_op_delete	perl_back_delete;
+
+LDAP_END_DECL
+
+#endif /* _PERL_EXTERNAL_H */
diff --git a/servers/slapd/back-perl/init.c b/servers/slapd/back-perl/init.c
new file mode 100644
index 0000000000000000000000000000000000000000..ec105f95430f8318d87c1eebd82fa7edd9433a69
--- /dev/null
+++ b/servers/slapd/back-perl/init.c
@@ -0,0 +1,184 @@
+/* $OpenLDAP$ */
+/*
+ *	 Copyright 1999, John C. Quillan, All rights reserved.
+ *	 Portions Copyright 2002, myinternet Limited. All rights reserved.
+ *
+ *	 Redistribution and use in source and binary forms are permitted only
+ *	 as authorized by the OpenLDAP Public License.	A copy of this
+ *	 license is available at http://www.OpenLDAP.org/license.html or
+ *	 in file LICENSE in the top-level directory of the distribution.
+ */
+
+#include "portable.h"
+ /* init.c - initialize shell backend */
+	
+#include <stdio.h>
+
+#include "slap.h"
+#ifdef HAVE_WIN32_ASPERL
+#include "asperl_undefs.h"
+#endif
+
+#include <EXTERN.h>
+#include <XSUB.h>
+#include <perl.h>
+
+#include "perl_back.h"
+
+
+static void perl_back_xs_init LDAP_P((PERL_BACK_XS_INIT_PARAMS));
+EXT void boot_DynaLoader LDAP_P((PERL_BACK_BOOT_DYNALOADER_PARAMS));
+
+PerlInterpreter *PERL_INTERPRETER = NULL;
+ldap_pvt_thread_mutex_t	perl_interpreter_mutex;
+
+#ifdef SLAPD_PERL_DYNAMIC
+
+int back_perl_LTX_init_module(int argc, char *argv[])
+{
+	BackendInfo bi;
+
+	memset( &bi, '\0', sizeof(bi) );
+	bi.bi_type = "perl";
+	bi.bi_init = perl_back_initialize;
+
+	backend_add(&bi);
+	return 0;
+}
+
+#endif /* SLAPD_PERL_DYNAMIC */
+
+
+/**********************************************************
+ *
+ * Init
+ *
+ **********************************************************/
+
+int
+perl_back_initialize(
+	BackendInfo	*bi
+)
+{
+	char *embedding[] = { "", "-e", "0" };
+
+	Debug( LDAP_DEBUG_TRACE, "perl backend open\n", 0, 0, 0 );
+
+	if( PERL_INTERPRETER != NULL ) {
+		Debug( LDAP_DEBUG_ANY, "perl backend open: already opened\n",
+			0, 0, 0 );
+		return 1;
+	}
+	
+	PERL_INTERPRETER = perl_alloc();
+	perl_construct(PERL_INTERPRETER);
+	perl_parse(PERL_INTERPRETER, perl_back_xs_init, 3, embedding, (char **)NULL);
+	perl_run(PERL_INTERPRETER);
+
+	bi->bi_open = perl_back_open;
+	bi->bi_config = 0;
+	bi->bi_close = perl_back_close;
+	bi->bi_destroy = perl_back_destroy;
+
+	bi->bi_db_init = perl_back_db_init;
+	bi->bi_db_config = perl_back_db_config;
+	bi->bi_db_open = perl_back_db_open;
+	bi->bi_db_close = 0;
+	bi->bi_db_destroy = perl_back_db_destroy;
+
+	bi->bi_op_bind = perl_back_bind;
+	bi->bi_op_unbind = 0;
+	bi->bi_op_search = perl_back_search;
+	bi->bi_op_compare = perl_back_compare;
+	bi->bi_op_modify = perl_back_modify;
+	bi->bi_op_modrdn = perl_back_modrdn;
+	bi->bi_op_add = perl_back_add;
+	bi->bi_op_delete = perl_back_delete;
+	bi->bi_op_abandon = 0;
+
+	bi->bi_extended = 0;
+
+	bi->bi_acl_group = 0;
+	bi->bi_acl_attribute = 0;
+	bi->bi_chk_referrals = 0;
+
+	bi->bi_connection_init = 0;
+	bi->bi_connection_destroy = 0;
+
+	return 0;
+}
+		
+int
+perl_back_open(
+	BackendInfo	*bi
+)
+{
+	ldap_pvt_thread_mutex_init( &perl_interpreter_mutex );
+	return 0;
+}
+
+int
+perl_back_db_init(
+	BackendDB	*be
+)
+{
+	be->be_private = (PerlBackend *) ch_malloc( sizeof(PerlBackend) );
+	memset( be->be_private, '\0', sizeof(PerlBackend));
+
+	((PerlBackend *)be->be_private)->pb_filter_search_results = 0;
+
+	Debug( LDAP_DEBUG_TRACE, "perl backend db init\n", 0, 0, 0 );
+
+	return 0;
+}
+
+int
+perl_back_db_open(
+	BackendDB	*be
+)
+{
+	int count;
+	int return_code;
+
+	PerlBackend *perl_back = (PerlBackend *) be->be_private;
+
+	ldap_pvt_thread_mutex_lock( &perl_interpreter_mutex );
+
+	{
+		dSP; ENTER; SAVETMPS;
+
+		PUSHMARK(sp);
+		XPUSHs( perl_back->pb_obj_ref );
+
+		PUTBACK;
+
+#ifdef PERL_IS_5_6
+		count = call_method("init", G_SCALAR);
+#else
+		count = perl_call_method("init", G_SCALAR);
+#endif
+
+		SPAGAIN;
+
+		if (count != 1) {
+			croak("Big trouble in perl_back_db_open\n");
+		}
+
+		return_code = POPi;
+
+		PUTBACK; FREETMPS; LEAVE;
+	}
+
+	ldap_pvt_thread_mutex_unlock( &perl_interpreter_mutex );
+
+	return return_code;
+}
+
+
+static void
+perl_back_xs_init()
+{
+	char *file = __FILE__;
+	dXSUB_SYS;
+	newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, file);
+}
diff --git a/servers/slapd/back-perl/modify.c b/servers/slapd/back-perl/modify.c
new file mode 100644
index 0000000000000000000000000000000000000000..2d0fb9817cbe432ecc264b5859abbe138c2ab108
--- /dev/null
+++ b/servers/slapd/back-perl/modify.c
@@ -0,0 +1,109 @@
+/* $OpenLDAP$ */
+/*
+ *	 Copyright 1999, John C. Quillan, All rights reserved.
+ *	 Portions Copyright 2002, myinternet Limited. All rights reserved.
+ *
+ *	 Redistribution and use in source and binary forms are permitted only
+ *	 as authorized by the OpenLDAP Public License.	A copy of this
+ *	 license is available at http://www.OpenLDAP.org/license.html or
+ *	 in file LICENSE in the top-level directory of the distribution.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include "slap.h"
+#ifdef HAVE_WIN32_ASPERL
+#include "asperl_undefs.h"
+#endif
+
+#include <EXTERN.h>
+#include <perl.h>
+
+#include "perl_back.h"
+
+int
+perl_back_modify(
+	Backend	*be,
+	Connection	*conn,
+	Operation	*op,
+	struct berval 	*dn,
+	struct berval 	*ndn,
+	Modifications	*modlist
+)
+{
+	char test[500];
+	int return_code;
+	int count;
+	int i;
+	int err = 0;
+	char *matched = NULL, *info = NULL;
+
+	PerlBackend *perl_back = (PerlBackend *)be->be_private;
+
+	ldap_pvt_thread_mutex_lock( &perl_interpreter_mutex );	
+
+	{
+		dSP; ENTER; SAVETMPS;
+		
+		PUSHMARK(sp);
+		XPUSHs( perl_back->pb_obj_ref );
+		XPUSHs(sv_2mortal(newSVpv( dn->bv_val , 0)));
+
+		for (; modlist != NULL; modlist = modlist->sml_next ) {
+			Modification *mods = &modlist->sml_mod;
+
+			switch ( mods->sm_op & ~LDAP_MOD_BVALUES ) {
+			case LDAP_MOD_ADD:
+				XPUSHs(sv_2mortal(newSVpv("ADD", 0 )));
+				break;
+				
+			case LDAP_MOD_DELETE:
+				XPUSHs(sv_2mortal(newSVpv("DELETE", 0 )));
+				break;
+				
+			case LDAP_MOD_REPLACE:
+				XPUSHs(sv_2mortal(newSVpv("REPLACE", 0 )));
+				break;
+			}
+
+			
+			XPUSHs(sv_2mortal(newSVpv( mods->sm_type.bv_val, 0 )));
+
+			for ( i = 0;
+				mods->sm_bvalues != NULL && mods->sm_bvalues[i].bv_val != NULL;
+				i++ )
+			{
+				XPUSHs(sv_2mortal(newSVpv( mods->sm_bvalues[i].bv_val, 0 )));
+			}
+		}
+
+		PUTBACK;
+
+#ifdef PERL_IS_5_6
+		count = call_method("modify", G_SCALAR);
+#else
+		count = perl_call_method("modify", G_SCALAR);
+#endif
+
+		SPAGAIN;
+
+		if (count != 1) {
+			croak("Big trouble in back_modify\n");
+		}
+							 
+		return_code = POPi;
+
+		PUTBACK; FREETMPS; LEAVE;
+	}
+
+	ldap_pvt_thread_mutex_unlock( &perl_interpreter_mutex );
+
+	send_ldap_result( conn, op, return_code,
+		NULL, NULL, NULL, NULL );
+
+	Debug( LDAP_DEBUG_ANY, "Perl MODIFY\n", 0, 0, 0 );
+	return( 0 );
+}
+
diff --git a/servers/slapd/back-perl/modrdn.c b/servers/slapd/back-perl/modrdn.c
new file mode 100644
index 0000000000000000000000000000000000000000..32db575665b898aab01540d95a1e1b870dcd77b8
--- /dev/null
+++ b/servers/slapd/back-perl/modrdn.c
@@ -0,0 +1,98 @@
+/* $OpenLDAP$ */
+/*
+ *	 Copyright 1999, John C. Quillan, All rights reserved.
+ *	 Portions Copyright 2002, myinternet Limited. All rights reserved.
+ *
+ *	 Redistribution and use in source and binary forms are permitted only
+ *	 as authorized by the OpenLDAP Public License.	A copy of this
+ *	 license is available at http://www.OpenLDAP.org/license.html or
+ *	 in file LICENSE in the top-level directory of the distribution.
+ */
+
+/*
+ * LDAP v3 newSuperior support.
+ *
+ * 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.
+ *
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include "slap.h"
+#ifdef HAVE_WIN32_ASPERL
+#include "asperl_undefs.h"
+#endif
+
+#include <EXTERN.h>
+#include <perl.h>
+
+#include "perl_back.h"
+
+int
+perl_back_modrdn(
+	Backend	*be,
+	Connection	*conn,
+	Operation	*op,
+	struct berval	*dn,
+	struct berval	*ndn,
+	struct berval	*newrdn,
+	struct berval	*nnewrdn,
+	int		deleteoldrdn,
+	struct berval	*newSuperior,
+	struct berval	*nnewSuperior
+)
+{
+	int len;
+	int count;
+	int return_code;
+
+	PerlBackend *perl_back = (PerlBackend *) be->be_private;
+
+	ldap_pvt_thread_mutex_lock( &perl_interpreter_mutex );	
+
+	{
+		dSP; ENTER; SAVETMPS;
+		
+		PUSHMARK(sp) ;
+		XPUSHs( perl_back->pb_obj_ref );
+		XPUSHs(sv_2mortal(newSVpv( dn->bv_val , 0 )));
+		XPUSHs(sv_2mortal(newSVpv( newrdn->bv_val , 0 )));
+		XPUSHs(sv_2mortal(newSViv( deleteoldrdn )));
+		if ( newSuperior != NULL ) {
+			XPUSHs(sv_2mortal(newSVpv( newSuperior->bv_val , 0 )));
+		}
+		PUTBACK ;
+
+#ifdef PERL_IS_5_6
+		count = call_method("modrdn", G_SCALAR);
+#else
+		count = perl_call_method("modrdn", G_SCALAR);
+#endif
+
+		SPAGAIN ;
+
+		if (count != 1) {
+			croak("Big trouble in back_modrdn\n") ;
+		}
+							 
+		return_code = POPi;
+
+		PUTBACK; FREETMPS; LEAVE ;
+	}
+
+	ldap_pvt_thread_mutex_unlock( &perl_interpreter_mutex );
+	
+	send_ldap_result( conn, op, return_code,
+		NULL, NULL, NULL, NULL );
+
+	Debug( LDAP_DEBUG_ANY, "Perl MODRDN\n", 0, 0, 0 );
+	return( 0 );
+}
diff --git a/servers/slapd/back-perl/perl_back.h b/servers/slapd/back-perl/perl_back.h
new file mode 100644
index 0000000000000000000000000000000000000000..232364492a425d445bb36329287f595ddfa69660
--- /dev/null
+++ b/servers/slapd/back-perl/perl_back.h
@@ -0,0 +1,53 @@
+/* $OpenLDAP$ */
+#ifndef PERL_BACK_H
+#define PERL_BACK_H 1
+
+LDAP_BEGIN_DECL
+
+/*
+ * From Apache mod_perl: test for Perl version.[ja
+ */
+#ifdef pTHX_
+#define PERL_IS_5_6
+#endif
+
+#define EVAL_BUF_SIZE 500
+
+#ifdef pTHX_
+#define PERL_IS_5_6
+#endif
+
+extern ldap_pvt_thread_mutex_t  perl_interpreter_mutex;
+
+#ifdef HAVE_WIN32_ASPERL
+/* We should be using the PL_errgv, I think */
+/* All the old style variables are prefixed with PL_ now */
+# define errgv	PL_errgv
+# define na	PL_na
+#endif
+
+#ifdef HAVE_WIN32_ASPERL 
+/* pTHX is needed often now */
+# define PERL_INTERPRETER			my_perl
+# define PERL_BACK_XS_INIT_PARAMS		pTHX
+# define PERL_BACK_BOOT_DYNALOADER_PARAMS	pTHX, CV *cv
+#else
+# define PERL_INTERPRETER			perl_interpreter
+# define PERL_BACK_XS_INIT_PARAMS		void
+# define PERL_BACK_BOOT_DYNALOADER_PARAMS	CV *cv
+#endif
+
+extern PerlInterpreter *PERL_INTERPRETER;
+
+
+typedef struct perl_backend_instance {
+	char	*pb_module_name;
+	SV	*pb_obj_ref;
+	int	pb_filter_search_results;
+} PerlBackend;
+
+LDAP_END_DECL
+
+#include "external.h"
+
+#endif
diff --git a/servers/slapd/back-perl/search.c b/servers/slapd/back-perl/search.c
new file mode 100644
index 0000000000000000000000000000000000000000..dea77e5b089f849fefd425f5819db52438fe1d60
--- /dev/null
+++ b/servers/slapd/back-perl/search.c
@@ -0,0 +1,141 @@
+/* $OpenLDAP$ */
+/*
+ *	 Copyright 1999, John C. Quillan, All rights reserved.
+ *	 Portions Copyright 2002, myinternet Limited. All rights reserved.
+ *
+ *	 Redistribution and use in source and binary forms are permitted only
+ *	 as authorized by the OpenLDAP Public License.	A copy of this
+ *	 license is available at http://www.OpenLDAP.org/license.html or
+ *	 in file LICENSE in the top-level directory of the distribution.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include "slap.h"
+#ifdef HAVE_WIN32_ASPERL
+#include "asperl_undefs.h"
+#endif
+
+#include <EXTERN.h>
+#include <perl.h>
+
+#include "perl_back.h"
+
+/**********************************************************
+ *
+ * Search
+ *
+ **********************************************************/
+int
+perl_back_search(
+	Backend *be,
+	Connection *conn,
+	Operation *op,
+	struct berval *base,
+	struct berval *nbase,
+	int scope,
+	int deref,
+	int sizelimit,
+	int timelimit,
+	Filter *filter,
+	struct berval *filterstr,
+	AttributeName *attrs,
+	int attrsonly
+	)
+{
+	char test[500];
+	int count ;
+	int err = 0;
+	char *matched = NULL, *info = NULL;
+	PerlBackend *perl_back = (PerlBackend *)be->be_private;
+	AttributeName *an;
+	Entry	*e;
+	char *buf;
+	int i;
+	int return_code;
+
+	ldap_pvt_thread_mutex_lock( &perl_interpreter_mutex );	
+
+	{
+		dSP; ENTER; SAVETMPS;
+
+		PUSHMARK(sp) ;
+		XPUSHs( perl_back->pb_obj_ref );
+		XPUSHs(sv_2mortal(newSVpv( nbase->bv_val , 0)));
+		XPUSHs(sv_2mortal(newSViv( scope )));
+		XPUSHs(sv_2mortal(newSViv( deref )));
+		XPUSHs(sv_2mortal(newSViv( sizelimit )));
+		XPUSHs(sv_2mortal(newSViv( timelimit )));
+		XPUSHs(sv_2mortal(newSVpv( filterstr->bv_val , 0)));
+		XPUSHs(sv_2mortal(newSViv( attrsonly )));
+
+		for ( an = attrs; an && an->an_name.bv_val; an++ ) {
+			XPUSHs(sv_2mortal(newSVpv( an->an_name.bv_val , 0)));
+		}
+		PUTBACK;
+
+#ifdef PERL_IS_5_6
+		count = call_method("search", G_ARRAY );
+#else
+		count = perl_call_method("search", G_ARRAY );
+#endif
+
+		SPAGAIN;
+
+		if (count < 1) {
+			croak("Big trouble in back_search\n") ;
+		}
+
+		if ( count > 1 ) {
+							 
+			for ( i = 1; i < count; i++ ) {
+
+				buf = POPp;
+
+				if ( (e = str2entry( buf )) == NULL ) {
+					Debug( LDAP_DEBUG_ANY, "str2entry(%s) failed\n", buf, 0, 0 );
+
+				} else {
+					int send_entry;
+
+					if (perl_back->pb_filter_search_results)
+						send_entry = (test_filter( be, conn, op, e, filter ) == LDAP_COMPARE_TRUE);
+					else
+						send_entry = 1;
+
+					if (send_entry) {
+						send_search_entry( be, conn, op,
+							e, attrs, attrsonly, NULL );
+					}
+
+					entry_free( e );
+				}
+			}
+		}
+
+		/*
+		 * We grab the return code last because the stack comes
+		 * from perl in reverse order. 
+		 *
+		 * ex perl: return ( 0, $res_1, $res_2 );
+		 *
+		 * ex stack: <$res_2> <$res_1> <0>
+		 */
+
+		return_code = POPi;
+
+
+
+		PUTBACK; FREETMPS; LEAVE;
+	}
+
+	ldap_pvt_thread_mutex_unlock( &perl_interpreter_mutex );	
+
+	send_ldap_result( conn, op, return_code,
+		NULL, NULL, NULL, NULL );
+
+	return 0;
+}
+
diff --git a/servers/slapd/back-sql/util.c b/servers/slapd/back-sql/util.c
new file mode 100644
index 0000000000000000000000000000000000000000..00e1d33889e6ffce2c509c180d20e50216abfcba
--- /dev/null
+++ b/servers/slapd/back-sql/util.c
@@ -0,0 +1,170 @@
+/*
+ *	 Copyright 1999, Dmitry Kovalev <mit@openldap.org>, All rights reserved.
+ *
+ *	 Redistribution and use in source and binary forms are permitted only
+ *	 as authorized by the OpenLDAP Public License.	A copy of this
+ *	 license is available at http://www.OpenLDAP.org/license.html or
+ *	 in file LICENSE in the top-level directory of the distribution.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_SQL
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <string.h>
+#include <stdarg.h>
+#include "slap.h"
+#include "back-sql.h"
+#include "schema-map.h"
+#include "util.h"
+
+
+char backsql_def_oc_query[]="SELECT id,name,keytbl,keycol,create_proc,delete_proc,expect_return FROM ldap_oc_mappings";
+char backsql_def_at_query[]="SELECT name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return,sel_expr_u FROM ldap_attr_mappings WHERE oc_map_id=?";
+char backsql_def_delentry_query[]="DELETE FROM ldap_entries WHERE id=?";
+char backsql_def_insentry_query[]="INSERT INTO ldap_entries (dn,oc_map_id,parent,keyval) VALUES (?,?,?,?)";
+char backsql_def_subtree_cond[]="ldap_entries.dn LIKE CONCAT('%',?)";
+char backsql_id_query[]="SELECT id,keyval,oc_map_id FROM ldap_entries WHERE ";
+
+/* TimesTen*/
+char backsql_check_dn_ru_query[] = "SELECT dn_ru from ldap_entries";
+
+char* backsql_strcat(char* dest,int *buflen, ...)
+{
+ va_list strs;
+ int cdlen,cslen,grow;
+ char *cstr;
+ 
+ /*Debug(LDAP_DEBUG_TRACE,"==>my_strcat()\n");*/
+ va_start(strs,buflen);
+ if (dest==NULL || *buflen<=0)
+  {
+   dest=(char*)ch_calloc(BACKSQL_STR_GROW,sizeof(char));
+   *buflen=BACKSQL_STR_GROW;
+  }
+ cdlen=strlen(dest)+1;
+ while ((cstr=va_arg(strs,char*)) != NULL)
+  {
+   cslen=strlen(cstr);
+   grow=BACKSQL_MAX(BACKSQL_STR_GROW,cslen);
+   if (*buflen-cdlen < cslen)
+    {
+     /*Debug(LDAP_DEBUG_TRACE,"my_strcat(): buflen=%d, cdlen=%d, cslen=%d -- reallocating dest\n",
+                           *buflen,cdlen,cslen); */
+     dest=(char*)ch_realloc(dest,(*buflen)+grow*sizeof(char));
+     if (dest == NULL)
+      {
+       Debug(LDAP_DEBUG_ANY,"my_strcat(): could not reallocate string buffer.\n",0,0,0);
+      }
+     *buflen+=grow;
+     /*Debug(LDAP_DEBUG_TRACE,"my_strcat(): new buflen=%d, dest=%p\n",*buflen,dest,0);*/
+    }
+   strcat(dest,cstr);
+   cdlen+=cslen;
+  }
+ va_end(strs);
+ /*Debug(LDAP_DEBUG_TRACE,"<==my_strcat() (dest='%s')\n",dest,0,0);*/
+ return dest;
+} 
+
+int backsql_entry_addattr(Entry *e,char *at_name,char *at_val,unsigned int at_val_len)
+{
+ Attribute *c_at=e->e_attrs;
+ struct berval* add_val[2];
+ struct berval cval;
+ AttributeDescription *ad;
+ int rc;
+ const char *text;
+ 
+ Debug(LDAP_DEBUG_TRACE,"backsql_entry_addattr(): at_name='%s', at_val='%s'\n",at_name,at_val,0);
+ cval.bv_val=at_val;
+ cval.bv_len=at_val_len;
+ add_val[0]=&cval;
+ add_val[1]=NULL;
+ 
+ ad=NULL;
+ rc = slap_str2ad( at_name, &ad, &text );
+ if( rc != LDAP_SUCCESS ) 
+  {
+   Debug(LDAP_DEBUG_TRACE,"backsql_entry_addattr(): failed to find AttributeDescription for '%s'\n",at_name,0,0);
+   return 0;
+  }
+  
+ rc = attr_merge(e,ad,add_val);
+
+ if( rc != 0 )
+  {
+   Debug(LDAP_DEBUG_TRACE,"backsql_entry_addattr(): failed to merge value '%s' for attribute '%s'\n",at_val,at_name,0);
+   return 0;
+  }
+ 
+ Debug(LDAP_DEBUG_TRACE,"<==backsql_query_addattr()\n",0,0,0);
+ return 1;
+}
+
+char* backsql_get_table_spec(char **p)
+{
+ char *s,*q;
+ char *res=NULL;
+ int res_len=0;
+
+ s=*p;
+ while(**p && **p!=',') (*p)++;
+ if (**p)
+  *(*p)++='\0';
+
+#define BACKSQL_NEXT_WORD { \
+  while (*s && isspace((unsigned char)*s)) s++; \
+  if (!*s) return res; \
+  q=s; \
+  while (*q && !isspace((unsigned char)*q)) q++; \
+  if (*q) *q++='\0'; \
+ }
+
+ BACKSQL_NEXT_WORD;
+ res=backsql_strcat(res,&res_len,s,NULL);/*table name*/
+ s=q;
+
+ BACKSQL_NEXT_WORD;
+ if (!strcasecmp(s,"as"))
+ {
+  s=q;
+  BACKSQL_NEXT_WORD;
+ }
+ /*res=backsql_strcat(res,&res_len," AS ",s,NULL);
+  *oracle doesn't understand AS :(
+  */
+ res=backsql_strcat(res,&res_len," ",s,NULL);/*table alias*/
+ return res;
+}
+
+int backsql_merge_from_clause(char **dest_from,int *dest_len,char *src_from)
+{
+ char *s,*p,*srcc,*pos,e;
+
+ /*Debug(LDAP_DEBUG_TRACE,"==>backsql_merge_from_clause(): dest_from='%s',src_from='%s'\n",
+ 				dest_from,src_from,0); */
+ srcc=ch_strdup(src_from);
+ p=srcc;
+ while(*p)
+ {
+  s=backsql_get_table_spec(&p);
+ /* Debug(LDAP_DEBUG_TRACE,"backsql_merge_from_clause(): p='%s' s='%s'\n",p,s,0);  */
+  if (*dest_from==NULL)
+   *dest_from=backsql_strcat(*dest_from,dest_len,s,NULL);
+  else
+	if((pos=strstr(*dest_from,s))==NULL)
+     *dest_from=backsql_strcat(*dest_from,dest_len,",",s,NULL);
+    else if((e=pos[strlen(s)])!='\0' && e!=',')
+      *dest_from=backsql_strcat(*dest_from,dest_len,",",s,NULL);
+  if (s)
+	ch_free(s);
+ }
+/* Debug(LDAP_DEBUG_TRACE,"<==backsql_merge_from_clause()\n",0,0,0);*/
+ free(srcc);
+ return 1;
+}
+
+#endif /* SLAPD_SQL */
diff --git a/servers/slapd/config.c b/servers/slapd/config.c
index 06016c0a7692d8d1ef159192d59cd77bb6d1acdf..286531681413c5b69ba5a3756f1fe10b90dfa8ef 100644
--- a/servers/slapd/config.c
+++ b/servers/slapd/config.c
@@ -2222,7 +2222,7 @@ read_config( const char *fname )
 			if ( rc )
 				return rc;
 		} else if ( !strcasecmp( cargv[0], "TLSVerifyClient" ) ) {
-			if ( isdigit( cargv[1][0] ) ) {
+			if ( isdigit( (unsigned char) cargv[1][0] ) ) {
 				i = atoi(cargv[1]);
 				rc = ldap_pvt_tls_set_option( NULL,
 						      LDAP_OPT_X_TLS_REQUIRE_CERT,
diff --git a/servers/slapd/dn.c b/servers/slapd/dn.c
index 450691d326a5c076b48dfc116a07b0e8f5f409b5..40064a47fa3af42d25058f4d0d76ba65e3371d52 100644
--- a/servers/slapd/dn.c
+++ b/servers/slapd/dn.c
@@ -829,3 +829,40 @@ dnIsSuffix(
 	/* compare */
 	return( strcmp( dn->bv_val + d, suffix->bv_val ) == 0 );
 }
+
+/*
+ * Convert a DN from X.500 format into a normalized DN
+ */
+int
+dnDCEnormalize( char *dce, struct berval *out )
+{
+	int rc;
+	LDAPDN *dn = NULL;
+
+	out->bv_val = NULL;
+	out->bv_len = 0;
+
+	rc = ldap_str2dn( dce, &dn, LDAP_DN_FORMAT_DCE );
+	if  ( rc != LDAP_SUCCESS )
+		return rc;
+
+	/*
+	 * Schema-aware rewrite
+	 */
+	if ( LDAPDN_rewrite( dn, 0 ) != LDAP_SUCCESS ) {
+		ldap_dnfree( dn );
+		return LDAP_INVALID_SYNTAX;
+	}
+
+	/*
+	 * Back to string representation
+	 */
+	rc = ldap_dn2bv( dn, out, LDAP_DN_FORMAT_LDAPV3 );
+
+	ldap_dnfree( dn );
+
+	if ( rc != LDAP_SUCCESS ) {
+		rc = LDAP_INVALID_SYNTAX;
+	}
+	return rc;
+}
diff --git a/servers/slapd/main.c b/servers/slapd/main.c
index fa1bb4916d5b4800840461b1dd2c214628f4278b..62249eeaf2ab5a3f63e9e3e2b98d9d10fe1111c9 100644
--- a/servers/slapd/main.c
+++ b/servers/slapd/main.c
@@ -88,6 +88,7 @@ static int   cnvt_str2int( char *, STRDISP_P, int );
 
 #endif	/* LOG_LOCAL4 */
 
+static int check_config = 0;
 
 static void
 usage( char *name )
@@ -95,22 +96,23 @@ usage( char *name )
 	fprintf( stderr,
 		"usage: %s options\n", name );
 	fprintf( stderr,
-		"\t-d level\tDebug Level" "\n"
-		"\t-f filename\tConfiguration File\n"
+		"\t-d level\tDebug level" "\n"
+		"\t-f filename\tConfiguration file\n"
 #if defined(HAVE_SETUID) && defined(HAVE_SETGID)
 		"\t-g group\tGroup (id or name) to run as\n"
 #endif
-		"\t-h URLs\tList of URLs to serve\n"
+		"\t-h URLs\t\tList of URLs to serve\n"
 #ifdef LOG_LOCAL4
-		"\t-l sysloguser\tSyslog User (default: LOCAL4)\n"
+		"\t-l facility\tSyslog facility (default: LOCAL4)\n"
 #endif
-		"\t-n serverName\tservice name\n"
+		"\t-n serverName\tService name\n"
 #ifdef HAVE_CHROOT
-		"\t-r directory\n"
+		"\t-r directory\tSandbox directory to chroot to\n"
 #endif
-		"\t-s level\tSyslog Level\n"
+		"\t-s level\tSyslog level\n"
+		"\t-t\t\tCheck configuration file and exit\n"
 #if defined(HAVE_SETUID) && defined(HAVE_SETGID)
-		"\t-u user\tUser (id or name) to run as\n"
+		"\t-u user\t\tUser (id or name) to run as\n"
 #endif
     );
 }
@@ -213,7 +215,7 @@ int main( int argc, char **argv )
 #endif
 
 	while ( (i = getopt( argc, argv,
-			     "d:f:h:s:n:"
+			     "d:f:h:s:n:t"
 #ifdef HAVE_CHROOT
 				"r:"
 #endif
@@ -280,6 +282,10 @@ int main( int argc, char **argv )
 			serverName = ch_strdup( optarg );
 			break;
 
+		case 't':
+			check_config++;
+			break;
+
 		default:
 			usage( argv[0] );
 			rc = 1;
@@ -319,7 +325,7 @@ int main( int argc, char **argv )
 	openlog( serverName, OPENLOG_OPTIONS );
 #endif
 
-	if( slapd_daemon_init( urls ) != 0 ) {
+	if( !check_config && slapd_daemon_init( urls ) != 0 ) {
 		rc = 1;
 		SERVICE_EXIT( ERROR_SERVICE_SPECIFIC_ERROR, 16 );
 		goto stop;
@@ -378,6 +384,17 @@ int main( int argc, char **argv )
 	if ( read_config( configfile ) != 0 ) {
 		rc = 1;
 		SERVICE_EXIT( ERROR_SERVICE_SPECIFIC_ERROR, 19 );
+
+		if ( check_config ) {
+			fprintf( stderr, "config check failed\n" );
+		}
+
+		goto destroy;
+	}
+
+	if ( check_config ) {
+		rc = 0;
+		fprintf( stderr, "config check succeeded\n" );
 		goto destroy;
 	}
 
diff --git a/servers/slapd/proto-slap.h b/servers/slapd/proto-slap.h
index f3d6a8b0b346478505805e4e893fbcae63879699..0e0939eac9ee01c53b1621f1fae45e5139ca1fc0 100644
--- a/servers/slapd/proto-slap.h
+++ b/servers/slapd/proto-slap.h
@@ -404,6 +404,8 @@ LDAP_SLAPD_F (void) build_new_dn LDAP_P((
 
 LDAP_SLAPD_F (void) dnParent LDAP_P(( struct berval *dn, struct berval *pdn ));
 
+LDAP_SLAPD_F (int) dnDCEnormalize LDAP_P(( char *dce, struct berval *out ));
+
 /*
  * entry.c
  */
diff --git a/servers/slapd/referral.c b/servers/slapd/referral.c
index d3caa3ffd4a0c39c767160ae4fc48ea1f04d1a92..53edf251fba585d36abccdac5f419b604b499240 100644
--- a/servers/slapd/referral.c
+++ b/servers/slapd/referral.c
@@ -314,7 +314,7 @@ BerVarray get_entry_referrals(
 
 		/* trim the label */
 		for( k=0; k<jv->bv_len; k++ ) {
-			if( isspace(jv->bv_val[k]) ) {
+			if( isspace( (unsigned char) jv->bv_val[k] ) ) {
 				jv->bv_val[k] = '\0';
 				jv->bv_len = k;
 				break;
diff --git a/servers/slapd/result.c b/servers/slapd/result.c
index 8c96ff0239683f6f1b0d69de9297b65ec2e695af..4343f90794cec7729ff25b0d62fa4b9bfbc0f9c8 100644
--- a/servers/slapd/result.c
+++ b/servers/slapd/result.c
@@ -594,10 +594,15 @@ send_search_result(
 		err, matched, text, refs,
 		NULL, NULL, NULL, ctrls );
 
+	{
+	char nbuf[64];
+	sprintf( nbuf, "%ld nentries=%d", (long) err, nentries );
+
 	Statslog( LDAP_DEBUG_STATS,
-	    "conn=%ld op=%ld SEARCH RESULT tag=%lu err=%ld text=%s\n",
+	    "conn=%ld op=%ld SEARCH RESULT tag=%lu err=%s text=%s\n",
 		(long) op->o_connid, (long) op->o_opid,
-		(unsigned long) tag, (long) err, text ? text : "" );
+		(unsigned long) tag, nbuf, text ? text : "" );
+	}
 
 	if (tmp != NULL) {
 	    ch_free(tmp);
diff --git a/servers/slapd/sasl.c b/servers/slapd/sasl.c
index 4876c076f8dada7137043cdefa924744fa580e7d..87f29735aa7394ed3eb00b9b33cc563f2d3d0a3d 100644
--- a/servers/slapd/sasl.c
+++ b/servers/slapd/sasl.c
@@ -38,7 +38,6 @@
 #endif
 
 /* Flags for telling slap_sasl_getdn() what type of identity is being passed */
-#define FLAG_GETDN_FINAL   1
 #define FLAG_GETDN_AUTHCID 2
 #define FLAG_GETDN_AUTHZID 4
 
@@ -130,6 +129,10 @@ slap_sasl_log(
    string returned in *dn is in its own allocated memory, and must be free'd 
    by the calling process.
    -Mark Adamson, Carnegie Mellon
+
+   The "dn:" prefix is no longer used anywhere inside slapd. It is only used
+   on strings passed in directly from SASL.
+   -Howard Chu, Symas Corp.
 */
 
 #define	SET_DN	1
@@ -137,11 +140,11 @@ slap_sasl_log(
 
 static struct berval ext_bv = { sizeof("EXTERNAL")-1, "EXTERNAL" };
 
-int slap_sasl_getdn( Connection *conn, char *id,
+int slap_sasl_getdn( Connection *conn, char *id, int len,
 	char *user_realm, struct berval *dn, int flags )
 {
 	char *c1;
-	int rc, len, is_dn = 0;
+	int rc, is_dn = 0, do_norm = 1;
 	sasl_conn_t *ctx;
 	struct berval dn2;
 
@@ -166,7 +169,7 @@ int slap_sasl_getdn( Connection *conn, char *id,
 		return( LDAP_SUCCESS );
 	}
 	ctx = conn->c_sasl_context;
-	len = strlen( id );
+	if ( len == 0 ) len = strlen( id );
 
 	/* An authcID needs to be converted to authzID form */
 	if( flags & FLAG_GETDN_AUTHCID ) {
@@ -176,9 +179,9 @@ int slap_sasl_getdn( Connection *conn, char *id,
 			&& id[0] == '/' )
 		{
 			/* check SASL external for X.509 style DN and */
-			/* convert to dn:<dn> form */
-			dn->bv_val = ldap_dcedn2dn( id );
-			dn->bv_len = strlen(dn->bv_val);
+			/* convert to dn:<dn> form, result is normalized */
+			dnDCEnormalize( id, dn );
+			do_norm = 0;
 			is_dn = SET_DN;
 
 		} else {
@@ -246,12 +249,13 @@ int slap_sasl_getdn( Connection *conn, char *id,
 	}
 
 	/* DN strings that are a cn=auth identity to run through regexp */
-	if( is_dn == SET_DN && ( ( flags & FLAG_GETDN_FINAL ) == 0 ) )
+	if( is_dn == SET_DN )
 	{
 		slap_sasl2dn( dn, &dn2 );
 		if( dn2.bv_val ) {
 			ch_free( dn->bv_val );
 			*dn = dn2;
+			do_norm = 0;	/* slap_sasl2dn normalizes */
 #ifdef NEW_LOGGING
 			LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
 				"slap_sasl_getdn: dn:id converted to %s.\n", dn->bv_val ));
@@ -262,10 +266,7 @@ int slap_sasl_getdn( Connection *conn, char *id,
 		}
 	}
 
-	if( flags & FLAG_GETDN_FINAL ) {
-		/* omit "dn:" prefix */
-		is_dn = 0;
-	} else {
+	if ( do_norm ) {
 		rc = dnNormalize2( NULL, dn, &dn2 );
 		free(dn->bv_val);
 		if ( rc != LDAP_SUCCESS ) {
@@ -275,16 +276,6 @@ int slap_sasl_getdn( Connection *conn, char *id,
 		*dn = dn2;
 	}
 
-	/* Attach the "dn:" prefix if needed */
-	if ( is_dn == SET_DN ) {
-		c1 = ch_malloc( dn->bv_len + sizeof("dn:") );
-		strcpy( c1, "dn:" );
-		strcpy( c1 + 3, dn->bv_val );
-		free( dn->bv_val );
-		dn->bv_val = c1;
-		dn->bv_len += 3;
-	}
-
 	return( LDAP_SUCCESS );
 }
 
@@ -306,11 +297,12 @@ slap_sasl_checkpass(
 	cred.bv_val = (char *)pass;
 	cred.bv_len = passlen;
 
-	/* XXX do we need to check sasldb as well? */
+	/* SASL will fallback to its own mechanisms if we don't
+	 * find an answer here.
+	 */
 
-	/* XXX can we do both steps at once? */
-	rc = slap_sasl_getdn( conn, (char *)username, NULL, &dn,
-		FLAG_GETDN_AUTHCID | FLAG_GETDN_FINAL );
+	rc = slap_sasl_getdn( conn, (char *)username, 0, NULL, &dn,
+		FLAG_GETDN_AUTHCID );
 	if ( rc != LDAP_SUCCESS ) {
 		sasl_seterror( sconn, 0, ldap_err2string( rc ) );
 		return SASL_NOUSER;
@@ -388,7 +380,7 @@ slap_sasl_canonicalize(
 			in ? in : "<empty>" );
 #endif
 
-	rc = slap_sasl_getdn( conn, (char *)in, (char *)user_realm, &dn,
+	rc = slap_sasl_getdn( conn, (char *)in, inlen, (char *)user_realm, &dn,
 		(flags == SASL_CU_AUTHID) ? FLAG_GETDN_AUTHCID : FLAG_GETDN_AUTHZID );
 	if ( rc != LDAP_SUCCESS ) {
 		sasl_seterror( sconn, 0, ldap_err2string( rc ) );
@@ -441,7 +433,7 @@ slap_sasl_authorize(
 	Connection *conn = (Connection *)context;
 	struct berval authcDN, authzDN;
 	char *realm;
-	int rc, equal = 1;
+	int rc, equal = 1, ext = 0;
 
 #ifdef NEW_LOGGING
 	LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
@@ -456,11 +448,19 @@ slap_sasl_authorize(
 	if ( requested_user )
 		equal = !strcmp( auth_identity, requested_user );
 
-	realm = strchr( auth_identity, '@' );
-	if ( realm )
-		*realm++ = '\0';
+	/* If using SASL-EXTERNAL, don't modify the ID in any way */
+	if ( conn->c_sasl_bind_mech.bv_len == ext_bv.bv_len
+		&& ( strcasecmp( ext_bv.bv_val, conn->c_sasl_bind_mech.bv_val ) == 0 ) 
+			&& auth_identity[0] == '/' ) {
+		ext = 1;
+		realm = NULL;
+	} else {
+	/* Else look for an embedded realm in the name */
+		realm = strchr( auth_identity, '@' );
+		if ( realm ) *realm++ = '\0';
+	}
 
-	rc = slap_sasl_getdn( conn, auth_identity, realm ? realm : (char *)def_realm,
+	rc = slap_sasl_getdn( conn, auth_identity, alen, realm ? realm : (char *)def_realm,
 		&authcDN, FLAG_GETDN_AUTHCID );
 	if ( realm )
 		realm[-1] = '@';
@@ -480,11 +480,14 @@ slap_sasl_authorize(
 		return SASL_OK;
 	}
 
-	realm = strchr( requested_user, '@' );
-	if ( realm )
-		*realm++ = '\0';
+	if ( ext ) {
+		realm = NULL;
+	} else {
+		realm = strchr( requested_user, '@' );
+		if ( realm ) *realm++ = '\0';
+	}
 
-	rc = slap_sasl_getdn( conn, requested_user, realm ? realm : (char *)def_realm,
+	rc = slap_sasl_getdn( conn, requested_user, rlen, realm ? realm : (char *)def_realm,
 		&authzDN, FLAG_GETDN_AUTHZID );
 	if ( realm )
 		realm[-1] = '@';
@@ -537,15 +540,15 @@ slap_sasl_authorize(
 static int
 slap_sasl_authorize(
 	void *context,
-	const char *authcid,
-	const char *authzid,
+	char *authcid,
+	char *authzid,
 	const char **user,
 	const char **errstr)
 {
 	struct berval authcDN, authzDN;
-	int rc;
+	int rc, ext = 0;
 	Connection *conn = context;
-	char *realm;
+	char *realm, *xrealm;
 
 	*user = NULL;
 
@@ -579,7 +582,17 @@ slap_sasl_authorize(
 
 	/* Convert the identities to DN's. If no authzid was given, client will
 	   be bound as the DN matching their username */
-	rc = slap_sasl_getdn( conn, (char *)authcid, realm, &authcDN, FLAG_GETDN_AUTHCID );
+	if ( conn->c_sasl_bind_mech.bv_len == ext_bv.bv_len
+		&& ( strcasecmp( ext_bv.bv_val, conn->c_sasl_bind_mech.bv_val ) == 0 ) 
+			&& authcid[0] == '/' ) {
+		ext = 1;
+		xrealm = NULL;
+	} else {
+		xrealm = strchr( authcid, '@' );
+		if ( xrealm ) *xrealm++ = '\0';
+	}
+	rc = slap_sasl_getdn( conn, (char *)authcid, 0, xrealm ? xrealm : realm, &authcDN, FLAG_GETDN_AUTHCID );
+	if ( xrealm ) xrealm[-1] = '@';
 	if( rc != LDAP_SUCCESS ) {
 		*errstr = ldap_err2string( rc );
 		return SASL_NOAUTHZ;
@@ -598,7 +611,14 @@ slap_sasl_authorize(
 		*errstr = NULL;
 		return SASL_OK;
 	}
-	rc = slap_sasl_getdn( conn, (char *)authzid, realm, &authzDN, FLAG_GETDN_AUTHZID );
+	if ( ext ) {
+		xrealm = NULL;
+	} else {
+		xrealm = strchr( authzid, '@' );
+		if ( xrealm ) *xrealm++ = '\0';
+	}
+	rc = slap_sasl_getdn( conn, (char *)authzid, 0, xrealm ? xrealm : realm, &authzDN, FLAG_GETDN_AUTHZID );
+	if ( xrealm ) xrealm[-1] = '@';
 	if( rc != LDAP_SUCCESS ) {
 		ch_free( authcDN.bv_val );
 		*errstr = ldap_err2string( rc );
@@ -1088,30 +1108,23 @@ int slap_sasl_bind(
 				NULL, "no SASL username", NULL, NULL );
 
 		} else {
-			rc = slap_sasl_getdn( conn, username, realm, edn, FLAG_GETDN_FINAL );
-
-			if( rc == LDAP_SUCCESS ) {
-				sasl_ssf_t *ssf = NULL;
-				(void) sasl_getprop( ctx, SASL_SSF, (void *)&ssf );
-				*ssfp = ssf ? *ssf : 0;
+			sasl_ssf_t *ssf = NULL;
 
-				if( *ssfp ) {
-					ldap_pvt_thread_mutex_lock( &conn->c_mutex );
-					conn->c_sasl_layers++;
-					ldap_pvt_thread_mutex_unlock( &conn->c_mutex );
-				}
+			rc = LDAP_SUCCESS;
+			ber_str2bv( username, 0, 1, edn );
 
-				send_ldap_sasl( conn, op, rc,
-					NULL, NULL, NULL, NULL,
-					response.bv_len ? &response : NULL );
+			(void) sasl_getprop( ctx, SASL_SSF, (void *)&ssf );
+			*ssfp = ssf ? *ssf : 0;
 
-			} else {
-#if SASL_VERSION_MAJOR >= 2
-				errstr = sasl_errdetail( ctx );
-#endif
-				send_ldap_result( conn, op, rc,
-					NULL, errstr, NULL, NULL );
+			if( *ssfp ) {
+				ldap_pvt_thread_mutex_lock( &conn->c_mutex );
+				conn->c_sasl_layers++;
+				ldap_pvt_thread_mutex_unlock( &conn->c_mutex );
 			}
+
+			send_ldap_sasl( conn, op, rc,
+				NULL, NULL, NULL, NULL,
+				response.bv_len ? &response : NULL );
 		}
 
 	} else if ( sc == SASL_CONTINUE ) {
diff --git a/servers/slapd/saslauthz.c b/servers/slapd/saslauthz.c
index 2f9ee9b74a12e514870cf7f731254c7ce675bb96..c13726b0b1a4613f805d84eea7e6c1546c372408 100644
--- a/servers/slapd/saslauthz.c
+++ b/servers/slapd/saslauthz.c
@@ -164,7 +164,7 @@ int slap_sasl_regexp_config( const char *match, const char *replace )
 	reg->sr_offset[0] = -2;
 	n = 1;
 	for ( c = reg->sr_replace;	 *c;  c++ ) {
-		if ( *c == '\\' ) {
+		if ( *c == '\\' && c[1] ) {
 			c++;
 			continue;
 		}
@@ -546,7 +546,7 @@ CONCLUDED:
  * attribute named by *attr. If any of those rules map to the *assertDN, the
  * authorization is approved.
  *
- * DN's passed in should have a dn: prefix
+ * The DNs should not have the dn: prefix
  */
 static int
 slap_sasl_check_authz(struct berval *searchDN, struct berval *assertDN, struct berval *attr, struct berval *authc)
@@ -555,7 +555,6 @@ slap_sasl_check_authz(struct berval *searchDN, struct berval *assertDN, struct b
 	int i, rc;
 	BerVarray vals=NULL;
 	AttributeDescription *ad=NULL;
-	struct berval bv;
 
 #ifdef NEW_LOGGING
 	LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
@@ -571,17 +570,13 @@ slap_sasl_check_authz(struct berval *searchDN, struct berval *assertDN, struct b
 	if( rc != LDAP_SUCCESS )
 		goto COMPLETE;
 
-	bv.bv_val = searchDN->bv_val + 3;
-	bv.bv_len = searchDN->bv_len - 3;
-	rc = backend_attribute( NULL, NULL, NULL, NULL, &bv, ad, &vals );
+	rc = backend_attribute( NULL, NULL, NULL, NULL, searchDN, ad, &vals );
 	if( rc != LDAP_SUCCESS )
 		goto COMPLETE;
 
-	bv.bv_val = assertDN->bv_val + 3;
-	bv.bv_len = assertDN->bv_len - 3;
 	/* Check if the *assertDN matches any **vals */
 	for( i=0; vals[i].bv_val != NULL; i++ ) {
-		rc = slap_sasl_match( &vals[i], &bv, authc );
+		rc = slap_sasl_match( &vals[i], assertDN, authc );
 		if ( rc == LDAP_SUCCESS )
 			goto COMPLETE;
 	}
@@ -604,7 +599,8 @@ COMPLETE:
 
 
 /* Check if a bind can SASL authorize to another identity.
-   Accepts authorization DN's with "dn:" prefix */
+ * The DNs should not have the dn: prefix
+ */
 
 static struct berval sasl_authz_src = {
 	sizeof(SASL_AUTHZ_SOURCE_ATTR)-1, SASL_AUTHZ_SOURCE_ATTR };
diff --git a/servers/slapd/schema_init.c b/servers/slapd/schema_init.c
index 8695cd8fe63855189fc1f100c9bf0ab59143a48e..85e9f2526fc379d8fc47d5b49dcf0c8cad9152e7 100644
--- a/servers/slapd/schema_init.c
+++ b/servers/slapd/schema_init.c
@@ -98,11 +98,11 @@ static const struct MatchingRulePtr {
 };
 
 
-static char *bvcasechr( struct berval *bv, int c, ber_len_t *len )
+static char *bvcasechr( struct berval *bv, unsigned char c, ber_len_t *len )
 {
 	ber_len_t i;
-	int lower = TOLOWER( c );
-	int upper = TOUPPER( c );
+	char lower = TOLOWER( c );
+	char upper = TOUPPER( c );
 
 	if( c == 0 ) return NULL;
 	
@@ -3421,7 +3421,7 @@ char digit[] = "0123456789";
  */
 
 static struct berval *
-asn1_integer2str(ASN1_INTEGER *a)
+asn1_integer2str(ASN1_INTEGER *a, struct berval *bv)
 {
 	char buf[256];
 	char *p;
@@ -3477,35 +3477,17 @@ asn1_integer2str(ASN1_INTEGER *a)
 		*--p = '-';
 	}
 
-	return ber_bvstrdup(p);
+	return ber_str2bv( p, 0, 1, bv );
 }
 
 /* Get a DN in RFC2253 format from a X509_NAME internal struct */
-static struct berval *
-dn_openssl2ldap(X509_NAME *name)
+int
+dn_openssl2ldap(X509_NAME *name, struct berval *out)
 {
-	char issuer_dn[1024];
-	BIO *bio;
-
-	bio = BIO_new(BIO_s_mem());
-	if ( !bio ) {
-#ifdef NEW_LOGGING
-		LDAP_LOG(( "schema", LDAP_LEVEL_ENTRY,
-			   "dn_openssl2ldap: error creating BIO_s_mem: %s\n",
-			   ERR_error_string(ERR_get_error(),NULL)));
-#else
-		Debug( LDAP_DEBUG_ARGS, "dn_openssl2ldap: "
-		       "error creating BIO: %s\n",
-		       ERR_error_string(ERR_get_error(),NULL), NULL, NULL );
-#endif
-		return NULL;
-	}
-	X509_NAME_print_ex(bio, name, 0, XN_FLAG_RFC2253);
-
-	BIO_gets(bio, issuer_dn, 1024);
+	char buf[2048], *p;
 
-	BIO_free(bio);
-	return ber_bvstrdup(issuer_dn);
+	p = X509_NAME_oneline( name, buf, sizeof( buf ) );
+	return dnDCEnormalize( p, out );
 }
 
 /*
@@ -3519,9 +3501,8 @@ certificateExactConvert(
 {
 	X509 *xcert;
 	unsigned char *p = in->bv_val;
-	struct berval *serial;
-	struct berval *issuer_dn;
-	struct berval *bv_tmp;
+	struct berval serial;
+	struct berval issuer_dn;
 
 	xcert = d2i_X509(NULL, &p, in->bv_len);
 	if ( !xcert ) {
@@ -3537,39 +3518,27 @@ certificateExactConvert(
 		return LDAP_INVALID_SYNTAX;
 	}
 
-	serial = asn1_integer2str(xcert->cert_info->serialNumber);
-	if ( !serial ) {
+	if ( !asn1_integer2str(xcert->cert_info->serialNumber, &serial) ) {
 		X509_free(xcert);
 		return LDAP_INVALID_SYNTAX;
 	}
-	issuer_dn = dn_openssl2ldap(X509_get_issuer_name(xcert));
-	if ( !issuer_dn ) {
+	if ( dn_openssl2ldap(X509_get_issuer_name(xcert), &issuer_dn ) != LDAP_SUCCESS ) {
 		X509_free(xcert);
-		ber_bvfree(serial);
+		ber_memfree(serial.bv_val);
 		return LDAP_INVALID_SYNTAX;
 	}
-	/* Actually, dn_openssl2ldap returns in a normalized format, but
-	   it is different from our normalized format */
-	bv_tmp = issuer_dn;
-	if ( dnNormalize(NULL, bv_tmp, &issuer_dn) != LDAP_SUCCESS ) {
-		X509_free(xcert);
-		ber_bvfree(serial);
-		ber_bvfree(bv_tmp);
-		return LDAP_INVALID_SYNTAX;
-	}
-	ber_bvfree(bv_tmp);
 
 	X509_free(xcert);
 
-	out->bv_len = serial->bv_len + issuer_dn->bv_len + sizeof(" $ ");
+	out->bv_len = serial.bv_len + issuer_dn.bv_len + sizeof(" $ ");
 	out->bv_val = ch_malloc(out->bv_len);
 	p = out->bv_val;
-	AC_MEMCPY(p, serial->bv_val, serial->bv_len);
-	p += serial->bv_len;
+	AC_MEMCPY(p, serial.bv_val, serial.bv_len);
+	p += serial.bv_len;
 	AC_MEMCPY(p, " $ ", sizeof(" $ ")-1);
 	p += 3;
-	AC_MEMCPY(p, issuer_dn->bv_val, issuer_dn->bv_len);
-	p += issuer_dn->bv_len;
+	AC_MEMCPY(p, issuer_dn.bv_val, issuer_dn.bv_len);
+	p += issuer_dn.bv_len;
 	*p++ = '\0';
 
 #ifdef NEW_LOGGING
@@ -3582,8 +3551,8 @@ certificateExactConvert(
 		out->bv_val, NULL, NULL );
 #endif
 
-	ber_bvfree(serial);
-	ber_bvfree(issuer_dn);
+	ber_memfree(serial.bv_val);
+	ber_memfree(issuer_dn.bv_val);
 
 	return LDAP_SUCCESS;
 }
@@ -3591,8 +3560,8 @@ certificateExactConvert(
 static int
 serial_and_issuer_parse(
 	struct berval *assertion,
-	struct berval **serial,
-	struct berval **issuer_dn
+	struct berval *serial,
+	struct berval *issuer_dn
 )
 {
 	char *begin;
@@ -3617,18 +3586,20 @@ serial_and_issuer_parse(
 
 	bv.bv_len = end-begin+1;
 	bv.bv_val = begin;
-	*serial = ber_dupbv(NULL, &bv);
+	ber_dupbv(serial, &bv);
 
 	/* now extract the issuer, remember p was at the dollar sign */
-	begin = p+1;
-	end = assertion->bv_val+assertion->bv_len-1;
-	while (ASCII_SPACE(*begin))
-		begin++;
-	/* should we trim spaces at the end too? is it safe always? */
+	if ( issuer_dn ) {
+		begin = p+1;
+		end = assertion->bv_val+assertion->bv_len-1;
+		while (ASCII_SPACE(*begin))
+			begin++;
+		/* should we trim spaces at the end too? is it safe always? */
 
-	bv.bv_len = end-begin+1;
-	bv.bv_val = begin;
-	dnNormalize( NULL, &bv, issuer_dn );
+		bv.bv_len = end-begin+1;
+		bv.bv_val = begin;
+		dnNormalize2( NULL, &bv, issuer_dn );
+	}
 
 	return LDAP_SUCCESS;
 }
@@ -3644,10 +3615,10 @@ certificateExactMatch(
 {
 	X509 *xcert;
 	unsigned char *p = value->bv_val;
-	struct berval *serial;
-	struct berval *issuer_dn;
-	struct berval *asserted_serial;
-	struct berval *asserted_issuer_dn;
+	struct berval serial;
+	struct berval issuer_dn;
+	struct berval asserted_serial;
+	struct berval asserted_issuer_dn;
 	int ret;
 
 	xcert = d2i_X509(NULL, &p, value->bv_len);
@@ -3664,8 +3635,8 @@ certificateExactMatch(
 		return LDAP_INVALID_SYNTAX;
 	}
 
-	serial = asn1_integer2str(xcert->cert_info->serialNumber);
-	issuer_dn = dn_openssl2ldap(X509_get_issuer_name(xcert));
+	asn1_integer2str(xcert->cert_info->serialNumber, &serial);
+	dn_openssl2ldap(X509_get_issuer_name(xcert), &issuer_dn);
 
 	X509_free(xcert);
 
@@ -3678,8 +3649,8 @@ certificateExactMatch(
 		flags,
 		slap_schema.si_syn_integer,
 		slap_schema.si_mr_integerMatch,
-		serial,
-		asserted_serial);
+		&serial,
+		&asserted_serial);
 	if ( ret == LDAP_SUCCESS ) {
 		if ( *matchp == 0 ) {
 			/* We need to normalize everything for dnMatch */
@@ -3688,29 +3659,29 @@ certificateExactMatch(
 				flags,
 				slap_schema.si_syn_distinguishedName,
 				slap_schema.si_mr_distinguishedNameMatch,
-				issuer_dn,
-				asserted_issuer_dn);
+				&issuer_dn,
+				&asserted_issuer_dn);
 		}
 	}
 
 #ifdef NEW_LOGGING
 	LDAP_LOG(( "schema", LDAP_LEVEL_ENTRY,
 		   "certificateExactMatch: %d\n	 %s $ %s\n	 %s $	%s\n",
-		   *matchp, serial->bv_val, issuer_dn->bv_val,
-		   asserted_serial->bv_val, asserted_issuer_dn->bv_val));
+		   *matchp, serial.bv_val, issuer_dn.bv_val,
+		   asserted_serial.bv_val, asserted_issuer_dn.bv_val));
 #else
 	Debug( LDAP_DEBUG_ARGS, "certificateExactMatch "
 		"%d\n\t\"%s $ %s\"\n",
-		*matchp, serial->bv_val, issuer_dn->bv_val );
+		*matchp, serial.bv_val, issuer_dn.bv_val );
 	Debug( LDAP_DEBUG_ARGS, "\t\"%s $ %s\"\n",
-		asserted_serial->bv_val, asserted_issuer_dn->bv_val,
+		asserted_serial.bv_val, asserted_issuer_dn.bv_val,
 		NULL );
 #endif
 
-	ber_bvfree(serial);
-	ber_bvfree(issuer_dn);
-	ber_bvfree(asserted_serial);
-	ber_bvfree(asserted_issuer_dn);
+	ber_memfree(serial.bv_val);
+	ber_memfree(issuer_dn.bv_val);
+	ber_memfree(asserted_serial.bv_val);
+	ber_memfree(asserted_issuer_dn.bv_val);
 
 	return ret;
 }
@@ -3733,7 +3704,7 @@ static int certificateExactIndexer(
 	BerVarray keys;
 	X509 *xcert;
 	unsigned char *p;
-	struct berval * serial;
+	struct berval serial;
 
 	/* we should have at least one value at this point */
 	assert( values != NULL && values[0].bv_val != NULL );
@@ -3762,12 +3733,12 @@ static int certificateExactIndexer(
 			return LDAP_INVALID_SYNTAX;
 		}
 
-		serial = asn1_integer2str(xcert->cert_info->serialNumber);
+		asn1_integer2str(xcert->cert_info->serialNumber, &serial);
 		X509_free(xcert);
 		integerNormalize( slap_schema.si_syn_integer,
-				  serial,
+				  &serial,
 				  &keys[i] );
-		ber_bvfree(serial);
+		ber_memfree(serial.bv_val);
 #ifdef NEW_LOGGING
 		LDAP_LOG(( "schema", LDAP_LEVEL_ENTRY,
 			   "certificateExactIndexer: returning: %s\n",
@@ -3797,20 +3768,18 @@ static int certificateExactFilter(
 	BerVarray *keysp )
 {
 	BerVarray keys;
-	struct berval *asserted_serial;
-	struct berval *asserted_issuer_dn;
+	struct berval asserted_serial;
 
 	serial_and_issuer_parse(assertValue,
 				&asserted_serial,
-				&asserted_issuer_dn);
+				NULL);
 
 	keys = ch_malloc( sizeof( struct berval ) * 2 );
-	integerNormalize( syntax, asserted_serial, &keys[0] );
+	integerNormalize( syntax, &asserted_serial, &keys[0] );
 	keys[1].bv_val = NULL;
 	*keysp = keys;
 
-	ber_bvfree(asserted_serial);
-	ber_bvfree(asserted_issuer_dn);
+	ber_memfree(asserted_serial.bv_val);
 	return LDAP_SUCCESS;
 }
 #endif
diff --git a/servers/slurpd/config.c b/servers/slurpd/config.c
index 7a8261e8492b7cfe799ddf915243dc2d62352450..db08952915a0d66188748d17a490132927c1725f 100644
--- a/servers/slurpd/config.c
+++ b/servers/slurpd/config.c
@@ -1,3 +1,8 @@
+/* $OpenLDAP$ */
+/*
+ * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
 /*
  * Copyright (c) 1996 Regents of the University of Michigan.
  * All rights reserved.
@@ -15,33 +20,28 @@
  * config.c - configuration file handling routines
  */
 
+#include "portable.h"
+
 #include <stdio.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/socket.h>
 
-#include <lber.h>
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/socket.h>
+#include <ac/ctype.h>
+
 #include <ldap.h>
 
 #include "slurp.h"
 #include "globals.h"
 
-#define MAXARGS	100
+#define MAXARGS	500
 
 /* Forward declarations */
-#ifdef NEEDPROTOS
-static void	add_replica( char **, int );
-static int	parse_replica_line( char **, int, Ri *);
-static void	parse_line( char *, int *, char ** );
-static char	*getline( FILE * );
-static char	*strtok_quote( char *, char * );
-#else /* NEEDPROTOS */
-static void	add_replica();
-static int	parse_replica_line();
-static void	parse_line();
-static char	*getline();
-static char	*strtok_quote();
-#endif /* NEEDPROTOS */
+static void	add_replica LDAP_P(( char **, int ));
+static int	parse_replica_line LDAP_P(( char **, int, Ri *));
+static void	parse_line LDAP_P(( char *, int *, char ** ));
+static char	*getline LDAP_P(( FILE * ));
+static char	*strtok_quote LDAP_P(( char *, char * ));
 
 /* current config file line # */
 static int	lineno;
@@ -59,17 +59,21 @@ slurpd_read_config(
 )
 {
     FILE	*fp;
-    char	buf[BUFSIZ];
-    char	*line, *p;
+    char	*line;
     int		cargc;
     char	*cargv[MAXARGS];
 
+#ifdef NEW_LOGGING
+    LDAP_LOG (( "config", LDAP_LEVEL_ARGS, 
+	"slurpd_read_config: Config: opening config file \"%s\"\n", fname ));
+#else
     Debug( LDAP_DEBUG_CONFIG, "Config: opening config file \"%s\"\n",
 	    fname, 0, 0 );
+#endif
 
     if ( (fp = fopen( fname, "r" )) == NULL ) {
 	perror( fname );
-	exit( 1 );
+	exit( EXIT_FAILURE );
     }
 
     lineno = 0;
@@ -79,7 +83,12 @@ slurpd_read_config(
 	    continue;
 	}
 
+#ifdef NEW_LOGGING
+    LDAP_LOG (( "config", LDAP_LEVEL_DETAIL1, 
+		"slurpd_read_config: Config: (%s)\n", line ));
+#else
 	Debug( LDAP_DEBUG_CONFIG, "Config: (%s)\n", line, 0, 0 );
+#endif
 
 	parse_line( line, &cargc, cargv );
 
@@ -101,23 +110,57 @@ slurpd_read_config(
 			"line %d: missing filename in \"replogfile ",
 			lineno );
 		    fprintf( stderr, "<filename>\" line\n" );
-		    exit( 1 );
+		    exit( EXIT_FAILURE );
 		} else if ( cargc > 2 && *cargv[2] != '#' ) {
 		    fprintf( stderr,
 			"line %d: extra cruft at the end of \"replogfile %s\"",
 			lineno, cargv[1] );
 		    fprintf( stderr, "line (ignored)\n" );
 		}
-		sprintf( sglob->slapd_replogfile, cargv[1] );
+		strcpy( sglob->slapd_replogfile, cargv[1] );
 	    }
 	} else if ( strcasecmp( cargv[0], "replica" ) == 0 ) {
 	    add_replica( cargv, cargc );
+	    
+	    /* include another config file */
+	} else if ( strcasecmp( cargv[0], "include" ) == 0 ) {
+	    char *savefname;
+	    int savelineno;
+
+            if ( cargc < 2 ) {
+#ifdef NEW_LOGGING
+                LDAP_LOG(( "config", LDAP_LEVEL_CRIT,
+                        "%s: line %d: missing filename in \"include "
+                        "<filename>\" line.\n", fname, lineno ));
+#else
+                Debug( LDAP_DEBUG_ANY,
+        "%s: line %d: missing filename in \"include <filename>\" line\n",
+                        fname, lineno, 0 );
+#endif
+		
+                return( 1 );
+            }
+	    savefname = strdup( cargv[1] );
+	    savelineno = lineno;
+	    
+	    if ( slurpd_read_config( savefname ) != 0 ) {
+	        return( 1 );
+	    }
+		
+	    free( savefname );
+	    lineno = savelineno - 1;
 	}
     }
     fclose( fp );
+#ifdef NEW_LOGGING
+    LDAP_LOG (( "config", LDAP_LEVEL_RESULTS, 
+		"slurpd_read_config: Config: "
+		"** configuration file successfully read and parsed\n" ));
+#else
     Debug( LDAP_DEBUG_CONFIG,
 	    "Config: ** configuration file successfully read and parsed\n",
 	    0, 0, 0 );
+#endif
     return 0;
 }
 
@@ -178,11 +221,13 @@ strtok_quote(
 	    } else {
 		inquote = 1;
 	    }
-	    strcpy( next, next + 1 );
+	    AC_MEMCPY( next, next + 1, strlen( next + 1 ) + 1 );
 	    break;
 
 	case '\\':
-	    strcpy( next, next + 1 );
+	    if ( next[1] )
+		AC_MEMCPY( next, next + 1, strlen( next + 1 ) + 1 );
+	    next++;		/* dont parse the escaped character */
 	    break;
 
 	default:
@@ -230,13 +275,17 @@ getline(
     CATLINE( buf );
     while ( fgets( buf, sizeof(buf), fp ) != NULL ) {
 	if ( (p = strchr( buf, '\n' )) != NULL ) {
-	    *p = '\0';
+		if( p > buf && p[-1] == '\r' ) --p;       
+		*p = '\0';
 	}
 	lineno++;
-	if ( ! isspace( buf[0] ) ) {
+	if ( ! isspace( (unsigned char) buf[0] ) ) {
 	    return( line );
 	}
 
+	/* change leading whitespace to space */
+	buf[0] = ' ';
+
 	CATLINE( buf );
     }
     buf[0] = '\0';
@@ -261,13 +310,13 @@ add_replica(
 	    ( nr + 1 )  * sizeof( Re * ));
     if ( sglob->replicas == NULL ) {
 	fprintf( stderr, "out of memory, add_replica\n" );
-	exit( 1 );
+	exit( EXIT_FAILURE );
     }
     sglob->replicas[ nr ] = NULL; 
 
     if ( Ri_init( &(sglob->replicas[ nr - 1 ])) < 0 ) {
 	fprintf( stderr, "out of memory, Ri_init\n" );
-	exit( 1 );
+	exit( EXIT_FAILURE );
     }
     if ( parse_replica_line( cargv, cargc,
 	    sglob->replicas[ nr - 1] ) < 0 ) {
@@ -280,17 +329,25 @@ add_replica(
 	sglob->replicas[ nr - 1] = NULL;
 	sglob->num_replicas--;
     } else {
+#ifdef NEW_LOGGING
+    LDAP_LOG (( "config", LDAP_LEVEL_RESULTS, 
+		"add_replica: Config: ** successfully added replica \"%s%d\"\n", 
+		sglob->replicas[ nr - 1 ]->ri_hostname == NULL ?
+		"(null)" : sglob->replicas[ nr - 1 ]->ri_hostname,
+		sglob->replicas[ nr - 1 ]->ri_port, 0 ));
+#else
 	Debug( LDAP_DEBUG_CONFIG,
 		"Config: ** successfully added replica \"%s:%d\"\n",
 		sglob->replicas[ nr - 1 ]->ri_hostname == NULL ?
 		"(null)" : sglob->replicas[ nr - 1 ]->ri_hostname,
 		sglob->replicas[ nr - 1 ]->ri_port, 0 );
+#endif
 	sglob->replicas[ nr - 1]->ri_stel =
 		sglob->st->st_add( sglob->st,
 		sglob->replicas[ nr - 1 ] );
 	if ( sglob->replicas[ nr - 1]->ri_stel == NULL ) {
 	    fprintf( stderr, "Failed to add status element structure\n" );
-	    exit( 1 );
+	    exit( EXIT_FAILURE );
 	}
     }
 }
@@ -301,7 +358,7 @@ add_replica(
  * Parse a "replica" line from the config file.  replica lines should be
  * in the following format:
  * replica    host=<hostname:portnumber> binddn=<binddn>
- *            bindmethod="simple|kerberos" credentials=<creds>
+ *            bindmethod="simple" credentials=<creds>
  *
  * where:
  * <hostname:portnumber> describes the host name and port number where the
@@ -309,12 +366,10 @@ add_replica(
  *
  * <binddn> is the DN to bind to the replica slapd as,
  *
- * bindmethod is either "simple" or "kerberos", and
+ * bindmethod is "simple", and
  *
  * <creds> are the credentials (e.g. password) for binddn.  <creds> are
- * only used for bindmethod=simple.  For bindmethod=kerberos, the
- * credentials= option should be omitted.  Credentials for kerberos
- * authentication are in the system srvtab file.
+ * only used for bindmethod=simple.  
  *
  * The "replica" config file line may be split across multiple lines.  If
  * a line begins with whitespace, it is considered a continuation of the
@@ -324,6 +379,8 @@ add_replica(
 #define	GOT_DN		2
 #define	GOT_METHOD	4
 #define	GOT_ALL		( GOT_HOST | GOT_DN | GOT_METHOD )
+#define	GOT_MECH	8
+
 static int
 parse_replica_line( 
     char	**cargv,
@@ -336,56 +393,86 @@ parse_replica_line(
     char	*hp, *val;
 
     for ( i = 1; i < cargc; i++ ) {
-	if ( !strncasecmp( cargv[ i ], HOSTSTR, strlen( HOSTSTR ))) {
-	    val = cargv[ i ] + strlen( HOSTSTR ) + 1;
+	if ( !strncasecmp( cargv[ i ], HOSTSTR, sizeof( HOSTSTR ) - 1 ) ) {
+	    val = cargv[ i ] + sizeof( HOSTSTR ); /* '\0' string terminator accounts for '=' */
 	    if (( hp = strchr( val, ':' )) != NULL ) {
 		*hp = '\0';
 		hp++;
 		ri->ri_port = atoi( hp );
 	    }
 	    if ( ri->ri_port <= 0 ) {
-		ri->ri_port = LDAP_PORT;
+		ri->ri_port = 0;
 	    }
 	    ri->ri_hostname = strdup( val );
 	    gots |= GOT_HOST;
+	} else if ( !strncasecmp( cargv[ i ], 
+			ATTRSTR, sizeof( ATTRSTR ) - 1 ) ) {
+	    /* ignore it */ ;
+	} else if ( !strncasecmp( cargv[ i ], 
+			SUFFIXSTR, sizeof( SUFFIXSTR ) - 1 ) ) {
+	    /* ignore it */ ;
+	} else if ( !strncasecmp( cargv[ i ], TLSSTR, sizeof( TLSSTR ) - 1 ) ) {
+	    val = cargv[ i ] + sizeof( TLSSTR );
+		if( !strcasecmp( val, TLSCRITICALSTR ) ) {
+			ri->ri_tls = TLS_CRITICAL;
+		} else {
+			ri->ri_tls = TLS_ON;
+		}
 	} else if ( !strncasecmp( cargv[ i ],
-		BINDDNSTR, strlen( BINDDNSTR ))) { 
-	    val = cargv[ i ] + strlen( BINDDNSTR ) + 1;
+			BINDDNSTR, sizeof( BINDDNSTR ) - 1 ) ) { 
+	    val = cargv[ i ] + sizeof( BINDDNSTR );
 	    ri->ri_bind_dn = strdup( val );
 	    gots |= GOT_DN;
 	} else if ( !strncasecmp( cargv[ i ], BINDMETHSTR,
-		strlen( BINDMETHSTR ))) {
-	    val = cargv[ i ] + strlen( BINDMETHSTR ) + 1;
+		sizeof( BINDMETHSTR ) - 1 ) ) {
+	    val = cargv[ i ] + sizeof( BINDMETHSTR );
 	    if ( !strcasecmp( val, KERBEROSSTR )) {
-#ifdef KERBEROS
-		ri->ri_bind_method = AUTH_KERBEROS;
-		if ( ri->ri_srvtab == NULL ) {
-		    ri->ri_srvtab = strdup( sglob->default_srvtab );
-		}
-		gots |= GOT_METHOD;
-#else /* KERBEROS */
 	    fprintf( stderr, "Error: a bind method of \"kerberos\" was\n" );
-	    fprintf( stderr, "specified in the slapd configuration file,\n" );
-	    fprintf( stderr, "but slurpd was not built with kerberos.\n" );
-	    fprintf( stderr, "You must rebuild the LDAP release with\n" );
-	    fprintf( stderr, "kerberos support if you wish to use\n" );
-	    fprintf( stderr, "bindmethod=kerberos\n" );
-	    exit( 1 );
-#endif /* KERBEROS */
+	    fprintf( stderr, "specified in the slapd configuration file.\n" );
+	    fprintf( stderr, "slurpd no longer supports Kerberos.\n" );
+	    exit( EXIT_FAILURE );
 	    } else if ( !strcasecmp( val, SIMPLESTR )) {
 		ri->ri_bind_method = AUTH_SIMPLE;
 		gots |= GOT_METHOD;
+	    } else if ( !strcasecmp( val, SASLSTR )) {
+		ri->ri_bind_method = AUTH_SASL;
+		gots |= GOT_METHOD;
 	    } else {
 		ri->ri_bind_method = -1;
 	    }
-	} else if ( !strncasecmp( cargv[ i ], CREDSTR, strlen( CREDSTR ))) {
-	    val = cargv[ i ] + strlen( CREDSTR ) + 1;
+	} else if ( !strncasecmp( cargv[ i ], 
+			SASLMECHSTR, sizeof( SASLMECHSTR ) - 1 ) ) {
+	    val = cargv[ i ] + sizeof( SASLMECHSTR );
+	    gots |= GOT_MECH;
+	    ri->ri_saslmech = strdup( val );
+	} else if ( !strncasecmp( cargv[ i ], 
+			CREDSTR, sizeof( CREDSTR ) - 1 ) ) {
+	    val = cargv[ i ] + sizeof( CREDSTR );
 	    ri->ri_password = strdup( val );
-	} else if ( !strncasecmp( cargv[ i ], BINDPSTR, strlen( BINDPSTR ))) {
-	    val = cargv[ i ] + strlen( BINDPSTR ) + 1;
-	    ri->ri_principal = strdup( val );
-	} else if ( !strncasecmp( cargv[ i ], SRVTABSTR, strlen( SRVTABSTR ))) {
-	    val = cargv[ i ] + strlen( SRVTABSTR ) + 1;
+	} else if ( !strncasecmp( cargv[ i ], 
+			SECPROPSSTR, sizeof( SECPROPSSTR ) - 1 ) ) {
+	    val = cargv[ i ] + sizeof( SECPROPSSTR );
+	    ri->ri_secprops = strdup( val );
+	} else if ( !strncasecmp( cargv[ i ], 
+			REALMSTR, sizeof( REALMSTR ) - 1 ) ) {
+	    val = cargv[ i ] + sizeof( REALMSTR );
+	    ri->ri_realm = strdup( val );
+	} else if ( !strncasecmp( cargv[ i ], 
+			AUTHCSTR, sizeof( AUTHCSTR ) - 1 ) ) {
+	    val = cargv[ i ] + sizeof( AUTHCSTR );
+	    ri->ri_authcId = strdup( val );
+	} else if ( !strncasecmp( cargv[ i ], 
+			OLDAUTHCSTR, sizeof( OLDAUTHCSTR ) - 1 ) ) {
+	    /* Old authcID is provided for some backwards compatibility */
+	    val = cargv[ i ] + sizeof( OLDAUTHCSTR );
+	    ri->ri_authcId = strdup( val );
+	} else if ( !strncasecmp( cargv[ i ], 
+			AUTHZSTR, sizeof( AUTHZSTR ) - 1 ) ) {
+	    val = cargv[ i ] + sizeof( AUTHZSTR );
+	    ri->ri_authzId = strdup( val );
+	} else if ( !strncasecmp( cargv[ i ], 
+			SRVTABSTR, sizeof( SRVTABSTR ) - 1 ) ) {
+	    val = cargv[ i ] + sizeof( SRVTABSTR );
 	    if ( ri->ri_srvtab != NULL ) {
 		free( ri->ri_srvtab );
 	    }
@@ -396,11 +483,19 @@ parse_replica_line(
 		    cargv[ i ] );
 	}
     }
-    if ( gots != GOT_ALL ) {
-	    fprintf( stderr, "Error: Malformed \"replica\" line in slapd " );
-	    fprintf( stderr, "config file, line %d\n", lineno );
-	return -1;
-    }
+    
+	if ( ri->ri_bind_method == AUTH_SASL) {
+		if ((gots & GOT_MECH) == 0) {
+			fprintf( stderr, "Error: \"replica\" line needs SASLmech flag in " );
+			fprintf( stderr, "slapd config file, line %d\n", lineno );
+			return -1;
+		}
+	}
+	else if ( gots != GOT_ALL ) {
+		fprintf( stderr, "Error: Malformed \"replica\" line in slapd " );
+		fprintf( stderr, "config file, line %d\n", lineno );
+		return -1;
+	}
     return 0;
 }
 
diff --git a/servers/slurpd/fm.c b/servers/slurpd/fm.c
index 2d36838bb549dc4ed3086f44bcf955af03d370a6..36fab6540e346a8588ca56e9adb4f3c1fcfeffea 100644
--- a/servers/slurpd/fm.c
+++ b/servers/slurpd/fm.c
@@ -1,3 +1,8 @@
+/* $OpenLDAP$ */
+/*
+ * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
 /*
  * Copyright (c) 1996 Regents of the University of Michigan.
  * All rights reserved.
@@ -14,50 +19,24 @@
  * fm.c - file management routines.
  */
 
+#include "portable.h"
+
 #include <stdio.h>
-#include <string.h>
-#include <signal.h>
+
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/signal.h>
 
 #include "slurp.h"
 #include "globals.h"
 
-extern void do_admin();
-
-static void set_shutdown();
-void do_nothing();
-
-/*
- * Externs
- */
-#ifdef NEEDPROTOS
-extern int file_nonempty( char * );
-extern int acquire_lock(char *, FILE **, FILE ** );
-extern int relinquish_lock(char *, FILE *, FILE * );
-#else /* NEEDPROTOS */
-extern int file_nonempty();
-extern int acquire_lock();
-extern int relinquish_lock();
-#endif /* NEEDPROTOS */
 
 /*
  * Forward references
  */
-#ifdef NEEDPROTOS
-static char *get_record( FILE * );
-static void populate_queue( char *f );
-static void set_shutdown();
-void do_nothing();
-#else /* NEEDPROTOS */
-static char *get_record();
-static void populate_queue();
-static void set_shutdown();
-void do_nothing();
-#endif /* NEEDPROTOS */
-
-#ifndef SYSERRLIST_IN_STDIO
-extern char *sys_errlist[];
-#endif /* SYSERRLIST_IN_STDIO */
-
+static char *get_record LDAP_P(( FILE * ));
+static void populate_queue LDAP_P(( char *f ));
+static RETSIGTYPE set_shutdown LDAP_P((int));
 
 
 /*
@@ -68,7 +47,7 @@ extern char *sys_errlist[];
  *  - adds items to the internal queue of replication work to do
  *  - signals the replication threads to let them know new work has arrived.
  */
-void
+void *
 fm(
     void *arg
 )
@@ -77,15 +56,16 @@ fm(
 
     /* Set up our signal handlers:
      * SIG{TERM,INT,HUP} causes a shutdown
-     * SIGUSR1 - does nothing, used to wake up sleeping threads.
-     * SIGUSR2 - causes slurpd to read its administrative interface file.
-     *           (not yet implemented).
+     * LDAP_SIGUSR1 - does nothing, used to wake up sleeping threads.
+	 * LDAP_SIGUSR2 - causes a shutdown
      */
-    (void) SIGNAL( SIGUSR1, (void *) do_nothing );
-    (void) SIGNAL( SIGUSR2, (void *) do_admin );
-    (void) SIGNAL( SIGTERM, (void *) set_shutdown );
-    (void) SIGNAL( SIGINT, (void *) set_shutdown );
-    (void) SIGNAL( SIGHUP, (void *) set_shutdown );
+    (void) SIGNAL( LDAP_SIGUSR1, do_nothing );
+    (void) SIGNAL( LDAP_SIGUSR2, set_shutdown );
+    (void) SIGNAL( SIGTERM, set_shutdown );
+    (void) SIGNAL( SIGINT, set_shutdown );
+#ifdef SIGHUP
+    (void) SIGNAL( SIGHUP, set_shutdown );
+#endif
 
     if ( sglob->one_shot_mode ) {
 	if ( file_nonempty( sglob->slapd_replogfile )) {
@@ -96,7 +76,7 @@ fm(
 		sglob->rq->rq_getcount( sglob->rq, RQ_COUNT_ALL ));
 	printf( "%d replication records to process.\n",
 		sglob->rq->rq_getcount( sglob->rq, RQ_COUNT_NZRC ));
-	return;
+	return NULL;
     }
     /*
      * There may be some leftover replication records in our own
@@ -111,21 +91,31 @@ fm(
     while ( !sglob->slurpd_shutdown ) {
 	if ( file_nonempty( sglob->slapd_replogfile )) {
 	    /* New work found - copy to slurpd replog file */
+#ifdef NEW_LOGGING
+    	LDAP_LOG (( "fm", LDAP_LEVEL_ARGS, 
+			"fm: new work in %s\n", sglob->slapd_replogfile ));
+#else
 	    Debug( LDAP_DEBUG_ARGS, "new work in %s\n",
 		    sglob->slapd_replogfile, 0, 0 );
+#endif
 	    if (( rc = copy_replog( sglob->slapd_replogfile,
 		    sglob->slurpd_replogfile )) == 0 )  {
 		populate_queue( sglob->slurpd_replogfile );
 	    } else {
 		if ( rc < 0 ) {
+#ifdef NEW_LOGGING
+    		LDAP_LOG (( "fm", LDAP_LEVEL_CRIT, 
+			"fm: Fatal error while copying replication log\n" ));
+#else
 		    Debug( LDAP_DEBUG_ANY,
 			    "Fatal error while copying replication log\n",
 			    0, 0, 0 );
+#endif
 		    sglob->slurpd_shutdown = 1;
 		}
 	    }
 	} else {
-	    tsleep( sglob->no_work_interval );
+	    ldap_pvt_thread_sleep( sglob->no_work_interval );
 	}
 
 	/* Garbage-collect queue */
@@ -136,16 +126,27 @@ fm(
 	    FILE *fp, *lfp;
 	    if (( rc = acquire_lock( sglob->slurpd_replogfile, &fp,
 		    &lfp )) < 0 ) {
+#ifdef NEW_LOGGING
+   		LDAP_LOG (( "fm", LDAP_LEVEL_ERR, 
+			"fm: Error: cannot acquire lock on \"%s\" for trimming\n",
+			sglob->slurpd_replogfile ));
+#else
 		Debug( LDAP_DEBUG_ANY,
 			"Error: cannot acquire lock on \"%s\" for trimming\n",
 			sglob->slurpd_replogfile, 0, 0 );
+#endif
 	    } else {
 		sglob->rq->rq_write( sglob->rq, fp );
 		(void) relinquish_lock( sglob->slurpd_replogfile, fp, lfp );
 	    }
 	}
     }
+#ifdef NEW_LOGGING
+	LDAP_LOG (( "fm", LDAP_LEVEL_RESULTS, "fm: exiting\n" ));
+#else
     Debug( LDAP_DEBUG_ARGS, "fm: exiting\n", 0, 0, 0 );
+#endif
+    return NULL;
 }
 
 
@@ -154,22 +155,20 @@ fm(
 /*
  * Set a global flag which signals that we're shutting down.
  */
-static void
-set_shutdown()
+static RETSIGTYPE
+set_shutdown(int sig)
 {
     int	i;
 
     sglob->slurpd_shutdown = 1;				/* set flag */
-    pthread_kill( sglob->fm_tid, SIGUSR1 );		/* wake up file mgr */
+    ldap_pvt_thread_kill( sglob->fm_tid, LDAP_SIGUSR1 );	/* wake up file mgr */
     sglob->rq->rq_lock( sglob->rq );			/* lock queue */
-    pthread_cond_broadcast( &(sglob->rq->rq_more) );	/* wake repl threads */
+    ldap_pvt_thread_cond_broadcast( &(sglob->rq->rq_more) );	/* wake repl threads */
     for ( i = 0; i < sglob->num_replicas; i++ ) {
 	(sglob->replicas[ i ])->ri_wake( sglob->replicas[ i ]);
     }
     sglob->rq->rq_unlock( sglob->rq );			/* unlock queue */
-    (void) SIGNAL( SIGTERM, (void *) set_shutdown );	/* reinstall handlers */
-    (void) SIGNAL( SIGINT, (void *) set_shutdown );
-    (void) SIGNAL( SIGHUP, (void *) set_shutdown );
+    (void) SIGNAL_REINSTALL( sig, set_shutdown );	/* reinstall handlers */
 }
 
 
@@ -178,10 +177,10 @@ set_shutdown()
 /*
  * A do-nothing signal handler.
  */
-void
-do_nothing()
+RETSIGTYPE
+do_nothing(int sig)
 {
-    (void) SIGNAL( SIGUSR1, (void *) do_nothing );
+    (void) SIGNAL_REINSTALL( sig, do_nothing );
 }
 
 
@@ -197,13 +196,18 @@ populate_queue(
 )
 {
     FILE	*fp, *lfp;
-    Rq		*rq = sglob->rq;
     char	*p;
 
     if ( acquire_lock( f, &fp, &lfp ) < 0 ) {
+#ifdef NEW_LOGGING
+	LDAP_LOG (( "fm", LDAP_LEVEL_ERR, 
+		"populate_queue: error: can't lock file \"%s\": %s\n", 
+		f, sys_errlist[ errno ] ));
+#else
 	Debug( LDAP_DEBUG_ANY,
 		"error: can't lock file \"%s\": %s\n",
 		f, sys_errlist[ errno ], 0 );
+#endif
 	return;
     }
 
@@ -212,11 +216,16 @@ populate_queue(
      * the queue.
      */
     if ( fseek( fp, sglob->srpos, 0 ) < 0 ) {
+#ifdef NEW_LOGGING
+	LDAP_LOG (( "fm", LDAP_LEVEL_ERR, 
+		"populate_queue: error: can't seek to offset %ld in file \"%s\"\n", 
+		sglob->srpos, f ));
+#else
 	Debug( LDAP_DEBUG_ANY,
 		"error: can't seek to offset %ld in file \"%s\"\n",
 		sglob->srpos, f, 0 );
-	return;
-    }
+#endif
+    } else {
     while (( p = get_record( fp )) != NULL ) {
 	if ( sglob->rq->rq_add( sglob->rq, p ) < 0 ) {
 	    char *t;
@@ -224,14 +233,21 @@ populate_queue(
 	    if (( t = strchr( p, '\n' )) != NULL ) {
 		*t = '\0';
 	    }
+#ifdef NEW_LOGGING
+		LDAP_LOG (( "fm", LDAP_LEVEL_ERR, 
+			"populate_queue: error: malformed replog entry "
+			"(begins with \"%s\")\n", p ));
+#else
 	    Debug( LDAP_DEBUG_ANY,
 		    "error: malformed replog entry (begins with \"%s\")\n",
 		    p, 0, 0 );
+#endif
 	}
 	free( p );
-	pthread_yield();
+	ldap_pvt_thread_yield();
     }
     sglob->srpos = ftell( fp );
+    }
     (void) relinquish_lock( f, fp, lfp );
 }
     
@@ -256,13 +272,13 @@ get_record(
 
     while (( fgets( line, sizeof(line), fp ) != NULL ) &&
 	    (( len = strlen( line )) > 1 )) {
-	while ( lcur + len + 1 > lmax ) {
-	    lmax += BUFSIZ;
-	    buf = (char *) ch_realloc( buf, lmax );
-	}
-	strcpy( buf + lcur, line );
-	lcur += len;
+
+		while ( lcur + len + 1 > lmax ) {
+		    lmax += BUFSIZ;
+		    buf = (char *) ch_realloc( buf, lmax );
+		}
+		strcpy( buf + lcur, line );
+		lcur += len;
     }
     return( buf );
 }
-
diff --git a/servers/slurpd/ldap_op.c b/servers/slurpd/ldap_op.c
index 4d676fe4523cacbdba673d9998b28a5eb780f328..2d63daefaff57867eb8b3a75a60f4079e6856724 100644
--- a/servers/slurpd/ldap_op.c
+++ b/servers/slurpd/ldap_op.c
@@ -84,47 +84,77 @@ do_ldap(
 		case T_ADDCT:
 			lderr = op_ldap_add( ri, re, errmsg );
 			if ( lderr != LDAP_SUCCESS ) {
+#ifdef NEW_LOGGING
+				LDAP_LOG (( " ldap_op", LDAP_LEVEL_ERR, "do_ldap: "
+					"Error: ldap_add_s failed adding \"%s\": %s\n",
+					*errmsg ? *errmsg : ldap_err2string( lderr ), re->re_dn ));
+#else
 				Debug( LDAP_DEBUG_ANY,
 					"Error: ldap_add_s failed adding \"%s\": %s\n",
 					*errmsg ? *errmsg : ldap_err2string( lderr ),
 					re->re_dn, 0 );
+#endif
 			}
 			break;
 
 		case T_MODIFYCT:
 			lderr = op_ldap_modify( ri, re, errmsg );
 			if ( lderr != LDAP_SUCCESS ) {
+#ifdef NEW_LOGGING
+				LDAP_LOG (( " ldap_op", LDAP_LEVEL_ERR, "do_ldap: "
+					"Error: ldap_modify_s failed modifying \"%s\": %s\n",
+					*errmsg ? *errmsg : ldap_err2string( lderr ), re->re_dn ));
+#else
 				Debug( LDAP_DEBUG_ANY,
 					"Error: ldap_modify_s failed modifying \"%s\": %s\n",
 					*errmsg ? *errmsg : ldap_err2string( lderr ),
 					re->re_dn, 0 );
+#endif
 			}
 			break;
 
 		case T_DELETECT:
 			lderr = op_ldap_delete( ri, re, errmsg );
 			if ( lderr != LDAP_SUCCESS ) {
+#ifdef NEW_LOGGING
+				LDAP_LOG (( " ldap_op", LDAP_LEVEL_ERR, "do_ldap: "
+					"Error: ldap_delete_s failed deleting \"%s\": %s\n",
+					*errmsg ? *errmsg : ldap_err2string( lderr ), re->re_dn ));
+#else
 				Debug( LDAP_DEBUG_ANY,
 					"Error: ldap_delete_s failed deleting \"%s\": %s\n",
 					*errmsg ? *errmsg : ldap_err2string( lderr ),
 					re->re_dn, 0 );
+#endif
 			}
 			break;
 
 		case T_MODRDNCT:
 			lderr = op_ldap_modrdn( ri, re, errmsg );
 			if ( lderr != LDAP_SUCCESS ) {
+#ifdef NEW_LOGGING
+				LDAP_LOG (( " ldap_op", LDAP_LEVEL_ERR, "do_ldap: "
+					"Error: ldap_modrdn_s failed modifying %s: %s\n",
+					*errmsg ? *errmsg : ldap_err2string( lderr ), re->re_dn ));
+#else
 				Debug( LDAP_DEBUG_ANY,
 					"Error: ldap_modrdn_s failed modifying %s: %s\n",
 					*errmsg ? *errmsg : ldap_err2string( lderr ),
 					re->re_dn, 0 );
+#endif
 			}
 			break;
 
 		default:
+#ifdef NEW_LOGGING
+			LDAP_LOG (( " ldap_op", LDAP_LEVEL_ERR, "do_ldap: "
+				"Error: bad op \"%d\", dn = \"%s\"\n",
+				re->re_changetype, re->re_dn ));
+#else
 			Debug( LDAP_DEBUG_ANY,
 				"Error: do_ldap: bad op \"%d\", dn = \"%s\"\n",
 				re->re_changetype, re->re_dn, 0 );
+#endif
 			return DO_LDAP_ERR_FATAL;
 		}
 
@@ -190,16 +220,27 @@ op_ldap_add(
 	ldmarr[ nattrs ] = NULL;
 
 	/* Perform the operation */
+#ifdef NEW_LOGGING
+	LDAP_LOG (( "ldap_op", LDAP_LEVEL_ARGS, 
+		"op_ldap_add: replica %s:%d - add dn \"%s\"\n",
+		ri->ri_hostname, ri->ri_port, re->re_dn ));
+#else
 	Debug( LDAP_DEBUG_ARGS, "replica %s:%d - add dn \"%s\"\n",
 		ri->ri_hostname, ri->ri_port, re->re_dn );
+#endif
 	rc = ldap_add_s( ri->ri_ldp, re->re_dn, ldmarr );
 
 	ldap_get_option( ri->ri_ldp, LDAP_OPT_ERROR_NUMBER, &lderr);
 
     } else {
 	*errmsg = "No modifications to do";
+#ifdef NEW_LOGGING
+	LDAP_LOG (( "ldap_op", LDAP_LEVEL_ERR, 
+		"op_ldap_add: Error: no mods to do (%s)!\n", re->re_dn ));
+#else
 	Debug( LDAP_DEBUG_ANY,
 	       "Error: op_ldap_add: no mods to do (%s)!\n", re->re_dn, 0, 0 );
+#endif
     }
     free_ldmarr( ldmarr );
     return( lderr ); 
@@ -235,8 +276,13 @@ op_ldap_modify(
 
     if ( re->re_mods == NULL ) {
 	*errmsg = "No arguments given";
+#ifdef NEW_LOGGING
+	LDAP_LOG (( "ldap_op", LDAP_LEVEL_ERR, 
+		"op_ldap_modify: Error: no arguments\n" ));
+#else
 	Debug( LDAP_DEBUG_ANY, "Error: op_ldap_modify: no arguments\n",
 		0, 0, 0 );
+#endif
 	    return -1;
     }
 
@@ -283,9 +329,14 @@ op_ldap_modify(
 	    break;
 	default:
 	    if ( state == AWAITING_OP ) {
+#ifdef NEW_LOGGING
+		LDAP_LOG (( "ldap_op", LDAP_LEVEL_ERR, 
+			"op_ldap_modify: Error: unknown mod type \"%s\"\n", type ));
+#else
 		Debug( LDAP_DEBUG_ANY,
 			"Error: op_ldap_modify: unknown mod type \"%s\"\n",
 			type, 0, 0 );
+#endif
 		continue;
 	    }
 
@@ -296,9 +347,16 @@ op_ldap_modify(
 	     * Construct the mod_bvalues part of the ldapmod struct.
 	     */
 	    if ( strcasecmp( type, ldm->mod_type )) {
+#ifdef NEW_LOGGING
+		LDAP_LOG (( "ldap_op", LDAP_LEVEL_ERR, 
+			"op_ldap_modify: Error: "
+			"malformed modify op, %s: %s (expecting \"%s\")\n", 
+			type, value, ldm->mod_type ));
+#else
 		Debug( LDAP_DEBUG_ANY,
 			"Error: malformed modify op, %s: %s (expecting %s:)\n",
 			type, value, ldm->mod_type );
+#endif
 		continue;
 	    }
 	    ldm->mod_bvalues = ( struct berval ** )
@@ -316,8 +374,14 @@ op_ldap_modify(
 
     if ( nops > 0 ) {
 	/* Actually perform the LDAP operation */
+#ifdef NEW_LOGGING
+	LDAP_LOG (( "ldap_op", LDAP_LEVEL_DETAIL1, 
+		"op_ldap_modify: replica %s:%d - modify dn \"%s\"\n", 
+		ri->ri_hostname, ri->ri_port, re->re_dn ));
+#else
 	Debug( LDAP_DEBUG_ARGS, "replica %s:%d - modify dn \"%s\"\n",
 		ri->ri_hostname, ri->ri_port, re->re_dn );
+#endif
 	rc = ldap_modify_s( ri->ri_ldp, re->re_dn, ldmarr );
     }
     free_ldmarr( ldmarr );
@@ -339,8 +403,14 @@ op_ldap_delete(
 {
     int		rc;
 
+#ifdef NEW_LOGGING
+	LDAP_LOG (( "ldap_op", LDAP_LEVEL_ARGS, 
+		"op_ldap_delete: replica %s:%d - delete dn \"%s\"\n",
+	    ri->ri_hostname, ri->ri_port, re->re_dn ));
+#else
     Debug( LDAP_DEBUG_ARGS, "replica %s:%d - delete dn \"%s\"\n",
 	    ri->ri_hostname, ri->ri_port, re->re_dn );
+#endif
     rc = ldap_delete_s( ri->ri_ldp, re->re_dn );
 
     return( rc );
@@ -376,8 +446,13 @@ op_ldap_modrdn(
 
     if ( re->re_mods == NULL ) {
 	*errmsg = "No arguments given";
+#ifdef NEW_LOGGING
+	LDAP_LOG (( "ldap_op", LDAP_LEVEL_ERR, 
+		"op_ldap_modrdn: Error: no arguments\n" ));
+#else
 	Debug( LDAP_DEBUG_ANY, "Error: op_ldap_modrdn: no arguments\n",
 		0, 0, 0 );
+#endif
 	    return -1;
     }
 
@@ -387,9 +462,15 @@ op_ldap_modrdn(
     for ( mi = re->re_mods, i = 0; mi[ i ].mi_type != NULL; i++ ) {
 	if ( !strcmp( mi[ i ].mi_type, T_NEWRDNSTR )) {
 		if( state & GOT_NEWRDN ) {
+#ifdef NEW_LOGGING
+		LDAP_LOG (( "ldap_op", LDAP_LEVEL_ERR, 
+			"op_ldap_modrdn: Error: multiple newrdn arg \"%s\"\n",
+			mi[ i ].mi_val ));
+#else
 		Debug( LDAP_DEBUG_ANY,
 			"Error: op_ldap_modrdn: multiple newrdn arg \"%s\"\n",
 			mi[ i ].mi_val, 0, 0 );
+#endif
 		*errmsg = "Multiple newrdn argument";
 		return -1;
 		}
@@ -399,9 +480,15 @@ op_ldap_modrdn(
 
 	} else if ( !strcmp( mi[ i ].mi_type, T_DELOLDRDNSTR )) {
 		if( state & GOT_DELOLDRDN ) {
+#ifdef NEW_LOGGING
+		LDAP_LOG (( "ldap_op", LDAP_LEVEL_ERR, 
+			"op_ldap_modrdn: Error: multiple deleteoldrdn arg \"%s\"\n",
+			mi[ i ].mi_val ));
+#else
 		Debug( LDAP_DEBUG_ANY,
 			"Error: op_ldap_modrdn: multiple deleteoldrdn arg \"%s\"\n",
 			mi[ i ].mi_val, 0, 0 );
+#endif
 		*errmsg = "Multiple newrdn argument";
 		return -1;
 		}
@@ -412,18 +499,30 @@ op_ldap_modrdn(
 	    } else if ( !strcmp( mi[ i ].mi_val, "1" )) {
 		drdnflag = 1;
 	    } else {
+#ifdef NEW_LOGGING
+		LDAP_LOG (( "ldap_op", LDAP_LEVEL_ERR, 
+			"op_ldap_modrdn: Error: bad deleteoldrdn arg \"%s\"\n",
+			mi[ i ].mi_val ));
+#else
 		Debug( LDAP_DEBUG_ANY,
 			"Error: op_ldap_modrdn: bad deleteoldrdn arg \"%s\"\n",
 			mi[ i ].mi_val, 0, 0 );
+#endif
 		*errmsg = "Incorrect argument to deleteoldrdn";
 		return -1;
 	    }
 
 	} else if ( !strcmp( mi[ i ].mi_type, T_NEWSUPSTR )) {
 		if( state & GOT_NEWSUP ) {
+#ifdef NEW_LOGGING
+		LDAP_LOG (( "ldap_op", LDAP_LEVEL_ERR, 
+			"op_ldap_modrdn: Error: multiple newsuperior arg \"%s\"\n",
+			mi[ i ].mi_val ));
+#else
 		Debug( LDAP_DEBUG_ANY,
 			"Error: op_ldap_modrdn: multiple newsuperior arg \"%s\"\n",
 			mi[ i ].mi_val, 0, 0 );
+#endif
 		*errmsg = "Multiple newsuperior argument";
 		return -1;
 		}
@@ -432,8 +531,14 @@ op_ldap_modrdn(
 	    state |= GOT_NEWSUP;
 
 	} else {
+#ifdef NEW_LOGGING
+		LDAP_LOG (( "ldap_op", LDAP_LEVEL_ERR, 
+			"op_ldap_modrdn: Error: bad type \"%s\"\n",
+			mi[ i ].mi_type ));
+#else
 	    Debug( LDAP_DEBUG_ANY, "Error: op_ldap_modrdn: bad type \"%s\"\n",
 		    mi[ i ].mi_type, 0, 0 );
+#endif
 	    *errmsg = "Bad value in replication log entry";
 	    return -1;
 	}
@@ -443,8 +548,13 @@ op_ldap_modrdn(
      * Punt if we don't have all the args.
      */
     if ( !GOT_ALL_MODDN(state) ) {
+#ifdef NEW_LOGGING
+		LDAP_LOG (( "ldap_op", LDAP_LEVEL_ERR, 
+			"op_ldap_modrdn: Error: missing arguments\n" ));
+#else
 	Debug( LDAP_DEBUG_ANY, "Error: op_ldap_modrdn: missing arguments\n",
 		0, 0, 0 );
+#endif
 	*errmsg = "Missing argument: requires \"newrdn\" and \"deleteoldrdn\"";
 	return -1;
     }
@@ -457,9 +567,15 @@ op_ldap_modrdn(
 	buf2 = (char *) ch_malloc( strlen( re->re_dn ) + strlen( mi->mi_val )
 		+ 10 );
 	sprintf( buf2, "(\"%s\" -> \"%s\")", re->re_dn, mi->mi_val );
+#ifdef NEW_LOGGING
+	LDAP_LOG (( "ldap_op", LDAP_LEVEL_ARGS, 
+		"op_ldap_modrdn: replica %s - modify rdn %s (flag: %d)\n",
+		buf, buf2, drdnflag ));
+#else
 	Debug( LDAP_DEBUG_ARGS,
 		"replica %s - modify rdn %s (flag: %d)\n",
 		buf, buf2, drdnflag );
+#endif
 	free( buf2 );
     }
 #endif /* LDAP_DEBUG */
@@ -592,9 +708,15 @@ do_unbind(
     if (( ri != NULL ) && ( ri->ri_ldp != NULL )) {
 	rc = ldap_unbind( ri->ri_ldp );
 	if ( rc != LDAP_SUCCESS ) {
+#ifdef NEW_LOGGING
+		LDAP_LOG (( "ldap_op", LDAP_LEVEL_ERR, 
+			"do_unbind: ldap_unbind failed for %s:%d: %s\n",
+		    ri->ri_hostname, ri->ri_port, ldap_err2string( rc ) ));
+#else
 	    Debug( LDAP_DEBUG_ANY,
 		    "Error: do_unbind: ldap_unbind failed for %s:%d: %s\n",
 		    ri->ri_hostname, ri->ri_port, ldap_err2string( rc ) );
+#endif
 	}
 	ri->ri_ldp = NULL;
     }
@@ -625,27 +747,48 @@ do_bind(
     *lderr = 0;
 
     if ( ri == NULL ) {
+#ifdef NEW_LOGGING
+	LDAP_LOG (( "ldap_op", LDAP_LEVEL_ERR, "do_bind: null ri ptr\n" ));
+#else
 	Debug( LDAP_DEBUG_ANY, "Error: do_bind: null ri ptr\n", 0, 0, 0 );
+#endif
 	return( BIND_ERR_BADRI );
     }
 
     if ( ri->ri_ldp != NULL ) {
 	ldrc = ldap_unbind( ri->ri_ldp );
 	if ( ldrc != LDAP_SUCCESS ) {
+#ifdef NEW_LOGGING
+		LDAP_LOG (( "ldap_op", LDAP_LEVEL_ERR, 
+			"do_bind: ldap_unbind failed: %s\n", ldap_err2string( ldrc ) ));
+#else
 	    Debug( LDAP_DEBUG_ANY,
 		    "Error: do_bind: ldap_unbind failed: %s\n",
 		    ldap_err2string( ldrc ), 0, 0 );
+#endif
 	}
 	ri->ri_ldp = NULL;
     }
 
+#ifdef NEW_LOGGING
+	LDAP_LOG (( "ldap_op", LDAP_LEVEL_ARGS, 
+		"do_bind: Initializing session to %s:%d\n", 
+	    ri->ri_hostname, ri->ri_port ));
+#else
     Debug( LDAP_DEBUG_ARGS, "Initializing session to %s:%d\n",
 	    ri->ri_hostname, ri->ri_port, 0 );
+#endif
 
     ri->ri_ldp = ldap_init( ri->ri_hostname, ri->ri_port );
     if ( ri->ri_ldp == NULL ) {
+#ifdef NEW_LOGGING
+		LDAP_LOG (( "ldap_op", LDAP_LEVEL_ERR, 
+			"do_bind: ldap_init (%s, %d) failed: %s\n",
+			ri->ri_hostname, ri->ri_port, sys_errlist[ errno ] ));
+#else
 		Debug( LDAP_DEBUG_ANY, "Error: ldap_init(%s, %d) failed: %s\n",
 			ri->ri_hostname, ri->ri_port, sys_errlist[ errno ] );
+#endif
 		return( BIND_ERR_OPEN );
     }
 
@@ -655,9 +798,15 @@ do_bind(
 			LDAP_OPT_PROTOCOL_VERSION, &version);
 
 		if( err != LDAP_OPT_SUCCESS ) {
+#ifdef NEW_LOGGING
+			LDAP_LOG (( "ldap_op", LDAP_LEVEL_ERR, "do_bind: ",
+				"Error: ldap_set_option(%s, LDAP_OPT_VERSION, 3) failed!\n",
+				ri->ri_hostname ));
+#else
 			Debug( LDAP_DEBUG_ANY,
 				"Error: ldap_set_option(%s, LDAP_OPT_VERSION, 3) failed!\n",
 				ri->ri_hostname, NULL, NULL );
+#endif
 
 			ldap_unbind( ri->ri_ldp );
 			ri->ri_ldp = NULL;
@@ -674,9 +823,15 @@ do_bind(
 		err = ldap_set_option(ri->ri_ldp, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
 
 		if( err != LDAP_OPT_SUCCESS ) {
+#ifdef NEW_LOGGING
+			LDAP_LOG (( "ldap_op", LDAP_LEVEL_ERR, "do_bind: ",
+				"Error: ldap_set_option(%s, REFERRALS, OFF) failed!\n",
+				ri->ri_hostname ));
+#else
 			Debug( LDAP_DEBUG_ANY,
 				"Error: ldap_set_option(%s,REFERRALS, OFF) failed!\n",
 				ri->ri_hostname, NULL, NULL );
+#endif
 			ldap_unbind( ri->ri_ldp );
 			ri->ri_ldp = NULL;
 			return BIND_ERR_REFERRALS;
@@ -689,10 +844,17 @@ do_bind(
 		err = ldap_start_tls_s(ri->ri_ldp, NULL, NULL);
 
 		if( err != LDAP_SUCCESS ) {
+#ifdef NEW_LOGGING
+			LDAP_LOG (( "ldap_op", LDAP_LEVEL_ERR, "do_bind: ",
+				"%s: ldap_start_tls failed: %s (%d)\n",
+				ri->ri_tls == TLS_CRITICAL ? "Error" : "Warning",
+				ldap_err2string( err ), err ));
+#else
 			Debug( LDAP_DEBUG_ANY,
 				"%s: ldap_start_tls failed: %s (%d)\n",
 				ri->ri_tls == TLS_CRITICAL ? "Error" : "Warning",
 				ldap_err2string( err ), err );
+#endif
 
 			if( ri->ri_tls == TLS_CRITICAL ) {
 				ldap_unbind( ri->ri_ldp );
@@ -707,14 +869,26 @@ do_bind(
 	/*
 	 * Bind with a plaintext password.
 	 */
+#ifdef NEW_LOGGING
+	LDAP_LOG (( "ldap_op", LDAP_LEVEL_ARGS, 
+		"do_bind: bind to %s:%d as %s (simple)\n", 
+		ri->ri_hostname, ri->ri_port, ri->ri_bind_dn ));
+#else
 	Debug( LDAP_DEBUG_ARGS, "bind to %s:%d as %s (simple)\n",
 		ri->ri_hostname, ri->ri_port, ri->ri_bind_dn );
+#endif
 	ldrc = ldap_simple_bind_s( ri->ri_ldp, ri->ri_bind_dn,
 		ri->ri_password );
 	if ( ldrc != LDAP_SUCCESS ) {
+#ifdef NEW_LOGGING
+		LDAP_LOG (( "ldap_op", LDAP_LEVEL_ERR, "do_bind: "
+		    "Error: ldap_simple_bind_s for %s:%d failed: %s\n",
+		    ri->ri_hostname, ri->ri_port, ldap_err2string( ldrc ) ));
+#else
 	    Debug( LDAP_DEBUG_ANY,
 		    "Error: ldap_simple_bind_s for %s:%d failed: %s\n",
 		    ri->ri_hostname, ri->ri_port, ldap_err2string( ldrc ));
+#endif
 	    *lderr = ldrc;
 		ldap_unbind( ri->ri_ldp );
 		ri->ri_ldp = NULL;
@@ -723,8 +897,14 @@ do_bind(
 	break;
 
 	case AUTH_SASL:
+#ifdef NEW_LOGGING
+	LDAP_LOG (( "ldap_op", LDAP_LEVEL_ARGS, 
+		"do_bind: bind to %s as %s via %s (SASL)\n", 
+		ri->ri_hostname, ri->ri_authcId, ri->ri_saslmech ));
+#else
 	Debug( LDAP_DEBUG_ARGS, "bind to %s as %s via %s (SASL)\n",
 		ri->ri_hostname, ri->ri_authcId, ri->ri_saslmech );
+#endif
 
 #ifdef HAVE_CYRUS_SASL
 	if( ri->ri_secprops != NULL ) {
@@ -733,9 +913,15 @@ do_bind(
 			ri->ri_secprops);
 
 		if( err != LDAP_OPT_SUCCESS ) {
+#ifdef NEW_LOGGING
+			LDAP_LOG (( "ldap_op", LDAP_LEVEL_ERR, "do_bind: "
+				"Error: ldap_set_option(%s,SECPROPS,\"%s\") failed!\n",
+				ri->ri_hostname, ri->ri_secprops ));
+#else
 			Debug( LDAP_DEBUG_ANY,
 				"Error: ldap_set_option(%s,SECPROPS,\"%s\") failed!\n",
 				ri->ri_hostname, ri->ri_secprops, NULL );
+#endif
 			ldap_unbind( ri->ri_ldp );
 			ri->ri_ldp = NULL;
 			return BIND_ERR_SASL_FAILED;
@@ -751,8 +937,14 @@ do_bind(
 		    ri->ri_saslmech, NULL, NULL,
 		    LDAP_SASL_QUIET, lutil_sasl_interact, defaults );
 		if ( ldrc != LDAP_SUCCESS ) {
+#ifdef NEW_LOGGING
+			LDAP_LOG (( "ldap_op", LDAP_LEVEL_ERR, "do_bind: "
+				"Error: LDAP SASL for %s:%d failed: %s\n",
+			    ri->ri_hostname, ri->ri_port, ldap_err2string( ldrc ) ));
+#else
 			Debug( LDAP_DEBUG_ANY, "Error: LDAP SASL for %s:%d failed: %s\n",
 			    ri->ri_hostname, ri->ri_port, ldap_err2string( ldrc ));
+#endif
 			*lderr = ldrc;
 			ldap_unbind( ri->ri_ldp );
 			ri->ri_ldp = NULL;
@@ -763,19 +955,31 @@ do_bind(
 		ber_memfree( defaults );
 	}
 	break;
+#else
+#ifdef NEW_LOGGING
+	LDAP_LOG (( "ldap_op", LDAP_LEVEL_ERR, "do_bind: "
+		"Error: do_bind: SASL not supported %s:%d\n",
+		 ri->ri_hostname, ri->ri_port ));
 #else
 	Debug( LDAP_DEBUG_ANY,
 		"Error: do_bind: SASL not supported %s:%d\n",
 		 ri->ri_hostname, ri->ri_port, NULL );
+#endif
 	ldap_unbind( ri->ri_ldp );
 	ri->ri_ldp = NULL;
 	return( BIND_ERR_BAD_ATYPE );
 #endif
 
     default:
+#ifdef NEW_LOGGING
+	LDAP_LOG (( "ldap_op", LDAP_LEVEL_ERR, "do_bind: "
+		"Error: do_bind: unknown auth type \"%d\" for %s:%d\n",
+		ri->ri_bind_method, ri->ri_hostname, ri->ri_port ));
+#else
 	Debug(  LDAP_DEBUG_ANY,
 		"Error: do_bind: unknown auth type \"%d\" for %s:%d\n",
 		ri->ri_bind_method, ri->ri_hostname, ri->ri_port );
+#endif
 	ldap_unbind( ri->ri_ldp );
 	ri->ri_ldp = NULL;
 	return( BIND_ERR_BAD_ATYPE );
@@ -796,9 +1000,15 @@ do_bind(
 		err = ldap_set_option(ri->ri_ldp, LDAP_OPT_SERVER_CONTROLS, &ctrls);
 
 		if( err != LDAP_OPT_SUCCESS ) {
+#ifdef NEW_LOGGING
+			LDAP_LOG (( "ldap_op", LDAP_LEVEL_ERR, "do_bind: "
+				"ldap_set_option(%s, SERVER_CONTROLS, ManageDSAit) failed!\n",
+				ri->ri_hostname ));
+#else
 			Debug( LDAP_DEBUG_ANY, "Error: "
 				"ldap_set_option(%s, SERVER_CONTROLS, ManageDSAit) failed!\n",
 				ri->ri_hostname, NULL, NULL );
+#endif
 			ldap_unbind( ri->ri_ldp );
 			ri->ri_ldp = NULL;
 			return BIND_ERR_MANAGEDSAIT;
@@ -827,6 +1037,17 @@ dump_ldm_array(
 
     for ( i = 0; ldmarr[ i ] != NULL; i++ ) {
 	ldm = ldmarr[ i ];
+#ifdef NEW_LOGGING
+	LDAP_LOG (( "ldap_op", LDAP_LEVEL_INFO, "dump_ldm_array: "
+		"Trace (%ld): *** ldmarr[ %d ] contents:\n",
+		(long) getpid(), i ));
+	LDAP_LOG (( "ldap_op", LDAP_LEVEL_INFO, "dump_ldm_array: "
+		"Trace (%ld): *** ldm->mod_op: %d\n",
+		(long) getpid(), ldm->mod_op ));
+	LDAP_LOG (( "ldap_op", LDAP_LEVEL_INFO, "dump_ldm_array: "
+		"Trace (%ld): *** ldm->mod_type: %s\n",
+		(long) getpid(), ldm->mod_type ));
+#else
 	Debug( LDAP_DEBUG_TRACE,
 		"Trace (%ld): *** ldmarr[ %d ] contents:\n",
 		(long) getpid(), i, 0 );
@@ -836,13 +1057,19 @@ dump_ldm_array(
 	Debug( LDAP_DEBUG_TRACE,
 		"Trace (%ld): *** ldm->mod_type: %s\n",
 		(long) getpid(), ldm->mod_type, 0 );
+#endif
 	if ( ldm->mod_bvalues != NULL ) {
 	    for ( j = 0; ( b = ldm->mod_bvalues[ j ] ) != NULL; j++ ) {
 		msgbuf = ch_malloc( b->bv_len + 512 );
 		sprintf( msgbuf, "***** bv[ %d ] len = %ld, val = <%s>",
 			j, b->bv_len, b->bv_val );
+#ifdef NEW_LOGGING
+		LDAP_LOG (( "ldap_op", LDAP_LEVEL_INFO, "dump_ldm_array: "
+			"Trace (%ld):%s\n", (long) getpid(), msgbuf ));
+#else
 		Debug( LDAP_DEBUG_TRACE,
 			"Trace (%ld):%s\n", (long) getpid(), msgbuf, 0 );
+#endif
 		free( msgbuf );
 	    }
 	}
diff --git a/servers/slurpd/lock.c b/servers/slurpd/lock.c
index 62aab06784b37f5acd99621aa00da0c789b69a70..d457cdfc21ae62aa655692e0bef074e06d05c21f 100644
--- a/servers/slurpd/lock.c
+++ b/servers/slurpd/lock.c
@@ -1,3 +1,8 @@
+/* $OpenLDAP$ */
+/*
+ * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
 /*
  * Copyright (c) 1996 Regents of the University of Michigan.
  * All rights reserved.
@@ -14,24 +19,27 @@
  * lock.c - routines to open and apply an advisory lock to a file
  */
 
+#include "portable.h"
+
 #include <stdio.h>
-#include <sys/time.h>
-#include <sys/types.h>
+
+#include <ac/param.h>
+#include <ac/string.h>
+#include <ac/socket.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+
+#ifdef HAVE_SYS_FILE_H
 #include <sys/file.h>
-#include <sys/param.h>
-#include <sys/socket.h>
-#include "portable.h"
-#ifdef USE_LOCKF
-#include <unistd.h>
 #endif
-#include "../slapd/slap.h"
 
+#include "slurp.h"
 
 
 FILE *
 lock_fopen(
-    char	*fname,
-    char	*type,
+    const char	*fname,
+    const char	*type,
     FILE	**lfp
 )
 {
@@ -42,29 +50,31 @@ lock_fopen(
 	strcpy( buf, fname );
 	strcat( buf, ".lock" );
 	if ( (*lfp = fopen( buf, "w" )) == NULL ) {
+#ifdef NEW_LOGGING
+		LDAP_LOG (( "lock", LDAP_LEVEL_ERR, "lock_fopen: "
+			"Error: could not open \"%s\"\n", buf ));
+#else
 		Debug( LDAP_DEBUG_ANY,
 			"Error: could not open \"%s\"\n", buf, 0, 0 );
+#endif
 		return( NULL );
 	}
 
 	/* acquire the lock */
-#ifdef USE_LOCKF
-	while ( lockf( fileno( *lfp ), F_LOCK, 0 ) != 0 ) {
-#else
-	while ( flock( fileno( *lfp ), LOCK_EX ) != 0 ) {
-#endif
-		;	/* NULL */
-	}
+	ldap_lockf( fileno(*lfp) );
 
 	/* open the log file */
 	if ( (fp = fopen( fname, type )) == NULL ) {
+#ifdef NEW_LOGGING
+		LDAP_LOG (( "lock", LDAP_LEVEL_ERR, "lock_fopen: "
+			"Error: could not open \"%s\"\n", fname ));
+#else
 		Debug( LDAP_DEBUG_ANY,
 			"Error: could not open \"%s\"\n", fname, 0, 0 );
-#ifdef USE_LOCKF
-		lockf( fileno( *lfp ), F_ULOCK, 0 );
-#else
-		flock( fileno( *lfp ), LOCK_UN );
 #endif
+		ldap_unlockf( fileno(*lfp) );
+		fclose( *lfp );
+		*lfp = NULL;
 		return( NULL );
 	}
 
@@ -80,11 +90,7 @@ lock_fclose(
 )
 {
 	/* unlock */
-#ifdef USE_LOCKF
-	lockf( fileno( lfp ), F_ULOCK, 0 );
-#else
-	flock( fileno( lfp ), LOCK_UN );
-#endif
+	ldap_unlockf( fileno(lfp) );
 	fclose( lfp );
 
 	return( fclose( fp ) );
@@ -97,15 +103,21 @@ lock_fclose(
  */
 int
 acquire_lock(
-    char	*file,
+    const char	*file,
     FILE	**rfp,
     FILE	**lfp
 )
 {
     if (( *rfp = lock_fopen( file, "r+", lfp )) == NULL ) {
+#ifdef NEW_LOGGING
+	LDAP_LOG (( "lock", LDAP_LEVEL_ERR, "acquire_lock: "
+		"Error: acquire_lock(%ld): Could not acquire lock on \"%s\"\n",
+		(long) getpid(), file ));
+#else
 	Debug( LDAP_DEBUG_ANY,
-		"Error: acquire_lock(%d): Could not acquire lock on \"%s\"\n",
-		getpid(), file, 0);
+		"Error: acquire_lock(%ld): Could not acquire lock on \"%s\"\n",
+		(long) getpid(), file, 0);
+#endif
 	return( -1 );
     }
     return( 0 );
@@ -119,15 +131,21 @@ acquire_lock(
  */
 int
 relinquish_lock(
-    char	*file,
+    const char	*file,
     FILE	*rfp,
     FILE	*lfp
 )
 {
     if ( lock_fclose( rfp, lfp ) == EOF ) {
+#ifdef NEW_LOGGING
+	LDAP_LOG (( "lock", LDAP_LEVEL_ERR, "relinguish_lock: "
+		"Error: relinquish_lock (%ld): Error closing \"%s\"\n",
+		(long) getpid(), file ));
+#else
 	Debug( LDAP_DEBUG_ANY,
-		"Error: relinquish_lock (%d): Error closing \"%s\"\n",
-		getpid(), file, 0 );
+		"Error: relinquish_lock (%ld): Error closing \"%s\"\n",
+		(long) getpid(), file, 0 );
+#endif
 	return( -1 );
     }
     return( 0 );
diff --git a/servers/slurpd/main.c b/servers/slurpd/main.c
index 19949128d31d294996029b9619ad24eadaa91e8a..7a391221b50530b28aecb430a00223010e47df59 100644
--- a/servers/slurpd/main.c
+++ b/servers/slurpd/main.c
@@ -120,8 +120,13 @@ main(
     if ( ldap_pvt_thread_create( &(sglob->fm_tid),
 		0, fm, (void *) NULL ) != 0 )
 	{
+#ifdef NEW_LOGGING
+	LDAP_LOG (( "main", LDAP_LEVEL_ERR,
+		"main: file manager ldap_pvt_thread_create failed\n" ));
+#else
 	Debug( LDAP_DEBUG_ANY, "file manager ldap_pvt_thread_create failed\n",
 		0, 0, 0 );
+#endif
 	exit( EXIT_FAILURE );
 
     }
@@ -157,7 +162,12 @@ main(
     /* destroy the thread package */
     ldap_pvt_thread_destroy();
 
+#ifdef NEW_LOGGING
+	LDAP_LOG (( "main", LDAP_LEVEL_RESULTS,
+		"main: slurpd terminated\n" ));
+#else
     Debug( LDAP_DEBUG_ANY, "slurpd: terminated.\n", 0, 0, 0 );
+#endif
 	return 0;
 #endif /* !NO_THREADS */
 }
diff --git a/servers/slurpd/re.c b/servers/slurpd/re.c
index 4a6dc382377247c1da34ff330d9170a69c274e01..7fd254893cf6b7ba015ca22ae2a2e17909f3d441 100644
--- a/servers/slurpd/re.c
+++ b/servers/slurpd/re.c
@@ -1,3 +1,8 @@
+/* $OpenLDAP$ */
+/*
+ * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
 /*
  * Copyright (c) 1996 Regents of the University of Michigan.
  * All rights reserved.
@@ -18,28 +23,27 @@
  */
 
 
+#include "portable.h"
+
 #include <stdio.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/socket.h>
 
-#include "../slapd/slap.h"
+#include <ac/errno.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/ctype.h>
+
 #include "slurp.h"
 #include "globals.h"
 
-/* externs */
-extern char *str_getline( char **next );
-extern void ch_free( char *p );
-
-extern char *sys_errlist[];
+#include "../slapd/slap.h"
 
 /* Forward references */
-static Rh 	*get_repl_hosts( char *, int *, char ** );
-static int	gettype( char * );
-static int	getchangetype( char *);
-static int	Re_parse( Re *re, char *replbuf );
-static void 	Re_dump( Re *re, FILE *fp );
-static void	warn_unknown_replica( char *, int port );
+static Rh 	*get_repl_hosts LDAP_P(( char *, int *, char ** ));
+static int	gettype LDAP_P(( char * ));
+static int	getchangetype LDAP_P(( char * ));
+static int	Re_parse LDAP_P(( Re *re, char *replbuf ));
+static void 	Re_dump LDAP_P(( Re *re, FILE *fp ));
+static void	warn_unknown_replica LDAP_P(( char *, int port ));
 
 /* Globals, scoped within this file */
 static int	nur = 0;	/* Number of unknown replicas */
@@ -62,6 +66,7 @@ Re_getnext(
 
 /*
  * Free an Re
+ * ??? Something should apparently return nonzero here, but I dont know what.
  */
 static int
 Re_free(
@@ -76,15 +81,18 @@ Re_free(
 	return 0;
     }
     if ( re->re_refcnt > 0 ) {
+#ifdef NEW_LOGGING
+	LDAP_LOG (( "re", LDAP_LEVEL_WARNING, "Re_free: "
+		"Warning: freeing re (dn: %s) with nonzero refcnt\n", re->re_dn ));
+#else
 	Debug( LDAP_DEBUG_ANY,
 		"Warning: freeing re (dn: %s) with nonzero refcnt\n",
 		re->re_dn, 0, 0 );
+#endif
     }
-#if !defined( THREAD_SUNOS4_LWP )
-    /* This seems to have problems under SunOS lwp */
-    pthread_mutex_destroy( &re->re_mutex );
-#endif /* THREAD_SUNOS4_LWP */
-    ch_free( re->re_timestamp );
+
+    ldap_pvt_thread_mutex_destroy( &re->re_mutex );
+
     if (( rh = re->re_replicas ) != NULL ) {
 	for ( i = 0; rh[ i ].rh_hostname != NULL; i++ ) {
 	    free( rh[ i ].rh_hostname );
@@ -100,6 +108,7 @@ Re_free(
 	free( mi );
     }
     free( re );
+    return 0;
 }
 
 
@@ -125,17 +134,25 @@ Re_parse(
     int			state;
     int			nml;
     char		*buf, *rp, *p;
-    long		buflen;
+    size_t		buflen;
     char		*type, *value;
-    int			len;
+    ber_len_t	len;
     int			nreplicas;
 
     if ( re == NULL ) {
+#ifdef NEW_LOGGING
+	LDAP_LOG (( "re", LDAP_LEVEL_ERR, "Re_parse: Error: re is NULL\n" ));
+#else
 	Debug( LDAP_DEBUG_ANY, "Re_parse: error: re is NULL\n", 0, 0, 0 );
+#endif
 	return -1;
     }
     if ( replbuf == NULL ) {
+#ifdef NEW_LOGGING
+	LDAP_LOG (( "re", LDAP_LEVEL_ERR, "Re_parse: Error: replbuf is NULL\n" ));
+#else
 	Debug( LDAP_DEBUG_ANY, "Re_parse: error: replbuf is NULL\n", 0, 0, 0 );
+#endif
 	return -1;
     }
 
@@ -147,7 +164,7 @@ Re_parse(
     re->re_refcnt = sglob->num_replicas;
 
     for (;;) {
-	if (( state == GOT_ALL ) || ( buf = str_getline( &rp )) == NULL ) {
+	if (( state == GOT_ALL ) || ( buf = ldif_getline( &rp )) == NULL ) {
 	    break;
 	}
 	/*
@@ -158,11 +175,16 @@ Re_parse(
 	if ( strncmp( buf, ERROR_STR, strlen( ERROR_STR )) == 0 ) {
 	    continue;
 	}
-	buflen = ( long ) strlen( buf );
-	if ( str_parse_line( buf, &type, &value, &len ) < 0 ) {
+	buflen = strlen( buf );
+	if ( ldif_parse_line( buf, &type, &value, &len ) < 0 ) {
+#ifdef NEW_LOGGING
+		LDAP_LOG (( "re", LDAP_LEVEL_ERR, 
+			"Re_parse: Error: malformed replog file\n" ));
+#else
 	    Debug( LDAP_DEBUG_ANY,
 		    "Error: Re_parse: malformed replog file\n",
 		    0, 0, 0 );
+#endif
 	    return -1;
 	}
 	switch ( gettype( type )) {
@@ -175,46 +197,71 @@ Re_parse(
 		/* there was a sequence number */
 		*p++ = '\0';
 	    }
-	    re->re_timestamp = strdup( value );
-	    if ( p != NULL && isdigit( *p )) {
+	    re->re_timestamp = atol( value );
+	    if ( p != NULL && isdigit( (unsigned char) *p )) {
 		re->re_seq = atoi( p );
 	    }
 	    state |= GOT_TIME;
 	    break;
 	case T_DN:
-	    re->re_dn = strdup( value );
+	    re->re_dn = ch_malloc( len + 1 );
+		AC_MEMCPY( re->re_dn, value, len );
+		re->re_dn[ len ]='\0';
 	    state |= GOT_DN;
 	    break;
 	default:
 	    if ( !( state == GOT_ALL )) {
+#ifdef NEW_LOGGING
+		LDAP_LOG (( "re", LDAP_LEVEL_ERR, 
+			"Re_parse: Error: bad type <%s>\n", type ));
+#else
 		Debug( LDAP_DEBUG_ANY,
 			"Error: Re_parse: bad type <%s>\n",
 			type, 0, 0 );
+#endif
+		free( type );
+		if ( value != NULL )
+			free( value );
 		return -1;
 	    }
 	}
+	free( type );
+	if ( value != NULL )
+		free( value );
     }
 
     if ( state != GOT_ALL ) {
+#ifdef NEW_LOGGING
+	LDAP_LOG (( "re", LDAP_LEVEL_ERR, 
+		"Re_parse: Error: malformed replog file\n" ));
+#else
 	Debug( LDAP_DEBUG_ANY,
 		"Error: Re_parse: malformed replog file\n",
 		0, 0, 0 );
+#endif
 	return -1;
     }
 
     for (;;) {
-	if (( buf = str_getline( &rp )) == NULL ) {
+	char *const dash = "-";
+
+	if (( buf = ldif_getline( &rp )) == NULL ) {
 	    break;
 	}
-	buflen = ( long ) strlen( buf );
+	buflen = strlen( buf );
 	if (( buflen == 1 ) && ( buf[ 0 ] == '-' )) {
-	    type = "-";
+	    type  = dash;
 	    value = NULL;
 	} else {
-	    if ( str_parse_line( buf, &type, &value, &len ) < 0 ) {
+	    if ( ldif_parse_line( buf, &type, &value, &len ) < 0 ) {
+#ifdef NEW_LOGGING
+		LDAP_LOG (( "re", LDAP_LEVEL_ERR, 
+			"Re_parse: Error: malformed replog line \"%s\"\n", buf ));
+#else
 		Debug( LDAP_DEBUG_ANY,
 			"Error: malformed replog line \"%s\"\n",
 			buf, 0, 0 );
+#endif
 		return -1;
 	    }
 	}
@@ -222,7 +269,9 @@ Re_parse(
 	    sizeof( Mi ) * ( nml + 2 ));
 	re->re_mods[ nml ].mi_type = strdup( type );
 	if ( value != NULL ) {
-	    re->re_mods[ nml ].mi_val = strdup( value );
+	    re->re_mods[ nml ].mi_val = ch_malloc( len + 1 );
+		AC_MEMCPY( re->re_mods[ nml ].mi_val, value, len );
+		re->re_mods[ nml ].mi_val[ len ] = '\0';
 	    re->re_mods[ nml ].mi_len = len;
 	} else {
 	    re->re_mods[ nml ].mi_val = NULL;
@@ -231,6 +280,11 @@ Re_parse(
 	re->re_mods[ nml + 1 ].mi_type = NULL;
 	re->re_mods[ nml + 1 ].mi_val = NULL;
 	nml++;
+
+	if ( type != dash )
+		free( type );
+	if ( value != NULL )
+		free( value );
     }
     return 0;
 }
@@ -252,10 +306,10 @@ get_repl_hosts(
     char	**r_rp
 )
 {
-    char		buf[ LINE_WIDTH + 1 ];
     char		*type, *value, *line, *p;
     Rh			*rh = NULL;
-    int			nreplicas, len;
+    int			nreplicas;
+	ber_len_t	len;
     int			port;
     int			repl_ok;
     int			i;
@@ -274,7 +328,7 @@ get_repl_hosts(
     for (;;) {
 	/* If this is a reject log, we need to skip over the ERROR: line */
 	if ( !strncmp( *r_rp, ERROR_STR, strlen( ERROR_STR ))) {
-	    line = str_getline( r_rp );
+	    line = ldif_getline( r_rp );
 	    if ( line == NULL ) {
 		break;
 	    }
@@ -282,14 +336,14 @@ get_repl_hosts(
 	if ( strncasecmp( *r_rp, "replica:", 7 )) {
 	    break;
 	}
-	line = str_getline( r_rp );
+	line = ldif_getline( r_rp );
 	if ( line == NULL ) {
 	    break;
 	}
-	if ( str_parse_line( line, &type, &value, &len ) < 0 ) {
+	if ( ldif_parse_line( line, &type, &value, &len ) < 0 ) {
 	    return( NULL );
 	}
-	port = LDAP_PORT;
+	port = 0;
 	if (( p = strchr( value, ':' )) != NULL ) {
 	    *p = '\0';
 	    p++;
@@ -309,20 +363,31 @@ get_repl_hosts(
 		break;
 	    }
 	}
+	free( type );
 	if ( !repl_ok ) {
 	    warn_unknown_replica( value, port );
+	    if ( value != NULL )
+		free( value );
 	    continue;
 	}
 
 	rh = (Rh *) ch_realloc((char *) rh, ( nreplicas + 2 ) * sizeof( Rh ));
 	if ( rh == NULL ) {
+#ifdef NEW_LOGGING
+		LDAP_LOG (( "re", LDAP_LEVEL_ERR, 
+			"get_repl_hosts: Out of memory\n" ));
+#else
 	    Debug( LDAP_DEBUG_ANY, "Out of memory in get_repl_hosts\n",
 		    0, 0, 0 );
+#endif
 	    return NULL;
 	}
 	rh[ nreplicas ].rh_hostname = strdup( value );
 	rh[ nreplicas ].rh_port = port;
 	nreplicas++;
+
+	if ( value != NULL )
+		free( value );
     }
 
     if ( nreplicas == 0 ) {
@@ -430,12 +495,16 @@ Re_dump(
     Mi *mi;
 
     if ( re == NULL ) {
+#ifdef NEW_LOGGING
+	LDAP_LOG (( "re", LDAP_LEVEL_ERR, "Re_dump: re is NULL\n" ));
+#else
 	Debug( LDAP_DEBUG_TRACE, "Re_dump: re is NULL\n", 0, 0, 0 );
+#endif
 	return;
     }
     fprintf( fp, "Re_dump: ******\n" );
     fprintf( fp, "re_refcnt: %d\n", re->re_refcnt );
-    fprintf( fp, "re_timestamp: %s\n", re->re_timestamp );
+    fprintf( fp, "re_timestamp: %ld\n", (long) re->re_timestamp );
     fprintf( fp, "re_seq: %d\n", re->re_seq );
     for ( i = 0; re->re_replicas && re->re_replicas[ i ].rh_hostname != NULL;
 		i++ ) {
@@ -494,11 +563,15 @@ Re_write(
     int		i;
     char	*s;
     int		rc = 0;
-    Rh		*rh;
 
     if ( re == NULL || fp == NULL ) {
+#ifdef NEW_LOGGING
+	LDAP_LOG (( "re", LDAP_LEVEL_ERR, 
+		"Re_write: Internal error: NULL argument\n" ));
+#else
 	Debug( LDAP_DEBUG_ANY, "Internal error: Re_write: NULL argument\n",
 		0, 0, 0 );
+#endif
 	return -1;
     }
 
@@ -522,7 +595,7 @@ Re_write(
 	    }
 	}
     }
-    if ( fprintf( fp, "time: %s.%d\n", re->re_timestamp, re->re_seq ) < 0 ) {
+    if ( fprintf( fp, "time: %ld.%d\n", (long) re->re_timestamp, re->re_seq ) < 0 ) {
 	rc = -1;
 	goto bad;
     }
@@ -563,7 +636,8 @@ Re_write(
 	    }
 	} else {
 	    char *obuf;
-	    obuf = ldif_type_and_value( re->re_mods[ i ].mi_type,
+	    obuf = ldif_put( LDIF_PUT_VALUE,
+			re->re_mods[ i ].mi_type,
 		    re->re_mods[ i ].mi_val ? re->re_mods[ i ].mi_val : "",
 		    re->re_mods[ i ].mi_len );
 	    if ( fputs( obuf, fp ) < 0 ) {
@@ -571,7 +645,7 @@ Re_write(
 		free( obuf );
 		goto bad;
 	    } else {
-		free( obuf );
+		ber_memfree( obuf );
 	    }
 	}
     }
@@ -585,8 +659,13 @@ Re_write(
     }
 bad:
     if ( rc != 0 ) {
+#ifdef NEW_LOGGING
+	LDAP_LOG (( "re", LDAP_LEVEL_ERR, 
+		"Re_write: Error while writing: %s\n", sys_errlist[ errno ] ));
+#else
 	Debug( LDAP_DEBUG_ANY, "Error while writing: %s\n",
 		sys_errlist[ errno ], 0, 0 );
+#endif
     }
     return rc;
 }
@@ -638,7 +717,7 @@ Re_lock(
     Re	*re
 )
 {
-    return( pthread_mutex_lock( &re->re_mutex ));
+    return( ldap_pvt_thread_mutex_lock( &re->re_mutex ));
 }
 
 
@@ -652,7 +731,7 @@ Re_unlock(
     Re	*re
 )
 {
-    return( pthread_mutex_unlock( &re->re_mutex ));
+    return( ldap_pvt_thread_mutex_unlock( &re->re_mutex ));
 }
 
 
@@ -685,7 +764,7 @@ Re_init(
 
     /* Initialize private data */
    (*re)->re_refcnt = sglob->num_replicas;
-   (*re)->re_timestamp = NULL;
+   (*re)->re_timestamp = (time_t) 0L;
    (*re)->re_replicas = NULL;
    (*re)->re_dn = NULL;
    (*re)->re_changetype = 0;
@@ -693,7 +772,7 @@ Re_init(
    (*re)->re_mods = NULL;
    (*re)->re_next = NULL;
 
-   pthread_mutex_init( &((*re)->re_mutex), pthread_mutexattr_default );
+   ldap_pvt_thread_mutex_init( &((*re)->re_mutex) );
    return 0;
 }
 
@@ -723,9 +802,15 @@ warn_unknown_replica(
 	}
     }
     if ( !found ) {
+#ifdef NEW_LOGGING
+	LDAP_LOG (( "re", LDAP_LEVEL_WARNING, "warn_unknown_replica: "
+		"Warning: unknown replica %s:%d found in replication log\n",
+		host, port ));
+#else
 	Debug( LDAP_DEBUG_ANY,
 		"Warning: unknown replica %s:%d found in replication log\n",
 		host, port, 0 );
+#endif
 	nur++;
 	ur = (Rh *) ch_realloc( (char *) ur, ( nur * sizeof( Rh )));
 	ur[ nur - 1 ].rh_hostname = strdup( host );
diff --git a/servers/slurpd/reject.c b/servers/slurpd/reject.c
index 8ae0dc63635728ca298566a8cced943f1ff4d134..d3346cf50902bd53205307c82ea02e22b170ee93 100644
--- a/servers/slurpd/reject.c
+++ b/servers/slurpd/reject.c
@@ -1,3 +1,8 @@
+/* $OpenLDAP$ */
+/*
+ * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
 /*
  * Copyright (c) 1996 Regents of the University of Michigan.
  * All rights reserved.
@@ -17,21 +22,19 @@
  * to a replica LDAP server.
  */
 
+#include "portable.h"
 
 #include <stdio.h>
-#include <sys/types.h>
+
+#include <ac/errno.h>
+#include <ac/unistd.h>
+
 #include <sys/stat.h>
 #include <fcntl.h>
-#include <unistd.h>
 
 #include "slurp.h"
 #include "globals.h"
 
-#ifndef SYSERRLIST_IN_STDIO
-extern char *sys_errlist[];
-#endif /* SYSERRLIST_IN_STDIO */
-
-
 /*
  * Write a replication record to a reject file.  The reject file has the
  * same name as the replica's private copy of the file but with ".rej"
@@ -52,7 +55,7 @@ write_reject(
     FILE	*rfp, *lfp;
     int		rc;
 
-    pthread_mutex_lock( &sglob->rej_mutex );
+    ldap_pvt_thread_mutex_lock( &sglob->rej_mutex );
     sprintf( rejfile, "%s/%s:%d.rej", sglob->slurpd_rdir,
 	    ri->ri_hostname, ri->ri_port );
 
@@ -61,18 +64,29 @@ write_reject(
 	int rjfd;
 	if (( rjfd = open( rejfile, O_RDWR | O_APPEND | O_CREAT,
 		S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP )) < 0 ) {
+#ifdef NEW_LOGGING
+		LDAP_LOG (( "reject", LDAP_LEVEL_ERR, "write_reject: "
+			"Error: Cannot create \"%s\":%s\n", 
+			rejfile, sys_errlist[ errno ] ));
+#else
 	    Debug( LDAP_DEBUG_ANY,
 		"Error: write_reject: Cannot create \"%s\": %s\n",
 		rejfile, sys_errlist[ errno ], 0 );
-	    pthread_mutex_unlock( &sglob->rej_mutex );
+#endif
+	    ldap_pvt_thread_mutex_unlock( &sglob->rej_mutex );
 	    return;
 	} else {
 	    close( rjfd );
 	}
     }
     if (( rc = acquire_lock( rejfile, &rfp, &lfp )) < 0 ) {
+#ifdef NEW_LOGGING
+	LDAP_LOG (( "reject", LDAP_LEVEL_ERR, "write_reject: "
+		"Error: Cannot open reject file \"%s\"\n", rejfile ));
+#else
 	Debug( LDAP_DEBUG_ANY, "Error: cannot open reject file \"%s\"\n",
 		rejfile, 0, 0 );
+#endif
     } else {
 	fseek( rfp, 0, 2 );
 	if ( errmsg != NULL ) {
@@ -81,16 +95,26 @@ write_reject(
 	    fprintf( rfp, "%s: %s\n", ERROR_STR, ldap_err2string( lderr ));
 	}
 	if ((rc = re->re_write( ri, re, rfp )) < 0 ) {
+#ifdef NEW_LOGGING
+		LDAP_LOG (( "reject", LDAP_LEVEL_ERR, "write_reject: "
+			"Error: Cannot write reject file \"%s\"\n", rejfile ));
+#else
 	    Debug( LDAP_DEBUG_ANY,
 		    "Error: cannot write reject file \"%s\"\n",
 		    rejfile, 0, 0 );
+#endif
 	}
 	(void) relinquish_lock( rejfile, rfp, lfp );
+#ifdef NEW_LOGGING
+	LDAP_LOG (( "reject", LDAP_LEVEL_ERR, "write_reject: "
+		"Error: ldap operation failed, data written to \"%s\"\n", rejfile ));
+#else
 	Debug( LDAP_DEBUG_ANY,
 		"Error: ldap operation failed, data written to \"%s\"\n",
 		rejfile, 0, 0 );
+#endif
     }
-    pthread_mutex_unlock( &sglob->rej_mutex );
+    ldap_pvt_thread_mutex_unlock( &sglob->rej_mutex );
     return;
 }
 
diff --git a/servers/slurpd/replica.c b/servers/slurpd/replica.c
index ed25a629077031feed19d6dee387adaea5b66f1b..9bb77ca8ef7dfcc190dec778c33aca82d8c4120f 100644
--- a/servers/slurpd/replica.c
+++ b/servers/slurpd/replica.c
@@ -1,3 +1,8 @@
+/* $OpenLDAP$ */
+/*
+ * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
 /*
  * Copyright (c) 1996 Regents of the University of Michigan.
  * All rights reserved.
@@ -15,6 +20,7 @@
  * replica.c - code to start up replica threads.
  */
 
+#include "portable.h"
 
 #include <stdio.h>
 
@@ -26,22 +32,33 @@
  * Just invoke the Ri's process() member function, and log the start and
  * finish.
  */
-void
+static void *
 replicate(
-    Ri	*ri
+    void	*ri_arg
 )
 {
-    int i;
-    unsigned long seq;
+    Ri		*ri = (Ri *) ri_arg;
 
+#ifdef NEW_LOGGING
+	LDAP_LOG (( "replica", LDAP_LEVEL_ARGS, "replicate: "
+		"begin replication thread for %s:%d\n",
+	    ((Ri *)ri)->ri_hostname, ((Ri *)ri)->ri_port ));
+#else
     Debug( LDAP_DEBUG_ARGS, "begin replication thread for %s:%d\n",
-	    ri->ri_hostname, ri->ri_port, 0 );
+	    ((Ri *)ri)->ri_hostname, ((Ri *)ri)->ri_port, 0 );
+#endif
 
     ri->ri_process( ri );
 
+#ifdef NEW_LOGGING
+	LDAP_LOG (( "replica", LDAP_LEVEL_ARGS, "replicate: "
+		"begin replication thread for %s:%d\n",
+	    ri->ri_hostname, ri->ri_port ));
+#else
     Debug( LDAP_DEBUG_ARGS, "end replication thread for %s:%d\n",
 	    ri->ri_hostname, ri->ri_port, 0 );
-    return;
+#endif
+    return NULL;
 }
 
 
@@ -54,18 +71,19 @@ start_replica_thread(
     Ri	*ri
 )
 {
-    pthread_attr_t	attr;
-
-    pthread_attr_init( &attr );
-    pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_DETACHED );
-
-    if ( pthread_create( &(ri->ri_tid), attr, (void *) replicate,
+    /* POSIX_THREADS or compatible */
+    if ( ldap_pvt_thread_create( &(ri->ri_tid), 0, replicate,
 	    (void *) ri ) != 0 ) {
-	Debug( LDAP_DEBUG_ANY, "replica \"%s:%d\" pthread_create failed\n",
+#ifdef NEW_LOGGING
+	LDAP_LOG (( "replica", LDAP_LEVEL_ERR, "start_replica_thread: "
+		"replica %s:%d ldap_pvt_thread_create failed\n",
+	    ri->ri_hostname, ri->ri_port ));
+#else
+	Debug( LDAP_DEBUG_ANY, "replica \"%s:%d\" ldap_pvt_thread_create failed\n",
 		ri->ri_hostname, ri->ri_port, 0 );
-	pthread_attr_destroy( &attr );
+#endif
 	return -1;
     }
-    pthread_attr_destroy( &attr );
+
     return 0;
 }
diff --git a/servers/slurpd/replog.c b/servers/slurpd/replog.c
index c3a855cffecbc626a00d2c67b443c74213c40e73..a93f9040d50b194570c85bcc2e8d16e59a3265eb 100644
--- a/servers/slurpd/replog.c
+++ b/servers/slurpd/replog.c
@@ -1,3 +1,8 @@
+/* $OpenLDAP$ */
+/*
+ * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
 /*
  * Copyright (c) 1996 Regents of the University of Michigan.
  * All rights reserved.
@@ -15,55 +20,24 @@
  * replog.c - routines which read and write replication log files.
  */
 
+#include "portable.h"
 
-#include <errno.h>
 #include <stdio.h>
-#include <syslog.h>
-#include <sys/time.h>
-#include <sys/types.h>
+
+#include <ac/errno.h>
+#include <ac/param.h>
+#include <ac/string.h>
+#include <ac/syslog.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+
 #include <sys/stat.h>
-#include <sys/param.h>
+
 #include <fcntl.h>
-#include <unistd.h>
-#include <string.h>
 
-#include "portable.h"
 #include "slurp.h"
 #include "globals.h"
 
-/*
- * Externs
- */
-#ifdef NEEDPROTOS
-extern FILE *lock_fopen( char *, char *, FILE ** );
-extern char *ch_malloc( unsigned long );
-#else /* NEEDPROTOS */
-extern FILE *lock_fopen();
-extern char *ch_malloc();
-#endif /* NEEDPROTOS */
-
-/*
- * Forward declarations
- */
-#ifdef NEEDPROTOS
-int file_nonempty( char * );
-#else /* NEEDPROTOS */
-int file_nonempty();
-#endif /* NEEDPROTOS */
-
-
-#ifndef SYSERRLIST_IN_STDIO
-extern char *sys_errlist[];
-#endif
-
-/*
- * Forward declarations
- */
-static int duplicate_replog( char *, char * );
-
-
-
-
 /*
  * Copy the replication log.  Returns 0 on success, 1 if a temporary
  * error occurs, and -1 if a fatal error occurs.
@@ -83,9 +57,14 @@ copy_replog(
     static char	rbuf[ 1024 ];
     char	*p;
 
+#ifdef NEW_LOGGING
+	LDAP_LOG (( "replog", LDAP_LEVEL_ARGS, "copy_replog: "
+		"copy replog \"%s\" to \"%s\"\n", src, dst ));
+#else
     Debug( LDAP_DEBUG_ARGS,
 	    "copy replog \"%s\" to \"%s\"\n", 
 	    src, dst, 0 );
+#endif
 
     /*
      * Make sure the destination directory is writable.  If not, exit
@@ -98,9 +77,15 @@ copy_replog(
 	*p = '\0';
     }
     if ( access( buf, W_OK ) < 0 ) {
+#ifdef NEW_LOGGING
+	LDAP_LOG (( "replog", LDAP_LEVEL_ERR, "copy_replog: "
+		"Error: (%ld): Directory %s is not writable\n",
+		(long) getpid(), buf ));
+#else
 	Debug( LDAP_DEBUG_ANY,
-		"Error: copy_replog (%d): Directory %s is not writable\n",
-		getpid(), buf, 0 );
+		"Error: copy_replog (%ld): Directory %s is not writable\n",
+		(long) getpid(), buf, 0 );
+#endif
 	return( -1 );
     }
     strcpy( buf, dst );
@@ -110,28 +95,46 @@ copy_replog(
 	*p = '\0';
     }
     if ( access( buf, W_OK ) < 0 ) {
+#ifdef NEW_LOGGING
+	LDAP_LOG (( "replog", LDAP_LEVEL_ERR, "copy_replog: "
+		"Error: (%ld): Directory %s is not writable\n",
+		(long) getpid(), buf ));
+#else
 	Debug( LDAP_DEBUG_ANY,
-		"Error: copy_replog (%d): Directory %s is not writable\n",
-		getpid(), buf, 0 );
+		"Error: copy_replog (%ld): Directory %s is not writable\n",
+		(long) getpid(), buf, 0 );
+#endif
 	return( -1 );
     }
 
     /* lock src */
     rfp = lock_fopen( src, "r", &lfp );
     if ( rfp == NULL ) {
+#ifdef NEW_LOGGING
+	LDAP_LOG (( "replog", LDAP_LEVEL_ERR, "copy_replog: "
+		"Error: Can't lock replog \"%s\" for read: %s\n",
+		src, sys_errlist[ errno ] ));
+#else
 	Debug( LDAP_DEBUG_ANY,
 		"Error: copy_replog: Can't lock replog \"%s\" for read: %s\n",
 		src, sys_errlist[ errno ], 0 );
+#endif
 	return( 1 );
     }
 
     /* lock dst */
     dfp = lock_fopen( dst, "a", &dlfp );
     if ( dfp == NULL ) {
+#ifdef NEW_LOGGING
+	LDAP_LOG (( "replog", LDAP_LEVEL_ERR, "copy_replog: "
+		"Error: Can't lock replog \"%s\" for write: %s\n",
+		src, sys_errlist[ errno ] ));
+#else
 	Debug( LDAP_DEBUG_ANY,
 		"Error: copy_replog: Can't lock replog \"%s\" for write: %s\n",
 		src, sys_errlist[ errno ], 0 );
-	lock_fclose( rfp );
+#endif
+	lock_fclose( rfp, lfp );
 	return( 1 );
     }
 
@@ -147,15 +150,25 @@ copy_replog(
 	truncate( src, (off_t) 0 );
     }
 
-    if ( lock_fclose( rfp, lfp ) == EOF ) {
+    if ( lock_fclose( dfp, dlfp ) == EOF ) {
+#ifdef NEW_LOGGING
+	LDAP_LOG (( "replog", LDAP_LEVEL_ERR, "copy_replog: "
+		"Error: Error closing \"%s\"\n", src ));
+#else
 	Debug( LDAP_DEBUG_ANY,
 		"Error: copy_replog: Error closing \"%s\"\n",
 		src, 0, 0 );
+#endif
     }
-    if ( lock_fclose( dfp, dlfp ) == EOF ) {
+    if ( lock_fclose( rfp, lfp ) == EOF ) {
+#ifdef NEW_LOGGING
+	LDAP_LOG (( "replog", LDAP_LEVEL_ERR, "copy_replog: "
+		"Error: Error closing \"%s\"\n", src ));
+#else
 	Debug( LDAP_DEBUG_ANY,
 		"Error: copy_replog: Error closing \"%s\"\n",
 		src, 0, 0 );
+#endif
     }
     return( rc );
 }
diff --git a/servers/slurpd/ri.c b/servers/slurpd/ri.c
index a805060a98f0672683e25c0127854154cd7d09a5..5dbdaf855d6e53f3d233407c21202ed89f2e6e5a 100644
--- a/servers/slurpd/ri.c
+++ b/servers/slurpd/ri.c
@@ -55,7 +55,12 @@ Ri_process(
     (void) SIGNAL( LDAP_SIGUSR1, do_nothing );
     (void) SIGNAL( SIGPIPE, SIG_IGN );
     if ( ri == NULL ) {
+#ifdef NEW_LOGGING
+	LDAP_LOG (( "ri", LDAP_LEVEL_ERR, "Ri_process: "
+		"Error: ri == NULL!\n" ));
+#else
 	Debug( LDAP_DEBUG_ANY, "Error: Ri_process: ri == NULL!\n", 0, 0, 0 );
+#endif
 	return -1;
     }
 
@@ -85,22 +90,40 @@ Ri_process(
 	if ( re != NULL ) {
 	    if ( !ismine( ri, re )) {
 		/* The Re doesn't list my host:port */
+#ifdef NEW_LOGGING
+		LDAP_LOG (( "ri", LDAP_LEVEL_DETAIL1, "Ri_process: "
+			"Replica %s:%d, skip repl record for %s (not mine)\n",
+			ri->ri_hostname, ri->ri_port, re->re_dn ));
+#else
 		Debug( LDAP_DEBUG_TRACE,
 			"Replica %s:%d, skip repl record for %s (not mine)\n",
 			ri->ri_hostname, ri->ri_port, re->re_dn );
+#endif
 	    } else if ( !isnew( ri, re )) {
 		/* This Re is older than my saved status information */
+#ifdef NEW_LOGGING
+		LDAP_LOG (( "ri", LDAP_LEVEL_DETAIL1, "Ri_process: "
+			"Replica %s:%d, skip repl record for %s (old)\n",
+			ri->ri_hostname, ri->ri_port, re->re_dn ));
+#else
 		Debug( LDAP_DEBUG_TRACE,
 			"Replica %s:%d, skip repl record for %s (old)\n",
 			ri->ri_hostname, ri->ri_port, re->re_dn );
+#endif
 	    } else {
 		rc = do_ldap( ri, re, &errmsg );
 		switch ( rc ) {
 		case DO_LDAP_ERR_RETRYABLE:
 		    ldap_pvt_thread_sleep( RETRY_SLEEP_TIME );
+#ifdef NEW_LOGGING
+			LDAP_LOG (( "ri", LDAP_LEVEL_DETAIL1, "Ri_process: "
+				"Retrying operation for DN %s on replica %s:%d\n",
+			    re->re_dn, ri->ri_hostname, ri->ri_port ));
+#else
 		    Debug( LDAP_DEBUG_ANY,
 			    "Retrying operation for DN %s on replica %s:%d\n",
 			    re->re_dn, ri->ri_hostname, ri->ri_port );
+#endif
 		    continue;
 		    break;
 		case DO_LDAP_ERR_FATAL: {
@@ -122,8 +145,13 @@ Ri_process(
 		}
 	    }
 	} else {
+#ifdef NEW_LOGGING
+		LDAP_LOG (( "ri", LDAP_LEVEL_ERR, "Ri_process: "
+			"Error: re is null in Ri_process\n" ));
+#else
 	    Debug( LDAP_DEBUG_ANY, "Error: re is null in Ri_process\n",
 		    0, 0, 0 );
+#endif
 	}
 	rq->rq_lock( rq );
 	while ( !sglob->slurpd_shutdown &&
diff --git a/servers/slurpd/rq.c b/servers/slurpd/rq.c
index 54adb4da1af77e4fd26db6da62d375ea781e94c6..37098c9f3649e23aa29a1a943c8d63350d922039 100644
--- a/servers/slurpd/rq.c
+++ b/servers/slurpd/rq.c
@@ -1,3 +1,8 @@
+/* $OpenLDAP$ */
+/*
+ * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
 /*
  * Copyright (c) 1996 Regents of the University of Michigan.
  * All rights reserved.
@@ -31,21 +36,24 @@
  *
  */
 
-#include <stdio.h>
-
-#include "slurp.h"
-#include "globals.h"
+#include "portable.h"
 
+#include <stdio.h>
+#include <sys/stat.h>
 
-/* externs */
-#ifdef NEEDPROTOS
-extern void Re_dump( Re *re );
-#else /* NEEDPROTOS */
-extern void Re_dump();
-#endif /* NEEDPROTOS */
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/unistd.h>		/* get ftruncate() */
 
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
 
-extern char *sys_errlist[];
+#include "slurp.h"
+#include "globals.h"
 
 
 /*
@@ -56,13 +64,10 @@ Rq_lock(
     Rq	*rq
 )
 {
-    return( pthread_mutex_lock( &rq->rq_mutex ));
+    return( ldap_pvt_thread_mutex_lock( &rq->rq_mutex ));
 }
 
 
-
-
-
 /*
  * Unlock the replication queue.
  */
@@ -71,7 +76,7 @@ Rq_unlock(
     Rq	*rq
 )
 {
-    return( pthread_mutex_unlock( &rq->rq_mutex ));
+    return( ldap_pvt_thread_mutex_unlock( &rq->rq_mutex ));
 }
 
 
@@ -89,8 +94,6 @@ Rq_gethead(
 }
 
 
-
-
 /*
  * Return the next item in the queue.  Callers should lock the queue before
  * calling this routine.
@@ -108,8 +111,6 @@ Rq_getnext(
 }
 
 
-
-
 /*
  * Delete the item at the head of the list.  The queue should be locked
  * by the caller before calling this routine.
@@ -132,8 +133,13 @@ Rq_delhead(
     }
 
     if ( savedhead->re_getrefcnt( savedhead ) != 0 ) {
+#ifdef NEW_LOGGING
+	LDAP_LOG (( "rq", LDAP_LEVEL_WARNING, "Rq_delhead: "
+		"Warning: attempt to delete when refcnt != 0\n" ));
+#else
 	Debug( LDAP_DEBUG_ANY, "Warning: attempt to delete when refcnt != 0\n",
 		0, 0, 0 );
+#endif
 	return( -1 );
     }
 
@@ -144,8 +150,6 @@ Rq_delhead(
 }
 
 
-
-
 /* 
  * Add an entry to the tail of the replication queue.  Locking is handled
  * internally.  When items are added to the queue, this routine wakes
@@ -188,7 +192,7 @@ Rq_add(
 
     /* set the sequence number */
     re->re_seq = 0;
-    if ( !wasempty && !strcmp(rq->rq_tail->re_timestamp, re->re_timestamp )) {
+    if ( !wasempty && ( rq->rq_tail->re_timestamp == re->re_timestamp )) {
 	/*
 	 * Our new re has the same timestamp as the tail's timestamp.
 	 * Increment the seq number in the tail and use it as our seq number.
@@ -200,7 +204,7 @@ Rq_add(
     /* Increment count of items in queue */
     rq->rq_nre++;
     /* wake up any threads waiting for more work */
-    pthread_cond_broadcast( &rq->rq_more );
+    ldap_pvt_thread_cond_broadcast( &rq->rq_more );
 
     /* ... and unlock the queue */
     rq->rq_unlock( rq );
@@ -209,8 +213,6 @@ Rq_add(
 }
 
 
-
-
 /*
  * Garbage-collect the replication queue.  Locking is handled internally.
  */
@@ -220,7 +222,11 @@ Rq_gc(
 )
 {
     if ( rq == NULL ) {
+#ifdef NEW_LOGGING
+	LDAP_LOG (( "rq", LDAP_LEVEL_DETAIL1, "Rq_gc: rq is NULL!\n" ));
+#else
 	Debug( LDAP_DEBUG_ANY, "Rq_gc: rq is NULL!\n", 0, 0, 0 );
+#endif
 	return;
     }
     rq->rq_lock( rq ); 
@@ -234,7 +240,6 @@ Rq_gc(
 }
 
 
-
 /*
  * For debugging: dump the contents of the replication queue to a file.
  * Locking is handled internally.
@@ -246,15 +251,45 @@ Rq_dump(
 {
     Re		*re;
     FILE	*fp;
+    int		tmpfd;
 
     if ( rq == NULL ) {
+#ifdef NEW_LOGGING
+	LDAP_LOG (( "rq", LDAP_LEVEL_ARGS, "Rq_dump: rq is NULL!\n" ));
+#else
 	Debug( LDAP_DEBUG_ANY, "Rq_dump: rq is NULL!\n", 0, 0, 0 );
+#endif
 	return;
     }
 
-    if (( fp = fopen( SLURPD_DUMPFILE, "w" )) == NULL ) {
+    if (unlink(SLURPD_DUMPFILE) == -1 && errno != ENOENT) {
+#ifdef NEW_LOGGING
+	LDAP_LOG (( "rq", LDAP_LEVEL_ERR, "Rq_dump: "
+		"\"%s\" exists, cannot unlink\n", SLURPD_DUMPFILE ));
+#else
+	Debug( LDAP_DEBUG_ANY, "Rq_dump: \"%s\" exists, and cannot unlink\n",
+		SLURPD_DUMPFILE, 0, 0 );
+#endif
+	return;
+    }
+    if (( tmpfd = open(SLURPD_DUMPFILE, O_CREAT|O_RDWR|O_EXCL, 0600)) == -1) {
+#ifdef NEW_LOGGING
+	LDAP_LOG (( "rq", LDAP_LEVEL_ERR, "Rq_dump: "
+		"cannot open \"%s\" for write\n", SLURPD_DUMPFILE ));
+#else
 	Debug( LDAP_DEBUG_ANY, "Rq_dump: cannot open \"%s\" for write\n",
 		SLURPD_DUMPFILE, 0, 0 );
+#endif
+	return;
+    }
+    if (( fp = fdopen( tmpfd, "w" )) == NULL ) {
+#ifdef NEW_LOGGING
+	LDAP_LOG (( "rq", LDAP_LEVEL_ERR, "Rq_dump: "
+		"cannot fdopen \"%s\" for write\n", SLURPD_DUMPFILE ));
+#else
+	Debug( LDAP_DEBUG_ANY, "Rq_dump: cannot fdopen \"%s\" for write\n",
+		SLURPD_DUMPFILE, 0, 0 );
+#endif
 	return;
     }
 
@@ -268,7 +303,6 @@ Rq_dump(
 }
 
 
-
 /*
  * Write the contents of a replication queue to a file.  Returns zero if
  * successful, -1 if not.  Handles queue locking internally.  Callers should
@@ -287,8 +321,13 @@ Rq_write(
 	return -1;
     }
 
+#ifdef NEW_LOGGING
+	LDAP_LOG (( "rq", LDAP_LEVEL_ENTRY, "Rq_write: "
+		"re-write on-disk replication log\n" ));
+#else
     Debug( LDAP_DEBUG_ARGS, "re-write on-disk replication log\n",
 	    0, 0, 0 );
+#endif
 #ifndef SEEK_SET
 #define SEEK_SET 0
 #endif
@@ -306,8 +345,13 @@ Rq_write(
     sglob->srpos = ftell( fp );	/* update replog file position */
     /* and truncate to correct len */
     if ( ftruncate( fileno( fp ), sglob->srpos ) < 0 ) {
+#ifdef NEW_LOGGING
+	LDAP_LOG (( "rq", LDAP_LEVEL_ERR, "Rq_write: "
+		"Error truncating replication log: %s\n", sys_errlist[ errno ] ));
+#else
 	Debug( LDAP_DEBUG_ANY, "Error truncating replication log: %s\n",
 		sys_errlist[ errno ], 0, 0 );
+#endif
     }
     rq->rq_ndel = 0;	/* reset count of deleted re's */
     time( &now );
@@ -317,8 +361,6 @@ Rq_write(
 }
 
 
-
-
 /*
  * Check to see if the private slurpd replication log needs trimming.
  * The current criteria are:
@@ -335,8 +377,6 @@ Rq_needtrim(
 )
 {
     int		rc = 0;
-    Re		*re;
-    int		nzrc = 0;	/* nzrc is count of entries with refcnt == 0 */
     time_t	now;
 
     if ( rq == NULL ) {
@@ -380,7 +420,7 @@ Rq_getcount(
 	for ( re = rq->rq_gethead( rq ); re != NULL;
 		re = rq->rq_getnext( re )) {
 	    if ( type == RQ_COUNT_NZRC ) {
-		if ( re->re_getrefcnt( re ) > 1 ) {
+		if ( re->re_getrefcnt( re ) > 0 ) {
 		    count++;
 		}
 	    }
@@ -391,8 +431,6 @@ Rq_getcount(
 }
 
 
-
-
 /* 
  * Allocate and initialize an Rq object.
  */
@@ -421,8 +459,8 @@ Rq_init(
     (*rq)->rq_getcount = Rq_getcount;
 
     /* Initialize private data */
-    pthread_mutex_init( &((*rq)->rq_mutex), pthread_mutexattr_default );
-    pthread_cond_init( &((*rq)->rq_more), pthread_condattr_default );
+    ldap_pvt_thread_mutex_init( &((*rq)->rq_mutex) );
+    ldap_pvt_thread_cond_init( &((*rq)->rq_more) );
     (*rq)->rq_head = NULL;
     (*rq)->rq_tail = NULL;
     (*rq)->rq_nre = 0;
@@ -431,4 +469,3 @@ Rq_init(
 
     return 0;
 }
-
diff --git a/servers/slurpd/st.c b/servers/slurpd/st.c
index 610b29585342e2b4205d82ed080288b4448f2934..ffe150ea241b505aa705b89e42a3e39f5a164e49 100644
--- a/servers/slurpd/st.c
+++ b/servers/slurpd/st.c
@@ -1,3 +1,8 @@
+/* $OpenLDAP$ */
+/*
+ * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
 /*
  * Copyright (c) 1996 Regents of the University of Michigan.
  * All rights reserved.
@@ -16,19 +21,17 @@
  * writing status information to disk.
  */
 
-
+#include "portable.h"
 
 #include <stdio.h>
-#include <string.h>
-#include <unistd.h>
+
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
 
 #include "slurp.h"
 #include "globals.h"
 
-#ifndef SYSERRLIST_IN_STDIO
-extern char *sys_errlist[];
-#endif /* SYSERRLIST_IN_STDIO */
-
 /*
  * Add information about replica host specified by Ri to list
  * of hosts.
@@ -46,29 +49,28 @@ St_add(
     }
 
     /* Serialize access to the St struct */
-    pthread_mutex_lock( &(st->st_mutex ));
+    ldap_pvt_thread_mutex_lock( &(st->st_mutex ));
 
     st->st_nreplicas++;
     ind = st->st_nreplicas - 1;
     st->st_data = ( Stel ** ) ch_realloc( st->st_data, 
 	    ( st->st_nreplicas * sizeof( Stel * )));
     if ( st->st_data == NULL ) {
-	pthread_mutex_unlock( &(st->st_mutex ));
+	ldap_pvt_thread_mutex_unlock( &(st->st_mutex ));
 	return NULL;
     }
-    st->st_data[ ind ]  = ( Stel * ) ch_malloc( st->st_data,
-	    sizeof( Stel ));
+    st->st_data[ ind ]  = ( Stel * ) ch_malloc( sizeof( Stel ) );
     if ( st->st_data[ ind ] == NULL ) {
-	pthread_mutex_unlock( &(st->st_mutex ));
+	ldap_pvt_thread_mutex_unlock( &(st->st_mutex ));
 	return NULL;
     }
 
     st->st_data[ ind ]->hostname = strdup( ri->ri_hostname );
     st->st_data[ ind ]->port = ri->ri_port;
-    memset( st->st_data[ ind ]->last, 0, sizeof( st->st_data[ ind ]->last )); 
+    st->st_data[ ind ]->last = 0; 
     st->st_data[ ind ]->seq = 0;
 
-    pthread_mutex_unlock( &(st->st_mutex ));
+    ldap_pvt_thread_mutex_unlock( &(st->st_mutex ));
     return st->st_data[ ind ];
 }
 
@@ -89,17 +91,23 @@ St_write (
     if ( st == NULL ) {
 	return -1;
     }
-    pthread_mutex_lock( &(st->st_mutex ));
+    ldap_pvt_thread_mutex_lock( &(st->st_mutex ));
     if ( st->st_fp == NULL ) {
 	/* Open file */
 	if (( rc = acquire_lock( sglob->slurpd_status_file, &(st->st_fp),
 		&(st->st_lfp))) < 0 ) {
 	    if ( !st->st_err_logged ) {
+#ifdef NEW_LOGGING
+		LDAP_LOG (( "st", LDAP_LEVEL_ERR, "St_write: "
+			"Error: cannot open status file \"%s\":%s\n",
+			sglob->slurpd_status_file, sys_errlist[ errno ] ));
+#else
 		Debug( LDAP_DEBUG_ANY,
 			"Error: cannot open status file \"%s\": %s\n",
 			sglob->slurpd_status_file, sys_errlist[ errno ], 0 );
+#endif
 		st->st_err_logged = 1;
-		pthread_mutex_unlock( &(st->st_mutex ));
+		ldap_pvt_thread_mutex_unlock( &(st->st_mutex ));
 		return -1;
 	    }
 	} else {
@@ -112,12 +120,13 @@ St_write (
     fseek( st->st_fp, 0L, 0 );
     for ( i = 0; i < st->st_nreplicas; i++ ) {
 	stel = st->st_data[ i ];
-	fprintf( st->st_fp, "%s:%d:%s:%d\n", stel->hostname, stel->port,
-		stel->last, stel->seq );
+	fprintf( st->st_fp, "%s:%d:%ld:%d\n",
+		stel->hostname, stel->port,
+		(long) stel->last, stel->seq );
     }
     fflush( st->st_fp );
 
-    pthread_mutex_unlock( &(st->st_mutex ));
+    ldap_pvt_thread_mutex_unlock( &(st->st_mutex ));
 
     return 0;
 }
@@ -139,10 +148,10 @@ St_update(
 	return -1;
     }
 
-    pthread_mutex_lock( &(st->st_mutex ));
-    strcpy( stel->last, re->re_timestamp );
+    ldap_pvt_thread_mutex_lock( &(st->st_mutex ));
+    stel->last = re->re_timestamp;
     stel->seq = re->re_seq;
-    pthread_mutex_unlock( &(st->st_mutex ));
+    ldap_pvt_thread_mutex_unlock( &(st->st_mutex ));
     return 0;
 }
 
@@ -168,24 +177,36 @@ St_read(
     if ( st == NULL ) {
 	return -1;
     }
-    pthread_mutex_lock( &(st->st_mutex ));
+    ldap_pvt_thread_mutex_lock( &(st->st_mutex ));
     if ( access( sglob->slurpd_status_file, F_OK ) < 0 ) {
 	/*
 	 * File doesn't exist, so create it and return.
 	 */
 	if (( fp = fopen( sglob->slurpd_status_file, "w" )) == NULL ) {
+#ifdef NEW_LOGGING
+		LDAP_LOG (( "st", LDAP_LEVEL_ERR, "St_write: "
+			"Error: cannot create status file \"%s\"\n",
+		    sglob->slurpd_status_file ));
+#else
 	    Debug( LDAP_DEBUG_ANY, "Error: cannot create status file \"%s\"\n",
 		    sglob->slurpd_status_file, 0, 0 );
-	    pthread_mutex_unlock( &(st->st_mutex ));
+#endif
+	    ldap_pvt_thread_mutex_unlock( &(st->st_mutex ));
 	    return -1;
 	}
 	(void) fclose( fp );
-	pthread_mutex_unlock( &(st->st_mutex ));
+	ldap_pvt_thread_mutex_unlock( &(st->st_mutex ));
+#ifdef NEW_LOGGING
+	LDAP_LOG (( "st", LDAP_LEVEL_DETAIL1, "St_write: "
+		"No status file found, defaulting values\n" ));
+#else
 	Debug( LDAP_DEBUG_ARGS, "No status file found, defaulting values\n",
 		0, 0, 0 );
+#endif
 	return 0;
     }
     if (( rc = acquire_lock( sglob->slurpd_status_file, &fp, &lfp)) < 0 ) {
+	ldap_pvt_thread_mutex_unlock( &(st->st_mutex ));
 	return 0;
     }
     while ( fgets( buf, sizeof( buf ), fp ) != NULL ) {
@@ -217,7 +238,7 @@ St_read(
 	    if ( !strcmp( hostname, sglob->st->st_data[ i ]->hostname ) &&
 		    atoi( port ) == sglob->st->st_data[ i ]->port ) {
 		found = 1;
-		strcpy( sglob->st->st_data[ i ]->last, timestamp );
+		sglob->st->st_data[ i ]->last = atol( timestamp );
 		sglob->st->st_data[ i ]->seq = atoi( seq );
 		break;
 	    }
@@ -226,21 +247,32 @@ St_read(
 	    char tbuf[ 255 ];
 	    sprintf( tbuf, "%s:%s (timestamp %s.%s)", hostname, port,
 		    timestamp, seq );
+#ifdef NEW_LOGGING
+		LDAP_LOG (( "st", LDAP_LEVEL_DETAIL1, "St_write: "
+			"Retrieved state information for %s\n", tbuf ));
+#else
 	    Debug( LDAP_DEBUG_ARGS,
 		    "Retrieved state information for %s\n", tbuf, 0, 0 );
+#endif
 	} else {
+#ifdef NEW_LOGGING
+		LDAP_LOG (( "st", LDAP_LEVEL_WARNING, "St_write: "
+			"Warning: saved state for %s:%s, not a known replica\n", 
+			hostname, port ));
+#else
 	    Debug(  LDAP_DEBUG_ANY,
 		    "Warning: saved state for %s:%s, not a known replica\n",
 		    hostname, port, 0 );
+#endif
 	}
     }
     (void) relinquish_lock( sglob->slurpd_status_file, fp, lfp);
-    pthread_mutex_unlock( &(st->st_mutex ));
+    ldap_pvt_thread_mutex_unlock( &(st->st_mutex ));
     return 0;
 
 bad:
     (void) relinquish_lock( sglob->slurpd_status_file, fp, lfp);
-    pthread_mutex_unlock( &(st->st_mutex ));
+    ldap_pvt_thread_mutex_unlock( &(st->st_mutex ));
     return -1;
 }
     
@@ -255,7 +287,7 @@ St_lock(
     St *st
 )
 {
-    return( pthread_mutex_lock( &st->st_mutex ));
+    return( ldap_pvt_thread_mutex_lock( &st->st_mutex ));
 }
 
 
@@ -269,7 +301,7 @@ St_unlock(
     St *st
 )
 {
-    return( pthread_mutex_unlock( &st->st_mutex ));
+    return( ldap_pvt_thread_mutex_unlock( &st->st_mutex ));
 }
 
 
@@ -292,7 +324,7 @@ St_init(
 	return -1;
     }
 
-    pthread_mutex_init( &((*st)->st_mutex), pthread_mutexattr_default );
+    ldap_pvt_thread_mutex_init( &((*st)->st_mutex) );
     (*st)->st_data = NULL;
     (*st)->st_fp = NULL;
     (*st)->st_lfp = NULL;