#include "asterisk.h"
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
+#include <regex.h>
#include "asterisk/strings.h"
#include "asterisk/pbx.h"
+#include "asterisk/vector.h"
/*!
* core handler for dynamic strings.
* ast_str_append_va(...)
*/
-#if (defined(MALLOC_DEBUG) && !defined(STANDALONE))
+#ifdef __AST_DEBUG_MALLOC
int __ast_debug_str_helper(struct ast_str **buf, ssize_t max_len,
int append, const char *fmt, va_list ap, const char *file, int lineno, const char *function)
#else
int append, const char *fmt, va_list ap)
#endif
{
- int res, need;
+ int res;
+ int added;
+ int need;
int offset = (append && (*buf)->__AST_STR_LEN) ? (*buf)->__AST_STR_USED : 0;
va_list aq;
+ if (max_len < 0) {
+ max_len = (*buf)->__AST_STR_LEN; /* don't exceed the allocated space */
+ }
+
do {
- if (max_len < 0) {
- max_len = (*buf)->__AST_STR_LEN; /* don't exceed the allocated space */
- }
- /*
- * Ask vsnprintf how much space we need. Remember that vsnprintf
- * does not count the final <code>'\\0'</code> so we must add 1.
- */
va_copy(aq, ap);
res = vsnprintf((*buf)->__AST_STR_STR + offset, (*buf)->__AST_STR_LEN - offset, fmt, aq);
+ va_end(aq);
+
+ if (res < 0) {
+ /*
+ * vsnprintf write to string failed.
+ * I don't think this is possible with a memory buffer.
+ */
+ res = AST_DYNSTR_BUILD_FAILED;
+ added = 0;
+ break;
+ }
- need = res + offset + 1;
/*
- * If there is not enough space and we are below the max length,
- * reallocate the buffer and return a message telling to retry.
+ * vsnprintf returns how much space we used or would need.
+ * Remember that vsnprintf does not count the nil terminator
+ * so we must add 1.
*/
- if (need > (*buf)->__AST_STR_LEN && (max_len == 0 || (*buf)->__AST_STR_LEN < max_len) ) {
- int len = (int)(*buf)->__AST_STR_LEN;
- if (max_len && max_len < need) { /* truncate as needed */
- need = max_len;
- } else if (max_len == 0) { /* if unbounded, give more room for next time */
- need += 16 + need / 4;
- }
- if (0) { /* debugging */
- ast_verbose("extend from %d to %d\n", len, need);
- }
- if (
-#if (defined(MALLOC_DEBUG) && !defined(STANDALONE))
- _ast_str_make_space(buf, need, file, lineno, function)
+ added = res;
+ need = offset + added + 1;
+ if (need <= (*buf)->__AST_STR_LEN
+ || (max_len && max_len <= (*buf)->__AST_STR_LEN)) {
+ /*
+ * There was enough room for the string or we are not
+ * allowed to try growing the string buffer.
+ */
+ break;
+ }
+
+ /* Reallocate the buffer and try again. */
+ if (max_len == 0) {
+ /* unbounded, give more room for next time */
+ need += 16 + need / 4;
+ } else if (max_len < need) {
+ /* truncate as needed */
+ need = max_len;
+ }
+
+ if (
+#ifdef __AST_DEBUG_MALLOC
+ _ast_str_make_space(buf, need, file, lineno, function)
#else
- ast_str_make_space(buf, need)
+ ast_str_make_space(buf, need)
#endif
- ) {
- ast_verbose("failed to extend from %d to %d\n", len, need);
- va_end(aq);
- return AST_DYNSTR_BUILD_FAILED;
- }
- (*buf)->__AST_STR_STR[offset] = '\0'; /* Truncate the partial write. */
+ ) {
+ ast_log_safe(LOG_VERBOSE, "failed to extend from %d to %d\n",
+ (int) (*buf)->__AST_STR_LEN, need);
- /* Restart va_copy before calling vsnprintf() again. */
- va_end(aq);
- continue;
+ res = AST_DYNSTR_BUILD_FAILED;
+ break;
}
- va_end(aq);
- break;
} while (1);
- /* update space used, keep in mind the truncation */
- (*buf)->__AST_STR_USED = (res + offset > (*buf)->__AST_STR_LEN) ? (*buf)->__AST_STR_LEN - 1: res + offset;
+
+ /* Update space used, keep in mind truncation may be necessary. */
+ (*buf)->__AST_STR_USED = ((*buf)->__AST_STR_LEN <= offset + added)
+ ? (*buf)->__AST_STR_LEN - 1
+ : offset + added;
+
+ /* Ensure that the string is terminated. */
+ (*buf)->__AST_STR_STR[(*buf)->__AST_STR_USED] = '\0';
return res;
}
return ast_str_hash(obj);
}
+static int str_sort(const void *lhs, const void *rhs, int flags)
+{
+ if ((flags & OBJ_SEARCH_MASK) == OBJ_SEARCH_PARTIAL_KEY) {
+ return strncmp(lhs, rhs, strlen(rhs));
+ } else {
+ return strcmp(lhs, rhs);
+ }
+}
+
static int str_cmp(void *lhs, void *rhs, int flags)
{
- return strcmp(lhs, rhs) ? 0 : CMP_MATCH;
+ int cmp = 0;
+
+ if ((flags & OBJ_SEARCH_MASK) == OBJ_SEARCH_PARTIAL_KEY) {
+ cmp = strncmp(lhs, rhs, strlen(rhs));
+ } else {
+ cmp = strcmp(lhs, rhs);
+ }
+
+ return cmp ? 0 : CMP_MATCH;
}
-struct ao2_container *ast_str_container_alloc_options(enum ao2_container_opts opts, int buckets)
+//struct ao2_container *ast_str_container_alloc_options(enum ao2_container_opts opts, int buckets)
+struct ao2_container *ast_str_container_alloc_options(enum ao2_alloc_opts opts, int buckets)
{
- return ao2_container_alloc_options(opts, buckets, str_hash, str_cmp);
+ return ao2_container_alloc_hash(opts, 0, buckets, str_hash, str_sort, str_cmp);
}
int ast_str_container_add(struct ao2_container *str_container, const char *add)
{
ao2_find(str_container, remove, OBJ_SEARCH_KEY | OBJ_NODATA | OBJ_UNLINK);
}
+
+char *ast_generate_random_string(char *buf, size_t size)
+{
+ int i;
+
+ for (i = 0; i < size - 1; ++i) {
+ buf[i] = 'a' + (ast_random() % 26);
+ }
+ buf[i] = '\0';
+
+ return buf;
+}
+
+int ast_strings_match(const char *left, const char *op, const char *right)
+{
+ char *internal_op = (char *)op;
+ char *internal_right = (char *)right;
+ double left_num;
+ double right_num;
+ int scan_numeric = 0;
+
+ if (!(left && right)) {
+ return 0;
+ }
+
+ if (ast_strlen_zero(op)) {
+ if (ast_strlen_zero(left) && ast_strlen_zero(right)) {
+ return 1;
+ }
+
+ if (strlen(right) >= 2 && right[0] == '/' && right[strlen(right) - 1] == '/') {
+ internal_op = "regex";
+ internal_right = ast_strdupa(right);
+ /* strip the leading and trailing '/' */
+ internal_right++;
+ internal_right[strlen(internal_right) - 1] = '\0';
+ goto regex;
+ } else {
+ internal_op = "=";
+ goto equals;
+ }
+ }
+
+ if (!strcasecmp(op, "like")) {
+ char *tok;
+ struct ast_str *buffer = ast_str_alloca(128);
+
+ if (!strchr(right, '%')) {
+ return !strcmp(left, right);
+ } else {
+ internal_op = "regex";
+ internal_right = ast_strdupa(right);
+ tok = strsep(&internal_right, "%");
+ ast_str_set(&buffer, 0, "^%s", tok);
+
+ while ((tok = strsep(&internal_right, "%"))) {
+ ast_str_append(&buffer, 0, ".*%s", tok);
+ }
+ ast_str_append(&buffer, 0, "%s", "$");
+
+ internal_right = ast_str_buffer(buffer);
+ /* fall through to regex */
+ }
+ }
+
+regex:
+ if (!strcasecmp(internal_op, "regex")) {
+ regex_t expression;
+ int rc;
+
+ if (regcomp(&expression, internal_right, REG_EXTENDED | REG_NOSUB)) {
+ return 0;
+ }
+
+ rc = regexec(&expression, left, 0, NULL, 0);
+ regfree(&expression);
+ return !rc;
+ }
+
+equals:
+ scan_numeric = (sscanf(left, "%lf", &left_num) > 0 && sscanf(internal_right, "%lf", &right_num) > 0);
+
+ if (internal_op[0] == '=') {
+ if (ast_strlen_zero(left) && ast_strlen_zero(internal_right)) {
+ return 1;
+ }
+
+ if (scan_numeric) {
+ return (left_num == right_num);
+ } else {
+ return (!strcmp(left, internal_right));
+ }
+ }
+
+ if (internal_op[0] == '!' && internal_op[1] == '=') {
+ if (scan_numeric) {
+ return (left_num != right_num);
+ } else {
+ return !!strcmp(left, internal_right);
+ }
+ }
+
+ if (internal_op[0] == '<') {
+ if (scan_numeric) {
+ if (internal_op[1] == '=') {
+ return (left_num <= right_num);
+ } else {
+ return (left_num < right_num);
+ }
+ } else {
+ if (internal_op[1] == '=') {
+ return strcmp(left, internal_right) <= 0;
+ } else {
+ return strcmp(left, internal_right) < 0;
+ }
+ }
+ }
+
+ if (internal_op[0] == '>') {
+ if (scan_numeric) {
+ if (internal_op[1] == '=') {
+ return (left_num >= right_num);
+ } else {
+ return (left_num > right_num);
+ }
+ } else {
+ if (internal_op[1] == '=') {
+ return strcmp(left, internal_right) >= 0;
+ } else {
+ return strcmp(left, internal_right) > 0;
+ }
+ }
+ }
+
+ return 0;
+}
+
+char *ast_read_line_from_buffer(char **buffer)
+{
+ char *start = *buffer;
+
+ if (!buffer || !*buffer || *(*buffer) == '\0') {
+ return NULL;
+ }
+
+ while (*(*buffer) && *(*buffer) != '\n' ) {
+ (*buffer)++;
+ }
+
+ *(*buffer) = '\0';
+ if (*(*buffer - 1) == '\r') {
+ *(*buffer - 1) = '\0';
+ }
+ (*buffer)++;
+
+ return start;
+}
+
+int ast_vector_string_split(struct ast_vector_string *dest,
+ const char *input, const char *delim, int flags,
+ int (*excludes_cmp)(const char *s1, const char *s2))
+{
+ char *buf;
+ char *cur;
+ int no_trim = flags & AST_VECTOR_STRING_SPLIT_NO_TRIM;
+ int allow_empty = flags & AST_VECTOR_STRING_SPLIT_ALLOW_EMPTY;
+
+ ast_assert(dest != NULL);
+ ast_assert(!ast_strlen_zero(delim));
+
+ if (ast_strlen_zero(input)) {
+ return 0;
+ }
+
+ buf = ast_strdupa(input);
+ while ((cur = strsep(&buf, delim))) {
+ if (!no_trim) {
+ cur = ast_strip(cur);
+ }
+
+ if (!allow_empty && ast_strlen_zero(cur)) {
+ continue;
+ }
+
+ if (excludes_cmp && AST_VECTOR_GET_CMP(dest, cur, !excludes_cmp)) {
+ continue;
+ }
+
+ cur = ast_strdup(cur);
+ if (!cur || AST_VECTOR_APPEND(dest, cur)) {
+ ast_free(cur);
+
+ return -1;
+ }
+ }
+
+ return 0;
+}