resetting manifest requested domain to floor
[platform/upstream/ed.git] / regex.c
1 /* regex.c: regular expression interface routines for the ed line editor. */
2 /*  GNU ed - The GNU line editor.
3     Copyright (C) 1993, 1994 Andrew Moore, Talke Studio
4     Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012
5     Free Software Foundation, Inc.
6
7     This program is free software: you can redistribute it and/or modify
8     it under the terms of the GNU General Public License as published by
9     the Free Software Foundation, either version 3 of the License, or
10     (at your option) any later version.
11
12     This program is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15     GNU General Public License for more details.
16
17     You should have received a copy of the GNU General Public License
18     along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include <stddef.h>
22 #include <errno.h>
23 #include <sys/types.h>
24 #include <regex.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include "ed.h"
30
31
32 static regex_t * global_pat = 0;
33 static bool patlock = false;    /* if set, pattern not freed by get_compiled_pattern */
34
35 static char * stbuf = 0;        /* substitution template buffer */
36 static int stbufsz = 0;         /* substitution template buffer size */
37 static int stlen = 0;           /* substitution template length */
38
39 static char * rbuf = 0;         /* replace_matching_text buffer */
40 static int rbufsz = 0;          /* replace_matching_text buffer size */
41
42
43 bool prev_pattern( void ) { return global_pat != 0; }
44
45
46 /* translate characters in a string */
47 static void translit_text( char * p, int len, const char from, const char to )
48   {
49   while( --len >= 0 )
50     {
51     if( *p == from ) *p = to;
52     ++p;
53     }
54   }
55
56
57 /* overwrite newlines with ASCII NULs */
58 static void newline_to_nul( char * const s, const int len )
59   { translit_text( s, len, '\n', '\0' ); }
60
61 /* overwrite ASCII NULs with newlines */
62 static void nul_to_newline( char * const s, const int len )
63   { translit_text( s, len, '\0', '\n' ); }
64
65
66 /* expand a POSIX character class */
67 static const char * parse_char_class( const char * p )
68   {
69   char c, d;
70
71   if( *p == '^' ) ++p;
72   if( *p == ']' ) ++p;
73   for( ; *p != ']' && *p != '\n'; ++p )
74     if( *p == '[' && ( ( d = p[1] ) == '.' || d == ':' || d == '=' ) )
75       for( ++p, c = *++p; *p != ']' || c != d; ++p )
76         if( ( c = *p ) == '\n' )
77           return 0;
78   return ( ( *p == ']' ) ? p : 0 );
79   }
80
81
82 /* copy a pattern string from the command buffer; return pointer to the copy */
83 static char * extract_pattern( const char ** const ibufpp, const char delimiter )
84   {
85   static char * buf = 0;
86   static int bufsz = 0;
87   const char * nd = *ibufpp;
88   int len;
89
90   while( *nd != delimiter && *nd != '\n' )
91     {
92     if( *nd == '[' )
93       {
94       nd = parse_char_class( ++nd );
95       if( !nd ) { set_error_msg( "Unbalanced brackets ([])" ); return 0; }
96       }
97     else if( *nd == '\\' && *++nd == '\n' )
98       { set_error_msg( "Trailing backslash (\\)" ); return 0; }
99     ++nd;
100     }
101   len = nd - *ibufpp;
102   if( !resize_buffer( &buf, &bufsz, len + 1 ) ) return 0;
103   memcpy( buf, *ibufpp, len );
104   buf[len] = 0;
105   *ibufpp = nd;
106   if( isbinary() ) nul_to_newline( buf, len );
107   return buf;
108   }
109
110
111 /* return pointer to compiled pattern from command buffer */
112 static regex_t * get_compiled_pattern( const char ** const ibufpp )
113   {
114   static regex_t * exp = 0;
115   const char * exps;
116   const char delimiter = **ibufpp;
117   int n;
118
119   if( delimiter == ' ' )
120     { set_error_msg( "Invalid pattern delimiter" ); return 0; }
121   if( delimiter == '\n' || *++*ibufpp == '\n' || **ibufpp == delimiter )
122     {
123     if( !exp ) set_error_msg( "No previous pattern" );
124     return exp;
125     }
126   exps = extract_pattern( ibufpp, delimiter );
127   if( !exps ) return 0;
128   /* buffer alloc'd && not reserved */
129   if( exp && !patlock ) regfree( exp );
130   else
131     {
132     exp = (regex_t *) malloc( sizeof (regex_t) );
133     if( !exp )
134       {
135       show_strerror( 0, errno );
136       set_error_msg( "Memory exhausted" );
137       return 0;
138       }
139     }
140   patlock = false;
141   n = regcomp( exp, exps, 0 );
142   if( n )
143     {
144     char buf[80];
145     regerror( n, exp, buf, sizeof buf );
146     set_error_msg( buf );
147     free( exp );
148     exp = 0;
149     }
150   return exp;
151   }
152
153
154 /* add line matching a pattern to the global-active list */
155 bool build_active_list( const char ** const ibufpp, const int first_addr,
156                         const int second_addr, const bool match )
157   {
158   const regex_t * pat;
159   const line_t * lp;
160   int addr;
161   const char delimiter = **ibufpp;
162
163   if( delimiter == ' ' || delimiter == '\n' )
164     { set_error_msg( "Invalid pattern delimiter" ); return false; }
165   pat = get_compiled_pattern( ibufpp );
166   if( !pat ) return false;
167   if( **ibufpp == delimiter ) ++*ibufpp;
168   clear_active_list();
169   lp = search_line_node( first_addr );
170   for( addr = first_addr; addr <= second_addr; ++addr, lp = lp->q_forw )
171     {
172     char * const s = get_sbuf_line( lp );
173     if( !s ) return false;
174     if( isbinary() ) nul_to_newline( s, lp->len );
175     if( !regexec( pat, s, 0, 0, 0 ) == match && !set_active_node( lp ) )
176       return false;
177     }
178   return true;
179   }
180
181
182 /* return pointer to copy of substitution template in the command buffer */
183 static char * extract_subst_template( const char ** const ibufpp,
184                                       const bool isglobal )
185   {
186   int i = 0, n = 0;
187   char c;
188   const char delimiter = **ibufpp;
189
190   ++*ibufpp;
191   if( **ibufpp == '%' && (*ibufpp)[1] == delimiter )
192     {
193     ++*ibufpp;
194     if( !stbuf ) set_error_msg( "No previous substitution" );
195     return stbuf;
196     }
197   while( **ibufpp != delimiter )
198     {
199     if( !resize_buffer( &stbuf, &stbufsz, i + 2 ) ) return 0;
200     c = stbuf[i++] = *(*ibufpp)++;
201     if( c == '\n' && **ibufpp == 0 ) { --i, --*ibufpp; break; }
202     if( c == '\\' && ( stbuf[i++] = *(*ibufpp)++ ) == '\n' && !isglobal )
203       {
204       while( ( *ibufpp = get_tty_line( &n ) ) &&
205              ( n == 0 || ( n > 0 && (*ibufpp)[n-1] != '\n' ) ) )
206         clearerr( stdin );
207       if( !*ibufpp ) return 0;
208       }
209     }
210   if( !resize_buffer( &stbuf, &stbufsz, i + 1 ) ) return 0;
211   stbuf[stlen = i] = 0;
212   return stbuf;
213   }
214
215
216 /* extract substitution tail from the command buffer */
217 bool extract_subst_tail( const char ** const ibufpp, int * const gflagsp,
218                          int * const snump, const bool isglobal )
219   {
220   const char delimiter = **ibufpp;
221
222   *gflagsp = *snump = 0;
223   if( delimiter == '\n' ) { stlen = 0; *gflagsp = GPR; return true; }
224   if( !extract_subst_template( ibufpp, isglobal ) ) return false;
225   if( **ibufpp == '\n' ) { *gflagsp = GPR; return true; }
226   if( **ibufpp == delimiter ) ++*ibufpp;
227   if( **ibufpp >= '1' && **ibufpp <= '9' )
228     return parse_int( snump, *ibufpp, ibufpp );
229   if( **ibufpp == 'g' ) { ++*ibufpp; *gflagsp = GSG; }
230   return true;
231   }
232
233
234 /* return the address of the next line matching a pattern in a given
235    direction. wrap around begin/end of editor buffer if necessary */
236 int next_matching_node_addr( const char ** const ibufpp, const bool forward )
237   {
238   const regex_t * const pat = get_compiled_pattern( ibufpp );
239   int addr = current_addr();
240
241   if( !pat ) return -1;
242   do {
243     addr = ( forward ? inc_addr( addr ) : dec_addr( addr ) );
244     if( addr )
245       {
246       const line_t * const lp = search_line_node( addr );
247       char * const s = get_sbuf_line( lp );
248       if( !s ) return -1;
249       if( isbinary() ) nul_to_newline( s, lp->len );
250       if( !regexec( pat, s, 0, 0, 0 ) ) return addr;
251       }
252     }
253   while( addr != current_addr() );
254   set_error_msg( "No match" );
255   return -1;
256   }
257
258
259 bool new_compiled_pattern( const char ** const ibufpp )
260   {
261   regex_t * tpat;
262
263   disable_interrupts();
264   tpat = get_compiled_pattern( ibufpp );
265   if( tpat && tpat != global_pat )
266     {
267     if( global_pat ) { regfree( global_pat ); free( global_pat ); }
268     global_pat = tpat;
269     patlock = true;             /* reserve pattern */
270     }
271   enable_interrupts();
272   return ( tpat ? true : false );
273   }
274
275
276 /* modify text according to a substitution template; return offset to
277    end of modified text */
278 static int apply_subst_template( const char * const boln,
279                                  const regmatch_t * const rm, int offset,
280                                  const int re_nsub )
281   {
282   const char * sub = stbuf;
283
284   for( ; sub - stbuf < stlen; ++sub )
285     {
286     int n;
287     if( *sub == '&' )
288       {
289       int j = rm[0].rm_so; int k = rm[0].rm_eo;
290       if( !resize_buffer( &rbuf, &rbufsz, offset + k - j ) ) return -1;
291       while( j < k ) rbuf[offset++] = boln[j++];
292       }
293     else if( *sub == '\\' && *++sub >= '1' && *sub <= '9' &&
294              ( n = *sub - '0' ) <= re_nsub )
295       {
296       int j = rm[n].rm_so; int k = rm[n].rm_eo;
297       if( !resize_buffer( &rbuf, &rbufsz, offset + k - j ) ) return -1;
298       while( j < k ) rbuf[offset++] = boln[j++];
299       }
300     else
301       {
302       if( !resize_buffer( &rbuf, &rbufsz, offset + 1 ) ) return -1;
303       rbuf[offset++] = *sub;
304       }
305     }
306   if( !resize_buffer( &rbuf, &rbufsz, offset + 1 ) ) return -1;
307   rbuf[offset] = 0;
308   return offset;
309   }
310
311
312 /* replace text matched by a pattern according to a substitution
313    template; return size of the modified text */
314 static int replace_matching_text( const line_t * const lp, const int gflags,
315                                   const int snum )
316   {
317   enum { se_max = 30 }; /* max subexpressions in a regular expression */
318   regmatch_t rm[se_max];
319   char * txt = get_sbuf_line( lp );
320   const char * eot;
321   int i = 0, offset = 0;
322   bool changed = false;
323
324   if( !txt ) return -1;
325   if( isbinary() ) nul_to_newline( txt, lp->len );
326   eot = txt + lp->len;
327   if( !regexec( global_pat, txt, se_max, rm, 0 ) )
328     {
329     int matchno = 0;
330     do {
331       if( !snum || snum == ++matchno )
332         {
333         changed = true; i = rm[0].rm_so;
334         if( !resize_buffer( &rbuf, &rbufsz, offset + i ) ) return -1;
335         if( isbinary() ) newline_to_nul( txt, rm[0].rm_eo );
336         memcpy( rbuf + offset, txt, i ); offset += i;
337         offset = apply_subst_template( txt, rm, offset, global_pat->re_nsub );
338         if( offset < 0 ) return -1;
339         }
340       else
341         {
342         i = rm[0].rm_eo;
343         if( !resize_buffer( &rbuf, &rbufsz, offset + i ) ) return -1;
344         if( isbinary() ) newline_to_nul( txt, i );
345         memcpy( rbuf + offset, txt, i ); offset += i;
346         }
347       txt += rm[0].rm_eo;
348       }
349     while( *txt && ( !changed || ( ( gflags & GSG ) && rm[0].rm_eo ) ) &&
350            !regexec( global_pat, txt, se_max, rm, REG_NOTBOL ) );
351     i = eot - txt;
352     if( !resize_buffer( &rbuf, &rbufsz, offset + i + 2 ) ) return -1;
353     if( i > 0 && !rm[0].rm_eo && ( gflags & GSG ) )
354       { set_error_msg( "Infinite substitution loop" ); return -1; }
355     if( isbinary() ) newline_to_nul( txt, i );
356     memcpy( rbuf + offset, txt, i );
357     memcpy( rbuf + offset + i, "\n", 2 );
358     }
359   return ( changed ? offset + i + 1 : 0 );
360   }
361
362
363 /* for each line in a range, change text matching a pattern according to
364    a substitution template; return false if error */
365 bool search_and_replace( const int first_addr, const int second_addr,
366                          const int gflags, const int snum, const bool isglobal )
367   {
368   int lc;
369   bool match_found = false;
370
371   set_current_addr( first_addr - 1 );
372   for( lc = 0; lc <= second_addr - first_addr; ++lc )
373     {
374     const line_t * const lp = search_line_node( inc_current_addr() );
375     const int size = replace_matching_text( lp, gflags, snum );
376     if( size < 0 ) return false;
377     if( size )
378       {
379       const char * txt = rbuf;
380       const char * const eot = rbuf + size;
381       undo_t * up = 0;
382       disable_interrupts();
383       if( !delete_lines( current_addr(), current_addr(), isglobal ) )
384         { enable_interrupts(); return false; }
385       do {
386         txt = put_sbuf_line( txt, size, current_addr() );
387         if( !txt ) { enable_interrupts(); return false; }
388         if( up ) up->tail = search_line_node( current_addr() );
389         else
390           {
391           up = push_undo_atom( UADD, current_addr(), current_addr() );
392           if( !up ) { enable_interrupts(); return false; }
393           }
394         }
395       while( txt != eot );
396       enable_interrupts();
397       match_found = true;
398       }
399     }
400   if( !match_found && !( gflags & GLB ) )
401     { set_error_msg( "No match" ); return false; }
402   return true;
403   }