Skip to content
Snippets Groups Projects
utils.c 39.7 KiB
Newer Older
  • Learn to ignore specific revisions
  • char *ast_strip_quoted(char *s, const char *beg_quotes, const char *end_quotes)
    {
    	char *e;
    	char *q;
    
    	s = ast_strip(s);
    
    	if ((q = strchr(beg_quotes, *s)) && *q != '\0') {
    
    		e = s + strlen(s) - 1;
    		if (*e == *(end_quotes + (q - beg_quotes))) {
    			s++;
    			*e = '\0';
    		}
    	}
    
    	return s;
    }
    
    
    char *ast_unescape_semicolon(char *s)
    {
    	char *e;
    	char *work = s;
    
    	while ((e = strchr(work, ';'))) {
    		if ((e > work) && (*(e-1) == '\\')) {
    			memmove(e - 1, e, strlen(e) + 1);
    			work = e;
    
    		} else {
    			work = e + 1;
    
    /* !\brief unescape some C sequences in place, return pointer to the original string.
     */
    char *ast_unescape_c(char *src)
    {
    	char c, *ret, *dst;
    
    	if (src == NULL)
    		return NULL;
    	for (ret = dst = src; (c = *src++); *dst++ = c ) {
    		if (c != '\\')
    			continue;	/* copy char at the end of the loop */
    		switch ((c = *src++)) {
    		case '\0':	/* special, trailing '\' */
    			c = '\\';
    			break;
    		case 'b':	/* backspace */
    			c = '\b';
    			break;
    		case 'f':	/* form feed */
    			c = '\f';
    			break;
    		case 'n':
    			c = '\n';
    			break;
    		case 'r':
    			c = '\r';
    			break;
    		case 't':
    			c = '\t';
    			break;
    		}
    		/* default, use the char literally */
    	}
    	*dst = '\0';
    	return ret;
    }
    
    
    int ast_build_string_va(char **buffer, size_t *space, const char *fmt, va_list ap)
    
    {
    	int result;
    
    	if (!buffer || !*buffer || !space || !*space)
    		return -1;
    
    	result = vsnprintf(*buffer, *space, fmt, ap);
    
    	if (result < 0)
    		return -1;
    	else if (result > *space)
    		result = *space;
    
    	*buffer += result;
    	*space -= result;
    	return 0;
    }
    
    
    int ast_build_string(char **buffer, size_t *space, const char *fmt, ...)
    {
    	va_list ap;
    	int result;
    
    	va_start(ap, fmt);
    	result = ast_build_string_va(buffer, space, fmt, ap);
    	va_end(ap);
    
    	return result;
    }
    
    
    		return 0;
    
    	/* Determine if this is a true value */
    	if (!strcasecmp(s, "yes") ||
    	    !strcasecmp(s, "true") ||
    	    !strcasecmp(s, "y") ||
    	    !strcasecmp(s, "t") ||
    	    !strcasecmp(s, "1") ||
    	    !strcasecmp(s, "on"))
    		return -1;
    
    	return 0;
    }
    
    int ast_false(const char *s)
    {
    
    		return 0;
    
    	/* Determine if this is a false value */
    	if (!strcasecmp(s, "no") ||
    	    !strcasecmp(s, "false") ||
    	    !strcasecmp(s, "n") ||
    	    !strcasecmp(s, "f") ||
    	    !strcasecmp(s, "0") ||
    	    !strcasecmp(s, "off"))
    		return -1;
    
    	return 0;
    }
    
    
    #define ONE_MILLION	1000000
    /*
     * put timeval in a valid range. usec is 0..999999
     * negative values are not allowed and truncated.
     */
    static struct timeval tvfix(struct timeval a)
    {
    	if (a.tv_usec >= ONE_MILLION) {
    
    		ast_log(LOG_WARNING, "warning too large timestamp %ld.%ld\n",
    
    			a.tv_sec, (long int) a.tv_usec);
    
    		a.tv_sec += a.tv_usec / ONE_MILLION;
    
    		a.tv_usec %= ONE_MILLION;
    	} else if (a.tv_usec < 0) {
    
    		ast_log(LOG_WARNING, "warning negative timestamp %ld.%ld\n",
    
    			a.tv_sec, (long int) a.tv_usec);
    
    		a.tv_usec = 0;
    	}
    	return a;
    }
    
    struct timeval ast_tvadd(struct timeval a, struct timeval b)
    {
    	/* consistency checks to guarantee usec in 0..999999 */
    	a = tvfix(a);
    	b = tvfix(b);
    	a.tv_sec += b.tv_sec;
    	a.tv_usec += b.tv_usec;
    	if (a.tv_usec >= ONE_MILLION) {
    		a.tv_sec++;
    		a.tv_usec -= ONE_MILLION;
    	}
    	return a;
    }
    
    struct timeval ast_tvsub(struct timeval a, struct timeval b)
    {
    	/* consistency checks to guarantee usec in 0..999999 */
    	a = tvfix(a);
    	b = tvfix(b);
    	a.tv_sec -= b.tv_sec;
    	a.tv_usec -= b.tv_usec;
    	if (a.tv_usec < 0) {
    		a.tv_sec-- ;
    		a.tv_usec += ONE_MILLION;
    	}
    	return a;
    }
    #undef ONE_MILLION
    
    
    Olle Johansson's avatar
    Olle Johansson committed
    /*! \brief glibc puts a lock inside random(3), so that the results are thread-safe.
    
     * BSD libc (and others) do not. */
    
    AST_MUTEX_DEFINE_STATIC(randomlock);
    
    long int ast_random(void)
    {
    	long int res;
    
    #ifdef HAVE_DEV_URANDOM
    	if (dev_urandom_fd >= 0) {
    		int read_res = read(dev_urandom_fd, &res, sizeof(res));
    
    	ast_mutex_lock(&randomlock);
    	res = random();
    	ast_mutex_unlock(&randomlock);
    
    char *ast_process_quotes_and_slashes(char *start, char find, char replace_with)
    {
     	char *dataPut = start;
    	int inEscape = 0;
    	int inQuotes = 0;
    
    	for (; *start; start++) {
    		if (inEscape) {
    			*dataPut++ = *start;       /* Always goes verbatim */
    			inEscape = 0;
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    		} else {
    
    			if (*start == '\\') {
    				inEscape = 1;      /* Do not copy \ into the data */
    			} else if (*start == '\'') {
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    				inQuotes = 1 - inQuotes;   /* Do not copy ' into the data */
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    				*dataPut++ = inQuotes ? *start : ((*start == find) ? replace_with : *start);
    
    void ast_join(char *s, size_t len, char * const w[])
    
    {
    	int x, ofs = 0;
    	const char *src;
    
    	/* Join words into a string */
    	if (!s)
    		return;
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    	for (x = 0; ofs < len && w[x]; x++) {
    
    		if (x > 0)
    			s[ofs++] = ' ';
    		for (src = w[x]; *src && ofs < len; src++)
    			s[ofs++] = *src;
    	}
    	if (ofs == len)
    		ofs--;
    	s[ofs] = '\0';
    }
    
    /*
     * stringfields support routines.
     */
    
    const char __ast_string_field_empty[] = ""; /*!< the empty string */
    
    /*! \brief add a new block to the pool.
     * We can only allocate from the topmost pool, so the
     * fields in *mgr reflect the size of that only.
     */
    static int add_string_pool(struct ast_string_field_mgr *mgr,
    	struct ast_string_field_pool **pool_head, size_t size)
    
    {
    	struct ast_string_field_pool *pool;
    
    	if (!(pool = ast_calloc(1, sizeof(*pool) + size)))
    		return -1;
    	
    
    	pool->prev = *pool_head;
    	*pool_head = pool;
    
    	mgr->size = size;
    	mgr->used = 0;
    
    	return 0;
    }
    
    
    /*
     * This is an internal API, code should not use it directly.
     * It initializes all fields as empty, then uses 'size' for 3 functions:
     * size > 0 means initialize the pool list with a pool of given size.
     *	This must be called right after allocating the object.
     * size = 0 means release all pools except the most recent one.
     *	This is useful to e.g. reset an object to the initial value.
     * size < 0 means release all pools.
     *	This must be done before destroying the object.
     */
    int __ast_string_field_init(struct ast_string_field_mgr *mgr,
    
    	struct ast_string_field_pool **pool_head, int size)
    
    	const char **p = (const char **)pool_head + 1;
    	struct ast_string_field_pool *cur = *pool_head;
    
    	/* clear fields - this is always necessary */
    	while ((struct ast_string_field_mgr *)p != mgr)
    		*p++ = __ast_string_field_empty;
    	if (size > 0) {			/* allocate the initial pool */
    		*pool_head = NULL;
    		return add_string_pool(mgr, pool_head, size);
    	}
    	if (size < 0) {			/* reset all pools */
    		*pool_head = NULL;
    	} else {			/* preserve the first pool */
    		if (cur == NULL) {
    			ast_log(LOG_WARNING, "trying to reset empty pool\n");
    			return -1;
    		}
    		cur = cur->prev;
    		(*pool_head)->prev = NULL;
    		mgr->used = 0;
    	}
    	while (cur) {
    		struct ast_string_field_pool *prev = cur->prev;
    		ast_free(cur);
    		cur = prev;
    	}
    
    ast_string_field __ast_string_field_alloc_space(struct ast_string_field_mgr *mgr,
    	struct ast_string_field_pool **pool_head, size_t needed)
    
    	size_t space = mgr->size - mgr->used;
    
    	if (__builtin_expect(needed > space, 0)) {
    
    		size_t new_size = mgr->size * 2;
    
    		while (new_size < needed)
    
    		if (add_string_pool(mgr, pool_head, new_size))
    
    	result = (*pool_head)->base + mgr->used;
    
    	mgr->used += needed;
    
    void __ast_string_field_ptr_build_va(struct ast_string_field_mgr *mgr,
    	struct ast_string_field_pool **pool_head,
    	const ast_string_field *ptr, const char *format, va_list ap1, va_list ap2)
    
    	char *dst = (*pool_head)->base + mgr->used;
    	const char **p = (const char **)ptr;
    	size_t space = mgr->size - mgr->used;
    
    	/* try to write using available space */
    	needed = vsnprintf(dst, space, format, ap1) + 1;
    
    	if (needed > space) {	/* if it fails, reallocate */
    
    		size_t new_size = mgr->size * 2;
    
    		while (new_size < needed)
    			new_size *= 2;
    
    
    		if (add_string_pool(mgr, pool_head, new_size))
    
    		dst = (*pool_head)->base + mgr->used;
    		vsprintf(dst, format, ap2);
    
    	mgr->used += needed;
    
    void __ast_string_field_ptr_build(struct ast_string_field_mgr *mgr,
    	struct ast_string_field_pool **pool_head,
    	const ast_string_field *ptr, const char *format, ...)
    
    	va_start(ap1, format);
    	va_start(ap2, format);		/* va_copy does not exist on FreeBSD */
    
    
    	__ast_string_field_ptr_build_va(mgr, pool_head, ptr, format, ap1, ap2);
    
    /* end of stringfields support */
    
    AST_MUTEX_DEFINE_STATIC(fetchadd_m); /* used for all fetc&add ops */
    
    int ast_atomic_fetchadd_int_slow(volatile int *p, int v)
    {
            int ret;
            ast_mutex_lock(&fetchadd_m);
            ret = *p;
            *p += v;
            ast_mutex_unlock(&fetchadd_m);
            return ret;
    }
    
    /*! \brief
     * get values from config variables.
     */
    int ast_get_timeval(const char *src, struct timeval *dst, struct timeval _default, int *consumed)
    {
    	long double dtv = 0.0;
    	int scanned;
    
    	if (dst == NULL)
    		return -1;
    
    	*dst = _default;
    
    	if (ast_strlen_zero(src))
    		return -1;
    
    	/* only integer at the moment, but one day we could accept more formats */
    	if (sscanf(src, "%Lf%n", &dtv, &scanned) > 0) {
    		dst->tv_sec = dtv;
    		dst->tv_usec = (dtv - dst->tv_sec) * 1000000.0;
    		if (consumed)
    			*consumed = scanned;
    		return 0;
    	} else
    		return -1;
    }
    
    
    Olle Johansson's avatar
    Olle Johansson committed
    /*! \brief
    
    int ast_get_time_t(const char *src, time_t *dst, time_t _default, int *consumed)
    
    
    	if (dst == NULL)
    		return -1;
    
    	*dst = _default;
    
    	if (ast_strlen_zero(src))
    		return -1;
    
    	/* only integer at the moment, but one day we could accept more formats */
    
    	if (sscanf(src, "%ld%n", &t, &scanned) == 1) {
    
    /*!
     * core handler for dynamic strings.
     * This is not meant to be called directly, but rather through the
     * various wrapper macros
    
     *	ast_str_set(...)
     *	ast_str_append(...)
     *	ast_str_set_va(...)
     *	ast_str_append_va(...)
    
    int __ast_str_helper(struct ast_str **buf, size_t max_len,
    	int append, const char *fmt, va_list ap)
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	int res, need;
    
    	int offset = (append && (*buf)->len) ? (*buf)->used : 0;
    
    	if (max_len < 0)
    		max_len = (*buf)->len;	/* don't exceed the allocated space */
    	/*
    	 * Ask vsnprintf how much space we need. Remember that vsnprintf
    	 * does not count the final '\0' so we must add 1.
    	 */
    
    	res = vsnprintf((*buf)->str + offset, (*buf)->len - offset, fmt, ap);
    
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	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.
    	 */
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	if (need > (*buf)->len && (max_len == 0 || (*buf)->len < max_len) ) {
    
    		if (max_len && max_len < need)	/* truncate as needed */
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    			need = max_len;
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		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", (int)(*buf)->len, need);
    		if (ast_str_make_space(buf, need)) {
    			ast_verbose("failed to extend from %d to %d\n", (int)(*buf)->len, need);
    
    			return AST_DYNSTR_BUILD_FAILED;
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		}
    
    		(*buf)->str[offset] = '\0';	/* Truncate the partial write. */
    
    		/* va_end() and va_start() must be done before calling
    		 * vsnprintf() again. */
    		return AST_DYNSTR_BUILD_RETRY;
    	}
    
    	/* update space used, keep in mind the truncation */
    	(*buf)->used = (res + offset > (*buf)->len) ? (*buf)->len : res + offset;
    
    
    void ast_enable_packet_fragmentation(int sock)
    {
    
    #if defined(HAVE_IP_MTU_DISCOVER)
    
    	int val = IP_PMTUDISC_DONT;
    	
    	if (setsockopt(sock, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val)))
    		ast_log(LOG_WARNING, "Unable to disable PMTU discovery. Large UDP packets may fail to be delivered when sent from this socket.\n");
    
    #endif /* HAVE_IP_MTU_DISCOVER */
    
    
    int ast_mkdir(const char *path, int mode)
    {
    	char *ptr;
    	int len = strlen(path), count = 0, x, piececount = 0;
    	char *tmp = ast_strdupa(path);
    	char **pieces;
    	char *fullpath = alloca(len + 1);
    	int res = 0;
    
    	for (ptr = tmp; *ptr; ptr++) {
    		if (*ptr == '/')
    			count++;
    	}
    
    	/* Count the components to the directory path */
    	pieces = alloca(count * sizeof(*pieces));
    	for (ptr = tmp; *ptr; ptr++) {
    		if (*ptr == '/') {
    			*ptr = '\0';
    			pieces[piececount++] = ptr + 1;
    		}
    	}
    
    	*fullpath = '\0';
    	for (x = 0; x < piececount; x++) {
    		/* This looks funky, but the buffer is always ideally-sized, so it's fine. */
    		strcat(fullpath, "/");
    		strcat(fullpath, pieces[x]);
    		res = mkdir(fullpath, mode);
    		if (res && errno != EEXIST)
    			return errno;
    	}
    	return 0;
    }
    
    
    int ast_utils_init(void)
    {
    #ifdef HAVE_DEV_URANDOM
    	dev_urandom_fd = open("/dev/urandom", O_RDONLY);
    #endif
    	base64_init();
    #ifdef DEBUG_THREADS
    	ast_cli_register_multiple(utils_cli, sizeof(utils_cli) / sizeof(utils_cli[0]));
    #endif
    	return 0;
    }