7aaff7992aacd22fc77428f0f3ff062851dab544
[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_REGISTER_FILE()
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 (
91 #if (defined(MALLOC_DEBUG) && !defined(STANDALONE))
92                                         _ast_str_make_space(buf, need, file, lineno, function)
93 #else
94                                         ast_str_make_space(buf, need)
95 #endif
96                                 ) {
97                                 ast_log_safe(LOG_VERBOSE, "failed to extend from %d to %d\n", len, need);
98                                 va_end(aq);
99                                 return AST_DYNSTR_BUILD_FAILED;
100                         }
101                         (*buf)->__AST_STR_STR[offset] = '\0';   /* Truncate the partial write. */
102
103                         /* Restart va_copy before calling vsnprintf() again. */
104                         va_end(aq);
105                         continue;
106                 }
107                 va_end(aq);
108                 break;
109         } while (1);
110         /* update space used, keep in mind the truncation */
111         (*buf)->__AST_STR_USED = (res + offset > (*buf)->__AST_STR_LEN) ? (*buf)->__AST_STR_LEN - 1: res + offset;
112
113         return res;
114 }
115
116 char *__ast_str_helper2(struct ast_str **buf, ssize_t maxlen, const char *src, size_t maxsrc, int append, int escapecommas)
117 {
118         int dynamic = 0;
119         char *ptr = append ? &((*buf)->__AST_STR_STR[(*buf)->__AST_STR_USED]) : (*buf)->__AST_STR_STR;
120
121         if (maxlen < 1) {
122                 if (maxlen == 0) {
123                         dynamic = 1;
124                 }
125                 maxlen = (*buf)->__AST_STR_LEN;
126         }
127
128         while (*src && maxsrc && maxlen && (!escapecommas || (maxlen - 1))) {
129                 if (escapecommas && (*src == '\\' || *src == ',')) {
130                         *ptr++ = '\\';
131                         maxlen--;
132                         (*buf)->__AST_STR_USED++;
133                 }
134                 *ptr++ = *src++;
135                 maxsrc--;
136                 maxlen--;
137                 (*buf)->__AST_STR_USED++;
138
139                 if ((ptr >= (*buf)->__AST_STR_STR + (*buf)->__AST_STR_LEN - 3) ||
140                         (dynamic && (!maxlen || (escapecommas && !(maxlen - 1))))) {
141                         char *oldbase = (*buf)->__AST_STR_STR;
142                         size_t old = (*buf)->__AST_STR_LEN;
143                         if (ast_str_make_space(buf, (*buf)->__AST_STR_LEN * 2)) {
144                                 /* If the buffer can't be extended, end it. */
145                                 break;
146                         }
147                         /* What we extended the buffer by */
148                         maxlen = old;
149
150                         ptr += (*buf)->__AST_STR_STR - oldbase;
151                 }
152         }
153         if (__builtin_expect(!maxlen, 0)) {
154                 ptr--;
155         }
156         *ptr = '\0';
157         return (*buf)->__AST_STR_STR;
158 }
159
160 static int str_hash(const void *obj, const int flags)
161 {
162         return ast_str_hash(obj);
163 }
164
165 static int str_cmp(void *lhs, void *rhs, int flags)
166 {
167         return strcmp(lhs, rhs) ? 0 : CMP_MATCH;
168 }
169
170 //struct ao2_container *ast_str_container_alloc_options(enum ao2_container_opts opts, int buckets)
171 struct ao2_container *ast_str_container_alloc_options(enum ao2_alloc_opts opts, int buckets)
172 {
173         return ao2_container_alloc_options(opts, buckets, str_hash, str_cmp);
174 }
175
176 int ast_str_container_add(struct ao2_container *str_container, const char *add)
177 {
178         char *ao2_add;
179
180         /* The ao2_add object is immutable so it doesn't need a lock of its own. */
181         ao2_add = ao2_alloc_options(strlen(add) + 1, NULL, AO2_ALLOC_OPT_LOCK_NOLOCK);
182         if (!ao2_add) {
183                 return -1;
184         }
185         strcpy(ao2_add, add);/* Safe */
186
187         ao2_link(str_container, ao2_add);
188         ao2_ref(ao2_add, -1);
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_SEARCH_KEY | OBJ_NODATA | OBJ_UNLINK);
195 }
196
197 char *ast_generate_random_string(char *buf, size_t size)
198 {
199         int i;
200
201         for (i = 0; i < size - 1; ++i) {
202                 buf[i] = 'a' + (ast_random() % 26);
203         }
204         buf[i] = '\0';
205
206         return buf;
207 }