1*cf1d77f7Schristos /*	$NetBSD: subst.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  * Compiles a substitution pattern
282de962bdSlukem  */
292de962bdSlukem struct rewrite_subst *
rewrite_subst_compile(struct rewrite_info * info,const char * str)302de962bdSlukem rewrite_subst_compile(
312de962bdSlukem 		struct rewrite_info *info,
322de962bdSlukem 		const char *str
332de962bdSlukem )
342de962bdSlukem {
352de962bdSlukem 	size_t subs_len;
362de962bdSlukem 	struct berval *subs = NULL, *tmps;
37*cf1d77f7Schristos 	struct rewrite_submatch *submatch = NULL, *tmpsm;
382de962bdSlukem 
392de962bdSlukem 	struct rewrite_subst *s = NULL;
402de962bdSlukem 
412de962bdSlukem 	char *result, *begin, *p;
422de962bdSlukem 	int nsub = 0, l;
432de962bdSlukem 
442de962bdSlukem 	assert( info != NULL );
452de962bdSlukem 	assert( str != NULL );
462de962bdSlukem 
472de962bdSlukem 	result = strdup( str );
482de962bdSlukem 	if ( result == NULL ) {
492de962bdSlukem 		return NULL;
502de962bdSlukem 	}
512de962bdSlukem 
522de962bdSlukem 	/*
532de962bdSlukem 	 * Take care of substitution string
542de962bdSlukem 	 */
552de962bdSlukem 	for ( p = begin = result, subs_len = 0; p[ 0 ] != '\0'; p++ ) {
562de962bdSlukem 
572de962bdSlukem 		/*
582de962bdSlukem 		 * Keep only single escapes '%'
592de962bdSlukem 		 */
602de962bdSlukem 		if (  !IS_REWRITE_SUBMATCH_ESCAPE( p[ 0 ] ) ) {
612de962bdSlukem 			continue;
622de962bdSlukem 		}
632de962bdSlukem 
642de962bdSlukem 		if (  IS_REWRITE_SUBMATCH_ESCAPE( p[ 1 ] ) ) {
652de962bdSlukem 			/* Pull &p[1] over p, including the trailing '\0' */
662de962bdSlukem 			AC_MEMCPY((char *)p, &p[ 1 ], strlen( p ) );
672de962bdSlukem 			continue;
682de962bdSlukem 		}
692de962bdSlukem 
702de962bdSlukem 		tmps = ( struct berval * )realloc( subs,
712de962bdSlukem 				sizeof( struct berval )*( nsub + 1 ) );
722de962bdSlukem 		if ( tmps == NULL ) {
732de962bdSlukem 			goto cleanup;
742de962bdSlukem 		}
752de962bdSlukem 		subs = tmps;
76*cf1d77f7Schristos 		subs[ nsub ].bv_val = NULL;
77*cf1d77f7Schristos 
78*cf1d77f7Schristos 		tmpsm = ( struct rewrite_submatch * )realloc( submatch,
79*cf1d77f7Schristos 				sizeof( struct rewrite_submatch )*( nsub + 1 ) );
80*cf1d77f7Schristos 		if ( tmpsm == NULL ) {
81*cf1d77f7Schristos 			goto cleanup;
82*cf1d77f7Schristos 		}
83*cf1d77f7Schristos 		submatch = tmpsm;
84*cf1d77f7Schristos 		submatch[ nsub ].ls_map = NULL;
852de962bdSlukem 
862de962bdSlukem 		/*
872de962bdSlukem 		 * I think an `if l > 0' at runtime is better outside than
882de962bdSlukem 		 * inside a function call ...
892de962bdSlukem 		 */
902de962bdSlukem 		l = p - begin;
912de962bdSlukem 		if ( l > 0 ) {
922de962bdSlukem 			subs_len += l;
932de962bdSlukem 			subs[ nsub ].bv_len = l;
942de962bdSlukem 			subs[ nsub ].bv_val = malloc( l + 1 );
952de962bdSlukem 			if ( subs[ nsub ].bv_val == NULL ) {
962de962bdSlukem 				goto cleanup;
972de962bdSlukem 			}
982de962bdSlukem 			AC_MEMCPY( subs[ nsub ].bv_val, begin, l );
992de962bdSlukem 			subs[ nsub ].bv_val[ l ] = '\0';
1002de962bdSlukem 		} else {
1012de962bdSlukem 			subs[ nsub ].bv_val = NULL;
1022de962bdSlukem 			subs[ nsub ].bv_len = 0;
1032de962bdSlukem 		}
1042de962bdSlukem 
1052de962bdSlukem 		/*
1062de962bdSlukem 		 * Substitution pattern
1072de962bdSlukem 		 */
1082de962bdSlukem 		if ( isdigit( (unsigned char) p[ 1 ] ) ) {
1092de962bdSlukem 			int d = p[ 1 ] - '0';
1102de962bdSlukem 
1112de962bdSlukem 			/*
1122de962bdSlukem 			 * Add a new value substitution scheme
1132de962bdSlukem 			 */
1142de962bdSlukem 
1152de962bdSlukem 			submatch[ nsub ].ls_submatch = d;
1162de962bdSlukem 
1172de962bdSlukem 			/*
1182de962bdSlukem 			 * If there is no argument, use default
1192de962bdSlukem 			 * (substitute substring as is)
1202de962bdSlukem 			 */
1212de962bdSlukem 			if ( p[ 2 ] != '{' ) {
1222de962bdSlukem 				submatch[ nsub ].ls_type =
1232de962bdSlukem 					REWRITE_SUBMATCH_ASIS;
1242de962bdSlukem 				submatch[ nsub ].ls_map = NULL;
1252de962bdSlukem 				begin = ++p + 1;
1262de962bdSlukem 
1272de962bdSlukem 			} else {
1282de962bdSlukem 				struct rewrite_map *map;
1292de962bdSlukem 
1302de962bdSlukem 				submatch[ nsub ].ls_type =
1312de962bdSlukem 					REWRITE_SUBMATCH_XMAP;
1322de962bdSlukem 
1332de962bdSlukem 				map = rewrite_xmap_parse( info,
1342de962bdSlukem 						p + 3, (const char **)&begin );
1352de962bdSlukem 				if ( map == NULL ) {
1362de962bdSlukem 					goto cleanup;
1372de962bdSlukem 				}
1382de962bdSlukem 				submatch[ nsub ].ls_map = map;
1392de962bdSlukem 				p = begin - 1;
1402de962bdSlukem 			}
1412de962bdSlukem 
1422de962bdSlukem 		/*
1432de962bdSlukem 		 * Map with args ...
1442de962bdSlukem 		 */
1452de962bdSlukem 		} else if ( p[ 1 ] == '{' ) {
1462de962bdSlukem 			struct rewrite_map *map;
1472de962bdSlukem 
1482de962bdSlukem 			map = rewrite_map_parse( info, p + 2,
1492de962bdSlukem 					(const char **)&begin );
1502de962bdSlukem 			if ( map == NULL ) {
1512de962bdSlukem 				goto cleanup;
1522de962bdSlukem 			}
1532de962bdSlukem 			p = begin - 1;
1542de962bdSlukem 
1552de962bdSlukem 			/*
1562de962bdSlukem 			 * Add a new value substitution scheme
1572de962bdSlukem 			 */
1582de962bdSlukem 			submatch[ nsub ].ls_type =
1592de962bdSlukem 				REWRITE_SUBMATCH_MAP_W_ARG;
1602de962bdSlukem 			submatch[ nsub ].ls_map = map;
1612de962bdSlukem 
1622de962bdSlukem 		/*
1632de962bdSlukem 		 * Escape '%' ...
1642de962bdSlukem 		 */
1652de962bdSlukem 		} else if ( p[ 1 ] == '%' ) {
1662de962bdSlukem 			AC_MEMCPY( &p[ 1 ], &p[ 2 ], strlen( &p[ 1 ] ) );
1672de962bdSlukem 			continue;
1682de962bdSlukem 
1692de962bdSlukem 		} else {
1702de962bdSlukem 			goto cleanup;
1712de962bdSlukem 		}
1722de962bdSlukem 
1732de962bdSlukem 		nsub++;
1742de962bdSlukem 	}
1752de962bdSlukem 
1762de962bdSlukem 	/*
1772de962bdSlukem 	 * Last part of string
1782de962bdSlukem 	 */
1792de962bdSlukem 	tmps = (struct berval * )realloc( subs, sizeof( struct berval )*( nsub + 1 ) );
1802de962bdSlukem 	if ( tmps == NULL ) {
1812de962bdSlukem 		/*
1822de962bdSlukem 		 * XXX need to free the value subst stuff!
1832de962bdSlukem 		 */
1842de962bdSlukem 		free( subs );
1852de962bdSlukem 		goto cleanup;
1862de962bdSlukem 	}
1872de962bdSlukem 	subs = tmps;
1882de962bdSlukem 	l = p - begin;
1892de962bdSlukem 	if ( l > 0 ) {
1902de962bdSlukem 		subs_len += l;
1912de962bdSlukem 		subs[ nsub ].bv_len = l;
1922de962bdSlukem 		subs[ nsub ].bv_val = malloc( l + 1 );
1934e6df137Slukem 		if ( subs[ nsub ].bv_val == NULL ) {
1944e6df137Slukem 			goto cleanup;
1954e6df137Slukem 		}
1962de962bdSlukem 		AC_MEMCPY( subs[ nsub ].bv_val, begin, l );
1972de962bdSlukem 		subs[ nsub ].bv_val[ l ] = '\0';
1982de962bdSlukem 	} else {
1992de962bdSlukem 		subs[ nsub ].bv_val = NULL;
2002de962bdSlukem 		subs[ nsub ].bv_len = 0;
2012de962bdSlukem 	}
2022de962bdSlukem 
2032de962bdSlukem 	s = calloc( sizeof( struct rewrite_subst ), 1 );
2042de962bdSlukem 	if ( s == NULL ) {
2052de962bdSlukem 		goto cleanup;
2062de962bdSlukem 	}
2072de962bdSlukem 
2082de962bdSlukem 	s->lt_subs_len = subs_len;
2092de962bdSlukem 	s->lt_subs = subs;
2102de962bdSlukem 	s->lt_num_submatch = nsub;
2112de962bdSlukem 	s->lt_submatch = submatch;
2128bd9f7cdSchristos 	subs = NULL;
2138bd9f7cdSchristos 	submatch = NULL;
2142de962bdSlukem 
2152de962bdSlukem cleanup:;
2168bd9f7cdSchristos 	if ( subs ) {
2178bd9f7cdSchristos 		for ( l=0; l<nsub; l++ ) {
2188bd9f7cdSchristos 			free( subs[nsub].bv_val );
2198bd9f7cdSchristos 		}
2208bd9f7cdSchristos 		free( subs );
2218bd9f7cdSchristos 	}
2228bd9f7cdSchristos 	if ( submatch ) {
2238bd9f7cdSchristos 		for ( l=0; l<nsub; l++ ) {
2248bd9f7cdSchristos 			free( submatch[nsub].ls_map );
2258bd9f7cdSchristos 		}
2268bd9f7cdSchristos 		free( submatch );
2278bd9f7cdSchristos 	}
2282de962bdSlukem 	free( result );
2292de962bdSlukem 
2302de962bdSlukem 	return s;
2312de962bdSlukem }
2322de962bdSlukem 
2332de962bdSlukem /*
2342de962bdSlukem  * Copies the match referred to by submatch and fetched in string by match.
2352de962bdSlukem  * Helper for rewrite_rule_apply.
2362de962bdSlukem  */
2372de962bdSlukem static int
submatch_copy(struct rewrite_submatch * submatch,const char * string,const regmatch_t * match,struct berval * val)2382de962bdSlukem submatch_copy(
2392de962bdSlukem 		struct rewrite_submatch *submatch,
2402de962bdSlukem 		const char *string,
2412de962bdSlukem 		const regmatch_t *match,
2422de962bdSlukem 		struct berval *val
2432de962bdSlukem )
2442de962bdSlukem {
2452de962bdSlukem 	int		c, l;
2462de962bdSlukem 	const char	*s;
2472de962bdSlukem 
2482de962bdSlukem 	assert( submatch != NULL );
2492de962bdSlukem 	assert( submatch->ls_type == REWRITE_SUBMATCH_ASIS
2502de962bdSlukem 			|| submatch->ls_type == REWRITE_SUBMATCH_XMAP );
2512de962bdSlukem 	assert( string != NULL );
2522de962bdSlukem 	assert( match != NULL );
2532de962bdSlukem 	assert( val != NULL );
2542de962bdSlukem 	assert( val->bv_val == NULL );
2552de962bdSlukem 
2562de962bdSlukem 	c = submatch->ls_submatch;
2572de962bdSlukem 	s = string + match[ c ].rm_so;
2582de962bdSlukem 	l = match[ c ].rm_eo - match[ c ].rm_so;
2592de962bdSlukem 
2602de962bdSlukem 	val->bv_len = l;
2612de962bdSlukem 	val->bv_val = malloc( l + 1 );
2622de962bdSlukem 	if ( val->bv_val == NULL ) {
2632de962bdSlukem 		return REWRITE_ERR;
2642de962bdSlukem 	}
2652de962bdSlukem 
2662de962bdSlukem 	AC_MEMCPY( val->bv_val, s, l );
2672de962bdSlukem 	val->bv_val[ l ] = '\0';
2682de962bdSlukem 
2692de962bdSlukem 	return REWRITE_SUCCESS;
2702de962bdSlukem }
2712de962bdSlukem 
2722de962bdSlukem /*
2732de962bdSlukem  * Substitutes a portion of rewritten string according to substitution
2742de962bdSlukem  * pattern using submatches
2752de962bdSlukem  */
2762de962bdSlukem int
rewrite_subst_apply(struct rewrite_info * info,struct rewrite_op * op,struct rewrite_subst * subst,const char * string,const regmatch_t * match,struct berval * val)2772de962bdSlukem rewrite_subst_apply(
2782de962bdSlukem 		struct rewrite_info *info,
2792de962bdSlukem 		struct rewrite_op *op,
2802de962bdSlukem 		struct rewrite_subst *subst,
2812de962bdSlukem 		const char *string,
2822de962bdSlukem 		const regmatch_t *match,
2832de962bdSlukem 		struct berval *val
2842de962bdSlukem )
2852de962bdSlukem {
2862de962bdSlukem 	struct berval *submatch = NULL;
2872de962bdSlukem 	char *res = NULL;
2882de962bdSlukem 	int n = 0, l, cl;
2892de962bdSlukem 	int rc = REWRITE_REGEXEC_OK;
2902de962bdSlukem 
2912de962bdSlukem 	assert( info != NULL );
2922de962bdSlukem 	assert( op != NULL );
2932de962bdSlukem 	assert( subst != NULL );
2942de962bdSlukem 	assert( string != NULL );
2952de962bdSlukem 	assert( match != NULL );
2962de962bdSlukem 	assert( val != NULL );
2972de962bdSlukem 
2982de962bdSlukem 	assert( val->bv_val == NULL );
2992de962bdSlukem 
3002de962bdSlukem 	val->bv_val = NULL;
3012de962bdSlukem 	val->bv_len = 0;
3022de962bdSlukem 
3032de962bdSlukem 	/*
3042de962bdSlukem 	 * Prepare room for submatch expansion
3052de962bdSlukem 	 */
3062de962bdSlukem 	if ( subst->lt_num_submatch > 0 ) {
3072de962bdSlukem 		submatch = calloc( sizeof( struct berval ),
3082de962bdSlukem 				subst->lt_num_submatch );
3092de962bdSlukem 		if ( submatch == NULL ) {
3102de962bdSlukem 			return REWRITE_REGEXEC_ERR;
3112de962bdSlukem 		}
3122de962bdSlukem 	}
3132de962bdSlukem 
3142de962bdSlukem 	/*
3152de962bdSlukem 	 * Resolve submatches (simple subst, map expansion and so).
3162de962bdSlukem 	 */
3172de962bdSlukem 	for ( n = 0, l = 0; n < subst->lt_num_submatch; n++ ) {
3182de962bdSlukem 		struct berval	key = { 0, NULL };
3192de962bdSlukem 
3202de962bdSlukem 		submatch[ n ].bv_val = NULL;
3212de962bdSlukem 
3222de962bdSlukem 		/*
3232de962bdSlukem 		 * Get key
3242de962bdSlukem 		 */
3252de962bdSlukem 		switch ( subst->lt_submatch[ n ].ls_type ) {
3262de962bdSlukem 		case REWRITE_SUBMATCH_ASIS:
3272de962bdSlukem 		case REWRITE_SUBMATCH_XMAP:
3282de962bdSlukem 			rc = submatch_copy( &subst->lt_submatch[ n ],
3292de962bdSlukem 					string, match, &key );
3302de962bdSlukem 			if ( rc != REWRITE_SUCCESS ) {
3312de962bdSlukem 				rc = REWRITE_REGEXEC_ERR;
3322de962bdSlukem 				goto cleanup;
3332de962bdSlukem 			}
3342de962bdSlukem 			break;
3352de962bdSlukem 
3362de962bdSlukem 		case REWRITE_SUBMATCH_MAP_W_ARG:
3372de962bdSlukem 			switch ( subst->lt_submatch[ n ].ls_map->lm_type ) {
3382de962bdSlukem 			case REWRITE_MAP_GET_OP_VAR:
3392de962bdSlukem 			case REWRITE_MAP_GET_SESN_VAR:
3402de962bdSlukem 			case REWRITE_MAP_GET_PARAM:
3412de962bdSlukem 				rc = REWRITE_SUCCESS;
3422de962bdSlukem 				break;
3432de962bdSlukem 
3442de962bdSlukem 			default:
3452de962bdSlukem 				rc = rewrite_subst_apply( info, op,
3462de962bdSlukem 					subst->lt_submatch[ n ].ls_map->lm_subst,
3472de962bdSlukem 					string, match, &key);
3482de962bdSlukem 			}
3492de962bdSlukem 
3502de962bdSlukem 			if ( rc != REWRITE_SUCCESS ) {
3512de962bdSlukem 				goto cleanup;
3522de962bdSlukem 			}
3532de962bdSlukem 			break;
3542de962bdSlukem 
3552de962bdSlukem 		default:
356*cf1d77f7Schristos 			Debug( LDAP_DEBUG_ANY, "Not Implemented\n" );
3572de962bdSlukem 			rc = REWRITE_ERR;
3582de962bdSlukem 			break;
3592de962bdSlukem 		}
3602de962bdSlukem 
3612de962bdSlukem 		if ( rc != REWRITE_SUCCESS ) {
3622de962bdSlukem 			rc = REWRITE_REGEXEC_ERR;
3632de962bdSlukem 			goto cleanup;
3642de962bdSlukem 		}
3652de962bdSlukem 
3662de962bdSlukem 		/*
3672de962bdSlukem 		 * Resolve key
3682de962bdSlukem 		 */
3692de962bdSlukem 		switch ( subst->lt_submatch[ n ].ls_type ) {
3702de962bdSlukem 		case REWRITE_SUBMATCH_ASIS:
3712de962bdSlukem 			submatch[ n ] = key;
3722de962bdSlukem 			rc = REWRITE_SUCCESS;
3732de962bdSlukem 			break;
3742de962bdSlukem 
3752de962bdSlukem 		case REWRITE_SUBMATCH_XMAP:
3762de962bdSlukem 			rc = rewrite_xmap_apply( info, op,
3772de962bdSlukem 					subst->lt_submatch[ n ].ls_map,
3782de962bdSlukem 					&key, &submatch[ n ] );
3792de962bdSlukem 			free( key.bv_val );
3802de962bdSlukem 			key.bv_val = NULL;
3812de962bdSlukem 			break;
3822de962bdSlukem 
3832de962bdSlukem 		case REWRITE_SUBMATCH_MAP_W_ARG:
3842de962bdSlukem 			rc = rewrite_map_apply( info, op,
3852de962bdSlukem 					subst->lt_submatch[ n ].ls_map,
3862de962bdSlukem 					&key, &submatch[ n ] );
3872de962bdSlukem 			free( key.bv_val );
3882de962bdSlukem 			key.bv_val = NULL;
3892de962bdSlukem 			break;
3902de962bdSlukem 
3912de962bdSlukem 		default:
3922de962bdSlukem 			/*
3932de962bdSlukem 			 * When implemented, this might return the
3942de962bdSlukem                          * exit status of a rewrite context,
3952de962bdSlukem                          * which may include a stop, or an
3962de962bdSlukem                          * unwilling to perform
3972de962bdSlukem                          */
3982de962bdSlukem 			rc = REWRITE_ERR;
3992de962bdSlukem 			break;
4002de962bdSlukem 		}
4012de962bdSlukem 
4022de962bdSlukem 		if ( rc != REWRITE_SUCCESS ) {
4032de962bdSlukem 			rc = REWRITE_REGEXEC_ERR;
4042de962bdSlukem 			goto cleanup;
4052de962bdSlukem 		}
4062de962bdSlukem 
4072de962bdSlukem 		/*
4082de962bdSlukem                  * Increment the length of the resulting string
4092de962bdSlukem                  */
4102de962bdSlukem 		l += submatch[ n ].bv_len;
4112de962bdSlukem 	}
4122de962bdSlukem 
4132de962bdSlukem 	/*
4142de962bdSlukem          * Alloc result buffer
4152de962bdSlukem          */
4162de962bdSlukem 	l += subst->lt_subs_len;
4172de962bdSlukem 	res = malloc( l + 1 );
4182de962bdSlukem 	if ( res == NULL ) {
4192de962bdSlukem 		rc = REWRITE_REGEXEC_ERR;
4202de962bdSlukem 		goto cleanup;
4212de962bdSlukem 	}
4222de962bdSlukem 
4232de962bdSlukem 	/*
4242de962bdSlukem 	 * Apply submatches (possibly resolved thru maps)
4252de962bdSlukem 	 */
4262de962bdSlukem         for ( n = 0, cl = 0; n < subst->lt_num_submatch; n++ ) {
4272de962bdSlukem 		if ( subst->lt_subs[ n ].bv_val != NULL ) {
4282de962bdSlukem                 	AC_MEMCPY( res + cl, subst->lt_subs[ n ].bv_val,
4292de962bdSlukem 					subst->lt_subs[ n ].bv_len );
4302de962bdSlukem 			cl += subst->lt_subs[ n ].bv_len;
4312de962bdSlukem 		}
4322de962bdSlukem 		AC_MEMCPY( res + cl, submatch[ n ].bv_val,
4332de962bdSlukem 				submatch[ n ].bv_len );
4342de962bdSlukem 		cl += submatch[ n ].bv_len;
4352de962bdSlukem 	}
4362de962bdSlukem 	if ( subst->lt_subs[ n ].bv_val != NULL ) {
4372de962bdSlukem 		AC_MEMCPY( res + cl, subst->lt_subs[ n ].bv_val,
4382de962bdSlukem 				subst->lt_subs[ n ].bv_len );
4392de962bdSlukem 		cl += subst->lt_subs[ n ].bv_len;
4402de962bdSlukem 	}
4412de962bdSlukem 	res[ cl ] = '\0';
4422de962bdSlukem 
4432de962bdSlukem 	val->bv_val = res;
4442de962bdSlukem 	val->bv_len = l;
4452de962bdSlukem 
4462de962bdSlukem cleanup:;
4472de962bdSlukem 	if ( submatch ) {
4482de962bdSlukem         	for ( ; --n >= 0; ) {
4492de962bdSlukem 			if ( submatch[ n ].bv_val ) {
4502de962bdSlukem 				free( submatch[ n ].bv_val );
4512de962bdSlukem 			}
4522de962bdSlukem 		}
4532de962bdSlukem 		free( submatch );
4542de962bdSlukem 	}
4552de962bdSlukem 
4562de962bdSlukem 	return rc;
4572de962bdSlukem }
4582de962bdSlukem 
4592de962bdSlukem /*
4602de962bdSlukem  * frees data
4612de962bdSlukem  */
4622de962bdSlukem int
rewrite_subst_destroy(struct rewrite_subst ** psubst)4632de962bdSlukem rewrite_subst_destroy(
4642de962bdSlukem 		struct rewrite_subst **psubst
4652de962bdSlukem )
4662de962bdSlukem {
4672de962bdSlukem 	int			n;
4682de962bdSlukem 	struct rewrite_subst	*subst;
4692de962bdSlukem 
4702de962bdSlukem 	assert( psubst != NULL );
4712de962bdSlukem 	assert( *psubst != NULL );
4722de962bdSlukem 
4732de962bdSlukem 	subst = *psubst;
4742de962bdSlukem 
4752de962bdSlukem 	for ( n = 0; n < subst->lt_num_submatch; n++ ) {
4762de962bdSlukem 		if ( subst->lt_subs[ n ].bv_val ) {
4772de962bdSlukem 			free( subst->lt_subs[ n ].bv_val );
4782de962bdSlukem 			subst->lt_subs[ n ].bv_val = NULL;
4792de962bdSlukem 		}
4802de962bdSlukem 
4812de962bdSlukem 		switch ( subst->lt_submatch[ n ].ls_type ) {
4822de962bdSlukem 		case REWRITE_SUBMATCH_ASIS:
4832de962bdSlukem 			break;
4842de962bdSlukem 
4852de962bdSlukem 		case REWRITE_SUBMATCH_XMAP:
4862de962bdSlukem 			rewrite_xmap_destroy( &subst->lt_submatch[ n ].ls_map );
4872de962bdSlukem 			break;
4882de962bdSlukem 
4892de962bdSlukem 		case REWRITE_SUBMATCH_MAP_W_ARG:
4902de962bdSlukem 			rewrite_map_destroy( &subst->lt_submatch[ n ].ls_map );
4912de962bdSlukem 			break;
4922de962bdSlukem 
4932de962bdSlukem 		default:
4942de962bdSlukem 			break;
4952de962bdSlukem 		}
4962de962bdSlukem 	}
4972de962bdSlukem 
4982de962bdSlukem 	free( subst->lt_submatch );
4992de962bdSlukem 	subst->lt_submatch = NULL;
5002de962bdSlukem 
5012de962bdSlukem 	/* last one */
5022de962bdSlukem 	if ( subst->lt_subs[ n ].bv_val ) {
5032de962bdSlukem 		free( subst->lt_subs[ n ].bv_val );
5042de962bdSlukem 		subst->lt_subs[ n ].bv_val = NULL;
5052de962bdSlukem 	}
5062de962bdSlukem 
5072de962bdSlukem 	free( subst->lt_subs );
5082de962bdSlukem 	subst->lt_subs = NULL;
5092de962bdSlukem 
5102de962bdSlukem 	free( subst );
5112de962bdSlukem 	*psubst = NULL;
5122de962bdSlukem 
5132de962bdSlukem 	return 0;
5142de962bdSlukem }
5152de962bdSlukem 
516