]> git.ipfire.org Git - thirdparty/squid.git/blob - src/String.cc
45e24c94828b90130d427a161622b6f7f6214bcf
[thirdparty/squid.git] / src / String.cc
1 /*
2 * Copyright (C) 1996-2025 The Squid Software Foundation and contributors
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
9 #include "squid.h"
10 #include "mem/forward.h"
11 #include "sbuf/SBuf.h"
12 #include "SquidString.h"
13
14 #include <climits>
15
16 // low-level buffer allocation,
17 // does not free old buffer and does not adjust or look at len_
18 void
19 String::allocBuffer(String::size_type sz)
20 {
21 assert (undefined());
22 auto *newBuffer = static_cast<char*>(memAllocBuf(sz, &sz));
23 setBuffer(newBuffer, sz);
24 }
25
26 // low-level buffer assignment
27 // does not free old buffer and does not adjust or look at len_
28 void
29 String::setBuffer(char *aBuf, String::size_type aSize)
30 {
31 assert(undefined());
32 assert(aSize <= SizeMax_);
33 buf_ = aBuf;
34 size_ = aSize;
35 }
36
37 String::String(char const *aString)
38 {
39 if (aString)
40 allocAndFill(aString, strlen(aString));
41 }
42
43 String &
44 String::operator =(char const *aString)
45 {
46 reset(aString);
47 return *this;
48 }
49
50 String &
51 String::operator =(String const &old)
52 {
53 clean(); // TODO: optimize to avoid cleaning the buffer we can use
54 if (old.size() > 0)
55 allocAndFill(old.rawBuf(), old.size());
56 return *this;
57 }
58
59 bool
60 String::operator ==(String const &that) const
61 {
62 if (0 == this->cmp(that))
63 return true;
64
65 return false;
66 }
67
68 bool
69 String::operator !=(String const &that) const
70 {
71 if (0 == this->cmp(that))
72 return false;
73
74 return true;
75 }
76
77 // public interface, makes sure that we clean the old buffer first
78 void
79 String::assign(const char *str, int len)
80 {
81 clean(); // TODO: optimize to avoid cleaning the buffer we can use
82 allocAndFill(str, len);
83 }
84
85 // Allocates the buffer to fit the supplied string and fills it.
86 // Does not clean.
87 void
88 String::allocAndFill(const char *str, int len)
89 {
90 assert(str);
91 allocBuffer(len + 1);
92 len_ = len;
93 memcpy(buf_, str, len);
94 buf_[len] = '\0';
95 }
96
97 String::String(String const &old) : size_(0), len_(0), buf_(nullptr)
98 {
99 if (old.size() > 0)
100 allocAndFill(old.rawBuf(), old.size());
101 }
102
103 void
104 String::clean()
105 {
106 /* TODO if mempools has already closed this will FAIL!! */
107 if (defined())
108 memFreeBuf(size_, buf_);
109
110 len_ = 0;
111
112 size_ = 0;
113
114 buf_ = nullptr;
115 }
116
117 String::~String()
118 {
119 clean();
120 }
121
122 void
123 String::reset(char const *str)
124 {
125 clean(); // TODO: optimize to avoid cleaning the buffer if we can reuse it
126 if (str)
127 allocAndFill(str, strlen(str));
128 }
129
130 void
131 String::append( char const *str, int len)
132 {
133 assert(str && len >= 0);
134
135 if (len_ + len + 1 /*'\0'*/ < size_) {
136 xstrncpy(buf_+len_, str, len+1);
137 len_ += len;
138 } else {
139 // Create a temporary string and absorb it later.
140 String snew;
141 assert(canGrowBy(len)); // otherwise snew.len_ may overflow below
142 snew.len_ = len_ + len;
143 snew.allocBuffer(snew.len_ + 1);
144
145 if (len_)
146 memcpy(snew.buf_, rawBuf(), len_);
147
148 if (len)
149 memcpy(snew.buf_ + len_, str, len);
150
151 snew.buf_[snew.len_] = '\0';
152
153 absorb(snew);
154 }
155 }
156
157 void
158 String::append(char const *str)
159 {
160 assert(str);
161 append(str, strlen(str));
162 }
163
164 void
165 String::append(char const chr)
166 {
167 char myString[2];
168 myString[0]=chr;
169 myString[1]='\0';
170 append(myString, 1);
171 }
172
173 void
174 String::append(String const &old)
175 {
176 append(old.rawBuf(), old.len_);
177 }
178
179 void
180 String::append(const SBuf &buf)
181 {
182 append(buf.rawContent(), buf.length());
183 }
184
185 void
186 String::absorb(String &old)
187 {
188 clean();
189 setBuffer(old.buf_, old.size_);
190 len_ = old.len_;
191 old.size_ = 0;
192 old.buf_ = nullptr;
193 old.len_ = 0;
194 }
195
196 String
197 String::substr(String::size_type from, String::size_type to) const
198 {
199 // Must(from >= 0 && from < size());
200 Must(from < size());
201 Must(to > 0 && to <= size());
202 Must(to > from);
203
204 String rv;
205 rv.assign(rawBuf()+from, to-from);
206 return rv;
207 }
208
209 void
210 String::cut(String::size_type newLength)
211 {
212 // size_type is size_t, unsigned. No need to check for newLength <0
213 if (newLength > len_) return;
214
215 len_ = newLength;
216
217 // buf_ may be nullptr on zero-length strings.
218 if (len_ == 0 && !buf_)
219 return;
220
221 buf_[newLength] = '\0';
222 }
223
224 /// compare NULL and empty strings because str*cmp() may fail on NULL strings
225 /// and because we need to return consistent results for strncmp(count == 0).
226 static bool
227 nilCmp(const bool thisIsNilOrEmpty, const bool otherIsNilOrEmpty, int &result)
228 {
229 if (!thisIsNilOrEmpty && !otherIsNilOrEmpty)
230 return false; // result does not matter
231
232 if (thisIsNilOrEmpty && otherIsNilOrEmpty)
233 result = 0;
234 else if (thisIsNilOrEmpty)
235 result = -1;
236 else // otherIsNilOrEmpty
237 result = +1;
238
239 return true;
240 }
241
242 int
243 String::cmp(char const *aString) const
244 {
245 int result = 0;
246 if (nilCmp(!size(), (!aString || !*aString), result))
247 return result;
248
249 return strcmp(termedBuf(), aString);
250 }
251
252 int
253 String::cmp(char const *aString, String::size_type count) const
254 {
255 int result = 0;
256 if (nilCmp((!size() || !count), (!aString || !*aString || !count), result))
257 return result;
258
259 return strncmp(termedBuf(), aString, count);
260 }
261
262 int
263 String::cmp(String const &aString) const
264 {
265 int result = 0;
266 if (nilCmp(!size(), !aString.size(), result))
267 return result;
268
269 return strcmp(termedBuf(), aString.termedBuf());
270 }
271
272 int
273 String::caseCmp(char const *aString) const
274 {
275 int result = 0;
276 if (nilCmp(!size(), (!aString || !*aString), result))
277 return result;
278
279 return strcasecmp(termedBuf(), aString);
280 }
281
282 int
283 String::caseCmp(char const *aString, String::size_type count) const
284 {
285 int result = 0;
286 if (nilCmp((!size() || !count), (!aString || !*aString || !count), result))
287 return result;
288
289 return strncasecmp(termedBuf(), aString, count);
290 }
291
292 /* TODO: move onto String */
293 int
294 stringHasWhitespace(const char *s)
295 {
296 return strpbrk(s, w_space) != nullptr;
297 }
298
299 /* TODO: move onto String */
300 int
301 stringHasCntl(const char *s)
302 {
303 unsigned char c;
304
305 while ((c = (unsigned char) *s++) != '\0') {
306 if (c <= 0x1f)
307 return 1;
308
309 if (c >= 0x7f && c <= 0x9f)
310 return 1;
311 }
312
313 return 0;
314 }
315
316 /*
317 * Similar to strtok, but has some rudimentary knowledge
318 * of quoting
319 */
320 char *
321 strwordtok(char *buf, char **t)
322 {
323 unsigned char *word = nullptr;
324 unsigned char *p = (unsigned char *) buf;
325 unsigned char *d;
326 unsigned char ch;
327 int quoted = 0;
328
329 if (!p)
330 p = (unsigned char *) *t;
331
332 if (!p)
333 goto error;
334
335 while (*p && xisspace(*p))
336 ++p;
337
338 if (!*p)
339 goto error;
340
341 word = d = p;
342
343 while ((ch = *p)) {
344 switch (ch) {
345
346 case '\\':
347 if (quoted)
348 ++p;
349
350 switch (*p) {
351
352 case 'n':
353 ch = '\n';
354
355 break;
356
357 case 'r':
358 ch = '\r';
359
360 break;
361
362 default:
363 ch = *p;
364
365 break;
366
367 }
368
369 *d = ch;
370 ++d;
371
372 if (ch)
373 ++p;
374
375 break;
376
377 case '"':
378 quoted = !quoted;
379
380 ++p;
381
382 break;
383
384 default:
385 if (!quoted && xisspace(*p)) {
386 ++p;
387 goto done;
388 }
389
390 *d = *p;
391 ++d;
392 ++p;
393 break;
394 }
395 }
396
397 done:
398 *d = '\0';
399
400 error:
401 *t = (char *) p;
402 return (char *) word;
403 }
404
405 const char *
406 checkNullString(const char *p)
407 {
408 return p ? p : "(NULL)";
409 }
410
411 const char *
412 String::pos(char const *aString) const
413 {
414 if (undefined())
415 return nullptr;
416 return strstr(termedBuf(), aString);
417 }
418
419 const char *
420 String::pos(char const ch) const
421 {
422 if (undefined())
423 return nullptr;
424 return strchr(termedBuf(), ch);
425 }
426
427 const char *
428 String::rpos(char const ch) const
429 {
430 if (undefined())
431 return nullptr;
432 return strrchr(termedBuf(), (ch));
433 }
434
435 String::size_type
436 String::find(char const ch) const
437 {
438 const char *c;
439 c=pos(ch);
440 if (c==nullptr)
441 return npos;
442 return c-rawBuf();
443 }
444
445 String::size_type
446 String::find(char const *aString) const
447 {
448 const char *c;
449 c=pos(aString);
450 if (c==nullptr)
451 return npos;
452 return c-rawBuf();
453 }
454
455 String::size_type
456 String::rfind(char const ch) const
457 {
458 const char *c;
459 c=rpos(ch);
460 if (c==nullptr)
461 return npos;
462 return c-rawBuf();
463 }
464