diff --git a/CHANGES b/CHANGES
index 09152a5253d3ccbc5b8ddeeb285208a6a85c6e7b..0a85e4c4e88c4a3d1cf91d6b35f92c13ce9f7067 100644
--- a/CHANGES
+++ b/CHANGES
@@ -20,6 +20,7 @@ OpenLDAP 2.4.12 Engineering
 	Fixed slapd spurious text output (ITS#5688)
 	Fixed slapd socket closing on Windows (ITS#5606)
 	Fixed slapd sortvals comparison (ITS#5578)
+	Added slapd substitute syntax support (ITS#5663)
 	Fixed slapd syncrepl contextCSN detection (ITS#5675)
 	Fixed slapd syncrepl error logging (ITS#5618)
 	Fixed slapd-bdb entry return if attr not present (ITS#5650)
diff --git a/doc/man/man5/slapd.conf.5 b/doc/man/man5/slapd.conf.5
index 9267801bbd6943f39456f24bd1752e1254774c82..31412cbf706ac01dec0bc48582f8b20aa1e391ee 100644
--- a/doc/man/man5/slapd.conf.5
+++ b/doc/man/man5/slapd.conf.5
@@ -508,6 +508,37 @@ changing these settings will generally require deleting any indices that
 depend on these parameters and recreating them with
 .BR slapindex (8).
 
+.HP
+.hy 0
+.B ldapsyntax "(\ <oid>\
+ [DESC\ <description>]\
+ [X-SUBST <substitute\-syntax>]\ )"
+.RS
+Specify an LDAP syntax using the LDAPv3 syntax defined in RFC 4512.
+The slapd parser extends the RFC 4512 definition by allowing string
+forms as well as numeric OIDs to be used for the syntax OID.
+(See the
+.B objectidentifier
+description.) 
+The slapd parser also honors the
+.B X-SUBST
+extension (an OpenLDAP-specific extension), which allows to use the
+.B ldapsyntax
+statement to define a non-implemented syntax along with another syntax,
+the extension value
+.IR substitute\-syntax ,
+as its temporary replacement.
+The
+.I substitute\-syntax
+must be defined.
+This allows to define attribute types that make use of non-implemented syntaxes
+using the correct syntax OID.
+Unless 
+.B X-SUBST
+is used, this configuration statement would result in an error,
+since no handlers would be associated to the resulting syntax structure.
+.RE
+
 .TP
 .B localSSF <SSF>
 Specifies the Security Strength Factor (SSF) to be given local LDAP sessions,
diff --git a/servers/slapd/bconfig.c b/servers/slapd/bconfig.c
index f80330e1c743bc02d86610d654e902266b708deb..a241c0f1107d94536069377e2b6508a8de328e99 100644
--- a/servers/slapd/bconfig.c
+++ b/servers/slapd/bconfig.c
@@ -63,6 +63,7 @@ typedef struct ConfigFile {
 	ContentRule *c_cr_head, *c_cr_tail;
 	ObjectClass *c_oc_head, *c_oc_tail;
 	OidMacro *c_om_head, *c_om_tail;
+	Syntax *c_syn_head, *c_syn_tail;
 	BerVarray c_dseFiles;
 } ConfigFile;
 
@@ -87,7 +88,7 @@ static struct berval cfdir;
 
 /* Private state */
 static AttributeDescription *cfAd_backend, *cfAd_database, *cfAd_overlay,
-	*cfAd_include, *cfAd_attr, *cfAd_oc, *cfAd_om;
+	*cfAd_include, *cfAd_attr, *cfAd_oc, *cfAd_om, *cfAd_syntax;
 
 static ConfigFile *cfn;
 
@@ -97,9 +98,11 @@ static Avlnode *CfOcTree;
 extern AttributeType *at_sys_tail;	/* at.c */
 extern ObjectClass *oc_sys_tail;	/* oc.c */
 extern OidMacro *om_sys_tail;	/* oidm.c */
+extern Syntax *syn_sys_tail;	/* syntax.c */
 static AttributeType *cf_at_tail;
 static ObjectClass *cf_oc_tail;
 static OidMacro *cf_om_tail;
+static Syntax *cf_syn_tail;
 
 static int config_add_internal( CfBackInfo *cfb, Entry *e, ConfigArgs *ca,
 	SlapReply *rs, int *renumber, Operation *op );
@@ -180,6 +183,7 @@ enum {
 	CFG_SERVERID,
 	CFG_SORTVALS,
 	CFG_IX_INTLEN,
+	CFG_SYNTAX,
 
 	CFG_LAST
 };
@@ -381,6 +385,13 @@ static ConfigTable config_back_cf_table[] = {
 	{ "lastmod", "on|off", 2, 2, 0, ARG_DB|ARG_ON_OFF|ARG_MAGIC|CFG_LASTMOD,
 		&config_generic, "( OLcfgDbAt:0.4 NAME 'olcLastMod' "
 			"SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+	{ "ldapsyntax",	"syntax", 2, 0, 0,
+		ARG_PAREN|ARG_MAGIC|CFG_SYNTAX,
+		&config_generic, "( OLcfgGlAt:85 NAME 'olcLdapSyntaxes' "
+			"DESC 'OpenLDAP ldapSyntax' "
+			"EQUALITY caseIgnoreMatch "
+			"SYNTAX OMsDirectoryString X-ORDERED 'VALUES' )",
+				NULL, NULL },
 	{ "limits", "limits", 2, 0, 0, ARG_DB|ARG_MAGIC|CFG_LIMITS,
 		&config_generic, "( OLcfgDbAt:0.5 NAME 'olcLimits' "
 			"EQUALITY caseIgnoreMatch "
@@ -732,13 +743,13 @@ static ConfigOCs cf_ocs[] = {
 		 "olcTLSRandFile $ olcTLSVerifyClient $ olcTLSDHParamFile $ "
 		 "olcTLSCRLFile $ olcToolThreads $ "
 		 "olcObjectIdentifier $ olcAttributeTypes $ olcObjectClasses $ "
-		 "olcDitContentRules ) )", Cft_Global },
+		 "olcDitContentRules $ olcLdapSyntaxes ) )", Cft_Global },
 	{ "( OLcfgGlOc:2 "
 		"NAME 'olcSchemaConfig' "
 		"DESC 'OpenLDAP schema object' "
 		"SUP olcConfig STRUCTURAL "
 		"MAY ( cn $ olcObjectIdentifier $ olcAttributeTypes $ "
-		 "olcObjectClasses $ olcDitContentRules ) )",
+		 "olcObjectClasses $ olcDitContentRules $ olcLdapSyntaxes ) )",
 		 	Cft_Schema, NULL, cfAddSchema },
 	{ "( OLcfgGlOc:3 "
 		"NAME 'olcBackendConfig' "
@@ -927,6 +938,17 @@ config_generic(ConfigArgs *c) {
 				rc = 1;
 			}
 			break;
+		case CFG_SYNTAX: {
+			ConfigFile *cf = c->ca_private;
+			if ( !cf )
+				syn_unparse( &c->rvalue_vals, NULL, NULL, 1 );
+			else if ( cf->c_syn_head )
+				syn_unparse( &c->rvalue_vals, cf->c_syn_head,
+					cf->c_syn_tail, 0 );
+			if ( !c->rvalue_vals )
+				rc = 1;
+			}
+			break;
 		case CFG_DIT: {
 			ConfigFile *cf = c->ca_private;
 			if ( !cf )
@@ -1277,6 +1299,44 @@ config_generic(ConfigArgs *c) {
 				}
 			}
 			break;
+
+		case CFG_SYNTAX: {
+			CfEntryInfo *ce;
+			/* Can be NULL when undoing a failed add */
+			if ( c->ca_entry ) {
+				ce = c->ca_entry->e_private;
+				/* can't modify the hardcoded schema */
+				if ( ce->ce_parent->ce_type == Cft_Global )
+					return 1;
+				}
+			}
+			cfn = c->ca_private;
+			if ( c->valx < 0 ) {
+				Syntax *syn;
+
+				for( syn = cfn->c_syn_head; syn; syn_next( &syn )) {
+					syn_delete( syn );
+					if ( syn == cfn->c_syn_tail )
+						break;
+				}
+				cfn->c_syn_head = cfn->c_syn_tail = NULL;
+			} else {
+				Syntax *syn, *prev = NULL;
+
+				for ( i = 0, syn = cfn->c_syn_head; i < c->valx; i++) {
+					prev = syn;
+					syn_next( &syn );
+				}
+				syn_delete( syn );
+				if ( cfn->c_syn_tail == syn ) {
+					cfn->c_syn_tail = prev;
+				}
+				if ( cfn->c_syn_head == syn ) {
+					syn_next( &syn );
+					cfn->c_syn_head = syn;
+				}
+			}
+			break;
 		case CFG_SORTVALS:
 			if ( c->valx < 0 ) {
 				ADlist *sv;
@@ -1503,6 +1563,38 @@ config_generic(ConfigArgs *c) {
 			}
 			break;
 
+		case CFG_SYNTAX: {
+			Syntax *syn, *prev;
+
+			if ( c->op == LDAP_MOD_ADD && c->ca_private && cfn != c->ca_private )
+				cfn = c->ca_private;
+			if ( c->valx < 0 ) {
+				prev = cfn->c_syn_tail;
+			} else {
+				prev = NULL;
+				/* If adding anything after the first, prev is easy */
+				if ( c->valx ) {
+					int i;
+					for ( i = 0, syn = cfn->c_syn_head; i < c->valx; i++ ) {
+						prev = syn;
+						syn_next( &syn );
+					}
+				} else
+				/* If adding the first, and head exists, find its prev */
+					if (cfn->c_syn_head) {
+					for ( syn_start( &syn ); syn != cfn->c_syn_head; ) {
+						prev = syn;
+						syn_next( &syn );
+					}
+				}
+				/* else prev is NULL, append to end of global list */
+			}
+			if ( parse_syn( c, &syn, prev ) ) return(1);
+			if ( !cfn->c_syn_head ) cfn->c_syn_head = syn;
+			if ( cfn->c_syn_tail == prev ) cfn->c_syn_tail = syn;
+			}
+			break;
+
 		case CFG_DIT: {
 			ContentRule *cr;
 
@@ -4062,6 +4154,13 @@ schema_destroy_one( ConfigArgs *ca, ConfigOCs **colst, int nocs,
 		ct = config_find_table( colst, nocs, ad, ca );
 		config_del_vals( ct, ca );
 	}
+	if ( cfn->c_syn_head ) {
+		struct berval bv = BER_BVC("olcLdapSyntaxes");
+		ad = NULL;
+		slap_bv2ad( &bv, &ad, &text );
+		ct = config_find_table( colst, nocs, ad, ca );
+		config_del_vals( ct, ca );
+	}
 	if ( cfn->c_om_head ) {
 		struct berval bv = BER_BVC("olcObjectIdentifier");
 		ad = NULL;
@@ -5556,7 +5655,7 @@ config_build_schema_inc( ConfigArgs *c, CfEntryInfo *ceparent,
 
 	for (; cf; cf=cf->c_sibs, c->depth++) {
 		if ( !cf->c_at_head && !cf->c_cr_head && !cf->c_oc_head &&
-			!cf->c_om_head ) continue;
+			!cf->c_om_head && !cf->c_syn_head ) continue;
 		c->value_dn.bv_val = c->log;
 		LUTIL_SLASHPATH( cf->c_file.bv_val );
 		bv.bv_val = strrchr(cf->c_file.bv_val, LDAP_DIRSEP[0]);
@@ -5685,6 +5784,21 @@ config_check_schema(Operation *op, CfBackInfo *cfb)
 			ber_bvarray_free( bv );
 			cf_oc_tail = oc_sys_tail;
 		}
+		if ( cf_syn_tail != syn_sys_tail ) {
+			a = attr_find( e->e_attrs, cfAd_syntax );
+			if ( a ) {
+				if ( a->a_nvals != a->a_vals )
+					ber_bvarray_free( a->a_nvals );
+				ber_bvarray_free( a->a_vals );
+				a->a_vals = NULL;
+				a->a_nvals = NULL;
+				a->a_numvals = 0;
+			}
+			syn_unparse( &bv, NULL, NULL, 1 );
+			attr_merge_normalize( e, cfAd_syntax, bv, NULL );
+			ber_bvarray_free( bv );
+			cf_syn_tail = syn_sys_tail;
+		}
 	} else {
 		SlapReply rs = {REP_RESULT};
 		c.ca_private = NULL;
@@ -5698,6 +5812,7 @@ config_check_schema(Operation *op, CfBackInfo *cfb)
 		cf_at_tail = at_sys_tail;
 		cf_oc_tail = oc_sys_tail;
 		cf_om_tail = om_sys_tail;
+		cf_syn_tail = syn_sys_tail;
 	}
 	return 0;
 }
@@ -5789,6 +5904,7 @@ config_back_db_open( BackendDB *be, ConfigReply *cr )
 	cf_at_tail = at_sys_tail;
 	cf_oc_tail = oc_sys_tail;
 	cf_om_tail = om_sys_tail;
+	cf_syn_tail = syn_sys_tail;
 
 	/* Create schema nodes for included schema... */
 	if ( cfb->cb_config->c_kids ) {
@@ -6222,6 +6338,7 @@ static struct {
 	{ "backend", &cfAd_backend },
 	{ "database", &cfAd_database },
 	{ "include", &cfAd_include },
+	{ "ldapsyntax", &cfAd_syntax },
 	{ "objectclass", &cfAd_oc },
 	{ "objectidentifier", &cfAd_om },
 	{ "overlay", &cfAd_overlay },
diff --git a/servers/slapd/proto-slap.h b/servers/slapd/proto-slap.h
index ea2292199939f7973cc08053c8e5a21b35e00650..27aaee29517580dca543d31d448eab028164c33a 100644
--- a/servers/slapd/proto-slap.h
+++ b/servers/slapd/proto-slap.h
@@ -1699,6 +1699,8 @@ LDAP_SLAPD_F (int) parse_at LDAP_P((
 LDAP_SLAPD_F (char *) scherr2str LDAP_P((int code)) LDAP_GCCATTR((const));
 LDAP_SLAPD_F (int) dscompare LDAP_P(( const char *s1, const char *s2del,
 	char delim ));
+LDAP_SLAPD_F (int) parse_syn LDAP_P((
+	struct config_args_s *ca, Syntax **sat, Syntax *prev ));
 
 /*
  * sessionlog.c
@@ -1758,7 +1760,10 @@ LDAP_SLAPD_F (Syntax *) syn_find_desc LDAP_P((
 	const char *syndesc, int *slen ));
 LDAP_SLAPD_F (int) syn_add LDAP_P((
 	LDAPSyntax *syn,
+	int user,
 	slap_syntax_defs_rec *def,
+	Syntax **ssyn,
+	Syntax *prev,
 	const char **err ));
 LDAP_SLAPD_F (void) syn_destroy LDAP_P(( void ));
 
@@ -1767,6 +1772,13 @@ LDAP_SLAPD_F (int) register_syntax LDAP_P((
 
 LDAP_SLAPD_F (int) syn_schema_info( Entry *e );
 
+LDAP_SLAPD_F (int) syn_start LDAP_P(( Syntax **at ));
+LDAP_SLAPD_F (int) syn_next LDAP_P(( Syntax **at ));
+LDAP_SLAPD_F (void) syn_delete LDAP_P(( Syntax *at ));
+
+LDAP_SLAPD_F (void) syn_unparse LDAP_P((
+	BerVarray *bva, Syntax *start, Syntax *end, int system ));
+
 /*
  * user.c
  */
diff --git a/servers/slapd/schemaparse.c b/servers/slapd/schemaparse.c
index c02fdea215f2e38c16e84f3475f0bc8df79640d0..b6ffcc5fd6b3d1f11f8bd77d6a790e9eb7a6a5fd 100644
--- a/servers/slapd/schemaparse.c
+++ b/servers/slapd/schemaparse.c
@@ -331,3 +331,68 @@ done:;
 
 	return code;
 }
+
+static void
+syn_usage( void )
+{
+	fprintf( stderr, "%s",
+		"SyntaxDescription = \"(\" whsp\n"
+		"  numericoid whsp                  ; object identifier\n"
+		"  [ whsp \"DESC\" whsp qdstring ]  ; description\n"
+		"  extensions whsp \")\"            ; extensions\n"
+		"  whsp \")\"\n");
+}
+
+int
+parse_syn(
+	struct config_args_s *c,
+	Syntax **ssyn,
+	Syntax *prev )
+{
+	LDAPSyntax		*syn;
+	slap_syntax_defs_rec	def = { 0 };
+	int			code;
+	const char		*err;
+	char			*line = strchr( c->line, '(' );
+
+	syn = ldap_str2syntax( line, &code, &err, LDAP_SCHEMA_ALLOW_ALL );
+	if ( !syn ) {
+		snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s: %s before %s",
+			c->argv[0], ldap_scherr2str(code), err );
+		Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
+			"%s %s\n", c->log, c->cr_msg, 0 );
+		syn_usage();
+		return 1;
+	}
+
+	if ( syn->syn_oid == NULL ) {
+		snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s: OID is missing",
+			c->argv[0] );
+		Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
+			"%s %s\n", c->log, c->cr_msg, 0 );
+		syn_usage();
+		code = 1;
+		goto done;
+	}
+
+	code = syn_add( syn, 1, &def, ssyn, prev, &err );
+	if ( code ) {
+		snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s: %s: \"%s\"",
+			c->argv[0], scherr2str(code), err);
+		Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
+			"%s %s\n", c->log, c->cr_msg, 0 );
+		code = 1;
+		goto done;
+	}
+
+done:;
+	if ( code ) {
+		ldap_syntax_free( syn );
+
+	} else {
+		ldap_memfree( syn );
+	}
+
+	return code;
+}
+
diff --git a/servers/slapd/slap.h b/servers/slapd/slap.h
index deba5720e9eaaac451ef8cc35df218950bb2cf44..79f076a7a74ad96d317bfc6a59e7f9a0ac28d6a3 100644
--- a/servers/slapd/slap.h
+++ b/servers/slapd/slap.h
@@ -415,6 +415,7 @@ struct Syntax {
 #else
 #define SLAP_SYNTAX_HIDE	0x8000U /* hide (do not publish) */
 #endif
+#define	SLAP_SYNTAX_HARDCODE	0x10000U	/* This is hardcoded schema */
 
 	Syntax				**ssyn_sups;
 
@@ -431,7 +432,7 @@ struct Syntax {
 	struct ComponentDesc* ssync_comp_syntax;
 #endif
 
-	LDAP_SLIST_ENTRY(Syntax)	ssyn_next;
+	LDAP_STAILQ_ENTRY(Syntax)	ssyn_next;
 };
 
 #define slap_syntax_is_flag(s,flag) ((int)((s)->ssyn_flags & (flag)) ? 1 : 0)
diff --git a/servers/slapd/syntax.c b/servers/slapd/syntax.c
index 85025683ecc652998ec38f94043c65f92345a400..40bb88e63dce44e3ac4c414f5fa68976cd5d3561 100644
--- a/servers/slapd/syntax.c
+++ b/servers/slapd/syntax.c
@@ -30,8 +30,11 @@ struct sindexrec {
 };
 
 static Avlnode	*syn_index = NULL;
-static LDAP_SLIST_HEAD(SyntaxList, Syntax) syn_list
-	= LDAP_SLIST_HEAD_INITIALIZER(&syn_list);
+static LDAP_STAILQ_HEAD(SyntaxList, Syntax) syn_list
+	= LDAP_STAILQ_HEAD_INITIALIZER(syn_list);
+
+/* Last hardcoded attribute registered */
+Syntax *syn_sys_tail;
 
 static int
 syn_index_cmp(
@@ -68,7 +71,7 @@ syn_find_desc( const char *syndesc, int *len )
 {
 	Syntax		*synp;
 
-	LDAP_SLIST_FOREACH(synp, &syn_list, ssyn_next) {
+	LDAP_STAILQ_FOREACH(synp, &syn_list, ssyn_next) {
 		if ((*len = dscompare( synp->ssyn_syn.syn_desc, syndesc, '{' /*'}'*/ ))) {
 			return synp;
 		}
@@ -111,9 +114,9 @@ syn_destroy( void )
 	Syntax	*s;
 
 	avl_free( syn_index, ldap_memfree );
-	while( !LDAP_SLIST_EMPTY( &syn_list ) ) {
-		s = LDAP_SLIST_FIRST( &syn_list );
-		LDAP_SLIST_REMOVE_HEAD( &syn_list, ssyn_next );
+	while( !LDAP_STAILQ_EMPTY( &syn_list ) ) {
+		s = LDAP_STAILQ_FIRST( &syn_list );
+		LDAP_STAILQ_REMOVE_HEAD( &syn_list, ssyn_next );
 		if ( s->ssyn_sups ) {
 			SLAP_FREE( s->ssyn_sups );
 		}
@@ -123,14 +126,13 @@ syn_destroy( void )
 
 static int
 syn_insert(
-    Syntax		*ssyn,
-    const char		**err
-)
+	Syntax		*ssyn,
+	Syntax		*prev,
+	const char	**err )
 {
 	struct sindexrec	*sir;
 
-	LDAP_SLIST_NEXT( ssyn, ssyn_next ) = NULL;
-	LDAP_SLIST_INSERT_HEAD( &syn_list, ssyn, ssyn_next );
+	LDAP_STAILQ_NEXT( ssyn, ssyn_next ) = NULL;
  
 	if ( ssyn->ssyn_oid ) {
 		sir = (struct sindexrec *)
@@ -150,19 +152,36 @@ syn_insert(
 		/* FIX: temporal consistency check */
 		syn_find(sir->sir_name);
 	}
+
+	if ( ssyn->ssyn_flags & SLAP_AT_HARDCODE ) {
+		prev = syn_sys_tail;
+		syn_sys_tail = ssyn;
+	}
+
+	if ( prev ) {
+		LDAP_STAILQ_INSERT_AFTER( &syn_list, prev, ssyn, ssyn_next );
+	} else {
+		LDAP_STAILQ_INSERT_TAIL( &syn_list, ssyn, ssyn_next );
+	}
 	return 0;
 }
 
 int
 syn_add(
-    LDAPSyntax		*syn,
-    slap_syntax_defs_rec *def,
-    const char		**err
-)
+	LDAPSyntax		*syn,
+	int			user,
+	slap_syntax_defs_rec	*def,
+	Syntax			**ssynp,
+	Syntax			*prev,
+	const char		**err )
 {
 	Syntax		*ssyn;
 	int		code = 0;
 
+	if ( ssynp != NULL ) {
+		*ssynp = NULL;
+	}
+
 	ssyn = (Syntax *) SLAP_CALLOC( 1, sizeof(Syntax) );
 	if ( ssyn == NULL ) {
 		Debug( LDAP_DEBUG_ANY, "SLAP_CALLOC Error\n", 0, 0, 0 );
@@ -171,7 +190,7 @@ syn_add(
 
 	AC_MEMCPY( &ssyn->ssyn_syn, syn, sizeof(LDAPSyntax) );
 
-	LDAP_SLIST_NEXT(ssyn,ssyn_next) = NULL;
+	LDAP_STAILQ_NEXT(ssyn,ssyn_next) = NULL;
 
 	/*
 	 * note: ssyn_bvoid uses the same memory of ssyn_syn.syn_oid;
@@ -213,9 +232,11 @@ syn_add(
 		}
 	}
 
-	if ( code == 0 ) {
-		code = syn_insert( ssyn, err );
+	if ( !user )
+		ssyn->ssyn_flags |= SLAP_SYNTAX_HARDCODE;
 
+	if ( code == 0 ) {
+		code = syn_insert( ssyn, prev, err );
 	}
 
 	if ( code != 0 && ssyn != NULL ) {
@@ -223,6 +244,11 @@ syn_add(
 			SLAP_FREE( ssyn->ssyn_sups );
 		}
 		SLAP_FREE( ssyn );
+		ssyn = NULL;
+	}
+
+	if (ssynp ) {
+		*ssynp = ssyn;
 	}
 
 	return code;
@@ -244,7 +270,7 @@ register_syntax(
 		return( -1 );
 	}
 
-	code = syn_add( syn, def, &err );
+	code = syn_add( syn, 0, def, NULL, NULL, &err );
 
 	if ( code ) {
 		Debug( LDAP_DEBUG_ANY, "Error in register_syntax: %s %s in %s\n",
@@ -267,7 +293,7 @@ syn_schema_info( Entry *e )
 	struct berval	val;
 	struct berval	nval;
 
-	LDAP_SLIST_FOREACH(syn, &syn_list, ssyn_next ) {
+	LDAP_STAILQ_FOREACH(syn, &syn_list, ssyn_next ) {
 		if ( ! syn->ssyn_validate ) {
 			/* skip syntaxes without validators */
 			continue;
@@ -297,3 +323,92 @@ syn_schema_info( Entry *e )
 	return 0;
 }
 
+void
+syn_delete( Syntax *syn )
+{
+	LDAP_STAILQ_REMOVE(&syn_list, syn, Syntax, ssyn_next);
+}
+
+int
+syn_start( Syntax **syn )
+{
+	assert( syn != NULL );
+
+	*syn = LDAP_STAILQ_FIRST(&syn_list);
+
+	return (*syn != NULL);
+}
+
+int
+syn_next( Syntax **syn )
+{
+	assert( syn != NULL );
+
+#if 0	/* pedantic check: don't use this */
+	{
+		Syntax *tmp = NULL;
+
+		LDAP_STAILQ_FOREACH(tmp,&syn_list,ssyn_next) {
+			if ( tmp == *syn ) {
+				break;
+			}
+		}
+
+		assert( tmp != NULL );
+	}
+#endif
+
+	*syn = LDAP_STAILQ_NEXT(*syn,ssyn_next);
+
+	return (*syn != NULL);
+}
+
+void
+syn_unparse( BerVarray *res, Syntax *start, Syntax *end, int sys )
+{
+	Syntax *syn;
+	int i, num;
+	struct berval bv, *bva = NULL, idx;
+	char ibuf[32];
+
+	if ( !start )
+		start = LDAP_STAILQ_FIRST( &syn_list );
+
+	/* count the result size */
+	i = 0;
+	for ( syn = start; syn; syn = LDAP_STAILQ_NEXT( syn, ssyn_next ) ) {
+		if ( sys && !( syn->ssyn_flags & SLAP_SYNTAX_HARDCODE ) ) break;
+		i++;
+		if ( syn == end ) break;
+	}
+	if ( !i ) return;
+
+	num = i;
+	bva = ch_malloc( (num+1) * sizeof(struct berval) );
+	BER_BVZERO( bva );
+	idx.bv_val = ibuf;
+	if ( sys ) {
+		idx.bv_len = 0;
+		ibuf[0] = '\0';
+	}
+	i = 0;
+	for ( syn = start; syn; syn = LDAP_STAILQ_NEXT( syn, ssyn_next ) ) {
+		if ( sys && !( syn->ssyn_flags & SLAP_SYNTAX_HARDCODE ) ) break;
+		if ( ldap_syntax2bv( &syn->ssyn_syn, &bv ) == NULL ) {
+			ber_bvarray_free( bva );
+		}
+		if ( !sys ) {
+			idx.bv_len = sprintf(idx.bv_val, "{%d}", i);
+		}
+		bva[i].bv_len = idx.bv_len + bv.bv_len;
+		bva[i].bv_val = ch_malloc( bva[i].bv_len + 1 );
+		strcpy( bva[i].bv_val, ibuf );
+		strcpy( bva[i].bv_val + idx.bv_len, bv.bv_val );
+		i++;
+		bva[i].bv_val = NULL;
+		ldap_memfree( bv.bv_val );
+		if ( syn == end ) break;
+	}
+	*res = bva;
+}
+