STRXFRM): Fix trailing \1 optimization if N is one bigger than return
[kopensolaris-gnu/glibc.git] / string / argz-replace.c
1 /* String replacement in an argz vector
2    Copyright (C) 1997, 1998, 2006 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4    Written by Miles Bader <miles@gnu.ai.mit.edu>
5
6    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Lesser General Public
8    License as published by the Free Software Foundation; either
9    version 2.1 of the License, or (at your option) any later version.
10
11    The GNU C Library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Lesser General Public License for more details.
15
16    You should have received a copy of the GNU Lesser General Public
17    License along with the GNU C Library; if not, write to the Free
18    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19    02111-1307 USA.  */
20
21 #include <stdlib.h>
22 #include <string.h>
23 #include <argz.h>
24
25 /* Append BUF, of length BUF_LEN to *TO, of length *TO_LEN, reallocating and
26    updating *TO & *TO_LEN appropriately.  If an allocation error occurs,
27    *TO's old value is freed, and *TO is set to 0.  */
28 static void
29 str_append (char **to, size_t *to_len, const char *buf, const size_t buf_len)
30 {
31   size_t new_len = *to_len + buf_len;
32   char *new_to = realloc (*to, new_len + 1);
33
34   if (new_to)
35     {
36       *((char *) __mempcpy (new_to + *to_len, buf, buf_len)) = '\0';
37       *to = new_to;
38       *to_len = new_len;
39     }
40   else
41     {
42       free (*to);
43       *to = 0;
44     }
45 }
46
47 /* Replace any occurrences of the string STR in ARGZ with WITH, reallocating
48    ARGZ as necessary.  If REPLACE_COUNT is non-zero, *REPLACE_COUNT will be
49    incremented by number of replacements performed.  */
50 error_t
51 __argz_replace (char **argz, size_t *argz_len, const char *str, const char *with,
52                 unsigned *replace_count)
53 {
54   error_t err = 0;
55
56   if (str && *str)
57     {
58       char *arg = 0;
59       char *src = *argz;
60       size_t src_len = *argz_len;
61       char *dst = 0;
62       size_t dst_len = 0;
63       int delayed_copy = 1;     /* True while we've avoided copying anything.  */
64       size_t str_len = strlen (str), with_len = strlen (with);
65
66       while (!err && (arg = argz_next (src, src_len, arg)))
67         {
68           char *match = strstr (arg, str);
69           if (match)
70             {
71               char *from = match + str_len;
72               size_t to_len = match - arg;
73               char *to = __strndup (arg, to_len);
74
75               while (to && from)
76                 {
77                   str_append (&to, &to_len, with, with_len);
78                   if (to)
79                     {
80                       match = strstr (from, str);
81                       if (match)
82                         {
83                           str_append (&to, &to_len, from, match - from);
84                           from = match + str_len;
85                         }
86                       else
87                         {
88                           str_append (&to, &to_len, from, strlen (from));
89                           from = 0;
90                         }
91                     }
92                 }
93
94               if (to)
95                 {
96                   if (delayed_copy)
97                     /* We avoided copying SRC to DST until we found a match;
98                        now that we've done so, copy everything from the start
99                        of SRC.  */
100                     {
101                       if (arg > src)
102                         err = __argz_append (&dst, &dst_len, src, (arg - src));
103                       delayed_copy = 0;
104                     }
105                   if (! err)
106                     err = __argz_add (&dst, &dst_len, to);
107                   free (to);
108                 }
109               else
110                 err = ENOMEM;
111
112               if (replace_count)
113                 (*replace_count)++;
114             }
115           else if (! delayed_copy)
116             err = __argz_add (&dst, &dst_len, arg);
117         }
118
119       if (! err)
120         {
121           if (! delayed_copy)
122             /* We never found any instances of str.  */
123             {
124               free (src);
125               *argz = dst;
126               *argz_len = dst_len;
127             }
128         }
129       else if (dst_len > 0)
130         free (dst);
131     }
132
133   return err;
134 }
135 weak_alias (__argz_replace, argz_replace)