minor changes and eliminate some compiler warnings
[asterisk/asterisk.git] / file.c
diff --git a/file.c b/file.c
index 57b2b7a..d48aaeb 100755 (executable)
--- a/file.c
+++ b/file.c
@@ -1,28 +1,28 @@
 /*
- * Asterisk -- A telephony toolkit for Linux.
+ * Asterisk -- An open source telephony toolkit.
  *
- * Generic File Format Support.
- * 
- * Copyright (C) 1999, Mark Spencer
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
  *
- * Mark Spencer <markster@linux-support.net>
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
  *
  * This program is free software, distributed under the terms of
- * the GNU General Public License
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*
+ *
+ * Generic File Format Support.
+ * 
  */
 
 #include <sys/types.h>
-#include <asterisk/frame.h>
-#include <asterisk/file.h>
-#include <asterisk/cli.h>
-#include <asterisk/logger.h>
-#include <asterisk/channel.h>
-#include <asterisk/sched.h>
-#include <asterisk/options.h>
-#include <asterisk/translate.h>
-#include <asterisk/utils.h>
-#include <asterisk/lock.h>
-#include <asterisk/app.h>
 #include <errno.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include <dirent.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+
 #include "asterisk.h"
-#include "astconf.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/frame.h"
+#include "asterisk/file.h"
+#include "asterisk/cli.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/sched.h"
+#include "asterisk/options.h"
+#include "asterisk/translate.h"
+#include "asterisk/utils.h"
+#include "asterisk/lock.h"
+#include "asterisk/app.h"
+#include "asterisk/pbx.h"
 
 struct ast_format {
        /* Name of format */
@@ -44,9 +59,9 @@ struct ast_format {
        /* Format of frames it uses/provides (one only) */
        int format;
        /* Open an input stream, and start playback */
-       struct ast_filestream * (*open)(int fd);
+       struct ast_filestream * (*open)(FILE * f);
        /* Open an output stream, of a given file descriptor and comment it appropriately if applicable */
-       struct ast_filestream * (*rewrite)(int fd, const char *comment);
+       struct ast_filestream * (*rewrite)(FILE *f, const char *comment);
        /* Write a frame to a channel */
        int (*write)(struct ast_filestream *, struct ast_frame *);
        /* seek num samples into file, whence(think normal seek) */
@@ -88,8 +103,8 @@ AST_MUTEX_DEFINE_STATIC(formatlock);
 static struct ast_format *formats = NULL;
 
 int ast_format_register(const char *name, const char *exts, int format,
-                                               struct ast_filestream * (*open)(int fd),
-                                               struct ast_filestream * (*rewrite)(int fd, const char *comment),
+                                               struct ast_filestream * (*open)(FILE *f),
+                                               struct ast_filestream * (*rewrite)(FILE *f, const char *comment),
                                                int (*write)(struct ast_filestream *, struct ast_frame *),
                                                int (*seek)(struct ast_filestream *, long sample_offset, int whence),
                                                int (*trunc)(struct ast_filestream *),
@@ -118,8 +133,8 @@ int ast_format_register(const char *name, const char *exts, int format,
                ast_mutex_unlock(&formatlock);
                return -1;
        }
-       strncpy(tmp->name, name, sizeof(tmp->name)-1);
-       strncpy(tmp->exts, exts, sizeof(tmp->exts)-1);
+       ast_copy_string(tmp->name, name, sizeof(tmp->name));
+       ast_copy_string(tmp->exts, exts, sizeof(tmp->exts));
        tmp->open = open;
        tmp->rewrite = rewrite;
        tmp->read = read;
@@ -280,21 +295,31 @@ static int copy(const char *infile, const char *outfile)
 
 static char *build_filename(const char *filename, const char *ext)
 {
-       char *fn;
+       char *fn, type[16];
        int fnsize = 0;
-       char tmp[AST_CONFIG_MAX_PATH]="";
-
-       snprintf(tmp, sizeof(tmp), "%s/%s", ast_config_AST_VAR_DIR, "sounds");
-       fnsize = strlen(tmp) + strlen(filename) + strlen(ext) + 10;
-       fn = malloc(fnsize);
-       if (fn) {
-               if (filename[0] == '/') 
-                       snprintf(fn, fnsize, "%s.%s", filename, ext);
-               else
-                       snprintf(fn, fnsize, "%s/%s.%s", tmp, filename, ext);
+
+       if (!strcmp(ext, "wav49")) {
+               ast_copy_string(type, "WAV", sizeof(type));
+       } else {
+               ast_copy_string(type, ext, sizeof(type));
        }
+
+       if (filename[0] == '/') {
+               fnsize = strlen(filename) + strlen(type) + 2;
+               fn = malloc(fnsize);
+               if (fn)
+                       snprintf(fn, fnsize, "%s.%s", filename, type);
+       } else {
+               char tmp[AST_CONFIG_MAX_PATH] = "";
+
+               snprintf(tmp, sizeof(tmp), "%s/%s", ast_config_AST_VAR_DIR, "sounds");
+               fnsize = strlen(tmp) + strlen(filename) + strlen(type) + 3;
+               fn = malloc(fnsize);
+               if (fn)
+                       snprintf(fn, fnsize, "%s/%s.%s", tmp, filename, type);
+       }
+
        return fn;
-       
 }
 
 static int exts_compare(const char *exts, const char *type)
@@ -302,7 +327,7 @@ static int exts_compare(const char *exts, const char *type)
        char *stringp = NULL, *ext;
        char tmp[256];
 
-       strncpy(tmp, exts, sizeof(tmp) - 1);
+       ast_copy_string(tmp, exts, sizeof(tmp));
        stringp = tmp;
        while ((ext = strsep(&stringp, "|"))) {
                if (!strcmp(ext, type)) {
@@ -326,6 +351,7 @@ static int ast_filehelper(const char *filename, const char *filename2, const cha
        struct ast_filestream *s;
        int res=0, ret = 0;
        char *ext=NULL, *exts, *fn, *nfn;
+       FILE *bfile;
        struct ast_channel *chan = (struct ast_channel *)filename2;
        
        /* Start with negative response */
@@ -347,13 +373,10 @@ static int ast_filehelper(const char *filename, const char *filename2, const cha
        while(f) {
                if (!fmt || exts_compare(f->exts, fmt)) {
                        char *stringp=NULL;
-                       exts = strdup(f->exts);
+                       exts = ast_strdupa(f->exts);
                        /* Try each kind of extension */
                        stringp=exts;
                        ext = strsep(&stringp, "|");
-                       if (!strcmp(ext,"wav49")) {
-                               ext = "WAV";
-                       }
                        do {
                                fn = build_filename(filename, ext);
                                if (fn) {
@@ -391,9 +414,10 @@ static int ast_filehelper(const char *filename, const char *filename2, const cha
                                                case ACTION_OPEN:
                                                        if ((ret < 0) && ((chan->writeformat & f->format) ||
                                                                                ((f->format >= AST_FORMAT_MAX_AUDIO) && fmt))) {
-                                                               ret = open(fn, O_RDONLY);
-                                                               if (ret >= 0) {
-                                                                       s = f->open(ret);
+                                                               bfile = fopen(fn, "r");
+                                                               if (bfile) {
+                                                                       ret = 1;
+                                                                       s = f->open(bfile);
                                                                        if (s) {
                                                                                s->lasttimeout = -1;
                                                                                s->fmt = f;
@@ -404,11 +428,14 @@ static int ast_filehelper(const char *filename, const char *filename2, const cha
                                                                                else
                                                                                        chan->vstream = s;
                                                                        } else {
-                                                                               close(ret);
-                                                                               ast_log(LOG_WARNING, "Unable to open fd on %s\n", fn);
+                                                                               fclose(bfile);
+                                                                               ast_log(LOG_WARNING, "Unable to open file on %s\n", fn);
+                                                                               ret = -1;
                                                                        }
-                                                               } else
+                                                               } else{
                                                                        ast_log(LOG_WARNING, "Couldn't open file %s\n", fn);
+                                                                       ret = -1;
+                                                               }
                                                        }
                                                        break;
                                                default:
@@ -422,7 +449,7 @@ static int ast_filehelper(const char *filename, const char *filename2, const cha
                                }
                                ext = strsep(&stringp, "|");
                        } while(ext);
-                       free(exts);
+                       
                }
                f = f->next;
        }
@@ -450,10 +477,9 @@ struct ast_filestream *ast_openstream_full(struct ast_channel *chan, const char
               set it up.
                   
        */
-       int fd = -1;
        int fmts = -1;
        char filename2[256]="";
-       char filename3[256]="";
+       char filename3[256];
        char *endpart;
        int res;
 
@@ -464,7 +490,7 @@ struct ast_filestream *ast_openstream_full(struct ast_channel *chan, const char
                        ast_deactivate_generator(chan);
        }
        if (preflang && !ast_strlen_zero(preflang)) {
-               strncpy(filename3, filename, sizeof(filename3) - 1);
+               ast_copy_string(filename3, filename, sizeof(filename3));
                endpart = strrchr(filename3, '/');
                if (endpart) {
                        *endpart = '\0';
@@ -475,7 +501,7 @@ struct ast_filestream *ast_openstream_full(struct ast_channel *chan, const char
                fmts = ast_fileexists(filename2, NULL, NULL);
        }
        if (fmts < 1) {
-               strncpy(filename2, filename, sizeof(filename2)-1);
+               ast_copy_string(filename2, filename, sizeof(filename2));
                fmts = ast_fileexists(filename2, NULL, NULL);
        }
        if (fmts < 1) {
@@ -486,8 +512,8 @@ struct ast_filestream *ast_openstream_full(struct ast_channel *chan, const char
        /* Set the channel to a format we can work with */
        res = ast_set_write_format(chan, fmts);
        
-       fd = ast_filehelper(filename2, (char *)chan, NULL, ACTION_OPEN);
-       if (fd >= 0)
+       res = ast_filehelper(filename2, (char *)chan, NULL, ACTION_OPEN);
+       if (res >= 0)
                return chan->stream;
        return NULL;
 }
@@ -516,13 +542,13 @@ struct ast_filestream *ast_openvstream(struct ast_channel *chan, const char *fil
                snprintf(filename2, sizeof(filename2), "%s/%s", preflang, filename);
                fmts = ast_fileexists(filename2, fmt, NULL);
                if (fmts < 1) {
-                       strncpy(lang2, preflang, sizeof(lang2)-1);
+                       ast_copy_string(lang2, preflang, sizeof(lang2));
                        snprintf(filename2, sizeof(filename2), "%s/%s", lang2, filename);
                        fmts = ast_fileexists(filename2, fmt, NULL);
                }
        }
        if (fmts < 1) {
-               strncpy(filename2, filename, sizeof(filename2)-1);
+               ast_copy_string(filename2, filename, sizeof(filename2));
                fmts = ast_fileexists(filename2, fmt, NULL);
        }
        if (fmts < 1) {
@@ -715,29 +741,37 @@ int ast_fileexists(const char *filename, const char *fmt, const char *preflang)
        int res = -1;
        if (preflang && !ast_strlen_zero(preflang)) {
                /* Insert the language between the last two parts of the path */
-               strncpy(tmp, filename, sizeof(tmp) - 1);
+               ast_copy_string(tmp, filename, sizeof(tmp));
                c = strrchr(tmp, '/');
                if (c) {
                        *c = '\0';
                        postfix = c+1;
                        prefix = tmp;
+                       snprintf(filename2, sizeof(filename2), "%s/%s/%s", prefix, preflang, postfix);
                } else {
                        postfix = tmp;
                        prefix="";
+                       snprintf(filename2, sizeof(filename2), "%s/%s", preflang, postfix);
                }
-               snprintf(filename2, sizeof(filename2), "%s/%s/%s", prefix, preflang, postfix);
                res = ast_filehelper(filename2, NULL, fmt, ACTION_EXISTS);
                if (res < 1) {
                        char *stringp=NULL;
-                       strncpy(lang2, preflang, sizeof(lang2)-1);
+                       ast_copy_string(lang2, preflang, sizeof(lang2));
                        stringp=lang2;
                        strsep(&stringp, "_");
+                       /* If language is a specific locality of a language (like es_MX), strip the locality and try again */
                        if (strcmp(lang2, preflang)) {
-                               snprintf(filename2, sizeof(filename2), "%s/%s/%s", prefix, lang2, postfix);
+                               if (ast_strlen_zero(prefix)) {
+                                       snprintf(filename2, sizeof(filename2), "%s/%s", lang2, postfix);
+                               } else {
+                                       snprintf(filename2, sizeof(filename2), "%s/%s/%s", prefix, lang2, postfix);
+                               }
                                res = ast_filehelper(filename2, NULL, fmt, ACTION_EXISTS);
                        }
                }
        }
+
+       /* Fallback to no language (usually winds up being American English) */
        if (res < 1) {
                res = ast_filehelper(filename, NULL, fmt, ACTION_EXISTS);
        }
@@ -789,145 +823,164 @@ int ast_streamfile(struct ast_channel *chan, const char *filename, const char *p
 
 struct ast_filestream *ast_readfile(const char *filename, const char *type, const char *comment, int flags, int check, mode_t mode)
 {
-       int fd,myflags = 0;
+       FILE *bfile;
        struct ast_format *f;
-       struct ast_filestream *fs=NULL;
+       struct ast_filestream *fs = NULL;
        char *fn;
-       char *ext;
+
        if (ast_mutex_lock(&formatlock)) {
                ast_log(LOG_WARNING, "Unable to lock format list\n");
                return NULL;
        }
-       f = formats;
-       while(f) {
-               if (exts_compare(f->exts, type)) {
-                       char *stringp=NULL;
-                       /* XXX Implement check XXX */
-                       ext = strdup(f->exts);
-                       stringp=ext;
-                       ext = strsep(&stringp, "|");
-                       fn = build_filename(filename, ext);
-                       fd = open(fn, flags | myflags);
-                       if (fd >= 0) {
-                               errno = 0;
-                               if ((fs = f->open(fd))) {
-                                       fs->trans = NULL;
-                                       fs->fmt = f;
-                                       fs->flags = flags;
-                                       fs->mode = mode;
-                                       fs->filename = strdup(filename);
-                                       fs->vfs = NULL;
-                               } else {
-                                       ast_log(LOG_WARNING, "Unable to open %s\n", fn);
-                                       close(fd);
-                                       unlink(fn);
-                               }
-                       } else if (errno != EEXIST)
-                               ast_log(LOG_WARNING, "Unable to open file %s: %s\n", fn, strerror(errno));
-                       free(fn);
-                       free(ext);
-                       break;
-               }
-               f = f->next;
+
+       for (f = formats; f && !fs; f = f->next) {
+               if (!exts_compare(f->exts, type))
+                       continue;
+
+               fn = build_filename(filename, type);
+               bfile = fopen(fn, "r");
+               if (bfile) {
+                       errno = 0;
+
+                       if (!(fs = f->open(bfile))) {
+                               ast_log(LOG_WARNING, "Unable to open %s\n", fn);
+                               fclose(bfile);
+                               free(fn);
+                               continue;
+                       }
+
+                       fs->trans = NULL;
+                       fs->fmt = f;
+                       fs->flags = flags;
+                       fs->mode = mode;
+                       fs->filename = strdup(filename);
+                       fs->vfs = NULL;
+               } else if (errno != EEXIST)
+                       ast_log(LOG_WARNING, "Unable to open file %s: %s\n", fn, strerror(errno));
+               free(fn);
        }
+
        ast_mutex_unlock(&formatlock);
-       if (!f) 
+       if (!fs) 
                ast_log(LOG_WARNING, "No such format '%s'\n", type);
+
        return fs;
 }
 
 struct ast_filestream *ast_writefile(const char *filename, const char *type, const char *comment, int flags, int check, mode_t mode)
 {
-       int fd,myflags = 0;
+       int fd, myflags = 0;
+       /* compiler claims this variable can be used before initialization... */
+       FILE *bfile = NULL;
        struct ast_format *f;
-       struct ast_filestream *fs=NULL;
-       char *fn,*orig_fn=NULL;
-       char *ext;
-       char *buf=NULL;
+       struct ast_filestream *fs = NULL;
+       char *fn, *orig_fn = NULL;
+       char *buf = NULL;
        size_t size = 0;
 
        if (ast_mutex_lock(&formatlock)) {
                ast_log(LOG_WARNING, "Unable to lock format list\n");
                return NULL;
        }
+
        /* set the O_TRUNC flag if and only if there is no O_APPEND specified */
-       if (!(flags & O_APPEND)) 
+       if (flags & O_APPEND) { 
+               /* We really can't use O_APPEND as it will break WAV header updates */
+               flags &= ~O_APPEND;
+       } else {
                myflags = O_TRUNC;
+       }
        
        myflags |= O_WRONLY | O_CREAT;
 
-       f = formats;
-       while(f) {
-               if (exts_compare(f->exts, type)) {
-                       char *stringp=NULL;
-                       /* XXX Implement check XXX */
-                       ext = ast_strdupa(f->exts);
-                       stringp=ext;
-                       ext = strsep(&stringp, "|");
-                       fn = build_filename(filename, ext);
-                       fd = open(fn, flags | myflags, mode);
+       for (f = formats; f && !fs; f = f->next) {
+               if (!exts_compare(f->exts, type))
+                       continue;
 
-                       if (option_cache_record_files && fd >= 0) {
+               fn = build_filename(filename, type);
+               fd = open(fn, flags | myflags, mode);
+               if (fd > -1) {
+                       /* fdopen() the resulting file stream */
+                       bfile = fdopen(fd, ((flags | myflags) & O_RDWR) ? "w+" : "w");
+                       if (!bfile) {
+                               ast_log(LOG_WARNING, "Whoa, fdopen failed: %s!\n", strerror(errno));
                                close(fd);
-                               /*
-                                  We touch orig_fn just as a place-holder so other things (like vmail) see the file is there.
-                                  What we are really doing is writing to record_cache_dir until we are done then we will mv the file into place.
-                               */
-                               orig_fn = ast_strdupa(fn); 
-                               for (size=0;size<strlen(fn);size++) {
-                                       if (fn[size] == '/')
-                                               fn[size] = '_';
-                               }
+                               fd = -1;
+                       }
+               }
+               
+               if (option_cache_record_files && (fd > -1)) {
+                       char *c;
 
-                               size += (strlen(record_cache_dir) + 10);
-                               buf = alloca(size);
-                               memset(buf, 0, size);
-                               snprintf(buf, size, "%s/%s", record_cache_dir, fn);
-                               free(fn);
-                               fn=buf;
-                               fd = open(fn, flags | myflags, mode);
+                       fclose(bfile);
+                       /*
+                         We touch orig_fn just as a place-holder so other things (like vmail) see the file is there.
+                         What we are really doing is writing to record_cache_dir until we are done then we will mv the file into place.
+                       */
+                       orig_fn = ast_strdupa(fn);
+                       for (c = fn; *c; c++)
+                               if (*c == '/')
+                                       *c = '_';
+
+                       size = strlen(fn) + strlen(record_cache_dir) + 2;
+                       buf = alloca(size);
+                       strcpy(buf, record_cache_dir);
+                       strcat(buf, "/");
+                       strcat(buf, fn);
+                       free(fn);
+                       fn = buf;
+                       fd = open(fn, flags | myflags, mode);
+                       if (fd > -1) {
+                               /* fdopen() the resulting file stream */
+                               bfile = fdopen(fd, ((flags | myflags) & O_RDWR) ? "w+" : "w");
+                               if (!bfile) {
+                                       ast_log(LOG_WARNING, "Whoa, fdopen failed: %s!\n", strerror(errno));
+                                       close(fd);
+                                       fd = -1;
+                               }
                        }
-                       if (fd >= 0) {
-                               errno = 0;
-                               if ((fs = f->rewrite(fd, comment))) {
-                                       fs->trans = NULL;
-                                       fs->fmt = f;
-                                       fs->flags = flags;
-                                       fs->mode = mode;
-                                       if (option_cache_record_files) {
-                                               fs->realfilename = build_filename(filename, ext);
-                                               fs->filename = strdup(fn);
-                                       } else {
-                                               fs->realfilename = NULL;
-                                               fs->filename = strdup(filename);
-                                       }
-                                       fs->vfs = NULL;
+               }
+               if (fd > -1) {
+                       errno = 0;
+                       if ((fs = f->rewrite(bfile, comment))) {
+                               fs->trans = NULL;
+                               fs->fmt = f;
+                               fs->flags = flags;
+                               fs->mode = mode;
+                               if (orig_fn) {
+                                       fs->realfilename = strdup(orig_fn);
+                                       fs->filename = strdup(fn);
                                } else {
-                                       ast_log(LOG_WARNING, "Unable to rewrite %s\n", fn);
-                                       close(fd);
-                                       unlink(fn);
-                                       if (orig_fn)
-                                               unlink(orig_fn);
+                                       fs->realfilename = NULL;
+                                       fs->filename = strdup(filename);
                                }
-                       } else if (errno != EEXIST) {
-                               ast_log(LOG_WARNING, "Unable to open file %s: %s\n", fn, strerror(errno));
-                               if (orig_fn)
+                               fs->vfs = NULL;
+                       } else {
+                               ast_log(LOG_WARNING, "Unable to rewrite %s\n", fn);
+                               close(fd);
+                               if (orig_fn) {
+                                       unlink(fn);
                                        unlink(orig_fn);
+                               }
                        }
-                       if (!buf) /* if buf != NULL then fn is already free and pointing to it */
-                               free(fn);
-
-                       break;
+               } else if (errno != EEXIST) {
+                       ast_log(LOG_WARNING, "Unable to open file %s: %s\n", fn, strerror(errno));
+                       if (orig_fn)
+                               unlink(orig_fn);
                }
-               f = f->next;
+               /* if buf != NULL then fn is already free and pointing to it */
+               if (!buf)
+                       free(fn);
        }
+
        ast_mutex_unlock(&formatlock);
-       if (!f) 
+       if (!fs)
                ast_log(LOG_WARNING, "No such format '%s'\n", type);
+
        return fs;
 }
 
-char ast_waitstream(struct ast_channel *c, const char *breakon)
+int ast_waitstream(struct ast_channel *c, const char *breakon)
 {
        /* XXX Maybe I should just front-end ast_waitstream_full ? XXX */
        int res;
@@ -969,6 +1022,7 @@ char ast_waitstream(struct ast_channel *c, const char *breakon)
                                        return -1;
                                case AST_CONTROL_RINGING:
                                case AST_CONTROL_ANSWER:
+                               case AST_CONTROL_VIDUPDATE:
                                        /* Unimportant */
                                        break;
                                default:
@@ -983,10 +1037,18 @@ char ast_waitstream(struct ast_channel *c, const char *breakon)
        return (c->_softhangup ? -1 : 0);
 }
 
-char ast_waitstream_fr(struct ast_channel *c, const char *breakon, const char *forward, const char *rewind, int ms)
+int ast_waitstream_fr(struct ast_channel *c, const char *breakon, const char *forward, const char *rewind, int ms)
 {
        int res;
        struct ast_frame *fr;
+
+       if (!breakon)
+                       breakon = "";
+       if (!forward)
+                       forward = "";
+       if (!rewind)
+                       rewind = "";
+       
        while(c->stream) {
                res = ast_sched_wait(c->sched);
                if ((res < 0) && !c->timingfunc) {
@@ -1044,13 +1106,16 @@ char ast_waitstream_fr(struct ast_channel *c, const char *breakon, const char *f
        return (c->_softhangup ? -1 : 0);
 }
 
-char ast_waitstream_full(struct ast_channel *c, const char *breakon, int audiofd, int cmdfd)
+int ast_waitstream_full(struct ast_channel *c, const char *breakon, int audiofd, int cmdfd)
 {
        int res;
        int ms;
        int outfd;
        struct ast_frame *fr;
        struct ast_channel *rchan;
+
+       if (!breakon)
+               breakon = "";
        
        while(c->stream) {
                ms = ast_sched_wait(c->sched);
@@ -1062,6 +1127,9 @@ char ast_waitstream_full(struct ast_channel *c, const char *breakon, int audiofd
                        ms = 1000;
                rchan = ast_waitfor_nandfds(&c, 1, &cmdfd, (cmdfd > -1) ? 1 : 0, NULL, &outfd, &ms);
                if (!rchan && (outfd < 0) && (ms)) {
+                       /* Continue */
+                       if (errno == EINTR)
+                               continue;
                        ast_log(LOG_WARNING, "Wait failed (%s)\n", strerror(errno));
                        return -1;
                } else if (outfd > -1) {
@@ -1105,8 +1173,67 @@ char ast_waitstream_full(struct ast_channel *c, const char *breakon, int audiofd
                        ast_frfree(fr);
                }
                ast_sched_runq(c->sched);
-       
-               
+       }
+       return (c->_softhangup ? -1 : 0);
+}
+
+int ast_waitstream_exten(struct ast_channel *c, const char *context)
+{
+       /* Waitstream, with return in the case of a valid 1 digit extension */
+       /* in the current or specified context being pressed */
+       /* XXX Maybe I should just front-end ast_waitstream_full ? XXX */
+       int res;
+       struct ast_frame *fr;
+       char exten[AST_MAX_EXTENSION];
+
+       if (!context) context = c->context;
+       while(c->stream) {
+               res = ast_sched_wait(c->sched);
+               if ((res < 0) && !c->timingfunc) {
+                       ast_stopstream(c);
+                       break;
+               }
+               if (res < 0)
+                       res = 1000;
+               res = ast_waitfor(c, res);
+               if (res < 0) {
+                       ast_log(LOG_WARNING, "Select failed (%s)\n", strerror(errno));
+                       return res;
+               } else if (res > 0) {
+                       fr = ast_read(c);
+                       if (!fr) {
+#if 0
+                               ast_log(LOG_DEBUG, "Got hung up\n");
+#endif
+                               return -1;
+                       }
+                       
+                       switch(fr->frametype) {
+                       case AST_FRAME_DTMF:
+                               res = fr->subclass;
+                               snprintf(exten, sizeof(exten), "%c", res);
+                               if (ast_exists_extension(c, context, exten, 1, c->cid.cid_num)) {
+                                       ast_frfree(fr);
+                                       return res;
+                               }
+                               break;
+                       case AST_FRAME_CONTROL:
+                               switch(fr->subclass) {
+                               case AST_CONTROL_HANGUP:
+                                       ast_frfree(fr);
+                                       return -1;
+                               case AST_CONTROL_RINGING:
+                               case AST_CONTROL_ANSWER:
+                                       /* Unimportant */
+                                       break;
+                               default:
+                                       ast_log(LOG_WARNING, "Unexpected control subclass '%d'\n", fr->subclass);
+                               }
+                       }
+                       /* Ignore */
+                       ast_frfree(fr);
+               }
+               ast_sched_runq(c->sched);
        }
        return (c->_softhangup ? -1 : 0);
 }
@@ -1116,6 +1243,8 @@ static int show_file_formats(int fd, int argc, char *argv[])
 #define FORMAT "%-10s %-10s %-20s\n"
 #define FORMAT2 "%-10s %-10s %-20s\n"
        struct ast_format *f;
+       int count_fmt = 0;
+
        if (argc != 3)
                return RESULT_SHOWUSAGE;
        ast_cli(fd, FORMAT, "Format", "Name", "Extensions");
@@ -1129,9 +1258,14 @@ static int show_file_formats(int fd, int argc, char *argv[])
        while(f) {
                ast_cli(fd, FORMAT2, ast_getformatname(f->format), f->name, f->exts);
                f = f->next;
+               count_fmt++;
        };
        ast_mutex_unlock(&formatlock);
+       ast_cli(fd, "%d file formats registered.\n", count_fmt);
        return RESULT_SUCCESS;
+#undef FORMAT
+#undef FORMAT2
+       
 }
 
 struct ast_cli_entry show_file =