Patrick Welche <prlw1@cam.ac.uk>
[netbsd-mini2440.git] / external / bsd / openldap / dist / libraries / librewrite / rule.c
blob4183f52805a9430542bbdc9113477b0e42dd4093
1 /* $OpenLDAP: pkg/ldap/libraries/librewrite/rule.c,v 1.23.2.3 2008/02/11 23:26:42 kurt Exp $ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 * Copyright 2000-2008 The OpenLDAP Foundation.
5 * All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted only as authorized by the OpenLDAP
9 * Public License.
11 * A copy of this license is available in the file LICENSE in the
12 * top-level directory of the distribution or, alternatively, at
13 * <http://www.OpenLDAP.org/license.html>.
15 /* ACKNOWLEDGEMENT:
16 * This work was initially developed by Pierangelo Masarati for
17 * inclusion in OpenLDAP Software.
20 #include <portable.h>
22 #include "rewrite-int.h"
25 * Appends a rule to the double linked list of rules
26 * Helper for rewrite_rule_compile
28 static int
29 append_rule(
30 struct rewrite_context *context,
31 struct rewrite_rule *rule
34 struct rewrite_rule *r;
36 assert( context != NULL );
37 assert( context->lc_rule != NULL );
38 assert( rule != NULL );
40 for ( r = context->lc_rule; r->lr_next != NULL; r = r->lr_next );
41 r->lr_next = rule;
42 rule->lr_prev = r;
44 return REWRITE_SUCCESS;
48 * Appends an action to the linked list of actions
49 * Helper for rewrite_rule_compile
51 static int
52 append_action(
53 struct rewrite_action **pbase,
54 struct rewrite_action *action
57 struct rewrite_action **pa;
59 assert( pbase != NULL );
60 assert( action != NULL );
62 for ( pa = pbase; *pa != NULL; pa = &(*pa)->la_next );
63 *pa = action;
65 return REWRITE_SUCCESS;
68 static int
69 destroy_action(
70 struct rewrite_action **paction
73 struct rewrite_action *action;
75 assert( paction != NULL );
76 assert( *paction != NULL );
78 action = *paction;
80 /* do something */
81 switch ( action->la_type ) {
82 case REWRITE_FLAG_GOTO:
83 case REWRITE_FLAG_USER: {
84 int *pi = (int *)action->la_args;
86 if ( pi ) {
87 free( pi );
89 break;
92 default:
93 break;
96 free( action );
97 *paction = NULL;
99 return 0;
102 static void
103 destroy_actions(
104 struct rewrite_action *paction
107 struct rewrite_action *next;
109 for (; paction; paction = next) {
110 next = paction->la_next;
111 destroy_action( &paction );
118 rewrite_rule_compile(
119 struct rewrite_info *info,
120 struct rewrite_context *context,
121 const char *pattern,
122 const char *result,
123 const char *flagstring
126 int flags = REWRITE_REGEX_EXTENDED | REWRITE_REGEX_ICASE;
127 int mode = REWRITE_RECURSE;
128 int max_passes = info->li_max_passes_per_rule;
130 struct rewrite_rule *rule = NULL;
131 struct rewrite_subst *subst = NULL;
132 struct rewrite_action *action = NULL, *first_action = NULL;
134 const char *p;
136 assert( info != NULL );
137 assert( context != NULL );
138 assert( pattern != NULL );
139 assert( result != NULL );
142 * A null flagstring should be allowed
146 * Take care of substitution string
148 subst = rewrite_subst_compile( info, result );
149 if ( subst == NULL ) {
150 return REWRITE_ERR;
154 * Take care of flags
156 for ( p = flagstring; p[ 0 ] != '\0'; p++ ) {
157 switch( p[ 0 ] ) {
160 * REGEX flags
162 case REWRITE_FLAG_HONORCASE: /* 'C' */
164 * Honor case (default is case insensitive)
166 flags &= ~REWRITE_REGEX_ICASE;
167 break;
169 case REWRITE_FLAG_BASICREGEX: /* 'R' */
171 * Use POSIX Basic Regular Expression syntax
172 * instead of POSIX Extended Regular Expression
173 * syntax (default)
175 flags &= ~REWRITE_REGEX_EXTENDED;
176 break;
179 * Execution mode flags
181 case REWRITE_FLAG_EXECONCE: /* ':' */
183 * Apply rule once only
185 mode &= ~REWRITE_RECURSE;
186 mode |= REWRITE_EXEC_ONCE;
187 break;
190 * Special action flags
192 case REWRITE_FLAG_STOP: /* '@' */
194 * Bail out after applying rule
196 action = calloc( sizeof( struct rewrite_action ), 1 );
197 if ( action == NULL ) {
198 goto fail;
201 action->la_type = REWRITE_ACTION_STOP;
202 break;
204 case REWRITE_FLAG_UNWILLING: /* '#' */
206 * Matching objs will be marked as gone!
208 action = calloc( sizeof( struct rewrite_action ), 1 );
209 if ( action == NULL ) {
210 goto fail;
213 mode &= ~REWRITE_RECURSE;
214 mode |= REWRITE_EXEC_ONCE;
215 action->la_type = REWRITE_ACTION_UNWILLING;
216 break;
218 case REWRITE_FLAG_GOTO: /* 'G' */
220 * After applying rule, jump N rules
223 case REWRITE_FLAG_USER: { /* 'U' */
225 * After applying rule, return user-defined
226 * error code
228 char *next = NULL;
229 int *d;
231 if ( p[ 1 ] != '{' ) {
232 goto fail;
235 d = malloc( sizeof( int ) );
236 if ( d == NULL ) {
237 goto fail;
240 d[ 0 ] = strtol( &p[ 2 ], &next, 0 );
241 if ( next == &p[ 2 ] || next[0] != '}' ) {
242 free( d );
243 goto fail;
246 action = calloc( sizeof( struct rewrite_action ), 1 );
247 if ( action == NULL ) {
248 free( d );
249 goto fail;
251 switch ( p[ 0 ] ) {
252 case REWRITE_FLAG_GOTO:
253 action->la_type = REWRITE_ACTION_GOTO;
254 break;
256 case REWRITE_FLAG_USER:
257 action->la_type = REWRITE_ACTION_USER;
258 break;
260 default:
261 assert(0);
264 action->la_args = (void *)d;
266 p = next; /* p is incremented by the for ... */
268 break;
271 case REWRITE_FLAG_MAX_PASSES: { /* 'U' */
273 * Set the number of max passes per rule
275 char *next = NULL;
277 if ( p[ 1 ] != '{' ) {
278 goto fail;
281 max_passes = strtol( &p[ 2 ], &next, 0 );
282 if ( next == &p[ 2 ] || next[0] != '}' ) {
283 goto fail;
286 if ( max_passes < 1 ) {
287 /* FIXME: nonsense ... */
288 max_passes = 1;
291 p = next; /* p is incremented by the for ... */
293 break;
296 case REWRITE_FLAG_IGNORE_ERR: /* 'I' */
298 * Ignore errors!
300 action = calloc( sizeof( struct rewrite_action ), 1 );
301 if ( action == NULL ) {
302 goto fail;
305 action->la_type = REWRITE_ACTION_IGNORE_ERR;
306 break;
309 * Other flags ...
311 default:
313 * Unimplemented feature (complain only)
315 break;
319 * Stupid way to append to a list ...
321 if ( action != NULL ) {
322 append_action( &first_action, action );
323 action = NULL;
328 * Finally, rule allocation
330 rule = calloc( sizeof( struct rewrite_rule ), 1 );
331 if ( rule == NULL ) {
332 goto fail;
336 * REGEX compilation (luckily I don't need to take care of this ...)
338 if ( regcomp( &rule->lr_regex, ( char * )pattern, flags ) != 0 ) {
339 free( rule );
340 goto fail;
344 * Just to remember them ...
346 rule->lr_pattern = strdup( pattern );
347 rule->lr_subststring = strdup( result );
348 rule->lr_flagstring = strdup( flagstring );
351 * Load compiled data into rule
353 rule->lr_subst = subst;
356 * Set various parameters
358 rule->lr_flags = flags; /* don't really need any longer ... */
359 rule->lr_mode = mode;
360 rule->lr_max_passes = max_passes;
361 rule->lr_action = first_action;
364 * Append rule at the end of the rewrite context
366 append_rule( context, rule );
368 return REWRITE_SUCCESS;
370 fail:
371 destroy_actions( first_action );
372 free( subst );
373 return REWRITE_ERR;
377 * Rewrites string according to rule; may return:
378 * OK: fine; if *result != NULL rule matched and rewrite succeeded.
379 * STOP: fine, rule matched; stop processing following rules
380 * UNWILL: rule matched; force 'unwilling to perform'
383 rewrite_rule_apply(
384 struct rewrite_info *info,
385 struct rewrite_op *op,
386 struct rewrite_rule *rule,
387 const char *arg,
388 char **result
391 size_t nmatch = REWRITE_MAX_MATCH;
392 regmatch_t match[ REWRITE_MAX_MATCH ];
394 int rc = REWRITE_SUCCESS;
396 char *string;
397 int strcnt = 0;
398 struct berval val = { 0, NULL };
400 assert( info != NULL );
401 assert( op != NULL );
402 assert( rule != NULL );
403 assert( arg != NULL );
404 assert( result != NULL );
406 *result = NULL;
408 string = (char *)arg;
411 * In case recursive match is required (default)
413 recurse:;
415 Debug( LDAP_DEBUG_TRACE, "==> rewrite_rule_apply"
416 " rule='%s' string='%s' [%d pass(es)]\n",
417 rule->lr_pattern, string, strcnt + 1 );
419 op->lo_num_passes++;
421 rc = regexec( &rule->lr_regex, string, nmatch, match, 0 );
422 if ( rc != 0 ) {
423 if ( *result == NULL && string != arg ) {
424 free( string );
428 * No match is OK; *result = NULL means no match
430 return REWRITE_REGEXEC_OK;
433 rc = rewrite_subst_apply( info, op, rule->lr_subst, string,
434 match, &val );
436 *result = val.bv_val;
437 val.bv_val = NULL;
438 if ( string != arg ) {
439 free( string );
440 string = NULL;
443 if ( rc != REWRITE_REGEXEC_OK ) {
444 return rc;
447 if ( ( rule->lr_mode & REWRITE_RECURSE ) == REWRITE_RECURSE
448 && op->lo_num_passes < info->li_max_passes
449 && ++strcnt < rule->lr_max_passes ) {
450 string = *result;
452 goto recurse;
455 return REWRITE_REGEXEC_OK;
459 rewrite_rule_destroy(
460 struct rewrite_rule **prule
463 struct rewrite_rule *rule;
465 assert( prule != NULL );
466 assert( *prule != NULL );
468 rule = *prule;
470 if ( rule->lr_pattern ) {
471 free( rule->lr_pattern );
472 rule->lr_pattern = NULL;
475 if ( rule->lr_subststring ) {
476 free( rule->lr_subststring );
477 rule->lr_subststring = NULL;
480 if ( rule->lr_flagstring ) {
481 free( rule->lr_flagstring );
482 rule->lr_flagstring = NULL;
485 if ( rule->lr_subst ) {
486 rewrite_subst_destroy( &rule->lr_subst );
489 regfree( &rule->lr_regex );
491 destroy_actions( rule->lr_action );
493 free( rule );
494 *prule = NULL;
496 return 0;