Newer
Older
/*
* CLI entries for upper commands ...
*/
static struct ast_cli_entry show_applications_cli =
{ { "show", "applications", NULL },
handle_show_applications, "Shows registered applications",
show_applications_help, complete_show_applications };
static struct ast_cli_entry show_application_cli =
{ { "show", "application", NULL },
handle_show_application, "Describe a specific application",
show_application_help, complete_show_application };
static struct ast_cli_entry show_dialplan_cli =
{ { "show", "dialplan", NULL },
handle_show_dialplan, "Show dialplan",
show_dialplan_help, complete_show_dialplan_context };
static struct ast_cli_entry show_switches_cli =
{ { "show", "switches", NULL },
handle_show_switches, "Show alternative switches",
show_switches_help, NULL };
int ast_unregister_application(const char *app) {
if (ast_mutex_lock(&applock)) {
ast_log(LOG_ERROR, "Unable to lock application list\n");
return -1;
}
tmp = apps;
while(tmp) {
if (!strcasecmp(app, tmp->name)) {
if (tmpl)
tmpl->next = tmp->next;
else
apps = tmp->next;
if (option_verbose > 1)
ast_verbose( VERBOSE_PREFIX_2 "Unregistered application '%s'\n", tmp->name);
ast_mutex_unlock(&applock);
return 0;
}
tmpl = tmp;
tmp = tmp->next;
}
ast_mutex_unlock(&applock);
struct ast_context *ast_context_create(struct ast_context **extcontexts, const char *name, const char *registrar)
struct ast_context *tmp, **local_contexts;
int length;
length = sizeof(struct ast_context);
length += strlen(name) + 1;
if (!extcontexts) {
local_contexts = &contexts;
ast_mutex_lock(&conlock);
} else
local_contexts = extcontexts;
tmp = *local_contexts;
while(tmp) {
if (!strcasecmp(tmp->name, name)) {
ast_mutex_unlock(&conlock);
ast_log(LOG_WARNING, "Tried to register context '%s', already in use\n", name);
ast_mutex_unlock(&conlock);
tmp = malloc(length);
memset(tmp, 0, length);
ast_mutex_init(&tmp->lock);
strcpy(tmp->name, name);
if (option_debug)
ast_log(LOG_DEBUG, "Registered context '%s'\n", tmp->name);
else if (option_verbose > 2)
ast_verbose( VERBOSE_PREFIX_3 "Registered extension context '%s'\n", tmp->name);
} else
ast_log(LOG_ERROR, "Out of memory\n");
ast_mutex_unlock(&conlock);
void __ast_context_destroy(struct ast_context *con, const char *registrar);
void ast_merge_contexts_and_delete(struct ast_context **extcontexts, const char *registrar) {
struct ast_context *tmp, *lasttmp = NULL;
tmp = *extcontexts;
ast_mutex_lock(&conlock);
Mark Spencer
committed
__ast_context_destroy(NULL,registrar);
while (tmp) {
lasttmp = tmp;
tmp = tmp->next;
}
} else {
while (tmp) {
Mark Spencer
committed
__ast_context_destroy(tmp,tmp->registrar);
}
if (lasttmp) {
lasttmp->next = contexts;
contexts = *extcontexts;
*extcontexts = NULL;
} else
ast_log(LOG_WARNING, "Requested contexts didn't get merged\n");
ast_mutex_unlock(&conlock);
int ast_context_add_include(const char *context, const char *include, const char *registrar)
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
{
struct ast_context *c;
if (ast_lock_contexts()) {
errno = EBUSY;
return -1;
}
/* walk contexts ... */
c = ast_walk_contexts(NULL);
while (c) {
/* ... search for the right one ... */
if (!strcmp(ast_get_context_name(c), context)) {
int ret = ast_context_add_include2(c, include, registrar);
/* ... unlock contexts list and return */
ast_unlock_contexts();
return ret;
}
c = ast_walk_contexts(c);
}
/* we can't find the right context */
ast_unlock_contexts();
#define FIND_NEXT \
do { \
c = info; \
while(*c && (*c != '|')) c++; \
static void get_timerange(struct ast_timing *i, char *times)
{
char *e;
int x;
int s1, s2;
int e1, e2;
/* start disabling all times, fill the fields with 0's, as they may contain garbage */
memset(i->minmask, 0, sizeof(i->minmask));
if (ast_strlen_zero(times) || !strcmp(times, "*")) {
for (x=0;x<24;x++)
i->minmask[x] = (1 << 30) - 1;
return;
}
/* Otherwise expect a range */
e = strchr(times, '-');
if (!e) {
ast_log(LOG_WARNING, "Time range is not valid. Assuming no restrictions based on time.\n");
return;
}
*e = '\0';
e++;
while(*e && !isdigit(*e)) e++;
if (!*e) {
ast_log(LOG_WARNING, "Invalid time range. Assuming no restrictions based on time.\n");
return;
}
if (sscanf(times, "%d:%d", &s1, &s2) != 2) {
ast_log(LOG_WARNING, "%s isn't a time. Assuming no restrictions based on time.\n", times);
return;
}
if (sscanf(e, "%d:%d", &e1, &e2) != 2) {
ast_log(LOG_WARNING, "%s isn't a time. Assuming no restrictions based on time.\n", e);
s1 = s1 * 30 + s2/2;
if ((s1 < 0) || (s1 >= 24*30)) {
ast_log(LOG_WARNING, "%s isn't a valid start time. Assuming no time.\n", times);
if ((e1 < 0) || (e1 >= 24*30)) {
ast_log(LOG_WARNING, "%s isn't a valid end time. Assuming no time.\n", e);
return;
}
/* Go through the time and enable each appropriate bit */
for (x=s1;x != e1;x = (x + 1) % (24 * 30)) {
i->minmask[x/30] |= (1 << (x % 30));
}
/* Do the last one */
i->minmask[x/30] |= (1 << (x % 30));
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
#else
for (cth=0;cth<24;cth++) {
/* Initialize masks to blank */
i->minmask[cth] = 0;
for (ctm=0;ctm<30;ctm++) {
if (
/* First hour with more than one hour */
(((cth == s1) && (ctm >= s2)) &&
((cth < e1)))
/* Only one hour */
|| (((cth == s1) && (ctm >= s2)) &&
((cth == e1) && (ctm <= e2)))
/* In between first and last hours (more than 2 hours) */
|| ((cth > s1) &&
(cth < e1))
/* Last hour with more than one hour */
|| ((cth > s1) &&
((cth == e1) && (ctm <= e2)))
)
i->minmask[cth] |= (1 << (ctm / 2));
}
}
#endif
}
static char *days[] =
{
"sun",
"mon",
"tue",
"wed",
"thu",
"fri",
"sat",
};
static unsigned int get_dow(char *dow)
{
char *c;
/* The following line is coincidence, really! */
int s, e, x;
if (ast_strlen_zero(dow) || !strcmp(dow, "*"))
return (1 << 7) - 1;
/* Get start and ending days */
c = strchr(dow, '-');
if (c) {
*c = '\0';
c++;
/* Find the start */
s = 0;
while((s < 7) && strcasecmp(dow, days[s])) s++;
if (s >= 7) {
ast_log(LOG_WARNING, "Invalid day '%s', assuming none\n", dow);
return 0;
}
if (c) {
e = 0;
if (e >= 7) {
ast_log(LOG_WARNING, "Invalid day '%s', assuming none\n", c);
return 0;
}
} else
e = s;
mask = 0;
for (x=s; x != e; x = (x + 1) % 7) {
mask |= (1 << x);
}
/* One last one */
mask |= (1 << x);
return mask;
}
static unsigned int get_day(char *day)
{
char *c;
/* The following line is coincidence, really! */
int s, e, x;
unsigned int mask;
if (ast_strlen_zero(day) || !strcmp(day, "*")) {
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
}
/* Get start and ending days */
c = strchr(day, '-');
if (c) {
*c = '\0';
c++;
}
/* Find the start */
if (sscanf(day, "%d", &s) != 1) {
ast_log(LOG_WARNING, "Invalid day '%s', assuming none\n", day);
return 0;
}
if ((s < 1) || (s > 31)) {
ast_log(LOG_WARNING, "Invalid day '%s', assuming none\n", day);
return 0;
}
s--;
if (c) {
if (sscanf(c, "%d", &e) != 1) {
ast_log(LOG_WARNING, "Invalid day '%s', assuming none\n", c);
return 0;
}
if ((e < 1) || (e > 31)) {
ast_log(LOG_WARNING, "Invalid day '%s', assuming none\n", c);
return 0;
}
e--;
} else
e = s;
mask = 0;
for (x=s;x!=e;x = (x + 1) % 31) {
mask |= (1 << x);
}
mask |= (1 << x);
return mask;
}
static char *months[] =
{
"jan",
"feb",
"mar",
"apr",
"may",
"jun",
"jul",
"aug",
"sep",
"oct",
"nov",
"dec",
};
static unsigned int get_month(char *mon)
{
char *c;
/* The following line is coincidence, really! */
int s, e, x;
unsigned int mask;
if (ast_strlen_zero(mon) || !strcmp(mon, "*"))
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
return (1 << 12) - 1;
/* Get start and ending days */
c = strchr(mon, '-');
if (c) {
*c = '\0';
c++;
}
/* Find the start */
s = 0;
while((s < 12) && strcasecmp(mon, months[s])) s++;
if (s >= 12) {
ast_log(LOG_WARNING, "Invalid month '%s', assuming none\n", mon);
return 0;
}
if (c) {
e = 0;
while((e < 12) && strcasecmp(mon, months[e])) e++;
if (e >= 12) {
ast_log(LOG_WARNING, "Invalid month '%s', assuming none\n", c);
return 0;
}
} else
e = s;
mask = 0;
mask |= (1 << x);
}
/* One last one */
mask |= (1 << x);
return mask;
}
int ast_build_timing(struct ast_timing *i, char *info_in)
char info_save[256];
char *info;
if (ast_strlen_zero(info_in))
return 0;
/* make a copy just in case we were passed a static string */
strncpy(info_save, info_in, sizeof(info_save));
info = info_save;
/* Assume everything except time */
i->monthmask = (1 << 12) - 1;
i->daymask = (1 << 30) - 1 + (1 << 30);
i->dowmask = (1 << 7) - 1;
FIND_NEXT;
/* Info has the time range, start with that */
get_timerange(i, info);
info = c;
if (!info)
FIND_NEXT;
/* Now check for day of week */
i->dowmask = get_dow(info);
info = c;
if (!info)
FIND_NEXT;
/* Now check for the day of the month */
i->daymask = get_day(info);
info = c;
if (!info)
FIND_NEXT;
/* And finally go for the month */
i->monthmask = get_month(info);
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
return 1;
}
int ast_check_timing(struct ast_timing *i)
{
struct tm tm;
time_t t;
time(&t);
localtime_r(&t,&tm);
/* If it's not the right month, return */
if (!(i->monthmask & (1 << tm.tm_mon))) {
return 0;
}
/* If it's not that time of the month.... */
/* Warning, tm_mday has range 1..31! */
if (!(i->daymask & (1 << (tm.tm_mday-1))))
return 0;
/* If it's not the right day of the week */
if (!(i->dowmask & (1 << tm.tm_wday)))
return 0;
/* Sanity check the hour just to be safe */
if ((tm.tm_hour < 0) || (tm.tm_hour > 23)) {
ast_log(LOG_WARNING, "Insane time...\n");
return 0;
}
/* Now the tough part, we calculate if it fits
in the right time based on min/hour */
if (!(i->minmask[tm.tm_hour] & (1 << (tm.tm_min / 2))))
return 0;
/* If we got this far, then we're good */
return 1;
/*
* errno values
* ENOMEM - out of memory
* EBUSY - can't lock
* EEXIST - already included
* EINVAL - there is no existence of context for inclusion
*/
int ast_context_add_include2(struct ast_context *con, const char *value,
const char *registrar)
struct ast_include *i, *il = NULL; /* include, include_last */
int length;
char *p;
length = sizeof(struct ast_include);
length += 2 * (strlen(value) + 1);
if (!(new_include = malloc(length))) {
ast_log(LOG_ERROR, "Out of memory\n");
errno = ENOMEM;
return -1;
}
/* ... fill in this structure ... */
memset(new_include, 0, length);
p = new_include->stuff;
new_include->name = p;
strcpy(new_include->name, value);
p += strlen(value) + 1;
new_include->rname = p;
strcpy(new_include->rname, value);
c = new_include->rname;
/* Strip off timing info */
while(*c && (*c != '|')) c++;
/* Process if it's there */
if (*c) {
new_include->hastime = ast_build_timing(&(new_include->timing), c+1);
new_include->next = NULL;
new_include->registrar = registrar;
/* ... try to lock this context ... */
if (ast_mutex_lock(&con->lock)) {
free(new_include);
errno = EBUSY;
return -1;
}
/* ... go to last include and check if context is already included too... */
i = con->includes;
while (i) {
if (!strcasecmp(i->name, new_include->name)) {
free(new_include);
ast_mutex_unlock(&con->lock);
errno = EEXIST;
return -1;
}
il = i;
i = i->next;
}
/* ... include new context into context list, unlock, return */
if (il)
il->next = new_include;
else
con->includes = new_include;
if (option_verbose > 2)
ast_verbose(VERBOSE_PREFIX_3 "Including context '%s' in context '%s'\n", new_include->name, ast_get_context_name(con));
ast_mutex_unlock(&con->lock);
return 0;
}
/*
* errno values
* EBUSY - can't lock
int ast_context_add_switch(const char *context, const char *sw, const char *data, const char *registrar)
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
{
struct ast_context *c;
if (ast_lock_contexts()) {
errno = EBUSY;
return -1;
}
/* walk contexts ... */
c = ast_walk_contexts(NULL);
while (c) {
/* ... search for the right one ... */
if (!strcmp(ast_get_context_name(c), context)) {
int ret = ast_context_add_switch2(c, sw, data, registrar);
/* ... unlock contexts list and return */
ast_unlock_contexts();
return ret;
}
c = ast_walk_contexts(c);
}
/* we can't find the right context */
ast_unlock_contexts();
return -1;
}
/*
* errno values
* ENOMEM - out of memory
* EBUSY - can't lock
* EEXIST - already included
* EINVAL - there is no existence of context for inclusion
*/
int ast_context_add_switch2(struct ast_context *con, const char *value,
const char *data, const char *registrar)
struct ast_sw *new_sw;
struct ast_sw *i, *il = NULL; /* sw, sw_last */
int length;
char *p;
length = sizeof(struct ast_sw);
length += strlen(value) + 1;
if (data)
length += strlen(data);
length++;
if (!(new_sw = malloc(length))) {
ast_log(LOG_ERROR, "Out of memory\n");
memset(new_sw, 0, length);
p = new_sw->stuff;
new_sw->name = p;
strcpy(new_sw->name, value);
p += strlen(value) + 1;
new_sw->data = p;
strcpy(new_sw->data, data);
strcpy(new_sw->data, "");
new_sw->next = NULL;
new_sw->registrar = registrar;
/* ... try to lock this context ... */
if (ast_mutex_lock(&con->lock)) {
free(new_sw);
errno = EBUSY;
return -1;
}
/* ... go to last sw and check if context is already swd too... */
i = con->alts;
while (i) {
if (!strcasecmp(i->name, new_sw->name) && !strcasecmp(i->data, new_sw->data)) {
ast_mutex_unlock(&con->lock);
errno = EEXIST;
return -1;
}
il = i;
i = i->next;
}
/* ... sw new context into context list, unlock, return */
if (il)
il->next = new_sw;
else
con->alts = new_sw;
if (option_verbose > 2)
ast_verbose(VERBOSE_PREFIX_3 "Including switch '%s/%s' in context '%s'\n", new_sw->name, new_sw->data, ast_get_context_name(con));
ast_mutex_unlock(&con->lock);
return 0;
}
/*
* EBUSY - can't lock
int ast_context_remove_ignorepat(const char *context, const char *ignorepat, const char *registrar)
{
struct ast_context *c;
if (ast_lock_contexts()) {
errno = EBUSY;
return -1;
}
c = ast_walk_contexts(NULL);
while (c) {
if (!strcmp(ast_get_context_name(c), context)) {
int ret = ast_context_remove_ignorepat2(c, ignorepat, registrar);
ast_unlock_contexts();
return ret;
}
c = ast_walk_contexts(c);
}
ast_unlock_contexts();
int ast_context_remove_ignorepat2(struct ast_context *con, const char *ignorepat, const char *registrar)
{
struct ast_ignorepat *ip, *ipl = NULL;
if (ast_mutex_lock(&con->lock)) {
errno = EBUSY;
return -1;
}
ip = con->ignorepats;
while (ip) {
if (!strcmp(ip->pattern, ignorepat) &&
Mark Spencer
committed
(!registrar || (registrar == ip->registrar))) {
if (ipl) {
ipl->next = ip->next;
free(ip);
} else {
con->ignorepats = ip->next;
free(ip);
}
ast_mutex_unlock(&con->lock);
return 0;
}
ipl = ip; ip = ip->next;
}
ast_mutex_unlock(&con->lock);
errno = EINVAL;
return -1;
}
/*
* EBUSY - can't lock
int ast_context_add_ignorepat(const char *con, const char *value, const char *registrar)
{
struct ast_context *c;
if (ast_lock_contexts()) {
errno = EBUSY;
return -1;
}
c = ast_walk_contexts(NULL);
while (c) {
if (!strcmp(ast_get_context_name(c), con)) {
int ret = ast_context_add_ignorepat2(c, value, registrar);
ast_unlock_contexts();
return ret;
}
c = ast_walk_contexts(c);
}
ast_unlock_contexts();
int ast_context_add_ignorepat2(struct ast_context *con, const char *value, const char *registrar)
{
struct ast_ignorepat *ignorepat, *ignorepatc, *ignorepatl = NULL;
int length;
length = sizeof(struct ast_ignorepat);
length += strlen(value) + 1;
ignorepat = malloc(length);
ast_log(LOG_ERROR, "Out of memory\n");
memset(ignorepat, 0, length);
strcpy(ignorepat->pattern, value);
ignorepat->next = NULL;
ignorepat->registrar = registrar;
ast_mutex_lock(&con->lock);
ignorepatc = con->ignorepats;
while(ignorepatc) {
ignorepatl = ignorepatc;
if (!strcasecmp(ignorepatc->pattern, value)) {
ast_mutex_unlock(&con->lock);
if (ignorepatl)
ignorepatl->next = ignorepat;
ast_mutex_unlock(&con->lock);
int ast_ignore_pattern(const char *context, const char *pattern)
{
struct ast_context *con;
struct ast_ignorepat *pat;
con = ast_context_find(context);
if (con) {
pat = con->ignorepats;
while (pat) {
if (ast_extension_match(pat->pattern, pattern))
return 1;
pat = pat->next;
}
}
return 0;
}
/*
* EBUSY - can't lock
int ast_add_extension(const char *context, int replace, const char *extension, int priority, const char *label, const char *callerid,
const char *application, void *data, void (*datad)(void *), const char *registrar)
{
struct ast_context *c;
if (ast_lock_contexts()) {
errno = EBUSY;
return -1;
}
c = ast_walk_contexts(NULL);
while (c) {
if (!strcmp(context, ast_get_context_name(c))) {
int ret = ast_add_extension2(c, replace, extension, priority, label, callerid,
application, data, datad, registrar);
ast_unlock_contexts();
return ret;
}
c = ast_walk_contexts(c);
}
ast_unlock_contexts();
int ast_explicit_goto(struct ast_channel *chan, const char *context, const char *exten, int priority) {
if (context && !ast_strlen_zero(context))
strncpy(chan->context, context, sizeof(chan->context) - 1);
if (exten && !ast_strlen_zero(exten))
strncpy(chan->exten, exten, sizeof(chan->context) - 1);
if(priority > -1)
chan->priority = priority;
return 0;
}
return -1;
}
int ast_async_goto(struct ast_channel *chan, const char *context, const char *exten, int priority)
Mark Spencer
committed
ast_mutex_lock(&chan->lock);
if (chan->pbx) {
/* This channel is currently in the PBX */
ast_explicit_goto(chan, context, exten, priority - 1);
ast_softhangup_nolock(chan, AST_SOFTHANGUP_ASYNCGOTO);
} else {
/* In order to do it when the channel doesn't really exist within
the PBX, we have to make a new channel, masquerade, and start the PBX
at the new location */
struct ast_channel *tmpchan;
tmpchan = ast_channel_alloc(0);
if (tmpchan) {
snprintf(tmpchan->name, sizeof(tmpchan->name), "AsyncGoto/%s", chan->name);
/* Make formats okay */
tmpchan->readformat = chan->readformat;
tmpchan->writeformat = chan->writeformat;
/* Setup proper location */
ast_explicit_goto(tmpchan,
(context && !ast_strlen_zero(context)) ? context : chan->context,
(exten && !ast_strlen_zero(exten)) ? exten : chan->exten,
/* Masquerade into temp channel */
ast_channel_masquerade(tmpchan, chan);
/* Grab the locks and get going */
ast_mutex_lock(&tmpchan->lock);
Mark Spencer
committed
ast_do_masquerade(tmpchan);
ast_mutex_unlock(&tmpchan->lock);
/* Start the PBX going on our stolen channel */
if (ast_pbx_start(tmpchan)) {
ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmpchan->name);
ast_hangup(tmpchan);
res = -1;
}
} else {
res = -1;
}
}
Mark Spencer
committed
ast_mutex_unlock(&chan->lock);
int ast_async_goto_by_name(const char *channame, const char *context, const char *exten, int priority)
int res = -1;
chan = ast_channel_walk_locked(NULL);
while(chan) {
if (!strcasecmp(channame, chan->name))
break;
ast_mutex_unlock(&chan->lock);
chan = ast_channel_walk_locked(chan);
if (chan) {
res = ast_async_goto(chan, context, exten, priority);
ast_mutex_unlock(&chan->lock);
}
return res;
static int ext_strncpy(char *dst, const char *src, int len)
while(*src && (count < len - 1)) {
switch(*src) {
case ' ':
/* otherwise exten => [a-b],1,... doesn't work */
/* case '-': */
/* Ignore */
break;
default:
*dst = *src;
dst++;
}
src++;
count++;
}
*dst = '\0';
}
/*
* EBUSY - can't lock
* EEXIST - extension with the same priority exist and no replace is set
*
*/
int replace, const char *extension, int priority, const char *label, const char *callerid,
const char *application, void *data, void (*datad)(void *),
const char *registrar)
#define LOG do { if (option_debug) {\
if (tmp->matchcid) { \
ast_log(LOG_DEBUG, "Added extension '%s' priority %d (CID match '%s') to %s\n", tmp->exten, tmp->priority, tmp->cidmatch, con->name); \
} else { \
ast_log(LOG_DEBUG, "Added extension '%s' priority %d to %s\n", tmp->exten, tmp->priority, con->name); \
} \
} else if (option_verbose > 2) { \
if (tmp->matchcid) { \
ast_verbose( VERBOSE_PREFIX_3 "Added extension '%s' priority %d (CID match '%s')to %s\n", tmp->exten, tmp->priority, tmp->cidmatch, con->name); \
} else { \
ast_verbose( VERBOSE_PREFIX_3 "Added extension '%s' priority %d to %s\n", tmp->exten, tmp->priority, con->name); \
} \
} } while(0)
/*
* This is a fairly complex routine. Different extensions are kept
* in order by the extension number. Then, extensions of different
* priorities (same extension) are kept in a list, according to the
* peer pointer.
*/
struct ast_exten *tmp, *e, *el = NULL, *ep = NULL;
int res;
int length;
char *p;
length = sizeof(struct ast_exten);
length += strlen(extension) + 1;
length += strlen(application) + 1;
if (label)
length += strlen(label) + 1;
if (callerid)
length += strlen(callerid) + 1;
else
length ++;
/* Be optimistic: Build the extension structure first */
tmp = malloc(length);
memset(tmp, 0, length);
p = tmp->stuff;