]> git.sur5r.net Git - openldap/blob - libraries/librewrite/subst.c
Sync with HEAD
[openldap] / libraries / librewrite / subst.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 2000-2005 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                 AC_MEMCPY( subs[ nsub ].bv_val, begin, l );
197                 subs[ nsub ].bv_val[ l ] = '\0';
198         } else {
199                 subs[ nsub ].bv_val = NULL;
200                 subs[ nsub ].bv_len = 0;
201         }
202
203         s = calloc( sizeof( struct rewrite_subst ), 1 );
204         if ( s == NULL ) {
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
213 cleanup:;
214         free( result );
215
216         return s;
217 }
218
219 /*
220  * Copies the match referred to by submatch and fetched in string by match.
221  * Helper for rewrite_rule_apply.
222  */
223 static int
224 submatch_copy(
225                 struct rewrite_submatch *submatch,
226                 const char *string,
227                 const regmatch_t *match,
228                 struct berval *val
229 )
230 {
231         int             c, l;
232         const char      *s;
233
234         assert( submatch != NULL );
235         assert( submatch->ls_type == REWRITE_SUBMATCH_ASIS
236                         || submatch->ls_type == REWRITE_SUBMATCH_XMAP );
237         assert( string != NULL );
238         assert( match != NULL );
239         assert( val != NULL );
240         assert( val->bv_val == NULL );
241         
242         c = submatch->ls_submatch;
243         s = string + match[ c ].rm_so;
244         l = match[ c ].rm_eo - match[ c ].rm_so;
245         
246         val->bv_len = l;
247         val->bv_val = malloc( l + 1 );
248         if ( val->bv_val == NULL ) {
249                 return REWRITE_ERR;
250         }
251         
252         AC_MEMCPY( val->bv_val, s, l );
253         val->bv_val[ l ] = '\0';
254         
255         return REWRITE_SUCCESS;
256 }
257
258 /*
259  * Substitutes a portion of rewritten string according to substitution
260  * pattern using submatches
261  */
262 int
263 rewrite_subst_apply(
264                 struct rewrite_info *info,
265                 struct rewrite_op *op,
266                 struct rewrite_subst *subst,
267                 const char *string,
268                 const regmatch_t *match,
269                 struct berval *val
270 )
271 {
272         struct berval *submatch = NULL;
273         char *res = NULL;
274         int n = 0, l, cl;
275         int rc = REWRITE_REGEXEC_OK;
276
277         assert( info != NULL );
278         assert( op != NULL );
279         assert( subst != NULL );
280         assert( string != NULL );
281         assert( match != NULL );
282         assert( val != NULL );
283
284         assert( val->bv_val == NULL );
285
286         val->bv_val = NULL;
287         val->bv_len = 0;
288
289         /*
290          * Prepare room for submatch expansion
291          */
292         if ( subst->lt_num_submatch > 0 ) {
293                 submatch = calloc( sizeof( struct berval ),
294                                 subst->lt_num_submatch );
295                 if ( submatch == NULL ) {
296                         return REWRITE_REGEXEC_ERR;
297                 }
298         }
299         
300         /*
301          * Resolve submatches (simple subst, map expansion and so).
302          */
303         for ( n = 0, l = 0; n < subst->lt_num_submatch; n++ ) {
304                 struct berval   key = { 0, NULL };
305
306                 submatch[ n ].bv_val = NULL;
307                 
308                 /*
309                  * Get key
310                  */
311                 switch ( subst->lt_submatch[ n ].ls_type ) {
312                 case REWRITE_SUBMATCH_ASIS:
313                 case REWRITE_SUBMATCH_XMAP:
314                         rc = submatch_copy( &subst->lt_submatch[ n ],
315                                         string, match, &key );
316                         if ( rc != REWRITE_SUCCESS ) {
317                                 rc = REWRITE_REGEXEC_ERR;
318                                 goto cleanup;
319                         }
320                         break;
321                         
322                 case REWRITE_SUBMATCH_MAP_W_ARG:
323                         switch ( subst->lt_submatch[ n ].ls_map->lm_type ) {
324                         case REWRITE_MAP_GET_OP_VAR:
325                         case REWRITE_MAP_GET_SESN_VAR:
326                         case REWRITE_MAP_GET_PARAM:
327                                 rc = REWRITE_SUCCESS;
328                                 break;
329
330                         default:
331                                 rc = rewrite_subst_apply( info, op, 
332                                         subst->lt_submatch[ n ].ls_map->lm_subst,
333                                         string, match, &key);
334                         }
335                         
336                         if ( rc != REWRITE_SUCCESS ) {
337                                 goto cleanup;
338                         }
339                         break;
340
341                 default:
342                         Debug( LDAP_DEBUG_ANY, "Not Implemented\n", 0, 0, 0 );
343                         rc = REWRITE_ERR;
344                         break;
345                 }
346                 
347                 if ( rc != REWRITE_SUCCESS ) {
348                         rc = REWRITE_REGEXEC_ERR;
349                         goto cleanup;
350                 }
351
352                 /*
353                  * Resolve key
354                  */
355                 switch ( subst->lt_submatch[ n ].ls_type ) {
356                 case REWRITE_SUBMATCH_ASIS:
357                         submatch[ n ] = key;
358                         rc = REWRITE_SUCCESS;
359                         break;
360                         
361                 case REWRITE_SUBMATCH_XMAP:
362                         rc = rewrite_xmap_apply( info, op,
363                                         subst->lt_submatch[ n ].ls_map,
364                                         &key, &submatch[ n ] );
365                         free( key.bv_val );
366                         key.bv_val = NULL;
367                         break;
368                         
369                 case REWRITE_SUBMATCH_MAP_W_ARG:
370                         rc = rewrite_map_apply( info, op,
371                                         subst->lt_submatch[ n ].ls_map,
372                                         &key, &submatch[ n ] );
373                         free( key.bv_val );
374                         key.bv_val = NULL;
375                         break;
376
377                 default:
378                         /*
379                          * When implemented, this might return the
380                          * exit status of a rewrite context,
381                          * which may include a stop, or an
382                          * unwilling to perform
383                          */
384                         rc = REWRITE_ERR;
385                         break;
386                 }
387
388                 if ( rc != REWRITE_SUCCESS ) {
389                         rc = REWRITE_REGEXEC_ERR;
390                         goto cleanup;
391                 }
392                 
393                 /*
394                  * Increment the length of the resulting string
395                  */
396                 l += submatch[ n ].bv_len;
397         }
398         
399         /*
400          * Alloc result buffer
401          */
402         l += subst->lt_subs_len;
403         res = malloc( l + 1 );
404         if ( res == NULL ) {
405                 rc = REWRITE_REGEXEC_ERR;
406                 goto cleanup;
407         }
408
409         /*
410          * Apply submatches (possibly resolved thru maps)
411          */
412         for ( n = 0, cl = 0; n < subst->lt_num_submatch; n++ ) {
413                 if ( subst->lt_subs[ n ].bv_val != NULL ) {
414                         AC_MEMCPY( res + cl, subst->lt_subs[ n ].bv_val,
415                                         subst->lt_subs[ n ].bv_len );
416                         cl += subst->lt_subs[ n ].bv_len;
417                 }
418                 AC_MEMCPY( res + cl, submatch[ n ].bv_val, 
419                                 submatch[ n ].bv_len );
420                 cl += submatch[ n ].bv_len;
421         }
422         if ( subst->lt_subs[ n ].bv_val != NULL ) {
423                 AC_MEMCPY( res + cl, subst->lt_subs[ n ].bv_val,
424                                 subst->lt_subs[ n ].bv_len );
425                 cl += subst->lt_subs[ n ].bv_len;
426         }
427         res[ cl ] = '\0';
428
429         val->bv_val = res;
430         val->bv_len = l;
431
432 cleanup:;
433         if ( submatch ) {
434                 for ( ; --n >= 0; ) {
435                         if ( submatch[ n ].bv_val ) {
436                                 free( submatch[ n ].bv_val );
437                         }
438                 }
439                 free( submatch );
440         }
441
442         return rc;
443 }
444
445 /*
446  * frees data
447  */
448 int
449 rewrite_subst_destroy(
450                 struct rewrite_subst **psubst
451 )
452 {
453         int                     n;
454         struct rewrite_subst    *subst;
455
456         assert( psubst != NULL );
457         assert( *psubst != NULL );
458
459         subst = *psubst;
460
461         for ( n = 0; n < subst->lt_num_submatch; n++ ) {
462                 if ( subst->lt_subs[ n ].bv_val ) {
463                         free( subst->lt_subs[ n ].bv_val );
464                         subst->lt_subs[ n ].bv_val = NULL;
465                 }
466
467                 switch ( subst->lt_submatch[ n ].ls_type ) {
468                 case REWRITE_SUBMATCH_ASIS:
469                         break;
470
471                 case REWRITE_SUBMATCH_XMAP:
472                         rewrite_xmap_destroy( &subst->lt_submatch[ n ].ls_map );
473                         break;
474
475                 case REWRITE_SUBMATCH_MAP_W_ARG:
476                         rewrite_map_destroy( &subst->lt_submatch[ n ].ls_map );
477                         break;
478
479                 default:
480                         break;
481                 }
482         }
483
484         free( subst->lt_submatch );
485         subst->lt_submatch = NULL;
486
487         /* last one */
488         if ( subst->lt_subs[ n ].bv_val ) {
489                 free( subst->lt_subs[ n ].bv_val );
490                 subst->lt_subs[ n ].bv_val = NULL;
491         }
492
493         free( subst->lt_subs );
494         subst->lt_subs = NULL;
495
496         free( subst );
497         *psubst = NULL;
498
499         return 0;
500 }
501