Newer
Older
* \brief A list of each thread's lock info
*/
static AST_LIST_HEAD_NOLOCK_STATIC(lock_infos, thr_lock_info);
/*!
* \brief Destroy a thread's lock info
*
* This gets called automatically when the thread stops
*/
static void lock_info_destroy(void *data)
{
struct thr_lock_info *lock_info = data;
int i;
pthread_mutex_lock(&lock_infos_lock.mutex);
AST_LIST_REMOVE(&lock_infos, lock_info, entry);
pthread_mutex_unlock(&lock_infos_lock.mutex);
for (i = 0; i < lock_info->num_locks; i++) {
if (lock_info->locks[i].pending == -1) {
/* This just means that the last lock this thread went for was by
* using trylock, and it failed. This is fine. */
break;
}
ast_log(LOG_ERROR,
"Thread '%s' still has a lock! - '%s' (%p) from '%s' in %s:%d!\n",
lock_info->thread_name,
lock_info->locks[i].lock_name,
lock_info->locks[i].lock_addr,
lock_info->locks[i].func,
lock_info->locks[i].file,
lock_info->locks[i].line_num
);
}
pthread_mutex_destroy(&lock_info->lock);
if (lock_info->thread_name) {
ast_free((void *) lock_info->thread_name);
}
ast_free(lock_info);
}
/*!
* \brief The thread storage key for per-thread lock info
*/
AST_THREADSTORAGE_CUSTOM(thread_lock_info, NULL, lock_info_destroy);
void ast_store_lock_info(enum ast_lock_type type, const char *filename,
int line_num, const char *func, const char *lock_name, void *lock_addr, struct ast_bt *bt)
struct thr_lock_info *lock_info;
int i;
if (!(lock_info = ast_threadstorage_get(&thread_lock_info, sizeof(*lock_info))))
return;
pthread_mutex_lock(&lock_info->lock);
for (i = 0; i < lock_info->num_locks; i++) {
if (lock_info->locks[i].lock_addr == lock_addr) {
lock_info->locks[i].times_locked++;
#ifdef HAVE_BKTR
lock_info->locks[i].backtrace = bt;
#endif
pthread_mutex_unlock(&lock_info->lock);
return;
}
}
if (lock_info->num_locks == AST_MAX_LOCKS) {
/* Can't use ast_log here, because it will cause infinite recursion */
fprintf(stderr, "XXX ERROR XXX A thread holds more locks than '%d'."
" Increase AST_MAX_LOCKS!\n", AST_MAX_LOCKS);
pthread_mutex_unlock(&lock_info->lock);
return;
}
if (i && lock_info->locks[i - 1].pending == -1) {
/* The last lock on the list was one that this thread tried to lock but
* failed at doing so. It has now moved on to something else, so remove
* the old lock from the list. */
i--;
lock_info->num_locks--;
memset(&lock_info->locks[i], 0, sizeof(lock_info->locks[0]));
}
lock_info->locks[i].file = filename;
lock_info->locks[i].line_num = line_num;
lock_info->locks[i].func = func;
lock_info->locks[i].lock_name = lock_name;
lock_info->locks[i].lock_addr = lock_addr;
lock_info->locks[i].times_locked = 1;
lock_info->locks[i].type = type;
lock_info->locks[i].pending = 1;
#ifdef HAVE_BKTR
lock_info->locks[i].backtrace = bt;
#endif
lock_info->num_locks++;
pthread_mutex_unlock(&lock_info->lock);
void ast_mark_lock_acquired(void *lock_addr)
struct thr_lock_info *lock_info;
if (!(lock_info = ast_threadstorage_get(&thread_lock_info, sizeof(*lock_info))))
return;
pthread_mutex_lock(&lock_info->lock);
if (lock_info->locks[lock_info->num_locks - 1].lock_addr == lock_addr) {
lock_info->locks[lock_info->num_locks - 1].pending = 0;
}
pthread_mutex_unlock(&lock_info->lock);
void ast_mark_lock_failed(void *lock_addr)
struct thr_lock_info *lock_info;
if (!(lock_info = ast_threadstorage_get(&thread_lock_info, sizeof(*lock_info))))
return;
pthread_mutex_lock(&lock_info->lock);
if (lock_info->locks[lock_info->num_locks - 1].lock_addr == lock_addr) {
lock_info->locks[lock_info->num_locks - 1].pending = -1;
lock_info->locks[lock_info->num_locks - 1].times_locked--;
}
pthread_mutex_unlock(&lock_info->lock);
int ast_find_lock_info(void *lock_addr, char *filename, size_t filename_size, int *lineno, char *func, size_t func_size, char *mutex_name, size_t mutex_name_size)
struct thr_lock_info *lock_info;
int i = 0;
if (!(lock_info = ast_threadstorage_get(&thread_lock_info, sizeof(*lock_info))))
return -1;
pthread_mutex_lock(&lock_info->lock);
for (i = lock_info->num_locks - 1; i >= 0; i--) {
if (lock_info->locks[i].lock_addr == lock_addr)
break;
}
if (i == -1) {
/* Lock not found :( */
pthread_mutex_unlock(&lock_info->lock);
return -1;
}
ast_copy_string(filename, lock_info->locks[i].file, filename_size);
*lineno = lock_info->locks[i].line_num;
ast_copy_string(func, lock_info->locks[i].func, func_size);
ast_copy_string(mutex_name, lock_info->locks[i].lock_name, mutex_name_size);
pthread_mutex_unlock(&lock_info->lock);
#else /* if defined(LOW_MEMORY) */
return -1;
#endif
void ast_suspend_lock_info(void *lock_addr)
{
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
struct thr_lock_info *lock_info;
int i = 0;
if (!(lock_info = ast_threadstorage_get(&thread_lock_info, sizeof(*lock_info)))) {
return;
}
pthread_mutex_lock(&lock_info->lock);
for (i = lock_info->num_locks - 1; i >= 0; i--) {
if (lock_info->locks[i].lock_addr == lock_addr)
break;
}
if (i == -1) {
/* Lock not found :( */
pthread_mutex_unlock(&lock_info->lock);
return;
}
lock_info->locks[i].suspended = 1;
pthread_mutex_unlock(&lock_info->lock);
}
void ast_restore_lock_info(void *lock_addr)
{
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
struct thr_lock_info *lock_info;
int i = 0;
if (!(lock_info = ast_threadstorage_get(&thread_lock_info, sizeof(*lock_info))))
return;
pthread_mutex_lock(&lock_info->lock);
for (i = lock_info->num_locks - 1; i >= 0; i--) {
if (lock_info->locks[i].lock_addr == lock_addr)
break;
}
if (i == -1) {
/* Lock not found :( */
pthread_mutex_unlock(&lock_info->lock);
return;
}
lock_info->locks[i].suspended = 0;
pthread_mutex_unlock(&lock_info->lock);
void ast_remove_lock_info(void *lock_addr, struct ast_bt *bt)
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
struct thr_lock_info *lock_info;
int i = 0;
if (!(lock_info = ast_threadstorage_get(&thread_lock_info, sizeof(*lock_info))))
return;
pthread_mutex_lock(&lock_info->lock);
for (i = lock_info->num_locks - 1; i >= 0; i--) {
if (lock_info->locks[i].lock_addr == lock_addr)
break;
}
if (i == -1) {
/* Lock not found :( */
pthread_mutex_unlock(&lock_info->lock);
return;
}
if (lock_info->locks[i].times_locked > 1) {
lock_info->locks[i].times_locked--;
#ifdef HAVE_BKTR
lock_info->locks[i].backtrace = bt;
#endif
pthread_mutex_unlock(&lock_info->lock);
return;
}
if (i < lock_info->num_locks - 1) {
/* Not the last one ... *should* be rare! */
memmove(&lock_info->locks[i], &lock_info->locks[i + 1],
(lock_info->num_locks - (i + 1)) * sizeof(lock_info->locks[0]));
}
lock_info->num_locks--;
pthread_mutex_unlock(&lock_info->lock);
static const char *locktype2str(enum ast_lock_type type)
{
switch (type) {
case AST_MUTEX:
return "MUTEX";
case AST_RDLOCK:
return "RDLOCK";
case AST_WRLOCK:
return "WRLOCK";
}
return "UNKNOWN";
}
#ifdef HAVE_BKTR
static void append_backtrace_information(struct ast_str **str, struct ast_bt *bt)
{
struct ast_vector_string *symbols;
if (!bt) {
ast_str_append(str, 0, "\tNo backtrace to print\n");
return;
}
/* store frame count locally to avoid the memory corruption that
* sometimes happens on virtualized CentOS 6.x systems */
if ((symbols = ast_bt_get_symbols(bt->addresses, num_frames))) {
int frame_iterator;
for (frame_iterator = 1; frame_iterator < AST_VECTOR_SIZE(symbols); ++frame_iterator) {
ast_str_append(str, 0, "\t%s\n", AST_VECTOR_GET(symbols, frame_iterator));
ast_bt_free_symbols(symbols);
} else {
ast_str_append(str, 0, "\tCouldn't retrieve backtrace symbols\n");
}
}
#endif
static void append_lock_information(struct ast_str **str, struct thr_lock_info *lock_info, int i)
{
int j;
ast_mutex_t *lock;
struct ast_lock_track *lt;
ast_str_append(str, 0, "=== ---> %sLock #%d (%s): %s %d %s %s %p (%d%s)\n",
lock_info->locks[i].pending > 0 ? "Waiting for " :
locktype2str(lock_info->locks[i].type),
lock_info->locks[i].line_num,
lock_info->locks[i].func, lock_info->locks[i].lock_name,
lock_info->locks[i].times_locked,
lock_info->locks[i].suspended ? " - suspended" : "");
#ifdef HAVE_BKTR
append_backtrace_information(str, lock_info->locks[i].backtrace);
#endif
if (!lock_info->locks[i].pending || lock_info->locks[i].pending == -1)
return;
/* We only have further details for mutexes right now */
if (lock_info->locks[i].type != AST_MUTEX)
return;
ast_reentrancy_lock(lt);
for (j = 0; *str && j < lt->reentrancy; j++) {
lt->file[j], lt->lineno[j], lt->func[j]);
/*! This function can help you find highly temporal locks; locks that happen for a
short time, but at unexpected times, usually at times that create a deadlock,
Why is this thing locked right then? Who is locking it? Who am I fighting
To answer such questions, just call this routine before you would normally try
to acquire a lock. It doesn't do anything if the lock is not acquired. If the
lock is taken, it will publish a line or two to the console via ast_log().
Sometimes, the lock message is pretty uninformative. For instance, you might
find that the lock is being acquired deep within the astobj2 code; this tells
you little about higher level routines that call the astobj2 routines.
But, using gdb, you can set a break at the ast_log below, and for that
breakpoint, you can set the commands:
where
cont
which will give a stack trace and continue. -- that aught to do the job!
*/
void ast_log_show_lock(void *this_lock_addr)
Steve Murphy
committed
{
Steve Murphy
committed
struct thr_lock_info *lock_info;
struct ast_str *str;
if (!(str = ast_str_create(4096))) {
ast_log(LOG_NOTICE,"Could not create str\n");
return;
}
Steve Murphy
committed
pthread_mutex_lock(&lock_infos_lock.mutex);
AST_LIST_TRAVERSE(&lock_infos, lock_info, entry) {
int i;
pthread_mutex_lock(&lock_info->lock);
for (i = 0; str && i < lock_info->num_locks; i++) {
/* ONLY show info about this particular lock, if
it's acquired... */
Steve Murphy
committed
if (lock_info->locks[i].lock_addr == this_lock_addr) {
ast_log(LOG_NOTICE, "%s", ast_str_buffer(str));
Steve Murphy
committed
}
}
pthread_mutex_unlock(&lock_info->lock);
}
pthread_mutex_unlock(&lock_infos_lock.mutex);
Steve Murphy
committed
}
struct ast_str *ast_dump_locks(void)
struct thr_lock_info *lock_info;
if (!(str = ast_str_create(4096))) {
"=======================================================================\n"
"=== %s\n"
"=== Currently Held Locks\n"
"=======================================================================\n"
"===\n"
Steve Murphy
committed
"=== <pending> <lock#> (<file>): <lock type> <line num> <function> <lock name> <lock addr> (times locked)\n"
if (!str) {
return NULL;
}
pthread_mutex_lock(&lock_infos_lock.mutex);
AST_LIST_TRAVERSE(&lock_infos, lock_info, entry) {
int i;
int header_printed = 0;
pthread_mutex_lock(&lock_info->lock);
for (i = 0; str && i < lock_info->num_locks; i++) {
/* Don't show suspended locks */
if (lock_info->locks[i].suspended) {
continue;
if (!header_printed) {
if (lock_info->lwp != -1) {
ast_str_append(&str, 0, "=== Thread ID: 0x%lx LWP:%d (%s)\n",
(long unsigned) lock_info->thread_id, lock_info->lwp, lock_info->thread_name);
} else {
ast_str_append(&str, 0, "=== Thread ID: 0x%lx (%s)\n",
(long unsigned) lock_info->thread_id, lock_info->thread_name);
header_printed = 1;
}
append_lock_information(&str, lock_info, i);
}
pthread_mutex_unlock(&lock_info->lock);
if (!str) {
break;
}
if (header_printed) {
ast_str_append(&str, 0, "=== -------------------------------------------------------------------\n"
"===\n");
}
if (!str) {
break;
}
}
pthread_mutex_unlock(&lock_infos_lock.mutex);
if (!str) {
return NULL;
}
ast_str_append(&str, 0, "=======================================================================\n"
#else /* if defined(LOW_MEMORY) */
return NULL;
#endif
static char *handle_show_locks(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
struct ast_str *str;
switch (cmd) {
case CLI_INIT:
e->command = "core show locks";
e->usage =
"Usage: core show locks\n"
" This command is for lock debugging. It prints out which locks\n"
"are owned by each active thread.\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
str = ast_dump_locks();
if (!str) {
ast_cli(a->fd, "%s", ast_str_buffer(str));
Tilghman Lesher
committed
ast_free(str);
}
static struct ast_cli_entry utils_cli[] = {
Jason Parker
committed
AST_CLI_DEFINE(handle_show_locks, "Show which locks are held by which thread"),
#endif /* DEBUG_THREADS */
/*
* support for 'show threads'. The start routine is wrapped by
* dummy_start(), so that ast_register_thread() and
* ast_unregister_thread() know the thread identifier.
*/
struct thr_arg {
void *(*start_routine)(void *);
void *data;
char *name;
};
/*
* on OS/X, pthread_cleanup_push() and pthread_cleanup_pop()
* are odd macros which start and end a block, so they _must_ be
* used in pairs (the latter with a '1' argument to call the
* handler on exit.
* On BSD we don't need this, but we keep it for compatibility.
*/
static void *dummy_start(void *data)
struct thr_arg a = *((struct thr_arg *) data); /* make a local copy */
#ifdef DEBUG_THREADS
struct thr_lock_info *lock_info;
pthread_mutexattr_t mutex_attr;
if (!(lock_info = ast_threadstorage_get(&thread_lock_info, sizeof(*lock_info))))
return NULL;
lock_info->thread_id = pthread_self();
lock_info->lwp = ast_get_tid();
lock_info->thread_name = ast_strdup(a.name);
pthread_mutexattr_init(&mutex_attr);
pthread_mutexattr_settype(&mutex_attr, AST_MUTEX_KIND);
pthread_mutex_init(&lock_info->lock, &mutex_attr);
pthread_mutexattr_destroy(&mutex_attr);
pthread_mutex_lock(&lock_infos_lock.mutex); /* Intentionally not the wrapper */
AST_LIST_INSERT_TAIL(&lock_infos, lock_info, entry);
pthread_mutex_unlock(&lock_infos_lock.mutex); /* Intentionally not the wrapper */
#endif /* DEBUG_THREADS */
Matthew Jordan
committed
/* note that even though data->name is a pointer to allocated memory,
we are not freeing it here because ast_register_thread is going to
keep a copy of the pointer and then ast_unregister_thread will
free the memory
*/
ast_free(data);
ast_register_thread(a.name);
pthread_cleanup_push(ast_unregister_thread, (void *) pthread_self());
ret = a.start_routine(a.data);
#endif /* !LOW_MEMORY */
int ast_background_stacksize(void)
{
#if !defined(LOW_MEMORY)
return AST_STACKSIZE;
#else
return AST_STACKSIZE_LOW;
#endif
}
int ast_pthread_create_stack(pthread_t *thread, pthread_attr_t *attr, void *(*start_routine)(void *),
void *data, size_t stacksize, const char *file, const char *caller,
int line, const char *start_fn)
attr = ast_alloca(sizeof(*attr));
#if defined(__linux__) || defined(__FreeBSD__)
/* On Linux and FreeBSD , pthread_attr_init() defaults to PTHREAD_EXPLICIT_SCHED,
which is kind of useless. Change this here to
PTHREAD_INHERIT_SCHED; that way the -p option to set realtime
priority will propagate down to new threads by default.
This does mean that callers cannot set a different priority using
PTHREAD_EXPLICIT_SCHED in the attr argument; instead they must set
the priority afterwards with pthread_setschedparam(). */
if ((errno = pthread_attr_setinheritsched(attr, PTHREAD_INHERIT_SCHED)))
ast_log(LOG_WARNING, "pthread_attr_setinheritsched: %s\n", strerror(errno));
#endif
if (!stacksize)
stacksize = AST_STACKSIZE;
if ((errno = pthread_attr_setstacksize(attr, stacksize ? stacksize : AST_STACKSIZE)))
ast_log(LOG_WARNING, "pthread_attr_setstacksize: %s\n", strerror(errno));
if ((a = ast_malloc(sizeof(*a)))) {
a->start_routine = start_routine;
a->data = data;
start_routine = dummy_start;
if (ast_asprintf(&a->name, "%-20s started at [%5d] %s %s()",
Kevin P. Fleming
committed
start_fn, line, file, caller) < 0) {
a->name = NULL;
}
return pthread_create(thread, attr, start_routine, data); /* We're in ast_pthread_create, so it's okay */
}
Russell Bryant
committed
int ast_pthread_create_detached_stack(pthread_t *thread, pthread_attr_t *attr, void *(*start_routine)(void *),
void *data, size_t stacksize, const char *file, const char *caller,
int line, const char *start_fn)
{
unsigned char attr_destroy = 0;
int res;
if (!attr) {
attr = ast_alloca(sizeof(*attr));
Russell Bryant
committed
pthread_attr_init(attr);
attr_destroy = 1;
}
if ((errno = pthread_attr_setdetachstate(attr, PTHREAD_CREATE_DETACHED)))
ast_log(LOG_WARNING, "pthread_attr_setdetachstate: %s\n", strerror(errno));
res = ast_pthread_create_stack(thread, attr, start_routine, data,
Russell Bryant
committed
stacksize, file, caller, line, start_fn);
if (attr_destroy)
pthread_attr_destroy(attr);
return res;
}
int ast_wait_for_input(int fd, int ms)
{
struct pollfd pfd[1];
Richard Mudgett
committed
memset(pfd, 0, sizeof(pfd));
pfd[0].fd = fd;
pfd[0].events = POLLIN | POLLPRI;
return ast_poll(pfd, 1, ms);
}
int ast_wait_for_output(int fd, int ms)
{
struct pollfd pfd[1];
memset(pfd, 0, sizeof(pfd));
pfd[0].fd = fd;
Richard Mudgett
committed
pfd[0].events = POLLOUT;
return ast_poll(pfd, 1, ms);
Richard Mudgett
committed
static int wait_for_output(int fd, int timeoutms)
{
struct pollfd pfd = {
.fd = fd,
.events = POLLOUT,
};
int res;
struct timeval start = ast_tvnow();
int elapsed = 0;
/* poll() until the fd is writable without blocking */
while ((res = ast_poll(&pfd, 1, timeoutms - elapsed)) <= 0) {
if (res == 0) {
/* timed out. */
ast_debug(1, "Timed out trying to write\n");
return -1;
} else if (res == -1) {
/* poll() returned an error, check to see if it was fatal */
if (errno == EINTR || errno == EAGAIN) {
elapsed = ast_tvdiff_ms(ast_tvnow(), start);
if (elapsed >= timeoutms) {
return -1;
}
/* This was an acceptable error, go back into poll() */
continue;
}
/* Fatal error, bail. */
ast_log(LOG_ERROR, "poll returned error: %s\n", strerror(errno));
return -1;
}
elapsed = ast_tvdiff_ms(ast_tvnow(), start);
if (elapsed >= timeoutms) {
return -1;
}
}
return 0;
}
/*!
* Try to write string, but wait no more than ms milliseconds before timing out.
*
* \note The code assumes that the file descriptor has NONBLOCK set,
* so there is only one system call made to do a write, unless we actually
* have a need to wait. This way, we get better performance.
* If the descriptor is blocking, all assumptions on the guaranteed
* detail do not apply anymore.
*/
int ast_carefulwrite(int fd, char *s, int len, int timeoutms)
struct timeval start = ast_tvnow();
int res = 0;
while (len) {
Richard Mudgett
committed
if (wait_for_output(fd, timeoutms - elapsed)) {
return -1;
res = write(fd, s, len);
if (res < 0 && errno != EAGAIN && errno != EINTR) {
/* fatal error from write() */
if (errno == EPIPE) {
#ifndef STANDALONE
ast_debug(1, "write() failed due to reading end being closed: %s\n", strerror(errno));
#endif
} else {
ast_log(LOG_ERROR, "write() returned error: %s\n", strerror(errno));
}
return -1;
}
if (res < 0) {
/* It was an acceptable error */
res = 0;
}
/* Update how much data we have left to write */
len -= res;
s += res;
res = 0;
elapsed = ast_tvdiff_ms(ast_tvnow(), start);
if (elapsed >= timeoutms) {
* This is only an error condition if we haven't finished writing. */
res = len ? -1 : 0;
break;
}
return res;
}
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_strsep(char **iss, const char sep, uint32_t flags)
{
char *st = *iss;
char *is;
int inquote = 0;
int found = 0;
char stack[8];
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
return NULL;
}
memset(stack, 0, sizeof(stack));
for(is = st; *is; is++) {
if (*is == '\\') {
if (*++is != '\0') {
is++;
} else {
break;
}
}
if (*is == '\'' || *is == '"') {
if (*is == stack[inquote]) {
stack[inquote--] = '\0';
} else {
if (++inquote >= sizeof(stack)) {
return NULL;
}
stack[inquote] = *is;
}
}
if (*is == sep && !inquote) {
*is = '\0';
found = 1;
*iss = is + 1;
break;
}
}
if (!found) {
*iss = NULL;
}
if (flags & AST_STRSEP_STRIP) {
st = ast_strip_quoted(st, "'\"", "'\"");
}
if (flags & AST_STRSEP_TRIM) {
st = ast_strip(st);
}
if (flags & AST_STRSEP_UNESCAPE) {
ast_unescape_quoted(st);
}
return st;
}
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;
}
}
return s;
}
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
/* !\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;
}
/*
* Standard escape sequences - Note, '\0' is not included as a valid character
* to escape, but instead is used here as a NULL terminator for the string.
*/
char escape_sequences[] = {
'\a', '\b', '\f', '\n', '\r', '\t', '\v', '\\', '\'', '\"', '\?', '\0'
};
/*
* Standard escape sequences output map (has to maintain matching order with
* escape_sequences). '\0' is included here as a NULL terminator for the string.
*/
static char escape_sequences_map[] = {
'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', '\'', '"', '?', '\0'
};
char *ast_escape(char *dest, const char *s, size_t size, const char *to_escape)
if (!dest || !size) {
return dest;
}
if (ast_strlen_zero(s)) {
*dest = '\0';
return dest;
}
if (ast_strlen_zero(to_escape)) {
ast_copy_string(dest, s, size);
for (p = dest; *s && --size; ++s, ++p) {
/* If in the list of characters to escape then escape it */
if (strchr(to_escape, *s)) {
if (!--size) {
/* Not enough room left for the escape sequence. */
break;
}
/*
* See if the character to escape is part of the standard escape
* sequences. If so we'll have to use its mapped counterpart
* otherwise just use the current character.
*/
c = strchr(escape_sequences, *s);
*p++ = '\\';
*p = c ? escape_sequences_map[c - escape_sequences] : *s;
} else {
*p = *s;
}
}
*p = '\0';
char *ast_escape_c(char *dest, const char *s, size_t size)
{
/*
* Note - This is an optimized version of ast_escape. When looking only
* for escape_sequences a couple of checks used in the generic case can
* be left out thus making it slightly more efficient.
*/
char *p;
if (!dest || !size) {
return dest;
}
if (ast_strlen_zero(s)) {
*dest = '\0';
for (p = dest; *s && --size; ++s, ++p) {
/*
* See if the character to escape is part of the standard escape
* sequences. If so use its mapped counterpart.
*/
c = strchr(escape_sequences, *s);