]> git.ipfire.org Git - thirdparty/squid.git/blame - src/String.cc
Source Format Enforcement (#763)
[thirdparty/squid.git] / src / String.cc
CommitLineData
ad80164a 1/*
f70aedc4 2 * Copyright (C) 1996-2021 The Squid Software Foundation and contributors
ad80164a 3 *
bbc27441
AJ
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.
ad80164a 7 */
8
bbc27441
AJ
9/* DEBUG: section 67 String */
10
582c2af2 11#include "squid.h"
3d93a84d 12#include "base/TextException.h"
8822ebee 13#include "mgr/Registration.h"
582c2af2 14#include "profiler/Profiler.h"
ad80164a 15#include "Store.h"
696a257c 16
074d6a40 17#include <climits>
582c2af2 18
26ac0430 19// low-level buffer allocation,
e7e1bf60 20// does not free old buffer and does not adjust or look at len_
ad80164a 21void
a7a42b14 22String::allocBuffer(String::size_type sz)
ad80164a 23{
24 PROF_start(StringInitBuf);
cb3b44fc 25 assert (undefined());
e7e1bf60 26 char *newBuffer = (char*)memAllocString(sz, &sz);
27 setBuffer(newBuffer, sz);
ad80164a 28 PROF_stop(StringInitBuf);
29}
30
e7e1bf60 31// low-level buffer assignment
32// does not free old buffer and does not adjust or look at len_
ad80164a 33void
a7a42b14 34String::setBuffer(char *aBuf, String::size_type aSize)
ad80164a 35{
cb3b44fc 36 assert(undefined());
70df76e3 37 assert(aSize <= SizeMax_);
e7e1bf60 38 buf_ = aBuf;
39 size_ = aSize;
ad80164a 40}
41
f63c4b05
AJ
42String::String()
43{
44#if DEBUGSTRINGS
45 StringRegistry::Instance().add(this);
46#endif
47}
48
49String::String(char const *aString)
ad80164a 50{
e7e1bf60 51 if (aString)
52 allocAndFill(aString, strlen(aString));
ad80164a 53#if DEBUGSTRINGS
30abd221 54 StringRegistry::Instance().add(this);
ad80164a 55#endif
56}
57
30abd221 58String &
59String::operator =(char const *aString)
ad80164a 60{
e7e1bf60 61 reset(aString);
ad80164a 62 return *this;
63}
64
30abd221 65String &
9e9ef416 66String::operator =(String const &old)
ad80164a 67{
e7e1bf60 68 clean(); // TODO: optimize to avoid cleaning the buffer we can use
69 if (old.size() > 0)
cb3b44fc 70 allocAndFill(old.rawBuf(), old.size());
ad80164a 71 return *this;
72}
73
74bool
9e9ef416 75String::operator ==(String const &that) const
ad80164a 76{
30abd221 77 if (0 == this->cmp(that))
78 return true;
ad80164a 79
30abd221 80 return false;
ad80164a 81}
82
83bool
9e9ef416 84String::operator !=(String const &that) const
ad80164a 85{
30abd221 86 if (0 == this->cmp(that))
87 return false;
88
89 return true;
ad80164a 90}
91
e7e1bf60 92// public interface, makes sure that we clean the old buffer first
30abd221 93void
2fe0439c 94String::assign(const char *str, int len)
ad80164a 95{
e7e1bf60 96 clean(); // TODO: optimize to avoid cleaning the buffer we can use
97 allocAndFill(str, len);
98}
4fbc2a54 99
e7e1bf60 100// Allocates the buffer to fit the supplied string and fills it.
101// Does not clean.
102void
103String::allocAndFill(const char *str, int len)
104{
105 PROF_start(StringAllocAndFill);
328a07ab 106 assert(str);
e7e1bf60 107 allocBuffer(len + 1);
30abd221 108 len_ = len;
41d00cd3 109 memcpy(buf_, str, len);
30abd221 110 buf_[len] = '\0';
e7e1bf60 111 PROF_stop(StringAllocAndFill);
ad80164a 112}
113
9e9ef416 114String::String(String const &old) : size_(0), len_(0), buf_(NULL)
ad80164a 115{
dcb1fef8 116 if (old.size() > 0)
cb3b44fc 117 allocAndFill(old.rawBuf(), old.size());
30abd221 118#if DEBUGSTRINGS
119
120 StringRegistry::Instance().add(this);
121#endif
ad80164a 122}
123
30abd221 124void
125String::clean()
ad80164a 126{
30abd221 127 PROF_start(StringClean);
30abd221 128
8536ad02 129 /* TODO if mempools has already closed this will FAIL!! */
cb3b44fc 130 if (defined())
30abd221 131 memFreeString(size_, buf_);
132
133 len_ = 0;
134
135 size_ = 0;
136
137 buf_ = NULL;
138 PROF_stop(StringClean);
ad80164a 139}
140
30abd221 141String::~String()
ad80164a 142{
30abd221 143 clean();
ad80164a 144#if DEBUGSTRINGS
145
30abd221 146 StringRegistry::Instance().remove(this);
ad80164a 147#endif
148}
149
150void
9e9ef416 151String::reset(char const *str)
30abd221 152{
153 PROF_start(StringReset);
e7e1bf60 154 clean(); // TODO: optimize to avoid cleaning the buffer if we can reuse it
155 if (str)
156 allocAndFill(str, strlen(str));
30abd221 157 PROF_stop(StringReset);
158}
159
160void
9e9ef416 161String::append( char const *str, int len)
ad80164a 162{
30abd221 163 assert(str && len >= 0);
ad80164a 164
165 PROF_start(StringAppend);
5358a024
AJ
166 if (len_ + len + 1 /*'\0'*/ < size_) {
167 xstrncpy(buf_+len_, str, len+1);
ad80164a 168 len_ += len;
169 } else {
e7e1bf60 170 // Create a temporary string and absorb it later.
30abd221 171 String snew;
70df76e3 172 assert(canGrowBy(len)); // otherwise snew.len_ may overflow below
30abd221 173 snew.len_ = len_ + len;
e7e1bf60 174 snew.allocBuffer(snew.len_ + 1);
ad80164a 175
e7e1bf60 176 if (len_)
41d00cd3 177 memcpy(snew.buf_, rawBuf(), len_);
ad80164a 178
179 if (len)
41d00cd3 180 memcpy(snew.buf_ + len_, str, len);
ad80164a 181
30abd221 182 snew.buf_[snew.len_] = '\0';
ad80164a 183
30abd221 184 absorb(snew);
ad80164a 185 }
186 PROF_stop(StringAppend);
187}
188
189void
30abd221 190String::append(char const *str)
ad80164a 191{
9e9ef416
RP
192 assert(str);
193 append(str, strlen(str));
ad80164a 194}
195
196void
9e9ef416 197String::append(char const chr)
ad80164a 198{
199 char myString[2];
200 myString[0]=chr;
201 myString[1]='\0';
9e9ef416 202 append(myString, 1);
ad80164a 203}
204
205void
30abd221 206String::append(String const &old)
ad80164a 207{
9e9ef416 208 append(old.rawBuf(), old.len_);
ad80164a 209}
210
30abd221 211void
212String::absorb(String &old)
73d41272 213{
30abd221 214 clean();
e7e1bf60 215 setBuffer(old.buf_, old.size_);
30abd221 216 len_ = old.len_;
217 old.size_ = 0;
218 old.buf_ = NULL;
219 old.len_ = 0;
73d41272 220}
221
9b558d8a 222String
a7a42b14 223String::substr(String::size_type from, String::size_type to) const
9b558d8a 224{
1545d2d1
AJ
225// Must(from >= 0 && from < size());
226 Must(from < size());
9b558d8a
FC
227 Must(to > 0 && to <= size());
228 Must(to > from);
229
230 String rv;
2fe0439c 231 rv.assign(rawBuf()+from, to-from);
9b558d8a
FC
232 return rv;
233}
234
f63c4b05
AJ
235void
236String::cut(String::size_type newLength)
237{
238 // size_type is size_t, unsigned. No need to check for newLength <0
239 if (newLength > len_) return;
240
241 len_ = newLength;
242
243 // buf_ may be nullptr on zero-length strings.
244 if (len_ == 0 && !buf_)
245 return;
246
247 buf_[newLength] = '\0';
248}
249
250/// compare NULL and empty strings because str*cmp() may fail on NULL strings
251/// and because we need to return consistent results for strncmp(count == 0).
252static bool
253nilCmp(const bool thisIsNilOrEmpty, const bool otherIsNilOrEmpty, int &result)
254{
255 if (!thisIsNilOrEmpty && !otherIsNilOrEmpty)
256 return false; // result does not matter
257
258 if (thisIsNilOrEmpty && otherIsNilOrEmpty)
259 result = 0;
260 else if (thisIsNilOrEmpty)
261 result = -1;
262 else // otherIsNilOrEmpty
263 result = +1;
264
265 return true;
266}
267
268int
269String::cmp(char const *aString) const
270{
271 int result = 0;
272 if (nilCmp(!size(), (!aString || !*aString), result))
273 return result;
274
275 return strcmp(termedBuf(), aString);
276}
277
278int
279String::cmp(char const *aString, String::size_type count) const
280{
281 int result = 0;
282 if (nilCmp((!size() || !count), (!aString || !*aString || !count), result))
283 return result;
284
285 return strncmp(termedBuf(), aString, count);
286}
287
288int
289String::cmp(String const &aString) const
290{
291 int result = 0;
292 if (nilCmp(!size(), !aString.size(), result))
293 return result;
294
295 return strcmp(termedBuf(), aString.termedBuf());
296}
297
298int
299String::caseCmp(char const *aString) const
300{
301 int result = 0;
302 if (nilCmp(!size(), (!aString || !*aString), result))
303 return result;
304
305 return strcasecmp(termedBuf(), aString);
306}
307
308int
309String::caseCmp(char const *aString, String::size_type count) const
310{
311 int result = 0;
312 if (nilCmp((!size() || !count), (!aString || !*aString || !count), result))
313 return result;
314
315 return strncasecmp(termedBuf(), aString, count);
316}
317
ad80164a 318#if DEBUGSTRINGS
319void
30abd221 320String::stat(StoreEntry *entry) const
ad80164a 321{
cb3b44fc 322 storeAppendPrintf(entry, "%p : %d/%d \"%.*s\"\n",this,len_, size_, size(), rawBuf());
ad80164a 323}
324
30abd221 325StringRegistry &
326StringRegistry::Instance()
ad80164a 327{
328 return Instance_;
329}
330
331template <class C>
332int
333ptrcmp(C const &lhs, C const &rhs)
334{
335 return lhs - rhs;
336}
337
6852be71 338StringRegistry::StringRegistry()
ad80164a 339{
6852be71 340#if DEBUGSTRINGS
8822ebee 341 Mgr::RegisterAction("strings",
d9fc6862 342 "Strings in use in squid", Stat, 0, 1);
6852be71 343#endif
ad80164a 344}
345
346void
6ca34f6f 347StringRegistry::add(String const *entry)
ad80164a 348{
349 entries.insert(entry, ptrcmp);
350}
351
352void
6ca34f6f 353StringRegistry::remove(String const *entry)
ad80164a 354{
355 entries.remove(entry, ptrcmp);
356}
357
30abd221 358StringRegistry StringRegistry::Instance_;
ad80164a 359
82afb125 360String::size_type memStringCount();
ad80164a 361
362void
30abd221 363StringRegistry::Stat(StoreEntry *entry)
ad80164a 364{
365 storeAppendPrintf(entry, "%lu entries, %lu reported from MemPool\n", (unsigned long) Instance().entries.elements, (unsigned long) memStringCount());
366 Instance().entries.head->walk(Stater, entry);
367}
368
369void
30abd221 370StringRegistry::Stater(String const * const & nodedata, void *state)
ad80164a 371{
372 StoreEntry *entry = (StoreEntry *) state;
373 nodedata->stat(entry);
374}
375
376#endif
377
30abd221 378/* TODO: move onto String */
379int
380stringHasWhitespace(const char *s)
381{
382 return strpbrk(s, w_space) != NULL;
383}
384
385/* TODO: move onto String */
386int
387stringHasCntl(const char *s)
388{
389 unsigned char c;
390
391 while ((c = (unsigned char) *s++) != '\0') {
392 if (c <= 0x1f)
393 return 1;
394
395 if (c >= 0x7f && c <= 0x9f)
396 return 1;
397 }
398
399 return 0;
400}
401
ad80164a 402/*
403 * Similar to strtok, but has some rudimentary knowledge
404 * of quoting
405 */
406char *
407strwordtok(char *buf, char **t)
408{
409 unsigned char *word = NULL;
410 unsigned char *p = (unsigned char *) buf;
411 unsigned char *d;
412 unsigned char ch;
413 int quoted = 0;
414
415 if (!p)
416 p = (unsigned char *) *t;
417
418 if (!p)
419 goto error;
420
421 while (*p && xisspace(*p))
5db6bf73 422 ++p;
ad80164a 423
424 if (!*p)
425 goto error;
426
427 word = d = p;
428
429 while ((ch = *p)) {
430 switch (ch) {
431
432 case '\\':
b48a944e
AJ
433 if (quoted)
434 ++p;
ad80164a 435
436 switch (*p) {
437
438 case 'n':
439 ch = '\n';
440
441 break;
442
443 case 'r':
444 ch = '\r';
445
446 break;
447
448 default:
449 ch = *p;
450
451 break;
452
453 }
454
5db6bf73
FC
455 *d = ch;
456 ++d;
ad80164a 457
458 if (ch)
5db6bf73 459 ++p;
ad80164a 460
461 break;
462
463 case '"':
464 quoted = !quoted;
465
5db6bf73 466 ++p;
ad80164a 467
468 break;
469
470 default:
471 if (!quoted && xisspace(*p)) {
5db6bf73 472 ++p;
ad80164a 473 goto done;
474 }
475
aec55359
FC
476 *d = *p;
477 ++d;
478 ++p;
ad80164a 479 break;
480 }
481 }
482
483done:
5db6bf73 484 *d = '\0';
ad80164a 485
486error:
487 *t = (char *) p;
488 return (char *) word;
489}
490
30abd221 491const char *
492checkNullString(const char *p)
493{
494 return p ? p : "(NULL)";
495}
496
826a1fed
FC
497const char *
498String::pos(char const *aString) const
499{
4f4611bf
FC
500 if (undefined())
501 return NULL;
826a1fed
FC
502 return strstr(termedBuf(), aString);
503}
504
505const char *
506String::pos(char const ch) const
507{
4f4611bf
FC
508 if (undefined())
509 return NULL;
826a1fed
FC
510 return strchr(termedBuf(), ch);
511}
512
513const char *
514String::rpos(char const ch) const
515{
4f4611bf
FC
516 if (undefined())
517 return NULL;
826a1fed
FC
518 return strrchr(termedBuf(), (ch));
519}
520
521String::size_type
522String::find(char const ch) const
523{
524 const char *c;
525 c=pos(ch);
526 if (c==NULL)
527 return npos;
528 return c-rawBuf();
529}
530
531String::size_type
532String::find(char const *aString) const
533{
534 const char *c;
535 c=pos(aString);
536 if (c==NULL)
537 return npos;
538 return c-rawBuf();
539}
540
541String::size_type
542String::rfind(char const ch) const
543{
544 const char *c;
545 c=rpos(ch);
546 if (c==NULL)
547 return npos;
548 return c-rawBuf();
549}
550