495011ec5cd7406b83dc3e3d4f999f503f532a93
[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;
64         int added;
65         int need;
66         int offset = (append && (*buf)->__AST_STR_LEN) ? (*buf)->__AST_STR_USED : 0;
67         va_list aq;
68
69         if (max_len < 0) {
70                 max_len = (*buf)->__AST_STR_LEN;        /* don't exceed the allocated space */
71         }
72
73         do {
74                 va_copy(aq, ap);
75                 res = vsnprintf((*buf)->__AST_STR_STR + offset, (*buf)->__AST_STR_LEN - offset, fmt, aq);
76                 va_end(aq);
77
78                 if (res < 0) {
79                         /*
80                          * vsnprintf write to string failed.
81                          * I don't think this is possible with a memory buffer.
82                          */
83                         res = AST_DYNSTR_BUILD_FAILED;
84                         added = 0;
85                         break;
86                 }
87
88                 /*
89                  * vsnprintf returns how much space we used or would need.
90                  * Remember that vsnprintf does not count the nil terminator
91                  * so we must add 1.
92                  */
93                 added = res;
94                 need = offset + added + 1;
95                 if (need <= (*buf)->__AST_STR_LEN
96                         || (max_len && max_len <= (*buf)->__AST_STR_LEN)) {
97                         /*
98                          * There was enough room for the string or we are not
99                          * allowed to try growing the string buffer.
100                          */
101                         break;
102                 }
103
104                 /* Reallocate the buffer and try again. */
105                 if (max_len == 0) {
106                         /* unbounded, give more room for next time */
107                         need += 16 + need / 4;
108                 } else if (max_len < need) {
109                         /* truncate as needed */
110                         need = max_len;
111                 }
112
113                 if (
114 #if (defined(MALLOC_DEBUG) && !defined(STANDALONE))
115                         _ast_str_make_space(buf, need, file, lineno, function)
116 #else
117                         ast_str_make_space(buf, need)
118 #endif
119                         ) {
120                         ast_log_safe(LOG_VERBOSE, "failed to extend from %d to %d\n",
121                                 (int) (*buf)->__AST_STR_LEN, need);
122
123                         res = AST_DYNSTR_BUILD_FAILED;
124                         break;
125                 }
126         } while (1);
127
128         /* Update space used, keep in mind truncation may be necessary. */
129         (*buf)->__AST_STR_USED = ((*buf)->__AST_STR_LEN <= offset + added)
130                 ? (*buf)->__AST_STR_LEN - 1
131                 : offset + added;
132
133         /* Ensure that the string is terminated. */
134         (*buf)->__AST_STR_STR[(*buf)->__AST_STR_USED] = '\0';
135
136         return res;
137 }
138
139 char *__ast_str_helper2(struct ast_str **buf, ssize_t maxlen, const char *src, size_t maxsrc, int append, int escapecommas)
140 {
141         int dynamic = 0;
142         char *ptr = append ? &((*buf)->__AST_STR_STR[(*buf)->__AST_STR_USED]) : (*buf)->__AST_STR_STR;
143
144         if (maxlen < 1) {
145                 if (maxlen == 0) {
146                         dynamic = 1;
147                 }
148                 maxlen = (*buf)->__AST_STR_LEN;
149         }
150
151         while (*src && maxsrc && maxlen && (!escapecommas || (maxlen - 1))) {
152                 if (escapecommas && (*src == '\\' || *src == ',')) {
153                         *ptr++ = '\\';
154                         maxlen--;
155                         (*buf)->__AST_STR_USED++;
156                 }
157                 *ptr++ = *src++;
158                 maxsrc--;
159                 maxlen--;
160                 (*buf)->__AST_STR_USED++;
161
162                 if ((ptr >= (*buf)->__AST_STR_STR + (*buf)->__AST_STR_LEN - 3) ||
163                         (dynamic && (!maxlen || (escapecommas && !(maxlen - 1))))) {
164                         char *oldbase = (*buf)->__AST_STR_STR;
165                         size_t old = (*buf)->__AST_STR_LEN;
166                         if (ast_str_make_space(buf, (*buf)->__AST_STR_LEN * 2)) {
167                                 /* If the buffer can't be extended, end it. */
168                                 break;
169                         }
170                         /* What we extended the buffer by */
171                         maxlen = old;
172
173                         ptr += (*buf)->__AST_STR_STR - oldbase;
174                 }
175         }
176         if (__builtin_expect(!maxlen, 0)) {
177                 ptr--;
178         }
179         *ptr = '\0';
180         return (*buf)->__AST_STR_STR;
181 }
182
183 static int str_hash(const void *obj, const int flags)
184 {
185         return ast_str_hash(obj);
186 }
187
188 static int str_cmp(void *lhs, void *rhs, int flags)
189 {
190         return strcmp(lhs, rhs) ? 0 : CMP_MATCH;
191 }
192
193 //struct ao2_container *ast_str_container_alloc_options(enum ao2_container_opts opts, int buckets)
194 struct ao2_container *ast_str_container_alloc_options(enum ao2_alloc_opts opts, int buckets)
195 {
196         return ao2_container_alloc_options(opts, buckets, str_hash, str_cmp);
197 }
198
199 int ast_str_container_add(struct ao2_container *str_container, const char *add)
200 {
201         char *ao2_add;
202
203         /* The ao2_add object is immutable so it doesn't need a lock of its own. */
204         ao2_add = ao2_alloc_options(strlen(add) + 1, NULL, AO2_ALLOC_OPT_LOCK_NOLOCK);
205         if (!ao2_add) {
206                 return -1;
207         }
208         strcpy(ao2_add, add);/* Safe */
209
210         ao2_link(str_container, ao2_add);
211         ao2_ref(ao2_add, -1);
212         return 0;
213 }
214
215 void ast_str_container_remove(struct ao2_container *str_container, const char *remove)
216 {
217         ao2_find(str_container, remove, OBJ_SEARCH_KEY | OBJ_NODATA | OBJ_UNLINK);
218 }
219
220 char *ast_generate_random_string(char *buf, size_t size)
221 {
222         int i;
223
224         for (i = 0; i < size - 1; ++i) {
225                 buf[i] = 'a' + (ast_random() % 26);
226         }
227         buf[i] = '\0';
228
229         return buf;
230 }