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