diff options
Diffstat (limited to 'utils')
-rw-r--r-- | utils/Makefile | 132 | ||||
-rw-r--r-- | utils/ael_main.c | 564 | ||||
-rw-r--r-- | utils/astman.1 | 102 | ||||
-rw-r--r-- | utils/astman.c | 755 | ||||
-rw-r--r-- | utils/check_expr.c | 355 | ||||
-rw-r--r-- | utils/expr2.testinput | 92 | ||||
-rw-r--r-- | utils/frame.c | 1092 | ||||
-rw-r--r-- | utils/frame.h | 300 | ||||
-rw-r--r-- | utils/muted.c | 709 | ||||
-rw-r--r-- | utils/smsq.c | 768 | ||||
-rw-r--r-- | utils/stereorize.c | 159 | ||||
-rw-r--r-- | utils/streamplayer.c | 124 |
12 files changed, 5152 insertions, 0 deletions
diff --git a/utils/Makefile b/utils/Makefile new file mode 100644 index 000000000..b52bba7af --- /dev/null +++ b/utils/Makefile @@ -0,0 +1,132 @@ +# +# Asterisk -- A telephony toolkit for Linux. +# +# Various utilities +# +# Copyright (C) 1999-2006, Digium +# +# Mark Spencer <markster@digium.com> +# +# This program is free software, distributed under the terms of +# the GNU General Public License +# + +-include ../menuselect.makeopts + +.PHONY: clean all uninstall + +# to get check_expr, add it to the ALL_UTILS list +ALL_UTILS:=astman smsq stereorize streamplayer aelparse muted +UTILS:=$(ALL_UTILS) + +include $(ASTTOPDIR)/Makefile.rules + +ifeq ($(OSARCH),SunOS) + LIBS+=-lsocket -lnsl + UTILS:=$(filter-out muted,$(UTILS)) +endif + +ifeq ($(OSARCH),OpenBSD) + UTILS:=$(filter-out muted,$(UTILS)) +endif + +ifeq ($(POPT_LIB),) + UTILS:=$(filter-out smsq,$(UTILS)) +endif + +ifeq ($(NEWT_LIB),) + UTILS:=$(filter-out astman,$(UTILS)) +endif + +ifneq ($(filter pbx_ael,$(MENUSELECT_PBX)),) + UTILS:=$(filter-out aelparse,$(UTILS)) +endif + +all: $(UTILS) + +install: + for x in $(UTILS); do \ + if [ "$$x" != "none" ]; then \ + $(INSTALL) -m 755 $$x $(DESTDIR)$(ASTSBINDIR)/$$x; \ + fi; \ + done + +uninstall: + for x in $(ALL_UTILS); do rm -f $$x $(DESTDIR)$(ASTSBINDIR)/$$x; done + +clean: + rm -f *.o $(ALL_UTILS) check_expr *.s *.i + rm -f .*.d + rm -f md5.c strcompat.c ast_expr2.c ast_expr2f.c pbx_ael.c + rm -f aelparse.c aelbison.c + +md5.c: ../main/md5.c + @cp $< $@ + +astman: astman.o md5.o +astman: LIBS+=$(NEWT_LIB) +astman.o: ASTCFLAGS+=-DNO_MALLOC_DEBUG + +stereorize: stereorize.o frame.o +stereorize: LIBS+=-lm + +strcompat.c: ../main/strcompat.c + @cp $< $@ + +../main/ast_expr2.c: + @echo " [BISON] ../main/ast_expr2.y -> $@" + @bison -o $@ -d --name-prefix=ast_yy ../main/ast_expr2.y + +../main/ast_expr2f.c: + @echo " [FLEX] ../main/ast_expr2.fl -> $@" + @flex -o $@ --full ../main/ast_expr2.fl + +ast_expr2.c: ../main/ast_expr2.c + @cp $< $@ + +ast_expr2.o: ASTCFLAGS+=-DSTANDALONE_AEL + +ast_expr2f.c: ../main/ast_expr2f.c + @cp $< $@ + +ast_expr2f.o: ASTCFLAGS+=-DSTANDALONE_AEL -I../main -Wno-unused + +check_expr: check_expr.o ast_expr2.o ast_expr2f.o + +aelbison.c: ../pbx/ael/ael.tab.c + @cp $< $@ +aelbison.o: aelbison.c ../pbx/ael/ael.tab.h ../include/asterisk/ael_structs.h +aelbison.o: ASTCFLAGS+=-I../pbx -DSTANDALONE_AEL + +pbx_ael.c: ../pbx/pbx_ael.c + @cp $< $@ +pbx_ael.o: ASTCFLAGS+=-DSTANDALONE_AEL + +ael_main.o: ael_main.c ../include/asterisk/ael_structs.h +ael_main.o: ASTCFLAGS+=-DSTANDALONE_AEL + +aelparse.c: ../pbx/ael/ael_lex.c + @cp $< $@ +aelparse.o: aelparse.c ../include/asterisk/ael_structs.h ../pbx/ael/ael.tab.h +aelparse.o: ASTCFLAGS+=-I../pbx -DSTANDALONE_AEL -Wno-unused + +aelparse: aelparse.o aelbison.o pbx_ael.o ael_main.o ast_expr2f.o ast_expr2.o strcompat.o + +testexpr2s: ../main/ast_expr2f.c ../main/ast_expr2.c ../main/ast_expr2.h + $(CC) -g -c -I../include -DSTANDALONE_AEL ../main/ast_expr2f.c -o ast_expr2f.o + $(CC) -g -c -I../include -DSTANDALONE_AEL ../main/ast_expr2.c -o ast_expr2.o + $(CC) -g -o testexpr2s ast_expr2f.o ast_expr2.o + rm ast_expr2.o ast_expr2f.o + ./testexpr2s expr2.testinput + +smsq: smsq.o strcompat.o +smsq: LIBS+=$(POPT_LIB) + +streamplayer: streamplayer.o + +muted: muted.o +muted: LIBS+=$(AUDIO_LIBS) + +ifneq ($(wildcard .*.d),) + include .*.d +endif diff --git a/utils/ael_main.c b/utils/ael_main.c new file mode 100644 index 000000000..85a2cc835 --- /dev/null +++ b/utils/ael_main.c @@ -0,0 +1,564 @@ +#include "asterisk.h" + +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <locale.h> +#include <ctype.h> +#include <errno.h> +#include <regex.h> +#include <limits.h> + +#include "asterisk/ast_expr.h" +#include "asterisk/channel.h" +#include "asterisk/module.h" +#include "asterisk/app.h" +#include "asterisk/ael_structs.h" + +/*** MODULEINFO + <depend>pbx_ael</depend> + ***/ + +struct namelist +{ + char name[100]; + char name2[100]; + struct namelist *next; +}; + +struct ast_context +{ + int extension_count; + char name[100]; + char registrar[100]; + struct namelist *includes; + struct namelist *ignorepats; + struct namelist *switches; + struct namelist *eswitches; + + struct namelist *includes_last; + struct namelist *ignorepats_last; + struct namelist *switches_last; + struct namelist *eswitches_last; + + struct ast_context *next; +}; + +#define ADD_LAST(headptr,memptr) if(!headptr){ headptr=(memptr); (headptr##_last)=(memptr);} else {(headptr##_last)->next = (memptr); (headptr##_last) = (memptr);} + +void destroy_namelist(struct namelist *x); +void destroy_namelist(struct namelist *x) +{ + struct namelist *z,*z2; + for(z=x; z; z = z2) + { + z2 = z->next; + z->next = 0; + free(z); + } +} + +struct namelist *create_name(const char *name); +struct namelist *create_name(const char *name) +{ + struct namelist *x = calloc(1, sizeof(*x)); + if (!x) + return NULL; + strncpy(x->name, name, sizeof(x->name) - 1); + return x; +} + +struct ast_context *context_list; +struct ast_context *last_context; +struct namelist *globalvars; +struct namelist *globalvars_last; + +int conts=0, extens=0, priors=0; +char last_exten[18000]; +char ast_config_AST_CONFIG_DIR[PATH_MAX]; +char ast_config_AST_VAR_DIR[PATH_MAX]; + +void ast_cli_register_multiple(void); +int ast_add_extension2(struct ast_context *con, + int replace, const char *extension, int priority, const char *label, const char *callerid, + const char *application, void *data, void (*datad)(void *), + const char *registrar); +void pbx_builtin_setvar(void *chan, void *data); +struct ast_context * ast_context_create(void **extcontexts, const char *name, const char *registrar); +struct ast_context * ast_context_find_or_create(void **extcontexts, const char *name, const char *registrar); +void ast_context_add_ignorepat2(struct ast_context *con, const char *value, const char *registrar); +void ast_context_add_include2(struct ast_context *con, const char *value, const char *registrar); +void ast_context_add_switch2(struct ast_context *con, const char *value, const char *data, int eval, const char *registrar); +void ast_merge_contexts_and_delete(void); +void ast_context_verify_includes(void); +struct ast_context * ast_walk_contexts(void); +void ast_cli_unregister_multiple(void); +void ast_context_destroy(void); +void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...); +char *ast_process_quotes_and_slashes(char *start, char find, char replace_with); +void ast_verbose(const char *fmt, ...); +struct ast_app *pbx_findapp(const char *app); +void filter_leading_space_from_exprs(char *str); +void filter_newlines(char *str); +static int quiet = 0; +static int no_comp = 0; +static int use_curr_dir = 0; +static int dump_extensions = 0; +static int FIRST_TIME = 0; +static FILE *dumpfile; + +struct ast_app *pbx_findapp(const char *app) +{ + return (struct ast_app*)1; /* so as not to trigger an error */ +} + +int ast_loader_register(int (*updater)(void)) +{ + return 1; +} + +int ast_loader_unregister(int (*updater)(void)) +{ + return 1; +} +void ast_module_register(const struct ast_module_info *x) +{ +} + +void ast_module_unregister(const struct ast_module_info *x) +{ +} + + +void ast_cli_register_multiple(void) +{ + if(!no_comp) + printf("Executed ast_cli_register_multiple();\n"); +} + +void pbx_substitute_variables_helper(struct ast_channel *c,const char *cp1,char *cp2,int count); +void pbx_substitute_variables_helper(struct ast_channel *c,const char *cp1,char *cp2,int count) +{ + if (cp1 && *cp1) + strncpy(cp2,cp1,AST_MAX_EXTENSION); /* Right now, this routine is ONLY being called for + a possible var substitution on extension names, + so....! */ + else + *cp2 = 0; +} + +int ast_add_extension2(struct ast_context *con, + int replace, const char *extension, int priority, const char *label, const char *callerid, + const char *application, void *data, void (*datad)(void *), + const char *registrar) +{ + priors++; + con->extension_count++; + if (strcmp(extension,last_exten) != 0) { + extens++; + strcpy(last_exten, extension); + } + if (!label) { + label = "(null)"; + } + if (!callerid) { + callerid = "(null)"; + } + if (!application) { + application = "(null)"; + } + + if(!no_comp) + printf("Executed ast_add_extension2(context=%s, rep=%d, exten=%s, priority=%d, label=%s, callerid=%s, appl=%s, data=%s, FREE, registrar=%s);\n", + con->name, replace, extension, priority, label, callerid, application, (data?(char*)data:"(null)"), registrar); + + if( dump_extensions && dumpfile ) { + struct namelist *n; + char *data2,*data3=0; + int commacount = 0; + + if( FIRST_TIME ) { + FIRST_TIME = 0; + + if( globalvars ) + fprintf(dumpfile,"[globals]\n"); + + for(n=globalvars;n;n=n->next) { + fprintf(dumpfile, "%s\n", n->name); + } + } + + /* print out each extension , possibly the context header also */ + if( con != last_context ) { + fprintf(dumpfile,"\n\n[%s]\n", con->name); + last_context = con; + for(n=con->ignorepats;n;n=n->next) { + fprintf(dumpfile, "ignorepat => %s\n", n->name); + } + for(n=con->includes;n;n=n->next) { + fprintf(dumpfile, "include => %s\n", n->name); + } + for(n=con->switches;n;n=n->next) { + fprintf(dumpfile, "switch => %s/%s\n", n->name, n->name2); + } + for(n=con->eswitches;n;n=n->next) { + fprintf(dumpfile, "eswitch => %s/%s\n", n->name, n->name2); + } + + } + if( data ) { + filter_newlines((char*)data); + filter_leading_space_from_exprs((char*)data); + + /* compiling turns commas into vertical bars in the app data, and also removes the backslash from before escaped commas; + we have to restore the escaping backslash in front of any commas; the vertical bars are OK to leave as-is */ + for (data2 = data; *data2; data2++) { + if (*data2 == ',') + commacount++; /* we need to know how much bigger the string will grow-- one backslash for each comma */ + } + if (commacount) + { + char *d3,*d4; + + data2 = (char*)malloc(strlen(data)+commacount+1); + data3 = data; + d3 = data; + d4 = data2; + while (*d3) { + if (*d3 == ',') { + *d4++ = '\\'; /* put a backslash in front of each comma */ + *d4++ = *d3++; + } else + *d4++ = *d3++; /* or just copy the char */ + } + *d4++ = 0; /* cap off the new string */ + data = data2; + } else + data2 = 0; + + if( strcmp(label,"(null)") != 0 ) + fprintf(dumpfile,"exten => %s,%d(%s),%s(%s)\n", extension, priority, label, application, (char*)data); + else + fprintf(dumpfile,"exten => %s,%d,%s(%s)\n", extension, priority, application, (char*)data); + + if (data2) { + free(data2); + data2 = 0; + data = data3; /* restore data to pre-messedup state */ + } + + } else { + + if( strcmp(label,"(null)") != 0 ) + fprintf(dumpfile,"exten => %s,%d(%s),%s\n", extension, priority, label, application); + else + fprintf(dumpfile,"exten => %s,%d,%s\n", extension, priority, application); + } + } + + /* since add_extension2 is responsible for the malloc'd data stuff */ + if( data ) + free(data); + return 0; +} + +void pbx_builtin_setvar(void *chan, void *data) +{ + struct namelist *x = create_name(data); + if(!no_comp) + printf("Executed pbx_builtin_setvar(chan, data=%s);\n", (char*)data); + + if( dump_extensions ) { + x = create_name(data); + ADD_LAST(globalvars,x); + } +} + + +struct ast_context * ast_context_create(void **extcontexts, const char *name, const char *registrar) +{ + struct ast_context *x = calloc(1, sizeof(*x)); + if (!x) + return NULL; + x->next = context_list; + context_list = x; + if (!no_comp) + printf("Executed ast_context_create(conts, name=%s, registrar=%s);\n", name, registrar); + conts++; + strncpy(x->name, name, sizeof(x->name) - 1); + strncpy(x->registrar, registrar, sizeof(x->registrar) - 1); + return x; +} + +struct ast_context * ast_context_find_or_create(void **extcontexts, const char *name, const char *registrar) +{ + struct ast_context *x = calloc(1, sizeof(*x)); + if (!x) + return NULL; + x->next = context_list; + context_list = x; + if (!no_comp) + printf("Executed ast_context_find_or_create(conts, name=%s, registrar=%s);\n", name, registrar); + conts++; + strncpy(x->name, name, sizeof(x->name) - 1); + strncpy(x->registrar, registrar, sizeof(x->registrar) - 1); + return x; +} + +void ast_context_add_ignorepat2(struct ast_context *con, const char *value, const char *registrar) +{ + if(!no_comp) + printf("Executed ast_context_add_ignorepat2(con, value=%s, registrar=%s);\n", value, registrar); + if( dump_extensions ) { + struct namelist *x; + x = create_name(value); + ADD_LAST(con->ignorepats,x); + } +} + +void ast_context_add_include2(struct ast_context *con, const char *value, const char *registrar) +{ + if(!no_comp) + printf("Executed ast_context_add_include2(con, value=%s, registrar=%s);\n", value, registrar); + if( dump_extensions ) { + struct namelist *x; + x = create_name((char*)value); + ADD_LAST(con->includes,x); + } +} + +void ast_context_add_switch2(struct ast_context *con, const char *value, const char *data, int eval, const char *registrar) +{ + if(!no_comp) + printf("Executed ast_context_add_switch2(con, value=%s, data=%s, eval=%d, registrar=%s);\n", value, data, eval, registrar); + if( dump_extensions ) { + struct namelist *x; + x = create_name((char*)value); + strncpy(x->name2,data,100); + if( eval ) { + + ADD_LAST(con->switches,x); + + } else { + + ADD_LAST(con->eswitches,x); + } + } +} + +void ast_merge_contexts_and_delete(void) +{ + if(!no_comp) + printf("Executed ast_merge_contexts_and_delete();\n"); +} + +void ast_context_verify_includes(void) +{ + if(!no_comp) + printf("Executed ast_context_verify_includes();\n"); +} + +struct ast_context * ast_walk_contexts(void) +{ + if(!no_comp) + printf("Executed ast_walk_contexts();\n"); + return 0; +} + +void ast_cli_unregister_multiple(void) +{ + if(!no_comp) + printf("Executed ast_cli_unregister_multiple();\n"); +} + +void ast_context_destroy(void) +{ + if( !no_comp) + printf("Executed ast_context_destroy();\n"); +} + +void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...) +{ + va_list vars; + va_start(vars,fmt); + if( !quiet || level > 2 ) { + printf("LOG: lev:%d file:%s line:%d func: %s ", + level, file, line, function); + vprintf(fmt, vars); + fflush(stdout); + va_end(vars); + } +} + +void ast_verbose(const char *fmt, ...) +{ + va_list vars; + va_start(vars,fmt); + + printf("VERBOSE: "); + vprintf(fmt, vars); + fflush(stdout); + va_end(vars); +} + +char *ast_process_quotes_and_slashes(char *start, char find, char replace_with) +{ + char *dataPut = start; + int inEscape = 0; + int inQuotes = 0; + + for (; *start; start++) { + if (inEscape) { + *dataPut++ = *start; /* Always goes verbatim */ + inEscape = 0; + } else { + if (*start == '\\') { + inEscape = 1; /* Do not copy \ into the data */ + } else if (*start == '\'') { + inQuotes = 1-inQuotes; /* Do not copy ' into the data */ + } else { + /* Replace , with |, unless in quotes */ + *dataPut++ = inQuotes ? *start : ((*start==find) ? replace_with : *start); + } + } + } + if (start != dataPut) + *dataPut = 0; + return dataPut; +} + +void filter_leading_space_from_exprs(char *str) +{ + /* Mainly for aesthetics */ + char *t, *v, *u = str; + + while ( u && *u ) { + + if( *u == '$' && *(u+1) == '[' ) { + t = u+2; + while( *t == '\n' || *t == '\r' || *t == '\t' || *t == ' ' ) { + v = t; + while ( *v ) { + *v = *(v+1); + v++; + } + } + } + + u++; + } +} + +void filter_newlines(char *str) +{ + /* remove all newlines, returns */ + char *t=str; + while( t && *t ) { + if( *t == '\n' || *t == '\r' ) { + *t = ' '; /* just replace newlines and returns with spaces; they act as + token separators, and just blindly removing them could be + harmful. */ + } + t++; + } +} + + +extern struct module_symbols mod_data; +extern int ael_external_load_module(void); + +int main(int argc, char **argv) +{ + int i; + struct namelist *n; + struct ast_context *lp,*lp2; + + for(i=1;i<argc;i++) { + if( argv[i][0] == '-' && argv[i][1] == 'n' ) + no_comp =1; + if( argv[i][0] == '-' && argv[i][1] == 'q' ) { + quiet = 1; + no_comp =1; + } + if( argv[i][0] == '-' && argv[i][1] == 'd' ) + use_curr_dir =1; + if( argv[i][0] == '-' && argv[i][1] == 'w' ) + dump_extensions =1; + } + + if( !quiet ) { + printf("\n(If you find progress and other non-error messages irritating, you can use -q to suppress them)\n"); + if( !no_comp ) + printf("\n(You can use the -n option if you aren't interested in seeing all the instructions generated by the compiler)\n\n"); + if( !use_curr_dir ) + printf("\n(You can use the -d option if you want to use the current working directory as the CONFIG_DIR. I will look in this dir for extensions.ael* and its included files)\n\n"); + if( !dump_extensions ) + printf("\n(You can use the -w option to dump extensions.conf format to extensions.conf.aeldump)\n"); + } + + if( use_curr_dir ) { + strcpy(ast_config_AST_CONFIG_DIR, "."); + } + else { + strcpy(ast_config_AST_CONFIG_DIR, "/etc/asterisk"); + } + strcpy(ast_config_AST_VAR_DIR, "/var/lib/asterisk"); + + if( dump_extensions ) { + dumpfile = fopen("extensions.conf.aeldump","w"); + if( !dumpfile ) { + printf("\n\nSorry, cannot open extensions.conf.aeldump for writing! Correct the situation and try again!\n\n"); + exit(10); + } + + } + + FIRST_TIME = 1; + + ael_external_load_module(); + + ast_log(4, "ael2_parse", __LINE__, "main", "%d contexts, %d extensions, %d priorities\n", conts, extens, priors); + + if( dump_extensions && dumpfile ) { + + for( lp = context_list; lp; lp = lp->next ) { /* print out any contexts that didn't have any + extensions in them */ + if( lp->extension_count == 0 ) { + + fprintf(dumpfile,"\n\n[%s]\n", lp->name); + + for(n=lp->ignorepats;n;n=n->next) { + fprintf(dumpfile, "ignorepat => %s\n", n->name); + } + for(n=lp->includes;n;n=n->next) { + fprintf(dumpfile, "include => %s\n", n->name); + } + for(n=lp->switches;n;n=n->next) { + fprintf(dumpfile, "switch => %s/%s\n", n->name, n->name2); + } + for(n=lp->eswitches;n;n=n->next) { + fprintf(dumpfile, "eswitch => %s/%s\n", n->name, n->name2); + } + } + } + } + + if( dump_extensions && dumpfile ) + fclose(dumpfile); + + for( lp = context_list; lp; lp = lp2 ) { /* free the ast_context structs */ + lp2 = lp->next; + lp->next = 0; + + destroy_namelist(lp->includes); + destroy_namelist(lp->ignorepats); + destroy_namelist(lp->switches); + destroy_namelist(lp->eswitches); + + free(lp); + } + + return 0; +} diff --git a/utils/astman.1 b/utils/astman.1 new file mode 100644 index 000000000..6a36ca4da --- /dev/null +++ b/utils/astman.1 @@ -0,0 +1,102 @@ +.\" $Header$ +.\" +.\" transcript compatibility for postscript use. +.\" +.\" synopsis: .P! <file.ps> +.\" +.de P! +.fl +\!!1 setgray +.fl +\\&.\" +.fl +\!!0 setgray +.fl \" force out current output buffer +\!!save /psv exch def currentpoint translate 0 0 moveto +\!!/showpage{}def +.fl \" prolog +.sy sed \-e 's/^/!/' \\$1\" bring in postscript file +\!!psv restore +. +.de pF +.ie \\*(f1 .ds f1 \\n(.f +.el .ie \\*(f2 .ds f2 \\n(.f +.el .ie \\*(f3 .ds f3 \\n(.f +.el .ie \\*(f4 .ds f4 \\n(.f +.el .tm ? font overflow +.ft \\$1 +.. +.de fP +.ie !\\*(f4 \{\ +. ft \\*(f4 +. ds f4\" +' br \} +.el .ie !\\*(f3 \{\ +. ft \\*(f3 +. ds f3\" +' br \} +.el .ie !\\*(f2 \{\ +. ft \\*(f2 +. ds f2\" +' br \} +.el .ie !\\*(f1 \{\ +. ft \\*(f1 +. ds f1\" +' br \} +.el .tm ? font underflow +.. +.ds f1\" +.ds f2\" +.ds f3\" +.ds f4\" +'\" t +.ta 8n 16n 24n 32n 40n 48n 56n 64n 72n +.TH ASTMAN 1 "Jun 12th, 2005" "astman" "Linux Programmer's Manual" +.SH NAME +.B astman +-- a client to asterisk's manager interface +.SH SYNOPSIS +.PP +.B astman +.I hostname + +.SH DESCRIPTION +.B astman +This program is a full-screen (terminal) client for Asterisk's manager +interface. + +.SH OPTIONS +.B hostname + +The host name or IP address to connect to (TCP port 5038). If astman +fails to connect it will exit immidiately. + +.SH USAGE +If \fBastman\fR has successfully cunnected to the manager port it will +prompt the user for a username and a secret (password) for the manager +interface on the remote Asterisk manager interface. It will then be able +to report existing channels (calls). You will then be able to redirect +calls to or terminate them. + +.SH "SEE ALSO" +asterisk(8) + +http://www.voip-info.org/wiki-Asterisk+astman + +.SH BUGS +The hostname does not default to localhost. + +Impossible to use a port other than 5038. + +The username and password cannot be defined from the command-line. + +I mean, what's the point in a man page if the syntax is so simple? + +.SH "AUTHOR" +This manual page was written by Tzafrir Cohen <tzafrir.cohen@xorcom.com> +Permission is granted to copy, distribute and/or modify this document under +the terms of the GNU General Public License, Version 2 any +later version published by the Free Software Foundation. + +On Debian systems, the complete text of the GNU General Public +License can be found in /usr/share/common-licenses/GPL. diff --git a/utils/astman.c b/utils/astman.c new file mode 100644 index 000000000..ae2444ae7 --- /dev/null +++ b/utils/astman.c @@ -0,0 +1,755 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 1999 - 2005, Digium, Inc. + * + * Mark Spencer <markster@digium.com> + * + * 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 Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/* + * + * ASTerisk MANager + * + */ + +#include "asterisk.h" + +#include <newt.h> +#include <stdio.h> +#include <sys/time.h> +#include <netdb.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <sys/socket.h> +#include <sys/select.h> +#include <fcntl.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> + +#include "asterisk/md5.h" +#include "asterisk/linkedlists.h" + +#undef gethostbyname + +#define MAX_HEADERS 80 +#define MAX_LEN 256 + +/* + * 2005.05.27 - different versions of newt define the type of the buffer + * for the 5th argument to newtEntry() as char ** or const char ** . To + * let the code compile cleanly with -Werror, we cast it to void * through + * _NEWT_CAST. + */ +#define _NEWT_CAST (void *) + +#define DEFAULT_MANAGER_PORT 5038 + +struct message { + unsigned int hdrcount; + char headers[MAX_HEADERS][MAX_LEN]; +}; + +static struct ast_mansession { + struct sockaddr_in sin; + int fd; + char inbuf[MAX_LEN]; + int inlen; +} session; + +struct ast_chan { + char name[80]; + char exten[20]; + char context[20]; + char priority[20]; + char callerid[40]; + char state[10]; + AST_LIST_ENTRY(ast_chan) list; +}; + +static AST_LIST_HEAD_NOLOCK_STATIC(chans, ast_chan); + +/* dummy functions to be compatible with the Asterisk core for md5.c */ +void ast_register_file_version(const char *file, const char *version); +void ast_register_file_version(const char *file, const char *version) +{ +} + +void ast_unregister_file_version(const char *file); +void ast_unregister_file_version(const char *file) +{ +} + +int ast_add_profile(const char *, uint64_t scale); +int ast_add_profile(const char *s, uint64_t scale) +{ + return -1; +} + +int64_t ast_profile(int, int64_t); +int64_t ast_profile(int key, int64_t val) +{ + return 0; +} +int64_t ast_mark(int, int start1_stop0); +int64_t ast_mark(int key, int start1_stop0) +{ + return 0; +} + +/* end of dummy functions */ + +static struct ast_chan *find_chan(char *name) +{ + struct ast_chan *chan; + AST_LIST_TRAVERSE(&chans, chan, list) { + if (!strcmp(name, chan->name)) + return chan; + } + chan = malloc(sizeof(struct ast_chan)); + if (chan) { + memset(chan, 0, sizeof(struct ast_chan)); + strncpy(chan->name, name, sizeof(chan->name) - 1); + AST_LIST_INSERT_TAIL(&chans, chan, list); + } + return chan; +} + +static void del_chan(char *name) +{ + struct ast_chan *chan; + AST_LIST_TRAVERSE_SAFE_BEGIN(&chans, chan, list) { + if (!strcmp(name, chan->name)) { + AST_LIST_REMOVE_CURRENT(&chans, list); + free(chan); + return; + } + } + AST_LIST_TRAVERSE_SAFE_END +} + +static void __attribute__((format(printf, 2, 3))) fdprintf(int fd, char *fmt, ...) +{ + char stuff[4096]; + va_list ap; + int res; + + va_start(ap, fmt); + vsnprintf(stuff, sizeof(stuff), fmt, ap); + va_end(ap); + if ((res = write(fd, stuff, strlen(stuff))) < 0) { + fprintf(stderr, "write() failed: %s\n", strerror(errno)); + } +} + +static char *get_header(struct message *m, char *var) +{ + char cmp[80]; + int x; + snprintf(cmp, sizeof(cmp), "%s: ", var); + for (x=0;x<m->hdrcount;x++) + if (!strncasecmp(cmp, m->headers[x], strlen(cmp))) + return m->headers[x] + strlen(cmp); + return ""; +} + +static int event_newstate(struct ast_mansession *s, struct message *m) +{ + struct ast_chan *chan; + chan = find_chan(get_header(m, "Channel")); + strncpy(chan->state, get_header(m, "State"), sizeof(chan->state) - 1); + return 0; +} + +static int event_newexten(struct ast_mansession *s, struct message *m) +{ + struct ast_chan *chan; + chan = find_chan(get_header(m, "Channel")); + strncpy(chan->exten, get_header(m, "Extension"), sizeof(chan->exten) - 1); + strncpy(chan->context, get_header(m, "Context"), sizeof(chan->context) - 1); + strncpy(chan->priority, get_header(m, "Priority"), sizeof(chan->priority) - 1); + return 0; +} + +static int event_newchannel(struct ast_mansession *s, struct message *m) +{ + struct ast_chan *chan; + chan = find_chan(get_header(m, "Channel")); + strncpy(chan->state, get_header(m, "State"), sizeof(chan->state) - 1); + strncpy(chan->callerid, get_header(m, "Callerid"), sizeof(chan->callerid) - 1); + return 0; +} + +static int event_status(struct ast_mansession *s, struct message *m) +{ + struct ast_chan *chan; + chan = find_chan(get_header(m, "Channel")); + strncpy(chan->state, get_header(m, "State"), sizeof(chan->state) - 1); + strncpy(chan->callerid, get_header(m, "Callerid"), sizeof(chan->callerid) - 1); + strncpy(chan->exten, get_header(m, "Extension"), sizeof(chan->exten) - 1); + strncpy(chan->context, get_header(m, "Context"), sizeof(chan->context) - 1); + strncpy(chan->priority, get_header(m, "Priority"), sizeof(chan->priority) - 1); + return 0; +} + +static int event_hangup(struct ast_mansession *s, struct message *m) +{ + del_chan(get_header(m, "Channel")); + return 0; +} + +static int event_ignore(struct ast_mansession *s, struct message *m) +{ + return 0; +} + +static int event_rename(struct ast_mansession *s, struct message *m) +{ + struct ast_chan *chan; + chan = find_chan(get_header(m, "Oldname")); + strncpy(chan->name, get_header(m, "Newname"), sizeof(chan->name) - 1); + return 0; +} +static struct event { + char *event; + int (*func)(struct ast_mansession *s, struct message *m); +} events[] = { + { "Newstate", event_newstate }, + { "Newchannel", event_newchannel }, + { "Newexten", event_newexten }, + { "Hangup", event_hangup }, + { "Rename", event_rename }, + { "Status", event_status }, + { "Link", event_ignore }, + { "Unlink", event_ignore }, + { "StatusComplete", event_ignore } +}; + +static int process_message(struct ast_mansession *s, struct message *m) +{ + int x; + char event[80] = ""; + strncpy(event, get_header(m, "Event"), sizeof(event) - 1); + if (!strlen(event)) { + fprintf(stderr, "Missing event in request"); + return 0; + } + for (x=0;x<sizeof(events) / sizeof(events[0]);x++) { + if (!strcasecmp(event, events[x].event)) { + if (events[x].func(s, m)) + return -1; + break; + } + } + if (x >= sizeof(events) / sizeof(events[0])) + fprintf(stderr, "Ignoring unknown event '%s'", event); +#if 0 + for (x=0;x<m->hdrcount;x++) { + printf("Header: %s\n", m->headers[x]); + } +#endif + return 0; +} + +static void rebuild_channels(newtComponent c) +{ + void *prev = NULL; + struct ast_chan *chan; + char tmpn[42]; + char tmp[256]; + int x=0; + prev = newtListboxGetCurrent(c); + newtListboxClear(c); + AST_LIST_TRAVERSE(&chans, chan, list) { + snprintf(tmpn, sizeof(tmpn), "%s (%s)", chan->name, chan->callerid); + if (strlen(chan->exten)) + snprintf(tmp, sizeof(tmp), "%-30s %8s -> %s@%s:%s", + tmpn, chan->state, + chan->exten, chan->context, chan->priority); + else + snprintf(tmp, sizeof(tmp), "%-30s %8s", + tmpn, chan->state); + newtListboxAppendEntry(c, tmp, chan); + x++; + } + if (!x) + newtListboxAppendEntry(c, " << No Active Channels >> ", NULL); + newtListboxSetCurrentByKey(c, prev); +} + +static int has_input(struct ast_mansession *s) +{ + int x; + for (x=1;x<s->inlen;x++) + if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r')) + return 1; + return 0; +} + +static int get_input(struct ast_mansession *s, char *output) +{ + /* output must have at least sizeof(s->inbuf) space */ + int res; + int x; + struct timeval tv = {0, 0}; + fd_set fds; + for (x=1;x<s->inlen;x++) { + if ((s->inbuf[x] == '\n') && (s->inbuf[x-1] == '\r')) { + /* Copy output data up to and including \r\n */ + memcpy(output, s->inbuf, x + 1); + /* Add trailing \0 */ + output[x+1] = '\0'; + /* Move remaining data back to the front */ + memmove(s->inbuf, s->inbuf + x + 1, s->inlen - x); + s->inlen -= (x + 1); + return 1; + } + } + if (s->inlen >= sizeof(s->inbuf) - 1) { + fprintf(stderr, "Dumping long line with no return from %s: %s\n", inet_ntoa(s->sin.sin_addr), s->inbuf); + s->inlen = 0; + } + FD_ZERO(&fds); + FD_SET(s->fd, &fds); + res = select(s->fd + 1, &fds, NULL, NULL, &tv); + if (res < 0) { + fprintf(stderr, "Select returned error: %s\n", strerror(errno)); + } else if (res > 0) { + res = read(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen); + if (res < 1) + return -1; + s->inlen += res; + s->inbuf[s->inlen] = '\0'; + } else { + return 2; + } + return 0; +} + +static int input_check(struct ast_mansession *s, struct message **mout) +{ + static struct message m; + int res; + + if (mout) + *mout = NULL; + + for(;;) { + res = get_input(s, m.headers[m.hdrcount]); + if (res == 1) { +#if 0 + fprintf(stderr, "Got header: %s", m.headers[m.hdrcount]); + fgetc(stdin); +#endif + /* Strip trailing \r\n */ + if (strlen(m.headers[m.hdrcount]) < 2) + continue; + m.headers[m.hdrcount][strlen(m.headers[m.hdrcount]) - 2] = '\0'; + if (!strlen(m.headers[m.hdrcount])) { + if (mout && strlen(get_header(&m, "Response"))) { + *mout = &m; + return 0; + } + if (process_message(s, &m)) + break; + memset(&m, 0, sizeof(&m)); + } else if (m.hdrcount < MAX_HEADERS - 1) + m.hdrcount++; + } else if (res < 0) { + return -1; + } else if (res == 2) + return 0; + } + return -1; +} + +static struct message *wait_for_response(int timeout) +{ + struct message *m; + struct timeval tv; + int res; + fd_set fds; + for (;;) { + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + FD_SET(session.fd, &fds); + res = select(session.fd + 1, &fds, NULL, NULL, &tv); + if (res < 1) + break; + if (input_check(&session, &m) < 0) { + return NULL; + } + if (m) + return m; + } + return NULL; +} + +static int __attribute__((format(printf, 2, 3))) manager_action(char *action, char *fmt, ...) +{ + struct ast_mansession *s; + char tmp[4096]; + va_list ap; + int res; + + s = &session; + fdprintf(s->fd, "Action: %s\r\n", action); + va_start(ap, fmt); + vsnprintf(tmp, sizeof(tmp), fmt, ap); + va_end(ap); + if ((res = write(s->fd, tmp, strlen(tmp))) < 0) { + fprintf(stderr, "write() failed: %s\n", strerror(errno)); + } + fdprintf(s->fd, "\r\n"); + return 0; +} + +static int show_message(char *title, char *msg) +{ + newtComponent form; + newtComponent label; + newtComponent ok; + struct newtExitStruct es; + + newtCenteredWindow(60,7, title); + + label = newtLabel(4,1,msg); + ok = newtButton(27, 3, "OK"); + form = newtForm(NULL, NULL, 0); + newtFormAddComponents(form, label, ok, NULL); + newtFormRun(form, &es); + newtPopWindow(); + newtFormDestroy(form); + return 0; +} + +static newtComponent showform; +static int show_doing(char *title, char *tmp) +{ + struct newtExitStruct es; + newtComponent label; + showform = newtForm(NULL, NULL, 0); + newtCenteredWindow(70,4, title); + label = newtLabel(3,1,tmp); + newtFormAddComponents(showform,label, NULL); + newtFormSetTimer(showform, 200); + newtFormRun(showform, &es); + return 0; +} + +static int hide_doing(void) +{ + newtPopWindow(); + newtFormDestroy(showform); + return 0; +} + +static void try_status(void) +{ + struct message *m; + manager_action("Status", "%s", ""); + m = wait_for_response(10000); + if (!m) { + show_message("Status Failed", "Timeout waiting for response"); + } else if (strcasecmp(get_header(m, "Response"), "Success")) { + show_message("Status Failed Failed", get_header(m, "Message")); + } +} + + +static void try_hangup(newtComponent c) +{ + struct ast_chan *chan; + struct message *m; + + chan = newtListboxGetCurrent(c); + if (chan) { + manager_action("Hangup", "Channel: %s\r\n", chan->name); + m = wait_for_response(10000); + if (!m) { + show_message("Hangup Failed", "Timeout waiting for response"); + } else if (strcasecmp(get_header(m, "Response"), "Success")) { + show_message("Hangup Failed", get_header(m, "Message")); + } + } + +} + +static int get_user_input(char *msg, char *buf, int buflen) +{ + newtComponent form; + newtComponent ok; + newtComponent cancel; + newtComponent inpfield; + const char *input; + int res = -1; + struct newtExitStruct es; + + newtCenteredWindow(60,7, msg); + + inpfield = newtEntry(5, 2, "", 50, _NEWT_CAST &input, 0); + ok = newtButton(22, 3, "OK"); + cancel = newtButton(32, 3, "Cancel"); + form = newtForm(NULL, NULL, 0); + newtFormAddComponents(form, inpfield, ok, cancel, NULL); + newtFormRun(form, &es); + strncpy(buf, input, buflen - 1); + if (es.u.co == ok) + res = 0; + else + res = -1; + newtPopWindow(); + newtFormDestroy(form); + return res; +} + +static void try_redirect(newtComponent c) +{ + struct ast_chan *chan; + char dest[256]; + struct message *m; + char channame[256]; + char tmp[80]; + char *context; + + chan = newtListboxGetCurrent(c); + if (chan) { + strncpy(channame, chan->name, sizeof(channame) - 1); + snprintf(tmp, sizeof(tmp), "Enter new extension for %s", channame); + if (get_user_input(tmp, dest, sizeof(dest))) + return; + if ((context = strchr(dest, '@'))) { + *context = '\0'; + context++; + manager_action("Redirect", "Channel: %s\r\nContext: %s\r\nExten: %s\r\nPriority: 1\r\n", chan->name,context,dest); + } else { + manager_action("Redirect", "Channel: %s\r\nExten: %s\r\nPriority: 1\r\n", chan->name, dest); + } + m = wait_for_response(10000); + if (!m) { + show_message("Hangup Failed", "Timeout waiting for response"); + } else if (strcasecmp(get_header(m, "Response"), "Success")) { + show_message("Hangup Failed", get_header(m, "Message")); + } + } + +} + +static int manage_calls(char *host) +{ + newtComponent form; + newtComponent quit; + newtComponent hangup; + newtComponent redirect; + newtComponent channels; + struct newtExitStruct es; + char tmp[80]; + + /* Mark: If there's one thing you learn from this code, it is this... + Never, ever fly Air France. Their customer service is absolutely + the worst. I've never heard the words "That's not my problem" as + many times as I have from their staff -- It should, without doubt + be their corporate motto if it isn't already. Don't bother giving + them business because you're just a pain in their side and they + will be sure to let you know the first time you speak to them. + + If you ever want to make me happy just tell me that you, too, will + never fly Air France again either (in spite of their excellent + cuisine). + + Update by oej: The merger with KLM has transferred this + behaviour to KLM as well. + Don't bother giving them business either... + + Only if you want to travel randomly without luggage, you + might pick either of them. + + */ + snprintf(tmp, sizeof(tmp), "Asterisk Manager at %s", host); + newtCenteredWindow(74, 20, tmp); + form = newtForm(NULL, NULL, 0); + newtFormWatchFd(form, session.fd, NEWT_FD_READ); + newtFormSetTimer(form, 100); + quit = newtButton(62, 16, "Quit"); + redirect = newtButton(35, 16, "Redirect"); + hangup = newtButton(50, 16, "Hangup"); + channels = newtListbox(1,1,14, NEWT_FLAG_SCROLL); + newtFormAddComponents(form, channels, redirect, hangup, quit, NULL); + newtListboxSetWidth(channels, 72); + + show_doing("Getting Status", "Retrieving system status..."); + try_status(); + hide_doing(); + + for(;;) { + newtFormRun(form, &es); + if (has_input(&session) || (es.reason == NEWT_EXIT_FDREADY)) { + if (input_check(&session, NULL)) { + show_message("Disconnected", "Disconnected from remote host"); + break; + } + } else if (es.reason == NEWT_EXIT_COMPONENT) { + if (es.u.co == quit) + break; + if (es.u.co == hangup) { + try_hangup(channels); + } else if (es.u.co == redirect) { + try_redirect(channels); + } + } + rebuild_channels(channels); + } + newtFormDestroy(form); + return 0; +} + +static int login(char *hostname) +{ + newtComponent form; + newtComponent cancel; + newtComponent login; + newtComponent username; + newtComponent password; + newtComponent label; + newtComponent ulabel; + newtComponent plabel; + const char *user; + const char *pass; + struct message *m; + struct newtExitStruct es; + char tmp[55]; + struct hostent *hp; + int res = -1; + + session.fd = socket(AF_INET, SOCK_STREAM, 0); + if (session.fd < 0) { + snprintf(tmp, sizeof(tmp), "socket() failed: %s\n", strerror(errno)); + show_message("Socket failed", tmp); + return -1; + } + + snprintf(tmp, sizeof(tmp), "Looking up %s\n", hostname); + show_doing("Connecting....", tmp); + + + hp = gethostbyname(hostname); + if (!hp) { + snprintf(tmp, sizeof(tmp), "No such address: %s\n", hostname); + show_message("Host lookup failed", tmp); + return -1; + } + hide_doing(); + snprintf(tmp, sizeof(tmp), "Connecting to %s", hostname); + show_doing("Connecting...", tmp); + + session.sin.sin_family = AF_INET; + session.sin.sin_port = htons(DEFAULT_MANAGER_PORT); + memcpy(&session.sin.sin_addr, hp->h_addr, sizeof(session.sin.sin_addr)); + + if (connect(session.fd,(struct sockaddr*)&session.sin, sizeof(session.sin))) { + snprintf(tmp, sizeof(tmp), "%s failed: %s\n", hostname, strerror(errno)); + show_message("Connect Failed", tmp); + return -1; + } + + hide_doing(); + + login = newtButton(5, 6, "Login"); + cancel = newtButton(25, 6, "Cancel"); + newtCenteredWindow(40, 10, "Asterisk Manager Login"); + snprintf(tmp, sizeof(tmp), "Host: %s", hostname); + label = newtLabel(4,1, tmp); + + ulabel = newtLabel(4,2,"Username:"); + plabel = newtLabel(4,3,"Password:"); + + username = newtEntry(14, 2, "", 20, _NEWT_CAST &user, 0); + password = newtEntry(14, 3, "", 20, _NEWT_CAST &pass, NEWT_FLAG_HIDDEN); + + form = newtForm(NULL, NULL, 0); + newtFormAddComponents(form, username, password, login, cancel, label, ulabel, plabel,NULL); + newtFormRun(form, &es); + if (es.reason == NEWT_EXIT_COMPONENT) { + if (es.u.co == login) { + snprintf(tmp, sizeof(tmp), "Logging in '%s'...", user); + show_doing("Logging in", tmp); + /* Check to see if the remote host supports MD5 Authentication */ + manager_action("Challenge", "AuthType: MD5\r\n"); + m = wait_for_response(10000); + if (m && !strcasecmp(get_header(m, "Response"), "Success")) { + char *challenge = get_header(m, "Challenge"); + int x; + int len = 0; + char md5key[256] = ""; + struct MD5Context md5; + unsigned char digest[16]; + MD5Init(&md5); + MD5Update(&md5, (unsigned char *)challenge, strlen(challenge)); + MD5Update(&md5, (unsigned char *)pass, strlen(pass)); + MD5Final(digest, &md5); + for (x=0; x<16; x++) + len += sprintf(md5key + len, "%2.2x", digest[x]); + manager_action("Login", + "AuthType: MD5\r\n" + "Username: %s\r\n" + "Key: %s\r\n", + user, md5key); + m = wait_for_response(10000); + hide_doing(); + if (!strcasecmp(get_header(m, "Response"), "Success")) { + res = 0; + } else { + show_message("Login Failed", get_header(m, "Message")); + } + } else { + memset(m, 0, sizeof(m)); + manager_action("Login", + "Username: %s\r\n" + "Secret: %s\r\n", + user, pass); + m = wait_for_response(10000); + hide_doing(); + if (m) { + if (!strcasecmp(get_header(m, "Response"), "Success")) { + res = 0; + } else { + show_message("Login Failed", get_header(m, "Message")); + } + } + } + } + } + newtFormDestroy(form); + return res; +} + +int main(int argc, char *argv[]) +{ + if (argc < 2) { + fprintf(stderr, "Usage: astman <host>\n"); + exit(1); + } + newtInit(); + newtCls(); + newtDrawRootText(0, 0, "Asterisk Manager (C)2002, Linux Support Services, Inc."); + newtPushHelpLine("Welcome to the Asterisk Manager!"); + if (login(argv[1])) { + newtFinished(); + exit(1); + } + manage_calls(argv[1]); + newtFinished(); + return 0; +} diff --git a/utils/check_expr.c b/utils/check_expr.c new file mode 100644 index 000000000..d3dcc5d19 --- /dev/null +++ b/utils/check_expr.c @@ -0,0 +1,355 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 1999 - 2005, Digium, Inc. + * + * Mark Spencer <markster@digium.com> + * + * 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 Version 2. See the LICENSE file + * at the top of the source tree. + */ + +#include <stdio.h> +#include <stddef.h> +#include <stdarg.h> +#include <string.h> +#include <stdlib.h> +#include <../include/asterisk/ast_expr.h> + +static int global_lineno = 1; +static int global_expr_count=0; +static int global_expr_max_size=0; +static int global_expr_tot_size=0; +static int global_warn_count=0; +static int global_OK_count=0; + +struct varz +{ + char varname[100]; /* a really ultra-simple, space-wasting linked list of var=val data */ + char varval[1000]; /* if any varname is bigger than 100 chars, or val greater than 1000, then **CRASH** */ + struct varz *next; +}; + +struct varz *global_varlist; + +/* Our own version of ast_log, since the expr parser uses it. */ + +void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...) __attribute__((format(printf,5,6))); + +void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...) +{ + va_list vars; + va_start(vars,fmt); + + printf("LOG: lev:%d file:%s line:%d func: %s ", + level, file, line, function); + vprintf(fmt, vars); + fflush(stdout); + va_end(vars); +} +void ast_register_file_version(const char *file, const char *version); +void ast_unregister_file_version(const char *file); + +char *find_var(const char *varname); +void set_var(const char *varname, const char *varval); +unsigned int check_expr(char* buffer, char* error_report); +int check_eval(char *buffer, char *error_report); +void parse_file(const char *fname); + +void ast_register_file_version(const char *file, const char *version) +{ +} + +void ast_unregister_file_version(const char *file) +{ +} + +char *find_var(const char *varname) /* the list should be pretty short, if there's any list at all */ +{ + struct varz *t; + for (t= global_varlist; t; t = t->next) { + if (!strcmp(t->varname, varname)) { + return t->varval; + } + } + return 0; +} + +void set_var(const char *varname, const char *varval) +{ + struct varz *t = (struct varz*)calloc(1,sizeof(struct varz)); + strcpy(t->varname, varname); + strcpy(t->varval, varval); + t->next = global_varlist; + global_varlist = t; +} + +unsigned int check_expr(char* buffer, char* error_report) +{ + char* cp; + unsigned int warn_found = 0; + + error_report[0] = 0; + + for (cp = buffer; *cp; ++cp) + { + switch (*cp) + { + case '"': + /* skip to the other end */ + while (*(++cp) && *cp != '"') ; + + if (*cp == 0) + { + fprintf(stderr, + "Trouble? Unterminated double quote found at line %d\n", + global_lineno); + } + break; + + case '>': + case '<': + case '!': + if ( (*(cp + 1) == '=') + && ( ( (cp > buffer) && (*(cp - 1) != ' ') ) || (*(cp + 2) != ' ') ) ) + { + char msg[200]; + snprintf(msg, + sizeof(msg), + "WARNING: line %d: '%c%c' operator not separated by spaces. This may lead to confusion. You may wish to use double quotes to quote the grouping it is in. Please check!\n", + global_lineno, *cp, *(cp + 1)); + strcat(error_report, msg); + ++global_warn_count; + ++warn_found; + } + break; + + case '|': + case '&': + case '=': + case '+': + case '-': + case '*': + case '/': + case '%': + case '?': + case ':': + if ( ( (cp > buffer) && (*(cp - 1) != ' ') ) || (*(cp + 1) != ' ') ) + { + char msg[200]; + snprintf(msg, + sizeof(msg), + "WARNING: line %d: '%c' operator not separated by spaces. This may lead to confusion. You may wish to use double quotes to quote the grouping it is in. Please check!\n", + global_lineno, *cp ); + strcat(error_report, msg); + ++global_warn_count; + ++warn_found; + } + break; + } + } + + return warn_found; +} + +int check_eval(char *buffer, char *error_report) +{ + char *cp, *ep; + char s[4096]; + char evalbuf[80000]; + int result; + + error_report[0] = 0; + ep = evalbuf; + + for (cp=buffer;*cp;cp++) { + if (*cp == '$' && *(cp+1) == '{') { + int brack_lev = 1; + char *xp= cp+2; + + while (*xp) { + if (*xp == '{') + brack_lev++; + else if (*xp == '}') + brack_lev--; + + if (brack_lev == 0) + break; + xp++; + } + if (*xp == '}') { + char varname[200]; + char *val; + + strncpy(varname,cp+2, xp-cp-2); + varname[xp-cp-2] = 0; + cp = xp; + val = find_var(varname); + if (val) { + char *z = val; + while (*z) + *ep++ = *z++; + } + else { + *ep++ = '5'; /* why not */ + *ep++ = '5'; + *ep++ = '5'; + } + } + else { + printf("Unterminated variable reference at line %d\n", global_lineno); + *ep++ = *cp; + } + } + else if (*cp == '\\') { + /* braindead simple elim of backslash */ + cp++; + *ep++ = *cp; + } + else + *ep++ = *cp; + } + *ep++ = 0; + + /* now, run the test */ + result = ast_expr(evalbuf, s, sizeof(s)); + if (result) { + sprintf(error_report,"line %d, evaluation of $[ %s ] result: %s\n", global_lineno, evalbuf, s); + return 1; + } else { + sprintf(error_report,"line %d, evaluation of $[ %s ] result: ****SYNTAX ERROR****\n", global_lineno, evalbuf); + return 1; + } +} + + +void parse_file(const char *fname) +{ + FILE *f = fopen(fname,"r"); + FILE *l = fopen("expr2_log","w"); + int c1; + char last_char= 0; + char buffer[30000]; /* I sure hope no expr gets this big! */ + + if (!f) { + fprintf(stderr,"Couldn't open %s for reading... need an extensions.conf file to parse!\n",fname); + exit(20); + } + if (!l) { + fprintf(stderr,"Couldn't open 'expr2_log' file for writing... please fix and re-run!\n"); + exit(21); + } + + global_lineno = 1; + + while ((c1 = fgetc(f)) != EOF) { + if (c1 == '\n') + global_lineno++; + else if (c1 == '[') { + if (last_char == '$') { + /* bingo, an expr */ + int bracklev = 1; + int bufcount = 0; + int retval; + char error_report[30000]; + + while ((c1 = fgetc(f)) != EOF) { + if (c1 == '[') + bracklev++; + else if (c1 == ']') + bracklev--; + if (c1 == '\n') { + fprintf(l, "ERROR-- A newline in an expression? Weird! ...at line %d\n", global_lineno); + fclose(f); + fclose(l); + printf("--- ERROR --- A newline in the middle of an expression at line %d!\n", global_lineno); + } + + if (bracklev == 0) + break; + buffer[bufcount++] = c1; + } + if (c1 == EOF) { + fprintf(l, "ERROR-- End of File Reached in the middle of an Expr at line %d\n", global_lineno); + fclose(f); + fclose(l); + printf("--- ERROR --- EOF reached in middle of an expression at line %d!\n", global_lineno); + exit(22); + } + + buffer[bufcount] = 0; + /* update stats */ + global_expr_tot_size += bufcount; + global_expr_count++; + if (bufcount > global_expr_max_size) + global_expr_max_size = bufcount; + + retval = check_expr(buffer, error_report); /* check_expr should bump the warning counter */ + if (retval != 0) { + /* print error report */ + printf("Warning(s) at line %d, expression: $[%s]; see expr2_log file for details\n", + global_lineno, buffer); + fprintf(l, "%s", error_report); + } + else { + printf("OK -- $[%s] at line %d\n", buffer, global_lineno); + global_OK_count++; + } + error_report[0] = 0; + retval = check_eval(buffer, error_report); + fprintf(l, "%s", error_report); + } + } + last_char = c1; + } + printf("Summary:\n Expressions detected: %d\n Expressions OK: %d\n Total # Warnings: %d\n Longest Expr: %d chars\n Ave expr len: %d chars\n", + global_expr_count, + global_OK_count, + global_warn_count, + global_expr_max_size, + (global_expr_count) ? global_expr_tot_size/global_expr_count : 0); + + fclose(f); + fclose(l); +} + + +int main(int argc,char **argv) +{ + int argc1; + char *eq; + + if (argc < 2) { + printf("check_expr -- a program to look thru extensions.conf files for $[...] expressions,\n"); + printf(" and run them thru the parser, looking for problems\n"); + printf("Hey-- give me a path to an extensions.conf file!\n"); + printf(" You can also follow the file path with a series of variable decls,\n"); + printf(" of the form, varname=value, each separated from the next by spaces.\n"); + printf(" (this might allow you to avoid division by zero messages, check that math\n"); + printf(" is being done correctly, etc.)\n"); + printf(" Note that messages about operators not being surrounded by spaces is merely to alert\n"); + printf(" you to possible problems where you might be expecting those operators as part of a string.\n"); + printf(" (to include operators in a string, wrap with double quotes!)\n"); + + exit(19); + } + global_varlist = 0; + for (argc1=2;argc1 < argc; argc1++) { + if ((eq = strchr(argv[argc1],'='))) { + *eq = 0; + set_var(argv[argc1],eq+1); + } + } + + /* parse command args for x=y and set varz */ + + parse_file(argv[1]); + return 0; +} diff --git a/utils/expr2.testinput b/utils/expr2.testinput new file mode 100644 index 000000000..948baaf94 --- /dev/null +++ b/utils/expr2.testinput @@ -0,0 +1,92 @@ +2 + 2 + 2 + 2 + +2 - 4 +4 - 2 +-4 - -2 +4 + 2 * 8 +(4 + 2) * 8 +4 + (2 * 8) +4 + (2 * 8) ? 3 :: 6 +4 + 8 / 2 +4 + 8 / 3 +(4+8) / 3 +4 + 8 % 3 +4 + 9 % 3 +(4+9) %3 +(4+8) %3 +(4+9) %3 +(4+8) %3 +(4+9) % 3 +(4+8) % 3 +(4+9) % 3 +(4+8) % 3 +(4+9)% 3 +(4+8)% 3 +(4+9)% 3 +(4+8)% 3 +4 & 4 +0 & 4 +0 & 0 +2 | 0 +2 | 4 +0 | 0 +!0 | 0 +!4 | 0 +4 | !0 +!4 | !0 +3 < 4 +4 < 3 +3 > 4 +4 > 3 +3 = 3 +3 = 4 +3 != 3 +3 != 4 +3 >= 4 +3 >= 3 +4 >= 3 +3 <= 4 +4 <= 3 +4 <= 4 +3 > 4 & 4 < 3 +4 > 3 & 3 < 4 +x = x +y = x +x != y +x != x +"Something interesting" =~ interesting +"Something interesting" =~ Something +"Something interesting" : Something +"Something interesting" : interesting +"Something interesting" =~ "interesting" +"Something interesting" =~ "Something" +"Something interesting" : "Something" +"Something interesting" : "interesting" +"Something interesting" =~ (interesting) +"Something interesting" =~ (Something) +"Something interesting" : (Something) +"Something interesting" : (interesting) +"Something interesting" =~ "\(interesting\)" +"Something interesting" =~ "\(Something\)" +"Something interesting" : "\(Something\)" +"Something interesting" : "\(interesting\)" +"011043567857575" : "011\(..\)" +"9011043567857575" : "011\(..\)" +"011043567857575" =~ "011\(..\)" +"9011043567857575" =~ "011\(..\)" +"Something interesting" =~ (interesting) +"Something interesting" =~ (Something) +"Something interesting" : (Something) +"Something interesting" : (interesting) +"Something interesting" =~ "(interesting)" +"Something interesting" =~ "(Something)" +"Something interesting" : "(Something)" +"Something interesting" : "(interesting)" +"011043567857575" : "011(..)" +"9011043567857575" : "011(..)" +"011043567857575" =~ "011(..)" +"9011043567857575" =~ "011(..)" +3 +something +043 diff --git a/utils/frame.c b/utils/frame.c new file mode 100644 index 000000000..bdc65e12f --- /dev/null +++ b/utils/frame.c @@ -0,0 +1,1092 @@ +/**************************************************************************** + * + * Programs for processing sound files in raw- or WAV-format. + * -- Useful functions for parsing command line options and + * issuing errors, warnings, and chit chat. + * + * Name: frame.c + * Version: see static char *standardversion, below. + * Author: Mark Roberts <mark@manumark.de> + * Michael Labuschke <michael@labuschke.de> sys_errlist fixes + * + ****************************************************************************/ +/**************************************************************************** + * These are useful functions that all DSP programs might find handy + ****************************************************************************/ + +#include <stdio.h> +#include <math.h> +#include <stdlib.h> /* for exit and malloc */ +#include <string.h> +#include <time.h> +#include <stdarg.h> +#include <errno.h> +#include <assert.h> +#include "frame.h" + +time_t stopwatch; /* will hold time at start of calculation */ +int samplefrequency; +unsigned short samplewidth; +unsigned short channels; +int wavout; /* TRUE iff out file should be a .WAV file */ +int iswav; /* TRUE iff in file was found to be a .WAV file */ +FILE *in, *out; +char *infilename, *outfilename; +int verboselevel; +char *version = ""; +char *usage = ""; +static int test_usage; + +static char *standardversion = "frame version 1.3, June 13th 2001"; +static char *standardusage = +"\nOptions common to all mark-dsp programs:\n" + +"-h \t\t create a WAV-header on output files.\n" +"-c#\t\t set number of channels to # (1 or 2). Default: like input.\n" +"-w#\t\t set number of bits per sample (width) to # (only 16)\n" +"-f#\t\t set sample frequency to #. Default: like input.\n" +"-V \t\t verbose: talk a lot.\n" +"-Q \t\t quiet: talk as little as possible.\n\n" +"In most cases, a filename of '-' means stdin or stdout.\n\n" +"Bug-reports: mark@manumark.de\n" +; + +/* ----------------------------------------------------------------------- + Writes the number of samples to result that are yet to be read from anyin. + Return values are TRUE on success, FALSE on failure. + -----------------------------------------------------------------------*/ +int getremainingfilelength( FILE *anyin, long *result) +{ + long i; + + i = ftell(anyin); + if (i == -1) return FALSE; + if (fseek(anyin, 0, SEEK_END) == -1) return FALSE; + *result = ftell(anyin); + if (*result == -1) return FALSE; + (*result) -= i; + (*result) /= samplewidth; + if (fseek(anyin, i, SEEK_SET) == -1) return FALSE; + return TRUE; +} + +/* ----------------------------------------------------------------------- + Read a .pk-header from 'anyin'. + -----------------------------------------------------------------------*/ +void readpkheader( FILE *anyin) +{ + unsigned short tempushort; + int tempint, i, x; + unsigned char blood[8]; + + for (i = 0; i < 11; i++) + { + if (!fread( &tempint, 4, 1, anyin)) { + return; + } + printf( "%d: %d, ", i, tempint); + } + printf( "\n"); + if (!fread( blood, 1, 8, anyin)) { + return; + } + for (i = 0; i < 8; i++) + printf( "%d ", blood[i]); + printf( "\n"); + for (i = 0; i < 8; i++) + { + for (x = 128; x > 0; x /= 2) + printf((blood[i] & x) == 0? "0 ":"1 "); + printf(i%4==3? "\n":"| "); + } + printf( "\n"); + for (i = 0; i < 2; i++) + { + if (!fread( &tempint, 4, 1, anyin)) { + return; + } + printf( "%d: %d, ", i, tempint); + } + printf( "\n"); + for (i = 0; i < 2; i++) + { + if (!fread( &tempushort, 2, 1, anyin)) { + return; + } + printf( "%d: %d, ", i, tempushort); + } + printf( "\n"); +} + + + +/* ----------------------------------------------------------------------- + Read a .WAV header from 'anyin'. See header for details. + -----------------------------------------------------------------------*/ +void readwavheader( FILE *anyin) +{ + unsigned int tempuint, sf; + unsigned short tempushort, cn; + char str[9]; + int nowav = FALSE; + + iswav = FALSE; + + if (ftell(anyin) == -1) /* If we cannot seek this file */ + { + nowav = TRUE; /* -> Pretend this is no wav-file */ + chat("File not seekable: not checking for WAV-header.\n"); + } + else + { + /* Expect four bytes "RIFF" and four bytes filelength */ + if (!fread(str, 1, 8, anyin)) { /* 0 */ + return; + } + str[4] = '\0'; + if (strcmp(str, "RIFF") != 0) nowav = TRUE; + /* Expect eight bytes "WAVEfmt " */ + if (!fread(str, 1, 8, anyin)) { /* 8 */ + return; + } + str[8] = '\0'; + if (strcmp(str, "WAVEfmt ") != 0) nowav = TRUE; + /* Expect length of fmt data, which should be 16 */ + if (!fread(&tempuint, 4, 1, anyin)) { /* 16 */ + return; + } + if (tempuint != 16) nowav = TRUE; + /* Expect format tag, which should be 1 for pcm */ + if (!fread(&tempushort, 2, 1, anyin)) { /* 20 */ + return; + } + if (tempushort != 1) + nowav = TRUE; + /* Expect number of channels */ + if (!fread(&cn, 2, 1, anyin)) { /* 20 */ + return; + } + if (cn != 1 && cn != 2) nowav = TRUE; + /* Read samplefrequency */ + if (!fread(&sf, 4, 1, anyin)) { /* 24 */ + return; + } + /* Read bytes per second: Should be samplefreq * channels * 2 */ + if (!fread(&tempuint, 4, 1, anyin)) { /* 28 */ + return; + } + if (tempuint != sf * cn * 2) nowav = TRUE; + /* read bytes per frame: Should be channels * 2 */ + if (!fread(&tempushort, 2, 1, anyin)) { /* 32 */ + return; + } + if (tempushort != cn * 2) nowav = TRUE; + /* Read bits per sample: Should be 16 */ + if (!fread(&tempushort, 2, 1, anyin)) { /* 34 */ + return; + } + if (tempushort != 16) nowav = TRUE; + if (!fread(str, 4, 1, anyin)) { /* 36 */ + return; + } + str[4] = '\0'; + if (strcmp(str, "data") != 0) nowav = TRUE; + if (!fread(&tempuint, 4, 1, anyin)) { /* 40 */ + return; + } + if (nowav) + { + fseek(anyin, 0, SEEK_SET); /* Back to beginning of file */ + chat("File has no WAV header.\n"); + } + else + { + samplefrequency = sf; + channels = cn; + chat("Read WAV header: %d channels, samplefrequency %d.\n", + channels, samplefrequency); + iswav = TRUE; + } + } + return; +} + + + +/* ----------------------------------------------------------------------- + Write a .WAV header to 'out'. See header for details. + -----------------------------------------------------------------------*/ +void makewavheader( void) +{ + unsigned int tempuint, filelength; + unsigned short tempushort; + + /* If fseek fails, don't create the header. */ + if (fseek(out, 0, SEEK_END) != -1) + { + filelength = ftell(out); + chat("filelength %d, ", filelength); + fseek(out, 0, SEEK_SET); + if (!fwrite("RIFF", 1, 4, out)) { /* 0 */ + return; + } + tempuint = filelength - 8; + if (!fwrite(&tempuint, 4, 1, out)) { /* 4 */ + return; + } + if (!fwrite("WAVEfmt ", 1, 8, out)) { /* 8 */ + return; + } + /* length of fmt data 16 bytes */ + tempuint = 16; + if (!fwrite(&tempuint, 4, 1, out)) { /* 16 */ + return; + } + /* Format tag: 1 for pcm */ + tempushort = 1; + if (!fwrite(&tempushort, 2, 1, out)) { /* 20 */ + return; + } + chat("%d channels\n", channels); + if (!fwrite(&channels, 2, 1, out)) { + return; + } + chat("samplefrequency %d\n", samplefrequency); + if (!fwrite(&samplefrequency, 4, 1, out)) { /* 24 */ + return; + } + /* Bytes per second */ + tempuint = channels * samplefrequency * 2; + if (!fwrite(&tempuint, 4, 1, out)) { /* 28 */ + return; + } + /* Block align */ + tempushort = 2 * channels; + if (!fwrite(&tempushort, 2, 1, out)) { /* 32 */ + return; + } + /* Bits per sample */ + tempushort = 16; + if (!fwrite(&tempushort, 2, 1, out)) { /* 34 */ + return; + } + if (!fwrite("data", 4, 1, out)) { /* 36 */ + return; + } + tempuint = filelength - 44; + if (!fwrite(&tempuint, 4, 1, out)) { /* 40 */ + return; + } + } + return; +} + +/* ----------------------------------------------------------------------- + After all is read and done, inform the inclined user of the elapsed time + -----------------------------------------------------------------------*/ +static void statistics( void) +{ + int temp; + + temp = time(NULL) - stopwatch; + if (temp != 1) + { + inform ("\nTime: %d seconds\n", temp); + } + else + { + inform ("\nTime: 1 second\n"); + } + return; +} + + +/* ----------------------------------------------------------------------- + Start the stopwatch and make sure the user is informed at end of program. + -----------------------------------------------------------------------*/ +void startstopwatch(void) +{ + stopwatch = time(NULL); /* Remember time 'now' */ + atexit(statistics); /* Call function statistics() at exit. */ + + return; +} + +/* -------------------------------------------------------------------- + Tests the character 'coal' for being a command line option character, + momentarrily '-'. + -------------------------------------------------------------------- */ +int isoptionchar (char coal) +{ + return (coal =='-'); +} + +/* ----------------------------------------------------------------------- + Reads through the arguments on the lookout for an option starting + with 'string'. The rest of the option is read as a time and passed + to *result, where the result is meant to mean 'number of samples' in + that time. + On failure, *result is unchanged. + return value is TRUE on success, FALSE otherwise. + -----------------------------------------------------------------------*/ +int parsetimearg( int argcount, char *args[], char *string, int *result) +{ + int i; + + if ((i = findoption( argcount, args, string)) > 0) + { + if (parsetime(args[i] + 1 + strlen( string), result)) + return TRUE; + argerrornum(args[i]+1, ME_NOTIME); + } + return FALSE; +} + +/* ----------------------------------------------------------------------- + The string argument is read as a time and passed + to *result, where the result is meant to mean 'number of samples' in + that time. + On failure, *result is unchanged. + return value is TRUE on success, FALSE otherwise. + -----------------------------------------------------------------------*/ +int parsetime(char *string, int *result) +{ + int k; + double temp; + char m, s, end; + + k = sscanf(string, "%lf%c%c%c", &temp, &m, &s, &end); + switch (k) + { + case 0: case EOF: case 4: + return FALSE; + case 1: + *result = temp; + break; + case 2: + if (m == 's') + *result = temp * samplefrequency; + else + return FALSE; + break; + case 3: + if (m == 'm' && s == 's') + *result = temp * samplefrequency / 1000; + else if (m == 'H' && s == 'z') + *result = samplefrequency / temp; + else + return FALSE; + break; + default: + argerrornum(NULL, ME_THISCANTHAPPEN); + } + return TRUE; +} + +/* ----------------------------------------------------------------------- + The string argument is read as a frequency and passed + to *result, where the result is meant to mean 'number of samples' in + one cycle of that frequency. + On failure, *result is unchanged. + return value is TRUE on success, FALSE otherwise. + -----------------------------------------------------------------------*/ +int parsefreq(char *string, double *result) +{ + int k; + double temp; + char m, s, end; + + k = sscanf(string, "%lf%c%c%c", &temp, &m, &s, &end); + switch (k) + { + case 0: case EOF: case 2: case 4: + return FALSE; + case 1: + *result = temp; + break; + case 3: + if (m == 'H' && s == 'z') + *result = samplefrequency / temp; + else + return FALSE; + break; + default: + argerrornum(NULL, ME_THISCANTHAPPEN); + } + return TRUE; +} + +char *parsefilearg( int argcount, char *args[]) +{ + int i; + char *result = NULL; + + for (i = 1; i < argcount; i++) + { + if (args[i][0] != '\0' && + (!isoptionchar (args[i][0]) || args[i][1] == '\0' )) + { + /*---------------------------------------------* + * The argument is a filename: * + * it is either no dash followed by something, * + * or it is a dash following by nothing. * + *---------------------------------------------*/ + result = malloc( strlen( args[i]) + 1); + if (result == NULL) + fatalperror( "Couldn't allocate memory for filename\n"); + strcpy( result, args[i]); + args[i][0] = '\0'; /* Mark as used up */ + break; + } + } + return result; +} + +int parseswitch( char *found, char *wanted) +{ + if (strncmp( found, wanted, strlen( wanted)) == 0) + { + if (found[strlen( wanted)] == '\0') + return TRUE; + else + argerrornum( found, ME_NOSWITCH); + } + return FALSE; +} + +int parseswitcharg( int argcount, char *args[], char *string) +{ + int i; + + if ((i = findoption( argcount, args, string)) > 0) + { + if (args[i][strlen( string) + 1] == '\0') + return TRUE; + else + argerrornum( args[i] + 1, ME_NOSWITCH); + } + return FALSE; +} + +int parseintarg( int argcount, char *args[], char *string, int *result) +{ + int i, temp; + char c; + + if ((i = findoption( argcount, args, string)) > 0) + { + switch (sscanf(args[i] + 1 + strlen( string), + "%d%c", &temp, &c)) + { + case 0: case EOF: case 2: + argerrornum(args[i]+1, ME_NOINT); + return FALSE; + case 1: + *result = temp; + break; + default: + say("frame.c: This can't happen\n"); + } + return TRUE; + } + else + { + return FALSE; + } +} + +/* -------------------------------------------------------------------- + Reads through the arguments on the lookout for an option starting + with 'string'. The rest of the option is read as a double and + passed to *result. + On failure, *result is unchanged. + return value is TRUE on success, FALSE otherwise. + -------------------------------------------------------------------- */ +int parsedoublearg( int argcount, char *args[], char *string, double *result) +{ + int i; + double temp; + char end; + + if ((i = findoption( argcount, args, string)) > 0) + { + switch (sscanf(args[i] + 1 + strlen( string), "%lf%c", &temp, &end)) + { + case 0: case EOF: case 2: + argerrornum(args[i]+1, ME_NODOUBLE); + return FALSE; + case 1: + *result = temp; + break; + default: + say("frame.c: This can't happen\n"); + } + return TRUE; + } + else + { + return FALSE; + } +} + +/* -------------------------------------------------------------------- + Reads through the arguments on the lookout for an option starting + with 'string'. The rest of the option is read as a volume, i.e. + absolute, percent or db. The result is passed to *result. + On failure, *result is unchanged. + return value is TRUE on success, FALSE otherwise. + -------------------------------------------------------------------- */ +int parsevolarg( int argcount, char *args[], char *string, double *result) +{ + double vol = 1.0; + char sbd, sbb, end; + int i, weird = FALSE; + + if ((i = findoption( argcount, args, string)) > 0) + { + switch (sscanf(args[i] + 1 + strlen( string), + "%lf%c%c%c", &vol, &sbd, &sbb, &end)) + { + case 0: case EOF: case 4: + weird = TRUE; + break; /* No number: error */ + case 1: + *result = vol; + break; + case 2: + if (sbd == '%') + *result = vol / 100; + else + weird = TRUE; /* One char but no percent: error */ + break; + case 3: + if (sbd =='d' && sbb == 'b') + *result = pow(2, vol / 6.02); + else + weird = TRUE; /* Two chars but not db: error */ + break; + default: + say("frame.c: This can't happen.\n"); + } + if (weird) + argerrornum( args[i] + 1, ME_NOVOL); + /* ("Weird option: couldn't parse volume '%s'\n", args[i]+2); */ + return !weird; + } + else + { + return FALSE; + } +} + + +/* -------------------------------------------------------------------- + Reads the specified string 's' and interprets it as a volume. The string + would be of the form 1.8 or 180% or 5db. + On success, the return value TRUE and *result is given result + (i.e. the relative volume, i.e. 1.8). On failure, FALSE is returned and + result is given value 1.0. + -------------------------------------------------------------------- */ +int parsevolume(char *s, double *result) +{ + int k; + char sbd, sbb, end; + + *result = 1.0; + k = sscanf(s, "%lf%c%c%c", result, &sbd, &sbb, &end); + switch (k) + { + case 0: + case EOF: + case 4: + return FALSE; + case 1: + break; + case 2: + if (sbd != '%') + return FALSE; + (*result) /=100; + break; + case 3: + if (sbd !='d' || sbb != 'b') + return FALSE; + (*result) = pow(2, (*result) / 6.02); + break; + default: + say("parsevolume: This can't happen (%d).\n", k); + } + return TRUE; +} + +/* -------------------------------------------------------------------- + Reports an error due to parsing the string 's' encountered on the + command line. + -------------------------------------------------------------------- */ +void argerror(char *s) +{ + error ("Error parsing command line. Unrecognized option:\n\t-%s\n", s); + fatalerror("\nTry --help for help.\n"); +} + +/* -------------------------------------------------------------------- + Reports an error due to parsing the string 's' encountered on the + command line. 'code' indicates the type of error. + -------------------------------------------------------------------- */ +void argerrornum(char *s, Errornum code) +{ + char *message; + + if (code == ME_TOOMANYFILES) + { + error("Too many files on command line: '%s'.\n", s); + } + else + { + if (s != NULL) + error ("Error parsing option -%s:\n\t", s); + switch( code) + { + case ME_NOINT: + message = "Integer expected"; + break; + case ME_NODOUBLE: + message = "Floating point number expected"; + break; + case ME_NOTIME: + message = "Time argument expected"; + break; + case ME_NOVOL: + message = "Volume argument expected"; + break; + case ME_NOSWITCH: + message = "Garbage after switch-type option"; + break; + case ME_HEADERONTEXTFILE: + message = "Option -h is not useful for text-output"; + break; + case ME_NOINFILE: + message = "No input file specified"; + break; + case ME_NOOUTFILE: + message = "No output file specified"; + break; + case ME_NOIOFILE: + message = "No input/output file specified"; + break; + case ME_NOSTDIN: + message = "Standard in not supported here"; + break; + case ME_NOSTDOUT: + message = "Standard out not supported here"; + break; + case ME_NOSTDIO: + message = "Standard in/out not supported here"; + break; + case ME_NOTENOUGHFILES: + message = "Not enough files specified"; + break; + case ME_THISCANTHAPPEN: + fatalerror("\nThis can't happen. Report this as a bug\n"); + /* fatalerror does not return */ + default: + error("Error code %d not implemented. Fix me!\n", code); + message = "Error message not implemented. Fix me!"; + } + error("%s\n", message); + } + fatalerror("\nTry --help for help.\n"); +} + +/* -------------------------------------------------------------------- + Reports an error due to parsing the string 's' encountered on the + command line. 'message' explains the type of error. + -------------------------------------------------------------------- */ +void argerrortxt(char *s, char *message) +{ + if (s != NULL) + error ("Error parsing option -%s:\n\t", s); + else + error ("Error parsing command line:\n\t"); + error ("%s\n", message); + fatalerror("\nTry --help for help.\n"); +} + +/* -------------------------------------------------------------------- + Check for any remaining arguments and complain about their existence + -------------------------------------------------------------------- */ +void checknoargs( int argcount, char *args[]) +{ + int i, errorcount = 0; + + for (i = 1; i < argcount; i++) + { + if (args[i][0] != '\0') /* An unused argument! */ + { + errorcount++; + if (errorcount == 1) + error("The following arguments were not recognized:\n"); + error("\t%s\n", args[i]); + } + } + if (errorcount > 0) /* Errors are fatal */ + fatalerror("\nTry --help for help.\n"); + + return; /* No errors? Return. */ +} + +/* -------------------------------------------------------------------- + Parses the command line arguments as represented by the function + arguments. Sets the global variables 'in', 'out', 'samplefrequency' + and 'samplewidth' accordingly. Also verboselevel. + The files 'in' and 'out' are even opened according to 'fileswitch'. + See headerfile for details + -------------------------------------------------------------------- */ +void parseargs( int argcount, char *args[], int fileswitch) +{ + char *filename; + int tempint; + + if ((fileswitch & 1) != 0) /* If getting infile */ + in = NULL; + if ((fileswitch & 4) != 0) /* If getting outfile */ + out = NULL; + wavout = FALSE; + verboselevel = 5; + samplefrequency = DEFAULTFREQ; + samplewidth = 2; + channels = 1; + + /*-----------------------------------------------* + * First first check testcase, usage and version * + *-----------------------------------------------*/ + test_usage = parseswitcharg( argcount, args, "-test-usage"); + if (parseswitcharg( argcount, args, "-help")) + { + printf("%s%s", usage, standardusage); + exit(0); + } + if (parseswitcharg( argcount, args, "-version")) + { + printf("%s\n(%s)\n", version, standardversion); + exit(0); + } + /*--------------------------------------* + * Set verboselevel * + *--------------------------------------*/ + while (parseswitcharg( argcount, args, "V")) + verboselevel = 10; + while (parseswitcharg( argcount, args, "Q")) + verboselevel = 1; + /*-------------------------------------------------* + * Get filenames and open files * + *-------------------------------------------------*/ + if ((fileswitch & 1) != 0) /* Infile wanted */ + { + infilename = parsefilearg( argcount, args); + if (infilename == NULL) + argerrornum( NULL, ME_NOINFILE); + if (strcmp( infilename, "-") == 0) + { + infilename = "<stdin>"; + in = stdin; + if ((fileswitch & 2) != 0) /* Binfile wanted */ + readwavheader( in); + } + else + { + if ((fileswitch & 2) == 0) /* Textfile wanted */ + in = fopen(infilename, "rt"); + else /* Binfile wanted */ + if ((in = fopen(infilename, "rb")) != NULL) + readwavheader( in); + } + if (in == NULL) + fatalerror("Error opening input file '%s': %s\n", infilename,strerror(errno)); + else + inform("Using file '%s' as input\n", infilename); + } + if ((fileswitch & 4) != 0) /* Outfile wanted */ + { + outfilename = parsefilearg( argcount, args); + if (outfilename == NULL) + argerrornum( NULL, ME_NOOUTFILE); + if (strcmp( outfilename, "-") == 0) + { + outfilename = "<stdout>"; + out = stdout; + } + else + { + + if ((fileswitch & 8) == 0) /* Textfile wanted */ + out = fopen(outfilename, "wt"); + else /* Binfile wanted */ + out = fopen(outfilename, "wb"); + } + if (out == NULL) + fatalerror("Error opening output file '%s': %s\n", outfilename,strerror(errno)); + else + inform("Using file '%s' as output\n", outfilename); + } + if ((fileswitch & 32) != 0) /* In-/Outfile wanted */ + { + assert (in == NULL && out == NULL); + infilename = outfilename = parsefilearg( argcount, args); + if (outfilename == NULL) + argerrornum( NULL, ME_NOIOFILE); + if (strcmp( infilename, "-") == 0) + argerrornum( infilename, ME_NOSTDIN); + inform("Using file '%s' as input/output\n", outfilename); + in = out = fopen(outfilename, "r+"); + if (out == NULL) + fatalerror("Error opening input/output file '%s': %s\n", outfilename,strerror(errno)); + + readwavheader( in); + } + if ((fileswitch & 16) == 0) /* No additional files wanted */ + { + if ((filename = parsefilearg( argcount, args)) != NULL) + argerrornum( filename, ME_TOOMANYFILES); + } + + /*-------------------------------------------------* + * Set samplefrequency, width, wavout, + *-------------------------------------------------*/ + parseintarg( argcount, args, "f", &samplefrequency); + wavout = parseswitcharg( argcount, args, "h"); + if (parseintarg( argcount, args, "w", &tempint)) + { + if (tempint != 16) + argerrortxt(NULL, "Option -w is only valid " + "with value 16. Sorry."); + else + samplewidth = tempint; + } + if (parseintarg( argcount, args, "c", &tempint)) + { + if (tempint != 1 && tempint != 2) + argerrortxt(NULL, "Option -c is only valid " + "with values 1 or 2. Sorry."); + else + channels = tempint; + } + /*-------------------------------------------------* + * Create WAV-header on output if wanted. * + *-------------------------------------------------*/ + if (wavout) + switch (fileswitch & (12)) + { + case 4: /* User wants header on textfile */ + argerrornum( NULL, ME_HEADERONTEXTFILE); + case 12: /* User wants header on binfile */ + makewavheader(); + break; + case 0: /* User wants header, but there is no outfile */ + /* Problem: what about i/o-file, 32? You might want a header + on that? Better ignore this case. */ + break; + case 8: /* An application musn't ask for this */ + default: /* This can't happen */ + assert( FALSE); + } + return; +} + +/* -------------------------------------------------------------------- + Returns the index 'i' of the first argument that IS an option, and + which begins with the label 's'. If there is none, -1. + We also mark that option as done with, i.e. we cross it out. + -------------------------------------------------------------------- */ +int findoption( int argcount, char *args[], char *s) +{ + int i; + + if (test_usage) + printf("Checking for option -%s\n", s); + + for (i=1; i<argcount; i++) + { + if (isoptionchar (args[i][0]) && + strncmp( args[i] + 1, s, strlen( s)) == 0) + { + args[i][0] = '\0'; + return i; + } + } + return -1; +} + +/* -------------------------------------------------------------------- + Finishes off the .WAV header (if any) and exits correctly and formerly. + -------------------------------------------------------------------- */ +int myexit (int value) +{ + switch (value) + { + case 0: + if (wavout) + makewavheader(); /* Writes a fully informed .WAV header */ + chat("Success!\n"); + break; + default: + chat("Failure.\n"); + break; + } + exit (value); +} + +/* -------------------------------------------------------------------- + Reads the stated input file bufferwise, calls the function 'work' + with the proper values, and writes the result to the stated output file. + Return value: TRUE on success, FALSE otherwise. + -------------------------------------------------------------------- */ +int workloop( FILE *theinfile, FILE *theoutfile, + int (*work)( short *buffer, int length) ) +{ + short *buffer; + int length, nowlength; + + length = BUFFSIZE; + if ((buffer = malloc( sizeof(short) * length)) == NULL) + fatalperror (""); + while (TRUE) + { + nowlength = fread(buffer, sizeof(short), length, theinfile); + if (ferror( theinfile) != 0) + fatalperror("Error reading input file"); + if (nowlength == 0) /* Reached end of input file */ + break; + /* Call the routine that does the work */ + if (!work (buffer, nowlength)) /* On error, stop. */ + return FALSE; + if (!fwrite(buffer, sizeof(short), nowlength, theoutfile)) { + return FALSE; + } + if (ferror( theoutfile) != 0) + fatalperror("Error writing to output file"); + } + return TRUE; /* Input file done with, no errors. */ +} + +int __attribute__((format(printf, 1, 2))) chat( const char *format, ...) +{ + va_list ap; + int result = 0; + + if (verboselevel > 5) + { + va_start( ap, format); + result = vfprintf( stderr, format, ap); + va_end( ap); + } + return result; +} + + +int __attribute__((format(printf, 1, 2))) inform( const char *format, ...) +{ + va_list ap; + int result = 0; + + if (verboselevel > 1) + { + va_start( ap, format); + result = vfprintf( stderr, format, ap); + va_end( ap); + } + return result; +} + +int __attribute__((format(printf, 1, 2))) error( const char *format, ...) +{ + va_list ap; + int result; + + va_start( ap, format); + result = vfprintf( stderr, format, ap); + va_end( ap); + return result; +} + +void __attribute__((format(printf, 1, 2))) fatalerror( const char *format, ...) +{ + va_list ap; + + va_start( ap, format); + vfprintf( stderr, format, ap); + va_end( ap); + myexit(1); +} + +void fatalperror( const char *string) +{ + perror( string); + myexit( 1); +} + +int __attribute__((format(printf, 1, 2))) say( const char *format, ...) +{ + va_list ap; + int result; + + va_start( ap, format); + result = vfprintf( stdout, format, ap); + va_end( ap); + return result; +} + + +char *malloccopy( char *string) +{ + char *result; + + result = malloc( strlen( string) + 1); + if (result != NULL) + strcpy( result, string); + return result; +} + + +char *mallocconcat( char *one, char *two) +{ + char *result; + + result = malloc( strlen( one) + strlen( two) + 1); + if (result != NULL) + { + strcpy( result, one); + strcat( result, two); + } + return result; +} + +double double2db( double value) +{ + if (value < 0) + value = -value; + return 6.0 * log( value / 32767) / log( 2); +} + +void readawaysamples( FILE *in, size_t size) +{ + short *buffer; + int samplesread, count; + + buffer = malloc( sizeof( *buffer) * BUFFSIZE); + if (buffer == NULL) fatalperror("Couldn't allocate buffer"); + + while (size > 0) + { + if (size > BUFFSIZE) + count = BUFFSIZE; + else + count = size; + + samplesread = fread( buffer, sizeof(*buffer), count, in); + if (ferror( in) != 0) + fatalperror("Error reading input file"); + size -= samplesread; + } + free( buffer); +} + diff --git a/utils/frame.h b/utils/frame.h new file mode 100644 index 000000000..a07c605ec --- /dev/null +++ b/utils/frame.h @@ -0,0 +1,300 @@ +/**************************************************************************** + * + * Programs for processing sound files in raw- or WAV-format. + * -- Useful functions for parsing command line options and + * issuing errors, warnings, and chit chat. + * + * Name: frame.h + * Version: see frame.c + * Author: Mark Roberts <mark@manumark.de> + * + ****************************************************************************/ +/**************************************************************************** + * These are useful functions that all DSP programs might find handy + ****************************************************************************/ + +/* fileswitch for parseargs: + + The following are masks for several different ways of opening files. + -------------------------------------------------------------------- + Bit 0: Open infile? + Bit 1: Open infile as binary (as opposed to text) + Bit 2: Open outfile? + Bit 3: Open outfile as binary (as opposed to text) + Bit 4: Do not complain about too many file arguments + Bit 5: Open one file for input AND output, binary. +*/ +#define INTEXT (1+0) +#define INBIN (1+2) +#define OUTTEXT (4) +#define OUTBIN (4+8) +#define NOFILES (0) +#define NOCOMPLAIN (16) +#define IOBIN (32) + +#ifndef FALSE + #define FALSE (0==1) + #define TRUE (0==0) +#endif + +extern int samplefrequency; +extern unsigned short samplewidth; +extern unsigned short channels; +extern int wavout; /* TRUE iff out file is .WAV file */ +extern int iswav; /* TRUE iff in file was found to be a .WAV file */ +extern FILE *in, *out; +extern char *infilename, *outfilename; +extern int verboselevel; +extern char *version; /* String to be issued as version string. Should + be set by application. */ +extern char *usage; /* String to be issued as usage string. Should be + set by application. */ + +#define DEFAULTFREQ 44100 +#define BUFFSIZE 50000 /* How many samples to read in one go (preferred) */ +#define MINBUFFSIZE 5000 /* How many samples to read in one go (minimum) */ + +/************************************************* + * Types of errors handled by argerrornum() * + *************************************************/ +typedef enum +{ + ME_NOINT, + ME_NODOUBLE, + ME_NOTIME, + ME_NOVOL, + ME_NOSWITCH, + ME_TOOMANYFILES, + ME_HEADERONTEXTFILE, + ME_NOINFILE, + ME_NOOUTFILE, + ME_NOIOFILE, + ME_NOSTDIN, + ME_NOSTDOUT, + ME_NOSTDIO, + ME_NOTENOUGHFILES, + ME_THISCANTHAPPEN +} Errornum; + + +/* ----------------------------------------------------------------------- + Create memory and copy 'string', returning a pointer to the copy. + NULL is returned if malloc fails. + -----------------------------------------------------------------------*/ +extern char *malloccopy( char *string); + +/* ----------------------------------------------------------------------- + Start the stopwatch and make sure the user is informed at end of program. + -----------------------------------------------------------------------*/ +extern void startstopwatch(void); + +/* ----------------------------------------------------------------------- + Writes the number of samples to result that are yet to be read from anyin. + I.e. the number of remaining bytes is divided by the number of bytes per + sample value, but not by the number of channels. + Return values are TRUE on success, FALSE on failure. + -----------------------------------------------------------------------*/ +extern int getremainingfilelength( FILE *anyin, long *result); + +/* ----------------------------------------------------------------------- + Read a .pk-header from 'anyin' and printf the entries. + -----------------------------------------------------------------------*/ +void readpkheader( FILE *anyin); + +/* ----------------------------------------------------------------------- + Read a .WAV header from 'anyin'. + If it is recognised, the data is used. + Otherwise, we assume it's PCM-data and ignore the header. + The global variable 'iswav' is set on success, otherwise cleared. + -----------------------------------------------------------------------*/ +extern void readwavheader( FILE *anyin); + +/* ----------------------------------------------------------------------- + Write a .WAV header to 'out'. + The filepointer is placed at the end of 'out' before operation. + This should be called before any data is + written, and again, when ALL the data has been written. + First time, this positions the file pointer correctly; second time, the + missing data can be inserted that wasn't known the first time round. + -----------------------------------------------------------------------*/ +extern void makewavheader( void); + +/* -------------------------------------------------------------------- + Tests the character 'coal' for being a command line option character, + momentarrily '/' or '-'. + -------------------------------------------------------------------- */ +extern int isoptionchar (char coal); + +/* ----------------------------------------------------------------------- + Reads through the arguments on the lookout for an option starting + with 'string'. The rest of the option is read as a time and passed + to *result, where the result is meant to mean 'number of samples' in + that time. + On failure, *result is unchanged. + return value is TRUE on success, FALSE otherwise. + -----------------------------------------------------------------------*/ +extern int parsetimearg( int argcount, char *args[], char *string, + int *result); + +/* ----------------------------------------------------------------------- + The string argument is read as a time and passed to *result, where + the result is meant to mean 'number of samples' in that time. On + failure, *result is unchanged. + return value is TRUE on success, FALSE otherwise. + -----------------------------------------------------------------------*/ +int parsetime(char *string, int *result); + +/* ----------------------------------------------------------------------- + The string argument is read as a frequency and passed + to *result, where the result is meant to mean 'number of samples' in + one cycle of that frequency. + On failure, *result is unchanged. + return value is TRUE on success, FALSE otherwise. + -----------------------------------------------------------------------*/ +int parsefreq(char *string, double *result); + +/* -------------------------------------------------------------------- + Reads through the arguments on the lookout for a switch -'string'. + return value is TRUE if one exists, FALSE otherwise. + If characters remain after the switch, a fatal error is issued. + -------------------------------------------------------------------- */ +extern int parseswitcharg( int argcount, char *args[], char *string); + +/* -------------------------------------------------------------------- + Reads through the arguments on the lookout for an option starting + with 'string'. The rest of the option is read as an integer and + passed to &result. + On failure, &result is unchanged. + return value is TRUE on success, FALSE otherwise. + -------------------------------------------------------------------- */ +extern int parseintarg( int argcount, char *args[], char *string, + int *result); + +/* -------------------------------------------------------------------- + Reads through the arguments on the lookout for a filename, i.e. anything + that does not start with the optionchar. The filename is copied to + newly allocated memory, a pointer to which is returned. + The argument is marked as used. Therefore repeated use of this function + will yield a complete list of filenames on the commandline. + If malloc() fails, the function does not return. + -------------------------------------------------------------------- */ +extern char *parsefilearg( int argcount, char *args[]); + +/* -------------------------------------------------------------------- + Reads through the arguments on the lookout for an option starting + with 'string'. The rest of the option is read as a double and + passed to *result. + On failure, *result is unchanged. + return value is TRUE on success, FALSE otherwise. + -------------------------------------------------------------------- */ +extern int parsedoublearg( int argcount, char *args[], char *string, + double *result); + +/* -------------------------------------------------------------------- + Reads through the arguments on the lookout for an option starting + with 'string'. The rest of the option is read as a volume, i.e. + absolute, percent or db. The result is passed to *result. + On failure, *result is unchanged. + -------------------------------------------------------------------- */ +extern int parsevolarg( int argcount, char *args[], char *string, + double *result); + +/* -------------------------------------------------------------------- + Reads the specified string and interprets it as a volume. The string + would be of the form 1.8 or 180% or 5db. + On success, the return value is the relative volume, i.e. 1.8 + On failure, -1 is returned. + -------------------------------------------------------------------- */ +extern int parsevolume(char *s, double *result); + +/* -------------------------------------------------------------------- + Reads through the arguments on the lookout for a switch -'string'. + return value is TRUE if one exists, FALSE otherwise. + If characters remain after the switch, a fatal error is issued. + -------------------------------------------------------------------- */ +extern int parseswitch( char *found, char *wanted); + +/* -------------------------------------------------------------------- + Reports an error due to parsing the string 's' encountered on the + command line. + -------------------------------------------------------------------- */ +extern void argerror(char *s); + +/* -------------------------------------------------------------------- + Reports an error due to parsing the string 's' encountered on the + command line. 'code' indicates the type of error. + -------------------------------------------------------------------- */ +extern void argerrornum(char *s, Errornum code); + +/* -------------------------------------------------------------------- + Reports an error due to parsing the string 's' encountered on the + command line. 'message' explains the type of error. + -------------------------------------------------------------------- */ +extern void argerrortxt(char *s, char *message); + +/* -------------------------------------------------------------------- + Check for any remaining arguments and complain about their existence. + If arguments are found, this function does not return. + -------------------------------------------------------------------- */ +extern void checknoargs( int argcount, char *args[]); + +/* -------------------------------------------------------------------- + Parses the command line arguments as represented by the function + arguments. Sets the global variables 'in', 'out', 'samplefrequency' + and 'samplewidth' accordingly. + According to 'fileswitch', in and out files are opened or not. See + above for an explanation of 'fileswitch'. + -------------------------------------------------------------------- */ +extern void parseargs( int argcount, char *args[], int fileswitch); + +/* -------------------------------------------------------------------- + Returns the index 'i' of the first argument that IS an option, and + which begins with the label 's'. If there is none, -1. + We also mark that option as done with, i.e. we cross it out. + -------------------------------------------------------------------- */ +extern int findoption( int argcount, char *args[], char *s); + +/* -------------------------------------------------------------------- + Finishes off the .WAV header (if any) and exits correctly and formerly. + -------------------------------------------------------------------- */ +extern int myexit (int value); + +/* -------------------------------------------------------------------- + Reads the stated input file bufferwise, calls the function 'work' + with the proper values, and writes the result to the stated output file. + Return value: TRUE on success, FALSE otherwise. + -------------------------------------------------------------------- */ +extern int workloop( FILE *theinfile, FILE *theoutfile, + int (*work)( short *buffer, int length) ); + +/* -------------------------------------------------------------------- + Five functions for printing to stderr. Depending on the level of verbose, + output may be supressed. fatalerror() is like error() but does not return. + fatalperror() is like the standard function perror() but does not return. + -------------------------------------------------------------------- */ +extern int chat( const char *format, ...); +extern int inform( const char *format, ...); +extern int error( const char *format, ...); +extern void fatalerror( const char *format, ...); +extern void fatalperror( const char *string); + +/* -------------------------------------------------------------------- + And one functions for printing to stdout. + -------------------------------------------------------------------- */ +extern int say( const char *format, ...); + +/* -------------------------------------------------------------------- + Allocate memory for it and return a pointer to a string made up of + the two argument strings. + -------------------------------------------------------------------- */ +extern char *mallocconcat( char *one, char *two); + +/* -------------------------------------------------------------------- + Convert a sample value to decibel. + -------------------------------------------------------------------- */ +extern double double2db( double value); + +/* -------------------------------------------------------------------- + Read 'size' samples from file 'in' and lose them. + -------------------------------------------------------------------- */ +extern void readawaysamples( FILE *in, size_t size); diff --git a/utils/muted.c b/utils/muted.c new file mode 100644 index 000000000..179d596a6 --- /dev/null +++ b/utils/muted.c @@ -0,0 +1,709 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 1999 - 2005, Digium, Inc. + * + * Mark Spencer <markster@digium.com> + * + * Updated for Mac OSX CoreAudio + * by Josh Roberson <josh@asteriasgi.com> + * + * 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 Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * + * \brief Mute Daemon + * + * \author Mark Spencer <markster@digium.com> + * + * Updated for Mac OSX CoreAudio + * \arg Josh Roberson <josh@asteriasgi.com> + * + * \note Specially written for Malcolm Davenport, but I think I'll use it too + * Connects to the Asterisk Manager Interface, AMI, and listens for events + * on certain devices. If a phone call is connected to one of the devices (phones) + * the local sound is muted to a lower volume during the call. + * + */ + +#ifdef __Darwin__ +#include <CoreAudio/AudioHardware.h> +#elif defined(__linux__) || defined(__FreeBSD__) +#include <sys/soundcard.h> +#endif +#include <stdio.h> +#include <errno.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <netdb.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +static char *config = "/etc/asterisk/muted.conf"; + +static char host[256] = ""; +static char user[256] = ""; +static char pass[256] = ""; +static int smoothfade = 0; +static int mutelevel = 20; +static int muted = 0; +static int needfork = 1; +static int debug = 0; +static int stepsize = 3; +#ifndef __Darwin__ +static int mixchan = SOUND_MIXER_VOLUME; +#endif + +struct subchannel { + char *name; + struct subchannel *next; +}; + +static struct channel { + char *tech; + char *location; + struct channel *next; + struct subchannel *subs; +} *channels; + +static void add_channel(char *tech, char *location) +{ + struct channel *chan; + chan = malloc(sizeof(struct channel)); + if (chan) { + memset(chan, 0, sizeof(struct channel)); + if (!(chan->tech = strdup(tech))) { + free(chan); + return; + } + if (!(chan->location = strdup(location))) { + free(chan->tech); + free(chan); + return; + } + chan->next = channels; + channels = chan; + } + +} + +static int load_config(void) +{ + FILE *f; + char buf[1024]; + char *val; + char *val2; + int lineno=0; + int x; + f = fopen(config, "r"); + if (!f) { + fprintf(stderr, "Unable to open config file '%s': %s\n", config, strerror(errno)); + return -1; + } + while(!feof(f)) { + if (!fgets(buf, sizeof(buf), f)) { + continue; + } + if (!feof(f)) { + lineno++; + val = strchr(buf, '#'); + if (val) *val = '\0'; + while(strlen(buf) && (buf[strlen(buf) - 1] < 33)) + buf[strlen(buf) - 1] = '\0'; + if (!strlen(buf)) + continue; + val = buf; + while(*val) { + if (*val < 33) + break; + val++; + } + if (*val) { + *val = '\0'; + val++; + while(*val && (*val < 33)) val++; + } + if (!strcasecmp(buf, "host")) { + if (val && strlen(val)) + strncpy(host, val, sizeof(host) - 1); + else + fprintf(stderr, "host needs an argument (the host) at line %d\n", lineno); + } else if (!strcasecmp(buf, "user")) { + if (val && strlen(val)) + strncpy(user, val, sizeof(user) - 1); + else + fprintf(stderr, "user needs an argument (the user) at line %d\n", lineno); + } else if (!strcasecmp(buf, "pass")) { + if (val && strlen(val)) + strncpy(pass, val, sizeof(pass) - 1); + else + fprintf(stderr, "pass needs an argument (the password) at line %d\n", lineno); + } else if (!strcasecmp(buf, "smoothfade")) { + smoothfade = 1; + } else if (!strcasecmp(buf, "mutelevel")) { + if (val && (sscanf(val, "%d", &x) == 1) && (x > -1) && (x < 101)) { + mutelevel = x; + } else + fprintf(stderr, "mutelevel must be a number from 0 (most muted) to 100 (no mute) at line %d\n", lineno); + } else if (!strcasecmp(buf, "channel")) { + if (val && strlen(val)) { + val2 = strchr(val, '/'); + if (val2) { + *val2 = '\0'; + val2++; + add_channel(val, val2); + } else + fprintf(stderr, "channel needs to be of the format Tech/Location at line %d\n", lineno); + } else + fprintf(stderr, "channel needs an argument (the channel) at line %d\n", lineno); + } else { + fprintf(stderr, "ignoring unknown keyword '%s'\n", buf); + } + } + } + fclose(f); + if (!strlen(host)) + fprintf(stderr, "no 'host' specification in config file\n"); + else if (!strlen(user)) + fprintf(stderr, "no 'user' specification in config file\n"); + else if (!channels) + fprintf(stderr, "no 'channel' specifications in config file\n"); + else + return 0; + return -1; +} + +static FILE *astf; +#ifndef __Darwin__ +static int mixfd; + +static int open_mixer(void) +{ + mixfd = open("/dev/mixer", O_RDWR); + if (mixfd < 0) { + fprintf(stderr, "Unable to open /dev/mixer: %s\n", strerror(errno)); + return -1; + } + return 0; +} +#endif /* !__Darwin */ + +/*! Connect to the asterisk manager interface */ +static int connect_asterisk(void) +{ + int sock; + struct hostent *hp; + char *ports; + int port = 5038; + struct sockaddr_in sin; + + ports = strchr(host, ':'); + if (ports) { + *ports = '\0'; + ports++; + if ((sscanf(ports, "%d", &port) != 1) || (port < 1) || (port > 65535)) { + fprintf(stderr, "'%s' is not a valid port number in the hostname\n", ports); + return -1; + } + } + hp = gethostbyname(host); + if (!hp) { + fprintf(stderr, "Can't find host '%s'\n", host); + return -1; + } + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) { + fprintf(stderr, "Failed to create socket: %s\n", strerror(errno)); + return -1; + } + sin.sin_family = AF_INET; + sin.sin_port = htons(port); + memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr)); + if (connect(sock, (struct sockaddr *)&sin, sizeof(sin))) { + fprintf(stderr, "Failed to connect to '%s' port '%d': %s\n", host, port, strerror(errno)); + close(sock); + return -1; + } + astf = fdopen(sock, "r+"); + if (!astf) { + fprintf(stderr, "fdopen failed: %s\n", strerror(errno)); + close(sock); + return -1; + } + return 0; +} + +static char *get_line(void) +{ + static char buf[1024]; + if (fgets(buf, sizeof(buf), astf)) { + while(strlen(buf) && (buf[strlen(buf) - 1] < 33)) + buf[strlen(buf) - 1] = '\0'; + return buf; + } else + return NULL; +} + +/*! Login to the asterisk manager interface */ +static int login_asterisk(void) +{ + char *welcome; + char *resp; + if (!(welcome = get_line())) { + fprintf(stderr, "disconnected (1)\n"); + return -1; + } + fprintf(astf, + "Action: Login\r\n" + "Username: %s\r\n" + "Secret: %s\r\n\r\n", user, pass); + if (!(welcome = get_line())) { + fprintf(stderr, "disconnected (2)\n"); + return -1; + } + if (strcasecmp(welcome, "Response: Success")) { + fprintf(stderr, "login failed ('%s')\n", welcome); + return -1; + } + /* Eat the rest of the event */ + while((resp = get_line()) && strlen(resp)); + if (!resp) { + fprintf(stderr, "disconnected (3)\n"); + return -1; + } + fprintf(astf, + "Action: Status\r\n\r\n"); + if (!(welcome = get_line())) { + fprintf(stderr, "disconnected (4)\n"); + return -1; + } + if (strcasecmp(welcome, "Response: Success")) { + fprintf(stderr, "status failed ('%s')\n", welcome); + return -1; + } + /* Eat the rest of the event */ + while((resp = get_line()) && strlen(resp)); + if (!resp) { + fprintf(stderr, "disconnected (5)\n"); + return -1; + } + return 0; +} + +static struct channel *find_channel(char *channel) +{ + char tmp[256] = ""; + char *s, *t; + struct channel *chan; + strncpy(tmp, channel, sizeof(tmp) - 1); + s = strchr(tmp, '/'); + if (s) { + *s = '\0'; + s++; + t = strrchr(s, '-'); + if (t) { + *t = '\0'; + } + if (debug) + printf("Searching for '%s' tech, '%s' location\n", tmp, s); + chan = channels; + while(chan) { + if (!strcasecmp(chan->tech, tmp) && !strcasecmp(chan->location, s)) { + if (debug) + printf("Found '%s'/'%s'\n", chan->tech, chan->location); + break; + } + chan = chan->next; + } + } else + chan = NULL; + return chan; +} + +#ifndef __Darwin__ +static int getvol(void) +{ + int vol; + + if (ioctl(mixfd, MIXER_READ(mixchan), &vol)) { +#else +static float getvol(void) +{ + float volumeL, volumeR, vol; + OSStatus err; + AudioDeviceID device; + UInt32 size; + UInt32 channels[2]; + + size = sizeof(device); + err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &size, &device); + size = sizeof(channels); + if (!err) + err = AudioDeviceGetProperty(device, 0, false, kAudioDevicePropertyPreferredChannelsForStereo, &size, &channels); + size = sizeof(vol); + if (!err) + err = AudioDeviceGetProperty(device, channels[0], false, kAudioDevicePropertyVolumeScalar, &size, &volumeL); + if (!err) + err = AudioDeviceGetProperty(device, channels[1], false, kAudioDevicePropertyVolumeScalar, &size, &volumeR); + if (!err) + vol = (volumeL < volumeR) ? volumeR : volumeL; + else { +#endif + fprintf(stderr, "Unable to read mixer volume: %s\n", strerror(errno)); + return -1; + } + return vol; +} + +#ifndef __Darwin__ +static int setvol(int vol) +#else +static int setvol(float vol) +#endif +{ +#ifndef __Darwin__ + if (ioctl(mixfd, MIXER_WRITE(mixchan), &vol)) { +#else + float volumeL = vol; + float volumeR = vol; + OSStatus err; + AudioDeviceID device; + UInt32 size; + UInt32 channels[2]; + + size = sizeof(device); + err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &size, &device); + size = sizeof(channels); + err = AudioDeviceGetProperty(device, 0, false, kAudioDevicePropertyPreferredChannelsForStereo, &size, &channels); + size = sizeof(vol); + if (!err) + err = AudioDeviceSetProperty(device, 0, channels[0], false, kAudioDevicePropertyVolumeScalar, size, &volumeL); + if (!err) + err = AudioDeviceSetProperty(device, 0, channels[1], false, kAudioDevicePropertyVolumeScalar, size, &volumeR); + if (err) { +#endif + + fprintf(stderr, "Unable to write mixer volume: %s\n", strerror(errno)); + return -1; + + } + return 0; +} + +#ifndef __Darwin__ +static int oldvol = 0; +static int mutevol = 0; +#else +static float oldvol = 0; +static float mutevol = 0; +#endif + +#ifndef __Darwin__ +static int mutedlevel(int orig, int mutelevel) +{ + int l = orig >> 8; + int r = orig & 0xff; + l = (float)(mutelevel) * (float)(l) / 100.0; + r = (float)(mutelevel) * (float)(r) / 100.0; + + return (l << 8) | r; +#else +static float mutedlevel(float orig, float mutelevel) +{ + float master = orig; + master = mutelevel * master / 100.0; + return master; +#endif + +} + +static void mute(void) +{ +#ifndef __Darwin__ + int vol; + int start; + int x; +#else + float vol; + float start = 1.0; + float x; +#endif + vol = getvol(); + oldvol = vol; + if (smoothfade) +#ifdef __Darwin__ + start = mutelevel; +#else + start = 100; + else + start = mutelevel; +#endif + for (x=start;x>=mutelevel;x-=stepsize) { + mutevol = mutedlevel(vol, x); + setvol(mutevol); + /* Wait 0.01 sec */ + usleep(10000); + } + mutevol = mutedlevel(vol, mutelevel); + setvol(mutevol); + if (debug) +#ifdef __Darwin__ + printf("Mute from '%f' to '%f'!\n", oldvol, mutevol); +#else + printf("Mute from '%04x' to '%04x'!\n", oldvol, mutevol); +#endif + muted = 1; +} + +static void unmute(void) +{ +#ifdef __Darwin__ + float vol; + float start; + float x; +#else + int vol; + int start; + int x; +#endif + vol = getvol(); + if (debug) +#ifdef __Darwin__ + printf("Unmute from '%f' (should be '%f') to '%f'!\n", vol, mutevol, oldvol); + mutevol = vol; + if (vol == mutevol) { +#else + printf("Unmute from '%04x' (should be '%04x') to '%04x'!\n", vol, mutevol, oldvol); + if ((int)vol == mutevol) { +#endif + if (smoothfade) + start = mutelevel; + else +#ifdef __Darwin__ + start = 1.0; +#else + start = 100; +#endif + for (x=start;x<100;x+=stepsize) { + mutevol = mutedlevel(oldvol, x); + setvol(mutevol); + /* Wait 0.01 sec */ + usleep(10000); + } + setvol(oldvol); + } else + printf("Whoops, it's already been changed!\n"); + muted = 0; +} + +static void check_mute(void) +{ + int offhook = 0; + struct channel *chan; + chan = channels; + while(chan) { + if (chan->subs) { + offhook++; + break; + } + chan = chan->next; + } + if (offhook && !muted) + mute(); + else if (!offhook && muted) + unmute(); +} + +static void delete_sub(struct channel *chan, char *name) +{ + struct subchannel *sub, *prev; + prev = NULL; + sub = chan->subs; + while(sub) { + if (!strcasecmp(sub->name, name)) { + if (prev) + prev->next = sub->next; + else + chan->subs = sub->next; + free(sub->name); + free(sub); + return; + } + prev = sub; + sub = sub->next; + } +} + +static void append_sub(struct channel *chan, char *name) +{ + struct subchannel *sub; + sub = chan->subs; + while(sub) { + if (!strcasecmp(sub->name, name)) + return; + sub = sub->next; + } + sub = malloc(sizeof(struct subchannel)); + if (sub) { + memset(sub, 0, sizeof(struct subchannel)); + if (!(sub->name = strdup(name))) { + free(sub); + return; + } + sub->next = chan->subs; + chan->subs = sub; + } +} + +static void hangup_chan(char *channel) +{ + struct channel *chan; + if (debug) + printf("Hangup '%s'\n", channel); + chan = find_channel(channel); + if (chan) + delete_sub(chan, channel); + check_mute(); +} + +static void offhook_chan(char *channel) +{ + struct channel *chan; + if (debug) + printf("Offhook '%s'\n", channel); + chan = find_channel(channel); + if (chan) + append_sub(chan, channel); + check_mute(); +} + +static int wait_event(void) +{ + char *resp; + char event[120]=""; + char channel[120]=""; + char oldname[120]=""; + char newname[120]=""; + + resp = get_line(); + if (!resp) { + fprintf(stderr, "disconnected (6)\n"); + return -1; + } + if (!strncasecmp(resp, "Event: ", strlen("Event: "))) { + strncpy(event, resp + strlen("Event: "), sizeof(event) - 1); + /* Consume the rest of the non-event */ + while((resp = get_line()) && strlen(resp)) { + if (!strncasecmp(resp, "Channel: ", strlen("Channel: "))) + strncpy(channel, resp + strlen("Channel: "), sizeof(channel) - 1); + if (!strncasecmp(resp, "Newname: ", strlen("Newname: "))) + strncpy(newname, resp + strlen("Newname: "), sizeof(newname) - 1); + if (!strncasecmp(resp, "Oldname: ", strlen("Oldname: "))) + strncpy(oldname, resp + strlen("Oldname: "), sizeof(oldname) - 1); + } + if (strlen(channel)) { + if (!strcasecmp(event, "Hangup")) + hangup_chan(channel); + else + offhook_chan(channel); + } + if (strlen(newname) && strlen(oldname)) { + if (!strcasecmp(event, "Rename")) { + hangup_chan(oldname); + offhook_chan(newname); + } + } + } else { + /* Consume the rest of the non-event */ + while((resp = get_line()) && strlen(resp)); + } + if (!resp) { + fprintf(stderr, "disconnected (7)\n"); + return -1; + } + return 0; +} + +static void usage(void) +{ + printf("Usage: muted [-f] [-d]\n" + " -f : Do not fork\n" + " -d : Debug (implies -f)\n"); +} + +int main(int argc, char *argv[]) +{ + int x; + while((x = getopt(argc, argv, "fhd")) > 0) { + switch(x) { + case 'd': + debug = 1; + needfork = 0; + break; + case 'f': + needfork = 0; + break; + case 'h': + /* Fall through */ + default: + usage(); + exit(1); + } + } + if (load_config()) + exit(1); +#ifndef __Darwin__ + if (open_mixer()) + exit(1); +#endif + if (connect_asterisk()) { +#ifndef __Darwin__ + close(mixfd); +#endif + exit(1); + } + if (login_asterisk()) { +#ifndef __Darwin__ + close(mixfd); +#endif + fclose(astf); + exit(1); + } + if (needfork) { +#ifndef HAVE_SBIN_LAUNCHD + if (daemon(0,0) < 0) { + fprintf(stderr, "daemon() failed: %s\n", strerror(errno)); + exit(1); + } +#else + fprintf(stderr, "Mac OS X detected. Use 'launchd -d muted -f' to launch.\n"); + exit(1); +#endif + } + for(;;) { + if (wait_event()) { + fclose(astf); + while(connect_asterisk()) { + sleep(5); + } + if (login_asterisk()) { + fclose(astf); + exit(1); + } + } + } + exit(0); +} diff --git a/utils/smsq.c b/utils/smsq.c new file mode 100644 index 000000000..4b52ce7ef --- /dev/null +++ b/utils/smsq.c @@ -0,0 +1,768 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2004 - 2005 + * + * SMS queuing application for use with asterisk app_sms + * by Adrian Kennard + * + * 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 Version 2. See the LICENSE file + * at the top of the source tree. + */ + +#include <stdio.h> +#include <popt.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <dirent.h> +#include <string.h> +#include <ctype.h> +#include <stdlib.h> +#include <unistd.h> +#include <time.h> +#include <errno.h> + +#include <asterisk/compat.h> +#ifdef SOLARIS +#define POPT_ARGFLAG_SHOW_DEFAULT 0x00800000 +#endif +#if !defined(POPT_ARGFLAG_SHOW_DEFAULT) +#define POPT_ARGFLAG_SHOW_DEFAULT 0x00800000 +#endif + + +/* reads next USC character from null terminated UTF-8 string and advanced pointer */ +/* for non valid UTF-8 sequences, returns character as is */ +/* Does not advance pointer for null termination */ +static int utf8decode (unsigned char **pp) +{ + unsigned char *p = *pp; + if (!*p) + return 0; /* null termination of string */ + (*pp)++; + if (*p < 0xC0) + return *p; /* ascii or continuation character */ + if (*p < 0xE0) + { + if (*p < 0xC2 || (p[1] & 0xC0) != 0x80) + return *p; /* not valid UTF-8 */ + (*pp)++; + return ((*p & 0x1F) << 6) + (p[1] & 0x3F); + } + if (*p < 0xF0) + { + if ((*p == 0xE0 && p[1] < 0xA0) || (p[1] & 0xC0) != 0x80 || (p[2] & 0xC0) != 0x80) + return *p; /* not valid UTF-8 */ + (*pp) += 2; + return ((*p & 0x0F) << 12) + ((p[1] & 0x3F) << 6) + (p[2] & 0x3F); + } + if (*p < 0xF8) + { + if ((*p == 0xF0 && p[1] < 0x90) || (p[1] & 0xC0) != 0x80 || (p[2] & 0xC0) != 0x80 || (p[3] & 0xC0) != 0x80) + return *p; /* not valid UTF-8 */ + (*pp) += 3; + return ((*p & 0x07) << 18) + ((p[1] & 0x3F) << 12) + ((p[2] & 0x3F) << 6) + (p[3] & 0x3F); + } + if (*p < 0xFC) + { + if ((*p == 0xF8 && p[1] < 0x88) || (p[1] & 0xC0) != 0x80 || (p[2] & 0xC0) != 0x80 || (p[3] & 0xC0) != 0x80 + || (p[4] & 0xC0) != 0x80) + return *p; /* not valid UTF-8 */ + (*pp) += 4; + return ((*p & 0x03) << 24) + ((p[1] & 0x3F) << 18) + ((p[2] & 0x3F) << 12) + ((p[3] & 0x3F) << 6) + (p[4] & 0x3F); + } + if (*p < 0xFE) + { + if ((*p == 0xFC && p[1] < 0x84) || (p[1] & 0xC0) != 0x80 || (p[2] & 0xC0) != 0x80 || (p[3] & 0xC0) != 0x80 + || (p[4] & 0xC0) != 0x80 || (p[5] & 0xC0) != 0x80) + return *p; /* not valid UTF-8 */ + (*pp) += 5; + return ((*p & 0x01) << 30) + ((p[1] & 0x3F) << 24) + ((p[2] & 0x3F) << 18) + ((p[3] & 0x3F) << 12) + ((p[4] & 0x3F) << 6) + + (p[5] & 0x3F); + } + return *p; /* not sensible */ +} + +/* check for any queued messages in specific queue (queue="" means any queue) */ +/* returns 0 if nothing queued, 1 if queued and outgoing set up OK, 2 of outgoing exists */ +static char txqcheck (char *dir, char *queue, char subaddress, char *channel, char *callerid, int wait, int delay, int retries, int concurrent) +{ + char ogname[100], + temp[100], + dirname[100], + *p=NULL; + FILE *f; + DIR *d; + int ql = strlen (queue), qfl = ql; + struct dirent *fn; + snprintf (dirname, sizeof(dirname), "sms/%s", dir); + d = opendir (dirname); + if (!d) + return 0; + while ((fn = readdir (d)) + && !(*fn->d_name != '.' + && ((!ql && (p = strchr (fn->d_name, '.'))) || (ql && !strncmp (fn->d_name, queue, ql) && fn->d_name[ql] == '.')))); + if (!fn) + { + closedir (d); + return 0; + } + if (!ql) + { /* not searching any specific queue, so use whatr we found as the queue */ + queue = fn->d_name; + qfl = ql = p - queue; + } + p = strchr (queue, '-'); + if (p && p < queue + ql) + { + ql = p - queue; + subaddress = p[1]; + } + snprintf (temp, sizeof(temp), "sms/.smsq-%d", (int)getpid ()); + f = fopen (temp, "w"); + if (!f) + { + perror (temp); + closedir (d); + return 0; + } + fprintf (f, "Channel: "); + if (!channel) + fprintf (f, "Local/%.*s\n", ql, queue); + else + { + p = strchr (channel, '/'); + if (!p) + p = channel; + p = strchr (p, 'X'); + if (p) + fprintf (f, "%.*s%c%s\n", (int)(p - channel), channel, subaddress, p + 1); + else + fprintf (f, "%s\n", channel); + } + fprintf (f, "Callerid: SMS <"); + if (!callerid) + fprintf (f, "%.*s", ql, queue); + else + { + p = strchr (callerid, 'X'); + if (p) + fprintf (f, "%.*s%c%s", (int)(p - callerid), callerid, subaddress, p + 1); + else + fprintf (f, "%s", callerid); + } + fprintf (f, ">\n"); + fprintf (f, "Application: SMS\n"); + fprintf (f, "Data: %.*s", qfl, queue); + if (dir[1] == 't') + fprintf (f, "|s"); + fprintf (f, "\nMaxRetries: %d\n", retries); + fprintf (f, "RetryTime: %d\n", delay); + fprintf (f, "WaitTime: %d\n", wait); + fclose (f); + closedir (d); + { + int try = 0; + while (try < concurrent) + { + try++; + snprintf(ogname, sizeof(ogname), "outgoing/smsq.%s.%s.%d", dir, queue, try); + if (!link (temp, ogname)) + { /* queued OK */ + unlink (temp); + return 1; + } + } + } + /* failed to create call queue */ + unlink (temp); + return 2; +} + +/* Process received queue entries and run through a process, setting environment variables */ +static void rxqcheck (char *dir, char *queue, char *process) +{ + char *p; + void *pp = &p; + char dirname[100], + temp[100]; + DIR *d; + int ql = strlen (queue); + struct dirent *fn; + snprintf(temp, sizeof(temp), "sms/.smsq-%d", (int)getpid ()); + snprintf(dirname, sizeof(dirname), "sms/%s", dir); + d = opendir (dirname); + if (!d) + return; + while ((fn = readdir (d))) + if ((*fn->d_name != '.' + && ((!ql && (p = strchr (fn->d_name, '.'))) || (ql && !strncmp (fn->d_name, queue, ql) && fn->d_name[ql] == '.')))) + { /* process file */ + char filename[1010]; + char line[1000]; + unsigned short ud[160]; + unsigned char udl = 0; + FILE *f; + snprintf (filename, sizeof(filename), "sms/%s/%s", dir, fn->d_name); + if (rename (filename, temp)) + continue; /* cannot access file */ + f = fopen (temp, "r"); + unlink (temp); + if (!f) + { + perror (temp); + continue; + } + unsetenv ("oa"); + unsetenv ("da"); + unsetenv ("scts"); + unsetenv ("pid"); + unsetenv ("dcs"); + unsetenv ("mr"); + unsetenv ("srr"); + unsetenv ("rp"); + unsetenv ("vp"); + unsetenv ("udh"); + unsetenv ("ud"); + unsetenv ("ude"); + unsetenv ("ud8"); + unsetenv ("ud16"); + unsetenv ("morx"); + unsetenv ("motx"); + unsetenv ("queue"); + if (*queue) + setenv ("queue", queue, 1); + setenv (dir, "", 1); + while (fgets (line, sizeof (line), f)) + { + for (p = line; *p && *p != '\n' && *p != '\r'; p++); + *p = 0; /* strip eoln */ + p = line; + if (!*p || *p == ';') + continue; /* blank line or comment, ignore */ + while (isalnum (*p)) + { + *p = tolower (*p); + p++; + } + while (isspace (*p)) + *p++ = 0; + if (*p == '=') + { /* = */ + *p++ = 0; + if (!strcmp (line, "oa") || !strcmp (line, "da") || !strcmp (line, "scts") || !strcmp (line, "pid") + || !strcmp (line, "dcs") || !strcmp (line, "mr") || !strcmp (line, "vp")) + setenv (line, p, 1); + else if ((!strcmp (line, "srr") || !strcmp (line, "rp")) && atoi (p)) + setenv (line, "", 1); + else if (!strcmp (line, "ud")) + { /* read the user data as UTF-8 */ + long v; + udl = 0; + while ((v = utf8decode (pp)) && udl < 160) + if (v && v <= 0xFFFF) + ud[udl++] = v; + } + } else if (*p == '#') + { + *p++ = 0; + if (*p == '#') + { /* ## */ + p++; + if (!strcmp (line, "udh")) + setenv (line, p, 1); + else if (!strcmp (line, "ud")) + { /* read user data UCS-2 */ + udl = 0; + while (*p && udl < 160) + { + if (isxdigit (*p) && isxdigit (p[1]) && isxdigit (p[2]) && isxdigit (p[3])) + { + ud[udl++] = + (((isalpha (*p) ? 9 : 0) + (*p & 0xF)) << 12) + + (((isalpha (p[1]) ? 9 : 0) + (p[1] & 0xF)) << 8) + + (((isalpha (p[2]) ? 9 : 0) + (p[2] & 0xF)) << 4) + ((isalpha (p[3]) ? 9 : 0) + (p[3] & 0xF)); + p += 4; + } else + break; + } + } + } else + { /* # */ + if (!strcmp (line, "ud")) + { /* read user data UCS-1 */ + udl = 0; + while (*p && udl < 160) + { + if (isxdigit (*p) && isxdigit (p[1])) + { + ud[udl++] = (((isalpha (*p) ? 9 : 0) + (*p & 0xF)) << 4) + ((isalpha (p[1]) ? 9 : 0) + (p[1] & 0xF)); + p += 2; + } else + break; + } + } + } + } + } + fclose (f); + /* set up user data variables */ + { + char temp[481]; + int n, + p; + for (n = 0, p = 0; p < udl; p++) + { + unsigned short v = ud[p]; + if (v) + { + if (v < 0x80) + temp[n++] = v; + else if (v < 0x800) + { + temp[n++] = (0xC0 + (v >> 6)); + temp[n++] = (0x80 + (v & 0x3F)); + } else + { + temp[n++] = (0xE0 + (v >> 12)); + temp[n++] = (0x80 + ((v >> 6) & 0x3F)); + temp[n++] = (0x80 + (v & 0x3F)); + } + } + } + temp[n] = 0; + setenv ("ud", temp, 1); + for (n = 0, p = 0; p < udl; p++) + { + unsigned short v = ud[p]; + if (v < ' ' || v == '\\') + { + temp[n++] = '\\'; + if (v == '\\') + temp[n++] = '\\'; + else if (v == '\n') + temp[n++] = 'n'; + else if (v == '\r') + temp[n++] = 'r'; + else if (v == '\t') + temp[n++] = 't'; + else if (v == '\f') + temp[n++] = 'f'; + else + { + temp[n++] = '0' + (v >> 6); + temp[n++] = '0' + ((v >> 3) & 7); + temp[n++] = '0' + (v & 7); + } + } else if (v < 0x80) + temp[n++] = v; + else if (v < 0x800) + { + temp[n++] = (0xC0 + (v >> 6)); + temp[n++] = (0x80 + (v & 0x3F)); + } else + { + temp[n++] = (0xE0 + (v >> 12)); + temp[n++] = (0x80 + ((v >> 6) & 0x3F)); + temp[n++] = (0x80 + (v & 0x3F)); + } + } + temp[n] = 0; + setenv ("ude", temp, 1); + for (p = 0; p < udl && ud[p] < 0x100; p++); + if (p == udl) + { + for (n = 0, p = 0; p < udl; p++) + { + sprintf (temp + n, "%02X", ud[p]); + n += 2; + } + setenv ("ud8", temp, 1); + } + for (n = 0, p = 0; p < udl; p++) + { + sprintf (temp + n, "%04X", ud[p]); + n += 4; + } + setenv ("ud16", temp, 1); + } + /* run the command */ + if (system (process) == -1) { + fprintf(stderr, "Failed to fork process '%s'\n", process); + } + } + closedir (d); +} + +/* Main app */ +int +main (int argc, const char *argv[]) +{ + char c; + int mt = 0, + mo = 0, + tx = 0, + rx = 0, + nodial = 0, + nowait = 0, + concurrent = 1, + motxwait = 10, + motxdelay = 1, + motxretries = 10, + mttxwait = 10, + mttxdelay = 30, + mttxretries = 100, + mr = -1, + pid = -1, + dcs = -1, + srr = 0, + rp = 0, + vp = 0, + udl = 0, + utf8 = 0, + ucs1 = 0, + ucs2 = 0; + unsigned short ud[160]; + unsigned char *uds = 0, + *udh = 0; + char *da = 0, + *oa = 0, + *queue = "", + *udfile = 0, + *process = 0, + *spooldir = "/var/spool/asterisk", + *motxchannel = "Local/1709400X", + *motxcallerid = 0, + *mttxchannel = 0, + *mttxcallerid = "080058752X0", + *defaultsubaddress = "9", + subaddress = 0, + *scts = 0; + poptContext optCon; /* context for parsing command-line options */ + const struct poptOption optionsTable[] = { + {"queue", 'q', POPT_ARG_STRING | POPT_ARGFLAG_SHOW_DEFAULT, &queue, 0, "Queue [inc sub address]", "number[-X]"}, + {"da", 'd', POPT_ARG_STRING, &da, 0, "Destination address", "number"}, + {"oa", 'o', POPT_ARG_STRING, &oa, 0, "Origination address", "number"}, + {"ud", 'm', POPT_ARG_STRING, &uds, 0, "Message", "text"}, + {"ud-file", 'f', POPT_ARG_STRING, &udfile, 0, "Message file", "filename"}, + {"UTF-8", 0, POPT_ARG_NONE, &utf8, 0, "File treated as null terminated UTF-8 (default)", 0}, + {"UCS-1", 0, POPT_ARG_NONE, &ucs1, 0, "File treated as UCS-1", 0}, + {"UCS-2", 0, POPT_ARG_NONE, &ucs2, 0, "File treated as UCS-2", 0}, + {"mt", 't', POPT_ARG_NONE, &mt, 0, "Mobile Terminated", 0}, + {"mo", 0, POPT_ARG_NONE, &mo, 0, "Mobile Originated", 0}, + {"tx", 0, POPT_ARG_NONE, &tx, 0, "Send message", 0}, + {"rx", 'r', POPT_ARG_NONE, &rx, 0, "Queue for receipt", 0}, + {"process", 'e', POPT_ARG_STRING, &process, 0, "Rx queue process command", "command"}, + {"no-dial", 'x', POPT_ARG_NONE, &nodial, 0, "Do not dial", 0}, + {"no-wait", 0, POPT_ARG_NONE, &nowait, 0, "Do not wait if already calling", 0}, + {"concurrent", 0, POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, &concurrent, 0, "Number of concurrent calls to allow", "n"}, + {"motx-channel", 0, POPT_ARG_STRING | POPT_ARGFLAG_SHOW_DEFAULT, &motxchannel, 0, "Channel for motx calls", "channel"}, + {"motx-callerid", 0, POPT_ARG_STRING, &motxcallerid, 0, + "Caller ID for motx calls (default is queue name without sub address)", "number"}, + {"motx-wait", 0, POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, &motxwait, 0, "Time to wait for motx call to answer", + "seconds"}, + {"motx-delay", 0, POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, &motxdelay, 0, "Time between motx call retries", "seconds"}, + {"motx-retries", 0, POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, &motxretries, 0, "Number of retries for motx call", "n"}, + {"mttx-channel", 0, POPT_ARG_STRING, &mttxchannel, 0, + "Channel for mttx calls (default is Local/ and queue name without sub address)", "channel"}, + {"mttx-callerid", 0, POPT_ARG_STRING | POPT_ARGFLAG_SHOW_DEFAULT, &mttxcallerid, 0, + "Caller ID for mttx calls (default is queue name without sub address)", "number"}, + {"mttx-wait", 0, POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, &mttxwait, 0, "Time to wait for mttx call to answer", + "seconds"}, + {"mttx-delay", 0, POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, &mttxdelay, 0, "Time between mttx call retries", "seconds"}, + {"mttx-retries", 0, POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, &mttxretries, 0, "Number of retries for mttx call", "n"}, + {"mr", 'n', POPT_ARG_INT, &mr, 0, "Message reference", "n"}, + {"pid", 'p', POPT_ARG_INT, &pid, 0, "Protocol ID", "n"}, + {"dcs", 'c', POPT_ARG_INT, &dcs, 0, "Data Coding Scheme", "n"}, + {"udh", 0, POPT_ARG_STRING, &udh, 0, "User data header", "hex"}, + {"srr", 0, POPT_ARG_NONE, &srr, 0, "Status Report Request", 0}, + {"rp", 0, POPT_ARG_NONE, &rp, 0, "Return Path request", 0}, + {"v", 0, POPT_ARG_INT, &vp, 0, "Validity Period", "seconds"}, + {"scts", 0, POPT_ARG_STRING, &scts, 0, "Timestamp", "YYYY-MM-SSTHH:MM:SS"}, + {"default-sub-address", 0, POPT_ARG_STRING | POPT_ARGFLAG_SHOW_DEFAULT, &defaultsubaddress, 0, "Default sub address", "X"}, + {"spool-dir", 0, POPT_ARG_STRING | POPT_ARGFLAG_SHOW_DEFAULT, &spooldir, 0, "Asterisk spool dir", "dirname"}, + POPT_AUTOHELP {NULL, 0, 0, NULL, 0} + }; + + optCon = poptGetContext (NULL, argc, argv, optionsTable, 0); + poptSetOtherOptionHelp (optCon, "<oa/da> <message>"); + + /* Now do options processing, get portname */ + if ((c = poptGetNextOpt (optCon)) < -1) + { + /* an error occurred during option processing */ + fprintf (stderr, "%s: %s\n", poptBadOption (optCon, POPT_BADOPTION_NOALIAS), poptStrerror (c)); + return 1; + } + if (!ucs1 && !ucs2) + utf8 = 1; + if (utf8 + ucs1 + ucs2 > 1) + { + fprintf (stderr, "Pick one of UTF-8, UCS-1 or UCS-2 only\n"); + return 1; + } + if (!udfile && (ucs1 || ucs2)) + { + fprintf (stderr, "Command line arguments always treated as UTF-8\n"); + return 1; + } + /* if (!where && poptPeekArg (optCon)) where = (char *) poptGetArg (optCon); */ + if (!mt && !mo && process) + mt = 1; + if (!mt && !mo && oa) + mt = 1; + if (!mt) + mo = 1; + if (mt && mo) + { + fprintf (stderr, "Cannot be --mt and --mo\n"); + return 1; + } + if (!rx && !tx && process) + rx = 1; + if (!rx) + tx = 1; + if (tx && rx) + { + fprintf (stderr, "Cannot be --tx and --rx\n"); + return 1; + } + if (rx) + nodial = 1; + if (uds && udfile) + { + fprintf (stderr, "Cannot have --ud and --ud-file\n"); + return 1; + } + if (mo && !da && poptPeekArg (optCon)) + da = (char *) poptGetArg (optCon); + if (mt && !oa && poptPeekArg (optCon)) + oa = (char *) poptGetArg (optCon); + if (tx && oa && mo) + { + fprintf (stderr, "--oa makes no sense with --mo as CLI is used (i.e. queue name)\n"); + return 1; + } + if (tx && da && mt) + { + fprintf (stderr, "--da makes no sense with --mt as called number is used (i.e. queue name)\n"); + return 1; + } + if (da && strlen (da) > 20) + { + fprintf (stderr, "--da too long\n"); + return 1; + } + if (oa && strlen (oa) > 20) + { + fprintf (stderr, "--oa too long\n"); + return 1; + } + if (queue && strlen (queue) > 20) + { + fprintf (stderr, "--queue name too long\n"); + return 1; + } + if (mo && scts) + { + fprintf (stderr, "scts is set my service centre\n"); + return 1; + } + if (uds) + { /* simple user data command line option in \UTF-8 */ + while (udl < 160 && *uds) + { + int v = utf8decode (&uds); + if (v > 0xFFFF) + { + fprintf (stderr, "Invalid character U+%X at %d\n", v, udl); + return 1; + } + ud[udl++] = v; + } + } + if (!uds && !udfile && poptPeekArg (optCon)) + { /* multiple command line arguments in UTF-8 */ + while (poptPeekArg (optCon) && udl < 160) + { + unsigned char *a = (unsigned char *) poptGetArg (optCon); + if (udl && udl < 160) + ud[udl++] = ' '; /* space between arguments */ + while (udl < 160 && *a) + { + int v = utf8decode (&a); + if (v > 0xFFFF) + { + fprintf (stderr, "Invalid character U+%X at %d\n", v, udl); + return 1; + } + ud[udl++] = v; + } + } + } + if (poptPeekArg (optCon)) + { + fprintf (stderr, "Unknown argument %s\n", poptGetArg (optCon)); + return 1; + } + if (udfile) + { /* get message from file */ + unsigned char dat[1204], + *p = dat, + *e; + int f, + n; + if (*udfile) + f = open (udfile, O_RDONLY); + else + f = fileno (stdin); + if (f < 0) + { + perror (udfile); + return 1; + } + n = read (f, dat, sizeof (dat)); + if (n < 0) + { + perror (udfile); + return 1; + } + if (*udfile) + close (f); + e = dat + n; + if (utf8) + { /* UTF-8 */ + while (p < e && udl < 160 && *p) + ud[udl++] = utf8decode (&p); + } else if (ucs1) + { /* UCS-1 */ + while (p < e && udl < 160) + ud[udl++] = *p++; + } else + { /* UCS-2 */ + while (p + 1 < e && udl < 160) + { + ud[udl++] = (*p << 8) + p[1]; + p += 2; + } + } + } + if (queue) + { + char *d = strrchr (queue, '-'); + if (d && d[1]) + subaddress = d[1]; + else + subaddress = *defaultsubaddress; + } + + if (chdir (spooldir)) + { + perror (spooldir); + return 1; + } + + if (oa || da) + { /* send message */ + char temp[100], + queuename[100], + *dir = (mo ? rx ? "sms/morx" : "sms/motx" : rx ? "sms/mtrx" : "sms/mttx"); + FILE *f; + snprintf (temp, sizeof(temp), "sms/.smsq-%d", (int)getpid ()); + mkdir ("sms", 0777); /* ensure directory exists */ + mkdir (dir, 0777); /* ensure directory exists */ + snprintf (queuename, sizeof(queuename), "%s/%s.%ld-%d", dir, *queue ? queue : "0", (long)time (0), (int)getpid ()); + f = fopen (temp, "w"); + if (!f) + { + perror (temp); + return 1; + } + if (oa) + fprintf (f, "oa=%s\n", oa); + if (da) + fprintf (f, "da=%s\n", da); + if (scts) + fprintf (f, "scts=%s\n", scts); + if (pid >= 0) + fprintf (f, "pid=%d\n", pid); + if (dcs >= 0) + fprintf (f, "dcs=%d\n", dcs); + if (mr >= 0) + fprintf (f, "mr=%d\n", mr); + if (srr) + fprintf (f, "srr=1\n"); + if (rp) + fprintf (f, "rp=1\n"); + if (udh) + fprintf (f, "udh#%s\n", udh); + if (vp > 0) + fprintf (f, "vp=%d\n", vp); + if (udl) + { + int p; + for (p = 0; p < udl && ud[p] < 0x100; p++); + if (p == udl) + { + for (p = 0; p < udl && ud[p] < 0x80 && ud[p] >= 0x20; p++); + if (p == udl) + { /* use text */ + fprintf (f, "ud="); + for (p = 0; p < udl; p++) + fputc (ud[p], f); + } else + { /* use one byte hex */ + fprintf (f, "ud#"); + for (p = 0; p < udl; p++) + fprintf (f, "%02X", ud[p]); + } + } else + { /* use two byte hex */ + fprintf (f, "ud##"); + for (p = 0; p < udl; p++) + fprintf (f, "%04X", ud[p]); + } + fprintf (f, "\n"); + } + fclose (f); + if (rename (temp, queuename)) + { + perror (queuename); + unlink (temp); + return 1; + } + } + + if (!nodial && tx && !process) + { /* dial to send messages */ + char ret=0, + try = 3; + if (nowait) + try = 1; + while (try--) + { + if (mo) + ret = txqcheck ("motx", queue, subaddress, motxchannel, motxcallerid, motxwait, motxdelay, motxretries, concurrent); + else + ret = txqcheck ("mttx", queue, subaddress, mttxchannel, mttxcallerid, mttxwait, mttxdelay, mttxretries, concurrent); + if (ret < 2) + break; /* sent, or queued OK */ + if (try) + sleep (1); + } + if (ret == 2 && !nowait) + fprintf (stderr, "No call scheduled as already sending\n"); + } + if (process) + rxqcheck (mo ? rx ? "morx" : "motx" : rx ? "mtrx" : "mttx", queue, process); + + return 0; +} diff --git a/utils/stereorize.c b/utils/stereorize.c new file mode 100644 index 000000000..c8428320d --- /dev/null +++ b/utils/stereorize.c @@ -0,0 +1,159 @@ +/**************************************************************************** + * + * Programs for processing sound files in raw- or WAV-format. + * -- Merge two mono WAV-files to one stereo WAV-file. + * + * Name: stereorize.c + * Version: 1.1 + * Author: Mark Roberts <mark@manumark.de> + * Michael Labuschke <michael@labuschke.de> + * + ****************************************************************************/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <time.h> +#include <assert.h> +#include "frame.h" + +static char *Version = "stereorize 1.1, November 5th 2000"; +static char *Usage = +"Usage: stereorize [options] infile-left infile-right outfile\n\n" + +"Example:\n" +" stereorize left.wav right.wav stereo.wav -h\n\n" + +"Creates stereo.wav (with WAV-header, option -h) from data in mono files\n" +"left.wav and right.wav.\n" +; + +int main( int argcount, char *args[]) +{ + int i, k[2], maxk, stdin_in_use=FALSE; + short *leftsample, *rightsample, *stereosample; + FILE *channel[2]; + char *filename[2], *tempname; + + version = Version; + usage = Usage; + + channel[0] = NULL; + channel[1] = NULL; + + parseargs( argcount, args, NOFILES | NOCOMPLAIN); + + for (i = 0; i < 2; i++) + { + filename[i] = parsefilearg( argcount, args); + if (filename[i] == NULL) + argerrornum( NULL, ME_NOTENOUGHFILES); + if (strcmp (filename[i], "-") == 0) + { + if (stdin_in_use) + argerrortxt( filename[i] + 1, + "Cannot use <stdin> for both input files"); + filename[i] = "<stdin>"; + channel[i] = stdin; + stdin_in_use = TRUE; + } + else + { + channel[i] = fopen(filename[i], "rb"); + } + if (channel[i] == NULL) + fatalerror( "Error opening input file '%s': %s\n", filename[i],strerror(errno)); + else + inform("Using file '%s' as input\n", filename[i]); + } + for (i = 0; i < 2; i++) + { + assert ( channel[i] != NULL); + readwavheader( channel[i]); + if (iswav && channels != 1) + inform("Warning: '%s' is no mono file\n", filename[i]); + } + + outfilename = parsefilearg( argcount, args); + if (outfilename == NULL) argerrornum( NULL, ME_NOOUTFILE); + if (strcmp (outfilename, "-") == 0) + { + outfilename = "<stdout>"; + out = stdout; + } + else + { + out = fopen(outfilename, "wb"); + } + if (out == NULL) + fatalerror( "Error opening output file '%s': %s\n", outfilename,strerror(errno)); + else + inform("Using file '%s' as output\n", outfilename); + + if ((tempname = parsefilearg( argcount, args)) != NULL) + argerrornum( tempname, ME_TOOMANYFILES); + + checknoargs(argcount, args); /* Check that no arguments are left */ + + leftsample = malloc( sizeof(*leftsample) * BUFFSIZE); + rightsample = malloc( sizeof(*leftsample) * BUFFSIZE); + stereosample = malloc( sizeof(*leftsample) * 2 * BUFFSIZE); + if (leftsample == NULL || rightsample == NULL || stereosample == NULL) + fatalperror (""); + + channels = 2; /* Output files are stereo */ + if (wavout) + { + if ((strcmp(outfilename,"<stdout>")!=0) && (fseek( out, 0, SEEK_SET) != 0)) + fatalerror("Couldn't navigate output file '%s': %s\n",outfilename, strerror(errno)); + makewavheader(); + } + + startstopwatch(); + while (TRUE) + { + maxk = 0; + for (i = 0; i < 2; i++) + { + k[i] = fread(i==0? leftsample : rightsample, + sizeof(*leftsample), + BUFFSIZE, + channel[i]); + if (k[i] == -1) + fatalerror("Error reading file '%s': %s\n", filename[i],strerror(errno)); + if (k[i] > maxk) + maxk = k[i]; + } + if (maxk == 0) + myexit (0); + + /*-------------------------------------------------* + * First the left channel as far as it goes ... * + *-------------------------------------------------*/ + for (i = 0; i < k[0]; i++) + stereosample[2 * i] = leftsample[i]; + /*-------------------------------------------------* + * ... and fill up till the end of this buffer. * + *-------------------------------------------------*/ + for (; i < maxk; i++) + stereosample[2 * i] = 0; + + /*-------------------------------------------------* + * Next the right channel as far as it goes ... * + *-------------------------------------------------*/ + for (i = 0; i < k[1]; i++) + stereosample[2 * i + 1] = rightsample[i]; + /*-------------------------------------------------* + * ... and fill up till the end of this buffer. * + *-------------------------------------------------*/ + for (; i < maxk; i++) + stereosample[2 * i + 1] = 0; + + if (!fwrite(stereosample, sizeof(*leftsample), 2 * maxk, out)) { + fatalerror("Error writing to file '%s': %s\n", + outfilename, strerror(errno)); + } + } + /* That was an endless loop. This point is never reached. */ +} diff --git a/utils/streamplayer.c b/utils/streamplayer.c new file mode 100644 index 000000000..ebb12e54b --- /dev/null +++ b/utils/streamplayer.c @@ -0,0 +1,124 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 1999 - 2005, Digium, Inc. + * + * Russell Bryant <russell@digium.com> + * + * 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 Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! + * \file + * \author Russell Bryant <russell@digium.com> + * + * \brief A utility for reading from a raw TCP stream + * + * This application is intended for use when a raw TCP stream is desired to be + * used as a music on hold source for Asterisk. Some devices are capable of + * taking some kind of audio input and provide it as a raw TCP stream over the + * network, which is what inspired someone to fund this to be written. + * However, it would certainly be possible to write your own server application + * to provide music over a TCP stream from a centralized location. + * + * This application is quite simple. It just reads the data from the TCP + * stream and dumps it straight to stdout. Due to the way Asterisk handles + * music on hold sources, this application checks to make sure writing + * to stdout will not be a blocking operation before doing so. If so, the data + * is just thrown away. This ensures that the stream will continue to be + * serviced, even if Asterisk is not currently using the source. + * + * \todo Update this application to be able to connect to a stream via HTTP, + * since that is the #1 most requested feature, and it would be quite useful. + * A lot of people think that is what this is for and email me when it does + * not work. :) + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <netdb.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__Darwin__) || defined(__CYGWIN__) +#include <netinet/in.h> +#endif +#include <sys/time.h> + + +int main(int argc, char *argv[]) +{ + struct sockaddr_in sin; + struct hostent *hp; + int s; + int res; + char buf[2048]; + fd_set wfds; + struct timeval tv; + + if (argc != 3) { + fprintf(stderr, "streamplayer -- A utility for reading from a raw TCP stream.\n"); + fprintf(stderr, "Written for use with Asterisk (http://www.asterisk.org)\n"); + fprintf(stderr, "Copyright (C) 2005 -- Russell Bryant -- Digium, Inc.\n\n"); + fprintf(stderr, "Usage: ./streamplayer <ip> <port>\n"); + exit(1); + } + + hp = gethostbyname(argv[1]); + if (!hp) { + fprintf(stderr, "Unable to lookup IP for host '%s'\n", argv[1]); + exit(1); + } + + memset(&sin, 0, sizeof(sin)); + + sin.sin_family = AF_INET; + sin.sin_port = htons(atoi(argv[2])); + memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr)); + + s = socket(AF_INET, SOCK_STREAM, 0); + + if (s < 0) { + fprintf(stderr, "Unable to allocate socket!\n"); + exit(1); + } + + res = connect(s, (struct sockaddr *)&sin, sizeof(sin)); + + if (res) { + fprintf(stderr, "Unable to connect to host!\n"); + close(s); + exit(1); + } + + while (1) { + res = read(s, buf, sizeof(buf)); + + if (res < 1) + break; + + memset(&tv, 0, sizeof(tv)); + FD_ZERO(&wfds); + FD_SET(1, &wfds); + + select(2, NULL, &wfds, NULL, &tv); + + if (FD_ISSET(1, &wfds)) { + if (write(1, buf, res) < 1) { + break; + } + } + } + + close(s); + exit(res); +} |