1*cf1d77f7Schristos /*	$NetBSD: rule.c,v 1.3 2021/08/14 16:14:58 christos Exp $	*/
24e6df137Slukem 
333197c6aStron /* $OpenLDAP$ */
42de962bdSlukem /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
52de962bdSlukem  *
6*cf1d77f7Schristos  * Copyright 2000-2021 The OpenLDAP Foundation.
72de962bdSlukem  * All rights reserved.
82de962bdSlukem  *
92de962bdSlukem  * Redistribution and use in source and binary forms, with or without
102de962bdSlukem  * modification, are permitted only as authorized by the OpenLDAP
112de962bdSlukem  * Public License.
122de962bdSlukem  *
132de962bdSlukem  * A copy of this license is available in the file LICENSE in the
142de962bdSlukem  * top-level directory of the distribution or, alternatively, at
152de962bdSlukem  * <http://www.OpenLDAP.org/license.html>.
162de962bdSlukem  */
172de962bdSlukem /* ACKNOWLEDGEMENT:
182de962bdSlukem  * This work was initially developed by Pierangelo Masarati for
192de962bdSlukem  * inclusion in OpenLDAP Software.
202de962bdSlukem  */
212de962bdSlukem 
222de962bdSlukem #include <portable.h>
232de962bdSlukem 
242de962bdSlukem #include "rewrite-int.h"
252de962bdSlukem 
262de962bdSlukem /*
272de962bdSlukem  * Appends a rule to the double linked list of rules
282de962bdSlukem  * Helper for rewrite_rule_compile
292de962bdSlukem  */
302de962bdSlukem static int
append_rule(struct rewrite_context * context,struct rewrite_rule * rule)312de962bdSlukem append_rule(
322de962bdSlukem 		struct rewrite_context *context,
332de962bdSlukem 		struct rewrite_rule *rule
342de962bdSlukem )
352de962bdSlukem {
362de962bdSlukem 	struct rewrite_rule *r;
372de962bdSlukem 
382de962bdSlukem 	assert( context != NULL );
392de962bdSlukem 	assert( context->lc_rule != NULL );
402de962bdSlukem 	assert( rule != NULL );
412de962bdSlukem 
422de962bdSlukem 	for ( r = context->lc_rule; r->lr_next != NULL; r = r->lr_next );
432de962bdSlukem 	r->lr_next = rule;
442de962bdSlukem 	rule->lr_prev = r;
452de962bdSlukem 
462de962bdSlukem 	return REWRITE_SUCCESS;
472de962bdSlukem }
482de962bdSlukem 
492de962bdSlukem /*
502de962bdSlukem  * Appends an action to the linked list of actions
512de962bdSlukem  * Helper for rewrite_rule_compile
522de962bdSlukem  */
532de962bdSlukem static int
append_action(struct rewrite_action ** pbase,struct rewrite_action * action)542de962bdSlukem append_action(
552de962bdSlukem 		struct rewrite_action **pbase,
562de962bdSlukem 		struct rewrite_action *action
572de962bdSlukem )
582de962bdSlukem {
592de962bdSlukem 	struct rewrite_action **pa;
602de962bdSlukem 
612de962bdSlukem 	assert( pbase != NULL );
622de962bdSlukem 	assert( action != NULL );
632de962bdSlukem 
642de962bdSlukem 	for ( pa = pbase; *pa != NULL; pa = &(*pa)->la_next );
652de962bdSlukem 	*pa = action;
662de962bdSlukem 
672de962bdSlukem 	return REWRITE_SUCCESS;
682de962bdSlukem }
692de962bdSlukem 
702de962bdSlukem static int
destroy_action(struct rewrite_action ** paction)712de962bdSlukem destroy_action(
722de962bdSlukem 		struct rewrite_action **paction
732de962bdSlukem )
742de962bdSlukem {
752de962bdSlukem 	struct rewrite_action	*action;
762de962bdSlukem 
772de962bdSlukem 	assert( paction != NULL );
782de962bdSlukem 	assert( *paction != NULL );
792de962bdSlukem 
802de962bdSlukem 	action = *paction;
812de962bdSlukem 
822de962bdSlukem 	/* do something */
832de962bdSlukem 	switch ( action->la_type ) {
842de962bdSlukem 	case REWRITE_FLAG_GOTO:
852de962bdSlukem 	case REWRITE_FLAG_USER: {
862de962bdSlukem 		int *pi = (int *)action->la_args;
872de962bdSlukem 
882de962bdSlukem 		if ( pi ) {
892de962bdSlukem 			free( pi );
902de962bdSlukem 		}
912de962bdSlukem 		break;
922de962bdSlukem 	}
932de962bdSlukem 
942de962bdSlukem 	default:
952de962bdSlukem 		break;
962de962bdSlukem 	}
972de962bdSlukem 
982de962bdSlukem 	free( action );
992de962bdSlukem 	*paction = NULL;
1002de962bdSlukem 
1012de962bdSlukem 	return 0;
1022de962bdSlukem }
1032de962bdSlukem 
1042de962bdSlukem static void
destroy_actions(struct rewrite_action * paction)1052de962bdSlukem destroy_actions(
1062de962bdSlukem 	struct rewrite_action *paction
1072de962bdSlukem )
1082de962bdSlukem {
1092de962bdSlukem 	struct rewrite_action *next;
1102de962bdSlukem 
1112de962bdSlukem 	for (; paction; paction = next) {
1122de962bdSlukem 		next = paction->la_next;
1132de962bdSlukem 		destroy_action( &paction );
1142de962bdSlukem 	}
1152de962bdSlukem }
1162de962bdSlukem 
1172de962bdSlukem /*
1182de962bdSlukem  */
1192de962bdSlukem int
rewrite_rule_compile(struct rewrite_info * info,struct rewrite_context * context,const char * pattern,const char * result,const char * flagstring)1202de962bdSlukem rewrite_rule_compile(
1212de962bdSlukem 		struct rewrite_info *info,
1222de962bdSlukem 		struct rewrite_context *context,
1232de962bdSlukem 		const char *pattern,
1242de962bdSlukem 		const char *result,
1252de962bdSlukem 		const char *flagstring
1262de962bdSlukem )
1272de962bdSlukem {
1282de962bdSlukem 	int flags = REWRITE_REGEX_EXTENDED | REWRITE_REGEX_ICASE;
1292de962bdSlukem 	int mode = REWRITE_RECURSE;
13033197c6aStron 	int max_passes;
1312de962bdSlukem 
1322de962bdSlukem 	struct rewrite_rule *rule = NULL;
1332de962bdSlukem 	struct rewrite_subst *subst = NULL;
1342de962bdSlukem 	struct rewrite_action *action = NULL, *first_action = NULL;
1352de962bdSlukem 
1362de962bdSlukem 	const char *p;
1372de962bdSlukem 
1382de962bdSlukem 	assert( info != NULL );
1392de962bdSlukem 	assert( context != NULL );
1402de962bdSlukem 	assert( pattern != NULL );
1412de962bdSlukem 	assert( result != NULL );
1422de962bdSlukem 	/*
1432de962bdSlukem 	 * A null flagstring should be allowed
1442de962bdSlukem 	 */
1452de962bdSlukem 
14633197c6aStron 	max_passes = info->li_max_passes_per_rule;
14733197c6aStron 
1482de962bdSlukem 	/*
1492de962bdSlukem 	 * Take care of substitution string
1502de962bdSlukem 	 */
1512de962bdSlukem 	subst = rewrite_subst_compile( info, result );
1522de962bdSlukem 	if ( subst == NULL ) {
1532de962bdSlukem 		return REWRITE_ERR;
1542de962bdSlukem 	}
1552de962bdSlukem 
1562de962bdSlukem 	/*
1572de962bdSlukem 	 * Take care of flags
1582de962bdSlukem 	 */
1592de962bdSlukem 	for ( p = flagstring; p[ 0 ] != '\0'; p++ ) {
1602de962bdSlukem 		switch( p[ 0 ] ) {
1612de962bdSlukem 
1622de962bdSlukem 		/*
1632de962bdSlukem 		 * REGEX flags
1642de962bdSlukem 		 */
1652de962bdSlukem 		case REWRITE_FLAG_HONORCASE: 		/* 'C' */
1662de962bdSlukem 			/*
1672de962bdSlukem 			 * Honor case (default is case insensitive)
1682de962bdSlukem 			 */
1692de962bdSlukem 			flags &= ~REWRITE_REGEX_ICASE;
1702de962bdSlukem 			break;
1712de962bdSlukem 
1722de962bdSlukem 		case REWRITE_FLAG_BASICREGEX: 		/* 'R' */
1732de962bdSlukem 			/*
1742de962bdSlukem 			 * Use POSIX Basic Regular Expression syntax
1752de962bdSlukem 			 * instead of POSIX Extended Regular Expression
1762de962bdSlukem 			 * syntax (default)
1772de962bdSlukem 			 */
1782de962bdSlukem 			flags &= ~REWRITE_REGEX_EXTENDED;
1792de962bdSlukem 			break;
1802de962bdSlukem 
1812de962bdSlukem 		/*
1822de962bdSlukem 		 * Execution mode flags
1832de962bdSlukem 		 */
1842de962bdSlukem 		case REWRITE_FLAG_EXECONCE: 		/* ':' */
1852de962bdSlukem 			/*
1862de962bdSlukem 			 * Apply rule once only
1872de962bdSlukem 			 */
1882de962bdSlukem 			mode &= ~REWRITE_RECURSE;
1892de962bdSlukem 			mode |= REWRITE_EXEC_ONCE;
1902de962bdSlukem 			break;
1912de962bdSlukem 
1922de962bdSlukem 		/*
1932de962bdSlukem 		 * Special action flags
1942de962bdSlukem 		 */
1952de962bdSlukem 		case REWRITE_FLAG_STOP:	 		/* '@' */
1962de962bdSlukem 			/*
1972de962bdSlukem 			 * Bail out after applying rule
1982de962bdSlukem 			 */
1992de962bdSlukem 			action = calloc( sizeof( struct rewrite_action ), 1 );
2002de962bdSlukem 			if ( action == NULL ) {
2012de962bdSlukem 				goto fail;
2022de962bdSlukem 			}
2032de962bdSlukem 
2042de962bdSlukem 			action->la_type = REWRITE_ACTION_STOP;
2052de962bdSlukem 			break;
2062de962bdSlukem 
2072de962bdSlukem 		case REWRITE_FLAG_UNWILLING: 		/* '#' */
2082de962bdSlukem 			/*
2092de962bdSlukem 			 * Matching objs will be marked as gone!
2102de962bdSlukem 			 */
2112de962bdSlukem 			action = calloc( sizeof( struct rewrite_action ), 1 );
2122de962bdSlukem 			if ( action == NULL ) {
2132de962bdSlukem 				goto fail;
2142de962bdSlukem 			}
2152de962bdSlukem 
2162de962bdSlukem 			mode &= ~REWRITE_RECURSE;
2172de962bdSlukem 			mode |= REWRITE_EXEC_ONCE;
2182de962bdSlukem 			action->la_type = REWRITE_ACTION_UNWILLING;
2192de962bdSlukem 			break;
2202de962bdSlukem 
2212de962bdSlukem 		case REWRITE_FLAG_GOTO:				/* 'G' */
2222de962bdSlukem 			/*
2232de962bdSlukem 			 * After applying rule, jump N rules
2242de962bdSlukem 			 */
2252de962bdSlukem 
2262de962bdSlukem 		case REWRITE_FLAG_USER: {			/* 'U' */
2272de962bdSlukem 			/*
2282de962bdSlukem 			 * After applying rule, return user-defined
2292de962bdSlukem 			 * error code
2302de962bdSlukem 			 */
2312de962bdSlukem 			char *next = NULL;
2322de962bdSlukem 			int *d;
2332de962bdSlukem 
2342de962bdSlukem 			if ( p[ 1 ] != '{' ) {
2352de962bdSlukem 				goto fail;
2362de962bdSlukem 			}
2372de962bdSlukem 
2382de962bdSlukem 			d = malloc( sizeof( int ) );
2392de962bdSlukem 			if ( d == NULL ) {
2402de962bdSlukem 				goto fail;
2412de962bdSlukem 			}
2422de962bdSlukem 
2432de962bdSlukem 			d[ 0 ] = strtol( &p[ 2 ], &next, 0 );
2442de962bdSlukem 			if ( next == &p[ 2 ] || next[0] != '}' ) {
2452de962bdSlukem 				free( d );
2462de962bdSlukem 				goto fail;
2472de962bdSlukem 			}
2482de962bdSlukem 
2492de962bdSlukem 			action = calloc( sizeof( struct rewrite_action ), 1 );
2502de962bdSlukem 			if ( action == NULL ) {
2512de962bdSlukem 				free( d );
2522de962bdSlukem 				goto fail;
2532de962bdSlukem 			}
2542de962bdSlukem 			switch ( p[ 0 ] ) {
2552de962bdSlukem 			case REWRITE_FLAG_GOTO:
2562de962bdSlukem 				action->la_type = REWRITE_ACTION_GOTO;
2572de962bdSlukem 				break;
2582de962bdSlukem 
2592de962bdSlukem 			case REWRITE_FLAG_USER:
2602de962bdSlukem 				action->la_type = REWRITE_ACTION_USER;
2612de962bdSlukem 				break;
2622de962bdSlukem 
2632de962bdSlukem 			default:
2642de962bdSlukem 				assert(0);
2652de962bdSlukem 			}
2662de962bdSlukem 
2672de962bdSlukem 			action->la_args = (void *)d;
2682de962bdSlukem 
2692de962bdSlukem 			p = next;	/* p is incremented by the for ... */
2702de962bdSlukem 
2712de962bdSlukem 			break;
2722de962bdSlukem 		}
2732de962bdSlukem 
2742de962bdSlukem 		case REWRITE_FLAG_MAX_PASSES: {			/* 'U' */
2752de962bdSlukem 			/*
2762de962bdSlukem 			 * Set the number of max passes per rule
2772de962bdSlukem 			 */
2782de962bdSlukem 			char *next = NULL;
2792de962bdSlukem 
2802de962bdSlukem 			if ( p[ 1 ] != '{' ) {
2812de962bdSlukem 				goto fail;
2822de962bdSlukem 			}
2832de962bdSlukem 
2842de962bdSlukem 			max_passes = strtol( &p[ 2 ], &next, 0 );
2852de962bdSlukem 			if ( next == &p[ 2 ] || next[0] != '}' ) {
2862de962bdSlukem 				goto fail;
2872de962bdSlukem 			}
2882de962bdSlukem 
2892de962bdSlukem 			if ( max_passes < 1 ) {
2902de962bdSlukem 				/* FIXME: nonsense ... */
2912de962bdSlukem 				max_passes = 1;
2922de962bdSlukem 			}
2932de962bdSlukem 
2942de962bdSlukem 			p = next;	/* p is incremented by the for ... */
2952de962bdSlukem 
2962de962bdSlukem 			break;
2972de962bdSlukem 		}
2982de962bdSlukem 
2992de962bdSlukem 		case REWRITE_FLAG_IGNORE_ERR:               /* 'I' */
3002de962bdSlukem 			/*
3012de962bdSlukem 			 * Ignore errors!
3022de962bdSlukem 			 */
3032de962bdSlukem 			action = calloc( sizeof( struct rewrite_action ), 1 );
3042de962bdSlukem 			if ( action == NULL ) {
3052de962bdSlukem 				goto fail;
3062de962bdSlukem 			}
3072de962bdSlukem 
3082de962bdSlukem 			action->la_type = REWRITE_ACTION_IGNORE_ERR;
3092de962bdSlukem 			break;
3102de962bdSlukem 
3112de962bdSlukem 		/*
3122de962bdSlukem 		 * Other flags ...
3132de962bdSlukem 		 */
3142de962bdSlukem 		default:
3152de962bdSlukem 			/*
3162de962bdSlukem 			 * Unimplemented feature (complain only)
3172de962bdSlukem 			 */
3182de962bdSlukem 			break;
3192de962bdSlukem 		}
3202de962bdSlukem 
3212de962bdSlukem 		/*
3222de962bdSlukem 		 * Stupid way to append to a list ...
3232de962bdSlukem 		 */
3242de962bdSlukem 		if ( action != NULL ) {
3252de962bdSlukem 			append_action( &first_action, action );
3262de962bdSlukem 			action = NULL;
3272de962bdSlukem 		}
3282de962bdSlukem 	}
3292de962bdSlukem 
3302de962bdSlukem 	/*
3312de962bdSlukem 	 * Finally, rule allocation
3322de962bdSlukem 	 */
3332de962bdSlukem 	rule = calloc( sizeof( struct rewrite_rule ), 1 );
3342de962bdSlukem 	if ( rule == NULL ) {
3352de962bdSlukem 		goto fail;
3362de962bdSlukem 	}
3372de962bdSlukem 
3382de962bdSlukem 	/*
3392de962bdSlukem 	 * REGEX compilation (luckily I don't need to take care of this ...)
3402de962bdSlukem 	 */
3412de962bdSlukem 	if ( regcomp( &rule->lr_regex, ( char * )pattern, flags ) != 0 ) {
3422de962bdSlukem 		goto fail;
3432de962bdSlukem 	}
3442de962bdSlukem 
3452de962bdSlukem 	/*
3462de962bdSlukem 	 * Just to remember them ...
3472de962bdSlukem 	 */
3482de962bdSlukem 	rule->lr_pattern = strdup( pattern );
3492de962bdSlukem 	rule->lr_subststring = strdup( result );
3502de962bdSlukem 	rule->lr_flagstring = strdup( flagstring );
3514e6df137Slukem 	if ( rule->lr_pattern == NULL
3524e6df137Slukem 		|| rule->lr_subststring == NULL
3534e6df137Slukem 		|| rule->lr_flagstring == NULL )
3544e6df137Slukem 	{
3554e6df137Slukem 		goto fail;
3564e6df137Slukem 	}
3572de962bdSlukem 
3582de962bdSlukem 	/*
3592de962bdSlukem 	 * Load compiled data into rule
3602de962bdSlukem 	 */
3612de962bdSlukem 	rule->lr_subst = subst;
3622de962bdSlukem 
3632de962bdSlukem 	/*
3642de962bdSlukem 	 * Set various parameters
3652de962bdSlukem 	 */
3662de962bdSlukem 	rule->lr_flags = flags;		/* don't really need any longer ... */
3672de962bdSlukem 	rule->lr_mode = mode;
3682de962bdSlukem 	rule->lr_max_passes = max_passes;
3692de962bdSlukem 	rule->lr_action = first_action;
3702de962bdSlukem 
3712de962bdSlukem 	/*
3722de962bdSlukem 	 * Append rule at the end of the rewrite context
3732de962bdSlukem 	 */
3742de962bdSlukem 	append_rule( context, rule );
3752de962bdSlukem 
3762de962bdSlukem 	return REWRITE_SUCCESS;
3772de962bdSlukem 
3782de962bdSlukem fail:
3794e6df137Slukem 	if ( rule ) {
3804e6df137Slukem 		if ( rule->lr_pattern ) free( rule->lr_pattern );
3814e6df137Slukem 		if ( rule->lr_subststring ) free( rule->lr_subststring );
3824e6df137Slukem 		if ( rule->lr_flagstring ) free( rule->lr_flagstring );
3834e6df137Slukem 		free( rule );
3844e6df137Slukem 	}
3852de962bdSlukem 	destroy_actions( first_action );
3862de962bdSlukem 	free( subst );
3872de962bdSlukem 	return REWRITE_ERR;
3882de962bdSlukem }
3892de962bdSlukem 
3902de962bdSlukem /*
3912de962bdSlukem  * Rewrites string according to rule; may return:
3922de962bdSlukem  *      OK:     fine; if *result != NULL rule matched and rewrite succeeded.
3932de962bdSlukem  *      STOP:   fine, rule matched; stop processing following rules
3942de962bdSlukem  *      UNWILL: rule matched; force 'unwilling to perform'
3952de962bdSlukem  */
3962de962bdSlukem int
rewrite_rule_apply(struct rewrite_info * info,struct rewrite_op * op,struct rewrite_rule * rule,const char * arg,char ** result)3972de962bdSlukem rewrite_rule_apply(
3982de962bdSlukem 		struct rewrite_info *info,
3992de962bdSlukem 		struct rewrite_op *op,
4002de962bdSlukem 		struct rewrite_rule *rule,
4012de962bdSlukem 		const char *arg,
4022de962bdSlukem 		char **result
4032de962bdSlukem 		)
4042de962bdSlukem {
4052de962bdSlukem 	size_t nmatch = REWRITE_MAX_MATCH;
4062de962bdSlukem 	regmatch_t match[ REWRITE_MAX_MATCH ];
4072de962bdSlukem 
4082de962bdSlukem 	int rc = REWRITE_SUCCESS;
4092de962bdSlukem 
4102de962bdSlukem 	char *string;
4112de962bdSlukem 	int strcnt = 0;
4122de962bdSlukem 	struct berval val = { 0, NULL };
4132de962bdSlukem 
4142de962bdSlukem 	assert( info != NULL );
4152de962bdSlukem 	assert( op != NULL );
4162de962bdSlukem 	assert( rule != NULL );
4172de962bdSlukem 	assert( arg != NULL );
4182de962bdSlukem 	assert( result != NULL );
4192de962bdSlukem 
4202de962bdSlukem 	*result = NULL;
4212de962bdSlukem 
4222de962bdSlukem 	string = (char *)arg;
4232de962bdSlukem 
4242de962bdSlukem 	/*
4252de962bdSlukem 	 * In case recursive match is required (default)
4262de962bdSlukem 	 */
4272de962bdSlukem recurse:;
4282de962bdSlukem 
4292de962bdSlukem 	Debug( LDAP_DEBUG_TRACE, "==> rewrite_rule_apply"
4302de962bdSlukem 			" rule='%s' string='%s' [%d pass(es)]\n",
4312de962bdSlukem 			rule->lr_pattern, string, strcnt + 1 );
4322de962bdSlukem 
4332de962bdSlukem 	op->lo_num_passes++;
4342de962bdSlukem 
4352de962bdSlukem 	rc = regexec( &rule->lr_regex, string, nmatch, match, 0 );
4362de962bdSlukem 	if ( rc != 0 ) {
4372de962bdSlukem 		if ( *result == NULL && string != arg ) {
4382de962bdSlukem 			free( string );
4392de962bdSlukem 		}
4402de962bdSlukem 
4412de962bdSlukem 		/*
4422de962bdSlukem 		 * No match is OK; *result = NULL means no match
4432de962bdSlukem 		 */
4442de962bdSlukem 		return REWRITE_REGEXEC_OK;
4452de962bdSlukem 	}
4462de962bdSlukem 
4472de962bdSlukem 	rc = rewrite_subst_apply( info, op, rule->lr_subst, string,
4482de962bdSlukem 			match, &val );
4492de962bdSlukem 
4502de962bdSlukem 	*result = val.bv_val;
4512de962bdSlukem 	val.bv_val = NULL;
4522de962bdSlukem 	if ( string != arg ) {
4532de962bdSlukem 		free( string );
4542de962bdSlukem 		string = NULL;
4552de962bdSlukem 	}
4562de962bdSlukem 
4572de962bdSlukem 	if ( rc != REWRITE_REGEXEC_OK ) {
4582de962bdSlukem 		return rc;
4592de962bdSlukem 	}
4602de962bdSlukem 
4612de962bdSlukem 	if ( ( rule->lr_mode & REWRITE_RECURSE ) == REWRITE_RECURSE
4622de962bdSlukem 			&& op->lo_num_passes < info->li_max_passes
4632de962bdSlukem 			&& ++strcnt < rule->lr_max_passes ) {
4642de962bdSlukem 		string = *result;
4652de962bdSlukem 
4662de962bdSlukem 		goto recurse;
4672de962bdSlukem 	}
4682de962bdSlukem 
4692de962bdSlukem 	return REWRITE_REGEXEC_OK;
4702de962bdSlukem }
4712de962bdSlukem 
4722de962bdSlukem int
rewrite_rule_destroy(struct rewrite_rule ** prule)4732de962bdSlukem rewrite_rule_destroy(
4742de962bdSlukem 		struct rewrite_rule **prule
4752de962bdSlukem 		)
4762de962bdSlukem {
4772de962bdSlukem 	struct rewrite_rule *rule;
4782de962bdSlukem 
4792de962bdSlukem 	assert( prule != NULL );
4802de962bdSlukem 	assert( *prule != NULL );
4812de962bdSlukem 
4822de962bdSlukem 	rule = *prule;
4832de962bdSlukem 
4842de962bdSlukem 	if ( rule->lr_pattern ) {
4852de962bdSlukem 		free( rule->lr_pattern );
4862de962bdSlukem 		rule->lr_pattern = NULL;
4872de962bdSlukem 	}
4882de962bdSlukem 
4892de962bdSlukem 	if ( rule->lr_subststring ) {
4902de962bdSlukem 		free( rule->lr_subststring );
4912de962bdSlukem 		rule->lr_subststring = NULL;
4922de962bdSlukem 	}
4932de962bdSlukem 
4942de962bdSlukem 	if ( rule->lr_flagstring ) {
4952de962bdSlukem 		free( rule->lr_flagstring );
4962de962bdSlukem 		rule->lr_flagstring = NULL;
4972de962bdSlukem 	}
4982de962bdSlukem 
4992de962bdSlukem 	if ( rule->lr_subst ) {
5002de962bdSlukem 		rewrite_subst_destroy( &rule->lr_subst );
5012de962bdSlukem 	}
5022de962bdSlukem 
5032de962bdSlukem 	regfree( &rule->lr_regex );
5042de962bdSlukem 
5052de962bdSlukem 	destroy_actions( rule->lr_action );
5062de962bdSlukem 
5072de962bdSlukem 	free( rule );
5082de962bdSlukem 	*prule = NULL;
5092de962bdSlukem 
5102de962bdSlukem 	return 0;
5112de962bdSlukem }
5122de962bdSlukem 
513