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