strings.c 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  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. /*! \file
  19. *
  20. * \brief String manipulation API
  21. *
  22. * \author Tilghman Lesher <tilghman@digium.com>
  23. */
  24. /*** MAKEOPTS
  25. <category name="MENUSELECT_CFLAGS" displayname="Compiler Flags" positive_output="yes">
  26. <member name="DEBUG_OPAQUE" displayname="Change ast_str internals to detect improper usage" touch_on_change="include/asterisk/strings.h">
  27. <defaultenabled>yes</defaultenabled>
  28. </member>
  29. </category>
  30. ***/
  31. /*** MODULEINFO
  32. <support_level>core</support_level>
  33. ***/
  34. #include "asterisk.h"
  35. ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  36. #include <regex.h>
  37. #include "asterisk/strings.h"
  38. #include "asterisk/pbx.h"
  39. /*!
  40. * core handler for dynamic strings.
  41. * This is not meant to be called directly, but rather through the
  42. * various wrapper macros
  43. * ast_str_set(...)
  44. * ast_str_append(...)
  45. * ast_str_set_va(...)
  46. * ast_str_append_va(...)
  47. */
  48. #ifdef __AST_DEBUG_MALLOC
  49. int __ast_debug_str_helper(struct ast_str **buf, ssize_t max_len,
  50. int append, const char *fmt, va_list ap, const char *file, int lineno, const char *function)
  51. #else
  52. int __ast_str_helper(struct ast_str **buf, ssize_t max_len,
  53. int append, const char *fmt, va_list ap)
  54. #endif
  55. {
  56. int res;
  57. int added;
  58. int need;
  59. int offset = (append && (*buf)->__AST_STR_LEN) ? (*buf)->__AST_STR_USED : 0;
  60. va_list aq;
  61. if (max_len < 0) {
  62. max_len = (*buf)->__AST_STR_LEN; /* don't exceed the allocated space */
  63. }
  64. do {
  65. va_copy(aq, ap);
  66. res = vsnprintf((*buf)->__AST_STR_STR + offset, (*buf)->__AST_STR_LEN - offset, fmt, aq);
  67. va_end(aq);
  68. if (res < 0) {
  69. /*
  70. * vsnprintf write to string failed.
  71. * I don't think this is possible with a memory buffer.
  72. */
  73. res = AST_DYNSTR_BUILD_FAILED;
  74. added = 0;
  75. break;
  76. }
  77. /*
  78. * vsnprintf returns how much space we used or would need.
  79. * Remember that vsnprintf does not count the nil terminator
  80. * so we must add 1.
  81. */
  82. added = res;
  83. need = offset + added + 1;
  84. if (need <= (*buf)->__AST_STR_LEN
  85. || (max_len && max_len <= (*buf)->__AST_STR_LEN)) {
  86. /*
  87. * There was enough room for the string or we are not
  88. * allowed to try growing the string buffer.
  89. */
  90. break;
  91. }
  92. /* Reallocate the buffer and try again. */
  93. if (max_len == 0) {
  94. /* unbounded, give more room for next time */
  95. need += 16 + need / 4;
  96. } else if (max_len < need) {
  97. /* truncate as needed */
  98. need = max_len;
  99. }
  100. if (
  101. #ifdef __AST_DEBUG_MALLOC
  102. _ast_str_make_space(buf, need, file, lineno, function)
  103. #else
  104. ast_str_make_space(buf, need)
  105. #endif
  106. ) {
  107. ast_log_safe(LOG_VERBOSE, "failed to extend from %d to %d\n",
  108. (int) (*buf)->__AST_STR_LEN, need);
  109. res = AST_DYNSTR_BUILD_FAILED;
  110. break;
  111. }
  112. } while (1);
  113. /* Update space used, keep in mind truncation may be necessary. */
  114. (*buf)->__AST_STR_USED = ((*buf)->__AST_STR_LEN <= offset + added)
  115. ? (*buf)->__AST_STR_LEN - 1
  116. : offset + added;
  117. /* Ensure that the string is terminated. */
  118. (*buf)->__AST_STR_STR[(*buf)->__AST_STR_USED] = '\0';
  119. return res;
  120. }
  121. char *__ast_str_helper2(struct ast_str **buf, ssize_t maxlen, const char *src, size_t maxsrc, int append, int escapecommas)
  122. {
  123. int dynamic = 0;
  124. char *ptr = append ? &((*buf)->__AST_STR_STR[(*buf)->__AST_STR_USED]) : (*buf)->__AST_STR_STR;
  125. if (maxlen < 1) {
  126. if (maxlen == 0) {
  127. dynamic = 1;
  128. }
  129. maxlen = (*buf)->__AST_STR_LEN;
  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. if ((ptr >= (*buf)->__AST_STR_STR + (*buf)->__AST_STR_LEN - 3) ||
  142. (dynamic && (!maxlen || (escapecommas && !(maxlen - 1))))) {
  143. char *oldbase = (*buf)->__AST_STR_STR;
  144. size_t old = (*buf)->__AST_STR_LEN;
  145. if (ast_str_make_space(buf, (*buf)->__AST_STR_LEN * 2)) {
  146. /* If the buffer can't be extended, end it. */
  147. break;
  148. }
  149. /* What we extended the buffer by */
  150. maxlen = old;
  151. ptr += (*buf)->__AST_STR_STR - oldbase;
  152. }
  153. }
  154. if (__builtin_expect(!maxlen, 0)) {
  155. ptr--;
  156. }
  157. *ptr = '\0';
  158. return (*buf)->__AST_STR_STR;
  159. }
  160. static int str_hash(const void *obj, const int flags)
  161. {
  162. return ast_str_hash(obj);
  163. }
  164. static int str_sort(const void *lhs, const void *rhs, int flags)
  165. {
  166. if ((flags & OBJ_SEARCH_MASK) == OBJ_SEARCH_PARTIAL_KEY) {
  167. return strncmp(lhs, rhs, strlen(rhs));
  168. } else {
  169. return strcmp(lhs, rhs);
  170. }
  171. }
  172. static int str_cmp(void *lhs, void *rhs, int flags)
  173. {
  174. int cmp = 0;
  175. if ((flags & OBJ_SEARCH_MASK) == OBJ_SEARCH_PARTIAL_KEY) {
  176. cmp = strncmp(lhs, rhs, strlen(rhs));
  177. } else {
  178. cmp = strcmp(lhs, rhs);
  179. }
  180. return cmp ? 0 : CMP_MATCH;
  181. }
  182. //struct ao2_container *ast_str_container_alloc_options(enum ao2_container_opts opts, int buckets)
  183. struct ao2_container *ast_str_container_alloc_options(enum ao2_alloc_opts opts, int buckets)
  184. {
  185. return ao2_container_alloc_hash(opts, 0, buckets, str_hash, str_sort, str_cmp);
  186. }
  187. int ast_str_container_add(struct ao2_container *str_container, const char *add)
  188. {
  189. char *ao2_add;
  190. /* The ao2_add object is immutable so it doesn't need a lock of its own. */
  191. ao2_add = ao2_alloc_options(strlen(add) + 1, NULL, AO2_ALLOC_OPT_LOCK_NOLOCK);
  192. if (!ao2_add) {
  193. return -1;
  194. }
  195. strcpy(ao2_add, add);/* Safe */
  196. ao2_link(str_container, ao2_add);
  197. ao2_ref(ao2_add, -1);
  198. return 0;
  199. }
  200. void ast_str_container_remove(struct ao2_container *str_container, const char *remove)
  201. {
  202. ao2_find(str_container, remove, OBJ_SEARCH_KEY | OBJ_NODATA | OBJ_UNLINK);
  203. }
  204. char *ast_generate_random_string(char *buf, size_t size)
  205. {
  206. int i;
  207. for (i = 0; i < size - 1; ++i) {
  208. buf[i] = 'a' + (ast_random() % 26);
  209. }
  210. buf[i] = '\0';
  211. return buf;
  212. }
  213. int ast_strings_match(const char *left, const char *op, const char *right)
  214. {
  215. char *internal_op = (char *)op;
  216. char *internal_right = (char *)right;
  217. double left_num;
  218. double right_num;
  219. int scan_numeric = 0;
  220. if (!(left && right)) {
  221. return 0;
  222. }
  223. if (ast_strlen_zero(op)) {
  224. if (ast_strlen_zero(left) && ast_strlen_zero(right)) {
  225. return 1;
  226. }
  227. if (strlen(right) >= 2 && right[0] == '/' && right[strlen(right) - 1] == '/') {
  228. internal_op = "regex";
  229. internal_right = ast_strdupa(right);
  230. /* strip the leading and trailing '/' */
  231. internal_right++;
  232. internal_right[strlen(internal_right) - 1] = '\0';
  233. goto regex;
  234. } else {
  235. internal_op = "=";
  236. goto equals;
  237. }
  238. }
  239. if (!strcasecmp(op, "like")) {
  240. char *tok;
  241. struct ast_str *buffer = ast_str_alloca(128);
  242. if (!strchr(right, '%')) {
  243. return !strcmp(left, right);
  244. } else {
  245. internal_op = "regex";
  246. internal_right = ast_strdupa(right);
  247. tok = strsep(&internal_right, "%");
  248. ast_str_set(&buffer, 0, "^%s", tok);
  249. while ((tok = strsep(&internal_right, "%"))) {
  250. ast_str_append(&buffer, 0, ".*%s", tok);
  251. }
  252. ast_str_append(&buffer, 0, "%s", "$");
  253. internal_right = ast_str_buffer(buffer);
  254. /* fall through to regex */
  255. }
  256. }
  257. regex:
  258. if (!strcasecmp(internal_op, "regex")) {
  259. regex_t expression;
  260. int rc;
  261. if (regcomp(&expression, internal_right, REG_EXTENDED | REG_NOSUB)) {
  262. return 0;
  263. }
  264. rc = regexec(&expression, left, 0, NULL, 0);
  265. regfree(&expression);
  266. return !rc;
  267. }
  268. equals:
  269. scan_numeric = (sscanf(left, "%lf", &left_num) > 0 && sscanf(internal_right, "%lf", &right_num) > 0);
  270. if (internal_op[0] == '=') {
  271. if (ast_strlen_zero(left) && ast_strlen_zero(internal_right)) {
  272. return 1;
  273. }
  274. if (scan_numeric) {
  275. return (left_num == right_num);
  276. } else {
  277. return (!strcmp(left, internal_right));
  278. }
  279. }
  280. if (internal_op[0] == '!' && internal_op[1] == '=') {
  281. if (scan_numeric) {
  282. return (left_num != right_num);
  283. } else {
  284. return !!strcmp(left, internal_right);
  285. }
  286. }
  287. if (internal_op[0] == '<') {
  288. if (scan_numeric) {
  289. if (internal_op[1] == '=') {
  290. return (left_num <= right_num);
  291. } else {
  292. return (left_num < right_num);
  293. }
  294. } else {
  295. if (internal_op[1] == '=') {
  296. return strcmp(left, internal_right) <= 0;
  297. } else {
  298. return strcmp(left, internal_right) < 0;
  299. }
  300. }
  301. }
  302. if (internal_op[0] == '>') {
  303. if (scan_numeric) {
  304. if (internal_op[1] == '=') {
  305. return (left_num >= right_num);
  306. } else {
  307. return (left_num > right_num);
  308. }
  309. } else {
  310. if (internal_op[1] == '=') {
  311. return strcmp(left, internal_right) >= 0;
  312. } else {
  313. return strcmp(left, internal_right) > 0;
  314. }
  315. }
  316. }
  317. return 0;
  318. }
  319. char *ast_read_line_from_buffer(char **buffer)
  320. {
  321. char *start = *buffer;
  322. if (!buffer || !*buffer || *(*buffer) == '\0') {
  323. return NULL;
  324. }
  325. while (*(*buffer) && *(*buffer) != '\n' ) {
  326. (*buffer)++;
  327. }
  328. *(*buffer) = '\0';
  329. if (*(*buffer - 1) == '\r') {
  330. *(*buffer - 1) = '\0';
  331. }
  332. (*buffer)++;
  333. return start;
  334. }