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