Home | History | Annotate | Line # | Download | only in librewrite
      1 /*	$NetBSD: subst.c,v 1.4 2025/09/05 21:16:23 christos Exp $	*/
      2 
      3 /* $OpenLDAP$ */
      4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
      5  *
      6  * Copyright 2000-2024 The OpenLDAP Foundation.
      7  * All rights reserved.
      8  *
      9  * Redistribution and use in source and binary forms, with or without
     10  * modification, are permitted only as authorized by the OpenLDAP
     11  * Public License.
     12  *
     13  * A copy of this license is available in the file LICENSE in the
     14  * top-level directory of the distribution or, alternatively, at
     15  * <http://www.OpenLDAP.org/license.html>.
     16  */
     17 /* ACKNOWLEDGEMENT:
     18  * This work was initially developed by Pierangelo Masarati for
     19  * inclusion in OpenLDAP Software.
     20  */
     21 
     22 #include <portable.h>
     23 
     24 #include "rewrite-int.h"
     25 
     26 /*
     27  * Compiles a substitution pattern
     28  */
     29 struct rewrite_subst *
     30 rewrite_subst_compile(
     31 		struct rewrite_info *info,
     32 		const char *str
     33 )
     34 {
     35 	size_t subs_len;
     36 	struct berval *subs = NULL, *tmps;
     37 	struct rewrite_submatch *submatch = NULL, *tmpsm;
     38 
     39 	struct rewrite_subst *s = NULL;
     40 
     41 	char *result, *begin, *p;
     42 	int nsub = 0, l;
     43 
     44 	assert( info != NULL );
     45 	assert( str != NULL );
     46 
     47 	result = strdup( str );
     48 	if ( result == NULL ) {
     49 		return NULL;
     50 	}
     51 
     52 	/*
     53 	 * Take care of substitution string
     54 	 */
     55 	for ( p = begin = result, subs_len = 0; p[ 0 ] != '\0'; p++ ) {
     56 
     57 		/*
     58 		 * Keep only single escapes '%'
     59 		 */
     60 		if (  !IS_REWRITE_SUBMATCH_ESCAPE( p[ 0 ] ) ) {
     61 			continue;
     62 		}
     63 
     64 		if (  IS_REWRITE_SUBMATCH_ESCAPE( p[ 1 ] ) ) {
     65 			/* Pull &p[1] over p, including the trailing '\0' */
     66 			AC_MEMCPY((char *)p, &p[ 1 ], strlen( p ) );
     67 			continue;
     68 		}
     69 
     70 		tmps = ( struct berval * )realloc( subs,
     71 				sizeof( struct berval )*( nsub + 1 ) );
     72 		if ( tmps == NULL ) {
     73 			goto cleanup;
     74 		}
     75 		subs = tmps;
     76 		subs[ nsub ].bv_val = NULL;
     77 
     78 		tmpsm = ( struct rewrite_submatch * )realloc( submatch,
     79 				sizeof( struct rewrite_submatch )*( nsub + 1 ) );
     80 		if ( tmpsm == NULL ) {
     81 			goto cleanup;
     82 		}
     83 		submatch = tmpsm;
     84 		submatch[ nsub ].ls_map = NULL;
     85 
     86 		/*
     87 		 * I think an `if l > 0' at runtime is better outside than
     88 		 * inside a function call ...
     89 		 */
     90 		l = p - begin;
     91 		if ( l > 0 ) {
     92 			subs_len += l;
     93 			subs[ nsub ].bv_len = l;
     94 			subs[ nsub ].bv_val = malloc( l + 1 );
     95 			if ( subs[ nsub ].bv_val == NULL ) {
     96 				goto cleanup;
     97 			}
     98 			AC_MEMCPY( subs[ nsub ].bv_val, begin, l );
     99 			subs[ nsub ].bv_val[ l ] = '\0';
    100 		} else {
    101 			subs[ nsub ].bv_val = NULL;
    102 			subs[ nsub ].bv_len = 0;
    103 		}
    104 
    105 		/*
    106 		 * Substitution pattern
    107 		 */
    108 		if ( isdigit( (unsigned char) p[ 1 ] ) ) {
    109 			int d = p[ 1 ] - '0';
    110 
    111 			/*
    112 			 * Add a new value substitution scheme
    113 			 */
    114 
    115 			submatch[ nsub ].ls_submatch = d;
    116 
    117 			/*
    118 			 * If there is no argument, use default
    119 			 * (substitute substring as is)
    120 			 */
    121 			if ( p[ 2 ] != '{' ) {
    122 				submatch[ nsub ].ls_type =
    123 					REWRITE_SUBMATCH_ASIS;
    124 				submatch[ nsub ].ls_map = NULL;
    125 				begin = ++p + 1;
    126 
    127 			} else {
    128 				struct rewrite_map *map;
    129 
    130 				submatch[ nsub ].ls_type =
    131 					REWRITE_SUBMATCH_XMAP;
    132 
    133 				map = rewrite_xmap_parse( info,
    134 						p + 3, (const char **)&begin );
    135 				if ( map == NULL ) {
    136 					nsub++;	/* make sure subs[nsub] is freed */
    137 					goto cleanup;
    138 				}
    139 				submatch[ nsub ].ls_map = map;
    140 				p = begin - 1;
    141 			}
    142 
    143 		/*
    144 		 * Map with args ...
    145 		 */
    146 		} else if ( p[ 1 ] == '{' ) {
    147 			struct rewrite_map *map;
    148 
    149 			map = rewrite_map_parse( info, p + 2,
    150 					(const char **)&begin );
    151 			if ( map == NULL ) {
    152 				nsub++;	/* make sure subs[nsub] is freed */
    153 				goto cleanup;
    154 			}
    155 			p = begin - 1;
    156 
    157 			/*
    158 			 * Add a new value substitution scheme
    159 			 */
    160 			submatch[ nsub ].ls_type =
    161 				REWRITE_SUBMATCH_MAP_W_ARG;
    162 			submatch[ nsub ].ls_map = map;
    163 
    164 		/*
    165 		 * Escape '%' ...
    166 		 */
    167 		} else if ( p[ 1 ] == '%' ) {
    168 			AC_MEMCPY( &p[ 1 ], &p[ 2 ], strlen( &p[ 1 ] ) );
    169 			continue;
    170 
    171 		} else {
    172 			nsub++;	/* make sure subs[nsub] is freed */
    173 			goto cleanup;
    174 		}
    175 
    176 		nsub++;
    177 	}
    178 
    179 	/*
    180 	 * Last part of string
    181 	 */
    182 	tmps = (struct berval * )realloc( subs, sizeof( struct berval )*( nsub + 1 ) );
    183 	if ( tmps == NULL ) {
    184 		goto cleanup;
    185 	}
    186 	subs = tmps;
    187 	l = p - begin;
    188 	if ( l > 0 ) {
    189 		subs_len += l;
    190 		subs[ nsub ].bv_len = l;
    191 		subs[ nsub ].bv_val = malloc( l + 1 );
    192 		if ( subs[ nsub ].bv_val == NULL ) {
    193 			goto cleanup;
    194 		}
    195 		AC_MEMCPY( subs[ nsub ].bv_val, begin, l );
    196 		subs[ nsub ].bv_val[ l ] = '\0';
    197 	} else {
    198 		subs[ nsub ].bv_val = NULL;
    199 		subs[ nsub ].bv_len = 0;
    200 	}
    201 
    202 	s = calloc( sizeof( struct rewrite_subst ), 1 );
    203 	if ( s == NULL ) {
    204 		nsub++;	/* make sure last elements are freed */
    205 		goto cleanup;
    206 	}
    207 
    208 	s->lt_subs_len = subs_len;
    209 	s->lt_subs = subs;
    210 	s->lt_num_submatch = nsub;
    211 	s->lt_submatch = submatch;
    212 	subs = NULL;
    213 	submatch = NULL;
    214 
    215 cleanup:;
    216 	if ( subs ) {
    217 		for ( l=0; l<nsub; l++ ) {
    218 			free( subs[l].bv_val );
    219 		}
    220 		free( subs );
    221 	}
    222 	if ( submatch ) {
    223 		for ( l=0; l<nsub; l++ ) {
    224 			free( submatch[l].ls_map );
    225 		}
    226 		free( submatch );
    227 	}
    228 	free( result );
    229 
    230 	return s;
    231 }
    232 
    233 /*
    234  * Copies the match referred to by submatch and fetched in string by match.
    235  * Helper for rewrite_rule_apply.
    236  */
    237 static int
    238 submatch_copy(
    239 		struct rewrite_submatch *submatch,
    240 		const char *string,
    241 		const regmatch_t *match,
    242 		struct berval *val
    243 )
    244 {
    245 	int		c, l;
    246 	const char	*s;
    247 
    248 	assert( submatch != NULL );
    249 	assert( submatch->ls_type == REWRITE_SUBMATCH_ASIS
    250 			|| submatch->ls_type == REWRITE_SUBMATCH_XMAP );
    251 	assert( string != NULL );
    252 	assert( match != NULL );
    253 	assert( val != NULL );
    254 	assert( val->bv_val == NULL );
    255 
    256 	c = submatch->ls_submatch;
    257 	s = string + match[ c ].rm_so;
    258 	l = match[ c ].rm_eo - match[ c ].rm_so;
    259 
    260 	val->bv_len = l;
    261 	val->bv_val = malloc( l + 1 );
    262 	if ( val->bv_val == NULL ) {
    263 		return REWRITE_ERR;
    264 	}
    265 
    266 	AC_MEMCPY( val->bv_val, s, l );
    267 	val->bv_val[ l ] = '\0';
    268 
    269 	return REWRITE_SUCCESS;
    270 }
    271 
    272 /*
    273  * Substitutes a portion of rewritten string according to substitution
    274  * pattern using submatches
    275  */
    276 int
    277 rewrite_subst_apply(
    278 		struct rewrite_info *info,
    279 		struct rewrite_op *op,
    280 		struct rewrite_subst *subst,
    281 		const char *string,
    282 		const regmatch_t *match,
    283 		struct berval *val
    284 )
    285 {
    286 	struct berval *submatch = NULL;
    287 	char *res = NULL;
    288 	int n = 0, l, cl;
    289 	int rc = REWRITE_REGEXEC_OK;
    290 
    291 	assert( info != NULL );
    292 	assert( op != NULL );
    293 	assert( subst != NULL );
    294 	assert( string != NULL );
    295 	assert( match != NULL );
    296 	assert( val != NULL );
    297 
    298 	assert( val->bv_val == NULL );
    299 
    300 	val->bv_val = NULL;
    301 	val->bv_len = 0;
    302 
    303 	/*
    304 	 * Prepare room for submatch expansion
    305 	 */
    306 	if ( subst->lt_num_submatch > 0 ) {
    307 		submatch = calloc( sizeof( struct berval ),
    308 				subst->lt_num_submatch );
    309 		if ( submatch == NULL ) {
    310 			return REWRITE_REGEXEC_ERR;
    311 		}
    312 	}
    313 
    314 	/*
    315 	 * Resolve submatches (simple subst, map expansion and so).
    316 	 */
    317 	for ( n = 0, l = 0; n < subst->lt_num_submatch; n++ ) {
    318 		struct berval	key = { 0, NULL };
    319 
    320 		submatch[ n ].bv_val = NULL;
    321 
    322 		/*
    323 		 * Get key
    324 		 */
    325 		switch ( subst->lt_submatch[ n ].ls_type ) {
    326 		case REWRITE_SUBMATCH_ASIS:
    327 		case REWRITE_SUBMATCH_XMAP:
    328 			rc = submatch_copy( &subst->lt_submatch[ n ],
    329 					string, match, &key );
    330 			if ( rc != REWRITE_SUCCESS ) {
    331 				rc = REWRITE_REGEXEC_ERR;
    332 				goto cleanup;
    333 			}
    334 			break;
    335 
    336 		case REWRITE_SUBMATCH_MAP_W_ARG:
    337 			switch ( subst->lt_submatch[ n ].ls_map->lm_type ) {
    338 			case REWRITE_MAP_GET_OP_VAR:
    339 			case REWRITE_MAP_GET_SESN_VAR:
    340 			case REWRITE_MAP_GET_PARAM:
    341 				rc = REWRITE_SUCCESS;
    342 				break;
    343 
    344 			default:
    345 				rc = rewrite_subst_apply( info, op,
    346 					subst->lt_submatch[ n ].ls_map->lm_subst,
    347 					string, match, &key);
    348 			}
    349 
    350 			if ( rc != REWRITE_SUCCESS ) {
    351 				goto cleanup;
    352 			}
    353 			break;
    354 
    355 		default:
    356 			Debug( LDAP_DEBUG_ANY, "Not Implemented\n" );
    357 			rc = REWRITE_ERR;
    358 			break;
    359 		}
    360 
    361 		if ( rc != REWRITE_SUCCESS ) {
    362 			rc = REWRITE_REGEXEC_ERR;
    363 			goto cleanup;
    364 		}
    365 
    366 		/*
    367 		 * Resolve key
    368 		 */
    369 		switch ( subst->lt_submatch[ n ].ls_type ) {
    370 		case REWRITE_SUBMATCH_ASIS:
    371 			submatch[ n ] = key;
    372 			rc = REWRITE_SUCCESS;
    373 			break;
    374 
    375 		case REWRITE_SUBMATCH_XMAP:
    376 			rc = rewrite_xmap_apply( info, op,
    377 					subst->lt_submatch[ n ].ls_map,
    378 					&key, &submatch[ n ] );
    379 			free( key.bv_val );
    380 			key.bv_val = NULL;
    381 			break;
    382 
    383 		case REWRITE_SUBMATCH_MAP_W_ARG:
    384 			rc = rewrite_map_apply( info, op,
    385 					subst->lt_submatch[ n ].ls_map,
    386 					&key, &submatch[ n ] );
    387 			free( key.bv_val );
    388 			key.bv_val = NULL;
    389 			break;
    390 
    391 		default:
    392 			/*
    393 			 * When implemented, this might return the
    394                          * exit status of a rewrite context,
    395                          * which may include a stop, or an
    396                          * unwilling to perform
    397                          */
    398 			rc = REWRITE_ERR;
    399 			break;
    400 		}
    401 
    402 		if ( rc != REWRITE_SUCCESS ) {
    403 			rc = REWRITE_REGEXEC_ERR;
    404 			goto cleanup;
    405 		}
    406 
    407 		/*
    408                  * Increment the length of the resulting string
    409                  */
    410 		l += submatch[ n ].bv_len;
    411 	}
    412 
    413 	/*
    414          * Alloc result buffer
    415          */
    416 	l += subst->lt_subs_len;
    417 	res = malloc( l + 1 );
    418 	if ( res == NULL ) {
    419 		rc = REWRITE_REGEXEC_ERR;
    420 		goto cleanup;
    421 	}
    422 
    423 	/*
    424 	 * Apply submatches (possibly resolved thru maps)
    425 	 */
    426         for ( n = 0, cl = 0; n < subst->lt_num_submatch; n++ ) {
    427 		if ( subst->lt_subs[ n ].bv_val != NULL ) {
    428                 	AC_MEMCPY( res + cl, subst->lt_subs[ n ].bv_val,
    429 					subst->lt_subs[ n ].bv_len );
    430 			cl += subst->lt_subs[ n ].bv_len;
    431 		}
    432 		AC_MEMCPY( res + cl, submatch[ n ].bv_val,
    433 				submatch[ n ].bv_len );
    434 		cl += submatch[ n ].bv_len;
    435 	}
    436 	if ( subst->lt_subs[ n ].bv_val != NULL ) {
    437 		AC_MEMCPY( res + cl, subst->lt_subs[ n ].bv_val,
    438 				subst->lt_subs[ n ].bv_len );
    439 		cl += subst->lt_subs[ n ].bv_len;
    440 	}
    441 	res[ cl ] = '\0';
    442 
    443 	val->bv_val = res;
    444 	val->bv_len = l;
    445 
    446 cleanup:;
    447 	if ( submatch ) {
    448         	for ( ; --n >= 0; ) {
    449 			if ( submatch[ n ].bv_val ) {
    450 				free( submatch[ n ].bv_val );
    451 			}
    452 		}
    453 		free( submatch );
    454 	}
    455 
    456 	return rc;
    457 }
    458 
    459 /*
    460  * frees data
    461  */
    462 int
    463 rewrite_subst_destroy(
    464 		struct rewrite_subst **psubst
    465 )
    466 {
    467 	int			n;
    468 	struct rewrite_subst	*subst;
    469 
    470 	assert( psubst != NULL );
    471 	assert( *psubst != NULL );
    472 
    473 	subst = *psubst;
    474 
    475 	for ( n = 0; n < subst->lt_num_submatch; n++ ) {
    476 		if ( subst->lt_subs[ n ].bv_val ) {
    477 			free( subst->lt_subs[ n ].bv_val );
    478 			subst->lt_subs[ n ].bv_val = NULL;
    479 		}
    480 
    481 		switch ( subst->lt_submatch[ n ].ls_type ) {
    482 		case REWRITE_SUBMATCH_ASIS:
    483 			break;
    484 
    485 		case REWRITE_SUBMATCH_XMAP:
    486 			rewrite_xmap_destroy( &subst->lt_submatch[ n ].ls_map );
    487 			break;
    488 
    489 		case REWRITE_SUBMATCH_MAP_W_ARG:
    490 			rewrite_map_destroy( &subst->lt_submatch[ n ].ls_map );
    491 			break;
    492 
    493 		default:
    494 			break;
    495 		}
    496 	}
    497 
    498 	free( subst->lt_submatch );
    499 	subst->lt_submatch = NULL;
    500 
    501 	/* last one */
    502 	if ( subst->lt_subs[ n ].bv_val ) {
    503 		free( subst->lt_subs[ n ].bv_val );
    504 		subst->lt_subs[ n ].bv_val = NULL;
    505 	}
    506 
    507 	free( subst->lt_subs );
    508 	subst->lt_subs = NULL;
    509 
    510 	free( subst );
    511 	*psubst = NULL;
    512 
    513 	return 0;
    514 }
    515 
    516