Asterisk.org: Developer Documentation (28 Oct 2009)

Wed Oct 28 15:47:53 2009

## Asterisk developer's documentation

- Main Page
- Related Pages
- Modules
- Data Structures
- Files
- Directories

# file.c

Go to the documentation of this file.

    00001 /*
    00002  * Asterisk -- An open source telephony toolkit.
    00003  *
    00004  * Copyright (C) 1999 - 2005, Digium, Inc.
    00005  *
    00006  * Mark Spencer <markster@digium.com>
    00007  *
    00008  * See http://www.asterisk.org for more information about
    00009  * the Asterisk project. Please do not directly contact
    00010  * any of the maintainers of this project for assistance;
    00011  * the project provides a web site, mailing lists and IRC
    00012  * channels for your use.
    00013  *
    00014  * This program is free software, distributed under the terms of
    00015  * the GNU General Public License Version 2. See the LICENSE file
    00016  * at the top of the source tree.
    00017  */
    00018
    00019 /*! \file
    00020  *
    00021  * \brief Generic File Format Support.
    00022  *
    00023  */
    00024
    00025 #include <sys/types.h>
    00026 #include <errno.h>
    00027 #include <unistd.h>
    00028 #include <stdlib.h>
    00029 #include <string.h>
    00030 #include <stdio.h>
    00031 #include <fcntl.h>
    00032 #include <dirent.h>
    00033 #include <sys/types.h>
    00034 #include <sys/stat.h>
    00035
    00036 #include "asterisk.h"
    00037
    00038 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 71065 $")
    00039
    00040 #include "asterisk/frame.h"
    00041 #include "asterisk/file.h"
    00042 #include "asterisk/cli.h"
    00043 #include "asterisk/logger.h"
    00044 #include "asterisk/channel.h"
    00045 #include "asterisk/sched.h"
    00046 #include "asterisk/options.h"
    00047 #include "asterisk/translate.h"
    00048 #include "asterisk/utils.h"
    00049 #include "asterisk/lock.h"
    00050 #include "asterisk/app.h"
    00051 #include "asterisk/pbx.h"
    00052
    00053 struct ast_format {
    00054    /* Name of format */
    00055    char name[80];
    00056    /* Extensions (separated by | if more than one)
    00057       this format can read.  First is assumed for writing (e.g. .mp3) */
    00058    char exts[80];
    00059    /* Format of frames it uses/provides (one only) */
    00060    int format;
    00061    /* Open an input stream, and start playback */
    00062    struct ast_filestream * (*open)(FILE * f);
    00063    /* Open an output stream, of a given file descriptor and comment it appropriately if applicable */
    00064    struct ast_filestream * (*rewrite)(FILE *f, const char *comment);
    00065    /* Write a frame to a channel */
    00066    int (*write)(struct ast_filestream *, struct ast_frame *);
    00067    /* seek num samples into file, whence(think normal seek) */
    00068    int (*seek)(struct ast_filestream *, long offset, int whence);
    00069    /* trunc file to current position */
    00070    int (*trunc)(struct ast_filestream *fs);
    00071    /* tell current position */
    00072    long (*tell)(struct ast_filestream *fs);
    00073    /* Read the next frame from the filestream (if available) and report when to get next one
    00074       (in samples) */
    00075    struct ast_frame * (*read)(struct ast_filestream *, int *whennext);
    00076    /* Close file, and destroy filestream structure */
    00077    void (*close)(struct ast_filestream *);
    00078    /* Retrieve file comment */
    00079    char * (*getcomment)(struct ast_filestream *);
    00080    /* Link */
    00081    struct ast_format *next;
    00082 };
    00083
    00084 struct ast_filestream {
    00085    /* Everybody reserves a block of AST_RESERVED_POINTERS pointers for us */
    00086    struct ast_format *fmt;
    00087    int flags;
    00088    mode_t mode;
    00089    char *filename;
    00090    char *realfilename;
    00091    /* Video file stream */
    00092    struct ast_filestream *vfs;
    00093    /* Transparently translate from another format -- just once */
    00094    struct ast_trans_pvt *trans;
    00095    struct ast_tranlator_pvt *tr;
    00096    int lastwriteformat;
    00097    int lasttimeout;
    00098    struct ast_channel *owner;
    00099 };
    00100
    00101 AST_MUTEX_DEFINE_STATIC(formatlock);
    00102
    00103 static struct ast_format *formats = NULL;
    00104
    00105 int ast_format_register(const char *name, const char *exts, int format,
    00106                   struct ast_filestream * (*open)(FILE *f),
    00107                   struct ast_filestream * (*rewrite)(FILE *f, const char *comment),
    00108                   int (*write)(struct ast_filestream *, struct ast_frame *),
    00109                   int (*seek)(struct ast_filestream *, long sample_offset, int whence),
    00110                   int (*trunc)(struct ast_filestream *),
    00111                   long (*tell)(struct ast_filestream *),
    00112                   struct ast_frame * (*read)(struct ast_filestream *, int *whennext),
    00113                   void (*close)(struct ast_filestream *),
    00114                   char * (*getcomment)(struct ast_filestream *))
    00115 {
    00116    struct ast_format *tmp;
    00117    if (ast_mutex_lock(&formatlock)) {
    00118       ast_log(LOG_WARNING, "Unable to lock format list\n");
    00119       return -1;
    00120    }
    00121    tmp = formats;
    00122    while(tmp) {
    00123       if (!strcasecmp(name, tmp->name)) {
    00124          ast_mutex_unlock(&formatlock);
    00125          ast_log(LOG_WARNING, "Tried to register '%s' format, already registered\n", name);
    00126          return -1;
    00127       }
    00128       tmp = tmp->next;
    00129    }
    00130    tmp = malloc(sizeof(struct ast_format));
    00131    if (!tmp) {
    00132       ast_log(LOG_WARNING, "Out of memory\n");
    00133       ast_mutex_unlock(&formatlock);
    00134       return -1;
    00135    }
    00136    ast_copy_string(tmp->name, name, sizeof(tmp->name));
    00137    ast_copy_string(tmp->exts, exts, sizeof(tmp->exts));
    00138    tmp->open = open;
    00139    tmp->rewrite = rewrite;
    00140    tmp->read = read;
    00141    tmp->write = write;
    00142    tmp->seek = seek;
    00143    tmp->trunc = trunc;
    00144    tmp->tell = tell;
    00145    tmp->close = close;
    00146    tmp->format = format;
    00147    tmp->getcomment = getcomment;
    00148    tmp->next = formats;
    00149    formats = tmp;
    00150    ast_mutex_unlock(&formatlock);
    00151    if (option_verbose > 1)
    00152       ast_verbose( VERBOSE_PREFIX_2 "Registered file format %s, extension(s) %s\n", name, exts);
    00153    return 0;
    00154 }
    00155
    00156 int ast_format_unregister(const char *name)
    00157 {
    00158    struct ast_format *tmp, *tmpl = NULL;
    00159    if (ast_mutex_lock(&formatlock)) {
    00160       ast_log(LOG_WARNING, "Unable to lock format list\n");
    00161       return -1;
    00162    }
    00163    tmp = formats;
    00164    while(tmp) {
    00165       if (!strcasecmp(name, tmp->name)) {
    00166          if (tmpl)
    00167             tmpl->next = tmp->next;
    00168          else
    00169             formats = tmp->next;
    00170          free(tmp);
    00171          ast_mutex_unlock(&formatlock);
    00172          if (option_verbose > 1)
    00173             ast_verbose( VERBOSE_PREFIX_2 "Unregistered format %s\n", name);
    00174          return 0;
    00175       }
    00176       tmpl = tmp;
    00177       tmp = tmp->next;
    00178    }
    00179    ast_mutex_unlock(&formatlock);
    00180    ast_log(LOG_WARNING, "Tried to unregister format %s, already unregistered\n", name);
    00181    return -1;
    00182 }
    00183
    00184 int ast_stopstream(struct ast_channel *tmp)
    00185 {
    00186    /* Stop a running stream if there is one */
    00187    if (tmp->vstream) {
    00188       ast_closestream(tmp->vstream);
    00189       tmp->vstream = NULL;
    00190    }
    00191    if (tmp->stream) {
    00192       ast_closestream(tmp->stream);
    00193       tmp->stream = NULL;
    00194       if (tmp->oldwriteformat && ast_set_write_format(tmp, tmp->oldwriteformat))
    00195          ast_log(LOG_WARNING, "Unable to restore format back to %d\n", tmp->oldwriteformat);
    00196    }
    00197    return 0;
    00198 }
    00199
    00200 int ast_writestream(struct ast_filestream *fs, struct ast_frame *f)
    00201 {
    00202    struct ast_frame *trf;
    00203    int res = -1;
    00204    int alt=0;
    00205    if (f->frametype == AST_FRAME_VIDEO) {
    00206       if (fs->fmt->format < AST_FORMAT_MAX_AUDIO) {
    00207          /* This is the audio portion.  Call the video one... */
    00208          if (!fs->vfs && fs->filename) {
    00209             /* XXX Support other video formats XXX */
    00210             const char *type = "h263";
    00211             fs->vfs = ast_writefile(fs->filename, type, NULL, fs->flags, 0, fs->mode);
    00212             ast_log(LOG_DEBUG, "Opened video output file\n");
    00213          }
    00214          if (fs->vfs)
    00215             return ast_writestream(fs->vfs, f);
    00216          /* Ignore */
    00217          return 0;
    00218       } else {
    00219          /* Might / might not have mark set */
    00220          alt = 1;
    00221       }
    00222    } else if (f->frametype != AST_FRAME_VOICE) {
    00223       ast_log(LOG_WARNING, "Tried to write non-voice frame\n");
    00224       return -1;
    00225    }
    00226    if (((fs->fmt->format | alt) & f->subclass) == f->subclass) {
    00227       res =  fs->fmt->write(fs, f);
    00228       if (res < 0)
    00229          ast_log(LOG_WARNING, "Natural write failed\n");
    00230       if (res > 0)
    00231          ast_log(LOG_WARNING, "Huh??\n");
    00232       return res;
    00233    } else {
    00234       /* XXX If they try to send us a type of frame that isn't the normal frame, and isn't
    00235              the one we've setup a translator for, we do the "wrong thing" XXX */
    00236       if (fs->trans && (f->subclass != fs->lastwriteformat)) {
    00237          ast_translator_free_path(fs->trans);
    00238          fs->trans = NULL;
    00239       }
    00240       if (!fs->trans)
    00241          fs->trans = ast_translator_build_path(fs->fmt->format, f->subclass);
    00242       if (!fs->trans)
    00243          ast_log(LOG_WARNING, "Unable to translate to format %s, source format %s\n", fs->fmt->name, ast_getformatname(f->subclass));
    00244       else {
    00245          fs->lastwriteformat = f->subclass;
    00246          res = 0;
    00247          /* Get the translated frame but don't consume the original in case they're using it on another stream */
    00248          trf = ast_translate(fs->trans, f, 0);
    00249          if (trf) {
    00250             res = fs->fmt->write(fs, trf);
    00251             if (res)
    00252                ast_log(LOG_WARNING, "Translated frame write failed\n");
    00253          } else
    00254             res = 0;
    00255       }
    00256       return res;
    00257    }
    00258 }
    00259
    00260 static int copy(const char *infile, const char *outfile)
    00261 {
    00262    int ifd;
    00263    int ofd;
    00264    int res;
    00265    int len;
    00266    char buf[4096];
    00267
    00268    if ((ifd = open(infile, O_RDONLY)) < 0) {
    00269       ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
    00270       return -1;
    00271    }
    00272    if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
    00273       ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
    00274       close(ifd);
    00275       return -1;
    00276    }
    00277    do {
    00278       len = read(ifd, buf, sizeof(buf));
    00279       if (len < 0) {
    00280          ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
    00281          close(ifd);
    00282          close(ofd);
    00283          unlink(outfile);
    00284       }
    00285       if (len) {
    00286          res = write(ofd, buf, len);
    00287          if (res != len) {
    00288             ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
    00289             close(ifd);
    00290             close(ofd);
    00291             unlink(outfile);
    00292          }
    00293       }
    00294    } while(len);
    00295    close(ifd);
    00296    close(ofd);
    00297    return 0;
    00298 }
    00299
    00300 static char *build_filename(const char *filename, const char *ext)
    00301 {
    00302    char *fn, type[16];
    00303    int fnsize = 0;
    00304
    00305    if (!strcmp(ext, "wav49")) {
    00306       ast_copy_string(type, "WAV", sizeof(type));
    00307    } else {
    00308       ast_copy_string(type, ext, sizeof(type));
    00309    }
    00310
    00311    if (filename[0] == '/') {
    00312       fnsize = strlen(filename) + strlen(type) + 2;
    00313       fn = malloc(fnsize);
    00314       if (fn)
    00315          snprintf(fn, fnsize, "%s.%s", filename, type);
    00316    } else {
    00317       char tmp[AST_CONFIG_MAX_PATH] = "";
    00318
    00319       snprintf(tmp, sizeof(tmp), "%s/%s", ast_config_AST_VAR_DIR, "sounds");
    00320       fnsize = strlen(tmp) + strlen(filename) + strlen(type) + 3;
    00321       fn = malloc(fnsize);
    00322       if (fn)
    00323          snprintf(fn, fnsize, "%s/%s.%s", tmp, filename, type);
    00324    }
    00325
    00326    return fn;
    00327 }
    00328
    00329 static int exts_compare(const char *exts, const char *type)
    00330 {
    00331    char *stringp = NULL, *ext;
    00332    char tmp[256];
    00333
    00334    ast_copy_string(tmp, exts, sizeof(tmp));
    00335    stringp = tmp;
    00336    while ((ext = strsep(&stringp, "|"))) {
    00337       if (!strcmp(ext, type)) {
    00338          return 1;
    00339       }
    00340    }
    00341
    00342    return 0;
    00343 }
    00344
    00345 #define ACTION_EXISTS 1
    00346 #define ACTION_DELETE 2
    00347 #define ACTION_RENAME 3
    00348 #define ACTION_OPEN   4
    00349 #define ACTION_COPY   5
    00350
    00351 static int ast_filehelper(const char *filename, const char *filename2, const char *fmt, int action)
    00352 {
    00353    struct stat st;
    00354    struct ast_format *f;
    00355    struct ast_filestream *s;
    00356    int res=0, ret = 0;
    00357    char *ext=NULL, *exts, *fn, *nfn;
    00358    FILE *bfile;
    00359    struct ast_channel *chan = (struct ast_channel *)filename2;
    00360
    00361    /* Start with negative response */
    00362    if (action == ACTION_EXISTS)
    00363       res = 0;
    00364    else
    00365       res = -1;
    00366    if (action == ACTION_OPEN)
    00367       ret = -1;
    00368    /* Check for a specific format */
    00369    if (ast_mutex_lock(&formatlock)) {
    00370       ast_log(LOG_WARNING, "Unable to lock format list\n");
    00371       if (action == ACTION_EXISTS)
    00372          return 0;
    00373       else
    00374          return -1;
    00375    }
    00376    f = formats;
    00377    while(f) {
    00378       if (!fmt || exts_compare(f->exts, fmt)) {
    00379          char *stringp=NULL;
    00380          exts = ast_strdupa(f->exts);
    00381          /* Try each kind of extension */
    00382          stringp=exts;
    00383          ext = strsep(&stringp, "|");
    00384          do {
    00385             fn = build_filename(filename, ext);
    00386             if (fn) {
    00387                res = stat(fn, &st);
    00388                if (!res) {
    00389                   switch(action) {
    00390                   case ACTION_EXISTS:
    00391                      ret |= f->format;
    00392                      break;
    00393                   case ACTION_DELETE:
    00394                      res = unlink(fn);
    00395                      if (res)
    00396                         ast_log(LOG_WARNING, "unlink(%s) failed: %s\n", fn, strerror(errno));
    00397                      break;
    00398                   case ACTION_RENAME:
    00399                      nfn = build_filename(filename2, ext);
    00400                      if (nfn) {
    00401                         res = rename(fn, nfn);
    00402                         if (res)
    00403                            ast_log(LOG_WARNING, "rename(%s,%s) failed: %s\n", fn, nfn, strerror(errno));
    00404                         free(nfn);
    00405                      } else
    00406                         ast_log(LOG_WARNING, "Out of memory\n");
    00407                      break;
    00408                   case ACTION_COPY:
    00409                      nfn = build_filename(filename2, ext);
    00410                      if (nfn) {
    00411                         res = copy(fn, nfn);
    00412                         if (res)
    00413                            ast_log(LOG_WARNING, "copy(%s,%s) failed: %s\n", fn, nfn, strerror(errno));
    00414                         free(nfn);
    00415                      } else
    00416                         ast_log(LOG_WARNING, "Out of memory\n");
    00417                      break;
    00418                   case ACTION_OPEN:
    00419                      if ((ret < 0) && ((chan->writeformat & f->format) ||
    00420                               ((f->format >= AST_FORMAT_MAX_AUDIO) && fmt))) {
    00421                         bfile = fopen(fn, "r");
    00422                         if (bfile) {
    00423                            ret = 1;
    00424                            s = f->open(bfile);
    00425                            if (s) {
    00426                               s->lasttimeout = -1;
    00427                               s->fmt = f;
    00428                               s->trans = NULL;
    00429                               s->filename = NULL;
    00430                               if (s->fmt->format < AST_FORMAT_MAX_AUDIO) {
    00431                                  if (chan->stream)
    00432                                     ast_closestream(chan->stream);
    00433                                  chan->stream = s;
    00434                               } else {
    00435                                  if (chan->vstream)
    00436                                     ast_closestream(chan->vstream);
    00437                                  chan->vstream = s;
    00438                               }
    00439                            } else {
    00440                               fclose(bfile);
    00441                               ast_log(LOG_WARNING, "Unable to open file on %s\n", fn);
    00442                               ret = -1;
    00443                            }
    00444                         } else{
    00445                            ast_log(LOG_WARNING, "Couldn't open file %s\n", fn);
    00446                            ret = -1;
    00447                         }
    00448                      }
    00449                      break;
    00450                   default:
    00451                      ast_log(LOG_WARNING, "Unknown helper %d\n", action);
    00452                   }
    00453                   /* Conveniently this logic is the same for all */
    00454                   if (res)
    00455                      break;
    00456                }
    00457                free(fn);
    00458             }
    00459             ext = strsep(&stringp, "|");
    00460          } while(ext);
    00461
    00462       }
    00463       f = f->next;
    00464    }
    00465    ast_mutex_unlock(&formatlock);
    00466    if ((action == ACTION_EXISTS) || (action == ACTION_OPEN))
    00467       res = ret ? ret : -1;
    00468    return res;
    00469 }
    00470 struct ast_filestream *ast_openstream(struct ast_channel *chan, const char *filename, const char *preflang)
    00471 {
    00472    return ast_openstream_full(chan, filename, preflang, 0);
    00473 }
    00474
    00475 struct ast_filestream *ast_openstream_full(struct ast_channel *chan, const char *filename, const char *preflang, int asis)
    00476 {
    00477    /* This is a fairly complex routine.  Essentially we should do
    00478       the following:
    00479
    00480       1) Find which file handlers produce our type of format.
    00481       2) Look for a filename which it can handle.
    00482       3) If we find one, then great.
    00483       4) If not, see what files are there
    00484       5) See what we can actually support
    00485       6) Choose the one with the least costly translator path and
    00486           set it up.
    00487
    00488    */
    00489    int fmts = -1;
    00490    char filename2[256]="";
    00491    char filename3[256];
    00492    char *endpart;
    00493    int res;
    00494
    00495    if (!asis) {
    00496       /* do this first, otherwise we detect the wrong writeformat */
    00497       ast_stopstream(chan);
    00498       if (chan->generator)
    00499          ast_deactivate_generator(chan);
    00500    }
    00501    if (!ast_strlen_zero(preflang)) {
    00502       ast_copy_string(filename3, filename, sizeof(filename3));
    00503       endpart = strrchr(filename3, '/');
    00504       if (endpart) {
    00505          *endpart = '\0';
    00506          endpart++;
    00507          snprintf(filename2, sizeof(filename2), "%s/%s/%s", filename3, preflang, endpart);
    00508       } else
    00509          snprintf(filename2, sizeof(filename2), "%s/%s", preflang, filename);
    00510       fmts = ast_fileexists(filename2, NULL, NULL);
    00511    }
    00512    if (fmts < 1) {
    00513       ast_copy_string(filename2, filename, sizeof(filename2));
    00514       fmts = ast_fileexists(filename2, NULL, NULL);
    00515    }
    00516    if (fmts < 1) {
    00517       ast_log(LOG_WARNING, "File %s does not exist in any format\n", filename);
    00518       return NULL;
    00519    }
    00520    chan->oldwriteformat = chan->writeformat;
    00521    /* Set the channel to a format we can work with */
    00522    res = ast_set_write_format(chan, fmts);
    00523
    00524    res = ast_filehelper(filename2, (char *)chan, NULL, ACTION_OPEN);
    00525    if (res >= 0)
    00526       return chan->stream;
    00527    return NULL;
    00528 }
    00529
    00530 struct ast_filestream *ast_openvstream(struct ast_channel *chan, const char *filename, const char *preflang)
    00531 {
    00532    /* This is a fairly complex routine.  Essentially we should do
    00533       the following:
    00534
    00535       1) Find which file handlers produce our type of format.
    00536       2) Look for a filename which it can handle.
    00537       3) If we find one, then great.
    00538       4) If not, see what files are there
    00539       5) See what we can actually support
    00540       6) Choose the one with the least costly translator path and
    00541           set it up.
    00542
    00543    */
    00544    int fd = -1;
    00545    int fmts = -1;
    00546    char filename2[256];
    00547    char lang2[MAX_LANGUAGE];
    00548    /* XXX H.263 only XXX */
    00549    char *fmt = "h263";
    00550    if (!ast_strlen_zero(preflang)) {
    00551       snprintf(filename2, sizeof(filename2), "%s/%s", preflang, filename);
    00552       fmts = ast_fileexists(filename2, fmt, NULL);
    00553       if (fmts < 1) {
    00554          ast_copy_string(lang2, preflang, sizeof(lang2));
    00555          snprintf(filename2, sizeof(filename2), "%s/%s", lang2, filename);
    00556          fmts = ast_fileexists(filename2, fmt, NULL);
    00557       }
    00558    }
    00559    if (fmts < 1) {
    00560       ast_copy_string(filename2, filename, sizeof(filename2));
    00561       fmts = ast_fileexists(filename2, fmt, NULL);
    00562    }
    00563    if (fmts < 1) {
    00564       return NULL;
    00565    }
    00566    fd = ast_filehelper(filename2, (char *)chan, fmt, ACTION_OPEN);
    00567    if (fd >= 0)
    00568       return chan->vstream;
    00569    ast_log(LOG_WARNING, "File %s has video but couldn't be opened\n", filename);
    00570    return NULL;
    00571 }
    00572
    00573 struct ast_frame *ast_readframe(struct ast_filestream *s)
    00574 {
    00575    struct ast_frame *f = NULL;
    00576    int whennext = 0;
    00577    if (s && s->fmt)
    00578       f = s->fmt->read(s, &whennext);
    00579    return f;
    00580 }
    00581
    00582 static int ast_readaudio_callback(void *data)
    00583 {
    00584    struct ast_filestream *s = data;
    00585    struct ast_frame *fr;
    00586    int whennext = 0;
    00587
    00588    while(!whennext) {
    00589       fr = s->fmt->read(s, &whennext);
    00590       if (fr) {
    00591          if (ast_write(s->owner, fr)) {
    00592             ast_log(LOG_WARNING, "Failed to write frame\n");
    00593             s->owner->streamid = -1;
    00594 #ifdef ZAPTEL_OPTIMIZATIONS
    00595             ast_settimeout(s->owner, 0, NULL, NULL);
    00596 #endif
    00597             return 0;
    00598          }
    00599       } else {
    00600          /* Stream has finished */
    00601          s->owner->streamid = -1;
    00602 #ifdef ZAPTEL_OPTIMIZATIONS
    00603          ast_settimeout(s->owner, 0, NULL, NULL);
    00604 #endif
    00605          return 0;
    00606       }
    00607    }
    00608    if (whennext != s->lasttimeout) {
    00609 #ifdef ZAPTEL_OPTIMIZATIONS
    00610       if (s->owner->timingfd > -1)
    00611          ast_settimeout(s->owner, whennext, ast_readaudio_callback, s);
    00612       else
    00613 #endif
    00614          s->owner->streamid = ast_sched_add(s->owner->sched, whennext/8, ast_readaudio_callback, s);
    00615       s->lasttimeout = whennext;
    00616       return 0;
    00617    }
    00618    return 1;
    00619 }
    00620
    00621 static int ast_readvideo_callback(void *data)
    00622 {
    00623    struct ast_filestream *s = data;
    00624    struct ast_frame *fr;
    00625    int whennext = 0;
    00626
    00627    while(!whennext) {
    00628       fr = s->fmt->read(s, &whennext);
    00629       if (fr) {
    00630          if (ast_write(s->owner, fr)) {
    00631             ast_log(LOG_WARNING, "Failed to write frame\n");
    00632             s->owner->vstreamid = -1;
    00633             return 0;
    00634          }
    00635       } else {
    00636          /* Stream has finished */
    00637          s->owner->vstreamid = -1;
    00638          return 0;
    00639       }
    00640    }
    00641    if (whennext != s->lasttimeout) {
    00642       s->owner->vstreamid = ast_sched_add(s->owner->sched, whennext/8, ast_readvideo_callback, s);
    00643       s->lasttimeout = whennext;
    00644       return 0;
    00645    }
    00646    return 1;
    00647 }
    00648
    00649 int ast_applystream(struct ast_channel *chan, struct ast_filestream *s)
    00650 {
    00651    s->owner = chan;
    00652    return 0;
    00653 }
    00654
    00655 int ast_playstream(struct ast_filestream *s)
    00656 {
    00657    if (s->fmt->format < AST_FORMAT_MAX_AUDIO)
    00658       ast_readaudio_callback(s);
    00659    else
    00660       ast_readvideo_callback(s);
    00661    return 0;
    00662 }
    00663
    00664 int ast_seekstream(struct ast_filestream *fs, long sample_offset, int whence)
    00665 {
    00666    return fs->fmt->seek(fs, sample_offset, whence);
    00667 }
    00668
    00669 int ast_truncstream(struct ast_filestream *fs)
    00670 {
    00671    return fs->fmt->trunc(fs);
    00672 }
    00673
    00674 long ast_tellstream(struct ast_filestream *fs)
    00675 {
    00676    return fs->fmt->tell(fs);
    00677 }
    00678
    00679 int ast_stream_fastforward(struct ast_filestream *fs, long ms)
    00680 {
    00681    /* I think this is right, 8000 samples per second, 1000 ms a second so 8
    00682     * samples per ms  */
    00683    long samples = ms * 8;
    00684    return ast_seekstream(fs, samples, SEEK_CUR);
    00685 }
    00686
    00687 int ast_stream_rewind(struct ast_filestream *fs, long ms)
    00688 {
    00689    long samples = ms * 8;
    00690    samples = samples * -1;
    00691    return ast_seekstream(fs, samples, SEEK_CUR);
    00692 }
    00693
    00694 int ast_closestream(struct ast_filestream *f)
    00695 {
    00696    char *cmd = NULL;
    00697    size_t size = 0;
    00698    /* Stop a running stream if there is one */
    00699    if (f->owner) {
    00700       if (f->fmt->format < AST_FORMAT_MAX_AUDIO) {
    00701          f->owner->stream = NULL;
    00702          if (f->owner->streamid > -1)
    00703             ast_sched_del(f->owner->sched, f->owner->streamid);
    00704          f->owner->streamid = -1;
    00705 #ifdef ZAPTEL_OPTIMIZATIONS
    00706          ast_settimeout(f->owner, 0, NULL, NULL);
    00707 #endif
    00708       } else {
    00709          f->owner->vstream = NULL;
    00710          if (f->owner->vstreamid > -1)
    00711             ast_sched_del(f->owner->sched, f->owner->vstreamid);
    00712          f->owner->vstreamid = -1;
    00713       }
    00714    }
    00715    /* destroy the translator on exit */
    00716    if (f->trans) {
    00717       ast_translator_free_path(f->trans);
    00718       f->trans = NULL;
    00719    }
    00720
    00721    if (f->realfilename && f->filename) {
    00722          size = strlen(f->filename) + strlen(f->realfilename) + 15;
    00723          cmd = alloca(size);
    00724          memset(cmd,0,size);
    00725          snprintf(cmd,size,"/bin/mv -f %s %s",f->filename,f->realfilename);
    00726          ast_safe_system(cmd);
    00727    }
    00728
    00729    if (f->filename) {
    00730       free(f->filename);
    00731       f->filename = NULL;
    00732    }
    00733    if (f->realfilename) {
    00734       free(f->realfilename);
    00735       f->realfilename = NULL;
    00736    }
    00737    f->fmt->close(f);
    00738    return 0;
    00739 }
    00740
    00741
    00742 int ast_fileexists(const char *filename, const char *fmt, const char *preflang)
    00743 {
    00744    char filename2[256];
    00745    char tmp[256];
    00746    char *postfix;
    00747    char *prefix;
    00748    char *c;
    00749    char lang2[MAX_LANGUAGE];
    00750    int res = -1;
    00751    if (!ast_strlen_zero(preflang)) {
    00752       /* Insert the language between the last two parts of the path */
    00753       ast_copy_string(tmp, filename, sizeof(tmp));
    00754       c = strrchr(tmp, '/');
    00755       if (c) {
    00756          *c = '\0';
    00757          postfix = c+1;
    00758          prefix = tmp;
    00759          snprintf(filename2, sizeof(filename2), "%s/%s/%s", prefix, preflang, postfix);
    00760       } else {
    00761          postfix = tmp;
    00762          prefix="";
    00763          snprintf(filename2, sizeof(filename2), "%s/%s", preflang, postfix);
    00764       }
    00765       res = ast_filehelper(filename2, NULL, fmt, ACTION_EXISTS);
    00766       if (res < 1) {
    00767          char *stringp=NULL;
    00768          ast_copy_string(lang2, preflang, sizeof(lang2));
    00769          stringp=lang2;
    00770          strsep(&stringp, "_");
    00771          /* If language is a specific locality of a language (like es_MX), strip the locality and try again */
    00772          if (strcmp(lang2, preflang)) {
    00773             if (ast_strlen_zero(prefix)) {
    00774                snprintf(filename2, sizeof(filename2), "%s/%s", lang2, postfix);
    00775             } else {
    00776                snprintf(filename2, sizeof(filename2), "%s/%s/%s", prefix, lang2, postfix);
    00777             }
    00778             res = ast_filehelper(filename2, NULL, fmt, ACTION_EXISTS);
    00779          }
    00780       }
    00781    }
    00782
    00783    /* Fallback to no language (usually winds up being American English) */
    00784    if (res < 1) {
    00785       res = ast_filehelper(filename, NULL, fmt, ACTION_EXISTS);
    00786    }
    00787    return res;
    00788 }
    00789
    00790 int ast_filedelete(const char *filename, const char *fmt)
    00791 {
    00792    return ast_filehelper(filename, NULL, fmt, ACTION_DELETE);
    00793 }
    00794
    00795 int ast_filerename(const char *filename, const char *filename2, const char *fmt)
    00796 {
    00797    return ast_filehelper(filename, filename2, fmt, ACTION_RENAME);
    00798 }
    00799
    00800 int ast_filecopy(const char *filename, const char *filename2, const char *fmt)
    00801 {
    00802    return ast_filehelper(filename, filename2, fmt, ACTION_COPY);
    00803 }
    00804
    00805 int ast_streamfile(struct ast_channel *chan, const char *filename, const char *preflang)
    00806 {
    00807    struct ast_filestream *fs;
    00808    struct ast_filestream *vfs;
    00809
    00810    fs = ast_openstream(chan, filename, preflang);
    00811    vfs = ast_openvstream(chan, filename, preflang);
    00812    if (vfs)
    00813       ast_log(LOG_DEBUG, "Ooh, found a video stream, too\n");
    00814    if (fs){
    00815       if (ast_applystream(chan, fs))
    00816          return -1;
    00817       if (vfs && ast_applystream(chan, vfs))
    00818          return -1;
    00819       ast_playstream(fs);
    00820       if (vfs)
    00821          ast_playstream(vfs);
    00822 #if 1
    00823       if (option_verbose > 2)
    00824          ast_verbose(VERBOSE_PREFIX_3 "Playing '%s' (language '%s')\n", filename, preflang ? preflang : "default");
    00825 #endif
    00826       return 0;
    00827    }
    00828    ast_log(LOG_WARNING, "Unable to open %s (format %s): %s\n", filename, ast_getformatname(chan->nativeformats), strerror(errno));
    00829    return -1;
    00830 }
    00831
    00832 struct ast_filestream *ast_readfile(const char *filename, const char *type, const char *comment, int flags, int check, mode_t mode)
    00833 {
    00834    FILE *bfile;
    00835    struct ast_format *f;
    00836    struct ast_filestream *fs = NULL;
    00837    char *fn;
    00838
    00839    if (ast_mutex_lock(&formatlock)) {
    00840       ast_log(LOG_WARNING, "Unable to lock format list\n");
    00841       return NULL;
    00842    }
    00843
    00844    for (f = formats; f && !fs; f = f->next) {
    00845       if (!exts_compare(f->exts, type))
    00846          continue;
    00847
    00848       fn = build_filename(filename, type);
    00849       bfile = fopen(fn, "r");
    00850       if (bfile) {
    00851          errno = 0;
    00852
    00853          if (!(fs = f->open(bfile))) {
    00854             ast_log(LOG_WARNING, "Unable to open %s\n", fn);
    00855             fclose(bfile);
    00856             free(fn);
    00857             continue;
    00858          }
    00859
    00860          fs->trans = NULL;
    00861          fs->fmt = f;
    00862          fs->flags = flags;
    00863          fs->mode = mode;
    00864          fs->filename = strdup(filename);
    00865          fs->vfs = NULL;
    00866       } else if (errno != EEXIST)
    00867          ast_log(LOG_WARNING, "Unable to open file %s: %s\n", fn, strerror(errno));
    00868       free(fn);
    00869    }
    00870
    00871    ast_mutex_unlock(&formatlock);
    00872    if (!fs)
    00873       ast_log(LOG_WARNING, "No such format '%s'\n", type);
    00874
    00875    return fs;
    00876 }
    00877
    00878 struct ast_filestream *ast_writefile(const char *filename, const char *type, const char *comment, int flags, int check, mode_t mode)
    00879 {
    00880    int fd, myflags = 0;
    00881    /* compiler claims this variable can be used before initialization... */
    00882    FILE *bfile = NULL;
    00883    struct ast_format *f;
    00884    struct ast_filestream *fs = NULL;
    00885    char *fn, *orig_fn = NULL;
    00886    char *buf = NULL;
    00887    size_t size = 0;
    00888    int format_found = 0;
    00889
    00890    if (ast_mutex_lock(&formatlock)) {
    00891       ast_log(LOG_WARNING, "Unable to lock format list\n");
    00892       return NULL;
    00893    }
    00894
    00895    /* set the O_TRUNC flag if and only if there is no O_APPEND specified */
    00896    if (flags & O_APPEND) {
    00897       /* We really can't use O_APPEND as it will break WAV header updates */
    00898       flags &= ~O_APPEND;
    00899    } else {
    00900       myflags = O_TRUNC;
    00901    }
    00902
    00903    myflags |= O_WRONLY | O_CREAT;
    00904
    00905    for (f = formats; f && !fs; f = f->next) {
    00906       if (!exts_compare(f->exts, type))
    00907          continue;
    00908       else
    00909          format_found = 1;
    00910
    00911       fn = build_filename(filename, type);
    00912       fd = open(fn, flags | myflags, mode);
    00913       if (fd > -1) {
    00914          /* fdopen() the resulting file stream */
    00915          bfile = fdopen(fd, ((flags | myflags) & O_RDWR) ? "w+" : "w");
    00916          if (!bfile) {
    00917             ast_log(LOG_WARNING, "Whoa, fdopen failed: %s!\n", strerror(errno));
    00918             close(fd);
    00919             fd = -1;
    00920          }
    00921       }
    00922
    00923       if (option_cache_record_files && (fd > -1)) {
    00924          char *c;
    00925
    00926          fclose(bfile);
    00927          /*
    00928            We touch orig_fn just as a place-holder so other things (like vmail) see the file is there.
    00929            What we are really doing is writing to record_cache_dir until we are done then we will mv the file into place.
    00930          */
    00931          orig_fn = ast_strdupa(fn);
    00932          for (c = fn; *c; c++)
    00933             if (*c == '/')
    00934                *c = '_';
    00935
    00936          size = strlen(fn) + strlen(record_cache_dir) + 2;
    00937          buf = alloca(size);
    00938          strcpy(buf, record_cache_dir);
    00939          strcat(buf, "/");
    00940          strcat(buf, fn);
    00941          free(fn);
    00942          fn = buf;
    00943          fd = open(fn, flags | myflags, mode);
    00944          if (fd > -1) {
    00945             /* fdopen() the resulting file stream */
    00946             bfile = fdopen(fd, ((flags | myflags) & O_RDWR) ? "w+" : "w");
    00947             if (!bfile) {
    00948                ast_log(LOG_WARNING, "Whoa, fdopen failed: %s!\n", strerror(errno));
    00949                close(fd);
    00950                fd = -1;
    00951             }
    00952          }
    00953       }
    00954       if (fd > -1) {
    00955          errno = 0;
    00956          if ((fs = f->rewrite(bfile, comment))) {
    00957             fs->trans = NULL;
    00958             fs->fmt = f;
    00959             fs->flags = flags;
    00960             fs->mode = mode;
    00961             if (orig_fn) {
    00962                fs->realfilename = strdup(orig_fn);
    00963                fs->filename = strdup(fn);
    00964             } else {
    00965                fs->realfilename = NULL;
    00966                fs->filename = strdup(filename);
    00967             }
    00968             fs->vfs = NULL;
    00969             /* If truncated, we'll be at the beginning; if not truncated, then append */
    00970             f->seek(fs, 0, SEEK_END);
    00971          } else {
    00972             ast_log(LOG_WARNING, "Unable to rewrite %s\n", fn);
    00973             close(fd);
    00974             if (orig_fn) {
    00975                unlink(fn);
    00976                unlink(orig_fn);
    00977             }
    00978          }
    00979       } else if (errno != EEXIST) {
    00980          ast_log(LOG_WARNING, "Unable to open file %s: %s\n", fn, strerror(errno));
    00981          if (orig_fn)
    00982             unlink(orig_fn);
    00983       }
    00984       /* if buf != NULL then fn is already free and pointing to it */
    00985       if (!buf)
    00986          free(fn);
    00987    }
    00988
    00989    ast_mutex_unlock(&formatlock);
    00990
    00991    if (!format_found)
    00992       ast_log(LOG_WARNING, "No such format '%s'\n", type);
    00993
    00994    return fs;
    00995 }
    00996
    00997 int ast_waitstream(struct ast_channel *c, const char *breakon)
    00998 {
    00999    /* XXX Maybe I should just front-end ast_waitstream_full ? XXX */
    01000    int res;
    01001    struct ast_frame *fr;
    01002    if (!breakon) breakon = "";
    01003    while(c->stream) {
    01004       res = ast_sched_wait(c->sched);
    01005       if ((res < 0) && !c->timingfunc) {
    01006          ast_stopstream(c);
    01007          break;
    01008       }
    01009       if (res < 0)
    01010          res = 1000;
    01011       res = ast_waitfor(c, res);
    01012       if (res < 0) {
    01013          ast_log(LOG_WARNING, "Select failed (%s)\n", strerror(errno));
    01014          return res;
    01015       } else if (res > 0) {
    01016          fr = ast_read(c);
    01017          if (!fr) {
    01018 #if 0
    01019             ast_log(LOG_DEBUG, "Got hung up\n");
    01020 #endif
    01021             return -1;
    01022          }
    01023
    01024          switch(fr->frametype) {
    01025          case AST_FRAME_DTMF:
    01026             res = fr->subclass;
    01027             if (strchr(breakon, res)) {
    01028                ast_frfree(fr);
    01029                return res;
    01030             }
    01031             break;
    01032          case AST_FRAME_CONTROL:
    01033             switch(fr->subclass) {
    01034             case AST_CONTROL_HANGUP:
    01035             case AST_CONTROL_BUSY:
    01036             case AST_CONTROL_CONGESTION:
    01037                ast_frfree(fr);
    01038                return -1;
    01039             case AST_CONTROL_RINGING:
    01040             case AST_CONTROL_ANSWER:
    01041             case AST_CONTROL_VIDUPDATE:
    01042                /* Unimportant */
    01043                break;
    01044             default:
    01045                ast_log(LOG_WARNING, "Unexpected control subclass '%d'\n", fr->subclass);
    01046             }
    01047          }
    01048          /* Ignore */
    01049          ast_frfree(fr);
    01050       }
    01051       ast_sched_runq(c->sched);
    01052    }
    01053    return (c->_softhangup ? -1 : 0);
    01054 }
    01055
    01056 int ast_waitstream_fr(struct ast_channel *c, const char *breakon, const char *forward, const char *rewind, int ms)
    01057 {
    01058    int res;
    01059    struct ast_frame *fr;
    01060
    01061    if (!breakon)
    01062          breakon = "";
    01063    if (!forward)
    01064          forward = "";
    01065    if (!rewind)
    01066          rewind = "";
    01067
    01068    while(c->stream) {
    01069       res = ast_sched_wait(c->sched);
    01070       if ((res < 0) && !c->timingfunc) {
    01071          ast_stopstream(c);
    01072          break;
    01073       }
    01074       if (res < 0)
    01075          res = 1000;
    01076       res = ast_waitfor(c, res);
    01077       if (res < 0) {
    01078          ast_log(LOG_WARNING, "Select failed (%s)\n", strerror(errno));
    01079          return res;
    01080       } else
    01081       if (res > 0) {
    01082          fr = ast_read(c);
    01083          if (!fr) {
    01084 #if 0
    01085             ast_log(LOG_DEBUG, "Got hung up\n");
    01086 #endif
    01087             return -1;
    01088          }
    01089
    01090          switch(fr->frametype) {
    01091          case AST_FRAME_DTMF:
    01092             res = fr->subclass;
    01093             if (strchr(forward,res)) {
    01094                ast_stream_fastforward(c->stream, ms);
    01095             } else if (strchr(rewind,res)) {
    01096                ast_stream_rewind(c->stream, ms);
    01097             } else if (strchr(breakon, res)) {
    01098                ast_frfree(fr);
    01099                return res;
    01100             }
    01101             break;
    01102          case AST_FRAME_CONTROL:
    01103             switch(fr->subclass) {
    01104             case AST_CONTROL_HANGUP:
    01105             case AST_CONTROL_BUSY:
    01106             case AST_CONTROL_CONGESTION:
    01107                ast_frfree(fr);
    01108                return -1;
    01109             case AST_CONTROL_RINGING:
    01110             case AST_CONTROL_ANSWER:
    01111                /* Unimportant */
    01112                break;
    01113             default:
    01114                ast_log(LOG_WARNING, "Unexpected control subclass '%d'\n", fr->subclass);
    01115             }
    01116          }
    01117          /* Ignore */
    01118          ast_frfree(fr);
    01119       } else
    01120          ast_sched_runq(c->sched);
    01121
    01122
    01123    }
    01124    return (c->_softhangup ? -1 : 0);
    01125 }
    01126
    01127 int ast_waitstream_full(struct ast_channel *c, const char *breakon, int audiofd, int cmdfd)
    01128 {
    01129    int res;
    01130    int ms;
    01131    int outfd;
    01132    struct ast_frame *fr;
    01133    struct ast_channel *rchan;
    01134
    01135    if (!breakon)
    01136       breakon = "";
    01137
    01138    while(c->stream) {
    01139       ms = ast_sched_wait(c->sched);
    01140       if ((ms < 0) && !c->timingfunc) {
    01141          ast_stopstream(c);
    01142          break;
    01143       }
    01144       if (ms < 0)
    01145          ms = 1000;
    01146       rchan = ast_waitfor_nandfds(&c, 1, &cmdfd, (cmdfd > -1) ? 1 : 0, NULL, &outfd, &ms);
    01147       if (!rchan && (outfd < 0) && (ms)) {
    01148          /* Continue */
    01149          if (errno == EINTR)
    01150             continue;
    01151          ast_log(LOG_WARNING, "Wait failed (%s)\n", strerror(errno));
    01152          return -1;
    01153       } else if (outfd > -1) {
    01154          /* The FD we were watching has something waiting */
    01155          return 1;
    01156       } else if (rchan) {
    01157          fr = ast_read(c);
    01158          if (!fr) {
    01159 #if 0
    01160             ast_log(LOG_DEBUG, "Got hung up\n");
    01161 #endif
    01162             return -1;
    01163          }
    01164
    01165          switch(fr->frametype) {
    01166          case AST_FRAME_DTMF:
    01167             res = fr->subclass;
    01168             if (strchr(breakon, res)) {
    01169                ast_frfree(fr);
    01170                return res;
    01171             }
    01172             break;
    01173          case AST_FRAME_CONTROL:
    01174             switch(fr->subclass) {
    01175             case AST_CONTROL_HANGUP:
    01176             case AST_CONTROL_BUSY:
    01177             case AST_CONTROL_CONGESTION:
    01178                ast_frfree(fr);
    01179                return -1;
    01180             case AST_CONTROL_RINGING:
    01181             case AST_CONTROL_ANSWER:
    01182                /* Unimportant */
    01183                break;
    01184             default:
    01185                ast_log(LOG_WARNING, "Unexpected control subclass '%d'\n", fr->subclass);
    01186             }
    01187          case AST_FRAME_VOICE:
    01188             /* Write audio if appropriate */
    01189             if (audiofd > -1)
    01190                write(audiofd, fr->data, fr->datalen);
    01191          }
    01192          /* Ignore */
    01193          ast_frfree(fr);
    01194       }
    01195       ast_sched_runq(c->sched);
    01196    }
    01197    return (c->_softhangup ? -1 : 0);
    01198 }
    01199
    01200 int ast_waitstream_exten(struct ast_channel *c, const char *context)
    01201 {
    01202    /* Waitstream, with return in the case of a valid 1 digit extension */
    01203    /* in the current or specified context being pressed */
    01204    /* XXX Maybe I should just front-end ast_waitstream_full ? XXX */
    01205    int res;
    01206    struct ast_frame *fr;
    01207    char exten[AST_MAX_EXTENSION];
    01208
    01209    if (!context) context = c->context;
    01210    while(c->stream) {
    01211       res = ast_sched_wait(c->sched);
    01212       if ((res < 0) && !c->timingfunc) {
    01213          ast_stopstream(c);
    01214          break;
    01215       }
    01216       if (res < 0)
    01217          res = 1000;
    01218       res = ast_waitfor(c, res);
    01219       if (res < 0) {
    01220          ast_log(LOG_WARNING, "Select failed (%s)\n", strerror(errno));
    01221          return res;
    01222       } else if (res > 0) {
    01223          fr = ast_read(c);
    01224          if (!fr) {
    01225 #if 0
    01226             ast_log(LOG_DEBUG, "Got hung up\n");
    01227 #endif
    01228             return -1;
    01229          }
    01230
    01231          switch(fr->frametype) {
    01232          case AST_FRAME_DTMF:
    01233             res = fr->subclass;
    01234             snprintf(exten, sizeof(exten), "%c", res);
    01235             if (ast_exists_extension(c, context, exten, 1, c->cid.cid_num)) {
    01236                ast_frfree(fr);
    01237                return res;
    01238             }
    01239             break;
    01240          case AST_FRAME_CONTROL:
    01241             switch(fr->subclass) {
    01242             case AST_CONTROL_HANGUP:
    01243             case AST_CONTROL_BUSY:
    01244             case AST_CONTROL_CONGESTION:
    01245                ast_frfree(fr);
    01246                return -1;
    01247             case AST_CONTROL_RINGING:
    01248             case AST_CONTROL_ANSWER:
    01249                /* Unimportant */
    01250                break;
    01251             default:
    01252                ast_log(LOG_WARNING, "Unexpected control subclass '%d'\n", fr->subclass);
    01253             }
    01254          }
    01255          /* Ignore */
    01256          ast_frfree(fr);
    01257       }
    01258       ast_sched_runq(c->sched);
    01259    }
    01260    return (c->_softhangup ? -1 : 0);
    01261 }
    01262
    01263 static int show_file_formats(int fd, int argc, char *argv[])
    01264 {
    01265 #define FORMAT "%-10s %-10s %-20s\n"
    01266 #define FORMAT2 "%-10s %-10s %-20s\n"
    01267    struct ast_format *f;
    01268    int count_fmt = 0;
    01269
    01270    if (argc != 3)
    01271       return RESULT_SHOWUSAGE;
    01272    ast_cli(fd, FORMAT, "Format", "Name", "Extensions");
    01273
    01274    if (ast_mutex_lock(&formatlock)) {
    01275       ast_log(LOG_WARNING, "Unable to lock format list\n");
    01276       return -1;
    01277    }
    01278
    01279    f = formats;
    01280    while(f) {
    01281       ast_cli(fd, FORMAT2, ast_getformatname(f->format), f->name, f->exts);
    01282       f = f->next;
    01283       count_fmt++;
    01284    };
    01285    ast_mutex_unlock(&formatlock);
    01286    ast_cli(fd, "%d file formats registered.\n", count_fmt);
    01287    return RESULT_SUCCESS;
    01288 #undef FORMAT
    01289 #undef FORMAT2
    01290
    01291 }
    01292
    01293 struct ast_cli_entry show_file =
    01294 {
    01295    { "show", "file", "formats" },
    01296    show_file_formats,
    01297    "Displays file formats",
    01298    "Usage: show file formats\n"
    01299    "       displays currently registered file formats (if any)\n"
    01300 };
    01301
    01302 int ast_file_init(void)
    01303 {
    01304    ast_cli_register(&show_file);
    01305    return 0;
    01306 }