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