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