Mercurial > dovecot > original-hg > dovecot-1.2
annotate src/lib/strfuncs.c @ 8:2d8711a043a0 HEAD
added some extra asserts
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Thu, 22 Aug 2002 01:45:34 +0300 |
parents | 1b34ec11fff8 |
children | d493b9cc265e |
rev | line source |
---|---|
0 | 1 /* |
2 strfuncs.c : String manipulation functions (note: LGPL, because the ) | |
3 | |
4 Copyright (C) 2001-2002 Timo Sirainen | |
5 | |
6 printf_string_upper_bound() code is taken from GLIB: | |
7 Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald | |
8 Modified by the GLib Team and others 1997-1999. | |
9 | |
10 | |
11 This library is free software; you can redistribute it and/or | |
12 modify it under the terms of the GNU Library General Public | |
13 License as published by the Free Software Foundation; either | |
14 version 2 of the License, or (at your option) any later version. | |
15 | |
16 This library is distributed in the hope that it will be useful, | |
17 but WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
19 Library General Public License for more details. | |
20 | |
21 You should have received a copy of the GNU Library General Public | |
22 License along with this library; if not, write to the | |
23 Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
24 Boston, MA 02111-1307, USA. | |
25 */ | |
26 | |
27 #include "lib.h" | |
28 #include "strfuncs.h" | |
29 | |
30 #include <stdio.h> | |
31 #include <limits.h> | |
32 #include <ctype.h> | |
33 | |
34 #define STRCONCAT_BUFSIZE 512 | |
35 | |
36 typedef void *(*ALLOC_FUNC)(Pool, unsigned int); | |
37 | |
38 static void *tp_malloc(Pool pool __attr_unused__, unsigned int size) | |
39 { | |
40 return t_malloc(size); | |
41 } | |
42 | |
43 typedef union _GDoubleIEEE754 GDoubleIEEE754; | |
44 #define G_IEEE754_DOUBLE_BIAS (1023) | |
45 /* multiply with base2 exponent to get base10 exponent (nomal numbers) */ | |
46 #define G_LOG_2_BASE_10 (0.30102999566398119521) | |
47 #if G_BYTE_ORDER == G_LITTLE_ENDIAN | |
48 union _GDoubleIEEE754 | |
49 { | |
50 double v_double; | |
51 struct { | |
52 unsigned int mantissa_low : 32; | |
53 unsigned int mantissa_high : 20; | |
54 unsigned int biased_exponent : 11; | |
55 unsigned int sign : 1; | |
56 } mpn; | |
57 }; | |
58 #elif G_BYTE_ORDER == G_BIG_ENDIAN | |
59 union _GDoubleIEEE754 | |
60 { | |
61 double v_double; | |
62 struct { | |
63 unsigned int sign : 1; | |
64 unsigned int biased_exponent : 11; | |
65 unsigned int mantissa_high : 20; | |
66 unsigned int mantissa_low : 32; | |
67 } mpn; | |
68 }; | |
69 #else /* !G_LITTLE_ENDIAN && !G_BIG_ENDIAN */ | |
70 #error unknown ENDIAN type | |
71 #endif /* !G_LITTLE_ENDIAN && !G_BIG_ENDIAN */ | |
72 | |
73 typedef struct | |
74 { | |
75 unsigned int min_width; | |
76 unsigned int precision; | |
77 int alternate_format, zero_padding, adjust_left, locale_grouping; | |
78 int add_space, add_sign, possible_sign, seen_precision; | |
79 int mod_half, mod_long, mod_extra_long; | |
80 } PrintfArgSpec; | |
81 | |
82 #if (SIZEOF_LONG > 4) || (SIZEOF_VOID_P > 4) | |
83 # define HONOUR_LONGS 1 | |
84 #else | |
85 # define HONOUR_LONGS 0 | |
86 #endif | |
87 | |
88 unsigned int printf_string_upper_bound(const char *format, va_list args) | |
89 { | |
90 int len = 1; | |
91 | |
92 if (!format) | |
93 return len; | |
94 | |
95 while (*format) | |
96 { | |
97 register char c = *format++; | |
98 | |
99 if (c != '%') | |
100 len += 1; | |
101 else /* (c == '%') */ | |
102 { | |
103 PrintfArgSpec spec; | |
104 int seen_l = FALSE, conv_done = FALSE; | |
105 unsigned int conv_len = 0; | |
106 const char *spec_start = format; | |
107 | |
108 memset(&spec, 0, sizeof(spec)); | |
109 do | |
110 { | |
111 c = *format++; | |
112 switch (c) | |
113 { | |
114 GDoubleIEEE754 u_double; | |
115 unsigned int v_uint; | |
116 int v_int; | |
117 const char *v_string; | |
118 | |
119 /* beware of positional parameters | |
120 */ | |
121 case '$': | |
122 i_warning (GNUC_PRETTY_FUNCTION | |
123 "(): unable to handle positional parameters (%%n$)"); | |
124 len += 1024; /* try adding some safety padding */ | |
125 break; | |
126 | |
127 /* parse flags | |
128 */ | |
129 case '#': | |
130 spec.alternate_format = TRUE; | |
131 break; | |
132 case '0': | |
133 spec.zero_padding = TRUE; | |
134 break; | |
135 case '-': | |
136 spec.adjust_left = TRUE; | |
137 break; | |
138 case ' ': | |
139 spec.add_space = TRUE; | |
140 break; | |
141 case '+': | |
142 spec.add_sign = TRUE; | |
143 break; | |
144 case '\'': | |
145 spec.locale_grouping = TRUE; | |
146 break; | |
147 | |
148 /* parse output size specifications | |
149 */ | |
150 case '.': | |
151 spec.seen_precision = TRUE; | |
152 break; | |
153 case '1': | |
154 case '2': | |
155 case '3': | |
156 case '4': | |
157 case '5': | |
158 case '6': | |
159 case '7': | |
160 case '8': | |
161 case '9': | |
162 v_uint = c - '0'; | |
163 c = *format; | |
164 while (c >= '0' && c <= '9') | |
165 { | |
166 format++; | |
167 v_uint = v_uint * 10 + c - '0'; | |
168 c = *format; | |
169 } | |
170 if (spec.seen_precision) | |
171 spec.precision = I_MAX (spec.precision, v_uint); | |
172 else | |
173 spec.min_width = I_MAX (spec.min_width, v_uint); | |
174 break; | |
175 case '*': | |
176 v_int = va_arg (args, int); | |
177 if (spec.seen_precision) | |
178 { | |
179 /* forget about negative precision */ | |
180 if (v_int >= 0) | |
181 spec.precision = I_MAX ((int)spec.precision, v_int); | |
182 } | |
183 else | |
184 { | |
185 if (v_int < 0) | |
186 { | |
187 v_int = - v_int; | |
188 spec.adjust_left = TRUE; | |
189 } | |
190 spec.min_width = I_MAX ((int)spec.min_width, v_int); | |
191 } | |
192 break; | |
193 | |
194 /* parse type modifiers | |
195 */ | |
196 case 'h': | |
197 spec.mod_half = TRUE; | |
198 break; | |
199 case 'l': | |
200 if (!seen_l) | |
201 { | |
202 spec.mod_long = TRUE; | |
203 seen_l = TRUE; | |
204 break; | |
205 } | |
206 /* else, fall through */ | |
207 case 'L': | |
208 case 'q': | |
209 spec.mod_long = TRUE; | |
210 spec.mod_extra_long = TRUE; | |
211 break; | |
212 case 'z': | |
213 case 'Z': | |
214 #if GLIB_SIZEOF_SIZE_T > 4 | |
215 spec.mod_long = TRUE; | |
216 spec.mod_extra_long = TRUE; | |
217 #endif /* GLIB_SIZEOF_SIZE_T > 4 */ | |
218 break; | |
219 case 't': | |
220 #if GLIB_SIZEOF_PTRDIFF_T > 4 | |
221 spec.mod_long = TRUE; | |
222 spec.mod_extra_long = TRUE; | |
223 #endif /* GLIB_SIZEOF_PTRDIFF_T > 4 */ | |
224 break; | |
225 case 'j': | |
226 #if GLIB_SIZEOF_INTMAX_T > 4 | |
227 spec.mod_long = TRUE; | |
228 spec.mod_extra_long = TRUE; | |
229 #endif /* GLIB_SIZEOF_INTMAX_T > 4 */ | |
230 break; | |
231 | |
232 /* parse output conversions | |
233 */ | |
234 case '%': | |
235 conv_len += 1; | |
236 break; | |
237 case 'O': | |
238 case 'D': | |
239 case 'I': | |
240 case 'U': | |
241 /* some C libraries feature long variants for these as well? */ | |
242 spec.mod_long = TRUE; | |
243 /* fall through */ | |
244 case 'o': | |
245 conv_len += 2; | |
246 /* fall through */ | |
247 case 'd': | |
248 case 'i': | |
249 conv_len += 1; /* sign */ | |
250 /* fall through */ | |
251 case 'u': | |
252 conv_len += 4; | |
253 /* fall through */ | |
254 case 'x': | |
255 case 'X': | |
256 spec.possible_sign = TRUE; | |
257 conv_len += 10; | |
258 if (spec.mod_long && HONOUR_LONGS) | |
259 conv_len *= 2; | |
260 if (spec.mod_extra_long) | |
261 conv_len *= 2; | |
262 if (spec.mod_extra_long) | |
263 { | |
264 #ifdef G_HAVE_GINT64 | |
265 (void) va_arg (args, gint64); | |
266 #else | |
267 (void) va_arg (args, long); | |
268 #endif | |
269 } | |
270 else if (spec.mod_long) | |
271 (void) va_arg (args, long); | |
272 else | |
273 (void) va_arg (args, int); | |
274 break; | |
275 case 'A': | |
276 case 'a': | |
277 /* 0x */ | |
278 conv_len += 2; | |
279 /* fall through */ | |
280 case 'g': | |
281 case 'G': | |
282 case 'e': | |
283 case 'E': | |
284 case 'f': | |
285 spec.possible_sign = TRUE; | |
286 /* n . dddddddddddddddddddddddd E +- eeee */ | |
287 conv_len += 1 + 1 + I_MAX (24, spec.precision) + 1 + 1 + 4; | |
288 if (spec.mod_extra_long) | |
289 i_warning (GNUC_PRETTY_FUNCTION | |
290 "(): unable to handle long double, collecting double only"); | |
291 #ifdef HAVE_LONG_DOUBLE | |
292 #error need to implement special handling for long double | |
293 #endif | |
294 u_double.v_double = va_arg (args, double); | |
295 /* %f can expand up to all significant digits before '.' (308) */ | |
296 if (c == 'f' && | |
297 u_double.mpn.biased_exponent > 0 && u_double.mpn.biased_exponent < 2047) | |
298 { | |
299 int exp = u_double.mpn.biased_exponent; | |
300 | |
301 exp -= G_IEEE754_DOUBLE_BIAS; | |
302 exp = exp * G_LOG_2_BASE_10 + 1; | |
303 conv_len += exp; | |
304 } | |
305 /* some printf() implementations require extra padding for rounding */ | |
306 conv_len += 2; | |
307 /* we can't really handle locale specific grouping here */ | |
308 if (spec.locale_grouping) | |
309 conv_len *= 2; | |
310 break; | |
311 case 'C': | |
312 spec.mod_long = TRUE; | |
313 /* fall through */ | |
314 case 'c': | |
315 conv_len += spec.mod_long ? MB_LEN_MAX : 1; | |
316 (void) va_arg (args, int); | |
317 break; | |
318 case 'S': | |
319 spec.mod_long = TRUE; | |
320 /* fall through */ | |
321 case 's': | |
322 v_string = va_arg (args, char*); | |
323 if (!v_string) | |
324 conv_len += 8; /* hold "(null)" */ | |
325 else if (spec.seen_precision) | |
326 conv_len += spec.precision; | |
327 else | |
328 conv_len += strlen (v_string); | |
329 conv_done = TRUE; | |
330 if (spec.mod_long) | |
331 { | |
332 i_warning (GNUC_PRETTY_FUNCTION | |
333 "(): unable to handle wide char strings"); | |
334 len += 1024; /* try adding some safety padding */ | |
335 } | |
336 break; | |
337 case 'P': /* do we actually need this? */ | |
338 /* fall through */ | |
339 case 'p': | |
340 spec.alternate_format = TRUE; | |
341 conv_len += 10; | |
342 if (HONOUR_LONGS) | |
343 conv_len *= 2; | |
344 /* fall through */ | |
345 case 'n': | |
346 conv_done = TRUE; | |
347 (void) va_arg (args, void*); | |
348 break; | |
349 case 'm': | |
350 /* there's not much we can do to be clever */ | |
351 v_string = strerror (errno); | |
352 v_uint = v_string ? strlen (v_string) : 0; | |
353 conv_len += I_MAX (256, v_uint); | |
354 break; | |
355 | |
356 /* handle invalid cases | |
357 */ | |
358 case '\000': | |
359 /* no conversion specification, bad bad */ | |
360 conv_len += format - spec_start; | |
361 break; | |
362 default: | |
363 i_warning (GNUC_PRETTY_FUNCTION | |
364 "(): unable to handle `%c' while parsing format", | |
365 c); | |
366 break; | |
367 } | |
368 conv_done |= conv_len > 0; | |
369 } | |
370 while (!conv_done); | |
371 /* handle width specifications */ | |
372 conv_len = I_MAX (conv_len, I_MAX (spec.precision, spec.min_width)); | |
373 /* handle flags */ | |
374 conv_len += spec.alternate_format ? 2 : 0; | |
375 conv_len += (spec.add_space || spec.add_sign || spec.possible_sign); | |
376 /* finally done */ | |
377 len += conv_len; | |
378 } /* else (c == '%') */ | |
379 } /* while (*format) */ | |
380 | |
381 return len; | |
382 } | |
383 | |
384 static const char *fix_format_real(const char *fmt, const char *p) | |
385 { | |
386 const char *errstr; | |
387 char *buf; | |
388 unsigned int pos, alloc, errlen; | |
389 | |
390 errstr = strerror(errno); | |
391 errlen = strlen(errstr); | |
392 | |
393 pos = (unsigned int) (p-fmt); | |
394 i_assert(pos < INT_MAX); | |
395 | |
396 alloc = pos + errlen + 128; | |
397 buf = t_buffer_get(alloc); | |
398 | |
399 memcpy(buf, fmt, pos); | |
400 | |
401 while (*p != '\0') { | |
402 if (*p == '%' && p[1] == 'm') { | |
403 if (pos+errlen+1 > alloc) { | |
404 alloc += errlen+1 + 128; | |
405 buf = t_buffer_get(alloc); | |
406 } | |
407 | |
408 memcpy(buf+pos, errstr, errlen); | |
409 pos += errlen; | |
410 p += 2; | |
411 } else { | |
412 /* p + \0 */ | |
413 if (pos+2 > alloc) { | |
414 alloc += 128; | |
415 buf = t_buffer_get(alloc); | |
416 } | |
417 | |
418 buf[pos++] = *p; | |
419 p++; | |
420 } | |
421 } | |
422 | |
423 buf[pos++] = '\0'; | |
424 t_buffer_alloc(pos); | |
425 return buf; | |
426 } | |
427 | |
428 /* replace %m with strerror() */ | |
429 static const char *fix_format(const char *fmt) | |
430 { | |
431 const char *p; | |
432 | |
433 for (p = fmt; *p != '\0'; p++) { | |
434 if (*p == '%' && p[1] == 'm') | |
435 return fix_format_real(fmt, p); | |
436 } | |
437 | |
438 return fmt; | |
439 } | |
440 | |
441 int i_snprintf(char *str, unsigned int max_chars, const char *format, ...) | |
442 { | |
443 #ifdef HAVE_VSNPRINTF | |
444 va_list args; | |
445 int ret; | |
446 | |
447 i_assert(str != NULL); | |
448 i_assert(max_chars < INT_MAX); | |
449 i_assert(format != NULL); | |
450 | |
451 va_start(args, format); | |
452 ret = vsnprintf(str, max_chars, fix_format(format), args); | |
453 va_end(args); | |
454 | |
455 if (ret < 0) { | |
456 str[max_chars-1] = '\0'; | |
457 ret = strlen(str); | |
458 } | |
459 | |
460 return ret; | |
461 #else | |
462 char *buf; | |
463 va_list args; | |
464 int len; | |
465 | |
466 i_assert(str != NULL); | |
467 i_assert(max_chars < INT_MAX); | |
468 i_assert(format != NULL); | |
469 | |
470 va_start(args, format); | |
471 format = fix_format(format); | |
472 buf = t_buffer_get(printf_string_upper_bound(format, args)); | |
473 va_end(args); | |
474 | |
475 len = vsprintf(buf, format, args); | |
476 if (len >= (int)max_chars) | |
477 len = max_chars-1; | |
478 | |
479 memcpy(str, buf, len); | |
480 str[len] = '\0'; | |
481 return len; | |
482 #endif | |
483 } | |
484 | |
485 #define STRDUP_CORE(alloc_func, str) STMT_START { \ | |
486 void *mem; \ | |
487 unsigned int len; \ | |
488 \ | |
489 for (len = 0; (str)[len] != '\0'; ) \ | |
490 len++; \ | |
491 len++; \ | |
492 mem = alloc_func; \ | |
493 memcpy(mem, str, sizeof(str[0])*len); \ | |
494 return mem; \ | |
495 } STMT_END | |
496 | |
497 char *p_strdup(Pool pool, const char *str) | |
498 { | |
499 if (str == NULL) | |
500 return NULL; | |
501 | |
502 STRDUP_CORE(p_malloc(pool, len), str); | |
503 } | |
504 | |
505 const char *t_strdup(const char *str) | |
506 { | |
507 if (str == NULL) | |
508 return NULL; | |
509 | |
510 STRDUP_CORE(t_malloc(len), str); | |
511 } | |
512 | |
513 int *p_intarrdup(Pool pool, const int *arr) | |
514 { | |
515 if (arr == NULL) | |
516 return NULL; | |
517 | |
518 STRDUP_CORE(p_malloc(pool, sizeof(int) * len), arr); | |
519 } | |
520 | |
521 const int *t_intarrdup(const int *arr) | |
522 { | |
523 if (arr == NULL) | |
524 return NULL; | |
525 | |
526 STRDUP_CORE(t_malloc(sizeof(int) * len), arr); | |
527 } | |
528 | |
529 #define STRDUP_EMPTY_CORE(alloc_func, str) STMT_START { \ | |
530 if ((str) == NULL || (str)[0] == '\0') \ | |
531 return NULL; \ | |
532 \ | |
533 STRDUP_CORE(alloc_func, str); \ | |
534 } STMT_END | |
535 | |
536 | |
537 char *p_strdup_empty(Pool pool, const char *str) | |
538 { | |
539 STRDUP_EMPTY_CORE(p_malloc(pool, len), str); | |
540 } | |
541 | |
542 const char *t_strdup_empty(const char *str) | |
543 { | |
544 STRDUP_EMPTY_CORE(t_malloc(len), str); | |
545 } | |
546 | |
5
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
547 char *p_strdup_until(Pool pool, const char *start, const char *end) |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
548 { |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
549 unsigned int size; |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
550 char *mem; |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
551 |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
552 i_assert(start <= end); |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
553 |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
554 size = (unsigned int) (end-start); |
8 | 555 i_assert(size < UINT_MAX); |
556 | |
557 mem = p_malloc(pool, size + 1); | |
5
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
558 memcpy(mem, start, size); |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
559 return mem; |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
560 } |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
561 |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
562 const char *t_strdup_until(const char *start, const char *end) |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
563 { |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
564 unsigned int size; |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
565 char *mem; |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
566 |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
567 i_assert(start <= end); |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
568 |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
569 size = (unsigned int) (end-start); |
8 | 570 i_assert(size < UINT_MAX); |
571 | |
572 mem = t_malloc(size + 1); | |
5
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
573 memcpy(mem, start, size); |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
574 mem[size] = '\0'; |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
575 return mem; |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
576 } |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
577 |
0 | 578 static inline char * |
579 strndup_core(const char *str, unsigned int max_chars, | |
580 ALLOC_FUNC alloc, Pool pool) | |
581 { | |
582 char *mem; | |
583 unsigned int len; | |
584 | |
585 i_assert(max_chars < INT_MAX); | |
586 | |
587 if (str == NULL) | |
588 return NULL; | |
589 | |
590 len = 0; | |
591 while (str[len] != '\0' && len < max_chars) | |
592 len++; | |
593 | |
594 mem = alloc(pool, len+1); | |
595 memcpy(mem, str, len); | |
596 mem[len] = '\0'; | |
597 return mem; | |
598 } | |
599 | |
600 char *p_strndup(Pool pool, const char *str, unsigned int max_chars) | |
601 { | |
602 return strndup_core(str, max_chars, pool->malloc, pool); | |
603 } | |
604 | |
605 const char *t_strndup(const char *str, unsigned int max_chars) | |
606 { | |
607 return strndup_core(str, max_chars, tp_malloc, NULL); | |
608 } | |
609 | |
610 char *p_strdup_printf(Pool pool, const char *format, ...) | |
611 { | |
612 va_list args; | |
613 char *ret; | |
614 | |
615 va_start(args, format); | |
616 ret = p_strdup_vprintf(pool, format, args); | |
617 va_end(args); | |
618 | |
619 return ret; | |
620 } | |
621 | |
622 const char *t_strdup_printf(const char *format, ...) | |
623 { | |
624 va_list args; | |
625 const char *ret; | |
626 | |
627 va_start(args, format); | |
628 ret = t_strdup_vprintf(format, args); | |
629 va_end(args); | |
630 | |
631 return ret; | |
632 } | |
633 | |
634 static inline char * | |
635 strdup_vprintf_core(const char *format, va_list args, | |
636 ALLOC_FUNC alloc_func, Pool pool) | |
637 { | |
638 va_list temp_args; | |
639 char *ret; | |
640 | |
641 if (format == NULL) | |
642 return NULL; | |
643 format = fix_format(format); | |
644 | |
645 VA_COPY(temp_args, args); | |
646 | |
647 ret = alloc_func(pool, printf_string_upper_bound(format, args)); | |
648 vsprintf(ret, format, args); | |
649 | |
650 va_end(temp_args); | |
651 | |
652 return ret; | |
653 } | |
654 | |
655 char *p_strdup_vprintf(Pool pool, const char *format, va_list args) | |
656 { | |
657 return strdup_vprintf_core(format, args, pool->malloc, pool); | |
658 } | |
659 | |
660 const char *t_strdup_vprintf(const char *format, va_list args) | |
661 { | |
662 return strdup_vprintf_core(format, args, tp_malloc, NULL); | |
663 } | |
664 | |
665 void p_strdup_replace(Pool pool, char **dest, const char *str) | |
666 { | |
667 p_free(pool, *dest); | |
668 *dest = p_strdup(pool, str); | |
669 } | |
670 | |
671 const char *temp_strconcat(const char *str1, va_list args, | |
672 unsigned int *ret_len) | |
673 { | |
674 const char *str; | |
675 char *temp; | |
676 unsigned int full_len, len, bufsize; | |
677 | |
678 if (str1 == NULL) | |
679 return NULL; | |
680 | |
681 /* put str1 to buffer */ | |
682 len = strlen(str1); | |
683 bufsize = len <= STRCONCAT_BUFSIZE ? STRCONCAT_BUFSIZE : | |
684 nearest_power(len+1); | |
685 temp = t_buffer_get(bufsize); | |
686 | |
687 memcpy(temp, str1, len); | |
688 full_len = len; | |
689 | |
690 /* put rest of the strings to buffer */ | |
691 while ((str = va_arg(args, char *)) != NULL) { | |
692 len = strlen(str); | |
693 if (len == 0) | |
694 continue; | |
695 | |
696 if (bufsize < full_len+len+1) { | |
697 bufsize = nearest_power(bufsize+len+1); | |
698 temp = t_buffer_reget(temp, bufsize); | |
699 } | |
700 | |
701 memcpy(temp+full_len, str, len); | |
702 full_len += len; | |
703 } | |
704 | |
705 temp[full_len] = '\0'; | |
706 *ret_len = full_len+1; | |
707 return temp; | |
708 } | |
709 | |
710 char *p_strconcat(Pool pool, const char *str1, ...) | |
711 { | |
712 va_list args; | |
713 const char *temp; | |
714 char *ret; | |
715 unsigned int len; | |
716 | |
717 va_start(args, str1); | |
718 | |
719 temp = temp_strconcat(str1, args, &len); | |
720 if (temp == NULL) | |
721 ret = NULL; | |
722 else { | |
723 ret = p_malloc(pool, len); | |
724 memcpy(ret, temp, len); | |
725 } | |
726 | |
727 va_end(args); | |
728 return ret; | |
729 } | |
730 | |
731 const char *t_strconcat(const char *str1, ...) | |
732 { | |
733 va_list args; | |
734 const char *ret; | |
735 unsigned int len; | |
736 | |
737 va_start(args, str1); | |
738 | |
739 ret = temp_strconcat(str1, args, &len); | |
740 if (ret != NULL) | |
741 t_buffer_alloc(len); | |
742 | |
743 va_end(args); | |
744 return ret; | |
745 } | |
746 | |
747 const char *t_strcut(const char *str, char cutchar) | |
748 { | |
749 const char *p; | |
750 | |
751 for (p = str; *p != '\0'; p++) { | |
752 if (*p == cutchar) | |
5
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
753 return t_strdup_until(str, p); |
0 | 754 } |
755 | |
756 return str; | |
757 } | |
758 | |
759 int is_numeric(const char *str, char end_char) | |
760 { | |
761 if (*str == '\0' || *str == end_char) | |
762 return FALSE; | |
763 | |
764 while (*str != '\0' && *str != end_char) { | |
765 if (!i_isdigit(*str)) | |
766 return FALSE; | |
767 str++; | |
768 } | |
769 | |
770 return TRUE; | |
771 } | |
772 | |
773 char *str_ucase(char *str) | |
774 { | |
775 char *p; | |
776 | |
777 for (p = str; *p != '\0'; p++) | |
778 *p = i_toupper(*p); | |
779 return str; | |
780 } | |
781 | |
782 char *str_lcase(char *str) | |
783 { | |
784 char *p; | |
785 | |
786 for (p = str; *p != '\0'; p++) | |
787 *p = i_tolower(*p); | |
788 return str; | |
789 } | |
790 | |
791 char *i_strtoken(char **str, char delim) | |
792 { | |
793 char *ret; | |
794 | |
795 if (*str == NULL || **str == '\0') | |
796 return NULL; | |
797 | |
798 ret = *str; | |
799 while (**str != '\0') { | |
800 if (**str == delim) { | |
801 **str = '\0'; | |
802 (*str)++; | |
803 break; | |
804 } | |
805 (*str)++; | |
806 } | |
807 return ret; | |
808 } | |
809 | |
810 void string_remove_escapes(char *str) | |
811 { | |
812 char *dest; | |
813 | |
814 for (dest = str; *str != '\0'; str++) { | |
815 if (*str != '\\' || str[1] == '\0') | |
816 *dest++ = *str; | |
817 } | |
818 | |
819 *dest = '\0'; | |
820 } | |
821 | |
822 int strarray_length(char *const array[]) | |
823 { | |
824 int len; | |
825 | |
826 len = 0; | |
827 while (*array) { | |
828 len++; | |
829 array++; | |
830 } | |
831 return len; | |
832 } | |
833 | |
834 int strarray_find(char *const array[], const char *item) | |
835 { | |
836 int index; | |
837 | |
838 i_assert(item != NULL); | |
839 | |
840 for (index = 0; *array != NULL; index++, array++) { | |
841 if (strcasecmp(*array, item) == 0) | |
842 return index; | |
843 } | |
844 | |
845 return -1; | |
846 } | |
847 | |
848 char *const *t_strsplit(const char *data, const char *separators) | |
849 { | |
850 const char **array; | |
851 char *str; | |
852 int alloc_len, len; | |
853 | |
854 i_assert(*separators != '\0'); | |
855 | |
856 str = (char *) t_strdup(data); | |
857 | |
858 alloc_len = 20; | |
859 array = t_buffer_get(sizeof(const char *) * alloc_len); | |
860 | |
861 array[0] = str; len = 1; | |
862 while (*str != '\0') { | |
863 if (strchr(separators, *str) != NULL) { | |
864 /* separator found */ | |
865 if (len+1 >= alloc_len) { | |
866 alloc_len *= 2; | |
867 array = t_buffer_reget(array, | |
868 sizeof(const char *) * | |
869 alloc_len); | |
870 } | |
871 | |
872 *str = '\0'; | |
873 array[len++] = str+1; | |
874 } | |
875 | |
876 str++; | |
877 } | |
878 array[len] = NULL; | |
879 | |
880 t_buffer_alloc(sizeof(const char *) * (len+1)); | |
881 return (char *const *) array; | |
882 } | |
883 | |
884 const char *t_strjoin_replace(char *const args[], char separator, | |
885 int replacearg, const char *replacedata) | |
886 { | |
887 const char *arg; | |
888 char *data; | |
889 unsigned int alloc_len, arg_len, full_len; | |
890 int i; | |
891 | |
892 if (args[0] == NULL) | |
893 return NULL; | |
894 | |
895 alloc_len = 512; full_len = 0; | |
896 data = t_buffer_get(alloc_len); | |
897 for (i = 0; args[i] != NULL; i++) { | |
898 arg = i == replacearg ? replacedata : args[i]; | |
899 arg_len = strlen(arg); | |
900 | |
901 if (full_len + arg_len+1 >= alloc_len) { | |
902 alloc_len = nearest_power(full_len + arg_len+1); | |
903 data = t_buffer_reget(data, alloc_len); | |
904 } | |
905 | |
906 memcpy(data+full_len, arg, arg_len); | |
907 full_len += arg_len; | |
908 | |
909 data[full_len++] = separator; | |
910 } | |
911 data[full_len-1] = '\0'; | |
912 | |
913 t_buffer_alloc(full_len); | |
914 return data; | |
915 } |