]> git.sur5r.net Git - openldap/commitdiff
ITS#5663
authorQuanah Gibson-Mount <quanah@openldap.org>
Mon, 29 Sep 2008 22:58:51 +0000 (22:58 +0000)
committerQuanah Gibson-Mount <quanah@openldap.org>
Mon, 29 Sep 2008 22:58:51 +0000 (22:58 +0000)
CHANGES
doc/man/man5/slapd.conf.5
servers/slapd/bconfig.c
servers/slapd/proto-slap.h
servers/slapd/schemaparse.c
servers/slapd/slap.h
servers/slapd/syntax.c

diff --git a/CHANGES b/CHANGES
index 09152a5253d3ccbc5b8ddeeb285208a6a85c6e7b..0a85e4c4e88c4a3d1cf91d6b35f92c13ce9f7067 100644 (file)
--- 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)
index 9267801bbd6943f39456f24bd1752e1254774c82..31412cbf706ac01dec0bc48582f8b20aa1e391ee 100644 (file)
@@ -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,
index f80330e1c743bc02d86610d654e902266b708deb..a241c0f1107d94536069377e2b6508a8de328e99 100644 (file)
@@ -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 },
index ea2292199939f7973cc08053c8e5a21b35e00650..27aaee29517580dca543d31d448eab028164c33a 100644 (file)
@@ -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
  */
index c02fdea215f2e38c16e84f3475f0bc8df79640d0..b6ffcc5fd6b3d1f11f8bd77d6a790e9eb7a6a5fd 100644 (file)
@@ -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;
+}
+
index deba5720e9eaaac451ef8cc35df218950bb2cf44..79f076a7a74ad96d317bfc6a59e7f9a0ac28d6a3 100644 (file)
@@ -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)
index 85025683ecc652998ec38f94043c65f92345a400..40bb88e63dce44e3ac4c414f5fa68976cd5d3561 100644 (file)
@@ -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;
+}
+