Fix DEBUG_THREADS when lock is acquired in __constructor__
[asterisk/asterisk.git] / main / strings.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2008, Digium, Inc.
5  *
6  * Tilghman Lesher <tlesher@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief String manipulation API
22  *
23  * \author Tilghman Lesher <tilghman@digium.com>
24  */
25
26 /*** MAKEOPTS
27 <category name="MENUSELECT_CFLAGS" displayname="Compiler Flags" positive_output="yes">
28         <member name="DEBUG_OPAQUE" displayname="Change ast_str internals to detect improper usage" touch_on_change="include/asterisk/strings.h">
29                 <defaultenabled>yes</defaultenabled>
30         </member>
31 </category>
32  ***/
33
34 /*** MODULEINFO
35         <support_level>core</support_level>
36  ***/
37
38 #include "asterisk.h"
39
40 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
41
42 #include "asterisk/strings.h"
43 #include "asterisk/pbx.h"
44
45 /*!
46  * core handler for dynamic strings.
47  * This is not meant to be called directly, but rather through the
48  * various wrapper macros
49  *      ast_str_set(...)
50  *      ast_str_append(...)
51  *      ast_str_set_va(...)
52  *      ast_str_append_va(...)
53  */
54
55 #if (defined(MALLOC_DEBUG) && !defined(STANDALONE))
56 int __ast_debug_str_helper(struct ast_str **buf, ssize_t max_len,
57         int append, const char *fmt, va_list ap, const char *file, int lineno, const char *function)
58 #else
59 int __ast_str_helper(struct ast_str **buf, ssize_t max_len,
60         int append, const char *fmt, va_list ap)
61 #endif
62 {
63         int res, need;
64         int offset = (append && (*buf)->__AST_STR_LEN) ? (*buf)->__AST_STR_USED : 0;
65         va_list aq;
66
67         do {
68                 if (max_len < 0) {
69                         max_len = (*buf)->__AST_STR_LEN;        /* don't exceed the allocated space */
70                 }
71                 /*
72                  * Ask vsnprintf how much space we need. Remember that vsnprintf
73                  * does not count the final <code>'\\0'</code> so we must add 1.
74                  */
75                 va_copy(aq, ap);
76                 res = vsnprintf((*buf)->__AST_STR_STR + offset, (*buf)->__AST_STR_LEN - offset, fmt, aq);
77
78                 need = res + offset + 1;
79                 /*
80                  * If there is not enough space and we are below the max length,
81                  * reallocate the buffer and return a message telling to retry.
82                  */
83                 if (need > (*buf)->__AST_STR_LEN && (max_len == 0 || (*buf)->__AST_STR_LEN < max_len) ) {
84                         int len = (int)(*buf)->__AST_STR_LEN;
85                         if (max_len && max_len < need) {        /* truncate as needed */
86                                 need = max_len;
87                         } else if (max_len == 0) {      /* if unbounded, give more room for next time */
88                                 need += 16 + need / 4;
89                         }
90                         if (0) {        /* debugging */
91                                 ast_verbose("extend from %d to %d\n", len, need);
92                         }
93                         if (
94 #if (defined(MALLOC_DEBUG) && !defined(STANDALONE))
95                                         _ast_str_make_space(buf, need, file, lineno, function)
96 #else
97                                         ast_str_make_space(buf, need)
98 #endif
99                                 ) {
100                                 ast_verbose("failed to extend from %d to %d\n", len, need);
101                                 va_end(aq);
102                                 return AST_DYNSTR_BUILD_FAILED;
103                         }
104                         (*buf)->__AST_STR_STR[offset] = '\0';   /* Truncate the partial write. */
105
106                         /* Restart va_copy before calling vsnprintf() again. */
107                         va_end(aq);
108                         continue;
109                 }
110                 va_end(aq);
111                 break;
112         } while (1);
113         /* update space used, keep in mind the truncation */
114         (*buf)->__AST_STR_USED = (res + offset > (*buf)->__AST_STR_LEN) ? (*buf)->__AST_STR_LEN - 1: res + offset;
115
116         return res;
117 }
118
119 char *__ast_str_helper2(struct ast_str **buf, ssize_t maxlen, const char *src, size_t maxsrc, int append, int escapecommas)
120 {
121         int dynamic = 0;
122         char *ptr = append ? &((*buf)->__AST_STR_STR[(*buf)->__AST_STR_USED]) : (*buf)->__AST_STR_STR;
123
124         if (maxlen < 1) {
125                 if (maxlen == 0) {
126                         dynamic = 1;
127                 }
128                 maxlen = (*buf)->__AST_STR_LEN;
129         }
130
131         while (*src && maxsrc && maxlen && (!escapecommas || (maxlen - 1))) {
132                 if (escapecommas && (*src == '\\' || *src == ',')) {
133                         *ptr++ = '\\';
134                         maxlen--;
135                         (*buf)->__AST_STR_USED++;
136                 }
137                 *ptr++ = *src++;
138                 maxsrc--;
139                 maxlen--;
140                 (*buf)->__AST_STR_USED++;
141
142                 if ((ptr >= (*buf)->__AST_STR_STR + (*buf)->__AST_STR_LEN - 3) ||
143                         (dynamic && (!maxlen || (escapecommas && !(maxlen - 1))))) {
144                         char *oldbase = (*buf)->__AST_STR_STR;
145                         size_t old = (*buf)->__AST_STR_LEN;
146                         if (ast_str_make_space(buf, (*buf)->__AST_STR_LEN * 2)) {
147                                 /* If the buffer can't be extended, end it. */
148                                 break;
149                         }
150                         /* What we extended the buffer by */
151                         maxlen = old;
152
153                         ptr += (*buf)->__AST_STR_STR - oldbase;
154                 }
155         }
156         if (__builtin_expect(!maxlen, 0)) {
157                 ptr--;
158         }
159         *ptr = '\0';
160         return (*buf)->__AST_STR_STR;
161 }
162
163 static int str_hash(const void *obj, const int flags)
164 {
165         return ast_str_hash(obj);
166 }
167
168 static int str_cmp(void *lhs, void *rhs, int flags)
169 {
170         return strcmp(lhs, rhs) ? 0 : CMP_MATCH;
171 }
172
173 struct ao2_container *ast_str_container_alloc_options(enum ao2_container_opts opts, int buckets)
174 {
175         return ao2_container_alloc_options(opts, buckets, str_hash, str_cmp);
176 }
177
178 int ast_str_container_add(struct ao2_container *str_container, const char *add)
179 {
180         RAII_VAR(char *, ao2_add, ao2_alloc(strlen(add) + 1, NULL), ao2_cleanup);
181
182         if (!ao2_add) {
183                 return -1;
184         }
185
186         /* safe strcpy */
187         strcpy(ao2_add, add);
188         ao2_link(str_container, ao2_add);
189         return 0;
190 }
191
192 void ast_str_container_remove(struct ao2_container *str_container, const char *remove)
193 {
194         ao2_find(str_container, remove, OBJ_KEY | OBJ_NODATA | OBJ_UNLINK);
195 }