util: Fix off-by-one error in message about overflow
[samba.git] / lib / util / substitute.c
1 /* 
2    Unix SMB/CIFS implementation.
3    Samba utility functions
4    
5    Copyright (C) Andrew Tridgell 1992-2001
6    Copyright (C) Simo Sorce      2001-2002
7    Copyright (C) Martin Pool     2003
8    Copyright (C) James Peach     2005
9    
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 3 of the License, or
13    (at your option) any later version.
14    
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19    
20    You should have received a copy of the GNU General Public License
21    along with this program.  If not, see <http://www.gnu.org/licenses/>.
22 */
23
24 #include "replace.h"
25 #include "debug.h"
26 #ifndef SAMBA_UTIL_CORE_ONLY
27 #include "charset/charset.h"
28 #else
29 #include "charset_compat.h"
30 #endif
31 #include "substitute.h"
32
33 /**
34  * @file
35  * @brief Substitute utilities.
36  **/
37
38 /**
39  Substitute a string for a pattern in another string. Make sure there is
40  enough room!
41
42  This routine looks for pattern in s and replaces it with
43  insert. It may do multiple replacements or just one.
44
45  Any of " ; ' $ or ` in the insert string are replaced with _
46  if len==0 then the string cannot be extended. This is different from the old
47  use of len==0 which was for no length checks to be done.
48 **/
49
50 static void string_sub2(char *s,const char *pattern, const char *insert, size_t len,
51                         bool remove_unsafe_characters, bool replace_once,
52                         bool allow_trailing_dollar)
53 {
54         char *p;
55         size_t ls, lp, li, i;
56
57         if (!insert || !pattern || !*pattern || !s)
58                 return;
59
60         ls = strlen(s);
61         lp = strlen(pattern);
62         li = strlen(insert);
63
64         if (len == 0)
65                 len = ls + 1; /* len is number of *bytes* */
66
67         while (lp <= ls && (p = strstr_m(s,pattern))) {
68                 if (ls + li - lp >= len) {
69                         DBG_ERR("ERROR: string overflow by "
70                                 "%zu in string_sub(%.50s, %zu)\n",
71                                 ls + li - lp + 1 - len,
72                                 pattern,
73                                 len);
74                         break;
75                 }
76                 if (li != lp) {
77                         memmove(p+li,p+lp,strlen(p+lp)+1);
78                 }
79                 for (i=0;i<li;i++) {
80                         switch (insert[i]) {
81                         case '$':
82                                 /* allow a trailing $
83                                  * (as in machine accounts) */
84                                 if (allow_trailing_dollar && (i == li - 1 )) {
85                                         p[i] = insert[i];
86                                         break;
87                                 }
88                                 FALL_THROUGH;
89                         case '`':
90                         case '"':
91                         case '\'':
92                         case ';':
93                         case '%':
94                         case '\r':
95                         case '\n':
96                                 if ( remove_unsafe_characters ) {
97                                         p[i] = '_';
98                                         /* yes this break should be here
99                                          * since we want to fall throw if
100                                          * not replacing unsafe chars */
101                                         break;
102                                 }
103                                 FALL_THROUGH;
104                         default:
105                                 p[i] = insert[i];
106                         }
107                 }
108                 s = p + li;
109                 ls = ls + li - lp;
110
111                 if (replace_once)
112                         break;
113         }
114 }
115
116 void string_sub_once(char *s, const char *pattern,
117                 const char *insert, size_t len)
118 {
119         string_sub2( s, pattern, insert, len, true, true, false );
120 }
121
122 void string_sub(char *s,const char *pattern, const char *insert, size_t len)
123 {
124         string_sub2( s, pattern, insert, len, true, false, false );
125 }
126
127 /**
128  * Talloc'ed version of string_sub
129  */
130 _PUBLIC_ char *string_sub_talloc(TALLOC_CTX *mem_ctx, const char *s, 
131                                 const char *pattern, const char *insert)
132 {
133         const char *p;
134         char *ret;
135         size_t len, alloc_len;
136
137         if (insert == NULL || pattern == NULL || !*pattern || s == NULL)
138                 return NULL;
139
140         /* determine length needed */
141         len = strlen(s);
142         
143         for (p = strstr(s, pattern); p != NULL; 
144              p = strstr(p+strlen(pattern), pattern)) {
145                 len += strlen(insert) - strlen(pattern);
146         }
147
148         alloc_len = MAX(len, strlen(s))+1;
149         ret = talloc_array(mem_ctx, char, alloc_len);
150         if (ret == NULL)
151                 return NULL;
152         strncpy(ret, s, alloc_len);
153         string_sub(ret, pattern, insert, alloc_len);
154
155         ret = talloc_realloc(mem_ctx, ret, char, len+1);
156         if (ret == NULL)
157                 return NULL;
158
159         if (ret[len] != '\0') {
160                 DEBUG(0,("Internal error at %s(%d): string not terminated\n",
161                          __FILE__, __LINE__));
162                 abort();
163         }
164
165         talloc_set_name_const(ret, ret);
166
167         return ret;
168 }
169
170 /**
171  Similar to string_sub() but allows for any character to be substituted. 
172  Use with caution!
173  if len==0 then the string cannot be extended. This is different from the old
174  use of len==0 which was for no length checks to be done.
175 **/
176
177 _PUBLIC_ void all_string_sub(char *s,const char *pattern,const char *insert, size_t len)
178 {
179         char *p;
180         size_t ls,lp,li;
181
182         if (!insert || !pattern || !s)
183                 return;
184
185         ls = strlen(s);
186         lp = strlen(pattern);
187         li = strlen(insert);
188
189         if (!*pattern)
190                 return;
191
192         if (len == 0)
193                 len = ls + 1; /* len is number of *bytes* */
194
195         while (lp <= ls && (p = strstr_m(s,pattern))) {
196                 if (ls + li - lp >= len) {
197                         DBG_ERR("ERROR: string overflow by "
198                                 "%zu in all_string_sub(%.50s, %zu)\n",
199                                 ls + li - lp + 1 - len,
200                                 pattern,
201                                 len);
202                         break;
203                 }
204                 if (li != lp) {
205                         memmove(p+li,p+lp,strlen(p+lp)+1);
206                 }
207                 memcpy(p, insert, li);
208                 s = p + li;
209                 ls = ls + li - lp;
210         }
211 }