+#define LBUFSIZ 80
+AttributeName*
+file2anlist( AttributeName *an, const char *fname, const char *brkstr )
+{
+ FILE *fp;
+ char *line = NULL;
+ char *lcur = NULL;
+ char *c;
+ size_t lmax = LBUFSIZ;
+
+ fp = fopen( fname, "r" );
+ if ( fp == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "get_attrs_from_file: failed to open attribute list file "
+ "\"%s\": %s\n", fname, strerror(errno), 0 );
+ return NULL;
+ }
+
+ lcur = line = (char *) ch_malloc( lmax );
+ if ( !line ) {
+ Debug( LDAP_DEBUG_ANY,
+ "get_attrs_from_file: could not allocate memory\n",
+ 0, 0, 0 );
+ fclose(fp);
+ return NULL;
+ }
+
+ while ( fgets( lcur, LBUFSIZ, fp ) != NULL ) {
+ if ( ( c = strchr( lcur, '\n' ) ) ) {
+ if ( c == line ) {
+ *c = '\0';
+ } else if ( *(c-1) == '\r' ) {
+ *(c-1) = '\0';
+ } else {
+ *c = '\0';
+ }
+ } else {
+ lmax += LBUFSIZ;
+ line = (char *) ch_realloc( line, lmax );
+ if ( !line ) {
+ Debug( LDAP_DEBUG_ANY,
+ "get_attrs_from_file: could not allocate memory\n",
+ 0, 0, 0 );
+ fclose(fp);
+ return NULL;
+ }
+ lcur = line + strlen( line );
+ continue;
+ }
+ an = str2anlist( an, line, brkstr );
+ if ( an == NULL )
+ break;
+ lcur = line;
+ }
+ ch_free( line );
+ fclose(fp);
+ return an;
+}
+#undef LBUFSIZ
+
+/* Define an attribute option. */
+int
+ad_define_option( const char *name, const char *fname, int lineno )
+{
+ int i;
+ unsigned int optlen;
+
+ if ( options == &lang_option ) {
+ options = NULL;
+ option_count = 0;
+ }
+ if ( name == NULL )
+ return 0;
+
+ optlen = 0;
+ do {
+ if ( !DESC_CHAR( name[optlen] ) ) {
+ /* allow trailing '=', same as '-' */
+ if ( name[optlen] == '=' && !name[optlen+1] ) {
+ msad_range_hack = 1;
+ continue;
+ }
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: illegal option name \"%s\"\n",
+ fname, lineno, name );
+ return 1;
+ }
+ } while ( name[++optlen] );
+
+ options = ch_realloc( options,
+ (option_count+1) * sizeof(Attr_option) );
+
+ if ( strcasecmp( name, "binary" ) == 0
+ || ad_find_option_definition( name, optlen ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: option \"%s\" is already defined\n",
+ fname, lineno, name );
+ return 1;
+ }
+
+ for ( i = option_count; i; --i ) {
+ if ( strcasecmp( name, options[i-1].name.bv_val ) >= 0 )
+ break;
+ options[i] = options[i-1];
+ }
+
+ options[i].name.bv_val = ch_strdup( name );
+ options[i].name.bv_len = optlen;
+ options[i].prefix = (name[optlen-1] == '-') ||
+ (name[optlen-1] == '=');
+
+ if ( i != option_count &&
+ options[i].prefix &&
+ optlen < options[i+1].name.bv_len &&
+ strncasecmp( name, options[i+1].name.bv_val, optlen ) == 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: option \"%s\" overrides previous option\n",
+ fname, lineno, name );
+ return 1;
+ }
+
+ option_count++;
+ return 0;
+}
+
+void
+ad_unparse_options( BerVarray *res )
+{
+ int i;
+ for ( i = 0; i < option_count; i++ ) {
+ value_add_one( res, &options[i].name );
+ }
+}
+
+/* Find the definition of the option name or prefix matching the arguments */
+static Attr_option *
+ad_find_option_definition( const char *opt, int optlen )
+{
+ int top = 0, bot = option_count;
+ while ( top < bot ) {
+ int mid = (top + bot) / 2;
+ int mlen = options[mid].name.bv_len;
+ char *mname = options[mid].name.bv_val;
+ int j;
+ if ( optlen < mlen ) {
+ j = strncasecmp( opt, mname, optlen ) - 1;
+ } else {
+ j = strncasecmp( opt, mname, mlen );
+ if ( j==0 && (optlen==mlen || options[mid].prefix) )
+ return &options[mid];
+ }
+ if ( j < 0 )
+ bot = mid;
+ else
+ top = mid + 1;
+ }
+ return NULL;
+}
+
+MatchingRule *ad_mr(
+ AttributeDescription *ad,
+ unsigned usage )
+{
+ switch( usage & SLAP_MR_TYPE_MASK ) {
+ case SLAP_MR_NONE:
+ case SLAP_MR_EQUALITY:
+ return ad->ad_type->sat_equality;
+ break;
+ case SLAP_MR_ORDERING:
+ return ad->ad_type->sat_ordering;
+ break;
+ case SLAP_MR_SUBSTR:
+ return ad->ad_type->sat_substr;
+ break;
+ case SLAP_MR_EXT:
+ default:
+ assert( 0 /* ad_mr: bad usage */);
+ }
+ return NULL;
+}