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