int rewrite_rule_compile( struct rewrite_info *info, struct rewrite_context *context, const char *pattern, const char *result, const char *flagstring ) { int flags = REWRITE_REGEX_EXTENDED | REWRITE_REGEX_ICASE; int mode = REWRITE_RECURSE; int max_passes = info->li_max_passes_per_rule; struct rewrite_rule *rule = NULL; struct rewrite_subst *subst = NULL; struct rewrite_action *action = NULL, *first_action = NULL; const char *p; assert( info != NULL ); assert( context != NULL ); assert( pattern != NULL ); assert( result != NULL ); /* * A null flagstring should be allowed */ /* * Take care of substitution string */ subst = rewrite_subst_compile( info, result ); if ( subst == NULL ) { return REWRITE_ERR; } /* * Take care of flags */ for ( p = flagstring; p[ 0 ] != '\0'; p++ ) { switch( p[ 0 ] ) { /* * REGEX flags */ case REWRITE_FLAG_HONORCASE: /* 'C' */ /* * Honor case (default is case insensitive) */ flags &= ~REWRITE_REGEX_ICASE; break; case REWRITE_FLAG_BASICREGEX: /* 'R' */ /* * Use POSIX Basic Regular Expression syntax * instead of POSIX Extended Regular Expression * syntax (default) */ flags &= ~REWRITE_REGEX_EXTENDED; break; /* * Execution mode flags */ case REWRITE_FLAG_EXECONCE: /* ':' */ /* * Apply rule once only */ mode &= ~REWRITE_RECURSE; mode |= REWRITE_EXEC_ONCE; break; /* * Special action flags */ case REWRITE_FLAG_STOP: /* '@' */ /* * Bail out after applying rule */ action = calloc( sizeof( struct rewrite_action ), 1 ); if ( action == NULL ) { goto fail; } action->la_type = REWRITE_ACTION_STOP; break; case REWRITE_FLAG_UNWILLING: /* '#' */ /* * Matching objs will be marked as gone! */ action = calloc( sizeof( struct rewrite_action ), 1 ); if ( action == NULL ) { goto fail; } mode &= ~REWRITE_RECURSE; mode |= REWRITE_EXEC_ONCE; action->la_type = REWRITE_ACTION_UNWILLING; break; case REWRITE_FLAG_GOTO: /* 'G' */ /* * After applying rule, jump N rules */ case REWRITE_FLAG_USER: { /* 'U' */ /* * After applying rule, return user-defined * error code */ char *next = NULL; int *d; if ( p[ 1 ] != '{' ) { goto fail; } d = malloc( sizeof( int ) ); if ( d == NULL ) { goto fail; } d[ 0 ] = strtol( &p[ 2 ], &next, 0 ); if ( next == &p[ 2 ] || next[0] != '}' ) { free( d ); goto fail; } action = calloc( sizeof( struct rewrite_action ), 1 ); if ( action == NULL ) { free( d ); goto fail; } switch ( p[ 0 ] ) { case REWRITE_FLAG_GOTO: action->la_type = REWRITE_ACTION_GOTO; break; case REWRITE_FLAG_USER: action->la_type = REWRITE_ACTION_USER; break; default: assert(0); } action->la_args = (void *)d; p = next; /* p is incremented by the for ... */ break; } case REWRITE_FLAG_MAX_PASSES: { /* 'U' */ /* * Set the number of max passes per rule */ char *next = NULL; if ( p[ 1 ] != '{' ) { goto fail; } max_passes = strtol( &p[ 2 ], &next, 0 ); if ( next == &p[ 2 ] || next[0] != '}' ) { goto fail; } if ( max_passes < 1 ) { /* FIXME: nonsense ... */ max_passes = 1; } p = next; /* p is incremented by the for ... */ break; } case REWRITE_FLAG_IGNORE_ERR: /* 'I' */ /* * Ignore errors! */ action = calloc( sizeof( struct rewrite_action ), 1 ); if ( action == NULL ) { goto fail; } action->la_type = REWRITE_ACTION_IGNORE_ERR; break; /* * Other flags ... */ default: /* * Unimplemented feature (complain only) */ break; } /* * Stupid way to append to a list ... */ if ( action != NULL ) { append_action( &first_action, action ); action = NULL; } } /* * Finally, rule allocation */ rule = calloc( sizeof( struct rewrite_rule ), 1 ); if ( rule == NULL ) { goto fail; } /* * REGEX compilation (luckily I don't need to take care of this ...) */ if ( regcomp( &rule->lr_regex, ( char * )pattern, flags ) != 0 ) { goto fail; } /* * Just to remember them ... */ rule->lr_pattern = strdup( pattern ); rule->lr_subststring = strdup( result ); rule->lr_flagstring = strdup( flagstring ); if ( rule->lr_pattern == NULL || rule->lr_subststring == NULL || rule->lr_flagstring == NULL ) { goto fail; } /* * Load compiled data into rule */ rule->lr_subst = subst; /* * Set various parameters */ rule->lr_flags = flags; /* don't really need any longer ... */ rule->lr_mode = mode; rule->lr_max_passes = max_passes; rule->lr_action = first_action; /* * Append rule at the end of the rewrite context */ append_rule( context, rule ); return REWRITE_SUCCESS; fail: if ( rule ) { if ( rule->lr_pattern ) free( rule->lr_pattern ); if ( rule->lr_subststring ) free( rule->lr_subststring ); if ( rule->lr_flagstring ) free( rule->lr_flagstring ); free( rule ); } destroy_actions( first_action ); free( subst ); return REWRITE_ERR; }
struct rewrite_map * rewrite_map_parse( struct rewrite_info *info, const char *string, const char **currpos ) { struct rewrite_map *map = NULL; struct rewrite_subst *subst = NULL; char *s, *begin = NULL, *end; const char *p; int l, cnt, mtx = 0, rc = 0; assert( info != NULL ); assert( string != NULL ); assert( currpos != NULL ); *currpos = NULL; /* * Go to the end of the map invocation (the right closing brace) */ for ( p = string, cnt = 1; p[ 0 ] != '\0' && cnt > 0; p++ ) { if ( IS_REWRITE_SUBMATCH_ESCAPE( p[ 0 ] ) ) { /* * '%' marks the beginning of a new map */ if ( p[ 1 ] == '{' ) { cnt++; /* * '%' followed by a digit may mark the beginning * of an old map */ } else if ( isdigit( (unsigned char) p[ 1 ] ) && p[ 2 ] == '{' ) { cnt++; p++; } if ( p[ 1 ] != '\0' ) { p++; } } else if ( p[ 0 ] == '}' ) { cnt--; } } if ( cnt != 0 ) { return NULL; } *currpos = p; /* * Copy the map invocation */ l = p - string - 1; s = calloc( sizeof( char ), l + 1 ); if ( s == NULL ) { return NULL; } AC_MEMCPY( s, string, l ); s[ l ] = 0; /* * Isolate the map name (except for variable deref) */ switch ( s[ 0 ] ) { case REWRITE_OPERATOR_VARIABLE_GET: case REWRITE_OPERATOR_PARAM_GET: break; default: begin = strchr( s, '(' ); if ( begin == NULL ) { rc = -1; goto cleanup; } begin[ 0 ] = '\0'; begin++; break; } /* * Check for special map types */ p = s; switch ( p[ 0 ] ) { case REWRITE_OPERATOR_SUBCONTEXT: case REWRITE_OPERATOR_COMMAND: case REWRITE_OPERATOR_VARIABLE_SET: case REWRITE_OPERATOR_VARIABLE_GET: case REWRITE_OPERATOR_PARAM_GET: p++; break; } /* * Variable set and get may be repeated to indicate session-wide * instead of operation-wide variables */ switch ( p[ 0 ] ) { case REWRITE_OPERATOR_VARIABLE_SET: case REWRITE_OPERATOR_VARIABLE_GET: p++; break; } /* * Variable get token can be appended to variable set to mean store * AND rewrite */ if ( p[ 0 ] == REWRITE_OPERATOR_VARIABLE_GET ) { p++; } /* * Check the syntax of the variable name */ if ( !isalpha( (unsigned char) p[ 0 ] ) ) { rc = -1; goto cleanup; } for ( p++; p[ 0 ] != '\0'; p++ ) { if ( !isalnum( (unsigned char) p[ 0 ] ) ) { rc = -1; goto cleanup; } } /* * Isolate the argument of the map (except for variable deref) */ switch ( s[ 0 ] ) { case REWRITE_OPERATOR_VARIABLE_GET: case REWRITE_OPERATOR_PARAM_GET: break; default: end = strrchr( begin, ')' ); if ( end == NULL ) { rc = -1; goto cleanup; } end[ 0 ] = '\0'; /* * Compile the substitution pattern of the map argument */ subst = rewrite_subst_compile( info, begin ); if ( subst == NULL ) { rc = -1; goto cleanup; } break; } /* * Create the map */ map = calloc( sizeof( struct rewrite_map ), 1 ); if ( map == NULL ) { rc = -1; goto cleanup; } memset( map, 0, sizeof( struct rewrite_map ) ); #ifdef USE_REWRITE_LDAP_PVT_THREADS if ( ldap_pvt_thread_mutex_init( &map->lm_mutex ) ) { rc = -1; goto cleanup; } ++mtx; #endif /* USE_REWRITE_LDAP_PVT_THREADS */ /* * No subst for variable deref */ switch ( s[ 0 ] ) { case REWRITE_OPERATOR_VARIABLE_GET: case REWRITE_OPERATOR_PARAM_GET: break; default: map->lm_subst = subst; break; } /* * Parses special map types */ switch ( s[ 0 ] ) { /* * Subcontext */ case REWRITE_OPERATOR_SUBCONTEXT: /* '>' */ /* * Fetch the rewrite context * it MUST have been defined previously */ map->lm_type = REWRITE_MAP_SUBCONTEXT; map->lm_name = strdup( s + 1 ); if ( map->lm_name == NULL ) { rc = -1; goto cleanup; } map->lm_data = rewrite_context_find( info, s + 1 ); if ( map->lm_data == NULL ) { rc = -1; goto cleanup; } break; /* * External command (not implemented yet) */ case REWRITE_OPERATOR_COMMAND: /* '|' */ rc = -1; goto cleanup; /* * Variable set */ case REWRITE_OPERATOR_VARIABLE_SET: /* '&' */ if ( s[ 1 ] == REWRITE_OPERATOR_VARIABLE_SET ) { if ( s[ 2 ] == REWRITE_OPERATOR_VARIABLE_GET ) { map->lm_type = REWRITE_MAP_SETW_SESN_VAR; map->lm_name = strdup( s + 3 ); } else { map->lm_type = REWRITE_MAP_SET_SESN_VAR; map->lm_name = strdup( s + 2 ); } } else { if ( s[ 1 ] == REWRITE_OPERATOR_VARIABLE_GET ) { map->lm_type = REWRITE_MAP_SETW_OP_VAR; map->lm_name = strdup( s + 2 ); } else { map->lm_type = REWRITE_MAP_SET_OP_VAR; map->lm_name = strdup( s + 1 ); } } if ( map->lm_name == NULL ) { rc = -1; goto cleanup; } break; /* * Variable dereference */ case REWRITE_OPERATOR_VARIABLE_GET: /* '*' */ if ( s[ 1 ] == REWRITE_OPERATOR_VARIABLE_GET ) { map->lm_type = REWRITE_MAP_GET_SESN_VAR; map->lm_name = strdup( s + 2 ); } else { map->lm_type = REWRITE_MAP_GET_OP_VAR; map->lm_name = strdup( s + 1 ); } if ( map->lm_name == NULL ) { rc = -1; goto cleanup; } break; /* * Parameter */ case REWRITE_OPERATOR_PARAM_GET: /* '$' */ map->lm_type = REWRITE_MAP_GET_PARAM; map->lm_name = strdup( s + 1 ); if ( map->lm_name == NULL ) { rc = -1; goto cleanup; } break; /* * Built-in map */ default: map->lm_type = REWRITE_MAP_BUILTIN; map->lm_name = strdup( s ); if ( map->lm_name == NULL ) { rc = -1; goto cleanup; } map->lm_data = rewrite_builtin_map_find( info, s ); if ( map->lm_data == NULL ) { rc = -1; goto cleanup; } break; } cleanup: free( s ); if ( rc ) { if ( subst != NULL ) { free( subst ); } if ( map ) { #ifdef USE_REWRITE_LDAP_PVT_THREADS if ( mtx ) { ldap_pvt_thread_mutex_destroy( &map->lm_mutex ); } #endif /* USE_REWRITE_LDAP_PVT_THREADS */ if ( map->lm_name ) { free( map->lm_name ); map->lm_name = NULL; } free( map ); map = NULL; } } return map; }