aboutsummaryrefslogtreecommitdiffstats
path: root/trunk/main
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/main')
-rw-r--r--trunk/main/Makefile174
-rw-r--r--trunk/main/abstract_jb.c774
-rw-r--r--trunk/main/acl.c357
-rw-r--r--trunk/main/adsistub.c77
-rw-r--r--trunk/main/aescrypt.c321
-rw-r--r--trunk/main/aeskey.c473
-rw-r--r--trunk/main/aesopt.h1029
-rw-r--r--trunk/main/aestab.c236
-rw-r--r--trunk/main/alaw.c210
-rw-r--r--trunk/main/app.c1739
-rw-r--r--trunk/main/ast_expr2.c3448
-rw-r--r--trunk/main/ast_expr2.fl444
-rw-r--r--trunk/main/ast_expr2.h115
-rw-r--r--trunk/main/ast_expr2.y1619
-rw-r--r--trunk/main/ast_expr2f.c2515
-rw-r--r--trunk/main/asterisk.c3267
-rw-r--r--trunk/main/astmm.c479
-rw-r--r--trunk/main/astobj2.c732
-rw-r--r--trunk/main/audiohook.c693
-rw-r--r--trunk/main/autoservice.c252
-rw-r--r--trunk/main/buildinfo.c33
-rw-r--r--trunk/main/callerid.c1115
-rw-r--r--trunk/main/cdr.c1456
-rw-r--r--trunk/main/channel.c4843
-rw-r--r--trunk/main/chanvars.c82
-rw-r--r--trunk/main/cli.c1918
-rw-r--r--trunk/main/config.c2281
-rw-r--r--trunk/main/cryptostub.c67
-rw-r--r--trunk/main/cygload.c39
-rw-r--r--trunk/main/db.c671
-rw-r--r--trunk/main/db1-ast/Makefile72
-rw-r--r--trunk/main/db1-ast/btree/bt_close.c182
-rw-r--r--trunk/main/db1-ast/btree/bt_conv.c221
-rw-r--r--trunk/main/db1-ast/btree/bt_debug.c329
-rw-r--r--trunk/main/db1-ast/btree/bt_delete.c657
-rw-r--r--trunk/main/db1-ast/btree/bt_get.c105
-rw-r--r--trunk/main/db1-ast/btree/bt_open.c458
-rw-r--r--trunk/main/db1-ast/btree/bt_overflow.c228
-rw-r--r--trunk/main/db1-ast/btree/bt_page.c100
-rw-r--r--trunk/main/db1-ast/btree/bt_put.c321
-rw-r--r--trunk/main/db1-ast/btree/bt_search.c213
-rw-r--r--trunk/main/db1-ast/btree/bt_seq.c460
-rw-r--r--trunk/main/db1-ast/btree/bt_split.c829
-rw-r--r--trunk/main/db1-ast/btree/bt_utils.c260
-rw-r--r--trunk/main/db1-ast/btree/btree.h391
-rw-r--r--trunk/main/db1-ast/btree/extern.h70
-rw-r--r--trunk/main/db1-ast/db/db.c103
-rw-r--r--trunk/main/db1-ast/hash/README72
-rw-r--r--trunk/main/db1-ast/hash/extern.h65
-rw-r--r--trunk/main/db1-ast/hash/hash.c999
-rw-r--r--trunk/main/db1-ast/hash/hash.h293
-rw-r--r--trunk/main/db1-ast/hash/hash_bigkey.c668
-rw-r--r--trunk/main/db1-ast/hash/hash_buf.c355
-rw-r--r--trunk/main/db1-ast/hash/hash_func.c225
-rw-r--r--trunk/main/db1-ast/hash/hash_log2.c56
-rw-r--r--trunk/main/db1-ast/hash/hash_page.c944
-rw-r--r--trunk/main/db1-ast/hash/hsearch.c107
-rw-r--r--trunk/main/db1-ast/hash/ndbm.c235
-rw-r--r--trunk/main/db1-ast/hash/page.h92
-rw-r--r--trunk/main/db1-ast/hash/search.h51
-rw-r--r--trunk/main/db1-ast/include/circ-queue.h131
-rw-r--r--trunk/main/db1-ast/include/compat.h49
-rw-r--r--trunk/main/db1-ast/include/db.h250
-rw-r--r--trunk/main/db1-ast/include/mpool.h115
-rw-r--r--trunk/main/db1-ast/include/ndbm.h79
-rw-r--r--trunk/main/db1-ast/libdb.map11
-rw-r--r--trunk/main/db1-ast/mpool/README7
-rw-r--r--trunk/main/db1-ast/mpool/mpool.c498
-rw-r--r--trunk/main/db1-ast/recno/extern.h54
-rw-r--r--trunk/main/db1-ast/recno/rec_close.c183
-rw-r--r--trunk/main/db1-ast/recno/rec_delete.c197
-rw-r--r--trunk/main/db1-ast/recno/rec_get.c311
-rw-r--r--trunk/main/db1-ast/recno/rec_open.c241
-rw-r--r--trunk/main/db1-ast/recno/rec_put.c280
-rw-r--r--trunk/main/db1-ast/recno/rec_search.c126
-rw-r--r--trunk/main/db1-ast/recno/rec_seq.c131
-rw-r--r--trunk/main/db1-ast/recno/rec_utils.c122
-rw-r--r--trunk/main/db1-ast/recno/recno.h39
-rw-r--r--trunk/main/devicestate.c553
-rw-r--r--trunk/main/dial.c1014
-rw-r--r--trunk/main/dlfcn.c1309
-rw-r--r--trunk/main/dns.c299
-rw-r--r--trunk/main/dnsmgr.c425
-rw-r--r--trunk/main/dsp.c1349
-rw-r--r--trunk/main/ecdisa.h15
-rw-r--r--trunk/main/editline/CHANGES42
-rw-r--r--trunk/main/editline/INSTALL64
-rw-r--r--trunk/main/editline/Makefile.in234
-rw-r--r--trunk/main/editline/PLATFORMS13
-rw-r--r--trunk/main/editline/README11
-rw-r--r--trunk/main/editline/TEST/test.c268
-rw-r--r--trunk/main/editline/chared.c695
-rw-r--r--trunk/main/editline/chared.h159
-rw-r--r--trunk/main/editline/common.c951
-rwxr-xr-xtrunk/main/editline/config.guess1449
-rw-r--r--trunk/main/editline/config.h.in21
-rwxr-xr-xtrunk/main/editline/config.sub1412
-rwxr-xr-xtrunk/main/editline/configure2309
-rw-r--r--trunk/main/editline/configure.in274
-rw-r--r--trunk/main/editline/editline.3646
-rw-r--r--trunk/main/editline/editrc.5491
-rw-r--r--trunk/main/editline/el.c509
-rw-r--r--trunk/main/editline/el.h145
-rw-r--r--trunk/main/editline/emacs.c488
-rw-r--r--trunk/main/editline/hist.c197
-rw-r--r--trunk/main/editline/hist.h80
-rw-r--r--trunk/main/editline/histedit.h197
-rw-r--r--trunk/main/editline/history.c875
-rwxr-xr-xtrunk/main/editline/install-sh250
-rw-r--r--trunk/main/editline/key.c687
-rw-r--r--trunk/main/editline/key.h79
-rw-r--r--trunk/main/editline/makelist254
-rw-r--r--trunk/main/editline/map.c1418
-rw-r--r--trunk/main/editline/map.h79
-rw-r--r--trunk/main/editline/np/fgetln.c88
-rw-r--r--trunk/main/editline/np/strlcat.c75
-rw-r--r--trunk/main/editline/np/strlcpy.c75
-rw-r--r--trunk/main/editline/np/unvis.c322
-rw-r--r--trunk/main/editline/np/vis.c347
-rw-r--r--trunk/main/editline/np/vis.h96
-rw-r--r--trunk/main/editline/parse.c259
-rw-r--r--trunk/main/editline/parse.h52
-rw-r--r--trunk/main/editline/prompt.c174
-rw-r--r--trunk/main/editline/prompt.h62
-rw-r--r--trunk/main/editline/read.c555
-rw-r--r--trunk/main/editline/read.h55
-rw-r--r--trunk/main/editline/readline.c1665
-rw-r--r--trunk/main/editline/readline/readline.h118
-rw-r--r--trunk/main/editline/refresh.c1104
-rw-r--r--trunk/main/editline/refresh.h63
-rw-r--r--trunk/main/editline/search.c649
-rw-r--r--trunk/main/editline/search.h70
-rw-r--r--trunk/main/editline/sig.c198
-rw-r--r--trunk/main/editline/sig.h72
-rw-r--r--trunk/main/editline/sys.h123
-rw-r--r--trunk/main/editline/term.c1587
-rw-r--r--trunk/main/editline/term.h124
-rw-r--r--trunk/main/editline/tokenizer.c397
-rw-r--r--trunk/main/editline/tokenizer.h54
-rw-r--r--trunk/main/editline/tty.c1182
-rw-r--r--trunk/main/editline/tty.h484
-rw-r--r--trunk/main/editline/vi.c941
-rw-r--r--trunk/main/enum.c655
-rw-r--r--trunk/main/event.c822
-rw-r--r--trunk/main/file.c1245
-rw-r--r--trunk/main/fixedjitterbuf.c347
-rw-r--r--trunk/main/fixedjitterbuf.h93
-rw-r--r--trunk/main/frame.c1513
-rw-r--r--trunk/main/fskmodem.c360
-rw-r--r--trunk/main/global_datastores.c83
-rw-r--r--trunk/main/hashtab.c805
-rw-r--r--trunk/main/http.c1118
-rw-r--r--trunk/main/image.c208
-rw-r--r--trunk/main/indications.c598
-rw-r--r--trunk/main/io.c381
-rw-r--r--trunk/main/jitterbuf.c844
-rw-r--r--trunk/main/libresample/LICENSE.txt463
-rw-r--r--trunk/main/libresample/Makefile.asterisk11
-rw-r--r--trunk/main/libresample/Makefile.in52
-rw-r--r--trunk/main/libresample/README.txt84
-rwxr-xr-xtrunk/main/libresample/config.guess1432
-rwxr-xr-xtrunk/main/libresample/config.sub1537
-rwxr-xr-xtrunk/main/libresample/configure4552
-rw-r--r--trunk/main/libresample/configure.in68
-rw-r--r--trunk/main/libresample/include/libresample.h120
-rwxr-xr-xtrunk/main/libresample/install-sh251
-rw-r--r--trunk/main/libresample/src/configtemplate.h7
-rw-r--r--trunk/main/libresample/src/filterkit.c215
-rw-r--r--trunk/main/libresample/src/filterkit.h28
-rw-r--r--trunk/main/libresample/src/resample.c347
-rw-r--r--trunk/main/libresample/src/resample_defs.h88
-rw-r--r--trunk/main/libresample/src/resamplesubs.c123
-rw-r--r--trunk/main/libresample/tests/compareresample.c183
-rw-r--r--trunk/main/libresample/tests/resample-sndfile.c213
-rw-r--r--trunk/main/libresample/tests/testresample.c182
-rw-r--r--trunk/main/libresample/win/libresample.dsp116
-rw-r--r--trunk/main/libresample/win/libresample.vcproj192
-rw-r--r--trunk/main/loader.c1006
-rw-r--r--trunk/main/logger.c1203
-rw-r--r--trunk/main/manager.c3768
-rw-r--r--trunk/main/md5.c265
-rw-r--r--trunk/main/minimime/Make.conf7
-rw-r--r--trunk/main/minimime/Makefile67
-rw-r--r--trunk/main/minimime/mimeparser.h76
-rw-r--r--trunk/main/minimime/mimeparser.l484
-rw-r--r--trunk/main/minimime/mimeparser.tab.c2339
-rw-r--r--trunk/main/minimime/mimeparser.tab.h112
-rw-r--r--trunk/main/minimime/mimeparser.y748
-rw-r--r--trunk/main/minimime/mimeparser.yy.c2627
-rw-r--r--trunk/main/minimime/minimime.c244
-rw-r--r--trunk/main/minimime/mm.h367
-rw-r--r--trunk/main/minimime/mm_base64.c210
-rw-r--r--trunk/main/minimime/mm_codecs.c250
-rw-r--r--trunk/main/minimime/mm_contenttype.c759
-rw-r--r--trunk/main/minimime/mm_context.c614
-rw-r--r--trunk/main/minimime/mm_envelope.c271
-rw-r--r--trunk/main/minimime/mm_error.c123
-rw-r--r--trunk/main/minimime/mm_header.c213
-rw-r--r--trunk/main/minimime/mm_init.c65
-rw-r--r--trunk/main/minimime/mm_internal.h65
-rw-r--r--trunk/main/minimime/mm_mem.c170
-rw-r--r--trunk/main/minimime/mm_mem.h32
-rw-r--r--trunk/main/minimime/mm_mimepart.c659
-rw-r--r--trunk/main/minimime/mm_mimeutil.c137
-rw-r--r--trunk/main/minimime/mm_param.c225
-rw-r--r--trunk/main/minimime/mm_parse.c168
-rw-r--r--trunk/main/minimime/mm_queue.h508
-rw-r--r--trunk/main/minimime/mm_util.c412
-rw-r--r--trunk/main/minimime/mm_util.h50
-rw-r--r--trunk/main/minimime/mm_warnings.c99
-rw-r--r--trunk/main/minimime/strlcat.c70
-rw-r--r--trunk/main/minimime/strlcpy.c66
-rw-r--r--trunk/main/minimime/sys/mm_queue.h503
-rwxr-xr-xtrunk/main/minimime/test.sh54
-rw-r--r--trunk/main/minimime/tests/Makefile18
-rw-r--r--trunk/main/minimime/tests/create.c105
-rw-r--r--trunk/main/minimime/tests/messages/test1.txt50
-rw-r--r--trunk/main/minimime/tests/messages/test2.txt50
-rw-r--r--trunk/main/minimime/tests/messages/test3.txt12
-rw-r--r--trunk/main/minimime/tests/messages/test4.txt168
-rw-r--r--trunk/main/minimime/tests/messages/test5.txt44
-rw-r--r--trunk/main/minimime/tests/messages/test6.txt12
-rw-r--r--trunk/main/minimime/tests/messages/test7.txt64
-rw-r--r--trunk/main/minimime/tests/parse.c230
-rw-r--r--trunk/main/netsock.c208
-rw-r--r--trunk/main/pbx.c7800
-rw-r--r--trunk/main/plc.c248
-rw-r--r--trunk/main/poll.c306
-rw-r--r--trunk/main/privacy.c112
-rw-r--r--trunk/main/rtp.c4093
-rw-r--r--trunk/main/say.c7461
-rw-r--r--trunk/main/sched.c406
-rw-r--r--trunk/main/sha1.c337
-rw-r--r--trunk/main/slinfactory.c165
-rw-r--r--trunk/main/srv.c238
-rw-r--r--trunk/main/stdtime/Makefile29
-rw-r--r--trunk/main/stdtime/localtime.c1820
-rw-r--r--trunk/main/stdtime/private.h358
-rw-r--r--trunk/main/stdtime/test.c21
-rw-r--r--trunk/main/stdtime/tzfile.h184
-rw-r--r--trunk/main/strcompat.c463
-rw-r--r--trunk/main/tcptls.c452
-rw-r--r--trunk/main/tdd.c338
-rw-r--r--trunk/main/term.c297
-rw-r--r--trunk/main/threadstorage.c239
-rw-r--r--trunk/main/translate.c904
-rw-r--r--trunk/main/udptl.c1273
-rw-r--r--trunk/main/ulaw.c246
-rw-r--r--trunk/main/utils.c1557
249 files changed, 142376 insertions, 0 deletions
diff --git a/trunk/main/Makefile b/trunk/main/Makefile
new file mode 100644
index 000000000..3504b547b
--- /dev/null
+++ b/trunk/main/Makefile
@@ -0,0 +1,174 @@
+#
+# Asterisk -- A telephony toolkit for Linux.
+#
+# Makefile to build main Asterisk binary
+#
+# Copyright (C) 1999-2006, Digium, Inc.
+#
+# Mark Spencer <markster@digium.com>
+#
+# This program is free software, distributed under the terms of
+# the GNU General Public License
+#
+
+-include $(ASTTOPDIR)/menuselect.makeopts $(ASTTOPDIR)/menuselect.makedeps $(ASTTOPDIR)/makeopts.embed_rules
+
+all: asterisk
+
+include $(ASTTOPDIR)/Makefile.moddir_rules
+
+RESAMPLE_OBJS:=libresample/src/resample.o libresample/src/resamplesubs.o libresample/src/filterkit.o
+
+OBJS= tcptls.o io.o sched.o logger.o frame.o loader.o config.o channel.o \
+ translate.o file.o pbx.o cli.o md5.o term.o \
+ ulaw.o alaw.o callerid.o fskmodem.o image.o app.o \
+ cdr.o tdd.o acl.o rtp.o udptl.o manager.o asterisk.o \
+ dsp.o chanvars.o indications.o autoservice.o db.o privacy.o \
+ astmm.o enum.o srv.o dns.o aescrypt.o aestab.o aeskey.o \
+ utils.o plc.o jitterbuf.o dnsmgr.o devicestate.o \
+ netsock.o slinfactory.o ast_expr2.o ast_expr2f.o \
+ cryptostub.o sha1.o http.o fixedjitterbuf.o abstract_jb.o \
+ strcompat.o threadstorage.o dial.o event.o adsistub.o audiohook.o \
+ astobj2.o hashtab.o global_datastores.o $(RESAMPLE_OBJS) version.o
+
+# we need to link in the objects statically, not as a library, because
+# otherwise modules will not have them available if none of the static
+# objects use it.
+OBJS+=stdtime/localtime.o
+
+# At the moment say.o is an optional component which can be overridden
+# by a module.
+OBJS+=say.o
+
+AST_LIBS += $(SSL_LIB)
+AST_LIBS += $(BKTR_LIB)
+
+ifeq ($(POLL_AVAILABLE),)
+ OBJS+=poll.o
+endif
+
+ifeq ($(wildcard /usr/include/dlfcn.h),)
+ OBJS+=dlfcn.o
+endif
+
+ifneq ($(findstring $(OSARCH), linux-gnu uclinux linux-uclibc ),)
+ ifneq ($(findstring LOADABLE_MODULES,$(MENUSELECT_CFLAGS)),)
+ AST_LIBS+=-ldl
+ endif
+ ifneq (x$(CAP_LIB),x)
+ AST_LIBS+=$(CAP_LIB)
+ endif
+ AST_LIBS+=-lpthread $(EDITLINE_LIB) -lm -lresolv
+else
+ AST_LIBS+=$(EDITLINE_LIB) -lm
+endif
+
+ifneq ($(findstring darwin,$(OSARCH)),)
+ AST_LIBS+=-lresolv
+ ifneq ($(findstring LOADABLE_MODULES,$(MENUSELECT_CFLAGS)),)
+ ASTLINK=-Wl,-dynamic
+ endif
+else
+# These are used for all but Darwin
+ ifneq ($(findstring LOADABLE_MODULES,$(MENUSELECT_CFLAGS)),)
+ ASTLINK+=-Wl,--export-dynamic
+ else
+ ASTLINK+=${GC_LDFLAGS}
+ endif
+ ifneq ($(findstring BSD,$(OSARCH)),)
+ LDFLAGS+=-L/usr/local/lib
+ endif
+endif
+
+ifeq ($(OSARCH),FreeBSD)
+ # -V is understood by BSD Make, not by GNU make.
+ BSDVERSION=$(shell make -V OSVERSION -f /usr/share/mk/bsd.port.subdir.mk)
+ AST_LIBS+=$(shell if test $(BSDVERSION) -lt 502102 ; then echo "-lc_r"; else echo "-pthread"; fi)
+ AST_LIBS+=-lcrypto
+endif
+
+ifneq ($(findstring $(OSARCH), mingw32 cygwin ),)
+ AST_LIBS+=-lminires -ldl
+ ASTLINK+= -shared -Wl,--out-implib,libasterisk.a
+endif
+ifeq ($(OSARCH),NetBSD)
+ AST_LIBS+=-lpthread -lcrypto -lm -L/usr/pkg/lib $(EDITLINE_LIB)
+endif
+
+ifeq ($(OSARCH),OpenBSD)
+ AST_LIBS+=-lcrypto -lpthread -lm $(EDITLINE_LIB)
+endif
+
+ifeq ($(OSARCH),SunOS)
+ AST_LIBS+=-lpthread -ldl -lnsl -lsocket -lresolv -L/opt/ssl/lib -L/usr/local/ssl/lib
+ ASTLINK=
+endif
+
+CHECK_SUBDIR: # do nothing, just make sure that we recurse in the subdir/
+
+editline/libedit.a: CHECK_SUBDIR
+ cd editline && test -f config.h || CFLAGS="$(PTHREAD_CFLAGS) $(ASTCFLAGS:-Werror=)" LDFLAGS="$(ASTLDFLAGS)" ./configure --build=$(BUILD_PLATFORM) --host=$(HOST_PLATFORM) --with-ncurses=$(NCURSES_DIR) --with-curses=$(CURSES_DIR) --with-termcap=$(TERMCAP_DIR) --with-tinfo=$(TINFO_DIR)
+ $(MAKE) -C editline libedit.a
+
+db1-ast/libdb1.a: CHECK_SUBDIR
+ CFLAGS="$(ASTCFLAGS)" LDFLAGS="$(ASTLDFLAGS)" $(MAKE) -C db1-ast libdb1.a
+
+ast_expr2.c ast_expr2.h:
+ bison -o $@ -d --name-prefix=ast_yy ast_expr2.y
+
+ast_expr2f.c:
+ flex -o $@ --full ast_expr2.fl # moved the correction of yyfree into the flex input file itself.
+ sed 's@#if __STDC_VERSION__ >= 199901L@#if !defined __STDC_VERSION__ || __STDC_VERSION__ >= 199901L@' ast_expr2f.c > zz
+ mv zz ast_expr2f.c
+
+testexpr2: ast_expr2f.c ast_expr2.c ast_expr2.h
+ $(CC) -g -c -Iinclude -DSTANDALONE ast_expr2f.c
+ $(CC) -g -c -Iinclude -DSTANDALONE ast_expr2.c
+ $(CC) -g -o testexpr2 ast_expr2f.o ast_expr2.o -lm
+ rm ast_expr2.o ast_expr2f.o
+
+channel.o: ASTCFLAGS+=$(ZAPTEL_INCLUDE)
+
+AST_EMBED_LDSCRIPTS:=$(sort $(EMBED_LDSCRIPTS))
+AST_EMBED_LDFLAGS:=$(foreach dep,$(EMBED_LDFLAGS),$(value $(dep)))
+AST_EMBED_LIBS:=$(foreach dep,$(EMBED_LIBS),$(value $(dep)))
+OBJS:=$(sort $(OBJS))
+
+ifneq ($(wildcard ../channels/h323/Makefile.ast),)
+ include ../channels/h323/Makefile.ast
+else
+ H323LDFLAGS=
+ H323LDLIBS=
+endif
+
+minimime/libmmime.a: CHECK_SUBDIR
+ @cd minimime && $(MAKE) libmmime.a
+
+ifneq ($(findstring $(OSARCH), mingw32 cygwin ),)
+MAIN_TGT:=asterisk.dll
+asterisk: cygload
+ mv cygload.exe asterisk.exe
+
+cygload: asterisk.dll
+else
+MAIN_TGT:=asterisk
+endif
+
+$(MAIN_TGT): $(OBJS) editline/libedit.a db1-ast/libdb1.a minimime/libmmime.a $(AST_EMBED_LDSCRIPTS)
+ @$(CC) -c -o buildinfo.o $(ASTCFLAGS) buildinfo.c
+ $(ECHO_PREFIX) echo " [LD] $^ -> $@"
+ifneq ($(findstring chan_h323,$(MENUSELECT_CHANNELS)),)
+ $(CMD_PREFIX) $(CC) $(STATIC_BUILD) -o $@ $(ASTLINK) $(AST_EMBED_LDFLAGS) $(ASTLDFLAGS) $^ buildinfo.o $(AST_LIBS) $(AST_EMBED_LIBS)
+else
+ $(CMD_PREFIX) $(CXX) $(STATIC_BUILD) -o $@ $(ASTLINK) $(AST_EMBED_LDFLAGS) $(ASTLDFLAGS) $(H323LDFLAGS) $^ buildinfo.o $(AST_LIBS) $(AST_EMBED_LIBS) $(H323LDLIBS)
+endif
+ @$(ASTTOPDIR)/build_tools/strip_nonapi $@
+
+clean::
+ rm -f asterisk
+ rm -f db1-ast/.*.d
+ @if [ -f editline/Makefile ]; then $(MAKE) -C editline distclean ; fi
+ @$(MAKE) -C db1-ast clean
+ @$(MAKE) -C stdtime clean
+ @$(MAKE) -C minimime clean
+ rm -f libresample/src/*.o
diff --git a/trunk/main/abstract_jb.c b/trunk/main/abstract_jb.c
new file mode 100644
index 000000000..4f464796d
--- /dev/null
+++ b/trunk/main/abstract_jb.c
@@ -0,0 +1,774 @@
+/*
+ * abstract_jb: common implementation-independent jitterbuffer stuff
+ *
+ * Copyright (C) 2005, Attractel OOD
+ *
+ * Contributors:
+ * Slav Klenov <slav@securax.org>
+ *
+ * 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.
+ *
+ * A license has been granted to Digium (via disclaimer) for the use of
+ * this code.
+ */
+
+/*! \file
+ *
+ * \brief Common implementation-independent jitterbuffer stuff.
+ *
+ * \author Slav Klenov <slav@securax.org>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/frame.h"
+#include "asterisk/channel.h"
+#include "asterisk/term.h"
+#include "asterisk/utils.h"
+
+#include "asterisk/abstract_jb.h"
+#include "fixedjitterbuf.h"
+#include "jitterbuf.h"
+
+/*! Internal jb flags */
+enum {
+ JB_USE = (1 << 0),
+ JB_TIMEBASE_INITIALIZED = (1 << 1),
+ JB_CREATED = (1 << 2)
+};
+
+/* Hooks for the abstract jb implementation */
+
+/*! \brief Create */
+typedef void * (*jb_create_impl)(struct ast_jb_conf *general_config, long resynch_threshold);
+/*! \brief Destroy */
+typedef void (*jb_destroy_impl)(void *jb);
+/*! \brief Put first frame */
+typedef int (*jb_put_first_impl)(void *jb, struct ast_frame *fin, long now);
+/*! \brief Put frame */
+typedef int (*jb_put_impl)(void *jb, struct ast_frame *fin, long now);
+/*! \brief Get frame for now */
+typedef int (*jb_get_impl)(void *jb, struct ast_frame **fout, long now, long interpl);
+/*! \brief Get next */
+typedef long (*jb_next_impl)(void *jb);
+/*! \brief Remove first frame */
+typedef int (*jb_remove_impl)(void *jb, struct ast_frame **fout);
+/*! \brief Force resynch */
+typedef void (*jb_force_resynch_impl)(void *jb);
+
+
+/*!
+ * \brief Jitterbuffer implementation private struct.
+ */
+struct ast_jb_impl
+{
+ char name[AST_JB_IMPL_NAME_SIZE];
+ jb_create_impl create;
+ jb_destroy_impl destroy;
+ jb_put_first_impl put_first;
+ jb_put_impl put;
+ jb_get_impl get;
+ jb_next_impl next;
+ jb_remove_impl remove;
+ jb_force_resynch_impl force_resync;
+};
+
+/* Implementation functions */
+/* fixed */
+static void * jb_create_fixed(struct ast_jb_conf *general_config, long resynch_threshold);
+static void jb_destroy_fixed(void *jb);
+static int jb_put_first_fixed(void *jb, struct ast_frame *fin, long now);
+static int jb_put_fixed(void *jb, struct ast_frame *fin, long now);
+static int jb_get_fixed(void *jb, struct ast_frame **fout, long now, long interpl);
+static long jb_next_fixed(void *jb);
+static int jb_remove_fixed(void *jb, struct ast_frame **fout);
+static void jb_force_resynch_fixed(void *jb);
+/* adaptive */
+static void * jb_create_adaptive(struct ast_jb_conf *general_config, long resynch_threshold);
+static void jb_destroy_adaptive(void *jb);
+static int jb_put_first_adaptive(void *jb, struct ast_frame *fin, long now);
+static int jb_put_adaptive(void *jb, struct ast_frame *fin, long now);
+static int jb_get_adaptive(void *jb, struct ast_frame **fout, long now, long interpl);
+static long jb_next_adaptive(void *jb);
+static int jb_remove_adaptive(void *jb, struct ast_frame **fout);
+static void jb_force_resynch_adaptive(void *jb);
+
+/* Available jb implementations */
+static struct ast_jb_impl avail_impl[] =
+{
+ {
+ .name = "fixed",
+ .create = jb_create_fixed,
+ .destroy = jb_destroy_fixed,
+ .put_first = jb_put_first_fixed,
+ .put = jb_put_fixed,
+ .get = jb_get_fixed,
+ .next = jb_next_fixed,
+ .remove = jb_remove_fixed,
+ .force_resync = jb_force_resynch_fixed
+ },
+ {
+ .name = "adaptive",
+ .create = jb_create_adaptive,
+ .destroy = jb_destroy_adaptive,
+ .put_first = jb_put_first_adaptive,
+ .put = jb_put_adaptive,
+ .get = jb_get_adaptive,
+ .next = jb_next_adaptive,
+ .remove = jb_remove_adaptive,
+ .force_resync = jb_force_resynch_adaptive
+ }
+};
+
+static int default_impl = 0;
+
+
+/*! Abstract return codes */
+enum {
+ JB_IMPL_OK,
+ JB_IMPL_DROP,
+ JB_IMPL_INTERP,
+ JB_IMPL_NOFRAME
+};
+
+/* Translations between impl and abstract return codes */
+static int fixed_to_abstract_code[] =
+ {JB_IMPL_OK, JB_IMPL_DROP, JB_IMPL_INTERP, JB_IMPL_NOFRAME};
+static int adaptive_to_abstract_code[] =
+ {JB_IMPL_OK, JB_IMPL_NOFRAME, JB_IMPL_NOFRAME, JB_IMPL_INTERP, JB_IMPL_DROP, JB_IMPL_OK};
+
+/* JB_GET actions (used only for the frames log) */
+static char *jb_get_actions[] = {"Delivered", "Dropped", "Interpolated", "No"};
+
+/*! \brief Macros for the frame log files */
+#define jb_framelog(...) do { \
+ if (jb->logfile) { \
+ fprintf(jb->logfile, __VA_ARGS__); \
+ fflush(jb->logfile); \
+ } \
+} while (0)
+
+
+/* Internal utility functions */
+static void jb_choose_impl(struct ast_channel *chan);
+static void jb_get_and_deliver(struct ast_channel *chan);
+static int create_jb(struct ast_channel *chan, struct ast_frame *first_frame);
+static long get_now(struct ast_jb *jb, struct timeval *tv);
+
+
+/* Interface ast jb functions impl */
+
+
+static void jb_choose_impl(struct ast_channel *chan)
+{
+ struct ast_jb *jb = &chan->jb;
+ struct ast_jb_conf *jbconf = &jb->conf;
+ struct ast_jb_impl *test_impl;
+ int i, avail_impl_count = sizeof(avail_impl) / sizeof(avail_impl[0]);
+
+ jb->impl = &avail_impl[default_impl];
+
+ if (ast_strlen_zero(jbconf->impl))
+ return;
+
+ for (i = 0; i < avail_impl_count; i++) {
+ test_impl = &avail_impl[i];
+ if (!strcasecmp(jbconf->impl, test_impl->name)) {
+ jb->impl = test_impl;
+ return;
+ }
+ }
+}
+
+int ast_jb_do_usecheck(struct ast_channel *c0, struct ast_channel *c1)
+{
+ struct ast_jb *jb0 = &c0->jb;
+ struct ast_jb *jb1 = &c1->jb;
+ struct ast_jb_conf *conf0 = &jb0->conf;
+ struct ast_jb_conf *conf1 = &jb1->conf;
+ int c0_wants_jitter = c0->tech->properties & AST_CHAN_TP_WANTSJITTER;
+ int c0_creates_jitter = c0->tech->properties & AST_CHAN_TP_CREATESJITTER;
+ int c0_jb_enabled = ast_test_flag(conf0, AST_JB_ENABLED);
+ int c0_force_jb = ast_test_flag(conf0, AST_JB_FORCED);
+ int c0_jb_timebase_initialized = ast_test_flag(jb0, JB_TIMEBASE_INITIALIZED);
+ int c0_jb_created = ast_test_flag(jb0, JB_CREATED);
+ int c1_wants_jitter = c1->tech->properties & AST_CHAN_TP_WANTSJITTER;
+ int c1_creates_jitter = c1->tech->properties & AST_CHAN_TP_CREATESJITTER;
+ int c1_jb_enabled = ast_test_flag(conf1, AST_JB_ENABLED);
+ int c1_force_jb = ast_test_flag(conf1, AST_JB_FORCED);
+ int c1_jb_timebase_initialized = ast_test_flag(jb1, JB_TIMEBASE_INITIALIZED);
+ int c1_jb_created = ast_test_flag(jb1, JB_CREATED);
+ int inuse = 0;
+
+ /* Determine whether audio going to c0 needs a jitter buffer */
+ if (((!c0_wants_jitter && c1_creates_jitter) || (c0_force_jb && c1_creates_jitter)) && c0_jb_enabled) {
+ ast_set_flag(jb0, JB_USE);
+ if (!c0_jb_timebase_initialized) {
+ if (c1_jb_timebase_initialized) {
+ memcpy(&jb0->timebase, &jb1->timebase, sizeof(struct timeval));
+ } else {
+ gettimeofday(&jb0->timebase, NULL);
+ }
+ ast_set_flag(jb0, JB_TIMEBASE_INITIALIZED);
+ }
+
+ if (!c0_jb_created) {
+ jb_choose_impl(c0);
+ }
+
+ inuse = 1;
+ }
+
+ /* Determine whether audio going to c1 needs a jitter buffer */
+ if (((!c1_wants_jitter && c0_creates_jitter) || (c1_force_jb && c0_creates_jitter)) && c1_jb_enabled) {
+ ast_set_flag(jb1, JB_USE);
+ if (!c1_jb_timebase_initialized) {
+ if (c0_jb_timebase_initialized) {
+ memcpy(&jb1->timebase, &jb0->timebase, sizeof(struct timeval));
+ } else {
+ gettimeofday(&jb1->timebase, NULL);
+ }
+ ast_set_flag(jb1, JB_TIMEBASE_INITIALIZED);
+ }
+
+ if (!c1_jb_created) {
+ jb_choose_impl(c1);
+ }
+
+ inuse = 1;
+ }
+
+ return inuse;
+}
+
+int ast_jb_get_when_to_wakeup(struct ast_channel *c0, struct ast_channel *c1, int time_left)
+{
+ struct ast_jb *jb0 = &c0->jb;
+ struct ast_jb *jb1 = &c1->jb;
+ int c0_use_jb = ast_test_flag(jb0, JB_USE);
+ int c0_jb_is_created = ast_test_flag(jb0, JB_CREATED);
+ int c1_use_jb = ast_test_flag(jb1, JB_USE);
+ int c1_jb_is_created = ast_test_flag(jb1, JB_CREATED);
+ int wait, wait0, wait1;
+ struct timeval tv_now;
+
+ if (time_left == 0) {
+ /* No time left - the bridge will be retried */
+ /* TODO: Test disable this */
+ /*return 0;*/
+ }
+
+ if (time_left < 0) {
+ time_left = INT_MAX;
+ }
+
+ gettimeofday(&tv_now, NULL);
+
+ wait0 = (c0_use_jb && c0_jb_is_created) ? jb0->next - get_now(jb0, &tv_now) : time_left;
+ wait1 = (c1_use_jb && c1_jb_is_created) ? jb1->next - get_now(jb1, &tv_now) : time_left;
+
+ wait = wait0 < wait1 ? wait0 : wait1;
+ wait = wait < time_left ? wait : time_left;
+
+ if (wait == INT_MAX) {
+ wait = -1;
+ } else if (wait < 1) {
+ /* don't let wait=0, because this can cause the pbx thread to loop without any sleeping at all */
+ wait = 1;
+ }
+
+ return wait;
+}
+
+
+int ast_jb_put(struct ast_channel *chan, struct ast_frame *f)
+{
+ struct ast_jb *jb = &chan->jb;
+ struct ast_jb_impl *jbimpl = jb->impl;
+ void *jbobj = jb->jbobj;
+ struct ast_frame *frr;
+ long now = 0;
+
+ if (!ast_test_flag(jb, JB_USE))
+ return -1;
+
+ if (f->frametype != AST_FRAME_VOICE) {
+ if (f->frametype == AST_FRAME_DTMF && ast_test_flag(jb, JB_CREATED)) {
+ jb_framelog("JB_PUT {now=%ld}: Received DTMF frame. Force resynching jb...\n", now);
+ jbimpl->force_resync(jbobj);
+ }
+
+ return -1;
+ }
+
+ /* We consider an enabled jitterbuffer should receive frames with valid timing info. */
+ if (!ast_test_flag(f, AST_FRFLAG_HAS_TIMING_INFO) || f->len < 2 || f->ts < 0) {
+ ast_log(LOG_WARNING, "%s received frame with invalid timing info: "
+ "has_timing_info=%d, len=%ld, ts=%ld, src=%s\n",
+ chan->name, ast_test_flag(f, AST_FRFLAG_HAS_TIMING_INFO), f->len, f->ts, f->src);
+ return -1;
+ }
+
+ frr = ast_frdup(f);
+
+ if (!frr) {
+ ast_log(LOG_ERROR, "Failed to isolate frame for the jitterbuffer on channel '%s'\n", chan->name);
+ return -1;
+ }
+
+ if (!ast_test_flag(jb, JB_CREATED)) {
+ if (create_jb(chan, frr)) {
+ ast_frfree(frr);
+ /* Disable the jitterbuffer */
+ ast_clear_flag(jb, JB_USE);
+ return -1;
+ }
+
+ ast_set_flag(jb, JB_CREATED);
+ return 0;
+ } else {
+ now = get_now(jb, NULL);
+ if (jbimpl->put(jbobj, frr, now) != JB_IMPL_OK) {
+ jb_framelog("JB_PUT {now=%ld}: Dropped frame with ts=%ld and len=%ld\n", now, frr->ts, frr->len);
+ ast_frfree(frr);
+ /*return -1;*/
+ /* TODO: Check this fix - should return 0 here, because the dropped frame shouldn't
+ be delivered at all */
+ return 0;
+ }
+
+ jb->next = jbimpl->next(jbobj);
+
+ jb_framelog("JB_PUT {now=%ld}: Queued frame with ts=%ld and len=%ld\n", now, frr->ts, frr->len);
+
+ return 0;
+ }
+}
+
+
+void ast_jb_get_and_deliver(struct ast_channel *c0, struct ast_channel *c1)
+{
+ struct ast_jb *jb0 = &c0->jb;
+ struct ast_jb *jb1 = &c1->jb;
+ int c0_use_jb = ast_test_flag(jb0, JB_USE);
+ int c0_jb_is_created = ast_test_flag(jb0, JB_CREATED);
+ int c1_use_jb = ast_test_flag(jb1, JB_USE);
+ int c1_jb_is_created = ast_test_flag(jb1, JB_CREATED);
+
+ if (c0_use_jb && c0_jb_is_created)
+ jb_get_and_deliver(c0);
+
+ if (c1_use_jb && c1_jb_is_created)
+ jb_get_and_deliver(c1);
+}
+
+
+static void jb_get_and_deliver(struct ast_channel *chan)
+{
+ struct ast_jb *jb = &chan->jb;
+ struct ast_jb_impl *jbimpl = jb->impl;
+ void *jbobj = jb->jbobj;
+ struct ast_frame *f, finterp;
+ long now;
+ int interpolation_len, res;
+
+ now = get_now(jb, NULL);
+ jb->next = jbimpl->next(jbobj);
+ if (now < jb->next) {
+ jb_framelog("\tJB_GET {now=%ld}: now < next=%ld\n", now, jb->next);
+ return;
+ }
+
+ while (now >= jb->next) {
+ interpolation_len = ast_codec_interp_len(jb->last_format);
+
+ res = jbimpl->get(jbobj, &f, now, interpolation_len);
+
+ switch (res) {
+ case JB_IMPL_OK:
+ /* deliver the frame */
+ ast_write(chan, f);
+ case JB_IMPL_DROP:
+ jb_framelog("\tJB_GET {now=%ld}: %s frame with ts=%ld and len=%ld\n",
+ now, jb_get_actions[res], f->ts, f->len);
+ jb->last_format = f->subclass;
+ ast_frfree(f);
+ break;
+ case JB_IMPL_INTERP:
+ /* interpolate a frame */
+ f = &finterp;
+ f->frametype = AST_FRAME_VOICE;
+ f->subclass = jb->last_format;
+ f->datalen = 0;
+ f->samples = interpolation_len * 8;
+ f->mallocd = 0;
+ f->src = "JB interpolation";
+ f->data = NULL;
+ f->delivery = ast_tvadd(jb->timebase, ast_samp2tv(jb->next, 1000));
+ f->offset = AST_FRIENDLY_OFFSET;
+ /* deliver the interpolated frame */
+ ast_write(chan, f);
+ jb_framelog("\tJB_GET {now=%ld}: Interpolated frame with len=%d\n", now, interpolation_len);
+ break;
+ case JB_IMPL_NOFRAME:
+ ast_log(LOG_WARNING,
+ "JB_IMPL_NOFRAME is retuned from the %s jb when now=%ld >= next=%ld, jbnext=%ld!\n",
+ jbimpl->name, now, jb->next, jbimpl->next(jbobj));
+ jb_framelog("\tJB_GET {now=%ld}: No frame for now!?\n", now);
+ return;
+ default:
+ ast_log(LOG_ERROR, "This should never happen!\n");
+ CRASH;
+ break;
+ }
+
+ jb->next = jbimpl->next(jbobj);
+ }
+}
+
+
+static int create_jb(struct ast_channel *chan, struct ast_frame *frr)
+{
+ struct ast_jb *jb = &chan->jb;
+ struct ast_jb_conf *jbconf = &jb->conf;
+ struct ast_jb_impl *jbimpl = jb->impl;
+ void *jbobj;
+ struct ast_channel *bridged;
+ long now;
+ char logfile_pathname[20 + AST_JB_IMPL_NAME_SIZE + 2*AST_CHANNEL_NAME + 1];
+ char name1[AST_CHANNEL_NAME], name2[AST_CHANNEL_NAME], *tmp;
+ int res;
+
+ jbobj = jb->jbobj = jbimpl->create(jbconf, jbconf->resync_threshold);
+ if (!jbobj) {
+ ast_log(LOG_WARNING, "Failed to create jitterbuffer on channel '%s'\n", chan->name);
+ return -1;
+ }
+
+ now = get_now(jb, NULL);
+ res = jbimpl->put_first(jbobj, frr, now);
+
+ /* The result of putting the first frame should not differ from OK. However, its possible
+ some implementations (i.e. adaptive's when resynch_threshold is specified) to drop it. */
+ if (res != JB_IMPL_OK) {
+ ast_log(LOG_WARNING, "Failed to put first frame in the jitterbuffer on channel '%s'\n", chan->name);
+ /*
+ jbimpl->destroy(jbobj);
+ return -1;
+ */
+ }
+
+ /* Init next */
+ jb->next = jbimpl->next(jbobj);
+
+ /* Init last format for a first time. */
+ jb->last_format = frr->subclass;
+
+ /* Create a frame log file */
+ if (ast_test_flag(jbconf, AST_JB_LOG)) {
+ snprintf(name2, sizeof(name2), "%s", chan->name);
+ tmp = strchr(name2, '/');
+ if (tmp)
+ *tmp = '#';
+
+ bridged = ast_bridged_channel(chan);
+ if (!bridged) {
+ /* We should always have bridged chan if a jitterbuffer is in use */
+ CRASH;
+ }
+ snprintf(name1, sizeof(name1), "%s", bridged->name);
+ tmp = strchr(name1, '/');
+ if (tmp)
+ *tmp = '#';
+
+ snprintf(logfile_pathname, sizeof(logfile_pathname),
+ "/tmp/ast_%s_jb_%s--%s.log", jbimpl->name, name1, name2);
+ jb->logfile = fopen(logfile_pathname, "w+b");
+
+ if (!jb->logfile)
+ ast_log(LOG_WARNING, "Failed to create frame log file with pathname '%s'\n", logfile_pathname);
+
+ if (res == JB_IMPL_OK)
+ jb_framelog("JB_PUT_FIRST {now=%ld}: Queued frame with ts=%ld and len=%ld\n",
+ now, frr->ts, frr->len);
+ else
+ jb_framelog("JB_PUT_FIRST {now=%ld}: Dropped frame with ts=%ld and len=%ld\n",
+ now, frr->ts, frr->len);
+ }
+
+ ast_verb(3, "%s jitterbuffer created on channel %s\n", jbimpl->name, chan->name);
+
+ /* Free the frame if it has not been queued in the jb */
+ if (res != JB_IMPL_OK)
+ ast_frfree(frr);
+
+ return 0;
+}
+
+
+void ast_jb_destroy(struct ast_channel *chan)
+{
+ struct ast_jb *jb = &chan->jb;
+ struct ast_jb_impl *jbimpl = jb->impl;
+ void *jbobj = jb->jbobj;
+ struct ast_frame *f;
+
+ if (jb->logfile) {
+ fclose(jb->logfile);
+ jb->logfile = NULL;
+ }
+
+ if (ast_test_flag(jb, JB_CREATED)) {
+ /* Remove and free all frames still queued in jb */
+ while (jbimpl->remove(jbobj, &f) == JB_IMPL_OK) {
+ ast_frfree(f);
+ }
+
+ jbimpl->destroy(jbobj);
+ jb->jbobj = NULL;
+
+ ast_clear_flag(jb, JB_CREATED);
+
+ ast_verb(3, "%s jitterbuffer destroyed on channel %s\n", jbimpl->name, chan->name);
+ }
+}
+
+
+static long get_now(struct ast_jb *jb, struct timeval *tv)
+{
+ struct timeval now;
+
+ if (!tv) {
+ tv = &now;
+ gettimeofday(tv, NULL);
+ }
+
+ return ast_tvdiff_ms(*tv, jb->timebase);
+}
+
+
+int ast_jb_read_conf(struct ast_jb_conf *conf, const char *varname, const char *value)
+{
+ int prefixlen = sizeof(AST_JB_CONF_PREFIX) - 1;
+ const char *name;
+ int tmp;
+
+ if (strncasecmp(AST_JB_CONF_PREFIX, varname, prefixlen))
+ return -1;
+
+ name = varname + prefixlen;
+
+ if (!strcasecmp(name, AST_JB_CONF_ENABLE)) {
+ ast_set2_flag(conf, ast_true(value), AST_JB_ENABLED);
+ } else if (!strcasecmp(name, AST_JB_CONF_FORCE)) {
+ ast_set2_flag(conf, ast_true(value), AST_JB_FORCED);
+ } else if (!strcasecmp(name, AST_JB_CONF_MAX_SIZE)) {
+ if ((tmp = atoi(value)) > 0)
+ conf->max_size = tmp;
+ } else if (!strcasecmp(name, AST_JB_CONF_RESYNCH_THRESHOLD)) {
+ if ((tmp = atoi(value)) > 0)
+ conf->resync_threshold = tmp;
+ } else if (!strcasecmp(name, AST_JB_CONF_IMPL)) {
+ if (!ast_strlen_zero(value))
+ snprintf(conf->impl, sizeof(conf->impl), "%s", value);
+ } else if (!strcasecmp(name, AST_JB_CONF_LOG)) {
+ ast_set2_flag(conf, ast_true(value), AST_JB_LOG);
+ } else {
+ return -1;
+ }
+
+ return 0;
+}
+
+
+void ast_jb_configure(struct ast_channel *chan, const struct ast_jb_conf *conf)
+{
+ memcpy(&chan->jb.conf, conf, sizeof(*conf));
+}
+
+
+void ast_jb_get_config(const struct ast_channel *chan, struct ast_jb_conf *conf)
+{
+ memcpy(conf, &chan->jb.conf, sizeof(*conf));
+}
+
+
+/* Implementation functions */
+
+/* fixed */
+
+static void * jb_create_fixed(struct ast_jb_conf *general_config, long resynch_threshold)
+{
+ struct fixed_jb_conf conf;
+
+ conf.jbsize = general_config->max_size;
+ conf.resync_threshold = resynch_threshold;
+
+ return fixed_jb_new(&conf);
+}
+
+
+static void jb_destroy_fixed(void *jb)
+{
+ struct fixed_jb *fixedjb = (struct fixed_jb *) jb;
+
+ /* destroy the jb */
+ fixed_jb_destroy(fixedjb);
+}
+
+
+static int jb_put_first_fixed(void *jb, struct ast_frame *fin, long now)
+{
+ struct fixed_jb *fixedjb = (struct fixed_jb *) jb;
+ int res;
+
+ res = fixed_jb_put_first(fixedjb, fin, fin->len, fin->ts, now);
+
+ return fixed_to_abstract_code[res];
+}
+
+
+static int jb_put_fixed(void *jb, struct ast_frame *fin, long now)
+{
+ struct fixed_jb *fixedjb = (struct fixed_jb *) jb;
+ int res;
+
+ res = fixed_jb_put(fixedjb, fin, fin->len, fin->ts, now);
+
+ return fixed_to_abstract_code[res];
+}
+
+
+static int jb_get_fixed(void *jb, struct ast_frame **fout, long now, long interpl)
+{
+ struct fixed_jb *fixedjb = (struct fixed_jb *) jb;
+ struct fixed_jb_frame frame;
+ int res;
+
+ res = fixed_jb_get(fixedjb, &frame, now, interpl);
+ *fout = frame.data;
+
+ return fixed_to_abstract_code[res];
+}
+
+
+static long jb_next_fixed(void *jb)
+{
+ struct fixed_jb *fixedjb = (struct fixed_jb *) jb;
+
+ return fixed_jb_next(fixedjb);
+}
+
+
+static int jb_remove_fixed(void *jb, struct ast_frame **fout)
+{
+ struct fixed_jb *fixedjb = (struct fixed_jb *) jb;
+ struct fixed_jb_frame frame;
+ int res;
+
+ res = fixed_jb_remove(fixedjb, &frame);
+ *fout = frame.data;
+
+ return fixed_to_abstract_code[res];
+}
+
+
+static void jb_force_resynch_fixed(void *jb)
+{
+ struct fixed_jb *fixedjb = (struct fixed_jb *) jb;
+
+ fixed_jb_set_force_resynch(fixedjb);
+}
+
+
+/* adaptive */
+
+static void *jb_create_adaptive(struct ast_jb_conf *general_config, long resynch_threshold)
+{
+ jb_conf jbconf;
+ jitterbuf *adaptivejb;
+
+ adaptivejb = jb_new();
+ if (adaptivejb) {
+ jbconf.max_jitterbuf = general_config->max_size;
+ jbconf.resync_threshold = general_config->resync_threshold;
+ jbconf.max_contig_interp = 10;
+ jb_setconf(adaptivejb, &jbconf);
+ }
+
+ return adaptivejb;
+}
+
+
+static void jb_destroy_adaptive(void *jb)
+{
+ jitterbuf *adaptivejb = (jitterbuf *) jb;
+
+ jb_destroy(adaptivejb);
+}
+
+
+static int jb_put_first_adaptive(void *jb, struct ast_frame *fin, long now)
+{
+ return jb_put_adaptive(jb, fin, now);
+}
+
+
+static int jb_put_adaptive(void *jb, struct ast_frame *fin, long now)
+{
+ jitterbuf *adaptivejb = (jitterbuf *) jb;
+ int res;
+
+ res = jb_put(adaptivejb, fin, JB_TYPE_VOICE, fin->len, fin->ts, now);
+
+ return adaptive_to_abstract_code[res];
+}
+
+
+static int jb_get_adaptive(void *jb, struct ast_frame **fout, long now, long interpl)
+{
+ jitterbuf *adaptivejb = (jitterbuf *) jb;
+ jb_frame frame;
+ int res;
+
+ res = jb_get(adaptivejb, &frame, now, interpl);
+ *fout = frame.data;
+
+ return adaptive_to_abstract_code[res];
+}
+
+
+static long jb_next_adaptive(void *jb)
+{
+ jitterbuf *adaptivejb = (jitterbuf *) jb;
+
+ return jb_next(adaptivejb);
+}
+
+
+static int jb_remove_adaptive(void *jb, struct ast_frame **fout)
+{
+ jitterbuf *adaptivejb = (jitterbuf *) jb;
+ jb_frame frame;
+ int res;
+
+ res = jb_getall(adaptivejb, &frame);
+ *fout = frame.data;
+
+ return adaptive_to_abstract_code[res];
+}
+
+
+static void jb_force_resynch_adaptive(void *jb)
+{
+}
diff --git a/trunk/main/acl.c b/trunk/main/acl.c
new file mode 100644
index 000000000..ed149b963
--- /dev/null
+++ b/trunk/main/acl.c
@@ -0,0 +1,357 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006, 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.
+ */
+
+/*! \file
+ *
+ * \brief Various sorts of access control
+ *
+ * \author Mark Spencer <markster@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/network.h"
+
+#if defined(SOLARIS)
+#include <sys/sockio.h>
+#endif
+
+#include "asterisk/acl.h"
+#include "asterisk/channel.h"
+#include "asterisk/utils.h"
+#include "asterisk/lock.h"
+#include "asterisk/srv.h"
+
+/* Free HA structure */
+void ast_free_ha(struct ast_ha *ha)
+{
+ struct ast_ha *hal;
+ while (ha) {
+ hal = ha;
+ ha = ha->next;
+ ast_free(hal);
+ }
+}
+
+/* Copy HA structure */
+static void ast_copy_ha(struct ast_ha *from, struct ast_ha *to)
+{
+ memcpy(&to->netaddr, &from->netaddr, sizeof(from->netaddr));
+ memcpy(&to->netmask, &from->netmask, sizeof(from->netmask));
+ to->sense = from->sense;
+}
+
+/* Create duplicate of ha structure */
+static struct ast_ha *ast_duplicate_ha(struct ast_ha *original)
+{
+ struct ast_ha *new_ha;
+
+ if ((new_ha = ast_malloc(sizeof(*new_ha)))) {
+ /* Copy from original to new object */
+ ast_copy_ha(original, new_ha);
+ }
+
+ return new_ha;
+}
+
+/* Create duplicate HA link list */
+/* Used in chan_sip2 templates */
+struct ast_ha *ast_duplicate_ha_list(struct ast_ha *original)
+{
+ struct ast_ha *start = original;
+ struct ast_ha *ret = NULL;
+ struct ast_ha *link, *prev = NULL;
+
+ while (start) {
+ link = ast_duplicate_ha(start); /* Create copy of this object */
+ if (prev)
+ prev->next = link; /* Link previous to this object */
+
+ if (!ret)
+ ret = link; /* Save starting point */
+
+ start = start->next; /* Go to next object */
+ prev = link; /* Save pointer to this object */
+ }
+ return ret; /* Return start of list */
+}
+
+struct ast_ha *ast_append_ha(const char *sense, const char *stuff, struct ast_ha *path, int *error)
+{
+ struct ast_ha *ha;
+ char *nm;
+ struct ast_ha *prev = NULL;
+ struct ast_ha *ret;
+ int x;
+ char *tmp = ast_strdupa(stuff);
+
+ ret = path;
+ while (path) {
+ prev = path;
+ path = path->next;
+ }
+
+ ha = ast_malloc(sizeof(*ha));
+ if (!ha)
+ return ret;
+
+ nm = strchr(tmp, '/');
+ if (!nm) {
+ /* assume /32. Yes, htonl does not do anything for this particular mask
+ but we better use it to show we remember about byte order */
+ ha->netmask.s_addr = htonl(0xFFFFFFFF);
+ } else {
+ *nm = '\0';
+ nm++;
+
+ if (!strchr(nm, '.')) {
+ if ((sscanf(nm, "%d", &x) == 1) && (x >= 0) && (x <= 32))
+ ha->netmask.s_addr = htonl(0xFFFFFFFF << (32 - x));
+ else {
+ ast_log(LOG_WARNING, "Invalid CIDR in %s\n", stuff);
+ ast_free(ha);
+ if (error)
+ *error = 1;
+ return ret;
+ }
+ } else if (!inet_aton(nm, &ha->netmask)) {
+ ast_log(LOG_WARNING, "Invalid mask in %s\n", stuff);
+ ast_free(ha);
+ if (error)
+ *error = 1;
+ return ret;
+ }
+ }
+
+ if (!inet_aton(tmp, &ha->netaddr)) {
+ ast_log(LOG_WARNING, "Invalid IP address in %s\n", stuff);
+ ast_free(ha);
+ if (error)
+ *error = 1;
+ return ret;
+ }
+
+ ha->netaddr.s_addr &= ha->netmask.s_addr;
+
+ ha->sense = strncasecmp(sense, "p", 1) ? AST_SENSE_DENY : AST_SENSE_ALLOW;
+
+ ha->next = NULL;
+ if (prev) {
+ prev->next = ha;
+ } else {
+ ret = ha;
+ }
+
+ ast_debug(1, "%s/%s sense %d appended to acl for peer\n", ast_strdupa(ast_inet_ntoa(ha->netaddr)), ast_strdupa(ast_inet_ntoa(ha->netmask)), ha->sense);
+
+ return ret;
+}
+
+int ast_apply_ha(struct ast_ha *ha, struct sockaddr_in *sin)
+{
+ /* Start optimistic */
+ int res = AST_SENSE_ALLOW;
+ while (ha) {
+#if 0 /* debugging code */
+ char iabuf[INET_ADDRSTRLEN];
+ char iabuf2[INET_ADDRSTRLEN];
+ /* DEBUG */
+ ast_copy_string(iabuf, ast_inet_ntoa(sin->sin_addr), sizeof(iabuf));
+ ast_copy_string(iabuf2, ast_inet_ntoa(ha->netaddr), sizeof(iabuf2));
+ ast_debug(1, "##### Testing %s with %s\n", iabuf, iabuf2);
+#endif
+ /* For each rule, if this address and the netmask = the net address
+ apply the current rule */
+ if ((sin->sin_addr.s_addr & ha->netmask.s_addr) == ha->netaddr.s_addr)
+ res = ha->sense;
+ ha = ha->next;
+ }
+ return res;
+}
+
+int ast_get_ip_or_srv(struct sockaddr_in *sin, const char *value, const char *service)
+{
+ struct hostent *hp;
+ struct ast_hostent ahp;
+ char srv[256];
+ char host[256];
+ int tportno = ntohs(sin->sin_port);
+ if (service) {
+ snprintf(srv, sizeof(srv), "%s.%s", service, value);
+ if (ast_get_srv(NULL, host, sizeof(host), &tportno, srv) > 0) {
+ sin->sin_port = htons(tportno);
+ value = host;
+ }
+ }
+ hp = ast_gethostbyname(value, &ahp);
+ if (hp) {
+ memcpy(&sin->sin_addr, hp->h_addr, sizeof(sin->sin_addr));
+ } else {
+ ast_log(LOG_WARNING, "Unable to lookup '%s'\n", value);
+ return -1;
+ }
+ return 0;
+}
+
+struct dscp_codepoint {
+ char *name;
+ unsigned int space;
+};
+
+/* IANA registered DSCP codepoints */
+
+static const struct dscp_codepoint dscp_pool1[] = {
+ { "CS0", 0x00 },
+ { "CS1", 0x08 },
+ { "CS2", 0x10 },
+ { "CS3", 0x18 },
+ { "CS4", 0x20 },
+ { "CS5", 0x28 },
+ { "CS6", 0x30 },
+ { "CS7", 0x38 },
+ { "AF11", 0x0A },
+ { "AF12", 0x0C },
+ { "AF13", 0x0E },
+ { "AF21", 0x12 },
+ { "AF22", 0x14 },
+ { "AF23", 0x16 },
+ { "AF31", 0x1A },
+ { "AF32", 0x1C },
+ { "AF33", 0x1E },
+ { "AF41", 0x22 },
+ { "AF42", 0x24 },
+ { "AF43", 0x26 },
+ { "EF", 0x2E },
+};
+
+int ast_str2cos(const char *value, unsigned int *cos)
+{
+ int fval;
+
+ if (sscanf(value, "%d", &fval) == 1) {
+ if (fval < 8) {
+ *cos = fval;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+int ast_str2tos(const char *value, unsigned int *tos)
+{
+ int fval;
+ unsigned int x;
+
+ if (sscanf(value, "%i", &fval) == 1) {
+ *tos = fval & 0xFF;
+ return 0;
+ }
+
+ for (x = 0; x < sizeof(dscp_pool1) / sizeof(dscp_pool1[0]); x++) {
+ if (!strcasecmp(value, dscp_pool1[x].name)) {
+ *tos = dscp_pool1[x].space << 2;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+const char *ast_tos2str(unsigned int tos)
+{
+ unsigned int x;
+
+ for (x = 0; x < sizeof(dscp_pool1) / sizeof(dscp_pool1[0]); x++) {
+ if (dscp_pool1[x].space == (tos >> 2))
+ return dscp_pool1[x].name;
+ }
+
+ return "unknown";
+}
+
+int ast_get_ip(struct sockaddr_in *sin, const char *value)
+{
+ return ast_get_ip_or_srv(sin, value, NULL);
+}
+
+int ast_ouraddrfor(struct in_addr *them, struct in_addr *us)
+{
+ int s;
+ struct sockaddr_in sin;
+ socklen_t slen;
+
+ s = socket(PF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ ast_log(LOG_ERROR, "Cannot create socket\n");
+ return -1;
+ }
+ sin.sin_family = AF_INET;
+ sin.sin_port = 5060;
+ sin.sin_addr = *them;
+ if (connect(s, (struct sockaddr *)&sin, sizeof(sin))) {
+ ast_log(LOG_WARNING, "Cannot connect\n");
+ close(s);
+ return -1;
+ }
+ slen = sizeof(sin);
+ if (getsockname(s, (struct sockaddr *)&sin, &slen)) {
+ ast_log(LOG_WARNING, "Cannot get socket name\n");
+ close(s);
+ return -1;
+ }
+ close(s);
+ ast_debug(3, "Found IP address for this socket\n");
+ *us = sin.sin_addr;
+ return 0;
+}
+
+int ast_find_ourip(struct in_addr *ourip, struct sockaddr_in bindaddr)
+{
+ char ourhost[MAXHOSTNAMELEN] = "";
+ struct ast_hostent ahp;
+ struct hostent *hp;
+ struct in_addr saddr;
+
+ /* just use the bind address if it is nonzero */
+ if (ntohl(bindaddr.sin_addr.s_addr)) {
+ memcpy(ourip, &bindaddr.sin_addr, sizeof(*ourip));
+ ast_debug(3, "Attached to given IP address\n");
+ return 0;
+ }
+ /* try to use our hostname */
+ if (gethostname(ourhost, sizeof(ourhost) - 1)) {
+ ast_log(LOG_WARNING, "Unable to get hostname\n");
+ } else {
+ hp = ast_gethostbyname(ourhost, &ahp);
+ if (hp) {
+ memcpy(ourip, hp->h_addr, sizeof(*ourip));
+ ast_debug(3, "Found one IP address based on local hostname %s.\n", ourhost);
+ return 0;
+ }
+ }
+ ast_debug(3, "Trying to check A.ROOT-SERVERS.NET and get our IP address for that connection\n");
+ /* A.ROOT-SERVERS.NET. */
+ if (inet_aton("198.41.0.4", &saddr) && !ast_ouraddrfor(&saddr, ourip))
+ return 0;
+ ast_debug(3, "Failed to find any IP address for us\n");
+ return -1;
+}
+
diff --git a/trunk/main/adsistub.c b/trunk/main/adsistub.c
new file mode 100644
index 000000000..8f2908225
--- /dev/null
+++ b/trunk/main/adsistub.c
@@ -0,0 +1,77 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Christopher L. Wade <wade.christopher@gmail.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 "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/channel.h"
+#include "asterisk/adsi.h"
+
+#ifdef SKREP
+#define build_stub(func_name,...) \
+static int stub_ ## func_name(__VA_ARGS__) \
+{ \
+ if (option_debug > 4) \
+ ast_log(LOG_NOTICE, "ADSI support not loaded!\n"); \
+ return -1; \
+} \
+\
+int (*func_name)(__VA_ARGS__) = \
+ stub_ ## func_name;
+#endif
+#define build_stub(func_name,...) \
+static int stub_##func_name(__VA_ARGS__) \
+{ \
+ ast_debug(5, "ADSI support not loaded!\n"); \
+ return -1; \
+} \
+\
+int (*func_name)(__VA_ARGS__) = \
+ stub_##func_name;
+
+build_stub(ast_adsi_channel_init, struct ast_channel *chan)
+build_stub(ast_adsi_begin_download, struct ast_channel *chan, char *service, unsigned char *fdn, unsigned char *sec, int version)
+build_stub(ast_adsi_end_download, struct ast_channel *chan)
+build_stub(ast_adsi_channel_restore, struct ast_channel *chan)
+build_stub(ast_adsi_print, struct ast_channel *chan, char **lines, int *align, int voice)
+build_stub(ast_adsi_load_session, struct ast_channel *chan, unsigned char *app, int ver, int data)
+build_stub(ast_adsi_unload_session, struct ast_channel *chan)
+build_stub(ast_adsi_transmit_messages, struct ast_channel *chan, unsigned char **msg, int *msglen, int *msgtype)
+build_stub(ast_adsi_transmit_message, struct ast_channel *chan, unsigned char *msg, int msglen, int msgtype)
+build_stub(ast_adsi_transmit_message_full, struct ast_channel *chan, unsigned char *msg, int msglen, int msgtype, int dowait)
+build_stub(ast_adsi_read_encoded_dtmf, struct ast_channel *chan, unsigned char *buf, int maxlen)
+build_stub(ast_adsi_connect_session, unsigned char *buf, unsigned char *fdn, int ver)
+build_stub(ast_adsi_query_cpeid, unsigned char *buf)
+build_stub(ast_adsi_query_cpeinfo, unsigned char *buf)
+build_stub(ast_adsi_get_cpeid, struct ast_channel *chan, unsigned char *cpeid, int voice)
+build_stub(ast_adsi_get_cpeinfo, struct ast_channel *chan, int *width, int *height, int *buttons, int voice)
+build_stub(ast_adsi_download_connect, unsigned char *buf, char *service, unsigned char *fdn, unsigned char *sec, int ver)
+build_stub(ast_adsi_disconnect_session, unsigned char *buf)
+build_stub(ast_adsi_download_disconnect, unsigned char *buf)
+build_stub(ast_adsi_data_mode, unsigned char *buf)
+build_stub(ast_adsi_clear_soft_keys, unsigned char *buf)
+build_stub(ast_adsi_clear_screen, unsigned char *buf)
+build_stub(ast_adsi_voice_mode, unsigned char *buf, int when)
+build_stub(ast_adsi_available, struct ast_channel *chan)
+build_stub(ast_adsi_display, unsigned char *buf, int page, int line, int just, int wrap, char *col1, char *col2)
+build_stub(ast_adsi_set_line, unsigned char *buf, int page, int line)
+build_stub(ast_adsi_load_soft_key, unsigned char *buf, int key, const char *llabel, const char *slabel, char *ret, int data)
+build_stub(ast_adsi_set_keys, unsigned char *buf, unsigned char *keys)
+build_stub(ast_adsi_input_control, unsigned char *buf, int page, int line, int display, int format, int just)
+build_stub(ast_adsi_input_format, unsigned char *buf, int num, int dir, int wrap, char *format1, char *format2)
diff --git a/trunk/main/aescrypt.c b/trunk/main/aescrypt.c
new file mode 100644
index 000000000..7b34809bb
--- /dev/null
+++ b/trunk/main/aescrypt.c
@@ -0,0 +1,321 @@
+/*
+ ---------------------------------------------------------------------------
+ Copyright (c) 2003, Dr Brian Gladman <brg@gladman.me.uk>, Worcester, UK.
+ All rights reserved.
+
+ LICENSE TERMS
+
+ The free distribution and use of this software in both source and binary
+ form is allowed (with or without changes) provided that:
+
+ 1. distributions of this source code include the above copyright
+ notice, this list of conditions and the following disclaimer;
+
+ 2. distributions in binary form include the above copyright
+ notice, this list of conditions and the following disclaimer
+ in the documentation and/or other associated materials;
+
+ 3. the copyright holder's name is not used to endorse products
+ built using this software without specific written permission.
+
+ ALTERNATIVELY, provided that this notice is retained in full, this product
+ may be distributed under the terms of the GNU General Public License (GPL),
+ in which case the provisions of the GPL apply INSTEAD OF those given above.
+
+ DISCLAIMER
+
+ This software is provided 'as is' with no explicit or implied warranties
+ in respect of its properties, including, but not limited to, correctness
+ and/or fitness for purpose.
+ ---------------------------------------------------------------------------
+ Issue Date: 26/08/2003
+
+*/
+
+/*! \file
+ *
+ * \brief This file contains the code for implementing encryption and decryption
+ * for AES (Rijndael) for block and key sizes of 16, 24 and 32 bytes. It
+ * can optionally be replaced by code written in assembler using NASM. For
+ * further details see the file aesopt.h
+ *
+ * \author Dr Brian Gladman <brg@gladman.me.uk>
+ */
+
+#if defined(__cplusplus)
+extern "C"
+{
+#endif
+
+#ifndef HAVE_CRYPTO
+
+#include "aesopt.h"
+
+#define si(y,x,k,c) (s(y,c) = word_in(x, c) ^ (k)[c])
+#define so(y,x,c) word_out(y, c, s(x,c))
+
+#if defined(ARRAYS)
+#define locals(y,x) x[4],y[4]
+#else
+#define locals(y,x) x##0,x##1,x##2,x##3,y##0,y##1,y##2,y##3
+#endif
+
+#define l_copy(y, x) s(y,0) = s(x,0); s(y,1) = s(x,1); \
+ s(y,2) = s(x,2); s(y,3) = s(x,3);
+#define state_in(y,x,k) si(y,x,k,0); si(y,x,k,1); si(y,x,k,2); si(y,x,k,3)
+#define state_out(y,x) so(y,x,0); so(y,x,1); so(y,x,2); so(y,x,3)
+#define round(rm,y,x,k) rm(y,x,k,0); rm(y,x,k,1); rm(y,x,k,2); rm(y,x,k,3)
+
+#if defined(ENCRYPTION) && !defined(AES_ASM)
+
+/* Visual C++ .Net v7.1 provides the fastest encryption code when using
+ Pentium optimiation with small code but this is poor for decryption
+ so we need to control this with the following VC++ pragmas
+*/
+
+#if defined(_MSC_VER)
+#pragma optimize( "s", on )
+#endif
+
+/* Given the column (c) of the output state variable, the following
+ macros give the input state variables which are needed in its
+ computation for each row (r) of the state. All the alternative
+ macros give the same end values but expand into different ways
+ of calculating these values. In particular the complex macro
+ used for dynamically variable block sizes is designed to expand
+ to a compile time constant whenever possible but will expand to
+ conditional clauses on some branches (I am grateful to Frank
+ Yellin for this construction)
+*/
+
+#define fwd_var(x,r,c)\
+ ( r == 0 ? ( c == 0 ? s(x,0) : c == 1 ? s(x,1) : c == 2 ? s(x,2) : s(x,3))\
+ : r == 1 ? ( c == 0 ? s(x,1) : c == 1 ? s(x,2) : c == 2 ? s(x,3) : s(x,0))\
+ : r == 2 ? ( c == 0 ? s(x,2) : c == 1 ? s(x,3) : c == 2 ? s(x,0) : s(x,1))\
+ : ( c == 0 ? s(x,3) : c == 1 ? s(x,0) : c == 2 ? s(x,1) : s(x,2)))
+
+#if defined(FT4_SET)
+#undef dec_fmvars
+#define fwd_rnd(y,x,k,c) (s(y,c) = (k)[c] ^ four_tables(x,t_use(f,n),fwd_var,rf1,c))
+#elif defined(FT1_SET)
+#undef dec_fmvars
+#define fwd_rnd(y,x,k,c) (s(y,c) = (k)[c] ^ one_table(x,upr,t_use(f,n),fwd_var,rf1,c))
+#else
+#define fwd_rnd(y,x,k,c) (s(y,c) = (k)[c] ^ fwd_mcol(no_table(x,t_use(s,box),fwd_var,rf1,c)))
+#endif
+
+#if defined(FL4_SET)
+#define fwd_lrnd(y,x,k,c) (s(y,c) = (k)[c] ^ four_tables(x,t_use(f,l),fwd_var,rf1,c))
+#elif defined(FL1_SET)
+#define fwd_lrnd(y,x,k,c) (s(y,c) = (k)[c] ^ one_table(x,ups,t_use(f,l),fwd_var,rf1,c))
+#else
+#define fwd_lrnd(y,x,k,c) (s(y,c) = (k)[c] ^ no_table(x,t_use(s,box),fwd_var,rf1,c))
+#endif
+
+aes_rval aes_encrypt(const void *in_blk, void *out_blk, const aes_encrypt_ctx cx[1])
+{ aes_32t locals(b0, b1);
+ const aes_32t *kp = cx->ks;
+#ifdef dec_fmvars
+ dec_fmvars; /* declare variables for fwd_mcol() if needed */
+#endif
+
+ aes_32t nr = (kp[45] ^ kp[52] ^ kp[53] ? kp[52] : 14);
+
+#ifdef AES_ERR_CHK
+ if( (nr != 10 || !(kp[0] | kp[3] | kp[4]))
+ && (nr != 12 || !(kp[0] | kp[5] | kp[6]))
+ && (nr != 14 || !(kp[0] | kp[7] | kp[8])) )
+ return aes_error;
+#endif
+
+ state_in(b0, in_blk, kp);
+
+#if (ENC_UNROLL == FULL)
+
+ switch(nr)
+ {
+ case 14:
+ round(fwd_rnd, b1, b0, kp + 1 * N_COLS);
+ round(fwd_rnd, b0, b1, kp + 2 * N_COLS);
+ kp += 2 * N_COLS;
+ case 12:
+ round(fwd_rnd, b1, b0, kp + 1 * N_COLS);
+ round(fwd_rnd, b0, b1, kp + 2 * N_COLS);
+ kp += 2 * N_COLS;
+ case 10:
+ round(fwd_rnd, b1, b0, kp + 1 * N_COLS);
+ round(fwd_rnd, b0, b1, kp + 2 * N_COLS);
+ round(fwd_rnd, b1, b0, kp + 3 * N_COLS);
+ round(fwd_rnd, b0, b1, kp + 4 * N_COLS);
+ round(fwd_rnd, b1, b0, kp + 5 * N_COLS);
+ round(fwd_rnd, b0, b1, kp + 6 * N_COLS);
+ round(fwd_rnd, b1, b0, kp + 7 * N_COLS);
+ round(fwd_rnd, b0, b1, kp + 8 * N_COLS);
+ round(fwd_rnd, b1, b0, kp + 9 * N_COLS);
+ round(fwd_lrnd, b0, b1, kp +10 * N_COLS);
+ }
+
+#else
+
+#if (ENC_UNROLL == PARTIAL)
+ { aes_32t rnd;
+ for(rnd = 0; rnd < (nr >> 1) - 1; ++rnd)
+ {
+ kp += N_COLS;
+ round(fwd_rnd, b1, b0, kp);
+ kp += N_COLS;
+ round(fwd_rnd, b0, b1, kp);
+ }
+ kp += N_COLS;
+ round(fwd_rnd, b1, b0, kp);
+#else
+ { aes_32t rnd;
+ for(rnd = 0; rnd < nr - 1; ++rnd)
+ {
+ kp += N_COLS;
+ round(fwd_rnd, b1, b0, kp);
+ l_copy(b0, b1);
+ }
+#endif
+ kp += N_COLS;
+ round(fwd_lrnd, b0, b1, kp);
+ }
+#endif
+
+ state_out(out_blk, b0);
+#ifdef AES_ERR_CHK
+ return aes_good;
+#endif
+}
+
+#endif
+
+#if defined(DECRYPTION) && !defined(AES_ASM)
+
+/* Visual C++ .Net v7.1 provides the fastest encryption code when using
+ Pentium optimiation with small code but this is poor for decryption
+ so we need to control this with the following VC++ pragmas
+*/
+
+#if defined(_MSC_VER)
+#pragma optimize( "t", on )
+#endif
+
+/* Given the column (c) of the output state variable, the following
+ macros give the input state variables which are needed in its
+ computation for each row (r) of the state. All the alternative
+ macros give the same end values but expand into different ways
+ of calculating these values. In particular the complex macro
+ used for dynamically variable block sizes is designed to expand
+ to a compile time constant whenever possible but will expand to
+ conditional clauses on some branches (I am grateful to Frank
+ Yellin for this construction)
+*/
+
+#define inv_var(x,r,c)\
+ ( r == 0 ? ( c == 0 ? s(x,0) : c == 1 ? s(x,1) : c == 2 ? s(x,2) : s(x,3))\
+ : r == 1 ? ( c == 0 ? s(x,3) : c == 1 ? s(x,0) : c == 2 ? s(x,1) : s(x,2))\
+ : r == 2 ? ( c == 0 ? s(x,2) : c == 1 ? s(x,3) : c == 2 ? s(x,0) : s(x,1))\
+ : ( c == 0 ? s(x,1) : c == 1 ? s(x,2) : c == 2 ? s(x,3) : s(x,0)))
+
+#if defined(IT4_SET)
+#undef dec_imvars
+#define inv_rnd(y,x,k,c) (s(y,c) = (k)[c] ^ four_tables(x,t_use(i,n),inv_var,rf1,c))
+#elif defined(IT1_SET)
+#undef dec_imvars
+#define inv_rnd(y,x,k,c) (s(y,c) = (k)[c] ^ one_table(x,upr,t_use(i,n),inv_var,rf1,c))
+#else
+#define inv_rnd(y,x,k,c) (s(y,c) = inv_mcol((k)[c] ^ no_table(x,t_use(i,box),inv_var,rf1,c)))
+#endif
+
+#if defined(IL4_SET)
+#define inv_lrnd(y,x,k,c) (s(y,c) = (k)[c] ^ four_tables(x,t_use(i,l),inv_var,rf1,c))
+#elif defined(IL1_SET)
+#define inv_lrnd(y,x,k,c) (s(y,c) = (k)[c] ^ one_table(x,ups,t_use(i,l),inv_var,rf1,c))
+#else
+#define inv_lrnd(y,x,k,c) (s(y,c) = (k)[c] ^ no_table(x,t_use(i,box),inv_var,rf1,c))
+#endif
+
+aes_rval aes_decrypt(const void *in_blk, void *out_blk, const aes_decrypt_ctx cx[1])
+{ aes_32t locals(b0, b1);
+#ifdef dec_imvars
+ dec_imvars; /* declare variables for inv_mcol() if needed */
+#endif
+
+ aes_32t nr = (cx->ks[45] ^ cx->ks[52] ^ cx->ks[53] ? cx->ks[52] : 14);
+ const aes_32t *kp = cx->ks + nr * N_COLS;
+
+#ifdef AES_ERR_CHK
+ if( (nr != 10 || !(cx->ks[0] | cx->ks[3] | cx->ks[4]))
+ && (nr != 12 || !(cx->ks[0] | cx->ks[5] | cx->ks[6]))
+ && (nr != 14 || !(cx->ks[0] | cx->ks[7] | cx->ks[8])) )
+ return aes_error;
+#endif
+
+ state_in(b0, in_blk, kp);
+
+#if (DEC_UNROLL == FULL)
+
+ switch(nr)
+ {
+ case 14:
+ round(inv_rnd, b1, b0, kp - 1 * N_COLS);
+ round(inv_rnd, b0, b1, kp - 2 * N_COLS);
+ kp -= 2 * N_COLS;
+ case 12:
+ round(inv_rnd, b1, b0, kp - 1 * N_COLS);
+ round(inv_rnd, b0, b1, kp - 2 * N_COLS);
+ kp -= 2 * N_COLS;
+ case 10:
+ round(inv_rnd, b1, b0, kp - 1 * N_COLS);
+ round(inv_rnd, b0, b1, kp - 2 * N_COLS);
+ round(inv_rnd, b1, b0, kp - 3 * N_COLS);
+ round(inv_rnd, b0, b1, kp - 4 * N_COLS);
+ round(inv_rnd, b1, b0, kp - 5 * N_COLS);
+ round(inv_rnd, b0, b1, kp - 6 * N_COLS);
+ round(inv_rnd, b1, b0, kp - 7 * N_COLS);
+ round(inv_rnd, b0, b1, kp - 8 * N_COLS);
+ round(inv_rnd, b1, b0, kp - 9 * N_COLS);
+ round(inv_lrnd, b0, b1, kp - 10 * N_COLS);
+ }
+
+#else
+
+#if (DEC_UNROLL == PARTIAL)
+ { aes_32t rnd;
+ for(rnd = 0; rnd < (nr >> 1) - 1; ++rnd)
+ {
+ kp -= N_COLS;
+ round(inv_rnd, b1, b0, kp);
+ kp -= N_COLS;
+ round(inv_rnd, b0, b1, kp);
+ }
+ kp -= N_COLS;
+ round(inv_rnd, b1, b0, kp);
+#else
+ { aes_32t rnd;
+ for(rnd = 0; rnd < nr - 1; ++rnd)
+ {
+ kp -= N_COLS;
+ round(inv_rnd, b1, b0, kp);
+ l_copy(b0, b1);
+ }
+#endif
+ kp -= N_COLS;
+ round(inv_lrnd, b0, b1, kp);
+ }
+#endif
+
+ state_out(out_blk, b0);
+#ifdef AES_ERR_CHK
+ return aes_good;
+#endif
+}
+
+#endif
+
+#endif /* !HAVE_CRYPTO */
+
+#if defined(__cplusplus)
+}
+#endif
diff --git a/trunk/main/aeskey.c b/trunk/main/aeskey.c
new file mode 100644
index 000000000..cd0c7faf8
--- /dev/null
+++ b/trunk/main/aeskey.c
@@ -0,0 +1,473 @@
+/*
+ ---------------------------------------------------------------------------
+ Copyright (c) 2003, Dr Brian Gladman <brg@gladman.me.uk>, Worcester, UK.
+ All rights reserved.
+
+ LICENSE TERMS
+
+ The free distribution and use of this software in both source and binary
+ form is allowed (with or without changes) provided that:
+
+ 1. distributions of this source code include the above copyright
+ notice, this list of conditions and the following disclaimer;
+
+ 2. distributions in binary form include the above copyright
+ notice, this list of conditions and the following disclaimer
+ in the documentation and/or other associated materials;
+
+ 3. the copyright holder's name is not used to endorse products
+ built using this software without specific written permission.
+
+ ALTERNATIVELY, provided that this notice is retained in full, this product
+ may be distributed under the terms of the GNU General Public License (GPL),
+ in which case the provisions of the GPL apply INSTEAD OF those given above.
+
+ DISCLAIMER
+
+ This software is provided 'as is' with no explicit or implied warranties
+ in respect of its properties, including, but not limited to, correctness
+ and/or fitness for purpose.
+ ---------------------------------------------------------------------------
+ Issue Date: 26/08/2003
+
+*/
+
+/*! \file
+ *
+ * \brief This file contains the code for implementing the key schedule for AES
+ * (Rijndael) for block and key sizes of 16, 24, and 32 bytes. See aesopt.h
+ * for further details including optimisation.
+ *
+ * \author Dr Brian Gladman <brg@gladman.me.uk>
+ */
+
+#if defined(__cplusplus)
+extern "C"
+{
+#endif
+
+#ifndef HAVE_CRYPTO
+
+#include "aesopt.h"
+
+/* Initialise the key schedule from the user supplied key. The key
+ length can be specified in bytes, with legal values of 16, 24
+ and 32, or in bits, with legal values of 128, 192 and 256. These
+ values correspond with Nk values of 4, 6 and 8 respectively.
+
+ The following macros implement a single cycle in the key
+ schedule generation process. The number of cycles needed
+ for each cx->n_col and nk value is:
+
+ nk = 4 5 6 7 8
+ ------------------------------
+ cx->n_col = 4 10 9 8 7 7
+ cx->n_col = 5 14 11 10 9 9
+ cx->n_col = 6 19 15 12 11 11
+ cx->n_col = 7 21 19 16 13 14
+ cx->n_col = 8 29 23 19 17 14
+*/
+
+#define ke4(k,i) \
+{ k[4*(i)+4] = ss[0] ^= ls_box(ss[3],3) ^ t_use(r,c)[i]; k[4*(i)+5] = ss[1] ^= ss[0]; \
+ k[4*(i)+6] = ss[2] ^= ss[1]; k[4*(i)+7] = ss[3] ^= ss[2]; \
+}
+#define kel4(k,i) \
+{ k[4*(i)+4] = ss[0] ^= ls_box(ss[3],3) ^ t_use(r,c)[i]; k[4*(i)+5] = ss[1] ^= ss[0]; \
+ k[4*(i)+6] = ss[2] ^= ss[1]; k[4*(i)+7] = ss[3] ^= ss[2]; \
+}
+
+#define ke6(k,i) \
+{ k[6*(i)+ 6] = ss[0] ^= ls_box(ss[5],3) ^ t_use(r,c)[i]; k[6*(i)+ 7] = ss[1] ^= ss[0]; \
+ k[6*(i)+ 8] = ss[2] ^= ss[1]; k[6*(i)+ 9] = ss[3] ^= ss[2]; \
+ k[6*(i)+10] = ss[4] ^= ss[3]; k[6*(i)+11] = ss[5] ^= ss[4]; \
+}
+#define kel6(k,i) \
+{ k[6*(i)+ 6] = ss[0] ^= ls_box(ss[5],3) ^ t_use(r,c)[i]; k[6*(i)+ 7] = ss[1] ^= ss[0]; \
+ k[6*(i)+ 8] = ss[2] ^= ss[1]; k[6*(i)+ 9] = ss[3] ^= ss[2]; \
+}
+
+#define ke8(k,i) \
+{ k[8*(i)+ 8] = ss[0] ^= ls_box(ss[7],3) ^ t_use(r,c)[i]; k[8*(i)+ 9] = ss[1] ^= ss[0]; \
+ k[8*(i)+10] = ss[2] ^= ss[1]; k[8*(i)+11] = ss[3] ^= ss[2]; \
+ k[8*(i)+12] = ss[4] ^= ls_box(ss[3],0); k[8*(i)+13] = ss[5] ^= ss[4]; \
+ k[8*(i)+14] = ss[6] ^= ss[5]; k[8*(i)+15] = ss[7] ^= ss[6]; \
+}
+#define kel8(k,i) \
+{ k[8*(i)+ 8] = ss[0] ^= ls_box(ss[7],3) ^ t_use(r,c)[i]; k[8*(i)+ 9] = ss[1] ^= ss[0]; \
+ k[8*(i)+10] = ss[2] ^= ss[1]; k[8*(i)+11] = ss[3] ^= ss[2]; \
+}
+
+#if defined(ENCRYPTION_KEY_SCHEDULE)
+
+#if defined(AES_128) || defined(AES_VAR)
+
+aes_rval aes_encrypt_key128(const void *in_key, aes_encrypt_ctx cx[1])
+{ aes_32t ss[4];
+
+ cx->ks[0] = ss[0] = word_in(in_key, 0);
+ cx->ks[1] = ss[1] = word_in(in_key, 1);
+ cx->ks[2] = ss[2] = word_in(in_key, 2);
+ cx->ks[3] = ss[3] = word_in(in_key, 3);
+
+#if ENC_UNROLL == NONE
+ { aes_32t i;
+
+ for(i = 0; i < ((11 * N_COLS - 1) / 4); ++i)
+ ke4(cx->ks, i);
+ }
+#else
+ ke4(cx->ks, 0); ke4(cx->ks, 1);
+ ke4(cx->ks, 2); ke4(cx->ks, 3);
+ ke4(cx->ks, 4); ke4(cx->ks, 5);
+ ke4(cx->ks, 6); ke4(cx->ks, 7);
+ ke4(cx->ks, 8); kel4(cx->ks, 9);
+#endif
+
+ /* cx->ks[45] ^ cx->ks[52] ^ cx->ks[53] is zero for a 256 bit */
+ /* key and must be non-zero for 128 and 192 bits keys */
+ cx->ks[53] = cx->ks[45] = 0;
+ cx->ks[52] = 10;
+#ifdef AES_ERR_CHK
+ return aes_good;
+#endif
+}
+
+#endif
+
+#if defined(AES_192) || defined(AES_VAR)
+
+aes_rval aes_encrypt_key192(const void *in_key, aes_encrypt_ctx cx[1])
+{ aes_32t ss[6];
+
+ cx->ks[0] = ss[0] = word_in(in_key, 0);
+ cx->ks[1] = ss[1] = word_in(in_key, 1);
+ cx->ks[2] = ss[2] = word_in(in_key, 2);
+ cx->ks[3] = ss[3] = word_in(in_key, 3);
+ cx->ks[4] = ss[4] = word_in(in_key, 4);
+ cx->ks[5] = ss[5] = word_in(in_key, 5);
+
+#if ENC_UNROLL == NONE
+ { aes_32t i;
+
+ for(i = 0; i < (13 * N_COLS - 1) / 6; ++i)
+ ke6(cx->ks, i);
+ }
+#else
+ ke6(cx->ks, 0); ke6(cx->ks, 1);
+ ke6(cx->ks, 2); ke6(cx->ks, 3);
+ ke6(cx->ks, 4); ke6(cx->ks, 5);
+ ke6(cx->ks, 6); kel6(cx->ks, 7);
+#endif
+
+ /* cx->ks[45] ^ cx->ks[52] ^ cx->ks[53] is zero for a 256 bit */
+ /* key and must be non-zero for 128 and 192 bits keys */
+ cx->ks[53] = cx->ks[45];
+ cx->ks[52] = 12;
+#ifdef AES_ERR_CHK
+ return aes_good;
+#endif
+}
+
+#endif
+
+#if defined(AES_256) || defined(AES_VAR)
+
+aes_rval aes_encrypt_key256(const void *in_key, aes_encrypt_ctx cx[1])
+{ aes_32t ss[8];
+
+ cx->ks[0] = ss[0] = word_in(in_key, 0);
+ cx->ks[1] = ss[1] = word_in(in_key, 1);
+ cx->ks[2] = ss[2] = word_in(in_key, 2);
+ cx->ks[3] = ss[3] = word_in(in_key, 3);
+ cx->ks[4] = ss[4] = word_in(in_key, 4);
+ cx->ks[5] = ss[5] = word_in(in_key, 5);
+ cx->ks[6] = ss[6] = word_in(in_key, 6);
+ cx->ks[7] = ss[7] = word_in(in_key, 7);
+
+#if ENC_UNROLL == NONE
+ { aes_32t i;
+
+ for(i = 0; i < (15 * N_COLS - 1) / 8; ++i)
+ ke8(cx->ks, i);
+ }
+#else
+ ke8(cx->ks, 0); ke8(cx->ks, 1);
+ ke8(cx->ks, 2); ke8(cx->ks, 3);
+ ke8(cx->ks, 4); ke8(cx->ks, 5);
+ kel8(cx->ks, 6);
+#endif
+#ifdef AES_ERR_CHK
+ return aes_good;
+#endif
+}
+
+#endif
+
+#if defined(AES_VAR)
+
+aes_rval aes_encrypt_key(const void *in_key, int key_len, aes_encrypt_ctx cx[1])
+{
+ switch(key_len)
+ {
+#ifdef AES_ERR_CHK
+ case 16: case 128: return aes_encrypt_key128(in_key, cx);
+ case 24: case 192: return aes_encrypt_key192(in_key, cx);
+ case 32: case 256: return aes_encrypt_key256(in_key, cx);
+ default: return aes_error;
+#else
+ case 16: case 128: aes_encrypt_key128(in_key, cx); return;
+ case 24: case 192: aes_encrypt_key192(in_key, cx); return;
+ case 32: case 256: aes_encrypt_key256(in_key, cx); return;
+#endif
+ }
+}
+
+#endif
+
+#endif
+
+#if defined(DECRYPTION_KEY_SCHEDULE)
+
+#if DEC_ROUND == NO_TABLES
+#define ff(x) (x)
+#else
+#define ff(x) inv_mcol(x)
+#ifdef dec_imvars
+#define d_vars dec_imvars
+#endif
+#endif
+
+#if 1
+#define kdf4(k,i) \
+{ ss[0] = ss[0] ^ ss[2] ^ ss[1] ^ ss[3]; ss[1] = ss[1] ^ ss[3]; ss[2] = ss[2] ^ ss[3]; ss[3] = ss[3]; \
+ ss[4] = ls_box(ss[(i+3) % 4], 3) ^ t_use(r,c)[i]; ss[i % 4] ^= ss[4]; \
+ ss[4] ^= k[4*(i)]; k[4*(i)+4] = ff(ss[4]); ss[4] ^= k[4*(i)+1]; k[4*(i)+5] = ff(ss[4]); \
+ ss[4] ^= k[4*(i)+2]; k[4*(i)+6] = ff(ss[4]); ss[4] ^= k[4*(i)+3]; k[4*(i)+7] = ff(ss[4]); \
+}
+#define kd4(k,i) \
+{ ss[4] = ls_box(ss[(i+3) % 4], 3) ^ t_use(r,c)[i]; ss[i % 4] ^= ss[4]; ss[4] = ff(ss[4]); \
+ k[4*(i)+4] = ss[4] ^= k[4*(i)]; k[4*(i)+5] = ss[4] ^= k[4*(i)+1]; \
+ k[4*(i)+6] = ss[4] ^= k[4*(i)+2]; k[4*(i)+7] = ss[4] ^= k[4*(i)+3]; \
+}
+#define kdl4(k,i) \
+{ ss[4] = ls_box(ss[(i+3) % 4], 3) ^ t_use(r,c)[i]; ss[i % 4] ^= ss[4]; \
+ k[4*(i)+4] = (ss[0] ^= ss[1]) ^ ss[2] ^ ss[3]; k[4*(i)+5] = ss[1] ^ ss[3]; \
+ k[4*(i)+6] = ss[0]; k[4*(i)+7] = ss[1]; \
+}
+#else
+#define kdf4(k,i) \
+{ ss[0] ^= ls_box(ss[3],3) ^ t_use(r,c)[i]; k[4*(i)+ 4] = ff(ss[0]); ss[1] ^= ss[0]; k[4*(i)+ 5] = ff(ss[1]); \
+ ss[2] ^= ss[1]; k[4*(i)+ 6] = ff(ss[2]); ss[3] ^= ss[2]; k[4*(i)+ 7] = ff(ss[3]); \
+}
+#define kd4(k,i) \
+{ ss[4] = ls_box(ss[3],3) ^ t_use(r,c)[i]; \
+ ss[0] ^= ss[4]; ss[4] = ff(ss[4]); k[4*(i)+ 4] = ss[4] ^= k[4*(i)]; \
+ ss[1] ^= ss[0]; k[4*(i)+ 5] = ss[4] ^= k[4*(i)+ 1]; \
+ ss[2] ^= ss[1]; k[4*(i)+ 6] = ss[4] ^= k[4*(i)+ 2]; \
+ ss[3] ^= ss[2]; k[4*(i)+ 7] = ss[4] ^= k[4*(i)+ 3]; \
+}
+#define kdl4(k,i) \
+{ ss[0] ^= ls_box(ss[3],3) ^ t_use(r,c)[i]; k[4*(i)+ 4] = ss[0]; ss[1] ^= ss[0]; k[4*(i)+ 5] = ss[1]; \
+ ss[2] ^= ss[1]; k[4*(i)+ 6] = ss[2]; ss[3] ^= ss[2]; k[4*(i)+ 7] = ss[3]; \
+}
+#endif
+
+#define kdf6(k,i) \
+{ ss[0] ^= ls_box(ss[5],3) ^ t_use(r,c)[i]; k[6*(i)+ 6] = ff(ss[0]); ss[1] ^= ss[0]; k[6*(i)+ 7] = ff(ss[1]); \
+ ss[2] ^= ss[1]; k[6*(i)+ 8] = ff(ss[2]); ss[3] ^= ss[2]; k[6*(i)+ 9] = ff(ss[3]); \
+ ss[4] ^= ss[3]; k[6*(i)+10] = ff(ss[4]); ss[5] ^= ss[4]; k[6*(i)+11] = ff(ss[5]); \
+}
+#define kd6(k,i) \
+{ ss[6] = ls_box(ss[5],3) ^ t_use(r,c)[i]; \
+ ss[0] ^= ss[6]; ss[6] = ff(ss[6]); k[6*(i)+ 6] = ss[6] ^= k[6*(i)]; \
+ ss[1] ^= ss[0]; k[6*(i)+ 7] = ss[6] ^= k[6*(i)+ 1]; \
+ ss[2] ^= ss[1]; k[6*(i)+ 8] = ss[6] ^= k[6*(i)+ 2]; \
+ ss[3] ^= ss[2]; k[6*(i)+ 9] = ss[6] ^= k[6*(i)+ 3]; \
+ ss[4] ^= ss[3]; k[6*(i)+10] = ss[6] ^= k[6*(i)+ 4]; \
+ ss[5] ^= ss[4]; k[6*(i)+11] = ss[6] ^= k[6*(i)+ 5]; \
+}
+#define kdl6(k,i) \
+{ ss[0] ^= ls_box(ss[5],3) ^ t_use(r,c)[i]; k[6*(i)+ 6] = ss[0]; ss[1] ^= ss[0]; k[6*(i)+ 7] = ss[1]; \
+ ss[2] ^= ss[1]; k[6*(i)+ 8] = ss[2]; ss[3] ^= ss[2]; k[6*(i)+ 9] = ss[3]; \
+}
+
+#define kdf8(k,i) \
+{ ss[0] ^= ls_box(ss[7],3) ^ t_use(r,c)[i]; k[8*(i)+ 8] = ff(ss[0]); ss[1] ^= ss[0]; k[8*(i)+ 9] = ff(ss[1]); \
+ ss[2] ^= ss[1]; k[8*(i)+10] = ff(ss[2]); ss[3] ^= ss[2]; k[8*(i)+11] = ff(ss[3]); \
+ ss[4] ^= ls_box(ss[3],0); k[8*(i)+12] = ff(ss[4]); ss[5] ^= ss[4]; k[8*(i)+13] = ff(ss[5]); \
+ ss[6] ^= ss[5]; k[8*(i)+14] = ff(ss[6]); ss[7] ^= ss[6]; k[8*(i)+15] = ff(ss[7]); \
+}
+#define kd8(k,i) \
+{ aes_32t g = ls_box(ss[7],3) ^ t_use(r,c)[i]; \
+ ss[0] ^= g; g = ff(g); k[8*(i)+ 8] = g ^= k[8*(i)]; \
+ ss[1] ^= ss[0]; k[8*(i)+ 9] = g ^= k[8*(i)+ 1]; \
+ ss[2] ^= ss[1]; k[8*(i)+10] = g ^= k[8*(i)+ 2]; \
+ ss[3] ^= ss[2]; k[8*(i)+11] = g ^= k[8*(i)+ 3]; \
+ g = ls_box(ss[3],0); \
+ ss[4] ^= g; g = ff(g); k[8*(i)+12] = g ^= k[8*(i)+ 4]; \
+ ss[5] ^= ss[4]; k[8*(i)+13] = g ^= k[8*(i)+ 5]; \
+ ss[6] ^= ss[5]; k[8*(i)+14] = g ^= k[8*(i)+ 6]; \
+ ss[7] ^= ss[6]; k[8*(i)+15] = g ^= k[8*(i)+ 7]; \
+}
+#define kdl8(k,i) \
+{ ss[0] ^= ls_box(ss[7],3) ^ t_use(r,c)[i]; k[8*(i)+ 8] = ss[0]; ss[1] ^= ss[0]; k[8*(i)+ 9] = ss[1]; \
+ ss[2] ^= ss[1]; k[8*(i)+10] = ss[2]; ss[3] ^= ss[2]; k[8*(i)+11] = ss[3]; \
+}
+
+#if defined(AES_128) || defined(AES_VAR)
+
+aes_rval aes_decrypt_key128(const void *in_key, aes_decrypt_ctx cx[1])
+{ aes_32t ss[5];
+#ifdef d_vars
+ d_vars;
+#endif
+ cx->ks[0] = ss[0] = word_in(in_key, 0);
+ cx->ks[1] = ss[1] = word_in(in_key, 1);
+ cx->ks[2] = ss[2] = word_in(in_key, 2);
+ cx->ks[3] = ss[3] = word_in(in_key, 3);
+
+#if DEC_UNROLL == NONE
+ { aes_32t i;
+
+ for(i = 0; i < (11 * N_COLS - 1) / 4; ++i)
+ ke4(cx->ks, i);
+#if !(DEC_ROUND == NO_TABLES)
+ for(i = N_COLS; i < 10 * N_COLS; ++i)
+ cx->ks[i] = inv_mcol(cx->ks[i]);
+#endif
+ }
+#else
+ kdf4(cx->ks, 0); kd4(cx->ks, 1);
+ kd4(cx->ks, 2); kd4(cx->ks, 3);
+ kd4(cx->ks, 4); kd4(cx->ks, 5);
+ kd4(cx->ks, 6); kd4(cx->ks, 7);
+ kd4(cx->ks, 8); kdl4(cx->ks, 9);
+#endif
+
+ /* cx->ks[45] ^ cx->ks[52] ^ cx->ks[53] is zero for a 256 bit */
+ /* key and must be non-zero for 128 and 192 bits keys */
+ cx->ks[53] = cx->ks[45] = 0;
+ cx->ks[52] = 10;
+#ifdef AES_ERR_CHK
+ return aes_good;
+#endif
+}
+
+#endif
+
+#if defined(AES_192) || defined(AES_VAR)
+
+aes_rval aes_decrypt_key192(const void *in_key, aes_decrypt_ctx cx[1])
+{ aes_32t ss[7];
+#ifdef d_vars
+ d_vars;
+#endif
+ cx->ks[0] = ss[0] = word_in(in_key, 0);
+ cx->ks[1] = ss[1] = word_in(in_key, 1);
+ cx->ks[2] = ss[2] = word_in(in_key, 2);
+ cx->ks[3] = ss[3] = word_in(in_key, 3);
+
+#if DEC_UNROLL == NONE
+ cx->ks[4] = ss[4] = word_in(in_key, 4);
+ cx->ks[5] = ss[5] = word_in(in_key, 5);
+ { aes_32t i;
+
+ for(i = 0; i < (13 * N_COLS - 1) / 6; ++i)
+ ke6(cx->ks, i);
+#if !(DEC_ROUND == NO_TABLES)
+ for(i = N_COLS; i < 12 * N_COLS; ++i)
+ cx->ks[i] = inv_mcol(cx->ks[i]);
+#endif
+ }
+#else
+ cx->ks[4] = ff(ss[4] = word_in(in_key, 4));
+ cx->ks[5] = ff(ss[5] = word_in(in_key, 5));
+ kdf6(cx->ks, 0); kd6(cx->ks, 1);
+ kd6(cx->ks, 2); kd6(cx->ks, 3);
+ kd6(cx->ks, 4); kd6(cx->ks, 5);
+ kd6(cx->ks, 6); kdl6(cx->ks, 7);
+#endif
+
+ /* cx->ks[45] ^ cx->ks[52] ^ cx->ks[53] is zero for a 256 bit */
+ /* key and must be non-zero for 128 and 192 bits keys */
+ cx->ks[53] = cx->ks[45];
+ cx->ks[52] = 12;
+#ifdef AES_ERR_CHK
+ return aes_good;
+#endif
+}
+
+#endif
+
+#if defined(AES_256) || defined(AES_VAR)
+
+aes_rval aes_decrypt_key256(const void *in_key, aes_decrypt_ctx cx[1])
+{ aes_32t ss[8];
+#ifdef d_vars
+ d_vars;
+#endif
+ cx->ks[0] = ss[0] = word_in(in_key, 0);
+ cx->ks[1] = ss[1] = word_in(in_key, 1);
+ cx->ks[2] = ss[2] = word_in(in_key, 2);
+ cx->ks[3] = ss[3] = word_in(in_key, 3);
+
+#if DEC_UNROLL == NONE
+ cx->ks[4] = ss[4] = word_in(in_key, 4);
+ cx->ks[5] = ss[5] = word_in(in_key, 5);
+ cx->ks[6] = ss[6] = word_in(in_key, 6);
+ cx->ks[7] = ss[7] = word_in(in_key, 7);
+ { aes_32t i;
+
+ for(i = 0; i < (15 * N_COLS - 1) / 8; ++i)
+ ke8(cx->ks, i);
+#if !(DEC_ROUND == NO_TABLES)
+ for(i = N_COLS; i < 14 * N_COLS; ++i)
+ cx->ks[i] = inv_mcol(cx->ks[i]);
+#endif
+ }
+#else
+ cx->ks[4] = ff(ss[4] = word_in(in_key, 4));
+ cx->ks[5] = ff(ss[5] = word_in(in_key, 5));
+ cx->ks[6] = ff(ss[6] = word_in(in_key, 6));
+ cx->ks[7] = ff(ss[7] = word_in(in_key, 7));
+ kdf8(cx->ks, 0); kd8(cx->ks, 1);
+ kd8(cx->ks, 2); kd8(cx->ks, 3);
+ kd8(cx->ks, 4); kd8(cx->ks, 5);
+ kdl8(cx->ks, 6);
+#endif
+#ifdef AES_ERR_CHK
+ return aes_good;
+#endif
+}
+
+#endif
+
+#if defined(AES_VAR)
+
+aes_rval aes_decrypt_key(const void *in_key, int key_len, aes_decrypt_ctx cx[1])
+{
+ switch(key_len)
+ {
+#ifdef AES_ERR_CHK
+ case 16: case 128: return aes_decrypt_key128(in_key, cx);
+ case 24: case 192: return aes_decrypt_key192(in_key, cx);
+ case 32: case 256: return aes_decrypt_key256(in_key, cx);
+ default: return aes_error;
+#else
+ case 16: case 128: aes_decrypt_key128(in_key, cx); return;
+ case 24: case 192: aes_decrypt_key192(in_key, cx); return;
+ case 32: case 256: aes_decrypt_key256(in_key, cx); return;
+#endif
+ }
+}
+
+#endif
+
+#endif
+
+#endif /* !HAVE_CRYPTO */
+
+#if defined(__cplusplus)
+}
+#endif
diff --git a/trunk/main/aesopt.h b/trunk/main/aesopt.h
new file mode 100644
index 000000000..bb4f05a0b
--- /dev/null
+++ b/trunk/main/aesopt.h
@@ -0,0 +1,1029 @@
+/*
+ ---------------------------------------------------------------------------
+ Copyright (c) 2003, Dr Brian Gladman <brg@gladman.me.uk>, Worcester, UK.
+ All rights reserved.
+
+ LICENSE TERMS
+
+ The free distribution and use of this software in both source and binary
+ form is allowed (with or without changes) provided that:
+
+ 1. distributions of this source code include the above copyright
+ notice, this list of conditions and the following disclaimer;
+
+ 2. distributions in binary form include the above copyright
+ notice, this list of conditions and the following disclaimer
+ in the documentation and/or other associated materials;
+
+ 3. the copyright holder's name is not used to endorse products
+ built using this software without specific written permission.
+
+ ALTERNATIVELY, provided that this notice is retained in full, this product
+ may be distributed under the terms of the GNU General Public License (GPL),
+ in which case the provisions of the GPL apply INSTEAD OF those given above.
+
+ DISCLAIMER
+
+ This software is provided 'as is' with no explicit or implied warranties
+ in respect of its properties, including, but not limited to, correctness
+ and/or fitness for purpose.
+ ---------------------------------------------------------------------------
+ Issue Date: 26/08/2003
+
+ My thanks go to Dag Arne Osvik for devising the schemes used here for key
+ length derivation from the form of the key schedule
+
+ This file contains the compilation options for AES (Rijndael) and code
+ that is common across encryption, key scheduling and table generation.
+
+ OPERATION
+
+ These source code files implement the AES algorithm Rijndael designed by
+ Joan Daemen and Vincent Rijmen. This version is designed for the standard
+ block size of 16 bytes and for key sizes of 128, 192 and 256 bits (16, 24
+ and 32 bytes).
+
+ This version is designed for flexibility and speed using operations on
+ 32-bit words rather than operations on bytes. It can be compiled with
+ either big or little endian internal byte order but is faster when the
+ native byte order for the processor is used.
+
+ THE CIPHER INTERFACE
+
+ The cipher interface is implemented as an array of bytes in which lower
+ AES bit sequence indexes map to higher numeric significance within bytes.
+
+ aes_08t (an unsigned 8-bit type)
+ aes_32t (an unsigned 32-bit type)
+ struct aes_encrypt_ctx (structure for the cipher encryption context)
+ struct aes_decrypt_ctx (structure for the cipher decryption context)
+ aes_rval the function return type
+
+ C subroutine calls:
+
+ aes_rval aes_encrypt_key128(const void *in_key, aes_encrypt_ctx cx[1]);
+ aes_rval aes_encrypt_key192(const void *in_key, aes_encrypt_ctx cx[1]);
+ aes_rval aes_encrypt_key256(const void *in_key, aes_encrypt_ctx cx[1]);
+ aes_rval aes_encrypt(const void *in_blk,
+ void *out_blk, const aes_encrypt_ctx cx[1]);
+
+ aes_rval aes_decrypt_key128(const void *in_key, aes_decrypt_ctx cx[1]);
+ aes_rval aes_decrypt_key192(const void *in_key, aes_decrypt_ctx cx[1]);
+ aes_rval aes_decrypt_key256(const void *in_key, aes_decrypt_ctx cx[1]);
+ aes_rval aes_decrypt(const void *in_blk,
+ void *out_blk, const aes_decrypt_ctx cx[1]);
+
+ IMPORTANT NOTE: If you are using this C interface with dynamic tables make sure that
+ you call genTabs() before AES is used so that the tables are initialised.
+
+ C++ aes class subroutines:
+
+ Class AESencrypt for encryption
+
+ Construtors:
+ AESencrypt(void)
+ AESencrypt(const void *in_key) - 128 bit key
+ Members:
+ void key128(const void *in_key)
+ void key192(const void *in_key)
+ void key256(const void *in_key)
+ void encrypt(const void *in_blk, void *out_blk) const
+
+ Class AESdecrypt for encryption
+ Construtors:
+ AESdecrypt(void)
+ AESdecrypt(const void *in_key) - 128 bit key
+ Members:
+ void key128(const void *in_key)
+ void key192(const void *in_key)
+ void key256(const void *in_key)
+ void decrypt(const void *in_blk, void *out_blk) const
+
+ COMPILATION
+
+ The files used to provide AES (Rijndael) are
+
+ a. aes.h for the definitions needed for use in C.
+ b. aescpp.h for the definitions needed for use in C++.
+ c. aesopt.h for setting compilation options (also includes common code).
+ d. aescrypt.c for encryption and decrytpion, or
+ e. aeskey.c for key scheduling.
+ f. aestab.c for table loading or generation.
+ g. aescrypt.asm for encryption and decryption using assembler code.
+ h. aescrypt.mmx.asm for encryption and decryption using MMX assembler.
+
+ To compile AES (Rijndael) for use in C code use aes.h and set the
+ defines here for the facilities you need (key lengths, encryption
+ and/or decryption). Do not define AES_DLL or AES_CPP. Set the options
+ for optimisations and table sizes here.
+
+ To compile AES (Rijndael) for use in in C++ code use aescpp.h but do
+ not define AES_DLL
+
+ To compile AES (Rijndael) in C as a Dynamic Link Library DLL) use
+ aes.h and include the AES_DLL define.
+
+ CONFIGURATION OPTIONS (here and in aes.h)
+
+ a. set AES_DLL in aes.h if AES (Rijndael) is to be compiled as a DLL
+ b. You may need to set PLATFORM_BYTE_ORDER to define the byte order.
+ c. If you want the code to run in a specific internal byte order, then
+ ALGORITHM_BYTE_ORDER must be set accordingly.
+ d. set other configuration options decribed below.
+*/
+
+#ifndef _AESOPT_H
+#define _AESOPT_H
+
+#include "asterisk/aes.h"
+#include "asterisk/endian.h"
+
+/* CONFIGURATION - USE OF DEFINES
+
+ Later in this section there are a number of defines that control the
+ operation of the code. In each section, the purpose of each define is
+ explained so that the relevant form can be included or excluded by
+ setting either 1's or 0's respectively on the branches of the related
+ #if clauses.
+*/
+
+/* BYTE ORDER IN 32-BIT WORDS
+
+ To obtain the highest speed on processors with 32-bit words, this code
+ needs to determine the byte order of the target machine. The following
+ block of code is an attempt to capture the most obvious ways in which
+ various environemnts define byte order. It may well fail, in which case
+ the definitions will need to be set by editing at the points marked
+ **** EDIT HERE IF NECESSARY **** below. My thanks to Peter Gutmann for
+ some of these defines (from cryptlib).
+*/
+
+#define BRG_LITTLE_ENDIAN 1234 /* byte 0 is least significant (i386) */
+#define BRG_BIG_ENDIAN 4321 /* byte 0 is most significant (mc68k) */
+
+#if defined( __alpha__ ) || defined( __alpha ) || defined( i386 ) || \
+ defined( __i386__ ) || defined( _M_I86 ) || defined( _M_IX86 ) || \
+ defined( __OS2__ ) || defined( sun386 ) || defined( __TURBOC__ ) || \
+ defined( vax ) || defined( vms ) || defined( VMS ) || \
+ defined( __VMS )
+
+#define PLATFORM_BYTE_ORDER BRG_LITTLE_ENDIAN
+
+#endif
+
+#if defined( AMIGA ) || defined( applec ) || defined( __AS400__ ) || \
+ defined( _CRAY ) || defined( __hppa ) || defined( __hp9000 ) || \
+ defined( ibm370 ) || defined( mc68000 ) || defined( m68k ) || \
+ defined( __MRC__ ) || defined( __MVS__ ) || defined( __MWERKS__ ) || \
+ defined( sparc ) || defined( __sparc) || defined( SYMANTEC_C ) || \
+ defined( __TANDEM ) || defined( THINK_C ) || defined( __VMCMS__ )
+
+#define PLATFORM_BYTE_ORDER BRG_BIG_ENDIAN
+
+#endif
+
+/* if the platform is still not known, try to find its byte order */
+/* from commonly used definitions in the headers included earlier */
+
+#if !defined(PLATFORM_BYTE_ORDER)
+
+#if defined(LITTLE_ENDIAN) || defined(BIG_ENDIAN)
+# if defined(LITTLE_ENDIAN) && !defined(BIG_ENDIAN)
+# define PLATFORM_BYTE_ORDER BRG_LITTLE_ENDIAN
+# elif !defined(LITTLE_ENDIAN) && defined(BIG_ENDIAN)
+# define PLATFORM_BYTE_ORDER BRG_BIG_ENDIAN
+# elif defined(BYTE_ORDER) && (BYTE_ORDER == LITTLE_ENDIAN)
+# define PLATFORM_BYTE_ORDER BRG_LITTLE_ENDIAN
+# elif defined(BYTE_ORDER) && (BYTE_ORDER == BIG_ENDIAN)
+# define PLATFORM_BYTE_ORDER BRG_BIG_ENDIAN
+# endif
+
+#elif defined(_LITTLE_ENDIAN) || defined(_BIG_ENDIAN)
+# if defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN)
+# define PLATFORM_BYTE_ORDER BRG_LITTLE_ENDIAN
+# elif !defined(_LITTLE_ENDIAN) && defined(_BIG_ENDIAN)
+# define PLATFORM_BYTE_ORDER BRG_BIG_ENDIAN
+# elif defined(_BYTE_ORDER) && (_BYTE_ORDER == _LITTLE_ENDIAN)
+# define PLATFORM_BYTE_ORDER BRG_LITTLE_ENDIAN
+# elif defined(_BYTE_ORDER) && (_BYTE_ORDER == _BIG_ENDIAN)
+# define PLATFORM_BYTE_ORDER BRG_BIG_ENDIAN
+# endif
+
+#elif defined(__LITTLE_ENDIAN__) || defined(__BIG_ENDIAN__)
+# if defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__)
+# define PLATFORM_BYTE_ORDER BRG_LITTLE_ENDIAN
+# elif !defined(__LITTLE_ENDIAN__) && defined(__BIG_ENDIAN__)
+# define PLATFORM_BYTE_ORDER BRG_BIG_ENDIAN
+# elif defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __LITTLE_ENDIAN__)
+# define PLATFORM_BYTE_ORDER BRG_LITTLE_ENDIAN
+# elif defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __BIG_ENDIAN__)
+# define PLATFORM_BYTE_ORDER BRG_BIG_ENDIAN
+# endif
+
+#elif 0 /* **** EDIT HERE IF NECESSARY **** */
+#define PLATFORM_BYTE_ORDER BRG_LITTLE_ENDIAN
+
+#elif 0 /* **** EDIT HERE IF NECESSARY **** */
+#define PLATFORM_BYTE_ORDER BRG_BIG_ENDIAN
+
+#else
+#error Please edit aesopt.h (line 235 or 238) to set the platform byte order
+#endif
+
+#endif
+
+/* SOME LOCAL DEFINITIONS */
+
+#define NO_TABLES 0
+#define ONE_TABLE 1
+#define FOUR_TABLES 4
+#define NONE 0
+#define PARTIAL 1
+#define FULL 2
+
+#if defined(bswap32)
+#define aes_sw32 bswap32
+#elif defined(bswap_32)
+#define aes_sw32 bswap_32
+#else
+#define brot(x,n) (((aes_32t)(x) << n) | ((aes_32t)(x) >> (32 - n)))
+#define aes_sw32(x) ((brot((x),8) & 0x00ff00ff) | (brot((x),24) & 0xff00ff00))
+#endif
+
+/* 1. FUNCTIONS REQUIRED
+
+ This implementation provides subroutines for encryption, decryption
+ and for setting the three key lengths (separately) for encryption
+ and decryption. When the assembler code is not being used the following
+ definition blocks allow the selection of the routines that are to be
+ included in the compilation.
+*/
+#ifdef AES_ENCRYPT
+#define ENCRYPTION
+#define ENCRYPTION_KEY_SCHEDULE
+#endif
+
+#ifdef AES_DECRYPT
+#define DECRYPTION
+#define DECRYPTION_KEY_SCHEDULE
+#endif
+
+/* 2. ASSEMBLER SUPPORT
+
+ This define (which can be on the command line) enables the use of the
+ assembler code routines for encryption and decryption with the C code
+ only providing key scheduling
+*/
+#if 0
+#define AES_ASM
+#endif
+
+/* 3. BYTE ORDER WITHIN 32 BIT WORDS
+
+ The fundamental data processing units in Rijndael are 8-bit bytes. The
+ input, output and key input are all enumerated arrays of bytes in which
+ bytes are numbered starting at zero and increasing to one less than the
+ number of bytes in the array in question. This enumeration is only used
+ for naming bytes and does not imply any adjacency or order relationship
+ from one byte to another. When these inputs and outputs are considered
+ as bit sequences, bits 8*n to 8*n+7 of the bit sequence are mapped to
+ byte[n] with bit 8n+i in the sequence mapped to bit 7-i within the byte.
+ In this implementation bits are numbered from 0 to 7 starting at the
+ numerically least significant end of each byte (bit n represents 2^n).
+
+ However, Rijndael can be implemented more efficiently using 32-bit
+ words by packing bytes into words so that bytes 4*n to 4*n+3 are placed
+ into word[n]. While in principle these bytes can be assembled into words
+ in any positions, this implementation only supports the two formats in
+ which bytes in adjacent positions within words also have adjacent byte
+ numbers. This order is called big-endian if the lowest numbered bytes
+ in words have the highest numeric significance and little-endian if the
+ opposite applies.
+
+ This code can work in either order irrespective of the order used by the
+ machine on which it runs. Normally the internal byte order will be set
+ to the order of the processor on which the code is to be run but this
+ define can be used to reverse this in special situations
+
+ NOTE: Assembler code versions rely on PLATFORM_BYTE_ORDER being set
+*/
+#if 1 || defined(AES_ASM)
+#define ALGORITHM_BYTE_ORDER PLATFORM_BYTE_ORDER
+#elif 0
+#define ALGORITHM_BYTE_ORDER BRG_LITTLE_ENDIAN
+#elif 0
+#define ALGORITHM_BYTE_ORDER BRG_BIG_ENDIAN
+#else
+#error The algorithm byte order is not defined
+#endif
+
+/* 4. FAST INPUT/OUTPUT OPERATIONS.
+
+ On some machines it is possible to improve speed by transferring the
+ bytes in the input and output arrays to and from the internal 32-bit
+ variables by addressing these arrays as if they are arrays of 32-bit
+ words. On some machines this will always be possible but there may
+ be a large performance penalty if the byte arrays are not aligned on
+ the normal word boundaries. On other machines this technique will
+ lead to memory access errors when such 32-bit word accesses are not
+ properly aligned. The option SAFE_IO avoids such problems but will
+ often be slower on those machines that support misaligned access
+ (especially so if care is taken to align the input and output byte
+ arrays on 32-bit word boundaries). If SAFE_IO is not defined it is
+ assumed that access to byte arrays as if they are arrays of 32-bit
+ words will not cause problems when such accesses are misaligned.
+*/
+#if 1 && !defined(_MSC_VER)
+#define SAFE_IO
+#endif
+
+/* 5. LOOP UNROLLING
+
+ The code for encryption and decrytpion cycles through a number of rounds
+ that can be implemented either in a loop or by expanding the code into a
+ long sequence of instructions, the latter producing a larger program but
+ one that will often be much faster. The latter is called loop unrolling.
+ There are also potential speed advantages in expanding two iterations in
+ a loop with half the number of iterations, which is called partial loop
+ unrolling. The following options allow partial or full loop unrolling
+ to be set independently for encryption and decryption
+*/
+#if 1
+#define ENC_UNROLL FULL
+#elif 0
+#define ENC_UNROLL PARTIAL
+#else
+#define ENC_UNROLL NONE
+#endif
+
+#if 1
+#define DEC_UNROLL FULL
+#elif 0
+#define DEC_UNROLL PARTIAL
+#else
+#define DEC_UNROLL NONE
+#endif
+
+/* 6. FAST FINITE FIELD OPERATIONS
+
+ If this section is included, tables are used to provide faster finite
+ field arithmetic (this has no effect if FIXED_TABLES is defined).
+*/
+#if 1
+#define FF_TABLES
+#endif
+
+/* 7. INTERNAL STATE VARIABLE FORMAT
+
+ The internal state of Rijndael is stored in a number of local 32-bit
+ word varaibles which can be defined either as an array or as individual
+ names variables. Include this section if you want to store these local
+ varaibles in arrays. Otherwise individual local variables will be used.
+*/
+#if 1
+#define ARRAYS
+#endif
+
+/* In this implementation the columns of the state array are each held in
+ 32-bit words. The state array can be held in various ways: in an array
+ of words, in a number of individual word variables or in a number of
+ processor registers. The following define maps a variable name x and
+ a column number c to the way the state array variable is to be held.
+ The first define below maps the state into an array x[c] whereas the
+ second form maps the state into a number of individual variables x0,
+ x1, etc. Another form could map individual state colums to machine
+ register names.
+*/
+
+#if defined(ARRAYS)
+#define s(x,c) x[c]
+#else
+#define s(x,c) x##c
+#endif
+
+/* 8. FIXED OR DYNAMIC TABLES
+
+ When this section is included the tables used by the code are compiled
+ statically into the binary file. Otherwise the subroutine gen_tabs()
+ must be called to compute them before the code is first used.
+*/
+#if 1
+#define FIXED_TABLES
+#endif
+
+/* 9. TABLE ALIGNMENT
+
+ On some sytsems speed will be improved by aligning the AES large lookup
+ tables on particular boundaries. This define should be set to a power of
+ two giving the desired alignment. It can be left undefined if alignment
+ is not needed. This option is specific to the Microsft VC++ compiler -
+ it seems to sometimes cause trouble for the VC++ version 6 compiler.
+*/
+
+#if 0 && defined(_MSC_VER) && (_MSC_VER >= 1300)
+#define TABLE_ALIGN 64
+#endif
+
+/* 10. INTERNAL TABLE CONFIGURATION
+
+ This cipher proceeds by repeating in a number of cycles known as 'rounds'
+ which are implemented by a round function which can optionally be speeded
+ up using tables. The basic tables are each 256 32-bit words, with either
+ one or four tables being required for each round function depending on
+ how much speed is required. The encryption and decryption round functions
+ are different and the last encryption and decrytpion round functions are
+ different again making four different round functions in all.
+
+ This means that:
+ 1. Normal encryption and decryption rounds can each use either 0, 1
+ or 4 tables and table spaces of 0, 1024 or 4096 bytes each.
+ 2. The last encryption and decryption rounds can also use either 0, 1
+ or 4 tables and table spaces of 0, 1024 or 4096 bytes each.
+
+ Include or exclude the appropriate definitions below to set the number
+ of tables used by this implementation.
+*/
+
+#if 1 /* set tables for the normal encryption round */
+#define ENC_ROUND FOUR_TABLES
+#elif 0
+#define ENC_ROUND ONE_TABLE
+#else
+#define ENC_ROUND NO_TABLES
+#endif
+
+#if 1 /* set tables for the last encryption round */
+#define LAST_ENC_ROUND FOUR_TABLES
+#elif 0
+#define LAST_ENC_ROUND ONE_TABLE
+#else
+#define LAST_ENC_ROUND NO_TABLES
+#endif
+
+#if 1 /* set tables for the normal decryption round */
+#define DEC_ROUND FOUR_TABLES
+#elif 0
+#define DEC_ROUND ONE_TABLE
+#else
+#define DEC_ROUND NO_TABLES
+#endif
+
+#if 1 /* set tables for the last decryption round */
+#define LAST_DEC_ROUND FOUR_TABLES
+#elif 0
+#define LAST_DEC_ROUND ONE_TABLE
+#else
+#define LAST_DEC_ROUND NO_TABLES
+#endif
+
+/* The decryption key schedule can be speeded up with tables in the same
+ way that the round functions can. Include or exclude the following
+ defines to set this requirement.
+*/
+#if 1
+#define KEY_SCHED FOUR_TABLES
+#elif 0
+#define KEY_SCHED ONE_TABLE
+#else
+#define KEY_SCHED NO_TABLES
+#endif
+
+/* END OF CONFIGURATION OPTIONS */
+
+#define RC_LENGTH (5 * (AES_BLOCK_SIZE / 4 - 2))
+
+/* Disable or report errors on some combinations of options */
+
+#if ENC_ROUND == NO_TABLES && LAST_ENC_ROUND != NO_TABLES
+#undef LAST_ENC_ROUND
+#define LAST_ENC_ROUND NO_TABLES
+#elif ENC_ROUND == ONE_TABLE && LAST_ENC_ROUND == FOUR_TABLES
+#undef LAST_ENC_ROUND
+#define LAST_ENC_ROUND ONE_TABLE
+#endif
+
+#if ENC_ROUND == NO_TABLES && ENC_UNROLL != NONE
+#undef ENC_UNROLL
+#define ENC_UNROLL NONE
+#endif
+
+#if DEC_ROUND == NO_TABLES && LAST_DEC_ROUND != NO_TABLES
+#undef LAST_DEC_ROUND
+#define LAST_DEC_ROUND NO_TABLES
+#elif DEC_ROUND == ONE_TABLE && LAST_DEC_ROUND == FOUR_TABLES
+#undef LAST_DEC_ROUND
+#define LAST_DEC_ROUND ONE_TABLE
+#endif
+
+#if DEC_ROUND == NO_TABLES && DEC_UNROLL != NONE
+#undef DEC_UNROLL
+#define DEC_UNROLL NONE
+#endif
+
+/* upr(x,n): rotates bytes within words by n positions, moving bytes to
+ higher index positions with wrap around into low positions
+ ups(x,n): moves bytes by n positions to higher index positions in
+ words but without wrap around
+ bval(x,n): extracts a byte from a word
+
+ NOTE: The definitions given here are intended only for use with
+ unsigned variables and with shift counts that are compile
+ time constants
+*/
+
+#if (ALGORITHM_BYTE_ORDER == BRG_LITTLE_ENDIAN)
+#define upr(x,n) (((aes_32t)(x) << (8 * (n))) | ((aes_32t)(x) >> (32 - 8 * (n))))
+#define ups(x,n) ((aes_32t) (x) << (8 * (n)))
+#define bval(x,n) ((aes_08t)((x) >> (8 * (n))))
+#define bytes2word(b0, b1, b2, b3) \
+ (((aes_32t)(b3) << 24) | ((aes_32t)(b2) << 16) | ((aes_32t)(b1) << 8) | (b0))
+#endif
+
+#if (ALGORITHM_BYTE_ORDER == BRG_BIG_ENDIAN)
+#define upr(x,n) (((aes_32t)(x) >> (8 * (n))) | ((aes_32t)(x) << (32 - 8 * (n))))
+#define ups(x,n) ((aes_32t) (x) >> (8 * (n))))
+#define bval(x,n) ((aes_08t)((x) >> (24 - 8 * (n))))
+#define bytes2word(b0, b1, b2, b3) \
+ (((aes_32t)(b0) << 24) | ((aes_32t)(b1) << 16) | ((aes_32t)(b2) << 8) | (b3))
+#endif
+
+#if defined(SAFE_IO)
+
+#define word_in(x,c) bytes2word(((aes_08t*)(x)+4*c)[0], ((aes_08t*)(x)+4*c)[1], \
+ ((aes_08t*)(x)+4*c)[2], ((aes_08t*)(x)+4*c)[3])
+#define word_out(x,c,v) { ((aes_08t*)(x)+4*c)[0] = bval(v,0); ((aes_08t*)(x)+4*c)[1] = bval(v,1); \
+ ((aes_08t*)(x)+4*c)[2] = bval(v,2); ((aes_08t*)(x)+4*c)[3] = bval(v,3); }
+
+#elif (ALGORITHM_BYTE_ORDER == PLATFORM_BYTE_ORDER)
+
+#define word_in(x,c) (*((aes_32t*)(x)+(c)))
+#define word_out(x,c,v) (*((aes_32t*)(x)+(c)) = (v))
+
+#else
+
+#define word_in(x,c) aes_sw32(*((aes_32t*)(x)+(c)))
+#define word_out(x,c,v) (*((aes_32t*)(x)+(c)) = aes_sw32(v))
+
+#endif
+
+/* the finite field modular polynomial and elements */
+
+#define WPOLY 0x011b
+#define BPOLY 0x1b
+
+/* multiply four bytes in GF(2^8) by 'x' {02} in parallel */
+
+#define m1 0x80808080
+#define m2 0x7f7f7f7f
+#define gf_mulx(x) ((((x) & m2) << 1) ^ ((((x) & m1) >> 7) * BPOLY))
+
+/* The following defines provide alternative definitions of gf_mulx that might
+ give improved performance if a fast 32-bit multiply is not available. Note
+ that a temporary variable u needs to be defined where gf_mulx is used.
+
+#define gf_mulx(x) (u = (x) & m1, u |= (u >> 1), ((x) & m2) << 1) ^ ((u >> 3) | (u >> 6))
+#define m4 (0x01010101 * BPOLY)
+#define gf_mulx(x) (u = (x) & m1, ((x) & m2) << 1) ^ ((u - (u >> 7)) & m4)
+*/
+
+/* Work out which tables are needed for the different options */
+
+#ifdef AES_ASM
+#ifdef ENC_ROUND
+#undef ENC_ROUND
+#endif
+#define ENC_ROUND FOUR_TABLES
+#ifdef LAST_ENC_ROUND
+#undef LAST_ENC_ROUND
+#endif
+#define LAST_ENC_ROUND FOUR_TABLES
+#ifdef DEC_ROUND
+#undef DEC_ROUND
+#endif
+#define DEC_ROUND FOUR_TABLES
+#ifdef LAST_DEC_ROUND
+#undef LAST_DEC_ROUND
+#endif
+#define LAST_DEC_ROUND FOUR_TABLES
+#ifdef KEY_SCHED
+#undef KEY_SCHED
+#define KEY_SCHED FOUR_TABLES
+#endif
+#endif
+
+#if defined(ENCRYPTION) || defined(AES_ASM)
+#if ENC_ROUND == ONE_TABLE
+#define FT1_SET
+#elif ENC_ROUND == FOUR_TABLES
+#define FT4_SET
+#else
+#define SBX_SET
+#endif
+#if LAST_ENC_ROUND == ONE_TABLE
+#define FL1_SET
+#elif LAST_ENC_ROUND == FOUR_TABLES
+#define FL4_SET
+#elif !defined(SBX_SET)
+#define SBX_SET
+#endif
+#endif
+
+#if defined(DECRYPTION) || defined(AES_ASM)
+#if DEC_ROUND == ONE_TABLE
+#define IT1_SET
+#elif DEC_ROUND == FOUR_TABLES
+#define IT4_SET
+#else
+#define ISB_SET
+#endif
+#if LAST_DEC_ROUND == ONE_TABLE
+#define IL1_SET
+#elif LAST_DEC_ROUND == FOUR_TABLES
+#define IL4_SET
+#elif !defined(ISB_SET)
+#define ISB_SET
+#endif
+#endif
+
+#if defined(ENCRYPTION_KEY_SCHEDULE) || defined(DECRYPTION_KEY_SCHEDULE)
+#if KEY_SCHED == ONE_TABLE
+#define LS1_SET
+#define IM1_SET
+#elif KEY_SCHED == FOUR_TABLES
+#define LS4_SET
+#define IM4_SET
+#elif !defined(SBX_SET)
+#define SBX_SET
+#endif
+#endif
+
+/* generic definitions of Rijndael macros that use tables */
+
+#define no_table(x,box,vf,rf,c) bytes2word( \
+ box[bval(vf(x,0,c),rf(0,c))], \
+ box[bval(vf(x,1,c),rf(1,c))], \
+ box[bval(vf(x,2,c),rf(2,c))], \
+ box[bval(vf(x,3,c),rf(3,c))])
+
+#define one_table(x,op,tab,vf,rf,c) \
+ ( tab[bval(vf(x,0,c),rf(0,c))] \
+ ^ op(tab[bval(vf(x,1,c),rf(1,c))],1) \
+ ^ op(tab[bval(vf(x,2,c),rf(2,c))],2) \
+ ^ op(tab[bval(vf(x,3,c),rf(3,c))],3))
+
+#define four_tables(x,tab,vf,rf,c) \
+ ( tab[0][bval(vf(x,0,c),rf(0,c))] \
+ ^ tab[1][bval(vf(x,1,c),rf(1,c))] \
+ ^ tab[2][bval(vf(x,2,c),rf(2,c))] \
+ ^ tab[3][bval(vf(x,3,c),rf(3,c))])
+
+#define vf1(x,r,c) (x)
+#define rf1(r,c) (r)
+#define rf2(r,c) ((8+r-c)&3)
+
+/* perform forward and inverse column mix operation on four bytes in long word x in */
+/* parallel. NOTE: x must be a simple variable, NOT an expression in these macros. */
+
+#if defined(FM4_SET) /* not currently used */
+#define fwd_mcol(x) four_tables(x,t_use(f,m),vf1,rf1,0)
+#elif defined(FM1_SET) /* not currently used */
+#define fwd_mcol(x) one_table(x,upr,t_use(f,m),vf1,rf1,0)
+#else
+#define dec_fmvars aes_32t g2
+#define fwd_mcol(x) (g2 = gf_mulx(x), g2 ^ upr((x) ^ g2, 3) ^ upr((x), 2) ^ upr((x), 1))
+#endif
+
+#if defined(IM4_SET)
+#define inv_mcol(x) four_tables(x,t_use(i,m),vf1,rf1,0)
+#elif defined(IM1_SET)
+#define inv_mcol(x) one_table(x,upr,t_use(i,m),vf1,rf1,0)
+#else
+#define dec_imvars aes_32t g2, g4, g9
+#define inv_mcol(x) (g2 = gf_mulx(x), g4 = gf_mulx(g2), g9 = (x) ^ gf_mulx(g4), g4 ^= g9, \
+ (x) ^ g2 ^ g4 ^ upr(g2 ^ g9, 3) ^ upr(g4, 2) ^ upr(g9, 1))
+#endif
+
+#if defined(FL4_SET)
+#define ls_box(x,c) four_tables(x,t_use(f,l),vf1,rf2,c)
+#elif defined(LS4_SET)
+#define ls_box(x,c) four_tables(x,t_use(l,s),vf1,rf2,c)
+#elif defined(FL1_SET)
+#define ls_box(x,c) one_table(x,upr,t_use(f,l),vf1,rf2,c)
+#elif defined(LS1_SET)
+#define ls_box(x,c) one_table(x,upr,t_use(l,s),vf1,rf2,c)
+#else
+#define ls_box(x,c) no_table(x,t_use(s,box),vf1,rf2,c)
+#endif
+
+#if defined(__cplusplus)
+extern "C"
+{
+#endif
+
+/* If there are no global variables, the definitions here can be
+ used to put the AES tables in a structure so that a pointer
+ can then be added to the AES context to pass them to the AES
+ routines that need them. If this facility is used, the calling
+ program has to ensure that this pointer is managed appropriately.
+ In particular, the value of the t_dec(in,it) item in the table
+ structure must be set to zero in order to ensure that the tables
+ are initialised. In practice the three code sequences in aeskey.c
+ that control the calls to gen_tabs() and the gen_tabs() routine
+ itself will have to be changed for a specific implementation. If
+ global variables are available it will generally be preferable to
+ use them with the precomputed FIXED_TABLES option that uses static
+ global tables.
+
+ The following defines can be used to control the way the tables
+ are defined, initialised and used in embedded environments that
+ require special features for these purposes
+
+ the 't_dec' construction is used to declare fixed table arrays
+ the 't_set' construction is used to set fixed table values
+ the 't_use' construction is used to access fixed table values
+
+ 256 byte tables:
+
+ t_xxx(s,box) => forward S box
+ t_xxx(i,box) => inverse S box
+
+ 256 32-bit word OR 4 x 256 32-bit word tables:
+
+ t_xxx(f,n) => forward normal round
+ t_xxx(f,l) => forward last round
+ t_xxx(i,n) => inverse normal round
+ t_xxx(i,l) => inverse last round
+ t_xxx(l,s) => key schedule table
+ t_xxx(i,m) => key schedule table
+
+ Other variables and tables:
+
+ t_xxx(r,c) => the rcon table
+*/
+
+#define t_dec(m,n) t_##m##n
+#define t_set(m,n) t_##m##n
+#define t_use(m,n) t_##m##n
+
+#if defined(DO_TABLES) /* declare and instantiate tables */
+
+/* finite field arithmetic operations for table generation */
+
+#if defined(FIXED_TABLES) || !defined(FF_TABLES)
+
+#define f2(x) ((x<<1) ^ (((x>>7) & 1) * WPOLY))
+#define f4(x) ((x<<2) ^ (((x>>6) & 1) * WPOLY) ^ (((x>>6) & 2) * WPOLY))
+#define f8(x) ((x<<3) ^ (((x>>5) & 1) * WPOLY) ^ (((x>>5) & 2) * WPOLY) \
+ ^ (((x>>5) & 4) * WPOLY))
+#define f3(x) (f2(x) ^ x)
+#define f9(x) (f8(x) ^ x)
+#define fb(x) (f8(x) ^ f2(x) ^ x)
+#define fd(x) (f8(x) ^ f4(x) ^ x)
+#define fe(x) (f8(x) ^ f4(x) ^ f2(x))
+
+#else
+
+#define f2(x) ((x) ? pow[log[x] + 0x19] : 0)
+#define f3(x) ((x) ? pow[log[x] + 0x01] : 0)
+#define f9(x) ((x) ? pow[log[x] + 0xc7] : 0)
+#define fb(x) ((x) ? pow[log[x] + 0x68] : 0)
+#define fd(x) ((x) ? pow[log[x] + 0xee] : 0)
+#define fe(x) ((x) ? pow[log[x] + 0xdf] : 0)
+#define fi(x) ((x) ? pow[ 255 - log[x]] : 0)
+
+#endif
+
+#if defined(FIXED_TABLES) /* declare and set values for static tables */
+
+#define sb_data(w) \
+ w(0x63), w(0x7c), w(0x77), w(0x7b), w(0xf2), w(0x6b), w(0x6f), w(0xc5),\
+ w(0x30), w(0x01), w(0x67), w(0x2b), w(0xfe), w(0xd7), w(0xab), w(0x76),\
+ w(0xca), w(0x82), w(0xc9), w(0x7d), w(0xfa), w(0x59), w(0x47), w(0xf0),\
+ w(0xad), w(0xd4), w(0xa2), w(0xaf), w(0x9c), w(0xa4), w(0x72), w(0xc0),\
+ w(0xb7), w(0xfd), w(0x93), w(0x26), w(0x36), w(0x3f), w(0xf7), w(0xcc),\
+ w(0x34), w(0xa5), w(0xe5), w(0xf1), w(0x71), w(0xd8), w(0x31), w(0x15),\
+ w(0x04), w(0xc7), w(0x23), w(0xc3), w(0x18), w(0x96), w(0x05), w(0x9a),\
+ w(0x07), w(0x12), w(0x80), w(0xe2), w(0xeb), w(0x27), w(0xb2), w(0x75),\
+ w(0x09), w(0x83), w(0x2c), w(0x1a), w(0x1b), w(0x6e), w(0x5a), w(0xa0),\
+ w(0x52), w(0x3b), w(0xd6), w(0xb3), w(0x29), w(0xe3), w(0x2f), w(0x84),\
+ w(0x53), w(0xd1), w(0x00), w(0xed), w(0x20), w(0xfc), w(0xb1), w(0x5b),\
+ w(0x6a), w(0xcb), w(0xbe), w(0x39), w(0x4a), w(0x4c), w(0x58), w(0xcf),\
+ w(0xd0), w(0xef), w(0xaa), w(0xfb), w(0x43), w(0x4d), w(0x33), w(0x85),\
+ w(0x45), w(0xf9), w(0x02), w(0x7f), w(0x50), w(0x3c), w(0x9f), w(0xa8),\
+ w(0x51), w(0xa3), w(0x40), w(0x8f), w(0x92), w(0x9d), w(0x38), w(0xf5),\
+ w(0xbc), w(0xb6), w(0xda), w(0x21), w(0x10), w(0xff), w(0xf3), w(0xd2),\
+ w(0xcd), w(0x0c), w(0x13), w(0xec), w(0x5f), w(0x97), w(0x44), w(0x17),\
+ w(0xc4), w(0xa7), w(0x7e), w(0x3d), w(0x64), w(0x5d), w(0x19), w(0x73),\
+ w(0x60), w(0x81), w(0x4f), w(0xdc), w(0x22), w(0x2a), w(0x90), w(0x88),\
+ w(0x46), w(0xee), w(0xb8), w(0x14), w(0xde), w(0x5e), w(0x0b), w(0xdb),\
+ w(0xe0), w(0x32), w(0x3a), w(0x0a), w(0x49), w(0x06), w(0x24), w(0x5c),\
+ w(0xc2), w(0xd3), w(0xac), w(0x62), w(0x91), w(0x95), w(0xe4), w(0x79),\
+ w(0xe7), w(0xc8), w(0x37), w(0x6d), w(0x8d), w(0xd5), w(0x4e), w(0xa9),\
+ w(0x6c), w(0x56), w(0xf4), w(0xea), w(0x65), w(0x7a), w(0xae), w(0x08),\
+ w(0xba), w(0x78), w(0x25), w(0x2e), w(0x1c), w(0xa6), w(0xb4), w(0xc6),\
+ w(0xe8), w(0xdd), w(0x74), w(0x1f), w(0x4b), w(0xbd), w(0x8b), w(0x8a),\
+ w(0x70), w(0x3e), w(0xb5), w(0x66), w(0x48), w(0x03), w(0xf6), w(0x0e),\
+ w(0x61), w(0x35), w(0x57), w(0xb9), w(0x86), w(0xc1), w(0x1d), w(0x9e),\
+ w(0xe1), w(0xf8), w(0x98), w(0x11), w(0x69), w(0xd9), w(0x8e), w(0x94),\
+ w(0x9b), w(0x1e), w(0x87), w(0xe9), w(0xce), w(0x55), w(0x28), w(0xdf),\
+ w(0x8c), w(0xa1), w(0x89), w(0x0d), w(0xbf), w(0xe6), w(0x42), w(0x68),\
+ w(0x41), w(0x99), w(0x2d), w(0x0f), w(0xb0), w(0x54), w(0xbb), w(0x16)
+
+#define isb_data(w) \
+ w(0x52), w(0x09), w(0x6a), w(0xd5), w(0x30), w(0x36), w(0xa5), w(0x38),\
+ w(0xbf), w(0x40), w(0xa3), w(0x9e), w(0x81), w(0xf3), w(0xd7), w(0xfb),\
+ w(0x7c), w(0xe3), w(0x39), w(0x82), w(0x9b), w(0x2f), w(0xff), w(0x87),\
+ w(0x34), w(0x8e), w(0x43), w(0x44), w(0xc4), w(0xde), w(0xe9), w(0xcb),\
+ w(0x54), w(0x7b), w(0x94), w(0x32), w(0xa6), w(0xc2), w(0x23), w(0x3d),\
+ w(0xee), w(0x4c), w(0x95), w(0x0b), w(0x42), w(0xfa), w(0xc3), w(0x4e),\
+ w(0x08), w(0x2e), w(0xa1), w(0x66), w(0x28), w(0xd9), w(0x24), w(0xb2),\
+ w(0x76), w(0x5b), w(0xa2), w(0x49), w(0x6d), w(0x8b), w(0xd1), w(0x25),\
+ w(0x72), w(0xf8), w(0xf6), w(0x64), w(0x86), w(0x68), w(0x98), w(0x16),\
+ w(0xd4), w(0xa4), w(0x5c), w(0xcc), w(0x5d), w(0x65), w(0xb6), w(0x92),\
+ w(0x6c), w(0x70), w(0x48), w(0x50), w(0xfd), w(0xed), w(0xb9), w(0xda),\
+ w(0x5e), w(0x15), w(0x46), w(0x57), w(0xa7), w(0x8d), w(0x9d), w(0x84),\
+ w(0x90), w(0xd8), w(0xab), w(0x00), w(0x8c), w(0xbc), w(0xd3), w(0x0a),\
+ w(0xf7), w(0xe4), w(0x58), w(0x05), w(0xb8), w(0xb3), w(0x45), w(0x06),\
+ w(0xd0), w(0x2c), w(0x1e), w(0x8f), w(0xca), w(0x3f), w(0x0f), w(0x02),\
+ w(0xc1), w(0xaf), w(0xbd), w(0x03), w(0x01), w(0x13), w(0x8a), w(0x6b),\
+ w(0x3a), w(0x91), w(0x11), w(0x41), w(0x4f), w(0x67), w(0xdc), w(0xea),\
+ w(0x97), w(0xf2), w(0xcf), w(0xce), w(0xf0), w(0xb4), w(0xe6), w(0x73),\
+ w(0x96), w(0xac), w(0x74), w(0x22), w(0xe7), w(0xad), w(0x35), w(0x85),\
+ w(0xe2), w(0xf9), w(0x37), w(0xe8), w(0x1c), w(0x75), w(0xdf), w(0x6e),\
+ w(0x47), w(0xf1), w(0x1a), w(0x71), w(0x1d), w(0x29), w(0xc5), w(0x89),\
+ w(0x6f), w(0xb7), w(0x62), w(0x0e), w(0xaa), w(0x18), w(0xbe), w(0x1b),\
+ w(0xfc), w(0x56), w(0x3e), w(0x4b), w(0xc6), w(0xd2), w(0x79), w(0x20),\
+ w(0x9a), w(0xdb), w(0xc0), w(0xfe), w(0x78), w(0xcd), w(0x5a), w(0xf4),\
+ w(0x1f), w(0xdd), w(0xa8), w(0x33), w(0x88), w(0x07), w(0xc7), w(0x31),\
+ w(0xb1), w(0x12), w(0x10), w(0x59), w(0x27), w(0x80), w(0xec), w(0x5f),\
+ w(0x60), w(0x51), w(0x7f), w(0xa9), w(0x19), w(0xb5), w(0x4a), w(0x0d),\
+ w(0x2d), w(0xe5), w(0x7a), w(0x9f), w(0x93), w(0xc9), w(0x9c), w(0xef),\
+ w(0xa0), w(0xe0), w(0x3b), w(0x4d), w(0xae), w(0x2a), w(0xf5), w(0xb0),\
+ w(0xc8), w(0xeb), w(0xbb), w(0x3c), w(0x83), w(0x53), w(0x99), w(0x61),\
+ w(0x17), w(0x2b), w(0x04), w(0x7e), w(0xba), w(0x77), w(0xd6), w(0x26),\
+ w(0xe1), w(0x69), w(0x14), w(0x63), w(0x55), w(0x21), w(0x0c), w(0x7d),
+
+#define mm_data(w) \
+ w(0x00), w(0x01), w(0x02), w(0x03), w(0x04), w(0x05), w(0x06), w(0x07),\
+ w(0x08), w(0x09), w(0x0a), w(0x0b), w(0x0c), w(0x0d), w(0x0e), w(0x0f),\
+ w(0x10), w(0x11), w(0x12), w(0x13), w(0x14), w(0x15), w(0x16), w(0x17),\
+ w(0x18), w(0x19), w(0x1a), w(0x1b), w(0x1c), w(0x1d), w(0x1e), w(0x1f),\
+ w(0x20), w(0x21), w(0x22), w(0x23), w(0x24), w(0x25), w(0x26), w(0x27),\
+ w(0x28), w(0x29), w(0x2a), w(0x2b), w(0x2c), w(0x2d), w(0x2e), w(0x2f),\
+ w(0x30), w(0x31), w(0x32), w(0x33), w(0x34), w(0x35), w(0x36), w(0x37),\
+ w(0x38), w(0x39), w(0x3a), w(0x3b), w(0x3c), w(0x3d), w(0x3e), w(0x3f),\
+ w(0x40), w(0x41), w(0x42), w(0x43), w(0x44), w(0x45), w(0x46), w(0x47),\
+ w(0x48), w(0x49), w(0x4a), w(0x4b), w(0x4c), w(0x4d), w(0x4e), w(0x4f),\
+ w(0x50), w(0x51), w(0x52), w(0x53), w(0x54), w(0x55), w(0x56), w(0x57),\
+ w(0x58), w(0x59), w(0x5a), w(0x5b), w(0x5c), w(0x5d), w(0x5e), w(0x5f),\
+ w(0x60), w(0x61), w(0x62), w(0x63), w(0x64), w(0x65), w(0x66), w(0x67),\
+ w(0x68), w(0x69), w(0x6a), w(0x6b), w(0x6c), w(0x6d), w(0x6e), w(0x6f),\
+ w(0x70), w(0x71), w(0x72), w(0x73), w(0x74), w(0x75), w(0x76), w(0x77),\
+ w(0x78), w(0x79), w(0x7a), w(0x7b), w(0x7c), w(0x7d), w(0x7e), w(0x7f),\
+ w(0x80), w(0x81), w(0x82), w(0x83), w(0x84), w(0x85), w(0x86), w(0x87),\
+ w(0x88), w(0x89), w(0x8a), w(0x8b), w(0x8c), w(0x8d), w(0x8e), w(0x8f),\
+ w(0x90), w(0x91), w(0x92), w(0x93), w(0x94), w(0x95), w(0x96), w(0x97),\
+ w(0x98), w(0x99), w(0x9a), w(0x9b), w(0x9c), w(0x9d), w(0x9e), w(0x9f),\
+ w(0xa0), w(0xa1), w(0xa2), w(0xa3), w(0xa4), w(0xa5), w(0xa6), w(0xa7),\
+ w(0xa8), w(0xa9), w(0xaa), w(0xab), w(0xac), w(0xad), w(0xae), w(0xaf),\
+ w(0xb0), w(0xb1), w(0xb2), w(0xb3), w(0xb4), w(0xb5), w(0xb6), w(0xb7),\
+ w(0xb8), w(0xb9), w(0xba), w(0xbb), w(0xbc), w(0xbd), w(0xbe), w(0xbf),\
+ w(0xc0), w(0xc1), w(0xc2), w(0xc3), w(0xc4), w(0xc5), w(0xc6), w(0xc7),\
+ w(0xc8), w(0xc9), w(0xca), w(0xcb), w(0xcc), w(0xcd), w(0xce), w(0xcf),\
+ w(0xd0), w(0xd1), w(0xd2), w(0xd3), w(0xd4), w(0xd5), w(0xd6), w(0xd7),\
+ w(0xd8), w(0xd9), w(0xda), w(0xdb), w(0xdc), w(0xdd), w(0xde), w(0xdf),\
+ w(0xe0), w(0xe1), w(0xe2), w(0xe3), w(0xe4), w(0xe5), w(0xe6), w(0xe7),\
+ w(0xe8), w(0xe9), w(0xea), w(0xeb), w(0xec), w(0xed), w(0xee), w(0xef),\
+ w(0xf0), w(0xf1), w(0xf2), w(0xf3), w(0xf4), w(0xf5), w(0xf6), w(0xf7),\
+ w(0xf8), w(0xf9), w(0xfa), w(0xfb), w(0xfc), w(0xfd), w(0xfe), w(0xff)
+
+#define h0(x) (x)
+
+/* These defines are used to ensure tables are generated in the
+ right format depending on the internal byte order required
+*/
+
+#define w0(p) bytes2word(p, 0, 0, 0)
+#define w1(p) bytes2word(0, p, 0, 0)
+#define w2(p) bytes2word(0, 0, p, 0)
+#define w3(p) bytes2word(0, 0, 0, p)
+
+#define u0(p) bytes2word(f2(p), p, p, f3(p))
+#define u1(p) bytes2word(f3(p), f2(p), p, p)
+#define u2(p) bytes2word(p, f3(p), f2(p), p)
+#define u3(p) bytes2word(p, p, f3(p), f2(p))
+
+#define v0(p) bytes2word(fe(p), f9(p), fd(p), fb(p))
+#define v1(p) bytes2word(fb(p), fe(p), f9(p), fd(p))
+#define v2(p) bytes2word(fd(p), fb(p), fe(p), f9(p))
+#define v3(p) bytes2word(f9(p), fd(p), fb(p), fe(p))
+
+const aes_32t t_dec(r,c)[RC_LENGTH] =
+{
+ w0(0x01), w0(0x02), w0(0x04), w0(0x08), w0(0x10),
+ w0(0x20), w0(0x40), w0(0x80), w0(0x1b), w0(0x36)
+};
+
+#define d_1(t,n,b,v) const t n[256] = { b(v##0) }
+#define d_4(t,n,b,v) const t n[4][256] = { { b(v##0) }, { b(v##1) }, { b(v##2) }, { b(v##3) } }
+
+#else /* declare and instantiate tables for dynamic value generation in in tab.c */
+
+aes_32t t_dec(r,c)[RC_LENGTH];
+
+#define d_1(t,n,b,v) t n[256]
+#define d_4(t,n,b,v) t n[4][256]
+
+#endif
+
+#else /* declare tables without instantiation */
+
+#if defined(FIXED_TABLES)
+
+extern const aes_32t t_dec(r,c)[RC_LENGTH];
+
+#if defined(_MSC_VER) && defined(TABLE_ALIGN)
+#define d_1(t,n,b,v) extern __declspec(align(TABLE_ALIGN)) const t n[256]
+#define d_4(t,n,b,v) extern __declspec(align(TABLE_ALIGN)) const t n[4][256]
+#else
+#define d_1(t,n,b,v) extern const t n[256]
+#define d_4(t,n,b,v) extern const t n[4][256]
+#endif
+#else
+
+extern aes_32t t_dec(r,c)[RC_LENGTH];
+
+#if defined(_MSC_VER) && defined(TABLE_ALIGN)
+#define d_1(t,n,b,v) extern __declspec(align(TABLE_ALIGN)) t n[256]
+#define d_4(t,n,b,v) extern __declspec(align(TABLE_ALIGN)) t n[4][256]
+#else
+#define d_1(t,n,b,v) extern t n[256]
+#define d_4(t,n,b,v) extern t n[4][256]
+#endif
+#endif
+
+#endif
+
+#ifdef SBX_SET
+ d_1(aes_08t, t_dec(s,box), sb_data, h);
+#endif
+#ifdef ISB_SET
+ d_1(aes_08t, t_dec(i,box), isb_data, h);
+#endif
+
+#ifdef FT1_SET
+ d_1(aes_32t, t_dec(f,n), sb_data, u);
+#endif
+#ifdef FT4_SET
+ d_4(aes_32t, t_dec(f,n), sb_data, u);
+#endif
+
+#ifdef FL1_SET
+ d_1(aes_32t, t_dec(f,l), sb_data, w);
+#endif
+#ifdef FL4_SET
+ d_4(aes_32t, t_dec(f,l), sb_data, w);
+#endif
+
+#ifdef IT1_SET
+ d_1(aes_32t, t_dec(i,n), isb_data, v);
+#endif
+#ifdef IT4_SET
+ d_4(aes_32t, t_dec(i,n), isb_data, v);
+#endif
+
+#ifdef IL1_SET
+ d_1(aes_32t, t_dec(i,l), isb_data, w);
+#endif
+#ifdef IL4_SET
+ d_4(aes_32t, t_dec(i,l), isb_data, w);
+#endif
+
+#ifdef LS1_SET
+#ifdef FL1_SET
+#undef LS1_SET
+#else
+ d_1(aes_32t, t_dec(l,s), sb_data, w);
+#endif
+#endif
+
+#ifdef LS4_SET
+#ifdef FL4_SET
+#undef LS4_SET
+#else
+ d_4(aes_32t, t_dec(l,s), sb_data, w);
+#endif
+#endif
+
+#ifdef IM1_SET
+ d_1(aes_32t, t_dec(i,m), mm_data, v);
+#endif
+#ifdef IM4_SET
+ d_4(aes_32t, t_dec(i,m), mm_data, v);
+#endif
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/trunk/main/aestab.c b/trunk/main/aestab.c
new file mode 100644
index 000000000..b134d22d8
--- /dev/null
+++ b/trunk/main/aestab.c
@@ -0,0 +1,236 @@
+/*
+ ---------------------------------------------------------------------------
+ Copyright (c) 2003, Dr Brian Gladman <brg@gladman.me.uk>, Worcester, UK.
+ All rights reserved.
+
+ LICENSE TERMS
+
+ The free distribution and use of this software in both source and binary
+ form is allowed (with or without changes) provided that:
+
+ 1. distributions of this source code include the above copyright
+ notice, this list of conditions and the following disclaimer;
+
+ 2. distributions in binary form include the above copyright
+ notice, this list of conditions and the following disclaimer
+ in the documentation and/or other associated materials;
+
+ 3. the copyright holder's name is not used to endorse products
+ built using this software without specific written permission.
+
+ ALTERNATIVELY, provided that this notice is retained in full, this product
+ may be distributed under the terms of the GNU General Public License (GPL),
+ in which case the provisions of the GPL apply INSTEAD OF those given above.
+
+ DISCLAIMER
+
+ This software is provided 'as is' with no explicit or implied warranties
+ in respect of its properties, including, but not limited to, correctness
+ and/or fitness for purpose.
+ ---------------------------------------------------------------------------
+ Issue Date: 26/08/2003
+
+*/
+
+#if defined(__cplusplus)
+extern "C"
+{
+#endif
+
+#ifndef HAVE_CRYPTO
+
+#define DO_TABLES
+
+#include "aesopt.h"
+
+#if defined(FIXED_TABLES)
+
+/* implemented in case of wrong call for fixed tables */
+
+void gen_tabs(void)
+{
+}
+
+#else /* dynamic table generation */
+
+#if !defined(FF_TABLES)
+
+/* Generate the tables for the dynamic table option
+
+ It will generally be sensible to use tables to compute finite
+ field multiplies and inverses but where memory is scarse this
+ code might sometimes be better. But it only has effect during
+ initialisation so its pretty unimportant in overall terms.
+*/
+
+/* return 2 ^ (n - 1) where n is the bit number of the highest bit
+ set in x with x in the range 1 < x < 0x00000200. This form is
+ used so that locals within fi can be bytes rather than words
+*/
+
+static aes_08t hibit(const aes_32t x)
+{ aes_08t r = (aes_08t)((x >> 1) | (x >> 2));
+
+ r |= (r >> 2);
+ r |= (r >> 4);
+ return (r + 1) >> 1;
+}
+
+/* return the inverse of the finite field element x */
+
+static aes_08t fi(const aes_08t x)
+{ aes_08t p1 = x, p2 = BPOLY, n1 = hibit(x), n2 = 0x80, v1 = 1, v2 = 0;
+
+ if(x < 2) return x;
+
+ for(;;)
+ {
+ if(!n1) return v1;
+
+ while(n2 >= n1)
+ {
+ n2 /= n1; p2 ^= p1 * n2; v2 ^= v1 * n2; n2 = hibit(p2);
+ }
+
+ if(!n2) return v2;
+
+ while(n1 >= n2)
+ {
+ n1 /= n2; p1 ^= p2 * n1; v1 ^= v2 * n1; n1 = hibit(p1);
+ }
+ }
+}
+
+#endif
+
+/* The forward and inverse affine transformations used in the S-box */
+
+#define fwd_affine(x) \
+ (w = (aes_32t)x, w ^= (w<<1)^(w<<2)^(w<<3)^(w<<4), 0x63^(aes_08t)(w^(w>>8)))
+
+#define inv_affine(x) \
+ (w = (aes_32t)x, w = (w<<1)^(w<<3)^(w<<6), 0x05^(aes_08t)(w^(w>>8)))
+
+static int init = 0;
+
+void gen_tabs(void)
+{ aes_32t i, w;
+
+#if defined(FF_TABLES)
+
+ aes_08t pow[512], log[256];
+
+ if(init) return;
+ /* log and power tables for GF(2^8) finite field with
+ WPOLY as modular polynomial - the simplest primitive
+ root is 0x03, used here to generate the tables
+ */
+
+ i = 0; w = 1;
+ do
+ {
+ pow[i] = (aes_08t)w;
+ pow[i + 255] = (aes_08t)w;
+ log[w] = (aes_08t)i++;
+ w ^= (w << 1) ^ (w & 0x80 ? WPOLY : 0);
+ }
+ while (w != 1);
+
+#else
+ if(init) return;
+#endif
+
+ for(i = 0, w = 1; i < RC_LENGTH; ++i)
+ {
+ t_set(r,c)[i] = bytes2word(w, 0, 0, 0);
+ w = f2(w);
+ }
+
+ for(i = 0; i < 256; ++i)
+ { aes_08t b;
+
+ b = fwd_affine(fi((aes_08t)i));
+ w = bytes2word(f2(b), b, b, f3(b));
+
+#ifdef SBX_SET
+ t_set(s,box)[i] = b;
+#endif
+
+#ifdef FT1_SET /* tables for a normal encryption round */
+ t_set(f,n)[i] = w;
+#endif
+#ifdef FT4_SET
+ t_set(f,n)[0][i] = w;
+ t_set(f,n)[1][i] = upr(w,1);
+ t_set(f,n)[2][i] = upr(w,2);
+ t_set(f,n)[3][i] = upr(w,3);
+#endif
+ w = bytes2word(b, 0, 0, 0);
+
+#ifdef FL1_SET /* tables for last encryption round (may also */
+ t_set(f,l)[i] = w; /* be used in the key schedule) */
+#endif
+#ifdef FL4_SET
+ t_set(f,l)[0][i] = w;
+ t_set(f,l)[1][i] = upr(w,1);
+ t_set(f,l)[2][i] = upr(w,2);
+ t_set(f,l)[3][i] = upr(w,3);
+#endif
+
+#ifdef LS1_SET /* table for key schedule if t_set(f,l) above is */
+ t_set(l,s)[i] = w; /* not of the required form */
+#endif
+#ifdef LS4_SET
+ t_set(l,s)[0][i] = w;
+ t_set(l,s)[1][i] = upr(w,1);
+ t_set(l,s)[2][i] = upr(w,2);
+ t_set(l,s)[3][i] = upr(w,3);
+#endif
+
+ b = fi(inv_affine((aes_08t)i));
+ w = bytes2word(fe(b), f9(b), fd(b), fb(b));
+
+#ifdef IM1_SET /* tables for the inverse mix column operation */
+ t_set(i,m)[b] = w;
+#endif
+#ifdef IM4_SET
+ t_set(i,m)[0][b] = w;
+ t_set(i,m)[1][b] = upr(w,1);
+ t_set(i,m)[2][b] = upr(w,2);
+ t_set(i,m)[3][b] = upr(w,3);
+#endif
+
+#ifdef ISB_SET
+ t_set(i,box)[i] = b;
+#endif
+#ifdef IT1_SET /* tables for a normal decryption round */
+ t_set(i,n)[i] = w;
+#endif
+#ifdef IT4_SET
+ t_set(i,n)[0][i] = w;
+ t_set(i,n)[1][i] = upr(w,1);
+ t_set(i,n)[2][i] = upr(w,2);
+ t_set(i,n)[3][i] = upr(w,3);
+#endif
+ w = bytes2word(b, 0, 0, 0);
+#ifdef IL1_SET /* tables for last decryption round */
+ t_set(i,l)[i] = w;
+#endif
+#ifdef IL4_SET
+ t_set(i,l)[0][i] = w;
+ t_set(i,l)[1][i] = upr(w,1);
+ t_set(i,l)[2][i] = upr(w,2);
+ t_set(i,l)[3][i] = upr(w,3);
+#endif
+ }
+ init = 1;
+}
+
+#endif
+
+#endif /* !HAVE_CRYPTO */
+
+#if defined(__cplusplus)
+}
+#endif
+
diff --git a/trunk/main/alaw.c b/trunk/main/alaw.c
new file mode 100644
index 000000000..8d3595206
--- /dev/null
+++ b/trunk/main/alaw.c
@@ -0,0 +1,210 @@
+/*
+ * 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.
+ */
+
+/*! \file
+ *
+ * \brief a-Law to Signed linear conversion
+ *
+ * \author Mark Spencer <markster@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/alaw.h"
+
+#ifndef G711_NEW_ALGORITHM
+#define AMI_MASK 0x55
+
+static inline unsigned char linear2alaw(short int linear)
+{
+ int mask;
+ int seg;
+ int pcm_val;
+ static int seg_end[8] =
+ {
+ 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF
+ };
+
+ pcm_val = linear;
+ if (pcm_val >= 0) {
+ /* Sign (7th) bit = 1 */
+ mask = AMI_MASK | 0x80;
+ } else {
+ /* Sign bit = 0 */
+ mask = AMI_MASK;
+ pcm_val = -pcm_val;
+ }
+
+ /* Convert the scaled magnitude to segment number. */
+ for (seg = 0; seg < 8; seg++) {
+ if (pcm_val <= seg_end[seg])
+ break;
+ }
+ /* Combine the sign, segment, and quantization bits. */
+ return ((seg << 4) | ((pcm_val >> ((seg) ? (seg + 3) : 4)) & 0x0F)) ^ mask;
+}
+#else
+static unsigned char linear2alaw(short sample, int full_coding)
+{
+ static const unsigned exp_lut[128] = {
+ 1,1,2,2,3,3,3,3,
+ 4,4,4,4,4,4,4,4,
+ 5,5,5,5,5,5,5,5,
+ 5,5,5,5,5,5,5,5,
+ 6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,
+ 7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7 };
+ unsigned sign, exponent, mantissa, mag;
+ unsigned char alawbyte;
+
+ ast_alaw_get_sign_mag(sample, &sign, &mag);
+ if (mag > 32767)
+ mag = 32767; /* clip the magnitude for -32768 */
+
+ exponent = exp_lut[(mag >> 8) & 0x7f];
+ mantissa = (mag >> (exponent + 3)) & 0x0f;
+ if (mag < 0x100)
+ exponent = 0;
+
+ if (full_coding) {
+ /* full encoding, with sign and xform */
+ alawbyte = (unsigned char)(sign | (exponent << 4) | mantissa);
+ alawbyte ^= AST_ALAW_AMI_MASK;
+ } else {
+ /* half-cooked coding -- mantissa+exponent only (for lookup tab) */
+ alawbyte = (exponent << 4) | mantissa;
+ }
+ return alawbyte;
+}
+#endif
+
+#ifndef G711_NEW_ALGORITHM
+static inline short int alaw2linear (unsigned char alaw)
+{
+ int i;
+ int seg;
+
+ alaw ^= AMI_MASK;
+ i = ((alaw & 0x0F) << 4) + 8 /* rounding error */;
+ seg = (((int) alaw & 0x70) >> 4);
+ if (seg)
+ i = (i + 0x100) << (seg - 1);
+ return (short int) ((alaw & 0x80) ? i : -i);
+}
+#else
+static inline short alaw2linear(unsigned char alawbyte)
+{
+ unsigned exponent, mantissa;
+ short sample;
+
+ alawbyte ^= AST_ALAW_AMI_MASK;
+ exponent = (alawbyte & 0x70) >> 4;
+ mantissa = alawbyte & 0x0f;
+ sample = (mantissa << 4) + 8 /* rounding error */;
+ if (exponent)
+ sample = (sample + 0x100) << (exponent - 1);
+ if (!(alawbyte & 0x80))
+ sample = -sample;
+ return sample;
+}
+#endif
+
+
+
+#ifndef G711_NEW_ALGORITHM
+unsigned char __ast_lin2a[8192];
+#else
+unsigned char __ast_lin2a[AST_ALAW_TAB_SIZE];
+#endif
+short __ast_alaw[256];
+
+void ast_alaw_init(void)
+{
+ int i;
+ /*
+ * Set up mu-law conversion table
+ */
+#ifndef G711_NEW_ALGORITHM
+ for (i = 0; i < 256; i++) {
+ __ast_alaw[i] = alaw2linear(i);
+ }
+ /* set up the reverse (mu-law) conversion table */
+ for (i = -32768; i < 32768; i++) {
+ __ast_lin2a[((unsigned short)i) >> 3] = linear2alaw(i);
+ }
+#else
+ for (i = 0; i < 256; i++) {
+ __ast_alaw[i] = alaw2linear(i);
+ }
+ /* set up the reverse (a-law) conversion table */
+ for (i = 0; i <= 32768; i += AST_ALAW_STEP) {
+ AST_LIN2A_LOOKUP(i) = linear2alaw(i, 0 /* half-cooked */);
+ }
+#endif
+
+#ifdef TEST_CODING_TABLES
+ for (i = -32768; i < 32768; ++i) {
+#ifndef G711_NEW_ALGORITHM
+ unsigned char e1 = linear2alaw(i);
+#else
+ unsigned char e1 = linear2alaw(i, 1);
+#endif
+ short d1 = alaw2linear(e1);
+ unsigned char e2 = AST_LIN2A(i);
+ short d2 = alaw2linear(e2);
+ short d3 = AST_ALAW(e1);
+
+ if (e1 != e2 || d1 != d3 || d2 != d3) {
+ ast_log(LOG_WARNING, "a-Law coding tables test failed on %d: e1=%u, e2=%u, d1=%d, d2=%d\n",
+ i, (unsigned)e1, (unsigned)e2, (int)d1, (int)d2);
+ }
+ }
+ ast_log(LOG_NOTICE, "a-Law coding tables test complete.\n");
+#endif /* TEST_CODING_TABLES */
+
+#ifdef TEST_TANDEM_TRANSCODING
+ /* tandem transcoding test */
+ for (i = -32768; i < 32768; ++i) {
+ unsigned char e1 = AST_LIN2A(i);
+ short d1 = AST_ALAW(e1);
+ unsigned char e2 = AST_LIN2A(d1);
+ short d2 = AST_ALAW(e2);
+ unsigned char e3 = AST_LIN2A(d2);
+ short d3 = AST_ALAW(e3);
+
+ if (e1 != e2 || e2 != e3 || d1 != d2 || d2 != d3) {
+ ast_log(LOG_WARNING, "a-Law tandem transcoding test failed on %d: e1=%u, e2=%u, d1=%d, d2=%d, d3=%d\n",
+ i, (unsigned)e1, (unsigned)e2, (int)d1, (int)d2, (int)d3);
+ }
+ }
+ ast_log(LOG_NOTICE, "a-Law tandem transcoding test complete.\n");
+#endif /* TEST_TANDEM_TRANSCODING */
+
+}
+
diff --git a/trunk/main/app.c b/trunk/main/app.c
new file mode 100644
index 000000000..696b22c6f
--- /dev/null
+++ b/trunk/main/app.c
@@ -0,0 +1,1739 @@
+/*
+ * 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.
+ */
+
+/*! \file
+ *
+ * \brief Convenient Application Routines
+ *
+ * \author Mark Spencer <markster@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#include <regex.h>
+#include <sys/file.h> /* added this to allow to compile, sorry! */
+
+#include "asterisk/paths.h" /* use ast_config_AST_DATA_DIR */
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/file.h"
+#include "asterisk/app.h"
+#include "asterisk/dsp.h"
+#include "asterisk/utils.h"
+#include "asterisk/lock.h"
+#include "asterisk/indications.h"
+#include "asterisk/linkedlists.h"
+
+#define MAX_OTHER_FORMATS 10
+
+static AST_RWLIST_HEAD_STATIC(groups, ast_group_info);
+
+/*!
+ * \brief This function presents a dialtone and reads an extension into 'collect'
+ * which must be a pointer to a **pre-initialized** array of char having a
+ * size of 'size' suitable for writing to. It will collect no more than the smaller
+ * of 'maxlen' or 'size' minus the original strlen() of collect digits.
+ * \param chan struct.
+ * \param context
+ * \param collect
+ * \param size
+ * \param maxlen
+ * \param timeout timeout in seconds
+ *
+ * \return 0 if extension does not exist, 1 if extension exists
+*/
+int ast_app_dtget(struct ast_channel *chan, const char *context, char *collect, size_t size, int maxlen, int timeout)
+{
+ struct ind_tone_zone_sound *ts;
+ int res = 0, x = 0;
+
+ if (maxlen > size)
+ maxlen = size;
+
+ if (!timeout && chan->pbx)
+ timeout = chan->pbx->dtimeout;
+ else if (!timeout)
+ timeout = 5;
+
+ if ((ts = ast_get_indication_tone(chan->zone, "dial")) && ts->data[0])
+ res = ast_playtones_start(chan, 0, ts->data, 0);
+ else
+ ast_log(LOG_NOTICE,"Huh....? no dial for indications?\n");
+
+ for (x = strlen(collect); x < maxlen; ) {
+ res = ast_waitfordigit(chan, timeout);
+ if (!ast_ignore_pattern(context, collect))
+ ast_playtones_stop(chan);
+ if (res < 1)
+ break;
+ if (res == '#')
+ break;
+ collect[x++] = res;
+ if (!ast_matchmore_extension(chan, context, collect, 1, chan->cid.cid_num))
+ break;
+ }
+
+ if (res >= 0)
+ res = ast_exists_extension(chan, context, collect, 1, chan->cid.cid_num) ? 1 : 0;
+
+ return res;
+}
+
+/*!
+ * \brief ast_app_getdata
+ * \param c The channel to read from
+ * \param prompt The file to stream to the channel
+ * \param s The string to read in to. Must be at least the size of your length
+ * \param maxlen How many digits to read (maximum)
+ * \param timeout set timeout to 0 for "standard" timeouts. Set timeout to -1 for
+ * "ludicrous time" (essentially never times out) */
+int ast_app_getdata(struct ast_channel *c, const char *prompt, char *s, int maxlen, int timeout)
+{
+ int res = 0, to, fto;
+ char *front, *filename;
+
+ /* XXX Merge with full version? XXX */
+
+ if (maxlen)
+ s[0] = '\0';
+
+ if (!prompt)
+ prompt="";
+
+ filename = ast_strdupa(prompt);
+ while ((front = strsep(&filename, "&"))) {
+ if (!ast_strlen_zero(front)) {
+ res = ast_streamfile(c, front, c->language);
+ if (res)
+ continue;
+ }
+ if (ast_strlen_zero(filename)) {
+ /* set timeouts for the last prompt */
+ fto = c->pbx ? c->pbx->rtimeout * 1000 : 6000;
+ to = c->pbx ? c->pbx->dtimeout * 1000 : 2000;
+
+ if (timeout > 0)
+ fto = to = timeout;
+ if (timeout < 0)
+ fto = to = 1000000000;
+ } else {
+ /* there is more than one prompt, so
+ get rid of the long timeout between
+ prompts, and make it 50ms */
+ fto = 50;
+ to = c->pbx ? c->pbx->dtimeout * 1000 : 2000;
+ }
+ res = ast_readstring(c, s, maxlen, to, fto, "#");
+ if (!ast_strlen_zero(s))
+ return res;
+ }
+
+ return res;
+}
+
+/* The lock type used by ast_lock_path() / ast_unlock_path() */
+static enum AST_LOCK_TYPE ast_lock_type = AST_LOCK_TYPE_LOCKFILE;
+
+int ast_app_getdata_full(struct ast_channel *c, char *prompt, char *s, int maxlen, int timeout, int audiofd, int ctrlfd)
+{
+ int res, to = 2000, fto = 6000;
+
+ if (!ast_strlen_zero(prompt)) {
+ res = ast_streamfile(c, prompt, c->language);
+ if (res < 0)
+ return res;
+ }
+
+ if (timeout > 0)
+ fto = to = timeout;
+ if (timeout < 0)
+ fto = to = 1000000000;
+
+ res = ast_readstring_full(c, s, maxlen, to, fto, "#", audiofd, ctrlfd);
+
+ return res;
+}
+
+static int (*ast_has_voicemail_func)(const char *mailbox, const char *folder) = NULL;
+static int (*ast_inboxcount_func)(const char *mailbox, int *newmsgs, int *oldmsgs) = NULL;
+static int (*ast_messagecount_func)(const char *context, const char *mailbox, const char *folder) = NULL;
+
+void ast_install_vm_functions(int (*has_voicemail_func)(const char *mailbox, const char *folder),
+ int (*inboxcount_func)(const char *mailbox, int *newmsgs, int *oldmsgs),
+ int (*messagecount_func)(const char *context, const char *mailbox, const char *folder))
+{
+ ast_has_voicemail_func = has_voicemail_func;
+ ast_inboxcount_func = inboxcount_func;
+ ast_messagecount_func = messagecount_func;
+}
+
+void ast_uninstall_vm_functions(void)
+{
+ ast_has_voicemail_func = NULL;
+ ast_inboxcount_func = NULL;
+ ast_messagecount_func = NULL;
+}
+
+int ast_app_has_voicemail(const char *mailbox, const char *folder)
+{
+ static int warned = 0;
+ if (ast_has_voicemail_func)
+ return ast_has_voicemail_func(mailbox, folder);
+
+ if (!warned) {
+ ast_verb(3, "Message check requested for mailbox %s/folder %s but voicemail not loaded.\n", mailbox, folder ? folder : "INBOX");
+ warned++;
+ }
+ return 0;
+}
+
+
+int ast_app_inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
+{
+ static int warned = 0;
+ if (newmsgs)
+ *newmsgs = 0;
+ if (oldmsgs)
+ *oldmsgs = 0;
+ if (ast_inboxcount_func)
+ return ast_inboxcount_func(mailbox, newmsgs, oldmsgs);
+
+ if (!warned) {
+ warned++;
+ ast_verb(3, "Message count requested for mailbox %s but voicemail not loaded.\n", mailbox);
+ }
+
+ return 0;
+}
+
+int ast_app_messagecount(const char *context, const char *mailbox, const char *folder)
+{
+ static int warned = 0;
+ if (ast_messagecount_func)
+ return ast_messagecount_func(context, mailbox, folder);
+
+ if (!warned) {
+ warned++;
+ ast_verb(3, "Message count requested for mailbox %s@%s/%s but voicemail not loaded.\n", mailbox, context, folder);
+ }
+
+ return 0;
+}
+
+int ast_dtmf_stream(struct ast_channel *chan, struct ast_channel *peer, const char *digits, int between, unsigned int duration)
+{
+ const char *ptr;
+ int res = 0;
+
+ if (!between)
+ between = 100;
+
+ if (peer)
+ res = ast_autoservice_start(peer);
+
+ if (!res)
+ res = ast_waitfor(chan, 100);
+
+ /* ast_waitfor will return the number of remaining ms on success */
+ if (res < 0)
+ return res;
+
+ for (ptr = digits; *ptr; ptr++) {
+ if (*ptr == 'w') {
+ /* 'w' -- wait half a second */
+ if ((res = ast_safe_sleep(chan, 500)))
+ break;
+ } else if (strchr("0123456789*#abcdfABCDF", *ptr)) {
+ /* Character represents valid DTMF */
+ if (*ptr == 'f' || *ptr == 'F') {
+ /* ignore return values if not supported by channel */
+ ast_indicate(chan, AST_CONTROL_FLASH);
+ } else
+ ast_senddigit(chan, *ptr, duration);
+ /* pause between digits */
+ if ((res = ast_safe_sleep(chan, between)))
+ break;
+ } else
+ ast_log(LOG_WARNING, "Illegal DTMF character '%c' in string. (0-9*#aAbBcCdD allowed)\n",*ptr);
+ }
+
+ if (peer) {
+ /* Stop autoservice on the peer channel, but don't overwrite any error condition
+ that has occurred previously while acting on the primary channel */
+ if (ast_autoservice_stop(peer) && !res)
+ res = -1;
+ }
+
+ return res;
+}
+
+struct linear_state {
+ int fd;
+ int autoclose;
+ int allowoverride;
+ int origwfmt;
+};
+
+static void linear_release(struct ast_channel *chan, void *params)
+{
+ struct linear_state *ls = params;
+
+ if (ls->origwfmt && ast_set_write_format(chan, ls->origwfmt))
+ ast_log(LOG_WARNING, "Unable to restore channel '%s' to format '%d'\n", chan->name, ls->origwfmt);
+
+ if (ls->autoclose)
+ close(ls->fd);
+
+ ast_free(params);
+}
+
+static int linear_generator(struct ast_channel *chan, void *data, int len, int samples)
+{
+ short buf[2048 + AST_FRIENDLY_OFFSET / 2];
+ struct linear_state *ls = data;
+ struct ast_frame f = {
+ .frametype = AST_FRAME_VOICE,
+ .subclass = AST_FORMAT_SLINEAR,
+ .data = buf + AST_FRIENDLY_OFFSET / 2,
+ .offset = AST_FRIENDLY_OFFSET,
+ };
+ int res;
+
+ len = samples * 2;
+ if (len > sizeof(buf) - AST_FRIENDLY_OFFSET) {
+ ast_log(LOG_WARNING, "Can't generate %d bytes of data!\n" ,len);
+ len = sizeof(buf) - AST_FRIENDLY_OFFSET;
+ }
+ res = read(ls->fd, buf + AST_FRIENDLY_OFFSET/2, len);
+ if (res > 0) {
+ f.datalen = res;
+ f.samples = res / 2;
+ ast_write(chan, &f);
+ if (res == len)
+ return 0;
+ }
+ return -1;
+}
+
+static void *linear_alloc(struct ast_channel *chan, void *params)
+{
+ struct linear_state *ls = params;
+
+ if (!params)
+ return NULL;
+
+ /* In this case, params is already malloc'd */
+ if (ls->allowoverride)
+ ast_set_flag(chan, AST_FLAG_WRITE_INT);
+ else
+ ast_clear_flag(chan, AST_FLAG_WRITE_INT);
+
+ ls->origwfmt = chan->writeformat;
+
+ if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) {
+ ast_log(LOG_WARNING, "Unable to set '%s' to linear format (write)\n", chan->name);
+ ast_free(ls);
+ ls = params = NULL;
+ }
+
+ return params;
+}
+
+static struct ast_generator linearstream =
+{
+ alloc: linear_alloc,
+ release: linear_release,
+ generate: linear_generator,
+};
+
+int ast_linear_stream(struct ast_channel *chan, const char *filename, int fd, int allowoverride)
+{
+ struct linear_state *lin;
+ char tmpf[256];
+ int res = -1;
+ int autoclose = 0;
+ if (fd < 0) {
+ if (ast_strlen_zero(filename))
+ return -1;
+ autoclose = 1;
+ if (filename[0] == '/')
+ ast_copy_string(tmpf, filename, sizeof(tmpf));
+ else
+ snprintf(tmpf, sizeof(tmpf), "%s/%s/%s", ast_config_AST_DATA_DIR, "sounds", filename);
+ if ((fd = open(tmpf, O_RDONLY)) < 0) {
+ ast_log(LOG_WARNING, "Unable to open file '%s': %s\n", tmpf, strerror(errno));
+ return -1;
+ }
+ }
+ if ((lin = ast_calloc(1, sizeof(*lin)))) {
+ lin->fd = fd;
+ lin->allowoverride = allowoverride;
+ lin->autoclose = autoclose;
+ res = ast_activate_generator(chan, &linearstream, lin);
+ }
+ return res;
+}
+
+int ast_control_streamfile(struct ast_channel *chan, const char *file,
+ const char *fwd, const char *rev,
+ const char *stop, const char *pause,
+ const char *restart, int skipms, long *offsetms)
+{
+ char *breaks = NULL;
+ char *end = NULL;
+ int blen = 2;
+ int res;
+ long pause_restart_point = 0;
+ long offset = 0;
+
+ if (offsetms)
+ offset = *offsetms * 8; /* XXX Assumes 8kHz */
+
+ if (stop)
+ blen += strlen(stop);
+ if (pause)
+ blen += strlen(pause);
+ if (restart)
+ blen += strlen(restart);
+
+ if (blen > 2) {
+ breaks = alloca(blen + 1);
+ breaks[0] = '\0';
+ if (stop)
+ strcat(breaks, stop);
+ if (pause)
+ strcat(breaks, pause);
+ if (restart)
+ strcat(breaks, restart);
+ }
+ if (chan->_state != AST_STATE_UP)
+ res = ast_answer(chan);
+
+ if (file) {
+ if ((end = strchr(file,':'))) {
+ if (!strcasecmp(end, ":end")) {
+ *end = '\0';
+ end++;
+ }
+ }
+ }
+
+ for (;;) {
+ ast_stopstream(chan);
+ res = ast_streamfile(chan, file, chan->language);
+ if (!res) {
+ if (pause_restart_point) {
+ ast_seekstream(chan->stream, pause_restart_point, SEEK_SET);
+ pause_restart_point = 0;
+ }
+ else if (end || offset < 0) {
+ if (offset == -8)
+ offset = 0;
+ ast_verb(3, "ControlPlayback seek to offset %ld from end\n", offset);
+
+ ast_seekstream(chan->stream, offset, SEEK_END);
+ end = NULL;
+ offset = 0;
+ } else if (offset) {
+ ast_verb(3, "ControlPlayback seek to offset %ld\n", offset);
+ ast_seekstream(chan->stream, offset, SEEK_SET);
+ offset = 0;
+ };
+ res = ast_waitstream_fr(chan, breaks, fwd, rev, skipms);
+ }
+
+ if (res < 1)
+ break;
+
+ /* We go at next loop if we got the restart char */
+ if (restart && strchr(restart, res)) {
+ ast_debug(1, "we'll restart the stream here at next loop\n");
+ pause_restart_point = 0;
+ continue;
+ }
+
+ if (pause && strchr(pause, res)) {
+ pause_restart_point = ast_tellstream(chan->stream);
+ for (;;) {
+ ast_stopstream(chan);
+ res = ast_waitfordigit(chan, 1000);
+ if (!res)
+ continue;
+ else if (res == -1 || strchr(pause, res) || (stop && strchr(stop, res)))
+ break;
+ }
+ if (res == *pause) {
+ res = 0;
+ continue;
+ }
+ }
+
+ if (res == -1)
+ break;
+
+ /* if we get one of our stop chars, return it to the calling function */
+ if (stop && strchr(stop, res))
+ break;
+ }
+
+ if (pause_restart_point) {
+ offset = pause_restart_point;
+ } else {
+ if (chan->stream) {
+ offset = ast_tellstream(chan->stream);
+ } else {
+ offset = -8; /* indicate end of file */
+ }
+ }
+
+ if (offsetms)
+ *offsetms = offset / 8; /* samples --> ms ... XXX Assumes 8 kHz */
+
+ /* If we are returning a digit cast it as char */
+ if (res > 0 || chan->stream)
+ res = (char)res;
+
+ ast_stopstream(chan);
+
+ return res;
+}
+
+int ast_play_and_wait(struct ast_channel *chan, const char *fn)
+{
+ int d = 0;
+
+ if ((d = ast_streamfile(chan, fn, chan->language)))
+ return d;
+
+ d = ast_waitstream(chan, AST_DIGIT_ANY);
+
+ ast_stopstream(chan);
+
+ return d;
+}
+
+static int global_silence_threshold = 128;
+static int global_maxsilence = 0;
+
+/*! Optionally play a sound file or a beep, then record audio and video from the channel.
+ * \param chan Channel to playback to/record from.
+ * \param playfile Filename of sound to play before recording begins.
+ * \param recordfile Filename to record to.
+ * \param maxtime Maximum length of recording (in milliseconds).
+ * \param fmt Format(s) to record message in. Multiple formats may be specified by separating them with a '|'.
+ * \param duration Where to store actual length of the recorded message (in milliseconds).
+ * \param beep Whether to play a beep before starting to record.
+ * \param silencethreshold
+ * \param maxsilence Length of silence that will end a recording (in milliseconds).
+ * \param path Optional filesystem path to unlock.
+ * \param prepend If true, prepend the recorded audio to an existing file.
+ * \param acceptdtmf DTMF digits that will end the recording.
+ * \param canceldtmf DTMF digits that will cancel the recording.
+ */
+
+static int __ast_play_and_record(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, int beep, int silencethreshold, int maxsilence, const char *path, int prepend, const char *acceptdtmf, const char *canceldtmf)
+{
+ int d = 0;
+ char *fmts;
+ char comment[256];
+ int x, fmtcnt = 1, res = -1, outmsg = 0;
+ struct ast_filestream *others[MAX_OTHER_FORMATS];
+ char *sfmt[MAX_OTHER_FORMATS];
+ char *stringp = NULL;
+ time_t start, end;
+ struct ast_dsp *sildet = NULL; /* silence detector dsp */
+ int totalsilence = 0;
+ int rfmt = 0;
+ struct ast_silence_generator *silgen = NULL;
+ char prependfile[80];
+
+ if (silencethreshold < 0)
+ silencethreshold = global_silence_threshold;
+
+ if (maxsilence < 0)
+ maxsilence = global_maxsilence;
+
+ /* barf if no pointer passed to store duration in */
+ if (!duration) {
+ ast_log(LOG_WARNING, "Error play_and_record called without duration pointer\n");
+ return -1;
+ }
+
+ ast_debug(1, "play_and_record: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
+ snprintf(comment, sizeof(comment), "Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
+
+ if (playfile || beep) {
+ if (!beep)
+ d = ast_play_and_wait(chan, playfile);
+ if (d > -1)
+ d = ast_stream_and_wait(chan, "beep", "");
+ if (d < 0)
+ return -1;
+ }
+
+ if (prepend) {
+ ast_copy_string(prependfile, recordfile, sizeof(prependfile));
+ strncat(prependfile, "-prepend", sizeof(prependfile) - strlen(prependfile) - 1);
+ }
+
+ fmts = ast_strdupa(fmt);
+
+ stringp = fmts;
+ strsep(&stringp, "|");
+ ast_debug(1, "Recording Formats: sfmts=%s\n", fmts);
+ sfmt[0] = ast_strdupa(fmts);
+
+ while ((fmt = strsep(&stringp, "|"))) {
+ if (fmtcnt > MAX_OTHER_FORMATS - 1) {
+ ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app.c\n");
+ break;
+ }
+ sfmt[fmtcnt++] = ast_strdupa(fmt);
+ }
+
+ end = start = time(NULL); /* pre-initialize end to be same as start in case we never get into loop */
+ for (x = 0; x < fmtcnt; x++) {
+ others[x] = ast_writefile(prepend ? prependfile : recordfile, sfmt[x], comment, O_TRUNC, 0, AST_FILE_MODE);
+ ast_verb(3, "x=%d, open writing: %s format: %s, %p\n", x, prepend ? prependfile : recordfile, sfmt[x], others[x]);
+
+ if (!others[x])
+ break;
+ }
+
+ if (path)
+ ast_unlock_path(path);
+
+ if (maxsilence > 0) {
+ sildet = ast_dsp_new(); /* Create the silence detector */
+ if (!sildet) {
+ ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
+ return -1;
+ }
+ ast_dsp_set_threshold(sildet, silencethreshold);
+ rfmt = chan->readformat;
+ res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
+ ast_dsp_free(sildet);
+ return -1;
+ }
+ }
+
+ if (!prepend) {
+ /* Request a video update */
+ ast_indicate(chan, AST_CONTROL_VIDUPDATE);
+
+ if (ast_opt_transmit_silence)
+ silgen = ast_channel_start_silence_generator(chan);
+ }
+
+ if (x == fmtcnt) {
+ /* Loop forever, writing the packets we read to the writer(s), until
+ we read a digit or get a hangup */
+ struct ast_frame *f;
+ for (;;) {
+ res = ast_waitfor(chan, 2000);
+ if (!res) {
+ ast_debug(1, "One waitfor failed, trying another\n");
+ /* Try one more time in case of masq */
+ res = ast_waitfor(chan, 2000);
+ if (!res) {
+ ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
+ res = -1;
+ }
+ }
+
+ if (res < 0) {
+ f = NULL;
+ break;
+ }
+ f = ast_read(chan);
+ if (!f)
+ break;
+ if (f->frametype == AST_FRAME_VOICE) {
+ /* write each format */
+ for (x = 0; x < fmtcnt; x++) {
+ if (prepend && !others[x])
+ break;
+ res = ast_writestream(others[x], f);
+ }
+
+ /* Silence Detection */
+ if (maxsilence > 0) {
+ int dspsilence = 0;
+ ast_dsp_silence(sildet, f, &dspsilence);
+ if (dspsilence)
+ totalsilence = dspsilence;
+ else
+ totalsilence = 0;
+
+ if (totalsilence > maxsilence) {
+ /* Ended happily with silence */
+ ast_verb(3, "Recording automatically stopped after a silence of %d seconds\n", totalsilence/1000);
+ res = 'S';
+ outmsg = 2;
+ break;
+ }
+ }
+ /* Exit on any error */
+ if (res) {
+ ast_log(LOG_WARNING, "Error writing frame\n");
+ break;
+ }
+ } else if (f->frametype == AST_FRAME_VIDEO) {
+ /* Write only once */
+ ast_writestream(others[0], f);
+ } else if (f->frametype == AST_FRAME_DTMF) {
+ if (prepend) {
+ /* stop recording with any digit */
+ ast_verb(3, "User ended message by pressing %c\n", f->subclass);
+ res = 't';
+ outmsg = 2;
+ break;
+ }
+ if (strchr(acceptdtmf, f->subclass)) {
+ ast_verb(3, "User ended message by pressing %c\n", f->subclass);
+ res = f->subclass;
+ outmsg = 2;
+ break;
+ }
+ if (strchr(canceldtmf, f->subclass)) {
+ ast_verb(3, "User cancelled message by pressing %c\n", f->subclass);
+ res = f->subclass;
+ outmsg = 0;
+ break;
+ }
+ }
+ if (maxtime) {
+ end = time(NULL);
+ if (maxtime < (end - start)) {
+ ast_verb(3, "Took too long, cutting it short...\n");
+ res = 't';
+ outmsg = 2;
+ break;
+ }
+ }
+ ast_frfree(f);
+ }
+ if (!f) {
+ ast_verb(3, "User hung up\n");
+ res = -1;
+ outmsg = 1;
+ } else {
+ ast_frfree(f);
+ }
+ } else {
+ ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", recordfile, sfmt[x]);
+ }
+
+ if (!prepend) {
+ if (silgen)
+ ast_channel_stop_silence_generator(chan, silgen);
+ }
+
+ /*!\note
+ * Instead of asking how much time passed (end - start), calculate the number
+ * of seconds of audio which actually went into the file. This fixes a
+ * problem where audio is stopped up on the network and never gets to us.
+ *
+ * Note that we still want to use the number of seconds passed for the max
+ * message, otherwise we could get a situation where this stream is never
+ * closed (which would create a resource leak).
+ */
+ *duration = ast_tellstream(others[0]) / 8000;
+
+ if (!prepend) {
+ for (x = 0; x < fmtcnt; x++) {
+ if (!others[x])
+ break;
+ /*!\note
+ * If we ended with silence, trim all but the first 200ms of silence
+ * off the recording. However, if we ended with '#', we don't want
+ * to trim ANY part of the recording.
+ */
+ if (res > 0 && totalsilence)
+ ast_stream_rewind(others[x], totalsilence - 200);
+ ast_truncstream(others[x]);
+ ast_closestream(others[x]);
+ }
+ }
+
+ if (prepend && outmsg) {
+ struct ast_filestream *realfiles[MAX_OTHER_FORMATS];
+ struct ast_frame *fr;
+
+ for (x = 0; x < fmtcnt; x++) {
+ snprintf(comment, sizeof(comment), "Opening the real file %s.%s\n", recordfile, sfmt[x]);
+ realfiles[x] = ast_readfile(recordfile, sfmt[x], comment, O_RDONLY, 0, 0);
+ if (!others[x] || !realfiles[x])
+ break;
+ /*!\note Same logic as above. */
+ if (totalsilence)
+ ast_stream_rewind(others[x], totalsilence - 200);
+ ast_truncstream(others[x]);
+ /* add the original file too */
+ while ((fr = ast_readframe(realfiles[x]))) {
+ ast_writestream(others[x], fr);
+ ast_frfree(fr);
+ }
+ ast_closestream(others[x]);
+ ast_closestream(realfiles[x]);
+ ast_filerename(prependfile, recordfile, sfmt[x]);
+ ast_verb(4, "Recording Format: sfmts=%s, prependfile %s, recordfile %s\n", sfmt[x], prependfile, recordfile);
+ ast_filedelete(prependfile, sfmt[x]);
+ }
+ }
+ if (rfmt && ast_set_read_format(chan, rfmt)) {
+ ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
+ }
+ if (outmsg == 2) {
+ ast_stream_and_wait(chan, "auth-thankyou", "");
+ }
+ if (sildet)
+ ast_dsp_free(sildet);
+ return res;
+}
+
+static char default_acceptdtmf[] = "#";
+static char default_canceldtmf[] = "";
+
+int ast_play_and_record_full(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, int silencethreshold, int maxsilence, const char *path, const char *acceptdtmf, const char *canceldtmf)
+{
+ return __ast_play_and_record(chan, playfile, recordfile, maxtime, fmt, duration, 0, silencethreshold, maxsilence, path, 0, S_OR(acceptdtmf, default_acceptdtmf), S_OR(canceldtmf, default_canceldtmf));
+}
+
+int ast_play_and_record(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, int silencethreshold, int maxsilence, const char *path)
+{
+ return __ast_play_and_record(chan, playfile, recordfile, maxtime, fmt, duration, 0, silencethreshold, maxsilence, path, 0, default_acceptdtmf, default_canceldtmf);
+}
+
+int ast_play_and_prepend(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int *duration, int beep, int silencethreshold, int maxsilence)
+{
+ return __ast_play_and_record(chan, playfile, recordfile, maxtime, fmt, duration, beep, silencethreshold, maxsilence, NULL, 1, default_acceptdtmf, default_canceldtmf);
+}
+
+/* Channel group core functions */
+
+int ast_app_group_split_group(const char *data, char *group, int group_max, char *category, int category_max)
+{
+ int res = 0;
+ char tmp[256];
+ char *grp = NULL, *cat = NULL;
+
+ if (!ast_strlen_zero(data)) {
+ ast_copy_string(tmp, data, sizeof(tmp));
+ grp = tmp;
+ cat = strchr(tmp, '@');
+ if (cat) {
+ *cat = '\0';
+ cat++;
+ }
+ }
+
+ if (!ast_strlen_zero(grp))
+ ast_copy_string(group, grp, group_max);
+ else
+ *group = '\0';
+
+ if (!ast_strlen_zero(cat))
+ ast_copy_string(category, cat, category_max);
+
+ return res;
+}
+
+int ast_app_group_set_channel(struct ast_channel *chan, const char *data)
+{
+ int res = 0;
+ char group[80] = "", category[80] = "";
+ struct ast_group_info *gi = NULL;
+ size_t len = 0;
+
+ if (ast_app_group_split_group(data, group, sizeof(group), category, sizeof(category)))
+ return -1;
+
+ /* Calculate memory we will need if this is new */
+ len = sizeof(*gi) + strlen(group) + 1;
+ if (!ast_strlen_zero(category))
+ len += strlen(category) + 1;
+
+ AST_RWLIST_WRLOCK(&groups);
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&groups, gi, list) {
+ if ((gi->chan == chan) && ((ast_strlen_zero(category) && ast_strlen_zero(gi->category)) || (!ast_strlen_zero(gi->category) && !strcasecmp(gi->category, category)))) {
+ AST_RWLIST_REMOVE_CURRENT(list);
+ free(gi);
+ break;
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+
+ if (ast_strlen_zero(group)) {
+ /* Enable unsetting the group */
+ } else if ((gi = calloc(1, len))) {
+ gi->chan = chan;
+ gi->group = (char *) gi + sizeof(*gi);
+ strcpy(gi->group, group);
+ if (!ast_strlen_zero(category)) {
+ gi->category = (char *) gi + sizeof(*gi) + strlen(group) + 1;
+ strcpy(gi->category, category);
+ }
+ AST_RWLIST_INSERT_TAIL(&groups, gi, list);
+ } else {
+ res = -1;
+ }
+
+ AST_RWLIST_UNLOCK(&groups);
+
+ return res;
+}
+
+int ast_app_group_get_count(const char *group, const char *category)
+{
+ struct ast_group_info *gi = NULL;
+ int count = 0;
+
+ if (ast_strlen_zero(group))
+ return 0;
+
+ AST_RWLIST_RDLOCK(&groups);
+ AST_RWLIST_TRAVERSE(&groups, gi, list) {
+ if (!strcasecmp(gi->group, group) && (ast_strlen_zero(category) || (!ast_strlen_zero(gi->category) && !strcasecmp(gi->category, category))))
+ count++;
+ }
+ AST_RWLIST_UNLOCK(&groups);
+
+ return count;
+}
+
+int ast_app_group_match_get_count(const char *groupmatch, const char *category)
+{
+ struct ast_group_info *gi = NULL;
+ regex_t regexbuf;
+ int count = 0;
+
+ if (ast_strlen_zero(groupmatch))
+ return 0;
+
+ /* if regex compilation fails, return zero matches */
+ if (regcomp(&regexbuf, groupmatch, REG_EXTENDED | REG_NOSUB))
+ return 0;
+
+ AST_RWLIST_RDLOCK(&groups);
+ AST_RWLIST_TRAVERSE(&groups, gi, list) {
+ if (!regexec(&regexbuf, gi->group, 0, NULL, 0) && (ast_strlen_zero(category) || (!ast_strlen_zero(gi->category) && !strcasecmp(gi->category, category))))
+ count++;
+ }
+ AST_RWLIST_UNLOCK(&groups);
+
+ regfree(&regexbuf);
+
+ return count;
+}
+
+int ast_app_group_update(struct ast_channel *old, struct ast_channel *new)
+{
+ struct ast_group_info *gi = NULL;
+
+ AST_RWLIST_WRLOCK(&groups);
+ AST_RWLIST_TRAVERSE(&groups, gi, list) {
+ if (gi->chan == old)
+ gi->chan = new;
+ }
+ AST_RWLIST_UNLOCK(&groups);
+
+ return 0;
+}
+
+int ast_app_group_discard(struct ast_channel *chan)
+{
+ struct ast_group_info *gi = NULL;
+
+ AST_RWLIST_WRLOCK(&groups);
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&groups, gi, list) {
+ if (gi->chan == chan) {
+ AST_RWLIST_REMOVE_CURRENT(list);
+ ast_free(gi);
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+ AST_RWLIST_UNLOCK(&groups);
+
+ return 0;
+}
+
+int ast_app_group_list_wrlock(void)
+{
+ return AST_RWLIST_WRLOCK(&groups);
+}
+
+int ast_app_group_list_rdlock(void)
+{
+ return AST_RWLIST_RDLOCK(&groups);
+}
+
+struct ast_group_info *ast_app_group_list_head(void)
+{
+ return AST_RWLIST_FIRST(&groups);
+}
+
+int ast_app_group_list_unlock(void)
+{
+ return AST_RWLIST_UNLOCK(&groups);
+}
+
+unsigned int ast_app_separate_args(char *buf, char delim, char **array, int arraylen)
+{
+ int argc;
+ char *scan;
+ int paren = 0, quote = 0;
+
+ if (!buf || !array || !arraylen)
+ return 0;
+
+ memset(array, 0, arraylen * sizeof(*array));
+
+ scan = buf;
+
+ for (argc = 0; *scan && (argc < arraylen - 1); argc++) {
+ array[argc] = scan;
+ for (; *scan; scan++) {
+ if (*scan == '(')
+ paren++;
+ else if (*scan == ')') {
+ if (paren)
+ paren--;
+ } else if (*scan == '"' && delim != '"') {
+ quote = quote ? 0 : 1;
+ /* Remove quote character from argument */
+ memmove(scan, scan + 1, strlen(scan));
+ scan--;
+ } else if (*scan == '\\') {
+ /* Literal character, don't parse */
+ memmove(scan, scan + 1, strlen(scan));
+ } else if ((*scan == delim) && !paren && !quote) {
+ *scan++ = '\0';
+ break;
+ }
+ }
+ }
+
+ if (*scan)
+ array[argc++] = scan;
+
+ return argc;
+}
+
+static enum AST_LOCK_RESULT ast_lock_path_lockfile(const char *path)
+{
+ char *s;
+ char *fs;
+ int res;
+ int fd;
+ int lp = strlen(path);
+ time_t start;
+
+ s = alloca(lp + 10);
+ fs = alloca(lp + 20);
+
+ snprintf(fs, strlen(path) + 19, "%s/.lock-%08lx", path, ast_random());
+ fd = open(fs, O_WRONLY | O_CREAT | O_EXCL, AST_FILE_MODE);
+ if (fd < 0) {
+ ast_log(LOG_ERROR, "Unable to create lock file '%s': %s\n", path, strerror(errno));
+ return AST_LOCK_PATH_NOT_FOUND;
+ }
+ close(fd);
+
+ snprintf(s, strlen(path) + 9, "%s/.lock", path);
+ start = time(NULL);
+ while (((res = link(fs, s)) < 0) && (errno == EEXIST) && (time(NULL) - start < 5))
+ usleep(1);
+
+ unlink(fs);
+
+ if (res) {
+ ast_log(LOG_WARNING, "Failed to lock path '%s': %s\n", path, strerror(errno));
+ return AST_LOCK_TIMEOUT;
+ } else {
+ ast_debug(1, "Locked path '%s'\n", path);
+ return AST_LOCK_SUCCESS;
+ }
+}
+
+static int ast_unlock_path_lockfile(const char *path)
+{
+ char *s;
+ int res;
+
+ s = alloca(strlen(path) + 10);
+
+ snprintf(s, strlen(path) + 9, "%s/%s", path, ".lock");
+
+ if ((res = unlink(s)))
+ ast_log(LOG_ERROR, "Could not unlock path '%s': %s\n", path, strerror(errno));
+ else {
+ ast_debug(1, "Unlocked path '%s'\n", path);
+ }
+
+ return res;
+}
+
+struct path_lock {
+ AST_LIST_ENTRY(path_lock) le;
+ int fd;
+ char *path;
+};
+
+static AST_LIST_HEAD_STATIC(path_lock_list, path_lock);
+
+static void path_lock_destroy(struct path_lock *obj)
+{
+ if (obj->fd >= 0)
+ close(obj->fd);
+ if (obj->path)
+ free(obj->path);
+ free(obj);
+}
+
+static enum AST_LOCK_RESULT ast_lock_path_flock(const char *path)
+{
+ char *fs;
+ int res;
+ int fd;
+ time_t start;
+ struct path_lock *pl;
+ struct stat st, ost;
+
+ fs = alloca(strlen(path) + 20);
+
+ snprintf(fs, strlen(path) + 19, "%s/lock", path);
+ if (lstat(fs, &st) == 0) {
+ if ((st.st_mode & S_IFMT) == S_IFLNK) {
+ ast_log(LOG_WARNING, "Unable to create lock file "
+ "'%s': it's already a symbolic link\n",
+ fs);
+ return AST_LOCK_FAILURE;
+ }
+ if (st.st_nlink > 1) {
+ ast_log(LOG_WARNING, "Unable to create lock file "
+ "'%s': %u hard links exist\n",
+ fs, (unsigned int) st.st_nlink);
+ return AST_LOCK_FAILURE;
+ }
+ }
+ fd = open(fs, O_WRONLY | O_CREAT, 0600);
+ if (fd < 0) {
+ ast_log(LOG_WARNING, "Unable to create lock file '%s': %s\n",
+ fs, strerror(errno));
+ return AST_LOCK_PATH_NOT_FOUND;
+ }
+ pl = ast_calloc(1, sizeof(*pl));
+ if (!pl) {
+ /* We don't unlink the lock file here, on the possibility that
+ * someone else created it - better to leave a little mess
+ * than create a big one by destroying someone else's lock
+ * and causing something to be corrupted.
+ */
+ close(fd);
+ return AST_LOCK_FAILURE;
+ }
+ pl->fd = fd;
+ pl->path = strdup(path);
+
+ time(&start);
+ while ((
+ #ifdef SOLARIS
+ (res = fcntl(pl->fd, F_SETLK, fcntl(pl->fd,F_GETFL)|O_NONBLOCK)) < 0) &&
+ #else
+ (res = flock(pl->fd, LOCK_EX | LOCK_NB)) < 0) &&
+ #endif
+ (errno == EWOULDBLOCK) &&
+ (time(NULL) - start < 5))
+ usleep(1000);
+ if (res) {
+ ast_log(LOG_WARNING, "Failed to lock path '%s': %s\n",
+ path, strerror(errno));
+ /* No unlinking of lock done, since we tried and failed to
+ * flock() it.
+ */
+ path_lock_destroy(pl);
+ return AST_LOCK_TIMEOUT;
+ }
+
+ /* Check for the race where the file is recreated or deleted out from
+ * underneath us.
+ */
+ if (lstat(fs, &st) != 0 && fstat(pl->fd, &ost) != 0 &&
+ st.st_dev != ost.st_dev &&
+ st.st_ino != ost.st_ino) {
+ ast_log(LOG_WARNING, "Unable to create lock file '%s': "
+ "file changed underneath us\n", fs);
+ path_lock_destroy(pl);
+ return AST_LOCK_FAILURE;
+ }
+
+ /* Success: file created, flocked, and is the one we started with */
+ AST_LIST_LOCK(&path_lock_list);
+ AST_LIST_INSERT_TAIL(&path_lock_list, pl, le);
+ AST_LIST_UNLOCK(&path_lock_list);
+
+ ast_debug(1, "Locked path '%s'\n", path);
+
+ return AST_LOCK_SUCCESS;
+}
+
+static int ast_unlock_path_flock(const char *path)
+{
+ char *s;
+ struct path_lock *p;
+
+ s = alloca(strlen(path) + 20);
+
+ AST_LIST_LOCK(&path_lock_list);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&path_lock_list, p, le) {
+ if (!strcmp(p->path, path)) {
+ AST_LIST_REMOVE_CURRENT(le);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ AST_LIST_UNLOCK(&path_lock_list);
+
+ if (p) {
+ snprintf(s, strlen(path) + 19, "%s/lock", path);
+ unlink(s);
+ path_lock_destroy(p);
+ ast_log(LOG_DEBUG, "Unlocked path '%s'\n", path);
+ } else
+ ast_log(LOG_DEBUG, "Failed to unlock path '%s': "
+ "lock not found\n", path);
+
+ return 0;
+}
+
+void ast_set_lock_type(enum AST_LOCK_TYPE type)
+{
+ ast_lock_type = type;
+}
+
+enum AST_LOCK_RESULT ast_lock_path(const char *path)
+{
+ enum AST_LOCK_RESULT r = AST_LOCK_FAILURE;
+
+ switch (ast_lock_type) {
+ case AST_LOCK_TYPE_LOCKFILE:
+ r = ast_lock_path_lockfile(path);
+ break;
+ case AST_LOCK_TYPE_FLOCK:
+ r = ast_lock_path_flock(path);
+ break;
+ }
+
+ return r;
+}
+
+int ast_unlock_path(const char *path)
+{
+ int r = 0;
+
+ switch (ast_lock_type) {
+ case AST_LOCK_TYPE_LOCKFILE:
+ r = ast_unlock_path_lockfile(path);
+ break;
+ case AST_LOCK_TYPE_FLOCK:
+ r = ast_unlock_path_flock(path);
+ break;
+ }
+
+ return r;
+}
+
+int ast_record_review(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, const char *path)
+{
+ int silencethreshold = 128;
+ int maxsilence = 0;
+ int res = 0;
+ int cmd = 0;
+ int max_attempts = 3;
+ int attempts = 0;
+ int recorded = 0;
+ int message_exists = 0;
+ /* Note that urgent and private are for flagging messages as such in the future */
+
+ /* barf if no pointer passed to store duration in */
+ if (!duration) {
+ ast_log(LOG_WARNING, "Error ast_record_review called without duration pointer\n");
+ return -1;
+ }
+
+ cmd = '3'; /* Want to start by recording */
+
+ while ((cmd >= 0) && (cmd != 't')) {
+ switch (cmd) {
+ case '1':
+ if (!message_exists) {
+ /* In this case, 1 is to record a message */
+ cmd = '3';
+ break;
+ } else {
+ ast_stream_and_wait(chan, "vm-msgsaved", "");
+ cmd = 't';
+ return res;
+ }
+ case '2':
+ /* Review */
+ ast_verb(3, "Reviewing the recording\n");
+ cmd = ast_stream_and_wait(chan, recordfile, AST_DIGIT_ANY);
+ break;
+ case '3':
+ message_exists = 0;
+ /* Record */
+ if (recorded == 1)
+ ast_verb(3, "Re-recording\n");
+ else
+ ast_verb(3, "Recording\n");
+ recorded = 1;
+ cmd = ast_play_and_record(chan, playfile, recordfile, maxtime, fmt, duration, silencethreshold, maxsilence, path);
+ if (cmd == -1) {
+ /* User has hung up, no options to give */
+ return cmd;
+ }
+ if (cmd == '0') {
+ break;
+ } else if (cmd == '*') {
+ break;
+ }
+ else {
+ /* If all is well, a message exists */
+ message_exists = 1;
+ cmd = 0;
+ }
+ break;
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case '*':
+ case '#':
+ cmd = ast_play_and_wait(chan, "vm-sorry");
+ break;
+ default:
+ if (message_exists) {
+ cmd = ast_play_and_wait(chan, "vm-review");
+ }
+ else {
+ cmd = ast_play_and_wait(chan, "vm-torerecord");
+ if (!cmd)
+ cmd = ast_waitfordigit(chan, 600);
+ }
+
+ if (!cmd)
+ cmd = ast_waitfordigit(chan, 6000);
+ if (!cmd) {
+ attempts++;
+ }
+ if (attempts > max_attempts) {
+ cmd = 't';
+ }
+ }
+ }
+ if (cmd == 't')
+ cmd = 0;
+ return cmd;
+}
+
+#define RES_UPONE (1 << 16)
+#define RES_EXIT (1 << 17)
+#define RES_REPEAT (1 << 18)
+#define RES_RESTART ((1 << 19) | RES_REPEAT)
+
+static int ast_ivr_menu_run_internal(struct ast_channel *chan, struct ast_ivr_menu *menu, void *cbdata);
+
+static int ivr_dispatch(struct ast_channel *chan, struct ast_ivr_option *option, char *exten, void *cbdata)
+{
+ int res;
+ int (*ivr_func)(struct ast_channel *, void *);
+ char *c;
+ char *n;
+
+ switch (option->action) {
+ case AST_ACTION_UPONE:
+ return RES_UPONE;
+ case AST_ACTION_EXIT:
+ return RES_EXIT | (((unsigned long)(option->adata)) & 0xffff);
+ case AST_ACTION_REPEAT:
+ return RES_REPEAT | (((unsigned long)(option->adata)) & 0xffff);
+ case AST_ACTION_RESTART:
+ return RES_RESTART ;
+ case AST_ACTION_NOOP:
+ return 0;
+ case AST_ACTION_BACKGROUND:
+ res = ast_stream_and_wait(chan, (char *)option->adata, AST_DIGIT_ANY);
+ if (res < 0) {
+ ast_log(LOG_NOTICE, "Unable to find file '%s'!\n", (char *)option->adata);
+ res = 0;
+ }
+ return res;
+ case AST_ACTION_PLAYBACK:
+ res = ast_stream_and_wait(chan, (char *)option->adata, "");
+ if (res < 0) {
+ ast_log(LOG_NOTICE, "Unable to find file '%s'!\n", (char *)option->adata);
+ res = 0;
+ }
+ return res;
+ case AST_ACTION_MENU:
+ res = ast_ivr_menu_run_internal(chan, (struct ast_ivr_menu *)option->adata, cbdata);
+ /* Do not pass entry errors back up, treat as though it was an "UPONE" */
+ if (res == -2)
+ res = 0;
+ return res;
+ case AST_ACTION_WAITOPTION:
+ res = ast_waitfordigit(chan, 1000 * (chan->pbx ? chan->pbx->rtimeout : 10));
+ if (!res)
+ return 't';
+ return res;
+ case AST_ACTION_CALLBACK:
+ ivr_func = option->adata;
+ res = ivr_func(chan, cbdata);
+ return res;
+ case AST_ACTION_TRANSFER:
+ res = ast_parseable_goto(chan, option->adata);
+ return 0;
+ case AST_ACTION_PLAYLIST:
+ case AST_ACTION_BACKLIST:
+ res = 0;
+ c = ast_strdupa(option->adata);
+ while ((n = strsep(&c, ";"))) {
+ if ((res = ast_stream_and_wait(chan, n,
+ (option->action == AST_ACTION_BACKLIST) ? AST_DIGIT_ANY : "")))
+ break;
+ }
+ ast_stopstream(chan);
+ return res;
+ default:
+ ast_log(LOG_NOTICE, "Unknown dispatch function %d, ignoring!\n", option->action);
+ return 0;
+ };
+ return -1;
+}
+
+static int option_exists(struct ast_ivr_menu *menu, char *option)
+{
+ int x;
+ for (x = 0; menu->options[x].option; x++)
+ if (!strcasecmp(menu->options[x].option, option))
+ return x;
+ return -1;
+}
+
+static int option_matchmore(struct ast_ivr_menu *menu, char *option)
+{
+ int x;
+ for (x = 0; menu->options[x].option; x++)
+ if ((!strncasecmp(menu->options[x].option, option, strlen(option))) &&
+ (menu->options[x].option[strlen(option)]))
+ return x;
+ return -1;
+}
+
+static int read_newoption(struct ast_channel *chan, struct ast_ivr_menu *menu, char *exten, int maxexten)
+{
+ int res=0;
+ int ms;
+ while (option_matchmore(menu, exten)) {
+ ms = chan->pbx ? chan->pbx->dtimeout : 5000;
+ if (strlen(exten) >= maxexten - 1)
+ break;
+ res = ast_waitfordigit(chan, ms);
+ if (res < 1)
+ break;
+ exten[strlen(exten) + 1] = '\0';
+ exten[strlen(exten)] = res;
+ }
+ return res > 0 ? 0 : res;
+}
+
+static int ast_ivr_menu_run_internal(struct ast_channel *chan, struct ast_ivr_menu *menu, void *cbdata)
+{
+ /* Execute an IVR menu structure */
+ int res=0;
+ int pos = 0;
+ int retries = 0;
+ char exten[AST_MAX_EXTENSION] = "s";
+ if (option_exists(menu, "s") < 0) {
+ strcpy(exten, "g");
+ if (option_exists(menu, "g") < 0) {
+ ast_log(LOG_WARNING, "No 's' nor 'g' extension in menu '%s'!\n", menu->title);
+ return -1;
+ }
+ }
+ while (!res) {
+ while (menu->options[pos].option) {
+ if (!strcasecmp(menu->options[pos].option, exten)) {
+ res = ivr_dispatch(chan, menu->options + pos, exten, cbdata);
+ ast_debug(1, "IVR Dispatch of '%s' (pos %d) yields %d\n", exten, pos, res);
+ if (res < 0)
+ break;
+ else if (res & RES_UPONE)
+ return 0;
+ else if (res & RES_EXIT)
+ return res;
+ else if (res & RES_REPEAT) {
+ int maxretries = res & 0xffff;
+ if ((res & RES_RESTART) == RES_RESTART) {
+ retries = 0;
+ } else
+ retries++;
+ if (!maxretries)
+ maxretries = 3;
+ if ((maxretries > 0) && (retries >= maxretries)) {
+ ast_debug(1, "Max retries %d exceeded\n", maxretries);
+ return -2;
+ } else {
+ if (option_exists(menu, "g") > -1)
+ strcpy(exten, "g");
+ else if (option_exists(menu, "s") > -1)
+ strcpy(exten, "s");
+ }
+ pos = 0;
+ continue;
+ } else if (res && strchr(AST_DIGIT_ANY, res)) {
+ ast_debug(1, "Got start of extension, %c\n", res);
+ exten[1] = '\0';
+ exten[0] = res;
+ if ((res = read_newoption(chan, menu, exten, sizeof(exten))))
+ break;
+ if (option_exists(menu, exten) < 0) {
+ if (option_exists(menu, "i")) {
+ ast_debug(1, "Invalid extension entered, going to 'i'!\n");
+ strcpy(exten, "i");
+ pos = 0;
+ continue;
+ } else {
+ ast_debug(1, "Aborting on invalid entry, with no 'i' option!\n");
+ res = -2;
+ break;
+ }
+ } else {
+ ast_debug(1, "New existing extension: %s\n", exten);
+ pos = 0;
+ continue;
+ }
+ }
+ }
+ pos++;
+ }
+ ast_debug(1, "Stopping option '%s', res is %d\n", exten, res);
+ pos = 0;
+ if (!strcasecmp(exten, "s"))
+ strcpy(exten, "g");
+ else
+ break;
+ }
+ return res;
+}
+
+int ast_ivr_menu_run(struct ast_channel *chan, struct ast_ivr_menu *menu, void *cbdata)
+{
+ int res = ast_ivr_menu_run_internal(chan, menu, cbdata);
+ /* Hide internal coding */
+ return res > 0 ? 0 : res;
+}
+
+char *ast_read_textfile(const char *filename)
+{
+ int fd, count = 0, res;
+ char *output = NULL;
+ struct stat filesize;
+
+ if (stat(filename, &filesize) == -1) {
+ ast_log(LOG_WARNING, "Error can't stat %s\n", filename);
+ return NULL;
+ }
+
+ count = filesize.st_size + 1;
+
+ if ((fd = open(filename, O_RDONLY)) < 0) {
+ ast_log(LOG_WARNING, "Cannot open file '%s' for reading: %s\n", filename, strerror(errno));
+ return NULL;
+ }
+
+ if ((output = ast_malloc(count))) {
+ res = read(fd, output, count - 1);
+ if (res == count - 1) {
+ output[res] = '\0';
+ } else {
+ ast_log(LOG_WARNING, "Short read of %s (%d of %d): %s\n", filename, res, count - 1, strerror(errno));
+ ast_free(output);
+ output = NULL;
+ }
+ }
+
+ close(fd);
+
+ return output;
+}
+
+int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
+{
+ char *s, *arg;
+ int curarg, res = 0;
+ unsigned int argloc;
+
+ ast_clear_flag(flags, AST_FLAGS_ALL);
+
+ if (!optstr)
+ return 0;
+
+ s = optstr;
+ while (*s) {
+ curarg = *s++ & 0x7f; /* the array (in app.h) has 128 entries */
+ argloc = options[curarg].arg_index;
+ if (*s == '(') {
+ /* Has argument */
+ arg = ++s;
+ if ((s = strchr(s, ')'))) {
+ if (argloc)
+ args[argloc - 1] = arg;
+ *s++ = '\0';
+ } else {
+ ast_log(LOG_WARNING, "Missing closing parenthesis for argument '%c' in string '%s'\n", curarg, arg);
+ res = -1;
+ break;
+ }
+ } else if (argloc) {
+ args[argloc - 1] = "";
+ }
+ ast_set_flag(flags, options[curarg].flag);
+ }
+
+ return res;
+}
+
+/* the following function will probably only be used in app_dial, until app_dial is reorganized to
+ better handle the large number of options it provides. After it is, you need to get rid of this variant
+ -- unless, of course, someone else digs up some use for large flag fields. */
+
+int ast_app_parse_options64(const struct ast_app_option *options, struct ast_flags64 *flags, char **args, char *optstr)
+{
+ char *s, *arg;
+ int curarg, res = 0;
+ unsigned int argloc;
+
+ flags->flags = 0;
+
+ if (!optstr)
+ return 0;
+
+ s = optstr;
+ while (*s) {
+ curarg = *s++ & 0x7f; /* the array (in app.h) has 128 entries */
+ ast_set_flag64(flags, options[curarg].flag);
+ argloc = options[curarg].arg_index;
+ if (*s == '(') {
+ /* Has argument */
+ arg = ++s;
+ if ((s = strchr(s, ')'))) {
+ if (argloc)
+ args[argloc - 1] = arg;
+ *s++ = '\0';
+ } else {
+ ast_log(LOG_WARNING, "Missing closing parenthesis for argument '%c' in string '%s'\n", curarg, arg);
+ res = -1;
+ break;
+ }
+ } else if (argloc) {
+ args[argloc - 1] = NULL;
+ }
+ }
+
+ return res;
+}
+
+int ast_get_encoded_char(const char *stream, char *result, size_t *consumed)
+{
+ int i;
+ *consumed = 1;
+ *result = 0;
+ if (*stream == '\\') {
+ *consumed = 2;
+ switch (*(stream + 1)) {
+ case 'n':
+ *result = '\n';
+ break;
+ case 'r':
+ *result = '\r';
+ break;
+ case 't':
+ *result = '\t';
+ break;
+ case 'x':
+ /* Hexadecimal */
+ if (strchr("0123456789ABCDEFabcdef", *(stream + 2)) && *(stream + 2) != '\0') {
+ *consumed = 3;
+ if (*(stream + 2) <= '9')
+ *result = *(stream + 2) - '0';
+ else if (*(stream + 2) <= 'F')
+ *result = *(stream + 2) - 'A' + 10;
+ else
+ *result = *(stream + 2) - 'a' + 10;
+ } else {
+ ast_log(LOG_ERROR, "Illegal character '%c' in hexadecimal string\n", *(stream + 2));
+ return -1;
+ }
+
+ if (strchr("0123456789ABCDEFabcdef", *(stream + 3)) && *(stream + 3) != '\0') {
+ *consumed = 4;
+ *result <<= 4;
+ if (*(stream + 3) <= '9')
+ *result += *(stream + 3) - '0';
+ else if (*(stream + 3) <= 'F')
+ *result += *(stream + 3) - 'A' + 10;
+ else
+ *result += *(stream + 3) - 'a' + 10;
+ }
+ break;
+ case '0':
+ /* Octal */
+ *consumed = 2;
+ for (i = 2; ; i++) {
+ if (strchr("01234567", *(stream + i)) && *(stream + i) != '\0') {
+ (*consumed)++;
+ ast_debug(5, "result was %d, ", *result);
+ *result <<= 3;
+ *result += *(stream + i) - '0';
+ ast_debug(5, "is now %d\n", *result);
+ } else
+ break;
+ }
+ break;
+ default:
+ *result = *(stream + 1);
+ }
+ } else {
+ *result = *stream;
+ *consumed = 1;
+ }
+ return 0;
+}
+
diff --git a/trunk/main/ast_expr2.c b/trunk/main/ast_expr2.c
new file mode 100644
index 000000000..693a7896a
--- /dev/null
+++ b/trunk/main/ast_expr2.c
@@ -0,0 +1,3448 @@
+/* A Bison parser, made by GNU Bison 2.1a. */
+
+/* Skeleton parser for Yacc-like parsing with Bison,
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+/* As a special exception, when this file is copied by Bison into a
+ Bison output file, you may use that output file without restriction.
+ This special exception was added by the Free Software Foundation
+ in version 1.24 of Bison. */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+ simplifying the original so-called "semantic" parser. */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+ infringing on user name space. This should be done even for local
+ variables, as they might otherwise be expanded by user macros.
+ There are some unavoidable exceptions within include files to
+ define necessary library symbols; they are noted "INFRINGES ON
+ USER NAME SPACE" below. */
+
+/* Identify Bison output. */
+#define YYBISON 1
+
+/* Bison version. */
+#define YYBISON_VERSION "2.1a"
+
+/* Skeleton name. */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers. */
+#define YYPURE 1
+
+/* Using locations. */
+#define YYLSP_NEEDED 1
+
+/* Substitute the variable and function names. */
+#define yyparse ast_yyparse
+#define yylex ast_yylex
+#define yyerror ast_yyerror
+#define yylval ast_yylval
+#define yychar ast_yychar
+#define yydebug ast_yydebug
+#define yynerrs ast_yynerrs
+#define yylloc ast_yylloc
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ TOK_COMMA = 258,
+ TOK_COLONCOLON = 259,
+ TOK_COND = 260,
+ TOK_OR = 261,
+ TOK_AND = 262,
+ TOK_NE = 263,
+ TOK_LE = 264,
+ TOK_GE = 265,
+ TOK_LT = 266,
+ TOK_GT = 267,
+ TOK_EQ = 268,
+ TOK_MINUS = 269,
+ TOK_PLUS = 270,
+ TOK_MOD = 271,
+ TOK_DIV = 272,
+ TOK_MULT = 273,
+ TOK_COMPL = 274,
+ TOK_EQTILDE = 275,
+ TOK_COLON = 276,
+ TOK_LP = 277,
+ TOK_RP = 278,
+ TOKEN = 279
+ };
+#endif
+/* Tokens. */
+#define TOK_COMMA 258
+#define TOK_COLONCOLON 259
+#define TOK_COND 260
+#define TOK_OR 261
+#define TOK_AND 262
+#define TOK_NE 263
+#define TOK_LE 264
+#define TOK_GE 265
+#define TOK_LT 266
+#define TOK_GT 267
+#define TOK_EQ 268
+#define TOK_MINUS 269
+#define TOK_PLUS 270
+#define TOK_MOD 271
+#define TOK_DIV 272
+#define TOK_MULT 273
+#define TOK_COMPL 274
+#define TOK_EQTILDE 275
+#define TOK_COLON 276
+#define TOK_LP 277
+#define TOK_RP 278
+#define TOKEN 279
+
+
+
+
+/* Copy the first part of user declarations. */
+#line 1 "ast_expr2.y"
+
+/* Written by Pace Willisson (pace@blitz.com)
+ * and placed in the public domain.
+ *
+ * Largely rewritten by J.T. Conklin (jtc@wimsey.com)
+ *
+ * And then overhauled twice by Steve Murphy (murf@digium.com)
+ * to add double-quoted strings, allow mult. spaces, improve
+ * error messages, and then to fold in a flex scanner for the
+ * yylex operation.
+ *
+ * $FreeBSD: src/bin/expr/expr.y,v 1.16 2000/07/22 10:59:36 se Exp $
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include "asterisk.h"
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+#ifdef STANDALONE
+#ifndef __USE_ISOC99
+#define __USE_ISOC99 1
+#endif
+#endif
+
+#ifdef __USE_ISOC99
+#define FP___PRINTF "%.18Lg"
+#define FP___TYPE long double
+#else
+#define FP___PRINTF "%.16g"
+#define FP___TYPE double
+#endif
+
+#ifdef HAVE_COSL
+#define FUNC_COS cosl
+#elif defined(HAVE_COS)
+#define FUNC_COS (long double)cos
+#endif
+
+#ifdef HAVE_SINL
+#define FUNC_SIN sinl
+#elif defined(HAVE_SIN)
+#define FUNC_SIN (long double)sin
+#endif
+
+#ifdef HAVE_TANL
+#define FUNC_TAN tanl
+#elif defined(HAVE_TAN)
+#define FUNC_TAN (long double)tan
+#endif
+
+#ifdef HAVE_ACOSL
+#define FUNC_ACOS acosl
+#elif defined(HAVE_ACOS)
+#define FUNC_ACOS (long double)acos
+#endif
+
+#ifdef HAVE_ASINL
+#define FUNC_ASIN asinl
+#elif defined(HAVE_ASIN)
+#define FUNC_ASIN (long double)asin
+#endif
+
+#ifdef HAVE_ATANL
+#define FUNC_ATAN atanl
+#elif defined(HAVE_ATAN)
+#define FUNC_ATAN (long double)atan
+#endif
+
+#ifdef HAVE_ATAN2L
+#define FUNC_ATAN2 atan2l
+#elif defined(HAVE_ATAN2)
+#define FUNC_ATAN2 (long double)atan2
+#endif
+
+#ifdef HAVE_POWL
+#define FUNC_POW powl
+#elif defined(HAVE_POW)
+#define FUNC_POW (long double)pow
+#endif
+
+#ifdef HAVE_SQRTL
+#define FUNC_SQRT sqrtl
+#elif defined(HAVE_SQRT)
+#define FUNC_SQRT (long double)sqrt
+#endif
+
+#ifdef HAVE_RINTL
+#define FUNC_RINT rintl
+#elif defined(HAVE_RINT)
+#define FUNC_RINT (long double)rint
+#endif
+
+#ifdef HAVE_EXPL
+#define FUNC_EXP expl
+#elif defined(HAVE_EXP)
+#define FUNC_EXP (long double)exp
+#endif
+
+#ifdef HAVE_LOGL
+#define FUNC_LOG logl
+#elif defined(HAVE_LOG)
+#define FUNC_LOG (long double)log
+#endif
+
+#ifdef HAVE_REMINDERL
+#define FUNC_REMINDER reminderl
+#elif defined(HAVE_REMINDER)
+#define FUNC_REMINDER (long double)reminder
+#endif
+
+#ifdef HAVE_FMODL
+#define FUNC_FMOD fmodl
+#elif defined(HAVE_FMOD)
+#define FUNC_FMOD (long double)fmod
+#endif
+
+#ifdef HAVE_STRTOLD
+#define FUNC_STRTOD strtold
+#elif defined(HAVE_STRTOD)
+#define FUNC_STRTOD (long double)strtod
+#endif
+
+#ifdef HAVE_FLOORL
+#define FUNC_FLOOR floorl
+#elif defined(HAVE_FLOOR)
+#define FUNC_FLOOR (long double)floor
+#endif
+
+#ifdef HAVE_CEILL
+#define FUNC_CEIL ceill
+#elif defined(HAVE_CEIL)
+#define FUNC_CEIL (long double)ceil
+#endif
+
+#ifdef HAVE_ROUNDL
+#define FUNC_ROUND roundl
+#elif defined(HAVE_ROUND)
+#define FUNC_ROUND (long double)round
+#endif
+
+#ifdef HAVE_TRUNCL
+#define FUNC_TRUNC truncl
+#elif defined(HAVE_TRUNC)
+#define FUNC_TRUNC (long double)trunc
+#endif
+
+/*! \note
+ * Oddly enough, some platforms have some ISO C99 functions, but not others, so
+ * we define the missing functions in terms of their mathematical identities.
+ */
+#ifdef HAVE_EXP2L
+#define FUNC_EXP2 exp2l
+#elif (defined(HAVE_EXPL) && defined(HAVE_LOGL))
+#define FUNC_EXP2(x) expl((x) * logl(2.0))
+#elif (defined(HAVE_EXP) && defined(HAVE_LOG))
+#define FUNC_EXP2(x) (long double)exp((x) * log(2.0))
+#endif
+
+#ifdef HAVE_EXP10L
+#define FUNC_EXP10 exp10l
+#elif (defined(HAVE_EXPL) && defined(HAVE_LOGL))
+#define FUNC_EXP10(x) expl((x) * logl(10.0))
+#elif (defined(HAVE_EXP) && defined(HAVE_LOG))
+#define FUNC_EXP10(x) (long double)exp((x) * log(10.0))
+#endif
+
+#ifdef HAVE_LOG2L
+#define FUNC_LOG2 log2l
+#elif defined(HAVE_LOGL)
+#define FUNC_LOG2(x) (logl(x) / logl(2.0))
+#elif defined(HAVE_LOG10L)
+#define FUNC_LOG2(x) (log10l(x) / log10l(2.0))
+#elif defined(HAVE_LOG2)
+#define FUNC_LOG2 (long double)log2
+#elif defined(HAVE_LOG)
+#define FUNC_LOG2(x) ((long double)log(x) / log(2.0))
+#endif
+
+#ifdef HAVE_LOG10L
+#define FUNC_LOG10 log10l
+#elif defined(HAVE_LOGL)
+#define FUNC_LOG10(x) (logl(x) / logl(10.0))
+#elif defined(HAVE_LOG2L)
+#define FUNC_LOG10(x) (log2l(x) / log2l(10.0))
+#elif defined(HAVE_LOG10)
+#define FUNC_LOG10(x) (long double)log10(x)
+#elif defined(HAVE_LOG)
+#define FUNC_LOG10(x) ((long double)log(x) / log(10.0))
+#endif
+
+
+#include <stdlib.h>
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include <string.h>
+#include <math.h>
+#include <locale.h>
+#include <unistd.h>
+#include <ctype.h>
+#if !defined(SOLARIS) && !defined(__CYGWIN__)
+ /* #include <err.h> */
+#else
+#define quad_t int64_t
+#endif
+#include <errno.h>
+#include <regex.h>
+#include <limits.h>
+
+#include "asterisk/ast_expr.h"
+#include "asterisk/logger.h"
+#ifndef STANDALONE
+#include "asterisk/pbx.h"
+#endif
+
+#if defined(LONG_LONG_MIN) && !defined(QUAD_MIN)
+#define QUAD_MIN LONG_LONG_MIN
+#endif
+#if defined(LONG_LONG_MAX) && !defined(QUAD_MAX)
+#define QUAD_MAX LONG_LONG_MAX
+#endif
+
+# if ! defined(QUAD_MIN)
+# define QUAD_MIN (-0x7fffffffffffffffLL-1)
+# endif
+# if ! defined(QUAD_MAX)
+# define QUAD_MAX (0x7fffffffffffffffLL)
+# endif
+#define YYENABLE_NLS 0
+#define YYPARSE_PARAM parseio
+#define YYLEX_PARAM ((struct parse_io *)parseio)->scanner
+#define YYERROR_VERBOSE 1
+extern char extra_error_message[4095];
+extern int extra_error_message_supplied;
+
+enum valtype {
+ AST_EXPR_number, AST_EXPR_numeric_string, AST_EXPR_string
+} ;
+
+#ifdef STANDALONE
+void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...) __attribute__ ((format (printf,5,6)));
+#endif
+
+struct val {
+ enum valtype type;
+ union {
+ char *s;
+ FP___TYPE i; /* either long double, or just double, on a bad day */
+ } u;
+} ;
+
+enum node_type {
+ AST_EXPR_NODE_COMMA, AST_EXPR_NODE_STRING, AST_EXPR_NODE_VAL
+} ;
+
+struct expr_node
+{
+ enum node_type type;
+ struct val *val;
+ struct expr_node *left;
+ struct expr_node *right;
+};
+
+
+typedef void *yyscan_t;
+
+struct parse_io
+{
+ char *string;
+ struct val *val;
+ yyscan_t scanner;
+ struct ast_channel *chan;
+};
+
+static int chk_div __P((FP___TYPE, FP___TYPE));
+static int chk_minus __P((FP___TYPE, FP___TYPE, FP___TYPE));
+static int chk_plus __P((FP___TYPE, FP___TYPE, FP___TYPE));
+static int chk_times __P((FP___TYPE, FP___TYPE, FP___TYPE));
+static void free_value __P((struct val *));
+static int is_zero_or_null __P((struct val *));
+static int isstring __P((struct val *));
+static struct val *make_number __P((FP___TYPE));
+static struct val *make_str __P((const char *));
+static struct val *op_and __P((struct val *, struct val *));
+static struct val *op_colon __P((struct val *, struct val *));
+static struct val *op_eqtilde __P((struct val *, struct val *));
+static struct val *op_div __P((struct val *, struct val *));
+static struct val *op_eq __P((struct val *, struct val *));
+static struct val *op_ge __P((struct val *, struct val *));
+static struct val *op_gt __P((struct val *, struct val *));
+static struct val *op_le __P((struct val *, struct val *));
+static struct val *op_lt __P((struct val *, struct val *));
+static struct val *op_cond __P((struct val *, struct val *, struct val *));
+static struct val *op_minus __P((struct val *, struct val *));
+static struct val *op_negate __P((struct val *));
+static struct val *op_compl __P((struct val *));
+static struct val *op_ne __P((struct val *, struct val *));
+static struct val *op_or __P((struct val *, struct val *));
+static struct val *op_plus __P((struct val *, struct val *));
+static struct val *op_rem __P((struct val *, struct val *));
+static struct val *op_times __P((struct val *, struct val *));
+static struct val *op_func(struct val *funcname, struct expr_node *arglist, struct ast_channel *chan);
+static int to_number __P((struct val *));
+static void to_string __P((struct val *));
+static struct expr_node *alloc_expr_node(enum node_type);
+static void destroy_arglist(struct expr_node *arglist);
+static int is_really_num(char *str);
+
+/* uh, if I want to predeclare yylex with a YYLTYPE, I have to predeclare the yyltype... sigh */
+typedef struct yyltype
+{
+ int first_line;
+ int first_column;
+
+ int last_line;
+ int last_column;
+} yyltype;
+
+# define YYLTYPE yyltype
+# define YYLTYPE_IS_TRIVIAL 1
+
+/* we will get warning about no prototype for yylex! But we can't
+ define it here, we have no definition yet for YYSTYPE. */
+
+int ast_yyerror(const char *,YYLTYPE *, struct parse_io *);
+
+/* I wanted to add args to the yyerror routine, so I could print out
+ some useful info about the error. Not as easy as it looks, but it
+ is possible. */
+#define ast_yyerror(x) ast_yyerror(x,&yyloc,parseio)
+#define DESTROY(x) {if((x)->type == AST_EXPR_numeric_string || (x)->type == AST_EXPR_string) free((x)->u.s); (x)->u.s = 0; free(x);}
+
+
+/* Enabling traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+/* Enabling verbose error messages. */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+/* Enabling the token table. */
+#ifndef YYTOKEN_TABLE
+# define YYTOKEN_TABLE 0
+#endif
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+#line 341 "ast_expr2.y"
+{
+ struct val *val;
+ struct expr_node *arglist;
+}
+/* Line 198 of yacc.c. */
+#line 480 "ast_expr2.c"
+ YYSTYPE;
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED
+typedef struct YYLTYPE
+{
+ int first_line;
+ int first_column;
+ int last_line;
+ int last_column;
+} YYLTYPE;
+# define yyltype YYLTYPE /* obsolescent; will be withdrawn */
+# define YYLTYPE_IS_DECLARED 1
+# define YYLTYPE_IS_TRIVIAL 1
+#endif
+
+
+/* Copy the second part of user declarations. */
+#line 346 "ast_expr2.y"
+
+extern int ast_yylex __P((YYSTYPE *, YYLTYPE *, yyscan_t));
+
+
+/* Line 221 of yacc.c. */
+#line 508 "ast_expr2.c"
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#elif (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+typedef signed char yytype_int8;
+#else
+typedef short int yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+# define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+# define YYSIZE_T size_t
+# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# else
+# define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if YYENABLE_NLS
+# if ENABLE_NLS
+# include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+# define YY_(msgid) dgettext ("bison-runtime", msgid)
+# endif
+# endif
+# ifndef YY_
+# define YY_(msgid) msgid
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E. */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(e) ((void) (e))
+#else
+# define YYUSE(e) /* empty */
+#endif
+
+/* Identity function, used to suppress warnings about constant conditions. */
+#ifndef lint
+# define YYID(n) (n)
+#else
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static int
+YYID (int i)
+#else
+static int
+YYID (i)
+ int i;
+#endif
+{
+ return i;
+}
+#endif
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols. */
+
+# ifdef YYSTACK_USE_ALLOCA
+# if YYSTACK_USE_ALLOCA
+# ifdef __GNUC__
+# define YYSTACK_ALLOC __builtin_alloca
+# elif defined __BUILTIN_VA_ARG_INCR
+# include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+# elif defined _AIX
+# define YYSTACK_ALLOC __alloca
+# elif defined _MSC_VER
+# include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+# define alloca _alloca
+# else
+# define YYSTACK_ALLOC alloca
+# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef _STDLIB_H
+# define _STDLIB_H 1
+# endif
+# endif
+# endif
+# endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+ /* Pacify GCC's `empty if-body' warning. */
+# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0))
+# ifndef YYSTACK_ALLOC_MAXIMUM
+ /* The OS might guarantee only one guard page at the bottom of the stack,
+ and a page size can be as small as 4096 bytes. So we cannot safely
+ invoke alloca (N) if N exceeds 4096. Use a slightly smaller number
+ to allow for a few compiler-allocated temporary stack slots. */
+# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+# endif
+# else
+# define YYSTACK_ALLOC YYMALLOC
+# define YYSTACK_FREE YYFREE
+# ifndef YYSTACK_ALLOC_MAXIMUM
+# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+# endif
+# ifdef __cplusplus
+extern "C" {
+# endif
+# ifndef YYMALLOC
+# define YYMALLOC malloc
+# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# ifndef YYFREE
+# define YYFREE free
+# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# ifdef __cplusplus
+}
+# endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+ && (! defined __cplusplus \
+ || (defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL \
+ && defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member. */
+union yyalloc
+{
+ yytype_int16 yyss;
+ YYSTYPE yyvs;
+ YYLTYPE yyls;
+};
+
+/* The size of the maximum gap between one aligned stack and the next. */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+ N elements. */
+# define YYSTACK_BYTES(N) \
+ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE) + sizeof (YYLTYPE)) \
+ + 2 * YYSTACK_GAP_MAXIMUM)
+
+/* Copy COUNT objects from FROM to TO. The source and destination do
+ not overlap. */
+# ifndef YYCOPY
+# if defined __GNUC__ && 1 < __GNUC__
+# define YYCOPY(To, From, Count) \
+ __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+# else
+# define YYCOPY(To, From, Count) \
+ do \
+ { \
+ YYSIZE_T yyi; \
+ for (yyi = 0; yyi < (Count); yyi++) \
+ (To)[yyi] = (From)[yyi]; \
+ } \
+ while (YYID (0))
+# endif
+# endif
+
+/* Relocate STACK from its old location to the new one. The
+ local variables YYSIZE and YYSTACKSIZE give the old and new number of
+ elements in the stack, and YYPTR gives the new location of the
+ stack. Advance YYPTR to a properly aligned location for the next
+ stack. */
+# define YYSTACK_RELOCATE(Stack) \
+ do \
+ { \
+ YYSIZE_T yynewbytes; \
+ YYCOPY (&yyptr->Stack, Stack, yysize); \
+ Stack = &yyptr->Stack; \
+ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+ yyptr += yynewbytes / sizeof (*yyptr); \
+ } \
+ while (YYID (0))
+
+#endif
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL 11
+/* YYLAST -- Last index in YYTABLE. */
+#define YYLAST 150
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS 25
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS 4
+/* YYNRULES -- Number of rules. */
+#define YYNRULES 26
+/* YYNRULES -- Number of states. */
+#define YYNSTATES 52
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
+#define YYUNDEFTOK 2
+#define YYMAXUTOK 279
+
+#define YYTRANSLATE(YYX) \
+ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */
+static const yytype_uint8 yytranslate[] =
+{
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+ YYRHS. */
+static const yytype_uint8 yyprhs[] =
+{
+ 0, 0, 3, 5, 6, 8, 12, 17, 19, 23,
+ 27, 31, 35, 39, 43, 47, 51, 55, 59, 63,
+ 66, 69, 73, 77, 81, 85, 89
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS. */
+static const yytype_int8 yyrhs[] =
+{
+ 26, 0, -1, 28, -1, -1, 28, -1, 27, 3,
+ 28, -1, 24, 22, 27, 23, -1, 24, -1, 22,
+ 28, 23, -1, 28, 6, 28, -1, 28, 7, 28,
+ -1, 28, 13, 28, -1, 28, 12, 28, -1, 28,
+ 11, 28, -1, 28, 10, 28, -1, 28, 9, 28,
+ -1, 28, 8, 28, -1, 28, 15, 28, -1, 28,
+ 14, 28, -1, 14, 28, -1, 19, 28, -1, 28,
+ 18, 28, -1, 28, 17, 28, -1, 28, 16, 28,
+ -1, 28, 21, 28, -1, 28, 20, 28, -1, 28,
+ 5, 28, 4, 28, -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
+static const yytype_uint16 yyrline[] =
+{
+ 0, 372, 372, 380, 387, 388, 397, 403, 404, 408,
+ 412, 416, 420, 424, 428, 432, 436, 440, 444, 448,
+ 452, 456, 460, 464, 468, 472, 476
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+ First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+static const char *const yytname[] =
+{
+ "$end", "error", "$undefined", "TOK_COMMA", "TOK_COLONCOLON",
+ "TOK_COND", "TOK_OR", "TOK_AND", "TOK_NE", "TOK_LE", "TOK_GE", "TOK_LT",
+ "TOK_GT", "TOK_EQ", "TOK_MINUS", "TOK_PLUS", "TOK_MOD", "TOK_DIV",
+ "TOK_MULT", "TOK_COMPL", "TOK_EQTILDE", "TOK_COLON", "TOK_LP", "TOK_RP",
+ "TOKEN", "$accept", "start", "arglist", "expr", 0
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+ token YYLEX-NUM. */
+static const yytype_uint16 yytoknum[] =
+{
+ 0, 256, 257, 258, 259, 260, 261, 262, 263, 264,
+ 265, 266, 267, 268, 269, 270, 271, 272, 273, 274,
+ 275, 276, 277, 278, 279
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
+static const yytype_uint8 yyr1[] =
+{
+ 0, 25, 26, 26, 27, 27, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
+static const yytype_uint8 yyr2[] =
+{
+ 0, 2, 1, 0, 1, 3, 4, 1, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 2,
+ 2, 3, 3, 3, 3, 3, 5
+};
+
+/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
+ STATE-NUM when YYTABLE doesn't specify something else to do. Zero
+ means the default is an error. */
+static const yytype_uint8 yydefact[] =
+{
+ 3, 0, 0, 0, 7, 0, 2, 19, 20, 0,
+ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 8, 0,
+ 4, 0, 9, 10, 16, 15, 14, 13, 12, 11,
+ 18, 17, 23, 22, 21, 25, 24, 0, 6, 0,
+ 5, 26
+};
+
+/* YYDEFGOTO[NTERM-NUM]. */
+static const yytype_int8 yydefgoto[] =
+{
+ -1, 5, 29, 6
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ STATE-NUM. */
+#define YYPACT_NINF -18
+static const yytype_int16 yypact[] =
+{
+ 112, 112, 112, 112, -16, 5, 62, -17, -17, 24,
+ 112, -18, 112, 112, 112, 112, 112, 112, 112, 112,
+ 112, 112, 112, 112, 112, 112, 112, 112, -18, 4,
+ 62, 45, 93, 107, 123, 123, 123, 123, 123, 123,
+ 129, 129, -17, -17, -17, -18, -18, 112, -18, 112,
+ 62, 78
+};
+
+/* YYPGOTO[NTERM-NUM]. */
+static const yytype_int8 yypgoto[] =
+{
+ -18, -18, -18, -1
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If
+ positive, shift that token. If negative, reduce the rule which
+ number is the opposite. If zero, do what YYDEFACT says.
+ If YYTABLE_NINF, syntax error. */
+#define YYTABLE_NINF -1
+static const yytype_uint8 yytable[] =
+{
+ 7, 8, 9, 26, 27, 11, 10, 47, 0, 30,
+ 0, 31, 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 48, 0, 12,
+ 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
+ 23, 24, 25, 0, 26, 27, 50, 28, 51, 49,
+ 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+ 22, 23, 24, 25, 0, 26, 27, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 0, 26, 27, 13, 14, 15, 16, 17, 18,
+ 19, 20, 21, 22, 23, 24, 25, 0, 26, 27,
+ 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 0, 26, 27, 15, 16, 17, 18, 19,
+ 20, 21, 22, 23, 24, 25, 1, 26, 27, 0,
+ 0, 2, 0, 0, 3, 0, 4, 21, 22, 23,
+ 24, 25, 0, 26, 27, 23, 24, 25, 0, 26,
+ 27
+};
+
+static const yytype_int8 yycheck[] =
+{
+ 1, 2, 3, 20, 21, 0, 22, 3, -1, 10,
+ -1, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+ 21, 22, 23, 24, 25, 26, 27, 23, -1, 5,
+ 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, -1, 20, 21, 47, 23, 49, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, -1, 20, 21, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
+ 18, -1, 20, 21, 6, 7, 8, 9, 10, 11,
+ 12, 13, 14, 15, 16, 17, 18, -1, 20, 21,
+ 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, -1, 20, 21, 8, 9, 10, 11, 12,
+ 13, 14, 15, 16, 17, 18, 14, 20, 21, -1,
+ -1, 19, -1, -1, 22, -1, 24, 14, 15, 16,
+ 17, 18, -1, 20, 21, 16, 17, 18, -1, 20,
+ 21
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+ symbol of state STATE-NUM. */
+static const yytype_uint8 yystos[] =
+{
+ 0, 14, 19, 22, 24, 26, 28, 28, 28, 28,
+ 22, 0, 5, 6, 7, 8, 9, 10, 11, 12,
+ 13, 14, 15, 16, 17, 18, 20, 21, 23, 27,
+ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 3, 23, 4,
+ 28, 28
+};
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY (-2)
+#define YYEOF 0
+
+#define YYACCEPT goto yyacceptlab
+#define YYABORT goto yyabortlab
+#define YYERROR goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror. This remains here temporarily
+ to ease the transition to the new meaning of YYERROR, for GCC.
+ Once GCC version 2 has supplanted version 1, this can go. */
+
+#define YYFAIL goto yyerrlab
+
+#define YYRECOVERING() (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value) \
+do \
+ if (yychar == YYEMPTY && yylen == 1) \
+ { \
+ yychar = (Token); \
+ yylval = (Value); \
+ yytoken = YYTRANSLATE (yychar); \
+ YYPOPSTACK (1); \
+ goto yybackup; \
+ } \
+ else \
+ { \
+ yyerror (YY_("syntax error: cannot back up")); \
+ YYERROR; \
+ } \
+while (YYID (0))
+
+
+#define YYTERROR 1
+#define YYERRCODE 256
+
+
+/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
+ If N is 0, then set CURRENT to the empty location which ends
+ the previous symbol: RHS[0] (always defined). */
+
+#define YYRHSLOC(Rhs, K) ((Rhs)[K])
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N) \
+ do \
+ if (YYID (N)) \
+ { \
+ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \
+ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \
+ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \
+ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \
+ } \
+ else \
+ { \
+ (Current).first_line = (Current).last_line = \
+ YYRHSLOC (Rhs, 0).last_line; \
+ (Current).first_column = (Current).last_column = \
+ YYRHSLOC (Rhs, 0).last_column; \
+ } \
+ while (YYID (0))
+#endif
+
+
+/* YY_LOCATION_PRINT -- Print the location on the stream.
+ This macro was not mandated originally: define only if we know
+ we won't break user code: when these are the locations we know. */
+
+#ifndef YY_LOCATION_PRINT
+# if YYLTYPE_IS_TRIVIAL
+# define YY_LOCATION_PRINT(File, Loc) \
+ fprintf (File, "%d.%d-%d.%d", \
+ (Loc).first_line, (Loc).first_column, \
+ (Loc).last_line, (Loc).last_column)
+# else
+# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+# endif
+#endif
+
+
+/* YYLEX -- calling `yylex' with the right arguments. */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (&yylval, &yylloc, YYLEX_PARAM)
+#else
+# define YYLEX yylex (&yylval, &yylloc)
+#endif
+
+/* Enable debugging if requested. */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+# include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+# define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args) \
+do { \
+ if (yydebug) \
+ YYFPRINTF Args; \
+} while (YYID (0))
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \
+do { \
+ if (yydebug) \
+ { \
+ YYFPRINTF (stderr, "%s ", Title); \
+ yy_symbol_print (stderr, \
+ Type, Value, Location); \
+ YYFPRINTF (stderr, "\n"); \
+ } \
+} while (YYID (0))
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, const YYSTYPE * const yyvaluep, const YYLTYPE * const yylocationp)
+#else
+static void
+yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp)
+ FILE *yyoutput;
+ int yytype;
+ const YYSTYPE * const yyvaluep;
+ const YYLTYPE * const yylocationp;
+#endif
+{
+ if (!yyvaluep)
+ return;
+ YYUSE (yylocationp);
+# ifdef YYPRINT
+ if (yytype < YYNTOKENS)
+ YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# else
+ YYUSE (yyoutput);
+# endif
+ switch (yytype)
+ {
+ default:
+ break;
+ }
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, const YYSTYPE * const yyvaluep, const YYLTYPE * const yylocationp)
+#else
+static void
+yy_symbol_print (yyoutput, yytype, yyvaluep, yylocationp)
+ FILE *yyoutput;
+ int yytype;
+ const YYSTYPE * const yyvaluep;
+ const YYLTYPE * const yylocationp;
+#endif
+{
+ if (yytype < YYNTOKENS)
+ YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+ else
+ YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+ YY_LOCATION_PRINT (yyoutput, *yylocationp);
+ YYFPRINTF (yyoutput, ": ");
+ yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp);
+ YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included). |
+`------------------------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_stack_print (yytype_int16 *bottom, yytype_int16 *top)
+#else
+static void
+yy_stack_print (bottom, top)
+ yytype_int16 *bottom;
+ yytype_int16 *top;
+#endif
+{
+ YYFPRINTF (stderr, "Stack now");
+ for (; bottom <= top; ++bottom)
+ YYFPRINTF (stderr, " %d", *bottom);
+ YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top) \
+do { \
+ if (yydebug) \
+ yy_stack_print ((Bottom), (Top)); \
+} while (YYID (0))
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced. |
+`------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_reduce_print (YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule)
+#else
+static void
+yy_reduce_print (yyvsp, yylsp, yyrule
+ )
+ YYSTYPE *yyvsp;
+ YYLTYPE *yylsp;
+ int yyrule;
+#endif
+{
+ int yynrhs = yyr2[yyrule];
+ int yyi;
+ unsigned long int yylno = yyrline[yyrule];
+ YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+ yyrule - 1, yylno);
+ /* The symbols being reduced. */
+ for (yyi = 0; yyi < yynrhs; yyi++)
+ {
+ fprintf (stderr, " $%d = ", yyi + 1);
+ yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
+ &(yyvsp[(yyi + 1) - (yynrhs)])
+ , &(yylsp[(yyi + 1) - (yynrhs)]) );
+ fprintf (stderr, "\n");
+ }
+}
+
+# define YY_REDUCE_PRINT(Rule) \
+do { \
+ if (yydebug) \
+ yy_reduce_print (yyvsp, yylsp, Rule); \
+} while (YYID (0))
+
+/* Nonzero means print parse trace. It is left uninitialized so that
+ multiple parsers can coexist. */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks. */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+ if the built-in stack extension method is used).
+
+ Do not make this value too large; the results are undefined if
+ YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+ evaluated with infinite-precision integer arithmetic. */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+# if defined __GLIBC__ && defined _STRING_H
+# define yystrlen strlen
+# else
+/* Return the length of YYSTR. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static YYSIZE_T
+yystrlen (const char *yystr)
+#else
+static YYSIZE_T
+yystrlen (yystr)
+ const char *yystr;
+#endif
+{
+ YYSIZE_T yylen;
+ for (yylen = 0; yystr[yylen]; yylen++)
+ continue;
+ return yylen;
+}
+# endif
+# endif
+
+# ifndef yystpcpy
+# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+# define yystpcpy stpcpy
+# else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+ YYDEST. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+#else
+static char *
+yystpcpy (yydest, yysrc)
+ char *yydest;
+ const char *yysrc;
+#endif
+{
+ char *yyd = yydest;
+ const char *yys = yysrc;
+
+ while ((*yyd++ = *yys++) != '\0')
+ continue;
+
+ return yyd - 1;
+}
+# endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+ quotes and backslashes, so that it's suitable for yyerror. The
+ heuristic is that double-quoting is unnecessary unless the string
+ contains an apostrophe, a comma, or backslash (other than
+ backslash-backslash). YYSTR is taken from yytname. If YYRES is
+ null, do not copy; instead, return the length of what the result
+ would have been. */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+ if (*yystr == '"')
+ {
+ size_t yyn = 0;
+ char const *yyp = yystr;
+
+ for (;;)
+ switch (*++yyp)
+ {
+ case '\'':
+ case ',':
+ goto do_not_strip_quotes;
+
+ case '\\':
+ if (*++yyp != '\\')
+ goto do_not_strip_quotes;
+ /* Fall through. */
+ default:
+ if (yyres)
+ yyres[yyn] = *yyp;
+ yyn++;
+ break;
+
+ case '"':
+ if (yyres)
+ yyres[yyn] = '\0';
+ return yyn;
+ }
+ do_not_strip_quotes: ;
+ }
+
+ if (! yyres)
+ return yystrlen (yystr);
+
+ return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+/* Copy into YYRESULT an error message about the unexpected token
+ YYCHAR while in state YYSTATE. Return the number of bytes copied,
+ including the terminating null byte. If YYRESULT is null, do not
+ copy anything; just return the number of bytes that would be
+ copied. As a special case, return 0 if an ordinary "syntax error"
+ message will do. Return YYSIZE_MAXIMUM if overflow occurs during
+ size calculation. */
+static YYSIZE_T
+yysyntax_error (char *yyresult, int yystate, int yychar)
+{
+ int yyn = yypact[yystate];
+
+ if (! (YYPACT_NINF < yyn && yyn < YYLAST))
+ return 0;
+ else
+ {
+ int yytype = YYTRANSLATE (yychar);
+ YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]);
+ YYSIZE_T yysize = yysize0;
+ YYSIZE_T yysize1;
+ int yysize_overflow = 0;
+ enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+ int yyx;
+
+# if 0
+ /* This is so xgettext sees the translatable formats that are
+ constructed on the fly. */
+ YY_("syntax error, unexpected %s");
+ YY_("syntax error, unexpected %s, expecting %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s or %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s");
+# endif
+ char *yyfmt;
+ char const *yyf;
+ static char const yyunexpected[] = "syntax error, unexpected %s";
+ static char const yyexpecting[] = ", expecting %s";
+ static char const yyor[] = " or %s";
+ char yyformat[sizeof yyunexpected
+ + sizeof yyexpecting - 1
+ + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2)
+ * (sizeof yyor - 1))];
+ char const *yyprefix = yyexpecting;
+
+ /* Start YYX at -YYN if negative to avoid negative indexes in
+ YYCHECK. */
+ int yyxbegin = yyn < 0 ? -yyn : 0;
+
+ /* Stay within bounds of both yycheck and yytname. */
+ int yychecklim = YYLAST - yyn;
+ int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+ int yycount = 1;
+
+ yyarg[0] = yytname[yytype];
+ yyfmt = yystpcpy (yyformat, yyunexpected);
+
+ for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+ {
+ if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+ {
+ yycount = 1;
+ yysize = yysize0;
+ yyformat[sizeof yyunexpected - 1] = '\0';
+ break;
+ }
+ yyarg[yycount++] = yytname[yyx];
+ yysize1 = yysize + yytnamerr (0, yytname[yyx]);
+ yysize_overflow |= (yysize1 < yysize);
+ yysize = yysize1;
+ yyfmt = yystpcpy (yyfmt, yyprefix);
+ yyprefix = yyor;
+ }
+
+ yyf = YY_(yyformat);
+ yysize1 = yysize + yystrlen (yyf);
+ yysize_overflow |= (yysize1 < yysize);
+ yysize = yysize1;
+
+ if (yysize_overflow)
+ return YYSIZE_MAXIMUM;
+
+ if (yyresult)
+ {
+ /* Avoid sprintf, as that infringes on the user's name space.
+ Don't have undefined behavior even if the translation
+ produced a string with the wrong number of "%s"s. */
+ char *yyp = yyresult;
+ int yyi = 0;
+ while ((*yyp = *yyf) != '\0')
+ {
+ if (*yyp == '%' && yyf[1] == 's' && yyi < yycount)
+ {
+ yyp += yytnamerr (yyp, yyarg[yyi++]);
+ yyf += 2;
+ }
+ else
+ {
+ yyp++;
+ yyf++;
+ }
+ }
+ }
+ return yysize;
+ }
+}
+#endif /* YYERROR_VERBOSE */
+
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol. |
+`-----------------------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocationp)
+#else
+static void
+yydestruct (yymsg, yytype, yyvaluep, yylocationp)
+ const char *yymsg;
+ int yytype;
+ YYSTYPE *yyvaluep;
+ YYLTYPE *yylocationp;
+#endif
+{
+ YYUSE (yyvaluep);
+ YYUSE (yylocationp);
+
+ if (!yymsg)
+ yymsg = "Deleting";
+ YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+ switch (yytype)
+ {
+ case 4: /* "TOK_COLONCOLON" */
+#line 366 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1460 "ast_expr2.c"
+ break;
+ case 5: /* "TOK_COND" */
+#line 366 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1465 "ast_expr2.c"
+ break;
+ case 6: /* "TOK_OR" */
+#line 366 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1470 "ast_expr2.c"
+ break;
+ case 7: /* "TOK_AND" */
+#line 366 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1475 "ast_expr2.c"
+ break;
+ case 8: /* "TOK_NE" */
+#line 366 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1480 "ast_expr2.c"
+ break;
+ case 9: /* "TOK_LE" */
+#line 366 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1485 "ast_expr2.c"
+ break;
+ case 10: /* "TOK_GE" */
+#line 366 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1490 "ast_expr2.c"
+ break;
+ case 11: /* "TOK_LT" */
+#line 366 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1495 "ast_expr2.c"
+ break;
+ case 12: /* "TOK_GT" */
+#line 366 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1500 "ast_expr2.c"
+ break;
+ case 13: /* "TOK_EQ" */
+#line 366 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1505 "ast_expr2.c"
+ break;
+ case 14: /* "TOK_MINUS" */
+#line 366 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1510 "ast_expr2.c"
+ break;
+ case 15: /* "TOK_PLUS" */
+#line 366 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1515 "ast_expr2.c"
+ break;
+ case 16: /* "TOK_MOD" */
+#line 366 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1520 "ast_expr2.c"
+ break;
+ case 17: /* "TOK_DIV" */
+#line 366 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1525 "ast_expr2.c"
+ break;
+ case 18: /* "TOK_MULT" */
+#line 366 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1530 "ast_expr2.c"
+ break;
+ case 19: /* "TOK_COMPL" */
+#line 366 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1535 "ast_expr2.c"
+ break;
+ case 20: /* "TOK_EQTILDE" */
+#line 366 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1540 "ast_expr2.c"
+ break;
+ case 21: /* "TOK_COLON" */
+#line 366 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1545 "ast_expr2.c"
+ break;
+ case 22: /* "TOK_LP" */
+#line 366 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1550 "ast_expr2.c"
+ break;
+ case 23: /* "TOK_RP" */
+#line 366 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1555 "ast_expr2.c"
+ break;
+ case 24: /* "TOKEN" */
+#line 366 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1560 "ast_expr2.c"
+ break;
+ case 28: /* "expr" */
+#line 366 "ast_expr2.y"
+ { free_value((yyvaluep->val)); };
+#line 1565 "ast_expr2.c"
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+/* Prevent warnings from -Wmissing-prototypes. */
+
+#ifdef YYPARSE_PARAM
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void *YYPARSE_PARAM);
+#else
+int yyparse ();
+#endif
+#else /* ! YYPARSE_PARAM */
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+
+
+
+
+
+/*----------.
+| yyparse. |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void *YYPARSE_PARAM)
+#else
+int
+yyparse (YYPARSE_PARAM)
+ void *YYPARSE_PARAM;
+#endif
+#else /* ! YYPARSE_PARAM */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void)
+#else
+int
+yyparse ()
+
+#endif
+#endif
+{
+ /* The look-ahead symbol. */
+int yychar;
+
+/* The semantic value of the look-ahead symbol. */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far. */
+int yynerrs;
+/* Location data for the look-ahead symbol. */
+YYLTYPE yylloc;
+
+ int yystate;
+ int yyn;
+ int yyresult;
+ /* Number of tokens to shift before error messages enabled. */
+ int yyerrstatus;
+ /* Look-ahead token as an internal (translated) token number. */
+ int yytoken = 0;
+#if YYERROR_VERBOSE
+ /* Buffer for error messages, and its allocated size. */
+ char yymsgbuf[128];
+ char *yymsg = yymsgbuf;
+ YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+ /* Three stacks and their tools:
+ `yyss': related to states,
+ `yyvs': related to semantic values,
+ `yyls': related to locations.
+
+ Refer to the stacks thru separate pointers, to allow yyoverflow
+ to reallocate them elsewhere. */
+
+ /* The state stack. */
+ yytype_int16 yyssa[YYINITDEPTH];
+ yytype_int16 *yyss = yyssa;
+ yytype_int16 *yyssp;
+
+ /* The semantic value stack. */
+ YYSTYPE yyvsa[YYINITDEPTH];
+ YYSTYPE *yyvs = yyvsa;
+ YYSTYPE *yyvsp;
+
+ /* The location stack. */
+ YYLTYPE yylsa[YYINITDEPTH];
+ YYLTYPE *yyls = yylsa;
+ YYLTYPE *yylsp;
+ /* The locations where the error started and ended. */
+ YYLTYPE yyerror_range[2];
+
+#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N), yylsp -= (N))
+
+ YYSIZE_T yystacksize = YYINITDEPTH;
+
+ /* The variables used to return semantic value and location from the
+ action routines. */
+ YYSTYPE yyval;
+ YYLTYPE yyloc;
+
+ /* The number of symbols on the RHS of the reduced rule.
+ Keep to zero when no symbol should be popped. */
+ int yylen = 0;
+
+ YYDPRINTF ((stderr, "Starting parse\n"));
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+
+ /* Initialize stack pointers.
+ Waste one element of value and location stack
+ so that they stay on the same level as the state stack.
+ The wasted elements are never initialized. */
+
+ yyssp = yyss;
+ yyvsp = yyvs;
+ yylsp = yyls;
+#if YYLTYPE_IS_TRIVIAL
+ /* Initialize the default location before parsing starts. */
+ yylloc.first_line = yylloc.last_line = 1;
+ yylloc.first_column = yylloc.last_column = 0;
+#endif
+
+ goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate. |
+`------------------------------------------------------------*/
+ yynewstate:
+ /* In all cases, when you get here, the value and location stacks
+ have just been pushed. So pushing a state here evens the stacks. */
+ yyssp++;
+
+ yysetstate:
+ *yyssp = yystate;
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ {
+ /* Get the current used size of the three stacks, in elements. */
+ YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ {
+ /* Give user a chance to reallocate the stack. Use copies of
+ these so that the &'s don't force the real ones into
+ memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ yytype_int16 *yyss1 = yyss;
+ YYLTYPE *yyls1 = yyls;
+
+ /* Each stack pointer address is followed by the size of the
+ data in use in that stack, in bytes. This used to be a
+ conditional around just the two extra args, but that might
+ be undefined if yyoverflow is a macro. */
+ yyoverflow (YY_("memory exhausted"),
+ &yyss1, yysize * sizeof (*yyssp),
+ &yyvs1, yysize * sizeof (*yyvsp),
+ &yyls1, yysize * sizeof (*yylsp),
+ &yystacksize);
+ yyls = yyls1;
+ yyss = yyss1;
+ yyvs = yyvs1;
+ }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+ goto yyexhaustedlab;
+# else
+ /* Extend the stack our own way. */
+ if (YYMAXDEPTH <= yystacksize)
+ goto yyexhaustedlab;
+ yystacksize *= 2;
+ if (YYMAXDEPTH < yystacksize)
+ yystacksize = YYMAXDEPTH;
+
+ {
+ yytype_int16 *yyss1 = yyss;
+ union yyalloc *yyptr =
+ (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+ if (! yyptr)
+ goto yyexhaustedlab;
+ YYSTACK_RELOCATE (yyss);
+ YYSTACK_RELOCATE (yyvs);
+ YYSTACK_RELOCATE (yyls);
+# undef YYSTACK_RELOCATE
+ if (yyss1 != yyssa)
+ YYSTACK_FREE (yyss1);
+ }
+# endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + yysize - 1;
+ yyvsp = yyvs + yysize - 1;
+ yylsp = yyls + yysize - 1;
+
+ YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+ (unsigned long int) yystacksize));
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ YYABORT;
+ }
+
+ YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+ goto yybackup;
+
+/*-----------.
+| yybackup. |
+`-----------*/
+yybackup:
+
+ /* Do appropriate processing given the current state. Read a
+ look-ahead token if we need one and don't already have one. */
+
+ /* First try to decide what to do without reference to look-ahead token. */
+ yyn = yypact[yystate];
+ if (yyn == YYPACT_NINF)
+ goto yydefault;
+
+ /* Not known => get a look-ahead token if don't already have one. */
+
+ /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */
+ if (yychar == YYEMPTY)
+ {
+ YYDPRINTF ((stderr, "Reading a token: "));
+ yychar = YYLEX;
+ }
+
+ if (yychar <= YYEOF)
+ {
+ yychar = yytoken = YYEOF;
+ YYDPRINTF ((stderr, "Now at end of input.\n"));
+ }
+ else
+ {
+ yytoken = YYTRANSLATE (yychar);
+ YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+ }
+
+ /* If the proper action on seeing token YYTOKEN is to reduce or to
+ detect an error, take that action. */
+ yyn += yytoken;
+ if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+ goto yydefault;
+ yyn = yytable[yyn];
+ if (yyn <= 0)
+ {
+ if (yyn == 0 || yyn == YYTABLE_NINF)
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ /* Count tokens shifted since error; after three, turn off error
+ status. */
+ if (yyerrstatus)
+ yyerrstatus--;
+
+ /* Shift the look-ahead token. */
+ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+ /* Discard the shifted token unless it is eof. */
+ if (yychar != YYEOF)
+ yychar = YYEMPTY;
+
+ yystate = yyn;
+ *++yyvsp = yylval;
+ *++yylsp = yylloc;
+ goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state. |
+`-----------------------------------------------------------*/
+yydefault:
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+ goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction. |
+`-----------------------------*/
+yyreduce:
+ /* yyn is the number of a rule to reduce with. */
+ yylen = yyr2[yyn];
+
+ /* If YYLEN is nonzero, implement the default value of the action:
+ `$$ = $1'.
+
+ Otherwise, the following line sets YYVAL to garbage.
+ This behavior is undocumented and Bison
+ users should not rely upon it. Assigning to YYVAL
+ unconditionally makes the parser a bit smaller, and it avoids a
+ GCC warning that YYVAL may be used uninitialized. */
+ yyval = yyvsp[1-yylen];
+
+ /* Default location. */
+ YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen);
+ YY_REDUCE_PRINT (yyn);
+ switch (yyn)
+ {
+ case 2:
+#line 372 "ast_expr2.y"
+ { ((struct parse_io *)parseio)->val = (struct val *)calloc(sizeof(struct val),1);
+ ((struct parse_io *)parseio)->val->type = (yyvsp[(1) - (1)].val)->type;
+ if( (yyvsp[(1) - (1)].val)->type == AST_EXPR_number )
+ ((struct parse_io *)parseio)->val->u.i = (yyvsp[(1) - (1)].val)->u.i;
+ else
+ ((struct parse_io *)parseio)->val->u.s = (yyvsp[(1) - (1)].val)->u.s;
+ free((yyvsp[(1) - (1)].val));
+ ;}
+ break;
+
+ case 3:
+#line 380 "ast_expr2.y"
+ {/* nothing */ ((struct parse_io *)parseio)->val = (struct val *)calloc(sizeof(struct val),1);
+ ((struct parse_io *)parseio)->val->type = AST_EXPR_string;
+ ((struct parse_io *)parseio)->val->u.s = strdup("");
+ ;}
+ break;
+
+ case 4:
+#line 387 "ast_expr2.y"
+ { (yyval.arglist) = alloc_expr_node(AST_EXPR_NODE_VAL); (yyval.arglist)->val = (yyvsp[(1) - (1)].val);;}
+ break;
+
+ case 5:
+#line 388 "ast_expr2.y"
+ {struct expr_node *x = alloc_expr_node(AST_EXPR_NODE_VAL);
+ struct expr_node *t;
+ DESTROY((yyvsp[(2) - (3)].val));
+ for (t=(yyvsp[(1) - (3)].arglist);t->right;t=t->right)
+ ;
+ (yyval.arglist) = (yyvsp[(1) - (3)].arglist); t->right = x; x->val = (yyvsp[(3) - (3)].val);;}
+ break;
+
+ case 6:
+#line 397 "ast_expr2.y"
+ { (yyval.val) = op_func((yyvsp[(1) - (4)].val),(yyvsp[(3) - (4)].arglist), ((struct parse_io *)parseio)->chan);
+ DESTROY((yyvsp[(2) - (4)].val));
+ DESTROY((yyvsp[(4) - (4)].val));
+ DESTROY((yyvsp[(1) - (4)].val));
+ destroy_arglist((yyvsp[(3) - (4)].arglist));
+ ;}
+ break;
+
+ case 7:
+#line 403 "ast_expr2.y"
+ {(yyval.val) = (yyvsp[(1) - (1)].val);;}
+ break;
+
+ case 8:
+#line 404 "ast_expr2.y"
+ { (yyval.val) = (yyvsp[(2) - (3)].val);
+ (yyloc).first_column = (yylsp[(1) - (3)]).first_column; (yyloc).last_column = (yylsp[(3) - (3)]).last_column;
+ (yyloc).first_line=0; (yyloc).last_line=0;
+ DESTROY((yyvsp[(1) - (3)].val)); DESTROY((yyvsp[(3) - (3)].val)); ;}
+ break;
+
+ case 9:
+#line 408 "ast_expr2.y"
+ { (yyval.val) = op_or ((yyvsp[(1) - (3)].val), (yyvsp[(3) - (3)].val));
+ DESTROY((yyvsp[(2) - (3)].val));
+ (yyloc).first_column = (yylsp[(1) - (3)]).first_column; (yyloc).last_column = (yylsp[(3) - (3)]).last_column;
+ (yyloc).first_line=0; (yyloc).last_line=0;;}
+ break;
+
+ case 10:
+#line 412 "ast_expr2.y"
+ { (yyval.val) = op_and ((yyvsp[(1) - (3)].val), (yyvsp[(3) - (3)].val));
+ DESTROY((yyvsp[(2) - (3)].val));
+ (yyloc).first_column = (yylsp[(1) - (3)]).first_column; (yyloc).last_column = (yylsp[(3) - (3)]).last_column;
+ (yyloc).first_line=0; (yyloc).last_line=0;;}
+ break;
+
+ case 11:
+#line 416 "ast_expr2.y"
+ { (yyval.val) = op_eq ((yyvsp[(1) - (3)].val), (yyvsp[(3) - (3)].val));
+ DESTROY((yyvsp[(2) - (3)].val));
+ (yyloc).first_column = (yylsp[(1) - (3)]).first_column; (yyloc).last_column = (yylsp[(3) - (3)]).last_column;
+ (yyloc).first_line=0; (yyloc).last_line=0;;}
+ break;
+
+ case 12:
+#line 420 "ast_expr2.y"
+ { (yyval.val) = op_gt ((yyvsp[(1) - (3)].val), (yyvsp[(3) - (3)].val));
+ DESTROY((yyvsp[(2) - (3)].val));
+ (yyloc).first_column = (yylsp[(1) - (3)]).first_column; (yyloc).last_column = (yylsp[(3) - (3)]).last_column;
+ (yyloc).first_line=0; (yyloc).last_line=0;;}
+ break;
+
+ case 13:
+#line 424 "ast_expr2.y"
+ { (yyval.val) = op_lt ((yyvsp[(1) - (3)].val), (yyvsp[(3) - (3)].val));
+ DESTROY((yyvsp[(2) - (3)].val));
+ (yyloc).first_column = (yylsp[(1) - (3)]).first_column; (yyloc).last_column = (yylsp[(3) - (3)]).last_column;
+ (yyloc).first_line=0; (yyloc).last_line=0;;}
+ break;
+
+ case 14:
+#line 428 "ast_expr2.y"
+ { (yyval.val) = op_ge ((yyvsp[(1) - (3)].val), (yyvsp[(3) - (3)].val));
+ DESTROY((yyvsp[(2) - (3)].val));
+ (yyloc).first_column = (yylsp[(1) - (3)]).first_column; (yyloc).last_column = (yylsp[(3) - (3)]).last_column;
+ (yyloc).first_line=0; (yyloc).last_line=0;;}
+ break;
+
+ case 15:
+#line 432 "ast_expr2.y"
+ { (yyval.val) = op_le ((yyvsp[(1) - (3)].val), (yyvsp[(3) - (3)].val));
+ DESTROY((yyvsp[(2) - (3)].val));
+ (yyloc).first_column = (yylsp[(1) - (3)]).first_column; (yyloc).last_column = (yylsp[(3) - (3)]).last_column;
+ (yyloc).first_line=0; (yyloc).last_line=0;;}
+ break;
+
+ case 16:
+#line 436 "ast_expr2.y"
+ { (yyval.val) = op_ne ((yyvsp[(1) - (3)].val), (yyvsp[(3) - (3)].val));
+ DESTROY((yyvsp[(2) - (3)].val));
+ (yyloc).first_column = (yylsp[(1) - (3)]).first_column; (yyloc).last_column = (yylsp[(3) - (3)]).last_column;
+ (yyloc).first_line=0; (yyloc).last_line=0;;}
+ break;
+
+ case 17:
+#line 440 "ast_expr2.y"
+ { (yyval.val) = op_plus ((yyvsp[(1) - (3)].val), (yyvsp[(3) - (3)].val));
+ DESTROY((yyvsp[(2) - (3)].val));
+ (yyloc).first_column = (yylsp[(1) - (3)]).first_column; (yyloc).last_column = (yylsp[(3) - (3)]).last_column;
+ (yyloc).first_line=0; (yyloc).last_line=0;;}
+ break;
+
+ case 18:
+#line 444 "ast_expr2.y"
+ { (yyval.val) = op_minus ((yyvsp[(1) - (3)].val), (yyvsp[(3) - (3)].val));
+ DESTROY((yyvsp[(2) - (3)].val));
+ (yyloc).first_column = (yylsp[(1) - (3)]).first_column; (yyloc).last_column = (yylsp[(3) - (3)]).last_column;
+ (yyloc).first_line=0; (yyloc).last_line=0;;}
+ break;
+
+ case 19:
+#line 448 "ast_expr2.y"
+ { (yyval.val) = op_negate ((yyvsp[(2) - (2)].val));
+ DESTROY((yyvsp[(1) - (2)].val));
+ (yyloc).first_column = (yylsp[(1) - (2)]).first_column; (yyloc).last_column = (yylsp[(2) - (2)]).last_column;
+ (yyloc).first_line=0; (yyloc).last_line=0;;}
+ break;
+
+ case 20:
+#line 452 "ast_expr2.y"
+ { (yyval.val) = op_compl ((yyvsp[(2) - (2)].val));
+ DESTROY((yyvsp[(1) - (2)].val));
+ (yyloc).first_column = (yylsp[(1) - (2)]).first_column; (yyloc).last_column = (yylsp[(2) - (2)]).last_column;
+ (yyloc).first_line=0; (yyloc).last_line=0;;}
+ break;
+
+ case 21:
+#line 456 "ast_expr2.y"
+ { (yyval.val) = op_times ((yyvsp[(1) - (3)].val), (yyvsp[(3) - (3)].val));
+ DESTROY((yyvsp[(2) - (3)].val));
+ (yyloc).first_column = (yylsp[(1) - (3)]).first_column; (yyloc).last_column = (yylsp[(3) - (3)]).last_column;
+ (yyloc).first_line=0; (yyloc).last_line=0;;}
+ break;
+
+ case 22:
+#line 460 "ast_expr2.y"
+ { (yyval.val) = op_div ((yyvsp[(1) - (3)].val), (yyvsp[(3) - (3)].val));
+ DESTROY((yyvsp[(2) - (3)].val));
+ (yyloc).first_column = (yylsp[(1) - (3)]).first_column; (yyloc).last_column = (yylsp[(3) - (3)]).last_column;
+ (yyloc).first_line=0; (yyloc).last_line=0;;}
+ break;
+
+ case 23:
+#line 464 "ast_expr2.y"
+ { (yyval.val) = op_rem ((yyvsp[(1) - (3)].val), (yyvsp[(3) - (3)].val));
+ DESTROY((yyvsp[(2) - (3)].val));
+ (yyloc).first_column = (yylsp[(1) - (3)]).first_column; (yyloc).last_column = (yylsp[(3) - (3)]).last_column;
+ (yyloc).first_line=0; (yyloc).last_line=0;;}
+ break;
+
+ case 24:
+#line 468 "ast_expr2.y"
+ { (yyval.val) = op_colon ((yyvsp[(1) - (3)].val), (yyvsp[(3) - (3)].val));
+ DESTROY((yyvsp[(2) - (3)].val));
+ (yyloc).first_column = (yylsp[(1) - (3)]).first_column; (yyloc).last_column = (yylsp[(3) - (3)]).last_column;
+ (yyloc).first_line=0; (yyloc).last_line=0;;}
+ break;
+
+ case 25:
+#line 472 "ast_expr2.y"
+ { (yyval.val) = op_eqtilde ((yyvsp[(1) - (3)].val), (yyvsp[(3) - (3)].val));
+ DESTROY((yyvsp[(2) - (3)].val));
+ (yyloc).first_column = (yylsp[(1) - (3)]).first_column; (yyloc).last_column = (yylsp[(3) - (3)]).last_column;
+ (yyloc).first_line=0; (yyloc).last_line=0;;}
+ break;
+
+ case 26:
+#line 476 "ast_expr2.y"
+ { (yyval.val) = op_cond ((yyvsp[(1) - (5)].val), (yyvsp[(3) - (5)].val), (yyvsp[(5) - (5)].val));
+ DESTROY((yyvsp[(2) - (5)].val));
+ DESTROY((yyvsp[(4) - (5)].val));
+ (yyloc).first_column = (yylsp[(1) - (5)]).first_column; (yyloc).last_column = (yylsp[(3) - (5)]).last_column;
+ (yyloc).first_line=0; (yyloc).last_line=0;;}
+ break;
+
+
+/* Line 1270 of yacc.c. */
+#line 2092 "ast_expr2.c"
+ default: break;
+ }
+ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+
+ *++yyvsp = yyval;
+ *++yylsp = yyloc;
+
+ /* Now `shift' the result of the reduction. Determine what state
+ that goes to, based on the state we popped back to and the rule
+ number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+ if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTOKENS];
+
+ goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+ /* If not already recovering from an error, report this error. */
+ if (!yyerrstatus)
+ {
+ ++yynerrs;
+#if ! YYERROR_VERBOSE
+ yyerror (YY_("syntax error"));
+#else
+ {
+ YYSIZE_T yysize = yysyntax_error (0, yystate, yychar);
+ if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM)
+ {
+ YYSIZE_T yyalloc = 2 * yysize;
+ if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM))
+ yyalloc = YYSTACK_ALLOC_MAXIMUM;
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+ yymsg = (char *) YYSTACK_ALLOC (yyalloc);
+ if (yymsg)
+ yymsg_alloc = yyalloc;
+ else
+ {
+ yymsg = yymsgbuf;
+ yymsg_alloc = sizeof yymsgbuf;
+ }
+ }
+
+ if (0 < yysize && yysize <= yymsg_alloc)
+ {
+ (void) yysyntax_error (yymsg, yystate, yychar);
+ yyerror (yymsg);
+ }
+ else
+ {
+ yyerror (YY_("syntax error"));
+ if (yysize != 0)
+ goto yyexhaustedlab;
+ }
+ }
+#endif
+ }
+
+ yyerror_range[0] = yylloc;
+
+ if (yyerrstatus == 3)
+ {
+ /* If just tried and failed to reuse look-ahead token after an
+ error, discard it. */
+
+ if (yychar <= YYEOF)
+ {
+ /* Return failure if at end of input. */
+ if (yychar == YYEOF)
+ YYABORT;
+ }
+ else
+ {
+ yydestruct ("Error: discarding",
+ yytoken, &yylval, &yylloc);
+ yychar = YYEMPTY;
+ }
+ }
+
+ /* Else will try to reuse look-ahead token after shifting the error
+ token. */
+ goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR. |
+`---------------------------------------------------*/
+yyerrorlab:
+
+ /* Pacify compilers like GCC when the user code never invokes
+ YYERROR and the label yyerrorlab therefore never appears in user
+ code. */
+ if (/*CONSTCOND*/ 0)
+ goto yyerrorlab;
+
+ yyerror_range[0] = yylsp[1-yylen];
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYERROR. */
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+ yystate = *yyssp;
+ goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR. |
+`-------------------------------------------------------------*/
+yyerrlab1:
+ yyerrstatus = 3; /* Each real token shifted decrements this. */
+
+ for (;;)
+ {
+ yyn = yypact[yystate];
+ if (yyn != YYPACT_NINF)
+ {
+ yyn += YYTERROR;
+ if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+ {
+ yyn = yytable[yyn];
+ if (0 < yyn)
+ break;
+ }
+ }
+
+ /* Pop the current state because it cannot handle the error token. */
+ if (yyssp == yyss)
+ YYABORT;
+
+ yyerror_range[0] = *yylsp;
+ yydestruct ("Error: popping",
+ yystos[yystate], yyvsp, yylsp);
+ YYPOPSTACK (1);
+ yystate = *yyssp;
+ YY_STACK_PRINT (yyss, yyssp);
+ }
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ *++yyvsp = yylval;
+
+ yyerror_range[1] = yylloc;
+ /* Using YYLLOC is tempting, but would change the location of
+ the look-ahead. YYLOC is available though. */
+ YYLLOC_DEFAULT (yyloc, (yyerror_range - 1), 2);
+ *++yylsp = yyloc;
+
+ /* Shift the error token. */
+ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here. |
+`-------------------------------------*/
+yyacceptlab:
+ yyresult = 0;
+ goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here. |
+`-----------------------------------*/
+yyabortlab:
+ yyresult = 1;
+ goto yyreturn;
+
+#ifndef yyoverflow
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here. |
+`-------------------------------------------------*/
+yyexhaustedlab:
+ yyerror (YY_("memory exhausted"));
+ yyresult = 2;
+ /* Fall through. */
+#endif
+
+yyreturn:
+ if (yychar != YYEOF && yychar != YYEMPTY)
+ yydestruct ("Cleanup: discarding lookahead",
+ yytoken, &yylval, &yylloc);
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYABORT or YYACCEPT. */
+ YYPOPSTACK (yylen);
+ YY_STACK_PRINT (yyss, yyssp);
+ while (yyssp != yyss)
+ {
+ yydestruct ("Cleanup: popping",
+ yystos[*yyssp], yyvsp, yylsp);
+ YYPOPSTACK (1);
+ }
+#ifndef yyoverflow
+ if (yyss != yyssa)
+ YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+#endif
+ return yyresult;
+}
+
+
+#line 483 "ast_expr2.y"
+
+
+static struct expr_node *alloc_expr_node(enum node_type nt)
+{
+ struct expr_node *x = calloc(1,sizeof(struct expr_node));
+ if (!x) {
+ ast_log(LOG_ERROR, "Allocation for expr_node FAILED!!\n");
+ return 0;
+ }
+ x->type = nt;
+ return x;
+}
+
+
+
+static struct val *
+make_number (FP___TYPE i)
+{
+ struct val *vp;
+
+ vp = (struct val *) malloc (sizeof (*vp));
+ if (vp == NULL) {
+ ast_log(LOG_WARNING, "malloc() failed\n");
+ return(NULL);
+ }
+
+ vp->type = AST_EXPR_number;
+ vp->u.i = i;
+ return vp;
+}
+
+static struct val *
+make_str (const char *s)
+{
+ struct val *vp;
+ size_t i;
+ int isint; /* this started out being a test for an integer, but then ended up being a test for a float */
+
+ vp = (struct val *) malloc (sizeof (*vp));
+ if (vp == NULL || ((vp->u.s = strdup (s)) == NULL)) {
+ ast_log(LOG_WARNING,"malloc() failed\n");
+ return(NULL);
+ }
+
+ for (i = 0, isint = (isdigit(s[0]) || s[0] == '-' || s[0]=='.'); isint && i < strlen(s); i++)
+ {
+ if (!isdigit(s[i]) && s[i] != '.') {
+ isint = 0;
+ break;
+ }
+ }
+ if (isint)
+ vp->type = AST_EXPR_numeric_string;
+ else
+ vp->type = AST_EXPR_string;
+
+ return vp;
+}
+
+
+static void
+free_value (struct val *vp)
+{
+ if (vp==NULL) {
+ return;
+ }
+ if (vp->type == AST_EXPR_string || vp->type == AST_EXPR_numeric_string)
+ free (vp->u.s);
+ free(vp);
+}
+
+
+static int
+to_number (struct val *vp)
+{
+ FP___TYPE i;
+
+ if (vp == NULL) {
+ ast_log(LOG_WARNING,"vp==NULL in to_number()\n");
+ return(0);
+ }
+
+ if (vp->type == AST_EXPR_number)
+ return 1;
+
+ if (vp->type == AST_EXPR_string)
+ return 0;
+
+ /* vp->type == AST_EXPR_numeric_string, make it numeric */
+ errno = 0;
+ i = FUNC_STRTOD(vp->u.s, (char**)0); /* either strtod, or strtold on a good day */
+ if (errno != 0) {
+ ast_log(LOG_WARNING,"Conversion of %s to number under/overflowed!\n", vp->u.s);
+ free(vp->u.s);
+ vp->u.s = 0;
+ return(0);
+ }
+ free (vp->u.s);
+ vp->u.i = i;
+ vp->type = AST_EXPR_number;
+ return 1;
+}
+
+static void
+strip_quotes(struct val *vp)
+{
+ if (vp->type != AST_EXPR_string && vp->type != AST_EXPR_numeric_string)
+ return;
+
+ if( vp->u.s[0] == '"' && vp->u.s[strlen(vp->u.s)-1] == '"' )
+ {
+ char *f, *t;
+ f = vp->u.s;
+ t = vp->u.s;
+
+ while( *f )
+ {
+ if( *f && *f != '"' )
+ *t++ = *f++;
+ else
+ f++;
+ }
+ *t = *f;
+ }
+}
+
+static void
+to_string (struct val *vp)
+{
+ char *tmp;
+
+ if (vp->type == AST_EXPR_string || vp->type == AST_EXPR_numeric_string)
+ return;
+
+ tmp = malloc ((size_t)25);
+ if (tmp == NULL) {
+ ast_log(LOG_WARNING,"malloc() failed\n");
+ return;
+ }
+
+ sprintf(tmp, FP___PRINTF, vp->u.i);
+ vp->type = AST_EXPR_string;
+ vp->u.s = tmp;
+}
+
+
+static int
+isstring (struct val *vp)
+{
+ /* only TRUE if this string is not a valid number */
+ return (vp->type == AST_EXPR_string);
+}
+
+
+static int
+is_zero_or_null (struct val *vp)
+{
+ if (vp->type == AST_EXPR_number) {
+ return (vp->u.i == 0);
+ } else {
+ return (*vp->u.s == 0 || (to_number(vp) && vp->u.i == 0));
+ }
+ /* NOTREACHED */
+}
+
+#ifdef STANDALONE
+
+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);
+}
+
+
+int main(int argc,char **argv) {
+ char s[4096];
+ char out[4096];
+ FILE *infile;
+
+ if( !argv[1] )
+ exit(20);
+
+ if( access(argv[1],F_OK)== 0 )
+ {
+ int ret;
+
+ infile = fopen(argv[1],"r");
+ if( !infile )
+ {
+ printf("Sorry, couldn't open %s for reading!\n", argv[1]);
+ exit(10);
+ }
+ while( fgets(s,sizeof(s),infile) )
+ {
+ if( s[strlen(s)-1] == '\n' )
+ s[strlen(s)-1] = 0;
+
+ ret = ast_expr(s, out, sizeof(out),NULL);
+ printf("Expression: %s Result: [%d] '%s'\n",
+ s, ret, out);
+ }
+ fclose(infile);
+ }
+ else
+ {
+ if (ast_expr(argv[1], s, sizeof(s), NULL))
+ printf("=====%s======\n",s);
+ else
+ printf("No result\n");
+ }
+}
+
+#endif
+
+#undef ast_yyerror
+#define ast_yyerror(x) ast_yyerror(x, YYLTYPE *yylloc, struct parse_io *parseio)
+
+/* I put the ast_yyerror func in the flex input file,
+ because it refers to the buffer state. Best to
+ let it access the BUFFER stuff there and not trying
+ define all the structs, macros etc. in this file! */
+
+static void destroy_arglist(struct expr_node *arglist)
+{
+ struct expr_node *arglist_next;
+
+ while (arglist)
+ {
+ arglist_next = arglist->right;
+ if (arglist->val)
+ free_value(arglist->val);
+ arglist->val = 0;
+ arglist->right = 0;
+ free(arglist);
+ arglist = arglist_next;
+ }
+}
+
+static char *compose_func_args(struct expr_node *arglist)
+{
+ struct expr_node *t = arglist;
+ char *argbuf;
+ int total_len = 0;
+
+ while (t) {
+ if (t != arglist)
+ total_len += 1; /* for the sep */
+ if (t->val) {
+ if (t->val->type == AST_EXPR_number)
+ total_len += 25; /* worst case */
+ else
+ total_len += strlen(t->val->u.s);
+ }
+
+ t = t->right;
+ }
+ total_len++; /* for the null */
+ ast_log(LOG_NOTICE,"argbuf allocated %d bytes;\n", total_len);
+ argbuf = malloc(total_len);
+ argbuf[0] = 0;
+ t = arglist;
+ while (t) {
+ char numbuf[30];
+
+ if (t != arglist)
+ strcat(argbuf,"|");
+
+ if (t->val) {
+ if (t->val->type == AST_EXPR_number) {
+ sprintf(numbuf,FP___PRINTF,t->val->u.i);
+ strcat(argbuf,numbuf);
+ } else
+ strcat(argbuf,t->val->u.s);
+ }
+ t = t->right;
+ }
+ ast_log(LOG_NOTICE,"argbuf uses %d bytes;\n", (int) strlen(argbuf));
+ return argbuf;
+}
+
+static int is_really_num(char *str)
+{
+ if ( strspn(str,"-0123456789. ") == strlen(str))
+ return 1;
+ else
+ return 0;
+}
+
+
+static struct val *op_func(struct val *funcname, struct expr_node *arglist, struct ast_channel *chan)
+{
+ if (strspn(funcname->u.s,"ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789") == strlen(funcname->u.s))
+ {
+ struct val *result;
+ if (0) {
+#ifdef FUNC_COS
+ } else if (strcmp(funcname->u.s,"COS") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_COS(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_SIN
+ } else if (strcmp(funcname->u.s,"SIN") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_SIN(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_TAN
+ } else if (strcmp(funcname->u.s,"TAN") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_TAN(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_ACOS
+ } else if (strcmp(funcname->u.s,"ACOS") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_ACOS(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_ASIN
+ } else if (strcmp(funcname->u.s,"ASIN") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_ASIN(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_ATAN
+ } else if (strcmp(funcname->u.s,"ATAN") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_ATAN(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_ATAN2
+ } else if (strcmp(funcname->u.s,"ATAN2") == 0) {
+ if (arglist && arglist->right && !arglist->right->right && arglist->val && arglist->right->val){
+ to_number(arglist->val);
+ to_number(arglist->right->val);
+ result = make_number(FUNC_ATAN2(arglist->val->u.i, arglist->right->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_POW
+ } else if (strcmp(funcname->u.s,"POW") == 0) {
+ if (arglist && arglist->right && !arglist->right->right && arglist->val && arglist->right->val){
+ to_number(arglist->val);
+ to_number(arglist->right->val);
+ result = make_number(FUNC_POW(arglist->val->u.i, arglist->right->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_SQRT
+ } else if (strcmp(funcname->u.s,"SQRT") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_SQRT(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_FLOOR
+ } else if (strcmp(funcname->u.s,"FLOOR") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_FLOOR(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_CEIL
+ } else if (strcmp(funcname->u.s,"CEIL") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_CEIL(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_ROUND
+ } else if (strcmp(funcname->u.s,"ROUND") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_ROUND(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif /* defined(FUNC_ROUND) */
+#ifdef FUNC_RINT
+ } else if (strcmp(funcname->u.s,"RINT") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_RINT(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_TRUNC
+ } else if (strcmp(funcname->u.s,"TRUNC") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_TRUNC(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif /* defined(FUNC_TRUNC) */
+#ifdef FUNC_EXP
+ } else if (strcmp(funcname->u.s,"EXP") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_EXP(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_EXP2
+ } else if (strcmp(funcname->u.s,"EXP2") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_EXP2(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_EXP10
+ } else if (strcmp(funcname->u.s,"EXP10") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_EXP10(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_LOG
+ } else if (strcmp(funcname->u.s,"LOG") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_LOG(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_LOG2
+ } else if (strcmp(funcname->u.s,"LOG2") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_LOG2(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_LOG10
+ } else if (strcmp(funcname->u.s,"LOG10") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_LOG10(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_REMAINDER
+ } else if (strcmp(funcname->u.s,"REMAINDER") == 0) {
+ if (arglist && arglist->right && !arglist->right->right && arglist->val && arglist->right->val){
+ to_number(arglist->val);
+ to_number(arglist->right->val);
+ result = make_number(FUNC_REMAINDER(arglist->val->u.i, arglist->right->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+ } else {
+ /* is this a custom function we should execute and collect the results of? */
+#ifndef STANDALONE
+ struct ast_custom_function *f = ast_custom_function_find(funcname->u.s);
+ if (!chan)
+ ast_log(LOG_WARNING,"Hey! chan is NULL.\n");
+ if (!f)
+ ast_log(LOG_WARNING,"Hey! could not find func %s.\n", funcname->u.s);
+
+ if (f && chan) {
+ if (f->read) {
+ char workspace[512];
+ char *argbuf = compose_func_args(arglist);
+ f->read(chan, funcname->u.s, argbuf, workspace, sizeof(workspace));
+ free(argbuf);
+ if (is_really_num(workspace))
+ return make_number(FUNC_STRTOD(workspace,(char **)NULL));
+ else
+ return make_str(workspace);
+ } else {
+ ast_log(LOG_ERROR,"Error! Function '%s' cannot be read!\n", funcname->u.s);
+ return (make_number ((FP___TYPE)0.0));
+ }
+
+ } else {
+ ast_log(LOG_ERROR,"Error! '%s' doesn't appear to be an available function!", funcname->u.s);
+ return (make_number ((FP___TYPE)0.0));
+ }
+#else
+ ast_log(LOG_ERROR,"Error! '%s' is not available in the standalone version!", funcname->u.s);
+ return (make_number ((FP___TYPE)0.0));
+#endif
+ }
+ }
+ else
+ {
+ ast_log(LOG_ERROR,"Error! '%s' is not possibly a function name!", funcname->u.s);
+ return (make_number ((FP___TYPE)0.0));
+ }
+ return (make_number ((FP___TYPE)0.0));
+}
+
+
+static struct val *
+op_or (struct val *a, struct val *b)
+{
+ if (is_zero_or_null (a)) {
+ free_value (a);
+ return (b);
+ } else {
+ free_value (b);
+ return (a);
+ }
+}
+
+static struct val *
+op_and (struct val *a, struct val *b)
+{
+ if (is_zero_or_null (a) || is_zero_or_null (b)) {
+ free_value (a);
+ free_value (b);
+ return (make_number ((FP___TYPE)0.0));
+ } else {
+ free_value (b);
+ return (a);
+ }
+}
+
+static struct val *
+op_eq (struct val *a, struct val *b)
+{
+ struct val *r;
+
+ if (isstring (a) || isstring (b)) {
+ to_string (a);
+ to_string (b);
+ r = make_number ((FP___TYPE)(strcoll (a->u.s, b->u.s) == 0));
+ } else {
+#ifdef DEBUG_FOR_CONVERSIONS
+ char buffer[2000];
+ sprintf(buffer,"Converting '%s' and '%s' ", a->u.s, b->u.s);
+#endif
+ (void)to_number(a);
+ (void)to_number(b);
+#ifdef DEBUG_FOR_CONVERSIONS
+ ast_log(LOG_WARNING,"%s to '%lld' and '%lld'\n", buffer, a->u.i, b->u.i);
+#endif
+ r = make_number ((FP___TYPE)(a->u.i == b->u.i));
+ }
+
+ free_value (a);
+ free_value (b);
+ return r;
+}
+
+static struct val *
+op_gt (struct val *a, struct val *b)
+{
+ struct val *r;
+
+ if (isstring (a) || isstring (b)) {
+ to_string (a);
+ to_string (b);
+ r = make_number ((FP___TYPE)(strcoll (a->u.s, b->u.s) > 0));
+ } else {
+ (void)to_number(a);
+ (void)to_number(b);
+ r = make_number ((FP___TYPE)(a->u.i > b->u.i));
+ }
+
+ free_value (a);
+ free_value (b);
+ return r;
+}
+
+static struct val *
+op_lt (struct val *a, struct val *b)
+{
+ struct val *r;
+
+ if (isstring (a) || isstring (b)) {
+ to_string (a);
+ to_string (b);
+ r = make_number ((FP___TYPE)(strcoll (a->u.s, b->u.s) < 0));
+ } else {
+ (void)to_number(a);
+ (void)to_number(b);
+ r = make_number ((FP___TYPE)(a->u.i < b->u.i));
+ }
+
+ free_value (a);
+ free_value (b);
+ return r;
+}
+
+static struct val *
+op_ge (struct val *a, struct val *b)
+{
+ struct val *r;
+
+ if (isstring (a) || isstring (b)) {
+ to_string (a);
+ to_string (b);
+ r = make_number ((FP___TYPE)(strcoll (a->u.s, b->u.s) >= 0));
+ } else {
+ (void)to_number(a);
+ (void)to_number(b);
+ r = make_number ((FP___TYPE)(a->u.i >= b->u.i));
+ }
+
+ free_value (a);
+ free_value (b);
+ return r;
+}
+
+static struct val *
+op_le (struct val *a, struct val *b)
+{
+ struct val *r;
+
+ if (isstring (a) || isstring (b)) {
+ to_string (a);
+ to_string (b);
+ r = make_number ((FP___TYPE)(strcoll (a->u.s, b->u.s) <= 0));
+ } else {
+ (void)to_number(a);
+ (void)to_number(b);
+ r = make_number ((FP___TYPE)(a->u.i <= b->u.i));
+ }
+
+ free_value (a);
+ free_value (b);
+ return r;
+}
+
+static struct val *
+op_cond (struct val *a, struct val *b, struct val *c)
+{
+ struct val *r;
+
+ if( isstring(a) )
+ {
+ if( strlen(a->u.s) && strcmp(a->u.s, "\"\"") != 0 && strcmp(a->u.s,"0") != 0 )
+ {
+ free_value(a);
+ free_value(c);
+ r = b;
+ }
+ else
+ {
+ free_value(a);
+ free_value(b);
+ r = c;
+ }
+ }
+ else
+ {
+ (void)to_number(a);
+ if( a->u.i )
+ {
+ free_value(a);
+ free_value(c);
+ r = b;
+ }
+ else
+ {
+ free_value(a);
+ free_value(b);
+ r = c;
+ }
+ }
+ return r;
+}
+
+static struct val *
+op_ne (struct val *a, struct val *b)
+{
+ struct val *r;
+
+ if (isstring (a) || isstring (b)) {
+ to_string (a);
+ to_string (b);
+ r = make_number ((FP___TYPE)(strcoll (a->u.s, b->u.s) != 0));
+ } else {
+ (void)to_number(a);
+ (void)to_number(b);
+ r = make_number ((FP___TYPE)(a->u.i != b->u.i));
+ }
+
+ free_value (a);
+ free_value (b);
+ return r;
+}
+
+static int
+chk_plus (FP___TYPE a, FP___TYPE b, FP___TYPE r)
+{
+ /* sum of two positive numbers must be positive */
+ if (a > 0 && b > 0 && r <= 0)
+ return 1;
+ /* sum of two negative numbers must be negative */
+ if (a < 0 && b < 0 && r >= 0)
+ return 1;
+ /* all other cases are OK */
+ return 0;
+}
+
+static struct val *
+op_plus (struct val *a, struct val *b)
+{
+ struct val *r;
+
+ if (!to_number (a)) {
+ if( !extra_error_message_supplied )
+ ast_log(LOG_WARNING,"non-numeric argument\n");
+ if (!to_number (b)) {
+ free_value(a);
+ free_value(b);
+ return make_number(0);
+ } else {
+ free_value(a);
+ return (b);
+ }
+ } else if (!to_number(b)) {
+ free_value(b);
+ return (a);
+ }
+
+ r = make_number (a->u.i + b->u.i);
+ if (chk_plus (a->u.i, b->u.i, r->u.i)) {
+ ast_log(LOG_WARNING,"overflow\n");
+ }
+ free_value (a);
+ free_value (b);
+ return r;
+}
+
+static int
+chk_minus (FP___TYPE a, FP___TYPE b, FP___TYPE r)
+{
+ /* special case subtraction of QUAD_MIN */
+ if (b == QUAD_MIN) {
+ if (a >= 0)
+ return 1;
+ else
+ return 0;
+ }
+ /* this is allowed for b != QUAD_MIN */
+ return chk_plus (a, -b, r);
+}
+
+static struct val *
+op_minus (struct val *a, struct val *b)
+{
+ struct val *r;
+
+ if (!to_number (a)) {
+ if( !extra_error_message_supplied )
+ ast_log(LOG_WARNING, "non-numeric argument\n");
+ if (!to_number (b)) {
+ free_value(a);
+ free_value(b);
+ return make_number(0);
+ } else {
+ r = make_number(0 - b->u.i);
+ free_value(a);
+ free_value(b);
+ return (r);
+ }
+ } else if (!to_number(b)) {
+ if( !extra_error_message_supplied )
+ ast_log(LOG_WARNING, "non-numeric argument\n");
+ free_value(b);
+ return (a);
+ }
+
+ r = make_number (a->u.i - b->u.i);
+ if (chk_minus (a->u.i, b->u.i, r->u.i)) {
+ ast_log(LOG_WARNING, "overflow\n");
+ }
+ free_value (a);
+ free_value (b);
+ return r;
+}
+
+static struct val *
+op_negate (struct val *a)
+{
+ struct val *r;
+
+ if (!to_number (a) ) {
+ free_value(a);
+ if( !extra_error_message_supplied )
+ ast_log(LOG_WARNING, "non-numeric argument\n");
+ return make_number(0);
+ }
+
+ r = make_number (- a->u.i);
+ if (chk_minus (0, a->u.i, r->u.i)) {
+ ast_log(LOG_WARNING, "overflow\n");
+ }
+ free_value (a);
+ return r;
+}
+
+static struct val *
+op_compl (struct val *a)
+{
+ int v1 = 1;
+ struct val *r;
+
+ if( !a )
+ {
+ v1 = 0;
+ }
+ else
+ {
+ switch( a->type )
+ {
+ case AST_EXPR_number:
+ if( a->u.i == 0 )
+ v1 = 0;
+ break;
+
+ case AST_EXPR_string:
+ if( a->u.s == 0 )
+ v1 = 0;
+ else
+ {
+ if( a->u.s[0] == 0 )
+ v1 = 0;
+ else if (strlen(a->u.s) == 1 && a->u.s[0] == '0' )
+ v1 = 0;
+ }
+ break;
+
+ case AST_EXPR_numeric_string:
+ if( a->u.s == 0 )
+ v1 = 0;
+ else
+ {
+ if( a->u.s[0] == 0 )
+ v1 = 0;
+ else if (strlen(a->u.s) == 1 && a->u.s[0] == '0' )
+ v1 = 0;
+ }
+ break;
+ }
+ }
+
+ r = make_number (!v1);
+ free_value (a);
+ return r;
+}
+
+static int
+chk_times (FP___TYPE a, FP___TYPE b, FP___TYPE r)
+{
+ /* special case: first operand is 0, no overflow possible */
+ if (a == 0)
+ return 0;
+ /* cerify that result of division matches second operand */
+ if (r / a != b)
+ return 1;
+ return 0;
+}
+
+static struct val *
+op_times (struct val *a, struct val *b)
+{
+ struct val *r;
+
+ if (!to_number (a) || !to_number (b)) {
+ free_value(a);
+ free_value(b);
+ if( !extra_error_message_supplied )
+ ast_log(LOG_WARNING, "non-numeric argument\n");
+ return(make_number(0));
+ }
+
+ r = make_number (a->u.i * b->u.i);
+ if (chk_times (a->u.i, b->u.i, r->u.i)) {
+ ast_log(LOG_WARNING, "overflow\n");
+ }
+ free_value (a);
+ free_value (b);
+ return (r);
+}
+
+static int
+chk_div (FP___TYPE a, FP___TYPE b)
+{
+ /* div by zero has been taken care of before */
+ /* only QUAD_MIN / -1 causes overflow */
+ if (a == QUAD_MIN && b == -1)
+ return 1;
+ /* everything else is OK */
+ return 0;
+}
+
+static struct val *
+op_div (struct val *a, struct val *b)
+{
+ struct val *r;
+
+ if (!to_number (a)) {
+ free_value(a);
+ free_value(b);
+ if( !extra_error_message_supplied )
+ ast_log(LOG_WARNING, "non-numeric argument\n");
+ return make_number(0);
+ } else if (!to_number (b)) {
+ free_value(a);
+ free_value(b);
+ if( !extra_error_message_supplied )
+ ast_log(LOG_WARNING, "non-numeric argument\n");
+ return make_number(INT_MAX);
+ }
+
+ if (b->u.i == 0) {
+ ast_log(LOG_WARNING, "division by zero\n");
+ free_value(a);
+ free_value(b);
+ return make_number(INT_MAX);
+ }
+
+ r = make_number (a->u.i / b->u.i);
+ if (chk_div (a->u.i, b->u.i)) {
+ ast_log(LOG_WARNING, "overflow\n");
+ }
+ free_value (a);
+ free_value (b);
+ return r;
+}
+
+static struct val *
+op_rem (struct val *a, struct val *b)
+{
+ struct val *r;
+
+ if (!to_number (a) || !to_number (b)) {
+ if( !extra_error_message_supplied )
+ ast_log(LOG_WARNING, "non-numeric argument\n");
+ free_value(a);
+ free_value(b);
+ return make_number(0);
+ }
+
+ if (b->u.i == 0) {
+ ast_log(LOG_WARNING, "div by zero\n");
+ free_value(a);
+ return(b);
+ }
+
+ r = make_number (FUNC_FMOD(a->u.i, b->u.i)); /* either fmod or fmodl if FP___TYPE is available */
+ /* chk_rem necessary ??? */
+ free_value (a);
+ free_value (b);
+ return r;
+}
+
+
+static struct val *
+op_colon (struct val *a, struct val *b)
+{
+ regex_t rp;
+ regmatch_t rm[2];
+ char errbuf[256];
+ int eval;
+ struct val *v;
+
+ /* coerce to both arguments to strings */
+ to_string(a);
+ to_string(b);
+ /* strip double quotes from both -- they'll screw up the pattern, and the search string starting at ^ */
+ strip_quotes(a);
+ strip_quotes(b);
+ /* compile regular expression */
+ if ((eval = regcomp (&rp, b->u.s, REG_EXTENDED)) != 0) {
+ regerror (eval, &rp, errbuf, sizeof(errbuf));
+ ast_log(LOG_WARNING,"regcomp() error : %s",errbuf);
+ free_value(a);
+ free_value(b);
+ return make_str("");
+ }
+
+ /* compare string against pattern */
+ /* remember that patterns are anchored to the beginning of the line */
+ if (regexec(&rp, a->u.s, (size_t)2, rm, 0) == 0 && rm[0].rm_so == 0) {
+ if (rm[1].rm_so >= 0) {
+ *(a->u.s + rm[1].rm_eo) = '\0';
+ v = make_str (a->u.s + rm[1].rm_so);
+
+ } else {
+ v = make_number ((FP___TYPE)(rm[0].rm_eo - rm[0].rm_so));
+ }
+ } else {
+ if (rp.re_nsub == 0) {
+ v = make_number ((FP___TYPE)0);
+ } else {
+ v = make_str ("");
+ }
+ }
+
+ /* free arguments and pattern buffer */
+ free_value (a);
+ free_value (b);
+ regfree (&rp);
+
+ return v;
+}
+
+
+static struct val *
+op_eqtilde (struct val *a, struct val *b)
+{
+ regex_t rp;
+ regmatch_t rm[2];
+ char errbuf[256];
+ int eval;
+ struct val *v;
+
+ /* coerce to both arguments to strings */
+ to_string(a);
+ to_string(b);
+ /* strip double quotes from both -- they'll screw up the pattern, and the search string starting at ^ */
+ strip_quotes(a);
+ strip_quotes(b);
+ /* compile regular expression */
+ if ((eval = regcomp (&rp, b->u.s, REG_EXTENDED)) != 0) {
+ regerror (eval, &rp, errbuf, sizeof(errbuf));
+ ast_log(LOG_WARNING,"regcomp() error : %s",errbuf);
+ free_value(a);
+ free_value(b);
+ return make_str("");
+ }
+
+ /* compare string against pattern */
+ /* remember that patterns are anchored to the beginning of the line */
+ if (regexec(&rp, a->u.s, (size_t)2, rm, 0) == 0 ) {
+ if (rm[1].rm_so >= 0) {
+ *(a->u.s + rm[1].rm_eo) = '\0';
+ v = make_str (a->u.s + rm[1].rm_so);
+
+ } else {
+ v = make_number ((FP___TYPE)(rm[0].rm_eo - rm[0].rm_so));
+ }
+ } else {
+ if (rp.re_nsub == 0) {
+ v = make_number ((FP___TYPE)0.0);
+ } else {
+ v = make_str ("");
+ }
+ }
+
+ /* free arguments and pattern buffer */
+ free_value (a);
+ free_value (b);
+ regfree (&rp);
+
+ return v;
+}
+
diff --git a/trunk/main/ast_expr2.fl b/trunk/main/ast_expr2.fl
new file mode 100644
index 000000000..723eebf5a
--- /dev/null
+++ b/trunk/main/ast_expr2.fl
@@ -0,0 +1,444 @@
+%{
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006, 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.
+ */
+
+/*! \file
+ *
+ * \brief Dialplan Expression Lexical Scanner
+ */
+
+#include "asterisk.h"
+
+#include <sys/types.h>
+#include <stdio.h>
+
+#ifndef STANDALONE
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+#else
+#ifndef __USE_ISOC99
+#define __USE_ISOC99 1
+#endif
+#endif
+
+#ifdef __USE_ISOC99
+#define FP___PRINTF "%.18Lg"
+#define FP___FMOD fmodl
+#define FP___STRTOD strtold
+#define FP___TYPE long double
+#else
+#define FP___PRINTF "%.16g"
+#define FP___FMOD fmod
+#define FP___STRTOD strtod
+#define FP___TYPE double
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <locale.h>
+#include <ctype.h>
+#if !defined(SOLARIS) && !defined(__CYGWIN__)
+/* #include <err.h> */
+#else
+#define quad_t int64_t
+#endif
+#include <errno.h>
+#include <regex.h>
+#include <limits.h>
+
+#include "asterisk/ast_expr.h"
+#include "asterisk/logger.h"
+#ifndef STANDALONE
+#include "asterisk/strings.h"
+#include "asterisk/channel.h"
+#endif
+
+enum valtype {
+ AST_EXPR_number, AST_EXPR_numeric_string, AST_EXPR_string
+} ;
+
+struct val {
+ enum valtype type;
+ union {
+ char *s;
+ FP___TYPE i; /* long double or just double if it's a bad day */
+ } u;
+} ;
+
+#include "ast_expr2.h" /* the o/p of the bison on ast_expr2.y */
+
+#define SET_COLUMNS do { \
+ yylloc_param->first_column = (int)(yyg->yytext_r - YY_CURRENT_BUFFER_LVALUE->yy_ch_buf); \
+ yylloc_param->last_column += yyleng - 1; \
+ yylloc_param->first_line = yylloc_param->last_line = 1; \
+ } while (0)
+
+#define SET_STRING do { \
+ yylval_param->val = calloc(1, sizeof(struct val)); \
+ yylval_param->val->type = AST_EXPR_string; \
+ yylval_param->val->u.s = strdup(yytext); \
+ } while (0)
+
+#define SET_NUMERIC_STRING do { \
+ yylval_param->val = calloc(1, sizeof(struct val)); \
+ yylval_param->val->type = AST_EXPR_numeric_string; \
+ yylval_param->val->u.s = strdup(yytext); \
+ } while (0)
+
+struct parse_io
+{
+ char *string;
+ struct val *val;
+ yyscan_t scanner;
+ struct ast_channel *chan;
+};
+
+void ast_yyset_column(int column_no, yyscan_t yyscanner);
+int ast_yyget_column(yyscan_t yyscanner);
+static int curlycount = 0;
+static char *expr2_token_subst(const char *mess);
+%}
+
+%option prefix="ast_yy"
+%option batch
+%option 8bit
+%option outfile="ast_expr2f.c"
+%option reentrant
+%option bison-bridge
+%option bison-locations
+%option noyywrap
+%option noyyfree
+%x var trail
+
+%%
+
+\| { SET_COLUMNS; SET_STRING; return TOK_OR;}
+\& { SET_COLUMNS; SET_STRING; return TOK_AND;}
+\= { SET_COLUMNS; SET_STRING; return TOK_EQ;}
+\|\| { SET_COLUMNS; SET_STRING; return TOK_OR;}
+\&\& { SET_COLUMNS; SET_STRING; return TOK_AND;}
+\=\= { SET_COLUMNS; SET_STRING; return TOK_EQ;}
+\=~ { SET_COLUMNS; SET_STRING; return TOK_EQTILDE;}
+\> { SET_COLUMNS; SET_STRING; return TOK_GT;}
+\< { SET_COLUMNS; SET_STRING; return TOK_LT;}
+\>\= { SET_COLUMNS; SET_STRING; return TOK_GE;}
+\<\= { SET_COLUMNS; SET_STRING; return TOK_LE;}
+\!\= { SET_COLUMNS; SET_STRING; return TOK_NE;}
+\+ { SET_COLUMNS; SET_STRING; return TOK_PLUS;}
+\, { SET_COLUMNS; SET_STRING; return TOK_COMMA;}
+\- { SET_COLUMNS; SET_STRING; return TOK_MINUS;}
+\* { SET_COLUMNS; SET_STRING; return TOK_MULT;}
+\/ { SET_COLUMNS; SET_STRING; return TOK_DIV;}
+\% { SET_COLUMNS; SET_STRING; return TOK_MOD;}
+\? { SET_COLUMNS; SET_STRING; return TOK_COND;}
+\! { SET_COLUMNS; SET_STRING; return TOK_COMPL;}
+\: { SET_COLUMNS; SET_STRING; return TOK_COLON;}
+\:\: { SET_COLUMNS; SET_STRING; return TOK_COLONCOLON;}
+\( { SET_COLUMNS; SET_STRING; return TOK_LP;}
+\) { SET_COLUMNS; SET_STRING; return TOK_RP;}
+\$\{ {
+ /* gather the contents of ${} expressions, with trailing stuff,
+ * into a single TOKEN.
+ * They are much more complex now than they used to be
+ */
+ curlycount = 0;
+ BEGIN(var);
+ yymore();
+ }
+
+[ \t\r] {}
+\"[^"]*\" {SET_COLUMNS; SET_STRING; return TOKEN;}
+
+[\n] {/* what to do with eol */}
+[0-9]+(\.[0-9]+)? {
+ SET_COLUMNS;
+ /* the original behavior of the expression parser was
+ * to bring in numbers as a numeric string
+ */
+ SET_NUMERIC_STRING;
+ return TOKEN;
+ }
+
+([a-zA-Z0-9\.';\\_^$#@]|[\x80-\xff])+ {
+ SET_COLUMNS;
+ SET_STRING;
+ return TOKEN;
+ }
+
+
+<var>[^{}]*\} {
+ curlycount--;
+ if (curlycount < 0) {
+ BEGIN(trail);
+ yymore();
+ } else {
+ yymore();
+ }
+ }
+
+<var>[^{}]*\{ {
+ curlycount++;
+ yymore();
+ }
+
+
+<trail>[^-\t\r \n$():?%/+=*<>!|&]* {
+ BEGIN(0);
+ SET_COLUMNS;
+ SET_STRING;
+ return TOKEN;
+ }
+
+<trail>[-\t\r \n$():?%/+=*<>!|&] {
+ char c = yytext[yyleng-1];
+ BEGIN(0);
+ unput(c);
+ SET_COLUMNS;
+ SET_STRING;
+ return TOKEN;
+ }
+
+<trail>\$\{ {
+ curlycount = 0;
+ BEGIN(var);
+ yymore();
+ }
+
+<trail><<EOF>> {
+ BEGIN(0);
+ SET_COLUMNS;
+ SET_STRING;
+ return TOKEN;
+ /*actually, if an expr is only a variable ref, this could happen a LOT */
+ }
+
+%%
+
+/* I'm putting the interface routine to the whole parse here in the flexer input file
+ mainly because of all the flexer initialization that has to be done. Shouldn't matter
+ where it is, as long as it's somewhere. I didn't want to define a prototype for the
+ ast_yy_scan_string in the .y file, because then, I'd have to define YY_BUFFER_STATE there...
+ UGH! that would be inappropriate. */
+
+int ast_yyparse(void *); /* need to/should define this prototype for the call to yyparse */
+int ast_yyerror(const char *, YYLTYPE *, struct parse_io *); /* likewise */
+
+void ast_yyfree(void *ptr, yyscan_t yyscanner)
+{
+ if (ptr) /* the normal generated yyfree func just frees its first arg;
+ this get complaints on some systems, as sometimes this
+ arg is a nil ptr! It's usually not fatal, but is irritating! */
+ free( (char *) ptr );
+}
+
+int ast_expr(char *expr, char *buf, int length, struct ast_channel *chan)
+{
+ struct parse_io io;
+ int return_value = 0;
+
+ memset(&io, 0, sizeof(io));
+ io.string = expr; /* to pass to the error routine */
+ io.chan = chan;
+
+ ast_yylex_init(&io.scanner);
+
+ ast_yy_scan_string(expr, io.scanner);
+
+ ast_yyparse ((void *) &io);
+
+ ast_yylex_destroy(io.scanner);
+
+ if (!io.val) {
+ if (length > 1) {
+ strcpy(buf, "0");
+ return_value = 1;
+ }
+ } else {
+ if (io.val->type == AST_EXPR_number) {
+ int res_length;
+
+ res_length = snprintf(buf, length, FP___PRINTF, io.val->u.i);
+ return_value = (res_length <= length) ? res_length : length;
+ } else {
+ if (io.val->u.s)
+#if defined(STANDALONE) || defined(LOW_MEMORY) || defined(STANDALONE_AEL)
+ strncpy(buf, io.val->u.s, length - 1);
+#else /* !STANDALONE && !LOW_MEMORY */
+ ast_copy_string(buf, io.val->u.s, length);
+#endif /* STANDALONE || LOW_MEMORY */
+ else
+ buf[0] = 0;
+ return_value = strlen(buf);
+ if (io.val->u.s)
+ free(io.val->u.s);
+ }
+ free(io.val);
+ }
+ return return_value;
+}
+
+
+char extra_error_message[4095];
+int extra_error_message_supplied = 0;
+void ast_expr_register_extra_error_info(char *message);
+void ast_expr_clear_extra_error_info(void);
+
+void ast_expr_register_extra_error_info(char *message)
+{
+ extra_error_message_supplied=1;
+ strcpy(extra_error_message, message);
+}
+
+void ast_expr_clear_extra_error_info(void)
+{
+ extra_error_message_supplied=0;
+ extra_error_message[0] = 0;
+}
+
+static char *expr2_token_equivs1[] =
+{
+ "TOKEN",
+ "TOK_COND",
+ "TOK_COLONCOLON",
+ "TOK_OR",
+ "TOK_AND",
+ "TOK_EQ",
+ "TOK_GT",
+ "TOK_LT",
+ "TOK_GE",
+ "TOK_LE",
+ "TOK_NE",
+ "TOK_PLUS",
+ "TOK_MINUS",
+ "TOK_MULT",
+ "TOK_DIV",
+ "TOK_MOD",
+ "TOK_COMPL",
+ "TOK_COLON",
+ "TOK_EQTILDE",
+ "TOK_COMMA",
+ "TOK_RP",
+ "TOK_LP"
+};
+
+static char *expr2_token_equivs2[] =
+{
+ "<token>",
+ "?",
+ "::",
+ "|",
+ "&",
+ "=",
+ ">",
+ "<",
+ ">=",
+ "<=",
+ "!=",
+ "+",
+ "-",
+ "*",
+ "/",
+ "%",
+ "!",
+ ":",
+ "=~",
+ ",",
+ ")",
+ "("
+};
+
+
+static char *expr2_token_subst(const char *mess)
+{
+ /* calc a length, malloc, fill, and return; yyerror had better free it! */
+ int len=0,i;
+ const char *p;
+ char *res, *s,*t;
+ int expr2_token_equivs_entries = sizeof(expr2_token_equivs1)/sizeof(char*);
+
+ for (p=mess; *p; p++) {
+ for (i=0; i<expr2_token_equivs_entries; i++) {
+ if ( strncmp(p,expr2_token_equivs1[i],strlen(expr2_token_equivs1[i])) == 0 )
+ {
+ len+=strlen(expr2_token_equivs2[i])+2;
+ p += strlen(expr2_token_equivs1[i])-1;
+ break;
+ }
+ }
+ len++;
+ }
+ res = (char*)malloc(len+1);
+ res[0] = 0;
+ s = res;
+ for (p=mess; *p;) {
+ int found = 0;
+ for (i=0; i<expr2_token_equivs_entries; i++) {
+ if ( strncmp(p,expr2_token_equivs1[i],strlen(expr2_token_equivs1[i])) == 0 ) {
+ *s++ = '\'';
+ for (t=expr2_token_equivs2[i]; *t;) {
+ *s++ = *t++;
+ }
+ *s++ = '\'';
+ p += strlen(expr2_token_equivs1[i]);
+ found = 1;
+ break;
+ }
+ }
+ if( !found )
+ *s++ = *p++;
+ }
+ *s++ = 0;
+ return res;
+}
+
+int ast_yyerror (const char *s, yyltype *loc, struct parse_io *parseio )
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)(parseio->scanner);
+ char spacebuf[8000]; /* best safe than sorry */
+ char spacebuf2[8000]; /* best safe than sorry */
+ int i=0;
+ char *s2 = expr2_token_subst(s);
+ spacebuf[0] = 0;
+
+ for(i=0;i< (int)(yytext - YY_CURRENT_BUFFER_LVALUE->yy_ch_buf);i++) spacebuf2[i] = ' '; /* uh... assuming yyg is defined, then I can use the yycolumn macro,
+ which is the same thing as... get this:
+ yyg->yy_buffer_stack[yyg->yy_buffer_stack_top]->yy_bs_column
+ I was tempted to just use yy_buf_pos in the STATE, but..., well:
+ a. the yy_buf_pos is the current position in the buffer, which
+ may not relate to the entire string/buffer because of the
+ buffering.
+ b. but, analysis of the situation is that when you use the
+ yy_scan_string func, it creates a single buffer the size of
+ string, so the two would be the same...
+ so, in the end, the yycolumn macro is available, shorter, therefore easier. */
+ spacebuf2[i++]='^';
+ spacebuf2[i]= 0;
+
+#ifdef STANDALONE3
+ /* easier to read in the standalone version */
+ printf("ast_yyerror(): %s syntax error: %s; Input:\n%s\n%s\n",
+ (extra_error_message_supplied?extra_error_message:""), s2, parseio->string,spacebuf2);
+#else
+ ast_log(LOG_WARNING,"ast_yyerror(): %s syntax error: %s; Input:\n%s\n%s\n",
+ (extra_error_message_supplied?extra_error_message:""), s2, parseio->string,spacebuf2);
+#endif
+#ifndef STANDALONE
+ ast_log(LOG_WARNING,"If you have questions, please refer to doc/channelvariables.txt in the asterisk source.\n");
+#endif
+ free(s2);
+ return(0);
+}
diff --git a/trunk/main/ast_expr2.h b/trunk/main/ast_expr2.h
new file mode 100644
index 000000000..cd2077149
--- /dev/null
+++ b/trunk/main/ast_expr2.h
@@ -0,0 +1,115 @@
+/* A Bison parser, made by GNU Bison 2.1a. */
+
+/* Skeleton parser for Yacc-like parsing with Bison,
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+/* As a special exception, when this file is copied by Bison into a
+ Bison output file, you may use that output file without restriction.
+ This special exception was added by the Free Software Foundation
+ in version 1.24 of Bison. */
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ TOK_COMMA = 258,
+ TOK_COLONCOLON = 259,
+ TOK_COND = 260,
+ TOK_OR = 261,
+ TOK_AND = 262,
+ TOK_NE = 263,
+ TOK_LE = 264,
+ TOK_GE = 265,
+ TOK_LT = 266,
+ TOK_GT = 267,
+ TOK_EQ = 268,
+ TOK_MINUS = 269,
+ TOK_PLUS = 270,
+ TOK_MOD = 271,
+ TOK_DIV = 272,
+ TOK_MULT = 273,
+ TOK_COMPL = 274,
+ TOK_EQTILDE = 275,
+ TOK_COLON = 276,
+ TOK_LP = 277,
+ TOK_RP = 278,
+ TOKEN = 279
+ };
+#endif
+/* Tokens. */
+#define TOK_COMMA 258
+#define TOK_COLONCOLON 259
+#define TOK_COND 260
+#define TOK_OR 261
+#define TOK_AND 262
+#define TOK_NE 263
+#define TOK_LE 264
+#define TOK_GE 265
+#define TOK_LT 266
+#define TOK_GT 267
+#define TOK_EQ 268
+#define TOK_MINUS 269
+#define TOK_PLUS 270
+#define TOK_MOD 271
+#define TOK_DIV 272
+#define TOK_MULT 273
+#define TOK_COMPL 274
+#define TOK_EQTILDE 275
+#define TOK_COLON 276
+#define TOK_LP 277
+#define TOK_RP 278
+#define TOKEN 279
+
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+#line 341 "ast_expr2.y"
+{
+ struct val *val;
+ struct expr_node *arglist;
+}
+/* Line 1536 of yacc.c. */
+#line 92 "ast_expr2.h"
+ YYSTYPE;
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+
+
+#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED
+typedef struct YYLTYPE
+{
+ int first_line;
+ int first_column;
+ int last_line;
+ int last_column;
+} YYLTYPE;
+# define yyltype YYLTYPE /* obsolescent; will be withdrawn */
+# define YYLTYPE_IS_DECLARED 1
+# define YYLTYPE_IS_TRIVIAL 1
+#endif
+
+
+
+
diff --git a/trunk/main/ast_expr2.y b/trunk/main/ast_expr2.y
new file mode 100644
index 000000000..7eba6d165
--- /dev/null
+++ b/trunk/main/ast_expr2.y
@@ -0,0 +1,1619 @@
+%{
+/* Written by Pace Willisson (pace@blitz.com)
+ * and placed in the public domain.
+ *
+ * Largely rewritten by J.T. Conklin (jtc@wimsey.com)
+ *
+ * And then overhauled twice by Steve Murphy (murf@digium.com)
+ * to add double-quoted strings, allow mult. spaces, improve
+ * error messages, and then to fold in a flex scanner for the
+ * yylex operation.
+ *
+ * $FreeBSD: src/bin/expr/expr.y,v 1.16 2000/07/22 10:59:36 se Exp $
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include "asterisk.h"
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+#ifdef STANDALONE
+#ifndef __USE_ISOC99
+#define __USE_ISOC99 1
+#endif
+#endif
+
+#ifdef __USE_ISOC99
+#define FP___PRINTF "%.18Lg"
+#define FP___TYPE long double
+#else
+#define FP___PRINTF "%.16g"
+#define FP___TYPE double
+#endif
+
+#ifdef HAVE_COSL
+#define FUNC_COS cosl
+#elif defined(HAVE_COS)
+#define FUNC_COS (long double)cos
+#endif
+
+#ifdef HAVE_SINL
+#define FUNC_SIN sinl
+#elif defined(HAVE_SIN)
+#define FUNC_SIN (long double)sin
+#endif
+
+#ifdef HAVE_TANL
+#define FUNC_TAN tanl
+#elif defined(HAVE_TAN)
+#define FUNC_TAN (long double)tan
+#endif
+
+#ifdef HAVE_ACOSL
+#define FUNC_ACOS acosl
+#elif defined(HAVE_ACOS)
+#define FUNC_ACOS (long double)acos
+#endif
+
+#ifdef HAVE_ASINL
+#define FUNC_ASIN asinl
+#elif defined(HAVE_ASIN)
+#define FUNC_ASIN (long double)asin
+#endif
+
+#ifdef HAVE_ATANL
+#define FUNC_ATAN atanl
+#elif defined(HAVE_ATAN)
+#define FUNC_ATAN (long double)atan
+#endif
+
+#ifdef HAVE_ATAN2L
+#define FUNC_ATAN2 atan2l
+#elif defined(HAVE_ATAN2)
+#define FUNC_ATAN2 (long double)atan2
+#endif
+
+#ifdef HAVE_POWL
+#define FUNC_POW powl
+#elif defined(HAVE_POW)
+#define FUNC_POW (long double)pow
+#endif
+
+#ifdef HAVE_SQRTL
+#define FUNC_SQRT sqrtl
+#elif defined(HAVE_SQRT)
+#define FUNC_SQRT (long double)sqrt
+#endif
+
+#ifdef HAVE_RINTL
+#define FUNC_RINT rintl
+#elif defined(HAVE_RINT)
+#define FUNC_RINT (long double)rint
+#endif
+
+#ifdef HAVE_EXPL
+#define FUNC_EXP expl
+#elif defined(HAVE_EXP)
+#define FUNC_EXP (long double)exp
+#endif
+
+#ifdef HAVE_LOGL
+#define FUNC_LOG logl
+#elif defined(HAVE_LOG)
+#define FUNC_LOG (long double)log
+#endif
+
+#ifdef HAVE_REMINDERL
+#define FUNC_REMINDER reminderl
+#elif defined(HAVE_REMINDER)
+#define FUNC_REMINDER (long double)reminder
+#endif
+
+#ifdef HAVE_FMODL
+#define FUNC_FMOD fmodl
+#elif defined(HAVE_FMOD)
+#define FUNC_FMOD (long double)fmod
+#endif
+
+#ifdef HAVE_STRTOLD
+#define FUNC_STRTOD strtold
+#elif defined(HAVE_STRTOD)
+#define FUNC_STRTOD (long double)strtod
+#endif
+
+#ifdef HAVE_FLOORL
+#define FUNC_FLOOR floorl
+#elif defined(HAVE_FLOOR)
+#define FUNC_FLOOR (long double)floor
+#endif
+
+#ifdef HAVE_CEILL
+#define FUNC_CEIL ceill
+#elif defined(HAVE_CEIL)
+#define FUNC_CEIL (long double)ceil
+#endif
+
+#ifdef HAVE_ROUNDL
+#define FUNC_ROUND roundl
+#elif defined(HAVE_ROUND)
+#define FUNC_ROUND (long double)round
+#endif
+
+#ifdef HAVE_TRUNCL
+#define FUNC_TRUNC truncl
+#elif defined(HAVE_TRUNC)
+#define FUNC_TRUNC (long double)trunc
+#endif
+
+/*! \note
+ * Oddly enough, some platforms have some ISO C99 functions, but not others, so
+ * we define the missing functions in terms of their mathematical identities.
+ */
+#ifdef HAVE_EXP2L
+#define FUNC_EXP2 exp2l
+#elif (defined(HAVE_EXPL) && defined(HAVE_LOGL))
+#define FUNC_EXP2(x) expl((x) * logl(2.0))
+#elif (defined(HAVE_EXP) && defined(HAVE_LOG))
+#define FUNC_EXP2(x) (long double)exp((x) * log(2.0))
+#endif
+
+#ifdef HAVE_EXP10L
+#define FUNC_EXP10 exp10l
+#elif (defined(HAVE_EXPL) && defined(HAVE_LOGL))
+#define FUNC_EXP10(x) expl((x) * logl(10.0))
+#elif (defined(HAVE_EXP) && defined(HAVE_LOG))
+#define FUNC_EXP10(x) (long double)exp((x) * log(10.0))
+#endif
+
+#ifdef HAVE_LOG2L
+#define FUNC_LOG2 log2l
+#elif defined(HAVE_LOGL)
+#define FUNC_LOG2(x) (logl(x) / logl(2.0))
+#elif defined(HAVE_LOG10L)
+#define FUNC_LOG2(x) (log10l(x) / log10l(2.0))
+#elif defined(HAVE_LOG2)
+#define FUNC_LOG2 (long double)log2
+#elif defined(HAVE_LOG)
+#define FUNC_LOG2(x) ((long double)log(x) / log(2.0))
+#endif
+
+#ifdef HAVE_LOG10L
+#define FUNC_LOG10 log10l
+#elif defined(HAVE_LOGL)
+#define FUNC_LOG10(x) (logl(x) / logl(10.0))
+#elif defined(HAVE_LOG2L)
+#define FUNC_LOG10(x) (log2l(x) / log2l(10.0))
+#elif defined(HAVE_LOG10)
+#define FUNC_LOG10(x) (long double)log10(x)
+#elif defined(HAVE_LOG)
+#define FUNC_LOG10(x) ((long double)log(x) / log(10.0))
+#endif
+
+
+#include <stdlib.h>
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include <string.h>
+#include <math.h>
+#include <locale.h>
+#include <unistd.h>
+#include <ctype.h>
+#if !defined(SOLARIS) && !defined(__CYGWIN__)
+ /* #include <err.h> */
+#else
+#define quad_t int64_t
+#endif
+#include <errno.h>
+#include <regex.h>
+#include <limits.h>
+
+#include "asterisk/ast_expr.h"
+#include "asterisk/logger.h"
+#ifndef STANDALONE
+#include "asterisk/pbx.h"
+#endif
+
+#if defined(LONG_LONG_MIN) && !defined(QUAD_MIN)
+#define QUAD_MIN LONG_LONG_MIN
+#endif
+#if defined(LONG_LONG_MAX) && !defined(QUAD_MAX)
+#define QUAD_MAX LONG_LONG_MAX
+#endif
+
+# if ! defined(QUAD_MIN)
+# define QUAD_MIN (-0x7fffffffffffffffLL-1)
+# endif
+# if ! defined(QUAD_MAX)
+# define QUAD_MAX (0x7fffffffffffffffLL)
+# endif
+#define YYENABLE_NLS 0
+#define YYPARSE_PARAM parseio
+#define YYLEX_PARAM ((struct parse_io *)parseio)->scanner
+#define YYERROR_VERBOSE 1
+extern char extra_error_message[4095];
+extern int extra_error_message_supplied;
+
+enum valtype {
+ AST_EXPR_number, AST_EXPR_numeric_string, AST_EXPR_string
+} ;
+
+#ifdef STANDALONE
+void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...) __attribute__ ((format (printf,5,6)));
+#endif
+
+struct val {
+ enum valtype type;
+ union {
+ char *s;
+ FP___TYPE i; /* either long double, or just double, on a bad day */
+ } u;
+} ;
+
+enum node_type {
+ AST_EXPR_NODE_COMMA, AST_EXPR_NODE_STRING, AST_EXPR_NODE_VAL
+} ;
+
+struct expr_node
+{
+ enum node_type type;
+ struct val *val;
+ struct expr_node *left;
+ struct expr_node *right;
+};
+
+
+typedef void *yyscan_t;
+
+struct parse_io
+{
+ char *string;
+ struct val *val;
+ yyscan_t scanner;
+ struct ast_channel *chan;
+};
+
+static int chk_div __P((FP___TYPE, FP___TYPE));
+static int chk_minus __P((FP___TYPE, FP___TYPE, FP___TYPE));
+static int chk_plus __P((FP___TYPE, FP___TYPE, FP___TYPE));
+static int chk_times __P((FP___TYPE, FP___TYPE, FP___TYPE));
+static void free_value __P((struct val *));
+static int is_zero_or_null __P((struct val *));
+static int isstring __P((struct val *));
+static struct val *make_number __P((FP___TYPE));
+static struct val *make_str __P((const char *));
+static struct val *op_and __P((struct val *, struct val *));
+static struct val *op_colon __P((struct val *, struct val *));
+static struct val *op_eqtilde __P((struct val *, struct val *));
+static struct val *op_div __P((struct val *, struct val *));
+static struct val *op_eq __P((struct val *, struct val *));
+static struct val *op_ge __P((struct val *, struct val *));
+static struct val *op_gt __P((struct val *, struct val *));
+static struct val *op_le __P((struct val *, struct val *));
+static struct val *op_lt __P((struct val *, struct val *));
+static struct val *op_cond __P((struct val *, struct val *, struct val *));
+static struct val *op_minus __P((struct val *, struct val *));
+static struct val *op_negate __P((struct val *));
+static struct val *op_compl __P((struct val *));
+static struct val *op_ne __P((struct val *, struct val *));
+static struct val *op_or __P((struct val *, struct val *));
+static struct val *op_plus __P((struct val *, struct val *));
+static struct val *op_rem __P((struct val *, struct val *));
+static struct val *op_times __P((struct val *, struct val *));
+static struct val *op_func(struct val *funcname, struct expr_node *arglist, struct ast_channel *chan);
+static int to_number __P((struct val *));
+static void to_string __P((struct val *));
+static struct expr_node *alloc_expr_node(enum node_type);
+static void destroy_arglist(struct expr_node *arglist);
+static int is_really_num(char *str);
+
+/* uh, if I want to predeclare yylex with a YYLTYPE, I have to predeclare the yyltype... sigh */
+typedef struct yyltype
+{
+ int first_line;
+ int first_column;
+
+ int last_line;
+ int last_column;
+} yyltype;
+
+# define YYLTYPE yyltype
+# define YYLTYPE_IS_TRIVIAL 1
+
+/* we will get warning about no prototype for yylex! But we can't
+ define it here, we have no definition yet for YYSTYPE. */
+
+int ast_yyerror(const char *,YYLTYPE *, struct parse_io *);
+
+/* I wanted to add args to the yyerror routine, so I could print out
+ some useful info about the error. Not as easy as it looks, but it
+ is possible. */
+#define ast_yyerror(x) ast_yyerror(x,&yyloc,parseio)
+#define DESTROY(x) {if((x)->type == AST_EXPR_numeric_string || (x)->type == AST_EXPR_string) free((x)->u.s); (x)->u.s = 0; free(x);}
+%}
+
+%pure-parser
+%locations
+/* %debug for when you are having big problems */
+
+/* %name-prefix="ast_yy" */
+
+%union
+{
+ struct val *val;
+ struct expr_node *arglist;
+}
+
+%{
+extern int ast_yylex __P((YYSTYPE *, YYLTYPE *, yyscan_t));
+%}
+%left <val> TOK_COMMA
+%left <val> TOK_COND TOK_COLONCOLON
+%left <val> TOK_OR
+%left <val> TOK_AND
+%left <val> TOK_EQ TOK_GT TOK_LT TOK_GE TOK_LE TOK_NE
+%left <val> TOK_PLUS TOK_MINUS
+%left <val> TOK_MULT TOK_DIV TOK_MOD
+%right <val> TOK_COMPL
+%left <val> TOK_COLON TOK_EQTILDE
+%left <val> TOK_RP TOK_LP
+
+
+%token <val> TOKEN
+%type <arglist> arglist
+%type <val> start expr
+
+
+%destructor { free_value($$); } expr TOKEN TOK_COND TOK_COLONCOLON TOK_OR TOK_AND TOK_EQ
+ TOK_GT TOK_LT TOK_GE TOK_LE TOK_NE TOK_PLUS TOK_MINUS TOK_MULT TOK_DIV TOK_MOD TOK_COMPL TOK_COLON TOK_EQTILDE
+ TOK_RP TOK_LP
+
+%%
+
+start: expr { ((struct parse_io *)parseio)->val = (struct val *)calloc(sizeof(struct val),1);
+ ((struct parse_io *)parseio)->val->type = $1->type;
+ if( $1->type == AST_EXPR_number )
+ ((struct parse_io *)parseio)->val->u.i = $1->u.i;
+ else
+ ((struct parse_io *)parseio)->val->u.s = $1->u.s;
+ free($1);
+ }
+ | {/* nothing */ ((struct parse_io *)parseio)->val = (struct val *)calloc(sizeof(struct val),1);
+ ((struct parse_io *)parseio)->val->type = AST_EXPR_string;
+ ((struct parse_io *)parseio)->val->u.s = strdup("");
+ }
+
+ ;
+
+arglist: expr { $$ = alloc_expr_node(AST_EXPR_NODE_VAL); $$->val = $1;}
+ | arglist TOK_COMMA expr %prec TOK_RP{struct expr_node *x = alloc_expr_node(AST_EXPR_NODE_VAL);
+ struct expr_node *t;
+ DESTROY($2);
+ for (t=$1;t->right;t=t->right)
+ ;
+ $$ = $1; t->right = x; x->val = $3;}
+ ;
+
+expr:
+ TOKEN TOK_LP arglist TOK_RP { $$ = op_func($1,$3, ((struct parse_io *)parseio)->chan);
+ DESTROY($2);
+ DESTROY($4);
+ DESTROY($1);
+ destroy_arglist($3);
+ }
+ | TOKEN {$$ = $1;}
+ | TOK_LP expr TOK_RP { $$ = $2;
+ @$.first_column = @1.first_column; @$.last_column = @3.last_column;
+ @$.first_line=0; @$.last_line=0;
+ DESTROY($1); DESTROY($3); }
+ | expr TOK_OR expr { $$ = op_or ($1, $3);
+ DESTROY($2);
+ @$.first_column = @1.first_column; @$.last_column = @3.last_column;
+ @$.first_line=0; @$.last_line=0;}
+ | expr TOK_AND expr { $$ = op_and ($1, $3);
+ DESTROY($2);
+ @$.first_column = @1.first_column; @$.last_column = @3.last_column;
+ @$.first_line=0; @$.last_line=0;}
+ | expr TOK_EQ expr { $$ = op_eq ($1, $3);
+ DESTROY($2);
+ @$.first_column = @1.first_column; @$.last_column = @3.last_column;
+ @$.first_line=0; @$.last_line=0;}
+ | expr TOK_GT expr { $$ = op_gt ($1, $3);
+ DESTROY($2);
+ @$.first_column = @1.first_column; @$.last_column = @3.last_column;
+ @$.first_line=0; @$.last_line=0;}
+ | expr TOK_LT expr { $$ = op_lt ($1, $3);
+ DESTROY($2);
+ @$.first_column = @1.first_column; @$.last_column = @3.last_column;
+ @$.first_line=0; @$.last_line=0;}
+ | expr TOK_GE expr { $$ = op_ge ($1, $3);
+ DESTROY($2);
+ @$.first_column = @1.first_column; @$.last_column = @3.last_column;
+ @$.first_line=0; @$.last_line=0;}
+ | expr TOK_LE expr { $$ = op_le ($1, $3);
+ DESTROY($2);
+ @$.first_column = @1.first_column; @$.last_column = @3.last_column;
+ @$.first_line=0; @$.last_line=0;}
+ | expr TOK_NE expr { $$ = op_ne ($1, $3);
+ DESTROY($2);
+ @$.first_column = @1.first_column; @$.last_column = @3.last_column;
+ @$.first_line=0; @$.last_line=0;}
+ | expr TOK_PLUS expr { $$ = op_plus ($1, $3);
+ DESTROY($2);
+ @$.first_column = @1.first_column; @$.last_column = @3.last_column;
+ @$.first_line=0; @$.last_line=0;}
+ | expr TOK_MINUS expr { $$ = op_minus ($1, $3);
+ DESTROY($2);
+ @$.first_column = @1.first_column; @$.last_column = @3.last_column;
+ @$.first_line=0; @$.last_line=0;}
+ | TOK_MINUS expr %prec TOK_COMPL { $$ = op_negate ($2);
+ DESTROY($1);
+ @$.first_column = @1.first_column; @$.last_column = @2.last_column;
+ @$.first_line=0; @$.last_line=0;}
+ | TOK_COMPL expr { $$ = op_compl ($2);
+ DESTROY($1);
+ @$.first_column = @1.first_column; @$.last_column = @2.last_column;
+ @$.first_line=0; @$.last_line=0;}
+ | expr TOK_MULT expr { $$ = op_times ($1, $3);
+ DESTROY($2);
+ @$.first_column = @1.first_column; @$.last_column = @3.last_column;
+ @$.first_line=0; @$.last_line=0;}
+ | expr TOK_DIV expr { $$ = op_div ($1, $3);
+ DESTROY($2);
+ @$.first_column = @1.first_column; @$.last_column = @3.last_column;
+ @$.first_line=0; @$.last_line=0;}
+ | expr TOK_MOD expr { $$ = op_rem ($1, $3);
+ DESTROY($2);
+ @$.first_column = @1.first_column; @$.last_column = @3.last_column;
+ @$.first_line=0; @$.last_line=0;}
+ | expr TOK_COLON expr { $$ = op_colon ($1, $3);
+ DESTROY($2);
+ @$.first_column = @1.first_column; @$.last_column = @3.last_column;
+ @$.first_line=0; @$.last_line=0;}
+ | expr TOK_EQTILDE expr { $$ = op_eqtilde ($1, $3);
+ DESTROY($2);
+ @$.first_column = @1.first_column; @$.last_column = @3.last_column;
+ @$.first_line=0; @$.last_line=0;}
+ | expr TOK_COND expr TOK_COLONCOLON expr { $$ = op_cond ($1, $3, $5);
+ DESTROY($2);
+ DESTROY($4);
+ @$.first_column = @1.first_column; @$.last_column = @3.last_column;
+ @$.first_line=0; @$.last_line=0;}
+ ;
+
+%%
+
+static struct expr_node *alloc_expr_node(enum node_type nt)
+{
+ struct expr_node *x = calloc(1,sizeof(struct expr_node));
+ if (!x) {
+ ast_log(LOG_ERROR, "Allocation for expr_node FAILED!!\n");
+ return 0;
+ }
+ x->type = nt;
+ return x;
+}
+
+
+
+static struct val *
+make_number (FP___TYPE i)
+{
+ struct val *vp;
+
+ vp = (struct val *) malloc (sizeof (*vp));
+ if (vp == NULL) {
+ ast_log(LOG_WARNING, "malloc() failed\n");
+ return(NULL);
+ }
+
+ vp->type = AST_EXPR_number;
+ vp->u.i = i;
+ return vp;
+}
+
+static struct val *
+make_str (const char *s)
+{
+ struct val *vp;
+ size_t i;
+ int isint; /* this started out being a test for an integer, but then ended up being a test for a float */
+
+ vp = (struct val *) malloc (sizeof (*vp));
+ if (vp == NULL || ((vp->u.s = strdup (s)) == NULL)) {
+ ast_log(LOG_WARNING,"malloc() failed\n");
+ return(NULL);
+ }
+
+ for (i = 0, isint = (isdigit(s[0]) || s[0] == '-' || s[0]=='.'); isint && i < strlen(s); i++)
+ {
+ if (!isdigit(s[i]) && s[i] != '.') {
+ isint = 0;
+ break;
+ }
+ }
+ if (isint)
+ vp->type = AST_EXPR_numeric_string;
+ else
+ vp->type = AST_EXPR_string;
+
+ return vp;
+}
+
+
+static void
+free_value (struct val *vp)
+{
+ if (vp==NULL) {
+ return;
+ }
+ if (vp->type == AST_EXPR_string || vp->type == AST_EXPR_numeric_string)
+ free (vp->u.s);
+ free(vp);
+}
+
+
+static int
+to_number (struct val *vp)
+{
+ FP___TYPE i;
+
+ if (vp == NULL) {
+ ast_log(LOG_WARNING,"vp==NULL in to_number()\n");
+ return(0);
+ }
+
+ if (vp->type == AST_EXPR_number)
+ return 1;
+
+ if (vp->type == AST_EXPR_string)
+ return 0;
+
+ /* vp->type == AST_EXPR_numeric_string, make it numeric */
+ errno = 0;
+ i = FUNC_STRTOD(vp->u.s, (char**)0); /* either strtod, or strtold on a good day */
+ if (errno != 0) {
+ ast_log(LOG_WARNING,"Conversion of %s to number under/overflowed!\n", vp->u.s);
+ free(vp->u.s);
+ vp->u.s = 0;
+ return(0);
+ }
+ free (vp->u.s);
+ vp->u.i = i;
+ vp->type = AST_EXPR_number;
+ return 1;
+}
+
+static void
+strip_quotes(struct val *vp)
+{
+ if (vp->type != AST_EXPR_string && vp->type != AST_EXPR_numeric_string)
+ return;
+
+ if( vp->u.s[0] == '"' && vp->u.s[strlen(vp->u.s)-1] == '"' )
+ {
+ char *f, *t;
+ f = vp->u.s;
+ t = vp->u.s;
+
+ while( *f )
+ {
+ if( *f && *f != '"' )
+ *t++ = *f++;
+ else
+ f++;
+ }
+ *t = *f;
+ }
+}
+
+static void
+to_string (struct val *vp)
+{
+ char *tmp;
+
+ if (vp->type == AST_EXPR_string || vp->type == AST_EXPR_numeric_string)
+ return;
+
+ tmp = malloc ((size_t)25);
+ if (tmp == NULL) {
+ ast_log(LOG_WARNING,"malloc() failed\n");
+ return;
+ }
+
+ sprintf(tmp, FP___PRINTF, vp->u.i);
+ vp->type = AST_EXPR_string;
+ vp->u.s = tmp;
+}
+
+
+static int
+isstring (struct val *vp)
+{
+ /* only TRUE if this string is not a valid number */
+ return (vp->type == AST_EXPR_string);
+}
+
+
+static int
+is_zero_or_null (struct val *vp)
+{
+ if (vp->type == AST_EXPR_number) {
+ return (vp->u.i == 0);
+ } else {
+ return (*vp->u.s == 0 || (to_number(vp) && vp->u.i == 0));
+ }
+ /* NOTREACHED */
+}
+
+#ifdef STANDALONE
+
+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);
+}
+
+
+int main(int argc,char **argv) {
+ char s[4096];
+ char out[4096];
+ FILE *infile;
+
+ if( !argv[1] )
+ exit(20);
+
+ if( access(argv[1],F_OK)== 0 )
+ {
+ int ret;
+
+ infile = fopen(argv[1],"r");
+ if( !infile )
+ {
+ printf("Sorry, couldn't open %s for reading!\n", argv[1]);
+ exit(10);
+ }
+ while( fgets(s,sizeof(s),infile) )
+ {
+ if( s[strlen(s)-1] == '\n' )
+ s[strlen(s)-1] = 0;
+
+ ret = ast_expr(s, out, sizeof(out),NULL);
+ printf("Expression: %s Result: [%d] '%s'\n",
+ s, ret, out);
+ }
+ fclose(infile);
+ }
+ else
+ {
+ if (ast_expr(argv[1], s, sizeof(s), NULL))
+ printf("=====%s======\n",s);
+ else
+ printf("No result\n");
+ }
+}
+
+#endif
+
+#undef ast_yyerror
+#define ast_yyerror(x) ast_yyerror(x, YYLTYPE *yylloc, struct parse_io *parseio)
+
+/* I put the ast_yyerror func in the flex input file,
+ because it refers to the buffer state. Best to
+ let it access the BUFFER stuff there and not trying
+ define all the structs, macros etc. in this file! */
+
+static void destroy_arglist(struct expr_node *arglist)
+{
+ struct expr_node *arglist_next;
+
+ while (arglist)
+ {
+ arglist_next = arglist->right;
+ if (arglist->val)
+ free_value(arglist->val);
+ arglist->val = 0;
+ arglist->right = 0;
+ free(arglist);
+ arglist = arglist_next;
+ }
+}
+
+static char *compose_func_args(struct expr_node *arglist)
+{
+ struct expr_node *t = arglist;
+ char *argbuf;
+ int total_len = 0;
+
+ while (t) {
+ if (t != arglist)
+ total_len += 1; /* for the sep */
+ if (t->val) {
+ if (t->val->type == AST_EXPR_number)
+ total_len += 25; /* worst case */
+ else
+ total_len += strlen(t->val->u.s);
+ }
+
+ t = t->right;
+ }
+ total_len++; /* for the null */
+ ast_log(LOG_NOTICE,"argbuf allocated %d bytes;\n", total_len);
+ argbuf = malloc(total_len);
+ argbuf[0] = 0;
+ t = arglist;
+ while (t) {
+ char numbuf[30];
+
+ if (t != arglist)
+ strcat(argbuf,"|");
+
+ if (t->val) {
+ if (t->val->type == AST_EXPR_number) {
+ sprintf(numbuf,FP___PRINTF,t->val->u.i);
+ strcat(argbuf,numbuf);
+ } else
+ strcat(argbuf,t->val->u.s);
+ }
+ t = t->right;
+ }
+ ast_log(LOG_NOTICE,"argbuf uses %d bytes;\n", (int) strlen(argbuf));
+ return argbuf;
+}
+
+static int is_really_num(char *str)
+{
+ if ( strspn(str,"-0123456789. ") == strlen(str))
+ return 1;
+ else
+ return 0;
+}
+
+
+static struct val *op_func(struct val *funcname, struct expr_node *arglist, struct ast_channel *chan)
+{
+ if (strspn(funcname->u.s,"ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789") == strlen(funcname->u.s))
+ {
+ struct val *result;
+ if (0) {
+#ifdef FUNC_COS
+ } else if (strcmp(funcname->u.s,"COS") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_COS(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_SIN
+ } else if (strcmp(funcname->u.s,"SIN") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_SIN(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_TAN
+ } else if (strcmp(funcname->u.s,"TAN") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_TAN(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_ACOS
+ } else if (strcmp(funcname->u.s,"ACOS") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_ACOS(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_ASIN
+ } else if (strcmp(funcname->u.s,"ASIN") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_ASIN(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_ATAN
+ } else if (strcmp(funcname->u.s,"ATAN") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_ATAN(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_ATAN2
+ } else if (strcmp(funcname->u.s,"ATAN2") == 0) {
+ if (arglist && arglist->right && !arglist->right->right && arglist->val && arglist->right->val){
+ to_number(arglist->val);
+ to_number(arglist->right->val);
+ result = make_number(FUNC_ATAN2(arglist->val->u.i, arglist->right->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_POW
+ } else if (strcmp(funcname->u.s,"POW") == 0) {
+ if (arglist && arglist->right && !arglist->right->right && arglist->val && arglist->right->val){
+ to_number(arglist->val);
+ to_number(arglist->right->val);
+ result = make_number(FUNC_POW(arglist->val->u.i, arglist->right->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_SQRT
+ } else if (strcmp(funcname->u.s,"SQRT") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_SQRT(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_FLOOR
+ } else if (strcmp(funcname->u.s,"FLOOR") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_FLOOR(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_CEIL
+ } else if (strcmp(funcname->u.s,"CEIL") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_CEIL(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_ROUND
+ } else if (strcmp(funcname->u.s,"ROUND") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_ROUND(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif /* defined(FUNC_ROUND) */
+#ifdef FUNC_RINT
+ } else if (strcmp(funcname->u.s,"RINT") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_RINT(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_TRUNC
+ } else if (strcmp(funcname->u.s,"TRUNC") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_TRUNC(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif /* defined(FUNC_TRUNC) */
+#ifdef FUNC_EXP
+ } else if (strcmp(funcname->u.s,"EXP") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_EXP(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_EXP2
+ } else if (strcmp(funcname->u.s,"EXP2") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_EXP2(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_EXP10
+ } else if (strcmp(funcname->u.s,"EXP10") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_EXP10(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_LOG
+ } else if (strcmp(funcname->u.s,"LOG") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_LOG(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_LOG2
+ } else if (strcmp(funcname->u.s,"LOG2") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_LOG2(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_LOG10
+ } else if (strcmp(funcname->u.s,"LOG10") == 0) {
+ if (arglist && !arglist->right && arglist->val){
+ to_number(arglist->val);
+ result = make_number(FUNC_LOG10(arglist->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+#ifdef FUNC_REMAINDER
+ } else if (strcmp(funcname->u.s,"REMAINDER") == 0) {
+ if (arglist && arglist->right && !arglist->right->right && arglist->val && arglist->right->val){
+ to_number(arglist->val);
+ to_number(arglist->right->val);
+ result = make_number(FUNC_REMAINDER(arglist->val->u.i, arglist->right->val->u.i));
+ return result;
+ } else {
+ ast_log(LOG_WARNING,"Wrong args to %s() function\n",funcname->u.s);
+ return make_number(0.0);
+ }
+#endif
+ } else {
+ /* is this a custom function we should execute and collect the results of? */
+#ifndef STANDALONE
+ struct ast_custom_function *f = ast_custom_function_find(funcname->u.s);
+ if (!chan)
+ ast_log(LOG_WARNING,"Hey! chan is NULL.\n");
+ if (!f)
+ ast_log(LOG_WARNING,"Hey! could not find func %s.\n", funcname->u.s);
+
+ if (f && chan) {
+ if (f->read) {
+ char workspace[512];
+ char *argbuf = compose_func_args(arglist);
+ f->read(chan, funcname->u.s, argbuf, workspace, sizeof(workspace));
+ free(argbuf);
+ if (is_really_num(workspace))
+ return make_number(FUNC_STRTOD(workspace,(char **)NULL));
+ else
+ return make_str(workspace);
+ } else {
+ ast_log(LOG_ERROR,"Error! Function '%s' cannot be read!\n", funcname->u.s);
+ return (make_number ((FP___TYPE)0.0));
+ }
+
+ } else {
+ ast_log(LOG_ERROR,"Error! '%s' doesn't appear to be an available function!", funcname->u.s);
+ return (make_number ((FP___TYPE)0.0));
+ }
+#else
+ ast_log(LOG_ERROR,"Error! '%s' is not available in the standalone version!", funcname->u.s);
+ return (make_number ((FP___TYPE)0.0));
+#endif
+ }
+ }
+ else
+ {
+ ast_log(LOG_ERROR,"Error! '%s' is not possibly a function name!", funcname->u.s);
+ return (make_number ((FP___TYPE)0.0));
+ }
+ return (make_number ((FP___TYPE)0.0));
+}
+
+
+static struct val *
+op_or (struct val *a, struct val *b)
+{
+ if (is_zero_or_null (a)) {
+ free_value (a);
+ return (b);
+ } else {
+ free_value (b);
+ return (a);
+ }
+}
+
+static struct val *
+op_and (struct val *a, struct val *b)
+{
+ if (is_zero_or_null (a) || is_zero_or_null (b)) {
+ free_value (a);
+ free_value (b);
+ return (make_number ((FP___TYPE)0.0));
+ } else {
+ free_value (b);
+ return (a);
+ }
+}
+
+static struct val *
+op_eq (struct val *a, struct val *b)
+{
+ struct val *r;
+
+ if (isstring (a) || isstring (b)) {
+ to_string (a);
+ to_string (b);
+ r = make_number ((FP___TYPE)(strcoll (a->u.s, b->u.s) == 0));
+ } else {
+#ifdef DEBUG_FOR_CONVERSIONS
+ char buffer[2000];
+ sprintf(buffer,"Converting '%s' and '%s' ", a->u.s, b->u.s);
+#endif
+ (void)to_number(a);
+ (void)to_number(b);
+#ifdef DEBUG_FOR_CONVERSIONS
+ ast_log(LOG_WARNING,"%s to '%lld' and '%lld'\n", buffer, a->u.i, b->u.i);
+#endif
+ r = make_number ((FP___TYPE)(a->u.i == b->u.i));
+ }
+
+ free_value (a);
+ free_value (b);
+ return r;
+}
+
+static struct val *
+op_gt (struct val *a, struct val *b)
+{
+ struct val *r;
+
+ if (isstring (a) || isstring (b)) {
+ to_string (a);
+ to_string (b);
+ r = make_number ((FP___TYPE)(strcoll (a->u.s, b->u.s) > 0));
+ } else {
+ (void)to_number(a);
+ (void)to_number(b);
+ r = make_number ((FP___TYPE)(a->u.i > b->u.i));
+ }
+
+ free_value (a);
+ free_value (b);
+ return r;
+}
+
+static struct val *
+op_lt (struct val *a, struct val *b)
+{
+ struct val *r;
+
+ if (isstring (a) || isstring (b)) {
+ to_string (a);
+ to_string (b);
+ r = make_number ((FP___TYPE)(strcoll (a->u.s, b->u.s) < 0));
+ } else {
+ (void)to_number(a);
+ (void)to_number(b);
+ r = make_number ((FP___TYPE)(a->u.i < b->u.i));
+ }
+
+ free_value (a);
+ free_value (b);
+ return r;
+}
+
+static struct val *
+op_ge (struct val *a, struct val *b)
+{
+ struct val *r;
+
+ if (isstring (a) || isstring (b)) {
+ to_string (a);
+ to_string (b);
+ r = make_number ((FP___TYPE)(strcoll (a->u.s, b->u.s) >= 0));
+ } else {
+ (void)to_number(a);
+ (void)to_number(b);
+ r = make_number ((FP___TYPE)(a->u.i >= b->u.i));
+ }
+
+ free_value (a);
+ free_value (b);
+ return r;
+}
+
+static struct val *
+op_le (struct val *a, struct val *b)
+{
+ struct val *r;
+
+ if (isstring (a) || isstring (b)) {
+ to_string (a);
+ to_string (b);
+ r = make_number ((FP___TYPE)(strcoll (a->u.s, b->u.s) <= 0));
+ } else {
+ (void)to_number(a);
+ (void)to_number(b);
+ r = make_number ((FP___TYPE)(a->u.i <= b->u.i));
+ }
+
+ free_value (a);
+ free_value (b);
+ return r;
+}
+
+static struct val *
+op_cond (struct val *a, struct val *b, struct val *c)
+{
+ struct val *r;
+
+ if( isstring(a) )
+ {
+ if( strlen(a->u.s) && strcmp(a->u.s, "\"\"") != 0 && strcmp(a->u.s,"0") != 0 )
+ {
+ free_value(a);
+ free_value(c);
+ r = b;
+ }
+ else
+ {
+ free_value(a);
+ free_value(b);
+ r = c;
+ }
+ }
+ else
+ {
+ (void)to_number(a);
+ if( a->u.i )
+ {
+ free_value(a);
+ free_value(c);
+ r = b;
+ }
+ else
+ {
+ free_value(a);
+ free_value(b);
+ r = c;
+ }
+ }
+ return r;
+}
+
+static struct val *
+op_ne (struct val *a, struct val *b)
+{
+ struct val *r;
+
+ if (isstring (a) || isstring (b)) {
+ to_string (a);
+ to_string (b);
+ r = make_number ((FP___TYPE)(strcoll (a->u.s, b->u.s) != 0));
+ } else {
+ (void)to_number(a);
+ (void)to_number(b);
+ r = make_number ((FP___TYPE)(a->u.i != b->u.i));
+ }
+
+ free_value (a);
+ free_value (b);
+ return r;
+}
+
+static int
+chk_plus (FP___TYPE a, FP___TYPE b, FP___TYPE r)
+{
+ /* sum of two positive numbers must be positive */
+ if (a > 0 && b > 0 && r <= 0)
+ return 1;
+ /* sum of two negative numbers must be negative */
+ if (a < 0 && b < 0 && r >= 0)
+ return 1;
+ /* all other cases are OK */
+ return 0;
+}
+
+static struct val *
+op_plus (struct val *a, struct val *b)
+{
+ struct val *r;
+
+ if (!to_number (a)) {
+ if( !extra_error_message_supplied )
+ ast_log(LOG_WARNING,"non-numeric argument\n");
+ if (!to_number (b)) {
+ free_value(a);
+ free_value(b);
+ return make_number(0);
+ } else {
+ free_value(a);
+ return (b);
+ }
+ } else if (!to_number(b)) {
+ free_value(b);
+ return (a);
+ }
+
+ r = make_number (a->u.i + b->u.i);
+ if (chk_plus (a->u.i, b->u.i, r->u.i)) {
+ ast_log(LOG_WARNING,"overflow\n");
+ }
+ free_value (a);
+ free_value (b);
+ return r;
+}
+
+static int
+chk_minus (FP___TYPE a, FP___TYPE b, FP___TYPE r)
+{
+ /* special case subtraction of QUAD_MIN */
+ if (b == QUAD_MIN) {
+ if (a >= 0)
+ return 1;
+ else
+ return 0;
+ }
+ /* this is allowed for b != QUAD_MIN */
+ return chk_plus (a, -b, r);
+}
+
+static struct val *
+op_minus (struct val *a, struct val *b)
+{
+ struct val *r;
+
+ if (!to_number (a)) {
+ if( !extra_error_message_supplied )
+ ast_log(LOG_WARNING, "non-numeric argument\n");
+ if (!to_number (b)) {
+ free_value(a);
+ free_value(b);
+ return make_number(0);
+ } else {
+ r = make_number(0 - b->u.i);
+ free_value(a);
+ free_value(b);
+ return (r);
+ }
+ } else if (!to_number(b)) {
+ if( !extra_error_message_supplied )
+ ast_log(LOG_WARNING, "non-numeric argument\n");
+ free_value(b);
+ return (a);
+ }
+
+ r = make_number (a->u.i - b->u.i);
+ if (chk_minus (a->u.i, b->u.i, r->u.i)) {
+ ast_log(LOG_WARNING, "overflow\n");
+ }
+ free_value (a);
+ free_value (b);
+ return r;
+}
+
+static struct val *
+op_negate (struct val *a)
+{
+ struct val *r;
+
+ if (!to_number (a) ) {
+ free_value(a);
+ if( !extra_error_message_supplied )
+ ast_log(LOG_WARNING, "non-numeric argument\n");
+ return make_number(0);
+ }
+
+ r = make_number (- a->u.i);
+ if (chk_minus (0, a->u.i, r->u.i)) {
+ ast_log(LOG_WARNING, "overflow\n");
+ }
+ free_value (a);
+ return r;
+}
+
+static struct val *
+op_compl (struct val *a)
+{
+ int v1 = 1;
+ struct val *r;
+
+ if( !a )
+ {
+ v1 = 0;
+ }
+ else
+ {
+ switch( a->type )
+ {
+ case AST_EXPR_number:
+ if( a->u.i == 0 )
+ v1 = 0;
+ break;
+
+ case AST_EXPR_string:
+ if( a->u.s == 0 )
+ v1 = 0;
+ else
+ {
+ if( a->u.s[0] == 0 )
+ v1 = 0;
+ else if (strlen(a->u.s) == 1 && a->u.s[0] == '0' )
+ v1 = 0;
+ }
+ break;
+
+ case AST_EXPR_numeric_string:
+ if( a->u.s == 0 )
+ v1 = 0;
+ else
+ {
+ if( a->u.s[0] == 0 )
+ v1 = 0;
+ else if (strlen(a->u.s) == 1 && a->u.s[0] == '0' )
+ v1 = 0;
+ }
+ break;
+ }
+ }
+
+ r = make_number (!v1);
+ free_value (a);
+ return r;
+}
+
+static int
+chk_times (FP___TYPE a, FP___TYPE b, FP___TYPE r)
+{
+ /* special case: first operand is 0, no overflow possible */
+ if (a == 0)
+ return 0;
+ /* cerify that result of division matches second operand */
+ if (r / a != b)
+ return 1;
+ return 0;
+}
+
+static struct val *
+op_times (struct val *a, struct val *b)
+{
+ struct val *r;
+
+ if (!to_number (a) || !to_number (b)) {
+ free_value(a);
+ free_value(b);
+ if( !extra_error_message_supplied )
+ ast_log(LOG_WARNING, "non-numeric argument\n");
+ return(make_number(0));
+ }
+
+ r = make_number (a->u.i * b->u.i);
+ if (chk_times (a->u.i, b->u.i, r->u.i)) {
+ ast_log(LOG_WARNING, "overflow\n");
+ }
+ free_value (a);
+ free_value (b);
+ return (r);
+}
+
+static int
+chk_div (FP___TYPE a, FP___TYPE b)
+{
+ /* div by zero has been taken care of before */
+ /* only QUAD_MIN / -1 causes overflow */
+ if (a == QUAD_MIN && b == -1)
+ return 1;
+ /* everything else is OK */
+ return 0;
+}
+
+static struct val *
+op_div (struct val *a, struct val *b)
+{
+ struct val *r;
+
+ if (!to_number (a)) {
+ free_value(a);
+ free_value(b);
+ if( !extra_error_message_supplied )
+ ast_log(LOG_WARNING, "non-numeric argument\n");
+ return make_number(0);
+ } else if (!to_number (b)) {
+ free_value(a);
+ free_value(b);
+ if( !extra_error_message_supplied )
+ ast_log(LOG_WARNING, "non-numeric argument\n");
+ return make_number(INT_MAX);
+ }
+
+ if (b->u.i == 0) {
+ ast_log(LOG_WARNING, "division by zero\n");
+ free_value(a);
+ free_value(b);
+ return make_number(INT_MAX);
+ }
+
+ r = make_number (a->u.i / b->u.i);
+ if (chk_div (a->u.i, b->u.i)) {
+ ast_log(LOG_WARNING, "overflow\n");
+ }
+ free_value (a);
+ free_value (b);
+ return r;
+}
+
+static struct val *
+op_rem (struct val *a, struct val *b)
+{
+ struct val *r;
+
+ if (!to_number (a) || !to_number (b)) {
+ if( !extra_error_message_supplied )
+ ast_log(LOG_WARNING, "non-numeric argument\n");
+ free_value(a);
+ free_value(b);
+ return make_number(0);
+ }
+
+ if (b->u.i == 0) {
+ ast_log(LOG_WARNING, "div by zero\n");
+ free_value(a);
+ return(b);
+ }
+
+ r = make_number (FUNC_FMOD(a->u.i, b->u.i)); /* either fmod or fmodl if FP___TYPE is available */
+ /* chk_rem necessary ??? */
+ free_value (a);
+ free_value (b);
+ return r;
+}
+
+
+static struct val *
+op_colon (struct val *a, struct val *b)
+{
+ regex_t rp;
+ regmatch_t rm[2];
+ char errbuf[256];
+ int eval;
+ struct val *v;
+
+ /* coerce to both arguments to strings */
+ to_string(a);
+ to_string(b);
+ /* strip double quotes from both -- they'll screw up the pattern, and the search string starting at ^ */
+ strip_quotes(a);
+ strip_quotes(b);
+ /* compile regular expression */
+ if ((eval = regcomp (&rp, b->u.s, REG_EXTENDED)) != 0) {
+ regerror (eval, &rp, errbuf, sizeof(errbuf));
+ ast_log(LOG_WARNING,"regcomp() error : %s",errbuf);
+ free_value(a);
+ free_value(b);
+ return make_str("");
+ }
+
+ /* compare string against pattern */
+ /* remember that patterns are anchored to the beginning of the line */
+ if (regexec(&rp, a->u.s, (size_t)2, rm, 0) == 0 && rm[0].rm_so == 0) {
+ if (rm[1].rm_so >= 0) {
+ *(a->u.s + rm[1].rm_eo) = '\0';
+ v = make_str (a->u.s + rm[1].rm_so);
+
+ } else {
+ v = make_number ((FP___TYPE)(rm[0].rm_eo - rm[0].rm_so));
+ }
+ } else {
+ if (rp.re_nsub == 0) {
+ v = make_number ((FP___TYPE)0);
+ } else {
+ v = make_str ("");
+ }
+ }
+
+ /* free arguments and pattern buffer */
+ free_value (a);
+ free_value (b);
+ regfree (&rp);
+
+ return v;
+}
+
+
+static struct val *
+op_eqtilde (struct val *a, struct val *b)
+{
+ regex_t rp;
+ regmatch_t rm[2];
+ char errbuf[256];
+ int eval;
+ struct val *v;
+
+ /* coerce to both arguments to strings */
+ to_string(a);
+ to_string(b);
+ /* strip double quotes from both -- they'll screw up the pattern, and the search string starting at ^ */
+ strip_quotes(a);
+ strip_quotes(b);
+ /* compile regular expression */
+ if ((eval = regcomp (&rp, b->u.s, REG_EXTENDED)) != 0) {
+ regerror (eval, &rp, errbuf, sizeof(errbuf));
+ ast_log(LOG_WARNING,"regcomp() error : %s",errbuf);
+ free_value(a);
+ free_value(b);
+ return make_str("");
+ }
+
+ /* compare string against pattern */
+ /* remember that patterns are anchored to the beginning of the line */
+ if (regexec(&rp, a->u.s, (size_t)2, rm, 0) == 0 ) {
+ if (rm[1].rm_so >= 0) {
+ *(a->u.s + rm[1].rm_eo) = '\0';
+ v = make_str (a->u.s + rm[1].rm_so);
+
+ } else {
+ v = make_number ((FP___TYPE)(rm[0].rm_eo - rm[0].rm_so));
+ }
+ } else {
+ if (rp.re_nsub == 0) {
+ v = make_number ((FP___TYPE)0.0);
+ } else {
+ v = make_str ("");
+ }
+ }
+
+ /* free arguments and pattern buffer */
+ free_value (a);
+ free_value (b);
+ regfree (&rp);
+
+ return v;
+}
diff --git a/trunk/main/ast_expr2f.c b/trunk/main/ast_expr2f.c
new file mode 100644
index 000000000..770b8d1a5
--- /dev/null
+++ b/trunk/main/ast_expr2f.c
@@ -0,0 +1,2515 @@
+#line 2 "ast_expr2f.c"
+
+#line 4 "ast_expr2f.c"
+
+#define YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 33
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if !defined __STDC_VERSION__ || __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types.
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t;
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+#endif /* ! C99 */
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX (4294967295U)
+#endif
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else /* ! __cplusplus */
+
+#if __STDC__
+
+#define YY_USE_CONST
+
+#endif /* __STDC__ */
+#endif /* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index. If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* An opaque pointer. */
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void* yyscan_t;
+#endif
+
+/* For convenience, these vars (plus the bison vars far below)
+ are macros in the reentrant scanner. */
+#define yyin yyg->yyin_r
+#define yyout yyg->yyout_r
+#define yyextra yyg->yyextra_r
+#define yyleng yyg->yyleng_r
+#define yytext yyg->yytext_r
+#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno)
+#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column)
+#define yy_flex_debug yyg->yy_flex_debug_r
+
+int ast_yylex_init (yyscan_t* scanner);
+
+/* Enter a start condition. This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN yyg->yy_start = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state. The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START ((yyg->yy_start - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE ast_yyrestart(yyin ,yyscanner )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#define YY_BUF_SIZE 16384
+#endif
+
+/* The state buf must be large enough to hold one state per character in the main buffer.
+ */
+#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+ #define YY_LESS_LINENO(n)
+
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ *yy_cp = yyg->yy_hold_char; \
+ YY_RESTORE_YY_MORE_OFFSET \
+ yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+ YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+ } \
+ while ( 0 )
+
+#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner )
+
+/* The following is because we cannot portably get our hands on size_t
+ * (without autoconf's help, which isn't available because we want
+ * flex-generated scanners to compile on their own).
+ */
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef unsigned int yy_size_t;
+#endif
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+ {
+ FILE *yy_input_file;
+
+ char *yy_ch_buf; /* input buffer */
+ char *yy_buf_pos; /* current position in input buffer */
+
+ /* Size of input buffer in bytes, not including room for EOB
+ * characters.
+ */
+ yy_size_t yy_buf_size;
+
+ /* Number of characters read into yy_ch_buf, not including EOB
+ * characters.
+ */
+ int yy_n_chars;
+
+ /* Whether we "own" the buffer - i.e., we know we created it,
+ * and can realloc() it to grow it, and should free() it to
+ * delete it.
+ */
+ int yy_is_our_buffer;
+
+ /* Whether this is an "interactive" input source; if so, and
+ * if we're using stdio for input, then we want to use getc()
+ * instead of fread(), to make sure we stop fetching input after
+ * each newline.
+ */
+ int yy_is_interactive;
+
+ /* Whether we're considered to be at the beginning of a line.
+ * If so, '^' rules will be active on the next match, otherwise
+ * not.
+ */
+ int yy_at_bol;
+
+ int yy_bs_lineno; /**< The line count. */
+ int yy_bs_column; /**< The column count. */
+
+ /* Whether to try to fill the input buffer when we reach the
+ * end of it.
+ */
+ int yy_fill_buffer;
+
+ int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+ /* When an EOF's been seen but there's still some text to process
+ * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+ * shouldn't try reading from the input source any more. We might
+ * still have a bunch of tokens to match, though, because of
+ * possible backing-up.
+ *
+ * When we actually see the EOF, we change the status to "new"
+ * (via ast_yyrestart()), so that the user can continue scanning by
+ * just pointing yyin at a new input file.
+ */
+#define YY_BUFFER_EOF_PENDING 2
+
+ };
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \
+ ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \
+ : NULL)
+
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top]
+
+void ast_yyrestart (FILE *input_file ,yyscan_t yyscanner );
+void ast_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
+YY_BUFFER_STATE ast_yy_create_buffer (FILE *file,int size ,yyscan_t yyscanner );
+void ast_yy_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
+void ast_yy_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
+void ast_yypush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
+void ast_yypop_buffer_state (yyscan_t yyscanner );
+
+static void ast_yyensure_buffer_stack (yyscan_t yyscanner );
+static void ast_yy_load_buffer_state (yyscan_t yyscanner );
+static void ast_yy_init_buffer (YY_BUFFER_STATE b,FILE *file ,yyscan_t yyscanner );
+
+#define YY_FLUSH_BUFFER ast_yy_flush_buffer(YY_CURRENT_BUFFER ,yyscanner)
+
+YY_BUFFER_STATE ast_yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner );
+YY_BUFFER_STATE ast_yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner );
+YY_BUFFER_STATE ast_yy_scan_bytes (yyconst char *bytes,int len ,yyscan_t yyscanner );
+
+void *ast_yyalloc (yy_size_t ,yyscan_t yyscanner );
+void *ast_yyrealloc (void *,yy_size_t ,yyscan_t yyscanner );
+void ast_yyfree (void * ,yyscan_t yyscanner );
+
+#define yy_new_buffer ast_yy_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){ \
+ ast_yyensure_buffer_stack (yyscanner); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ ast_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+ }
+
+#define yy_set_bol(at_bol) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){\
+ ast_yyensure_buffer_stack (yyscanner); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ ast_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+ }
+
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* Begin user sect3 */
+
+#define ast_yywrap(n) 1
+#define YY_SKIP_YYWRAP
+
+typedef unsigned char YY_CHAR;
+
+typedef int yy_state_type;
+
+#define yytext_ptr yytext_r
+
+static yy_state_type yy_get_previous_state (yyscan_t yyscanner );
+static yy_state_type yy_try_NUL_trans (yy_state_type current_state ,yyscan_t yyscanner);
+static int yy_get_next_buffer (yyscan_t yyscanner );
+static void yy_fatal_error (yyconst char msg[] ,yyscan_t yyscanner );
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+ yyg->yytext_ptr = yy_bp; \
+ yyg->yytext_ptr -= yyg->yy_more_len; \
+ yyleng = (size_t) (yy_cp - yyg->yytext_ptr); \
+ yyg->yy_hold_char = *yy_cp; \
+ *yy_cp = '\0'; \
+ yyg->yy_c_buf_p = yy_cp;
+
+#define YY_NUM_RULES 36
+#define YY_END_OF_BUFFER 37
+/* This struct is not used in this scanner,
+ but its presence is necessary. */
+struct yy_trans_info
+ {
+ flex_int32_t yy_verify;
+ flex_int32_t yy_nxt;
+ };
+static yyconst flex_int16_t yy_accept[58] =
+ { 0,
+ 0, 0, 0, 0, 33, 33, 37, 36, 26, 28,
+ 20, 36, 30, 30, 18, 2, 23, 24, 16, 13,
+ 14, 15, 17, 29, 21, 9, 3, 8, 19, 1,
+ 36, 32, 31, 33, 34, 34, 12, 0, 27, 30,
+ 25, 5, 30, 29, 22, 11, 6, 7, 10, 4,
+ 0, 32, 31, 33, 35, 29, 0
+ } ;
+
+static yyconst flex_int32_t yy_ec[256] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 3,
+ 1, 1, 2, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 2, 4, 5, 6, 7, 8, 9, 6, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 19, 6, 20,
+ 21, 22, 23, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 1, 6, 1, 6, 6, 1, 6, 6, 6, 6,
+
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 24, 25, 26, 27, 1, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+
+ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28
+ } ;
+
+static yyconst flex_int32_t yy_meta[29] =
+ { 0,
+ 1, 2, 2, 2, 1, 3, 4, 2, 2, 2,
+ 2, 2, 2, 1, 2, 3, 2, 3, 2, 2,
+ 2, 2, 2, 1, 2, 1, 1, 3
+ } ;
+
+static yyconst flex_int16_t yy_base[64] =
+ { 0,
+ 0, 0, 5, 6, 32, 60, 64, 110, 110, 110,
+ 42, 57, 0, 33, 110, 46, 110, 110, 110, 110,
+ 110, 110, 110, 18, 35, 32, 14, 31, 110, 26,
+ 16, 110, 110, 0, 110, 25, 110, 42, 110, 0,
+ 110, 110, 26, 0, 110, 110, 110, 110, 110, 110,
+ 19, 110, 110, 0, 110, 0, 110, 88, 92, 96,
+ 98, 102, 106
+ } ;
+
+static yyconst flex_int16_t yy_def[64] =
+ { 0,
+ 57, 1, 58, 58, 59, 59, 57, 57, 57, 57,
+ 57, 60, 61, 61, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 61, 57, 57, 57, 57, 57, 57,
+ 62, 57, 57, 63, 57, 57, 57, 60, 57, 61,
+ 57, 57, 61, 24, 57, 57, 57, 57, 57, 57,
+ 62, 57, 57, 63, 57, 43, 0, 57, 57, 57,
+ 57, 57, 57
+ } ;
+
+static yyconst flex_int16_t yy_nxt[139] =
+ { 0,
+ 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
+ 18, 19, 20, 21, 22, 13, 23, 24, 25, 26,
+ 27, 28, 29, 8, 30, 8, 8, 13, 32, 32,
+ 33, 33, 34, 43, 47, 44, 34, 34, 36, 52,
+ 48, 53, 52, 56, 53, 34, 39, 34, 55, 34,
+ 50, 49, 46, 45, 42, 34, 41, 34, 34, 34,
+ 34, 39, 37, 57, 34, 34, 36, 57, 57, 57,
+ 57, 57, 57, 34, 57, 34, 57, 34, 57, 57,
+ 57, 57, 57, 34, 57, 34, 34, 34, 31, 31,
+ 31, 31, 35, 35, 35, 35, 38, 38, 38, 38,
+
+ 40, 40, 51, 51, 51, 51, 54, 57, 54, 7,
+ 57, 57, 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57
+ } ;
+
+static yyconst flex_int16_t yy_chk[139] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 3, 4,
+ 3, 4, 5, 24, 27, 24, 5, 5, 5, 31,
+ 27, 31, 51, 43, 51, 5, 38, 5, 36, 5,
+ 30, 28, 26, 25, 16, 5, 14, 5, 5, 5,
+ 6, 12, 11, 7, 6, 6, 6, 0, 0, 0,
+ 0, 0, 0, 6, 0, 6, 0, 6, 0, 0,
+ 0, 0, 0, 6, 0, 6, 6, 6, 58, 58,
+ 58, 58, 59, 59, 59, 59, 60, 60, 60, 60,
+
+ 61, 61, 62, 62, 62, 62, 63, 0, 63, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57
+ } ;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() (yyg->yy_more_flag = 1)
+#define YY_MORE_ADJ yyg->yy_more_len
+#define YY_RESTORE_YY_MORE_OFFSET
+#line 1 "ast_expr2.fl"
+#line 2 "ast_expr2.fl"
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006, 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.
+ */
+
+/*! \file
+ *
+ * \brief Dialplan Expression Lexical Scanner
+ */
+
+#include "asterisk.h"
+
+#include <sys/types.h>
+#include <stdio.h>
+
+#ifndef STANDALONE
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+#else
+#ifndef __USE_ISOC99
+#define __USE_ISOC99 1
+#endif
+#endif
+
+#ifdef __USE_ISOC99
+#define FP___PRINTF "%.18Lg"
+#define FP___FMOD fmodl
+#define FP___STRTOD strtold
+#define FP___TYPE long double
+#else
+#define FP___PRINTF "%.16g"
+#define FP___FMOD fmod
+#define FP___STRTOD strtod
+#define FP___TYPE double
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <locale.h>
+#include <ctype.h>
+#if !defined(SOLARIS) && !defined(__CYGWIN__)
+/* #include <err.h> */
+#else
+#define quad_t int64_t
+#endif
+#include <errno.h>
+#include <regex.h>
+#include <limits.h>
+
+#include "asterisk/ast_expr.h"
+#include "asterisk/logger.h"
+#ifndef STANDALONE
+#include "asterisk/strings.h"
+#include "asterisk/channel.h"
+#endif
+
+enum valtype {
+ AST_EXPR_number, AST_EXPR_numeric_string, AST_EXPR_string
+} ;
+
+struct val {
+ enum valtype type;
+ union {
+ char *s;
+ FP___TYPE i; /* long double or just double if it's a bad day */
+ } u;
+} ;
+
+#include "ast_expr2.h" /* the o/p of the bison on ast_expr2.y */
+
+#define SET_COLUMNS do { \
+ yylloc_param->first_column = (int)(yyg->yytext_r - YY_CURRENT_BUFFER_LVALUE->yy_ch_buf); \
+ yylloc_param->last_column += yyleng - 1; \
+ yylloc_param->first_line = yylloc_param->last_line = 1; \
+ } while (0)
+
+#define SET_STRING do { \
+ yylval_param->val = calloc(1, sizeof(struct val)); \
+ yylval_param->val->type = AST_EXPR_string; \
+ yylval_param->val->u.s = strdup(yytext); \
+ } while (0)
+
+#define SET_NUMERIC_STRING do { \
+ yylval_param->val = calloc(1, sizeof(struct val)); \
+ yylval_param->val->type = AST_EXPR_numeric_string; \
+ yylval_param->val->u.s = strdup(yytext); \
+ } while (0)
+
+struct parse_io
+{
+ char *string;
+ struct val *val;
+ yyscan_t scanner;
+ struct ast_channel *chan;
+};
+
+void ast_yyset_column(int column_no, yyscan_t yyscanner);
+int ast_yyget_column(yyscan_t yyscanner);
+static int curlycount = 0;
+static char *expr2_token_subst(const char *mess);
+
+#line 600 "ast_expr2f.c"
+
+#define INITIAL 0
+#define var 1
+#define trail 2
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+/* Holds the entire state of the reentrant scanner. */
+struct yyguts_t
+ {
+
+ /* User-defined. Not touched by flex. */
+ YY_EXTRA_TYPE yyextra_r;
+
+ /* The rest are the same as the globals declared in the non-reentrant scanner. */
+ FILE *yyin_r, *yyout_r;
+ size_t yy_buffer_stack_top; /**< index of top of stack. */
+ size_t yy_buffer_stack_max; /**< capacity of stack. */
+ YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */
+ char yy_hold_char;
+ int yy_n_chars;
+ int yyleng_r;
+ char *yy_c_buf_p;
+ int yy_init;
+ int yy_start;
+ int yy_did_buffer_switch_on_eof;
+ int yy_start_stack_ptr;
+ int yy_start_stack_depth;
+ int *yy_start_stack;
+ yy_state_type yy_last_accepting_state;
+ char* yy_last_accepting_cpos;
+
+ int yylineno_r;
+ int yy_flex_debug_r;
+
+ char *yytext_r;
+ int yy_more_flag;
+ int yy_more_len;
+
+ YYSTYPE * yylval_r;
+
+ YYLTYPE * yylloc_r;
+
+ }; /* end struct yyguts_t */
+
+static int yy_init_globals (yyscan_t yyscanner );
+
+ /* This must go here because YYSTYPE and YYLTYPE are included
+ * from bison output in section 1.*/
+ # define yylval yyg->yylval_r
+
+ # define yylloc yyg->yylloc_r
+
+/* Accessor methods to globals.
+ These are made visible to non-reentrant scanners for convenience. */
+
+int ast_yylex_destroy (yyscan_t yyscanner );
+
+int ast_yyget_debug (yyscan_t yyscanner );
+
+void ast_yyset_debug (int debug_flag ,yyscan_t yyscanner );
+
+YY_EXTRA_TYPE ast_yyget_extra (yyscan_t yyscanner );
+
+void ast_yyset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner );
+
+FILE *ast_yyget_in (yyscan_t yyscanner );
+
+void ast_yyset_in (FILE * in_str ,yyscan_t yyscanner );
+
+FILE *ast_yyget_out (yyscan_t yyscanner );
+
+void ast_yyset_out (FILE * out_str ,yyscan_t yyscanner );
+
+int ast_yyget_leng (yyscan_t yyscanner );
+
+char *ast_yyget_text (yyscan_t yyscanner );
+
+int ast_yyget_lineno (yyscan_t yyscanner );
+
+void ast_yyset_lineno (int line_number ,yyscan_t yyscanner );
+
+YYSTYPE * ast_yyget_lval (yyscan_t yyscanner );
+
+void ast_yyset_lval (YYSTYPE * yylval_param ,yyscan_t yyscanner );
+
+ YYLTYPE *ast_yyget_lloc (yyscan_t yyscanner );
+
+ void ast_yyset_lloc (YYLTYPE * yylloc_param ,yyscan_t yyscanner );
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int ast_yywrap (yyscan_t yyscanner );
+#else
+extern int ast_yywrap (yyscan_t yyscanner );
+#endif
+#endif
+
+ static void yyunput (int c,char *buf_ptr ,yyscan_t yyscanner);
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner);
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner);
+#endif
+
+#ifndef YY_NO_INPUT
+
+#ifdef __cplusplus
+static int yyinput (yyscan_t yyscanner );
+#else
+static int input (yyscan_t yyscanner );
+#endif
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO (void) fwrite( yytext, yyleng, 1, yyout )
+#endif
+
+/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
+ { \
+ int c = '*'; \
+ size_t n; \
+ for ( n = 0; n < max_size && \
+ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
+ buf[n] = (char) c; \
+ if ( c == '\n' ) \
+ buf[n++] = (char) c; \
+ if ( c == EOF && ferror( yyin ) ) \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ result = n; \
+ } \
+ else \
+ { \
+ errno=0; \
+ while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \
+ { \
+ if( errno != EINTR) \
+ { \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ break; \
+ } \
+ errno=0; \
+ clearerr(yyin); \
+ } \
+ }\
+\
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner)
+#endif
+
+/* end tables serialization structures and prototypes */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int ast_yylex (YYSTYPE * yylval_param,YYLTYPE * yylloc_param ,yyscan_t yyscanner);
+
+#define YY_DECL int ast_yylex (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner)
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+ YY_USER_ACTION
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp, *yy_bp;
+ register int yy_act;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+#line 127 "ast_expr2.fl"
+
+
+#line 840 "ast_expr2f.c"
+
+ yylval = yylval_param;
+
+ yylloc = yylloc_param;
+
+ if ( !yyg->yy_init )
+ {
+ yyg->yy_init = 1;
+
+#ifdef YY_USER_INIT
+ YY_USER_INIT;
+#endif
+
+ if ( ! yyg->yy_start )
+ yyg->yy_start = 1; /* first start state */
+
+ if ( ! yyin )
+ yyin = stdin;
+
+ if ( ! yyout )
+ yyout = stdout;
+
+ if ( ! YY_CURRENT_BUFFER ) {
+ ast_yyensure_buffer_stack (yyscanner);
+ YY_CURRENT_BUFFER_LVALUE =
+ ast_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner);
+ }
+
+ ast_yy_load_buffer_state(yyscanner );
+ }
+
+ while ( 1 ) /* loops until end-of-file is reached */
+ {
+ yyg->yy_more_len = 0;
+ if ( yyg->yy_more_flag )
+ {
+ yyg->yy_more_len = yyg->yy_c_buf_p - yyg->yytext_ptr;
+ yyg->yy_more_flag = 0;
+ }
+ yy_cp = yyg->yy_c_buf_p;
+
+ /* Support of yytext. */
+ *yy_cp = yyg->yy_hold_char;
+
+ /* yy_bp points to the position in yy_ch_buf of the start of
+ * the current run.
+ */
+ yy_bp = yy_cp;
+
+ yy_current_state = yyg->yy_start;
+yy_match:
+ do
+ {
+ register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
+ if ( yy_accept[yy_current_state] )
+ {
+ yyg->yy_last_accepting_state = yy_current_state;
+ yyg->yy_last_accepting_cpos = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 58 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ ++yy_cp;
+ }
+ while ( yy_current_state != 57 );
+ yy_cp = yyg->yy_last_accepting_cpos;
+ yy_current_state = yyg->yy_last_accepting_state;
+
+yy_find_action:
+ yy_act = yy_accept[yy_current_state];
+
+ YY_DO_BEFORE_ACTION;
+
+do_action: /* This label is used only to access EOF actions. */
+
+ switch ( yy_act )
+ { /* beginning of action switch */
+ case 0: /* must back up */
+ /* undo the effects of YY_DO_BEFORE_ACTION */
+ *yy_cp = yyg->yy_hold_char;
+ yy_cp = yyg->yy_last_accepting_cpos;
+ yy_current_state = yyg->yy_last_accepting_state;
+ goto yy_find_action;
+
+case 1:
+YY_RULE_SETUP
+#line 129 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_OR;}
+ YY_BREAK
+case 2:
+YY_RULE_SETUP
+#line 130 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_AND;}
+ YY_BREAK
+case 3:
+YY_RULE_SETUP
+#line 131 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_EQ;}
+ YY_BREAK
+case 4:
+YY_RULE_SETUP
+#line 132 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_OR;}
+ YY_BREAK
+case 5:
+YY_RULE_SETUP
+#line 133 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_AND;}
+ YY_BREAK
+case 6:
+YY_RULE_SETUP
+#line 134 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_EQ;}
+ YY_BREAK
+case 7:
+YY_RULE_SETUP
+#line 135 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_EQTILDE;}
+ YY_BREAK
+case 8:
+YY_RULE_SETUP
+#line 136 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_GT;}
+ YY_BREAK
+case 9:
+YY_RULE_SETUP
+#line 137 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_LT;}
+ YY_BREAK
+case 10:
+YY_RULE_SETUP
+#line 138 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_GE;}
+ YY_BREAK
+case 11:
+YY_RULE_SETUP
+#line 139 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_LE;}
+ YY_BREAK
+case 12:
+YY_RULE_SETUP
+#line 140 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_NE;}
+ YY_BREAK
+case 13:
+YY_RULE_SETUP
+#line 141 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_PLUS;}
+ YY_BREAK
+case 14:
+YY_RULE_SETUP
+#line 142 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_COMMA;}
+ YY_BREAK
+case 15:
+YY_RULE_SETUP
+#line 143 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_MINUS;}
+ YY_BREAK
+case 16:
+YY_RULE_SETUP
+#line 144 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_MULT;}
+ YY_BREAK
+case 17:
+YY_RULE_SETUP
+#line 145 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_DIV;}
+ YY_BREAK
+case 18:
+YY_RULE_SETUP
+#line 146 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_MOD;}
+ YY_BREAK
+case 19:
+YY_RULE_SETUP
+#line 147 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_COND;}
+ YY_BREAK
+case 20:
+YY_RULE_SETUP
+#line 148 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_COMPL;}
+ YY_BREAK
+case 21:
+YY_RULE_SETUP
+#line 149 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_COLON;}
+ YY_BREAK
+case 22:
+YY_RULE_SETUP
+#line 150 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_COLONCOLON;}
+ YY_BREAK
+case 23:
+YY_RULE_SETUP
+#line 151 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_LP;}
+ YY_BREAK
+case 24:
+YY_RULE_SETUP
+#line 152 "ast_expr2.fl"
+{ SET_COLUMNS; SET_STRING; return TOK_RP;}
+ YY_BREAK
+case 25:
+YY_RULE_SETUP
+#line 153 "ast_expr2.fl"
+{
+ /* gather the contents of ${} expressions, with trailing stuff,
+ * into a single TOKEN.
+ * They are much more complex now than they used to be
+ */
+ curlycount = 0;
+ BEGIN(var);
+ yymore();
+ }
+ YY_BREAK
+case 26:
+YY_RULE_SETUP
+#line 163 "ast_expr2.fl"
+{}
+ YY_BREAK
+case 27:
+/* rule 27 can match eol */
+YY_RULE_SETUP
+#line 164 "ast_expr2.fl"
+{SET_COLUMNS; SET_STRING; return TOKEN;}
+ YY_BREAK
+case 28:
+/* rule 28 can match eol */
+YY_RULE_SETUP
+#line 166 "ast_expr2.fl"
+{/* what to do with eol */}
+ YY_BREAK
+case 29:
+YY_RULE_SETUP
+#line 167 "ast_expr2.fl"
+{
+ SET_COLUMNS;
+ /* the original behavior of the expression parser was
+ * to bring in numbers as a numeric string
+ */
+ SET_NUMERIC_STRING;
+ return TOKEN;
+ }
+ YY_BREAK
+case 30:
+YY_RULE_SETUP
+#line 176 "ast_expr2.fl"
+{
+ SET_COLUMNS;
+ SET_STRING;
+ return TOKEN;
+ }
+ YY_BREAK
+case 31:
+/* rule 31 can match eol */
+YY_RULE_SETUP
+#line 183 "ast_expr2.fl"
+{
+ curlycount--;
+ if (curlycount < 0) {
+ BEGIN(trail);
+ yymore();
+ } else {
+ yymore();
+ }
+ }
+ YY_BREAK
+case 32:
+/* rule 32 can match eol */
+YY_RULE_SETUP
+#line 193 "ast_expr2.fl"
+{
+ curlycount++;
+ yymore();
+ }
+ YY_BREAK
+case 33:
+YY_RULE_SETUP
+#line 199 "ast_expr2.fl"
+{
+ BEGIN(0);
+ SET_COLUMNS;
+ SET_STRING;
+ return TOKEN;
+ }
+ YY_BREAK
+case 34:
+/* rule 34 can match eol */
+YY_RULE_SETUP
+#line 206 "ast_expr2.fl"
+{
+ char c = yytext[yyleng-1];
+ BEGIN(0);
+ unput(c);
+ SET_COLUMNS;
+ SET_STRING;
+ return TOKEN;
+ }
+ YY_BREAK
+case 35:
+YY_RULE_SETUP
+#line 215 "ast_expr2.fl"
+{
+ curlycount = 0;
+ BEGIN(var);
+ yymore();
+ }
+ YY_BREAK
+case YY_STATE_EOF(trail):
+#line 221 "ast_expr2.fl"
+{
+ BEGIN(0);
+ SET_COLUMNS;
+ SET_STRING;
+ return TOKEN;
+ /*actually, if an expr is only a variable ref, this could happen a LOT */
+ }
+ YY_BREAK
+case 36:
+YY_RULE_SETUP
+#line 229 "ast_expr2.fl"
+ECHO;
+ YY_BREAK
+#line 1170 "ast_expr2f.c"
+case YY_STATE_EOF(INITIAL):
+case YY_STATE_EOF(var):
+ yyterminate();
+
+ case YY_END_OF_BUFFER:
+ {
+ /* Amount of text matched not including the EOB char. */
+ int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1;
+
+ /* Undo the effects of YY_DO_BEFORE_ACTION. */
+ *yy_cp = yyg->yy_hold_char;
+ YY_RESTORE_YY_MORE_OFFSET
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+ {
+ /* We're scanning a new file or input source. It's
+ * possible that this happened because the user
+ * just pointed yyin at a new source and called
+ * ast_yylex(). If so, then we have to assure
+ * consistency between YY_CURRENT_BUFFER and our
+ * globals. Here is the right place to do so, because
+ * this is the first action (other than possibly a
+ * back-up) that will match for the new input source.
+ */
+ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+ }
+
+ /* Note that here we test for yy_c_buf_p "<=" to the position
+ * of the first EOB in the buffer, since yy_c_buf_p will
+ * already have been incremented past the NUL character
+ * (since all states make transitions on EOB to the
+ * end-of-buffer state). Contrast this with the test
+ * in input().
+ */
+ if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] )
+ { /* This was really a NUL. */
+ yy_state_type yy_next_state;
+
+ yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( yyscanner );
+
+ /* Okay, we're now positioned to make the NUL
+ * transition. We couldn't have
+ * yy_get_previous_state() go ahead and do it
+ * for us because it doesn't know how to deal
+ * with the possibility of jamming (and we don't
+ * want to build jamming into it because then it
+ * will run more slowly).
+ */
+
+ yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner);
+
+ yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+
+ if ( yy_next_state )
+ {
+ /* Consume the NUL. */
+ yy_cp = ++yyg->yy_c_buf_p;
+ yy_current_state = yy_next_state;
+ goto yy_match;
+ }
+
+ else
+ {
+ yy_cp = yyg->yy_last_accepting_cpos;
+ yy_current_state = yyg->yy_last_accepting_state;
+ goto yy_find_action;
+ }
+ }
+
+ else switch ( yy_get_next_buffer( yyscanner ) )
+ {
+ case EOB_ACT_END_OF_FILE:
+ {
+ yyg->yy_did_buffer_switch_on_eof = 0;
+
+ if ( ast_yywrap(yyscanner ) )
+ {
+ /* Note: because we've taken care in
+ * yy_get_next_buffer() to have set up
+ * yytext, we can now set up
+ * yy_c_buf_p so that if some total
+ * hoser (like flex itself) wants to
+ * call the scanner after we return the
+ * YY_NULL, it'll still work - another
+ * YY_NULL will get returned.
+ */
+ yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ;
+
+ yy_act = YY_STATE_EOF(YY_START);
+ goto do_action;
+ }
+
+ else
+ {
+ if ( ! yyg->yy_did_buffer_switch_on_eof )
+ YY_NEW_FILE;
+ }
+ break;
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ yyg->yy_c_buf_p =
+ yyg->yytext_ptr + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( yyscanner );
+
+ yy_cp = yyg->yy_c_buf_p;
+ yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+ goto yy_match;
+
+ case EOB_ACT_LAST_MATCH:
+ yyg->yy_c_buf_p =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars];
+
+ yy_current_state = yy_get_previous_state( yyscanner );
+
+ yy_cp = yyg->yy_c_buf_p;
+ yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+ goto yy_find_action;
+ }
+ break;
+ }
+
+ default:
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--no action found" );
+ } /* end of action switch */
+ } /* end of scanning one token */
+} /* end of ast_yylex */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ * EOB_ACT_LAST_MATCH -
+ * EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ * EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+ register char *source = yyg->yytext_ptr;
+ register int number_to_move, i;
+ int ret_val;
+
+ if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] )
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--end of buffer missed" );
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+ { /* Don't try to fill the buffer, so this is an EOF. */
+ if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 )
+ {
+ /* We matched a single character, the EOB, so
+ * treat this as a final EOF.
+ */
+ return EOB_ACT_END_OF_FILE;
+ }
+
+ else
+ {
+ /* We matched some text prior to the EOB, first
+ * process it.
+ */
+ return EOB_ACT_LAST_MATCH;
+ }
+ }
+
+ /* Try to read more data. */
+
+ /* First move last chars to start of buffer. */
+ number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr) - 1;
+
+ for ( i = 0; i < number_to_move; ++i )
+ *(dest++) = *(source++);
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+ /* don't do the read, it's not guaranteed to return an EOF,
+ * just force an EOF
+ */
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0;
+
+ else
+ {
+ int num_to_read =
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+ while ( num_to_read <= 0 )
+ { /* Not enough room in the buffer - grow it. */
+
+ /* just a shorter name for the current buffer */
+ YY_BUFFER_STATE b = YY_CURRENT_BUFFER;
+
+ int yy_c_buf_p_offset =
+ (int) (yyg->yy_c_buf_p - b->yy_ch_buf);
+
+ if ( b->yy_is_our_buffer )
+ {
+ int new_size = b->yy_buf_size * 2;
+
+ if ( new_size <= 0 )
+ b->yy_buf_size += b->yy_buf_size / 8;
+ else
+ b->yy_buf_size *= 2;
+
+ b->yy_ch_buf = (char *)
+ /* Include room in for 2 EOB chars. */
+ ast_yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ,yyscanner );
+ }
+ else
+ /* Can't grow it, we don't own it. */
+ b->yy_ch_buf = 0;
+
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR(
+ "fatal error - scanner input buffer overflow" );
+
+ yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+ num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+ number_to_move - 1;
+
+ }
+
+ if ( num_to_read > YY_READ_BUF_SIZE )
+ num_to_read = YY_READ_BUF_SIZE;
+
+ /* Read in more data. */
+ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+ yyg->yy_n_chars, num_to_read );
+
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+ }
+
+ if ( yyg->yy_n_chars == 0 )
+ {
+ if ( number_to_move == YY_MORE_ADJ )
+ {
+ ret_val = EOB_ACT_END_OF_FILE;
+ ast_yyrestart(yyin ,yyscanner);
+ }
+
+ else
+ {
+ ret_val = EOB_ACT_LAST_MATCH;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+ YY_BUFFER_EOF_PENDING;
+ }
+ }
+
+ else
+ ret_val = EOB_ACT_CONTINUE_SCAN;
+
+ yyg->yy_n_chars += number_to_move;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR;
+
+ yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+ return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+ static yy_state_type yy_get_previous_state (yyscan_t yyscanner)
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ yy_current_state = yyg->yy_start;
+
+ for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp )
+ {
+ register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+ if ( yy_accept[yy_current_state] )
+ {
+ yyg->yy_last_accepting_state = yy_current_state;
+ yyg->yy_last_accepting_cpos = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 58 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ }
+
+ return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ * next_state = yy_try_NUL_trans( current_state );
+ */
+ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner)
+{
+ register int yy_is_jam;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */
+ register char *yy_cp = yyg->yy_c_buf_p;
+
+ register YY_CHAR yy_c = 1;
+ if ( yy_accept[yy_current_state] )
+ {
+ yyg->yy_last_accepting_state = yy_current_state;
+ yyg->yy_last_accepting_cpos = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 58 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ yy_is_jam = (yy_current_state == 57);
+
+ return yy_is_jam ? 0 : yy_current_state;
+}
+
+ static void yyunput (int c, register char * yy_bp , yyscan_t yyscanner)
+{
+ register char *yy_cp;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ yy_cp = yyg->yy_c_buf_p;
+
+ /* undo effects of setting up yytext */
+ *yy_cp = yyg->yy_hold_char;
+
+ if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+ { /* need to shift things up to make room */
+ /* +2 for EOB chars. */
+ register int number_to_move = yyg->yy_n_chars + 2;
+ register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2];
+ register char *source =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move];
+
+ while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+ *--dest = *--source;
+
+ yy_cp += (int) (dest - source);
+ yy_bp += (int) (dest - source);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars =
+ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_buf_size;
+
+ if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+ YY_FATAL_ERROR( "flex scanner push-back overflow" );
+ }
+
+ *--yy_cp = (char) c;
+
+ yyg->yytext_ptr = yy_bp;
+ yyg->yy_hold_char = *yy_cp;
+ yyg->yy_c_buf_p = yy_cp;
+}
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+ static int yyinput (yyscan_t yyscanner)
+#else
+ static int input (yyscan_t yyscanner)
+#endif
+
+{
+ int c;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ *yyg->yy_c_buf_p = yyg->yy_hold_char;
+
+ if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR )
+ {
+ /* yy_c_buf_p now points to the character we want to return.
+ * If this occurs *before* the EOB characters, then it's a
+ * valid NUL; if not, then we've hit the end of the buffer.
+ */
+ if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] )
+ /* This was really a NUL. */
+ *yyg->yy_c_buf_p = '\0';
+
+ else
+ { /* need more input */
+ int offset = yyg->yy_c_buf_p - yyg->yytext_ptr;
+ ++yyg->yy_c_buf_p;
+
+ switch ( yy_get_next_buffer( yyscanner ) )
+ {
+ case EOB_ACT_LAST_MATCH:
+ /* This happens because yy_g_n_b()
+ * sees that we've accumulated a
+ * token and flags that we need to
+ * try matching the token before
+ * proceeding. But for input(),
+ * there's no matching to consider.
+ * So convert the EOB_ACT_LAST_MATCH
+ * to EOB_ACT_END_OF_FILE.
+ */
+
+ /* Reset buffer status. */
+ ast_yyrestart(yyin ,yyscanner);
+
+ /*FALLTHROUGH*/
+
+ case EOB_ACT_END_OF_FILE:
+ {
+ if ( ast_yywrap(yyscanner ) )
+ return EOF;
+
+ if ( ! yyg->yy_did_buffer_switch_on_eof )
+ YY_NEW_FILE;
+#ifdef __cplusplus
+ return yyinput(yyscanner);
+#else
+ return input(yyscanner);
+#endif
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ yyg->yy_c_buf_p = yyg->yytext_ptr + offset;
+ break;
+ }
+ }
+ }
+
+ c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */
+ *yyg->yy_c_buf_p = '\0'; /* preserve yytext */
+ yyg->yy_hold_char = *++yyg->yy_c_buf_p;
+
+ return c;
+}
+#endif /* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ * @param yyscanner The scanner object.
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+ void ast_yyrestart (FILE * input_file , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if ( ! YY_CURRENT_BUFFER ){
+ ast_yyensure_buffer_stack (yyscanner);
+ YY_CURRENT_BUFFER_LVALUE =
+ ast_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner);
+ }
+
+ ast_yy_init_buffer(YY_CURRENT_BUFFER,input_file ,yyscanner);
+ ast_yy_load_buffer_state(yyscanner );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ * @param yyscanner The scanner object.
+ */
+ void ast_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* TODO. We should be able to replace this entire function body
+ * with
+ * ast_yypop_buffer_state();
+ * ast_yypush_buffer_state(new_buffer);
+ */
+ ast_yyensure_buffer_stack (yyscanner);
+ if ( YY_CURRENT_BUFFER == new_buffer )
+ return;
+
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *yyg->yy_c_buf_p = yyg->yy_hold_char;
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p;
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+ }
+
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+ ast_yy_load_buffer_state(yyscanner );
+
+ /* We don't actually know whether we did this switch during
+ * EOF (ast_yywrap()) processing, but the only time this flag
+ * is looked at is after ast_yywrap() is called, so it's safe
+ * to go ahead and always set it.
+ */
+ yyg->yy_did_buffer_switch_on_eof = 1;
+}
+
+static void ast_yy_load_buffer_state (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+ yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+ yyg->yy_hold_char = *yyg->yy_c_buf_p;
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ * @param yyscanner The scanner object.
+ * @return the allocated buffer state.
+ */
+ YY_BUFFER_STATE ast_yy_create_buffer (FILE * file, int size , yyscan_t yyscanner)
+{
+ YY_BUFFER_STATE b;
+
+ b = (YY_BUFFER_STATE) ast_yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in ast_yy_create_buffer()" );
+
+ b->yy_buf_size = size;
+
+ /* yy_ch_buf has to be 2 characters longer than the size given because
+ * we need to put in 2 end-of-buffer characters.
+ */
+ b->yy_ch_buf = (char *) ast_yyalloc(b->yy_buf_size + 2 ,yyscanner );
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in ast_yy_create_buffer()" );
+
+ b->yy_is_our_buffer = 1;
+
+ ast_yy_init_buffer(b,file ,yyscanner);
+
+ return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with ast_yy_create_buffer()
+ * @param yyscanner The scanner object.
+ */
+ void ast_yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if ( ! b )
+ return;
+
+ if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+ if ( b->yy_is_our_buffer )
+ ast_yyfree((void *) b->yy_ch_buf ,yyscanner );
+
+ ast_yyfree((void *) b ,yyscanner );
+}
+
+#ifndef __cplusplus
+extern int isatty (int );
+#endif /* __cplusplus */
+
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a ast_yyrestart() or at EOF.
+ */
+ static void ast_yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner)
+
+{
+ int oerrno = errno;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ ast_yy_flush_buffer(b ,yyscanner);
+
+ b->yy_input_file = file;
+ b->yy_fill_buffer = 1;
+
+ /* If b is the current buffer, then ast_yy_init_buffer was _probably_
+ * called from ast_yyrestart() or through yy_get_next_buffer.
+ * In that case, we don't want to reset the lineno or column.
+ */
+ if (b != YY_CURRENT_BUFFER){
+ b->yy_bs_lineno = 1;
+ b->yy_bs_column = 0;
+ }
+
+ b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+
+ errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ * @param yyscanner The scanner object.
+ */
+ void ast_yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ if ( ! b )
+ return;
+
+ b->yy_n_chars = 0;
+
+ /* We always need two end-of-buffer characters. The first causes
+ * a transition to the end-of-buffer state. The second causes
+ * a jam in that state.
+ */
+ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+ b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+ b->yy_buf_pos = &b->yy_ch_buf[0];
+
+ b->yy_at_bol = 1;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ if ( b == YY_CURRENT_BUFFER )
+ ast_yy_load_buffer_state(yyscanner );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ * the current state. This function will allocate the stack
+ * if necessary.
+ * @param new_buffer The new state.
+ * @param yyscanner The scanner object.
+ */
+void ast_yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ if (new_buffer == NULL)
+ return;
+
+ ast_yyensure_buffer_stack(yyscanner);
+
+ /* This block is copied from ast_yy_switch_to_buffer. */
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *yyg->yy_c_buf_p = yyg->yy_hold_char;
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p;
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+ }
+
+ /* Only push if top exists. Otherwise, replace top. */
+ if (YY_CURRENT_BUFFER)
+ yyg->yy_buffer_stack_top++;
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+ /* copied from ast_yy_switch_to_buffer. */
+ ast_yy_load_buffer_state(yyscanner );
+ yyg->yy_did_buffer_switch_on_eof = 1;
+}
+
+/** Removes and deletes the top of the stack, if present.
+ * The next element becomes the new top.
+ * @param yyscanner The scanner object.
+ */
+void ast_yypop_buffer_state (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ if (!YY_CURRENT_BUFFER)
+ return;
+
+ ast_yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner);
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ if (yyg->yy_buffer_stack_top > 0)
+ --yyg->yy_buffer_stack_top;
+
+ if (YY_CURRENT_BUFFER) {
+ ast_yy_load_buffer_state(yyscanner );
+ yyg->yy_did_buffer_switch_on_eof = 1;
+ }
+}
+
+/* Allocates the stack if it does not exist.
+ * Guarantees space for at least one push.
+ */
+static void ast_yyensure_buffer_stack (yyscan_t yyscanner)
+{
+ int num_to_alloc;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if (!yyg->yy_buffer_stack) {
+
+ /* First allocation is just for 2 elements, since we don't know if this
+ * scanner will even need a stack. We use 2 instead of 1 to avoid an
+ * immediate realloc on the next call.
+ */
+ num_to_alloc = 1;
+ yyg->yy_buffer_stack = (struct yy_buffer_state**)ast_yyalloc
+ (num_to_alloc * sizeof(struct yy_buffer_state*)
+ , yyscanner);
+
+ memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+
+ yyg->yy_buffer_stack_max = num_to_alloc;
+ yyg->yy_buffer_stack_top = 0;
+ return;
+ }
+
+ if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){
+
+ /* Increase the buffer to prepare for a possible push. */
+ int grow_size = 8 /* arbitrary grow size */;
+
+ num_to_alloc = yyg->yy_buffer_stack_max + grow_size;
+ yyg->yy_buffer_stack = (struct yy_buffer_state**)ast_yyrealloc
+ (yyg->yy_buffer_stack,
+ num_to_alloc * sizeof(struct yy_buffer_state*)
+ , yyscanner);
+
+ /* zero only the new slots.*/
+ memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*));
+ yyg->yy_buffer_stack_max = num_to_alloc;
+ }
+}
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE ast_yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner)
+{
+ YY_BUFFER_STATE b;
+
+ if ( size < 2 ||
+ base[size-2] != YY_END_OF_BUFFER_CHAR ||
+ base[size-1] != YY_END_OF_BUFFER_CHAR )
+ /* They forgot to leave room for the EOB's. */
+ return 0;
+
+ b = (YY_BUFFER_STATE) ast_yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in ast_yy_scan_buffer()" );
+
+ b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */
+ b->yy_buf_pos = b->yy_ch_buf = base;
+ b->yy_is_our_buffer = 0;
+ b->yy_input_file = 0;
+ b->yy_n_chars = b->yy_buf_size;
+ b->yy_is_interactive = 0;
+ b->yy_at_bol = 1;
+ b->yy_fill_buffer = 0;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ ast_yy_switch_to_buffer(b ,yyscanner );
+
+ return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to ast_yylex() will
+ * scan from a @e copy of @a str.
+ * @param str a NUL-terminated string to scan
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ * ast_yy_scan_bytes() instead.
+ */
+YY_BUFFER_STATE ast_yy_scan_string (yyconst char * yystr , yyscan_t yyscanner)
+{
+
+ return ast_yy_scan_bytes(yystr,strlen(yystr) ,yyscanner);
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to ast_yylex() will
+ * scan from a @e copy of @a bytes.
+ * @param bytes the byte buffer to scan
+ * @param len the number of bytes in the buffer pointed to by @a bytes.
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE ast_yy_scan_bytes (yyconst char * yybytes, int _yybytes_len , yyscan_t yyscanner)
+{
+ YY_BUFFER_STATE b;
+ char *buf;
+ yy_size_t n;
+ int i;
+
+ /* Get memory for full buffer, including space for trailing EOB's. */
+ n = _yybytes_len + 2;
+ buf = (char *) ast_yyalloc(n ,yyscanner );
+ if ( ! buf )
+ YY_FATAL_ERROR( "out of dynamic memory in ast_yy_scan_bytes()" );
+
+ for ( i = 0; i < _yybytes_len; ++i )
+ buf[i] = yybytes[i];
+
+ buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
+
+ b = ast_yy_scan_buffer(buf,n ,yyscanner);
+ if ( ! b )
+ YY_FATAL_ERROR( "bad buffer in ast_yy_scan_bytes()" );
+
+ /* It's okay to grow etc. this buffer, and we should throw it
+ * away when we're done.
+ */
+ b->yy_is_our_buffer = 1;
+
+ return b;
+}
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yy_fatal_error (yyconst char* msg , yyscan_t yyscanner)
+{
+ (void) fprintf( stderr, "%s\n", msg );
+ exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ yytext[yyleng] = yyg->yy_hold_char; \
+ yyg->yy_c_buf_p = yytext + yyless_macro_arg; \
+ yyg->yy_hold_char = *yyg->yy_c_buf_p; \
+ *yyg->yy_c_buf_p = '\0'; \
+ yyleng = yyless_macro_arg; \
+ } \
+ while ( 0 )
+
+/* Accessor methods (get/set functions) to struct members. */
+
+/** Get the user-defined data for this scanner.
+ * @param yyscanner The scanner object.
+ */
+YY_EXTRA_TYPE ast_yyget_extra (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyextra;
+}
+
+/** Get the current line number.
+ * @param yyscanner The scanner object.
+ */
+int ast_yyget_lineno (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if (! YY_CURRENT_BUFFER)
+ return 0;
+
+ return yylineno;
+}
+
+/** Get the current column number.
+ * @param yyscanner The scanner object.
+ */
+int ast_yyget_column (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if (! YY_CURRENT_BUFFER)
+ return 0;
+
+ return yycolumn;
+}
+
+/** Get the input stream.
+ * @param yyscanner The scanner object.
+ */
+FILE *ast_yyget_in (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyin;
+}
+
+/** Get the output stream.
+ * @param yyscanner The scanner object.
+ */
+FILE *ast_yyget_out (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyout;
+}
+
+/** Get the length of the current token.
+ * @param yyscanner The scanner object.
+ */
+int ast_yyget_leng (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyleng;
+}
+
+/** Get the current token.
+ * @param yyscanner The scanner object.
+ */
+
+char *ast_yyget_text (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yytext;
+}
+
+/** Set the user-defined data. This data is never touched by the scanner.
+ * @param user_defined The data to be associated with this scanner.
+ * @param yyscanner The scanner object.
+ */
+void ast_yyset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyextra = user_defined ;
+}
+
+/** Set the current line number.
+ * @param line_number
+ * @param yyscanner The scanner object.
+ */
+void ast_yyset_lineno (int line_number , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* lineno is only valid if an input buffer exists. */
+ if (! YY_CURRENT_BUFFER )
+ yy_fatal_error( "ast_yyset_lineno called with no buffer" , yyscanner);
+
+ yylineno = line_number;
+}
+
+/** Set the current column.
+ * @param line_number
+ * @param yyscanner The scanner object.
+ */
+void ast_yyset_column (int column_no , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* column is only valid if an input buffer exists. */
+ if (! YY_CURRENT_BUFFER )
+ yy_fatal_error( "ast_yyset_column called with no buffer" , yyscanner);
+
+ yycolumn = column_no;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param in_str A readable stream.
+ * @param yyscanner The scanner object.
+ * @see ast_yy_switch_to_buffer
+ */
+void ast_yyset_in (FILE * in_str , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyin = in_str ;
+}
+
+void ast_yyset_out (FILE * out_str , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyout = out_str ;
+}
+
+int ast_yyget_debug (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yy_flex_debug;
+}
+
+void ast_yyset_debug (int bdebug , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yy_flex_debug = bdebug ;
+}
+
+/* Accessor methods for yylval and yylloc */
+
+YYSTYPE * ast_yyget_lval (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yylval;
+}
+
+void ast_yyset_lval (YYSTYPE * yylval_param , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yylval = yylval_param;
+}
+
+YYLTYPE *ast_yyget_lloc (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yylloc;
+}
+
+void ast_yyset_lloc (YYLTYPE * yylloc_param , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yylloc = yylloc_param;
+}
+
+/* User-visible API */
+
+/* ast_yylex_init is special because it creates the scanner itself, so it is
+ * the ONLY reentrant function that doesn't take the scanner as the last argument.
+ * That's why we explicitly handle the declaration, instead of using our macros.
+ */
+
+int ast_yylex_init(yyscan_t* ptr_yy_globals)
+
+{
+ if (ptr_yy_globals == NULL){
+ errno = EINVAL;
+ return 1;
+ }
+
+ *ptr_yy_globals = (yyscan_t) ast_yyalloc ( sizeof( struct yyguts_t ), NULL );
+
+ if (*ptr_yy_globals == NULL){
+ errno = ENOMEM;
+ return 1;
+ }
+
+ /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */
+ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t));
+
+ return yy_init_globals ( *ptr_yy_globals );
+}
+
+static int yy_init_globals (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ /* Initialization is the same as for the non-reentrant scanner.
+ * This function is called from ast_yylex_destroy(), so don't allocate here.
+ */
+
+ yyg->yy_buffer_stack = 0;
+ yyg->yy_buffer_stack_top = 0;
+ yyg->yy_buffer_stack_max = 0;
+ yyg->yy_c_buf_p = (char *) 0;
+ yyg->yy_init = 0;
+ yyg->yy_start = 0;
+
+ yyg->yy_start_stack_ptr = 0;
+ yyg->yy_start_stack_depth = 0;
+ yyg->yy_start_stack = NULL;
+
+/* Defined in main.c */
+#ifdef YY_STDINIT
+ yyin = stdin;
+ yyout = stdout;
+#else
+ yyin = (FILE *) 0;
+ yyout = (FILE *) 0;
+#endif
+
+ /* For future reference: Set errno on error, since we are called by
+ * ast_yylex_init()
+ */
+ return 0;
+}
+
+/* ast_yylex_destroy is for both reentrant and non-reentrant scanners. */
+int ast_yylex_destroy (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* Pop the buffer stack, destroying each element. */
+ while(YY_CURRENT_BUFFER){
+ ast_yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ ast_yypop_buffer_state(yyscanner);
+ }
+
+ /* Destroy the stack itself. */
+ ast_yyfree(yyg->yy_buffer_stack ,yyscanner);
+ yyg->yy_buffer_stack = NULL;
+
+ /* Destroy the start condition stack. */
+ ast_yyfree(yyg->yy_start_stack ,yyscanner );
+ yyg->yy_start_stack = NULL;
+
+ /* Reset the globals. This is important in a non-reentrant scanner so the next time
+ * ast_yylex() is called, initialization will occur. */
+ yy_init_globals( yyscanner);
+
+ /* Destroy the main struct (reentrant only). */
+ ast_yyfree ( yyscanner , yyscanner );
+ yyscanner = NULL;
+ return 0;
+}
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, yyconst char * s2, int n , yyscan_t yyscanner)
+{
+ register int i;
+ for ( i = 0; i < n; ++i )
+ s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * s , yyscan_t yyscanner)
+{
+ register int n;
+ for ( n = 0; s[n]; ++n )
+ ;
+
+ return n;
+}
+#endif
+
+void *ast_yyalloc (yy_size_t size , yyscan_t yyscanner)
+{
+ return (void *) malloc( size );
+}
+
+void *ast_yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner)
+{
+ /* The cast to (char *) in the following accommodates both
+ * implementations that use char* generic pointers, and those
+ * that use void* generic pointers. It works with the latter
+ * because both ANSI C and C++ allow castless assignment from
+ * any pointer type to void*, and deal with argument conversions
+ * as though doing an assignment.
+ */
+ return (void *) realloc( (char *) ptr, size );
+}
+
+#define YYTABLES_NAME "yytables"
+
+#line 229 "ast_expr2.fl"
+
+
+
+/* I'm putting the interface routine to the whole parse here in the flexer input file
+ mainly because of all the flexer initialization that has to be done. Shouldn't matter
+ where it is, as long as it's somewhere. I didn't want to define a prototype for the
+ ast_yy_scan_string in the .y file, because then, I'd have to define YY_BUFFER_STATE there...
+ UGH! that would be inappropriate. */
+
+int ast_yyparse(void *); /* need to/should define this prototype for the call to yyparse */
+int ast_yyerror(const char *, YYLTYPE *, struct parse_io *); /* likewise */
+
+void ast_yyfree(void *ptr, yyscan_t yyscanner)
+{
+ if (ptr) /* the normal generated ast_yyfree func just frees its first arg;
+ this get complaints on some systems, as sometimes this
+ arg is a nil ptr! It's usually not fatal, but is irritating! */
+ free( (char *) ptr );
+}
+
+int ast_expr(char *expr, char *buf, int length, struct ast_channel *chan)
+{
+ struct parse_io io;
+ int return_value = 0;
+
+ memset(&io, 0, sizeof(io));
+ io.string = expr; /* to pass to the error routine */
+ io.chan = chan;
+
+ ast_yylex_init(&io.scanner);
+
+ ast_yy_scan_string(expr, io.scanner);
+
+ ast_yyparse ((void *) &io);
+
+ ast_yylex_destroy(io.scanner);
+
+ if (!io.val) {
+ if (length > 1) {
+ strcpy(buf, "0");
+ return_value = 1;
+ }
+ } else {
+ if (io.val->type == AST_EXPR_number) {
+ int res_length;
+
+ res_length = snprintf(buf, length, FP___PRINTF, io.val->u.i);
+ return_value = (res_length <= length) ? res_length : length;
+ } else {
+ if (io.val->u.s)
+#if defined(STANDALONE) || defined(LOW_MEMORY) || defined(STANDALONE_AEL)
+ strncpy(buf, io.val->u.s, length - 1);
+#else /* !STANDALONE && !LOW_MEMORY */
+ ast_copy_string(buf, io.val->u.s, length);
+#endif /* STANDALONE || LOW_MEMORY */
+ else
+ buf[0] = 0;
+ return_value = strlen(buf);
+ if (io.val->u.s)
+ free(io.val->u.s);
+ }
+ free(io.val);
+ }
+ return return_value;
+}
+
+
+char extra_error_message[4095];
+int extra_error_message_supplied = 0;
+void ast_expr_register_extra_error_info(char *message);
+void ast_expr_clear_extra_error_info(void);
+
+void ast_expr_register_extra_error_info(char *message)
+{
+ extra_error_message_supplied=1;
+ strcpy(extra_error_message, message);
+}
+
+void ast_expr_clear_extra_error_info(void)
+{
+ extra_error_message_supplied=0;
+ extra_error_message[0] = 0;
+}
+
+static char *expr2_token_equivs1[] =
+{
+ "TOKEN",
+ "TOK_COND",
+ "TOK_COLONCOLON",
+ "TOK_OR",
+ "TOK_AND",
+ "TOK_EQ",
+ "TOK_GT",
+ "TOK_LT",
+ "TOK_GE",
+ "TOK_LE",
+ "TOK_NE",
+ "TOK_PLUS",
+ "TOK_MINUS",
+ "TOK_MULT",
+ "TOK_DIV",
+ "TOK_MOD",
+ "TOK_COMPL",
+ "TOK_COLON",
+ "TOK_EQTILDE",
+ "TOK_COMMA",
+ "TOK_RP",
+ "TOK_LP"
+};
+
+static char *expr2_token_equivs2[] =
+{
+ "<token>",
+ "?",
+ "::",
+ "|",
+ "&",
+ "=",
+ ">",
+ "<",
+ ">=",
+ "<=",
+ "!=",
+ "+",
+ "-",
+ "*",
+ "/",
+ "%",
+ "!",
+ ":",
+ "=~",
+ ",",
+ ")",
+ "("
+};
+
+
+static char *expr2_token_subst(const char *mess)
+{
+ /* calc a length, malloc, fill, and return; yyerror had better free it! */
+ int len=0,i;
+ const char *p;
+ char *res, *s,*t;
+ int expr2_token_equivs_entries = sizeof(expr2_token_equivs1)/sizeof(char*);
+
+ for (p=mess; *p; p++) {
+ for (i=0; i<expr2_token_equivs_entries; i++) {
+ if ( strncmp(p,expr2_token_equivs1[i],strlen(expr2_token_equivs1[i])) == 0 )
+ {
+ len+=strlen(expr2_token_equivs2[i])+2;
+ p += strlen(expr2_token_equivs1[i])-1;
+ break;
+ }
+ }
+ len++;
+ }
+ res = (char*)malloc(len+1);
+ res[0] = 0;
+ s = res;
+ for (p=mess; *p;) {
+ int found = 0;
+ for (i=0; i<expr2_token_equivs_entries; i++) {
+ if ( strncmp(p,expr2_token_equivs1[i],strlen(expr2_token_equivs1[i])) == 0 ) {
+ *s++ = '\'';
+ for (t=expr2_token_equivs2[i]; *t;) {
+ *s++ = *t++;
+ }
+ *s++ = '\'';
+ p += strlen(expr2_token_equivs1[i]);
+ found = 1;
+ break;
+ }
+ }
+ if( !found )
+ *s++ = *p++;
+ }
+ *s++ = 0;
+ return res;
+}
+
+int ast_yyerror (const char *s, yyltype *loc, struct parse_io *parseio )
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)(parseio->scanner);
+ char spacebuf[8000]; /* best safe than sorry */
+ char spacebuf2[8000]; /* best safe than sorry */
+ int i=0;
+ char *s2 = expr2_token_subst(s);
+ spacebuf[0] = 0;
+
+ for(i=0;i< (int)(yytext - YY_CURRENT_BUFFER_LVALUE->yy_ch_buf);i++) spacebuf2[i] = ' '; /* uh... assuming yyg is defined, then I can use the yycolumn macro,
+ which is the same thing as... get this:
+ yyg->yy_buffer_stack[yyg->yy_buffer_stack_top]->yy_bs_column
+ I was tempted to just use yy_buf_pos in the STATE, but..., well:
+ a. the yy_buf_pos is the current position in the buffer, which
+ may not relate to the entire string/buffer because of the
+ buffering.
+ b. but, analysis of the situation is that when you use the
+ ast_yy_scan_string func, it creates a single buffer the size of
+ string, so the two would be the same...
+ so, in the end, the yycolumn macro is available, shorter, therefore easier. */
+ spacebuf2[i++]='^';
+ spacebuf2[i]= 0;
+
+#ifdef STANDALONE3
+ /* easier to read in the standalone version */
+ printf("ast_yyerror(): %s syntax error: %s; Input:\n%s\n%s\n",
+ (extra_error_message_supplied?extra_error_message:""), s2, parseio->string,spacebuf2);
+#else
+ ast_log(LOG_WARNING,"ast_yyerror(): %s syntax error: %s; Input:\n%s\n%s\n",
+ (extra_error_message_supplied?extra_error_message:""), s2, parseio->string,spacebuf2);
+#endif
+#ifndef STANDALONE
+ ast_log(LOG_WARNING,"If you have questions, please refer to doc/channelvariables.txt in the asterisk source.\n");
+#endif
+ free(s2);
+ return(0);
+}
+
diff --git a/trunk/main/asterisk.c b/trunk/main/asterisk.c
new file mode 100644
index 000000000..3b3c2cb2a
--- /dev/null
+++ b/trunk/main/asterisk.c
@@ -0,0 +1,3267 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006, 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.
+ */
+
+
+/* Doxygenified Copyright Header */
+/*!
+ * \mainpage Asterisk -- An Open Source Telephony Toolkit
+ *
+ * \par Developer Documentation for Asterisk
+ * This is the main developer documentation for Asterisk. It is
+ * generated by running "make progdocs".
+ * \par Additional documentation
+ * \arg \ref DevDoc
+ * \arg \ref ConfigFiles
+ *
+ * \section copyright Copyright and author
+ *
+ * Copyright (C) 1999 - 2006, Digium, Inc.
+ * Asterisk is a trade mark registered by Digium, Inc.
+ *
+ * \author Mark Spencer <markster@digium.com>
+ * Also see \ref AstCREDITS
+ *
+ * \section license License
+ * 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.
+ *
+ * \verbinclude LICENSE
+ *
+ */
+
+/*! \file
+ \brief Top level source file for Asterisk - the Open Source PBX. Implementation
+ of PBX core functions and CLI interface.
+
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/_private.h"
+
+#undef sched_setscheduler
+#undef setpriority
+#include <sys/time.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sched.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+#include <ctype.h>
+#include <sys/resource.h>
+#include <grp.h>
+#include <pwd.h>
+#include <sys/stat.h>
+#if defined(HAVE_SYSINFO)
+#include <sys/sysinfo.h>
+#endif
+#ifdef linux
+#include <sys/prctl.h>
+#ifdef HAVE_CAP
+#include <sys/capability.h>
+#endif /* HAVE_CAP */
+#endif /* linux */
+#include <regex.h>
+
+#if defined(SOLARIS)
+int daemon(int, int); /* defined in libresolv of all places */
+#include <sys/loadavg.h>
+#endif
+
+#include "asterisk/paths.h" /* we define here the variables so better agree on the prototype */
+#include "asterisk/network.h"
+#include "asterisk/cli.h"
+#include "asterisk/channel.h"
+#include "asterisk/ulaw.h"
+#include "asterisk/alaw.h"
+#include "asterisk/callerid.h"
+#include "asterisk/image.h"
+#include "asterisk/tdd.h"
+#include "asterisk/term.h"
+#include "asterisk/manager.h"
+#include "asterisk/cdr.h"
+#include "asterisk/pbx.h"
+#include "asterisk/enum.h"
+#include "asterisk/rtp.h"
+#include "asterisk/http.h"
+#include "asterisk/udptl.h"
+#include "asterisk/app.h"
+#include "asterisk/lock.h"
+#include "asterisk/utils.h"
+#include "asterisk/file.h"
+#include "asterisk/io.h"
+#include "editline/histedit.h"
+#include "asterisk/config.h"
+#include "asterisk/version.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/devicestate.h"
+#include "asterisk/module.h"
+
+#include "asterisk/doxyref.h" /* Doxygen documentation */
+
+#include "../defaults.h"
+
+#ifndef AF_LOCAL
+#define AF_LOCAL AF_UNIX
+#define PF_LOCAL PF_UNIX
+#endif
+
+#define AST_MAX_CONNECTS 128
+#define NUM_MSGS 64
+
+/*! \brief Welcome message when starting a CLI interface */
+#define WELCOME_MESSAGE \
+ ast_verbose("Asterisk %s, Copyright (C) 1999 - 2007 Digium, Inc. and others.\n" \
+ "Created by Mark Spencer <markster@digium.com>\n" \
+ "Asterisk comes with ABSOLUTELY NO WARRANTY; type 'core show warranty' for details.\n" \
+ "This is free software, with components licensed under the GNU General Public\n" \
+ "License version 2 and other licenses; you are welcome to redistribute it under\n" \
+ "certain conditions. Type 'core show license' for details.\n" \
+ "=========================================================================\n" \
+ "NOTE: This is a development version of Asterisk, and should not be used in\n" \
+ "production installations.\n", ast_get_version());
+
+/*! \defgroup main_options Main Configuration Options
+ * \brief Main configuration options from asterisk.conf or OS command line on starting Asterisk.
+ * \arg \ref Config_ast "asterisk.conf"
+ * \note Some of them can be changed in the CLI
+ */
+/*! @{ */
+
+struct ast_flags ast_options = { AST_DEFAULT_OPTIONS };
+
+int option_verbose; /*!< Verbosity level */
+int option_debug; /*!< Debug level */
+double option_maxload; /*!< Max load avg on system */
+int option_maxcalls; /*!< Max number of active calls */
+int option_maxfiles; /*!< Max number of open file handles (files, sockets) */
+#if defined(HAVE_SYSINFO)
+long option_minmemfree; /*!< Minimum amount of free system memory - stop accepting calls if free memory falls below this watermark */
+#endif
+
+/*! @} */
+
+/* XXX tmpdir is a subdir of the spool directory, and no way to remap it */
+char record_cache_dir[AST_CACHE_DIR_LEN] = DEFAULT_TMP_DIR;
+
+static int ast_socket = -1; /*!< UNIX Socket for allowing remote control */
+static int ast_consock = -1; /*!< UNIX Socket for controlling another asterisk */
+pid_t ast_mainpid;
+struct console {
+ int fd; /*!< File descriptor */
+ int p[2]; /*!< Pipe */
+ pthread_t t; /*!< Thread of handler */
+ int mute; /*!< Is the console muted for logs */
+};
+
+struct ast_atexit {
+ void (*func)(void);
+ AST_RWLIST_ENTRY(ast_atexit) list;
+};
+
+static AST_RWLIST_HEAD_STATIC(atexits, ast_atexit);
+
+struct timeval ast_startuptime;
+struct timeval ast_lastreloadtime;
+
+static History *el_hist;
+static EditLine *el;
+static char *remotehostname;
+
+struct console consoles[AST_MAX_CONNECTS];
+
+char defaultlanguage[MAX_LANGUAGE] = DEFAULT_LANGUAGE;
+
+static int ast_el_add_history(char *);
+static int ast_el_read_history(char *);
+static int ast_el_write_history(char *);
+
+struct _cfg_paths {
+ char config_dir[PATH_MAX];
+ char module_dir[PATH_MAX];
+ char spool_dir[PATH_MAX];
+ char monitor_dir[PATH_MAX];
+ char var_dir[PATH_MAX];
+ char data_dir[PATH_MAX];
+ char log_dir[PATH_MAX];
+ char agi_dir[PATH_MAX];
+ char run_dir[PATH_MAX];
+ char key_dir[PATH_MAX];
+
+ char config_file[PATH_MAX];
+ char db_path[PATH_MAX];
+ char pid_path[PATH_MAX];
+ char socket_path[PATH_MAX];
+ char run_user[PATH_MAX];
+ char run_group[PATH_MAX];
+ char system_name[128];
+};
+
+static struct _cfg_paths cfg_paths;
+
+const char *ast_config_AST_CONFIG_DIR = cfg_paths.config_dir;
+const char *ast_config_AST_CONFIG_FILE = cfg_paths.config_file;
+const char *ast_config_AST_MODULE_DIR = cfg_paths.module_dir;
+const char *ast_config_AST_SPOOL_DIR = cfg_paths.spool_dir;
+const char *ast_config_AST_MONITOR_DIR = cfg_paths.monitor_dir;
+const char *ast_config_AST_VAR_DIR = cfg_paths.var_dir;
+const char *ast_config_AST_DATA_DIR = cfg_paths.data_dir;
+const char *ast_config_AST_LOG_DIR = cfg_paths.log_dir;
+const char *ast_config_AST_AGI_DIR = cfg_paths.agi_dir;
+const char *ast_config_AST_KEY_DIR = cfg_paths.key_dir;
+const char *ast_config_AST_RUN_DIR = cfg_paths.run_dir;
+
+const char *ast_config_AST_DB = cfg_paths.db_path;
+const char *ast_config_AST_PID = cfg_paths.pid_path;
+const char *ast_config_AST_SOCKET = cfg_paths.socket_path;
+const char *ast_config_AST_RUN_USER = cfg_paths.run_user;
+const char *ast_config_AST_RUN_GROUP = cfg_paths.run_group;
+const char *ast_config_AST_SYSTEM_NAME = cfg_paths.system_name;
+
+static char ast_config_AST_CTL_PERMISSIONS[PATH_MAX];
+static char ast_config_AST_CTL_OWNER[PATH_MAX] = "\0";
+static char ast_config_AST_CTL_GROUP[PATH_MAX] = "\0";
+static char ast_config_AST_CTL[PATH_MAX] = "asterisk.ctl";
+
+extern const char *ast_build_hostname;
+extern const char *ast_build_kernel;
+extern const char *ast_build_machine;
+extern const char *ast_build_os;
+extern const char *ast_build_date;
+extern const char *ast_build_user;
+
+static char *_argv[256];
+static int shuttingdown;
+static int restartnow;
+static pthread_t consolethread = AST_PTHREADT_NULL;
+static int canary_pid = 0;
+static char canary_filename[128];
+
+static char randompool[256];
+
+static int sig_alert_pipe[2] = { -1, -1 };
+static struct {
+ unsigned int need_reload:1;
+ unsigned int need_quit:1;
+} sig_flags;
+
+#if !defined(LOW_MEMORY)
+struct file_version {
+ AST_RWLIST_ENTRY(file_version) list;
+ const char *file;
+ char *version;
+};
+
+static AST_RWLIST_HEAD_STATIC(file_versions, file_version);
+
+void ast_register_file_version(const char *file, const char *version)
+{
+ struct file_version *new;
+ char *work;
+ size_t version_length;
+
+ work = ast_strdupa(version);
+ work = ast_strip(ast_strip_quoted(work, "$", "$"));
+ version_length = strlen(work) + 1;
+
+ if (!(new = ast_calloc(1, sizeof(*new) + version_length)))
+ return;
+
+ new->file = file;
+ new->version = (char *) new + sizeof(*new);
+ memcpy(new->version, work, version_length);
+ AST_RWLIST_WRLOCK(&file_versions);
+ AST_RWLIST_INSERT_HEAD(&file_versions, new, list);
+ AST_RWLIST_UNLOCK(&file_versions);
+}
+
+void ast_unregister_file_version(const char *file)
+{
+ struct file_version *find;
+
+ AST_RWLIST_WRLOCK(&file_versions);
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&file_versions, find, list) {
+ if (!strcasecmp(find->file, file)) {
+ AST_RWLIST_REMOVE_CURRENT(list);
+ break;
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+ AST_RWLIST_UNLOCK(&file_versions);
+
+ if (find)
+ ast_free(find);
+}
+
+/*! \brief Find version for given module name */
+const char *ast_file_version_find(const char *file)
+{
+ struct file_version *iterator;
+
+ AST_RWLIST_WRLOCK(&file_versions);
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&file_versions, iterator, list) {
+ if (!strcasecmp(iterator->file, file))
+ break;
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+ AST_RWLIST_UNLOCK(&file_versions);
+ if (iterator)
+ return iterator->version;
+ return NULL;
+}
+
+
+
+struct thread_list_t {
+ AST_RWLIST_ENTRY(thread_list_t) list;
+ char *name;
+ pthread_t id;
+};
+
+static AST_RWLIST_HEAD_STATIC(thread_list, thread_list_t);
+
+void ast_register_thread(char *name)
+{
+ struct thread_list_t *new = ast_calloc(1, sizeof(*new));
+
+ if (!new)
+ return;
+ new->id = pthread_self();
+ new->name = name; /* steal the allocated memory for the thread name */
+ AST_RWLIST_WRLOCK(&thread_list);
+ AST_RWLIST_INSERT_HEAD(&thread_list, new, list);
+ AST_RWLIST_UNLOCK(&thread_list);
+}
+
+void ast_unregister_thread(void *id)
+{
+ struct thread_list_t *x;
+
+ AST_RWLIST_WRLOCK(&thread_list);
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&thread_list, x, list) {
+ if ((void *) x->id == id) {
+ AST_RWLIST_REMOVE_CURRENT(list);
+ break;
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+ AST_RWLIST_UNLOCK(&thread_list);
+ if (x) {
+ ast_free(x->name);
+ ast_free(x);
+ }
+}
+
+/*! \brief Give an overview of core settings */
+static char *handle_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char buf[BUFSIZ];
+ struct ast_tm tm;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show settings";
+ e->usage = "Usage: core show settings\n"
+ " Show core misc settings";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ ast_cli(a->fd, "\nPBX Core settings\n");
+ ast_cli(a->fd, "-----------------\n");
+ ast_cli(a->fd, " Version: %s\n", ast_get_version());
+ if (option_maxcalls)
+ ast_cli(a->fd, " Max. calls: %d (Current %d)\n", option_maxcalls, ast_active_channels());
+ else
+ ast_cli(a->fd, " Max. calls: Not set\n");
+ if (option_maxfiles)
+ ast_cli(a->fd, " Max. open file handles: %d\n", option_maxfiles);
+ else
+ ast_cli(a->fd, " Max. open file handles: Not set\n");
+ ast_cli(a->fd, " Verbosity: %d\n", option_verbose);
+ ast_cli(a->fd, " Debug level: %d\n", option_debug);
+ ast_cli(a->fd, " Max load avg: %lf\n", option_maxload);
+#if defined(HAVE_SYSINFO)
+ ast_cli(a->fd, " Min Free Memory: %ld MB\n", option_minmemfree);
+#endif
+ if (ast_localtime(&ast_startuptime, &tm, NULL)) {
+ ast_strftime(buf, sizeof(buf), "%H:%M:%S", &tm);
+ ast_cli(a->fd, " Startup time: %s\n", buf);
+ }
+ if (ast_localtime(&ast_lastreloadtime, &tm, NULL)) {
+ ast_strftime(buf, sizeof(buf), "%H:%M:%S", &tm);
+ ast_cli(a->fd, " Last reload time: %s\n", buf);
+ }
+ ast_cli(a->fd, " System: %s/%s built by %s on %s %s\n", ast_build_os, ast_build_kernel, ast_build_user, ast_build_machine, ast_build_date);
+ ast_cli(a->fd, " System name: %s\n", ast_config_AST_SYSTEM_NAME);
+ ast_cli(a->fd, " Default language: %s\n", defaultlanguage);
+ ast_cli(a->fd, " Language prefix: %s\n", ast_language_is_prefix ? "Enabled" : "Disabled");
+ ast_cli(a->fd, " User name and group: %s/%s\n", ast_config_AST_RUN_USER, ast_config_AST_RUN_GROUP);
+ ast_cli(a->fd, " Executable includes: %s\n", ast_test_flag(&ast_options, AST_OPT_FLAG_EXEC_INCLUDES) ? "Enabled" : "Disabled");
+ ast_cli(a->fd, " Transcode via SLIN: %s\n", ast_test_flag(&ast_options, AST_OPT_FLAG_TRANSCODE_VIA_SLIN) ? "Enabled" : "Disabled");
+ ast_cli(a->fd, " Internal timing: %s\n", ast_test_flag(&ast_options, AST_OPT_FLAG_INTERNAL_TIMING) ? "Enabled" : "Disabled");
+ ast_cli(a->fd, " Transmit silence during rec: %s\n", ast_test_flag(&ast_options, AST_OPT_FLAG_INTERNAL_TIMING) ? "Enabled" : "Disabled");
+
+ ast_cli(a->fd, "\n* Subsystems\n");
+ ast_cli(a->fd, " -------------\n");
+ ast_cli(a->fd, " Manager (AMI): %s\n", check_manager_enabled() ? "Enabled" : "Disabled");
+ ast_cli(a->fd, " Web Manager (AMI/HTTP): %s\n", check_webmanager_enabled() ? "Enabled" : "Disabled");
+ ast_cli(a->fd, " Call data records: %s\n", check_cdr_enabled() ? "Enabled" : "Disabled");
+ ast_cli(a->fd, " Realtime Architecture (ARA): %s\n", ast_realtime_enabled() ? "Enabled" : "Disabled");
+
+ /*! \todo we could check musiconhold, voicemail, smdi, adsi, queues */
+
+ ast_cli(a->fd, "\n* Directories\n");
+ ast_cli(a->fd, " -------------\n");
+ ast_cli(a->fd, " Configuration file: %s\n", ast_config_AST_CONFIG_FILE);
+ ast_cli(a->fd, " Configuration directory: %s\n", ast_config_AST_CONFIG_DIR);
+ ast_cli(a->fd, " Module directory: %s\n", ast_config_AST_MODULE_DIR);
+ ast_cli(a->fd, " Spool directory: %s\n", ast_config_AST_SPOOL_DIR);
+ ast_cli(a->fd, " Log directory: %s\n", ast_config_AST_LOG_DIR);
+ ast_cli(a->fd, "\n\n");
+ return CLI_SUCCESS;
+}
+
+static char *handle_show_threads(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int count = 0;
+ struct thread_list_t *cur;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show threads";
+ e->usage =
+ "Usage: core show threads\n"
+ " List threads currently active in the system.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ AST_RWLIST_RDLOCK(&thread_list);
+ AST_RWLIST_TRAVERSE(&thread_list, cur, list) {
+ ast_cli(a->fd, "%p %s\n", (void *)cur->id, cur->name);
+ count++;
+ }
+ AST_RWLIST_UNLOCK(&thread_list);
+ ast_cli(a->fd, "%d threads listed.\n", count);
+ return CLI_SUCCESS;
+}
+
+#if defined(HAVE_SYSINFO)
+/*! \brief Give an overview of system statistics */
+static char *handle_show_sysinfo(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct sysinfo sys_info;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show sysinfo";
+ e->usage =
+ "Usage: core show sysinfo\n"
+ " List current system information.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (sysinfo(&sys_info)) {
+ ast_cli(a->fd, "FAILED to retrieve system information\n\n");
+ return CLI_FAILURE;
+ }
+ ast_cli(a->fd, "\nSystem Statistics\n");
+ ast_cli(a->fd, "-----------------\n");
+ ast_cli(a->fd, " System Uptime: %ld hours\n", sys_info.uptime/3600);
+ ast_cli(a->fd, " Total RAM: %ld KiB\n", (sys_info.totalram / sys_info.mem_unit)/1024);
+ ast_cli(a->fd, " Free RAM: %ld KiB\n", (sys_info.freeram / sys_info.mem_unit)/1024);
+ ast_cli(a->fd, " Buffer RAM: %ld KiB\n", (sys_info.bufferram / sys_info.mem_unit)/1024);
+ ast_cli(a->fd, " Total Swap Space: %ld KiB\n", (sys_info.totalswap / sys_info.mem_unit)/1024);
+ ast_cli(a->fd, " Free Swap Space: %ld KiB\n\n", (sys_info.freeswap / sys_info.mem_unit)/1024);
+ ast_cli(a->fd, " Number of Processes: %d \n\n", sys_info.procs);
+ return CLI_SUCCESS;
+}
+#endif
+
+struct profile_entry {
+ const char *name;
+ uint64_t scale; /* if non-zero, values are scaled by this */
+ int64_t mark;
+ int64_t value;
+ int64_t events;
+};
+
+struct profile_data {
+ int entries;
+ int max_size;
+ struct profile_entry e[0];
+};
+
+static struct profile_data *prof_data;
+
+/*! \brief allocates a counter with a given name and scale.
+ * \return Returns the identifier of the counter.
+ */
+int ast_add_profile(const char *name, uint64_t scale)
+{
+ int l = sizeof(struct profile_data);
+ int n = 10; /* default entries */
+
+ if (prof_data == NULL) {
+ prof_data = ast_calloc(1, l + n*sizeof(struct profile_entry));
+ if (prof_data == NULL)
+ return -1;
+ prof_data->entries = 0;
+ prof_data->max_size = n;
+ }
+ if (prof_data->entries >= prof_data->max_size) {
+ void *p;
+ n = prof_data->max_size + 20;
+ p = ast_realloc(prof_data, l + n*sizeof(struct profile_entry));
+ if (p == NULL)
+ return -1;
+ prof_data = p;
+ prof_data->max_size = n;
+ }
+ n = prof_data->entries++;
+ prof_data->e[n].name = ast_strdup(name);
+ prof_data->e[n].value = 0;
+ prof_data->e[n].events = 0;
+ prof_data->e[n].mark = 0;
+ prof_data->e[n].scale = scale;
+ return n;
+}
+
+int64_t ast_profile(int i, int64_t delta)
+{
+ if (!prof_data || i < 0 || i > prof_data->entries) /* invalid index */
+ return 0;
+ if (prof_data->e[i].scale > 1)
+ delta /= prof_data->e[i].scale;
+ prof_data->e[i].value += delta;
+ prof_data->e[i].events++;
+ return prof_data->e[i].value;
+}
+
+#if defined ( __i386__) && (defined(__FreeBSD__) || defined(linux))
+#if defined(__FreeBSD__)
+#include <machine/cpufunc.h>
+#elif defined(linux)
+static __inline uint64_t
+rdtsc(void)
+{
+ uint64_t rv;
+
+ __asm __volatile(".byte 0x0f, 0x31" : "=A" (rv));
+ return (rv);
+}
+#endif
+#else /* supply a dummy function on other platforms */
+static __inline uint64_t
+rdtsc(void)
+{
+ return 0;
+}
+#endif
+
+int64_t ast_mark(int i, int startstop)
+{
+ if (!prof_data || i < 0 || i > prof_data->entries) /* invalid index */
+ return 0;
+ if (startstop == 1)
+ prof_data->e[i].mark = rdtsc();
+ else {
+ prof_data->e[i].mark = (rdtsc() - prof_data->e[i].mark);
+ if (prof_data->e[i].scale > 1)
+ prof_data->e[i].mark /= prof_data->e[i].scale;
+ prof_data->e[i].value += prof_data->e[i].mark;
+ prof_data->e[i].events++;
+ }
+ return prof_data->e[i].mark;
+}
+
+#define DEFINE_PROFILE_MIN_MAX_VALUES min = 0; \
+ max = prof_data->entries;\
+ if (a->argc > 3) { /* specific entries */ \
+ if (isdigit(a->argv[3][0])) { \
+ min = atoi(a->argv[3]); \
+ if (a->argc == 5 && strcmp(a->argv[4], "-")) \
+ max = atoi(a->argv[4]); \
+ } else \
+ search = a->argv[3]; \
+ } \
+ if (max > prof_data->entries) \
+ max = prof_data->entries;
+
+static char *handle_show_profile(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int i, min, max;
+ char *search = NULL;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show profile";
+ e->usage = "Usage: core show profile\n"
+ " show profile information";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (prof_data == NULL)
+ return 0;
+
+ DEFINE_PROFILE_MIN_MAX_VALUES;
+ ast_cli(a->fd, "profile values (%d, allocated %d)\n-------------------\n",
+ prof_data->entries, prof_data->max_size);
+ ast_cli(a->fd, "%6s %8s %10s %12s %12s %s\n", "ID", "Scale", "Events",
+ "Value", "Average", "Name");
+ for (i = min; i < max; i++) {
+ struct profile_entry *e = &prof_data->e[i];
+ if (!search || strstr(prof_data->e[i].name, search))
+ ast_cli(a->fd, "%6d: [%8ld] %10ld %12lld %12lld %s\n",
+ i,
+ (long)e->scale,
+ (long)e->events, (long long)e->value,
+ (long long)(e->events ? e->value / e->events : e->value),
+ e->name);
+ }
+ return CLI_SUCCESS;
+}
+
+static char *handle_clear_profile(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int i, min, max;
+ char *search = NULL;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core clear profile";
+ e->usage = "Usage: core clear profile\n"
+ " clear profile information";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (prof_data == NULL)
+ return 0;
+
+ DEFINE_PROFILE_MIN_MAX_VALUES;
+ for (i= min; i < max; i++) {
+ if (!search || strstr(prof_data->e[i].name, search)) {
+ prof_data->e[i].value = 0;
+ prof_data->e[i].events = 0;
+ }
+ }
+ return CLI_SUCCESS;
+}
+#undef DEFINE_PROFILE_MIN_MAX_VALUES
+
+/*! \brief CLI command to list module versions */
+static char *handle_show_version_files(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+#define FORMAT "%-25.25s %-40.40s\n"
+ struct file_version *iterator;
+ regex_t regexbuf;
+ int havepattern = 0;
+ int havename = 0;
+ int count_files = 0;
+ char *ret = NULL;
+ int matchlen, which = 0;
+ struct file_version *find;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show file version [like]";
+ e->usage =
+ "Usage: core show file version [like <pattern>]\n"
+ " Lists the revision numbers of the files used to build this copy of Asterisk.\n"
+ " Optional regular expression pattern is used to filter the file list.\n";
+ return NULL;
+ case CLI_GENERATE:
+ matchlen = strlen(a->word);
+ if (a->pos != 3)
+ return NULL;
+ AST_RWLIST_RDLOCK(&file_versions);
+ AST_RWLIST_TRAVERSE(&file_versions, find, list) {
+ if (!strncasecmp(a->word, find->file, matchlen) && ++which > a->n) {
+ ret = ast_strdup(find->file);
+ break;
+ }
+ }
+ AST_RWLIST_UNLOCK(&file_versions);
+ return ret;
+ }
+
+
+ switch (a->argc) {
+ case 6:
+ if (!strcasecmp(a->argv[4], "like")) {
+ if (regcomp(&regexbuf, a->argv[5], REG_EXTENDED | REG_NOSUB))
+ return CLI_SHOWUSAGE;
+ havepattern = 1;
+ } else
+ return CLI_SHOWUSAGE;
+ break;
+ case 5:
+ havename = 1;
+ break;
+ case 4:
+ break;
+ default:
+ return CLI_SHOWUSAGE;
+ }
+
+ ast_cli(a->fd, FORMAT, "File", "Revision");
+ ast_cli(a->fd, FORMAT, "----", "--------");
+ AST_RWLIST_RDLOCK(&file_versions);
+ AST_RWLIST_TRAVERSE(&file_versions, iterator, list) {
+ if (havename && strcasecmp(iterator->file, a->argv[4]))
+ continue;
+
+ if (havepattern && regexec(&regexbuf, iterator->file, 0, NULL, 0))
+ continue;
+
+ ast_cli(a->fd, FORMAT, iterator->file, iterator->version);
+ count_files++;
+ if (havename)
+ break;
+ }
+ AST_RWLIST_UNLOCK(&file_versions);
+ if (!havename) {
+ ast_cli(a->fd, "%d files listed.\n", count_files);
+ }
+
+ if (havepattern)
+ regfree(&regexbuf);
+
+ return CLI_SUCCESS;
+#undef FORMAT
+}
+
+#endif /* ! LOW_MEMORY */
+
+int ast_register_atexit(void (*func)(void))
+{
+ struct ast_atexit *ae;
+
+ if (!(ae = ast_calloc(1, sizeof(*ae))))
+ return -1;
+
+ ae->func = func;
+
+ ast_unregister_atexit(func);
+
+ AST_RWLIST_WRLOCK(&atexits);
+ AST_RWLIST_INSERT_HEAD(&atexits, ae, list);
+ AST_RWLIST_UNLOCK(&atexits);
+
+ return 0;
+}
+
+void ast_unregister_atexit(void (*func)(void))
+{
+ struct ast_atexit *ae = NULL;
+
+ AST_RWLIST_WRLOCK(&atexits);
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&atexits, ae, list) {
+ if (ae->func == func) {
+ AST_RWLIST_REMOVE_CURRENT(list);
+ break;
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+ AST_RWLIST_UNLOCK(&atexits);
+
+ if (ae)
+ free(ae);
+}
+
+static int fdprint(int fd, const char *s)
+{
+ return write(fd, s, strlen(s) + 1);
+}
+
+/*! \brief NULL handler so we can collect the child exit status */
+static void null_sig_handler(int signal)
+{
+
+}
+
+AST_MUTEX_DEFINE_STATIC(safe_system_lock);
+/*! \brief Keep track of how many threads are currently trying to wait*() on
+ * a child process */
+static unsigned int safe_system_level = 0;
+static void *safe_system_prev_handler;
+
+void ast_replace_sigchld(void)
+{
+ unsigned int level;
+
+ ast_mutex_lock(&safe_system_lock);
+ level = safe_system_level++;
+
+ /* only replace the handler if it has not already been done */
+ if (level == 0)
+ safe_system_prev_handler = signal(SIGCHLD, null_sig_handler);
+
+ ast_mutex_unlock(&safe_system_lock);
+}
+
+void ast_unreplace_sigchld(void)
+{
+ unsigned int level;
+
+ ast_mutex_lock(&safe_system_lock);
+ level = --safe_system_level;
+
+ /* only restore the handler if we are the last one */
+ if (level == 0)
+ signal(SIGCHLD, safe_system_prev_handler);
+
+ ast_mutex_unlock(&safe_system_lock);
+}
+
+int ast_safe_system(const char *s)
+{
+ pid_t pid;
+#ifdef HAVE_WORKING_FORK
+ int x;
+#endif
+ int res;
+ struct rusage rusage;
+ int status;
+
+#if defined(HAVE_WORKING_FORK) || defined(HAVE_WORKING_VFORK)
+ ast_replace_sigchld();
+
+#ifdef HAVE_WORKING_FORK
+ pid = fork();
+#else
+ pid = vfork();
+#endif
+
+ if (pid == 0) {
+#ifdef HAVE_WORKING_FORK
+ if (ast_opt_high_priority)
+ ast_set_priority(0);
+ /* Close file descriptors and launch system command */
+ for (x = STDERR_FILENO + 1; x < 4096; x++)
+ close(x);
+#endif
+ execl("/bin/sh", "/bin/sh", "-c", s, (char *) NULL);
+ _exit(1);
+ } else if (pid > 0) {
+ for (;;) {
+ res = wait4(pid, &status, 0, &rusage);
+ if (res > -1) {
+ res = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
+ break;
+ } else if (errno != EINTR)
+ break;
+ }
+ } else {
+ ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno));
+ res = -1;
+ }
+
+ ast_unreplace_sigchld();
+#else
+ res = -1;
+#endif
+
+ return res;
+}
+
+/*!
+ * \brief mute or unmute a console from logging
+ */
+void ast_console_toggle_mute(int fd, int silent) {
+ int x;
+ for (x = 0;x < AST_MAX_CONNECTS; x++) {
+ if (fd == consoles[x].fd) {
+ if (consoles[x].mute) {
+ consoles[x].mute = 0;
+ if (!silent)
+ ast_cli(fd, "Console is not muted anymore.\n");
+ } else {
+ consoles[x].mute = 1;
+ if (!silent)
+ ast_cli(fd, "Console is muted.\n");
+ }
+ return;
+ }
+ }
+ ast_cli(fd, "Couldn't find remote console.\n");
+}
+
+/*!
+ * \brief log the string to all attached console clients
+ */
+static void ast_network_puts_mutable(const char *string)
+{
+ int x;
+ for (x = 0;x < AST_MAX_CONNECTS; x++) {
+ if (consoles[x].mute)
+ continue;
+ if (consoles[x].fd > -1)
+ fdprint(consoles[x].p[1], string);
+ }
+}
+
+/*!
+ * \brief log the string to the console, and all attached
+ * console clients
+ */
+void ast_console_puts_mutable(const char *string)
+{
+ fputs(string, stdout);
+ fflush(stdout);
+ ast_network_puts_mutable(string);
+}
+
+/*!
+ * \brief write the string to all attached console clients
+ */
+static void ast_network_puts(const char *string)
+{
+ int x;
+ for (x=0; x < AST_MAX_CONNECTS; x++) {
+ if (consoles[x].fd > -1)
+ fdprint(consoles[x].p[1], string);
+ }
+}
+
+/*!
+ * write the string to the console, and all attached
+ * console clients
+ */
+void ast_console_puts(const char *string)
+{
+ fputs(string, stdout);
+ fflush(stdout);
+ ast_network_puts(string);
+}
+
+static void network_verboser(const char *s)
+{
+ ast_network_puts_mutable(s);
+}
+
+static pthread_t lthread;
+
+static void *netconsole(void *vconsole)
+{
+ struct console *con = vconsole;
+ char hostname[MAXHOSTNAMELEN] = "";
+ char tmp[512];
+ int res;
+ struct pollfd fds[2];
+
+ if (gethostname(hostname, sizeof(hostname)-1))
+ ast_copy_string(hostname, "<Unknown>", sizeof(hostname));
+ snprintf(tmp, sizeof(tmp), "%s/%ld/%s\n", hostname, (long)ast_mainpid, ast_get_version());
+ fdprint(con->fd, tmp);
+ for (;;) {
+ fds[0].fd = con->fd;
+ fds[0].events = POLLIN;
+ fds[0].revents = 0;
+ fds[1].fd = con->p[0];
+ fds[1].events = POLLIN;
+ fds[1].revents = 0;
+
+ res = poll(fds, 2, -1);
+ if (res < 0) {
+ if (errno != EINTR)
+ ast_log(LOG_WARNING, "poll returned < 0: %s\n", strerror(errno));
+ continue;
+ }
+ if (fds[0].revents) {
+ res = read(con->fd, tmp, sizeof(tmp));
+ if (res < 1) {
+ break;
+ }
+ tmp[res] = 0;
+ ast_cli_command_multiple(con->fd, res, tmp);
+ }
+ if (fds[1].revents) {
+ res = read(con->p[0], tmp, sizeof(tmp));
+ if (res < 1) {
+ ast_log(LOG_ERROR, "read returned %d\n", res);
+ break;
+ }
+ res = write(con->fd, tmp, res);
+ if (res < 1)
+ break;
+ }
+ }
+ ast_verb(3, "Remote UNIX connection disconnected\n");
+ close(con->fd);
+ close(con->p[0]);
+ close(con->p[1]);
+ con->fd = -1;
+
+ return NULL;
+}
+
+static void *listener(void *unused)
+{
+ struct sockaddr_un sunaddr;
+ int s;
+ socklen_t len;
+ int x;
+ int flags;
+ struct pollfd fds[1];
+ for (;;) {
+ if (ast_socket < 0)
+ return NULL;
+ fds[0].fd = ast_socket;
+ fds[0].events = POLLIN;
+ s = poll(fds, 1, -1);
+ pthread_testcancel();
+ if (s < 0) {
+ if (errno != EINTR)
+ ast_log(LOG_WARNING, "poll returned error: %s\n", strerror(errno));
+ continue;
+ }
+ len = sizeof(sunaddr);
+ s = accept(ast_socket, (struct sockaddr *)&sunaddr, &len);
+ if (s < 0) {
+ if (errno != EINTR)
+ ast_log(LOG_WARNING, "Accept returned %d: %s\n", s, strerror(errno));
+ } else {
+ for (x = 0; x < AST_MAX_CONNECTS; x++) {
+ if (consoles[x].fd < 0) {
+ if (socketpair(AF_LOCAL, SOCK_STREAM, 0, consoles[x].p)) {
+ ast_log(LOG_ERROR, "Unable to create pipe: %s\n", strerror(errno));
+ consoles[x].fd = -1;
+ fdprint(s, "Server failed to create pipe\n");
+ close(s);
+ break;
+ }
+ flags = fcntl(consoles[x].p[1], F_GETFL);
+ fcntl(consoles[x].p[1], F_SETFL, flags | O_NONBLOCK);
+ consoles[x].fd = s;
+ consoles[x].mute = 1; /* Default is muted, we will un-mute if necessary */
+ if (ast_pthread_create_detached_background(&consoles[x].t, NULL, netconsole, &consoles[x])) {
+ ast_log(LOG_ERROR, "Unable to spawn thread to handle connection: %s\n", strerror(errno));
+ close(consoles[x].p[0]);
+ close(consoles[x].p[1]);
+ consoles[x].fd = -1;
+ fdprint(s, "Server failed to spawn thread\n");
+ close(s);
+ }
+ break;
+ }
+ }
+ if (x >= AST_MAX_CONNECTS) {
+ fdprint(s, "No more connections allowed\n");
+ ast_log(LOG_WARNING, "No more connections allowed\n");
+ close(s);
+ } else if (consoles[x].fd > -1)
+ ast_verb(3, "Remote UNIX connection\n");
+ }
+ }
+ return NULL;
+}
+
+static int ast_makesocket(void)
+{
+ struct sockaddr_un sunaddr;
+ int res;
+ int x;
+ uid_t uid = -1;
+ gid_t gid = -1;
+
+ for (x = 0; x < AST_MAX_CONNECTS; x++)
+ consoles[x].fd = -1;
+ unlink(ast_config_AST_SOCKET);
+ ast_socket = socket(PF_LOCAL, SOCK_STREAM, 0);
+ if (ast_socket < 0) {
+ ast_log(LOG_WARNING, "Unable to create control socket: %s\n", strerror(errno));
+ return -1;
+ }
+ memset(&sunaddr, 0, sizeof(sunaddr));
+ sunaddr.sun_family = AF_LOCAL;
+ ast_copy_string(sunaddr.sun_path, ast_config_AST_SOCKET, sizeof(sunaddr.sun_path));
+ res = bind(ast_socket, (struct sockaddr *)&sunaddr, sizeof(sunaddr));
+ if (res) {
+ ast_log(LOG_WARNING, "Unable to bind socket to %s: %s\n", ast_config_AST_SOCKET, strerror(errno));
+ close(ast_socket);
+ ast_socket = -1;
+ return -1;
+ }
+ res = listen(ast_socket, 2);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Unable to listen on socket %s: %s\n", ast_config_AST_SOCKET, strerror(errno));
+ close(ast_socket);
+ ast_socket = -1;
+ return -1;
+ }
+ ast_register_verbose(network_verboser);
+ ast_pthread_create_background(&lthread, NULL, listener, NULL);
+
+ if (!ast_strlen_zero(ast_config_AST_CTL_OWNER)) {
+ struct passwd *pw;
+ if ((pw = getpwnam(ast_config_AST_CTL_OWNER)) == NULL)
+ ast_log(LOG_WARNING, "Unable to find uid of user %s\n", ast_config_AST_CTL_OWNER);
+ else
+ uid = pw->pw_uid;
+ }
+
+ if (!ast_strlen_zero(ast_config_AST_CTL_GROUP)) {
+ struct group *grp;
+ if ((grp = getgrnam(ast_config_AST_CTL_GROUP)) == NULL)
+ ast_log(LOG_WARNING, "Unable to find gid of group %s\n", ast_config_AST_CTL_GROUP);
+ else
+ gid = grp->gr_gid;
+ }
+
+ if (chown(ast_config_AST_SOCKET, uid, gid) < 0)
+ ast_log(LOG_WARNING, "Unable to change ownership of %s: %s\n", ast_config_AST_SOCKET, strerror(errno));
+
+ if (!ast_strlen_zero(ast_config_AST_CTL_PERMISSIONS)) {
+ int p1;
+ mode_t p;
+ sscanf(ast_config_AST_CTL_PERMISSIONS, "%o", &p1);
+ p = p1;
+ if ((chmod(ast_config_AST_SOCKET, p)) < 0)
+ ast_log(LOG_WARNING, "Unable to change file permissions of %s: %s\n", ast_config_AST_SOCKET, strerror(errno));
+ }
+
+ return 0;
+}
+
+static int ast_tryconnect(void)
+{
+ struct sockaddr_un sunaddr;
+ int res;
+ ast_consock = socket(PF_LOCAL, SOCK_STREAM, 0);
+ if (ast_consock < 0) {
+ ast_log(LOG_WARNING, "Unable to create socket: %s\n", strerror(errno));
+ return 0;
+ }
+ memset(&sunaddr, 0, sizeof(sunaddr));
+ sunaddr.sun_family = AF_LOCAL;
+ ast_copy_string(sunaddr.sun_path, ast_config_AST_SOCKET, sizeof(sunaddr.sun_path));
+ res = connect(ast_consock, (struct sockaddr *)&sunaddr, sizeof(sunaddr));
+ if (res) {
+ close(ast_consock);
+ ast_consock = -1;
+ return 0;
+ } else
+ return 1;
+}
+
+/*! \brief Urgent handler
+
+ Called by soft_hangup to interrupt the poll, read, or other
+ system call. We don't actually need to do anything though.
+ Remember: Cannot EVER ast_log from within a signal handler
+ */
+static void urg_handler(int num)
+{
+ signal(num, urg_handler);
+ return;
+}
+
+static void hup_handler(int num)
+{
+ int a = 0;
+ if (option_verbose > 1)
+ printf("Received HUP signal -- Reloading configs\n");
+ if (restartnow)
+ execvp(_argv[0], _argv);
+ sig_flags.need_reload = 1;
+ if (sig_alert_pipe[1] != -1)
+ write(sig_alert_pipe[1], &a, sizeof(a));
+ signal(num, hup_handler);
+}
+
+static void child_handler(int sig)
+{
+ /* Must not ever ast_log or ast_verbose within signal handler */
+ int n, status;
+
+ /*
+ * Reap all dead children -- not just one
+ */
+ for (n = 0; wait4(-1, &status, WNOHANG, NULL) > 0; n++)
+ ;
+ if (n == 0 && option_debug)
+ printf("Huh? Child handler, but nobody there?\n");
+ signal(sig, child_handler);
+}
+
+/*! \brief Set maximum open files */
+static void set_ulimit(int value)
+{
+ struct rlimit l = {0, 0};
+
+ if (value <= 0) {
+ ast_log(LOG_WARNING, "Unable to change max files open to invalid value %i\n",value);
+ return;
+ }
+
+ l.rlim_cur = value;
+ l.rlim_max = value;
+
+ if (setrlimit(RLIMIT_NOFILE, &l)) {
+ ast_log(LOG_WARNING, "Unable to disable core size resource limit: %s\n",strerror(errno));
+ return;
+ }
+
+ ast_log(LOG_NOTICE, "Setting max files open to %d\n",value);
+
+ return;
+}
+
+/*! \brief Set an X-term or screen title */
+static void set_title(char *text)
+{
+ if (getenv("TERM") && strstr(getenv("TERM"), "xterm"))
+ fprintf(stdout, "\033]2;%s\007", text);
+}
+
+static void set_icon(char *text)
+{
+ if (getenv("TERM") && strstr(getenv("TERM"), "xterm"))
+ fprintf(stdout, "\033]1;%s\007", text);
+}
+
+/*! \brief We set ourselves to a high priority, that we might pre-empt everything
+ else. If your PBX has heavy activity on it, this is a good thing. */
+int ast_set_priority(int pri)
+{
+ struct sched_param sched;
+ memset(&sched, 0, sizeof(sched));
+#ifdef __linux__
+ if (pri) {
+ sched.sched_priority = 10;
+ if (sched_setscheduler(0, SCHED_RR, &sched)) {
+ ast_log(LOG_WARNING, "Unable to set high priority\n");
+ return -1;
+ } else
+ if (option_verbose)
+ ast_verbose("Set to realtime thread\n");
+ } else {
+ sched.sched_priority = 0;
+ /* According to the manpage, these parameters can never fail. */
+ sched_setscheduler(0, SCHED_OTHER, &sched);
+ }
+#else
+ if (pri) {
+ if (setpriority(PRIO_PROCESS, 0, -10) == -1) {
+ ast_log(LOG_WARNING, "Unable to set high priority\n");
+ return -1;
+ } else
+ if (option_verbose)
+ ast_verbose("Set to high priority\n");
+ } else {
+ /* According to the manpage, these parameters can never fail. */
+ setpriority(PRIO_PROCESS, 0, 0);
+ }
+#endif
+ return 0;
+}
+
+static void ast_run_atexits(void)
+{
+ struct ast_atexit *ae;
+ AST_RWLIST_RDLOCK(&atexits);
+ AST_RWLIST_TRAVERSE(&atexits, ae, list) {
+ if (ae->func)
+ ae->func();
+ }
+ AST_RWLIST_UNLOCK(&atexits);
+}
+
+static void quit_handler(int num, int nice, int safeshutdown, int restart)
+{
+ char filename[80] = "";
+ time_t s,e;
+ int x;
+ /* Try to get as many CDRs as possible submitted to the backend engines (if in batch mode) */
+ ast_cdr_engine_term();
+ if (safeshutdown) {
+ shuttingdown = 1;
+ if (!nice) {
+ /* Begin shutdown routine, hanging up active channels */
+ ast_begin_shutdown(1);
+ if (option_verbose && ast_opt_console)
+ ast_verbose("Beginning asterisk %s....\n", restart ? "restart" : "shutdown");
+ time(&s);
+ for (;;) {
+ time(&e);
+ /* Wait up to 15 seconds for all channels to go away */
+ if ((e - s) > 15)
+ break;
+ if (!ast_active_channels())
+ break;
+ if (!shuttingdown)
+ break;
+ /* Sleep 1/10 of a second */
+ usleep(100000);
+ }
+ } else {
+ if (nice < 2)
+ ast_begin_shutdown(0);
+ if (option_verbose && ast_opt_console)
+ ast_verbose("Waiting for inactivity to perform %s...\n", restart ? "restart" : "halt");
+ for (;;) {
+ if (!ast_active_channels())
+ break;
+ if (!shuttingdown)
+ break;
+ sleep(1);
+ }
+ }
+
+ if (!shuttingdown) {
+ if (option_verbose && ast_opt_console)
+ ast_verbose("Asterisk %s cancelled.\n", restart ? "restart" : "shutdown");
+ return;
+ }
+
+ if (nice)
+ ast_module_shutdown();
+ }
+ if (ast_opt_console || ast_opt_remote) {
+ if (getenv("HOME"))
+ snprintf(filename, sizeof(filename), "%s/.asterisk_history", getenv("HOME"));
+ if (!ast_strlen_zero(filename))
+ ast_el_write_history(filename);
+ if (el != NULL)
+ el_end(el);
+ if (el_hist != NULL)
+ history_end(el_hist);
+ }
+ if (option_verbose)
+ ast_verbose("Executing last minute cleanups\n");
+ ast_run_atexits();
+ /* Called on exit */
+ if (option_verbose && ast_opt_console)
+ ast_verbose("Asterisk %s ending (%d).\n", ast_active_channels() ? "uncleanly" : "cleanly", num);
+ ast_debug(1, "Asterisk ending (%d).\n", num);
+ manager_event(EVENT_FLAG_SYSTEM, "Shutdown", "Shutdown: %s\r\nRestart: %s\r\n", ast_active_channels() ? "Uncleanly" : "Cleanly", restart ? "True" : "False");
+ if (ast_socket > -1) {
+ pthread_cancel(lthread);
+ close(ast_socket);
+ ast_socket = -1;
+ unlink(ast_config_AST_SOCKET);
+ }
+ if (ast_consock > -1)
+ close(ast_consock);
+ if (!ast_opt_remote)
+ unlink(ast_config_AST_PID);
+ printf(term_quit());
+ if (restart) {
+ if (option_verbose || ast_opt_console)
+ ast_verbose("Preparing for Asterisk restart...\n");
+ /* Mark all FD's for closing on exec */
+ for (x=3; x < 32768; x++) {
+ fcntl(x, F_SETFD, FD_CLOEXEC);
+ }
+ if (option_verbose || ast_opt_console)
+ ast_verbose("Asterisk is now restarting...\n");
+ restartnow = 1;
+
+ /* close logger */
+ close_logger();
+
+ /* If there is a consolethread running send it a SIGHUP
+ so it can execvp, otherwise we can do it ourselves */
+ if ((consolethread != AST_PTHREADT_NULL) && (consolethread != pthread_self())) {
+ pthread_kill(consolethread, SIGHUP);
+ /* Give the signal handler some time to complete */
+ sleep(2);
+ } else
+ execvp(_argv[0], _argv);
+
+ } else {
+ /* close logger */
+ close_logger();
+ }
+ exit(0);
+}
+
+static void __quit_handler(int num)
+{
+ int a = 0;
+ sig_flags.need_quit = 1;
+ if (sig_alert_pipe[1] != -1)
+ write(sig_alert_pipe[1], &a, sizeof(a));
+ /* There is no need to restore the signal handler here, since the app
+ * is going to exit */
+}
+
+static const char *fix_header(char *outbuf, int maxout, const char *s, char *cmp)
+{
+ const char *c;
+ if (!strncmp(s, cmp, strlen(cmp))) {
+ c = s + strlen(cmp);
+ term_color(outbuf, cmp, COLOR_GRAY, 0, maxout);
+ return c;
+ }
+ return NULL;
+}
+
+static void console_verboser(const char *s)
+{
+ char tmp[80];
+ const char *c = NULL;
+
+ if ((c = fix_header(tmp, sizeof(tmp), s, VERBOSE_PREFIX_4)) ||
+ (c = fix_header(tmp, sizeof(tmp), s, VERBOSE_PREFIX_3)) ||
+ (c = fix_header(tmp, sizeof(tmp), s, VERBOSE_PREFIX_2)) ||
+ (c = fix_header(tmp, sizeof(tmp), s, VERBOSE_PREFIX_1))) {
+ fputs(tmp, stdout);
+ fputs(c, stdout);
+ } else
+ fputs(s, stdout);
+
+ fflush(stdout);
+
+ /* Wake up a poll()ing console */
+ if (ast_opt_console && consolethread != AST_PTHREADT_NULL)
+ pthread_kill(consolethread, SIGURG);
+}
+
+static int ast_all_zeros(char *s)
+{
+ while (*s) {
+ if (*s > 32)
+ return 0;
+ s++;
+ }
+ return 1;
+}
+
+static void consolehandler(char *s)
+{
+ printf(term_end());
+ fflush(stdout);
+
+ /* Called when readline data is available */
+ if (!ast_all_zeros(s))
+ ast_el_add_history(s);
+ /* The real handler for bang */
+ if (s[0] == '!') {
+ if (s[1])
+ ast_safe_system(s+1);
+ else
+ ast_safe_system(getenv("SHELL") ? getenv("SHELL") : "/bin/sh");
+ } else
+ ast_cli_command(STDOUT_FILENO, s);
+}
+
+static int remoteconsolehandler(char *s)
+{
+ int ret = 0;
+
+ /* Called when readline data is available */
+ if (!ast_all_zeros(s))
+ ast_el_add_history(s);
+ /* The real handler for bang */
+ if (s[0] == '!') {
+ if (s[1])
+ ast_safe_system(s+1);
+ else
+ ast_safe_system(getenv("SHELL") ? getenv("SHELL") : "/bin/sh");
+ ret = 1;
+ }
+ if ((strncasecmp(s, "quit", 4) == 0 || strncasecmp(s, "exit", 4) == 0) &&
+ (s[4] == '\0' || isspace(s[4]))) {
+ quit_handler(0, 0, 0, 0);
+ ret = 1;
+ }
+
+ return ret;
+}
+
+static char *handle_version(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show version";
+ e->usage =
+ "Usage: core show version\n"
+ " Shows Asterisk version information.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+ ast_cli(a->fd, "Asterisk %s built by %s @ %s on a %s running %s on %s\n",
+ ast_get_version(), ast_build_user, ast_build_hostname,
+ ast_build_machine, ast_build_os, ast_build_date);
+ return CLI_SUCCESS;
+}
+
+#if 0
+static int handle_quit(int fd, int argc, char *argv[])
+{
+ if (argc != 1)
+ return RESULT_SHOWUSAGE;
+ quit_handler(0, 0, 1, 0);
+ return RESULT_SUCCESS;
+}
+#endif
+
+static char *handle_stop_now(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "stop now";
+ e->usage =
+ "Usage: stop now\n"
+ " Shuts down a running Asterisk immediately, hanging up all active calls .\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 2)
+ return CLI_SHOWUSAGE;
+ quit_handler(0, 0 /* Not nice */, 1 /* safely */, 0 /* not restart */);
+ return CLI_SUCCESS;
+}
+
+static char *handle_stop_gracefully(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "stop gracefully";
+ e->usage =
+ "Usage: stop gracefully\n"
+ " Causes Asterisk to not accept new calls, and exit when all\n"
+ " active calls have terminated normally.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 2)
+ return CLI_SHOWUSAGE;
+ quit_handler(0, 1 /* nicely */, 1 /* safely */, 0 /* no restart */);
+ return CLI_SUCCESS;
+}
+
+static char *handle_stop_when_convenient(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "stop when convenient";
+ e->usage =
+ "Usage: stop when convenient\n"
+ " Causes Asterisk to perform a shutdown when all active calls have ended.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+ ast_cli(a->fd, "Waiting for inactivity to perform halt\n");
+ quit_handler(0, 2 /* really nicely */, 1 /* safely */, 0 /* don't restart */);
+ return CLI_SUCCESS;
+}
+
+static char *handle_restart_now(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "restart now";
+ e->usage =
+ "Usage: restart now\n"
+ " Causes Asterisk to hangup all calls and exec() itself performing a cold\n"
+ " restart.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 2)
+ return CLI_SHOWUSAGE;
+ quit_handler(0, 0 /* not nicely */, 1 /* safely */, 1 /* restart */);
+ return CLI_SUCCESS;
+}
+
+static char *handle_restart_gracefully(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "restart gracefully";
+ e->usage =
+ "Usage: restart gracefully\n"
+ " Causes Asterisk to stop accepting new calls and exec() itself performing a cold\n"
+ " restart when all active calls have ended.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 2)
+ return CLI_SHOWUSAGE;
+ quit_handler(0, 1 /* nicely */, 1 /* safely */, 1 /* restart */);
+ return CLI_SUCCESS;
+}
+
+static char *handle_restart_when_convenient(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "restart when convenient";
+ e->usage =
+ "Usage: restart when convenient\n"
+ " Causes Asterisk to perform a cold restart when all active calls have ended.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+ ast_cli(a->fd, "Waiting for inactivity to perform restart\n");
+ quit_handler(0, 2 /* really nicely */, 1 /* safely */, 1 /* restart */);
+ return CLI_SUCCESS;
+}
+
+static char *handle_abort_shutdown(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "abort shutdown";
+ e->usage =
+ "Usage: abort shutdown\n"
+ " Causes Asterisk to abort an executing shutdown or restart, and resume normal\n"
+ " call operations.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 2)
+ return CLI_SHOWUSAGE;
+ ast_cancel_shutdown();
+ shuttingdown = 0;
+ return CLI_SUCCESS;
+}
+
+static char *handle_bang(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "!";
+ e->usage =
+ "Usage: !<command>\n"
+ " Executes a given shell command\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ return CLI_SUCCESS;
+}
+static const char warranty_lines[] = {
+ "\n"
+ " NO WARRANTY\n"
+ "\n"
+ "BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\n"
+ "FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN\n"
+ "OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\n"
+ "PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\n"
+ "OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\n"
+ "MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS\n"
+ "TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE\n"
+ "PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\n"
+ "REPAIR OR CORRECTION.\n"
+ "\n"
+ "IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\n"
+ "WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\n"
+ "REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\n"
+ "INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\n"
+ "OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\n"
+ "TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\n"
+ "YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\n"
+ "PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\n"
+ "POSSIBILITY OF SUCH DAMAGES.\n"
+};
+
+static char *show_warranty(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show warranty";
+ e->usage =
+ "Usage: core show warranty\n"
+ " Shows the warranty (if any) for this copy of Asterisk.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ ast_cli(a->fd, warranty_lines);
+
+ return CLI_SUCCESS;
+}
+
+static const char license_lines[] = {
+ "\n"
+ "This program is free software; you can redistribute it and/or modify\n"
+ "it under the terms of the GNU General Public License version 2 as\n"
+ "published by the Free Software Foundation.\n"
+ "\n"
+ "This program also contains components licensed under other licenses.\n"
+ "They include:\n"
+ "\n"
+ "This program is distributed in the hope that it will be useful,\n"
+ "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
+ "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
+ "GNU General Public License for more details.\n"
+ "\n"
+ "You should have received a copy of the GNU General Public License\n"
+ "along with this program; if not, write to the Free Software\n"
+ "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n"
+};
+
+static char *show_license(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show license";
+ e->usage =
+ "Usage: core show license\n"
+ " Shows the license(s) for this copy of Asterisk.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ ast_cli(a->fd, license_lines);
+
+ return CLI_SUCCESS;
+}
+
+#define ASTERISK_PROMPT "*CLI> "
+
+#define ASTERISK_PROMPT2 "%s*CLI> "
+
+static struct ast_cli_entry cli_asterisk[] = {
+ AST_CLI_DEFINE(handle_abort_shutdown, "Cancel a running shutdown"),
+ AST_CLI_DEFINE(handle_stop_now, "Shut down Asterisk immediately"),
+ AST_CLI_DEFINE(handle_stop_gracefully, "Gracefully shut down Asterisk"),
+ AST_CLI_DEFINE(handle_stop_when_convenient, "Shut down Asterisk at empty call volume"),
+ AST_CLI_DEFINE(handle_restart_now, "Restart Asterisk immediately"),
+ AST_CLI_DEFINE(handle_restart_gracefully, "Restart Asterisk gracefully"),
+ AST_CLI_DEFINE(handle_restart_when_convenient, "Restart Asterisk at empty call volume"),
+ AST_CLI_DEFINE(show_warranty, "Show the warranty (if any) for this copy of Asterisk"),
+ AST_CLI_DEFINE(show_license, "Show the license(s) for this copy of Asterisk"),
+ AST_CLI_DEFINE(handle_version, "Display version info"),
+ AST_CLI_DEFINE(handle_bang, "Execute a shell command"),
+#if !defined(LOW_MEMORY)
+ AST_CLI_DEFINE(handle_show_version_files, "List versions of files used to build Asterisk"),
+ AST_CLI_DEFINE(handle_show_threads, "Show running threads"),
+#if defined(HAVE_SYSINFO)
+ AST_CLI_DEFINE(handle_show_sysinfo, "Show System Information"),
+#endif
+ AST_CLI_DEFINE(handle_show_profile, "Display profiling info"),
+ AST_CLI_DEFINE(handle_show_settings, "Show some core settings"),
+ AST_CLI_DEFINE(handle_clear_profile, "Clear profiling info"),
+#endif /* ! LOW_MEMORY */
+};
+
+static int ast_el_read_char(EditLine *el, char *cp)
+{
+ int num_read = 0;
+ int lastpos = 0;
+ struct pollfd fds[2];
+ int res;
+ int max;
+#define EL_BUF_SIZE 512
+ char buf[EL_BUF_SIZE];
+
+ for (;;) {
+ max = 1;
+ fds[0].fd = ast_consock;
+ fds[0].events = POLLIN;
+ if (!ast_opt_exec) {
+ fds[1].fd = STDIN_FILENO;
+ fds[1].events = POLLIN;
+ max++;
+ }
+ res = poll(fds, max, -1);
+ if (res < 0) {
+ if (errno == EINTR)
+ continue;
+ ast_log(LOG_ERROR, "poll failed: %s\n", strerror(errno));
+ break;
+ }
+
+ if (!ast_opt_exec && fds[1].revents) {
+ num_read = read(STDIN_FILENO, cp, 1);
+ if (num_read < 1) {
+ break;
+ } else
+ return (num_read);
+ }
+ if (fds[0].revents) {
+ res = read(ast_consock, buf, sizeof(buf) - 1);
+ /* if the remote side disappears exit */
+ if (res < 1) {
+ fprintf(stderr, "\nDisconnected from Asterisk server\n");
+ if (!ast_opt_reconnect) {
+ quit_handler(0, 0, 0, 0);
+ } else {
+ int tries;
+ int reconnects_per_second = 20;
+ fprintf(stderr, "Attempting to reconnect for 30 seconds\n");
+ for (tries=0; tries < 30 * reconnects_per_second; tries++) {
+ if (ast_tryconnect()) {
+ fprintf(stderr, "Reconnect succeeded after %.3f seconds\n", 1.0 / reconnects_per_second * tries);
+ printf(term_quit());
+ WELCOME_MESSAGE;
+ if (!ast_opt_mute)
+ fdprint(ast_consock, "logger mute silent");
+ else
+ printf("log and verbose output currently muted ('logger mute' to unmute)\n");
+ break;
+ } else
+ usleep(1000000 / reconnects_per_second);
+ }
+ if (tries >= 30 * reconnects_per_second) {
+ fprintf(stderr, "Failed to reconnect for 30 seconds. Quitting.\n");
+ quit_handler(0, 0, 0, 0);
+ }
+ }
+ }
+
+ buf[res] = '\0';
+
+ if (!ast_opt_exec && !lastpos)
+ write(STDOUT_FILENO, "\r", 1);
+ write(STDOUT_FILENO, buf, res);
+ if ((res < EL_BUF_SIZE - 1) && ((buf[res-1] == '\n') || (buf[res-2] == '\n'))) {
+ *cp = CC_REFRESH;
+ return(1);
+ } else
+ lastpos = 1;
+ }
+ }
+
+ *cp = '\0';
+ return (0);
+}
+
+static char *cli_prompt(EditLine *el)
+{
+ static char prompt[200];
+ char *pfmt;
+ int color_used = 0;
+ char term_code[20];
+
+ if ((pfmt = getenv("ASTERISK_PROMPT"))) {
+ char *t = pfmt, *p = prompt;
+ memset(prompt, 0, sizeof(prompt));
+ while (*t != '\0' && *p < sizeof(prompt)) {
+ if (*t == '%') {
+ char hostname[MAXHOSTNAMELEN]="";
+ int i;
+ struct timeval ts = ast_tvnow();
+ struct ast_tm tm = { 0, };
+#ifdef linux
+ FILE *LOADAVG;
+#endif
+ int fgcolor = COLOR_WHITE, bgcolor = COLOR_BLACK;
+
+ t++;
+ switch (*t) {
+ case 'C': /* color */
+ t++;
+ if (sscanf(t, "%d;%d%n", &fgcolor, &bgcolor, &i) == 2) {
+ strncat(p, term_color_code(term_code, fgcolor, bgcolor, sizeof(term_code)),sizeof(prompt) - strlen(prompt) - 1);
+ t += i - 1;
+ } else if (sscanf(t, "%d%n", &fgcolor, &i) == 1) {
+ strncat(p, term_color_code(term_code, fgcolor, 0, sizeof(term_code)),sizeof(prompt) - strlen(prompt) - 1);
+ t += i - 1;
+ }
+
+ /* If the color has been reset correctly, then there's no need to reset it later */
+ color_used = ((fgcolor == COLOR_WHITE) && (bgcolor == COLOR_BLACK)) ? 0 : 1;
+ break;
+ case 'd': /* date */
+ if (ast_localtime(&ts, &tm, NULL))
+ ast_strftime(p, sizeof(prompt) - strlen(prompt), "%Y-%m-%d", &tm);
+ break;
+ case 'h': /* hostname */
+ if (!gethostname(hostname, sizeof(hostname) - 1))
+ strncat(p, hostname, sizeof(prompt) - strlen(prompt) - 1);
+ else
+ strncat(p, "localhost", sizeof(prompt) - strlen(prompt) - 1);
+ break;
+ case 'H': /* short hostname */
+ if (!gethostname(hostname, sizeof(hostname) - 1)) {
+ for (i = 0; i < sizeof(hostname); i++) {
+ if (hostname[i] == '.') {
+ hostname[i] = '\0';
+ break;
+ }
+ }
+ strncat(p, hostname, sizeof(prompt) - strlen(prompt) - 1);
+ } else
+ strncat(p, "localhost", sizeof(prompt) - strlen(prompt) - 1);
+ break;
+#ifdef linux
+ case 'l': /* load avg */
+ t++;
+ if ((LOADAVG = fopen("/proc/loadavg", "r"))) {
+ float avg1, avg2, avg3;
+ int actproc, totproc, npid, which;
+ fscanf(LOADAVG, "%f %f %f %d/%d %d",
+ &avg1, &avg2, &avg3, &actproc, &totproc, &npid);
+ if (sscanf(t, "%d", &which) == 1) {
+ switch (which) {
+ case 1:
+ snprintf(p, sizeof(prompt) - strlen(prompt), "%.2f", avg1);
+ break;
+ case 2:
+ snprintf(p, sizeof(prompt) - strlen(prompt), "%.2f", avg2);
+ break;
+ case 3:
+ snprintf(p, sizeof(prompt) - strlen(prompt), "%.2f", avg3);
+ break;
+ case 4:
+ snprintf(p, sizeof(prompt) - strlen(prompt), "%d/%d", actproc, totproc);
+ break;
+ case 5:
+ snprintf(p, sizeof(prompt) - strlen(prompt), "%d", npid);
+ break;
+ }
+ }
+ }
+ break;
+#endif
+ case 's': /* Asterisk system name (from asterisk.conf) */
+ strncat(p, ast_config_AST_SYSTEM_NAME, sizeof(prompt) - strlen(prompt) - 1);
+ break;
+ case 't': /* time */
+ if (ast_localtime(&ts, &tm, NULL))
+ ast_strftime(p, sizeof(prompt) - strlen(prompt), "%H:%M:%S", &tm);
+ break;
+ case '#': /* process console or remote? */
+ if (!ast_opt_remote)
+ strncat(p, "#", sizeof(prompt) - strlen(prompt) - 1);
+ else
+ strncat(p, ">", sizeof(prompt) - strlen(prompt) - 1);
+ break;
+ case '%': /* literal % */
+ strncat(p, "%", sizeof(prompt) - strlen(prompt) - 1);
+ break;
+ case '\0': /* % is last character - prevent bug */
+ t--;
+ break;
+ }
+ while (*p != '\0')
+ p++;
+ t++;
+ } else {
+ *p = *t;
+ p++;
+ t++;
+ }
+ }
+ if (color_used) {
+ /* Force colors back to normal at end */
+ term_color_code(term_code, COLOR_WHITE, COLOR_BLACK, sizeof(term_code));
+ if (strlen(term_code) > sizeof(prompt) - strlen(prompt))
+ strncat(prompt + sizeof(prompt) - strlen(term_code) - 1, term_code, strlen(term_code));
+ else
+ strncat(p, term_code, sizeof(term_code));
+ }
+ } else if (remotehostname)
+ snprintf(prompt, sizeof(prompt), ASTERISK_PROMPT2, remotehostname);
+ else
+ ast_copy_string(prompt, ASTERISK_PROMPT, sizeof(prompt));
+
+ return(prompt);
+}
+
+static char **ast_el_strtoarr(char *buf)
+{
+ char **match_list = NULL, **match_list_tmp, *retstr;
+ size_t match_list_len;
+ int matches = 0;
+
+ match_list_len = 1;
+ while ( (retstr = strsep(&buf, " ")) != NULL) {
+
+ if (!strcmp(retstr, AST_CLI_COMPLETE_EOF))
+ break;
+ if (matches + 1 >= match_list_len) {
+ match_list_len <<= 1;
+ if ((match_list_tmp = ast_realloc(match_list, match_list_len * sizeof(char *)))) {
+ match_list = match_list_tmp;
+ } else {
+ if (match_list)
+ ast_free(match_list);
+ return (char **) NULL;
+ }
+ }
+
+ match_list[matches++] = ast_strdup(retstr);
+ }
+
+ if (!match_list)
+ return (char **) NULL;
+
+ if (matches >= match_list_len) {
+ if ((match_list_tmp = ast_realloc(match_list, (match_list_len + 1) * sizeof(char *)))) {
+ match_list = match_list_tmp;
+ } else {
+ if (match_list)
+ ast_free(match_list);
+ return (char **) NULL;
+ }
+ }
+
+ match_list[matches] = (char *) NULL;
+
+ return match_list;
+}
+
+static int ast_el_sort_compare(const void *i1, const void *i2)
+{
+ char *s1, *s2;
+
+ s1 = ((char **)i1)[0];
+ s2 = ((char **)i2)[0];
+
+ return strcasecmp(s1, s2);
+}
+
+static int ast_cli_display_match_list(char **matches, int len, int max)
+{
+ int i, idx, limit, count;
+ int screenwidth = 0;
+ int numoutput = 0, numoutputline = 0;
+
+ screenwidth = ast_get_termcols(STDOUT_FILENO);
+
+ /* find out how many entries can be put on one line, with two spaces between strings */
+ limit = screenwidth / (max + 2);
+ if (limit == 0)
+ limit = 1;
+
+ /* how many lines of output */
+ count = len / limit;
+ if (count * limit < len)
+ count++;
+
+ idx = 1;
+
+ qsort(&matches[0], (size_t)(len), sizeof(char *), ast_el_sort_compare);
+
+ for (; count > 0; count--) {
+ numoutputline = 0;
+ for (i=0; i < limit && matches[idx]; i++, idx++) {
+
+ /* Don't print dupes */
+ if ( (matches[idx+1] != NULL && strcmp(matches[idx], matches[idx+1]) == 0 ) ) {
+ i--;
+ ast_free(matches[idx]);
+ matches[idx] = NULL;
+ continue;
+ }
+
+ numoutput++;
+ numoutputline++;
+ fprintf(stdout, "%-*s ", max, matches[idx]);
+ ast_free(matches[idx]);
+ matches[idx] = NULL;
+ }
+ if (numoutputline > 0)
+ fprintf(stdout, "\n");
+ }
+
+ return numoutput;
+}
+
+
+static char *cli_complete(EditLine *el, int ch)
+{
+ int len = 0;
+ char *ptr;
+ int nummatches = 0;
+ char **matches;
+ int retval = CC_ERROR;
+ char buf[2048];
+ int res;
+
+ LineInfo *lf = (LineInfo *)el_line(el);
+
+ *(char *)lf->cursor = '\0';
+ ptr = (char *)lf->cursor;
+ if (ptr) {
+ while (ptr > lf->buffer) {
+ if (isspace(*ptr)) {
+ ptr++;
+ break;
+ }
+ ptr--;
+ }
+ }
+
+ len = lf->cursor - ptr;
+
+ if (ast_opt_remote) {
+ snprintf(buf, sizeof(buf),"_COMMAND NUMMATCHES \"%s\" \"%s\"", lf->buffer, ptr);
+ fdprint(ast_consock, buf);
+ res = read(ast_consock, buf, sizeof(buf));
+ buf[res] = '\0';
+ nummatches = atoi(buf);
+
+ if (nummatches > 0) {
+ char *mbuf;
+ int mlen = 0, maxmbuf = 2048;
+ /* Start with a 2048 byte buffer */
+ if (!(mbuf = ast_malloc(maxmbuf)))
+ return (char *)(CC_ERROR);
+ snprintf(buf, sizeof(buf),"_COMMAND MATCHESARRAY \"%s\" \"%s\"", lf->buffer, ptr);
+ fdprint(ast_consock, buf);
+ res = 0;
+ mbuf[0] = '\0';
+ while (!strstr(mbuf, AST_CLI_COMPLETE_EOF) && res != -1) {
+ if (mlen + 1024 > maxmbuf) {
+ /* Every step increment buffer 1024 bytes */
+ maxmbuf += 1024;
+ if (!(mbuf = ast_realloc(mbuf, maxmbuf)))
+ return (char *)(CC_ERROR);
+ }
+ /* Only read 1024 bytes at a time */
+ res = read(ast_consock, mbuf + mlen, 1024);
+ if (res > 0)
+ mlen += res;
+ }
+ mbuf[mlen] = '\0';
+
+ matches = ast_el_strtoarr(mbuf);
+ ast_free(mbuf);
+ } else
+ matches = (char **) NULL;
+ } else {
+ char **p, *oldbuf=NULL;
+ nummatches = 0;
+ matches = ast_cli_completion_matches((char *)lf->buffer,ptr);
+ for (p = matches; p && *p; p++) {
+ if (!oldbuf || strcmp(*p,oldbuf))
+ nummatches++;
+ oldbuf = *p;
+ }
+ }
+
+ if (matches) {
+ int i;
+ int matches_num, maxlen, match_len;
+
+ if (matches[0][0] != '\0') {
+ el_deletestr(el, (int) len);
+ el_insertstr(el, matches[0]);
+ retval = CC_REFRESH;
+ }
+
+ if (nummatches == 1) {
+ /* Found an exact match */
+ el_insertstr(el, " ");
+ retval = CC_REFRESH;
+ } else {
+ /* Must be more than one match */
+ for (i=1, maxlen=0; matches[i]; i++) {
+ match_len = strlen(matches[i]);
+ if (match_len > maxlen)
+ maxlen = match_len;
+ }
+ matches_num = i - 1;
+ if (matches_num >1) {
+ fprintf(stdout, "\n");
+ ast_cli_display_match_list(matches, nummatches, maxlen);
+ retval = CC_REDISPLAY;
+ } else {
+ el_insertstr(el," ");
+ retval = CC_REFRESH;
+ }
+ }
+ for (i = 0; matches[i]; i++)
+ ast_free(matches[i]);
+ ast_free(matches);
+ }
+
+ return (char *)(long)retval;
+}
+
+static int ast_el_initialize(void)
+{
+ HistEvent ev;
+ char *editor = getenv("AST_EDITOR");
+
+ if (el != NULL)
+ el_end(el);
+ if (el_hist != NULL)
+ history_end(el_hist);
+
+ el = el_init("asterisk", stdin, stdout, stderr);
+ el_set(el, EL_PROMPT, cli_prompt);
+
+ el_set(el, EL_EDITMODE, 1);
+ el_set(el, EL_EDITOR, editor ? editor : "emacs");
+ el_hist = history_init();
+ if (!el || !el_hist)
+ return -1;
+
+ /* setup history with 100 entries */
+ history(el_hist, &ev, H_SETSIZE, 100);
+
+ el_set(el, EL_HIST, history, el_hist);
+
+ el_set(el, EL_ADDFN, "ed-complete", "Complete argument", cli_complete);
+ /* Bind <tab> to command completion */
+ el_set(el, EL_BIND, "^I", "ed-complete", NULL);
+ /* Bind ? to command completion */
+ el_set(el, EL_BIND, "?", "ed-complete", NULL);
+ /* Bind ^D to redisplay */
+ el_set(el, EL_BIND, "^D", "ed-redisplay", NULL);
+
+ return 0;
+}
+
+static int ast_el_add_history(char *buf)
+{
+ HistEvent ev;
+
+ if (el_hist == NULL || el == NULL)
+ ast_el_initialize();
+ if (strlen(buf) > 256)
+ return 0;
+ return (history(el_hist, &ev, H_ENTER, buf));
+}
+
+static int ast_el_write_history(char *filename)
+{
+ HistEvent ev;
+
+ if (el_hist == NULL || el == NULL)
+ ast_el_initialize();
+
+ return (history(el_hist, &ev, H_SAVE, filename));
+}
+
+static int ast_el_read_history(char *filename)
+{
+ char buf[256];
+ FILE *f;
+ int ret = -1;
+
+ if (el_hist == NULL || el == NULL)
+ ast_el_initialize();
+
+ if ((f = fopen(filename, "r")) == NULL)
+ return ret;
+
+ while (!feof(f)) {
+ fgets(buf, sizeof(buf), f);
+ if (!strcmp(buf, "_HiStOrY_V2_\n"))
+ continue;
+ if (ast_all_zeros(buf))
+ continue;
+ if ((ret = ast_el_add_history(buf)) == -1)
+ break;
+ }
+ fclose(f);
+
+ return ret;
+}
+
+static void ast_remotecontrol(char * data)
+{
+ char buf[80];
+ int res;
+ char filename[80] = "";
+ char *hostname;
+ char *cpid;
+ char *version;
+ int pid;
+ char tmp[80];
+ char *stringp = NULL;
+
+ char *ebuf;
+ int num = 0;
+
+ read(ast_consock, buf, sizeof(buf));
+ if (data)
+ write(ast_consock, data, strlen(data) + 1);
+ stringp = buf;
+ hostname = strsep(&stringp, "/");
+ cpid = strsep(&stringp, "/");
+ version = strsep(&stringp, "\n");
+ if (!version)
+ version = "<Version Unknown>";
+ stringp = hostname;
+ strsep(&stringp, ".");
+ if (cpid)
+ pid = atoi(cpid);
+ else
+ pid = -1;
+ if (!data) {
+ snprintf(tmp, sizeof(tmp), "core set verbose atleast %d", option_verbose);
+ fdprint(ast_consock, tmp);
+ snprintf(tmp, sizeof(tmp), "core set debug atleast %d", option_debug);
+ fdprint(ast_consock, tmp);
+ if (!ast_opt_mute)
+ fdprint(ast_consock, "logger mute silent");
+ else
+ printf("log and verbose output currently muted ('logger mute' to unmute)\n");
+ }
+ ast_verbose("Connected to Asterisk %s currently running on %s (pid = %d)\n", version, hostname, pid);
+ remotehostname = hostname;
+ if (getenv("HOME"))
+ snprintf(filename, sizeof(filename), "%s/.asterisk_history", getenv("HOME"));
+ if (el_hist == NULL || el == NULL)
+ ast_el_initialize();
+
+ el_set(el, EL_GETCFN, ast_el_read_char);
+
+ if (!ast_strlen_zero(filename))
+ ast_el_read_history(filename);
+
+ if (ast_opt_exec && data) { /* hack to print output then exit if asterisk -rx is used */
+ char tempchar;
+ struct pollfd fds;
+ fds.fd = ast_consock;
+ fds.events = POLLIN;
+ fds.revents = 0;
+ while (poll(&fds, 1, 100) > 0)
+ ast_el_read_char(el, &tempchar);
+ return;
+ }
+ for (;;) {
+ ebuf = (char *)el_gets(el, &num);
+
+ if (!ebuf && write(1, "", 1) < 0)
+ break;
+
+ if (!ast_strlen_zero(ebuf)) {
+ if (ebuf[strlen(ebuf)-1] == '\n')
+ ebuf[strlen(ebuf)-1] = '\0';
+ if (!remoteconsolehandler(ebuf)) {
+ res = write(ast_consock, ebuf, strlen(ebuf) + 1);
+ if (res < 1) {
+ ast_log(LOG_WARNING, "Unable to write: %s\n", strerror(errno));
+ break;
+ }
+ }
+ }
+ }
+ printf("\nDisconnected from Asterisk server\n");
+}
+
+static int show_version(void)
+{
+ printf("Asterisk %s\n", ast_get_version());
+ return 0;
+}
+
+static int show_cli_help(void) {
+ printf("Asterisk %s, Copyright (C) 1999 - 2007, Digium, Inc. and others.\n", ast_get_version());
+ printf("Usage: asterisk [OPTIONS]\n");
+ printf("Valid Options:\n");
+ printf(" -V Display version number and exit\n");
+ printf(" -C <configfile> Use an alternate configuration file\n");
+ printf(" -G <group> Run as a group other than the caller\n");
+ printf(" -U <user> Run as a user other than the caller\n");
+ printf(" -c Provide console CLI\n");
+ printf(" -d Enable extra debugging\n");
+#if HAVE_WORKING_FORK
+ printf(" -f Do not fork\n");
+ printf(" -F Always fork\n");
+#endif
+ printf(" -g Dump core in case of a crash\n");
+ printf(" -h This help screen\n");
+ printf(" -i Initialize crypto keys at startup\n");
+ printf(" -I Enable internal timing if Zaptel timer is available\n");
+ printf(" -L <load> Limit the maximum load average before rejecting new calls\n");
+ printf(" -M <value> Limit the maximum number of calls to the specified value\n");
+ printf(" -m Mute debugging and console output on the console\n");
+ printf(" -n Disable console colorization\n");
+ printf(" -p Run as pseudo-realtime thread\n");
+ printf(" -q Quiet mode (suppress output)\n");
+ printf(" -r Connect to Asterisk on this machine\n");
+ printf(" -R Same as -r, except attempt to reconnect if disconnected\n");
+ printf(" -t Record soundfiles in /var/tmp and move them where they\n");
+ printf(" belong after they are done\n");
+ printf(" -T Display the time in [Mmm dd hh:mm:ss] format for each line\n");
+ printf(" of output to the CLI\n");
+ printf(" -v Increase verbosity (multiple v's = more verbose)\n");
+ printf(" -x <cmd> Execute command <cmd> (only valid with -r)\n");
+ printf(" -s <socket> Connect to Asterisk via socket <socket> (only valid with -r)\n");
+ printf("\n");
+ return 0;
+}
+
+static void ast_readconfig(void)
+{
+ struct ast_config *cfg;
+ struct ast_variable *v;
+ char *config = DEFAULT_CONFIG_FILE;
+ char hostname[MAXHOSTNAMELEN] = "";
+ struct ast_flags config_flags = { 0 };
+ struct {
+ unsigned int dbdir:1;
+ unsigned int keydir:1;
+ } found = { 0, 0 };
+
+ if (ast_opt_override_config) {
+ cfg = ast_config_load(ast_config_AST_CONFIG_FILE, config_flags);
+ if (!cfg)
+ ast_log(LOG_WARNING, "Unable to open specified master config file '%s', using built-in defaults\n", ast_config_AST_CONFIG_FILE);
+ } else
+ cfg = ast_config_load(config, config_flags);
+
+ /* init with buildtime config */
+ ast_copy_string(cfg_paths.config_dir, DEFAULT_CONFIG_DIR, sizeof(cfg_paths.config_dir));
+ ast_copy_string(cfg_paths.spool_dir, DEFAULT_SPOOL_DIR, sizeof(cfg_paths.spool_dir));
+ ast_copy_string(cfg_paths.module_dir, DEFAULT_MODULE_DIR, sizeof(cfg_paths.module_dir));
+ snprintf(cfg_paths.monitor_dir, sizeof(cfg_paths.monitor_dir) - 1, "%s/monitor", cfg_paths.spool_dir);
+ ast_copy_string(cfg_paths.var_dir, DEFAULT_VAR_DIR, sizeof(cfg_paths.var_dir));
+ ast_copy_string(cfg_paths.data_dir, DEFAULT_DATA_DIR, sizeof(cfg_paths.data_dir));
+ ast_copy_string(cfg_paths.log_dir, DEFAULT_LOG_DIR, sizeof(cfg_paths.log_dir));
+ ast_copy_string(cfg_paths.agi_dir, DEFAULT_AGI_DIR, sizeof(cfg_paths.agi_dir));
+ ast_copy_string(cfg_paths.db_path, DEFAULT_DB, sizeof(cfg_paths.db_path));
+ ast_copy_string(cfg_paths.key_dir, DEFAULT_KEY_DIR, sizeof(cfg_paths.key_dir));
+ ast_copy_string(cfg_paths.pid_path, DEFAULT_PID, sizeof(cfg_paths.pid_path));
+ ast_copy_string(cfg_paths.socket_path, DEFAULT_SOCKET, sizeof(cfg_paths.socket_path));
+ ast_copy_string(cfg_paths.run_dir, DEFAULT_RUN_DIR, sizeof(cfg_paths.run_dir));
+
+ /* no asterisk.conf? no problem, use buildtime config! */
+ if (!cfg) {
+ return;
+ }
+
+ for (v = ast_variable_browse(cfg, "files"); v; v = v->next) {
+ if (!strcasecmp(v->name, "astctlpermissions"))
+ ast_copy_string(ast_config_AST_CTL_PERMISSIONS, v->value, sizeof(ast_config_AST_CTL_PERMISSIONS));
+ else if (!strcasecmp(v->name, "astctlowner"))
+ ast_copy_string(ast_config_AST_CTL_OWNER, v->value, sizeof(ast_config_AST_CTL_OWNER));
+ else if (!strcasecmp(v->name, "astctlgroup"))
+ ast_copy_string(ast_config_AST_CTL_GROUP, v->value, sizeof(ast_config_AST_CTL_GROUP));
+ else if (!strcasecmp(v->name, "astctl"))
+ ast_copy_string(ast_config_AST_CTL, v->value, sizeof(ast_config_AST_CTL));
+ }
+
+ for (v = ast_variable_browse(cfg, "directories"); v; v = v->next) {
+ if (!strcasecmp(v->name, "astetcdir")) {
+ ast_copy_string(cfg_paths.config_dir, v->value, sizeof(cfg_paths.config_dir));
+ } else if (!strcasecmp(v->name, "astspooldir")) {
+ ast_copy_string(cfg_paths.spool_dir, v->value, sizeof(cfg_paths.spool_dir));
+ snprintf(cfg_paths.monitor_dir, sizeof(cfg_paths.monitor_dir) - 1, "%s/monitor", v->value);
+ } else if (!strcasecmp(v->name, "astvarlibdir")) {
+ ast_copy_string(cfg_paths.var_dir, v->value, sizeof(cfg_paths.var_dir));
+ if (!found.dbdir)
+ snprintf(cfg_paths.db_path, sizeof(cfg_paths.db_path), "%s/astdb", v->value);
+ } else if (!strcasecmp(v->name, "astdbdir")) {
+ snprintf(cfg_paths.db_path, sizeof(cfg_paths.db_path), "%s/astdb", v->value);
+ found.dbdir = 1;
+ } else if (!strcasecmp(v->name, "astdatadir")) {
+ ast_copy_string(cfg_paths.data_dir, v->value, sizeof(cfg_paths.data_dir));
+ if (!found.keydir)
+ snprintf(cfg_paths.key_dir, sizeof(cfg_paths.key_dir), "%s/keys", v->value);
+ } else if (!strcasecmp(v->name, "astkeydir")) {
+ snprintf(cfg_paths.key_dir, sizeof(cfg_paths.key_dir), "%s/keys", v->value);
+ found.keydir = 1;
+ } else if (!strcasecmp(v->name, "astlogdir")) {
+ ast_copy_string(cfg_paths.log_dir, v->value, sizeof(cfg_paths.log_dir));
+ } else if (!strcasecmp(v->name, "astagidir")) {
+ ast_copy_string(cfg_paths.agi_dir, v->value, sizeof(cfg_paths.agi_dir));
+ } else if (!strcasecmp(v->name, "astrundir")) {
+ snprintf(cfg_paths.pid_path, sizeof(cfg_paths.pid_path), "%s/%s", v->value, "asterisk.pid");
+ snprintf(cfg_paths.socket_path, sizeof(cfg_paths.socket_path), "%s/%s", v->value, ast_config_AST_CTL);
+ ast_copy_string(cfg_paths.run_dir, v->value, sizeof(cfg_paths.run_dir));
+ } else if (!strcasecmp(v->name, "astmoddir")) {
+ ast_copy_string(cfg_paths.module_dir, v->value, sizeof(cfg_paths.module_dir));
+ }
+ }
+
+ for (v = ast_variable_browse(cfg, "options"); v; v = v->next) {
+ /* verbose level (-v at startup) */
+ if (!strcasecmp(v->name, "verbose")) {
+ option_verbose = atoi(v->value);
+ /* whether or not to force timestamping in CLI verbose output. (-T at startup) */
+ } else if (!strcasecmp(v->name, "timestamp")) {
+ ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_TIMESTAMP);
+ /* whether or not to support #exec in config files */
+ } else if (!strcasecmp(v->name, "execincludes")) {
+ ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_EXEC_INCLUDES);
+ /* debug level (-d at startup) */
+ } else if (!strcasecmp(v->name, "debug")) {
+ option_debug = 0;
+ if (sscanf(v->value, "%d", &option_debug) != 1) {
+ option_debug = ast_true(v->value);
+ }
+#if HAVE_WORKING_FORK
+ /* Disable forking (-f at startup) */
+ } else if (!strcasecmp(v->name, "nofork")) {
+ ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_NO_FORK);
+ /* Always fork, even if verbose or debug are enabled (-F at startup) */
+ } else if (!strcasecmp(v->name, "alwaysfork")) {
+ ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_ALWAYS_FORK);
+#endif
+ /* Run quietly (-q at startup ) */
+ } else if (!strcasecmp(v->name, "quiet")) {
+ ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_QUIET);
+ /* Run as console (-c at startup, implies nofork) */
+ } else if (!strcasecmp(v->name, "console")) {
+ ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_CONSOLE);
+ /* Run with high priority if the O/S permits (-p at startup) */
+ } else if (!strcasecmp(v->name, "highpriority")) {
+ ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_HIGH_PRIORITY);
+ /* Initialize RSA auth keys (IAX2) (-i at startup) */
+ } else if (!strcasecmp(v->name, "initcrypto")) {
+ ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_INIT_KEYS);
+ /* Disable ANSI colors for console (-c at startup) */
+ } else if (!strcasecmp(v->name, "nocolor")) {
+ ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_NO_COLOR);
+ /* Disable some usage warnings for picky people :p */
+ } else if (!strcasecmp(v->name, "dontwarn")) {
+ ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_DONT_WARN);
+ /* Dump core in case of crash (-g) */
+ } else if (!strcasecmp(v->name, "dumpcore")) {
+ ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_DUMP_CORE);
+ /* Cache recorded sound files to another directory during recording */
+ } else if (!strcasecmp(v->name, "cache_record_files")) {
+ ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_CACHE_RECORD_FILES);
+ /* Specify cache directory */
+ } else if (!strcasecmp(v->name, "record_cache_dir")) {
+ ast_copy_string(record_cache_dir, v->value, AST_CACHE_DIR_LEN);
+ /* Build transcode paths via SLINEAR, instead of directly */
+ } else if (!strcasecmp(v->name, "transcode_via_sln")) {
+ ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_TRANSCODE_VIA_SLIN);
+ /* Transmit SLINEAR silence while a channel is being recorded */
+ } else if (!strcasecmp(v->name, "transmit_silence_during_record")) {
+ ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_TRANSMIT_SILENCE);
+ /* Enable internal timing */
+ } else if (!strcasecmp(v->name, "internal_timing")) {
+ ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_INTERNAL_TIMING);
+ } else if (!strcasecmp(v->name, "maxcalls")) {
+ if ((sscanf(v->value, "%d", &option_maxcalls) != 1) || (option_maxcalls < 0)) {
+ option_maxcalls = 0;
+ }
+ } else if (!strcasecmp(v->name, "maxload")) {
+ double test[1];
+
+ if (getloadavg(test, 1) == -1) {
+ ast_log(LOG_ERROR, "Cannot obtain load average on this system. 'maxload' option disabled.\n");
+ option_maxload = 0.0;
+ } else if ((sscanf(v->value, "%lf", &option_maxload) != 1) || (option_maxload < 0.0)) {
+ option_maxload = 0.0;
+ }
+ /* Set the maximum amount of open files */
+ } else if (!strcasecmp(v->name, "maxfiles")) {
+ option_maxfiles = atoi(v->value);
+ set_ulimit(option_maxfiles);
+ /* What user to run as */
+ } else if (!strcasecmp(v->name, "runuser")) {
+ ast_copy_string(cfg_paths.run_user, v->value, sizeof(cfg_paths.run_user));
+ /* What group to run as */
+ } else if (!strcasecmp(v->name, "rungroup")) {
+ ast_copy_string(cfg_paths.run_group, v->value, sizeof(cfg_paths.run_group));
+ } else if (!strcasecmp(v->name, "systemname")) {
+ ast_copy_string(cfg_paths.system_name, v->value, sizeof(cfg_paths.system_name));
+ } else if (!strcasecmp(v->name, "autosystemname")) {
+ if (ast_true(v->value)) {
+ if (!gethostname(hostname, sizeof(hostname) - 1))
+ ast_copy_string(cfg_paths.system_name, hostname, sizeof(cfg_paths.system_name));
+ else {
+ if (ast_strlen_zero(ast_config_AST_SYSTEM_NAME)){
+ ast_copy_string(cfg_paths.system_name, "localhost", sizeof(cfg_paths.system_name));
+ }
+ ast_log(LOG_ERROR, "Cannot obtain hostname for this system. Using '%s' instead.\n", ast_config_AST_SYSTEM_NAME);
+ }
+ }
+ } else if (!strcasecmp(v->name, "languageprefix")) {
+ ast_language_is_prefix = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "lockmode")) {
+ if (!strcasecmp(v->value, "lockfile")) {
+ ast_set_lock_type(AST_LOCK_TYPE_LOCKFILE);
+ } else if (!strcasecmp(v->value, "flock")) {
+ ast_set_lock_type(AST_LOCK_TYPE_FLOCK);
+ } else {
+ ast_log(LOG_WARNING, "'%s' is not a valid setting for the lockmode option, "
+ "defaulting to 'lockfile'\n", v->value);
+ ast_set_lock_type(AST_LOCK_TYPE_LOCKFILE);
+ }
+#if defined(HAVE_SYSINFO)
+ } else if (!strcasecmp(v->name, "minmemfree")) {
+ /* specify the minimum amount of free memory to retain. Asterisk should stop accepting new calls
+ * if the amount of free memory falls below this watermark */
+ if ((sscanf(v->value, "%ld", &option_minmemfree) != 1) || (option_minmemfree < 0)) {
+ option_minmemfree = 0;
+ }
+#endif
+ }
+ }
+ ast_config_destroy(cfg);
+}
+
+static void *monitor_sig_flags(void *unused)
+{
+ for (;;) {
+ struct pollfd p = { sig_alert_pipe[0], POLLIN, 0 };
+ int a;
+ poll(&p, 1, -1);
+ if (sig_flags.need_reload) {
+ sig_flags.need_reload = 0;
+ ast_module_reload(NULL);
+ }
+ if (sig_flags.need_quit) {
+ sig_flags.need_quit = 0;
+ quit_handler(0, 0, 1, 0);
+ }
+ read(sig_alert_pipe[0], &a, sizeof(a));
+ }
+
+ return NULL;
+}
+
+static void *canary_thread(void *unused)
+{
+ struct stat canary_stat;
+ struct timeval tv;
+
+ /* Give the canary time to sing */
+ sleep(120);
+
+ for (;;) {
+ stat(canary_filename, &canary_stat);
+ tv = ast_tvnow();
+ if (tv.tv_sec > canary_stat.st_mtime + 60) {
+ ast_log(LOG_WARNING, "The canary is no more. He has ceased to be! He's expired and gone to meet his maker! He's a stiff! Bereft of life, he rests in peace. His metabolic processes are now history! He's off the twig! He's kicked the bucket. He's shuffled off his mortal coil, run down the curtain, and joined the bleeding choir invisible!! THIS is an EX-CANARY. (Reducing priority)\n");
+ ast_set_priority(0);
+ pthread_exit(NULL);
+ }
+
+ /* Check the canary once a minute */
+ sleep(60);
+ }
+}
+
+/* Used by libc's atexit(3) function */
+static void canary_exit(void)
+{
+ if (canary_pid > 0)
+ kill(canary_pid, SIGKILL);
+}
+
+static void run_startup_commands(void)
+{
+ char filename[PATH_MAX];
+ char buf[256];
+ FILE *f;
+ int fd;
+
+ fd = open("/dev/null", O_RDWR);
+ if (fd < 0)
+ return;
+
+ snprintf(filename, sizeof(filename), "%s/startup_commands", ast_config_AST_CONFIG_DIR);
+
+ if (!(f = fopen(filename, "r"))) {
+ close(fd);
+ return;
+ }
+
+ while (fgets(buf, sizeof(buf), f)) {
+ size_t res = strlen(buf);
+
+ if (!res)
+ continue;
+
+ if (buf[res - 1] == '\n')
+ buf[res - 1] = '\0';
+
+ ast_cli_command(fd, buf);
+ }
+
+ fclose(f);
+ close(fd);
+}
+
+int main(int argc, char *argv[])
+{
+ int c;
+ char filename[80] = "";
+ char hostname[MAXHOSTNAMELEN] = "";
+ char tmp[80];
+ char * xarg = NULL;
+ int x;
+ FILE *f;
+ sigset_t sigs;
+ int num;
+ int isroot = 1;
+ char *buf;
+ const char *runuser = NULL, *rungroup = NULL;
+ char *remotesock = NULL;
+
+ /* Remember original args for restart */
+ if (argc > sizeof(_argv) / sizeof(_argv[0]) - 1) {
+ fprintf(stderr, "Truncating argument size to %d\n", (int)(sizeof(_argv) / sizeof(_argv[0])) - 1);
+ argc = sizeof(_argv) / sizeof(_argv[0]) - 1;
+ }
+ for (x=0; x<argc; x++)
+ _argv[x] = argv[x];
+ _argv[x] = NULL;
+
+ if (geteuid() != 0)
+ isroot = 0;
+
+ /* if the progname is rasterisk consider it a remote console */
+ if (argv[0] && (strstr(argv[0], "rasterisk")) != NULL) {
+ ast_set_flag(&ast_options, AST_OPT_FLAG_NO_FORK | AST_OPT_FLAG_REMOTE);
+ }
+ if (gethostname(hostname, sizeof(hostname)-1))
+ ast_copy_string(hostname, "<Unknown>", sizeof(hostname));
+ ast_mainpid = getpid();
+ ast_ulaw_init();
+ ast_alaw_init();
+ callerid_init();
+ ast_builtins_init();
+ ast_utils_init();
+ tdd_init();
+
+ if (getenv("HOME"))
+ snprintf(filename, sizeof(filename), "%s/.asterisk_history", getenv("HOME"));
+ /* Check for options */
+ while ((c = getopt(argc, argv, "mtThfFdvVqprRgciInx:U:G:C:L:M:e:s:")) != -1) {
+ switch (c) {
+#if defined(HAVE_SYSINFO)
+ case 'e':
+ if ((sscanf(&optarg[1], "%ld", &option_minmemfree) != 1) || (option_minmemfree < 0)) {
+ option_minmemfree = 0;
+ }
+ break;
+#endif
+#if HAVE_WORKING_FORK
+ case 'F':
+ ast_set_flag(&ast_options, AST_OPT_FLAG_ALWAYS_FORK);
+ break;
+ case 'f':
+ ast_set_flag(&ast_options, AST_OPT_FLAG_NO_FORK);
+ break;
+#endif
+ case 'd':
+ option_debug++;
+ ast_set_flag(&ast_options, AST_OPT_FLAG_NO_FORK);
+ break;
+ case 'c':
+ ast_set_flag(&ast_options, AST_OPT_FLAG_NO_FORK | AST_OPT_FLAG_CONSOLE);
+ break;
+ case 'n':
+ ast_set_flag(&ast_options, AST_OPT_FLAG_NO_COLOR);
+ break;
+ case 'r':
+ ast_set_flag(&ast_options, AST_OPT_FLAG_NO_FORK | AST_OPT_FLAG_REMOTE);
+ break;
+ case 'R':
+ ast_set_flag(&ast_options, AST_OPT_FLAG_NO_FORK | AST_OPT_FLAG_REMOTE | AST_OPT_FLAG_RECONNECT);
+ break;
+ case 'p':
+ ast_set_flag(&ast_options, AST_OPT_FLAG_HIGH_PRIORITY);
+ break;
+ case 'v':
+ option_verbose++;
+ ast_set_flag(&ast_options, AST_OPT_FLAG_NO_FORK);
+ break;
+ case 'm':
+ ast_set_flag(&ast_options, AST_OPT_FLAG_MUTE);
+ break;
+ case 'M':
+ if ((sscanf(optarg, "%d", &option_maxcalls) != 1) || (option_maxcalls < 0))
+ option_maxcalls = 0;
+ break;
+ case 'L':
+ if ((sscanf(optarg, "%lf", &option_maxload) != 1) || (option_maxload < 0.0))
+ option_maxload = 0.0;
+ break;
+ case 'q':
+ ast_set_flag(&ast_options, AST_OPT_FLAG_QUIET);
+ break;
+ case 't':
+ ast_set_flag(&ast_options, AST_OPT_FLAG_CACHE_RECORD_FILES);
+ break;
+ case 'T':
+ ast_set_flag(&ast_options, AST_OPT_FLAG_TIMESTAMP);
+ break;
+ case 'x':
+ ast_set_flag(&ast_options, AST_OPT_FLAG_EXEC);
+ xarg = ast_strdupa(optarg);
+ break;
+ case 'C':
+ ast_copy_string(cfg_paths.config_file, optarg, sizeof(cfg_paths.config_file));
+ ast_set_flag(&ast_options, AST_OPT_FLAG_OVERRIDE_CONFIG);
+ break;
+ case 'I':
+ ast_set_flag(&ast_options, AST_OPT_FLAG_INTERNAL_TIMING);
+ break;
+ case 'i':
+ ast_set_flag(&ast_options, AST_OPT_FLAG_INIT_KEYS);
+ break;
+ case 'g':
+ ast_set_flag(&ast_options, AST_OPT_FLAG_DUMP_CORE);
+ break;
+ case 'h':
+ show_cli_help();
+ exit(0);
+ case 'V':
+ show_version();
+ exit(0);
+ case 'U':
+ runuser = ast_strdupa(optarg);
+ break;
+ case 'G':
+ rungroup = ast_strdupa(optarg);
+ break;
+ case 's':
+ remotesock = ast_strdupa(optarg);
+ break;
+ case '?':
+ exit(1);
+ }
+ }
+
+ if (ast_opt_console || option_verbose || (ast_opt_remote && !ast_opt_exec)) {
+ ast_register_verbose(console_verboser);
+ WELCOME_MESSAGE;
+ }
+
+ if (ast_opt_console && !option_verbose)
+ ast_verbose("[ Booting...\n");
+
+ if (ast_opt_always_fork && (ast_opt_remote || ast_opt_console)) {
+ ast_log(LOG_WARNING, "'alwaysfork' is not compatible with console or remote console mode; ignored\n");
+ ast_clear_flag(&ast_options, AST_OPT_FLAG_ALWAYS_FORK);
+ }
+
+ /* For remote connections, change the name of the remote connection.
+ * We do this for the benefit of init scripts (which need to know if/when
+ * the main asterisk process has died yet). */
+ if (ast_opt_remote) {
+ strcpy(argv[0], "rasterisk");
+ for (x = 1; x < argc; x++) {
+ argv[x] = argv[0] + 10;
+ }
+ }
+
+ if (ast_opt_console && !option_verbose)
+ ast_verbose("[ Reading Master Configuration ]\n");
+ ast_readconfig();
+
+ if (ast_opt_remote && remotesock != NULL)
+ ast_copy_string((char *) cfg_paths.socket_path, remotesock, sizeof(cfg_paths.socket_path));
+
+ if (!ast_language_is_prefix && !ast_opt_remote)
+ ast_log(LOG_WARNING, "The 'languageprefix' option in asterisk.conf is deprecated; in a future release it will be removed, and your sound files will need to be organized in the 'new style' language layout.\n");
+
+ if (ast_opt_dump_core) {
+ struct rlimit l;
+ memset(&l, 0, sizeof(l));
+ l.rlim_cur = RLIM_INFINITY;
+ l.rlim_max = RLIM_INFINITY;
+ if (setrlimit(RLIMIT_CORE, &l)) {
+ ast_log(LOG_WARNING, "Unable to disable core size resource limit: %s\n", strerror(errno));
+ }
+ }
+
+ if ((!rungroup) && !ast_strlen_zero(ast_config_AST_RUN_GROUP))
+ rungroup = ast_config_AST_RUN_GROUP;
+ if ((!runuser) && !ast_strlen_zero(ast_config_AST_RUN_USER))
+ runuser = ast_config_AST_RUN_USER;
+
+ /* Must install this signal handler up here to ensure that if the canary
+ * fails to execute that it doesn't kill the Asterisk process.
+ */
+ signal(SIGCHLD, child_handler);
+
+#ifndef __CYGWIN__
+
+ if (isroot) {
+ ast_set_priority(ast_opt_high_priority);
+ if (ast_opt_high_priority) {
+ snprintf(canary_filename, sizeof(canary_filename), "%s/alt.asterisk.canary.tweet.tweet.tweet", ast_config_AST_RUN_DIR);
+
+ canary_pid = fork();
+ if (canary_pid == 0) {
+ char canary_binary[128], *lastslash;
+ int fd;
+
+ /* Reset signal handler */
+ signal(SIGCHLD, SIG_DFL);
+
+ for (fd = 0; fd < 100; fd++)
+ close(fd);
+
+ execlp("astcanary", "astcanary", canary_filename, (char *)NULL);
+
+ /* If not found, try the same path as used to execute asterisk */
+ ast_copy_string(canary_binary, argv[0], sizeof(canary_binary));
+ if ((lastslash = strrchr(canary_binary, '/'))) {
+ ast_copy_string(lastslash + 1, "astcanary", sizeof(canary_binary) + canary_binary - (lastslash + 1));
+ execl(canary_binary, "astcanary", canary_filename, (char *)NULL);
+ }
+
+ /* Should never happen */
+ _exit(1);
+ } else if (canary_pid > 0) {
+ pthread_t dont_care;
+ ast_pthread_create_detached(&dont_care, NULL, canary_thread, NULL);
+ }
+
+ /* Kill the canary when we exit */
+ atexit(canary_exit);
+ }
+ }
+
+ if (isroot && rungroup) {
+ struct group *gr;
+ gr = getgrnam(rungroup);
+ if (!gr) {
+ ast_log(LOG_WARNING, "No such group '%s'!\n", rungroup);
+ exit(1);
+ }
+ if (setgid(gr->gr_gid)) {
+ ast_log(LOG_WARNING, "Unable to setgid to %d (%s)\n", (int)gr->gr_gid, rungroup);
+ exit(1);
+ }
+ if (setgroups(0, NULL)) {
+ ast_log(LOG_WARNING, "Unable to drop unneeded groups\n");
+ exit(1);
+ }
+ if (option_verbose)
+ ast_verbose("Running as group '%s'\n", rungroup);
+ }
+
+ if (runuser && !ast_test_flag(&ast_options, AST_OPT_FLAG_REMOTE)) {
+#ifdef HAVE_CAP
+ int has_cap = 1;
+#endif /* HAVE_CAP */
+ struct passwd *pw;
+ pw = getpwnam(runuser);
+ if (!pw) {
+ ast_log(LOG_WARNING, "No such user '%s'!\n", runuser);
+ exit(1);
+ }
+#ifdef HAVE_CAP
+ if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0)) {
+ ast_log(LOG_WARNING, "Unable to keep capabilities.\n");
+ has_cap = 0;
+ }
+#endif /* HAVE_CAP */
+ if (!isroot && pw->pw_uid != geteuid()) {
+ ast_log(LOG_ERROR, "Asterisk started as nonroot, but runuser '%s' requested.\n", runuser);
+ exit(1);
+ }
+ if (!rungroup) {
+ if (setgid(pw->pw_gid)) {
+ ast_log(LOG_WARNING, "Unable to setgid to %d!\n", (int)pw->pw_gid);
+ exit(1);
+ }
+ if (isroot && initgroups(pw->pw_name, pw->pw_gid)) {
+ ast_log(LOG_WARNING, "Unable to init groups for '%s'\n", runuser);
+ exit(1);
+ }
+ }
+ if (setuid(pw->pw_uid)) {
+ ast_log(LOG_WARNING, "Unable to setuid to %d (%s)\n", (int)pw->pw_uid, runuser);
+ exit(1);
+ }
+ if (option_verbose)
+ ast_verbose("Running as user '%s'\n", runuser);
+#ifdef HAVE_CAP
+ if (has_cap) {
+ cap_t cap;
+
+ cap = cap_from_text("cap_net_admin=ep");
+
+ if (cap_set_proc(cap))
+ ast_log(LOG_WARNING, "Unable to install capabilities.\n");
+
+ if (cap_free(cap))
+ ast_log(LOG_WARNING, "Unable to drop capabilities.\n");
+ }
+#endif /* HAVE_CAP */
+ }
+
+#endif /* __CYGWIN__ */
+
+#ifdef linux
+ if (geteuid() && ast_opt_dump_core) {
+ if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) < 0) {
+ ast_log(LOG_WARNING, "Unable to set the process for core dumps after changing to a non-root user. %s\n", strerror(errno));
+ }
+ }
+#endif
+
+ ast_term_init();
+ printf(term_end());
+ fflush(stdout);
+
+ if (ast_opt_console && !option_verbose)
+ ast_verbose("[ Initializing Custom Configuration Options ]\n");
+ /* custom config setup */
+ register_config_cli();
+ read_config_maps();
+
+ if (ast_opt_console) {
+ if (el_hist == NULL || el == NULL)
+ ast_el_initialize();
+
+ if (!ast_strlen_zero(filename))
+ ast_el_read_history(filename);
+ }
+
+ if (ast_tryconnect()) {
+ /* One is already running */
+ if (ast_opt_remote) {
+ if (ast_opt_exec) {
+ ast_remotecontrol(xarg);
+ quit_handler(0, 0, 0, 0);
+ exit(0);
+ }
+ printf(term_quit());
+ ast_remotecontrol(NULL);
+ quit_handler(0, 0, 0, 0);
+ exit(0);
+ } else {
+ ast_log(LOG_ERROR, "Asterisk already running on %s. Use 'asterisk -r' to connect.\n", ast_config_AST_SOCKET);
+ printf(term_quit());
+ exit(1);
+ }
+ } else if (ast_opt_remote || ast_opt_exec) {
+ ast_log(LOG_ERROR, "Unable to connect to remote asterisk (does %s exist?)\n", ast_config_AST_SOCKET);
+ printf(term_quit());
+ exit(1);
+ }
+ /* Blindly write pid file since we couldn't connect */
+ unlink(ast_config_AST_PID);
+ f = fopen(ast_config_AST_PID, "w");
+ if (f) {
+ fprintf(f, "%ld\n", (long)getpid());
+ fclose(f);
+ } else
+ ast_log(LOG_WARNING, "Unable to open pid file '%s': %s\n", ast_config_AST_PID, strerror(errno));
+
+#if HAVE_WORKING_FORK
+ if (ast_opt_always_fork || !ast_opt_no_fork) {
+ daemon(1, 0);
+ ast_mainpid = getpid();
+ /* Blindly re-write pid file since we are forking */
+ unlink(ast_config_AST_PID);
+ f = fopen(ast_config_AST_PID, "w");
+ if (f) {
+ fprintf(f, "%ld\n", (long)ast_mainpid);
+ fclose(f);
+ } else
+ ast_log(LOG_WARNING, "Unable to open pid file '%s': %s\n", ast_config_AST_PID, strerror(errno));
+ }
+#endif
+
+ /* Test recursive mutex locking. */
+ if (test_for_thread_safety())
+ ast_verbose("Warning! Asterisk is not thread safe.\n");
+
+ ast_event_init();
+
+ ast_makesocket();
+ sigemptyset(&sigs);
+ sigaddset(&sigs, SIGHUP);
+ sigaddset(&sigs, SIGTERM);
+ sigaddset(&sigs, SIGINT);
+ sigaddset(&sigs, SIGPIPE);
+ sigaddset(&sigs, SIGWINCH);
+ pthread_sigmask(SIG_BLOCK, &sigs, NULL);
+ signal(SIGURG, urg_handler);
+ signal(SIGINT, __quit_handler);
+ signal(SIGTERM, __quit_handler);
+ signal(SIGHUP, hup_handler);
+ signal(SIGPIPE, SIG_IGN);
+
+ /* ensure that the random number generators are seeded with a different value every time
+ Asterisk is started
+ */
+ srand((unsigned int) getpid() + (unsigned int) time(NULL));
+ initstate((unsigned int) getpid() * 65536 + (unsigned int) time(NULL), randompool, sizeof(randompool));
+
+ if (init_logger()) { /* Start logging subsystem */
+ printf(term_quit());
+ exit(1);
+ }
+
+ threadstorage_init();
+
+ astobj2_init();
+
+ if (load_modules(1)) { /* Load modules, pre-load only */
+ printf(term_quit());
+ exit(1);
+ }
+
+ if (dnsmgr_init()) { /* Initialize the DNS manager */
+ printf(term_quit());
+ exit(1);
+ }
+
+ ast_http_init(); /* Start the HTTP server, if needed */
+
+ ast_channels_init();
+
+ if (init_manager()) {
+ printf(term_quit());
+ exit(1);
+ }
+
+ if (ast_cdr_engine_init()) {
+ printf(term_quit());
+ exit(1);
+ }
+
+ if (ast_device_state_engine_init()) {
+ printf(term_quit());
+ exit(1);
+ }
+
+ ast_rtp_init();
+
+ ast_udptl_init();
+
+ if (ast_image_init()) {
+ printf(term_quit());
+ exit(1);
+ }
+
+ if (ast_file_init()) {
+ printf(term_quit());
+ exit(1);
+ }
+
+ if (load_pbx()) {
+ printf(term_quit());
+ exit(1);
+ }
+
+ if (init_framer()) {
+ printf(term_quit());
+ exit(1);
+ }
+
+ if (astdb_init()) {
+ printf(term_quit());
+ exit(1);
+ }
+
+ if (ast_enum_init()) {
+ printf(term_quit());
+ exit(1);
+ }
+
+ if (load_modules(0)) {
+ printf(term_quit());
+ exit(1);
+ }
+
+ dnsmgr_start_refresh();
+
+ /* We might have the option of showing a console, but for now just
+ do nothing... */
+ if (ast_opt_console && !option_verbose)
+ ast_verbose(" ]\n");
+ if (option_verbose || ast_opt_console)
+ ast_verbose(term_color(tmp, "Asterisk Ready.\n", COLOR_BRWHITE, COLOR_BLACK, sizeof(tmp)));
+ if (ast_opt_no_fork)
+ consolethread = pthread_self();
+
+ if (pipe(sig_alert_pipe))
+ sig_alert_pipe[0] = sig_alert_pipe[1] = -1;
+
+ ast_set_flag(&ast_options, AST_OPT_FLAG_FULLY_BOOTED);
+ pthread_sigmask(SIG_UNBLOCK, &sigs, NULL);
+
+#ifdef __AST_DEBUG_MALLOC
+ __ast_mm_init();
+#endif
+
+ ast_lastreloadtime = ast_startuptime = ast_tvnow();
+ ast_cli_register_multiple(cli_asterisk, sizeof(cli_asterisk) / sizeof(struct ast_cli_entry));
+
+ run_startup_commands();
+
+ if (ast_opt_console) {
+ /* Console stuff now... */
+ /* Register our quit function */
+ char title[256];
+ pthread_t dont_care;
+
+ ast_pthread_create_detached(&dont_care, NULL, monitor_sig_flags, NULL);
+
+ set_icon("Asterisk");
+ snprintf(title, sizeof(title), "Asterisk Console on '%s' (pid %ld)", hostname, (long)ast_mainpid);
+ set_title(title);
+
+ for (;;) {
+ buf = (char *) el_gets(el, &num);
+
+ if (!buf && write(1, "", 1) < 0)
+ goto lostterm;
+
+ if (buf) {
+ if (buf[strlen(buf)-1] == '\n')
+ buf[strlen(buf)-1] = '\0';
+
+ consolehandler((char *)buf);
+ } else if (ast_opt_remote && (write(STDOUT_FILENO, "\nUse EXIT or QUIT to exit the asterisk console\n",
+ strlen("\nUse EXIT or QUIT to exit the asterisk console\n")) < 0)) {
+ /* Whoa, stdout disappeared from under us... Make /dev/null's */
+ int fd;
+ fd = open("/dev/null", O_RDWR);
+ if (fd > -1) {
+ dup2(fd, STDOUT_FILENO);
+ dup2(fd, STDIN_FILENO);
+ } else
+ ast_log(LOG_WARNING, "Failed to open /dev/null to recover from dead console. Bad things will happen!\n");
+ break;
+ }
+ }
+ }
+
+ monitor_sig_flags(NULL);
+
+lostterm:
+ return 0;
+}
diff --git a/trunk/main/astmm.c b/trunk/main/astmm.c
new file mode 100644
index 000000000..3d180acec
--- /dev/null
+++ b/trunk/main/astmm.c
@@ -0,0 +1,479 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006, 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.
+ */
+
+/*! \file
+ *
+ * \brief Memory Management
+ *
+ * \author Mark Spencer <markster@digium.com>
+ */
+
+#ifdef __AST_DEBUG_MALLOC
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/paths.h" /* use ast_config_AST_LOG_DIR */
+#include <time.h>
+
+#include "asterisk/cli.h"
+#include "asterisk/lock.h"
+#include "asterisk/strings.h"
+#include "asterisk/unaligned.h"
+
+#define SOME_PRIME 563
+
+enum func_type {
+ FUNC_CALLOC = 1,
+ FUNC_MALLOC,
+ FUNC_REALLOC,
+ FUNC_STRDUP,
+ FUNC_STRNDUP,
+ FUNC_VASPRINTF,
+ FUNC_ASPRINTF
+};
+
+/* Undefine all our macros */
+#undef malloc
+#undef calloc
+#undef realloc
+#undef strdup
+#undef strndup
+#undef free
+#undef vasprintf
+#undef asprintf
+
+#define FENCE_MAGIC 0xdeadbeef
+
+static FILE *mmlog;
+
+static struct ast_region {
+ struct ast_region *next;
+ char file[40];
+ char func[40];
+ unsigned int lineno;
+ enum func_type which;
+ unsigned int cache; /* region was allocated as part of a cache pool */
+ size_t len;
+ unsigned int fence;
+ unsigned char data[0];
+} *regions[SOME_PRIME];
+
+#define HASH(a) \
+ (((unsigned long)(a)) % SOME_PRIME)
+
+/*! Tracking this mutex will cause infinite recursion, as the mutex tracking
+ * code allocates memory */
+AST_MUTEX_DEFINE_STATIC_NOTRACKING(reglock);
+
+#define astmm_log(...) \
+ do { \
+ fprintf(stderr, __VA_ARGS__); \
+ if (mmlog) { \
+ fprintf(mmlog, __VA_ARGS__); \
+ fflush(mmlog); \
+ } \
+ } while (0)
+
+static inline void *__ast_alloc_region(size_t size, const enum func_type which, const char *file, int lineno, const char *func, unsigned int cache)
+{
+ struct ast_region *reg;
+ void *ptr = NULL;
+ unsigned int *fence;
+ int hash;
+
+ if (!(reg = malloc(size + sizeof(*reg) + sizeof(*fence)))) {
+ astmm_log("Memory Allocation Failure - '%d' bytes in function %s "
+ "at line %d of %s\n", (int) size, func, lineno, file);
+ }
+
+ ast_copy_string(reg->file, file, sizeof(reg->file));
+ ast_copy_string(reg->func, func, sizeof(reg->func));
+ reg->lineno = lineno;
+ reg->len = size;
+ reg->which = which;
+ reg->cache = cache;
+ ptr = reg->data;
+ hash = HASH(ptr);
+ reg->fence = FENCE_MAGIC;
+ fence = (ptr + reg->len);
+ put_unaligned_uint32(fence, FENCE_MAGIC);
+
+ ast_mutex_lock(&reglock);
+ reg->next = regions[hash];
+ regions[hash] = reg;
+ ast_mutex_unlock(&reglock);
+
+ return ptr;
+}
+
+static inline size_t __ast_sizeof_region(void *ptr)
+{
+ int hash = HASH(ptr);
+ struct ast_region *reg;
+ size_t len = 0;
+
+ ast_mutex_lock(&reglock);
+ for (reg = regions[hash]; reg; reg = reg->next) {
+ if (reg->data == ptr) {
+ len = reg->len;
+ break;
+ }
+ }
+ ast_mutex_unlock(&reglock);
+
+ return len;
+}
+
+static void __ast_free_region(void *ptr, const char *file, int lineno, const char *func)
+{
+ int hash = HASH(ptr);
+ struct ast_region *reg, *prev = NULL;
+ unsigned int *fence;
+
+ ast_mutex_lock(&reglock);
+ for (reg = regions[hash]; reg; reg = reg->next) {
+ if (reg->data == ptr) {
+ if (prev)
+ prev->next = reg->next;
+ else
+ regions[hash] = reg->next;
+ break;
+ }
+ prev = reg;
+ }
+ ast_mutex_unlock(&reglock);
+
+ if (reg) {
+ fence = (unsigned int *)(reg->data + reg->len);
+ if (reg->fence != FENCE_MAGIC) {
+ astmm_log("WARNING: Low fence violation at %p, in %s of %s, "
+ "line %d\n", reg->data, reg->func, reg->file, reg->lineno);
+ }
+ if (get_unaligned_uint32(fence) != FENCE_MAGIC) {
+ astmm_log("WARNING: High fence violation at %p, in %s of %s, "
+ "line %d\n", reg->data, reg->func, reg->file, reg->lineno);
+ }
+ free(reg);
+ } else {
+ astmm_log("WARNING: Freeing unused memory at %p, in %s of %s, line %d\n",
+ ptr, func, file, lineno);
+ }
+}
+
+void *__ast_calloc(size_t nmemb, size_t size, const char *file, int lineno, const char *func)
+{
+ void *ptr;
+
+ if ((ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func, 0)))
+ memset(ptr, 0, size * nmemb);
+
+ return ptr;
+}
+
+void *__ast_calloc_cache(size_t nmemb, size_t size, const char *file, int lineno, const char *func)
+{
+ void *ptr;
+
+ if ((ptr = __ast_alloc_region(size * nmemb, FUNC_CALLOC, file, lineno, func, 1)))
+ memset(ptr, 0, size * nmemb);
+
+ return ptr;
+}
+
+void *__ast_malloc(size_t size, const char *file, int lineno, const char *func)
+{
+ return __ast_alloc_region(size, FUNC_MALLOC, file, lineno, func, 0);
+}
+
+void __ast_free(void *ptr, const char *file, int lineno, const char *func)
+{
+ __ast_free_region(ptr, file, lineno, func);
+}
+
+void *__ast_realloc(void *ptr, size_t size, const char *file, int lineno, const char *func)
+{
+ void *tmp;
+ size_t len = 0;
+
+ if (ptr && !(len = __ast_sizeof_region(ptr))) {
+ astmm_log("WARNING: Realloc of unalloced memory at %p, in %s of %s, "
+ "line %d\n", ptr, func, file, lineno);
+ return NULL;
+ }
+
+ if (!(tmp = __ast_alloc_region(size, FUNC_REALLOC, file, lineno, func, 0)))
+ return NULL;
+
+ if (len > size)
+ len = size;
+ if (ptr) {
+ memcpy(tmp, ptr, len);
+ __ast_free_region(ptr, file, lineno, func);
+ }
+
+ return tmp;
+}
+
+char *__ast_strdup(const char *s, const char *file, int lineno, const char *func)
+{
+ size_t len;
+ void *ptr;
+
+ if (!s)
+ return NULL;
+
+ len = strlen(s) + 1;
+ if ((ptr = __ast_alloc_region(len, FUNC_STRDUP, file, lineno, func, 0)))
+ strcpy(ptr, s);
+
+ return ptr;
+}
+
+char *__ast_strndup(const char *s, size_t n, const char *file, int lineno, const char *func)
+{
+ size_t len;
+ void *ptr;
+
+ if (!s)
+ return NULL;
+
+ len = strlen(s) + 1;
+ if (len > n)
+ len = n;
+ if ((ptr = __ast_alloc_region(len, FUNC_STRNDUP, file, lineno, func, 0)))
+ strcpy(ptr, s);
+
+ return ptr;
+}
+
+int __ast_asprintf(const char *file, int lineno, const char *func, char **strp, const char *fmt, ...)
+{
+ int size;
+ va_list ap, ap2;
+ char s;
+
+ *strp = NULL;
+ va_start(ap, fmt);
+ va_copy(ap2, ap);
+ size = vsnprintf(&s, 1, fmt, ap2);
+ va_end(ap2);
+ if (!(*strp = __ast_alloc_region(size + 1, FUNC_ASPRINTF, file, lineno, func, 0))) {
+ va_end(ap);
+ return -1;
+ }
+ vsnprintf(*strp, size + 1, fmt, ap);
+ va_end(ap);
+
+ return size;
+}
+
+int __ast_vasprintf(char **strp, const char *fmt, va_list ap, const char *file, int lineno, const char *func)
+{
+ int size;
+ va_list ap2;
+ char s;
+
+ *strp = NULL;
+ va_copy(ap2, ap);
+ size = vsnprintf(&s, 1, fmt, ap2);
+ va_end(ap2);
+ if (!(*strp = __ast_alloc_region(size + 1, FUNC_VASPRINTF, file, lineno, func, 0))) {
+ va_end(ap);
+ return -1;
+ }
+ vsnprintf(*strp, size + 1, fmt, ap);
+
+ return size;
+}
+
+static char *handle_memory_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char *fn = NULL;
+ struct ast_region *reg;
+ unsigned int x;
+ unsigned int len = 0;
+ unsigned int cache_len = 0;
+ unsigned int count = 0;
+ unsigned int *fence;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "memory show allocations";
+ e->usage =
+ "Usage: memory show allocations [<file>]\n"
+ " Dumps a list of all segments of allocated memory, optionally\n"
+ " limited to those from a specific file\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+
+ if (a->argc > 3)
+ fn = a->argv[3];
+
+ ast_mutex_lock(&reglock);
+ for (x = 0; x < SOME_PRIME; x++) {
+ for (reg = regions[x]; reg; reg = reg->next) {
+ if (!fn || !strcasecmp(fn, reg->file) || !strcasecmp(fn, "anomolies")) {
+ fence = (unsigned int *)(reg->data + reg->len);
+ if (reg->fence != FENCE_MAGIC) {
+ astmm_log("WARNING: Low fence violation at %p, "
+ "in %s of %s, line %d\n", reg->data,
+ reg->func, reg->file, reg->lineno);
+ }
+ if (get_unaligned_uint32(fence) != FENCE_MAGIC) {
+ astmm_log("WARNING: High fence violation at %p, in %s of %s, "
+ "line %d\n", reg->data, reg->func, reg->file, reg->lineno);
+ }
+ }
+ if (!fn || !strcasecmp(fn, reg->file)) {
+ ast_cli(a->fd, "%10d bytes allocated%s in %20s at line %5d of %s\n",
+ (int) reg->len, reg->cache ? " (cache)" : "",
+ reg->func, reg->lineno, reg->file);
+ len += reg->len;
+ if (reg->cache)
+ cache_len += reg->len;
+ count++;
+ }
+ }
+ }
+ ast_mutex_unlock(&reglock);
+
+ if (cache_len)
+ ast_cli(a->fd, "%d bytes allocated (%d in caches) in %d allocations\n", len, cache_len, count);
+ else
+ ast_cli(a->fd, "%d bytes allocated in %d allocations\n", len, count);
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_memory_show_summary(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char *fn = NULL;
+ int x;
+ struct ast_region *reg;
+ unsigned int len = 0;
+ unsigned int cache_len = 0;
+ int count = 0;
+ struct file_summary {
+ char fn[80];
+ int len;
+ int cache_len;
+ int count;
+ struct file_summary *next;
+ } *list = NULL, *cur;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "memory show summary";
+ e->usage =
+ "Usage: memory show summary [<file>]\n"
+ " Summarizes heap memory allocations by file, or optionally\n"
+ "by function, if a file is specified\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc > 3)
+ fn = a->argv[3];
+
+ ast_mutex_lock(&reglock);
+ for (x = 0; x < SOME_PRIME; x++) {
+ for (reg = regions[x]; reg; reg = reg->next) {
+ if (fn && strcasecmp(fn, reg->file))
+ continue;
+
+ for (cur = list; cur; cur = cur->next) {
+ if ((!fn && !strcmp(cur->fn, reg->file)) || (fn && !strcmp(cur->fn, reg->func)))
+ break;
+ }
+ if (!cur) {
+ cur = alloca(sizeof(*cur));
+ memset(cur, 0, sizeof(*cur));
+ ast_copy_string(cur->fn, fn ? reg->func : reg->file, sizeof(cur->fn));
+ cur->next = list;
+ list = cur;
+ }
+
+ cur->len += reg->len;
+ if (reg->cache)
+ cur->cache_len += reg->len;
+ cur->count++;
+ }
+ }
+ ast_mutex_unlock(&reglock);
+
+ /* Dump the whole list */
+ for (cur = list; cur; cur = cur->next) {
+ len += cur->len;
+ cache_len += cur->cache_len;
+ count += cur->count;
+ if (cur->cache_len) {
+ if (fn) {
+ ast_cli(a->fd, "%10d bytes (%10d cache) in %d allocations in function '%s' of '%s'\n",
+ cur->len, cur->cache_len, cur->count, cur->fn, fn);
+ } else {
+ ast_cli(a->fd, "%10d bytes (%10d cache) in %d allocations in file '%s'\n",
+ cur->len, cur->cache_len, cur->count, cur->fn);
+ }
+ } else {
+ if (fn) {
+ ast_cli(a->fd, "%10d bytes in %d allocations in function '%s' of '%s'\n",
+ cur->len, cur->count, cur->fn, fn);
+ } else {
+ ast_cli(a->fd, "%10d bytes in %d allocations in file '%s'\n",
+ cur->len, cur->count, cur->fn);
+ }
+ }
+ }
+
+ if (cache_len)
+ ast_cli(a->fd, "%d bytes allocated (%d in caches) in %d allocations\n", len, cache_len, count);
+ else
+ ast_cli(a->fd, "%d bytes allocated in %d allocations\n", len, count);
+
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli_memory[] = {
+ AST_CLI_DEFINE(handle_memory_show, "Display outstanding memory allocations"),
+ AST_CLI_DEFINE(handle_memory_show_summary, "Summarize outstanding memory allocations"),
+};
+
+void __ast_mm_init(void)
+{
+ char filename[PATH_MAX];
+
+ ast_cli_register_multiple(cli_memory, sizeof(cli_memory) / sizeof(struct ast_cli_entry));
+
+ snprintf(filename, sizeof(filename), "%s/mmlog", ast_config_AST_LOG_DIR);
+
+ if (option_verbose)
+ ast_verbose("Asterisk Malloc Debugger Started (see %s))\n", filename);
+
+ if ((mmlog = fopen(filename, "a+"))) {
+ fprintf(mmlog, "%ld - New session\n", (long)time(NULL));
+ fflush(mmlog);
+ }
+}
+
+#endif
diff --git a/trunk/main/astobj2.c b/trunk/main/astobj2.c
new file mode 100644
index 000000000..2d3611889
--- /dev/null
+++ b/trunk/main/astobj2.c
@@ -0,0 +1,732 @@
+/*
+ * astobj2 - replacement containers for asterisk data structures.
+ *
+ * Copyright (C) 2006 Marta Carbone, Luigi Rizzo - Univ. di Pisa, Italy
+ *
+ * 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.
+ */
+
+/*
+ * Function implementing astobj2 objects.
+ */
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/_private.h"
+#include "asterisk/astobj2.h"
+#include "asterisk/utils.h"
+#include "asterisk/cli.h"
+
+/*!
+ * astobj2 objects are always preceded by this data structure,
+ * which contains a lock, a reference counter,
+ * the flags and a pointer to a destructor.
+ * The refcount is used to decide when it is time to
+ * invoke the destructor.
+ * The magic number is used for consistency check.
+ * XXX the lock is not always needed, and its initialization may be
+ * expensive. Consider making it external.
+ */
+struct __priv_data {
+ ast_mutex_t lock;
+ int ref_counter;
+ ao2_destructor_fn destructor_fn;
+ /*! for stats */
+ size_t data_size;
+ /*! magic number. This is used to verify that a pointer passed in is a
+ * valid astobj2 */
+ uint32_t magic;
+};
+
+#define AO2_MAGIC 0xa570b123
+
+/*!
+ * What an astobj2 object looks like: fixed-size private data
+ * followed by variable-size user data.
+ */
+struct astobj2 {
+ struct __priv_data priv_data;
+ void *user_data[0];
+};
+
+#ifdef AST_DEVMODE
+#define AO2_DEBUG 1
+#endif
+
+#ifdef AO2_DEBUG
+struct ao2_stats {
+ volatile int total_objects;
+ volatile int total_mem;
+ volatile int total_containers;
+ volatile int total_refs;
+ volatile int total_locked;
+};
+
+static struct ao2_stats ao2;
+#endif
+
+#ifndef HAVE_BKTR /* backtrace support */
+void ao2_bt(void) {}
+#else
+#include <execinfo.h> /* for backtrace */
+
+void ao2_bt(void)
+{
+ int c, i;
+#define N1 20
+ void *addresses[N1];
+ char **strings;
+
+ c = backtrace(addresses, N1);
+ strings = backtrace_symbols(addresses,c);
+ ast_verbose("backtrace returned: %d\n", c);
+ for(i = 0; i < c; i++) {
+ ast_verbose("%d: %p %s\n", i, addresses[i], strings[i]);
+ }
+ free(strings);
+}
+#endif
+
+/*!
+ * \brief convert from a pointer _p to a user-defined object
+ *
+ * \return the pointer to the astobj2 structure
+ */
+static inline struct astobj2 *INTERNAL_OBJ(void *user_data)
+{
+ struct astobj2 *p;
+
+ if (!user_data) {
+ ast_log(LOG_ERROR, "user_data is NULL\n");
+ return NULL;
+ }
+
+ p = (struct astobj2 *) ((char *) user_data - sizeof(*p));
+ if (AO2_MAGIC != (p->priv_data.magic) ) {
+ ast_log(LOG_ERROR, "bad magic number 0x%x for %p\n", p->priv_data.magic, p);
+ p = NULL;
+ }
+
+ return p;
+}
+
+/*!
+ * \brief convert from a pointer _p to an astobj2 object
+ *
+ * \return the pointer to the user-defined portion.
+ */
+#define EXTERNAL_OBJ(_p) ((_p) == NULL ? NULL : (_p)->user_data)
+
+int ao2_lock(void *user_data)
+{
+ struct astobj2 *p = INTERNAL_OBJ(user_data);
+
+ if (p == NULL)
+ return -1;
+
+#ifdef AO2_DEBUG
+ ast_atomic_fetchadd_int(&ao2.total_locked, 1);
+#endif
+
+ return ast_mutex_lock(&p->priv_data.lock);
+}
+
+int ao2_unlock(void *user_data)
+{
+ struct astobj2 *p = INTERNAL_OBJ(user_data);
+
+ if (p == NULL)
+ return -1;
+
+#ifdef AO2_DEBUG
+ ast_atomic_fetchadd_int(&ao2.total_locked, -1);
+#endif
+
+ return ast_mutex_unlock(&p->priv_data.lock);
+}
+
+/*
+ * The argument is a pointer to the user portion.
+ */
+int ao2_ref(void *user_data, const int delta)
+{
+ int current_value;
+ int ret;
+ struct astobj2 *obj = INTERNAL_OBJ(user_data);
+
+ if (obj == NULL)
+ return -1;
+
+ /* if delta is 0, just return the refcount */
+ if (delta == 0)
+ return (obj->priv_data.ref_counter);
+
+ /* we modify with an atomic operation the reference counter */
+ ret = ast_atomic_fetchadd_int(&obj->priv_data.ref_counter, delta);
+ current_value = ret + delta;
+
+#ifdef AO2_DEBUG
+ ast_atomic_fetchadd_int(&ao2.total_refs, delta);
+#endif
+
+ /* this case must never happen */
+ if (current_value < 0)
+ ast_log(LOG_ERROR, "refcount %d on object %p\n", current_value, user_data);
+
+ if (current_value <= 0) { /* last reference, destroy the object */
+ if (obj->priv_data.destructor_fn != NULL)
+ obj->priv_data.destructor_fn(user_data);
+
+ ast_mutex_destroy(&obj->priv_data.lock);
+#ifdef AO2_DEBUG
+ ast_atomic_fetchadd_int(&ao2.total_mem, - obj->priv_data.data_size);
+ ast_atomic_fetchadd_int(&ao2.total_objects, -1);
+#endif
+ /* for safety, zero-out the astobj2 header and also the
+ * first word of the user-data, which we make sure is always
+ * allocated. */
+ bzero(obj, sizeof(struct astobj2 *) + sizeof(void *) );
+ free(obj);
+ }
+
+ return ret;
+}
+
+/*
+ * We always alloc at least the size of a void *,
+ * for debugging purposes.
+ */
+void *ao2_alloc(size_t data_size, ao2_destructor_fn destructor_fn)
+{
+ /* allocation */
+ struct astobj2 *obj;
+
+ if (data_size < sizeof(void *))
+ data_size = sizeof(void *);
+
+ obj = ast_calloc(1, sizeof(*obj) + data_size);
+
+ if (obj == NULL)
+ return NULL;
+
+ ast_mutex_init(&obj->priv_data.lock);
+ obj->priv_data.magic = AO2_MAGIC;
+ obj->priv_data.data_size = data_size;
+ obj->priv_data.ref_counter = 1;
+ obj->priv_data.destructor_fn = destructor_fn; /* can be NULL */
+
+#ifdef AO2_DEBUG
+ ast_atomic_fetchadd_int(&ao2.total_objects, 1);
+ ast_atomic_fetchadd_int(&ao2.total_mem, data_size);
+ ast_atomic_fetchadd_int(&ao2.total_refs, 1);
+#endif
+
+ /* return a pointer to the user data */
+ return EXTERNAL_OBJ(obj);
+}
+
+/* internal callback to destroy a container. */
+static void container_destruct(void *c);
+
+/* each bucket in the container is a tailq. */
+AST_LIST_HEAD_NOLOCK(bucket, bucket_list);
+
+/*!
+ * A container; stores the hash and callback functions, information on
+ * the size, the hash bucket heads, and a version number, starting at 0
+ * (for a newly created, empty container)
+ * and incremented every time an object is inserted or deleted.
+ * The assumption is that an object is never moved in a container,
+ * but removed and readded with the new number.
+ * The version number is especially useful when implementing iterators.
+ * In fact, we can associate a unique, monotonically increasing number to
+ * each object, which means that, within an iterator, we can store the
+ * version number of the current object, and easily look for the next one,
+ * which is the next one in the list with a higher number.
+ * Since all objects have a version >0, we can use 0 as a marker for
+ * 'we need the first object in the bucket'.
+ *
+ * \todo Linking and unlink objects is typically expensive, as it
+ * involves a malloc() of a small object which is very inefficient.
+ * To optimize this, we allocate larger arrays of bucket_list's
+ * when we run out of them, and then manage our own freelist.
+ * This will be more efficient as we can do the freelist management while
+ * we hold the lock (that we need anyways).
+ */
+struct ao2_container {
+ ao2_hash_fn *hash_fn;
+ ao2_callback_fn *cmp_fn;
+ int n_buckets;
+ /*! Number of elements in the container */
+ int elements;
+ /*! described above */
+ int version;
+ /*! variable size */
+ struct bucket buckets[0];
+};
+
+/*!
+ * \brief always zero hash function
+ *
+ * it is convenient to have a hash function that always returns 0.
+ * This is basically used when we want to have a container that is
+ * a simple linked list.
+ *
+ * \returns 0
+ */
+static int hash_zero(const void *user_obj, const int flags)
+{
+ return 0;
+}
+
+/*
+ * A container is just an object, after all!
+ */
+struct ao2_container *
+ao2_container_alloc(const uint n_buckets, ao2_hash_fn *hash_fn,
+ ao2_callback_fn *cmp_fn)
+{
+ /* XXX maybe consistency check on arguments ? */
+ /* compute the container size */
+ size_t container_size = sizeof(struct ao2_container) + n_buckets * sizeof(struct bucket);
+
+ struct ao2_container *c = ao2_alloc(container_size, container_destruct);
+
+ if (!c)
+ return NULL;
+
+ c->version = 1; /* 0 is a reserved value here */
+ c->n_buckets = n_buckets;
+ c->hash_fn = hash_fn ? hash_fn : hash_zero;
+ c->cmp_fn = cmp_fn;
+
+#ifdef AO2_DEBUG
+ ast_atomic_fetchadd_int(&ao2.total_containers, 1);
+#endif
+
+ return c;
+}
+
+/*!
+ * return the number of elements in the container
+ */
+int ao2_container_count(struct ao2_container *c)
+{
+ return c->elements;
+}
+
+/*!
+ * A structure to create a linked list of entries,
+ * used within a bucket.
+ * XXX \todo this should be private to the container code
+ */
+struct bucket_list {
+ AST_LIST_ENTRY(bucket_list) entry;
+ int version;
+ struct astobj2 *astobj; /* pointer to internal data */
+};
+
+/*
+ * link an object to a container
+ */
+void *ao2_link(struct ao2_container *c, void *user_data)
+{
+ int i;
+ /* create a new list entry */
+ struct bucket_list *p;
+ struct astobj2 *obj = INTERNAL_OBJ(user_data);
+
+ if (!obj)
+ return NULL;
+
+ if (INTERNAL_OBJ(c) == NULL)
+ return NULL;
+
+ p = ast_calloc(1, sizeof(*p));
+ if (!p)
+ return NULL;
+
+ i = c->hash_fn(user_data, OBJ_POINTER);
+
+ ao2_lock(c);
+ i %= c->n_buckets;
+ p->astobj = obj;
+ p->version = ast_atomic_fetchadd_int(&c->version, 1);
+ AST_LIST_INSERT_TAIL(&c->buckets[i], p, entry);
+ ast_atomic_fetchadd_int(&c->elements, 1);
+ ao2_ref(user_data, +1);
+ ao2_unlock(c);
+
+ return p;
+}
+
+/*!
+ * \brief another convenience function is a callback that matches on address
+ */
+int ao2_match_by_addr(void *user_data, void *arg, int flags)
+{
+ return (user_data == arg) ? (CMP_MATCH | CMP_STOP) : 0;
+}
+
+/*
+ * Unlink an object from the container
+ * and destroy the associated * ao2_bucket_list structure.
+ */
+void *ao2_unlink(struct ao2_container *c, void *user_data)
+{
+ if (INTERNAL_OBJ(user_data) == NULL) /* safety check on the argument */
+ return NULL;
+
+ ao2_callback(c, OBJ_UNLINK | OBJ_POINTER | OBJ_NODATA, ao2_match_by_addr, user_data);
+
+ return NULL;
+}
+
+/*!
+ * \brief special callback that matches all
+ */
+static int cb_true(void *user_data, void *arg, int flags)
+{
+ return CMP_MATCH;
+}
+
+/*!
+ * Browse the container using different stategies accoding the flags.
+ * \return Is a pointer to an object or to a list of object if OBJ_MULTIPLE is
+ * specified.
+ */
+void *ao2_callback(struct ao2_container *c,
+ const enum search_flags flags,
+ ao2_callback_fn *cb_fn, void *arg)
+{
+ int i, last; /* search boundaries */
+ void *ret = NULL;
+
+ if (INTERNAL_OBJ(c) == NULL) /* safety check on the argument */
+ return NULL;
+
+ if ((flags & (OBJ_MULTIPLE | OBJ_NODATA)) == OBJ_MULTIPLE) {
+ ast_log(LOG_WARNING, "multiple data return not implemented yet (flags %x)\n", flags);
+ return NULL;
+ }
+
+ /* override the match function if necessary */
+ if (cb_fn == NULL) /* if NULL, match everything */
+ cb_fn = cb_true;
+ /*
+ * XXX this can be optimized.
+ * If we have a hash function and lookup by pointer,
+ * run the hash function. Otherwise, scan the whole container
+ * (this only for the time being. We need to optimize this.)
+ */
+ if ((flags & OBJ_POINTER)) /* we know hash can handle this case */
+ i = c->hash_fn(arg, flags & OBJ_POINTER) % c->n_buckets;
+ else /* don't know, let's scan all buckets */
+ i = -1; /* XXX this must be fixed later. */
+
+ /* determine the search boundaries: i..last-1 */
+ if (i < 0) {
+ i = 0;
+ last = c->n_buckets;
+ } else {
+ last = i + 1;
+ }
+
+ ao2_lock(c); /* avoid modifications to the content */
+
+ for (; i < last ; i++) {
+ /* scan the list with prev-cur pointers */
+ struct bucket_list *cur;
+
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&c->buckets[i], cur, entry) {
+ int match = cb_fn(EXTERNAL_OBJ(cur->astobj), arg, flags) & (CMP_MATCH | CMP_STOP);
+
+ /* we found the object, performing operations according flags */
+ if (match == 0) { /* no match, no stop, continue */
+ continue;
+ } else if (match == CMP_STOP) { /* no match but stop, we are done */
+ i = last;
+ break;
+ }
+ /* we have a match (CMP_MATCH) here */
+ if (!(flags & OBJ_NODATA)) { /* if must return the object, record the value */
+ /* it is important to handle this case before the unlink */
+ ret = EXTERNAL_OBJ(cur->astobj);
+ ao2_ref(ret, 1);
+ }
+
+ if (flags & OBJ_UNLINK) { /* must unlink */
+ struct bucket_list *x = cur;
+
+ /* we are going to modify the container, so update version */
+ ast_atomic_fetchadd_int(&c->version, 1);
+ AST_LIST_REMOVE_CURRENT(entry);
+ /* update number of elements and version */
+ ast_atomic_fetchadd_int(&c->elements, -1);
+ ao2_ref(EXTERNAL_OBJ(x->astobj), -1);
+ free(x); /* free the link record */
+ }
+
+ if ((match & CMP_STOP) || (flags & OBJ_MULTIPLE) == 0) {
+ /* We found the only match we need */
+ i = last; /* force exit from outer loop */
+ break;
+ }
+ if (!(flags & OBJ_NODATA)) {
+#if 0 /* XXX to be completed */
+ /*
+ * This is the multiple-return case. We need to link
+ * the object in a list. The refcount is already increased.
+ */
+#endif
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ }
+ ao2_unlock(c);
+ return ret;
+}
+
+/*!
+ * the find function just invokes the default callback with some reasonable flags.
+ */
+void *ao2_find(struct ao2_container *c, void *arg, enum search_flags flags)
+{
+ return ao2_callback(c, flags, c->cmp_fn, arg);
+}
+
+/*!
+ * initialize an iterator so we start from the first object
+ */
+struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags)
+{
+ struct ao2_iterator a = {
+ .c = c,
+ .flags = flags
+ };
+
+ return a;
+}
+
+/*
+ * move to the next element in the container.
+ */
+void * ao2_iterator_next(struct ao2_iterator *a)
+{
+ int lim;
+ struct bucket_list *p = NULL;
+ void *ret = NULL;
+
+ if (INTERNAL_OBJ(a->c) == NULL)
+ return NULL;
+
+ if (!(a->flags & F_AO2I_DONTLOCK))
+ ao2_lock(a->c);
+
+ /* optimization. If the container is unchanged and
+ * we have a pointer, try follow it
+ */
+ if (a->c->version == a->c_version && (p = a->obj) ) {
+ if ( (p = AST_LIST_NEXT(p, entry)) )
+ goto found;
+ /* nope, start from the next bucket */
+ a->bucket++;
+ a->version = 0;
+ a->obj = NULL;
+ }
+
+ lim = a->c->n_buckets;
+
+ /* Browse the buckets array, moving to the next
+ * buckets if we don't find the entry in the current one.
+ * Stop when we find an element with version number greater
+ * than the current one (we reset the version to 0 when we
+ * switch buckets).
+ */
+ for (; a->bucket < lim; a->bucket++, a->version = 0) {
+ /* scan the current bucket */
+ AST_LIST_TRAVERSE(&a->c->buckets[a->bucket], p, entry) {
+ if (p->version > a->version)
+ goto found;
+ }
+ }
+
+found:
+ if (p) {
+ a->version = p->version;
+ a->obj = p;
+ a->c_version = a->c->version;
+ ret = EXTERNAL_OBJ(p->astobj);
+ /* inc refcount of returned object */
+ ao2_ref(ret, 1);
+ }
+
+ if (!(a->flags & F_AO2I_DONTLOCK))
+ ao2_unlock(a->c);
+
+ return ret;
+}
+
+/* callback for destroying container.
+ * we can make it simple as we know what it does
+ */
+static int cd_cb(void *obj, void *arg, int flag)
+{
+ ao2_ref(obj, -1);
+ return 0;
+}
+
+static void container_destruct(void *_c)
+{
+ struct ao2_container *c = _c;
+
+ ao2_callback(c, OBJ_UNLINK, cd_cb, NULL);
+
+#ifdef AO2_DEBUG
+ ast_atomic_fetchadd_int(&ao2.total_containers, -1);
+#endif
+}
+
+#ifdef AO2_DEBUG
+static int print_cb(void *obj, void *arg, int flag)
+{
+ int *fd = arg;
+ char *s = (char *)obj;
+
+ ast_cli(*fd, "string <%s>\n", s);
+ return 0;
+}
+
+/*
+ * Print stats
+ */
+static char *handle_astobj2_stats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "astobj2 stats";
+ e->usage = "Usage: astobj2 stats\n"
+ " Show astobj2 stats\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ ast_cli(a->fd, "Objects : %d\n", ao2.total_objects);
+ ast_cli(a->fd, "Containers : %d\n", ao2.total_containers);
+ ast_cli(a->fd, "Memory : %d\n", ao2.total_mem);
+ ast_cli(a->fd, "Locked : %d\n", ao2.total_locked);
+ ast_cli(a->fd, "Refs : %d\n", ao2.total_refs);
+ return CLI_SUCCESS;
+}
+
+/*
+ * This is testing code for astobj
+ */
+static char *handle_astobj2_test(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ao2_container *c1;
+ int i, lim;
+ char *obj;
+ static int prof_id = -1;
+ struct ast_cli_args fake_args = { a->fd, 0, NULL };
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "astobj2 test";
+ e->usage = "Usage: astobj2 test <num>\n"
+ " Runs astobj2 test. Creates 'num' objects,\n"
+ " and test iterators, callbacks and may be other stuff\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3) {
+ return CLI_SHOWUSAGE;
+ }
+
+ if (prof_id == -1)
+ prof_id = ast_add_profile("ao2_alloc", 0);
+
+ ast_cli(a->fd, "argc %d argv %s %s %s\n", a->argc, a->argv[0], a->argv[1], a->argv[2]);
+ lim = atoi(a->argv[2]);
+ ast_cli(a->fd, "called astobj_test\n");
+
+ handle_astobj2_stats(e, CLI_HANDLER, &fake_args);
+ /*
+ * allocate a container with no default callback, and no hash function.
+ * No hash means everything goes in the same bucket.
+ */
+ c1 = ao2_container_alloc(100, NULL /* no callback */, NULL /* no hash */);
+ ast_cli(a->fd, "container allocated as %p\n", c1);
+
+ /*
+ * fill the container with objects.
+ * ao2_alloc() gives us a reference which we pass to the
+ * container when we do the insert.
+ */
+ for (i = 0; i < lim; i++) {
+ ast_mark(prof_id, 1 /* start */);
+ obj = ao2_alloc(80, NULL);
+ ast_mark(prof_id, 0 /* stop */);
+ ast_cli(a->fd, "object %d allocated as %p\n", i, obj);
+ sprintf(obj, "-- this is obj %d --", i);
+ ao2_link(c1, obj);
+ }
+ ast_cli(a->fd, "testing callbacks\n");
+ ao2_callback(c1, 0, print_cb, &a->fd);
+
+ ast_cli(a->fd, "testing iterators, remove every second object\n");
+ {
+ struct ao2_iterator ai;
+ int x = 0;
+
+ ai = ao2_iterator_init(c1, 0);
+ while ( (obj = ao2_iterator_next(&ai)) ) {
+ ast_cli(a->fd, "iterator on <%s>\n", obj);
+ if (x++ & 1)
+ ao2_unlink(c1, obj);
+ ao2_ref(obj, -1);
+ }
+ ast_cli(a->fd, "testing iterators again\n");
+ ai = ao2_iterator_init(c1, 0);
+ while ( (obj = ao2_iterator_next(&ai)) ) {
+ ast_cli(a->fd, "iterator on <%s>\n", obj);
+ ao2_ref(obj, -1);
+ }
+ }
+ ast_cli(a->fd, "testing callbacks again\n");
+ ao2_callback(c1, 0, print_cb, &a->fd);
+
+ ast_verbose("now you should see an error message:\n");
+ ao2_ref(&i, -1); /* i is not a valid object so we print an error here */
+
+ ast_cli(a->fd, "destroy container\n");
+ ao2_ref(c1, -1); /* destroy container */
+ handle_astobj2_stats(e, CLI_HANDLER, &fake_args);
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli_astobj2[] = {
+ AST_CLI_DEFINE(handle_astobj2_stats, "Print astobj2 statistics"),
+ AST_CLI_DEFINE(handle_astobj2_test, "Test astobj2"),
+};
+#endif /* AO2_DEBUG */
+
+int astobj2_init(void)
+{
+#ifdef AO2_DEBUG
+ ast_cli_register_multiple(cli_astobj2, ARRAY_LEN(cli_astobj2));
+#endif
+
+ return 0;
+}
diff --git a/trunk/main/audiohook.c b/trunk/main/audiohook.c
new file mode 100644
index 000000000..c50e6c476
--- /dev/null
+++ b/trunk/main/audiohook.c
@@ -0,0 +1,693 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2007, Digium, Inc.
+ *
+ * Joshua Colp <jcolp@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
+ *
+ * \brief Audiohooks Architecture
+ *
+ * \author Joshua Colp <jcolp@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <signal.h>
+
+#include "asterisk/channel.h"
+#include "asterisk/utils.h"
+#include "asterisk/lock.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/audiohook.h"
+#include "asterisk/slinfactory.h"
+#include "asterisk/frame.h"
+#include "asterisk/translate.h"
+
+struct ast_audiohook_translate {
+ struct ast_trans_pvt *trans_pvt;
+ int format;
+};
+
+struct ast_audiohook_list {
+ struct ast_audiohook_translate in_translate[2];
+ struct ast_audiohook_translate out_translate[2];
+ AST_LIST_HEAD_NOLOCK(, ast_audiohook) spy_list;
+ AST_LIST_HEAD_NOLOCK(, ast_audiohook) whisper_list;
+ AST_LIST_HEAD_NOLOCK(, ast_audiohook) manipulate_list;
+};
+
+/*! \brief Initialize an audiohook structure
+ * \param audiohook Audiohook structure
+ * \param type
+ * \param source
+ * \return Returns 0 on success, -1 on failure
+ */
+int ast_audiohook_init(struct ast_audiohook *audiohook, enum ast_audiohook_type type, const char *source)
+{
+ /* Need to keep the type and source */
+ audiohook->type = type;
+ audiohook->source = source;
+
+ /* Initialize lock that protects our audiohook */
+ ast_mutex_init(&audiohook->lock);
+ ast_cond_init(&audiohook->trigger, NULL);
+
+ /* Setup the factories that are needed for this audiohook type */
+ switch (type) {
+ case AST_AUDIOHOOK_TYPE_SPY:
+ ast_slinfactory_init(&audiohook->read_factory);
+ case AST_AUDIOHOOK_TYPE_WHISPER:
+ ast_slinfactory_init(&audiohook->write_factory);
+ break;
+ default:
+ break;
+ }
+
+ /* Since we are just starting out... this audiohook is new */
+ audiohook->status = AST_AUDIOHOOK_STATUS_NEW;
+
+ return 0;
+}
+
+/*! \brief Destroys an audiohook structure
+ * \param audiohook Audiohook structure
+ * \return Returns 0 on success, -1 on failure
+ */
+int ast_audiohook_destroy(struct ast_audiohook *audiohook)
+{
+ /* Drop the factories used by this audiohook type */
+ switch (audiohook->type) {
+ case AST_AUDIOHOOK_TYPE_SPY:
+ ast_slinfactory_destroy(&audiohook->read_factory);
+ case AST_AUDIOHOOK_TYPE_WHISPER:
+ ast_slinfactory_destroy(&audiohook->write_factory);
+ break;
+ default:
+ break;
+ }
+
+ /* Destroy translation path if present */
+ if (audiohook->trans_pvt)
+ ast_translator_free_path(audiohook->trans_pvt);
+
+ /* Lock and trigger be gone! */
+ ast_cond_destroy(&audiohook->trigger);
+ ast_mutex_destroy(&audiohook->lock);
+
+ return 0;
+}
+
+/*! \brief Writes a frame into the audiohook structure
+ * \param audiohook Audiohook structure
+ * \param direction Direction the audio frame came from
+ * \param frame Frame to write in
+ * \return Returns 0 on success, -1 on failure
+ */
+int ast_audiohook_write_frame(struct ast_audiohook *audiohook, enum ast_audiohook_direction direction, struct ast_frame *frame)
+{
+ struct ast_slinfactory *factory = (direction == AST_AUDIOHOOK_DIRECTION_READ ? &audiohook->read_factory : &audiohook->write_factory);
+
+ /* Write frame out to respective factory */
+ ast_slinfactory_feed(factory, frame);
+
+ /* If we need to notify the respective handler of this audiohook, do so */
+ switch (ast_test_flag(audiohook, AST_AUDIOHOOK_TRIGGER_MODE)) {
+ case AST_AUDIOHOOK_TRIGGER_READ:
+ if (direction == AST_AUDIOHOOK_DIRECTION_READ)
+ ast_cond_signal(&audiohook->trigger);
+ break;
+ case AST_AUDIOHOOK_TRIGGER_WRITE:
+ if (direction == AST_AUDIOHOOK_DIRECTION_WRITE)
+ ast_cond_signal(&audiohook->trigger);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static struct ast_frame *audiohook_read_frame_single(struct ast_audiohook *audiohook, size_t samples, enum ast_audiohook_direction direction)
+{
+ struct ast_slinfactory *factory = (direction == AST_AUDIOHOOK_DIRECTION_READ ? &audiohook->read_factory : &audiohook->write_factory);
+ int vol = (direction == AST_AUDIOHOOK_DIRECTION_READ ? audiohook->options.read_volume : audiohook->options.write_volume);
+ short buf[samples];
+ struct ast_frame frame = {
+ .frametype = AST_FRAME_VOICE,
+ .subclass = AST_FORMAT_SLINEAR,
+ .data = buf,
+ .datalen = sizeof(buf),
+ .samples = samples,
+ };
+
+ /* Ensure the factory is able to give us the samples we want */
+ if (samples > ast_slinfactory_available(factory))
+ return NULL;
+
+ /* Read data in from factory */
+ if (!ast_slinfactory_read(factory, buf, samples))
+ return NULL;
+
+ /* If a volume adjustment needs to be applied apply it */
+ if (vol)
+ ast_frame_adjust_volume(&frame, vol);
+
+ return ast_frdup(&frame);
+}
+
+static struct ast_frame *audiohook_read_frame_both(struct ast_audiohook *audiohook, size_t samples)
+{
+ int i = 0;
+ short buf1[samples], buf2[samples], *read_buf = NULL, *write_buf = NULL, *final_buf = NULL, *data1 = NULL, *data2 = NULL;
+ struct ast_frame frame = {
+ .frametype = AST_FRAME_VOICE,
+ .subclass = AST_FORMAT_SLINEAR,
+ .data = NULL,
+ .datalen = sizeof(buf1),
+ .samples = samples,
+ };
+
+ /* Start with the read factory... if there are enough samples, read them in */
+ if (ast_slinfactory_available(&audiohook->read_factory) >= samples) {
+ if (ast_slinfactory_read(&audiohook->read_factory, buf1, samples)) {
+ read_buf = buf1;
+ /* Adjust read volume if need be */
+ if (audiohook->options.read_volume) {
+ int count = 0;
+ short adjust_value = abs(audiohook->options.read_volume);
+ for (count = 0; count < samples; count++) {
+ if (audiohook->options.read_volume > 0)
+ ast_slinear_saturated_multiply(&buf1[count], &adjust_value);
+ else if (audiohook->options.read_volume < 0)
+ ast_slinear_saturated_divide(&buf1[count], &adjust_value);
+ }
+ }
+ }
+ } else if (option_debug)
+ ast_log(LOG_DEBUG, "Failed to get %d samples from read factory %p\n", (int)samples, &audiohook->read_factory);
+
+ /* Move on to the write factory... if there are enough samples, read them in */
+ if (ast_slinfactory_available(&audiohook->write_factory) >= samples) {
+ if (ast_slinfactory_read(&audiohook->write_factory, buf2, samples)) {
+ write_buf = buf2;
+ /* Adjust write volume if need be */
+ if (audiohook->options.write_volume) {
+ int count = 0;
+ short adjust_value = abs(audiohook->options.write_volume);
+ for (count = 0; count < samples; count++) {
+ if (audiohook->options.write_volume > 0)
+ ast_slinear_saturated_multiply(&buf2[count], &adjust_value);
+ else if (audiohook->options.write_volume < 0)
+ ast_slinear_saturated_divide(&buf2[count], &adjust_value);
+ }
+ }
+ }
+ } else if (option_debug)
+ ast_log(LOG_DEBUG, "Failed to get %d samples from write factory %p\n", (int)samples, &audiohook->write_factory);
+
+ /* Basically we figure out which buffer to use... and if mixing can be done here */
+ if (!read_buf && !write_buf)
+ return NULL;
+ else if (read_buf && write_buf) {
+ for (i = 0, data1 = read_buf, data2 = write_buf; i < samples; i++, data1++, data2++)
+ ast_slinear_saturated_add(data1, data2);
+ final_buf = buf1;
+ } else if (read_buf)
+ final_buf = buf1;
+ else if (write_buf)
+ final_buf = buf2;
+
+ /* Make the final buffer part of the frame, so it gets duplicated fine */
+ frame.data = final_buf;
+
+ /* Yahoo, a combined copy of the audio! */
+ return ast_frdup(&frame);
+}
+
+/*! \brief Reads a frame in from the audiohook structure
+ * \param audiohook Audiohook structure
+ * \param samples Number of samples wanted
+ * \param direction Direction the audio frame came from
+ * \param format Format of frame remote side wants back
+ * \return Returns frame on success, NULL on failure
+ */
+struct ast_frame *ast_audiohook_read_frame(struct ast_audiohook *audiohook, size_t samples, enum ast_audiohook_direction direction, int format)
+{
+ struct ast_frame *read_frame = NULL, *final_frame = NULL;
+
+ if (!(read_frame = (direction == AST_AUDIOHOOK_DIRECTION_BOTH ? audiohook_read_frame_both(audiohook, samples) : audiohook_read_frame_single(audiohook, samples, direction))))
+ return NULL;
+
+ /* If they don't want signed linear back out, we'll have to send it through the translation path */
+ if (format != AST_FORMAT_SLINEAR) {
+ /* Rebuild translation path if different format then previously */
+ if (audiohook->format != format) {
+ if (audiohook->trans_pvt) {
+ ast_translator_free_path(audiohook->trans_pvt);
+ audiohook->trans_pvt = NULL;
+ }
+ /* Setup new translation path for this format... if we fail we can't very well return signed linear so free the frame and return nothing */
+ if (!(audiohook->trans_pvt = ast_translator_build_path(format, AST_FORMAT_SLINEAR))) {
+ ast_frfree(read_frame);
+ return NULL;
+ }
+ }
+ /* Convert to requested format, and allow the read in frame to be freed */
+ final_frame = ast_translate(audiohook->trans_pvt, read_frame, 1);
+ } else {
+ final_frame = read_frame;
+ }
+
+ return final_frame;
+}
+
+/*! \brief Attach audiohook to channel
+ * \param chan Channel
+ * \param audiohook Audiohook structure
+ * \return Returns 0 on success, -1 on failure
+ */
+int ast_audiohook_attach(struct ast_channel *chan, struct ast_audiohook *audiohook)
+{
+ ast_channel_lock(chan);
+
+ if (!chan->audiohooks) {
+ /* Whoops... allocate a new structure */
+ if (!(chan->audiohooks = ast_calloc(1, sizeof(*chan->audiohooks)))) {
+ ast_channel_unlock(chan);
+ return -1;
+ }
+ AST_LIST_HEAD_INIT_NOLOCK(&chan->audiohooks->spy_list);
+ AST_LIST_HEAD_INIT_NOLOCK(&chan->audiohooks->whisper_list);
+ AST_LIST_HEAD_INIT_NOLOCK(&chan->audiohooks->manipulate_list);
+ }
+
+ /* Drop into respective list */
+ if (audiohook->type == AST_AUDIOHOOK_TYPE_SPY)
+ AST_LIST_INSERT_TAIL(&chan->audiohooks->spy_list, audiohook, list);
+ else if (audiohook->type == AST_AUDIOHOOK_TYPE_WHISPER)
+ AST_LIST_INSERT_TAIL(&chan->audiohooks->whisper_list, audiohook, list);
+ else if (audiohook->type == AST_AUDIOHOOK_TYPE_MANIPULATE)
+ AST_LIST_INSERT_TAIL(&chan->audiohooks->manipulate_list, audiohook, list);
+
+ /* Change status over to running since it is now attached */
+ audiohook->status = AST_AUDIOHOOK_STATUS_RUNNING;
+
+ ast_channel_unlock(chan);
+
+ return 0;
+}
+
+/*! \brief Detach audiohook from channel
+ * \param audiohook Audiohook structure
+ * \return Returns 0 on success, -1 on failure
+ */
+int ast_audiohook_detach(struct ast_audiohook *audiohook)
+{
+ if (audiohook->status == AST_AUDIOHOOK_STATUS_DONE)
+ return 0;
+
+ audiohook->status = AST_AUDIOHOOK_STATUS_SHUTDOWN;
+
+ while (audiohook->status != AST_AUDIOHOOK_STATUS_DONE)
+ ast_audiohook_trigger_wait(audiohook);
+
+ return 0;
+}
+
+/*! \brief Detach audiohooks from list and destroy said list
+ * \param audiohook_list List of audiohooks
+ * \return Returns 0 on success, -1 on failure
+ */
+int ast_audiohook_detach_list(struct ast_audiohook_list *audiohook_list)
+{
+ int i = 0;
+ struct ast_audiohook *audiohook = NULL;
+
+ /* Drop any spies */
+ while ((audiohook = AST_LIST_REMOVE_HEAD(&audiohook_list->spy_list, list))) {
+ ast_audiohook_lock(audiohook);
+ audiohook->status = AST_AUDIOHOOK_STATUS_DONE;
+ ast_cond_signal(&audiohook->trigger);
+ ast_audiohook_unlock(audiohook);
+ }
+
+ /* Drop any whispering sources */
+ while ((audiohook = AST_LIST_REMOVE_HEAD(&audiohook_list->whisper_list, list))) {
+ ast_audiohook_lock(audiohook);
+ audiohook->status = AST_AUDIOHOOK_STATUS_DONE;
+ ast_cond_signal(&audiohook->trigger);
+ ast_audiohook_unlock(audiohook);
+ }
+
+ /* Drop any manipulaters */
+ while ((audiohook = AST_LIST_REMOVE_HEAD(&audiohook_list->manipulate_list, list))) {
+ ast_audiohook_lock(audiohook);
+ audiohook->status = AST_AUDIOHOOK_STATUS_DONE;
+ ast_audiohook_unlock(audiohook);
+ audiohook->manipulate_callback(audiohook, NULL, NULL, 0);
+ }
+
+ /* Drop translation paths if present */
+ for (i = 0; i < 2; i++) {
+ if (audiohook_list->in_translate[i].trans_pvt)
+ ast_translator_free_path(audiohook_list->in_translate[i].trans_pvt);
+ if (audiohook_list->out_translate[i].trans_pvt)
+ ast_translator_free_path(audiohook_list->out_translate[i].trans_pvt);
+ }
+
+ /* Free ourselves */
+ ast_free(audiohook_list);
+
+ return 0;
+}
+
+static struct ast_audiohook *find_audiohook_by_source(struct ast_audiohook_list *audiohook_list, const char *source)
+{
+ struct ast_audiohook *audiohook = NULL;
+
+ AST_LIST_TRAVERSE(&audiohook_list->spy_list, audiohook, list) {
+ if (!strcasecmp(audiohook->source, source))
+ return audiohook;
+ }
+
+ AST_LIST_TRAVERSE(&audiohook_list->whisper_list, audiohook, list) {
+ if (!strcasecmp(audiohook->source, source))
+ return audiohook;
+ }
+
+ AST_LIST_TRAVERSE(&audiohook_list->manipulate_list, audiohook, list) {
+ if (!strcasecmp(audiohook->source, source))
+ return audiohook;
+ }
+
+ return NULL;
+}
+
+/*! \brief Detach specified source audiohook from channel
+ * \param chan Channel to detach from
+ * \param source Name of source to detach
+ * \return Returns 0 on success, -1 on failure
+ */
+int ast_audiohook_detach_source(struct ast_channel *chan, const char *source)
+{
+ struct ast_audiohook *audiohook = NULL;
+
+ ast_channel_lock(chan);
+
+ /* Ensure the channel has audiohooks on it */
+ if (!chan->audiohooks) {
+ ast_channel_unlock(chan);
+ return -1;
+ }
+
+ audiohook = find_audiohook_by_source(chan->audiohooks, source);
+
+ ast_channel_unlock(chan);
+
+ if (audiohook && audiohook->status != AST_AUDIOHOOK_STATUS_DONE)
+ audiohook->status = AST_AUDIOHOOK_STATUS_SHUTDOWN;
+
+ return (audiohook ? 0 : -1);
+}
+
+/*! \brief Pass a DTMF frame off to be handled by the audiohook core
+ * \param chan Channel that the list is coming off of
+ * \param audiohook_list List of audiohooks
+ * \param direction Direction frame is coming in from
+ * \param frame The frame itself
+ * \return Return frame on success, NULL on failure
+ */
+static struct ast_frame *dtmf_audiohook_write_list(struct ast_channel *chan, struct ast_audiohook_list *audiohook_list, enum ast_audiohook_direction direction, struct ast_frame *frame)
+{
+ struct ast_audiohook *audiohook = NULL;
+
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&audiohook_list->manipulate_list, audiohook, list) {
+ ast_audiohook_lock(audiohook);
+ if (audiohook->status != AST_AUDIOHOOK_STATUS_RUNNING) {
+ AST_LIST_REMOVE_CURRENT(list);
+ audiohook->status = AST_AUDIOHOOK_STATUS_DONE;
+ ast_audiohook_unlock(audiohook);
+ audiohook->manipulate_callback(audiohook, NULL, NULL, 0);
+ continue;
+ }
+ if (ast_test_flag(audiohook, AST_AUDIOHOOK_WANTS_DTMF))
+ audiohook->manipulate_callback(audiohook, chan, frame, direction);
+ ast_audiohook_unlock(audiohook);
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+
+ return frame;
+}
+
+/*! \brief Pass an AUDIO frame off to be handled by the audiohook core
+ * \param chan Channel that the list is coming off of
+ * \param audiohook_list List of audiohooks
+ * \param direction Direction frame is coming in from
+ * \param frame The frame itself
+ * \return Return frame on success, NULL on failure
+ */
+static struct ast_frame *audio_audiohook_write_list(struct ast_channel *chan, struct ast_audiohook_list *audiohook_list, enum ast_audiohook_direction direction, struct ast_frame *frame)
+{
+ struct ast_audiohook_translate *in_translate = (direction == AST_AUDIOHOOK_DIRECTION_READ ? &audiohook_list->in_translate[0] : &audiohook_list->in_translate[1]);
+ struct ast_audiohook_translate *out_translate = (direction == AST_AUDIOHOOK_DIRECTION_READ ? &audiohook_list->out_translate[0] : &audiohook_list->out_translate[1]);
+ struct ast_frame *start_frame = frame, *middle_frame = frame, *end_frame = frame;
+ struct ast_audiohook *audiohook = NULL;
+ int samples = frame->samples;
+
+ /* If the frame coming in is not signed linear we have to send it through the in_translate path */
+ if (frame->subclass != AST_FORMAT_SLINEAR) {
+ if (in_translate->format != frame->subclass) {
+ if (in_translate->trans_pvt)
+ ast_translator_free_path(in_translate->trans_pvt);
+ if (!(in_translate->trans_pvt = ast_translator_build_path(AST_FORMAT_SLINEAR, frame->subclass)))
+ return frame;
+ in_translate->format = frame->subclass;
+ }
+ if (!(middle_frame = ast_translate(in_translate->trans_pvt, frame, 0)))
+ return frame;
+ }
+
+ /* Queue up signed linear frame to each spy */
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&audiohook_list->spy_list, audiohook, list) {
+ ast_audiohook_lock(audiohook);
+ if (audiohook->status != AST_AUDIOHOOK_STATUS_RUNNING) {
+ AST_LIST_REMOVE_CURRENT(list);
+ audiohook->status = AST_AUDIOHOOK_STATUS_DONE;
+ ast_cond_signal(&audiohook->trigger);
+ ast_audiohook_unlock(audiohook);
+ continue;
+ }
+ ast_audiohook_write_frame(audiohook, direction, middle_frame);
+ ast_audiohook_unlock(audiohook);
+ }
+ AST_LIST_TRAVERSE_SAFE_END
+
+ /* If this frame is being written out to the channel then we need to use whisper sources */
+ if (direction == AST_AUDIOHOOK_DIRECTION_WRITE && !AST_LIST_EMPTY(&audiohook_list->whisper_list)) {
+ int i = 0;
+ short read_buf[samples], combine_buf[samples], *data1 = NULL, *data2 = NULL;
+ memset(&combine_buf, 0, sizeof(combine_buf));
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&audiohook_list->whisper_list, audiohook, list) {
+ ast_audiohook_lock(audiohook);
+ if (audiohook->status != AST_AUDIOHOOK_STATUS_RUNNING) {
+ AST_LIST_REMOVE_CURRENT(list);
+ audiohook->status = AST_AUDIOHOOK_STATUS_DONE;
+ ast_cond_signal(&audiohook->trigger);
+ ast_audiohook_unlock(audiohook);
+ continue;
+ }
+ if (ast_slinfactory_available(&audiohook->write_factory) >= samples && ast_slinfactory_read(&audiohook->write_factory, read_buf, samples)) {
+ /* Take audio from this whisper source and combine it into our main buffer */
+ for (i = 0, data1 = combine_buf, data2 = read_buf; i < samples; i++, data1++, data2++)
+ ast_slinear_saturated_add(data1, data2);
+ }
+ ast_audiohook_unlock(audiohook);
+ }
+ AST_LIST_TRAVERSE_SAFE_END
+ /* We take all of the combined whisper sources and combine them into the audio being written out */
+ for (i = 0, data1 = middle_frame->data, data2 = combine_buf; i < samples; i++, data1++, data2++)
+ ast_slinear_saturated_add(data1, data2);
+ end_frame = middle_frame;
+ }
+
+ /* Pass off frame to manipulate audiohooks */
+ if (!AST_LIST_EMPTY(&audiohook_list->manipulate_list)) {
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&audiohook_list->manipulate_list, audiohook, list) {
+ ast_audiohook_lock(audiohook);
+ if (audiohook->status != AST_AUDIOHOOK_STATUS_RUNNING) {
+ AST_LIST_REMOVE_CURRENT(list);
+ audiohook->status = AST_AUDIOHOOK_STATUS_DONE;
+ ast_audiohook_unlock(audiohook);
+ /* We basically drop all of our links to the manipulate audiohook and prod it to do it's own destructive things */
+ audiohook->manipulate_callback(audiohook, chan, NULL, direction);
+ continue;
+ }
+ /* Feed in frame to manipulation */
+ audiohook->manipulate_callback(audiohook, chan, middle_frame, direction);
+ ast_audiohook_unlock(audiohook);
+ }
+ AST_LIST_TRAVERSE_SAFE_END
+ end_frame = middle_frame;
+ }
+
+ /* Now we figure out what to do with our end frame (whether to transcode or not) */
+ if (middle_frame == end_frame) {
+ /* Middle frame was modified and became the end frame... let's see if we need to transcode */
+ if (end_frame->subclass != start_frame->subclass) {
+ if (out_translate->format != start_frame->subclass) {
+ if (out_translate->trans_pvt)
+ ast_translator_free_path(out_translate->trans_pvt);
+ if (!(out_translate->trans_pvt = ast_translator_build_path(start_frame->subclass, AST_FORMAT_SLINEAR))) {
+ /* We can't transcode this... drop our middle frame and return the original */
+ ast_frfree(middle_frame);
+ return start_frame;
+ }
+ out_translate->format = start_frame->subclass;
+ }
+ /* Transcode from our middle (signed linear) frame to new format of the frame that came in */
+ if (!(end_frame = ast_translate(out_translate->trans_pvt, middle_frame, 0))) {
+ /* Failed to transcode the frame... drop it and return the original */
+ ast_frfree(middle_frame);
+ return start_frame;
+ }
+ /* Here's the scoop... middle frame is no longer of use to us */
+ ast_frfree(middle_frame);
+ }
+ } else {
+ /* No frame was modified, we can just drop our middle frame and pass the frame we got in out */
+ ast_frfree(middle_frame);
+ }
+
+ return end_frame;
+}
+
+/*! \brief Pass a frame off to be handled by the audiohook core
+ * \param chan Channel that the list is coming off of
+ * \param audiohook_list List of audiohooks
+ * \param direction Direction frame is coming in from
+ * \param frame The frame itself
+ * \return Return frame on success, NULL on failure
+ */
+struct ast_frame *ast_audiohook_write_list(struct ast_channel *chan, struct ast_audiohook_list *audiohook_list, enum ast_audiohook_direction direction, struct ast_frame *frame)
+{
+ /* Pass off frame to it's respective list write function */
+ if (frame->frametype == AST_FRAME_VOICE)
+ return audio_audiohook_write_list(chan, audiohook_list, direction, frame);
+ else if (frame->frametype == AST_FRAME_DTMF)
+ return dtmf_audiohook_write_list(chan, audiohook_list, direction, frame);
+ else
+ return frame;
+}
+
+
+/*! \brief Wait for audiohook trigger to be triggered
+ * \param audiohook Audiohook to wait on
+ */
+void ast_audiohook_trigger_wait(struct ast_audiohook *audiohook)
+{
+ struct timeval tv;
+ struct timespec ts;
+
+ tv = ast_tvadd(ast_tvnow(), ast_samp2tv(50000, 1000));
+ ts.tv_sec = tv.tv_sec;
+ ts.tv_nsec = tv.tv_usec * 1000;
+
+ ast_cond_timedwait(&audiohook->trigger, &audiohook->lock, &ts);
+
+ return;
+}
+
+/* Count number of channel audiohooks by type, regardless of type */
+int ast_channel_audiohook_count_by_source(struct ast_channel *chan, const char *source, enum ast_audiohook_type type)
+{
+ int count = 0;
+ struct ast_audiohook *ah = NULL;
+
+ if (!chan->audiohooks)
+ return -1;
+
+ switch (type) {
+ case AST_AUDIOHOOK_TYPE_SPY:
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->audiohooks->spy_list, ah, list) {
+ if (!strcmp(ah->source, source)) {
+ count++;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ break;
+ case AST_AUDIOHOOK_TYPE_WHISPER:
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->audiohooks->whisper_list, ah, list) {
+ if (!strcmp(ah->source, source)) {
+ count++;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ break;
+ case AST_AUDIOHOOK_TYPE_MANIPULATE:
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->audiohooks->manipulate_list, ah, list) {
+ if (!strcmp(ah->source, source)) {
+ count++;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ break;
+ default:
+ ast_log(LOG_DEBUG, "Invalid audiohook type supplied, (%d)\n", type);
+ return -1;
+ }
+
+ return count;
+}
+
+/* Count number of channel audiohooks by type that are running */
+int ast_channel_audiohook_count_by_source_running(struct ast_channel *chan, const char *source, enum ast_audiohook_type type)
+{
+ int count = 0;
+ struct ast_audiohook *ah = NULL;
+ if (!chan->audiohooks)
+ return -1;
+
+ switch (type) {
+ case AST_AUDIOHOOK_TYPE_SPY:
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->audiohooks->spy_list, ah, list) {
+ if ((!strcmp(ah->source, source)) && (ah->status == AST_AUDIOHOOK_STATUS_RUNNING))
+ count++;
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ break;
+ case AST_AUDIOHOOK_TYPE_WHISPER:
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->audiohooks->whisper_list, ah, list) {
+ if ((!strcmp(ah->source, source)) && (ah->status == AST_AUDIOHOOK_STATUS_RUNNING))
+ count++;
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ break;
+ case AST_AUDIOHOOK_TYPE_MANIPULATE:
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->audiohooks->manipulate_list, ah, list) {
+ if ((!strcmp(ah->source, source)) && (ah->status == AST_AUDIOHOOK_STATUS_RUNNING))
+ count++;
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ break;
+ default:
+ ast_log(LOG_DEBUG, "Invalid audiohook type supplied, (%d)\n", type);
+ return -1;
+ }
+ return count;
+}
+
diff --git a/trunk/main/autoservice.c b/trunk/main/autoservice.c
new file mode 100644
index 000000000..fcc42911f
--- /dev/null
+++ b/trunk/main/autoservice.c
@@ -0,0 +1,252 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006, 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.
+ */
+
+/*! \file
+ *
+ * \brief Automatic channel service routines
+ *
+ * \author Mark Spencer <markster@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/time.h>
+#include <signal.h>
+
+#include "asterisk/pbx.h"
+#include "asterisk/frame.h"
+#include "asterisk/sched.h"
+#include "asterisk/channel.h"
+#include "asterisk/file.h"
+#include "asterisk/translate.h"
+#include "asterisk/manager.h"
+#include "asterisk/chanvars.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/indications.h"
+#include "asterisk/lock.h"
+#include "asterisk/utils.h"
+
+#define MAX_AUTOMONS 1500
+
+struct asent {
+ struct ast_channel *chan;
+ /*! This gets incremented each time autoservice gets started on the same
+ * channel. It will ensure that it doesn't actually get stopped until
+ * it gets stopped for the last time. */
+ unsigned int use_count;
+ unsigned int orig_end_dtmf_flag:1;
+ AST_LIST_HEAD_NOLOCK(, ast_frame) dtmf_frames;
+ AST_LIST_ENTRY(asent) list;
+};
+
+static AST_RWLIST_HEAD_STATIC(aslist, asent);
+
+static pthread_t asthread = AST_PTHREADT_NULL;
+
+static void defer_frame(struct ast_channel *chan, struct ast_frame *f)
+{
+ struct ast_frame *dup_f;
+ struct asent *as;
+
+ AST_RWLIST_WRLOCK(&aslist);
+ AST_RWLIST_TRAVERSE(&aslist, as, list) {
+ if (as->chan != chan)
+ continue;
+ if ((dup_f = ast_frdup(f)))
+ AST_LIST_INSERT_TAIL(&as->dtmf_frames, dup_f, frame_list);
+ }
+ AST_RWLIST_UNLOCK(&aslist);
+}
+
+static void *autoservice_run(void *ign)
+{
+ for (;;) {
+ struct ast_channel *mons[MAX_AUTOMONS], *chan;
+ struct asent *as;
+ int x = 0, ms = 500;
+
+ AST_RWLIST_RDLOCK(&aslist);
+ AST_RWLIST_TRAVERSE(&aslist, as, list) {
+ if (!ast_check_hangup(as->chan)) {
+ if (x < MAX_AUTOMONS)
+ mons[x++] = as->chan;
+ else
+ ast_log(LOG_WARNING, "Exceeded maximum number of automatic monitoring events. Fix autoservice.c\n");
+ }
+ }
+ AST_RWLIST_UNLOCK(&aslist);
+
+ if ((chan = ast_waitfor_n(mons, x, &ms))) {
+ struct ast_frame *f = ast_read(chan);
+
+ if (!f) {
+ struct ast_frame hangup_frame = { 0, };
+ /* No frame means the channel has been hung up.
+ * A hangup frame needs to be queued here as ast_waitfor() may
+ * never return again for the condition to be detected outside
+ * of autoservice. So, we'll leave a HANGUP queued up so the
+ * thread in charge of this channel will know. */
+
+ hangup_frame.frametype = AST_FRAME_CONTROL;
+ hangup_frame.subclass = AST_CONTROL_HANGUP;
+
+ defer_frame(chan, &hangup_frame);
+
+ continue;
+ }
+
+ /* Do not add a default entry in this switch statement. Each new
+ * frame type should be addressed directly as to whether it should
+ * be queued up or not. */
+ switch (f->frametype) {
+ /* Save these frames */
+ case AST_FRAME_DTMF_END:
+ case AST_FRAME_CONTROL:
+ case AST_FRAME_TEXT:
+ case AST_FRAME_IMAGE:
+ case AST_FRAME_HTML:
+ defer_frame(chan, f);
+ break;
+
+ /* Throw these frames away */
+ case AST_FRAME_DTMF_BEGIN:
+ case AST_FRAME_VOICE:
+ case AST_FRAME_VIDEO:
+ case AST_FRAME_NULL:
+ case AST_FRAME_IAX:
+ case AST_FRAME_CNG:
+ case AST_FRAME_MODEM:
+ break;
+ }
+
+ if (f)
+ ast_frfree(f);
+ }
+ }
+
+ asthread = AST_PTHREADT_NULL;
+
+ return NULL;
+}
+
+int ast_autoservice_start(struct ast_channel *chan)
+{
+ int res = 0;
+ struct asent *as;
+
+ AST_RWLIST_WRLOCK(&aslist);
+ AST_RWLIST_TRAVERSE(&aslist, as, list) {
+ if (as->chan == chan) {
+ as->use_count++;
+ break;
+ }
+ }
+ AST_RWLIST_UNLOCK(&aslist);
+
+ if (as) {
+ /* Entry exists, autoservice is already handling this channel */
+ return 0;
+ }
+
+ if (!(as = ast_calloc(1, sizeof(*as))))
+ return -1;
+
+ /* New entry created */
+ as->chan = chan;
+ as->use_count = 1;
+
+ ast_channel_lock(chan);
+ as->orig_end_dtmf_flag = ast_test_flag(chan, AST_FLAG_END_DTMF_ONLY) ? 1 : 0;
+ if (!as->orig_end_dtmf_flag)
+ ast_set_flag(chan, AST_FLAG_END_DTMF_ONLY);
+ ast_channel_unlock(chan);
+
+ AST_RWLIST_WRLOCK(&aslist);
+ AST_RWLIST_INSERT_HEAD(&aslist, as, list);
+ AST_RWLIST_UNLOCK(&aslist);
+
+ if (asthread == AST_PTHREADT_NULL) { /* need start the thread */
+ if (ast_pthread_create_background(&asthread, NULL, autoservice_run, NULL)) {
+ ast_log(LOG_WARNING, "Unable to create autoservice thread :(\n");
+ /* There will only be a single member in the list at this point,
+ the one we just added. */
+ AST_RWLIST_WRLOCK(&aslist);
+ AST_RWLIST_REMOVE(&aslist, as, list);
+ AST_RWLIST_UNLOCK(&aslist);
+ free(as);
+ res = -1;
+ } else
+ pthread_kill(asthread, SIGURG);
+ }
+
+ return res;
+}
+
+int ast_autoservice_stop(struct ast_channel *chan)
+{
+ int res = -1;
+ struct asent *as;
+ AST_LIST_HEAD_NOLOCK(, ast_frame) dtmf_frames;
+ struct ast_frame *f;
+ int removed = 0;
+ int orig_end_dtmf_flag = 0;
+
+ AST_LIST_HEAD_INIT_NOLOCK(&dtmf_frames);
+
+ AST_RWLIST_WRLOCK(&aslist);
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&aslist, as, list) {
+ if (as->chan == chan) {
+ AST_RWLIST_REMOVE_CURRENT(list);
+ as->use_count--;
+ if (as->use_count)
+ break;
+ AST_LIST_APPEND_LIST(&dtmf_frames, &as->dtmf_frames, frame_list);
+ orig_end_dtmf_flag = as->orig_end_dtmf_flag;
+ ast_free(as);
+ removed = 1;
+ if (!ast_check_hangup(chan))
+ res = 0;
+ break;
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+
+ if (removed && asthread != AST_PTHREADT_NULL)
+ pthread_kill(asthread, SIGURG);
+
+ AST_RWLIST_UNLOCK(&aslist);
+
+ if (!removed)
+ return 0;
+
+ if (!orig_end_dtmf_flag)
+ ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY);
+
+ /* Wait for it to un-block */
+ while (ast_test_flag(chan, AST_FLAG_BLOCKING))
+ usleep(1000);
+
+ while ((f = AST_LIST_REMOVE_HEAD(&dtmf_frames, frame_list))) {
+ ast_queue_frame(chan, f);
+ ast_frfree(f);
+ }
+
+ return res;
+}
diff --git a/trunk/main/buildinfo.c b/trunk/main/buildinfo.c
new file mode 100644
index 000000000..964e06eb3
--- /dev/null
+++ b/trunk/main/buildinfo.c
@@ -0,0 +1,33 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2005, Digium, Inc.
+ *
+ * Kevin P. Fleming <kpfleming@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
+ *
+ * \brief Build timestamp variables
+ *
+ * \author Kevin P. Fleming <kpfleming@digium.com>
+ */
+
+#include "asterisk/build.h"
+
+const char *ast_build_hostname = BUILD_HOSTNAME;
+const char *ast_build_kernel = BUILD_KERNEL;
+const char *ast_build_machine = BUILD_MACHINE;
+const char *ast_build_os = BUILD_OS;
+const char *ast_build_date = BUILD_DATE;
+const char *ast_build_user = BUILD_USER;
diff --git a/trunk/main/callerid.c b/trunk/main/callerid.c
new file mode 100644
index 000000000..c1d5e80b9
--- /dev/null
+++ b/trunk/main/callerid.c
@@ -0,0 +1,1115 @@
+/*
+ * 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.
+ */
+
+/*! \file
+ *
+ * \brief CallerID Generation support
+ *
+ * \author Mark Spencer <markster@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <time.h>
+#include <math.h>
+#include <ctype.h>
+
+#include "asterisk/ulaw.h"
+#include "asterisk/alaw.h"
+#include "asterisk/frame.h"
+#include "asterisk/channel.h"
+#include "asterisk/callerid.h"
+#include "asterisk/fskmodem.h"
+#include "asterisk/utils.h"
+
+struct callerid_state {
+ fsk_data fskd;
+ char rawdata[256];
+ short oldstuff[160];
+ int oldlen;
+ int pos;
+ int type;
+ int cksum;
+ char name[64];
+ char number[64];
+ int flags;
+ int sawflag;
+ int len;
+
+ int skipflag;
+ unsigned short crc;
+};
+
+
+float cid_dr[4], cid_di[4];
+float clidsb = 8000.0 / 1200.0;
+float sasdr, sasdi;
+float casdr1, casdi1, casdr2, casdi2;
+
+#define CALLERID_SPACE 2200.0 /*!< 2200 hz for "0" */
+#define CALLERID_MARK 1200.0 /*!< 1200 hz for "1" */
+#define SAS_FREQ 440.0
+#define CAS_FREQ1 2130.0
+#define CAS_FREQ2 2750.0
+
+#define AST_CALLERID_UNKNOWN "<unknown>"
+
+static inline void gen_tones(unsigned char *buf, int len, int codec, float ddr1, float ddi1, float ddr2, float ddi2, float *cr1, float *ci1, float *cr2, float *ci2)
+{
+ int x;
+ float t;
+ for (x = 0; x < len; x++) {
+ t = *cr1 * ddr1 - *ci1 * ddi1;
+ *ci1 = *cr1 * ddi1 + *ci1 * ddr1;
+ *cr1 = t;
+ t = 2.0 - (*cr1 * *cr1 + *ci1 * *ci1);
+ *cr1 *= t;
+ *ci1 *= t;
+
+ t = *cr2 * ddr2 - *ci2 * ddi2;
+ *ci2 = *cr2 * ddi2 + *ci2 * ddr2;
+ *cr2 = t;
+ t = 2.0 - (*cr2 * *cr2 + *ci2 * *ci2);
+ *cr2 *= t;
+ *ci2 *= t;
+ buf[x] = AST_LIN2X((*cr1 + *cr2) * 2048.0);
+ }
+}
+
+static inline void gen_tone(unsigned char *buf, int len, int codec, float ddr1, float ddi1, float *cr1, float *ci1)
+{
+ int x;
+ float t;
+ for (x = 0; x < len; x++) {
+ t = *cr1 * ddr1 - *ci1 * ddi1;
+ *ci1 = *cr1 * ddi1 + *ci1 * ddr1;
+ *cr1 = t;
+ t = 2.0 - (*cr1 * *cr1 + *ci1 * *ci1);
+ *cr1 *= t;
+ *ci1 *= t;
+ buf[x] = AST_LIN2X(*cr1 * 8192.0);
+ }
+}
+
+/*! \brief Initialize stuff for inverse FFT */
+void callerid_init(void)
+{
+ cid_dr[0] = cos(CALLERID_SPACE * 2.0 * M_PI / 8000.0);
+ cid_di[0] = sin(CALLERID_SPACE * 2.0 * M_PI / 8000.0);
+ cid_dr[1] = cos(CALLERID_MARK * 2.0 * M_PI / 8000.0);
+ cid_di[1] = sin(CALLERID_MARK * 2.0 * M_PI / 8000.0);
+ sasdr = cos(SAS_FREQ * 2.0 * M_PI / 8000.0);
+ sasdi = sin(SAS_FREQ * 2.0 * M_PI / 8000.0);
+ casdr1 = cos(CAS_FREQ1 * 2.0 * M_PI / 8000.0);
+ casdi1 = sin(CAS_FREQ1 * 2.0 * M_PI / 8000.0);
+ casdr2 = cos(CAS_FREQ2 * 2.0 * M_PI / 8000.0);
+ casdi2 = sin(CAS_FREQ2 * 2.0 * M_PI / 8000.0);
+}
+
+struct callerid_state *callerid_new(int cid_signalling)
+{
+ struct callerid_state *cid;
+
+ if ((cid = ast_calloc(1, sizeof(*cid)))) {
+ cid->fskd.ispb = 7; /* 1200 baud */
+ /* Set up for 1200 / 8000 freq *32 to allow ints */
+ cid->fskd.pllispb = (int)(8000 * 32 / 1200);
+ cid->fskd.pllids = cid->fskd.pllispb/32;
+ cid->fskd.pllispb2 = cid->fskd.pllispb/2;
+
+ cid->fskd.icont = 0; /* PLL REset */
+ /* cid->fskd.hdlc = 0; */ /* Async */
+ cid->fskd.nbit = 8; /* 8 bits */
+ cid->fskd.instop = 1; /* 1 stop bit */
+ /* cid->fskd.paridad = 0; */ /* No parity */
+ cid->fskd.bw = 1; /* Filter 800 Hz */
+ if (cid_signalling == 2) { /* v23 signalling */
+ cid->fskd.f_mark_idx = 4; /* 1300 Hz */
+ cid->fskd.f_space_idx = 5; /* 2100 Hz */
+ } else { /* Bell 202 signalling as default */
+ cid->fskd.f_mark_idx = 2; /* 1200 Hz */
+ cid->fskd.f_space_idx = 3; /* 2200 Hz */
+ }
+ /* cid->fskd.pcola = 0; */ /* No clue */
+ /* cid->fskd.cont = 0.0; */ /* Digital PLL reset */
+ /* cid->fskd.x0 = 0.0; */
+ /* cid->fskd.state = 0; */
+ cid->flags = CID_UNKNOWN_NAME | CID_UNKNOWN_NUMBER;
+ /* cid->pos = 0; */
+
+ fskmodem_init(&cid->fskd);
+ }
+
+ return cid;
+}
+
+void callerid_get(struct callerid_state *cid, char **name, char **number, int *flags)
+{
+ *flags = cid->flags;
+ if (cid->flags & (CID_UNKNOWN_NAME | CID_PRIVATE_NAME))
+ *name = NULL;
+ else
+ *name = cid->name;
+ if (cid->flags & (CID_UNKNOWN_NUMBER | CID_PRIVATE_NUMBER))
+ *number = NULL;
+ else
+ *number = cid->number;
+}
+
+void callerid_get_dtmf(char *cidstring, char *number, int *flags)
+{
+ int i;
+ int code;
+
+ /* "Clear" the number-buffer. */
+ number[0] = 0;
+
+ if (strlen(cidstring) < 2) {
+ ast_debug(1, "No cid detected\n");
+ *flags = CID_UNKNOWN_NUMBER;
+ return;
+ }
+
+ /* Detect protocol and special types */
+ if (cidstring[0] == 'B') {
+ /* Handle special codes */
+ code = atoi(&cidstring[1]);
+ if (code == 0)
+ *flags = CID_UNKNOWN_NUMBER;
+ else if (code == 10)
+ *flags = CID_PRIVATE_NUMBER;
+ else
+ ast_debug(1, "Unknown DTMF code %d\n", code);
+ } else if (cidstring[0] == 'D' && cidstring[2] == '#') {
+ /* .DK special code */
+ if (cidstring[1] == '1')
+ *flags = CID_PRIVATE_NUMBER;
+ if (cidstring[1] == '2' || cidstring[1] == '3')
+ *flags = CID_UNKNOWN_NUMBER;
+ } else if (cidstring[0] == 'D' || cidstring[0] == 'A') {
+ /* "Standard" callerid */
+ for (i = 1; i < strlen(cidstring); i++) {
+ if (cidstring[i] == 'C' || cidstring[i] == '#')
+ break;
+ if (isdigit(cidstring[i]))
+ number[i-1] = cidstring[i];
+ else
+ ast_debug(1, "Unknown CID digit '%c'\n",
+ cidstring[i]);
+ }
+ number[i-1] = 0;
+ } else if (isdigit(cidstring[0])) {
+ /* It begins with a digit, so we parse it as a number and hope
+ * for the best */
+ ast_log(LOG_WARNING, "Couldn't detect start-character. CID "
+ "parsing might be unreliable\n");
+ for (i = 0; i < strlen(cidstring); i++) {
+ if (isdigit(cidstring[i]))
+ number[i] = cidstring[i];
+ else
+ break;
+ }
+ number[i] = 0;
+ } else {
+ ast_debug(1, "Unknown CID protocol, start digit '%c'\n", cidstring[0]);
+ *flags = CID_UNKNOWN_NUMBER;
+ }
+}
+
+int ast_gen_cas(unsigned char *outbuf, int sendsas, int len, int codec)
+{
+ int pos = 0;
+ int saslen = 2400;
+ float cr1 = 1.0;
+ float ci1 = 0.0;
+ float cr2 = 1.0;
+ float ci2 = 0.0;
+
+ if (sendsas) {
+ if (len < saslen)
+ return -1;
+ gen_tone(outbuf, saslen, codec, sasdr, sasdi, &cr1, &ci1);
+ len -= saslen;
+ pos += saslen;
+ cr2 = cr1;
+ ci2 = ci1;
+ }
+ gen_tones(outbuf + pos, len, codec, casdr1, casdi1, casdr2, casdi2, &cr1, &ci1, &cr2, &ci2);
+ return 0;
+}
+
+static unsigned short calc_crc(unsigned short crc, unsigned char data)
+{
+ unsigned int i, j, org, dst;
+ org = data;
+ dst = 0;
+
+ for (i = 0; i < CHAR_BIT; i++) {
+ org <<= 1;
+ dst >>= 1;
+ if (org & 0x100)
+ dst |= 0x80;
+ }
+ data = (unsigned char) dst;
+ crc ^= (unsigned int) data << (16 - CHAR_BIT);
+ for (j = 0; j < CHAR_BIT; j++) {
+ if (crc & 0x8000U)
+ crc = (crc << 1) ^ 0x1021U ;
+ else
+ crc <<= 1 ;
+ }
+ return crc;
+}
+
+int callerid_feed_jp(struct callerid_state *cid, unsigned char *ubuf, int len, int codec)
+{
+ int mylen = len;
+ int olen;
+ int b = 'X';
+ int b2;
+ int res;
+ int x;
+ short *buf;
+
+ buf = alloca(2 * len + cid->oldlen);
+
+ memcpy(buf, cid->oldstuff, cid->oldlen);
+ mylen += cid->oldlen / 2;
+
+ for (x = 0; x < len; x++)
+ buf[x+cid->oldlen/2] = AST_XLAW(ubuf[x]);
+
+ while (mylen >= 160) {
+ b = b2 = 0;
+ olen = mylen;
+ res = fsk_serial(&cid->fskd, buf, &mylen, &b);
+
+ if (mylen < 0) {
+ ast_log(LOG_ERROR, "fsk_serial made mylen < 0 (%d)\n", mylen);
+ return -1;
+ }
+
+ buf += (olen - mylen);
+
+ if (res < 0) {
+ ast_log(LOG_NOTICE, "fsk_serial failed\n");
+ return -1;
+ }
+
+ if (res == 1) {
+ b2 = b;
+ b &= 0x7f;
+
+ /* crc checksum calculation */
+ if (cid->sawflag > 1)
+ cid->crc = calc_crc(cid->crc, (unsigned char) b2);
+
+ /* Ignore invalid bytes */
+ if (b > 0xff)
+ continue;
+
+ /* skip DLE if needed */
+ if (cid->sawflag > 0) {
+ if (cid->sawflag != 5 && cid->skipflag == 0 && b == 0x10) {
+ cid->skipflag = 1 ;
+ continue ;
+ }
+ }
+ if (cid->skipflag == 1)
+ cid->skipflag = 0 ;
+
+ /* caller id retrieval */
+ switch (cid->sawflag) {
+ case 0: /* DLE */
+ if (b == 0x10) {
+ cid->sawflag = 1;
+ cid->skipflag = 0;
+ cid->crc = 0;
+ }
+ break;
+ case 1: /* SOH */
+ if (b == 0x01)
+ cid->sawflag = 2;
+ break ;
+ case 2: /* HEADER */
+ if (b == 0x07)
+ cid->sawflag = 3;
+ break;
+ case 3: /* STX */
+ if (b == 0x02)
+ cid->sawflag = 4;
+ break;
+ case 4: /* SERVICE TYPE */
+ if (b == 0x40)
+ cid->sawflag = 5;
+ break;
+ case 5: /* Frame Length */
+ cid->sawflag = 6;
+ break;
+ case 6: /* NUMBER TYPE */
+ cid->sawflag = 7;
+ cid->pos = 0;
+ cid->rawdata[cid->pos++] = b;
+ break;
+ case 7: /* NUMBER LENGTH */
+ cid->sawflag = 8;
+ cid->len = b;
+ if ((cid->len+2) >= sizeof(cid->rawdata)) {
+ ast_log(LOG_WARNING, "too long caller id string\n") ;
+ return -1;
+ }
+ cid->rawdata[cid->pos++] = b;
+ break;
+ case 8: /* Retrieve message */
+ cid->rawdata[cid->pos++] = b;
+ cid->len--;
+ if (cid->len<=0) {
+ cid->rawdata[cid->pos] = '\0';
+ cid->sawflag = 9;
+ }
+ break;
+ case 9: /* ETX */
+ cid->sawflag = 10;
+ break;
+ case 10: /* CRC Checksum 1 */
+ cid->sawflag = 11;
+ break;
+ case 11: /* CRC Checksum 2 */
+ cid->sawflag = 12;
+ if (cid->crc != 0) {
+ ast_log(LOG_WARNING, "crc checksum error\n") ;
+ return -1;
+ }
+ /* extract caller id data */
+ for (x = 0; x < cid->pos;) {
+ switch (cid->rawdata[x++]) {
+ case 0x02: /* caller id number */
+ cid->number[0] = '\0';
+ cid->name[0] = '\0';
+ cid->flags = 0;
+ res = cid->rawdata[x++];
+ ast_copy_string(cid->number, &cid->rawdata[x], res+1);
+ x += res;
+ break;
+ case 0x21: /* additional information */
+ /* length */
+ x++;
+ /* number type */
+ switch (cid->rawdata[x]) {
+ case 0x00: /* unknown */
+ case 0x01: /* international number */
+ case 0x02: /* domestic number */
+ case 0x03: /* network */
+ case 0x04: /* local call */
+ case 0x06: /* short dial number */
+ case 0x07: /* reserved */
+ default: /* reserved */
+ ast_debug(2, "cid info:#1=%X\n", cid->rawdata[x]);
+ break ;
+ }
+ x++;
+ /* numbering plan octed 4 */
+ x++;
+ /* numbering plan octed 5 */
+ switch (cid->rawdata[x]) {
+ case 0x00: /* unknown */
+ case 0x01: /* recommendation E.164 ISDN */
+ case 0x03: /* recommendation X.121 */
+ case 0x04: /* telex dial plan */
+ case 0x08: /* domestic dial plan */
+ case 0x09: /* private dial plan */
+ case 0x05: /* reserved */
+ default: /* reserved */
+ ast_debug(2, "cid info:#2=%X\n", cid->rawdata[x]);
+ break ;
+ }
+ x++;
+ break ;
+ case 0x04: /* no callerid reason */
+ /* length */
+ x++;
+ /* no callerid reason code */
+ switch (cid->rawdata[x]) {
+ case 'P': /* caller id denied by user */
+ case 'O': /* service not available */
+ case 'C': /* pay phone */
+ case 'S': /* service congested */
+ cid->flags |= CID_UNKNOWN_NUMBER;
+ ast_debug(2, "no cid reason:%c\n",cid->rawdata[x]);
+ break ;
+ }
+ x++;
+ break ;
+ case 0x09: /* dialed number */
+ /* length */
+ res = cid->rawdata[x++];
+ /* dialed number */
+ x += res;
+ break ;
+ case 0x22: /* dialed number additional information */
+ /* length */
+ x++;
+ /* number type */
+ switch (cid->rawdata[x]) {
+ case 0x00: /* unknown */
+ case 0x01: /* international number */
+ case 0x02: /* domestic number */
+ case 0x03: /* network */
+ case 0x04: /* local call */
+ case 0x06: /* short dial number */
+ case 0x07: /* reserved */
+ default: /* reserved */
+ if (option_debug > 1)
+ ast_log(LOG_NOTICE, "did info:#1=%X\n", cid->rawdata[x]);
+ break ;
+ }
+ x++;
+ /* numbering plan octed 4 */
+ x++;
+ /* numbering plan octed 5 */
+ switch (cid->rawdata[x]) {
+ case 0x00: /* unknown */
+ case 0x01: /* recommendation E.164 ISDN */
+ case 0x03: /* recommendation X.121 */
+ case 0x04: /* telex dial plan */
+ case 0x08: /* domestic dial plan */
+ case 0x09: /* private dial plan */
+ case 0x05: /* reserved */
+ default: /* reserved */
+ ast_debug(2, "did info:#2=%X\n", cid->rawdata[x]);
+ break ;
+ }
+ x++;
+ break ;
+ }
+ }
+ return 1;
+ break;
+ default:
+ ast_log(LOG_ERROR, "invalid value in sawflag %d\n", cid->sawflag);
+ }
+ }
+ }
+ if (mylen) {
+ memcpy(cid->oldstuff, buf, mylen * 2);
+ cid->oldlen = mylen * 2;
+ } else
+ cid->oldlen = 0;
+
+ return 0;
+}
+
+
+int callerid_feed(struct callerid_state *cid, unsigned char *ubuf, int len, int codec)
+{
+ int mylen = len;
+ int olen;
+ int b = 'X';
+ int res;
+ int x;
+ short *buf;
+
+ buf = alloca(2 * len + cid->oldlen);
+
+ memcpy(buf, cid->oldstuff, cid->oldlen);
+ mylen += cid->oldlen/2;
+
+ for (x = 0; x < len; x++)
+ buf[x+cid->oldlen/2] = AST_XLAW(ubuf[x]);
+ while (mylen >= 160) {
+ olen = mylen;
+ res = fsk_serial(&cid->fskd, buf, &mylen, &b);
+ if (mylen < 0) {
+ ast_log(LOG_ERROR, "fsk_serial made mylen < 0 (%d)\n", mylen);
+ return -1;
+ }
+ buf += (olen - mylen);
+ if (res < 0) {
+ ast_log(LOG_NOTICE, "fsk_serial failed\n");
+ return -1;
+ }
+ if (res == 1) {
+ /* Ignore invalid bytes */
+ if (b > 0xff)
+ continue;
+ switch (cid->sawflag) {
+ case 0: /* Look for flag */
+ if (b == 'U')
+ cid->sawflag = 2;
+ break;
+ case 2: /* Get lead-in */
+ if ((b == 0x04) || (b == 0x80) || (b == 0x06) || (b == 0x82)) {
+ cid->type = b;
+ cid->sawflag = 3;
+ cid->cksum = b;
+ }
+ break;
+ case 3: /* Get length */
+ /* Not a lead in. We're ready */
+ cid->sawflag = 4;
+ cid->len = b;
+ cid->pos = 0;
+ cid->cksum += b;
+ break;
+ case 4: /* Retrieve message */
+ if (cid->pos >= 128) {
+ ast_log(LOG_WARNING, "Caller ID too long???\n");
+ return -1;
+ }
+ cid->rawdata[cid->pos++] = b;
+ cid->len--;
+ cid->cksum += b;
+ if (!cid->len) {
+ cid->rawdata[cid->pos] = '\0';
+ cid->sawflag = 5;
+ }
+ break;
+ case 5: /* Check checksum */
+ if (b != (256 - (cid->cksum & 0xff))) {
+ ast_log(LOG_NOTICE, "Caller*ID failed checksum\n");
+ /* Try again */
+ cid->sawflag = 0;
+ break;
+ }
+
+ cid->number[0] = '\0';
+ cid->name[0] = '\0';
+ /* Update flags */
+ cid->flags = 0;
+ /* If we get this far we're fine. */
+ if ((cid->type == 0x80) || (cid->type == 0x82)) {
+ /* MDMF */
+ /* Go through each element and process */
+ for (x = 0; x < cid->pos;) {
+ switch (cid->rawdata[x++]) {
+ case 1:
+ /* Date */
+ break;
+ case 2: /* Number */
+ case 3: /* Number (for Zebble) */
+ case 4: /* Number */
+ res = cid->rawdata[x];
+ if (res > 32) {
+ ast_log(LOG_NOTICE, "Truncating long caller ID number from %d bytes to 32\n", cid->rawdata[x]);
+ res = 32;
+ }
+ if (ast_strlen_zero(cid->number)) {
+ memcpy(cid->number, cid->rawdata + x + 1, res);
+ /* Null terminate */
+ cid->number[res] = '\0';
+ }
+ break;
+ case 6: /* Stentor Call Qualifier (ie. Long Distance call) */
+ break;
+ case 7: /* Name */
+ case 8: /* Name */
+ res = cid->rawdata[x];
+ if (res > 32) {
+ ast_log(LOG_NOTICE, "Truncating long caller ID name from %d bytes to 32\n", cid->rawdata[x]);
+ res = 32;
+ }
+ memcpy(cid->name, cid->rawdata + x + 1, res);
+ cid->name[res] = '\0';
+ break;
+ case 11: /* Message Waiting */
+ res = cid->rawdata[x + 1];
+ if (res)
+ cid->flags |= CID_MSGWAITING;
+ else
+ cid->flags |= CID_NOMSGWAITING;
+ break;
+ case 17: /* UK: Call type, 1=Voice Call, 2=Ringback when free, 129=Message waiting */
+ case 19: /* UK: Network message system status (Number of messages waiting) */
+ case 22: /* Something French */
+ break;
+ default:
+ ast_log(LOG_NOTICE, "Unknown IE %d\n", cid->rawdata[x - 1]);
+ }
+ res = cid->rawdata[x];
+ if (0 > res){ /* Negative offset in the CID Spill */
+ ast_log(LOG_NOTICE, "IE %d has bad field length of %d at offset %d\n", cid->rawdata[x-1], cid->rawdata[x], x);
+ /* Try again */
+ cid->sawflag = 0;
+ break; /* Exit the loop */
+ }
+ x += cid->rawdata[x];
+ x++;
+ }
+ } else if (cid->type == 0x6) {
+ /* VMWI SDMF */
+ if (cid->rawdata[2] == 0x42) {
+ cid->flags |= CID_MSGWAITING;
+ } else if (cid->rawdata[2] == 0x6f) {
+ cid->flags |= CID_NOMSGWAITING;
+ }
+ } else {
+ /* SDMF */
+ ast_copy_string(cid->number, cid->rawdata + 8, sizeof(cid->number));
+ }
+ if (!strcmp(cid->number, "P")) {
+ strcpy(cid->number, "");
+ cid->flags |= CID_PRIVATE_NUMBER;
+ } else if (!strcmp(cid->number, "O") || ast_strlen_zero(cid->number)) {
+ strcpy(cid->number, "");
+ cid->flags |= CID_UNKNOWN_NUMBER;
+ }
+ if (!strcmp(cid->name, "P")) {
+ strcpy(cid->name, "");
+ cid->flags |= CID_PRIVATE_NAME;
+ } else if (!strcmp(cid->name, "O") || ast_strlen_zero(cid->name)) {
+ strcpy(cid->name, "");
+ cid->flags |= CID_UNKNOWN_NAME;
+ }
+ return 1;
+ break;
+ default:
+ ast_log(LOG_ERROR, "Dunno what to do with a digit in sawflag %d\n", cid->sawflag);
+ }
+ }
+ }
+ if (mylen) {
+ memcpy(cid->oldstuff, buf, mylen * 2);
+ cid->oldlen = mylen * 2;
+ } else
+ cid->oldlen = 0;
+
+ return 0;
+}
+
+void callerid_free(struct callerid_state *cid)
+{
+ ast_free(cid);
+}
+
+static int callerid_genmsg(char *msg, int size, const char *number, const char *name, int flags)
+{
+ struct timeval tv = ast_tvnow();
+ struct ast_tm tm;
+ char *ptr;
+ int res;
+ int i, x;
+
+ /* Get the time */
+ ast_localtime(&tv, &tm, NULL);
+
+ ptr = msg;
+
+ /* Format time and message header */
+ res = snprintf(ptr, size, "\001\010%02d%02d%02d%02d", tm.tm_mon + 1,
+ tm.tm_mday, tm.tm_hour, tm.tm_min);
+ size -= res;
+ ptr += res;
+ if (ast_strlen_zero(number) || (flags & CID_UNKNOWN_NUMBER)) {
+ /* Indicate number not known */
+ res = snprintf(ptr, size, "\004\001O");
+ size -= res;
+ ptr += res;
+ } else if (flags & CID_PRIVATE_NUMBER) {
+ /* Indicate number is private */
+ res = snprintf(ptr, size, "\004\001P");
+ size -= res;
+ ptr += res;
+ } else {
+ /* Send up to 16 digits of number MAX */
+ i = strlen(number);
+ if (i > 16)
+ i = 16;
+ res = snprintf(ptr, size, "\002%c", i);
+ size -= res;
+ ptr += res;
+ for (x = 0; x < i; x++)
+ ptr[x] = number[x];
+ ptr[i] = '\0';
+ ptr += i;
+ size -= i;
+ }
+
+ if (ast_strlen_zero(name) || (flags & CID_UNKNOWN_NAME)) {
+ /* Indicate name not known */
+ res = snprintf(ptr, size, "\010\001O");
+ size -= res;
+ ptr += res;
+ } else if (flags & CID_PRIVATE_NAME) {
+ /* Indicate name is private */
+ res = snprintf(ptr, size, "\010\001P");
+ size -= res;
+ ptr += res;
+ } else {
+ /* Send up to 16 digits of name MAX */
+ i = strlen(name);
+ if (i > 16)
+ i = 16;
+ res = snprintf(ptr, size, "\007%c", i);
+ size -= res;
+ ptr += res;
+ for (x = 0; x < i; x++)
+ ptr[x] = name[x];
+ ptr[i] = '\0';
+ ptr += i;
+ size -= i;
+ }
+ return (ptr - msg);
+
+}
+
+int vmwi_generate(unsigned char *buf, int active, int mdmf, int codec)
+{
+ unsigned char msg[256];
+ int len=0;
+ int sum;
+ int x;
+ int bytes = 0;
+ float cr = 1.0;
+ float ci = 0.0;
+ float scont = 0.0;
+
+ if (mdmf) {
+ /* MDMF Message waiting */
+ msg[len++] = 0x82;
+ /* Length is 3 */
+ msg[len++] = 3;
+ /* IE is "Message Waiting Parameter" */
+ msg[len++] = 0xb;
+ /* Length of IE is one */
+ msg[len++] = 1;
+ /* Active or not */
+ if (active)
+ msg[len++] = 0xff;
+ else
+ msg[len++] = 0x00;
+ } else {
+ /* SDMF Message waiting */
+ msg[len++] = 0x6;
+ /* Length is 3 */
+ msg[len++] = 3;
+ if (active) {
+ msg[len++] = 0x42;
+ msg[len++] = 0x42;
+ msg[len++] = 0x42;
+ } else {
+ msg[len++] = 0x6f;
+ msg[len++] = 0x6f;
+ msg[len++] = 0x6f;
+ }
+ }
+ sum = 0;
+ for (x = 0; x < len; x++)
+ sum += msg[x];
+ sum = (256 - (sum & 255));
+ msg[len++] = sum;
+ /* Wait a half a second */
+ for (x = 0; x < 4000; x++)
+ PUT_BYTE(0x7f);
+ /* Transmit 30 0x55's (looks like a square wave) for channel seizure */
+ for (x = 0; x < 30; x++)
+ PUT_CLID(0x55);
+ /* Send 170ms of callerid marks */
+ for (x = 0; x < 170; x++)
+ PUT_CLID_MARKMS;
+ for (x = 0; x < len; x++) {
+ PUT_CLID(msg[x]);
+ }
+ /* Send 50 more ms of marks */
+ for (x = 0; x < 50; x++)
+ PUT_CLID_MARKMS;
+ return bytes;
+}
+
+int callerid_generate(unsigned char *buf, const char *number, const char *name, int flags, int callwaiting, int codec)
+{
+ int bytes=0;
+ int x, sum;
+ int len;
+
+ /* Initial carriers (real/imaginary) */
+ float cr = 1.0;
+ float ci = 0.0;
+ float scont = 0.0;
+ char msg[256];
+ len = callerid_genmsg(msg, sizeof(msg), number, name, flags);
+ if (!callwaiting) {
+ /* Wait a half a second */
+ for (x = 0; x < 4000; x++)
+ PUT_BYTE(0x7f);
+ /* Transmit 30 0x55's (looks like a square wave) for channel seizure */
+ for (x = 0; x < 30; x++)
+ PUT_CLID(0x55);
+ }
+ /* Send 150ms of callerid marks */
+ for (x = 0; x < 150; x++)
+ PUT_CLID_MARKMS;
+ /* Send 0x80 indicating MDMF format */
+ PUT_CLID(0x80);
+ /* Put length of whole message */
+ PUT_CLID(len);
+ sum = 0x80 + strlen(msg);
+ /* Put each character of message and update checksum */
+ for (x = 0; x < len; x++) {
+ PUT_CLID(msg[x]);
+ sum += msg[x];
+ }
+ /* Send 2's compliment of sum */
+ PUT_CLID(256 - (sum & 255));
+
+ /* Send 50 more ms of marks */
+ for (x = 0; x < 50; x++)
+ PUT_CLID_MARKMS;
+
+ return bytes;
+}
+
+/*! \brief Clean up phone string
+ * remove '(', ' ', ')', non-trailing '.', and '-' not in square brackets.
+ * Basically, remove anything that could be invalid in a pattern.
+ */
+void ast_shrink_phone_number(char *n)
+{
+ int x, y=0;
+ int bracketed = 0;
+
+ for (x = 0; n[x]; x++) {
+ switch (n[x]) {
+ case '[':
+ bracketed++;
+ n[y++] = n[x];
+ break;
+ case ']':
+ bracketed--;
+ n[y++] = n[x];
+ break;
+ case '-':
+ if (bracketed)
+ n[y++] = n[x];
+ break;
+ case '.':
+ if (!n[x+1])
+ n[y++] = n[x];
+ break;
+ default:
+ /* ignore parenthesis and whitespace */
+ if (!strchr("( )", n[x]))
+ n[y++] = n[x];
+ }
+ }
+ n[y] = '\0';
+}
+
+/*! \brief Checks if phone number consists of valid characters
+ \param exten String that needs to be checked
+ \param valid Valid characters in string
+ \return 1 if valid string, 0 if string contains invalid characters
+*/
+static int ast_is_valid_string(const char *exten, const char *valid)
+{
+ int x;
+
+ if (ast_strlen_zero(exten))
+ return 0;
+ for (x = 0; exten[x]; x++)
+ if (!strchr(valid, exten[x]))
+ return 0;
+ return 1;
+}
+
+/*! \brief checks if string consists only of digits and * \# and +
+ \return 1 if string is valid AST phone number
+ \return 0 if not
+*/
+int ast_isphonenumber(const char *n)
+{
+ return ast_is_valid_string(n, "0123456789*#+");
+}
+
+/*! \brief checks if string consists only of digits and ( ) - * \# and +
+ Pre-qualifies the string for ast_shrink_phone_number()
+ \return 1 if string is valid AST shrinkable phone number
+ \return 0 if not
+*/
+int ast_is_shrinkable_phonenumber(const char *exten)
+{
+ return ast_is_valid_string(exten, "0123456789*#+()-.");
+}
+
+/*! \brief parse string for caller id information
+ \return always returns 0, as the code always returns something.
+ XXX note that 'name' is not parsed consistently e.g. we have
+
+ input location name
+ " foo bar " <123> 123 ' foo bar ' (with spaces around)
+ " foo bar " NULL 'foo bar' (without spaces around)
+ " foo bar <123>" 123 '" foo bar'
+ The parsing of leading and trailing space/quotes should be more consistent.
+*/
+int ast_callerid_parse(char *instr, char **name, char **location)
+{
+ char *ns, *ne, *ls, *le;
+
+ /* Try "name" <location> format or name <location> format */
+ if ((ls = strchr(instr, '<')) && (le = strchr(ls, '>'))) {
+ *ls = *le = '\0'; /* location found, trim off the brackets */
+ *location = ls + 1; /* and this is the result */
+ if ((ns = strchr(instr, '"')) && (ne = strchr(ns + 1, '"'))) {
+ *ns = *ne = '\0'; /* trim off the quotes */
+ *name = ns + 1; /* and this is the name */
+ } else { /* no quotes, trim off leading and trailing spaces */
+ *name = ast_skip_blanks(instr);
+ ast_trim_blanks(*name);
+ }
+ } else { /* no valid brackets */
+ char tmp[256];
+
+ ast_copy_string(tmp, instr, sizeof(tmp));
+ ast_shrink_phone_number(tmp);
+ if (ast_isphonenumber(tmp)) { /* Assume it's just a location */
+ *name = NULL;
+ strcpy(instr, tmp); /* safe, because tmp will always be the same size or smaller than instr */
+ *location = instr;
+ } else { /* Assume it's just a name. */
+ *location = NULL;
+ if ((ns = strchr(instr, '"')) && (ne = strchr(ns + 1, '"'))) {
+ *ns = *ne = '\0'; /* trim off the quotes */
+ *name = ns + 1; /* and this is the name */
+ } else { /* no quotes, trim off leading and trailing spaces */
+ *name = ast_skip_blanks(instr);
+ ast_trim_blanks(*name);
+ }
+ }
+ }
+ return 0;
+}
+
+static int __ast_callerid_generate(unsigned char *buf, const char *name, const char *number, int callwaiting, int codec)
+{
+ if (ast_strlen_zero(name))
+ name = NULL;
+ if (ast_strlen_zero(number))
+ number = NULL;
+ return callerid_generate(buf, number, name, 0, callwaiting, codec);
+}
+
+int ast_callerid_generate(unsigned char *buf, const char *name, const char *number, int codec)
+{
+ return __ast_callerid_generate(buf, name, number, 0, codec);
+}
+
+int ast_callerid_callwaiting_generate(unsigned char *buf, const char *name, const char *number, int codec)
+{
+ return __ast_callerid_generate(buf, name, number, 1, codec);
+}
+
+char *ast_callerid_merge(char *buf, int bufsiz, const char *name, const char *num, const char *unknown)
+{
+ if (!unknown)
+ unknown = "<unknown>";
+ if (name && num)
+ snprintf(buf, bufsiz, "\"%s\" <%s>", name, num);
+ else if (name)
+ ast_copy_string(buf, name, bufsiz);
+ else if (num)
+ ast_copy_string(buf, num, bufsiz);
+ else
+ ast_copy_string(buf, unknown, bufsiz);
+ return buf;
+}
+
+int ast_callerid_split(const char *buf, char *name, int namelen, char *num, int numlen)
+{
+ char *tmp;
+ char *l = NULL, *n = NULL;
+
+ tmp = ast_strdupa(buf);
+ ast_callerid_parse(tmp, &n, &l);
+ if (n)
+ ast_copy_string(name, n, namelen);
+ else
+ name[0] = '\0';
+ if (l) {
+ ast_shrink_phone_number(l);
+ ast_copy_string(num, l, numlen);
+ } else
+ num[0] = '\0';
+ return 0;
+}
+
+/*! \brief Translation table for Caller ID Presentation settings */
+static struct {
+ int val;
+ const char *name;
+ const char *description;
+} pres_types[] = {
+ { AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED, "allowed_not_screened", "Presentation Allowed, Not Screened"},
+ { AST_PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN, "allowed_passed_screen", "Presentation Allowed, Passed Screen"},
+ { AST_PRES_ALLOWED_USER_NUMBER_FAILED_SCREEN, "allowed_failed_screen", "Presentation Allowed, Failed Screen"},
+ { AST_PRES_ALLOWED_NETWORK_NUMBER, "allowed", "Presentation Allowed, Network Number"},
+ { AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED, "prohib_not_screened", "Presentation Prohibited, Not Screened"},
+ { AST_PRES_PROHIB_USER_NUMBER_PASSED_SCREEN, "prohib_passed_screen", "Presentation Prohibited, Passed Screen"},
+ { AST_PRES_PROHIB_USER_NUMBER_FAILED_SCREEN, "prohib_failed_screen", "Presentation Prohibited, Failed Screen"},
+ { AST_PRES_PROHIB_NETWORK_NUMBER, "prohib", "Presentation Prohibited, Network Number"},
+ { AST_PRES_NUMBER_NOT_AVAILABLE, "unavailable", "Number Unavailable"},
+};
+
+/*! \brief Convert caller ID text code to value
+ used in config file parsing
+ \param data text string
+ \return value AST_PRES_ from callerid.h
+*/
+int ast_parse_caller_presentation(const char *data)
+{
+ int i;
+
+ for (i = 0; i < ((sizeof(pres_types) / sizeof(pres_types[0]))); i++) {
+ if (!strcasecmp(pres_types[i].name, data))
+ return pres_types[i].val;
+ }
+
+ return -1;
+}
+
+/*! \brief Convert caller ID pres value to explanatory string
+ \param data value (see callerid.h AST_PRES_ )
+ \return string for human presentation
+*/
+const char *ast_describe_caller_presentation(int data)
+{
+ int i;
+
+ for (i = 0; i < ((sizeof(pres_types) / sizeof(pres_types[0]))); i++) {
+ if (pres_types[i].val == data)
+ return pres_types[i].description;
+ }
+
+ return "unknown";
+}
+
+/*! \brief Convert caller ID pres value to text code
+ \param data text string
+ \return string for config file
+*/
+const char *ast_named_caller_presentation(int data)
+{
+ int i;
+
+ for (i = 0; i < ((sizeof(pres_types) / sizeof(pres_types[0]))); i++) {
+ if (pres_types[i].val == data)
+ return pres_types[i].name;
+ }
+
+ return "unknown";
+}
diff --git a/trunk/main/cdr.c b/trunk/main/cdr.c
new file mode 100644
index 000000000..028e0eb14
--- /dev/null
+++ b/trunk/main/cdr.c
@@ -0,0 +1,1456 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006, 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.
+ */
+
+/*! \file
+ *
+ * \brief Call Detail Record API
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \note Includes code and algorithms from the Zapata library.
+ *
+ * \note We do a lot of checking here in the CDR code to try to be sure we don't ever let a CDR slip
+ * through our fingers somehow. If someone allocates a CDR, it must be completely handled normally
+ * or a WARNING shall be logged, so that we can best keep track of any escape condition where the CDR
+ * isn't properly generated and posted.
+ */
+
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <signal.h>
+
+#include "asterisk/lock.h"
+#include "asterisk/channel.h"
+#include "asterisk/cdr.h"
+#include "asterisk/callerid.h"
+#include "asterisk/manager.h"
+#include "asterisk/causes.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/utils.h"
+#include "asterisk/sched.h"
+#include "asterisk/config.h"
+#include "asterisk/cli.h"
+#include "asterisk/stringfields.h"
+
+/*! Default AMA flag for billing records (CDR's) */
+int ast_default_amaflags = AST_CDR_DOCUMENTATION;
+char ast_default_accountcode[AST_MAX_ACCOUNT_CODE];
+
+struct ast_cdr_beitem {
+ char name[20];
+ char desc[80];
+ ast_cdrbe be;
+ AST_RWLIST_ENTRY(ast_cdr_beitem) list;
+};
+
+static AST_RWLIST_HEAD_STATIC(be_list, ast_cdr_beitem);
+
+struct ast_cdr_batch_item {
+ struct ast_cdr *cdr;
+ struct ast_cdr_batch_item *next;
+};
+
+static struct ast_cdr_batch {
+ int size;
+ struct ast_cdr_batch_item *head;
+ struct ast_cdr_batch_item *tail;
+} *batch = NULL;
+
+static struct sched_context *sched;
+static int cdr_sched = -1;
+static pthread_t cdr_thread = AST_PTHREADT_NULL;
+
+#define BATCH_SIZE_DEFAULT 100
+#define BATCH_TIME_DEFAULT 300
+#define BATCH_SCHEDULER_ONLY_DEFAULT 0
+#define BATCH_SAFE_SHUTDOWN_DEFAULT 1
+
+static int enabled; /*! Is the CDR subsystem enabled ? */
+static int unanswered;
+static int batchmode;
+static int batchsize;
+static int batchtime;
+static int batchscheduleronly;
+static int batchsafeshutdown;
+
+AST_MUTEX_DEFINE_STATIC(cdr_batch_lock);
+
+/* these are used to wake up the CDR thread when there's work to do */
+AST_MUTEX_DEFINE_STATIC(cdr_pending_lock);
+static ast_cond_t cdr_pending_cond;
+
+int check_cdr_enabled()
+{
+ return enabled;
+}
+
+int ast_cdr_log_unanswered(void)
+{
+ return unanswered;
+}
+
+/*! Register a CDR driver. Each registered CDR driver generates a CDR
+ \return 0 on success, -1 on failure
+*/
+int ast_cdr_register(const char *name, const char *desc, ast_cdrbe be)
+{
+ struct ast_cdr_beitem *i = NULL;
+
+ if (!name)
+ return -1;
+
+ if (!be) {
+ ast_log(LOG_WARNING, "CDR engine '%s' lacks backend\n", name);
+ return -1;
+ }
+
+ AST_RWLIST_WRLOCK(&be_list);
+ AST_RWLIST_TRAVERSE(&be_list, i, list) {
+ if (!strcasecmp(name, i->name)) {
+ ast_log(LOG_WARNING, "Already have a CDR backend called '%s'\n", name);
+ AST_RWLIST_UNLOCK(&be_list);
+ return -1;
+ }
+ }
+
+ if (!(i = ast_calloc(1, sizeof(*i))))
+ return -1;
+
+ i->be = be;
+ ast_copy_string(i->name, name, sizeof(i->name));
+ ast_copy_string(i->desc, desc, sizeof(i->desc));
+
+ AST_RWLIST_INSERT_HEAD(&be_list, i, list);
+ AST_RWLIST_UNLOCK(&be_list);
+
+ return 0;
+}
+
+/*! unregister a CDR driver */
+void ast_cdr_unregister(const char *name)
+{
+ struct ast_cdr_beitem *i = NULL;
+
+ AST_RWLIST_WRLOCK(&be_list);
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&be_list, i, list) {
+ if (!strcasecmp(name, i->name)) {
+ AST_RWLIST_REMOVE_CURRENT(list);
+ ast_verb(2, "Unregistered '%s' CDR backend\n", name);
+ ast_free(i);
+ break;
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+ AST_RWLIST_UNLOCK(&be_list);
+}
+
+/*! Duplicate a CDR record
+ \returns Pointer to new CDR record
+*/
+struct ast_cdr *ast_cdr_dup(struct ast_cdr *cdr)
+{
+ struct ast_cdr *newcdr;
+
+ if (!cdr) /* don't die if we get a null cdr pointer */
+ return NULL;
+ newcdr = ast_cdr_alloc();
+ if (!newcdr)
+ return NULL;
+
+ memcpy(newcdr, cdr, sizeof(*newcdr));
+ /* The varshead is unusable, volatile even, after the memcpy so we take care of that here */
+ memset(&newcdr->varshead, 0, sizeof(newcdr->varshead));
+ ast_cdr_copy_vars(newcdr, cdr);
+ newcdr->next = NULL;
+
+ return newcdr;
+}
+
+static const char *ast_cdr_getvar_internal(struct ast_cdr *cdr, const char *name, int recur)
+{
+ if (ast_strlen_zero(name))
+ return NULL;
+
+ for (; cdr; cdr = recur ? cdr->next : NULL) {
+ struct ast_var_t *variables;
+ struct varshead *headp = &cdr->varshead;
+ AST_LIST_TRAVERSE(headp, variables, entries) {
+ if (!strcasecmp(name, ast_var_name(variables)))
+ return ast_var_value(variables);
+ }
+ }
+
+ return NULL;
+}
+
+static void cdr_get_tv(struct timeval tv, const char *fmt, char *buf, int bufsize)
+{
+ if (fmt == NULL) { /* raw mode */
+ snprintf(buf, bufsize, "%ld.%06ld", (long)tv.tv_sec, (long)tv.tv_usec);
+ } else {
+ struct ast_tm tm;
+
+ ast_localtime(&tv, &tm, NULL);
+ ast_strftime(buf, bufsize, fmt, &tm);
+ }
+}
+
+/*! CDR channel variable retrieval */
+void ast_cdr_getvar(struct ast_cdr *cdr, const char *name, char **ret, char *workspace, int workspacelen, int recur, int raw)
+{
+ const char *fmt = "%Y-%m-%d %T";
+ const char *varbuf;
+
+ if (!cdr) /* don't die if the cdr is null */
+ return;
+
+ *ret = NULL;
+ /* special vars (the ones from the struct ast_cdr when requested by name)
+ I'd almost say we should convert all the stringed vals to vars */
+
+ if (!strcasecmp(name, "clid"))
+ ast_copy_string(workspace, cdr->clid, workspacelen);
+ else if (!strcasecmp(name, "src"))
+ ast_copy_string(workspace, cdr->src, workspacelen);
+ else if (!strcasecmp(name, "dst"))
+ ast_copy_string(workspace, cdr->dst, workspacelen);
+ else if (!strcasecmp(name, "dcontext"))
+ ast_copy_string(workspace, cdr->dcontext, workspacelen);
+ else if (!strcasecmp(name, "channel"))
+ ast_copy_string(workspace, cdr->channel, workspacelen);
+ else if (!strcasecmp(name, "dstchannel"))
+ ast_copy_string(workspace, cdr->dstchannel, workspacelen);
+ else if (!strcasecmp(name, "lastapp"))
+ ast_copy_string(workspace, cdr->lastapp, workspacelen);
+ else if (!strcasecmp(name, "lastdata"))
+ ast_copy_string(workspace, cdr->lastdata, workspacelen);
+ else if (!strcasecmp(name, "start"))
+ cdr_get_tv(cdr->start, raw ? NULL : fmt, workspace, workspacelen);
+ else if (!strcasecmp(name, "answer"))
+ cdr_get_tv(cdr->answer, raw ? NULL : fmt, workspace, workspacelen);
+ else if (!strcasecmp(name, "end"))
+ cdr_get_tv(cdr->end, raw ? NULL : fmt, workspace, workspacelen);
+ else if (!strcasecmp(name, "duration"))
+ snprintf(workspace, workspacelen, "%ld", cdr->duration ? cdr->duration : (long)ast_tvdiff_ms(ast_tvnow(), cdr->start) / 1000);
+ else if (!strcasecmp(name, "billsec"))
+ snprintf(workspace, workspacelen, "%ld", cdr->billsec || cdr->answer.tv_sec == 0 ? cdr->billsec : (long)ast_tvdiff_ms(ast_tvnow(), cdr->answer) / 1000);
+ else if (!strcasecmp(name, "disposition")) {
+ if (raw) {
+ snprintf(workspace, workspacelen, "%ld", cdr->disposition);
+ } else {
+ ast_copy_string(workspace, ast_cdr_disp2str(cdr->disposition), workspacelen);
+ }
+ } else if (!strcasecmp(name, "amaflags")) {
+ if (raw) {
+ snprintf(workspace, workspacelen, "%ld", cdr->amaflags);
+ } else {
+ ast_copy_string(workspace, ast_cdr_flags2str(cdr->amaflags), workspacelen);
+ }
+ } else if (!strcasecmp(name, "accountcode"))
+ ast_copy_string(workspace, cdr->accountcode, workspacelen);
+ else if (!strcasecmp(name, "uniqueid"))
+ ast_copy_string(workspace, cdr->uniqueid, workspacelen);
+ else if (!strcasecmp(name, "userfield"))
+ ast_copy_string(workspace, cdr->userfield, workspacelen);
+ else if ((varbuf = ast_cdr_getvar_internal(cdr, name, recur)))
+ ast_copy_string(workspace, varbuf, workspacelen);
+ else
+ workspace[0] = '\0';
+
+ if (!ast_strlen_zero(workspace))
+ *ret = workspace;
+}
+
+/* readonly cdr variables */
+static const char *cdr_readonly_vars[] = { "clid", "src", "dst", "dcontext", "channel", "dstchannel",
+ "lastapp", "lastdata", "start", "answer", "end", "duration",
+ "billsec", "disposition", "amaflags", "accountcode", "uniqueid",
+ "userfield", NULL };
+/*! Set a CDR channel variable
+ \note You can't set the CDR variables that belong to the actual CDR record, like "billsec".
+*/
+int ast_cdr_setvar(struct ast_cdr *cdr, const char *name, const char *value, int recur)
+{
+ struct ast_var_t *newvariable;
+ struct varshead *headp;
+ int x;
+
+ if (!cdr) /* don't die if the cdr is null */
+ return -1;
+
+ for (x = 0; cdr_readonly_vars[x]; x++) {
+ if (!strcasecmp(name, cdr_readonly_vars[x])) {
+ ast_log(LOG_ERROR, "Attempt to set the '%s' read-only variable!.\n", name);
+ return -1;
+ }
+ }
+
+ if (!cdr) {
+ ast_log(LOG_ERROR, "Attempt to set a variable on a nonexistent CDR record.\n");
+ return -1;
+ }
+
+ for (; cdr; cdr = recur ? cdr->next : NULL) {
+ if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
+ headp = &cdr->varshead;
+ AST_LIST_TRAVERSE_SAFE_BEGIN(headp, newvariable, entries) {
+ if (!strcasecmp(ast_var_name(newvariable), name)) {
+ /* there is already such a variable, delete it */
+ AST_LIST_REMOVE_CURRENT(entries);
+ ast_var_delete(newvariable);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+
+ if (value) {
+ newvariable = ast_var_assign(name, value);
+ AST_LIST_INSERT_HEAD(headp, newvariable, entries);
+ }
+ }
+ }
+
+ return 0;
+}
+
+int ast_cdr_copy_vars(struct ast_cdr *to_cdr, struct ast_cdr *from_cdr)
+{
+ struct ast_var_t *variables, *newvariable = NULL;
+ struct varshead *headpa, *headpb;
+ const char *var, *val;
+ int x = 0;
+
+ if (!to_cdr || !from_cdr) /* don't die if one of the pointers is null */
+ return 0;
+
+ headpa = &from_cdr->varshead;
+ headpb = &to_cdr->varshead;
+
+ AST_LIST_TRAVERSE(headpa,variables,entries) {
+ if (variables &&
+ (var = ast_var_name(variables)) && (val = ast_var_value(variables)) &&
+ !ast_strlen_zero(var) && !ast_strlen_zero(val)) {
+ newvariable = ast_var_assign(var, val);
+ AST_LIST_INSERT_HEAD(headpb, newvariable, entries);
+ x++;
+ }
+ }
+
+ return x;
+}
+
+int ast_cdr_serialize_variables(struct ast_cdr *cdr, struct ast_str **buf, char delim, char sep, int recur)
+{
+ struct ast_var_t *variables;
+ const char *var, *val;
+ char *tmp;
+ char workspace[256];
+ int total = 0, x = 0, i;
+
+ (*buf)->used = 0;
+ (*buf)->str[0] = '\0';
+
+ for (; cdr; cdr = recur ? cdr->next : NULL) {
+ if (++x > 1)
+ ast_str_append(buf, 0, "\n");
+
+ AST_LIST_TRAVERSE(&cdr->varshead, variables, entries) {
+ if (variables &&
+ (var = ast_var_name(variables)) && (val = ast_var_value(variables)) &&
+ !ast_strlen_zero(var) && !ast_strlen_zero(val)) {
+ if (ast_str_append(buf, 0, "level %d: %s%c%s%c", x, var, delim, val, sep) < 0) {
+ ast_log(LOG_ERROR, "Data Buffer Size Exceeded!\n");
+ break;
+ } else
+ total++;
+ } else
+ break;
+ }
+
+ for (i = 0; cdr_readonly_vars[i]; i++) {
+ ast_cdr_getvar(cdr, cdr_readonly_vars[i], &tmp, workspace, sizeof(workspace), 0, 0);
+ if (!tmp)
+ continue;
+
+ if (ast_str_append(buf, 0, "level %d: %s%c%s%c", x, cdr_readonly_vars[i], delim, tmp, sep) < 0) {
+ ast_log(LOG_ERROR, "Data Buffer Size Exceeded!\n");
+ break;
+ } else
+ total++;
+ }
+ }
+
+ return total;
+}
+
+
+void ast_cdr_free_vars(struct ast_cdr *cdr, int recur)
+{
+
+ /* clear variables */
+ for (; cdr; cdr = recur ? cdr->next : NULL) {
+ struct ast_var_t *vardata;
+ struct varshead *headp = &cdr->varshead;
+ while ((vardata = AST_LIST_REMOVE_HEAD(headp, entries)))
+ ast_var_delete(vardata);
+ }
+}
+
+/*! \brief print a warning if cdr already posted */
+static void check_post(struct ast_cdr *cdr)
+{
+ if (!cdr)
+ return;
+ if (ast_test_flag(cdr, AST_CDR_FLAG_POSTED))
+ ast_log(LOG_NOTICE, "CDR on channel '%s' already posted\n", S_OR(cdr->channel, "<unknown>"));
+}
+
+void ast_cdr_free(struct ast_cdr *cdr)
+{
+
+ while (cdr) {
+ struct ast_cdr *next = cdr->next;
+ char *chan = S_OR(cdr->channel, "<unknown>");
+ if (!ast_test_flag(cdr, AST_CDR_FLAG_POSTED) && !ast_test_flag(cdr, AST_CDR_FLAG_POST_DISABLED))
+ ast_log(LOG_NOTICE, "CDR on channel '%s' not posted\n", chan);
+ if (ast_tvzero(cdr->end))
+ ast_log(LOG_NOTICE, "CDR on channel '%s' lacks end\n", chan);
+ if (ast_tvzero(cdr->start))
+ ast_log(LOG_NOTICE, "CDR on channel '%s' lacks start\n", chan);
+
+ ast_cdr_free_vars(cdr, 0);
+ ast_free(cdr);
+ cdr = next;
+ }
+}
+
+/*! \brief the same as a cdr_free call, only with no checks; just get rid of it */
+void ast_cdr_discard(struct ast_cdr *cdr)
+{
+ while (cdr) {
+ struct ast_cdr *next = cdr->next;
+
+ ast_cdr_free_vars(cdr, 0);
+ ast_free(cdr);
+ cdr = next;
+ }
+}
+
+struct ast_cdr *ast_cdr_alloc(void)
+{
+ struct ast_cdr *x;
+ x = ast_calloc(1, sizeof(*x));
+ if (!x)
+ ast_log(LOG_ERROR,"Allocation Failure for a CDR!\n");
+ return x;
+}
+
+static void cdr_merge_vars(struct ast_cdr *to, struct ast_cdr *from)
+{
+ struct ast_var_t *variablesfrom,*variablesto;
+ struct varshead *headpfrom = &to->varshead;
+ struct varshead *headpto = &from->varshead;
+ AST_LIST_TRAVERSE_SAFE_BEGIN(headpfrom, variablesfrom, entries) {
+ /* for every var in from, stick it in to */
+ const char *fromvarname, *fromvarval;
+ const char *tovarname = NULL, *tovarval = NULL;
+ fromvarname = ast_var_name(variablesfrom);
+ fromvarval = ast_var_value(variablesfrom);
+ tovarname = 0;
+
+ /* now, quick see if that var is in the 'to' cdr already */
+ AST_LIST_TRAVERSE(headpto, variablesto, entries) {
+
+ /* now, quick see if that var is in the 'to' cdr already */
+ if ( strcasecmp(fromvarname, ast_var_name(variablesto)) == 0 ) {
+ tovarname = ast_var_name(variablesto);
+ tovarval = ast_var_value(variablesto);
+ break;
+ }
+ }
+ if (tovarname && strcasecmp(fromvarval,tovarval) != 0) { /* this message here to see how irritating the userbase finds it */
+ ast_log(LOG_NOTICE, "Merging CDR's: variable %s value %s dropped in favor of value %s\n", tovarname, fromvarval, tovarval);
+ continue;
+ } else if (tovarname && strcasecmp(fromvarval,tovarval) == 0) /* if they are the same, the job is done */
+ continue;
+
+ /* rip this var out of the from cdr, and stick it in the to cdr */
+ AST_LIST_MOVE_CURRENT(headpto, entries);
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+}
+
+void ast_cdr_merge(struct ast_cdr *to, struct ast_cdr *from)
+{
+ struct ast_cdr *zcdr;
+ struct ast_cdr *lto = NULL;
+ struct ast_cdr *lfrom = NULL;
+ int discard_from = 0;
+
+ if (!to || !from)
+ return;
+
+ /* don't merge into locked CDR's -- it's bad business */
+ if (ast_test_flag(to, AST_CDR_FLAG_LOCKED)) {
+ zcdr = to; /* safety valve? */
+ while (to->next) {
+ lto = to;
+ to = to->next;
+ }
+
+ if (ast_test_flag(to, AST_CDR_FLAG_LOCKED)) {
+ ast_log(LOG_WARNING, "Merging into locked CDR... no choice.");
+ to = zcdr; /* safety-- if all there are is locked CDR's, then.... ?? */
+ lto = NULL;
+ }
+ }
+
+ if (ast_test_flag(from, AST_CDR_FLAG_LOCKED)) {
+ struct ast_cdr *llfrom = NULL;
+ discard_from = 1;
+ if (lto) {
+ /* insert the from stuff after lto */
+ lto->next = from;
+ lfrom = from;
+ while (lfrom && lfrom->next) {
+ if (!lfrom->next->next)
+ llfrom = lfrom;
+ lfrom = lfrom->next;
+ }
+ /* rip off the last entry and put a copy of the to at the end */
+ llfrom->next = to;
+ from = lfrom;
+ } else {
+ /* save copy of the current *to cdr */
+ struct ast_cdr tcdr;
+ memcpy(&tcdr, to, sizeof(tcdr));
+ /* copy in the locked from cdr */
+ memcpy(to, from, sizeof(*to));
+ lfrom = from;
+ while (lfrom && lfrom->next) {
+ if (!lfrom->next->next)
+ llfrom = lfrom;
+ lfrom = lfrom->next;
+ }
+ from->next = NULL;
+ /* rip off the last entry and put a copy of the to at the end */
+ if (llfrom == from)
+ to = to->next = ast_cdr_dup(&tcdr);
+ else
+ to = llfrom->next = ast_cdr_dup(&tcdr);
+ from = lfrom;
+ }
+ }
+
+ if (!ast_tvzero(from->start)) {
+ if (!ast_tvzero(to->start)) {
+ if (ast_tvcmp(to->start, from->start) > 0 ) {
+ to->start = from->start; /* use the earliest time */
+ from->start = ast_tv(0,0); /* we actively "steal" these values */
+ }
+ /* else nothing to do */
+ } else {
+ to->start = from->start;
+ from->start = ast_tv(0,0); /* we actively "steal" these values */
+ }
+ }
+ if (!ast_tvzero(from->answer)) {
+ if (!ast_tvzero(to->answer)) {
+ if (ast_tvcmp(to->answer, from->answer) > 0 ) {
+ to->answer = from->answer; /* use the earliest time */
+ from->answer = ast_tv(0,0); /* we actively "steal" these values */
+ }
+ /* we got the earliest answer time, so we'll settle for that? */
+ } else {
+ to->answer = from->answer;
+ from->answer = ast_tv(0,0); /* we actively "steal" these values */
+ }
+ }
+ if (!ast_tvzero(from->end)) {
+ if (!ast_tvzero(to->end)) {
+ if (ast_tvcmp(to->end, from->end) < 0 ) {
+ to->end = from->end; /* use the latest time */
+ from->end = ast_tv(0,0); /* we actively "steal" these values */
+ to->duration = to->end.tv_sec - to->start.tv_sec; /* don't forget to update the duration, billsec, when we set end */
+ to->billsec = ast_tvzero(to->answer) ? 0 : to->end.tv_sec - to->answer.tv_sec;
+ }
+ /* else, nothing to do */
+ } else {
+ to->end = from->end;
+ from->end = ast_tv(0,0); /* we actively "steal" these values */
+ to->duration = to->end.tv_sec - to->start.tv_sec;
+ to->billsec = ast_tvzero(to->answer) ? 0 : to->end.tv_sec - to->answer.tv_sec;
+ }
+ }
+ if (to->disposition < from->disposition) {
+ to->disposition = from->disposition;
+ from->disposition = AST_CDR_NOANSWER;
+ }
+ if (ast_strlen_zero(to->lastapp) && !ast_strlen_zero(from->lastapp)) {
+ ast_copy_string(to->lastapp, from->lastapp, sizeof(to->lastapp));
+ from->lastapp[0] = 0; /* theft */
+ }
+ if (ast_strlen_zero(to->lastdata) && !ast_strlen_zero(from->lastdata)) {
+ ast_copy_string(to->lastdata, from->lastdata, sizeof(to->lastdata));
+ from->lastdata[0] = 0; /* theft */
+ }
+ if (ast_strlen_zero(to->dcontext) && !ast_strlen_zero(from->dcontext)) {
+ ast_copy_string(to->dcontext, from->dcontext, sizeof(to->dcontext));
+ from->dcontext[0] = 0; /* theft */
+ }
+ if (ast_strlen_zero(to->dstchannel) && !ast_strlen_zero(from->dstchannel)) {
+ ast_copy_string(to->dstchannel, from->dstchannel, sizeof(to->dstchannel));
+ from->dstchannel[0] = 0; /* theft */
+ }
+ if (!ast_strlen_zero(from->channel) && (ast_strlen_zero(to->channel) || !strncasecmp(from->channel, "Agent/", 6))) {
+ ast_copy_string(to->channel, from->channel, sizeof(to->channel));
+ from->channel[0] = 0; /* theft */
+ }
+ if (ast_strlen_zero(to->src) && !ast_strlen_zero(from->src)) {
+ ast_copy_string(to->src, from->src, sizeof(to->src));
+ from->src[0] = 0; /* theft */
+ }
+ if (ast_strlen_zero(to->clid) && !ast_strlen_zero(from->clid)) {
+ ast_copy_string(to->clid, from->clid, sizeof(to->clid));
+ from->clid[0] = 0; /* theft */
+ }
+ if (ast_strlen_zero(to->dst) && !ast_strlen_zero(from->dst)) {
+ ast_copy_string(to->dst, from->dst, sizeof(to->dst));
+ from->dst[0] = 0; /* theft */
+ }
+ if (!to->amaflags)
+ to->amaflags = AST_CDR_DOCUMENTATION;
+ if (!from->amaflags)
+ from->amaflags = AST_CDR_DOCUMENTATION; /* make sure both amaflags are set to something (DOC is default) */
+ if (ast_test_flag(from, AST_CDR_FLAG_LOCKED) || (to->amaflags == AST_CDR_DOCUMENTATION && from->amaflags != AST_CDR_DOCUMENTATION)) {
+ to->amaflags = from->amaflags;
+ }
+ if (ast_test_flag(from, AST_CDR_FLAG_LOCKED) || (ast_strlen_zero(to->accountcode) && !ast_strlen_zero(from->accountcode))) {
+ ast_copy_string(to->accountcode, from->accountcode, sizeof(to->accountcode));
+ }
+ if (ast_test_flag(from, AST_CDR_FLAG_LOCKED) || (ast_strlen_zero(to->userfield) && !ast_strlen_zero(from->userfield))) {
+ ast_copy_string(to->userfield, from->userfield, sizeof(to->userfield));
+ }
+ /* flags, varsead, ? */
+ cdr_merge_vars(from, to);
+
+ if (ast_test_flag(from, AST_CDR_FLAG_KEEP_VARS))
+ ast_set_flag(to, AST_CDR_FLAG_KEEP_VARS);
+ if (ast_test_flag(from, AST_CDR_FLAG_POSTED))
+ ast_set_flag(to, AST_CDR_FLAG_POSTED);
+ if (ast_test_flag(from, AST_CDR_FLAG_LOCKED))
+ ast_set_flag(to, AST_CDR_FLAG_LOCKED);
+ if (ast_test_flag(from, AST_CDR_FLAG_CHILD))
+ ast_set_flag(to, AST_CDR_FLAG_CHILD);
+ if (ast_test_flag(from, AST_CDR_FLAG_POST_DISABLED))
+ ast_set_flag(to, AST_CDR_FLAG_POST_DISABLED);
+
+ /* last, but not least, we need to merge any forked CDRs to the 'to' cdr */
+ while (from->next) {
+ /* just rip 'em off the 'from' and insert them on the 'to' */
+ zcdr = from->next;
+ from->next = zcdr->next;
+ zcdr->next = NULL;
+ /* zcdr is now ripped from the current list; */
+ ast_cdr_append(to, zcdr);
+ }
+ if (discard_from)
+ ast_cdr_discard(from);
+}
+
+void ast_cdr_start(struct ast_cdr *cdr)
+{
+ char *chan;
+
+ for (; cdr; cdr = cdr->next) {
+ if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
+ chan = S_OR(cdr->channel, "<unknown>");
+ check_post(cdr);
+ cdr->start = ast_tvnow();
+ }
+ }
+}
+
+void ast_cdr_answer(struct ast_cdr *cdr)
+{
+
+ for (; cdr; cdr = cdr->next) {
+ if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
+ check_post(cdr);
+ if (cdr->disposition < AST_CDR_ANSWERED)
+ cdr->disposition = AST_CDR_ANSWERED;
+ if (ast_tvzero(cdr->answer))
+ cdr->answer = ast_tvnow();
+ }
+ }
+}
+
+void ast_cdr_busy(struct ast_cdr *cdr)
+{
+
+ for (; cdr; cdr = cdr->next) {
+ if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
+ check_post(cdr);
+ if (cdr->disposition < AST_CDR_BUSY)
+ cdr->disposition = AST_CDR_BUSY;
+ }
+ }
+}
+
+void ast_cdr_failed(struct ast_cdr *cdr)
+{
+ for (; cdr; cdr = cdr->next) {
+ if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
+ check_post(cdr);
+ if (cdr->disposition < AST_CDR_FAILED)
+ cdr->disposition = AST_CDR_FAILED;
+ }
+ }
+}
+
+void ast_cdr_noanswer(struct ast_cdr *cdr)
+{
+ char *chan;
+
+ while (cdr) {
+ if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
+ chan = !ast_strlen_zero(cdr->channel) ? cdr->channel : "<unknown>";
+ if (ast_test_flag(cdr, AST_CDR_FLAG_POSTED))
+ ast_log(LOG_WARNING, "CDR on channel '%s' already posted\n", chan);
+ if (cdr->disposition < AST_CDR_NOANSWER)
+ cdr->disposition = AST_CDR_NOANSWER;
+ }
+ cdr = cdr->next;
+ }
+}
+
+/* everywhere ast_cdr_disposition is called, it will call ast_cdr_failed()
+ if ast_cdr_disposition returns a non-zero value */
+
+int ast_cdr_disposition(struct ast_cdr *cdr, int cause)
+{
+ int res = 0;
+
+ for (; cdr; cdr = cdr->next) {
+ switch (cause) { /* handle all the non failure, busy cases, return 0 not to set disposition,
+ return -1 to set disposition to FAILED */
+ case AST_CAUSE_BUSY:
+ ast_cdr_busy(cdr);
+ break;
+ case AST_CAUSE_NORMAL:
+ break;
+ default:
+ res = -1;
+ }
+ }
+ return res;
+}
+
+void ast_cdr_setdestchan(struct ast_cdr *cdr, const char *chann)
+{
+ for (; cdr; cdr = cdr->next) {
+ if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
+ check_post(cdr);
+ ast_copy_string(cdr->dstchannel, chann, sizeof(cdr->dstchannel));
+ }
+ }
+}
+
+void ast_cdr_setapp(struct ast_cdr *cdr, char *app, char *data)
+{
+
+ for (; cdr; cdr = cdr->next) {
+ if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
+ check_post(cdr);
+ if (!app)
+ app = "";
+ ast_copy_string(cdr->lastapp, app, sizeof(cdr->lastapp));
+ if (!data)
+ data = "";
+ ast_copy_string(cdr->lastdata, data, sizeof(cdr->lastdata));
+ }
+ }
+}
+
+/* set cid info for one record */
+static void set_one_cid(struct ast_cdr *cdr, struct ast_channel *c)
+{
+ /* Grab source from ANI or normal Caller*ID */
+ const char *num = S_OR(c->cid.cid_ani, c->cid.cid_num);
+ if (!cdr)
+ return;
+ if (!ast_strlen_zero(c->cid.cid_name)) {
+ if (!ast_strlen_zero(num)) /* both name and number */
+ snprintf(cdr->clid, sizeof(cdr->clid), "\"%s\" <%s>", c->cid.cid_name, num);
+ else /* only name */
+ ast_copy_string(cdr->clid, c->cid.cid_name, sizeof(cdr->clid));
+ } else if (!ast_strlen_zero(num)) { /* only number */
+ ast_copy_string(cdr->clid, num, sizeof(cdr->clid));
+ } else { /* nothing known */
+ cdr->clid[0] = '\0';
+ }
+ ast_copy_string(cdr->src, S_OR(num, ""), sizeof(cdr->src));
+
+}
+int ast_cdr_setcid(struct ast_cdr *cdr, struct ast_channel *c)
+{
+ for (; cdr; cdr = cdr->next) {
+ if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
+ set_one_cid(cdr, c);
+ }
+ return 0;
+}
+
+int ast_cdr_init(struct ast_cdr *cdr, struct ast_channel *c)
+{
+ char *chan;
+
+ for ( ; cdr ; cdr = cdr->next) {
+ if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
+ chan = S_OR(cdr->channel, "<unknown>");
+ ast_copy_string(cdr->channel, c->name, sizeof(cdr->channel));
+ set_one_cid(cdr, c);
+
+ cdr->disposition = (c->_state == AST_STATE_UP) ? AST_CDR_ANSWERED : AST_CDR_NULL;
+ cdr->amaflags = c->amaflags ? c->amaflags : ast_default_amaflags;
+ ast_copy_string(cdr->accountcode, c->accountcode, sizeof(cdr->accountcode));
+ /* Destination information */
+ ast_copy_string(cdr->dst, S_OR(c->macroexten,c->exten), sizeof(cdr->dst));
+ ast_copy_string(cdr->dcontext, S_OR(c->macrocontext,c->context), sizeof(cdr->dcontext));
+ /* Unique call identifier */
+ ast_copy_string(cdr->uniqueid, c->uniqueid, sizeof(cdr->uniqueid));
+ }
+ }
+ return 0;
+}
+
+void ast_cdr_end(struct ast_cdr *cdr)
+{
+ for ( ; cdr ; cdr = cdr->next) {
+ if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
+ check_post(cdr);
+ if (ast_tvzero(cdr->end))
+ cdr->end = ast_tvnow();
+ if (ast_tvzero(cdr->start)) {
+ ast_log(LOG_WARNING, "CDR on channel '%s' has not started\n", S_OR(cdr->channel, "<unknown>"));
+ cdr->disposition = AST_CDR_FAILED;
+ } else
+ cdr->duration = cdr->end.tv_sec - cdr->start.tv_sec;
+ cdr->billsec = ast_tvzero(cdr->answer) ? 0 : cdr->end.tv_sec - cdr->answer.tv_sec;
+ }
+ }
+}
+
+char *ast_cdr_disp2str(int disposition)
+{
+ switch (disposition) {
+ case AST_CDR_NULL:
+ return "NO ANSWER"; /* by default, for backward compatibility */
+ case AST_CDR_NOANSWER:
+ return "NO ANSWER";
+ case AST_CDR_FAILED:
+ return "FAILED";
+ case AST_CDR_BUSY:
+ return "BUSY";
+ case AST_CDR_ANSWERED:
+ return "ANSWERED";
+ }
+ return "UNKNOWN";
+}
+
+/*! Converts AMA flag to printable string */
+char *ast_cdr_flags2str(int flag)
+{
+ switch (flag) {
+ case AST_CDR_OMIT:
+ return "OMIT";
+ case AST_CDR_BILLING:
+ return "BILLING";
+ case AST_CDR_DOCUMENTATION:
+ return "DOCUMENTATION";
+ }
+ return "Unknown";
+}
+
+int ast_cdr_setaccount(struct ast_channel *chan, const char *account)
+{
+ struct ast_cdr *cdr = chan->cdr;
+ char buf[BUFSIZ/2] = "";
+ if (!ast_strlen_zero(chan->accountcode))
+ ast_copy_string(buf, chan->accountcode, sizeof(buf));
+
+ ast_string_field_set(chan, accountcode, account);
+ for ( ; cdr ; cdr = cdr->next) {
+ if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
+ ast_copy_string(cdr->accountcode, chan->accountcode, sizeof(cdr->accountcode));
+ }
+ }
+
+ /* Signal change of account code to manager */
+ manager_event(EVENT_FLAG_CALL, "NewAccountCode", "Channel: %s\r\nUniqueid: %s\r\nAccountCode: %s\r\nOldAccountCode: %s\r\n", chan->name, chan->uniqueid, chan->accountcode, buf);
+ return 0;
+}
+
+int ast_cdr_setamaflags(struct ast_channel *chan, const char *flag)
+{
+ struct ast_cdr *cdr;
+ int newflag = ast_cdr_amaflags2int(flag);
+ if (newflag) {
+ for (cdr = chan->cdr; cdr; cdr = cdr->next) {
+ if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
+ cdr->amaflags = newflag;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int ast_cdr_setuserfield(struct ast_channel *chan, const char *userfield)
+{
+ struct ast_cdr *cdr = chan->cdr;
+
+ for ( ; cdr ; cdr = cdr->next) {
+ if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
+ ast_copy_string(cdr->userfield, userfield, sizeof(cdr->userfield));
+ }
+
+ return 0;
+}
+
+int ast_cdr_appenduserfield(struct ast_channel *chan, const char *userfield)
+{
+ struct ast_cdr *cdr = chan->cdr;
+
+ for ( ; cdr ; cdr = cdr->next) {
+ int len = strlen(cdr->userfield);
+
+ if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED))
+ ast_copy_string(cdr->userfield + len, userfield, sizeof(cdr->userfield) - len);
+ }
+
+ return 0;
+}
+
+int ast_cdr_update(struct ast_channel *c)
+{
+ struct ast_cdr *cdr = c->cdr;
+
+ for ( ; cdr ; cdr = cdr->next) {
+ if (!ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
+ set_one_cid(cdr, c);
+
+ /* Copy account code et-al */
+ ast_copy_string(cdr->accountcode, c->accountcode, sizeof(cdr->accountcode));
+
+ /* Destination information */ /* XXX privilege macro* ? */
+ ast_copy_string(cdr->dst, S_OR(c->macroexten, c->exten), sizeof(cdr->dst));
+ ast_copy_string(cdr->dcontext, S_OR(c->macrocontext, c->context), sizeof(cdr->dcontext));
+ }
+ }
+
+ return 0;
+}
+
+int ast_cdr_amaflags2int(const char *flag)
+{
+ if (!strcasecmp(flag, "default"))
+ return 0;
+ if (!strcasecmp(flag, "omit"))
+ return AST_CDR_OMIT;
+ if (!strcasecmp(flag, "billing"))
+ return AST_CDR_BILLING;
+ if (!strcasecmp(flag, "documentation"))
+ return AST_CDR_DOCUMENTATION;
+ return -1;
+}
+
+static void post_cdr(struct ast_cdr *cdr)
+{
+ char *chan;
+ struct ast_cdr_beitem *i;
+
+ for ( ; cdr ; cdr = cdr->next) {
+ chan = S_OR(cdr->channel, "<unknown>");
+ check_post(cdr);
+ if (ast_tvzero(cdr->end))
+ ast_log(LOG_WARNING, "CDR on channel '%s' lacks end\n", chan);
+ if (ast_tvzero(cdr->start))
+ ast_log(LOG_WARNING, "CDR on channel '%s' lacks start\n", chan);
+ ast_set_flag(cdr, AST_CDR_FLAG_POSTED);
+ if (ast_test_flag(cdr, AST_CDR_FLAG_POST_DISABLED))
+ continue;
+ AST_RWLIST_RDLOCK(&be_list);
+ AST_RWLIST_TRAVERSE(&be_list, i, list) {
+ i->be(cdr);
+ }
+ AST_RWLIST_UNLOCK(&be_list);
+ }
+}
+
+void ast_cdr_reset(struct ast_cdr *cdr, struct ast_flags *_flags)
+{
+ struct ast_cdr *dup;
+ struct ast_flags flags = { 0 };
+
+ if (_flags)
+ ast_copy_flags(&flags, _flags, AST_FLAGS_ALL);
+
+ for ( ; cdr ; cdr = cdr->next) {
+ /* Detach if post is requested */
+ if (ast_test_flag(&flags, AST_CDR_FLAG_LOCKED) || !ast_test_flag(cdr, AST_CDR_FLAG_LOCKED)) {
+ if (ast_test_flag(&flags, AST_CDR_FLAG_POSTED)) {
+ ast_cdr_end(cdr);
+ if ((dup = ast_cdr_dup(cdr))) {
+ ast_cdr_detach(dup);
+ }
+ ast_set_flag(cdr, AST_CDR_FLAG_POSTED);
+ }
+
+ /* clear variables */
+ if (!ast_test_flag(&flags, AST_CDR_FLAG_KEEP_VARS)) {
+ ast_cdr_free_vars(cdr, 0);
+ }
+
+ /* Reset to initial state */
+ ast_clear_flag(cdr, AST_FLAGS_ALL);
+ memset(&cdr->start, 0, sizeof(cdr->start));
+ memset(&cdr->end, 0, sizeof(cdr->end));
+ memset(&cdr->answer, 0, sizeof(cdr->answer));
+ cdr->billsec = 0;
+ cdr->duration = 0;
+ ast_cdr_start(cdr);
+ cdr->disposition = AST_CDR_NULL;
+ }
+ }
+}
+
+struct ast_cdr *ast_cdr_append(struct ast_cdr *cdr, struct ast_cdr *newcdr)
+{
+ struct ast_cdr *ret;
+
+ if (cdr) {
+ ret = cdr;
+
+ while (cdr->next)
+ cdr = cdr->next;
+ cdr->next = newcdr;
+ } else {
+ ret = newcdr;
+ }
+
+ return ret;
+}
+
+/*! \note Don't call without cdr_batch_lock */
+static void reset_batch(void)
+{
+ batch->size = 0;
+ batch->head = NULL;
+ batch->tail = NULL;
+}
+
+/*! \note Don't call without cdr_batch_lock */
+static int init_batch(void)
+{
+ /* This is the single meta-batch used to keep track of all CDRs during the entire life of the program */
+ if (!(batch = ast_malloc(sizeof(*batch))))
+ return -1;
+
+ reset_batch();
+
+ return 0;
+}
+
+static void *do_batch_backend_process(void *data)
+{
+ struct ast_cdr_batch_item *processeditem;
+ struct ast_cdr_batch_item *batchitem = data;
+
+ /* Push each CDR into storage mechanism(s) and free all the memory */
+ while (batchitem) {
+ post_cdr(batchitem->cdr);
+ ast_cdr_free(batchitem->cdr);
+ processeditem = batchitem;
+ batchitem = batchitem->next;
+ ast_free(processeditem);
+ }
+
+ return NULL;
+}
+
+void ast_cdr_submit_batch(int shutdown)
+{
+ struct ast_cdr_batch_item *oldbatchitems = NULL;
+ pthread_t batch_post_thread = AST_PTHREADT_NULL;
+
+ /* if there's no batch, or no CDRs in the batch, then there's nothing to do */
+ if (!batch || !batch->head)
+ return;
+
+ /* move the old CDRs aside, and prepare a new CDR batch */
+ ast_mutex_lock(&cdr_batch_lock);
+ oldbatchitems = batch->head;
+ reset_batch();
+ ast_mutex_unlock(&cdr_batch_lock);
+
+ /* if configured, spawn a new thread to post these CDRs,
+ also try to save as much as possible if we are shutting down safely */
+ if (batchscheduleronly || shutdown) {
+ ast_debug(1, "CDR single-threaded batch processing begins now\n");
+ do_batch_backend_process(oldbatchitems);
+ } else {
+ if (ast_pthread_create_detached_background(&batch_post_thread, NULL, do_batch_backend_process, oldbatchitems)) {
+ ast_log(LOG_WARNING, "CDR processing thread could not detach, now trying in this thread\n");
+ do_batch_backend_process(oldbatchitems);
+ } else {
+ ast_debug(1, "CDR multi-threaded batch processing begins now\n");
+ }
+ }
+}
+
+static int submit_scheduled_batch(const void *data)
+{
+ ast_cdr_submit_batch(0);
+ /* manually reschedule from this point in time */
+ cdr_sched = ast_sched_add(sched, batchtime * 1000, submit_scheduled_batch, NULL);
+ /* returning zero so the scheduler does not automatically reschedule */
+ return 0;
+}
+
+static void submit_unscheduled_batch(void)
+{
+ /* this is okay since we are not being called from within the scheduler */
+ if (cdr_sched > -1)
+ ast_sched_del(sched, cdr_sched);
+ /* schedule the submission to occur ASAP (1 ms) */
+ cdr_sched = ast_sched_add(sched, 1, submit_scheduled_batch, NULL);
+ /* signal the do_cdr thread to wakeup early and do some work (that lazy thread ;) */
+ ast_mutex_lock(&cdr_pending_lock);
+ ast_cond_signal(&cdr_pending_cond);
+ ast_mutex_unlock(&cdr_pending_lock);
+}
+
+void ast_cdr_detach(struct ast_cdr *cdr)
+{
+ struct ast_cdr_batch_item *newtail;
+ int curr;
+
+ if (!cdr)
+ return;
+
+ /* maybe they disabled CDR stuff completely, so just drop it */
+ if (!enabled) {
+ ast_debug(1, "Dropping CDR !\n");
+ ast_set_flag(cdr, AST_CDR_FLAG_POST_DISABLED);
+ ast_cdr_free(cdr);
+ return;
+ }
+
+ /* post stuff immediately if we are not in batch mode, this is legacy behaviour */
+ if (!batchmode) {
+ post_cdr(cdr);
+ ast_cdr_free(cdr);
+ return;
+ }
+
+ /* otherwise, each CDR gets put into a batch list (at the end) */
+ ast_debug(1, "CDR detaching from this thread\n");
+
+ /* we'll need a new tail for every CDR */
+ if (!(newtail = ast_calloc(1, sizeof(*newtail)))) {
+ post_cdr(cdr);
+ ast_cdr_free(cdr);
+ return;
+ }
+
+ /* don't traverse a whole list (just keep track of the tail) */
+ ast_mutex_lock(&cdr_batch_lock);
+ if (!batch)
+ init_batch();
+ if (!batch->head) {
+ /* new batch is empty, so point the head at the new tail */
+ batch->head = newtail;
+ } else {
+ /* already got a batch with something in it, so just append a new tail */
+ batch->tail->next = newtail;
+ }
+ newtail->cdr = cdr;
+ batch->tail = newtail;
+ curr = batch->size++;
+ ast_mutex_unlock(&cdr_batch_lock);
+
+ /* if we have enough stuff to post, then do it */
+ if (curr >= (batchsize - 1))
+ submit_unscheduled_batch();
+}
+
+static void *do_cdr(void *data)
+{
+ struct timespec timeout;
+ int schedms;
+ int numevents = 0;
+
+ for (;;) {
+ struct timeval now;
+ schedms = ast_sched_wait(sched);
+ /* this shouldn't happen, but provide a 1 second default just in case */
+ if (schedms <= 0)
+ schedms = 1000;
+ now = ast_tvadd(ast_tvnow(), ast_samp2tv(schedms, 1000));
+ timeout.tv_sec = now.tv_sec;
+ timeout.tv_nsec = now.tv_usec * 1000;
+ /* prevent stuff from clobbering cdr_pending_cond, then wait on signals sent to it until the timeout expires */
+ ast_mutex_lock(&cdr_pending_lock);
+ ast_cond_timedwait(&cdr_pending_cond, &cdr_pending_lock, &timeout);
+ numevents = ast_sched_runq(sched);
+ ast_mutex_unlock(&cdr_pending_lock);
+ ast_debug(2, "Processed %d scheduled CDR batches from the run queue\n", numevents);
+ }
+
+ return NULL;
+}
+
+static char *handle_cli_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_cdr_beitem *beitem=NULL;
+ int cnt=0;
+ long nextbatchtime=0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "cdr status";
+ e->usage =
+ "Usage: cdr status\n"
+ " Displays the Call Detail Record engine system status.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc > 2)
+ return CLI_SHOWUSAGE;
+
+ ast_cli(a->fd, "CDR logging: %s\n", enabled ? "enabled" : "disabled");
+ ast_cli(a->fd, "CDR mode: %s\n", batchmode ? "batch" : "simple");
+ if (enabled) {
+ ast_cli(a->fd, "CDR output unanswered calls: %s\n", unanswered ? "yes" : "no");
+ if (batchmode) {
+ if (batch)
+ cnt = batch->size;
+ if (cdr_sched > -1)
+ nextbatchtime = ast_sched_when(sched, cdr_sched);
+ ast_cli(a->fd, "CDR safe shut down: %s\n", batchsafeshutdown ? "enabled" : "disabled");
+ ast_cli(a->fd, "CDR batch threading model: %s\n", batchscheduleronly ? "scheduler only" : "scheduler plus separate threads");
+ ast_cli(a->fd, "CDR current batch size: %d record%s\n", cnt, ESS(cnt));
+ ast_cli(a->fd, "CDR maximum batch size: %d record%s\n", batchsize, ESS(batchsize));
+ ast_cli(a->fd, "CDR maximum batch time: %d second%s\n", batchtime, ESS(batchtime));
+ ast_cli(a->fd, "CDR next scheduled batch processing time: %ld second%s\n", nextbatchtime, ESS(nextbatchtime));
+ }
+ AST_RWLIST_RDLOCK(&be_list);
+ AST_RWLIST_TRAVERSE(&be_list, beitem, list) {
+ ast_cli(a->fd, "CDR registered backend: %s\n", beitem->name);
+ }
+ AST_RWLIST_UNLOCK(&be_list);
+ }
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_submit(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "cdr submit";
+ e->usage =
+ "Usage: cdr submit\n"
+ " Posts all pending batched CDR data to the configured CDR backend engine modules.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (a->argc > 2)
+ return CLI_SHOWUSAGE;
+
+ submit_unscheduled_batch();
+ ast_cli(a->fd, "Submitted CDRs to backend engines for processing. This may take a while.\n");
+
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli_submit = AST_CLI_DEFINE(handle_cli_submit, "Posts all pending batched CDR data");
+static struct ast_cli_entry cli_status = AST_CLI_DEFINE(handle_cli_status, "Display the CDR status");
+
+static int do_reload(int reload)
+{
+ struct ast_config *config;
+ const char *enabled_value;
+ const char *unanswered_value;
+ const char *batched_value;
+ const char *scheduleronly_value;
+ const char *batchsafeshutdown_value;
+ const char *size_value;
+ const char *time_value;
+ const char *end_before_h_value;
+ int cfg_size;
+ int cfg_time;
+ int was_enabled;
+ int was_batchmode;
+ int res=0;
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+
+ if ((config = ast_config_load("cdr.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
+ return 0;
+
+ ast_mutex_lock(&cdr_batch_lock);
+
+ batchsize = BATCH_SIZE_DEFAULT;
+ batchtime = BATCH_TIME_DEFAULT;
+ batchscheduleronly = BATCH_SCHEDULER_ONLY_DEFAULT;
+ batchsafeshutdown = BATCH_SAFE_SHUTDOWN_DEFAULT;
+ was_enabled = enabled;
+ was_batchmode = batchmode;
+ enabled = 1;
+ batchmode = 0;
+
+ /* don't run the next scheduled CDR posting while reloading */
+ if (cdr_sched > -1)
+ ast_sched_del(sched, cdr_sched);
+
+ if (config) {
+ if ((enabled_value = ast_variable_retrieve(config, "general", "enable"))) {
+ enabled = ast_true(enabled_value);
+ }
+ if ((unanswered_value = ast_variable_retrieve(config, "general", "unanswered"))) {
+ unanswered = ast_true(unanswered_value);
+ }
+ if ((batched_value = ast_variable_retrieve(config, "general", "batch"))) {
+ batchmode = ast_true(batched_value);
+ }
+ if ((scheduleronly_value = ast_variable_retrieve(config, "general", "scheduleronly"))) {
+ batchscheduleronly = ast_true(scheduleronly_value);
+ }
+ if ((batchsafeshutdown_value = ast_variable_retrieve(config, "general", "safeshutdown"))) {
+ batchsafeshutdown = ast_true(batchsafeshutdown_value);
+ }
+ if ((size_value = ast_variable_retrieve(config, "general", "size"))) {
+ if (sscanf(size_value, "%d", &cfg_size) < 1)
+ ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", size_value);
+ else if (size_value < 0)
+ ast_log(LOG_WARNING, "Invalid maximum batch size '%d' specified, using default\n", cfg_size);
+ else
+ batchsize = cfg_size;
+ }
+ if ((time_value = ast_variable_retrieve(config, "general", "time"))) {
+ if (sscanf(time_value, "%d", &cfg_time) < 1)
+ ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", time_value);
+ else if (time_value < 0)
+ ast_log(LOG_WARNING, "Invalid maximum batch time '%d' specified, using default\n", cfg_time);
+ else
+ batchtime = cfg_time;
+ }
+ if ((end_before_h_value = ast_variable_retrieve(config, "general", "endbeforehexten")))
+ ast_set2_flag(&ast_options, ast_true(end_before_h_value), AST_OPT_FLAG_END_CDR_BEFORE_H_EXTEN);
+ }
+
+ if (enabled && !batchmode) {
+ ast_log(LOG_NOTICE, "CDR simple logging enabled.\n");
+ } else if (enabled && batchmode) {
+ cdr_sched = ast_sched_add(sched, batchtime * 1000, submit_scheduled_batch, NULL);
+ ast_log(LOG_NOTICE, "CDR batch mode logging enabled, first of either size %d or time %d seconds.\n", batchsize, batchtime);
+ } else {
+ ast_log(LOG_NOTICE, "CDR logging disabled, data will be lost.\n");
+ }
+
+ /* if this reload enabled the CDR batch mode, create the background thread
+ if it does not exist */
+ if (enabled && batchmode && (!was_enabled || !was_batchmode) && (cdr_thread == AST_PTHREADT_NULL)) {
+ ast_cond_init(&cdr_pending_cond, NULL);
+ if (ast_pthread_create_background(&cdr_thread, NULL, do_cdr, NULL) < 0) {
+ ast_log(LOG_ERROR, "Unable to start CDR thread.\n");
+ ast_sched_del(sched, cdr_sched);
+ } else {
+ ast_cli_register(&cli_submit);
+ ast_register_atexit(ast_cdr_engine_term);
+ res = 0;
+ }
+ /* if this reload disabled the CDR and/or batch mode and there is a background thread,
+ kill it */
+ } else if (((!enabled && was_enabled) || (!batchmode && was_batchmode)) && (cdr_thread != AST_PTHREADT_NULL)) {
+ /* wake up the thread so it will exit */
+ pthread_cancel(cdr_thread);
+ pthread_kill(cdr_thread, SIGURG);
+ pthread_join(cdr_thread, NULL);
+ cdr_thread = AST_PTHREADT_NULL;
+ ast_cond_destroy(&cdr_pending_cond);
+ ast_cli_unregister(&cli_submit);
+ ast_unregister_atexit(ast_cdr_engine_term);
+ res = 0;
+ /* if leaving batch mode, then post the CDRs in the batch,
+ and don't reschedule, since we are stopping CDR logging */
+ if (!batchmode && was_batchmode) {
+ ast_cdr_engine_term();
+ }
+ } else {
+ res = 0;
+ }
+
+ ast_mutex_unlock(&cdr_batch_lock);
+ ast_config_destroy(config);
+ manager_event(EVENT_FLAG_SYSTEM, "Reload", "Module: CDR\r\nMessage: CDR subsystem reload requested\r\n");
+
+ return res;
+}
+
+int ast_cdr_engine_init(void)
+{
+ int res;
+
+ sched = sched_context_create();
+ if (!sched) {
+ ast_log(LOG_ERROR, "Unable to create schedule context.\n");
+ return -1;
+ }
+
+ ast_cli_register(&cli_status);
+
+ res = do_reload(0);
+ if (res) {
+ ast_mutex_lock(&cdr_batch_lock);
+ res = init_batch();
+ ast_mutex_unlock(&cdr_batch_lock);
+ }
+
+ return res;
+}
+
+/* \note This actually gets called a couple of times at shutdown. Once, before we start
+ hanging up channels, and then again, after the channel hangup timeout expires */
+void ast_cdr_engine_term(void)
+{
+ ast_cdr_submit_batch(batchsafeshutdown);
+}
+
+int ast_cdr_engine_reload(void)
+{
+ return do_reload(1);
+}
+
diff --git a/trunk/main/channel.c b/trunk/main/channel.c
new file mode 100644
index 000000000..d9e018310
--- /dev/null
+++ b/trunk/main/channel.c
@@ -0,0 +1,4843 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006, 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.
+ */
+
+/*! \file
+ *
+ * \brief Channel Management
+ *
+ * \author Mark Spencer <markster@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/_private.h"
+
+#include <sys/time.h>
+#include <signal.h>
+#include <math.h>
+
+#include "asterisk/paths.h" /* use ast_config_AST_SYSTEM_NAME */
+#include "asterisk/zapata.h"
+
+#include "asterisk/pbx.h"
+#include "asterisk/frame.h"
+#include "asterisk/sched.h"
+#include "asterisk/channel.h"
+#include "asterisk/musiconhold.h"
+#include "asterisk/say.h"
+#include "asterisk/file.h"
+#include "asterisk/cli.h"
+#include "asterisk/translate.h"
+#include "asterisk/manager.h"
+#include "asterisk/chanvars.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/indications.h"
+#include "asterisk/monitor.h"
+#include "asterisk/causes.h"
+#include "asterisk/callerid.h"
+#include "asterisk/utils.h"
+#include "asterisk/lock.h"
+#include "asterisk/app.h"
+#include "asterisk/transcap.h"
+#include "asterisk/devicestate.h"
+#include "asterisk/sha1.h"
+#include "asterisk/threadstorage.h"
+#include "asterisk/slinfactory.h"
+#include "asterisk/audiohook.h"
+
+#ifdef HAVE_EPOLL
+#include <sys/epoll.h>
+#endif
+
+struct ast_epoll_data {
+ struct ast_channel *chan;
+ int which;
+};
+
+/* uncomment if you have problems with 'monitoring' synchronized files */
+#if 0
+#define MONITOR_CONSTANT_DELAY
+#define MONITOR_DELAY 150 * 8 /*!< 150 ms of MONITORING DELAY */
+#endif
+
+/*! \brief Prevent new channel allocation if shutting down. */
+static int shutting_down;
+
+static int uniqueint;
+
+unsigned long global_fin, global_fout;
+
+AST_THREADSTORAGE(state2str_threadbuf);
+#define STATE2STR_BUFSIZE 32
+
+/*! Default amount of time to use when emulating a digit as a begin and end
+ * 100ms */
+#define AST_DEFAULT_EMULATE_DTMF_DURATION 100
+
+/*! Minimum allowed digit length - 80ms */
+#define AST_MIN_DTMF_DURATION 80
+
+/*! Minimum amount of time between the end of the last digit and the beginning
+ * of a new one - 45ms */
+#define AST_MIN_DTMF_GAP 45
+
+/*! \brief List of channel drivers */
+struct chanlist {
+ const struct ast_channel_tech *tech;
+ AST_LIST_ENTRY(chanlist) list;
+};
+
+/*! \brief the list of registered channel types */
+static AST_LIST_HEAD_NOLOCK_STATIC(backends, chanlist);
+
+/*! \brief the list of channels we have. Note that the lock for this list is used for
+ both the channels list and the backends list. */
+static AST_RWLIST_HEAD_STATIC(channels, ast_channel);
+
+/*! \brief map AST_CAUSE's to readable string representations
+ *
+ * \ref causes.h
+*/
+const struct ast_cause {
+ int cause;
+ const char *name;
+ const char *desc;
+} causes[] = {
+ { AST_CAUSE_UNALLOCATED, "UNALLOCATED", "Unallocated (unassigned) number" },
+ { AST_CAUSE_NO_ROUTE_TRANSIT_NET, "NO_ROUTE_TRANSIT_NET", "No route to specified transmit network" },
+ { AST_CAUSE_NO_ROUTE_DESTINATION, "NO_ROUTE_DESTINATION", "No route to destination" },
+ { AST_CAUSE_CHANNEL_UNACCEPTABLE, "CHANNEL_UNACCEPTABLE", "Channel unacceptable" },
+ { AST_CAUSE_CALL_AWARDED_DELIVERED, "CALL_AWARDED_DELIVERED", "Call awarded and being delivered in an established channel" },
+ { AST_CAUSE_NORMAL_CLEARING, "NORMAL_CLEARING", "Normal Clearing" },
+ { AST_CAUSE_USER_BUSY, "USER_BUSY", "User busy" },
+ { AST_CAUSE_NO_USER_RESPONSE, "NO_USER_RESPONSE", "No user responding" },
+ { AST_CAUSE_NO_ANSWER, "NO_ANSWER", "User alerting, no answer" },
+ { AST_CAUSE_CALL_REJECTED, "CALL_REJECTED", "Call Rejected" },
+ { AST_CAUSE_NUMBER_CHANGED, "NUMBER_CHANGED", "Number changed" },
+ { AST_CAUSE_DESTINATION_OUT_OF_ORDER, "DESTINATION_OUT_OF_ORDER", "Destination out of order" },
+ { AST_CAUSE_INVALID_NUMBER_FORMAT, "INVALID_NUMBER_FORMAT", "Invalid number format" },
+ { AST_CAUSE_FACILITY_REJECTED, "FACILITY_REJECTED", "Facility rejected" },
+ { AST_CAUSE_RESPONSE_TO_STATUS_ENQUIRY, "RESPONSE_TO_STATUS_ENQUIRY", "Response to STATus ENQuiry" },
+ { AST_CAUSE_NORMAL_UNSPECIFIED, "NORMAL_UNSPECIFIED", "Normal, unspecified" },
+ { AST_CAUSE_NORMAL_CIRCUIT_CONGESTION, "NORMAL_CIRCUIT_CONGESTION", "Circuit/channel congestion" },
+ { AST_CAUSE_NETWORK_OUT_OF_ORDER, "NETWORK_OUT_OF_ORDER", "Network out of order" },
+ { AST_CAUSE_NORMAL_TEMPORARY_FAILURE, "NORMAL_TEMPORARY_FAILURE", "Temporary failure" },
+ { AST_CAUSE_SWITCH_CONGESTION, "SWITCH_CONGESTION", "Switching equipment congestion" },
+ { AST_CAUSE_ACCESS_INFO_DISCARDED, "ACCESS_INFO_DISCARDED", "Access information discarded" },
+ { AST_CAUSE_REQUESTED_CHAN_UNAVAIL, "REQUESTED_CHAN_UNAVAIL", "Requested channel not available" },
+ { AST_CAUSE_PRE_EMPTED, "PRE_EMPTED", "Pre-empted" },
+ { AST_CAUSE_FACILITY_NOT_SUBSCRIBED, "FACILITY_NOT_SUBSCRIBED", "Facility not subscribed" },
+ { AST_CAUSE_OUTGOING_CALL_BARRED, "OUTGOING_CALL_BARRED", "Outgoing call barred" },
+ { AST_CAUSE_INCOMING_CALL_BARRED, "INCOMING_CALL_BARRED", "Incoming call barred" },
+ { AST_CAUSE_BEARERCAPABILITY_NOTAUTH, "BEARERCAPABILITY_NOTAUTH", "Bearer capability not authorized" },
+ { AST_CAUSE_BEARERCAPABILITY_NOTAVAIL, "BEARERCAPABILITY_NOTAVAIL", "Bearer capability not available" },
+ { AST_CAUSE_BEARERCAPABILITY_NOTIMPL, "BEARERCAPABILITY_NOTIMPL", "Bearer capability not implemented" },
+ { AST_CAUSE_CHAN_NOT_IMPLEMENTED, "CHAN_NOT_IMPLEMENTED", "Channel not implemented" },
+ { AST_CAUSE_FACILITY_NOT_IMPLEMENTED, "FACILITY_NOT_IMPLEMENTED", "Facility not implemented" },
+ { AST_CAUSE_INVALID_CALL_REFERENCE, "INVALID_CALL_REFERENCE", "Invalid call reference value" },
+ { AST_CAUSE_INCOMPATIBLE_DESTINATION, "INCOMPATIBLE_DESTINATION", "Incompatible destination" },
+ { AST_CAUSE_INVALID_MSG_UNSPECIFIED, "INVALID_MSG_UNSPECIFIED", "Invalid message unspecified" },
+ { AST_CAUSE_MANDATORY_IE_MISSING, "MANDATORY_IE_MISSING", "Mandatory information element is missing" },
+ { AST_CAUSE_MESSAGE_TYPE_NONEXIST, "MESSAGE_TYPE_NONEXIST", "Message type nonexist." },
+ { AST_CAUSE_WRONG_MESSAGE, "WRONG_MESSAGE", "Wrong message" },
+ { AST_CAUSE_IE_NONEXIST, "IE_NONEXIST", "Info. element nonexist or not implemented" },
+ { AST_CAUSE_INVALID_IE_CONTENTS, "INVALID_IE_CONTENTS", "Invalid information element contents" },
+ { AST_CAUSE_WRONG_CALL_STATE, "WRONG_CALL_STATE", "Message not compatible with call state" },
+ { AST_CAUSE_RECOVERY_ON_TIMER_EXPIRE, "RECOVERY_ON_TIMER_EXPIRE", "Recover on timer expiry" },
+ { AST_CAUSE_MANDATORY_IE_LENGTH_ERROR, "MANDATORY_IE_LENGTH_ERROR", "Mandatory IE length error" },
+ { AST_CAUSE_PROTOCOL_ERROR, "PROTOCOL_ERROR", "Protocol error, unspecified" },
+ { AST_CAUSE_INTERWORKING, "INTERWORKING", "Interworking, unspecified" },
+};
+
+struct ast_variable *ast_channeltype_list(void)
+{
+ struct chanlist *cl;
+ struct ast_variable *var=NULL, *prev = NULL;
+ AST_LIST_TRAVERSE(&backends, cl, list) {
+ if (prev) {
+ if ((prev->next = ast_variable_new(cl->tech->type, cl->tech->description, "")))
+ prev = prev->next;
+ } else {
+ var = ast_variable_new(cl->tech->type, cl->tech->description, "");
+ prev = var;
+ }
+ }
+ return var;
+}
+
+/*! \brief Show channel types - CLI command */
+static char *handle_cli_core_show_channeltypes(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+#define FORMAT "%-10.10s %-40.40s %-12.12s %-12.12s %-12.12s\n"
+ struct chanlist *cl;
+ int count_chan = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show channeltypes";
+ e->usage =
+ "Usage: core show channeltypes\n"
+ " Lists available channel types registered in your\n"
+ " Asterisk server.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+
+ ast_cli(a->fd, FORMAT, "Type", "Description", "Devicestate", "Indications", "Transfer");
+ ast_cli(a->fd, FORMAT, "----------", "-----------", "-----------", "-----------", "--------");
+ if (AST_RWLIST_RDLOCK(&channels)) {
+ ast_log(LOG_WARNING, "Unable to lock channel list\n");
+ return CLI_FAILURE;
+ }
+ AST_LIST_TRAVERSE(&backends, cl, list) {
+ ast_cli(a->fd, FORMAT, cl->tech->type, cl->tech->description,
+ (cl->tech->devicestate) ? "yes" : "no",
+ (cl->tech->indicate) ? "yes" : "no",
+ (cl->tech->transfer) ? "yes" : "no");
+ count_chan++;
+ }
+ AST_RWLIST_UNLOCK(&channels);
+ ast_cli(a->fd, "----------\n%d channel drivers registered.\n", count_chan);
+ return CLI_SUCCESS;
+
+#undef FORMAT
+}
+
+static char *complete_channeltypes(struct ast_cli_args *a)
+{
+ struct chanlist *cl;
+ int which = 0;
+ int wordlen;
+ char *ret = NULL;
+
+ if (a->pos != 3)
+ return NULL;
+
+ wordlen = strlen(a->word);
+
+ AST_LIST_TRAVERSE(&backends, cl, list) {
+ if (!strncasecmp(a->word, cl->tech->type, wordlen) && ++which > a->n) {
+ ret = ast_strdup(cl->tech->type);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+/*! \brief Show details about a channel driver - CLI command */
+static char *handle_cli_core_show_channeltype(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct chanlist *cl = NULL;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show channeltype";
+ e->usage =
+ "Usage: core show channeltype <name>\n"
+ " Show details about the specified channel type, <name>.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return complete_channeltypes(a);
+ }
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+
+ if (AST_RWLIST_RDLOCK(&channels)) {
+ ast_log(LOG_WARNING, "Unable to lock channel list\n");
+ return CLI_FAILURE;
+ }
+
+ AST_LIST_TRAVERSE(&backends, cl, list) {
+ if (!strncasecmp(cl->tech->type, a->argv[3], strlen(cl->tech->type)))
+ break;
+ }
+
+
+ if (!cl) {
+ ast_cli(a->fd, "\n%s is not a registered channel driver.\n", a->argv[3]);
+ AST_RWLIST_UNLOCK(&channels);
+ return CLI_FAILURE;
+ }
+
+ ast_cli(a->fd,
+ "-- Info about channel driver: %s --\n"
+ " Device State: %s\n"
+ " Indication: %s\n"
+ " Transfer : %s\n"
+ " Capabilities: %d\n"
+ " Digit Begin: %s\n"
+ " Digit End: %s\n"
+ " Send HTML : %s\n"
+ " Image Support: %s\n"
+ " Text Support: %s\n",
+ cl->tech->type,
+ (cl->tech->devicestate) ? "yes" : "no",
+ (cl->tech->indicate) ? "yes" : "no",
+ (cl->tech->transfer) ? "yes" : "no",
+ (cl->tech->capabilities) ? cl->tech->capabilities : -1,
+ (cl->tech->send_digit_begin) ? "yes" : "no",
+ (cl->tech->send_digit_end) ? "yes" : "no",
+ (cl->tech->send_html) ? "yes" : "no",
+ (cl->tech->send_image) ? "yes" : "no",
+ (cl->tech->send_text) ? "yes" : "no"
+
+ );
+
+ AST_RWLIST_UNLOCK(&channels);
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli_channel[] = {
+ AST_CLI_DEFINE(handle_cli_core_show_channeltypes, "List available channel types"),
+ AST_CLI_DEFINE(handle_cli_core_show_channeltype, "Give more details on that channel type")
+};
+
+/*! \brief Checks to see if a channel is needing hang up */
+int ast_check_hangup(struct ast_channel *chan)
+{
+ if (chan->_softhangup) /* yes if soft hangup flag set */
+ return 1;
+ if (!chan->tech_pvt) /* yes if no technology private data */
+ return 1;
+ if (!chan->whentohangup) /* no if no hangup scheduled */
+ return 0;
+ if (chan->whentohangup > time(NULL)) /* no if hangup time has not come yet. */
+ return 0;
+ chan->_softhangup |= AST_SOFTHANGUP_TIMEOUT; /* record event */
+ return 1;
+}
+
+static int ast_check_hangup_locked(struct ast_channel *chan)
+{
+ int res;
+ ast_channel_lock(chan);
+ res = ast_check_hangup(chan);
+ ast_channel_unlock(chan);
+ return res;
+}
+
+/*! \brief Initiate system shutdown */
+void ast_begin_shutdown(int hangup)
+{
+ struct ast_channel *c;
+ shutting_down = 1;
+ if (hangup) {
+ AST_RWLIST_RDLOCK(&channels);
+ AST_RWLIST_TRAVERSE(&channels, c, chan_list)
+ ast_softhangup(c, AST_SOFTHANGUP_SHUTDOWN);
+ AST_RWLIST_UNLOCK(&channels);
+ }
+}
+
+/*! \brief returns number of active/allocated channels */
+int ast_active_channels(void)
+{
+ struct ast_channel *c;
+ int cnt = 0;
+ AST_RWLIST_RDLOCK(&channels);
+ AST_RWLIST_TRAVERSE(&channels, c, chan_list)
+ cnt++;
+ AST_RWLIST_UNLOCK(&channels);
+ return cnt;
+}
+
+/*! \brief Cancel a shutdown in progress */
+void ast_cancel_shutdown(void)
+{
+ shutting_down = 0;
+}
+
+/*! \brief Returns non-zero if Asterisk is being shut down */
+int ast_shutting_down(void)
+{
+ return shutting_down;
+}
+
+/*! \brief Set when to hangup channel */
+void ast_channel_setwhentohangup(struct ast_channel *chan, time_t offset)
+{
+ chan->whentohangup = offset ? time(NULL) + offset : 0;
+ ast_queue_frame(chan, &ast_null_frame);
+ return;
+}
+
+/*! \brief Compare a offset with when to hangup channel */
+int ast_channel_cmpwhentohangup(struct ast_channel *chan, time_t offset)
+{
+ time_t whentohangup;
+
+ if (!chan->whentohangup)
+ return (offset == 0) ? 0 : -1;
+
+ if (!offset) /* XXX why is this special? */
+ return 1;
+
+ whentohangup = offset + time(NULL);
+
+ if (chan->whentohangup < whentohangup)
+ return 1;
+ else if (chan->whentohangup == whentohangup)
+ return 0;
+ else
+ return -1;
+}
+
+/*! \brief Register a new telephony channel in Asterisk */
+int ast_channel_register(const struct ast_channel_tech *tech)
+{
+ struct chanlist *chan;
+
+ AST_RWLIST_WRLOCK(&channels);
+
+ AST_LIST_TRAVERSE(&backends, chan, list) {
+ if (!strcasecmp(tech->type, chan->tech->type)) {
+ ast_log(LOG_WARNING, "Already have a handler for type '%s'\n", tech->type);
+ AST_RWLIST_UNLOCK(&channels);
+ return -1;
+ }
+ }
+
+ if (!(chan = ast_calloc(1, sizeof(*chan)))) {
+ AST_RWLIST_UNLOCK(&channels);
+ return -1;
+ }
+ chan->tech = tech;
+ AST_LIST_INSERT_HEAD(&backends, chan, list);
+
+ ast_debug(1, "Registered handler for '%s' (%s)\n", chan->tech->type, chan->tech->description);
+
+ ast_verb(2, "Registered channel type '%s' (%s)\n", chan->tech->type, chan->tech->description);
+
+ AST_RWLIST_UNLOCK(&channels);
+ return 0;
+}
+
+/*! \brief Unregister channel driver */
+void ast_channel_unregister(const struct ast_channel_tech *tech)
+{
+ struct chanlist *chan;
+
+ ast_debug(1, "Unregistering channel type '%s'\n", tech->type);
+
+ AST_RWLIST_WRLOCK(&channels);
+
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&backends, chan, list) {
+ if (chan->tech == tech) {
+ AST_LIST_REMOVE_CURRENT(list);
+ ast_free(chan);
+ ast_verb(2, "Unregistered channel type '%s'\n", tech->type);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+
+ AST_RWLIST_UNLOCK(&channels);
+}
+
+/*! \brief Get handle to channel driver based on name */
+const struct ast_channel_tech *ast_get_channel_tech(const char *name)
+{
+ struct chanlist *chanls;
+ const struct ast_channel_tech *ret = NULL;
+
+ if (AST_RWLIST_RDLOCK(&channels)) {
+ ast_log(LOG_WARNING, "Unable to lock channel tech list\n");
+ return NULL;
+ }
+
+ AST_LIST_TRAVERSE(&backends, chanls, list) {
+ if (!strcasecmp(name, chanls->tech->type)) {
+ ret = chanls->tech;
+ break;
+ }
+ }
+
+ AST_RWLIST_UNLOCK(&channels);
+
+ return ret;
+}
+
+/*! \brief Gives the string form of a given hangup cause */
+const char *ast_cause2str(int cause)
+{
+ int x;
+
+ for (x=0; x < sizeof(causes) / sizeof(causes[0]); x++) {
+ if (causes[x].cause == cause)
+ return causes[x].desc;
+ }
+
+ return "Unknown";
+}
+
+/*! \brief Convert a symbolic hangup cause to number */
+int ast_str2cause(const char *name)
+{
+ int x;
+
+ for (x = 0; x < sizeof(causes) / sizeof(causes[0]); x++)
+ if (strncasecmp(causes[x].name, name, strlen(causes[x].name)) == 0)
+ return causes[x].cause;
+
+ return -1;
+}
+
+/*! \brief Gives the string form of a given channel state.
+ \note This function is not reentrant.
+ */
+const char *ast_state2str(enum ast_channel_state state)
+{
+ char *buf;
+
+ switch (state) {
+ case AST_STATE_DOWN:
+ return "Down";
+ case AST_STATE_RESERVED:
+ return "Rsrvd";
+ case AST_STATE_OFFHOOK:
+ return "OffHook";
+ case AST_STATE_DIALING:
+ return "Dialing";
+ case AST_STATE_RING:
+ return "Ring";
+ case AST_STATE_RINGING:
+ return "Ringing";
+ case AST_STATE_UP:
+ return "Up";
+ case AST_STATE_BUSY:
+ return "Busy";
+ case AST_STATE_DIALING_OFFHOOK:
+ return "Dialing Offhook";
+ case AST_STATE_PRERING:
+ return "Pre-ring";
+ default:
+ if (!(buf = ast_threadstorage_get(&state2str_threadbuf, STATE2STR_BUFSIZE)))
+ return "Unknown";
+ snprintf(buf, STATE2STR_BUFSIZE, "Unknown (%d)", state);
+ return buf;
+ }
+}
+
+/*! \brief Gives the string form of a given transfer capability */
+char *ast_transfercapability2str(int transfercapability)
+{
+ switch (transfercapability) {
+ case AST_TRANS_CAP_SPEECH:
+ return "SPEECH";
+ case AST_TRANS_CAP_DIGITAL:
+ return "DIGITAL";
+ case AST_TRANS_CAP_RESTRICTED_DIGITAL:
+ return "RESTRICTED_DIGITAL";
+ case AST_TRANS_CAP_3_1K_AUDIO:
+ return "3K1AUDIO";
+ case AST_TRANS_CAP_DIGITAL_W_TONES:
+ return "DIGITAL_W_TONES";
+ case AST_TRANS_CAP_VIDEO:
+ return "VIDEO";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+/*! \brief Pick the best audio codec */
+int ast_best_codec(int fmts)
+{
+ /* This just our opinion, expressed in code. We are asked to choose
+ the best codec to use, given no information */
+ int x;
+ static const int prefs[] =
+ {
+ /*! Okay, ulaw is used by all telephony equipment, so start with it */
+ AST_FORMAT_ULAW,
+ /*! Unless of course, you're a silly European, so then prefer ALAW */
+ AST_FORMAT_ALAW,
+ /*! G.722 is better then all below, but not as common as the above... so give ulaw and alaw priority */
+ AST_FORMAT_G722,
+ /*! Okay, well, signed linear is easy to translate into other stuff */
+ AST_FORMAT_SLINEAR16,
+ AST_FORMAT_SLINEAR,
+ /*! G.726 is standard ADPCM, in RFC3551 packing order */
+ AST_FORMAT_G726,
+ /*! G.726 is standard ADPCM, in AAL2 packing order */
+ AST_FORMAT_G726_AAL2,
+ /*! ADPCM has great sound quality and is still pretty easy to translate */
+ AST_FORMAT_ADPCM,
+ /*! Okay, we're down to vocoders now, so pick GSM because it's small and easier to
+ translate and sounds pretty good */
+ AST_FORMAT_GSM,
+ /*! iLBC is not too bad */
+ AST_FORMAT_ILBC,
+ /*! Speex is free, but computationally more expensive than GSM */
+ AST_FORMAT_SPEEX,
+ /*! Ick, LPC10 sounds terrible, but at least we have code for it, if you're tacky enough
+ to use it */
+ AST_FORMAT_LPC10,
+ /*! G.729a is faster than 723 and slightly less expensive */
+ AST_FORMAT_G729A,
+ /*! Down to G.723.1 which is proprietary but at least designed for voice */
+ AST_FORMAT_G723_1,
+ };
+
+ /* Strip out video */
+ fmts &= AST_FORMAT_AUDIO_MASK;
+
+ /* Find the first preferred codec in the format given */
+ for (x=0; x < (sizeof(prefs) / sizeof(prefs[0]) ); x++)
+ if (fmts & prefs[x])
+ return prefs[x];
+ ast_log(LOG_WARNING, "Don't know any of 0x%x formats\n", fmts);
+ return 0;
+}
+
+static const struct ast_channel_tech null_tech = {
+ .type = "NULL",
+ .description = "Null channel (should not see this)",
+};
+
+/*! \brief Create a new channel structure */
+struct ast_channel *ast_channel_alloc(int needqueue, int state, const char *cid_num, const char *cid_name, const char *acctcode, const char *exten, const char *context, const int amaflag, const char *name_fmt, ...)
+{
+ struct ast_channel *tmp;
+ int x;
+ int flags;
+ struct varshead *headp;
+ va_list ap1, ap2;
+
+ /* If shutting down, don't allocate any new channels */
+ if (shutting_down) {
+ ast_log(LOG_WARNING, "Channel allocation failed: Refusing due to active shutdown\n");
+ return NULL;
+ }
+
+ if (!(tmp = ast_calloc(1, sizeof(*tmp))))
+ return NULL;
+
+ if (!(tmp->sched = sched_context_create())) {
+ ast_log(LOG_WARNING, "Channel allocation failed: Unable to create schedule context\n");
+ ast_free(tmp);
+ return NULL;
+ }
+
+ if ((ast_string_field_init(tmp, 128))) {
+ sched_context_destroy(tmp->sched);
+ ast_free(tmp);
+ return NULL;
+ }
+
+#ifdef HAVE_EPOLL
+ tmp->epfd = epoll_create(25);
+#endif
+
+ for (x = 0; x < AST_MAX_FDS; x++) {
+ tmp->fds[x] = -1;
+#ifdef HAVE_EPOLL
+ tmp->epfd_data[x] = NULL;
+#endif
+ }
+
+#ifdef HAVE_ZAPTEL
+ tmp->timingfd = open("/dev/zap/timer", O_RDWR);
+ if (tmp->timingfd > -1) {
+ /* Check if timing interface supports new
+ ping/pong scheme */
+ flags = 1;
+ if (!ioctl(tmp->timingfd, ZT_TIMERPONG, &flags))
+ needqueue = 0;
+ }
+#else
+ tmp->timingfd = -1;
+#endif
+
+ if (needqueue) {
+ if (pipe(tmp->alertpipe)) {
+ ast_log(LOG_WARNING, "Channel allocation failed: Can't create alert pipe!\n");
+#ifdef HAVE_ZAPTEL
+ if (tmp->timingfd > -1)
+ close(tmp->timingfd);
+#endif
+ sched_context_destroy(tmp->sched);
+ ast_string_field_free_memory(tmp);
+ ast_free(tmp);
+ return NULL;
+ } else {
+ flags = fcntl(tmp->alertpipe[0], F_GETFL);
+ fcntl(tmp->alertpipe[0], F_SETFL, flags | O_NONBLOCK);
+ flags = fcntl(tmp->alertpipe[1], F_GETFL);
+ fcntl(tmp->alertpipe[1], F_SETFL, flags | O_NONBLOCK);
+ }
+ } else /* Make sure we've got it done right if they don't */
+ tmp->alertpipe[0] = tmp->alertpipe[1] = -1;
+
+ /* Always watch the alertpipe */
+ ast_channel_set_fd(tmp, AST_ALERT_FD, tmp->alertpipe[0]);
+ /* And timing pipe */
+ ast_channel_set_fd(tmp, AST_TIMING_FD, tmp->timingfd);
+ ast_string_field_set(tmp, name, "**Unknown**");
+
+ /* Initial state */
+ tmp->_state = state;
+
+ tmp->streamid = -1;
+
+ tmp->fin = global_fin;
+ tmp->fout = global_fout;
+
+ if (ast_strlen_zero(ast_config_AST_SYSTEM_NAME)) {
+ ast_string_field_build(tmp, uniqueid, "%li.%d", (long) time(NULL),
+ ast_atomic_fetchadd_int(&uniqueint, 1));
+ } else {
+ ast_string_field_build(tmp, uniqueid, "%s-%li.%d", ast_config_AST_SYSTEM_NAME,
+ (long) time(NULL), ast_atomic_fetchadd_int(&uniqueint, 1));
+ }
+
+ tmp->cid.cid_name = ast_strdup(cid_name);
+ tmp->cid.cid_num = ast_strdup(cid_num);
+
+ if (!ast_strlen_zero(name_fmt)) {
+ /* Almost every channel is calling this function, and setting the name via the ast_string_field_build() call.
+ * And they all use slightly different formats for their name string.
+ * This means, to set the name here, we have to accept variable args, and call the string_field_build from here.
+ * This means, that the stringfields must have a routine that takes the va_lists directly, and
+ * uses them to build the string, instead of forming the va_lists internally from the vararg ... list.
+ * This new function was written so this can be accomplished.
+ */
+ va_start(ap1, name_fmt);
+ va_start(ap2, name_fmt);
+ ast_string_field_build_va(tmp, name, name_fmt, ap1, ap2);
+ va_end(ap1);
+ va_end(ap2);
+ }
+
+ /* Reminder for the future: under what conditions do we NOT want to track cdrs on channels? */
+
+ /* These 4 variables need to be set up for the cdr_init() to work right */
+ if (amaflag)
+ tmp->amaflags = amaflag;
+ else
+ tmp->amaflags = ast_default_amaflags;
+
+ if (!ast_strlen_zero(acctcode))
+ ast_string_field_set(tmp, accountcode, acctcode);
+ else
+ ast_string_field_set(tmp, accountcode, ast_default_accountcode);
+
+ if (!ast_strlen_zero(context))
+ ast_copy_string(tmp->context, context, sizeof(tmp->context));
+ else
+ strcpy(tmp->context, "default");
+
+ if (!ast_strlen_zero(exten))
+ ast_copy_string(tmp->exten, exten, sizeof(tmp->exten));
+ else
+ strcpy(tmp->exten, "s");
+
+ tmp->priority = 1;
+
+ tmp->cdr = ast_cdr_alloc();
+ ast_cdr_init(tmp->cdr, tmp);
+ ast_cdr_start(tmp->cdr);
+
+ headp = &tmp->varshead;
+ AST_LIST_HEAD_INIT_NOLOCK(headp);
+
+ ast_mutex_init(&tmp->lock_dont_use);
+
+ AST_LIST_HEAD_INIT_NOLOCK(&tmp->datastores);
+
+ ast_string_field_set(tmp, language, defaultlanguage);
+
+ tmp->tech = &null_tech;
+
+ AST_RWLIST_WRLOCK(&channels);
+ AST_RWLIST_INSERT_HEAD(&channels, tmp, chan_list);
+ AST_RWLIST_UNLOCK(&channels);
+
+ /*\!note
+ * and now, since the channel structure is built, and has its name, let's
+ * call the manager event generator with this Newchannel event. This is the
+ * proper and correct place to make this call, but you sure do have to pass
+ * a lot of data into this func to do it here!
+ */
+ if (!ast_strlen_zero(name_fmt)) {
+ manager_event(EVENT_FLAG_CALL, "Newchannel",
+ "Channel: %s\r\n"
+ "ChannelState: %d\r\n"
+ "ChannelStateDesc: %s\r\n"
+ "CallerIDNum: %s\r\n"
+ "CallerIDName: %s\r\n"
+ "AccountCode: %s\r\n"
+ "Uniqueid: %s\r\n",
+ tmp->name,
+ state,
+ ast_state2str(state),
+ S_OR(cid_num, ""),
+ S_OR(cid_name, ""),
+ tmp->accountcode,
+ tmp->uniqueid);
+ }
+
+ return tmp;
+}
+
+/*! \brief Queue an outgoing media frame */
+int ast_queue_frame(struct ast_channel *chan, struct ast_frame *fin)
+{
+ struct ast_frame *f;
+ struct ast_frame *cur;
+ int blah = 1;
+ int qlen = 0;
+
+ /* Build us a copy and free the original one */
+ if (!(f = ast_frdup(fin))) {
+ ast_log(LOG_WARNING, "Unable to duplicate frame\n");
+ return -1;
+ }
+ ast_channel_lock(chan);
+
+ /* See if the last frame on the queue is a hangup, if so don't queue anything */
+ if ((cur = AST_LIST_LAST(&chan->readq)) && (cur->frametype == AST_FRAME_CONTROL) && (cur->subclass == AST_CONTROL_HANGUP)) {
+ ast_frfree(f);
+ ast_channel_unlock(chan);
+ return 0;
+ }
+
+ /* Count how many frames exist on the queue */
+ AST_LIST_TRAVERSE(&chan->readq, cur, frame_list) {
+ qlen++;
+ }
+
+ /* Allow up to 96 voice frames outstanding, and up to 128 total frames */
+ if (((fin->frametype == AST_FRAME_VOICE) && (qlen > 96)) || (qlen > 128)) {
+ if (fin->frametype != AST_FRAME_VOICE) {
+ ast_log(LOG_WARNING, "Exceptionally long queue length queuing to %s\n", chan->name);
+ CRASH;
+ } else {
+ ast_debug(1, "Dropping voice to exceptionally long queue on %s\n", chan->name);
+ ast_frfree(f);
+ ast_channel_unlock(chan);
+ return 0;
+ }
+ }
+ AST_LIST_INSERT_TAIL(&chan->readq, f, frame_list);
+ if (chan->alertpipe[1] > -1) {
+ if (write(chan->alertpipe[1], &blah, sizeof(blah)) != sizeof(blah))
+ ast_log(LOG_WARNING, "Unable to write to alert pipe on %s, frametype/subclass %d/%d (qlen = %d): %s!\n",
+ chan->name, f->frametype, f->subclass, qlen, strerror(errno));
+#ifdef HAVE_ZAPTEL
+ } else if (chan->timingfd > -1) {
+ ioctl(chan->timingfd, ZT_TIMERPING, &blah);
+#endif
+ } else if (ast_test_flag(chan, AST_FLAG_BLOCKING)) {
+ pthread_kill(chan->blocker, SIGURG);
+ }
+ ast_channel_unlock(chan);
+ return 0;
+}
+
+/*! \brief Queue a hangup frame for channel */
+int ast_queue_hangup(struct ast_channel *chan)
+{
+ struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_HANGUP };
+ /* Yeah, let's not change a lock-critical value without locking */
+ if (!ast_channel_trylock(chan)) {
+ chan->_softhangup |= AST_SOFTHANGUP_DEV;
+ ast_channel_unlock(chan);
+ }
+ return ast_queue_frame(chan, &f);
+}
+
+/*! \brief Queue a control frame */
+int ast_queue_control(struct ast_channel *chan, enum ast_control_frame_type control)
+{
+ struct ast_frame f = { AST_FRAME_CONTROL, };
+
+ f.subclass = control;
+
+ return ast_queue_frame(chan, &f);
+}
+
+/*! \brief Queue a control frame with payload */
+int ast_queue_control_data(struct ast_channel *chan, enum ast_control_frame_type control,
+ const void *data, size_t datalen)
+{
+ struct ast_frame f = { AST_FRAME_CONTROL, };
+
+ f.subclass = control;
+ f.data = (void *) data;
+ f.datalen = datalen;
+
+ return ast_queue_frame(chan, &f);
+}
+
+/*! \brief Set defer DTMF flag on channel */
+int ast_channel_defer_dtmf(struct ast_channel *chan)
+{
+ int pre = 0;
+
+ if (chan) {
+ pre = ast_test_flag(chan, AST_FLAG_DEFER_DTMF);
+ ast_set_flag(chan, AST_FLAG_DEFER_DTMF);
+ }
+ return pre;
+}
+
+/*! \brief Unset defer DTMF flag on channel */
+void ast_channel_undefer_dtmf(struct ast_channel *chan)
+{
+ if (chan)
+ ast_clear_flag(chan, AST_FLAG_DEFER_DTMF);
+}
+
+/*!
+ * \brief Helper function to find channels.
+ *
+ * It supports these modes:
+ *
+ * prev != NULL : get channel next in list after prev
+ * name != NULL : get channel with matching name
+ * name != NULL && namelen != 0 : get channel whose name starts with prefix
+ * exten != NULL : get channel whose exten or macroexten matches
+ * context != NULL && exten != NULL : get channel whose context or macrocontext
+ *
+ * It returns with the channel's lock held. If getting the individual lock fails,
+ * unlock and retry quickly up to 10 times, then give up.
+ *
+ * \note XXX Note that this code has cost O(N) because of the need to verify
+ * that the object is still on the global list.
+ *
+ * \note XXX also note that accessing fields (e.g. c->name in ast_log())
+ * can only be done with the lock held or someone could delete the
+ * object while we work on it. This causes some ugliness in the code.
+ * Note that removing the first ast_log() may be harmful, as it would
+ * shorten the retry period and possibly cause failures.
+ * We should definitely go for a better scheme that is deadlock-free.
+ */
+static struct ast_channel *channel_find_locked(const struct ast_channel *prev,
+ const char *name, const int namelen,
+ const char *context, const char *exten)
+{
+ const char *msg = prev ? "deadlock" : "initial deadlock";
+ int retries;
+ struct ast_channel *c;
+ const struct ast_channel *_prev = prev;
+
+ for (retries = 0; retries < 10; retries++) {
+ int done;
+ AST_RWLIST_RDLOCK(&channels);
+ AST_RWLIST_TRAVERSE(&channels, c, chan_list) {
+ prev = _prev;
+ if (prev) { /* look for next item */
+ if (c != prev) /* not this one */
+ continue;
+ /* found, prepare to return c->next */
+ if ((c = AST_RWLIST_NEXT(c, chan_list)) == NULL) break;
+ /* If prev was the last item on the channel list, then we just
+ * want to return NULL, instead of trying to deref NULL in the
+ * next section.
+ */
+ prev = NULL;
+ /* We want prev to be NULL in case we end up doing more searching through
+ * the channel list to find the channel (ie: name searching). If we didn't
+ * set this to NULL the logic would just blow up
+ * XXX Need a better explanation for this ...
+ */
+ }
+ if (name) { /* want match by name */
+ if ((!namelen && strcasecmp(c->name, name) && strcmp(c->uniqueid, name)) ||
+ (namelen && strncasecmp(c->name, name, namelen)))
+ continue; /* name match failed */
+ } else if (exten) {
+ if (context && strcasecmp(c->context, context) &&
+ strcasecmp(c->macrocontext, context))
+ continue; /* context match failed */
+ if (strcasecmp(c->exten, exten) &&
+ strcasecmp(c->macroexten, exten))
+ continue; /* exten match failed */
+ }
+ /* if we get here, c points to the desired record */
+ break;
+ }
+ /* exit if chan not found or mutex acquired successfully */
+ /* this is slightly unsafe, as we _should_ hold the lock to access c->name */
+ done = c == NULL || ast_channel_trylock(c) == 0;
+ if (!done) {
+ ast_debug(1, "Avoiding %s for channel '%p'\n", msg, c);
+ if (retries == 9) {
+ /* We are about to fail due to a deadlock, so report this
+ * while we still have the list lock.
+ */
+ ast_debug(1, "Failure, could not lock '%p' after %d retries!\n", c, retries);
+ /* As we have deadlocked, we will skip this channel and
+ * see if there is another match.
+ * NOTE: No point doing this for a full-name match,
+ * as there can be no more matches.
+ */
+ if (!(name && !namelen)) {
+ prev = c;
+ retries = -1;
+ }
+ }
+ }
+ AST_RWLIST_UNLOCK(&channels);
+ if (done)
+ return c;
+ /* If we reach this point we basically tried to lock a channel and failed. Instead of
+ * starting from the beginning of the list we can restore our saved pointer to the previous
+ * channel and start from there.
+ */
+ prev = _prev;
+ usleep(1); /* give other threads a chance before retrying */
+ }
+
+ return NULL;
+}
+
+/*! \brief Browse channels in use */
+struct ast_channel *ast_channel_walk_locked(const struct ast_channel *prev)
+{
+ return channel_find_locked(prev, NULL, 0, NULL, NULL);
+}
+
+/*! \brief Get channel by name and lock it */
+struct ast_channel *ast_get_channel_by_name_locked(const char *name)
+{
+ return channel_find_locked(NULL, name, 0, NULL, NULL);
+}
+
+/*! \brief Get channel by name prefix and lock it */
+struct ast_channel *ast_get_channel_by_name_prefix_locked(const char *name, const int namelen)
+{
+ return channel_find_locked(NULL, name, namelen, NULL, NULL);
+}
+
+/*! \brief Get next channel by name prefix and lock it */
+struct ast_channel *ast_walk_channel_by_name_prefix_locked(const struct ast_channel *chan, const char *name,
+ const int namelen)
+{
+ return channel_find_locked(chan, name, namelen, NULL, NULL);
+}
+
+/*! \brief Get channel by exten (and optionally context) and lock it */
+struct ast_channel *ast_get_channel_by_exten_locked(const char *exten, const char *context)
+{
+ return channel_find_locked(NULL, NULL, 0, context, exten);
+}
+
+/*! \brief Get next channel by exten (and optionally context) and lock it */
+struct ast_channel *ast_walk_channel_by_exten_locked(const struct ast_channel *chan, const char *exten,
+ const char *context)
+{
+ return channel_find_locked(chan, NULL, 0, context, exten);
+}
+
+/*! \brief Wait, look for hangups and condition arg */
+int ast_safe_sleep_conditional(struct ast_channel *chan, int ms, int (*cond)(void*), void *data)
+{
+ struct ast_frame *f;
+
+ while (ms > 0) {
+ if (cond && ((*cond)(data) == 0))
+ return 0;
+ ms = ast_waitfor(chan, ms);
+ if (ms < 0)
+ return -1;
+ if (ms > 0) {
+ f = ast_read(chan);
+ if (!f)
+ return -1;
+ ast_frfree(f);
+ }
+ }
+ return 0;
+}
+
+/*! \brief Wait, look for hangups */
+int ast_safe_sleep(struct ast_channel *chan, int ms)
+{
+ return ast_safe_sleep_conditional(chan, ms, NULL, NULL);
+}
+
+static void free_cid(struct ast_callerid *cid)
+{
+ if (cid->cid_dnid)
+ ast_free(cid->cid_dnid);
+ if (cid->cid_num)
+ ast_free(cid->cid_num);
+ if (cid->cid_name)
+ ast_free(cid->cid_name);
+ if (cid->cid_ani)
+ ast_free(cid->cid_ani);
+ if (cid->cid_rdnis)
+ ast_free(cid->cid_rdnis);
+ cid->cid_dnid = cid->cid_num = cid->cid_name = cid->cid_ani = cid->cid_rdnis = NULL;
+}
+
+/*! \brief Free a channel structure */
+void ast_channel_free(struct ast_channel *chan)
+{
+ int fd;
+#ifdef HAVE_EPOLL
+ int i;
+#endif
+ struct ast_var_t *vardata;
+ struct ast_frame *f;
+ struct varshead *headp;
+ struct ast_datastore *datastore = NULL;
+ char name[AST_CHANNEL_NAME];
+
+ headp=&chan->varshead;
+
+ AST_RWLIST_WRLOCK(&channels);
+ if (!AST_RWLIST_REMOVE(&channels, chan, chan_list)) {
+ AST_RWLIST_UNLOCK(&channels);
+ ast_log(LOG_ERROR, "Unable to find channel in list to free. Assuming it has already been done.\n");
+ }
+ /* Lock and unlock the channel just to be sure nobody
+ has it locked still */
+ ast_channel_lock(chan);
+ ast_channel_unlock(chan);
+ if (chan->tech_pvt) {
+ ast_log(LOG_WARNING, "Channel '%s' may not have been hung up properly\n", chan->name);
+ ast_free(chan->tech_pvt);
+ }
+
+ if (chan->sched)
+ sched_context_destroy(chan->sched);
+
+ ast_copy_string(name, chan->name, sizeof(name));
+
+ /* Stop monitoring */
+ if (chan->monitor)
+ chan->monitor->stop( chan, 0 );
+
+ /* If there is native format music-on-hold state, free it */
+ if (chan->music_state)
+ ast_moh_cleanup(chan);
+
+ /* Free translators */
+ if (chan->readtrans)
+ ast_translator_free_path(chan->readtrans);
+ if (chan->writetrans)
+ ast_translator_free_path(chan->writetrans);
+ if (chan->pbx)
+ ast_log(LOG_WARNING, "PBX may not have been terminated properly on '%s'\n", chan->name);
+ free_cid(&chan->cid);
+ ast_mutex_destroy(&chan->lock_dont_use);
+ /* Close pipes if appropriate */
+ if ((fd = chan->alertpipe[0]) > -1)
+ close(fd);
+ if ((fd = chan->alertpipe[1]) > -1)
+ close(fd);
+ if ((fd = chan->timingfd) > -1)
+ close(fd);
+#ifdef HAVE_EPOLL
+ for (i = 0; i < AST_MAX_FDS; i++) {
+ if (chan->epfd_data[i])
+ free(chan->epfd_data[i]);
+ }
+ close(chan->epfd);
+#endif
+ while ((f = AST_LIST_REMOVE_HEAD(&chan->readq, frame_list)))
+ ast_frfree(f);
+
+ /* Get rid of each of the data stores on the channel */
+ while ((datastore = AST_LIST_REMOVE_HEAD(&chan->datastores, entry)))
+ /* Free the data store */
+ ast_channel_datastore_free(datastore);
+ AST_LIST_HEAD_INIT_NOLOCK(&chan->datastores);
+
+ /* loop over the variables list, freeing all data and deleting list items */
+ /* no need to lock the list, as the channel is already locked */
+
+ while ((vardata = AST_LIST_REMOVE_HEAD(headp, entries)))
+ ast_var_delete(vardata);
+
+ ast_app_group_discard(chan);
+
+ /* Destroy the jitterbuffer */
+ ast_jb_destroy(chan);
+
+ ast_string_field_free_memory(chan);
+ ast_free(chan);
+ AST_RWLIST_UNLOCK(&channels);
+
+ ast_device_state_changed_literal(name);
+}
+
+struct ast_datastore *ast_channel_datastore_alloc(const struct ast_datastore_info *info, const char *uid)
+{
+ struct ast_datastore *datastore = NULL;
+
+ /* Make sure we at least have type so we can identify this */
+ if (!info) {
+ return NULL;
+ }
+
+ /* Allocate memory for datastore and clear it */
+ datastore = ast_calloc(1, sizeof(*datastore));
+ if (!datastore) {
+ return NULL;
+ }
+
+ datastore->info = info;
+
+ datastore->uid = ast_strdup(uid);
+
+ return datastore;
+}
+
+int ast_channel_datastore_free(struct ast_datastore *datastore)
+{
+ int res = 0;
+
+ /* Using the destroy function (if present) destroy the data */
+ if (datastore->info->destroy != NULL && datastore->data != NULL) {
+ datastore->info->destroy(datastore->data);
+ datastore->data = NULL;
+ }
+
+ /* Free allocated UID memory */
+ if (datastore->uid != NULL) {
+ ast_free((void *) datastore->uid);
+ datastore->uid = NULL;
+ }
+
+ /* Finally free memory used by ourselves */
+ ast_free(datastore);
+
+ return res;
+}
+
+int ast_channel_datastore_inherit(struct ast_channel *from, struct ast_channel *to)
+{
+ struct ast_datastore *datastore = NULL, *datastore2;
+
+ AST_LIST_TRAVERSE(&from->datastores, datastore, entry) {
+ if (datastore->inheritance > 0) {
+ datastore2 = ast_channel_datastore_alloc(datastore->info, datastore->uid);
+ if (datastore2) {
+ datastore2->data = datastore->info->duplicate(datastore->data);
+ datastore2->inheritance = datastore->inheritance == DATASTORE_INHERIT_FOREVER ? DATASTORE_INHERIT_FOREVER : datastore->inheritance - 1;
+ AST_LIST_INSERT_TAIL(&to->datastores, datastore2, entry);
+ }
+ }
+ }
+ return 0;
+}
+
+int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
+{
+ int res = 0;
+
+ AST_LIST_INSERT_HEAD(&chan->datastores, datastore, entry);
+
+ return res;
+}
+
+int ast_channel_datastore_remove(struct ast_channel *chan, struct ast_datastore *datastore)
+{
+ return AST_LIST_REMOVE(&chan->datastores, datastore, entry) ? 0 : -1;
+}
+
+struct ast_datastore *ast_channel_datastore_find(struct ast_channel *chan, const struct ast_datastore_info *info, const char *uid)
+{
+ struct ast_datastore *datastore = NULL;
+
+ if (info == NULL)
+ return NULL;
+
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->datastores, datastore, entry) {
+ if (datastore->info == info) {
+ if (uid != NULL && datastore->uid != NULL) {
+ if (!strcasecmp(uid, datastore->uid)) {
+ /* Matched by type AND uid */
+ break;
+ }
+ } else {
+ /* Matched by type at least */
+ break;
+ }
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END
+
+ return datastore;
+}
+
+/*! Set the file descriptor on the channel */
+void ast_channel_set_fd(struct ast_channel *chan, int which, int fd)
+{
+#ifdef HAVE_EPOLL
+ struct epoll_event ev;
+ struct ast_epoll_data *aed = NULL;
+
+ if (chan->fds[which] > -1) {
+ epoll_ctl(chan->epfd, EPOLL_CTL_DEL, chan->fds[which], &ev);
+ aed = chan->epfd_data[which];
+ }
+
+ /* If this new fd is valid, add it to the epoll */
+ if (fd > -1) {
+ if (!aed && (!(aed = ast_calloc(1, sizeof(*aed)))))
+ return;
+
+ chan->epfd_data[which] = aed;
+ aed->chan = chan;
+ aed->which = which;
+
+ ev.events = EPOLLIN | EPOLLPRI | EPOLLERR | EPOLLHUP;
+ ev.data.ptr = aed;
+ epoll_ctl(chan->epfd, EPOLL_CTL_ADD, fd, &ev);
+ } else if (aed) {
+ /* We don't have to keep around this epoll data structure now */
+ free(aed);
+ chan->epfd_data[which] = NULL;
+ }
+#endif
+ chan->fds[which] = fd;
+ return;
+}
+
+/*! Add a channel to an optimized waitfor */
+void ast_poll_channel_add(struct ast_channel *chan0, struct ast_channel *chan1)
+{
+#ifdef HAVE_EPOLL
+ struct epoll_event ev;
+ int i = 0;
+
+ if (chan0->epfd == -1)
+ return;
+
+ /* Iterate through the file descriptors on chan1, adding them to chan0 */
+ for (i = 0; i < AST_MAX_FDS; i++) {
+ if (chan1->fds[i] == -1)
+ continue;
+ ev.events = EPOLLIN | EPOLLPRI | EPOLLERR | EPOLLHUP;
+ ev.data.ptr = chan1->epfd_data[i];
+ epoll_ctl(chan0->epfd, EPOLL_CTL_ADD, chan1->fds[i], &ev);
+ }
+
+#endif
+ return;
+}
+
+/*! Delete a channel from an optimized waitfor */
+void ast_poll_channel_del(struct ast_channel *chan0, struct ast_channel *chan1)
+{
+#ifdef HAVE_EPOLL
+ struct epoll_event ev;
+ int i = 0;
+
+ if (chan0->epfd == -1)
+ return;
+
+ for (i = 0; i < AST_MAX_FDS; i++) {
+ if (chan1->fds[i] == -1)
+ continue;
+ epoll_ctl(chan0->epfd, EPOLL_CTL_DEL, chan1->fds[i], &ev);
+ }
+
+#endif
+ return;
+}
+
+/*! \brief Softly hangup a channel, don't lock */
+int ast_softhangup_nolock(struct ast_channel *chan, int cause)
+{
+ ast_debug(1, "Soft-Hanging up channel '%s'\n", chan->name);
+ /* Inform channel driver that we need to be hung up, if it cares */
+ chan->_softhangup |= cause;
+ ast_queue_frame(chan, &ast_null_frame);
+ /* Interrupt any poll call or such */
+ if (ast_test_flag(chan, AST_FLAG_BLOCKING))
+ pthread_kill(chan->blocker, SIGURG);
+ return 0;
+}
+
+/*! \brief Softly hangup a channel, lock */
+int ast_softhangup(struct ast_channel *chan, int cause)
+{
+ int res;
+ ast_channel_lock(chan);
+ res = ast_softhangup_nolock(chan, cause);
+ ast_channel_unlock(chan);
+ return res;
+}
+
+static void free_translation(struct ast_channel *clone)
+{
+ if (clone->writetrans)
+ ast_translator_free_path(clone->writetrans);
+ if (clone->readtrans)
+ ast_translator_free_path(clone->readtrans);
+ clone->writetrans = NULL;
+ clone->readtrans = NULL;
+ clone->rawwriteformat = clone->nativeformats;
+ clone->rawreadformat = clone->nativeformats;
+}
+
+/*! \brief Hangup a channel */
+int ast_hangup(struct ast_channel *chan)
+{
+ int res = 0;
+ struct ast_cdr *cdr = NULL;
+
+ /* Don't actually hang up a channel that will masquerade as someone else, or
+ if someone is going to masquerade as us */
+ ast_channel_lock(chan);
+
+ if (chan->audiohooks) {
+ ast_audiohook_detach_list(chan->audiohooks);
+ chan->audiohooks = NULL;
+ }
+
+ ast_autoservice_stop(chan);
+
+ if (chan->masq) {
+ if (ast_do_masquerade(chan))
+ ast_log(LOG_WARNING, "Failed to perform masquerade\n");
+ }
+
+ if (chan->masq) {
+ ast_log(LOG_WARNING, "%s getting hung up, but someone is trying to masq into us?!?\n", chan->name);
+ ast_channel_unlock(chan);
+ return 0;
+ }
+ /* If this channel is one which will be masqueraded into something,
+ mark it as a zombie already, so we know to free it later */
+ if (chan->masqr) {
+ ast_set_flag(chan, AST_FLAG_ZOMBIE);
+ ast_channel_unlock(chan);
+ return 0;
+ }
+ free_translation(chan);
+ /* Close audio stream */
+ if (chan->stream) {
+ ast_closestream(chan->stream);
+ chan->stream = NULL;
+ }
+ /* Close video stream */
+ if (chan->vstream) {
+ ast_closestream(chan->vstream);
+ chan->vstream = NULL;
+ }
+ if (chan->sched) {
+ sched_context_destroy(chan->sched);
+ chan->sched = NULL;
+ }
+
+ if (chan->generatordata) /* Clear any tone stuff remaining */
+ if (chan->generator && chan->generator->release)
+ chan->generator->release(chan, chan->generatordata);
+ chan->generatordata = NULL;
+ chan->generator = NULL;
+ if (chan->cdr) { /* End the CDR if it hasn't already */
+ ast_cdr_end(chan->cdr);
+ cdr = chan->cdr;
+ chan->cdr = NULL;
+ }
+ if (ast_test_flag(chan, AST_FLAG_BLOCKING)) {
+ ast_log(LOG_WARNING, "Hard hangup called by thread %ld on %s, while fd "
+ "is blocked by thread %ld in procedure %s! Expect a failure\n",
+ (long)pthread_self(), chan->name, (long)chan->blocker, chan->blockproc);
+ CRASH;
+ }
+ if (!ast_test_flag(chan, AST_FLAG_ZOMBIE)) {
+ ast_debug(1, "Hanging up channel '%s'\n", chan->name);
+ if (chan->tech->hangup)
+ res = chan->tech->hangup(chan);
+ } else {
+ ast_debug(1, "Hanging up zombie '%s'\n", chan->name);
+ }
+
+ ast_channel_unlock(chan);
+ manager_event(EVENT_FLAG_CALL, "Hangup",
+ "Channel: %s\r\n"
+ "Uniqueid: %s\r\n"
+ "CallerIDNum: %s\r\n"
+ "CallerIDName: %s\r\n"
+ "Cause: %d\r\n"
+ "Cause-txt: %s\r\n",
+ chan->name,
+ chan->uniqueid,
+ S_OR(chan->cid.cid_num, "<unknown>"),
+ S_OR(chan->cid.cid_name, "<unknown>"),
+ chan->hangupcause,
+ ast_cause2str(chan->hangupcause)
+ );
+ ast_channel_free(chan);
+
+ if (cdr)
+ ast_cdr_detach(cdr);
+
+ return res;
+}
+
+int __ast_answer(struct ast_channel *chan, unsigned int delay)
+{
+ int res = 0;
+
+ ast_channel_lock(chan);
+
+ /* You can't answer an outbound call */
+ if (ast_test_flag(chan, AST_FLAG_OUTGOING)) {
+ ast_channel_unlock(chan);
+ return 0;
+ }
+
+ /* Stop if we're a zombie or need a soft hangup */
+ if (ast_test_flag(chan, AST_FLAG_ZOMBIE) || ast_check_hangup(chan)) {
+ ast_channel_unlock(chan);
+ return -1;
+ }
+
+ switch (chan->_state) {
+ case AST_STATE_RINGING:
+ case AST_STATE_RING:
+ if (chan->tech->answer)
+ res = chan->tech->answer(chan);
+ ast_setstate(chan, AST_STATE_UP);
+ ast_cdr_answer(chan->cdr);
+ ast_channel_unlock(chan);
+ if (delay)
+ ast_safe_sleep(chan, delay);
+ return res;
+ break;
+ case AST_STATE_UP:
+ ast_cdr_answer(chan->cdr);
+ break;
+ default:
+ break;
+ }
+ chan->visible_indication = 0;
+ ast_channel_unlock(chan);
+
+ return res;
+}
+
+int ast_answer(struct ast_channel *chan)
+{
+ return __ast_answer(chan, 500);
+}
+
+void ast_deactivate_generator(struct ast_channel *chan)
+{
+ ast_channel_lock(chan);
+ if (chan->generatordata) {
+ if (chan->generator && chan->generator->release)
+ chan->generator->release(chan, chan->generatordata);
+ chan->generatordata = NULL;
+ chan->generator = NULL;
+ ast_channel_set_fd(chan, AST_GENERATOR_FD, -1);
+ ast_clear_flag(chan, AST_FLAG_WRITE_INT);
+ ast_settimeout(chan, 0, NULL, NULL);
+ }
+ ast_channel_unlock(chan);
+}
+
+static int generator_force(const void *data)
+{
+ /* Called if generator doesn't have data */
+ void *tmp;
+ int res;
+ int (*generate)(struct ast_channel *chan, void *tmp, int datalen, int samples);
+ struct ast_channel *chan = (struct ast_channel *)data;
+ tmp = chan->generatordata;
+ chan->generatordata = NULL;
+ generate = chan->generator->generate;
+ res = generate(chan, tmp, 0, 160);
+ chan->generatordata = tmp;
+ if (res) {
+ ast_debug(1, "Auto-deactivating generator\n");
+ ast_deactivate_generator(chan);
+ }
+ return 0;
+}
+
+int ast_activate_generator(struct ast_channel *chan, struct ast_generator *gen, void *params)
+{
+ int res = 0;
+
+ ast_channel_lock(chan);
+
+ if (chan->generatordata) {
+ if (chan->generator && chan->generator->release)
+ chan->generator->release(chan, chan->generatordata);
+ chan->generatordata = NULL;
+ }
+
+ ast_prod(chan);
+ if (gen->alloc && !(chan->generatordata = gen->alloc(chan, params))) {
+ res = -1;
+ }
+
+ if (!res) {
+ ast_settimeout(chan, 160, generator_force, chan);
+ chan->generator = gen;
+ }
+
+ ast_channel_unlock(chan);
+
+ return res;
+}
+
+/*! \brief Wait for x amount of time on a file descriptor to have input. */
+int ast_waitfor_n_fd(int *fds, int n, int *ms, int *exception)
+{
+ int winner = -1;
+ ast_waitfor_nandfds(NULL, 0, fds, n, exception, &winner, ms);
+ return winner;
+}
+
+/*! \brief Wait for x amount of time on a file descriptor to have input. */
+#ifdef HAVE_EPOLL
+static struct ast_channel *ast_waitfor_nandfds_classic(struct ast_channel **c, int n, int *fds, int nfds,
+ int *exception, int *outfd, int *ms)
+#else
+struct ast_channel *ast_waitfor_nandfds(struct ast_channel **c, int n, int *fds, int nfds,
+ int *exception, int *outfd, int *ms)
+#endif
+{
+ struct timeval start = { 0 , 0 };
+ struct pollfd *pfds;
+ int res;
+ long rms;
+ int x, y, max;
+ int sz;
+ time_t now = 0;
+ long whentohangup = 0, diff;
+ struct ast_channel *winner = NULL;
+ struct fdmap {
+ int chan;
+ int fdno;
+ } *fdmap;
+
+ sz = n * AST_MAX_FDS + nfds;
+ pfds = alloca(sizeof(*pfds) * sz);
+ fdmap = alloca(sizeof(*fdmap) * sz);
+
+ if (outfd)
+ *outfd = -99999;
+ if (exception)
+ *exception = 0;
+
+ /* Perform any pending masquerades */
+ for (x = 0; x < n; x++) {
+ ast_channel_lock(c[x]);
+ if (c[x]->masq && ast_do_masquerade(c[x])) {
+ ast_log(LOG_WARNING, "Masquerade failed\n");
+ *ms = -1;
+ ast_channel_unlock(c[x]);
+ return NULL;
+ }
+ if (c[x]->whentohangup) {
+ if (!whentohangup)
+ time(&now);
+ diff = c[x]->whentohangup - now;
+ if (diff < 1) {
+ /* Should already be hungup */
+ c[x]->_softhangup |= AST_SOFTHANGUP_TIMEOUT;
+ ast_channel_unlock(c[x]);
+ return c[x];
+ }
+ if (!whentohangup || (diff < whentohangup))
+ whentohangup = diff;
+ }
+ ast_channel_unlock(c[x]);
+ }
+ /* Wait full interval */
+ rms = *ms;
+ if (whentohangup) {
+ rms = whentohangup * 1000; /* timeout in milliseconds */
+ if (*ms >= 0 && *ms < rms) /* original *ms still smaller */
+ rms = *ms;
+ }
+ /*
+ * Build the pollfd array, putting the channels' fds first,
+ * followed by individual fds. Order is important because
+ * individual fd's must have priority over channel fds.
+ */
+ max = 0;
+ for (x = 0; x < n; x++) {
+ for (y = 0; y < AST_MAX_FDS; y++) {
+ fdmap[max].fdno = y; /* fd y is linked to this pfds */
+ fdmap[max].chan = x; /* channel x is linked to this pfds */
+ max += ast_add_fd(&pfds[max], c[x]->fds[y]);
+ }
+ CHECK_BLOCKING(c[x]);
+ }
+ /* Add the individual fds */
+ for (x = 0; x < nfds; x++) {
+ fdmap[max].chan = -1;
+ max += ast_add_fd(&pfds[max], fds[x]);
+ }
+
+ if (*ms > 0)
+ start = ast_tvnow();
+
+ if (sizeof(int) == 4) { /* XXX fix timeout > 600000 on linux x86-32 */
+ do {
+ int kbrms = rms;
+ if (kbrms > 600000)
+ kbrms = 600000;
+ res = poll(pfds, max, kbrms);
+ if (!res)
+ rms -= kbrms;
+ } while (!res && (rms > 0));
+ } else {
+ res = poll(pfds, max, rms);
+ }
+ for (x = 0; x < n; x++)
+ ast_clear_flag(c[x], AST_FLAG_BLOCKING);
+ if (res < 0) { /* Simulate a timeout if we were interrupted */
+ if (errno != EINTR)
+ *ms = -1;
+ return NULL;
+ }
+ if (whentohangup) { /* if we have a timeout, check who expired */
+ time(&now);
+ for (x = 0; x < n; x++) {
+ if (c[x]->whentohangup && now >= c[x]->whentohangup) {
+ c[x]->_softhangup |= AST_SOFTHANGUP_TIMEOUT;
+ if (winner == NULL)
+ winner = c[x];
+ }
+ }
+ }
+ if (res == 0) { /* no fd ready, reset timeout and done */
+ *ms = 0; /* XXX use 0 since we may not have an exact timeout. */
+ return winner;
+ }
+ /*
+ * Then check if any channel or fd has a pending event.
+ * Remember to check channels first and fds last, as they
+ * must have priority on setting 'winner'
+ */
+ for (x = 0; x < max; x++) {
+ res = pfds[x].revents;
+ if (res == 0)
+ continue;
+ if (fdmap[x].chan >= 0) { /* this is a channel */
+ winner = c[fdmap[x].chan]; /* override previous winners */
+ if (res & POLLPRI)
+ ast_set_flag(winner, AST_FLAG_EXCEPTION);
+ else
+ ast_clear_flag(winner, AST_FLAG_EXCEPTION);
+ winner->fdno = fdmap[x].fdno;
+ } else { /* this is an fd */
+ if (outfd)
+ *outfd = pfds[x].fd;
+ if (exception)
+ *exception = (res & POLLPRI) ? -1 : 0;
+ winner = NULL;
+ }
+ }
+ if (*ms > 0) {
+ *ms -= ast_tvdiff_ms(ast_tvnow(), start);
+ if (*ms < 0)
+ *ms = 0;
+ }
+ return winner;
+}
+
+#ifdef HAVE_EPOLL
+static struct ast_channel *ast_waitfor_nandfds_simple(struct ast_channel *chan, int *ms)
+{
+ struct timeval start = { 0 , 0 };
+ int res = 0;
+ struct epoll_event ev[1];
+ long whentohangup = 0, rms = *ms;
+ time_t now;
+ struct ast_channel *winner = NULL;
+ struct ast_epoll_data *aed = NULL;
+
+ ast_channel_lock(chan);
+
+ /* See if this channel needs to be masqueraded */
+ if (chan->masq && ast_do_masquerade(chan)) {
+ ast_log(LOG_WARNING, "Failed to perform masquerade on %s\n", chan->name);
+ *ms = -1;
+ ast_channel_unlock(chan);
+ return NULL;
+ }
+
+ /* Figure out their timeout */
+ if (chan->whentohangup) {
+ time(&now);
+ if ((whentohangup = chan->whentohangup - now) < 1) {
+ /* They should already be hungup! */
+ chan->_softhangup |= AST_SOFTHANGUP_TIMEOUT;
+ ast_channel_unlock(chan);
+ return NULL;
+ }
+ /* If this value is smaller then the current one... make it priority */
+ whentohangup *= 1000;
+ if (rms > whentohangup)
+ rms = whentohangup;
+ }
+
+ ast_channel_unlock(chan);
+
+ /* Time to make this channel block... */
+ CHECK_BLOCKING(chan);
+
+ if (*ms > 0)
+ start = ast_tvnow();
+
+ /* We don't have to add any file descriptors... they are already added, we just have to wait! */
+ res = epoll_wait(chan->epfd, ev, 1, rms);
+
+ /* Stop blocking */
+ ast_clear_flag(chan, AST_FLAG_BLOCKING);
+
+ /* Simulate a timeout if we were interrupted */
+ if (res < 0) {
+ if (errno != EINTR)
+ *ms = -1;
+ return NULL;
+ }
+
+ /* If this channel has a timeout see if it expired */
+ if (chan->whentohangup) {
+ time(&now);
+ if (now >= chan->whentohangup) {
+ chan->_softhangup |= AST_SOFTHANGUP_TIMEOUT;
+ winner = chan;
+ }
+ }
+
+ /* No fd ready, reset timeout and be done for now */
+ if (!res) {
+ *ms = 0;
+ return winner;
+ }
+
+ /* See what events are pending */
+ aed = ev[0].data.ptr;
+ chan->fdno = aed->which;
+ if (ev[0].events & EPOLLPRI)
+ ast_set_flag(chan, AST_FLAG_EXCEPTION);
+ else
+ ast_clear_flag(chan, AST_FLAG_EXCEPTION);
+
+ if (*ms > 0) {
+ *ms -= ast_tvdiff_ms(ast_tvnow(), start);
+ if (*ms < 0)
+ *ms = 0;
+ }
+
+ return chan;
+}
+
+static struct ast_channel *ast_waitfor_nandfds_complex(struct ast_channel **c, int n, int *ms)
+{
+ struct timeval start = { 0 , 0 };
+ int res = 0, i;
+ struct epoll_event ev[25] = { { 0, } };
+ long whentohangup = 0, diff, rms = *ms;
+ time_t now;
+ struct ast_channel *winner = NULL;
+
+ for (i = 0; i < n; i++) {
+ ast_channel_lock(c[i]);
+ if (c[i]->masq && ast_do_masquerade(c[i])) {
+ ast_log(LOG_WARNING, "Masquerade failed\n");
+ *ms = -1;
+ ast_channel_unlock(c[i]);
+ return NULL;
+ }
+ if (c[i]->whentohangup) {
+ if (!whentohangup)
+ time(&now);
+ if ((diff = c[i]->whentohangup - now) < 1) {
+ c[i]->_softhangup |= AST_SOFTHANGUP_TIMEOUT;
+ ast_channel_unlock(c[i]);
+ return c[i];
+ }
+ if (!whentohangup || (diff < whentohangup))
+ whentohangup = diff;
+ }
+ ast_channel_unlock(c[i]);
+ CHECK_BLOCKING(c[i]);
+ }
+
+ rms = *ms;
+ if (whentohangup) {
+ rms = whentohangup * 1000;
+ if (*ms >= 0 && *ms < rms)
+ rms = *ms;
+ }
+
+ if (*ms > 0)
+ start = ast_tvnow();
+
+ res = epoll_wait(c[0]->epfd, ev, 25, rms);
+
+ for (i = 0; i < n; i++)
+ ast_clear_flag(c[i], AST_FLAG_BLOCKING);
+
+ if (res < 0) {
+ if (errno != EINTR)
+ *ms = -1;
+ return NULL;
+ }
+
+ if (whentohangup) {
+ time(&now);
+ for (i = 0; i < n; i++) {
+ if (c[i]->whentohangup && now >= c[i]->whentohangup) {
+ c[i]->_softhangup |= AST_SOFTHANGUP_TIMEOUT;
+ if (!winner)
+ winner = c[i];
+ }
+ }
+ }
+
+ if (!res) {
+ *ms = 0;
+ return winner;
+ }
+
+ for (i = 0; i < res; i++) {
+ struct ast_epoll_data *aed = ev[i].data.ptr;
+
+ if (!ev[i].events || !aed)
+ continue;
+
+ winner = aed->chan;
+ if (ev[i].events & EPOLLPRI)
+ ast_set_flag(winner, AST_FLAG_EXCEPTION);
+ else
+ ast_clear_flag(winner, AST_FLAG_EXCEPTION);
+ winner->fdno = aed->which;
+ }
+
+ if (*ms > 0) {
+ *ms -= ast_tvdiff_ms(ast_tvnow(), start);
+ if (*ms < 0)
+ *ms = 0;
+ }
+
+ return winner;
+}
+
+struct ast_channel *ast_waitfor_nandfds(struct ast_channel **c, int n, int *fds, int nfds,
+ int *exception, int *outfd, int *ms)
+{
+ /* Clear all provided values in one place. */
+ if (outfd)
+ *outfd = -99999;
+ if (exception)
+ *exception = 0;
+
+ /* If no epoll file descriptor is available resort to classic nandfds */
+ if (!n || nfds || c[0]->epfd == -1)
+ return ast_waitfor_nandfds_classic(c, n, fds, nfds, exception, outfd, ms);
+ else if (!nfds && n == 1)
+ return ast_waitfor_nandfds_simple(c[0], ms);
+ else
+ return ast_waitfor_nandfds_complex(c, n, ms);
+}
+#endif
+
+struct ast_channel *ast_waitfor_n(struct ast_channel **c, int n, int *ms)
+{
+ return ast_waitfor_nandfds(c, n, NULL, 0, NULL, NULL, ms);
+}
+
+int ast_waitfor(struct ast_channel *c, int ms)
+{
+ int oldms = ms; /* -1 if no timeout */
+
+ ast_waitfor_nandfds(&c, 1, NULL, 0, NULL, NULL, &ms);
+ if ((ms < 0) && (oldms < 0))
+ ms = 0;
+ return ms;
+}
+
+/* XXX never to be called with ms = -1 */
+int ast_waitfordigit(struct ast_channel *c, int ms)
+{
+ return ast_waitfordigit_full(c, ms, -1, -1);
+}
+
+int ast_settimeout(struct ast_channel *c, int samples, int (*func)(const void *data), void *data)
+{
+ int res = -1;
+#ifdef HAVE_ZAPTEL
+ if (c->timingfd > -1) {
+ if (!func) {
+ samples = 0;
+ data = 0;
+ }
+ ast_debug(1, "Scheduling timer at %d sample intervals\n", samples);
+ res = ioctl(c->timingfd, ZT_TIMERCONFIG, &samples);
+ c->timingfunc = func;
+ c->timingdata = data;
+ }
+#endif
+ return res;
+}
+
+int ast_waitfordigit_full(struct ast_channel *c, int ms, int audiofd, int cmdfd)
+{
+ /* Stop if we're a zombie or need a soft hangup */
+ if (ast_test_flag(c, AST_FLAG_ZOMBIE) || ast_check_hangup(c))
+ return -1;
+
+ /* Only look for the end of DTMF, don't bother with the beginning and don't emulate things */
+ ast_set_flag(c, AST_FLAG_END_DTMF_ONLY);
+
+ /* Wait for a digit, no more than ms milliseconds total. */
+
+ while (ms) {
+ struct ast_channel *rchan;
+ int outfd=-1;
+
+ errno = 0;
+ rchan = ast_waitfor_nandfds(&c, 1, &cmdfd, (cmdfd > -1) ? 1 : 0, NULL, &outfd, &ms);
+
+ if (!rchan && outfd < 0 && ms) {
+ if (errno == 0 || errno == EINTR)
+ continue;
+ ast_log(LOG_WARNING, "Wait failed (%s)\n", strerror(errno));
+ ast_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
+ return -1;
+ } else if (outfd > -1) {
+ /* The FD we were watching has something waiting */
+ ast_log(LOG_WARNING, "The FD we were waiting for has something waiting. Waitfordigit returning numeric 1\n");
+ ast_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
+ return 1;
+ } else if (rchan) {
+ int res;
+ struct ast_frame *f = ast_read(c);
+ if (!f)
+ return -1;
+
+ switch (f->frametype) {
+ case AST_FRAME_DTMF_BEGIN:
+ break;
+ case AST_FRAME_DTMF_END:
+ res = f->subclass;
+ ast_frfree(f);
+ ast_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
+ return res;
+ case AST_FRAME_CONTROL:
+ switch (f->subclass) {
+ case AST_CONTROL_HANGUP:
+ ast_frfree(f);
+ ast_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
+ return -1;
+ case AST_CONTROL_RINGING:
+ case AST_CONTROL_ANSWER:
+ /* Unimportant */
+ break;
+ default:
+ ast_log(LOG_WARNING, "Unexpected control subclass '%d'\n", f->subclass);
+ break;
+ }
+ break;
+ case AST_FRAME_VOICE:
+ /* Write audio if appropriate */
+ if (audiofd > -1)
+ write(audiofd, f->data, f->datalen);
+ default:
+ /* Ignore */
+ break;
+ }
+ ast_frfree(f);
+ }
+ }
+
+ ast_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
+
+ return 0; /* Time is up */
+}
+
+static void send_dtmf_event(const struct ast_channel *chan, const char *direction, const char digit, const char *begin, const char *end)
+{
+ manager_event(EVENT_FLAG_DTMF,
+ "DTMF",
+ "Channel: %s\r\n"
+ "Uniqueid: %s\r\n"
+ "Digit: %c\r\n"
+ "Direction: %s\r\n"
+ "Begin: %s\r\n"
+ "End: %s\r\n",
+ chan->name, chan->uniqueid, digit, direction, begin, end);
+}
+
+static void ast_read_generator_actions(struct ast_channel *chan, struct ast_frame *f)
+{
+ if (chan->generatordata && !ast_internal_timing_enabled(chan)) {
+ void *tmp = chan->generatordata;
+ int res;
+
+ if (chan->timingfunc) {
+ if (option_debug > 1)
+ ast_log(LOG_DEBUG, "Generator got voice, switching to phase locked mode\n");
+ ast_settimeout(chan, 0, NULL, NULL);
+ }
+
+ chan->generatordata = NULL; /* reset, to let writes go through */
+ res = chan->generator->generate(chan, tmp, f->datalen, f->samples);
+ chan->generatordata = tmp;
+ if (res) {
+ if (option_debug > 1)
+ ast_log(LOG_DEBUG, "Auto-deactivating generator\n");
+ ast_deactivate_generator(chan);
+ }
+
+ } else if (f->frametype == AST_FRAME_CNG) {
+ if (chan->generator && !chan->timingfunc && (chan->timingfd > -1)) {
+ if (option_debug > 1)
+ ast_log(LOG_DEBUG, "Generator got CNG, switching to timed mode\n");
+ ast_settimeout(chan, 160, generator_force, chan);
+ }
+ }
+}
+
+static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
+{
+ struct ast_frame *f = NULL; /* the return value */
+ int blah;
+ int prestate;
+ int count = 0;
+
+ /* this function is very long so make sure there is only one return
+ * point at the end (there are only two exceptions to this).
+ */
+ while(ast_channel_trylock(chan)) {
+ if(count++ > 10)
+ /*cannot goto done since the channel is not locked*/
+ return &ast_null_frame;
+ usleep(1);
+ }
+
+ if (chan->masq) {
+ if (ast_do_masquerade(chan))
+ ast_log(LOG_WARNING, "Failed to perform masquerade\n");
+ else
+ f = &ast_null_frame;
+ goto done;
+ }
+
+ /* Stop if we're a zombie or need a soft hangup */
+ if (ast_test_flag(chan, AST_FLAG_ZOMBIE) || ast_check_hangup(chan)) {
+ if (chan->generator)
+ ast_deactivate_generator(chan);
+ goto done;
+ }
+ prestate = chan->_state;
+
+ if (!ast_test_flag(chan, AST_FLAG_DEFER_DTMF | AST_FLAG_EMULATE_DTMF | AST_FLAG_IN_DTMF) &&
+ !ast_strlen_zero(chan->dtmfq) &&
+ (ast_tvzero(chan->dtmf_tv) || ast_tvdiff_ms(ast_tvnow(), chan->dtmf_tv) > AST_MIN_DTMF_GAP) ) {
+ /* We have DTMF that has been deferred. Return it now */
+ chan->dtmff.subclass = chan->dtmfq[0];
+ /* Drop first digit from the buffer */
+ memmove(chan->dtmfq, chan->dtmfq + 1, sizeof(chan->dtmfq) - 1);
+ f = &chan->dtmff;
+ if (ast_test_flag(chan, AST_FLAG_END_DTMF_ONLY)) {
+ ast_log(LOG_DTMF, "DTMF end emulation of '%c' queued on %s\n", f->subclass, chan->name);
+ chan->dtmff.frametype = AST_FRAME_DTMF_END;
+ } else {
+ ast_log(LOG_DTMF, "DTMF begin emulation of '%c' with duration %d queued on %s\n", f->subclass, AST_DEFAULT_EMULATE_DTMF_DURATION, chan->name);
+ chan->dtmff.frametype = AST_FRAME_DTMF_BEGIN;
+ ast_set_flag(chan, AST_FLAG_EMULATE_DTMF);
+ chan->emulate_dtmf_digit = f->subclass;
+ chan->emulate_dtmf_duration = AST_DEFAULT_EMULATE_DTMF_DURATION;
+ }
+ chan->dtmf_tv = ast_tvnow();
+ goto done;
+ }
+
+ /* Read and ignore anything on the alertpipe, but read only
+ one sizeof(blah) per frame that we send from it */
+ if (chan->alertpipe[0] > -1)
+ read(chan->alertpipe[0], &blah, sizeof(blah));
+
+#ifdef HAVE_ZAPTEL
+ if (chan->timingfd > -1 && chan->fdno == AST_TIMING_FD && ast_test_flag(chan, AST_FLAG_EXCEPTION)) {
+ int res;
+
+ ast_clear_flag(chan, AST_FLAG_EXCEPTION);
+ blah = -1;
+ /* IF we can't get event, assume it's an expired as-per the old interface */
+ res = ioctl(chan->timingfd, ZT_GETEVENT, &blah);
+ if (res)
+ blah = ZT_EVENT_TIMER_EXPIRED;
+
+ if (blah == ZT_EVENT_TIMER_PING) {
+ if (AST_LIST_EMPTY(&chan->readq) || !AST_LIST_NEXT(AST_LIST_FIRST(&chan->readq), frame_list)) {
+ /* Acknowledge PONG unless we need it again */
+ if (ioctl(chan->timingfd, ZT_TIMERPONG, &blah)) {
+ ast_log(LOG_WARNING, "Failed to pong timer on '%s': %s\n", chan->name, strerror(errno));
+ }
+ }
+ } else if (blah == ZT_EVENT_TIMER_EXPIRED) {
+ ioctl(chan->timingfd, ZT_TIMERACK, &blah);
+ if (chan->timingfunc) {
+ chan->timingfunc(chan->timingdata);
+ } else {
+ blah = 0;
+ ioctl(chan->timingfd, ZT_TIMERCONFIG, &blah);
+ chan->timingdata = NULL;
+ }
+ ast_channel_unlock(chan);
+ /* cannot 'goto done' because the channel is already unlocked */
+ return &ast_null_frame;
+ } else
+ ast_log(LOG_NOTICE, "No/unknown event '%d' on timer for '%s'?\n", blah, chan->name);
+ } else
+#endif
+ if (chan->fds[AST_GENERATOR_FD] > -1 && chan->fdno == AST_GENERATOR_FD) {
+ /* if the AST_GENERATOR_FD is set, call the generator with args
+ * set to -1 so it can do whatever it needs to.
+ */
+ void *tmp = chan->generatordata;
+ chan->generatordata = NULL; /* reset to let ast_write get through */
+ chan->generator->generate(chan, tmp, -1, -1);
+ chan->generatordata = tmp;
+ f = &ast_null_frame;
+ goto done;
+ }
+
+ /* Check for pending read queue */
+ if (!AST_LIST_EMPTY(&chan->readq)) {
+ f = AST_LIST_REMOVE_HEAD(&chan->readq, frame_list);
+ /* Interpret hangup and return NULL */
+ /* XXX why not the same for frames from the channel ? */
+ if (f->frametype == AST_FRAME_CONTROL && f->subclass == AST_CONTROL_HANGUP) {
+ ast_frfree(f);
+ f = NULL;
+ }
+ } else {
+ chan->blocker = pthread_self();
+ if (ast_test_flag(chan, AST_FLAG_EXCEPTION)) {
+ if (chan->tech->exception)
+ f = chan->tech->exception(chan);
+ else {
+ ast_log(LOG_WARNING, "Exception flag set on '%s', but no exception handler\n", chan->name);
+ f = &ast_null_frame;
+ }
+ /* Clear the exception flag */
+ ast_clear_flag(chan, AST_FLAG_EXCEPTION);
+ } else if (chan->tech->read)
+ f = chan->tech->read(chan);
+ else
+ ast_log(LOG_WARNING, "No read routine on channel %s\n", chan->name);
+ }
+
+ if (f) {
+ /* if the channel driver returned more than one frame, stuff the excess
+ into the readq for the next ast_read call (note that we can safely assume
+ that the readq is empty, because otherwise we would not have called into
+ the channel driver and f would be only a single frame)
+ */
+ if (AST_LIST_NEXT(f, frame_list)) {
+ AST_LIST_HEAD_SET_NOLOCK(&chan->readq, AST_LIST_NEXT(f, frame_list));
+ AST_LIST_NEXT(f, frame_list) = NULL;
+ }
+
+ switch (f->frametype) {
+ case AST_FRAME_CONTROL:
+ if (f->subclass == AST_CONTROL_ANSWER) {
+ if (!ast_test_flag(chan, AST_FLAG_OUTGOING)) {
+ ast_debug(1, "Ignoring answer on an inbound call!\n");
+ ast_frfree(f);
+ f = &ast_null_frame;
+ } else if (prestate == AST_STATE_UP) {
+ ast_debug(1, "Dropping duplicate answer!\n");
+ ast_frfree(f);
+ f = &ast_null_frame;
+ } else {
+ /* Answer the CDR */
+ ast_setstate(chan, AST_STATE_UP);
+ if (!chan->cdr) { /* up till now, this insertion hasn't been done. Therefore,
+ to keep from throwing off the basic order of the universe,
+ we will try to keep this cdr from getting posted. */
+ chan->cdr = ast_cdr_alloc();
+ ast_cdr_init(chan->cdr, chan);
+ ast_cdr_start(chan->cdr);
+ }
+
+ ast_cdr_answer(chan->cdr);
+ }
+ }
+ break;
+ case AST_FRAME_DTMF_END:
+ send_dtmf_event(chan, "Received", f->subclass, "No", "Yes");
+ ast_log(LOG_DTMF, "DTMF end '%c' received on %s, duration %ld ms\n", f->subclass, chan->name, f->len);
+ /* Queue it up if DTMF is deffered, or if DTMF emulation is forced.
+ * However, only let emulation be forced if the other end cares about BEGIN frames */
+ if ( ast_test_flag(chan, AST_FLAG_DEFER_DTMF) ||
+ (ast_test_flag(chan, AST_FLAG_EMULATE_DTMF) && !ast_test_flag(chan, AST_FLAG_END_DTMF_ONLY)) ) {
+ if (strlen(chan->dtmfq) < sizeof(chan->dtmfq) - 2) {
+ ast_log(LOG_DTMF, "DTMF end '%c' put into dtmf queue on %s\n", f->subclass, chan->name);
+ chan->dtmfq[strlen(chan->dtmfq)] = f->subclass;
+ } else
+ ast_log(LOG_WARNING, "Dropping deferred DTMF digits on %s\n", chan->name);
+ ast_frfree(f);
+ f = &ast_null_frame;
+ } else if (!ast_test_flag(chan, AST_FLAG_IN_DTMF | AST_FLAG_END_DTMF_ONLY)) {
+ if (!ast_tvzero(chan->dtmf_tv) &&
+ ast_tvdiff_ms(ast_tvnow(), chan->dtmf_tv) < AST_MIN_DTMF_GAP) {
+ /* If it hasn't been long enough, defer this digit */
+ if (strlen(chan->dtmfq) < sizeof(chan->dtmfq) - 2) {
+ ast_log(LOG_DTMF, "DTMF end '%c' put into dtmf queue on %s\n", f->subclass, chan->name);
+ chan->dtmfq[strlen(chan->dtmfq)] = f->subclass;
+ } else
+ ast_log(LOG_WARNING, "Dropping deferred DTMF digits on %s\n", chan->name);
+ ast_frfree(f);
+ f = &ast_null_frame;
+ } else {
+ /* There was no begin, turn this into a begin and send the end later */
+ f->frametype = AST_FRAME_DTMF_BEGIN;
+ ast_set_flag(chan, AST_FLAG_EMULATE_DTMF);
+ chan->emulate_dtmf_digit = f->subclass;
+ chan->dtmf_tv = ast_tvnow();
+ if (f->len) {
+ if (f->len > AST_MIN_DTMF_DURATION)
+ chan->emulate_dtmf_duration = f->len;
+ else
+ chan->emulate_dtmf_duration = AST_MIN_DTMF_DURATION;
+ } else
+ chan->emulate_dtmf_duration = AST_DEFAULT_EMULATE_DTMF_DURATION;
+ ast_log(LOG_DTMF, "DTMF begin emulation of '%c' with duration %u queued on %s\n", f->subclass, chan->emulate_dtmf_duration, chan->name);
+ }
+ if (chan->audiohooks) {
+ struct ast_frame *old_frame = f;
+ /*!
+ * \todo XXX It is possible to write a digit to the audiohook twice
+ * if the digit was originally read while the channel was in autoservice. */
+ f = ast_audiohook_write_list(chan, chan->audiohooks, AST_AUDIOHOOK_DIRECTION_READ, f);
+ if (old_frame != f)
+ ast_frfree(old_frame);
+ }
+ } else {
+ struct timeval now = ast_tvnow();
+ if (ast_test_flag(chan, AST_FLAG_IN_DTMF)) {
+ ast_log(LOG_DTMF, "DTMF end accepted with begin '%c' on %s\n", f->subclass, chan->name);
+ ast_clear_flag(chan, AST_FLAG_IN_DTMF);
+ if (!f->len)
+ f->len = ast_tvdiff_ms(now, chan->dtmf_tv);
+ } else if (!f->len) {
+ ast_log(LOG_DTMF, "DTMF end accepted without begin '%c' on %s\n", f->subclass, chan->name);
+ f->len = AST_MIN_DTMF_DURATION;
+ }
+ if (f->len < AST_MIN_DTMF_DURATION) {
+ ast_log(LOG_DTMF, "DTMF end '%c' has duration %ld but want minimum %d, emulating on %s\n", f->subclass, f->len, AST_MIN_DTMF_DURATION, chan->name);
+ ast_set_flag(chan, AST_FLAG_EMULATE_DTMF);
+ chan->emulate_dtmf_digit = f->subclass;
+ chan->emulate_dtmf_duration = AST_MIN_DTMF_DURATION - f->len;
+ ast_frfree(f);
+ f = &ast_null_frame;
+ } else {
+ ast_log(LOG_DTMF, "DTMF end passthrough '%c' on %s\n", f->subclass, chan->name);
+ chan->dtmf_tv = now;
+ }
+ if (chan->audiohooks) {
+ struct ast_frame *old_frame = f;
+ f = ast_audiohook_write_list(chan, chan->audiohooks, AST_AUDIOHOOK_DIRECTION_READ, f);
+ if (old_frame != f)
+ ast_frfree(old_frame);
+ }
+ }
+ break;
+ case AST_FRAME_DTMF_BEGIN:
+ send_dtmf_event(chan, "Received", f->subclass, "Yes", "No");
+ ast_log(LOG_DTMF, "DTMF begin '%c' received on %s\n", f->subclass, chan->name);
+ if ( ast_test_flag(chan, AST_FLAG_DEFER_DTMF | AST_FLAG_END_DTMF_ONLY) ||
+ (!ast_tvzero(chan->dtmf_tv) &&
+ ast_tvdiff_ms(ast_tvnow(), chan->dtmf_tv) < AST_MIN_DTMF_GAP) ) {
+ ast_log(LOG_DTMF, "DTMF begin ignored '%c' on %s\n", f->subclass, chan->name);
+ ast_frfree(f);
+ f = &ast_null_frame;
+ } else {
+ ast_set_flag(chan, AST_FLAG_IN_DTMF);
+ chan->dtmf_tv = ast_tvnow();
+ ast_log(LOG_DTMF, "DTMF begin passthrough '%c' on %s\n", f->subclass, chan->name);
+ }
+ break;
+ case AST_FRAME_NULL:
+ if (ast_test_flag(chan, AST_FLAG_EMULATE_DTMF)) {
+ struct timeval now = ast_tvnow();
+ if (ast_tvdiff_ms(now, chan->dtmf_tv) >= chan->emulate_dtmf_duration) {
+ chan->emulate_dtmf_duration = 0;
+ ast_frfree(f);
+ f = &chan->dtmff;
+ f->frametype = AST_FRAME_DTMF_END;
+ f->subclass = chan->emulate_dtmf_digit;
+ f->len = ast_tvdiff_ms(now, chan->dtmf_tv);
+ chan->dtmf_tv = now;
+ ast_clear_flag(chan, AST_FLAG_EMULATE_DTMF);
+ chan->emulate_dtmf_digit = 0;
+ ast_log(LOG_DTMF, "DTMF end emulation of '%c' queued on %s\n", f->subclass, chan->name);
+ }
+ }
+ break;
+ case AST_FRAME_VOICE:
+ /* The EMULATE_DTMF flag must be cleared here as opposed to when the duration
+ * is reached , because we want to make sure we pass at least one
+ * voice frame through before starting the next digit, to ensure a gap
+ * between DTMF digits. */
+ if (ast_test_flag(chan, AST_FLAG_EMULATE_DTMF) && !chan->emulate_dtmf_duration) {
+ ast_clear_flag(chan, AST_FLAG_EMULATE_DTMF);
+ chan->emulate_dtmf_digit = 0;
+ }
+
+ if (dropaudio || ast_test_flag(chan, AST_FLAG_IN_DTMF)) {
+ if (dropaudio)
+ ast_read_generator_actions(chan, f);
+ ast_frfree(f);
+ f = &ast_null_frame;
+ }
+
+ if (ast_test_flag(chan, AST_FLAG_EMULATE_DTMF) && !ast_test_flag(chan, AST_FLAG_IN_DTMF)) {
+ struct timeval now = ast_tvnow();
+ if (ast_tvdiff_ms(now, chan->dtmf_tv) >= chan->emulate_dtmf_duration) {
+ chan->emulate_dtmf_duration = 0;
+ ast_frfree(f);
+ f = &chan->dtmff;
+ f->frametype = AST_FRAME_DTMF_END;
+ f->subclass = chan->emulate_dtmf_digit;
+ f->len = ast_tvdiff_ms(now, chan->dtmf_tv);
+ chan->dtmf_tv = now;
+ if (chan->audiohooks) {
+ struct ast_frame *old_frame = f;
+ f = ast_audiohook_write_list(chan, chan->audiohooks, AST_AUDIOHOOK_DIRECTION_READ, f);
+ if (old_frame != f)
+ ast_frfree(old_frame);
+ }
+ ast_log(LOG_DTMF, "DTMF end emulation of '%c' queued on %s\n", f->subclass, chan->name);
+ } else {
+ /* Drop voice frames while we're still in the middle of the digit */
+ ast_frfree(f);
+ f = &ast_null_frame;
+ }
+ } else if ((f->frametype == AST_FRAME_VOICE) && !(f->subclass & chan->nativeformats)) {
+ /* This frame can't be from the current native formats -- drop it on the
+ floor */
+ ast_log(LOG_NOTICE, "Dropping incompatible voice frame on %s of format %s since our native format has changed to %s\n",
+ chan->name, ast_getformatname(f->subclass), ast_getformatname(chan->nativeformats));
+ ast_frfree(f);
+ f = &ast_null_frame;
+ } else if ((f->frametype == AST_FRAME_VOICE)) {
+ /* Send frame to audiohooks if present */
+ if (chan->audiohooks) {
+ struct ast_frame *old_frame = f;
+ f = ast_audiohook_write_list(chan, chan->audiohooks, AST_AUDIOHOOK_DIRECTION_READ, f);
+ if (old_frame != f)
+ ast_frfree(old_frame);
+ }
+ if (chan->monitor && chan->monitor->read_stream ) {
+ /* XXX what does this do ? */
+#ifndef MONITOR_CONSTANT_DELAY
+ int jump = chan->outsmpl - chan->insmpl - 4 * f->samples;
+ if (jump >= 0) {
+ jump = chan->outsmpl - chan->insmpl;
+ if (ast_seekstream(chan->monitor->read_stream, jump, SEEK_FORCECUR) == -1)
+ ast_log(LOG_WARNING, "Failed to perform seek in monitoring read stream, synchronization between the files may be broken\n");
+ chan->insmpl += jump + f->samples;
+ } else
+ chan->insmpl+= f->samples;
+#else
+ int jump = chan->outsmpl - chan->insmpl;
+ if (jump - MONITOR_DELAY >= 0) {
+ if (ast_seekstream(chan->monitor->read_stream, jump - f->samples, SEEK_FORCECUR) == -1)
+ ast_log(LOG_WARNING, "Failed to perform seek in monitoring read stream, synchronization between the files may be broken\n");
+ chan->insmpl += jump;
+ } else
+ chan->insmpl += f->samples;
+#endif
+ if (chan->monitor->state == AST_MONITOR_RUNNING) {
+ if (ast_writestream(chan->monitor->read_stream, f) < 0)
+ ast_log(LOG_WARNING, "Failed to write data to channel monitor read stream\n");
+ }
+ }
+
+ if (chan->readtrans && (f = ast_translate(chan->readtrans, f, 1)) == NULL)
+ f = &ast_null_frame;
+ else
+ /* Run generator sitting on the line if timing device not available
+ * and synchronous generation of outgoing frames is necessary */
+ ast_read_generator_actions(chan, f);
+ }
+ default:
+ /* Just pass it on! */
+ break;
+ }
+ } else {
+ /* Make sure we always return NULL in the future */
+ chan->_softhangup |= AST_SOFTHANGUP_DEV;
+ if (chan->generator)
+ ast_deactivate_generator(chan);
+ /* End the CDR if appropriate */
+ if (chan->cdr)
+ ast_cdr_end(chan->cdr);
+ }
+
+ /* High bit prints debugging */
+ if (chan->fin & DEBUGCHAN_FLAG)
+ ast_frame_dump(chan->name, f, "<<");
+ chan->fin = FRAMECOUNT_INC(chan->fin);
+
+done:
+ if (chan->music_state && chan->generator && chan->generator->digit && f && f->frametype == AST_FRAME_DTMF_END)
+ chan->generator->digit(chan, f->subclass);
+
+ ast_channel_unlock(chan);
+ return f;
+}
+
+int ast_internal_timing_enabled(struct ast_channel *chan)
+{
+ int ret = ast_opt_internal_timing && chan->timingfd > -1;
+ ast_debug(5, "Internal timing is %s (option_internal_timing=%d chan->timingfd=%d)\n", ret? "enabled": "disabled", ast_opt_internal_timing, chan->timingfd);
+ return ret;
+}
+
+struct ast_frame *ast_read(struct ast_channel *chan)
+{
+ return __ast_read(chan, 0);
+}
+
+struct ast_frame *ast_read_noaudio(struct ast_channel *chan)
+{
+ return __ast_read(chan, 1);
+}
+
+int ast_indicate(struct ast_channel *chan, int condition)
+{
+ return ast_indicate_data(chan, condition, NULL, 0);
+}
+
+int ast_indicate_data(struct ast_channel *chan, int condition, const void *data, size_t datalen)
+{
+ int res = -1;
+
+ ast_channel_lock(chan);
+ /* Stop if we're a zombie or need a soft hangup */
+ if (ast_test_flag(chan, AST_FLAG_ZOMBIE) || ast_check_hangup(chan)) {
+ ast_channel_unlock(chan);
+ return -1;
+ }
+ if (chan->tech->indicate)
+ res = chan->tech->indicate(chan, condition, data, datalen);
+ ast_channel_unlock(chan);
+ if (!chan->tech->indicate || res) {
+ /*
+ * Device does not support (that) indication, lets fake
+ * it by doing our own tone generation. (PM2002)
+ */
+ if (condition < 0)
+ ast_playtones_stop(chan);
+ else {
+ const struct ind_tone_zone_sound *ts = NULL;
+ switch (condition) {
+ case AST_CONTROL_RINGING:
+ ts = ast_get_indication_tone(chan->zone, "ring");
+ break;
+ case AST_CONTROL_BUSY:
+ ts = ast_get_indication_tone(chan->zone, "busy");
+ break;
+ case AST_CONTROL_CONGESTION:
+ ts = ast_get_indication_tone(chan->zone, "congestion");
+ break;
+ }
+ if (ts && ts->data[0]) {
+ ast_debug(1, "Driver for channel '%s' does not support indication %d, emulating it\n", chan->name, condition);
+ ast_playtones_start(chan,0,ts->data, 1);
+ res = 0;
+ chan->visible_indication = condition;
+ } else if (condition == AST_CONTROL_PROGRESS) {
+ /* ast_playtones_stop(chan); */
+ } else if (condition == AST_CONTROL_PROCEEDING) {
+ /* Do nothing, really */
+ } else if (condition == AST_CONTROL_HOLD) {
+ /* Do nothing.... */
+ } else if (condition == AST_CONTROL_UNHOLD) {
+ /* Do nothing.... */
+ } else if (condition == AST_CONTROL_VIDUPDATE) {
+ /* Do nothing.... */
+ } else {
+ /* not handled */
+ ast_log(LOG_WARNING, "Unable to handle indication %d for '%s'\n", condition, chan->name);
+ res = -1;
+ }
+ }
+ } else
+ chan->visible_indication = condition;
+
+ return res;
+}
+
+int ast_recvchar(struct ast_channel *chan, int timeout)
+{
+ int c;
+ char *buf = ast_recvtext(chan, timeout);
+ if (buf == NULL)
+ return -1; /* error or timeout */
+ c = *(unsigned char *)buf;
+ ast_free(buf);
+ return c;
+}
+
+char *ast_recvtext(struct ast_channel *chan, int timeout)
+{
+ int res, done = 0;
+ char *buf = NULL;
+
+ while (!done) {
+ struct ast_frame *f;
+ if (ast_check_hangup(chan))
+ break;
+ res = ast_waitfor(chan, timeout);
+ if (res <= 0) /* timeout or error */
+ break;
+ timeout = res; /* update timeout */
+ f = ast_read(chan);
+ if (f == NULL)
+ break; /* no frame */
+ if (f->frametype == AST_FRAME_CONTROL && f->subclass == AST_CONTROL_HANGUP)
+ done = 1; /* force a break */
+ else if (f->frametype == AST_FRAME_TEXT) { /* what we want */
+ buf = ast_strndup((char *) f->data, f->datalen); /* dup and break */
+ done = 1;
+ }
+ ast_frfree(f);
+ }
+ return buf;
+}
+
+int ast_sendtext(struct ast_channel *chan, const char *text)
+{
+ int res = 0;
+ /* Stop if we're a zombie or need a soft hangup */
+ if (ast_test_flag(chan, AST_FLAG_ZOMBIE) || ast_check_hangup(chan))
+ return -1;
+ CHECK_BLOCKING(chan);
+ if (chan->tech->send_text)
+ res = chan->tech->send_text(chan, text);
+ ast_clear_flag(chan, AST_FLAG_BLOCKING);
+ return res;
+}
+
+int ast_senddigit_begin(struct ast_channel *chan, char digit)
+{
+ /* Device does not support DTMF tones, lets fake
+ * it by doing our own generation. */
+ static const char* dtmf_tones[] = {
+ "941+1336", /* 0 */
+ "697+1209", /* 1 */
+ "697+1336", /* 2 */
+ "697+1477", /* 3 */
+ "770+1209", /* 4 */
+ "770+1336", /* 5 */
+ "770+1477", /* 6 */
+ "852+1209", /* 7 */
+ "852+1336", /* 8 */
+ "852+1477", /* 9 */
+ "697+1633", /* A */
+ "770+1633", /* B */
+ "852+1633", /* C */
+ "941+1633", /* D */
+ "941+1209", /* * */
+ "941+1477" /* # */
+ };
+
+ if (!chan->tech->send_digit_begin)
+ return 0;
+
+ if (!chan->tech->send_digit_begin(chan, digit))
+ return 0;
+
+ if (digit >= '0' && digit <='9')
+ ast_playtones_start(chan, 0, dtmf_tones[digit-'0'], 0);
+ else if (digit >= 'A' && digit <= 'D')
+ ast_playtones_start(chan, 0, dtmf_tones[digit-'A'+10], 0);
+ else if (digit == '*')
+ ast_playtones_start(chan, 0, dtmf_tones[14], 0);
+ else if (digit == '#')
+ ast_playtones_start(chan, 0, dtmf_tones[15], 0);
+ else {
+ /* not handled */
+ ast_debug(1, "Unable to generate DTMF tone '%c' for '%s'\n", digit, chan->name);
+ }
+
+ return 0;
+}
+
+int ast_senddigit_end(struct ast_channel *chan, char digit, unsigned int duration)
+{
+ int res = -1;
+
+ if (chan->tech->send_digit_end)
+ res = chan->tech->send_digit_end(chan, digit, duration);
+
+ if (res && chan->generator)
+ ast_playtones_stop(chan);
+
+ return 0;
+}
+
+int ast_senddigit(struct ast_channel *chan, char digit, unsigned int duration)
+{
+ if (chan->tech->send_digit_begin) {
+ ast_senddigit_begin(chan, digit);
+ ast_safe_sleep(chan, (duration >= AST_DEFAULT_EMULATE_DTMF_DURATION ? duration : AST_DEFAULT_EMULATE_DTMF_DURATION));
+ }
+
+ return ast_senddigit_end(chan, digit, (duration >= AST_DEFAULT_EMULATE_DTMF_DURATION ? duration : AST_DEFAULT_EMULATE_DTMF_DURATION));
+}
+
+int ast_prod(struct ast_channel *chan)
+{
+ struct ast_frame a = { AST_FRAME_VOICE };
+ char nothing[128];
+
+ /* Send an empty audio frame to get things moving */
+ if (chan->_state != AST_STATE_UP) {
+ ast_debug(1, "Prodding channel '%s'\n", chan->name);
+ a.subclass = chan->rawwriteformat;
+ a.data = nothing + AST_FRIENDLY_OFFSET;
+ a.src = "ast_prod";
+ if (ast_write(chan, &a))
+ ast_log(LOG_WARNING, "Prodding channel '%s' failed\n", chan->name);
+ }
+ return 0;
+}
+
+int ast_write_video(struct ast_channel *chan, struct ast_frame *fr)
+{
+ int res;
+ if (!chan->tech->write_video)
+ return 0;
+ res = ast_write(chan, fr);
+ if (!res)
+ res = 1;
+ return res;
+}
+
+int ast_write(struct ast_channel *chan, struct ast_frame *fr)
+{
+ int res = -1;
+ struct ast_frame *f = NULL, *f2 = NULL;
+ int count = 0;
+
+ /*Deadlock avoidance*/
+ while(ast_channel_trylock(chan)) {
+ /*cannot goto done since the channel is not locked*/
+ if(count++ > 10) {
+ ast_debug(1, "Deadlock avoided for write to channel '%s'\n", chan->name);
+ return 0;
+ }
+ usleep(1);
+ }
+ /* Stop if we're a zombie or need a soft hangup */
+ if (ast_test_flag(chan, AST_FLAG_ZOMBIE) || ast_check_hangup(chan))
+ goto done;
+
+ /* Handle any pending masquerades */
+ if (chan->masq && ast_do_masquerade(chan)) {
+ ast_log(LOG_WARNING, "Failed to perform masquerade\n");
+ goto done;
+ }
+ if (chan->masqr) {
+ res = 0; /* XXX explain, why 0 ? */
+ goto done;
+ }
+ if (chan->generatordata) {
+ if (ast_test_flag(chan, AST_FLAG_WRITE_INT))
+ ast_deactivate_generator(chan);
+ else {
+ if (fr->frametype == AST_FRAME_DTMF_END) {
+ /* There is a generator running while we're in the middle of a digit.
+ * It's probably inband DTMF, so go ahead and pass it so it can
+ * stop the generator */
+ ast_clear_flag(chan, AST_FLAG_BLOCKING);
+ ast_channel_unlock(chan);
+ res = ast_senddigit_end(chan, fr->subclass, fr->len);
+ ast_channel_lock(chan);
+ CHECK_BLOCKING(chan);
+ } else if (fr->frametype == AST_FRAME_CONTROL && fr->subclass == AST_CONTROL_UNHOLD) {
+ /* This is a side case where Echo is basically being called and the person put themselves on hold and took themselves off hold */
+ res = (chan->tech->indicate == NULL) ? 0 :
+ chan->tech->indicate(chan, fr->subclass, fr->data, fr->datalen);
+ }
+ res = 0; /* XXX explain, why 0 ? */
+ goto done;
+ }
+ }
+ /* High bit prints debugging */
+ if (chan->fout & DEBUGCHAN_FLAG)
+ ast_frame_dump(chan->name, fr, ">>");
+ CHECK_BLOCKING(chan);
+ switch (fr->frametype) {
+ case AST_FRAME_CONTROL:
+ res = (chan->tech->indicate == NULL) ? 0 :
+ chan->tech->indicate(chan, fr->subclass, fr->data, fr->datalen);
+ break;
+ case AST_FRAME_DTMF_BEGIN:
+ if (chan->audiohooks) {
+ struct ast_frame *old_frame = fr;
+ fr = ast_audiohook_write_list(chan, chan->audiohooks, AST_AUDIOHOOK_DIRECTION_WRITE, fr);
+ if (old_frame != fr)
+ f = fr;
+ }
+ send_dtmf_event(chan, "Sent", fr->subclass, "Yes", "No");
+ ast_clear_flag(chan, AST_FLAG_BLOCKING);
+ ast_channel_unlock(chan);
+ res = ast_senddigit_begin(chan, fr->subclass);
+ ast_channel_lock(chan);
+ CHECK_BLOCKING(chan);
+ break;
+ case AST_FRAME_DTMF_END:
+ if (chan->audiohooks) {
+ struct ast_frame *old_frame = fr;
+ fr = ast_audiohook_write_list(chan, chan->audiohooks, AST_AUDIOHOOK_DIRECTION_WRITE, fr);
+ if (old_frame != fr)
+ f = fr;
+ }
+ send_dtmf_event(chan, "Sent", fr->subclass, "No", "Yes");
+ ast_clear_flag(chan, AST_FLAG_BLOCKING);
+ ast_channel_unlock(chan);
+ res = ast_senddigit_end(chan, fr->subclass, fr->len);
+ ast_channel_lock(chan);
+ CHECK_BLOCKING(chan);
+ break;
+ case AST_FRAME_TEXT:
+ if (fr->subclass == AST_FORMAT_T140) {
+ res = (chan->tech->write_text == NULL) ? 0 :
+ chan->tech->write_text(chan, fr);
+ } else {
+ res = (chan->tech->send_text == NULL) ? 0 :
+ chan->tech->send_text(chan, (char *) fr->data);
+ }
+ break;
+ case AST_FRAME_HTML:
+ res = (chan->tech->send_html == NULL) ? 0 :
+ chan->tech->send_html(chan, fr->subclass, (char *) fr->data, fr->datalen);
+ break;
+ case AST_FRAME_VIDEO:
+ /* XXX Handle translation of video codecs one day XXX */
+ res = (chan->tech->write_video == NULL) ? 0 :
+ chan->tech->write_video(chan, fr);
+ break;
+ case AST_FRAME_MODEM:
+ res = (chan->tech->write == NULL) ? 0 :
+ chan->tech->write(chan, fr);
+ break;
+ case AST_FRAME_VOICE:
+ if (chan->tech->write == NULL)
+ break; /*! \todo XXX should return 0 maybe ? */
+
+ /* If audiohooks are present, write the frame out */
+ if (chan->audiohooks) {
+ struct ast_frame *old_frame = fr;
+ fr = ast_audiohook_write_list(chan, chan->audiohooks, AST_AUDIOHOOK_DIRECTION_WRITE, fr);
+ if (old_frame != fr)
+ f2 = fr;
+ }
+
+ /* If the frame is in the raw write format, then it's easy... just use the frame - otherwise we will have to translate */
+ if (fr->subclass == chan->rawwriteformat)
+ f = fr;
+ else
+ f = (chan->writetrans) ? ast_translate(chan->writetrans, fr, 0) : fr;
+
+ if (!f) {
+ res = 0;
+ break;
+ }
+
+ /* If Monitor is running on this channel, then we have to write frames out there too */
+ if (chan->monitor && chan->monitor->write_stream) {
+ /* XXX must explain this code */
+#ifndef MONITOR_CONSTANT_DELAY
+ int jump = chan->insmpl - chan->outsmpl - 4 * f->samples;
+ if (jump >= 0) {
+ jump = chan->insmpl - chan->outsmpl;
+ if (ast_seekstream(chan->monitor->write_stream, jump, SEEK_FORCECUR) == -1)
+ ast_log(LOG_WARNING, "Failed to perform seek in monitoring write stream, synchronization between the files may be broken\n");
+ chan->outsmpl += jump + f->samples;
+ } else
+ chan->outsmpl += f->samples;
+#else
+ int jump = chan->insmpl - chan->outsmpl;
+ if (jump - MONITOR_DELAY >= 0) {
+ if (ast_seekstream(chan->monitor->write_stream, jump - f->samples, SEEK_FORCECUR) == -1)
+ ast_log(LOG_WARNING, "Failed to perform seek in monitoring write stream, synchronization between the files may be broken\n");
+ chan->outsmpl += jump;
+ } else
+ chan->outsmpl += f->samples;
+#endif
+ if (chan->monitor->state == AST_MONITOR_RUNNING) {
+ if (ast_writestream(chan->monitor->write_stream, f) < 0)
+ ast_log(LOG_WARNING, "Failed to write data to channel monitor write stream\n");
+ }
+ }
+
+ if (f)
+ res = chan->tech->write(chan,f);
+ else
+ res = 0;
+ break;
+ case AST_FRAME_NULL:
+ case AST_FRAME_IAX:
+ /* Ignore these */
+ res = 0;
+ break;
+ default:
+ /* At this point, fr is the incoming frame and f is NULL. Channels do
+ * not expect to get NULL as a frame pointer and will segfault. Hence,
+ * we output the original frame passed in. */
+ res = chan->tech->write(chan, fr);
+ break;
+ }
+
+ if (f && f != fr)
+ ast_frfree(f);
+ if (f2)
+ ast_frfree(f2);
+ ast_clear_flag(chan, AST_FLAG_BLOCKING);
+ /* Consider a write failure to force a soft hangup */
+ if (res < 0)
+ chan->_softhangup |= AST_SOFTHANGUP_DEV;
+ else {
+ chan->fout = FRAMECOUNT_INC(chan->fout);
+ }
+done:
+ ast_channel_unlock(chan);
+ return res;
+}
+
+static int set_format(struct ast_channel *chan, int fmt, int *rawformat, int *format,
+ struct ast_trans_pvt **trans, const int direction)
+{
+ int native;
+ int res;
+
+ /* Make sure we only consider audio */
+ fmt &= AST_FORMAT_AUDIO_MASK;
+
+ native = chan->nativeformats;
+ /* Find a translation path from the native format to one of the desired formats */
+ if (!direction)
+ /* reading */
+ res = ast_translator_best_choice(&fmt, &native);
+ else
+ /* writing */
+ res = ast_translator_best_choice(&native, &fmt);
+
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Unable to find a codec translation path from %s to %s\n",
+ ast_getformatname(native), ast_getformatname(fmt));
+ return -1;
+ }
+
+ /* Now we have a good choice for both. */
+ ast_channel_lock(chan);
+
+ if ((*rawformat == native) && (*format == fmt) && ((*rawformat == *format) || (*trans))) {
+ /* the channel is already in these formats, so nothing to do */
+ ast_channel_unlock(chan);
+ return 0;
+ }
+
+ *rawformat = native;
+ /* User perspective is fmt */
+ *format = fmt;
+ /* Free any read translation we have right now */
+ if (*trans)
+ ast_translator_free_path(*trans);
+ /* Build a translation path from the raw format to the desired format */
+ if (!direction)
+ /* reading */
+ *trans = ast_translator_build_path(*format, *rawformat);
+ else
+ /* writing */
+ *trans = ast_translator_build_path(*rawformat, *format);
+ ast_channel_unlock(chan);
+ ast_debug(1, "Set channel %s to %s format %s\n", chan->name,
+ direction ? "write" : "read", ast_getformatname(fmt));
+ return 0;
+}
+
+int ast_set_read_format(struct ast_channel *chan, int fmt)
+{
+ return set_format(chan, fmt, &chan->rawreadformat, &chan->readformat,
+ &chan->readtrans, 0);
+}
+
+int ast_set_write_format(struct ast_channel *chan, int fmt)
+{
+ return set_format(chan, fmt, &chan->rawwriteformat, &chan->writeformat,
+ &chan->writetrans, 1);
+}
+
+const char *ast_channel_reason2str(int reason)
+{
+ switch (reason) /* the following appear to be the only ones actually returned by request_and_dial */
+ {
+ case 0:
+ return "Call Failure (not BUSY, and not NO_ANSWER, maybe Circuit busy or down?)";
+ case AST_CONTROL_HANGUP:
+ return "Hangup";
+ case AST_CONTROL_RING:
+ return "Local Ring";
+ case AST_CONTROL_RINGING:
+ return "Remote end Ringing";
+ case AST_CONTROL_ANSWER:
+ return "Remote end has Answered";
+ case AST_CONTROL_BUSY:
+ return "Remote end is Busy";
+ case AST_CONTROL_CONGESTION:
+ return "Congestion (circuits busy)";
+ default:
+ return "Unknown Reason!!";
+ }
+}
+
+struct ast_channel *__ast_request_and_dial(const char *type, int format, void *data, int timeout, int *outstate, const char *cid_num, const char *cid_name, struct outgoing_helper *oh)
+{
+ int dummy_outstate;
+ int cause = 0;
+ struct ast_channel *chan;
+ int res = 0;
+ int last_subclass = 0;
+
+ if (outstate)
+ *outstate = 0;
+ else
+ outstate = &dummy_outstate; /* make outstate always a valid pointer */
+
+ chan = ast_request(type, format, data, &cause);
+ if (!chan) {
+ ast_log(LOG_NOTICE, "Unable to request channel %s/%s\n", type, (char *)data);
+ /* compute error and return */
+ if (cause == AST_CAUSE_BUSY)
+ *outstate = AST_CONTROL_BUSY;
+ else if (cause == AST_CAUSE_CONGESTION)
+ *outstate = AST_CONTROL_CONGESTION;
+ return NULL;
+ }
+
+ if (oh) {
+ if (oh->vars)
+ ast_set_variables(chan, oh->vars);
+ /* XXX why is this necessary, for the parent_channel perhaps ? */
+ if (!ast_strlen_zero(oh->cid_num) && !ast_strlen_zero(oh->cid_name))
+ ast_set_callerid(chan, oh->cid_num, oh->cid_name, oh->cid_num);
+ if (oh->parent_channel) {
+ ast_channel_inherit_variables(oh->parent_channel, chan);
+ ast_channel_datastore_inherit(oh->parent_channel, chan);
+ }
+ if (oh->account)
+ ast_cdr_setaccount(chan, oh->account);
+ }
+ ast_set_callerid(chan, cid_num, cid_name, cid_num);
+
+
+
+ if (!chan->cdr) { /* up till now, this insertion hasn't been done. Therefore,
+ to keep from throwing off the basic order of the universe,
+ we will try to keep this cdr from getting posted. */
+ chan->cdr = ast_cdr_alloc();
+ ast_cdr_init(chan->cdr, chan);
+ ast_cdr_start(chan->cdr);
+ }
+ if (ast_call(chan, data, 0)) { /* ast_call failed... */
+ ast_log(LOG_NOTICE, "Unable to call channel %s/%s\n", type, (char *)data);
+ } else {
+ res = 1; /* mark success in case chan->_state is already AST_STATE_UP */
+ while (timeout && chan->_state != AST_STATE_UP) {
+ struct ast_frame *f;
+ res = ast_waitfor(chan, timeout);
+ if (res <= 0) /* error, timeout, or done */
+ break;
+ if (timeout > -1)
+ timeout = res;
+ f = ast_read(chan);
+ if (!f) {
+ *outstate = AST_CONTROL_HANGUP;
+ res = 0;
+ break;
+ }
+ if (f->frametype == AST_FRAME_CONTROL) {
+ switch (f->subclass) {
+ case AST_CONTROL_RINGING: /* record but keep going */
+ *outstate = f->subclass;
+ break;
+
+ case AST_CONTROL_BUSY:
+ case AST_CONTROL_CONGESTION:
+ case AST_CONTROL_ANSWER:
+ *outstate = f->subclass;
+ timeout = 0; /* trick to force exit from the while() */
+ break;
+
+ /* Ignore these */
+ case AST_CONTROL_PROGRESS:
+ case AST_CONTROL_PROCEEDING:
+ case AST_CONTROL_HOLD:
+ case AST_CONTROL_UNHOLD:
+ case AST_CONTROL_VIDUPDATE:
+ case -1: /* Ignore -- just stopping indications */
+ break;
+
+ default:
+ ast_log(LOG_NOTICE, "Don't know what to do with control frame %d\n", f->subclass);
+ }
+ last_subclass = f->subclass;
+ }
+ ast_frfree(f);
+ }
+ }
+
+ /* Final fixups */
+ if (oh) {
+ if (!ast_strlen_zero(oh->context))
+ ast_copy_string(chan->context, oh->context, sizeof(chan->context));
+ if (!ast_strlen_zero(oh->exten))
+ ast_copy_string(chan->exten, oh->exten, sizeof(chan->exten));
+ if (oh->priority)
+ chan->priority = oh->priority;
+ }
+ if (chan->_state == AST_STATE_UP)
+ *outstate = AST_CONTROL_ANSWER;
+
+ if (res <= 0) {
+ if ( AST_CONTROL_RINGING == last_subclass )
+ chan->hangupcause = AST_CAUSE_NO_ANSWER;
+ if (!chan->cdr && (chan->cdr = ast_cdr_alloc()))
+ ast_cdr_init(chan->cdr, chan);
+ if (chan->cdr) {
+ char tmp[256];
+ snprintf(tmp, sizeof(tmp), "%s/%s", type, (char *)data);
+ ast_cdr_setapp(chan->cdr,"Dial",tmp);
+ ast_cdr_update(chan);
+ ast_cdr_start(chan->cdr);
+ ast_cdr_end(chan->cdr);
+ /* If the cause wasn't handled properly */
+ if (ast_cdr_disposition(chan->cdr,chan->hangupcause))
+ ast_cdr_failed(chan->cdr);
+ }
+ ast_hangup(chan);
+ chan = NULL;
+ }
+ return chan;
+}
+
+struct ast_channel *ast_request_and_dial(const char *type, int format, void *data, int timeout, int *outstate, const char *cidnum, const char *cidname)
+{
+ return __ast_request_and_dial(type, format, data, timeout, outstate, cidnum, cidname, NULL);
+}
+
+struct ast_channel *ast_request(const char *type, int format, void *data, int *cause)
+{
+ struct chanlist *chan;
+ struct ast_channel *c;
+ int capabilities;
+ int fmt;
+ int res;
+ int foo;
+ int videoformat = format & AST_FORMAT_VIDEO_MASK;
+ int textformat = format & AST_FORMAT_TEXT_MASK;
+
+ if (!cause)
+ cause = &foo;
+ *cause = AST_CAUSE_NOTDEFINED;
+
+ if (AST_RWLIST_RDLOCK(&channels)) {
+ ast_log(LOG_WARNING, "Unable to lock channel list\n");
+ return NULL;
+ }
+
+ AST_LIST_TRAVERSE(&backends, chan, list) {
+ if (strcasecmp(type, chan->tech->type))
+ continue;
+
+ capabilities = chan->tech->capabilities;
+ fmt = format & AST_FORMAT_AUDIO_MASK;
+ res = ast_translator_best_choice(&fmt, &capabilities);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "No translator path exists for channel type %s (native 0x%x) to 0x%x\n", type, chan->tech->capabilities, format);
+ *cause = AST_CAUSE_BEARERCAPABILITY_NOTAVAIL;
+ AST_RWLIST_UNLOCK(&channels);
+ return NULL;
+ }
+ AST_RWLIST_UNLOCK(&channels);
+ if (!chan->tech->requester)
+ return NULL;
+
+ if (!(c = chan->tech->requester(type, capabilities | videoformat | textformat, data, cause)))
+ return NULL;
+
+ /* no need to generate a Newchannel event here; it is done in the channel_alloc call */
+ return c;
+ }
+
+ ast_log(LOG_WARNING, "No channel type registered for '%s'\n", type);
+ *cause = AST_CAUSE_NOSUCHDRIVER;
+ AST_RWLIST_UNLOCK(&channels);
+
+ return NULL;
+}
+
+int ast_call(struct ast_channel *chan, char *addr, int timeout)
+{
+ /* Place an outgoing call, but don't wait any longer than timeout ms before returning.
+ If the remote end does not answer within the timeout, then do NOT hang up, but
+ return anyway. */
+ int res = -1;
+ /* Stop if we're a zombie or need a soft hangup */
+ ast_channel_lock(chan);
+ if (!ast_test_flag(chan, AST_FLAG_ZOMBIE) && !ast_check_hangup(chan)) {
+ if (chan->tech->call)
+ res = chan->tech->call(chan, addr, timeout);
+ ast_set_flag(chan, AST_FLAG_OUTGOING);
+ }
+ ast_channel_unlock(chan);
+ return res;
+}
+
+/*!
+ \brief Transfer a call to dest, if the channel supports transfer
+
+ Called by:
+ \arg app_transfer
+ \arg the manager interface
+*/
+int ast_transfer(struct ast_channel *chan, char *dest)
+{
+ int res = -1;
+
+ /* Stop if we're a zombie or need a soft hangup */
+ ast_channel_lock(chan);
+ if (!ast_test_flag(chan, AST_FLAG_ZOMBIE) && !ast_check_hangup(chan)) {
+ if (chan->tech->transfer) {
+ res = chan->tech->transfer(chan, dest);
+ if (!res)
+ res = 1;
+ } else
+ res = 0;
+ }
+ ast_channel_unlock(chan);
+ return res;
+}
+
+int ast_readstring(struct ast_channel *c, char *s, int len, int timeout, int ftimeout, char *enders)
+{
+ return ast_readstring_full(c, s, len, timeout, ftimeout, enders, -1, -1);
+}
+
+int ast_readstring_full(struct ast_channel *c, char *s, int len, int timeout, int ftimeout, char *enders, int audiofd, int ctrlfd)
+{
+ int pos = 0; /* index in the buffer where we accumulate digits */
+ int to = ftimeout;
+
+ /* Stop if we're a zombie or need a soft hangup */
+ if (ast_test_flag(c, AST_FLAG_ZOMBIE) || ast_check_hangup(c))
+ return -1;
+ if (!len)
+ return -1;
+ for (;;) {
+ int d;
+ if (c->stream) {
+ d = ast_waitstream_full(c, AST_DIGIT_ANY, audiofd, ctrlfd);
+ ast_stopstream(c);
+ usleep(1000);
+ if (!d)
+ d = ast_waitfordigit_full(c, to, audiofd, ctrlfd);
+ } else {
+ d = ast_waitfordigit_full(c, to, audiofd, ctrlfd);
+ }
+ if (d < 0)
+ return -1;
+ if (d == 0) {
+ s[pos]='\0';
+ return 1;
+ }
+ if (d == 1) {
+ s[pos]='\0';
+ return 2;
+ }
+ if (!strchr(enders, d))
+ s[pos++] = d;
+ if (strchr(enders, d) || (pos >= len)) {
+ s[pos]='\0';
+ return 0;
+ }
+ to = timeout;
+ }
+ /* Never reached */
+ return 0;
+}
+
+int ast_channel_supports_html(struct ast_channel *chan)
+{
+ return (chan->tech->send_html) ? 1 : 0;
+}
+
+int ast_channel_sendhtml(struct ast_channel *chan, int subclass, const char *data, int datalen)
+{
+ if (chan->tech->send_html)
+ return chan->tech->send_html(chan, subclass, data, datalen);
+ return -1;
+}
+
+int ast_channel_sendurl(struct ast_channel *chan, const char *url)
+{
+ return ast_channel_sendhtml(chan, AST_HTML_URL, url, strlen(url) + 1);
+}
+
+/*! \brief Set up translation from one channel to another */
+static int ast_channel_make_compatible_helper(struct ast_channel *from, struct ast_channel *to)
+{
+ int src;
+ int dst;
+
+ if (from->readformat == to->writeformat && from->writeformat == to->readformat) {
+ /* Already compatible! Moving on ... */
+ return 0;
+ }
+
+ /* Set up translation from the 'from' channel to the 'to' channel */
+ src = from->nativeformats;
+ dst = to->nativeformats;
+ if (ast_translator_best_choice(&dst, &src) < 0) {
+ ast_log(LOG_WARNING, "No path to translate from %s(%d) to %s(%d)\n", from->name, src, to->name, dst);
+ return -1;
+ }
+
+ /* if the best path is not 'pass through', then
+ transcoding is needed; if desired, force transcode path
+ to use SLINEAR between channels, but only if there is
+ no direct conversion available */
+ if ((src != dst) && ast_opt_transcode_via_slin &&
+ (ast_translate_path_steps(dst, src) != 1))
+ dst = AST_FORMAT_SLINEAR;
+ if (ast_set_read_format(from, dst) < 0) {
+ ast_log(LOG_WARNING, "Unable to set read format on channel %s to %d\n", from->name, dst);
+ return -1;
+ }
+ if (ast_set_write_format(to, dst) < 0) {
+ ast_log(LOG_WARNING, "Unable to set write format on channel %s to %d\n", to->name, dst);
+ return -1;
+ }
+ return 0;
+}
+
+int ast_channel_make_compatible(struct ast_channel *chan, struct ast_channel *peer)
+{
+ /* Some callers do not check return code, and we must try to set all call legs correctly */
+ int rc = 0;
+
+ /* Set up translation from the chan to the peer */
+ rc = ast_channel_make_compatible_helper(chan, peer);
+
+ if (rc < 0)
+ return rc;
+
+ /* Set up translation from the peer to the chan */
+ rc = ast_channel_make_compatible_helper(peer, chan);
+
+ return rc;
+}
+
+int ast_channel_masquerade(struct ast_channel *original, struct ast_channel *clone)
+{
+ int res = -1;
+ struct ast_channel *final_orig, *final_clone, *base;
+
+retrymasq:
+ final_orig = original;
+ final_clone = clone;
+
+ ast_channel_lock(original);
+ while (ast_channel_trylock(clone)) {
+ ast_channel_unlock(original);
+ usleep(1);
+ ast_channel_lock(original);
+ }
+
+ /* each of these channels may be sitting behind a channel proxy (i.e. chan_agent)
+ and if so, we don't really want to masquerade it, but its proxy */
+ if (original->_bridge && (original->_bridge != ast_bridged_channel(original)) && (original->_bridge->_bridge != original))
+ final_orig = original->_bridge;
+
+ if (clone->_bridge && (clone->_bridge != ast_bridged_channel(clone)) && (clone->_bridge->_bridge != clone))
+ final_clone = clone->_bridge;
+
+ if (final_clone->tech->get_base_channel && (base = final_clone->tech->get_base_channel(final_clone))) {
+ final_clone = base;
+ }
+
+ if ((final_orig != original) || (final_clone != clone)) {
+ /* Lots and lots of deadlock avoidance. The main one we're competing with
+ * is ast_write(), which locks channels recursively, when working with a
+ * proxy channel. */
+ if (ast_channel_trylock(final_orig)) {
+ ast_channel_unlock(clone);
+ ast_channel_unlock(original);
+ goto retrymasq;
+ }
+ if (ast_channel_trylock(final_clone)) {
+ ast_channel_unlock(final_orig);
+ ast_channel_unlock(clone);
+ ast_channel_unlock(original);
+ goto retrymasq;
+ }
+ ast_channel_unlock(clone);
+ ast_channel_unlock(original);
+ original = final_orig;
+ clone = final_clone;
+ }
+
+ if (original == clone) {
+ ast_log(LOG_WARNING, "Can't masquerade channel '%s' into itself!\n", original->name);
+ ast_channel_unlock(clone);
+ ast_channel_unlock(original);
+ return -1;
+ }
+
+ ast_debug(1, "Planning to masquerade channel %s into the structure of %s\n",
+ clone->name, original->name);
+ if (original->masq) {
+ ast_log(LOG_WARNING, "%s is already going to masquerade as %s\n",
+ original->masq->name, original->name);
+ } else if (clone->masqr) {
+ ast_log(LOG_WARNING, "%s is already going to masquerade as %s\n",
+ clone->name, clone->masqr->name);
+ } else {
+ original->masq = clone;
+ clone->masqr = original;
+ ast_queue_frame(original, &ast_null_frame);
+ ast_queue_frame(clone, &ast_null_frame);
+ ast_debug(1, "Done planning to masquerade channel %s into the structure of %s\n", clone->name, original->name);
+ res = 0;
+ }
+
+ ast_channel_unlock(clone);
+ ast_channel_unlock(original);
+
+ return res;
+}
+
+void ast_change_name(struct ast_channel *chan, char *newname)
+{
+ manager_event(EVENT_FLAG_CALL, "Rename", "Channel: %s\r\nNewname: %s\r\nUniqueid: %s\r\n", chan->name, newname, chan->uniqueid);
+ ast_string_field_set(chan, name, newname);
+}
+
+void ast_channel_inherit_variables(const struct ast_channel *parent, struct ast_channel *child)
+{
+ struct ast_var_t *current, *newvar;
+ const char *varname;
+
+ AST_LIST_TRAVERSE(&parent->varshead, current, entries) {
+ int vartype = 0;
+
+ varname = ast_var_full_name(current);
+ if (!varname)
+ continue;
+
+ if (varname[0] == '_') {
+ vartype = 1;
+ if (varname[1] == '_')
+ vartype = 2;
+ }
+
+ switch (vartype) {
+ case 1:
+ newvar = ast_var_assign(&varname[1], ast_var_value(current));
+ if (newvar) {
+ AST_LIST_INSERT_TAIL(&child->varshead, newvar, entries);
+ ast_debug(1, "Copying soft-transferable variable %s.\n", ast_var_name(newvar));
+ }
+ break;
+ case 2:
+ newvar = ast_var_assign(varname, ast_var_value(current));
+ if (newvar) {
+ AST_LIST_INSERT_TAIL(&child->varshead, newvar, entries);
+ ast_debug(1, "Copying hard-transferable variable %s.\n", ast_var_name(newvar));
+ }
+ break;
+ default:
+ ast_debug(1, "Not copying variable %s.\n", ast_var_name(current));
+ break;
+ }
+ }
+}
+
+/*!
+ \brief Clone channel variables from 'clone' channel into 'original' channel
+
+ All variables except those related to app_groupcount are cloned.
+ Variables are actually _removed_ from 'clone' channel, presumably
+ because it will subsequently be destroyed.
+
+ \note Assumes locks will be in place on both channels when called.
+*/
+static void clone_variables(struct ast_channel *original, struct ast_channel *clone)
+{
+ struct ast_var_t *current, *newvar;
+ /* Append variables from clone channel into original channel */
+ /* XXX Is this always correct? We have to in order to keep MACROS working XXX */
+ if (AST_LIST_FIRST(&clone->varshead))
+ AST_LIST_APPEND_LIST(&original->varshead, &clone->varshead, entries);
+
+ /* then, dup the varshead list into the clone */
+
+ AST_LIST_TRAVERSE(&original->varshead, current, entries) {
+ newvar = ast_var_assign(current->name, current->value);
+ if (newvar)
+ AST_LIST_INSERT_TAIL(&clone->varshead, newvar, entries);
+ }
+}
+
+/*!
+ \brief Masquerade a channel
+
+ \note Assumes channel will be locked when called
+*/
+int ast_do_masquerade(struct ast_channel *original)
+{
+ int x,i;
+ int res=0;
+ int origstate;
+ struct ast_frame *cur;
+ const struct ast_channel_tech *t;
+ void *t_pvt;
+ struct ast_callerid tmpcid;
+ struct ast_channel *clone = original->masq;
+ struct ast_cdr *cdr;
+ int rformat = original->readformat;
+ int wformat = original->writeformat;
+ char newn[AST_CHANNEL_NAME];
+ char orig[AST_CHANNEL_NAME];
+ char masqn[AST_CHANNEL_NAME];
+ char zombn[AST_CHANNEL_NAME];
+
+ ast_debug(4, "Actually Masquerading %s(%d) into the structure of %s(%d)\n",
+ clone->name, clone->_state, original->name, original->_state);
+
+ manager_event(EVENT_FLAG_CALL, "Masquerade", "Clone: %s\r\nCloneState: %s\r\nOriginal: %s\r\nOriginalState: %s\r\n",
+ clone->name, ast_state2str(clone->_state), original->name, ast_state2str(original->_state));
+
+ /* XXX This is a seriously wacked out operation. We're essentially putting the guts of
+ the clone channel into the original channel. Start by killing off the original
+ channel's backend. I'm not sure we're going to keep this function, because
+ while the features are nice, the cost is very high in terms of pure nastiness. XXX */
+
+ /* We need the clone's lock, too */
+ ast_channel_lock(clone);
+
+ ast_debug(2, "Got clone lock for masquerade on '%s' at %p\n", clone->name, &clone->lock_dont_use);
+
+ /* Having remembered the original read/write formats, we turn off any translation on either
+ one */
+ free_translation(clone);
+ free_translation(original);
+
+
+ /* Unlink the masquerade */
+ original->masq = NULL;
+ clone->masqr = NULL;
+
+ /* Save the original name */
+ ast_copy_string(orig, original->name, sizeof(orig));
+ /* Save the new name */
+ ast_copy_string(newn, clone->name, sizeof(newn));
+ /* Create the masq name */
+ snprintf(masqn, sizeof(masqn), "%s<MASQ>", newn);
+
+ /* Copy the name from the clone channel */
+ ast_string_field_set(original, name, newn);
+
+ /* Mangle the name of the clone channel */
+ ast_string_field_set(clone, name, masqn);
+
+ /* Notify any managers of the change, first the masq then the other */
+ manager_event(EVENT_FLAG_CALL, "Rename", "Channel: %s\r\nNewname: %s\r\nUniqueid: %s\r\n", newn, masqn, clone->uniqueid);
+ manager_event(EVENT_FLAG_CALL, "Rename", "Channel: %s\r\nNewname: %s\r\nUniqueid: %s\r\n", orig, newn, original->uniqueid);
+
+ /* Swap the technologies */
+ t = original->tech;
+ original->tech = clone->tech;
+ clone->tech = t;
+
+ /* Swap the cdrs */
+ cdr = original->cdr;
+ original->cdr = clone->cdr;
+ clone->cdr = cdr;
+
+ t_pvt = original->tech_pvt;
+ original->tech_pvt = clone->tech_pvt;
+ clone->tech_pvt = t_pvt;
+
+ /* Swap the alertpipes */
+ for (i = 0; i < 2; i++) {
+ x = original->alertpipe[i];
+ original->alertpipe[i] = clone->alertpipe[i];
+ clone->alertpipe[i] = x;
+ }
+
+ /*
+ * Swap the readq's. The end result should be this:
+ *
+ * 1) All frames should be on the new (original) channel.
+ * 2) Any frames that were already on the new channel before this
+ * masquerade need to be at the end of the readq, after all of the
+ * frames on the old (clone) channel.
+ * 3) The alertpipe needs to get poked for every frame that was already
+ * on the new channel, since we are now using the alert pipe from the
+ * old (clone) channel.
+ */
+ {
+ AST_LIST_HEAD_NOLOCK(, ast_frame) tmp_readq;
+ AST_LIST_HEAD_SET_NOLOCK(&tmp_readq, NULL);
+
+ AST_LIST_APPEND_LIST(&tmp_readq, &original->readq, frame_list);
+ AST_LIST_APPEND_LIST(&original->readq, &clone->readq, frame_list);
+
+ while ((cur = AST_LIST_REMOVE_HEAD(&tmp_readq, frame_list))) {
+ AST_LIST_INSERT_TAIL(&original->readq, cur, frame_list);
+ if (original->alertpipe[1] > -1) {
+ int poke = 0;
+ write(original->alertpipe[1], &poke, sizeof(poke));
+ }
+ }
+ }
+
+ /* Swap the raw formats */
+ x = original->rawreadformat;
+ original->rawreadformat = clone->rawreadformat;
+ clone->rawreadformat = x;
+ x = original->rawwriteformat;
+ original->rawwriteformat = clone->rawwriteformat;
+ clone->rawwriteformat = x;
+
+ clone->_softhangup = AST_SOFTHANGUP_DEV;
+
+ /* And of course, so does our current state. Note we need not
+ call ast_setstate since the event manager doesn't really consider
+ these separate. We do this early so that the clone has the proper
+ state of the original channel. */
+ origstate = original->_state;
+ original->_state = clone->_state;
+ clone->_state = origstate;
+
+ if (clone->tech->fixup){
+ res = clone->tech->fixup(original, clone);
+ if (res)
+ ast_log(LOG_WARNING, "Fixup failed on channel %s, strange things may happen.\n", clone->name);
+ }
+
+ /* Start by disconnecting the original's physical side */
+ if (clone->tech->hangup)
+ res = clone->tech->hangup(clone);
+ if (res) {
+ ast_log(LOG_WARNING, "Hangup failed! Strange things may happen!\n");
+ ast_channel_unlock(clone);
+ return -1;
+ }
+
+ snprintf(zombn, sizeof(zombn), "%s<ZOMBIE>", orig);
+ /* Mangle the name of the clone channel */
+ ast_string_field_set(clone, name, zombn);
+ manager_event(EVENT_FLAG_CALL, "Rename", "Channel: %s\r\nNewname: %s\r\nUniqueid: %s\r\n", masqn, zombn, clone->uniqueid);
+
+ /* Update the type. */
+ t_pvt = original->monitor;
+ original->monitor = clone->monitor;
+ clone->monitor = t_pvt;
+
+ /* Keep the same language. */
+ ast_string_field_set(original, language, clone->language);
+ /* Copy the FD's other than the generator fd */
+ for (x = 0; x < AST_MAX_FDS; x++) {
+ if (x != AST_GENERATOR_FD)
+ ast_channel_set_fd(original, x, clone->fds[x]);
+ }
+
+ ast_app_group_update(clone, original);
+
+ /* Move data stores over */
+ if (AST_LIST_FIRST(&clone->datastores))
+ AST_LIST_APPEND_LIST(&original->datastores, &clone->datastores, entry);
+
+ clone_variables(original, clone);
+ /* Presense of ADSI capable CPE follows clone */
+ original->adsicpe = clone->adsicpe;
+ /* Bridge remains the same */
+ /* CDR fields remain the same */
+ /* XXX What about blocking, softhangup, blocker, and lock and blockproc? XXX */
+ /* Application and data remain the same */
+ /* Clone exception becomes real one, as with fdno */
+ ast_copy_flags(original, clone, AST_FLAG_EXCEPTION);
+ original->fdno = clone->fdno;
+ /* Schedule context remains the same */
+ /* Stream stuff stays the same */
+ /* Keep the original state. The fixup code will need to work with it most likely */
+
+ /* Just swap the whole structures, nevermind the allocations, they'll work themselves
+ out. */
+ tmpcid = original->cid;
+ original->cid = clone->cid;
+ clone->cid = tmpcid;
+
+ /* Restore original timing file descriptor */
+ ast_channel_set_fd(original, AST_TIMING_FD, original->timingfd);
+
+ /* Our native formats are different now */
+ original->nativeformats = clone->nativeformats;
+
+ /* Context, extension, priority, app data, jump table, remain the same */
+ /* pvt switches. pbx stays the same, as does next */
+
+ /* Set the write format */
+ ast_set_write_format(original, wformat);
+
+ /* Set the read format */
+ ast_set_read_format(original, rformat);
+
+ /* Copy the music class */
+ ast_string_field_set(original, musicclass, clone->musicclass);
+
+ ast_debug(1, "Putting channel %s in %d/%d formats\n", original->name, wformat, rformat);
+
+ /* Okay. Last thing is to let the channel driver know about all this mess, so he
+ can fix up everything as best as possible */
+ if (original->tech->fixup) {
+ res = original->tech->fixup(clone, original);
+ if (res) {
+ ast_log(LOG_WARNING, "Channel for type '%s' could not fixup channel %s\n",
+ original->tech->type, original->name);
+ ast_channel_unlock(clone);
+ return -1;
+ }
+ } else
+ ast_log(LOG_WARNING, "Channel type '%s' does not have a fixup routine (for %s)! Bad things may happen.\n",
+ original->tech->type, original->name);
+
+ /* If an indication is currently playing maintain it on the channel that is taking the place of original */
+ if (original->visible_indication)
+ ast_indicate(original, original->visible_indication);
+
+ /* Now, at this point, the "clone" channel is totally F'd up. We mark it as
+ a zombie so nothing tries to touch it. If it's already been marked as a
+ zombie, then free it now (since it already is considered invalid). */
+ if (ast_test_flag(clone, AST_FLAG_ZOMBIE)) {
+ ast_debug(1, "Destroying channel clone '%s'\n", clone->name);
+ ast_channel_unlock(clone);
+ manager_event(EVENT_FLAG_CALL, "Hangup",
+ "Channel: %s\r\n"
+ "Uniqueid: %s\r\n"
+ "Cause: %d\r\n"
+ "Cause-txt: %s\r\n",
+ clone->name,
+ clone->uniqueid,
+ clone->hangupcause,
+ ast_cause2str(clone->hangupcause)
+ );
+ ast_channel_free(clone);
+ } else {
+ ast_debug(1, "Released clone lock on '%s'\n", clone->name);
+ ast_set_flag(clone, AST_FLAG_ZOMBIE);
+ ast_queue_frame(clone, &ast_null_frame);
+ ast_channel_unlock(clone);
+ }
+
+ /* Signal any blocker */
+ if (ast_test_flag(original, AST_FLAG_BLOCKING))
+ pthread_kill(original->blocker, SIGURG);
+ ast_debug(1, "Done Masquerading %s (%d)\n", original->name, original->_state);
+ return 0;
+}
+
+void ast_set_callerid(struct ast_channel *chan, const char *cid_num, const char *cid_name, const char *cid_ani)
+{
+ ast_channel_lock(chan);
+
+ if (cid_num) {
+ if (chan->cid.cid_num)
+ ast_free(chan->cid.cid_num);
+ chan->cid.cid_num = ast_strdup(cid_num);
+ }
+ if (cid_name) {
+ if (chan->cid.cid_name)
+ ast_free(chan->cid.cid_name);
+ chan->cid.cid_name = ast_strdup(cid_name);
+ }
+ if (cid_ani) {
+ if (chan->cid.cid_ani)
+ ast_free(chan->cid.cid_ani);
+ chan->cid.cid_ani = ast_strdup(cid_ani);
+ }
+ if (chan->cdr)
+ ast_cdr_setcid(chan->cdr, chan);
+ manager_event(EVENT_FLAG_CALL, "NewCallerid",
+ "Channel: %s\r\n"
+ "CallerIDNum: %s\r\n"
+ "CallerIDName: %s\r\n"
+ "Uniqueid: %s\r\n"
+ "CID-CallingPres: %d (%s)\r\n",
+ chan->name,
+ S_OR(chan->cid.cid_num, ""),
+ S_OR(chan->cid.cid_name, ""),
+ chan->uniqueid,
+ chan->cid.cid_pres,
+ ast_describe_caller_presentation(chan->cid.cid_pres)
+ );
+
+ ast_channel_unlock(chan);
+}
+
+int ast_setstate(struct ast_channel *chan, enum ast_channel_state state)
+{
+ int oldstate = chan->_state;
+
+ if (oldstate == state)
+ return 0;
+
+ chan->_state = state;
+ ast_device_state_changed_literal(chan->name);
+ /* setstate used to conditionally report Newchannel; this is no more */
+ manager_event(EVENT_FLAG_CALL,
+ "Newstate",
+ "Channel: %s\r\n"
+ "ChannelState: %d\r\n"
+ "ChannelStateDesc: %s\r\n"
+ "CallerIDNum: %s\r\n"
+ "CallerIDName: %s\r\n"
+ "Uniqueid: %s\r\n",
+ chan->name, chan->_state, ast_state2str(chan->_state),
+ S_OR(chan->cid.cid_num, ""),
+ S_OR(chan->cid.cid_name, ""),
+ chan->uniqueid);
+
+ return 0;
+}
+
+/*! \brief Find bridged channel */
+struct ast_channel *ast_bridged_channel(struct ast_channel *chan)
+{
+ struct ast_channel *bridged;
+ bridged = chan->_bridge;
+ if (bridged && bridged->tech->bridged_channel)
+ bridged = bridged->tech->bridged_channel(chan, bridged);
+ return bridged;
+}
+
+static void bridge_playfile(struct ast_channel *chan, struct ast_channel *peer, const char *sound, int remain)
+{
+ int min = 0, sec = 0, check;
+
+ check = ast_autoservice_start(peer);
+ if (check)
+ return;
+
+ if (remain > 0) {
+ if (remain / 60 > 1) {
+ min = remain / 60;
+ sec = remain % 60;
+ } else {
+ sec = remain;
+ }
+ }
+
+ if (!strcmp(sound,"timeleft")) { /* Queue support */
+ ast_stream_and_wait(chan, "vm-youhave", "");
+ if (min) {
+ ast_say_number(chan, min, AST_DIGIT_ANY, chan->language, NULL);
+ ast_stream_and_wait(chan, "queue-minutes", "");
+ }
+ if (sec) {
+ ast_say_number(chan, sec, AST_DIGIT_ANY, chan->language, NULL);
+ ast_stream_and_wait(chan, "queue-seconds", "");
+ }
+ } else {
+ ast_stream_and_wait(chan, sound, "");
+ }
+
+ ast_autoservice_stop(peer);
+}
+
+static enum ast_bridge_result ast_generic_bridge(struct ast_channel *c0, struct ast_channel *c1,
+ struct ast_bridge_config *config, struct ast_frame **fo,
+ struct ast_channel **rc, struct timeval bridge_end)
+{
+ /* Copy voice back and forth between the two channels. */
+ struct ast_channel *cs[3];
+ struct ast_frame *f;
+ enum ast_bridge_result res = AST_BRIDGE_COMPLETE;
+ int o0nativeformats;
+ int o1nativeformats;
+ int watch_c0_dtmf;
+ int watch_c1_dtmf;
+ void *pvt0, *pvt1;
+ /* Indicates whether a frame was queued into a jitterbuffer */
+ int frame_put_in_jb = 0;
+ int jb_in_use;
+ int to;
+
+ cs[0] = c0;
+ cs[1] = c1;
+ pvt0 = c0->tech_pvt;
+ pvt1 = c1->tech_pvt;
+ o0nativeformats = c0->nativeformats;
+ o1nativeformats = c1->nativeformats;
+ watch_c0_dtmf = config->flags & AST_BRIDGE_DTMF_CHANNEL_0;
+ watch_c1_dtmf = config->flags & AST_BRIDGE_DTMF_CHANNEL_1;
+
+ /* Check the need of a jitterbuffer for each channel */
+ jb_in_use = ast_jb_do_usecheck(c0, c1);
+
+ ast_poll_channel_add(c0, c1);
+
+ for (;;) {
+ struct ast_channel *who, *other;
+
+ if ((c0->tech_pvt != pvt0) || (c1->tech_pvt != pvt1) ||
+ (o0nativeformats != c0->nativeformats) ||
+ (o1nativeformats != c1->nativeformats)) {
+ /* Check for Masquerade, codec changes, etc */
+ res = AST_BRIDGE_RETRY;
+ break;
+ }
+ if (bridge_end.tv_sec) {
+ to = ast_tvdiff_ms(bridge_end, ast_tvnow());
+ if (to <= 0) {
+ if (config->timelimit)
+ res = AST_BRIDGE_RETRY;
+ else
+ res = AST_BRIDGE_COMPLETE;
+ break;
+ }
+ } else
+ to = -1;
+ /* Calculate the appropriate max sleep interval - in general, this is the time,
+ left to the closest jb delivery moment */
+ if (jb_in_use)
+ to = ast_jb_get_when_to_wakeup(c0, c1, to);
+ who = ast_waitfor_n(cs, 2, &to);
+ if (!who) {
+ /* No frame received within the specified timeout - check if we have to deliver now */
+ if (jb_in_use)
+ ast_jb_get_and_deliver(c0, c1);
+ if (c0->_softhangup == AST_SOFTHANGUP_UNBRIDGE || c1->_softhangup == AST_SOFTHANGUP_UNBRIDGE) {
+ if (c0->_softhangup == AST_SOFTHANGUP_UNBRIDGE)
+ c0->_softhangup = 0;
+ if (c1->_softhangup == AST_SOFTHANGUP_UNBRIDGE)
+ c1->_softhangup = 0;
+ c0->_bridge = c1;
+ c1->_bridge = c0;
+ }
+ continue;
+ }
+ f = ast_read(who);
+ if (!f) {
+ *fo = NULL;
+ *rc = who;
+ ast_debug(1, "Didn't get a frame from channel: %s\n",who->name);
+ break;
+ }
+
+ other = (who == c0) ? c1 : c0; /* the 'other' channel */
+ /* Try add the frame info the who's bridged channel jitterbuff */
+ if (jb_in_use)
+ frame_put_in_jb = !ast_jb_put(other, f);
+
+ if ((f->frametype == AST_FRAME_CONTROL) && !(config->flags & AST_BRIDGE_IGNORE_SIGS)) {
+ int bridge_exit = 0;
+
+ switch (f->subclass) {
+ case AST_CONTROL_HOLD:
+ case AST_CONTROL_UNHOLD:
+ case AST_CONTROL_VIDUPDATE:
+ ast_indicate_data(other, f->subclass, f->data, f->datalen);
+ break;
+ default:
+ *fo = f;
+ *rc = who;
+ bridge_exit = 1;
+ ast_debug(1, "Got a FRAME_CONTROL (%d) frame on channel %s\n", f->subclass, who->name);
+ break;
+ }
+ if (bridge_exit)
+ break;
+ }
+ if ((f->frametype == AST_FRAME_VOICE) ||
+ (f->frametype == AST_FRAME_DTMF_BEGIN) ||
+ (f->frametype == AST_FRAME_DTMF) ||
+ (f->frametype == AST_FRAME_VIDEO) ||
+ (f->frametype == AST_FRAME_IMAGE) ||
+ (f->frametype == AST_FRAME_HTML) ||
+ (f->frametype == AST_FRAME_MODEM) ||
+ (f->frametype == AST_FRAME_TEXT)) {
+ /* monitored dtmf causes exit from bridge */
+ int monitored_source = (who == c0) ? watch_c0_dtmf : watch_c1_dtmf;
+
+ if (monitored_source &&
+ (f->frametype == AST_FRAME_DTMF_END ||
+ f->frametype == AST_FRAME_DTMF_BEGIN)) {
+ *fo = f;
+ *rc = who;
+ ast_debug(1, "Got DTMF %s on channel (%s)\n",
+ f->frametype == AST_FRAME_DTMF_END ? "end" : "begin",
+ who->name);
+ break;
+ }
+ /* Write immediately frames, not passed through jb */
+ if (!frame_put_in_jb)
+ ast_write(other, f);
+
+ /* Check if we have to deliver now */
+ if (jb_in_use)
+ ast_jb_get_and_deliver(c0, c1);
+ }
+ /* XXX do we want to pass on also frames not matched above ? */
+ ast_frfree(f);
+
+#ifndef HAVE_EPOLL
+ /* Swap who gets priority */
+ cs[2] = cs[0];
+ cs[0] = cs[1];
+ cs[1] = cs[2];
+#endif
+ }
+
+ ast_poll_channel_del(c0, c1);
+
+ return res;
+}
+
+/*! \brief Bridge two channels together (early) */
+int ast_channel_early_bridge(struct ast_channel *c0, struct ast_channel *c1)
+{
+ /* Make sure we can early bridge, if not error out */
+ if (!c0->tech->early_bridge || (c1 && (!c1->tech->early_bridge || c0->tech->early_bridge != c1->tech->early_bridge)))
+ return -1;
+
+ return c0->tech->early_bridge(c0, c1);
+}
+
+/*! \brief Send manager event for bridge link and unlink events.
+ \param type 1 for core, 2 for native
+*/
+static void manager_bridge_event(int onoff, int type, struct ast_channel *c0, struct ast_channel *c1)
+{
+ manager_event(EVENT_FLAG_CALL, "Bridge",
+ "Bridgestate: %s\r\n"
+ "Bridgetype: %s\r\n"
+ "Channel1: %s\r\n"
+ "Channel2: %s\r\n"
+ "Uniqueid1: %s\r\n"
+ "Uniqueid2: %s\r\n"
+ "CallerID1: %s\r\n"
+ "CallerID2: %s\r\n",
+ onoff ? "Link" : "Unlink",
+ type == 1 ? "core" : "native",
+ c0->name, c1->name, c0->uniqueid, c1->uniqueid,
+ S_OR(c0->cid.cid_num, ""),
+ S_OR(c1->cid.cid_num, ""));
+}
+
+/*! \brief Bridge two channels together */
+enum ast_bridge_result ast_channel_bridge(struct ast_channel *c0, struct ast_channel *c1,
+ struct ast_bridge_config *config, struct ast_frame **fo, struct ast_channel **rc)
+{
+ struct ast_channel *who = NULL;
+ enum ast_bridge_result res = AST_BRIDGE_COMPLETE;
+ int nativefailed=0;
+ int firstpass;
+ int o0nativeformats;
+ int o1nativeformats;
+ long time_left_ms=0;
+ struct timeval nexteventts = { 0, };
+ char caller_warning = 0;
+ char callee_warning = 0;
+
+ if (c0->_bridge) {
+ ast_log(LOG_WARNING, "%s is already in a bridge with %s\n",
+ c0->name, c0->_bridge->name);
+ return -1;
+ }
+ if (c1->_bridge) {
+ ast_log(LOG_WARNING, "%s is already in a bridge with %s\n",
+ c1->name, c1->_bridge->name);
+ return -1;
+ }
+
+ /* Stop if we're a zombie or need a soft hangup */
+ if (ast_test_flag(c0, AST_FLAG_ZOMBIE) || ast_check_hangup_locked(c0) ||
+ ast_test_flag(c1, AST_FLAG_ZOMBIE) || ast_check_hangup_locked(c1))
+ return -1;
+
+ *fo = NULL;
+ firstpass = config->firstpass;
+ config->firstpass = 0;
+
+ if (ast_tvzero(config->start_time))
+ config->start_time = ast_tvnow();
+ time_left_ms = config->timelimit;
+
+ caller_warning = ast_test_flag(&config->features_caller, AST_FEATURE_PLAY_WARNING);
+ callee_warning = ast_test_flag(&config->features_callee, AST_FEATURE_PLAY_WARNING);
+
+ if (config->start_sound && firstpass) {
+ if (caller_warning)
+ bridge_playfile(c0, c1, config->start_sound, time_left_ms / 1000);
+ if (callee_warning)
+ bridge_playfile(c1, c0, config->start_sound, time_left_ms / 1000);
+ }
+
+ /* Keep track of bridge */
+ c0->_bridge = c1;
+ c1->_bridge = c0;
+
+
+ o0nativeformats = c0->nativeformats;
+ o1nativeformats = c1->nativeformats;
+
+ if (config->feature_timer) {
+ nexteventts = ast_tvadd(config->start_time, ast_samp2tv(config->feature_timer, 1000));
+ } else if (config->timelimit) {
+ nexteventts = ast_tvadd(config->start_time, ast_samp2tv(config->timelimit, 1000));
+ if (caller_warning || callee_warning)
+ nexteventts = ast_tvsub(nexteventts, ast_samp2tv(config->play_warning, 1000));
+ }
+
+ if (!c0->tech->send_digit_begin)
+ ast_set_flag(c1, AST_FLAG_END_DTMF_ONLY);
+ if (!c1->tech->send_digit_begin)
+ ast_set_flag(c0, AST_FLAG_END_DTMF_ONLY);
+ manager_bridge_event(1, 1, c0, c1);
+
+ for (/* ever */;;) {
+ struct timeval now = { 0, };
+ int to;
+
+ to = -1;
+
+ if (!ast_tvzero(nexteventts)) {
+ now = ast_tvnow();
+ to = ast_tvdiff_ms(nexteventts, now);
+ if (to <= 0) {
+ if (!config->timelimit) {
+ res = AST_BRIDGE_COMPLETE;
+ break;
+ }
+ to = 0;
+ }
+ }
+
+ if (config->timelimit) {
+ time_left_ms = config->timelimit - ast_tvdiff_ms(now, config->start_time);
+ if (time_left_ms < to)
+ to = time_left_ms;
+
+ if (time_left_ms <= 0) {
+ if (caller_warning && config->end_sound)
+ bridge_playfile(c0, c1, config->end_sound, 0);
+ if (callee_warning && config->end_sound)
+ bridge_playfile(c1, c0, config->end_sound, 0);
+ *fo = NULL;
+ if (who)
+ *rc = who;
+ res = 0;
+ break;
+ }
+
+ if (!to) {
+ if (time_left_ms >= 5000 && config->warning_sound && config->play_warning) {
+ int t = (time_left_ms + 500) / 1000; /* round to nearest second */
+ if (caller_warning)
+ bridge_playfile(c0, c1, config->warning_sound, t);
+ if (callee_warning)
+ bridge_playfile(c1, c0, config->warning_sound, t);
+ }
+ if (config->warning_freq && (time_left_ms > (config->warning_freq + 5000)))
+ nexteventts = ast_tvadd(nexteventts, ast_samp2tv(config->warning_freq, 1000));
+ else
+ nexteventts = ast_tvadd(config->start_time, ast_samp2tv(config->timelimit, 1000));
+ }
+ }
+
+ if (c0->_softhangup == AST_SOFTHANGUP_UNBRIDGE || c1->_softhangup == AST_SOFTHANGUP_UNBRIDGE) {
+ if (c0->_softhangup == AST_SOFTHANGUP_UNBRIDGE)
+ c0->_softhangup = 0;
+ if (c1->_softhangup == AST_SOFTHANGUP_UNBRIDGE)
+ c1->_softhangup = 0;
+ c0->_bridge = c1;
+ c1->_bridge = c0;
+ ast_debug(1, "Unbridge signal received. Ending native bridge.\n");
+ continue;
+ }
+
+ /* Stop if we're a zombie or need a soft hangup */
+ if (ast_test_flag(c0, AST_FLAG_ZOMBIE) || ast_check_hangup_locked(c0) ||
+ ast_test_flag(c1, AST_FLAG_ZOMBIE) || ast_check_hangup_locked(c1)) {
+ *fo = NULL;
+ if (who)
+ *rc = who;
+ res = 0;
+ ast_debug(1, "Bridge stops because we're zombie or need a soft hangup: c0=%s, c1=%s, flags: %s,%s,%s,%s\n",
+ c0->name, c1->name,
+ ast_test_flag(c0, AST_FLAG_ZOMBIE) ? "Yes" : "No",
+ ast_check_hangup(c0) ? "Yes" : "No",
+ ast_test_flag(c1, AST_FLAG_ZOMBIE) ? "Yes" : "No",
+ ast_check_hangup(c1) ? "Yes" : "No");
+ break;
+ }
+
+ /* See if the BRIDGEPEER variable needs to be updated */
+ if (!ast_strlen_zero(pbx_builtin_getvar_helper(c0, "BRIDGEPEER")))
+ pbx_builtin_setvar_helper(c0, "BRIDGEPEER", c1->name);
+ if (!ast_strlen_zero(pbx_builtin_getvar_helper(c1, "BRIDGEPEER")))
+ pbx_builtin_setvar_helper(c1, "BRIDGEPEER", c0->name);
+
+ if (c0->tech->bridge &&
+ (config->timelimit == 0) &&
+ (c0->tech->bridge == c1->tech->bridge) &&
+ !nativefailed && !c0->monitor && !c1->monitor &&
+ !c0->audiohooks && !c1->audiohooks &&
+ !c0->masq && !c0->masqr && !c1->masq && !c1->masqr) {
+ /* Looks like they share a bridge method and nothing else is in the way */
+ ast_set_flag(c0, AST_FLAG_NBRIDGE);
+ ast_set_flag(c1, AST_FLAG_NBRIDGE);
+ if ((res = c0->tech->bridge(c0, c1, config->flags, fo, rc, to)) == AST_BRIDGE_COMPLETE) {
+ /* \todo XXX here should check that cid_num is not NULL */
+ manager_event(EVENT_FLAG_CALL, "Unlink",
+ "Channel1: %s\r\n"
+ "Channel2: %s\r\n"
+ "Uniqueid1: %s\r\n"
+ "Uniqueid2: %s\r\n"
+ "CallerID1: %s\r\n"
+ "CallerID2: %s\r\n",
+ c0->name, c1->name, c0->uniqueid, c1->uniqueid, c0->cid.cid_num, c1->cid.cid_num);
+ ast_debug(1, "Returning from native bridge, channels: %s, %s\n", c0->name, c1->name);
+
+ ast_clear_flag(c0, AST_FLAG_NBRIDGE);
+ ast_clear_flag(c1, AST_FLAG_NBRIDGE);
+
+ if (c0->_softhangup == AST_SOFTHANGUP_UNBRIDGE || c1->_softhangup == AST_SOFTHANGUP_UNBRIDGE)
+ continue;
+
+ c0->_bridge = NULL;
+ c1->_bridge = NULL;
+
+ return res;
+ } else {
+ ast_clear_flag(c0, AST_FLAG_NBRIDGE);
+ ast_clear_flag(c1, AST_FLAG_NBRIDGE);
+ }
+ switch (res) {
+ case AST_BRIDGE_RETRY:
+ continue;
+ default:
+ ast_verb(3, "Native bridging %s and %s ended\n", c0->name, c1->name);
+ /* fallthrough */
+ case AST_BRIDGE_FAILED_NOWARN:
+ nativefailed++;
+ break;
+ }
+ }
+
+ if (((c0->writeformat != c1->readformat) || (c0->readformat != c1->writeformat) ||
+ (c0->nativeformats != o0nativeformats) || (c1->nativeformats != o1nativeformats)) &&
+ !(c0->generator || c1->generator)) {
+ if (ast_channel_make_compatible(c0, c1)) {
+ ast_log(LOG_WARNING, "Can't make %s and %s compatible\n", c0->name, c1->name);
+ manager_bridge_event(0, 1, c0, c1);
+ return AST_BRIDGE_FAILED;
+ }
+ o0nativeformats = c0->nativeformats;
+ o1nativeformats = c1->nativeformats;
+ }
+ res = ast_generic_bridge(c0, c1, config, fo, rc, nexteventts);
+ if (res != AST_BRIDGE_RETRY)
+ break;
+ }
+
+ ast_clear_flag(c0, AST_FLAG_END_DTMF_ONLY);
+ ast_clear_flag(c1, AST_FLAG_END_DTMF_ONLY);
+
+ c0->_bridge = NULL;
+ c1->_bridge = NULL;
+
+ /* \todo XXX here should check that cid_num is not NULL */
+ manager_event(EVENT_FLAG_CALL, "Unlink",
+ "Channel1: %s\r\n"
+ "Channel2: %s\r\n"
+ "Uniqueid1: %s\r\n"
+ "Uniqueid2: %s\r\n"
+ "CallerID1: %s\r\n"
+ "CallerID2: %s\r\n",
+ c0->name, c1->name, c0->uniqueid, c1->uniqueid, c0->cid.cid_num, c1->cid.cid_num);
+ ast_debug(1, "Bridge stops bridging channels %s and %s\n", c0->name, c1->name);
+
+ return res;
+}
+
+/*! \brief Sets an option on a channel */
+int ast_channel_setoption(struct ast_channel *chan, int option, void *data, int datalen, int block)
+{
+ int res;
+
+ if (chan->tech->setoption) {
+ res = chan->tech->setoption(chan, option, data, datalen);
+ if (res < 0)
+ return res;
+ } else {
+ errno = ENOSYS;
+ return -1;
+ }
+ if (block) {
+ /* XXX Implement blocking -- just wait for our option frame reply, discarding
+ intermediate packets. XXX */
+ ast_log(LOG_ERROR, "XXX Blocking not implemented yet XXX\n");
+ return -1;
+ }
+ return 0;
+}
+
+struct tonepair_def {
+ int freq1;
+ int freq2;
+ int duration;
+ int vol;
+};
+
+struct tonepair_state {
+ int fac1;
+ int fac2;
+ int v1_1;
+ int v2_1;
+ int v3_1;
+ int v1_2;
+ int v2_2;
+ int v3_2;
+ int origwfmt;
+ int pos;
+ int duration;
+ int modulate;
+ struct ast_frame f;
+ unsigned char offset[AST_FRIENDLY_OFFSET];
+ short data[4000];
+};
+
+static void tonepair_release(struct ast_channel *chan, void *params)
+{
+ struct tonepair_state *ts = params;
+
+ if (chan)
+ ast_set_write_format(chan, ts->origwfmt);
+ ast_free(ts);
+}
+
+static void *tonepair_alloc(struct ast_channel *chan, void *params)
+{
+ struct tonepair_state *ts;
+ struct tonepair_def *td = params;
+
+ if (!(ts = ast_calloc(1, sizeof(*ts))))
+ return NULL;
+ ts->origwfmt = chan->writeformat;
+ if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) {
+ ast_log(LOG_WARNING, "Unable to set '%s' to signed linear format (write)\n", chan->name);
+ tonepair_release(NULL, ts);
+ ts = NULL;
+ } else {
+ ts->fac1 = 2.0 * cos(2.0 * M_PI * (td->freq1 / 8000.0)) * 32768.0;
+ ts->v1_1 = 0;
+ ts->v2_1 = sin(-4.0 * M_PI * (td->freq1 / 8000.0)) * td->vol;
+ ts->v3_1 = sin(-2.0 * M_PI * (td->freq1 / 8000.0)) * td->vol;
+ ts->v2_1 = 0;
+ ts->fac2 = 2.0 * cos(2.0 * M_PI * (td->freq2 / 8000.0)) * 32768.0;
+ ts->v2_2 = sin(-4.0 * M_PI * (td->freq2 / 8000.0)) * td->vol;
+ ts->v3_2 = sin(-2.0 * M_PI * (td->freq2 / 8000.0)) * td->vol;
+ ts->duration = td->duration;
+ ts->modulate = 0;
+ }
+ /* Let interrupts interrupt :) */
+ ast_set_flag(chan, AST_FLAG_WRITE_INT);
+ return ts;
+}
+
+static int tonepair_generator(struct ast_channel *chan, void *data, int len, int samples)
+{
+ struct tonepair_state *ts = data;
+ int x;
+
+ /* we need to prepare a frame with 16 * timelen samples as we're
+ * generating SLIN audio
+ */
+ len = samples * 2;
+
+ if (len > sizeof(ts->data) / 2 - 1) {
+ ast_log(LOG_WARNING, "Can't generate that much data!\n");
+ return -1;
+ }
+ memset(&ts->f, 0, sizeof(ts->f));
+ for (x=0;x<len/2;x++) {
+ ts->v1_1 = ts->v2_1;
+ ts->v2_1 = ts->v3_1;
+ ts->v3_1 = (ts->fac1 * ts->v2_1 >> 15) - ts->v1_1;
+
+ ts->v1_2 = ts->v2_2;
+ ts->v2_2 = ts->v3_2;
+ ts->v3_2 = (ts->fac2 * ts->v2_2 >> 15) - ts->v1_2;
+ if (ts->modulate) {
+ int p;
+ p = ts->v3_2 - 32768;
+ if (p < 0) p = -p;
+ p = ((p * 9) / 10) + 1;
+ ts->data[x] = (ts->v3_1 * p) >> 15;
+ } else
+ ts->data[x] = ts->v3_1 + ts->v3_2;
+ }
+ ts->f.frametype = AST_FRAME_VOICE;
+ ts->f.subclass = AST_FORMAT_SLINEAR;
+ ts->f.datalen = len;
+ ts->f.samples = samples;
+ ts->f.offset = AST_FRIENDLY_OFFSET;
+ ts->f.data = ts->data;
+ ast_write(chan, &ts->f);
+ ts->pos += x;
+ if (ts->duration > 0) {
+ if (ts->pos >= ts->duration * 8)
+ return -1;
+ }
+ return 0;
+}
+
+static struct ast_generator tonepair = {
+ alloc: tonepair_alloc,
+ release: tonepair_release,
+ generate: tonepair_generator,
+};
+
+int ast_tonepair_start(struct ast_channel *chan, int freq1, int freq2, int duration, int vol)
+{
+ struct tonepair_def d = { 0, };
+
+ d.freq1 = freq1;
+ d.freq2 = freq2;
+ d.duration = duration;
+ d.vol = (vol < 1) ? 8192 : vol; /* force invalid to 8192 */
+ if (ast_activate_generator(chan, &tonepair, &d))
+ return -1;
+ return 0;
+}
+
+void ast_tonepair_stop(struct ast_channel *chan)
+{
+ ast_deactivate_generator(chan);
+}
+
+int ast_tonepair(struct ast_channel *chan, int freq1, int freq2, int duration, int vol)
+{
+ int res;
+
+ if ((res = ast_tonepair_start(chan, freq1, freq2, duration, vol)))
+ return res;
+
+ /* Give us some wiggle room */
+ while (chan->generatordata && ast_waitfor(chan, 100) >= 0) {
+ struct ast_frame *f = ast_read(chan);
+ if (f)
+ ast_frfree(f);
+ else
+ return -1;
+ }
+ return 0;
+}
+
+ast_group_t ast_get_group(const char *s)
+{
+ char *piece;
+ char *c;
+ int start=0, finish=0, x;
+ ast_group_t group = 0;
+
+ if (ast_strlen_zero(s))
+ return 0;
+
+ c = ast_strdupa(s);
+
+ while ((piece = strsep(&c, ","))) {
+ if (sscanf(piece, "%d-%d", &start, &finish) == 2) {
+ /* Range */
+ } else if (sscanf(piece, "%d", &start)) {
+ /* Just one */
+ finish = start;
+ } else {
+ ast_log(LOG_ERROR, "Syntax error parsing group configuration '%s' at '%s'. Ignoring.\n", s, piece);
+ continue;
+ }
+ for (x = start; x <= finish; x++) {
+ if ((x > 63) || (x < 0)) {
+ ast_log(LOG_WARNING, "Ignoring invalid group %d (maximum group is 63)\n", x);
+ } else
+ group |= ((ast_group_t) 1 << x);
+ }
+ }
+ return group;
+}
+
+static int (*ast_moh_start_ptr)(struct ast_channel *, const char *, const char *) = NULL;
+static void (*ast_moh_stop_ptr)(struct ast_channel *) = NULL;
+static void (*ast_moh_cleanup_ptr)(struct ast_channel *) = NULL;
+
+void ast_install_music_functions(int (*start_ptr)(struct ast_channel *, const char *, const char *),
+ void (*stop_ptr)(struct ast_channel *),
+ void (*cleanup_ptr)(struct ast_channel *))
+{
+ ast_moh_start_ptr = start_ptr;
+ ast_moh_stop_ptr = stop_ptr;
+ ast_moh_cleanup_ptr = cleanup_ptr;
+}
+
+void ast_uninstall_music_functions(void)
+{
+ ast_moh_start_ptr = NULL;
+ ast_moh_stop_ptr = NULL;
+ ast_moh_cleanup_ptr = NULL;
+}
+
+/*! \brief Turn on music on hold on a given channel */
+int ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
+{
+ if (ast_moh_start_ptr)
+ return ast_moh_start_ptr(chan, mclass, interpclass);
+
+ ast_verb(3, "Music class %s requested but no musiconhold loaded.\n", mclass ? mclass : (interpclass ? interpclass : "default"));
+
+ return 0;
+}
+
+/*! \brief Turn off music on hold on a given channel */
+void ast_moh_stop(struct ast_channel *chan)
+{
+ if (ast_moh_stop_ptr)
+ ast_moh_stop_ptr(chan);
+}
+
+void ast_moh_cleanup(struct ast_channel *chan)
+{
+ if (ast_moh_cleanup_ptr)
+ ast_moh_cleanup_ptr(chan);
+}
+
+void ast_channels_init(void)
+{
+ ast_cli_register_multiple(cli_channel, sizeof(cli_channel) / sizeof(struct ast_cli_entry));
+}
+
+/*! \brief Print call group and pickup group ---*/
+char *ast_print_group(char *buf, int buflen, ast_group_t group)
+{
+ unsigned int i;
+ int first=1;
+ char num[3];
+
+ buf[0] = '\0';
+
+ if (!group) /* Return empty string if no group */
+ return buf;
+
+ for (i = 0; i <= 63; i++) { /* Max group is 63 */
+ if (group & ((ast_group_t) 1 << i)) {
+ if (!first) {
+ strncat(buf, ", ", buflen);
+ } else {
+ first=0;
+ }
+ snprintf(num, sizeof(num), "%u", i);
+ strncat(buf, num, buflen);
+ }
+ }
+ return buf;
+}
+
+void ast_set_variables(struct ast_channel *chan, struct ast_variable *vars)
+{
+ struct ast_variable *cur;
+
+ for (cur = vars; cur; cur = cur->next)
+ pbx_builtin_setvar_helper(chan, cur->name, cur->value);
+}
+
+static void *silence_generator_alloc(struct ast_channel *chan, void *data)
+{
+ /* just store the data pointer in the channel structure */
+ return data;
+}
+
+static void silence_generator_release(struct ast_channel *chan, void *data)
+{
+ /* nothing to do */
+}
+
+static int silence_generator_generate(struct ast_channel *chan, void *data, int len, int samples)
+{
+ short buf[samples];
+ struct ast_frame frame = {
+ .frametype = AST_FRAME_VOICE,
+ .subclass = AST_FORMAT_SLINEAR,
+ .data = buf,
+ .samples = samples,
+ .datalen = sizeof(buf),
+ };
+ memset(buf, 0, sizeof(buf));
+ if (ast_write(chan, &frame))
+ return -1;
+ return 0;
+}
+
+static struct ast_generator silence_generator = {
+ .alloc = silence_generator_alloc,
+ .release = silence_generator_release,
+ .generate = silence_generator_generate,
+};
+
+struct ast_silence_generator {
+ int old_write_format;
+};
+
+struct ast_silence_generator *ast_channel_start_silence_generator(struct ast_channel *chan)
+{
+ struct ast_silence_generator *state;
+
+ if (!(state = ast_calloc(1, sizeof(*state)))) {
+ return NULL;
+ }
+
+ state->old_write_format = chan->writeformat;
+
+ if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
+ ast_log(LOG_ERROR, "Could not set write format to SLINEAR\n");
+ ast_free(state);
+ return NULL;
+ }
+
+ ast_activate_generator(chan, &silence_generator, state);
+
+ ast_debug(1, "Started silence generator on '%s'\n", chan->name);
+
+ return state;
+}
+
+void ast_channel_stop_silence_generator(struct ast_channel *chan, struct ast_silence_generator *state)
+{
+ if (!state)
+ return;
+
+ ast_deactivate_generator(chan);
+
+ ast_debug(1, "Stopped silence generator on '%s'\n", chan->name);
+
+ if (ast_set_write_format(chan, state->old_write_format) < 0)
+ ast_log(LOG_ERROR, "Could not return write format to its original state\n");
+
+ ast_free(state);
+}
+
+
+/*! \ brief Convert channel reloadreason (ENUM) to text string for manager event */
+const char *channelreloadreason2txt(enum channelreloadreason reason)
+{
+ switch (reason) {
+ case CHANNEL_MODULE_LOAD:
+ return "LOAD (Channel module load)";
+
+ case CHANNEL_MODULE_RELOAD:
+ return "RELOAD (Channel module reload)";
+
+ case CHANNEL_CLI_RELOAD:
+ return "CLIRELOAD (Channel module reload by CLI command)";
+
+ default:
+ return "MANAGERRELOAD (Channel module reload by manager)";
+ }
+};
+
+#ifdef DEBUG_CHANNEL_LOCKS
+
+/*! \brief Unlock AST channel (and print debugging output)
+\note You need to enable DEBUG_CHANNEL_LOCKS for this function
+*/
+int ast_channel_unlock(struct ast_channel *chan)
+{
+ int res = 0;
+ ast_debug(3, "::::==== Unlocking AST channel %s\n", chan->name);
+
+ if (!chan) {
+ ast_debug(1, "::::==== Unlocking non-existing channel \n");
+ return 0;
+ }
+
+ res = ast_mutex_unlock(&chan->lock_dont_use);
+
+ if (option_debug > 2) {
+#ifdef DEBUG_THREADS
+ int count = 0;
+ if ((count = chan->lock_dont_use.reentrancy))
+ ast_debug(3, ":::=== Still have %d locks (recursive)\n", count);
+#endif
+ if (!res)
+ ast_debug(3, "::::==== Channel %s was unlocked\n", chan->name);
+ if (res == EINVAL) {
+ ast_debug(3, "::::==== Channel %s had no lock by this thread. Failed unlocking\n", chan->name);
+ }
+ }
+ if (res == EPERM) {
+ /* We had no lock, so okay any way*/
+ ast_debug(4, "::::==== Channel %s was not locked at all \n", chan->name);
+ res = 0;
+ }
+ return res;
+}
+
+/*! \brief Lock AST channel (and print debugging output)
+\note You need to enable DEBUG_CHANNEL_LOCKS for this function */
+int ast_channel_lock(struct ast_channel *chan)
+{
+ int res;
+
+ ast_debug(4, "====:::: Locking AST channel %s\n", chan->name);
+
+ res = ast_mutex_lock(&chan->lock_dont_use);
+
+ if (option_debug > 3) {
+#ifdef DEBUG_THREADS
+ int count = 0;
+ if ((count = chan->lock_dont_use.reentrancy))
+ ast_debug(4, ":::=== Now have %d locks (recursive)\n", count);
+#endif
+ if (!res)
+ ast_debug(4, "::::==== Channel %s was locked\n", chan->name);
+ if (res == EDEADLK) {
+ /* We had no lock, so okey any way */
+ ast_debug(4, "::::==== Channel %s was not locked by us. Lock would cause deadlock.\n", chan->name);
+ }
+ if (res == EINVAL) {
+ ast_debug(4, "::::==== Channel %s lock failed. No mutex.\n", chan->name);
+ }
+ }
+ return res;
+}
+
+/*! \brief Lock AST channel (and print debugging output)
+\note You need to enable DEBUG_CHANNEL_LOCKS for this function */
+int ast_channel_trylock(struct ast_channel *chan)
+{
+ int res;
+
+ ast_debug(3, "====:::: Trying to lock AST channel %s\n", chan->name);
+
+ res = ast_mutex_trylock(&chan->lock_dont_use);
+
+ if (option_debug > 2) {
+#ifdef DEBUG_THREADS
+ int count = 0;
+ if ((count = chan->lock_dont_use.reentrancy))
+ ast_debug(3, ":::=== Now have %d locks (recursive)\n", count);
+#endif
+ if (!res)
+ ast_debug(3, "::::==== Channel %s was locked\n", chan->name);
+ if (res == EBUSY) {
+ /* We failed to lock */
+ ast_debug(3, "::::==== Channel %s failed to lock. Not waiting around...\n", chan->name);
+ }
+ if (res == EDEADLK) {
+ /* We had no lock, so okey any way*/
+ ast_debug(3, "::::==== Channel %s was not locked. Lock would cause deadlock.\n", chan->name);
+ }
+ if (res == EINVAL)
+ ast_debug(3, "::::==== Channel %s lock failed. No mutex.\n", chan->name);
+ }
+ return res;
+}
+
+#endif
+
+/*
+ * Wrappers for various ast_say_*() functions that call the full version
+ * of the same functions.
+ * The proper place would be say.c, but that file is optional and one
+ * must be able to build asterisk even without it (using a loadable 'say'
+ * implementation that only supplies the 'full' version of the functions.
+ */
+
+int ast_say_number(struct ast_channel *chan, int num,
+ const char *ints, const char *language, const char *options)
+{
+ return ast_say_number_full(chan, num, ints, language, options, -1, -1);
+}
+
+int ast_say_enumeration(struct ast_channel *chan, int num,
+ const char *ints, const char *language, const char *options)
+{
+ return ast_say_enumeration_full(chan, num, ints, language, options, -1, -1);
+}
+
+int ast_say_digits(struct ast_channel *chan, int num,
+ const char *ints, const char *lang)
+{
+ return ast_say_digits_full(chan, num, ints, lang, -1, -1);
+}
+
+int ast_say_digit_str(struct ast_channel *chan, const char *str,
+ const char *ints, const char *lang)
+{
+ return ast_say_digit_str_full(chan, str, ints, lang, -1, -1);
+}
+
+int ast_say_character_str(struct ast_channel *chan, const char *str,
+ const char *ints, const char *lang)
+{
+ return ast_say_character_str_full(chan, str, ints, lang, -1, -1);
+}
+
+int ast_say_phonetic_str(struct ast_channel *chan, const char *str,
+ const char *ints, const char *lang)
+{
+ return ast_say_phonetic_str_full(chan, str, ints, lang, -1, -1);
+}
+
+int ast_say_digits_full(struct ast_channel *chan, int num,
+ const char *ints, const char *lang, int audiofd, int ctrlfd)
+{
+ char buf[256];
+
+ snprintf(buf, sizeof(buf), "%d", num);
+ return ast_say_digit_str_full(chan, buf, ints, lang, audiofd, ctrlfd);
+}
diff --git a/trunk/main/chanvars.c b/trunk/main/chanvars.c
new file mode 100644
index 000000000..14a89f767
--- /dev/null
+++ b/trunk/main/chanvars.c
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+/*! \file
+ *
+ * \brief Channel Variables
+ *
+ * \author Mark Spencer <markster@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/chanvars.h"
+#include "asterisk/strings.h"
+#include "asterisk/utils.h"
+
+struct ast_var_t *ast_var_assign(const char *name, const char *value)
+{
+ struct ast_var_t *var;
+ int name_len = strlen(name) + 1;
+ int value_len = strlen(value) + 1;
+
+ if (!(var = ast_calloc(sizeof(*var) + name_len + value_len, sizeof(char)))) {
+ return NULL;
+ }
+
+ ast_copy_string(var->name, name, name_len);
+ var->value = var->name + name_len;
+ ast_copy_string(var->value, value, value_len);
+
+ return var;
+}
+
+void ast_var_delete(struct ast_var_t *var)
+{
+ if (var)
+ ast_free(var);
+}
+
+const char *ast_var_name(const struct ast_var_t *var)
+{
+ const char *name;
+
+ if (var == NULL || (name = var->name) == NULL)
+ return NULL;
+ /* Return the name without the initial underscores */
+ if (name[0] == '_') {
+ name++;
+ if (name[0] == '_')
+ name++;
+ }
+ return name;
+}
+
+const char *ast_var_full_name(const struct ast_var_t *var)
+{
+ return (var ? var->name : NULL);
+}
+
+const char *ast_var_value(const struct ast_var_t *var)
+{
+ return (var ? var->value : NULL);
+}
+
+
diff --git a/trunk/main/cli.c b/trunk/main/cli.c
new file mode 100644
index 000000000..8813c6a3f
--- /dev/null
+++ b/trunk/main/cli.c
@@ -0,0 +1,1918 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006, 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.
+ */
+
+/*! \file
+ *
+ * \brief Standard Command Line Interface
+ *
+ * \author Mark Spencer <markster@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/_private.h"
+#include "asterisk/paths.h" /* use ast_config_AST_MODULE_DIR */
+#include <sys/signal.h>
+#include <signal.h>
+#include <ctype.h>
+#include <regex.h>
+
+#include "asterisk/cli.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/module.h"
+#include "asterisk/pbx.h"
+#include "asterisk/channel.h"
+#include "asterisk/utils.h"
+#include "asterisk/app.h"
+#include "asterisk/lock.h"
+#include "editline/readline/readline.h"
+#include "asterisk/threadstorage.h"
+
+/*!
+ * \brief map a debug or verbose value to a filename
+ */
+struct ast_debug_file {
+ unsigned int level;
+ AST_RWLIST_ENTRY(ast_debug_file) entry;
+ char filename[0];
+};
+
+AST_RWLIST_HEAD(debug_file_list, ast_debug_file);
+
+/*! list of filenames and their debug settings */
+static struct debug_file_list debug_files;
+/*! list of filenames and their verbose settings */
+static struct debug_file_list verbose_files;
+
+AST_THREADSTORAGE(ast_cli_buf);
+
+/*! \brief Initial buffer size for resulting strings in ast_cli() */
+#define AST_CLI_INITLEN 256
+
+void ast_cli(int fd, const char *fmt, ...)
+{
+ int res;
+ struct ast_str *buf;
+ va_list ap;
+
+ if (!(buf = ast_str_thread_get(&ast_cli_buf, AST_CLI_INITLEN)))
+ return;
+
+ va_start(ap, fmt);
+ res = ast_str_set_va(&buf, 0, fmt, ap);
+ va_end(ap);
+
+ if (res != AST_DYNSTR_BUILD_FAILED)
+ ast_carefulwrite(fd, buf->str, strlen(buf->str), 100);
+}
+
+unsigned int ast_debug_get_by_file(const char *file)
+{
+ struct ast_debug_file *adf;
+ unsigned int res = 0;
+
+ AST_RWLIST_RDLOCK(&debug_files);
+ AST_LIST_TRAVERSE(&debug_files, adf, entry) {
+ if (!strncasecmp(adf->filename, file, strlen(adf->filename))) {
+ res = adf->level;
+ break;
+ }
+ }
+ AST_RWLIST_UNLOCK(&debug_files);
+
+ return res;
+}
+
+unsigned int ast_verbose_get_by_file(const char *file)
+{
+ struct ast_debug_file *adf;
+ unsigned int res = 0;
+
+ AST_RWLIST_RDLOCK(&verbose_files);
+ AST_LIST_TRAVERSE(&verbose_files, adf, entry) {
+ if (!strncasecmp(adf->filename, file, strlen(file))) {
+ res = adf->level;
+ break;
+ }
+ }
+ AST_RWLIST_UNLOCK(&verbose_files);
+
+ return res;
+}
+
+static AST_RWLIST_HEAD_STATIC(helpers, ast_cli_entry);
+
+static char *complete_fn(const char *word, int state)
+{
+ char *c, *d;
+ char filename[256];
+
+ if (word[0] == '/')
+ ast_copy_string(filename, word, sizeof(filename));
+ else
+ snprintf(filename, sizeof(filename), "%s/%s", ast_config_AST_MODULE_DIR, word);
+
+ c = d = filename_completion_function(filename, state);
+
+ if (c && word[0] != '/')
+ c += (strlen(ast_config_AST_MODULE_DIR) + 1);
+ if (c)
+ c = ast_strdup(c);
+ if (d)
+ free(d);
+
+ return c;
+}
+
+static char *handle_load(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ /* "module load <mod>" */
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "module load";
+ e->usage =
+ "Usage: module load <module name>\n"
+ " Loads the specified module into Asterisk.\n";
+ return NULL;
+
+ case CLI_GENERATE:
+ if (a->pos != e->args)
+ return NULL;
+ return complete_fn(a->word, a->n);
+ }
+ if (a->argc != e->args + 1)
+ return CLI_SHOWUSAGE;
+ if (ast_load_resource(a->argv[e->args])) {
+ ast_cli(a->fd, "Unable to load module %s\n", a->argv[e->args]);
+ return CLI_FAILURE;
+ }
+ return CLI_SUCCESS;
+}
+
+static char *handle_load_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char *res = handle_load(e, cmd, a);
+ if (cmd == CLI_INIT)
+ e->command = "load";
+ return res;
+}
+
+static char *handle_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int x;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "module reload";
+ e->usage =
+ "Usage: module reload [module ...]\n"
+ " Reloads configuration files for all listed modules which support\n"
+ " reloading, or for all supported modules if none are listed.\n";
+ return NULL;
+
+ case CLI_GENERATE:
+ return ast_module_helper(a->line, a->word, a->pos, a->n, a->pos, 1);
+ }
+ if (a->argc == e->args) {
+ ast_module_reload(NULL);
+ return CLI_SUCCESS;
+ }
+ for (x = e->args; x < a->argc; x++) {
+ int res = ast_module_reload(a->argv[x]);
+ /* XXX reload has multiple error returns, including -1 on error and 2 on success */
+ switch (res) {
+ case 0:
+ ast_cli(a->fd, "No such module '%s'\n", a->argv[x]);
+ break;
+ case 1:
+ ast_cli(a->fd, "Module '%s' does not support reload\n", a->argv[x]);
+ break;
+ }
+ }
+ return CLI_SUCCESS;
+}
+
+static char *handle_reload_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char *s = handle_reload(e, cmd, a);
+ if (cmd == CLI_INIT) /* override command name */
+ e->command = "reload";
+ return s;
+}
+
+/*!
+ * \brief Find the debug or verbose file setting
+ * \arg debug 1 for debug, 0 for verbose
+ */
+static struct ast_debug_file *find_debug_file(const char *fn, unsigned int debug)
+{
+ struct ast_debug_file *df = NULL;
+ struct debug_file_list *dfl = debug ? &debug_files : &verbose_files;
+
+ AST_LIST_TRAVERSE(dfl, df, entry) {
+ if (!strcasecmp(df->filename, fn))
+ break;
+ }
+
+ return df;
+}
+
+static char *handle_verbose(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int oldval;
+ int newlevel;
+ int atleast = 0;
+ int fd = a->fd;
+ int argc = a->argc;
+ char **argv = a->argv;
+ int *dst;
+ char *what;
+ struct debug_file_list *dfl;
+ struct ast_debug_file *adf;
+ char *fn;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core set {debug|verbose} [off|atleast]";
+ e->usage =
+ "Usage: core set {debug|verbose} [atleast] <level> [filename]\n"
+ " core set {debug|verbose} off\n"
+ " Sets level of debug or verbose messages to be displayed or \n"
+ " sets a filename to display debug messages from.\n"
+ " 0 or off means no messages should be displayed.\n"
+ " Equivalent to -d[d[...]] or -v[v[v...]] on startup\n";
+ return NULL;
+
+ case CLI_GENERATE:
+ return NULL;
+ }
+ /* all the above return, so we proceed with the handler.
+ * we are guaranteed to be called with argc >= e->args;
+ */
+
+ if (argc < e->args)
+ return CLI_SHOWUSAGE;
+ if (!strcasecmp(argv[e->args - 2], "debug")) {
+ dst = &option_debug;
+ oldval = option_debug;
+ what = "Core debug";
+ } else {
+ dst = &option_verbose;
+ oldval = option_verbose;
+ what = "Verbosity";
+ }
+ if (argc == e->args && !strcasecmp(argv[e->args - 1], "off")) {
+ unsigned int debug = (*what == 'C');
+ newlevel = 0;
+
+ dfl = debug ? &debug_files : &verbose_files;
+
+ AST_RWLIST_WRLOCK(dfl);
+ while ((adf = AST_RWLIST_REMOVE_HEAD(dfl, entry)))
+ ast_free(adf);
+ ast_clear_flag(&ast_options, debug ? AST_OPT_FLAG_DEBUG_FILE : AST_OPT_FLAG_VERBOSE_FILE);
+ AST_RWLIST_UNLOCK(dfl);
+
+ goto done;
+ }
+ if (!strcasecmp(argv[e->args-1], "atleast"))
+ atleast = 1;
+ if (argc != e->args + atleast && argc != e->args + atleast + 1)
+ return CLI_SHOWUSAGE;
+ if (sscanf(argv[e->args + atleast - 1], "%d", &newlevel) != 1)
+ return CLI_SHOWUSAGE;
+ if (argc == e->args + atleast + 1) {
+ unsigned int debug = (*what == 'C');
+ dfl = debug ? &debug_files : &verbose_files;
+
+ fn = argv[e->args + atleast];
+
+ AST_RWLIST_WRLOCK(dfl);
+
+ if ((adf = find_debug_file(fn, debug)) && !newlevel) {
+ AST_RWLIST_REMOVE(dfl, adf, entry);
+ if (AST_RWLIST_EMPTY(dfl))
+ ast_clear_flag(&ast_options, debug ? AST_OPT_FLAG_DEBUG_FILE : AST_OPT_FLAG_VERBOSE_FILE);
+ AST_RWLIST_UNLOCK(dfl);
+ ast_cli(fd, "%s was %d and has been set to 0 for '%s'\n", what, adf->level, fn);
+ ast_free(adf);
+ return CLI_SUCCESS;
+ }
+
+ if (adf) {
+ if ((atleast && newlevel < adf->level) || adf->level == newlevel) {
+ ast_cli(fd, "%s is %d for '%s'\n", what, adf->level, fn);
+ AST_RWLIST_UNLOCK(dfl);
+ return CLI_SUCCESS;
+ }
+ } else if (!(adf = ast_calloc(1, sizeof(*adf) + strlen(fn) + 1))) {
+ AST_RWLIST_UNLOCK(dfl);
+ return CLI_FAILURE;
+ }
+
+ oldval = adf->level;
+ adf->level = newlevel;
+ strcpy(adf->filename, fn);
+
+ ast_set_flag(&ast_options, debug ? AST_OPT_FLAG_DEBUG_FILE : AST_OPT_FLAG_VERBOSE_FILE);
+
+ AST_RWLIST_INSERT_TAIL(dfl, adf, entry);
+ AST_RWLIST_UNLOCK(dfl);
+
+ ast_cli(fd, "%s was %d and has been set to %d for '%s'\n", what, oldval, adf->level, adf->filename);
+
+ return CLI_SUCCESS;
+ }
+
+done:
+ if (!atleast || newlevel > *dst)
+ *dst = newlevel;
+ if (oldval > 0 && *dst == 0)
+ ast_cli(fd, "%s is now OFF\n", what);
+ else if (*dst > 0) {
+ if (oldval == *dst)
+ ast_cli(fd, "%s is at least %d\n", what, *dst);
+ else
+ ast_cli(fd, "%s was %d and is now %d\n", what, oldval, *dst);
+ }
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_logger_mute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "logger mute";
+ e->usage =
+ "Usage: logger mute\n"
+ " Disables logging output to the current console, making it possible to\n"
+ " gather information without being disturbed by scrolling lines.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc < 2 || a->argc > 3)
+ return CLI_SHOWUSAGE;
+
+ if (a->argc == 3 && !strcasecmp(a->argv[2], "silent"))
+ ast_console_toggle_mute(a->fd, 1);
+ else
+ ast_console_toggle_mute(a->fd, 0);
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_unload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ /* "module unload mod_1 [mod_2 .. mod_N]" */
+ int x;
+ int force = AST_FORCE_SOFT;
+ char *s;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "module unload";
+ e->usage =
+ "Usage: module unload [-f|-h] <module_1> [<module_2> ... ]\n"
+ " Unloads the specified module from Asterisk. The -f\n"
+ " option causes the module to be unloaded even if it is\n"
+ " in use (may cause a crash) and the -h module causes the\n"
+ " module to be unloaded even if the module says it cannot, \n"
+ " which almost always will cause a crash.\n";
+ return NULL;
+
+ case CLI_GENERATE:
+ return ast_module_helper(a->line, a->word, a->pos, a->n, a->pos, 0);
+ }
+ if (a->argc < e->args + 1)
+ return CLI_SHOWUSAGE;
+ x = e->args; /* first argument */
+ s = a->argv[x];
+ if (s[0] == '-') {
+ if (s[1] == 'f')
+ force = AST_FORCE_FIRM;
+ else if (s[1] == 'h')
+ force = AST_FORCE_HARD;
+ else
+ return CLI_SHOWUSAGE;
+ if (a->argc < e->args + 2) /* need at least one module name */
+ return CLI_SHOWUSAGE;
+ x++; /* skip this argument */
+ }
+
+ for (; x < a->argc; x++) {
+ if (ast_unload_resource(a->argv[x], force)) {
+ ast_cli(a->fd, "Unable to unload resource %s\n", a->argv[x]);
+ return CLI_FAILURE;
+ }
+ }
+ return CLI_SUCCESS;
+}
+
+static char *handle_unload_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char *res = handle_unload(e, cmd, a);
+ if (cmd == CLI_INIT)
+ e->command = "unload"; /* XXX override */
+ return res;
+}
+
+#define MODLIST_FORMAT "%-30s %-40.40s %-10d\n"
+#define MODLIST_FORMAT2 "%-30s %-40.40s %-10s\n"
+
+AST_MUTEX_DEFINE_STATIC(climodentrylock);
+static int climodentryfd = -1;
+
+static int modlist_modentry(const char *module, const char *description, int usecnt, const char *like)
+{
+ /* Comparing the like with the module */
+ if (strcasestr(module, like) ) {
+ ast_cli(climodentryfd, MODLIST_FORMAT, module, description, usecnt);
+ return 1;
+ }
+ return 0;
+}
+
+static void print_uptimestr(int fd, struct timeval timeval, const char *prefix, int printsec)
+{
+ int x; /* the main part - years, weeks, etc. */
+ struct ast_str *out;
+
+#define SECOND (1)
+#define MINUTE (SECOND*60)
+#define HOUR (MINUTE*60)
+#define DAY (HOUR*24)
+#define WEEK (DAY*7)
+#define YEAR (DAY*365)
+#define NEEDCOMMA(x) ((x)? ",": "") /* define if we need a comma */
+ if (timeval.tv_sec < 0) /* invalid, nothing to show */
+ return;
+
+ if (printsec) { /* plain seconds output */
+ ast_cli(fd, "%s: %lu\n", prefix, (u_long)timeval.tv_sec);
+ return;
+ }
+ out = ast_str_alloca(256);
+ if (timeval.tv_sec > YEAR) {
+ x = (timeval.tv_sec / YEAR);
+ timeval.tv_sec -= (x * YEAR);
+ ast_str_append(&out, 0, "%d year%s%s ", x, ESS(x),NEEDCOMMA(timeval.tv_sec));
+ }
+ if (timeval.tv_sec > WEEK) {
+ x = (timeval.tv_sec / WEEK);
+ timeval.tv_sec -= (x * WEEK);
+ ast_str_append(&out, 0, "%d week%s%s ", x, ESS(x),NEEDCOMMA(timeval.tv_sec));
+ }
+ if (timeval.tv_sec > DAY) {
+ x = (timeval.tv_sec / DAY);
+ timeval.tv_sec -= (x * DAY);
+ ast_str_append(&out, 0, "%d day%s%s ", x, ESS(x),NEEDCOMMA(timeval.tv_sec));
+ }
+ if (timeval.tv_sec > HOUR) {
+ x = (timeval.tv_sec / HOUR);
+ timeval.tv_sec -= (x * HOUR);
+ ast_str_append(&out, 0, "%d hour%s%s ", x, ESS(x),NEEDCOMMA(timeval.tv_sec));
+ }
+ if (timeval.tv_sec > MINUTE) {
+ x = (timeval.tv_sec / MINUTE);
+ timeval.tv_sec -= (x * MINUTE);
+ ast_str_append(&out, 0, "%d minute%s%s ", x, ESS(x),NEEDCOMMA(timeval.tv_sec));
+ }
+ x = timeval.tv_sec;
+ if (x > 0 || out->used == 0) /* if there is nothing, print 0 seconds */
+ ast_str_append(&out, 0, "%d second%s ", x, ESS(x));
+ ast_cli(fd, "%s: %s\n", prefix, out->str);
+}
+
+static char * handle_showuptime(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct timeval curtime = ast_tvnow();
+ int printsec;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show uptime [seconds]";
+ e->usage =
+ "Usage: core show uptime [seconds]\n"
+ " Shows Asterisk uptime information.\n"
+ " The seconds word returns the uptime in seconds only.\n";
+ return NULL;
+
+ case CLI_GENERATE:
+ return NULL;
+ }
+ /* regular handler */
+ if (a->argc == e->args && !strcasecmp(a->argv[e->args-1],"seconds"))
+ printsec = 1;
+ else if (a->argc == e->args-1)
+ printsec = 0;
+ else
+ return CLI_SHOWUSAGE;
+ if (ast_startuptime.tv_sec)
+ print_uptimestr(a->fd, ast_tvsub(curtime, ast_startuptime), "System uptime", printsec);
+ if (ast_lastreloadtime.tv_sec)
+ print_uptimestr(a->fd, ast_tvsub(curtime, ast_lastreloadtime), "Last reload", printsec);
+ return CLI_SUCCESS;
+}
+
+static char *handle_modlist(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char *like;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "module show [like]";
+ e->usage =
+ "Usage: module show [like keyword]\n"
+ " Shows Asterisk modules currently in use, and usage statistics.\n";
+ return NULL;
+
+ case CLI_GENERATE:
+ if (a->pos == e->args)
+ return ast_module_helper(a->line, a->word, a->pos, a->n, a->pos, 0);
+ else
+ return NULL;
+ }
+ /* all the above return, so we proceed with the handler.
+ * we are guaranteed to have argc >= e->args
+ */
+ if (a->argc == e->args - 1)
+ like = "";
+ else if (a->argc == e->args + 1 && !strcasecmp(a->argv[e->args-1], "like") )
+ like = a->argv[e->args];
+ else
+ return CLI_SHOWUSAGE;
+
+ ast_mutex_lock(&climodentrylock);
+ climodentryfd = a->fd; /* global, protected by climodentrylock */
+ ast_cli(a->fd, MODLIST_FORMAT2, "Module", "Description", "Use Count");
+ ast_cli(a->fd,"%d modules loaded\n", ast_update_module_list(modlist_modentry, like));
+ climodentryfd = -1;
+ ast_mutex_unlock(&climodentrylock);
+ return CLI_SUCCESS;
+}
+#undef MODLIST_FORMAT
+#undef MODLIST_FORMAT2
+
+static char *handle_showcalls(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct timeval curtime = ast_tvnow();
+ int showuptime, printsec;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show calls [uptime]";
+ e->usage =
+ "Usage: core show calls [uptime] [seconds]\n"
+ " Lists number of currently active calls and total number of calls\n"
+ " processed through PBX since last restart. If 'uptime' is specified\n"
+ " the system uptime is also displayed. If 'seconds' is specified in\n"
+ " addition to 'uptime', the system uptime is displayed in seconds.\n";
+ return NULL;
+
+ case CLI_GENERATE:
+ if (a->pos != e->args)
+ return NULL;
+ return a->n == 0 ? ast_strdup("seconds") : NULL;
+ }
+
+ /* regular handler */
+ if (a->argc >= e->args && !strcasecmp(a->argv[e->args-1],"uptime")) {
+ showuptime = 1;
+
+ if (a->argc == e->args+1 && !strcasecmp(a->argv[e->args],"seconds"))
+ printsec = 1;
+ else if (a->argc == e->args)
+ printsec = 0;
+ else
+ return CLI_SHOWUSAGE;
+ } else if (a->argc == e->args-1) {
+ showuptime = 0;
+ printsec = 0;
+ } else
+ return CLI_SHOWUSAGE;
+
+ if (option_maxcalls) {
+ ast_cli(a->fd, "%d of %d max active call%s (%5.2f%% of capacity)\n",
+ ast_active_calls(), option_maxcalls, ESS(ast_active_calls()),
+ ((double)ast_active_calls() / (double)option_maxcalls) * 100.0);
+ } else {
+ ast_cli(a->fd, "%d active call%s\n", ast_active_calls(), ESS(ast_active_calls()));
+ }
+
+ ast_cli(a->fd, "%d call%s processed\n", ast_processed_calls(), ESS(ast_processed_calls()));
+
+ if (ast_startuptime.tv_sec && showuptime) {
+ print_uptimestr(a->fd, ast_tvsub(curtime, ast_startuptime), "System uptime", printsec);
+ }
+
+ return RESULT_SUCCESS;
+}
+
+static char *handle_chanlist(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+#define FORMAT_STRING "%-20.20s %-20.20s %-7.7s %-30.30s\n"
+#define FORMAT_STRING2 "%-20.20s %-20.20s %-7.7s %-30.30s\n"
+#define CONCISE_FORMAT_STRING "%s!%s!%s!%d!%s!%s!%s!%s!%s!%d!%s!%s!%s\n"
+#define VERBOSE_FORMAT_STRING "%-20.20s %-20.20s %-16.16s %4d %-7.7s %-12.12s %-25.25s %-15.15s %8.8s %-11.11s %-20.20s\n"
+#define VERBOSE_FORMAT_STRING2 "%-20.20s %-20.20s %-16.16s %-4.4s %-7.7s %-12.12s %-25.25s %-15.15s %8.8s %-11.11s %-20.20s\n"
+
+ struct ast_channel *c = NULL;
+ int numchans = 0, concise = 0, verbose = 0, count = 0;
+ int fd, argc;
+ char **argv;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show channels [concise|verbose|count]";
+ e->usage =
+ "Usage: core show channels [concise|verbose|count]\n"
+ " Lists currently defined channels and some information about them. If\n"
+ " 'concise' is specified, the format is abridged and in a more easily\n"
+ " machine parsable format. If 'verbose' is specified, the output includes\n"
+ " more and longer fields. If 'count' is specified only the channel and call\n"
+ " count is output.\n"
+ " The 'concise' option is deprecated and will be removed from future versions\n"
+ " of Asterisk.\n";
+ return NULL;
+
+ case CLI_GENERATE:
+ return NULL;
+ }
+ fd = a->fd;
+ argc = a->argc;
+ argv = a->argv;
+
+ if (a->argc == e->args) {
+ if (!strcasecmp(argv[e->args-1],"concise"))
+ concise = 1;
+ else if (!strcasecmp(argv[e->args-1],"verbose"))
+ verbose = 1;
+ else if (!strcasecmp(argv[e->args-1],"count"))
+ count = 1;
+ else
+ return CLI_SHOWUSAGE;
+ } else if (a->argc != e->args - 1)
+ return CLI_SHOWUSAGE;
+
+ if (!count) {
+ if (!concise && !verbose)
+ ast_cli(fd, FORMAT_STRING2, "Channel", "Location", "State", "Application(Data)");
+ else if (verbose)
+ ast_cli(fd, VERBOSE_FORMAT_STRING2, "Channel", "Context", "Extension", "Priority", "State", "Application", "Data",
+ "CallerID", "Duration", "Accountcode", "BridgedTo");
+ }
+
+ while ((c = ast_channel_walk_locked(c)) != NULL) {
+ struct ast_channel *bc = ast_bridged_channel(c);
+ char durbuf[10] = "-";
+
+ if (!count) {
+ if ((concise || verbose) && c->cdr && !ast_tvzero(c->cdr->start)) {
+ int duration = (int)(ast_tvdiff_ms(ast_tvnow(), c->cdr->start) / 1000);
+ if (verbose) {
+ int durh = duration / 3600;
+ int durm = (duration % 3600) / 60;
+ int durs = duration % 60;
+ snprintf(durbuf, sizeof(durbuf), "%02d:%02d:%02d", durh, durm, durs);
+ } else {
+ snprintf(durbuf, sizeof(durbuf), "%d", duration);
+ }
+ }
+ if (concise) {
+ ast_cli(fd, CONCISE_FORMAT_STRING, c->name, c->context, c->exten, c->priority, ast_state2str(c->_state),
+ c->appl ? c->appl : "(None)",
+ S_OR(c->data, ""), /* XXX different from verbose ? */
+ S_OR(c->cid.cid_num, ""),
+ S_OR(c->accountcode, ""),
+ c->amaflags,
+ durbuf,
+ bc ? bc->name : "(None)",
+ c->uniqueid);
+ } else if (verbose) {
+ ast_cli(fd, VERBOSE_FORMAT_STRING, c->name, c->context, c->exten, c->priority, ast_state2str(c->_state),
+ c->appl ? c->appl : "(None)",
+ c->data ? S_OR(c->data, "(Empty)" ): "(None)",
+ S_OR(c->cid.cid_num, ""),
+ durbuf,
+ S_OR(c->accountcode, ""),
+ bc ? bc->name : "(None)");
+ } else {
+ char locbuf[40] = "(None)";
+ char appdata[40] = "(None)";
+
+ if (!ast_strlen_zero(c->context) && !ast_strlen_zero(c->exten))
+ snprintf(locbuf, sizeof(locbuf), "%s@%s:%d", c->exten, c->context, c->priority);
+ if (c->appl)
+ snprintf(appdata, sizeof(appdata), "%s(%s)", c->appl, S_OR(c->data, ""));
+ ast_cli(fd, FORMAT_STRING, c->name, locbuf, ast_state2str(c->_state), appdata);
+ }
+ }
+ numchans++;
+ ast_channel_unlock(c);
+ }
+ if (!concise) {
+ ast_cli(fd, "%d active channel%s\n", numchans, ESS(numchans));
+ if (option_maxcalls)
+ ast_cli(fd, "%d of %d max active call%s (%5.2f%% of capacity)\n",
+ ast_active_calls(), option_maxcalls, ESS(ast_active_calls()),
+ ((double)ast_active_calls() / (double)option_maxcalls) * 100.0);
+ else
+ ast_cli(fd, "%d active call%s\n", ast_active_calls(), ESS(ast_active_calls()));
+
+ ast_cli(fd, "%d call%s processed\n", ast_processed_calls(), ESS(ast_processed_calls()));
+ }
+ return CLI_SUCCESS;
+
+#undef FORMAT_STRING
+#undef FORMAT_STRING2
+#undef CONCISE_FORMAT_STRING
+#undef VERBOSE_FORMAT_STRING
+#undef VERBOSE_FORMAT_STRING2
+}
+
+static char *handle_softhangup(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_channel *c=NULL;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "soft hangup";
+ e->usage =
+ "Usage: soft hangup <channel>\n"
+ " Request that a channel be hung up. The hangup takes effect\n"
+ " the next time the driver reads or writes from the channel\n";
+ return NULL;
+ case CLI_GENERATE:
+ return ast_complete_channels(a->line, a->word, a->pos, a->n, 2);
+ }
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+ c = ast_get_channel_by_name_locked(a->argv[2]);
+ if (c) {
+ ast_cli(a->fd, "Requested Hangup on channel '%s'\n", c->name);
+ ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
+ ast_channel_unlock(c);
+ } else
+ ast_cli(a->fd, "%s is not a known channel\n", a->argv[2]);
+ return CLI_SUCCESS;
+}
+
+static char *__ast_cli_generator(const char *text, const char *word, int state, int lock);
+
+static char *handle_commandmatchesarray(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char *buf, *obuf;
+ int buflen = 2048;
+ int len = 0;
+ char **matches;
+ int x, matchlen;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "_command matchesarray";
+ e->usage =
+ "Usage: _command matchesarray \"<line>\" text \n"
+ " This function is used internally to help with command completion and should.\n"
+ " never be called by the user directly.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+ if (!(buf = ast_malloc(buflen)))
+ return CLI_FAILURE;
+ buf[len] = '\0';
+ matches = ast_cli_completion_matches(a->argv[2], a->argv[3]);
+ if (matches) {
+ for (x=0; matches[x]; x++) {
+ matchlen = strlen(matches[x]) + 1;
+ if (len + matchlen >= buflen) {
+ buflen += matchlen * 3;
+ obuf = buf;
+ if (!(buf = ast_realloc(obuf, buflen)))
+ /* Memory allocation failure... Just free old buffer and be done */
+ ast_free(obuf);
+ }
+ if (buf)
+ len += sprintf( buf + len, "%s ", matches[x]);
+ ast_free(matches[x]);
+ matches[x] = NULL;
+ }
+ ast_free(matches);
+ }
+
+ if (buf) {
+ ast_cli(a->fd, "%s%s",buf, AST_CLI_COMPLETE_EOF);
+ ast_free(buf);
+ } else
+ ast_cli(a->fd, "NULL\n");
+
+ return CLI_SUCCESS;
+}
+
+
+
+static char *handle_commandnummatches(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int matches = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "_command nummatches";
+ e->usage =
+ "Usage: _command nummatches \"<line>\" text \n"
+ " This function is used internally to help with command completion and should.\n"
+ " never be called by the user directly.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+
+ matches = ast_cli_generatornummatches(a->argv[2], a->argv[3]);
+
+ ast_cli(a->fd, "%d", matches);
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_commandcomplete(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char *buf;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "_command complete";
+ e->usage =
+ "Usage: _command complete \"<line>\" text state\n"
+ " This function is used internally to help with command completion and should.\n"
+ " never be called by the user directly.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (a->argc != 5)
+ return CLI_SHOWUSAGE;
+ buf = __ast_cli_generator(a->argv[2], a->argv[3], atoi(a->argv[4]), 0);
+ if (buf) {
+ ast_cli(a->fd, buf);
+ ast_free(buf);
+ } else
+ ast_cli(a->fd, "NULL\n");
+ return CLI_SUCCESS;
+}
+
+static char *handle_core_set_debug_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_channel *c = NULL;
+ int is_all, is_off = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core set debug channel";
+ e->usage =
+ "Usage: core set debug channel <all|channel> [off]\n"
+ " Enables/disables debugging on all or on a specific channel.\n";
+ return NULL;
+
+ case CLI_GENERATE:
+ /* XXX remember to handle the optional "off" */
+ if (a->pos != e->args)
+ return NULL;
+ return a->n == 0 ? ast_strdup("all") : ast_complete_channels(a->line, a->word, a->pos, a->n - 1, e->args);
+ }
+ /* 'core set debug channel {all|chan_id}' */
+ if (a->argc == e->args + 2) {
+ if (!strcasecmp(a->argv[e->args + 1], "off"))
+ is_off = 1;
+ else
+ return CLI_SHOWUSAGE;
+ } else if (a->argc != e->args + 1)
+ return CLI_SHOWUSAGE;
+
+ is_all = !strcasecmp("all", a->argv[e->args]);
+ if (is_all) {
+ if (is_off) {
+ global_fin &= ~DEBUGCHAN_FLAG;
+ global_fout &= ~DEBUGCHAN_FLAG;
+ } else {
+ global_fin |= DEBUGCHAN_FLAG;
+ global_fout |= DEBUGCHAN_FLAG;
+ }
+ c = ast_channel_walk_locked(NULL);
+ } else {
+ c = ast_get_channel_by_name_locked(a->argv[e->args]);
+ if (c == NULL)
+ ast_cli(a->fd, "No such channel %s\n", a->argv[e->args]);
+ }
+ while (c) {
+ if (!(c->fin & DEBUGCHAN_FLAG) || !(c->fout & DEBUGCHAN_FLAG)) {
+ if (is_off) {
+ c->fin &= ~DEBUGCHAN_FLAG;
+ c->fout &= ~DEBUGCHAN_FLAG;
+ } else {
+ c->fin |= DEBUGCHAN_FLAG;
+ c->fout |= DEBUGCHAN_FLAG;
+ }
+ ast_cli(a->fd, "Debugging %s on channel %s\n", is_off ? "disabled" : "enabled", c->name);
+ }
+ ast_channel_unlock(c);
+ if (!is_all)
+ break;
+ c = ast_channel_walk_locked(c);
+ }
+ ast_cli(a->fd, "Debugging on new channels is %s\n", is_off ? "disabled" : "enabled");
+ return CLI_SUCCESS;
+}
+
+static char *handle_debugchan_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char *res;
+
+ if (cmd == CLI_HANDLER && a->argc != e->args + 1)
+ return CLI_SHOWUSAGE;
+ res = handle_core_set_debug_channel(e, cmd, a);
+ if (cmd == CLI_INIT)
+ e->command = "debug channel";
+ return res;
+}
+
+static char *handle_nodebugchan_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char *res;
+ if (cmd == CLI_HANDLER) {
+ if (a->argc != e->args + 1)
+ return CLI_SHOWUSAGE;
+ /* pretend we have an extra "off" at the end. We can do this as the array
+ * is NULL terminated so we overwrite that entry.
+ */
+ a->argv[e->args+1] = "off";
+ a->argc++;
+ }
+ res = handle_core_set_debug_channel(e, cmd, a);
+ if (cmd == CLI_INIT)
+ e->command = "no debug channel";
+ return res;
+}
+
+static char *handle_showchan(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_channel *c=NULL;
+ struct timeval now;
+ struct ast_str *out = ast_str_alloca(2048);
+ char cdrtime[256];
+ char nf[256], wf[256], rf[256];
+ long elapsed_seconds=0;
+ int hour=0, min=0, sec=0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show channel";
+ e->usage =
+ "Usage: core show channel <channel>\n"
+ " Shows lots of information about the specified channel.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return ast_complete_channels(a->line, a->word, a->pos, a->n, 3);
+ }
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+ now = ast_tvnow();
+ c = ast_get_channel_by_name_locked(a->argv[3]);
+ if (!c) {
+ ast_cli(a->fd, "%s is not a known channel\n", a->argv[3]);
+ return CLI_SUCCESS;
+ }
+ if (c->cdr) {
+ elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
+ hour = elapsed_seconds / 3600;
+ min = (elapsed_seconds % 3600) / 60;
+ sec = elapsed_seconds % 60;
+ snprintf(cdrtime, sizeof(cdrtime), "%dh%dm%ds", hour, min, sec);
+ } else
+ strcpy(cdrtime, "N/A");
+ ast_cli(a->fd,
+ " -- General --\n"
+ " Name: %s\n"
+ " Type: %s\n"
+ " UniqueID: %s\n"
+ " Caller ID: %s\n"
+ " Caller ID Name: %s\n"
+ " DNID Digits: %s\n"
+ " Language: %s\n"
+ " State: %s (%d)\n"
+ " Rings: %d\n"
+ " NativeFormats: %s\n"
+ " WriteFormat: %s\n"
+ " ReadFormat: %s\n"
+ " WriteTranscode: %s\n"
+ " ReadTranscode: %s\n"
+ "1st File Descriptor: %d\n"
+ " Frames in: %d%s\n"
+ " Frames out: %d%s\n"
+ " Time to Hangup: %ld\n"
+ " Elapsed Time: %s\n"
+ " Direct Bridge: %s\n"
+ "Indirect Bridge: %s\n"
+ " -- PBX --\n"
+ " Context: %s\n"
+ " Extension: %s\n"
+ " Priority: %d\n"
+ " Call Group: %llu\n"
+ " Pickup Group: %llu\n"
+ " Application: %s\n"
+ " Data: %s\n"
+ " Blocking in: %s\n",
+ c->name, c->tech->type, c->uniqueid,
+ S_OR(c->cid.cid_num, "(N/A)"),
+ S_OR(c->cid.cid_name, "(N/A)"),
+ S_OR(c->cid.cid_dnid, "(N/A)"),
+ c->language,
+ ast_state2str(c->_state), c->_state, c->rings,
+ ast_getformatname_multiple(nf, sizeof(nf), c->nativeformats),
+ ast_getformatname_multiple(wf, sizeof(wf), c->writeformat),
+ ast_getformatname_multiple(rf, sizeof(rf), c->readformat),
+ c->writetrans ? "Yes" : "No",
+ c->readtrans ? "Yes" : "No",
+ c->fds[0],
+ c->fin & ~DEBUGCHAN_FLAG, (c->fin & DEBUGCHAN_FLAG) ? " (DEBUGGED)" : "",
+ c->fout & ~DEBUGCHAN_FLAG, (c->fout & DEBUGCHAN_FLAG) ? " (DEBUGGED)" : "",
+ (long)c->whentohangup,
+ cdrtime, c->_bridge ? c->_bridge->name : "<none>", ast_bridged_channel(c) ? ast_bridged_channel(c)->name : "<none>",
+ c->context, c->exten, c->priority, c->callgroup, c->pickupgroup, ( c->appl ? c->appl : "(N/A)" ),
+ ( c-> data ? S_OR(c->data, "(Empty)") : "(None)"),
+ (ast_test_flag(c, AST_FLAG_BLOCKING) ? c->blockproc : "(Not Blocking)"));
+
+ if (pbx_builtin_serialize_variables(c, &out))
+ ast_cli(a->fd," Variables:\n%s\n", out->str);
+ if (c->cdr && ast_cdr_serialize_variables(c->cdr, &out, '=', '\n', 1))
+ ast_cli(a->fd," CDR Variables:\n%s\n", out->str);
+
+ ast_channel_unlock(c);
+ return CLI_SUCCESS;
+}
+
+/*
+ * helper function to generate CLI matches from a fixed set of values.
+ * A NULL word is acceptable.
+ */
+char *ast_cli_complete(const char *word, char *const choices[], int state)
+{
+ int i, which = 0, len;
+ len = ast_strlen_zero(word) ? 0 : strlen(word);
+
+ for (i = 0; choices[i]; i++) {
+ if ((!len || !strncasecmp(word, choices[i], len)) && ++which > state)
+ return ast_strdup(choices[i]);
+ }
+ return NULL;
+}
+
+char *ast_complete_channels(const char *line, const char *word, int pos, int state, int rpos)
+{
+ struct ast_channel *c = NULL;
+ int which = 0;
+ int wordlen;
+ char notfound = '\0';
+ char *ret = &notfound; /* so NULL can break the loop */
+
+ if (pos != rpos)
+ return NULL;
+
+ wordlen = strlen(word);
+
+ while (ret == &notfound && (c = ast_channel_walk_locked(c))) {
+ if (!strncasecmp(word, c->name, wordlen) && ++which > state)
+ ret = ast_strdup(c->name);
+ ast_channel_unlock(c);
+ }
+ return ret == &notfound ? NULL : ret;
+}
+
+static char *group_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+#define FORMAT_STRING "%-25s %-20s %-20s\n"
+
+ struct ast_group_info *gi = NULL;
+ int numchans = 0;
+ regex_t regexbuf;
+ int havepattern = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "group show channels";
+ e->usage =
+ "Usage: group show channels [pattern]\n"
+ " Lists all currently active channels with channel group(s) specified.\n"
+ " Optional regular expression pattern is matched to group names for each\n"
+ " channel.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc < 3 || a->argc > 4)
+ return CLI_SHOWUSAGE;
+
+ if (a->argc == 4) {
+ if (regcomp(&regexbuf, a->argv[3], REG_EXTENDED | REG_NOSUB))
+ return CLI_SHOWUSAGE;
+ havepattern = 1;
+ }
+
+ ast_cli(a->fd, FORMAT_STRING, "Channel", "Group", "Category");
+
+ ast_app_group_list_rdlock();
+
+ gi = ast_app_group_list_head();
+ while (gi) {
+ if (!havepattern || !regexec(&regexbuf, gi->group, 0, NULL, 0)) {
+ ast_cli(a->fd, FORMAT_STRING, gi->chan->name, gi->group, (ast_strlen_zero(gi->category) ? "(default)" : gi->category));
+ numchans++;
+ }
+ gi = AST_LIST_NEXT(gi, list);
+ }
+
+ ast_app_group_list_unlock();
+
+ if (havepattern)
+ regfree(&regexbuf);
+
+ ast_cli(a->fd, "%d active channel%s\n", numchans, ESS(numchans));
+ return CLI_SUCCESS;
+#undef FORMAT_STRING
+}
+
+static struct ast_cli_entry cli_debug_channel_deprecated = AST_CLI_DEFINE(handle_debugchan_deprecated, "Enable debugging on channel");
+static struct ast_cli_entry cli_module_load_deprecated = AST_CLI_DEFINE(handle_load_deprecated, "Load a module");
+static struct ast_cli_entry cli_module_reload_deprecated = AST_CLI_DEFINE(handle_reload_deprecated, "reload modules by name");
+static struct ast_cli_entry cli_module_unload_deprecated = AST_CLI_DEFINE(handle_unload_deprecated, "unload modules by name");
+
+static char *handle_help(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+
+static struct ast_cli_entry cli_cli[] = {
+ /* Deprecated, but preferred command is now consolidated (and already has a deprecated command for it). */
+ AST_CLI_DEFINE(handle_commandcomplete, "Command complete"),
+ AST_CLI_DEFINE(handle_commandnummatches, "Returns number of command matches"),
+ AST_CLI_DEFINE(handle_commandmatchesarray, "Returns command matches array"),
+
+ AST_CLI_DEFINE(handle_nodebugchan_deprecated, "Disable debugging on channel(s)"),
+
+ AST_CLI_DEFINE(handle_chanlist, "Display information on channels"),
+
+ AST_CLI_DEFINE(handle_showcalls, "Display information on calls"),
+
+ AST_CLI_DEFINE(handle_showchan, "Display information on a specific channel"),
+
+ AST_CLI_DEFINE(handle_core_set_debug_channel, "Enable/disable debugging on a channel",
+ .deprecate_cmd = &cli_debug_channel_deprecated),
+
+ AST_CLI_DEFINE(handle_verbose, "Set level of debug/verbose chattiness"),
+
+ AST_CLI_DEFINE(group_show_channels, "Display active channels with group(s)"),
+
+ AST_CLI_DEFINE(handle_help, "Display help list, or specific help on a command"),
+
+ AST_CLI_DEFINE(handle_logger_mute, "Toggle logging output to a console"),
+
+ AST_CLI_DEFINE(handle_modlist, "List modules and info"),
+
+ AST_CLI_DEFINE(handle_load, "Load a module by name", .deprecate_cmd = &cli_module_load_deprecated),
+
+ AST_CLI_DEFINE(handle_reload, "Reload configuration", .deprecate_cmd = &cli_module_reload_deprecated),
+
+ AST_CLI_DEFINE(handle_unload, "Unload a module by name", .deprecate_cmd = &cli_module_unload_deprecated ),
+
+ AST_CLI_DEFINE(handle_showuptime, "Show uptime information"),
+
+ AST_CLI_DEFINE(handle_softhangup, "Request a hangup on a given channel"),
+};
+
+/*!
+ * Some regexp characters in cli arguments are reserved and used as separators.
+ */
+static const char cli_rsvd[] = "[]{}|*%";
+
+/*!
+ * initialize the _full_cmd string and related parameters,
+ * return 0 on success, -1 on error.
+ */
+static int set_full_cmd(struct ast_cli_entry *e)
+{
+ int i;
+ char buf[80];
+
+ ast_join(buf, sizeof(buf), e->cmda);
+ e->_full_cmd = ast_strdup(buf);
+ if (!e->_full_cmd) {
+ ast_log(LOG_WARNING, "-- cannot allocate <%s>\n", buf);
+ return -1;
+ }
+ e->cmdlen = strcspn(e->_full_cmd, cli_rsvd);
+ for (i = 0; e->cmda[i]; i++)
+ ;
+ e->args = i;
+ return 0;
+}
+
+/*! \brief initialize the _full_cmd string in * each of the builtins. */
+void ast_builtins_init(void)
+{
+ ast_cli_register_multiple(cli_cli, sizeof(cli_cli) / sizeof(struct ast_cli_entry));
+}
+
+static struct ast_cli_entry *cli_next(struct ast_cli_entry *e)
+{
+ if (e == NULL)
+ e = AST_LIST_FIRST(&helpers);
+ if (e)
+ e = AST_LIST_NEXT(e, list);
+ return e;
+}
+
+/*!
+ * match a word in the CLI entry.
+ * returns -1 on mismatch, 0 on match of an optional word,
+ * 1 on match of a full word.
+ *
+ * The pattern can be
+ * any_word match for equal
+ * [foo|bar|baz] optionally, one of these words
+ * {foo|bar|baz} exactly, one of these words
+ * % any word
+ */
+static int word_match(const char *cmd, const char *cli_word)
+{
+ int l;
+ char *pos;
+
+ if (ast_strlen_zero(cmd) || ast_strlen_zero(cli_word))
+ return -1;
+ if (!strchr(cli_rsvd, cli_word[0])) /* normal match */
+ return (strcasecmp(cmd, cli_word) == 0) ? 1 : -1;
+ /* regexp match, takes [foo|bar] or {foo|bar} */
+ l = strlen(cmd);
+ /* wildcard match - will extend in the future */
+ if (l > 0 && cli_word[0] == '%') {
+ return 1; /* wildcard */
+ }
+ pos = strcasestr(cli_word, cmd);
+ if (pos == NULL) /* not found, say ok if optional */
+ return cli_word[0] == '[' ? 0 : -1;
+ if (pos == cli_word) /* no valid match at the beginning */
+ return -1;
+ if (strchr(cli_rsvd, pos[-1]) && strchr(cli_rsvd, pos[l]))
+ return 1; /* valid match */
+ return -1; /* not found */
+}
+
+/*! \brief if word is a valid prefix for token, returns the pos-th
+ * match as a malloced string, or NULL otherwise.
+ * Always tell in *actual how many matches we got.
+ */
+static char *is_prefix(const char *word, const char *token,
+ int pos, int *actual)
+{
+ int lw;
+ char *s, *t1;
+
+ *actual = 0;
+ if (ast_strlen_zero(token))
+ return NULL;
+ if (ast_strlen_zero(word))
+ word = ""; /* dummy */
+ lw = strlen(word);
+ if (strcspn(word, cli_rsvd) != lw)
+ return NULL; /* no match if word has reserved chars */
+ if (strchr(cli_rsvd, token[0]) == NULL) { /* regular match */
+ if (strncasecmp(token, word, lw)) /* no match */
+ return NULL;
+ *actual = 1;
+ return (pos != 0) ? NULL : ast_strdup(token);
+ }
+ /* now handle regexp match */
+
+ /* Wildcard always matches, so we never do is_prefix on them */
+
+ t1 = ast_strdupa(token + 1); /* copy, skipping first char */
+ while (pos >= 0 && (s = strsep(&t1, cli_rsvd)) && *s) {
+ if (*s == '%') /* wildcard */
+ continue;
+ if (strncasecmp(s, word, lw)) /* no match */
+ continue;
+ (*actual)++;
+ if (pos-- == 0)
+ return ast_strdup(s);
+ }
+ return NULL;
+}
+
+/*!
+ * \brief locate a cli command in the 'helpers' list (which must be locked).
+ * exact has 3 values:
+ * 0 returns if the search key is equal or longer than the entry.
+ * note that trailing optional arguments are skipped.
+ * -1 true if the mismatch is on the last word XXX not true!
+ * 1 true only on complete, exact match.
+ *
+ * The search compares word by word taking care of regexps in e->cmda
+ */
+static struct ast_cli_entry *find_cli(char *const cmds[], int match_type)
+{
+ int matchlen = -1; /* length of longest match so far */
+ struct ast_cli_entry *cand = NULL, *e=NULL;
+
+ while ( (e = cli_next(e)) ) {
+ /* word-by word regexp comparison */
+ char * const *src = cmds;
+ char * const *dst = e->cmda;
+ int n = 0;
+ for (;; dst++, src += n) {
+ n = word_match(*src, *dst);
+ if (n < 0)
+ break;
+ }
+ if (ast_strlen_zero(*dst) || ((*dst)[0] == '[' && ast_strlen_zero(dst[1]))) {
+ /* no more words in 'e' */
+ if (ast_strlen_zero(*src)) /* exact match, cannot do better */
+ break;
+ /* Here, cmds has more words than the entry 'e' */
+ if (match_type != 0) /* but we look for almost exact match... */
+ continue; /* so we skip this one. */
+ /* otherwise we like it (case 0) */
+ } else { /* still words in 'e' */
+ if (ast_strlen_zero(*src))
+ continue; /* cmds is shorter than 'e', not good */
+ /* Here we have leftover words in cmds and 'e',
+ * but there is a mismatch. We only accept this one if match_type == -1
+ * and this is the last word for both.
+ */
+ if (match_type != -1 || !ast_strlen_zero(src[1]) ||
+ !ast_strlen_zero(dst[1])) /* not the one we look for */
+ continue;
+ /* good, we are in case match_type == -1 and mismatch on last word */
+ }
+ if (src - cmds > matchlen) { /* remember the candidate */
+ matchlen = src - cmds;
+ cand = e;
+ }
+ }
+ return e ? e : cand;
+}
+
+static char *find_best(char *argv[])
+{
+ static char cmdline[80];
+ int x;
+ /* See how close we get, then print the candidate */
+ char *myargv[AST_MAX_CMD_LEN];
+ for (x=0;x<AST_MAX_CMD_LEN;x++)
+ myargv[x]=NULL;
+ AST_RWLIST_RDLOCK(&helpers);
+ for (x=0;argv[x];x++) {
+ myargv[x] = argv[x];
+ if (!find_cli(myargv, -1))
+ break;
+ }
+ AST_RWLIST_UNLOCK(&helpers);
+ ast_join(cmdline, sizeof(cmdline), myargv);
+ return cmdline;
+}
+
+static int __ast_cli_unregister(struct ast_cli_entry *e, struct ast_cli_entry *ed)
+{
+ if (e->deprecate_cmd) {
+ __ast_cli_unregister(e->deprecate_cmd, e);
+ }
+ if (e->inuse) {
+ ast_log(LOG_WARNING, "Can't remove command that is in use\n");
+ } else {
+ AST_RWLIST_WRLOCK(&helpers);
+ AST_RWLIST_REMOVE(&helpers, e, list);
+ AST_RWLIST_UNLOCK(&helpers);
+ ast_free(e->_full_cmd);
+ e->_full_cmd = NULL;
+ if (e->handler) {
+ /* this is a new-style entry. Reset fields and free memory. */
+ bzero((char **)(e->cmda), sizeof(e->cmda));
+ ast_free(e->command);
+ e->command = NULL;
+ e->usage = NULL;
+ }
+ }
+ return 0;
+}
+
+static int __ast_cli_register(struct ast_cli_entry *e, struct ast_cli_entry *ed)
+{
+ struct ast_cli_entry *cur;
+ int i, lf, ret = -1;
+
+ struct ast_cli_args a; /* fake argument */
+ char **dst = (char **)e->cmda; /* need to cast as the entry is readonly */
+ char *s;
+
+ bzero (&a, sizeof(a));
+ e->handler(e, CLI_INIT, &a);
+ /* XXX check that usage and command are filled up */
+ s = ast_skip_blanks(e->command);
+ s = e->command = ast_strdup(s);
+ for (i=0; !ast_strlen_zero(s) && i < AST_MAX_CMD_LEN-1; i++) {
+ *dst++ = s; /* store string */
+ s = ast_skip_nonblanks(s);
+ if (*s == '\0') /* we are done */
+ break;
+ *s++ = '\0';
+ s = ast_skip_blanks(s);
+ }
+ *dst++ = NULL;
+
+ AST_RWLIST_WRLOCK(&helpers);
+
+ if (find_cli(e->cmda, 1)) {
+ ast_log(LOG_WARNING, "Command '%s' already registered (or something close enough)\n", e->_full_cmd);
+ goto done;
+ }
+ if (set_full_cmd(e))
+ goto done;
+ if (!ed) {
+ e->deprecated = 0;
+ } else {
+ e->deprecated = 1;
+ e->summary = ed->summary;
+ e->usage = ed->usage;
+ /* XXX If command A deprecates command B, and command B deprecates command C...
+ Do we want to show command A or command B when telling the user to use new syntax?
+ This currently would show command A.
+ To show command B, you just need to always use ed->_full_cmd.
+ */
+ e->_deprecated_by = S_OR(ed->_deprecated_by, ed->_full_cmd);
+ }
+
+ lf = e->cmdlen;
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&helpers, cur, list) {
+ int len = cur->cmdlen;
+ if (lf < len)
+ len = lf;
+ if (strncasecmp(e->_full_cmd, cur->_full_cmd, len) < 0) {
+ AST_RWLIST_INSERT_BEFORE_CURRENT(e, list);
+ break;
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+
+ if (!cur)
+ AST_RWLIST_INSERT_TAIL(&helpers, e, list);
+ ret = 0; /* success */
+
+done:
+ AST_RWLIST_UNLOCK(&helpers);
+
+ if (e->deprecate_cmd) {
+ /* This command deprecates another command. Register that one also. */
+ __ast_cli_register(e->deprecate_cmd, e);
+ }
+
+ return ret;
+}
+
+/* wrapper function, so we can unregister deprecated commands recursively */
+int ast_cli_unregister(struct ast_cli_entry *e)
+{
+ return __ast_cli_unregister(e, NULL);
+}
+
+/* wrapper function, so we can register deprecated commands recursively */
+int ast_cli_register(struct ast_cli_entry *e)
+{
+ return __ast_cli_register(e, NULL);
+}
+
+/*
+ * register/unregister an array of entries.
+ */
+int ast_cli_register_multiple(struct ast_cli_entry *e, int len)
+{
+ int i, res = 0;
+
+ for (i = 0; i < len; i++)
+ res |= ast_cli_register(e + i);
+
+ return res;
+}
+
+int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
+{
+ int i, res = 0;
+
+ for (i = 0; i < len; i++)
+ res |= ast_cli_unregister(e + i);
+
+ return res;
+}
+
+
+/*! \brief helper for final part of handle_help
+ * if locked = 1, assume the list is already locked
+ */
+static char *help1(int fd, char *match[], int locked)
+{
+ char matchstr[80] = "";
+ struct ast_cli_entry *e = NULL;
+ int len = 0;
+ int found = 0;
+
+ if (match) {
+ ast_join(matchstr, sizeof(matchstr), match);
+ len = strlen(matchstr);
+ }
+ if (!locked)
+ AST_RWLIST_RDLOCK(&helpers);
+ while ( (e = cli_next(e)) ) {
+ /* Hide commands that start with '_' */
+ if (e->_full_cmd[0] == '_')
+ continue;
+ /* Hide commands that are marked as deprecated. */
+ if (e->deprecated)
+ continue;
+ if (match && strncasecmp(matchstr, e->_full_cmd, len))
+ continue;
+ ast_cli(fd, "%30.30s %s\n", e->_full_cmd, S_OR(e->summary, "<no description available>"));
+ found++;
+ }
+ if (!locked)
+ AST_RWLIST_UNLOCK(&helpers);
+ if (!found && matchstr[0])
+ ast_cli(fd, "No such command '%s'.\n", matchstr);
+ return CLI_SUCCESS;
+}
+
+static char *handle_help(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char fullcmd[80];
+ struct ast_cli_entry *my_e;
+ char *res = CLI_SUCCESS;
+
+ if (cmd == CLI_INIT) {
+ e->command = "help";
+ e->usage =
+ "Usage: help [topic]\n"
+ " When called with a topic as an argument, displays usage\n"
+ " information on the given command. If called without a\n"
+ " topic, it provides a list of commands.\n";
+ return NULL;
+
+ } else if (cmd == CLI_GENERATE) {
+ /* skip first 4 or 5 chars, "help " */
+ int l = strlen(a->line);
+
+ if (l > 5)
+ l = 5;
+ /* XXX watch out, should stop to the non-generator parts */
+ return __ast_cli_generator(a->line + l, a->word, a->n, 0);
+ }
+ if (a->argc == 1)
+ return help1(a->fd, NULL, 0);
+
+ AST_RWLIST_RDLOCK(&helpers);
+ my_e = find_cli(a->argv + 1, 1); /* try exact match first */
+ if (!my_e) {
+ res = help1(a->fd, a->argv + 1, 1 /* locked */);
+ AST_RWLIST_UNLOCK(&helpers);
+ return res;
+ }
+ if (my_e->usage)
+ ast_cli(a->fd, "%s", my_e->usage);
+ else {
+ ast_join(fullcmd, sizeof(fullcmd), a->argv+1);
+ ast_cli(a->fd, "No help text available for '%s'.\n", fullcmd);
+ }
+ AST_RWLIST_UNLOCK(&helpers);
+ return res;
+}
+
+static char *parse_args(const char *s, int *argc, char *argv[], int max, int *trailingwhitespace)
+{
+ char *dup, *cur;
+ int x = 0;
+ int quoted = 0;
+ int escaped = 0;
+ int whitespace = 1;
+ int dummy = 0;
+
+ if (trailingwhitespace == NULL)
+ trailingwhitespace = &dummy;
+ *trailingwhitespace = 0;
+ if (s == NULL) /* invalid, though! */
+ return NULL;
+ /* make a copy to store the parsed string */
+ if (!(dup = ast_strdup(s)))
+ return NULL;
+
+ cur = dup;
+ /* scan the original string copying into cur when needed */
+ for (; *s ; s++) {
+ if (x >= max - 1) {
+ ast_log(LOG_WARNING, "Too many arguments, truncating at %s\n", s);
+ break;
+ }
+ if (*s == '"' && !escaped) {
+ quoted = !quoted;
+ if (quoted && whitespace) {
+ /* start a quoted string from previous whitespace: new argument */
+ argv[x++] = cur;
+ whitespace = 0;
+ }
+ } else if ((*s == ' ' || *s == '\t') && !(quoted || escaped)) {
+ /* If we are not already in whitespace, and not in a quoted string or
+ processing an escape sequence, and just entered whitespace, then
+ finalize the previous argument and remember that we are in whitespace
+ */
+ if (!whitespace) {
+ *cur++ = '\0';
+ whitespace = 1;
+ }
+ } else if (*s == '\\' && !escaped) {
+ escaped = 1;
+ } else {
+ if (whitespace) {
+ /* we leave whitespace, and are not quoted. So it's a new argument */
+ argv[x++] = cur;
+ whitespace = 0;
+ }
+ *cur++ = *s;
+ escaped = 0;
+ }
+ }
+ /* Null terminate */
+ *cur++ = '\0';
+ /* XXX put a NULL in the last argument, because some functions that take
+ * the array may want a null-terminated array.
+ * argc still reflects the number of non-NULL entries.
+ */
+ argv[x] = NULL;
+ *argc = x;
+ *trailingwhitespace = whitespace;
+ return dup;
+}
+
+/*! \brief Return the number of unique matches for the generator */
+int ast_cli_generatornummatches(const char *text, const char *word)
+{
+ int matches = 0, i = 0;
+ char *buf = NULL, *oldbuf = NULL;
+
+ while ((buf = ast_cli_generator(text, word, i++))) {
+ if (!oldbuf || strcmp(buf,oldbuf))
+ matches++;
+ if (oldbuf)
+ ast_free(oldbuf);
+ oldbuf = buf;
+ }
+ if (oldbuf)
+ ast_free(oldbuf);
+ return matches;
+}
+
+char **ast_cli_completion_matches(const char *text, const char *word)
+{
+ char **match_list = NULL, *retstr, *prevstr;
+ size_t match_list_len, max_equal, which, i;
+ int matches = 0;
+
+ /* leave entry 0 free for the longest common substring */
+ match_list_len = 1;
+ while ((retstr = ast_cli_generator(text, word, matches)) != NULL) {
+ if (matches + 1 >= match_list_len) {
+ match_list_len <<= 1;
+ if (!(match_list = ast_realloc(match_list, match_list_len * sizeof(*match_list))))
+ return NULL;
+ }
+ match_list[++matches] = retstr;
+ }
+
+ if (!match_list)
+ return match_list; /* NULL */
+
+ /* Find the longest substring that is common to all results
+ * (it is a candidate for completion), and store a copy in entry 0.
+ */
+ prevstr = match_list[1];
+ max_equal = strlen(prevstr);
+ for (which = 2; which <= matches; which++) {
+ for (i = 0; i < max_equal && toupper(prevstr[i]) == toupper(match_list[which][i]); i++)
+ continue;
+ max_equal = i;
+ }
+
+ if (!(retstr = ast_malloc(max_equal + 1)))
+ return NULL;
+
+ ast_copy_string(retstr, match_list[1], max_equal + 1);
+ match_list[0] = retstr;
+
+ /* ensure that the array is NULL terminated */
+ if (matches + 1 >= match_list_len) {
+ if (!(match_list = ast_realloc(match_list, (match_list_len + 1) * sizeof(*match_list))))
+ return NULL;
+ }
+ match_list[matches + 1] = NULL;
+
+ return match_list;
+}
+
+/*! \brief returns true if there are more words to match */
+static int more_words (char * const *dst)
+{
+ int i;
+ for (i = 0; dst[i]; i++) {
+ if (dst[i][0] != '[')
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * generate the entry at position 'state'
+ */
+static char *__ast_cli_generator(const char *text, const char *word, int state, int lock)
+{
+ char *argv[AST_MAX_ARGS];
+ struct ast_cli_entry *e = NULL;
+ int x = 0, argindex, matchlen;
+ int matchnum=0;
+ char *ret = NULL;
+ char matchstr[80] = "";
+ int tws = 0;
+ /* Split the argument into an array of words */
+ char *dup = parse_args(text, &x, argv, sizeof(argv) / sizeof(argv[0]), &tws);
+
+ if (!dup) /* malloc error */
+ return NULL;
+
+ /* Compute the index of the last argument (could be an empty string) */
+ argindex = (!ast_strlen_zero(word) && x>0) ? x-1 : x;
+
+ /* rebuild the command, ignore terminating white space and flatten space */
+ ast_join(matchstr, sizeof(matchstr)-1, argv);
+ matchlen = strlen(matchstr);
+ if (tws) {
+ strcat(matchstr, " "); /* XXX */
+ if (matchlen)
+ matchlen++;
+ }
+ if (lock)
+ AST_RWLIST_RDLOCK(&helpers);
+ while ( (e = cli_next(e)) ) {
+ /* XXX repeated code */
+ int src = 0, dst = 0, n = 0;
+
+ if (e->command[0] == '_')
+ continue;
+
+ /*
+ * Try to match words, up to and excluding the last word, which
+ * is either a blank or something that we want to extend.
+ */
+ for (;src < argindex; dst++, src += n) {
+ n = word_match(argv[src], e->cmda[dst]);
+ if (n < 0)
+ break;
+ }
+
+ if (src != argindex && more_words(e->cmda + dst)) /* not a match */
+ continue;
+ ret = is_prefix(argv[src], e->cmda[dst], state - matchnum, &n);
+ matchnum += n; /* this many matches here */
+ if (ret) {
+ /*
+ * argv[src] is a valid prefix of the next word in this
+ * command. If this is also the correct entry, return it.
+ */
+ if (matchnum > state)
+ break;
+ ast_free(ret);
+ ret = NULL;
+ } else if (ast_strlen_zero(e->cmda[dst])) {
+ /*
+ * This entry is a prefix of the command string entered
+ * (only one entry in the list should have this property).
+ * Run the generator if one is available. In any case we are done.
+ */
+ if (e->handler) { /* new style command */
+ struct ast_cli_args a = {
+ .line = matchstr, .word = word,
+ .pos = argindex,
+ .n = state - matchnum };
+ ret = e->handler(e, CLI_GENERATE, &a);
+ }
+ if (ret)
+ break;
+ }
+ }
+ if (lock)
+ AST_RWLIST_UNLOCK(&helpers);
+ ast_free(dup);
+ return ret;
+}
+
+char *ast_cli_generator(const char *text, const char *word, int state)
+{
+ return __ast_cli_generator(text, word, state, 1);
+}
+
+int ast_cli_command(int fd, const char *s)
+{
+ char *args[AST_MAX_ARGS + 1];
+ struct ast_cli_entry *e;
+ int x;
+ char *dup = parse_args(s, &x, args + 1, AST_MAX_ARGS, NULL);
+ char *retval = NULL;
+ struct ast_cli_args a = {
+ .fd = fd, .argc = x, .argv = args+1 };
+
+ if (dup == NULL)
+ return -1;
+
+ if (x < 1) /* We need at least one entry, otherwise ignore */
+ goto done;
+
+ AST_RWLIST_RDLOCK(&helpers);
+ e = find_cli(args + 1, 0);
+ if (e)
+ ast_atomic_fetchadd_int(&e->inuse, 1);
+ AST_RWLIST_UNLOCK(&helpers);
+ if (e == NULL) {
+ ast_cli(fd, "No such command '%s' (type 'help' for help)\n", find_best(args + 1));
+ goto done;
+ }
+ /*
+ * Within the handler, argv[-1] contains a pointer to the ast_cli_entry.
+ * Remember that the array returned by parse_args is NULL-terminated.
+ */
+ args[0] = (char *)e;
+
+ retval = e->handler(e, CLI_HANDLER, &a);
+
+ if (retval == CLI_SHOWUSAGE) {
+ ast_cli(fd, "%s", S_OR(e->usage, "Invalid usage, but no usage information available.\n"));
+ AST_RWLIST_RDLOCK(&helpers);
+ if (e->deprecated)
+ ast_cli(fd, "The '%s' command is deprecated and will be removed in a future release. Please use '%s' instead.\n", e->_full_cmd, e->_deprecated_by);
+ AST_RWLIST_UNLOCK(&helpers);
+ } else {
+ if (retval == CLI_FAILURE)
+ ast_cli(fd, "Command '%s' failed.\n", s);
+ AST_RWLIST_RDLOCK(&helpers);
+ if (e->deprecated == 1) {
+ ast_cli(fd, "The '%s' command is deprecated and will be removed in a future release. Please use '%s' instead.\n", e->_full_cmd, e->_deprecated_by);
+ e->deprecated = 2;
+ }
+ AST_RWLIST_UNLOCK(&helpers);
+ }
+ ast_atomic_fetchadd_int(&e->inuse, -1);
+done:
+ ast_free(dup);
+ return 0;
+}
+
+int ast_cli_command_multiple(int fd, size_t size, const char *s)
+{
+ char cmd[512];
+ int x, y = 0, count = 0;
+
+ for (x = 0; x < size; x++) {
+ cmd[y] = s[x];
+ y++;
+ if (s[x] == '\0') {
+ ast_cli_command(fd, cmd);
+ y = 0;
+ count++;
+ }
+ }
+ return count;
+}
diff --git a/trunk/main/config.c b/trunk/main/config.c
new file mode 100644
index 000000000..d5b0738fc
--- /dev/null
+++ b/trunk/main/config.c
@@ -0,0 +1,2281 @@
+/*
+ * 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.
+ */
+
+/*! \file
+ *
+ * \brief Configuration File Parser
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * Includes the Asterisk Realtime API - ARA
+ * See doc/realtime.txt and doc/extconfig.txt
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/paths.h" /* use ast_config_AST_CONFIG_DIR */
+#include "asterisk/network.h" /* we do some sockaddr manipulation here */
+#include <time.h>
+#include <sys/stat.h>
+
+#include <math.h> /* HUGE_VAL */
+
+#define AST_INCLUDE_GLOB 1
+
+#ifdef AST_INCLUDE_GLOB
+/* glob compat stuff - eventually this should go in compat.h or some
+ * header in include/asterisk/
+ */
+#if defined(__Darwin__) || defined(__CYGWIN__)
+#define GLOB_ABORTED GLOB_ABEND
+#endif
+
+#include <glob.h>
+
+#ifdef SOLARIS
+#define MY_GLOB_FLAGS GLOB_NOCHECK
+#else
+#define MY_GLOB_FLAGS (GLOB_NOMAGIC|GLOB_BRACE)
+#endif
+
+#endif
+
+#include "asterisk/config.h"
+#include "asterisk/cli.h"
+#include "asterisk/lock.h"
+#include "asterisk/utils.h"
+#include "asterisk/channel.h"
+#include "asterisk/app.h"
+#include "asterisk/astobj2.h"
+#include "asterisk/strings.h" /* for the ast_str_*() API */
+
+#define MAX_NESTED_COMMENTS 128
+#define COMMENT_START ";--"
+#define COMMENT_END "--;"
+#define COMMENT_META ';'
+#define COMMENT_TAG '-'
+
+static char *extconfig_conf = "extconfig.conf";
+
+
+/*! \brief Structure to keep comments for rewriting configuration files */
+struct ast_comment {
+ struct ast_comment *next;
+ char cmt[0];
+};
+
+/*! \brief Hold the mtime for config files, so if we don't need to reread our config, don't. */
+struct cache_file_include {
+ AST_LIST_ENTRY(cache_file_include) list;
+ char include[0];
+};
+
+struct cache_file_mtime {
+ AST_LIST_ENTRY(cache_file_mtime) list;
+ AST_LIST_HEAD(includes, cache_file_include) includes;
+ unsigned int has_exec:1;
+ time_t mtime;
+ char filename[0];
+};
+
+static AST_LIST_HEAD_STATIC(cfmtime_head, cache_file_mtime);
+
+
+/* comment buffers are better implemented using the ast_str_*() API */
+#define CB_SIZE 250 /* initial size of comment buffers */
+
+static void CB_ADD(struct ast_str **cb, const char *str)
+{
+ ast_str_append(cb, 0, "%s", str);
+}
+
+static void CB_ADD_LEN(struct ast_str **cb, const char *str, int len)
+{
+ char *s = alloca(len + 1);
+ ast_copy_string(s, str, len);
+ ast_str_append(cb, 0, "%s", str);
+}
+
+static void CB_RESET(struct ast_str *cb, struct ast_str *llb)
+{
+ if (cb)
+ cb->used = 0;
+ if (llb)
+ llb->used = 0;
+}
+
+static struct ast_comment *ALLOC_COMMENT(const struct ast_str *buffer)
+{
+ struct ast_comment *x = NULL;
+ if (buffer && buffer->used)
+ x = ast_calloc(1, sizeof(*x) + buffer->used + 1);
+ if (x)
+ strcpy(x->cmt, buffer->str);
+ return x;
+}
+
+/* I need to keep track of each config file, and all its inclusions,
+ so that we can track blank lines in each */
+
+struct inclfile {
+ char *fname;
+ int lineno;
+};
+
+static int hash_string(const void *obj, const int flags)
+{
+ char *str = ((struct inclfile*)obj)->fname;
+ int total;
+
+ for (total=0; *str; str++) {
+ unsigned int tmp = total;
+ total <<= 1; /* multiply by 2 */
+ total += tmp; /* multiply by 3 */
+ total <<= 2; /* multiply by 12 */
+ total += tmp; /* multiply by 13 */
+
+ total += ((unsigned int)(*str));
+ }
+ if (total < 0)
+ total = -total;
+ return total;
+}
+
+static int hashtab_compare_strings(void *a, void *b, int flags)
+{
+ const struct inclfile *ae = a, *be = b;
+ return !strcmp(ae->fname, be->fname) ? CMP_MATCH : 0;
+}
+
+static struct ast_config_map {
+ struct ast_config_map *next;
+ char *name;
+ char *driver;
+ char *database;
+ char *table;
+ char stuff[0];
+} *config_maps = NULL;
+
+AST_MUTEX_DEFINE_STATIC(config_lock);
+static struct ast_config_engine *config_engine_list;
+
+#define MAX_INCLUDE_LEVEL 10
+
+struct ast_category_template_instance {
+ char name[80]; /* redundant? */
+ const struct ast_category *inst;
+ AST_LIST_ENTRY(ast_category_template_instance) next;
+};
+
+struct ast_category {
+ char name[80];
+ int ignored; /*!< do not let user of the config see this category -- set by (!) after the category decl; a template */
+ int include_level;
+ char *file; /*!< the file name from whence this declaration was read */
+ int lineno;
+ AST_LIST_HEAD_NOLOCK(template_instance_list, ast_category_template_instance) template_instances;
+ struct ast_comment *precomments;
+ struct ast_comment *sameline;
+ struct ast_comment *trailing; /*!< the last object in the list will get assigned any trailing comments when EOF is hit */
+ struct ast_variable *root;
+ struct ast_variable *last;
+ struct ast_category *next;
+};
+
+struct ast_config {
+ struct ast_category *root;
+ struct ast_category *last;
+ struct ast_category *current;
+ struct ast_category *last_browse; /*!< used to cache the last category supplied via category_browse */
+ int include_level;
+ int max_include_level;
+ struct ast_config_include *includes; /*!< a list of inclusions, which should describe the entire tree */
+};
+
+struct ast_config_include {
+ char *include_location_file; /*!< file name in which the include occurs */
+ int include_location_lineno; /*!< lineno where include occurred */
+ int exec; /*!< set to non-zero if itsa #exec statement */
+ char *exec_file; /*!< if it's an exec, you'll have both the /var/tmp to read, and the original script */
+ char *included_file; /*!< file name included */
+ int inclusion_count; /*!< if the file is included more than once, a running count thereof -- but, worry not,
+ we explode the instances and will include those-- so all entries will be unique */
+ int output; /*!< a flag to indicate if the inclusion has been output */
+ struct ast_config_include *next; /*!< ptr to next inclusion in the list */
+};
+
+struct ast_variable *ast_variable_new(const char *name, const char *value, const char *filename)
+{
+ struct ast_variable *variable;
+ int name_len = strlen(name) + 1;
+ int val_len = strlen(value) + 1;
+ int fn_len = strlen(filename) + 1;
+
+ if ((variable = ast_calloc(1, name_len + val_len + fn_len + sizeof(*variable)))) {
+ char *dst = variable->stuff; /* writable space starts here */
+ variable->name = strcpy(dst, name);
+ dst += name_len;
+ variable->value = strcpy(dst, value);
+ dst += val_len;
+ variable->file = strcpy(dst, filename);
+ }
+ return variable;
+}
+
+struct ast_config_include *ast_include_new(struct ast_config *conf, const char *from_file, const char *included_file, int is_exec, const char *exec_file, int from_lineno, char *real_included_file_name, int real_included_file_name_size)
+{
+ /* a file should be included ONCE. Otherwise, if one of the instances is changed,
+ then all be changed. -- how do we know to include it? -- Handling modified
+ instances is possible, I'd have
+ to create a new master for each instance. */
+ struct ast_config_include *inc;
+ struct stat statbuf;
+
+ inc = ast_include_find(conf, included_file);
+ if (inc) {
+ do {
+ inc->inclusion_count++;
+ snprintf(real_included_file_name, real_included_file_name_size, "%s~~%d", included_file, inc->inclusion_count);
+ } while (stat(real_included_file_name, &statbuf) == 0);
+ ast_log(LOG_WARNING,"'%s', line %d: Same File included more than once! This data will be saved in %s if saved back to disk.\n", from_file, from_lineno, real_included_file_name);
+ } else
+ *real_included_file_name = 0;
+
+ inc = ast_calloc(1,sizeof(struct ast_config_include));
+ inc->include_location_file = ast_strdup(from_file);
+ inc->include_location_lineno = from_lineno;
+ if (!ast_strlen_zero(real_included_file_name))
+ inc->included_file = ast_strdup(real_included_file_name);
+ else
+ inc->included_file = ast_strdup(included_file);
+
+ inc->exec = is_exec;
+ if (is_exec)
+ inc->exec_file = ast_strdup(exec_file);
+
+ /* attach this new struct to the conf struct */
+ inc->next = conf->includes;
+ conf->includes = inc;
+
+ return inc;
+}
+
+void ast_include_rename(struct ast_config *conf, const char *from_file, const char *to_file)
+{
+ struct ast_config_include *incl;
+ struct ast_category *cat;
+ struct ast_variable *v;
+
+ int from_len = strlen(from_file);
+ int to_len = strlen(to_file);
+
+ if (strcmp(from_file, to_file) == 0) /* no use wasting time if the name is the same */
+ return;
+
+ /* the manager code allows you to read in one config file, then
+ write it back out under a different name. But, the new arrangement
+ ties output lines to the file name. So, before you try to write
+ the config file to disk, better riffle thru the data and make sure
+ the file names are changed.
+ */
+ /* file names are on categories, includes (of course), and on variables. So,
+ traverse all this and swap names */
+
+ for (incl = conf->includes; incl; incl=incl->next) {
+ if (strcmp(incl->include_location_file,from_file) == 0) {
+ if (from_len >= to_len)
+ strcpy(incl->include_location_file, to_file);
+ else {
+ free(incl->include_location_file);
+ incl->include_location_file = strdup(to_file);
+ }
+ }
+ }
+ for (cat = conf->root; cat; cat = cat->next) {
+ if (strcmp(cat->file,from_file) == 0) {
+ if (from_len >= to_len)
+ strcpy(cat->file, to_file);
+ else {
+ free(cat->file);
+ cat->file = strdup(to_file);
+ }
+ }
+ for (v = cat->root; v; v = v->next) {
+ if (strcmp(v->file,from_file) == 0) {
+ if (from_len >= to_len)
+ strcpy(v->file, to_file);
+ else {
+ free(v->file);
+ v->file = strdup(to_file);
+ }
+ }
+ }
+ }
+}
+
+struct ast_config_include *ast_include_find(struct ast_config *conf, const char *included_file)
+{
+ struct ast_config_include *x;
+ for (x=conf->includes;x;x=x->next) {
+ if (strcmp(x->included_file,included_file) == 0)
+ return x;
+ }
+ return 0;
+}
+
+
+void ast_variable_append(struct ast_category *category, struct ast_variable *variable)
+{
+ if (!variable)
+ return;
+ if (category->last)
+ category->last->next = variable;
+ else
+ category->root = variable;
+ category->last = variable;
+ while (category->last->next)
+ category->last = category->last->next;
+}
+
+void ast_variables_destroy(struct ast_variable *v)
+{
+ struct ast_variable *vn;
+
+ while (v) {
+ vn = v;
+ v = v->next;
+ ast_free(vn);
+ }
+}
+
+struct ast_variable *ast_variable_browse(const struct ast_config *config, const char *category)
+{
+ struct ast_category *cat = NULL;
+
+ if (category && config->last_browse && (config->last_browse->name == category))
+ cat = config->last_browse;
+ else
+ cat = ast_category_get(config, category);
+
+ return (cat) ? cat->root : NULL;
+}
+
+const char *ast_config_option(struct ast_config *cfg, const char *cat, const char *var)
+{
+ const char *tmp;
+ tmp = ast_variable_retrieve(cfg, cat, var);
+ if (!tmp)
+ tmp = ast_variable_retrieve(cfg, "general", var);
+ return tmp;
+}
+
+
+const char *ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable)
+{
+ struct ast_variable *v;
+
+ if (category) {
+ for (v = ast_variable_browse(config, category); v; v = v->next) {
+ if (!strcasecmp(variable, v->name))
+ return v->value;
+ }
+ } else {
+ struct ast_category *cat;
+
+ for (cat = config->root; cat; cat = cat->next)
+ for (v = cat->root; v; v = v->next)
+ if (!strcasecmp(variable, v->name))
+ return v->value;
+ }
+
+ return NULL;
+}
+
+static struct ast_variable *variable_clone(const struct ast_variable *old)
+{
+ struct ast_variable *new = ast_variable_new(old->name, old->value, old->file);
+
+ if (new) {
+ new->lineno = old->lineno;
+ new->object = old->object;
+ new->blanklines = old->blanklines;
+ /* TODO: clone comments? */
+ }
+
+ return new;
+}
+
+static void move_variables(struct ast_category *old, struct ast_category *new)
+{
+ struct ast_variable *var = old->root;
+
+ old->root = NULL;
+ /* we can just move the entire list in a single op */
+ ast_variable_append(new, var);
+}
+
+struct ast_category *ast_category_new(const char *name, const char *in_file, int lineno)
+{
+ struct ast_category *category;
+
+ if ((category = ast_calloc(1, sizeof(*category))))
+ ast_copy_string(category->name, name, sizeof(category->name));
+ category->file = strdup(in_file);
+ category->lineno = lineno; /* if you don't know the lineno, set it to 999999 or something real big */
+ return category;
+}
+
+static struct ast_category *category_get(const struct ast_config *config, const char *category_name, int ignored)
+{
+ struct ast_category *cat;
+
+ /* try exact match first, then case-insensitive match */
+ for (cat = config->root; cat; cat = cat->next) {
+ if (cat->name == category_name && (ignored || !cat->ignored))
+ return cat;
+ }
+
+ for (cat = config->root; cat; cat = cat->next) {
+ if (!strcasecmp(cat->name, category_name) && (ignored || !cat->ignored))
+ return cat;
+ }
+
+ return NULL;
+}
+
+struct ast_category *ast_category_get(const struct ast_config *config, const char *category_name)
+{
+ return category_get(config, category_name, 0);
+}
+
+int ast_category_exist(const struct ast_config *config, const char *category_name)
+{
+ return !!ast_category_get(config, category_name);
+}
+
+void ast_category_append(struct ast_config *config, struct ast_category *category)
+{
+ if (config->last)
+ config->last->next = category;
+ else
+ config->root = category;
+ category->include_level = config->include_level;
+ config->last = category;
+ config->current = category;
+}
+
+static void ast_destroy_comments(struct ast_category *cat)
+{
+ struct ast_comment *n, *p;
+
+ for (p=cat->precomments; p; p=n) {
+ n = p->next;
+ free(p);
+ }
+ for (p=cat->sameline; p; p=n) {
+ n = p->next;
+ free(p);
+ }
+ for (p=cat->trailing; p; p=n) {
+ n = p->next;
+ free(p);
+ }
+ cat->precomments = NULL;
+ cat->sameline = NULL;
+ cat->trailing = NULL;
+}
+
+static void ast_destroy_template_list(struct ast_category *cat)
+{
+ struct ast_category_template_instance *x;
+
+ while ((x = AST_LIST_REMOVE_HEAD(&cat->template_instances, next)))
+ free(x);
+}
+
+void ast_category_destroy(struct ast_category *cat)
+{
+ ast_variables_destroy(cat->root);
+ if (cat->file) {
+ free(cat->file);
+ cat->file = 0;
+ }
+ ast_destroy_comments(cat);
+ ast_destroy_template_list(cat);
+ ast_free(cat);
+}
+
+static void ast_includes_destroy(struct ast_config_include *incls)
+{
+ struct ast_config_include *incl,*inclnext;
+
+ for (incl=incls; incl; incl = inclnext) {
+ inclnext = incl->next;
+ if (incl->include_location_file)
+ free(incl->include_location_file);
+ if (incl->exec_file)
+ free(incl->exec_file);
+ if (incl->included_file)
+ free(incl->included_file);
+ free(incl);
+ }
+}
+
+static struct ast_category *next_available_category(struct ast_category *cat)
+{
+ for (; cat && cat->ignored; cat = cat->next);
+
+ return cat;
+}
+
+/*! return the first var of a category */
+struct ast_variable *ast_category_first(struct ast_category *cat)
+{
+ return (cat) ? cat->root : NULL;
+}
+
+struct ast_variable *ast_category_root(struct ast_config *config, char *cat)
+{
+ struct ast_category *category = ast_category_get(config, cat);
+
+ if (category)
+ return category->root;
+ return NULL;
+}
+
+char *ast_category_browse(struct ast_config *config, const char *prev)
+{
+ struct ast_category *cat = NULL;
+
+ if (prev && config->last_browse && (config->last_browse->name == prev))
+ cat = config->last_browse->next;
+ else if (!prev && config->root)
+ cat = config->root;
+ else if (prev) {
+ for (cat = config->root; cat; cat = cat->next) {
+ if (cat->name == prev) {
+ cat = cat->next;
+ break;
+ }
+ }
+ if (!cat) {
+ for (cat = config->root; cat; cat = cat->next) {
+ if (!strcasecmp(cat->name, prev)) {
+ cat = cat->next;
+ break;
+ }
+ }
+ }
+ }
+
+ if (cat)
+ cat = next_available_category(cat);
+
+ config->last_browse = cat;
+ return (cat) ? cat->name : NULL;
+}
+
+struct ast_variable *ast_category_detach_variables(struct ast_category *cat)
+{
+ struct ast_variable *v;
+
+ v = cat->root;
+ cat->root = NULL;
+ cat->last = NULL;
+
+ return v;
+}
+
+void ast_category_rename(struct ast_category *cat, const char *name)
+{
+ ast_copy_string(cat->name, name, sizeof(cat->name));
+}
+
+static void inherit_category(struct ast_category *new, const struct ast_category *base)
+{
+ struct ast_variable *var;
+ struct ast_category_template_instance *x = ast_calloc(1,sizeof(struct ast_category_template_instance));
+
+ strcpy(x->name, base->name);
+ x->inst = base;
+ AST_LIST_INSERT_TAIL(&new->template_instances, x, next);
+ for (var = base->root; var; var = var->next)
+ ast_variable_append(new, variable_clone(var));
+}
+
+struct ast_config *ast_config_new(void)
+{
+ struct ast_config *config;
+
+ if ((config = ast_calloc(1, sizeof(*config))))
+ config->max_include_level = MAX_INCLUDE_LEVEL;
+ return config;
+}
+
+int ast_variable_delete(struct ast_category *category, const char *variable, const char *match)
+{
+ struct ast_variable *cur, *prev=NULL, *curn;
+ int res = -1;
+
+ cur = category->root;
+ while (cur) {
+ if (cur->name == variable) {
+ if (prev) {
+ prev->next = cur->next;
+ if (cur == category->last)
+ category->last = prev;
+ } else {
+ category->root = cur->next;
+ if (cur == category->last)
+ category->last = NULL;
+ }
+ cur->next = NULL;
+ ast_variables_destroy(cur);
+ return 0;
+ }
+ prev = cur;
+ cur = cur->next;
+ }
+
+ prev = NULL;
+ cur = category->root;
+ while (cur) {
+ curn = cur->next;
+ if (!strcasecmp(cur->name, variable) && (ast_strlen_zero(match) || !strcasecmp(cur->value, match))) {
+ if (prev) {
+ prev->next = cur->next;
+ if (cur == category->last)
+ category->last = prev;
+ } else {
+ category->root = cur->next;
+ if (cur == category->last)
+ category->last = NULL;
+ }
+ cur->next = NULL;
+ ast_variables_destroy(cur);
+ res = 0;
+ } else
+ prev = cur;
+
+ cur = curn;
+ }
+ return res;
+}
+
+int ast_variable_update(struct ast_category *category, const char *variable,
+ const char *value, const char *match, unsigned int object)
+{
+ struct ast_variable *cur, *prev=NULL, *newer=NULL;
+
+ for (cur = category->root; cur; prev = cur, cur = cur->next) {
+ if (strcasecmp(cur->name, variable) ||
+ (!ast_strlen_zero(match) && strcasecmp(cur->value, match)))
+ continue;
+
+ if (!(newer = ast_variable_new(variable, value, cur->file)))
+ return -1;
+
+ newer->next = cur->next;
+ newer->object = cur->object || object;
+ if (prev)
+ prev->next = newer;
+ else
+ category->root = newer;
+ if (category->last == cur)
+ category->last = newer;
+
+ cur->next = NULL;
+ ast_variables_destroy(cur);
+
+ return 0;
+ }
+
+ if (prev)
+ prev->next = newer;
+ else
+ category->root = newer;
+
+ return 0;
+}
+
+int ast_category_delete(struct ast_config *cfg, const char *category)
+{
+ struct ast_category *prev=NULL, *cat;
+
+ cat = cfg->root;
+ while (cat) {
+ if (cat->name == category) {
+ if (prev) {
+ prev->next = cat->next;
+ if (cat == cfg->last)
+ cfg->last = prev;
+ } else {
+ cfg->root = cat->next;
+ if (cat == cfg->last)
+ cfg->last = NULL;
+ }
+ ast_category_destroy(cat);
+ return 0;
+ }
+ prev = cat;
+ cat = cat->next;
+ }
+
+ prev = NULL;
+ cat = cfg->root;
+ while (cat) {
+ if (!strcasecmp(cat->name, category)) {
+ if (prev) {
+ prev->next = cat->next;
+ if (cat == cfg->last)
+ cfg->last = prev;
+ } else {
+ cfg->root = cat->next;
+ if (cat == cfg->last)
+ cfg->last = NULL;
+ }
+ ast_category_destroy(cat);
+ return 0;
+ }
+ prev = cat;
+ cat = cat->next;
+ }
+ return -1;
+}
+
+void ast_config_destroy(struct ast_config *cfg)
+{
+ struct ast_category *cat, *catn;
+
+ if (!cfg)
+ return;
+
+ ast_includes_destroy(cfg->includes);
+
+ cat = cfg->root;
+ while (cat) {
+ catn = cat;
+ cat = cat->next;
+ ast_category_destroy(catn);
+ }
+ ast_free(cfg);
+}
+
+struct ast_category *ast_config_get_current_category(const struct ast_config *cfg)
+{
+ return cfg->current;
+}
+
+void ast_config_set_current_category(struct ast_config *cfg, const struct ast_category *cat)
+{
+ /* cast below is just to silence compiler warning about dropping "const" */
+ cfg->current = (struct ast_category *) cat;
+}
+
+enum config_cache_attribute_enum {
+ ATTRIBUTE_INCLUDE = 0,
+ ATTRIBUTE_EXEC = 1,
+};
+
+static void config_cache_attribute(const char *configfile, enum config_cache_attribute_enum attrtype, const char *filename)
+{
+ struct cache_file_mtime *cfmtime;
+ struct cache_file_include *cfinclude;
+ struct stat statbuf = { 0, };
+
+ /* Find our cached entry for this configuration file */
+ AST_LIST_LOCK(&cfmtime_head);
+ AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
+ if (!strcmp(cfmtime->filename, configfile))
+ break;
+ }
+ if (!cfmtime) {
+ cfmtime = ast_calloc(1, sizeof(*cfmtime) + strlen(configfile) + 1);
+ if (!cfmtime) {
+ AST_LIST_UNLOCK(&cfmtime_head);
+ return;
+ }
+ AST_LIST_HEAD_INIT(&cfmtime->includes);
+ strcpy(cfmtime->filename, configfile);
+ /* Note that the file mtime is initialized to 0, i.e. 1970 */
+ AST_LIST_INSERT_TAIL(&cfmtime_head, cfmtime, list);
+ }
+
+ if (!stat(configfile, &statbuf))
+ cfmtime->mtime = 0;
+ else
+ cfmtime->mtime = statbuf.st_mtime;
+
+ switch (attrtype) {
+ case ATTRIBUTE_INCLUDE:
+ AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
+ if (!strcmp(cfinclude->include, filename)) {
+ AST_LIST_UNLOCK(&cfmtime_head);
+ return;
+ }
+ }
+ cfinclude = ast_calloc(1, sizeof(*cfinclude) + strlen(filename) + 1);
+ if (!cfinclude) {
+ AST_LIST_UNLOCK(&cfmtime_head);
+ return;
+ }
+ strcpy(cfinclude->include, filename);
+ AST_LIST_INSERT_TAIL(&cfmtime->includes, cfinclude, list);
+ break;
+ case ATTRIBUTE_EXEC:
+ cfmtime->has_exec = 1;
+ break;
+ }
+ AST_LIST_UNLOCK(&cfmtime_head);
+}
+
+/*! \brief parse one line in the configuration.
+ * We can have a category header [foo](...)
+ * a directive #include / #exec
+ * or a regular line name = value
+ */
+static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
+ char *buf, int lineno, const char *configfile, struct ast_flags flags,
+ struct ast_str *comment_buffer,
+ struct ast_str *lline_buffer,
+ const char *suggested_include_file,
+ struct ast_category **last_cat, struct ast_variable **last_var)
+{
+ char *c;
+ char *cur = buf;
+ struct ast_variable *v;
+ char cmd[512], exec_file[512];
+
+ /* Actually parse the entry */
+ if (cur[0] == '[') { /* A category header */
+ /* format is one of the following:
+ * [foo] define a new category named 'foo'
+ * [foo](!) define a new template category named 'foo'
+ * [foo](+) append to category 'foo', error if foo does not exist.
+ * [foo](a) define a new category and inherit from template a.
+ * You can put a comma-separated list of templates and '!' and '+'
+ * between parentheses, with obvious meaning.
+ */
+ struct ast_category *newcat = NULL;
+ char *catname;
+
+ c = strchr(cur, ']');
+ if (!c) {
+ ast_log(LOG_WARNING, "parse error: no closing ']', line %d of %s\n", lineno, configfile);
+ return -1;
+ }
+ *c++ = '\0';
+ cur++;
+ if (*c++ != '(')
+ c = NULL;
+ catname = cur;
+ if (!(*cat = newcat = ast_category_new(catname, ast_strlen_zero(suggested_include_file)?configfile:suggested_include_file, lineno))) {
+ return -1;
+ }
+ (*cat)->lineno = lineno;
+ *last_var = 0;
+ *last_cat = newcat;
+
+ /* add comments */
+ if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
+ newcat->precomments = ALLOC_COMMENT(comment_buffer);
+ if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
+ newcat->sameline = ALLOC_COMMENT(lline_buffer);
+ if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
+ CB_RESET(comment_buffer, lline_buffer);
+
+ /* If there are options or categories to inherit from, process them now */
+ if (c) {
+ if (!(cur = strchr(c, ')'))) {
+ ast_log(LOG_WARNING, "parse error: no closing ')', line %d of %s\n", lineno, configfile);
+ return -1;
+ }
+ *cur = '\0';
+ while ((cur = strsep(&c, ","))) {
+ if (!strcasecmp(cur, "!")) {
+ (*cat)->ignored = 1;
+ } else if (!strcasecmp(cur, "+")) {
+ *cat = category_get(cfg, catname, 1);
+ if (!(*cat)) {
+ if (newcat)
+ ast_category_destroy(newcat);
+ ast_log(LOG_WARNING, "Category addition requested, but category '%s' does not exist, line %d of %s\n", catname, lineno, configfile);
+ return -1;
+ }
+ if (newcat) {
+ move_variables(newcat, *cat);
+ ast_category_destroy(newcat);
+ newcat = NULL;
+ }
+ } else {
+ struct ast_category *base;
+
+ base = category_get(cfg, cur, 1);
+ if (!base) {
+ ast_log(LOG_WARNING, "Inheritance requested, but category '%s' does not exist, line %d of %s\n", cur, lineno, configfile);
+ return -1;
+ }
+ inherit_category(*cat, base);
+ }
+ }
+ }
+ if (newcat)
+ ast_category_append(cfg, *cat);
+ } else if (cur[0] == '#') { /* A directive - #include or #exec */
+ char *cur2;
+ char real_inclusion_name[256];
+ struct ast_config_include *inclu;
+ int do_include = 0; /* otherwise, it is exec */
+
+ cur++;
+ c = cur;
+ while (*c && (*c > 32)) c++;
+ if (*c) {
+ *c = '\0';
+ /* Find real argument */
+ c = ast_skip_blanks(c + 1);
+ if (!(*c))
+ c = NULL;
+ } else
+ c = NULL;
+ if (!strcasecmp(cur, "include")) {
+ do_include = 1;
+ } else if (!strcasecmp(cur, "exec")) {
+ if (!ast_opt_exec_includes) {
+ ast_log(LOG_WARNING, "Cannot perform #exec unless execincludes option is enabled in asterisk.conf (options section)!\n");
+ return 0; /* XXX is this correct ? or we should return -1 ? */
+ }
+ } else {
+ ast_log(LOG_WARNING, "Unknown directive '%s' at line %d of %s\n", cur, lineno, configfile);
+ return 0; /* XXX is this correct ? or we should return -1 ? */
+ }
+
+ if (c == NULL) {
+ ast_log(LOG_WARNING, "Directive '#%s' needs an argument (%s) at line %d of %s\n",
+ do_include ? "include" : "exec",
+ do_include ? "filename" : "/path/to/executable",
+ lineno,
+ configfile);
+ return 0; /* XXX is this correct ? or we should return -1 ? */
+ }
+
+ /* Strip off leading and trailing "'s and <>'s */
+ while ((*c == '<') || (*c == '>') || (*c == '\"')) c++;
+ /* Get rid of leading mess */
+ cur = c;
+ cur2 = cur;
+ while (!ast_strlen_zero(cur)) {
+ c = cur + strlen(cur) - 1;
+ if ((*c == '>') || (*c == '<') || (*c == '\"'))
+ *c = '\0';
+ else
+ break;
+ }
+ /* #exec </path/to/executable>
+ We create a tmp file, then we #include it, then we delete it. */
+ if (!do_include) {
+ if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
+ config_cache_attribute(configfile, ATTRIBUTE_EXEC, NULL);
+ snprintf(exec_file, sizeof(exec_file), "/var/tmp/exec.%d.%ld", (int)time(NULL), (long)pthread_self());
+ snprintf(cmd, sizeof(cmd), "%s > %s 2>&1", cur, exec_file);
+ ast_safe_system(cmd);
+ cur = exec_file;
+ } else {
+ if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
+ config_cache_attribute(configfile, ATTRIBUTE_INCLUDE, cur);
+ exec_file[0] = '\0';
+ }
+ /* A #include */
+ /* record this inclusion */
+ inclu = ast_include_new(cfg, configfile, cur, !do_include, cur2, lineno, real_inclusion_name, sizeof(real_inclusion_name));
+
+ do_include = ast_config_internal_load(cur, cfg, flags, real_inclusion_name) ? 1 : 0;
+ if (!ast_strlen_zero(exec_file))
+ unlink(exec_file);
+ if (!do_include) {
+ ast_log(LOG_ERROR, "The file '%s' was listed as a #include but it does not exist.\n", cur);
+ return -1;
+ }
+ /* XXX otherwise what ? the default return is 0 anyways */
+
+ } else {
+ /* Just a line (variable = value) */
+ if (!(*cat)) {
+ ast_log(LOG_WARNING,
+ "parse error: No category context for line %d of %s\n", lineno, configfile);
+ return -1;
+ }
+ c = strchr(cur, '=');
+ if (c) {
+ int object;
+ *c = 0;
+ c++;
+ /* Ignore > in => */
+ if (*c== '>') {
+ object = 1;
+ c++;
+ } else
+ object = 0;
+ if ((v = ast_variable_new(ast_strip(cur), ast_strip(c), *suggested_include_file ? suggested_include_file : configfile))) {
+ v->lineno = lineno;
+ v->object = object;
+ *last_cat = 0;
+ *last_var = v;
+ /* Put and reset comments */
+ v->blanklines = 0;
+ ast_variable_append(*cat, v);
+ /* add comments */
+ if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
+ v->precomments = ALLOC_COMMENT(comment_buffer);
+ if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
+ v->sameline = ALLOC_COMMENT(lline_buffer);
+ if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
+ CB_RESET(comment_buffer, lline_buffer);
+
+ } else {
+ return -1;
+ }
+ } else {
+ ast_log(LOG_WARNING, "No '=' (equal sign) in line %d of %s\n", lineno, configfile);
+ }
+ }
+ return 0;
+}
+
+static struct ast_config *config_text_file_load(const char *database, const char *table, const char *filename, struct ast_config *cfg, struct ast_flags flags, const char *suggested_include_file)
+{
+ char fn[256];
+ char buf[8192];
+ char *new_buf, *comment_p, *process_buf;
+ FILE *f;
+ int lineno=0;
+ int comment = 0, nest[MAX_NESTED_COMMENTS];
+ struct ast_category *cat = NULL;
+ int count = 0;
+ struct stat statbuf;
+ struct cache_file_mtime *cfmtime = NULL;
+ struct cache_file_include *cfinclude;
+ struct ast_variable *last_var = 0;
+ struct ast_category *last_cat = 0;
+ /*! Growable string buffer */
+ struct ast_str *comment_buffer = NULL; /*!< this will be a comment collector.*/
+ struct ast_str *lline_buffer = NULL; /*!< A buffer for stuff behind the ; */
+
+ if (cfg)
+ cat = ast_config_get_current_category(cfg);
+
+ if (filename[0] == '/') {
+ ast_copy_string(fn, filename, sizeof(fn));
+ } else {
+ snprintf(fn, sizeof(fn), "%s/%s", ast_config_AST_CONFIG_DIR, filename);
+ }
+
+ if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
+ comment_buffer = ast_str_create(CB_SIZE);
+ if (comment_buffer)
+ lline_buffer = ast_str_create(CB_SIZE);
+ if (!lline_buffer) {
+ if (comment_buffer)
+ ast_free(comment_buffer);
+ ast_log(LOG_ERROR, "Failed to initialize the comment buffer!\n");
+ return NULL;
+ }
+ }
+#ifdef AST_INCLUDE_GLOB
+ {
+ int glob_ret;
+ glob_t globbuf;
+ globbuf.gl_offs = 0; /* initialize it to silence gcc */
+ glob_ret = glob(fn, MY_GLOB_FLAGS, NULL, &globbuf);
+ if (glob_ret == GLOB_NOSPACE)
+ ast_log(LOG_WARNING,
+ "Glob Expansion of pattern '%s' failed: Not enough memory\n", fn);
+ else if (glob_ret == GLOB_ABORTED)
+ ast_log(LOG_WARNING,
+ "Glob Expansion of pattern '%s' failed: Read error\n", fn);
+ else {
+ /* loop over expanded files */
+ int i;
+ for (i=0; i<globbuf.gl_pathc; i++) {
+ ast_copy_string(fn, globbuf.gl_pathv[i], sizeof(fn));
+#endif
+ /*
+ * The following is not a loop, but just a convenient way to define a block
+ * (using do { } while(0) ), and be able to exit from it with 'continue'
+ * or 'break' in case of errors. Nice trick.
+ */
+ do {
+ if (stat(fn, &statbuf))
+ continue;
+
+ if (!S_ISREG(statbuf.st_mode)) {
+ ast_log(LOG_WARNING, "'%s' is not a regular file, ignoring\n", fn);
+ continue;
+ }
+
+ if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE)) {
+ /* Find our cached entry for this configuration file */
+ AST_LIST_LOCK(&cfmtime_head);
+ AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
+ if (!strcmp(cfmtime->filename, fn))
+ break;
+ }
+ if (!cfmtime) {
+ cfmtime = ast_calloc(1, sizeof(*cfmtime) + strlen(fn) + 1);
+ if (!cfmtime)
+ continue;
+ AST_LIST_HEAD_INIT(&cfmtime->includes);
+ strcpy(cfmtime->filename, fn);
+ /* Note that the file mtime is initialized to 0, i.e. 1970 */
+ AST_LIST_INSERT_TAIL(&cfmtime_head, cfmtime, list);
+ }
+ }
+
+ if (cfmtime && (!cfmtime->has_exec) && (cfmtime->mtime == statbuf.st_mtime) && ast_test_flag(&flags, CONFIG_FLAG_FILEUNCHANGED)) {
+ /* File is unchanged, what about the (cached) includes (if any)? */
+ int unchanged = 1;
+ AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
+ /* We must glob here, because if we did not, then adding a file to globbed directory would
+ * incorrectly cause no reload to be necessary. */
+ char fn2[256];
+#ifdef AST_INCLUDE_GLOB
+ int glob_ret;
+ glob_t globbuf = { .gl_offs = 0 };
+ glob_ret = glob(cfinclude->include, MY_GLOB_FLAGS, NULL, &globbuf);
+ /* On error, we reparse */
+ if (glob_ret == GLOB_NOSPACE || glob_ret == GLOB_ABORTED)
+ unchanged = 0;
+ else {
+ /* loop over expanded files */
+ int j;
+ for (j = 0; j < globbuf.gl_pathc; j++) {
+ ast_copy_string(fn2, globbuf.gl_pathv[j], sizeof(fn2));
+#else
+ ast_copy_string(fn2, cfinclude->include);
+#endif
+ if (config_text_file_load(NULL, NULL, fn2, NULL, flags, "") == NULL) { /* that last field needs to be looked at in this case... TODO */
+ unchanged = 0;
+ /* One change is enough to short-circuit and reload the whole shebang */
+ break;
+ }
+#ifdef AST_INCLUDE_GLOB
+ }
+ }
+#endif
+ }
+
+ if (unchanged) {
+ AST_LIST_UNLOCK(&cfmtime_head);
+ return CONFIG_STATUS_FILEUNCHANGED;
+ }
+ }
+ if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
+ AST_LIST_UNLOCK(&cfmtime_head);
+
+ /* If cfg is NULL, then we just want an answer */
+ if (cfg == NULL)
+ return NULL;
+
+ if (cfmtime)
+ cfmtime->mtime = statbuf.st_mtime;
+
+ ast_verb(2, "Parsing '%s': ", fn);
+ fflush(stdout);
+ if (!(f = fopen(fn, "r"))) {
+ ast_debug(1, "No file to parse: %s\n", fn);
+ ast_verb(2, "Not found (%s)\n", strerror(errno));
+ continue;
+ }
+ count++;
+ /* If we get to this point, then we're loading regardless */
+ ast_clear_flag(&flags, CONFIG_FLAG_FILEUNCHANGED);
+ ast_debug(1, "Parsing %s\n", fn);
+ ast_verb(2, "Found\n");
+ while (!feof(f)) {
+ lineno++;
+ if (fgets(buf, sizeof(buf), f)) {
+ if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && lline_buffer && lline_buffer->used) {
+ CB_ADD(&comment_buffer, lline_buffer->str); /* add the current lline buffer to the comment buffer */
+ lline_buffer->used = 0; /* erase the lline buffer */
+ }
+
+ new_buf = buf;
+ if (comment)
+ process_buf = NULL;
+ else
+ process_buf = buf;
+
+ if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && comment_buffer->used && (ast_strlen_zero(buf) || strlen(buf) == strspn(buf," \t\n\r"))) {
+ /* blank line? really? Can we add it to an existing comment and maybe preserve inter- and post- comment spacing? */
+ CB_ADD(&comment_buffer, "\n"); /* add a newline to the comment buffer */
+ continue; /* go get a new line, then */
+ }
+
+ while ((comment_p = strchr(new_buf, COMMENT_META))) {
+ if ((comment_p > new_buf) && (*(comment_p-1) == '\\')) {
+ /* Escaped semicolons aren't comments. */
+ new_buf = comment_p + 1;
+ } else if (comment_p[1] == COMMENT_TAG && comment_p[2] == COMMENT_TAG && (comment_p[3] != '-')) {
+ /* Meta-Comment start detected ";--" */
+ if (comment < MAX_NESTED_COMMENTS) {
+ *comment_p = '\0';
+ new_buf = comment_p + 3;
+ comment++;
+ nest[comment-1] = lineno;
+ } else {
+ ast_log(LOG_ERROR, "Maximum nest limit of %d reached.\n", MAX_NESTED_COMMENTS);
+ }
+ } else if ((comment_p >= new_buf + 2) &&
+ (*(comment_p - 1) == COMMENT_TAG) &&
+ (*(comment_p - 2) == COMMENT_TAG)) {
+ /* Meta-Comment end detected */
+ comment--;
+ new_buf = comment_p + 1;
+ if (!comment) {
+ /* Back to non-comment now */
+ if (process_buf) {
+ /* Actually have to move what's left over the top, then continue */
+ char *oldptr;
+ oldptr = process_buf + strlen(process_buf);
+ if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
+ CB_ADD(&comment_buffer, ";");
+ CB_ADD_LEN(&comment_buffer, oldptr+1, new_buf-oldptr-1);
+ }
+
+ memmove(oldptr, new_buf, strlen(new_buf) + 1);
+ new_buf = oldptr;
+ } else
+ process_buf = new_buf;
+ }
+ } else {
+ if (!comment) {
+ /* If ; is found, and we are not nested in a comment,
+ we immediately stop all comment processing */
+ if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
+ CB_ADD(&lline_buffer, comment_p);
+ }
+ *comment_p = '\0';
+ new_buf = comment_p;
+ } else
+ new_buf = comment_p + 1;
+ }
+ }
+ if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment && !process_buf ) {
+ CB_ADD(&comment_buffer, buf); /* the whole line is a comment, store it */
+ }
+
+ if (process_buf) {
+ char *buf = ast_strip(process_buf);
+ if (!ast_strlen_zero(buf)) {
+ if (process_text_line(cfg, &cat, buf, lineno, fn, flags, comment_buffer, lline_buffer, suggested_include_file, &last_cat, &last_var)) {
+ cfg = NULL;
+ break;
+ }
+ }
+ }
+ }
+ }
+ /* end of file-- anything in a comment buffer? */
+ if (last_cat) {
+ if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && comment_buffer->used ) {
+ if (lline_buffer && lline_buffer->used) {
+ CB_ADD(&comment_buffer, lline_buffer->str); /* add the current lline buffer to the comment buffer */
+ lline_buffer->used = 0; /* erase the lline buffer */
+ }
+ last_cat->trailing = ALLOC_COMMENT(comment_buffer);
+ }
+ } else if (last_var) {
+ if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && comment_buffer->used ) {
+ if (lline_buffer && lline_buffer->used) {
+ CB_ADD(&comment_buffer, lline_buffer->str); /* add the current lline buffer to the comment buffer */
+ lline_buffer->used = 0; /* erase the lline buffer */
+ }
+ last_var->trailing = ALLOC_COMMENT(comment_buffer);
+ }
+ } else {
+ if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && comment_buffer->used) {
+ ast_debug(1, "Nothing to attach comments to, discarded: %s\n", comment_buffer->str);
+ }
+ }
+ if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
+ CB_RESET(comment_buffer, lline_buffer);
+
+ fclose(f);
+ } while (0);
+ if (comment) {
+ ast_log(LOG_WARNING,"Unterminated comment detected beginning on line %d\n", nest[comment - 1]);
+ }
+#ifdef AST_INCLUDE_GLOB
+ if (cfg == NULL || cfg == CONFIG_STATUS_FILEUNCHANGED)
+ break;
+ }
+ globfree(&globbuf);
+ }
+ }
+#endif
+
+ if (cfg && cfg != CONFIG_STATUS_FILEUNCHANGED && cfg->include_level == 1 && ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
+ if (comment_buffer)
+ ast_free(comment_buffer);
+ if (lline_buffer)
+ ast_free(lline_buffer);
+ comment_buffer = NULL;
+ lline_buffer = NULL;
+ }
+
+ if (count == 0)
+ return NULL;
+
+ return cfg;
+}
+
+
+/* NOTE: categories and variables each have a file and lineno attribute. On a save operation, these are used to determine
+ which file and line number to write out to. Thus, an entire hierarchy of config files (via #include statements) can be
+ recreated. BUT, care must be taken to make sure that every cat and var has the proper file name stored, or you may
+ be shocked and mystified as to why things are not showing up in the files!
+
+ Also, All #include/#exec statements are recorded in the "includes" LL in the ast_config structure. The file name
+ and line number are stored for each include, plus the name of the file included, so that these statements may be
+ included in the output files on a file_save operation.
+
+ The lineno's are really just for relative placement in the file. There is no attempt to make sure that blank lines
+ are included to keep the lineno's the same between input and output. The lineno fields are used mainly to determine
+ the position of the #include and #exec directives. So, blank lines tend to disappear from a read/rewrite operation,
+ and a header gets added.
+
+ vars and category heads are output in the order they are stored in the config file. So, if the software
+ shuffles these at all, then the placement of #include directives might get a little mixed up, because the
+ file/lineno data probably won't get changed.
+
+*/
+
+static void gen_header(FILE *f1, const char *configfile, const char *fn, const char *generator)
+{
+ char date[256]="";
+ time_t t;
+
+ time(&t);
+ ast_copy_string(date, ctime(&t), sizeof(date));
+
+ fprintf(f1, ";!\n");
+ fprintf(f1, ";! Automatically generated configuration file\n");
+ if (strcmp(configfile, fn))
+ fprintf(f1, ";! Filename: %s (%s)\n", configfile, fn);
+ else
+ fprintf(f1, ";! Filename: %s\n", configfile);
+ fprintf(f1, ";! Generator: %s\n", generator);
+ fprintf(f1, ";! Creation Date: %s", date);
+ fprintf(f1, ";!\n");
+}
+
+static void inclfile_destroy(void *obj)
+{
+ const struct inclfile *o = obj;
+
+ if (o->fname)
+ free(o->fname);
+}
+
+
+static void set_fn(char *fn, int fn_size, const char *file, const char *configfile, struct ao2_container *fileset, struct inclfile **fi)
+{
+ struct inclfile lookup;
+
+ if (!file || file[0] == 0) {
+ if (configfile[0] == '/')
+ ast_copy_string(fn, configfile, fn_size);
+ else
+ snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, configfile);
+ } else if (file[0] == '/')
+ ast_copy_string(fn, file, fn_size);
+ else
+ snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, file);
+ lookup.fname = fn;
+ *fi = ao2_find(fileset, &lookup, OBJ_POINTER);
+ if (!(*fi)) {
+ /* set up a file scratch pad */
+ struct inclfile *fx = ao2_alloc(sizeof(struct inclfile), inclfile_destroy);
+ fx->fname = ast_strdup(fn);
+ fx->lineno = 1;
+ *fi = fx;
+ ao2_link(fileset, fx);
+ }
+}
+
+static int count_linefeeds(char *str)
+{
+ int count = 0;
+
+ while (*str) {
+ if (*str =='\n')
+ count++;
+ str++;
+ }
+ return count;
+}
+
+static int count_linefeeds_in_comments(struct ast_comment *x)
+{
+ int count = 0;
+
+ while (x) {
+ count += count_linefeeds(x->cmt);
+ x = x->next;
+ }
+ return count;
+}
+
+static void insert_leading_blank_lines(FILE *fp, struct inclfile *fi, struct ast_comment *precomments, int lineno)
+{
+ int precomment_lines = count_linefeeds_in_comments(precomments);
+ int i;
+
+ /* I don't have to worry about those ;! comments, they are
+ stored in the precomments, but not printed back out.
+ I did have to make sure that comments following
+ the ;! header comments were not also deleted in the process */
+ for (i=fi->lineno;i<lineno - precomment_lines; i++) {
+ fprintf(fp,"\n");
+ }
+ fi->lineno = lineno+1; /* Advance the file lineno */
+}
+
+int config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
+{
+ FILE *f;
+ char fn[256];
+ struct ast_variable *var;
+ struct ast_category *cat;
+ struct ast_comment *cmt;
+ struct ast_config_include *incl;
+ int blanklines = 0;
+ struct ao2_container *fileset = ao2_container_alloc(180000, hash_string, hashtab_compare_strings);
+ struct inclfile *fi = 0;
+
+ /* reset all the output flags, in case this isn't our first time saving this data */
+
+ for (incl=cfg->includes; incl; incl = incl->next)
+ incl->output = 0;
+
+ /* go thru all the inclusions and make sure all the files involved (configfile plus all its inclusions)
+ are all truncated to zero bytes and have that nice header*/
+
+ for (incl=cfg->includes; incl; incl = incl->next)
+ {
+ if (!incl->exec) { /* leave the execs alone -- we'll write out the #exec directives, but won't zero out the include files or exec files*/
+ FILE *f1;
+
+ set_fn(fn, sizeof(fn), incl->included_file, configfile, fileset, &fi); /* normally, fn is just set to incl->included_file, prepended with config dir if relative */
+ f1 = fopen(fn,"w");
+ if (f1) {
+ gen_header(f1, configfile, fn, generator);
+ fclose(f1); /* this should zero out the file */
+ } else {
+ ast_debug(1, "Unable to open for writing: %s\n", fn);
+ ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
+ }
+ ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
+ fi = 0;
+ }
+ }
+
+ set_fn(fn, sizeof(fn), 0, configfile, fileset, &fi); /* just set fn to absolute ver of configfile */
+#ifdef __CYGWIN__
+ if ((f = fopen(fn, "w+"))) {
+#else
+ if ((f = fopen(fn, "w"))) {
+#endif
+ ast_verb(2, "Saving '%s': ", fn);
+ gen_header(f, configfile, fn, generator);
+ cat = cfg->root;
+ fclose(f);
+ ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
+
+ /* from here out, we open each involved file and concat the stuff we need to add to the end and immediately close... */
+ /* since each var, cat, and associated comments can come from any file, we have to be
+ mobile, and open each file, print, and close it on an entry-by-entry basis */
+
+ while (cat) {
+ set_fn(fn, sizeof(fn), cat->file, configfile, fileset, &fi);
+ f = fopen(fn, "a");
+ if (!f)
+ {
+ ast_debug(1, "Unable to open for writing: %s\n", fn);
+ ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
+ ao2_ref(fileset, -1);
+ return -1;
+ }
+
+ /* dump any includes that happen before this category header */
+ for (incl=cfg->includes; incl; incl = incl->next) {
+ if (strcmp(incl->include_location_file, cat->file) == 0){
+ if (cat->lineno > incl->include_location_lineno && !incl->output) {
+ if (incl->exec)
+ fprintf(f,"#exec \"%s\"\n", incl->exec_file);
+ else
+ fprintf(f,"#include \"%s\"\n", incl->included_file);
+ incl->output = 1;
+ }
+ }
+ }
+
+ insert_leading_blank_lines(f, fi, cat->precomments, cat->lineno);
+ /* Dump section with any appropriate comment */
+ for (cmt = cat->precomments; cmt; cmt=cmt->next) {
+ char *cmtp = cmt->cmt;
+ while (*cmtp == ';' && *(cmtp+1) == '!') {
+ char *cmtp2 = strchr(cmtp+1, '\n');
+ if (cmtp2)
+ cmtp = cmtp2+1;
+ else cmtp = 0;
+ }
+ if (cmtp)
+ fprintf(f,"%s", cmtp);
+ }
+ if (!cat->precomments)
+ fprintf(f,"\n");
+ fprintf(f, "[%s]", cat->name);
+ if (cat->ignored)
+ fprintf(f, "(!)");
+ if (!AST_LIST_EMPTY(&cat->template_instances)) {
+ struct ast_category_template_instance *x;
+ fprintf(f, "(");
+ AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
+ fprintf(f,"%s",x->name);
+ if (x != AST_LIST_LAST(&cat->template_instances))
+ fprintf(f,",");
+ }
+ fprintf(f, ")");
+ }
+ for(cmt = cat->sameline; cmt; cmt=cmt->next)
+ {
+ fprintf(f,"%s", cmt->cmt);
+ }
+ if (!cat->sameline)
+ fprintf(f,"\n");
+ for (cmt = cat->trailing; cmt; cmt=cmt->next) {
+ if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
+ fprintf(f,"%s", cmt->cmt);
+ }
+ fclose(f);
+ ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
+ fi = 0;
+
+ var = cat->root;
+ while (var) {
+ struct ast_category_template_instance *x;
+ int found = 0;
+ AST_LIST_TRAVERSE(&cat->template_instances, x, next) {
+ struct ast_variable *v;
+ for (v = x->inst->root; v; v = v->next) {
+ if (!strcasecmp(var->name, v->name) && !strcmp(var->value, v->value)) {
+ found = 1;
+ break;
+ }
+ }
+ if (found)
+ break;
+ }
+ if (found) {
+ var = var->next;
+ continue;
+ }
+ set_fn(fn, sizeof(fn), var->file, configfile, fileset, &fi);
+ f = fopen(fn, "a");
+ if (!f)
+ {
+ ast_debug(1, "Unable to open for writing: %s\n", fn);
+ ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
+ ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
+ fi = 0;
+ ao2_ref(fileset, -1);
+ return -1;
+ }
+
+ /* dump any includes that happen before this category header */
+ for (incl=cfg->includes; incl; incl = incl->next) {
+ if (strcmp(incl->include_location_file, var->file) == 0){
+ if (var->lineno > incl->include_location_lineno && !incl->output) {
+ if (incl->exec)
+ fprintf(f,"#exec \"%s\"\n", incl->exec_file);
+ else
+ fprintf(f,"#include \"%s\"\n", incl->included_file);
+ incl->output = 1;
+ }
+ }
+ }
+
+ insert_leading_blank_lines(f, fi, var->precomments, var->lineno);
+ for (cmt = var->precomments; cmt; cmt=cmt->next) {
+ if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
+ fprintf(f,"%s", cmt->cmt);
+ }
+ if (var->sameline)
+ fprintf(f, "%s %s %s %s", var->name, (var->object ? "=>" : "="), var->value, var->sameline->cmt);
+ else
+ fprintf(f, "%s %s %s\n", var->name, (var->object ? "=>" : "="), var->value);
+ for (cmt = var->trailing; cmt; cmt=cmt->next) {
+ if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
+ fprintf(f,"%s", cmt->cmt);
+ }
+ if (var->blanklines) {
+ blanklines = var->blanklines;
+ while (blanklines--)
+ fprintf(f, "\n");
+ }
+
+ fclose(f);
+ ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
+ fi = 0;
+
+ var = var->next;
+ }
+ cat = cat->next;
+ }
+ if (!option_debug)
+ ast_verb(2, "Saved\n");
+ } else {
+ ast_debug(1, "Unable to open for writing: %s\n", fn);
+ ast_verb(2, "Unable to write (%s)", strerror(errno));
+ ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
+ ao2_ref(fileset, -1);
+ return -1;
+ }
+
+ /* Now, for files with trailing #include/#exec statements,
+ we have to make sure every entry is output */
+
+ for (incl=cfg->includes; incl; incl = incl->next) {
+ if (!incl->output) {
+ /* open the respective file */
+ set_fn(fn, sizeof(fn), incl->include_location_file, configfile, fileset, &fi);
+ f = fopen(fn, "a");
+ if (!f)
+ {
+ ast_debug(1, "Unable to open for writing: %s\n", fn);
+ ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
+ ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
+ fi = 0;
+ ao2_ref(fileset, -1);
+ return -1;
+ }
+
+ /* output the respective include */
+ if (incl->exec)
+ fprintf(f,"#exec \"%s\"\n", incl->exec_file);
+ else
+ fprintf(f,"#include \"%s\"\n", incl->included_file);
+ fclose(f);
+ incl->output = 1;
+ ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
+ fi = 0;
+ }
+ }
+ ao2_ref(fileset, -1); /* this should destroy the hash container */
+
+ return 0;
+}
+
+static void clear_config_maps(void)
+{
+ struct ast_config_map *map;
+
+ ast_mutex_lock(&config_lock);
+
+ while (config_maps) {
+ map = config_maps;
+ config_maps = config_maps->next;
+ ast_free(map);
+ }
+
+ ast_mutex_unlock(&config_lock);
+}
+
+static int append_mapping(const char *name, const char *driver, const char *database, const char *table)
+{
+ struct ast_config_map *map;
+ int length;
+
+ length = sizeof(*map);
+ length += strlen(name) + 1;
+ length += strlen(driver) + 1;
+ length += strlen(database) + 1;
+ if (table)
+ length += strlen(table) + 1;
+
+ if (!(map = ast_calloc(1, length)))
+ return -1;
+
+ map->name = map->stuff;
+ strcpy(map->name, name);
+ map->driver = map->name + strlen(map->name) + 1;
+ strcpy(map->driver, driver);
+ map->database = map->driver + strlen(map->driver) + 1;
+ strcpy(map->database, database);
+ if (table) {
+ map->table = map->database + strlen(map->database) + 1;
+ strcpy(map->table, table);
+ }
+ map->next = config_maps;
+
+ ast_verb(2, "Binding %s to %s/%s/%s\n", map->name, map->driver, map->database, map->table ? map->table : map->name);
+
+ config_maps = map;
+ return 0;
+}
+
+int read_config_maps(void)
+{
+ struct ast_config *config, *configtmp;
+ struct ast_variable *v;
+ char *driver, *table, *database, *stringp, *tmp;
+ struct ast_flags flags = { 0 };
+
+ clear_config_maps();
+
+ configtmp = ast_config_new();
+ configtmp->max_include_level = 1;
+ config = ast_config_internal_load(extconfig_conf, configtmp, flags, "");
+ if (!config) {
+ ast_config_destroy(configtmp);
+ return 0;
+ }
+
+ for (v = ast_variable_browse(config, "settings"); v; v = v->next) {
+ char buf[512];
+ ast_copy_string(buf, v->value, sizeof(buf));
+ stringp = buf;
+ driver = strsep(&stringp, ",");
+
+ if ((tmp = strchr(stringp, '\"')))
+ stringp = tmp;
+
+ /* check if the database text starts with a double quote */
+ if (*stringp == '"') {
+ stringp++;
+ database = strsep(&stringp, "\"");
+ strsep(&stringp, ",");
+ } else {
+ /* apparently this text has no quotes */
+ database = strsep(&stringp, ",");
+ }
+
+ table = strsep(&stringp, ",");
+
+ if (!strcmp(v->name, extconfig_conf)) {
+ ast_log(LOG_WARNING, "Cannot bind '%s'!\n", extconfig_conf);
+ continue;
+ }
+
+ if (!strcmp(v->name, "asterisk.conf")) {
+ ast_log(LOG_WARNING, "Cannot bind 'asterisk.conf'!\n");
+ continue;
+ }
+
+ if (!strcmp(v->name, "logger.conf")) {
+ ast_log(LOG_WARNING, "Cannot bind 'logger.conf'!\n");
+ continue;
+ }
+
+ if (!driver || !database)
+ continue;
+ if (!strcasecmp(v->name, "sipfriends")) {
+ ast_log(LOG_WARNING, "The 'sipfriends' table is obsolete, update your config to use sipusers and sippeers, though they can point to the same table.\n");
+ append_mapping("sipusers", driver, database, table ? table : "sipfriends");
+ append_mapping("sippeers", driver, database, table ? table : "sipfriends");
+ } else if (!strcasecmp(v->name, "iaxfriends")) {
+ ast_log(LOG_WARNING, "The 'iaxfriends' table is obsolete, update your config to use iaxusers and iaxpeers, though they can point to the same table.\n");
+ append_mapping("iaxusers", driver, database, table ? table : "iaxfriends");
+ append_mapping("iaxpeers", driver, database, table ? table : "iaxfriends");
+ } else
+ append_mapping(v->name, driver, database, table);
+ }
+
+ ast_config_destroy(config);
+ return 0;
+}
+
+int ast_config_engine_register(struct ast_config_engine *new)
+{
+ struct ast_config_engine *ptr;
+
+ ast_mutex_lock(&config_lock);
+
+ if (!config_engine_list) {
+ config_engine_list = new;
+ } else {
+ for (ptr = config_engine_list; ptr->next; ptr=ptr->next);
+ ptr->next = new;
+ }
+
+ ast_mutex_unlock(&config_lock);
+ ast_log(LOG_NOTICE,"Registered Config Engine %s\n", new->name);
+
+ return 1;
+}
+
+int ast_config_engine_deregister(struct ast_config_engine *del)
+{
+ struct ast_config_engine *ptr, *last=NULL;
+
+ ast_mutex_lock(&config_lock);
+
+ for (ptr = config_engine_list; ptr; ptr=ptr->next) {
+ if (ptr == del) {
+ if (last)
+ last->next = ptr->next;
+ else
+ config_engine_list = ptr->next;
+ break;
+ }
+ last = ptr;
+ }
+
+ ast_mutex_unlock(&config_lock);
+
+ return 0;
+}
+
+/*! \brief Find realtime engine for realtime family */
+static struct ast_config_engine *find_engine(const char *family, char *database, int dbsiz, char *table, int tabsiz)
+{
+ struct ast_config_engine *eng, *ret = NULL;
+ struct ast_config_map *map;
+
+ ast_mutex_lock(&config_lock);
+
+ for (map = config_maps; map; map = map->next) {
+ if (!strcasecmp(family, map->name)) {
+ if (database)
+ ast_copy_string(database, map->database, dbsiz);
+ if (table)
+ ast_copy_string(table, map->table ? map->table : family, tabsiz);
+ break;
+ }
+ }
+
+ /* Check if the required driver (engine) exist */
+ if (map) {
+ for (eng = config_engine_list; !ret && eng; eng = eng->next) {
+ if (!strcasecmp(eng->name, map->driver))
+ ret = eng;
+ }
+ }
+
+ ast_mutex_unlock(&config_lock);
+
+ /* if we found a mapping, but the engine is not available, then issue a warning */
+ if (map && !ret)
+ ast_log(LOG_WARNING, "Realtime mapping for '%s' found to engine '%s', but the engine is not available\n", map->name, map->driver);
+
+ return ret;
+}
+
+static struct ast_config_engine text_file_engine = {
+ .name = "text",
+ .load_func = config_text_file_load,
+};
+
+struct ast_config *ast_config_internal_load(const char *filename, struct ast_config *cfg, struct ast_flags flags, const char *suggested_include_file)
+{
+ char db[256];
+ char table[256];
+ struct ast_config_engine *loader = &text_file_engine;
+ struct ast_config *result;
+
+ if (cfg->include_level == cfg->max_include_level) {
+ ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", cfg->max_include_level);
+ return NULL;
+ }
+
+ cfg->include_level++;
+
+ if (strcmp(filename, extconfig_conf) && strcmp(filename, "asterisk.conf") && config_engine_list) {
+ struct ast_config_engine *eng;
+
+ eng = find_engine(filename, db, sizeof(db), table, sizeof(table));
+
+
+ if (eng && eng->load_func) {
+ loader = eng;
+ } else {
+ eng = find_engine("global", db, sizeof(db), table, sizeof(table));
+ if (eng && eng->load_func)
+ loader = eng;
+ }
+ }
+
+ result = loader->load_func(db, table, filename, cfg, flags, suggested_include_file);
+
+ if (result && result != CONFIG_STATUS_FILEUNCHANGED)
+ result->include_level--;
+ else
+ cfg->include_level--;
+
+ return result;
+}
+
+struct ast_config *ast_config_load(const char *filename, struct ast_flags flags)
+{
+ struct ast_config *cfg;
+ struct ast_config *result;
+
+ cfg = ast_config_new();
+ if (!cfg)
+ return NULL;
+
+ result = ast_config_internal_load(filename, cfg, flags, "");
+ if (!result || result == CONFIG_STATUS_FILEUNCHANGED)
+ ast_config_destroy(cfg);
+
+ return result;
+}
+
+static struct ast_variable *ast_load_realtime_helper(const char *family, va_list ap)
+{
+ struct ast_config_engine *eng;
+ char db[256]="";
+ char table[256]="";
+ struct ast_variable *res=NULL;
+
+ eng = find_engine(family, db, sizeof(db), table, sizeof(table));
+ if (eng && eng->realtime_func)
+ res = eng->realtime_func(db, table, ap);
+
+ return res;
+}
+
+struct ast_variable *ast_load_realtime_all(const char *family, ...)
+{
+ struct ast_variable *res;
+ va_list ap;
+
+ va_start(ap, family);
+ res = ast_load_realtime_helper(family, ap);
+ va_end(ap);
+
+ return res;
+}
+
+struct ast_variable *ast_load_realtime(const char *family, ...)
+{
+ struct ast_variable *res, *cur, *prev = NULL, *freeme = NULL;
+ va_list ap;
+
+ va_start(ap, family);
+ res = ast_load_realtime_helper(family, ap);
+ va_end(ap);
+
+ /* Eliminate blank entries */
+ for (cur = res; cur; cur = cur->next) {
+ if (freeme) {
+ ast_free(freeme);
+ freeme = NULL;
+ }
+
+ if (ast_strlen_zero(cur->value)) {
+ if (prev)
+ prev->next = cur->next;
+ else
+ res = cur->next;
+ freeme = cur;
+ } else {
+ prev = cur;
+ }
+ }
+ return res;
+}
+
+/*! \brief Check if realtime engine is configured for family */
+int ast_check_realtime(const char *family)
+{
+ struct ast_config_engine *eng;
+
+ eng = find_engine(family, NULL, 0, NULL, 0);
+ if (eng)
+ return 1;
+ return 0;
+}
+
+/*! \brief Check if there's any realtime engines loaded */
+int ast_realtime_enabled()
+{
+ return config_maps ? 1 : 0;
+}
+
+struct ast_config *ast_load_realtime_multientry(const char *family, ...)
+{
+ struct ast_config_engine *eng;
+ char db[256]="";
+ char table[256]="";
+ struct ast_config *res=NULL;
+ va_list ap;
+
+ va_start(ap, family);
+ eng = find_engine(family, db, sizeof(db), table, sizeof(table));
+ if (eng && eng->realtime_multi_func)
+ res = eng->realtime_multi_func(db, table, ap);
+ va_end(ap);
+
+ return res;
+}
+
+int ast_update_realtime(const char *family, const char *keyfield, const char *lookup, ...)
+{
+ struct ast_config_engine *eng;
+ int res = -1;
+ char db[256]="";
+ char table[256]="";
+ va_list ap;
+
+ va_start(ap, lookup);
+ eng = find_engine(family, db, sizeof(db), table, sizeof(table));
+ if (eng && eng->update_func)
+ res = eng->update_func(db, table, keyfield, lookup, ap);
+ va_end(ap);
+
+ return res;
+}
+
+int ast_store_realtime(const char *family, ...) {
+ struct ast_config_engine *eng;
+ int res = -1;
+ char db[256]="";
+ char table[256]="";
+ va_list ap;
+
+ va_start(ap, family);
+ eng = find_engine(family, db, sizeof(db), table, sizeof(table));
+ if (eng && eng->store_func)
+ res = eng->store_func(db, table, ap);
+ va_end(ap);
+
+ return res;
+}
+
+int ast_destroy_realtime(const char *family, const char *keyfield, const char *lookup, ...) {
+ struct ast_config_engine *eng;
+ int res = -1;
+ char db[256]="";
+ char table[256]="";
+ va_list ap;
+
+ va_start(ap, lookup);
+ eng = find_engine(family, db, sizeof(db), table, sizeof(table));
+ if (eng && eng->destroy_func)
+ res = eng->destroy_func(db, table, keyfield, lookup, ap);
+ va_end(ap);
+
+ return res;
+}
+
+/*! \brief Helper function to parse arguments
+ * See documentation in config.h
+ */
+int ast_parse_arg(const char *arg, enum ast_parse_flags flags,
+ void *p_result, ...)
+{
+ va_list ap;
+ int error = 0;
+
+ va_start(ap, p_result);
+ switch (flags & PARSE_TYPE) {
+ case PARSE_INT32:
+ {
+ int32_t *result = p_result;
+ int32_t x, def = result ? *result : 0,
+ high = (int32_t)0x7fffffff,
+ low = (int32_t)0x80000000;
+ /* optional argument: first default value, then range */
+ if (flags & PARSE_DEFAULT)
+ def = va_arg(ap, int32_t);
+ if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
+ /* range requested, update bounds */
+ low = va_arg(ap, int32_t);
+ high = va_arg(ap, int32_t);
+ }
+ x = strtol(arg, NULL, 0);
+ error = (x < low) || (x > high);
+ if (flags & PARSE_OUT_RANGE)
+ error = !error;
+ if (result)
+ *result = error ? def : x;
+ ast_debug(3,
+ "extract int from [%s] in [%d, %d] gives [%d](%d)\n",
+ arg, low, high,
+ result ? *result : x, error);
+ break;
+ }
+
+ case PARSE_UINT32:
+ {
+ uint32_t *result = p_result;
+ uint32_t x, def = result ? *result : 0,
+ low = 0, high = (uint32_t)~0;
+ /* optional argument: first default value, then range */
+ if (flags & PARSE_DEFAULT)
+ def = va_arg(ap, uint32_t);
+ if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
+ /* range requested, update bounds */
+ low = va_arg(ap, uint32_t);
+ high = va_arg(ap, uint32_t);
+ }
+ x = strtoul(arg, NULL, 0);
+ error = (x < low) || (x > high);
+ if (flags & PARSE_OUT_RANGE)
+ error = !error;
+ if (result)
+ *result = error ? def : x;
+ ast_debug(3,
+ "extract uint from [%s] in [%u, %u] gives [%u](%d)\n",
+ arg, low, high,
+ result ? *result : x, error);
+ break;
+ }
+
+ case PARSE_DOUBLE:
+ {
+ double *result = p_result;
+ double x, def = result ? *result : 0,
+ low = -HUGE_VAL, high = HUGE_VAL;
+
+ /* optional argument: first default value, then range */
+ if (flags & PARSE_DEFAULT)
+ def = va_arg(ap, double);
+ if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
+ /* range requested, update bounds */
+ low = va_arg(ap, double);
+ high = va_arg(ap, double);
+ }
+ x = strtod(arg, NULL);
+ error = (x < low) || (x > high);
+ if (flags & PARSE_OUT_RANGE)
+ error = !error;
+ if (result)
+ *result = error ? def : x;
+ ast_debug(3,
+ "extract double from [%s] in [%f, %f] gives [%f](%d)\n",
+ arg, low, high,
+ result ? *result : x, error);
+ break;
+ }
+ case PARSE_INADDR:
+ {
+ char *port, *buf;
+ struct sockaddr_in _sa_buf; /* buffer for the result */
+ struct sockaddr_in *sa = p_result ?
+ (struct sockaddr_in *)p_result : &_sa_buf;
+ /* default is either the supplied value or the result itself */
+ struct sockaddr_in *def = (flags & PARSE_DEFAULT) ?
+ va_arg(ap, struct sockaddr_in *) : sa;
+ struct hostent *hp;
+ struct ast_hostent ahp;
+
+ bzero(&_sa_buf, sizeof(_sa_buf)); /* clear buffer */
+ /* duplicate the string to strip away the :port */
+ port = ast_strdupa(arg);
+ buf = strsep(&port, ":");
+ sa->sin_family = AF_INET; /* assign family */
+ /*
+ * honor the ports flag setting, assign default value
+ * in case of errors or field unset.
+ */
+ flags &= PARSE_PORT_MASK; /* the only flags left to process */
+ if (port) {
+ if (flags == PARSE_PORT_FORBID) {
+ error = 1; /* port was forbidden */
+ sa->sin_port = def->sin_port;
+ } else if (flags == PARSE_PORT_IGNORE)
+ sa->sin_port = def->sin_port;
+ else /* accept or require */
+ sa->sin_port = htons(strtol(port, NULL, 0));
+ } else {
+ sa->sin_port = def->sin_port;
+ if (flags == PARSE_PORT_REQUIRE)
+ error = 1;
+ }
+ /* Now deal with host part, even if we have errors before. */
+ hp = ast_gethostbyname(buf, &ahp);
+ if (hp) /* resolved successfully */
+ memcpy(&sa->sin_addr, hp->h_addr, sizeof(sa->sin_addr));
+ else {
+ error = 1;
+ sa->sin_addr = def->sin_addr;
+ }
+ ast_debug(3,
+ "extract inaddr from [%s] gives [%s:%d](%d)\n",
+ arg, ast_inet_ntoa(sa->sin_addr),
+ ntohs(sa->sin_port), error);
+ break;
+ }
+ }
+ va_end(ap);
+ return error;
+}
+
+static char *handle_cli_core_show_config_mappings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_config_engine *eng;
+ struct ast_config_map *map;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show config mappings";
+ e->usage =
+ "Usage: core show config mappings\n"
+ " Shows the filenames to config engines.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ ast_mutex_lock(&config_lock);
+
+ if (!config_engine_list) {
+ ast_cli(a->fd, "No config mappings found.\n");
+ } else {
+ ast_cli(a->fd, "\n\n");
+ for (eng = config_engine_list; eng; eng = eng->next) {
+ ast_cli(a->fd, "\nConfig Engine: %s\n", eng->name);
+ for (map = config_maps; map; map = map->next) {
+ if (!strcasecmp(map->driver, eng->name)) {
+ ast_cli(a->fd, "===> %s (db=%s, table=%s)\n", map->name, map->database,
+ map->table ? map->table : map->name);
+ }
+ }
+ }
+ ast_cli(a->fd,"\n\n");
+ }
+
+ ast_mutex_unlock(&config_lock);
+
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli_config[] = {
+ AST_CLI_DEFINE(handle_cli_core_show_config_mappings, "Display config mappings (file names to config engines)"),
+};
+
+int register_config_cli()
+{
+ ast_cli_register_multiple(cli_config, sizeof(cli_config) / sizeof(struct ast_cli_entry));
+ return 0;
+}
diff --git a/trunk/main/cryptostub.c b/trunk/main/cryptostub.c
new file mode 100644
index 000000000..c3b59c7b3
--- /dev/null
+++ b/trunk/main/cryptostub.c
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+/*! \file
+ *
+ * \brief Stubs for res_crypto routines
+ *
+ * \author Mark Spencer <markster@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/crypto.h"
+
+static struct ast_key *stub_ast_key_get(const char *kname, int ktype)
+{
+ ast_log(LOG_NOTICE, "Crypto support not loaded!\n");
+ return NULL;
+}
+
+#ifdef SKREP
+#define build_stub(func_name,...) \
+static int stub_ ## func_name(__VA_ARGS__) \
+{ \
+ ast_log(LOG_NOTICE, "Crypto support not loaded!\n"); \
+ return -1; \
+} \
+\
+int (*func_name)(__VA_ARGS__) = \
+ stub_ ## func_name;
+#endif
+#define build_stub(func_name,...) \
+static int stub_##func_name(__VA_ARGS__) \
+{ \
+ ast_log(LOG_NOTICE, "Crypto support not loaded!\n"); \
+ return -1; \
+} \
+\
+int (*func_name)(__VA_ARGS__) = \
+ stub_##func_name;
+
+struct ast_key *(*ast_key_get)(const char *key, int type) =
+stub_ast_key_get;
+
+build_stub(ast_check_signature, struct ast_key *key, const char *msg, const char *sig);
+build_stub(ast_check_signature_bin, struct ast_key *key, const char *msg, int msglen, const unsigned char *sig);
+build_stub(ast_sign, struct ast_key *key, char *msg, char *sig);
+build_stub(ast_sign_bin, struct ast_key *key, const char *msg, int msglen, unsigned char *sig);
+build_stub(ast_encrypt_bin, unsigned char *dst, const unsigned char *src, int srclen, struct ast_key *key);
+build_stub(ast_decrypt_bin, unsigned char *dst, const unsigned char *src, int srclen, struct ast_key *key);
diff --git a/trunk/main/cygload.c b/trunk/main/cygload.c
new file mode 100644
index 000000000..fd8f3d5b6
--- /dev/null
+++ b/trunk/main/cygload.c
@@ -0,0 +1,39 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * 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
+ * Loader for Asterisk under Cygwin/windows.
+ * Open the dll, locate main, run.
+ */
+
+#include <unistd.h>
+#include <dlfcn.h>
+#include <stdio.h>
+
+typedef int (*main_f)(int argc, char *argv[]);
+
+int main(int argc, char *argv[])
+{
+ main_f ast_main = NULL;
+ void *handle = dlopen("asterisk.dll", 0);
+ if (handle)
+ ast_main = (main_f)dlsym(handle, "main");
+ if (ast_main)
+ return ast_main(argc, argv);
+ fprintf(stderr, "could not load Asterisk, %s\n", dlerror());
+ return 1; /* there was an error */
+}
diff --git a/trunk/main/db.c b/trunk/main/db.c
new file mode 100644
index 000000000..94bf4f001
--- /dev/null
+++ b/trunk/main/db.c
@@ -0,0 +1,671 @@
+/*
+ * 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.
+ */
+
+/*! \file
+ *
+ * \brief ASTdb Management
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \note DB3 is licensed under Sleepycat Public License and is thus incompatible
+ * with GPL. To avoid having to make another exception (and complicate
+ * licensing even further) we elect to use DB1 which is BSD licensed
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/_private.h"
+#include "asterisk/paths.h" /* use ast_config_AST_DB */
+#include <sys/time.h>
+#include <signal.h>
+#include <dirent.h>
+
+#include "asterisk/channel.h"
+#include "asterisk/file.h"
+#include "asterisk/app.h"
+#include "asterisk/dsp.h"
+#include "asterisk/astdb.h"
+#include "asterisk/cli.h"
+#include "asterisk/utils.h"
+#include "asterisk/lock.h"
+#include "asterisk/manager.h"
+#include "db1-ast/include/db.h"
+
+static DB *astdb;
+AST_MUTEX_DEFINE_STATIC(dblock);
+
+static int dbinit(void)
+{
+ if (!astdb && !(astdb = dbopen(ast_config_AST_DB, O_CREAT | O_RDWR, AST_FILE_MODE, DB_BTREE, NULL))) {
+ ast_log(LOG_WARNING, "Unable to open Asterisk database '%s': %s\n", ast_config_AST_DB, strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+
+static inline int keymatch(const char *key, const char *prefix)
+{
+ int preflen = strlen(prefix);
+ if (!preflen)
+ return 1;
+ if (!strcasecmp(key, prefix))
+ return 1;
+ if ((strlen(key) > preflen) && !strncasecmp(key, prefix, preflen)) {
+ if (key[preflen] == '/')
+ return 1;
+ }
+ return 0;
+}
+
+static inline int subkeymatch(const char *key, const char *suffix)
+{
+ int suffixlen = strlen(suffix);
+ if (suffixlen) {
+ const char *subkey = key + strlen(key) - suffixlen;
+ if (subkey < key)
+ return 0;
+ if (!strcasecmp(subkey, suffix))
+ return 1;
+ }
+ return 0;
+}
+
+int ast_db_deltree(const char *family, const char *keytree)
+{
+ char prefix[256];
+ DBT key, data;
+ char *keys;
+ int res;
+ int pass;
+ int counter = 0;
+
+ if (family) {
+ if (keytree) {
+ snprintf(prefix, sizeof(prefix), "/%s/%s", family, keytree);
+ } else {
+ snprintf(prefix, sizeof(prefix), "/%s", family);
+ }
+ } else if (keytree) {
+ return -1;
+ } else {
+ prefix[0] = '\0';
+ }
+
+ ast_mutex_lock(&dblock);
+ if (dbinit()) {
+ ast_mutex_unlock(&dblock);
+ return -1;
+ }
+
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ pass = 0;
+ while (!(res = astdb->seq(astdb, &key, &data, pass++ ? R_NEXT : R_FIRST))) {
+ if (key.size) {
+ keys = key.data;
+ keys[key.size - 1] = '\0';
+ } else {
+ keys = "<bad key>";
+ }
+ if (keymatch(keys, prefix)) {
+ astdb->del(astdb, &key, 0);
+ counter++;
+ }
+ }
+ astdb->sync(astdb, 0);
+ ast_mutex_unlock(&dblock);
+ return counter;
+}
+
+int ast_db_put(const char *family, const char *keys, const char *value)
+{
+ char fullkey[256];
+ DBT key, data;
+ int res, fullkeylen;
+
+ ast_mutex_lock(&dblock);
+ if (dbinit()) {
+ ast_mutex_unlock(&dblock);
+ return -1;
+ }
+
+ fullkeylen = snprintf(fullkey, sizeof(fullkey), "/%s/%s", family, keys);
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ key.data = fullkey;
+ key.size = fullkeylen + 1;
+ data.data = (char *) value;
+ data.size = strlen(value) + 1;
+ res = astdb->put(astdb, &key, &data, 0);
+ astdb->sync(astdb, 0);
+ ast_mutex_unlock(&dblock);
+ if (res)
+ ast_log(LOG_WARNING, "Unable to put value '%s' for key '%s' in family '%s'\n", value, keys, family);
+ return res;
+}
+
+int ast_db_get(const char *family, const char *keys, char *value, int valuelen)
+{
+ char fullkey[256] = "";
+ DBT key, data;
+ int res, fullkeylen;
+
+ ast_mutex_lock(&dblock);
+ if (dbinit()) {
+ ast_mutex_unlock(&dblock);
+ return -1;
+ }
+
+ fullkeylen = snprintf(fullkey, sizeof(fullkey), "/%s/%s", family, keys);
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ memset(value, 0, valuelen);
+ key.data = fullkey;
+ key.size = fullkeylen + 1;
+
+ res = astdb->get(astdb, &key, &data, 0);
+
+ ast_mutex_unlock(&dblock);
+
+ /* Be sure to NULL terminate our data either way */
+ if (res) {
+ ast_debug(1, "Unable to find key '%s' in family '%s'\n", keys, family);
+ } else {
+#if 0
+ printf("Got value of size %d\n", data.size);
+#endif
+ if (data.size) {
+ ((char *)data.data)[data.size - 1] = '\0';
+ /* Make sure that we don't write too much to the dst pointer or we don't read too much from the source pointer */
+ ast_copy_string(value, data.data, (valuelen > data.size) ? data.size : valuelen);
+ } else {
+ ast_log(LOG_NOTICE, "Strange, empty value for /%s/%s\n", family, keys);
+ }
+ }
+ return res;
+}
+
+int ast_db_del(const char *family, const char *keys)
+{
+ char fullkey[256];
+ DBT key;
+ int res, fullkeylen;
+
+ ast_mutex_lock(&dblock);
+ if (dbinit()) {
+ ast_mutex_unlock(&dblock);
+ return -1;
+ }
+
+ fullkeylen = snprintf(fullkey, sizeof(fullkey), "/%s/%s", family, keys);
+ memset(&key, 0, sizeof(key));
+ key.data = fullkey;
+ key.size = fullkeylen + 1;
+
+ res = astdb->del(astdb, &key, 0);
+ astdb->sync(astdb, 0);
+
+ ast_mutex_unlock(&dblock);
+
+ if (res) {
+ ast_debug(1, "Unable to find key '%s' in family '%s'\n", keys, family);
+ }
+ return res;
+}
+
+static char *handle_cli_database_put(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int res;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "database put";
+ e->usage =
+ "Usage: database put <family> <key> <value>\n"
+ " Adds or updates an entry in the Asterisk database for\n"
+ " a given family, key, and value.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 5)
+ return CLI_SHOWUSAGE;
+ res = ast_db_put(a->argv[2], a->argv[3], a->argv[4]);
+ if (res) {
+ ast_cli(a->fd, "Failed to update entry\n");
+ } else {
+ ast_cli(a->fd, "Updated database successfully\n");
+ }
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_database_get(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int res;
+ char tmp[256];
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "database get";
+ e->usage =
+ "Usage: database get <family> <key>\n"
+ " Retrieves an entry in the Asterisk database for a given\n"
+ " family and key.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+ res = ast_db_get(a->argv[2], a->argv[3], tmp, sizeof(tmp));
+ if (res) {
+ ast_cli(a->fd, "Database entry not found.\n");
+ } else {
+ ast_cli(a->fd, "Value: %s\n", tmp);
+ }
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_database_del(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int res;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "database del";
+ e->usage =
+ "Usage: database del <family> <key>\n"
+ " Deletes an entry in the Asterisk database for a given\n"
+ " family and key.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+ res = ast_db_del(a->argv[2], a->argv[3]);
+ if (res) {
+ ast_cli(a->fd, "Database entry does not exist.\n");
+ } else {
+ ast_cli(a->fd, "Database entry removed.\n");
+ }
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_database_deltree(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int res;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "database deltree";
+ e->usage =
+ "Usage: database deltree <family> [keytree]\n"
+ " Deletes a family or specific keytree within a family\n"
+ " in the Asterisk database.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if ((a->argc < 3) || (a->argc > 4))
+ return CLI_SHOWUSAGE;
+ if (a->argc == 4) {
+ res = ast_db_deltree(a->argv[2], a->argv[3]);
+ } else {
+ res = ast_db_deltree(a->argv[2], NULL);
+ }
+ if (res < 0) {
+ ast_cli(a->fd, "Database entries do not exist.\n");
+ } else {
+ ast_cli(a->fd, "%d database entries removed.\n",res);
+ }
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_database_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char prefix[256];
+ DBT key, data;
+ char *keys, *values;
+ int res;
+ int pass;
+ int counter = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "database show";
+ e->usage =
+ "Usage: database show [family [keytree]]\n"
+ " Shows Asterisk database contents, optionally restricted\n"
+ " to a given family, or family and keytree.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc == 4) {
+ /* Family and key tree */
+ snprintf(prefix, sizeof(prefix), "/%s/%s", a->argv[2], a->argv[3]);
+ } else if (a->argc == 3) {
+ /* Family only */
+ snprintf(prefix, sizeof(prefix), "/%s", a->argv[2]);
+ } else if (a->argc == 2) {
+ /* Neither */
+ prefix[0] = '\0';
+ } else {
+ return CLI_SHOWUSAGE;
+ }
+ ast_mutex_lock(&dblock);
+ if (dbinit()) {
+ ast_mutex_unlock(&dblock);
+ ast_cli(a->fd, "Database unavailable\n");
+ return CLI_SUCCESS;
+ }
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ pass = 0;
+ while (!(res = astdb->seq(astdb, &key, &data, pass++ ? R_NEXT : R_FIRST))) {
+ if (key.size) {
+ keys = key.data;
+ keys[key.size - 1] = '\0';
+ } else {
+ keys = "<bad key>";
+ }
+ if (data.size) {
+ values = data.data;
+ values[data.size - 1]='\0';
+ } else {
+ values = "<bad value>";
+ }
+ if (keymatch(keys, prefix)) {
+ ast_cli(a->fd, "%-50s: %-25s\n", keys, values);
+ counter++;
+ }
+ }
+ ast_mutex_unlock(&dblock);
+ ast_cli(a->fd, "%d results found.\n", counter);
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_database_showkey(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char suffix[256];
+ DBT key, data;
+ char *keys, *values;
+ int res;
+ int pass;
+ int counter = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "database showkey";
+ e->usage =
+ "Usage: database showkey <keytree>\n"
+ " Shows Asterisk database contents, restricted to a given key.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc == 3) {
+ /* Key only */
+ snprintf(suffix, sizeof(suffix), "/%s", a->argv[2]);
+ } else {
+ return CLI_SHOWUSAGE;
+ }
+ ast_mutex_lock(&dblock);
+ if (dbinit()) {
+ ast_mutex_unlock(&dblock);
+ ast_cli(a->fd, "Database unavailable\n");
+ return CLI_SUCCESS;
+ }
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ pass = 0;
+ while (!(res = astdb->seq(astdb, &key, &data, pass++ ? R_NEXT : R_FIRST))) {
+ if (key.size) {
+ keys = key.data;
+ keys[key.size - 1] = '\0';
+ } else {
+ keys = "<bad key>";
+ }
+ if (data.size) {
+ values = data.data;
+ values[data.size - 1]='\0';
+ } else {
+ values = "<bad value>";
+ }
+ if (subkeymatch(keys, suffix)) {
+ ast_cli(a->fd, "%-50s: %-25s\n", keys, values);
+ counter++;
+ }
+ }
+ ast_mutex_unlock(&dblock);
+ ast_cli(a->fd, "%d results found.\n", counter);
+ return CLI_SUCCESS;
+}
+
+struct ast_db_entry *ast_db_gettree(const char *family, const char *keytree)
+{
+ char prefix[256];
+ DBT key, data;
+ char *keys, *values;
+ int values_len;
+ int res;
+ int pass;
+ struct ast_db_entry *last = NULL;
+ struct ast_db_entry *cur, *ret=NULL;
+
+ if (!ast_strlen_zero(family)) {
+ if (!ast_strlen_zero(keytree)) {
+ /* Family and key tree */
+ snprintf(prefix, sizeof(prefix), "/%s/%s", family, prefix);
+ } else {
+ /* Family only */
+ snprintf(prefix, sizeof(prefix), "/%s", family);
+ }
+ } else {
+ prefix[0] = '\0';
+ }
+ ast_mutex_lock(&dblock);
+ if (dbinit()) {
+ ast_mutex_unlock(&dblock);
+ ast_log(LOG_WARNING, "Database unavailable\n");
+ return NULL;
+ }
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ pass = 0;
+ while (!(res = astdb->seq(astdb, &key, &data, pass++ ? R_NEXT : R_FIRST))) {
+ if (key.size) {
+ keys = key.data;
+ keys[key.size - 1] = '\0';
+ } else {
+ keys = "<bad key>";
+ }
+ if (data.size) {
+ values = data.data;
+ values[data.size - 1] = '\0';
+ } else {
+ values = "<bad value>";
+ }
+ values_len = strlen(values) + 1;
+ if (keymatch(keys, prefix) && (cur = ast_malloc(sizeof(*cur) + strlen(keys) + 1 + values_len))) {
+ cur->next = NULL;
+ cur->key = cur->data + values_len;
+ strcpy(cur->data, values);
+ strcpy(cur->key, keys);
+ if (last) {
+ last->next = cur;
+ } else {
+ ret = cur;
+ }
+ last = cur;
+ }
+ }
+ ast_mutex_unlock(&dblock);
+ return ret;
+}
+
+void ast_db_freetree(struct ast_db_entry *dbe)
+{
+ struct ast_db_entry *last;
+ while (dbe) {
+ last = dbe;
+ dbe = dbe->next;
+ ast_free(last);
+ }
+}
+
+struct ast_cli_entry cli_database[] = {
+ AST_CLI_DEFINE(handle_cli_database_show, "Shows database contents"),
+ AST_CLI_DEFINE(handle_cli_database_showkey, "Shows database contents"),
+ AST_CLI_DEFINE(handle_cli_database_get, "Gets database value"),
+ AST_CLI_DEFINE(handle_cli_database_put, "Adds/updates database value"),
+ AST_CLI_DEFINE(handle_cli_database_del, "Removes database key/value"),
+ AST_CLI_DEFINE(handle_cli_database_deltree, "Removes database keytree/values")
+};
+
+static int manager_dbput(struct mansession *s, const struct message *m)
+{
+ const char *family = astman_get_header(m, "Family");
+ const char *key = astman_get_header(m, "Key");
+ const char *val = astman_get_header(m, "Val");
+ int res;
+
+ if (ast_strlen_zero(family)) {
+ astman_send_error(s, m, "No family specified");
+ return 0;
+ }
+ if (ast_strlen_zero(key)) {
+ astman_send_error(s, m, "No key specified");
+ return 0;
+ }
+
+ res = ast_db_put(family, key, S_OR(val, ""));
+ if (res) {
+ astman_send_error(s, m, "Failed to update entry");
+ } else {
+ astman_send_ack(s, m, "Updated database successfully");
+ }
+ return 0;
+}
+
+static int manager_dbget(struct mansession *s, const struct message *m)
+{
+ const char *id = astman_get_header(m,"ActionID");
+ char idText[256] = "";
+ const char *family = astman_get_header(m, "Family");
+ const char *key = astman_get_header(m, "Key");
+ char tmp[256];
+ int res;
+
+ if (ast_strlen_zero(family)) {
+ astman_send_error(s, m, "No family specified.");
+ return 0;
+ }
+ if (ast_strlen_zero(key)) {
+ astman_send_error(s, m, "No key specified.");
+ return 0;
+ }
+
+ if (!ast_strlen_zero(id))
+ snprintf(idText, sizeof(idText) ,"ActionID: %s\r\n", id);
+
+ res = ast_db_get(family, key, tmp, sizeof(tmp));
+ if (res) {
+ astman_send_error(s, m, "Database entry not found");
+ } else {
+ astman_send_ack(s, m, "Result will follow");
+ astman_append(s, "Event: DBGetResponse\r\n"
+ "Family: %s\r\n"
+ "Key: %s\r\n"
+ "Val: %s\r\n"
+ "%s"
+ "\r\n",
+ family, key, tmp, idText);
+ }
+ return 0;
+}
+
+static int manager_dbdel(struct mansession *s, const struct message *m)
+{
+ const char *family = astman_get_header(m, "Family");
+ const char *key = astman_get_header(m, "Key");
+ int res;
+
+ if (ast_strlen_zero(family)) {
+ astman_send_error(s, m, "No family specified.");
+ return 0;
+ }
+
+ if (ast_strlen_zero(key)) {
+ astman_send_error(s, m, "No key specified.");
+ return 0;
+ }
+
+ res = ast_db_del(family, key);
+ if (res)
+ astman_send_error(s, m, "Database entry not found");
+ else
+ astman_send_ack(s, m, "Key deleted successfully");
+
+ return 0;
+}
+
+static int manager_dbdeltree(struct mansession *s, const struct message *m)
+{
+ const char *family = astman_get_header(m, "Family");
+ const char *key = astman_get_header(m, "Key");
+ int res;
+
+ if (ast_strlen_zero(family)) {
+ astman_send_error(s, m, "No family specified.");
+ return 0;
+ }
+
+ if (!ast_strlen_zero(key))
+ res = ast_db_deltree(family, key);
+ else
+ res = ast_db_deltree(family, NULL);
+
+ if (res < 0)
+ astman_send_error(s, m, "Database entry not found");
+ else
+ astman_send_ack(s, m, "Key tree deleted successfully");
+
+ return 0;
+}
+
+int astdb_init(void)
+{
+ dbinit();
+ ast_cli_register_multiple(cli_database, sizeof(cli_database) / sizeof(struct ast_cli_entry));
+ ast_manager_register("DBGet", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_dbget, "Get DB Entry");
+ ast_manager_register("DBPut", EVENT_FLAG_SYSTEM, manager_dbput, "Put DB Entry");
+ ast_manager_register("DBDel", EVENT_FLAG_SYSTEM, manager_dbdel, "Delete DB Entry");
+ ast_manager_register("DBDelTree", EVENT_FLAG_SYSTEM, manager_dbdeltree, "Delete DB Tree");
+ return 0;
+}
diff --git a/trunk/main/db1-ast/Makefile b/trunk/main/db1-ast/Makefile
new file mode 100644
index 000000000..56657f88f
--- /dev/null
+++ b/trunk/main/db1-ast/Makefile
@@ -0,0 +1,72 @@
+# @(#)Makefile 8.9 (Berkeley) 7/14/94
+
+LIBDB= libdb1.a
+ARCH=$(shell uname -m)
+ifeq ($(ARCH),alpha)
+SOVER=2.1
+else
+SOVER=2
+endif
+
+ifeq ($(OSARCH),Darwin)
+ OSARCH_DEFINE+=-D__Darwin__
+endif
+
+LIBDBSO=libdb.so.$(SOVER)
+PROG= db_dump185
+OBJ1= hash/hash.o hash/hash_bigkey.o hash/hash_buf.o hash/hash_func.o hash/hash_log2.o hash/hash_page.o \
+ hash/ndbm.o
+OBJ2= btree/bt_close.o btree/bt_conv.o btree/bt_debug.o btree/bt_delete.o btree/bt_get.o btree/bt_open.o \
+ btree/bt_overflow.o btree/bt_page.o btree/bt_put.o btree/bt_search.o btree/bt_seq.o btree/bt_split.o \
+ btree/bt_utils.o
+OBJ3= db/db.o
+OBJ4= mpool/mpool.o
+OBJ5= recno/rec_close.o recno/rec_delete.o recno/rec_get.o recno/rec_open.o recno/rec_put.o recno/rec_search.o \
+ recno/rec_seq.o recno/rec_utils.o
+MISC=
+OBJS= $(OBJ1) $(OBJ2) $(OBJ3) $(OBJ4) $(OBJ5) $(MISC)
+SHOBJS= $(patsubst %.o,%.os,$(OBJS))
+
+include $(ASTTOPDIR)/Makefile.rules
+
+all: $(LIBDB) #$(LIBDBSO) $(PROG)
+
+$(LIBDB): $(OBJS)
+ $(ECHO_PREFIX) echo " [AR] $^ -> $@"
+ $(CMD_PREFIX) $(AR) cr $@ $^
+ $(CMD_PREFIX) $(RANLIB) $@
+
+$(LIBDBSO): $(SHOBJS)
+ $(CC) -Wl,-O1 -Wl,--version-script=libdb.map -Wl,-soname=$(LIBDBSO) -shared -o $@ $^
+ ln -sf $@ libdb.so
+
+$(PROG): db_dump185.o $(LIBDBSO)
+ $(CC) -o $@ db_dump185.o -L. -ldb
+
+clean-depend:
+
+clean:
+ rm -f $(LIBDB) $(LIBDBSO) $(OBJS) $(SHOBJS)
+ rm -f *.s *.i
+
+ASTCFLAGS:=-Wall -D__DBINTERFACE_PRIVATE -I. -I.. -Iinclude -Ihash -Ibtree -Irecno $(ASTCFLAGS)
+
+OSTYPE=$(shell uname -s)
+ifeq ($(OSTYPE),SunOS)
+ASTCFLAGS+=-I../../include -I../../include/solaris-compat -DSOLARIS
+endif
+
+db_dump185.o: db_dump185.c
+ $(CL) -o $@ $<
+x%.o: hash/%.c
+ $(CL) -Ihash $(OSARCH_DEFINE) -o $@ $<
+%.os: hash/%.c
+ $(CL) -Ihash -fPIC -o $@ $<
+x%.o: btree/%.c
+ $(CL) -Ibtree -o $@ $<
+%.os: btree/%.c
+ $(CL) -Ibtree -fPIC -o $@ $<
+x%.o: recno/%.c
+ $(CL) -Irecno -o $@ $<
+%.os: recno/%.c
+ $(CL) -Irecno -fPIC -o $@ $<
diff --git a/trunk/main/db1-ast/btree/bt_close.c b/trunk/main/db1-ast/btree/bt_close.c
new file mode 100644
index 000000000..67a6e5340
--- /dev/null
+++ b/trunk/main/db1-ast/btree/bt_close.c
@@ -0,0 +1,182 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Olson.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)bt_close.c 8.7 (Berkeley) 8/17/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/param.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "../include/db.h"
+#include "btree.h"
+
+static int bt_meta __P((BTREE *));
+
+/*
+ * BT_CLOSE -- Close a btree.
+ *
+ * Parameters:
+ * dbp: pointer to access method
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS
+ */
+int
+__bt_close(dbp)
+ DB *dbp;
+{
+ BTREE *t;
+ int fd;
+
+ t = dbp->internal;
+
+ /* Toss any page pinned across calls. */
+ if (t->bt_pinned != NULL) {
+ mpool_put(t->bt_mp, t->bt_pinned, 0);
+ t->bt_pinned = NULL;
+ }
+
+ /* Sync the tree. */
+ if (__bt_sync(dbp, 0) == RET_ERROR)
+ return (RET_ERROR);
+
+ /* Close the memory pool. */
+ if (mpool_close(t->bt_mp) == RET_ERROR)
+ return (RET_ERROR);
+
+ /* Free random memory. */
+ if (t->bt_cursor.key.data != NULL) {
+ free(t->bt_cursor.key.data);
+ t->bt_cursor.key.size = 0;
+ t->bt_cursor.key.data = NULL;
+ }
+ if (t->bt_rkey.data) {
+ free(t->bt_rkey.data);
+ t->bt_rkey.size = 0;
+ t->bt_rkey.data = NULL;
+ }
+ if (t->bt_rdata.data) {
+ free(t->bt_rdata.data);
+ t->bt_rdata.size = 0;
+ t->bt_rdata.data = NULL;
+ }
+
+ fd = t->bt_fd;
+ free(t);
+ free(dbp);
+ return (close(fd) ? RET_ERROR : RET_SUCCESS);
+}
+
+/*
+ * BT_SYNC -- sync the btree to disk.
+ *
+ * Parameters:
+ * dbp: pointer to access method
+ *
+ * Returns:
+ * RET_SUCCESS, RET_ERROR.
+ */
+int
+__bt_sync(dbp, flags)
+ const DB *dbp;
+ u_int flags;
+{
+ BTREE *t;
+ int status;
+
+ t = dbp->internal;
+
+ /* Toss any page pinned across calls. */
+ if (t->bt_pinned != NULL) {
+ mpool_put(t->bt_mp, t->bt_pinned, 0);
+ t->bt_pinned = NULL;
+ }
+
+ /* Sync doesn't currently take any flags. */
+ if (flags != 0) {
+ errno = EINVAL;
+ return (RET_ERROR);
+ }
+
+ if (F_ISSET(t, B_INMEM | B_RDONLY) || !F_ISSET(t, B_MODIFIED))
+ return (RET_SUCCESS);
+
+ if (F_ISSET(t, B_METADIRTY) && bt_meta(t) == RET_ERROR)
+ return (RET_ERROR);
+
+ if ((status = mpool_sync(t->bt_mp)) == RET_SUCCESS)
+ F_CLR(t, B_MODIFIED);
+
+ return (status);
+}
+
+/*
+ * BT_META -- write the tree meta data to disk.
+ *
+ * Parameters:
+ * t: tree
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS
+ */
+static int
+bt_meta(t)
+ BTREE *t;
+{
+ BTMETA m;
+ void *p;
+
+ if ((p = mpool_get(t->bt_mp, P_META, 0)) == NULL)
+ return (RET_ERROR);
+
+ /* Fill in metadata. */
+ m.magic = BTREEMAGIC;
+ m.version = BTREEVERSION;
+ m.psize = t->bt_psize;
+ m.free = t->bt_free;
+ m.nrecs = t->bt_nrecs;
+ m.flags = F_ISSET(t, SAVEMETA);
+
+ memmove(p, &m, sizeof(BTMETA));
+ mpool_put(t->bt_mp, p, MPOOL_DIRTY);
+ return (RET_SUCCESS);
+}
diff --git a/trunk/main/db1-ast/btree/bt_conv.c b/trunk/main/db1-ast/btree/bt_conv.c
new file mode 100644
index 000000000..d2ebdc57b
--- /dev/null
+++ b/trunk/main/db1-ast/btree/bt_conv.c
@@ -0,0 +1,221 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Olson.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)bt_conv.c 8.5 (Berkeley) 8/17/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/param.h>
+
+#include <stdio.h>
+
+#include "../include/db.h"
+#include "btree.h"
+
+static void mswap __P((PAGE *));
+
+/*
+ * __BT_BPGIN, __BT_BPGOUT --
+ * Convert host-specific number layout to/from the host-independent
+ * format stored on disk.
+ *
+ * Parameters:
+ * t: tree
+ * pg: page number
+ * h: page to convert
+ */
+void
+__bt_pgin(t, pg, pp)
+ void *t;
+ pgno_t pg;
+ void *pp;
+{
+ PAGE *h;
+ indx_t i, top;
+ u_char flags;
+ char *p;
+
+ if (!F_ISSET(((BTREE *)t), B_NEEDSWAP))
+ return;
+ if (pg == P_META) {
+ mswap(pp);
+ return;
+ }
+
+ h = pp;
+ M_32_SWAP(h->pgno);
+ M_32_SWAP(h->prevpg);
+ M_32_SWAP(h->nextpg);
+ M_32_SWAP(h->flags);
+ M_16_SWAP(h->lower);
+ M_16_SWAP(h->upper);
+
+ top = NEXTINDEX(h);
+ if ((h->flags & P_TYPE) == P_BINTERNAL)
+ for (i = 0; i < top; i++) {
+ M_16_SWAP(h->linp[i]);
+ p = (char *)GETBINTERNAL(h, i);
+ P_32_SWAP(p);
+ p += sizeof(u_int32_t);
+ P_32_SWAP(p);
+ p += sizeof(pgno_t);
+ if (*(u_char *)p & P_BIGKEY) {
+ p += sizeof(u_char);
+ P_32_SWAP(p);
+ p += sizeof(pgno_t);
+ P_32_SWAP(p);
+ }
+ }
+ else if ((h->flags & P_TYPE) == P_BLEAF)
+ for (i = 0; i < top; i++) {
+ M_16_SWAP(h->linp[i]);
+ p = (char *)GETBLEAF(h, i);
+ P_32_SWAP(p);
+ p += sizeof(u_int32_t);
+ P_32_SWAP(p);
+ p += sizeof(u_int32_t);
+ flags = *(u_char *)p;
+ if (flags & (P_BIGKEY | P_BIGDATA)) {
+ p += sizeof(u_char);
+ if (flags & P_BIGKEY) {
+ P_32_SWAP(p);
+ p += sizeof(pgno_t);
+ P_32_SWAP(p);
+ }
+ if (flags & P_BIGDATA) {
+ p += sizeof(u_int32_t);
+ P_32_SWAP(p);
+ p += sizeof(pgno_t);
+ P_32_SWAP(p);
+ }
+ }
+ }
+}
+
+void
+__bt_pgout(t, pg, pp)
+ void *t;
+ pgno_t pg;
+ void *pp;
+{
+ PAGE *h;
+ indx_t i, top;
+ u_char flags;
+ char *p;
+
+ if (!F_ISSET(((BTREE *)t), B_NEEDSWAP))
+ return;
+ if (pg == P_META) {
+ mswap(pp);
+ return;
+ }
+
+ h = pp;
+ top = NEXTINDEX(h);
+ if ((h->flags & P_TYPE) == P_BINTERNAL)
+ for (i = 0; i < top; i++) {
+ p = (char *)GETBINTERNAL(h, i);
+ P_32_SWAP(p);
+ p += sizeof(u_int32_t);
+ P_32_SWAP(p);
+ p += sizeof(pgno_t);
+ if (*(u_char *)p & P_BIGKEY) {
+ p += sizeof(u_char);
+ P_32_SWAP(p);
+ p += sizeof(pgno_t);
+ P_32_SWAP(p);
+ }
+ M_16_SWAP(h->linp[i]);
+ }
+ else if ((h->flags & P_TYPE) == P_BLEAF)
+ for (i = 0; i < top; i++) {
+ p = (char *)GETBLEAF(h, i);
+ P_32_SWAP(p);
+ p += sizeof(u_int32_t);
+ P_32_SWAP(p);
+ p += sizeof(u_int32_t);
+ flags = *(u_char *)p;
+ if (flags & (P_BIGKEY | P_BIGDATA)) {
+ p += sizeof(u_char);
+ if (flags & P_BIGKEY) {
+ P_32_SWAP(p);
+ p += sizeof(pgno_t);
+ P_32_SWAP(p);
+ }
+ if (flags & P_BIGDATA) {
+ p += sizeof(u_int32_t);
+ P_32_SWAP(p);
+ p += sizeof(pgno_t);
+ P_32_SWAP(p);
+ }
+ }
+ M_16_SWAP(h->linp[i]);
+ }
+
+ M_32_SWAP(h->pgno);
+ M_32_SWAP(h->prevpg);
+ M_32_SWAP(h->nextpg);
+ M_32_SWAP(h->flags);
+ M_16_SWAP(h->lower);
+ M_16_SWAP(h->upper);
+}
+
+/*
+ * MSWAP -- Actually swap the bytes on the meta page.
+ *
+ * Parameters:
+ * p: page to convert
+ */
+static void
+mswap(pg)
+ PAGE *pg;
+{
+ char *p;
+
+ p = (char *)pg;
+ P_32_SWAP(p); /* magic */
+ p += sizeof(u_int32_t);
+ P_32_SWAP(p); /* version */
+ p += sizeof(u_int32_t);
+ P_32_SWAP(p); /* psize */
+ p += sizeof(u_int32_t);
+ P_32_SWAP(p); /* free */
+ p += sizeof(u_int32_t);
+ P_32_SWAP(p); /* nrecs */
+ p += sizeof(u_int32_t);
+ P_32_SWAP(p); /* flags */
+ p += sizeof(u_int32_t);
+}
diff --git a/trunk/main/db1-ast/btree/bt_debug.c b/trunk/main/db1-ast/btree/bt_debug.c
new file mode 100644
index 000000000..e035851a8
--- /dev/null
+++ b/trunk/main/db1-ast/btree/bt_debug.c
@@ -0,0 +1,329 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Olson.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)bt_debug.c 8.5 (Berkeley) 8/17/94";
+#endif /* LIBC_SCCS and not lint */
+
+#ifdef DEBUG
+#include <sys/param.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../include/db.h"
+#include "btree.h"
+
+/*
+ * BT_DUMP -- Dump the tree
+ *
+ * Parameters:
+ * dbp: pointer to the DB
+ */
+void
+__bt_dump(dbp)
+ DB *dbp;
+{
+ BTREE *t;
+ PAGE *h;
+ pgno_t i;
+ char *sep;
+
+ t = dbp->internal;
+ (void)fprintf(stderr, "%s: pgsz %d",
+ F_ISSET(t, B_INMEM) ? "memory" : "disk", t->bt_psize);
+ if (F_ISSET(t, R_RECNO))
+ (void)fprintf(stderr, " keys %lu", t->bt_nrecs);
+#undef X
+#define X(flag, name) \
+ if (F_ISSET(t, flag)) { \
+ (void)fprintf(stderr, "%s%s", sep, name); \
+ sep = ", "; \
+ }
+ if (t->flags != 0) {
+ sep = " flags (";
+ X(R_FIXLEN, "FIXLEN");
+ X(B_INMEM, "INMEM");
+ X(B_NODUPS, "NODUPS");
+ X(B_RDONLY, "RDONLY");
+ X(R_RECNO, "RECNO");
+ X(B_METADIRTY,"METADIRTY");
+ (void)fprintf(stderr, ")\n");
+ }
+#undef X
+
+ for (i = P_ROOT; (h = mpool_get(t->bt_mp, i, 0)) != NULL; ++i) {
+ __bt_dpage(h);
+ (void)mpool_put(t->bt_mp, h, 0);
+ }
+}
+
+/*
+ * BT_DMPAGE -- Dump the meta page
+ *
+ * Parameters:
+ * h: pointer to the PAGE
+ */
+void
+__bt_dmpage(h)
+ PAGE *h;
+{
+ BTMETA *m;
+ char *sep;
+
+ m = (BTMETA *)h;
+ (void)fprintf(stderr, "magic %lx\n", m->magic);
+ (void)fprintf(stderr, "version %lu\n", m->version);
+ (void)fprintf(stderr, "psize %lu\n", m->psize);
+ (void)fprintf(stderr, "free %lu\n", m->free);
+ (void)fprintf(stderr, "nrecs %lu\n", m->nrecs);
+ (void)fprintf(stderr, "flags %lu", m->flags);
+#undef X
+#define X(flag, name) \
+ if (m->flags & flag) { \
+ (void)fprintf(stderr, "%s%s", sep, name); \
+ sep = ", "; \
+ }
+ if (m->flags) {
+ sep = " (";
+ X(B_NODUPS, "NODUPS");
+ X(R_RECNO, "RECNO");
+ (void)fprintf(stderr, ")");
+ }
+}
+
+/*
+ * BT_DNPAGE -- Dump the page
+ *
+ * Parameters:
+ * n: page number to dump.
+ */
+void
+__bt_dnpage(dbp, pgno)
+ DB *dbp;
+ pgno_t pgno;
+{
+ BTREE *t;
+ PAGE *h;
+
+ t = dbp->internal;
+ if ((h = mpool_get(t->bt_mp, pgno, 0)) != NULL) {
+ __bt_dpage(h);
+ (void)mpool_put(t->bt_mp, h, 0);
+ }
+}
+
+/*
+ * BT_DPAGE -- Dump the page
+ *
+ * Parameters:
+ * h: pointer to the PAGE
+ */
+void
+__bt_dpage(h)
+ PAGE *h;
+{
+ BINTERNAL *bi;
+ BLEAF *bl;
+ RINTERNAL *ri;
+ RLEAF *rl;
+ indx_t cur, top;
+ char *sep;
+
+ (void)fprintf(stderr, " page %d: (", h->pgno);
+#undef X
+#define X(flag, name) \
+ if (h->flags & flag) { \
+ (void)fprintf(stderr, "%s%s", sep, name); \
+ sep = ", "; \
+ }
+ sep = "";
+ X(P_BINTERNAL, "BINTERNAL") /* types */
+ X(P_BLEAF, "BLEAF")
+ X(P_RINTERNAL, "RINTERNAL") /* types */
+ X(P_RLEAF, "RLEAF")
+ X(P_OVERFLOW, "OVERFLOW")
+ X(P_PRESERVE, "PRESERVE");
+ (void)fprintf(stderr, ")\n");
+#undef X
+
+ (void)fprintf(stderr, "\tprev %2d next %2d", h->prevpg, h->nextpg);
+ if (h->flags & P_OVERFLOW)
+ return;
+
+ top = NEXTINDEX(h);
+ (void)fprintf(stderr, " lower %3d upper %3d nextind %d\n",
+ h->lower, h->upper, top);
+ for (cur = 0; cur < top; cur++) {
+ (void)fprintf(stderr, "\t[%03d] %4d ", cur, h->linp[cur]);
+ switch (h->flags & P_TYPE) {
+ case P_BINTERNAL:
+ bi = GETBINTERNAL(h, cur);
+ (void)fprintf(stderr,
+ "size %03d pgno %03d", bi->ksize, bi->pgno);
+ if (bi->flags & P_BIGKEY)
+ (void)fprintf(stderr, " (indirect)");
+ else if (bi->ksize)
+ (void)fprintf(stderr,
+ " {%.*s}", (int)bi->ksize, bi->bytes);
+ break;
+ case P_RINTERNAL:
+ ri = GETRINTERNAL(h, cur);
+ (void)fprintf(stderr, "entries %03d pgno %03d",
+ ri->nrecs, ri->pgno);
+ break;
+ case P_BLEAF:
+ bl = GETBLEAF(h, cur);
+ if (bl->flags & P_BIGKEY)
+ (void)fprintf(stderr,
+ "big key page %lu size %u/",
+ *(pgno_t *)bl->bytes,
+ *(u_int32_t *)(bl->bytes + sizeof(pgno_t)));
+ else if (bl->ksize)
+ (void)fprintf(stderr, "%s/", bl->bytes);
+ if (bl->flags & P_BIGDATA)
+ (void)fprintf(stderr,
+ "big data page %lu size %u",
+ *(pgno_t *)(bl->bytes + bl->ksize),
+ *(u_int32_t *)(bl->bytes + bl->ksize +
+ sizeof(pgno_t)));
+ else if (bl->dsize)
+ (void)fprintf(stderr, "%.*s",
+ (int)bl->dsize, bl->bytes + bl->ksize);
+ break;
+ case P_RLEAF:
+ rl = GETRLEAF(h, cur);
+ if (rl->flags & P_BIGDATA)
+ (void)fprintf(stderr,
+ "big data page %lu size %u",
+ *(pgno_t *)rl->bytes,
+ *(u_int32_t *)(rl->bytes + sizeof(pgno_t)));
+ else if (rl->dsize)
+ (void)fprintf(stderr,
+ "%.*s", (int)rl->dsize, rl->bytes);
+ break;
+ }
+ (void)fprintf(stderr, "\n");
+ }
+}
+#endif
+
+#ifdef STATISTICS
+/*
+ * BT_STAT -- Gather/print the tree statistics
+ *
+ * Parameters:
+ * dbp: pointer to the DB
+ */
+void
+__bt_stat(dbp)
+ DB *dbp;
+{
+ extern u_long bt_cache_hit, bt_cache_miss, bt_pfxsaved, bt_rootsplit;
+ extern u_long bt_sortsplit, bt_split;
+ BTREE *t;
+ PAGE *h;
+ pgno_t i, pcont, pinternal, pleaf;
+ u_long ifree, lfree, nkeys;
+ int levels;
+
+ t = dbp->internal;
+ pcont = pinternal = pleaf = 0;
+ nkeys = ifree = lfree = 0;
+ for (i = P_ROOT; (h = mpool_get(t->bt_mp, i, 0)) != NULL; ++i) {
+ switch (h->flags & P_TYPE) {
+ case P_BINTERNAL:
+ case P_RINTERNAL:
+ ++pinternal;
+ ifree += h->upper - h->lower;
+ break;
+ case P_BLEAF:
+ case P_RLEAF:
+ ++pleaf;
+ lfree += h->upper - h->lower;
+ nkeys += NEXTINDEX(h);
+ break;
+ case P_OVERFLOW:
+ ++pcont;
+ break;
+ }
+ (void)mpool_put(t->bt_mp, h, 0);
+ }
+
+ /* Count the levels of the tree. */
+ for (i = P_ROOT, levels = 0 ;; ++levels) {
+ h = mpool_get(t->bt_mp, i, 0);
+ if (h->flags & (P_BLEAF|P_RLEAF)) {
+ if (levels == 0)
+ levels = 1;
+ (void)mpool_put(t->bt_mp, h, 0);
+ break;
+ }
+ i = F_ISSET(t, R_RECNO) ?
+ GETRINTERNAL(h, 0)->pgno :
+ GETBINTERNAL(h, 0)->pgno;
+ (void)mpool_put(t->bt_mp, h, 0);
+ }
+
+ (void)fprintf(stderr, "%d level%s with %ld keys",
+ levels, levels == 1 ? "" : "s", nkeys);
+ if (F_ISSET(t, R_RECNO))
+ (void)fprintf(stderr, " (%ld header count)", t->bt_nrecs);
+ (void)fprintf(stderr,
+ "\n%lu pages (leaf %ld, internal %ld, overflow %ld)\n",
+ pinternal + pleaf + pcont, pleaf, pinternal, pcont);
+ (void)fprintf(stderr, "%ld cache hits, %ld cache misses\n",
+ bt_cache_hit, bt_cache_miss);
+ (void)fprintf(stderr, "%ld splits (%ld root splits, %ld sort splits)\n",
+ bt_split, bt_rootsplit, bt_sortsplit);
+ pleaf *= t->bt_psize - BTDATAOFF;
+ if (pleaf)
+ (void)fprintf(stderr,
+ "%.0f%% leaf fill (%ld bytes used, %ld bytes free)\n",
+ ((double)(pleaf - lfree) / pleaf) * 100,
+ pleaf - lfree, lfree);
+ pinternal *= t->bt_psize - BTDATAOFF;
+ if (pinternal)
+ (void)fprintf(stderr,
+ "%.0f%% internal fill (%ld bytes used, %ld bytes free\n",
+ ((double)(pinternal - ifree) / pinternal) * 100,
+ pinternal - ifree, ifree);
+ if (bt_pfxsaved)
+ (void)fprintf(stderr, "prefix checking removed %lu bytes.\n",
+ bt_pfxsaved);
+}
+#endif
diff --git a/trunk/main/db1-ast/btree/bt_delete.c b/trunk/main/db1-ast/btree/bt_delete.c
new file mode 100644
index 000000000..e816c432a
--- /dev/null
+++ b/trunk/main/db1-ast/btree/bt_delete.c
@@ -0,0 +1,657 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Olson.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)bt_delete.c 8.13 (Berkeley) 7/28/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "../include/db.h"
+#include "btree.h"
+
+static int __bt_bdelete __P((BTREE *, const DBT *));
+static int __bt_curdel __P((BTREE *, const DBT *, PAGE *, u_int));
+static int __bt_pdelete __P((BTREE *, PAGE *));
+static int __bt_relink __P((BTREE *, PAGE *));
+static int __bt_stkacq __P((BTREE *, PAGE **, CURSOR *));
+
+/*
+ * __bt_delete
+ * Delete the item(s) referenced by a key.
+ *
+ * Return RET_SPECIAL if the key is not found.
+ */
+int
+__bt_delete(dbp, key, flags)
+ const DB *dbp;
+ const DBT *key;
+ u_int flags;
+{
+ BTREE *t;
+ CURSOR *c;
+ PAGE *h;
+ int status;
+
+ t = dbp->internal;
+
+ /* Toss any page pinned across calls. */
+ if (t->bt_pinned != NULL) {
+ mpool_put(t->bt_mp, t->bt_pinned, 0);
+ t->bt_pinned = NULL;
+ }
+
+ /* Check for change to a read-only tree. */
+ if (F_ISSET(t, B_RDONLY)) {
+ errno = EPERM;
+ return (RET_ERROR);
+ }
+
+ switch (flags) {
+ case 0:
+ status = __bt_bdelete(t, key);
+ break;
+ case R_CURSOR:
+ /*
+ * If flags is R_CURSOR, delete the cursor. Must already
+ * have started a scan and not have already deleted it.
+ */
+ c = &t->bt_cursor;
+ if (F_ISSET(c, CURS_INIT)) {
+ if (F_ISSET(c, CURS_ACQUIRE | CURS_AFTER | CURS_BEFORE))
+ return (RET_SPECIAL);
+ if ((h = mpool_get(t->bt_mp, c->pg.pgno, 0)) == NULL)
+ return (RET_ERROR);
+
+ /*
+ * If the page is about to be emptied, we'll need to
+ * delete it, which means we have to acquire a stack.
+ */
+ if (NEXTINDEX(h) == 1)
+ if (__bt_stkacq(t, &h, &t->bt_cursor))
+ return (RET_ERROR);
+
+ status = __bt_dleaf(t, NULL, h, c->pg.index);
+
+ if (NEXTINDEX(h) == 0 && status == RET_SUCCESS) {
+ if (__bt_pdelete(t, h))
+ return (RET_ERROR);
+ } else
+ mpool_put(t->bt_mp,
+ h, status == RET_SUCCESS ? MPOOL_DIRTY : 0);
+ break;
+ }
+ /* FALLTHROUGH */
+ default:
+ errno = EINVAL;
+ return (RET_ERROR);
+ }
+ if (status == RET_SUCCESS)
+ F_SET(t, B_MODIFIED);
+ return (status);
+}
+
+/*
+ * __bt_stkacq --
+ * Acquire a stack so we can delete a cursor entry.
+ *
+ * Parameters:
+ * t: tree
+ * hp: pointer to current, pinned PAGE pointer
+ * c: pointer to the cursor
+ *
+ * Returns:
+ * 0 on success, 1 on failure
+ */
+static int
+__bt_stkacq(t, hp, c)
+ BTREE *t;
+ PAGE **hp;
+ CURSOR *c;
+{
+ BINTERNAL *bi;
+ EPG *e;
+ EPGNO *parent;
+ PAGE *h;
+ indx_t index = 0;
+ pgno_t pgno;
+ recno_t nextpg, prevpg;
+ int exact, level;
+
+ /*
+ * Find the first occurrence of the key in the tree. Toss the
+ * currently locked page so we don't hit an already-locked page.
+ */
+ h = *hp;
+ mpool_put(t->bt_mp, h, 0);
+ if ((e = __bt_search(t, &c->key, &exact)) == NULL)
+ return (1);
+ h = e->page;
+
+ /* See if we got it in one shot. */
+ if (h->pgno == c->pg.pgno)
+ goto ret;
+
+ /*
+ * Move right, looking for the page. At each move we have to move
+ * up the stack until we don't have to move to the next page. If
+ * we have to change pages at an internal level, we have to fix the
+ * stack back up.
+ */
+ while (h->pgno != c->pg.pgno) {
+ if ((nextpg = h->nextpg) == P_INVALID)
+ break;
+ mpool_put(t->bt_mp, h, 0);
+
+ /* Move up the stack. */
+ for (level = 0; (parent = BT_POP(t)) != NULL; ++level) {
+ /* Get the parent page. */
+ if ((h = mpool_get(t->bt_mp, parent->pgno, 0)) == NULL)
+ return (1);
+
+ /* Move to the next index. */
+ if (parent->index != NEXTINDEX(h) - 1) {
+ index = parent->index + 1;
+ BT_PUSH(t, h->pgno, index);
+ break;
+ }
+ mpool_put(t->bt_mp, h, 0);
+ }
+
+ /* Restore the stack. */
+ while (level--) {
+ /* Push the next level down onto the stack. */
+ bi = GETBINTERNAL(h, index);
+ pgno = bi->pgno;
+ BT_PUSH(t, pgno, 0);
+
+ /* Lose the currently pinned page. */
+ mpool_put(t->bt_mp, h, 0);
+
+ /* Get the next level down. */
+ if ((h = mpool_get(t->bt_mp, pgno, 0)) == NULL)
+ return (1);
+ index = 0;
+ }
+ mpool_put(t->bt_mp, h, 0);
+ if ((h = mpool_get(t->bt_mp, nextpg, 0)) == NULL)
+ return (1);
+ }
+
+ if (h->pgno == c->pg.pgno)
+ goto ret;
+
+ /* Reacquire the original stack. */
+ mpool_put(t->bt_mp, h, 0);
+ if ((e = __bt_search(t, &c->key, &exact)) == NULL)
+ return (1);
+ h = e->page;
+
+ /*
+ * Move left, looking for the page. At each move we have to move
+ * up the stack until we don't have to change pages to move to the
+ * next page. If we have to change pages at an internal level, we
+ * have to fix the stack back up.
+ */
+ while (h->pgno != c->pg.pgno) {
+ if ((prevpg = h->prevpg) == P_INVALID)
+ break;
+ mpool_put(t->bt_mp, h, 0);
+
+ /* Move up the stack. */
+ for (level = 0; (parent = BT_POP(t)) != NULL; ++level) {
+ /* Get the parent page. */
+ if ((h = mpool_get(t->bt_mp, parent->pgno, 0)) == NULL)
+ return (1);
+
+ /* Move to the next index. */
+ if (parent->index != 0) {
+ index = parent->index - 1;
+ BT_PUSH(t, h->pgno, index);
+ break;
+ }
+ mpool_put(t->bt_mp, h, 0);
+ }
+
+ /* Restore the stack. */
+ while (level--) {
+ /* Push the next level down onto the stack. */
+ bi = GETBINTERNAL(h, index);
+ pgno = bi->pgno;
+
+ /* Lose the currently pinned page. */
+ mpool_put(t->bt_mp, h, 0);
+
+ /* Get the next level down. */
+ if ((h = mpool_get(t->bt_mp, pgno, 0)) == NULL)
+ return (1);
+
+ index = NEXTINDEX(h) - 1;
+ BT_PUSH(t, pgno, index);
+ }
+ mpool_put(t->bt_mp, h, 0);
+ if ((h = mpool_get(t->bt_mp, prevpg, 0)) == NULL)
+ return (1);
+ }
+
+
+ret: mpool_put(t->bt_mp, h, 0);
+ return ((*hp = mpool_get(t->bt_mp, c->pg.pgno, 0)) == NULL);
+}
+
+/*
+ * __bt_bdelete --
+ * Delete all key/data pairs matching the specified key.
+ *
+ * Parameters:
+ * t: tree
+ * key: key to delete
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS and RET_SPECIAL if the key not found.
+ */
+static int
+__bt_bdelete(t, key)
+ BTREE *t;
+ const DBT *key;
+{
+ EPG *e;
+ PAGE *h;
+ int deleted, exact, redo;
+
+ deleted = 0;
+
+ /* Find any matching record; __bt_search pins the page. */
+loop: if ((e = __bt_search(t, key, &exact)) == NULL)
+ return (deleted ? RET_SUCCESS : RET_ERROR);
+ if (!exact) {
+ mpool_put(t->bt_mp, e->page, 0);
+ return (deleted ? RET_SUCCESS : RET_SPECIAL);
+ }
+
+ /*
+ * Delete forward, then delete backward, from the found key. If
+ * there are duplicates and we reach either side of the page, do
+ * the key search again, so that we get them all.
+ */
+ redo = 0;
+ h = e->page;
+ do {
+ if (__bt_dleaf(t, key, h, e->index)) {
+ mpool_put(t->bt_mp, h, 0);
+ return (RET_ERROR);
+ }
+ if (F_ISSET(t, B_NODUPS)) {
+ if (NEXTINDEX(h) == 0) {
+ if (__bt_pdelete(t, h))
+ return (RET_ERROR);
+ } else
+ mpool_put(t->bt_mp, h, MPOOL_DIRTY);
+ return (RET_SUCCESS);
+ }
+ deleted = 1;
+ } while (e->index < NEXTINDEX(h) && __bt_cmp(t, key, e) == 0);
+
+ /* Check for right-hand edge of the page. */
+ if (e->index == NEXTINDEX(h))
+ redo = 1;
+
+ /* Delete from the key to the beginning of the page. */
+ while (e->index-- > 0) {
+ if (__bt_cmp(t, key, e) != 0)
+ break;
+ if (__bt_dleaf(t, key, h, e->index) == RET_ERROR) {
+ mpool_put(t->bt_mp, h, 0);
+ return (RET_ERROR);
+ }
+ if (e->index == 0)
+ redo = 1;
+ }
+
+ /* Check for an empty page. */
+ if (NEXTINDEX(h) == 0) {
+ if (__bt_pdelete(t, h))
+ return (RET_ERROR);
+ goto loop;
+ }
+
+ /* Put the page. */
+ mpool_put(t->bt_mp, h, MPOOL_DIRTY);
+
+ if (redo)
+ goto loop;
+ return (RET_SUCCESS);
+}
+
+/*
+ * __bt_pdelete --
+ * Delete a single page from the tree.
+ *
+ * Parameters:
+ * t: tree
+ * h: leaf page
+ *
+ * Returns:
+ * RET_SUCCESS, RET_ERROR.
+ *
+ * Side-effects:
+ * mpool_put's the page
+ */
+static int
+__bt_pdelete(t, h)
+ BTREE *t;
+ PAGE *h;
+{
+ BINTERNAL *bi;
+ PAGE *pg;
+ EPGNO *parent;
+ indx_t cnt, index, *ip, offset;
+ u_int32_t nksize;
+ char *from;
+
+ /*
+ * Walk the parent page stack -- a LIFO stack of the pages that were
+ * traversed when we searched for the page where the delete occurred.
+ * Each stack entry is a page number and a page index offset. The
+ * offset is for the page traversed on the search. We've just deleted
+ * a page, so we have to delete the key from the parent page.
+ *
+ * If the delete from the parent page makes it empty, this process may
+ * continue all the way up the tree. We stop if we reach the root page
+ * (which is never deleted, it's just not worth the effort) or if the
+ * delete does not empty the page.
+ */
+ while ((parent = BT_POP(t)) != NULL) {
+ /* Get the parent page. */
+ if ((pg = mpool_get(t->bt_mp, parent->pgno, 0)) == NULL)
+ return (RET_ERROR);
+
+ index = parent->index;
+ bi = GETBINTERNAL(pg, index);
+
+ /* Free any overflow pages. */
+ if (bi->flags & P_BIGKEY &&
+ __ovfl_delete(t, bi->bytes) == RET_ERROR) {
+ mpool_put(t->bt_mp, pg, 0);
+ return (RET_ERROR);
+ }
+
+ /*
+ * Free the parent if it has only the one key and it's not the
+ * root page. If it's the rootpage, turn it back into an empty
+ * leaf page.
+ */
+ if (NEXTINDEX(pg) == 1) {
+ if (pg->pgno == P_ROOT) {
+ pg->lower = BTDATAOFF;
+ pg->upper = t->bt_psize;
+ pg->flags = P_BLEAF;
+ } else {
+ if (__bt_relink(t, pg) || __bt_free(t, pg))
+ return (RET_ERROR);
+ continue;
+ }
+ } else {
+ /* Pack remaining key items at the end of the page. */
+ nksize = NBINTERNAL(bi->ksize);
+ from = (char *)pg + pg->upper;
+ memmove(from + nksize, from, (char *)bi - from);
+ pg->upper += nksize;
+
+ /* Adjust indices' offsets, shift the indices down. */
+ offset = pg->linp[index];
+ for (cnt = index, ip = &pg->linp[0]; cnt--; ++ip)
+ if (ip[0] < offset)
+ ip[0] += nksize;
+ for (cnt = NEXTINDEX(pg) - index; --cnt; ++ip)
+ ip[0] = ip[1] < offset ? ip[1] + nksize : ip[1];
+ pg->lower -= sizeof(indx_t);
+ }
+
+ mpool_put(t->bt_mp, pg, MPOOL_DIRTY);
+ break;
+ }
+
+ /* Free the leaf page, as long as it wasn't the root. */
+ if (h->pgno == P_ROOT) {
+ mpool_put(t->bt_mp, h, MPOOL_DIRTY);
+ return (RET_SUCCESS);
+ }
+ return (__bt_relink(t, h) || __bt_free(t, h));
+}
+
+/*
+ * __bt_dleaf --
+ * Delete a single record from a leaf page.
+ *
+ * Parameters:
+ * t: tree
+ * key: referenced key
+ * h: page
+ * index: index on page to delete
+ *
+ * Returns:
+ * RET_SUCCESS, RET_ERROR.
+ */
+int
+__bt_dleaf(t, key, h, index)
+ BTREE *t;
+ const DBT *key;
+ PAGE *h;
+ u_int index;
+{
+ BLEAF *bl;
+ indx_t cnt, *ip, offset;
+ u_int32_t nbytes;
+ void *to;
+ char *from;
+
+ /* If this record is referenced by the cursor, delete the cursor. */
+ if (F_ISSET(&t->bt_cursor, CURS_INIT) &&
+ !F_ISSET(&t->bt_cursor, CURS_ACQUIRE) &&
+ t->bt_cursor.pg.pgno == h->pgno && t->bt_cursor.pg.index == index &&
+ __bt_curdel(t, key, h, index))
+ return (RET_ERROR);
+
+ /* If the entry uses overflow pages, make them available for reuse. */
+ to = bl = GETBLEAF(h, index);
+ if (bl->flags & P_BIGKEY && __ovfl_delete(t, bl->bytes) == RET_ERROR)
+ return (RET_ERROR);
+ if (bl->flags & P_BIGDATA &&
+ __ovfl_delete(t, bl->bytes + bl->ksize) == RET_ERROR)
+ return (RET_ERROR);
+
+ /* Pack the remaining key/data items at the end of the page. */
+ nbytes = NBLEAF(bl);
+ from = (char *)h + h->upper;
+ memmove(from + nbytes, from, (char *)to - from);
+ h->upper += nbytes;
+
+ /* Adjust the indices' offsets, shift the indices down. */
+ offset = h->linp[index];
+ for (cnt = index, ip = &h->linp[0]; cnt--; ++ip)
+ if (ip[0] < offset)
+ ip[0] += nbytes;
+ for (cnt = NEXTINDEX(h) - index; --cnt; ++ip)
+ ip[0] = ip[1] < offset ? ip[1] + nbytes : ip[1];
+ h->lower -= sizeof(indx_t);
+
+ /* If the cursor is on this page, adjust it as necessary. */
+ if (F_ISSET(&t->bt_cursor, CURS_INIT) &&
+ !F_ISSET(&t->bt_cursor, CURS_ACQUIRE) &&
+ t->bt_cursor.pg.pgno == h->pgno && t->bt_cursor.pg.index > index)
+ --t->bt_cursor.pg.index;
+
+ return (RET_SUCCESS);
+}
+
+/*
+ * __bt_curdel --
+ * Delete the cursor.
+ *
+ * Parameters:
+ * t: tree
+ * key: referenced key (or NULL)
+ * h: page
+ * index: index on page to delete
+ *
+ * Returns:
+ * RET_SUCCESS, RET_ERROR.
+ */
+static int
+__bt_curdel(t, key, h, index)
+ BTREE *t;
+ const DBT *key;
+ PAGE *h;
+ u_int index;
+{
+ CURSOR *c;
+ EPG e;
+ PAGE *pg;
+ int curcopy, status;
+
+ /*
+ * If there are duplicates, move forward or backward to one.
+ * Otherwise, copy the key into the cursor area.
+ */
+ c = &t->bt_cursor;
+ F_CLR(c, CURS_AFTER | CURS_BEFORE | CURS_ACQUIRE);
+
+ curcopy = 0;
+ if (!F_ISSET(t, B_NODUPS)) {
+ /*
+ * We're going to have to do comparisons. If we weren't
+ * provided a copy of the key, i.e. the user is deleting
+ * the current cursor position, get one.
+ */
+ if (key == NULL) {
+ e.page = h;
+ e.index = index;
+ if ((status = __bt_ret(t, &e,
+ &c->key, &c->key, NULL, NULL, 1)) != RET_SUCCESS)
+ return (status);
+ curcopy = 1;
+ key = &c->key;
+ }
+ /* Check previous key, if not at the beginning of the page. */
+ if (index > 0) {
+ e.page = h;
+ e.index = index - 1;
+ if (__bt_cmp(t, key, &e) == 0) {
+ F_SET(c, CURS_BEFORE);
+ goto dup2;
+ }
+ }
+ /* Check next key, if not at the end of the page. */
+ if (index < NEXTINDEX(h) - 1) {
+ e.page = h;
+ e.index = index + 1;
+ if (__bt_cmp(t, key, &e) == 0) {
+ F_SET(c, CURS_AFTER);
+ goto dup2;
+ }
+ }
+ /* Check previous key if at the beginning of the page. */
+ if (index == 0 && h->prevpg != P_INVALID) {
+ if ((pg = mpool_get(t->bt_mp, h->prevpg, 0)) == NULL)
+ return (RET_ERROR);
+ e.page = pg;
+ e.index = NEXTINDEX(pg) - 1;
+ if (__bt_cmp(t, key, &e) == 0) {
+ F_SET(c, CURS_BEFORE);
+ goto dup1;
+ }
+ mpool_put(t->bt_mp, pg, 0);
+ }
+ /* Check next key if at the end of the page. */
+ if (index == NEXTINDEX(h) - 1 && h->nextpg != P_INVALID) {
+ if ((pg = mpool_get(t->bt_mp, h->nextpg, 0)) == NULL)
+ return (RET_ERROR);
+ e.page = pg;
+ e.index = 0;
+ if (__bt_cmp(t, key, &e) == 0) {
+ F_SET(c, CURS_AFTER);
+dup1: mpool_put(t->bt_mp, pg, 0);
+dup2: c->pg.pgno = e.page->pgno;
+ c->pg.index = e.index;
+ return (RET_SUCCESS);
+ }
+ mpool_put(t->bt_mp, pg, 0);
+ }
+ }
+ e.page = h;
+ e.index = index;
+ if (curcopy || (status =
+ __bt_ret(t, &e, &c->key, &c->key, NULL, NULL, 1)) == RET_SUCCESS) {
+ F_SET(c, CURS_ACQUIRE);
+ return (RET_SUCCESS);
+ }
+ return (status);
+}
+
+/*
+ * __bt_relink --
+ * Link around a deleted page.
+ *
+ * Parameters:
+ * t: tree
+ * h: page to be deleted
+ */
+static int
+__bt_relink(t, h)
+ BTREE *t;
+ PAGE *h;
+{
+ PAGE *pg;
+
+ if (h->nextpg != P_INVALID) {
+ if ((pg = mpool_get(t->bt_mp, h->nextpg, 0)) == NULL)
+ return (RET_ERROR);
+ pg->prevpg = h->prevpg;
+ mpool_put(t->bt_mp, pg, MPOOL_DIRTY);
+ }
+ if (h->prevpg != P_INVALID) {
+ if ((pg = mpool_get(t->bt_mp, h->prevpg, 0)) == NULL)
+ return (RET_ERROR);
+ pg->nextpg = h->nextpg;
+ mpool_put(t->bt_mp, pg, MPOOL_DIRTY);
+ }
+ return (0);
+}
diff --git a/trunk/main/db1-ast/btree/bt_get.c b/trunk/main/db1-ast/btree/bt_get.c
new file mode 100644
index 000000000..b5e18022c
--- /dev/null
+++ b/trunk/main/db1-ast/btree/bt_get.c
@@ -0,0 +1,105 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Olson.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)bt_get.c 8.6 (Berkeley) 7/20/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+
+#include "../include/db.h"
+#include "btree.h"
+
+/*
+ * __BT_GET -- Get a record from the btree.
+ *
+ * Parameters:
+ * dbp: pointer to access method
+ * key: key to find
+ * data: data to return
+ * flag: currently unused
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS and RET_SPECIAL if the key not found.
+ */
+int
+__bt_get(dbp, key, data, flags)
+ const DB *dbp;
+ const DBT *key;
+ DBT *data;
+ u_int flags;
+{
+ BTREE *t;
+ EPG *e;
+ int exact, status;
+
+ t = dbp->internal;
+
+ /* Toss any page pinned across calls. */
+ if (t->bt_pinned != NULL) {
+ mpool_put(t->bt_mp, t->bt_pinned, 0);
+ t->bt_pinned = NULL;
+ }
+
+ /* Get currently doesn't take any flags. */
+ if (flags) {
+ errno = EINVAL;
+ return (RET_ERROR);
+ }
+
+ if ((e = __bt_search(t, key, &exact)) == NULL)
+ return (RET_ERROR);
+ if (!exact) {
+ mpool_put(t->bt_mp, e->page, 0);
+ return (RET_SPECIAL);
+ }
+
+ status = __bt_ret(t, e, NULL, NULL, data, &t->bt_rdata, 0);
+
+ /*
+ * If the user is doing concurrent access, we copied the
+ * key/data, toss the page.
+ */
+ if (F_ISSET(t, B_DB_LOCK))
+ mpool_put(t->bt_mp, e->page, 0);
+ else
+ t->bt_pinned = e->page;
+ return (status);
+}
diff --git a/trunk/main/db1-ast/btree/bt_open.c b/trunk/main/db1-ast/btree/bt_open.c
new file mode 100644
index 000000000..5d40e4593
--- /dev/null
+++ b/trunk/main/db1-ast/btree/bt_open.c
@@ -0,0 +1,458 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Olson.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)bt_open.c 8.10 (Berkeley) 8/17/94";
+#endif /* LIBC_SCCS and not lint */
+
+/*
+ * Implementation of btree access method for 4.4BSD.
+ *
+ * The design here was originally based on that of the btree access method
+ * used in the Postgres database system at UC Berkeley. This implementation
+ * is wholly independent of the Postgres code.
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "../include/db.h"
+#include "btree.h"
+
+#ifdef DEBUG
+#undef MINPSIZE
+#define MINPSIZE 128
+#endif
+
+static int byteorder __P((void));
+static int nroot __P((BTREE *));
+static int tmp __P((void));
+
+/*
+ * __BT_OPEN -- Open a btree.
+ *
+ * Creates and fills a DB struct, and calls the routine that actually
+ * opens the btree.
+ *
+ * Parameters:
+ * fname: filename (NULL for in-memory trees)
+ * flags: open flag bits
+ * mode: open permission bits
+ * b: BTREEINFO pointer
+ *
+ * Returns:
+ * NULL on failure, pointer to DB on success.
+ *
+ */
+DB *
+__bt_open(fname, flags, mode, openinfo, dflags)
+ const char *fname;
+ int flags, mode, dflags;
+ const BTREEINFO *openinfo;
+{
+ struct stat sb;
+ BTMETA m;
+ BTREE *t;
+ BTREEINFO b;
+ DB *dbp;
+ pgno_t ncache;
+ ssize_t nr;
+ int machine_lorder;
+
+ t = NULL;
+
+ /*
+ * Intention is to make sure all of the user's selections are okay
+ * here and then use them without checking. Can't be complete, since
+ * we don't know the right page size, lorder or flags until the backing
+ * file is opened. Also, the file's page size can cause the cachesize
+ * to change.
+ */
+ machine_lorder = byteorder();
+ if (openinfo) {
+ b = *openinfo;
+
+ /* Flags: R_DUP. */
+ if (b.flags & ~(R_DUP))
+ goto einval;
+
+ /*
+ * Page size must be indx_t aligned and >= MINPSIZE. Default
+ * page size is set farther on, based on the underlying file
+ * transfer size.
+ */
+ if (b.psize &&
+ (b.psize < MINPSIZE || b.psize > MAX_PAGE_OFFSET + 1 ||
+ b.psize & (sizeof(indx_t) - 1)))
+ goto einval;
+
+ /* Minimum number of keys per page; absolute minimum is 2. */
+ if (b.minkeypage) {
+ if (b.minkeypage < 2)
+ goto einval;
+ } else
+ b.minkeypage = DEFMINKEYPAGE;
+
+ /* If no comparison, use default comparison and prefix. */
+ if (b.compare == NULL) {
+ b.compare = __bt_defcmp;
+ if (b.prefix == NULL)
+ b.prefix = __bt_defpfx;
+ }
+
+ if (b.lorder == 0)
+ b.lorder = machine_lorder;
+ } else {
+ b.compare = __bt_defcmp;
+ b.cachesize = 0;
+ b.flags = 0;
+ b.lorder = machine_lorder;
+ b.minkeypage = DEFMINKEYPAGE;
+ b.prefix = __bt_defpfx;
+ b.psize = 0;
+ }
+
+ /* Check for the ubiquitous PDP-11. */
+ if (b.lorder != BIG_ENDIAN && b.lorder != LITTLE_ENDIAN)
+ goto einval;
+
+ /* Allocate and initialize DB and BTREE structures. */
+ if ((t = (BTREE *)malloc(sizeof(BTREE))) == NULL)
+ goto err;
+ memset(t, 0, sizeof(BTREE));
+ t->bt_fd = -1; /* Don't close unopened fd on error. */
+ t->bt_lorder = b.lorder;
+ t->bt_order = NOT;
+ t->bt_cmp = b.compare;
+ t->bt_pfx = b.prefix;
+ t->bt_rfd = -1;
+
+ if ((t->bt_dbp = dbp = (DB *)malloc(sizeof(DB))) == NULL)
+ goto err;
+ memset(t->bt_dbp, 0, sizeof(DB));
+ if (t->bt_lorder != machine_lorder)
+ F_SET(t, B_NEEDSWAP);
+
+ dbp->type = DB_BTREE;
+ dbp->internal = t;
+ dbp->close = __bt_close;
+ dbp->del = __bt_delete;
+ dbp->fd = __bt_fd;
+ dbp->get = __bt_get;
+ dbp->put = __bt_put;
+ dbp->seq = __bt_seq;
+ dbp->sync = __bt_sync;
+
+ /*
+ * If no file name was supplied, this is an in-memory btree and we
+ * open a backing temporary file. Otherwise, it's a disk-based tree.
+ */
+ if (fname) {
+ switch (flags & O_ACCMODE) {
+ case O_RDONLY:
+ F_SET(t, B_RDONLY);
+ break;
+ case O_RDWR:
+ break;
+ case O_WRONLY:
+ default:
+ goto einval;
+ }
+
+ if ((t->bt_fd = open(fname, flags, mode)) < 0)
+ goto err;
+
+ } else {
+ if ((flags & O_ACCMODE) != O_RDWR)
+ goto einval;
+ if ((t->bt_fd = tmp()) == -1)
+ goto err;
+ F_SET(t, B_INMEM);
+ }
+
+ if (fcntl(t->bt_fd, F_SETFD, 1) == -1)
+ goto err;
+
+ if (fstat(t->bt_fd, &sb))
+ goto err;
+ if (sb.st_size) {
+ if ((nr = read(t->bt_fd, &m, sizeof(BTMETA))) < 0)
+ goto err;
+ if (nr != sizeof(BTMETA))
+ goto eftype;
+
+ /*
+ * Read in the meta-data. This can change the notion of what
+ * the lorder, page size and flags are, and, when the page size
+ * changes, the cachesize value can change too. If the user
+ * specified the wrong byte order for an existing database, we
+ * don't bother to return an error, we just clear the NEEDSWAP
+ * bit.
+ */
+ if (m.magic == BTREEMAGIC)
+ F_CLR(t, B_NEEDSWAP);
+ else {
+ F_SET(t, B_NEEDSWAP);
+ M_32_SWAP(m.magic);
+ M_32_SWAP(m.version);
+ M_32_SWAP(m.psize);
+ M_32_SWAP(m.free);
+ M_32_SWAP(m.nrecs);
+ M_32_SWAP(m.flags);
+ }
+ if (m.magic != BTREEMAGIC || m.version != BTREEVERSION)
+ goto eftype;
+ if (m.psize < MINPSIZE || m.psize > MAX_PAGE_OFFSET + 1 ||
+ m.psize & (sizeof(indx_t) - 1))
+ goto eftype;
+ if (m.flags & ~SAVEMETA)
+ goto eftype;
+ b.psize = m.psize;
+ F_SET(t, m.flags);
+ t->bt_free = m.free;
+ t->bt_nrecs = m.nrecs;
+ } else {
+ /*
+ * Set the page size to the best value for I/O to this file.
+ * Don't overflow the page offset type.
+ */
+ if (b.psize == 0) {
+#ifdef _STATBUF_ST_BLKSIZE
+ b.psize = sb.st_blksize;
+#endif
+ if (b.psize < MINPSIZE)
+ b.psize = MINPSIZE;
+ if (b.psize > MAX_PAGE_OFFSET + 1)
+ b.psize = MAX_PAGE_OFFSET + 1;
+ }
+
+ /* Set flag if duplicates permitted. */
+ if (!(b.flags & R_DUP))
+ F_SET(t, B_NODUPS);
+
+ t->bt_free = P_INVALID;
+ t->bt_nrecs = 0;
+ F_SET(t, B_METADIRTY);
+ }
+
+ t->bt_psize = b.psize;
+
+ /* Set the cache size; must be a multiple of the page size. */
+ if (b.cachesize && b.cachesize & (b.psize - 1))
+ b.cachesize += (~b.cachesize & (b.psize - 1)) + 1;
+ if (b.cachesize < b.psize * MINCACHE)
+ b.cachesize = b.psize * MINCACHE;
+
+ /* Calculate number of pages to cache. */
+ ncache = (b.cachesize + t->bt_psize - 1) / t->bt_psize;
+
+ /*
+ * The btree data structure requires that at least two keys can fit on
+ * a page, but other than that there's no fixed requirement. The user
+ * specified a minimum number per page, and we translated that into the
+ * number of bytes a key/data pair can use before being placed on an
+ * overflow page. This calculation includes the page header, the size
+ * of the index referencing the leaf item and the size of the leaf item
+ * structure. Also, don't let the user specify a minkeypage such that
+ * a key/data pair won't fit even if both key and data are on overflow
+ * pages.
+ */
+ t->bt_ovflsize = (t->bt_psize - BTDATAOFF) / b.minkeypage -
+ (sizeof(indx_t) + NBLEAFDBT(0, 0));
+ if (t->bt_ovflsize < NBLEAFDBT(NOVFLSIZE, NOVFLSIZE) + sizeof(indx_t))
+ t->bt_ovflsize =
+ NBLEAFDBT(NOVFLSIZE, NOVFLSIZE) + sizeof(indx_t);
+
+ /* Initialize the buffer pool. */
+ if ((t->bt_mp =
+ mpool_open(NULL, t->bt_fd, t->bt_psize, ncache)) == NULL)
+ goto err;
+ if (!F_ISSET(t, B_INMEM))
+ mpool_filter(t->bt_mp, __bt_pgin, __bt_pgout, t);
+
+ /* Create a root page if new tree. */
+ if (nroot(t) == RET_ERROR)
+ goto err;
+
+ /* Global flags. */
+ if (dflags & DB_LOCK)
+ F_SET(t, B_DB_LOCK);
+ if (dflags & DB_SHMEM)
+ F_SET(t, B_DB_SHMEM);
+ if (dflags & DB_TXN)
+ F_SET(t, B_DB_TXN);
+
+ return (dbp);
+
+einval: errno = EINVAL;
+ goto err;
+
+eftype: errno = EFTYPE;
+ goto err;
+
+err: if (t) {
+ if (t->bt_dbp)
+ free(t->bt_dbp);
+ if (t->bt_fd != -1)
+ (void)close(t->bt_fd);
+ free(t);
+ }
+ return (NULL);
+}
+
+/*
+ * NROOT -- Create the root of a new tree.
+ *
+ * Parameters:
+ * t: tree
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS
+ */
+static int
+nroot(t)
+ BTREE *t;
+{
+ PAGE *meta, *root;
+ pgno_t npg;
+
+ if ((meta = mpool_get(t->bt_mp, 0, 0)) != NULL) {
+ mpool_put(t->bt_mp, meta, 0);
+ return (RET_SUCCESS);
+ }
+ if (errno != EINVAL) /* It's OK to not exist. */
+ return (RET_ERROR);
+ errno = 0;
+
+ if ((meta = mpool_new(t->bt_mp, &npg)) == NULL)
+ return (RET_ERROR);
+
+ if ((root = mpool_new(t->bt_mp, &npg)) == NULL)
+ return (RET_ERROR);
+
+ if (npg != P_ROOT)
+ return (RET_ERROR);
+ root->pgno = npg;
+ root->prevpg = root->nextpg = P_INVALID;
+ root->lower = BTDATAOFF;
+ root->upper = t->bt_psize;
+ root->flags = P_BLEAF;
+ memset(meta, 0, t->bt_psize);
+ mpool_put(t->bt_mp, meta, MPOOL_DIRTY);
+ mpool_put(t->bt_mp, root, MPOOL_DIRTY);
+ return (RET_SUCCESS);
+}
+
+static int
+tmp()
+{
+ sigset_t set, oset;
+ int fd;
+ const char *envtmp;
+ char *path;
+ static const char fmt[] = "%s/bt.XXXXXX";
+ size_t n;
+
+ envtmp = getenv("TMPDIR");
+ if (!envtmp)
+ envtmp = "/tmp";
+ n = strlen (envtmp) + sizeof fmt;
+#ifdef __GNUC__
+ path = __builtin_alloca(n);
+#else
+ path = malloc(n);
+#endif
+ (void)snprintf(path, n, fmt, envtmp ? envtmp : "/tmp");
+
+ (void)sigfillset(&set);
+ (void)sigprocmask(SIG_BLOCK, &set, &oset);
+ if ((fd = mkstemp(path)) != -1)
+ (void)unlink(path);
+ (void)sigprocmask(SIG_SETMASK, &oset, NULL);
+#ifndef __GNUC__
+ free(path);
+#endif
+ return(fd);
+}
+
+static int
+byteorder()
+{
+ u_int32_t x;
+ u_char *p;
+
+ x = 0x01020304;
+ p = (u_char *)&x;
+ switch (*p) {
+ case 1:
+ return (BIG_ENDIAN);
+ case 4:
+ return (LITTLE_ENDIAN);
+ default:
+ return (0);
+ }
+}
+
+int
+__bt_fd(dbp)
+ const DB *dbp;
+{
+ BTREE *t;
+
+ t = dbp->internal;
+
+ /* Toss any page pinned across calls. */
+ if (t->bt_pinned != NULL) {
+ mpool_put(t->bt_mp, t->bt_pinned, 0);
+ t->bt_pinned = NULL;
+ }
+
+ /* In-memory database can't have a file descriptor. */
+ if (F_ISSET(t, B_INMEM)) {
+ errno = ENOENT;
+ return (-1);
+ }
+ return (t->bt_fd);
+}
diff --git a/trunk/main/db1-ast/btree/bt_overflow.c b/trunk/main/db1-ast/btree/bt_overflow.c
new file mode 100644
index 000000000..d8f310d91
--- /dev/null
+++ b/trunk/main/db1-ast/btree/bt_overflow.c
@@ -0,0 +1,228 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Olson.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)bt_overflow.c 8.5 (Berkeley) 7/16/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/param.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../include/db.h"
+#include "btree.h"
+
+/*
+ * Big key/data code.
+ *
+ * Big key and data entries are stored on linked lists of pages. The initial
+ * reference is byte string stored with the key or data and is the page number
+ * and size. The actual record is stored in a chain of pages linked by the
+ * nextpg field of the PAGE header.
+ *
+ * The first page of the chain has a special property. If the record is used
+ * by an internal page, it cannot be deleted and the P_PRESERVE bit will be set
+ * in the header.
+ *
+ * XXX
+ * A single DBT is written to each chain, so a lot of space on the last page
+ * is wasted. This is a fairly major bug for some data sets.
+ */
+
+/*
+ * __OVFL_GET -- Get an overflow key/data item.
+ *
+ * Parameters:
+ * t: tree
+ * p: pointer to { pgno_t, u_int32_t }
+ * buf: storage address
+ * bufsz: storage size
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS
+ */
+int
+__ovfl_get(t, p, ssz, buf, bufsz)
+ BTREE *t;
+ void *p;
+ size_t *ssz;
+ void **buf;
+ size_t *bufsz;
+{
+ PAGE *h;
+ pgno_t pg;
+ size_t nb, plen;
+ u_int32_t sz;
+
+ memmove(&pg, p, sizeof(pgno_t));
+ memmove(&sz, (char *)p + sizeof(pgno_t), sizeof(u_int32_t));
+ *ssz = sz;
+
+#ifdef DEBUG
+ if (pg == P_INVALID || sz == 0)
+ abort();
+#endif
+ /* Make the buffer bigger as necessary. */
+ if (*bufsz < sz) {
+ *buf = (char *)(*buf == NULL ? malloc(sz) : realloc(*buf, sz));
+ if (*buf == NULL)
+ return (RET_ERROR);
+ *bufsz = sz;
+ }
+
+ /*
+ * Step through the linked list of pages, copying the data on each one
+ * into the buffer. Never copy more than the data's length.
+ */
+ plen = t->bt_psize - BTDATAOFF;
+ for (p = *buf;; p = (char *)p + nb, pg = h->nextpg) {
+ if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
+ return (RET_ERROR);
+
+ nb = MIN(sz, plen);
+ memmove(p, (char *)h + BTDATAOFF, nb);
+ mpool_put(t->bt_mp, h, 0);
+
+ if ((sz -= nb) == 0)
+ break;
+ }
+ return (RET_SUCCESS);
+}
+
+/*
+ * __OVFL_PUT -- Store an overflow key/data item.
+ *
+ * Parameters:
+ * t: tree
+ * data: DBT to store
+ * pgno: storage page number
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS
+ */
+int
+__ovfl_put(t, dbt, pg)
+ BTREE *t;
+ const DBT *dbt;
+ pgno_t *pg;
+{
+ PAGE *h, *last;
+ void *p;
+ pgno_t npg;
+ size_t nb, plen;
+ u_int32_t sz;
+
+ /*
+ * Allocate pages and copy the key/data record into them. Store the
+ * number of the first page in the chain.
+ */
+ plen = t->bt_psize - BTDATAOFF;
+ for (last = NULL, p = dbt->data, sz = dbt->size;;
+ p = (char *)p + plen, last = h) {
+ if ((h = __bt_new(t, &npg)) == NULL)
+ return (RET_ERROR);
+
+ h->pgno = npg;
+ h->nextpg = h->prevpg = P_INVALID;
+ h->flags = P_OVERFLOW;
+ h->lower = h->upper = 0;
+
+ nb = MIN(sz, plen);
+ memmove((char *)h + BTDATAOFF, p, nb);
+
+ if (last) {
+ last->nextpg = h->pgno;
+ mpool_put(t->bt_mp, last, MPOOL_DIRTY);
+ } else
+ *pg = h->pgno;
+
+ if ((sz -= nb) == 0) {
+ mpool_put(t->bt_mp, h, MPOOL_DIRTY);
+ break;
+ }
+ }
+ return (RET_SUCCESS);
+}
+
+/*
+ * __OVFL_DELETE -- Delete an overflow chain.
+ *
+ * Parameters:
+ * t: tree
+ * p: pointer to { pgno_t, u_int32_t }
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS
+ */
+int
+__ovfl_delete(t, p)
+ BTREE *t;
+ void *p;
+{
+ PAGE *h;
+ pgno_t pg;
+ size_t plen;
+ u_int32_t sz;
+
+ memmove(&pg, p, sizeof(pgno_t));
+ memmove(&sz, (char *)p + sizeof(pgno_t), sizeof(u_int32_t));
+
+#ifdef DEBUG
+ if (pg == P_INVALID || sz == 0)
+ abort();
+#endif
+ if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
+ return (RET_ERROR);
+
+ /* Don't delete chains used by internal pages. */
+ if (h->flags & P_PRESERVE) {
+ mpool_put(t->bt_mp, h, 0);
+ return (RET_SUCCESS);
+ }
+
+ /* Step through the chain, calling the free routine for each page. */
+ for (plen = t->bt_psize - BTDATAOFF;; sz -= plen) {
+ pg = h->nextpg;
+ __bt_free(t, h);
+ if (sz <= plen)
+ break;
+ if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
+ return (RET_ERROR);
+ }
+ return (RET_SUCCESS);
+}
diff --git a/trunk/main/db1-ast/btree/bt_page.c b/trunk/main/db1-ast/btree/bt_page.c
new file mode 100644
index 000000000..e77a1d6b5
--- /dev/null
+++ b/trunk/main/db1-ast/btree/bt_page.c
@@ -0,0 +1,100 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)bt_page.c 8.3 (Berkeley) 7/14/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+
+#include <stdio.h>
+
+#include "../include/db.h"
+#include "btree.h"
+
+/*
+ * __bt_free --
+ * Put a page on the freelist.
+ *
+ * Parameters:
+ * t: tree
+ * h: page to free
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS
+ *
+ * Side-effect:
+ * mpool_put's the page.
+ */
+int
+__bt_free(t, h)
+ BTREE *t;
+ PAGE *h;
+{
+ /* Insert the page at the head of the free list. */
+ h->prevpg = P_INVALID;
+ h->nextpg = t->bt_free;
+ t->bt_free = h->pgno;
+ F_SET(t, B_METADIRTY);
+
+ /* Make sure the page gets written back. */
+ return (mpool_put(t->bt_mp, h, MPOOL_DIRTY));
+}
+
+/*
+ * __bt_new --
+ * Get a new page, preferably from the freelist.
+ *
+ * Parameters:
+ * t: tree
+ * npg: storage for page number.
+ *
+ * Returns:
+ * Pointer to a page, NULL on error.
+ */
+PAGE *
+__bt_new(t, npg)
+ BTREE *t;
+ pgno_t *npg;
+{
+ PAGE *h;
+
+ if (t->bt_free != P_INVALID &&
+ (h = mpool_get(t->bt_mp, t->bt_free, 0)) != NULL) {
+ *npg = t->bt_free;
+ t->bt_free = h->nextpg;
+ F_SET(t, B_METADIRTY);
+ return (h);
+ }
+ return (mpool_new(t->bt_mp, npg));
+}
diff --git a/trunk/main/db1-ast/btree/bt_put.c b/trunk/main/db1-ast/btree/bt_put.c
new file mode 100644
index 000000000..aeb0bb16c
--- /dev/null
+++ b/trunk/main/db1-ast/btree/bt_put.c
@@ -0,0 +1,321 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Olson.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)bt_put.c 8.8 (Berkeley) 7/26/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../include/db.h"
+#include "btree.h"
+
+static EPG *bt_fast __P((BTREE *, const DBT *, const DBT *, int *));
+
+/*
+ * __BT_PUT -- Add a btree item to the tree.
+ *
+ * Parameters:
+ * dbp: pointer to access method
+ * key: key
+ * data: data
+ * flag: R_NOOVERWRITE
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS and RET_SPECIAL if the key is already in the
+ * tree and R_NOOVERWRITE specified.
+ */
+int
+__bt_put(dbp, key, data, flags)
+ const DB *dbp;
+ DBT *key;
+ const DBT *data;
+ u_int flags;
+{
+ BTREE *t;
+ DBT tkey, tdata;
+ EPG *e = 0;
+ PAGE *h;
+ indx_t index, nxtindex;
+ pgno_t pg;
+ u_int32_t nbytes;
+ int dflags, exact, status;
+ char *dest, db[NOVFLSIZE], kb[NOVFLSIZE];
+
+ t = dbp->internal;
+
+ /* Toss any page pinned across calls. */
+ if (t->bt_pinned != NULL) {
+ mpool_put(t->bt_mp, t->bt_pinned, 0);
+ t->bt_pinned = NULL;
+ }
+
+ /* Check for change to a read-only tree. */
+ if (F_ISSET(t, B_RDONLY)) {
+ errno = EPERM;
+ return (RET_ERROR);
+ }
+
+ switch (flags) {
+ case 0:
+ case R_NOOVERWRITE:
+ break;
+ case R_CURSOR:
+ /*
+ * If flags is R_CURSOR, put the cursor. Must already
+ * have started a scan and not have already deleted it.
+ */
+ if (F_ISSET(&t->bt_cursor, CURS_INIT) &&
+ !F_ISSET(&t->bt_cursor,
+ CURS_ACQUIRE | CURS_AFTER | CURS_BEFORE))
+ break;
+ /* FALLTHROUGH */
+ default:
+ errno = EINVAL;
+ return (RET_ERROR);
+ }
+
+ /*
+ * If the key/data pair won't fit on a page, store it on overflow
+ * pages. Only put the key on the overflow page if the pair are
+ * still too big after moving the data to an overflow page.
+ *
+ * XXX
+ * If the insert fails later on, the overflow pages aren't recovered.
+ */
+ dflags = 0;
+ if (key->size + data->size > t->bt_ovflsize) {
+ if (key->size > t->bt_ovflsize) {
+storekey: if (__ovfl_put(t, key, &pg) == RET_ERROR)
+ return (RET_ERROR);
+ tkey.data = kb;
+ tkey.size = NOVFLSIZE;
+ memmove(kb, &pg, sizeof(pgno_t));
+ memmove(kb + sizeof(pgno_t),
+ &key->size, sizeof(u_int32_t));
+ dflags |= P_BIGKEY;
+ key = &tkey;
+ }
+ if (key->size + data->size > t->bt_ovflsize) {
+ if (__ovfl_put(t, data, &pg) == RET_ERROR)
+ return (RET_ERROR);
+ tdata.data = db;
+ tdata.size = NOVFLSIZE;
+ memmove(db, &pg, sizeof(pgno_t));
+ memmove(db + sizeof(pgno_t),
+ &data->size, sizeof(u_int32_t));
+ dflags |= P_BIGDATA;
+ data = &tdata;
+ }
+ if (key->size + data->size > t->bt_ovflsize)
+ goto storekey;
+ }
+
+ /* Replace the cursor. */
+ if (flags == R_CURSOR) {
+ if ((h = mpool_get(t->bt_mp, t->bt_cursor.pg.pgno, 0)) == NULL)
+ return (RET_ERROR);
+ index = t->bt_cursor.pg.index;
+ goto delete;
+ }
+
+ /*
+ * Find the key to delete, or, the location at which to insert.
+ * Bt_fast and __bt_search both pin the returned page.
+ */
+ if (t->bt_order == NOT || (e = bt_fast(t, key, data, &exact)) == NULL)
+ if ((e = __bt_search(t, key, &exact)) == NULL)
+ return (RET_ERROR);
+ h = e->page;
+ index = e->index;
+
+ /*
+ * Add the key/data pair to the tree. If an identical key is already
+ * in the tree, and R_NOOVERWRITE is set, an error is returned. If
+ * R_NOOVERWRITE is not set, the key is either added (if duplicates are
+ * permitted) or an error is returned.
+ */
+ switch (flags) {
+ case R_NOOVERWRITE:
+ if (!exact)
+ break;
+ mpool_put(t->bt_mp, h, 0);
+ return (RET_SPECIAL);
+ default:
+ if (!exact || !F_ISSET(t, B_NODUPS))
+ break;
+ /*
+ * !!!
+ * Note, the delete may empty the page, so we need to put a
+ * new entry into the page immediately.
+ */
+delete: if (__bt_dleaf(t, key, h, index) == RET_ERROR) {
+ mpool_put(t->bt_mp, h, 0);
+ return (RET_ERROR);
+ }
+ break;
+ }
+
+ /*
+ * If not enough room, or the user has put a ceiling on the number of
+ * keys permitted in the page, split the page. The split code will
+ * insert the key and data and unpin the current page. If inserting
+ * into the offset array, shift the pointers up.
+ */
+ nbytes = NBLEAFDBT(key->size, data->size);
+ if ((u_int32_t) (h->upper - h->lower) < nbytes + sizeof(indx_t)) {
+ if ((status = __bt_split(t, h, key,
+ data, dflags, nbytes, index)) != RET_SUCCESS)
+ return (status);
+ goto success;
+ }
+
+ if (index < (nxtindex = NEXTINDEX(h)))
+ memmove(h->linp + index + 1, h->linp + index,
+ (nxtindex - index) * sizeof(indx_t));
+ h->lower += sizeof(indx_t);
+
+ h->linp[index] = h->upper -= nbytes;
+ dest = (char *)h + h->upper;
+ WR_BLEAF(dest, key, data, dflags);
+
+ /* If the cursor is on this page, adjust it as necessary. */
+ if (F_ISSET(&t->bt_cursor, CURS_INIT) &&
+ !F_ISSET(&t->bt_cursor, CURS_ACQUIRE) &&
+ t->bt_cursor.pg.pgno == h->pgno && t->bt_cursor.pg.index >= index)
+ ++t->bt_cursor.pg.index;
+
+ if (t->bt_order == NOT) {
+ if (h->nextpg == P_INVALID) {
+ if (index == NEXTINDEX(h) - 1) {
+ t->bt_order = FORWARD;
+ t->bt_last.index = index;
+ t->bt_last.pgno = h->pgno;
+ }
+ } else if (h->prevpg == P_INVALID) {
+ if (index == 0) {
+ t->bt_order = BACK;
+ t->bt_last.index = 0;
+ t->bt_last.pgno = h->pgno;
+ }
+ }
+ }
+
+ mpool_put(t->bt_mp, h, MPOOL_DIRTY);
+
+success:
+ if (flags == R_SETCURSOR)
+ __bt_setcur(t, e->page->pgno, e->index);
+
+ F_SET(t, B_MODIFIED);
+ return (RET_SUCCESS);
+}
+
+#ifdef STATISTICS
+u_long bt_cache_hit, bt_cache_miss;
+#endif
+
+/*
+ * BT_FAST -- Do a quick check for sorted data.
+ *
+ * Parameters:
+ * t: tree
+ * key: key to insert
+ *
+ * Returns:
+ * EPG for new record or NULL if not found.
+ */
+static EPG *
+bt_fast(t, key, data, exactp)
+ BTREE *t;
+ const DBT *key, *data;
+ int *exactp;
+{
+ PAGE *h;
+ u_int32_t nbytes;
+ int cmp;
+
+ if ((h = mpool_get(t->bt_mp, t->bt_last.pgno, 0)) == NULL) {
+ t->bt_order = NOT;
+ return (NULL);
+ }
+ t->bt_cur.page = h;
+ t->bt_cur.index = t->bt_last.index;
+
+ /*
+ * If won't fit in this page or have too many keys in this page,
+ * have to search to get split stack.
+ */
+ nbytes = NBLEAFDBT(key->size, data->size);
+ if ((u_int32_t) (h->upper - h->lower) < nbytes + sizeof(indx_t))
+ goto miss;
+
+ if (t->bt_order == FORWARD) {
+ if (t->bt_cur.page->nextpg != P_INVALID)
+ goto miss;
+ if (t->bt_cur.index != NEXTINDEX(h) - 1)
+ goto miss;
+ if ((cmp = __bt_cmp(t, key, &t->bt_cur)) < 0)
+ goto miss;
+ t->bt_last.index = cmp ? ++t->bt_cur.index : t->bt_cur.index;
+ } else {
+ if (t->bt_cur.page->prevpg != P_INVALID)
+ goto miss;
+ if (t->bt_cur.index != 0)
+ goto miss;
+ if ((cmp = __bt_cmp(t, key, &t->bt_cur)) > 0)
+ goto miss;
+ t->bt_last.index = 0;
+ }
+ *exactp = cmp == 0;
+#ifdef STATISTICS
+ ++bt_cache_hit;
+#endif
+ return (&t->bt_cur);
+
+miss:
+#ifdef STATISTICS
+ ++bt_cache_miss;
+#endif
+ t->bt_order = NOT;
+ mpool_put(t->bt_mp, h, 0);
+ return (NULL);
+}
diff --git a/trunk/main/db1-ast/btree/bt_search.c b/trunk/main/db1-ast/btree/bt_search.c
new file mode 100644
index 000000000..623f43949
--- /dev/null
+++ b/trunk/main/db1-ast/btree/bt_search.c
@@ -0,0 +1,213 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Olson.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)bt_search.c 8.8 (Berkeley) 7/31/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+
+#include <stdio.h>
+
+#include "../include/db.h"
+#include "btree.h"
+
+static int __bt_snext __P((BTREE *, PAGE *, const DBT *, int *));
+static int __bt_sprev __P((BTREE *, PAGE *, const DBT *, int *));
+
+/*
+ * __bt_search --
+ * Search a btree for a key.
+ *
+ * Parameters:
+ * t: tree to search
+ * key: key to find
+ * exactp: pointer to exact match flag
+ *
+ * Returns:
+ * The EPG for matching record, if any, or the EPG for the location
+ * of the key, if it were inserted into the tree, is entered into
+ * the bt_cur field of the tree. A pointer to the field is returned.
+ */
+EPG *
+__bt_search(t, key, exactp)
+ BTREE *t;
+ const DBT *key;
+ int *exactp;
+{
+ PAGE *h;
+ indx_t base, index, lim;
+ pgno_t pg;
+ int cmp;
+
+ BT_CLR(t);
+ for (pg = P_ROOT;;) {
+ if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
+ return (NULL);
+
+ /* Do a binary search on the current page. */
+ t->bt_cur.page = h;
+ for (base = 0, lim = NEXTINDEX(h); lim; lim >>= 1) {
+ t->bt_cur.index = index = base + (lim >> 1);
+ if ((cmp = __bt_cmp(t, key, &t->bt_cur)) == 0) {
+ if (h->flags & P_BLEAF) {
+ *exactp = 1;
+ return (&t->bt_cur);
+ }
+ goto next;
+ }
+ if (cmp > 0) {
+ base = index + 1;
+ --lim;
+ }
+ }
+
+ /*
+ * If it's a leaf page, we're almost done. If no duplicates
+ * are allowed, or we have an exact match, we're done. Else,
+ * it's possible that there were matching keys on this page,
+ * which later deleted, and we're on a page with no matches
+ * while there are matches on other pages. If at the start or
+ * end of a page, check the adjacent page.
+ */
+ if (h->flags & P_BLEAF) {
+ if (!F_ISSET(t, B_NODUPS)) {
+ if (base == 0 &&
+ h->prevpg != P_INVALID &&
+ __bt_sprev(t, h, key, exactp))
+ return (&t->bt_cur);
+ if (base == NEXTINDEX(h) &&
+ h->nextpg != P_INVALID &&
+ __bt_snext(t, h, key, exactp))
+ return (&t->bt_cur);
+ }
+ *exactp = 0;
+ t->bt_cur.index = base;
+ return (&t->bt_cur);
+ }
+
+ /*
+ * No match found. Base is the smallest index greater than
+ * key and may be zero or a last + 1 index. If it's non-zero,
+ * decrement by one, and record the internal page which should
+ * be a parent page for the key. If a split later occurs, the
+ * inserted page will be to the right of the saved page.
+ */
+ index = base ? base - 1 : base;
+
+next: BT_PUSH(t, h->pgno, index);
+ pg = GETBINTERNAL(h, index)->pgno;
+ mpool_put(t->bt_mp, h, 0);
+ }
+}
+
+/*
+ * __bt_snext --
+ * Check for an exact match after the key.
+ *
+ * Parameters:
+ * t: tree
+ * h: current page
+ * key: key
+ * exactp: pointer to exact match flag
+ *
+ * Returns:
+ * If an exact match found.
+ */
+static int
+__bt_snext(t, h, key, exactp)
+ BTREE *t;
+ PAGE *h;
+ const DBT *key;
+ int *exactp;
+{
+ EPG e;
+
+ /*
+ * Get the next page. The key is either an exact
+ * match, or not as good as the one we already have.
+ */
+ if ((e.page = mpool_get(t->bt_mp, h->nextpg, 0)) == NULL)
+ return (0);
+ e.index = 0;
+ if (__bt_cmp(t, key, &e) == 0) {
+ mpool_put(t->bt_mp, h, 0);
+ t->bt_cur = e;
+ *exactp = 1;
+ return (1);
+ }
+ mpool_put(t->bt_mp, e.page, 0);
+ return (0);
+}
+
+/*
+ * __bt_sprev --
+ * Check for an exact match before the key.
+ *
+ * Parameters:
+ * t: tree
+ * h: current page
+ * key: key
+ * exactp: pointer to exact match flag
+ *
+ * Returns:
+ * If an exact match found.
+ */
+static int
+__bt_sprev(t, h, key, exactp)
+ BTREE *t;
+ PAGE *h;
+ const DBT *key;
+ int *exactp;
+{
+ EPG e;
+
+ /*
+ * Get the previous page. The key is either an exact
+ * match, or not as good as the one we already have.
+ */
+ if ((e.page = mpool_get(t->bt_mp, h->prevpg, 0)) == NULL)
+ return (0);
+ e.index = NEXTINDEX(e.page) - 1;
+ if (__bt_cmp(t, key, &e) == 0) {
+ mpool_put(t->bt_mp, h, 0);
+ t->bt_cur = e;
+ *exactp = 1;
+ return (1);
+ }
+ mpool_put(t->bt_mp, e.page, 0);
+ return (0);
+}
diff --git a/trunk/main/db1-ast/btree/bt_seq.c b/trunk/main/db1-ast/btree/bt_seq.c
new file mode 100644
index 000000000..3f1724274
--- /dev/null
+++ b/trunk/main/db1-ast/btree/bt_seq.c
@@ -0,0 +1,460 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Olson.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)bt_seq.c 8.7 (Berkeley) 7/20/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "../include/db.h"
+#include "btree.h"
+
+static int __bt_first __P((BTREE *, const DBT *, EPG *, int *));
+static int __bt_seqadv __P((BTREE *, EPG *, int));
+static int __bt_seqset __P((BTREE *, EPG *, DBT *, int));
+
+/*
+ * Sequential scan support.
+ *
+ * The tree can be scanned sequentially, starting from either end of the
+ * tree or from any specific key. A scan request before any scanning is
+ * done is initialized as starting from the least node.
+ */
+
+/*
+ * __bt_seq --
+ * Btree sequential scan interface.
+ *
+ * Parameters:
+ * dbp: pointer to access method
+ * key: key for positioning and return value
+ * data: data return value
+ * flags: R_CURSOR, R_FIRST, R_LAST, R_NEXT, R_PREV.
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS or RET_SPECIAL if there's no next key.
+ */
+int
+__bt_seq(dbp, key, data, flags)
+ const DB *dbp;
+ DBT *key, *data;
+ u_int flags;
+{
+ BTREE *t;
+ EPG e;
+ int status;
+
+ t = dbp->internal;
+
+ /* Toss any page pinned across calls. */
+ if (t->bt_pinned != NULL) {
+ mpool_put(t->bt_mp, t->bt_pinned, 0);
+ t->bt_pinned = NULL;
+ }
+
+ /*
+ * If scan uninitialized as yet, or starting at a specific record, set
+ * the scan to a specific key. Both __bt_seqset and __bt_seqadv pin
+ * the page the cursor references if they're successful.
+ */
+ switch (flags) {
+ case R_NEXT:
+ case R_PREV:
+ if (F_ISSET(&t->bt_cursor, CURS_INIT)) {
+ status = __bt_seqadv(t, &e, flags);
+ break;
+ }
+ /* FALLTHROUGH */
+ case R_FIRST:
+ case R_LAST:
+ case R_CURSOR:
+ status = __bt_seqset(t, &e, key, flags);
+ break;
+ default:
+ errno = EINVAL;
+ return (RET_ERROR);
+ }
+
+ if (status == RET_SUCCESS) {
+ __bt_setcur(t, e.page->pgno, e.index);
+
+ status =
+ __bt_ret(t, &e, key, &t->bt_rkey, data, &t->bt_rdata, 0);
+
+ /*
+ * If the user is doing concurrent access, we copied the
+ * key/data, toss the page.
+ */
+ if (F_ISSET(t, B_DB_LOCK))
+ mpool_put(t->bt_mp, e.page, 0);
+ else
+ t->bt_pinned = e.page;
+ }
+ return (status);
+}
+
+/*
+ * __bt_seqset --
+ * Set the sequential scan to a specific key.
+ *
+ * Parameters:
+ * t: tree
+ * ep: storage for returned key
+ * key: key for initial scan position
+ * flags: R_CURSOR, R_FIRST, R_LAST, R_NEXT, R_PREV
+ *
+ * Side effects:
+ * Pins the page the cursor references.
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS or RET_SPECIAL if there's no next key.
+ */
+static int
+__bt_seqset(t, ep, key, flags)
+ BTREE *t;
+ EPG *ep;
+ DBT *key;
+ int flags;
+{
+ PAGE *h;
+ pgno_t pg;
+ int exact;
+
+ /*
+ * Find the first, last or specific key in the tree and point the
+ * cursor at it. The cursor may not be moved until a new key has
+ * been found.
+ */
+ switch (flags) {
+ case R_CURSOR: /* Keyed scan. */
+ /*
+ * Find the first instance of the key or the smallest key
+ * which is greater than or equal to the specified key.
+ */
+ if (key->data == NULL || key->size == 0) {
+ errno = EINVAL;
+ return (RET_ERROR);
+ }
+ return (__bt_first(t, key, ep, &exact));
+ case R_FIRST: /* First record. */
+ case R_NEXT:
+ /* Walk down the left-hand side of the tree. */
+ for (pg = P_ROOT;;) {
+ if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
+ return (RET_ERROR);
+
+ /* Check for an empty tree. */
+ if (NEXTINDEX(h) == 0) {
+ mpool_put(t->bt_mp, h, 0);
+ return (RET_SPECIAL);
+ }
+
+ if (h->flags & (P_BLEAF | P_RLEAF))
+ break;
+ pg = GETBINTERNAL(h, 0)->pgno;
+ mpool_put(t->bt_mp, h, 0);
+ }
+ ep->page = h;
+ ep->index = 0;
+ break;
+ case R_LAST: /* Last record. */
+ case R_PREV:
+ /* Walk down the right-hand side of the tree. */
+ for (pg = P_ROOT;;) {
+ if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
+ return (RET_ERROR);
+
+ /* Check for an empty tree. */
+ if (NEXTINDEX(h) == 0) {
+ mpool_put(t->bt_mp, h, 0);
+ return (RET_SPECIAL);
+ }
+
+ if (h->flags & (P_BLEAF | P_RLEAF))
+ break;
+ pg = GETBINTERNAL(h, NEXTINDEX(h) - 1)->pgno;
+ mpool_put(t->bt_mp, h, 0);
+ }
+
+ ep->page = h;
+ ep->index = NEXTINDEX(h) - 1;
+ break;
+ }
+ return (RET_SUCCESS);
+}
+
+/*
+ * __bt_seqadvance --
+ * Advance the sequential scan.
+ *
+ * Parameters:
+ * t: tree
+ * flags: R_NEXT, R_PREV
+ *
+ * Side effects:
+ * Pins the page the new key/data record is on.
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS or RET_SPECIAL if there's no next key.
+ */
+static int
+__bt_seqadv(t, ep, flags)
+ BTREE *t;
+ EPG *ep;
+ int flags;
+{
+ CURSOR *c;
+ PAGE *h;
+ indx_t index = 0;
+ pgno_t pg;
+ int exact;
+
+ /*
+ * There are a couple of states that we can be in. The cursor has
+ * been initialized by the time we get here, but that's all we know.
+ */
+ c = &t->bt_cursor;
+
+ /*
+ * The cursor was deleted where there weren't any duplicate records,
+ * so the key was saved. Find out where that key would go in the
+ * current tree. It doesn't matter if the returned key is an exact
+ * match or not -- if it's an exact match, the record was added after
+ * the delete so we can just return it. If not, as long as there's
+ * a record there, return it.
+ */
+ if (F_ISSET(c, CURS_ACQUIRE))
+ return (__bt_first(t, &c->key, ep, &exact));
+
+ /* Get the page referenced by the cursor. */
+ if ((h = mpool_get(t->bt_mp, c->pg.pgno, 0)) == NULL)
+ return (RET_ERROR);
+
+ /*
+ * Find the next/previous record in the tree and point the cursor at
+ * it. The cursor may not be moved until a new key has been found.
+ */
+ switch (flags) {
+ case R_NEXT: /* Next record. */
+ /*
+ * The cursor was deleted in duplicate records, and moved
+ * forward to a record that has yet to be returned. Clear
+ * that flag, and return the record.
+ */
+ if (F_ISSET(c, CURS_AFTER))
+ goto usecurrent;
+ index = c->pg.index;
+ if (++index == NEXTINDEX(h)) {
+ pg = h->nextpg;
+ mpool_put(t->bt_mp, h, 0);
+ if (pg == P_INVALID)
+ return (RET_SPECIAL);
+ if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
+ return (RET_ERROR);
+ index = 0;
+ }
+ break;
+ case R_PREV: /* Previous record. */
+ /*
+ * The cursor was deleted in duplicate records, and moved
+ * backward to a record that has yet to be returned. Clear
+ * that flag, and return the record.
+ */
+ if (F_ISSET(c, CURS_BEFORE)) {
+usecurrent: F_CLR(c, CURS_AFTER | CURS_BEFORE);
+ ep->page = h;
+ ep->index = c->pg.index;
+ return (RET_SUCCESS);
+ }
+ index = c->pg.index;
+ if (index == 0) {
+ pg = h->prevpg;
+ mpool_put(t->bt_mp, h, 0);
+ if (pg == P_INVALID)
+ return (RET_SPECIAL);
+ if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
+ return (RET_ERROR);
+ index = NEXTINDEX(h) - 1;
+ } else
+ --index;
+ break;
+ }
+
+ ep->page = h;
+ ep->index = index;
+ return (RET_SUCCESS);
+}
+
+/*
+ * __bt_first --
+ * Find the first entry.
+ *
+ * Parameters:
+ * t: the tree
+ * key: the key
+ * erval: return EPG
+ * exactp: pointer to exact match flag
+ *
+ * Returns:
+ * The first entry in the tree greater than or equal to key,
+ * or RET_SPECIAL if no such key exists.
+ */
+static int
+__bt_first(t, key, erval, exactp)
+ BTREE *t;
+ const DBT *key;
+ EPG *erval;
+ int *exactp;
+{
+ PAGE *h;
+ EPG *ep, save;
+ pgno_t pg;
+
+ /*
+ * Find any matching record; __bt_search pins the page.
+ *
+ * If it's an exact match and duplicates are possible, walk backwards
+ * in the tree until we find the first one. Otherwise, make sure it's
+ * a valid key (__bt_search may return an index just past the end of a
+ * page) and return it.
+ */
+ if ((ep = __bt_search(t, key, exactp)) == NULL)
+ return (RET_SPECIAL);
+ if (*exactp) {
+ if (F_ISSET(t, B_NODUPS)) {
+ *erval = *ep;
+ return (RET_SUCCESS);
+ }
+
+ /*
+ * Walk backwards, as long as the entry matches and there are
+ * keys left in the tree. Save a copy of each match in case
+ * we go too far.
+ */
+ save = *ep;
+ h = ep->page;
+ do {
+ if (save.page->pgno != ep->page->pgno) {
+ mpool_put(t->bt_mp, save.page, 0);
+ save = *ep;
+ } else
+ save.index = ep->index;
+
+ /*
+ * Don't unpin the page the last (or original) match
+ * was on, but make sure it's unpinned if an error
+ * occurs.
+ */
+ if (ep->index == 0) {
+ if (h->prevpg == P_INVALID)
+ break;
+ if (h->pgno != save.page->pgno)
+ mpool_put(t->bt_mp, h, 0);
+ if ((h = mpool_get(t->bt_mp,
+ h->prevpg, 0)) == NULL) {
+ if (h->pgno == save.page->pgno)
+ mpool_put(t->bt_mp,
+ save.page, 0);
+ return (RET_ERROR);
+ }
+ ep->page = h;
+ ep->index = NEXTINDEX(h);
+ }
+ --ep->index;
+ } while (__bt_cmp(t, key, ep) == 0);
+
+ /*
+ * Reach here with the last page that was looked at pinned,
+ * which may or may not be the same as the last (or original)
+ * match page. If it's not useful, release it.
+ */
+ if (h->pgno != save.page->pgno)
+ mpool_put(t->bt_mp, h, 0);
+
+ *erval = save;
+ return (RET_SUCCESS);
+ }
+
+ /* If at the end of a page, find the next entry. */
+ if (ep->index == NEXTINDEX(ep->page)) {
+ h = ep->page;
+ pg = h->nextpg;
+ mpool_put(t->bt_mp, h, 0);
+ if (pg == P_INVALID)
+ return (RET_SPECIAL);
+ if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
+ return (RET_ERROR);
+ ep->index = 0;
+ ep->page = h;
+ }
+ *erval = *ep;
+ return (RET_SUCCESS);
+}
+
+/*
+ * __bt_setcur --
+ * Set the cursor to an entry in the tree.
+ *
+ * Parameters:
+ * t: the tree
+ * pgno: page number
+ * index: page index
+ */
+void
+__bt_setcur(t, pgno, index)
+ BTREE *t;
+ pgno_t pgno;
+ u_int index;
+{
+ /* Lose any already deleted key. */
+ if (t->bt_cursor.key.data != NULL) {
+ free(t->bt_cursor.key.data);
+ t->bt_cursor.key.size = 0;
+ t->bt_cursor.key.data = NULL;
+ }
+ F_CLR(&t->bt_cursor, CURS_ACQUIRE | CURS_AFTER | CURS_BEFORE);
+
+ /* Update the cursor. */
+ t->bt_cursor.pg.pgno = pgno;
+ t->bt_cursor.pg.index = index;
+ F_SET(&t->bt_cursor, CURS_INIT);
+}
diff --git a/trunk/main/db1-ast/btree/bt_split.c b/trunk/main/db1-ast/btree/bt_split.c
new file mode 100644
index 000000000..8fede1e45
--- /dev/null
+++ b/trunk/main/db1-ast/btree/bt_split.c
@@ -0,0 +1,829 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Olson.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)bt_split.c 8.9 (Berkeley) 7/26/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../include/db.h"
+#include "btree.h"
+
+static int bt_broot __P((BTREE *, PAGE *, PAGE *, PAGE *));
+static PAGE *bt_page
+ __P((BTREE *, PAGE *, PAGE **, PAGE **, indx_t *, size_t));
+static int bt_preserve __P((BTREE *, pgno_t));
+static PAGE *bt_psplit
+ __P((BTREE *, PAGE *, PAGE *, PAGE *, indx_t *, size_t));
+static PAGE *bt_root
+ __P((BTREE *, PAGE *, PAGE **, PAGE **, indx_t *, size_t));
+static int bt_rroot __P((BTREE *, PAGE *, PAGE *, PAGE *));
+static recno_t rec_total __P((PAGE *));
+
+#ifdef STATISTICS
+u_long bt_rootsplit, bt_split, bt_sortsplit, bt_pfxsaved;
+#endif
+
+/*
+ * __BT_SPLIT -- Split the tree.
+ *
+ * Parameters:
+ * t: tree
+ * sp: page to split
+ * key: key to insert
+ * data: data to insert
+ * flags: BIGKEY/BIGDATA flags
+ * ilen: insert length
+ * skip: index to leave open
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS
+ */
+int
+__bt_split(t, sp, key, data, flags, ilen, argskip)
+ BTREE *t;
+ PAGE *sp;
+ const DBT *key, *data;
+ int flags;
+ size_t ilen;
+ u_int32_t argskip;
+{
+ BINTERNAL *bi = 0;
+ BLEAF *bl = 0, *tbl;
+ DBT a, b;
+ EPGNO *parent;
+ PAGE *h, *l, *r, *lchild, *rchild;
+ indx_t nxtindex;
+ u_int16_t skip;
+ u_int32_t n, nbytes, nksize = 0;
+ int parentsplit;
+ char *dest;
+
+ /*
+ * Split the page into two pages, l and r. The split routines return
+ * a pointer to the page into which the key should be inserted and with
+ * skip set to the offset which should be used. Additionally, l and r
+ * are pinned.
+ */
+ skip = argskip;
+ h = sp->pgno == P_ROOT ?
+ bt_root(t, sp, &l, &r, &skip, ilen) :
+ bt_page(t, sp, &l, &r, &skip, ilen);
+ if (h == NULL)
+ return (RET_ERROR);
+
+ /*
+ * Insert the new key/data pair into the leaf page. (Key inserts
+ * always cause a leaf page to split first.)
+ */
+ h->linp[skip] = h->upper -= ilen;
+ dest = (char *)h + h->upper;
+ if (F_ISSET(t, R_RECNO))
+ WR_RLEAF(dest, data, flags)
+ else
+ WR_BLEAF(dest, key, data, flags)
+
+ /* If the root page was split, make it look right. */
+ if (sp->pgno == P_ROOT &&
+ (F_ISSET(t, R_RECNO) ?
+ bt_rroot(t, sp, l, r) : bt_broot(t, sp, l, r)) == RET_ERROR)
+ goto err2;
+
+ /*
+ * Now we walk the parent page stack -- a LIFO stack of the pages that
+ * were traversed when we searched for the page that split. Each stack
+ * entry is a page number and a page index offset. The offset is for
+ * the page traversed on the search. We've just split a page, so we
+ * have to insert a new key into the parent page.
+ *
+ * If the insert into the parent page causes it to split, may have to
+ * continue splitting all the way up the tree. We stop if the root
+ * splits or the page inserted into didn't have to split to hold the
+ * new key. Some algorithms replace the key for the old page as well
+ * as the new page. We don't, as there's no reason to believe that the
+ * first key on the old page is any better than the key we have, and,
+ * in the case of a key being placed at index 0 causing the split, the
+ * key is unavailable.
+ *
+ * There are a maximum of 5 pages pinned at any time. We keep the left
+ * and right pages pinned while working on the parent. The 5 are the
+ * two children, left parent and right parent (when the parent splits)
+ * and the root page or the overflow key page when calling bt_preserve.
+ * This code must make sure that all pins are released other than the
+ * root page or overflow page which is unlocked elsewhere.
+ */
+ while ((parent = BT_POP(t)) != NULL) {
+ lchild = l;
+ rchild = r;
+
+ /* Get the parent page. */
+ if ((h = mpool_get(t->bt_mp, parent->pgno, 0)) == NULL)
+ goto err2;
+
+ /*
+ * The new key goes ONE AFTER the index, because the split
+ * was to the right.
+ */
+ skip = parent->index + 1;
+
+ /*
+ * Calculate the space needed on the parent page.
+ *
+ * Prefix trees: space hack when inserting into BINTERNAL
+ * pages. Retain only what's needed to distinguish between
+ * the new entry and the LAST entry on the page to its left.
+ * If the keys compare equal, retain the entire key. Note,
+ * we don't touch overflow keys, and the entire key must be
+ * retained for the next-to-left most key on the leftmost
+ * page of each level, or the search will fail. Applicable
+ * ONLY to internal pages that have leaf pages as children.
+ * Further reduction of the key between pairs of internal
+ * pages loses too much information.
+ */
+ switch (rchild->flags & P_TYPE) {
+ case P_BINTERNAL:
+ bi = GETBINTERNAL(rchild, 0);
+ nbytes = NBINTERNAL(bi->ksize);
+ break;
+ case P_BLEAF:
+ bl = GETBLEAF(rchild, 0);
+ nbytes = NBINTERNAL(bl->ksize);
+ if (t->bt_pfx && !(bl->flags & P_BIGKEY) &&
+ (h->prevpg != P_INVALID || skip > 1)) {
+ tbl = GETBLEAF(lchild, NEXTINDEX(lchild) - 1);
+ a.size = tbl->ksize;
+ a.data = tbl->bytes;
+ b.size = bl->ksize;
+ b.data = bl->bytes;
+ nksize = t->bt_pfx(&a, &b);
+ n = NBINTERNAL(nksize);
+ if (n < nbytes) {
+#ifdef STATISTICS
+ bt_pfxsaved += nbytes - n;
+#endif
+ nbytes = n;
+ } else
+ nksize = 0;
+ } else
+ nksize = 0;
+ break;
+ case P_RINTERNAL:
+ case P_RLEAF:
+ nbytes = NRINTERNAL;
+ break;
+ default:
+ abort();
+ }
+
+ /* Split the parent page if necessary or shift the indices. */
+ if ((u_int32_t) (h->upper - h->lower)
+ < nbytes + sizeof(indx_t)) {
+ sp = h;
+ h = h->pgno == P_ROOT ?
+ bt_root(t, h, &l, &r, &skip, nbytes) :
+ bt_page(t, h, &l, &r, &skip, nbytes);
+ if (h == NULL)
+ goto err1;
+ parentsplit = 1;
+ } else {
+ if (skip < (nxtindex = NEXTINDEX(h)))
+ memmove(h->linp + skip + 1, h->linp + skip,
+ (nxtindex - skip) * sizeof(indx_t));
+ h->lower += sizeof(indx_t);
+ parentsplit = 0;
+ }
+
+ /* Insert the key into the parent page. */
+ switch (rchild->flags & P_TYPE) {
+ case P_BINTERNAL:
+ h->linp[skip] = h->upper -= nbytes;
+ dest = (char *)h + h->linp[skip];
+ memmove(dest, bi, nbytes);
+ ((BINTERNAL *)dest)->pgno = rchild->pgno;
+ break;
+ case P_BLEAF:
+ h->linp[skip] = h->upper -= nbytes;
+ dest = (char *)h + h->linp[skip];
+ WR_BINTERNAL(dest, nksize ? nksize : bl->ksize,
+ rchild->pgno, bl->flags & P_BIGKEY);
+ memmove(dest, bl->bytes, nksize ? nksize : bl->ksize);
+ if (bl->flags & P_BIGKEY &&
+ bt_preserve(t, *(pgno_t *)bl->bytes) == RET_ERROR)
+ goto err1;
+ break;
+ case P_RINTERNAL:
+ /*
+ * Update the left page count. If split
+ * added at index 0, fix the correct page.
+ */
+ if (skip > 0)
+ dest = (char *)h + h->linp[skip - 1];
+ else
+ dest = (char *)l + l->linp[NEXTINDEX(l) - 1];
+ ((RINTERNAL *)dest)->nrecs = rec_total(lchild);
+ ((RINTERNAL *)dest)->pgno = lchild->pgno;
+
+ /* Update the right page count. */
+ h->linp[skip] = h->upper -= nbytes;
+ dest = (char *)h + h->linp[skip];
+ ((RINTERNAL *)dest)->nrecs = rec_total(rchild);
+ ((RINTERNAL *)dest)->pgno = rchild->pgno;
+ break;
+ case P_RLEAF:
+ /*
+ * Update the left page count. If split
+ * added at index 0, fix the correct page.
+ */
+ if (skip > 0)
+ dest = (char *)h + h->linp[skip - 1];
+ else
+ dest = (char *)l + l->linp[NEXTINDEX(l) - 1];
+ ((RINTERNAL *)dest)->nrecs = NEXTINDEX(lchild);
+ ((RINTERNAL *)dest)->pgno = lchild->pgno;
+
+ /* Update the right page count. */
+ h->linp[skip] = h->upper -= nbytes;
+ dest = (char *)h + h->linp[skip];
+ ((RINTERNAL *)dest)->nrecs = NEXTINDEX(rchild);
+ ((RINTERNAL *)dest)->pgno = rchild->pgno;
+ break;
+ default:
+ abort();
+ }
+
+ /* Unpin the held pages. */
+ if (!parentsplit) {
+ mpool_put(t->bt_mp, h, MPOOL_DIRTY);
+ break;
+ }
+
+ /* If the root page was split, make it look right. */
+ if (sp->pgno == P_ROOT &&
+ (F_ISSET(t, R_RECNO) ?
+ bt_rroot(t, sp, l, r) : bt_broot(t, sp, l, r)) == RET_ERROR)
+ goto err1;
+
+ mpool_put(t->bt_mp, lchild, MPOOL_DIRTY);
+ mpool_put(t->bt_mp, rchild, MPOOL_DIRTY);
+ }
+
+ /* Unpin the held pages. */
+ mpool_put(t->bt_mp, l, MPOOL_DIRTY);
+ mpool_put(t->bt_mp, r, MPOOL_DIRTY);
+
+ /* Clear any pages left on the stack. */
+ return (RET_SUCCESS);
+
+ /*
+ * If something fails in the above loop we were already walking back
+ * up the tree and the tree is now inconsistent. Nothing much we can
+ * do about it but release any memory we're holding.
+ */
+err1: mpool_put(t->bt_mp, lchild, MPOOL_DIRTY);
+ mpool_put(t->bt_mp, rchild, MPOOL_DIRTY);
+
+err2: mpool_put(t->bt_mp, l, 0);
+ mpool_put(t->bt_mp, r, 0);
+ __dbpanic(t->bt_dbp);
+ return (RET_ERROR);
+}
+
+/*
+ * BT_PAGE -- Split a non-root page of a btree.
+ *
+ * Parameters:
+ * t: tree
+ * h: root page
+ * lp: pointer to left page pointer
+ * rp: pointer to right page pointer
+ * skip: pointer to index to leave open
+ * ilen: insert length
+ *
+ * Returns:
+ * Pointer to page in which to insert or NULL on error.
+ */
+static PAGE *
+bt_page(t, h, lp, rp, skip, ilen)
+ BTREE *t;
+ PAGE *h, **lp, **rp;
+ indx_t *skip;
+ size_t ilen;
+{
+ PAGE *l, *r, *tp;
+ pgno_t npg;
+
+#ifdef STATISTICS
+ ++bt_split;
+#endif
+ /* Put the new right page for the split into place. */
+ if ((r = __bt_new(t, &npg)) == NULL)
+ return (NULL);
+ r->pgno = npg;
+ r->lower = BTDATAOFF;
+ r->upper = t->bt_psize;
+ r->nextpg = h->nextpg;
+ r->prevpg = h->pgno;
+ r->flags = h->flags & P_TYPE;
+
+ /*
+ * If we're splitting the last page on a level because we're appending
+ * a key to it (skip is NEXTINDEX()), it's likely that the data is
+ * sorted. Adding an empty page on the side of the level is less work
+ * and can push the fill factor much higher than normal. If we're
+ * wrong it's no big deal, we'll just do the split the right way next
+ * time. It may look like it's equally easy to do a similar hack for
+ * reverse sorted data, that is, split the tree left, but it's not.
+ * Don't even try.
+ */
+ if (h->nextpg == P_INVALID && *skip == NEXTINDEX(h)) {
+#ifdef STATISTICS
+ ++bt_sortsplit;
+#endif
+ h->nextpg = r->pgno;
+ r->lower = BTDATAOFF + sizeof(indx_t);
+ *skip = 0;
+ *lp = h;
+ *rp = r;
+ return (r);
+ }
+
+ /* Put the new left page for the split into place. */
+ if ((l = (PAGE *)malloc(t->bt_psize)) == NULL) {
+ mpool_put(t->bt_mp, r, 0);
+ return (NULL);
+ }
+#ifdef PURIFY
+ memset(l, 0xff, t->bt_psize);
+#endif
+ l->pgno = h->pgno;
+ l->nextpg = r->pgno;
+ l->prevpg = h->prevpg;
+ l->lower = BTDATAOFF;
+ l->upper = t->bt_psize;
+ l->flags = h->flags & P_TYPE;
+
+ /* Fix up the previous pointer of the page after the split page. */
+ if (h->nextpg != P_INVALID) {
+ if ((tp = mpool_get(t->bt_mp, h->nextpg, 0)) == NULL) {
+ free(l);
+ /* XXX mpool_free(t->bt_mp, r->pgno); */
+ return (NULL);
+ }
+ tp->prevpg = r->pgno;
+ mpool_put(t->bt_mp, tp, MPOOL_DIRTY);
+ }
+
+ /*
+ * Split right. The key/data pairs aren't sorted in the btree page so
+ * it's simpler to copy the data from the split page onto two new pages
+ * instead of copying half the data to the right page and compacting
+ * the left page in place. Since the left page can't change, we have
+ * to swap the original and the allocated left page after the split.
+ */
+ tp = bt_psplit(t, h, l, r, skip, ilen);
+
+ /* Move the new left page onto the old left page. */
+ memmove(h, l, t->bt_psize);
+ if (tp == l)
+ tp = h;
+ free(l);
+
+ *lp = h;
+ *rp = r;
+ return (tp);
+}
+
+/*
+ * BT_ROOT -- Split the root page of a btree.
+ *
+ * Parameters:
+ * t: tree
+ * h: root page
+ * lp: pointer to left page pointer
+ * rp: pointer to right page pointer
+ * skip: pointer to index to leave open
+ * ilen: insert length
+ *
+ * Returns:
+ * Pointer to page in which to insert or NULL on error.
+ */
+static PAGE *
+bt_root(t, h, lp, rp, skip, ilen)
+ BTREE *t;
+ PAGE *h, **lp, **rp;
+ indx_t *skip;
+ size_t ilen;
+{
+ PAGE *l, *r, *tp;
+ pgno_t lnpg, rnpg;
+
+#ifdef STATISTICS
+ ++bt_split;
+ ++bt_rootsplit;
+#endif
+ /* Put the new left and right pages for the split into place. */
+ if ((l = __bt_new(t, &lnpg)) == NULL ||
+ (r = __bt_new(t, &rnpg)) == NULL)
+ return (NULL);
+ l->pgno = lnpg;
+ r->pgno = rnpg;
+ l->nextpg = r->pgno;
+ r->prevpg = l->pgno;
+ l->prevpg = r->nextpg = P_INVALID;
+ l->lower = r->lower = BTDATAOFF;
+ l->upper = r->upper = t->bt_psize;
+ l->flags = r->flags = h->flags & P_TYPE;
+
+ /* Split the root page. */
+ tp = bt_psplit(t, h, l, r, skip, ilen);
+
+ *lp = l;
+ *rp = r;
+ return (tp);
+}
+
+/*
+ * BT_RROOT -- Fix up the recno root page after it has been split.
+ *
+ * Parameters:
+ * t: tree
+ * h: root page
+ * l: left page
+ * r: right page
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS
+ */
+static int
+bt_rroot(t, h, l, r)
+ BTREE *t;
+ PAGE *h, *l, *r;
+{
+ char *dest;
+
+ /* Insert the left and right keys, set the header information. */
+ h->linp[0] = h->upper = t->bt_psize - NRINTERNAL;
+ dest = (char *)h + h->upper;
+ WR_RINTERNAL(dest,
+ l->flags & P_RLEAF ? NEXTINDEX(l) : rec_total(l), l->pgno);
+
+ h->linp[1] = h->upper -= NRINTERNAL;
+ dest = (char *)h + h->upper;
+ WR_RINTERNAL(dest,
+ r->flags & P_RLEAF ? NEXTINDEX(r) : rec_total(r), r->pgno);
+
+ h->lower = BTDATAOFF + 2 * sizeof(indx_t);
+
+ /* Unpin the root page, set to recno internal page. */
+ h->flags &= ~P_TYPE;
+ h->flags |= P_RINTERNAL;
+ mpool_put(t->bt_mp, h, MPOOL_DIRTY);
+
+ return (RET_SUCCESS);
+}
+
+/*
+ * BT_BROOT -- Fix up the btree root page after it has been split.
+ *
+ * Parameters:
+ * t: tree
+ * h: root page
+ * l: left page
+ * r: right page
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS
+ */
+static int
+bt_broot(t, h, l, r)
+ BTREE *t;
+ PAGE *h, *l, *r;
+{
+ BINTERNAL *bi;
+ BLEAF *bl;
+ u_int32_t nbytes;
+ char *dest;
+
+ /*
+ * If the root page was a leaf page, change it into an internal page.
+ * We copy the key we split on (but not the key's data, in the case of
+ * a leaf page) to the new root page.
+ *
+ * The btree comparison code guarantees that the left-most key on any
+ * level of the tree is never used, so it doesn't need to be filled in.
+ */
+ nbytes = NBINTERNAL(0);
+ h->linp[0] = h->upper = t->bt_psize - nbytes;
+ dest = (char *)h + h->upper;
+ WR_BINTERNAL(dest, 0, l->pgno, 0);
+
+ switch (h->flags & P_TYPE) {
+ case P_BLEAF:
+ bl = GETBLEAF(r, 0);
+ nbytes = NBINTERNAL(bl->ksize);
+ h->linp[1] = h->upper -= nbytes;
+ dest = (char *)h + h->upper;
+ WR_BINTERNAL(dest, bl->ksize, r->pgno, 0);
+ memmove(dest, bl->bytes, bl->ksize);
+
+ /*
+ * If the key is on an overflow page, mark the overflow chain
+ * so it isn't deleted when the leaf copy of the key is deleted.
+ */
+ if (bl->flags & P_BIGKEY &&
+ bt_preserve(t, *(pgno_t *)bl->bytes) == RET_ERROR)
+ return (RET_ERROR);
+ break;
+ case P_BINTERNAL:
+ bi = GETBINTERNAL(r, 0);
+ nbytes = NBINTERNAL(bi->ksize);
+ h->linp[1] = h->upper -= nbytes;
+ dest = (char *)h + h->upper;
+ memmove(dest, bi, nbytes);
+ ((BINTERNAL *)dest)->pgno = r->pgno;
+ break;
+ default:
+ abort();
+ }
+
+ /* There are two keys on the page. */
+ h->lower = BTDATAOFF + 2 * sizeof(indx_t);
+
+ /* Unpin the root page, set to btree internal page. */
+ h->flags &= ~P_TYPE;
+ h->flags |= P_BINTERNAL;
+ mpool_put(t->bt_mp, h, MPOOL_DIRTY);
+
+ return (RET_SUCCESS);
+}
+
+/*
+ * BT_PSPLIT -- Do the real work of splitting the page.
+ *
+ * Parameters:
+ * t: tree
+ * h: page to be split
+ * l: page to put lower half of data
+ * r: page to put upper half of data
+ * pskip: pointer to index to leave open
+ * ilen: insert length
+ *
+ * Returns:
+ * Pointer to page in which to insert.
+ */
+static PAGE *
+bt_psplit(t, h, l, r, pskip, ilen)
+ BTREE *t;
+ PAGE *h, *l, *r;
+ indx_t *pskip;
+ size_t ilen;
+{
+ BINTERNAL *bi;
+ BLEAF *bl;
+ CURSOR *c;
+ RLEAF *rl;
+ PAGE *rval;
+ void *src = 0;
+ indx_t full, half, nxt, off, skip, top, used;
+ u_int32_t nbytes;
+ int bigkeycnt, isbigkey;
+
+ /*
+ * Split the data to the left and right pages. Leave the skip index
+ * open. Additionally, make some effort not to split on an overflow
+ * key. This makes internal page processing faster and can save
+ * space as overflow keys used by internal pages are never deleted.
+ */
+ bigkeycnt = 0;
+ skip = *pskip;
+ full = t->bt_psize - BTDATAOFF;
+ half = full / 2;
+ used = 0;
+ for (nxt = off = 0, top = NEXTINDEX(h); nxt < top; ++off) {
+ if (skip == off) {
+ nbytes = ilen;
+ isbigkey = 0; /* XXX: not really known. */
+ } else
+ switch (h->flags & P_TYPE) {
+ case P_BINTERNAL:
+ src = bi = GETBINTERNAL(h, nxt);
+ nbytes = NBINTERNAL(bi->ksize);
+ isbigkey = bi->flags & P_BIGKEY;
+ break;
+ case P_BLEAF:
+ src = bl = GETBLEAF(h, nxt);
+ nbytes = NBLEAF(bl);
+ isbigkey = bl->flags & P_BIGKEY;
+ break;
+ case P_RINTERNAL:
+ src = GETRINTERNAL(h, nxt);
+ nbytes = NRINTERNAL;
+ isbigkey = 0;
+ break;
+ case P_RLEAF:
+ src = rl = GETRLEAF(h, nxt);
+ nbytes = NRLEAF(rl);
+ isbigkey = 0;
+ break;
+ default:
+ abort();
+ }
+
+ /*
+ * If the key/data pairs are substantial fractions of the max
+ * possible size for the page, it's possible to get situations
+ * where we decide to try and copy too much onto the left page.
+ * Make sure that doesn't happen.
+ */
+ if ((skip <= off && used + nbytes + sizeof(indx_t) >= full)
+ || nxt == top - 1) {
+ --off;
+ break;
+ }
+
+ /* Copy the key/data pair, if not the skipped index. */
+ if (skip != off) {
+ ++nxt;
+
+ l->linp[off] = l->upper -= nbytes;
+ memmove((char *)l + l->upper, src, nbytes);
+ }
+
+ used += nbytes + sizeof(indx_t);
+ if (used >= half) {
+ if (!isbigkey || bigkeycnt == 3)
+ break;
+ else
+ ++bigkeycnt;
+ }
+ }
+
+ /*
+ * Off is the last offset that's valid for the left page.
+ * Nxt is the first offset to be placed on the right page.
+ */
+ l->lower += (off + 1) * sizeof(indx_t);
+
+ /*
+ * If splitting the page that the cursor was on, the cursor has to be
+ * adjusted to point to the same record as before the split. If the
+ * cursor is at or past the skipped slot, the cursor is incremented by
+ * one. If the cursor is on the right page, it is decremented by the
+ * number of records split to the left page.
+ */
+ c = &t->bt_cursor;
+ if (F_ISSET(c, CURS_INIT) && c->pg.pgno == h->pgno) {
+ if (c->pg.index >= skip)
+ ++c->pg.index;
+ if (c->pg.index < nxt) /* Left page. */
+ c->pg.pgno = l->pgno;
+ else { /* Right page. */
+ c->pg.pgno = r->pgno;
+ c->pg.index -= nxt;
+ }
+ }
+
+ /*
+ * If the skipped index was on the left page, just return that page.
+ * Otherwise, adjust the skip index to reflect the new position on
+ * the right page.
+ */
+ if (skip <= off) {
+ skip = 0;
+ rval = l;
+ } else {
+ rval = r;
+ *pskip -= nxt;
+ }
+
+ for (off = 0; nxt < top; ++off) {
+ if (skip == nxt) {
+ ++off;
+ skip = 0;
+ }
+ switch (h->flags & P_TYPE) {
+ case P_BINTERNAL:
+ src = bi = GETBINTERNAL(h, nxt);
+ nbytes = NBINTERNAL(bi->ksize);
+ break;
+ case P_BLEAF:
+ src = bl = GETBLEAF(h, nxt);
+ nbytes = NBLEAF(bl);
+ break;
+ case P_RINTERNAL:
+ src = GETRINTERNAL(h, nxt);
+ nbytes = NRINTERNAL;
+ break;
+ case P_RLEAF:
+ src = rl = GETRLEAF(h, nxt);
+ nbytes = NRLEAF(rl);
+ break;
+ default:
+ abort();
+ }
+ ++nxt;
+ r->linp[off] = r->upper -= nbytes;
+ memmove((char *)r + r->upper, src, nbytes);
+ }
+ r->lower += off * sizeof(indx_t);
+
+ /* If the key is being appended to the page, adjust the index. */
+ if (skip == top)
+ r->lower += sizeof(indx_t);
+
+ return (rval);
+}
+
+/*
+ * BT_PRESERVE -- Mark a chain of pages as used by an internal node.
+ *
+ * Chains of indirect blocks pointed to by leaf nodes get reclaimed when the
+ * record that references them gets deleted. Chains pointed to by internal
+ * pages never get deleted. This routine marks a chain as pointed to by an
+ * internal page.
+ *
+ * Parameters:
+ * t: tree
+ * pg: page number of first page in the chain.
+ *
+ * Returns:
+ * RET_SUCCESS, RET_ERROR.
+ */
+static int
+bt_preserve(t, pg)
+ BTREE *t;
+ pgno_t pg;
+{
+ PAGE *h;
+
+ if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
+ return (RET_ERROR);
+ h->flags |= P_PRESERVE;
+ mpool_put(t->bt_mp, h, MPOOL_DIRTY);
+ return (RET_SUCCESS);
+}
+
+/*
+ * REC_TOTAL -- Return the number of recno entries below a page.
+ *
+ * Parameters:
+ * h: page
+ *
+ * Returns:
+ * The number of recno entries below a page.
+ *
+ * XXX
+ * These values could be set by the bt_psplit routine. The problem is that the
+ * entry has to be popped off of the stack etc. or the values have to be passed
+ * all the way back to bt_split/bt_rroot and it's not very clean.
+ */
+static recno_t
+rec_total(h)
+ PAGE *h;
+{
+ recno_t recs;
+ indx_t nxt, top;
+
+ for (recs = 0, nxt = 0, top = NEXTINDEX(h); nxt < top; ++nxt)
+ recs += GETRINTERNAL(h, nxt)->nrecs;
+ return (recs);
+}
diff --git a/trunk/main/db1-ast/btree/bt_utils.c b/trunk/main/db1-ast/btree/bt_utils.c
new file mode 100644
index 000000000..2ecb5e678
--- /dev/null
+++ b/trunk/main/db1-ast/btree/bt_utils.c
@@ -0,0 +1,260 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Olson.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)bt_utils.c 8.8 (Berkeley) 7/20/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/param.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../include/db.h"
+#include "btree.h"
+
+/*
+ * __bt_ret --
+ * Build return key/data pair.
+ *
+ * Parameters:
+ * t: tree
+ * e: key/data pair to be returned
+ * key: user's key structure (NULL if not to be filled in)
+ * rkey: memory area to hold key
+ * data: user's data structure (NULL if not to be filled in)
+ * rdata: memory area to hold data
+ * copy: always copy the key/data item
+ *
+ * Returns:
+ * RET_SUCCESS, RET_ERROR.
+ */
+int
+__bt_ret(t, e, key, rkey, data, rdata, copy)
+ BTREE *t;
+ EPG *e;
+ DBT *key, *rkey, *data, *rdata;
+ int copy;
+{
+ BLEAF *bl;
+ void *p;
+
+ bl = GETBLEAF(e->page, e->index);
+
+ /*
+ * We must copy big keys/data to make them contiguous. Otherwise,
+ * leave the page pinned and don't copy unless the user specified
+ * concurrent access.
+ */
+ if (key == NULL)
+ goto dataonly;
+
+ if (bl->flags & P_BIGKEY) {
+ if (__ovfl_get(t, bl->bytes,
+ &key->size, &rkey->data, &rkey->size))
+ return (RET_ERROR);
+ key->data = rkey->data;
+ } else if (copy || F_ISSET(t, B_DB_LOCK)) {
+ if (bl->ksize > rkey->size) {
+ p = (void *)(rkey->data == NULL ?
+ malloc(bl->ksize) : realloc(rkey->data, bl->ksize));
+ if (p == NULL)
+ return (RET_ERROR);
+ rkey->data = p;
+ rkey->size = bl->ksize;
+ }
+ memmove(rkey->data, bl->bytes, bl->ksize);
+ key->size = bl->ksize;
+ key->data = rkey->data;
+ } else {
+ key->size = bl->ksize;
+ key->data = bl->bytes;
+ }
+
+dataonly:
+ if (data == NULL)
+ return (RET_SUCCESS);
+
+ if (bl->flags & P_BIGDATA) {
+ if (__ovfl_get(t, bl->bytes + bl->ksize,
+ &data->size, &rdata->data, &rdata->size))
+ return (RET_ERROR);
+ data->data = rdata->data;
+ } else if (copy || F_ISSET(t, B_DB_LOCK)) {
+ /* Use +1 in case the first record retrieved is 0 length. */
+ if (bl->dsize + 1 > rdata->size) {
+ p = (void *)(rdata->data == NULL ?
+ malloc(bl->dsize + 1) :
+ realloc(rdata->data, bl->dsize + 1));
+ if (p == NULL)
+ return (RET_ERROR);
+ rdata->data = p;
+ rdata->size = bl->dsize + 1;
+ }
+ memmove(rdata->data, bl->bytes + bl->ksize, bl->dsize);
+ data->size = bl->dsize;
+ data->data = rdata->data;
+ } else {
+ data->size = bl->dsize;
+ data->data = bl->bytes + bl->ksize;
+ }
+
+ return (RET_SUCCESS);
+}
+
+/*
+ * __BT_CMP -- Compare a key to a given record.
+ *
+ * Parameters:
+ * t: tree
+ * k1: DBT pointer of first arg to comparison
+ * e: pointer to EPG for comparison
+ *
+ * Returns:
+ * < 0 if k1 is < record
+ * = 0 if k1 is = record
+ * > 0 if k1 is > record
+ */
+int
+__bt_cmp(t, k1, e)
+ BTREE *t;
+ const DBT *k1;
+ EPG *e;
+{
+ BINTERNAL *bi;
+ BLEAF *bl;
+ DBT k2;
+ PAGE *h;
+ void *bigkey;
+
+ /*
+ * The left-most key on internal pages, at any level of the tree, is
+ * guaranteed by the following code to be less than any user key.
+ * This saves us from having to update the leftmost key on an internal
+ * page when the user inserts a new key in the tree smaller than
+ * anything we've yet seen.
+ */
+ h = e->page;
+ if (e->index == 0 && h->prevpg == P_INVALID && !(h->flags & P_BLEAF))
+ return (1);
+
+ bigkey = NULL;
+ if (h->flags & P_BLEAF) {
+ bl = GETBLEAF(h, e->index);
+ if (bl->flags & P_BIGKEY)
+ bigkey = bl->bytes;
+ else {
+ k2.data = bl->bytes;
+ k2.size = bl->ksize;
+ }
+ } else {
+ bi = GETBINTERNAL(h, e->index);
+ if (bi->flags & P_BIGKEY)
+ bigkey = bi->bytes;
+ else {
+ k2.data = bi->bytes;
+ k2.size = bi->ksize;
+ }
+ }
+
+ if (bigkey) {
+ if (__ovfl_get(t, bigkey,
+ &k2.size, &t->bt_rdata.data, &t->bt_rdata.size))
+ return (RET_ERROR);
+ k2.data = t->bt_rdata.data;
+ }
+ return ((*t->bt_cmp)(k1, &k2));
+}
+
+/*
+ * __BT_DEFCMP -- Default comparison routine.
+ *
+ * Parameters:
+ * a: DBT #1
+ * b: DBT #2
+ *
+ * Returns:
+ * < 0 if a is < b
+ * = 0 if a is = b
+ * > 0 if a is > b
+ */
+int
+__bt_defcmp(a, b)
+ const DBT *a, *b;
+{
+ register size_t len;
+ register u_char *p1, *p2;
+
+ /*
+ * XXX
+ * If a size_t doesn't fit in an int, this routine can lose.
+ * What we need is a integral type which is guaranteed to be
+ * larger than a size_t, and there is no such thing.
+ */
+ len = MIN(a->size, b->size);
+ for (p1 = a->data, p2 = b->data; len--; ++p1, ++p2)
+ if (*p1 != *p2)
+ return ((int)*p1 - (int)*p2);
+ return ((int)a->size - (int)b->size);
+}
+
+/*
+ * __BT_DEFPFX -- Default prefix routine.
+ *
+ * Parameters:
+ * a: DBT #1
+ * b: DBT #2
+ *
+ * Returns:
+ * Number of bytes needed to distinguish b from a.
+ */
+size_t
+__bt_defpfx(a, b)
+ const DBT *a, *b;
+{
+ register u_char *p1, *p2;
+ register size_t cnt, len;
+
+ cnt = 1;
+ len = MIN(a->size, b->size);
+ for (p1 = a->data, p2 = b->data; len--; ++p1, ++p2, ++cnt)
+ if (*p1 != *p2)
+ return (cnt);
+
+ /* a->size must be <= b->size, or they wouldn't be in this order. */
+ return (a->size < b->size ? a->size + 1 : a->size);
+}
diff --git a/trunk/main/db1-ast/btree/btree.h b/trunk/main/db1-ast/btree/btree.h
new file mode 100644
index 000000000..1f4a9ec91
--- /dev/null
+++ b/trunk/main/db1-ast/btree/btree.h
@@ -0,0 +1,391 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Olson.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)btree.h 8.11 (Berkeley) 8/17/94
+ */
+
+/* Macros to set/clear/test flags. */
+#define F_SET(p, f) (p)->flags |= (f)
+#define F_CLR(p, f) (p)->flags &= ~(f)
+#define F_ISSET(p, f) ((p)->flags & (f))
+
+#include <mpool.h>
+
+#define mpool_open __mpool_open
+#define mpool_filter __mpool_filter
+#define mpool_new __mpool_new
+#define mpool_get __mpool_get
+#define mpool_put __mpool_put
+#define mpool_sync __mpool_sync
+#define mpool_close __mpool_close
+
+#define DEFMINKEYPAGE (2) /* Minimum keys per page */
+#define MINCACHE (5) /* Minimum cached pages */
+#define MINPSIZE (512) /* Minimum page size */
+
+/*
+ * Page 0 of a btree file contains a copy of the meta-data. This page is also
+ * used as an out-of-band page, i.e. page pointers that point to nowhere point
+ * to page 0. Page 1 is the root of the btree.
+ */
+#define P_INVALID 0 /* Invalid tree page number. */
+#define P_META 0 /* Tree metadata page number. */
+#define P_ROOT 1 /* Tree root page number. */
+
+/*
+ * There are five page layouts in the btree: btree internal pages (BINTERNAL),
+ * btree leaf pages (BLEAF), recno internal pages (RINTERNAL), recno leaf pages
+ * (RLEAF) and overflow pages. All five page types have a page header (PAGE).
+ * This implementation requires that values within structures NOT be padded.
+ * (ANSI C permits random padding.) If your compiler pads randomly you'll have
+ * to do some work to get this package to run.
+ */
+typedef struct _page {
+ pgno_t pgno; /* this page's page number */
+ pgno_t prevpg; /* left sibling */
+ pgno_t nextpg; /* right sibling */
+
+#define P_BINTERNAL 0x01 /* btree internal page */
+#define P_BLEAF 0x02 /* leaf page */
+#define P_OVERFLOW 0x04 /* overflow page */
+#define P_RINTERNAL 0x08 /* recno internal page */
+#define P_RLEAF 0x10 /* leaf page */
+#define P_TYPE 0x1f /* type mask */
+#define P_PRESERVE 0x20 /* never delete this chain of pages */
+ u_int32_t flags;
+
+ indx_t lower; /* lower bound of free space on page */
+ indx_t upper; /* upper bound of free space on page */
+ indx_t linp[1]; /* indx_t-aligned VAR. LENGTH DATA */
+} PAGE;
+
+/* First and next index. */
+#define BTDATAOFF \
+ (sizeof(pgno_t) + sizeof(pgno_t) + sizeof(pgno_t) + \
+ sizeof(u_int32_t) + sizeof(indx_t) + sizeof(indx_t))
+#define NEXTINDEX(p) (((p)->lower - BTDATAOFF) / sizeof(indx_t))
+
+/*
+ * For pages other than overflow pages, there is an array of offsets into the
+ * rest of the page immediately following the page header. Each offset is to
+ * an item which is unique to the type of page. The h_lower offset is just
+ * past the last filled-in index. The h_upper offset is the first item on the
+ * page. Offsets are from the beginning of the page.
+ *
+ * If an item is too big to store on a single page, a flag is set and the item
+ * is a { page, size } pair such that the page is the first page of an overflow
+ * chain with size bytes of item. Overflow pages are simply bytes without any
+ * external structure.
+ *
+ * The page number and size fields in the items are pgno_t-aligned so they can
+ * be manipulated without copying. (This presumes that 32 bit items can be
+ * manipulated on this system.)
+ */
+#define LALIGN(n) (((n) + sizeof(pgno_t) - 1) & ~(sizeof(pgno_t) - 1))
+#define NOVFLSIZE (sizeof(pgno_t) + sizeof(u_int32_t))
+
+/*
+ * For the btree internal pages, the item is a key. BINTERNALs are {key, pgno}
+ * pairs, such that the key compares less than or equal to all of the records
+ * on that page. For a tree without duplicate keys, an internal page with two
+ * consecutive keys, a and b, will have all records greater than or equal to a
+ * and less than b stored on the page associated with a. Duplicate keys are
+ * somewhat special and can cause duplicate internal and leaf page records and
+ * some minor modifications of the above rule.
+ */
+typedef struct _binternal {
+ u_int32_t ksize; /* key size */
+ pgno_t pgno; /* page number stored on */
+#define P_BIGDATA 0x01 /* overflow data */
+#define P_BIGKEY 0x02 /* overflow key */
+ u_char flags;
+ char bytes[1]; /* data */
+} BINTERNAL;
+
+/* Get the page's BINTERNAL structure at index indx. */
+#define GETBINTERNAL(pg, indx) \
+ ((BINTERNAL *)((char *)(pg) + (pg)->linp[indx]))
+
+/* Get the number of bytes in the entry. */
+#define NBINTERNAL(len) \
+ LALIGN(sizeof(u_int32_t) + sizeof(pgno_t) + sizeof(u_char) + (len))
+
+/* Copy a BINTERNAL entry to the page. */
+#define WR_BINTERNAL(p, size, pgno, flags) { \
+ *(u_int32_t *)p = size; \
+ p += sizeof(u_int32_t); \
+ *(pgno_t *)p = pgno; \
+ p += sizeof(pgno_t); \
+ *(u_char *)p = flags; \
+ p += sizeof(u_char); \
+}
+
+/*
+ * For the recno internal pages, the item is a page number with the number of
+ * keys found on that page and below.
+ */
+typedef struct _rinternal {
+ recno_t nrecs; /* number of records */
+ pgno_t pgno; /* page number stored below */
+} RINTERNAL;
+
+/* Get the page's RINTERNAL structure at index indx. */
+#define GETRINTERNAL(pg, indx) \
+ ((RINTERNAL *)((char *)(pg) + (pg)->linp[indx]))
+
+/* Get the number of bytes in the entry. */
+#define NRINTERNAL \
+ LALIGN(sizeof(recno_t) + sizeof(pgno_t))
+
+/* Copy a RINTERNAL entry to the page. */
+#define WR_RINTERNAL(p, nrecs, pgno) { \
+ *(recno_t *)p = nrecs; \
+ p += sizeof(recno_t); \
+ *(pgno_t *)p = pgno; \
+}
+
+/* For the btree leaf pages, the item is a key and data pair. */
+typedef struct _bleaf {
+ u_int32_t ksize; /* size of key */
+ u_int32_t dsize; /* size of data */
+ u_char flags; /* P_BIGDATA, P_BIGKEY */
+ char bytes[1]; /* data */
+} BLEAF;
+
+/* Get the page's BLEAF structure at index indx. */
+#define GETBLEAF(pg, indx) \
+ ((BLEAF *)((char *)(pg) + (pg)->linp[indx]))
+
+/* Get the number of bytes in the entry. */
+#define NBLEAF(p) NBLEAFDBT((p)->ksize, (p)->dsize)
+
+/* Get the number of bytes in the user's key/data pair. */
+#define NBLEAFDBT(ksize, dsize) \
+ LALIGN(sizeof(u_int32_t) + sizeof(u_int32_t) + sizeof(u_char) + \
+ (ksize) + (dsize))
+
+/* Copy a BLEAF entry to the page. */
+#define WR_BLEAF(p, key, data, flags) { \
+ *(u_int32_t *)p = key->size; \
+ p += sizeof(u_int32_t); \
+ *(u_int32_t *)p = data->size; \
+ p += sizeof(u_int32_t); \
+ *(u_char *)p = flags; \
+ p += sizeof(u_char); \
+ memmove(p, key->data, key->size); \
+ p += key->size; \
+ memmove(p, data->data, data->size); \
+}
+
+/* For the recno leaf pages, the item is a data entry. */
+typedef struct _rleaf {
+ u_int32_t dsize; /* size of data */
+ u_char flags; /* P_BIGDATA */
+ char bytes[1];
+} RLEAF;
+
+/* Get the page's RLEAF structure at index indx. */
+#define GETRLEAF(pg, indx) \
+ ((RLEAF *)((char *)(pg) + (pg)->linp[indx]))
+
+/* Get the number of bytes in the entry. */
+#define NRLEAF(p) NRLEAFDBT((p)->dsize)
+
+/* Get the number of bytes from the user's data. */
+#define NRLEAFDBT(dsize) \
+ LALIGN(sizeof(u_int32_t) + sizeof(u_char) + (dsize))
+
+/* Copy a RLEAF entry to the page. */
+#define WR_RLEAF(p, data, flags) { \
+ *(u_int32_t *)p = data->size; \
+ p += sizeof(u_int32_t); \
+ *(u_char *)p = flags; \
+ p += sizeof(u_char); \
+ memmove(p, data->data, data->size); \
+}
+
+/*
+ * A record in the tree is either a pointer to a page and an index in the page
+ * or a page number and an index. These structures are used as a cursor, stack
+ * entry and search returns as well as to pass records to other routines.
+ *
+ * One comment about searches. Internal page searches must find the largest
+ * record less than key in the tree so that descents work. Leaf page searches
+ * must find the smallest record greater than key so that the returned index
+ * is the record's correct position for insertion.
+ */
+typedef struct _epgno {
+ pgno_t pgno; /* the page number */
+ indx_t index; /* the index on the page */
+} EPGNO;
+
+typedef struct _epg {
+ PAGE *page; /* the (pinned) page */
+ indx_t index; /* the index on the page */
+} EPG;
+
+/*
+ * About cursors. The cursor (and the page that contained the key/data pair
+ * that it referenced) can be deleted, which makes things a bit tricky. If
+ * there are no duplicates of the cursor key in the tree (i.e. B_NODUPS is set
+ * or there simply aren't any duplicates of the key) we copy the key that it
+ * referenced when it's deleted, and reacquire a new cursor key if the cursor
+ * is used again. If there are duplicates keys, we move to the next/previous
+ * key, and set a flag so that we know what happened. NOTE: if duplicate (to
+ * the cursor) keys are added to the tree during this process, it is undefined
+ * if they will be returned or not in a cursor scan.
+ *
+ * The flags determine the possible states of the cursor:
+ *
+ * CURS_INIT The cursor references *something*.
+ * CURS_ACQUIRE The cursor was deleted, and a key has been saved so that
+ * we can reacquire the right position in the tree.
+ * CURS_AFTER, CURS_BEFORE
+ * The cursor was deleted, and now references a key/data pair
+ * that has not yet been returned, either before or after the
+ * deleted key/data pair.
+ * XXX
+ * This structure is broken out so that we can eventually offer multiple
+ * cursors as part of the DB interface.
+ */
+typedef struct _cursor {
+ EPGNO pg; /* B: Saved tree reference. */
+ DBT key; /* B: Saved key, or key.data == NULL. */
+ recno_t rcursor; /* R: recno cursor (1-based) */
+
+#define CURS_ACQUIRE 0x01 /* B: Cursor needs to be reacquired. */
+#define CURS_AFTER 0x02 /* B: Unreturned cursor after key. */
+#define CURS_BEFORE 0x04 /* B: Unreturned cursor before key. */
+#define CURS_INIT 0x08 /* RB: Cursor initialized. */
+ u_int8_t flags;
+} CURSOR;
+
+/*
+ * The metadata of the tree. The nrecs field is used only by the RECNO code.
+ * This is because the btree doesn't really need it and it requires that every
+ * put or delete call modify the metadata.
+ */
+typedef struct _btmeta {
+ u_int32_t magic; /* magic number */
+ u_int32_t version; /* version */
+ u_int32_t psize; /* page size */
+ u_int32_t free; /* page number of first free page */
+ u_int32_t nrecs; /* R: number of records */
+
+#define SAVEMETA (B_NODUPS | R_RECNO)
+ u_int32_t flags; /* bt_flags & SAVEMETA */
+} BTMETA;
+
+/* The in-memory btree/recno data structure. */
+typedef struct _btree {
+ MPOOL *bt_mp; /* memory pool cookie */
+
+ DB *bt_dbp; /* pointer to enclosing DB */
+
+ EPG bt_cur; /* current (pinned) page */
+ PAGE *bt_pinned; /* page pinned across calls */
+
+ CURSOR bt_cursor; /* cursor */
+
+#define BT_PUSH(t, p, i) { \
+ t->bt_sp->pgno = p; \
+ t->bt_sp->index = i; \
+ ++t->bt_sp; \
+}
+#define BT_POP(t) (t->bt_sp == t->bt_stack ? NULL : --t->bt_sp)
+#define BT_CLR(t) (t->bt_sp = t->bt_stack)
+ EPGNO bt_stack[50]; /* stack of parent pages */
+ EPGNO *bt_sp; /* current stack pointer */
+
+ DBT bt_rkey; /* returned key */
+ DBT bt_rdata; /* returned data */
+
+ int bt_fd; /* tree file descriptor */
+
+ pgno_t bt_free; /* next free page */
+ u_int32_t bt_psize; /* page size */
+ indx_t bt_ovflsize; /* cut-off for key/data overflow */
+ int bt_lorder; /* byte order */
+ /* sorted order */
+ enum { NOT, BACK, FORWARD } bt_order;
+ EPGNO bt_last; /* last insert */
+
+ /* B: key comparison function */
+ int (*bt_cmp) __P((const DBT *, const DBT *));
+ /* B: prefix comparison function */
+ size_t (*bt_pfx) __P((const DBT *, const DBT *));
+ /* R: recno input function */
+ int (*bt_irec) __P((struct _btree *, recno_t));
+
+ FILE *bt_rfp; /* R: record FILE pointer */
+ int bt_rfd; /* R: record file descriptor */
+
+ caddr_t bt_cmap; /* R: current point in mapped space */
+ caddr_t bt_smap; /* R: start of mapped space */
+ caddr_t bt_emap; /* R: end of mapped space */
+ size_t bt_msize; /* R: size of mapped region. */
+
+ recno_t bt_nrecs; /* R: number of records */
+ size_t bt_reclen; /* R: fixed record length */
+ u_char bt_bval; /* R: delimiting byte/pad character */
+
+/*
+ * NB:
+ * B_NODUPS and R_RECNO are stored on disk, and may not be changed.
+ */
+#define B_INMEM 0x00001 /* in-memory tree */
+#define B_METADIRTY 0x00002 /* need to write metadata */
+#define B_MODIFIED 0x00004 /* tree modified */
+#define B_NEEDSWAP 0x00008 /* if byte order requires swapping */
+#define B_RDONLY 0x00010 /* read-only tree */
+
+#define B_NODUPS 0x00020 /* no duplicate keys permitted */
+#define R_RECNO 0x00080 /* record oriented tree */
+
+#define R_CLOSEFP 0x00040 /* opened a file pointer */
+#define R_EOF 0x00100 /* end of input file reached. */
+#define R_FIXLEN 0x00200 /* fixed length records */
+#define R_MEMMAPPED 0x00400 /* memory mapped file. */
+#define R_INMEM 0x00800 /* in-memory file */
+#define R_MODIFIED 0x01000 /* modified file */
+#define R_RDONLY 0x02000 /* read-only file */
+
+#define B_DB_LOCK 0x04000 /* DB_LOCK specified. */
+#define B_DB_SHMEM 0x08000 /* DB_SHMEM specified. */
+#define B_DB_TXN 0x10000 /* DB_TXN specified. */
+ u_int32_t flags;
+} BTREE;
+
+#include "extern.h"
diff --git a/trunk/main/db1-ast/btree/extern.h b/trunk/main/db1-ast/btree/extern.h
new file mode 100644
index 000000000..ebd9c5492
--- /dev/null
+++ b/trunk/main/db1-ast/btree/extern.h
@@ -0,0 +1,70 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)extern.h 8.10 (Berkeley) 7/20/94
+ */
+
+int __bt_close __P((DB *));
+int __bt_cmp __P((BTREE *, const DBT *, EPG *));
+int __bt_crsrdel __P((BTREE *, EPGNO *));
+int __bt_defcmp __P((const DBT *, const DBT *));
+size_t __bt_defpfx __P((const DBT *, const DBT *));
+int __bt_delete __P((const DB *, const DBT *, u_int));
+int __bt_dleaf __P((BTREE *, const DBT *, PAGE *, u_int));
+int __bt_fd __P((const DB *));
+int __bt_free __P((BTREE *, PAGE *));
+int __bt_get __P((const DB *, const DBT *, DBT *, u_int));
+PAGE *__bt_new __P((BTREE *, pgno_t *));
+void __bt_pgin __P((void *, pgno_t, void *));
+void __bt_pgout __P((void *, pgno_t, void *));
+int __bt_push __P((BTREE *, pgno_t, int));
+int __bt_put __P((const DB *dbp, DBT *, const DBT *, u_int));
+int __bt_ret __P((BTREE *, EPG *, DBT *, DBT *, DBT *, DBT *, int));
+EPG *__bt_search __P((BTREE *, const DBT *, int *));
+int __bt_seq __P((const DB *, DBT *, DBT *, u_int));
+void __bt_setcur __P((BTREE *, pgno_t, u_int));
+int __bt_split __P((BTREE *, PAGE *,
+ const DBT *, const DBT *, int, size_t, u_int32_t));
+int __bt_sync __P((const DB *, u_int));
+
+int __ovfl_delete __P((BTREE *, void *));
+int __ovfl_get __P((BTREE *, void *, size_t *, void **, size_t *));
+int __ovfl_put __P((BTREE *, const DBT *, pgno_t *));
+
+#ifdef DEBUG
+void __bt_dnpage __P((DB *, pgno_t));
+void __bt_dpage __P((PAGE *));
+void __bt_dump __P((DB *));
+#endif
+#ifdef STATISTICS
+void __bt_stat __P((DB *));
+#endif
diff --git a/trunk/main/db1-ast/db/db.c b/trunk/main/db1-ast/db/db.c
new file mode 100644
index 000000000..8c0584d4c
--- /dev/null
+++ b/trunk/main/db1-ast/db/db.c
@@ -0,0 +1,103 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)db.c 8.4 (Berkeley) 2/21/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <stdio.h>
+
+#include "../include/db.h"
+
+DB *
+dbopen(fname, flags, mode, type, openinfo)
+ const char *fname;
+ int flags, mode;
+ DBTYPE type;
+ const void *openinfo;
+{
+
+#define DB_FLAGS (DB_LOCK | DB_SHMEM | DB_TXN)
+#define USE_OPEN_FLAGS \
+ (O_CREAT | O_EXCL | O_EXLOCK | O_NONBLOCK | O_RDONLY | \
+ O_RDWR | O_SHLOCK | O_TRUNC)
+
+ if ((flags & ~(USE_OPEN_FLAGS | DB_FLAGS)) == 0)
+ switch (type) {
+ case DB_BTREE:
+ return (__bt_open(fname, flags & USE_OPEN_FLAGS,
+ mode, openinfo, flags & DB_FLAGS));
+ case DB_HASH:
+ return (__hash_open(fname, flags & USE_OPEN_FLAGS,
+ mode, openinfo, flags & DB_FLAGS));
+ case DB_RECNO:
+ return (__rec_open(fname, flags & USE_OPEN_FLAGS,
+ mode, openinfo, flags & DB_FLAGS));
+ }
+ errno = EINVAL;
+ return (NULL);
+}
+
+static int
+__dberr __P((void))
+{
+ return (RET_ERROR);
+}
+
+/*
+ * __DBPANIC -- Stop.
+ *
+ * Parameters:
+ * dbp: pointer to the DB structure.
+ */
+void
+__dbpanic(dbp)
+ DB *dbp;
+{
+ /* The only thing that can succeed is a close. */
+ dbp->del = (int (*)__P((const struct __db *,
+ const DBT *, u_int))) __dberr;
+ dbp->get = (int (*)__P((const struct __db *,
+ const DBT *, DBT *, u_int))) __dberr;
+ dbp->put = (int (*)__P((const struct __db *,
+ DBT *, const DBT *, u_int))) __dberr;
+ dbp->seq = (int (*)__P((const struct __db *,
+ DBT *, DBT *, u_int))) __dberr;
+ dbp->sync = (int (*)__P((const struct __db *, u_int))) __dberr;
+ dbp->fd = (int (*)__P((const struct __db *))) __dberr;
+}
diff --git a/trunk/main/db1-ast/hash/README b/trunk/main/db1-ast/hash/README
new file mode 100644
index 000000000..f29ccf7e1
--- /dev/null
+++ b/trunk/main/db1-ast/hash/README
@@ -0,0 +1,72 @@
+# @(#)README 8.1 (Berkeley) 6/4/93
+
+This package implements a superset of the hsearch and dbm/ndbm libraries.
+
+Test Programs:
+ All test programs which need key/data pairs expect them entered
+ with key and data on separate lines
+
+ tcreat3.c
+ Takes
+ bucketsize (bsize),
+ fill factor (ffactor), and
+ initial number of elements (nelem).
+ Creates a hash table named hashtest containing the
+ keys/data pairs entered from standard in.
+ thash4.c
+ Takes
+ bucketsize (bsize),
+ fill factor (ffactor),
+ initial number of elements (nelem)
+ bytes of cache (ncached), and
+ file from which to read data (fname)
+ Creates a table from the key/data pairs on standard in and
+ then does a read of each key/data in fname
+ tdel.c
+ Takes
+ bucketsize (bsize), and
+ fill factor (ffactor).
+ file from which to read data (fname)
+ Reads each key/data pair from fname and deletes the
+ key from the hash table hashtest
+ tseq.c
+ Reads the key/data pairs in the file hashtest and writes them
+ to standard out.
+ tread2.c
+ Takes
+ butes of cache (ncached).
+ Reads key/data pairs from standard in and looks them up
+ in the file hashtest.
+ tverify.c
+ Reads key/data pairs from standard in, looks them up
+ in the file hashtest, and verifies that the data is
+ correct.
+
+NOTES:
+
+The file search.h is provided for using the hsearch compatible interface
+on BSD systems. On System V derived systems, search.h should appear in
+/usr/include.
+
+The man page ../man/db.3 explains the interface to the hashing system.
+The file hash.ps is a postscript copy of a paper explaining
+the history, implementation, and performance of the hash package.
+
+"bugs" or idiosyncracies
+
+If you have a lot of overflows, it is possible to run out of overflow
+pages. Currently, this will cause a message to be printed on stderr.
+Eventually, this will be indicated by a return error code.
+
+If you are using the ndbm interface and exit without flushing or closing the
+file, you may lose updates since the package buffers all writes. Also,
+the db interface only creates a single database file. To avoid overwriting
+the user's original file, the suffix ".db" is appended to the file name
+passed to dbm_open. Additionally, if your code "knows" about the historic
+.dir and .pag files, it will break.
+
+There is a fundamental difference between this package and the old hsearch.
+Hsearch requires the user to maintain the keys and data in the application's
+allocated memory while hash takes care of all storage management. The down
+side is that the byte strings passed in the ENTRY structure must be null
+terminated (both the keys and the data).
diff --git a/trunk/main/db1-ast/hash/extern.h b/trunk/main/db1-ast/hash/extern.h
new file mode 100644
index 000000000..4f1f23d67
--- /dev/null
+++ b/trunk/main/db1-ast/hash/extern.h
@@ -0,0 +1,65 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)extern.h 8.4 (Berkeley) 6/16/94
+ */
+
+BUFHEAD *__add_ovflpage __P((HTAB *, BUFHEAD *));
+int __addel __P((HTAB *, BUFHEAD *, const DBT *, const DBT *));
+int __big_delete __P((HTAB *, BUFHEAD *));
+int __big_insert __P((HTAB *, BUFHEAD *, const DBT *, const DBT *));
+int __big_keydata __P((HTAB *, BUFHEAD *, DBT *, DBT *, int));
+int __big_return __P((HTAB *, BUFHEAD *, int, DBT *, int));
+int __big_split __P((HTAB *, BUFHEAD *, BUFHEAD *, BUFHEAD *,
+ int, u_int32_t, SPLIT_RETURN *));
+int __buf_free __P((HTAB *, int, int));
+void __buf_init __P((HTAB *, int));
+u_int32_t __call_hash __P((HTAB *, char *, int));
+int __delpair __P((HTAB *, BUFHEAD *, int));
+int __expand_table __P((HTAB *));
+int __find_bigpair __P((HTAB *, BUFHEAD *, int, char *, int));
+u_int16_t __find_last_page __P((HTAB *, BUFHEAD **));
+void __free_ovflpage __P((HTAB *, BUFHEAD *));
+BUFHEAD *__get_buf __P((HTAB *, u_int32_t, BUFHEAD *, int));
+int __get_page __P((HTAB *, char *, u_int32_t, int, int, int));
+int __ibitmap __P((HTAB *, int, int, int));
+u_int32_t __hash_log2 __P((u_int32_t));
+int __put_page __P((HTAB *, char *, u_int32_t, int, int));
+void __reclaim_buf __P((HTAB *, BUFHEAD *));
+int __split_page __P((HTAB *, u_int32_t, u_int32_t));
+
+/* Default hash routine. */
+extern u_int32_t (*__default_hash) __P((const void *, size_t));
+
+#ifdef HASH_STATISTICS
+extern int hash_accesses, hash_collisions, hash_expansions, hash_overflows;
+#endif
diff --git a/trunk/main/db1-ast/hash/hash.c b/trunk/main/db1-ast/hash/hash.c
new file mode 100644
index 000000000..47dc52a0e
--- /dev/null
+++ b/trunk/main/db1-ast/hash/hash.c
@@ -0,0 +1,999 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Margo Seltzer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)hash.c 8.9 (Berkeley) 6/16/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#ifdef DEBUG
+#include <assert.h>
+#endif
+
+#include "../include/db.h"
+#include "hash.h"
+#include "page.h"
+#include "extern.h"
+
+static int alloc_segs __P((HTAB *, int));
+static int flush_meta __P((HTAB *));
+static int hash_access __P((HTAB *, ACTION, DBT *, DBT *));
+static int hash_close __P((DB *));
+static int hash_delete __P((const DB *, const DBT *, u_int32_t));
+static int hash_fd __P((const DB *));
+static int hash_get __P((const DB *, const DBT *, DBT *, u_int32_t));
+static int hash_put __P((const DB *, DBT *, const DBT *, u_int32_t));
+static void *hash_realloc __P((SEGMENT **, int, int));
+static int hash_seq __P((const DB *, DBT *, DBT *, u_int32_t));
+static int hash_sync __P((const DB *, u_int32_t));
+static int hdestroy __P((HTAB *));
+static HTAB *init_hash __P((HTAB *, const char *, const HASHINFO *));
+static int init_htab __P((HTAB *, int));
+#if BYTE_ORDER == LITTLE_ENDIAN
+static void swap_header __P((HTAB *));
+static void swap_header_copy __P((HASHHDR *, HASHHDR *));
+#endif
+
+/* Fast arithmetic, relying on powers of 2, */
+#define MOD(x, y) ((x) & ((y) - 1))
+
+#define RETURN_ERROR(ERR, LOC) { save_errno = ERR; goto LOC; }
+
+/* Return values */
+#define SUCCESS (0)
+#define ERROR (-1)
+#define ABNORMAL (1)
+
+#ifdef HASH_STATISTICS
+int hash_accesses, hash_collisions, hash_expansions, hash_overflows;
+#endif
+
+/************************** INTERFACE ROUTINES ***************************/
+/* OPEN/CLOSE */
+
+extern DB *
+__hash_open(file, flags, mode, info, dflags)
+ const char *file;
+ int flags, mode, dflags;
+ const HASHINFO *info; /* Special directives for create */
+{
+ HTAB *hashp;
+ struct stat statbuf;
+ DB *dbp;
+ int bpages, hdrsize, new_table, nsegs, save_errno;
+
+ if ((flags & O_ACCMODE) == O_WRONLY) {
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ if (!(hashp = (HTAB *)calloc(1, sizeof(HTAB))))
+ return (NULL);
+ hashp->fp = -1;
+
+ /*
+ * Even if user wants write only, we need to be able to read
+ * the actual file, so we need to open it read/write. But, the
+ * field in the hashp structure needs to be accurate so that
+ * we can check accesses.
+ */
+ hashp->flags = flags;
+
+ new_table = 0;
+ if (!file || (flags & O_TRUNC) ||
+ (stat(file, &statbuf) && (errno == ENOENT))) {
+ if (errno == ENOENT)
+ errno = 0; /* Just in case someone looks at errno */
+ new_table = 1;
+ }
+ if (file) {
+ if ((hashp->fp = open(file, flags, mode)) == -1)
+ RETURN_ERROR(errno, error0);
+ (void)fcntl(hashp->fp, F_SETFD, 1);
+ }
+ if (new_table) {
+ if (!(hashp = init_hash(hashp, file, info)))
+ RETURN_ERROR(errno, error1);
+ } else {
+ /* Table already exists */
+ if (info && info->hash)
+ hashp->hash = info->hash;
+ else
+ hashp->hash = __default_hash;
+
+ hdrsize = read(hashp->fp, &hashp->hdr, sizeof(HASHHDR));
+#if BYTE_ORDER == LITTLE_ENDIAN
+ swap_header(hashp);
+#endif
+ if (hdrsize == -1)
+ RETURN_ERROR(errno, error1);
+ if (hdrsize != sizeof(HASHHDR))
+ RETURN_ERROR(EFTYPE, error1);
+ /* Verify file type, versions and hash function */
+ if (hashp->MAGIC != HASHMAGIC)
+ RETURN_ERROR(EFTYPE, error1);
+#define OLDHASHVERSION 1
+ if (hashp->VERSION != HASHVERSION &&
+ hashp->VERSION != OLDHASHVERSION)
+ RETURN_ERROR(EFTYPE, error1);
+ if (hashp->hash(CHARKEY, sizeof(CHARKEY))
+ != (u_int32_t) hashp->H_CHARKEY)
+ RETURN_ERROR(EFTYPE, error1);
+ /*
+ * Figure out how many segments we need. Max_Bucket is the
+ * maximum bucket number, so the number of buckets is
+ * max_bucket + 1.
+ */
+ nsegs = (hashp->MAX_BUCKET + 1 + hashp->SGSIZE - 1) /
+ hashp->SGSIZE;
+ hashp->nsegs = 0;
+ if (alloc_segs(hashp, nsegs))
+ /*
+ * If alloc_segs fails, table will have been destroyed
+ * and errno will have been set.
+ */
+ return (NULL);
+ /* Read in bitmaps */
+ bpages = (hashp->SPARES[hashp->OVFL_POINT] +
+ (hashp->BSIZE << BYTE_SHIFT) - 1) >>
+ (hashp->BSHIFT + BYTE_SHIFT);
+
+ hashp->nmaps = bpages;
+ (void)memset(&hashp->mapp[0], 0, bpages * sizeof(u_int32_t *));
+ }
+
+ /* Initialize Buffer Manager */
+ if (info && info->cachesize)
+ __buf_init(hashp, info->cachesize);
+ else
+ __buf_init(hashp, DEF_BUFSIZE);
+
+ hashp->new_file = new_table;
+ hashp->save_file = file && (hashp->flags & O_ACCMODE) != O_RDONLY;
+ hashp->cbucket = -1;
+ if (!(dbp = (DB *)malloc(sizeof(DB)))) {
+ save_errno = errno;
+ hdestroy(hashp);
+ errno = save_errno;
+ return (NULL);
+ }
+ dbp->internal = hashp;
+ dbp->close = hash_close;
+ dbp->del = hash_delete;
+ dbp->fd = hash_fd;
+ dbp->get = hash_get;
+ dbp->put = hash_put;
+ dbp->seq = hash_seq;
+ dbp->sync = hash_sync;
+ dbp->type = DB_HASH;
+
+#ifdef DEBUG
+ (void)fprintf(stderr,
+"%s\n%s%x\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%x\n%s%x\n%s%d\n%s%d\n",
+ "init_htab:",
+ "TABLE POINTER ", hashp,
+ "BUCKET SIZE ", hashp->BSIZE,
+ "BUCKET SHIFT ", hashp->BSHIFT,
+ "DIRECTORY SIZE ", hashp->DSIZE,
+ "SEGMENT SIZE ", hashp->SGSIZE,
+ "SEGMENT SHIFT ", hashp->SSHIFT,
+ "FILL FACTOR ", hashp->FFACTOR,
+ "MAX BUCKET ", hashp->MAX_BUCKET,
+ "OVFL POINT ", hashp->OVFL_POINT,
+ "LAST FREED ", hashp->LAST_FREED,
+ "HIGH MASK ", hashp->HIGH_MASK,
+ "LOW MASK ", hashp->LOW_MASK,
+ "NSEGS ", hashp->nsegs,
+ "NKEYS ", hashp->NKEYS);
+#endif
+#ifdef HASH_STATISTICS
+ hash_overflows = hash_accesses = hash_collisions = hash_expansions = 0;
+#endif
+ return (dbp);
+
+error1:
+ if (hashp != NULL)
+ (void)close(hashp->fp);
+
+error0:
+ free(hashp);
+ errno = save_errno;
+ return (NULL);
+}
+
+static int
+hash_close(dbp)
+ DB *dbp;
+{
+ HTAB *hashp;
+ int retval;
+
+ if (!dbp)
+ return (ERROR);
+
+ hashp = (HTAB *)dbp->internal;
+ retval = hdestroy(hashp);
+ free(dbp);
+ return (retval);
+}
+
+static int
+hash_fd(dbp)
+ const DB *dbp;
+{
+ HTAB *hashp;
+
+ if (!dbp)
+ return (ERROR);
+
+ hashp = (HTAB *)dbp->internal;
+ if (hashp->fp == -1) {
+ errno = ENOENT;
+ return (-1);
+ }
+ return (hashp->fp);
+}
+
+/************************** LOCAL CREATION ROUTINES **********************/
+static HTAB *
+init_hash(hashp, file, info)
+ HTAB *hashp;
+ const char *file;
+ const HASHINFO *info;
+{
+#ifdef _STATBUF_ST_BLKSIZE
+ struct stat statbuf;
+#endif
+ int nelem;
+
+ nelem = 1;
+ hashp->NKEYS = 0;
+ hashp->LORDER = BYTE_ORDER;
+ hashp->BSIZE = DEF_BUCKET_SIZE;
+ hashp->BSHIFT = DEF_BUCKET_SHIFT;
+ hashp->SGSIZE = DEF_SEGSIZE;
+ hashp->SSHIFT = DEF_SEGSIZE_SHIFT;
+ hashp->DSIZE = DEF_DIRSIZE;
+ hashp->FFACTOR = DEF_FFACTOR;
+ hashp->hash = __default_hash;
+ memset(hashp->SPARES, 0, sizeof(hashp->SPARES));
+ memset(hashp->BITMAPS, 0, sizeof (hashp->BITMAPS));
+
+ /* Fix bucket size to be optimal for file system */
+#ifdef _STATBUF_ST_BLKSIZE
+ if (file != NULL) {
+ if (stat(file, &statbuf))
+ return (NULL);
+ hashp->BSIZE = statbuf.st_blksize;
+ hashp->BSHIFT = __hash_log2(hashp->BSIZE);
+ }
+#endif
+
+ if (info) {
+ if (info->bsize) {
+ /* Round pagesize up to power of 2 */
+ hashp->BSHIFT = __hash_log2(info->bsize);
+ hashp->BSIZE = 1 << hashp->BSHIFT;
+ if (hashp->BSIZE > MAX_BSIZE) {
+ errno = EINVAL;
+ return (NULL);
+ }
+ }
+ if (info->ffactor)
+ hashp->FFACTOR = info->ffactor;
+ if (info->hash)
+ hashp->hash = info->hash;
+ if (info->nelem)
+ nelem = info->nelem;
+ if (info->lorder) {
+ if (info->lorder != BIG_ENDIAN &&
+ info->lorder != LITTLE_ENDIAN) {
+ errno = EINVAL;
+ return (NULL);
+ }
+ hashp->LORDER = info->lorder;
+ }
+ }
+ /* init_htab should destroy the table and set errno if it fails */
+ if (init_htab(hashp, nelem))
+ return (NULL);
+ else
+ return (hashp);
+}
+/*
+ * This calls alloc_segs which may run out of memory. Alloc_segs will destroy
+ * the table and set errno, so we just pass the error information along.
+ *
+ * Returns 0 on No Error
+ */
+static int
+init_htab(hashp, nelem)
+ HTAB *hashp;
+ int nelem;
+{
+ register int nbuckets, nsegs;
+ int l2;
+
+ /*
+ * Divide number of elements by the fill factor and determine a
+ * desired number of buckets. Allocate space for the next greater
+ * power of two number of buckets.
+ */
+ nelem = (nelem - 1) / hashp->FFACTOR + 1;
+
+ l2 = __hash_log2(MAX(nelem, 2));
+ nbuckets = 1 << l2;
+
+ hashp->SPARES[l2] = l2 + 1;
+ hashp->SPARES[l2 + 1] = l2 + 1;
+ hashp->OVFL_POINT = l2;
+ hashp->LAST_FREED = 2;
+
+ /* First bitmap page is at: splitpoint l2 page offset 1 */
+ if (__ibitmap(hashp, OADDR_OF(l2, 1), l2 + 1, 0))
+ return (-1);
+
+ hashp->MAX_BUCKET = hashp->LOW_MASK = nbuckets - 1;
+ hashp->HIGH_MASK = (nbuckets << 1) - 1;
+ hashp->HDRPAGES = ((MAX(sizeof(HASHHDR), MINHDRSIZE) - 1) >>
+ hashp->BSHIFT) + 1;
+
+ nsegs = (nbuckets - 1) / hashp->SGSIZE + 1;
+ nsegs = 1 << __hash_log2(nsegs);
+
+ if (nsegs > hashp->DSIZE)
+ hashp->DSIZE = nsegs;
+ return (alloc_segs(hashp, nsegs));
+}
+
+/********************** DESTROY/CLOSE ROUTINES ************************/
+
+/*
+ * Flushes any changes to the file if necessary and destroys the hashp
+ * structure, freeing all allocated space.
+ */
+static int
+hdestroy(hashp)
+ HTAB *hashp;
+{
+ int i, save_errno;
+
+ save_errno = 0;
+
+#ifdef HASH_STATISTICS
+ (void)fprintf(stderr, "hdestroy: accesses %ld collisions %ld\n",
+ hash_accesses, hash_collisions);
+ (void)fprintf(stderr, "hdestroy: expansions %ld\n",
+ hash_expansions);
+ (void)fprintf(stderr, "hdestroy: overflows %ld\n",
+ hash_overflows);
+ (void)fprintf(stderr, "keys %ld maxp %d segmentcount %d\n",
+ hashp->NKEYS, hashp->MAX_BUCKET, hashp->nsegs);
+
+ for (i = 0; i < NCACHED; i++)
+ (void)fprintf(stderr,
+ "spares[%d] = %d\n", i, hashp->SPARES[i]);
+#endif
+ /*
+ * Call on buffer manager to free buffers, and if required,
+ * write them to disk.
+ */
+ if (__buf_free(hashp, 1, hashp->save_file))
+ save_errno = errno;
+ if (hashp->dir) {
+ free(*hashp->dir); /* Free initial segments */
+ /* Free extra segments */
+ while (hashp->exsegs--)
+ free(hashp->dir[--hashp->nsegs]);
+ free(hashp->dir);
+ }
+ if (flush_meta(hashp) && !save_errno)
+ save_errno = errno;
+ /* Free Bigmaps */
+ for (i = 0; i < hashp->nmaps; i++)
+ if (hashp->mapp[i])
+ free(hashp->mapp[i]);
+
+ if (hashp->fp != -1)
+ (void)close(hashp->fp);
+
+ free(hashp);
+
+ if (save_errno) {
+ errno = save_errno;
+ return (ERROR);
+ }
+ return (SUCCESS);
+}
+/*
+ * Write modified pages to disk
+ *
+ * Returns:
+ * 0 == OK
+ * -1 ERROR
+ */
+static int
+hash_sync(dbp, flags)
+ const DB *dbp;
+ u_int32_t flags;
+{
+ HTAB *hashp;
+
+ if (flags != 0) {
+ errno = EINVAL;
+ return (ERROR);
+ }
+
+ if (!dbp)
+ return (ERROR);
+
+ hashp = (HTAB *)dbp->internal;
+ if (!hashp->save_file)
+ return (0);
+ if (__buf_free(hashp, 0, 1) || flush_meta(hashp))
+ return (ERROR);
+ hashp->new_file = 0;
+ return (0);
+}
+
+/*
+ * Returns:
+ * 0 == OK
+ * -1 indicates that errno should be set
+ */
+static int
+flush_meta(hashp)
+ HTAB *hashp;
+{
+ HASHHDR *whdrp;
+#if BYTE_ORDER == LITTLE_ENDIAN
+ HASHHDR whdr;
+#endif
+ int fp, i, wsize;
+
+ if (!hashp->save_file)
+ return (0);
+ hashp->MAGIC = HASHMAGIC;
+ hashp->VERSION = HASHVERSION;
+ hashp->H_CHARKEY = hashp->hash(CHARKEY, sizeof(CHARKEY));
+
+ fp = hashp->fp;
+ whdrp = &hashp->hdr;
+#if BYTE_ORDER == LITTLE_ENDIAN
+ whdrp = &whdr;
+ swap_header_copy(&hashp->hdr, whdrp);
+#endif
+ if ((lseek(fp, (off_t)0, SEEK_SET) == -1) ||
+ ((wsize = write(fp, whdrp, sizeof(HASHHDR))) == -1))
+ return (-1);
+ else
+ if (wsize != sizeof(HASHHDR)) {
+ errno = EFTYPE;
+ hashp->errnum = errno;
+ return (-1);
+ }
+ for (i = 0; i < NCACHED; i++)
+ if (hashp->mapp[i])
+ if (__put_page(hashp, (char *)hashp->mapp[i],
+ hashp->BITMAPS[i], 0, 1))
+ return (-1);
+ return (0);
+}
+
+/*******************************SEARCH ROUTINES *****************************/
+/*
+ * All the access routines return
+ *
+ * Returns:
+ * 0 on SUCCESS
+ * 1 to indicate an external ERROR (i.e. key not found, etc)
+ * -1 to indicate an internal ERROR (i.e. out of memory, etc)
+ */
+static int
+hash_get(dbp, key, data, flag)
+ const DB *dbp;
+ const DBT *key;
+ DBT *data;
+ u_int32_t flag;
+{
+ HTAB *hashp;
+
+ hashp = (HTAB *)dbp->internal;
+ if (flag) {
+ hashp->errnum = errno = EINVAL;
+ return (ERROR);
+ }
+ return (hash_access(hashp, HASH_GET, (DBT *)key, data));
+}
+
+static int
+hash_put(dbp, key, data, flag)
+ const DB *dbp;
+ DBT *key;
+ const DBT *data;
+ u_int32_t flag;
+{
+ HTAB *hashp;
+
+ hashp = (HTAB *)dbp->internal;
+ if (flag && flag != R_NOOVERWRITE) {
+ hashp->errnum = errno = EINVAL;
+ return (ERROR);
+ }
+ if ((hashp->flags & O_ACCMODE) == O_RDONLY) {
+ hashp->errnum = errno = EPERM;
+ return (ERROR);
+ }
+ return (hash_access(hashp, flag == R_NOOVERWRITE ?
+ HASH_PUTNEW : HASH_PUT, (DBT *)key, (DBT *)data));
+}
+
+static int
+hash_delete(dbp, key, flag)
+ const DB *dbp;
+ const DBT *key;
+ u_int32_t flag; /* Ignored */
+{
+ HTAB *hashp;
+
+ hashp = (HTAB *)dbp->internal;
+ if (flag && flag != R_CURSOR) {
+ hashp->errnum = errno = EINVAL;
+ return (ERROR);
+ }
+ if ((hashp->flags & O_ACCMODE) == O_RDONLY) {
+ hashp->errnum = errno = EPERM;
+ return (ERROR);
+ }
+ return (hash_access(hashp, HASH_DELETE, (DBT *)key, NULL));
+}
+
+/*
+ * Assume that hashp has been set in wrapper routine.
+ */
+static int
+hash_access(hashp, action, key, val)
+ HTAB *hashp;
+ ACTION action;
+ DBT *key, *val;
+{
+ register BUFHEAD *rbufp;
+ BUFHEAD *bufp, *save_bufp;
+ register u_int16_t *bp;
+ register int n, ndx, off, size;
+ register char *kp;
+ u_int16_t pageno;
+
+#ifdef HASH_STATISTICS
+ hash_accesses++;
+#endif
+
+ off = hashp->BSIZE;
+ size = key->size;
+ kp = (char *)key->data;
+ rbufp = __get_buf(hashp, __call_hash(hashp, kp, size), NULL, 0);
+ if (!rbufp)
+ return (ERROR);
+ save_bufp = rbufp;
+
+ /* Pin the bucket chain */
+ rbufp->flags |= BUF_PIN;
+ for (bp = (u_int16_t *)rbufp->page, n = *bp++, ndx = 1; ndx < n;)
+ if (bp[1] >= REAL_KEY) {
+ /* Real key/data pair */
+ if (size == off - *bp &&
+ memcmp(kp, rbufp->page + *bp, size) == 0)
+ goto found;
+ off = bp[1];
+#ifdef HASH_STATISTICS
+ hash_collisions++;
+#endif
+ bp += 2;
+ ndx += 2;
+ } else if (bp[1] == OVFLPAGE) {
+ rbufp = __get_buf(hashp, *bp, rbufp, 0);
+ if (!rbufp) {
+ save_bufp->flags &= ~BUF_PIN;
+ return (ERROR);
+ }
+ /* FOR LOOP INIT */
+ bp = (u_int16_t *)rbufp->page;
+ n = *bp++;
+ ndx = 1;
+ off = hashp->BSIZE;
+ } else if (bp[1] < REAL_KEY) {
+ if ((ndx =
+ __find_bigpair(hashp, rbufp, ndx, kp, size)) > 0)
+ goto found;
+ if (ndx == -2) {
+ bufp = rbufp;
+ if (!(pageno =
+ __find_last_page(hashp, &bufp))) {
+ ndx = 0;
+ rbufp = bufp;
+ break; /* FOR */
+ }
+ rbufp = __get_buf(hashp, pageno, bufp, 0);
+ if (!rbufp) {
+ save_bufp->flags &= ~BUF_PIN;
+ return (ERROR);
+ }
+ /* FOR LOOP INIT */
+ bp = (u_int16_t *)rbufp->page;
+ n = *bp++;
+ ndx = 1;
+ off = hashp->BSIZE;
+ } else {
+ save_bufp->flags &= ~BUF_PIN;
+ return (ERROR);
+ }
+ }
+
+ /* Not found */
+ switch (action) {
+ case HASH_PUT:
+ case HASH_PUTNEW:
+ if (__addel(hashp, rbufp, key, val)) {
+ save_bufp->flags &= ~BUF_PIN;
+ return (ERROR);
+ } else {
+ save_bufp->flags &= ~BUF_PIN;
+ return (SUCCESS);
+ }
+ case HASH_GET:
+ case HASH_DELETE:
+ default:
+ save_bufp->flags &= ~BUF_PIN;
+ return (ABNORMAL);
+ }
+
+found:
+ switch (action) {
+ case HASH_PUTNEW:
+ save_bufp->flags &= ~BUF_PIN;
+ return (ABNORMAL);
+ case HASH_GET:
+ bp = (u_int16_t *)rbufp->page;
+ if (bp[ndx + 1] < REAL_KEY) {
+ if (__big_return(hashp, rbufp, ndx, val, 0))
+ return (ERROR);
+ } else {
+ val->data = (u_char *)rbufp->page + (int)bp[ndx + 1];
+ val->size = bp[ndx] - bp[ndx + 1];
+ }
+ break;
+ case HASH_PUT:
+ if ((__delpair(hashp, rbufp, ndx)) ||
+ (__addel(hashp, rbufp, key, val))) {
+ save_bufp->flags &= ~BUF_PIN;
+ return (ERROR);
+ }
+ break;
+ case HASH_DELETE:
+ if (__delpair(hashp, rbufp, ndx))
+ return (ERROR);
+ break;
+ default:
+ abort();
+ }
+ save_bufp->flags &= ~BUF_PIN;
+ return (SUCCESS);
+}
+
+static int
+hash_seq(dbp, key, data, flag)
+ const DB *dbp;
+ DBT *key, *data;
+ u_int32_t flag;
+{
+ register u_int32_t bucket;
+ register BUFHEAD *bufp = NULL;
+ HTAB *hashp;
+ u_int16_t *bp, ndx;
+
+ hashp = (HTAB *)dbp->internal;
+ if (flag && flag != R_FIRST && flag != R_NEXT) {
+ hashp->errnum = errno = EINVAL;
+ return (ERROR);
+ }
+#ifdef HASH_STATISTICS
+ hash_accesses++;
+#endif
+ if ((hashp->cbucket < 0) || (flag == R_FIRST)) {
+ hashp->cbucket = 0;
+ hashp->cndx = 1;
+ hashp->cpage = NULL;
+ }
+
+ for (bp = NULL; !bp || !bp[0]; ) {
+ if (!(bufp = hashp->cpage)) {
+ for (bucket = hashp->cbucket;
+ bucket <= (u_int32_t) hashp->MAX_BUCKET;
+ bucket++, hashp->cndx = 1) {
+ bufp = __get_buf(hashp, bucket, NULL, 0);
+ if (!bufp)
+ return (ERROR);
+ hashp->cpage = bufp;
+ bp = (u_int16_t *)bufp->page;
+ if (bp[0])
+ break;
+ }
+ hashp->cbucket = bucket;
+ if (hashp->cbucket > hashp->MAX_BUCKET) {
+ hashp->cbucket = -1;
+ return (ABNORMAL);
+ }
+ } else
+ bp = (u_int16_t *)hashp->cpage->page;
+
+#ifdef DEBUG
+ assert(bp);
+ assert(bufp);
+#endif
+ while (bp[hashp->cndx + 1] == OVFLPAGE) {
+ bufp = hashp->cpage =
+ __get_buf(hashp, bp[hashp->cndx], bufp, 0);
+ if (!bufp)
+ return (ERROR);
+ bp = (u_int16_t *)(bufp->page);
+ hashp->cndx = 1;
+ }
+ if (!bp[0]) {
+ hashp->cpage = NULL;
+ ++hashp->cbucket;
+ }
+ }
+ ndx = hashp->cndx;
+ if (bp[ndx + 1] < REAL_KEY) {
+ if (__big_keydata(hashp, bufp, key, data, 1))
+ return (ERROR);
+ } else {
+ key->data = (u_char *)hashp->cpage->page + bp[ndx];
+ key->size = (ndx > 1 ? bp[ndx - 1] : hashp->BSIZE) - bp[ndx];
+ data->data = (u_char *)hashp->cpage->page + bp[ndx + 1];
+ data->size = bp[ndx] - bp[ndx + 1];
+ ndx += 2;
+ if (ndx > bp[0]) {
+ hashp->cpage = NULL;
+ hashp->cbucket++;
+ hashp->cndx = 1;
+ } else
+ hashp->cndx = ndx;
+ }
+ return (SUCCESS);
+}
+
+/********************************* UTILITIES ************************/
+
+/*
+ * Returns:
+ * 0 ==> OK
+ * -1 ==> Error
+ */
+extern int
+__expand_table(hashp)
+ HTAB *hashp;
+{
+ u_int32_t old_bucket, new_bucket;
+ int dirsize, new_segnum, spare_ndx;
+
+#ifdef HASH_STATISTICS
+ hash_expansions++;
+#endif
+ new_bucket = ++hashp->MAX_BUCKET;
+ old_bucket = (hashp->MAX_BUCKET & hashp->LOW_MASK);
+
+ new_segnum = new_bucket >> hashp->SSHIFT;
+
+ /* Check if we need a new segment */
+ if (new_segnum >= hashp->nsegs) {
+ /* Check if we need to expand directory */
+ if (new_segnum >= hashp->DSIZE) {
+ /* Reallocate directory */
+ dirsize = hashp->DSIZE * sizeof(SEGMENT *);
+ if (!hash_realloc(&hashp->dir, dirsize, dirsize << 1))
+ return (-1);
+ hashp->DSIZE = dirsize << 1;
+ }
+ if ((hashp->dir[new_segnum] =
+ (SEGMENT)calloc(hashp->SGSIZE, sizeof(SEGMENT))) == NULL)
+ return (-1);
+ hashp->exsegs++;
+ hashp->nsegs++;
+ }
+ /*
+ * If the split point is increasing (MAX_BUCKET's log base 2
+ * * increases), we need to copy the current contents of the spare
+ * split bucket to the next bucket.
+ */
+ spare_ndx = __hash_log2(hashp->MAX_BUCKET + 1);
+ if (spare_ndx > hashp->OVFL_POINT) {
+ hashp->SPARES[spare_ndx] = hashp->SPARES[hashp->OVFL_POINT];
+ hashp->OVFL_POINT = spare_ndx;
+ }
+
+ if (new_bucket > (u_int32_t) hashp->HIGH_MASK) {
+ /* Starting a new doubling */
+ hashp->LOW_MASK = hashp->HIGH_MASK;
+ hashp->HIGH_MASK = new_bucket | hashp->LOW_MASK;
+ }
+ /* Relocate records to the new bucket */
+ return (__split_page(hashp, old_bucket, new_bucket));
+}
+
+/*
+ * If realloc guarantees that the pointer is not destroyed if the realloc
+ * fails, then this routine can go away.
+ */
+static void *
+hash_realloc(p_ptr, oldsize, newsize)
+ SEGMENT **p_ptr;
+ int oldsize, newsize;
+{
+ register void *p;
+
+ if ((p = malloc(newsize))) {
+ memmove(p, *p_ptr, oldsize);
+ memset((char *)p + oldsize, 0, newsize - oldsize);
+ free(*p_ptr);
+ *p_ptr = p;
+ }
+ return (p);
+}
+
+extern u_int32_t
+__call_hash(hashp, k, len)
+ HTAB *hashp;
+ char *k;
+ int len;
+{
+ int n, bucket;
+
+ n = hashp->hash(k, len);
+ bucket = n & hashp->HIGH_MASK;
+ if (bucket > hashp->MAX_BUCKET)
+ bucket = bucket & hashp->LOW_MASK;
+ return (bucket);
+}
+
+/*
+ * Allocate segment table. On error, destroy the table and set errno.
+ *
+ * Returns 0 on success
+ */
+static int
+alloc_segs(hashp, nsegs)
+ HTAB *hashp;
+ int nsegs;
+{
+ register int i;
+ register SEGMENT store;
+
+ int save_errno;
+
+ if ((hashp->dir =
+ (SEGMENT *)calloc(hashp->DSIZE, sizeof(SEGMENT *))) == NULL) {
+ save_errno = errno;
+ (void)hdestroy(hashp);
+ errno = save_errno;
+ return (-1);
+ }
+ /* Allocate segments */
+ if ((store =
+ (SEGMENT)calloc(nsegs << hashp->SSHIFT, sizeof(SEGMENT))) == NULL) {
+ save_errno = errno;
+ (void)hdestroy(hashp);
+ errno = save_errno;
+ return (-1);
+ }
+ for (i = 0; i < nsegs; i++, hashp->nsegs++)
+ hashp->dir[i] = &store[i << hashp->SSHIFT];
+ return (0);
+}
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+/*
+ * Hashp->hdr needs to be byteswapped.
+ */
+static void
+swap_header_copy(srcp, destp)
+ HASHHDR *srcp, *destp;
+{
+ int i;
+
+ P_32_COPY(srcp->magic, destp->magic);
+ P_32_COPY(srcp->version, destp->version);
+ P_32_COPY(srcp->lorder, destp->lorder);
+ P_32_COPY(srcp->bsize, destp->bsize);
+ P_32_COPY(srcp->bshift, destp->bshift);
+ P_32_COPY(srcp->dsize, destp->dsize);
+ P_32_COPY(srcp->ssize, destp->ssize);
+ P_32_COPY(srcp->sshift, destp->sshift);
+ P_32_COPY(srcp->ovfl_point, destp->ovfl_point);
+ P_32_COPY(srcp->last_freed, destp->last_freed);
+ P_32_COPY(srcp->max_bucket, destp->max_bucket);
+ P_32_COPY(srcp->high_mask, destp->high_mask);
+ P_32_COPY(srcp->low_mask, destp->low_mask);
+ P_32_COPY(srcp->ffactor, destp->ffactor);
+ P_32_COPY(srcp->nkeys, destp->nkeys);
+ P_32_COPY(srcp->hdrpages, destp->hdrpages);
+ P_32_COPY(srcp->h_charkey, destp->h_charkey);
+ for (i = 0; i < NCACHED; i++) {
+ P_32_COPY(srcp->spares[i], destp->spares[i]);
+ P_16_COPY(srcp->bitmaps[i], destp->bitmaps[i]);
+ }
+}
+
+static void
+swap_header(hashp)
+ HTAB *hashp;
+{
+ HASHHDR *hdrp;
+ int i;
+
+ hdrp = &hashp->hdr;
+
+ M_32_SWAP(hdrp->magic);
+ M_32_SWAP(hdrp->version);
+ M_32_SWAP(hdrp->lorder);
+ M_32_SWAP(hdrp->bsize);
+ M_32_SWAP(hdrp->bshift);
+ M_32_SWAP(hdrp->dsize);
+ M_32_SWAP(hdrp->ssize);
+ M_32_SWAP(hdrp->sshift);
+ M_32_SWAP(hdrp->ovfl_point);
+ M_32_SWAP(hdrp->last_freed);
+ M_32_SWAP(hdrp->max_bucket);
+ M_32_SWAP(hdrp->high_mask);
+ M_32_SWAP(hdrp->low_mask);
+ M_32_SWAP(hdrp->ffactor);
+ M_32_SWAP(hdrp->nkeys);
+ M_32_SWAP(hdrp->hdrpages);
+ M_32_SWAP(hdrp->h_charkey);
+ for (i = 0; i < NCACHED; i++) {
+ M_32_SWAP(hdrp->spares[i]);
+ M_16_SWAP(hdrp->bitmaps[i]);
+ }
+}
+#endif
diff --git a/trunk/main/db1-ast/hash/hash.h b/trunk/main/db1-ast/hash/hash.h
new file mode 100644
index 000000000..d07db6f07
--- /dev/null
+++ b/trunk/main/db1-ast/hash/hash.h
@@ -0,0 +1,293 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Margo Seltzer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)hash.h 8.3 (Berkeley) 5/31/94
+ */
+
+/* Operations */
+typedef enum {
+ HASH_GET, HASH_PUT, HASH_PUTNEW, HASH_DELETE, HASH_FIRST, HASH_NEXT
+} ACTION;
+
+/* Buffer Management structures */
+typedef struct _bufhead BUFHEAD;
+
+struct _bufhead {
+ BUFHEAD *prev; /* LRU links */
+ BUFHEAD *next; /* LRU links */
+ BUFHEAD *ovfl; /* Overflow page buffer header */
+ u_int32_t addr; /* Address of this page */
+ char *page; /* Actual page data */
+ char flags;
+#define BUF_MOD 0x0001
+#define BUF_DISK 0x0002
+#define BUF_BUCKET 0x0004
+#define BUF_PIN 0x0008
+};
+
+#define IS_BUCKET(X) ((X) & BUF_BUCKET)
+
+typedef BUFHEAD **SEGMENT;
+
+/* Hash Table Information */
+typedef struct hashhdr { /* Disk resident portion */
+ int magic; /* Magic NO for hash tables */
+ int version; /* Version ID */
+ u_int32_t lorder; /* Byte Order */
+ int bsize; /* Bucket/Page Size */
+ int bshift; /* Bucket shift */
+ int dsize; /* Directory Size */
+ int ssize; /* Segment Size */
+ int sshift; /* Segment shift */
+ int ovfl_point; /* Where overflow pages are being
+ * allocated */
+ int last_freed; /* Last overflow page freed */
+ int max_bucket; /* ID of Maximum bucket in use */
+ int high_mask; /* Mask to modulo into entire table */
+ int low_mask; /* Mask to modulo into lower half of
+ * table */
+ int ffactor; /* Fill factor */
+ int nkeys; /* Number of keys in hash table */
+ int hdrpages; /* Size of table header */
+ int h_charkey; /* value of hash(CHARKEY) */
+#define NCACHED 32 /* number of bit maps and spare
+ * points */
+ int spares[NCACHED];/* spare pages for overflow */
+ u_int16_t bitmaps[NCACHED]; /* address of overflow page
+ * bitmaps */
+} HASHHDR;
+
+typedef struct htab { /* Memory resident data structure */
+ HASHHDR hdr; /* Header */
+ int nsegs; /* Number of allocated segments */
+ int exsegs; /* Number of extra allocated
+ * segments */
+ u_int32_t /* Hash function */
+ (*hash)__P((const void *, size_t));
+ int flags; /* Flag values */
+ int fp; /* File pointer */
+ char *tmp_buf; /* Temporary Buffer for BIG data */
+ char *tmp_key; /* Temporary Buffer for BIG keys */
+ BUFHEAD *cpage; /* Current page */
+ int cbucket; /* Current bucket */
+ int cndx; /* Index of next item on cpage */
+ int errnum; /* Error Number -- for DBM
+ * compatibility */
+ int new_file; /* Indicates if fd is backing store
+ * or no */
+ int save_file; /* Indicates whether we need to flush
+ * file at
+ * exit */
+ u_int32_t *mapp[NCACHED]; /* Pointers to page maps */
+ int nmaps; /* Initial number of bitmaps */
+ int nbufs; /* Number of buffers left to
+ * allocate */
+ BUFHEAD bufhead; /* Header of buffer lru list */
+ SEGMENT *dir; /* Hash Bucket directory */
+} HTAB;
+
+/*
+ * Constants
+ */
+#define MAX_BSIZE 65536 /* 2^16 */
+#define MIN_BUFFERS 6
+#define MINHDRSIZE 512
+#define DEF_BUFSIZE 65536 /* 64 K */
+#define DEF_BUCKET_SIZE 4096
+#define DEF_BUCKET_SHIFT 12 /* log2(BUCKET) */
+#define DEF_SEGSIZE 256
+#define DEF_SEGSIZE_SHIFT 8 /* log2(SEGSIZE) */
+#define DEF_DIRSIZE 256
+#define DEF_FFACTOR 65536
+#define MIN_FFACTOR 4
+#define SPLTMAX 8
+#define CHARKEY "%$sniglet^&"
+#define NUMKEY 1038583
+#define BYTE_SHIFT 3
+#define INT_TO_BYTE 2
+#define INT_BYTE_SHIFT 5
+#define ALL_SET ((u_int32_t)0xFFFFFFFF)
+#define ALL_CLEAR 0
+
+#define PTROF(X) ((BUFHEAD *)((ptrdiff_t)(X)&~0x3))
+#define ISMOD(X) ((u_int32_t)(ptrdiff_t)(X)&0x1)
+#define DOMOD(X) ((X) = (char *)((ptrdiff_t)(X)|0x1))
+#define ISDISK(X) ((u_int32_t)(ptrdiff_t)(X)&0x2)
+#define DODISK(X) ((X) = (char *)((ptrdiff_t)(X)|0x2))
+
+#define BITS_PER_MAP 32
+
+/* Given the address of the beginning of a big map, clear/set the nth bit */
+#define CLRBIT(A, N) ((A)[(N)/BITS_PER_MAP] &= ~(1<<((N)%BITS_PER_MAP)))
+#define SETBIT(A, N) ((A)[(N)/BITS_PER_MAP] |= (1<<((N)%BITS_PER_MAP)))
+#define ISSET(A, N) ((A)[(N)/BITS_PER_MAP] & (1<<((N)%BITS_PER_MAP)))
+
+/* Overflow management */
+/*
+ * Overflow page numbers are allocated per split point. At each doubling of
+ * the table, we can allocate extra pages. So, an overflow page number has
+ * the top 5 bits indicate which split point and the lower 11 bits indicate
+ * which page at that split point is indicated (pages within split points are
+ * numberered starting with 1).
+ */
+
+#define SPLITSHIFT 11
+#define SPLITMASK 0x7FF
+#define SPLITNUM(N) (((u_int32_t)(N)) >> SPLITSHIFT)
+#define OPAGENUM(N) ((N) & SPLITMASK)
+#define OADDR_OF(S,O) ((u_int32_t)((u_int32_t)(S) << SPLITSHIFT) + (O))
+
+#define BUCKET_TO_PAGE(B) \
+ (B) + hashp->HDRPAGES + ((B) ? hashp->SPARES[__hash_log2((B)+1)-1] : 0)
+#define OADDR_TO_PAGE(B) \
+ BUCKET_TO_PAGE ( (1 << SPLITNUM((B))) -1 ) + OPAGENUM((B));
+
+/*
+ * page.h contains a detailed description of the page format.
+ *
+ * Normally, keys and data are accessed from offset tables in the top of
+ * each page which point to the beginning of the key and data. There are
+ * four flag values which may be stored in these offset tables which indicate
+ * the following:
+ *
+ *
+ * OVFLPAGE Rather than a key data pair, this pair contains
+ * the address of an overflow page. The format of
+ * the pair is:
+ * OVERFLOW_PAGE_NUMBER OVFLPAGE
+ *
+ * PARTIAL_KEY This must be the first key/data pair on a page
+ * and implies that page contains only a partial key.
+ * That is, the key is too big to fit on a single page
+ * so it starts on this page and continues on the next.
+ * The format of the page is:
+ * KEY_OFF PARTIAL_KEY OVFL_PAGENO OVFLPAGE
+ *
+ * KEY_OFF -- offset of the beginning of the key
+ * PARTIAL_KEY -- 1
+ * OVFL_PAGENO - page number of the next overflow page
+ * OVFLPAGE -- 0
+ *
+ * FULL_KEY This must be the first key/data pair on the page. It
+ * is used in two cases.
+ *
+ * Case 1:
+ * There is a complete key on the page but no data
+ * (because it wouldn't fit). The next page contains
+ * the data.
+ *
+ * Page format it:
+ * KEY_OFF FULL_KEY OVFL_PAGENO OVFL_PAGE
+ *
+ * KEY_OFF -- offset of the beginning of the key
+ * FULL_KEY -- 2
+ * OVFL_PAGENO - page number of the next overflow page
+ * OVFLPAGE -- 0
+ *
+ * Case 2:
+ * This page contains no key, but part of a large
+ * data field, which is continued on the next page.
+ *
+ * Page format it:
+ * DATA_OFF FULL_KEY OVFL_PAGENO OVFL_PAGE
+ *
+ * KEY_OFF -- offset of the beginning of the data on
+ * this page
+ * FULL_KEY -- 2
+ * OVFL_PAGENO - page number of the next overflow page
+ * OVFLPAGE -- 0
+ *
+ * FULL_KEY_DATA
+ * This must be the first key/data pair on the page.
+ * There are two cases:
+ *
+ * Case 1:
+ * This page contains a key and the beginning of the
+ * data field, but the data field is continued on the
+ * next page.
+ *
+ * Page format is:
+ * KEY_OFF FULL_KEY_DATA OVFL_PAGENO DATA_OFF
+ *
+ * KEY_OFF -- offset of the beginning of the key
+ * FULL_KEY_DATA -- 3
+ * OVFL_PAGENO - page number of the next overflow page
+ * DATA_OFF -- offset of the beginning of the data
+ *
+ * Case 2:
+ * This page contains the last page of a big data pair.
+ * There is no key, only the tail end of the data
+ * on this page.
+ *
+ * Page format is:
+ * DATA_OFF FULL_KEY_DATA <OVFL_PAGENO> <OVFLPAGE>
+ *
+ * DATA_OFF -- offset of the beginning of the data on
+ * this page
+ * FULL_KEY_DATA -- 3
+ * OVFL_PAGENO - page number of the next overflow page
+ * OVFLPAGE -- 0
+ *
+ * OVFL_PAGENO and OVFLPAGE are optional (they are
+ * not present if there is no next page).
+ */
+
+#define OVFLPAGE 0
+#define PARTIAL_KEY 1
+#define FULL_KEY 2
+#define FULL_KEY_DATA 3
+#define REAL_KEY 4
+
+/* Short hands for accessing structure */
+#define BSIZE hdr.bsize
+#define BSHIFT hdr.bshift
+#define DSIZE hdr.dsize
+#define SGSIZE hdr.ssize
+#define SSHIFT hdr.sshift
+#define LORDER hdr.lorder
+#define OVFL_POINT hdr.ovfl_point
+#define LAST_FREED hdr.last_freed
+#define MAX_BUCKET hdr.max_bucket
+#define FFACTOR hdr.ffactor
+#define HIGH_MASK hdr.high_mask
+#define LOW_MASK hdr.low_mask
+#define NKEYS hdr.nkeys
+#define HDRPAGES hdr.hdrpages
+#define SPARES hdr.spares
+#define BITMAPS hdr.bitmaps
+#define VERSION hdr.version
+#define MAGIC hdr.magic
+#define NEXT_FREE hdr.next_free
+#define H_CHARKEY hdr.h_charkey
diff --git a/trunk/main/db1-ast/hash/hash_bigkey.c b/trunk/main/db1-ast/hash/hash_bigkey.c
new file mode 100644
index 000000000..daa8c1f17
--- /dev/null
+++ b/trunk/main/db1-ast/hash/hash_bigkey.c
@@ -0,0 +1,668 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Margo Seltzer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)hash_bigkey.c 8.3 (Berkeley) 5/31/94";
+#endif /* LIBC_SCCS and not lint */
+
+/*
+ * PACKAGE: hash
+ * DESCRIPTION:
+ * Big key/data handling for the hashing package.
+ *
+ * ROUTINES:
+ * External
+ * __big_keydata
+ * __big_split
+ * __big_insert
+ * __big_return
+ * __big_delete
+ * __find_last_page
+ * Internal
+ * collect_key
+ * collect_data
+ */
+
+#include <sys/param.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef DEBUG
+#include <assert.h>
+#endif
+
+#include "../include/db.h"
+#include "hash.h"
+#include "page.h"
+#include "extern.h"
+
+static int collect_key __P((HTAB *, BUFHEAD *, int, DBT *, int));
+static int collect_data __P((HTAB *, BUFHEAD *, int, int));
+
+/*
+ * Big_insert
+ *
+ * You need to do an insert and the key/data pair is too big
+ *
+ * Returns:
+ * 0 ==> OK
+ *-1 ==> ERROR
+ */
+extern int
+__big_insert(hashp, bufp, key, val)
+ HTAB *hashp;
+ BUFHEAD *bufp;
+ const DBT *key, *val;
+{
+ register u_int16_t *p;
+ int key_size, n, val_size;
+ u_int16_t space, move_bytes, off;
+ char *cp, *key_data, *val_data;
+
+ cp = bufp->page; /* Character pointer of p. */
+ p = (u_int16_t *)cp;
+
+ key_data = (char *)key->data;
+ key_size = key->size;
+ val_data = (char *)val->data;
+ val_size = val->size;
+
+ /* First move the Key */
+ for (space = FREESPACE(p) - BIGOVERHEAD; key_size;
+ space = FREESPACE(p) - BIGOVERHEAD) {
+ move_bytes = MIN(space, key_size);
+ off = OFFSET(p) - move_bytes;
+ memmove(cp + off, key_data, move_bytes);
+ key_size -= move_bytes;
+ key_data += move_bytes;
+ n = p[0];
+ p[++n] = off;
+ p[0] = ++n;
+ FREESPACE(p) = off - PAGE_META(n);
+ OFFSET(p) = off;
+ p[n] = PARTIAL_KEY;
+ bufp = __add_ovflpage(hashp, bufp);
+ if (!bufp)
+ return (-1);
+ n = p[0];
+ if (!key_size) {
+ if (FREESPACE(p)) {
+ move_bytes = MIN(FREESPACE(p), val_size);
+ off = OFFSET(p) - move_bytes;
+ p[n] = off;
+ memmove(cp + off, val_data, move_bytes);
+ val_data += move_bytes;
+ val_size -= move_bytes;
+ p[n - 2] = FULL_KEY_DATA;
+ FREESPACE(p) = FREESPACE(p) - move_bytes;
+ OFFSET(p) = off;
+ } else
+ p[n - 2] = FULL_KEY;
+ }
+ p = (u_int16_t *)bufp->page;
+ cp = bufp->page;
+ bufp->flags |= BUF_MOD;
+ }
+
+ /* Now move the data */
+ for (space = FREESPACE(p) - BIGOVERHEAD; val_size;
+ space = FREESPACE(p) - BIGOVERHEAD) {
+ move_bytes = MIN(space, val_size);
+ /*
+ * Here's the hack to make sure that if the data ends on the
+ * same page as the key ends, FREESPACE is at least one.
+ */
+ if ((int) space == val_size && (size_t) val_size == val->size)
+ move_bytes--;
+ off = OFFSET(p) - move_bytes;
+ memmove(cp + off, val_data, move_bytes);
+ val_size -= move_bytes;
+ val_data += move_bytes;
+ n = p[0];
+ p[++n] = off;
+ p[0] = ++n;
+ FREESPACE(p) = off - PAGE_META(n);
+ OFFSET(p) = off;
+ if (val_size) {
+ p[n] = FULL_KEY;
+ bufp = __add_ovflpage(hashp, bufp);
+ if (!bufp)
+ return (-1);
+ cp = bufp->page;
+ p = (u_int16_t *)cp;
+ } else
+ p[n] = FULL_KEY_DATA;
+ bufp->flags |= BUF_MOD;
+ }
+ return (0);
+}
+
+/*
+ * Called when bufp's page contains a partial key (index should be 1)
+ *
+ * All pages in the big key/data pair except bufp are freed. We cannot
+ * free bufp because the page pointing to it is lost and we can't get rid
+ * of its pointer.
+ *
+ * Returns:
+ * 0 => OK
+ *-1 => ERROR
+ */
+extern int
+__big_delete(hashp, bufp)
+ HTAB *hashp;
+ BUFHEAD *bufp;
+{
+ register BUFHEAD *last_bfp, *rbufp;
+ u_int16_t *bp, pageno;
+ int key_done, n;
+
+ rbufp = bufp;
+ last_bfp = NULL;
+ bp = (u_int16_t *)bufp->page;
+ pageno = 0;
+ key_done = 0;
+
+ while (!key_done || (bp[2] != FULL_KEY_DATA)) {
+ if (bp[2] == FULL_KEY || bp[2] == FULL_KEY_DATA)
+ key_done = 1;
+
+ /*
+ * If there is freespace left on a FULL_KEY_DATA page, then
+ * the data is short and fits entirely on this page, and this
+ * is the last page.
+ */
+ if (bp[2] == FULL_KEY_DATA && FREESPACE(bp))
+ break;
+ pageno = bp[bp[0] - 1];
+ rbufp->flags |= BUF_MOD;
+ rbufp = __get_buf(hashp, pageno, rbufp, 0);
+ if (last_bfp)
+ __free_ovflpage(hashp, last_bfp);
+ last_bfp = rbufp;
+ if (!rbufp)
+ return (-1); /* Error. */
+ bp = (u_int16_t *)rbufp->page;
+ }
+
+ /*
+ * If we get here then rbufp points to the last page of the big
+ * key/data pair. Bufp points to the first one -- it should now be
+ * empty pointing to the next page after this pair. Can't free it
+ * because we don't have the page pointing to it.
+ */
+
+ /* This is information from the last page of the pair. */
+ n = bp[0];
+ pageno = bp[n - 1];
+
+ /* Now, bp is the first page of the pair. */
+ bp = (u_int16_t *)bufp->page;
+ if (n > 2) {
+ /* There is an overflow page. */
+ bp[1] = pageno;
+ bp[2] = OVFLPAGE;
+ bufp->ovfl = rbufp->ovfl;
+ } else
+ /* This is the last page. */
+ bufp->ovfl = NULL;
+ n -= 2;
+ bp[0] = n;
+ FREESPACE(bp) = hashp->BSIZE - PAGE_META(n);
+ OFFSET(bp) = hashp->BSIZE - 1;
+
+ bufp->flags |= BUF_MOD;
+ if (rbufp)
+ __free_ovflpage(hashp, rbufp);
+ if (last_bfp && last_bfp != rbufp)
+ __free_ovflpage(hashp, last_bfp);
+
+ hashp->NKEYS--;
+ return (0);
+}
+/*
+ * Returns:
+ * 0 = key not found
+ * -1 = get next overflow page
+ * -2 means key not found and this is big key/data
+ * -3 error
+ */
+extern int
+__find_bigpair(hashp, bufp, ndx, key, size)
+ HTAB *hashp;
+ BUFHEAD *bufp;
+ int ndx;
+ char *key;
+ int size;
+{
+ register u_int16_t *bp;
+ register char *p;
+ int ksize;
+ u_int16_t bytes;
+ char *kkey;
+
+ bp = (u_int16_t *)bufp->page;
+ p = bufp->page;
+ ksize = size;
+ kkey = key;
+
+ for (bytes = hashp->BSIZE - bp[ndx];
+ bytes <= size && bp[ndx + 1] == PARTIAL_KEY;
+ bytes = hashp->BSIZE - bp[ndx]) {
+ if (memcmp(p + bp[ndx], kkey, bytes))
+ return (-2);
+ kkey += bytes;
+ ksize -= bytes;
+ bufp = __get_buf(hashp, bp[ndx + 2], bufp, 0);
+ if (!bufp)
+ return (-3);
+ p = bufp->page;
+ bp = (u_int16_t *)p;
+ ndx = 1;
+ }
+
+ if (bytes != ksize || memcmp(p + bp[ndx], kkey, bytes)) {
+#ifdef HASH_STATISTICS
+ ++hash_collisions;
+#endif
+ return (-2);
+ } else
+ return (ndx);
+}
+
+/*
+ * Given the buffer pointer of the first overflow page of a big pair,
+ * find the end of the big pair
+ *
+ * This will set bpp to the buffer header of the last page of the big pair.
+ * It will return the pageno of the overflow page following the last page
+ * of the pair; 0 if there isn't any (i.e. big pair is the last key in the
+ * bucket)
+ */
+extern u_int16_t
+__find_last_page(hashp, bpp)
+ HTAB *hashp;
+ BUFHEAD **bpp;
+{
+ BUFHEAD *bufp;
+ u_int16_t *bp, pageno;
+ int n;
+
+ bufp = *bpp;
+ bp = (u_int16_t *)bufp->page;
+ for (;;) {
+ n = bp[0];
+
+ /*
+ * This is the last page if: the tag is FULL_KEY_DATA and
+ * either only 2 entries OVFLPAGE marker is explicit there
+ * is freespace on the page.
+ */
+ if (bp[2] == FULL_KEY_DATA &&
+ ((n == 2) || (bp[n] == OVFLPAGE) || (FREESPACE(bp))))
+ break;
+
+ pageno = bp[n - 1];
+ bufp = __get_buf(hashp, pageno, bufp, 0);
+ if (!bufp)
+ return (0); /* Need to indicate an error! */
+ bp = (u_int16_t *)bufp->page;
+ }
+
+ *bpp = bufp;
+ if (bp[0] > 2)
+ return (bp[3]);
+ else
+ return (0);
+}
+
+/*
+ * Return the data for the key/data pair that begins on this page at this
+ * index (index should always be 1).
+ */
+extern int
+__big_return(hashp, bufp, ndx, val, set_current)
+ HTAB *hashp;
+ BUFHEAD *bufp;
+ int ndx;
+ DBT *val;
+ int set_current;
+{
+ BUFHEAD *save_p;
+ u_int16_t *bp, len, off, save_addr;
+ char *tp;
+
+ bp = (u_int16_t *)bufp->page;
+ while (bp[ndx + 1] == PARTIAL_KEY) {
+ bufp = __get_buf(hashp, bp[bp[0] - 1], bufp, 0);
+ if (!bufp)
+ return (-1);
+ bp = (u_int16_t *)bufp->page;
+ ndx = 1;
+ }
+
+ if (bp[ndx + 1] == FULL_KEY) {
+ bufp = __get_buf(hashp, bp[bp[0] - 1], bufp, 0);
+ if (!bufp)
+ return (-1);
+ bp = (u_int16_t *)bufp->page;
+ save_p = bufp;
+ save_addr = save_p->addr;
+ off = bp[1];
+ len = 0;
+ } else
+ if (!FREESPACE(bp)) {
+ /*
+ * This is a hack. We can't distinguish between
+ * FULL_KEY_DATA that contains complete data or
+ * incomplete data, so we require that if the data
+ * is complete, there is at least 1 byte of free
+ * space left.
+ */
+ off = bp[bp[0]];
+ len = bp[1] - off;
+ save_p = bufp;
+ save_addr = bufp->addr;
+ bufp = __get_buf(hashp, bp[bp[0] - 1], bufp, 0);
+ if (!bufp)
+ return (-1);
+ bp = (u_int16_t *)bufp->page;
+ } else {
+ /* The data is all on one page. */
+ tp = (char *)bp;
+ off = bp[bp[0]];
+ val->data = (u_char *)tp + off;
+ val->size = bp[1] - off;
+ if (set_current) {
+ if (bp[0] == 2) { /* No more buckets in
+ * chain */
+ hashp->cpage = NULL;
+ hashp->cbucket++;
+ hashp->cndx = 1;
+ } else {
+ hashp->cpage = __get_buf(hashp,
+ bp[bp[0] - 1], bufp, 0);
+ if (!hashp->cpage)
+ return (-1);
+ hashp->cndx = 1;
+ if (!((u_int16_t *)
+ hashp->cpage->page)[0]) {
+ hashp->cbucket++;
+ hashp->cpage = NULL;
+ }
+ }
+ }
+ return (0);
+ }
+
+ val->size = collect_data(hashp, bufp, (int)len, set_current);
+ if (val->size == (size_t) -1)
+ return (-1);
+ if (save_p->addr != save_addr) {
+ /* We are pretty short on buffers. */
+ errno = EINVAL; /* OUT OF BUFFERS */
+ return (-1);
+ }
+ memmove(hashp->tmp_buf, (save_p->page) + off, len);
+ val->data = (u_char *)hashp->tmp_buf;
+ return (0);
+}
+/*
+ * Count how big the total datasize is by recursing through the pages. Then
+ * allocate a buffer and copy the data as you recurse up.
+ */
+static int
+collect_data(hashp, bufp, len, set)
+ HTAB *hashp;
+ BUFHEAD *bufp;
+ int len, set;
+{
+ register u_int16_t *bp;
+ register char *p;
+ BUFHEAD *xbp;
+ u_int16_t save_addr;
+ int mylen, totlen;
+
+ p = bufp->page;
+ bp = (u_int16_t *)p;
+ mylen = hashp->BSIZE - bp[1];
+ save_addr = bufp->addr;
+
+ if (bp[2] == FULL_KEY_DATA) { /* End of Data */
+ totlen = len + mylen;
+ if (hashp->tmp_buf)
+ free(hashp->tmp_buf);
+ if ((hashp->tmp_buf = (char *)malloc(totlen)) == NULL)
+ return (-1);
+ if (set) {
+ hashp->cndx = 1;
+ if (bp[0] == 2) { /* No more buckets in chain */
+ hashp->cpage = NULL;
+ hashp->cbucket++;
+ } else {
+ hashp->cpage =
+ __get_buf(hashp, bp[bp[0] - 1], bufp, 0);
+ if (!hashp->cpage)
+ return (-1);
+ else if (!((u_int16_t *)hashp->cpage->page)[0]) {
+ hashp->cbucket++;
+ hashp->cpage = NULL;
+ }
+ }
+ }
+ } else {
+ xbp = __get_buf(hashp, bp[bp[0] - 1], bufp, 0);
+ if (!xbp || ((totlen =
+ collect_data(hashp, xbp, len + mylen, set)) < 1))
+ return (-1);
+ }
+ if (bufp->addr != save_addr) {
+ errno = EINVAL; /* Out of buffers. */
+ return (-1);
+ }
+ memmove(&hashp->tmp_buf[len], (bufp->page) + bp[1], mylen);
+ return (totlen);
+}
+
+/*
+ * Fill in the key and data for this big pair.
+ */
+extern int
+__big_keydata(hashp, bufp, key, val, set)
+ HTAB *hashp;
+ BUFHEAD *bufp;
+ DBT *key, *val;
+ int set;
+{
+ key->size = collect_key(hashp, bufp, 0, val, set);
+ if (key->size == (size_t) -1)
+ return (-1);
+ key->data = (u_char *)hashp->tmp_key;
+ return (0);
+}
+
+/*
+ * Count how big the total key size is by recursing through the pages. Then
+ * collect the data, allocate a buffer and copy the key as you recurse up.
+ */
+static int
+collect_key(hashp, bufp, len, val, set)
+ HTAB *hashp;
+ BUFHEAD *bufp;
+ int len;
+ DBT *val;
+ int set;
+{
+ BUFHEAD *xbp;
+ char *p;
+ int mylen, totlen;
+ u_int16_t *bp, save_addr;
+
+ p = bufp->page;
+ bp = (u_int16_t *)p;
+ mylen = hashp->BSIZE - bp[1];
+
+ save_addr = bufp->addr;
+ totlen = len + mylen;
+ if (bp[2] == FULL_KEY || bp[2] == FULL_KEY_DATA) { /* End of Key. */
+ if (hashp->tmp_key != NULL)
+ free(hashp->tmp_key);
+ if ((hashp->tmp_key = (char *)malloc(totlen)) == NULL)
+ return (-1);
+ if (__big_return(hashp, bufp, 1, val, set))
+ return (-1);
+ } else {
+ xbp = __get_buf(hashp, bp[bp[0] - 1], bufp, 0);
+ if (!xbp || ((totlen =
+ collect_key(hashp, xbp, totlen, val, set)) < 1))
+ return (-1);
+ }
+ if (bufp->addr != save_addr) {
+ errno = EINVAL; /* MIS -- OUT OF BUFFERS */
+ return (-1);
+ }
+ memmove(&hashp->tmp_key[len], (bufp->page) + bp[1], mylen);
+ return (totlen);
+}
+
+/*
+ * Returns:
+ * 0 => OK
+ * -1 => error
+ */
+extern int
+__big_split(hashp, op, np, big_keyp, addr, obucket, ret)
+ HTAB *hashp;
+ BUFHEAD *op; /* Pointer to where to put keys that go in old bucket */
+ BUFHEAD *np; /* Pointer to new bucket page */
+ /* Pointer to first page containing the big key/data */
+ BUFHEAD *big_keyp;
+ int addr; /* Address of big_keyp */
+ u_int32_t obucket;/* Old Bucket */
+ SPLIT_RETURN *ret;
+{
+ register BUFHEAD *tmpp;
+ register u_int16_t *tp;
+ BUFHEAD *bp;
+ DBT key, val;
+ u_int32_t change;
+ u_int16_t free_space, n, off;
+
+ bp = big_keyp;
+
+ /* Now figure out where the big key/data goes */
+ if (__big_keydata(hashp, big_keyp, &key, &val, 0))
+ return (-1);
+ change = (__call_hash(hashp, key.data, key.size) != obucket);
+
+ if ((ret->next_addr = __find_last_page(hashp, &big_keyp))) {
+ if (!(ret->nextp =
+ __get_buf(hashp, ret->next_addr, big_keyp, 0)))
+ return (-1);;
+ } else
+ ret->nextp = NULL;
+
+ /* Now make one of np/op point to the big key/data pair */
+#ifdef DEBUG
+ assert(np->ovfl == NULL);
+#endif
+ if (change)
+ tmpp = np;
+ else
+ tmpp = op;
+
+ tmpp->flags |= BUF_MOD;
+#ifdef DEBUG1
+ (void)fprintf(stderr,
+ "BIG_SPLIT: %d->ovfl was %d is now %d\n", tmpp->addr,
+ (tmpp->ovfl ? tmpp->ovfl->addr : 0), (bp ? bp->addr : 0));
+#endif
+ tmpp->ovfl = bp; /* one of op/np point to big_keyp */
+ tp = (u_int16_t *)tmpp->page;
+#ifdef DEBUG
+ assert(FREESPACE(tp) >= OVFLSIZE);
+#endif
+ n = tp[0];
+ off = OFFSET(tp);
+ free_space = FREESPACE(tp);
+ tp[++n] = (u_int16_t)addr;
+ tp[++n] = OVFLPAGE;
+ tp[0] = n;
+ OFFSET(tp) = off;
+ FREESPACE(tp) = free_space - OVFLSIZE;
+
+ /*
+ * Finally, set the new and old return values. BIG_KEYP contains a
+ * pointer to the last page of the big key_data pair. Make sure that
+ * big_keyp has no following page (2 elements) or create an empty
+ * following page.
+ */
+
+ ret->newp = np;
+ ret->oldp = op;
+
+ tp = (u_int16_t *)big_keyp->page;
+ big_keyp->flags |= BUF_MOD;
+ if (tp[0] > 2) {
+ /*
+ * There may be either one or two offsets on this page. If
+ * there is one, then the overflow page is linked on normally
+ * and tp[4] is OVFLPAGE. If there are two, tp[4] contains
+ * the second offset and needs to get stuffed in after the
+ * next overflow page is added.
+ */
+ n = tp[4];
+ free_space = FREESPACE(tp);
+ off = OFFSET(tp);
+ tp[0] -= 2;
+ FREESPACE(tp) = free_space + OVFLSIZE;
+ OFFSET(tp) = off;
+ tmpp = __add_ovflpage(hashp, big_keyp);
+ if (!tmpp)
+ return (-1);
+ tp[4] = n;
+ } else
+ tmpp = big_keyp;
+
+ if (change)
+ ret->newp = tmpp;
+ else
+ ret->oldp = tmpp;
+ return (0);
+}
diff --git a/trunk/main/db1-ast/hash/hash_buf.c b/trunk/main/db1-ast/hash/hash_buf.c
new file mode 100644
index 000000000..063dd8229
--- /dev/null
+++ b/trunk/main/db1-ast/hash/hash_buf.c
@@ -0,0 +1,355 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Margo Seltzer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)hash_buf.c 8.5 (Berkeley) 7/15/94";
+#endif /* LIBC_SCCS and not lint */
+
+/*
+ * PACKAGE: hash
+ *
+ * DESCRIPTION:
+ * Contains buffer management
+ *
+ * ROUTINES:
+ * External
+ * __buf_init
+ * __get_buf
+ * __buf_free
+ * __reclaim_buf
+ * Internal
+ * newbuf
+ */
+
+#include <sys/param.h>
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef DEBUG
+#include <assert.h>
+#endif
+
+#include "../include/db.h"
+#include "hash.h"
+#include "page.h"
+#include "extern.h"
+
+static BUFHEAD *newbuf __P((HTAB *, u_int32_t, BUFHEAD *));
+
+/* Unlink B from its place in the lru */
+#define BUF_REMOVE(B) { \
+ (B)->prev->next = (B)->next; \
+ (B)->next->prev = (B)->prev; \
+}
+
+/* Insert B after P */
+#define BUF_INSERT(B, P) { \
+ (B)->next = (P)->next; \
+ (B)->prev = (P); \
+ (P)->next = (B); \
+ (B)->next->prev = (B); \
+}
+
+#define MRU hashp->bufhead.next
+#define LRU hashp->bufhead.prev
+
+#define MRU_INSERT(B) BUF_INSERT((B), &hashp->bufhead)
+#define LRU_INSERT(B) BUF_INSERT((B), LRU)
+
+/*
+ * We are looking for a buffer with address "addr". If prev_bp is NULL, then
+ * address is a bucket index. If prev_bp is not NULL, then it points to the
+ * page previous to an overflow page that we are trying to find.
+ *
+ * CAVEAT: The buffer header accessed via prev_bp's ovfl field may no longer
+ * be valid. Therefore, you must always verify that its address matches the
+ * address you are seeking.
+ */
+extern BUFHEAD *
+__get_buf(hashp, addr, prev_bp, newpage)
+ HTAB *hashp;
+ u_int32_t addr;
+ BUFHEAD *prev_bp;
+ int newpage; /* If prev_bp set, indicates a new overflow page. */
+{
+ register BUFHEAD *bp;
+ register u_int32_t is_disk_mask;
+ register int is_disk, segment_ndx = 0;
+ SEGMENT segp = 0;
+
+ is_disk = 0;
+ is_disk_mask = 0;
+ if (prev_bp) {
+ bp = prev_bp->ovfl;
+ if (!bp || (bp->addr != addr))
+ bp = NULL;
+ if (!newpage)
+ is_disk = BUF_DISK;
+ } else {
+ /* Grab buffer out of directory */
+ segment_ndx = addr & (hashp->SGSIZE - 1);
+
+ /* valid segment ensured by __call_hash() */
+ segp = hashp->dir[addr >> hashp->SSHIFT];
+#ifdef DEBUG
+ assert(segp != NULL);
+#endif
+ bp = PTROF(segp[segment_ndx]);
+ is_disk_mask = ISDISK(segp[segment_ndx]);
+ is_disk = is_disk_mask || !hashp->new_file;
+ }
+
+ if (!bp) {
+ bp = newbuf(hashp, addr, prev_bp);
+ if (!bp ||
+ __get_page(hashp, bp->page, addr, !prev_bp, is_disk, 0))
+ return (NULL);
+ if (!prev_bp)
+ segp[segment_ndx] =
+ (BUFHEAD *)((ptrdiff_t)bp | is_disk_mask);
+ } else {
+ BUF_REMOVE(bp);
+ MRU_INSERT(bp);
+ }
+ return (bp);
+}
+
+/*
+ * We need a buffer for this page. Either allocate one, or evict a resident
+ * one (if we have as many buffers as we're allowed) and put this one in.
+ *
+ * If newbuf finds an error (returning NULL), it also sets errno.
+ */
+static BUFHEAD *
+newbuf(hashp, addr, prev_bp)
+ HTAB *hashp;
+ u_int32_t addr;
+ BUFHEAD *prev_bp;
+{
+ register BUFHEAD *bp; /* The buffer we're going to use */
+ register BUFHEAD *xbp; /* Temp pointer */
+ register BUFHEAD *next_xbp;
+ SEGMENT segp;
+ int segment_ndx;
+ u_int16_t oaddr, *shortp;
+
+ oaddr = 0;
+ bp = LRU;
+ /*
+ * If LRU buffer is pinned, the buffer pool is too small. We need to
+ * allocate more buffers.
+ */
+ if (hashp->nbufs || (bp->flags & BUF_PIN)) {
+ /* Allocate a new one */
+ if ((bp = (BUFHEAD *)malloc(sizeof(BUFHEAD))) == NULL)
+ return (NULL);
+#ifdef PURIFY
+ memset(bp, 0xff, sizeof(BUFHEAD));
+#endif
+ if ((bp->page = (char *)malloc(hashp->BSIZE)) == NULL) {
+ free(bp);
+ return (NULL);
+ }
+#ifdef PURIFY
+ memset(bp->page, 0xff, hashp->BSIZE);
+#endif
+ if (hashp->nbufs)
+ hashp->nbufs--;
+ } else {
+ /* Kick someone out */
+ BUF_REMOVE(bp);
+ /*
+ * If this is an overflow page with addr 0, it's already been
+ * flushed back in an overflow chain and initialized.
+ */
+ if ((bp->addr != 0) || (bp->flags & BUF_BUCKET)) {
+ /*
+ * Set oaddr before __put_page so that you get it
+ * before bytes are swapped.
+ */
+ shortp = (u_int16_t *)bp->page;
+ if (shortp[0])
+ oaddr = shortp[shortp[0] - 1];
+ if ((bp->flags & BUF_MOD) && __put_page(hashp, bp->page,
+ bp->addr, (int)IS_BUCKET(bp->flags), 0))
+ return (NULL);
+ /*
+ * Update the pointer to this page (i.e. invalidate it).
+ *
+ * If this is a new file (i.e. we created it at open
+ * time), make sure that we mark pages which have been
+ * written to disk so we retrieve them from disk later,
+ * rather than allocating new pages.
+ */
+ if (IS_BUCKET(bp->flags)) {
+ segment_ndx = bp->addr & (hashp->SGSIZE - 1);
+ segp = hashp->dir[bp->addr >> hashp->SSHIFT];
+#ifdef DEBUG
+ assert(segp != NULL);
+#endif
+
+ if (hashp->new_file &&
+ ((bp->flags & BUF_MOD) ||
+ ISDISK(segp[segment_ndx])))
+ segp[segment_ndx] = (BUFHEAD *)BUF_DISK;
+ else
+ segp[segment_ndx] = NULL;
+ }
+ /*
+ * Since overflow pages can only be access by means of
+ * their bucket, free overflow pages associated with
+ * this bucket.
+ */
+ for (xbp = bp; xbp->ovfl;) {
+ next_xbp = xbp->ovfl;
+ xbp->ovfl = 0;
+ xbp = next_xbp;
+
+ /* Check that ovfl pointer is up date. */
+ if (IS_BUCKET(xbp->flags) ||
+ (oaddr != xbp->addr))
+ break;
+
+ shortp = (u_int16_t *)xbp->page;
+ if (shortp[0])
+ /* set before __put_page */
+ oaddr = shortp[shortp[0] - 1];
+ if ((xbp->flags & BUF_MOD) && __put_page(hashp,
+ xbp->page, xbp->addr, 0, 0))
+ return (NULL);
+ xbp->addr = 0;
+ xbp->flags = 0;
+ BUF_REMOVE(xbp);
+ LRU_INSERT(xbp);
+ }
+ }
+ }
+
+ /* Now assign this buffer */
+ bp->addr = addr;
+#ifdef DEBUG1
+ (void)fprintf(stderr, "NEWBUF1: %d->ovfl was %d is now %d\n",
+ bp->addr, (bp->ovfl ? bp->ovfl->addr : 0), 0);
+#endif
+ bp->ovfl = NULL;
+ if (prev_bp) {
+ /*
+ * If prev_bp is set, this is an overflow page, hook it in to
+ * the buffer overflow links.
+ */
+#ifdef DEBUG1
+ (void)fprintf(stderr, "NEWBUF2: %d->ovfl was %d is now %d\n",
+ prev_bp->addr, (prev_bp->ovfl ? bp->ovfl->addr : 0),
+ (bp ? bp->addr : 0));
+#endif
+ prev_bp->ovfl = bp;
+ bp->flags = 0;
+ } else
+ bp->flags = BUF_BUCKET;
+ MRU_INSERT(bp);
+ return (bp);
+}
+
+extern void
+__buf_init(hashp, nbytes)
+ HTAB *hashp;
+ int nbytes;
+{
+ BUFHEAD *bfp;
+ int npages;
+
+ bfp = &(hashp->bufhead);
+ npages = (nbytes + hashp->BSIZE - 1) >> hashp->BSHIFT;
+ npages = MAX(npages, MIN_BUFFERS);
+
+ hashp->nbufs = npages;
+ bfp->next = bfp;
+ bfp->prev = bfp;
+ /*
+ * This space is calloc'd so these are already null.
+ *
+ * bfp->ovfl = NULL;
+ * bfp->flags = 0;
+ * bfp->page = NULL;
+ * bfp->addr = 0;
+ */
+}
+
+extern int
+__buf_free(hashp, do_free, to_disk)
+ HTAB *hashp;
+ int do_free, to_disk;
+{
+ BUFHEAD *bp;
+
+ /* Need to make sure that buffer manager has been initialized */
+ if (!LRU)
+ return (0);
+ for (bp = LRU; bp != &hashp->bufhead;) {
+ /* Check that the buffer is valid */
+ if (bp->addr || IS_BUCKET(bp->flags)) {
+ if (to_disk && (bp->flags & BUF_MOD) &&
+ __put_page(hashp, bp->page,
+ bp->addr, IS_BUCKET(bp->flags), 0))
+ return (-1);
+ }
+ /* Check if we are freeing stuff */
+ if (do_free) {
+ if (bp->page)
+ free(bp->page);
+ BUF_REMOVE(bp);
+ free(bp);
+ bp = LRU;
+ } else
+ bp = bp->prev;
+ }
+ return (0);
+}
+
+extern void
+__reclaim_buf(hashp, bp)
+ HTAB *hashp;
+ BUFHEAD *bp;
+{
+ bp->ovfl = 0;
+ bp->addr = 0;
+ bp->flags = 0;
+ BUF_REMOVE(bp);
+ LRU_INSERT(bp);
+}
diff --git a/trunk/main/db1-ast/hash/hash_func.c b/trunk/main/db1-ast/hash/hash_func.c
new file mode 100644
index 000000000..4d7907b57
--- /dev/null
+++ b/trunk/main/db1-ast/hash/hash_func.c
@@ -0,0 +1,225 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Margo Seltzer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)hash_func.c 8.2 (Berkeley) 2/21/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+
+#include "../include/db.h"
+#include "hash.h"
+#include "page.h"
+#include "extern.h"
+
+/* only one of these can be defined */
+/* #define HASH1_EJB 1 */
+/* #define HASH2_PHONG 1 */
+/* #define HASH3_SDBM 1 */
+#define HASH4_TOREK 1
+
+static u_int32_t hashfunc __P((const void *, size_t));
+
+/* Global default hash function */
+u_int32_t (*__default_hash) __P((const void *, size_t)) = hashfunc;
+
+/*
+ * HASH FUNCTIONS
+ *
+ * Assume that we've already split the bucket to which this key hashes,
+ * calculate that bucket, and check that in fact we did already split it.
+ *
+ * This came from ejb's hsearch.
+ */
+
+#ifdef HASH1_EJB
+
+#define PRIME1 37
+#define PRIME2 1048583
+
+static u_int32_t
+hashfunc(keyarg, len)
+ const void *keyarg;
+ register size_t len;
+{
+ register const u_char *key;
+ register u_int32_t h;
+
+ /* Convert string to integer */
+ for (key = keyarg, h = 0; len--;)
+ h = h * PRIME1 ^ (*key++ - ' ');
+ h %= PRIME2;
+ return (h);
+}
+
+#endif
+
+#ifdef HASH2_PHONG
+/*
+ * Phong's linear congruential hash
+ */
+#define dcharhash(h, c) ((h) = 0x63c63cd9*(h) + 0x9c39c33d + (c))
+
+static u_int32_t
+hashfunc(keyarg, len)
+ const void *keyarg;
+ size_t len;
+{
+ register const u_char *e, *key;
+ register u_int32_t h;
+ register u_char c;
+
+ key = keyarg;
+ e = key + len;
+ for (h = 0; key != e;) {
+ c = *key++;
+ if (!c && key > e)
+ break;
+ dcharhash(h, c);
+ }
+ return (h);
+}
+#endif
+
+#ifdef HASH3_SDBM
+/*
+ * This is INCREDIBLY ugly, but fast. We break the string up into 8 byte
+ * units. On the first time through the loop we get the "leftover bytes"
+ * (strlen % 8). On every other iteration, we perform 8 HASHC's so we handle
+ * all 8 bytes. Essentially, this saves us 7 cmp & branch instructions. If
+ * this routine is heavily used enough, it's worth the ugly coding.
+ *
+ * OZ's original sdbm hash
+ */
+static u_int32_t
+hashfunc(keyarg, len)
+ const void *keyarg;
+ register size_t len;
+{
+ register const u_char *key;
+ register size_t loop;
+ register u_int32_t h;
+
+#define HASHC h = *key++ + 65599 * h
+
+ h = 0;
+ key = keyarg;
+ if (len > 0) {
+ loop = (len + 8 - 1) >> 3;
+
+ switch (len & (8 - 1)) {
+ case 0:
+ do {
+ HASHC;
+ /* FALLTHROUGH */
+ case 7:
+ HASHC;
+ /* FALLTHROUGH */
+ case 6:
+ HASHC;
+ /* FALLTHROUGH */
+ case 5:
+ HASHC;
+ /* FALLTHROUGH */
+ case 4:
+ HASHC;
+ /* FALLTHROUGH */
+ case 3:
+ HASHC;
+ /* FALLTHROUGH */
+ case 2:
+ HASHC;
+ /* FALLTHROUGH */
+ case 1:
+ HASHC;
+ } while (--loop);
+ }
+ }
+ return (h);
+}
+#endif
+
+#ifdef HASH4_TOREK
+/* Hash function from Chris Torek. */
+static u_int32_t
+hashfunc(keyarg, len)
+ const void *keyarg;
+ register size_t len;
+{
+ register const u_char *key;
+ register size_t loop;
+ register u_int32_t h;
+
+#define HASH4a h = (h << 5) - h + *key++;
+#define HASH4b h = (h << 5) + h + *key++;
+#define HASH4 HASH4b
+
+ h = 0;
+ key = keyarg;
+ if (len > 0) {
+ loop = (len + 8 - 1) >> 3;
+
+ switch (len & (8 - 1)) {
+ case 0:
+ do {
+ HASH4;
+ /* FALLTHROUGH */
+ case 7:
+ HASH4;
+ /* FALLTHROUGH */
+ case 6:
+ HASH4;
+ /* FALLTHROUGH */
+ case 5:
+ HASH4;
+ /* FALLTHROUGH */
+ case 4:
+ HASH4;
+ /* FALLTHROUGH */
+ case 3:
+ HASH4;
+ /* FALLTHROUGH */
+ case 2:
+ HASH4;
+ /* FALLTHROUGH */
+ case 1:
+ HASH4;
+ } while (--loop);
+ }
+ }
+ return (h);
+}
+#endif
diff --git a/trunk/main/db1-ast/hash/hash_log2.c b/trunk/main/db1-ast/hash/hash_log2.c
new file mode 100644
index 000000000..b86655d47
--- /dev/null
+++ b/trunk/main/db1-ast/hash/hash_log2.c
@@ -0,0 +1,56 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Margo Seltzer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)hash_log2.c 8.2 (Berkeley) 5/31/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+
+#include "../include/db.h"
+
+u_int32_t __hash_log2 __P((u_int32_t));
+
+u_int32_t
+__hash_log2(num)
+ u_int32_t num;
+{
+ register u_int32_t i, limit;
+
+ limit = 1;
+ for (i = 0; limit < num; limit = limit << 1, i++);
+ return (i);
+}
diff --git a/trunk/main/db1-ast/hash/hash_page.c b/trunk/main/db1-ast/hash/hash_page.c
new file mode 100644
index 000000000..737b97d32
--- /dev/null
+++ b/trunk/main/db1-ast/hash/hash_page.c
@@ -0,0 +1,944 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Margo Seltzer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)hash_page.c 8.7 (Berkeley) 8/16/94";
+#endif /* LIBC_SCCS and not lint */
+
+/*
+ * PACKAGE: hashing
+ *
+ * DESCRIPTION:
+ * Page manipulation for hashing package.
+ *
+ * ROUTINES:
+ *
+ * External
+ * __get_page
+ * __add_ovflpage
+ * Internal
+ * overflow_page
+ * open_temp
+ */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#ifdef DEBUG
+#include <assert.h>
+#endif
+
+#include "../include/db.h"
+#include "hash.h"
+#include "page.h"
+#include "extern.h"
+
+static u_int32_t *fetch_bitmap __P((HTAB *, int));
+static u_int32_t first_free __P((u_int32_t));
+static int open_temp __P((HTAB *));
+static u_int16_t overflow_page __P((HTAB *));
+static void putpair __P((char *, const DBT *, const DBT *));
+static void squeeze_key __P((u_int16_t *, const DBT *, const DBT *));
+static int ugly_split
+ __P((HTAB *, u_int32_t, BUFHEAD *, BUFHEAD *, int, int));
+
+#define PAGE_INIT(P) { \
+ ((u_int16_t *)(P))[0] = 0; \
+ ((u_int16_t *)(P))[1] = hashp->BSIZE - 3 * sizeof(u_int16_t); \
+ ((u_int16_t *)(P))[2] = hashp->BSIZE; \
+}
+
+/*
+ * This is called AFTER we have verified that there is room on the page for
+ * the pair (PAIRFITS has returned true) so we go right ahead and start moving
+ * stuff on.
+ */
+static void
+putpair(p, key, val)
+ char *p;
+ const DBT *key, *val;
+{
+ register u_int16_t *bp, n, off;
+
+ bp = (u_int16_t *)p;
+
+ /* Enter the key first. */
+ n = bp[0];
+
+ off = OFFSET(bp) - key->size;
+ memmove(p + off, key->data, key->size);
+ bp[++n] = off;
+
+ /* Now the data. */
+ off -= val->size;
+ memmove(p + off, val->data, val->size);
+ bp[++n] = off;
+
+ /* Adjust page info. */
+ bp[0] = n;
+ bp[n + 1] = off - ((n + 3) * sizeof(u_int16_t));
+ bp[n + 2] = off;
+}
+
+/*
+ * Returns:
+ * 0 OK
+ * -1 error
+ */
+extern int
+__delpair(hashp, bufp, ndx)
+ HTAB *hashp;
+ BUFHEAD *bufp;
+ register int ndx;
+{
+ register u_int16_t *bp, newoff;
+ register int n;
+ u_int16_t pairlen;
+
+ bp = (u_int16_t *)bufp->page;
+ n = bp[0];
+
+ if (bp[ndx + 1] < REAL_KEY)
+ return (__big_delete(hashp, bufp));
+ if (ndx != 1)
+ newoff = bp[ndx - 1];
+ else
+ newoff = hashp->BSIZE;
+ pairlen = newoff - bp[ndx + 1];
+
+ if (ndx != (n - 1)) {
+ /* Hard Case -- need to shuffle keys */
+ register int i;
+ register char *src = bufp->page + (int)OFFSET(bp);
+ register char *dst = src + (int)pairlen;
+ memmove(dst, src, bp[ndx + 1] - OFFSET(bp));
+
+ /* Now adjust the pointers */
+ for (i = ndx + 2; i <= n; i += 2) {
+ if (bp[i + 1] == OVFLPAGE) {
+ bp[i - 2] = bp[i];
+ bp[i - 1] = bp[i + 1];
+ } else {
+ bp[i - 2] = bp[i] + pairlen;
+ bp[i - 1] = bp[i + 1] + pairlen;
+ }
+ }
+ }
+ /* Finally adjust the page data */
+ bp[n] = OFFSET(bp) + pairlen;
+ bp[n - 1] = bp[n + 1] + pairlen + 2 * sizeof(u_int16_t);
+ bp[0] = n - 2;
+ hashp->NKEYS--;
+
+ bufp->flags |= BUF_MOD;
+ return (0);
+}
+/*
+ * Returns:
+ * 0 ==> OK
+ * -1 ==> Error
+ */
+extern int
+__split_page(hashp, obucket, nbucket)
+ HTAB *hashp;
+ u_int32_t obucket, nbucket;
+{
+ register BUFHEAD *new_bufp, *old_bufp;
+ register u_int16_t *ino;
+ register char *np;
+ DBT key, val;
+ int n, ndx, retval;
+ u_int16_t copyto, diff, off, moved;
+ char *op;
+
+ copyto = (u_int16_t)hashp->BSIZE;
+ off = (u_int16_t)hashp->BSIZE;
+ old_bufp = __get_buf(hashp, obucket, NULL, 0);
+ if (old_bufp == NULL)
+ return (-1);
+ new_bufp = __get_buf(hashp, nbucket, NULL, 0);
+ if (new_bufp == NULL)
+ return (-1);
+
+ old_bufp->flags |= (BUF_MOD | BUF_PIN);
+ new_bufp->flags |= (BUF_MOD | BUF_PIN);
+
+ ino = (u_int16_t *)(op = old_bufp->page);
+ np = new_bufp->page;
+
+ moved = 0;
+
+ for (n = 1, ndx = 1; n < ino[0]; n += 2) {
+ if (ino[n + 1] < REAL_KEY) {
+ retval = ugly_split(hashp, obucket, old_bufp, new_bufp,
+ (int)copyto, (int)moved);
+ old_bufp->flags &= ~BUF_PIN;
+ new_bufp->flags &= ~BUF_PIN;
+ return (retval);
+
+ }
+ key.data = (u_char *)op + ino[n];
+ key.size = off - ino[n];
+
+ if (__call_hash(hashp, key.data, key.size) == obucket) {
+ /* Don't switch page */
+ diff = copyto - off;
+ if (diff) {
+ copyto = ino[n + 1] + diff;
+ memmove(op + copyto, op + ino[n + 1],
+ off - ino[n + 1]);
+ ino[ndx] = copyto + ino[n] - ino[n + 1];
+ ino[ndx + 1] = copyto;
+ } else
+ copyto = ino[n + 1];
+ ndx += 2;
+ } else {
+ /* Switch page */
+ val.data = (u_char *)op + ino[n + 1];
+ val.size = ino[n] - ino[n + 1];
+ putpair(np, &key, &val);
+ moved += 2;
+ }
+
+ off = ino[n + 1];
+ }
+
+ /* Now clean up the page */
+ ino[0] -= moved;
+ FREESPACE(ino) = copyto - sizeof(u_int16_t) * (ino[0] + 3);
+ OFFSET(ino) = copyto;
+
+#ifdef DEBUG3
+ (void)fprintf(stderr, "split %d/%d\n",
+ ((u_int16_t *)np)[0] / 2,
+ ((u_int16_t *)op)[0] / 2);
+#endif
+ /* unpin both pages */
+ old_bufp->flags &= ~BUF_PIN;
+ new_bufp->flags &= ~BUF_PIN;
+ return (0);
+}
+
+/*
+ * Called when we encounter an overflow or big key/data page during split
+ * handling. This is special cased since we have to begin checking whether
+ * the key/data pairs fit on their respective pages and because we may need
+ * overflow pages for both the old and new pages.
+ *
+ * The first page might be a page with regular key/data pairs in which case
+ * we have a regular overflow condition and just need to go on to the next
+ * page or it might be a big key/data pair in which case we need to fix the
+ * big key/data pair.
+ *
+ * Returns:
+ * 0 ==> success
+ * -1 ==> failure
+ */
+static int
+ugly_split(hashp, obucket, old_bufp, new_bufp, copyto, moved)
+ HTAB *hashp;
+ u_int32_t obucket; /* Same as __split_page. */
+ BUFHEAD *old_bufp, *new_bufp;
+ int copyto; /* First byte on page which contains key/data values. */
+ int moved; /* Number of pairs moved to new page. */
+{
+ register BUFHEAD *bufp; /* Buffer header for ino */
+ register u_int16_t *ino; /* Page keys come off of */
+ register u_int16_t *np; /* New page */
+ register u_int16_t *op; /* Page keys go on to if they aren't moving */
+
+ BUFHEAD *last_bfp; /* Last buf header OVFL needing to be freed */
+ DBT key, val;
+ SPLIT_RETURN ret;
+ u_int16_t n, off, ov_addr, scopyto;
+ char *cino; /* Character value of ino */
+
+ bufp = old_bufp;
+ ino = (u_int16_t *)old_bufp->page;
+ np = (u_int16_t *)new_bufp->page;
+ op = (u_int16_t *)old_bufp->page;
+ last_bfp = NULL;
+ scopyto = (u_int16_t)copyto; /* ANSI */
+
+ n = ino[0] - 1;
+ while (n < ino[0]) {
+ if (ino[2] < REAL_KEY && ino[2] != OVFLPAGE) {
+ if (__big_split(hashp, old_bufp,
+ new_bufp, bufp, bufp->addr, obucket, &ret))
+ return (-1);
+ old_bufp = ret.oldp;
+ if (!old_bufp)
+ return (-1);
+ op = (u_int16_t *)old_bufp->page;
+ new_bufp = ret.newp;
+ if (!new_bufp)
+ return (-1);
+ np = (u_int16_t *)new_bufp->page;
+ bufp = ret.nextp;
+ if (!bufp)
+ return (0);
+ cino = (char *)bufp->page;
+ ino = (u_int16_t *)cino;
+ last_bfp = ret.nextp;
+ } else if (ino[n + 1] == OVFLPAGE) {
+ ov_addr = ino[n];
+ /*
+ * Fix up the old page -- the extra 2 are the fields
+ * which contained the overflow information.
+ */
+ ino[0] -= (moved + 2);
+ FREESPACE(ino) =
+ scopyto - sizeof(u_int16_t) * (ino[0] + 3);
+ OFFSET(ino) = scopyto;
+
+ bufp = __get_buf(hashp, ov_addr, bufp, 0);
+ if (!bufp)
+ return (-1);
+
+ ino = (u_int16_t *)bufp->page;
+ n = 1;
+ scopyto = hashp->BSIZE;
+ moved = 0;
+
+ if (last_bfp)
+ __free_ovflpage(hashp, last_bfp);
+ last_bfp = bufp;
+ }
+ /* Move regular sized pairs of there are any */
+ off = hashp->BSIZE;
+ for (n = 1; (n < ino[0]) && (ino[n + 1] >= REAL_KEY); n += 2) {
+ cino = (char *)ino;
+ key.data = (u_char *)cino + ino[n];
+ key.size = off - ino[n];
+ val.data = (u_char *)cino + ino[n + 1];
+ val.size = ino[n] - ino[n + 1];
+ off = ino[n + 1];
+
+ if (__call_hash(hashp, key.data, key.size) == obucket) {
+ /* Keep on old page */
+ if (PAIRFITS(op, (&key), (&val)))
+ putpair((char *)op, &key, &val);
+ else {
+ old_bufp =
+ __add_ovflpage(hashp, old_bufp);
+ if (!old_bufp)
+ return (-1);
+ op = (u_int16_t *)old_bufp->page;
+ putpair((char *)op, &key, &val);
+ }
+ old_bufp->flags |= BUF_MOD;
+ } else {
+ /* Move to new page */
+ if (PAIRFITS(np, (&key), (&val)))
+ putpair((char *)np, &key, &val);
+ else {
+ new_bufp =
+ __add_ovflpage(hashp, new_bufp);
+ if (!new_bufp)
+ return (-1);
+ np = (u_int16_t *)new_bufp->page;
+ putpair((char *)np, &key, &val);
+ }
+ new_bufp->flags |= BUF_MOD;
+ }
+ }
+ }
+ if (last_bfp)
+ __free_ovflpage(hashp, last_bfp);
+ return (0);
+}
+
+/*
+ * Add the given pair to the page
+ *
+ * Returns:
+ * 0 ==> OK
+ * 1 ==> failure
+ */
+extern int
+__addel(hashp, bufp, key, val)
+ HTAB *hashp;
+ BUFHEAD *bufp;
+ const DBT *key, *val;
+{
+ register u_int16_t *bp, *sop;
+ int do_expand;
+
+ bp = (u_int16_t *)bufp->page;
+ do_expand = 0;
+ while (bp[0] && (bp[2] < REAL_KEY || bp[bp[0]] < REAL_KEY))
+ /* Exception case */
+ if (bp[2] == FULL_KEY_DATA && bp[0] == 2)
+ /* This is the last page of a big key/data pair
+ and we need to add another page */
+ break;
+ else if (bp[2] < REAL_KEY && bp[bp[0]] != OVFLPAGE) {
+ bufp = __get_buf(hashp, bp[bp[0] - 1], bufp, 0);
+ if (!bufp)
+ return (-1);
+ bp = (u_int16_t *)bufp->page;
+ } else
+ /* Try to squeeze key on this page */
+ if (FREESPACE(bp) > PAIRSIZE(key, val)) {
+ squeeze_key(bp, key, val);
+ return (0);
+ } else {
+ bufp = __get_buf(hashp, bp[bp[0] - 1], bufp, 0);
+ if (!bufp)
+ return (-1);
+ bp = (u_int16_t *)bufp->page;
+ }
+
+ if (PAIRFITS(bp, key, val))
+ putpair(bufp->page, key, val);
+ else {
+ do_expand = 1;
+ bufp = __add_ovflpage(hashp, bufp);
+ if (!bufp)
+ return (-1);
+ sop = (u_int16_t *)bufp->page;
+
+ if (PAIRFITS(sop, key, val))
+ putpair((char *)sop, key, val);
+ else
+ if (__big_insert(hashp, bufp, key, val))
+ return (-1);
+ }
+ bufp->flags |= BUF_MOD;
+ /*
+ * If the average number of keys per bucket exceeds the fill factor,
+ * expand the table.
+ */
+ hashp->NKEYS++;
+ if (do_expand ||
+ (hashp->NKEYS / (hashp->MAX_BUCKET + 1) > hashp->FFACTOR))
+ return (__expand_table(hashp));
+ return (0);
+}
+
+/*
+ *
+ * Returns:
+ * pointer on success
+ * NULL on error
+ */
+extern BUFHEAD *
+__add_ovflpage(hashp, bufp)
+ HTAB *hashp;
+ BUFHEAD *bufp;
+{
+ register u_int16_t *sp;
+ u_int16_t ndx, ovfl_num;
+#ifdef DEBUG1
+ int tmp1, tmp2;
+#endif
+ sp = (u_int16_t *)bufp->page;
+
+ /* Check if we are dynamically determining the fill factor */
+ if (hashp->FFACTOR == DEF_FFACTOR) {
+ hashp->FFACTOR = sp[0] >> 1;
+ if (hashp->FFACTOR < MIN_FFACTOR)
+ hashp->FFACTOR = MIN_FFACTOR;
+ }
+ bufp->flags |= BUF_MOD;
+ ovfl_num = overflow_page(hashp);
+#ifdef DEBUG1
+ tmp1 = bufp->addr;
+ tmp2 = bufp->ovfl ? bufp->ovfl->addr : 0;
+#endif
+ if (!ovfl_num || !(bufp->ovfl = __get_buf(hashp, ovfl_num, bufp, 1)))
+ return (NULL);
+ bufp->ovfl->flags |= BUF_MOD;
+#ifdef DEBUG1
+ (void)fprintf(stderr, "ADDOVFLPAGE: %d->ovfl was %d is now %d\n",
+ tmp1, tmp2, bufp->ovfl->addr);
+#endif
+ ndx = sp[0];
+ /*
+ * Since a pair is allocated on a page only if there's room to add
+ * an overflow page, we know that the OVFL information will fit on
+ * the page.
+ */
+ sp[ndx + 4] = OFFSET(sp);
+ sp[ndx + 3] = FREESPACE(sp) - OVFLSIZE;
+ sp[ndx + 1] = ovfl_num;
+ sp[ndx + 2] = OVFLPAGE;
+ sp[0] = ndx + 2;
+#ifdef HASH_STATISTICS
+ hash_overflows++;
+#endif
+ return (bufp->ovfl);
+}
+
+/*
+ * Returns:
+ * 0 indicates SUCCESS
+ * -1 indicates FAILURE
+ */
+extern int
+__get_page(hashp, p, bucket, is_bucket, is_disk, is_bitmap)
+ HTAB *hashp;
+ char *p;
+ u_int32_t bucket;
+ int is_bucket, is_disk, is_bitmap;
+{
+ register int fd, page, size;
+ int rsize;
+ u_int16_t *bp;
+
+ fd = hashp->fp;
+ size = hashp->BSIZE;
+
+ if ((fd == -1) || !is_disk) {
+ PAGE_INIT(p);
+ return (0);
+ }
+ if (is_bucket)
+ page = BUCKET_TO_PAGE(bucket);
+ else
+ page = OADDR_TO_PAGE(bucket);
+ if ((lseek(fd, (off_t)page << hashp->BSHIFT, SEEK_SET) == -1) ||
+ ((rsize = read(fd, p, size)) == -1))
+ return (-1);
+ bp = (u_int16_t *)p;
+ if (!rsize)
+ bp[0] = 0; /* We hit the EOF, so initialize a new page */
+ else
+ if (rsize != size) {
+ errno = EFTYPE;
+ return (-1);
+ }
+ if (!is_bitmap && !bp[0]) {
+ PAGE_INIT(p);
+ } else
+ if (hashp->LORDER != BYTE_ORDER) {
+ register int i, max;
+
+ if (is_bitmap) {
+ max = hashp->BSIZE >> 2; /* divide by 4 */
+ for (i = 0; i < max; i++)
+ M_32_SWAP(((int *)p)[i]);
+ } else {
+ M_16_SWAP(bp[0]);
+ max = bp[0] + 2;
+ for (i = 1; i <= max; i++)
+ M_16_SWAP(bp[i]);
+ }
+ }
+ return (0);
+}
+
+/*
+ * Write page p to disk
+ *
+ * Returns:
+ * 0 ==> OK
+ * -1 ==>failure
+ */
+extern int
+__put_page(hashp, p, bucket, is_bucket, is_bitmap)
+ HTAB *hashp;
+ char *p;
+ u_int32_t bucket;
+ int is_bucket, is_bitmap;
+{
+ register int fd, page, size;
+ int wsize;
+
+ size = hashp->BSIZE;
+ if ((hashp->fp == -1) && open_temp(hashp))
+ return (-1);
+ fd = hashp->fp;
+
+ if (hashp->LORDER != BYTE_ORDER) {
+ register int i;
+ register int max;
+
+ if (is_bitmap) {
+ max = hashp->BSIZE >> 2; /* divide by 4 */
+ for (i = 0; i < max; i++)
+ M_32_SWAP(((int *)p)[i]);
+ } else {
+ max = ((u_int16_t *)p)[0] + 2;
+ for (i = 0; i <= max; i++)
+ M_16_SWAP(((u_int16_t *)p)[i]);
+ }
+ }
+ if (is_bucket)
+ page = BUCKET_TO_PAGE(bucket);
+ else
+ page = OADDR_TO_PAGE(bucket);
+ if ((lseek(fd, (off_t)page << hashp->BSHIFT, SEEK_SET) == -1) ||
+ ((wsize = write(fd, p, size)) == -1))
+ /* Errno is set */
+ return (-1);
+ if (wsize != size) {
+ errno = EFTYPE;
+ return (-1);
+ }
+ return (0);
+}
+
+#define BYTE_MASK ((1 << INT_BYTE_SHIFT) -1)
+/*
+ * Initialize a new bitmap page. Bitmap pages are left in memory
+ * once they are read in.
+ */
+extern int
+__ibitmap(hashp, pnum, nbits, ndx)
+ HTAB *hashp;
+ int pnum, nbits, ndx;
+{
+ u_int32_t *ip;
+ int clearbytes, clearints;
+
+ if ((ip = (u_int32_t *)malloc(hashp->BSIZE)) == NULL)
+ return (1);
+ hashp->nmaps++;
+ clearints = ((nbits - 1) >> INT_BYTE_SHIFT) + 1;
+ clearbytes = clearints << INT_TO_BYTE;
+ (void)memset((char *)ip, 0, clearbytes);
+ (void)memset(((char *)ip) + clearbytes, 0xFF,
+ hashp->BSIZE - clearbytes);
+ ip[clearints - 1] = ALL_SET << (nbits & BYTE_MASK);
+ SETBIT(ip, 0);
+ hashp->BITMAPS[ndx] = (u_int16_t)pnum;
+ hashp->mapp[ndx] = ip;
+ return (0);
+}
+
+static u_int32_t
+first_free(map)
+ u_int32_t map;
+{
+ register u_int32_t i, mask;
+
+ mask = 0x1;
+ for (i = 0; i < BITS_PER_MAP; i++) {
+ if (!(mask & map))
+ return (i);
+ mask = mask << 1;
+ }
+ return (i);
+}
+
+static u_int16_t
+overflow_page(hashp)
+ HTAB *hashp;
+{
+ register u_int32_t *freep = 0;
+ register int max_free, offset, splitnum;
+ u_int16_t addr;
+ int bit, first_page, free_bit, free_page, i, in_use_bits, j;
+#ifdef DEBUG2
+ int tmp1, tmp2;
+#endif
+ splitnum = hashp->OVFL_POINT;
+ max_free = hashp->SPARES[splitnum];
+
+ free_page = (max_free - 1) >> (hashp->BSHIFT + BYTE_SHIFT);
+ free_bit = (max_free - 1) & ((hashp->BSIZE << BYTE_SHIFT) - 1);
+
+ /* Look through all the free maps to find the first free block */
+ first_page = hashp->LAST_FREED >>(hashp->BSHIFT + BYTE_SHIFT);
+ for ( i = first_page; i <= free_page; i++ ) {
+ if (!(freep = (u_int32_t *)hashp->mapp[i]) &&
+ !(freep = fetch_bitmap(hashp, i)))
+ return (0);
+ if (i == free_page)
+ in_use_bits = free_bit;
+ else
+ in_use_bits = (hashp->BSIZE << BYTE_SHIFT) - 1;
+
+ if (i == first_page) {
+ bit = hashp->LAST_FREED &
+ ((hashp->BSIZE << BYTE_SHIFT) - 1);
+ j = bit / BITS_PER_MAP;
+ bit = bit & ~(BITS_PER_MAP - 1);
+ } else {
+ bit = 0;
+ j = 0;
+ }
+ for (; bit <= in_use_bits; j++, bit += BITS_PER_MAP)
+ if (freep[j] != ALL_SET)
+ goto found;
+ }
+
+ /* No Free Page Found */
+ hashp->LAST_FREED = hashp->SPARES[splitnum];
+ hashp->SPARES[splitnum]++;
+ offset = hashp->SPARES[splitnum] -
+ (splitnum ? hashp->SPARES[splitnum - 1] : 0);
+
+#define OVMSG "HASH: Out of overflow pages. Increase page size\n"
+ if (offset > SPLITMASK) {
+ if (++splitnum >= NCACHED) {
+ (void)write(STDERR_FILENO, OVMSG, sizeof(OVMSG) - 1);
+ return (0);
+ }
+ hashp->OVFL_POINT = splitnum;
+ hashp->SPARES[splitnum] = hashp->SPARES[splitnum-1];
+ hashp->SPARES[splitnum-1]--;
+ offset = 1;
+ }
+
+ /* Check if we need to allocate a new bitmap page */
+ if (free_bit == (hashp->BSIZE << BYTE_SHIFT) - 1) {
+ free_page++;
+ if (free_page >= NCACHED) {
+ (void)write(STDERR_FILENO, OVMSG, sizeof(OVMSG) - 1);
+ return (0);
+ }
+ /*
+ * This is tricky. The 1 indicates that you want the new page
+ * allocated with 1 clear bit. Actually, you are going to
+ * allocate 2 pages from this map. The first is going to be
+ * the map page, the second is the overflow page we were
+ * looking for. The init_bitmap routine automatically, sets
+ * the first bit of itself to indicate that the bitmap itself
+ * is in use. We would explicitly set the second bit, but
+ * don't have to if we tell init_bitmap not to leave it clear
+ * in the first place.
+ */
+ if (__ibitmap(hashp,
+ (int)OADDR_OF(splitnum, offset), 1, free_page))
+ return (0);
+ hashp->SPARES[splitnum]++;
+#ifdef DEBUG2
+ free_bit = 2;
+#endif
+ offset++;
+ if (offset > SPLITMASK) {
+ if (++splitnum >= NCACHED) {
+ (void)write(STDERR_FILENO, OVMSG,
+ sizeof(OVMSG) - 1);
+ return (0);
+ }
+ hashp->OVFL_POINT = splitnum;
+ hashp->SPARES[splitnum] = hashp->SPARES[splitnum-1];
+ hashp->SPARES[splitnum-1]--;
+ offset = 0;
+ }
+ } else {
+ /*
+ * Free_bit addresses the last used bit. Bump it to address
+ * the first available bit.
+ */
+ free_bit++;
+ SETBIT(freep, free_bit);
+ }
+
+ /* Calculate address of the new overflow page */
+ addr = OADDR_OF(splitnum, offset);
+#ifdef DEBUG2
+ (void)fprintf(stderr, "OVERFLOW_PAGE: ADDR: %d BIT: %d PAGE %d\n",
+ addr, free_bit, free_page);
+#endif
+ return (addr);
+
+found:
+ bit = bit + first_free(freep[j]);
+ SETBIT(freep, bit);
+#ifdef DEBUG2
+ tmp1 = bit;
+ tmp2 = i;
+#endif
+ /*
+ * Bits are addressed starting with 0, but overflow pages are addressed
+ * beginning at 1. Bit is a bit addressnumber, so we need to increment
+ * it to convert it to a page number.
+ */
+ bit = 1 + bit + (i * (hashp->BSIZE << BYTE_SHIFT));
+ if (bit >= hashp->LAST_FREED)
+ hashp->LAST_FREED = bit - 1;
+
+ /* Calculate the split number for this page */
+ for (i = 0; (i < splitnum) && (bit > hashp->SPARES[i]); i++);
+ offset = (i ? bit - hashp->SPARES[i - 1] : bit);
+ if (offset >= SPLITMASK)
+ return (0); /* Out of overflow pages */
+ addr = OADDR_OF(i, offset);
+#ifdef DEBUG2
+ (void)fprintf(stderr, "OVERFLOW_PAGE: ADDR: %d BIT: %d PAGE %d\n",
+ addr, tmp1, tmp2);
+#endif
+
+ /* Allocate and return the overflow page */
+ return (addr);
+}
+
+/*
+ * Mark this overflow page as free.
+ */
+extern void
+__free_ovflpage(hashp, obufp)
+ HTAB *hashp;
+ BUFHEAD *obufp;
+{
+ register u_int16_t addr;
+ u_int32_t *freep;
+ int bit_address, free_page, free_bit;
+ u_int16_t ndx;
+
+ addr = obufp->addr;
+#ifdef DEBUG1
+ (void)fprintf(stderr, "Freeing %d\n", addr);
+#endif
+ ndx = (((u_int16_t)addr) >> SPLITSHIFT);
+ bit_address =
+ (ndx ? hashp->SPARES[ndx - 1] : 0) + (addr & SPLITMASK) - 1;
+ if (bit_address < hashp->LAST_FREED)
+ hashp->LAST_FREED = bit_address;
+ free_page = (bit_address >> (hashp->BSHIFT + BYTE_SHIFT));
+ free_bit = bit_address & ((hashp->BSIZE << BYTE_SHIFT) - 1);
+
+ if (!(freep = hashp->mapp[free_page]))
+ freep = fetch_bitmap(hashp, free_page);
+#ifdef DEBUG
+ /*
+ * This had better never happen. It means we tried to read a bitmap
+ * that has already had overflow pages allocated off it, and we
+ * failed to read it from the file.
+ */
+ if (!freep)
+ assert(0);
+#endif
+ CLRBIT(freep, free_bit);
+#ifdef DEBUG2
+ (void)fprintf(stderr, "FREE_OVFLPAGE: ADDR: %d BIT: %d PAGE %d\n",
+ obufp->addr, free_bit, free_page);
+#endif
+ __reclaim_buf(hashp, obufp);
+}
+
+/*
+ * Returns:
+ * 0 success
+ * -1 failure
+ */
+static int
+open_temp(hashp)
+ HTAB *hashp;
+{
+ sigset_t set, oset;
+ static char namestr[] = "_hashXXXXXX";
+
+ /* Block signals; make sure file goes away at process exit. */
+ (void)sigfillset(&set);
+ (void)sigprocmask(SIG_BLOCK, &set, &oset);
+ if ((hashp->fp = mkstemp(namestr)) != -1) {
+ (void)unlink(namestr);
+ (void)fcntl(hashp->fp, F_SETFD, 1);
+ }
+ (void)sigprocmask(SIG_SETMASK, &oset, (sigset_t *)NULL);
+ return (hashp->fp != -1 ? 0 : -1);
+}
+
+/*
+ * We have to know that the key will fit, but the last entry on the page is
+ * an overflow pair, so we need to shift things.
+ */
+static void
+squeeze_key(sp, key, val)
+ u_int16_t *sp;
+ const DBT *key, *val;
+{
+ register char *p;
+ u_int16_t free_space, n, off, pageno;
+
+ p = (char *)sp;
+ n = sp[0];
+ free_space = FREESPACE(sp);
+ off = OFFSET(sp);
+
+ pageno = sp[n - 1];
+ off -= key->size;
+ sp[n - 1] = off;
+ memmove(p + off, key->data, key->size);
+ off -= val->size;
+ sp[n] = off;
+ memmove(p + off, val->data, val->size);
+ sp[0] = n + 2;
+ sp[n + 1] = pageno;
+ sp[n + 2] = OVFLPAGE;
+ FREESPACE(sp) = free_space - PAIRSIZE(key, val);
+ OFFSET(sp) = off;
+}
+
+static u_int32_t *
+fetch_bitmap(hashp, ndx)
+ HTAB *hashp;
+ int ndx;
+{
+ if (ndx >= hashp->nmaps)
+ return (NULL);
+ if ((hashp->mapp[ndx] = (u_int32_t *)malloc(hashp->BSIZE)) == NULL)
+ return (NULL);
+ if (__get_page(hashp,
+ (char *)hashp->mapp[ndx], hashp->BITMAPS[ndx], 0, 1, 1)) {
+ free(hashp->mapp[ndx]);
+ return (NULL);
+ }
+ return (hashp->mapp[ndx]);
+}
+
+#ifdef DEBUG4
+int
+print_chain(addr)
+ int addr;
+{
+ BUFHEAD *bufp;
+ short *bp, oaddr;
+
+ (void)fprintf(stderr, "%d ", addr);
+ bufp = __get_buf(hashp, addr, NULL, 0);
+ bp = (short *)bufp->page;
+ while (bp[0] && ((bp[bp[0]] == OVFLPAGE) ||
+ ((bp[0] > 2) && bp[2] < REAL_KEY))) {
+ oaddr = bp[bp[0] - 1];
+ (void)fprintf(stderr, "%d ", (int)oaddr);
+ bufp = __get_buf(hashp, (int)oaddr, bufp, 0);
+ bp = (short *)bufp->page;
+ }
+ (void)fprintf(stderr, "\n");
+}
+#endif
diff --git a/trunk/main/db1-ast/hash/hsearch.c b/trunk/main/db1-ast/hash/hsearch.c
new file mode 100644
index 000000000..2971f9308
--- /dev/null
+++ b/trunk/main/db1-ast/hash/hsearch.c
@@ -0,0 +1,107 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Margo Seltzer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)hsearch.c 8.4 (Berkeley) 7/21/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+
+#include <fcntl.h>
+#include <string.h>
+
+#include "../include/db.h"
+#include "search.h"
+
+static DB *dbp = NULL;
+static ENTRY retval;
+
+extern int
+hcreate(nel)
+ u_int nel;
+{
+ HASHINFO info;
+
+ info.nelem = nel;
+ info.bsize = 256;
+ info.ffactor = 8;
+ info.cachesize = 0;
+ info.hash = NULL;
+ info.lorder = 0;
+ dbp = (DB *)__hash_open(NULL, O_CREAT | O_RDWR, 0600, &info, 0);
+ return ((int)dbp);
+}
+
+extern ENTRY *
+hsearch(item, action)
+ ENTRY item;
+ ACTION action;
+{
+ DBT key, val;
+ int status;
+
+ if (!dbp)
+ return (NULL);
+ key.data = (u_char *)item.key;
+ key.size = strlen(item.key) + 1;
+
+ if (action == ENTER) {
+ val.data = (u_char *)item.data;
+ val.size = strlen(item.data) + 1;
+ status = (dbp->put)(dbp, &key, &val, R_NOOVERWRITE);
+ if (status)
+ return (NULL);
+ } else {
+ /* FIND */
+ status = (dbp->get)(dbp, &key, &val, 0);
+ if (status)
+ return (NULL);
+ else
+ item.data = (char *)val.data;
+ }
+ retval.key = item.key;
+ retval.data = item.data;
+ return (&retval);
+}
+
+extern void
+hdestroy()
+{
+ if (dbp) {
+ (void)(dbp->close)(dbp);
+ dbp = NULL;
+ }
+}
diff --git a/trunk/main/db1-ast/hash/ndbm.c b/trunk/main/db1-ast/hash/ndbm.c
new file mode 100644
index 000000000..d702f737a
--- /dev/null
+++ b/trunk/main/db1-ast/hash/ndbm.c
@@ -0,0 +1,235 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Margo Seltzer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)ndbm.c 8.4 (Berkeley) 7/21/94";
+#endif /* LIBC_SCCS and not lint */
+
+/*
+ * This package provides a dbm compatible interface to the new hashing
+ * package described in db(3).
+ */
+
+#include <sys/param.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <ndbm.h>
+#include "hash.h"
+
+/*
+ * Returns:
+ * *DBM on success
+ * NULL on failure
+ */
+extern DBM *
+dbm_open(file, flags, mode)
+ const char *file;
+ int flags, mode;
+{
+ DBM *db;
+ HASHINFO info;
+ const size_t len = strlen(file) + sizeof (DBM_SUFFIX);
+#ifdef __GNUC__
+ char path[len];
+#else
+ char *path = malloc(len);
+ if (path == NULL)
+ return NULL;
+#endif
+
+ info.bsize = 4096;
+ info.ffactor = 40;
+ info.nelem = 1;
+ info.cachesize = 0;
+ info.hash = NULL;
+ info.lorder = 0;
+ (void)strncpy(path, file, len - 1);
+ (void)strncat(path, DBM_SUFFIX, len - strlen(path) - 1);
+ db = (DBM *)__hash_open(path, flags, mode, &info, 0);
+#ifndef __GNUC__
+ free(path);
+#endif
+ return db;
+}
+
+extern void
+dbm_close(db)
+ DBM *db;
+{
+ (void)(db->close)(db);
+}
+
+/*
+ * Returns:
+ * DATUM on success
+ * NULL on failure
+ */
+extern datum
+dbm_fetch(db, key)
+ DBM *db;
+ datum key;
+{
+ datum retdata;
+ int status;
+ DBT dbtkey, dbtretdata;
+
+ dbtkey.data = key.dptr;
+ dbtkey.size = key.dsize;
+ status = (db->get)(db, &dbtkey, &dbtretdata, 0);
+ if (status) {
+ dbtretdata.data = NULL;
+ dbtretdata.size = 0;
+ }
+ retdata.dptr = dbtretdata.data;
+ retdata.dsize = dbtretdata.size;
+ return (retdata);
+}
+
+/*
+ * Returns:
+ * DATUM on success
+ * NULL on failure
+ */
+extern datum
+dbm_firstkey(db)
+ DBM *db;
+{
+ int status;
+ datum retkey;
+ DBT dbtretkey, dbtretdata;
+
+ status = (db->seq)(db, &dbtretkey, &dbtretdata, R_FIRST);
+ if (status)
+ dbtretkey.data = NULL;
+ retkey.dptr = dbtretkey.data;
+ retkey.dsize = dbtretkey.size;
+ return (retkey);
+}
+
+/*
+ * Returns:
+ * DATUM on success
+ * NULL on failure
+ */
+extern datum
+dbm_nextkey(db)
+ DBM *db;
+{
+ int status;
+ datum retkey;
+ DBT dbtretkey, dbtretdata;
+
+ status = (db->seq)(db, &dbtretkey, &dbtretdata, R_NEXT);
+ if (status)
+ dbtretkey.data = NULL;
+ retkey.dptr = dbtretkey.data;
+ retkey.dsize = dbtretkey.size;
+ return (retkey);
+}
+/*
+ * Returns:
+ * 0 on success
+ * <0 failure
+ */
+extern int
+dbm_delete(db, key)
+ DBM *db;
+ datum key;
+{
+ int status;
+ DBT dbtkey;
+
+ dbtkey.data = key.dptr;
+ dbtkey.size = key.dsize;
+ status = (db->del)(db, &dbtkey, 0);
+ if (status)
+ return (-1);
+ else
+ return (0);
+}
+
+/*
+ * Returns:
+ * 0 on success
+ * <0 failure
+ * 1 if DBM_INSERT and entry exists
+ */
+extern int
+dbm_store(db, key, data, flags)
+ DBM *db;
+ datum key, data;
+ int flags;
+{
+ DBT dbtkey, dbtdata;
+
+ dbtkey.data = key.dptr;
+ dbtkey.size = key.dsize;
+ dbtdata.data = data.dptr;
+ dbtdata.size = data.dsize;
+ return ((db->put)(db, &dbtkey, &dbtdata,
+ (flags == DBM_INSERT) ? R_NOOVERWRITE : 0));
+}
+
+extern int
+dbm_error(db)
+ DBM *db;
+{
+ HTAB *hp;
+
+ hp = (HTAB *)db->internal;
+ return (hp->errnum);
+}
+
+extern int
+dbm_clearerr(db)
+ DBM *db;
+{
+ HTAB *hp;
+
+ hp = (HTAB *)db->internal;
+ hp->errnum = 0;
+ return (0);
+}
+
+extern int
+dbm_dirfno(db)
+ DBM *db;
+{
+ return(((HTAB *)db->internal)->fp);
+}
diff --git a/trunk/main/db1-ast/hash/page.h b/trunk/main/db1-ast/hash/page.h
new file mode 100644
index 000000000..0fc0d5a3e
--- /dev/null
+++ b/trunk/main/db1-ast/hash/page.h
@@ -0,0 +1,92 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Margo Seltzer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)page.h 8.2 (Berkeley) 5/31/94
+ */
+
+/*
+ * Definitions for hashing page file format.
+ */
+
+/*
+ * routines dealing with a data page
+ *
+ * page format:
+ * +------------------------------+
+ * p | n | keyoff | datoff | keyoff |
+ * +------------+--------+--------+
+ * | datoff | free | ptr | --> |
+ * +--------+---------------------+
+ * | F R E E A R E A |
+ * +--------------+---------------+
+ * | <---- - - - | data |
+ * +--------+-----+----+----------+
+ * | key | data | key |
+ * +--------+----------+----------+
+ *
+ * Pointer to the free space is always: p[p[0] + 2]
+ * Amount of free space on the page is: p[p[0] + 1]
+ */
+
+/*
+ * How many bytes required for this pair?
+ * 2 shorts in the table at the top of the page + room for the
+ * key and room for the data
+ *
+ * We prohibit entering a pair on a page unless there is also room to append
+ * an overflow page. The reason for this it that you can get in a situation
+ * where a single key/data pair fits on a page, but you can't append an
+ * overflow page and later you'd have to split the key/data and handle like
+ * a big pair.
+ * You might as well do this up front.
+ */
+
+#define PAIRSIZE(K,D) (2*sizeof(u_int16_t) + (K)->size + (D)->size)
+#define BIGOVERHEAD (4*sizeof(u_int16_t))
+#define KEYSIZE(K) (4*sizeof(u_int16_t) + (K)->size);
+#define OVFLSIZE (2*sizeof(u_int16_t))
+#define FREESPACE(P) ((P)[(P)[0]+1])
+#define OFFSET(P) ((P)[(P)[0]+2])
+#define PAIRFITS(P,K,D) \
+ (((P)[2] >= REAL_KEY) && \
+ (PAIRSIZE((K),(D)) + OVFLSIZE) <= FREESPACE((P)))
+#define PAGE_META(N) (((N)+3) * sizeof(u_int16_t))
+
+typedef struct {
+ BUFHEAD *newp;
+ BUFHEAD *oldp;
+ BUFHEAD *nextp;
+ u_int16_t next_addr;
+} SPLIT_RETURN;
diff --git a/trunk/main/db1-ast/hash/search.h b/trunk/main/db1-ast/hash/search.h
new file mode 100644
index 000000000..4d3b9143e
--- /dev/null
+++ b/trunk/main/db1-ast/hash/search.h
@@ -0,0 +1,51 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Margo Seltzer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)search.h 8.1 (Berkeley) 6/4/93
+ */
+
+/* Backward compatibility to hsearch interface. */
+typedef struct entry {
+ char *key;
+ char *data;
+} ENTRY;
+
+typedef enum {
+ FIND, ENTER
+} ACTION;
+
+int hcreate __P((unsigned int));
+void hdestroy __P((void));
+ENTRY *hsearch __P((ENTRY, ACTION));
diff --git a/trunk/main/db1-ast/include/circ-queue.h b/trunk/main/db1-ast/include/circ-queue.h
new file mode 100644
index 000000000..33ba9115b
--- /dev/null
+++ b/trunk/main/db1-ast/include/circ-queue.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)queue.h 8.5 (Berkeley) 8/20/94
+ * $FreeBSD: ports/misc/44bsd-more/files/queue.h,v 1.1 2001/01/06 03:41:36 hoek Exp $
+ */
+
+/*
+ * Circular queue definitions.
+ */
+#define CIRCLEQ_HEAD(name, type) \
+struct name { \
+ struct type *cqh_first; /* first element */ \
+ struct type *cqh_last; /* last element */ \
+}
+
+#define CIRCLEQ_ENTRY(type) \
+struct { \
+ struct type *cqe_next; /* next element */ \
+ struct type *cqe_prev; /* previous element */ \
+}
+
+/*
+ * Circular queue functions.
+ */
+#define CIRCLEQ_EMPTY(head) ((head)->cqh_first == (void *)(head))
+
+#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
+
+#define CIRCLEQ_FOREACH(var, head, field) \
+ for((var) = (head)->cqh_first; \
+ (var) != (void *)(head); \
+ (var) = (var)->field.cqe_next)
+
+#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
+ for((var) = (head)->cqh_last; \
+ (var) != (void *)(head); \
+ (var) = (var)->field.cqe_prev)
+
+#define CIRCLEQ_INIT(head) do { \
+ (head)->cqh_first = (void *)(head); \
+ (head)->cqh_last = (void *)(head); \
+} while (0)
+
+#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ (elm)->field.cqe_next = (listelm)->field.cqe_next; \
+ (elm)->field.cqe_prev = (listelm); \
+ if ((listelm)->field.cqe_next == (void *)(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (listelm)->field.cqe_next->field.cqe_prev = (elm); \
+ (listelm)->field.cqe_next = (elm); \
+} while (0)
+
+#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
+ (elm)->field.cqe_next = (listelm); \
+ (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
+ if ((listelm)->field.cqe_prev == (void *)(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (listelm)->field.cqe_prev->field.cqe_next = (elm); \
+ (listelm)->field.cqe_prev = (elm); \
+} while (0)
+
+#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
+ (elm)->field.cqe_next = (head)->cqh_first; \
+ (elm)->field.cqe_prev = (void *)(head); \
+ if ((head)->cqh_last == (void *)(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (head)->cqh_first->field.cqe_prev = (elm); \
+ (head)->cqh_first = (elm); \
+} while (0)
+
+#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.cqe_next = (void *)(head); \
+ (elm)->field.cqe_prev = (head)->cqh_last; \
+ if ((head)->cqh_first == (void *)(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (head)->cqh_last->field.cqe_next = (elm); \
+ (head)->cqh_last = (elm); \
+} while (0)
+
+#define CIRCLEQ_LAST(head) ((head)->cqh_last)
+
+#define CIRCLEQ_NEXT(elm,field) ((elm)->field.cqe_next)
+
+#define CIRCLEQ_PREV(elm,field) ((elm)->field.cqe_prev)
+
+#define CIRCLEQ_REMOVE(head, elm, field) do { \
+ if ((elm)->field.cqe_next == (void *)(head)) \
+ (head)->cqh_last = (elm)->field.cqe_prev; \
+ else \
+ (elm)->field.cqe_next->field.cqe_prev = \
+ (elm)->field.cqe_prev; \
+ if ((elm)->field.cqe_prev == (void *)(head)) \
+ (head)->cqh_first = (elm)->field.cqe_next; \
+ else \
+ (elm)->field.cqe_prev->field.cqe_next = \
+ (elm)->field.cqe_next; \
+} while (0)
diff --git a/trunk/main/db1-ast/include/compat.h b/trunk/main/db1-ast/include/compat.h
new file mode 100644
index 000000000..706e58265
--- /dev/null
+++ b/trunk/main/db1-ast/include/compat.h
@@ -0,0 +1,49 @@
+/* Values for building 4.4 BSD db routines in the GNU C library. */
+
+#ifndef _compat_h_
+#define _compat_h_
+
+#include <fcntl.h>
+
+/*
+ * If you can't provide lock values in the open(2) call. Note, this
+ * allows races to happen.
+ */
+#ifndef O_EXLOCK /* 4.4BSD extension. */
+#define O_EXLOCK 0
+#endif
+
+#ifndef O_SHLOCK /* 4.4BSD extension. */
+#define O_SHLOCK 0
+#endif
+
+#include <errno.h>
+
+#ifndef EFTYPE
+#define EFTYPE EINVAL /* POSIX 1003.1 format errno. */
+#endif
+
+#include <unistd.h>
+#include <limits.h>
+
+#ifndef _POSIX_VDISABLE /* POSIX 1003.1 disabling char. */
+#define _POSIX_VDISABLE 0 /* Some systems used 0. */
+#endif
+
+#include <termios.h>
+
+#ifndef TCSASOFT /* 4.4BSD extension. */
+#define TCSASOFT 0
+#endif
+
+#include <sys/param.h>
+
+#ifndef MAX /* Usually found in <sys/param.h>. */
+#define MAX(_a,_b) ((_a)<(_b)?(_b):(_a))
+#endif
+#ifndef MIN /* Usually found in <sys/param.h>. */
+#define MIN(_a,_b) ((_a)<(_b)?(_a):(_b))
+#endif
+
+
+#endif /* compat.h */
diff --git a/trunk/main/db1-ast/include/db.h b/trunk/main/db1-ast/include/db.h
new file mode 100644
index 000000000..a58724bdd
--- /dev/null
+++ b/trunk/main/db1-ast/include/db.h
@@ -0,0 +1,250 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)db.h 8.7 (Berkeley) 6/16/94
+ */
+
+#ifndef _DB_H
+#define _DB_H 1
+
+#include <sys/types.h>
+#include <sys/cdefs.h>
+
+#include <limits.h>
+
+#ifdef __DBINTERFACE_PRIVATE
+#include <compat.h>
+#endif
+
+#ifdef SOLARIS
+#include "solaris-compat/compat.h"
+#endif
+
+#define RET_ERROR -1 /* Return values. */
+#define RET_SUCCESS 0
+#define RET_SPECIAL 1
+
+#ifndef __BIT_TYPES_DEFINED__
+#define __BIT_TYPES_DEFINED__
+#if (!defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__APPLE__))
+typedef __signed char int8_t;
+typedef short int16_t;
+typedef int int32_t;
+typedef unsigned char u_int8_t;
+typedef unsigned short u_int16_t;
+typedef unsigned int u_int32_t;
+#ifdef WE_DONT_NEED_QUADS
+typedef long long int64_t;
+typedef unsigned long long u_int64_t;
+#endif
+#endif /* __FreeBSD__ */
+#endif
+
+#ifdef SOLARIS
+#define __P(p) p
+#define __BEGIN_DECLS
+#define __END_DECLS
+#endif
+
+#define MAX_PAGE_NUMBER 0xffffffff /* >= # of pages in a file */
+typedef u_int32_t pgno_t;
+#define MAX_PAGE_OFFSET 65535 /* >= # of bytes in a page */
+typedef u_int16_t indx_t;
+#define MAX_REC_NUMBER 0xffffffff /* >= # of records in a tree */
+typedef u_int32_t recno_t;
+
+/* Key/data structure -- a Data-Base Thang. */
+typedef struct {
+ void *data; /* data */
+ size_t size; /* data length */
+} DBT;
+
+/* Routine flags. */
+#define R_CURSOR 1 /* del, put, seq */
+#define __R_UNUSED 2 /* UNUSED */
+#define R_FIRST 3 /* seq */
+#define R_IAFTER 4 /* put (RECNO) */
+#define R_IBEFORE 5 /* put (RECNO) */
+#define R_LAST 6 /* seq (BTREE, RECNO) */
+#define R_NEXT 7 /* seq */
+#define R_NOOVERWRITE 8 /* put */
+#define R_PREV 9 /* seq (BTREE, RECNO) */
+#define R_SETCURSOR 10 /* put (RECNO) */
+#define R_RECNOSYNC 11 /* sync (RECNO) */
+
+typedef enum { DB_BTREE, DB_HASH, DB_RECNO } DBTYPE;
+
+/*
+ * !!!
+ * The following flags are included in the dbopen(3) call as part of the
+ * open(2) flags. In order to avoid conflicts with the open flags, start
+ * at the top of the 16 or 32-bit number space and work our way down. If
+ * the open flags were significantly expanded in the future, it could be
+ * a problem. Wish I'd left another flags word in the dbopen call.
+ *
+ * !!!
+ * None of this stuff is implemented yet. The only reason that it's here
+ * is so that the access methods can skip copying the key/data pair when
+ * the DB_LOCK flag isn't set.
+ */
+#if UINT_MAX > 65535
+#define DB_LOCK 0x20000000 /* Do locking. */
+#define DB_SHMEM 0x40000000 /* Use shared memory. */
+#define DB_TXN 0x80000000 /* Do transactions. */
+#else
+#define DB_LOCK 0x2000 /* Do locking. */
+#define DB_SHMEM 0x4000 /* Use shared memory. */
+#define DB_TXN 0x8000 /* Do transactions. */
+#endif
+
+/* Access method description structure. */
+typedef struct __db {
+ DBTYPE type; /* Underlying db type. */
+ int (*close) __P((struct __db *));
+ int (*del) __P((const struct __db *, const DBT *, u_int));
+ int (*get) __P((const struct __db *, const DBT *, DBT *, u_int));
+ int (*put) __P((const struct __db *, DBT *, const DBT *, u_int));
+ int (*seq) __P((const struct __db *, DBT *, DBT *, u_int));
+ int (*sync) __P((const struct __db *, u_int));
+ void *internal; /* Access method private. */
+ int (*fd) __P((const struct __db *));
+} DB;
+
+#define BTREEMAGIC 0x053162
+#define BTREEVERSION 3
+
+/* Structure used to pass parameters to the btree routines. */
+typedef struct {
+#define R_DUP 0x01 /* duplicate keys */
+ u_long flags;
+ u_int cachesize; /* bytes to cache */
+ int maxkeypage; /* maximum keys per page */
+ int minkeypage; /* minimum keys per page */
+ u_int psize; /* page size */
+ int (*compare) /* comparison function */
+ __P((const DBT *, const DBT *));
+ size_t (*prefix) /* prefix function */
+ __P((const DBT *, const DBT *));
+ int lorder; /* byte order */
+} BTREEINFO;
+
+#define HASHMAGIC 0x061561
+#define HASHVERSION 2
+
+/* Structure used to pass parameters to the hashing routines. */
+typedef struct {
+ u_int bsize; /* bucket size */
+ u_int ffactor; /* fill factor */
+ u_int nelem; /* number of elements */
+ u_int cachesize; /* bytes to cache */
+ u_int32_t /* hash function */
+ (*hash) __P((const void *, size_t));
+ int lorder; /* byte order */
+} HASHINFO;
+
+/* Structure used to pass parameters to the record routines. */
+typedef struct {
+#define R_FIXEDLEN 0x01 /* fixed-length records */
+#define R_NOKEY 0x02 /* key not required */
+#define R_SNAPSHOT 0x04 /* snapshot the input */
+ u_long flags;
+ u_int cachesize; /* bytes to cache */
+ u_int psize; /* page size */
+ int lorder; /* byte order */
+ size_t reclen; /* record length (fixed-length records) */
+ u_char bval; /* delimiting byte (variable-length records */
+ char *bfname; /* btree file name */
+} RECNOINFO;
+
+#ifdef __DBINTERFACE_PRIVATE
+/*
+ * Little endian <==> big endian 32-bit swap macros.
+ * M_32_SWAP swap a memory location
+ * P_32_SWAP swap a referenced memory location
+ * P_32_COPY swap from one location to another
+ */
+#define M_32_SWAP(a) { \
+ u_int32_t _tmp = a; \
+ ((char *)&a)[0] = ((char *)&_tmp)[3]; \
+ ((char *)&a)[1] = ((char *)&_tmp)[2]; \
+ ((char *)&a)[2] = ((char *)&_tmp)[1]; \
+ ((char *)&a)[3] = ((char *)&_tmp)[0]; \
+}
+#define P_32_SWAP(a) { \
+ u_int32_t _tmp = *(u_int32_t *)a; \
+ ((char *)a)[0] = ((char *)&_tmp)[3]; \
+ ((char *)a)[1] = ((char *)&_tmp)[2]; \
+ ((char *)a)[2] = ((char *)&_tmp)[1]; \
+ ((char *)a)[3] = ((char *)&_tmp)[0]; \
+}
+#define P_32_COPY(a, b) { \
+ ((char *)&(b))[0] = ((char *)&(a))[3]; \
+ ((char *)&(b))[1] = ((char *)&(a))[2]; \
+ ((char *)&(b))[2] = ((char *)&(a))[1]; \
+ ((char *)&(b))[3] = ((char *)&(a))[0]; \
+}
+
+/*
+ * Little endian <==> big endian 16-bit swap macros.
+ * M_16_SWAP swap a memory location
+ * P_16_SWAP swap a referenced memory location
+ * P_16_COPY swap from one location to another
+ */
+#define M_16_SWAP(a) { \
+ u_int16_t _tmp = a; \
+ ((char *)&a)[0] = ((char *)&_tmp)[1]; \
+ ((char *)&a)[1] = ((char *)&_tmp)[0]; \
+}
+#define P_16_SWAP(a) { \
+ u_int16_t _tmp = *(u_int16_t *)a; \
+ ((char *)a)[0] = ((char *)&_tmp)[1]; \
+ ((char *)a)[1] = ((char *)&_tmp)[0]; \
+}
+#define P_16_COPY(a, b) { \
+ ((char *)&(b))[0] = ((char *)&(a))[1]; \
+ ((char *)&(b))[1] = ((char *)&(a))[0]; \
+}
+#endif
+
+__BEGIN_DECLS
+DB *__dbopen __P((const char *, int, int, DBTYPE, const void *));
+DB *dbopen __P((const char *, int, int, DBTYPE, const void *));
+
+#ifdef __DBINTERFACE_PRIVATE
+DB *__bt_open __P((const char *, int, int, const BTREEINFO *, int));
+DB *__hash_open __P((const char *, int, int, const HASHINFO *, int));
+DB *__rec_open __P((const char *, int, int, const RECNOINFO *, int));
+void __dbpanic __P((DB *dbp));
+#endif
+__END_DECLS
+
+#endif /* db.h */
diff --git a/trunk/main/db1-ast/include/mpool.h b/trunk/main/db1-ast/include/mpool.h
new file mode 100644
index 000000000..0cfc5741c
--- /dev/null
+++ b/trunk/main/db1-ast/include/mpool.h
@@ -0,0 +1,115 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)mpool.h 8.2 (Berkeley) 7/14/94
+ */
+
+#ifndef _MPOOL_H
+#define _MPOOL_H 1
+
+#include <sys/queue.h>
+#ifndef CIRCLEQ_HEAD
+#include <circ-queue.h>
+#endif
+
+/*
+ * The memory pool scheme is a simple one. Each in-memory page is referenced
+ * by a bucket which is threaded in up to two of three ways. All active pages
+ * are threaded on a hash chain (hashed by page number) and an lru chain.
+ * Inactive pages are threaded on a free chain. Each reference to a memory
+ * pool is handed an opaque MPOOL cookie which stores all of this information.
+ */
+#define HASHSIZE 128
+#define HASHKEY(pgno) ((pgno - 1) % HASHSIZE)
+
+/* The BKT structures are the elements of the queues. */
+typedef struct _bkt {
+ CIRCLEQ_ENTRY(_bkt) hq; /* hash queue */
+ CIRCLEQ_ENTRY(_bkt) q; /* lru queue */
+ void *page; /* page */
+ pgno_t pgno; /* page number */
+
+#define MPOOL_DIRTY 0x01 /* page needs to be written */
+#define MPOOL_PINNED 0x02 /* page is pinned into memory */
+ u_int8_t flags; /* flags */
+} BKT;
+
+typedef struct MPOOL {
+ CIRCLEQ_HEAD(_lqh, _bkt) lqh; /* lru queue head */
+ /* hash queue array */
+ CIRCLEQ_HEAD(_hqh, _bkt) hqh[HASHSIZE];
+ pgno_t curcache; /* current number of cached pages */
+ pgno_t maxcache; /* max number of cached pages */
+ pgno_t npages; /* number of pages in the file */
+ u_long pagesize; /* file page size */
+ int fd; /* file descriptor */
+ /* page in conversion routine */
+ void (*pgin) __P((void *, pgno_t, void *));
+ /* page out conversion routine */
+ void (*pgout) __P((void *, pgno_t, void *));
+ void *pgcookie; /* cookie for page in/out routines */
+#ifdef STATISTICS
+ u_long cachehit;
+ u_long cachemiss;
+ u_long pagealloc;
+ u_long pageflush;
+ u_long pageget;
+ u_long pagenew;
+ u_long pageput;
+ u_long pageread;
+ u_long pagewrite;
+#endif
+} MPOOL;
+
+__BEGIN_DECLS
+MPOOL *__mpool_open __P((void *, int, pgno_t, pgno_t));
+MPOOL *mpool_open __P((void *, int, pgno_t, pgno_t));
+void __mpool_filter __P((MPOOL *, void (*)(void *, pgno_t, void *),
+ void (*)(void *, pgno_t, void *), void *));
+void mpool_filter __P((MPOOL *, void (*)(void *, pgno_t, void *),
+ void (*)(void *, pgno_t, void *), void *));
+void *__mpool_new __P((MPOOL *, pgno_t *));
+void *mpool_new __P((MPOOL *, pgno_t *));
+void *__mpool_get __P((MPOOL *, pgno_t, u_int));
+void *mpool_get __P((MPOOL *, pgno_t, u_int));
+int __mpool_put __P((MPOOL *, void *, u_int));
+int mpool_put __P((MPOOL *, void *, u_int));
+int __mpool_sync __P((MPOOL *));
+int mpool_sync __P((MPOOL *));
+int __mpool_close __P((MPOOL *));
+int mpool_close __P((MPOOL *));
+#ifdef STATISTICS
+void mpool_stat __P((MPOOL *));
+#endif
+__END_DECLS
+
+#endif /* mpool.h */
diff --git a/trunk/main/db1-ast/include/ndbm.h b/trunk/main/db1-ast/include/ndbm.h
new file mode 100644
index 000000000..d2079b0a5
--- /dev/null
+++ b/trunk/main/db1-ast/include/ndbm.h
@@ -0,0 +1,79 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Margo Seltzer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ndbm.h 8.1 (Berkeley) 6/2/93
+ */
+
+#ifndef _NDBM_H
+#define _NDBM_H 1
+
+#include "db.h"
+
+/* Map dbm interface onto db(3). */
+#define DBM_RDONLY O_RDONLY
+
+/* Flags to dbm_store(). */
+#define DBM_INSERT 0
+#define DBM_REPLACE 1
+
+/*
+ * The db(3) support for ndbm(3) always appends this suffix to the
+ * file name to avoid overwriting the user's original database.
+ */
+#define DBM_SUFFIX ".db"
+
+typedef struct {
+ char *dptr;
+ int dsize;
+} datum;
+
+typedef DB DBM;
+#define dbm_pagfno(a) DBM_PAGFNO_NOT_AVAILABLE
+
+__BEGIN_DECLS
+void dbm_close __P((DBM *));
+int dbm_delete __P((DBM *, datum));
+datum dbm_fetch __P((DBM *, datum));
+datum dbm_firstkey __P((DBM *));
+long dbm_forder __P((DBM *, datum));
+datum dbm_nextkey __P((DBM *));
+DBM *dbm_open __P((const char *, int, int));
+int dbm_store __P((DBM *, datum, datum, int));
+int dbm_dirfno __P((DBM *));
+int dbm_error __P((DBM *));
+int dbm_clearerr __P((DBM *));
+__END_DECLS
+
+#endif /* ndbm.h */
diff --git a/trunk/main/db1-ast/libdb.map b/trunk/main/db1-ast/libdb.map
new file mode 100644
index 000000000..87e34c430
--- /dev/null
+++ b/trunk/main/db1-ast/libdb.map
@@ -0,0 +1,11 @@
+GLIBC_2.0 {
+ global:
+ # the real DB entry point.
+ dbopen; __dbopen;
+
+ # The compatibility functions.
+ dbm_clearerr; dbm_close; dbm_delete; dbm_dirfno; dbm_error;
+ dbm_fetch; dbm_firstkey; dbm_nextkey; dbm_open; dbm_store;
+ local:
+ *;
+};
diff --git a/trunk/main/db1-ast/mpool/README b/trunk/main/db1-ast/mpool/README
new file mode 100644
index 000000000..0f01fbcdb
--- /dev/null
+++ b/trunk/main/db1-ast/mpool/README
@@ -0,0 +1,7 @@
+# @(#)README 8.1 (Berkeley) 6/4/93
+
+These are the current memory pool routines.
+They aren't ready for prime time, yet, and
+the interface is expected to change.
+
+--keith
diff --git a/trunk/main/db1-ast/mpool/mpool.c b/trunk/main/db1-ast/mpool/mpool.c
new file mode 100644
index 000000000..b5b7c86d6
--- /dev/null
+++ b/trunk/main/db1-ast/mpool/mpool.c
@@ -0,0 +1,498 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)mpool.c 8.5 (Berkeley) 7/26/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "../include/db.h"
+
+#define __MPOOLINTERFACE_PRIVATE
+#include <mpool.h>
+
+#undef __APPLE__
+
+#ifndef __APPLE__
+#define mpool_open __mpool_open
+#define mpool_filter __mpool_filter
+#define mpool_new __mpool_new
+#define mpool_get __mpool_get
+#define mpool_put __mpool_put
+#define mpool_sync __mpool_sync
+#define mpool_close __mpool_close
+#endif
+
+static BKT *mpool_bkt __P((MPOOL *));
+static BKT *mpool_look __P((MPOOL *, pgno_t));
+static int mpool_write __P((MPOOL *, BKT *));
+
+/*
+ * mpool_open --
+ * Initialize a memory pool.
+ */
+MPOOL *
+mpool_open(key, fd, pagesize, maxcache)
+ void *key;
+ int fd;
+ pgno_t pagesize, maxcache;
+{
+ struct stat sb;
+ MPOOL *mp;
+ int entry;
+
+ /*
+ * Get information about the file.
+ *
+ * XXX
+ * We don't currently handle pipes, although we should.
+ */
+ if (fstat(fd, &sb))
+ return (NULL);
+ if (!S_ISREG(sb.st_mode)) {
+ errno = ESPIPE;
+ return (NULL);
+ }
+
+ /* Allocate and initialize the MPOOL cookie. */
+ if ((mp = (MPOOL *)calloc(1, sizeof(MPOOL))) == NULL)
+ return (NULL);
+ CIRCLEQ_INIT(&mp->lqh);
+ for (entry = 0; entry < HASHSIZE; ++entry)
+ CIRCLEQ_INIT(&mp->hqh[entry]);
+ mp->maxcache = maxcache;
+ mp->npages = sb.st_size / pagesize;
+ mp->pagesize = pagesize;
+ mp->fd = fd;
+ return (mp);
+}
+
+/*
+ * mpool_filter --
+ * Initialize input/output filters.
+ */
+void
+mpool_filter(mp, pgin, pgout, pgcookie)
+ MPOOL *mp;
+ void (*pgin) __P((void *, pgno_t, void *));
+ void (*pgout) __P((void *, pgno_t, void *));
+ void *pgcookie;
+{
+ mp->pgin = pgin;
+ mp->pgout = pgout;
+ mp->pgcookie = pgcookie;
+}
+
+/*
+ * mpool_new --
+ * Get a new page of memory.
+ */
+void *
+mpool_new(mp, pgnoaddr)
+ MPOOL *mp;
+ pgno_t *pgnoaddr;
+{
+ struct _hqh *head;
+ BKT *bp;
+
+ if (mp->npages == MAX_PAGE_NUMBER) {
+ (void)fprintf(stderr, "mpool_new: page allocation overflow.\n");
+ abort();
+ }
+#ifdef STATISTICS
+ ++mp->pagenew;
+#endif
+ /*
+ * Get a BKT from the cache. Assign a new page number, attach
+ * it to the head of the hash chain, the tail of the lru chain,
+ * and return.
+ */
+ if ((bp = mpool_bkt(mp)) == NULL)
+ return (NULL);
+ *pgnoaddr = bp->pgno = mp->npages++;
+ bp->flags = MPOOL_PINNED;
+
+ head = &mp->hqh[HASHKEY(bp->pgno)];
+ CIRCLEQ_INSERT_HEAD(head, bp, hq);
+ CIRCLEQ_INSERT_TAIL(&mp->lqh, bp, q);
+ return (bp->page);
+}
+
+/*
+ * mpool_get
+ * Get a page.
+ */
+void *
+mpool_get(mp, pgno, flags)
+ MPOOL *mp;
+ pgno_t pgno;
+ u_int flags; /* XXX not used? */
+{
+ struct _hqh *head;
+ BKT *bp;
+ off_t off;
+ int nr;
+
+ /* Check for attempt to retrieve a non-existent page. */
+ if (pgno >= mp->npages) {
+ errno = EINVAL;
+ return (NULL);
+ }
+
+#ifdef STATISTICS
+ ++mp->pageget;
+#endif
+
+ /* Check for a page that is cached. */
+ if ((bp = mpool_look(mp, pgno)) != NULL) {
+#ifdef DEBUG
+ if (bp->flags & MPOOL_PINNED) {
+ (void)fprintf(stderr,
+ "mpool_get: page %d already pinned\n", bp->pgno);
+ abort();
+ }
+#endif
+ /*
+ * Move the page to the head of the hash chain and the tail
+ * of the lru chain.
+ */
+ head = &mp->hqh[HASHKEY(bp->pgno)];
+ CIRCLEQ_REMOVE(head, bp, hq);
+ CIRCLEQ_INSERT_HEAD(head, bp, hq);
+ CIRCLEQ_REMOVE(&mp->lqh, bp, q);
+ CIRCLEQ_INSERT_TAIL(&mp->lqh, bp, q);
+
+ /* Return a pinned page. */
+ bp->flags |= MPOOL_PINNED;
+ return (bp->page);
+ }
+
+ /* Get a page from the cache. */
+ if ((bp = mpool_bkt(mp)) == NULL)
+ return (NULL);
+
+ /* Read in the contents. */
+#ifdef STATISTICS
+ ++mp->pageread;
+#endif
+ off = mp->pagesize * pgno;
+ if (lseek(mp->fd, off, SEEK_SET) != off)
+ return (NULL);
+ if ((u_long) (nr = read(mp->fd, bp->page, mp->pagesize))
+ != mp->pagesize) {
+ if (nr >= 0)
+ errno = EFTYPE;
+ return (NULL);
+ }
+
+ /* Set the page number, pin the page. */
+ bp->pgno = pgno;
+ bp->flags = MPOOL_PINNED;
+
+ /*
+ * Add the page to the head of the hash chain and the tail
+ * of the lru chain.
+ */
+ head = &mp->hqh[HASHKEY(bp->pgno)];
+ CIRCLEQ_INSERT_HEAD(head, bp, hq);
+ CIRCLEQ_INSERT_TAIL(&mp->lqh, bp, q);
+
+ /* Run through the user's filter. */
+ if (mp->pgin != NULL)
+ (mp->pgin)(mp->pgcookie, bp->pgno, bp->page);
+
+ return (bp->page);
+}
+
+/*
+ * mpool_put
+ * Return a page.
+ */
+int
+mpool_put(mp, page, flags)
+ MPOOL *mp;
+ void *page;
+ u_int flags;
+{
+ BKT *bp;
+
+#ifdef STATISTICS
+ ++mp->pageput;
+#endif
+ bp = (BKT *)((char *)page - sizeof(BKT));
+#ifdef DEBUG
+ if (!(bp->flags & MPOOL_PINNED)) {
+ (void)fprintf(stderr,
+ "mpool_put: page %d not pinned\n", bp->pgno);
+ abort();
+ }
+#endif
+ bp->flags &= ~MPOOL_PINNED;
+ bp->flags |= flags & MPOOL_DIRTY;
+ return (RET_SUCCESS);
+}
+
+/*
+ * mpool_close
+ * Close the buffer pool.
+ */
+int
+mpool_close(mp)
+ MPOOL *mp;
+{
+ BKT *bp;
+
+ /* Free up any space allocated to the lru pages. */
+ while ((bp = mp->lqh.cqh_first) != (void *)&mp->lqh) {
+ CIRCLEQ_REMOVE(&mp->lqh, mp->lqh.cqh_first, q);
+ free(bp);
+ }
+
+ /* Free the MPOOL cookie. */
+ free(mp);
+ return (RET_SUCCESS);
+}
+
+/*
+ * mpool_sync
+ * Sync the pool to disk.
+ */
+int
+mpool_sync(mp)
+ MPOOL *mp;
+{
+ BKT *bp;
+
+ /* Walk the lru chain, flushing any dirty pages to disk. */
+ for (bp = mp->lqh.cqh_first;
+ bp != (void *)&mp->lqh; bp = bp->q.cqe_next)
+ if (bp->flags & MPOOL_DIRTY &&
+ mpool_write(mp, bp) == RET_ERROR)
+ return (RET_ERROR);
+
+ /* Sync the file descriptor. */
+ return (fsync(mp->fd) ? RET_ERROR : RET_SUCCESS);
+}
+
+#define __APPLE__
+
+#ifndef __APPLE__
+#undef mpool_open
+#undef mpool_filter
+#undef mpool_new
+#undef mpool_get
+#undef mpool_put
+#undef mpool_close
+#undef mpool_sync
+
+#define weak_alias(original, alias) \
+ asm (".weak " #alias "\n" #alias " = " #original);
+weak_alias (__mpool_open, mpool_open)
+weak_alias (__mpool_filter, mpool_filter)
+weak_alias (__mpool_new, mpool_new)
+weak_alias (__mpool_get, mpool_get)
+weak_alias (__mpool_put, mpool_put)
+weak_alias (__mpool_close, mpool_close)
+weak_alias (__mpool_sync, mpool_sync)
+#endif
+
+/*
+ * mpool_bkt
+ * Get a page from the cache (or create one).
+ */
+static BKT *
+mpool_bkt(mp)
+ MPOOL *mp;
+{
+ struct _hqh *head;
+ BKT *bp;
+
+ /* If under the max cached, always create a new page. */
+ if (mp->curcache < mp->maxcache)
+ goto new;
+
+ /*
+ * If the cache is max'd out, walk the lru list for a buffer we
+ * can flush. If we find one, write it (if necessary) and take it
+ * off any lists. If we don't find anything we grow the cache anyway.
+ * The cache never shrinks.
+ */
+ for (bp = mp->lqh.cqh_first;
+ bp != (void *)&mp->lqh; bp = bp->q.cqe_next)
+ if (!(bp->flags & MPOOL_PINNED)) {
+ /* Flush if dirty. */
+ if (bp->flags & MPOOL_DIRTY &&
+ mpool_write(mp, bp) == RET_ERROR)
+ return (NULL);
+#ifdef STATISTICS
+ ++mp->pageflush;
+#endif
+ /* Remove from the hash and lru queues. */
+ head = &mp->hqh[HASHKEY(bp->pgno)];
+ CIRCLEQ_REMOVE(head, bp, hq);
+ CIRCLEQ_REMOVE(&mp->lqh, bp, q);
+#ifdef DEBUG
+ { void *spage;
+ spage = bp->page;
+ memset(bp, 0xff, sizeof(BKT) + mp->pagesize);
+ bp->page = spage;
+ }
+#endif
+ return (bp);
+ }
+
+new: if ((bp = (BKT *)malloc(sizeof(BKT) + mp->pagesize)) == NULL)
+ return (NULL);
+#ifdef STATISTICS
+ ++mp->pagealloc;
+#endif
+#if defined(DEBUG) || defined(PURIFY)
+ memset(bp, 0xff, sizeof(BKT) + mp->pagesize);
+#endif
+ bp->page = (char *)bp + sizeof(BKT);
+ ++mp->curcache;
+ return (bp);
+}
+
+/*
+ * mpool_write
+ * Write a page to disk.
+ */
+static int
+mpool_write(mp, bp)
+ MPOOL *mp;
+ BKT *bp;
+{
+ off_t off;
+
+#ifdef STATISTICS
+ ++mp->pagewrite;
+#endif
+
+ /* Run through the user's filter. */
+ if (mp->pgout)
+ (mp->pgout)(mp->pgcookie, bp->pgno, bp->page);
+
+ off = mp->pagesize * bp->pgno;
+ if (lseek(mp->fd, off, SEEK_SET) != off)
+ return (RET_ERROR);
+ if ((u_long) write(mp->fd, bp->page, mp->pagesize) != mp->pagesize)
+ return (RET_ERROR);
+
+ bp->flags &= ~MPOOL_DIRTY;
+ return (RET_SUCCESS);
+}
+
+/*
+ * mpool_look
+ * Lookup a page in the cache.
+ */
+static BKT *
+mpool_look(mp, pgno)
+ MPOOL *mp;
+ pgno_t pgno;
+{
+ struct _hqh *head;
+ BKT *bp;
+
+ head = &mp->hqh[HASHKEY(pgno)];
+ for (bp = head->cqh_first; bp != (void *)head; bp = bp->hq.cqe_next)
+ if (bp->pgno == pgno) {
+#ifdef STATISTICS
+ ++mp->cachehit;
+#endif
+ return (bp);
+ }
+#ifdef STATISTICS
+ ++mp->cachemiss;
+#endif
+ return (NULL);
+}
+
+#ifdef STATISTICS
+/*
+ * mpool_stat
+ * Print out cache statistics.
+ */
+void
+mpool_stat(mp)
+ MPOOL *mp;
+{
+ BKT *bp;
+ int cnt;
+ char *sep;
+
+ (void)fprintf(stderr, "%lu pages in the file\n", mp->npages);
+ (void)fprintf(stderr,
+ "page size %lu, cacheing %lu pages of %lu page max cache\n",
+ mp->pagesize, mp->curcache, mp->maxcache);
+ (void)fprintf(stderr, "%lu page puts, %lu page gets, %lu page new\n",
+ mp->pageput, mp->pageget, mp->pagenew);
+ (void)fprintf(stderr, "%lu page allocs, %lu page flushes\n",
+ mp->pagealloc, mp->pageflush);
+ if (mp->cachehit + mp->cachemiss)
+ (void)fprintf(stderr,
+ "%.0f%% cache hit rate (%lu hits, %lu misses)\n",
+ ((double)mp->cachehit / (mp->cachehit + mp->cachemiss))
+ * 100, mp->cachehit, mp->cachemiss);
+ (void)fprintf(stderr, "%lu page reads, %lu page writes\n",
+ mp->pageread, mp->pagewrite);
+
+ sep = "";
+ cnt = 0;
+ for (bp = mp->lqh.cqh_first;
+ bp != (void *)&mp->lqh; bp = bp->q.cqe_next) {
+ (void)fprintf(stderr, "%s%d", sep, bp->pgno);
+ if (bp->flags & MPOOL_DIRTY)
+ (void)fprintf(stderr, "d");
+ if (bp->flags & MPOOL_PINNED)
+ (void)fprintf(stderr, "P");
+ if (++cnt == 10) {
+ sep = "\n";
+ cnt = 0;
+ } else
+ sep = ", ";
+
+ }
+ (void)fprintf(stderr, "\n");
+}
+#endif
diff --git a/trunk/main/db1-ast/recno/extern.h b/trunk/main/db1-ast/recno/extern.h
new file mode 100644
index 000000000..feed43445
--- /dev/null
+++ b/trunk/main/db1-ast/recno/extern.h
@@ -0,0 +1,54 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)extern.h 8.3 (Berkeley) 6/4/94
+ */
+
+#include "../btree/extern.h"
+
+int __rec_close __P((DB *));
+int __rec_delete __P((const DB *, const DBT *, u_int));
+int __rec_dleaf __P((BTREE *, PAGE *, u_int32_t));
+int __rec_fd __P((const DB *));
+int __rec_fmap __P((BTREE *, recno_t));
+int __rec_fout __P((BTREE *));
+int __rec_fpipe __P((BTREE *, recno_t));
+int __rec_get __P((const DB *, const DBT *, DBT *, u_int));
+int __rec_iput __P((BTREE *, recno_t, const DBT *, u_int));
+int __rec_put __P((const DB *dbp, DBT *, const DBT *, u_int));
+int __rec_ret __P((BTREE *, EPG *, recno_t, DBT *, DBT *));
+EPG *__rec_search __P((BTREE *, recno_t, enum SRCHOP));
+int __rec_seq __P((const DB *, DBT *, DBT *, u_int));
+int __rec_sync __P((const DB *, u_int));
+int __rec_vmap __P((BTREE *, recno_t));
+int __rec_vout __P((BTREE *));
+int __rec_vpipe __P((BTREE *, recno_t));
diff --git a/trunk/main/db1-ast/recno/rec_close.c b/trunk/main/db1-ast/recno/rec_close.c
new file mode 100644
index 000000000..20b00f52c
--- /dev/null
+++ b/trunk/main/db1-ast/recno/rec_close.c
@@ -0,0 +1,183 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)rec_close.c 8.6 (Berkeley) 8/18/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/mman.h>
+
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "../include/db.h"
+#include "recno.h"
+
+/*
+ * __REC_CLOSE -- Close a recno tree.
+ *
+ * Parameters:
+ * dbp: pointer to access method
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS
+ */
+int
+__rec_close(dbp)
+ DB *dbp;
+{
+ BTREE *t;
+ int status;
+
+ t = dbp->internal;
+
+ /* Toss any page pinned across calls. */
+ if (t->bt_pinned != NULL) {
+ mpool_put(t->bt_mp, t->bt_pinned, 0);
+ t->bt_pinned = NULL;
+ }
+
+ if (__rec_sync(dbp, 0) == RET_ERROR)
+ return (RET_ERROR);
+
+ /* Committed to closing. */
+ status = RET_SUCCESS;
+ if (F_ISSET(t, R_MEMMAPPED) && munmap(t->bt_smap, t->bt_msize))
+ status = RET_ERROR;
+
+ if (!F_ISSET(t, R_INMEM)) {
+ if (F_ISSET(t, R_CLOSEFP)) {
+ if (fclose(t->bt_rfp))
+ status = RET_ERROR;
+ } else
+ if (close(t->bt_rfd))
+ status = RET_ERROR;
+ }
+
+ if (__bt_close(dbp) == RET_ERROR)
+ status = RET_ERROR;
+
+ return (status);
+}
+
+/*
+ * __REC_SYNC -- sync the recno tree to disk.
+ *
+ * Parameters:
+ * dbp: pointer to access method
+ *
+ * Returns:
+ * RET_SUCCESS, RET_ERROR.
+ */
+int
+__rec_sync(dbp, flags)
+ const DB *dbp;
+ u_int flags;
+{
+ struct iovec iov[2];
+ BTREE *t;
+ DBT data, key;
+ off_t off;
+ recno_t scursor, trec;
+ int status;
+
+ t = dbp->internal;
+
+ /* Toss any page pinned across calls. */
+ if (t->bt_pinned != NULL) {
+ mpool_put(t->bt_mp, t->bt_pinned, 0);
+ t->bt_pinned = NULL;
+ }
+
+ if (flags == R_RECNOSYNC)
+ return (__bt_sync(dbp, 0));
+
+ if (F_ISSET(t, R_RDONLY | R_INMEM) || !F_ISSET(t, R_MODIFIED))
+ return (RET_SUCCESS);
+
+ /* Read any remaining records into the tree. */
+ if (!F_ISSET(t, R_EOF) && t->bt_irec(t, MAX_REC_NUMBER) == RET_ERROR)
+ return (RET_ERROR);
+
+ /* Rewind the file descriptor. */
+ if (lseek(t->bt_rfd, (off_t)0, SEEK_SET) != 0)
+ return (RET_ERROR);
+
+ /* Save the cursor. */
+ scursor = t->bt_cursor.rcursor;
+
+ key.size = sizeof(recno_t);
+ key.data = &trec;
+
+ if (F_ISSET(t, R_FIXLEN)) {
+ /*
+ * We assume that fixed length records are all fixed length.
+ * Any that aren't are either EINVAL'd or corrected by the
+ * record put code.
+ */
+ status = (dbp->seq)(dbp, &key, &data, R_FIRST);
+ while (status == RET_SUCCESS) {
+ if ((size_t) write(t->bt_rfd, data.data, data.size) != data.size)
+ return (RET_ERROR);
+ status = (dbp->seq)(dbp, &key, &data, R_NEXT);
+ }
+ } else {
+ iov[1].iov_base = &t->bt_bval;
+ iov[1].iov_len = 1;
+
+ status = (dbp->seq)(dbp, &key, &data, R_FIRST);
+ while (status == RET_SUCCESS) {
+ iov[0].iov_base = data.data;
+ iov[0].iov_len = data.size;
+ if ((size_t) writev(t->bt_rfd, iov, 2) != data.size + 1)
+ return (RET_ERROR);
+ status = (dbp->seq)(dbp, &key, &data, R_NEXT);
+ }
+ }
+
+ /* Restore the cursor. */
+ t->bt_cursor.rcursor = scursor;
+
+ if (status == RET_ERROR)
+ return (RET_ERROR);
+ if ((off = lseek(t->bt_rfd, (off_t)0, SEEK_CUR)) == -1)
+ return (RET_ERROR);
+ if (ftruncate(t->bt_rfd, off))
+ return (RET_ERROR);
+ F_CLR(t, R_MODIFIED);
+ return (RET_SUCCESS);
+}
diff --git a/trunk/main/db1-ast/recno/rec_delete.c b/trunk/main/db1-ast/recno/rec_delete.c
new file mode 100644
index 000000000..fc2047226
--- /dev/null
+++ b/trunk/main/db1-ast/recno/rec_delete.c
@@ -0,0 +1,197 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Olson.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)rec_delete.c 8.7 (Berkeley) 7/14/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "../include/db.h"
+#include "recno.h"
+
+static int rec_rdelete __P((BTREE *, recno_t));
+
+/*
+ * __REC_DELETE -- Delete the item(s) referenced by a key.
+ *
+ * Parameters:
+ * dbp: pointer to access method
+ * key: key to delete
+ * flags: R_CURSOR if deleting what the cursor references
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS and RET_SPECIAL if the key not found.
+ */
+int
+__rec_delete(dbp, key, flags)
+ const DB *dbp;
+ const DBT *key;
+ u_int flags;
+{
+ BTREE *t;
+ recno_t nrec;
+ int status;
+
+ t = dbp->internal;
+
+ /* Toss any page pinned across calls. */
+ if (t->bt_pinned != NULL) {
+ mpool_put(t->bt_mp, t->bt_pinned, 0);
+ t->bt_pinned = NULL;
+ }
+
+ switch(flags) {
+ case 0:
+ if ((nrec = *(recno_t *)key->data) == 0)
+ goto einval;
+ if (nrec > t->bt_nrecs)
+ return (RET_SPECIAL);
+ --nrec;
+ status = rec_rdelete(t, nrec);
+ break;
+ case R_CURSOR:
+ if (!F_ISSET(&t->bt_cursor, CURS_INIT))
+ goto einval;
+ if (t->bt_nrecs == 0)
+ return (RET_SPECIAL);
+ status = rec_rdelete(t, t->bt_cursor.rcursor - 1);
+ if (status == RET_SUCCESS)
+ --t->bt_cursor.rcursor;
+ break;
+ default:
+einval: errno = EINVAL;
+ return (RET_ERROR);
+ }
+
+ if (status == RET_SUCCESS)
+ F_SET(t, B_MODIFIED | R_MODIFIED);
+ return (status);
+}
+
+/*
+ * REC_RDELETE -- Delete the data matching the specified key.
+ *
+ * Parameters:
+ * tree: tree
+ * nrec: record to delete
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS and RET_SPECIAL if the key not found.
+ */
+static int
+rec_rdelete(t, nrec)
+ BTREE *t;
+ recno_t nrec;
+{
+ EPG *e;
+ PAGE *h;
+ int status;
+
+ /* Find the record; __rec_search pins the page. */
+ if ((e = __rec_search(t, nrec, SDELETE)) == NULL)
+ return (RET_ERROR);
+
+ /* Delete the record. */
+ h = e->page;
+ status = __rec_dleaf(t, h, e->index);
+ if (status != RET_SUCCESS) {
+ mpool_put(t->bt_mp, h, 0);
+ return (status);
+ }
+ mpool_put(t->bt_mp, h, MPOOL_DIRTY);
+ return (RET_SUCCESS);
+}
+
+/*
+ * __REC_DLEAF -- Delete a single record from a recno leaf page.
+ *
+ * Parameters:
+ * t: tree
+ * index: index on current page to delete
+ *
+ * Returns:
+ * RET_SUCCESS, RET_ERROR.
+ */
+int
+__rec_dleaf(t, h, index)
+ BTREE *t;
+ PAGE *h;
+ u_int32_t index;
+{
+ RLEAF *rl;
+ indx_t *ip, cnt, offset;
+ u_int32_t nbytes;
+ char *from;
+ void *to;
+
+ /*
+ * Delete a record from a recno leaf page. Internal records are never
+ * deleted from internal pages, regardless of the records that caused
+ * them to be added being deleted. Pages made empty by deletion are
+ * not reclaimed. They are, however, made available for reuse.
+ *
+ * Pack the remaining entries at the end of the page, shift the indices
+ * down, overwriting the deleted record and its index. If the record
+ * uses overflow pages, make them available for reuse.
+ */
+ to = rl = GETRLEAF(h, index);
+ if (rl->flags & P_BIGDATA && __ovfl_delete(t, rl->bytes) == RET_ERROR)
+ return (RET_ERROR);
+ nbytes = NRLEAF(rl);
+
+ /*
+ * Compress the key/data pairs. Compress and adjust the [BR]LEAF
+ * offsets. Reset the headers.
+ */
+ from = (char *)h + h->upper;
+ memmove(from + nbytes, from, (char *)to - from);
+ h->upper += nbytes;
+
+ offset = h->linp[index];
+ for (cnt = &h->linp[index] - (ip = &h->linp[0]); cnt--; ++ip)
+ if (ip[0] < offset)
+ ip[0] += nbytes;
+ for (cnt = &h->linp[NEXTINDEX(h)] - ip; --cnt; ++ip)
+ ip[0] = ip[1] < offset ? ip[1] + nbytes : ip[1];
+ h->lower -= sizeof(indx_t);
+ --t->bt_nrecs;
+ return (RET_SUCCESS);
+}
diff --git a/trunk/main/db1-ast/recno/rec_get.c b/trunk/main/db1-ast/recno/rec_get.c
new file mode 100644
index 000000000..7038cc81a
--- /dev/null
+++ b/trunk/main/db1-ast/recno/rec_get.c
@@ -0,0 +1,311 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)rec_get.c 8.9 (Berkeley) 8/18/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "../include/db.h"
+#include "recno.h"
+
+/*
+ * __REC_GET -- Get a record from the btree.
+ *
+ * Parameters:
+ * dbp: pointer to access method
+ * key: key to find
+ * data: data to return
+ * flag: currently unused
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS and RET_SPECIAL if the key not found.
+ */
+int
+__rec_get(dbp, key, data, flags)
+ const DB *dbp;
+ const DBT *key;
+ DBT *data;
+ u_int flags;
+{
+ BTREE *t;
+ EPG *e;
+ recno_t nrec;
+ int status;
+
+ t = dbp->internal;
+
+ /* Toss any page pinned across calls. */
+ if (t->bt_pinned != NULL) {
+ mpool_put(t->bt_mp, t->bt_pinned, 0);
+ t->bt_pinned = NULL;
+ }
+
+ /* Get currently doesn't take any flags, and keys of 0 are illegal. */
+ if (flags || (nrec = *(recno_t *)key->data) == 0) {
+ errno = EINVAL;
+ return (RET_ERROR);
+ }
+
+ /*
+ * If we haven't seen this record yet, try to find it in the
+ * original file.
+ */
+ if (nrec > t->bt_nrecs) {
+ if (F_ISSET(t, R_EOF | R_INMEM))
+ return (RET_SPECIAL);
+ if ((status = t->bt_irec(t, nrec)) != RET_SUCCESS)
+ return (status);
+ }
+
+ --nrec;
+ if ((e = __rec_search(t, nrec, SEARCH)) == NULL)
+ return (RET_ERROR);
+
+ status = __rec_ret(t, e, 0, NULL, data);
+ if (F_ISSET(t, B_DB_LOCK))
+ mpool_put(t->bt_mp, e->page, 0);
+ else
+ t->bt_pinned = e->page;
+ return (status);
+}
+
+/*
+ * __REC_FPIPE -- Get fixed length records from a pipe.
+ *
+ * Parameters:
+ * t: tree
+ * cnt: records to read
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS
+ */
+int
+__rec_fpipe(t, top)
+ BTREE *t;
+ recno_t top;
+{
+ DBT data;
+ recno_t nrec;
+ size_t len;
+ int ch;
+ u_char *p;
+
+ if (t->bt_rdata.size < t->bt_reclen) {
+ t->bt_rdata.data = t->bt_rdata.data == NULL ?
+ malloc(t->bt_reclen) :
+ realloc(t->bt_rdata.data, t->bt_reclen);
+ if (t->bt_rdata.data == NULL)
+ return (RET_ERROR);
+ t->bt_rdata.size = t->bt_reclen;
+ }
+ data.data = t->bt_rdata.data;
+ data.size = t->bt_reclen;
+
+ for (nrec = t->bt_nrecs; nrec < top;) {
+ len = t->bt_reclen;
+ for (p = t->bt_rdata.data;; *p++ = ch)
+ if ((ch = getc(t->bt_rfp)) == EOF || !--len) {
+ if (ch != EOF)
+ *p = ch;
+ if (len != 0)
+ memset(p, t->bt_bval, len);
+ if (__rec_iput(t,
+ nrec, &data, 0) != RET_SUCCESS)
+ return (RET_ERROR);
+ ++nrec;
+ break;
+ }
+ if (ch == EOF)
+ break;
+ }
+ if (nrec < top) {
+ F_SET(t, R_EOF);
+ return (RET_SPECIAL);
+ }
+ return (RET_SUCCESS);
+}
+
+/*
+ * __REC_VPIPE -- Get variable length records from a pipe.
+ *
+ * Parameters:
+ * t: tree
+ * cnt: records to read
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS
+ */
+int
+__rec_vpipe(t, top)
+ BTREE *t;
+ recno_t top;
+{
+ DBT data;
+ recno_t nrec;
+ indx_t len;
+ size_t sz;
+ int bval, ch;
+ u_char *p;
+
+ bval = t->bt_bval;
+ for (nrec = t->bt_nrecs; nrec < top; ++nrec) {
+ for (p = t->bt_rdata.data,
+ sz = t->bt_rdata.size;; *p++ = ch, --sz) {
+ if ((ch = getc(t->bt_rfp)) == EOF || ch == bval) {
+ data.data = t->bt_rdata.data;
+ data.size = p - (u_char *)t->bt_rdata.data;
+ if (ch == EOF && data.size == 0)
+ break;
+ if (__rec_iput(t, nrec, &data, 0)
+ != RET_SUCCESS)
+ return (RET_ERROR);
+ break;
+ }
+ if (sz == 0) {
+ len = p - (u_char *)t->bt_rdata.data;
+ t->bt_rdata.size += (sz = 256);
+ t->bt_rdata.data = t->bt_rdata.data == NULL ?
+ malloc(t->bt_rdata.size) :
+ realloc(t->bt_rdata.data, t->bt_rdata.size);
+ if (t->bt_rdata.data == NULL)
+ return (RET_ERROR);
+ p = (u_char *)t->bt_rdata.data + len;
+ }
+ }
+ if (ch == EOF)
+ break;
+ }
+ if (nrec < top) {
+ F_SET(t, R_EOF);
+ return (RET_SPECIAL);
+ }
+ return (RET_SUCCESS);
+}
+
+/*
+ * __REC_FMAP -- Get fixed length records from a file.
+ *
+ * Parameters:
+ * t: tree
+ * cnt: records to read
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS
+ */
+int
+__rec_fmap(t, top)
+ BTREE *t;
+ recno_t top;
+{
+ DBT data;
+ recno_t nrec;
+ u_char *sp, *ep, *p;
+ size_t len;
+
+ if (t->bt_rdata.size < t->bt_reclen) {
+ t->bt_rdata.data = t->bt_rdata.data == NULL ?
+ malloc(t->bt_reclen) :
+ realloc(t->bt_rdata.data, t->bt_reclen);
+ if (t->bt_rdata.data == NULL)
+ return (RET_ERROR);
+ t->bt_rdata.size = t->bt_reclen;
+ }
+ data.data = t->bt_rdata.data;
+ data.size = t->bt_reclen;
+
+ sp = (u_char *)t->bt_cmap;
+ ep = (u_char *)t->bt_emap;
+ for (nrec = t->bt_nrecs; nrec < top; ++nrec) {
+ if (sp >= ep) {
+ F_SET(t, R_EOF);
+ return (RET_SPECIAL);
+ }
+ len = t->bt_reclen;
+ for (p = t->bt_rdata.data;
+ sp < ep && len > 0; *p++ = *sp++, --len);
+ if (len != 0)
+ memset(p, t->bt_bval, len);
+ if (__rec_iput(t, nrec, &data, 0) != RET_SUCCESS)
+ return (RET_ERROR);
+ }
+ t->bt_cmap = (caddr_t)sp;
+ return (RET_SUCCESS);
+}
+
+/*
+ * __REC_VMAP -- Get variable length records from a file.
+ *
+ * Parameters:
+ * t: tree
+ * cnt: records to read
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS
+ */
+int
+__rec_vmap(t, top)
+ BTREE *t;
+ recno_t top;
+{
+ DBT data;
+ u_char *sp, *ep;
+ recno_t nrec;
+ int bval;
+
+ sp = (u_char *)t->bt_cmap;
+ ep = (u_char *)t->bt_emap;
+ bval = t->bt_bval;
+
+ for (nrec = t->bt_nrecs; nrec < top; ++nrec) {
+ if (sp >= ep) {
+ F_SET(t, R_EOF);
+ return (RET_SPECIAL);
+ }
+ for (data.data = sp; sp < ep && *sp != bval; ++sp);
+ data.size = sp - (u_char *)data.data;
+ if (__rec_iput(t, nrec, &data, 0) != RET_SUCCESS)
+ return (RET_ERROR);
+ ++sp;
+ }
+ t->bt_cmap = (caddr_t)sp;
+ return (RET_SUCCESS);
+}
diff --git a/trunk/main/db1-ast/recno/rec_open.c b/trunk/main/db1-ast/recno/rec_open.c
new file mode 100644
index 000000000..0ebc8c7c4
--- /dev/null
+++ b/trunk/main/db1-ast/recno/rec_open.c
@@ -0,0 +1,241 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Olson.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)rec_open.c 8.10 (Berkeley) 9/1/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "../include/db.h"
+#include "recno.h"
+
+DB *
+__rec_open(fname, flags, mode, openinfo, dflags)
+ const char *fname;
+ int flags, mode, dflags;
+ const RECNOINFO *openinfo;
+{
+ BTREE *t;
+ BTREEINFO btopeninfo;
+ DB *dbp;
+ PAGE *h;
+ struct stat sb;
+ int rfd = 0, sverrno;
+
+ /* Open the user's file -- if this fails, we're done. */
+ if (fname != NULL && (rfd = open(fname, flags, mode)) < 0)
+ return (NULL);
+
+ /* Create a btree in memory (backed by disk). */
+ dbp = NULL;
+ if (openinfo) {
+ if (openinfo->flags & ~(R_FIXEDLEN | R_NOKEY | R_SNAPSHOT))
+ goto einval;
+ btopeninfo.flags = 0;
+ btopeninfo.cachesize = openinfo->cachesize;
+ btopeninfo.maxkeypage = 0;
+ btopeninfo.minkeypage = 0;
+ btopeninfo.psize = openinfo->psize;
+ btopeninfo.compare = NULL;
+ btopeninfo.prefix = NULL;
+ btopeninfo.lorder = openinfo->lorder;
+ dbp = __bt_open(openinfo->bfname,
+ O_RDWR, S_IRUSR | S_IWUSR, &btopeninfo, dflags);
+ } else
+ dbp = __bt_open(NULL, O_RDWR, S_IRUSR | S_IWUSR, NULL, dflags);
+ if (dbp == NULL)
+ goto err;
+
+ /*
+ * Some fields in the tree structure are recno specific. Fill them
+ * in and make the btree structure look like a recno structure. We
+ * don't change the bt_ovflsize value, it's close enough and slightly
+ * bigger.
+ */
+ t = dbp->internal;
+ if (openinfo) {
+ if (openinfo->flags & R_FIXEDLEN) {
+ F_SET(t, R_FIXLEN);
+ t->bt_reclen = openinfo->reclen;
+ if (t->bt_reclen == 0)
+ goto einval;
+ }
+ t->bt_bval = openinfo->bval;
+ } else
+ t->bt_bval = '\n';
+
+ F_SET(t, R_RECNO);
+ if (fname == NULL)
+ F_SET(t, R_EOF | R_INMEM);
+ else
+ t->bt_rfd = rfd;
+
+ if (fname != NULL) {
+ /*
+ * In 4.4BSD, stat(2) returns true for ISSOCK on pipes.
+ * Unfortunately, that's not portable, so we use lseek
+ * and check the errno values.
+ */
+ errno = 0;
+ if (lseek(rfd, (off_t)0, SEEK_CUR) == -1 && errno == ESPIPE) {
+ switch (flags & O_ACCMODE) {
+ case O_RDONLY:
+ F_SET(t, R_RDONLY);
+ break;
+ default:
+ goto einval;
+ }
+slow: if ((t->bt_rfp = fdopen(rfd, "r")) == NULL)
+ goto err;
+ F_SET(t, R_CLOSEFP);
+ t->bt_irec =
+ F_ISSET(t, R_FIXLEN) ? __rec_fpipe : __rec_vpipe;
+ } else {
+ switch (flags & O_ACCMODE) {
+ case O_RDONLY:
+ F_SET(t, R_RDONLY);
+ break;
+ case O_RDWR:
+ break;
+ default:
+ goto einval;
+ }
+
+ if (fstat(rfd, &sb))
+ goto err;
+ /*
+ * Kluge -- we'd like to test to see if the file is too
+ * big to mmap. Since, we don't know what size or type
+ * off_t's or size_t's are, what the largest unsigned
+ * integral type is, or what random insanity the local
+ * C compiler will perpetrate, doing the comparison in
+ * a portable way is flatly impossible. Hope that mmap
+ * fails if the file is too large.
+ */
+ if (sb.st_size == 0)
+ F_SET(t, R_EOF);
+ else {
+#ifdef MMAP_NOT_AVAILABLE
+ /*
+ * XXX
+ * Mmap doesn't work correctly on many current
+ * systems. In particular, it can fail subtly,
+ * with cache coherency problems. Don't use it
+ * for now.
+ */
+ t->bt_msize = sb.st_size;
+ if ((t->bt_smap = mmap(NULL, t->bt_msize,
+ PROT_READ, MAP_PRIVATE, rfd,
+ (off_t)0)) == (caddr_t)-1)
+ goto slow;
+ t->bt_cmap = t->bt_smap;
+ t->bt_emap = t->bt_smap + sb.st_size;
+ t->bt_irec = F_ISSET(t, R_FIXLEN) ?
+ __rec_fmap : __rec_vmap;
+ F_SET(t, R_MEMMAPPED);
+#else
+ goto slow;
+#endif
+ }
+ }
+ }
+
+ /* Use the recno routines. */
+ dbp->close = __rec_close;
+ dbp->del = __rec_delete;
+ dbp->fd = __rec_fd;
+ dbp->get = __rec_get;
+ dbp->put = __rec_put;
+ dbp->seq = __rec_seq;
+ dbp->sync = __rec_sync;
+
+ /* If the root page was created, reset the flags. */
+ if ((h = mpool_get(t->bt_mp, P_ROOT, 0)) == NULL)
+ goto err;
+ if ((h->flags & P_TYPE) == P_BLEAF) {
+ F_CLR(h, P_TYPE);
+ F_SET(h, P_RLEAF);
+ mpool_put(t->bt_mp, h, MPOOL_DIRTY);
+ } else
+ mpool_put(t->bt_mp, h, 0);
+
+ if (openinfo && openinfo->flags & R_SNAPSHOT &&
+ !F_ISSET(t, R_EOF | R_INMEM) &&
+ t->bt_irec(t, MAX_REC_NUMBER) == RET_ERROR)
+ goto err;
+ return (dbp);
+
+einval: errno = EINVAL;
+err: sverrno = errno;
+ if (dbp != NULL)
+ (void)__bt_close(dbp);
+ if (fname != NULL)
+ (void)close(rfd);
+ errno = sverrno;
+ return (NULL);
+}
+
+int
+__rec_fd(dbp)
+ const DB *dbp;
+{
+ BTREE *t;
+
+ t = dbp->internal;
+
+ /* Toss any page pinned across calls. */
+ if (t->bt_pinned != NULL) {
+ mpool_put(t->bt_mp, t->bt_pinned, 0);
+ t->bt_pinned = NULL;
+ }
+
+ /* In-memory database can't have a file descriptor. */
+ if (F_ISSET(t, R_INMEM)) {
+ errno = ENOENT;
+ return (-1);
+ }
+ return (t->bt_rfd);
+}
diff --git a/trunk/main/db1-ast/recno/rec_put.c b/trunk/main/db1-ast/recno/rec_put.c
new file mode 100644
index 000000000..331699867
--- /dev/null
+++ b/trunk/main/db1-ast/recno/rec_put.c
@@ -0,0 +1,280 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)rec_put.c 8.7 (Berkeley) 8/18/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../include/db.h"
+#include "recno.h"
+
+/*
+ * __REC_PUT -- Add a recno item to the tree.
+ *
+ * Parameters:
+ * dbp: pointer to access method
+ * key: key
+ * data: data
+ * flag: R_CURSOR, R_IAFTER, R_IBEFORE, R_NOOVERWRITE
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS and RET_SPECIAL if the key is
+ * already in the tree and R_NOOVERWRITE specified.
+ */
+int
+__rec_put(dbp, key, data, flags)
+ const DB *dbp;
+ DBT *key;
+ const DBT *data;
+ u_int flags;
+{
+ BTREE *t;
+ DBT fdata, tdata;
+ recno_t nrec;
+ int status;
+
+ t = dbp->internal;
+
+ /* Toss any page pinned across calls. */
+ if (t->bt_pinned != NULL) {
+ mpool_put(t->bt_mp, t->bt_pinned, 0);
+ t->bt_pinned = NULL;
+ }
+
+ /*
+ * If using fixed-length records, and the record is long, return
+ * EINVAL. If it's short, pad it out. Use the record data return
+ * memory, it's only short-term.
+ */
+ if (F_ISSET(t, R_FIXLEN) && data->size != t->bt_reclen) {
+ if (data->size > t->bt_reclen)
+ goto einval;
+
+ if (t->bt_rdata.size < t->bt_reclen) {
+ t->bt_rdata.data = t->bt_rdata.data == NULL ?
+ malloc(t->bt_reclen) :
+ realloc(t->bt_rdata.data, t->bt_reclen);
+ if (t->bt_rdata.data == NULL)
+ return (RET_ERROR);
+ t->bt_rdata.size = t->bt_reclen;
+ }
+ memmove(t->bt_rdata.data, data->data, data->size);
+ memset((char *)t->bt_rdata.data + data->size,
+ t->bt_bval, t->bt_reclen - data->size);
+ fdata.data = t->bt_rdata.data;
+ fdata.size = t->bt_reclen;
+ } else {
+ fdata.data = data->data;
+ fdata.size = data->size;
+ }
+
+ switch (flags) {
+ case R_CURSOR:
+ if (!F_ISSET(&t->bt_cursor, CURS_INIT))
+ goto einval;
+ nrec = t->bt_cursor.rcursor;
+ break;
+ case R_SETCURSOR:
+ if ((nrec = *(recno_t *)key->data) == 0)
+ goto einval;
+ break;
+ case R_IAFTER:
+ if ((nrec = *(recno_t *)key->data) == 0) {
+ nrec = 1;
+ flags = R_IBEFORE;
+ }
+ break;
+ case 0:
+ case R_IBEFORE:
+ if ((nrec = *(recno_t *)key->data) == 0)
+ goto einval;
+ break;
+ case R_NOOVERWRITE:
+ if ((nrec = *(recno_t *)key->data) == 0)
+ goto einval;
+ if (nrec <= t->bt_nrecs)
+ return (RET_SPECIAL);
+ break;
+ default:
+einval: errno = EINVAL;
+ return (RET_ERROR);
+ }
+
+ /*
+ * Make sure that records up to and including the put record are
+ * already in the database. If skipping records, create empty ones.
+ */
+ if (nrec > t->bt_nrecs) {
+ if (!F_ISSET(t, R_EOF | R_INMEM) &&
+ t->bt_irec(t, nrec) == RET_ERROR)
+ return (RET_ERROR);
+ if (nrec > t->bt_nrecs + 1) {
+ if (F_ISSET(t, R_FIXLEN)) {
+ if ((tdata.data =
+ (void *)malloc(t->bt_reclen)) == NULL)
+ return (RET_ERROR);
+ tdata.size = t->bt_reclen;
+ memset(tdata.data, t->bt_bval, tdata.size);
+ } else {
+ tdata.data = NULL;
+ tdata.size = 0;
+ }
+ while (nrec > t->bt_nrecs + 1)
+ if (__rec_iput(t,
+ t->bt_nrecs, &tdata, 0) != RET_SUCCESS)
+ return (RET_ERROR);
+ if (F_ISSET(t, R_FIXLEN))
+ free(tdata.data);
+ }
+ }
+
+ if ((status = __rec_iput(t, nrec - 1, &fdata, flags)) != RET_SUCCESS)
+ return (status);
+
+ if (flags == R_SETCURSOR)
+ t->bt_cursor.rcursor = nrec;
+
+ F_SET(t, R_MODIFIED);
+ return (__rec_ret(t, NULL, nrec, key, NULL));
+}
+
+/*
+ * __REC_IPUT -- Add a recno item to the tree.
+ *
+ * Parameters:
+ * t: tree
+ * nrec: record number
+ * data: data
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS
+ */
+int
+__rec_iput(t, nrec, data, flags)
+ BTREE *t;
+ recno_t nrec;
+ const DBT *data;
+ u_int flags;
+{
+ DBT tdata;
+ EPG *e;
+ PAGE *h;
+ indx_t index, nxtindex;
+ pgno_t pg;
+ u_int32_t nbytes;
+ int dflags, status;
+ char *dest, db[NOVFLSIZE];
+
+ /*
+ * If the data won't fit on a page, store it on indirect pages.
+ *
+ * XXX
+ * If the insert fails later on, these pages aren't recovered.
+ */
+ if (data->size > t->bt_ovflsize) {
+ if (__ovfl_put(t, data, &pg) == RET_ERROR)
+ return (RET_ERROR);
+ tdata.data = db;
+ tdata.size = NOVFLSIZE;
+ *(pgno_t *)db = pg;
+ *(u_int32_t *)(db + sizeof(pgno_t)) = data->size;
+ dflags = P_BIGDATA;
+ data = &tdata;
+ } else
+ dflags = 0;
+
+ /* __rec_search pins the returned page. */
+ if ((e = __rec_search(t, nrec,
+ nrec > t->bt_nrecs || flags == R_IAFTER || flags == R_IBEFORE ?
+ SINSERT : SEARCH)) == NULL)
+ return (RET_ERROR);
+
+ h = e->page;
+ index = e->index;
+
+ /*
+ * Add the specified key/data pair to the tree. The R_IAFTER and
+ * R_IBEFORE flags insert the key after/before the specified key.
+ *
+ * Pages are split as required.
+ */
+ switch (flags) {
+ case R_IAFTER:
+ ++index;
+ break;
+ case R_IBEFORE:
+ break;
+ default:
+ if (nrec < t->bt_nrecs &&
+ __rec_dleaf(t, h, index) == RET_ERROR) {
+ mpool_put(t->bt_mp, h, 0);
+ return (RET_ERROR);
+ }
+ break;
+ }
+
+ /*
+ * If not enough room, split the page. The split code will insert
+ * the key and data and unpin the current page. If inserting into
+ * the offset array, shift the pointers up.
+ */
+ nbytes = NRLEAFDBT(data->size);
+ if ((u_int32_t) (h->upper - h->lower) < nbytes + sizeof(indx_t)) {
+ status = __bt_split(t, h, NULL, data, dflags, nbytes, index);
+ if (status == RET_SUCCESS)
+ ++t->bt_nrecs;
+ return (status);
+ }
+
+ if (index < (nxtindex = NEXTINDEX(h)))
+ memmove(h->linp + index + 1, h->linp + index,
+ (nxtindex - index) * sizeof(indx_t));
+ h->lower += sizeof(indx_t);
+
+ h->linp[index] = h->upper -= nbytes;
+ dest = (char *)h + h->upper;
+ WR_RLEAF(dest, data, dflags);
+
+ ++t->bt_nrecs;
+ F_SET(t, B_MODIFIED);
+ mpool_put(t->bt_mp, h, MPOOL_DIRTY);
+
+ return (RET_SUCCESS);
+}
diff --git a/trunk/main/db1-ast/recno/rec_search.c b/trunk/main/db1-ast/recno/rec_search.c
new file mode 100644
index 000000000..e70fe4c13
--- /dev/null
+++ b/trunk/main/db1-ast/recno/rec_search.c
@@ -0,0 +1,126 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)rec_search.c 8.4 (Berkeley) 7/14/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stdio.h>
+
+#include "../include/db.h"
+#include "recno.h"
+
+/*
+ * __REC_SEARCH -- Search a btree for a key.
+ *
+ * Parameters:
+ * t: tree to search
+ * recno: key to find
+ * op: search operation
+ *
+ * Returns:
+ * EPG for matching record, if any, or the EPG for the location of the
+ * key, if it were inserted into the tree.
+ *
+ * Returns:
+ * The EPG for matching record, if any, or the EPG for the location
+ * of the key, if it were inserted into the tree, is entered into
+ * the bt_cur field of the tree. A pointer to the field is returned.
+ */
+EPG *
+__rec_search(t, recno, op)
+ BTREE *t;
+ recno_t recno;
+ enum SRCHOP op;
+{
+ register indx_t index;
+ register PAGE *h;
+ EPGNO *parent;
+ RINTERNAL *r;
+ pgno_t pg;
+ indx_t top;
+ recno_t total;
+ int sverrno;
+
+ BT_CLR(t);
+ for (pg = P_ROOT, total = 0;;) {
+ if ((h = mpool_get(t->bt_mp, pg, 0)) == NULL)
+ goto err;
+ if (h->flags & P_RLEAF) {
+ t->bt_cur.page = h;
+ t->bt_cur.index = recno - total;
+ return (&t->bt_cur);
+ }
+ for (index = 0, top = NEXTINDEX(h);;) {
+ r = GETRINTERNAL(h, index);
+ if (++index == top || total + r->nrecs > recno)
+ break;
+ total += r->nrecs;
+ }
+
+ BT_PUSH(t, pg, index - 1);
+
+ pg = r->pgno;
+ switch (op) {
+ case SDELETE:
+ --GETRINTERNAL(h, (index - 1))->nrecs;
+ mpool_put(t->bt_mp, h, MPOOL_DIRTY);
+ break;
+ case SINSERT:
+ ++GETRINTERNAL(h, (index - 1))->nrecs;
+ mpool_put(t->bt_mp, h, MPOOL_DIRTY);
+ break;
+ case SEARCH:
+ mpool_put(t->bt_mp, h, 0);
+ break;
+ }
+
+ }
+ /* Try and recover the tree. */
+err: sverrno = errno;
+ if (op != SEARCH)
+ while ((parent = BT_POP(t)) != NULL) {
+ if ((h = mpool_get(t->bt_mp, parent->pgno, 0)) == NULL)
+ break;
+ if (op == SINSERT)
+ --GETRINTERNAL(h, parent->index)->nrecs;
+ else
+ ++GETRINTERNAL(h, parent->index)->nrecs;
+ mpool_put(t->bt_mp, h, MPOOL_DIRTY);
+ }
+ errno = sverrno;
+ return (NULL);
+}
diff --git a/trunk/main/db1-ast/recno/rec_seq.c b/trunk/main/db1-ast/recno/rec_seq.c
new file mode 100644
index 000000000..ca3451ca6
--- /dev/null
+++ b/trunk/main/db1-ast/recno/rec_seq.c
@@ -0,0 +1,131 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)rec_seq.c 8.3 (Berkeley) 7/14/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "../include/db.h"
+#include "recno.h"
+
+/*
+ * __REC_SEQ -- Recno sequential scan interface.
+ *
+ * Parameters:
+ * dbp: pointer to access method
+ * key: key for positioning and return value
+ * data: data return value
+ * flags: R_CURSOR, R_FIRST, R_LAST, R_NEXT, R_PREV.
+ *
+ * Returns:
+ * RET_ERROR, RET_SUCCESS or RET_SPECIAL if there's no next key.
+ */
+int
+__rec_seq(dbp, key, data, flags)
+ const DB *dbp;
+ DBT *key, *data;
+ u_int flags;
+{
+ BTREE *t;
+ EPG *e;
+ recno_t nrec;
+ int status;
+
+ t = dbp->internal;
+
+ /* Toss any page pinned across calls. */
+ if (t->bt_pinned != NULL) {
+ mpool_put(t->bt_mp, t->bt_pinned, 0);
+ t->bt_pinned = NULL;
+ }
+
+ switch(flags) {
+ case R_CURSOR:
+ if ((nrec = *(recno_t *)key->data) == 0)
+ goto einval;
+ break;
+ case R_NEXT:
+ if (F_ISSET(&t->bt_cursor, CURS_INIT)) {
+ nrec = t->bt_cursor.rcursor + 1;
+ break;
+ }
+ /* FALLTHROUGH */
+ case R_FIRST:
+ nrec = 1;
+ break;
+ case R_PREV:
+ if (F_ISSET(&t->bt_cursor, CURS_INIT)) {
+ if ((nrec = t->bt_cursor.rcursor - 1) == 0)
+ return (RET_SPECIAL);
+ break;
+ }
+ /* FALLTHROUGH */
+ case R_LAST:
+ if (!F_ISSET(t, R_EOF | R_INMEM) &&
+ t->bt_irec(t, MAX_REC_NUMBER) == RET_ERROR)
+ return (RET_ERROR);
+ nrec = t->bt_nrecs;
+ break;
+ default:
+einval: errno = EINVAL;
+ return (RET_ERROR);
+ }
+
+ if (t->bt_nrecs == 0 || nrec > t->bt_nrecs) {
+ if (!F_ISSET(t, R_EOF | R_INMEM) &&
+ (status = t->bt_irec(t, nrec)) != RET_SUCCESS)
+ return (status);
+ if (t->bt_nrecs == 0 || nrec > t->bt_nrecs)
+ return (RET_SPECIAL);
+ }
+
+ if ((e = __rec_search(t, nrec - 1, SEARCH)) == NULL)
+ return (RET_ERROR);
+
+ F_SET(&t->bt_cursor, CURS_INIT);
+ t->bt_cursor.rcursor = nrec;
+
+ status = __rec_ret(t, e, nrec, key, data);
+ if (F_ISSET(t, B_DB_LOCK))
+ mpool_put(t->bt_mp, e->page, 0);
+ else
+ t->bt_pinned = e->page;
+ return (status);
+}
diff --git a/trunk/main/db1-ast/recno/rec_utils.c b/trunk/main/db1-ast/recno/rec_utils.c
new file mode 100644
index 000000000..ddc309712
--- /dev/null
+++ b/trunk/main/db1-ast/recno/rec_utils.c
@@ -0,0 +1,122 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)rec_utils.c 8.6 (Berkeley) 7/16/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/param.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../include/db.h"
+#include "recno.h"
+
+/*
+ * __rec_ret --
+ * Build return data.
+ *
+ * Parameters:
+ * t: tree
+ * e: key/data pair to be returned
+ * nrec: record number
+ * key: user's key structure
+ * data: user's data structure
+ *
+ * Returns:
+ * RET_SUCCESS, RET_ERROR.
+ */
+int
+__rec_ret(t, e, nrec, key, data)
+ BTREE *t;
+ EPG *e;
+ recno_t nrec;
+ DBT *key, *data;
+{
+ RLEAF *rl;
+ void *p;
+
+ if (key == NULL)
+ goto dataonly;
+
+ /* We have to copy the key, it's not on the page. */
+ if (sizeof(recno_t) > t->bt_rkey.size) {
+ p = (void *)(t->bt_rkey.data == NULL ?
+ malloc(sizeof(recno_t)) :
+ realloc(t->bt_rkey.data, sizeof(recno_t)));
+ if (p == NULL)
+ return (RET_ERROR);
+ t->bt_rkey.data = p;
+ t->bt_rkey.size = sizeof(recno_t);
+ }
+ memmove(t->bt_rkey.data, &nrec, sizeof(recno_t));
+ key->size = sizeof(recno_t);
+ key->data = t->bt_rkey.data;
+
+dataonly:
+ if (data == NULL)
+ return (RET_SUCCESS);
+
+ /*
+ * We must copy big keys/data to make them contiguous. Otherwise,
+ * leave the page pinned and don't copy unless the user specified
+ * concurrent access.
+ */
+ rl = GETRLEAF(e->page, e->index);
+ if (rl->flags & P_BIGDATA) {
+ if (__ovfl_get(t, rl->bytes,
+ &data->size, &t->bt_rdata.data, &t->bt_rdata.size))
+ return (RET_ERROR);
+ data->data = t->bt_rdata.data;
+ } else if (F_ISSET(t, B_DB_LOCK)) {
+ /* Use +1 in case the first record retrieved is 0 length. */
+ if (rl->dsize + 1 > t->bt_rdata.size) {
+ p = (void *)(t->bt_rdata.data == NULL ?
+ malloc(rl->dsize + 1) :
+ realloc(t->bt_rdata.data, rl->dsize + 1));
+ if (p == NULL)
+ return (RET_ERROR);
+ t->bt_rdata.data = p;
+ t->bt_rdata.size = rl->dsize + 1;
+ }
+ memmove(t->bt_rdata.data, rl->bytes, rl->dsize);
+ data->size = rl->dsize;
+ data->data = t->bt_rdata.data;
+ } else {
+ data->size = rl->dsize;
+ data->data = rl->bytes;
+ }
+ return (RET_SUCCESS);
+}
diff --git a/trunk/main/db1-ast/recno/recno.h b/trunk/main/db1-ast/recno/recno.h
new file mode 100644
index 000000000..bec772c2f
--- /dev/null
+++ b/trunk/main/db1-ast/recno/recno.h
@@ -0,0 +1,39 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)recno.h 8.1 (Berkeley) 6/4/93
+ */
+
+enum SRCHOP { SDELETE, SINSERT, SEARCH}; /* Rec_search operation. */
+
+#include "../btree/btree.h"
+#include "extern.h"
diff --git a/trunk/main/devicestate.c b/trunk/main/devicestate.c
new file mode 100644
index 000000000..bd9261306
--- /dev/null
+++ b/trunk/main/devicestate.c
@@ -0,0 +1,553 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2007, 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.
+ */
+
+/*! \file
+ *
+ * \brief Device state management
+ *
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \arg \ref AstExtState
+ */
+
+/*! \page AstExtState Extension and device states in Asterisk
+ *
+ * Asterisk has an internal system that reports states
+ * for an extension. By using the dialplan priority -1,
+ * also called a \b hint, a connection can be made from an
+ * extension to one or many devices. The state of the extension
+ * now depends on the combined state of the devices.
+ *
+ * The device state is basically based on the current calls.
+ * If the devicestate engine can find a call from or to the
+ * device, it's in use.
+ *
+ * Some channel drivers implement a callback function for
+ * a better level of reporting device states. The SIP channel
+ * has a complicated system for this, which is improved
+ * by adding call limits to the configuration.
+ *
+ * Functions that want to check the status of an extension
+ * register themself as a \b watcher.
+ * Watchers in this system can subscribe either to all extensions
+ * or just a specific extensions.
+ *
+ * For non-device related states, there's an API called
+ * devicestate providers. This is an extendible system for
+ * delivering state information from outside sources or
+ * functions within Asterisk. Currently we have providers
+ * for app_meetme.c - the conference bridge - and call
+ * parking (metermaids).
+ *
+ * There are manly three subscribers to extension states
+ * within Asterisk:
+ * - AMI, the manager interface
+ * - app_queue.c - the Queue dialplan application
+ * - SIP subscriptions, a.k.a. "blinking lamps" or
+ * "buddy lists"
+ *
+ * The CLI command "show hints" show last known state
+ *
+ * \note None of these handle user states, like an IM presence
+ * system. res_jabber.c can subscribe and watch such states
+ * in jabber/xmpp based systems.
+ *
+ * \section AstDevStateArch Architecture for devicestates
+ *
+ * When a channel driver or asterisk app changes state for
+ * a watched object, it alerts the core. The core queues
+ * a change. When the change is processed, there's a query
+ * sent to the channel driver/provider if there's a function
+ * to handle that, otherwise a channel walk is issued to find
+ * a channel that involves the object.
+ *
+ * The changes are queued and processed by a separate thread.
+ * This thread calls the watchers subscribing to status
+ * changes for the object. For manager, this results
+ * in events. For SIP, NOTIFY requests.
+ *
+ * - Device states
+ * \arg \ref devicestate.c
+ * \arg \ref devicestate.h
+ *
+ * \section AstExtStateArch Architecture for extension states
+ *
+ * Hints are connected to extension. If an extension changes state
+ * it checks the hint devices. If there is a hint, the callbacks into
+ * device states are checked. The aggregated state is set for the hint
+ * and reported back.
+ *
+ * - Extension states
+ * \arg \ref AstENUM ast_extension_states
+ * \arg \ref pbx.c
+ * \arg \ref pbx.h
+ * - Structures
+ * - \ref ast_state_cb struct. Callbacks for watchers
+ * - Callback ast_state_cb_type
+ * - \ref ast_hint struct.
+ * - Functions
+ * - ast_extension_state_add()
+ * - ast_extension_state_del()
+ * - ast_get_hint()
+ *
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/_private.h"
+#include "asterisk/channel.h"
+#include "asterisk/utils.h"
+#include "asterisk/lock.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/devicestate.h"
+#include "asterisk/pbx.h"
+#include "asterisk/app.h"
+#include "asterisk/event.h"
+
+/*! \brief Device state strings for printing */
+static const char *devstatestring[] = {
+ /* 0 AST_DEVICE_UNKNOWN */ "Unknown", /*!< Valid, but unknown state */
+ /* 1 AST_DEVICE_NOT_INUSE */ "Not in use", /*!< Not used */
+ /* 2 AST_DEVICE IN USE */ "In use", /*!< In use */
+ /* 3 AST_DEVICE_BUSY */ "Busy", /*!< Busy */
+ /* 4 AST_DEVICE_INVALID */ "Invalid", /*!< Invalid - not known to Asterisk */
+ /* 5 AST_DEVICE_UNAVAILABLE */ "Unavailable", /*!< Unavailable (not registred) */
+ /* 6 AST_DEVICE_RINGING */ "Ringing", /*!< Ring, ring, ring */
+ /* 7 AST_DEVICE_RINGINUSE */ "Ring+Inuse", /*!< Ring and in use */
+ /* 8 AST_DEVICE_ONHOLD */ "On Hold" /*!< On Hold */
+};
+
+/*! \brief A device state provider (not a channel) */
+struct devstate_prov {
+ char label[40];
+ ast_devstate_prov_cb_type callback;
+ AST_RWLIST_ENTRY(devstate_prov) list;
+};
+
+/*! \brief A list of providers */
+static AST_RWLIST_HEAD_STATIC(devstate_provs, devstate_prov);
+
+struct state_change {
+ AST_LIST_ENTRY(state_change) list;
+ char device[1];
+};
+
+/*! \brief The state change queue. State changes are queued
+ for processing by a separate thread */
+static AST_LIST_HEAD_STATIC(state_changes, state_change);
+
+/*! \brief The device state change notification thread */
+static pthread_t change_thread = AST_PTHREADT_NULL;
+
+/*! \brief Flag for the queue */
+static ast_cond_t change_pending;
+
+/*! \brief Whether or not to cache this device state value */
+enum devstate_cache {
+ /*! Cache this value as it is coming from a device state provider which is
+ * pushing up state change events to us as they happen */
+ CACHE_ON,
+ /*! Don't cache this result, since it was pulled from the device state provider.
+ * We only want to cache results from device state providers that are being nice
+ * and pushing state change events up to us as they happen. */
+ CACHE_OFF,
+};
+
+/* Forward declarations */
+static int getproviderstate(const char *provider, const char *address);
+
+/*! \brief Find devicestate as text message for output */
+const char *devstate2str(enum ast_device_state devstate)
+{
+ return devstatestring[devstate];
+}
+
+const char *ast_devstate_str(enum ast_device_state state)
+{
+ const char *res = "UNKNOWN";
+
+ switch (state) {
+ case AST_DEVICE_UNKNOWN:
+ break;
+ case AST_DEVICE_NOT_INUSE:
+ res = "NOT_INUSE";
+ break;
+ case AST_DEVICE_INUSE:
+ res = "INUSE";
+ break;
+ case AST_DEVICE_BUSY:
+ res = "BUSY";
+ break;
+ case AST_DEVICE_INVALID:
+ res = "INVALID";
+ break;
+ case AST_DEVICE_UNAVAILABLE:
+ res = "UNAVAILABLE";
+ break;
+ case AST_DEVICE_RINGING:
+ res = "RINGING";
+ break;
+ case AST_DEVICE_RINGINUSE:
+ res = "RINGINUSE";
+ break;
+ case AST_DEVICE_ONHOLD:
+ res = "ONHOLD";
+ break;
+ }
+
+ return res;
+}
+
+enum ast_device_state ast_devstate_val(const char *val)
+{
+ if (!strcasecmp(val, "NOT_INUSE"))
+ return AST_DEVICE_NOT_INUSE;
+ else if (!strcasecmp(val, "INUSE"))
+ return AST_DEVICE_INUSE;
+ else if (!strcasecmp(val, "BUSY"))
+ return AST_DEVICE_BUSY;
+ else if (!strcasecmp(val, "INVALID"))
+ return AST_DEVICE_INVALID;
+ else if (!strcasecmp(val, "UNAVAILABLE"))
+ return AST_DEVICE_UNAVAILABLE;
+ else if (!strcasecmp(val, "RINGING"))
+ return AST_DEVICE_RINGING;
+ else if (!strcasecmp(val, "RINGINUSE"))
+ return AST_DEVICE_RINGINUSE;
+ else if (!strcasecmp(val, "ONHOLD"))
+ return AST_DEVICE_ONHOLD;
+
+ return AST_DEVICE_UNKNOWN;
+}
+
+/*! \brief Find out if device is active in a call or not
+ \note find channels with the device's name in it
+ This function is only used for channels that does not implement
+ devicestate natively
+*/
+enum ast_device_state ast_parse_device_state(const char *device)
+{
+ struct ast_channel *chan;
+ char match[AST_CHANNEL_NAME];
+ enum ast_device_state res;
+
+ ast_copy_string(match, device, sizeof(match)-1);
+ strcat(match, "-");
+ chan = ast_get_channel_by_name_prefix_locked(match, strlen(match));
+
+ if (!chan)
+ return AST_DEVICE_UNKNOWN;
+
+ if (chan->_state == AST_STATE_RINGING)
+ res = AST_DEVICE_RINGING;
+ else
+ res = AST_DEVICE_INUSE;
+
+ ast_channel_unlock(chan);
+
+ return res;
+}
+
+static enum ast_device_state devstate_cached(const char *device)
+{
+ enum ast_device_state res = AST_DEVICE_UNKNOWN;
+ struct ast_event *event;
+
+ event = ast_event_get_cached(AST_EVENT_DEVICE_STATE,
+ AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, device,
+ AST_EVENT_IE_END);
+
+ if (!event)
+ return res;
+
+ res = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE);
+
+ ast_event_destroy(event);
+
+ return res;
+}
+
+/*! \brief Check device state through channel specific function or generic function */
+enum ast_device_state ast_device_state(const char *device)
+{
+ char *buf;
+ char *number;
+ const struct ast_channel_tech *chan_tech;
+ enum ast_device_state res;
+ /*! \brief Channel driver that provides device state */
+ char *tech;
+ /*! \brief Another provider of device state */
+ char *provider = NULL;
+
+ /* If the last known state is cached, just return that */
+ res = devstate_cached(device);
+ if (res != AST_DEVICE_UNKNOWN)
+ return res;
+
+ buf = ast_strdupa(device);
+ tech = strsep(&buf, "/");
+ if (!(number = buf)) {
+ if (!(provider = strsep(&tech, ":")))
+ return AST_DEVICE_INVALID;
+ /* We have a provider */
+ number = tech;
+ tech = NULL;
+ }
+
+ if (provider) {
+ ast_debug(3, "Checking if I can find provider for \"%s\" - number: %s\n", provider, number);
+ return getproviderstate(provider, number);
+ }
+
+ ast_debug(4, "No provider found, checking channel drivers for %s - %s\n", tech, number);
+
+ if (!(chan_tech = ast_get_channel_tech(tech)))
+ return AST_DEVICE_INVALID;
+
+ if (!(chan_tech->devicestate)) /* Does the channel driver support device state notification? */
+ return ast_parse_device_state(device); /* No, try the generic function */
+
+ res = chan_tech->devicestate(number);
+
+ if (res != AST_DEVICE_UNKNOWN)
+ return res;
+
+ res = ast_parse_device_state(device);
+
+ if (res == AST_DEVICE_UNKNOWN)
+ return AST_DEVICE_NOT_INUSE;
+
+ return res;
+}
+
+/*! \brief Add device state provider */
+int ast_devstate_prov_add(const char *label, ast_devstate_prov_cb_type callback)
+{
+ struct devstate_prov *devprov;
+
+ if (!callback || !(devprov = ast_calloc(1, sizeof(*devprov))))
+ return -1;
+
+ devprov->callback = callback;
+ ast_copy_string(devprov->label, label, sizeof(devprov->label));
+
+ AST_RWLIST_WRLOCK(&devstate_provs);
+ AST_RWLIST_INSERT_HEAD(&devstate_provs, devprov, list);
+ AST_RWLIST_UNLOCK(&devstate_provs);
+
+ return 0;
+}
+
+/*! \brief Remove device state provider */
+int ast_devstate_prov_del(const char *label)
+{
+ struct devstate_prov *devcb;
+ int res = -1;
+
+ AST_RWLIST_WRLOCK(&devstate_provs);
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&devstate_provs, devcb, list) {
+ if (!strcasecmp(devcb->label, label)) {
+ AST_RWLIST_REMOVE_CURRENT(list);
+ ast_free(devcb);
+ res = 0;
+ break;
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+ AST_RWLIST_UNLOCK(&devstate_provs);
+
+ return res;
+}
+
+/*! \brief Get provider device state */
+static int getproviderstate(const char *provider, const char *address)
+{
+ struct devstate_prov *devprov;
+ int res = AST_DEVICE_INVALID;
+
+
+ AST_RWLIST_RDLOCK(&devstate_provs);
+ AST_RWLIST_TRAVERSE(&devstate_provs, devprov, list) {
+ ast_debug(5, "Checking provider %s with %s\n", devprov->label, provider);
+
+ if (!strcasecmp(devprov->label, provider)) {
+ res = devprov->callback(address);
+ break;
+ }
+ }
+ AST_RWLIST_UNLOCK(&devstate_provs);
+ return res;
+}
+
+static void devstate_event(const char *device, enum ast_device_state state, enum devstate_cache cache)
+{
+ struct ast_event *event;
+
+ if (!(event = ast_event_new(AST_EVENT_DEVICE_STATE,
+ AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, device,
+ AST_EVENT_IE_STATE, AST_EVENT_IE_PLTYPE_UINT, state,
+ AST_EVENT_IE_END))) {
+ return;
+ }
+
+ if (cache == CACHE_ON) {
+ /* Cache this event, replacing an event in the cache with the same
+ * device name if it exists. */
+ ast_event_queue_and_cache(event,
+ AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR,
+ AST_EVENT_IE_END);
+ } else {
+ ast_event_queue(event);
+ }
+}
+
+/*! Called by the state change thread to find out what the state is, and then
+ * to queue up the state change event */
+static void do_state_change(const char *device)
+{
+ enum ast_device_state state;
+
+ state = ast_device_state(device);
+
+ ast_debug(3, "Changing state for %s - state %d (%s)\n", device, state, devstate2str(state));
+
+ devstate_event(device, state, CACHE_OFF);
+}
+
+static int __ast_devstate_changed_literal(enum ast_device_state state, char *buf, int norecurse)
+{
+ char *device;
+ struct state_change *change;
+ char *tmp = NULL;
+
+ ast_debug(3, "Notification of state change to be queued on device/channel %s\n", buf);
+
+ device = buf;
+
+ if (state != AST_DEVICE_UNKNOWN) {
+ devstate_event(device, state, CACHE_ON);
+ } else if (change_thread == AST_PTHREADT_NULL || !(change = ast_calloc(1, sizeof(*change) + strlen(device)))) {
+ /* we could not allocate a change struct, or */
+ /* there is no background thread, so process the change now */
+ do_state_change(device);
+ } else {
+ /* queue the change */
+ strcpy(change->device, device);
+ AST_LIST_LOCK(&state_changes);
+ AST_LIST_INSERT_TAIL(&state_changes, change, list);
+ ast_cond_signal(&change_pending);
+ AST_LIST_UNLOCK(&state_changes);
+ }
+
+ /* The problem with this API is that a device may be called with the unique
+ * identifier appended or not, but it's separated from the channel name
+ * with a '-', which is also a legitimate character in a channel name. So,
+ * we have to force both names to get their names checked for state changes
+ * to ensure that the right one gets notified. Not a huge performance hit,
+ * but it might could be fixed by an enterprising programmer in trunk.
+ */
+ if (!norecurse && (tmp = strrchr(device, '-'))) {
+ *tmp = '\0';
+ __ast_devstate_changed_literal(state, device, 1);
+ }
+
+ return 1;
+}
+
+int ast_devstate_changed_literal(enum ast_device_state state, const char *dev)
+{
+ char *buf;
+
+ buf = ast_strdupa(dev);
+
+ return __ast_devstate_changed_literal(state, buf, 0);
+}
+
+int ast_device_state_changed_literal(const char *dev)
+{
+ char *buf;
+
+ buf = ast_strdupa(dev);
+
+ return __ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, buf, 0);
+}
+
+int ast_devstate_changed(enum ast_device_state state, const char *fmt, ...)
+{
+ char buf[AST_MAX_EXTENSION];
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ return __ast_devstate_changed_literal(state, buf, 0);
+}
+
+/*! \brief Accept change notification, add it to change queue */
+int ast_device_state_changed(const char *fmt, ...)
+{
+ char buf[AST_MAX_EXTENSION];
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ return __ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, buf, 0);
+}
+
+/*! \brief Go through the dev state change queue and update changes in the dev state thread */
+static void *do_devstate_changes(void *data)
+{
+ struct state_change *next, *current;
+
+ for (;;) {
+ /* This basically pops off any state change entries, resets the list back to NULL, unlocks, and processes each state change */
+ AST_LIST_LOCK(&state_changes);
+ if (AST_LIST_EMPTY(&state_changes))
+ ast_cond_wait(&change_pending, &state_changes.lock);
+ next = AST_LIST_FIRST(&state_changes);
+ AST_LIST_HEAD_INIT_NOLOCK(&state_changes);
+ AST_LIST_UNLOCK(&state_changes);
+
+ /* Process each state change */
+ while ((current = next)) {
+ next = AST_LIST_NEXT(current, list);
+ do_state_change(current->device);
+ ast_free(current);
+ }
+ }
+
+ return NULL;
+}
+
+/*! \brief Initialize the device state engine in separate thread */
+int ast_device_state_engine_init(void)
+{
+ ast_cond_init(&change_pending, NULL);
+ if (ast_pthread_create_background(&change_thread, NULL, do_devstate_changes, NULL) < 0) {
+ ast_log(LOG_ERROR, "Unable to start device state change thread.\n");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/trunk/main/dial.c b/trunk/main/dial.c
new file mode 100644
index 000000000..7b6ce871e
--- /dev/null
+++ b/trunk/main/dial.c
@@ -0,0 +1,1014 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2007, Digium, Inc.
+ *
+ * Joshua Colp <jcolp@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
+ *
+ * \brief Dialing API
+ *
+ * \author Joshua Colp <jcolp@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/time.h>
+#include <signal.h>
+
+#include "asterisk/channel.h"
+#include "asterisk/utils.h"
+#include "asterisk/lock.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/dial.h"
+#include "asterisk/pbx.h"
+#include "asterisk/musiconhold.h"
+
+/*! \brief Main dialing structure. Contains global options, channels being dialed, and more! */
+struct ast_dial {
+ int num; /*!< Current number to give to next dialed channel */
+ int timeout; /*!< Maximum time allowed for dial attempts */
+ int actual_timeout; /*!< Actual timeout based on all factors (ie: channels) */
+ enum ast_dial_result state; /*!< Status of dial */
+ void *options[AST_DIAL_OPTION_MAX]; /*!< Global options */
+ ast_dial_state_callback state_callback; /*!< Status callback */
+ AST_LIST_HEAD_NOLOCK(, ast_dial_channel) channels; /*!< Channels being dialed */
+ pthread_t thread; /*!< Thread (if running in async) */
+ ast_mutex_t lock; /*! Lock to protect the thread information above */
+};
+
+/*! \brief Dialing channel structure. Contains per-channel dialing options, asterisk channel, and more! */
+struct ast_dial_channel {
+ int num; /*!< Unique number for dialed channel */
+ int timeout; /*!< Maximum time allowed for attempt */
+ char *tech; /*!< Technology being dialed */
+ char *device; /*!< Device being dialed */
+ void *options[AST_DIAL_OPTION_MAX]; /*!< Channel specific options */
+ int cause; /*!< Cause code in case of failure */
+ int is_running_app:1; /*!< Is this running an application? */
+ struct ast_channel *owner; /*!< Asterisk channel */
+ AST_LIST_ENTRY(ast_dial_channel) list; /*!< Linked list information */
+};
+
+/*! \brief Typedef for dial option enable */
+typedef void *(*ast_dial_option_cb_enable)(void *data);
+
+/*! \brief Typedef for dial option disable */
+typedef int (*ast_dial_option_cb_disable)(void *data);
+
+/*! \brief Structure for 'ANSWER_EXEC' option */
+struct answer_exec_struct {
+ char app[AST_MAX_APP]; /*!< Application name */
+ char *args; /*!< Application arguments */
+};
+
+/*! \brief Enable function for 'ANSWER_EXEC' option */
+static void *answer_exec_enable(void *data)
+{
+ struct answer_exec_struct *answer_exec = NULL;
+ char *app = ast_strdupa((char*)data), *args = NULL;
+
+ /* Not giving any data to this option is bad, mmmk? */
+ if (ast_strlen_zero(app))
+ return NULL;
+
+ /* Create new data structure */
+ if (!(answer_exec = ast_calloc(1, sizeof(*answer_exec))))
+ return NULL;
+
+ /* Parse out application and arguments */
+ if ((args = strchr(app, '|'))) {
+ *args++ = '\0';
+ answer_exec->args = ast_strdup(args);
+ }
+
+ /* Copy application name */
+ ast_copy_string(answer_exec->app, app, sizeof(answer_exec->app));
+
+ return answer_exec;
+}
+
+/*! \brief Disable function for 'ANSWER_EXEC' option */
+static int answer_exec_disable(void *data)
+{
+ struct answer_exec_struct *answer_exec = data;
+
+ /* Make sure we have a value */
+ if (!answer_exec)
+ return -1;
+
+ /* If arguments are present, free them too */
+ if (answer_exec->args)
+ ast_free(answer_exec->args);
+
+ /* This is simple - just free the structure */
+ ast_free(answer_exec);
+
+ return 0;
+}
+
+static void *music_enable(void *data)
+{
+ return ast_strdup(data);
+}
+
+static int music_disable(void *data)
+{
+ if (!data)
+ return -1;
+
+ ast_free(data);
+
+ return 0;
+}
+
+/*! \brief Application execution function for 'ANSWER_EXEC' option */
+static void answer_exec_run(struct ast_dial *dial, struct ast_dial_channel *dial_channel, char *app, char *args)
+{
+ struct ast_channel *chan = dial_channel->owner;
+ struct ast_app *ast_app = pbx_findapp(app);
+
+ /* If the application was not found, return immediately */
+ if (!ast_app)
+ return;
+
+ /* All is well... execute the application */
+ pbx_exec(chan, ast_app, args);
+
+ /* If another thread is not taking over hang up the channel */
+ ast_mutex_lock(&dial->lock);
+ if (dial->thread != AST_PTHREADT_STOP) {
+ ast_hangup(chan);
+ dial_channel->owner = NULL;
+ }
+ ast_mutex_unlock(&dial->lock);
+
+ return;
+}
+
+/*! \brief Options structure - maps options to respective handlers (enable/disable). This list MUST be perfectly kept in order, or else madness will happen. */
+static const struct ast_option_types {
+ enum ast_dial_option option;
+ ast_dial_option_cb_enable enable;
+ ast_dial_option_cb_disable disable;
+} option_types[] = {
+ { AST_DIAL_OPTION_RINGING, NULL, NULL }, /*!< Always indicate ringing to caller */
+ { AST_DIAL_OPTION_ANSWER_EXEC, answer_exec_enable, answer_exec_disable }, /*!< Execute application upon answer in async mode */
+ { AST_DIAL_OPTION_MUSIC, music_enable, music_disable }, /*!< Play music to the caller instead of ringing */
+ { AST_DIAL_OPTION_DISABLE_CALL_FORWARDING, NULL, NULL }, /*!< Disable call forwarding on channels */
+ { AST_DIAL_OPTION_MAX, NULL, NULL }, /*!< Terminator of list */
+};
+
+/*! \brief free the buffer if allocated, and set the pointer to the second arg */
+#define S_REPLACE(s, new_val) \
+ do { \
+ if (s) \
+ free(s); \
+ s = (new_val); \
+ } while (0)
+
+/*! \brief Maximum number of channels we can watch at a time */
+#define AST_MAX_WATCHERS 256
+
+/*! \brief Macro for finding the option structure to use on a dialed channel */
+#define FIND_RELATIVE_OPTION(dial, dial_channel, ast_dial_option) (dial_channel->options[ast_dial_option] ? dial_channel->options[ast_dial_option] : dial->options[ast_dial_option])
+
+/*! \brief Macro that determines whether a channel is the caller or not */
+#define IS_CALLER(chan, owner) (chan == owner ? 1 : 0)
+
+/*! \brief New dialing structure
+ * \note Create a dialing structure
+ * \return Returns a calloc'd ast_dial structure, NULL on failure
+ */
+struct ast_dial *ast_dial_create(void)
+{
+ struct ast_dial *dial = NULL;
+
+ /* Allocate new memory for structure */
+ if (!(dial = ast_calloc(1, sizeof(*dial))))
+ return NULL;
+
+ /* Initialize list of channels */
+ AST_LIST_HEAD_INIT_NOLOCK(&dial->channels);
+
+ /* Initialize thread to NULL */
+ dial->thread = AST_PTHREADT_NULL;
+
+ /* No timeout exists... yet */
+ dial->timeout = -1;
+ dial->actual_timeout = -1;
+
+ /* Can't forget about the lock */
+ ast_mutex_init(&dial->lock);
+
+ return dial;
+}
+
+/*! \brief Append a channel
+ * \note Appends a channel to a dialing structure
+ * \return Returns channel reference number on success, -1 on failure
+ */
+int ast_dial_append(struct ast_dial *dial, const char *tech, const char *device)
+{
+ struct ast_dial_channel *channel = NULL;
+
+ /* Make sure we have required arguments */
+ if (!dial || !tech || !device)
+ return -1;
+
+ /* Allocate new memory for dialed channel structure */
+ if (!(channel = ast_calloc(1, sizeof(*channel))))
+ return -1;
+
+ /* Record technology and device for when we actually dial */
+ channel->tech = ast_strdup(tech);
+ channel->device = ast_strdup(device);
+
+ /* Grab reference number from dial structure */
+ channel->num = ast_atomic_fetchadd_int(&dial->num, +1);
+
+ /* No timeout exists... yet */
+ channel->timeout = -1;
+
+ /* Insert into channels list */
+ AST_LIST_INSERT_TAIL(&dial->channels, channel, list);
+
+ return channel->num;
+}
+
+/*! \brief Helper function that does the beginning dialing per-appended channel */
+static int begin_dial_channel(struct ast_dial_channel *channel, struct ast_channel *chan)
+{
+ char numsubst[AST_MAX_EXTENSION];
+ int res = 1;
+
+ /* Copy device string over */
+ ast_copy_string(numsubst, channel->device, sizeof(numsubst));
+
+ /* If we fail to create our owner channel bail out */
+ if (!(channel->owner = ast_request(channel->tech, chan ? chan->nativeformats : AST_FORMAT_AUDIO_MASK, numsubst, &channel->cause)))
+ return -1;
+
+ channel->owner->appl = "AppDial2";
+ channel->owner->data = "(Outgoing Line)";
+ channel->owner->whentohangup = 0;
+
+ /* Inherit everything from he who spawned this dial */
+ if (chan) {
+ ast_channel_inherit_variables(chan, channel->owner);
+
+ /* Copy over callerid information */
+ S_REPLACE(channel->owner->cid.cid_num, ast_strdup(chan->cid.cid_num));
+ S_REPLACE(channel->owner->cid.cid_name, ast_strdup(chan->cid.cid_name));
+ S_REPLACE(channel->owner->cid.cid_ani, ast_strdup(chan->cid.cid_ani));
+ S_REPLACE(channel->owner->cid.cid_rdnis, ast_strdup(chan->cid.cid_rdnis));
+
+ ast_string_field_set(channel->owner, language, chan->language);
+ ast_string_field_set(channel->owner, accountcode, chan->accountcode);
+ channel->owner->cdrflags = chan->cdrflags;
+ if (ast_strlen_zero(channel->owner->musicclass))
+ ast_string_field_set(channel->owner, musicclass, chan->musicclass);
+
+ channel->owner->cid.cid_pres = chan->cid.cid_pres;
+ channel->owner->cid.cid_ton = chan->cid.cid_ton;
+ channel->owner->cid.cid_tns = chan->cid.cid_tns;
+ channel->owner->adsicpe = chan->adsicpe;
+ channel->owner->transfercapability = chan->transfercapability;
+ }
+
+ /* Attempt to actually call this device */
+ if ((res = ast_call(channel->owner, numsubst, 0))) {
+ res = 0;
+ ast_hangup(channel->owner);
+ channel->owner = NULL;
+ } else {
+ if (chan)
+ ast_poll_channel_add(chan, channel->owner);
+ res = 1;
+ ast_verb(3, "Called %s\n", numsubst);
+ }
+
+ return res;
+}
+
+/*! \brief Helper function that does the beginning dialing per dial structure */
+static int begin_dial(struct ast_dial *dial, struct ast_channel *chan)
+{
+ struct ast_dial_channel *channel = NULL;
+ int success = 0;
+
+ /* Iterate through channel list, requesting and calling each one */
+ AST_LIST_TRAVERSE(&dial->channels, channel, list) {
+ success += begin_dial_channel(channel, chan);
+ }
+
+ /* If number of failures matches the number of channels, then this truly failed */
+ return success;
+}
+
+/*! \brief Helper function to handle channels that have been call forwarded */
+static int handle_call_forward(struct ast_dial *dial, struct ast_dial_channel *channel, struct ast_channel *chan)
+{
+ struct ast_channel *original = channel->owner;
+ char *tmp = ast_strdupa(channel->owner->call_forward);
+ char *tech = "Local", *device = tmp, *stuff;
+
+ /* If call forwarding is disabled just drop the original channel and don't attempt to dial the new one */
+ if (FIND_RELATIVE_OPTION(dial, channel, AST_DIAL_OPTION_DISABLE_CALL_FORWARDING)) {
+ ast_hangup(original);
+ channel->owner = NULL;
+ return 0;
+ }
+
+ /* Figure out the new destination */
+ if ((stuff = strchr(tmp, '/'))) {
+ *stuff++ = '\0';
+ tech = tmp;
+ device = stuff;
+ }
+
+ /* Drop old destination information */
+ ast_free(channel->tech);
+ ast_free(channel->device);
+
+ /* Update the dial channel with the new destination information */
+ channel->tech = ast_strdup(tech);
+ channel->device = ast_strdup(device);
+
+ /* Finally give it a go... send it out into the world */
+ begin_dial_channel(channel, chan);
+
+ /* Drop the original channel */
+ ast_hangup(original);
+
+ return 0;
+}
+
+/*! \brief Helper function that finds the dialed channel based on owner */
+static struct ast_dial_channel *find_relative_dial_channel(struct ast_dial *dial, struct ast_channel *owner)
+{
+ struct ast_dial_channel *channel = NULL;
+
+ AST_LIST_TRAVERSE(&dial->channels, channel, list) {
+ if (channel->owner == owner)
+ break;
+ }
+
+ return channel;
+}
+
+static void set_state(struct ast_dial *dial, enum ast_dial_result state)
+{
+ dial->state = state;
+
+ if (dial->state_callback)
+ dial->state_callback(dial);
+}
+
+/*! \brief Helper function that handles control frames WITH owner */
+static void handle_frame(struct ast_dial *dial, struct ast_dial_channel *channel, struct ast_frame *fr, struct ast_channel *chan)
+{
+ if (fr->frametype == AST_FRAME_CONTROL) {
+ switch (fr->subclass) {
+ case AST_CONTROL_ANSWER:
+ ast_verb(3, "%s answered %s\n", channel->owner->name, chan->name);
+ AST_LIST_REMOVE(&dial->channels, channel, list);
+ AST_LIST_INSERT_HEAD(&dial->channels, channel, list);
+ set_state(dial, AST_DIAL_RESULT_ANSWERED);
+ break;
+ case AST_CONTROL_BUSY:
+ ast_verb(3, "%s is busy\n", channel->owner->name);
+ ast_hangup(channel->owner);
+ channel->owner = NULL;
+ break;
+ case AST_CONTROL_CONGESTION:
+ ast_verb(3, "%s is circuit-busy\n", channel->owner->name);
+ ast_hangup(channel->owner);
+ channel->owner = NULL;
+ break;
+ case AST_CONTROL_RINGING:
+ ast_verb(3, "%s is ringing\n", channel->owner->name);
+ if (!dial->options[AST_DIAL_OPTION_MUSIC])
+ ast_indicate(chan, AST_CONTROL_RINGING);
+ set_state(dial, AST_DIAL_RESULT_RINGING);
+ break;
+ case AST_CONTROL_PROGRESS:
+ ast_verb(3, "%s is making progress, passing it to %s\n", channel->owner->name, chan->name);
+ ast_indicate(chan, AST_CONTROL_PROGRESS);
+ set_state(dial, AST_DIAL_RESULT_PROGRESS);
+ break;
+ case AST_CONTROL_VIDUPDATE:
+ ast_verb(3, "%s requested a video update, passing it to %s\n", channel->owner->name, chan->name);
+ ast_indicate(chan, AST_CONTROL_VIDUPDATE);
+ break;
+ case AST_CONTROL_PROCEEDING:
+ ast_verb(3, "%s is proceeding, passing it to %s\n", channel->owner->name, chan->name);
+ ast_indicate(chan, AST_CONTROL_PROCEEDING);
+ set_state(dial, AST_DIAL_RESULT_PROCEEDING);
+ break;
+ case AST_CONTROL_HOLD:
+ ast_verb(3, "Call on %s placed on hold\n", chan->name);
+ ast_indicate(chan, AST_CONTROL_HOLD);
+ break;
+ case AST_CONTROL_UNHOLD:
+ ast_verb(3, "Call on %s left from hold\n", chan->name);
+ ast_indicate(chan, AST_CONTROL_UNHOLD);
+ break;
+ case AST_CONTROL_OFFHOOK:
+ case AST_CONTROL_FLASH:
+ break;
+ case -1:
+ /* Prod the channel */
+ ast_indicate(chan, -1);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return;
+}
+
+/*! \brief Helper function that handles control frames WITHOUT owner */
+static void handle_frame_ownerless(struct ast_dial *dial, struct ast_dial_channel *channel, struct ast_frame *fr)
+{
+ /* If we have no owner we can only update the state of the dial structure, so only look at control frames */
+ if (fr->frametype != AST_FRAME_CONTROL)
+ return;
+
+ switch (fr->subclass) {
+ case AST_CONTROL_ANSWER:
+ ast_verb(3, "%s answered\n", channel->owner->name);
+ AST_LIST_REMOVE(&dial->channels, channel, list);
+ AST_LIST_INSERT_HEAD(&dial->channels, channel, list);
+ set_state(dial, AST_DIAL_RESULT_ANSWERED);
+ break;
+ case AST_CONTROL_BUSY:
+ ast_verb(3, "%s is busy\n", channel->owner->name);
+ ast_hangup(channel->owner);
+ channel->owner = NULL;
+ break;
+ case AST_CONTROL_CONGESTION:
+ ast_verb(3, "%s is circuit-busy\n", channel->owner->name);
+ ast_hangup(channel->owner);
+ channel->owner = NULL;
+ break;
+ case AST_CONTROL_RINGING:
+ ast_verb(3, "%s is ringing\n", channel->owner->name);
+ set_state(dial, AST_DIAL_RESULT_RINGING);
+ break;
+ case AST_CONTROL_PROGRESS:
+ ast_verb(3, "%s is making progress\n", channel->owner->name);
+ set_state(dial, AST_DIAL_RESULT_PROGRESS);
+ break;
+ case AST_CONTROL_PROCEEDING:
+ ast_verb(3, "%s is proceeding\n", channel->owner->name);
+ set_state(dial, AST_DIAL_RESULT_PROCEEDING);
+ break;
+ default:
+ break;
+ }
+
+ return;
+}
+
+/*! \brief Helper function to handle when a timeout occurs on dialing attempt */
+static int handle_timeout_trip(struct ast_dial *dial, struct timeval start)
+{
+ struct ast_dial_channel *channel = NULL;
+ int diff = ast_tvdiff_ms(ast_tvnow(), start), lowest_timeout = -1, new_timeout = -1;
+
+ /* If the global dial timeout tripped switch the state to timeout so our channel loop will drop every channel */
+ if (diff >= dial->timeout) {
+ set_state(dial, AST_DIAL_RESULT_TIMEOUT);
+ new_timeout = 0;
+ }
+
+ /* Go through dropping out channels that have met their timeout */
+ AST_LIST_TRAVERSE(&dial->channels, channel, list) {
+ if (dial->state == AST_DIAL_RESULT_TIMEOUT || diff >= channel->timeout) {
+ ast_hangup(channel->owner);
+ channel->owner = NULL;
+ } else if ((lowest_timeout == -1) || (lowest_timeout > channel->timeout)) {
+ lowest_timeout = channel->timeout;
+ }
+ }
+
+ /* Calculate the new timeout using the lowest timeout found */
+ if (lowest_timeout >= 0)
+ new_timeout = lowest_timeout - diff;
+
+ return new_timeout;
+}
+
+/*! \brief Helper function that basically keeps tabs on dialing attempts */
+static enum ast_dial_result monitor_dial(struct ast_dial *dial, struct ast_channel *chan)
+{
+ int timeout = -1;
+ struct ast_channel *cs[AST_MAX_WATCHERS], *who = NULL;
+ struct ast_dial_channel *channel = NULL;
+ struct answer_exec_struct *answer_exec = NULL;
+ struct timeval start;
+
+ set_state(dial, AST_DIAL_RESULT_TRYING);
+
+ /* If the "always indicate ringing" option is set, change state to ringing and indicate to the owner if present */
+ if (dial->options[AST_DIAL_OPTION_RINGING]) {
+ set_state(dial, AST_DIAL_RESULT_RINGING);
+ if (chan)
+ ast_indicate(chan, AST_CONTROL_RINGING);
+ } else if (chan && dial->options[AST_DIAL_OPTION_MUSIC] &&
+ !ast_strlen_zero(dial->options[AST_DIAL_OPTION_MUSIC])) {
+ char *original_moh = ast_strdupa(chan->musicclass);
+ ast_indicate(chan, -1);
+ ast_string_field_set(chan, musicclass, dial->options[AST_DIAL_OPTION_MUSIC]);
+ ast_moh_start(chan, dial->options[AST_DIAL_OPTION_MUSIC], NULL);
+ ast_string_field_set(chan, musicclass, original_moh);
+ }
+
+ /* Record start time for timeout purposes */
+ start = ast_tvnow();
+
+ /* We actually figured out the maximum timeout we can do as they were added, so we can directly access the info */
+ timeout = dial->actual_timeout;
+
+ /* Go into an infinite loop while we are trying */
+ while ((dial->state != AST_DIAL_RESULT_UNANSWERED) && (dial->state != AST_DIAL_RESULT_ANSWERED) && (dial->state != AST_DIAL_RESULT_HANGUP) && (dial->state != AST_DIAL_RESULT_TIMEOUT)) {
+ int pos = 0, count = 0;
+ struct ast_frame *fr = NULL;
+
+ /* Set up channel structure array */
+ pos = count = 0;
+ if (chan)
+ cs[pos++] = chan;
+
+ /* Add channels we are attempting to dial */
+ AST_LIST_TRAVERSE(&dial->channels, channel, list) {
+ if (channel->owner) {
+ cs[pos++] = channel->owner;
+ count++;
+ }
+ }
+
+ /* If we have no outbound channels in progress, switch state to unanswered and stop */
+ if (!count) {
+ set_state(dial, AST_DIAL_RESULT_UNANSWERED);
+ break;
+ }
+
+ /* Just to be safe... */
+ if (dial->thread == AST_PTHREADT_STOP)
+ break;
+
+ /* Wait for frames from channels */
+ who = ast_waitfor_n(cs, pos, &timeout);
+
+ /* Check to see if our thread is being cancelled */
+ if (dial->thread == AST_PTHREADT_STOP)
+ break;
+
+ /* If the timeout no longer exists OR if we got no channel it basically means the timeout was tripped, so handle it */
+ if (!timeout || !who) {
+ timeout = handle_timeout_trip(dial, start);
+ continue;
+ }
+
+ /* Find relative dial channel */
+ if (!chan || !IS_CALLER(chan, who))
+ channel = find_relative_dial_channel(dial, who);
+
+ /* See if this channel has been forwarded elsewhere */
+ if (!ast_strlen_zero(who->call_forward)) {
+ handle_call_forward(dial, channel, chan);
+ continue;
+ }
+
+ /* Attempt to read in a frame */
+ if (!(fr = ast_read(who))) {
+ /* If this is the caller then we switch state to hangup and stop */
+ if (chan && IS_CALLER(chan, who)) {
+ set_state(dial, AST_DIAL_RESULT_HANGUP);
+ break;
+ }
+ if (chan)
+ ast_poll_channel_del(chan, channel->owner);
+ ast_hangup(who);
+ channel->owner = NULL;
+ continue;
+ }
+
+ /* Process the frame */
+ if (chan)
+ handle_frame(dial, channel, fr, chan);
+ else
+ handle_frame_ownerless(dial, channel, fr);
+
+ /* Free the received frame and start all over */
+ ast_frfree(fr);
+ }
+
+ /* Do post-processing from loop */
+ if (dial->state == AST_DIAL_RESULT_ANSWERED) {
+ /* Hangup everything except that which answered */
+ AST_LIST_TRAVERSE(&dial->channels, channel, list) {
+ if (!channel->owner || channel->owner == who)
+ continue;
+ if (chan)
+ ast_poll_channel_del(chan, channel->owner);
+ ast_hangup(channel->owner);
+ channel->owner = NULL;
+ }
+ /* If ANSWER_EXEC is enabled as an option, execute application on answered channel */
+ if ((channel = find_relative_dial_channel(dial, who)) && (answer_exec = FIND_RELATIVE_OPTION(dial, channel, AST_DIAL_OPTION_ANSWER_EXEC))) {
+ channel->is_running_app = 1;
+ answer_exec_run(dial, channel, answer_exec->app, answer_exec->args);
+ channel->is_running_app = 0;
+ }
+
+ if (chan && dial->options[AST_DIAL_OPTION_MUSIC] &&
+ !ast_strlen_zero(dial->options[AST_DIAL_OPTION_MUSIC])) {
+ ast_moh_stop(chan);
+ }
+ } else if (dial->state == AST_DIAL_RESULT_HANGUP) {
+ /* Hangup everything */
+ AST_LIST_TRAVERSE(&dial->channels, channel, list) {
+ if (!channel->owner)
+ continue;
+ if (chan)
+ ast_poll_channel_del(chan, channel->owner);
+ ast_hangup(channel->owner);
+ channel->owner = NULL;
+ }
+ }
+
+ return dial->state;
+}
+
+/*! \brief Dial async thread function */
+static void *async_dial(void *data)
+{
+ struct ast_dial *dial = data;
+
+ /* This is really really simple... we basically pass monitor_dial a NULL owner and it changes it's behavior */
+ monitor_dial(dial, NULL);
+
+ return NULL;
+}
+
+/*! \brief Execute dialing synchronously or asynchronously
+ * \note Dials channels in a dial structure.
+ * \return Returns dial result code. (TRYING/INVALID/FAILED/ANSWERED/TIMEOUT/UNANSWERED).
+ */
+enum ast_dial_result ast_dial_run(struct ast_dial *dial, struct ast_channel *chan, int async)
+{
+ enum ast_dial_result res = AST_DIAL_RESULT_TRYING;
+
+ /* Ensure required arguments are passed */
+ if (!dial || (!chan && !async)) {
+ ast_debug(1, "invalid #1\n");
+ return AST_DIAL_RESULT_INVALID;
+ }
+
+ /* If there are no channels to dial we can't very well try to dial them */
+ if (AST_LIST_EMPTY(&dial->channels)) {
+ ast_debug(1, "invalid #2\n");
+ return AST_DIAL_RESULT_INVALID;
+ }
+
+ /* Dial each requested channel */
+ if (!begin_dial(dial, chan))
+ return AST_DIAL_RESULT_FAILED;
+
+ /* If we are running async spawn a thread and send it away... otherwise block here */
+ if (async) {
+ dial->state = AST_DIAL_RESULT_TRYING;
+ /* Try to create a thread */
+ if (ast_pthread_create(&dial->thread, NULL, async_dial, dial)) {
+ /* Failed to create the thread - hangup all dialed channels and return failed */
+ ast_dial_hangup(dial);
+ res = AST_DIAL_RESULT_FAILED;
+ }
+ } else {
+ res = monitor_dial(dial, chan);
+ }
+
+ return res;
+}
+
+/*! \brief Return channel that answered
+ * \note Returns the Asterisk channel that answered
+ * \param dial Dialing structure
+ */
+struct ast_channel *ast_dial_answered(struct ast_dial *dial)
+{
+ if (!dial)
+ return NULL;
+
+ return ((dial->state == AST_DIAL_RESULT_ANSWERED) ? AST_LIST_FIRST(&dial->channels)->owner : NULL);
+}
+
+/*! \brief Return state of dial
+ * \note Returns the state of the dial attempt
+ * \param dial Dialing structure
+ */
+enum ast_dial_result ast_dial_state(struct ast_dial *dial)
+{
+ return dial->state;
+}
+
+/*! \brief Cancel async thread
+ * \note Cancel a running async thread
+ * \param dial Dialing structure
+ */
+enum ast_dial_result ast_dial_join(struct ast_dial *dial)
+{
+ pthread_t thread;
+
+ /* If the dial structure is not running in async, return failed */
+ if (dial->thread == AST_PTHREADT_NULL)
+ return AST_DIAL_RESULT_FAILED;
+
+ /* Record thread */
+ thread = dial->thread;
+
+ /* Boom, commence locking */
+ ast_mutex_lock(&dial->lock);
+
+ /* Stop the thread */
+ dial->thread = AST_PTHREADT_STOP;
+
+ /* If the answered channel is running an application we have to soft hangup it, can't just poke the thread */
+ if (AST_LIST_FIRST(&dial->channels)->is_running_app) {
+ struct ast_channel *chan = AST_LIST_FIRST(&dial->channels)->owner;
+ ast_channel_lock(chan);
+ ast_softhangup(chan, AST_SOFTHANGUP_EXPLICIT);
+ ast_channel_unlock(chan);
+ } else {
+ /* Now we signal it with SIGURG so it will break out of it's waitfor */
+ pthread_kill(thread, SIGURG);
+ }
+
+ /* Yay done with it */
+ ast_mutex_unlock(&dial->lock);
+
+ /* Finally wait for the thread to exit */
+ pthread_join(thread, NULL);
+
+ /* Yay thread is all gone */
+ dial->thread = AST_PTHREADT_NULL;
+
+ return dial->state;
+}
+
+/*! \brief Hangup channels
+ * \note Hangup all active channels
+ * \param dial Dialing structure
+ */
+void ast_dial_hangup(struct ast_dial *dial)
+{
+ struct ast_dial_channel *channel = NULL;
+
+ if (!dial)
+ return;
+
+ AST_LIST_TRAVERSE(&dial->channels, channel, list) {
+ if (channel->owner) {
+ ast_hangup(channel->owner);
+ channel->owner = NULL;
+ }
+ }
+
+ return;
+}
+
+/*! \brief Destroys a dialing structure
+ * \note Destroys (free's) the given ast_dial structure
+ * \param dial Dialing structure to free
+ * \return Returns 0 on success, -1 on failure
+ */
+int ast_dial_destroy(struct ast_dial *dial)
+{
+ int i = 0;
+ struct ast_dial_channel *channel = NULL;
+
+ if (!dial)
+ return -1;
+
+ /* Hangup and deallocate all the dialed channels */
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&dial->channels, channel, list) {
+ /* Disable any enabled options */
+ for (i = 0; i < AST_DIAL_OPTION_MAX; i++) {
+ if (!channel->options[i])
+ continue;
+ if (option_types[i].disable)
+ option_types[i].disable(channel->options[i]);
+ channel->options[i] = NULL;
+ }
+ /* Hang up channel if need be */
+ if (channel->owner) {
+ ast_hangup(channel->owner);
+ channel->owner = NULL;
+ }
+ /* Free structure */
+ ast_free(channel->tech);
+ ast_free(channel->device);
+ AST_LIST_REMOVE_CURRENT(list);
+ ast_free(channel);
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+
+ /* Disable any enabled options globally */
+ for (i = 0; i < AST_DIAL_OPTION_MAX; i++) {
+ if (!dial->options[i])
+ continue;
+ if (option_types[i].disable)
+ option_types[i].disable(dial->options[i]);
+ dial->options[i] = NULL;
+ }
+
+ /* Lock be gone! */
+ ast_mutex_destroy(&dial->lock);
+
+ /* Free structure */
+ ast_free(dial);
+
+ return 0;
+}
+
+/*! \brief Enables an option globally
+ * \param dial Dial structure to enable option on
+ * \param option Option to enable
+ * \param data Data to pass to this option (not always needed)
+ * \return Returns 0 on success, -1 on failure
+ */
+int ast_dial_option_global_enable(struct ast_dial *dial, enum ast_dial_option option, void *data)
+{
+ /* If the option is already enabled, return failure */
+ if (dial->options[option])
+ return -1;
+
+ /* Execute enable callback if it exists, if not simply make sure the value is set */
+ if (option_types[option].enable)
+ dial->options[option] = option_types[option].enable(data);
+ else
+ dial->options[option] = (void*)1;
+
+ return 0;
+}
+
+/*! \brief Helper function for finding a channel in a dial structure based on number
+ */
+static struct ast_dial_channel *find_dial_channel(struct ast_dial *dial, int num)
+{
+ struct ast_dial_channel *channel = AST_LIST_LAST(&dial->channels);
+
+ /* We can try to predict programmer behavior, the last channel they added is probably the one they wanted to modify */
+ if (channel->num == num)
+ return channel;
+
+ /* Hrm not at the end... looking through the list it is! */
+ AST_LIST_TRAVERSE(&dial->channels, channel, list) {
+ if (channel->num == num)
+ break;
+ }
+
+ return channel;
+}
+
+/*! \brief Enables an option per channel
+ * \param dial Dial structure
+ * \param num Channel number to enable option on
+ * \param option Option to enable
+ * \param data Data to pass to this option (not always needed)
+ * \return Returns 0 on success, -1 on failure
+ */
+int ast_dial_option_enable(struct ast_dial *dial, int num, enum ast_dial_option option, void *data)
+{
+ struct ast_dial_channel *channel = NULL;
+
+ /* Ensure we have required arguments */
+ if (!dial || AST_LIST_EMPTY(&dial->channels))
+ return -1;
+
+ if (!(channel = find_dial_channel(dial, num)))
+ return -1;
+
+ /* If the option is already enabled, return failure */
+ if (channel->options[option])
+ return -1;
+
+ /* Execute enable callback if it exists, if not simply make sure the value is set */
+ if (option_types[option].enable)
+ channel->options[option] = option_types[option].enable(data);
+ else
+ channel->options[option] = (void*)1;
+
+ return 0;
+}
+
+/*! \brief Disables an option globally
+ * \param dial Dial structure to disable option on
+ * \param option Option to disable
+ * \return Returns 0 on success, -1 on failure
+ */
+int ast_dial_option_global_disable(struct ast_dial *dial, enum ast_dial_option option)
+{
+ /* If the option is not enabled, return failure */
+ if (!dial->options[option])
+ return -1;
+
+ /* Execute callback of option to disable if it exists */
+ if (option_types[option].disable)
+ option_types[option].disable(dial->options[option]);
+
+ /* Finally disable option on the structure */
+ dial->options[option] = NULL;
+
+ return 0;
+}
+
+/*! \brief Disables an option per channel
+ * \param dial Dial structure
+ * \param num Channel number to disable option on
+ * \param option Option to disable
+ * \return Returns 0 on success, -1 on failure
+ */
+int ast_dial_option_disable(struct ast_dial *dial, int num, enum ast_dial_option option)
+{
+ struct ast_dial_channel *channel = NULL;
+
+ /* Ensure we have required arguments */
+ if (!dial || AST_LIST_EMPTY(&dial->channels))
+ return -1;
+
+ if (!(channel = find_dial_channel(dial, num)))
+ return -1;
+
+ /* If the option is not enabled, return failure */
+ if (!channel->options[option])
+ return -1;
+
+ /* Execute callback of option to disable it if it exists */
+ if (option_types[option].disable)
+ option_types[option].disable(channel->options[option]);
+
+ /* Finally disable the option on the structure */
+ channel->options[option] = NULL;
+
+ return 0;
+}
+
+void ast_dial_set_state_callback(struct ast_dial *dial, ast_dial_state_callback callback)
+{
+ dial->state_callback = callback;
+}
+
+/*! \brief Set the maximum time (globally) allowed for trying to ring phones
+ * \param dial The dial structure to apply the time limit to
+ * \param timeout Maximum time allowed
+ * \return nothing
+ */
+void ast_dial_set_global_timeout(struct ast_dial *dial, int timeout)
+{
+ dial->timeout = timeout;
+
+ if (dial->timeout > 0 && dial->actual_timeout > dial->timeout)
+ dial->actual_timeout = dial->timeout;
+
+ return;
+}
+
+/*! \brief Set the maximum time (per channel) allowed for trying to ring the phone
+ * \param dial The dial structure the channel belongs to
+ * \param num Channel number to set timeout on
+ * \param timeout Maximum time allowed
+ * \return nothing
+ */
+void ast_dial_set_timeout(struct ast_dial *dial, int num, int timeout)
+{
+ struct ast_dial_channel *channel = NULL;
+
+ if (!(channel = find_dial_channel(dial, num)))
+ return;
+
+ channel->timeout = timeout;
+
+ if (channel->timeout > 0 && dial->actual_timeout > channel->timeout)
+ dial->actual_timeout = channel->timeout;
+
+ return;
+}
diff --git a/trunk/main/dlfcn.c b/trunk/main/dlfcn.c
new file mode 100644
index 000000000..802e7942f
--- /dev/null
+++ b/trunk/main/dlfcn.c
@@ -0,0 +1,1309 @@
+/*
+Copyright (c) 2002 Jorge Acereda <jacereda@users.sourceforge.net> &
+ Peter O'Gorman <ogorman@users.sourceforge.net>
+
+Portions may be copyright others, see the AUTHORS file included with this
+distribution.
+
+Maintained by Peter O'Gorman <ogorman@users.sourceforge.net>
+
+Bug Reports and other queries should go to <ogorman@users.sourceforge.net>
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <pthread.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <mach-o/dyld.h>
+#include <mach-o/nlist.h>
+#include <mach-o/getsect.h>
+/* Just playing to see if it would compile with the freebsd headers, it does,
+ * but because of the different values for RTLD_LOCAL etc, it would break binary
+ * compat... oh well
+ */
+#ifndef __BSD_VISIBLE
+#define __BSD_VISIBLE 1
+#endif
+
+#include "asterisk/dlfcn-compat.h"
+
+#ifndef dl_restrict
+#define dl_restrict __restrict
+#endif
+/* This is not available on 10.1 */
+#ifndef LC_LOAD_WEAK_DYLIB
+#define LC_LOAD_WEAK_DYLIB (0x18 | LC_REQ_DYLD)
+#endif
+
+/* With this stuff here, this thing may actually compile/run on 10.0 systems
+ * Not that I have a 10.0 system to test it on anylonger
+ */
+#ifndef LC_REQ_DYLD
+#define LC_REQ_DYLD 0x80000000
+#endif
+#ifndef NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED
+#define NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED 0x4
+#endif
+#ifndef NSADDIMAGE_OPTION_RETURN_ON_ERROR
+#define NSADDIMAGE_OPTION_RETURN_ON_ERROR 0x1
+#endif
+#ifndef NSLOOKUPSYMBOLINIMAGE_OPTION_BIND
+#define NSLOOKUPSYMBOLINIMAGE_OPTION_BIND 0x0
+#endif
+#ifndef NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR
+#define NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR 0x4
+#endif
+/* These symbols will be looked for in dyld */
+static const struct mach_header *(*dyld_NSAddImage) (const char *, unsigned long) = 0;
+static int (*dyld_NSIsSymbolNameDefinedInImage) (const struct mach_header *, const char *) = 0;
+static NSSymbol(*dyld_NSLookupSymbolInImage)
+ (const struct mach_header *, const char *, unsigned long) = 0;
+
+/* Define this to make dlcompat reuse data block. This way in theory we save
+ * a little bit of overhead. However we then couldn't correctly catch excess
+ * calls to dlclose(). Hence we don't use this feature
+ */
+#undef REUSE_STATUS
+
+/* Size of the internal error message buffer (used by dlerror()) */
+#define ERR_STR_LEN 251
+
+/* Maximum number of search paths supported by getSearchPath */
+#define MAX_SEARCH_PATHS 32
+
+
+#define MAGIC_DYLIB_OFI ((NSObjectFileImage) 'DYOF')
+#define MAGIC_DYLIB_MOD ((NSModule) 'DYMO')
+
+/* internal flags */
+#define DL_IN_LIST 0x01
+
+/* our mutex */
+static pthread_mutex_t dlcompat_mutex;
+/* Our thread specific storage
+ */
+static pthread_key_t dlerror_key;
+
+struct dlthread
+{
+ int lockcnt;
+ unsigned char errset;
+ char errstr[ERR_STR_LEN];
+};
+
+/* This is our central data structure. Whenever a module is loaded via
+ * dlopen(), we create such a struct.
+ */
+struct dlstatus
+{
+ struct dlstatus *next; /* pointer to next element in the linked list */
+ NSModule module;
+ const struct mach_header *lib;
+ int refs; /* reference count */
+ int mode; /* mode in which this module was loaded */
+ dev_t device;
+ ino_t inode;
+ int flags; /* Any internal flags we may need */
+};
+
+/* Head node of the dlstatus list */
+static struct dlstatus mainStatus = { 0, MAGIC_DYLIB_MOD, NULL, -1, RTLD_GLOBAL, 0, 0, 0 };
+static struct dlstatus *stqueue = &mainStatus;
+
+
+/* Storage for the last error message (used by dlerror()) */
+/* static char err_str[ERR_STR_LEN]; */
+/* static int err_filled = 0; */
+
+/* Prototypes to internal functions */
+static void debug(const char *fmt, ...);
+static void error(const char *str, ...);
+static const char *safegetenv(const char *s);
+static const char *searchList(void);
+static const char *getSearchPath(int i);
+static const char *getFullPath(int i, const char *file);
+static const struct stat *findFile(const char *file, const char **fullPath);
+static int isValidStatus(struct dlstatus *status);
+static inline int isFlagSet(int mode, int flag);
+static struct dlstatus *lookupStatus(const struct stat *sbuf);
+static void insertStatus(struct dlstatus *dls, const struct stat *sbuf);
+static int promoteLocalToGlobal(struct dlstatus *dls);
+static void *reference(struct dlstatus *dls, int mode);
+static void *dlsymIntern(struct dlstatus *dls, const char *symbol, int canSetError);
+static struct dlstatus *allocStatus(void);
+static struct dlstatus *loadModule(const char *path, const struct stat *sbuf, int mode);
+static NSSymbol *search_linked_libs(const struct mach_header *mh, const char *symbol);
+static const char *get_lib_name(const struct mach_header *mh);
+static const struct mach_header *get_mach_header_from_NSModule(NSModule * mod);
+static void dlcompat_init_func(void);
+static inline void dolock(void);
+static inline void dounlock(void);
+static void dlerrorfree(void *data);
+static void resetdlerror(void);
+static const struct mach_header *my_find_image(const char *name);
+static const struct mach_header *image_for_address(const void *address);
+static void dlcompat_cleanup(void);
+static inline const char *dyld_error_str(void);
+
+#if FINK_BUILD
+/* Two Global Functions */
+void *dlsym_prepend_underscore(void *handle, const char *symbol);
+void *dlsym_auto_underscore(void *handle, const char *symbol);
+
+/* And their _intern counterparts */
+static void *dlsym_prepend_underscore_intern(void *handle, const char *symbol);
+static void *dlsym_auto_underscore_intern(void *handle, const char *symbol);
+#endif
+
+/* Functions */
+
+static void debug(const char *fmt, ...)
+{
+#if DEBUG > 1
+ va_list arg;
+ va_start(arg, fmt);
+ fprintf(stderr, "DLDEBUG: ");
+ vfprintf(stderr, fmt, arg);
+ fprintf(stderr, "\n");
+ fflush(stderr);
+ va_end(arg);
+#endif
+}
+
+static void error(const char *str, ...)
+{
+ va_list arg;
+ struct dlthread *tss;
+ char * err_str;
+ va_start(arg, str);
+ tss = pthread_getspecific(dlerror_key);
+ err_str = tss->errstr;
+ strncpy(err_str, "dlcompat: ", ERR_STR_LEN);
+ vsnprintf(err_str + 10, ERR_STR_LEN - 10, str, arg);
+ va_end(arg);
+ debug("ERROR: %s\n", err_str);
+ tss->errset = 1;
+}
+
+static void warning(const char *str)
+{
+#if DEBUG > 0
+ fprintf(stderr, "WARNING: dlcompat: %s\n", str);
+#endif
+}
+
+static const char *safegetenv(const char *s)
+{
+ const char *ss = getenv(s);
+ return ss ? ss : "";
+}
+
+/* because this is only used for debugging and error reporting functions, we
+ * don't really care about how elegant it is... it could use the load
+ * commands to find the install name of the library, but...
+ */
+static const char *get_lib_name(const struct mach_header *mh)
+{
+ unsigned long count = _dyld_image_count();
+ unsigned long i;
+ const char *val = NULL;
+ if (mh)
+ {
+ for (i = 0; i < count; i++)
+ {
+ if (mh == _dyld_get_image_header(i))
+ {
+ val = _dyld_get_image_name(i);
+ break;
+ }
+ }
+ }
+ return val;
+}
+
+/* Returns the mach_header for the module bu going through all the loaded images
+ * and finding the one with the same name as the module. There really ought to be
+ * an api for doing this, would be faster, but there isn't one right now
+ */
+static const struct mach_header *get_mach_header_from_NSModule(NSModule * mod)
+{
+ const char *mod_name = NSNameOfModule(mod);
+ struct mach_header *mh = NULL;
+ unsigned long count = _dyld_image_count();
+ unsigned long i;
+ debug("Module name: %s", mod_name);
+ for (i = 0; i < count; i++)
+ {
+ if (!strcmp(mod_name, _dyld_get_image_name(i)))
+ {
+ mh = _dyld_get_image_header(i);
+ break;
+ }
+ }
+ return mh;
+}
+
+
+/* Compute and return a list of all directories that we should search when
+ * trying to locate a module. We first look at the values of LD_LIBRARY_PATH
+ * and DYLD_LIBRARY_PATH, and then finally fall back to looking into
+ * /usr/lib and /lib. Since both of the environments variables can contain a
+ * list of colon separated paths, we simply concat them and the two other paths
+ * into one big string, which we then can easily parse.
+ * Splitting this string into the actual path list is done by getSearchPath()
+ */
+static const char *searchList()
+{
+ size_t buf_size;
+ static char *buf=NULL;
+ const char *ldlp = safegetenv("LD_LIBRARY_PATH");
+ const char *dyldlp = safegetenv("DYLD_LIBRARY_PATH");
+ const char *stdpath = getenv("DYLD_FALLBACK_LIBRARY_PATH");
+ if (!stdpath)
+ stdpath = "/usr/local/lib:/lib:/usr/lib";
+ if (!buf)
+ {
+ buf_size = strlen(ldlp) + strlen(dyldlp) + strlen(stdpath) + 4;
+ buf = ast_malloc(buf_size);
+ snprintf(buf, buf_size, "%s%s%s%s%s%c", dyldlp, (dyldlp[0] ? ":" : ""), ldlp, (ldlp[0] ? ":" : ""),
+ stdpath, '\0');
+ }
+ return buf;
+}
+
+/* Returns the ith search path from the list as computed by searchList() */
+static const char *getSearchPath(int i)
+{
+ static const char *list = 0;
+ static char **path = (char **)0;
+ static int end = 0;
+ static int numsize = MAX_SEARCH_PATHS;
+ static char **tmp;
+ /* So we can call free() in the "destructor" we use i=-1 to return the alloc'd array */
+ if (i == -1)
+ {
+ return (const char*)path;
+ }
+ if (!path)
+ {
+ path = ast_calloc(MAX_SEARCH_PATHS, sizeof(char **));
+ }
+ if (!list && !end)
+ list = searchList();
+ if (i >= (numsize))
+ {
+ debug("Increasing size for long PATH");
+ tmp = ast_calloc((MAX_SEARCH_PATHS + numsize), sizeof(char **));
+ if (tmp)
+ {
+ memcpy(tmp, path, sizeof(char **) * numsize);
+ ast_free(path);
+ path = tmp;
+ numsize += MAX_SEARCH_PATHS;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ while (!path[i] && !end)
+ {
+ path[i] = strsep((char **)&list, ":");
+
+ if (path[i][0] == 0)
+ path[i] = 0;
+ end = (list == 0);
+ }
+ return path[i];
+}
+
+static const char *getFullPath(int i, const char *file)
+{
+ static char buf[PATH_MAX];
+ const char *path = getSearchPath(i);
+ if (path)
+ {
+ snprintf(buf, PATH_MAX, "%s/%s", path, file);
+ }
+ return path ? buf : 0;
+}
+
+/* Given a file name, try to determine the full path for that file. Starts
+ * its search in the current directory, and then tries all paths in the
+ * search list in the order they are specified there.
+ */
+static const struct stat *findFile(const char *file, const char **fullPath)
+{
+ int i = 0;
+ static struct stat sbuf;
+ char *fileName;
+ debug("finding file %s", file);
+ *fullPath = file;
+ if (0 == stat(file, &sbuf))
+ return &sbuf;
+ if (strchr(file, '/'))
+ return 0; /* If the path had a / we don't look in env var places */
+ fileName = NULL;
+ if (!fileName)
+ fileName = (char *)file;
+ while ((*fullPath = getFullPath(i++, fileName)))
+ {
+ if (0 == stat(*fullPath, &sbuf))
+ return &sbuf;
+ }
+ ;
+ return 0;
+}
+
+/* Determine whether a given dlstatus is valid or not */
+static int isValidStatus(struct dlstatus *status)
+{
+ /* Walk the list to verify status is contained in it */
+ struct dlstatus *dls = stqueue;
+ while (dls && status != dls)
+ dls = dls->next;
+ if (dls == 0)
+ error("invalid handle");
+ else if ((dls->module == 0) || (dls->refs == 0))
+ error("handle to closed library");
+ else
+ return TRUE;
+ return FALSE;
+}
+
+static inline int isFlagSet(int mode, int flag)
+{
+ return (mode & flag) == flag;
+}
+
+static struct dlstatus *lookupStatus(const struct stat *sbuf)
+{
+ struct dlstatus *dls = stqueue;
+ debug("looking for status");
+ while (dls && ( /* isFlagSet(dls->mode, RTLD_UNSHARED) */ 0
+ || sbuf->st_dev != dls->device || sbuf->st_ino != dls->inode))
+ dls = dls->next;
+ return dls;
+}
+
+static void insertStatus(struct dlstatus *dls, const struct stat *sbuf)
+{
+ debug("inserting status");
+ dls->inode = sbuf->st_ino;
+ dls->device = sbuf->st_dev;
+ dls->refs = 0;
+ dls->mode = 0;
+ if ((dls->flags & DL_IN_LIST) == 0)
+ {
+ dls->next = stqueue;
+ stqueue = dls;
+ dls->flags |= DL_IN_LIST;
+ }
+}
+
+static struct dlstatus *allocStatus()
+{
+ struct dlstatus *dls;
+#ifdef REUSE_STATUS
+ dls = stqueue;
+ while (dls && dls->module)
+ dls = dls->next;
+ if (!dls)
+#endif
+ dls = ast_malloc(sizeof(*dls));
+ dls->flags = 0;
+ return dls;
+}
+
+static int promoteLocalToGlobal(struct dlstatus *dls)
+{
+ static int (*p) (NSModule module) = 0;
+ debug("promoting");
+ if (!p)
+ _dyld_func_lookup("__dyld_NSMakePrivateModulePublic", (unsigned long *)&p);
+ return (dls->module == MAGIC_DYLIB_MOD) || (p && p(dls->module));
+}
+
+static void *reference(struct dlstatus *dls, int mode)
+{
+ if (dls)
+ {
+ if (dls->module == MAGIC_DYLIB_MOD && !isFlagSet(mode, RTLD_GLOBAL))
+ {
+ warning("trying to open a .dylib with RTLD_LOCAL");
+ error("unable to open a .dylib with RTLD_LOCAL");
+ return NULL;
+ }
+ if (isFlagSet(mode, RTLD_GLOBAL) &&
+ !isFlagSet(dls->mode, RTLD_GLOBAL) && !promoteLocalToGlobal(dls))
+ {
+ error("unable to promote local module to global");
+ return NULL;
+ }
+ dls->mode |= mode;
+ dls->refs++;
+ }
+ else
+ debug("reference called with NULL argument");
+
+ return dls;
+}
+
+static const struct mach_header *my_find_image(const char *name)
+{
+ const struct mach_header *mh = 0;
+ const char *id = NULL;
+ int i = _dyld_image_count();
+ int j;
+ mh = (struct mach_header *)
+ dyld_NSAddImage(name, NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED |
+ NSADDIMAGE_OPTION_RETURN_ON_ERROR);
+ if (!mh)
+ {
+ for (j = 0; j < i; j++)
+ {
+ id = _dyld_get_image_name(j);
+ if (!strcmp(id, name))
+ {
+ mh = _dyld_get_image_header(j);
+ break;
+ }
+ }
+ }
+ return mh;
+}
+
+/*
+ * dyld adds libraries by first adding the directly dependant libraries in link order, and
+ * then adding the dependencies for those libraries, so we should do the same... but we don't
+ * bother adding the extra dependencies, if the symbols are neither in the loaded image nor
+ * any of it's direct dependencies, then it probably isn't there.
+ */
+NSSymbol *search_linked_libs(const struct mach_header * mh, const char *symbol)
+{
+ int n;
+ struct load_command *lc = 0;
+ struct mach_header *wh;
+ NSSymbol *nssym = 0;
+ if (dyld_NSAddImage && dyld_NSIsSymbolNameDefinedInImage && dyld_NSLookupSymbolInImage)
+ {
+ lc = (struct load_command *)((char *)mh + sizeof(struct mach_header));
+ for (n = 0; n < mh->ncmds; n++, lc = (struct load_command *)((char *)lc + lc->cmdsize))
+ {
+ if ((LC_LOAD_DYLIB == lc->cmd) || (LC_LOAD_WEAK_DYLIB == lc->cmd))
+ {
+ if ((wh = (struct mach_header *)
+ my_find_image((char *)(((struct dylib_command *)lc)->dylib.name.offset +
+ (char *)lc))))
+ {
+ if (dyld_NSIsSymbolNameDefinedInImage(wh, symbol))
+ {
+ nssym = dyld_NSLookupSymbolInImage(wh,
+ symbol,
+ NSLOOKUPSYMBOLINIMAGE_OPTION_BIND |
+ NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR);
+ break;
+ }
+ }
+ }
+ }
+ if ((!nssym) && NSIsSymbolNameDefined(symbol))
+ {
+ /* I've never seen this debug message...*/
+ debug("Symbol \"%s\" is defined but was not found", symbol);
+ }
+ }
+ return nssym;
+}
+
+/* Up to the caller to free() returned string */
+static inline const char *dyld_error_str()
+{
+ NSLinkEditErrors dylder;
+ int dylderno;
+ const char *dylderrstr;
+ const char *dyldfile;
+ const char* retStr = NULL;
+ NSLinkEditError(&dylder, &dylderno, &dyldfile, &dylderrstr);
+ if (dylderrstr && strlen(dylderrstr))
+ {
+ retStr = ast_malloc(strlen(dylderrstr) +1);
+ strcpy((char*)retStr,dylderrstr);
+ }
+ return retStr;
+}
+
+static void *dlsymIntern(struct dlstatus *dls, const char *symbol, int canSetError)
+{
+ NSSymbol *nssym = 0;
+ void *caller = __builtin_return_address(1); /* Be *very* careful about inlining */
+ const struct mach_header *caller_mh = 0;
+ const char* savedErrorStr = NULL;
+ resetdlerror();
+#ifndef RTLD_SELF
+#define RTLD_SELF ((void *) -3)
+#endif
+ if (NULL == dls)
+ dls = RTLD_SELF;
+ if ((RTLD_NEXT == dls) || (RTLD_SELF == dls))
+ {
+ if (dyld_NSIsSymbolNameDefinedInImage && dyld_NSLookupSymbolInImage)
+ {
+ caller_mh = image_for_address(caller);
+ if (RTLD_SELF == dls)
+ {
+ /* FIXME: We should be using the NSModule api, if SELF is an MH_BUNDLE
+ * But it appears to work anyway, and looking at the code in dyld_libfuncs.c
+ * this is acceptable.
+ */
+ if (dyld_NSIsSymbolNameDefinedInImage(caller_mh, symbol))
+ {
+ nssym = dyld_NSLookupSymbolInImage(caller_mh,
+ symbol,
+ NSLOOKUPSYMBOLINIMAGE_OPTION_BIND |
+ NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR);
+ }
+ }
+ if (!nssym)
+ {
+ if (RTLD_SELF == dls)
+ savedErrorStr = dyld_error_str();
+ nssym = search_linked_libs(caller_mh, symbol);
+ }
+ }
+ else
+ {
+ if (canSetError)
+ error("RTLD_SELF and RTLD_NEXT are not supported");
+ return NULL;
+ }
+ }
+ if (!nssym)
+ {
+
+ if (RTLD_DEFAULT == dls)
+ {
+ dls = &mainStatus;
+ }
+ if (!isValidStatus(dls))
+ return NULL;
+
+ if (dls->module != MAGIC_DYLIB_MOD)
+ {
+ nssym = NSLookupSymbolInModule(dls->module, symbol);
+ if (!nssym && NSIsSymbolNameDefined(symbol))
+ {
+ debug("Searching dependencies");
+ savedErrorStr = dyld_error_str();
+ nssym = search_linked_libs(get_mach_header_from_NSModule(dls->module), symbol);
+ }
+ }
+ else if (dls->lib && dyld_NSIsSymbolNameDefinedInImage && dyld_NSLookupSymbolInImage)
+ {
+ if (dyld_NSIsSymbolNameDefinedInImage(dls->lib, symbol))
+ {
+ nssym = dyld_NSLookupSymbolInImage(dls->lib,
+ symbol,
+ NSLOOKUPSYMBOLINIMAGE_OPTION_BIND |
+ NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR);
+ }
+ else if (NSIsSymbolNameDefined(symbol))
+ {
+ debug("Searching dependencies");
+ savedErrorStr = dyld_error_str();
+ nssym = search_linked_libs(dls->lib, symbol);
+ }
+ }
+ else if (dls->module == MAGIC_DYLIB_MOD)
+ {
+ /* Global context, use NSLookupAndBindSymbol */
+ if (NSIsSymbolNameDefined(symbol))
+ {
+ /* There doesn't seem to be a return on error option for this call???
+ this is potentially broken, if binding fails, it will improperly
+ exit the application. */
+ nssym = NSLookupAndBindSymbol(symbol);
+ }
+ else
+ {
+ if (savedErrorStr)
+ ast_free(savedErrorStr);
+ savedErrorStr = ast_malloc(256);
+ snprintf((char*)savedErrorStr, 256, "Symbol \"%s\" not in global context",symbol);
+ }
+ }
+ }
+ /* Error reporting */
+ if (!nssym)
+ {
+ if (!savedErrorStr || !strlen(savedErrorStr))
+ {
+ if (savedErrorStr)
+ ast_free(savedErrorStr);
+ savedErrorStr = ast_malloc(256);
+ snprintf((char*)savedErrorStr, 256,"Symbol \"%s\" not found",symbol);
+ }
+ if (canSetError)
+ {
+ error(savedErrorStr);
+ }
+ else
+ {
+ debug(savedErrorStr);
+ }
+ if (savedErrorStr)
+ ast_free(savedErrorStr);
+ return NULL;
+ }
+ return NSAddressOfSymbol(nssym);
+}
+
+static struct dlstatus *loadModule(const char *path, const struct stat *sbuf, int mode)
+{
+ NSObjectFileImage ofi = 0;
+ NSObjectFileImageReturnCode ofirc;
+ struct dlstatus *dls;
+ NSLinkEditErrors ler;
+ int lerno;
+ const char *errstr;
+ const char *file;
+ void (*init) (void);
+ ofirc = NSCreateObjectFileImageFromFile(path, &ofi);
+ switch (ofirc)
+ {
+ case NSObjectFileImageSuccess:
+ break;
+ case NSObjectFileImageInappropriateFile:
+ if (dyld_NSAddImage && dyld_NSIsSymbolNameDefinedInImage && dyld_NSLookupSymbolInImage)
+ {
+ if (!isFlagSet(mode, RTLD_GLOBAL))
+ {
+ warning("trying to open a .dylib with RTLD_LOCAL");
+ error("unable to open this file with RTLD_LOCAL");
+ return NULL;
+ }
+ }
+ else
+ {
+ error("opening this file is unsupported on this system");
+ return NULL;
+ }
+ break;
+ case NSObjectFileImageFailure:
+ error("object file setup failure");
+ return NULL;
+ case NSObjectFileImageArch:
+ error("no object for this architecture");
+ return NULL;
+ case NSObjectFileImageFormat:
+ error("bad object file format");
+ return NULL;
+ case NSObjectFileImageAccess:
+ error("can't read object file");
+ return NULL;
+ default:
+ error("unknown error from NSCreateObjectFileImageFromFile()");
+ return NULL;
+ }
+ dls = lookupStatus(sbuf);
+ if (!dls)
+ {
+ dls = allocStatus();
+ }
+ if (!dls)
+ {
+ error("unable to allocate memory");
+ return NULL;
+ }
+ dls->lib = 0;
+ if (ofirc == NSObjectFileImageInappropriateFile)
+ {
+ if ((dls->lib = dyld_NSAddImage(path, NSADDIMAGE_OPTION_RETURN_ON_ERROR)))
+ {
+ debug("Dynamic lib loaded at %ld", dls->lib);
+ ofi = MAGIC_DYLIB_OFI;
+ dls->module = MAGIC_DYLIB_MOD;
+ ofirc = NSObjectFileImageSuccess;
+ /* Although it is possible with a bit of work to modify this so it works and
+ functions with RTLD_NOW, I don't deem it necessary at the moment */
+ }
+ if (!(dls->module))
+ {
+ NSLinkEditError(&ler, &lerno, &file, &errstr);
+ if (!errstr || (!strlen(errstr)))
+ error("Can't open this file type");
+ else
+ error(errstr);
+ if ((dls->flags & DL_IN_LIST) == 0)
+ {
+ ast_free(dls);
+ }
+ return NULL;
+ }
+ }
+ else
+ {
+ dls->module = NSLinkModule(ofi, path,
+ NSLINKMODULE_OPTION_RETURN_ON_ERROR |
+ NSLINKMODULE_OPTION_PRIVATE |
+ (isFlagSet(mode, RTLD_NOW) ? NSLINKMODULE_OPTION_BINDNOW : 0));
+ NSDestroyObjectFileImage(ofi);
+ if (dls->module)
+ {
+ dls->lib = get_mach_header_from_NSModule(dls->module);
+ }
+ }
+ if (!dls->module)
+ {
+ NSLinkEditError(&ler, &lerno, &file, &errstr);
+ if ((dls->flags & DL_IN_LIST) == 0)
+ {
+ ast_free(dls);
+ }
+ error(errstr);
+ return NULL;
+ }
+
+ insertStatus(dls, sbuf);
+ dls = reference(dls, mode);
+ if ((init = dlsymIntern(dls, "__init", 0)))
+ {
+ debug("calling _init()");
+ init();
+ }
+ return dls;
+}
+
+static void dlcompat_init_func(void)
+{
+ static int inited = 0;
+ if (!inited)
+ {
+ inited = 1;
+ _dyld_func_lookup("__dyld_NSAddImage", (unsigned long *)&dyld_NSAddImage);
+ _dyld_func_lookup("__dyld_NSIsSymbolNameDefinedInImage",
+ (unsigned long *)&dyld_NSIsSymbolNameDefinedInImage);
+ _dyld_func_lookup("__dyld_NSLookupSymbolInImage", (unsigned long *)&dyld_NSLookupSymbolInImage);
+ if (pthread_mutex_init(&dlcompat_mutex, NULL))
+ exit(1);
+ if (pthread_key_create(&dlerror_key, &dlerrorfree))
+ exit(1);
+ /* And be neat and tidy and clean up after ourselves */
+ atexit(dlcompat_cleanup);
+ }
+}
+
+#if 0
+#pragma CALL_ON_LOAD dlcompat_init_func
+#endif
+
+static void dlcompat_cleanup(void)
+{
+ struct dlstatus *dls;
+ struct dlstatus *next;
+ char *data;
+ data = (char *)searchList();
+ if ( data )
+ ast_free(data);
+ data = (char *)getSearchPath(-1);
+ if ( data )
+ ast_free(data);
+ pthread_mutex_destroy(&dlcompat_mutex);
+ pthread_key_delete(dlerror_key);
+ next = stqueue;
+ while (next && (next != &mainStatus))
+ {
+ dls = next;
+ next = dls->next;
+ ast_free(dls);
+ }
+}
+
+static void resetdlerror()
+{
+ struct dlthread *tss;
+ tss = pthread_getspecific(dlerror_key);
+ tss->errset = 0;
+}
+
+static void dlerrorfree(void *data)
+{
+ ast_free(data);
+}
+
+/* We kind of want a recursive lock here, but meet a little trouble
+ * because they are not available pre OS X 10.2, so we fake it
+ * using thread specific storage to keep a lock count
+ */
+static inline void dolock(void)
+{
+ int err = 0;
+ struct dlthread *tss;
+ tss = pthread_getspecific(dlerror_key);
+ if (!tss)
+ {
+ tss = ast_malloc(sizeof(*tss));
+ tss->lockcnt = 0;
+ tss->errset = 0;
+ if (pthread_setspecific(dlerror_key, tss))
+ {
+ fprintf(stderr,"dlcompat: pthread_setspecific failed\n");
+ exit(1);
+ }
+ }
+ if (!tss->lockcnt)
+ err = pthread_mutex_lock(&dlcompat_mutex);
+ tss->lockcnt = tss->lockcnt +1;
+ if (err)
+ exit(err);
+}
+
+static inline void dounlock(void)
+{
+ int err = 0;
+ struct dlthread *tss;
+ tss = pthread_getspecific(dlerror_key);
+ tss->lockcnt = tss->lockcnt -1;
+ if (!tss->lockcnt)
+ err = pthread_mutex_unlock(&dlcompat_mutex);
+ if (err)
+ exit(err);
+}
+
+void *dlopen(const char *path, int mode)
+{
+ const struct stat *sbuf;
+ struct dlstatus *dls;
+ const char *fullPath;
+ dlcompat_init_func(); /* Just in case */
+ dolock();
+ resetdlerror();
+ if (!path)
+ {
+ dls = &mainStatus;
+ goto dlopenok;
+ }
+ if (!(sbuf = findFile(path, &fullPath)))
+ {
+ error("file \"%s\" not found", path);
+ goto dlopenerror;
+ }
+ /* Now checks that it hasn't been closed already */
+ if ((dls = lookupStatus(sbuf)) && (dls->refs > 0))
+ {
+ /* debug("status found"); */
+ dls = reference(dls, mode);
+ goto dlopenok;
+ }
+#ifdef RTLD_NOLOAD
+ if (isFlagSet(mode, RTLD_NOLOAD))
+ {
+ error("no existing handle and RTLD_NOLOAD specified");
+ goto dlopenerror;
+ }
+#endif
+ if (isFlagSet(mode, RTLD_LAZY) && isFlagSet(mode, RTLD_NOW))
+ {
+ error("how can I load something both RTLD_LAZY and RTLD_NOW?");
+ goto dlopenerror;
+ }
+ dls = loadModule(fullPath, sbuf, mode);
+
+ dlopenok:
+ dounlock();
+ return (void *)dls;
+ dlopenerror:
+ dounlock();
+ return NULL;
+}
+
+#if !FINK_BUILD
+void *dlsym(void * dl_restrict handle, const char * dl_restrict symbol)
+{
+ int sym_len = strlen(symbol);
+ void *value = NULL;
+ char *malloc_sym = NULL;
+ dolock();
+ malloc_sym = ast_malloc(sym_len + 2);
+ if (malloc_sym)
+ {
+ sprintf(malloc_sym, "_%s", symbol);
+ value = dlsymIntern(handle, malloc_sym, 1);
+ ast_free(malloc_sym);
+ }
+ else
+ {
+ error("Unable to allocate memory");
+ goto dlsymerror;
+ }
+ dounlock();
+ return value;
+ dlsymerror:
+ dounlock();
+ return NULL;
+}
+#endif
+
+#if FINK_BUILD
+
+void *dlsym_prepend_underscore(void *handle, const char *symbol)
+{
+ void *answer;
+ dolock();
+ answer = dlsym_prepend_underscore_intern(handle, symbol);
+ dounlock();
+ return answer;
+}
+
+static void *dlsym_prepend_underscore_intern(void *handle, const char *symbol)
+{
+/*
+ * A quick and easy way for porting packages which call dlsym(handle,"sym")
+ * If the porter adds -Ddlsym=dlsym_prepend_underscore to the CFLAGS then
+ * this function will be called, and will add the required underscore.
+ *
+ * Note that I haven't figured out yet which should be "standard", prepend
+ * the underscore always, or not at all. These global functions need to go away
+ * for opendarwin.
+ */
+ int sym_len = strlen(symbol);
+ void *value = NULL;
+ char *malloc_sym = NULL;
+ malloc_sym = ast_malloc(sym_len + 2);
+ if (malloc_sym)
+ {
+ sprintf(malloc_sym, "_%s", symbol);
+ value = dlsymIntern(handle, malloc_sym, 1);
+ ast_free(malloc_sym);
+ }
+ else
+ {
+ error("Unable to allocate memory");
+ }
+ return value;
+}
+
+void *dlsym_auto_underscore(void *handle, const char *symbol)
+{
+ void *answer;
+ dolock();
+ answer = dlsym_auto_underscore_intern(handle, symbol);
+ dounlock();
+ return answer;
+
+}
+static void *dlsym_auto_underscore_intern(void *handle, const char *symbol)
+{
+ struct dlstatus *dls = handle;
+ void *addr = 0;
+ addr = dlsymIntern(dls, symbol, 0);
+ if (!addr)
+ addr = dlsym_prepend_underscore_intern(handle, symbol);
+ return addr;
+}
+
+
+void *dlsym(void * dl_restrict handle, const char * dl_restrict symbol)
+{
+ struct dlstatus *dls = handle;
+ void *addr = 0;
+ dolock();
+ addr = dlsymIntern(dls, symbol, 1);
+ dounlock();
+ return addr;
+}
+#endif
+
+int dlclose(void *handle)
+{
+ struct dlstatus *dls = handle;
+ dolock();
+ resetdlerror();
+ if (!isValidStatus(dls))
+ {
+ goto dlcloseerror;
+ }
+ if (dls->module == MAGIC_DYLIB_MOD)
+ {
+ const char *name;
+ if (!dls->lib)
+ {
+ name = "global context";
+ }
+ else
+ {
+ name = get_lib_name(dls->lib);
+ }
+ warning("trying to close a .dylib!");
+ error("Not closing \"%s\" - dynamic libraries cannot be closed", name);
+ goto dlcloseerror;
+ }
+ if (!dls->module)
+ {
+ error("module already closed");
+ goto dlcloseerror;
+ }
+
+ if (dls->refs == 1)
+ {
+ unsigned long options = 0;
+ void (*fini) (void);
+ if ((fini = dlsymIntern(dls, "__fini", 0)))
+ {
+ debug("calling _fini()");
+ fini();
+ }
+#ifdef __ppc__
+ options |= NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES;
+#endif
+#if 1
+/* Currently, if a module contains c++ static destructors and it is unloaded, we
+ * get a segfault in atexit(), due to compiler and dynamic loader differences of
+ * opinion, this works around that.
+ * I really need a way to figure out from code if this is still necessary.
+ */
+ if ((const struct section *)NULL !=
+ getsectbynamefromheader(get_mach_header_from_NSModule(dls->module),
+ "__DATA", "__mod_term_func"))
+ {
+ options |= NSUNLINKMODULE_OPTION_KEEP_MEMORY_MAPPED;
+ }
+#endif
+#ifdef RTLD_NODELETE
+ if (isFlagSet(dls->mode, RTLD_NODELETE))
+ options |= NSUNLINKMODULE_OPTION_KEEP_MEMORY_MAPPED;
+#endif
+ if (!NSUnLinkModule(dls->module, options))
+ {
+ error("unable to unlink module");
+ goto dlcloseerror;
+ }
+ dls->refs--;
+ dls->module = 0;
+ /* Note: the dlstatus struct dls is neither removed from the list
+ * nor is the memory it occupies freed. This shouldn't pose a
+ * problem in mostly all cases, though.
+ */
+ }
+ dounlock();
+ return 0;
+ dlcloseerror:
+ dounlock();
+ return 1;
+}
+
+const char *dlerror(void)
+{
+ struct dlthread *tss;
+ char * err_str;
+ tss = pthread_getspecific(dlerror_key);
+ err_str = tss->errstr;
+ tss = pthread_getspecific(dlerror_key);
+ if (tss->errset == 0)
+ return 0;
+ tss->errset = 0;
+ return (err_str );
+}
+
+/* Given an address, return the mach_header for the image containing it
+ * or zero if the given address is not contained in any loaded images.
+ */
+const struct mach_header *image_for_address(const void *address)
+{
+ unsigned long i;
+ unsigned long j;
+ unsigned long count = _dyld_image_count();
+ struct mach_header *mh = 0;
+ struct load_command *lc = 0;
+ unsigned long addr = NULL;
+ for (i = 0; i < count; i++)
+ {
+ addr = (unsigned long)address - _dyld_get_image_vmaddr_slide(i);
+ mh = _dyld_get_image_header(i);
+ if (mh)
+ {
+ lc = (struct load_command *)((char *)mh + sizeof(struct mach_header));
+ for (j = 0; j < mh->ncmds; j++, lc = (struct load_command *)((char *)lc + lc->cmdsize))
+ {
+ if (LC_SEGMENT == lc->cmd &&
+ addr >= ((struct segment_command *)lc)->vmaddr &&
+ addr <
+ ((struct segment_command *)lc)->vmaddr + ((struct segment_command *)lc)->vmsize)
+ {
+ goto image_found;
+ }
+ }
+ }
+ mh = 0;
+ }
+ image_found:
+ return mh;
+}
+
+int dladdr(const void * dl_restrict p, Dl_info * dl_restrict info)
+{
+/*
+ FIXME: USe the routine image_for_address.
+*/
+ unsigned long i;
+ unsigned long j;
+ unsigned long count = _dyld_image_count();
+ struct mach_header *mh = 0;
+ struct load_command *lc = 0;
+ unsigned long addr = NULL;
+ unsigned long table_off = (unsigned long)0;
+ int found = 0;
+ if (!info)
+ return 0;
+ dolock();
+ resetdlerror();
+ info->dli_fname = 0;
+ info->dli_fbase = 0;
+ info->dli_sname = 0;
+ info->dli_saddr = 0;
+/* Some of this was swiped from code posted by Douglas Davidson <ddavidso AT apple DOT com>
+ * to darwin-development AT lists DOT apple DOT com and slightly modified
+ */
+ for (i = 0; i < count; i++)
+ {
+ addr = (unsigned long)p - _dyld_get_image_vmaddr_slide(i);
+ mh = _dyld_get_image_header(i);
+ if (mh)
+ {
+ lc = (struct load_command *)((char *)mh + sizeof(struct mach_header));
+ for (j = 0; j < mh->ncmds; j++, lc = (struct load_command *)((char *)lc + lc->cmdsize))
+ {
+ if (LC_SEGMENT == lc->cmd &&
+ addr >= ((struct segment_command *)lc)->vmaddr &&
+ addr <
+ ((struct segment_command *)lc)->vmaddr + ((struct segment_command *)lc)->vmsize)
+ {
+ info->dli_fname = _dyld_get_image_name(i);
+ info->dli_fbase = (void *)mh;
+ found = 1;
+ break;
+ }
+ }
+ if (found)
+ break;
+ }
+ }
+ if (!found)
+ {
+ dounlock();
+ return 0;
+ }
+ lc = (struct load_command *)((char *)mh + sizeof(struct mach_header));
+ for (j = 0; j < mh->ncmds; j++, lc = (struct load_command *)((char *)lc + lc->cmdsize))
+ {
+ if (LC_SEGMENT == lc->cmd)
+ {
+ if (!strcmp(((struct segment_command *)lc)->segname, "__LINKEDIT"))
+ break;
+ }
+ }
+ table_off =
+ ((unsigned long)((struct segment_command *)lc)->vmaddr) -
+ ((unsigned long)((struct segment_command *)lc)->fileoff) + _dyld_get_image_vmaddr_slide(i);
+ debug("table off %x", table_off);
+
+ lc = (struct load_command *)((char *)mh + sizeof(struct mach_header));
+ for (j = 0; j < mh->ncmds; j++, lc = (struct load_command *)((char *)lc + lc->cmdsize))
+ {
+ if (LC_SYMTAB == lc->cmd)
+ {
+
+ struct nlist *symtable = (struct nlist *)(((struct symtab_command *)lc)->symoff + table_off);
+ unsigned long numsyms = ((struct symtab_command *)lc)->nsyms;
+ struct nlist *nearest = NULL;
+ unsigned long diff = 0xffffffff;
+ unsigned long strtable = (unsigned long)(((struct symtab_command *)lc)->stroff + table_off);
+ debug("symtable %x", symtable);
+ for (i = 0; i < numsyms; i++)
+ {
+ /* Ignore the following kinds of Symbols */
+ if ((!symtable->n_value) /* Undefined */
+ || (symtable->n_type >= N_PEXT) /* Debug symbol */
+ || (!(symtable->n_type & N_EXT)) /* Local Symbol */
+ )
+ {
+ symtable++;
+ continue;
+ }
+ if ((addr >= symtable->n_value) && (diff >= (symtable->n_value - addr)))
+ {
+ diff = (unsigned long)symtable->n_value - addr;
+ nearest = symtable;
+ }
+ symtable++;
+ }
+ if (nearest)
+ {
+ info->dli_saddr = nearest->n_value + ((void *)p - addr);
+ info->dli_sname = (char *)(strtable + nearest->n_un.n_strx);
+ }
+ }
+ }
+ dounlock();
+ return 1;
+}
+
+
+/*
+ * Implement the dlfunc() interface, which behaves exactly the same as
+ * dlsym() except that it returns a function pointer instead of a data
+ * pointer. This can be used by applications to avoid compiler warnings
+ * about undefined behavior, and is intended as prior art for future
+ * POSIX standardization. This function requires that all pointer types
+ * have the same representation, which is true on all platforms FreeBSD
+ * runs on, but is not guaranteed by the C standard.
+ */
+#if 0
+dlfunc_t dlfunc(void * dl_restrict handle, const char * dl_restrict symbol)
+{
+ union
+ {
+ void *d;
+ dlfunc_t f;
+ } rv;
+ int sym_len = strlen(symbol);
+ char *malloc_sym = NULL;
+ dolock();
+ malloc_sym = ast_malloc(sym_len + 2);
+ if (malloc_sym)
+ {
+ sprintf(malloc_sym, "_%s", symbol);
+ rv.d = dlsymIntern(handle, malloc_sym, 1);
+ ast_free(malloc_sym);
+ }
+ else
+ {
+ error("Unable to allocate memory");
+ goto dlfuncerror;
+ }
+ dounlock();
+ return rv.f;
+ dlfuncerror:
+ dounlock();
+ return NULL;
+}
+#endif
diff --git a/trunk/main/dns.c b/trunk/main/dns.c
new file mode 100644
index 000000000..34212fdf1
--- /dev/null
+++ b/trunk/main/dns.c
@@ -0,0 +1,299 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006 Thorsten Lockert
+ *
+ * Written by Thorsten Lockert <tholo@trollphone.org>
+ *
+ * Funding provided by Troll Phone Networks AS
+ *
+ * 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 DNS Support for Asterisk
+ *
+ * \author Thorsten Lockert <tholo@trollphone.org>
+ *
+ * \par Reference
+ * - DNR SRV records http://www.ietf.org/rfc/rfc2782.txt
+ *
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/network.h"
+#include <arpa/nameser.h> /* res_* functions */
+#include <resolv.h>
+
+#include "asterisk/channel.h"
+#include "asterisk/dns.h"
+#include "asterisk/endian.h"
+
+#define MAX_SIZE 4096
+
+#ifdef __PDP_ENDIAN
+#if __BYTE_ORDER == __PDP_ENDIAN
+#define DETERMINED_BYTE_ORDER __LITTLE_ENDIAN
+#endif
+#endif
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define DETERMINED_BYTE_ORDER __BIG_ENDIAN
+#endif
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define DETERMINED_BYTE_ORDER __LITTLE_ENDIAN
+#endif
+
+/* The dns_HEADER structure definition below originated
+ in the arpa/nameser.h header file distributed with ISC
+ BIND, which contains the following copyright and license
+ notices:
+
+ * ++Copyright++ 1983, 1989, 1993
+ * -
+ * Copyright (c) 1983, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * -
+ * Portions Copyright (c) 1993 by Digital Equipment Corporation.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies, and that
+ * the name of Digital Equipment Corporation not be used in advertising or
+ * publicity pertaining to distribution of the document or software without
+ * specific, written prior permission.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
+ * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ * -
+ * --Copyright--
+ */
+
+typedef struct {
+ unsigned id:16; /*!< query identification number */
+#if DETERMINED_BYTE_ORDER == __BIG_ENDIAN
+ /* fields in third byte */
+ unsigned qr:1; /*!< response flag */
+ unsigned opcode:4; /*!< purpose of message */
+ unsigned aa:1; /*!< authoritive answer */
+ unsigned tc:1; /*!< truncated message */
+ unsigned rd:1; /*!< recursion desired */
+ /* fields in fourth byte */
+ unsigned ra:1; /*!< recursion available */
+ unsigned unused:1; /*!< unused bits (MBZ as of 4.9.3a3) */
+ unsigned ad:1; /*!< authentic data from named */
+ unsigned cd:1; /*!< checking disabled by resolver */
+ unsigned rcode:4; /*!< response code */
+#endif
+#if DETERMINED_BYTE_ORDER == __LITTLE_ENDIAN
+ /* fields in third byte */
+ unsigned rd:1; /*!< recursion desired */
+ unsigned tc:1; /*!< truncated message */
+ unsigned aa:1; /*!< authoritive answer */
+ unsigned opcode:4; /*!< purpose of message */
+ unsigned qr:1; /*!< response flag */
+ /* fields in fourth byte */
+ unsigned rcode:4; /*!< response code */
+ unsigned cd:1; /*!< checking disabled by resolver */
+ unsigned ad:1; /*!< authentic data from named */
+ unsigned unused:1; /*!< unused bits (MBZ as of 4.9.3a3) */
+ unsigned ra:1; /*!< recursion available */
+#endif
+ /* remaining bytes */
+ unsigned qdcount:16; /*!< number of question entries */
+ unsigned ancount:16; /*!< number of answer entries */
+ unsigned nscount:16; /*!< number of authority entries */
+ unsigned arcount:16; /*!< number of resource entries */
+} dns_HEADER;
+
+struct dn_answer {
+ unsigned short rtype;
+ unsigned short class;
+ unsigned int ttl;
+ unsigned short size;
+} __attribute__ ((__packed__));
+
+static int skip_name(unsigned char *s, int len)
+{
+ int x = 0;
+
+ while (x < len) {
+ if (*s == '\0') {
+ s++;
+ x++;
+ break;
+ }
+ if ((*s & 0xc0) == 0xc0) {
+ s += 2;
+ x += 2;
+ break;
+ }
+ x += *s + 1;
+ s += *s + 1;
+ }
+ if (x >= len)
+ return -1;
+ return x;
+}
+
+/*! \brief Parse DNS lookup result, call callback */
+static int dns_parse_answer(void *context,
+ int class, int type, unsigned char *answer, int len,
+ int (*callback)(void *context, unsigned char *answer, int len, unsigned char *fullanswer))
+{
+ unsigned char *fullanswer = answer;
+ struct dn_answer *ans;
+ dns_HEADER *h;
+ int res;
+ int x;
+
+ h = (dns_HEADER *)answer;
+ answer += sizeof(dns_HEADER);
+ len -= sizeof(dns_HEADER);
+
+ for (x = 0; x < ntohs(h->qdcount); x++) {
+ if ((res = skip_name(answer, len)) < 0) {
+ ast_log(LOG_WARNING, "Couldn't skip over name\n");
+ return -1;
+ }
+ answer += res + 4; /* Skip name and QCODE / QCLASS */
+ len -= res + 4;
+ if (len < 0) {
+ ast_log(LOG_WARNING, "Strange query size\n");
+ return -1;
+ }
+ }
+
+ for (x = 0; x < ntohs(h->ancount); x++) {
+ if ((res = skip_name(answer, len)) < 0) {
+ ast_log(LOG_WARNING, "Failed skipping name\n");
+ return -1;
+ }
+ answer += res;
+ len -= res;
+ ans = (struct dn_answer *)answer;
+ answer += sizeof(struct dn_answer);
+ len -= sizeof(struct dn_answer);
+ if (len < 0) {
+ ast_log(LOG_WARNING, "Strange result size\n");
+ return -1;
+ }
+ if (len < 0) {
+ ast_log(LOG_WARNING, "Length exceeds frame\n");
+ return -1;
+ }
+
+ if (ntohs(ans->class) == class && ntohs(ans->rtype) == type) {
+ if (callback) {
+ if ((res = callback(context, answer, ntohs(ans->size), fullanswer)) < 0) {
+ ast_log(LOG_WARNING, "Failed to parse result\n");
+ return -1;
+ }
+ if (res > 0)
+ return 1;
+ }
+ }
+ answer += ntohs(ans->size);
+ len -= ntohs(ans->size);
+ }
+ return 0;
+}
+
+#ifndef HAVE_RES_NINIT
+AST_MUTEX_DEFINE_STATIC(res_lock);
+#endif
+
+/*! \brief Lookup record in DNS
+\note Asterisk DNS is synchronus at this time. This means that if your DNS does
+not work properly, Asterisk might not start properly or a channel may lock.
+*/
+int ast_search_dns(void *context,
+ const char *dname, int class, int type,
+ int (*callback)(void *context, unsigned char *answer, int len, unsigned char *fullanswer))
+{
+#ifdef HAVE_RES_NINIT
+ struct __res_state dnsstate;
+#endif
+ unsigned char answer[MAX_SIZE];
+ int res, ret = -1;
+
+#ifdef HAVE_RES_NINIT
+ res_ninit(&dnsstate);
+ res = res_nsearch(&dnsstate, dname, class, type, answer, sizeof(answer));
+#else
+ ast_mutex_lock(&res_lock);
+ res_init();
+ res = res_search(dname, class, type, answer, sizeof(answer));
+#endif
+ if (res > 0) {
+ if ((res = dns_parse_answer(context, class, type, answer, res, callback)) < 0) {
+ ast_log(LOG_WARNING, "DNS Parse error for %s\n", dname);
+ ret = -1;
+ }
+ else if (res == 0) {
+ ast_debug(1, "No matches found in DNS for %s\n", dname);
+ ret = 0;
+ }
+ else
+ ret = 1;
+ }
+#ifdef HAVE_RES_NINIT
+#ifdef HAVE_RES_NDESTROY
+ res_ndestroy(&dnsstate);
+#else
+ res_nclose(&dnsstate);
+#endif
+#else
+#ifndef __APPLE__
+ res_close();
+#endif
+ ast_mutex_unlock(&res_lock);
+#endif
+
+ return ret;
+}
diff --git a/trunk/main/dnsmgr.c b/trunk/main/dnsmgr.c
new file mode 100644
index 000000000..ed2426c2e
--- /dev/null
+++ b/trunk/main/dnsmgr.c
@@ -0,0 +1,425 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2005-2006, Kevin P. Fleming
+ *
+ * Kevin P. Fleming <kpfleming@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
+ *
+ * \brief Background DNS update manager
+ *
+ * \author Kevin P. Fleming <kpfleming@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/_private.h"
+#include <regex.h>
+#include <signal.h>
+
+#include "asterisk/dnsmgr.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/utils.h"
+#include "asterisk/config.h"
+#include "asterisk/sched.h"
+#include "asterisk/cli.h"
+#include "asterisk/manager.h"
+
+static struct sched_context *sched;
+static int refresh_sched = -1;
+static pthread_t refresh_thread = AST_PTHREADT_NULL;
+
+struct ast_dnsmgr_entry {
+ /*! where we will store the resulting address */
+ struct in_addr *result;
+ /*! the last result, used to check if address has changed */
+ struct in_addr last;
+ /*! Set to 1 if the entry changes */
+ int changed:1;
+ ast_mutex_t lock;
+ AST_RWLIST_ENTRY(ast_dnsmgr_entry) list;
+ /*! just 1 here, but we use calloc to allocate the correct size */
+ char name[1];
+};
+
+static AST_RWLIST_HEAD_STATIC(entry_list, ast_dnsmgr_entry);
+
+AST_MUTEX_DEFINE_STATIC(refresh_lock);
+
+#define REFRESH_DEFAULT 300
+
+static int enabled;
+static int refresh_interval;
+
+struct refresh_info {
+ struct entry_list *entries;
+ int verbose;
+ unsigned int regex_present:1;
+ regex_t filter;
+};
+
+static struct refresh_info master_refresh_info = {
+ .entries = &entry_list,
+ .verbose = 0,
+};
+
+struct ast_dnsmgr_entry *ast_dnsmgr_get(const char *name, struct in_addr *result)
+{
+ struct ast_dnsmgr_entry *entry;
+
+ if (!result || ast_strlen_zero(name) || !(entry = ast_calloc(1, sizeof(*entry) + strlen(name))))
+ return NULL;
+
+ entry->result = result;
+ ast_mutex_init(&entry->lock);
+ strcpy(entry->name, name);
+ memcpy(&entry->last, result, sizeof(entry->last));
+
+ AST_RWLIST_WRLOCK(&entry_list);
+ AST_RWLIST_INSERT_HEAD(&entry_list, entry, list);
+ AST_RWLIST_UNLOCK(&entry_list);
+
+ return entry;
+}
+
+void ast_dnsmgr_release(struct ast_dnsmgr_entry *entry)
+{
+ if (!entry)
+ return;
+
+ AST_RWLIST_WRLOCK(&entry_list);
+ AST_RWLIST_REMOVE(&entry_list, entry, list);
+ AST_RWLIST_UNLOCK(&entry_list);
+ ast_verb(4, "removing dns manager for '%s'\n", entry->name);
+
+ ast_mutex_destroy(&entry->lock);
+ ast_free(entry);
+}
+
+int ast_dnsmgr_lookup(const char *name, struct in_addr *result, struct ast_dnsmgr_entry **dnsmgr)
+{
+ struct ast_hostent ahp;
+ struct hostent *hp;
+
+ if (ast_strlen_zero(name) || !result || !dnsmgr)
+ return -1;
+
+ if (*dnsmgr && !strcasecmp((*dnsmgr)->name, name))
+ return 0;
+
+ ast_verb(4, "doing dnsmgr_lookup for '%s'\n", name);
+
+ /* if it's actually an IP address and not a name,
+ there's no need for a managed lookup */
+ if (inet_aton(name, result))
+ return 0;
+
+ /* do a lookup now but add a manager so it will automagically get updated in the background */
+ if ((hp = ast_gethostbyname(name, &ahp)))
+ memcpy(result, hp->h_addr, sizeof(result));
+
+ /* if dnsmgr is not enable don't bother adding an entry */
+ if (!enabled)
+ return 0;
+
+ ast_verb(3, "adding dns manager for '%s'\n", name);
+ *dnsmgr = ast_dnsmgr_get(name, result);
+ return !*dnsmgr;
+}
+
+/*
+ * Refresh a dnsmgr entry
+ */
+static int dnsmgr_refresh(struct ast_dnsmgr_entry *entry, int verbose)
+{
+ struct ast_hostent ahp;
+ struct hostent *hp;
+ char iabuf[INET_ADDRSTRLEN];
+ char iabuf2[INET_ADDRSTRLEN];
+ struct in_addr tmp;
+ int changed = 0;
+
+ ast_mutex_lock(&entry->lock);
+ if (verbose)
+ ast_verb(3, "refreshing '%s'\n", entry->name);
+
+ if ((hp = ast_gethostbyname(entry->name, &ahp))) {
+ /* check to see if it has changed, do callback if requested (where de callback is defined ????) */
+ memcpy(&tmp, hp->h_addr, sizeof(tmp));
+ if (tmp.s_addr != entry->last.s_addr) {
+ ast_copy_string(iabuf, ast_inet_ntoa(entry->last), sizeof(iabuf));
+ ast_copy_string(iabuf2, ast_inet_ntoa(tmp), sizeof(iabuf2));
+ ast_log(LOG_NOTICE, "host '%s' changed from %s to %s\n",
+ entry->name, iabuf, iabuf2);
+ memcpy(entry->result, hp->h_addr, sizeof(entry->result));
+ memcpy(&entry->last, hp->h_addr, sizeof(entry->last));
+ changed = entry->changed = 1;
+ }
+
+ }
+ ast_mutex_unlock(&entry->lock);
+ return changed;
+}
+
+int ast_dnsmgr_refresh(struct ast_dnsmgr_entry *entry)
+{
+ return dnsmgr_refresh(entry, 0);
+}
+
+/*
+ * Check if dnsmgr entry has changed from since last call to this function
+ */
+int ast_dnsmgr_changed(struct ast_dnsmgr_entry *entry)
+{
+ int changed;
+
+ ast_mutex_lock(&entry->lock);
+
+ changed = entry->changed;
+ entry->changed = 0;
+
+ ast_mutex_unlock(&entry->lock);
+
+ return changed;
+}
+
+static void *do_refresh(void *data)
+{
+ for (;;) {
+ pthread_testcancel();
+ usleep((ast_sched_wait(sched)*1000));
+ pthread_testcancel();
+ ast_sched_runq(sched);
+ }
+ return NULL;
+}
+
+static int refresh_list(const void *data)
+{
+ struct refresh_info *info = (struct refresh_info *)data;
+ struct ast_dnsmgr_entry *entry;
+
+ /* if a refresh or reload is already in progress, exit now */
+ if (ast_mutex_trylock(&refresh_lock)) {
+ if (info->verbose)
+ ast_log(LOG_WARNING, "DNS Manager refresh already in progress.\n");
+ return -1;
+ }
+
+ ast_verb(3, "Refreshing DNS lookups.\n");
+ AST_RWLIST_RDLOCK(info->entries);
+ AST_RWLIST_TRAVERSE(info->entries, entry, list) {
+ if (info->regex_present && regexec(&info->filter, entry->name, 0, NULL, 0))
+ continue;
+
+ dnsmgr_refresh(entry, info->verbose);
+ }
+ AST_RWLIST_UNLOCK(info->entries);
+
+ ast_mutex_unlock(&refresh_lock);
+
+ /* automatically reschedule based on the interval */
+ return refresh_interval * 1000;
+}
+
+void dnsmgr_start_refresh(void)
+{
+ if (refresh_sched > -1) {
+ ast_sched_del(sched, refresh_sched);
+ refresh_sched = ast_sched_add_variable(sched, 100, refresh_list, &master_refresh_info, 1);
+ }
+}
+
+static int do_reload(int loading);
+
+static char *handle_cli_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "dnsmgr reload";
+ e->usage =
+ "Usage: dnsmgr reload\n"
+ " Reloads the DNS manager configuration.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (a->argc > 2)
+ return CLI_SHOWUSAGE;
+
+ do_reload(0);
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_refresh(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct refresh_info info = {
+ .entries = &entry_list,
+ .verbose = 1,
+ };
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "dnsmgr refresh";
+ e->usage =
+ "Usage: dnsmgr refresh [pattern]\n"
+ " Peforms an immediate refresh of the managed DNS entries.\n"
+ " Optional regular expression pattern is used to filter the entries to refresh.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (a->argc > 3)
+ return CLI_SHOWUSAGE;
+
+ if (a->argc == 3) {
+ if ( regcomp(&info.filter, a->argv[2], REG_EXTENDED | REG_NOSUB) )
+ return CLI_SHOWUSAGE;
+ else
+ info.regex_present = 1;
+ }
+
+ refresh_list(&info);
+
+ if (info.regex_present)
+ regfree(&info.filter);
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int count = 0;
+ struct ast_dnsmgr_entry *entry;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "dnsmgr status";
+ e->usage =
+ "Usage: dnsmgr status\n"
+ " Displays the DNS manager status.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc > 2)
+ return CLI_SHOWUSAGE;
+
+ ast_cli(a->fd, "DNS Manager: %s\n", enabled ? "enabled" : "disabled");
+ ast_cli(a->fd, "Refresh Interval: %d seconds\n", refresh_interval);
+ AST_RWLIST_RDLOCK(&entry_list);
+ AST_RWLIST_TRAVERSE(&entry_list, entry, list)
+ count++;
+ AST_RWLIST_UNLOCK(&entry_list);
+ ast_cli(a->fd, "Number of entries: %d\n", count);
+
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli_reload = AST_CLI_DEFINE(handle_cli_reload, "Reloads the DNS manager configuration");
+static struct ast_cli_entry cli_refresh = AST_CLI_DEFINE(handle_cli_refresh, "Performs an immediate refresh");
+static struct ast_cli_entry cli_status = AST_CLI_DEFINE(handle_cli_status, "Display the DNS manager status");
+
+int dnsmgr_init(void)
+{
+ if (!(sched = sched_context_create())) {
+ ast_log(LOG_ERROR, "Unable to create schedule context.\n");
+ return -1;
+ }
+ ast_cli_register(&cli_reload);
+ ast_cli_register(&cli_status);
+ ast_cli_register(&cli_refresh);
+ return do_reload(1);
+}
+
+int dnsmgr_reload(void)
+{
+ return do_reload(0);
+}
+
+static int do_reload(int loading)
+{
+ struct ast_config *config;
+ struct ast_flags config_flags = { loading ? 0 : CONFIG_FLAG_FILEUNCHANGED };
+ const char *interval_value;
+ const char *enabled_value;
+ int interval;
+ int was_enabled;
+ int res = -1;
+
+ if ((config = ast_config_load("dnsmgr.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
+ return 0;
+
+ /* ensure that no refresh cycles run while the reload is in progress */
+ ast_mutex_lock(&refresh_lock);
+
+ /* reset defaults in preparation for reading config file */
+ refresh_interval = REFRESH_DEFAULT;
+ was_enabled = enabled;
+ enabled = 0;
+
+ if (refresh_sched > -1)
+ ast_sched_del(sched, refresh_sched);
+
+ if (config) {
+ if ((enabled_value = ast_variable_retrieve(config, "general", "enable"))) {
+ enabled = ast_true(enabled_value);
+ }
+ if ((interval_value = ast_variable_retrieve(config, "general", "refreshinterval"))) {
+ if (sscanf(interval_value, "%d", &interval) < 1)
+ ast_log(LOG_WARNING, "Unable to convert '%s' to a numeric value.\n", interval_value);
+ else if (interval < 0)
+ ast_log(LOG_WARNING, "Invalid refresh interval '%d' specified, using default\n", interval);
+ else
+ refresh_interval = interval;
+ }
+ ast_config_destroy(config);
+ }
+
+ if (enabled && refresh_interval)
+ ast_log(LOG_NOTICE, "Managed DNS entries will be refreshed every %d seconds.\n", refresh_interval);
+
+ /* if this reload enabled the manager, create the background thread
+ if it does not exist */
+ if (enabled) {
+ if (!was_enabled && (refresh_thread == AST_PTHREADT_NULL)) {
+ if (ast_pthread_create_background(&refresh_thread, NULL, do_refresh, NULL) < 0) {
+ ast_log(LOG_ERROR, "Unable to start refresh thread.\n");
+ }
+ }
+ /* make a background refresh happen right away */
+ refresh_sched = ast_sched_add_variable(sched, 100, refresh_list, &master_refresh_info, 1);
+ res = 0;
+ }
+ /* if this reload disabled the manager and there is a background thread,
+ kill it */
+ else if (!enabled && was_enabled && (refresh_thread != AST_PTHREADT_NULL)) {
+ /* wake up the thread so it will exit */
+ pthread_cancel(refresh_thread);
+ pthread_kill(refresh_thread, SIGURG);
+ pthread_join(refresh_thread, NULL);
+ refresh_thread = AST_PTHREADT_NULL;
+ ast_cli_unregister(&cli_refresh);
+ res = 0;
+ }
+ else
+ res = 0;
+
+ ast_mutex_unlock(&refresh_lock);
+ manager_event(EVENT_FLAG_SYSTEM, "Reload", "Module: DNSmgr\r\nStatus: %s\r/nMessage: DNSmgr reload Requested\r\n", enabled ? "Enabled" : "Disabled");
+
+ return res;
+}
diff --git a/trunk/main/dsp.c b/trunk/main/dsp.c
new file mode 100644
index 000000000..8355aca4e
--- /dev/null
+++ b/trunk/main/dsp.c
@@ -0,0 +1,1349 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * Goertzel routines are borrowed from Steve Underwood's tremendous work on the
+ * DTMF detector.
+ *
+ * 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 Convenience Signal Processing routines
+ *
+ * \author Mark Spencer <markster@digium.com>
+ * \author Steve Underwood <steveu@coppice.org>
+ */
+
+/* Some routines from tone_detect.c by Steven Underwood as published under the zapata library */
+/*
+ tone_detect.c - General telephony tone detection, and specific
+ detection of DTMF.
+
+ Copyright (C) 2001 Steve Underwood <steveu@coppice.org>
+
+ Despite my general liking of the GPL, I place this code in the
+ public domain for the benefit of all mankind - even the slimy
+ ones who might try to proprietize my work and use it to my
+ detriment.
+*/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <math.h>
+
+#include "asterisk/frame.h"
+#include "asterisk/channel.h"
+#include "asterisk/dsp.h"
+#include "asterisk/ulaw.h"
+#include "asterisk/alaw.h"
+#include "asterisk/utils.h"
+#include "asterisk/options.h"
+
+/*! Number of goertzels for progress detect */
+enum gsamp_size {
+ GSAMP_SIZE_NA = 183, /*!< North America - 350, 440, 480, 620, 950, 1400, 1800 Hz */
+ GSAMP_SIZE_CR = 188, /*!< Costa Rica, Brazil - Only care about 425 Hz */
+ GSAMP_SIZE_UK = 160 /*!< UK disconnect goertzel feed - should trigger 400hz */
+};
+
+enum prog_mode {
+ PROG_MODE_NA = 0,
+ PROG_MODE_CR,
+ PROG_MODE_UK
+};
+
+enum freq_index {
+ /*! For US modes { */
+ HZ_350 = 0,
+ HZ_440,
+ HZ_480,
+ HZ_620,
+ HZ_950,
+ HZ_1400,
+ HZ_1800, /*!< } */
+
+ /*! For CR/BR modes */
+ HZ_425 = 0,
+
+ /*! For UK mode */
+ HZ_400 = 0
+};
+
+static struct progalias {
+ char *name;
+ enum prog_mode mode;
+} aliases[] = {
+ { "us", PROG_MODE_NA },
+ { "ca", PROG_MODE_NA },
+ { "cr", PROG_MODE_CR },
+ { "br", PROG_MODE_CR },
+ { "uk", PROG_MODE_UK },
+};
+
+static struct progress {
+ enum gsamp_size size;
+ int freqs[7];
+} modes[] = {
+ { GSAMP_SIZE_NA, { 350, 440, 480, 620, 950, 1400, 1800 } }, /*!< North America */
+ { GSAMP_SIZE_CR, { 425 } }, /*!< Costa Rica, Brazil */
+ { GSAMP_SIZE_UK, { 400 } }, /*!< UK */
+};
+
+#define DEFAULT_THRESHOLD 512
+
+enum busy_detect {
+ BUSY_PERCENT = 10, /*!< The percentage difference between the two last silence periods */
+ BUSY_PAT_PERCENT = 7, /*!< The percentage difference between measured and actual pattern */
+ BUSY_THRESHOLD = 100, /*!< Max number of ms difference between max and min times in busy */
+ BUSY_MIN = 75, /*!< Busy must be at least 80 ms in half-cadence */
+ BUSY_MAX =3100 /*!< Busy can't be longer than 3100 ms in half-cadence */
+};
+
+/*! Remember last 15 units */
+#define DSP_HISTORY 15
+
+#define TONE_THRESH 10.0 /*!< How much louder the tone should be than channel energy */
+#define TONE_MIN_THRESH 1e8 /*!< How much tone there should be at least to attempt */
+
+/*! All THRESH_XXX values are in GSAMP_SIZE chunks (us = 22ms) */
+enum gsamp_thresh {
+ THRESH_RING = 8, /*!< Need at least 150ms ring to accept */
+ THRESH_TALK = 2, /*!< Talk detection does not work continuously */
+ THRESH_BUSY = 4, /*!< Need at least 80ms to accept */
+ THRESH_CONGESTION = 4, /*!< Need at least 80ms to accept */
+ THRESH_HANGUP = 60, /*!< Need at least 1300ms to accept hangup */
+ THRESH_RING2ANSWER = 300 /*!< Timeout from start of ring to answer (about 6600 ms) */
+};
+
+#define MAX_DTMF_DIGITS 128
+
+/* Basic DTMF specs:
+ *
+ * Minimum tone on = 40ms
+ * Minimum tone off = 50ms
+ * Maximum digit rate = 10 per second
+ * Normal twist <= 8dB accepted
+ * Reverse twist <= 4dB accepted
+ * S/N >= 15dB will detect OK
+ * Attenuation <= 26dB will detect OK
+ * Frequency tolerance +- 1.5% will detect, +-3.5% will reject
+ */
+
+#define DTMF_THRESHOLD 8.0e7
+#define FAX_THRESHOLD 8.0e7
+#define FAX_2ND_HARMONIC 2.0 /* 4dB */
+#define DTMF_NORMAL_TWIST 6.3 /* 8dB */
+#ifdef RADIO_RELAX
+#define DTMF_REVERSE_TWIST ((digitmode & DSP_DIGITMODE_RELAXDTMF) ? 6.5 : 2.5) /* 4dB normal */
+#else
+#define DTMF_REVERSE_TWIST ((digitmode & DSP_DIGITMODE_RELAXDTMF) ? 4.0 : 2.5) /* 4dB normal */
+#endif
+#define DTMF_RELATIVE_PEAK_ROW 6.3 /* 8dB */
+#define DTMF_RELATIVE_PEAK_COL 6.3 /* 8dB */
+#define DTMF_2ND_HARMONIC_ROW ((digitmode & DSP_DIGITMODE_RELAXDTMF) ? 1.7 : 2.5) /* 4dB normal */
+#define DTMF_2ND_HARMONIC_COL 63.1 /* 18dB */
+#define DTMF_TO_TOTAL_ENERGY 42.0
+
+#define BELL_MF_THRESHOLD 1.6e9
+#define BELL_MF_TWIST 4.0 /* 6dB */
+#define BELL_MF_RELATIVE_PEAK 12.6 /* 11dB */
+
+#if defined(BUSYDETECT_TONEONLY) && defined(BUSYDETECT_COMPARE_TONE_AND_SILENCE)
+#error You cant use BUSYDETECT_TONEONLY together with BUSYDETECT_COMPARE_TONE_AND_SILENCE
+#endif
+
+typedef struct {
+ int v2;
+ int v3;
+ int chunky;
+ int fac;
+ int samples;
+} goertzel_state_t;
+
+typedef struct {
+ int value;
+ int power;
+} goertzel_result_t;
+
+typedef struct
+{
+ goertzel_state_t row_out[4];
+ goertzel_state_t col_out[4];
+ goertzel_state_t fax_tone;
+ int lasthit;
+ int mhit;
+ float energy;
+ int current_sample;
+
+ char digits[MAX_DTMF_DIGITS + 1];
+
+ int current_digits;
+ int detected_digits;
+ int lost_digits;
+ int digit_hits[16];
+ int fax_hits;
+} dtmf_detect_state_t;
+
+typedef struct
+{
+ goertzel_state_t tone_out[6];
+ int mhit;
+ int hits[5];
+ int current_sample;
+
+ char digits[MAX_DTMF_DIGITS + 1];
+
+ int current_digits;
+ int detected_digits;
+ int lost_digits;
+} mf_detect_state_t;
+
+static float dtmf_row[] =
+{
+ 697.0, 770.0, 852.0, 941.0
+};
+static float dtmf_col[] =
+{
+ 1209.0, 1336.0, 1477.0, 1633.0
+};
+
+static float mf_tones[] =
+{
+ 700.0, 900.0, 1100.0, 1300.0, 1500.0, 1700.0
+};
+
+static float fax_freq = 1100.0;
+
+static char dtmf_positions[] = "123A" "456B" "789C" "*0#D";
+
+static char bell_mf_positions[] = "1247C-358A--69*---0B----#";
+
+static inline void goertzel_sample(goertzel_state_t *s, short sample)
+{
+ int v1;
+
+ v1 = s->v2;
+ s->v2 = s->v3;
+
+ s->v3 = (s->fac * s->v2) >> 15;
+ s->v3 = s->v3 - v1 + (sample >> s->chunky);
+ if (abs(s->v3) > 32768) {
+ s->chunky++;
+ s->v3 = s->v3 >> 1;
+ s->v2 = s->v2 >> 1;
+ v1 = v1 >> 1;
+ }
+}
+
+static inline void goertzel_update(goertzel_state_t *s, short *samps, int count)
+{
+ int i;
+
+ for (i=0;i<count;i++)
+ goertzel_sample(s, samps[i]);
+}
+
+
+static inline float goertzel_result(goertzel_state_t *s)
+{
+ goertzel_result_t r;
+ r.value = (s->v3 * s->v3) + (s->v2 * s->v2);
+ r.value -= ((s->v2 * s->v3) >> 15) * s->fac;
+ r.power = s->chunky * 2;
+ return (float)r.value * (float)(1 << r.power);
+}
+
+static inline void goertzel_init(goertzel_state_t *s, float freq, int samples)
+{
+ s->v2 = s->v3 = s->chunky = 0.0;
+ s->fac = (int)(32768.0 * 2.0 * cos(2.0 * M_PI * (freq / 8000.0)));
+ s->samples = samples;
+}
+
+static inline void goertzel_reset(goertzel_state_t *s)
+{
+ s->v2 = s->v3 = s->chunky = 0.0;
+}
+
+struct ast_dsp {
+ struct ast_frame f;
+ int threshold;
+ int totalsilence;
+ int totalnoise;
+ int features;
+ int ringtimeout;
+ int busymaybe;
+ int busycount;
+ int busy_tonelength;
+ int busy_quietlength;
+ int historicnoise[DSP_HISTORY];
+ int historicsilence[DSP_HISTORY];
+ goertzel_state_t freqs[7];
+ int freqcount;
+ int gsamps;
+ enum gsamp_size gsamp_size;
+ enum prog_mode progmode;
+ int tstate;
+ int tcount;
+ int digitmode;
+ int thinkdigit;
+ float genergy;
+ union {
+ dtmf_detect_state_t dtmf;
+ mf_detect_state_t mf;
+ } td;
+};
+
+static void ast_dtmf_detect_init (dtmf_detect_state_t *s)
+{
+ int i;
+
+ s->lasthit = 0;
+ for (i = 0; i < 4; i++) {
+ goertzel_init (&s->row_out[i], dtmf_row[i], 102);
+ goertzel_init (&s->col_out[i], dtmf_col[i], 102);
+ s->energy = 0.0;
+ }
+ /* Same for the fax dector */
+ goertzel_init (&s->fax_tone, fax_freq, 102);
+
+ s->current_sample = 0;
+ s->detected_digits = 0;
+ s->current_digits = 0;
+ memset(&s->digits, 0, sizeof(s->digits));
+ s->lost_digits = 0;
+ s->digits[0] = '\0';
+}
+
+static void ast_mf_detect_init (mf_detect_state_t *s)
+{
+ int i;
+ s->hits[0] = s->hits[1] = s->hits[2] = s->hits[3] = s->hits[4] = 0;
+ for (i = 0; i < 6; i++) {
+ goertzel_init (&s->tone_out[i], mf_tones[i], 160);
+ }
+ s->current_digits = 0;
+ memset(&s->digits, 0, sizeof(s->digits));
+ s->current_sample = 0;
+ s->detected_digits = 0;
+ s->lost_digits = 0;
+ s->digits[0] = '\0';
+ s->mhit = 0;
+}
+
+static int dtmf_detect (dtmf_detect_state_t *s, int16_t amp[], int samples,
+ int digitmode, int *writeback, int faxdetect)
+{
+ float row_energy[4];
+ float col_energy[4];
+ float fax_energy = 0.0;
+ float famp;
+ int i;
+ int j;
+ int sample;
+ int best_row;
+ int best_col;
+ int hit;
+ int limit;
+
+ hit = 0;
+ for (sample = 0; sample < samples; sample = limit) {
+ /* 102 is optimised to meet the DTMF specs. */
+ if ((samples - sample) >= (102 - s->current_sample))
+ limit = sample + (102 - s->current_sample);
+ else
+ limit = samples;
+ /* The following unrolled loop takes only 35% (rough estimate) of the
+ time of a rolled loop on the machine on which it was developed */
+ for (j = sample; j < limit; j++) {
+ famp = amp[j];
+ s->energy += famp*famp;
+ /* With GCC 2.95, the following unrolled code seems to take about 35%
+ (rough estimate) as long as a neat little 0-3 loop */
+ goertzel_sample(s->row_out, amp[j]);
+ goertzel_sample(s->col_out, amp[j]);
+ goertzel_sample(s->row_out + 1, amp[j]);
+ goertzel_sample(s->col_out + 1, amp[j]);
+ goertzel_sample(s->row_out + 2, amp[j]);
+ goertzel_sample(s->col_out + 2, amp[j]);
+ goertzel_sample(s->row_out + 3, amp[j]);
+ goertzel_sample(s->col_out + 3, amp[j]);
+
+ /* Update fax tone */
+ if (faxdetect)
+ goertzel_sample(&s->fax_tone, amp[j]);
+ }
+ s->current_sample += (limit - sample);
+ if (s->current_sample < 102) {
+ if (hit && !((digitmode & DSP_DIGITMODE_NOQUELCH))) {
+ /* If we had a hit last time, go ahead and clear this out since likely it
+ will be another hit */
+ for (i=sample;i<limit;i++)
+ amp[i] = 0;
+ *writeback = 1;
+ }
+ continue;
+ }
+ /* Detect the fax energy, too */
+ if (faxdetect)
+ fax_energy = goertzel_result(&s->fax_tone);
+ /* We are at the end of a DTMF detection block */
+ /* Find the peak row and the peak column */
+ row_energy[0] = goertzel_result (&s->row_out[0]);
+ col_energy[0] = goertzel_result (&s->col_out[0]);
+
+ for (best_row = best_col = 0, i = 1; i < 4; i++) {
+ row_energy[i] = goertzel_result (&s->row_out[i]);
+ if (row_energy[i] > row_energy[best_row])
+ best_row = i;
+ col_energy[i] = goertzel_result (&s->col_out[i]);
+ if (col_energy[i] > col_energy[best_col])
+ best_col = i;
+ }
+ hit = 0;
+ /* Basic signal level test and the twist test */
+ if (row_energy[best_row] >= DTMF_THRESHOLD &&
+ col_energy[best_col] >= DTMF_THRESHOLD &&
+ col_energy[best_col] < row_energy[best_row]*DTMF_REVERSE_TWIST &&
+ col_energy[best_col]*DTMF_NORMAL_TWIST > row_energy[best_row]) {
+ /* Relative peak test */
+ for (i = 0; i < 4; i++) {
+ if ((i != best_col &&
+ col_energy[i]*DTMF_RELATIVE_PEAK_COL > col_energy[best_col]) ||
+ (i != best_row
+ && row_energy[i]*DTMF_RELATIVE_PEAK_ROW > row_energy[best_row])) {
+ break;
+ }
+ }
+ /* ... and fraction of total energy test */
+ if (i >= 4 &&
+ (row_energy[best_row] + col_energy[best_col]) > DTMF_TO_TOTAL_ENERGY*s->energy) {
+ /* Got a hit */
+ hit = dtmf_positions[(best_row << 2) + best_col];
+ if (!(digitmode & DSP_DIGITMODE_NOQUELCH)) {
+ /* Zero out frame data if this is part DTMF */
+ for (i=sample;i<limit;i++)
+ amp[i] = 0;
+ *writeback = 1;
+ }
+ }
+ }
+
+ /* Look for two successive similar results */
+ /* The logic in the next test is:
+ We need two successive identical clean detects, with
+ something different preceeding it. This can work with
+ back to back differing digits. More importantly, it
+ can work with nasty phones that give a very wobbly start
+ to a digit */
+ if (hit == s->lasthit && hit != s->mhit) {
+ if (hit) {
+ s->digit_hits[(best_row << 2) + best_col]++;
+ s->detected_digits++;
+ if (s->current_digits < MAX_DTMF_DIGITS) {
+ s->digits[s->current_digits++] = hit;
+ s->digits[s->current_digits] = '\0';
+ } else {
+ s->lost_digits++;
+ }
+ }
+ s->mhit = hit;
+ }
+
+ if (!hit && faxdetect && (fax_energy >= FAX_THRESHOLD) &&
+ (fax_energy >= DTMF_TO_TOTAL_ENERGY*s->energy)) {
+ /* XXX Probably need better checking than just this the energy XXX */
+ hit = 'f';
+ s->fax_hits++;
+ } else {
+ if (s->fax_hits > 5) {
+ hit = 'f';
+ s->mhit = 'f';
+ s->detected_digits++;
+ if (s->current_digits < MAX_DTMF_DIGITS) {
+ s->digits[s->current_digits++] = hit;
+ s->digits[s->current_digits] = '\0';
+ } else {
+ s->lost_digits++;
+ }
+ }
+ s->fax_hits = 0;
+ }
+ s->lasthit = hit;
+ /* Reinitialise the detector for the next block */
+ for (i = 0; i < 4; i++) {
+ goertzel_reset(&s->row_out[i]);
+ goertzel_reset(&s->col_out[i]);
+ }
+ if (faxdetect)
+ goertzel_reset (&s->fax_tone);
+ s->energy = 0.0;
+ s->current_sample = 0;
+ }
+ return (s->mhit); /* return the debounced hit */
+}
+
+/* MF goertzel size */
+#define MF_GSIZE 120
+
+static int mf_detect (mf_detect_state_t *s, int16_t amp[],
+ int samples, int digitmode, int *writeback)
+{
+ float energy[6];
+ int best;
+ int second_best;
+ float famp;
+ int i;
+ int j;
+ int sample;
+ int hit;
+ int limit;
+
+ hit = 0;
+ for (sample = 0; sample < samples; sample = limit) {
+ /* 80 is optimised to meet the MF specs. */
+ if ((samples - sample) >= (MF_GSIZE - s->current_sample))
+ limit = sample + (MF_GSIZE - s->current_sample);
+ else
+ limit = samples;
+ /* The following unrolled loop takes only 35% (rough estimate) of the
+ time of a rolled loop on the machine on which it was developed */
+ for (j = sample; j < limit; j++) {
+ famp = amp[j];
+ /* With GCC 2.95, the following unrolled code seems to take about 35%
+ (rough estimate) as long as a neat little 0-3 loop */
+ goertzel_sample(s->tone_out, amp[j]);
+ goertzel_sample(s->tone_out + 1, amp[j]);
+ goertzel_sample(s->tone_out + 2, amp[j]);
+ goertzel_sample(s->tone_out + 3, amp[j]);
+ goertzel_sample(s->tone_out + 4, amp[j]);
+ goertzel_sample(s->tone_out + 5, amp[j]);
+ }
+ s->current_sample += (limit - sample);
+ if (s->current_sample < MF_GSIZE) {
+ if (hit && !((digitmode & DSP_DIGITMODE_NOQUELCH))) {
+ /* If we had a hit last time, go ahead and clear this out since likely it
+ will be another hit */
+ for (i=sample;i<limit;i++)
+ amp[i] = 0;
+ *writeback = 1;
+ }
+ continue;
+ }
+ /* We're at the end of an MF detection block. */
+ /* Find the two highest energies. The spec says to look for
+ two tones and two tones only. Taking this literally -ie
+ only two tones pass the minimum threshold - doesn't work
+ well. The sinc function mess, due to rectangular windowing
+ ensure that! Find the two highest energies and ensure they
+ are considerably stronger than any of the others. */
+ energy[0] = goertzel_result(&s->tone_out[0]);
+ energy[1] = goertzel_result(&s->tone_out[1]);
+ if (energy[0] > energy[1]) {
+ best = 0;
+ second_best = 1;
+ } else {
+ best = 1;
+ second_best = 0;
+ }
+ /*endif*/
+ for (i=2;i<6;i++) {
+ energy[i] = goertzel_result(&s->tone_out[i]);
+ if (energy[i] >= energy[best]) {
+ second_best = best;
+ best = i;
+ } else if (energy[i] >= energy[second_best]) {
+ second_best = i;
+ }
+ }
+ /* Basic signal level and twist tests */
+ hit = 0;
+ if (energy[best] >= BELL_MF_THRESHOLD && energy[second_best] >= BELL_MF_THRESHOLD
+ && energy[best] < energy[second_best]*BELL_MF_TWIST
+ && energy[best]*BELL_MF_TWIST > energy[second_best]) {
+ /* Relative peak test */
+ hit = -1;
+ for (i=0;i<6;i++) {
+ if (i != best && i != second_best) {
+ if (energy[i]*BELL_MF_RELATIVE_PEAK >= energy[second_best]) {
+ /* The best two are not clearly the best */
+ hit = 0;
+ break;
+ }
+ }
+ }
+ }
+ if (hit) {
+ /* Get the values into ascending order */
+ if (second_best < best) {
+ i = best;
+ best = second_best;
+ second_best = i;
+ }
+ best = best*5 + second_best - 1;
+ hit = bell_mf_positions[best];
+ /* Look for two successive similar results */
+ /* The logic in the next test is:
+ For KP we need 4 successive identical clean detects, with
+ two blocks of something different preceeding it. For anything
+ else we need two successive identical clean detects, with
+ two blocks of something different preceeding it. */
+ if (hit == s->hits[4] && hit == s->hits[3] &&
+ ((hit != '*' && hit != s->hits[2] && hit != s->hits[1])||
+ (hit == '*' && hit == s->hits[2] && hit != s->hits[1] &&
+ hit != s->hits[0]))) {
+ s->detected_digits++;
+ if (s->current_digits < MAX_DTMF_DIGITS) {
+ s->digits[s->current_digits++] = hit;
+ s->digits[s->current_digits] = '\0';
+ } else {
+ s->lost_digits++;
+ }
+ }
+ } else {
+ hit = 0;
+ }
+ s->hits[0] = s->hits[1];
+ s->hits[1] = s->hits[2];
+ s->hits[2] = s->hits[3];
+ s->hits[3] = s->hits[4];
+ s->hits[4] = hit;
+ /* Reinitialise the detector for the next block */
+ for (i = 0; i < 6; i++)
+ goertzel_reset(&s->tone_out[i]);
+ s->current_sample = 0;
+ }
+ if ((!s->mhit) || (s->mhit != hit)) {
+ s->mhit = 0;
+ return(0);
+ }
+ return (hit);
+}
+
+static int __ast_dsp_digitdetect(struct ast_dsp *dsp, short *s, int len, int *writeback)
+{
+ int res;
+
+ if (dsp->digitmode & DSP_DIGITMODE_MF)
+ res = mf_detect(&dsp->td.mf, s, len, dsp->digitmode & DSP_DIGITMODE_RELAXDTMF, writeback);
+ else
+ res = dtmf_detect(&dsp->td.dtmf, s, len, dsp->digitmode & DSP_DIGITMODE_RELAXDTMF, writeback, dsp->features & DSP_FEATURE_FAX_DETECT);
+ return res;
+}
+
+int ast_dsp_digitdetect(struct ast_dsp *dsp, struct ast_frame *inf)
+{
+ short *s;
+ int len;
+ int ign=0;
+
+ if (inf->frametype != AST_FRAME_VOICE) {
+ ast_log(LOG_WARNING, "Can't check call progress of non-voice frames\n");
+ return 0;
+ }
+ if (inf->subclass != AST_FORMAT_SLINEAR) {
+ ast_log(LOG_WARNING, "Can only check call progress in signed-linear frames\n");
+ return 0;
+ }
+ s = inf->data;
+ len = inf->datalen / 2;
+ return __ast_dsp_digitdetect(dsp, s, len, &ign);
+}
+
+static inline int pair_there(float p1, float p2, float i1, float i2, float e)
+{
+ /* See if p1 and p2 are there, relative to i1 and i2 and total energy */
+ /* Make sure absolute levels are high enough */
+ if ((p1 < TONE_MIN_THRESH) || (p2 < TONE_MIN_THRESH))
+ return 0;
+ /* Amplify ignored stuff */
+ i2 *= TONE_THRESH;
+ i1 *= TONE_THRESH;
+ e *= TONE_THRESH;
+ /* Check first tone */
+ if ((p1 < i1) || (p1 < i2) || (p1 < e))
+ return 0;
+ /* And second */
+ if ((p2 < i1) || (p2 < i2) || (p2 < e))
+ return 0;
+ /* Guess it's there... */
+ return 1;
+}
+
+int ast_dsp_getdigits (struct ast_dsp *dsp, char *buf, int max)
+{
+ if (dsp->digitmode & DSP_DIGITMODE_MF) {
+ if (max > dsp->td.mf.current_digits)
+ max = dsp->td.mf.current_digits;
+ if (max > 0) {
+ memcpy(buf, dsp->td.mf.digits, max);
+ memmove(dsp->td.mf.digits, dsp->td.mf.digits + max, dsp->td.mf.current_digits - max);
+ dsp->td.mf.current_digits -= max;
+ }
+ buf[max] = '\0';
+ return max;
+ } else {
+ if (max > dsp->td.dtmf.current_digits)
+ max = dsp->td.dtmf.current_digits;
+ if (max > 0) {
+ memcpy (buf, dsp->td.dtmf.digits, max);
+ memmove (dsp->td.dtmf.digits, dsp->td.dtmf.digits + max, dsp->td.dtmf.current_digits - max);
+ dsp->td.dtmf.current_digits -= max;
+ }
+ buf[max] = '\0';
+ return max;
+ }
+}
+
+static int __ast_dsp_call_progress(struct ast_dsp *dsp, short *s, int len)
+{
+ int x;
+ int y;
+ int pass;
+ int newstate = DSP_TONE_STATE_SILENCE;
+ int res = 0;
+ while (len) {
+ /* Take the lesser of the number of samples we need and what we have */
+ pass = len;
+ if (pass > dsp->gsamp_size - dsp->gsamps)
+ pass = dsp->gsamp_size - dsp->gsamps;
+ for (x=0;x<pass;x++) {
+ for (y=0;y<dsp->freqcount;y++)
+ goertzel_sample(&dsp->freqs[y], s[x]);
+ dsp->genergy += s[x] * s[x];
+ }
+ s += pass;
+ dsp->gsamps += pass;
+ len -= pass;
+ if (dsp->gsamps == dsp->gsamp_size) {
+ float hz[7];
+ for (y=0;y<7;y++)
+ hz[y] = goertzel_result(&dsp->freqs[y]);
+ switch (dsp->progmode) {
+ case PROG_MODE_NA:
+ if (pair_there(hz[HZ_480], hz[HZ_620], hz[HZ_350], hz[HZ_440], dsp->genergy)) {
+ newstate = DSP_TONE_STATE_BUSY;
+ } else if (pair_there(hz[HZ_440], hz[HZ_480], hz[HZ_350], hz[HZ_620], dsp->genergy)) {
+ newstate = DSP_TONE_STATE_RINGING;
+ } else if (pair_there(hz[HZ_350], hz[HZ_440], hz[HZ_480], hz[HZ_620], dsp->genergy)) {
+ newstate = DSP_TONE_STATE_DIALTONE;
+ } else if (hz[HZ_950] > TONE_MIN_THRESH * TONE_THRESH) {
+ newstate = DSP_TONE_STATE_SPECIAL1;
+ } else if (hz[HZ_1400] > TONE_MIN_THRESH * TONE_THRESH) {
+ if (dsp->tstate == DSP_TONE_STATE_SPECIAL1)
+ newstate = DSP_TONE_STATE_SPECIAL2;
+ } else if (hz[HZ_1800] > TONE_MIN_THRESH * TONE_THRESH) {
+ if (dsp->tstate == DSP_TONE_STATE_SPECIAL2)
+ newstate = DSP_TONE_STATE_SPECIAL3;
+ } else if (dsp->genergy > TONE_MIN_THRESH * TONE_THRESH) {
+ newstate = DSP_TONE_STATE_TALKING;
+ } else
+ newstate = DSP_TONE_STATE_SILENCE;
+ break;
+ case PROG_MODE_CR:
+ if (hz[HZ_425] > TONE_MIN_THRESH * TONE_THRESH) {
+ newstate = DSP_TONE_STATE_RINGING;
+ } else if (dsp->genergy > TONE_MIN_THRESH * TONE_THRESH) {
+ newstate = DSP_TONE_STATE_TALKING;
+ } else
+ newstate = DSP_TONE_STATE_SILENCE;
+ break;
+ case PROG_MODE_UK:
+ if (hz[HZ_400] > TONE_MIN_THRESH * TONE_THRESH) {
+ newstate = DSP_TONE_STATE_HUNGUP;
+ }
+ break;
+ default:
+ ast_log(LOG_WARNING, "Can't process in unknown prog mode '%d'\n", dsp->progmode);
+ }
+ if (newstate == dsp->tstate) {
+ dsp->tcount++;
+ if (dsp->ringtimeout)
+ dsp->ringtimeout++;
+ switch (dsp->tstate) {
+ case DSP_TONE_STATE_RINGING:
+ if ((dsp->features & DSP_PROGRESS_RINGING) &&
+ (dsp->tcount==THRESH_RING)) {
+ res = AST_CONTROL_RINGING;
+ dsp->ringtimeout= 1;
+ }
+ break;
+ case DSP_TONE_STATE_BUSY:
+ if ((dsp->features & DSP_PROGRESS_BUSY) &&
+ (dsp->tcount==THRESH_BUSY)) {
+ res = AST_CONTROL_BUSY;
+ dsp->features &= ~DSP_FEATURE_CALL_PROGRESS;
+ }
+ break;
+ case DSP_TONE_STATE_TALKING:
+ if ((dsp->features & DSP_PROGRESS_TALK) &&
+ (dsp->tcount==THRESH_TALK)) {
+ res = AST_CONTROL_ANSWER;
+ dsp->features &= ~DSP_FEATURE_CALL_PROGRESS;
+ }
+ break;
+ case DSP_TONE_STATE_SPECIAL3:
+ if ((dsp->features & DSP_PROGRESS_CONGESTION) &&
+ (dsp->tcount==THRESH_CONGESTION)) {
+ res = AST_CONTROL_CONGESTION;
+ dsp->features &= ~DSP_FEATURE_CALL_PROGRESS;
+ }
+ break;
+ case DSP_TONE_STATE_HUNGUP:
+ if ((dsp->features & DSP_FEATURE_CALL_PROGRESS) &&
+ (dsp->tcount==THRESH_HANGUP)) {
+ res = AST_CONTROL_HANGUP;
+ dsp->features &= ~DSP_FEATURE_CALL_PROGRESS;
+ }
+ break;
+ }
+ if (dsp->ringtimeout==THRESH_RING2ANSWER) {
+ ast_debug(1, "Consider call as answered because of timeout after last ring\n");
+ res = AST_CONTROL_ANSWER;
+ dsp->features &= ~DSP_FEATURE_CALL_PROGRESS;
+ }
+ } else {
+ ast_debug(5, "Stop state %d with duration %d\n", dsp->tstate, dsp->tcount);
+ ast_debug(5, "Start state %d\n", newstate);
+ dsp->tstate = newstate;
+ dsp->tcount = 1;
+ }
+
+ /* Reset goertzel */
+ for (x=0;x<7;x++)
+ dsp->freqs[x].v2 = dsp->freqs[x].v3 = 0.0;
+ dsp->gsamps = 0;
+ dsp->genergy = 0.0;
+ }
+ }
+
+ return res;
+}
+
+int ast_dsp_call_progress(struct ast_dsp *dsp, struct ast_frame *inf)
+{
+ if (inf->frametype != AST_FRAME_VOICE) {
+ ast_log(LOG_WARNING, "Can't check call progress of non-voice frames\n");
+ return 0;
+ }
+ if (inf->subclass != AST_FORMAT_SLINEAR) {
+ ast_log(LOG_WARNING, "Can only check call progress in signed-linear frames\n");
+ return 0;
+ }
+ return __ast_dsp_call_progress(dsp, inf->data, inf->datalen / 2);
+}
+
+static int __ast_dsp_silence(struct ast_dsp *dsp, short *s, int len, int *totalsilence)
+{
+ int accum;
+ int x;
+ int res = 0;
+
+ if (!len)
+ return 0;
+ accum = 0;
+ for (x=0;x<len; x++)
+ accum += abs(s[x]);
+ accum /= len;
+ if (accum < dsp->threshold) {
+ /* Silent */
+ dsp->totalsilence += len/8;
+ if (dsp->totalnoise) {
+ /* Move and save history */
+ memmove(dsp->historicnoise + DSP_HISTORY - dsp->busycount, dsp->historicnoise + DSP_HISTORY - dsp->busycount +1, dsp->busycount*sizeof(dsp->historicnoise[0]));
+ dsp->historicnoise[DSP_HISTORY - 1] = dsp->totalnoise;
+/* we don't want to check for busydetect that frequently */
+#if 0
+ dsp->busymaybe = 1;
+#endif
+ }
+ dsp->totalnoise = 0;
+ res = 1;
+ } else {
+ /* Not silent */
+ dsp->totalnoise += len/8;
+ if (dsp->totalsilence) {
+ int silence1 = dsp->historicsilence[DSP_HISTORY - 1];
+ int silence2 = dsp->historicsilence[DSP_HISTORY - 2];
+ /* Move and save history */
+ memmove(dsp->historicsilence + DSP_HISTORY - dsp->busycount, dsp->historicsilence + DSP_HISTORY - dsp->busycount + 1, dsp->busycount*sizeof(dsp->historicsilence[0]));
+ dsp->historicsilence[DSP_HISTORY - 1] = dsp->totalsilence;
+ /* check if the previous sample differs only by BUSY_PERCENT from the one before it */
+ if (silence1 < silence2) {
+ if (silence1 + silence1*BUSY_PERCENT/100 >= silence2)
+ dsp->busymaybe = 1;
+ else
+ dsp->busymaybe = 0;
+ } else {
+ if (silence1 - silence1*BUSY_PERCENT/100 <= silence2)
+ dsp->busymaybe = 1;
+ else
+ dsp->busymaybe = 0;
+ }
+ }
+ dsp->totalsilence = 0;
+ }
+ if (totalsilence)
+ *totalsilence = dsp->totalsilence;
+ return res;
+}
+
+int ast_dsp_busydetect(struct ast_dsp *dsp)
+{
+ int res = 0, x;
+#ifndef BUSYDETECT_TONEONLY
+ int avgsilence = 0, hitsilence = 0;
+#endif
+ int avgtone = 0, hittone = 0;
+ if (!dsp->busymaybe)
+ return res;
+ for (x=DSP_HISTORY - dsp->busycount;x<DSP_HISTORY;x++) {
+#ifndef BUSYDETECT_TONEONLY
+ avgsilence += dsp->historicsilence[x];
+#endif
+ avgtone += dsp->historicnoise[x];
+ }
+#ifndef BUSYDETECT_TONEONLY
+ avgsilence /= dsp->busycount;
+#endif
+ avgtone /= dsp->busycount;
+ for (x=DSP_HISTORY - dsp->busycount;x<DSP_HISTORY;x++) {
+#ifndef BUSYDETECT_TONEONLY
+ if (avgsilence > dsp->historicsilence[x]) {
+ if (avgsilence - (avgsilence*BUSY_PERCENT/100) <= dsp->historicsilence[x])
+ hitsilence++;
+ } else {
+ if (avgsilence + (avgsilence*BUSY_PERCENT/100) >= dsp->historicsilence[x])
+ hitsilence++;
+ }
+#endif
+ if (avgtone > dsp->historicnoise[x]) {
+ if (avgtone - (avgtone*BUSY_PERCENT/100) <= dsp->historicnoise[x])
+ hittone++;
+ } else {
+ if (avgtone + (avgtone*BUSY_PERCENT/100) >= dsp->historicnoise[x])
+ hittone++;
+ }
+ }
+#ifndef BUSYDETECT_TONEONLY
+ if ((hittone >= dsp->busycount - 1) && (hitsilence >= dsp->busycount - 1) &&
+ (avgtone >= BUSY_MIN && avgtone <= BUSY_MAX) &&
+ (avgsilence >= BUSY_MIN && avgsilence <= BUSY_MAX)) {
+#else
+ if ((hittone >= dsp->busycount - 1) && (avgtone >= BUSY_MIN && avgtone <= BUSY_MAX)) {
+#endif
+#ifdef BUSYDETECT_COMPARE_TONE_AND_SILENCE
+ if (avgtone > avgsilence) {
+ if (avgtone - avgtone*BUSY_PERCENT/100 <= avgsilence)
+ res = 1;
+ } else {
+ if (avgtone + avgtone*BUSY_PERCENT/100 >= avgsilence)
+ res = 1;
+ }
+#else
+ res = 1;
+#endif
+ }
+ /* If we know the expected busy tone length, check we are in the range */
+ if (res && (dsp->busy_tonelength > 0)) {
+ if (abs(avgtone - dsp->busy_tonelength) > (dsp->busy_tonelength*BUSY_PAT_PERCENT/100)) {
+#ifdef BUSYDETECT_DEBUG
+ ast_debug(5, "busy detector: avgtone of %d not close enough to desired %d\n",
+ avgtone, dsp->busy_tonelength);
+#endif
+ res = 0;
+ }
+ }
+#ifndef BUSYDETECT_TONEONLY
+ /* If we know the expected busy tone silent-period length, check we are in the range */
+ if (res && (dsp->busy_quietlength > 0)) {
+ if (abs(avgsilence - dsp->busy_quietlength) > (dsp->busy_quietlength*BUSY_PAT_PERCENT/100)) {
+#ifdef BUSYDETECT_DEBUG
+ ast_debug(5, "busy detector: avgsilence of %d not close enough to desired %d\n",
+ avgsilence, dsp->busy_quietlength);
+#endif
+ res = 0;
+ }
+ }
+#endif
+#if !defined(BUSYDETECT_TONEONLY) && defined(BUSYDETECT_DEBUG)
+ if (res) {
+ ast_debug(5, "ast_dsp_busydetect detected busy, avgtone: %d, avgsilence %d\n", avgtone, avgsilence);
+ } else {
+ ast_debug(5, "busy detector: FAILED with avgtone: %d, avgsilence %d\n", avgtone, avgsilence);
+ }
+#endif
+ return res;
+}
+
+int ast_dsp_silence(struct ast_dsp *dsp, struct ast_frame *f, int *totalsilence)
+{
+ short *s;
+ int len;
+
+ if (f->frametype != AST_FRAME_VOICE) {
+ ast_log(LOG_WARNING, "Can't calculate silence on a non-voice frame\n");
+ return 0;
+ }
+ if (f->subclass != AST_FORMAT_SLINEAR) {
+ ast_log(LOG_WARNING, "Can only calculate silence on signed-linear frames :(\n");
+ return 0;
+ }
+ s = f->data;
+ len = f->datalen/2;
+ return __ast_dsp_silence(dsp, s, len, totalsilence);
+}
+
+struct ast_frame *ast_dsp_process(struct ast_channel *chan, struct ast_dsp *dsp, struct ast_frame *af)
+{
+ int silence;
+ int res;
+ int digit;
+ int x;
+ short *shortdata;
+ unsigned char *odata;
+ int len;
+ int writeback = 0;
+
+#define FIX_INF(inf) do { \
+ if (writeback) { \
+ switch (inf->subclass) { \
+ case AST_FORMAT_SLINEAR: \
+ break; \
+ case AST_FORMAT_ULAW: \
+ for (x=0;x<len;x++) \
+ odata[x] = AST_LIN2MU((unsigned short)shortdata[x]); \
+ break; \
+ case AST_FORMAT_ALAW: \
+ for (x=0;x<len;x++) \
+ odata[x] = AST_LIN2A((unsigned short)shortdata[x]); \
+ break; \
+ } \
+ } \
+ } while(0)
+
+ if (!af)
+ return NULL;
+ if (af->frametype != AST_FRAME_VOICE)
+ return af;
+ odata = af->data;
+ len = af->datalen;
+ /* Make sure we have short data */
+ switch (af->subclass) {
+ case AST_FORMAT_SLINEAR:
+ shortdata = af->data;
+ len = af->datalen / 2;
+ break;
+ case AST_FORMAT_ULAW:
+ shortdata = alloca(af->datalen * 2);
+ for (x = 0;x < len; x++)
+ shortdata[x] = AST_MULAW(odata[x]);
+ break;
+ case AST_FORMAT_ALAW:
+ shortdata = alloca(af->datalen * 2);
+ for (x = 0; x < len; x++)
+ shortdata[x] = AST_ALAW(odata[x]);
+ break;
+ default:
+ ast_log(LOG_WARNING, "Inband DTMF is not supported on codec %s. Use RFC2833\n", ast_getformatname(af->subclass));
+ return af;
+ }
+ silence = __ast_dsp_silence(dsp, shortdata, len, NULL);
+ if ((dsp->features & DSP_FEATURE_SILENCE_SUPPRESS) && silence) {
+ memset(&dsp->f, 0, sizeof(dsp->f));
+ dsp->f.frametype = AST_FRAME_NULL;
+ ast_frfree(af);
+ return &dsp->f;
+ }
+ if ((dsp->features & DSP_FEATURE_BUSY_DETECT) && ast_dsp_busydetect(dsp)) {
+ chan->_softhangup |= AST_SOFTHANGUP_DEV;
+ memset(&dsp->f, 0, sizeof(dsp->f));
+ dsp->f.frametype = AST_FRAME_CONTROL;
+ dsp->f.subclass = AST_CONTROL_BUSY;
+ ast_frfree(af);
+ ast_debug(1, "Requesting Hangup because the busy tone was detected on channel %s\n", chan->name);
+ return &dsp->f;
+ }
+ if ((dsp->features & DSP_FEATURE_DTMF_DETECT)) {
+ digit = __ast_dsp_digitdetect(dsp, shortdata, len, &writeback);
+#if 0
+ if (digit)
+ printf("Performing digit detection returned %d, digitmode is %d\n", digit, dsp->digitmode);
+#endif
+ if (dsp->digitmode & (DSP_DIGITMODE_MUTECONF | DSP_DIGITMODE_MUTEMAX)) {
+ if (!dsp->thinkdigit) {
+ if (digit) {
+ /* Looks like we might have something.
+ * Request a conference mute for the moment */
+ memset(&dsp->f, 0, sizeof(dsp->f));
+ dsp->f.frametype = AST_FRAME_DTMF;
+ dsp->f.subclass = 'm';
+ dsp->thinkdigit = 'x';
+ FIX_INF(af);
+ if (chan)
+ ast_queue_frame(chan, af);
+ ast_frfree(af);
+ return &dsp->f;
+ }
+ } else {
+ if (digit) {
+ /* Thought we saw one last time. Pretty sure we really have now */
+ if ((dsp->thinkdigit != 'x') && (dsp->thinkdigit != digit)) {
+ /* If we found a digit, and we're changing digits, go
+ ahead and send this one, but DON'T stop confmute because
+ we're detecting something else, too... */
+ memset(&dsp->f, 0, sizeof(dsp->f));
+ dsp->f.frametype = AST_FRAME_DTMF_END;
+ dsp->f.subclass = dsp->thinkdigit;
+ FIX_INF(af);
+ if (chan)
+ ast_queue_frame(chan, af);
+ ast_frfree(af);
+ } else {
+ dsp->thinkdigit = digit;
+ memset(&dsp->f, 0, sizeof(dsp->f));
+ dsp->f.frametype = AST_FRAME_DTMF_BEGIN;
+ dsp->f.subclass = dsp->thinkdigit;
+ FIX_INF(af);
+ if (chan)
+ ast_queue_frame(chan, af);
+ ast_frfree(af);
+ }
+ return &dsp->f;
+ } else {
+ memset(&dsp->f, 0, sizeof(dsp->f));
+ if (dsp->thinkdigit != 'x') {
+ /* If we found a digit, send it now */
+ dsp->f.frametype = AST_FRAME_DTMF_END;
+ dsp->f.subclass = dsp->thinkdigit;
+ dsp->thinkdigit = 0;
+ } else {
+ dsp->f.frametype = AST_FRAME_DTMF;
+ dsp->f.subclass = 'u';
+ dsp->thinkdigit = 0;
+ }
+ FIX_INF(af);
+ if (chan)
+ ast_queue_frame(chan, af);
+ ast_frfree(af);
+ return &dsp->f;
+ }
+ }
+ } else if (!digit) {
+ /* Only check when there is *not* a hit... */
+ if (dsp->digitmode & DSP_DIGITMODE_MF) {
+ if (dsp->td.mf.current_digits) {
+ memset(&dsp->f, 0, sizeof(dsp->f));
+ dsp->f.frametype = AST_FRAME_DTMF;
+ dsp->f.subclass = dsp->td.mf.digits[0];
+ memmove(dsp->td.mf.digits, dsp->td.mf.digits + 1, dsp->td.mf.current_digits);
+ dsp->td.mf.current_digits--;
+ FIX_INF(af);
+ if (chan)
+ ast_queue_frame(chan, af);
+ ast_frfree(af);
+ return &dsp->f;
+ }
+ } else {
+ if (dsp->td.dtmf.current_digits) {
+ memset(&dsp->f, 0, sizeof(dsp->f));
+ dsp->f.frametype = AST_FRAME_DTMF_END;
+ dsp->f.subclass = dsp->td.dtmf.digits[0];
+ memmove(dsp->td.dtmf.digits, dsp->td.dtmf.digits + 1, dsp->td.dtmf.current_digits);
+ dsp->td.dtmf.current_digits--;
+ FIX_INF(af);
+ if (chan)
+ ast_queue_frame(chan, af);
+ ast_frfree(af);
+ return &dsp->f;
+ }
+ }
+ }
+ }
+ if ((dsp->features & DSP_FEATURE_CALL_PROGRESS)) {
+ res = __ast_dsp_call_progress(dsp, shortdata, len);
+ if (res) {
+ switch (res) {
+ case AST_CONTROL_ANSWER:
+ case AST_CONTROL_BUSY:
+ case AST_CONTROL_RINGING:
+ case AST_CONTROL_CONGESTION:
+ case AST_CONTROL_HANGUP:
+ memset(&dsp->f, 0, sizeof(dsp->f));
+ dsp->f.frametype = AST_FRAME_CONTROL;
+ dsp->f.subclass = res;
+ dsp->f.src = "dsp_progress";
+ if (chan)
+ ast_queue_frame(chan, &dsp->f);
+ break;
+ default:
+ ast_log(LOG_WARNING, "Don't know how to represent call progress message %d\n", res);
+ }
+ }
+ }
+ FIX_INF(af);
+ return af;
+}
+
+static void ast_dsp_prog_reset(struct ast_dsp *dsp)
+{
+ int max = 0;
+ int x;
+
+ dsp->gsamp_size = modes[dsp->progmode].size;
+ dsp->gsamps = 0;
+ for (x = 0; x < sizeof(modes[dsp->progmode].freqs) / sizeof(modes[dsp->progmode].freqs[0]); x++) {
+ if (modes[dsp->progmode].freqs[x]) {
+ goertzel_init(&dsp->freqs[x], (float)modes[dsp->progmode].freqs[x], dsp->gsamp_size);
+ max = x + 1;
+ }
+ }
+ dsp->freqcount = max;
+ dsp->ringtimeout= 0;
+}
+
+struct ast_dsp *ast_dsp_new(void)
+{
+ struct ast_dsp *dsp;
+
+ if ((dsp = ast_calloc(1, sizeof(*dsp)))) {
+ dsp->threshold = DEFAULT_THRESHOLD;
+ dsp->features = DSP_FEATURE_SILENCE_SUPPRESS;
+ dsp->busycount = DSP_HISTORY;
+ /* Initialize DTMF detector */
+ ast_dtmf_detect_init(&dsp->td.dtmf);
+ /* Initialize initial DSP progress detect parameters */
+ ast_dsp_prog_reset(dsp);
+ }
+ return dsp;
+}
+
+void ast_dsp_set_features(struct ast_dsp *dsp, int features)
+{
+ dsp->features = features;
+}
+
+void ast_dsp_free(struct ast_dsp *dsp)
+{
+ ast_free(dsp);
+}
+
+void ast_dsp_set_threshold(struct ast_dsp *dsp, int threshold)
+{
+ dsp->threshold = threshold;
+}
+
+void ast_dsp_set_busy_count(struct ast_dsp *dsp, int cadences)
+{
+ if (cadences < 4)
+ cadences = 4;
+ if (cadences > DSP_HISTORY)
+ cadences = DSP_HISTORY;
+ dsp->busycount = cadences;
+}
+
+void ast_dsp_set_busy_pattern(struct ast_dsp *dsp, int tonelength, int quietlength)
+{
+ dsp->busy_tonelength = tonelength;
+ dsp->busy_quietlength = quietlength;
+ ast_debug(1, "dsp busy pattern set to %d,%d\n", tonelength, quietlength);
+}
+
+void ast_dsp_digitreset(struct ast_dsp *dsp)
+{
+ int i;
+
+ dsp->thinkdigit = 0;
+ if (dsp->digitmode & DSP_DIGITMODE_MF) {
+ memset(dsp->td.mf.digits, 0, sizeof(dsp->td.mf.digits));
+ dsp->td.mf.current_digits = 0;
+ /* Reinitialise the detector for the next block */
+ for (i = 0; i < 6; i++) {
+ goertzel_reset(&dsp->td.mf.tone_out[i]);
+ }
+ dsp->td.mf.hits[4] = dsp->td.mf.hits[3] = dsp->td.mf.hits[2] = dsp->td.mf.hits[1] = dsp->td.mf.hits[0] = dsp->td.mf.mhit = 0;
+ dsp->td.mf.current_sample = 0;
+ } else {
+ memset(dsp->td.dtmf.digits, 0, sizeof(dsp->td.dtmf.digits));
+ dsp->td.dtmf.current_digits = 0;
+ /* Reinitialise the detector for the next block */
+ for (i = 0; i < 4; i++) {
+ goertzel_reset(&dsp->td.dtmf.row_out[i]);
+ goertzel_reset(&dsp->td.dtmf.col_out[i]);
+ }
+ goertzel_reset (&dsp->td.dtmf.fax_tone);
+ dsp->td.dtmf.lasthit = dsp->td.dtmf.mhit = 0;
+ dsp->td.dtmf.energy = 0.0;
+ dsp->td.dtmf.current_sample = 0;
+ }
+}
+
+void ast_dsp_reset(struct ast_dsp *dsp)
+{
+ int x;
+
+ dsp->totalsilence = 0;
+ dsp->gsamps = 0;
+ for (x=0;x<4;x++)
+ dsp->freqs[x].v2 = dsp->freqs[x].v3 = 0.0;
+ memset(dsp->historicsilence, 0, sizeof(dsp->historicsilence));
+ memset(dsp->historicnoise, 0, sizeof(dsp->historicnoise));
+ dsp->ringtimeout= 0;
+}
+
+int ast_dsp_digitmode(struct ast_dsp *dsp, int digitmode)
+{
+ int new;
+ int old;
+
+ old = dsp->digitmode & (DSP_DIGITMODE_DTMF | DSP_DIGITMODE_MF | DSP_DIGITMODE_MUTECONF | DSP_DIGITMODE_MUTEMAX);
+ new = digitmode & (DSP_DIGITMODE_DTMF | DSP_DIGITMODE_MF | DSP_DIGITMODE_MUTECONF | DSP_DIGITMODE_MUTEMAX);
+ if (old != new) {
+ /* Must initialize structures if switching from MF to DTMF or vice-versa */
+ if (new & DSP_DIGITMODE_MF)
+ ast_mf_detect_init(&dsp->td.mf);
+ else
+ ast_dtmf_detect_init(&dsp->td.dtmf);
+ }
+ dsp->digitmode = digitmode;
+ return 0;
+}
+
+int ast_dsp_set_call_progress_zone(struct ast_dsp *dsp, char *zone)
+{
+ int x;
+
+ for (x = 0; x < ARRAY_LEN(aliases); x++) {
+ if (!strcasecmp(aliases[x].name, zone)) {
+ dsp->progmode = aliases[x].mode;
+ ast_dsp_prog_reset(dsp);
+ return 0;
+ }
+ }
+ return -1;
+}
+
+int ast_dsp_get_tstate(struct ast_dsp *dsp)
+{
+ return dsp->tstate;
+}
+
+int ast_dsp_get_tcount(struct ast_dsp *dsp)
+{
+ return dsp->tcount;
+}
diff --git a/trunk/main/ecdisa.h b/trunk/main/ecdisa.h
new file mode 100644
index 000000000..df6f773a0
--- /dev/null
+++ b/trunk/main/ecdisa.h
@@ -0,0 +1,15 @@
+/* ecdisa.h: Generated from frequency 2100
+ by gentone. 80 samples */
+static unsigned char ecdisa[80] = {
+ 255, 143, 58, 16, 171, 146, 34, 20,
+ 156, 151, 25, 26, 149, 159, 19, 38,
+ 145, 177, 16, 73, 143, 73, 16, 177,
+ 145, 38, 19, 159, 149, 26, 25, 151,
+ 156, 20, 34, 146, 171, 16, 58, 143,
+ 255, 15, 186, 144, 43, 18, 162, 148,
+ 28, 23, 153, 154, 21, 31, 147, 166,
+ 17, 49, 144, 201, 15, 201, 144, 49,
+ 17, 166, 147, 31, 21, 154, 153, 23,
+ 28, 148, 162, 18, 43, 144, 186, 15,
+
+};
diff --git a/trunk/main/editline/CHANGES b/trunk/main/editline/CHANGES
new file mode 100644
index 000000000..c18b56cdf
--- /dev/null
+++ b/trunk/main/editline/CHANGES
@@ -0,0 +1,42 @@
+2002-02-25 : Christos Zoulas <christos@netbsd.org>
+ * Bring in constification fixes from NetBSD tree.
+ * Use NetBSD's vis, fgetln
+
+2002-02-10 : Jason Evans <jasone@freebsd.org>
+
+ * Makefile.in : Append "" arguments to for loops, to avoid syntax errors
+ with some shells that occur if there are no arguments to the for
+ loops.
+
+2002-02-09 : Jason Evans <jasone@freebsd.org>
+
+ * Install man pages in @prefix@/man/, rather than @prefix@/share/man.
+
+ * Fix the Darwin -install_name S_LDFLAGS argument to default to a prefix
+ of /usr/local if --prefix is not specified.
+
+2002-02-05 : Jason Evans <jasone@freebsd.org>
+
+ * Convert to using an autoconf-generated config.h, rather than passing
+ -D_HAVE_<foo>=1 definitions on the command line. Include sys.h in
+ config.h, and include config.h in .c files rather than sys.h.
+
+ * Mangle function names for implementations in the np directory in order
+ to avoid namespace collisions with other code that may provide copies
+ of the same unimplemented functions. For example:
+
+ #define fgetln libedit_fgetln
+
+2002-02-03 : Jason Evans <jasone@freebsd.org>
+
+ * Add autoconf infrastructure, plus a generic Makefile that works with
+ at least BSD make, GNU make and Sun make.
+
+ * Port and/or test on FreeBSD 4.5, FreeBSD-current, NetBSD 1.5 (sparc64
+ and arm32), Apple OS X 10.1.2, Solaris 2.6, and Red Hat Linux 2.6.
+ Add the np directory, which contains implementations of non-portable
+ functions.
+
+ * Add the LIBEDIT_MAJOR and LIBEDIT_MINOR macros to histedit.h, since
+ there is otherwise no straightforward method of programmatically
+ detecting the library version.
diff --git a/trunk/main/editline/INSTALL b/trunk/main/editline/INSTALL
new file mode 100644
index 000000000..16fb6ffd1
--- /dev/null
+++ b/trunk/main/editline/INSTALL
@@ -0,0 +1,64 @@
+Building this distribution in many cases is as simple as typing the following
+while in the root directory of the source tree:
+
+ ./configure
+ make
+
+To install, do the above, then type:
+
+ make install
+
+Additional build targets of finer granularity include:
+
+ lib_a
+ lib_s
+ install_hdr
+ install_lib
+ install_lib_a
+ install_lib_s
+ install_man
+ test
+
+Cleanup targets include:
+
+ clean
+ distclean
+
+Optionally, pass any of the following (not a definitive list) arguments to
+'configure':
+
+--prefix=<install-root-dir>
+ Set the base directory in which to install. For example:
+
+ ./configure --prefix=/usr/local
+
+ will cause files to be installed into /usr/local/bin, /usr/local/man,
+ /usr/local/include, /usr/local/lib, and /usr/local/share.
+
+--disable-readline
+ By default, libedit is built and installed such that it works as a
+ drop-in replacement for the readline library. This option turns that
+ behavior off.
+
+--enable-debug
+ Build debugging code (for libedit development).
+
+Optionally, define environment variables, including (not exclusively):
+
+CFLAGS="?"
+ Pass these flags to the compiler. You probably shouldn't define this
+ unless you know what you are doing.
+
+CPPFLAGS="?"
+ Pass these flags to the C preprocessor. Note that CFLAGS is not passed
+ to 'cpp' when 'configure' is looking for include files, so you must use
+ CPPFLAGS instead if you need to help 'configure' find header files.
+
+LD_LIBRARY_PATH="?"
+ 'ld' uses this colon-separated list to find libraries.
+
+LDFLAGS="?"
+ Flags passed to 'gcc', which should normally be passed on to 'ld'.
+
+PATH="?"
+ 'configure' uses this to find programs.
diff --git a/trunk/main/editline/Makefile.in b/trunk/main/editline/Makefile.in
new file mode 100644
index 000000000..f17cfd43a
--- /dev/null
+++ b/trunk/main/editline/Makefile.in
@@ -0,0 +1,234 @@
+#
+# Generic Makefile for libedit.
+#
+
+OSTYPE=$(shell uname -s)
+define cyg_subst_sys
+ if uname -s | grep -qi cygwin; then \
+ cat $@ | sed -e s/"sys\.h"/"config.h"/g > $@.copy; \
+ mv --force $@.copy $@; \
+ fi
+endef
+
+SHELL = /bin/sh
+
+CC = @CC@
+AR = @AR@
+RANLIB = @RANLIB@
+CPPFLAGS = @CPPFLAGS@ -I.
+CFLAGS = @CFLAGS@
+A_CFLAGS = @A_CFLAGS@
+S_CFLAGS = @S_CFLAGS@
+LDFLAGS = @LDFLAGS@
+S_LDFLAGS = @S_LDFLAGS@
+LIBS = @LIBS@
+
+INSTALL = @INSTALL@
+PREFIX = @prefix@
+
+ifeq ($(OSTYPE),SunOS)
+CFLAGS+=-DSOLARIS -I../../include/solaris-compat
+endif
+
+# .c files.
+ACSRCS = @ACSRCS@
+BCSRCS = @BCSRCS@
+CCSRCS = @CCSRCS@
+
+# Generated .c files.
+AGCSRCS = @AGCSRCS@
+BGCSRCS = @BGCSRCS@
+
+# .h files.
+HDRS = @HDRS@
+
+# Generated .h files.
+AGHDRS = @AGHDRS@
+BGHDRS = @BGHDRS@
+
+# Installed .h files.
+IHDRS = @IHDRS@
+IHDR_LINKS = @IHDR_LINKS@
+HDR_DIRS = @HDR_DIRS@
+
+# Man pages.
+MAN3 = @MAN3@
+MAN5 = @MAN5@
+MAN3_LINKS = @MAN3_LINKS@
+MAN_DIRS = @MAN_DIRS@
+
+# Library.
+LIB_DIRS = @LIB_DIRS@
+LIB_VER = @LIB_VER@
+LIB_A = @LIB_A@
+LIB_A_LINKS = @LIB_A_LINKS@
+LIB_S = @LIB_S@
+LIB_S_LINKS = @LIB_S_LINKS@
+
+# Test program.
+TEST = @TEST@
+TCSRCS = @TCSRCS@
+
+# Clear out all paths, then set just one (default path) for the main build
+# directory.
+.PATH :
+.PATH : .
+
+.SUFFIXES :
+.SUFFIXES : .c .o .o_a .o_s
+
+all : lib_a lib_s
+
+lib_a : $(LIB_A)
+lib_s : $(LIB_S)
+
+test : $(TEST)
+
+install : install_hdr install_lib install_man
+
+install_hdr :
+ @for i in $(HDR_DIRS) ; do \
+ echo "$(INSTALL) -d $(PREFIX)/$$i/"; \
+ $(INSTALL) -d $(PREFIX)/$$i/; \
+ done
+ @for i in $(IHDRS); do \
+ echo "$(INSTALL) -m 0444 $$i $(PREFIX)/include/`dirname $$i`/"; \
+ $(INSTALL) -m 0444 $$i $(PREFIX)/include/`dirname $$i`/; \
+ done
+ @f=; \
+ for i in $(IHDR_LINKS) ""; do \
+ if test -z "$$f" ; then \
+ f=$$i; \
+ else \
+ echo "rm -f $(PREFIX)/include/$$i"; \
+ rm -f $(PREFIX)/include/$$i; \
+ echo "ln -s $$f $(PREFIX)/include/$$i"; \
+ ln -s $$f $(PREFIX)/include/$$i; \
+ f=; \
+ fi; \
+ done
+
+install_lib : install_lib_a install_lib_s
+
+install_lib_common :
+ @for i in $(LIB_DIRS) ; do \
+ echo "$(INSTALL) -d $(PREFIX)/$$i/"; \
+ $(INSTALL) -d $(PREFIX)/$$i/; \
+ done
+
+install_lib_a : $(LIB_A) install_lib_common
+ $(INSTALL) -m 0644 $(LIB_A) $(PREFIX)/lib/
+ @f=; \
+ for i in $(LIB_A_LINKS) ""; do \
+ if test -z "$$f" ; then \
+ f=$$i; \
+ else \
+ echo "rm -f $(PREFIX)/lib/$$i"; \
+ rm -f $(PREFIX)/lib/$$i; \
+ echo "ln -s $$f $(PREFIX)/lib/$$i"; \
+ ln -s $$f $(PREFIX)/lib/$$i; \
+ f=; \
+ fi; \
+ done
+
+install_lib_s : $(LIB_S) install_lib_common
+ $(INSTALL) -m 0755 $(LIB_S) $(PREFIX)/lib/
+ @f=; \
+ for i in $(LIB_S_LINKS) ""; do \
+ if test -z "$$f" ; then \
+ f=$$i; \
+ else \
+ echo "rm -f $(PREFIX)/lib/$$i"; \
+ rm -f $(PREFIX)/lib/$$i; \
+ echo "ln -s $$f $(PREFIX)/lib/$$i"; \
+ ln -s $$f $(PREFIX)/lib/$$i; \
+ f=; \
+ fi; \
+ done
+
+install_man :
+ @for i in $(MAN_DIRS) ; do \
+ echo "$(INSTALL) -d $(PREFIX)/$$i/"; \
+ $(INSTALL) -d $(PREFIX)/$$i/; \
+ done
+ @for i in $(MAN3); do \
+ echo $(INSTALL) -m 0444 $$i $(PREFIX)/man/man3/; \
+ $(INSTALL) -m 0444 $$i $(PREFIX)/man/man3/; \
+ done
+ @f=; \
+ for i in $(MAN3_LINKS) ""; do \
+ if test -z "$$f" ; then \
+ f=$$i; \
+ else \
+ echo "rm -f $(PREFIX)/man/man3/$$i"; \
+ rm -f $(PREFIX)/man/man3/$$i; \
+ echo "ln -s $$f $(PREFIX)/man/man3/$$i"; \
+ ln -s $$f $(PREFIX)/man/man3/$$i; \
+ f=; \
+ fi; \
+ done
+ @for i in $(MAN5); do\
+ echo $(INSTALL) -m 0444 $$i $(PREFIX)/man/man5/; \
+ $(INSTALL) -m 0444 $$i $(PREFIX)/man/man5/; \
+ done
+
+clean :
+ rm -f $(AGCSRCS) $(BGCSRCS) $(AGHDRS) $(BGHDRS) $(LIB_A) $(LIB_S)
+ rm -f $(BGCSRCS:.c=.o_a) $(CCSRCS:.c=.o_a)
+ rm -f $(BGCSRCS:.c=.o_s) $(CCSRCS:.c=.o_s)
+ rm -f $(TCSRCS:.c=.o) $(TEST)
+ rm -f *.s *.i
+
+distclean : clean
+ rm -f config.cache config.log config.status config.h Makefile
+
+#
+# Internal targets and rules.
+#
+
+$(LIB_A) : $(BGCSRCS:.c=.o_a) $(CCSRCS:.c=.o_a)
+ $(AR) cru $@ $?
+ $(RANLIB) $@
+
+$(LIB_S) : $(BGCSRCS:.c=.o_s) $(CCSRCS:.c=.o_s)
+ $(CC) $(S_LDFLAGS) -o $@ $(BGCSRCS:.c=.o_s) $(CCSRCS:.c=.o_s) $(LIBS)
+
+$(TEST) : $(TCSRCS:.c=.o) $(LIB_A)
+ $(CC) -o $@ $(TCSRCS:.c=.o) $(LIB_A) $(LIBS)
+
+common.h : common.c
+ $(SHELL) makelist -h common.c > $@
+
+emacs.h : emacs.c
+ $(SHELL) makelist -h emacs.c> $@
+
+vi.h : vi.c
+ $(SHELL) makelist -h vi.c > $@
+
+fcns.h : $(AGHDRS)
+ $(SHELL) makelist -fh $(AGHDRS) > $@
+
+fcns.c : $(AGHDRS) fcns.h
+ $(SHELL) makelist -fc $(AGHDRS) > $@
+ $(cyg_subst_sys)
+
+help.h : $(ACSRCS)
+ $(SHELL) makelist -bh $(ACSRCS) > $@
+
+help.c : $(ACSRCS) help.h
+ $(SHELL) makelist -bc $(ACSRCS) > $@
+ $(cyg_subst_sys)
+
+editline.c : $(ACSRCS) $(BCSRCS) $(AGCSRCS)
+ $(SHELL) makelist -e $(ACSRCS) $(BCSRCS) $(AGCSRCS) > $@
+
+.c.o :
+ $(CC) -c $(A_CFLAGS) $(CFLAGS) $(CPPFLAGS) $< -o $@
+
+.c.o_a : $(AGHDRS) $(BGHDRS)
+ $(CC) -c $(A_CFLAGS) $(CFLAGS) $(CPPFLAGS) $< -o $@
+
+.c.o_s : $(AGHDRS) $(BGHDRS)
+ $(CC) -c $(S_CFLAGS) $(CFLAGS) $(CPPFLAGS) $< -o $@
+
+$(CCSRCS) : $(BGHDRS)
diff --git a/trunk/main/editline/PLATFORMS b/trunk/main/editline/PLATFORMS
new file mode 100644
index 000000000..ea7c5bb68
--- /dev/null
+++ b/trunk/main/editline/PLATFORMS
@@ -0,0 +1,13 @@
+This distribution of libedit is expected to work on at least the following
+platforms. It may also work on additional platforms, but no explicit support
+for them is built into the configuration system.
+
+* Apple OS X 10.1.
+
+* FreeBSD 4.x.
+
+* NetBSD 1.5.
+
+* Red Hat Linux 7.2.
+
+* Sun Solaris 2.6.
diff --git a/trunk/main/editline/README b/trunk/main/editline/README
new file mode 100644
index 000000000..49a2a6947
--- /dev/null
+++ b/trunk/main/editline/README
@@ -0,0 +1,11 @@
+libedit is a command line editing and history library. It is designed to be
+used by interactive programs that allow the user to type commands at a terminal
+prompt.
+
+The following files may be of direct interest to the user:
+
+* CHANGES - Software change log.
+
+* INSTALL - Installation information.
+
+* PLATFORMS - Supported platforms and platform-specific information.
diff --git a/trunk/main/editline/TEST/test.c b/trunk/main/editline/TEST/test.c
new file mode 100644
index 000000000..3169a2071
--- /dev/null
+++ b/trunk/main/editline/TEST/test.c
@@ -0,0 +1,268 @@
+/* $NetBSD: test.c,v 1.9 2000/09/04 23:36:41 lukem Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+#ifndef lint
+__COPYRIGHT("@(#) Copyright (c) 1992, 1993\n\
+ The Regents of the University of California. All rights reserved.\n");
+#endif /* not lint */
+
+#if !defined(lint) && !defined(SCCSID)
+#if 0
+static char sccsid[] = "@(#)test.c 8.1 (Berkeley) 6/4/93";
+#else
+__RCSID("$NetBSD: test.c,v 1.9 2000/09/04 23:36:41 lukem Exp $");
+#endif
+#endif /* not lint && not SCCSID */
+
+/*
+ * test.c: A little test program
+ */
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <dirent.h>
+
+#include "histedit.h"
+#include "tokenizer.h"
+
+static int continuation = 0;
+static EditLine *el = NULL;
+
+static u_char complete(EditLine *, int);
+ int main(int, char **);
+static char *prompt(EditLine *);
+static void sig(int);
+
+static char *
+prompt(EditLine *el)
+{
+ static char a[] = "Edit$";
+ static char b[] = "Edit>";
+
+ return (continuation ? b : a);
+}
+
+static void
+sig(int i)
+{
+
+ (void) fprintf(stderr, "Got signal %d.\n", i);
+ el_reset(el);
+}
+
+static unsigned char
+complete(EditLine *el, int ch)
+{
+ DIR *dd = opendir(".");
+ struct dirent *dp;
+ const char* ptr;
+ const LineInfo *lf = el_line(el);
+ int len;
+
+ /*
+ * Find the last word
+ */
+ for (ptr = lf->cursor - 1; !isspace(*ptr) && ptr > lf->buffer; ptr--)
+ continue;
+ len = lf->cursor - ++ptr;
+
+ for (dp = readdir(dd); dp != NULL; dp = readdir(dd)) {
+ if (len > strlen(dp->d_name))
+ continue;
+ if (strncmp(dp->d_name, ptr, len) == 0) {
+ closedir(dd);
+ if (el_insertstr(el, &dp->d_name[len]) == -1)
+ return (CC_ERROR);
+ else
+ return (CC_REFRESH);
+ }
+ }
+
+ closedir(dd);
+ return (CC_ERROR);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int num;
+ const char *buf;
+ Tokenizer *tok;
+#if 0
+ int lastevent = 0;
+#endif
+ int ncontinuation;
+ History *hist;
+ HistEvent ev;
+
+ (void) signal(SIGINT, sig);
+ (void) signal(SIGQUIT, sig);
+ (void) signal(SIGHUP, sig);
+ (void) signal(SIGTERM, sig);
+
+ hist = history_init(); /* Init the builtin history */
+ /* Remember 100 events */
+ history(hist, &ev, H_SETSIZE, 100);
+
+ tok = tok_init(NULL); /* Initialize the tokenizer */
+
+ /* Initialize editline */
+ el = el_init(*argv, stdin, stdout, stderr);
+
+ el_set(el, EL_EDITOR, "vi"); /* Default editor is vi */
+ el_set(el, EL_SIGNAL, 1); /* Handle signals gracefully */
+ el_set(el, EL_PROMPT, prompt); /* Set the prompt function */
+
+ /* Tell editline to use this history interface */
+ el_set(el, EL_HIST, history, hist);
+
+ /* Add a user-defined function */
+ el_set(el, EL_ADDFN, "ed-complete", "Complete argument", complete);
+
+ /* Bind tab to it */
+ el_set(el, EL_BIND, "^I", "ed-complete", NULL);
+
+ /*
+ * Bind j, k in vi command mode to previous and next line, instead
+ * of previous and next history.
+ */
+ el_set(el, EL_BIND, "-a", "k", "ed-prev-line", NULL);
+ el_set(el, EL_BIND, "-a", "j", "ed-next-line", NULL);
+
+ /*
+ * Source the user's defaults file.
+ */
+ el_source(el, NULL);
+
+ while ((buf = el_gets(el, &num)) != NULL && num != 0) {
+ int ac;
+ const char **av;
+#ifdef DEBUG
+ (void) fprintf(stderr, "got %d %s", num, buf);
+#endif
+ if (!continuation && num == 1)
+ continue;
+
+ ncontinuation = tok_line(tok, buf, &ac, &av) > 0;
+#if 0
+ if (continuation) {
+ /*
+ * Append to the right event in case the user
+ * moved around in history.
+ */
+ if (history(hist, &ev, H_SET, lastevent) == -1)
+ err(1, "%d: %s\n", lastevent, ev.str);
+ history(hist, &ev, H_ADD , buf);
+ } else {
+ history(hist, &ev, H_ENTER, buf);
+ lastevent = ev.num;
+ }
+#else
+ /* Simpler */
+ history(hist, &ev, continuation ? H_APPEND : H_ENTER, buf);
+#endif
+
+ continuation = ncontinuation;
+ ncontinuation = 0;
+
+ if (strcmp(av[0], "history") == 0) {
+ int rv;
+
+ switch (ac) {
+ case 1:
+ for (rv = history(hist, &ev, H_LAST); rv != -1;
+ rv = history(hist, &ev, H_PREV))
+ (void) fprintf(stdout, "%4d %s",
+ ev.num, ev.str);
+ break;
+
+ case 2:
+ if (strcmp(av[1], "clear") == 0)
+ history(hist, &ev, H_CLEAR);
+ else
+ goto badhist;
+ break;
+
+ case 3:
+ if (strcmp(av[1], "load") == 0)
+ history(hist, &ev, H_LOAD, av[2]);
+ else if (strcmp(av[1], "save") == 0)
+ history(hist, &ev, H_SAVE, av[2]);
+ break;
+
+ badhist:
+ default:
+ (void) fprintf(stderr,
+ "Bad history arguments\n");
+ break;
+ }
+ } else if (el_parse(el, ac, av) == -1) {
+ switch (fork()) {
+ case 0:
+ execvp(av[0], (char *const *)av);
+ perror(av[0]);
+ _exit(1);
+ /*NOTREACHED*/
+ break;
+
+ case -1:
+ perror("fork");
+ break;
+
+ default:
+ if (wait(&num) == -1)
+ perror("wait");
+ (void) fprintf(stderr, "Exit %x\n", num);
+ break;
+ }
+ }
+
+ tok_reset(tok);
+ }
+
+ el_end(el);
+ tok_end(tok);
+ history_end(hist);
+
+ return (0);
+}
diff --git a/trunk/main/editline/chared.c b/trunk/main/editline/chared.c
new file mode 100644
index 000000000..8eaeb3b54
--- /dev/null
+++ b/trunk/main/editline/chared.c
@@ -0,0 +1,695 @@
+/* $NetBSD: chared.c,v 1.15 2002/03/18 16:00:50 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+#if !defined(lint) && !defined(SCCSID)
+#if 0
+static char sccsid[] = "@(#)chared.c 8.1 (Berkeley) 6/4/93";
+#else
+__RCSID("$NetBSD: chared.c,v 1.15 2002/03/18 16:00:50 christos Exp $");
+#endif
+#endif /* not lint && not SCCSID */
+
+/*
+ * chared.c: Character editor utilities
+ */
+#include <stdlib.h>
+#include "el.h"
+
+/* value to leave unused in line buffer */
+#define EL_LEAVE 2
+
+/* cv_undo():
+ * Handle state for the vi undo command
+ */
+protected void
+cv_undo(EditLine *el,int action, size_t size, char *ptr)
+{
+ c_undo_t *vu = &el->el_chared.c_undo;
+ vu->action = action;
+ vu->ptr = ptr;
+ vu->isize = size;
+ (void) memcpy(vu->buf, vu->ptr, size);
+#ifdef DEBUG_UNDO
+ (void) fprintf(el->el_errfile, "Undo buffer \"%s\" size = +%d -%d\n",
+ vu->ptr, vu->isize, vu->dsize);
+#endif
+}
+
+
+/* c_insert():
+ * Insert num characters
+ */
+protected void
+c_insert(EditLine *el, int num)
+{
+ char *cp;
+
+ if (el->el_line.lastchar + num >= el->el_line.limit)
+ return; /* can't go past end of buffer */
+
+ if (el->el_line.cursor < el->el_line.lastchar) {
+ /* if I must move chars */
+ for (cp = el->el_line.lastchar; cp >= el->el_line.cursor; cp--)
+ cp[num] = *cp;
+ }
+ el->el_line.lastchar += num;
+}
+
+
+/* c_delafter():
+ * Delete num characters after the cursor
+ */
+protected void
+c_delafter(EditLine *el, int num)
+{
+
+ if (el->el_line.cursor + num > el->el_line.lastchar)
+ num = el->el_line.lastchar - el->el_line.cursor;
+
+ if (num > 0) {
+ char *cp;
+
+ if (el->el_map.current != el->el_map.emacs)
+ cv_undo(el, INSERT, (size_t)num, el->el_line.cursor);
+
+ for (cp = el->el_line.cursor; cp <= el->el_line.lastchar; cp++)
+ *cp = cp[num];
+
+ el->el_line.lastchar -= num;
+ }
+}
+
+
+/* c_delbefore():
+ * Delete num characters before the cursor
+ */
+protected void
+c_delbefore(EditLine *el, int num)
+{
+
+ if (el->el_line.cursor - num < el->el_line.buffer)
+ num = el->el_line.cursor - el->el_line.buffer;
+
+ if (num > 0) {
+ char *cp;
+
+ if (el->el_map.current != el->el_map.emacs)
+ cv_undo(el, INSERT, (size_t)num,
+ el->el_line.cursor - num);
+
+ for (cp = el->el_line.cursor - num;
+ cp <= el->el_line.lastchar;
+ cp++)
+ *cp = cp[num];
+
+ el->el_line.lastchar -= num;
+ }
+}
+
+
+/* ce__isword():
+ * Return if p is part of a word according to emacs
+ */
+protected int
+ce__isword(int p)
+{
+ return (isalpha(p) || isdigit(p) || strchr("*?_-.[]~=", p) != NULL);
+}
+
+
+/* cv__isword():
+ * Return if p is part of a word according to vi
+ */
+protected int
+cv__isword(int p)
+{
+ return (!isspace(p));
+}
+
+
+/* c__prev_word():
+ * Find the previous word
+ */
+protected char *
+c__prev_word(char *p, char *low, int n, int (*wtest)(int))
+{
+ p--;
+
+ while (n--) {
+ while ((p >= low) && !(*wtest)((unsigned char) *p))
+ p--;
+ while ((p >= low) && (*wtest)((unsigned char) *p))
+ p--;
+ }
+
+ /* cp now points to one character before the word */
+ p++;
+ if (p < low)
+ p = low;
+ /* cp now points where we want it */
+ return (p);
+}
+
+
+/* c__next_word():
+ * Find the next word
+ */
+protected char *
+c__next_word(char *p, char *high, int n, int (*wtest)(int))
+{
+ while (n--) {
+ while ((p < high) && !(*wtest)((unsigned char) *p))
+ p++;
+ while ((p < high) && (*wtest)((unsigned char) *p))
+ p++;
+ }
+ if (p > high)
+ p = high;
+ /* p now points where we want it */
+ return (p);
+}
+
+/* cv_next_word():
+ * Find the next word vi style
+ */
+protected char *
+cv_next_word(EditLine *el, char *p, char *high, int n, int (*wtest)(int))
+{
+ int test;
+
+ while (n--) {
+ test = (*wtest)((unsigned char) *p);
+ while ((p < high) && (*wtest)((unsigned char) *p) == test)
+ p++;
+ /*
+ * vi historically deletes with cw only the word preserving the
+ * trailing whitespace! This is not what 'w' does..
+ */
+ if (el->el_chared.c_vcmd.action != (DELETE|INSERT))
+ while ((p < high) && isspace((unsigned char) *p))
+ p++;
+ }
+
+ /* p now points where we want it */
+ if (p > high)
+ return (high);
+ else
+ return (p);
+}
+
+
+/* cv_prev_word():
+ * Find the previous word vi style
+ */
+protected char *
+cv_prev_word(EditLine *el, char *p, char *low, int n, int (*wtest)(int))
+{
+ int test;
+
+ while (n--) {
+ p--;
+ /*
+ * vi historically deletes with cb only the word preserving the
+ * leading whitespace! This is not what 'b' does..
+ */
+ if (el->el_chared.c_vcmd.action != (DELETE|INSERT))
+ while ((p > low) && isspace((unsigned char) *p))
+ p--;
+ test = (*wtest)((unsigned char) *p);
+ while ((p >= low) && (*wtest)((unsigned char) *p) == test)
+ p--;
+ p++;
+ while (isspace((unsigned char) *p))
+ p++;
+ }
+
+ /* p now points where we want it */
+ if (p < low)
+ return (low);
+ else
+ return (p);
+}
+
+
+#ifdef notdef
+/* c__number():
+ * Ignore character p points to, return number appearing after that.
+ * A '$' by itself means a big number; "$-" is for negative; '^' means 1.
+ * Return p pointing to last char used.
+ */
+protected char *
+c__number(
+ char *p, /* character position */
+ int *num, /* Return value */
+ int dval) /* dval is the number to subtract from like $-3 */
+{
+ int i;
+ int sign = 1;
+
+ if (*++p == '^') {
+ *num = 1;
+ return (p);
+ }
+ if (*p == '$') {
+ if (*++p != '-') {
+ *num = 0x7fffffff; /* Handle $ */
+ return (--p);
+ }
+ sign = -1; /* Handle $- */
+ ++p;
+ }
+ for (i = 0; isdigit((unsigned char) *p); i = 10 * i + *p++ - '0')
+ continue;
+ *num = (sign < 0 ? dval - i : i);
+ return (--p);
+}
+#endif
+
+/* cv_delfini():
+ * Finish vi delete action
+ */
+protected void
+cv_delfini(EditLine *el)
+{
+ int size;
+ int oaction;
+
+ if (el->el_chared.c_vcmd.action & INSERT)
+ el->el_map.current = el->el_map.key;
+
+ oaction = el->el_chared.c_vcmd.action;
+ el->el_chared.c_vcmd.action = NOP;
+
+ if (el->el_chared.c_vcmd.pos == 0)
+ return;
+
+
+ if (el->el_line.cursor > el->el_chared.c_vcmd.pos) {
+ size = (int) (el->el_line.cursor - el->el_chared.c_vcmd.pos);
+ c_delbefore(el, size);
+ el->el_line.cursor = el->el_chared.c_vcmd.pos;
+ re_refresh_cursor(el);
+ } else if (el->el_line.cursor < el->el_chared.c_vcmd.pos) {
+ size = (int)(el->el_chared.c_vcmd.pos - el->el_line.cursor);
+ c_delafter(el, size);
+ } else {
+ size = 1;
+ c_delafter(el, size);
+ }
+ switch (oaction) {
+ case DELETE|INSERT:
+ el->el_chared.c_undo.action = DELETE|INSERT;
+ break;
+ case DELETE:
+ el->el_chared.c_undo.action = INSERT;
+ break;
+ case NOP:
+ case INSERT:
+ default:
+ EL_ABORT((el->el_errfile, "Bad oaction %d\n", oaction));
+ break;
+ }
+
+
+ el->el_chared.c_undo.ptr = el->el_line.cursor;
+ el->el_chared.c_undo.dsize = size;
+}
+
+
+#ifdef notdef
+/* ce__endword():
+ * Go to the end of this word according to emacs
+ */
+protected char *
+ce__endword(char *p, char *high, int n)
+{
+ p++;
+
+ while (n--) {
+ while ((p < high) && isspace((unsigned char) *p))
+ p++;
+ while ((p < high) && !isspace((unsigned char) *p))
+ p++;
+ }
+
+ p--;
+ return (p);
+}
+#endif
+
+
+/* cv__endword():
+ * Go to the end of this word according to vi
+ */
+protected char *
+cv__endword(char *p, char *high, int n)
+{
+ p++;
+
+ while (n--) {
+ while ((p < high) && isspace((unsigned char) *p))
+ p++;
+
+ if (isalnum((unsigned char) *p))
+ while ((p < high) && isalnum((unsigned char) *p))
+ p++;
+ else
+ while ((p < high) && !(isspace((unsigned char) *p) ||
+ isalnum((unsigned char) *p)))
+ p++;
+ }
+ p--;
+ return (p);
+}
+
+/* ch_init():
+ * Initialize the character editor
+ */
+protected int
+ch_init(EditLine *el)
+{
+ el->el_line.buffer = (char *) el_malloc(EL_BUFSIZ);
+ if (el->el_line.buffer == NULL)
+ return (-1);
+
+ (void) memset(el->el_line.buffer, 0, EL_BUFSIZ);
+ el->el_line.cursor = el->el_line.buffer;
+ el->el_line.lastchar = el->el_line.buffer;
+ el->el_line.limit = &el->el_line.buffer[EL_BUFSIZ - 2];
+
+ el->el_chared.c_undo.buf = (char *) el_malloc(EL_BUFSIZ);
+ if (el->el_chared.c_undo.buf == NULL)
+ return (-1);
+ (void) memset(el->el_chared.c_undo.buf, 0, EL_BUFSIZ);
+ el->el_chared.c_undo.action = NOP;
+ el->el_chared.c_undo.isize = 0;
+ el->el_chared.c_undo.dsize = 0;
+ el->el_chared.c_undo.ptr = el->el_line.buffer;
+
+ el->el_chared.c_vcmd.action = NOP;
+ el->el_chared.c_vcmd.pos = el->el_line.buffer;
+ el->el_chared.c_vcmd.ins = el->el_line.buffer;
+
+ el->el_chared.c_kill.buf = (char *) el_malloc(EL_BUFSIZ);
+ if (el->el_chared.c_kill.buf == NULL)
+ return (-1);
+ (void) memset(el->el_chared.c_kill.buf, 0, EL_BUFSIZ);
+ el->el_chared.c_kill.mark = el->el_line.buffer;
+ el->el_chared.c_kill.last = el->el_chared.c_kill.buf;
+
+ el->el_map.current = el->el_map.key;
+
+ el->el_state.inputmode = MODE_INSERT; /* XXX: save a default */
+ el->el_state.doingarg = 0;
+ el->el_state.metanext = 0;
+ el->el_state.argument = 1;
+ el->el_state.lastcmd = ED_UNASSIGNED;
+
+ el->el_chared.c_macro.nline = NULL;
+ el->el_chared.c_macro.level = -1;
+ el->el_chared.c_macro.macro = (char **) el_malloc(EL_MAXMACRO *
+ sizeof(char *));
+ if (el->el_chared.c_macro.macro == NULL)
+ return (-1);
+ return (0);
+}
+
+/* ch_reset():
+ * Reset the character editor
+ */
+protected void
+ch_reset(EditLine *el)
+{
+ el->el_line.cursor = el->el_line.buffer;
+ el->el_line.lastchar = el->el_line.buffer;
+
+ el->el_chared.c_undo.action = NOP;
+ el->el_chared.c_undo.isize = 0;
+ el->el_chared.c_undo.dsize = 0;
+ el->el_chared.c_undo.ptr = el->el_line.buffer;
+
+ el->el_chared.c_vcmd.action = NOP;
+ el->el_chared.c_vcmd.pos = el->el_line.buffer;
+ el->el_chared.c_vcmd.ins = el->el_line.buffer;
+
+ el->el_chared.c_kill.mark = el->el_line.buffer;
+
+ el->el_map.current = el->el_map.key;
+
+ el->el_state.inputmode = MODE_INSERT; /* XXX: save a default */
+ el->el_state.doingarg = 0;
+ el->el_state.metanext = 0;
+ el->el_state.argument = 1;
+ el->el_state.lastcmd = ED_UNASSIGNED;
+
+ el->el_chared.c_macro.level = -1;
+
+ el->el_history.eventno = 0;
+}
+
+/* ch_enlargebufs():
+ * Enlarge line buffer to be able to hold twice as much characters.
+ * Returns 1 if successful, 0 if not.
+ */
+protected int
+ch_enlargebufs(el, addlen)
+ EditLine *el;
+ size_t addlen;
+{
+ size_t sz, newsz;
+ char *newbuffer, *oldbuf, *oldkbuf;
+
+ sz = el->el_line.limit - el->el_line.buffer + EL_LEAVE;
+ newsz = sz * 2;
+ /*
+ * If newly required length is longer than current buffer, we need
+ * to make the buffer big enough to hold both old and new stuff.
+ */
+ if (addlen > sz) {
+ while(newsz - sz < addlen)
+ newsz *= 2;
+ }
+
+ /*
+ * Reallocate line buffer.
+ */
+ newbuffer = el_realloc(el->el_line.buffer, newsz);
+ if (!newbuffer)
+ return 0;
+
+ /* zero the newly added memory, leave old data in */
+ (void) memset(&newbuffer[sz], 0, newsz - sz);
+
+ oldbuf = el->el_line.buffer;
+
+ el->el_line.buffer = newbuffer;
+ el->el_line.cursor = newbuffer + (el->el_line.cursor - oldbuf);
+ el->el_line.lastchar = newbuffer + (el->el_line.lastchar - oldbuf);
+ el->el_line.limit = &newbuffer[newsz - EL_LEAVE];
+
+ /*
+ * Reallocate kill buffer.
+ */
+ newbuffer = el_realloc(el->el_chared.c_kill.buf, newsz);
+ if (!newbuffer)
+ return 0;
+
+ /* zero the newly added memory, leave old data in */
+ (void) memset(&newbuffer[sz], 0, newsz - sz);
+
+ oldkbuf = el->el_chared.c_kill.buf;
+
+ el->el_chared.c_kill.buf = newbuffer;
+ el->el_chared.c_kill.last = newbuffer +
+ (el->el_chared.c_kill.last - oldkbuf);
+ el->el_chared.c_kill.mark = el->el_line.buffer +
+ (el->el_chared.c_kill.mark - oldbuf);
+
+ /*
+ * Reallocate undo buffer.
+ */
+ newbuffer = el_realloc(el->el_chared.c_undo.buf, newsz);
+ if (!newbuffer)
+ return 0;
+
+ /* zero the newly added memory, leave old data in */
+ (void) memset(&newbuffer[sz], 0, newsz - sz);
+
+ el->el_chared.c_undo.ptr = el->el_line.buffer +
+ (el->el_chared.c_undo.ptr - oldbuf);
+ el->el_chared.c_undo.buf = newbuffer;
+
+ if (!hist_enlargebuf(el, sz, newsz))
+ return 0;
+
+ return 1;
+}
+
+/* ch_end():
+ * Free the data structures used by the editor
+ */
+protected void
+ch_end(EditLine *el)
+{
+ el_free((ptr_t) el->el_line.buffer);
+ el->el_line.buffer = NULL;
+ el->el_line.limit = NULL;
+ el_free((ptr_t) el->el_chared.c_undo.buf);
+ el->el_chared.c_undo.buf = NULL;
+ el_free((ptr_t) el->el_chared.c_kill.buf);
+ el->el_chared.c_kill.buf = NULL;
+ el_free((ptr_t) el->el_chared.c_macro.macro);
+ el->el_chared.c_macro.macro = NULL;
+ ch_reset(el);
+}
+
+
+/* el_insertstr():
+ * Insert string at cursorI
+ */
+public int
+el_insertstr(EditLine *el, const char *s)
+{
+ size_t len;
+
+ if ((len = strlen(s)) == 0)
+ return (-1);
+ if (el->el_line.lastchar + len >= el->el_line.limit) {
+ if (!ch_enlargebufs(el, len))
+ return (-1);
+ }
+
+ c_insert(el, (int)len);
+ while (*s)
+ *el->el_line.cursor++ = *s++;
+ return (0);
+}
+
+
+/* el_deletestr():
+ * Delete num characters before the cursor
+ */
+public void
+el_deletestr(EditLine *el, int n)
+{
+ if (n <= 0)
+ return;
+
+ if (el->el_line.cursor < &el->el_line.buffer[n])
+ return;
+
+ c_delbefore(el, n); /* delete before dot */
+ el->el_line.cursor -= n;
+ if (el->el_line.cursor < el->el_line.buffer)
+ el->el_line.cursor = el->el_line.buffer;
+}
+
+/* c_gets():
+ * Get a string
+ */
+protected int
+c_gets(EditLine *el, char *buf)
+{
+ char ch;
+ int len = 0;
+
+ for (ch = 0; ch == 0;) {
+ if (el_getc(el, &ch) != 1)
+ return (ed_end_of_file(el, 0));
+ switch (ch) {
+ case 0010: /* Delete and backspace */
+ case 0177:
+ if (len > 1) {
+ *el->el_line.cursor-- = '\0';
+ el->el_line.lastchar = el->el_line.cursor;
+ buf[len--] = '\0';
+ } else {
+ el->el_line.buffer[0] = '\0';
+ el->el_line.lastchar = el->el_line.buffer;
+ el->el_line.cursor = el->el_line.buffer;
+ return (CC_REFRESH);
+ }
+ re_refresh(el);
+ ch = 0;
+ break;
+
+ case 0033: /* ESC */
+ case '\r': /* Newline */
+ case '\n':
+ break;
+
+ default:
+ if (len >= EL_BUFSIZ)
+ term_beep(el);
+ else {
+ buf[len++] = ch;
+ *el->el_line.cursor++ = ch;
+ el->el_line.lastchar = el->el_line.cursor;
+ }
+ re_refresh(el);
+ ch = 0;
+ break;
+ }
+ }
+ buf[len] = ch;
+ return (len);
+}
+
+
+/* c_hpos():
+ * Return the current horizontal position of the cursor
+ */
+protected int
+c_hpos(EditLine *el)
+{
+ char *ptr;
+
+ /*
+ * Find how many characters till the beginning of this line.
+ */
+ if (el->el_line.cursor == el->el_line.buffer)
+ return (0);
+ else {
+ for (ptr = el->el_line.cursor - 1;
+ ptr >= el->el_line.buffer && *ptr != '\n';
+ ptr--)
+ continue;
+ return (el->el_line.cursor - ptr - 1);
+ }
+}
diff --git a/trunk/main/editline/chared.h b/trunk/main/editline/chared.h
new file mode 100644
index 000000000..403eca011
--- /dev/null
+++ b/trunk/main/editline/chared.h
@@ -0,0 +1,159 @@
+/* $NetBSD: chared.h,v 1.8 2002/03/18 16:00:51 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)chared.h 8.1 (Berkeley) 6/4/93
+ */
+
+/*
+ * el.chared.h: Character editor interface
+ */
+#ifndef _h_el_chared
+#define _h_el_chared
+
+#include <ctype.h>
+#include <string.h>
+
+#include "histedit.h"
+
+#define EL_MAXMACRO 10
+
+/*
+ * This is a issue of basic "vi" look-and-feel. Defining VI_MOVE works
+ * like real vi: i.e. the transition from command<->insert modes moves
+ * the cursor.
+ *
+ * On the other hand we really don't want to move the cursor, because
+ * all the editing commands don't include the character under the cursor.
+ * Probably the best fix is to make all the editing commands aware of
+ * this fact.
+ */
+#define VI_MOVE
+
+
+typedef struct c_macro_t {
+ int level;
+ char **macro;
+ char *nline;
+} c_macro_t;
+
+/*
+ * Undo information for both vi and emacs
+ */
+typedef struct c_undo_t {
+ int action;
+ size_t isize;
+ size_t dsize;
+ char *ptr;
+ char *buf;
+} c_undo_t;
+
+/*
+ * Current action information for vi
+ */
+typedef struct c_vcmd_t {
+ int action;
+ char *pos;
+ char *ins;
+} c_vcmd_t;
+
+/*
+ * Kill buffer for emacs
+ */
+typedef struct c_kill_t {
+ char *buf;
+ char *last;
+ char *mark;
+} c_kill_t;
+
+/*
+ * Note that we use both data structures because the user can bind
+ * commands from both editors!
+ */
+typedef struct el_chared_t {
+ c_undo_t c_undo;
+ c_kill_t c_kill;
+ c_vcmd_t c_vcmd;
+ c_macro_t c_macro;
+} el_chared_t;
+
+
+#define STReof "^D\b\b"
+#define STRQQ "\"\""
+
+#define isglob(a) (strchr("*[]?", (a)) != NULL)
+#define isword(a) (isprint(a))
+
+#define NOP 0x00
+#define DELETE 0x01
+#define INSERT 0x02
+#define CHANGE 0x04
+
+#define CHAR_FWD 0
+#define CHAR_BACK 1
+
+#define MODE_INSERT 0
+#define MODE_REPLACE 1
+#define MODE_REPLACE_1 2
+
+#include "common.h"
+#include "vi.h"
+#include "emacs.h"
+#include "search.h"
+#include "fcns.h"
+
+
+protected int cv__isword(int);
+protected void cv_delfini(EditLine *);
+protected char *cv__endword(char *, char *, int);
+protected int ce__isword(int);
+protected void cv_undo(EditLine *, int, size_t, char *);
+protected char *cv_next_word(EditLine*, char *, char *, int, int (*)(int));
+protected char *cv_prev_word(EditLine*, char *, char *, int, int (*)(int));
+protected char *c__next_word(char *, char *, int, int (*)(int));
+protected char *c__prev_word(char *, char *, int, int (*)(int));
+protected void c_insert(EditLine *, int);
+protected void c_delbefore(EditLine *, int);
+protected void c_delafter(EditLine *, int);
+protected int c_gets(EditLine *, char *);
+protected int c_hpos(EditLine *);
+
+protected int ch_init(EditLine *);
+protected void ch_reset(EditLine *);
+protected int ch_enlargebufs(EditLine *, size_t);
+protected void ch_end(EditLine *);
+
+#endif /* _h_el_chared */
diff --git a/trunk/main/editline/common.c b/trunk/main/editline/common.c
new file mode 100644
index 000000000..c831e79a3
--- /dev/null
+++ b/trunk/main/editline/common.c
@@ -0,0 +1,951 @@
+/* $NetBSD: common.c,v 1.11 2002/03/18 16:00:51 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+#if !defined(lint) && !defined(SCCSID)
+#if 0
+static char sccsid[] = "@(#)common.c 8.1 (Berkeley) 6/4/93";
+#else
+__RCSID("$NetBSD: common.c,v 1.11 2002/03/18 16:00:51 christos Exp $");
+#endif
+#endif /* not lint && not SCCSID */
+
+/*
+ * common.c: Common Editor functions
+ */
+#include "el.h"
+
+/* ed_end_of_file():
+ * Indicate end of file
+ * [^D]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_end_of_file(EditLine *el, int c)
+{
+
+ re_goto_bottom(el);
+ *el->el_line.lastchar = '\0';
+ return (CC_EOF);
+}
+
+
+/* ed_insert():
+ * Add character to the line
+ * Insert a character [bound to all insert keys]
+ */
+protected el_action_t
+ed_insert(EditLine *el, int c)
+{
+ int i;
+
+ if (c == '\0')
+ return (CC_ERROR);
+
+ if (el->el_line.lastchar + el->el_state.argument >=
+ el->el_line.limit) {
+ /* end of buffer space, try to allocate more */
+ if (!ch_enlargebufs(el, (size_t) el->el_state.argument))
+ return CC_ERROR; /* error allocating more */
+ }
+
+ if (el->el_state.argument == 1) {
+ if (el->el_state.inputmode != MODE_INSERT) {
+ el->el_chared.c_undo.buf[el->el_chared.c_undo.isize++] =
+ *el->el_line.cursor;
+ el->el_chared.c_undo.buf[el->el_chared.c_undo.isize] =
+ '\0';
+ c_delafter(el, 1);
+ }
+ c_insert(el, 1);
+
+ *el->el_line.cursor++ = c;
+ el->el_state.doingarg = 0; /* just in case */
+ re_fastaddc(el); /* fast refresh for one char. */
+ } else {
+ if (el->el_state.inputmode != MODE_INSERT) {
+ for (i = 0; i < el->el_state.argument; i++)
+ el->el_chared.c_undo.buf[el->el_chared.c_undo.isize++] =
+ el->el_line.cursor[i];
+
+ el->el_chared.c_undo.buf[el->el_chared.c_undo.isize] =
+ '\0';
+ c_delafter(el, el->el_state.argument);
+ }
+ c_insert(el, el->el_state.argument);
+
+ while (el->el_state.argument--)
+ *el->el_line.cursor++ = c;
+ re_refresh(el);
+ }
+
+ if (el->el_state.inputmode == MODE_REPLACE_1)
+ (void) vi_command_mode(el, 0);
+
+ return (CC_NORM);
+}
+
+
+/* ed_delete_prev_word():
+ * Delete from beginning of current word to cursor
+ * [M-^?] [^W]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_delete_prev_word(EditLine *el, int c)
+{
+ char *cp, *p, *kp;
+
+ if (el->el_line.cursor == el->el_line.buffer)
+ return (CC_ERROR);
+
+ cp = c__prev_word(el->el_line.cursor, el->el_line.buffer,
+ el->el_state.argument, ce__isword);
+
+ for (p = cp, kp = el->el_chared.c_kill.buf; p < el->el_line.cursor; p++)
+ *kp++ = *p;
+ el->el_chared.c_kill.last = kp;
+
+ c_delbefore(el, el->el_line.cursor - cp); /* delete before dot */
+ el->el_line.cursor = cp;
+ if (el->el_line.cursor < el->el_line.buffer)
+ el->el_line.cursor = el->el_line.buffer; /* bounds check */
+ return (CC_REFRESH);
+}
+
+
+/* ed_delete_next_char():
+ * Delete character under cursor
+ * [^D] [x]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_delete_next_char(EditLine *el, int c)
+{
+#ifdef notdef /* XXX */
+#define EL el->el_line
+ (void) fprintf(el->el_errlfile,
+ "\nD(b: %x(%s) c: %x(%s) last: %x(%s) limit: %x(%s)\n",
+ EL.buffer, EL.buffer, EL.cursor, EL.cursor, EL.lastchar,
+ EL.lastchar, EL.limit, EL.limit);
+#endif
+ if (el->el_line.cursor == el->el_line.lastchar) {
+ /* if I'm at the end */
+ if (el->el_map.type == MAP_VI) {
+ if (el->el_line.cursor == el->el_line.buffer) {
+ /* if I'm also at the beginning */
+#ifdef KSHVI
+ return (CC_ERROR);
+#else
+ term_overwrite(el, STReof, 4);
+ /* then do a EOF */
+ term__flush();
+ return (CC_EOF);
+#endif
+ } else {
+#ifdef KSHVI
+ el->el_line.cursor--;
+#else
+ return (CC_ERROR);
+#endif
+ }
+ } else {
+ if (el->el_line.cursor != el->el_line.buffer)
+ el->el_line.cursor--;
+ else
+ return (CC_ERROR);
+ }
+ }
+ c_delafter(el, el->el_state.argument); /* delete after dot */
+ if (el->el_line.cursor >= el->el_line.lastchar &&
+ el->el_line.cursor > el->el_line.buffer)
+ /* bounds check */
+ el->el_line.cursor = el->el_line.lastchar - 1;
+ return (CC_REFRESH);
+}
+
+
+/* ed_kill_line():
+ * Cut to the end of line
+ * [^K] [^K]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_kill_line(EditLine *el, int c)
+{
+ char *kp, *cp;
+
+ cp = el->el_line.cursor;
+ kp = el->el_chared.c_kill.buf;
+ while (cp < el->el_line.lastchar)
+ *kp++ = *cp++; /* copy it */
+ el->el_chared.c_kill.last = kp;
+ /* zap! -- delete to end */
+ el->el_line.lastchar = el->el_line.cursor;
+ return (CC_REFRESH);
+}
+
+
+/* ed_move_to_end():
+ * Move cursor to the end of line
+ * [^E] [^E]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_move_to_end(EditLine *el, int c)
+{
+
+ el->el_line.cursor = el->el_line.lastchar;
+ if (el->el_map.type == MAP_VI) {
+#ifdef VI_MOVE
+ el->el_line.cursor--;
+#endif
+ if (el->el_chared.c_vcmd.action & DELETE) {
+ cv_delfini(el);
+ return (CC_REFRESH);
+ }
+ }
+ return (CC_CURSOR);
+}
+
+
+/* ed_move_to_beg():
+ * Move cursor to the beginning of line
+ * [^A] [^A]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_move_to_beg(EditLine *el, int c)
+{
+
+ el->el_line.cursor = el->el_line.buffer;
+
+ if (el->el_map.type == MAP_VI) {
+ /* We want FIRST non space character */
+ while (isspace((unsigned char) *el->el_line.cursor))
+ el->el_line.cursor++;
+ if (el->el_chared.c_vcmd.action & DELETE) {
+ cv_delfini(el);
+ return (CC_REFRESH);
+ }
+ }
+ return (CC_CURSOR);
+}
+
+
+/* ed_transpose_chars():
+ * Exchange the character to the left of the cursor with the one under it
+ * [^T] [^T]
+ */
+protected el_action_t
+ed_transpose_chars(EditLine *el, int c)
+{
+
+ if (el->el_line.cursor < el->el_line.lastchar) {
+ if (el->el_line.lastchar <= &el->el_line.buffer[1])
+ return (CC_ERROR);
+ else
+ el->el_line.cursor++;
+ }
+ if (el->el_line.cursor > &el->el_line.buffer[1]) {
+ /* must have at least two chars entered */
+ c = el->el_line.cursor[-2];
+ el->el_line.cursor[-2] = el->el_line.cursor[-1];
+ el->el_line.cursor[-1] = c;
+ return (CC_REFRESH);
+ } else
+ return (CC_ERROR);
+}
+
+
+/* ed_next_char():
+ * Move to the right one character
+ * [^F] [^F]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_next_char(EditLine *el, int c)
+{
+
+ if (el->el_line.cursor >= el->el_line.lastchar)
+ return (CC_ERROR);
+
+ el->el_line.cursor += el->el_state.argument;
+ if (el->el_line.cursor > el->el_line.lastchar)
+ el->el_line.cursor = el->el_line.lastchar;
+
+ if (el->el_map.type == MAP_VI)
+ if (el->el_chared.c_vcmd.action & DELETE) {
+ cv_delfini(el);
+ return (CC_REFRESH);
+ }
+ return (CC_CURSOR);
+}
+
+
+/* ed_prev_word():
+ * Move to the beginning of the current word
+ * [M-b] [b]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_prev_word(EditLine *el, int c)
+{
+
+ if (el->el_line.cursor == el->el_line.buffer)
+ return (CC_ERROR);
+
+ el->el_line.cursor = c__prev_word(el->el_line.cursor,
+ el->el_line.buffer,
+ el->el_state.argument,
+ ce__isword);
+
+ if (el->el_map.type == MAP_VI)
+ if (el->el_chared.c_vcmd.action & DELETE) {
+ cv_delfini(el);
+ return (CC_REFRESH);
+ }
+ return (CC_CURSOR);
+}
+
+
+/* ed_prev_char():
+ * Move to the left one character
+ * [^B] [^B]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_prev_char(EditLine *el, int c)
+{
+
+ if (el->el_line.cursor > el->el_line.buffer) {
+ el->el_line.cursor -= el->el_state.argument;
+ if (el->el_line.cursor < el->el_line.buffer)
+ el->el_line.cursor = el->el_line.buffer;
+
+ if (el->el_map.type == MAP_VI)
+ if (el->el_chared.c_vcmd.action & DELETE) {
+ cv_delfini(el);
+ return (CC_REFRESH);
+ }
+ return (CC_CURSOR);
+ } else
+ return (CC_ERROR);
+}
+
+
+/* ed_quoted_insert():
+ * Add the next character typed verbatim
+ * [^V] [^V]
+ */
+protected el_action_t
+ed_quoted_insert(EditLine *el, int c)
+{
+ int num;
+ char tc;
+
+ tty_quotemode(el);
+ num = el_getc(el, &tc);
+ c = (unsigned char) tc;
+ tty_noquotemode(el);
+ if (num == 1)
+ return (ed_insert(el, c));
+ else
+ return (ed_end_of_file(el, 0));
+}
+
+
+/* ed_digit():
+ * Adds to argument or enters a digit
+ */
+protected el_action_t
+ed_digit(EditLine *el, int c)
+{
+
+ if (!isdigit(c))
+ return (CC_ERROR);
+
+ if (el->el_state.doingarg) {
+ /* if doing an arg, add this in... */
+ if (el->el_state.lastcmd == EM_UNIVERSAL_ARGUMENT)
+ el->el_state.argument = c - '0';
+ else {
+ if (el->el_state.argument > 1000000)
+ return (CC_ERROR);
+ el->el_state.argument =
+ (el->el_state.argument * 10) + (c - '0');
+ }
+ return (CC_ARGHACK);
+ } else {
+ if (el->el_line.lastchar + 1 >= el->el_line.limit) {
+ if (!ch_enlargebufs(el, 1))
+ return (CC_ERROR);
+ }
+
+ if (el->el_state.inputmode != MODE_INSERT) {
+ el->el_chared.c_undo.buf[el->el_chared.c_undo.isize++] =
+ *el->el_line.cursor;
+ el->el_chared.c_undo.buf[el->el_chared.c_undo.isize] =
+ '\0';
+ c_delafter(el, 1);
+ }
+ c_insert(el, 1);
+ *el->el_line.cursor++ = c;
+ el->el_state.doingarg = 0;
+ re_fastaddc(el);
+ }
+ return (CC_NORM);
+}
+
+
+/* ed_argument_digit():
+ * Digit that starts argument
+ * For ESC-n
+ */
+protected el_action_t
+ed_argument_digit(EditLine *el, int c)
+{
+
+ if (!isdigit(c))
+ return (CC_ERROR);
+
+ if (el->el_state.doingarg) {
+ if (el->el_state.argument > 1000000)
+ return (CC_ERROR);
+ el->el_state.argument = (el->el_state.argument * 10) +
+ (c - '0');
+ } else { /* else starting an argument */
+ el->el_state.argument = c - '0';
+ el->el_state.doingarg = 1;
+ }
+ return (CC_ARGHACK);
+}
+
+
+/* ed_unassigned():
+ * Indicates unbound character
+ * Bound to keys that are not assigned
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_unassigned(EditLine *el, int c)
+{
+
+ term_beep(el);
+ term__flush();
+ return (CC_NORM);
+}
+
+
+/**
+ ** TTY key handling.
+ **/
+
+/* ed_tty_sigint():
+ * Tty interrupt character
+ * [^C]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_tty_sigint(EditLine *el, int c)
+{
+
+ return (CC_NORM);
+}
+
+
+/* ed_tty_dsusp():
+ * Tty delayed suspend character
+ * [^Y]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_tty_dsusp(EditLine *el, int c)
+{
+
+ return (CC_NORM);
+}
+
+
+/* ed_tty_flush_output():
+ * Tty flush output characters
+ * [^O]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_tty_flush_output(EditLine *el, int c)
+{
+
+ return (CC_NORM);
+}
+
+
+/* ed_tty_sigquit():
+ * Tty quit character
+ * [^\]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_tty_sigquit(EditLine *el, int c)
+{
+
+ return (CC_NORM);
+}
+
+
+/* ed_tty_sigtstp():
+ * Tty suspend character
+ * [^Z]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_tty_sigtstp(EditLine *el, int c)
+{
+
+ return (CC_NORM);
+}
+
+
+/* ed_tty_stop_output():
+ * Tty disallow output characters
+ * [^S]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_tty_stop_output(EditLine *el, int c)
+{
+
+ return (CC_NORM);
+}
+
+
+/* ed_tty_start_output():
+ * Tty allow output characters
+ * [^Q]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_tty_start_output(EditLine *el, int c)
+{
+
+ return (CC_NORM);
+}
+
+
+/* ed_newline():
+ * Execute command
+ * [^J]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_newline(EditLine *el, int c)
+{
+
+ re_goto_bottom(el);
+ *el->el_line.lastchar++ = '\n';
+ *el->el_line.lastchar = '\0';
+ if (el->el_map.type == MAP_VI)
+ el->el_chared.c_vcmd.ins = el->el_line.buffer;
+ return (CC_NEWLINE);
+}
+
+
+/* ed_delete_prev_char():
+ * Delete the character to the left of the cursor
+ * [^?]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_delete_prev_char(EditLine *el, int c)
+{
+
+ if (el->el_line.cursor <= el->el_line.buffer)
+ return (CC_ERROR);
+
+ c_delbefore(el, el->el_state.argument);
+ el->el_line.cursor -= el->el_state.argument;
+ if (el->el_line.cursor < el->el_line.buffer)
+ el->el_line.cursor = el->el_line.buffer;
+ return (CC_REFRESH);
+}
+
+
+/* ed_clear_screen():
+ * Clear screen leaving current line at the top
+ * [^L]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_clear_screen(EditLine *el, int c)
+{
+
+ term_clear_screen(el); /* clear the whole real screen */
+ re_clear_display(el); /* reset everything */
+ return (CC_REFRESH);
+}
+
+
+/* ed_redisplay():
+ * Redisplay everything
+ * ^R
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_redisplay(EditLine *el, int c)
+{
+
+ return (CC_REDISPLAY);
+}
+
+
+/* ed_start_over():
+ * Erase current line and start from scratch
+ * [^G]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_start_over(EditLine *el, int c)
+{
+
+ ch_reset(el);
+ return (CC_REFRESH);
+}
+
+
+/* ed_sequence_lead_in():
+ * First character in a bound sequence
+ * Placeholder for external keys
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_sequence_lead_in(EditLine *el, int c)
+{
+
+ return (CC_NORM);
+}
+
+
+/* ed_prev_history():
+ * Move to the previous history line
+ * [^P] [k]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_prev_history(EditLine *el, int c)
+{
+ char beep = 0;
+
+ el->el_chared.c_undo.action = NOP;
+ *el->el_line.lastchar = '\0'; /* just in case */
+
+ if (el->el_history.eventno == 0) { /* save the current buffer
+ * away */
+ (void) strncpy(el->el_history.buf, el->el_line.buffer,
+ EL_BUFSIZ - 1);
+ el->el_history.last = el->el_history.buf +
+ (el->el_line.lastchar - el->el_line.buffer);
+ }
+ el->el_history.eventno += el->el_state.argument;
+
+ if (hist_get(el) == CC_ERROR) {
+ beep = 1;
+ /* el->el_history.eventno was fixed by first call */
+ (void) hist_get(el);
+ }
+ re_refresh(el);
+ if (beep)
+ return (CC_ERROR);
+ else
+ return (CC_NORM); /* was CC_UP_HIST */
+}
+
+
+/* ed_next_history():
+ * Move to the next history line
+ * [^N] [j]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_next_history(EditLine *el, int c)
+{
+
+ el->el_chared.c_undo.action = NOP;
+ *el->el_line.lastchar = '\0'; /* just in case */
+
+ el->el_history.eventno -= el->el_state.argument;
+
+ if (el->el_history.eventno < 0) {
+ el->el_history.eventno = 0;
+ return (CC_ERROR);/* make it beep */
+ }
+ return (hist_get(el));
+}
+
+
+/* ed_search_prev_history():
+ * Search previous in history for a line matching the current
+ * next search history [M-P] [K]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_search_prev_history(EditLine *el, int c)
+{
+ const char *hp;
+ int h;
+ bool_t found = 0;
+
+ el->el_chared.c_vcmd.action = NOP;
+ el->el_chared.c_undo.action = NOP;
+ *el->el_line.lastchar = '\0'; /* just in case */
+ if (el->el_history.eventno < 0) {
+#ifdef DEBUG_EDIT
+ (void) fprintf(el->el_errfile,
+ "e_prev_search_hist(): eventno < 0;\n");
+#endif
+ el->el_history.eventno = 0;
+ return (CC_ERROR);
+ }
+ if (el->el_history.eventno == 0) {
+ (void) strncpy(el->el_history.buf, el->el_line.buffer,
+ EL_BUFSIZ - 1);
+ el->el_history.last = el->el_history.buf +
+ (el->el_line.lastchar - el->el_line.buffer);
+ }
+ if (el->el_history.ref == NULL)
+ return (CC_ERROR);
+
+ hp = HIST_FIRST(el);
+ if (hp == NULL)
+ return (CC_ERROR);
+
+ c_setpat(el); /* Set search pattern !! */
+
+ for (h = 1; h <= el->el_history.eventno; h++)
+ hp = HIST_NEXT(el);
+
+ while (hp != NULL) {
+#ifdef SDEBUG
+ (void) fprintf(el->el_errfile, "Comparing with \"%s\"\n", hp);
+#endif
+ if ((strncmp(hp, el->el_line.buffer, (size_t)
+ (el->el_line.lastchar - el->el_line.buffer)) ||
+ hp[el->el_line.lastchar - el->el_line.buffer]) &&
+ c_hmatch(el, hp)) {
+ found++;
+ break;
+ }
+ h++;
+ hp = HIST_NEXT(el);
+ }
+
+ if (!found) {
+#ifdef SDEBUG
+ (void) fprintf(el->el_errfile, "not found\n");
+#endif
+ return (CC_ERROR);
+ }
+ el->el_history.eventno = h;
+
+ return (hist_get(el));
+}
+
+
+/* ed_search_next_history():
+ * Search next in history for a line matching the current
+ * [M-N] [J]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_search_next_history(EditLine *el, int c)
+{
+ const char *hp;
+ int h;
+ bool_t found = 0;
+
+ el->el_chared.c_vcmd.action = NOP;
+ el->el_chared.c_undo.action = NOP;
+ *el->el_line.lastchar = '\0'; /* just in case */
+
+ if (el->el_history.eventno == 0)
+ return (CC_ERROR);
+
+ if (el->el_history.ref == NULL)
+ return (CC_ERROR);
+
+ hp = HIST_FIRST(el);
+ if (hp == NULL)
+ return (CC_ERROR);
+
+ c_setpat(el); /* Set search pattern !! */
+
+ for (h = 1; h < el->el_history.eventno && hp; h++) {
+#ifdef SDEBUG
+ (void) fprintf(el->el_errfile, "Comparing with \"%s\"\n", hp);
+#endif
+ if ((strncmp(hp, el->el_line.buffer, (size_t)
+ (el->el_line.lastchar - el->el_line.buffer)) ||
+ hp[el->el_line.lastchar - el->el_line.buffer]) &&
+ c_hmatch(el, hp))
+ found = h;
+ hp = HIST_NEXT(el);
+ }
+
+ if (!found) { /* is it the current history number? */
+ if (!c_hmatch(el, el->el_history.buf)) {
+#ifdef SDEBUG
+ (void) fprintf(el->el_errfile, "not found\n");
+#endif
+ return (CC_ERROR);
+ }
+ }
+ el->el_history.eventno = found;
+
+ return (hist_get(el));
+}
+
+
+/* ed_prev_line():
+ * Move up one line
+ * Could be [k] [^p]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_prev_line(EditLine *el, int c)
+{
+ char *ptr;
+ int nchars = c_hpos(el);
+
+ /*
+ * Move to the line requested
+ */
+ if (*(ptr = el->el_line.cursor) == '\n')
+ ptr--;
+
+ for (; ptr >= el->el_line.buffer; ptr--)
+ if (*ptr == '\n' && --el->el_state.argument <= 0)
+ break;
+
+ if (el->el_state.argument > 0)
+ return (CC_ERROR);
+
+ /*
+ * Move to the beginning of the line
+ */
+ for (ptr--; ptr >= el->el_line.buffer && *ptr != '\n'; ptr--)
+ continue;
+
+ /*
+ * Move to the character requested
+ */
+ for (ptr++;
+ nchars-- > 0 && ptr < el->el_line.lastchar && *ptr != '\n';
+ ptr++)
+ continue;
+
+ el->el_line.cursor = ptr;
+ return (CC_CURSOR);
+}
+
+
+/* ed_next_line():
+ * Move down one line
+ * Could be [j] [^n]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_next_line(EditLine *el, int c)
+{
+ char *ptr;
+ int nchars = c_hpos(el);
+
+ /*
+ * Move to the line requested
+ */
+ for (ptr = el->el_line.cursor; ptr < el->el_line.lastchar; ptr++)
+ if (*ptr == '\n' && --el->el_state.argument <= 0)
+ break;
+
+ if (el->el_state.argument > 0)
+ return (CC_ERROR);
+
+ /*
+ * Move to the character requested
+ */
+ for (ptr++;
+ nchars-- > 0 && ptr < el->el_line.lastchar && *ptr != '\n';
+ ptr++)
+ continue;
+
+ el->el_line.cursor = ptr;
+ return (CC_CURSOR);
+}
+
+
+/* ed_command():
+ * Editline extended command
+ * [M-X] [:]
+ */
+protected el_action_t
+/*ARGSUSED*/
+ed_command(EditLine *el, int c)
+{
+ char tmpbuf[EL_BUFSIZ];
+ int tmplen;
+
+ el->el_line.buffer[0] = '\0';
+ el->el_line.lastchar = el->el_line.buffer;
+ el->el_line.cursor = el->el_line.buffer;
+
+ c_insert(el, 3); /* prompt + ": " */
+ *el->el_line.cursor++ = '\n';
+ *el->el_line.cursor++ = ':';
+ *el->el_line.cursor++ = ' ';
+ re_refresh(el);
+
+ tmplen = c_gets(el, tmpbuf);
+ tmpbuf[tmplen] = '\0';
+
+ el->el_line.buffer[0] = '\0';
+ el->el_line.lastchar = el->el_line.buffer;
+ el->el_line.cursor = el->el_line.buffer;
+
+ if (parse_line(el, tmpbuf) == -1)
+ return (CC_ERROR);
+ else
+ return (CC_REFRESH);
+}
diff --git a/trunk/main/editline/config.guess b/trunk/main/editline/config.guess
new file mode 100755
index 000000000..a6d8a945f
--- /dev/null
+++ b/trunk/main/editline/config.guess
@@ -0,0 +1,1449 @@
+#! /bin/sh
+# Attempt to guess a canonical system name.
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+# 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+
+timestamp='2004-06-24'
+
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Originally written by Per Bothner <per@bothner.com>.
+# Please send patches to <config-patches@gnu.org>. Submit a context
+# diff and a properly formatted ChangeLog entry.
+#
+# This script attempts to guess a canonical system name similar to
+# config.sub. If it succeeds, it prints the system name on stdout, and
+# exits with 0. Otherwise, it exits with 1.
+#
+# The plan is that this can be called by configure scripts if you
+# don't specify an explicit build system type.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION]
+
+Output the configuration name of the system \`$me' is run on.
+
+Operation modes:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.guess ($timestamp)
+
+Originally written by Per Bothner.
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit 0 ;;
+ --version | -v )
+ echo "$version" ; exit 0 ;;
+ --help | --h* | -h )
+ echo "$usage"; exit 0 ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help" >&2
+ exit 1 ;;
+ * )
+ break ;;
+ esac
+done
+
+if test $# != 0; then
+ echo "$me: too many arguments$help" >&2
+ exit 1
+fi
+
+trap 'exit 1' 1 2 15
+
+# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
+# compiler to aid in system detection is discouraged as it requires
+# temporary files to be created and, as you can see below, it is a
+# headache to deal with in a portable fashion.
+
+# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
+# use `HOST_CC' if defined, but it is deprecated.
+
+# Portable tmp directory creation inspired by the Autoconf team.
+
+set_cc_for_build='
+trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ;
+trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ;
+: ${TMPDIR=/tmp} ;
+ { tmp=`(umask 077 && mktemp -d -q "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
+ { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } ||
+ { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } ||
+ { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ;
+dummy=$tmp/dummy ;
+tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ;
+case $CC_FOR_BUILD,$HOST_CC,$CC in
+ ,,) echo "int x;" > $dummy.c ;
+ for c in cc gcc c89 c99 ; do
+ if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then
+ CC_FOR_BUILD="$c"; break ;
+ fi ;
+ done ;
+ if test x"$CC_FOR_BUILD" = x ; then
+ CC_FOR_BUILD=no_compiler_found ;
+ fi
+ ;;
+ ,,*) CC_FOR_BUILD=$CC ;;
+ ,*,*) CC_FOR_BUILD=$HOST_CC ;;
+esac ;'
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 1994-08-24)
+if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
+ PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
+ *:NetBSD:*:*)
+ # NetBSD (nbsd) targets should (where applicable) match one or
+ # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*,
+ # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently
+ # switched to ELF, *-*-netbsd* would select the old
+ # object file format. This provides both forward
+ # compatibility and a consistent mechanism for selecting the
+ # object file format.
+ #
+ # Note: NetBSD doesn't particularly care about the vendor
+ # portion of the name. We always set it to "unknown".
+ sysctl="sysctl -n hw.machine_arch"
+ UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \
+ /usr/sbin/$sysctl 2>/dev/null || echo unknown)`
+ case "${UNAME_MACHINE_ARCH}" in
+ armeb) machine=armeb-unknown ;;
+ arm*) machine=arm-unknown ;;
+ sh3el) machine=shl-unknown ;;
+ sh3eb) machine=sh-unknown ;;
+ *) machine=${UNAME_MACHINE_ARCH}-unknown ;;
+ esac
+ # The Operating System including object format, if it has switched
+ # to ELF recently, or will in the future.
+ case "${UNAME_MACHINE_ARCH}" in
+ arm*|i386|m68k|ns32k|sh3*|sparc|vax)
+ eval $set_cc_for_build
+ if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep __ELF__ >/dev/null
+ then
+ # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
+ # Return netbsd for either. FIX?
+ os=netbsd
+ else
+ os=netbsdelf
+ fi
+ ;;
+ *)
+ os=netbsd
+ ;;
+ esac
+ # The OS release
+ # Debian GNU/NetBSD machines have a different userland, and
+ # thus, need a distinct triplet. However, they do not need
+ # kernel version information, so it can be replaced with a
+ # suitable tag, in the style of linux-gnu.
+ case "${UNAME_VERSION}" in
+ Debian*)
+ release='-gnu'
+ ;;
+ *)
+ release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+ ;;
+ esac
+ # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
+ # contains redundant information, the shorter form:
+ # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+ echo "${machine}-${os}${release}"
+ exit 0 ;;
+ amd64:OpenBSD:*:*)
+ echo x86_64-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ amiga:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ arc:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ cats:OpenBSD:*:*)
+ echo arm-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ hp300:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ luna88k:OpenBSD:*:*)
+ echo m88k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mac68k:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ macppc:OpenBSD:*:*)
+ echo powerpc-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mvme68k:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mvme88k:OpenBSD:*:*)
+ echo m88k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mvmeppc:OpenBSD:*:*)
+ echo powerpc-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ pmax:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ sgi:OpenBSD:*:*)
+ echo mipseb-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ sun3:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ wgrisc:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ *:OpenBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ *:ekkoBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE}
+ exit 0 ;;
+ macppc:MirBSD:*:*)
+ echo powerppc-unknown-mirbsd${UNAME_RELEASE}
+ exit 0 ;;
+ *:MirBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}
+ exit 0 ;;
+ alpha:OSF1:*:*)
+ case $UNAME_RELEASE in
+ *4.0)
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+ ;;
+ *5.*)
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
+ ;;
+ esac
+ # According to Compaq, /usr/sbin/psrinfo has been available on
+ # OSF/1 and Tru64 systems produced since 1995. I hope that
+ # covers most systems running today. This code pipes the CPU
+ # types through head -n 1, so we only detect the type of CPU 0.
+ ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1`
+ case "$ALPHA_CPU_TYPE" in
+ "EV4 (21064)")
+ UNAME_MACHINE="alpha" ;;
+ "EV4.5 (21064)")
+ UNAME_MACHINE="alpha" ;;
+ "LCA4 (21066/21068)")
+ UNAME_MACHINE="alpha" ;;
+ "EV5 (21164)")
+ UNAME_MACHINE="alphaev5" ;;
+ "EV5.6 (21164A)")
+ UNAME_MACHINE="alphaev56" ;;
+ "EV5.6 (21164PC)")
+ UNAME_MACHINE="alphapca56" ;;
+ "EV5.7 (21164PC)")
+ UNAME_MACHINE="alphapca57" ;;
+ "EV6 (21264)")
+ UNAME_MACHINE="alphaev6" ;;
+ "EV6.7 (21264A)")
+ UNAME_MACHINE="alphaev67" ;;
+ "EV6.8CB (21264C)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.8AL (21264B)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.8CX (21264D)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.9A (21264/EV69A)")
+ UNAME_MACHINE="alphaev69" ;;
+ "EV7 (21364)")
+ UNAME_MACHINE="alphaev7" ;;
+ "EV7.9 (21364A)")
+ UNAME_MACHINE="alphaev79" ;;
+ esac
+ # A Pn.n version is a patched version.
+ # A Vn.n version is a released version.
+ # A Tn.n version is a released field test version.
+ # A Xn.n version is an unreleased experimental baselevel.
+ # 1.2 uses "1.2" for uname -r.
+ echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+ exit 0 ;;
+ Alpha\ *:Windows_NT*:*)
+ # How do we know it's Interix rather than the generic POSIX subsystem?
+ # Should we change UNAME_MACHINE based on the output of uname instead
+ # of the specific Alpha model?
+ echo alpha-pc-interix
+ exit 0 ;;
+ 21064:Windows_NT:50:3)
+ echo alpha-dec-winnt3.5
+ exit 0 ;;
+ Amiga*:UNIX_System_V:4.0:*)
+ echo m68k-unknown-sysv4
+ exit 0;;
+ *:[Aa]miga[Oo][Ss]:*:*)
+ echo ${UNAME_MACHINE}-unknown-amigaos
+ exit 0 ;;
+ *:[Mm]orph[Oo][Ss]:*:*)
+ echo ${UNAME_MACHINE}-unknown-morphos
+ exit 0 ;;
+ *:OS/390:*:*)
+ echo i370-ibm-openedition
+ exit 0 ;;
+ *:OS400:*:*)
+ echo powerpc-ibm-os400
+ exit 0 ;;
+ arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+ echo arm-acorn-riscix${UNAME_RELEASE}
+ exit 0;;
+ SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+ echo hppa1.1-hitachi-hiuxmpp
+ exit 0;;
+ Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+ # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+ if test "`(/bin/universe) 2>/dev/null`" = att ; then
+ echo pyramid-pyramid-sysv3
+ else
+ echo pyramid-pyramid-bsd
+ fi
+ exit 0 ;;
+ NILE*:*:*:dcosx)
+ echo pyramid-pyramid-svr4
+ exit 0 ;;
+ DRS?6000:unix:4.0:6*)
+ echo sparc-icl-nx6
+ exit 0 ;;
+ DRS?6000:UNIX_SV:4.2*:7*)
+ case `/usr/bin/uname -p` in
+ sparc) echo sparc-icl-nx7 && exit 0 ;;
+ esac ;;
+ sun4H:SunOS:5.*:*)
+ echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+ echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ i86pc:SunOS:5.*:*)
+ echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ sun4*:SunOS:6*:*)
+ # According to config.sub, this is the proper way to canonicalize
+ # SunOS6. Hard to guess exactly what SunOS6 will be like, but
+ # it's likely to be more like Solaris than SunOS4.
+ echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ sun4*:SunOS:*:*)
+ case "`/usr/bin/arch -k`" in
+ Series*|S4*)
+ UNAME_RELEASE=`uname -v`
+ ;;
+ esac
+ # Japanese Language versions have a version number like `4.1.3-JL'.
+ echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
+ exit 0 ;;
+ sun3*:SunOS:*:*)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ exit 0 ;;
+ sun*:*:4.2BSD:*)
+ UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+ test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
+ case "`/bin/arch`" in
+ sun3)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ ;;
+ sun4)
+ echo sparc-sun-sunos${UNAME_RELEASE}
+ ;;
+ esac
+ exit 0 ;;
+ aushp:SunOS:*:*)
+ echo sparc-auspex-sunos${UNAME_RELEASE}
+ exit 0 ;;
+ # The situation for MiNT is a little confusing. The machine name
+ # can be virtually everything (everything which is not
+ # "atarist" or "atariste" at least should have a processor
+ # > m68000). The system name ranges from "MiNT" over "FreeMiNT"
+ # to the lowercase version "mint" (or "freemint"). Finally
+ # the system name "TOS" denotes a system which is actually not
+ # MiNT. But MiNT is downward compatible to TOS, so this should
+ # be no problem.
+ atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit 0 ;;
+ atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit 0 ;;
+ *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit 0 ;;
+ milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+ echo m68k-milan-mint${UNAME_RELEASE}
+ exit 0 ;;
+ hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+ echo m68k-hades-mint${UNAME_RELEASE}
+ exit 0 ;;
+ *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+ echo m68k-unknown-mint${UNAME_RELEASE}
+ exit 0 ;;
+ m68k:machten:*:*)
+ echo m68k-apple-machten${UNAME_RELEASE}
+ exit 0 ;;
+ powerpc:machten:*:*)
+ echo powerpc-apple-machten${UNAME_RELEASE}
+ exit 0 ;;
+ RISC*:Mach:*:*)
+ echo mips-dec-mach_bsd4.3
+ exit 0 ;;
+ RISC*:ULTRIX:*:*)
+ echo mips-dec-ultrix${UNAME_RELEASE}
+ exit 0 ;;
+ VAX*:ULTRIX*:*:*)
+ echo vax-dec-ultrix${UNAME_RELEASE}
+ exit 0 ;;
+ 2020:CLIX:*:* | 2430:CLIX:*:*)
+ echo clipper-intergraph-clix${UNAME_RELEASE}
+ exit 0 ;;
+ mips:*:*:UMIPS | mips:*:*:RISCos)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+#ifdef __cplusplus
+#include <stdio.h> /* for printf() prototype */
+ int main (int argc, char *argv[]) {
+#else
+ int main (argc, argv) int argc; char *argv[]; {
+#endif
+ #if defined (host_mips) && defined (MIPSEB)
+ #if defined (SYSTYPE_SYSV)
+ printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_SVR4)
+ printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+ printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
+ #endif
+ #endif
+ exit (-1);
+ }
+EOF
+ $CC_FOR_BUILD -o $dummy $dummy.c \
+ && $dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \
+ && exit 0
+ echo mips-mips-riscos${UNAME_RELEASE}
+ exit 0 ;;
+ Motorola:PowerMAX_OS:*:*)
+ echo powerpc-motorola-powermax
+ exit 0 ;;
+ Motorola:*:4.3:PL8-*)
+ echo powerpc-harris-powermax
+ exit 0 ;;
+ Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
+ echo powerpc-harris-powermax
+ exit 0 ;;
+ Night_Hawk:Power_UNIX:*:*)
+ echo powerpc-harris-powerunix
+ exit 0 ;;
+ m88k:CX/UX:7*:*)
+ echo m88k-harris-cxux7
+ exit 0 ;;
+ m88k:*:4*:R4*)
+ echo m88k-motorola-sysv4
+ exit 0 ;;
+ m88k:*:3*:R3*)
+ echo m88k-motorola-sysv3
+ exit 0 ;;
+ AViiON:dgux:*:*)
+ # DG/UX returns AViiON for all architectures
+ UNAME_PROCESSOR=`/usr/bin/uname -p`
+ if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
+ then
+ if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
+ [ ${TARGET_BINARY_INTERFACE}x = x ]
+ then
+ echo m88k-dg-dgux${UNAME_RELEASE}
+ else
+ echo m88k-dg-dguxbcs${UNAME_RELEASE}
+ fi
+ else
+ echo i586-dg-dgux${UNAME_RELEASE}
+ fi
+ exit 0 ;;
+ M88*:DolphinOS:*:*) # DolphinOS (SVR3)
+ echo m88k-dolphin-sysv3
+ exit 0 ;;
+ M88*:*:R3*:*)
+ # Delta 88k system running SVR3
+ echo m88k-motorola-sysv3
+ exit 0 ;;
+ XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+ echo m88k-tektronix-sysv3
+ exit 0 ;;
+ Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+ echo m68k-tektronix-bsd
+ exit 0 ;;
+ *:IRIX*:*:*)
+ echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
+ exit 0 ;;
+ ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+ echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id
+ exit 0 ;; # Note that: echo "'`uname -s`'" gives 'AIX '
+ i*86:AIX:*:*)
+ echo i386-ibm-aix
+ exit 0 ;;
+ ia64:AIX:*:*)
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+ fi
+ echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
+ exit 0 ;;
+ *:AIX:2:3)
+ if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <sys/systemcfg.h>
+
+ main()
+ {
+ if (!__power_pc())
+ exit(1);
+ puts("powerpc-ibm-aix3.2.5");
+ exit(0);
+ }
+EOF
+ $CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0
+ echo rs6000-ibm-aix3.2.5
+ elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+ echo rs6000-ibm-aix3.2.4
+ else
+ echo rs6000-ibm-aix3.2
+ fi
+ exit 0 ;;
+ *:AIX:*:[45])
+ IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
+ if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
+ IBM_ARCH=rs6000
+ else
+ IBM_ARCH=powerpc
+ fi
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+ fi
+ echo ${IBM_ARCH}-ibm-aix${IBM_REV}
+ exit 0 ;;
+ *:AIX:*:*)
+ echo rs6000-ibm-aix
+ exit 0 ;;
+ ibmrt:4.4BSD:*|romp-ibm:BSD:*)
+ echo romp-ibm-bsd4.4
+ exit 0 ;;
+ ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and
+ echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to
+ exit 0 ;; # report: romp-ibm BSD 4.3
+ *:BOSX:*:*)
+ echo rs6000-bull-bosx
+ exit 0 ;;
+ DPX/2?00:B.O.S.:*:*)
+ echo m68k-bull-sysv3
+ exit 0 ;;
+ 9000/[34]??:4.3bsd:1.*:*)
+ echo m68k-hp-bsd
+ exit 0 ;;
+ hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+ echo m68k-hp-bsd4.4
+ exit 0 ;;
+ 9000/[34678]??:HP-UX:*:*)
+ HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+ case "${UNAME_MACHINE}" in
+ 9000/31? ) HP_ARCH=m68000 ;;
+ 9000/[34]?? ) HP_ARCH=m68k ;;
+ 9000/[678][0-9][0-9])
+ if [ -x /usr/bin/getconf ]; then
+ sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+ sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+ case "${sc_cpu_version}" in
+ 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
+ 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
+ 532) # CPU_PA_RISC2_0
+ case "${sc_kernel_bits}" in
+ 32) HP_ARCH="hppa2.0n" ;;
+ 64) HP_ARCH="hppa2.0w" ;;
+ '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20
+ esac ;;
+ esac
+ fi
+ if [ "${HP_ARCH}" = "" ]; then
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+
+ #define _HPUX_SOURCE
+ #include <stdlib.h>
+ #include <unistd.h>
+
+ int main ()
+ {
+ #if defined(_SC_KERNEL_BITS)
+ long bits = sysconf(_SC_KERNEL_BITS);
+ #endif
+ long cpu = sysconf (_SC_CPU_VERSION);
+
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+ case CPU_PA_RISC2_0:
+ #if defined(_SC_KERNEL_BITS)
+ switch (bits)
+ {
+ case 64: puts ("hppa2.0w"); break;
+ case 32: puts ("hppa2.0n"); break;
+ default: puts ("hppa2.0"); break;
+ } break;
+ #else /* !defined(_SC_KERNEL_BITS) */
+ puts ("hppa2.0"); break;
+ #endif
+ default: puts ("hppa1.0"); break;
+ }
+ exit (0);
+ }
+EOF
+ (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
+ test -z "$HP_ARCH" && HP_ARCH=hppa
+ fi ;;
+ esac
+ if [ ${HP_ARCH} = "hppa2.0w" ]
+ then
+ # avoid double evaluation of $set_cc_for_build
+ test -n "$CC_FOR_BUILD" || eval $set_cc_for_build
+ if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E -) | grep __LP64__ >/dev/null
+ then
+ HP_ARCH="hppa2.0w"
+ else
+ HP_ARCH="hppa64"
+ fi
+ fi
+ echo ${HP_ARCH}-hp-hpux${HPUX_REV}
+ exit 0 ;;
+ ia64:HP-UX:*:*)
+ HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+ echo ia64-hp-hpux${HPUX_REV}
+ exit 0 ;;
+ 3050*:HI-UX:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <unistd.h>
+ int
+ main ()
+ {
+ long cpu = sysconf (_SC_CPU_VERSION);
+ /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+ true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct
+ results, however. */
+ if (CPU_IS_PA_RISC (cpu))
+ {
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+ default: puts ("hppa-hitachi-hiuxwe2"); break;
+ }
+ }
+ else if (CPU_IS_HP_MC68K (cpu))
+ puts ("m68k-hitachi-hiuxwe2");
+ else puts ("unknown-hitachi-hiuxwe2");
+ exit (0);
+ }
+EOF
+ $CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0
+ echo unknown-hitachi-hiuxwe2
+ exit 0 ;;
+ 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
+ echo hppa1.1-hp-bsd
+ exit 0 ;;
+ 9000/8??:4.3bsd:*:*)
+ echo hppa1.0-hp-bsd
+ exit 0 ;;
+ *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
+ echo hppa1.0-hp-mpeix
+ exit 0 ;;
+ hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
+ echo hppa1.1-hp-osf
+ exit 0 ;;
+ hp8??:OSF1:*:*)
+ echo hppa1.0-hp-osf
+ exit 0 ;;
+ i*86:OSF1:*:*)
+ if [ -x /usr/sbin/sysversion ] ; then
+ echo ${UNAME_MACHINE}-unknown-osf1mk
+ else
+ echo ${UNAME_MACHINE}-unknown-osf1
+ fi
+ exit 0 ;;
+ parisc*:Lites*:*:*)
+ echo hppa1.1-hp-lites
+ exit 0 ;;
+ C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+ echo c1-convex-bsd
+ exit 0 ;;
+ C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit 0 ;;
+ C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+ echo c34-convex-bsd
+ exit 0 ;;
+ C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+ echo c38-convex-bsd
+ exit 0 ;;
+ C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+ echo c4-convex-bsd
+ exit 0 ;;
+ CRAY*Y-MP:*:*:*)
+ echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ CRAY*[A-Z]90:*:*:*)
+ echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
+ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
+ -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ CRAY*TS:*:*:*)
+ echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ CRAY*T3E:*:*:*)
+ echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ CRAY*SV1:*:*:*)
+ echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ *:UNICOS/mp:*:*)
+ echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+ FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+ FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+ FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
+ echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit 0 ;;
+ 5000:UNIX_System_V:4.*:*)
+ FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+ FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'`
+ echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit 0 ;;
+ i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+ echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
+ exit 0 ;;
+ sparc*:BSD/OS:*:*)
+ echo sparc-unknown-bsdi${UNAME_RELEASE}
+ exit 0 ;;
+ *:BSD/OS:*:*)
+ echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
+ exit 0 ;;
+ *:FreeBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+ exit 0 ;;
+ i*:CYGWIN*:*)
+ echo ${UNAME_MACHINE}-pc-cygwin
+ exit 0 ;;
+ i*:MINGW*:*)
+ echo ${UNAME_MACHINE}-pc-mingw32
+ exit 0 ;;
+ i*:PW*:*)
+ echo ${UNAME_MACHINE}-pc-pw32
+ exit 0 ;;
+ x86:Interix*:[34]*)
+ echo i586-pc-interix${UNAME_RELEASE}|sed -e 's/\..*//'
+ exit 0 ;;
+ [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
+ echo i${UNAME_MACHINE}-pc-mks
+ exit 0 ;;
+ i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
+ # How do we know it's Interix rather than the generic POSIX subsystem?
+ # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
+ # UNAME_MACHINE based on the output of uname instead of i386?
+ echo i586-pc-interix
+ exit 0 ;;
+ i*:UWIN*:*)
+ echo ${UNAME_MACHINE}-pc-uwin
+ exit 0 ;;
+ p*:CYGWIN*:*)
+ echo powerpcle-unknown-cygwin
+ exit 0 ;;
+ prep*:SunOS:5.*:*)
+ echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ *:GNU:*:*)
+ # the GNU system
+ echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+ exit 0 ;;
+ *:GNU/*:*:*)
+ # other systems with GNU libc and userland
+ echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu
+ exit 0 ;;
+ i*86:Minix:*:*)
+ echo ${UNAME_MACHINE}-pc-minix
+ exit 0 ;;
+ arm*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ cris:Linux:*:*)
+ echo cris-axis-linux-gnu
+ exit 0 ;;
+ ia64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ m32r*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ m68*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ mips:Linux:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #undef CPU
+ #undef mips
+ #undef mipsel
+ #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+ CPU=mipsel
+ #else
+ #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+ CPU=mips
+ #else
+ CPU=
+ #endif
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=`
+ test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0
+ ;;
+ mips64:Linux:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #undef CPU
+ #undef mips64
+ #undef mips64el
+ #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+ CPU=mips64el
+ #else
+ #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+ CPU=mips64
+ #else
+ CPU=
+ #endif
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=`
+ test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0
+ ;;
+ ppc:Linux:*:*)
+ echo powerpc-unknown-linux-gnu
+ exit 0 ;;
+ ppc64:Linux:*:*)
+ echo powerpc64-unknown-linux-gnu
+ exit 0 ;;
+ alpha:Linux:*:*)
+ case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
+ EV5) UNAME_MACHINE=alphaev5 ;;
+ EV56) UNAME_MACHINE=alphaev56 ;;
+ PCA56) UNAME_MACHINE=alphapca56 ;;
+ PCA57) UNAME_MACHINE=alphapca56 ;;
+ EV6) UNAME_MACHINE=alphaev6 ;;
+ EV67) UNAME_MACHINE=alphaev67 ;;
+ EV68*) UNAME_MACHINE=alphaev68 ;;
+ esac
+ objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null
+ if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
+ echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
+ exit 0 ;;
+ parisc:Linux:*:* | hppa:Linux:*:*)
+ # Look for CPU level
+ case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+ PA7*) echo hppa1.1-unknown-linux-gnu ;;
+ PA8*) echo hppa2.0-unknown-linux-gnu ;;
+ *) echo hppa-unknown-linux-gnu ;;
+ esac
+ exit 0 ;;
+ parisc64:Linux:*:* | hppa64:Linux:*:*)
+ echo hppa64-unknown-linux-gnu
+ exit 0 ;;
+ s390:Linux:*:* | s390x:Linux:*:*)
+ echo ${UNAME_MACHINE}-ibm-linux
+ exit 0 ;;
+ sh64*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ sh*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ sparc:Linux:*:* | sparc64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ x86_64:Linux:*:*)
+ echo x86_64-unknown-linux-gnu
+ exit 0 ;;
+ i*86:Linux:*:*)
+ # The BFD linker knows what the default object file format is, so
+ # first see if it will tell us. cd to the root directory to prevent
+ # problems with other programs or directories called `ld' in the path.
+ # Set LC_ALL=C to ensure ld outputs messages in English.
+ ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \
+ | sed -ne '/supported targets:/!d
+ s/[ ][ ]*/ /g
+ s/.*supported targets: *//
+ s/ .*//
+ p'`
+ case "$ld_supported_targets" in
+ elf32-i386)
+ TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu"
+ ;;
+ a.out-i386-linux)
+ echo "${UNAME_MACHINE}-pc-linux-gnuaout"
+ exit 0 ;;
+ coff-i386)
+ echo "${UNAME_MACHINE}-pc-linux-gnucoff"
+ exit 0 ;;
+ "")
+ # Either a pre-BFD a.out linker (linux-gnuoldld) or
+ # one that does not give us useful --help.
+ echo "${UNAME_MACHINE}-pc-linux-gnuoldld"
+ exit 0 ;;
+ esac
+ # Determine whether the default compiler is a.out or elf
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <features.h>
+ #ifdef __ELF__
+ # ifdef __GLIBC__
+ # if __GLIBC__ >= 2
+ LIBC=gnu
+ # else
+ LIBC=gnulibc1
+ # endif
+ # else
+ LIBC=gnulibc1
+ # endif
+ #else
+ #ifdef __INTEL_COMPILER
+ LIBC=gnu
+ #else
+ LIBC=gnuaout
+ #endif
+ #endif
+ #ifdef __dietlibc__
+ LIBC=dietlibc
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=`
+ test x"${LIBC}" != x && echo "${UNAME_MACHINE}-pc-linux-${LIBC}" && exit 0
+ test x"${TENTATIVE}" != x && echo "${TENTATIVE}" && exit 0
+ ;;
+ i*86:DYNIX/ptx:4*:*)
+ # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+ # earlier versions are messed up and put the nodename in both
+ # sysname and nodename.
+ echo i386-sequent-sysv4
+ exit 0 ;;
+ i*86:UNIX_SV:4.2MP:2.*)
+ # Unixware is an offshoot of SVR4, but it has its own version
+ # number series starting with 2...
+ # I am not positive that other SVR4 systems won't match this,
+ # I just have to hope. -- rms.
+ # Use sysv4.2uw... so that sysv4* matches it.
+ echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
+ exit 0 ;;
+ i*86:OS/2:*:*)
+ # If we were able to find `uname', then EMX Unix compatibility
+ # is probably installed.
+ echo ${UNAME_MACHINE}-pc-os2-emx
+ exit 0 ;;
+ i*86:XTS-300:*:STOP)
+ echo ${UNAME_MACHINE}-unknown-stop
+ exit 0 ;;
+ i*86:atheos:*:*)
+ echo ${UNAME_MACHINE}-unknown-atheos
+ exit 0 ;;
+ i*86:syllable:*:*)
+ echo ${UNAME_MACHINE}-pc-syllable
+ exit 0 ;;
+ i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*)
+ echo i386-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ i*86:*DOS:*:*)
+ echo ${UNAME_MACHINE}-pc-msdosdjgpp
+ exit 0 ;;
+ i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
+ UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
+ if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+ echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
+ else
+ echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
+ fi
+ exit 0 ;;
+ i*86:*:5:[78]*)
+ case `/bin/uname -X | grep "^Machine"` in
+ *486*) UNAME_MACHINE=i486 ;;
+ *Pentium) UNAME_MACHINE=i586 ;;
+ *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
+ esac
+ echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
+ exit 0 ;;
+ i*86:*:3.2:*)
+ if test -f /usr/options/cb.name; then
+ UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+ echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
+ elif /bin/uname -X 2>/dev/null >/dev/null ; then
+ UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
+ (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
+ (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
+ && UNAME_MACHINE=i586
+ (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
+ && UNAME_MACHINE=i686
+ (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
+ && UNAME_MACHINE=i686
+ echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
+ else
+ echo ${UNAME_MACHINE}-pc-sysv32
+ fi
+ exit 0 ;;
+ pc:*:*:*)
+ # Left here for compatibility:
+ # uname -m prints for DJGPP always 'pc', but it prints nothing about
+ # the processor, so we play safe by assuming i386.
+ echo i386-pc-msdosdjgpp
+ exit 0 ;;
+ Intel:Mach:3*:*)
+ echo i386-pc-mach3
+ exit 0 ;;
+ paragon:*:*:*)
+ echo i860-intel-osf1
+ exit 0 ;;
+ i860:*:4.*:*) # i860-SVR4
+ if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+ echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
+ else # Add other i860-SVR4 vendors below as they are discovered.
+ echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4
+ fi
+ exit 0 ;;
+ mini*:CTIX:SYS*5:*)
+ # "miniframe"
+ echo m68010-convergent-sysv
+ exit 0 ;;
+ mc68k:UNIX:SYSTEM5:3.51m)
+ echo m68k-convergent-sysv
+ exit 0 ;;
+ M680?0:D-NIX:5.3:*)
+ echo m68k-diab-dnix
+ exit 0 ;;
+ M68*:*:R3V[5678]*:*)
+ test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;;
+ 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0)
+ OS_REL=''
+ test -r /etc/.relid \
+ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && echo i486-ncr-sysv4.3${OS_REL} && exit 0
+ /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+ && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;;
+ 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && echo i486-ncr-sysv4 && exit 0 ;;
+ m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+ echo m68k-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ mc68030:UNIX_System_V:4.*:*)
+ echo m68k-atari-sysv4
+ exit 0 ;;
+ TSUNAMI:LynxOS:2.*:*)
+ echo sparc-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ rs6000:LynxOS:2.*:*)
+ echo rs6000-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*)
+ echo powerpc-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ SM[BE]S:UNIX_SV:*:*)
+ echo mips-dde-sysv${UNAME_RELEASE}
+ exit 0 ;;
+ RM*:ReliantUNIX-*:*:*)
+ echo mips-sni-sysv4
+ exit 0 ;;
+ RM*:SINIX-*:*:*)
+ echo mips-sni-sysv4
+ exit 0 ;;
+ *:SINIX-*:*:*)
+ if uname -p 2>/dev/null >/dev/null ; then
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ echo ${UNAME_MACHINE}-sni-sysv4
+ else
+ echo ns32k-sni-sysv
+ fi
+ exit 0 ;;
+ PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+ # says <Richard.M.Bartel@ccMail.Census.GOV>
+ echo i586-unisys-sysv4
+ exit 0 ;;
+ *:UNIX_System_V:4*:FTX*)
+ # From Gerald Hewes <hewes@openmarket.com>.
+ # How about differentiating between stratus architectures? -djm
+ echo hppa1.1-stratus-sysv4
+ exit 0 ;;
+ *:*:*:FTX*)
+ # From seanf@swdc.stratus.com.
+ echo i860-stratus-sysv4
+ exit 0 ;;
+ *:VOS:*:*)
+ # From Paul.Green@stratus.com.
+ echo hppa1.1-stratus-vos
+ exit 0 ;;
+ mc68*:A/UX:*:*)
+ echo m68k-apple-aux${UNAME_RELEASE}
+ exit 0 ;;
+ news*:NEWS-OS:6*:*)
+ echo mips-sony-newsos6
+ exit 0 ;;
+ R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+ if [ -d /usr/nec ]; then
+ echo mips-nec-sysv${UNAME_RELEASE}
+ else
+ echo mips-unknown-sysv${UNAME_RELEASE}
+ fi
+ exit 0 ;;
+ BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.
+ echo powerpc-be-beos
+ exit 0 ;;
+ BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only.
+ echo powerpc-apple-beos
+ exit 0 ;;
+ BePC:BeOS:*:*) # BeOS running on Intel PC compatible.
+ echo i586-pc-beos
+ exit 0 ;;
+ SX-4:SUPER-UX:*:*)
+ echo sx4-nec-superux${UNAME_RELEASE}
+ exit 0 ;;
+ SX-5:SUPER-UX:*:*)
+ echo sx5-nec-superux${UNAME_RELEASE}
+ exit 0 ;;
+ SX-6:SUPER-UX:*:*)
+ echo sx6-nec-superux${UNAME_RELEASE}
+ exit 0 ;;
+ Power*:Rhapsody:*:*)
+ echo powerpc-apple-rhapsody${UNAME_RELEASE}
+ exit 0 ;;
+ *:Rhapsody:*:*)
+ echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
+ exit 0 ;;
+ *:Darwin:*:*)
+ case `uname -p` in
+ *86) UNAME_PROCESSOR=i686 ;;
+ powerpc) UNAME_PROCESSOR=powerpc ;;
+ esac
+ echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
+ exit 0 ;;
+ *:procnto*:*:* | *:QNX:[0123456789]*:*)
+ UNAME_PROCESSOR=`uname -p`
+ if test "$UNAME_PROCESSOR" = "x86"; then
+ UNAME_PROCESSOR=i386
+ UNAME_MACHINE=pc
+ fi
+ echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE}
+ exit 0 ;;
+ *:QNX:*:4*)
+ echo i386-pc-qnx
+ exit 0 ;;
+ NSR-?:NONSTOP_KERNEL:*:*)
+ echo nsr-tandem-nsk${UNAME_RELEASE}
+ exit 0 ;;
+ *:NonStop-UX:*:*)
+ echo mips-compaq-nonstopux
+ exit 0 ;;
+ BS2000:POSIX*:*:*)
+ echo bs2000-siemens-sysv
+ exit 0 ;;
+ DS/*:UNIX_System_V:*:*)
+ echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
+ exit 0 ;;
+ *:Plan9:*:*)
+ # "uname -m" is not consistent, so use $cputype instead. 386
+ # is converted to i386 for consistency with other x86
+ # operating systems.
+ if test "$cputype" = "386"; then
+ UNAME_MACHINE=i386
+ else
+ UNAME_MACHINE="$cputype"
+ fi
+ echo ${UNAME_MACHINE}-unknown-plan9
+ exit 0 ;;
+ *:TOPS-10:*:*)
+ echo pdp10-unknown-tops10
+ exit 0 ;;
+ *:TENEX:*:*)
+ echo pdp10-unknown-tenex
+ exit 0 ;;
+ KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+ echo pdp10-dec-tops20
+ exit 0 ;;
+ XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+ echo pdp10-xkl-tops20
+ exit 0 ;;
+ *:TOPS-20:*:*)
+ echo pdp10-unknown-tops20
+ exit 0 ;;
+ *:ITS:*:*)
+ echo pdp10-unknown-its
+ exit 0 ;;
+ SEI:*:*:SEIUX)
+ echo mips-sei-seiux${UNAME_RELEASE}
+ exit 0 ;;
+ *:DragonFly:*:*)
+ echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+ exit 0 ;;
+ *:*VMS:*:*)
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ case "${UNAME_MACHINE}" in
+ A*) echo alpha-dec-vms && exit 0 ;;
+ I*) echo ia64-dec-vms && exit 0 ;;
+ V*) echo vax-dec-vms && exit 0 ;;
+ esac
+esac
+
+#echo '(No uname command or uname output not recognized.)' 1>&2
+#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
+
+eval $set_cc_for_build
+cat >$dummy.c <<EOF
+#ifdef _SEQUENT_
+# include <sys/types.h>
+# include <sys/utsname.h>
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+ /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed,
+ I don't know.... */
+ printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+ printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+ "4"
+#else
+ ""
+#endif
+ ); exit (0);
+#endif
+#endif
+
+#if defined (__arm) && defined (__acorn) && defined (__unix)
+ printf ("arm-acorn-riscix"); exit (0);
+#endif
+
+#if defined (hp300) && !defined (hpux)
+ printf ("m68k-hp-bsd\n"); exit (0);
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+ int version;
+ version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+ if (version < 4)
+ printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+ else
+ printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+ exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+ printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+ printf ("ns32k-encore-mach\n"); exit (0);
+#else
+ printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+ printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+ printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+ printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+ struct utsname un;
+
+ uname(&un);
+
+ if (strncmp(un.version, "V2", 2) == 0) {
+ printf ("i386-sequent-ptx2\n"); exit (0);
+ }
+ if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+ printf ("i386-sequent-ptx1\n"); exit (0);
+ }
+ printf ("i386-sequent-ptx\n"); exit (0);
+
+#endif
+
+#if defined (vax)
+# if !defined (ultrix)
+# include <sys/param.h>
+# if defined (BSD)
+# if BSD == 43
+ printf ("vax-dec-bsd4.3\n"); exit (0);
+# else
+# if BSD == 199006
+ printf ("vax-dec-bsd4.3reno\n"); exit (0);
+# else
+ printf ("vax-dec-bsd\n"); exit (0);
+# endif
+# endif
+# else
+ printf ("vax-dec-bsd\n"); exit (0);
+# endif
+# else
+ printf ("vax-dec-ultrix\n"); exit (0);
+# endif
+#endif
+
+#if defined (alliant) && defined (i860)
+ printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+ exit (1);
+}
+EOF
+
+$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && $dummy && exit 0
+
+# Apollos put the system type in the environment.
+
+test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; }
+
+# Convex versions that predate uname can use getsysinfo(1)
+
+if [ -x /usr/convex/getsysinfo ]
+then
+ case `getsysinfo -f cpu_type` in
+ c1*)
+ echo c1-convex-bsd
+ exit 0 ;;
+ c2*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit 0 ;;
+ c34*)
+ echo c34-convex-bsd
+ exit 0 ;;
+ c38*)
+ echo c38-convex-bsd
+ exit 0 ;;
+ c4*)
+ echo c4-convex-bsd
+ exit 0 ;;
+ esac
+fi
+
+cat >&2 <<EOF
+$0: unable to guess system type
+
+This script, last modified $timestamp, has failed to recognize
+the operating system you are using. It is advised that you
+download the most up to date version of the config scripts from
+
+ ftp://ftp.gnu.org/pub/gnu/config/
+
+If the version you run ($0) is already up to date, please
+send the following data and any information you think might be
+pertinent to <config-patches@gnu.org> in order to provide the needed
+information to handle your system.
+
+config.guess timestamp = $timestamp
+
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null`
+
+hostinfo = `(hostinfo) 2>/dev/null`
+/bin/universe = `(/bin/universe) 2>/dev/null`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null`
+/bin/arch = `(/bin/arch) 2>/dev/null`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
+
+UNAME_MACHINE = ${UNAME_MACHINE}
+UNAME_RELEASE = ${UNAME_RELEASE}
+UNAME_SYSTEM = ${UNAME_SYSTEM}
+UNAME_VERSION = ${UNAME_VERSION}
+EOF
+
+exit 1
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/trunk/main/editline/config.h.in b/trunk/main/editline/config.h.in
new file mode 100644
index 000000000..151fb226d
--- /dev/null
+++ b/trunk/main/editline/config.h.in
@@ -0,0 +1,21 @@
+#undef SUNOS
+#undef CYGWIN
+
+#undef HAVE_SYS_CDEFS_H
+#undef HAVE_TERMCAP_H
+#undef HAVE_CURSES_H
+#undef HAVE_NCURSES_H
+#undef HAVE_TERM_H
+#undef HAVE_VIS_H
+#undef HAVE_ISSETUGID
+
+#undef HAVE_STRLCAT
+#undef HAVE_STRLCPY
+#undef HAVE_FGETLN
+#undef HAVE_STRVIS
+#undef HAVE_STRUNVIS
+
+#include "sys.h"
+#ifdef CYGWIN
+# include "cygdef.h"
+#endif
diff --git a/trunk/main/editline/config.sub b/trunk/main/editline/config.sub
new file mode 100755
index 000000000..838237e98
--- /dev/null
+++ b/trunk/main/editline/config.sub
@@ -0,0 +1,1412 @@
+#! /bin/sh
+# Configuration validation subroutine script.
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+# Free Software Foundation, Inc.
+
+timestamp='2001-09-14'
+
+# This file is (in principle) common to ALL GNU software.
+# The presence of a machine in this file suggests that SOME GNU software
+# can handle that machine. It does not imply ALL GNU software can.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Please send patches to <config-patches@gnu.org>.
+#
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support. The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION] CPU-MFR-OPSYS
+ $0 [OPTION] ALIAS
+
+Canonicalize a configuration name.
+
+Operation modes:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.sub ($timestamp)
+
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit 0 ;;
+ --version | -v )
+ echo "$version" ; exit 0 ;;
+ --help | --h* | -h )
+ echo "$usage"; exit 0 ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help"
+ exit 1 ;;
+
+ *local*)
+ # First pass through any local machine types.
+ echo $1
+ exit 0;;
+
+ * )
+ break ;;
+ esac
+done
+
+case $# in
+ 0) echo "$me: missing argument$help" >&2
+ exit 1;;
+ 1) ;;
+ *) echo "$me: too many arguments$help" >&2
+ exit 1;;
+esac
+
+# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
+# Here we must recognize all the valid KERNEL-OS combinations.
+maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
+case $maybe_os in
+ nto-qnx* | linux-gnu* | storm-chaos* | os2-emx* | windows32-*)
+ os=-$maybe_os
+ basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
+ ;;
+ *)
+ basic_machine=`echo $1 | sed 's/-[^-]*$//'`
+ if [ $basic_machine != $1 ]
+ then os=`echo $1 | sed 's/.*-/-/'`
+ else os=; fi
+ ;;
+esac
+
+### Let's recognize common machines as not being operating systems so
+### that things like config.sub decstation-3100 work. We also
+### recognize some manufacturers as not being operating systems, so we
+### can provide default operating systems below.
+case $os in
+ -sun*os*)
+ # Prevent following clause from handling this invalid input.
+ ;;
+ -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
+ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
+ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
+ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
+ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
+ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
+ -apple | -axis)
+ os=
+ basic_machine=$1
+ ;;
+ -sim | -cisco | -oki | -wec | -winbond)
+ os=
+ basic_machine=$1
+ ;;
+ -scout)
+ ;;
+ -wrs)
+ os=-vxworks
+ basic_machine=$1
+ ;;
+ -chorusos*)
+ os=-chorusos
+ basic_machine=$1
+ ;;
+ -chorusrdb)
+ os=-chorusrdb
+ basic_machine=$1
+ ;;
+ -hiux*)
+ os=-hiuxwe2
+ ;;
+ -sco5)
+ os=-sco3.2v5
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco4)
+ os=-sco3.2v4
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco3.2.[4-9]*)
+ os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco3.2v[4-9]*)
+ # Don't forget version if it is 3.2v4 or newer.
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco*)
+ os=-sco3.2v2
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -udk*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -isc)
+ os=-isc2.2
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -clix*)
+ basic_machine=clipper-intergraph
+ ;;
+ -isc*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -lynx*)
+ os=-lynxos
+ ;;
+ -ptx*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
+ ;;
+ -windowsnt*)
+ os=`echo $os | sed -e 's/windowsnt/winnt/'`
+ ;;
+ -psos*)
+ os=-psos
+ ;;
+ -mint | -mint[0-9]*)
+ basic_machine=m68k-atari
+ os=-mint
+ ;;
+esac
+
+# Decode aliases for certain CPU-COMPANY combinations.
+case $basic_machine in
+ # Recognize the basic CPU types without company name.
+ # Some are omitted here because they have special meanings below.
+ 1750a | 580 \
+ | a29k \
+ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
+ | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \
+ | c4x | clipper \
+ | d10v | d30v | dsp16xx \
+ | fr30 \
+ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+ | i370 | i860 | i960 | ia64 \
+ | m32r | m68000 | m68k | m88k | mcore \
+ | mips16 | mips64 | mips64el | mips64orion | mips64orionel \
+ | mips64vr4100 | mips64vr4100el | mips64vr4300 \
+ | mips64vr4300el | mips64vr5000 | mips64vr5000el \
+ | mipsbe | mipseb | mipsel | mipsle | mipstx39 | mipstx39el \
+ | mipsisa32 \
+ | mn10200 | mn10300 \
+ | ns16k | ns32k \
+ | openrisc \
+ | pdp10 | pdp11 | pj | pjl \
+ | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
+ | pyramid \
+ | s390 | s390x \
+ | sh | sh[34] | sh[34]eb | shbe | shle \
+ | sparc | sparc64 | sparclet | sparclite | sparcv9 | sparcv9b \
+ | stormy16 | strongarm \
+ | tahoe | thumb | tic80 | tron \
+ | v850 \
+ | we32k \
+ | x86 | xscale \
+ | z8k)
+ basic_machine=$basic_machine-unknown
+ ;;
+ m6811 | m68hc11 | m6812 | m68hc12)
+ # Motorola 68HC11/12.
+ basic_machine=$basic_machine-unknown
+ os=-none
+ ;;
+ m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
+ ;;
+
+ # We use `pc' rather than `unknown'
+ # because (1) that's what they normally are, and
+ # (2) the word "unknown" tends to confuse beginning users.
+ i*86 | x86_64)
+ basic_machine=$basic_machine-pc
+ ;;
+ # Object if more than one company name word.
+ *-*-*)
+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+ exit 1
+ ;;
+ # Recognize the basic CPU types with company name.
+ 580-* \
+ | a29k-* \
+ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
+ | alphapca5[67]-* | arc-* \
+ | arm-* | armbe-* | armle-* | armv*-* \
+ | avr-* \
+ | bs2000-* \
+ | c[123]* | c30-* | [cjt]90-* | c54x-* \
+ | clipper-* | cray2-* | cydra-* \
+ | d10v-* | d30v-* \
+ | elxsi-* \
+ | f30[01]-* | f700-* | fr30-* | fx80-* \
+ | h8300-* | h8500-* \
+ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
+ | i*86-* | i860-* | i960-* | ia64-* \
+ | m32r-* \
+ | m68000-* | m680[01234]0-* | m68360-* | m683?2-* | m68k-* \
+ | m88110-* | m88k-* | mcore-* \
+ | mips-* | mips16-* | mips64-* | mips64el-* | mips64orion-* \
+ | mips64orionel-* | mips64vr4100-* | mips64vr4100el-* \
+ | mips64vr4300-* | mips64vr4300el-* | mipsbe-* | mipseb-* \
+ | mipsle-* | mipsel-* | mipstx39-* | mipstx39el-* \
+ | none-* | np1-* | ns16k-* | ns32k-* \
+ | orion-* \
+ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
+ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
+ | pyramid-* \
+ | romp-* | rs6000-* \
+ | s390-* | s390x-* \
+ | sh-* | sh[34]-* | sh[34]eb-* | shbe-* | shle-* \
+ | sparc-* | sparc64-* | sparc86x-* | sparclite-* \
+ | sparcv9-* | sparcv9b-* | stormy16-* | strongarm-* | sv1-* \
+ | t3e-* | tahoe-* | thumb-* | tic30-* | tic54x-* | tic80-* | tron-* \
+ | v850-* | vax-* \
+ | we32k-* \
+ | x86-* | x86_64-* | xmp-* | xps100-* | xscale-* \
+ | ymp-* \
+ | z8k-*)
+ ;;
+ # Recognize the various machine names and aliases which stand
+ # for a CPU type and a company and sometimes even an OS.
+ 386bsd)
+ basic_machine=i386-unknown
+ os=-bsd
+ ;;
+ 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+ basic_machine=m68000-att
+ ;;
+ 3b*)
+ basic_machine=we32k-att
+ ;;
+ a29khif)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ adobe68k)
+ basic_machine=m68010-adobe
+ os=-scout
+ ;;
+ alliant | fx80)
+ basic_machine=fx80-alliant
+ ;;
+ altos | altos3068)
+ basic_machine=m68k-altos
+ ;;
+ am29k)
+ basic_machine=a29k-none
+ os=-bsd
+ ;;
+ amdahl)
+ basic_machine=580-amdahl
+ os=-sysv
+ ;;
+ amiga | amiga-*)
+ basic_machine=m68k-unknown
+ ;;
+ amigaos | amigados)
+ basic_machine=m68k-unknown
+ os=-amigaos
+ ;;
+ amigaunix | amix)
+ basic_machine=m68k-unknown
+ os=-sysv4
+ ;;
+ apollo68)
+ basic_machine=m68k-apollo
+ os=-sysv
+ ;;
+ apollo68bsd)
+ basic_machine=m68k-apollo
+ os=-bsd
+ ;;
+ aux)
+ basic_machine=m68k-apple
+ os=-aux
+ ;;
+ balance)
+ basic_machine=ns32k-sequent
+ os=-dynix
+ ;;
+ convex-c1)
+ basic_machine=c1-convex
+ os=-bsd
+ ;;
+ convex-c2)
+ basic_machine=c2-convex
+ os=-bsd
+ ;;
+ convex-c32)
+ basic_machine=c32-convex
+ os=-bsd
+ ;;
+ convex-c34)
+ basic_machine=c34-convex
+ os=-bsd
+ ;;
+ convex-c38)
+ basic_machine=c38-convex
+ os=-bsd
+ ;;
+ cray | ymp)
+ basic_machine=ymp-cray
+ os=-unicos
+ ;;
+ cray2)
+ basic_machine=cray2-cray
+ os=-unicos
+ ;;
+ [cjt]90)
+ basic_machine=${basic_machine}-cray
+ os=-unicos
+ ;;
+ crds | unos)
+ basic_machine=m68k-crds
+ ;;
+ cris | cris-* | etrax*)
+ basic_machine=cris-axis
+ ;;
+ da30 | da30-*)
+ basic_machine=m68k-da30
+ ;;
+ decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
+ basic_machine=mips-dec
+ ;;
+ delta | 3300 | motorola-3300 | motorola-delta \
+ | 3300-motorola | delta-motorola)
+ basic_machine=m68k-motorola
+ ;;
+ delta88)
+ basic_machine=m88k-motorola
+ os=-sysv3
+ ;;
+ dpx20 | dpx20-*)
+ basic_machine=rs6000-bull
+ os=-bosx
+ ;;
+ dpx2* | dpx2*-bull)
+ basic_machine=m68k-bull
+ os=-sysv3
+ ;;
+ ebmon29k)
+ basic_machine=a29k-amd
+ os=-ebmon
+ ;;
+ elxsi)
+ basic_machine=elxsi-elxsi
+ os=-bsd
+ ;;
+ encore | umax | mmax)
+ basic_machine=ns32k-encore
+ ;;
+ es1800 | OSE68k | ose68k | ose | OSE)
+ basic_machine=m68k-ericsson
+ os=-ose
+ ;;
+ fx2800)
+ basic_machine=i860-alliant
+ ;;
+ genix)
+ basic_machine=ns32k-ns
+ ;;
+ gmicro)
+ basic_machine=tron-gmicro
+ os=-sysv
+ ;;
+ go32)
+ basic_machine=i386-pc
+ os=-go32
+ ;;
+ h3050r* | hiux*)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ h8300hms)
+ basic_machine=h8300-hitachi
+ os=-hms
+ ;;
+ h8300xray)
+ basic_machine=h8300-hitachi
+ os=-xray
+ ;;
+ h8500hms)
+ basic_machine=h8500-hitachi
+ os=-hms
+ ;;
+ harris)
+ basic_machine=m88k-harris
+ os=-sysv3
+ ;;
+ hp300-*)
+ basic_machine=m68k-hp
+ ;;
+ hp300bsd)
+ basic_machine=m68k-hp
+ os=-bsd
+ ;;
+ hp300hpux)
+ basic_machine=m68k-hp
+ os=-hpux
+ ;;
+ hp3k9[0-9][0-9] | hp9[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hp9k2[0-9][0-9] | hp9k31[0-9])
+ basic_machine=m68000-hp
+ ;;
+ hp9k3[2-9][0-9])
+ basic_machine=m68k-hp
+ ;;
+ hp9k6[0-9][0-9] | hp6[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hp9k7[0-79][0-9] | hp7[0-79][0-9])
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k78[0-9] | hp78[0-9])
+ # FIXME: really hppa2.0-hp
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+ # FIXME: really hppa2.0-hp
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[0-9][13679] | hp8[0-9][13679])
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[0-9][0-9] | hp8[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hppa-next)
+ os=-nextstep3
+ ;;
+ hppaosf)
+ basic_machine=hppa1.1-hp
+ os=-osf
+ ;;
+ hppro)
+ basic_machine=hppa1.1-hp
+ os=-proelf
+ ;;
+ i370-ibm* | ibm*)
+ basic_machine=i370-ibm
+ ;;
+# I'm not sure what "Sysv32" means. Should this be sysv3.2?
+ i*86v32)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv32
+ ;;
+ i*86v4*)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv4
+ ;;
+ i*86v)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv
+ ;;
+ i*86sol2)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-solaris2
+ ;;
+ i386mach)
+ basic_machine=i386-mach
+ os=-mach
+ ;;
+ i386-vsta | vsta)
+ basic_machine=i386-unknown
+ os=-vsta
+ ;;
+ iris | iris4d)
+ basic_machine=mips-sgi
+ case $os in
+ -irix*)
+ ;;
+ *)
+ os=-irix4
+ ;;
+ esac
+ ;;
+ isi68 | isi)
+ basic_machine=m68k-isi
+ os=-sysv
+ ;;
+ m88k-omron*)
+ basic_machine=m88k-omron
+ ;;
+ magnum | m3230)
+ basic_machine=mips-mips
+ os=-sysv
+ ;;
+ merlin)
+ basic_machine=ns32k-utek
+ os=-sysv
+ ;;
+ mingw32)
+ basic_machine=i386-pc
+ os=-mingw32
+ ;;
+ miniframe)
+ basic_machine=m68000-convergent
+ ;;
+ *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
+ basic_machine=m68k-atari
+ os=-mint
+ ;;
+ mipsel*-linux*)
+ basic_machine=mipsel-unknown
+ os=-linux-gnu
+ ;;
+ mips*-linux*)
+ basic_machine=mips-unknown
+ os=-linux-gnu
+ ;;
+ mips3*-*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
+ ;;
+ mips3*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
+ ;;
+ mmix*)
+ basic_machine=mmix-knuth
+ os=-mmixware
+ ;;
+ monitor)
+ basic_machine=m68k-rom68k
+ os=-coff
+ ;;
+ msdos)
+ basic_machine=i386-pc
+ os=-msdos
+ ;;
+ mvs)
+ basic_machine=i370-ibm
+ os=-mvs
+ ;;
+ ncr3000)
+ basic_machine=i486-ncr
+ os=-sysv4
+ ;;
+ netbsd386)
+ basic_machine=i386-unknown
+ os=-netbsd
+ ;;
+ netwinder)
+ basic_machine=armv4l-rebel
+ os=-linux
+ ;;
+ news | news700 | news800 | news900)
+ basic_machine=m68k-sony
+ os=-newsos
+ ;;
+ news1000)
+ basic_machine=m68030-sony
+ os=-newsos
+ ;;
+ news-3600 | risc-news)
+ basic_machine=mips-sony
+ os=-newsos
+ ;;
+ necv70)
+ basic_machine=v70-nec
+ os=-sysv
+ ;;
+ next | m*-next )
+ basic_machine=m68k-next
+ case $os in
+ -nextstep* )
+ ;;
+ -ns2*)
+ os=-nextstep2
+ ;;
+ *)
+ os=-nextstep3
+ ;;
+ esac
+ ;;
+ nh3000)
+ basic_machine=m68k-harris
+ os=-cxux
+ ;;
+ nh[45]000)
+ basic_machine=m88k-harris
+ os=-cxux
+ ;;
+ nindy960)
+ basic_machine=i960-intel
+ os=-nindy
+ ;;
+ mon960)
+ basic_machine=i960-intel
+ os=-mon960
+ ;;
+ nonstopux)
+ basic_machine=mips-compaq
+ os=-nonstopux
+ ;;
+ np1)
+ basic_machine=np1-gould
+ ;;
+ nsr-tandem)
+ basic_machine=nsr-tandem
+ ;;
+ op50n-* | op60c-*)
+ basic_machine=hppa1.1-oki
+ os=-proelf
+ ;;
+ OSE68000 | ose68000)
+ basic_machine=m68000-ericsson
+ os=-ose
+ ;;
+ os68k)
+ basic_machine=m68k-none
+ os=-os68k
+ ;;
+ pa-hitachi)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ paragon)
+ basic_machine=i860-intel
+ os=-osf
+ ;;
+ pbd)
+ basic_machine=sparc-tti
+ ;;
+ pbb)
+ basic_machine=m68k-tti
+ ;;
+ pc532 | pc532-*)
+ basic_machine=ns32k-pc532
+ ;;
+ pentium | p5 | k5 | k6 | nexgen)
+ basic_machine=i586-pc
+ ;;
+ pentiumpro | p6 | 6x86 | athlon)
+ basic_machine=i686-pc
+ ;;
+ pentiumii | pentium2)
+ basic_machine=i686-pc
+ ;;
+ pentium-* | p5-* | k5-* | k6-* | nexgen-*)
+ basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentiumpro-* | p6-* | 6x86-* | athlon-*)
+ basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentiumii-* | pentium2-*)
+ basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pn)
+ basic_machine=pn-gould
+ ;;
+ power) basic_machine=power-ibm
+ ;;
+ ppc) basic_machine=powerpc-unknown
+ ;;
+ ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppcle | powerpclittle | ppc-le | powerpc-little)
+ basic_machine=powerpcle-unknown
+ ;;
+ ppcle-* | powerpclittle-*)
+ basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppc64) basic_machine=powerpc64-unknown
+ ;;
+ ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppc64le | powerpc64little | ppc64-le | powerpc64-little)
+ basic_machine=powerpc64le-unknown
+ ;;
+ ppc64le-* | powerpc64little-*)
+ basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ps2)
+ basic_machine=i386-ibm
+ ;;
+ pw32)
+ basic_machine=i586-unknown
+ os=-pw32
+ ;;
+ rom68k)
+ basic_machine=m68k-rom68k
+ os=-coff
+ ;;
+ rm[46]00)
+ basic_machine=mips-siemens
+ ;;
+ rtpc | rtpc-*)
+ basic_machine=romp-ibm
+ ;;
+ sa29200)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ sequent)
+ basic_machine=i386-sequent
+ ;;
+ sh)
+ basic_machine=sh-hitachi
+ os=-hms
+ ;;
+ sparclite-wrs)
+ basic_machine=sparclite-wrs
+ os=-vxworks
+ ;;
+ sps7)
+ basic_machine=m68k-bull
+ os=-sysv2
+ ;;
+ spur)
+ basic_machine=spur-unknown
+ ;;
+ st2000)
+ basic_machine=m68k-tandem
+ ;;
+ stratus)
+ basic_machine=i860-stratus
+ os=-sysv4
+ ;;
+ sun2)
+ basic_machine=m68000-sun
+ ;;
+ sun2os3)
+ basic_machine=m68000-sun
+ os=-sunos3
+ ;;
+ sun2os4)
+ basic_machine=m68000-sun
+ os=-sunos4
+ ;;
+ sun3os3)
+ basic_machine=m68k-sun
+ os=-sunos3
+ ;;
+ sun3os4)
+ basic_machine=m68k-sun
+ os=-sunos4
+ ;;
+ sun4os3)
+ basic_machine=sparc-sun
+ os=-sunos3
+ ;;
+ sun4os4)
+ basic_machine=sparc-sun
+ os=-sunos4
+ ;;
+ sun4sol2)
+ basic_machine=sparc-sun
+ os=-solaris2
+ ;;
+ sun3 | sun3-*)
+ basic_machine=m68k-sun
+ ;;
+ sun4)
+ basic_machine=sparc-sun
+ ;;
+ sun386 | sun386i | roadrunner)
+ basic_machine=i386-sun
+ ;;
+ sv1)
+ basic_machine=sv1-cray
+ os=-unicos
+ ;;
+ symmetry)
+ basic_machine=i386-sequent
+ os=-dynix
+ ;;
+ t3e)
+ basic_machine=t3e-cray
+ os=-unicos
+ ;;
+ tic54x | c54x*)
+ basic_machine=tic54x-unknown
+ os=-coff
+ ;;
+ tx39)
+ basic_machine=mipstx39-unknown
+ ;;
+ tx39el)
+ basic_machine=mipstx39el-unknown
+ ;;
+ tower | tower-32)
+ basic_machine=m68k-ncr
+ ;;
+ udi29k)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ ultra3)
+ basic_machine=a29k-nyu
+ os=-sym1
+ ;;
+ v810 | necv810)
+ basic_machine=v810-nec
+ os=-none
+ ;;
+ vaxv)
+ basic_machine=vax-dec
+ os=-sysv
+ ;;
+ vms)
+ basic_machine=vax-dec
+ os=-vms
+ ;;
+ vpp*|vx|vx-*)
+ basic_machine=f301-fujitsu
+ ;;
+ vxworks960)
+ basic_machine=i960-wrs
+ os=-vxworks
+ ;;
+ vxworks68)
+ basic_machine=m68k-wrs
+ os=-vxworks
+ ;;
+ vxworks29k)
+ basic_machine=a29k-wrs
+ os=-vxworks
+ ;;
+ w65*)
+ basic_machine=w65-wdc
+ os=-none
+ ;;
+ w89k-*)
+ basic_machine=hppa1.1-winbond
+ os=-proelf
+ ;;
+ windows32)
+ basic_machine=i386-pc
+ os=-windows32-msvcrt
+ ;;
+ xmp)
+ basic_machine=xmp-cray
+ os=-unicos
+ ;;
+ xps | xps100)
+ basic_machine=xps100-honeywell
+ ;;
+ z8k-*-coff)
+ basic_machine=z8k-unknown
+ os=-sim
+ ;;
+ none)
+ basic_machine=none-none
+ os=-none
+ ;;
+
+# Here we handle the default manufacturer of certain CPU types. It is in
+# some cases the only manufacturer, in others, it is the most popular.
+ w89k)
+ basic_machine=hppa1.1-winbond
+ ;;
+ op50n)
+ basic_machine=hppa1.1-oki
+ ;;
+ op60c)
+ basic_machine=hppa1.1-oki
+ ;;
+ mips)
+ if [ x$os = x-linux-gnu ]; then
+ basic_machine=mips-unknown
+ else
+ basic_machine=mips-mips
+ fi
+ ;;
+ romp)
+ basic_machine=romp-ibm
+ ;;
+ rs6000)
+ basic_machine=rs6000-ibm
+ ;;
+ vax)
+ basic_machine=vax-dec
+ ;;
+ pdp10)
+ # there are many clones, so DEC is not a safe bet
+ basic_machine=pdp10-unknown
+ ;;
+ pdp11)
+ basic_machine=pdp11-dec
+ ;;
+ we32k)
+ basic_machine=we32k-att
+ ;;
+ sh3 | sh4 | sh3eb | sh4eb)
+ basic_machine=sh-unknown
+ ;;
+ sparc | sparcv9 | sparcv9b)
+ basic_machine=sparc-sun
+ ;;
+ cydra)
+ basic_machine=cydra-cydrome
+ ;;
+ orion)
+ basic_machine=orion-highlevel
+ ;;
+ orion105)
+ basic_machine=clipper-highlevel
+ ;;
+ mac | mpw | mac-mpw)
+ basic_machine=m68k-apple
+ ;;
+ pmac | pmac-mpw)
+ basic_machine=powerpc-apple
+ ;;
+ c4x*)
+ basic_machine=c4x-none
+ os=-coff
+ ;;
+ *-unknown)
+ # Make sure to match an already-canonicalized machine name.
+ ;;
+ *)
+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $basic_machine in
+ *-digital*)
+ basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
+ ;;
+ *-commodore*)
+ basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
+ ;;
+ *)
+ ;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if [ x"$os" != x"" ]
+then
+case $os in
+ # First match some system type aliases
+ # that might get confused with valid system types.
+ # -solaris* is a basic system type, with this one exception.
+ -solaris1 | -solaris1.*)
+ os=`echo $os | sed -e 's|solaris1|sunos4|'`
+ ;;
+ -solaris)
+ os=-solaris2
+ ;;
+ -svr4*)
+ os=-sysv4
+ ;;
+ -unixware*)
+ os=-sysv4.2uw
+ ;;
+ -gnu/linux*)
+ os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
+ ;;
+ # First accept the basic system types.
+ # The portable systems comes first.
+ # Each alternative MUST END IN A *, to match a version number.
+ # -sysv* is not here because it comes later, after sysvr4.
+ -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
+ | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\
+ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \
+ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
+ | -aos* \
+ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
+ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
+ | -hiux* | -386bsd* | -netbsd* | -openbsd* | -freebsd* | -riscix* \
+ | -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
+ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
+ | -chorusos* | -chorusrdb* \
+ | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+ | -mingw32* | -linux-gnu* | -uxpv* | -beos* | -mpeix* | -udk* \
+ | -interix* | -uwin* | -rhapsody* | -darwin* | -opened* \
+ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
+ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
+ | -os2* | -vos*)
+ # Remember, each alternative MUST END IN *, to match a version number.
+ ;;
+ -qnx*)
+ case $basic_machine in
+ x86-* | i*86-*)
+ ;;
+ *)
+ os=-nto$os
+ ;;
+ esac
+ ;;
+ -nto*)
+ os=-nto-qnx
+ ;;
+ -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
+ | -windows* | -osx | -abug | -netware* | -os9* | -beos* \
+ | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
+ ;;
+ -mac*)
+ os=`echo $os | sed -e 's|mac|macos|'`
+ ;;
+ -linux*)
+ os=`echo $os | sed -e 's|linux|linux-gnu|'`
+ ;;
+ -sunos5*)
+ os=`echo $os | sed -e 's|sunos5|solaris2|'`
+ ;;
+ -sunos6*)
+ os=`echo $os | sed -e 's|sunos6|solaris3|'`
+ ;;
+ -opened*)
+ os=-openedition
+ ;;
+ -wince*)
+ os=-wince
+ ;;
+ -osfrose*)
+ os=-osfrose
+ ;;
+ -osf*)
+ os=-osf
+ ;;
+ -utek*)
+ os=-bsd
+ ;;
+ -dynix*)
+ os=-bsd
+ ;;
+ -acis*)
+ os=-aos
+ ;;
+ -386bsd)
+ os=-bsd
+ ;;
+ -ctix* | -uts*)
+ os=-sysv
+ ;;
+ -ns2 )
+ os=-nextstep2
+ ;;
+ -nsk*)
+ os=-nsk
+ ;;
+ # Preserve the version number of sinix5.
+ -sinix5.*)
+ os=`echo $os | sed -e 's|sinix|sysv|'`
+ ;;
+ -sinix*)
+ os=-sysv4
+ ;;
+ -triton*)
+ os=-sysv3
+ ;;
+ -oss*)
+ os=-sysv3
+ ;;
+ -svr4)
+ os=-sysv4
+ ;;
+ -svr3)
+ os=-sysv3
+ ;;
+ -sysvr4)
+ os=-sysv4
+ ;;
+ # This must come after -sysvr4.
+ -sysv*)
+ ;;
+ -ose*)
+ os=-ose
+ ;;
+ -es1800*)
+ os=-ose
+ ;;
+ -xenix)
+ os=-xenix
+ ;;
+ -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+ os=-mint
+ ;;
+ -none)
+ ;;
+ *)
+ # Get rid of the `-' at the beginning of $os.
+ os=`echo $os | sed 's/[^-]*-//'`
+ echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system. Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+case $basic_machine in
+ *-acorn)
+ os=-riscix1.2
+ ;;
+ arm*-rebel)
+ os=-linux
+ ;;
+ arm*-semi)
+ os=-aout
+ ;;
+ pdp10-*)
+ os=-tops20
+ ;;
+ pdp11-*)
+ os=-none
+ ;;
+ *-dec | vax-*)
+ os=-ultrix4.2
+ ;;
+ m68*-apollo)
+ os=-domain
+ ;;
+ i386-sun)
+ os=-sunos4.0.2
+ ;;
+ m68000-sun)
+ os=-sunos3
+ # This also exists in the configure program, but was not the
+ # default.
+ # os=-sunos4
+ ;;
+ m68*-cisco)
+ os=-aout
+ ;;
+ mips*-cisco)
+ os=-elf
+ ;;
+ mips*-*)
+ os=-elf
+ ;;
+ *-tti) # must be before sparc entry or we get the wrong os.
+ os=-sysv3
+ ;;
+ sparc-* | *-sun)
+ os=-sunos4.1.1
+ ;;
+ *-be)
+ os=-beos
+ ;;
+ *-ibm)
+ os=-aix
+ ;;
+ *-wec)
+ os=-proelf
+ ;;
+ *-winbond)
+ os=-proelf
+ ;;
+ *-oki)
+ os=-proelf
+ ;;
+ *-hp)
+ os=-hpux
+ ;;
+ *-hitachi)
+ os=-hiux
+ ;;
+ i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+ os=-sysv
+ ;;
+ *-cbm)
+ os=-amigaos
+ ;;
+ *-dg)
+ os=-dgux
+ ;;
+ *-dolphin)
+ os=-sysv3
+ ;;
+ m68k-ccur)
+ os=-rtu
+ ;;
+ m88k-omron*)
+ os=-luna
+ ;;
+ *-next )
+ os=-nextstep
+ ;;
+ *-sequent)
+ os=-ptx
+ ;;
+ *-crds)
+ os=-unos
+ ;;
+ *-ns)
+ os=-genix
+ ;;
+ i370-*)
+ os=-mvs
+ ;;
+ *-next)
+ os=-nextstep3
+ ;;
+ *-gould)
+ os=-sysv
+ ;;
+ *-highlevel)
+ os=-bsd
+ ;;
+ *-encore)
+ os=-bsd
+ ;;
+ *-sgi)
+ os=-irix
+ ;;
+ *-siemens)
+ os=-sysv4
+ ;;
+ *-masscomp)
+ os=-rtu
+ ;;
+ f30[01]-fujitsu | f700-fujitsu)
+ os=-uxpv
+ ;;
+ *-rom68k)
+ os=-coff
+ ;;
+ *-*bug)
+ os=-coff
+ ;;
+ *-apple)
+ os=-macos
+ ;;
+ *-atari*)
+ os=-mint
+ ;;
+ *)
+ os=-none
+ ;;
+esac
+fi
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer. We pick the logical manufacturer.
+vendor=unknown
+case $basic_machine in
+ *-unknown)
+ case $os in
+ -riscix*)
+ vendor=acorn
+ ;;
+ -sunos*)
+ vendor=sun
+ ;;
+ -aix*)
+ vendor=ibm
+ ;;
+ -beos*)
+ vendor=be
+ ;;
+ -hpux*)
+ vendor=hp
+ ;;
+ -mpeix*)
+ vendor=hp
+ ;;
+ -hiux*)
+ vendor=hitachi
+ ;;
+ -unos*)
+ vendor=crds
+ ;;
+ -dgux*)
+ vendor=dg
+ ;;
+ -luna*)
+ vendor=omron
+ ;;
+ -genix*)
+ vendor=ns
+ ;;
+ -mvs* | -opened*)
+ vendor=ibm
+ ;;
+ -ptx*)
+ vendor=sequent
+ ;;
+ -vxsim* | -vxworks*)
+ vendor=wrs
+ ;;
+ -aux*)
+ vendor=apple
+ ;;
+ -hms*)
+ vendor=hitachi
+ ;;
+ -mpw* | -macos*)
+ vendor=apple
+ ;;
+ -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+ vendor=atari
+ ;;
+ -vos*)
+ vendor=stratus
+ ;;
+ esac
+ basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
+ ;;
+esac
+
+echo $basic_machine$os
+exit 0
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/trunk/main/editline/configure b/trunk/main/editline/configure
new file mode 100755
index 000000000..bb250aea1
--- /dev/null
+++ b/trunk/main/editline/configure
@@ -0,0 +1,2309 @@
+#! /bin/sh
+
+# Guess values for system-dependent variables and create Makefiles.
+# Generated automatically using autoconf version 2.13
+# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc.
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+
+# Defaults:
+ac_help=
+ac_default_prefix=/usr/local
+# Any additions from configure.in:
+ac_help="$ac_help
+ --disable-readline Disable readline compatibility"
+ac_help="$ac_help
+ --enable-debug Enable debugging code"
+
+# Initialize some variables set by options.
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+build=NONE
+cache_file=./config.cache
+exec_prefix=NONE
+host=NONE
+no_create=
+nonopt=NONE
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+target=NONE
+verbose=
+x_includes=NONE
+x_libraries=NONE
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datadir='${prefix}/share'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+libdir='${exec_prefix}/lib'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+infodir='${prefix}/info'
+mandir='${prefix}/man'
+
+# Initialize some other variables.
+subdirs=
+MFLAGS= MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+# Maximum number of lines to put in a shell here document.
+ac_max_here_lines=12
+
+ac_prev=
+for ac_option
+do
+
+ # If the previous option needs an argument, assign it.
+ if test -n "$ac_prev"; then
+ eval "$ac_prev=\$ac_option"
+ ac_prev=
+ continue
+ fi
+
+ case "$ac_option" in
+ -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+ *) ac_optarg= ;;
+ esac
+
+ # Accept the important Cygnus configure options, so we can diagnose typos.
+
+ case "$ac_option" in
+
+ -bindir | --bindir | --bindi | --bind | --bin | --bi)
+ ac_prev=bindir ;;
+ -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+ bindir="$ac_optarg" ;;
+
+ -build | --build | --buil | --bui | --bu)
+ ac_prev=build ;;
+ -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+ build="$ac_optarg" ;;
+
+ -cache-file | --cache-file | --cache-fil | --cache-fi \
+ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+ ac_prev=cache_file ;;
+ -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+ cache_file="$ac_optarg" ;;
+
+ -datadir | --datadir | --datadi | --datad | --data | --dat | --da)
+ ac_prev=datadir ;;
+ -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
+ | --da=*)
+ datadir="$ac_optarg" ;;
+
+ -disable-* | --disable-*)
+ ac_feature=`echo $ac_option|sed -e 's/-*disable-//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then
+ { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+ fi
+ ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+ eval "enable_${ac_feature}=no" ;;
+
+ -enable-* | --enable-*)
+ ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then
+ { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+ fi
+ ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+ case "$ac_option" in
+ *=*) ;;
+ *) ac_optarg=yes ;;
+ esac
+ eval "enable_${ac_feature}='$ac_optarg'" ;;
+
+ -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+ | --exec | --exe | --ex)
+ ac_prev=exec_prefix ;;
+ -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+ | --exec=* | --exe=* | --ex=*)
+ exec_prefix="$ac_optarg" ;;
+
+ -gas | --gas | --ga | --g)
+ # Obsolete; use --with-gas.
+ with_gas=yes ;;
+
+ -help | --help | --hel | --he)
+ # Omit some internal or obsolete options to make the list less imposing.
+ # This message is too long to be a string in the A/UX 3.1 sh.
+ cat << EOF
+Usage: configure [options] [host]
+Options: [defaults in brackets after descriptions]
+Configuration:
+ --cache-file=FILE cache test results in FILE
+ --help print this message
+ --no-create do not create output files
+ --quiet, --silent do not print \`checking...' messages
+ --version print the version of autoconf that created configure
+Directory and file names:
+ --prefix=PREFIX install architecture-independent files in PREFIX
+ [$ac_default_prefix]
+ --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX
+ [same as prefix]
+ --bindir=DIR user executables in DIR [EPREFIX/bin]
+ --sbindir=DIR system admin executables in DIR [EPREFIX/sbin]
+ --libexecdir=DIR program executables in DIR [EPREFIX/libexec]
+ --datadir=DIR read-only architecture-independent data in DIR
+ [PREFIX/share]
+ --sysconfdir=DIR read-only single-machine data in DIR [PREFIX/etc]
+ --sharedstatedir=DIR modifiable architecture-independent data in DIR
+ [PREFIX/com]
+ --localstatedir=DIR modifiable single-machine data in DIR [PREFIX/var]
+ --libdir=DIR object code libraries in DIR [EPREFIX/lib]
+ --includedir=DIR C header files in DIR [PREFIX/include]
+ --oldincludedir=DIR C header files for non-gcc in DIR [/usr/include]
+ --infodir=DIR info documentation in DIR [PREFIX/info]
+ --mandir=DIR man documentation in DIR [PREFIX/man]
+ --srcdir=DIR find the sources in DIR [configure dir or ..]
+ --program-prefix=PREFIX prepend PREFIX to installed program names
+ --program-suffix=SUFFIX append SUFFIX to installed program names
+ --program-transform-name=PROGRAM
+ run sed PROGRAM on installed program names
+EOF
+ cat << EOF
+Host type:
+ --build=BUILD configure for building on BUILD [BUILD=HOST]
+ --host=HOST configure for HOST [guessed]
+ --target=TARGET configure for TARGET [TARGET=HOST]
+Features and packages:
+ --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
+ --enable-FEATURE[=ARG] include FEATURE [ARG=yes]
+ --with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
+ --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
+ --x-includes=DIR X include files are in DIR
+ --x-libraries=DIR X library files are in DIR
+EOF
+ if test -n "$ac_help"; then
+ echo "--enable and --with options recognized:$ac_help"
+ fi
+ exit 0 ;;
+
+ -host | --host | --hos | --ho)
+ ac_prev=host ;;
+ -host=* | --host=* | --hos=* | --ho=*)
+ host="$ac_optarg" ;;
+
+ -includedir | --includedir | --includedi | --included | --include \
+ | --includ | --inclu | --incl | --inc)
+ ac_prev=includedir ;;
+ -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+ | --includ=* | --inclu=* | --incl=* | --inc=*)
+ includedir="$ac_optarg" ;;
+
+ -infodir | --infodir | --infodi | --infod | --info | --inf)
+ ac_prev=infodir ;;
+ -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+ infodir="$ac_optarg" ;;
+
+ -libdir | --libdir | --libdi | --libd)
+ ac_prev=libdir ;;
+ -libdir=* | --libdir=* | --libdi=* | --libd=*)
+ libdir="$ac_optarg" ;;
+
+ -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+ | --libexe | --libex | --libe)
+ ac_prev=libexecdir ;;
+ -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+ | --libexe=* | --libex=* | --libe=*)
+ libexecdir="$ac_optarg" ;;
+
+ -localstatedir | --localstatedir | --localstatedi | --localstated \
+ | --localstate | --localstat | --localsta | --localst \
+ | --locals | --local | --loca | --loc | --lo)
+ ac_prev=localstatedir ;;
+ -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+ | --localstate=* | --localstat=* | --localsta=* | --localst=* \
+ | --locals=* | --local=* | --loca=* | --loc=* | --lo=*)
+ localstatedir="$ac_optarg" ;;
+
+ -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+ ac_prev=mandir ;;
+ -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+ mandir="$ac_optarg" ;;
+
+ -nfp | --nfp | --nf)
+ # Obsolete; use --without-fp.
+ with_fp=no ;;
+
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c)
+ no_create=yes ;;
+
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+ no_recursion=yes ;;
+
+ -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+ | --oldin | --oldi | --old | --ol | --o)
+ ac_prev=oldincludedir ;;
+ -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+ oldincludedir="$ac_optarg" ;;
+
+ -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+ ac_prev=prefix ;;
+ -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+ prefix="$ac_optarg" ;;
+
+ -program-prefix | --program-prefix | --program-prefi | --program-pref \
+ | --program-pre | --program-pr | --program-p)
+ ac_prev=program_prefix ;;
+ -program-prefix=* | --program-prefix=* | --program-prefi=* \
+ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+ program_prefix="$ac_optarg" ;;
+
+ -program-suffix | --program-suffix | --program-suffi | --program-suff \
+ | --program-suf | --program-su | --program-s)
+ ac_prev=program_suffix ;;
+ -program-suffix=* | --program-suffix=* | --program-suffi=* \
+ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+ program_suffix="$ac_optarg" ;;
+
+ -program-transform-name | --program-transform-name \
+ | --program-transform-nam | --program-transform-na \
+ | --program-transform-n | --program-transform- \
+ | --program-transform | --program-transfor \
+ | --program-transfo | --program-transf \
+ | --program-trans | --program-tran \
+ | --progr-tra | --program-tr | --program-t)
+ ac_prev=program_transform_name ;;
+ -program-transform-name=* | --program-transform-name=* \
+ | --program-transform-nam=* | --program-transform-na=* \
+ | --program-transform-n=* | --program-transform-=* \
+ | --program-transform=* | --program-transfor=* \
+ | --program-transfo=* | --program-transf=* \
+ | --program-trans=* | --program-tran=* \
+ | --progr-tra=* | --program-tr=* | --program-t=*)
+ program_transform_name="$ac_optarg" ;;
+
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ silent=yes ;;
+
+ -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+ ac_prev=sbindir ;;
+ -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+ | --sbi=* | --sb=*)
+ sbindir="$ac_optarg" ;;
+
+ -sharedstatedir | --sharedstatedir | --sharedstatedi \
+ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+ | --sharedst | --shareds | --shared | --share | --shar \
+ | --sha | --sh)
+ ac_prev=sharedstatedir ;;
+ -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+ | --sha=* | --sh=*)
+ sharedstatedir="$ac_optarg" ;;
+
+ -site | --site | --sit)
+ ac_prev=site ;;
+ -site=* | --site=* | --sit=*)
+ site="$ac_optarg" ;;
+
+ -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+ ac_prev=srcdir ;;
+ -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+ srcdir="$ac_optarg" ;;
+
+ -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+ | --syscon | --sysco | --sysc | --sys | --sy)
+ ac_prev=sysconfdir ;;
+ -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+ sysconfdir="$ac_optarg" ;;
+
+ -target | --target | --targe | --targ | --tar | --ta | --t)
+ ac_prev=target ;;
+ -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+ target="$ac_optarg" ;;
+
+ -v | -verbose | --verbose | --verbos | --verbo | --verb)
+ verbose=yes ;;
+
+ -version | --version | --versio | --versi | --vers)
+ echo "configure generated by autoconf version 2.13"
+ exit 0 ;;
+
+ -with-* | --with-*)
+ ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then
+ { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+ fi
+ ac_package=`echo $ac_package| sed 's/-/_/g'`
+ case "$ac_option" in
+ *=*) ;;
+ *) ac_optarg=yes ;;
+ esac
+ eval "with_${ac_package}='$ac_optarg'" ;;
+
+ -without-* | --without-*)
+ ac_package=`echo $ac_option|sed -e 's/-*without-//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then
+ { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+ fi
+ ac_package=`echo $ac_package| sed 's/-/_/g'`
+ eval "with_${ac_package}=no" ;;
+
+ --x)
+ # Obsolete; use --with-x.
+ with_x=yes ;;
+
+ -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+ | --x-incl | --x-inc | --x-in | --x-i)
+ ac_prev=x_includes ;;
+ -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+ x_includes="$ac_optarg" ;;
+
+ -x-libraries | --x-libraries | --x-librarie | --x-librari \
+ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+ ac_prev=x_libraries ;;
+ -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+ x_libraries="$ac_optarg" ;;
+
+ -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; }
+ ;;
+
+ *)
+ if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then
+ echo "configure: warning: $ac_option: invalid host type" 1>&2
+ fi
+ if test "x$nonopt" != xNONE; then
+ { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; }
+ fi
+ nonopt="$ac_option"
+ ;;
+
+ esac
+done
+
+if test -n "$ac_prev"; then
+ { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; }
+fi
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+# File descriptor usage:
+# 0 standard input
+# 1 file creation
+# 2 errors and warnings
+# 3 some systems may open it to /dev/tty
+# 4 used on the Kubota Titan
+# 6 checking for... messages and results
+# 5 compiler messages saved in config.log
+if test "$silent" = yes; then
+ exec 6>/dev/null
+else
+ exec 6>&1
+fi
+exec 5>./config.log
+
+echo "\
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+" 1>&5
+
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Also quote any args containing shell metacharacters.
+ac_configure_args=
+for ac_arg
+do
+ case "$ac_arg" in
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c) ;;
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;;
+ *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*)
+ ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+ *) ac_configure_args="$ac_configure_args $ac_arg" ;;
+ esac
+done
+
+# NLS nuisances.
+# Only set these to C if already set. These must not be set unconditionally
+# because not all systems understand e.g. LANG=C (notably SCO).
+# Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'!
+# Non-C LC_CTYPE values break the ctype check.
+if test "${LANG+set}" = set; then LANG=C; export LANG; fi
+if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi
+if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi
+if test "${LC_CTYPE+set}" = set; then LC_CTYPE=C; export LC_CTYPE; fi
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -rf conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo > confdefs.h
+
+# A filename unique to this package, relative to the directory that
+# configure is in, which we can look for to find out if srcdir is correct.
+ac_unique_file=Makefile.in
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+ ac_srcdir_defaulted=yes
+ # Try the directory containing this script, then its parent.
+ ac_prog=$0
+ ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'`
+ test "x$ac_confdir" = "x$ac_prog" && ac_confdir=.
+ srcdir=$ac_confdir
+ if test ! -r $srcdir/$ac_unique_file; then
+ srcdir=..
+ fi
+else
+ ac_srcdir_defaulted=no
+fi
+if test ! -r $srcdir/$ac_unique_file; then
+ if test "$ac_srcdir_defaulted" = yes; then
+ { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; }
+ else
+ { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; }
+ fi
+fi
+srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'`
+
+# Prefer explicitly selected file to automatically selected ones.
+if test -z "$CONFIG_SITE"; then
+ if test "x$prefix" != xNONE; then
+ CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
+ else
+ CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+ fi
+fi
+for ac_site_file in $CONFIG_SITE; do
+ if test -r "$ac_site_file"; then
+ echo "loading site script $ac_site_file"
+ . "$ac_site_file"
+ fi
+done
+
+if test -r "$cache_file"; then
+ echo "loading cache $cache_file"
+ . $cache_file
+else
+ echo "creating cache $cache_file"
+ > $cache_file
+fi
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+ac_exeext=
+ac_objext=o
+if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then
+ # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu.
+ if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then
+ ac_n= ac_c='
+' ac_t=' '
+ else
+ ac_n=-n ac_c= ac_t=
+ fi
+else
+ ac_n= ac_c='\c' ac_t=
+fi
+
+
+
+CFLAGS=$CFLAGS
+# Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:534: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_CC="gcc"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+ echo "$ac_t""$CC" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+if test -z "$CC"; then
+ # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:564: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_prog_rejected=no
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then
+ ac_prog_rejected=yes
+ continue
+ fi
+ ac_cv_prog_CC="cc"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+if test $ac_prog_rejected = yes; then
+ # We found a bogon in the path, so make sure we never use it.
+ set dummy $ac_cv_prog_CC
+ shift
+ if test $# -gt 0; then
+ # We chose a different compiler from the bogus one.
+ # However, it has the same basename, so the bogon will be chosen
+ # first if we set CC to just the basename; use the full file name.
+ shift
+ set dummy "$ac_dir/$ac_word" "$@"
+ shift
+ ac_cv_prog_CC="$@"
+ fi
+fi
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+ echo "$ac_t""$CC" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+ if test -z "$CC"; then
+ case "`uname -s`" in
+ *win32* | *WIN32*)
+ # Extract the first word of "cl", so it can be a program name with args.
+set dummy cl; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:615: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_CC="cl"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+ echo "$ac_t""$CC" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+ ;;
+ esac
+ fi
+ test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; }
+fi
+
+echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6
+echo "configure:647: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+cat > conftest.$ac_ext << EOF
+
+#line 658 "configure"
+#include "confdefs.h"
+
+main(){return(0);}
+EOF
+if { (eval echo configure:663: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ ac_cv_prog_cc_works=yes
+ # If we can't run a trivial program, we are probably using a cross compiler.
+ if (./conftest; exit) 2>/dev/null; then
+ ac_cv_prog_cc_cross=no
+ else
+ ac_cv_prog_cc_cross=yes
+ fi
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ ac_cv_prog_cc_works=no
+fi
+rm -fr conftest*
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+echo "$ac_t""$ac_cv_prog_cc_works" 1>&6
+if test $ac_cv_prog_cc_works = no; then
+ { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; }
+fi
+echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6
+echo "configure:689: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5
+echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6
+cross_compiling=$ac_cv_prog_cc_cross
+
+echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6
+echo "configure:694: checking whether we are using GNU C" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.c <<EOF
+#ifdef __GNUC__
+ yes;
+#endif
+EOF
+if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:703: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then
+ ac_cv_prog_gcc=yes
+else
+ ac_cv_prog_gcc=no
+fi
+fi
+
+echo "$ac_t""$ac_cv_prog_gcc" 1>&6
+
+if test $ac_cv_prog_gcc = yes; then
+ GCC=yes
+else
+ GCC=
+fi
+
+ac_test_CFLAGS="${CFLAGS+set}"
+ac_save_CFLAGS="$CFLAGS"
+CFLAGS=
+echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6
+echo "configure:722: checking whether ${CC-cc} accepts -g" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ echo 'void f(){}' > conftest.c
+if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then
+ ac_cv_prog_cc_g=yes
+else
+ ac_cv_prog_cc_g=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$ac_cv_prog_cc_g" 1>&6
+if test "$ac_test_CFLAGS" = set; then
+ CFLAGS="$ac_save_CFLAGS"
+elif test $ac_cv_prog_cc_g = yes; then
+ if test "$GCC" = yes; then
+ CFLAGS="-g -O2"
+ else
+ CFLAGS="-g"
+ fi
+else
+ if test "$GCC" = yes; then
+ CFLAGS="-O2"
+ else
+ CFLAGS=
+ fi
+fi
+
+if test "x$CFLAGS" = "x" ; then
+ no_CFLAGS="yes"
+fi
+if test "x$no_CFLAGS" = "xyes" -a "x$GCC" = "xyes" ; then
+ CFLAGS="-Wall -pipe -g3"
+fi
+A_CFLAGS=""
+
+S_CFLAGS="-fPIC -DPIC"
+
+echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6
+echo "configure:764: checking how to run the C preprocessor" >&5
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+ CPP=
+fi
+if test -z "$CPP"; then
+if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ # This must be in double quotes, not single quotes, because CPP may get
+ # substituted into the Makefile and "${CC-cc}" will confuse make.
+ CPP="${CC-cc} -E"
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp.
+ cat > conftest.$ac_ext <<EOF
+#line 779 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:785: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ :
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ CPP="${CC-cc} -E -traditional-cpp"
+ cat > conftest.$ac_ext <<EOF
+#line 796 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:802: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ :
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ CPP="${CC-cc} -nologo -E"
+ cat > conftest.$ac_ext <<EOF
+#line 813 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:819: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ :
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ CPP=/lib/cpp
+fi
+rm -f conftest*
+fi
+rm -f conftest*
+fi
+rm -f conftest*
+ ac_cv_prog_CPP="$CPP"
+fi
+ CPP="$ac_cv_prog_CPP"
+else
+ ac_cv_prog_CPP="$CPP"
+fi
+echo "$ac_t""$CPP" 1>&6
+
+
+ac_aux_dir=
+for ac_dir in ${GNUSYSTEM_AUX_DIR} $srcdir $srcdir/.. $srcdir/../..; do
+ if test -f $ac_dir/install-sh; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install-sh -c"
+ break
+ elif test -f $ac_dir/install.sh; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install.sh -c"
+ break
+ fi
+done
+if test -z "$ac_aux_dir"; then
+ { echo "configure: error: can not find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." 1>&2; exit 1; }
+fi
+ac_config_guess=$ac_aux_dir/config.guess
+ac_config_sub=$ac_aux_dir/config.sub
+ac_configure=$ac_aux_dir/configure # This should be Cygnus configure.
+
+
+# Make sure we can run config.sub.
+if ${CONFIG_SHELL-/bin/sh} $ac_config_sub sun4 >/dev/null 2>&1; then :
+else { echo "configure: error: can not run $ac_config_sub" 1>&2; exit 1; }
+fi
+
+echo $ac_n "checking host system type""... $ac_c" 1>&6
+echo "configure:870: checking host system type" >&5
+
+host_alias=$host
+case "$host_alias" in
+NONE)
+ case $nonopt in
+ NONE)
+ if host_alias=`${CONFIG_SHELL-/bin/sh} $ac_config_guess`; then :
+ else { echo "configure: error: can not guess host type; you must specify one" 1>&2; exit 1; }
+ fi ;;
+ *) host_alias=$nonopt ;;
+ esac ;;
+esac
+
+host=`${CONFIG_SHELL-/bin/sh} $ac_config_sub $host_alias`
+host_cpu=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
+host_vendor=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
+host_os=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
+echo "$ac_t""$host" 1>&6
+
+case "${host}" in
+ *-*-darwin*)
+ CFLAGS="$CFLAGS -fno-common -no-cpp-precomp"
+ ABI="macho"
+ ;;
+ *-*-freebsd*)
+ ABI="elf"
+ ;;
+ *-*-linux* | *cygwin*)
+ if echo ${host} | grep -q cygwin ; then \
+ echo "cygwin detected"; \
+ S_CFLAGS=""; \
+ echo "/* cygdef.h. Generated automatically by configure. */
+#ifndef _CYGDEF_H_
+#define _CYGDEF_H_ 1
+#include <sys/ioctl.h>
+#define __linux__ 1
+
+
+typedef void (*sig_t)(int);
+
+
+#endif /* _CYGDEF_H_ */" > cygdef.h; \
+ echo "
+ #define CYGWIN 1
+" > confdefs.h; \
+ fi
+ ABI="elf"
+ ;;
+ *-*-netbsd*)
+ echo $ac_n "checking ABI""... $ac_c" 1>&6
+echo "configure:903: checking ABI" >&5
+ cat > conftest.$ac_ext <<EOF
+#line 905 "configure"
+#include "confdefs.h"
+#ifdef __ELF__
+ yes
+#endif
+
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "yes" >/dev/null 2>&1; then
+ rm -rf conftest*
+ ABI="elf"
+else
+ rm -rf conftest*
+ ABI="aout"
+fi
+rm -f conftest*
+
+ echo "$ac_t""$ABI" 1>&6
+ ;;
+ *-*-solaris2*)
+ ABI="elf"
+ cat >> confdefs.h <<\EOF
+#define SUNOS 1
+EOF
+
+ ;;
+ *)
+ echo "$ac_t""Unsupported operating system: ${host}" 1>&6
+ ABI="elf"
+ ;;
+esac
+
+# Find a good install program. We prefer a C program (faster),
+# so one script is as good as another. But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# ./install, which can be erroneously created by make from ./install.sh.
+echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6
+echo "configure:949: checking for a BSD compatible install" >&5
+if test -z "$INSTALL"; then
+if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ IFS="${IFS= }"; ac_save_IFS="$IFS"; IFS=":"
+ for ac_dir in $PATH; do
+ # Account for people who put trailing slashes in PATH elements.
+ case "$ac_dir/" in
+ /|./|.//|/etc/*|/usr/sbin/*|/usr/etc/*|/sbin/*|/usr/afsws/bin/*|/usr/ucb/*) ;;
+ *)
+ # OSF1 and SCO ODT 3.0 have their own names for install.
+ # Don't use installbsd from OSF since it installs stuff as root
+ # by default.
+ for ac_prog in ginstall scoinst install; do
+ if test -f $ac_dir/$ac_prog; then
+ if test $ac_prog = install &&
+ grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then
+ # AIX install. It has an incompatible calling convention.
+ :
+ else
+ ac_cv_path_install="$ac_dir/$ac_prog -c"
+ break 2
+ fi
+ fi
+ done
+ ;;
+ esac
+ done
+ IFS="$ac_save_IFS"
+
+fi
+ if test "${ac_cv_path_install+set}" = set; then
+ INSTALL="$ac_cv_path_install"
+ else
+ # As a last resort, use the slow shell script. We don't cache a
+ # path for INSTALL within a source directory, because that will
+ # break other packages using the cache if that directory is
+ # removed, or if the path is relative.
+ INSTALL="$ac_install_sh"
+ fi
+fi
+echo "$ac_t""$INSTALL" 1>&6
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL_PROGRAM}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+# Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:1004: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$RANLIB"; then
+ ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_RANLIB="ranlib"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+ test -z "$ac_cv_prog_RANLIB" && ac_cv_prog_RANLIB=":"
+fi
+fi
+RANLIB="$ac_cv_prog_RANLIB"
+if test -n "$RANLIB"; then
+ echo "$ac_t""$RANLIB" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+# Extract the first word of "ar", so it can be a program name with args.
+set dummy ar; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:1034: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_path_AR'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ case "$AR" in
+ /*)
+ ac_cv_path_AR="$AR" # Let the user override the test with a path.
+ ;;
+ ?:/*)
+ ac_cv_path_AR="$AR" # Let the user override the test with a dos path.
+ ;;
+ *)
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_path_AR="$ac_dir/$ac_word"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+ ;;
+esac
+fi
+AR="$ac_cv_path_AR"
+if test -n "$AR"; then
+ echo "$ac_t""$AR" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+
+echo $ac_n "checking for tgetent in -ltermcap""... $ac_c" 1>&6
+echo "configure:1068: checking for tgetent in -ltermcap" >&5
+ac_lib_var=`echo termcap'_'tgetent | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-ltermcap $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1076 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char tgetent();
+
+int main() {
+tgetent()
+; return 0; }
+EOF
+if { (eval echo configure:1087: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_lib=HAVE_LIB`echo termcap | sed -e 's/[^a-zA-Z0-9_]/_/g' \
+ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+ LIBS="-ltermcap $LIBS"
+
+else
+ echo "$ac_t""no" 1>&6
+\
+ echo $ac_n "checking for tgetent in -ltinfo""... $ac_c" 1>&6
+echo "configure:1114: checking for tgetent in -ltinfo" >&5
+ac_lib_var=`echo tinfo'_'tgetent | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-ltinfo $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1122 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char tgetent();
+
+int main() {
+tgetent()
+; return 0; }
+EOF
+if { (eval echo configure:1133: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_lib=HAVE_LIB`echo tinfo | sed -e 's/^a-zA-Z0-9_/_/g' \
+ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+ LIBS="-ltinfo $LIBS"
+
+else
+ echo "$ac_t""no" 1>&6
+\
+ echo $ac_n "checking for tgetent in -lcurses""... $ac_c" 1>&6
+echo "configure:1160: checking for tgetent in -lcurses" >&5
+ac_lib_var=`echo curses'_'tgetent | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lcurses $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1168 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char tgetent();
+
+int main() {
+tgetent()
+; return 0; }
+EOF
+if { (eval echo configure:1179: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_lib=HAVE_LIB`echo curses | sed -e 's/^a-zA-Z0-9_/_/g' \
+ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+ LIBS="-lcurses $LIBS"
+
+else
+ echo "$ac_t""no" 1>&6
+\
+ echo $ac_n "checking for tgetent in -lncurses""... $ac_c" 1>&6
+echo "configure:1206: checking for tgetent in -lncurses" >&5
+ac_lib_var=`echo ncurses'_'tgetent | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ ac_save_LIBS="$LIBS"
+LIBS="-lncurses $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1214 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char tgetent();
+
+int main() {
+tgetent()
+; return 0; }
+EOF
+if { (eval echo configure:1225: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_lib=HAVE_LIB`echo ncurses | sed -e 's/^a-zA-Z0-9_/_/g' \
+ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_lib 1
+EOF
+
+ LIBS="-lncurses $LIBS"
+
+else
+ echo "$ac_t""no" 1>&6
+\
+ { echo "configure: error: termcap support not found" 1>&2; exit 1; }
+fi
+
+fi
+
+fi
+
+fi
+
+
+for ac_hdr in termcap.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:1265: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1270 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1275: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=yes"
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+\
+ for ac_hdr in term.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:1303: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1308 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1313: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=yes"
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+\
+ echo "$ac_t""Need term.h since termcap.h is missing" 1>&6
+fi
+done
+
+ for ac_hdr in curses.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:1345: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1350 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1355: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=yes"
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+\
+ for ac_hdr in ncurses.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:1383: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1388 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1393: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=yes"
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+\
+ echo "$ac_t""Need curses.h or ncurses.h" 1>&6
+fi
+done
+
+fi
+done
+
+fi
+done
+
+
+for ac_hdr in sys/cdefs.h vis.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:1432: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1437 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1442: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=yes"
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+done
+
+
+for ac_func in issetugid
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+echo "configure:1472: checking for $ac_func" >&5
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1477 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char $ac_func();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1500: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+fi
+done
+
+for ac_func in fgetln
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+echo "configure:1639: checking for $ac_func" >&5
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1644 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char $ac_func();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1667: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+CCSRCS="$CCSRCS np/fgetln.c"
+fi
+done
+
+for ac_func in strvis
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+echo "configure:1695: checking for $ac_func" >&5
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1700 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char $ac_func();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1723: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+CCSRCS="$CCSRCS np/vis.c"
+fi
+done
+
+for ac_func in strunvis
+do
+echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+echo "configure:1751: checking for $ac_func" >&5
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1756 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char $ac_func();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1779: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+ cat >> confdefs.h <<EOF
+#define $ac_tr_func 1
+EOF
+
+else
+ echo "$ac_t""no" 1>&6
+CCSRCS="$CCSRCS np/unvis.c"
+fi
+done
+
+
+cat > conftest.$ac_ext <<EOF
+#line 1806 "configure"
+#include "confdefs.h"
+#include <sys/cdefs.h>
+#ifdef __RCSID
+ yes
+#endif
+
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "yes" >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ CPPFLAGS="$CPPFLAGS '-D__RCSID(x)='"
+fi
+rm -f conftest*
+
+
+cat > conftest.$ac_ext <<EOF
+#line 1825 "configure"
+#include "confdefs.h"
+#include <sys/cdefs.h>
+#ifdef __COPYRIGHT
+ yes
+#endif
+
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "yes" >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ CPPFLAGS="$CPPFLAGS '-D__COPYRIGHT(x)='"
+fi
+rm -f conftest*
+
+
+cat > conftest.$ac_ext <<EOF
+#line 1844 "configure"
+#include "confdefs.h"
+#include <sys/cdefs.h>
+#ifdef __RENAME
+ yes
+#endif
+
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "yes" >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ CPPFLAGS="$CPPFLAGS '-D__RENAME(x)='"
+fi
+rm -f conftest*
+
+
+cat > conftest.$ac_ext <<EOF
+#line 1863 "configure"
+#include "confdefs.h"
+#include <sys/cdefs.h>
+#ifdef _DIAGASSERT
+ yes
+#endif
+
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "yes" >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ CPPFLAGS="$CPPFLAGS '-D_DIAGASSERT(x)='"
+fi
+rm -f conftest*
+
+
+# Check whether --enable-readline or --disable-readline was given.
+if test "${enable_readline+set}" = set; then
+ enableval="$enable_readline"
+ if test "x$enable_readline" != "xyes" ; then
+ enable_readline="no"
+fi
+
+else
+ enable_readline="yes"
+
+fi
+
+
+# Check whether --enable-debug or --disable-debug was given.
+if test "${enable_debug+set}" = set; then
+ enableval="$enable_debug"
+ if test "x$enable_debug" != "xyes" ; then
+ enable_debug="no"
+fi
+
+else
+ enable_debug="no"
+
+fi
+
+if test "x$enable_debug" = "xyes" ; then
+ CPPFLAGS="$CPPFLAGS -DDEBUG_TTY -DDEBUG_KEY -DDEBUG_READ -DDEBUG"
+ CPPFLAGS="$CPPFLAGS -DDEBUG_REFRESH -DDEBUG_PASTE"
+fi
+
+
+
+ACSRCS="common.c emacs.c vi.c"
+BCSRCS="chared.c el.c hist.c key.c map.c parse.c prompt.c read.c refresh.c search.c sig.c term.c tty.c"
+CCSRCS="$CCSRCS history.c tokenizer.c"
+
+AGCSRCS="fcns.c help.c"
+BGCSRCS="editline.c"
+
+HDRS="chared.h el.h hist.h key.h map.h parse.h prompt.h refresh.h search.h sig.h sys.h term.h tokenizer.h tty.h"
+
+IHDRS="histedit.h"
+
+IHDR_LINKS=
+
+AGHDRS="common.h emacs.h vi.h"
+BGHDRS="fcns.h help.h"
+
+HDR_DIRS="include"
+
+MAN3="editline.3"
+MAN5="editrc.5"
+
+MAN3_LINKS=
+for i in el_init.3 el_end.3 el_reset.3 el_gets.3 el_getc.3 el_push.3 \
+ el_parse.3 el_set.3 el_get.3 el_source.3 el_resize.3 el_line.3 \
+ el_insertstr.3 el_deletestr.3 history_init.3 history_end.3 \
+ history.3 ; do
+ MAN3_LINKS="$MAN3_LINKS editline.3 $i"
+done
+
+MAN_DIRS="man/man3 man/man5"
+
+LIB_DIRS="lib"
+LIB_MAJOR="2"
+LIB_MINOR="6"
+LIB_A="libedit.a"
+LIB_A_LINKS=
+
+if test "x$ABI" = "xelf" ; then
+ LIB_S="libedit.so.$LIB_MAJOR"
+ LIB_S_LINK="libedit.so"
+ LIB_S_LINKS="$LIB_S $LIB_S_LINK"
+ S_LDFLAGS="-shared"
+elif test "x$ABI" = "xaout" ; then
+ LIB_S="libedit.so.$LIB_MAJOR.$LIB_MINOR"
+ LIB_S_LINKS=
+ S_LDFLAGS="-shared"
+elif test "x$ABI" = "xmacho" ; then
+ S_LDFLAGS="-shared"
+ LIB_S="libedit.$LIB_MAJOR.dylib"
+ LIB_S_LINK="libedit.dylib"
+ LIB_S_LINKS="$LIB_S $LIB_S_LINK"
+ if test "x$prefix" = "xNONE" ; then
+ S_LDFLAGS="-undefined suppress -flat_namespace -dynamiclib -compatibility_version $LIB_MAJOR -current_version $LIB_MAJOR -install_name /usr/local/lib/$LIB_S"
+ else
+ S_LDFLAGS="-undefined suppress -flat_namespace -dynamiclib -compatibility_version $LIB_MAJOR -current_version $LIB_MAJOR -install_name $prefix/lib/$LIB_S"
+ fi
+fi
+
+TEST="TEST/test"
+TCSRCS="TEST/test.c"
+
+if test "x$enable_readline" = "xyes" ; then
+ CCSRCS="$CCSRCS readline.c"
+ IHDRS="$IHDRS readline/readline.h"
+ IHDR_LINKS="readline.h readline/history.h"
+ HDR_DIRS="$HDR_DIRS include/readline"
+ LIB_A_LINKS="$LIB_A_LINKS libedit.a libreadline.a"
+ if test "x$ABI" = "xelf" ; then
+ LIB_S_LINKS="$LIB_S_LINKS $LIB_S_LINK libreadline.so"
+ elif test "x$ABI" = "xaout" ; then
+ LIB_S_LINKS="$LIB_S_LINKS $LIB_S libreadline.so.$LIB_MAJOR.$LIB_MINOR"
+ elif test "x$ABI" = "xmacho" ; then
+ LIB_S_LINKS="$LIB_S_LINKS $LIB_S_LINK libreadline.dylib"
+ fi
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+trap '' 1 2 15
+cat > confcache <<\EOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs. It is not useful on other systems.
+# If it contains results you don't want to keep, you may remove or edit it.
+#
+# By default, configure uses ./config.cache as the cache file,
+# creating it if it does not exist already. You can give configure
+# the --cache-file=FILE option to use a different cache file; that is
+# what configure does when it calls configure scripts in
+# subdirectories, so they share the cache.
+# Giving --cache-file=/dev/null disables caching, for debugging configure.
+# config.status only pays attention to the cache file if you give it the
+# --recheck option to rerun configure.
+#
+EOF
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, don't put newlines in cache variables' values.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(set) 2>&1 |
+ case `(ac_space=' '; set | grep ac_space) 2>&1` in
+ *ac_space=\ *)
+ # `set' does not quote correctly, so add quotes (double-quote substitution
+ # turns \\\\ into \\, and sed turns \\ into \).
+ sed -n \
+ -e "s/'/'\\\\''/g" \
+ -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p"
+ ;;
+ *)
+ # `set' quotes correctly as required by POSIX, so do not add quotes.
+ sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p'
+ ;;
+ esac >> confcache
+if cmp -s $cache_file confcache; then
+ :
+else
+ if test -w $cache_file; then
+ echo "updating cache $cache_file"
+ cat confcache > $cache_file
+ else
+ echo "not updating unwritable cache $cache_file"
+ fi
+fi
+rm -f confcache
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# Any assignment to VPATH causes Sun make to only execute
+# the first set of double-colon rules, so remove it if not needed.
+# If there is a colon in the path, we need to keep it.
+if test "x$srcdir" = x.; then
+ ac_vpsub='/^[ ]*VPATH[ ]*=[^:]*$/d'
+fi
+
+trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15
+
+DEFS=-DHAVE_CONFIG_H
+
+# Without the "./", some shells look in PATH for config.status.
+: ${CONFIG_STATUS=./config.status}
+
+echo creating $CONFIG_STATUS
+rm -f $CONFIG_STATUS
+cat > $CONFIG_STATUS <<EOF
+#! /bin/sh
+# Generated automatically by configure.
+# Run this file to recreate the current configuration.
+# This directory was configured as follows,
+# on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+#
+# $0 $ac_configure_args
+#
+# Compiler output produced by configure, useful for debugging
+# configure, is in ./config.log if it exists.
+
+ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]"
+for ac_option
+do
+ case "\$ac_option" in
+ -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+ echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion"
+ exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;;
+ -version | --version | --versio | --versi | --vers | --ver | --ve | --v)
+ echo "$CONFIG_STATUS generated by autoconf version 2.13"
+ exit 0 ;;
+ -help | --help | --hel | --he | --h)
+ echo "\$ac_cs_usage"; exit 0 ;;
+ *) echo "\$ac_cs_usage"; exit 1 ;;
+ esac
+done
+
+ac_given_srcdir=$srcdir
+ac_given_INSTALL="$INSTALL"
+
+trap 'rm -fr `echo "Makefile config.h" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+# Protect against being on the right side of a sed subst in config.status.
+sed 's/%@/@@/; s/@%/@@/; s/%g\$/@g/; /@g\$/s/[\\\\&%]/\\\\&/g;
+ s/@@/%@/; s/@@/@%/; s/@g\$/%g/' > conftest.subs <<\\CEOF
+$ac_vpsub
+$extrasub
+s%@SHELL@%$SHELL%g
+s%@CFLAGS@%$CFLAGS%g
+s%@CPPFLAGS@%$CPPFLAGS%g
+s%@CXXFLAGS@%$CXXFLAGS%g
+s%@FFLAGS@%$FFLAGS%g
+s%@DEFS@%$DEFS%g
+s%@LDFLAGS@%$LDFLAGS%g
+s%@LIBS@%$LIBS%g
+s%@exec_prefix@%$exec_prefix%g
+s%@prefix@%$prefix%g
+s%@program_transform_name@%$program_transform_name%g
+s%@bindir@%$bindir%g
+s%@sbindir@%$sbindir%g
+s%@libexecdir@%$libexecdir%g
+s%@datadir@%$datadir%g
+s%@sysconfdir@%$sysconfdir%g
+s%@sharedstatedir@%$sharedstatedir%g
+s%@localstatedir@%$localstatedir%g
+s%@libdir@%$libdir%g
+s%@includedir@%$includedir%g
+s%@oldincludedir@%$oldincludedir%g
+s%@infodir@%$infodir%g
+s%@mandir@%$mandir%g
+s%@CC@%$CC%g
+s%@A_CFLAGS@%$A_CFLAGS%g
+s%@S_CFLAGS@%$S_CFLAGS%g
+s%@CPP@%$CPP%g
+s%@host@%$host%g
+s%@host_alias@%$host_alias%g
+s%@host_cpu@%$host_cpu%g
+s%@host_vendor@%$host_vendor%g
+s%@host_os@%$host_os%g
+s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g
+s%@INSTALL_SCRIPT@%$INSTALL_SCRIPT%g
+s%@INSTALL_DATA@%$INSTALL_DATA%g
+s%@RANLIB@%$RANLIB%g
+s%@AR@%$AR%g
+s%@ACSRCS@%$ACSRCS%g
+s%@BCSRCS@%$BCSRCS%g
+s%@CCSRCS@%$CCSRCS%g
+s%@AGCSRCS@%$AGCSRCS%g
+s%@BGCSRCS@%$BGCSRCS%g
+s%@HDRS@%$HDRS%g
+s%@IHDRS@%$IHDRS%g
+s%@IHDR_LINKS@%$IHDR_LINKS%g
+s%@AGHDRS@%$AGHDRS%g
+s%@BGHDRS@%$BGHDRS%g
+s%@HDR_DIRS@%$HDR_DIRS%g
+s%@MAN3@%$MAN3%g
+s%@MAN5@%$MAN5%g
+s%@MAN3_LINKS@%$MAN3_LINKS%g
+s%@MAN_DIRS@%$MAN_DIRS%g
+s%@LIB_DIRS@%$LIB_DIRS%g
+s%@LIB_VER@%$LIB_VER%g
+s%@LIB_A@%$LIB_A%g
+s%@LIB_A_LINKS@%$LIB_A_LINKS%g
+s%@LIB_S@%$LIB_S%g
+s%@LIB_S_LINKS@%$LIB_S_LINKS%g
+s%@S_LDFLAGS@%$S_LDFLAGS%g
+s%@TEST@%$TEST%g
+s%@TCSRCS@%$TCSRCS%g
+
+CEOF
+EOF
+
+cat >> $CONFIG_STATUS <<\EOF
+
+# Split the substitutions into bite-sized pieces for seds with
+# small command number limits, like on Digital OSF/1 and HP-UX.
+ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script.
+ac_file=1 # Number of current file.
+ac_beg=1 # First line for current file.
+ac_end=$ac_max_sed_cmds # Line after last line for current file.
+ac_more_lines=:
+ac_sed_cmds=""
+while $ac_more_lines; do
+ if test $ac_beg -gt 1; then
+ sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file
+ else
+ sed "${ac_end}q" conftest.subs > conftest.s$ac_file
+ fi
+ if test ! -s conftest.s$ac_file; then
+ ac_more_lines=false
+ rm -f conftest.s$ac_file
+ else
+ if test -z "$ac_sed_cmds"; then
+ ac_sed_cmds="sed -f conftest.s$ac_file"
+ else
+ ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file"
+ fi
+ ac_file=`expr $ac_file + 1`
+ ac_beg=$ac_end
+ ac_end=`expr $ac_end + $ac_max_sed_cmds`
+ fi
+done
+if test -z "$ac_sed_cmds"; then
+ ac_sed_cmds=cat
+fi
+EOF
+
+cat >> $CONFIG_STATUS <<EOF
+
+CONFIG_FILES=\${CONFIG_FILES-"Makefile"}
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then
+ # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+ case "$ac_file" in
+ *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
+ ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+ *) ac_file_in="${ac_file}.in" ;;
+ esac
+
+ # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories.
+
+ # Remove last slash and all that follows it. Not all systems have dirname.
+ ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+ if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+ # The file is in a subdirectory.
+ test ! -d "$ac_dir" && mkdir "$ac_dir"
+ ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`"
+ # A "../" for each directory in $ac_dir_suffix.
+ ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'`
+ else
+ ac_dir_suffix= ac_dots=
+ fi
+
+ case "$ac_given_srcdir" in
+ .) srcdir=.
+ if test -z "$ac_dots"; then top_srcdir=.
+ else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;;
+ /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;;
+ *) # Relative path.
+ srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix"
+ top_srcdir="$ac_dots$ac_given_srcdir" ;;
+ esac
+
+ case "$ac_given_INSTALL" in
+ [/$]*) INSTALL="$ac_given_INSTALL" ;;
+ *) INSTALL="$ac_dots$ac_given_INSTALL" ;;
+ esac
+
+ echo creating "$ac_file"
+ rm -f "$ac_file"
+ configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure."
+ case "$ac_file" in
+ *Makefile*) ac_comsub="1i\\
+# $configure_input" ;;
+ *) ac_comsub= ;;
+ esac
+
+ ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
+ sed -e "$ac_comsub
+s%@configure_input@%$configure_input%g
+s%@srcdir@%$srcdir%g
+s%@top_srcdir@%$top_srcdir%g
+s%@INSTALL@%$INSTALL%g
+" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file
+fi; done
+rm -f conftest.s*
+
+# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where
+# NAME is the cpp macro being defined and VALUE is the value it is being given.
+#
+# ac_d sets the value in "#define NAME VALUE" lines.
+ac_dA='s%^\([ ]*\)#\([ ]*define[ ][ ]*\)'
+ac_dB='\([ ][ ]*\)[^ ]*%\1#\2'
+ac_dC='\3'
+ac_dD='%g'
+# ac_u turns "#undef NAME" with trailing blanks into "#define NAME VALUE".
+ac_uA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)'
+ac_uB='\([ ]\)%\1#\2define\3'
+ac_uC=' '
+ac_uD='\4%g'
+# ac_e turns "#undef NAME" without trailing blanks into "#define NAME VALUE".
+ac_eA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)'
+ac_eB='$%\1#\2define\3'
+ac_eC=' '
+ac_eD='%g'
+
+if test "${CONFIG_HEADERS+set}" != set; then
+EOF
+cat >> $CONFIG_STATUS <<EOF
+ CONFIG_HEADERS="config.h"
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+fi
+for ac_file in .. $CONFIG_HEADERS; do if test "x$ac_file" != x..; then
+ # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+ case "$ac_file" in
+ *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
+ ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+ *) ac_file_in="${ac_file}.in" ;;
+ esac
+
+ echo creating $ac_file
+
+ rm -f conftest.frag conftest.in conftest.out
+ ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
+ cat $ac_file_inputs > conftest.in
+
+EOF
+
+# Transform confdefs.h into a sed script conftest.vals that substitutes
+# the proper values into config.h.in to produce config.h. And first:
+# Protect against being on the right side of a sed subst in config.status.
+# Protect against being in an unquoted here document in config.status.
+rm -f conftest.vals
+cat > conftest.hdr <<\EOF
+s/[\\&%]/\\&/g
+s%[\\$`]%\\&%g
+s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD}%gp
+s%ac_d%ac_u%gp
+s%ac_u%ac_e%gp
+EOF
+sed -n -f conftest.hdr confdefs.h > conftest.vals
+rm -f conftest.hdr
+
+# This sed command replaces #undef with comments. This is necessary, for
+# example, in the case of _POSIX_SOURCE, which is predefined and required
+# on some systems where configure will not decide to define it.
+cat >> conftest.vals <<\EOF
+s%^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*%/* & */%
+EOF
+
+# Break up conftest.vals because some shells have a limit on
+# the size of here documents, and old seds have small limits too.
+
+rm -f conftest.tail
+while :
+do
+ ac_lines=`grep -c . conftest.vals`
+ # grep -c gives empty output for an empty file on some AIX systems.
+ if test -z "$ac_lines" || test "$ac_lines" -eq 0; then break; fi
+ # Write a limited-size here document to conftest.frag.
+ echo ' cat > conftest.frag <<CEOF' >> $CONFIG_STATUS
+ sed ${ac_max_here_lines}q conftest.vals >> $CONFIG_STATUS
+ echo 'CEOF
+ sed -f conftest.frag conftest.in > conftest.out
+ rm -f conftest.in
+ mv conftest.out conftest.in
+' >> $CONFIG_STATUS
+ sed 1,${ac_max_here_lines}d conftest.vals > conftest.tail
+ rm -f conftest.vals
+ mv conftest.tail conftest.vals
+done
+rm -f conftest.vals
+
+cat >> $CONFIG_STATUS <<\EOF
+ rm -f conftest.frag conftest.h
+ echo "/* $ac_file. Generated automatically by configure. */" > conftest.h
+ cat conftest.in >> conftest.h
+ rm -f conftest.in
+ if cmp -s $ac_file conftest.h 2>/dev/null; then
+ echo "$ac_file is unchanged"
+ rm -f conftest.h
+ else
+ # Remove last slash and all that follows it. Not all systems have dirname.
+ ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+ if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+ # The file is in a subdirectory.
+ test ! -d "$ac_dir" && mkdir "$ac_dir"
+ fi
+ rm -f $ac_file
+ mv conftest.h $ac_file
+ fi
+fi; done
+
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+
+exit 0
+EOF
+chmod +x $CONFIG_STATUS
+rm -fr confdefs* $ac_clean_files
+test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1
+
diff --git a/trunk/main/editline/configure.in b/trunk/main/editline/configure.in
new file mode 100644
index 000000000..7feebd696
--- /dev/null
+++ b/trunk/main/editline/configure.in
@@ -0,0 +1,274 @@
+dnl
+dnl Process this file with autoconf to produce a configure script.
+dnl
+AC_INIT(Makefile.in)
+
+dnl If CFLAGS isn't defined and using gcc, set CFLAGS to something reasonable.
+dnl Otherwise, just prevent autoconf from molesting CFLAGS.
+CFLAGS=$CFLAGS
+AC_PROG_CC
+if test "x$CFLAGS" = "x" ; then
+ no_CFLAGS="yes"
+fi
+if test "x$no_CFLAGS" = "xyes" -a "x$GCC" = "xyes" ; then
+ CFLAGS="-Wall -pipe -g3"
+fi
+A_CFLAGS=""
+AC_SUBST(A_CFLAGS)
+S_CFLAGS="-fPIC -DPIC"
+AC_SUBST(S_CFLAGS)
+AC_PROG_CPP
+
+dnl Platform-specific settings. The ABI can probably be determined
+dnl programmatically, but doing so is error-prone, which makes it generally
+dnl not worth the trouble.
+AC_CANONICAL_HOST
+case "${host}" in
+ *-*-darwin*)
+ CFLAGS="$CFLAGS -fno-common -no-cpp-precomp"
+ ABI="macho"
+ ;;
+ *-*-freebsd*)
+ ABI="elf"
+ ;;
+ *-*-linux* | *cygwin*)
+ if echo ${host} | grep -q cygwin ; then \
+ echo "cygwin detected"; \
+ S_CFLAGS=""; \
+ echo "/* cygdef.h. Generated automatically by configure. */
+#ifndef _CYGDEF_H_
+#define _CYGDEF_H_ 1
+#include <sys/ioctl.h>
+#define __linux__ 1
+
+
+typedef void (*sig_t)(int);
+
+
+#endif /* _CYGDEF_H_ */" > cygdef.h; \
+ echo "
+ #define CYGWIN 1
+" > confdefs.h; \
+ fi
+ ABI="elf"
+ ;;
+ *-*-netbsd*)
+ AC_MSG_CHECKING(ABI)
+ AC_EGREP_CPP(yes,
+[#ifdef __ELF__
+ yes
+#endif
+],
+ ABI="elf",
+ ABI="aout")
+ AC_MSG_RESULT($ABI)
+ ;;
+ *-*-solaris2*)
+ ABI="elf"
+ AC_DEFINE(SUNOS)
+ ;;
+ *)
+ AC_MSG_RESULT(Unsupported operating system: ${host})
+ ABI="elf"
+ ;;
+esac
+
+AC_PROG_INSTALL
+AC_PROG_RANLIB
+AC_PATH_PROG(AR, ar, , $PATH)
+
+dnl Search for termcap access routines in termcap, tinfo, curses, and ncurses.
+AC_CHECK_LIB(termcap, tgetent, , \
+ AC_CHECK_LIB(tinfo, tgetent, , \
+ AC_CHECK_LIB(curses, tgetent, , \
+ AC_CHECK_LIB(ncurses, tgetent, , \
+ AC_MSG_ERROR(termcap support not found)))))
+
+dnl Use termcap.h if it exists; otherwise we need both term.h and [n]curses.h.
+AC_CHECK_HEADERS(termcap.h, , \
+ AC_CHECK_HEADERS(term.h, , \
+ AC_MSG_RESULT(Need term.h since termcap.h is missing))
+ AC_CHECK_HEADERS(curses.h, , \
+ AC_CHECK_HEADERS(ncurses.h, , \
+ AC_MSG_RESULT(Need curses.h or ncurses.h))))
+
+AC_CHECK_HEADERS(sys/cdefs.h vis.h)
+
+AC_CHECK_FUNCS(issetugid)
+AC_CHECK_FUNCS(fgetln, , CCSRCS="$CCSRCS np/fgetln.c")
+AC_CHECK_FUNCS(strvis, , CCSRCS="$CCSRCS np/vis.c")
+AC_CHECK_FUNCS(strunvis, , CCSRCS="$CCSRCS np/unvis.c")
+
+AC_EGREP_CPP(yes,
+[#include <sys/cdefs.h>
+#ifdef __RCSID
+ yes
+#endif
+], , [CPPFLAGS="$CPPFLAGS '-D__RCSID(x)='"])
+
+AC_EGREP_CPP(yes,
+[#include <sys/cdefs.h>
+#ifdef __COPYRIGHT
+ yes
+#endif
+], , [CPPFLAGS="$CPPFLAGS '-D__COPYRIGHT(x)='"])
+
+AC_EGREP_CPP(yes,
+[#include <sys/cdefs.h>
+#ifdef __RENAME
+ yes
+#endif
+], , [CPPFLAGS="$CPPFLAGS '-D__RENAME(x)='"])
+
+AC_EGREP_CPP(yes,
+[#include <sys/cdefs.h>
+#ifdef _DIAGASSERT
+ yes
+#endif
+], , [CPPFLAGS="$CPPFLAGS '-D_DIAGASSERT(x)='"])
+
+dnl Enable readline compatibility by default.
+AC_ARG_ENABLE(readline, [ --disable-readline Disable readline compatibility],
+if test "x$enable_readline" != "xyes" ; then
+ enable_readline="no"
+fi
+,
+enable_readline="yes"
+)
+
+dnl Optionally enable debugging.
+AC_ARG_ENABLE(debug, [ --enable-debug Enable debugging code],
+if test "x$enable_debug" != "xyes" ; then
+ enable_debug="no"
+fi
+,
+enable_debug="no"
+)
+if test "x$enable_debug" = "xyes" ; then
+ CPPFLAGS="$CPPFLAGS -DDEBUG_TTY -DDEBUG_KEY -DDEBUG_READ -DDEBUG"
+ CPPFLAGS="$CPPFLAGS -DDEBUG_REFRESH -DDEBUG_PASTE"
+else
+ CFLAGS="$CFLAGS -O"
+fi
+
+
+dnl
+dnl File lists. This is done here instead of in the Makefile in order to avoid
+dnl the need for conditionals.
+dnl
+
+dnl .c files.
+ACSRCS="common.c emacs.c vi.c"
+BCSRCS="chared.c el.c hist.c key.c map.c parse.c prompt.c read.c refresh.c search.c sig.c term.c tty.c"
+CCSRCS="$CCSRCS history.c tokenizer.c"
+
+dnl Generated .c files.
+AGCSRCS="fcns.c help.c"
+BGCSRCS="editline.c"
+
+dnl .h files.
+HDRS="chared.h el.h hist.h key.h map.h parse.h prompt.h refresh.h search.h sig.h sys.h term.h tokenizer.h tty.h"
+
+dnl Installed .h files.
+IHDRS="histedit.h"
+
+dnl Installed headers for readline compatibility.
+IHDR_LINKS=
+
+dnl Generated .h files.
+AGHDRS="common.h emacs.h vi.h"
+BGHDRS="fcns.h help.h"
+
+dnl Header installation directories.
+HDR_DIRS="include"
+
+dnl Man pages.
+MAN3="editline.3"
+MAN5="editrc.5"
+
+MAN3_LINKS=
+for i in el_init.3 el_end.3 el_reset.3 el_gets.3 el_getc.3 el_push.3 \
+ el_parse.3 el_set.3 el_get.3 el_source.3 el_resize.3 el_line.3 \
+ el_insertstr.3 el_deletestr.3 history_init.3 history_end.3 \
+ history.3 ; do
+ MAN3_LINKS="$MAN3_LINKS editline.3 $i"
+done
+
+dnl Man page installation directories.
+MAN_DIRS="man/man3 man/man5"
+
+dnl Library settings.
+LIB_DIRS="lib"
+LIB_MAJOR="2"
+LIB_MINOR="6"
+LIB_A="libedit.a"
+LIB_A_LINKS=
+
+if test "x$ABI" = "xelf" ; then
+ LIB_S="libedit.so.$LIB_MAJOR"
+ LIB_S_LINK="libedit.so"
+ LIB_S_LINKS="$LIB_S $LIB_S_LINK"
+ S_LDFLAGS="-shared"
+elif test "x$ABI" = "xaout" ; then
+ LIB_S="libedit.so.$LIB_MAJOR.$LIB_MINOR"
+ LIB_S_LINKS=
+ S_LDFLAGS="-shared"
+elif test "x$ABI" = "xmacho" ; then
+ S_LDFLAGS="-shared"
+ LIB_S="libedit.$LIB_MAJOR.dylib"
+ LIB_S_LINK="libedit.dylib"
+ LIB_S_LINKS="$LIB_S $LIB_S_LINK"
+ if test "x$prefix" = "xNONE" ; then
+ S_LDFLAGS="-undefined suppress -flat_namespace -dynamiclib -compatibility_version $LIB_MAJOR -current_version $LIB_MAJOR -install_name /usr/local/lib/$LIB_S"
+ else
+ S_LDFLAGS="-undefined suppress -flat_namespace -dynamiclib -compatibility_version $LIB_MAJOR -current_version $LIB_MAJOR -install_name $prefix/lib/$LIB_S"
+ fi
+fi
+
+dnl Test program.
+TEST="TEST/test"
+TCSRCS="TEST/test.c"
+
+dnl Add files to the lists if readline compatibility is enabled.
+if test "x$enable_readline" = "xyes" ; then
+ CCSRCS="$CCSRCS readline.c"
+ IHDRS="$IHDRS readline/readline.h"
+ IHDR_LINKS="readline.h readline/history.h"
+ HDR_DIRS="$HDR_DIRS include/readline"
+ LIB_A_LINKS="$LIB_A_LINKS libedit.a libreadline.a"
+ if test "x$ABI" = "xelf" ; then
+ LIB_S_LINKS="$LIB_S_LINKS $LIB_S_LINK libreadline.so"
+ elif test "x$ABI" = "xaout" ; then
+ LIB_S_LINKS="$LIB_S_LINKS $LIB_S libreadline.so.$LIB_MAJOR.$LIB_MINOR"
+ elif test "x$ABI" = "xmacho" ; then
+ LIB_S_LINKS="$LIB_S_LINKS $LIB_S_LINK libreadline.dylib"
+ fi
+fi
+
+AC_SUBST(ACSRCS)
+AC_SUBST(BCSRCS)
+AC_SUBST(CCSRCS)
+AC_SUBST(AGCSRCS)
+AC_SUBST(BGCSRCS)
+AC_SUBST(HDRS)
+AC_SUBST(IHDRS)
+AC_SUBST(IHDR_LINKS)
+AC_SUBST(AGHDRS)
+AC_SUBST(BGHDRS)
+AC_SUBST(HDR_DIRS)
+AC_SUBST(MAN3)
+AC_SUBST(MAN5)
+AC_SUBST(MAN3_LINKS)
+AC_SUBST(MAN_DIRS)
+AC_SUBST(LIB_DIRS)
+AC_SUBST(LIB_VER)
+AC_SUBST(LIB_A)
+AC_SUBST(LIB_A_LINKS)
+AC_SUBST(LIB_S)
+AC_SUBST(LIB_S_LINKS)
+AC_SUBST(S_LDFLAGS)
+AC_SUBST(TEST)
+AC_SUBST(TCSRCS)
+
+AC_CONFIG_HEADER(config.h)
+AC_OUTPUT(Makefile)
diff --git a/trunk/main/editline/editline.3 b/trunk/main/editline/editline.3
new file mode 100644
index 000000000..28f6ddb84
--- /dev/null
+++ b/trunk/main/editline/editline.3
@@ -0,0 +1,646 @@
+.\" $NetBSD: editline.3,v 1.25 2002/01/15 02:46:22 wiz Exp $
+.\"
+.\" Copyright (c) 1997-1999 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This file was contributed to The NetBSD Foundation by Luke Mewburn.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the NetBSD
+.\" Foundation, Inc. and its contributors.
+.\" 4. Neither the name of The NetBSD Foundation nor the names of its
+.\" contributors may be used to endorse or promote products derived
+.\" from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd November 12, 1999
+.Os
+.Dt EDITLINE 3
+.Sh NAME
+.Nm editline ,
+.Nm el_init ,
+.Nm el_end ,
+.Nm el_reset ,
+.Nm el_gets ,
+.Nm el_getc ,
+.Nm el_push ,
+.Nm el_parse ,
+.Nm el_set ,
+.Nm el_source ,
+.Nm el_resize ,
+.Nm el_line ,
+.Nm el_insertstr ,
+.Nm el_deletestr ,
+.Nm history_init ,
+.Nm history_end ,
+.Nm history
+.Nd line editor and history functions
+.Sh LIBRARY
+.Lb libedit
+.Sh SYNOPSIS
+.Fd #include <histedit.h>
+.Ft EditLine *
+.Fn el_init "const char *prog" "FILE *fin" "FILE *fout" "FILE *ferr"
+.Ft void
+.Fn el_end "EditLine *e"
+.Ft void
+.Fn el_reset "EditLine *e"
+.Ft const char *
+.Fn el_gets "EditLine *e" "int *count"
+.Ft int
+.Fn el_getc "EditLine *e" "char *ch"
+.Ft void
+.Fn el_push "EditLine *e" "const char *str"
+.Ft int
+.Fn el_parse "EditLine *e" "int argc" "char *argv[]"
+.Ft int
+.Fn el_set "EditLine *e" "int op" "..."
+.Ft int
+.Fn el_get "EditLine *e" "int op" "void *result"
+.Ft int
+.Fn el_source "EditLine *e" "const char *file"
+.Ft void
+.Fn el_resize "EditLine *e"
+.Ft const LineInfo *
+.Fn el_line "EditLine *e"
+.Ft int
+.Fn el_insertstr "EditLine *e" "const char *str"
+.Ft void
+.Fn el_deletestr "EditLine *e" "int count"
+.Ft History *
+.Fn history_init
+.Ft void
+.Fn history_end "History *h"
+.Ft int
+.Fn history "History *h" "HistEvent *ev" "int op" "..."
+.Sh DESCRIPTION
+The
+.Nm
+library provides generic line editing and history functions,
+similar to those found in
+.Xr sh 1 .
+.Pp
+These functions are available in the
+.Nm libedit
+library (which needs the
+.Nm libtermcap
+library).
+Programs should be linked with
+.Fl ledit ltermcap .
+.Sh LINE EDITING FUNCTIONS
+The line editing functions use a common data structure,
+.Fa EditLine ,
+which is created by
+.Fn el_init
+and freed by
+.Fn el_end .
+.Pp
+The following functions are available:
+.Bl -tag -width 4n
+.It Fn el_init
+Initialise the line editor, and return a data structure
+to be used by all other line editing functions.
+.Fa prog
+is the name of the invoking program, used when reading the
+.Xr editrc 5
+file to determine which settings to use.
+.Fa fin ,
+.Fa fout
+and
+.Fa ferr
+are the input, output, and error streams (respectively) to use.
+In this documentation, references to
+.Dq the tty
+are actually to this input/output stream combination.
+.It Fn el_end
+Clean up and finish with
+.Fa e ,
+assumed to have been created with
+.Fn el_init .
+.It Fn el_reset
+Reset the tty and the parser.
+This should be called after an error which may have upset the tty's
+state.
+.It Fn el_gets
+Read a line from the tty.
+.Fa count
+is modified to contain the number of characters read.
+Returns the line read if successful, or
+.Dv NULL
+if no characters were read or if an error occurred.
+.It Fn el_getc
+Read a character from the tty.
+.Fa ch
+is modified to contain the character read.
+Returns the number of characters read if successful, -1 otherwise.
+.It Fn el_push
+Pushes
+.Fa str
+back onto the input stream.
+This is used by the macro expansion mechanism.
+Refer to the description of
+.Ic bind
+.Fl s
+in
+.Xr editrc 5
+for more information.
+.It Fn el_parse
+Parses the
+.Fa argv
+array (which is
+.Fa argc
+elements in size)
+to execute builtin
+.Nm
+commands.
+If the command is prefixed with
+.Dq prog :
+then
+.Fn el_parse
+will only execute the command if
+.Dq prog
+matches the
+.Fa prog
+argument supplied to
+.Fn el_init .
+The return value is
+-1 if the command is unknown,
+0 if there was no error or
+.Dq prog
+didn't match, or
+1 if the command returned an error.
+Refer to
+.Xr editrc 5
+for more information.
+.It Fn el_set
+Set
+.Nm
+parameters.
+.Fa op
+determines which parameter to set, and each operation has its
+own parameter list.
+.Pp
+The following values for
+.Fa op
+are supported, along with the required argument list:
+.Bl -tag -width 4n
+.It Dv EL_PROMPT , Fa "char *(*f)(EditLine *)"
+Define prompt printing function as
+.Fa f ,
+which is to return a string that contains the prompt.
+.It Dv EL_RPROMPT , Fa "char *(*f)(EditLine *)"
+Define right side prompt printing function as
+.Fa f ,
+which is to return a string that contains the prompt.
+.It Dv EL_TERMINAL , Fa "const char *type"
+Define terminal type of the tty to be
+.Fa type ,
+or to
+.Ev TERM
+if
+.Fa type
+is
+.Dv NULL .
+.It Dv EL_EDITOR , Fa "const char *mode"
+Set editing mode to
+.Fa mode ,
+which must be one of
+.Dq emacs
+or
+.Dq vi .
+.It Dv EL_SIGNAL , Fa "int flag"
+If
+.Fa flag
+is non-zero,
+.Nm
+will install its own signal handler for the following signals when
+reading command input:
+.Dv SIGCONT ,
+.Dv SIGHUP ,
+.Dv SIGINT ,
+.Dv SIGQUIT ,
+.Dv SIGSTOP ,
+.Dv SIGTERM ,
+.Dv SIGTSTP ,
+and
+.Dv SIGWINCH .
+Otherwise, the current signal handlers will be used.
+.It Dv EL_BIND , Xo
+.Fa "const char *" ,
+.Fa "..." ,
+.Dv NULL
+.Xc
+Perform the
+.Ic bind
+builtin command.
+Refer to
+.Xr editrc 5
+for more information.
+.It Dv EL_ECHOTC , Xo
+.Fa "const char *" ,
+.Fa "..." ,
+.Dv NULL
+.Xc
+Perform the
+.Ic echotc
+builtin command.
+Refer to
+.Xr editrc 5
+for more information.
+.It Dv EL_SETTC , Xo
+.Fa "const char *" ,
+.Fa "..." ,
+.Dv NULL
+.Xc
+Perform the
+.Ic settc
+builtin command.
+Refer to
+.Xr editrc 5
+for more information.
+.It Dv EL_SETTY , Xo
+.Fa "const char *" ,
+.Fa "..." ,
+.Dv NULL
+.Xc
+Perform the
+.Ic setty
+builtin command.
+Refer to
+.Xr editrc 5
+for more information.
+.It Dv EL_TELLTC , Xo
+.Fa "const char *" ,
+.Fa "..." ,
+.Dv NULL
+.Xc
+Perform the
+.Ic telltc
+builtin command.
+Refer to
+.Xr editrc 5
+for more information.
+.It Dv EL_ADDFN , Xo
+.Fa "const char *name" ,
+.Fa "const char *help" ,
+.Fa "unsigned char (*func)(EditLine *e, int ch)
+.Xc
+Add a user defined function,
+.Fn func ,
+referred to as
+.Fa name
+which is invoked when a key which is bound to
+.Fa name
+is entered.
+.Fa help
+is a description of
+.Fa name .
+At invocation time,
+.Fa ch
+is the key which caused the invocation.
+The return value of
+.Fn func
+should be one of:
+.Bl -tag -width "CC_REDISPLAY"
+.It Dv CC_NORM
+Add a normal character.
+.It Dv CC_NEWLINE
+End of line was entered.
+.It Dv CC_EOF
+EOF was entered.
+.It Dv CC_ARGHACK
+Expecting further command input as arguments, do nothing visually.
+.It Dv CC_REFRESH
+Refresh display.
+.It Dv CC_REFRESH_BEEP
+Refresh display, and beep.
+.It Dv CC_CURSOR
+Cursor moved, so update and perform
+.Dv CC_REFRESH .
+.It Dv CC_REDISPLAY
+Redisplay entire input line.
+This is useful if a key binding outputs extra information.
+.It Dv CC_ERROR
+An error occurred.
+Beep, and flush tty.
+.It Dv CC_FATAL
+Fatal error, reset tty to known state.
+.El
+.It Dv EL_HIST , Xo
+.Fa "History *(*func)(History *, int op, ...)" ,
+.Fa "const char *ptr"
+.Xc
+Defines which history function to use, which is usually
+.Fn history .
+.Fa ptr
+should be the value returned by
+.Fn history_init .
+.It Dv EL_EDITMODE , Fa "int flag"
+If
+.Fa flag
+is non-zero,
+editing is enabled (the default).
+Note that this is only an indication, and does not
+affect the operation of
+.Nm "" .
+At this time, it is the caller's responsibility to
+check this
+(using
+.Fn el_get )
+to determine if editing should be enabled or not.
+.It Dv EL_GETCFN , Fa "int (*f)(EditLine *, char *c)"
+Define the character reading function as
+.Fa f ,
+which is to return the number of characters read and store them in
+.Fa c .
+This function is called internally by
+.Fn el_gets
+and
+.Fn el_getc .
+The builtin function can be set or restored with the special function
+name ``EL_BUILTIN_GETCFN''.
+.It Dv EL_CLIENTDATA , Fa "void *data"
+Register
+.Fa data
+to be associated with this EditLine structure. It can be retrieved with
+the corresponding
+.Fn el_get
+call.
+.El
+.It Fn el_get
+Get
+.Nm
+parameters.
+.Fa op
+determines which parameter to retrieve into
+.Fa result .
+.Pp
+The following values for
+.Fa op
+are supported, along with actual type of
+.Fa result :
+.Bl -tag -width 4n
+.It Dv EL_PROMPT , Fa "char *(*f)(EditLine *)"
+Return a pointer to the function that displays the prompt.
+.It Dv EL_RPROMPT , Fa "char *(*f)(EditLine *)"
+Return a pointer to the function that displays the rightside prompt.
+.It Dv EL_EDITOR , Fa "const char *"
+Return the name of the editor, which will be one of
+.Dq emacs
+or
+.Dq vi .
+.It Dv EL_SIGNAL , Fa "int *"
+Return non-zero if
+.Nm
+has installed private signal handlers (see
+.Fn el_get
+above).
+.It Dv EL_EDITMODE, Fa "int *"
+Return non-zero if editing is enabled.
+.It Dv EL_GETCFN, Fa "int (**f)(EditLine *, char *)"
+Return a pointer to the function that read characters, which is equal to
+``EL_BUILTIN_GETCFN'' in the case of the default builtin function.
+.It Dv EL_CLIENTDATA , Fa "void **data"
+Retrieve
+.Fa data
+previously registered with the corresponding
+.Fn el_set
+call.
+.El
+.It Fn el_source
+Initialise
+.Nm
+by reading the contents of
+.Fa file .
+.Fn el_parse
+is called for each line in
+.Fa file .
+If
+.Fa file
+is
+.Dv NULL ,
+try
+.Pa $PWD/.editrc
+then
+.Pa $HOME/.editrc .
+Refer to
+.Xr editrc 5
+for details on the format of
+.Fa file .
+.It Fn el_resize
+Must be called if the terminal size changes.
+If
+.Dv EL_SIGNAL
+has been set with
+.Fn el_set ,
+then this is done automatically.
+Otherwise, it's the responsibility of the application to call
+.Fn el_resize
+on the appropriate occasions.
+.It Fn el_line
+Return the editing information for the current line in a
+.Fa LineInfo
+structure, which is defined as follows:
+.Bd -literal
+typedef struct lineinfo {
+ const char *buffer; /* address of buffer */
+ const char *cursor; /* address of cursor */
+ const char *lastchar; /* address of last character */
+} LineInfo;
+.Ed
+.It Fn el_insertstr
+Insert
+.Fa str
+into the line at the cursor.
+Returns -1 if
+.Fa str
+is empty or won't fit, and 0 otherwise.
+.It Fn el_deletestr
+Delete
+.Fa num
+characters before the cursor.
+.El
+.Sh HISTORY LIST FUNCTIONS
+The history functions use a common data structure,
+.Fa History ,
+which is created by
+.Fn history_init
+and freed by
+.Fn history_end .
+.Pp
+The following functions are available:
+.Bl -tag -width 4n
+.It Fn history_init
+Initialise the history list, and return a data structure
+to be used by all other history list functions.
+.It Fn history_end
+Clean up and finish with
+.Fa h ,
+assumed to have been created with
+.Fn history_init .
+.It Fn history
+Perform operation
+.Fa op
+on the history list, with optional arguments as needed by the
+operation.
+.Fa ev
+is changed accordingly to operation.
+The following values for
+.Fa op
+are supported, along with the required argument list:
+.Bl -tag -width 4n
+.It Dv H_SETSIZE , Fa "int size"
+Set size of history to
+.Fa size
+elements.
+.It Dv H_GETSIZE
+Get number of events currently in history.
+.It Dv H_END
+Cleans up and finishes with
+.Fa h ,
+assumed to be created with
+.Fn history_init .
+.It Dv H_CLEAR
+Clear the history.
+.It Dv H_FUNC , Xo
+.Fa "void *ptr" ,
+.Fa "history_gfun_t first" ,
+.Fa "history_gfun_t next" ,
+.Fa "history_gfun_t last" ,
+.Fa "history_gfun_t prev" ,
+.Fa "history_gfun_t curr" ,
+.Fa "history_sfun_t set" ,
+.Fa "history_vfun_t clear" ,
+.Fa "history_efun_t enter" ,
+.Fa "history_efun_t add"
+.Xc
+Define functions to perform various history operations.
+.Fa ptr
+is the argument given to a function when it's invoked.
+.It Dv H_FIRST
+Return the first element in the history.
+.It Dv H_LAST
+Return the last element in the history.
+.It Dv H_PREV
+Return the previous element in the history.
+.It Dv H_NEXT
+Return the next element in the history.
+.It Dv H_CURR
+Return the current element in the history.
+.It Dv H_SET
+Set the cursor to point to the requested element.
+.It Dv H_ADD , Fa "const char *str"
+Append
+.Fa str
+to the current element of the history, or create an element with
+.It Dv H_APPEND , Fa "const char *str"
+Append
+.Fa str
+to the last new element of the history.
+.It Dv H_ENTER , Fa "const char *str"
+Add
+.Fa str
+as a new element to the history, and, if necessary,
+removing the oldest entry to keep the list to the created size.
+.It Dv H_PREV_STR , Fa "const char *str"
+Return the closest previous event that starts with
+.Fa str .
+.It Dv H_NEXT_STR , Fa "const char *str"
+Return the closest next event that starts with
+.Fa str .
+.It Dv H_PREV_EVENT , Fa "int e"
+Return the previous event numbered
+.Fa e .
+.It Dv H_NEXT_EVENT , Fa "int e"
+Return the next event numbered
+.Fa e .
+.It Dv H_LOAD , Fa "const char *file"
+Load the history list stored in
+.Fa file .
+.It Dv H_SAVE , Fa "const char *file"
+Save the history list to
+.Fa file .
+.El
+.Pp
+.Fn history
+returns 0 if the operation
+.Fa op
+succeeds. Otherwise, -1 is returned and
+.Fa ev
+is updated to contain more details about the error.
+.El
+.\"XXX.Sh EXAMPLES
+.\"XXX: provide some examples
+.Sh SEE ALSO
+.Xr sh 1 ,
+.Xr signal 3 ,
+.Xr termcap 3 ,
+.Xr editrc 5
+.Sh HISTORY
+The
+.Nm
+library first appeared in
+.Bx 4.4 .
+.Dv CC_REDISPLAY
+appeared in
+.Nx 1.3 .
+.Dv CC_REFRESH_BEEP ,
+.Dv EL_EDITMODE
+and the readline emulation appeared in
+.Nx 1.4 .
+.Dv EL_RPROMPT
+appeared in
+.Nx 1.5 .
+.Sh AUTHORS
+The
+.Nm
+library was written by Christos Zoulas.
+Luke Mewburn wrote this manual and implemented
+.Dv CC_REDISPLAY ,
+.Dv CC_REFRESH_BEEP ,
+.Dv EL_EDITMODE ,
+and
+.Dv EL_RPROMPT .
+Jaromir Dolecek implemented the readline emulation.
+.Sh BUGS
+The tokenization functions are not publicly defined in
+.Fd <histedit.h> .
+.Pp
+At this time, it is the responsibility of the caller to
+check the result of the
+.Dv EL_EDITMODE
+operation of
+.Fn el_get
+(after an
+.Fn el_source
+or
+.Fn el_parse )
+to determine if
+.Nm
+should be used for further input.
+I.e.,
+.Dv EL_EDITMODE
+is purely an indication of the result of the most recent
+.Xr editrc 5
+.Ic edit
+command.
diff --git a/trunk/main/editline/editrc.5 b/trunk/main/editline/editrc.5
new file mode 100644
index 000000000..ddd12897b
--- /dev/null
+++ b/trunk/main/editline/editrc.5
@@ -0,0 +1,491 @@
+.\" $NetBSD: editrc.5,v 1.12 2002/01/15 02:46:44 wiz Exp $
+.\"
+.\" Copyright (c) 1997-2000 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This file was contributed to The NetBSD Foundation by Luke Mewburn.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the NetBSD
+.\" Foundation, Inc. and its contributors.
+.\" 4. Neither the name of The NetBSD Foundation nor the names of its
+.\" contributors may be used to endorse or promote products derived
+.\" from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd November 8, 2000
+.Os
+.Dt EDITRC 5
+.Sh NAME
+.Nm editrc
+.Nd configuration file for editline library
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+The
+.Nm
+file defines various settings to be used by the
+.Xr editline 3
+library.
+.Pp
+The format of each line is:
+.Dl [prog:]command [arg [...]]
+.Pp
+.Ar command
+is one of the
+.Xr editline 3
+builtin commands.
+Refer to
+.Sx BUILTIN COMMANDS
+for more information.
+.Pp
+.Ar prog
+is the program name string that a program defines when it calls
+.Xr el_init 3
+to setup
+.Xr editline 3 ,
+which is usually
+.Va argv[0] .
+.Ar command
+will be executed for any program which matches
+.Ar prog .
+.Pp
+.Ar prog
+may also be a
+.Xr regex 3
+style
+regular expression, in which case
+.Ar command
+will be executed for any program that matches the regular expression.
+.Pp
+If
+.Ar prog
+is absent,
+.Ar command
+is executed for all programs.
+.Sh BUILTIN COMMANDS
+The
+.Nm editline
+library has some builtin commands, which affect the way
+that the line editing and history functions operate.
+These are based on similar named builtins present in the
+.Xr tcsh 1
+shell.
+.Pp
+The following builtin commands are available:
+.Bl -tag -width 4n
+.It Ic bind Xo
+.Op Fl a
+.Op Fl e
+.Op Fl k
+.Op Fl l
+.Op Fl r
+.Op Fl s
+.Op Fl v
+.Op Ar key Op Ar command
+.Xc
+Without options, list all bound keys, and the editor command to which
+each is bound.
+If
+.Ar key
+is supplied, show the bindings for
+.Ar key .
+If
+.Ar key command
+is supplied, bind
+.Ar command
+to
+.Ar key .
+Options include:
+.Bl -tag -width 4n
+.It Fl e
+Bind all keys to the standard GNU Emacs-like bindings.
+.It Fl v
+Bind all keys to the standard
+.Xr vi 1 -like
+bindings.
+.It Fl a
+List or change key bindings in the
+.Xr vi 1
+mode alternate (command mode) key map.
+.It Fl k
+.Ar key
+is interpreted as a symbolic arrow key name, which may be one of
+.Sq up ,
+.Sq down ,
+.Sq left
+or
+.Sq right .
+.It Fl l
+List all editor commands and a short description of each.
+.It Fl r
+Remove a key's binding.
+.It Fl s
+.Ar command
+is taken as a literal string and treated as terminal input when
+.Ar key
+is typed.
+Bound keys in
+.Ar command
+are themselves reinterpreted, and this continues for ten levels of
+interpretation.
+.El
+.Pp
+.Ar command
+may be one of the commands documented in
+.Sx "EDITOR COMMANDS"
+below, or another key.
+.Pp
+.Ar key
+and
+.Ar command
+can contain control characters of the form
+.Sm off
+.Sq No ^ Ar character
+.Sm on
+.Po
+e.g.
+.Sq ^A
+.Pc ,
+and the following backslashed escape sequences:
+.Pp
+.Bl -tag -compact -offset indent -width 4n
+.It Ic \ea
+Bell
+.It Ic \eb
+Backspace
+.It Ic \ee
+Escape
+.It Ic \ef
+Formfeed
+.It Ic \en
+Newline
+.It Ic \er
+Carriage return
+.It Ic \et
+Horizontal tab
+.It Ic \ev
+Vertical tab
+.Sm off
+.It Sy \e Ar nnn
+.Sm on
+The ASCII character corresponding to the octal number
+.Ar nnn .
+.El
+.Pp
+.Sq \e
+nullifies the special meaning of the following character,
+if it has any, notably
+.Sq \e
+and
+.Sq ^ .
+.It Ic echotc Xo
+.Op Fl sv
+.Ar arg
+.Ar ...
+.Xc
+Exercise terminal capabilities given in
+.Ar arg Ar ... .
+If
+.Ar arg
+is
+.Sq baud ,
+.Sq cols ,
+.Sq lines ,
+.Sq rows ,
+.Sq meta or
+.Sq tabs ,
+the value of that capability is printed, with
+.Dq yes
+or
+.Dq no
+indicating that the terminal does or does not have that capability.
+.Pp
+.Fl s
+returns an emptry string for non-existent capabilities, rather than
+causing an error.
+.Fl v
+causes messages to be verbose.
+.It Ic edit Op Li on | Li off
+Enable or disable the
+.Nm editline
+functionality in a program.
+.It Ic history
+List the history.
+.It Ic telltc
+List the values of all the terminal capabilities (see
+.Xr termcap 5 ) .
+.It Ic settc Ar cap Ar val
+Set the terminal capability
+.Ar cap
+to
+.Ar val ,
+as defined in
+.Xr termcap 5 .
+No sanity checking is done.
+.It Ic setty Xo
+.Op Fl a
+.Op Fl d
+.Op Fl q
+.Op Fl x
+.Op Ar +mode
+.Op Ar -mode
+.Op Ar mode
+.Xc
+Control which tty modes that
+.Nm
+won't allow the user to change.
+.Fl d ,
+.Fl q
+or
+.Fl x
+tells
+.Ic setty
+to act on the
+.Sq edit ,
+.Sq quote
+or
+.Sq execute
+set of tty modes respectively; defaulting to
+.Fl x .
+.Pp
+Without other arguments,
+.Ic setty
+lists the modes in the chosen set which are fixed on
+.Po
+.Sq +mode
+.Pc
+or off
+.Po
+.Sq -mode
+.Pc .
+.Fl a
+lists all tty modes in the chosen set regardless of the setting.
+With
+.Ar +mode ,
+.Ar -mode
+or
+.Ar mode ,
+fixes
+.Ar mode
+on or off or removes control of
+.Ar mode
+in the chosen set.
+.El
+.Sh EDITOR COMMANDS
+The following editor commands are available for use in key bindings:
+.\" Section automatically generated with makelist
+.Bl -tag -width 4n
+.It Ic vi-paste-next
+Vi paste previous deletion to the right of the cursor.
+.It Ic vi-paste-prev
+Vi paste previous deletion to the left of the cursor.
+.It Ic vi-prev-space-word
+Vi move to the previous space delimited word.
+.It Ic vi-prev-word
+Vi move to the previous word.
+.It Ic vi-next-space-word
+Vi move to the next space delimited word.
+.It Ic vi-next-word
+Vi move to the next word.
+.It Ic vi-change-case
+Vi change case of character under the cursor and advance one character.
+.It Ic vi-change-meta
+Vi change prefix command.
+.It Ic vi-insert-at-bol
+Vi enter insert mode at the beginning of line.
+.It Ic vi-replace-char
+Vi replace character under the cursor with the next character typed.
+.It Ic vi-replace-mode
+Vi enter replace mode.
+.It Ic vi-substitute-char
+Vi replace character under the cursor and enter insert mode.
+.It Ic vi-substitute-line
+Vi substitute entire line.
+.It Ic vi-change-to-eol
+Vi change to end of line.
+.It Ic vi-insert
+Vi enter insert mode.
+.It Ic vi-add
+Vi enter insert mode after the cursor.
+.It Ic vi-add-at-eol
+Vi enter insert mode at end of line.
+.It Ic vi-delete-meta
+Vi delete prefix command.
+.It Ic vi-end-word
+Vi move to the end of the current space delimited word.
+.It Ic vi-to-end-word
+Vi move to the end of the current word.
+.It Ic vi-undo
+Vi undo last change.
+.It Ic vi-command-mode
+Vi enter command mode (use alternative key bindings).
+.It Ic vi-zero
+Vi move to the beginning of line.
+.It Ic vi-delete-prev-char
+Vi move to previous character (backspace).
+.It Ic vi-list-or-eof
+Vi list choices for completion or indicate end of file if empty line.
+.It Ic vi-kill-line-prev
+Vi cut from beginning of line to cursor.
+.It Ic vi-search-prev
+Vi search history previous.
+.It Ic vi-search-next
+Vi search history next.
+.It Ic vi-repeat-search-next
+Vi repeat current search in the same search direction.
+.It Ic vi-repeat-search-prev
+Vi repeat current search in the opposite search direction.
+.It Ic vi-next-char
+Vi move to the character specified next.
+.It Ic vi-prev-char
+Vi move to the character specified previous.
+.It Ic vi-to-next-char
+Vi move up to the character specified next.
+.It Ic vi-to-prev-char
+Vi move up to the character specified previous.
+.It Ic vi-repeat-next-char
+Vi repeat current character search in the same search direction.
+.It Ic vi-repeat-prev-char
+Vi repeat current character search in the opposite search direction.
+.It Ic em-delete-or-list
+Delete character under cursor or list completions if at end of line.
+.It Ic em-delete-next-word
+Cut from cursor to end of current word.
+.It Ic em-yank
+Paste cut buffer at cursor position.
+.It Ic em-kill-line
+Cut the entire line and save in cut buffer.
+.It Ic em-kill-region
+Cut area between mark and cursor and save in cut buffer.
+.It Ic em-copy-region
+Copy area between mark and cursor to cut buffer.
+.It Ic em-gosmacs-traspose
+Exchange the two characters before the cursor.
+.It Ic em-next-word
+Move next to end of current word.
+.It Ic em-upper-case
+Uppercase the characters from cursor to end of current word.
+.It Ic em-capitol-case
+Capitalize the characters from cursor to end of current word.
+.It Ic em-lower-case
+Lowercase the characters from cursor to end of current word.
+.It Ic em-set-mark
+Set the mark at cursor.
+.It Ic em-exchange-mark
+Exchange the cursor and mark.
+.It Ic em-universal-argument
+Universal argument (argument times 4).
+.It Ic em-meta-next
+Add 8th bit to next character typed.
+.It Ic em-toggle-overwrite
+Switch from insert to overwrite mode or vice versa.
+.It Ic em-copy-prev-word
+Copy current word to cursor.
+.It Ic em-inc-search-next
+Emacs incremental next search.
+.It Ic em-inc-search-prev
+Emacs incremental reverse search.
+.It Ic ed-end-of-file
+Indicate end of file.
+.It Ic ed-insert
+Add character to the line.
+.It Ic ed-delete-prev-word
+Delete from beginning of current word to cursor.
+.It Ic ed-delete-next-char
+Delete character under cursor.
+.It Ic ed-kill-line
+Cut to the end of line.
+.It Ic ed-move-to-end
+Move cursor to the end of line.
+.It Ic ed-move-to-beg
+Move cursor to the beginning of line.
+.It Ic ed-transpose-chars
+Exchange the character to the left of the cursor with the one under it.
+.It Ic ed-next-char
+Move to the right one character.
+.It Ic ed-prev-word
+Move to the beginning of the current word.
+.It Ic ed-prev-char
+Move to the left one character.
+.It Ic ed-quoted-insert
+Add the next character typed verbatim.
+.It Ic ed-digit
+Adds to argument or enters a digit.
+.It Ic ed-argument-digit
+Digit that starts argument.
+.It Ic ed-unassigned
+Indicates unbound character.
+.It Ic ed-tty-sigint
+Tty interrupt character.
+.It Ic ed-tty-dsusp
+Tty delayed suspend character.
+.It Ic ed-tty-flush-output
+Tty flush output characters.
+.It Ic ed-tty-sigquit
+Tty quit character.
+.It Ic ed-tty-sigtstp
+Tty suspend character.
+.It Ic ed-tty-stop-output
+Tty disallow output characters.
+.It Ic ed-tty-start-output
+Tty allow output characters.
+.It Ic ed-newline
+Execute command.
+.It Ic ed-delete-prev-char
+Delete the character to the left of the cursor.
+.It Ic ed-clear-screen
+Clear screen leaving current line at the top.
+.It Ic ed-redisplay
+Redisplay everything.
+.It Ic ed-start-over
+Erase current line and start from scratch.
+.It Ic ed-sequence-lead-in
+First character in a bound sequence.
+.It Ic ed-prev-history
+Move to the previous history line.
+.It Ic ed-next-history
+Move to the next history line.
+.It Ic ed-search-prev-history
+Search previous in history for a line matching the current.
+.It Ic ed-search-next-history
+Search next in history for a line matching the current.
+.It Ic ed-prev-line
+Move up one line.
+.It Ic ed-next-line
+Move down one line.
+.It Ic ed-command
+Editline extended command.
+.El
+.\" End of section automatically generated with makelist
+.Sh SEE ALSO
+.Xr editline 3 ,
+.Xr regex 3 ,
+.Xr termcap 5
+.Sh AUTHORS
+The
+.Nm editline
+library was written by Christos Zoulas,
+and this manual was written by Luke Mewburn,
+with some sections inspired by
+.Xr tcsh 1 .
diff --git a/trunk/main/editline/el.c b/trunk/main/editline/el.c
new file mode 100644
index 000000000..514316fbe
--- /dev/null
+++ b/trunk/main/editline/el.c
@@ -0,0 +1,509 @@
+/* $NetBSD: el.c,v 1.29 2002/03/18 16:00:52 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+#if !defined(lint) && !defined(SCCSID)
+#if 0
+static char sccsid[] = "@(#)el.c 8.2 (Berkeley) 1/3/94";
+#else
+__RCSID("$NetBSD: el.c,v 1.29 2002/03/18 16:00:52 christos Exp $");
+#endif
+#endif /* not lint && not SCCSID */
+
+/*
+ * el.c: EditLine interface functions
+ */
+#include <sys/types.h>
+#include <sys/param.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include "el.h"
+
+/* el_init():
+ * Initialize editline and set default parameters.
+ */
+public EditLine *
+el_init(const char *prog, FILE *fin, FILE *fout, FILE *ferr)
+{
+
+ EditLine *el = (EditLine *) el_malloc(sizeof(EditLine));
+
+ if (el == NULL)
+ return (NULL);
+
+ memset(el, 0, sizeof(EditLine));
+
+ el->el_infd = fileno(fin);
+ el->el_outfile = fout;
+ el->el_errfile = ferr;
+ el->el_prog = strdup(prog);
+
+ /*
+ * Initialize all the modules. Order is important!!!
+ */
+ el->el_flags = 0;
+
+ if (term_init(el) == -1) {
+ free(el->el_prog);
+ el_free(el);
+ return NULL;
+ }
+ (void) key_init(el);
+ (void) map_init(el);
+ if (tty_init(el) == -1)
+ el->el_flags |= NO_TTY;
+ (void) ch_init(el);
+ (void) search_init(el);
+ (void) hist_init(el);
+ (void) prompt_init(el);
+ (void) sig_init(el);
+ (void) read_init(el);
+
+ return (el);
+}
+
+
+/* el_end():
+ * Clean up.
+ */
+public void
+el_end(EditLine *el)
+{
+
+ if (el == NULL)
+ return;
+
+ el_reset(el);
+
+ term_end(el);
+ key_end(el);
+ map_end(el);
+ tty_end(el);
+ ch_end(el);
+ search_end(el);
+ hist_end(el);
+ prompt_end(el);
+ sig_end(el);
+
+ if (el->el_prog)
+ el_free((ptr_t) el->el_prog);
+ el_free((ptr_t) el);
+}
+
+
+/* el_reset():
+ * Reset the tty and the parser
+ */
+public void
+el_reset(EditLine *el)
+{
+
+ tty_cookedmode(el);
+ ch_reset(el); /* XXX: Do we want that? */
+}
+
+
+/* el_set():
+ * set the editline parameters
+ */
+public int
+el_set(EditLine *el, int op, ...)
+{
+ va_list va;
+ int rv = 0;
+
+ if (el == NULL)
+ return (-1);
+ va_start(va, op);
+
+ switch (op) {
+ case EL_PROMPT:
+ case EL_RPROMPT:
+ rv = prompt_set(el, va_arg(va, el_pfunc_t), op);
+ break;
+
+ case EL_TERMINAL:
+ rv = term_set(el, va_arg(va, char *));
+ break;
+
+ case EL_EDITOR:
+ rv = map_set_editor(el, va_arg(va, char *));
+ break;
+
+ case EL_SIGNAL:
+ if (va_arg(va, int))
+ el->el_flags |= HANDLE_SIGNALS;
+ else
+ el->el_flags &= ~HANDLE_SIGNALS;
+ break;
+
+ case EL_BIND:
+ case EL_TELLTC:
+ case EL_SETTC:
+ case EL_ECHOTC:
+ case EL_SETTY:
+ {
+ const char *argv[20];
+ int i;
+
+ for (i = 1; i < 20; i++)
+ if ((argv[i] = va_arg(va, char *)) == NULL)
+ break;
+
+ switch (op) {
+ case EL_BIND:
+ argv[0] = "bind";
+ rv = map_bind(el, i, argv);
+ break;
+
+ case EL_TELLTC:
+ argv[0] = "telltc";
+ rv = term_telltc(el, i, argv);
+ break;
+
+ case EL_SETTC:
+ argv[0] = "settc";
+ rv = term_settc(el, i, argv);
+ break;
+
+ case EL_ECHOTC:
+ argv[0] = "echotc";
+ rv = term_echotc(el, i, argv);
+ break;
+
+ case EL_SETTY:
+ argv[0] = "setty";
+ rv = tty_stty(el, i, argv);
+ break;
+
+ default:
+ rv = -1;
+ EL_ABORT((el->el_errfile, "Bad op %d\n", op));
+ break;
+ }
+ break;
+ }
+
+ case EL_ADDFN:
+ {
+ char *name = va_arg(va, char *);
+ char *help = va_arg(va, char *);
+ el_func_t func = va_arg(va, el_func_t);
+
+ rv = map_addfunc(el, name, help, func);
+ break;
+ }
+
+ case EL_HIST:
+ {
+ hist_fun_t func = va_arg(va, hist_fun_t);
+ ptr_t ptr = va_arg(va, char *);
+
+ rv = hist_set(el, func, ptr);
+ break;
+ }
+
+ case EL_EDITMODE:
+ if (va_arg(va, int))
+ el->el_flags &= ~EDIT_DISABLED;
+ else
+ el->el_flags |= EDIT_DISABLED;
+ rv = 0;
+ break;
+
+ case EL_GETCFN:
+ {
+ el_rfunc_t rc = va_arg(va, el_rfunc_t);
+ rv = el_read_setfn(el, rc);
+ break;
+ }
+
+ case EL_CLIENTDATA:
+ el->el_data = va_arg(va, void *);
+ break;
+
+ default:
+ rv = -1;
+ break;
+ }
+
+ va_end(va);
+ return (rv);
+}
+
+
+/* el_get():
+ * retrieve the editline parameters
+ */
+public int
+el_get(EditLine *el, int op, void *ret)
+{
+ int rv;
+
+ if (el == NULL || ret == NULL)
+ return (-1);
+ switch (op) {
+ case EL_PROMPT:
+ case EL_RPROMPT:
+ rv = prompt_get(el, (el_pfunc_t *) & ret, op);
+ break;
+
+ case EL_EDITOR:
+ rv = map_get_editor(el, (const char **) &ret);
+ break;
+
+ case EL_SIGNAL:
+ *((int *) ret) = (el->el_flags & HANDLE_SIGNALS);
+ rv = 0;
+ break;
+
+ case EL_EDITMODE:
+ *((int *) ret) = (!(el->el_flags & EDIT_DISABLED));
+ rv = 0;
+ break;
+
+#if 0 /* XXX */
+ case EL_TERMINAL:
+ rv = term_get(el, (const char *) &ret);
+ break;
+
+ case EL_BIND:
+ case EL_TELLTC:
+ case EL_SETTC:
+ case EL_ECHOTC:
+ case EL_SETTY:
+ {
+ char *argv[20];
+ int i;
+
+ for (i = 1; i < 20; i++)
+ if ((argv[i] = va_arg(va, char *)) == NULL)
+ break;
+
+ switch (op) {
+ case EL_BIND:
+ argv[0] = "bind";
+ rv = map_bind(el, i, argv);
+ break;
+
+ case EL_TELLTC:
+ argv[0] = "telltc";
+ rv = term_telltc(el, i, argv);
+ break;
+
+ case EL_SETTC:
+ argv[0] = "settc";
+ rv = term_settc(el, i, argv);
+ break;
+
+ case EL_ECHOTC:
+ argv[0] = "echotc";
+ rv = term_echotc(el, i, argv);
+ break;
+
+ case EL_SETTY:
+ argv[0] = "setty";
+ rv = tty_stty(el, i, argv);
+ break;
+
+ default:
+ rv = -1;
+ EL_ABORT((el->errfile, "Bad op %d\n", op));
+ break;
+ }
+ break;
+ }
+
+ case EL_ADDFN:
+ {
+ char *name = va_arg(va, char *);
+ char *help = va_arg(va, char *);
+ el_func_t func = va_arg(va, el_func_t);
+
+ rv = map_addfunc(el, name, help, func);
+ break;
+ }
+
+ case EL_HIST:
+ {
+ hist_fun_t func = va_arg(va, hist_fun_t);
+ ptr_t ptr = va_arg(va, char *);
+ rv = hist_set(el, func, ptr);
+ }
+ break;
+#endif /* XXX */
+
+ case EL_GETCFN:
+ *((el_rfunc_t *)ret) = el_read_getfn(el);
+ rv = 0;
+ break;
+
+ case EL_CLIENTDATA:
+ *((void **)ret) = el->el_data;
+ rv = 0;
+ break;
+
+ default:
+ rv = -1;
+ }
+
+ return (rv);
+}
+
+
+/* el_line():
+ * Return editing info
+ */
+public const LineInfo *
+el_line(EditLine *el)
+{
+
+ return (const LineInfo *) (void *) &el->el_line;
+}
+
+
+/* el_source():
+ * Source a file
+ */
+public int
+el_source(EditLine *el, const char *fname)
+{
+ FILE *fp;
+ size_t len;
+ char *ptr;
+
+ fp = NULL;
+ if (fname == NULL) {
+#ifdef HAVE_ISSETUGID
+ static const char elpath[] = "/.editrc";
+ char path[MAXPATHLEN];
+
+ if (issetugid())
+ return (-1);
+ if ((ptr = getenv("HOME")) == NULL)
+ return (-1);
+ if (strlcpy(path, ptr, sizeof(path)) >= sizeof(path))
+ return (-1);
+ if (strlcat(path, elpath, sizeof(path)) >= sizeof(path))
+ return (-1);
+ fname = path;
+#else
+ /*
+ * If issetugid() is missing, always return an error, in order
+ * to keep from inadvertently opening up the user to a security
+ * hole.
+ */
+ return (-1);
+#endif
+ }
+ if (fp == NULL)
+ fp = fopen(fname, "r");
+ if (fp == NULL)
+ return (-1);
+
+ while ((ptr = fgetln(fp, &len)) != NULL) {
+ if (len > 0 && ptr[len - 1] == '\n')
+ --len;
+ ptr[len] = '\0';
+ if (parse_line(el, ptr) == -1) {
+ (void) fclose(fp);
+ return (-1);
+ }
+ }
+
+ (void) fclose(fp);
+ return (0);
+}
+
+
+/* el_resize():
+ * Called from program when terminal is resized
+ */
+public void
+el_resize(EditLine *el)
+{
+ int lins, cols;
+ sigset_t oset, nset;
+
+ (void) sigemptyset(&nset);
+ (void) sigaddset(&nset, SIGWINCH);
+ (void) sigprocmask(SIG_BLOCK, &nset, &oset);
+
+ /* get the correct window size */
+ if (term_get_size(el, &lins, &cols))
+ term_change_size(el, lins, cols);
+
+ (void) sigprocmask(SIG_SETMASK, &oset, NULL);
+}
+
+
+/* el_beep():
+ * Called from the program to beep
+ */
+public void
+el_beep(EditLine *el)
+{
+
+ term_beep(el);
+}
+
+
+/* el_editmode()
+ * Set the state of EDIT_DISABLED from the `edit' command.
+ */
+protected int
+/*ARGSUSED*/
+el_editmode(EditLine *el, int argc, const char **argv)
+{
+ const char *how;
+
+ if (argv == NULL || argc != 2 || argv[1] == NULL)
+ return (-1);
+
+ how = argv[1];
+ if (strcmp(how, "on") == 0)
+ el->el_flags &= ~EDIT_DISABLED;
+ else if (strcmp(how, "off") == 0)
+ el->el_flags |= EDIT_DISABLED;
+ else {
+ (void) fprintf(el->el_errfile, "edit: Bad value `%s'.\n", how);
+ return (-1);
+ }
+ return (0);
+}
diff --git a/trunk/main/editline/el.h b/trunk/main/editline/el.h
new file mode 100644
index 000000000..641081e87
--- /dev/null
+++ b/trunk/main/editline/el.h
@@ -0,0 +1,145 @@
+/* $NetBSD: el.h,v 1.11 2002/03/18 16:00:52 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)el.h 8.1 (Berkeley) 6/4/93
+ */
+
+/*
+ * el.h: Internal structures.
+ */
+#ifndef _h_el
+#define _h_el
+/*
+ * Local defaults
+ */
+#define KSHVI
+#define VIDEFAULT
+#define ANCHOR
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#define EL_BUFSIZ 1024 /* Maximum line size */
+
+#define HANDLE_SIGNALS 1<<0
+#define NO_TTY 1<<1
+#define EDIT_DISABLED 1<<2
+
+typedef int bool_t; /* True or not */
+
+typedef unsigned char el_action_t; /* Index to command array */
+
+typedef struct coord_t { /* Position on the screen */
+ int h;
+ int v;
+} coord_t;
+
+typedef struct el_line_t {
+ char *buffer; /* Input line */
+ char *cursor; /* Cursor position */
+ char *lastchar; /* Last character */
+ const char *limit; /* Max position */
+} el_line_t;
+
+/*
+ * Editor state
+ */
+typedef struct el_state_t {
+ int inputmode; /* What mode are we in? */
+ int doingarg; /* Are we getting an argument? */
+ int argument; /* Numeric argument */
+ int metanext; /* Is the next char a meta char */
+ el_action_t lastcmd; /* Previous command */
+} el_state_t;
+
+/*
+ * Until we come up with something better...
+ */
+#define el_malloc(a) malloc(a)
+#define el_realloc(a,b) realloc(a, b)
+#define el_free(a) free(a)
+
+#include "tty.h"
+#include "prompt.h"
+#include "key.h"
+#include "term.h"
+#include "refresh.h"
+#include "chared.h"
+#include "common.h"
+#include "search.h"
+#include "hist.h"
+#include "map.h"
+#include "parse.h"
+#include "sig.h"
+#include "help.h"
+#include "read.h"
+
+struct editline {
+ char *el_prog; /* the program name */
+ FILE *el_outfile; /* Stdio stuff */
+ FILE *el_errfile; /* Stdio stuff */
+ int el_infd; /* Input file descriptor */
+ int el_flags; /* Various flags. */
+ coord_t el_cursor; /* Cursor location */
+ char **el_display; /* Real screen image = what is there */
+ char **el_vdisplay; /* Virtual screen image = what we see */
+ void *el_data; /* Client data */
+ el_line_t el_line; /* The current line information */
+ el_state_t el_state; /* Current editor state */
+ el_term_t el_term; /* Terminal dependent stuff */
+ el_tty_t el_tty; /* Tty dependent stuff */
+ el_refresh_t el_refresh; /* Refresh stuff */
+ el_prompt_t el_prompt; /* Prompt stuff */
+ el_prompt_t el_rprompt; /* Prompt stuff */
+ el_chared_t el_chared; /* Characted editor stuff */
+ el_map_t el_map; /* Key mapping stuff */
+ el_key_t el_key; /* Key binding stuff */
+ el_history_t el_history; /* History stuff */
+ el_search_t el_search; /* Search stuff */
+ el_signal_t el_signal; /* Signal handling stuff */
+ el_read_t el_read; /* Character reading stuff */
+};
+
+protected int el_editmode(EditLine *, int, const char **);
+
+#ifdef DEBUG
+#define EL_ABORT(a) (void) (fprintf(el->el_errfile, "%s, %d: ", \
+ __FILE__, __LINE__), fprintf a, abort())
+#else
+#define EL_ABORT(a) abort()
+#endif
+#endif /* _h_el */
diff --git a/trunk/main/editline/emacs.c b/trunk/main/editline/emacs.c
new file mode 100644
index 000000000..f520d024b
--- /dev/null
+++ b/trunk/main/editline/emacs.c
@@ -0,0 +1,488 @@
+/* $NetBSD: emacs.c,v 1.10 2002/03/18 16:00:52 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+#if !defined(lint) && !defined(SCCSID)
+#if 0
+static char sccsid[] = "@(#)emacs.c 8.1 (Berkeley) 6/4/93";
+#else
+__RCSID("$NetBSD: emacs.c,v 1.10 2002/03/18 16:00:52 christos Exp $");
+#endif
+#endif /* not lint && not SCCSID */
+
+/*
+ * emacs.c: Emacs functions
+ */
+#include "el.h"
+
+/* em_delete_or_list():
+ * Delete character under cursor or list completions if at end of line
+ * [^D]
+ */
+protected el_action_t
+/*ARGSUSED*/
+em_delete_or_list(EditLine *el, int c)
+{
+
+ if (el->el_line.cursor == el->el_line.lastchar) {
+ /* if I'm at the end */
+ if (el->el_line.cursor == el->el_line.buffer) {
+ /* and the beginning */
+ term_overwrite(el, STReof, 4); /* then do a EOF */
+ term__flush();
+ return (CC_EOF);
+ } else {
+ /*
+ * Here we could list completions, but it is an
+ * error right now
+ */
+ term_beep(el);
+ return (CC_ERROR);
+ }
+ } else {
+ c_delafter(el, el->el_state.argument); /* delete after dot */
+ if (el->el_line.cursor > el->el_line.lastchar)
+ el->el_line.cursor = el->el_line.lastchar;
+ /* bounds check */
+ return (CC_REFRESH);
+ }
+}
+
+
+/* em_delete_next_word():
+ * Cut from cursor to end of current word
+ * [M-d]
+ */
+protected el_action_t
+/*ARGSUSED*/
+em_delete_next_word(EditLine *el, int c)
+{
+ char *cp, *p, *kp;
+
+ if (el->el_line.cursor == el->el_line.lastchar)
+ return (CC_ERROR);
+
+ cp = c__next_word(el->el_line.cursor, el->el_line.lastchar,
+ el->el_state.argument, ce__isword);
+
+ for (p = el->el_line.cursor, kp = el->el_chared.c_kill.buf; p < cp; p++)
+ /* save the text */
+ *kp++ = *p;
+ el->el_chared.c_kill.last = kp;
+
+ c_delafter(el, cp - el->el_line.cursor); /* delete after dot */
+ if (el->el_line.cursor > el->el_line.lastchar)
+ el->el_line.cursor = el->el_line.lastchar;
+ /* bounds check */
+ return (CC_REFRESH);
+}
+
+
+/* em_yank():
+ * Paste cut buffer at cursor position
+ * [^Y]
+ */
+protected el_action_t
+/*ARGSUSED*/
+em_yank(EditLine *el, int c)
+{
+ char *kp, *cp;
+
+ if (el->el_chared.c_kill.last == el->el_chared.c_kill.buf) {
+ if (!ch_enlargebufs(el, 1))
+ return (CC_ERROR);
+ }
+
+ if (el->el_line.lastchar +
+ (el->el_chared.c_kill.last - el->el_chared.c_kill.buf) >=
+ el->el_line.limit)
+ return (CC_ERROR);
+
+ el->el_chared.c_kill.mark = el->el_line.cursor;
+ cp = el->el_line.cursor;
+
+ /* open the space, */
+ c_insert(el, el->el_chared.c_kill.last - el->el_chared.c_kill.buf);
+ /* copy the chars */
+ for (kp = el->el_chared.c_kill.buf; kp < el->el_chared.c_kill.last; kp++)
+ *cp++ = *kp;
+
+ /* if an arg, cursor at beginning else cursor at end */
+ if (el->el_state.argument == 1)
+ el->el_line.cursor = cp;
+
+ return (CC_REFRESH);
+}
+
+
+/* em_kill_line():
+ * Cut the entire line and save in cut buffer
+ * [^U]
+ */
+protected el_action_t
+/*ARGSUSED*/
+em_kill_line(EditLine *el, int c)
+{
+ char *kp, *cp;
+
+ cp = el->el_line.buffer;
+ kp = el->el_chared.c_kill.buf;
+ while (cp < el->el_line.lastchar)
+ *kp++ = *cp++; /* copy it */
+ el->el_chared.c_kill.last = kp;
+ /* zap! -- delete all of it */
+ el->el_line.lastchar = el->el_line.buffer;
+ el->el_line.cursor = el->el_line.buffer;
+ return (CC_REFRESH);
+}
+
+
+/* em_kill_region():
+ * Cut area between mark and cursor and save in cut buffer
+ * [^W]
+ */
+protected el_action_t
+/*ARGSUSED*/
+em_kill_region(EditLine *el, int c)
+{
+ char *kp, *cp;
+
+ if (!el->el_chared.c_kill.mark)
+ return (CC_ERROR);
+
+ if (el->el_chared.c_kill.mark > el->el_line.cursor) {
+ cp = el->el_line.cursor;
+ kp = el->el_chared.c_kill.buf;
+ while (cp < el->el_chared.c_kill.mark)
+ *kp++ = *cp++; /* copy it */
+ el->el_chared.c_kill.last = kp;
+ c_delafter(el, cp - el->el_line.cursor);
+ } else { /* mark is before cursor */
+ cp = el->el_chared.c_kill.mark;
+ kp = el->el_chared.c_kill.buf;
+ while (cp < el->el_line.cursor)
+ *kp++ = *cp++; /* copy it */
+ el->el_chared.c_kill.last = kp;
+ c_delbefore(el, cp - el->el_chared.c_kill.mark);
+ el->el_line.cursor = el->el_chared.c_kill.mark;
+ }
+ return (CC_REFRESH);
+}
+
+
+/* em_copy_region():
+ * Copy area between mark and cursor to cut buffer
+ * [M-W]
+ */
+protected el_action_t
+/*ARGSUSED*/
+em_copy_region(EditLine *el, int c)
+{
+ char *kp, *cp;
+
+ if (el->el_chared.c_kill.mark)
+ return (CC_ERROR);
+
+ if (el->el_chared.c_kill.mark > el->el_line.cursor) {
+ cp = el->el_line.cursor;
+ kp = el->el_chared.c_kill.buf;
+ while (cp < el->el_chared.c_kill.mark)
+ *kp++ = *cp++; /* copy it */
+ el->el_chared.c_kill.last = kp;
+ } else {
+ cp = el->el_chared.c_kill.mark;
+ kp = el->el_chared.c_kill.buf;
+ while (cp < el->el_line.cursor)
+ *kp++ = *cp++; /* copy it */
+ el->el_chared.c_kill.last = kp;
+ }
+ return (CC_NORM);
+}
+
+
+/* em_gosmacs_traspose():
+ * Exchange the two characters before the cursor
+ * Gosling emacs transpose chars [^T]
+ */
+protected el_action_t
+em_gosmacs_traspose(EditLine *el, int c)
+{
+
+ if (el->el_line.cursor > &el->el_line.buffer[1]) {
+ /* must have at least two chars entered */
+ c = el->el_line.cursor[-2];
+ el->el_line.cursor[-2] = el->el_line.cursor[-1];
+ el->el_line.cursor[-1] = c;
+ return (CC_REFRESH);
+ } else
+ return (CC_ERROR);
+}
+
+
+/* em_next_word():
+ * Move next to end of current word
+ * [M-f]
+ */
+protected el_action_t
+/*ARGSUSED*/
+em_next_word(EditLine *el, int c)
+{
+ if (el->el_line.cursor == el->el_line.lastchar)
+ return (CC_ERROR);
+
+ el->el_line.cursor = c__next_word(el->el_line.cursor,
+ el->el_line.lastchar,
+ el->el_state.argument,
+ ce__isword);
+
+ if (el->el_map.type == MAP_VI)
+ if (el->el_chared.c_vcmd.action & DELETE) {
+ cv_delfini(el);
+ return (CC_REFRESH);
+ }
+ return (CC_CURSOR);
+}
+
+
+/* em_upper_case():
+ * Uppercase the characters from cursor to end of current word
+ * [M-u]
+ */
+protected el_action_t
+/*ARGSUSED*/
+em_upper_case(EditLine *el, int c)
+{
+ char *cp, *ep;
+
+ ep = c__next_word(el->el_line.cursor, el->el_line.lastchar,
+ el->el_state.argument, ce__isword);
+
+ for (cp = el->el_line.cursor; cp < ep; cp++)
+ if (islower((unsigned char) *cp))
+ *cp = toupper(*cp);
+
+ el->el_line.cursor = ep;
+ if (el->el_line.cursor > el->el_line.lastchar)
+ el->el_line.cursor = el->el_line.lastchar;
+ return (CC_REFRESH);
+}
+
+
+/* em_capitol_case():
+ * Capitalize the characters from cursor to end of current word
+ * [M-c]
+ */
+protected el_action_t
+/*ARGSUSED*/
+em_capitol_case(EditLine *el, int c)
+{
+ char *cp, *ep;
+
+ ep = c__next_word(el->el_line.cursor, el->el_line.lastchar,
+ el->el_state.argument, ce__isword);
+
+ for (cp = el->el_line.cursor; cp < ep; cp++) {
+ if (isalpha((unsigned char) *cp)) {
+ if (islower((unsigned char) *cp))
+ *cp = toupper(*cp);
+ cp++;
+ break;
+ }
+ }
+ for (; cp < ep; cp++)
+ if (isupper((unsigned char) *cp))
+ *cp = tolower(*cp);
+
+ el->el_line.cursor = ep;
+ if (el->el_line.cursor > el->el_line.lastchar)
+ el->el_line.cursor = el->el_line.lastchar;
+ return (CC_REFRESH);
+}
+
+
+/* em_lower_case():
+ * Lowercase the characters from cursor to end of current word
+ * [M-l]
+ */
+protected el_action_t
+/*ARGSUSED*/
+em_lower_case(EditLine *el, int c)
+{
+ char *cp, *ep;
+
+ ep = c__next_word(el->el_line.cursor, el->el_line.lastchar,
+ el->el_state.argument, ce__isword);
+
+ for (cp = el->el_line.cursor; cp < ep; cp++)
+ if (isupper((unsigned char) *cp))
+ *cp = tolower(*cp);
+
+ el->el_line.cursor = ep;
+ if (el->el_line.cursor > el->el_line.lastchar)
+ el->el_line.cursor = el->el_line.lastchar;
+ return (CC_REFRESH);
+}
+
+
+/* em_set_mark():
+ * Set the mark at cursor
+ * [^@]
+ */
+protected el_action_t
+/*ARGSUSED*/
+em_set_mark(EditLine *el, int c)
+{
+
+ el->el_chared.c_kill.mark = el->el_line.cursor;
+ return (CC_NORM);
+}
+
+
+/* em_exchange_mark():
+ * Exchange the cursor and mark
+ * [^X^X]
+ */
+protected el_action_t
+/*ARGSUSED*/
+em_exchange_mark(EditLine *el, int c)
+{
+ char *cp;
+
+ cp = el->el_line.cursor;
+ el->el_line.cursor = el->el_chared.c_kill.mark;
+ el->el_chared.c_kill.mark = cp;
+ return (CC_CURSOR);
+}
+
+
+/* em_universal_argument():
+ * Universal argument (argument times 4)
+ * [^U]
+ */
+protected el_action_t
+/*ARGSUSED*/
+em_universal_argument(EditLine *el, int c)
+{ /* multiply current argument by 4 */
+
+ if (el->el_state.argument > 1000000)
+ return (CC_ERROR);
+ el->el_state.doingarg = 1;
+ el->el_state.argument *= 4;
+ return (CC_ARGHACK);
+}
+
+
+/* em_meta_next():
+ * Add 8th bit to next character typed
+ * [<ESC>]
+ */
+protected el_action_t
+/*ARGSUSED*/
+em_meta_next(EditLine *el, int c)
+{
+
+ el->el_state.metanext = 1;
+ return (CC_ARGHACK);
+}
+
+
+/* em_toggle_overwrite():
+ * Switch from insert to overwrite mode or vice versa
+ */
+protected el_action_t
+/*ARGSUSED*/
+em_toggle_overwrite(EditLine *el, int c)
+{
+
+ el->el_state.inputmode = (el->el_state.inputmode == MODE_INSERT) ?
+ MODE_REPLACE : MODE_INSERT;
+ return (CC_NORM);
+}
+
+
+/* em_copy_prev_word():
+ * Copy current word to cursor
+ */
+protected el_action_t
+/*ARGSUSED*/
+em_copy_prev_word(EditLine *el, int c)
+{
+ char *cp, *oldc, *dp;
+
+ if (el->el_line.cursor == el->el_line.buffer)
+ return (CC_ERROR);
+
+ oldc = el->el_line.cursor;
+ /* does a bounds check */
+ cp = c__prev_word(el->el_line.cursor, el->el_line.buffer,
+ el->el_state.argument, ce__isword);
+
+ c_insert(el, oldc - cp);
+ for (dp = oldc; cp < oldc && dp < el->el_line.lastchar; cp++)
+ *dp++ = *cp;
+
+ el->el_line.cursor = dp;/* put cursor at end */
+
+ return (CC_REFRESH);
+}
+
+
+/* em_inc_search_next():
+ * Emacs incremental next search
+ */
+protected el_action_t
+/*ARGSUSED*/
+em_inc_search_next(EditLine *el, int c)
+{
+
+ el->el_search.patlen = 0;
+ return (ce_inc_search(el, ED_SEARCH_NEXT_HISTORY));
+}
+
+
+/* em_inc_search_prev():
+ * Emacs incremental reverse search
+ */
+protected el_action_t
+/*ARGSUSED*/
+em_inc_search_prev(EditLine *el, int c)
+{
+
+ el->el_search.patlen = 0;
+ return (ce_inc_search(el, ED_SEARCH_PREV_HISTORY));
+}
diff --git a/trunk/main/editline/hist.c b/trunk/main/editline/hist.c
new file mode 100644
index 000000000..11f39ae10
--- /dev/null
+++ b/trunk/main/editline/hist.c
@@ -0,0 +1,197 @@
+/* $NetBSD: hist.c,v 1.10 2002/03/18 16:00:53 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+#if !defined(lint) && !defined(SCCSID)
+#if 0
+static char sccsid[] = "@(#)hist.c 8.1 (Berkeley) 6/4/93";
+#else
+__RCSID("$NetBSD: hist.c,v 1.10 2002/03/18 16:00:53 christos Exp $");
+#endif
+#endif /* not lint && not SCCSID */
+
+/*
+ * hist.c: History access functions
+ */
+#include <stdlib.h>
+#include "el.h"
+
+/* hist_init():
+ * Initialization function.
+ */
+protected int
+hist_init(EditLine *el)
+{
+
+ el->el_history.fun = NULL;
+ el->el_history.ref = NULL;
+ el->el_history.buf = (char *) el_malloc(EL_BUFSIZ);
+ el->el_history.sz = EL_BUFSIZ;
+ if (el->el_history.buf == NULL)
+ return (-1);
+ el->el_history.last = el->el_history.buf;
+ return (0);
+}
+
+
+/* hist_end():
+ * clean up history;
+ */
+protected void
+hist_end(EditLine *el)
+{
+
+ el_free((ptr_t) el->el_history.buf);
+ el->el_history.buf = NULL;
+}
+
+
+/* hist_set():
+ * Set new history interface
+ */
+protected int
+hist_set(EditLine *el, hist_fun_t fun, ptr_t ptr)
+{
+
+ el->el_history.ref = ptr;
+ el->el_history.fun = fun;
+ return (0);
+}
+
+
+/* hist_get():
+ * Get a history line and update it in the buffer.
+ * eventno tells us the event to get.
+ */
+protected el_action_t
+hist_get(EditLine *el)
+{
+ const char *hp;
+ int h;
+
+ if (el->el_history.eventno == 0) { /* if really the current line */
+ (void) strncpy(el->el_line.buffer, el->el_history.buf,
+ el->el_history.sz - 1);
+ el->el_line.lastchar = el->el_line.buffer +
+ (el->el_history.last - el->el_history.buf);
+
+#ifdef KSHVI
+ if (el->el_map.type == MAP_VI)
+ el->el_line.cursor = el->el_line.buffer;
+ else
+#endif /* KSHVI */
+ el->el_line.cursor = el->el_line.lastchar;
+
+ return (CC_REFRESH);
+ }
+ if (el->el_history.ref == NULL)
+ return (CC_ERROR);
+
+ hp = HIST_FIRST(el);
+
+ if (hp == NULL)
+ return (CC_ERROR);
+
+ for (h = 1; h < el->el_history.eventno; h++)
+ if ((hp = HIST_NEXT(el)) == NULL) {
+ el->el_history.eventno = h;
+ return (CC_ERROR);
+ }
+ (void) strncpy(el->el_line.buffer, hp,
+ (size_t)(el->el_line.limit - el->el_line.buffer));
+ el->el_line.lastchar = el->el_line.buffer + strlen(el->el_line.buffer);
+
+ if (el->el_line.lastchar > el->el_line.buffer) {
+ if (el->el_line.lastchar[-1] == '\n')
+ el->el_line.lastchar--;
+ if ((el->el_line.lastchar > el->el_line.buffer)&&(el->el_line.lastchar[-1] == ' ')) /* bill heckel */
+ el->el_line.lastchar--;
+ if (el->el_line.lastchar < el->el_line.buffer)
+ el->el_line.lastchar = el->el_line.buffer;
+ }
+#ifdef KSHVI
+ if (el->el_map.type == MAP_VI)
+ el->el_line.cursor = el->el_line.buffer;
+ else
+#endif /* KSHVI */
+ el->el_line.cursor = el->el_line.lastchar;
+
+ return (CC_REFRESH);
+}
+
+
+/* hist_list()
+ * List history entries
+ */
+protected int
+/*ARGSUSED*/
+hist_list(EditLine *el, int argc, const char **argv)
+{
+ const char *str;
+
+ if (el->el_history.ref == NULL)
+ return (-1);
+ for (str = HIST_LAST(el); str != NULL; str = HIST_PREV(el))
+ (void) fprintf(el->el_outfile, "%d %s",
+ el->el_history.ev.num, str);
+ return (0);
+}
+
+/* hist_enlargebuf()
+ * Enlarge history buffer to specified value. Called from el_enlargebufs().
+ * Return 0 for failure, 1 for success.
+ */
+protected int
+/*ARGSUSED*/
+hist_enlargebuf(EditLine *el, size_t oldsz, size_t newsz)
+{
+ char *newbuf;
+
+ newbuf = realloc(el->el_history.buf, newsz);
+ if (!newbuf)
+ return 0;
+
+ (void) memset(&newbuf[oldsz], '\0', newsz - oldsz);
+
+ el->el_history.last = newbuf +
+ (el->el_history.last - el->el_history.buf);
+ el->el_history.buf = newbuf;
+ el->el_history.sz = newsz;
+
+ return 1;
+}
diff --git a/trunk/main/editline/hist.h b/trunk/main/editline/hist.h
new file mode 100644
index 000000000..5fdccd08e
--- /dev/null
+++ b/trunk/main/editline/hist.h
@@ -0,0 +1,80 @@
+/* $NetBSD: hist.h,v 1.7 2002/03/18 16:00:53 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)hist.h 8.1 (Berkeley) 6/4/93
+ */
+
+/*
+ * el.hist.c: History functions
+ */
+#ifndef _h_el_hist
+#define _h_el_hist
+
+#include "histedit.h"
+
+typedef int (*hist_fun_t)(ptr_t, HistEvent *, int, ...);
+
+typedef struct el_history_t {
+ char *buf; /* The history buffer */
+ size_t sz; /* Size of history buffer */
+ char *last; /* The last character */
+ int eventno; /* Event we are looking for */
+ ptr_t ref; /* Argument for history fcns */
+ hist_fun_t fun; /* Event access */
+ HistEvent ev; /* Event cookie */
+} el_history_t;
+
+#define HIST_FUN(el, fn, arg) \
+ ((((*(el)->el_history.fun) ((el)->el_history.ref, &(el)->el_history.ev, \
+ fn, arg)) == -1) ? NULL : (el)->el_history.ev.str)
+
+#define HIST_NEXT(el) HIST_FUN(el, H_NEXT, NULL)
+#define HIST_FIRST(el) HIST_FUN(el, H_FIRST, NULL)
+#define HIST_LAST(el) HIST_FUN(el, H_LAST, NULL)
+#define HIST_PREV(el) HIST_FUN(el, H_PREV, NULL)
+#define HIST_EVENT(el, num) HIST_FUN(el, H_EVENT, num)
+#define HIST_LOAD(el, fname) HIST_FUN(el, H_LOAD fname)
+#define HIST_SAVE(el, fname) HIST_FUN(el, H_SAVE fname)
+
+protected int hist_init(EditLine *);
+protected void hist_end(EditLine *);
+protected el_action_t hist_get(EditLine *);
+protected int hist_set(EditLine *, hist_fun_t, ptr_t);
+protected int hist_list(EditLine *, int, const char **);
+protected int hist_enlargebuf(EditLine *, size_t, size_t);
+
+#endif /* _h_el_hist */
diff --git a/trunk/main/editline/histedit.h b/trunk/main/editline/histedit.h
new file mode 100644
index 000000000..e387e3b81
--- /dev/null
+++ b/trunk/main/editline/histedit.h
@@ -0,0 +1,197 @@
+/* $NetBSD: histedit.h,v 1.19 2002/03/18 16:00:54 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)histedit.h 8.2 (Berkeley) 1/3/94
+ */
+
+/*
+ * histedit.h: Line editor and history interface.
+ */
+#ifndef _HISTEDIT_H_
+#define _HISTEDIT_H_
+
+#define LIBEDIT_MAJOR 2
+#define LIBEDIT_MINOR 6
+
+#include <sys/types.h>
+#include <stdio.h>
+
+/*
+ * ==== Editing ====
+ */
+typedef struct editline EditLine;
+
+/*
+ * For user-defined function interface
+ */
+typedef struct lineinfo {
+ char *buffer;
+ char *cursor;
+ char *lastchar;
+} LineInfo;
+
+
+/*
+ * EditLine editor function return codes.
+ * For user-defined function interface
+ */
+#define CC_NORM 0
+#define CC_NEWLINE 1
+#define CC_EOF 2
+#define CC_ARGHACK 3
+#define CC_REFRESH 4
+#define CC_CURSOR 5
+#define CC_ERROR 6
+#define CC_FATAL 7
+#define CC_REDISPLAY 8
+#define CC_REFRESH_BEEP 9
+
+/*
+ * Initialization, cleanup, and resetting
+ */
+EditLine *el_init(const char *, FILE *, FILE *, FILE *);
+void el_reset(EditLine *);
+void el_end(EditLine *);
+
+
+/*
+ * Get a line, a character or push a string back in the input queue
+ */
+const char *el_gets(EditLine *, int *);
+int el_getc(EditLine *, char *);
+void el_push(EditLine *, char *);
+
+/*
+ * Beep!
+ */
+void el_beep(EditLine *);
+
+/*
+ * High level function internals control
+ * Parses argc, argv array and executes builtin editline commands
+ */
+int el_parse(EditLine *, int, const char **);
+
+/*
+ * Low level editline access functions
+ */
+int el_set(EditLine *, int, ...);
+int el_get(EditLine *, int, void *);
+
+/*
+ * el_set/el_get parameters
+ */
+#define EL_PROMPT 0 /* , el_pfunc_t); */
+#define EL_TERMINAL 1 /* , const char *); */
+#define EL_EDITOR 2 /* , const char *); */
+#define EL_SIGNAL 3 /* , int); */
+#define EL_BIND 4 /* , const char *, ..., NULL); */
+#define EL_TELLTC 5 /* , const char *, ..., NULL); */
+#define EL_SETTC 6 /* , const char *, ..., NULL); */
+#define EL_ECHOTC 7 /* , const char *, ..., NULL); */
+#define EL_SETTY 8 /* , const char *, ..., NULL); */
+#define EL_ADDFN 9 /* , const char *, const char * */
+ /* , el_func_t); */
+#define EL_HIST 10 /* , hist_fun_t, const char *); */
+#define EL_EDITMODE 11 /* , int); */
+#define EL_RPROMPT 12 /* , el_pfunc_t); */
+#define EL_GETCFN 13 /* , el_rfunc_t); */
+#define EL_CLIENTDATA 14 /* , void *); */
+
+#define EL_BUILTIN_GETCFN (NULL)
+
+/*
+ * Source named file or $PWD/.editrc or $HOME/.editrc
+ */
+int el_source(EditLine *, const char *);
+
+/*
+ * Must be called when the terminal changes size; If EL_SIGNAL
+ * is set this is done automatically otherwise it is the responsibility
+ * of the application
+ */
+void el_resize(EditLine *);
+
+
+/*
+ * User-defined function interface.
+ */
+const LineInfo *el_line(EditLine *);
+int el_insertstr(EditLine *, const char *);
+void el_deletestr(EditLine *, int);
+
+/*
+ * ==== History ====
+ */
+
+typedef struct history History;
+
+typedef struct HistEvent {
+ int num;
+ const char *str;
+} HistEvent;
+
+/*
+ * History access functions.
+ */
+History * history_init(void);
+void history_end(History *);
+
+int history(History *, HistEvent *, int, ...);
+
+#define H_FUNC 0 /* , UTSL */
+#define H_SETSIZE 1 /* , const int); */
+#define H_GETSIZE 2 /* , void); */
+#define H_FIRST 3 /* , void); */
+#define H_LAST 4 /* , void); */
+#define H_PREV 5 /* , void); */
+#define H_NEXT 6 /* , void); */
+#define H_CURR 8 /* , const int); */
+#define H_SET 7 /* , void); */
+#define H_ADD 9 /* , const char *); */
+#define H_ENTER 10 /* , const char *); */
+#define H_APPEND 11 /* , const char *); */
+#define H_END 12 /* , void); */
+#define H_NEXT_STR 13 /* , const char *); */
+#define H_PREV_STR 14 /* , const char *); */
+#define H_NEXT_EVENT 15 /* , const int); */
+#define H_PREV_EVENT 16 /* , const int); */
+#define H_LOAD 17 /* , const char *); */
+#define H_SAVE 18 /* , const char *); */
+#define H_CLEAR 19 /* , void); */
+
+#endif /* _HISTEDIT_H_ */
diff --git a/trunk/main/editline/history.c b/trunk/main/editline/history.c
new file mode 100644
index 000000000..f133d2eb0
--- /dev/null
+++ b/trunk/main/editline/history.c
@@ -0,0 +1,875 @@
+/* $NetBSD: history.c,v 1.19 2002/03/18 16:00:54 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+#if !defined(lint) && !defined(SCCSID)
+#if 0
+static char sccsid[] = "@(#)history.c 8.1 (Berkeley) 6/4/93";
+#else
+__RCSID("$NetBSD: history.c,v 1.19 2002/03/18 16:00:54 christos Exp $");
+#endif
+#endif /* not lint && not SCCSID */
+
+/*
+ * hist.c: History access functions
+ */
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#ifdef HAVE_VIS_H
+#include <vis.h>
+#else
+#include "np/vis.h"
+#endif
+#include <sys/stat.h>
+
+static const char hist_cookie[] = "_HiStOrY_V2_\n";
+
+#include "histedit.h"
+
+typedef int (*history_gfun_t)(ptr_t, HistEvent *);
+typedef int (*history_efun_t)(ptr_t, HistEvent *, const char *);
+typedef void (*history_vfun_t)(ptr_t, HistEvent *);
+typedef int (*history_sfun_t)(ptr_t, HistEvent *, const int);
+
+struct history {
+ ptr_t h_ref; /* Argument for history fcns */
+ int h_ent; /* Last entry point for history */
+ history_gfun_t h_first; /* Get the first element */
+ history_gfun_t h_next; /* Get the next element */
+ history_gfun_t h_last; /* Get the last element */
+ history_gfun_t h_prev; /* Get the previous element */
+ history_gfun_t h_curr; /* Get the current element */
+ history_sfun_t h_set; /* Set the current element */
+ history_vfun_t h_clear; /* Clear the history list */
+ history_efun_t h_enter; /* Add an element */
+ history_efun_t h_add; /* Append to an element */
+};
+#define HNEXT(h, ev) (*(h)->h_next)((h)->h_ref, ev)
+#define HFIRST(h, ev) (*(h)->h_first)((h)->h_ref, ev)
+#define HPREV(h, ev) (*(h)->h_prev)((h)->h_ref, ev)
+#define HLAST(h, ev) (*(h)->h_last)((h)->h_ref, ev)
+#define HCURR(h, ev) (*(h)->h_curr)((h)->h_ref, ev)
+#define HSET(h, ev, n) (*(h)->h_set)((h)->h_ref, ev, n)
+#define HCLEAR(h, ev) (*(h)->h_clear)((h)->h_ref, ev)
+#define HENTER(h, ev, str) (*(h)->h_enter)((h)->h_ref, ev, str)
+#define HADD(h, ev, str) (*(h)->h_add)((h)->h_ref, ev, str)
+
+#define h_malloc(a) malloc(a)
+#define h_realloc(a, b) realloc((a), (b))
+#define h_free(a) free(a)
+
+typedef struct {
+ int num;
+ char *str;
+} HistEventPrivate;
+
+
+
+private int history_setsize(History *, HistEvent *, int);
+private int history_getsize(History *, HistEvent *);
+private int history_set_fun(History *, History *);
+private int history_load(History *, const char *);
+private int history_save(History *, const char *);
+private int history_prev_event(History *, HistEvent *, int);
+private int history_next_event(History *, HistEvent *, int);
+private int history_next_string(History *, HistEvent *, const char *);
+private int history_prev_string(History *, HistEvent *, const char *);
+
+
+/***********************************************************************/
+
+/*
+ * Builtin- history implementation
+ */
+typedef struct hentry_t {
+ HistEvent ev; /* What we return */
+ struct hentry_t *next; /* Next entry */
+ struct hentry_t *prev; /* Previous entry */
+} hentry_t;
+
+typedef struct history_t {
+ hentry_t list; /* Fake list header element */
+ hentry_t *cursor; /* Current element in the list */
+ int max; /* Maximum number of events */
+ int cur; /* Current number of events */
+ int eventid; /* For generation of unique event id */
+} history_t;
+
+private int history_def_first(ptr_t, HistEvent *);
+private int history_def_last(ptr_t, HistEvent *);
+private int history_def_next(ptr_t, HistEvent *);
+private int history_def_prev(ptr_t, HistEvent *);
+private int history_def_curr(ptr_t, HistEvent *);
+private int history_def_set(ptr_t, HistEvent *, const int n);
+private int history_def_enter(ptr_t, HistEvent *, const char *);
+private int history_def_add(ptr_t, HistEvent *, const char *);
+private void history_def_init(ptr_t *, HistEvent *, int);
+private void history_def_clear(ptr_t, HistEvent *);
+private int history_def_insert(history_t *, HistEvent *, const char *);
+private void history_def_delete(history_t *, HistEvent *, hentry_t *);
+
+#define history_def_setsize(p, num)(void) (((history_t *) p)->max = (num))
+#define history_def_getsize(p) (((history_t *) p)->cur)
+
+#define he_strerror(code) he_errlist[code]
+#define he_seterrev(evp, code) {\
+ evp->num = code;\
+ evp->str = he_strerror(code);\
+ }
+
+/* error messages */
+static const char *const he_errlist[] = {
+ "OK",
+ "unknown error",
+ "malloc() failed",
+ "first event not found",
+ "last event not found",
+ "empty list",
+ "no next event",
+ "no previous event",
+ "current event is invalid",
+ "event not found",
+ "can't read history from file",
+ "can't write history",
+ "required parameter(s) not supplied",
+ "history size negative",
+ "function not allowed with other history-functions-set the default",
+ "bad parameters"
+};
+/* error codes */
+#define _HE_OK 0
+#define _HE_UNKNOWN 1
+#define _HE_MALLOC_FAILED 2
+#define _HE_FIRST_NOTFOUND 3
+#define _HE_LAST_NOTFOUND 4
+#define _HE_EMPTY_LIST 5
+#define _HE_END_REACHED 6
+#define _HE_START_REACHED 7
+#define _HE_CURR_INVALID 8
+#define _HE_NOT_FOUND 9
+#define _HE_HIST_READ 10
+#define _HE_HIST_WRITE 11
+#define _HE_PARAM_MISSING 12
+#define _HE_SIZE_NEGATIVE 13
+#define _HE_NOT_ALLOWED 14
+#define _HE_BAD_PARAM 15
+
+/* history_def_first():
+ * Default function to return the first event in the history.
+ */
+private int
+history_def_first(ptr_t p, HistEvent *ev)
+{
+ history_t *h = (history_t *) p;
+
+ h->cursor = h->list.next;
+ if (h->cursor != &h->list)
+ *ev = h->cursor->ev;
+ else {
+ he_seterrev(ev, _HE_FIRST_NOTFOUND);
+ return (-1);
+ }
+
+ return (0);
+}
+
+
+/* history_def_last():
+ * Default function to return the last event in the history.
+ */
+private int
+history_def_last(ptr_t p, HistEvent *ev)
+{
+ history_t *h = (history_t *) p;
+
+ h->cursor = h->list.prev;
+ if (h->cursor != &h->list)
+ *ev = h->cursor->ev;
+ else {
+ he_seterrev(ev, _HE_LAST_NOTFOUND);
+ return (-1);
+ }
+
+ return (0);
+}
+
+
+/* history_def_next():
+ * Default function to return the next event in the history.
+ */
+private int
+history_def_next(ptr_t p, HistEvent *ev)
+{
+ history_t *h = (history_t *) p;
+
+ if (h->cursor != &h->list)
+ h->cursor = h->cursor->next;
+ else {
+ he_seterrev(ev, _HE_EMPTY_LIST);
+ return (-1);
+ }
+
+ if (h->cursor != &h->list)
+ *ev = h->cursor->ev;
+ else {
+ he_seterrev(ev, _HE_END_REACHED);
+ return (-1);
+ }
+
+ return (0);
+}
+
+
+/* history_def_prev():
+ * Default function to return the previous event in the history.
+ */
+private int
+history_def_prev(ptr_t p, HistEvent *ev)
+{
+ history_t *h = (history_t *) p;
+
+ if (h->cursor != &h->list)
+ h->cursor = h->cursor->prev;
+ else {
+ he_seterrev(ev,
+ (h->cur > 0) ? _HE_END_REACHED : _HE_EMPTY_LIST);
+ return (-1);
+ }
+
+ if (h->cursor != &h->list)
+ *ev = h->cursor->ev;
+ else {
+ he_seterrev(ev, _HE_START_REACHED);
+ return (-1);
+ }
+
+ return (0);
+}
+
+
+/* history_def_curr():
+ * Default function to return the current event in the history.
+ */
+private int
+history_def_curr(ptr_t p, HistEvent *ev)
+{
+ history_t *h = (history_t *) p;
+
+ if (h->cursor != &h->list)
+ *ev = h->cursor->ev;
+ else {
+ he_seterrev(ev,
+ (h->cur > 0) ? _HE_CURR_INVALID : _HE_EMPTY_LIST);
+ return (-1);
+ }
+
+ return (0);
+}
+
+
+/* history_def_set():
+ * Default function to set the current event in the history to the
+ * given one.
+ */
+private int
+history_def_set(ptr_t p, HistEvent *ev, const int n)
+{
+ history_t *h = (history_t *) p;
+
+ if (h->cur == 0) {
+ he_seterrev(ev, _HE_EMPTY_LIST);
+ return (-1);
+ }
+ if (h->cursor == &h->list || h->cursor->ev.num != n) {
+ for (h->cursor = h->list.next; h->cursor != &h->list;
+ h->cursor = h->cursor->next)
+ if (h->cursor->ev.num == n)
+ break;
+ }
+ if (h->cursor == &h->list) {
+ he_seterrev(ev, _HE_NOT_FOUND);
+ return (-1);
+ }
+ return (0);
+}
+
+
+/* history_def_add():
+ * Append string to element
+ */
+private int
+history_def_add(ptr_t p, HistEvent *ev, const char *str)
+{
+ history_t *h = (history_t *) p;
+ size_t len;
+ char *s;
+ HistEventPrivate *evp = (void *)&h->cursor->ev;
+
+ if (h->cursor == &h->list)
+ return (history_def_enter(p, ev, str));
+ len = strlen(evp->str) + strlen(str) + 1;
+ s = (char *) h_malloc(len);
+ if (!s) {
+ he_seterrev(ev, _HE_MALLOC_FAILED);
+ return (-1);
+ }
+ (void) strlcpy(s, h->cursor->ev.str, len);
+ (void) strlcat(s, str, len);
+ h_free(evp->str);
+ evp->str = s;
+ *ev = h->cursor->ev;
+ return (0);
+}
+
+
+/* history_def_delete():
+ * Delete element hp of the h list
+ */
+/* ARGSUSED */
+private void
+history_def_delete(history_t *h, HistEvent *ev, hentry_t *hp)
+{
+ HistEventPrivate *evp = (void *)&hp->ev;
+ if (hp == &h->list)
+ abort();
+ hp->prev->next = hp->next;
+ hp->next->prev = hp->prev;
+ h_free((ptr_t) evp->str);
+ h_free(hp);
+ h->cur--;
+}
+
+
+/* history_def_insert():
+ * Insert element with string str in the h list
+ */
+private int
+history_def_insert(history_t *h, HistEvent *ev, const char *str)
+{
+
+ h->cursor = (hentry_t *) h_malloc(sizeof(hentry_t));
+ if (h->cursor)
+ h->cursor->ev.str = strdup(str);
+ if (!h->cursor || !h->cursor->ev.str) {
+ he_seterrev(ev, _HE_MALLOC_FAILED);
+ return (-1);
+ }
+ h->cursor->ev.num = ++h->eventid;
+ h->cursor->next = h->list.next;
+ h->cursor->prev = &h->list;
+ h->list.next->prev = h->cursor;
+ h->list.next = h->cursor;
+ h->cur++;
+
+ *ev = h->cursor->ev;
+ return (0);
+}
+
+
+/* history_def_enter():
+ * Default function to enter an item in the history
+ */
+private int
+history_def_enter(ptr_t p, HistEvent *ev, const char *str)
+{
+ history_t *h = (history_t *) p;
+
+ if (history_def_insert(h, ev, str) == -1)
+ return (-1); /* error, keep error message */
+
+ /*
+ * Always keep at least one entry.
+ * This way we don't have to check for the empty list.
+ */
+ while (h->cur > h->max && h->cur > 0)
+ history_def_delete(h, ev, h->list.prev);
+
+ return (0);
+}
+
+
+/* history_def_init():
+ * Default history initialization function
+ */
+/* ARGSUSED */
+private void
+history_def_init(ptr_t *p, HistEvent *ev, int n)
+{
+ history_t *h = (history_t *) h_malloc(sizeof(history_t));
+
+ if (n <= 0)
+ n = 0;
+ h->eventid = 0;
+ h->cur = 0;
+ h->max = n;
+ h->list.next = h->list.prev = &h->list;
+ h->list.ev.str = NULL;
+ h->list.ev.num = 0;
+ h->cursor = &h->list;
+ *p = (ptr_t) h;
+}
+
+
+/* history_def_clear():
+ * Default history cleanup function
+ */
+private void
+history_def_clear(ptr_t p, HistEvent *ev)
+{
+ history_t *h = (history_t *) p;
+
+ while (h->list.prev != &h->list)
+ history_def_delete(h, ev, h->list.prev);
+ h->eventid = 0;
+ h->cur = 0;
+}
+
+
+
+
+/************************************************************************/
+
+/* history_init():
+ * Initialization function.
+ */
+public History *
+history_init(void)
+{
+ History *h = (History *) h_malloc(sizeof(History));
+ HistEvent ev;
+
+ history_def_init(&h->h_ref, &ev, 0);
+ h->h_ent = -1;
+ h->h_next = history_def_next;
+ h->h_first = history_def_first;
+ h->h_last = history_def_last;
+ h->h_prev = history_def_prev;
+ h->h_curr = history_def_curr;
+ h->h_set = history_def_set;
+ h->h_clear = history_def_clear;
+ h->h_enter = history_def_enter;
+ h->h_add = history_def_add;
+
+ return (h);
+}
+
+
+/* history_end():
+ * clean up history;
+ */
+public void
+history_end(History *h)
+{
+ HistEvent ev;
+
+ if (h->h_next == history_def_next)
+ history_def_clear(h->h_ref, &ev);
+}
+
+
+
+/* history_setsize():
+ * Set history number of events
+ */
+private int
+history_setsize(History *h, HistEvent *ev, int num)
+{
+
+ if (h->h_next != history_def_next) {
+ he_seterrev(ev, _HE_NOT_ALLOWED);
+ return (-1);
+ }
+ if (num < 0) {
+ he_seterrev(ev, _HE_BAD_PARAM);
+ return (-1);
+ }
+ history_def_setsize(h->h_ref, num);
+ return (0);
+}
+
+
+/* history_getsize():
+ * Get number of events currently in history
+ */
+private int
+history_getsize(History *h, HistEvent *ev)
+{
+ int retval = 0;
+
+ if (h->h_next != history_def_next) {
+ he_seterrev(ev, _HE_NOT_ALLOWED);
+ return (-1);
+ }
+ retval = history_def_getsize(h->h_ref);
+ if (retval < -1) {
+ he_seterrev(ev, _HE_SIZE_NEGATIVE);
+ return (-1);
+ }
+ ev->num = retval;
+ return (0);
+}
+
+
+/* history_set_fun():
+ * Set history functions
+ */
+private int
+history_set_fun(History *h, History *nh)
+{
+ HistEvent ev;
+
+ if (nh->h_first == NULL || nh->h_next == NULL || nh->h_last == NULL ||
+ nh->h_prev == NULL || nh->h_curr == NULL || nh->h_set == NULL ||
+ nh->h_enter == NULL || nh->h_add == NULL || nh->h_clear == NULL ||
+ nh->h_ref == NULL) {
+ if (h->h_next != history_def_next) {
+ history_def_init(&h->h_ref, &ev, 0);
+ h->h_first = history_def_first;
+ h->h_next = history_def_next;
+ h->h_last = history_def_last;
+ h->h_prev = history_def_prev;
+ h->h_curr = history_def_curr;
+ h->h_set = history_def_set;
+ h->h_clear = history_def_clear;
+ h->h_enter = history_def_enter;
+ h->h_add = history_def_add;
+ }
+ return (-1);
+ }
+ if (h->h_next == history_def_next)
+ history_def_clear(h->h_ref, &ev);
+
+ h->h_ent = -1;
+ h->h_first = nh->h_first;
+ h->h_next = nh->h_next;
+ h->h_last = nh->h_last;
+ h->h_prev = nh->h_prev;
+ h->h_curr = nh->h_curr;
+ h->h_set = nh->h_set;
+ h->h_clear = nh->h_clear;
+ h->h_enter = nh->h_enter;
+ h->h_add = nh->h_add;
+
+ return (0);
+}
+
+
+/* history_load():
+ * History load function
+ */
+private int
+history_load(History *h, const char *fname)
+{
+ FILE *fp;
+ char *line;
+ size_t sz, max_size;
+ char *ptr;
+ int i = -1;
+ HistEvent ev;
+
+ if ((fp = fopen(fname, "r")) == NULL)
+ return (i);
+
+ if ((line = fgetln(fp, &sz)) == NULL)
+ goto done;
+
+ if (strncmp(line, hist_cookie, sz) != 0)
+ goto done;
+
+ ptr = h_malloc(max_size = 1024);
+ for (i = 0; (line = fgetln(fp, &sz)) != NULL; i++) {
+ char c = line[sz];
+
+ if (sz != 0 && line[sz - 1] == '\n')
+ line[--sz] = '\0';
+ else
+ line[sz] = '\0';
+
+ if (max_size < sz) {
+ max_size = (sz + 1023) & ~1023;
+ ptr = h_realloc(ptr, max_size);
+ }
+ (void) strunvis(ptr, line);
+ line[sz] = c;
+ HENTER(h, &ev, ptr);
+ }
+ h_free(ptr);
+
+done:
+ (void) fclose(fp);
+ return (i);
+}
+
+
+/* history_save():
+ * History save function
+ */
+private int
+history_save(History *h, const char *fname)
+{
+ FILE *fp;
+ HistEvent ev;
+ int i = 0, retval;
+ size_t len, max_size;
+ char *ptr;
+
+ if ((fp = fopen(fname, "w")) == NULL)
+ return (-1);
+
+ (void) fchmod(fileno(fp), S_IRUSR|S_IWUSR);
+ (void) fputs(hist_cookie, fp);
+ ptr = h_malloc(max_size = 1024);
+ for (retval = HLAST(h, &ev);
+ retval != -1;
+ retval = HPREV(h, &ev), i++) {
+ len = strlen(ev.str) * 4;
+ if (len >= max_size) {
+ max_size = (len + 1023) & 1023;
+ ptr = h_realloc(ptr, max_size);
+ }
+ (void) strvis(ptr, ev.str, VIS_WHITE);
+ (void) fprintf(fp, "%s\n", ev.str);
+ }
+ h_free(ptr);
+ (void) fclose(fp);
+ return (i);
+}
+
+
+/* history_prev_event():
+ * Find the previous event, with number given
+ */
+private int
+history_prev_event(History *h, HistEvent *ev, int num)
+{
+ int retval;
+
+ for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
+ if (ev->num == num)
+ return (0);
+
+ he_seterrev(ev, _HE_NOT_FOUND);
+ return (-1);
+}
+
+
+/* history_next_event():
+ * Find the next event, with number given
+ */
+private int
+history_next_event(History *h, HistEvent *ev, int num)
+{
+ int retval;
+
+ for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
+ if (ev->num == num)
+ return (0);
+
+ he_seterrev(ev, _HE_NOT_FOUND);
+ return (-1);
+}
+
+
+/* history_prev_string():
+ * Find the previous event beginning with string
+ */
+private int
+history_prev_string(History *h, HistEvent *ev, const char *str)
+{
+ size_t len = strlen(str);
+ int retval;
+
+ for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
+ if (strncmp(str, ev->str, len) == 0)
+ return (0);
+
+ he_seterrev(ev, _HE_NOT_FOUND);
+ return (-1);
+}
+
+
+/* history_next_string():
+ * Find the next event beginning with string
+ */
+private int
+history_next_string(History *h, HistEvent *ev, const char *str)
+{
+ size_t len = strlen(str);
+ int retval;
+
+ for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
+ if (strncmp(str, ev->str, len) == 0)
+ return (0);
+
+ he_seterrev(ev, _HE_NOT_FOUND);
+ return (-1);
+}
+
+
+/* history():
+ * User interface to history functions.
+ */
+int
+history(History *h, HistEvent *ev, int fun, ...)
+{
+ va_list va;
+ const char *str;
+ int retval;
+
+ va_start(va, fun);
+
+ he_seterrev(ev, _HE_OK);
+
+ switch (fun) {
+ case H_GETSIZE:
+ retval = history_getsize(h, ev);
+ break;
+
+ case H_SETSIZE:
+ retval = history_setsize(h, ev, va_arg(va, int));
+ break;
+
+ case H_ADD:
+ str = va_arg(va, const char *);
+ retval = HADD(h, ev, str);
+ break;
+
+ case H_ENTER:
+ str = va_arg(va, const char *);
+ if ((retval = HENTER(h, ev, str)) != -1)
+ h->h_ent = ev->num;
+ break;
+
+ case H_APPEND:
+ str = va_arg(va, const char *);
+ if ((retval = HSET(h, ev, h->h_ent)) != -1)
+ retval = HADD(h, ev, str);
+ break;
+
+ case H_FIRST:
+ retval = HFIRST(h, ev);
+ break;
+
+ case H_NEXT:
+ retval = HNEXT(h, ev);
+ break;
+
+ case H_LAST:
+ retval = HLAST(h, ev);
+ break;
+
+ case H_PREV:
+ retval = HPREV(h, ev);
+ break;
+
+ case H_CURR:
+ retval = HCURR(h, ev);
+ break;
+
+ case H_SET:
+ retval = HSET(h, ev, va_arg(va, const int));
+ break;
+
+ case H_CLEAR:
+ HCLEAR(h, ev);
+ retval = 0;
+ break;
+
+ case H_LOAD:
+ retval = history_load(h, va_arg(va, const char *));
+ if (retval == -1)
+ he_seterrev(ev, _HE_HIST_READ);
+ break;
+
+ case H_SAVE:
+ retval = history_save(h, va_arg(va, const char *));
+ if (retval == -1)
+ he_seterrev(ev, _HE_HIST_WRITE);
+ break;
+
+ case H_PREV_EVENT:
+ retval = history_prev_event(h, ev, va_arg(va, int));
+ break;
+
+ case H_NEXT_EVENT:
+ retval = history_next_event(h, ev, va_arg(va, int));
+ break;
+
+ case H_PREV_STR:
+ retval = history_prev_string(h, ev, va_arg(va, const char *));
+ break;
+
+ case H_NEXT_STR:
+ retval = history_next_string(h, ev, va_arg(va, const char *));
+ break;
+
+ case H_FUNC:
+ {
+ History hf;
+
+ hf.h_ref = va_arg(va, ptr_t);
+ h->h_ent = -1;
+ hf.h_first = va_arg(va, history_gfun_t);
+ hf.h_next = va_arg(va, history_gfun_t);
+ hf.h_last = va_arg(va, history_gfun_t);
+ hf.h_prev = va_arg(va, history_gfun_t);
+ hf.h_curr = va_arg(va, history_gfun_t);
+ hf.h_set = va_arg(va, history_sfun_t);
+ hf.h_clear = va_arg(va, history_vfun_t);
+ hf.h_enter = va_arg(va, history_efun_t);
+ hf.h_add = va_arg(va, history_efun_t);
+
+ if ((retval = history_set_fun(h, &hf)) == -1)
+ he_seterrev(ev, _HE_PARAM_MISSING);
+ break;
+ }
+
+ case H_END:
+ history_end(h);
+ retval = 0;
+ break;
+
+ default:
+ retval = -1;
+ he_seterrev(ev, _HE_UNKNOWN);
+ break;
+ }
+ va_end(va);
+ return (retval);
+}
diff --git a/trunk/main/editline/install-sh b/trunk/main/editline/install-sh
new file mode 100755
index 000000000..ebc66913e
--- /dev/null
+++ b/trunk/main/editline/install-sh
@@ -0,0 +1,250 @@
+#! /bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5 (mit/util/scripts/install.sh).
+#
+# Copyright 1991 by the Massachusetts Institute of Technology
+#
+# Permission to use, copy, modify, distribute, and sell this software and its
+# documentation for any purpose is hereby granted without fee, provided that
+# the above copyright notice appear in all copies and that both that
+# copyright notice and this permission notice appear in supporting
+# documentation, and that the name of M.I.T. not be used in advertising or
+# publicity pertaining to distribution of the software without specific,
+# written prior permission. M.I.T. makes no representations about the
+# suitability of this software for any purpose. It is provided "as is"
+# without express or implied warranty.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch. It can only install one file at a time, a restriction
+# shared with many OS's install programs.
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+transformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+ case $1 in
+ -c) instcmd="$cpprog"
+ shift
+ continue;;
+
+ -d) dir_arg=true
+ shift
+ continue;;
+
+ -m) chmodcmd="$chmodprog $2"
+ shift
+ shift
+ continue;;
+
+ -o) chowncmd="$chownprog $2"
+ shift
+ shift
+ continue;;
+
+ -g) chgrpcmd="$chgrpprog $2"
+ shift
+ shift
+ continue;;
+
+ -s) stripcmd="$stripprog"
+ shift
+ continue;;
+
+ -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+ shift
+ continue;;
+
+ -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+ shift
+ continue;;
+
+ *) if [ x"$src" = x ]
+ then
+ src=$1
+ else
+ # this colon is to work around a 386BSD /bin/sh bug
+ :
+ dst=$1
+ fi
+ shift
+ continue;;
+ esac
+done
+
+if [ x"$src" = x ]
+then
+ echo "install: no input file specified"
+ exit 1
+else
+ true
+fi
+
+if [ x"$dir_arg" != x ]; then
+ dst=$src
+ src=""
+
+ if [ -d $dst ]; then
+ instcmd=:
+ else
+ instcmd=mkdir
+ fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad
+# if $src (and thus $dsttmp) contains '*'.
+
+ if [ -f $src -o -d $src ]
+ then
+ true
+ else
+ echo "install: $src does not exist"
+ exit 1
+ fi
+
+ if [ x"$dst" = x ]
+ then
+ echo "install: no destination specified"
+ exit 1
+ else
+ true
+ fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+ if [ -d $dst ]
+ then
+ dst="$dst"/`basename $src`
+ else
+ true
+ fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+# this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+ pathcomp="${pathcomp}${1}"
+ shift
+
+ if [ ! -d "${pathcomp}" ] ;
+ then
+ $mkdirprog "${pathcomp}"
+ else
+ true
+ fi
+
+ pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+ $doit $instcmd $dst &&
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+ if [ x"$transformarg" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ dstfile=`basename $dst $transformbasename |
+ sed $transformarg`$transformbasename
+ fi
+
+# don't allow the sed command to completely eliminate the filename
+
+ if [ x"$dstfile" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ true
+ fi
+
+# Make a temp file name in the proper directory.
+
+ dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+ $doit $instcmd $src $dsttmp &&
+
+ trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing. If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+ $doit $rmcmd -f $dstdir/$dstfile &&
+ $doit $mvcmd $dsttmp $dstdir/$dstfile
+
+fi &&
+
+
+exit 0
diff --git a/trunk/main/editline/key.c b/trunk/main/editline/key.c
new file mode 100644
index 000000000..0dcdf4191
--- /dev/null
+++ b/trunk/main/editline/key.c
@@ -0,0 +1,687 @@
+/* $NetBSD: key.c,v 1.13 2002/03/18 16:00:55 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+#if !defined(lint) && !defined(SCCSID)
+#if 0
+static char sccsid[] = "@(#)key.c 8.1 (Berkeley) 6/4/93";
+#else
+__RCSID("$NetBSD: key.c,v 1.13 2002/03/18 16:00:55 christos Exp $");
+#endif
+#endif /* not lint && not SCCSID */
+
+/*
+ * key.c: This module contains the procedures for maintaining
+ * the extended-key map.
+ *
+ * An extended-key (key) is a sequence of keystrokes introduced
+ * with an sequence introducer and consisting of an arbitrary
+ * number of characters. This module maintains a map (the el->el_key.map)
+ * to convert these extended-key sequences into input strs
+ * (XK_STR), editor functions (XK_CMD), or unix commands (XK_EXE).
+ *
+ * Warning:
+ * If key is a substr of some other keys, then the longer
+ * keys are lost!! That is, if the keys "abcd" and "abcef"
+ * are in el->el_key.map, adding the key "abc" will cause the first two
+ * definitions to be lost.
+ *
+ * Restrictions:
+ * -------------
+ * 1) It is not possible to have one key that is a
+ * substr of another.
+ */
+#include <string.h>
+#include <stdlib.h>
+
+#include "el.h"
+
+/*
+ * The Nodes of the el->el_key.map. The el->el_key.map is a linked list
+ * of these node elements
+ */
+struct key_node_t {
+ char ch; /* single character of key */
+ int type; /* node type */
+ key_value_t val; /* command code or pointer to str, */
+ /* if this is a leaf */
+ struct key_node_t *next; /* ptr to next char of this key */
+ struct key_node_t *sibling; /* ptr to another key with same prefix*/
+};
+
+private int node_trav(EditLine *, key_node_t *, char *,
+ key_value_t *);
+private int node__try(EditLine *, key_node_t *, const char *,
+ key_value_t *, int);
+private key_node_t *node__get(int);
+private void node__put(EditLine *, key_node_t *);
+private int node__delete(EditLine *, key_node_t **, const char *);
+private int node_lookup(EditLine *, const char *, key_node_t *,
+ int);
+private int node_enum(EditLine *, key_node_t *, int);
+private int key__decode_char(char *, int, int);
+
+#define KEY_BUFSIZ EL_BUFSIZ
+
+
+/* key_init():
+ * Initialize the key maps
+ */
+protected int
+key_init(EditLine *el)
+{
+
+ el->el_key.buf = (char *) el_malloc(KEY_BUFSIZ);
+ if (el->el_key.buf == NULL)
+ return (-1);
+ el->el_key.map = NULL;
+ key_reset(el);
+ return (0);
+}
+
+
+/* key_end():
+ * Free the key maps
+ */
+protected void
+key_end(EditLine *el)
+{
+
+ el_free((ptr_t) el->el_key.buf);
+ el->el_key.buf = NULL;
+ node__put(el, el->el_key.map);
+ el->el_key.map = NULL;
+}
+
+
+/* key_map_cmd():
+ * Associate cmd with a key value
+ */
+protected key_value_t *
+key_map_cmd(EditLine *el, int cmd)
+{
+
+ el->el_key.val.cmd = (el_action_t) cmd;
+ return (&el->el_key.val);
+}
+
+
+/* key_map_str():
+ * Associate str with a key value
+ */
+protected key_value_t *
+key_map_str(EditLine *el, char *str)
+{
+
+ el->el_key.val.str = str;
+ return (&el->el_key.val);
+}
+
+
+/* key_reset():
+ * Takes all nodes on el->el_key.map and puts them on free list. Then
+ * initializes el->el_key.map with arrow keys
+ * [Always bind the ansi arrow keys?]
+ */
+protected void
+key_reset(EditLine *el)
+{
+
+ node__put(el, el->el_key.map);
+ el->el_key.map = NULL;
+ return;
+}
+
+
+/* key_get():
+ * Calls the recursive function with entry point el->el_key.map
+ * Looks up *ch in map and then reads characters until a
+ * complete match is found or a mismatch occurs. Returns the
+ * type of the match found (XK_STR, XK_CMD, or XK_EXE).
+ * Returns NULL in val.str and XK_STR for no match.
+ * The last character read is returned in *ch.
+ */
+protected int
+key_get(EditLine *el, char *ch, key_value_t *val)
+{
+
+ return (node_trav(el, el->el_key.map, ch, val));
+}
+
+
+/* key_add():
+ * Adds key to the el->el_key.map and associates the value in val with it.
+ * If key is already is in el->el_key.map, the new code is applied to the
+ * existing key. Ntype specifies if code is a command, an
+ * out str or a unix command.
+ */
+protected void
+key_add(EditLine *el, const char *key, key_value_t *val, int ntype)
+{
+
+ if (key[0] == '\0') {
+ (void) fprintf(el->el_errfile,
+ "key_add: Null extended-key not allowed.\n");
+ return;
+ }
+ if (ntype == XK_CMD && val->cmd == ED_SEQUENCE_LEAD_IN) {
+ (void) fprintf(el->el_errfile,
+ "key_add: sequence-lead-in command not allowed\n");
+ return;
+ }
+ if (el->el_key.map == NULL)
+ /* tree is initially empty. Set up new node to match key[0] */
+ el->el_key.map = node__get(key[0]);
+ /* it is properly initialized */
+
+ /* Now recurse through el->el_key.map */
+ (void) node__try(el, el->el_key.map, key, val, ntype);
+ return;
+}
+
+
+/* key_clear():
+ *
+ */
+protected void
+key_clear(EditLine *el, el_action_t *map, const char *in)
+{
+
+ if ((map[(unsigned char)*in] == ED_SEQUENCE_LEAD_IN) &&
+ ((map == el->el_map.key &&
+ el->el_map.alt[(unsigned char)*in] != ED_SEQUENCE_LEAD_IN) ||
+ (map == el->el_map.alt &&
+ el->el_map.key[(unsigned char)*in] != ED_SEQUENCE_LEAD_IN)))
+ (void) key_delete(el, in);
+}
+
+
+/* key_delete():
+ * Delete the key and all longer keys staring with key, if
+ * they exists.
+ */
+protected int
+key_delete(EditLine *el, const char *key)
+{
+
+ if (key[0] == '\0') {
+ (void) fprintf(el->el_errfile,
+ "key_delete: Null extended-key not allowed.\n");
+ return (-1);
+ }
+ if (el->el_key.map == NULL)
+ return (0);
+
+ (void) node__delete(el, &el->el_key.map, key);
+ return (0);
+}
+
+
+/* key_print():
+ * Print the binding associated with key key.
+ * Print entire el->el_key.map if null
+ */
+protected void
+key_print(EditLine *el, const char *key)
+{
+
+ /* do nothing if el->el_key.map is empty and null key specified */
+ if (el->el_key.map == NULL && *key == 0)
+ return;
+
+ el->el_key.buf[0] = '"';
+ if (node_lookup(el, key, el->el_key.map, 1) <= -1)
+ /* key is not bound */
+ (void) fprintf(el->el_errfile, "Unbound extended key \"%s\"\n",
+ key);
+ return;
+}
+
+
+/* node_trav():
+ * recursively traverses node in tree until match or mismatch is
+ * found. May read in more characters.
+ */
+private int
+node_trav(EditLine *el, key_node_t *ptr, char *ch, key_value_t *val)
+{
+
+ if (ptr->ch == *ch) {
+ /* match found */
+ if (ptr->next) {
+ /* key not complete so get next char */
+ if (el_getc(el, ch) != 1) { /* if EOF or error */
+ val->cmd = ED_END_OF_FILE;
+ return (XK_CMD);
+ /* PWP: Pretend we just read an end-of-file */
+ }
+ return (node_trav(el, ptr->next, ch, val));
+ } else {
+ *val = ptr->val;
+ if (ptr->type != XK_CMD)
+ *ch = '\0';
+ return (ptr->type);
+ }
+ } else {
+ /* no match found here */
+ if (ptr->sibling) {
+ /* try next sibling */
+ return (node_trav(el, ptr->sibling, ch, val));
+ } else {
+ /* no next sibling -- mismatch */
+ val->str = NULL;
+ return (XK_STR);
+ }
+ }
+}
+
+
+/* node__try():
+ * Find a node that matches *str or allocate a new one
+ */
+private int
+node__try(EditLine *el, key_node_t *ptr, const char *str, key_value_t *val, int ntype)
+{
+
+ if (ptr->ch != *str) {
+ key_node_t *xm;
+
+ for (xm = ptr; xm->sibling != NULL; xm = xm->sibling)
+ if (xm->sibling->ch == *str)
+ break;
+ if (xm->sibling == NULL)
+ xm->sibling = node__get(*str); /* setup new node */
+ ptr = xm->sibling;
+ }
+ if (*++str == '\0') {
+ /* we're there */
+ if (ptr->next != NULL) {
+ node__put(el, ptr->next);
+ /* lose longer keys with this prefix */
+ ptr->next = NULL;
+ }
+ switch (ptr->type) {
+ case XK_CMD:
+ case XK_NOD:
+ break;
+ case XK_STR:
+ case XK_EXE:
+ if (ptr->val.str)
+ el_free((ptr_t) ptr->val.str);
+ break;
+ default:
+ EL_ABORT((el->el_errfile, "Bad XK_ type %d\n",
+ ptr->type));
+ break;
+ }
+
+ switch (ptr->type = ntype) {
+ case XK_CMD:
+ ptr->val = *val;
+ break;
+ case XK_STR:
+ case XK_EXE:
+ ptr->val.str = strdup(val->str);
+ break;
+ default:
+ EL_ABORT((el->el_errfile, "Bad XK_ type %d\n", ntype));
+ break;
+ }
+ } else {
+ /* still more chars to go */
+ if (ptr->next == NULL)
+ ptr->next = node__get(*str); /* setup new node */
+ (void) node__try(el, ptr->next, str, val, ntype);
+ }
+ return (0);
+}
+
+
+/* node__delete():
+ * Delete node that matches str
+ */
+private int
+node__delete(EditLine *el, key_node_t **inptr, const char *str)
+{
+ key_node_t *ptr;
+ key_node_t *prev_ptr = NULL;
+
+ ptr = *inptr;
+
+ if (ptr->ch != *str) {
+ key_node_t *xm;
+
+ for (xm = ptr; xm->sibling != NULL; xm = xm->sibling)
+ if (xm->sibling->ch == *str)
+ break;
+ if (xm->sibling == NULL)
+ return (0);
+ prev_ptr = xm;
+ ptr = xm->sibling;
+ }
+ if (*++str == '\0') {
+ /* we're there */
+ if (prev_ptr == NULL)
+ *inptr = ptr->sibling;
+ else
+ prev_ptr->sibling = ptr->sibling;
+ ptr->sibling = NULL;
+ node__put(el, ptr);
+ return (1);
+ } else if (ptr->next != NULL &&
+ node__delete(el, &ptr->next, str) == 1) {
+ if (ptr->next != NULL)
+ return (0);
+ if (prev_ptr == NULL)
+ *inptr = ptr->sibling;
+ else
+ prev_ptr->sibling = ptr->sibling;
+ ptr->sibling = NULL;
+ node__put(el, ptr);
+ return (1);
+ } else {
+ return (0);
+ }
+}
+
+
+/* node__put():
+ * Puts a tree of nodes onto free list using free(3).
+ */
+private void
+node__put(EditLine *el, key_node_t *ptr)
+{
+ if (ptr == NULL)
+ return;
+
+ if (ptr->next != NULL) {
+ node__put(el, ptr->next);
+ ptr->next = NULL;
+ }
+ node__put(el, ptr->sibling);
+
+ switch (ptr->type) {
+ case XK_CMD:
+ case XK_NOD:
+ break;
+ case XK_EXE:
+ case XK_STR:
+ if (ptr->val.str != NULL)
+ el_free((ptr_t) ptr->val.str);
+ break;
+ default:
+ EL_ABORT((el->el_errfile, "Bad XK_ type %d\n", ptr->type));
+ break;
+ }
+ el_free((ptr_t) ptr);
+}
+
+
+/* node__get():
+ * Returns pointer to an key_node_t for ch.
+ */
+private key_node_t *
+node__get(int ch)
+{
+ key_node_t *ptr;
+
+ ptr = (key_node_t *) el_malloc((size_t) sizeof(key_node_t));
+ if (ptr == NULL)
+ return NULL;
+ ptr->ch = ch;
+ ptr->type = XK_NOD;
+ ptr->val.str = NULL;
+ ptr->next = NULL;
+ ptr->sibling = NULL;
+ return (ptr);
+}
+
+
+
+/* node_lookup():
+ * look for the str starting at node ptr.
+ * Print if last node
+ */
+private int
+node_lookup(EditLine *el, const char *str, key_node_t *ptr, int cnt)
+{
+ int ncnt;
+
+ if (ptr == NULL)
+ return (-1); /* cannot have null ptr */
+
+ if (*str == 0) {
+ /* no more chars in str. node_enum from here. */
+ (void) node_enum(el, ptr, cnt);
+ return (0);
+ } else {
+ /* If match put this char into el->el_key.buf. Recurse */
+ if (ptr->ch == *str) {
+ /* match found */
+ ncnt = key__decode_char(el->el_key.buf, cnt,
+ (unsigned char) ptr->ch);
+ if (ptr->next != NULL)
+ /* not yet at leaf */
+ return (node_lookup(el, str + 1, ptr->next,
+ ncnt + 1));
+ else {
+ /* next node is null so key should be complete */
+ if (str[1] == 0) {
+ el->el_key.buf[ncnt + 1] = '"';
+ el->el_key.buf[ncnt + 2] = '\0';
+ key_kprint(el, el->el_key.buf,
+ &ptr->val, ptr->type);
+ return (0);
+ } else
+ return (-1);
+ /* mismatch -- str still has chars */
+ }
+ } else {
+ /* no match found try sibling */
+ if (ptr->sibling)
+ return (node_lookup(el, str, ptr->sibling,
+ cnt));
+ else
+ return (-1);
+ }
+ }
+}
+
+
+/* node_enum():
+ * Traverse the node printing the characters it is bound in buffer
+ */
+private int
+node_enum(EditLine *el, key_node_t *ptr, int cnt)
+{
+ int ncnt;
+
+ if (cnt >= KEY_BUFSIZ - 5) { /* buffer too small */
+ el->el_key.buf[++cnt] = '"';
+ el->el_key.buf[++cnt] = '\0';
+ (void) fprintf(el->el_errfile,
+ "Some extended keys too long for internal print buffer");
+ (void) fprintf(el->el_errfile, " \"%s...\"\n", el->el_key.buf);
+ return (0);
+ }
+ if (ptr == NULL) {
+#ifdef DEBUG_EDIT
+ (void) fprintf(el->el_errfile,
+ "node_enum: BUG!! Null ptr passed\n!");
+#endif
+ return (-1);
+ }
+ /* put this char at end of str */
+ ncnt = key__decode_char(el->el_key.buf, cnt, (unsigned char) ptr->ch);
+ if (ptr->next == NULL) {
+ /* print this key and function */
+ el->el_key.buf[ncnt + 1] = '"';
+ el->el_key.buf[ncnt + 2] = '\0';
+ key_kprint(el, el->el_key.buf, &ptr->val, ptr->type);
+ } else
+ (void) node_enum(el, ptr->next, ncnt + 1);
+
+ /* go to sibling if there is one */
+ if (ptr->sibling)
+ (void) node_enum(el, ptr->sibling, cnt);
+ return (0);
+}
+
+
+/* key_kprint():
+ * Print the specified key and its associated
+ * function specified by val
+ */
+protected void
+key_kprint(EditLine *el, const char *key, key_value_t *val, int ntype)
+{
+ el_bindings_t *fp;
+ char unparsbuf[EL_BUFSIZ];
+ static const char fmt[] = "%-15s-> %s\n";
+
+ if (val != NULL)
+ switch (ntype) {
+ case XK_STR:
+ case XK_EXE:
+ (void) fprintf(el->el_outfile, fmt, key,
+ key__decode_str(val->str, unparsbuf,
+ ntype == XK_STR ? "\"\"" : "[]"));
+ break;
+ case XK_CMD:
+ for (fp = el->el_map.help; fp->name; fp++)
+ if (val->cmd == fp->func) {
+ (void) fprintf(el->el_outfile, fmt,
+ key, fp->name);
+ break;
+ }
+#ifdef DEBUG_KEY
+ if (fp->name == NULL)
+ (void) fprintf(el->el_outfile,
+ "BUG! Command not found.\n");
+#endif
+
+ break;
+ default:
+ EL_ABORT((el->el_errfile, "Bad XK_ type %d\n", ntype));
+ break;
+ }
+ else
+ (void) fprintf(el->el_outfile, fmt, key, "no input");
+}
+
+
+/* key__decode_char():
+ * Put a printable form of char in buf.
+ */
+private int
+key__decode_char(char *buf, int cnt, int ch)
+{
+ if (ch == 0) {
+ buf[cnt++] = '^';
+ buf[cnt] = '@';
+ return (cnt);
+ }
+ if (iscntrl(ch)) {
+ buf[cnt++] = '^';
+ if (ch == '\177')
+ buf[cnt] = '?';
+ else
+ buf[cnt] = ch | 0100;
+ } else if (ch == '^') {
+ buf[cnt++] = '\\';
+ buf[cnt] = '^';
+ } else if (ch == '\\') {
+ buf[cnt++] = '\\';
+ buf[cnt] = '\\';
+ } else if (ch == ' ' || (isprint(ch) && !isspace(ch))) {
+ buf[cnt] = ch;
+ } else {
+ buf[cnt++] = '\\';
+ buf[cnt++] = (((unsigned int) ch >> 6) & 7) + '0';
+ buf[cnt++] = (((unsigned int) ch >> 3) & 7) + '0';
+ buf[cnt] = (ch & 7) + '0';
+ }
+ return (cnt);
+}
+
+
+/* key__decode_str():
+ * Make a printable version of the ey
+ */
+protected char *
+key__decode_str(const char *str, char *buf, const char *sep)
+{
+ char *b;
+ const char *p;
+
+ b = buf;
+ if (sep[0] != '\0')
+ *b++ = sep[0];
+ if (*str == 0) {
+ *b++ = '^';
+ *b++ = '@';
+ if (sep[0] != '\0' && sep[1] != '\0')
+ *b++ = sep[1];
+ *b++ = 0;
+ return (buf);
+ }
+ for (p = str; *p != 0; p++) {
+ if (iscntrl((unsigned char) *p)) {
+ *b++ = '^';
+ if (*p == '\177')
+ *b++ = '?';
+ else
+ *b++ = *p | 0100;
+ } else if (*p == '^' || *p == '\\') {
+ *b++ = '\\';
+ *b++ = *p;
+ } else if (*p == ' ' || (isprint((unsigned char) *p) &&
+ !isspace((unsigned char) *p))) {
+ *b++ = *p;
+ } else {
+ *b++ = '\\';
+ *b++ = (((unsigned int) *p >> 6) & 7) + '0';
+ *b++ = (((unsigned int) *p >> 3) & 7) + '0';
+ *b++ = (*p & 7) + '0';
+ }
+ }
+ if (sep[0] != '\0' && sep[1] != '\0')
+ *b++ = sep[1];
+ *b++ = 0;
+ return (buf); /* should check for overflow */
+}
diff --git a/trunk/main/editline/key.h b/trunk/main/editline/key.h
new file mode 100644
index 000000000..80d8626b8
--- /dev/null
+++ b/trunk/main/editline/key.h
@@ -0,0 +1,79 @@
+/* $NetBSD: key.h,v 1.6 2002/03/18 16:00:55 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)key.h 8.1 (Berkeley) 6/4/93
+ */
+
+/*
+ * el.key.h: Key macro header
+ */
+#ifndef _h_el_key
+#define _h_el_key
+
+typedef union key_value_t {
+ el_action_t cmd; /* If it is a command the # */
+ char *str; /* If it is a string... */
+} key_value_t;
+
+typedef struct key_node_t key_node_t;
+
+typedef struct el_key_t {
+ char *buf; /* Key print buffer */
+ key_node_t *map; /* Key map */
+ key_value_t val; /* Local conversion buffer */
+} el_key_t;
+
+#define XK_CMD 0
+#define XK_STR 1
+#define XK_NOD 2
+#define XK_EXE 3
+
+protected int key_init(EditLine *);
+protected void key_end(EditLine *);
+protected key_value_t *key_map_cmd(EditLine *, int);
+protected key_value_t *key_map_str(EditLine *, char *);
+protected void key_reset(EditLine *);
+protected int key_get(EditLine *, char *, key_value_t *);
+protected void key_add(EditLine *, const char *, key_value_t *, int);
+protected void key_clear(EditLine *, el_action_t *, const char *);
+protected int key_delete(EditLine *, const char *);
+protected void key_print(EditLine *, const char *);
+protected void key_kprint(EditLine *, const char *, key_value_t *,
+ int);
+protected char *key__decode_str(const char *, char *, const char *);
+
+#endif /* _h_el_key */
diff --git a/trunk/main/editline/makelist b/trunk/main/editline/makelist
new file mode 100644
index 000000000..36f434cd0
--- /dev/null
+++ b/trunk/main/editline/makelist
@@ -0,0 +1,254 @@
+#!/bin/sh -
+# $NetBSD: makelist,v 1.7 2001/01/09 19:22:31 jdolecek Exp $
+#
+# Copyright (c) 1992, 1993
+# The Regents of the University of California. All rights reserved.
+#
+# This code is derived from software contributed to Berkeley by
+# Christos Zoulas of Cornell University.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the University of
+# California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# @(#)makelist 5.3 (Berkeley) 6/4/93
+
+# makelist.sh: Automatically generate header files...
+
+AWK=/usr/bin/awk
+USAGE="Usage: $0 -h|-e|-fc|-fh|-bc|-bh|-m <filenames>"
+
+if [ "x$1" = "x" ]
+then
+ echo $USAGE 1>&2
+ exit 1
+fi
+
+FLAG="$1"
+shift
+
+FILES="$@"
+
+case $FLAG in
+
+# generate foo.h file from foo.c
+#
+-h)
+ set - `echo $FILES | sed -e 's/\\./_/g'`
+ hdr="_h_`basename $1`"
+ cat $FILES | $AWK '
+ BEGIN {
+ printf("/* Automatically generated file, do not edit */\n");
+ printf("#ifndef %s\n#define %s\n", "'$hdr'", "'$hdr'");
+ }
+ /\(\):/ {
+ pr = substr($2, 1, 2);
+ if (pr == "vi" || pr == "em" || pr == "ed") {
+ name = substr($2, 1, length($2) - 3);
+#
+# XXX: need a space between name and prototype so that -fc and -fh
+# parsing is much easier
+#
+ printf("protected el_action_t\t%s (EditLine *, int);\n", name);
+ }
+ }
+ END {
+ printf("#endif /* %s */\n", "'$hdr'");
+ }'
+ ;;
+
+# generate help.c from various .c files
+#
+-bc)
+ cat $FILES | $AWK '
+ BEGIN {
+ printf("/* Automatically generated file, do not edit */\n");
+ printf("#include \"sys.h\"\n#include \"el.h\"\n");
+ printf("private const struct el_bindings_t el_func_help[] = {\n");
+ low = "abcdefghijklmnopqrstuvwxyz_";
+ high = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_";
+ for (i = 1; i <= length(low); i++)
+ tr[substr(low, i, 1)] = substr(high, i, 1);
+ }
+ /\(\):/ {
+ pr = substr($2, 1, 2);
+ if (pr == "vi" || pr == "em" || pr == "ed") {
+ name = substr($2, 1, length($2) - 3);
+ uname = "";
+ fname = "";
+ for (i = 1; i <= length(name); i++) {
+ s = substr(name, i, 1);
+ uname = uname tr[s];
+ if (s == "_")
+ s = "-";
+ fname = fname s;
+ }
+
+ printf(" { %-30.30s %-30.30s\n","\"" fname "\",", uname ",");
+ ok = 1;
+ }
+ }
+ /^ \*/ {
+ if (ok) {
+ printf(" \"");
+ for (i = 2; i < NF; i++)
+ printf("%s ", $i);
+ printf("%s\" },\n", $i);
+ ok = 0;
+ }
+ }
+ END {
+ printf(" { NULL, 0, NULL }\n");
+ printf("};\n");
+ printf("\nprotected const el_bindings_t* help__get()");
+ printf("{ return el_func_help; }\n");
+ }'
+ ;;
+
+# generate help.h from various .c files
+#
+-bh)
+ $AWK '
+ BEGIN {
+ printf("/* Automatically generated file, do not edit */\n");
+ printf("#ifndef _h_help_c\n#define _h_help_c\n");
+ printf("protected const el_bindings_t *help__get(void);\n");
+ printf("#endif /* _h_help_c */\n");
+ }' /dev/null
+ ;;
+
+# generate fcns.h from various .h files
+#
+-fh)
+ cat $FILES | $AWK '/el_action_t/ { print $3 }' | \
+ sort | tr '[:lower:]' '[:upper:]' | $AWK '
+ BEGIN {
+ printf("/* Automatically generated file, do not edit */\n");
+ printf("#ifndef _h_fcns_c\n#define _h_fcns_c\n");
+ count = 0;
+ }
+ {
+ printf("#define\t%-30.30s\t%3d\n", $1, count++);
+ }
+ END {
+ printf("#define\t%-30.30s\t%3d\n", "EL_NUM_FCNS", count);
+
+ printf("typedef el_action_t (*el_func_t)(EditLine *, int);");
+ printf("\nprotected const el_func_t* func__get(void);\n");
+ printf("#endif /* _h_fcns_c */\n");
+ }'
+ ;;
+
+# generate fcns.c from various .h files
+#
+-fc)
+ cat $FILES | $AWK '/el_action_t/ { print $3 }' | sort | $AWK '
+ BEGIN {
+ printf("/* Automatically generated file, do not edit */\n");
+ printf("#include \"sys.h\"\n#include \"el.h\"\n");
+ printf("private const el_func_t el_func[] = {");
+ maxlen = 80;
+ needn = 1;
+ len = 0;
+ }
+ {
+ clen = 25 + 2;
+ len += clen;
+ if (len >= maxlen)
+ needn = 1;
+ if (needn) {
+ printf("\n ");
+ needn = 0;
+ len = 4 + clen;
+ }
+ s = $1 ",";
+ printf("%-26.26s ", s);
+ }
+ END {
+ printf("\n};\n");
+ printf("\nprotected const el_func_t* func__get() { return el_func; }\n");
+ }'
+ ;;
+
+# generate editline.c from various .c files
+#
+-e)
+ echo "$FILES" | tr ' ' '\012' | $AWK '
+ BEGIN {
+ printf("/* Automatically generated file, do not edit */\n");
+ printf("#define protected static\n");
+ printf("#define SCCSID\n");
+ }
+ {
+ printf("#include \"%s\"\n", $1);
+ }'
+ ;;
+
+# generate man page fragment from various .c files
+#
+-m)
+ cat $FILES | $AWK '
+ BEGIN {
+ printf(".\\\" Section automatically generated with makelist\n");
+ printf(".Bl -tag -width 4n\n");
+ }
+ /\(\):/ {
+ pr = substr($2, 1, 2);
+ if (pr == "vi" || pr == "em" || pr == "ed") {
+ name = substr($2, 1, length($2) - 3);
+ fname = "";
+ for (i = 1; i <= length(name); i++) {
+ s = substr(name, i, 1);
+ if (s == "_")
+ s = "-";
+ fname = fname s;
+ }
+
+ printf(".It Ic %s\n", fname);
+ ok = 1;
+ }
+ }
+ /^ \*/ {
+ if (ok) {
+ for (i = 2; i < NF; i++)
+ printf("%s ", $i);
+ printf("%s.\n", $i);
+ ok = 0;
+ }
+ }
+ END {
+ printf(".El\n");
+ printf(".\\\" End of section automatically generated with makelist\n");
+ }'
+ ;;
+
+*)
+ echo $USAGE 1>&2
+ exit 1
+ ;;
+
+esac
diff --git a/trunk/main/editline/map.c b/trunk/main/editline/map.c
new file mode 100644
index 000000000..4187cb597
--- /dev/null
+++ b/trunk/main/editline/map.c
@@ -0,0 +1,1418 @@
+/* $NetBSD: map.c,v 1.15 2002/03/18 16:00:55 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+#if !defined(lint) && !defined(SCCSID)
+#if 0
+static char sccsid[] = "@(#)map.c 8.1 (Berkeley) 6/4/93";
+#else
+__RCSID("$NetBSD: map.c,v 1.15 2002/03/18 16:00:55 christos Exp $");
+#endif
+#endif /* not lint && not SCCSID */
+
+/*
+ * map.c: Editor function definitions
+ */
+#include <stdlib.h>
+#include "el.h"
+
+#define N_KEYS 256
+
+private void map_print_key(EditLine *, el_action_t *, const char *);
+private void map_print_some_keys(EditLine *, el_action_t *, int, int);
+private void map_print_all_keys(EditLine *);
+private void map_init_nls(EditLine *);
+private void map_init_meta(EditLine *);
+
+/* keymap tables ; should be N_KEYS*sizeof(KEYCMD) bytes long */
+
+
+private const el_action_t el_map_emacs[] = {
+ /* 0 */ EM_SET_MARK, /* ^@ */
+ /* 1 */ ED_MOVE_TO_BEG, /* ^A */
+ /* 2 */ ED_PREV_CHAR, /* ^B */
+ /* 3 */ ED_TTY_SIGINT, /* ^C */
+ /* 4 */ EM_DELETE_OR_LIST, /* ^D */
+ /* 5 */ ED_MOVE_TO_END, /* ^E */
+ /* 6 */ ED_NEXT_CHAR, /* ^F */
+ /* 7 */ ED_UNASSIGNED, /* ^G */
+ /* 8 */ ED_DELETE_PREV_CHAR, /* ^H */
+ /* 9 */ ED_UNASSIGNED, /* ^I */
+ /* 10 */ ED_NEWLINE, /* ^J */
+ /* 11 */ ED_KILL_LINE, /* ^K */
+ /* 12 */ ED_CLEAR_SCREEN, /* ^L */
+ /* 13 */ ED_NEWLINE, /* ^M */
+ /* 14 */ ED_NEXT_HISTORY, /* ^N */
+ /* 15 */ ED_TTY_FLUSH_OUTPUT, /* ^O */
+ /* 16 */ ED_PREV_HISTORY, /* ^P */
+ /* 17 */ ED_TTY_START_OUTPUT, /* ^Q */
+ /* 18 */ ED_REDISPLAY, /* ^R */
+ /* 19 */ ED_TTY_STOP_OUTPUT, /* ^S */
+ /* 20 */ ED_TRANSPOSE_CHARS, /* ^T */
+ /* 21 */ EM_KILL_LINE, /* ^U */
+ /* 22 */ ED_QUOTED_INSERT, /* ^V */
+ /* 23 */ EM_KILL_REGION, /* ^W */
+ /* 24 */ ED_SEQUENCE_LEAD_IN, /* ^X */
+ /* 25 */ EM_YANK, /* ^Y */
+ /* 26 */ ED_TTY_SIGTSTP, /* ^Z */
+ /* 27 */ EM_META_NEXT, /* ^[ */
+ /* 28 */ ED_TTY_SIGQUIT, /* ^\ */
+ /* 29 */ ED_TTY_DSUSP, /* ^] */
+ /* 30 */ ED_UNASSIGNED, /* ^^ */
+ /* 31 */ ED_UNASSIGNED, /* ^_ */
+ /* 32 */ ED_INSERT, /* SPACE */
+ /* 33 */ ED_INSERT, /* ! */
+ /* 34 */ ED_INSERT, /* " */
+ /* 35 */ ED_INSERT, /* # */
+ /* 36 */ ED_INSERT, /* $ */
+ /* 37 */ ED_INSERT, /* % */
+ /* 38 */ ED_INSERT, /* & */
+ /* 39 */ ED_INSERT, /* ' */
+ /* 40 */ ED_INSERT, /* ( */
+ /* 41 */ ED_INSERT, /* ) */
+ /* 42 */ ED_INSERT, /* * */
+ /* 43 */ ED_INSERT, /* + */
+ /* 44 */ ED_INSERT, /* , */
+ /* 45 */ ED_INSERT, /* - */
+ /* 46 */ ED_INSERT, /* . */
+ /* 47 */ ED_INSERT, /* / */
+ /* 48 */ ED_DIGIT, /* 0 */
+ /* 49 */ ED_DIGIT, /* 1 */
+ /* 50 */ ED_DIGIT, /* 2 */
+ /* 51 */ ED_DIGIT, /* 3 */
+ /* 52 */ ED_DIGIT, /* 4 */
+ /* 53 */ ED_DIGIT, /* 5 */
+ /* 54 */ ED_DIGIT, /* 6 */
+ /* 55 */ ED_DIGIT, /* 7 */
+ /* 56 */ ED_DIGIT, /* 8 */
+ /* 57 */ ED_DIGIT, /* 9 */
+ /* 58 */ ED_INSERT, /* : */
+ /* 59 */ ED_INSERT, /* ; */
+ /* 60 */ ED_INSERT, /* < */
+ /* 61 */ ED_INSERT, /* = */
+ /* 62 */ ED_INSERT, /* > */
+ /* 63 */ ED_INSERT, /* ? */
+ /* 64 */ ED_INSERT, /* @ */
+ /* 65 */ ED_INSERT, /* A */
+ /* 66 */ ED_INSERT, /* B */
+ /* 67 */ ED_INSERT, /* C */
+ /* 68 */ ED_INSERT, /* D */
+ /* 69 */ ED_INSERT, /* E */
+ /* 70 */ ED_INSERT, /* F */
+ /* 71 */ ED_INSERT, /* G */
+ /* 72 */ ED_INSERT, /* H */
+ /* 73 */ ED_INSERT, /* I */
+ /* 74 */ ED_INSERT, /* J */
+ /* 75 */ ED_INSERT, /* K */
+ /* 76 */ ED_INSERT, /* L */
+ /* 77 */ ED_INSERT, /* M */
+ /* 78 */ ED_INSERT, /* N */
+ /* 79 */ ED_INSERT, /* O */
+ /* 80 */ ED_INSERT, /* P */
+ /* 81 */ ED_INSERT, /* Q */
+ /* 82 */ ED_INSERT, /* R */
+ /* 83 */ ED_INSERT, /* S */
+ /* 84 */ ED_INSERT, /* T */
+ /* 85 */ ED_INSERT, /* U */
+ /* 86 */ ED_INSERT, /* V */
+ /* 87 */ ED_INSERT, /* W */
+ /* 88 */ ED_INSERT, /* X */
+ /* 89 */ ED_INSERT, /* Y */
+ /* 90 */ ED_INSERT, /* Z */
+ /* 91 */ ED_INSERT, /* [ */
+ /* 92 */ ED_INSERT, /* \ */
+ /* 93 */ ED_INSERT, /* ] */
+ /* 94 */ ED_INSERT, /* ^ */
+ /* 95 */ ED_INSERT, /* _ */
+ /* 96 */ ED_INSERT, /* ` */
+ /* 97 */ ED_INSERT, /* a */
+ /* 98 */ ED_INSERT, /* b */
+ /* 99 */ ED_INSERT, /* c */
+ /* 100 */ ED_INSERT, /* d */
+ /* 101 */ ED_INSERT, /* e */
+ /* 102 */ ED_INSERT, /* f */
+ /* 103 */ ED_INSERT, /* g */
+ /* 104 */ ED_INSERT, /* h */
+ /* 105 */ ED_INSERT, /* i */
+ /* 106 */ ED_INSERT, /* j */
+ /* 107 */ ED_INSERT, /* k */
+ /* 108 */ ED_INSERT, /* l */
+ /* 109 */ ED_INSERT, /* m */
+ /* 110 */ ED_INSERT, /* n */
+ /* 111 */ ED_INSERT, /* o */
+ /* 112 */ ED_INSERT, /* p */
+ /* 113 */ ED_INSERT, /* q */
+ /* 114 */ ED_INSERT, /* r */
+ /* 115 */ ED_INSERT, /* s */
+ /* 116 */ ED_INSERT, /* t */
+ /* 117 */ ED_INSERT, /* u */
+ /* 118 */ ED_INSERT, /* v */
+ /* 119 */ ED_INSERT, /* w */
+ /* 120 */ ED_INSERT, /* x */
+ /* 121 */ ED_INSERT, /* y */
+ /* 122 */ ED_INSERT, /* z */
+ /* 123 */ ED_INSERT, /* { */
+ /* 124 */ ED_INSERT, /* | */
+ /* 125 */ ED_INSERT, /* } */
+ /* 126 */ ED_INSERT, /* ~ */
+ /* 127 */ ED_DELETE_PREV_CHAR, /* ^? */
+ /* 128 */ ED_UNASSIGNED, /* M-^@ */
+ /* 129 */ ED_UNASSIGNED, /* M-^A */
+ /* 130 */ ED_UNASSIGNED, /* M-^B */
+ /* 131 */ ED_UNASSIGNED, /* M-^C */
+ /* 132 */ ED_UNASSIGNED, /* M-^D */
+ /* 133 */ ED_UNASSIGNED, /* M-^E */
+ /* 134 */ ED_UNASSIGNED, /* M-^F */
+ /* 135 */ ED_UNASSIGNED, /* M-^G */
+ /* 136 */ ED_DELETE_PREV_WORD, /* M-^H */
+ /* 137 */ ED_UNASSIGNED, /* M-^I */
+ /* 138 */ ED_UNASSIGNED, /* M-^J */
+ /* 139 */ ED_UNASSIGNED, /* M-^K */
+ /* 140 */ ED_CLEAR_SCREEN, /* M-^L */
+ /* 141 */ ED_UNASSIGNED, /* M-^M */
+ /* 142 */ ED_UNASSIGNED, /* M-^N */
+ /* 143 */ ED_UNASSIGNED, /* M-^O */
+ /* 144 */ ED_UNASSIGNED, /* M-^P */
+ /* 145 */ ED_UNASSIGNED, /* M-^Q */
+ /* 146 */ ED_UNASSIGNED, /* M-^R */
+ /* 147 */ ED_UNASSIGNED, /* M-^S */
+ /* 148 */ ED_UNASSIGNED, /* M-^T */
+ /* 149 */ ED_UNASSIGNED, /* M-^U */
+ /* 150 */ ED_UNASSIGNED, /* M-^V */
+ /* 151 */ ED_UNASSIGNED, /* M-^W */
+ /* 152 */ ED_UNASSIGNED, /* M-^X */
+ /* 153 */ ED_UNASSIGNED, /* M-^Y */
+ /* 154 */ ED_UNASSIGNED, /* M-^Z */
+ /* 155 */ ED_UNASSIGNED, /* M-^[ */
+ /* 156 */ ED_UNASSIGNED, /* M-^\ */
+ /* 157 */ ED_UNASSIGNED, /* M-^] */
+ /* 158 */ ED_UNASSIGNED, /* M-^^ */
+ /* 159 */ EM_COPY_PREV_WORD, /* M-^_ */
+ /* 160 */ ED_UNASSIGNED, /* M-SPACE */
+ /* 161 */ ED_UNASSIGNED, /* M-! */
+ /* 162 */ ED_UNASSIGNED, /* M-" */
+ /* 163 */ ED_UNASSIGNED, /* M-# */
+ /* 164 */ ED_UNASSIGNED, /* M-$ */
+ /* 165 */ ED_UNASSIGNED, /* M-% */
+ /* 166 */ ED_UNASSIGNED, /* M-& */
+ /* 167 */ ED_UNASSIGNED, /* M-' */
+ /* 168 */ ED_UNASSIGNED, /* M-( */
+ /* 169 */ ED_UNASSIGNED, /* M-) */
+ /* 170 */ ED_UNASSIGNED, /* M-* */
+ /* 171 */ ED_UNASSIGNED, /* M-+ */
+ /* 172 */ ED_UNASSIGNED, /* M-, */
+ /* 173 */ ED_UNASSIGNED, /* M-- */
+ /* 174 */ ED_UNASSIGNED, /* M-. */
+ /* 175 */ ED_UNASSIGNED, /* M-/ */
+ /* 176 */ ED_ARGUMENT_DIGIT, /* M-0 */
+ /* 177 */ ED_ARGUMENT_DIGIT, /* M-1 */
+ /* 178 */ ED_ARGUMENT_DIGIT, /* M-2 */
+ /* 179 */ ED_ARGUMENT_DIGIT, /* M-3 */
+ /* 180 */ ED_ARGUMENT_DIGIT, /* M-4 */
+ /* 181 */ ED_ARGUMENT_DIGIT, /* M-5 */
+ /* 182 */ ED_ARGUMENT_DIGIT, /* M-6 */
+ /* 183 */ ED_ARGUMENT_DIGIT, /* M-7 */
+ /* 184 */ ED_ARGUMENT_DIGIT, /* M-8 */
+ /* 185 */ ED_ARGUMENT_DIGIT, /* M-9 */
+ /* 186 */ ED_UNASSIGNED, /* M-: */
+ /* 187 */ ED_UNASSIGNED, /* M-; */
+ /* 188 */ ED_UNASSIGNED, /* M-< */
+ /* 189 */ ED_UNASSIGNED, /* M-= */
+ /* 190 */ ED_UNASSIGNED, /* M-> */
+ /* 191 */ ED_UNASSIGNED, /* M-? */
+ /* 192 */ ED_UNASSIGNED, /* M-@ */
+ /* 193 */ ED_UNASSIGNED, /* M-A */
+ /* 194 */ ED_PREV_WORD, /* M-B */
+ /* 195 */ EM_CAPITOL_CASE, /* M-C */
+ /* 196 */ EM_DELETE_NEXT_WORD, /* M-D */
+ /* 197 */ ED_UNASSIGNED, /* M-E */
+ /* 198 */ EM_NEXT_WORD, /* M-F */
+ /* 199 */ ED_UNASSIGNED, /* M-G */
+ /* 200 */ ED_UNASSIGNED, /* M-H */
+ /* 201 */ ED_UNASSIGNED, /* M-I */
+ /* 202 */ ED_UNASSIGNED, /* M-J */
+ /* 203 */ ED_UNASSIGNED, /* M-K */
+ /* 204 */ EM_LOWER_CASE, /* M-L */
+ /* 205 */ ED_UNASSIGNED, /* M-M */
+ /* 206 */ ED_SEARCH_NEXT_HISTORY, /* M-N */
+ /* 207 */ ED_SEQUENCE_LEAD_IN, /* M-O */
+ /* 208 */ ED_SEARCH_PREV_HISTORY, /* M-P */
+ /* 209 */ ED_UNASSIGNED, /* M-Q */
+ /* 210 */ ED_UNASSIGNED, /* M-R */
+ /* 211 */ ED_UNASSIGNED, /* M-S */
+ /* 212 */ ED_UNASSIGNED, /* M-T */
+ /* 213 */ EM_UPPER_CASE, /* M-U */
+ /* 214 */ ED_UNASSIGNED, /* M-V */
+ /* 215 */ EM_COPY_REGION, /* M-W */
+ /* 216 */ ED_COMMAND, /* M-X */
+ /* 217 */ ED_UNASSIGNED, /* M-Y */
+ /* 218 */ ED_UNASSIGNED, /* M-Z */
+ /* 219 */ ED_SEQUENCE_LEAD_IN, /* M-[ */
+ /* 220 */ ED_UNASSIGNED, /* M-\ */
+ /* 221 */ ED_UNASSIGNED, /* M-] */
+ /* 222 */ ED_UNASSIGNED, /* M-^ */
+ /* 223 */ ED_UNASSIGNED, /* M-_ */
+ /* 223 */ ED_UNASSIGNED, /* M-` */
+ /* 224 */ ED_UNASSIGNED, /* M-a */
+ /* 225 */ ED_PREV_WORD, /* M-b */
+ /* 226 */ EM_CAPITOL_CASE, /* M-c */
+ /* 227 */ EM_DELETE_NEXT_WORD, /* M-d */
+ /* 228 */ ED_UNASSIGNED, /* M-e */
+ /* 229 */ EM_NEXT_WORD, /* M-f */
+ /* 230 */ ED_UNASSIGNED, /* M-g */
+ /* 231 */ ED_UNASSIGNED, /* M-h */
+ /* 232 */ ED_UNASSIGNED, /* M-i */
+ /* 233 */ ED_UNASSIGNED, /* M-j */
+ /* 234 */ ED_UNASSIGNED, /* M-k */
+ /* 235 */ EM_LOWER_CASE, /* M-l */
+ /* 236 */ ED_UNASSIGNED, /* M-m */
+ /* 237 */ ED_SEARCH_NEXT_HISTORY, /* M-n */
+ /* 238 */ ED_UNASSIGNED, /* M-o */
+ /* 239 */ ED_SEARCH_PREV_HISTORY, /* M-p */
+ /* 240 */ ED_UNASSIGNED, /* M-q */
+ /* 241 */ ED_UNASSIGNED, /* M-r */
+ /* 242 */ ED_UNASSIGNED, /* M-s */
+ /* 243 */ ED_UNASSIGNED, /* M-t */
+ /* 244 */ EM_UPPER_CASE, /* M-u */
+ /* 245 */ ED_UNASSIGNED, /* M-v */
+ /* 246 */ EM_COPY_REGION, /* M-w */
+ /* 247 */ ED_COMMAND, /* M-x */
+ /* 248 */ ED_UNASSIGNED, /* M-y */
+ /* 249 */ ED_UNASSIGNED, /* M-z */
+ /* 250 */ ED_UNASSIGNED, /* M-{ */
+ /* 251 */ ED_UNASSIGNED, /* M-| */
+ /* 252 */ ED_UNASSIGNED, /* M-} */
+ /* 253 */ ED_UNASSIGNED, /* M-~ */
+ /* 254 */ ED_DELETE_PREV_WORD /* M-^? */
+ /* 255 */
+};
+
+
+/*
+ * keymap table for vi. Each index into above tbl; should be
+ * N_KEYS entries long. Vi mode uses a sticky-extend to do command mode:
+ * insert mode characters are in the normal keymap, and command mode
+ * in the extended keymap.
+ */
+private const el_action_t el_map_vi_insert[] = {
+#ifdef KSHVI
+ /* 0 */ ED_UNASSIGNED, /* ^@ */
+ /* 1 */ ED_INSERT, /* ^A */
+ /* 2 */ ED_INSERT, /* ^B */
+ /* 3 */ ED_INSERT, /* ^C */
+ /* 4 */ VI_LIST_OR_EOF, /* ^D */
+ /* 5 */ ED_INSERT, /* ^E */
+ /* 6 */ ED_INSERT, /* ^F */
+ /* 7 */ ED_INSERT, /* ^G */
+ /* 8 */ VI_DELETE_PREV_CHAR, /* ^H */ /* BackSpace key */
+ /* 9 */ ED_INSERT, /* ^I */ /* Tab Key */
+ /* 10 */ ED_NEWLINE, /* ^J */
+ /* 11 */ ED_INSERT, /* ^K */
+ /* 12 */ ED_INSERT, /* ^L */
+ /* 13 */ ED_NEWLINE, /* ^M */
+ /* 14 */ ED_INSERT, /* ^N */
+ /* 15 */ ED_INSERT, /* ^O */
+ /* 16 */ ED_INSERT, /* ^P */
+ /* 17 */ ED_TTY_START_OUTPUT, /* ^Q */
+ /* 18 */ ED_INSERT, /* ^R */
+ /* 19 */ ED_TTY_STOP_OUTPUT, /* ^S */
+ /* 20 */ ED_INSERT, /* ^T */
+ /* 21 */ VI_KILL_LINE_PREV, /* ^U */
+ /* 22 */ ED_QUOTED_INSERT, /* ^V */
+ /* 23 */ ED_DELETE_PREV_WORD, /* ^W */
+ /* ED_DELETE_PREV_WORD: Only until strt edit pos */
+ /* 24 */ ED_INSERT, /* ^X */
+ /* 25 */ ED_INSERT, /* ^Y */
+ /* 26 */ ED_INSERT, /* ^Z */
+ /* 27 */ VI_COMMAND_MODE, /* ^[ */ /* [ Esc ] key */
+ /* 28 */ ED_TTY_SIGQUIT, /* ^\ */
+ /* 29 */ ED_INSERT, /* ^] */
+ /* 30 */ ED_INSERT, /* ^^ */
+ /* 31 */ ED_INSERT, /* ^_ */
+#else /* !KSHVI */
+ /*
+ * NOTE: These mappings do NOT Correspond well
+ * to the KSH VI editing assignments.
+ * On the other and they are convenient and
+ * many people have have gotten used to them.
+ */
+ /* 0 */ ED_UNASSIGNED, /* ^@ */
+ /* 1 */ ED_MOVE_TO_BEG, /* ^A */
+ /* 2 */ ED_PREV_CHAR, /* ^B */
+ /* 3 */ ED_TTY_SIGINT, /* ^C */
+ /* 4 */ VI_LIST_OR_EOF, /* ^D */
+ /* 5 */ ED_MOVE_TO_END, /* ^E */
+ /* 6 */ ED_NEXT_CHAR, /* ^F */
+ /* 7 */ ED_UNASSIGNED, /* ^G */
+ /* 8 */ ED_DELETE_PREV_CHAR, /* ^H */ /* BackSpace key */
+ /* 9 */ ED_UNASSIGNED, /* ^I */ /* Tab Key */
+ /* 10 */ ED_NEWLINE, /* ^J */
+ /* 11 */ ED_KILL_LINE, /* ^K */
+ /* 12 */ ED_CLEAR_SCREEN, /* ^L */
+ /* 13 */ ED_NEWLINE, /* ^M */
+ /* 14 */ ED_NEXT_HISTORY, /* ^N */
+ /* 15 */ ED_TTY_FLUSH_OUTPUT, /* ^O */
+ /* 16 */ ED_PREV_HISTORY, /* ^P */
+ /* 17 */ ED_TTY_START_OUTPUT, /* ^Q */
+ /* 18 */ ED_REDISPLAY, /* ^R */
+ /* 19 */ ED_TTY_STOP_OUTPUT, /* ^S */
+ /* 20 */ ED_TRANSPOSE_CHARS, /* ^T */
+ /* 21 */ VI_KILL_LINE_PREV, /* ^U */
+ /* 22 */ ED_QUOTED_INSERT, /* ^V */
+ /* 23 */ ED_DELETE_PREV_WORD, /* ^W */
+ /* 24 */ ED_UNASSIGNED, /* ^X */
+ /* 25 */ ED_TTY_DSUSP, /* ^Y */
+ /* 26 */ ED_TTY_SIGTSTP, /* ^Z */
+ /* 27 */ VI_COMMAND_MODE, /* ^[ */
+ /* 28 */ ED_TTY_SIGQUIT, /* ^\ */
+ /* 29 */ ED_UNASSIGNED, /* ^] */
+ /* 30 */ ED_UNASSIGNED, /* ^^ */
+ /* 31 */ ED_UNASSIGNED, /* ^_ */
+#endif /* KSHVI */
+ /* 32 */ ED_INSERT, /* SPACE */
+ /* 33 */ ED_INSERT, /* ! */
+ /* 34 */ ED_INSERT, /* " */
+ /* 35 */ ED_INSERT, /* # */
+ /* 36 */ ED_INSERT, /* $ */
+ /* 37 */ ED_INSERT, /* % */
+ /* 38 */ ED_INSERT, /* & */
+ /* 39 */ ED_INSERT, /* ' */
+ /* 40 */ ED_INSERT, /* ( */
+ /* 41 */ ED_INSERT, /* ) */
+ /* 42 */ ED_INSERT, /* * */
+ /* 43 */ ED_INSERT, /* + */
+ /* 44 */ ED_INSERT, /* , */
+ /* 45 */ ED_INSERT, /* - */
+ /* 46 */ ED_INSERT, /* . */
+ /* 47 */ ED_INSERT, /* / */
+ /* 48 */ ED_INSERT, /* 0 */
+ /* 49 */ ED_INSERT, /* 1 */
+ /* 50 */ ED_INSERT, /* 2 */
+ /* 51 */ ED_INSERT, /* 3 */
+ /* 52 */ ED_INSERT, /* 4 */
+ /* 53 */ ED_INSERT, /* 5 */
+ /* 54 */ ED_INSERT, /* 6 */
+ /* 55 */ ED_INSERT, /* 7 */
+ /* 56 */ ED_INSERT, /* 8 */
+ /* 57 */ ED_INSERT, /* 9 */
+ /* 58 */ ED_INSERT, /* : */
+ /* 59 */ ED_INSERT, /* ; */
+ /* 60 */ ED_INSERT, /* < */
+ /* 61 */ ED_INSERT, /* = */
+ /* 62 */ ED_INSERT, /* > */
+ /* 63 */ ED_INSERT, /* ? */
+ /* 64 */ ED_INSERT, /* @ */
+ /* 65 */ ED_INSERT, /* A */
+ /* 66 */ ED_INSERT, /* B */
+ /* 67 */ ED_INSERT, /* C */
+ /* 68 */ ED_INSERT, /* D */
+ /* 69 */ ED_INSERT, /* E */
+ /* 70 */ ED_INSERT, /* F */
+ /* 71 */ ED_INSERT, /* G */
+ /* 72 */ ED_INSERT, /* H */
+ /* 73 */ ED_INSERT, /* I */
+ /* 74 */ ED_INSERT, /* J */
+ /* 75 */ ED_INSERT, /* K */
+ /* 76 */ ED_INSERT, /* L */
+ /* 77 */ ED_INSERT, /* M */
+ /* 78 */ ED_INSERT, /* N */
+ /* 79 */ ED_INSERT, /* O */
+ /* 80 */ ED_INSERT, /* P */
+ /* 81 */ ED_INSERT, /* Q */
+ /* 82 */ ED_INSERT, /* R */
+ /* 83 */ ED_INSERT, /* S */
+ /* 84 */ ED_INSERT, /* T */
+ /* 85 */ ED_INSERT, /* U */
+ /* 86 */ ED_INSERT, /* V */
+ /* 87 */ ED_INSERT, /* W */
+ /* 88 */ ED_INSERT, /* X */
+ /* 89 */ ED_INSERT, /* Y */
+ /* 90 */ ED_INSERT, /* Z */
+ /* 91 */ ED_INSERT, /* [ */
+ /* 92 */ ED_INSERT, /* \ */
+ /* 93 */ ED_INSERT, /* ] */
+ /* 94 */ ED_INSERT, /* ^ */
+ /* 95 */ ED_INSERT, /* _ */
+ /* 96 */ ED_INSERT, /* ` */
+ /* 97 */ ED_INSERT, /* a */
+ /* 98 */ ED_INSERT, /* b */
+ /* 99 */ ED_INSERT, /* c */
+ /* 100 */ ED_INSERT, /* d */
+ /* 101 */ ED_INSERT, /* e */
+ /* 102 */ ED_INSERT, /* f */
+ /* 103 */ ED_INSERT, /* g */
+ /* 104 */ ED_INSERT, /* h */
+ /* 105 */ ED_INSERT, /* i */
+ /* 106 */ ED_INSERT, /* j */
+ /* 107 */ ED_INSERT, /* k */
+ /* 108 */ ED_INSERT, /* l */
+ /* 109 */ ED_INSERT, /* m */
+ /* 110 */ ED_INSERT, /* n */
+ /* 111 */ ED_INSERT, /* o */
+ /* 112 */ ED_INSERT, /* p */
+ /* 113 */ ED_INSERT, /* q */
+ /* 114 */ ED_INSERT, /* r */
+ /* 115 */ ED_INSERT, /* s */
+ /* 116 */ ED_INSERT, /* t */
+ /* 117 */ ED_INSERT, /* u */
+ /* 118 */ ED_INSERT, /* v */
+ /* 119 */ ED_INSERT, /* w */
+ /* 120 */ ED_INSERT, /* x */
+ /* 121 */ ED_INSERT, /* y */
+ /* 122 */ ED_INSERT, /* z */
+ /* 123 */ ED_INSERT, /* { */
+ /* 124 */ ED_INSERT, /* | */
+ /* 125 */ ED_INSERT, /* } */
+ /* 126 */ ED_INSERT, /* ~ */
+ /* 127 */ ED_DELETE_PREV_CHAR, /* ^? */
+ /* 128 */ ED_UNASSIGNED, /* M-^@ */
+ /* 129 */ ED_UNASSIGNED, /* M-^A */
+ /* 130 */ ED_UNASSIGNED, /* M-^B */
+ /* 131 */ ED_UNASSIGNED, /* M-^C */
+ /* 132 */ ED_UNASSIGNED, /* M-^D */
+ /* 133 */ ED_UNASSIGNED, /* M-^E */
+ /* 134 */ ED_UNASSIGNED, /* M-^F */
+ /* 135 */ ED_UNASSIGNED, /* M-^G */
+ /* 136 */ ED_UNASSIGNED, /* M-^H */
+ /* 137 */ ED_UNASSIGNED, /* M-^I */
+ /* 138 */ ED_UNASSIGNED, /* M-^J */
+ /* 139 */ ED_UNASSIGNED, /* M-^K */
+ /* 140 */ ED_UNASSIGNED, /* M-^L */
+ /* 141 */ ED_UNASSIGNED, /* M-^M */
+ /* 142 */ ED_UNASSIGNED, /* M-^N */
+ /* 143 */ ED_UNASSIGNED, /* M-^O */
+ /* 144 */ ED_UNASSIGNED, /* M-^P */
+ /* 145 */ ED_UNASSIGNED, /* M-^Q */
+ /* 146 */ ED_UNASSIGNED, /* M-^R */
+ /* 147 */ ED_UNASSIGNED, /* M-^S */
+ /* 148 */ ED_UNASSIGNED, /* M-^T */
+ /* 149 */ ED_UNASSIGNED, /* M-^U */
+ /* 150 */ ED_UNASSIGNED, /* M-^V */
+ /* 151 */ ED_UNASSIGNED, /* M-^W */
+ /* 152 */ ED_UNASSIGNED, /* M-^X */
+ /* 153 */ ED_UNASSIGNED, /* M-^Y */
+ /* 154 */ ED_UNASSIGNED, /* M-^Z */
+ /* 155 */ ED_UNASSIGNED, /* M-^[ */
+ /* 156 */ ED_UNASSIGNED, /* M-^\ */
+ /* 157 */ ED_UNASSIGNED, /* M-^] */
+ /* 158 */ ED_UNASSIGNED, /* M-^^ */
+ /* 159 */ ED_UNASSIGNED, /* M-^_ */
+ /* 160 */ ED_UNASSIGNED, /* M-SPACE */
+ /* 161 */ ED_UNASSIGNED, /* M-! */
+ /* 162 */ ED_UNASSIGNED, /* M-" */
+ /* 163 */ ED_UNASSIGNED, /* M-# */
+ /* 164 */ ED_UNASSIGNED, /* M-$ */
+ /* 165 */ ED_UNASSIGNED, /* M-% */
+ /* 166 */ ED_UNASSIGNED, /* M-& */
+ /* 167 */ ED_UNASSIGNED, /* M-' */
+ /* 168 */ ED_UNASSIGNED, /* M-( */
+ /* 169 */ ED_UNASSIGNED, /* M-) */
+ /* 170 */ ED_UNASSIGNED, /* M-* */
+ /* 171 */ ED_UNASSIGNED, /* M-+ */
+ /* 172 */ ED_UNASSIGNED, /* M-, */
+ /* 173 */ ED_UNASSIGNED, /* M-- */
+ /* 174 */ ED_UNASSIGNED, /* M-. */
+ /* 175 */ ED_UNASSIGNED, /* M-/ */
+ /* 176 */ ED_UNASSIGNED, /* M-0 */
+ /* 177 */ ED_UNASSIGNED, /* M-1 */
+ /* 178 */ ED_UNASSIGNED, /* M-2 */
+ /* 179 */ ED_UNASSIGNED, /* M-3 */
+ /* 180 */ ED_UNASSIGNED, /* M-4 */
+ /* 181 */ ED_UNASSIGNED, /* M-5 */
+ /* 182 */ ED_UNASSIGNED, /* M-6 */
+ /* 183 */ ED_UNASSIGNED, /* M-7 */
+ /* 184 */ ED_UNASSIGNED, /* M-8 */
+ /* 185 */ ED_UNASSIGNED, /* M-9 */
+ /* 186 */ ED_UNASSIGNED, /* M-: */
+ /* 187 */ ED_UNASSIGNED, /* M-; */
+ /* 188 */ ED_UNASSIGNED, /* M-< */
+ /* 189 */ ED_UNASSIGNED, /* M-= */
+ /* 190 */ ED_UNASSIGNED, /* M-> */
+ /* 191 */ ED_UNASSIGNED, /* M-? */
+ /* 192 */ ED_UNASSIGNED, /* M-@ */
+ /* 193 */ ED_UNASSIGNED, /* M-A */
+ /* 194 */ ED_UNASSIGNED, /* M-B */
+ /* 195 */ ED_UNASSIGNED, /* M-C */
+ /* 196 */ ED_UNASSIGNED, /* M-D */
+ /* 197 */ ED_UNASSIGNED, /* M-E */
+ /* 198 */ ED_UNASSIGNED, /* M-F */
+ /* 199 */ ED_UNASSIGNED, /* M-G */
+ /* 200 */ ED_UNASSIGNED, /* M-H */
+ /* 201 */ ED_UNASSIGNED, /* M-I */
+ /* 202 */ ED_UNASSIGNED, /* M-J */
+ /* 203 */ ED_UNASSIGNED, /* M-K */
+ /* 204 */ ED_UNASSIGNED, /* M-L */
+ /* 205 */ ED_UNASSIGNED, /* M-M */
+ /* 206 */ ED_UNASSIGNED, /* M-N */
+ /* 207 */ ED_UNASSIGNED, /* M-O */
+ /* 208 */ ED_UNASSIGNED, /* M-P */
+ /* 209 */ ED_UNASSIGNED, /* M-Q */
+ /* 210 */ ED_UNASSIGNED, /* M-R */
+ /* 211 */ ED_UNASSIGNED, /* M-S */
+ /* 212 */ ED_UNASSIGNED, /* M-T */
+ /* 213 */ ED_UNASSIGNED, /* M-U */
+ /* 214 */ ED_UNASSIGNED, /* M-V */
+ /* 215 */ ED_UNASSIGNED, /* M-W */
+ /* 216 */ ED_UNASSIGNED, /* M-X */
+ /* 217 */ ED_UNASSIGNED, /* M-Y */
+ /* 218 */ ED_UNASSIGNED, /* M-Z */
+ /* 219 */ ED_UNASSIGNED, /* M-[ */
+ /* 220 */ ED_UNASSIGNED, /* M-\ */
+ /* 221 */ ED_UNASSIGNED, /* M-] */
+ /* 222 */ ED_UNASSIGNED, /* M-^ */
+ /* 223 */ ED_UNASSIGNED, /* M-_ */
+ /* 224 */ ED_UNASSIGNED, /* M-` */
+ /* 225 */ ED_UNASSIGNED, /* M-a */
+ /* 226 */ ED_UNASSIGNED, /* M-b */
+ /* 227 */ ED_UNASSIGNED, /* M-c */
+ /* 228 */ ED_UNASSIGNED, /* M-d */
+ /* 229 */ ED_UNASSIGNED, /* M-e */
+ /* 230 */ ED_UNASSIGNED, /* M-f */
+ /* 231 */ ED_UNASSIGNED, /* M-g */
+ /* 232 */ ED_UNASSIGNED, /* M-h */
+ /* 233 */ ED_UNASSIGNED, /* M-i */
+ /* 234 */ ED_UNASSIGNED, /* M-j */
+ /* 235 */ ED_UNASSIGNED, /* M-k */
+ /* 236 */ ED_UNASSIGNED, /* M-l */
+ /* 237 */ ED_UNASSIGNED, /* M-m */
+ /* 238 */ ED_UNASSIGNED, /* M-n */
+ /* 239 */ ED_UNASSIGNED, /* M-o */
+ /* 240 */ ED_UNASSIGNED, /* M-p */
+ /* 241 */ ED_UNASSIGNED, /* M-q */
+ /* 242 */ ED_UNASSIGNED, /* M-r */
+ /* 243 */ ED_UNASSIGNED, /* M-s */
+ /* 244 */ ED_UNASSIGNED, /* M-t */
+ /* 245 */ ED_UNASSIGNED, /* M-u */
+ /* 246 */ ED_UNASSIGNED, /* M-v */
+ /* 247 */ ED_UNASSIGNED, /* M-w */
+ /* 248 */ ED_UNASSIGNED, /* M-x */
+ /* 249 */ ED_UNASSIGNED, /* M-y */
+ /* 250 */ ED_UNASSIGNED, /* M-z */
+ /* 251 */ ED_UNASSIGNED, /* M-{ */
+ /* 252 */ ED_UNASSIGNED, /* M-| */
+ /* 253 */ ED_UNASSIGNED, /* M-} */
+ /* 254 */ ED_UNASSIGNED, /* M-~ */
+ /* 255 */ ED_UNASSIGNED /* M-^? */
+};
+
+private const el_action_t el_map_vi_command[] = {
+ /* 0 */ ED_UNASSIGNED, /* ^@ */
+ /* 1 */ ED_MOVE_TO_BEG, /* ^A */
+ /* 2 */ ED_UNASSIGNED, /* ^B */
+ /* 3 */ ED_TTY_SIGINT, /* ^C */
+ /* 4 */ ED_UNASSIGNED, /* ^D */
+ /* 5 */ ED_MOVE_TO_END, /* ^E */
+ /* 6 */ ED_UNASSIGNED, /* ^F */
+ /* 7 */ ED_UNASSIGNED, /* ^G */
+ /* 8 */ ED_PREV_CHAR, /* ^H */
+ /* 9 */ ED_UNASSIGNED, /* ^I */
+ /* 10 */ ED_NEWLINE, /* ^J */
+ /* 11 */ ED_KILL_LINE, /* ^K */
+ /* 12 */ ED_CLEAR_SCREEN, /* ^L */
+ /* 13 */ ED_NEWLINE, /* ^M */
+ /* 14 */ ED_NEXT_HISTORY, /* ^N */
+ /* 15 */ ED_TTY_FLUSH_OUTPUT, /* ^O */
+ /* 16 */ ED_PREV_HISTORY, /* ^P */
+ /* 17 */ ED_TTY_START_OUTPUT, /* ^Q */
+ /* 18 */ ED_REDISPLAY, /* ^R */
+ /* 19 */ ED_TTY_STOP_OUTPUT, /* ^S */
+ /* 20 */ ED_UNASSIGNED, /* ^T */
+ /* 21 */ VI_KILL_LINE_PREV, /* ^U */
+ /* 22 */ ED_UNASSIGNED, /* ^V */
+ /* 23 */ ED_DELETE_PREV_WORD, /* ^W */
+ /* 24 */ ED_UNASSIGNED, /* ^X */
+ /* 25 */ ED_UNASSIGNED, /* ^Y */
+ /* 26 */ ED_UNASSIGNED, /* ^Z */
+ /* 27 */ EM_META_NEXT, /* ^[ */
+ /* 28 */ ED_TTY_SIGQUIT, /* ^\ */
+ /* 29 */ ED_UNASSIGNED, /* ^] */
+ /* 30 */ ED_UNASSIGNED, /* ^^ */
+ /* 31 */ ED_UNASSIGNED, /* ^_ */
+ /* 32 */ ED_NEXT_CHAR, /* SPACE */
+ /* 33 */ ED_UNASSIGNED, /* ! */
+ /* 34 */ ED_UNASSIGNED, /* " */
+ /* 35 */ ED_UNASSIGNED, /* # */
+ /* 36 */ ED_MOVE_TO_END, /* $ */
+ /* 37 */ ED_UNASSIGNED, /* % */
+ /* 38 */ ED_UNASSIGNED, /* & */
+ /* 39 */ ED_UNASSIGNED, /* ' */
+ /* 40 */ ED_UNASSIGNED, /* ( */
+ /* 41 */ ED_UNASSIGNED, /* ) */
+ /* 42 */ ED_UNASSIGNED, /* * */
+ /* 43 */ ED_NEXT_HISTORY, /* + */
+ /* 44 */ VI_REPEAT_PREV_CHAR, /* , */
+ /* 45 */ ED_PREV_HISTORY, /* - */
+ /* 46 */ ED_UNASSIGNED, /* . */
+ /* 47 */ VI_SEARCH_PREV, /* / */
+ /* 48 */ VI_ZERO, /* 0 */
+ /* 49 */ ED_ARGUMENT_DIGIT, /* 1 */
+ /* 50 */ ED_ARGUMENT_DIGIT, /* 2 */
+ /* 51 */ ED_ARGUMENT_DIGIT, /* 3 */
+ /* 52 */ ED_ARGUMENT_DIGIT, /* 4 */
+ /* 53 */ ED_ARGUMENT_DIGIT, /* 5 */
+ /* 54 */ ED_ARGUMENT_DIGIT, /* 6 */
+ /* 55 */ ED_ARGUMENT_DIGIT, /* 7 */
+ /* 56 */ ED_ARGUMENT_DIGIT, /* 8 */
+ /* 57 */ ED_ARGUMENT_DIGIT, /* 9 */
+ /* 58 */ ED_COMMAND, /* : */
+ /* 59 */ VI_REPEAT_NEXT_CHAR, /* ; */
+ /* 60 */ ED_UNASSIGNED, /* < */
+ /* 61 */ ED_UNASSIGNED, /* = */
+ /* 62 */ ED_UNASSIGNED, /* > */
+ /* 63 */ VI_SEARCH_NEXT, /* ? */
+ /* 64 */ ED_UNASSIGNED, /* @ */
+ /* 65 */ VI_ADD_AT_EOL, /* A */
+ /* 66 */ VI_PREV_SPACE_WORD, /* B */
+ /* 67 */ VI_CHANGE_TO_EOL, /* C */
+ /* 68 */ ED_KILL_LINE, /* D */
+ /* 69 */ VI_TO_END_WORD, /* E */
+ /* 70 */ VI_PREV_CHAR, /* F */
+ /* 71 */ ED_UNASSIGNED, /* G */
+ /* 72 */ ED_UNASSIGNED, /* H */
+ /* 73 */ VI_INSERT_AT_BOL, /* I */
+ /* 74 */ ED_SEARCH_NEXT_HISTORY, /* J */
+ /* 75 */ ED_SEARCH_PREV_HISTORY, /* K */
+ /* 76 */ ED_UNASSIGNED, /* L */
+ /* 77 */ ED_UNASSIGNED, /* M */
+ /* 78 */ VI_REPEAT_SEARCH_PREV, /* N */
+ /* 79 */ ED_SEQUENCE_LEAD_IN, /* O */
+ /* 80 */ VI_PASTE_PREV, /* P */
+ /* 81 */ ED_UNASSIGNED, /* Q */
+ /* 82 */ VI_REPLACE_MODE, /* R */
+ /* 83 */ VI_SUBSTITUTE_LINE, /* S */
+ /* 84 */ VI_TO_PREV_CHAR, /* T */
+ /* 85 */ ED_UNASSIGNED, /* U */
+ /* 86 */ ED_UNASSIGNED, /* V */
+ /* 87 */ VI_NEXT_SPACE_WORD, /* W */
+ /* 88 */ ED_DELETE_PREV_CHAR, /* X */
+ /* 89 */ ED_UNASSIGNED, /* Y */
+ /* 90 */ ED_UNASSIGNED, /* Z */
+ /* 91 */ ED_SEQUENCE_LEAD_IN, /* [ */
+ /* 92 */ ED_UNASSIGNED, /* \ */
+ /* 93 */ ED_UNASSIGNED, /* ] */
+ /* 94 */ ED_MOVE_TO_BEG, /* ^ */
+ /* 95 */ ED_UNASSIGNED, /* _ */
+ /* 96 */ ED_UNASSIGNED, /* ` */
+ /* 97 */ VI_ADD, /* a */
+ /* 98 */ VI_PREV_WORD, /* b */
+ /* 99 */ VI_CHANGE_META, /* c */
+ /* 100 */ VI_DELETE_META, /* d */
+ /* 101 */ VI_END_WORD, /* e */
+ /* 102 */ VI_NEXT_CHAR, /* f */
+ /* 103 */ ED_UNASSIGNED, /* g */
+ /* 104 */ ED_PREV_CHAR, /* h */
+ /* 105 */ VI_INSERT, /* i */
+ /* 106 */ ED_NEXT_HISTORY, /* j */
+ /* 107 */ ED_PREV_HISTORY, /* k */
+ /* 108 */ ED_NEXT_CHAR, /* l */
+ /* 109 */ ED_UNASSIGNED, /* m */
+ /* 110 */ VI_REPEAT_SEARCH_NEXT, /* n */
+ /* 111 */ ED_UNASSIGNED, /* o */
+ /* 112 */ VI_PASTE_NEXT, /* p */
+ /* 113 */ ED_UNASSIGNED, /* q */
+ /* 114 */ VI_REPLACE_CHAR, /* r */
+ /* 115 */ VI_SUBSTITUTE_CHAR, /* s */
+ /* 116 */ VI_TO_NEXT_CHAR, /* t */
+ /* 117 */ VI_UNDO, /* u */
+ /* 118 */ ED_UNASSIGNED, /* v */
+ /* 119 */ VI_NEXT_WORD, /* w */
+ /* 120 */ ED_DELETE_NEXT_CHAR, /* x */
+ /* 121 */ ED_UNASSIGNED, /* y */
+ /* 122 */ ED_UNASSIGNED, /* z */
+ /* 123 */ ED_UNASSIGNED, /* { */
+ /* 124 */ ED_UNASSIGNED, /* | */
+ /* 125 */ ED_UNASSIGNED, /* } */
+ /* 126 */ VI_CHANGE_CASE, /* ~ */
+ /* 127 */ ED_DELETE_PREV_CHAR, /* ^? */
+ /* 128 */ ED_UNASSIGNED, /* M-^@ */
+ /* 129 */ ED_UNASSIGNED, /* M-^A */
+ /* 130 */ ED_UNASSIGNED, /* M-^B */
+ /* 131 */ ED_UNASSIGNED, /* M-^C */
+ /* 132 */ ED_UNASSIGNED, /* M-^D */
+ /* 133 */ ED_UNASSIGNED, /* M-^E */
+ /* 134 */ ED_UNASSIGNED, /* M-^F */
+ /* 135 */ ED_UNASSIGNED, /* M-^G */
+ /* 136 */ ED_UNASSIGNED, /* M-^H */
+ /* 137 */ ED_UNASSIGNED, /* M-^I */
+ /* 138 */ ED_UNASSIGNED, /* M-^J */
+ /* 139 */ ED_UNASSIGNED, /* M-^K */
+ /* 140 */ ED_UNASSIGNED, /* M-^L */
+ /* 141 */ ED_UNASSIGNED, /* M-^M */
+ /* 142 */ ED_UNASSIGNED, /* M-^N */
+ /* 143 */ ED_UNASSIGNED, /* M-^O */
+ /* 144 */ ED_UNASSIGNED, /* M-^P */
+ /* 145 */ ED_UNASSIGNED, /* M-^Q */
+ /* 146 */ ED_UNASSIGNED, /* M-^R */
+ /* 147 */ ED_UNASSIGNED, /* M-^S */
+ /* 148 */ ED_UNASSIGNED, /* M-^T */
+ /* 149 */ ED_UNASSIGNED, /* M-^U */
+ /* 150 */ ED_UNASSIGNED, /* M-^V */
+ /* 151 */ ED_UNASSIGNED, /* M-^W */
+ /* 152 */ ED_UNASSIGNED, /* M-^X */
+ /* 153 */ ED_UNASSIGNED, /* M-^Y */
+ /* 154 */ ED_UNASSIGNED, /* M-^Z */
+ /* 155 */ ED_UNASSIGNED, /* M-^[ */
+ /* 156 */ ED_UNASSIGNED, /* M-^\ */
+ /* 157 */ ED_UNASSIGNED, /* M-^] */
+ /* 158 */ ED_UNASSIGNED, /* M-^^ */
+ /* 159 */ ED_UNASSIGNED, /* M-^_ */
+ /* 160 */ ED_UNASSIGNED, /* M-SPACE */
+ /* 161 */ ED_UNASSIGNED, /* M-! */
+ /* 162 */ ED_UNASSIGNED, /* M-" */
+ /* 163 */ ED_UNASSIGNED, /* M-# */
+ /* 164 */ ED_UNASSIGNED, /* M-$ */
+ /* 165 */ ED_UNASSIGNED, /* M-% */
+ /* 166 */ ED_UNASSIGNED, /* M-& */
+ /* 167 */ ED_UNASSIGNED, /* M-' */
+ /* 168 */ ED_UNASSIGNED, /* M-( */
+ /* 169 */ ED_UNASSIGNED, /* M-) */
+ /* 170 */ ED_UNASSIGNED, /* M-* */
+ /* 171 */ ED_UNASSIGNED, /* M-+ */
+ /* 172 */ ED_UNASSIGNED, /* M-, */
+ /* 173 */ ED_UNASSIGNED, /* M-- */
+ /* 174 */ ED_UNASSIGNED, /* M-. */
+ /* 175 */ ED_UNASSIGNED, /* M-/ */
+ /* 176 */ ED_UNASSIGNED, /* M-0 */
+ /* 177 */ ED_UNASSIGNED, /* M-1 */
+ /* 178 */ ED_UNASSIGNED, /* M-2 */
+ /* 179 */ ED_UNASSIGNED, /* M-3 */
+ /* 180 */ ED_UNASSIGNED, /* M-4 */
+ /* 181 */ ED_UNASSIGNED, /* M-5 */
+ /* 182 */ ED_UNASSIGNED, /* M-6 */
+ /* 183 */ ED_UNASSIGNED, /* M-7 */
+ /* 184 */ ED_UNASSIGNED, /* M-8 */
+ /* 185 */ ED_UNASSIGNED, /* M-9 */
+ /* 186 */ ED_UNASSIGNED, /* M-: */
+ /* 187 */ ED_UNASSIGNED, /* M-; */
+ /* 188 */ ED_UNASSIGNED, /* M-< */
+ /* 189 */ ED_UNASSIGNED, /* M-= */
+ /* 190 */ ED_UNASSIGNED, /* M-> */
+ /* 191 */ ED_UNASSIGNED, /* M-? */
+ /* 192 */ ED_UNASSIGNED, /* M-@ */
+ /* 193 */ ED_UNASSIGNED, /* M-A */
+ /* 194 */ ED_UNASSIGNED, /* M-B */
+ /* 195 */ ED_UNASSIGNED, /* M-C */
+ /* 196 */ ED_UNASSIGNED, /* M-D */
+ /* 197 */ ED_UNASSIGNED, /* M-E */
+ /* 198 */ ED_UNASSIGNED, /* M-F */
+ /* 199 */ ED_UNASSIGNED, /* M-G */
+ /* 200 */ ED_UNASSIGNED, /* M-H */
+ /* 201 */ ED_UNASSIGNED, /* M-I */
+ /* 202 */ ED_UNASSIGNED, /* M-J */
+ /* 203 */ ED_UNASSIGNED, /* M-K */
+ /* 204 */ ED_UNASSIGNED, /* M-L */
+ /* 205 */ ED_UNASSIGNED, /* M-M */
+ /* 206 */ ED_UNASSIGNED, /* M-N */
+ /* 207 */ ED_SEQUENCE_LEAD_IN, /* M-O */
+ /* 208 */ ED_UNASSIGNED, /* M-P */
+ /* 209 */ ED_UNASSIGNED, /* M-Q */
+ /* 210 */ ED_UNASSIGNED, /* M-R */
+ /* 211 */ ED_UNASSIGNED, /* M-S */
+ /* 212 */ ED_UNASSIGNED, /* M-T */
+ /* 213 */ ED_UNASSIGNED, /* M-U */
+ /* 214 */ ED_UNASSIGNED, /* M-V */
+ /* 215 */ ED_UNASSIGNED, /* M-W */
+ /* 216 */ ED_UNASSIGNED, /* M-X */
+ /* 217 */ ED_UNASSIGNED, /* M-Y */
+ /* 218 */ ED_UNASSIGNED, /* M-Z */
+ /* 219 */ ED_SEQUENCE_LEAD_IN, /* M-[ */
+ /* 220 */ ED_UNASSIGNED, /* M-\ */
+ /* 221 */ ED_UNASSIGNED, /* M-] */
+ /* 222 */ ED_UNASSIGNED, /* M-^ */
+ /* 223 */ ED_UNASSIGNED, /* M-_ */
+ /* 224 */ ED_UNASSIGNED, /* M-` */
+ /* 225 */ ED_UNASSIGNED, /* M-a */
+ /* 226 */ ED_UNASSIGNED, /* M-b */
+ /* 227 */ ED_UNASSIGNED, /* M-c */
+ /* 228 */ ED_UNASSIGNED, /* M-d */
+ /* 229 */ ED_UNASSIGNED, /* M-e */
+ /* 230 */ ED_UNASSIGNED, /* M-f */
+ /* 231 */ ED_UNASSIGNED, /* M-g */
+ /* 232 */ ED_UNASSIGNED, /* M-h */
+ /* 233 */ ED_UNASSIGNED, /* M-i */
+ /* 234 */ ED_UNASSIGNED, /* M-j */
+ /* 235 */ ED_UNASSIGNED, /* M-k */
+ /* 236 */ ED_UNASSIGNED, /* M-l */
+ /* 237 */ ED_UNASSIGNED, /* M-m */
+ /* 238 */ ED_UNASSIGNED, /* M-n */
+ /* 239 */ ED_UNASSIGNED, /* M-o */
+ /* 240 */ ED_UNASSIGNED, /* M-p */
+ /* 241 */ ED_UNASSIGNED, /* M-q */
+ /* 242 */ ED_UNASSIGNED, /* M-r */
+ /* 243 */ ED_UNASSIGNED, /* M-s */
+ /* 244 */ ED_UNASSIGNED, /* M-t */
+ /* 245 */ ED_UNASSIGNED, /* M-u */
+ /* 246 */ ED_UNASSIGNED, /* M-v */
+ /* 247 */ ED_UNASSIGNED, /* M-w */
+ /* 248 */ ED_UNASSIGNED, /* M-x */
+ /* 249 */ ED_UNASSIGNED, /* M-y */
+ /* 250 */ ED_UNASSIGNED, /* M-z */
+ /* 251 */ ED_UNASSIGNED, /* M-{ */
+ /* 252 */ ED_UNASSIGNED, /* M-| */
+ /* 253 */ ED_UNASSIGNED, /* M-} */
+ /* 254 */ ED_UNASSIGNED, /* M-~ */
+ /* 255 */ ED_UNASSIGNED /* M-^? */
+};
+
+
+/* map_init():
+ * Initialize and allocate the maps
+ */
+protected int
+map_init(EditLine *el)
+{
+
+ /*
+ * Make sure those are correct before starting.
+ */
+#ifdef MAP_DEBUG
+ if (sizeof(el_map_emacs) != N_KEYS * sizeof(el_action_t))
+ EL_ABORT((el->errfile, "Emacs map incorrect\n"));
+ if (sizeof(el_map_vi_command) != N_KEYS * sizeof(el_action_t))
+ EL_ABORT((el->errfile, "Vi command map incorrect\n"));
+ if (sizeof(el_map_vi_insert) != N_KEYS * sizeof(el_action_t))
+ EL_ABORT((el->errfile, "Vi insert map incorrect\n"));
+#endif
+
+ el->el_map.alt = (el_action_t *)el_malloc(sizeof(el_action_t) * N_KEYS);
+ if (el->el_map.alt == NULL)
+ return (-1);
+ el->el_map.key = (el_action_t *)el_malloc(sizeof(el_action_t) * N_KEYS);
+ if (el->el_map.key == NULL)
+ return (-1);
+ el->el_map.emacs = el_map_emacs;
+ el->el_map.vic = el_map_vi_command;
+ el->el_map.vii = el_map_vi_insert;
+ el->el_map.help = (el_bindings_t *) el_malloc(sizeof(el_bindings_t) *
+ EL_NUM_FCNS);
+ if (el->el_map.help == NULL)
+ return (-1);
+ (void) memcpy(el->el_map.help, help__get(),
+ sizeof(el_bindings_t) * EL_NUM_FCNS);
+ el->el_map.func = (el_func_t *)el_malloc(sizeof(el_func_t) *
+ EL_NUM_FCNS);
+ if (el->el_map.func == NULL)
+ return (-1);
+ memcpy(el->el_map.func, func__get(), sizeof(el_func_t) * EL_NUM_FCNS);
+ el->el_map.nfunc = EL_NUM_FCNS;
+
+#ifdef VIDEFAULT
+ map_init_vi(el);
+#else
+ map_init_emacs(el);
+#endif /* VIDEFAULT */
+ return (0);
+}
+
+
+/* map_end():
+ * Free the space taken by the editor maps
+ */
+protected void
+map_end(EditLine *el)
+{
+
+ el_free((ptr_t) el->el_map.alt);
+ el->el_map.alt = NULL;
+ el_free((ptr_t) el->el_map.key);
+ el->el_map.key = NULL;
+ el->el_map.emacs = NULL;
+ el->el_map.vic = NULL;
+ el->el_map.vii = NULL;
+ el_free((ptr_t) el->el_map.help);
+ el->el_map.help = NULL;
+ el_free((ptr_t) el->el_map.func);
+ el->el_map.func = NULL;
+}
+
+
+/* map_init_nls():
+ * Find all the printable keys and bind them to self insert
+ */
+private void
+map_init_nls(EditLine *el)
+{
+ int i;
+
+ el_action_t *map = el->el_map.key;
+
+ for (i = 0200; i <= 0377; i++)
+ if (isprint(i))
+ map[i] = ED_INSERT;
+}
+
+
+/* map_init_meta():
+ * Bind all the meta keys to the appropriate ESC-<key> sequence
+ */
+private void
+map_init_meta(EditLine *el)
+{
+ char buf[3];
+ int i;
+ el_action_t *map = el->el_map.key;
+ el_action_t *alt = el->el_map.alt;
+
+ for (i = 0; i <= 0377 && map[i] != EM_META_NEXT; i++)
+ continue;
+
+ if (i > 0377) {
+ for (i = 0; i <= 0377 && alt[i] != EM_META_NEXT; i++)
+ continue;
+ if (i > 0377) {
+ i = 033;
+ if (el->el_map.type == MAP_VI)
+ map = alt;
+ } else
+ map = alt;
+ }
+ buf[0] = (char) i;
+ buf[2] = 0;
+ for (i = 0200; i <= 0377; i++)
+ switch (map[i]) {
+ case ED_INSERT:
+ case ED_UNASSIGNED:
+ case ED_SEQUENCE_LEAD_IN:
+ break;
+ default:
+ buf[1] = i & 0177;
+ key_add(el, buf, key_map_cmd(el, (int) map[i]), XK_CMD);
+ break;
+ }
+ map[(int) buf[0]] = ED_SEQUENCE_LEAD_IN;
+}
+
+
+/* map_init_vi():
+ * Initialize the vi bindings
+ */
+protected void
+map_init_vi(EditLine *el)
+{
+ int i;
+ el_action_t *key = el->el_map.key;
+ el_action_t *alt = el->el_map.alt;
+ const el_action_t *vii = el->el_map.vii;
+ const el_action_t *vic = el->el_map.vic;
+
+ el->el_map.type = MAP_VI;
+ el->el_map.current = el->el_map.key;
+
+ key_reset(el);
+
+ for (i = 0; i < N_KEYS; i++) {
+ key[i] = vii[i];
+ alt[i] = vic[i];
+ }
+
+ map_init_meta(el);
+ map_init_nls(el);
+
+ tty_bind_char(el, 1);
+ term_bind_arrow(el);
+}
+
+
+/* map_init_emacs():
+ * Initialize the emacs bindings
+ */
+protected void
+map_init_emacs(EditLine *el)
+{
+ int i;
+ char buf[3];
+ el_action_t *key = el->el_map.key;
+ el_action_t *alt = el->el_map.alt;
+ const el_action_t *emacs = el->el_map.emacs;
+
+ el->el_map.type = MAP_EMACS;
+ el->el_map.current = el->el_map.key;
+ key_reset(el);
+
+ for (i = 0; i < N_KEYS; i++) {
+ key[i] = emacs[i];
+ alt[i] = ED_UNASSIGNED;
+ }
+
+ map_init_meta(el);
+ map_init_nls(el);
+
+ buf[0] = CONTROL('X');
+ buf[1] = CONTROL('X');
+ buf[2] = 0;
+ key_add(el, buf, key_map_cmd(el, EM_EXCHANGE_MARK), XK_CMD);
+
+ tty_bind_char(el, 1);
+ term_bind_arrow(el);
+}
+
+
+/* map_set_editor():
+ * Set the editor
+ */
+protected int
+map_set_editor(EditLine *el, char *editor)
+{
+
+ if (strcmp(editor, "emacs") == 0) {
+ map_init_emacs(el);
+ return (0);
+ }
+ if (strcmp(editor, "vi") == 0) {
+ map_init_vi(el);
+ return (0);
+ }
+ return (-1);
+}
+
+
+/* map_get_editor():
+ * Retrieve the editor
+ */
+protected int
+map_get_editor(EditLine *el, const char **editor)
+{
+
+ if (editor == NULL)
+ return (-1);
+ switch (el->el_map.type) {
+ case MAP_EMACS:
+ *editor = "emacs";
+ return (0);
+ case MAP_VI:
+ *editor = "vi";
+ return (0);
+ }
+ return (-1);
+}
+
+
+/* map_print_key():
+ * Print the function description for 1 key
+ */
+private void
+map_print_key(EditLine *el, el_action_t *map, const char *in)
+{
+ char outbuf[EL_BUFSIZ];
+ el_bindings_t *bp;
+
+ if (in[0] == '\0' || in[1] == '\0') {
+ (void) key__decode_str(in, outbuf, "");
+ for (bp = el->el_map.help; bp->name != NULL; bp++)
+ if (bp->func == map[(unsigned char) *in]) {
+ (void) fprintf(el->el_outfile,
+ "%s\t->\t%s\n", outbuf, bp->name);
+ return;
+ }
+ } else
+ key_print(el, in);
+}
+
+
+/* map_print_some_keys():
+ * Print keys from first to last
+ */
+private void
+map_print_some_keys(EditLine *el, el_action_t *map, int first, int last)
+{
+ el_bindings_t *bp;
+ char firstbuf[2], lastbuf[2];
+ char unparsbuf[EL_BUFSIZ], extrabuf[EL_BUFSIZ];
+
+ firstbuf[0] = first;
+ firstbuf[1] = 0;
+ lastbuf[0] = last;
+ lastbuf[1] = 0;
+ if (map[first] == ED_UNASSIGNED) {
+ if (first == last)
+ (void) fprintf(el->el_outfile,
+ "%-15s-> is undefined\n",
+ key__decode_str(firstbuf, unparsbuf, STRQQ));
+ return;
+ }
+ for (bp = el->el_map.help; bp->name != NULL; bp++) {
+ if (bp->func == map[first]) {
+ if (first == last) {
+ (void) fprintf(el->el_outfile, "%-15s-> %s\n",
+ key__decode_str(firstbuf, unparsbuf, STRQQ),
+ bp->name);
+ } else {
+ (void) fprintf(el->el_outfile,
+ "%-4s to %-7s-> %s\n",
+ key__decode_str(firstbuf, unparsbuf, STRQQ),
+ key__decode_str(lastbuf, extrabuf, STRQQ),
+ bp->name);
+ }
+ return;
+ }
+ }
+#ifdef MAP_DEBUG
+ if (map == el->el_map.key) {
+ (void) fprintf(el->el_outfile,
+ "BUG!!! %s isn't bound to anything.\n",
+ key__decode_str(firstbuf, unparsbuf, STRQQ));
+ (void) fprintf(el->el_outfile, "el->el_map.key[%d] == %d\n",
+ first, el->el_map.key[first]);
+ } else {
+ (void) fprintf(el->el_outfile,
+ "BUG!!! %s isn't bound to anything.\n",
+ key__decode_str(firstbuf, unparsbuf, STRQQ));
+ (void) fprintf(el->el_outfile, "el->el_map.alt[%d] == %d\n",
+ first, el->el_map.alt[first]);
+ }
+#endif
+ EL_ABORT((el->el_errfile, "Error printing keys\n"));
+}
+
+
+/* map_print_all_keys():
+ * Print the function description for all keys.
+ */
+private void
+map_print_all_keys(EditLine *el)
+{
+ int prev, i;
+
+ (void) fprintf(el->el_outfile, "Standard key bindings\n");
+ prev = 0;
+ for (i = 0; i < N_KEYS; i++) {
+ if (el->el_map.key[prev] == el->el_map.key[i])
+ continue;
+ map_print_some_keys(el, el->el_map.key, prev, i - 1);
+ prev = i;
+ }
+ map_print_some_keys(el, el->el_map.key, prev, i - 1);
+
+ (void) fprintf(el->el_outfile, "Alternative key bindings\n");
+ prev = 0;
+ for (i = 0; i < N_KEYS; i++) {
+ if (el->el_map.alt[prev] == el->el_map.alt[i])
+ continue;
+ map_print_some_keys(el, el->el_map.alt, prev, i - 1);
+ prev = i;
+ }
+ map_print_some_keys(el, el->el_map.alt, prev, i - 1);
+
+ (void) fprintf(el->el_outfile, "Multi-character bindings\n");
+ key_print(el, "");
+ (void) fprintf(el->el_outfile, "Arrow key bindings\n");
+ term_print_arrow(el, "");
+}
+
+
+/* map_bind():
+ * Add/remove/change bindings
+ */
+protected int
+map_bind(EditLine *el, int argc, const char **argv)
+{
+ el_action_t *map;
+ int ntype, rem;
+ const char *p;
+ char inbuf[EL_BUFSIZ];
+ char outbuf[EL_BUFSIZ];
+ const char *in = NULL;
+ char *out = NULL;
+ el_bindings_t *bp;
+ int cmd;
+ int key;
+
+ if (argv == NULL)
+ return (-1);
+
+ map = el->el_map.key;
+ ntype = XK_CMD;
+ key = rem = 0;
+ for (argc = 1; (p = argv[argc]) != NULL; argc++)
+ if (p[0] == '-')
+ switch (p[1]) {
+ case 'a':
+ map = el->el_map.alt;
+ break;
+
+ case 's':
+ ntype = XK_STR;
+ break;
+#ifdef notyet
+ case 'c':
+ ntype = XK_EXE;
+ break;
+#endif
+ case 'k':
+ key = 1;
+ break;
+
+ case 'r':
+ rem = 1;
+ break;
+
+ case 'v':
+ map_init_vi(el);
+ return (0);
+
+ case 'e':
+ map_init_emacs(el);
+ return (0);
+
+ case 'l':
+ for (bp = el->el_map.help; bp->name != NULL;
+ bp++)
+ (void) fprintf(el->el_outfile,
+ "%s\n\t%s\n",
+ bp->name, bp->description);
+ return (0);
+ default:
+ (void) fprintf(el->el_errfile,
+ "%s: Invalid switch `%c'.\n",
+ argv[0], p[1]);
+ }
+ else
+ break;
+
+ if (argv[argc] == NULL) {
+ map_print_all_keys(el);
+ return (0);
+ }
+ if (key)
+ in = argv[argc++];
+ else if ((in = parse__string(inbuf, argv[argc++])) == NULL) {
+ (void) fprintf(el->el_errfile,
+ "%s: Invalid \\ or ^ in instring.\n",
+ argv[0]);
+ return (-1);
+ }
+ if (rem) {
+ if (key) {
+ (void) term_clear_arrow(el, in);
+ return (-1);
+ }
+ if (in[1])
+ (void) key_delete(el, in);
+ else if (map[(unsigned char) *in] == ED_SEQUENCE_LEAD_IN)
+ (void) key_delete(el, in);
+ else
+ map[(unsigned char) *in] = ED_UNASSIGNED;
+ return (0);
+ }
+ if (argv[argc] == NULL) {
+ if (key)
+ term_print_arrow(el, in);
+ else
+ map_print_key(el, map, in);
+ return (0);
+ }
+#ifdef notyet
+ if (argv[argc + 1] != NULL) {
+ bindkey_usage();
+ return (-1);
+ }
+#endif
+
+ switch (ntype) {
+ case XK_STR:
+ case XK_EXE:
+ if ((out = parse__string(outbuf, argv[argc])) == NULL) {
+ (void) fprintf(el->el_errfile,
+ "%s: Invalid \\ or ^ in outstring.\n", argv[0]);
+ return (-1);
+ }
+ if (key)
+ term_set_arrow(el, in, key_map_str(el, out), ntype);
+ else
+ key_add(el, in, key_map_str(el, out), ntype);
+ map[(unsigned char) *in] = ED_SEQUENCE_LEAD_IN;
+ break;
+
+ case XK_CMD:
+ if ((cmd = parse_cmd(el, argv[argc])) == -1) {
+ (void) fprintf(el->el_errfile,
+ "%s: Invalid command `%s'.\n", argv[0], argv[argc]);
+ return (-1);
+ }
+ if (key)
+ term_set_arrow(el, in, key_map_str(el, out), ntype);
+ else {
+ if (in[1]) {
+ key_add(el, in, key_map_cmd(el, cmd), ntype);
+ map[(unsigned char) *in] = ED_SEQUENCE_LEAD_IN;
+ } else {
+ key_clear(el, map, in);
+ map[(unsigned char) *in] = cmd;
+ }
+ }
+ break;
+
+ default:
+ EL_ABORT((el->el_errfile, "Bad XK_ type\n", ntype));
+ break;
+ }
+ return (0);
+}
+
+
+/* map_addfunc():
+ * add a user defined function
+ */
+protected int
+map_addfunc(EditLine *el, const char *name, const char *help, el_func_t func)
+{
+ void *p;
+ int nf = el->el_map.nfunc + 2;
+
+ if (name == NULL || help == NULL || func == NULL)
+ return (-1);
+
+ if ((p = el_realloc(el->el_map.func, nf * sizeof(el_func_t))) == NULL)
+ return (-1);
+ el->el_map.func = (el_func_t *) p;
+ if ((p = el_realloc(el->el_map.help, nf * sizeof(el_bindings_t)))
+ == NULL)
+ return (-1);
+ el->el_map.help = (el_bindings_t *) p;
+
+ nf = el->el_map.nfunc;
+ el->el_map.func[nf] = func;
+
+ el->el_map.help[nf].name = name;
+ el->el_map.help[nf].func = nf;
+ el->el_map.help[nf].description = help;
+ el->el_map.help[++nf].name = NULL;
+ el->el_map.nfunc++;
+
+ return (0);
+}
diff --git a/trunk/main/editline/map.h b/trunk/main/editline/map.h
new file mode 100644
index 000000000..3c9948ccf
--- /dev/null
+++ b/trunk/main/editline/map.h
@@ -0,0 +1,79 @@
+/* $NetBSD: map.h,v 1.7 2002/03/18 16:00:56 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)map.h 8.1 (Berkeley) 6/4/93
+ */
+
+/*
+ * el.map.h: Editor maps
+ */
+#ifndef _h_el_map
+#define _h_el_map
+
+typedef struct el_bindings_t { /* for the "bind" shell command */
+ const char *name; /* function name for bind command */
+ int func; /* function numeric value */
+ const char *description; /* description of function */
+} el_bindings_t;
+
+
+typedef struct el_map_t {
+ el_action_t *alt; /* The current alternate key map */
+ el_action_t *key; /* The current normal key map */
+ el_action_t *current; /* The keymap we are using */
+ const el_action_t *emacs; /* The default emacs key map */
+ const el_action_t *vic; /* The vi command mode key map */
+ const el_action_t *vii; /* The vi insert mode key map */
+ int type; /* Emacs or vi */
+ el_bindings_t *help; /* The help for the editor functions */
+ el_func_t *func; /* List of available functions */
+ int nfunc; /* The number of functions/help items */
+} el_map_t;
+
+#define MAP_EMACS 0
+#define MAP_VI 1
+
+protected int map_bind(EditLine *, int, const char **);
+protected int map_init(EditLine *);
+protected void map_end(EditLine *);
+protected void map_init_vi(EditLine *);
+protected void map_init_emacs(EditLine *);
+protected int map_set_editor(EditLine *, char *);
+protected int map_get_editor(EditLine *, const char **);
+protected int map_addfunc(EditLine *, const char *, const char *, el_func_t);
+
+#endif /* _h_el_map */
diff --git a/trunk/main/editline/np/fgetln.c b/trunk/main/editline/np/fgetln.c
new file mode 100644
index 000000000..93da9914d
--- /dev/null
+++ b/trunk/main/editline/np/fgetln.c
@@ -0,0 +1,88 @@
+/* $NetBSD: fgetln.c,v 1.1.1.1 1999/04/12 07:43:21 crooksa Exp $ */
+
+/*-
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+
+char *
+fgetln(fp, len)
+ FILE *fp;
+ size_t *len;
+{
+ static char *buf = NULL;
+ static size_t bufsiz = 0;
+ char *ptr;
+
+
+ if (buf == NULL) {
+ bufsiz = BUFSIZ;
+ if ((buf = malloc(bufsiz)) == NULL)
+ return NULL;
+ }
+
+ if (fgets(buf, bufsiz, fp) == NULL)
+ return NULL;
+ *len = 0;
+
+ while ((ptr = strchr(&buf[*len], '\n')) == NULL) {
+ size_t nbufsiz = bufsiz + BUFSIZ;
+ char *nbuf = realloc(buf, nbufsiz);
+
+ if (nbuf == NULL) {
+ int oerrno = errno;
+ free(buf);
+ errno = oerrno;
+ buf = NULL;
+ return NULL;
+ } else
+ buf = nbuf;
+
+ *len = bufsiz;
+ if (fgets(&buf[bufsiz], BUFSIZ, fp) == NULL)
+ return buf;
+
+ bufsiz = nbufsiz;
+ }
+
+ *len = (ptr - buf) + 1;
+ return buf;
+}
diff --git a/trunk/main/editline/np/strlcat.c b/trunk/main/editline/np/strlcat.c
new file mode 100644
index 000000000..6c9f1e92d
--- /dev/null
+++ b/trunk/main/editline/np/strlcat.c
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#if defined(LIBC_SCCS) && !defined(lint)
+static char *rcsid = "$OpenBSD: strlcat.c,v 1.2 1999/06/17 16:28:58 millert Exp $";
+#endif /* LIBC_SCCS and not lint */
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD: src/lib/libc/string/strlcat.c,v 1.2.4.2 2001/07/09 23:30:06 obrien Exp $";
+#endif
+
+#include <sys/types.h>
+#include <string.h>
+
+/*
+ * Appends src to string dst of size siz (unlike strncat, siz is the
+ * full size of dst, not space left). At most siz-1 characters
+ * will be copied. Always NUL terminates (unless siz <= strlen(dst)).
+ * Returns strlen(initial dst) + strlen(src); if retval >= siz,
+ * truncation occurred.
+ */
+size_t strlcat(dst, src, siz)
+ char *dst;
+ const char *src;
+ size_t siz;
+{
+ register char *d = dst;
+ register const char *s = src;
+ register size_t n = siz;
+ size_t dlen;
+
+ /* Find the end of dst and adjust bytes left but don't go past end */
+ while (n-- != 0 && *d != '\0')
+ d++;
+ dlen = d - dst;
+ n = siz - dlen;
+
+ if (n == 0)
+ return(dlen + strlen(s));
+ while (*s != '\0') {
+ if (n != 1) {
+ *d++ = *s;
+ n--;
+ }
+ s++;
+ }
+ *d = '\0';
+
+ return(dlen + (s - src)); /* count does not include NUL */
+}
diff --git a/trunk/main/editline/np/strlcpy.c b/trunk/main/editline/np/strlcpy.c
new file mode 100644
index 000000000..1f154bcf2
--- /dev/null
+++ b/trunk/main/editline/np/strlcpy.c
@@ -0,0 +1,75 @@
+/* $OpenBSD: strlcpy.c,v 1.4 1999/05/01 18:56:41 millert Exp $ */
+
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#if defined(LIBC_SCCS) && !defined(lint)
+#if 0
+static char *rcsid = "$OpenBSD: strlcpy.c,v 1.4 1999/05/01 18:56:41 millert Exp $";
+#endif
+#endif /* LIBC_SCCS and not lint */
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD: src/lib/libc/string/strlcpy.c,v 1.2.4.1 2001/07/09 23:30:06 obrien Exp $";
+#endif
+
+#include <sys/types.h>
+#include <string.h>
+
+/*
+ * Copy src to string dst of size siz. At most siz-1 characters
+ * will be copied. Always NUL terminates (unless siz == 0).
+ * Returns strlen(src); if retval >= siz, truncation occurred.
+ */
+size_t strlcpy(dst, src, siz)
+ char *dst;
+ const char *src;
+ size_t siz;
+{
+ register char *d = dst;
+ register const char *s = src;
+ register size_t n = siz;
+
+ /* Copy as many bytes as will fit */
+ if (n != 0 && --n != 0) {
+ do {
+ if ((*d++ = *s++) == 0)
+ break;
+ } while (--n != 0);
+ }
+
+ /* Not enough room in dst, add NUL and traverse rest of src */
+ if (n == 0) {
+ if (siz != 0)
+ *d = '\0'; /* NUL-terminate dst */
+ while (*s++)
+ ;
+ }
+
+ return(s - src - 1); /* count does not include NUL */
+}
diff --git a/trunk/main/editline/np/unvis.c b/trunk/main/editline/np/unvis.c
new file mode 100644
index 000000000..f43c4c749
--- /dev/null
+++ b/trunk/main/editline/np/unvis.c
@@ -0,0 +1,322 @@
+/* $NetBSD: unvis.c,v 1.22 2002/03/23 17:38:27 christos Exp $ */
+
+/*-
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#if defined(LIBC_SCCS) && !defined(lint)
+#if 0
+static char sccsid[] = "@(#)unvis.c 8.1 (Berkeley) 6/4/93";
+#else
+__RCSID("$NetBSD: unvis.c,v 1.22 2002/03/23 17:38:27 christos Exp $");
+#endif
+#endif /* LIBC_SCCS and not lint */
+
+#define __LIBC12_SOURCE__
+
+#include <sys/types.h>
+
+#include <assert.h>
+#include <ctype.h>
+#include <stdio.h>
+#include "np/vis.h"
+
+#ifdef __weak_alias
+__weak_alias(strunvis,_strunvis)
+__weak_alias(unvis,_unvis)
+#endif
+
+#ifdef __warn_references
+__warn_references(unvis,
+ "warning: reference to compatibility unvis(); include <vis.h> for correct reference")
+#endif
+
+#if !HAVE_VIS_H
+/*
+ * decode driven by state machine
+ */
+#define S_GROUND 0 /* haven't seen escape char */
+#define S_START 1 /* start decoding special sequence */
+#define S_META 2 /* metachar started (M) */
+#define S_META1 3 /* metachar more, regular char (-) */
+#define S_CTRL 4 /* control char started (^) */
+#define S_OCTAL2 5 /* octal digit 2 */
+#define S_OCTAL3 6 /* octal digit 3 */
+#define S_HEX1 7 /* hex digit */
+#define S_HEX2 8 /* hex digit 2 */
+
+#define isoctal(c) (((u_char)(c)) >= '0' && ((u_char)(c)) <= '7')
+#define xtod(c) (isdigit(c) ? (c - '0') : ((tolower(c) - 'a') + 10))
+
+int
+unvis(cp, c, astate, flag)
+ char *cp;
+ int c;
+ int *astate, flag;
+{
+ return __unvis13(cp, (int)c, astate, flag);
+}
+
+/*
+ * unvis - decode characters previously encoded by vis
+ */
+int
+__unvis13(cp, c, astate, flag)
+ char *cp;
+ int c;
+ int *astate, flag;
+{
+
+ _DIAGASSERT(cp != NULL);
+ _DIAGASSERT(astate != NULL);
+
+ if (flag & UNVIS_END) {
+ if (*astate == S_OCTAL2 || *astate == S_OCTAL3
+ || *astate == S_HEX2) {
+ *astate = S_GROUND;
+ return (UNVIS_VALID);
+ }
+ return (*astate == S_GROUND ? UNVIS_NOCHAR : UNVIS_SYNBAD);
+ }
+
+ switch (*astate) {
+
+ case S_GROUND:
+ *cp = 0;
+ if (c == '\\') {
+ *astate = S_START;
+ return (0);
+ }
+ if ((flag & VIS_HTTPSTYLE) && c == '%') {
+ *astate = S_HEX1;
+ return (0);
+ }
+ *cp = c;
+ return (UNVIS_VALID);
+
+ case S_START:
+ switch(c) {
+ case '\\':
+ *cp = c;
+ *astate = S_GROUND;
+ return (UNVIS_VALID);
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ *cp = (c - '0');
+ *astate = S_OCTAL2;
+ return (0);
+ case 'M':
+ *cp = (char)0200;
+ *astate = S_META;
+ return (0);
+ case '^':
+ *astate = S_CTRL;
+ return (0);
+ case 'n':
+ *cp = '\n';
+ *astate = S_GROUND;
+ return (UNVIS_VALID);
+ case 'r':
+ *cp = '\r';
+ *astate = S_GROUND;
+ return (UNVIS_VALID);
+ case 'b':
+ *cp = '\b';
+ *astate = S_GROUND;
+ return (UNVIS_VALID);
+ case 'a':
+ *cp = '\007';
+ *astate = S_GROUND;
+ return (UNVIS_VALID);
+ case 'v':
+ *cp = '\v';
+ *astate = S_GROUND;
+ return (UNVIS_VALID);
+ case 't':
+ *cp = '\t';
+ *astate = S_GROUND;
+ return (UNVIS_VALID);
+ case 'f':
+ *cp = '\f';
+ *astate = S_GROUND;
+ return (UNVIS_VALID);
+ case 's':
+ *cp = ' ';
+ *astate = S_GROUND;
+ return (UNVIS_VALID);
+ case 'E':
+ *cp = '\033';
+ *astate = S_GROUND;
+ return (UNVIS_VALID);
+ case '\n':
+ /*
+ * hidden newline
+ */
+ *astate = S_GROUND;
+ return (UNVIS_NOCHAR);
+ case '$':
+ /*
+ * hidden marker
+ */
+ *astate = S_GROUND;
+ return (UNVIS_NOCHAR);
+ }
+ *astate = S_GROUND;
+ return (UNVIS_SYNBAD);
+
+ case S_META:
+ if (c == '-')
+ *astate = S_META1;
+ else if (c == '^')
+ *astate = S_CTRL;
+ else {
+ *astate = S_GROUND;
+ return (UNVIS_SYNBAD);
+ }
+ return (0);
+
+ case S_META1:
+ *astate = S_GROUND;
+ *cp |= c;
+ return (UNVIS_VALID);
+
+ case S_CTRL:
+ if (c == '?')
+ *cp |= 0177;
+ else
+ *cp |= c & 037;
+ *astate = S_GROUND;
+ return (UNVIS_VALID);
+
+ case S_OCTAL2: /* second possible octal digit */
+ if (isoctal(c)) {
+ /*
+ * yes - and maybe a third
+ */
+ *cp = (*cp << 3) + (c - '0');
+ *astate = S_OCTAL3;
+ return (0);
+ }
+ /*
+ * no - done with current sequence, push back passed char
+ */
+ *astate = S_GROUND;
+ return (UNVIS_VALIDPUSH);
+
+ case S_OCTAL3: /* third possible octal digit */
+ *astate = S_GROUND;
+ if (isoctal(c)) {
+ *cp = (*cp << 3) + (c - '0');
+ return (UNVIS_VALID);
+ }
+ /*
+ * we were done, push back passed char
+ */
+ return (UNVIS_VALIDPUSH);
+ case S_HEX1:
+ if (isxdigit(c)) {
+ *cp = xtod(c);
+ *astate = S_HEX2;
+ return (0);
+ }
+ /*
+ * no - done with current sequence, push back passed char
+ */
+ *astate = S_GROUND;
+ return (UNVIS_VALIDPUSH);
+ case S_HEX2:
+ *astate = S_GROUND;
+ if (isxdigit(c)) {
+ *cp = xtod(c) | (*cp << 4);
+ return (UNVIS_VALID);
+ }
+ return (UNVIS_VALIDPUSH);
+ default:
+ /*
+ * decoder in unknown state - (probably uninitialized)
+ */
+ *astate = S_GROUND;
+ return (UNVIS_SYNBAD);
+ }
+}
+
+/*
+ * strunvis - decode src into dst
+ *
+ * Number of chars decoded into dst is returned, -1 on error.
+ * Dst is null terminated.
+ */
+
+int
+strunvisx(dst, src, flag)
+ char *dst;
+ const char *src;
+ int flag;
+{
+ char c;
+ char *start = dst;
+ int state = 0;
+
+ _DIAGASSERT(src != NULL);
+ _DIAGASSERT(dst != NULL);
+
+ while ((c = *src++) != '\0') {
+ again:
+ switch (__unvis13(dst, c, &state, flag)) {
+ case UNVIS_VALID:
+ dst++;
+ break;
+ case UNVIS_VALIDPUSH:
+ dst++;
+ goto again;
+ case 0:
+ case UNVIS_NOCHAR:
+ break;
+ default:
+ return (-1);
+ }
+ }
+ if (__unvis13(dst, c, &state, UNVIS_END) == UNVIS_VALID)
+ dst++;
+ *dst = '\0';
+ return (dst - start);
+}
+
+int
+strunvis(dst, src)
+ char *dst;
+ const char *src;
+{
+ return strunvisx(dst, src, 0);
+}
+#endif
diff --git a/trunk/main/editline/np/vis.c b/trunk/main/editline/np/vis.c
new file mode 100644
index 000000000..7d9117faa
--- /dev/null
+++ b/trunk/main/editline/np/vis.c
@@ -0,0 +1,347 @@
+/* $NetBSD: vis.c,v 1.22 2002/03/23 17:38:27 christos Exp $ */
+
+/*-
+ * Copyright (c) 1999 The NetBSD Foundation, Inc.
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#if defined(LIBC_SCCS) && !defined(lint)
+__RCSID("$NetBSD: vis.c,v 1.22 2002/03/23 17:38:27 christos Exp $");
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+
+#include <assert.h>
+#include "np/vis.h"
+#include <stdlib.h>
+
+#ifdef __weak_alias
+__weak_alias(strsvis,_strsvis)
+__weak_alias(strsvisx,_strsvisx)
+__weak_alias(strvis,_strvis)
+__weak_alias(strvisx,_strvisx)
+__weak_alias(svis,_svis)
+__weak_alias(vis,_vis)
+#endif
+
+#ifndef HAVE_VIS_H
+#include <ctype.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+
+#undef BELL
+#if defined(__STDC__)
+#define BELL '\a'
+#else
+#define BELL '\007'
+#endif
+
+#ifdef SOLARIS
+#include <alloca.h>
+#endif
+
+#define isoctal(c) (((u_char)(c)) >= '0' && ((u_char)(c)) <= '7')
+#define iswhite(c) (c == ' ' || c == '\t' || c == '\n')
+#define issafe(c) (c == '\b' || c == BELL || c == '\r')
+#define xtoa(c) "0123456789abcdef"[c]
+
+#define MAXEXTRAS 5
+
+
+#define MAKEEXTRALIST(flag, extra, orig) \
+do { \
+ const char *o = orig; \
+ char *e; \
+ while (*o++) \
+ continue; \
+ extra = alloca((size_t)((o - orig) + MAXEXTRAS)); \
+ for (o = orig, e = extra; (*e++ = *o++) != '\0';) \
+ continue; \
+ e--; \
+ if (flag & VIS_SP) *e++ = ' '; \
+ if (flag & VIS_TAB) *e++ = '\t'; \
+ if (flag & VIS_NL) *e++ = '\n'; \
+ if ((flag & VIS_NOSLASH) == 0) *e++ = '\\'; \
+ *e = '\0'; \
+} while (/*CONSTCOND*/0)
+
+
+/*
+ * This is HVIS, the macro of vis used to HTTP style (RFC 1808)
+ */
+#define HVIS(dst, c, flag, nextc, extra) \
+do \
+ if (!isascii(c) || !isalnum(c) || strchr("$-_.+!*'(),", c) != NULL) { \
+ *dst++ = '%'; \
+ *dst++ = xtoa(((unsigned int)c >> 4) & 0xf); \
+ *dst++ = xtoa((unsigned int)c & 0xf); \
+ } else { \
+ SVIS(dst, c, flag, nextc, extra); \
+ } \
+while (/*CONSTCOND*/0)
+
+/*
+ * This is SVIS, the central macro of vis.
+ * dst: Pointer to the destination buffer
+ * c: Character to encode
+ * flag: Flag word
+ * nextc: The character following 'c'
+ * extra: Pointer to the list of extra characters to be
+ * backslash-protected.
+ */
+#define SVIS(dst, c, flag, nextc, extra) \
+do { \
+ int isextra, isc; \
+ isextra = strchr(extra, c) != NULL; \
+ if (!isextra && isascii(c) && (isgraph(c) || iswhite(c) || \
+ ((flag & VIS_SAFE) && issafe(c)))) { \
+ *dst++ = c; \
+ break; \
+ } \
+ isc = 0; \
+ if (flag & VIS_CSTYLE) { \
+ switch (c) { \
+ case '\n': \
+ isc = 1; *dst++ = '\\'; *dst++ = 'n'; \
+ break; \
+ case '\r': \
+ isc = 1; *dst++ = '\\'; *dst++ = 'r'; \
+ break; \
+ case '\b': \
+ isc = 1; *dst++ = '\\'; *dst++ = 'b'; \
+ break; \
+ case BELL: \
+ isc = 1; *dst++ = '\\'; *dst++ = 'a'; \
+ break; \
+ case '\v': \
+ isc = 1; *dst++ = '\\'; *dst++ = 'v'; \
+ break; \
+ case '\t': \
+ isc = 1; *dst++ = '\\'; *dst++ = 't'; \
+ break; \
+ case '\f': \
+ isc = 1; *dst++ = '\\'; *dst++ = 'f'; \
+ break; \
+ case ' ': \
+ isc = 1; *dst++ = '\\'; *dst++ = 's'; \
+ break; \
+ case '\0': \
+ isc = 1; *dst++ = '\\'; *dst++ = '0'; \
+ if (isoctal(nextc)) { \
+ *dst++ = '0'; \
+ *dst++ = '0'; \
+ } \
+ } \
+ } \
+ if (isc) break; \
+ if (isextra || ((c & 0177) == ' ') || (flag & VIS_OCTAL)) { \
+ *dst++ = '\\'; \
+ *dst++ = (u_char)(((u_int32_t)(u_char)c >> 6) & 03) + '0'; \
+ *dst++ = (u_char)(((u_int32_t)(u_char)c >> 3) & 07) + '0'; \
+ *dst++ = (c & 07) + '0'; \
+ } else { \
+ if ((flag & VIS_NOSLASH) == 0) *dst++ = '\\'; \
+ if (c & 0200) { \
+ c &= 0177; *dst++ = 'M'; \
+ } \
+ if (iscntrl(c)) { \
+ *dst++ = '^'; \
+ if (c == 0177) \
+ *dst++ = '?'; \
+ else \
+ *dst++ = c + '@'; \
+ } else { \
+ *dst++ = '-'; *dst++ = c; \
+ } \
+ } \
+} while (/*CONSTCOND*/0)
+
+
+/*
+ * svis - visually encode characters, also encoding the characters
+ * pointed to by `extra'
+ */
+char *
+svis(dst, c, flag, nextc, extra)
+ char *dst;
+ int c, flag, nextc;
+ const char *extra;
+{
+ char *nextra;
+ _DIAGASSERT(dst != NULL);
+ _DIAGASSERT(extra != NULL);
+ MAKEEXTRALIST(flag, nextra, extra);
+ if (flag & VIS_HTTPSTYLE)
+ HVIS(dst, c, flag, nextc, nextra);
+ else
+ SVIS(dst, c, flag, nextc, nextra);
+ *dst = '\0';
+ return(dst);
+}
+
+
+/*
+ * strsvis, strsvisx - visually encode characters from src into dst
+ *
+ * Extra is a pointer to a \0-terminated list of characters to
+ * be encoded, too. These functions are useful e. g. to
+ * encode strings in such a way so that they are not interpreted
+ * by a shell.
+ *
+ * Dst must be 4 times the size of src to account for possible
+ * expansion. The length of dst, not including the trailing NULL,
+ * is returned.
+ *
+ * Strsvisx encodes exactly len bytes from src into dst.
+ * This is useful for encoding a block of data.
+ */
+int
+strsvis(dst, src, flag, extra)
+ char *dst;
+ const char *src;
+ int flag;
+ const char *extra;
+{
+ char c;
+ char *start;
+ char *nextra;
+
+ _DIAGASSERT(dst != NULL);
+ _DIAGASSERT(src != NULL);
+ _DIAGASSERT(extra != NULL);
+ MAKEEXTRALIST(flag, nextra, extra);
+ if (flag & VIS_HTTPSTYLE) {
+ for (start = dst; (c = *src++) != '\0'; /* empty */)
+ HVIS(dst, c, flag, *src, nextra);
+ } else {
+ for (start = dst; (c = *src++) != '\0'; /* empty */)
+ SVIS(dst, c, flag, *src, nextra);
+ }
+ *dst = '\0';
+ return (dst - start);
+}
+
+
+int
+strsvisx(dst, src, len, flag, extra)
+ char *dst;
+ const char *src;
+ size_t len;
+ int flag;
+ const char *extra;
+{
+ char c;
+ char *start;
+ char *nextra;
+
+ _DIAGASSERT(dst != NULL);
+ _DIAGASSERT(src != NULL);
+ _DIAGASSERT(extra != NULL);
+ MAKEEXTRALIST(flag, nextra, extra);
+
+ if (flag & VIS_HTTPSTYLE) {
+ for (start = dst; len > 0; len--) {
+ c = *src++;
+ HVIS(dst, c, flag, len ? *src : '\0', nextra);
+ }
+ } else {
+ for (start = dst; len > 0; len--) {
+ c = *src++;
+ SVIS(dst, c, flag, len ? *src : '\0', nextra);
+ }
+ }
+ *dst = '\0';
+ return (dst - start);
+}
+
+
+/*
+ * vis - visually encode characters
+ */
+char *
+vis(dst, c, flag, nextc)
+ char *dst;
+ int c, flag, nextc;
+
+{
+ char *extra;
+
+ _DIAGASSERT(dst != NULL);
+
+ MAKEEXTRALIST(flag, extra, "");
+ if (flag & VIS_HTTPSTYLE)
+ HVIS(dst, c, flag, nextc, extra);
+ else
+ SVIS(dst, c, flag, nextc, extra);
+ *dst = '\0';
+ return (dst);
+}
+
+
+/*
+ * strvis, strvisx - visually encode characters from src into dst
+ *
+ * Dst must be 4 times the size of src to account for possible
+ * expansion. The length of dst, not including the trailing NULL,
+ * is returned.
+ *
+ * Strvisx encodes exactly len bytes from src into dst.
+ * This is useful for encoding a block of data.
+ */
+int
+strvis(dst, src, flag)
+ char *dst;
+ const char *src;
+ int flag;
+{
+ char *extra;
+
+ MAKEEXTRALIST(flag, extra, "");
+ return (strsvis(dst, src, flag, extra));
+}
+
+
+int
+strvisx(dst, src, len, flag)
+ char *dst;
+ const char *src;
+ size_t len;
+ int flag;
+{
+ char *extra;
+
+ MAKEEXTRALIST(flag, extra, "");
+ return (strsvisx(dst, src, len, flag, extra));
+}
+#endif
diff --git a/trunk/main/editline/np/vis.h b/trunk/main/editline/np/vis.h
new file mode 100644
index 000000000..0739c1dfa
--- /dev/null
+++ b/trunk/main/editline/np/vis.h
@@ -0,0 +1,96 @@
+/* $NetBSD: vis.h,v 1.12 2002/03/23 17:39:05 christos Exp $ */
+
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)vis.h 8.1 (Berkeley) 6/2/93
+ */
+
+#ifndef _VIS_H_
+#define _VIS_H_
+
+/*
+ * to select alternate encoding format
+ */
+#define VIS_OCTAL 0x01 /* use octal \ddd format */
+#define VIS_CSTYLE 0x02 /* use \[nrft0..] where appropiate */
+
+/*
+ * to alter set of characters encoded (default is to encode all
+ * non-graphic except space, tab, and newline).
+ */
+#define VIS_SP 0x04 /* also encode space */
+#define VIS_TAB 0x08 /* also encode tab */
+#define VIS_NL 0x10 /* also encode newline */
+#define VIS_WHITE (VIS_SP | VIS_TAB | VIS_NL)
+#define VIS_SAFE 0x20 /* only encode "unsafe" characters */
+
+/*
+ * other
+ */
+#define VIS_NOSLASH 0x40 /* inhibit printing '\' */
+#define VIS_HTTPSTYLE 0x80 /* http-style escape % HEX HEX */
+
+/*
+ * unvis return codes
+ */
+#define UNVIS_VALID 1 /* character valid */
+#define UNVIS_VALIDPUSH 2 /* character valid, push back passed char */
+#define UNVIS_NOCHAR 3 /* valid sequence, no character produced */
+#define UNVIS_SYNBAD -1 /* unrecognized escape sequence */
+#define UNVIS_ERROR -2 /* decoder in unknown state (unrecoverable) */
+
+/*
+ * unvis flags
+ */
+#define UNVIS_END 1 /* no more characters */
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+char *vis __P((char *, int, int, int));
+char *svis __P((char *, int, int, int, const char *));
+int strvis __P((char *, const char *, int));
+int strsvis __P((char *, const char *, int, const char *));
+int strvisx __P((char *, const char *, size_t, int));
+int strsvisx __P((char *, const char *, size_t, int, const char *));
+int strunvis __P((char *, const char *));
+int strunvisx __P((char *, const char *, int));
+#ifdef __LIBC12_SOURCE__
+int unvis __P((char *, int, int *, int));
+int __unvis13 __P((char *, int, int *, int));
+#else
+int unvis __P((char *, int, int *, int)) __RENAME(__unvis13);
+#endif
+__END_DECLS
+
+#endif /* !_VIS_H_ */
diff --git a/trunk/main/editline/parse.c b/trunk/main/editline/parse.c
new file mode 100644
index 000000000..0a34f0b15
--- /dev/null
+++ b/trunk/main/editline/parse.c
@@ -0,0 +1,259 @@
+/* $NetBSD: parse.c,v 1.15 2002/03/18 16:00:56 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+#if !defined(lint) && !defined(SCCSID)
+#if 0
+static char sccsid[] = "@(#)parse.c 8.1 (Berkeley) 6/4/93";
+#else
+__RCSID("$NetBSD: parse.c,v 1.15 2002/03/18 16:00:56 christos Exp $");
+#endif
+#endif /* not lint && not SCCSID */
+
+/*
+ * parse.c: parse an editline extended command
+ *
+ * commands are:
+ *
+ * bind
+ * echotc
+ * edit
+ * gettc
+ * history
+ * settc
+ * setty
+ */
+#include "el.h"
+#include "tokenizer.h"
+#include <stdlib.h>
+
+private const struct {
+ const char *name;
+ int (*func)(EditLine *, int, const char **);
+} cmds[] = {
+ { "bind", map_bind },
+ { "echotc", term_echotc },
+ { "edit", el_editmode },
+ { "history", hist_list },
+ { "telltc", term_telltc },
+ { "settc", term_settc },
+ { "setty", tty_stty },
+ { NULL, NULL }
+};
+
+
+/* parse_line():
+ * Parse a line and dispatch it
+ */
+protected int
+parse_line(EditLine *el, const char *line)
+{
+ const char **argv;
+ int argc;
+ Tokenizer *tok;
+
+ tok = tok_init(NULL);
+ tok_line(tok, line, &argc, &argv);
+ argc = el_parse(el, argc, argv);
+ tok_end(tok);
+ return (argc);
+}
+
+
+/* el_parse():
+ * Command dispatcher
+ */
+public int
+el_parse(EditLine *el, int argc, const char *argv[])
+{
+ const char *ptr;
+ int i;
+
+ if (argc < 1)
+ return (-1);
+ ptr = strchr(argv[0], ':');
+ if (ptr != NULL) {
+ char *tprog;
+ size_t l;
+
+ if (ptr == argv[0])
+ return (0);
+ l = ptr - argv[0] - 1;
+ tprog = (char *) el_malloc(l + 1);
+ if (tprog == NULL)
+ return (0);
+ (void) strncpy(tprog, argv[0], l);
+ tprog[l] = '\0';
+ ptr++;
+ l = el_match(el->el_prog, tprog);
+ el_free(tprog);
+ if (!l)
+ return (0);
+ } else
+ ptr = argv[0];
+
+ for (i = 0; cmds[i].name != NULL; i++)
+ if (strcmp(cmds[i].name, ptr) == 0) {
+ i = (*cmds[i].func) (el, argc, argv);
+ return (-i);
+ }
+ return (-1);
+}
+
+
+/* parse__escape():
+ * Parse a string of the form ^<char> \<odigit> \<char> and return
+ * the appropriate character or -1 if the escape is not valid
+ */
+protected int
+parse__escape(const char **const ptr)
+{
+ const char *p;
+ int c;
+
+ p = *ptr;
+
+ if (p[1] == 0)
+ return (-1);
+
+ if (*p == '\\') {
+ p++;
+ switch (*p) {
+ case 'a':
+ c = '\007'; /* Bell */
+ break;
+ case 'b':
+ c = '\010'; /* Backspace */
+ break;
+ case 't':
+ c = '\011'; /* Horizontal Tab */
+ break;
+ case 'n':
+ c = '\012'; /* New Line */
+ break;
+ case 'v':
+ c = '\013'; /* Vertical Tab */
+ break;
+ case 'f':
+ c = '\014'; /* Form Feed */
+ break;
+ case 'r':
+ c = '\015'; /* Carriage Return */
+ break;
+ case 'e':
+ c = '\033'; /* Escape */
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ {
+ int cnt, ch;
+
+ for (cnt = 0, c = 0; cnt < 3; cnt++) {
+ ch = *p++;
+ if (ch < '0' || ch > '7') {
+ p--;
+ break;
+ }
+ c = (c << 3) | (ch - '0');
+ }
+ if ((c & 0xffffff00) != 0)
+ return (-1);
+ --p;
+ break;
+ }
+ default:
+ c = *p;
+ break;
+ }
+ } else if (*p == '^' && isalpha((unsigned char) p[1])) {
+ p++;
+ c = (*p == '?') ? '\177' : (*p & 0237);
+ } else
+ c = *p;
+ *ptr = ++p;
+ return (c);
+}
+/* parse__string():
+ * Parse the escapes from in and put the raw string out
+ */
+protected char *
+parse__string(char *out, const char *in)
+{
+ char *rv = out;
+ int n;
+
+ for (;;)
+ switch (*in) {
+ case '\0':
+ *out = '\0';
+ return (rv);
+
+ case '\\':
+ case '^':
+ if ((n = parse__escape(&in)) == -1)
+ return (NULL);
+ *out++ = n;
+ break;
+
+ default:
+ *out++ = *in++;
+ break;
+ }
+}
+
+
+/* parse_cmd():
+ * Return the command number for the command string given
+ * or -1 if one is not found
+ */
+protected int
+parse_cmd(EditLine *el, const char *cmd)
+{
+ el_bindings_t *b;
+
+ for (b = el->el_map.help; b->name != NULL; b++)
+ if (strcmp(b->name, cmd) == 0)
+ return (b->func);
+ return (-1);
+}
diff --git a/trunk/main/editline/parse.h b/trunk/main/editline/parse.h
new file mode 100644
index 000000000..4aaef2f83
--- /dev/null
+++ b/trunk/main/editline/parse.h
@@ -0,0 +1,52 @@
+/* $NetBSD: parse.h,v 1.4 2000/09/04 22:06:31 lukem Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)parse.h 8.1 (Berkeley) 6/4/93
+ */
+
+/*
+ * el.parse.h: Parser functions
+ */
+#ifndef _h_el_parse
+#define _h_el_parse
+
+protected int parse_line(EditLine *, const char *);
+protected int parse__escape(const char ** const);
+protected char *parse__string(char *, const char *);
+protected int parse_cmd(EditLine *, const char *);
+
+#endif /* _h_el_parse */
diff --git a/trunk/main/editline/prompt.c b/trunk/main/editline/prompt.c
new file mode 100644
index 000000000..5c069e17a
--- /dev/null
+++ b/trunk/main/editline/prompt.c
@@ -0,0 +1,174 @@
+/* $NetBSD: prompt.c,v 1.9 2002/03/18 16:00:56 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+#if !defined(lint) && !defined(SCCSID)
+#if 0
+static char sccsid[] = "@(#)prompt.c 8.1 (Berkeley) 6/4/93";
+#else
+__RCSID("$NetBSD: prompt.c,v 1.9 2002/03/18 16:00:56 christos Exp $");
+#endif
+#endif /* not lint && not SCCSID */
+
+/*
+ * prompt.c: Prompt printing functions
+ */
+#include <stdio.h>
+#include "el.h"
+
+private char *prompt_default(EditLine *);
+private char *prompt_default_r(EditLine *);
+
+/* prompt_default():
+ * Just a default prompt, in case the user did not provide one
+ */
+private char *
+/*ARGSUSED*/
+prompt_default(EditLine *el)
+{
+ static char a[3] = {'?', ' ', '\0'};
+
+ return (a);
+}
+
+
+/* prompt_default_r():
+ * Just a default rprompt, in case the user did not provide one
+ */
+private char *
+/*ARGSUSED*/
+prompt_default_r(EditLine *el)
+{
+ static char a[1] = {'\0'};
+
+ return (a);
+}
+
+
+/* prompt_print():
+ * Print the prompt and update the prompt position.
+ * We use an array of integers in case we want to pass
+ * literal escape sequences in the prompt and we want a
+ * bit to flag them
+ */
+protected void
+prompt_print(EditLine *el, int op)
+{
+ el_prompt_t *elp;
+ char *p;
+
+ if (op == EL_PROMPT)
+ elp = &el->el_prompt;
+ else
+ elp = &el->el_rprompt;
+ p = (elp->p_func) (el);
+ while (*p)
+ re_putc(el, *p++, 1);
+
+ elp->p_pos.v = el->el_refresh.r_cursor.v;
+ elp->p_pos.h = el->el_refresh.r_cursor.h;
+}
+
+
+/* prompt_init():
+ * Initialize the prompt stuff
+ */
+protected int
+prompt_init(EditLine *el)
+{
+
+ el->el_prompt.p_func = prompt_default;
+ el->el_prompt.p_pos.v = 0;
+ el->el_prompt.p_pos.h = 0;
+ el->el_rprompt.p_func = prompt_default_r;
+ el->el_rprompt.p_pos.v = 0;
+ el->el_rprompt.p_pos.h = 0;
+ return (0);
+}
+
+
+/* prompt_end():
+ * Clean up the prompt stuff
+ */
+protected void
+/*ARGSUSED*/
+prompt_end(EditLine *el)
+{
+}
+
+
+/* prompt_set():
+ * Install a prompt printing function
+ */
+protected int
+prompt_set(EditLine *el, el_pfunc_t prf, int op)
+{
+ el_prompt_t *p;
+
+ if (op == EL_PROMPT)
+ p = &el->el_prompt;
+ else
+ p = &el->el_rprompt;
+ if (prf == NULL) {
+ if (op == EL_PROMPT)
+ p->p_func = prompt_default;
+ else
+ p->p_func = prompt_default_r;
+ } else
+ p->p_func = prf;
+ p->p_pos.v = 0;
+ p->p_pos.h = 0;
+ return (0);
+}
+
+
+/* prompt_get():
+ * Retrieve the prompt printing function
+ */
+protected int
+prompt_get(EditLine *el, el_pfunc_t *prf, int op)
+{
+
+ if (prf == NULL)
+ return (-1);
+ if (op == EL_PROMPT)
+ *prf = el->el_prompt.p_func;
+ else
+ *prf = el->el_rprompt.p_func;
+ return (0);
+}
diff --git a/trunk/main/editline/prompt.h b/trunk/main/editline/prompt.h
new file mode 100644
index 000000000..08810e22a
--- /dev/null
+++ b/trunk/main/editline/prompt.h
@@ -0,0 +1,62 @@
+/* $NetBSD: prompt.h,v 1.5 2000/09/04 22:06:31 lukem Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)prompt.h 8.1 (Berkeley) 6/4/93
+ */
+
+/*
+ * el.prompt.h: Prompt printing stuff
+ */
+#ifndef _h_el_prompt
+#define _h_el_prompt
+
+#include "histedit.h"
+
+typedef char * (*el_pfunc_t)(EditLine*);
+
+typedef struct el_prompt_t {
+ el_pfunc_t p_func; /* Function to return the prompt */
+ coord_t p_pos; /* position in the line after prompt */
+} el_prompt_t;
+
+protected void prompt_print(EditLine *, int);
+protected int prompt_set(EditLine *, el_pfunc_t, int);
+protected int prompt_get(EditLine *, el_pfunc_t *, int);
+protected int prompt_init(EditLine *);
+protected void prompt_end(EditLine *);
+
+#endif /* _h_el_prompt */
diff --git a/trunk/main/editline/read.c b/trunk/main/editline/read.c
new file mode 100644
index 000000000..ccd0a06e5
--- /dev/null
+++ b/trunk/main/editline/read.c
@@ -0,0 +1,555 @@
+/* $NetBSD: read.c,v 1.21 2002/03/18 16:00:57 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+#if !defined(lint) && !defined(SCCSID)
+#if 0
+static char sccsid[] = "@(#)read.c 8.1 (Berkeley) 6/4/93";
+#else
+__RCSID("$NetBSD: read.c,v 1.21 2002/03/18 16:00:57 christos Exp $");
+#endif
+#endif /* not lint && not SCCSID */
+
+/*
+ * read.c: Clean this junk up! This is horrible code.
+ * Terminal read functions
+ */
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include "el.h"
+
+#define OKCMD -1
+
+private int read__fixio(int, int);
+private int read_preread(EditLine *);
+private int read_char(EditLine *, char *);
+private int read_getcmd(EditLine *, el_action_t *, char *);
+
+/* read_init():
+ * Initialize the read stuff
+ */
+protected int
+read_init(EditLine *el)
+{
+ /* builtin read_char */
+ el->el_read.read_char = read_char;
+ return 0;
+}
+
+
+/* el_read_setfn():
+ * Set the read char function to the one provided.
+ * If it is set to EL_BUILTIN_GETCFN, then reset to the builtin one.
+ */
+protected int
+el_read_setfn(EditLine *el, el_rfunc_t rc)
+{
+ el->el_read.read_char = (rc == EL_BUILTIN_GETCFN) ? read_char : rc;
+ return 0;
+}
+
+
+/* el_read_getfn():
+ * return the current read char function, or EL_BUILTIN_GETCFN
+ * if it is the default one
+ */
+protected el_rfunc_t
+el_read_getfn(EditLine *el)
+{
+ return (el->el_read.read_char == read_char) ?
+ EL_BUILTIN_GETCFN : el->el_read.read_char;
+}
+
+
+#ifdef DEBUG_EDIT
+private void
+read_debug(EditLine *el)
+{
+
+ if (el->el_line.cursor > el->el_line.lastchar)
+ (void) fprintf(el->el_errfile, "cursor > lastchar\r\n");
+ if (el->el_line.cursor < el->el_line.buffer)
+ (void) fprintf(el->el_errfile, "cursor < buffer\r\n");
+ if (el->el_line.cursor > el->el_line.limit)
+ (void) fprintf(el->el_errfile, "cursor > limit\r\n");
+ if (el->el_line.lastchar > el->el_line.limit)
+ (void) fprintf(el->el_errfile, "lastchar > limit\r\n");
+ if (el->el_line.limit != &el->el_line.buffer[EL_BUFSIZ - 2])
+ (void) fprintf(el->el_errfile, "limit != &buffer[EL_BUFSIZ-2]\r\n");
+}
+#endif /* DEBUG_EDIT */
+
+
+/* read__fixio():
+ * Try to recover from a read error
+ */
+/* ARGSUSED */
+private int
+read__fixio(int fd, int e)
+{
+
+ switch (e) {
+ case -1: /* Make sure that the code is reachable */
+
+#ifdef EWOULDBLOCK
+ case EWOULDBLOCK:
+#ifndef TRY_AGAIN
+#define TRY_AGAIN
+#endif
+#endif /* EWOULDBLOCK */
+
+#if defined(POSIX) && defined(EAGAIN)
+#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
+ case EAGAIN:
+#ifndef TRY_AGAIN
+#define TRY_AGAIN
+#endif
+#endif /* EWOULDBLOCK && EWOULDBLOCK != EAGAIN */
+#endif /* POSIX && EAGAIN */
+
+ e = 0;
+#ifdef TRY_AGAIN
+#if defined(F_SETFL) && defined(O_NDELAY)
+ if ((e = fcntl(fd, F_GETFL, 0)) == -1)
+ return (-1);
+
+ if (fcntl(fd, F_SETFL, e & ~O_NDELAY) == -1)
+ return (-1);
+ else
+ e = 1;
+#endif /* F_SETFL && O_NDELAY */
+
+#ifdef FIONBIO
+ {
+ int zero = 0;
+
+ if (ioctl(fd, FIONBIO, (ioctl_t) & zero) == -1)
+ return (-1);
+ else
+ e = 1;
+ }
+#endif /* FIONBIO */
+
+#endif /* TRY_AGAIN */
+ return (e ? 0 : -1);
+
+ case EINTR:
+ return (0);
+
+ default:
+ return (-1);
+ }
+}
+
+
+/* read_preread():
+ * Try to read the stuff in the input queue;
+ */
+private int
+read_preread(EditLine *el)
+{
+ int chrs = 0;
+
+ if (el->el_chared.c_macro.nline) {
+ el_free((ptr_t) el->el_chared.c_macro.nline);
+ el->el_chared.c_macro.nline = NULL;
+ }
+ if (el->el_tty.t_mode == ED_IO)
+ return (0);
+
+#ifdef FIONREAD
+ (void) ioctl(el->el_infd, FIONREAD, (ioctl_t) & chrs);
+ if (chrs > 0) {
+ char buf[EL_BUFSIZ];
+
+ chrs = read(el->el_infd, buf,
+ (size_t) MIN(chrs, EL_BUFSIZ - 1));
+ if (chrs > 0) {
+ buf[chrs] = '\0';
+ el->el_chared.c_macro.nline = strdup(buf);
+ el_push(el, el->el_chared.c_macro.nline);
+ }
+ }
+#endif /* FIONREAD */
+
+ return (chrs > 0);
+}
+
+
+/* el_push():
+ * Push a macro
+ */
+public void
+el_push(EditLine *el, char *str)
+{
+ c_macro_t *ma = &el->el_chared.c_macro;
+
+ if (str != NULL && ma->level + 1 < EL_MAXMACRO) {
+ ma->level++;
+ ma->macro[ma->level] = str;
+ } else {
+ term_beep(el);
+ term__flush();
+ }
+}
+
+
+/* read_getcmd():
+ * Return next command from the input stream.
+ */
+private int
+read_getcmd(EditLine *el, el_action_t *cmdnum, char *ch)
+{
+ el_action_t cmd = ED_UNASSIGNED;
+ int num;
+
+ while (cmd == ED_UNASSIGNED || cmd == ED_SEQUENCE_LEAD_IN) {
+ if ((num = el_getc(el, ch)) != 1) /* if EOF or error */
+ return (num);
+
+#ifdef KANJI
+ if ((*ch & 0200)) {
+ el->el_state.metanext = 0;
+ cmd = CcViMap[' '];
+ break;
+ } else
+#endif /* KANJI */
+
+ if (el->el_state.metanext) {
+ el->el_state.metanext = 0;
+ *ch |= 0200;
+ }
+ cmd = el->el_map.current[(unsigned char) *ch];
+ if (cmd == ED_SEQUENCE_LEAD_IN) {
+ key_value_t val;
+ switch (key_get(el, ch, &val)) {
+ case XK_CMD:
+ cmd = val.cmd;
+ break;
+ case XK_STR:
+ el_push(el, val.str);
+ break;
+#ifdef notyet
+ case XK_EXE:
+ /* XXX: In the future to run a user function */
+ RunCommand(val.str);
+ break;
+#endif
+ default:
+ EL_ABORT((el->el_errfile, "Bad XK_ type \n"));
+ break;
+ }
+ }
+ if (el->el_map.alt == NULL)
+ el->el_map.current = el->el_map.key;
+ }
+ *cmdnum = cmd;
+ return (OKCMD);
+}
+
+
+/* read_char():
+ * Read a character from the tty.
+ */
+private int
+read_char(EditLine *el, char *cp)
+{
+ int num_read;
+ int tried = 0;
+
+ while ((num_read = read(el->el_infd, cp, 1)) == -1)
+ if (!tried && read__fixio(el->el_infd, errno) == 0)
+ tried = 1;
+ else {
+ *cp = '\0';
+ return (-1);
+ }
+
+ return (num_read);
+}
+
+
+/* el_getc():
+ * Read a character
+ */
+public int
+el_getc(EditLine *el, char *cp)
+{
+ int num_read;
+ c_macro_t *ma = &el->el_chared.c_macro;
+
+ term__flush();
+ for (;;) {
+ if (ma->level < 0) {
+ if (!read_preread(el))
+ break;
+ }
+ if (ma->level < 0)
+ break;
+
+ if (*ma->macro[ma->level] == 0) {
+ ma->level--;
+ continue;
+ }
+ *cp = *ma->macro[ma->level]++ & 0377;
+ if (*ma->macro[ma->level] == 0) { /* Needed for QuoteMode
+ * On */
+ ma->level--;
+ }
+ return (1);
+ }
+
+#ifdef DEBUG_READ
+ (void) fprintf(el->el_errfile, "Turning raw mode on\n");
+#endif /* DEBUG_READ */
+ if (tty_rawmode(el) < 0)/* make sure the tty is set up correctly */
+ return (0);
+
+#ifdef DEBUG_READ
+ (void) fprintf(el->el_errfile, "Reading a character\n");
+#endif /* DEBUG_READ */
+ num_read = (*el->el_read.read_char)(el, cp);
+#ifdef DEBUG_READ
+ (void) fprintf(el->el_errfile, "Got it %c\n", *cp);
+#endif /* DEBUG_READ */
+ return (num_read);
+}
+
+
+public const char *
+el_gets(EditLine *el, int *nread)
+{
+ int retval;
+ el_action_t cmdnum = 0;
+ int num; /* how many chars we have read at NL */
+ char ch;
+#ifdef FIONREAD
+ c_macro_t *ma = &el->el_chared.c_macro;
+#endif /* FIONREAD */
+
+ if (el->el_flags & HANDLE_SIGNALS)
+ sig_set(el);
+
+ if (el->el_flags & NO_TTY) {
+ char *cp = el->el_line.buffer;
+ size_t idx;
+
+ while ((*el->el_read.read_char)(el, cp) == 1) {
+ /* make sure there is space for next character */
+ if (cp + 1 >= el->el_line.limit) {
+ idx = (cp - el->el_line.buffer);
+ if (!ch_enlargebufs(el, 2))
+ break;
+ cp = &el->el_line.buffer[idx];
+ }
+ cp++;
+ if (cp[-1] == '\r' || cp[-1] == '\n')
+ break;
+ }
+
+ el->el_line.cursor = el->el_line.lastchar = cp;
+ *cp = '\0';
+ if (nread)
+ *nread = el->el_line.cursor - el->el_line.buffer;
+ return (el->el_line.buffer);
+ }
+ re_clear_display(el); /* reset the display stuff */
+ ch_reset(el);
+
+#ifdef FIONREAD
+ if (el->el_tty.t_mode == EX_IO && ma->level < 0) {
+ long chrs = 0;
+
+ (void) ioctl(el->el_infd, FIONREAD, (ioctl_t) & chrs);
+ if (chrs == 0) {
+ if (tty_rawmode(el) < 0) {
+ if (nread)
+ *nread = 0;
+ return (NULL);
+ }
+ }
+ }
+#endif /* FIONREAD */
+
+ re_refresh(el); /* print the prompt */
+
+ if (el->el_flags & EDIT_DISABLED) {
+ char *cp = el->el_line.buffer;
+ size_t idx;
+
+ term__flush();
+
+ while ((*el->el_read.read_char)(el, cp) == 1) {
+ /* make sure there is space next character */
+ if (cp + 1 >= el->el_line.limit) {
+ idx = (cp - el->el_line.buffer);
+ if (!ch_enlargebufs(el, 2))
+ break;
+ cp = &el->el_line.buffer[idx];
+ }
+ cp++;
+ if (cp[-1] == '\r' || cp[-1] == '\n')
+ break;
+ }
+
+ el->el_line.cursor = el->el_line.lastchar = cp;
+ *cp = '\0';
+ if (nread)
+ *nread = el->el_line.cursor - el->el_line.buffer;
+ return (el->el_line.buffer);
+ }
+ for (num = OKCMD; num == OKCMD;) { /* while still editing this
+ * line */
+#ifdef DEBUG_EDIT
+ read_debug(el);
+#endif /* DEBUG_EDIT */
+ /* if EOF or error */
+ if ((num = read_getcmd(el, &cmdnum, &ch)) != OKCMD) {
+#ifdef DEBUG_READ
+ (void) fprintf(el->el_errfile,
+ "Returning from el_gets %d\n", num);
+#endif /* DEBUG_READ */
+ break;
+ }
+ if ((int) cmdnum >= el->el_map.nfunc) { /* BUG CHECK command */
+#ifdef DEBUG_EDIT
+ (void) fprintf(el->el_errfile,
+ "ERROR: illegal command from key 0%o\r\n", ch);
+#endif /* DEBUG_EDIT */
+ continue; /* try again */
+ }
+ /* now do the real command */
+#ifdef DEBUG_READ
+ {
+ el_bindings_t *b;
+ for (b = el->el_map.help; b->name; b++)
+ if (b->func == cmdnum)
+ break;
+ if (b->name)
+ (void) fprintf(el->el_errfile,
+ "Executing %s\n", b->name);
+ else
+ (void) fprintf(el->el_errfile,
+ "Error command = %d\n", cmdnum);
+ }
+#endif /* DEBUG_READ */
+ retval = (*el->el_map.func[cmdnum]) (el, ch);
+
+ /* save the last command here */
+ el->el_state.lastcmd = cmdnum;
+
+ /* use any return value */
+ switch (retval) {
+ case CC_CURSOR:
+ el->el_state.argument = 1;
+ el->el_state.doingarg = 0;
+ re_refresh_cursor(el);
+ break;
+
+ case CC_REDISPLAY:
+ re_clear_lines(el);
+ re_clear_display(el);
+ /* FALLTHROUGH */
+
+ case CC_REFRESH:
+ el->el_state.argument = 1;
+ el->el_state.doingarg = 0;
+ re_refresh(el);
+ break;
+
+ case CC_REFRESH_BEEP:
+ el->el_state.argument = 1;
+ el->el_state.doingarg = 0;
+ re_refresh(el);
+ term_beep(el);
+ break;
+
+ case CC_NORM: /* normal char */
+ el->el_state.argument = 1;
+ el->el_state.doingarg = 0;
+ break;
+
+ case CC_ARGHACK: /* Suggested by Rich Salz */
+ /* <rsalz@pineapple.bbn.com> */
+ break; /* keep going... */
+
+ case CC_EOF: /* end of file typed */
+ num = 0;
+ break;
+
+ case CC_NEWLINE: /* normal end of line */
+ num = el->el_line.lastchar - el->el_line.buffer;
+ break;
+
+ case CC_FATAL: /* fatal error, reset to known state */
+#ifdef DEBUG_READ
+ (void) fprintf(el->el_errfile,
+ "*** editor fatal ERROR ***\r\n\n");
+#endif /* DEBUG_READ */
+ /* put (real) cursor in a known place */
+ re_clear_display(el); /* reset the display stuff */
+ ch_reset(el); /* reset the input pointers */
+ re_refresh(el); /* print the prompt again */
+ el->el_state.argument = 1;
+ el->el_state.doingarg = 0;
+ break;
+
+ case CC_ERROR:
+ default: /* functions we don't know about */
+#ifdef DEBUG_READ
+ (void) fprintf(el->el_errfile,
+ "*** editor ERROR ***\r\n\n");
+#endif /* DEBUG_READ */
+ el->el_state.argument = 1;
+ el->el_state.doingarg = 0;
+ term_beep(el);
+ term__flush();
+ break;
+ }
+ }
+
+ /* make sure the tty is set up correctly */
+ (void) tty_cookedmode(el);
+ term__flush(); /* flush any buffered output */
+ if (el->el_flags & HANDLE_SIGNALS)
+ sig_clr(el);
+ if (nread)
+ *nread = num;
+ return (num ? el->el_line.buffer : NULL);
+}
diff --git a/trunk/main/editline/read.h b/trunk/main/editline/read.h
new file mode 100644
index 000000000..b01e77db2
--- /dev/null
+++ b/trunk/main/editline/read.h
@@ -0,0 +1,55 @@
+/* $NetBSD: read.h,v 1.1 2001/09/27 19:29:50 christos Exp $ */
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Anthony Mallet.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * el.read.h: Character reading functions
+ */
+#ifndef _h_el_read
+#define _h_el_read
+
+typedef int (*el_rfunc_t)(EditLine *, char *);
+
+typedef struct el_read_t {
+ el_rfunc_t read_char; /* Function to read a character */
+} el_read_t;
+
+protected int read_init(EditLine *);
+protected int el_read_setfn(EditLine *, el_rfunc_t);
+protected el_rfunc_t el_read_getfn(EditLine *);
+
+#endif /* _h_el_read */
diff --git a/trunk/main/editline/readline.c b/trunk/main/editline/readline.c
new file mode 100644
index 000000000..3fbbb79a5
--- /dev/null
+++ b/trunk/main/editline/readline.c
@@ -0,0 +1,1665 @@
+/* $NetBSD: readline.c,v 1.21 2002/03/18 16:20:36 christos Exp $ */
+
+/*-
+ * Copyright (c) 1997 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jaromir Dolecek.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#if !defined(lint) && !defined(SCCSID)
+__RCSID("$NetBSD: readline.c,v 1.21 2002/03/18 16:20:36 christos Exp $");
+#endif /* not lint && not SCCSID */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <string.h>
+#include <pwd.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <limits.h>
+
+#ifdef SOLARIS
+#include <alloca.h>
+#endif
+
+#include "histedit.h"
+#include "readline/readline.h"
+#include "el.h"
+#include "fcns.h" /* for EL_NUM_FCNS */
+
+/* for rl_complete() */
+#define TAB '\r'
+
+/* see comment at the #ifdef for sense of this */
+#define GDB_411_HACK
+
+/* readline compatibility stuff - look at readline sources/documentation */
+/* to see what these variables mean */
+const char *rl_library_version = "EditLine wrapper";
+static char empty[] = { '\0' };
+static char expand_chars[] = { ' ', '\t', '\n', '=', '(', '\0' };
+static char break_chars[] = { ' ', '\t', '\n', '"', '\\', '\'', '`', '@', '$',
+ '>', '<', '=', ';', '|', '&', '{', '(', '\0' };
+char *rl_readline_name = empty;
+FILE *rl_instream = NULL;
+FILE *rl_outstream = NULL;
+int rl_point = 0;
+int rl_end = 0;
+char *rl_line_buffer = NULL;
+
+int history_base = 1; /* probably never subject to change */
+int history_length = 0;
+int max_input_history = 0;
+char history_expansion_char = '!';
+char history_subst_char = '^';
+char *history_no_expand_chars = expand_chars;
+Function *history_inhibit_expansion_function = NULL;
+
+int rl_inhibit_completion = 0;
+int rl_attempted_completion_over = 0;
+char *rl_basic_word_break_characters = break_chars;
+char *rl_completer_word_break_characters = NULL;
+char *rl_completer_quote_characters = NULL;
+CPFunction *rl_completion_entry_function = NULL;
+CPPFunction *rl_attempted_completion_function = NULL;
+
+/*
+ * This is set to character indicating type of completion being done by
+ * rl_complete_internal(); this is available for application completion
+ * functions.
+ */
+int rl_completion_type = 0;
+
+/*
+ * If more than this number of items results from query for possible
+ * completions, we ask user if they are sure to really display the list.
+ */
+int rl_completion_query_items = 100;
+
+/*
+ * List of characters which are word break characters, but should be left
+ * in the parsed text when it is passed to the completion function.
+ * Shell uses this to help determine what kind of completing to do.
+ */
+char *rl_special_prefixes = (char *)NULL;
+
+/*
+ * This is the character appended to the completed words if at the end of
+ * the line. Default is ' ' (a space).
+ */
+int rl_completion_append_character = ' ';
+
+/* stuff below is used internally by libedit for readline emulation */
+
+/* if not zero, non-unique completions always show list of possible matches */
+static int _rl_complete_show_all = 0;
+
+static History *h = NULL;
+static EditLine *e = NULL;
+static int el_rl_complete_cmdnum = 0;
+
+/* internal functions */
+static unsigned char _el_rl_complete(EditLine *, int);
+static char *_get_prompt(EditLine *);
+static HIST_ENTRY *_move_history(int);
+static int _history_search_gen(const char *, int, int);
+static int _history_expand_command(const char *, size_t, char **);
+static char *_rl_compat_sub(const char *, const char *,
+ const char *, int);
+static int rl_complete_internal(int);
+static int _rl_qsort_string_compare(const void *, const void *);
+
+/*
+ * needed for prompt switching in readline()
+ */
+static char *el_rl_prompt = NULL;
+
+
+/* ARGSUSED */
+static char *
+_get_prompt(EditLine *el)
+{
+ return (el_rl_prompt);
+}
+
+
+/*
+ * generic function for moving around history
+ */
+static HIST_ENTRY *
+_move_history(int op)
+{
+ HistEvent ev;
+ static HIST_ENTRY rl_he;
+
+ if (history(h, &ev, op) != 0)
+ return (HIST_ENTRY *) NULL;
+
+ rl_he.line = ev.str;
+ rl_he.data = "";
+
+ return (&rl_he);
+}
+
+
+/*
+ * READLINE compatibility stuff
+ */
+
+/*
+ * initialize rl compat stuff
+ */
+int
+rl_initialize(void)
+{
+ HistEvent ev;
+ const LineInfo *li;
+ int i;
+ int editmode = 1;
+ struct termios t;
+
+ if (e != NULL)
+ el_end(e);
+ if (h != NULL)
+ history_end(h);
+
+ if (!rl_instream)
+ rl_instream = stdin;
+ if (!rl_outstream)
+ rl_outstream = stdout;
+
+ /*
+ * See if we don't really want to run the editor
+ */
+ if (tcgetattr(fileno(rl_instream), &t) != -1 && (t.c_lflag & ECHO) == 0)
+ editmode = 0;
+
+ e = el_init(rl_readline_name, rl_instream, rl_outstream, stderr);
+
+ if (!editmode)
+ el_set(e, EL_EDITMODE, 0);
+
+ h = history_init();
+ if (!e || !h)
+ return (-1);
+
+ history(h, &ev, H_SETSIZE, INT_MAX); /* unlimited */
+ history_length = 0;
+ max_input_history = INT_MAX;
+ el_set(e, EL_HIST, history, h);
+
+ /* for proper prompt printing in readline() */
+ el_rl_prompt = strdup("");
+ el_set(e, EL_PROMPT, _get_prompt);
+ el_set(e, EL_SIGNAL, 1);
+
+ /* set default mode to "emacs"-style and read setting afterwards */
+ /* so this can be overriden */
+ el_set(e, EL_EDITOR, "emacs");
+
+ /*
+ * Word completition - this has to go AFTER rebinding keys
+ * to emacs-style.
+ */
+ el_set(e, EL_ADDFN, "rl_complete",
+ "ReadLine compatible completition function",
+ _el_rl_complete);
+ el_set(e, EL_BIND, "^I", "rl_complete", NULL);
+
+ /*
+ * Find out where the rl_complete function was added; this is
+ * used later to detect that lastcmd was also rl_complete.
+ */
+ for(i=EL_NUM_FCNS; i < e->el_map.nfunc; i++) {
+ if (e->el_map.func[i] == _el_rl_complete) {
+ el_rl_complete_cmdnum = i;
+ break;
+ }
+ }
+
+ /* read settings from configuration file */
+ el_source(e, NULL);
+
+ /*
+ * Unfortunately, some applications really do use rl_point
+ * and rl_line_buffer directly.
+ */
+ li = el_line(e);
+ /* a cheesy way to get rid of const cast. */
+ rl_line_buffer = memchr(li->buffer, *li->buffer, 1);
+ rl_point = rl_end = 0;
+
+ return (0);
+}
+
+
+/*
+ * read one line from input stream and return it, chomping
+ * trailing newline (if there is any)
+ */
+char *
+readline(const char *prompt)
+{
+ HistEvent ev;
+ int count;
+ const char *ret;
+ char *buf;
+
+ if (e == NULL || h == NULL)
+ rl_initialize();
+
+ /* update prompt accordingly to what has been passed */
+ if (!prompt)
+ prompt = "";
+ if (strcmp(el_rl_prompt, prompt) != 0) {
+ free(el_rl_prompt);
+ el_rl_prompt = strdup(prompt);
+ }
+ /* get one line from input stream */
+ ret = el_gets(e, &count);
+
+ if (ret && count > 0) {
+ int lastidx;
+
+ buf = strdup(ret);
+ lastidx = count - 1;
+ if (buf[lastidx] == '\n')
+ buf[lastidx] = '\0';
+ } else
+ buf = NULL;
+
+ history(h, &ev, H_GETSIZE);
+ history_length = ev.num;
+
+ return buf;
+}
+
+/*
+ * history functions
+ */
+
+/*
+ * is normally called before application starts to use
+ * history expansion functions
+ */
+void
+using_history(void)
+{
+ if (h == NULL || e == NULL)
+ rl_initialize();
+}
+
+
+/*
+ * substitute ``what'' with ``with'', returning resulting string; if
+ * globally == 1, substitutes all occurences of what, otherwise only the
+ * first one
+ */
+static char *
+_rl_compat_sub(const char *str, const char *what, const char *with,
+ int globally)
+{
+ char *result;
+ const char *temp, *new;
+ int len, with_len, what_len, add;
+ size_t size, i;
+
+ result = malloc((size = 16));
+ temp = str;
+ with_len = strlen(with);
+ what_len = strlen(what);
+ len = 0;
+ do {
+ new = strstr(temp, what);
+ if (new) {
+ i = new - temp;
+ add = i + with_len;
+ if (i + add + 1 >= size) {
+ size += add + 1;
+ result = realloc(result, size);
+ }
+ (void) strncpy(&result[len], temp, i);
+ len += i;
+ (void) strcpy(&result[len], with); /* safe */
+ len += with_len;
+ temp = new + what_len;
+ } else {
+ add = strlen(temp);
+ if (len + add + 1 >= size) {
+ size += add + 1;
+ result = realloc(result, size);
+ }
+ (void) strcpy(&result[len], temp); /* safe */
+ len += add;
+ temp = NULL;
+ }
+ } while (temp && globally);
+ result[len] = '\0';
+
+ return (result);
+}
+
+
+/*
+ * the real function doing history expansion - takes as argument command
+ * to do and data upon which the command should be executed
+ * does expansion the way I've understood readline documentation
+ * word designator ``%'' isn't supported (yet ?)
+ *
+ * returns 0 if data was not modified, 1 if it was and 2 if the string
+ * should be only printed and not executed; in case of error,
+ * returns -1 and *result points to NULL
+ * it's callers responsibility to free() string returned in *result
+ */
+static int
+_history_expand_command(const char *command, size_t cmdlen, char **result)
+{
+ char **arr, *tempcmd, *line, *search = NULL, *cmd;
+ const char *event_data = NULL;
+ static char *from = NULL, *to = NULL;
+ int start = -1, end = -1, max, i, idx;
+ int h_on = 0, t_on = 0, r_on = 0, e_on = 0, p_on = 0, g_on = 0;
+ int event_num = 0, retval;
+ size_t cmdsize;
+
+ *result = NULL;
+
+ cmd = alloca(cmdlen + 1);
+ (void) strncpy(cmd, command, cmdlen);
+ cmd[cmdlen] = 0;
+
+ idx = 1;
+ /* find out which event to take */
+ if (cmd[idx] == history_expansion_char) {
+ event_num = history_length;
+ idx++;
+ } else {
+ int off, num;
+ size_t len;
+ off = idx;
+ while (cmd[off] && !strchr(":^$*-%", cmd[off]))
+ off++;
+ num = atoi(&cmd[idx]);
+ if (num != 0) {
+ event_num = num;
+ if (num < 0)
+ event_num += history_length + 1;
+ } else {
+ int prefix = 1, curr_num;
+ HistEvent ev;
+
+ len = off - idx;
+ if (cmd[idx] == '?') {
+ idx++, len--;
+ if (cmd[off - 1] == '?')
+ len--;
+ else if (cmd[off] != '\n' && cmd[off] != '\0')
+ return (-1);
+ prefix = 0;
+ }
+ search = alloca(len + 1);
+ (void) strncpy(search, &cmd[idx], len);
+ search[len] = '\0';
+
+ if (history(h, &ev, H_CURR) != 0)
+ return (-1);
+ curr_num = ev.num;
+
+ if (prefix)
+ retval = history_search_prefix(search, -1);
+ else
+ retval = history_search(search, -1);
+
+ if (retval == -1) {
+ fprintf(rl_outstream, "%s: Event not found\n",
+ search);
+ return (-1);
+ }
+ if (history(h, &ev, H_CURR) != 0)
+ return (-1);
+ event_data = ev.str;
+
+ /* roll back to original position */
+ history(h, &ev, H_NEXT_EVENT, curr_num);
+ }
+ idx = off;
+ }
+
+ if (!event_data && event_num >= 0) {
+ HIST_ENTRY *rl_he;
+ rl_he = history_get(event_num);
+ if (!rl_he)
+ return (0);
+ event_data = rl_he->line;
+ } else
+ return (-1);
+
+ if (cmd[idx] != ':')
+ return (-1);
+ cmd += idx + 1;
+
+ /* recognize cmd */
+ if (*cmd == '^')
+ start = end = 1, cmd++;
+ else if (*cmd == '$')
+ start = end = -1, cmd++;
+ else if (*cmd == '*')
+ start = 1, end = -1, cmd++;
+ else if (isdigit((unsigned char) *cmd)) {
+ const char *temp;
+ int shifted = 0;
+
+ start = atoi(cmd);
+ temp = cmd;
+ for (; isdigit((unsigned char) *cmd); cmd++);
+ if (temp != cmd)
+ shifted = 1;
+ if (shifted && *cmd == '-') {
+ if (!isdigit((unsigned char) *(cmd + 1)))
+ end = -2;
+ else {
+ end = atoi(cmd + 1);
+ for (; isdigit((unsigned char) *cmd); cmd++);
+ }
+ } else if (shifted && *cmd == '*')
+ end = -1, cmd++;
+ else if (shifted)
+ end = start;
+ }
+ if (*cmd == ':')
+ cmd++;
+
+ line = strdup(event_data);
+ for (; *cmd; cmd++) {
+ if (*cmd == ':')
+ continue;
+ else if (*cmd == 'h')
+ h_on = 1 | g_on, g_on = 0;
+ else if (*cmd == 't')
+ t_on = 1 | g_on, g_on = 0;
+ else if (*cmd == 'r')
+ r_on = 1 | g_on, g_on = 0;
+ else if (*cmd == 'e')
+ e_on = 1 | g_on, g_on = 0;
+ else if (*cmd == 'p')
+ p_on = 1 | g_on, g_on = 0;
+ else if (*cmd == 'g')
+ g_on = 2;
+ else if (*cmd == 's' || *cmd == '&') {
+ char *what, *with, delim;
+ int len, from_len;
+ size_t size;
+
+ if (*cmd == '&' && (from == NULL || to == NULL))
+ continue;
+ else if (*cmd == 's') {
+ delim = *(++cmd), cmd++;
+ size = 16;
+ what = realloc(from, size);
+ len = 0;
+ for (; *cmd && *cmd != delim; cmd++) {
+ if (*cmd == '\\'
+ && *(cmd + 1) == delim)
+ cmd++;
+ if (len >= size)
+ what = realloc(what,
+ (size <<= 1));
+ what[len++] = *cmd;
+ }
+ what[len] = '\0';
+ from = what;
+ if (*what == '\0') {
+ free(what);
+ if (search)
+ from = strdup(search);
+ else {
+ from = NULL;
+ return (-1);
+ }
+ }
+ cmd++; /* shift after delim */
+ if (!*cmd)
+ continue;
+
+ size = 16;
+ with = realloc(to, size);
+ len = 0;
+ from_len = strlen(from);
+ for (; *cmd && *cmd != delim; cmd++) {
+ if (len + from_len + 1 >= size) {
+ size += from_len + 1;
+ with = realloc(with, size);
+ }
+ if (*cmd == '&') {
+ /* safe */
+ (void) strcpy(&with[len], from);
+ len += from_len;
+ continue;
+ }
+ if (*cmd == '\\'
+ && (*(cmd + 1) == delim
+ || *(cmd + 1) == '&'))
+ cmd++;
+ with[len++] = *cmd;
+ }
+ with[len] = '\0';
+ to = with;
+
+ tempcmd = _rl_compat_sub(line, from, to,
+ (g_on) ? 1 : 0);
+ free(line);
+ line = tempcmd;
+ g_on = 0;
+ }
+ }
+ }
+
+ arr = history_tokenize(line);
+ free(line); /* no more needed */
+ if (arr && *arr == NULL)
+ free(arr), arr = NULL;
+ if (!arr)
+ return (-1);
+
+ /* find out max valid idx to array of array */
+ max = 0;
+ for (i = 0; arr[i]; i++)
+ max++;
+ max--;
+
+ /* set boundaries to something relevant */
+ if (start < 0)
+ start = 1;
+ if (end < 0)
+ end = max - ((end < -1) ? 1 : 0);
+
+ /* check boundaries ... */
+ if (start > max || end > max || start > end)
+ return (-1);
+
+ for (i = 0; i <= max; i++) {
+ char *temp;
+ if (h_on && (i == 1 || h_on > 1) &&
+ (temp = strrchr(arr[i], '/')))
+ *(temp + 1) = '\0';
+ if (t_on && (i == 1 || t_on > 1) &&
+ (temp = strrchr(arr[i], '/')))
+ (void) strcpy(arr[i], temp + 1);
+ if (r_on && (i == 1 || r_on > 1) &&
+ (temp = strrchr(arr[i], '.')))
+ *temp = '\0';
+ if (e_on && (i == 1 || e_on > 1) &&
+ (temp = strrchr(arr[i], '.')))
+ (void) strcpy(arr[i], temp);
+ }
+
+ cmdsize = 1, cmdlen = 0;
+ tempcmd = malloc(cmdsize);
+ for (i = start; start <= i && i <= end; i++) {
+ int arr_len;
+
+ arr_len = strlen(arr[i]);
+ if (cmdlen + arr_len + 1 >= cmdsize) {
+ cmdsize += arr_len + 1;
+ tempcmd = realloc(tempcmd, cmdsize);
+ }
+ (void) strcpy(&tempcmd[cmdlen], arr[i]); /* safe */
+ cmdlen += arr_len;
+ tempcmd[cmdlen++] = ' '; /* add a space */
+ }
+ while (cmdlen > 0 && isspace((unsigned char) tempcmd[cmdlen - 1]))
+ cmdlen--;
+ tempcmd[cmdlen] = '\0';
+
+ *result = tempcmd;
+
+ for (i = 0; i <= max; i++)
+ free(arr[i]);
+ free(arr), arr = (char **) NULL;
+ return (p_on) ? 2 : 1;
+}
+
+
+/*
+ * csh-style history expansion
+ */
+int
+history_expand(char *str, char **output)
+{
+ int i, retval = 0, idx;
+ size_t size;
+ char *temp, *result;
+
+ if (h == NULL || e == NULL)
+ rl_initialize();
+
+ *output = strdup(str); /* do it early */
+
+ if (str[0] == history_subst_char) {
+ /* ^foo^foo2^ is equivalent to !!:s^foo^foo2^ */
+ temp = alloca(4 + strlen(str) + 1);
+ temp[0] = temp[1] = history_expansion_char;
+ temp[2] = ':';
+ temp[3] = 's';
+ (void) strcpy(temp + 4, str);
+ str = temp;
+ }
+#define ADD_STRING(what, len) \
+ { \
+ if (idx + len + 1 > size) \
+ result = realloc(result, (size += len + 1)); \
+ (void)strncpy(&result[idx], what, len); \
+ idx += len; \
+ result[idx] = '\0'; \
+ }
+
+ result = NULL;
+ size = idx = 0;
+ for (i = 0; str[i];) {
+ int start, j, loop_again;
+ size_t len;
+
+ loop_again = 1;
+ start = j = i;
+loop:
+ for (; str[j]; j++) {
+ if (str[j] == '\\' &&
+ str[j + 1] == history_expansion_char) {
+ (void) strcpy(&str[j], &str[j + 1]);
+ continue;
+ }
+ if (!loop_again) {
+ if (str[j] == '?') {
+ while (str[j] && str[++j] != '?');
+ if (str[j] == '?')
+ j++;
+ } else if (isspace((unsigned char) str[j]))
+ break;
+ }
+ if (str[j] == history_expansion_char
+ && !strchr(history_no_expand_chars, str[j + 1])
+ && (!history_inhibit_expansion_function ||
+ (*history_inhibit_expansion_function)(str, j) == 0))
+ break;
+ }
+
+ if (str[j] && str[j + 1] != '#' && loop_again) {
+ i = j;
+ j++;
+ if (str[j] == history_expansion_char)
+ j++;
+ loop_again = 0;
+ goto loop;
+ }
+ len = i - start;
+ temp = &str[start];
+ ADD_STRING(temp, len);
+
+ if (str[i] == '\0' || str[i] != history_expansion_char
+ || str[i + 1] == '#') {
+ len = j - i;
+ temp = &str[i];
+ ADD_STRING(temp, len);
+ if (start == 0)
+ retval = 0;
+ else
+ retval = 1;
+ break;
+ }
+ retval = _history_expand_command(&str[i], (size_t) (j - i),
+ &temp);
+ if (retval != -1) {
+ len = strlen(temp);
+ ADD_STRING(temp, len);
+ }
+ i = j;
+ } /* for(i ...) */
+
+ if (retval == 2) {
+ add_history(temp);
+#ifdef GDB_411_HACK
+ /* gdb 4.11 has been shipped with readline, where */
+ /* history_expand() returned -1 when the line */
+ /* should not be executed; in readline 2.1+ */
+ /* it should return 2 in such a case */
+ retval = -1;
+#endif
+ }
+ free(*output);
+ *output = result;
+
+ return (retval);
+}
+
+
+/*
+ * Parse the string into individual tokens, similarily to how shell would do it.
+ */
+char **
+history_tokenize(const char *str)
+{
+ int size = 1, result_idx = 0, i, start;
+ size_t len;
+ char **result = NULL, *temp, delim = '\0';
+
+ for (i = 0; str[i]; i++) {
+ while (isspace((unsigned char) str[i]))
+ i++;
+ start = i;
+ for (; str[i]; i++) {
+ if (str[i] == '\\') {
+ if (str[i+1] != '\0')
+ i++;
+ } else if (str[i] == delim)
+ delim = '\0';
+ else if (!delim &&
+ (isspace((unsigned char) str[i]) ||
+ strchr("()<>;&|$", str[i])))
+ break;
+ else if (!delim && strchr("'`\"", str[i]))
+ delim = str[i];
+ }
+
+ if (result_idx + 2 >= size) {
+ size <<= 1;
+ result = realloc(result, size * sizeof(char *));
+ }
+ len = i - start;
+ temp = malloc(len + 1);
+ (void) strncpy(temp, &str[start], len);
+ temp[len] = '\0';
+ result[result_idx++] = temp;
+ result[result_idx] = NULL;
+ }
+
+ return (result);
+}
+
+
+/*
+ * limit size of history record to ``max'' events
+ */
+void
+stifle_history(int max)
+{
+ HistEvent ev;
+
+ if (h == NULL || e == NULL)
+ rl_initialize();
+
+ if (history(h, &ev, H_SETSIZE, max) == 0)
+ max_input_history = max;
+}
+
+
+/*
+ * "unlimit" size of history - set the limit to maximum allowed int value
+ */
+int
+unstifle_history(void)
+{
+ HistEvent ev;
+ int omax;
+
+ history(h, &ev, H_SETSIZE, INT_MAX);
+ omax = max_input_history;
+ max_input_history = INT_MAX;
+ return (omax); /* some value _must_ be returned */
+}
+
+
+int
+history_is_stifled(void)
+{
+
+ /* cannot return true answer */
+ return (max_input_history != INT_MAX);
+}
+
+
+/*
+ * read history from a file given
+ */
+int
+read_history(const char *filename)
+{
+ HistEvent ev;
+
+ if (h == NULL || e == NULL)
+ rl_initialize();
+ return (history(h, &ev, H_LOAD, filename));
+}
+
+
+/*
+ * write history to a file given
+ */
+int
+write_history(const char *filename)
+{
+ HistEvent ev;
+
+ if (h == NULL || e == NULL)
+ rl_initialize();
+ return (history(h, &ev, H_SAVE, filename));
+}
+
+
+/*
+ * returns history ``num''th event
+ *
+ * returned pointer points to static variable
+ */
+HIST_ENTRY *
+history_get(int num)
+{
+ static HIST_ENTRY she;
+ HistEvent ev;
+ int i = 1, curr_num;
+
+ if (h == NULL || e == NULL)
+ rl_initialize();
+
+ /* rewind to beginning */
+ if (history(h, &ev, H_CURR) != 0)
+ return (NULL);
+ curr_num = ev.num;
+ if (history(h, &ev, H_LAST) != 0)
+ return (NULL); /* error */
+ while (i < num && history(h, &ev, H_PREV) == 0)
+ i++;
+ if (i != num)
+ return (NULL); /* not so many entries */
+
+ she.line = ev.str;
+ she.data = NULL;
+
+ /* rewind history to the same event it was before */
+ (void) history(h, &ev, H_FIRST);
+ (void) history(h, &ev, H_NEXT_EVENT, curr_num);
+
+ return (&she);
+}
+
+
+/*
+ * add the line to history table
+ */
+int
+add_history(const char *line)
+{
+ HistEvent ev;
+
+ if (h == NULL || e == NULL)
+ rl_initialize();
+
+ (void) history(h, &ev, H_ENTER, line);
+ if (history(h, &ev, H_GETSIZE) == 0)
+ history_length = ev.num;
+
+ return (!(history_length > 0)); /* return 0 if all is okay */
+}
+
+
+/*
+ * clear the history list - delete all entries
+ */
+void
+clear_history(void)
+{
+ HistEvent ev;
+
+ history(h, &ev, H_CLEAR);
+}
+
+
+/*
+ * returns offset of the current history event
+ */
+int
+where_history(void)
+{
+ HistEvent ev;
+ int curr_num, off;
+
+ if (history(h, &ev, H_CURR) != 0)
+ return (0);
+ curr_num = ev.num;
+
+ history(h, &ev, H_FIRST);
+ off = 1;
+ while (ev.num != curr_num && history(h, &ev, H_NEXT) == 0)
+ off++;
+
+ return (off);
+}
+
+
+/*
+ * returns current history event or NULL if there is no such event
+ */
+HIST_ENTRY *
+current_history(void)
+{
+
+ return (_move_history(H_CURR));
+}
+
+
+/*
+ * returns total number of bytes history events' data are using
+ */
+int
+history_total_bytes(void)
+{
+ HistEvent ev;
+ int curr_num, size;
+
+ if (history(h, &ev, H_CURR) != 0)
+ return (-1);
+ curr_num = ev.num;
+
+ history(h, &ev, H_FIRST);
+ size = 0;
+ do
+ size += strlen(ev.str);
+ while (history(h, &ev, H_NEXT) == 0);
+
+ /* get to the same position as before */
+ history(h, &ev, H_PREV_EVENT, curr_num);
+
+ return (size);
+}
+
+
+/*
+ * sets the position in the history list to ``pos''
+ */
+int
+history_set_pos(int pos)
+{
+ HistEvent ev;
+ int off, curr_num;
+
+ if (pos > history_length || pos < 0)
+ return (-1);
+
+ history(h, &ev, H_CURR);
+ curr_num = ev.num;
+ history(h, &ev, H_FIRST);
+ off = 0;
+ while (off < pos && history(h, &ev, H_NEXT) == 0)
+ off++;
+
+ if (off != pos) { /* do a rollback in case of error */
+ history(h, &ev, H_FIRST);
+ history(h, &ev, H_NEXT_EVENT, curr_num);
+ return (-1);
+ }
+ return (0);
+}
+
+
+/*
+ * returns previous event in history and shifts pointer accordingly
+ */
+HIST_ENTRY *
+previous_history(void)
+{
+
+ return (_move_history(H_PREV));
+}
+
+
+/*
+ * returns next event in history and shifts pointer accordingly
+ */
+HIST_ENTRY *
+next_history(void)
+{
+
+ return (_move_history(H_NEXT));
+}
+
+
+/*
+ * generic history search function
+ */
+static int
+_history_search_gen(const char *str, int direction, int pos)
+{
+ HistEvent ev;
+ const char *strp;
+ int curr_num;
+
+ if (history(h, &ev, H_CURR) != 0)
+ return (-1);
+ curr_num = ev.num;
+
+ for (;;) {
+ strp = strstr(ev.str, str);
+ if (strp && (pos < 0 || &ev.str[pos] == strp))
+ return (int) (strp - ev.str);
+ if (history(h, &ev, direction < 0 ? H_PREV : H_NEXT) != 0)
+ break;
+ }
+
+ history(h, &ev, direction < 0 ? H_NEXT_EVENT : H_PREV_EVENT, curr_num);
+
+ return (-1);
+}
+
+
+/*
+ * searches for first history event containing the str
+ */
+int
+history_search(const char *str, int direction)
+{
+
+ return (_history_search_gen(str, direction, -1));
+}
+
+
+/*
+ * searches for first history event beginning with str
+ */
+int
+history_search_prefix(const char *str, int direction)
+{
+
+ return (_history_search_gen(str, direction, 0));
+}
+
+
+/*
+ * search for event in history containing str, starting at offset
+ * abs(pos); continue backward, if pos<0, forward otherwise
+ */
+/* ARGSUSED */
+int
+history_search_pos(const char *str, int direction, int pos)
+{
+ HistEvent ev;
+ int curr_num, off;
+
+ off = (pos > 0) ? pos : -pos;
+ pos = (pos > 0) ? 1 : -1;
+
+ if (history(h, &ev, H_CURR) != 0)
+ return (-1);
+ curr_num = ev.num;
+
+ if (history_set_pos(off) != 0 || history(h, &ev, H_CURR) != 0)
+ return (-1);
+
+
+ for (;;) {
+ if (strstr(ev.str, str))
+ return (off);
+ if (history(h, &ev, (pos < 0) ? H_PREV : H_NEXT) != 0)
+ break;
+ }
+
+ /* set "current" pointer back to previous state */
+ history(h, &ev, (pos < 0) ? H_NEXT_EVENT : H_PREV_EVENT, curr_num);
+
+ return (-1);
+}
+
+
+/********************************/
+/* completition functions */
+
+/*
+ * does tilde expansion of strings of type ``~user/foo''
+ * if ``user'' isn't valid user name or ``txt'' doesn't start
+ * w/ '~', returns pointer to strdup()ed copy of ``txt''
+ *
+ * it's callers's responsibility to free() returned string
+ */
+char *
+tilde_expand(char *txt)
+{
+ struct passwd *pass;
+ char *temp;
+ size_t len = 0;
+
+ if (txt[0] != '~')
+ return (strdup(txt));
+
+ temp = strchr(txt + 1, '/');
+ if (temp == NULL)
+ temp = strdup(txt + 1);
+ else {
+ len = temp - txt + 1; /* text until string after slash */
+ temp = malloc(len);
+ (void) strncpy(temp, txt + 1, len - 2);
+ temp[len - 2] = '\0';
+ }
+ pass = getpwnam(temp);
+ free(temp); /* value no more needed */
+ if (pass == NULL)
+ return (strdup(txt));
+
+ /* update pointer txt to point at string immedially following */
+ /* first slash */
+ txt += len;
+
+ temp = malloc(strlen(pass->pw_dir) + 1 + strlen(txt) + 1);
+ (void) sprintf(temp, "%s/%s", pass->pw_dir, txt);
+
+ return (temp);
+}
+
+
+/*
+ * return first found file name starting by the ``text'' or NULL if no
+ * such file can be found.
+ * The first ``state'' matches are ignored.
+ *
+ * it's caller's responsibility to free returned string
+ */
+char *
+filename_completion_function(const char *text, int state)
+{
+ DIR *dir = NULL;
+ char *filename = NULL, *dirname = NULL;
+ size_t filename_len = 0;
+ struct dirent *entry;
+ char *temp;
+ size_t len;
+ int count = 0;
+
+ temp = strrchr(text, '/');
+ if (temp) {
+ temp++;
+ filename = realloc(filename, strlen(temp) + 1);
+ (void) strcpy(filename, temp);
+ len = temp - text; /* including last slash */
+ dirname = realloc(dirname, len + 1);
+ (void) strncpy(dirname, text, len);
+ dirname[len] = '\0';
+ } else {
+ filename = strdup(text);
+ dirname = NULL;
+ }
+
+ /* support for ``~user'' syntax */
+ if (dirname && *dirname == '~') {
+ temp = tilde_expand(dirname);
+ dirname = realloc(dirname, strlen(temp) + 1);
+ (void) strcpy(dirname, temp); /* safe */
+ free(temp); /* no longer needed */
+ }
+ /* will be used in cycle */
+ filename_len = strlen(filename);
+
+ dir = opendir(dirname ? dirname : ".");
+ if (!dir)
+ return (NULL); /* cannot open the directory */
+
+ /* find the match */
+ while ((entry = readdir(dir)) != NULL) {
+ /* otherwise, get first entry where first */
+ /* filename_len characters are equal */
+ if (
+#if defined(__SVR4) || defined(__linux__)
+ strlen(entry->d_name) >= filename_len
+#else
+ entry->d_namlen >= filename_len
+#endif
+ && strncmp(entry->d_name, filename,
+ filename_len) == 0
+ && (state-- == 0))
+ break;
+ }
+
+ if (entry) { /* match found */
+
+ struct stat stbuf;
+#if defined(__SVR4) || defined(__linux__)
+ len = strlen(entry->d_name) +
+#else
+ len = entry->d_namlen +
+#endif
+ ((dirname) ? strlen(dirname) : 0) + 1 + 1;
+ temp = malloc(len);
+ (void) sprintf(temp, "%s%s",
+ dirname ? dirname : "", entry->d_name); /* safe */
+
+ /* test, if it's directory */
+ if (stat(temp, &stbuf) == 0 && S_ISDIR(stbuf.st_mode))
+ strcat(temp, "/"); /* safe */
+ } else
+ temp = NULL;
+ closedir(dir);
+
+ return (temp);
+}
+
+
+/*
+ * a completion generator for usernames; returns _first_ username
+ * which starts with supplied text
+ * text contains a partial username preceded by random character
+ * (usually '~'); state is ignored
+ * it's callers responsibility to free returned value
+ */
+char *
+username_completion_function(const char *text, int state)
+{
+ struct passwd *pwd;
+
+ if (text[0] == '\0')
+ return (NULL);
+
+ if (*text == '~')
+ text++;
+
+ if (state == 0)
+ setpwent();
+
+ while ((pwd = getpwent()) && text[0] == pwd->pw_name[0]
+ && strcmp(text, pwd->pw_name) == 0);
+
+ if (pwd == NULL) {
+ endpwent();
+ return (NULL);
+ }
+ return (strdup(pwd->pw_name));
+}
+
+
+/*
+ * el-compatible wrapper around rl_complete; needed for key binding
+ */
+/* ARGSUSED */
+static unsigned char
+_el_rl_complete(EditLine *el, int ch)
+{
+ return (unsigned char) rl_complete(0, ch);
+}
+
+
+/*
+ * returns list of completitions for text given
+ */
+char **
+completion_matches(const char *text, CPFunction *genfunc)
+{
+ char **match_list = NULL, *retstr, *prevstr;
+ size_t match_list_len, max_equal, which, i;
+ int matches;
+
+ if (h == NULL || e == NULL)
+ rl_initialize();
+
+ matches = 0;
+ match_list_len = 1;
+ while ((retstr = (*genfunc) (text, matches)) != NULL) {
+ if (matches + 1 >= match_list_len) {
+ match_list_len <<= 1;
+ match_list = realloc(match_list,
+ match_list_len * sizeof(char *));
+ }
+ match_list[++matches] = retstr;
+ }
+
+ if (!match_list)
+ return (char **) NULL; /* nothing found */
+
+ /* find least denominator and insert it to match_list[0] */
+ which = 2;
+ prevstr = match_list[1];
+ max_equal = strlen(prevstr);
+ for (; which <= matches; which++) {
+ for (i = 0; i < max_equal &&
+ prevstr[i] == match_list[which][i]; i++)
+ continue;
+ max_equal = i;
+ }
+
+ retstr = malloc(max_equal + 1);
+ (void) strncpy(retstr, match_list[1], max_equal);
+ retstr[max_equal] = '\0';
+ match_list[0] = retstr;
+
+ /* add NULL as last pointer to the array */
+ if (matches + 1 >= match_list_len)
+ match_list = realloc(match_list,
+ (match_list_len + 1) * sizeof(char *));
+ match_list[matches + 1] = (char *) NULL;
+
+ return (match_list);
+}
+
+/*
+ * Sort function for qsort(). Just wrapper around strcasecmp().
+ */
+static int
+_rl_qsort_string_compare(i1, i2)
+ const void *i1, *i2;
+{
+ /* LINTED const castaway */
+ const char *s1 = ((const char **)i1)[0];
+ /* LINTED const castaway */
+ const char *s2 = ((const char **)i2)[0];
+
+ return strcasecmp(s1, s2);
+}
+
+/*
+ * Display list of strings in columnar format on readline's output stream.
+ * 'matches' is list of strings, 'len' is number of strings in 'matches',
+ * 'max' is maximum length of string in 'matches'.
+ */
+void
+rl_display_match_list (matches, len, max)
+ char **matches;
+ int len, max;
+{
+ int i, idx, limit, count;
+ int screenwidth = e->el_term.t_size.h;
+
+ /*
+ * Find out how many entries can be put on one line, count
+ * with two spaces between strings.
+ */
+ limit = screenwidth / (max + 2);
+ if (limit == 0)
+ limit = 1;
+
+ /* how many lines of output */
+ count = len / limit;
+ if (count * limit < len)
+ count++;
+
+ /* Sort the items if they are not already sorted. */
+ qsort(&matches[1], (size_t)(len - 1), sizeof(char *),
+ _rl_qsort_string_compare);
+
+ idx = 1;
+ for(; count > 0; count--) {
+ for(i=0; i < limit && matches[idx]; i++, idx++)
+ fprintf(e->el_outfile, "%-*s ", max, matches[idx]);
+ fprintf(e->el_outfile, "\n");
+ }
+}
+
+/*
+ * Complete the word at or before point, called by rl_complete()
+ * 'what_to_do' says what to do with the completion.
+ * `?' means list the possible completions.
+ * TAB means do standard completion.
+ * `*' means insert all of the possible completions.
+ * `!' means to do standard completion, and list all possible completions if
+ * there is more than one.
+ *
+ * Note: '*' support is not implemented
+ */
+static int
+rl_complete_internal(int what_to_do)
+{
+ CPFunction *complet_func;
+ const LineInfo *li;
+ char *temp, **matches;
+ const char *ctemp;
+ size_t len;
+
+ rl_completion_type = what_to_do;
+
+ if (h == NULL || e == NULL)
+ rl_initialize();
+
+ complet_func = rl_completion_entry_function;
+ if (!complet_func)
+ complet_func = filename_completion_function;
+
+ /* We now look backwards for the start of a filename/variable word */
+ li = el_line(e);
+ ctemp = (const char *) li->cursor;
+ while (ctemp > li->buffer
+ && !strchr(rl_basic_word_break_characters, ctemp[-1])
+ && (!rl_special_prefixes
+ || !strchr(rl_special_prefixes, ctemp[-1]) ) )
+ ctemp--;
+
+ len = li->cursor - ctemp;
+ temp = alloca(len + 1);
+ (void) strncpy(temp, ctemp, len);
+ temp[len] = '\0';
+
+ /* these can be used by function called in completion_matches() */
+ /* or (*rl_attempted_completion_function)() */
+ rl_point = li->cursor - li->buffer;
+ rl_end = li->lastchar - li->buffer;
+
+ if (!rl_attempted_completion_function)
+ matches = completion_matches(temp, complet_func);
+ else {
+ int end = li->cursor - li->buffer;
+ matches = (*rl_attempted_completion_function) (temp, (int)
+ (end - len), end);
+ }
+
+ if (matches) {
+ int i, retval = CC_REFRESH;
+ int matches_num, maxlen, match_len, match_display=1;
+
+ /*
+ * Only replace the completed string with common part of
+ * possible matches if there is possible completion.
+ */
+ if (matches[0][0] != '\0') {
+ el_deletestr(e, (int) len);
+ el_insertstr(e, matches[0]);
+ }
+
+ if (what_to_do == '?')
+ goto display_matches;
+
+ if (matches[2] == NULL && strcmp(matches[0], matches[1]) == 0) {
+ /*
+ * We found exact match. Add a space after
+ * it, unless we do filename completition and the
+ * object is a directory.
+ */
+ size_t alen = strlen(matches[0]);
+ if ((complet_func != filename_completion_function
+ || (alen > 0 && (matches[0])[alen - 1] != '/'))
+ && rl_completion_append_character) {
+ char buf[2];
+ buf[0] = rl_completion_append_character;
+ buf[1] = '\0';
+ el_insertstr(e, buf);
+ }
+ } else if (what_to_do == '!') {
+ display_matches:
+ /*
+ * More than one match and requested to list possible
+ * matches.
+ */
+
+ for(i=1, maxlen=0; matches[i]; i++) {
+ match_len = strlen(matches[i]);
+ if (match_len > maxlen)
+ maxlen = match_len;
+ }
+ matches_num = i - 1;
+
+ /* newline to get on next line from command line */
+ fprintf(e->el_outfile, "\n");
+
+ /*
+ * If there are too many items, ask user for display
+ * confirmation.
+ */
+ if (matches_num > rl_completion_query_items) {
+ fprintf(e->el_outfile,
+ "Display all %d possibilities? (y or n) ",
+ matches_num);
+ fflush(e->el_outfile);
+ if (getc(stdin) != 'y')
+ match_display = 0;
+ fprintf(e->el_outfile, "\n");
+ }
+
+ if (match_display)
+ rl_display_match_list(matches, matches_num,
+ maxlen);
+ retval = CC_REDISPLAY;
+ } else if (matches[0][0]) {
+ /*
+ * There was some common match, but the name was
+ * not complete enough. Next tab will print possible
+ * completions.
+ */
+ el_beep(e);
+ } else {
+ /* lcd is not a valid object - further specification */
+ /* is needed */
+ el_beep(e);
+ retval = CC_NORM;
+ }
+
+ /* free elements of array and the array itself */
+ for (i = 0; matches[i]; i++)
+ free(matches[i]);
+ free(matches), matches = NULL;
+
+ return (retval);
+ }
+ return (CC_NORM);
+}
+
+
+/*
+ * complete word at current point
+ */
+int
+rl_complete(int ignore, int invoking_key)
+{
+ if (h == NULL || e == NULL)
+ rl_initialize();
+
+ if (rl_inhibit_completion) {
+ rl_insert(ignore, invoking_key);
+ return (CC_REFRESH);
+ } else if (e->el_state.lastcmd == el_rl_complete_cmdnum)
+ return rl_complete_internal('?');
+ else if (_rl_complete_show_all)
+ return rl_complete_internal('!');
+ else
+ return (rl_complete_internal(TAB));
+}
+
+
+/*
+ * misc other functions
+ */
+
+/*
+ * bind key c to readline-type function func
+ */
+int
+rl_bind_key(int c, int func(int, int))
+{
+ int retval = -1;
+
+ if (h == NULL || e == NULL)
+ rl_initialize();
+
+ if (func == rl_insert) {
+ /* XXX notice there is no range checking of ``c'' */
+ e->el_map.key[c] = ED_INSERT;
+ retval = 0;
+ }
+ return (retval);
+}
+
+
+/*
+ * read one key from input - handles chars pushed back
+ * to input stream also
+ */
+int
+rl_read_key(void)
+{
+ char fooarr[2 * sizeof(int)];
+
+ if (e == NULL || h == NULL)
+ rl_initialize();
+
+ return (el_getc(e, fooarr));
+}
+
+
+/*
+ * reset the terminal
+ */
+/* ARGSUSED */
+void
+rl_reset_terminal(const char *p)
+{
+
+ if (h == NULL || e == NULL)
+ rl_initialize();
+ el_reset(e);
+}
+
+
+/*
+ * insert character ``c'' back into input stream, ``count'' times
+ */
+int
+rl_insert(int count, int c)
+{
+ char arr[2];
+
+ if (h == NULL || e == NULL)
+ rl_initialize();
+
+ /* XXX - int -> char conversion can lose on multichars */
+ arr[0] = c;
+ arr[1] = '\0';
+
+ for (; count > 0; count--)
+ el_push(e, arr);
+
+ return (0);
+}
diff --git a/trunk/main/editline/readline/readline.h b/trunk/main/editline/readline/readline.h
new file mode 100644
index 000000000..7485dde40
--- /dev/null
+++ b/trunk/main/editline/readline/readline.h
@@ -0,0 +1,118 @@
+/* $NetBSD: readline.h,v 1.1 2001/01/05 21:15:50 jdolecek Exp $ */
+
+/*-
+ * Copyright (c) 1997 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jaromir Dolecek.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef _READLINE_H_
+#define _READLINE_H_
+
+#include <sys/types.h>
+
+/* list of readline stuff supported by editline library's readline wrapper */
+
+/* typedefs */
+typedef int Function(const char *, int);
+typedef void VFunction(void);
+typedef char *CPFunction(const char *, int);
+typedef char **CPPFunction(const char *, int, int);
+
+typedef struct _hist_entry {
+ const char *line;
+ const char *data;
+} HIST_ENTRY;
+
+/* global variables used by readline enabled applications */
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern const char *rl_library_version;
+extern char *rl_readline_name;
+extern FILE *rl_instream;
+extern FILE *rl_outstream;
+extern char *rl_line_buffer;
+extern int rl_point, rl_end;
+extern int history_base, history_length;
+extern int max_input_history;
+extern char *rl_basic_word_break_characters;
+extern char *rl_completer_word_break_characters;
+extern char *rl_completer_quote_characters;
+extern CPFunction *rl_completion_entry_function;
+extern CPPFunction *rl_attempted_completion_function;
+extern int rl_completion_type;
+extern int rl_completion_query_items;
+extern char *rl_special_prefixes;
+extern int rl_completion_append_character;
+
+/* supported functions */
+char *readline(const char *);
+int rl_initialize(void);
+
+void using_history(void);
+int add_history(const char *);
+void clear_history(void);
+void stifle_history(int);
+int unstifle_history(void);
+int history_is_stifled(void);
+int where_history(void);
+HIST_ENTRY *current_history(void);
+HIST_ENTRY *history_get(int);
+int history_total_bytes(void);
+int history_set_pos(int);
+HIST_ENTRY *previous_history(void);
+HIST_ENTRY *next_history(void);
+int history_search(const char *, int);
+int history_search_prefix(const char *, int);
+int history_search_pos(const char *, int, int);
+int read_history(const char *);
+int write_history(const char *);
+int history_expand(char *, char **);
+char **history_tokenize(const char *);
+
+char *tilde_expand(char *);
+char *filename_completion_function(const char *, int);
+char *username_completion_function(const char *, int);
+int rl_complete(int, int);
+int rl_read_key(void);
+char **completion_matches(const char *, CPFunction *);
+void rl_display_match_list(char **, int, int);
+
+int rl_insert(int, int);
+void rl_reset_terminal(const char *);
+int rl_bind_key(int, int (*)(int, int));
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _READLINE_H_ */
diff --git a/trunk/main/editline/refresh.c b/trunk/main/editline/refresh.c
new file mode 100644
index 000000000..fcebe1253
--- /dev/null
+++ b/trunk/main/editline/refresh.c
@@ -0,0 +1,1104 @@
+/* $NetBSD: refresh.c,v 1.18 2002/03/18 16:00:58 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+#if !defined(lint) && !defined(SCCSID)
+#if 0
+static char sccsid[] = "@(#)refresh.c 8.1 (Berkeley) 6/4/93";
+#else
+__RCSID("$NetBSD: refresh.c,v 1.18 2002/03/18 16:00:58 christos Exp $");
+#endif
+#endif /* not lint && not SCCSID */
+
+/*
+ * refresh.c: Lower level screen refreshing functions
+ */
+#include <stdio.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "el.h"
+
+private void re_addc(EditLine *, int);
+private void re_update_line(EditLine *, char *, char *, int);
+private void re_insert (EditLine *, char *, int, int, char *, int);
+private void re_delete(EditLine *, char *, int, int, int);
+private void re_fastputc(EditLine *, int);
+private void re__strncopy(char *, char *, size_t);
+private void re__copy_and_pad(char *, const char *, size_t);
+
+#ifdef DEBUG_REFRESH
+private void re_printstr(EditLine *, char *, char *, char *);
+#define __F el->el_errfile
+#define ELRE_ASSERT(a, b, c) do \
+ if (a) { \
+ (void) fprintf b; \
+ c; \
+ } \
+ while (0)
+#define ELRE_DEBUG(a, b) ELRE_ASSERT(a,b,;)
+
+/* re_printstr():
+ * Print a string on the debugging pty
+ */
+private void
+re_printstr(EditLine *el, char *str, char *f, char *t)
+{
+
+ ELRE_DEBUG(1, (__F, "%s:\"", str));
+ while (f < t)
+ ELRE_DEBUG(1, (__F, "%c", *f++ & 0177));
+ ELRE_DEBUG(1, (__F, "\"\r\n"));
+}
+#else
+#define ELRE_ASSERT(a, b, c)
+#define ELRE_DEBUG(a, b)
+#endif
+
+
+/* re_addc():
+ * Draw c, expanding tabs, control chars etc.
+ */
+private void
+re_addc(EditLine *el, int c)
+{
+
+ if (isprint(c)) {
+ re_putc(el, c, 1);
+ return;
+ }
+ if (c == '\n') { /* expand the newline */
+ int oldv = el->el_refresh.r_cursor.v;
+ re_putc(el, '\0', 0); /* assure end of line */
+ if (oldv == el->el_refresh.r_cursor.v) { /* XXX */
+ el->el_refresh.r_cursor.h = 0; /* reset cursor pos */
+ el->el_refresh.r_cursor.v++;
+ }
+ return;
+ }
+ if (c == '\t') { /* expand the tab */
+ for (;;) {
+ re_putc(el, ' ', 1);
+ if ((el->el_refresh.r_cursor.h & 07) == 0)
+ break; /* go until tab stop */
+ }
+ } else if (iscntrl(c)) {
+ re_putc(el, '^', 1);
+ if (c == '\177')
+ re_putc(el, '?', 1);
+ else
+ /* uncontrolify it; works only for iso8859-1 like sets */
+ re_putc(el, (c | 0100), 1);
+ } else {
+ re_putc(el, '\\', 1);
+ re_putc(el, (int) ((((unsigned int) c >> 6) & 07) + '0'), 1);
+ re_putc(el, (int) ((((unsigned int) c >> 3) & 07) + '0'), 1);
+ re_putc(el, (c & 07) + '0', 1);
+ }
+}
+
+
+/* re_putc():
+ * Draw the character given
+ */
+protected void
+re_putc(EditLine *el, int c, int shift)
+{
+
+ ELRE_DEBUG(1, (__F, "printing %3.3o '%c'\r\n", c, c));
+
+ el->el_vdisplay[el->el_refresh.r_cursor.v][el->el_refresh.r_cursor.h] = c;
+ if (!shift)
+ return;
+
+ el->el_refresh.r_cursor.h++; /* advance to next place */
+ if (el->el_refresh.r_cursor.h >= el->el_term.t_size.h) {
+ el->el_vdisplay[el->el_refresh.r_cursor.v][el->el_term.t_size.h] = '\0';
+ /* assure end of line */
+ el->el_refresh.r_cursor.h = 0; /* reset it. */
+
+ /*
+ * If we would overflow (input is longer than terminal size),
+ * emulate scroll by dropping first line and shuffling the rest.
+ * We do this via pointer shuffling - it's safe in this case
+ * and we avoid memcpy().
+ */
+ if (el->el_refresh.r_cursor.v + 1 >= el->el_term.t_size.v) {
+ int i, lins = el->el_term.t_size.v;
+ char *firstline = el->el_vdisplay[0];
+
+ for(i=1; i < lins; i++)
+ el->el_vdisplay[i-1] = el->el_vdisplay[i];
+
+ firstline[0] = '\0'; /* empty the string */
+ el->el_vdisplay[i-1] = firstline;
+ } else
+ el->el_refresh.r_cursor.v++;
+
+ ELRE_ASSERT(el->el_refresh.r_cursor.v >= el->el_term.t_size.v,
+ (__F, "\r\nre_putc: overflow! r_cursor.v == %d > %d\r\n",
+ el->el_refresh.r_cursor.v, el->el_term.t_size.v),
+ abort());
+ }
+}
+
+
+/* re_refresh():
+ * draws the new virtual screen image from the current input
+ * line, then goes line-by-line changing the real image to the new
+ * virtual image. The routine to re-draw a line can be replaced
+ * easily in hopes of a smarter one being placed there.
+ */
+protected void
+re_refresh(EditLine *el)
+{
+ int i, rhdiff;
+ char *cp, *st;
+ coord_t cur;
+#ifdef notyet
+ size_t termsz;
+#endif
+
+ ELRE_DEBUG(1, (__F, "el->el_line.buffer = :%s:\r\n",
+ el->el_line.buffer));
+
+ /* reset the Drawing cursor */
+ el->el_refresh.r_cursor.h = 0;
+ el->el_refresh.r_cursor.v = 0;
+
+ /* temporarily draw rprompt to calculate its size */
+ prompt_print(el, EL_RPROMPT);
+
+ /* reset the Drawing cursor */
+ el->el_refresh.r_cursor.h = 0;
+ el->el_refresh.r_cursor.v = 0;
+
+ cur.h = -1; /* set flag in case I'm not set */
+ cur.v = 0;
+
+ prompt_print(el, EL_PROMPT);
+
+ /* draw the current input buffer */
+#ifdef notyet
+ termsz = el->el_term.t_size.h * el->el_term.t_size.v;
+ if (el->el_line.lastchar - el->el_line.buffer > termsz) {
+ /*
+ * If line is longer than terminal, process only part
+ * of line which would influence display.
+ */
+ size_t rem = (el->el_line.lastchar-el->el_line.buffer)%termsz;
+
+ st = el->el_line.lastchar - rem
+ - (termsz - (((rem / el->el_term.t_size.v) - 1)
+ * el->el_term.t_size.v));
+ } else
+#endif
+ st = el->el_line.buffer;
+
+ for (cp = st; cp < el->el_line.lastchar; cp++) {
+ if (cp == el->el_line.cursor) {
+ /* save for later */
+ cur.h = el->el_refresh.r_cursor.h;
+ cur.v = el->el_refresh.r_cursor.v;
+ }
+ re_addc(el, (unsigned char) *cp);
+ }
+
+ if (cur.h == -1) { /* if I haven't been set yet, I'm at the end */
+ cur.h = el->el_refresh.r_cursor.h;
+ cur.v = el->el_refresh.r_cursor.v;
+ }
+ rhdiff = el->el_term.t_size.h - el->el_refresh.r_cursor.h -
+ el->el_rprompt.p_pos.h;
+ if (el->el_rprompt.p_pos.h && !el->el_rprompt.p_pos.v &&
+ !el->el_refresh.r_cursor.v && rhdiff > 1) {
+ /*
+ * have a right-hand side prompt that will fit
+ * on the end of the first line with at least
+ * one character gap to the input buffer.
+ */
+ while (--rhdiff > 0) /* pad out with spaces */
+ re_putc(el, ' ', 1);
+ prompt_print(el, EL_RPROMPT);
+ } else {
+ el->el_rprompt.p_pos.h = 0; /* flag "not using rprompt" */
+ el->el_rprompt.p_pos.v = 0;
+ }
+
+ re_putc(el, '\0', 0); /* make line ended with NUL, no cursor shift */
+
+ el->el_refresh.r_newcv = el->el_refresh.r_cursor.v;
+
+ ELRE_DEBUG(1, (__F,
+ "term.h=%d vcur.h=%d vcur.v=%d vdisplay[0]=\r\n:%80.80s:\r\n",
+ el->el_term.t_size.h, el->el_refresh.r_cursor.h,
+ el->el_refresh.r_cursor.v, el->el_vdisplay[0]));
+
+ ELRE_DEBUG(1, (__F, "updating %d lines.\r\n", el->el_refresh.r_newcv));
+ for (i = 0; i <= el->el_refresh.r_newcv; i++) {
+ /* NOTE THAT re_update_line MAY CHANGE el_display[i] */
+ re_update_line(el, el->el_display[i], el->el_vdisplay[i], i);
+
+ /*
+ * Copy the new line to be the current one, and pad out with
+ * spaces to the full width of the terminal so that if we try
+ * moving the cursor by writing the character that is at the
+ * end of the screen line, it won't be a NUL or some old
+ * leftover stuff.
+ */
+ re__copy_and_pad(el->el_display[i], el->el_vdisplay[i],
+ (size_t) el->el_term.t_size.h);
+ }
+ ELRE_DEBUG(1, (__F,
+ "\r\nel->el_refresh.r_cursor.v=%d,el->el_refresh.r_oldcv=%d i=%d\r\n",
+ el->el_refresh.r_cursor.v, el->el_refresh.r_oldcv, i));
+
+ if (el->el_refresh.r_oldcv > el->el_refresh.r_newcv)
+ for (; i <= el->el_refresh.r_oldcv; i++) {
+ term_move_to_line(el, i);
+ term_move_to_char(el, 0);
+ term_clear_EOL(el, (int) strlen(el->el_display[i]));
+#ifdef DEBUG_REFRESH
+ term_overwrite(el, "C\b", 2);
+#endif /* DEBUG_REFRESH */
+ el->el_display[i][0] = '\0';
+ }
+
+ el->el_refresh.r_oldcv = el->el_refresh.r_newcv; /* set for next time */
+ ELRE_DEBUG(1, (__F,
+ "\r\ncursor.h = %d, cursor.v = %d, cur.h = %d, cur.v = %d\r\n",
+ el->el_refresh.r_cursor.h, el->el_refresh.r_cursor.v,
+ cur.h, cur.v));
+ term_move_to_line(el, cur.v); /* go to where the cursor is */
+ term_move_to_char(el, cur.h);
+}
+
+
+/* re_goto_bottom():
+ * used to go to last used screen line
+ */
+protected void
+re_goto_bottom(EditLine *el)
+{
+
+ term_move_to_line(el, el->el_refresh.r_oldcv);
+ term__putc('\r');
+ term__putc('\n');
+ re_clear_display(el);
+ term__flush();
+}
+
+
+/* re_insert():
+ * insert num characters of s into d (in front of the character)
+ * at dat, maximum length of d is dlen
+ */
+private void
+/*ARGSUSED*/
+re_insert(EditLine *el, char *d, int dat, int dlen, char *s, int num)
+{
+ char *a, *b;
+
+ if (num <= 0)
+ return;
+ if (num > dlen - dat)
+ num = dlen - dat;
+
+ ELRE_DEBUG(1,
+ (__F, "re_insert() starting: %d at %d max %d, d == \"%s\"\n",
+ num, dat, dlen, d));
+ ELRE_DEBUG(1, (__F, "s == \"%s\"n", s));
+
+ /* open up the space for num chars */
+ if (num > 0) {
+ b = d + dlen - 1;
+ a = b - num;
+ while (a >= &d[dat])
+ *b-- = *a--;
+ d[dlen] = '\0'; /* just in case */
+ }
+ ELRE_DEBUG(1, (__F,
+ "re_insert() after insert: %d at %d max %d, d == \"%s\"\n",
+ num, dat, dlen, d));
+ ELRE_DEBUG(1, (__F, "s == \"%s\"n", s));
+
+ /* copy the characters */
+ for (a = d + dat; (a < d + dlen) && (num > 0); num--)
+ *a++ = *s++;
+
+ ELRE_DEBUG(1,
+ (__F, "re_insert() after copy: %d at %d max %d, %s == \"%s\"\n",
+ num, dat, dlen, d, s));
+ ELRE_DEBUG(1, (__F, "s == \"%s\"n", s));
+}
+
+
+/* re_delete():
+ * delete num characters d at dat, maximum length of d is dlen
+ */
+private void
+/*ARGSUSED*/
+re_delete(EditLine *el, char *d, int dat, int dlen, int num)
+{
+ char *a, *b;
+
+ if (num <= 0)
+ return;
+ if (dat + num >= dlen) {
+ d[dat] = '\0';
+ return;
+ }
+ ELRE_DEBUG(1,
+ (__F, "re_delete() starting: %d at %d max %d, d == \"%s\"\n",
+ num, dat, dlen, d));
+
+ /* open up the space for num chars */
+ if (num > 0) {
+ b = d + dat;
+ a = b + num;
+ while (a < &d[dlen])
+ *b++ = *a++;
+ d[dlen] = '\0'; /* just in case */
+ }
+ ELRE_DEBUG(1,
+ (__F, "re_delete() after delete: %d at %d max %d, d == \"%s\"\n",
+ num, dat, dlen, d));
+}
+
+
+/* re__strncopy():
+ * Like strncpy without padding.
+ */
+private void
+re__strncopy(char *a, char *b, size_t n)
+{
+
+ while (n-- && *b)
+ *a++ = *b++;
+}
+
+
+/*****************************************************************
+ re_update_line() is based on finding the middle difference of each line
+ on the screen; vis:
+
+ /old first difference
+ /beginning of line | /old last same /old EOL
+ v v v v
+old: eddie> Oh, my little gruntle-buggy is to me, as lurgid as
+new: eddie> Oh, my little buggy says to me, as lurgid as
+ ^ ^ ^ ^
+ \beginning of line | \new last same \new end of line
+ \new first difference
+
+ all are character pointers for the sake of speed. Special cases for
+ no differences, as well as for end of line additions must be handled.
+**************************************************************** */
+
+/* Minimum at which doing an insert it "worth it". This should be about
+ * half the "cost" of going into insert mode, inserting a character, and
+ * going back out. This should really be calculated from the termcap
+ * data... For the moment, a good number for ANSI terminals.
+ */
+#define MIN_END_KEEP 4
+
+private void
+re_update_line(EditLine *el, char *old, char *new, int i)
+{
+ char *o, *n, *p, c;
+ char *ofd, *ols, *oe, *nfd, *nls, *ne;
+ char *osb, *ose, *nsb, *nse;
+ int fx, sx;
+
+ /*
+ * find first diff
+ */
+ for (o = old, n = new; *o && (*o == *n); o++, n++)
+ continue;
+ ofd = o;
+ nfd = n;
+
+ /*
+ * Find the end of both old and new
+ */
+ while (*o)
+ o++;
+ /*
+ * Remove any trailing blanks off of the end, being careful not to
+ * back up past the beginning.
+ */
+ while (ofd < o) {
+ if (o[-1] != ' ')
+ break;
+ o--;
+ }
+ oe = o;
+ *oe = '\0';
+
+ while (*n)
+ n++;
+
+ /* remove blanks from end of new */
+ while (nfd < n) {
+ if (n[-1] != ' ')
+ break;
+ n--;
+ }
+ ne = n;
+ *ne = '\0';
+
+ /*
+ * if no diff, continue to next line of redraw
+ */
+ if (*ofd == '\0' && *nfd == '\0') {
+ ELRE_DEBUG(1, (__F, "no difference.\r\n"));
+ return;
+ }
+ /*
+ * find last same pointer
+ */
+ while ((o > ofd) && (n > nfd) && (*--o == *--n))
+ continue;
+ ols = ++o;
+ nls = ++n;
+
+ /*
+ * find same begining and same end
+ */
+ osb = ols;
+ nsb = nls;
+ ose = ols;
+ nse = nls;
+
+ /*
+ * case 1: insert: scan from nfd to nls looking for *ofd
+ */
+ if (*ofd) {
+ for (c = *ofd, n = nfd; n < nls; n++) {
+ if (c == *n) {
+ for (o = ofd, p = n;
+ p < nls && o < ols && *o == *p;
+ o++, p++)
+ continue;
+ /*
+ * if the new match is longer and it's worth
+ * keeping, then we take it
+ */
+ if (((nse - nsb) < (p - n)) &&
+ (2 * (p - n) > n - nfd)) {
+ nsb = n;
+ nse = p;
+ osb = ofd;
+ ose = o;
+ }
+ }
+ }
+ }
+ /*
+ * case 2: delete: scan from ofd to ols looking for *nfd
+ */
+ if (*nfd) {
+ for (c = *nfd, o = ofd; o < ols; o++) {
+ if (c == *o) {
+ for (n = nfd, p = o;
+ p < ols && n < nls && *p == *n;
+ p++, n++)
+ continue;
+ /*
+ * if the new match is longer and it's worth
+ * keeping, then we take it
+ */
+ if (((ose - osb) < (p - o)) &&
+ (2 * (p - o) > o - ofd)) {
+ nsb = nfd;
+ nse = n;
+ osb = o;
+ ose = p;
+ }
+ }
+ }
+ }
+ /*
+ * Pragmatics I: If old trailing whitespace or not enough characters to
+ * save to be worth it, then don't save the last same info.
+ */
+ if ((oe - ols) < MIN_END_KEEP) {
+ ols = oe;
+ nls = ne;
+ }
+ /*
+ * Pragmatics II: if the terminal isn't smart enough, make the data
+ * dumber so the smart update doesn't try anything fancy
+ */
+
+ /*
+ * fx is the number of characters we need to insert/delete: in the
+ * beginning to bring the two same begins together
+ */
+ fx = (nsb - nfd) - (osb - ofd);
+ /*
+ * sx is the number of characters we need to insert/delete: in the
+ * end to bring the two same last parts together
+ */
+ sx = (nls - nse) - (ols - ose);
+
+ if (!EL_CAN_INSERT) {
+ if (fx > 0) {
+ osb = ols;
+ ose = ols;
+ nsb = nls;
+ nse = nls;
+ }
+ if (sx > 0) {
+ ols = oe;
+ nls = ne;
+ }
+ if ((ols - ofd) < (nls - nfd)) {
+ ols = oe;
+ nls = ne;
+ }
+ }
+ if (!EL_CAN_DELETE) {
+ if (fx < 0) {
+ osb = ols;
+ ose = ols;
+ nsb = nls;
+ nse = nls;
+ }
+ if (sx < 0) {
+ ols = oe;
+ nls = ne;
+ }
+ if ((ols - ofd) > (nls - nfd)) {
+ ols = oe;
+ nls = ne;
+ }
+ }
+ /*
+ * Pragmatics III: make sure the middle shifted pointers are correct if
+ * they don't point to anything (we may have moved ols or nls).
+ */
+ /* if the change isn't worth it, don't bother */
+ /* was: if (osb == ose) */
+ if ((ose - osb) < MIN_END_KEEP) {
+ osb = ols;
+ ose = ols;
+ nsb = nls;
+ nse = nls;
+ }
+ /*
+ * Now that we are done with pragmatics we recompute fx, sx
+ */
+ fx = (nsb - nfd) - (osb - ofd);
+ sx = (nls - nse) - (ols - ose);
+
+ ELRE_DEBUG(1, (__F, "\n"));
+ ELRE_DEBUG(1, (__F, "ofd %d, osb %d, ose %d, ols %d, oe %d\n",
+ ofd - old, osb - old, ose - old, ols - old, oe - old));
+ ELRE_DEBUG(1, (__F, "nfd %d, nsb %d, nse %d, nls %d, ne %d\n",
+ nfd - new, nsb - new, nse - new, nls - new, ne - new));
+ ELRE_DEBUG(1, (__F,
+ "xxx-xxx:\"00000000001111111111222222222233333333334\"\r\n"));
+ ELRE_DEBUG(1, (__F,
+ "xxx-xxx:\"01234567890123456789012345678901234567890\"\r\n"));
+#ifdef DEBUG_REFRESH
+ re_printstr(el, "old- oe", old, oe);
+ re_printstr(el, "new- ne", new, ne);
+ re_printstr(el, "old-ofd", old, ofd);
+ re_printstr(el, "new-nfd", new, nfd);
+ re_printstr(el, "ofd-osb", ofd, osb);
+ re_printstr(el, "nfd-nsb", nfd, nsb);
+ re_printstr(el, "osb-ose", osb, ose);
+ re_printstr(el, "nsb-nse", nsb, nse);
+ re_printstr(el, "ose-ols", ose, ols);
+ re_printstr(el, "nse-nls", nse, nls);
+ re_printstr(el, "ols- oe", ols, oe);
+ re_printstr(el, "nls- ne", nls, ne);
+#endif /* DEBUG_REFRESH */
+
+ /*
+ * el_cursor.v to this line i MUST be in this routine so that if we
+ * don't have to change the line, we don't move to it. el_cursor.h to
+ * first diff char
+ */
+ term_move_to_line(el, i);
+
+ /*
+ * at this point we have something like this:
+ *
+ * /old /ofd /osb /ose /ols /oe
+ * v.....................v v..................v v........v
+ * eddie> Oh, my fredded gruntle-buggy is to me, as foo var lurgid as
+ * eddie> Oh, my fredded quiux buggy is to me, as gruntle-lurgid as
+ * ^.....................^ ^..................^ ^........^
+ * \new \nfd \nsb \nse \nls \ne
+ *
+ * fx is the difference in length between the chars between nfd and
+ * nsb, and the chars between ofd and osb, and is thus the number of
+ * characters to delete if < 0 (new is shorter than old, as above),
+ * or insert (new is longer than short).
+ *
+ * sx is the same for the second differences.
+ */
+
+ /*
+ * if we have a net insert on the first difference, AND inserting the
+ * net amount ((nsb-nfd) - (osb-ofd)) won't push the last useful
+ * character (which is ne if nls != ne, otherwise is nse) off the edge
+ * of the screen (el->el_term.t_size.h) else we do the deletes first
+ * so that we keep everything we need to.
+ */
+
+ /*
+ * if the last same is the same like the end, there is no last same
+ * part, otherwise we want to keep the last same part set p to the
+ * last useful old character
+ */
+ p = (ols != oe) ? oe : ose;
+
+ /*
+ * if (There is a diffence in the beginning) && (we need to insert
+ * characters) && (the number of characters to insert is less than
+ * the term width)
+ * We need to do an insert!
+ * else if (we need to delete characters)
+ * We need to delete characters!
+ * else
+ * No insert or delete
+ */
+ if ((nsb != nfd) && fx > 0 &&
+ ((p - old) + fx <= el->el_term.t_size.h)) {
+ ELRE_DEBUG(1,
+ (__F, "first diff insert at %d...\r\n", nfd - new));
+ /*
+ * Move to the first char to insert, where the first diff is.
+ */
+ term_move_to_char(el, nfd - new);
+ /*
+ * Check if we have stuff to keep at end
+ */
+ if (nsb != ne) {
+ ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n"));
+ /*
+ * insert fx chars of new starting at nfd
+ */
+ if (fx > 0) {
+ ELRE_DEBUG(!EL_CAN_INSERT, (__F,
+ "ERROR: cannot insert in early first diff\n"));
+ term_insertwrite(el, nfd, fx);
+ re_insert(el, old, ofd - old,
+ el->el_term.t_size.h, nfd, fx);
+ }
+ /*
+ * write (nsb-nfd) - fx chars of new starting at
+ * (nfd + fx)
+ */
+ term_overwrite(el, nfd + fx, (nsb - nfd) - fx);
+ re__strncopy(ofd + fx, nfd + fx,
+ (size_t) ((nsb - nfd) - fx));
+ } else {
+ ELRE_DEBUG(1, (__F, "without anything to save\r\n"));
+ term_overwrite(el, nfd, (nsb - nfd));
+ re__strncopy(ofd, nfd, (size_t) (nsb - nfd));
+ /*
+ * Done
+ */
+ return;
+ }
+ } else if (fx < 0) {
+ ELRE_DEBUG(1,
+ (__F, "first diff delete at %d...\r\n", ofd - old));
+ /*
+ * move to the first char to delete where the first diff is
+ */
+ term_move_to_char(el, ofd - old);
+ /*
+ * Check if we have stuff to save
+ */
+ if (osb != oe) {
+ ELRE_DEBUG(1, (__F, "with stuff to save at end\r\n"));
+ /*
+ * fx is less than zero *always* here but we check
+ * for code symmetry
+ */
+ if (fx < 0) {
+ ELRE_DEBUG(!EL_CAN_DELETE, (__F,
+ "ERROR: cannot delete in first diff\n"));
+ term_deletechars(el, -fx);
+ re_delete(el, old, ofd - old,
+ el->el_term.t_size.h, -fx);
+ }
+ /*
+ * write (nsb-nfd) chars of new starting at nfd
+ */
+ term_overwrite(el, nfd, (nsb - nfd));
+ re__strncopy(ofd, nfd, (size_t) (nsb - nfd));
+
+ } else {
+ ELRE_DEBUG(1, (__F,
+ "but with nothing left to save\r\n"));
+ /*
+ * write (nsb-nfd) chars of new starting at nfd
+ */
+ term_overwrite(el, nfd, (nsb - nfd));
+ ELRE_DEBUG(1, (__F,
+ "cleareol %d\n", (oe - old) - (ne - new)));
+ term_clear_EOL(el, (oe - old) - (ne - new));
+ /*
+ * Done
+ */
+ return;
+ }
+ } else
+ fx = 0;
+
+ if (sx < 0 && (ose - old) + fx < el->el_term.t_size.h) {
+ ELRE_DEBUG(1, (__F,
+ "second diff delete at %d...\r\n", (ose - old) + fx));
+ /*
+ * Check if we have stuff to delete
+ */
+ /*
+ * fx is the number of characters inserted (+) or deleted (-)
+ */
+
+ term_move_to_char(el, (ose - old) + fx);
+ /*
+ * Check if we have stuff to save
+ */
+ if (ols != oe) {
+ ELRE_DEBUG(1, (__F, "with stuff to save at end\r\n"));
+ /*
+ * Again a duplicate test.
+ */
+ if (sx < 0) {
+ ELRE_DEBUG(!EL_CAN_DELETE, (__F,
+ "ERROR: cannot delete in second diff\n"));
+ term_deletechars(el, -sx);
+ }
+ /*
+ * write (nls-nse) chars of new starting at nse
+ */
+ term_overwrite(el, nse, (nls - nse));
+ } else {
+ ELRE_DEBUG(1, (__F,
+ "but with nothing left to save\r\n"));
+ term_overwrite(el, nse, (nls - nse));
+ ELRE_DEBUG(1, (__F,
+ "cleareol %d\n", (oe - old) - (ne - new)));
+ if ((oe - old) - (ne - new) != 0)
+ term_clear_EOL(el, (oe - old) - (ne - new));
+ }
+ }
+ /*
+ * if we have a first insert AND WE HAVEN'T ALREADY DONE IT...
+ */
+ if ((nsb != nfd) && (osb - ofd) <= (nsb - nfd) && (fx == 0)) {
+ ELRE_DEBUG(1, (__F, "late first diff insert at %d...\r\n",
+ nfd - new));
+
+ term_move_to_char(el, nfd - new);
+ /*
+ * Check if we have stuff to keep at the end
+ */
+ if (nsb != ne) {
+ ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n"));
+ /*
+ * We have to recalculate fx here because we set it
+ * to zero above as a flag saying that we hadn't done
+ * an early first insert.
+ */
+ fx = (nsb - nfd) - (osb - ofd);
+ if (fx > 0) {
+ /*
+ * insert fx chars of new starting at nfd
+ */
+ ELRE_DEBUG(!EL_CAN_INSERT, (__F,
+ "ERROR: cannot insert in late first diff\n"));
+ term_insertwrite(el, nfd, fx);
+ re_insert(el, old, ofd - old,
+ el->el_term.t_size.h, nfd, fx);
+ }
+ /*
+ * write (nsb-nfd) - fx chars of new starting at
+ * (nfd + fx)
+ */
+ term_overwrite(el, nfd + fx, (nsb - nfd) - fx);
+ re__strncopy(ofd + fx, nfd + fx,
+ (size_t) ((nsb - nfd) - fx));
+ } else {
+ ELRE_DEBUG(1, (__F, "without anything to save\r\n"));
+ term_overwrite(el, nfd, (nsb - nfd));
+ re__strncopy(ofd, nfd, (size_t) (nsb - nfd));
+ }
+ }
+ /*
+ * line is now NEW up to nse
+ */
+ if (sx >= 0) {
+ ELRE_DEBUG(1, (__F,
+ "second diff insert at %d...\r\n", nse - new));
+ term_move_to_char(el, nse - new);
+ if (ols != oe) {
+ ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n"));
+ if (sx > 0) {
+ /* insert sx chars of new starting at nse */
+ ELRE_DEBUG(!EL_CAN_INSERT, (__F,
+ "ERROR: cannot insert in second diff\n"));
+ term_insertwrite(el, nse, sx);
+ }
+ /*
+ * write (nls-nse) - sx chars of new starting at
+ * (nse + sx)
+ */
+ term_overwrite(el, nse + sx, (nls - nse) - sx);
+ } else {
+ ELRE_DEBUG(1, (__F, "without anything to save\r\n"));
+ term_overwrite(el, nse, (nls - nse));
+
+ /*
+ * No need to do a clear-to-end here because we were
+ * doing a second insert, so we will have over
+ * written all of the old string.
+ */
+ }
+ }
+ ELRE_DEBUG(1, (__F, "done.\r\n"));
+}
+
+
+/* re__copy_and_pad():
+ * Copy string and pad with spaces
+ */
+private void
+re__copy_and_pad(char *dst, const char *src, size_t width)
+{
+ int i;
+
+ for (i = 0; i < width; i++) {
+ if (*src == '\0')
+ break;
+ *dst++ = *src++;
+ }
+
+ for (; i < width; i++)
+ *dst++ = ' ';
+
+ *dst = '\0';
+}
+
+
+/* re_refresh_cursor():
+ * Move to the new cursor position
+ */
+protected void
+re_refresh_cursor(EditLine *el)
+{
+ char *cp, c;
+ int h, v, th;
+
+ /* first we must find where the cursor is... */
+ h = el->el_prompt.p_pos.h;
+ v = el->el_prompt.p_pos.v;
+ th = el->el_term.t_size.h; /* optimize for speed */
+
+ /* do input buffer to el->el_line.cursor */
+ for (cp = el->el_line.buffer; cp < el->el_line.cursor; cp++) {
+ c = *cp;
+ h++; /* all chars at least this long */
+
+ if (c == '\n') {/* handle newline in data part too */
+ h = 0;
+ v++;
+ } else {
+ if (c == '\t') { /* if a tab, to next tab stop */
+ while (h & 07) {
+ h++;
+ }
+ } else if (iscntrl((unsigned char) c)) {
+ /* if control char */
+ h++;
+ if (h > th) { /* if overflow, compensate */
+ h = 1;
+ v++;
+ }
+ } else if (!isprint((unsigned char) c)) {
+ h += 3;
+ if (h > th) { /* if overflow, compensate */
+ h = h - th;
+ v++;
+ }
+ }
+ }
+
+ if (h >= th) { /* check, extra long tabs picked up here also */
+ h = 0;
+ v++;
+ }
+ }
+
+ /* now go there */
+ term_move_to_line(el, v);
+ term_move_to_char(el, h);
+ term__flush();
+}
+
+
+/* re_fastputc():
+ * Add a character fast.
+ */
+private void
+re_fastputc(EditLine *el, int c)
+{
+
+ term__putc(c);
+ el->el_display[el->el_cursor.v][el->el_cursor.h++] = c;
+ if (el->el_cursor.h >= el->el_term.t_size.h) {
+ /* if we must overflow */
+ el->el_cursor.h = 0;
+
+ /*
+ * If we would overflow (input is longer than terminal size),
+ * emulate scroll by dropping first line and shuffling the rest.
+ * We do this via pointer shuffling - it's safe in this case
+ * and we avoid memcpy().
+ */
+ if (el->el_cursor.v + 1 >= el->el_term.t_size.v) {
+ int i, lins = el->el_term.t_size.v;
+ char *firstline = el->el_display[0];
+
+ for(i=1; i < lins; i++)
+ el->el_display[i-1] = el->el_display[i];
+
+ re__copy_and_pad(firstline, "", 0);
+ el->el_display[i-1] = firstline;
+ } else {
+ el->el_cursor.v++;
+ el->el_refresh.r_oldcv++;
+ }
+ if (EL_HAS_AUTO_MARGINS) {
+ if (EL_HAS_MAGIC_MARGINS) {
+ term__putc(' ');
+ term__putc('\b');
+ }
+ } else {
+ term__putc('\r');
+ term__putc('\n');
+ }
+ }
+}
+
+
+/* re_fastaddc():
+ * we added just one char, handle it fast.
+ * Assumes that screen cursor == real cursor
+ */
+protected void
+re_fastaddc(EditLine *el)
+{
+ char c;
+ int rhdiff;
+
+ c = el->el_line.cursor[-1];
+
+ if (c == '\t' || el->el_line.cursor != el->el_line.lastchar) {
+ re_refresh(el); /* too hard to handle */
+ return;
+ }
+ rhdiff = el->el_term.t_size.h - el->el_cursor.h -
+ el->el_rprompt.p_pos.h;
+ if (el->el_rprompt.p_pos.h && rhdiff < 3) {
+ re_refresh(el); /* clear out rprompt if less than 1 char gap */
+ return;
+ } /* else (only do at end of line, no TAB) */
+ if (iscntrl((unsigned char) c)) { /* if control char, do caret */
+ char mc = (c == '\177') ? '?' : (c | 0100);
+ re_fastputc(el, '^');
+ re_fastputc(el, mc);
+ } else if (isprint((unsigned char) c)) { /* normal char */
+ re_fastputc(el, c);
+ } else {
+ re_fastputc(el, '\\');
+ re_fastputc(el, (int) ((((unsigned int) c >> 6) & 7) + '0'));
+ re_fastputc(el, (int) ((((unsigned int) c >> 3) & 7) + '0'));
+ re_fastputc(el, (c & 7) + '0');
+ }
+ term__flush();
+}
+
+
+/* re_clear_display():
+ * clear the screen buffers so that new new prompt starts fresh.
+ */
+protected void
+re_clear_display(EditLine *el)
+{
+ int i;
+
+ el->el_cursor.v = 0;
+ el->el_cursor.h = 0;
+ for (i = 0; i < el->el_term.t_size.v; i++)
+ el->el_display[i][0] = '\0';
+ el->el_refresh.r_oldcv = 0;
+}
+
+
+/* re_clear_lines():
+ * Make sure all lines are *really* blank
+ */
+protected void
+re_clear_lines(EditLine *el)
+{
+
+ if (EL_CAN_CEOL) {
+ int i;
+ term_move_to_char(el, 0);
+ for (i = 0; i <= el->el_refresh.r_oldcv; i++) {
+ /* for each line on the screen */
+ term_move_to_line(el, i);
+ term_clear_EOL(el, el->el_term.t_size.h);
+ }
+ term_move_to_line(el, 0);
+ } else {
+ term_move_to_line(el, el->el_refresh.r_oldcv);
+ /* go to last line */
+ term__putc('\r'); /* go to BOL */
+ term__putc('\n'); /* go to new line */
+ }
+}
diff --git a/trunk/main/editline/refresh.h b/trunk/main/editline/refresh.h
new file mode 100644
index 000000000..33c0887c1
--- /dev/null
+++ b/trunk/main/editline/refresh.h
@@ -0,0 +1,63 @@
+/* $NetBSD: refresh.h,v 1.4 2001/01/10 07:45:42 jdolecek Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)refresh.h 8.1 (Berkeley) 6/4/93
+ */
+
+/*
+ * el.refresh.h: Screen refresh functions
+ */
+#ifndef _h_el_refresh
+#define _h_el_refresh
+
+#include "histedit.h"
+
+typedef struct {
+ coord_t r_cursor; /* Refresh cursor position */
+ int r_oldcv; /* Vertical locations */
+ int r_newcv;
+} el_refresh_t;
+
+protected void re_putc(EditLine *, int, int);
+protected void re_clear_lines(EditLine *);
+protected void re_clear_display(EditLine *);
+protected void re_refresh(EditLine *);
+protected void re_refresh_cursor(EditLine *);
+protected void re_fastaddc(EditLine *);
+protected void re_goto_bottom(EditLine *);
+
+#endif /* _h_el_refresh */
diff --git a/trunk/main/editline/search.c b/trunk/main/editline/search.c
new file mode 100644
index 000000000..7c1cb84ef
--- /dev/null
+++ b/trunk/main/editline/search.c
@@ -0,0 +1,649 @@
+/* $NetBSD: search.c,v 1.12 2002/03/18 16:00:58 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+#if !defined(lint) && !defined(SCCSID)
+#if 0
+static char sccsid[] = "@(#)search.c 8.1 (Berkeley) 6/4/93";
+#else
+__RCSID("$NetBSD: search.c,v 1.12 2002/03/18 16:00:58 christos Exp $");
+#endif
+#endif /* not lint && not SCCSID */
+
+/*
+ * search.c: History and character search functions
+ */
+#include <stdlib.h>
+#if defined(REGEX)
+#include <regex.h>
+#elif defined(REGEXP)
+#include <regexp.h>
+#endif
+#include "el.h"
+
+/*
+ * Adjust cursor in vi mode to include the character under it
+ */
+#define EL_CURSOR(el) \
+ ((el)->el_line.cursor + (((el)->el_map.type == MAP_VI) && \
+ ((el)->el_map.current == (el)->el_map.alt)))
+
+/* search_init():
+ * Initialize the search stuff
+ */
+protected int
+search_init(EditLine *el)
+{
+
+ el->el_search.patbuf = (char *) el_malloc(EL_BUFSIZ);
+ if (el->el_search.patbuf == NULL)
+ return (-1);
+ el->el_search.patlen = 0;
+ el->el_search.patdir = -1;
+ el->el_search.chacha = '\0';
+ el->el_search.chadir = -1;
+ return (0);
+}
+
+
+/* search_end():
+ * Initialize the search stuff
+ */
+protected void
+search_end(EditLine *el)
+{
+
+ el_free((ptr_t) el->el_search.patbuf);
+ el->el_search.patbuf = NULL;
+}
+
+
+#ifdef REGEXP
+/* regerror():
+ * Handle regular expression errors
+ */
+public void
+/*ARGSUSED*/
+regerror(const char *msg)
+{
+}
+#endif
+
+
+/* el_match():
+ * Return if string matches pattern
+ */
+protected int
+el_match(const char *str, const char *pat)
+{
+#if defined (REGEX)
+ regex_t re;
+ int rv;
+#elif defined (REGEXP)
+ regexp *rp;
+ int rv;
+#else
+ extern char *re_comp(const char *);
+ extern int re_exec(const char *);
+#endif
+
+ if (strstr(str, pat) != NULL)
+ return (1);
+
+#if defined(REGEX)
+ if (regcomp(&re, pat, 0) == 0) {
+ rv = regexec(&re, str, 0, NULL, 0) == 0;
+ regfree(&re);
+ } else {
+ rv = 0;
+ }
+ return (rv);
+#elif defined(REGEXP)
+ if ((re = regcomp(pat)) != NULL) {
+ rv = regexec(re, str);
+ free((ptr_t) re);
+ } else {
+ rv = 0;
+ }
+ return (rv);
+#else
+ if (re_comp(pat) != NULL)
+ return (0);
+ else
+ return (re_exec(str) == 1);
+#endif
+}
+
+
+/* c_hmatch():
+ * return True if the pattern matches the prefix
+ */
+protected int
+c_hmatch(EditLine *el, const char *str)
+{
+#ifdef SDEBUG
+ (void) fprintf(el->el_errfile, "match `%s' with `%s'\n",
+ el->el_search.patbuf, str);
+#endif /* SDEBUG */
+
+ return (el_match(str, el->el_search.patbuf));
+}
+
+
+/* c_setpat():
+ * Set the history seatch pattern
+ */
+protected void
+c_setpat(EditLine *el)
+{
+ if (el->el_state.lastcmd != ED_SEARCH_PREV_HISTORY &&
+ el->el_state.lastcmd != ED_SEARCH_NEXT_HISTORY) {
+ el->el_search.patlen = EL_CURSOR(el) - el->el_line.buffer;
+ if (el->el_search.patlen >= EL_BUFSIZ)
+ el->el_search.patlen = EL_BUFSIZ - 1;
+ if (el->el_search.patlen != 0) {
+ (void) strncpy(el->el_search.patbuf, el->el_line.buffer,
+ el->el_search.patlen);
+ el->el_search.patbuf[el->el_search.patlen] = '\0';
+ } else
+ el->el_search.patlen = strlen(el->el_search.patbuf);
+ }
+#ifdef SDEBUG
+ (void) fprintf(el->el_errfile, "\neventno = %d\n",
+ el->el_history.eventno);
+ (void) fprintf(el->el_errfile, "patlen = %d\n", el->el_search.patlen);
+ (void) fprintf(el->el_errfile, "patbuf = \"%s\"\n",
+ el->el_search.patbuf);
+ (void) fprintf(el->el_errfile, "cursor %d lastchar %d\n",
+ EL_CURSOR(el) - el->el_line.buffer,
+ el->el_line.lastchar - el->el_line.buffer);
+#endif
+}
+
+
+/* ce_inc_search():
+ * Emacs incremental search
+ */
+protected el_action_t
+ce_inc_search(EditLine *el, int dir)
+{
+ static const char STRfwd[] = {'f', 'w', 'd', '\0'},
+ STRbck[] = {'b', 'c', 'k', '\0'};
+ static char pchar = ':';/* ':' = normal, '?' = failed */
+ static char endcmd[2] = {'\0', '\0'};
+ char ch, *ocursor = el->el_line.cursor, oldpchar = pchar;
+ const char *cp;
+
+ el_action_t ret = CC_NORM;
+
+ int ohisteventno = el->el_history.eventno;
+ int oldpatlen = el->el_search.patlen;
+ int newdir = dir;
+ int done, redo;
+
+ if (el->el_line.lastchar + sizeof(STRfwd) / sizeof(char) + 2 +
+ el->el_search.patlen >= el->el_line.limit)
+ return (CC_ERROR);
+
+ for (;;) {
+
+ if (el->el_search.patlen == 0) { /* first round */
+ pchar = ':';
+#ifdef ANCHOR
+ el->el_search.patbuf[el->el_search.patlen++] = '.';
+ el->el_search.patbuf[el->el_search.patlen++] = '*';
+#endif
+ }
+ done = redo = 0;
+ *el->el_line.lastchar++ = '\n';
+ for (cp = (newdir == ED_SEARCH_PREV_HISTORY) ? STRbck : STRfwd;
+ *cp; *el->el_line.lastchar++ = *cp++)
+ continue;
+ *el->el_line.lastchar++ = pchar;
+ for (cp = &el->el_search.patbuf[1];
+ cp < &el->el_search.patbuf[el->el_search.patlen];
+ *el->el_line.lastchar++ = *cp++)
+ continue;
+ *el->el_line.lastchar = '\0';
+ re_refresh(el);
+
+ if (el_getc(el, &ch) != 1)
+ return (ed_end_of_file(el, 0));
+
+ switch (el->el_map.current[(unsigned char) ch]) {
+ case ED_INSERT:
+ case ED_DIGIT:
+ if (el->el_search.patlen > EL_BUFSIZ - 3)
+ term_beep(el);
+ else {
+ el->el_search.patbuf[el->el_search.patlen++] =
+ ch;
+ *el->el_line.lastchar++ = ch;
+ *el->el_line.lastchar = '\0';
+ re_refresh(el);
+ }
+ break;
+
+ case EM_INC_SEARCH_NEXT:
+ newdir = ED_SEARCH_NEXT_HISTORY;
+ redo++;
+ break;
+
+ case EM_INC_SEARCH_PREV:
+ newdir = ED_SEARCH_PREV_HISTORY;
+ redo++;
+ break;
+
+ case ED_DELETE_PREV_CHAR:
+ if (el->el_search.patlen > 1)
+ done++;
+ else
+ term_beep(el);
+ break;
+
+ default:
+ switch (ch) {
+ case 0007: /* ^G: Abort */
+ ret = CC_ERROR;
+ done++;
+ break;
+
+ case 0027: /* ^W: Append word */
+ /* No can do if globbing characters in pattern */
+ for (cp = &el->el_search.patbuf[1];; cp++)
+ if (cp >= &el->el_search.patbuf[el->el_search.patlen]) {
+ el->el_line.cursor +=
+ el->el_search.patlen - 1;
+ cp = c__next_word(el->el_line.cursor,
+ el->el_line.lastchar, 1,
+ ce__isword);
+ while (el->el_line.cursor < cp &&
+ *el->el_line.cursor != '\n') {
+ if (el->el_search.patlen >
+ EL_BUFSIZ - 3) {
+ term_beep(el);
+ break;
+ }
+ el->el_search.patbuf[el->el_search.patlen++] =
+ *el->el_line.cursor;
+ *el->el_line.lastchar++ =
+ *el->el_line.cursor++;
+ }
+ el->el_line.cursor = ocursor;
+ *el->el_line.lastchar = '\0';
+ re_refresh(el);
+ break;
+ } else if (isglob(*cp)) {
+ term_beep(el);
+ break;
+ }
+ break;
+
+ default: /* Terminate and execute cmd */
+ endcmd[0] = ch;
+ el_push(el, endcmd);
+ /* FALLTHROUGH */
+
+ case 0033: /* ESC: Terminate */
+ ret = CC_REFRESH;
+ done++;
+ break;
+ }
+ break;
+ }
+
+ while (el->el_line.lastchar > el->el_line.buffer &&
+ *el->el_line.lastchar != '\n')
+ *el->el_line.lastchar-- = '\0';
+ *el->el_line.lastchar = '\0';
+
+ if (!done) {
+
+ /* Can't search if unmatched '[' */
+ for (cp = &el->el_search.patbuf[el->el_search.patlen-1],
+ ch = ']';
+ cp > el->el_search.patbuf;
+ cp--)
+ if (*cp == '[' || *cp == ']') {
+ ch = *cp;
+ break;
+ }
+ if (el->el_search.patlen > 1 && ch != '[') {
+ if (redo && newdir == dir) {
+ if (pchar == '?') { /* wrap around */
+ el->el_history.eventno =
+ newdir == ED_SEARCH_PREV_HISTORY ? 0 : 0x7fffffff;
+ if (hist_get(el) == CC_ERROR)
+ /* el->el_history.event
+ * no was fixed by
+ * first call */
+ (void) hist_get(el);
+ el->el_line.cursor = newdir ==
+ ED_SEARCH_PREV_HISTORY ?
+ el->el_line.lastchar :
+ el->el_line.buffer;
+ } else
+ el->el_line.cursor +=
+ newdir ==
+ ED_SEARCH_PREV_HISTORY ?
+ -1 : 1;
+ }
+#ifdef ANCHOR
+ el->el_search.patbuf[el->el_search.patlen++] =
+ '.';
+ el->el_search.patbuf[el->el_search.patlen++] =
+ '*';
+#endif
+ el->el_search.patbuf[el->el_search.patlen] =
+ '\0';
+ if (el->el_line.cursor < el->el_line.buffer ||
+ el->el_line.cursor > el->el_line.lastchar ||
+ (ret = ce_search_line(el,
+ &el->el_search.patbuf[1],
+ newdir)) == CC_ERROR) {
+ /* avoid c_setpat */
+ el->el_state.lastcmd =
+ (el_action_t) newdir;
+ ret = newdir == ED_SEARCH_PREV_HISTORY ?
+ ed_search_prev_history(el, 0) :
+ ed_search_next_history(el, 0);
+ if (ret != CC_ERROR) {
+ el->el_line.cursor = newdir ==
+ ED_SEARCH_PREV_HISTORY ?
+ el->el_line.lastchar :
+ el->el_line.buffer;
+ (void) ce_search_line(el,
+ &el->el_search.patbuf[1],
+ newdir);
+ }
+ }
+ el->el_search.patbuf[--el->el_search.patlen] =
+ '\0';
+ if (ret == CC_ERROR) {
+ term_beep(el);
+ if (el->el_history.eventno !=
+ ohisteventno) {
+ el->el_history.eventno =
+ ohisteventno;
+ if (hist_get(el) == CC_ERROR)
+ return (CC_ERROR);
+ }
+ el->el_line.cursor = ocursor;
+ pchar = '?';
+ } else {
+ pchar = ':';
+ }
+ }
+ ret = ce_inc_search(el, newdir);
+
+ if (ret == CC_ERROR && pchar == '?' && oldpchar == ':')
+ /*
+ * break abort of failed search at last
+ * non-failed
+ */
+ ret = CC_NORM;
+
+ }
+ if (ret == CC_NORM || (ret == CC_ERROR && oldpatlen == 0)) {
+ /* restore on normal return or error exit */
+ pchar = oldpchar;
+ el->el_search.patlen = oldpatlen;
+ if (el->el_history.eventno != ohisteventno) {
+ el->el_history.eventno = ohisteventno;
+ if (hist_get(el) == CC_ERROR)
+ return (CC_ERROR);
+ }
+ el->el_line.cursor = ocursor;
+ if (ret == CC_ERROR)
+ re_refresh(el);
+ }
+ if (done || ret != CC_NORM)
+ return (ret);
+ }
+}
+
+
+/* cv_search():
+ * Vi search.
+ */
+protected el_action_t
+cv_search(EditLine *el, int dir)
+{
+ char ch;
+ char tmpbuf[EL_BUFSIZ];
+ int tmplen;
+
+ tmplen = 0;
+#ifdef ANCHOR
+ tmpbuf[tmplen++] = '.';
+ tmpbuf[tmplen++] = '*';
+#endif
+
+ el->el_line.buffer[0] = '\0';
+ el->el_line.lastchar = el->el_line.buffer;
+ el->el_line.cursor = el->el_line.buffer;
+ el->el_search.patdir = dir;
+
+ c_insert(el, 2); /* prompt + '\n' */
+ *el->el_line.cursor++ = '\n';
+ *el->el_line.cursor++ = dir == ED_SEARCH_PREV_HISTORY ? '/' : '?';
+ re_refresh(el);
+
+#ifdef ANCHOR
+#define LEN 2
+#else
+#define LEN 0
+#endif
+
+ tmplen = c_gets(el, &tmpbuf[LEN]) + LEN;
+ ch = tmpbuf[tmplen];
+ tmpbuf[tmplen] = '\0';
+
+ if (tmplen == LEN) {
+ /*
+ * Use the old pattern, but wild-card it.
+ */
+ if (el->el_search.patlen == 0) {
+ el->el_line.buffer[0] = '\0';
+ el->el_line.lastchar = el->el_line.buffer;
+ el->el_line.cursor = el->el_line.buffer;
+ re_refresh(el);
+ return (CC_ERROR);
+ }
+#ifdef ANCHOR
+ if (el->el_search.patbuf[0] != '.' &&
+ el->el_search.patbuf[0] != '*') {
+ (void) strncpy(tmpbuf, el->el_search.patbuf,
+ sizeof(tmpbuf) - 1);
+ el->el_search.patbuf[0] = '.';
+ el->el_search.patbuf[1] = '*';
+ (void) strncpy(&el->el_search.patbuf[2], tmpbuf,
+ EL_BUFSIZ - 3);
+ el->el_search.patlen++;
+ el->el_search.patbuf[el->el_search.patlen++] = '.';
+ el->el_search.patbuf[el->el_search.patlen++] = '*';
+ el->el_search.patbuf[el->el_search.patlen] = '\0';
+ }
+#endif
+ } else {
+#ifdef ANCHOR
+ tmpbuf[tmplen++] = '.';
+ tmpbuf[tmplen++] = '*';
+#endif
+ tmpbuf[tmplen] = '\0';
+ (void) strncpy(el->el_search.patbuf, tmpbuf, EL_BUFSIZ - 1);
+ el->el_search.patlen = tmplen;
+ }
+ el->el_state.lastcmd = (el_action_t) dir; /* avoid c_setpat */
+ el->el_line.cursor = el->el_line.lastchar = el->el_line.buffer;
+ if ((dir == ED_SEARCH_PREV_HISTORY ? ed_search_prev_history(el, 0) :
+ ed_search_next_history(el, 0)) == CC_ERROR) {
+ re_refresh(el);
+ return (CC_ERROR);
+ } else {
+ if (ch == 0033) {
+ re_refresh(el);
+ *el->el_line.lastchar++ = '\n';
+ *el->el_line.lastchar = '\0';
+ re_goto_bottom(el);
+ return (CC_NEWLINE);
+ } else
+ return (CC_REFRESH);
+ }
+}
+
+
+/* ce_search_line():
+ * Look for a pattern inside a line
+ */
+protected el_action_t
+ce_search_line(EditLine *el, char *pattern, int dir)
+{
+ char *cp;
+
+ if (dir == ED_SEARCH_PREV_HISTORY) {
+ for (cp = el->el_line.cursor; cp >= el->el_line.buffer; cp--)
+ if (el_match(cp, pattern)) {
+ el->el_line.cursor = cp;
+ return (CC_NORM);
+ }
+ return (CC_ERROR);
+ } else {
+ for (cp = el->el_line.cursor; *cp != '\0' &&
+ cp < el->el_line.limit; cp++)
+ if (el_match(cp, pattern)) {
+ el->el_line.cursor = cp;
+ return (CC_NORM);
+ }
+ return (CC_ERROR);
+ }
+}
+
+
+/* cv_repeat_srch():
+ * Vi repeat search
+ */
+protected el_action_t
+cv_repeat_srch(EditLine *el, int c)
+{
+
+#ifdef SDEBUG
+ (void) fprintf(el->el_errfile, "dir %d patlen %d patbuf %s\n",
+ c, el->el_search.patlen, el->el_search.patbuf);
+#endif
+
+ el->el_state.lastcmd = (el_action_t) c; /* Hack to stop c_setpat */
+ el->el_line.lastchar = el->el_line.buffer;
+
+ switch (c) {
+ case ED_SEARCH_NEXT_HISTORY:
+ return (ed_search_next_history(el, 0));
+ case ED_SEARCH_PREV_HISTORY:
+ return (ed_search_prev_history(el, 0));
+ default:
+ return (CC_ERROR);
+ }
+}
+
+
+/* cv_csearch_back():
+ * Vi character search reverse
+ */
+protected el_action_t
+cv_csearch_back(EditLine *el, int ch, int count, int tflag)
+{
+ char *cp;
+
+ cp = el->el_line.cursor;
+ while (count--) {
+ if (*cp == ch)
+ cp--;
+ while (cp > el->el_line.buffer && *cp != ch)
+ cp--;
+ }
+
+ if (cp < el->el_line.buffer || (cp == el->el_line.buffer && *cp != ch))
+ return (CC_ERROR);
+
+ if (*cp == ch && tflag)
+ cp++;
+
+ el->el_line.cursor = cp;
+
+ if (el->el_chared.c_vcmd.action & DELETE) {
+ el->el_line.cursor++;
+ cv_delfini(el);
+ return (CC_REFRESH);
+ }
+ re_refresh_cursor(el);
+ return (CC_NORM);
+}
+
+
+/* cv_csearch_fwd():
+ * Vi character search forward
+ */
+protected el_action_t
+cv_csearch_fwd(EditLine *el, int ch, int count, int tflag)
+{
+ char *cp;
+
+ cp = el->el_line.cursor;
+ while (count--) {
+ if (*cp == ch)
+ cp++;
+ while (cp < el->el_line.lastchar && *cp != ch)
+ cp++;
+ }
+
+ if (cp >= el->el_line.lastchar)
+ return (CC_ERROR);
+
+ if (*cp == ch && tflag)
+ cp--;
+
+ el->el_line.cursor = cp;
+
+ if (el->el_chared.c_vcmd.action & DELETE) {
+ el->el_line.cursor++;
+ cv_delfini(el);
+ return (CC_REFRESH);
+ }
+ re_refresh_cursor(el);
+ return (CC_NORM);
+}
diff --git a/trunk/main/editline/search.h b/trunk/main/editline/search.h
new file mode 100644
index 000000000..676bbe2e3
--- /dev/null
+++ b/trunk/main/editline/search.h
@@ -0,0 +1,70 @@
+/* $NetBSD: search.h,v 1.5 2000/09/04 22:06:32 lukem Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)search.h 8.1 (Berkeley) 6/4/93
+ */
+
+/*
+ * el.search.h: Line and history searching utilities
+ */
+#ifndef _h_el_search
+#define _h_el_search
+
+#include "histedit.h"
+
+typedef struct el_search_t {
+ char *patbuf; /* The pattern buffer */
+ size_t patlen; /* Length of the pattern buffer */
+ int patdir; /* Direction of the last search */
+ int chadir; /* Character search direction */
+ char chacha; /* Character we are looking for */
+} el_search_t;
+
+
+protected int el_match(const char *, const char *);
+protected int search_init(EditLine *);
+protected void search_end(EditLine *);
+protected int c_hmatch(EditLine *, const char *);
+protected void c_setpat(EditLine *);
+protected el_action_t ce_inc_search(EditLine *, int);
+protected el_action_t cv_search(EditLine *, int);
+protected el_action_t ce_search_line(EditLine *, char *, int);
+protected el_action_t cv_repeat_srch(EditLine *, int);
+protected el_action_t cv_csearch_back(EditLine *, int, int, int);
+protected el_action_t cv_csearch_fwd(EditLine *, int, int, int);
+
+#endif /* _h_el_search */
diff --git a/trunk/main/editline/sig.c b/trunk/main/editline/sig.c
new file mode 100644
index 000000000..0acba1247
--- /dev/null
+++ b/trunk/main/editline/sig.c
@@ -0,0 +1,198 @@
+/* $NetBSD: sig.c,v 1.9 2002/03/18 16:00:58 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+#if !defined(lint) && !defined(SCCSID)
+#if 0
+static char sccsid[] = "@(#)sig.c 8.1 (Berkeley) 6/4/93";
+#else
+__RCSID("$NetBSD: sig.c,v 1.9 2002/03/18 16:00:58 christos Exp $");
+#endif
+#endif /* not lint && not SCCSID */
+
+/*
+ * sig.c: Signal handling stuff.
+ * our policy is to trap all signals, set a good state
+ * and pass the ball to our caller.
+ */
+#include "el.h"
+#include <stdlib.h>
+
+private EditLine *sel = NULL;
+
+private const int sighdl[] = {
+#define _DO(a) (a),
+ ALLSIGS
+#undef _DO
+ - 1
+};
+
+private void sig_handler(int);
+
+/* sig_handler():
+ * This is the handler called for all signals
+ * XXX: we cannot pass any data so we just store the old editline
+ * state in a private variable
+ */
+private void
+sig_handler(int signo)
+{
+ int i;
+ sigset_t nset, oset;
+
+ (void) sigemptyset(&nset);
+ (void) sigaddset(&nset, signo);
+ (void) sigprocmask(SIG_BLOCK, &nset, &oset);
+
+ switch (signo) {
+ case SIGCONT:
+ tty_rawmode(sel);
+ if (ed_redisplay(sel, 0) == CC_REFRESH)
+ re_refresh(sel);
+ term__flush();
+ break;
+
+ case SIGWINCH:
+ el_resize(sel);
+ break;
+
+ default:
+ tty_cookedmode(sel);
+ break;
+ }
+
+ for (i = 0; sighdl[i] != -1; i++)
+ if (signo == sighdl[i])
+ break;
+
+ (void) signal(signo, sel->el_signal[i]);
+ (void) sigprocmask(SIG_SETMASK, &oset, NULL);
+ (void) kill(0, signo);
+}
+
+
+/* sig_init():
+ * Initialize all signal stuff
+ */
+protected int
+sig_init(EditLine *el)
+{
+ int i;
+ sigset_t nset, oset;
+
+ (void) sigemptyset(&nset);
+#define _DO(a) (void) sigaddset(&nset, a);
+ ALLSIGS
+#undef _DO
+ (void) sigprocmask(SIG_BLOCK, &nset, &oset);
+
+#define SIGSIZE (sizeof(sighdl) / sizeof(sighdl[0]) * sizeof(sig_t))
+
+ el->el_signal = (sig_t *) el_malloc(SIGSIZE);
+ if (el->el_signal == NULL)
+ return (-1);
+ for (i = 0; sighdl[i] != -1; i++)
+ el->el_signal[i] = SIG_ERR;
+
+ (void) sigprocmask(SIG_SETMASK, &oset, NULL);
+
+ return (0);
+}
+
+
+/* sig_end():
+ * Clear all signal stuff
+ */
+protected void
+sig_end(EditLine *el)
+{
+
+ el_free((ptr_t) el->el_signal);
+ el->el_signal = NULL;
+}
+
+
+/* sig_set():
+ * set all the signal handlers
+ */
+protected void
+sig_set(EditLine *el)
+{
+ int i;
+ sigset_t nset, oset;
+
+ (void) sigemptyset(&nset);
+#define _DO(a) (void) sigaddset(&nset, a);
+ ALLSIGS
+#undef _DO
+ (void) sigprocmask(SIG_BLOCK, &nset, &oset);
+
+ for (i = 0; sighdl[i] != -1; i++) {
+ sig_t s;
+ /* This could happen if we get interrupted */
+ if ((s = signal(sighdl[i], sig_handler)) != sig_handler)
+ el->el_signal[i] = s;
+ }
+ sel = el;
+ (void) sigprocmask(SIG_SETMASK, &oset, NULL);
+}
+
+
+/* sig_clr():
+ * clear all the signal handlers
+ */
+protected void
+sig_clr(EditLine *el)
+{
+ int i;
+ sigset_t nset, oset;
+
+ (void) sigemptyset(&nset);
+#define _DO(a) (void) sigaddset(&nset, a);
+ ALLSIGS
+#undef _DO
+ (void) sigprocmask(SIG_BLOCK, &nset, &oset);
+
+ for (i = 0; sighdl[i] != -1; i++)
+ if (el->el_signal[i] != SIG_ERR)
+ (void) signal(sighdl[i], el->el_signal[i]);
+
+ sel = NULL; /* we are going to die if the handler is
+ * called */
+ (void) sigprocmask(SIG_SETMASK, &oset, NULL);
+}
diff --git a/trunk/main/editline/sig.h b/trunk/main/editline/sig.h
new file mode 100644
index 000000000..e7231b65c
--- /dev/null
+++ b/trunk/main/editline/sig.h
@@ -0,0 +1,72 @@
+/* $NetBSD: sig.h,v 1.3 2000/09/04 22:06:32 lukem Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)sig.h 8.1 (Berkeley) 6/4/93
+ */
+
+/*
+ * el.sig.h: Signal handling functions
+ */
+#ifndef _h_el_sig
+#define _h_el_sig
+
+#include <signal.h>
+
+#include "histedit.h"
+
+/*
+ * Define here all the signals we are going to handle
+ * The _DO macro is used to iterate in the source code
+ */
+#define ALLSIGS \
+ _DO(SIGINT) \
+ _DO(SIGTSTP) \
+ _DO(SIGSTOP) \
+ _DO(SIGQUIT) \
+ _DO(SIGHUP) \
+ _DO(SIGTERM) \
+ _DO(SIGCONT) \
+ _DO(SIGWINCH)
+
+typedef sig_t *el_signal_t;
+
+protected void sig_end(EditLine*);
+protected int sig_init(EditLine*);
+protected void sig_set(EditLine*);
+protected void sig_clr(EditLine*);
+
+#endif /* _h_el_sig */
diff --git a/trunk/main/editline/sys.h b/trunk/main/editline/sys.h
new file mode 100644
index 000000000..87ecc899c
--- /dev/null
+++ b/trunk/main/editline/sys.h
@@ -0,0 +1,123 @@
+/* $NetBSD: sys.h,v 1.5 2002/03/18 16:00:59 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)sys.h 8.1 (Berkeley) 6/4/93
+ */
+
+/*
+ * sys.h: Put all the stupid compiler and system dependencies here...
+ */
+#ifndef _h_sys
+#define _h_sys
+
+#ifndef public
+# define public /* Externally visible functions/variables */
+#endif
+
+#ifndef private
+# define private static /* Always hidden internals */
+#endif
+
+#ifndef protected
+# define protected /* Redefined from elsewhere to "static" */
+ /* When we want to hide everything */
+#endif
+
+#ifndef _PTR_T
+# define _PTR_T
+typedef void *ptr_t;
+#endif
+
+#ifndef _IOCTL_T
+# define _IOCTL_T
+typedef void *ioctl_t;
+#endif
+
+#include <stdio.h>
+
+#ifndef HAVE_FGETLN
+#define fgetln libedit_fgetln
+char *fgetln(FILE *fp, size_t *len);
+#endif
+
+#define REGEX /* Use POSIX.2 regular expression functions */
+#undef REGEXP /* Use UNIX V8 regular expression functions */
+
+#ifdef SUNOS
+# undef REGEX
+# undef REGEXP
+/* # include <malloc.h> XXX Removed for Solaris build XXX */
+typedef void (*sig_t)(int);
+# ifdef __GNUC__
+/*
+ * Broken hdrs.
+ */
+#ifndef SOLARIS
+extern int tgetent(const char *bp, char *name);
+extern int tgetflag(const char *id);
+extern int tgetnum(const char *id);
+extern char *tgetstr(const char *id, char **area);
+#endif
+extern char *tgoto(const char *cap, int col, int row);
+extern int tputs(const char *str, int affcnt, int (*putc)(int));
+extern char *getenv(const char *);
+extern int fprintf(FILE *, const char *, ...);
+extern int sigsetmask(int);
+extern int sigblock(int);
+extern int fputc(int, FILE *);
+extern int fgetc(FILE *);
+extern int fflush(FILE *);
+extern int tolower(int);
+extern int toupper(int);
+extern int errno, sys_nerr;
+extern char *sys_errlist[];
+extern void perror(const char *);
+# include <string.h>
+# define strerror(e) sys_errlist[e]
+# endif
+# ifdef SABER
+extern ptr_t memcpy(ptr_t, const ptr_t, size_t);
+extern ptr_t memset(ptr_t, int, size_t);
+# endif
+extern char *fgetline(FILE *, int *);
+#endif
+
+#ifdef HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+
+#endif /* _h_sys */
diff --git a/trunk/main/editline/term.c b/trunk/main/editline/term.c
new file mode 100644
index 000000000..fb627cabb
--- /dev/null
+++ b/trunk/main/editline/term.c
@@ -0,0 +1,1587 @@
+/* $NetBSD: term.c,v 1.35 2002/03/18 16:00:59 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+#if !defined(lint) && !defined(SCCSID)
+#if 0
+static char sccsid[] = "@(#)term.c 8.2 (Berkeley) 4/30/95";
+#else
+__RCSID("$NetBSD: term.c,v 1.35 2002/03/18 16:00:59 christos Exp $");
+#endif
+#endif /* not lint && not SCCSID */
+
+/*
+ * term.c: Editor/termcap-curses interface
+ * We have to declare a static variable here, since the
+ * termcap putchar routine does not take an argument!
+ */
+#include <stdio.h>
+#include <signal.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#ifdef HAVE_TERMCAP_H
+#include <termcap.h>
+#endif
+#ifdef HAVE_CURSES_H
+#include <curses.h>
+#endif
+#ifdef HAVE_NCURSES_H
+#include <ncurses.h>
+#endif
+#if defined(HAVE_TERM_H)
+#include "term.h"
+/* Can not use /usr/include/term.h because of a lot of incompatibilities, so just define some prototypes */
+extern int tgetent(char *, const char *);
+extern int tgetflag(const char *);
+extern int tgetnum(const char *);
+extern char *tgetstr(const char *, char **);
+extern int tputs (const char *, int, int (*)(int));
+extern char *tgoto (const char *, int, int);
+#endif /* defined(HAVE_TERM_H) */
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include "el.h"
+
+/*
+ * IMPORTANT NOTE: these routines are allowed to look at the current screen
+ * and the current possition assuming that it is correct. If this is not
+ * true, then the update will be WRONG! This is (should be) a valid
+ * assumption...
+ */
+
+#define TC_BUFSIZE 2048
+
+#define GoodStr(a) (el->el_term.t_str[a] != NULL && \
+ el->el_term.t_str[a][0] != '\0')
+#define Str(a) el->el_term.t_str[a]
+#define Val(a) el->el_term.t_val[a]
+
+#ifdef notdef
+private const struct {
+ const char *b_name;
+ int b_rate;
+} baud_rate[] = {
+#ifdef B0
+ { "0", B0 },
+#endif
+#ifdef B50
+ { "50", B50 },
+#endif
+#ifdef B75
+ { "75", B75 },
+#endif
+#ifdef B110
+ { "110", B110 },
+#endif
+#ifdef B134
+ { "134", B134 },
+#endif
+#ifdef B150
+ { "150", B150 },
+#endif
+#ifdef B200
+ { "200", B200 },
+#endif
+#ifdef B300
+ { "300", B300 },
+#endif
+#ifdef B600
+ { "600", B600 },
+#endif
+#ifdef B900
+ { "900", B900 },
+#endif
+#ifdef B1200
+ { "1200", B1200 },
+#endif
+#ifdef B1800
+ { "1800", B1800 },
+#endif
+#ifdef B2400
+ { "2400", B2400 },
+#endif
+#ifdef B3600
+ { "3600", B3600 },
+#endif
+#ifdef B4800
+ { "4800", B4800 },
+#endif
+#ifdef B7200
+ { "7200", B7200 },
+#endif
+#ifdef B9600
+ { "9600", B9600 },
+#endif
+#ifdef EXTA
+ { "19200", EXTA },
+#endif
+#ifdef B19200
+ { "19200", B19200 },
+#endif
+#ifdef EXTB
+ { "38400", EXTB },
+#endif
+#ifdef B38400
+ { "38400", B38400 },
+#endif
+ { NULL, 0 }
+};
+#endif
+
+private const struct termcapstr {
+ const char *name;
+ const char *long_name;
+} tstr[] = {
+#define T_al 0
+ { "al", "add new blank line" },
+#define T_bl 1
+ { "bl", "audible bell" },
+#define T_cd 2
+ { "cd", "clear to bottom" },
+#define T_ce 3
+ { "ce", "clear to end of line" },
+#define T_ch 4
+ { "ch", "cursor to horiz pos" },
+#define T_cl 5
+ { "cl", "clear screen" },
+#define T_dc 6
+ { "dc", "delete a character" },
+#define T_dl 7
+ { "dl", "delete a line" },
+#define T_dm 8
+ { "dm", "start delete mode" },
+#define T_ed 9
+ { "ed", "end delete mode" },
+#define T_ei 10
+ { "ei", "end insert mode" },
+#define T_fs 11
+ { "fs", "cursor from status line" },
+#define T_ho 12
+ { "ho", "home cursor" },
+#define T_ic 13
+ { "ic", "insert character" },
+#define T_im 14
+ { "im", "start insert mode" },
+#define T_ip 15
+ { "ip", "insert padding" },
+#define T_kd 16
+ { "kd", "sends cursor down" },
+#define T_kl 17
+ { "kl", "sends cursor left" },
+#define T_kr 18
+ { "kr", "sends cursor right" },
+#define T_ku 19
+ { "ku", "sends cursor up" },
+#define T_md 20
+ { "md", "begin bold" },
+#define T_me 21
+ { "me", "end attributes" },
+#define T_nd 22
+ { "nd", "non destructive space" },
+#define T_se 23
+ { "se", "end standout" },
+#define T_so 24
+ { "so", "begin standout" },
+#define T_ts 25
+ { "ts", "cursor to status line" },
+#define T_up 26
+ { "up", "cursor up one" },
+#define T_us 27
+ { "us", "begin underline" },
+#define T_ue 28
+ { "ue", "end underline" },
+#define T_vb 29
+ { "vb", "visible bell" },
+#define T_DC 30
+ { "DC", "delete multiple chars" },
+#define T_DO 31
+ { "DO", "cursor down multiple" },
+#define T_IC 32
+ { "IC", "insert multiple chars" },
+#define T_LE 33
+ { "LE", "cursor left multiple" },
+#define T_RI 34
+ { "RI", "cursor right multiple" },
+#define T_UP 35
+ { "UP", "cursor up multiple" },
+#define T_kh 36
+ { "kh", "send cursor home" },
+#define T_at7 37
+ { "@7", "send cursor end" },
+#define T_str 38
+ { NULL, NULL }
+};
+
+private const struct termcapval {
+ const char *name;
+ const char *long_name;
+} tval[] = {
+#define T_am 0
+ { "am", "has automatic margins" },
+#define T_pt 1
+ { "pt", "has physical tabs" },
+#define T_li 2
+ { "li", "Number of lines" },
+#define T_co 3
+ { "co", "Number of columns" },
+#define T_km 4
+ { "km", "Has meta key" },
+#define T_xt 5
+ { "xt", "Tab chars destructive" },
+#define T_xn 6
+ { "xn", "newline ignored at right margin" },
+#define T_MT 7
+ { "MT", "Has meta key" }, /* XXX? */
+#define T_val 8
+ { NULL, NULL, }
+};
+/* do two or more of the attributes use me */
+
+private void term_setflags(EditLine *);
+private int term_rebuffer_display(EditLine *);
+private void term_free_display(EditLine *);
+private int term_alloc_display(EditLine *);
+private void term_alloc(EditLine *, const struct termcapstr *, const char *);
+private void term_init_arrow(EditLine *);
+private void term_reset_arrow(EditLine *);
+
+
+private FILE *term_outfile = NULL; /* XXX: How do we fix that? */
+
+
+/* term_setflags():
+ * Set the terminal capability flags
+ */
+private void
+term_setflags(EditLine *el)
+{
+ EL_FLAGS = 0;
+ if (el->el_tty.t_tabs)
+ EL_FLAGS |= (Val(T_pt) && !Val(T_xt)) ? TERM_CAN_TAB : 0;
+
+ EL_FLAGS |= (Val(T_km) || Val(T_MT)) ? TERM_HAS_META : 0;
+ EL_FLAGS |= GoodStr(T_ce) ? TERM_CAN_CEOL : 0;
+ EL_FLAGS |= (GoodStr(T_dc) || GoodStr(T_DC)) ? TERM_CAN_DELETE : 0;
+ EL_FLAGS |= (GoodStr(T_im) || GoodStr(T_ic) || GoodStr(T_IC)) ?
+ TERM_CAN_INSERT : 0;
+ EL_FLAGS |= (GoodStr(T_up) || GoodStr(T_UP)) ? TERM_CAN_UP : 0;
+ EL_FLAGS |= Val(T_am) ? TERM_HAS_AUTO_MARGINS : 0;
+ EL_FLAGS |= Val(T_xn) ? TERM_HAS_MAGIC_MARGINS : 0;
+
+ if (GoodStr(T_me) && GoodStr(T_ue))
+ EL_FLAGS |= (strcmp(Str(T_me), Str(T_ue)) == 0) ?
+ TERM_CAN_ME : 0;
+ else
+ EL_FLAGS &= ~TERM_CAN_ME;
+ if (GoodStr(T_me) && GoodStr(T_se))
+ EL_FLAGS |= (strcmp(Str(T_me), Str(T_se)) == 0) ?
+ TERM_CAN_ME : 0;
+
+
+#ifdef DEBUG_SCREEN
+ if (!EL_CAN_UP) {
+ (void) fprintf(el->el_errfile,
+ "WARNING: Your terminal cannot move up.\n");
+ (void) fprintf(el->el_errfile,
+ "Editing may be odd for long lines.\n");
+ }
+ if (!EL_CAN_CEOL)
+ (void) fprintf(el->el_errfile, "no clear EOL capability.\n");
+ if (!EL_CAN_DELETE)
+ (void) fprintf(el->el_errfile, "no delete char capability.\n");
+ if (!EL_CAN_INSERT)
+ (void) fprintf(el->el_errfile, "no insert char capability.\n");
+#endif /* DEBUG_SCREEN */
+}
+
+
+/* term_init():
+ * Initialize the terminal stuff
+ */
+protected int
+term_init(EditLine *el)
+{
+
+ el->el_term.t_buf = (char *) el_malloc(TC_BUFSIZE);
+ if (el->el_term.t_buf == NULL)
+ return (-1);
+ el->el_term.t_cap = (char *) el_malloc(TC_BUFSIZE);
+ if (el->el_term.t_cap == NULL)
+ return (-1);
+ el->el_term.t_fkey = (fkey_t *) el_malloc(A_K_NKEYS * sizeof(fkey_t));
+ if (el->el_term.t_fkey == NULL)
+ return (-1);
+ el->el_term.t_loc = 0;
+ el->el_term.t_str = (char **) el_malloc(T_str * sizeof(char *));
+ if (el->el_term.t_str == NULL)
+ return (-1);
+ (void) memset(el->el_term.t_str, 0, T_str * sizeof(char *));
+ el->el_term.t_val = (int *) el_malloc(T_val * sizeof(int));
+ if (el->el_term.t_val == NULL)
+ return (-1);
+ (void) memset(el->el_term.t_val, 0, T_val * sizeof(int));
+ term_outfile = el->el_outfile;
+ (void) term_set(el, NULL);
+ term_init_arrow(el);
+ return (0);
+}
+/* term_end():
+ * Clean up the terminal stuff
+ */
+protected void
+term_end(EditLine *el)
+{
+
+ el_free((ptr_t) el->el_term.t_buf);
+ el->el_term.t_buf = NULL;
+ el_free((ptr_t) el->el_term.t_cap);
+ el->el_term.t_cap = NULL;
+ el_free((ptr_t) el->el_term.t_fkey);
+ el->el_term.t_fkey = NULL;
+ el->el_term.t_loc = 0;
+ el_free((ptr_t) el->el_term.t_str);
+ el->el_term.t_str = NULL;
+ el_free((ptr_t) el->el_term.t_val);
+ el->el_term.t_val = NULL;
+ term_free_display(el);
+}
+
+
+/* term_alloc():
+ * Maintain a string pool for termcap strings
+ */
+private void
+term_alloc(EditLine *el, const struct termcapstr *t, const char *cap)
+{
+ char termbuf[TC_BUFSIZE];
+ int tlen, clen;
+ char **tlist = el->el_term.t_str;
+ char **tmp, **str = &tlist[t - tstr];
+
+ if (cap == NULL || *cap == '\0') {
+ *str = NULL;
+ return;
+ } else
+ clen = strlen(cap);
+
+ tlen = *str == NULL ? 0 : strlen(*str);
+
+ /*
+ * New string is shorter; no need to allocate space
+ */
+ if (clen <= tlen) {
+ (void) strcpy(*str, cap); /* XXX strcpy is safe */
+ return;
+ }
+ /*
+ * New string is longer; see if we have enough space to append
+ */
+ if (el->el_term.t_loc + 3 < TC_BUFSIZE) {
+ /* XXX strcpy is safe */
+ (void) strcpy(*str = &el->el_term.t_buf[el->el_term.t_loc],
+ cap);
+ el->el_term.t_loc += clen + 1; /* one for \0 */
+ return;
+ }
+ /*
+ * Compact our buffer; no need to check compaction, cause we know it
+ * fits...
+ */
+ tlen = 0;
+ for (tmp = tlist; tmp < &tlist[T_str]; tmp++)
+ if (*tmp != NULL && *tmp != '\0' && *tmp != *str) {
+ char *ptr;
+
+ for (ptr = *tmp; *ptr != '\0'; termbuf[tlen++] = *ptr++)
+ continue;
+ termbuf[tlen++] = '\0';
+ }
+ memcpy(el->el_term.t_buf, termbuf, TC_BUFSIZE);
+ el->el_term.t_loc = tlen;
+ if (el->el_term.t_loc + 3 >= TC_BUFSIZE) {
+ (void) fprintf(el->el_errfile,
+ "Out of termcap string space.\n");
+ return;
+ }
+ /* XXX strcpy is safe */
+ (void) strcpy(*str = &el->el_term.t_buf[el->el_term.t_loc], cap);
+ el->el_term.t_loc += clen + 1; /* one for \0 */
+ return;
+}
+
+
+/* term_rebuffer_display():
+ * Rebuffer the display after the screen changed size
+ */
+private int
+term_rebuffer_display(EditLine *el)
+{
+ coord_t *c = &el->el_term.t_size;
+
+ term_free_display(el);
+
+ c->h = Val(T_co);
+ c->v = Val(T_li);
+
+ if (term_alloc_display(el) == -1)
+ return (-1);
+ return (0);
+}
+
+
+/* term_alloc_display():
+ * Allocate a new display.
+ */
+private int
+term_alloc_display(EditLine *el)
+{
+ int i;
+ char **b;
+ coord_t *c = &el->el_term.t_size;
+
+ b = (char **) el_malloc((size_t) (sizeof(char *) * (c->v + 1)));
+ if (b == NULL)
+ return (-1);
+ for (i = 0; i < c->v; i++) {
+ b[i] = (char *) el_malloc((size_t) (sizeof(char) * (c->h + 1)));
+ if (b[i] == NULL)
+ return (-1);
+ }
+ b[c->v] = NULL;
+ el->el_display = b;
+
+ b = (char **) el_malloc((size_t) (sizeof(char *) * (c->v + 1)));
+ if (b == NULL)
+ return (-1);
+ for (i = 0; i < c->v; i++) {
+ b[i] = (char *) el_malloc((size_t) (sizeof(char) * (c->h + 1)));
+ if (b[i] == NULL)
+ return (-1);
+ }
+ b[c->v] = NULL;
+ el->el_vdisplay = b;
+ return (0);
+}
+
+
+/* term_free_display():
+ * Free the display buffers
+ */
+private void
+term_free_display(EditLine *el)
+{
+ char **b;
+ char **bufp;
+
+ b = el->el_display;
+ el->el_display = NULL;
+ if (b != NULL) {
+ for (bufp = b; *bufp != NULL; bufp++)
+ el_free((ptr_t) * bufp);
+ el_free((ptr_t) b);
+ }
+ b = el->el_vdisplay;
+ el->el_vdisplay = NULL;
+ if (b != NULL) {
+ for (bufp = b; *bufp != NULL; bufp++)
+ el_free((ptr_t) * bufp);
+ el_free((ptr_t) b);
+ }
+}
+
+
+/* term_move_to_line():
+ * move to line <where> (first line == 0)
+ * as efficiently as possible
+ */
+protected void
+term_move_to_line(EditLine *el, int where)
+{
+ int del;
+
+ if (where == el->el_cursor.v)
+ return;
+
+ if (where > el->el_term.t_size.v) {
+#ifdef DEBUG_SCREEN
+ (void) fprintf(el->el_errfile,
+ "term_move_to_line: where is ridiculous: %d\r\n", where);
+#endif /* DEBUG_SCREEN */
+ return;
+ }
+ if ((del = where - el->el_cursor.v) > 0) {
+ while (del > 0) {
+ if (EL_HAS_AUTO_MARGINS &&
+ el->el_display[el->el_cursor.v][0] != '\0') {
+ /* move without newline */
+ term_move_to_char(el, el->el_term.t_size.h - 1);
+ term_overwrite(el,
+ &el->el_display[el->el_cursor.v][el->el_cursor.h],
+ 1);
+ /* updates Cursor */
+ del--;
+ } else {
+ if ((del > 1) && GoodStr(T_DO)) {
+ (void) tputs(tgoto(Str(T_DO), del, del),
+ del, term__putc);
+ del = 0;
+ } else {
+ for (; del > 0; del--)
+ term__putc('\n');
+ /* because the \n will become \r\n */
+ el->el_cursor.h = 0;
+ }
+ }
+ }
+ } else { /* del < 0 */
+ if (GoodStr(T_UP) && (-del > 1 || !GoodStr(T_up)))
+ (void) tputs(tgoto(Str(T_UP), -del, -del), -del,
+ term__putc);
+ else {
+ if (GoodStr(T_up))
+ for (; del < 0; del++)
+ (void) tputs(Str(T_up), 1, term__putc);
+ }
+ }
+ el->el_cursor.v = where;/* now where is here */
+}
+
+
+/* term_move_to_char():
+ * Move to the character position specified
+ */
+protected void
+term_move_to_char(EditLine *el, int where)
+{
+ int del, i;
+
+mc_again:
+ if (where == el->el_cursor.h)
+ return;
+
+ if (where > el->el_term.t_size.h) {
+#ifdef DEBUG_SCREEN
+ (void) fprintf(el->el_errfile,
+ "term_move_to_char: where is riduculous: %d\r\n", where);
+#endif /* DEBUG_SCREEN */
+ return;
+ }
+ if (!where) { /* if where is first column */
+ term__putc('\r'); /* do a CR */
+ el->el_cursor.h = 0;
+ return;
+ }
+ del = where - el->el_cursor.h;
+
+ if ((del < -4 || del > 4) && GoodStr(T_ch))
+ /* go there directly */
+ (void) tputs(tgoto(Str(T_ch), where, where), where, term__putc);
+ else {
+ if (del > 0) { /* moving forward */
+ if ((del > 4) && GoodStr(T_RI))
+ (void) tputs(tgoto(Str(T_RI), del, del),
+ del, term__putc);
+ else {
+ /* if I can do tabs, use them */
+ if (EL_CAN_TAB) {
+ if ((el->el_cursor.h & 0370) !=
+ (where & 0370)) {
+ /* if not within tab stop */
+ for (i =
+ (el->el_cursor.h & 0370);
+ i < (where & 0370);
+ i += 8)
+ term__putc('\t');
+ /* then tab over */
+ el->el_cursor.h = where & 0370;
+ }
+ }
+ /*
+ * it's usually cheaper to just write the
+ * chars, so we do.
+ */
+ /*
+ * NOTE THAT term_overwrite() WILL CHANGE
+ * el->el_cursor.h!!!
+ */
+ term_overwrite(el,
+ &el->el_display[el->el_cursor.v][el->el_cursor.h],
+ where - el->el_cursor.h);
+
+ }
+ } else { /* del < 0 := moving backward */
+ if ((-del > 4) && GoodStr(T_LE))
+ (void) tputs(tgoto(Str(T_LE), -del, -del),
+ -del, term__putc);
+ else { /* can't go directly there */
+ /*
+ * if the "cost" is greater than the "cost"
+ * from col 0
+ */
+ if (EL_CAN_TAB ?
+ (-del > (((unsigned int) where >> 3) +
+ (where & 07)))
+ : (-del > where)) {
+ term__putc('\r'); /* do a CR */
+ el->el_cursor.h = 0;
+ goto mc_again; /* and try again */
+ }
+ for (i = 0; i < -del; i++)
+ term__putc('\b');
+ }
+ }
+ }
+ el->el_cursor.h = where; /* now where is here */
+}
+
+
+/* term_overwrite():
+ * Overstrike num characters
+ */
+protected void
+term_overwrite(EditLine *el, const char *cp, int n)
+{
+ if (n <= 0)
+ return; /* catch bugs */
+
+ if (n > el->el_term.t_size.h) {
+#ifdef DEBUG_SCREEN
+ (void) fprintf(el->el_errfile,
+ "term_overwrite: n is riduculous: %d\r\n", n);
+#endif /* DEBUG_SCREEN */
+ return;
+ }
+ do {
+ term__putc(*cp++);
+ el->el_cursor.h++;
+ } while (--n);
+
+ if (el->el_cursor.h >= el->el_term.t_size.h) { /* wrap? */
+ if (EL_HAS_AUTO_MARGINS) { /* yes */
+ el->el_cursor.h = 0;
+ el->el_cursor.v++;
+ if (EL_HAS_MAGIC_MARGINS) {
+ /* force the wrap to avoid the "magic"
+ * situation */
+ char c;
+ if ((c = el->el_display[el->el_cursor.v][el->el_cursor.h])
+ != '\0')
+ term_overwrite(el, &c, 1);
+ else
+ term__putc(' ');
+ el->el_cursor.h = 1;
+ }
+ } else /* no wrap, but cursor stays on screen */
+ el->el_cursor.h = el->el_term.t_size.h;
+ }
+}
+
+
+/* term_deletechars():
+ * Delete num characters
+ */
+protected void
+term_deletechars(EditLine *el, int num)
+{
+ if (num <= 0)
+ return;
+
+ if (!EL_CAN_DELETE) {
+#ifdef DEBUG_EDIT
+ (void) fprintf(el->el_errfile, " ERROR: cannot delete \n");
+#endif /* DEBUG_EDIT */
+ return;
+ }
+ if (num > el->el_term.t_size.h) {
+#ifdef DEBUG_SCREEN
+ (void) fprintf(el->el_errfile,
+ "term_deletechars: num is riduculous: %d\r\n", num);
+#endif /* DEBUG_SCREEN */
+ return;
+ }
+ if (GoodStr(T_DC)) /* if I have multiple delete */
+ if ((num > 1) || !GoodStr(T_dc)) { /* if dc would be more
+ * expen. */
+ (void) tputs(tgoto(Str(T_DC), num, num),
+ num, term__putc);
+ return;
+ }
+ if (GoodStr(T_dm)) /* if I have delete mode */
+ (void) tputs(Str(T_dm), 1, term__putc);
+
+ if (GoodStr(T_dc)) /* else do one at a time */
+ while (num--)
+ (void) tputs(Str(T_dc), 1, term__putc);
+
+ if (GoodStr(T_ed)) /* if I have delete mode */
+ (void) tputs(Str(T_ed), 1, term__putc);
+}
+
+
+/* term_insertwrite():
+ * Puts terminal in insert character mode or inserts num
+ * characters in the line
+ */
+protected void
+term_insertwrite(EditLine *el, char *cp, int num)
+{
+ if (num <= 0)
+ return;
+ if (!EL_CAN_INSERT) {
+#ifdef DEBUG_EDIT
+ (void) fprintf(el->el_errfile, " ERROR: cannot insert \n");
+#endif /* DEBUG_EDIT */
+ return;
+ }
+ if (num > el->el_term.t_size.h) {
+#ifdef DEBUG_SCREEN
+ (void) fprintf(el->el_errfile,
+ "StartInsert: num is riduculous: %d\r\n", num);
+#endif /* DEBUG_SCREEN */
+ return;
+ }
+ if (GoodStr(T_IC)) /* if I have multiple insert */
+ if ((num > 1) || !GoodStr(T_ic)) {
+ /* if ic would be more expensive */
+ (void) tputs(tgoto(Str(T_IC), num, num),
+ num, term__putc);
+ term_overwrite(el, cp, num);
+ /* this updates el_cursor.h */
+ return;
+ }
+ if (GoodStr(T_im) && GoodStr(T_ei)) { /* if I have insert mode */
+ (void) tputs(Str(T_im), 1, term__putc);
+
+ el->el_cursor.h += num;
+ do
+ term__putc(*cp++);
+ while (--num);
+
+ if (GoodStr(T_ip)) /* have to make num chars insert */
+ (void) tputs(Str(T_ip), 1, term__putc);
+
+ (void) tputs(Str(T_ei), 1, term__putc);
+ return;
+ }
+ do {
+ if (GoodStr(T_ic)) /* have to make num chars insert */
+ (void) tputs(Str(T_ic), 1, term__putc);
+ /* insert a char */
+
+ term__putc(*cp++);
+
+ el->el_cursor.h++;
+
+ if (GoodStr(T_ip)) /* have to make num chars insert */
+ (void) tputs(Str(T_ip), 1, term__putc);
+ /* pad the inserted char */
+
+ } while (--num);
+}
+
+
+/* term_clear_EOL():
+ * clear to end of line. There are num characters to clear
+ */
+protected void
+term_clear_EOL(EditLine *el, int num)
+{
+ int i;
+
+ if (EL_CAN_CEOL && GoodStr(T_ce))
+ (void) tputs(Str(T_ce), 1, term__putc);
+ else {
+ for (i = 0; i < num; i++)
+ term__putc(' ');
+ el->el_cursor.h += num; /* have written num spaces */
+ }
+}
+
+
+/* term_clear_screen():
+ * Clear the screen
+ */
+protected void
+term_clear_screen(EditLine *el)
+{ /* clear the whole screen and home */
+
+ if (GoodStr(T_cl))
+ /* send the clear screen code */
+ (void) tputs(Str(T_cl), Val(T_li), term__putc);
+ else if (GoodStr(T_ho) && GoodStr(T_cd)) {
+ (void) tputs(Str(T_ho), Val(T_li), term__putc); /* home */
+ /* clear to bottom of screen */
+ (void) tputs(Str(T_cd), Val(T_li), term__putc);
+ } else {
+ term__putc('\r');
+ term__putc('\n');
+ }
+}
+
+
+/* term_beep():
+ * Beep the way the terminal wants us
+ */
+protected void
+term_beep(EditLine *el)
+{
+ if (GoodStr(T_bl))
+ /* what termcap says we should use */
+ (void) tputs(Str(T_bl), 1, term__putc);
+ else
+ term__putc('\007'); /* an ASCII bell; ^G */
+}
+
+
+#ifdef notdef
+/* term_clear_to_bottom():
+ * Clear to the bottom of the screen
+ */
+protected void
+term_clear_to_bottom(EditLine *el)
+{
+ if (GoodStr(T_cd))
+ (void) tputs(Str(T_cd), Val(T_li), term__putc);
+ else if (GoodStr(T_ce))
+ (void) tputs(Str(T_ce), Val(T_li), term__putc);
+}
+#endif
+
+
+/* term_set():
+ * Read in the terminal capabilities from the requested terminal
+ */
+protected int
+term_set(EditLine *el, const char *term)
+{
+ int i;
+ char buf[TC_BUFSIZE];
+ char *area;
+ const struct termcapstr *t;
+ sigset_t oset, nset;
+ int lins, cols;
+
+ (void) sigemptyset(&nset);
+ (void) sigaddset(&nset, SIGWINCH);
+ (void) sigprocmask(SIG_BLOCK, &nset, &oset);
+
+ area = buf;
+
+
+ if (term == NULL)
+ term = getenv("TERM");
+
+ if (!term || !term[0])
+ term = "dumb";
+
+ if (strcmp(term, "emacs") == 0)
+ el->el_flags |= EDIT_DISABLED;
+
+ memset(el->el_term.t_cap, 0, TC_BUFSIZE);
+
+ i = tgetent(el->el_term.t_cap, term);
+
+ if (i <= 0) {
+ if (i == -1)
+ (void) fprintf(el->el_errfile,
+ "Cannot read termcap database;\n");
+ else if (i == 0)
+ (void) fprintf(el->el_errfile,
+ "No entry for terminal type \"%s\";\n", term);
+ (void) fprintf(el->el_errfile,
+ "using dumb terminal settings.\n");
+ Val(T_co) = 80; /* do a dumb terminal */
+ Val(T_pt) = Val(T_km) = Val(T_li) = 0;
+ Val(T_xt) = Val(T_MT);
+ for (t = tstr; t->name != NULL; t++)
+ term_alloc(el, t, NULL);
+ } else {
+ /* auto/magic margins */
+ Val(T_am) = tgetflag("am");
+ Val(T_xn) = tgetflag("xn");
+ /* Can we tab */
+ Val(T_pt) = tgetflag("pt");
+ Val(T_xt) = tgetflag("xt");
+ /* do we have a meta? */
+ Val(T_km) = tgetflag("km");
+ Val(T_MT) = tgetflag("MT");
+ /* Get the size */
+ Val(T_co) = tgetnum("co");
+ Val(T_li) = tgetnum("li");
+ for (t = tstr; t->name != NULL; t++)
+ term_alloc(el, t, tgetstr((char *)t->name, &area));
+ }
+
+ if (Val(T_co) < 2)
+ Val(T_co) = 80; /* just in case */
+ if (Val(T_li) < 1)
+ Val(T_li) = 24;
+
+ el->el_term.t_size.v = Val(T_co);
+ el->el_term.t_size.h = Val(T_li);
+
+ term_setflags(el);
+
+ /* get the correct window size */
+ (void) term_get_size(el, &lins, &cols);
+ if (term_change_size(el, lins, cols) == -1)
+ return (-1);
+ (void) sigprocmask(SIG_SETMASK, &oset, NULL);
+ term_bind_arrow(el);
+ return (i <= 0 ? -1 : 0);
+}
+
+
+/* term_get_size():
+ * Return the new window size in lines and cols, and
+ * true if the size was changed.
+ */
+protected int
+term_get_size(EditLine *el, int *lins, int *cols)
+{
+
+ *cols = Val(T_co);
+ *lins = Val(T_li);
+
+#ifdef TIOCGWINSZ
+ {
+ struct winsize ws;
+ if (ioctl(el->el_infd, TIOCGWINSZ, (ioctl_t) & ws) != -1) {
+ if (ws.ws_col)
+ *cols = ws.ws_col;
+ if (ws.ws_row)
+ *lins = ws.ws_row;
+ }
+ }
+#endif
+#ifdef TIOCGSIZE
+ {
+ struct ttysize ts;
+ if (ioctl(el->el_infd, TIOCGSIZE, (ioctl_t) & ts) != -1) {
+ if (ts.ts_cols)
+ *cols = ts.ts_cols;
+ if (ts.ts_lines)
+ *lins = ts.ts_lines;
+ }
+ }
+#endif
+ return (Val(T_co) != *cols || Val(T_li) != *lins);
+}
+
+
+/* term_change_size():
+ * Change the size of the terminal
+ */
+protected int
+term_change_size(EditLine *el, int lins, int cols)
+{
+ /*
+ * Just in case
+ */
+ Val(T_co) = (cols < 2) ? 80 : cols;
+ Val(T_li) = (lins < 1) ? 24 : lins;
+
+ /* re-make display buffers */
+ if (term_rebuffer_display(el) == -1)
+ return (-1);
+ re_clear_display(el);
+ return (0);
+}
+
+
+/* term_init_arrow():
+ * Initialize the arrow key bindings from termcap
+ */
+private void
+term_init_arrow(EditLine *el)
+{
+ fkey_t *arrow = el->el_term.t_fkey;
+
+ arrow[A_K_DN].name = "down";
+ arrow[A_K_DN].key = T_kd;
+ arrow[A_K_DN].fun.cmd = ED_NEXT_HISTORY;
+ arrow[A_K_DN].type = XK_CMD;
+
+ arrow[A_K_UP].name = "up";
+ arrow[A_K_UP].key = T_ku;
+ arrow[A_K_UP].fun.cmd = ED_PREV_HISTORY;
+ arrow[A_K_UP].type = XK_CMD;
+
+ arrow[A_K_LT].name = "left";
+ arrow[A_K_LT].key = T_kl;
+ arrow[A_K_LT].fun.cmd = ED_PREV_CHAR;
+ arrow[A_K_LT].type = XK_CMD;
+
+ arrow[A_K_RT].name = "right";
+ arrow[A_K_RT].key = T_kr;
+ arrow[A_K_RT].fun.cmd = ED_NEXT_CHAR;
+ arrow[A_K_RT].type = XK_CMD;
+
+ arrow[A_K_HO].name = "home";
+ arrow[A_K_HO].key = T_kh;
+ arrow[A_K_HO].fun.cmd = ED_MOVE_TO_BEG;
+ arrow[A_K_HO].type = XK_CMD;
+
+ arrow[A_K_EN].name = "end";
+ arrow[A_K_EN].key = T_at7;
+ arrow[A_K_EN].fun.cmd = ED_MOVE_TO_END;
+ arrow[A_K_EN].type = XK_CMD;
+}
+
+
+/* term_reset_arrow():
+ * Reset arrow key bindings
+ */
+private void
+term_reset_arrow(EditLine *el)
+{
+ fkey_t *arrow = el->el_term.t_fkey;
+ static const char strA[] = {033, '[', 'A', '\0'};
+ static const char strB[] = {033, '[', 'B', '\0'};
+ static const char strC[] = {033, '[', 'C', '\0'};
+ static const char strD[] = {033, '[', 'D', '\0'};
+ static const char strH[] = {033, '[', 'H', '\0'};
+ static const char strF[] = {033, '[', 'F', '\0'};
+ static const char stOA[] = {033, 'O', 'A', '\0'};
+ static const char stOB[] = {033, 'O', 'B', '\0'};
+ static const char stOC[] = {033, 'O', 'C', '\0'};
+ static const char stOD[] = {033, 'O', 'D', '\0'};
+ static const char stOH[] = {033, 'O', 'H', '\0'};
+ static const char stOF[] = {033, 'O', 'F', '\0'};
+
+ key_add(el, strA, &arrow[A_K_UP].fun, arrow[A_K_UP].type);
+ key_add(el, strB, &arrow[A_K_DN].fun, arrow[A_K_DN].type);
+ key_add(el, strC, &arrow[A_K_RT].fun, arrow[A_K_RT].type);
+ key_add(el, strD, &arrow[A_K_LT].fun, arrow[A_K_LT].type);
+ key_add(el, strH, &arrow[A_K_HO].fun, arrow[A_K_HO].type);
+ key_add(el, strF, &arrow[A_K_EN].fun, arrow[A_K_EN].type);
+ key_add(el, stOA, &arrow[A_K_UP].fun, arrow[A_K_UP].type);
+ key_add(el, stOB, &arrow[A_K_DN].fun, arrow[A_K_DN].type);
+ key_add(el, stOC, &arrow[A_K_RT].fun, arrow[A_K_RT].type);
+ key_add(el, stOD, &arrow[A_K_LT].fun, arrow[A_K_LT].type);
+ key_add(el, stOH, &arrow[A_K_HO].fun, arrow[A_K_HO].type);
+ key_add(el, stOF, &arrow[A_K_EN].fun, arrow[A_K_EN].type);
+
+ if (el->el_map.type == MAP_VI) {
+ key_add(el, &strA[1], &arrow[A_K_UP].fun, arrow[A_K_UP].type);
+ key_add(el, &strB[1], &arrow[A_K_DN].fun, arrow[A_K_DN].type);
+ key_add(el, &strC[1], &arrow[A_K_RT].fun, arrow[A_K_RT].type);
+ key_add(el, &strD[1], &arrow[A_K_LT].fun, arrow[A_K_LT].type);
+ key_add(el, &strH[1], &arrow[A_K_HO].fun, arrow[A_K_HO].type);
+ key_add(el, &strF[1], &arrow[A_K_EN].fun, arrow[A_K_EN].type);
+ key_add(el, &stOA[1], &arrow[A_K_UP].fun, arrow[A_K_UP].type);
+ key_add(el, &stOB[1], &arrow[A_K_DN].fun, arrow[A_K_DN].type);
+ key_add(el, &stOC[1], &arrow[A_K_RT].fun, arrow[A_K_RT].type);
+ key_add(el, &stOD[1], &arrow[A_K_LT].fun, arrow[A_K_LT].type);
+ key_add(el, &stOH[1], &arrow[A_K_HO].fun, arrow[A_K_HO].type);
+ key_add(el, &stOF[1], &arrow[A_K_EN].fun, arrow[A_K_EN].type);
+ }
+}
+
+
+/* term_set_arrow():
+ * Set an arrow key binding
+ */
+protected int
+term_set_arrow(EditLine *el, const char *name, key_value_t *fun, int type)
+{
+ fkey_t *arrow = el->el_term.t_fkey;
+ int i;
+
+ for (i = 0; i < A_K_NKEYS; i++)
+ if (strcmp(name, arrow[i].name) == 0) {
+ arrow[i].fun = *fun;
+ arrow[i].type = type;
+ return (0);
+ }
+ return (-1);
+}
+
+
+/* term_clear_arrow():
+ * Clear an arrow key binding
+ */
+protected int
+term_clear_arrow(EditLine *el, const char *name)
+{
+ fkey_t *arrow = el->el_term.t_fkey;
+ int i;
+
+ for (i = 0; i < A_K_NKEYS; i++)
+ if (strcmp(name, arrow[i].name) == 0) {
+ arrow[i].type = XK_NOD;
+ return (0);
+ }
+ return (-1);
+}
+
+
+/* term_print_arrow():
+ * Print the arrow key bindings
+ */
+protected void
+term_print_arrow(EditLine *el, const char *name)
+{
+ int i;
+ fkey_t *arrow = el->el_term.t_fkey;
+
+ for (i = 0; i < A_K_NKEYS; i++)
+ if (*name == '\0' || strcmp(name, arrow[i].name) == 0)
+ if (arrow[i].type != XK_NOD)
+ key_kprint(el, arrow[i].name, &arrow[i].fun,
+ arrow[i].type);
+}
+
+
+/* term_bind_arrow():
+ * Bind the arrow keys
+ */
+protected void
+term_bind_arrow(EditLine *el)
+{
+ el_action_t *map;
+ const el_action_t *dmap;
+ int i, j;
+ char *p;
+ fkey_t *arrow = el->el_term.t_fkey;
+
+ /* Check if the components needed are initialized */
+ if (el->el_term.t_buf == NULL || el->el_map.key == NULL)
+ return;
+
+ map = el->el_map.type == MAP_VI ? el->el_map.alt : el->el_map.key;
+ dmap = el->el_map.type == MAP_VI ? el->el_map.vic : el->el_map.emacs;
+
+ term_reset_arrow(el);
+
+ for (i = 0; i < A_K_NKEYS; i++) {
+ p = el->el_term.t_str[arrow[i].key];
+ if (p && *p) {
+ j = (unsigned char) *p;
+ /*
+ * Assign the arrow keys only if:
+ *
+ * 1. They are multi-character arrow keys and the user
+ * has not re-assigned the leading character, or
+ * has re-assigned the leading character to be
+ * ED_SEQUENCE_LEAD_IN
+ * 2. They are single arrow keys pointing to an
+ * unassigned key.
+ */
+ if (arrow[i].type == XK_NOD)
+ key_clear(el, map, p);
+ else {
+ if (p[1] && (dmap[j] == map[j] ||
+ map[j] == ED_SEQUENCE_LEAD_IN)) {
+ key_add(el, p, &arrow[i].fun,
+ arrow[i].type);
+ map[j] = ED_SEQUENCE_LEAD_IN;
+ } else if (map[j] == ED_UNASSIGNED) {
+ key_clear(el, map, p);
+ if (arrow[i].type == XK_CMD)
+ map[j] = arrow[i].fun.cmd;
+ else
+ key_add(el, p, &arrow[i].fun,
+ arrow[i].type);
+ }
+ }
+ }
+ }
+}
+
+
+/* term__putc():
+ * Add a character
+ */
+protected int
+term__putc(int c)
+{
+
+ return (fputc(c, term_outfile));
+}
+
+
+/* term__flush():
+ * Flush output
+ */
+protected void
+term__flush(void)
+{
+
+ (void) fflush(term_outfile);
+}
+
+
+/* term_telltc():
+ * Print the current termcap characteristics
+ */
+protected int
+/*ARGSUSED*/
+term_telltc(EditLine *el, int argc, const char **argv)
+{
+ const struct termcapstr *t;
+ char **ts;
+ char upbuf[EL_BUFSIZ];
+
+ (void) fprintf(el->el_outfile, "\n\tYour terminal has the\n");
+ (void) fprintf(el->el_outfile, "\tfollowing characteristics:\n\n");
+ (void) fprintf(el->el_outfile, "\tIt has %d columns and %d lines\n",
+ Val(T_co), Val(T_li));
+ (void) fprintf(el->el_outfile,
+ "\tIt has %s meta key\n", EL_HAS_META ? "a" : "no");
+ (void) fprintf(el->el_outfile,
+ "\tIt can%suse tabs\n", EL_CAN_TAB ? " " : "not ");
+ (void) fprintf(el->el_outfile, "\tIt %s automatic margins\n",
+ EL_HAS_AUTO_MARGINS ? "has" : "does not have");
+ if (EL_HAS_AUTO_MARGINS)
+ (void) fprintf(el->el_outfile, "\tIt %s magic margins\n",
+ EL_HAS_MAGIC_MARGINS ? "has" : "does not have");
+
+ for (t = tstr, ts = el->el_term.t_str; t->name != NULL; t++, ts++)
+ (void) fprintf(el->el_outfile, "\t%25s (%s) == %s\n",
+ t->long_name,
+ t->name, *ts && **ts ?
+ key__decode_str(*ts, upbuf, "") : "(empty)");
+ (void) fputc('\n', el->el_outfile);
+ return (0);
+}
+
+
+/* term_settc():
+ * Change the current terminal characteristics
+ */
+protected int
+/*ARGSUSED*/
+term_settc(EditLine *el, int argc, const char **argv)
+{
+ const struct termcapstr *ts;
+ const struct termcapval *tv;
+ const char *what, *how;
+
+ if (argv == NULL || argv[1] == NULL || argv[2] == NULL)
+ return (-1);
+
+ what = argv[1];
+ how = argv[2];
+
+ /*
+ * Do the strings first
+ */
+ for (ts = tstr; ts->name != NULL; ts++)
+ if (strcmp(ts->name, what) == 0)
+ break;
+
+ if (ts->name != NULL) {
+ term_alloc(el, ts, how);
+ term_setflags(el);
+ return (0);
+ }
+ /*
+ * Do the numeric ones second
+ */
+ for (tv = tval; tv->name != NULL; tv++)
+ if (strcmp(tv->name, what) == 0)
+ break;
+
+ if (tv->name != NULL) {
+ if (tv == &tval[T_pt] || tv == &tval[T_km] ||
+ tv == &tval[T_am] || tv == &tval[T_xn]) {
+ if (strcmp(how, "yes") == 0)
+ el->el_term.t_val[tv - tval] = 1;
+ else if (strcmp(how, "no") == 0)
+ el->el_term.t_val[tv - tval] = 0;
+ else {
+ (void) fprintf(el->el_errfile,
+ "settc: Bad value `%s'.\n", how);
+ return (-1);
+ }
+ term_setflags(el);
+ if (term_change_size(el, Val(T_li), Val(T_co)) == -1)
+ return (-1);
+ return (0);
+ } else {
+ long i;
+ char *ep;
+
+ i = strtol(how, &ep, 10);
+ if (*ep != '\0') {
+ (void) fprintf(el->el_errfile,
+ "settc: Bad value `%s'.\n", how);
+ return (-1);
+ }
+ el->el_term.t_val[tv - tval] = (int) i;
+ el->el_term.t_size.v = Val(T_co);
+ el->el_term.t_size.h = Val(T_li);
+ if (tv == &tval[T_co] || tv == &tval[T_li])
+ if (term_change_size(el, Val(T_li), Val(T_co))
+ == -1)
+ return (-1);
+ return (0);
+ }
+ }
+ return (-1);
+}
+
+
+/* term_echotc():
+ * Print the termcap string out with variable substitution
+ */
+protected int
+/*ARGSUSED*/
+term_echotc(EditLine *el, int argc, const char **argv)
+{
+ char *cap, *scap, *ep;
+ int arg_need, arg_cols, arg_rows;
+ int verbose = 0, silent = 0;
+ char *area;
+ static const char fmts[] = "%s\n", fmtd[] = "%d\n";
+ const struct termcapstr *t;
+ char buf[TC_BUFSIZE];
+ long i;
+
+ area = buf;
+
+ if (argv == NULL || argv[1] == NULL)
+ return (-1);
+ argv++;
+
+ if (argv[0][0] == '-') {
+ switch (argv[0][1]) {
+ case 'v':
+ verbose = 1;
+ break;
+ case 's':
+ silent = 1;
+ break;
+ default:
+ /* stderror(ERR_NAME | ERR_TCUSAGE); */
+ break;
+ }
+ argv++;
+ }
+ if (!*argv || *argv[0] == '\0')
+ return (0);
+ if (strcmp(*argv, "tabs") == 0) {
+ (void) fprintf(el->el_outfile, fmts, EL_CAN_TAB ? "yes" : "no");
+ return (0);
+ } else if (strcmp(*argv, "meta") == 0) {
+ (void) fprintf(el->el_outfile, fmts, Val(T_km) ? "yes" : "no");
+ return (0);
+ } else if (strcmp(*argv, "xn") == 0) {
+ (void) fprintf(el->el_outfile, fmts, EL_HAS_MAGIC_MARGINS ?
+ "yes" : "no");
+ return (0);
+ } else if (strcmp(*argv, "am") == 0) {
+ (void) fprintf(el->el_outfile, fmts, EL_HAS_AUTO_MARGINS ?
+ "yes" : "no");
+ return (0);
+ } else if (strcmp(*argv, "baud") == 0) {
+#ifdef notdef
+ int i;
+
+ for (i = 0; baud_rate[i].b_name != NULL; i++)
+ if (el->el_tty.t_speed == baud_rate[i].b_rate) {
+ (void) fprintf(el->el_outfile, fmts,
+ baud_rate[i].b_name);
+ return (0);
+ }
+ (void) fprintf(el->el_outfile, fmtd, 0);
+#else
+ (void) fprintf(el->el_outfile, fmtd, (int) el->el_tty.t_speed);
+#endif
+ return (0);
+ } else if (strcmp(*argv, "rows") == 0 || strcmp(*argv, "lines") == 0) {
+ (void) fprintf(el->el_outfile, fmtd, Val(T_li));
+ return (0);
+ } else if (strcmp(*argv, "cols") == 0) {
+ (void) fprintf(el->el_outfile, fmtd, Val(T_co));
+ return (0);
+ }
+ /*
+ * Try to use our local definition first
+ */
+ scap = NULL;
+ for (t = tstr; t->name != NULL; t++)
+ if (strcmp(t->name, *argv) == 0) {
+ scap = el->el_term.t_str[t - tstr];
+ break;
+ }
+ if (t->name == NULL)
+ scap = tgetstr((char *)argv, &area);
+ if (!scap || scap[0] == '\0') {
+ if (!silent)
+ (void) fprintf(el->el_errfile,
+ "echotc: Termcap parameter `%s' not found.\n",
+ *argv);
+ return (-1);
+ }
+ /*
+ * Count home many values we need for this capability.
+ */
+ for (cap = scap, arg_need = 0; *cap; cap++)
+ if (*cap == '%')
+ switch (*++cap) {
+ case 'd':
+ case '2':
+ case '3':
+ case '.':
+ case '+':
+ arg_need++;
+ break;
+ case '%':
+ case '>':
+ case 'i':
+ case 'r':
+ case 'n':
+ case 'B':
+ case 'D':
+ break;
+ default:
+ /*
+ * hpux has lot's of them...
+ */
+ if (verbose)
+ (void) fprintf(el->el_errfile,
+ "echotc: Warning: unknown termcap %% `%c'.\n",
+ *cap);
+ /* This is bad, but I won't complain */
+ break;
+ }
+
+ switch (arg_need) {
+ case 0:
+ argv++;
+ if (*argv && *argv[0]) {
+ if (!silent)
+ (void) fprintf(el->el_errfile,
+ "echotc: Warning: Extra argument `%s'.\n",
+ *argv);
+ return (-1);
+ }
+ (void) tputs(scap, 1, term__putc);
+ break;
+ case 1:
+ argv++;
+ if (!*argv || *argv[0] == '\0') {
+ if (!silent)
+ (void) fprintf(el->el_errfile,
+ "echotc: Warning: Missing argument.\n");
+ return (-1);
+ }
+ arg_cols = 0;
+ i = strtol(*argv, &ep, 10);
+ if (*ep != '\0' || i < 0) {
+ if (!silent)
+ (void) fprintf(el->el_errfile,
+ "echotc: Bad value `%s' for rows.\n",
+ *argv);
+ return (-1);
+ }
+ arg_rows = (int) i;
+ argv++;
+ if (*argv && *argv[0]) {
+ if (!silent)
+ (void) fprintf(el->el_errfile,
+ "echotc: Warning: Extra argument `%s'.\n",
+ *argv);
+ return (-1);
+ }
+ (void) tputs(tgoto(scap, arg_cols, arg_rows), 1, term__putc);
+ break;
+ default:
+ /* This is wrong, but I will ignore it... */
+ if (verbose)
+ (void) fprintf(el->el_errfile,
+ "echotc: Warning: Too many required arguments (%d).\n",
+ arg_need);
+ /* FALLTHROUGH */
+ case 2:
+ argv++;
+ if (!*argv || *argv[0] == '\0') {
+ if (!silent)
+ (void) fprintf(el->el_errfile,
+ "echotc: Warning: Missing argument.\n");
+ return (-1);
+ }
+ i = strtol(*argv, &ep, 10);
+ if (*ep != '\0' || i < 0) {
+ if (!silent)
+ (void) fprintf(el->el_errfile,
+ "echotc: Bad value `%s' for cols.\n",
+ *argv);
+ return (-1);
+ }
+ arg_cols = (int) i;
+ argv++;
+ if (!*argv || *argv[0] == '\0') {
+ if (!silent)
+ (void) fprintf(el->el_errfile,
+ "echotc: Warning: Missing argument.\n");
+ return (-1);
+ }
+ i = strtol(*argv, &ep, 10);
+ if (*ep != '\0' || i < 0) {
+ if (!silent)
+ (void) fprintf(el->el_errfile,
+ "echotc: Bad value `%s' for rows.\n",
+ *argv);
+ return (-1);
+ }
+ arg_rows = (int) i;
+ if (*ep != '\0') {
+ if (!silent)
+ (void) fprintf(el->el_errfile,
+ "echotc: Bad value `%s'.\n", *argv);
+ return (-1);
+ }
+ argv++;
+ if (*argv && *argv[0]) {
+ if (!silent)
+ (void) fprintf(el->el_errfile,
+ "echotc: Warning: Extra argument `%s'.\n",
+ *argv);
+ return (-1);
+ }
+ (void) tputs(tgoto(scap, arg_cols, arg_rows), arg_rows,
+ term__putc);
+ break;
+ }
+ return (0);
+}
diff --git a/trunk/main/editline/term.h b/trunk/main/editline/term.h
new file mode 100644
index 000000000..47e08e84b
--- /dev/null
+++ b/trunk/main/editline/term.h
@@ -0,0 +1,124 @@
+/* $NetBSD: term.h,v 1.13 2002/03/18 16:01:00 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)term.h 8.1 (Berkeley) 6/4/93
+ */
+
+/*
+ * el.term.h: Termcap header
+ */
+#ifndef _h_el_term
+#define _h_el_term
+
+#include "histedit.h"
+
+typedef struct { /* Symbolic function key bindings */
+ const char *name; /* name of the key */
+ int key; /* Index in termcap table */
+ key_value_t fun; /* Function bound to it */
+ int type; /* Type of function */
+} fkey_t;
+
+typedef struct {
+ coord_t t_size; /* # lines and cols */
+ int t_flags;
+#define TERM_CAN_INSERT 0x001 /* Has insert cap */
+#define TERM_CAN_DELETE 0x002 /* Has delete cap */
+#define TERM_CAN_CEOL 0x004 /* Has CEOL cap */
+#define TERM_CAN_TAB 0x008 /* Can use tabs */
+#define TERM_CAN_ME 0x010 /* Can turn all attrs. */
+#define TERM_CAN_UP 0x020 /* Can move up */
+#define TERM_HAS_META 0x040 /* Has a meta key */
+#define TERM_HAS_AUTO_MARGINS 0x080 /* Has auto margins */
+#define TERM_HAS_MAGIC_MARGINS 0x100 /* Has magic margins */
+ char *t_buf; /* Termcap buffer */
+ int t_loc; /* location used */
+ char **t_str; /* termcap strings */
+ int *t_val; /* termcap values */
+ char *t_cap; /* Termcap buffer */
+ fkey_t *t_fkey; /* Array of keys */
+} el_term_t;
+
+/*
+ * fKey indexes
+ */
+#define A_K_DN 0
+#define A_K_UP 1
+#define A_K_LT 2
+#define A_K_RT 3
+#define A_K_HO 4
+#define A_K_EN 5
+#define A_K_NKEYS 6
+
+protected void term_move_to_line(EditLine *, int);
+protected void term_move_to_char(EditLine *, int);
+protected void term_clear_EOL(EditLine *, int);
+protected void term_overwrite(EditLine *, const char *, int);
+protected void term_insertwrite(EditLine *, char *, int);
+protected void term_deletechars(EditLine *, int);
+protected void term_clear_screen(EditLine *);
+protected void term_beep(EditLine *);
+protected int term_change_size(EditLine *, int, int);
+protected int term_get_size(EditLine *, int *, int *);
+protected int term_init(EditLine *);
+protected void term_bind_arrow(EditLine *);
+protected void term_print_arrow(EditLine *, const char *);
+protected int term_clear_arrow(EditLine *, const char *);
+protected int term_set_arrow(EditLine *, const char *, key_value_t *, int);
+protected void term_end(EditLine *);
+protected int term_set(EditLine *, const char *);
+protected int term_settc(EditLine *, int, const char **);
+protected int term_telltc(EditLine *, int, const char **);
+protected int term_echotc(EditLine *, int, const char **);
+protected int term__putc(int);
+protected void term__flush(void);
+
+/*
+ * Easy access macros
+ */
+#define EL_FLAGS (el)->el_term.t_flags
+
+#define EL_CAN_INSERT (EL_FLAGS & TERM_CAN_INSERT)
+#define EL_CAN_DELETE (EL_FLAGS & TERM_CAN_DELETE)
+#define EL_CAN_CEOL (EL_FLAGS & TERM_CAN_CEOL)
+#define EL_CAN_TAB (EL_FLAGS & TERM_CAN_TAB)
+#define EL_CAN_ME (EL_FLAGS & TERM_CAN_ME)
+#define EL_HAS_META (EL_FLAGS & TERM_HAS_META)
+#define EL_HAS_AUTO_MARGINS (EL_FLAGS & TERM_HAS_AUTO_MARGINS)
+#define EL_HAS_MAGIC_MARGINS (EL_FLAGS & TERM_HAS_MAGIC_MARGINS)
+
+#endif /* _h_el_term */
diff --git a/trunk/main/editline/tokenizer.c b/trunk/main/editline/tokenizer.c
new file mode 100644
index 000000000..f0de39bc9
--- /dev/null
+++ b/trunk/main/editline/tokenizer.c
@@ -0,0 +1,397 @@
+/* $NetBSD: tokenizer.c,v 1.10 2002/03/18 16:01:00 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+#if !defined(lint) && !defined(SCCSID)
+#if 0
+static char sccsid[] = "@(#)tokenizer.c 8.1 (Berkeley) 6/4/93";
+#else
+__RCSID("$NetBSD: tokenizer.c,v 1.10 2002/03/18 16:01:00 christos Exp $");
+#endif
+#endif /* not lint && not SCCSID */
+
+/*
+ * tokenize.c: Bourne shell like tokenizer
+ */
+#include <string.h>
+#include <stdlib.h>
+#include "tokenizer.h"
+
+typedef enum {
+ Q_none, Q_single, Q_double, Q_one, Q_doubleone
+} quote_t;
+
+#define IFS "\t \n"
+
+#define TOK_KEEP 1
+#define TOK_EAT 2
+
+#define WINCR 20
+#define AINCR 10
+
+#define tok_malloc(a) malloc(a)
+#define tok_free(a) free(a)
+#define tok_realloc(a, b) realloc(a, b)
+
+
+struct tokenizer {
+ char *ifs; /* In field separator */
+ int argc, amax; /* Current and maximum number of args */
+ char **argv; /* Argument list */
+ char *wptr, *wmax; /* Space and limit on the word buffer */
+ char *wstart; /* Beginning of next word */
+ char *wspace; /* Space of word buffer */
+ quote_t quote; /* Quoting state */
+ int flags; /* flags; */
+};
+
+
+private void tok_finish(Tokenizer *);
+
+
+/* tok_finish():
+ * Finish a word in the tokenizer.
+ */
+private void
+tok_finish(Tokenizer *tok)
+{
+
+ *tok->wptr = '\0';
+ if ((tok->flags & TOK_KEEP) || tok->wptr != tok->wstart) {
+ tok->argv[tok->argc++] = tok->wstart;
+ tok->argv[tok->argc] = NULL;
+ tok->wstart = ++tok->wptr;
+ }
+ tok->flags &= ~TOK_KEEP;
+}
+
+
+/* tok_init():
+ * Initialize the tokenizer
+ */
+public Tokenizer *
+tok_init(const char *ifs)
+{
+ Tokenizer *tok = (Tokenizer *) tok_malloc(sizeof(Tokenizer));
+
+ tok->ifs = strdup(ifs ? ifs : IFS);
+ tok->argc = 0;
+ tok->amax = AINCR;
+ tok->argv = (char **) tok_malloc(sizeof(char *) * tok->amax);
+ if (tok->argv == NULL)
+ return (NULL);
+ tok->argv[0] = NULL;
+ tok->wspace = (char *) tok_malloc(WINCR);
+ if (tok->wspace == NULL)
+ return (NULL);
+ tok->wmax = tok->wspace + WINCR;
+ tok->wstart = tok->wspace;
+ tok->wptr = tok->wspace;
+ tok->flags = 0;
+ tok->quote = Q_none;
+
+ return (tok);
+}
+
+
+/* tok_reset():
+ * Reset the tokenizer
+ */
+public void
+tok_reset(Tokenizer *tok)
+{
+
+ tok->argc = 0;
+ tok->wstart = tok->wspace;
+ tok->wptr = tok->wspace;
+ tok->flags = 0;
+ tok->quote = Q_none;
+}
+
+
+/* tok_end():
+ * Clean up
+ */
+public void
+tok_end(Tokenizer *tok)
+{
+
+ tok_free((ptr_t) tok->ifs);
+ tok_free((ptr_t) tok->wspace);
+ tok_free((ptr_t) tok->argv);
+ tok_free((ptr_t) tok);
+}
+
+
+
+/* tok_line():
+ * Bourne shell like tokenizing
+ * Return:
+ * -1: Internal error
+ * 3: Quoted return
+ * 2: Unmatched double quote
+ * 1: Unmatched single quote
+ * 0: Ok
+ */
+public int
+tok_line(Tokenizer *tok, const char *line, int *argc, const char ***argv)
+{
+ const char *ptr;
+
+ for (;;) {
+ switch (*(ptr = line++)) {
+ case '\'':
+ tok->flags |= TOK_KEEP;
+ tok->flags &= ~TOK_EAT;
+ switch (tok->quote) {
+ case Q_none:
+ tok->quote = Q_single; /* Enter single quote
+ * mode */
+ break;
+
+ case Q_single: /* Exit single quote mode */
+ tok->quote = Q_none;
+ break;
+
+ case Q_one: /* Quote this ' */
+ tok->quote = Q_none;
+ *tok->wptr++ = *ptr;
+ break;
+
+ case Q_double: /* Stay in double quote mode */
+ *tok->wptr++ = *ptr;
+ break;
+
+ case Q_doubleone: /* Quote this ' */
+ tok->quote = Q_double;
+ *tok->wptr++ = *ptr;
+ break;
+
+ default:
+ return (-1);
+ }
+ break;
+
+ case '"':
+ tok->flags &= ~TOK_EAT;
+ tok->flags |= TOK_KEEP;
+ switch (tok->quote) {
+ case Q_none: /* Enter double quote mode */
+ tok->quote = Q_double;
+ break;
+
+ case Q_double: /* Exit double quote mode */
+ tok->quote = Q_none;
+ break;
+
+ case Q_one: /* Quote this " */
+ tok->quote = Q_none;
+ *tok->wptr++ = *ptr;
+ break;
+
+ case Q_single: /* Stay in single quote mode */
+ *tok->wptr++ = *ptr;
+ break;
+
+ case Q_doubleone: /* Quote this " */
+ tok->quote = Q_double;
+ *tok->wptr++ = *ptr;
+ break;
+
+ default:
+ return (-1);
+ }
+ break;
+
+ case '\\':
+ tok->flags |= TOK_KEEP;
+ tok->flags &= ~TOK_EAT;
+ switch (tok->quote) {
+ case Q_none: /* Quote next character */
+ tok->quote = Q_one;
+ break;
+
+ case Q_double: /* Quote next character */
+ tok->quote = Q_doubleone;
+ break;
+
+ case Q_one: /* Quote this, restore state */
+ *tok->wptr++ = *ptr;
+ tok->quote = Q_none;
+ break;
+
+ case Q_single: /* Stay in single quote mode */
+ *tok->wptr++ = *ptr;
+ break;
+
+ case Q_doubleone: /* Quote this \ */
+ tok->quote = Q_double;
+ *tok->wptr++ = *ptr;
+ break;
+
+ default:
+ return (-1);
+ }
+ break;
+
+ case '\n':
+ tok->flags &= ~TOK_EAT;
+ switch (tok->quote) {
+ case Q_none:
+ tok_finish(tok);
+ *argv = (const char **)tok->argv;
+ *argc = tok->argc;
+ return (0);
+
+ case Q_single:
+ case Q_double:
+ *tok->wptr++ = *ptr; /* Add the return */
+ break;
+
+ case Q_doubleone: /* Back to double, eat the '\n' */
+ tok->flags |= TOK_EAT;
+ tok->quote = Q_double;
+ break;
+
+ case Q_one: /* No quote, more eat the '\n' */
+ tok->flags |= TOK_EAT;
+ tok->quote = Q_none;
+ break;
+
+ default:
+ return (0);
+ }
+ break;
+
+ case '\0':
+ switch (tok->quote) {
+ case Q_none:
+ /* Finish word and return */
+ if (tok->flags & TOK_EAT) {
+ tok->flags &= ~TOK_EAT;
+ return (3);
+ }
+ tok_finish(tok);
+ *argv = (const char **)tok->argv;
+ *argc = tok->argc;
+ return (0);
+
+ case Q_single:
+ return (1);
+
+ case Q_double:
+ return (2);
+
+ case Q_doubleone:
+ tok->quote = Q_double;
+ *tok->wptr++ = *ptr;
+ break;
+
+ case Q_one:
+ tok->quote = Q_none;
+ *tok->wptr++ = *ptr;
+ break;
+
+ default:
+ return (-1);
+ }
+ break;
+
+ default:
+ tok->flags &= ~TOK_EAT;
+ switch (tok->quote) {
+ case Q_none:
+ if (strchr(tok->ifs, *ptr) != NULL)
+ tok_finish(tok);
+ else
+ *tok->wptr++ = *ptr;
+ break;
+
+ case Q_single:
+ case Q_double:
+ *tok->wptr++ = *ptr;
+ break;
+
+
+ case Q_doubleone:
+ *tok->wptr++ = '\\';
+ tok->quote = Q_double;
+ *tok->wptr++ = *ptr;
+ break;
+
+ case Q_one:
+ tok->quote = Q_none;
+ *tok->wptr++ = *ptr;
+ break;
+
+ default:
+ return (-1);
+
+ }
+ break;
+ }
+
+ if (tok->wptr >= tok->wmax - 4) {
+ size_t size = tok->wmax - tok->wspace + WINCR;
+ char *s = (char *) tok_realloc(tok->wspace, size);
+ if (s == NULL)
+ return (-1);
+
+ if (s != tok->wspace) {
+ int i;
+ for (i = 0; i < tok->argc; i++) {
+ tok->argv[i] =
+ (tok->argv[i] - tok->wspace) + s;
+ }
+ tok->wptr = (tok->wptr - tok->wspace) + s;
+ tok->wstart = (tok->wstart - tok->wspace) + s;
+ tok->wspace = s;
+ }
+ tok->wmax = s + size;
+ }
+ if (tok->argc >= tok->amax - 4) {
+ char **p;
+ tok->amax += AINCR;
+ p = (char **) tok_realloc(tok->argv,
+ tok->amax * sizeof(char *));
+ if (p == NULL)
+ return (-1);
+ tok->argv = p;
+ }
+ }
+}
diff --git a/trunk/main/editline/tokenizer.h b/trunk/main/editline/tokenizer.h
new file mode 100644
index 000000000..7cc7a3346
--- /dev/null
+++ b/trunk/main/editline/tokenizer.h
@@ -0,0 +1,54 @@
+/* $NetBSD: tokenizer.h,v 1.5 2002/03/18 16:01:00 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tokenizer.h 8.1 (Berkeley) 6/4/93
+ */
+
+/*
+ * tokenizer.h: Header file for tokenizer routines
+ */
+#ifndef _h_tokenizer
+#define _h_tokenizer
+
+typedef struct tokenizer Tokenizer;
+
+Tokenizer *tok_init(const char *);
+void tok_reset(Tokenizer *);
+void tok_end(Tokenizer *);
+int tok_line(Tokenizer *, const char *, int *, const char ***);
+
+#endif /* _h_tokenizer */
diff --git a/trunk/main/editline/tty.c b/trunk/main/editline/tty.c
new file mode 100644
index 000000000..256cf780b
--- /dev/null
+++ b/trunk/main/editline/tty.c
@@ -0,0 +1,1182 @@
+/* $NetBSD: tty.c,v 1.16 2002/03/18 16:01:01 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+#if !defined(lint) && !defined(SCCSID)
+#if 0
+static char sccsid[] = "@(#)tty.c 8.1 (Berkeley) 6/4/93";
+#else
+__RCSID("$NetBSD: tty.c,v 1.16 2002/03/18 16:01:01 christos Exp $");
+#endif
+#endif /* not lint && not SCCSID */
+
+/*
+ * tty.c: tty interface stuff
+ */
+#include "tty.h"
+#include "el.h"
+
+typedef struct ttymodes_t {
+ const char *m_name;
+ u_int m_value;
+ int m_type;
+} ttymodes_t;
+
+typedef struct ttymap_t {
+ int nch, och; /* Internal and termio rep of chars */
+ el_action_t bind[3]; /* emacs, vi, and vi-cmd */
+} ttymap_t;
+
+
+private const ttyperm_t ttyperm = {
+ {
+ {"iflag:", ICRNL, (INLCR | IGNCR)},
+ {"oflag:", (OPOST | ONLCR), ONLRET},
+ {"cflag:", 0, 0},
+ {"lflag:", (ISIG | ICANON | ECHO | ECHOE | ECHOCTL | IEXTEN),
+ (NOFLSH | ECHONL | EXTPROC | FLUSHO)},
+ {"chars:", 0, 0},
+ },
+ {
+ {"iflag:", (INLCR | ICRNL), IGNCR},
+ {"oflag:", (OPOST | ONLCR), ONLRET},
+ {"cflag:", 0, 0},
+ {"lflag:", ISIG,
+ (NOFLSH | ICANON | ECHO | ECHOK | ECHONL | EXTPROC | IEXTEN | FLUSHO)},
+ {"chars:", (C_SH(C_MIN) | C_SH(C_TIME) | C_SH(C_SWTCH) | C_SH(C_DSWTCH) |
+ C_SH(C_SUSP) | C_SH(C_DSUSP) | C_SH(C_EOL) | C_SH(C_DISCARD) |
+ C_SH(C_PGOFF) | C_SH(C_PAGE) | C_SH(C_STATUS)), 0}
+ },
+ {
+ {"iflag:", 0, IXON | IXOFF | INLCR | ICRNL},
+ {"oflag:", 0, 0},
+ {"cflag:", 0, 0},
+ {"lflag:", 0, ISIG | IEXTEN},
+ {"chars:", 0, 0},
+ }
+};
+
+private const ttychar_t ttychar = {
+ {
+ CINTR, CQUIT, CERASE, CKILL,
+ CEOF, CEOL, CEOL2, CSWTCH,
+ CDSWTCH, CERASE2, CSTART, CSTOP,
+ CWERASE, CSUSP, CDSUSP, CREPRINT,
+ CDISCARD, CLNEXT, CSTATUS, CPAGE,
+ CPGOFF, CKILL2, CBRK, CMIN,
+ CTIME
+ },
+ {
+ CINTR, CQUIT, CERASE, CKILL,
+ _POSIX_VDISABLE, _POSIX_VDISABLE, _POSIX_VDISABLE, _POSIX_VDISABLE,
+ _POSIX_VDISABLE, CERASE2, CSTART, CSTOP,
+ _POSIX_VDISABLE, CSUSP, _POSIX_VDISABLE, _POSIX_VDISABLE,
+ CDISCARD, _POSIX_VDISABLE, _POSIX_VDISABLE, _POSIX_VDISABLE,
+ _POSIX_VDISABLE, _POSIX_VDISABLE, _POSIX_VDISABLE, 1,
+ 0
+ },
+ {
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0
+ }
+};
+
+private const ttymap_t tty_map[] = {
+#ifdef VERASE
+ {C_ERASE, VERASE,
+ {ED_DELETE_PREV_CHAR, VI_DELETE_PREV_CHAR, ED_PREV_CHAR}},
+#endif /* VERASE */
+#ifdef VERASE2
+ {C_ERASE2, VERASE2,
+ {ED_DELETE_PREV_CHAR, VI_DELETE_PREV_CHAR, ED_PREV_CHAR}},
+#endif /* VERASE2 */
+#ifdef VKILL
+ {C_KILL, VKILL,
+ {EM_KILL_LINE, VI_KILL_LINE_PREV, ED_UNASSIGNED}},
+#endif /* VKILL */
+#ifdef VKILL2
+ {C_KILL2, VKILL2,
+ {EM_KILL_LINE, VI_KILL_LINE_PREV, ED_UNASSIGNED}},
+#endif /* VKILL2 */
+#ifdef VEOF
+ {C_EOF, VEOF,
+ {EM_DELETE_OR_LIST, VI_LIST_OR_EOF, ED_UNASSIGNED}},
+#endif /* VEOF */
+#ifdef VWERASE
+ {C_WERASE, VWERASE,
+ {ED_DELETE_PREV_WORD, ED_DELETE_PREV_WORD, ED_PREV_WORD}},
+#endif /* VWERASE */
+#ifdef VREPRINT
+ {C_REPRINT, VREPRINT,
+ {ED_REDISPLAY, ED_INSERT, ED_REDISPLAY}},
+#endif /* VREPRINT */
+#ifdef VLNEXT
+ {C_LNEXT, VLNEXT,
+ {ED_QUOTED_INSERT, ED_QUOTED_INSERT, ED_UNASSIGNED}},
+#endif /* VLNEXT */
+ {-1, -1,
+ {ED_UNASSIGNED, ED_UNASSIGNED, ED_UNASSIGNED}}
+};
+
+private const ttymodes_t ttymodes[] = {
+#ifdef IGNBRK
+ {"ignbrk", IGNBRK, MD_INP},
+#endif /* IGNBRK */
+#ifdef BRKINT
+ {"brkint", BRKINT, MD_INP},
+#endif /* BRKINT */
+#ifdef IGNPAR
+ {"ignpar", IGNPAR, MD_INP},
+#endif /* IGNPAR */
+#ifdef PARMRK
+ {"parmrk", PARMRK, MD_INP},
+#endif /* PARMRK */
+#ifdef INPCK
+ {"inpck", INPCK, MD_INP},
+#endif /* INPCK */
+#ifdef ISTRIP
+ {"istrip", ISTRIP, MD_INP},
+#endif /* ISTRIP */
+#ifdef INLCR
+ {"inlcr", INLCR, MD_INP},
+#endif /* INLCR */
+#ifdef IGNCR
+ {"igncr", IGNCR, MD_INP},
+#endif /* IGNCR */
+#ifdef ICRNL
+ {"icrnl", ICRNL, MD_INP},
+#endif /* ICRNL */
+#ifdef IUCLC
+ {"iuclc", IUCLC, MD_INP},
+#endif /* IUCLC */
+#ifdef IXON
+ {"ixon", IXON, MD_INP},
+#endif /* IXON */
+#ifdef IXANY
+ {"ixany", IXANY, MD_INP},
+#endif /* IXANY */
+#ifdef IXOFF
+ {"ixoff", IXOFF, MD_INP},
+#endif /* IXOFF */
+#ifdef IMAXBEL
+ {"imaxbel", IMAXBEL, MD_INP},
+#endif /* IMAXBEL */
+
+#ifdef OPOST
+ {"opost", OPOST, MD_OUT},
+#endif /* OPOST */
+#ifdef OLCUC
+ {"olcuc", OLCUC, MD_OUT},
+#endif /* OLCUC */
+#ifdef ONLCR
+ {"onlcr", ONLCR, MD_OUT},
+#endif /* ONLCR */
+#ifdef OCRNL
+ {"ocrnl", OCRNL, MD_OUT},
+#endif /* OCRNL */
+#ifdef ONOCR
+ {"onocr", ONOCR, MD_OUT},
+#endif /* ONOCR */
+#ifdef ONOEOT
+ {"onoeot", ONOEOT, MD_OUT},
+#endif /* ONOEOT */
+#ifdef ONLRET
+ {"onlret", ONLRET, MD_OUT},
+#endif /* ONLRET */
+#ifdef OFILL
+ {"ofill", OFILL, MD_OUT},
+#endif /* OFILL */
+#ifdef OFDEL
+ {"ofdel", OFDEL, MD_OUT},
+#endif /* OFDEL */
+#ifdef NLDLY
+ {"nldly", NLDLY, MD_OUT},
+#endif /* NLDLY */
+#ifdef CRDLY
+ {"crdly", CRDLY, MD_OUT},
+#endif /* CRDLY */
+#ifdef TABDLY
+ {"tabdly", TABDLY, MD_OUT},
+#endif /* TABDLY */
+#ifdef XTABS
+ {"xtabs", XTABS, MD_OUT},
+#endif /* XTABS */
+#ifdef BSDLY
+ {"bsdly", BSDLY, MD_OUT},
+#endif /* BSDLY */
+#ifdef VTDLY
+ {"vtdly", VTDLY, MD_OUT},
+#endif /* VTDLY */
+#ifdef FFDLY
+ {"ffdly", FFDLY, MD_OUT},
+#endif /* FFDLY */
+#ifdef PAGEOUT
+ {"pageout", PAGEOUT, MD_OUT},
+#endif /* PAGEOUT */
+#ifdef WRAP
+ {"wrap", WRAP, MD_OUT},
+#endif /* WRAP */
+
+#ifdef CIGNORE
+ {"cignore", CIGNORE, MD_CTL},
+#endif /* CBAUD */
+#ifdef CBAUD
+ {"cbaud", CBAUD, MD_CTL},
+#endif /* CBAUD */
+#ifdef CSTOPB
+ {"cstopb", CSTOPB, MD_CTL},
+#endif /* CSTOPB */
+#ifdef CREAD
+ {"cread", CREAD, MD_CTL},
+#endif /* CREAD */
+#ifdef PARENB
+ {"parenb", PARENB, MD_CTL},
+#endif /* PARENB */
+#ifdef PARODD
+ {"parodd", PARODD, MD_CTL},
+#endif /* PARODD */
+#ifdef HUPCL
+ {"hupcl", HUPCL, MD_CTL},
+#endif /* HUPCL */
+#ifdef CLOCAL
+ {"clocal", CLOCAL, MD_CTL},
+#endif /* CLOCAL */
+#ifdef LOBLK
+ {"loblk", LOBLK, MD_CTL},
+#endif /* LOBLK */
+#ifdef CIBAUD
+ {"cibaud", CIBAUD, MD_CTL},
+#endif /* CIBAUD */
+#ifdef CRTSCTS
+#ifdef CCTS_OFLOW
+ {"ccts_oflow", CCTS_OFLOW, MD_CTL},
+#else
+ {"crtscts", CRTSCTS, MD_CTL},
+#endif /* CCTS_OFLOW */
+#endif /* CRTSCTS */
+#ifdef CRTS_IFLOW
+ {"crts_iflow", CRTS_IFLOW, MD_CTL},
+#endif /* CRTS_IFLOW */
+#ifdef CDTRCTS
+ {"cdtrcts", CDTRCTS, MD_CTL},
+#endif /* CDTRCTS */
+#ifdef MDMBUF
+ {"mdmbuf", MDMBUF, MD_CTL},
+#endif /* MDMBUF */
+#ifdef RCV1EN
+ {"rcv1en", RCV1EN, MD_CTL},
+#endif /* RCV1EN */
+#ifdef XMT1EN
+ {"xmt1en", XMT1EN, MD_CTL},
+#endif /* XMT1EN */
+
+#ifdef ISIG
+ {"isig", ISIG, MD_LIN},
+#endif /* ISIG */
+#ifdef ICANON
+ {"icanon", ICANON, MD_LIN},
+#endif /* ICANON */
+#ifdef XCASE
+ {"xcase", XCASE, MD_LIN},
+#endif /* XCASE */
+#ifdef ECHO
+ {"echo", ECHO, MD_LIN},
+#endif /* ECHO */
+#ifdef ECHOE
+ {"echoe", ECHOE, MD_LIN},
+#endif /* ECHOE */
+#ifdef ECHOK
+ {"echok", ECHOK, MD_LIN},
+#endif /* ECHOK */
+#ifdef ECHONL
+ {"echonl", ECHONL, MD_LIN},
+#endif /* ECHONL */
+#ifdef NOFLSH
+ {"noflsh", NOFLSH, MD_LIN},
+#endif /* NOFLSH */
+#ifdef TOSTOP
+ {"tostop", TOSTOP, MD_LIN},
+#endif /* TOSTOP */
+#ifdef ECHOCTL
+ {"echoctl", ECHOCTL, MD_LIN},
+#endif /* ECHOCTL */
+#ifdef ECHOPRT
+ {"echoprt", ECHOPRT, MD_LIN},
+#endif /* ECHOPRT */
+#ifdef ECHOKE
+ {"echoke", ECHOKE, MD_LIN},
+#endif /* ECHOKE */
+#ifdef DEFECHO
+ {"defecho", DEFECHO, MD_LIN},
+#endif /* DEFECHO */
+#ifdef FLUSHO
+ {"flusho", FLUSHO, MD_LIN},
+#endif /* FLUSHO */
+#ifdef PENDIN
+ {"pendin", PENDIN, MD_LIN},
+#endif /* PENDIN */
+#ifdef IEXTEN
+ {"iexten", IEXTEN, MD_LIN},
+#endif /* IEXTEN */
+#ifdef NOKERNINFO
+ {"nokerninfo", NOKERNINFO, MD_LIN},
+#endif /* NOKERNINFO */
+#ifdef ALTWERASE
+ {"altwerase", ALTWERASE, MD_LIN},
+#endif /* ALTWERASE */
+#ifdef EXTPROC
+ {"extproc", EXTPROC, MD_LIN},
+#endif /* EXTPROC */
+
+#if defined(VINTR)
+ {"intr", C_SH(C_INTR), MD_CHAR},
+#endif /* VINTR */
+#if defined(VQUIT)
+ {"quit", C_SH(C_QUIT), MD_CHAR},
+#endif /* VQUIT */
+#if defined(VERASE)
+ {"erase", C_SH(C_ERASE), MD_CHAR},
+#endif /* VERASE */
+#if defined(VKILL)
+ {"kill", C_SH(C_KILL), MD_CHAR},
+#endif /* VKILL */
+#if defined(VEOF)
+ {"eof", C_SH(C_EOF), MD_CHAR},
+#endif /* VEOF */
+#if defined(VEOL)
+ {"eol", C_SH(C_EOL), MD_CHAR},
+#endif /* VEOL */
+#if defined(VEOL2)
+ {"eol2", C_SH(C_EOL2), MD_CHAR},
+#endif /* VEOL2 */
+#if defined(VSWTCH)
+ {"swtch", C_SH(C_SWTCH), MD_CHAR},
+#endif /* VSWTCH */
+#if defined(VDSWTCH)
+ {"dswtch", C_SH(C_DSWTCH), MD_CHAR},
+#endif /* VDSWTCH */
+#if defined(VERASE2)
+ {"erase2", C_SH(C_ERASE2), MD_CHAR},
+#endif /* VERASE2 */
+#if defined(VSTART)
+ {"start", C_SH(C_START), MD_CHAR},
+#endif /* VSTART */
+#if defined(VSTOP)
+ {"stop", C_SH(C_STOP), MD_CHAR},
+#endif /* VSTOP */
+#if defined(VWERASE)
+ {"werase", C_SH(C_WERASE), MD_CHAR},
+#endif /* VWERASE */
+#if defined(VSUSP)
+ {"susp", C_SH(C_SUSP), MD_CHAR},
+#endif /* VSUSP */
+#if defined(VDSUSP)
+ {"dsusp", C_SH(C_DSUSP), MD_CHAR},
+#endif /* VDSUSP */
+#if defined(VREPRINT)
+ {"reprint", C_SH(C_REPRINT), MD_CHAR},
+#endif /* VREPRINT */
+#if defined(VDISCARD)
+ {"discard", C_SH(C_DISCARD), MD_CHAR},
+#endif /* VDISCARD */
+#if defined(VLNEXT)
+ {"lnext", C_SH(C_LNEXT), MD_CHAR},
+#endif /* VLNEXT */
+#if defined(VSTATUS)
+ {"status", C_SH(C_STATUS), MD_CHAR},
+#endif /* VSTATUS */
+#if defined(VPAGE)
+ {"page", C_SH(C_PAGE), MD_CHAR},
+#endif /* VPAGE */
+#if defined(VPGOFF)
+ {"pgoff", C_SH(C_PGOFF), MD_CHAR},
+#endif /* VPGOFF */
+#if defined(VKILL2)
+ {"kill2", C_SH(C_KILL2), MD_CHAR},
+#endif /* VKILL2 */
+#if defined(VBRK)
+ {"brk", C_SH(C_BRK), MD_CHAR},
+#endif /* VBRK */
+#if defined(VMIN)
+ {"min", C_SH(C_MIN), MD_CHAR},
+#endif /* VMIN */
+#if defined(VTIME)
+ {"time", C_SH(C_TIME), MD_CHAR},
+#endif /* VTIME */
+ {NULL, 0, -1},
+};
+
+
+
+#define tty_getty(el, td) tcgetattr((el)->el_infd, (td))
+#define tty_setty(el, td) tcsetattr((el)->el_infd, TCSADRAIN, (td))
+
+#define tty__gettabs(td) ((((td)->c_oflag & TAB3) == TAB3) ? 0 : 1)
+#define tty__geteightbit(td) (((td)->c_cflag & CSIZE) == CS8)
+#define tty__cooked_mode(td) ((td)->c_lflag & ICANON)
+
+private void tty__getchar(struct termios *, unsigned char *);
+private void tty__setchar(struct termios *, unsigned char *);
+private speed_t tty__getspeed(struct termios *);
+private int tty_setup(EditLine *);
+
+#define t_qu t_ts
+
+
+/* tty_setup():
+ * Get the tty parameters and initialize the editing state
+ */
+private int
+tty_setup(EditLine *el)
+{
+ int rst = 1;
+
+ if (el->el_flags & EDIT_DISABLED)
+ return (0);
+
+ if (tty_getty(el, &el->el_tty.t_ed) == -1) {
+#ifdef DEBUG_TTY
+ (void) fprintf(el->el_errfile,
+ "tty_setup: tty_getty: %s\n", strerror(errno));
+#endif /* DEBUG_TTY */
+ return (-1);
+ }
+ el->el_tty.t_ts = el->el_tty.t_ex = el->el_tty.t_ed;
+
+ el->el_tty.t_speed = tty__getspeed(&el->el_tty.t_ex);
+ el->el_tty.t_tabs = tty__gettabs(&el->el_tty.t_ex);
+ el->el_tty.t_eight = tty__geteightbit(&el->el_tty.t_ex);
+
+ el->el_tty.t_ex.c_iflag &= ~el->el_tty.t_t[EX_IO][MD_INP].t_clrmask;
+ el->el_tty.t_ex.c_iflag |= el->el_tty.t_t[EX_IO][MD_INP].t_setmask;
+
+ el->el_tty.t_ex.c_oflag &= ~el->el_tty.t_t[EX_IO][MD_OUT].t_clrmask;
+ el->el_tty.t_ex.c_oflag |= el->el_tty.t_t[EX_IO][MD_OUT].t_setmask;
+
+ el->el_tty.t_ex.c_cflag &= ~el->el_tty.t_t[EX_IO][MD_CTL].t_clrmask;
+ el->el_tty.t_ex.c_cflag |= el->el_tty.t_t[EX_IO][MD_CTL].t_setmask;
+
+ el->el_tty.t_ex.c_lflag &= ~el->el_tty.t_t[EX_IO][MD_LIN].t_clrmask;
+ el->el_tty.t_ex.c_lflag |= el->el_tty.t_t[EX_IO][MD_LIN].t_setmask;
+
+ /*
+ * Reset the tty chars to reasonable defaults
+ * If they are disabled, then enable them.
+ */
+ if (rst) {
+ if (tty__cooked_mode(&el->el_tty.t_ts)) {
+ tty__getchar(&el->el_tty.t_ts, el->el_tty.t_c[TS_IO]);
+ /*
+ * Don't affect CMIN and CTIME for the editor mode
+ */
+ for (rst = 0; rst < C_NCC - 2; rst++)
+ if (el->el_tty.t_c[TS_IO][rst] !=
+ el->el_tty.t_vdisable
+ && el->el_tty.t_c[ED_IO][rst] !=
+ el->el_tty.t_vdisable)
+ el->el_tty.t_c[ED_IO][rst] =
+ el->el_tty.t_c[TS_IO][rst];
+ for (rst = 0; rst < C_NCC; rst++)
+ if (el->el_tty.t_c[TS_IO][rst] !=
+ el->el_tty.t_vdisable)
+ el->el_tty.t_c[EX_IO][rst] =
+ el->el_tty.t_c[TS_IO][rst];
+ }
+ tty__setchar(&el->el_tty.t_ex, el->el_tty.t_c[EX_IO]);
+ if (tty_setty(el, &el->el_tty.t_ex) == -1) {
+#ifdef DEBUG_TTY
+ (void) fprintf(el->el_errfile,
+ "tty_setup: tty_setty: %s\n",
+ strerror(errno));
+#endif /* DEBUG_TTY */
+ return (-1);
+ }
+ } else
+ tty__setchar(&el->el_tty.t_ex, el->el_tty.t_c[EX_IO]);
+
+ el->el_tty.t_ed.c_iflag &= ~el->el_tty.t_t[ED_IO][MD_INP].t_clrmask;
+ el->el_tty.t_ed.c_iflag |= el->el_tty.t_t[ED_IO][MD_INP].t_setmask;
+
+ el->el_tty.t_ed.c_oflag &= ~el->el_tty.t_t[ED_IO][MD_OUT].t_clrmask;
+ el->el_tty.t_ed.c_oflag |= el->el_tty.t_t[ED_IO][MD_OUT].t_setmask;
+
+ el->el_tty.t_ed.c_cflag &= ~el->el_tty.t_t[ED_IO][MD_CTL].t_clrmask;
+ el->el_tty.t_ed.c_cflag |= el->el_tty.t_t[ED_IO][MD_CTL].t_setmask;
+
+ el->el_tty.t_ed.c_lflag &= ~el->el_tty.t_t[ED_IO][MD_LIN].t_clrmask;
+ el->el_tty.t_ed.c_lflag |= el->el_tty.t_t[ED_IO][MD_LIN].t_setmask;
+
+ tty__setchar(&el->el_tty.t_ed, el->el_tty.t_c[ED_IO]);
+ tty_bind_char(el, 1);
+ return (0);
+}
+
+protected int
+tty_init(EditLine *el)
+{
+
+ el->el_tty.t_mode = EX_IO;
+ el->el_tty.t_vdisable = _POSIX_VDISABLE;
+ (void) memcpy(el->el_tty.t_t, ttyperm, sizeof(ttyperm_t));
+ (void) memcpy(el->el_tty.t_c, ttychar, sizeof(ttychar_t));
+ return (tty_setup(el));
+}
+
+
+/* tty_end():
+ * Restore the tty to its original settings
+ */
+protected void
+/*ARGSUSED*/
+tty_end(EditLine *el)
+{
+
+ /* XXX: Maybe reset to an initial state? */
+}
+
+
+/* tty__getspeed():
+ * Get the tty speed
+ */
+private speed_t
+tty__getspeed(struct termios *td)
+{
+ speed_t spd;
+
+ if ((spd = cfgetispeed(td)) == 0)
+ spd = cfgetospeed(td);
+ return (spd);
+}
+
+
+/* tty__getchar():
+ * Get the tty characters
+ */
+private void
+tty__getchar(struct termios *td, unsigned char *s)
+{
+
+#ifdef VINTR
+ s[C_INTR] = td->c_cc[VINTR];
+#endif /* VINTR */
+#ifdef VQUIT
+ s[C_QUIT] = td->c_cc[VQUIT];
+#endif /* VQUIT */
+#ifdef VERASE
+ s[C_ERASE] = td->c_cc[VERASE];
+#endif /* VERASE */
+#ifdef VKILL
+ s[C_KILL] = td->c_cc[VKILL];
+#endif /* VKILL */
+#ifdef VEOF
+ s[C_EOF] = td->c_cc[VEOF];
+#endif /* VEOF */
+#ifdef VEOL
+ s[C_EOL] = td->c_cc[VEOL];
+#endif /* VEOL */
+#ifdef VEOL2
+ s[C_EOL2] = td->c_cc[VEOL2];
+#endif /* VEOL2 */
+#ifdef VSWTCH
+ s[C_SWTCH] = td->c_cc[VSWTCH];
+#endif /* VSWTCH */
+#ifdef VDSWTCH
+ s[C_DSWTCH] = td->c_cc[VDSWTCH];
+#endif /* VDSWTCH */
+#ifdef VERASE2
+ s[C_ERASE2] = td->c_cc[VERASE2];
+#endif /* VERASE2 */
+#ifdef VSTART
+ s[C_START] = td->c_cc[VSTART];
+#endif /* VSTART */
+#ifdef VSTOP
+ s[C_STOP] = td->c_cc[VSTOP];
+#endif /* VSTOP */
+#ifdef VWERASE
+ s[C_WERASE] = td->c_cc[VWERASE];
+#endif /* VWERASE */
+#ifdef VSUSP
+ s[C_SUSP] = td->c_cc[VSUSP];
+#endif /* VSUSP */
+#ifdef VDSUSP
+ s[C_DSUSP] = td->c_cc[VDSUSP];
+#endif /* VDSUSP */
+#ifdef VREPRINT
+ s[C_REPRINT] = td->c_cc[VREPRINT];
+#endif /* VREPRINT */
+#ifdef VDISCARD
+ s[C_DISCARD] = td->c_cc[VDISCARD];
+#endif /* VDISCARD */
+#ifdef VLNEXT
+ s[C_LNEXT] = td->c_cc[VLNEXT];
+#endif /* VLNEXT */
+#ifdef VSTATUS
+ s[C_STATUS] = td->c_cc[VSTATUS];
+#endif /* VSTATUS */
+#ifdef VPAGE
+ s[C_PAGE] = td->c_cc[VPAGE];
+#endif /* VPAGE */
+#ifdef VPGOFF
+ s[C_PGOFF] = td->c_cc[VPGOFF];
+#endif /* VPGOFF */
+#ifdef VKILL2
+ s[C_KILL2] = td->c_cc[VKILL2];
+#endif /* KILL2 */
+#ifdef VMIN
+ s[C_MIN] = td->c_cc[VMIN];
+#endif /* VMIN */
+#ifdef VTIME
+ s[C_TIME] = td->c_cc[VTIME];
+#endif /* VTIME */
+} /* tty__getchar */
+
+
+/* tty__setchar():
+ * Set the tty characters
+ */
+private void
+tty__setchar(struct termios *td, unsigned char *s)
+{
+
+#ifdef VINTR
+ td->c_cc[VINTR] = s[C_INTR];
+#endif /* VINTR */
+#ifdef VQUIT
+ td->c_cc[VQUIT] = s[C_QUIT];
+#endif /* VQUIT */
+#ifdef VERASE
+ td->c_cc[VERASE] = s[C_ERASE];
+#endif /* VERASE */
+#ifdef VKILL
+ td->c_cc[VKILL] = s[C_KILL];
+#endif /* VKILL */
+#ifdef VEOF
+ td->c_cc[VEOF] = s[C_EOF];
+#endif /* VEOF */
+#ifdef VEOL
+ td->c_cc[VEOL] = s[C_EOL];
+#endif /* VEOL */
+#ifdef VEOL2
+ td->c_cc[VEOL2] = s[C_EOL2];
+#endif /* VEOL2 */
+#ifdef VSWTCH
+ td->c_cc[VSWTCH] = s[C_SWTCH];
+#endif /* VSWTCH */
+#ifdef VDSWTCH
+ td->c_cc[VDSWTCH] = s[C_DSWTCH];
+#endif /* VDSWTCH */
+#ifdef VERASE2
+ td->c_cc[VERASE2] = s[C_ERASE2];
+#endif /* VERASE2 */
+#ifdef VSTART
+ td->c_cc[VSTART] = s[C_START];
+#endif /* VSTART */
+#ifdef VSTOP
+ td->c_cc[VSTOP] = s[C_STOP];
+#endif /* VSTOP */
+#ifdef VWERASE
+ td->c_cc[VWERASE] = s[C_WERASE];
+#endif /* VWERASE */
+#ifdef VSUSP
+ td->c_cc[VSUSP] = s[C_SUSP];
+#endif /* VSUSP */
+#ifdef VDSUSP
+ td->c_cc[VDSUSP] = s[C_DSUSP];
+#endif /* VDSUSP */
+#ifdef VREPRINT
+ td->c_cc[VREPRINT] = s[C_REPRINT];
+#endif /* VREPRINT */
+#ifdef VDISCARD
+ td->c_cc[VDISCARD] = s[C_DISCARD];
+#endif /* VDISCARD */
+#ifdef VLNEXT
+ td->c_cc[VLNEXT] = s[C_LNEXT];
+#endif /* VLNEXT */
+#ifdef VSTATUS
+ td->c_cc[VSTATUS] = s[C_STATUS];
+#endif /* VSTATUS */
+#ifdef VPAGE
+ td->c_cc[VPAGE] = s[C_PAGE];
+#endif /* VPAGE */
+#ifdef VPGOFF
+ td->c_cc[VPGOFF] = s[C_PGOFF];
+#endif /* VPGOFF */
+#ifdef VKILL2
+ td->c_cc[VKILL2] = s[C_KILL2];
+#endif /* VKILL2 */
+#ifdef VMIN
+ td->c_cc[VMIN] = s[C_MIN];
+#endif /* VMIN */
+#ifdef VTIME
+ td->c_cc[VTIME] = s[C_TIME];
+#endif /* VTIME */
+} /* tty__setchar */
+
+
+/* tty_bind_char():
+ * Rebind the editline functions
+ */
+protected void
+tty_bind_char(EditLine *el, int force)
+{
+
+ unsigned char *t_n = el->el_tty.t_c[ED_IO];
+ unsigned char *t_o = el->el_tty.t_ed.c_cc;
+ unsigned char new[2], old[2];
+ const ttymap_t *tp;
+ el_action_t *map, *alt;
+ const el_action_t *dmap, *dalt;
+ new[1] = old[1] = '\0';
+
+ map = el->el_map.key;
+ alt = el->el_map.alt;
+ if (el->el_map.type == MAP_VI) {
+ dmap = el->el_map.vii;
+ dalt = el->el_map.vic;
+ } else {
+ dmap = el->el_map.emacs;
+ dalt = NULL;
+ }
+
+ for (tp = tty_map; tp->nch != -1; tp++) {
+ new[0] = t_n[tp->nch];
+ old[0] = t_o[tp->och];
+ if (new[0] == old[0] && !force)
+ continue;
+ /* Put the old default binding back, and set the new binding */
+ key_clear(el, map, (char *)old);
+ map[old[0]] = dmap[old[0]];
+ key_clear(el, map, (char *)new);
+ /* MAP_VI == 1, MAP_EMACS == 0... */
+ map[new[0]] = tp->bind[el->el_map.type];
+ if (dalt) {
+ key_clear(el, alt, (char *)old);
+ alt[old[0]] = dalt[old[0]];
+ key_clear(el, alt, (char *)new);
+ alt[new[0]] = tp->bind[el->el_map.type + 1];
+ }
+ }
+}
+
+
+/* tty_rawmode():
+ * Set terminal into 1 character at a time mode.
+ */
+protected int
+tty_rawmode(EditLine *el)
+{
+
+ if (el->el_tty.t_mode == ED_IO || el->el_tty.t_mode == QU_IO)
+ return (0);
+
+ if (el->el_flags & EDIT_DISABLED)
+ return (0);
+
+ if (tty_getty(el, &el->el_tty.t_ts) == -1) {
+#ifdef DEBUG_TTY
+ (void) fprintf(el->el_errfile, "tty_rawmode: tty_getty: %s\n",
+ strerror(errno));
+#endif /* DEBUG_TTY */
+ return (-1);
+ }
+ /*
+ * We always keep up with the eight bit setting and the speed of the
+ * tty. But only we only believe changes that are made to cooked mode!
+ */
+ el->el_tty.t_eight = tty__geteightbit(&el->el_tty.t_ts);
+ el->el_tty.t_speed = tty__getspeed(&el->el_tty.t_ts);
+
+ if (tty__getspeed(&el->el_tty.t_ex) != el->el_tty.t_speed ||
+ tty__getspeed(&el->el_tty.t_ed) != el->el_tty.t_speed) {
+ (void) cfsetispeed(&el->el_tty.t_ex, el->el_tty.t_speed);
+ (void) cfsetospeed(&el->el_tty.t_ex, el->el_tty.t_speed);
+ (void) cfsetispeed(&el->el_tty.t_ed, el->el_tty.t_speed);
+ (void) cfsetospeed(&el->el_tty.t_ed, el->el_tty.t_speed);
+ }
+ if (tty__cooked_mode(&el->el_tty.t_ts)) {
+ if (el->el_tty.t_ts.c_cflag != el->el_tty.t_ex.c_cflag) {
+ el->el_tty.t_ex.c_cflag =
+ el->el_tty.t_ts.c_cflag;
+ el->el_tty.t_ex.c_cflag &=
+ ~el->el_tty.t_t[EX_IO][MD_CTL].t_clrmask;
+ el->el_tty.t_ex.c_cflag |=
+ el->el_tty.t_t[EX_IO][MD_CTL].t_setmask;
+
+ el->el_tty.t_ed.c_cflag =
+ el->el_tty.t_ts.c_cflag;
+ el->el_tty.t_ed.c_cflag &=
+ ~el->el_tty.t_t[ED_IO][MD_CTL].t_clrmask;
+ el->el_tty.t_ed.c_cflag |=
+ el->el_tty.t_t[ED_IO][MD_CTL].t_setmask;
+ }
+ if ((el->el_tty.t_ts.c_lflag != el->el_tty.t_ex.c_lflag) &&
+ (el->el_tty.t_ts.c_lflag != el->el_tty.t_ed.c_lflag)) {
+ el->el_tty.t_ex.c_lflag =
+ el->el_tty.t_ts.c_lflag;
+ el->el_tty.t_ex.c_lflag &=
+ ~el->el_tty.t_t[EX_IO][MD_LIN].t_clrmask;
+ el->el_tty.t_ex.c_lflag |=
+ el->el_tty.t_t[EX_IO][MD_LIN].t_setmask;
+
+ el->el_tty.t_ed.c_lflag =
+ el->el_tty.t_ts.c_lflag;
+ el->el_tty.t_ed.c_lflag &=
+ ~el->el_tty.t_t[ED_IO][MD_LIN].t_clrmask;
+ el->el_tty.t_ed.c_lflag |=
+ el->el_tty.t_t[ED_IO][MD_LIN].t_setmask;
+ }
+ if ((el->el_tty.t_ts.c_iflag != el->el_tty.t_ex.c_iflag) &&
+ (el->el_tty.t_ts.c_iflag != el->el_tty.t_ed.c_iflag)) {
+ el->el_tty.t_ex.c_iflag =
+ el->el_tty.t_ts.c_iflag;
+ el->el_tty.t_ex.c_iflag &=
+ ~el->el_tty.t_t[EX_IO][MD_INP].t_clrmask;
+ el->el_tty.t_ex.c_iflag |=
+ el->el_tty.t_t[EX_IO][MD_INP].t_setmask;
+
+ el->el_tty.t_ed.c_iflag =
+ el->el_tty.t_ts.c_iflag;
+ el->el_tty.t_ed.c_iflag &=
+ ~el->el_tty.t_t[ED_IO][MD_INP].t_clrmask;
+ el->el_tty.t_ed.c_iflag |=
+ el->el_tty.t_t[ED_IO][MD_INP].t_setmask;
+ }
+ if ((el->el_tty.t_ts.c_oflag != el->el_tty.t_ex.c_oflag) &&
+ (el->el_tty.t_ts.c_oflag != el->el_tty.t_ed.c_oflag)) {
+ el->el_tty.t_ex.c_oflag =
+ el->el_tty.t_ts.c_oflag;
+ el->el_tty.t_ex.c_oflag &=
+ ~el->el_tty.t_t[EX_IO][MD_OUT].t_clrmask;
+ el->el_tty.t_ex.c_oflag |=
+ el->el_tty.t_t[EX_IO][MD_OUT].t_setmask;
+
+ el->el_tty.t_ed.c_oflag =
+ el->el_tty.t_ts.c_oflag;
+ el->el_tty.t_ed.c_oflag &=
+ ~el->el_tty.t_t[ED_IO][MD_OUT].t_clrmask;
+ el->el_tty.t_ed.c_oflag |=
+ el->el_tty.t_t[ED_IO][MD_OUT].t_setmask;
+ }
+ if (tty__gettabs(&el->el_tty.t_ex) == 0)
+ el->el_tty.t_tabs = 0;
+ else
+ el->el_tty.t_tabs = EL_CAN_TAB ? 1 : 0;
+
+ {
+ int i;
+
+ tty__getchar(&el->el_tty.t_ts, el->el_tty.t_c[TS_IO]);
+ /*
+ * Check if the user made any changes.
+ * If he did, then propagate the changes to the
+ * edit and execute data structures.
+ */
+ for (i = 0; i < C_NCC; i++)
+ if (el->el_tty.t_c[TS_IO][i] !=
+ el->el_tty.t_c[EX_IO][i])
+ break;
+
+ if (i != C_NCC) {
+ /*
+ * Propagate changes only to the unprotected
+ * chars that have been modified just now.
+ */
+ for (i = 0; i < C_NCC; i++) {
+ if (!((el->el_tty.t_t[ED_IO][MD_CHAR].t_setmask & C_SH(i)))
+ && (el->el_tty.t_c[TS_IO][i] != el->el_tty.t_c[EX_IO][i]))
+ el->el_tty.t_c[ED_IO][i] = el->el_tty.t_c[TS_IO][i];
+ if (el->el_tty.t_t[ED_IO][MD_CHAR].t_clrmask & C_SH(i))
+ el->el_tty.t_c[ED_IO][i] = el->el_tty.t_vdisable;
+ }
+ tty_bind_char(el, 0);
+ tty__setchar(&el->el_tty.t_ed, el->el_tty.t_c[ED_IO]);
+
+ for (i = 0; i < C_NCC; i++) {
+ if (!((el->el_tty.t_t[EX_IO][MD_CHAR].t_setmask & C_SH(i)))
+ && (el->el_tty.t_c[TS_IO][i] != el->el_tty.t_c[EX_IO][i]))
+ el->el_tty.t_c[EX_IO][i] = el->el_tty.t_c[TS_IO][i];
+ if (el->el_tty.t_t[EX_IO][MD_CHAR].t_clrmask & C_SH(i))
+ el->el_tty.t_c[EX_IO][i] = el->el_tty.t_vdisable;
+ }
+ tty__setchar(&el->el_tty.t_ex, el->el_tty.t_c[EX_IO]);
+ }
+ }
+ }
+ if (tty_setty(el, &el->el_tty.t_ed) == -1) {
+#ifdef DEBUG_TTY
+ (void) fprintf(el->el_errfile, "tty_rawmode: tty_setty: %s\n",
+ strerror(errno));
+#endif /* DEBUG_TTY */
+ return (-1);
+ }
+ el->el_tty.t_mode = ED_IO;
+ return (0);
+}
+
+
+/* tty_cookedmode():
+ * Set the tty back to normal mode
+ */
+protected int
+tty_cookedmode(EditLine *el)
+{ /* set tty in normal setup */
+
+ if (el->el_tty.t_mode == EX_IO)
+ return (0);
+
+ if (el->el_flags & EDIT_DISABLED)
+ return (0);
+
+ if (tty_setty(el, &el->el_tty.t_ex) == -1) {
+#ifdef DEBUG_TTY
+ (void) fprintf(el->el_errfile,
+ "tty_cookedmode: tty_setty: %s\n",
+ strerror(errno));
+#endif /* DEBUG_TTY */
+ return (-1);
+ }
+ el->el_tty.t_mode = EX_IO;
+ return (0);
+}
+
+
+/* tty_quotemode():
+ * Turn on quote mode
+ */
+protected int
+tty_quotemode(EditLine *el)
+{
+ if (el->el_tty.t_mode == QU_IO)
+ return (0);
+
+ el->el_tty.t_qu = el->el_tty.t_ed;
+
+ el->el_tty.t_qu.c_iflag &= ~el->el_tty.t_t[QU_IO][MD_INP].t_clrmask;
+ el->el_tty.t_qu.c_iflag |= el->el_tty.t_t[QU_IO][MD_INP].t_setmask;
+
+ el->el_tty.t_qu.c_oflag &= ~el->el_tty.t_t[QU_IO][MD_OUT].t_clrmask;
+ el->el_tty.t_qu.c_oflag |= el->el_tty.t_t[QU_IO][MD_OUT].t_setmask;
+
+ el->el_tty.t_qu.c_cflag &= ~el->el_tty.t_t[QU_IO][MD_CTL].t_clrmask;
+ el->el_tty.t_qu.c_cflag |= el->el_tty.t_t[QU_IO][MD_CTL].t_setmask;
+
+ el->el_tty.t_qu.c_lflag &= ~el->el_tty.t_t[QU_IO][MD_LIN].t_clrmask;
+ el->el_tty.t_qu.c_lflag |= el->el_tty.t_t[QU_IO][MD_LIN].t_setmask;
+
+ if (tty_setty(el, &el->el_tty.t_qu) == -1) {
+#ifdef DEBUG_TTY
+ (void) fprintf(el->el_errfile, "QuoteModeOn: tty_setty: %s\n",
+ strerror(errno));
+#endif /* DEBUG_TTY */
+ return (-1);
+ }
+ el->el_tty.t_mode = QU_IO;
+ return (0);
+}
+
+
+/* tty_noquotemode():
+ * Turn off quote mode
+ */
+protected int
+tty_noquotemode(EditLine *el)
+{
+
+ if (el->el_tty.t_mode != QU_IO)
+ return (0);
+ if (tty_setty(el, &el->el_tty.t_ed) == -1) {
+#ifdef DEBUG_TTY
+ (void) fprintf(el->el_errfile, "QuoteModeOff: tty_setty: %s\n",
+ strerror(errno));
+#endif /* DEBUG_TTY */
+ return (-1);
+ }
+ el->el_tty.t_mode = ED_IO;
+ return (0);
+}
+
+
+/* tty_stty():
+ * Stty builtin
+ */
+protected int
+/*ARGSUSED*/
+tty_stty(EditLine *el, int argc, const char **argv)
+{
+ const ttymodes_t *m;
+ char x;
+ int aflag = 0;
+ const char *s, *d;
+ const char *name;
+ int z = EX_IO;
+
+ if (argv == NULL)
+ return (-1);
+ name = *argv++;
+
+ while (argv && *argv && argv[0][0] == '-' && argv[0][2] == '\0')
+ switch (argv[0][1]) {
+ case 'a':
+ aflag++;
+ argv++;
+ break;
+ case 'd':
+ argv++;
+ z = ED_IO;
+ break;
+ case 'x':
+ argv++;
+ z = EX_IO;
+ break;
+ case 'q':
+ argv++;
+ z = QU_IO;
+ break;
+ default:
+ (void) fprintf(el->el_errfile,
+ "%s: Unknown switch `%c'.\n",
+ name, argv[0][1]);
+ return (-1);
+ }
+
+ if (!argv || !*argv) {
+ int i = -1;
+ int len = 0, st = 0, cu;
+ for (m = ttymodes; m->m_name; m++) {
+ if (m->m_type != i) {
+ (void) fprintf(el->el_outfile, "%s%s",
+ i != -1 ? "\n" : "",
+ el->el_tty.t_t[z][m->m_type].t_name);
+ i = m->m_type;
+ st = len =
+ strlen(el->el_tty.t_t[z][m->m_type].t_name);
+ }
+ x = (el->el_tty.t_t[z][i].t_setmask & m->m_value)
+ ? '+' : '\0';
+ x = (el->el_tty.t_t[z][i].t_clrmask & m->m_value)
+ ? '-' : x;
+
+ if (x != '\0' || aflag) {
+
+ cu = strlen(m->m_name) + (x != '\0') + 1;
+
+ if (len + cu >= el->el_term.t_size.h) {
+ (void) fprintf(el->el_outfile, "\n%*s",
+ st, "");
+ len = st + cu;
+ } else
+ len += cu;
+
+ if (x != '\0')
+ (void) fprintf(el->el_outfile, "%c%s ",
+ x, m->m_name);
+ else
+ (void) fprintf(el->el_outfile, "%s ",
+ m->m_name);
+ }
+ }
+ (void) fprintf(el->el_outfile, "\n");
+ return (0);
+ }
+ while (argv && (s = *argv++)) {
+ switch (*s) {
+ case '+':
+ case '-':
+ x = *s++;
+ break;
+ default:
+ x = '\0';
+ break;
+ }
+ d = s;
+ for (m = ttymodes; m->m_name; m++)
+ if (strcmp(m->m_name, d) == 0)
+ break;
+
+ if (!m->m_name) {
+ (void) fprintf(el->el_errfile,
+ "%s: Invalid argument `%s'.\n", name, d);
+ return (-1);
+ }
+ switch (x) {
+ case '+':
+ el->el_tty.t_t[z][m->m_type].t_setmask |= m->m_value;
+ el->el_tty.t_t[z][m->m_type].t_clrmask &= ~m->m_value;
+ break;
+ case '-':
+ el->el_tty.t_t[z][m->m_type].t_setmask &= ~m->m_value;
+ el->el_tty.t_t[z][m->m_type].t_clrmask |= m->m_value;
+ break;
+ default:
+ el->el_tty.t_t[z][m->m_type].t_setmask &= ~m->m_value;
+ el->el_tty.t_t[z][m->m_type].t_clrmask &= ~m->m_value;
+ break;
+ }
+ }
+ return (0);
+}
+
+
+#ifdef notyet
+/* tty_printchar():
+ * DEbugging routine to print the tty characters
+ */
+private void
+tty_printchar(EditLine *el, unsigned char *s)
+{
+ ttyperm_t *m;
+ int i;
+
+ for (i = 0; i < C_NCC; i++) {
+ for (m = el->el_tty.t_t; m->m_name; m++)
+ if (m->m_type == MD_CHAR && C_SH(i) == m->m_value)
+ break;
+ if (m->m_name)
+ (void) fprintf(el->el_errfile, "%s ^%c ",
+ m->m_name, s[i] + 'A' - 1);
+ if (i % 5 == 0)
+ (void) fprintf(el->el_errfile, "\n");
+ }
+ (void) fprintf(el->el_errfile, "\n");
+}
+#endif /* notyet */
diff --git a/trunk/main/editline/tty.h b/trunk/main/editline/tty.h
new file mode 100644
index 000000000..e9597fceb
--- /dev/null
+++ b/trunk/main/editline/tty.h
@@ -0,0 +1,484 @@
+/* $NetBSD: tty.h,v 1.9 2002/03/18 16:01:01 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tty.h 8.1 (Berkeley) 6/4/93
+ */
+
+/*
+ * el.tty.h: Local terminal header
+ */
+#ifndef _h_el_tty
+#define _h_el_tty
+
+#include "histedit.h"
+#include <termios.h>
+#include <unistd.h>
+
+/* Define our own since everyone gets it wrong! */
+#define CONTROL(A) ((A) & 037)
+
+/*
+ * Aix compatible names
+ */
+# if defined(VWERSE) && !defined(VWERASE)
+# define VWERASE VWERSE
+# endif /* VWERSE && !VWERASE */
+
+# if defined(VDISCRD) && !defined(VDISCARD)
+# define VDISCARD VDISCRD
+# endif /* VDISCRD && !VDISCARD */
+
+# if defined(VFLUSHO) && !defined(VDISCARD)
+# define VDISCARD VFLUSHO
+# endif /* VFLUSHO && VDISCARD */
+
+# if defined(VSTRT) && !defined(VSTART)
+# define VSTART VSTRT
+# endif /* VSTRT && ! VSTART */
+
+# if defined(VSTAT) && !defined(VSTATUS)
+# define VSTATUS VSTAT
+# endif /* VSTAT && ! VSTATUS */
+
+# ifndef ONLRET
+# define ONLRET 0
+# endif /* ONLRET */
+
+# ifndef TAB3
+# ifdef OXTABS
+# define TAB3 OXTABS
+# else
+# define TAB3 0
+# endif /* OXTABS */
+# endif /* !TAB3 */
+
+# if defined(OXTABS) && !defined(XTABS)
+# define XTABS OXTABS
+# endif /* OXTABS && !XTABS */
+
+# ifndef ONLCR
+# define ONLCR 0
+# endif /* ONLCR */
+
+# ifndef IEXTEN
+# define IEXTEN 0
+# endif /* IEXTEN */
+
+# ifndef ECHOCTL
+# define ECHOCTL 0
+# endif /* ECHOCTL */
+
+# ifndef PARENB
+# define PARENB 0
+# endif /* PARENB */
+
+# ifndef EXTPROC
+# define EXTPROC 0
+# endif /* EXTPROC */
+
+# ifndef FLUSHO
+# define FLUSHO 0
+# endif /* FLUSHO */
+
+
+# if defined(VDISABLE) && !defined(_POSIX_VDISABLE)
+# define _POSIX_VDISABLE VDISABLE
+# endif /* VDISABLE && ! _POSIX_VDISABLE */
+
+/*
+ * Work around ISC's definition of IEXTEN which is
+ * XCASE!
+ */
+# ifdef ISC
+# if defined(IEXTEN) && defined(XCASE)
+# if IEXTEN == XCASE
+# undef IEXTEN
+# define IEXTEN 0
+# endif /* IEXTEN == XCASE */
+# endif /* IEXTEN && XCASE */
+# if defined(IEXTEN) && !defined(XCASE)
+# define XCASE IEXTEN
+# undef IEXTEN
+# define IEXTEN 0
+# endif /* IEXTEN && !XCASE */
+# endif /* ISC */
+
+/*
+ * Work around convex weirdness where turning off IEXTEN makes us
+ * lose all postprocessing!
+ */
+#if defined(convex) || defined(__convex__)
+# if defined(IEXTEN) && IEXTEN != 0
+# undef IEXTEN
+# define IEXTEN 0
+# endif /* IEXTEN != 0 */
+#endif /* convex || __convex__ */
+
+/*
+ * So that we don't lose job control.
+ */
+#ifdef __SVR4
+# undef CSWTCH
+#endif
+
+#ifndef _POSIX_VDISABLE
+# define _POSIX_VDISABLE ((unsigned char) -1)
+#endif /* _POSIX_VDISABLE */
+
+#if !defined(CREPRINT) && defined(CRPRNT)
+# define CREPRINT CRPRNT
+#endif /* !CREPRINT && CRPRNT */
+#if !defined(CDISCARD) && defined(CFLUSH)
+# define CDISCARD CFLUSH
+#endif /* !CDISCARD && CFLUSH */
+
+#ifndef CINTR
+# define CINTR CONTROL('c')
+#endif /* CINTR */
+#ifndef CQUIT
+# define CQUIT 034 /* ^\ */
+#endif /* CQUIT */
+#ifndef CERASE
+# define CERASE 0177 /* ^? */
+#endif /* CERASE */
+#ifndef CKILL
+# define CKILL CONTROL('u')
+#endif /* CKILL */
+#ifndef CEOF
+# define CEOF CONTROL('d')
+#endif /* CEOF */
+#ifndef CEOL
+# define CEOL _POSIX_VDISABLE
+#endif /* CEOL */
+#ifndef CEOL2
+# define CEOL2 _POSIX_VDISABLE
+#endif /* CEOL2 */
+#ifndef CSWTCH
+# define CSWTCH _POSIX_VDISABLE
+#endif /* CSWTCH */
+#ifndef CDSWTCH
+# define CDSWTCH _POSIX_VDISABLE
+#endif /* CDSWTCH */
+#ifndef CERASE2
+# define CERASE2 _POSIX_VDISABLE
+#endif /* CERASE2 */
+#ifndef CSTART
+# define CSTART CONTROL('q')
+#endif /* CSTART */
+#ifndef CSTOP
+# define CSTOP CONTROL('s')
+#endif /* CSTOP */
+#ifndef CSUSP
+# define CSUSP CONTROL('z')
+#endif /* CSUSP */
+#ifndef CDSUSP
+# define CDSUSP CONTROL('y')
+#endif /* CDSUSP */
+
+#ifdef hpux
+
+# ifndef CREPRINT
+# define CREPRINT _POSIX_VDISABLE
+# endif /* CREPRINT */
+# ifndef CDISCARD
+# define CDISCARD _POSIX_VDISABLE
+# endif /* CDISCARD */
+# ifndef CLNEXT
+# define CLNEXT _POSIX_VDISABLE
+# endif /* CLNEXT */
+# ifndef CWERASE
+# define CWERASE _POSIX_VDISABLE
+# endif /* CWERASE */
+
+#else /* !hpux */
+
+# ifndef CREPRINT
+# define CREPRINT CONTROL('r')
+# endif /* CREPRINT */
+# ifndef CDISCARD
+# define CDISCARD CONTROL('o')
+# endif /* CDISCARD */
+# ifndef CLNEXT
+# define CLNEXT CONTROL('v')
+# endif /* CLNEXT */
+# ifndef CWERASE
+# define CWERASE CONTROL('w')
+# endif /* CWERASE */
+
+#endif /* hpux */
+
+#ifndef CSTATUS
+# define CSTATUS CONTROL('t')
+#endif /* CSTATUS */
+#ifndef CPAGE
+# define CPAGE ' '
+#endif /* CPAGE */
+#ifndef CPGOFF
+# define CPGOFF CONTROL('m')
+#endif /* CPGOFF */
+#ifndef CKILL2
+# define CKILL2 _POSIX_VDISABLE
+#endif /* CKILL2 */
+#ifndef CBRK
+# ifndef masscomp
+# define CBRK 0377
+# else
+# define CBRK '\0'
+# endif /* masscomp */
+#endif /* CBRK */
+#ifndef CMIN
+# define CMIN CEOF
+#endif /* CMIN */
+#ifndef CTIME
+# define CTIME CEOL
+#endif /* CTIME */
+
+/*
+ * Fix for sun inconsistency. On termio VSUSP and the rest of the
+ * ttychars > NCC are defined. So we undefine them.
+ */
+#if defined(TERMIO) || defined(POSIX)
+# if defined(POSIX) && defined(NCCS)
+# define NUMCC NCCS
+# else
+# ifdef NCC
+# define NUMCC NCC
+# endif /* NCC */
+# endif /* POSIX && NCCS */
+# ifdef NUMCC
+# ifdef VINTR
+# if NUMCC <= VINTR
+# undef VINTR
+# endif /* NUMCC <= VINTR */
+# endif /* VINTR */
+# ifdef VQUIT
+# if NUMCC <= VQUIT
+# undef VQUIT
+# endif /* NUMCC <= VQUIT */
+# endif /* VQUIT */
+# ifdef VERASE
+# if NUMCC <= VERASE
+# undef VERASE
+# endif /* NUMCC <= VERASE */
+# endif /* VERASE */
+# ifdef VKILL
+# if NUMCC <= VKILL
+# undef VKILL
+# endif /* NUMCC <= VKILL */
+# endif /* VKILL */
+# ifdef VEOF
+# if NUMCC <= VEOF
+# undef VEOF
+# endif /* NUMCC <= VEOF */
+# endif /* VEOF */
+# ifdef VEOL
+# if NUMCC <= VEOL
+# undef VEOL
+# endif /* NUMCC <= VEOL */
+# endif /* VEOL */
+# ifdef VEOL2
+# if NUMCC <= VEOL2
+# undef VEOL2
+# endif /* NUMCC <= VEOL2 */
+# endif /* VEOL2 */
+# ifdef VSWTCH
+# if NUMCC <= VSWTCH
+# undef VSWTCH
+# endif /* NUMCC <= VSWTCH */
+# endif /* VSWTCH */
+# ifdef VDSWTCH
+# if NUMCC <= VDSWTCH
+# undef VDSWTCH
+# endif /* NUMCC <= VDSWTCH */
+# endif /* VDSWTCH */
+# ifdef VERASE2
+# if NUMCC <= VERASE2
+# undef VERASE2
+# endif /* NUMCC <= VERASE2 */
+# endif /* VERASE2 */
+# ifdef VSTART
+# if NUMCC <= VSTART
+# undef VSTART
+# endif /* NUMCC <= VSTART */
+# endif /* VSTART */
+# ifdef VSTOP
+# if NUMCC <= VSTOP
+# undef VSTOP
+# endif /* NUMCC <= VSTOP */
+# endif /* VSTOP */
+# ifdef VWERASE
+# if NUMCC <= VWERASE
+# undef VWERASE
+# endif /* NUMCC <= VWERASE */
+# endif /* VWERASE */
+# ifdef VSUSP
+# if NUMCC <= VSUSP
+# undef VSUSP
+# endif /* NUMCC <= VSUSP */
+# endif /* VSUSP */
+# ifdef VDSUSP
+# if NUMCC <= VDSUSP
+# undef VDSUSP
+# endif /* NUMCC <= VDSUSP */
+# endif /* VDSUSP */
+# ifdef VREPRINT
+# if NUMCC <= VREPRINT
+# undef VREPRINT
+# endif /* NUMCC <= VREPRINT */
+# endif /* VREPRINT */
+# ifdef VDISCARD
+# if NUMCC <= VDISCARD
+# undef VDISCARD
+# endif /* NUMCC <= VDISCARD */
+# endif /* VDISCARD */
+# ifdef VLNEXT
+# if NUMCC <= VLNEXT
+# undef VLNEXT
+# endif /* NUMCC <= VLNEXT */
+# endif /* VLNEXT */
+# ifdef VSTATUS
+# if NUMCC <= VSTATUS
+# undef VSTATUS
+# endif /* NUMCC <= VSTATUS */
+# endif /* VSTATUS */
+# ifdef VPAGE
+# if NUMCC <= VPAGE
+# undef VPAGE
+# endif /* NUMCC <= VPAGE */
+# endif /* VPAGE */
+# ifdef VPGOFF
+# if NUMCC <= VPGOFF
+# undef VPGOFF
+# endif /* NUMCC <= VPGOFF */
+# endif /* VPGOFF */
+# ifdef VKILL2
+# if NUMCC <= VKILL2
+# undef VKILL2
+# endif /* NUMCC <= VKILL2 */
+# endif /* VKILL2 */
+# ifdef VBRK
+# if NUMCC <= VBRK
+# undef VBRK
+# endif /* NUMCC <= VBRK */
+# endif /* VBRK */
+# ifdef VMIN
+# if NUMCC <= VMIN
+# undef VMIN
+# endif /* NUMCC <= VMIN */
+# endif /* VMIN */
+# ifdef VTIME
+# if NUMCC <= VTIME
+# undef VTIME
+# endif /* NUMCC <= VTIME */
+# endif /* VTIME */
+# endif /* NUMCC */
+#endif /* !POSIX */
+
+#define C_INTR 0
+#define C_QUIT 1
+#define C_ERASE 2
+#define C_KILL 3
+#define C_EOF 4
+#define C_EOL 5
+#define C_EOL2 6
+#define C_SWTCH 7
+#define C_DSWTCH 8
+#define C_ERASE2 9
+#define C_START 10
+#define C_STOP 11
+#define C_WERASE 12
+#define C_SUSP 13
+#define C_DSUSP 14
+#define C_REPRINT 15
+#define C_DISCARD 16
+#define C_LNEXT 17
+#define C_STATUS 18
+#define C_PAGE 19
+#define C_PGOFF 20
+#define C_KILL2 21
+#define C_BRK 22
+#define C_MIN 23
+#define C_TIME 24
+#define C_NCC 25
+#define C_SH(A) (1 << (A))
+
+/*
+ * Terminal dependend data structures
+ */
+#define EX_IO 0 /* while we are executing */
+#define ED_IO 1 /* while we are editing */
+#define TS_IO 2 /* new mode from terminal */
+#define QU_IO 2 /* used only for quoted chars */
+#define NN_IO 3 /* The number of entries */
+
+#define MD_INP 0
+#define MD_OUT 1
+#define MD_CTL 2
+#define MD_LIN 3
+#define MD_CHAR 4
+#define MD_NN 5
+
+typedef struct {
+ const char *t_name;
+ u_int t_setmask;
+ u_int t_clrmask;
+} ttyperm_t[NN_IO][MD_NN];
+
+typedef unsigned char ttychar_t[NN_IO][C_NCC];
+
+protected int tty_init(EditLine *);
+protected void tty_end(EditLine *);
+protected int tty_stty(EditLine *, int, const char **);
+protected int tty_rawmode(EditLine *);
+protected int tty_cookedmode(EditLine *);
+protected int tty_quotemode(EditLine *);
+protected int tty_noquotemode(EditLine *);
+protected void tty_bind_char(EditLine *, int);
+
+typedef struct {
+ ttyperm_t t_t;
+ ttychar_t t_c;
+ struct termios t_ex, t_ed, t_ts;
+ int t_tabs;
+ int t_eight;
+ speed_t t_speed;
+ int t_mode;
+ unsigned char t_vdisable;
+} el_tty_t;
+
+
+#endif /* _h_el_tty */
diff --git a/trunk/main/editline/vi.c b/trunk/main/editline/vi.c
new file mode 100644
index 000000000..5683c7de0
--- /dev/null
+++ b/trunk/main/editline/vi.c
@@ -0,0 +1,941 @@
+/* $NetBSD: vi.c,v 1.9 2002/03/18 16:01:01 christos Exp $ */
+
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christos Zoulas of Cornell University.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+#if !defined(lint) && !defined(SCCSID)
+#if 0
+static char sccsid[] = "@(#)vi.c 8.1 (Berkeley) 6/4/93";
+#else
+__RCSID("$NetBSD: vi.c,v 1.9 2002/03/18 16:01:01 christos Exp $");
+#endif
+#endif /* not lint && not SCCSID */
+
+/*
+ * vi.c: Vi mode commands.
+ */
+#include "el.h"
+
+private el_action_t cv_action(EditLine *, int);
+private el_action_t cv_paste(EditLine *, int);
+
+/* cv_action():
+ * Handle vi actions.
+ */
+private el_action_t
+cv_action(EditLine *el, int c)
+{
+ char *cp, *kp;
+
+ if (el->el_chared.c_vcmd.action & DELETE) {
+ el->el_chared.c_vcmd.action = NOP;
+ el->el_chared.c_vcmd.pos = 0;
+
+ el->el_chared.c_undo.isize = 0;
+ el->el_chared.c_undo.dsize = 0;
+ kp = el->el_chared.c_undo.buf;
+ for (cp = el->el_line.buffer; cp < el->el_line.lastchar; cp++) {
+ *kp++ = *cp;
+ el->el_chared.c_undo.dsize++;
+ }
+
+ el->el_chared.c_undo.action = INSERT;
+ el->el_chared.c_undo.ptr = el->el_line.buffer;
+ el->el_line.lastchar = el->el_line.buffer;
+ el->el_line.cursor = el->el_line.buffer;
+ if (c & INSERT)
+ el->el_map.current = el->el_map.key;
+
+ return (CC_REFRESH);
+ }
+ el->el_chared.c_vcmd.pos = el->el_line.cursor;
+ el->el_chared.c_vcmd.action = c;
+ return (CC_ARGHACK);
+
+#ifdef notdef
+ /*
+ * I don't think that this is needed. But we keep it for now
+ */
+ else
+ if (el_chared.c_vcmd.action == NOP) {
+ el->el_chared.c_vcmd.pos = el->el_line.cursor;
+ el->el_chared.c_vcmd.action = c;
+ return (CC_ARGHACK);
+ } else {
+ el->el_chared.c_vcmd.action = 0;
+ el->el_chared.c_vcmd.pos = 0;
+ return (CC_ERROR);
+ }
+#endif
+}
+
+
+/* cv_paste():
+ * Paste previous deletion before or after the cursor
+ */
+private el_action_t
+cv_paste(EditLine *el, int c)
+{
+ char *ptr;
+ c_undo_t *un = &el->el_chared.c_undo;
+
+#ifdef DEBUG_PASTE
+ (void) fprintf(el->el_errfile, "Paste: %x \"%s\" +%d -%d\n",
+ un->action, un->buf, un->isize, un->dsize);
+#endif
+ if (un->isize == 0)
+ return (CC_ERROR);
+
+ if (!c && el->el_line.cursor < el->el_line.lastchar)
+ el->el_line.cursor++;
+ ptr = el->el_line.cursor;
+
+ c_insert(el, (int) un->isize);
+ if (el->el_line.cursor + un->isize > el->el_line.lastchar)
+ return (CC_ERROR);
+ (void) memcpy(ptr, un->buf, un->isize);
+ return (CC_REFRESH);
+}
+
+
+/* vi_paste_next():
+ * Vi paste previous deletion to the right of the cursor
+ * [p]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_paste_next(EditLine *el, int c)
+{
+
+ return (cv_paste(el, 0));
+}
+
+
+/* vi_paste_prev():
+ * Vi paste previous deletion to the left of the cursor
+ * [P]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_paste_prev(EditLine *el, int c)
+{
+
+ return (cv_paste(el, 1));
+}
+
+
+/* vi_prev_space_word():
+ * Vi move to the previous space delimited word
+ * [B]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_prev_space_word(EditLine *el, int c)
+{
+
+ if (el->el_line.cursor == el->el_line.buffer)
+ return (CC_ERROR);
+
+ el->el_line.cursor = cv_prev_word(el, el->el_line.cursor,
+ el->el_line.buffer,
+ el->el_state.argument,
+ cv__isword);
+
+ if (el->el_chared.c_vcmd.action & DELETE) {
+ cv_delfini(el);
+ return (CC_REFRESH);
+ }
+ return (CC_CURSOR);
+}
+
+
+/* vi_prev_word():
+ * Vi move to the previous word
+ * [B]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_prev_word(EditLine *el, int c)
+{
+
+ if (el->el_line.cursor == el->el_line.buffer)
+ return (CC_ERROR);
+
+ el->el_line.cursor = cv_prev_word(el, el->el_line.cursor,
+ el->el_line.buffer,
+ el->el_state.argument,
+ ce__isword);
+
+ if (el->el_chared.c_vcmd.action & DELETE) {
+ cv_delfini(el);
+ return (CC_REFRESH);
+ }
+ return (CC_CURSOR);
+}
+
+
+/* vi_next_space_word():
+ * Vi move to the next space delimited word
+ * [W]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_next_space_word(EditLine *el, int c)
+{
+
+ if (el->el_line.cursor == el->el_line.lastchar)
+ return (CC_ERROR);
+
+ el->el_line.cursor = cv_next_word(el, el->el_line.cursor,
+ el->el_line.lastchar,
+ el->el_state.argument,
+ cv__isword);
+
+ if (el->el_map.type == MAP_VI)
+ if (el->el_chared.c_vcmd.action & DELETE) {
+ cv_delfini(el);
+ return (CC_REFRESH);
+ }
+ return (CC_CURSOR);
+}
+
+
+/* vi_next_word():
+ * Vi move to the next word
+ * [w]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_next_word(EditLine *el, int c)
+{
+
+ if (el->el_line.cursor == el->el_line.lastchar)
+ return (CC_ERROR);
+
+ el->el_line.cursor = cv_next_word(el, el->el_line.cursor,
+ el->el_line.lastchar,
+ el->el_state.argument,
+ ce__isword);
+
+ if (el->el_map.type == MAP_VI)
+ if (el->el_chared.c_vcmd.action & DELETE) {
+ cv_delfini(el);
+ return (CC_REFRESH);
+ }
+ return (CC_CURSOR);
+}
+
+
+/* vi_change_case():
+ * Vi change case of character under the cursor and advance one character
+ * [~]
+ */
+protected el_action_t
+vi_change_case(EditLine *el, int c)
+{
+
+ if (el->el_line.cursor < el->el_line.lastchar) {
+ c = *el->el_line.cursor;
+ if (isupper(c))
+ *el->el_line.cursor++ = tolower(c);
+ else if (islower(c))
+ *el->el_line.cursor++ = toupper(c);
+ else
+ el->el_line.cursor++;
+ re_fastaddc(el);
+ return (CC_NORM);
+ }
+ return (CC_ERROR);
+}
+
+
+/* vi_change_meta():
+ * Vi change prefix command
+ * [c]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_change_meta(EditLine *el, int c)
+{
+
+ /*
+ * Delete with insert == change: first we delete and then we leave in
+ * insert mode.
+ */
+ return (cv_action(el, DELETE | INSERT));
+}
+
+
+/* vi_insert_at_bol():
+ * Vi enter insert mode at the beginning of line
+ * [I]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_insert_at_bol(EditLine *el, int c)
+{
+
+ el->el_line.cursor = el->el_line.buffer;
+ el->el_chared.c_vcmd.ins = el->el_line.cursor;
+
+ el->el_chared.c_undo.ptr = el->el_line.cursor;
+ el->el_chared.c_undo.action = DELETE;
+
+ el->el_map.current = el->el_map.key;
+ return (CC_CURSOR);
+}
+
+
+/* vi_replace_char():
+ * Vi replace character under the cursor with the next character typed
+ * [r]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_replace_char(EditLine *el, int c)
+{
+
+ el->el_map.current = el->el_map.key;
+ el->el_state.inputmode = MODE_REPLACE_1;
+ el->el_chared.c_undo.action = CHANGE;
+ el->el_chared.c_undo.ptr = el->el_line.cursor;
+ el->el_chared.c_undo.isize = 0;
+ el->el_chared.c_undo.dsize = 0;
+ return (CC_NORM);
+}
+
+
+/* vi_replace_mode():
+ * Vi enter replace mode
+ * [R]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_replace_mode(EditLine *el, int c)
+{
+
+ el->el_map.current = el->el_map.key;
+ el->el_state.inputmode = MODE_REPLACE;
+ el->el_chared.c_undo.action = CHANGE;
+ el->el_chared.c_undo.ptr = el->el_line.cursor;
+ el->el_chared.c_undo.isize = 0;
+ el->el_chared.c_undo.dsize = 0;
+ return (CC_NORM);
+}
+
+
+/* vi_substitute_char():
+ * Vi replace character under the cursor and enter insert mode
+ * [r]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_substitute_char(EditLine *el, int c)
+{
+
+ c_delafter(el, el->el_state.argument);
+ el->el_map.current = el->el_map.key;
+ return (CC_REFRESH);
+}
+
+
+/* vi_substitute_line():
+ * Vi substitute entire line
+ * [S]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_substitute_line(EditLine *el, int c)
+{
+
+ (void) em_kill_line(el, 0);
+ el->el_map.current = el->el_map.key;
+ return (CC_REFRESH);
+}
+
+
+/* vi_change_to_eol():
+ * Vi change to end of line
+ * [C]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_change_to_eol(EditLine *el, int c)
+{
+
+ (void) ed_kill_line(el, 0);
+ el->el_map.current = el->el_map.key;
+ return (CC_REFRESH);
+}
+
+
+/* vi_insert():
+ * Vi enter insert mode
+ * [i]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_insert(EditLine *el, int c)
+{
+
+ el->el_map.current = el->el_map.key;
+
+ el->el_chared.c_vcmd.ins = el->el_line.cursor;
+ el->el_chared.c_undo.ptr = el->el_line.cursor;
+ el->el_chared.c_undo.action = DELETE;
+
+ return (CC_NORM);
+}
+
+
+/* vi_add():
+ * Vi enter insert mode after the cursor
+ * [a]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_add(EditLine *el, int c)
+{
+ int ret;
+
+ el->el_map.current = el->el_map.key;
+ if (el->el_line.cursor < el->el_line.lastchar) {
+ el->el_line.cursor++;
+ if (el->el_line.cursor > el->el_line.lastchar)
+ el->el_line.cursor = el->el_line.lastchar;
+ ret = CC_CURSOR;
+ } else
+ ret = CC_NORM;
+
+ el->el_chared.c_vcmd.ins = el->el_line.cursor;
+ el->el_chared.c_undo.ptr = el->el_line.cursor;
+ el->el_chared.c_undo.action = DELETE;
+
+ return (ret);
+}
+
+
+/* vi_add_at_eol():
+ * Vi enter insert mode at end of line
+ * [A]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_add_at_eol(EditLine *el, int c)
+{
+
+ el->el_map.current = el->el_map.key;
+ el->el_line.cursor = el->el_line.lastchar;
+
+ /* Mark where insertion begins */
+ el->el_chared.c_vcmd.ins = el->el_line.lastchar;
+ el->el_chared.c_undo.ptr = el->el_line.lastchar;
+ el->el_chared.c_undo.action = DELETE;
+ return (CC_CURSOR);
+}
+
+
+/* vi_delete_meta():
+ * Vi delete prefix command
+ * [d]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_delete_meta(EditLine *el, int c)
+{
+
+ return (cv_action(el, DELETE));
+}
+
+
+/* vi_end_word():
+ * Vi move to the end of the current space delimited word
+ * [E]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_end_word(EditLine *el, int c)
+{
+
+ if (el->el_line.cursor == el->el_line.lastchar)
+ return (CC_ERROR);
+
+ el->el_line.cursor = cv__endword(el->el_line.cursor,
+ el->el_line.lastchar, el->el_state.argument);
+
+ if (el->el_chared.c_vcmd.action & DELETE) {
+ el->el_line.cursor++;
+ cv_delfini(el);
+ return (CC_REFRESH);
+ }
+ return (CC_CURSOR);
+}
+
+
+/* vi_to_end_word():
+ * Vi move to the end of the current word
+ * [e]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_to_end_word(EditLine *el, int c)
+{
+
+ if (el->el_line.cursor == el->el_line.lastchar)
+ return (CC_ERROR);
+
+ el->el_line.cursor = cv__endword(el->el_line.cursor,
+ el->el_line.lastchar, el->el_state.argument);
+
+ if (el->el_chared.c_vcmd.action & DELETE) {
+ el->el_line.cursor++;
+ cv_delfini(el);
+ return (CC_REFRESH);
+ }
+ return (CC_CURSOR);
+}
+
+
+/* vi_undo():
+ * Vi undo last change
+ * [u]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_undo(EditLine *el, int c)
+{
+ char *cp, *kp;
+ char temp;
+ int i, size;
+ c_undo_t *un = &el->el_chared.c_undo;
+
+#ifdef DEBUG_UNDO
+ (void) fprintf(el->el_errfile, "Undo: %x \"%s\" +%d -%d\n",
+ un->action, un->buf, un->isize, un->dsize);
+#endif
+ switch (un->action) {
+ case DELETE:
+ if (un->dsize == 0)
+ return (CC_NORM);
+
+ (void) memcpy(un->buf, un->ptr, un->dsize);
+ for (cp = un->ptr; cp <= el->el_line.lastchar; cp++)
+ *cp = cp[un->dsize];
+
+ el->el_line.lastchar -= un->dsize;
+ el->el_line.cursor = un->ptr;
+
+ un->action = INSERT;
+ un->isize = un->dsize;
+ un->dsize = 0;
+ break;
+
+ case DELETE | INSERT:
+ size = un->isize - un->dsize;
+ if (size > 0)
+ i = un->dsize;
+ else
+ i = un->isize;
+ cp = un->ptr;
+ kp = un->buf;
+ while (i-- > 0) {
+ temp = *kp;
+ *kp++ = *cp;
+ *cp++ = temp;
+ }
+ if (size > 0) {
+ el->el_line.cursor = cp;
+ c_insert(el, size);
+ while (size-- > 0 && cp < el->el_line.lastchar) {
+ temp = *kp;
+ *kp++ = *cp;
+ *cp++ = temp;
+ }
+ } else if (size < 0) {
+ size = -size;
+ for (; cp <= el->el_line.lastchar; cp++) {
+ *kp++ = *cp;
+ *cp = cp[size];
+ }
+ el->el_line.lastchar -= size;
+ }
+ el->el_line.cursor = un->ptr;
+ i = un->dsize;
+ un->dsize = un->isize;
+ un->isize = i;
+ break;
+
+ case INSERT:
+ if (un->isize == 0)
+ return (CC_NORM);
+
+ el->el_line.cursor = un->ptr;
+ c_insert(el, (int) un->isize);
+ (void) memcpy(un->ptr, un->buf, un->isize);
+ un->action = DELETE;
+ un->dsize = un->isize;
+ un->isize = 0;
+ break;
+
+ case CHANGE:
+ if (un->isize == 0)
+ return (CC_NORM);
+
+ el->el_line.cursor = un->ptr;
+ size = (int) (el->el_line.cursor - el->el_line.lastchar);
+ if (size < un->isize)
+ size = un->isize;
+ cp = un->ptr;
+ kp = un->buf;
+ for (i = 0; i < size; i++) {
+ temp = *kp;
+ *kp++ = *cp;
+ *cp++ = temp;
+ }
+ un->dsize = 0;
+ break;
+
+ default:
+ return (CC_ERROR);
+ }
+
+ return (CC_REFRESH);
+}
+
+
+/* vi_command_mode():
+ * Vi enter command mode (use alternative key bindings)
+ * [<ESC>]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_command_mode(EditLine *el, int c)
+{
+ int size;
+
+ /* [Esc] cancels pending action */
+ el->el_chared.c_vcmd.ins = 0;
+ el->el_chared.c_vcmd.action = NOP;
+ el->el_chared.c_vcmd.pos = 0;
+
+ el->el_state.doingarg = 0;
+ size = el->el_chared.c_undo.ptr - el->el_line.cursor;
+ if (size < 0)
+ size = -size;
+ if (el->el_chared.c_undo.action == (INSERT | DELETE) ||
+ el->el_chared.c_undo.action == DELETE)
+ el->el_chared.c_undo.dsize = size;
+ else
+ el->el_chared.c_undo.isize = size;
+
+ el->el_state.inputmode = MODE_INSERT;
+ el->el_map.current = el->el_map.alt;
+#ifdef VI_MOVE
+ if (el->el_line.cursor > el->el_line.buffer)
+ el->el_line.cursor--;
+#endif
+ return (CC_CURSOR);
+}
+
+
+/* vi_zero():
+ * Vi move to the beginning of line
+ * [0]
+ */
+protected el_action_t
+vi_zero(EditLine *el, int c)
+{
+
+ if (el->el_state.doingarg) {
+ if (el->el_state.argument > 1000000)
+ return (CC_ERROR);
+ el->el_state.argument =
+ (el->el_state.argument * 10) + (c - '0');
+ return (CC_ARGHACK);
+ } else {
+ el->el_line.cursor = el->el_line.buffer;
+ if (el->el_chared.c_vcmd.action & DELETE) {
+ cv_delfini(el);
+ return (CC_REFRESH);
+ }
+ return (CC_CURSOR);
+ }
+}
+
+
+/* vi_delete_prev_char():
+ * Vi move to previous character (backspace)
+ * [^H]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_delete_prev_char(EditLine *el, int c)
+{
+
+ if (el->el_chared.c_vcmd.ins == 0)
+ return (CC_ERROR);
+
+ if (el->el_chared.c_vcmd.ins >
+ el->el_line.cursor - el->el_state.argument)
+ return (CC_ERROR);
+
+ c_delbefore(el, el->el_state.argument);
+ el->el_line.cursor -= el->el_state.argument;
+
+ return (CC_REFRESH);
+}
+
+
+/* vi_list_or_eof():
+ * Vi list choices for completion or indicate end of file if empty line
+ * [^D]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_list_or_eof(EditLine *el, int c)
+{
+
+#ifdef notyet
+ if (el->el_line.cursor == el->el_line.lastchar &&
+ el->el_line.cursor == el->el_line.buffer) {
+#endif
+ term_overwrite(el, STReof, 4); /* then do a EOF */
+ term__flush();
+ return (CC_EOF);
+#ifdef notyet
+ } else {
+ re_goto_bottom(el);
+ *el->el_line.lastchar = '\0'; /* just in case */
+ return (CC_LIST_CHOICES);
+ }
+#endif
+}
+
+
+/* vi_kill_line_prev():
+ * Vi cut from beginning of line to cursor
+ * [^U]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_kill_line_prev(EditLine *el, int c)
+{
+ char *kp, *cp;
+
+ cp = el->el_line.buffer;
+ kp = el->el_chared.c_kill.buf;
+ while (cp < el->el_line.cursor)
+ *kp++ = *cp++; /* copy it */
+ el->el_chared.c_kill.last = kp;
+ c_delbefore(el, el->el_line.cursor - el->el_line.buffer);
+ el->el_line.cursor = el->el_line.buffer; /* zap! */
+ return (CC_REFRESH);
+}
+
+
+/* vi_search_prev():
+ * Vi search history previous
+ * [?]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_search_prev(EditLine *el, int c)
+{
+
+ return (cv_search(el, ED_SEARCH_PREV_HISTORY));
+}
+
+
+/* vi_search_next():
+ * Vi search history next
+ * [/]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_search_next(EditLine *el, int c)
+{
+
+ return (cv_search(el, ED_SEARCH_NEXT_HISTORY));
+}
+
+
+/* vi_repeat_search_next():
+ * Vi repeat current search in the same search direction
+ * [n]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_repeat_search_next(EditLine *el, int c)
+{
+
+ if (el->el_search.patlen == 0)
+ return (CC_ERROR);
+ else
+ return (cv_repeat_srch(el, el->el_search.patdir));
+}
+
+
+/* vi_repeat_search_prev():
+ * Vi repeat current search in the opposite search direction
+ * [N]
+ */
+/*ARGSUSED*/
+protected el_action_t
+vi_repeat_search_prev(EditLine *el, int c)
+{
+
+ if (el->el_search.patlen == 0)
+ return (CC_ERROR);
+ else
+ return (cv_repeat_srch(el,
+ el->el_search.patdir == ED_SEARCH_PREV_HISTORY ?
+ ED_SEARCH_NEXT_HISTORY : ED_SEARCH_PREV_HISTORY));
+}
+
+
+/* vi_next_char():
+ * Vi move to the character specified next
+ * [f]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_next_char(EditLine *el, int c)
+{
+ char ch;
+
+ if (el_getc(el, &ch) != 1)
+ return (ed_end_of_file(el, 0));
+
+ el->el_search.chadir = CHAR_FWD;
+ el->el_search.chacha = ch;
+
+ return (cv_csearch_fwd(el, ch, el->el_state.argument, 0));
+
+}
+
+
+/* vi_prev_char():
+ * Vi move to the character specified previous
+ * [F]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_prev_char(EditLine *el, int c)
+{
+ char ch;
+
+ if (el_getc(el, &ch) != 1)
+ return (ed_end_of_file(el, 0));
+
+ el->el_search.chadir = CHAR_BACK;
+ el->el_search.chacha = ch;
+
+ return (cv_csearch_back(el, ch, el->el_state.argument, 0));
+}
+
+
+/* vi_to_next_char():
+ * Vi move up to the character specified next
+ * [t]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_to_next_char(EditLine *el, int c)
+{
+ char ch;
+
+ if (el_getc(el, &ch) != 1)
+ return (ed_end_of_file(el, 0));
+
+ return (cv_csearch_fwd(el, ch, el->el_state.argument, 1));
+
+}
+
+
+/* vi_to_prev_char():
+ * Vi move up to the character specified previous
+ * [T]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_to_prev_char(EditLine *el, int c)
+{
+ char ch;
+
+ if (el_getc(el, &ch) != 1)
+ return (ed_end_of_file(el, 0));
+
+ return (cv_csearch_back(el, ch, el->el_state.argument, 1));
+}
+
+
+/* vi_repeat_next_char():
+ * Vi repeat current character search in the same search direction
+ * [;]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_repeat_next_char(EditLine *el, int c)
+{
+
+ if (el->el_search.chacha == 0)
+ return (CC_ERROR);
+
+ return (el->el_search.chadir == CHAR_FWD
+ ? cv_csearch_fwd(el, el->el_search.chacha,
+ el->el_state.argument, 0)
+ : cv_csearch_back(el, el->el_search.chacha,
+ el->el_state.argument, 0));
+}
+
+
+/* vi_repeat_prev_char():
+ * Vi repeat current character search in the opposite search direction
+ * [,]
+ */
+protected el_action_t
+/*ARGSUSED*/
+vi_repeat_prev_char(EditLine *el, int c)
+{
+
+ if (el->el_search.chacha == 0)
+ return (CC_ERROR);
+
+ return el->el_search.chadir == CHAR_BACK ?
+ cv_csearch_fwd(el, el->el_search.chacha, el->el_state.argument, 0) :
+ cv_csearch_back(el, el->el_search.chacha, el->el_state.argument, 0);
+}
diff --git a/trunk/main/enum.c b/trunk/main/enum.c
new file mode 100644
index 000000000..b54580263
--- /dev/null
+++ b/trunk/main/enum.c
@@ -0,0 +1,655 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * Funding provided by nic.at
+ *
+ * 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 ENUM Support for Asterisk
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \arg Funding provided by nic.at
+ *
+ * \par Enum standards
+ *
+ * - NAPTR records: http://ietf.nri.reston.va.us/rfc/rfc2915.txt
+ * - DNS SRV records: http://www.ietf.org/rfc/rfc2782.txt
+ * - ENUM http://www.ietf.org/rfc/rfc3761.txt
+ * - ENUM for H.323: http://www.ietf.org/rfc/rfc3762.txt
+ * - ENUM SIP: http://www.ietf.org/rfc/rfc3764.txt
+ * - IANA ENUM Services: http://www.iana.org/assignments/enum-services
+ *
+ * \par Possible improvement
+ * \todo Implement a caching mechanism for multile enum lookups
+ * - See http://bugs.digium.com/view.php?id=6739
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#ifdef __APPLE__
+#if __APPLE_CC__ >= 1495
+#include <arpa/nameser_compat.h>
+#endif
+#endif
+#include <resolv.h>
+#include <ctype.h>
+#include <regex.h>
+
+#include "asterisk/enum.h"
+#include "asterisk/dns.h"
+#include "asterisk/channel.h"
+#include "asterisk/config.h"
+#include "asterisk/utils.h"
+#include "asterisk/manager.h"
+
+#ifdef __APPLE__
+#undef T_NAPTR
+#define T_NAPTR 35
+#endif
+
+#ifdef __APPLE__
+#undef T_TXT
+#define T_TXT 16
+#endif
+
+#define TOPLEV "e164.arpa." /*!< The IETF Enum standard root, managed by the ITU */
+
+/* Linked list from config file */
+static struct enum_search {
+ char toplev[512];
+ struct enum_search *next;
+} *toplevs;
+
+static int enumver;
+
+AST_MUTEX_DEFINE_STATIC(enumlock);
+
+/*! \brief Parse NAPTR record information elements */
+static unsigned int parse_ie(char *data, unsigned int maxdatalen, unsigned char *src, unsigned int srclen)
+{
+ unsigned int len, olen;
+
+ len = olen = (unsigned int) src[0];
+ src++;
+ srclen--;
+
+ if (len > srclen) {
+ ast_log(LOG_WARNING, "ENUM parsing failed: Wanted %d characters, got %d\n", len, srclen);
+ return -1;
+ }
+
+ if (len > maxdatalen)
+ len = maxdatalen;
+ memcpy(data, src, len);
+
+ return olen + 1;
+}
+
+/*! \brief Parse DNS NAPTR record used in ENUM ---*/
+static int parse_naptr(char *dst, int dstsize, char *tech, int techsize, unsigned char *answer, int len, char *naptrinput)
+{
+ char tech_return[80];
+ unsigned char *oanswer = answer;
+ char flags[512] = "";
+ char services[512] = "";
+ char *p;
+ char regexp[512] = "";
+ char repl[512] = "";
+ char temp[512] = "";
+ char delim;
+ char *delim2;
+ char *pattern, *subst, *d;
+ int res;
+ int regexp_len, size, backref;
+ int d_len = sizeof(temp) - 1;
+ regex_t preg;
+ regmatch_t pmatch[9];
+
+ tech_return[0] = '\0';
+
+ dst[0] = '\0';
+
+ if (len < sizeof(struct naptr)) {
+ ast_log(LOG_WARNING, "NAPTR record length too short\n");
+ return -1;
+ }
+ answer += sizeof(struct naptr);
+ len -= sizeof(struct naptr);
+ if ((res = parse_ie(flags, sizeof(flags) - 1, answer, len)) < 0) {
+ ast_log(LOG_WARNING, "Failed to get flags from NAPTR record\n");
+ return -1;
+ } else {
+ answer += res;
+ len -= res;
+ }
+ if ((res = parse_ie(services, sizeof(services) - 1, answer, len)) < 0) {
+ ast_log(LOG_WARNING, "Failed to get services from NAPTR record\n");
+ return -1;
+ } else {
+ answer += res;
+ len -= res;
+ }
+ if ((res = parse_ie(regexp, sizeof(regexp) - 1, answer, len)) < 0) {
+ ast_log(LOG_WARNING, "Failed to get regexp from NAPTR record\n");
+ return -1;
+ } else {
+ answer += res;
+ len -= res;
+ }
+
+ if ((res = dn_expand(oanswer, answer + len, answer, repl, sizeof(repl) - 1)) < 0) {
+ ast_log(LOG_WARNING, "Failed to expand hostname\n");
+ return -1;
+ }
+
+ ast_debug(3, "NAPTR input='%s', flags='%s', services='%s', regexp='%s', repl='%s'\n",
+ naptrinput, flags, services, regexp, repl);
+
+ if (tolower(flags[0]) != 'u') {
+ ast_log(LOG_WARNING, "NAPTR Flag must be 'U' or 'u'.\n");
+ return -1;
+ }
+
+ p = strstr(services, "e2u+");
+ if (p == NULL)
+ p = strstr(services, "E2U+");
+ if (p){
+ p = p + 4;
+ if (strchr(p, ':')){
+ p = strchr(p, ':') + 1;
+ }
+ ast_copy_string(tech_return, p, sizeof(tech_return));
+ } else {
+
+ p = strstr(services, "+e2u");
+ if (p == NULL)
+ p = strstr(services, "+E2U");
+ if (p) {
+ *p = 0;
+ p = strchr(services, ':');
+ if (p)
+ *p = 0;
+ ast_copy_string(tech_return, services, sizeof(tech_return));
+ }
+ }
+
+ /* DEDBUGGING STUB
+ ast_copy_string(regexp, "!^\\+43(.*)$!\\1@bla.fasel!", sizeof(regexp) - 1);
+ */
+
+ regexp_len = strlen(regexp);
+ if (regexp_len < 7) {
+ ast_log(LOG_WARNING, "Regex too short to be meaningful.\n");
+ return -1;
+ }
+
+
+ delim = regexp[0];
+ delim2 = strchr(regexp + 1, delim);
+ if ((delim2 == NULL) || (regexp[regexp_len-1] != delim)) {
+ ast_log(LOG_WARNING, "Regex delimiter error (on \"%s\").\n",regexp);
+ return -1;
+ }
+
+ pattern = regexp + 1;
+ *delim2 = 0;
+ subst = delim2 + 1;
+ regexp[regexp_len-1] = 0;
+
+/*
+ * now do the regex wizardry.
+ */
+
+ if (regcomp(&preg, pattern, REG_EXTENDED | REG_NEWLINE)) {
+ ast_log(LOG_WARNING, "NAPTR Regex compilation error (regex = \"%s\").\n",regexp);
+ return -1;
+ }
+
+ if (preg.re_nsub > 9) {
+ ast_log(LOG_WARNING, "NAPTR Regex compilation error: too many subs.\n");
+ regfree(&preg);
+ return -1;
+ }
+
+ if (regexec(&preg, naptrinput, 9, pmatch, 0)) {
+ ast_log(LOG_WARNING, "NAPTR Regex match failed.\n");
+ regfree(&preg);
+ return -1;
+ }
+ regfree(&preg);
+
+ d = temp;
+ d_len--;
+ while (*subst && (d_len > 0)) {
+ if ((subst[0] == '\\') && isdigit(subst[1]) && (pmatch[subst[1]-'0'].rm_so != -1)) {
+ backref = subst[1]-'0';
+ size = pmatch[backref].rm_eo - pmatch[backref].rm_so;
+ if (size > d_len) {
+ ast_log(LOG_WARNING, "Not enough space during NAPTR regex substitution.\n");
+ return -1;
+ }
+ memcpy(d, naptrinput + pmatch[backref].rm_so, size);
+ d += size;
+ d_len -= size;
+ subst += 2;
+ } else if (isprint(*subst)) {
+ *d++ = *subst++;
+ d_len--;
+ } else {
+ ast_log(LOG_WARNING, "Error during regex substitution.\n");
+ return -1;
+ }
+ }
+ *d = 0;
+ ast_copy_string(dst, temp, dstsize);
+ dst[dstsize - 1] = '\0';
+
+ if (*tech != '\0'){ /* check if it is requested NAPTR */
+ if (!strncasecmp(tech, "ALL", techsize)){
+ return 1; /* return or count any RR */
+ }
+ if (!strncasecmp(tech_return, tech, sizeof(tech_return)<techsize?sizeof(tech_return):techsize)){
+ ast_copy_string(tech, tech_return, techsize);
+ return 1; /* we got out RR */
+ } else { /* go to the next RR in the DNS answer */
+ return 0;
+ }
+ }
+
+ /* tech was not specified, return first parsed RR */
+ ast_copy_string(tech, tech_return, techsize);
+
+ return 1;
+}
+
+/* do not return requested value, just count RRs and return thei number in dst */
+#define ENUMLOOKUP_OPTIONS_COUNT 1
+
+
+/*! \brief Callback for TXT record lookup */
+static int txt_callback(void *context, unsigned char *answer, int len, unsigned char *fullanswer)
+{
+ struct enum_context *c = (struct enum_context *)context;
+
+ if (answer == NULL) {
+ c->txt = NULL;
+ c->txtlen = 0;
+ return 0;
+ }
+
+ /* skip over first byte, as for some reason it's a vertical tab character */
+ answer += 1;
+ len -= 1;
+
+ /* answer is not null-terminated, but should be */
+ /* this is safe to do, as answer has extra bytes on the end we can
+ * safely overwrite with a null */
+ answer[len] = '\0';
+ /* now increment len so that len includes the null, so that we can
+ * compare apples to apples */
+ len +=1;
+
+ /* finally, copy the answer into c->txt */
+ ast_copy_string(c->txt, (const char *) answer, len < c->txtlen ? len : (c->txtlen));
+
+ /* just to be safe, let's make sure c->txt is null terminated */
+ c->txt[(c->txtlen)-1] = '\0';
+
+ return 1;
+}
+
+/*! \brief Callback from ENUM lookup function */
+static int enum_callback(void *context, unsigned char *answer, int len, unsigned char *fullanswer)
+{
+ struct enum_context *c = context;
+ void *p = NULL;
+ int res;
+
+ res = parse_naptr(c->dst, c->dstlen, c->tech, c->techlen, answer, len, c->naptrinput);
+
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Failed to parse naptr :(\n");
+ return -1;
+ } else if (res > 0 && !ast_strlen_zero(c->dst)){ /* ok, we got needed NAPTR */
+ if (c->options & ENUMLOOKUP_OPTIONS_COUNT){ /* counting RRs */
+ c->position++;
+ snprintf(c->dst, c->dstlen, "%d", c->position);
+ } else {
+ if ((p = ast_realloc(c->naptr_rrs, sizeof(*c->naptr_rrs) * (c->naptr_rrs_count + 1)))) {
+ c->naptr_rrs = p;
+ memcpy(&c->naptr_rrs[c->naptr_rrs_count].naptr, answer, sizeof(c->naptr_rrs->naptr));
+ c->naptr_rrs[c->naptr_rrs_count].result = ast_strdup(c->dst);
+ c->naptr_rrs[c->naptr_rrs_count].tech = ast_strdup(c->tech);
+ c->naptr_rrs[c->naptr_rrs_count].sort_pos = c->naptr_rrs_count;
+ c->naptr_rrs_count++;
+ }
+ c->dst[0] = 0;
+ }
+ return 0;
+ }
+
+ if (c->options & ENUMLOOKUP_OPTIONS_COUNT) { /* counting RRs */
+ snprintf(c->dst, c->dstlen, "%d", c->position);
+ }
+
+ return 0;
+}
+
+/* ENUM lookup */
+int ast_get_enum(struct ast_channel *chan, const char *number, char *dst, int dstlen, char *tech, int techlen, char* suffix, char* options, unsigned int record, struct enum_context **argcontext)
+{
+ struct enum_context *context;
+ char tmp[259 + 512];
+ char naptrinput[512];
+ int pos = strlen(number) - 1;
+ int newpos = 0;
+ int ret = -1;
+ struct enum_search *s = NULL;
+ int version = -1;
+ /* for ISN rewrite */
+ char *p1 = NULL;
+ char *p2 = NULL;
+ int k = 0;
+ int i = 0;
+ int z = 0;
+
+ if (!(context = ast_calloc(1, sizeof(*context))))
+ return -1;
+
+ ast_copy_string(naptrinput, number[0] == 'n' ? number+1 : number, sizeof(naptrinput));
+
+ context->naptrinput = naptrinput; /* The number */
+ context->dst = dst; /* Return string */
+ context->dstlen = dstlen;
+ context->tech = tech;
+ context->techlen = techlen;
+ context->options = 0;
+ context->position = record;
+ context->naptr_rrs = NULL;
+ context->naptr_rrs_count = 0;
+
+ if (options != NULL) {
+ if (*options == 'c') {
+ context->options = ENUMLOOKUP_OPTIONS_COUNT;
+ context->position = 0;
+ }
+ }
+
+ ast_debug(1, "ast_get_enum(): n='%s', tech='%s', suffix='%s', options='%d', record='%d'\n",
+ number, tech, suffix, context->options, context->position);
+
+ if (pos > 128)
+ pos = 128;
+
+ /* ISN rewrite */
+ p1 = strchr(number, '*');
+
+ if (number[0] == 'n') { /* do not perform ISN rewrite ('n' is testing flag) */
+ p1 = NULL;
+ k = 1; /* strip 'n' from number */
+ }
+
+ if (p1 != NULL) {
+ p2 = p1+1;
+ while (p1 > number){
+ p1--;
+ tmp[newpos++] = *p1;
+ tmp[newpos++] = '.';
+ }
+ if (*p2) {
+ while (*p2 && newpos < 128){
+ tmp[newpos++] = *p2;
+ p2++;
+ }
+ tmp[newpos++] = '.';
+ }
+
+ } else {
+ while (pos >= k) {
+ if (isdigit(number[pos])) {
+ tmp[newpos++] = number[pos];
+ tmp[newpos++] = '.';
+ }
+ pos--;
+ }
+ }
+
+ if (chan && ast_autoservice_start(chan) < 0) {
+ ast_free(context);
+ return -1;
+ }
+
+ if (suffix) {
+ ast_copy_string(tmp + newpos, suffix, sizeof(tmp) - newpos);
+ ret = ast_search_dns(context, tmp, C_IN, T_NAPTR, enum_callback);
+ ast_debug(1, "ast_get_enum: ast_search_dns(%s) returned %d\n", tmp, ret);
+ } else {
+ ret = -1; /* this is actually dead code since the demise of app_enum.c */
+ for (;;) {
+ ast_mutex_lock(&enumlock);
+ if (version != enumver) {
+ /* Ooh, a reload... */
+ s = toplevs;
+ version = enumver;
+ } else {
+ s = s->next;
+ }
+ ast_mutex_unlock(&enumlock);
+
+ if (!s)
+ break;
+
+ ast_copy_string(tmp + newpos, s->toplev, sizeof(tmp) - newpos);
+ ret = ast_search_dns(&context, tmp, C_IN, T_NAPTR, enum_callback);
+ ast_debug(1, "ast_get_enum: ast_search_dns(%s) returned %d\n", tmp, ret);
+ if (ret > 0)
+ break;
+ }
+ }
+
+ if (ret < 0) {
+ ast_debug(1, "No such number found: %s (%s)\n", tmp, strerror(errno));
+ strcpy(dst, "0");
+ ret = 0;
+ }
+
+ if (context->naptr_rrs_count >= context->position && ! (context->options & ENUMLOOKUP_OPTIONS_COUNT)) {
+ /* sort array by NAPTR order/preference */
+ for (k = 0; k < context->naptr_rrs_count; k++) {
+ for (i = 0; i < context->naptr_rrs_count; i++) {
+ /* use order first and then preference to compare */
+ if ((ntohs(context->naptr_rrs[k].naptr.order) < ntohs(context->naptr_rrs[i].naptr.order)
+ && context->naptr_rrs[k].sort_pos > context->naptr_rrs[i].sort_pos)
+ || (ntohs(context->naptr_rrs[k].naptr.order) > ntohs(context->naptr_rrs[i].naptr.order)
+ && context->naptr_rrs[k].sort_pos < context->naptr_rrs[i].sort_pos)){
+ z = context->naptr_rrs[k].sort_pos;
+ context->naptr_rrs[k].sort_pos = context->naptr_rrs[i].sort_pos;
+ context->naptr_rrs[i].sort_pos = z;
+ continue;
+ }
+ if (ntohs(context->naptr_rrs[k].naptr.order) == ntohs(context->naptr_rrs[i].naptr.order)) {
+ if ((ntohs(context->naptr_rrs[k].naptr.pref) < ntohs(context->naptr_rrs[i].naptr.pref)
+ && context->naptr_rrs[k].sort_pos > context->naptr_rrs[i].sort_pos)
+ || (ntohs(context->naptr_rrs[k].naptr.pref) > ntohs(context->naptr_rrs[i].naptr.pref)
+ && context->naptr_rrs[k].sort_pos < context->naptr_rrs[i].sort_pos)){
+ z = context->naptr_rrs[k].sort_pos;
+ context->naptr_rrs[k].sort_pos = context->naptr_rrs[i].sort_pos;
+ context->naptr_rrs[i].sort_pos = z;
+ }
+ }
+ }
+ }
+ for (k = 0; k < context->naptr_rrs_count; k++) {
+ if (context->naptr_rrs[k].sort_pos == context->position-1) {
+ ast_copy_string(context->dst, context->naptr_rrs[k].result, dstlen);
+ ast_copy_string(context->tech, context->naptr_rrs[k].tech, techlen);
+ break;
+ }
+ }
+ } else if (!(context->options & ENUMLOOKUP_OPTIONS_COUNT)) {
+ context->dst[0] = 0;
+ }
+ if (chan)
+ ret |= ast_autoservice_stop(chan);
+
+ if (!argcontext) {
+ for (k = 0; k < context->naptr_rrs_count; k++) {
+ ast_free(context->naptr_rrs[k].result);
+ ast_free(context->naptr_rrs[k].tech);
+ }
+ ast_free(context->naptr_rrs);
+ ast_free(context);
+ } else
+ *argcontext = context;
+
+ return ret;
+}
+
+/* Get TXT record from DNS. Really has nothing to do with enum, but anyway... */
+int ast_get_txt(struct ast_channel *chan, const char *number, char *dst, int dstlen, char *tech, int techlen, char *txt, int txtlen)
+{
+ struct enum_context context;
+ char tmp[259 + 512];
+ char naptrinput[512] = "+";
+ int pos = strlen(number) - 1;
+ int newpos = 0;
+ int ret = -1;
+ struct enum_search *s = NULL;
+ int version = -1;
+
+ strncat(naptrinput, number, sizeof(naptrinput) - 2);
+
+ context.naptrinput = naptrinput;
+ context.dst = dst;
+ context.dstlen = dstlen;
+ context.tech = tech;
+ context.techlen = techlen;
+ context.txt = txt;
+ context.txtlen = txtlen;
+
+ if (pos > 128)
+ pos = 128;
+ while (pos >= 0) {
+ tmp[newpos++] = number[pos--];
+ tmp[newpos++] = '.';
+ }
+
+ if (chan && ast_autoservice_start(chan) < 0)
+ return -1;
+
+ for (;;) {
+ ast_mutex_lock(&enumlock);
+ if (version != enumver) {
+ /* Ooh, a reload... */
+ s = toplevs;
+ version = enumver;
+ } else {
+ s = s->next;
+ }
+ if (s) {
+ ast_copy_string(tmp + newpos, s->toplev, sizeof(tmp) - newpos);
+ }
+ ast_mutex_unlock(&enumlock);
+ if (!s)
+ break;
+
+ ret = ast_search_dns(&context, tmp, C_IN, T_TXT, txt_callback);
+ if (ret > 0)
+ break;
+ }
+ if (ret < 0) {
+ ast_debug(2, "No such number found in ENUM: %s (%s)\n", tmp, strerror(errno));
+ ret = 0;
+ }
+ if (chan)
+ ret |= ast_autoservice_stop(chan);
+ return ret;
+}
+
+/*! \brief Add enum tree to linked list */
+static struct enum_search *enum_newtoplev(const char *s)
+{
+ struct enum_search *tmp;
+
+ if ((tmp = ast_calloc(1, sizeof(*tmp)))) {
+ ast_copy_string(tmp->toplev, s, sizeof(tmp->toplev));
+ }
+ return tmp;
+}
+
+/*! \brief Initialize the ENUM support subsystem */
+static int private_enum_init(int reload)
+{
+ struct ast_config *cfg;
+ struct enum_search *s, *sl;
+ struct ast_variable *v;
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+
+ if ((cfg = ast_config_load("enum.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
+ return 0;
+
+ /* Destroy existing list */
+ ast_mutex_lock(&enumlock);
+ s = toplevs;
+ while (s) {
+ sl = s;
+ s = s->next;
+ ast_free(sl);
+ }
+ toplevs = NULL;
+ if (cfg) {
+ sl = NULL;
+ v = ast_variable_browse(cfg, "general");
+ while (v) {
+ if (!strcasecmp(v->name, "search")) {
+ s = enum_newtoplev(v->value);
+ if (s) {
+ if (sl)
+ sl->next = s;
+ else
+ toplevs = s;
+ sl = s;
+ }
+ }
+ v = v->next;
+ }
+ ast_config_destroy(cfg);
+ } else {
+ toplevs = enum_newtoplev(TOPLEV);
+ }
+ enumver++;
+ ast_mutex_unlock(&enumlock);
+ manager_event(EVENT_FLAG_SYSTEM, "Reload", "Module: Enum\r\nStatus: Enabled\r\nMessage: ENUM reload Requested\r\n");
+ return 0;
+}
+
+int ast_enum_init(void)
+{
+ return private_enum_init(0);
+}
+
+int ast_enum_reload(void)
+{
+ return private_enum_init(1);
+}
diff --git a/trunk/main/event.c b/trunk/main/event.c
new file mode 100644
index 000000000..333b628a1
--- /dev/null
+++ b/trunk/main/event.c
@@ -0,0 +1,822 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2007, 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
+ *
+ * \brief Internal generic event system
+ *
+ * \author Russell Bryant <russell@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/_private.h"
+#include "asterisk/event.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/lock.h"
+#include "asterisk/utils.h"
+#include "asterisk/unaligned.h"
+
+/* Only use one thread for now to ensure ordered delivery */
+#define NUM_EVENT_THREADS 1
+
+/*!
+ * \brief An event information element
+ *
+ * \note The format of this structure is important. Since these events may
+ * be sent directly over a network, changing this structure will break
+ * compatibility with older versions. However, at this point, this code
+ * has not made it into a release, so it is still fair game for change.
+ */
+struct ast_event_ie {
+ enum ast_event_ie_type ie_type:16;
+ /*! Total length of the IE payload */
+ uint16_t ie_payload_len;
+ unsigned char ie_payload[0];
+} __attribute__ ((packed));
+
+/*!
+ * \brief An event
+ *
+ * An ast_event consists of an event header (this structure), and zero or
+ * more information elements defined by ast_event_ie.
+ *
+ * \note The format of this structure is important. Since these events may
+ * be sent directly over a network, changing this structure will break
+ * compatibility with older versions. However, at this point, this code
+ * has not made it into a release, so it is still fair game for change.
+ */
+struct ast_event {
+ /*! Event type */
+ enum ast_event_type type:16;
+ /*! Total length of the event */
+ uint16_t event_len:16;
+ /*! The data payload of the event, made up of information elements */
+ unsigned char payload[0];
+} __attribute__ ((packed));
+
+struct ast_event_ref {
+ struct ast_event *event;
+ AST_LIST_ENTRY(ast_event_ref) entry;
+};
+
+struct ast_event_iterator {
+ uint16_t event_len;
+ const struct ast_event *event;
+ struct ast_event_ie *ie;
+};
+
+/*! \brief data shared between event dispatching threads */
+static struct {
+ ast_cond_t cond;
+ ast_mutex_t lock;
+ AST_LIST_HEAD_NOLOCK(, ast_event_ref) event_q;
+} event_thread = {
+ .lock = AST_MUTEX_INIT_VALUE,
+};
+
+struct ast_event_ie_val {
+ AST_LIST_ENTRY(ast_event_ie_val) entry;
+ enum ast_event_ie_type ie_type;
+ enum ast_event_ie_pltype ie_pltype;
+ union {
+ uint32_t uint;
+ const char *str;
+ } payload;
+};
+
+/*! \brief Event subscription */
+struct ast_event_sub {
+ enum ast_event_type type;
+ ast_event_cb_t cb;
+ void *userdata;
+ uint32_t uniqueid;
+ AST_LIST_HEAD_NOLOCK(, ast_event_ie_val) ie_vals;
+ AST_RWLIST_ENTRY(ast_event_sub) entry;
+};
+
+static uint32_t sub_uniqueid;
+
+/*! \brief Event subscriptions
+ * The event subscribers are indexed by which event they are subscribed to */
+static AST_RWLIST_HEAD(ast_event_sub_list, ast_event_sub) ast_event_subs[AST_EVENT_TOTAL];
+
+/*! \brief Cached events
+ * The event cache is indexed on the event type. The purpose of this is
+ * for events that express some sort of state. So, when someone first
+ * needs to know this state, it can get the last known state from the cache. */
+static AST_RWLIST_HEAD(ast_event_ref_list, ast_event_ref) ast_event_cache[AST_EVENT_TOTAL];
+
+static void ast_event_ie_val_destroy(struct ast_event_ie_val *ie_val)
+{
+ if (ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_STR)
+ ast_free((void *) ie_val->payload.str);
+
+ ast_free(ie_val);
+}
+
+enum ast_event_subscriber_res ast_event_check_subscriber(enum ast_event_type type, ...)
+{
+ va_list ap;
+ enum ast_event_ie_type ie_type;
+ enum ast_event_subscriber_res res = AST_EVENT_SUB_NONE;
+ struct ast_event_ie_val *ie_val, *sub_ie_val;
+ struct ast_event_sub *sub;
+ AST_LIST_HEAD_NOLOCK_STATIC(ie_vals, ast_event_ie_val);
+
+ if (type >= AST_EVENT_TOTAL) {
+ ast_log(LOG_ERROR, "%u is an invalid type!\n", type);
+ return res;
+ }
+
+ va_start(ap, type);
+ for (ie_type = va_arg(ap, enum ast_event_type);
+ ie_type != AST_EVENT_IE_END;
+ ie_type = va_arg(ap, enum ast_event_type))
+ {
+ struct ast_event_ie_val *ie_val = alloca(sizeof(*ie_val));
+ memset(ie_val, 0, sizeof(*ie_val));
+ ie_val->ie_type = ie_type;
+ ie_val->ie_pltype = va_arg(ap, enum ast_event_ie_pltype);
+ if (ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_UINT)
+ ie_val->payload.uint = va_arg(ap, uint32_t);
+ else if (ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_STR)
+ ie_val->payload.str = ast_strdupa(va_arg(ap, const char *));
+ AST_LIST_INSERT_TAIL(&ie_vals, ie_val, entry);
+ }
+ va_end(ap);
+
+ AST_RWLIST_RDLOCK(&ast_event_subs[type]);
+ AST_RWLIST_TRAVERSE(&ast_event_subs[type], sub, entry) {
+ AST_LIST_TRAVERSE(&ie_vals, ie_val, entry) {
+ AST_LIST_TRAVERSE(&sub->ie_vals, sub_ie_val, entry) {
+ if (sub_ie_val->ie_type == ie_val->ie_type)
+ break;
+ }
+ if (!sub_ie_val) {
+ if (ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_EXISTS)
+ break;
+ continue;
+ }
+ /* The subscriber doesn't actually care what the value is */
+ if (sub_ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_EXISTS)
+ continue;
+ if (ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_UINT &&
+ ie_val->payload.uint != sub_ie_val->payload.uint)
+ break;
+ if (ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_STR &&
+ strcmp(ie_val->payload.str, sub_ie_val->payload.str))
+ break;
+ }
+ if (!ie_val)
+ break;
+ }
+ AST_RWLIST_UNLOCK(&ast_event_subs[type]);
+
+ if (sub) /* All parameters were matched */
+ return AST_EVENT_SUB_EXISTS;
+
+ AST_RWLIST_RDLOCK(&ast_event_subs[AST_EVENT_ALL]);
+ if (!AST_LIST_EMPTY(&ast_event_subs[AST_EVENT_ALL]))
+ res = AST_EVENT_SUB_EXISTS;
+ AST_RWLIST_UNLOCK(&ast_event_subs[AST_EVENT_ALL]);
+
+ return res;
+}
+
+/*! \brief Send AST_EVENT_SUB events to this subscriber of ... subscriber events */
+void ast_event_report_subs(const struct ast_event_sub *event_sub)
+{
+ struct ast_event *event;
+ struct ast_event_sub *sub;
+ enum ast_event_type event_type = -1;
+ struct ast_event_ie_val *ie_val;
+
+ if (event_sub->type != AST_EVENT_SUB)
+ return;
+
+ AST_LIST_TRAVERSE(&event_sub->ie_vals, ie_val, entry) {
+ if (ie_val->ie_type == AST_EVENT_IE_EVENTTYPE) {
+ event_type = ie_val->payload.uint;
+ break;
+ }
+ }
+
+ if (event_type == -1)
+ return;
+
+ AST_RWLIST_RDLOCK(&ast_event_subs[event_type]);
+ AST_RWLIST_TRAVERSE(&ast_event_subs[event_type], sub, entry) {
+ if (event_sub == sub)
+ continue;
+
+ event = ast_event_new(AST_EVENT_SUB,
+ AST_EVENT_IE_UNIQUEID, AST_EVENT_IE_PLTYPE_UINT, sub->uniqueid,
+ AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, sub->type,
+ AST_EVENT_IE_END);
+
+ AST_LIST_TRAVERSE(&sub->ie_vals, ie_val, entry) {
+ switch (ie_val->ie_pltype) {
+ case AST_EVENT_IE_PLTYPE_EXISTS:
+ ast_event_append_ie_uint(&event, AST_EVENT_IE_EXISTS, ie_val->ie_type);
+ break;
+ case AST_EVENT_IE_PLTYPE_UINT:
+ ast_event_append_ie_uint(&event, ie_val->ie_type, ie_val->payload.uint);
+ break;
+ case AST_EVENT_IE_PLTYPE_STR:
+ ast_event_append_ie_str(&event, ie_val->ie_type, ie_val->payload.str);
+ break;
+ }
+ if (!event)
+ break;
+ }
+
+ if (!event)
+ continue;
+
+ event_sub->cb(event, event_sub->userdata);
+
+ ast_event_destroy(event);
+ }
+ AST_RWLIST_UNLOCK(&ast_event_subs[event_type]);
+}
+
+struct ast_event_sub *ast_event_subscribe(enum ast_event_type type, ast_event_cb_t cb,
+ void *userdata, ...)
+{
+ va_list ap;
+ enum ast_event_ie_type ie_type;
+ struct ast_event_sub *sub;
+ struct ast_event *event;
+
+ if (type >= AST_EVENT_TOTAL) {
+ ast_log(LOG_ERROR, "%u is an invalid type!\n", type);
+ return NULL;
+ }
+
+ if (!(sub = ast_calloc(1, sizeof(*sub))))
+ return NULL;
+
+ va_start(ap, userdata);
+ for (ie_type = va_arg(ap, enum ast_event_type);
+ ie_type != AST_EVENT_IE_END;
+ ie_type = va_arg(ap, enum ast_event_type))
+ {
+ struct ast_event_ie_val *ie_val;
+ if (!(ie_val = ast_calloc(1, sizeof(*ie_val))))
+ continue;
+ ie_val->ie_type = ie_type;
+ ie_val->ie_pltype = va_arg(ap, enum ast_event_ie_pltype);
+ if (ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_UINT)
+ ie_val->payload.uint = va_arg(ap, uint32_t);
+ else if (ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_STR) {
+ if (!(ie_val->payload.str = ast_strdup(va_arg(ap, const char *)))) {
+ ast_free(ie_val);
+ continue;
+ }
+ }
+ AST_LIST_INSERT_TAIL(&sub->ie_vals, ie_val, entry);
+ }
+ va_end(ap);
+
+ sub->type = type;
+ sub->cb = cb;
+ sub->userdata = userdata;
+ sub->uniqueid = ast_atomic_fetchadd_int((int *) &sub_uniqueid, 1);
+
+ if (ast_event_check_subscriber(AST_EVENT_SUB,
+ AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, type,
+ AST_EVENT_IE_END) != AST_EVENT_SUB_NONE) {
+ struct ast_event_ie_val *ie_val;
+
+ event = ast_event_new(AST_EVENT_SUB,
+ AST_EVENT_IE_UNIQUEID, AST_EVENT_IE_PLTYPE_UINT, sub->uniqueid,
+ AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, sub->type,
+ AST_EVENT_IE_END);
+
+ AST_LIST_TRAVERSE(&sub->ie_vals, ie_val, entry) {
+ switch (ie_val->ie_pltype) {
+ case AST_EVENT_IE_PLTYPE_EXISTS:
+ ast_event_append_ie_uint(&event, AST_EVENT_IE_EXISTS, ie_val->ie_type);
+ break;
+ case AST_EVENT_IE_PLTYPE_UINT:
+ ast_event_append_ie_uint(&event, ie_val->ie_type, ie_val->payload.uint);
+ break;
+ case AST_EVENT_IE_PLTYPE_STR:
+ ast_event_append_ie_str(&event, ie_val->ie_type, ie_val->payload.str);
+ break;
+ }
+ if (!event)
+ break;
+ }
+
+ if (event)
+ ast_event_queue(event);
+ }
+
+ AST_RWLIST_WRLOCK(&ast_event_subs[type]);
+ AST_RWLIST_INSERT_TAIL(&ast_event_subs[type], sub, entry);
+ AST_RWLIST_UNLOCK(&ast_event_subs[type]);
+
+ return sub;
+}
+
+static void ast_event_sub_destroy(struct ast_event_sub *sub)
+{
+ struct ast_event_ie_val *ie_val;
+
+ while ((ie_val = AST_LIST_REMOVE_HEAD(&sub->ie_vals, entry)))
+ ast_event_ie_val_destroy(ie_val);
+
+ ast_free(sub);
+}
+
+void ast_event_unsubscribe(struct ast_event_sub *sub)
+{
+ struct ast_event *event;
+
+ AST_RWLIST_WRLOCK(&ast_event_subs[sub->type]);
+ AST_LIST_REMOVE(&ast_event_subs[sub->type], sub, entry);
+ AST_RWLIST_UNLOCK(&ast_event_subs[sub->type]);
+
+ if (ast_event_check_subscriber(AST_EVENT_UNSUB,
+ AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, sub->type,
+ AST_EVENT_IE_END) != AST_EVENT_SUB_NONE) {
+
+ event = ast_event_new(AST_EVENT_UNSUB,
+ AST_EVENT_IE_UNIQUEID, AST_EVENT_IE_PLTYPE_UINT, sub->uniqueid,
+ AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, sub->type,
+ AST_EVENT_IE_END);
+
+ if (event)
+ ast_event_queue(event);
+ }
+
+ ast_event_sub_destroy(sub);
+}
+
+void ast_event_iterator_init(struct ast_event_iterator *iterator, const struct ast_event *event)
+{
+ iterator->event_len = ntohs(event->event_len);
+ iterator->event = event;
+ iterator->ie = (struct ast_event_ie *) ( ((char *) event) + sizeof(*event) );
+ return;
+}
+
+int ast_event_iterator_next(struct ast_event_iterator *iterator)
+{
+ iterator->ie = (struct ast_event_ie *) ( ((char *) iterator->ie) + sizeof(*iterator->ie) + ntohs(iterator->ie->ie_payload_len));
+ return ((iterator->event_len < (((char *) iterator->ie) - ((char *) iterator->event))) ? -1 : 0);
+}
+
+enum ast_event_ie_type ast_event_iterator_get_ie_type(struct ast_event_iterator *iterator)
+{
+ return iterator->ie->ie_type;
+}
+
+uint32_t ast_event_iterator_get_ie_uint(struct ast_event_iterator *iterator)
+{
+ return ntohl(get_unaligned_uint32(iterator->ie->ie_payload));
+}
+
+const char *ast_event_iterator_get_ie_str(struct ast_event_iterator *iterator)
+{
+ return (const char*)iterator->ie->ie_payload;
+}
+
+void *ast_event_iterator_get_ie_raw(struct ast_event_iterator *iterator)
+{
+ return iterator->ie->ie_payload;
+}
+
+enum ast_event_type ast_event_get_type(const struct ast_event *event)
+{
+ return ntohs(event->type);
+}
+
+uint32_t ast_event_get_ie_uint(const struct ast_event *event, enum ast_event_ie_type ie_type)
+{
+ const uint32_t *ie_val;
+
+ ie_val = ast_event_get_ie_raw(event, ie_type);
+
+ return ie_val ? ntohl(get_unaligned_uint32(ie_val)) : 0;
+}
+
+const char *ast_event_get_ie_str(const struct ast_event *event, enum ast_event_ie_type ie_type)
+{
+ return ast_event_get_ie_raw(event, ie_type);
+}
+
+const void *ast_event_get_ie_raw(const struct ast_event *event, enum ast_event_ie_type ie_type)
+{
+ struct ast_event_iterator iterator;
+ int res = 0;
+
+ ie_type = ntohs(ie_type);
+
+ for (ast_event_iterator_init(&iterator, event); !res; res = ast_event_iterator_next(&iterator)) {
+ if (ast_event_iterator_get_ie_type(&iterator) == ie_type)
+ return ast_event_iterator_get_ie_raw(&iterator);
+ }
+
+ return NULL;
+}
+
+int ast_event_append_ie_str(struct ast_event **event, enum ast_event_ie_type ie_type,
+ const char *str)
+{
+ return ast_event_append_ie_raw(event, ie_type, str, strlen(str) + 1);
+}
+
+int ast_event_append_ie_uint(struct ast_event **event, enum ast_event_ie_type ie_type,
+ uint32_t data)
+{
+ data = htonl(data);
+ return ast_event_append_ie_raw(event, ie_type, &data, sizeof(data));
+}
+
+int ast_event_append_ie_raw(struct ast_event **event, enum ast_event_ie_type ie_type,
+ const void *data, size_t data_len)
+{
+ struct ast_event_ie *ie;
+ unsigned int extra_len;
+ uint16_t event_len;
+
+ event_len = ntohs((*event)->event_len);
+ extra_len = sizeof(*ie) + data_len;
+
+ if (!(*event = ast_realloc(*event, event_len + extra_len)))
+ return -1;
+
+ ie = (struct ast_event_ie *) ( ((char *) *event) + event_len );
+ ie->ie_type = htons(ie_type);
+ ie->ie_payload_len = htons(data_len);
+ memcpy(ie->ie_payload, data, data_len);
+
+ (*event)->event_len = htons(event_len + extra_len);
+
+ return 0;
+}
+
+struct ast_event *ast_event_new(enum ast_event_type type, ...)
+{
+ va_list ap;
+ struct ast_event *event;
+ enum ast_event_type ie_type;
+ struct ast_event_ie_val *ie_val;
+ AST_LIST_HEAD_NOLOCK_STATIC(ie_vals, ast_event_ie_val);
+
+ /* Invalid type */
+ if (type >= AST_EVENT_TOTAL) {
+ ast_log(LOG_WARNING, "Someone tried to create an event of invalid "
+ "type '%d'!\n", type);
+ return NULL;
+ }
+
+ va_start(ap, type);
+ for (ie_type = va_arg(ap, enum ast_event_type);
+ ie_type != AST_EVENT_IE_END;
+ ie_type = va_arg(ap, enum ast_event_type))
+ {
+ struct ast_event_ie_val *ie_val = alloca(sizeof(*ie_val));
+ memset(ie_val, 0, sizeof(*ie_val));
+ ie_val->ie_type = ie_type;
+ ie_val->ie_pltype = va_arg(ap, enum ast_event_ie_pltype);
+ if (ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_UINT)
+ ie_val->payload.uint = va_arg(ap, uint32_t);
+ else if (ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_STR)
+ ie_val->payload.str = ast_strdupa(va_arg(ap, const char *));
+ AST_LIST_INSERT_TAIL(&ie_vals, ie_val, entry);
+ }
+ va_end(ap);
+
+ if (!(event = ast_calloc(1, sizeof(*event))))
+ return NULL;
+
+ event->type = htons(type);
+ event->event_len = htons(sizeof(*event));
+
+ AST_LIST_TRAVERSE(&ie_vals, ie_val, entry) {
+ if (ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_STR)
+ ast_event_append_ie_str(&event, ie_val->ie_type, ie_val->payload.str);
+ else if (ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_UINT)
+ ast_event_append_ie_uint(&event, ie_val->ie_type, ie_val->payload.uint);
+
+ if (!event)
+ break;
+ }
+
+ return event;
+}
+
+void ast_event_destroy(struct ast_event *event)
+{
+ ast_free(event);
+}
+
+static void ast_event_ref_destroy(struct ast_event_ref *event_ref)
+{
+ ast_event_destroy(event_ref->event);
+ ast_free(event_ref);
+}
+
+static struct ast_event *ast_event_dup(const struct ast_event *event)
+{
+ struct ast_event *dup_event;
+ uint16_t event_len;
+
+ event_len = ntohs(event->event_len);
+
+ if (!(dup_event = ast_calloc(1, event_len)))
+ return NULL;
+
+ memcpy(dup_event, event, event_len);
+
+ return dup_event;
+}
+
+struct ast_event *ast_event_get_cached(enum ast_event_type type, ...)
+{
+ va_list ap;
+ enum ast_event_ie_type ie_type;
+ struct ast_event *dup_event = NULL;
+ struct ast_event_ref *event_ref;
+ struct cache_arg {
+ AST_LIST_ENTRY(cache_arg) entry;
+ enum ast_event_ie_type ie_type;
+ enum ast_event_ie_pltype ie_pltype;
+ union {
+ uint32_t uint;
+ const char *str;
+ } payload;
+ } *cache_arg;
+ AST_LIST_HEAD_NOLOCK_STATIC(cache_args, cache_arg);
+
+ if (type >= AST_EVENT_TOTAL) {
+ ast_log(LOG_ERROR, "%u is an invalid type!\n", type);
+ return NULL;
+ }
+
+ va_start(ap, type);
+ for (ie_type = va_arg(ap, enum ast_event_type);
+ ie_type != AST_EVENT_IE_END;
+ ie_type = va_arg(ap, enum ast_event_type))
+ {
+ cache_arg = alloca(sizeof(*cache_arg));
+ memset(cache_arg, 0, sizeof(*cache_arg));
+ cache_arg->ie_type = ie_type;
+ cache_arg->ie_pltype = va_arg(ap, enum ast_event_ie_pltype);
+ if (cache_arg->ie_pltype == AST_EVENT_IE_PLTYPE_UINT)
+ cache_arg->payload.uint = va_arg(ap, uint32_t);
+ else if (cache_arg->ie_pltype == AST_EVENT_IE_PLTYPE_STR)
+ cache_arg->payload.str = ast_strdupa(va_arg(ap, const char *));
+ AST_LIST_INSERT_TAIL(&cache_args, cache_arg, entry);
+ }
+ va_end(ap);
+
+ if (AST_LIST_EMPTY(&cache_args)) {
+ ast_log(LOG_ERROR, "Events can not be retrieved from the cache without "
+ "specifying at least one IE type!\n");
+ return NULL;
+ }
+
+ AST_RWLIST_RDLOCK(&ast_event_cache[type]);
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&ast_event_cache[type], event_ref, entry) {
+ AST_LIST_TRAVERSE(&cache_args, cache_arg, entry) {
+ if ( ! ( (cache_arg->ie_pltype == AST_EVENT_IE_PLTYPE_UINT &&
+ (cache_arg->payload.uint ==
+ ast_event_get_ie_uint(event_ref->event, cache_arg->ie_type))) ||
+
+ (cache_arg->ie_pltype == AST_EVENT_IE_PLTYPE_STR &&
+ (!strcmp(cache_arg->payload.str,
+ ast_event_get_ie_str(event_ref->event, cache_arg->ie_type)))) ||
+
+ (cache_arg->ie_pltype == AST_EVENT_IE_PLTYPE_EXISTS &&
+ ast_event_get_ie_raw(event_ref->event, cache_arg->ie_type)) ) )
+ {
+ break;
+ }
+ }
+ if (!cache_arg) {
+ /* All parameters were matched on this cache entry, so return it */
+ dup_event = ast_event_dup(event_ref->event);
+ break;
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END
+ AST_RWLIST_UNLOCK(&ast_event_cache[type]);
+
+ return dup_event;
+}
+
+/*! \brief Duplicate an event and add it to the cache
+ * \note This assumes this index in to the cache is locked */
+static int ast_event_dup_and_cache(const struct ast_event *event)
+{
+ struct ast_event *dup_event;
+ struct ast_event_ref *event_ref;
+
+ if (!(dup_event = ast_event_dup(event)))
+ return -1;
+ if (!(event_ref = ast_calloc(1, sizeof(*event_ref))))
+ return -1;
+
+ event_ref->event = dup_event;
+
+ AST_LIST_INSERT_TAIL(&ast_event_cache[ntohs(event->type)], event_ref, entry);
+
+ return 0;
+}
+
+int ast_event_queue_and_cache(struct ast_event *event, ...)
+{
+ va_list ap;
+ enum ast_event_type ie_type;
+ uint16_t host_event_type;
+ struct ast_event_ref *event_ref;
+ int res;
+ struct cache_arg {
+ AST_LIST_ENTRY(cache_arg) entry;
+ enum ast_event_ie_type ie_type;
+ enum ast_event_ie_pltype ie_pltype;
+ } *cache_arg;
+ AST_LIST_HEAD_NOLOCK_STATIC(cache_args, cache_arg);
+
+ host_event_type = ntohs(event->type);
+
+ /* Invalid type */
+ if (host_event_type >= AST_EVENT_TOTAL) {
+ ast_log(LOG_WARNING, "Someone tried to queue an event of invalid "
+ "type '%d'!\n", host_event_type);
+ return -1;
+ }
+
+ va_start(ap, event);
+ for (ie_type = va_arg(ap, enum ast_event_type);
+ ie_type != AST_EVENT_IE_END;
+ ie_type = va_arg(ap, enum ast_event_type))
+ {
+ cache_arg = alloca(sizeof(*cache_arg));
+ memset(cache_arg, 0, sizeof(*cache_arg));
+ cache_arg->ie_type = ie_type;
+ cache_arg->ie_pltype = va_arg(ap, enum ast_event_ie_pltype);
+ AST_LIST_INSERT_TAIL(&cache_args, cache_arg, entry);
+ }
+ va_end(ap);
+
+ if (AST_LIST_EMPTY(&cache_args)) {
+ ast_log(LOG_ERROR, "Events can not be cached without specifying at "
+ "least one IE type!\n");
+ return ast_event_queue(event);
+ }
+
+ AST_RWLIST_WRLOCK(&ast_event_cache[host_event_type]);
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&ast_event_cache[host_event_type], event_ref, entry) {
+ AST_LIST_TRAVERSE(&cache_args, cache_arg, entry) {
+ if ( ! ( (cache_arg->ie_pltype == AST_EVENT_IE_PLTYPE_UINT &&
+ (ast_event_get_ie_uint(event, cache_arg->ie_type) ==
+ ast_event_get_ie_uint(event_ref->event, cache_arg->ie_type))) ||
+
+ (cache_arg->ie_pltype == AST_EVENT_IE_PLTYPE_STR &&
+ (!strcmp(ast_event_get_ie_str(event, cache_arg->ie_type),
+ ast_event_get_ie_str(event_ref->event, cache_arg->ie_type)))) ||
+
+ (cache_arg->ie_pltype == AST_EVENT_IE_PLTYPE_EXISTS &&
+ ast_event_get_ie_raw(event_ref->event, cache_arg->ie_type)) ) )
+ {
+ break;
+ }
+ }
+ if (!cache_arg) {
+ /* All parameters were matched on this cache entry, so remove it */
+ AST_LIST_REMOVE_CURRENT(entry);
+ ast_event_ref_destroy(event_ref);
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+ res = ast_event_dup_and_cache(event);
+ AST_RWLIST_UNLOCK(&ast_event_cache[host_event_type]);
+
+ return (ast_event_queue(event) || res) ? -1 : 0;
+}
+
+int ast_event_queue(struct ast_event *event)
+{
+ struct ast_event_ref *event_ref;
+ uint16_t host_event_type;
+
+ host_event_type = ntohs(event->type);
+
+ /* Invalid type */
+ if (host_event_type >= AST_EVENT_TOTAL) {
+ ast_log(LOG_WARNING, "Someone tried to queue an event of invalid "
+ "type '%d'!\n", host_event_type);
+ return -1;
+ }
+
+ /* If nobody has subscribed to this event type, throw it away now */
+ if (ast_event_check_subscriber(host_event_type, AST_EVENT_IE_END)
+ == AST_EVENT_SUB_NONE) {
+ ast_event_destroy(event);
+ return 0;
+ }
+
+ if (!(event_ref = ast_calloc(1, sizeof(*event_ref))))
+ return -1;
+
+ event_ref->event = event;
+
+ ast_mutex_lock(&event_thread.lock);
+ AST_LIST_INSERT_TAIL(&event_thread.event_q, event_ref, entry);
+ ast_cond_signal(&event_thread.cond);
+ ast_mutex_unlock(&event_thread.lock);
+
+ return 0;
+}
+
+static void *ast_event_dispatcher(void *unused)
+{
+ for (;;) {
+ struct ast_event_ref *event_ref;
+ struct ast_event_sub *sub;
+ uint16_t host_event_type;
+
+ ast_mutex_lock(&event_thread.lock);
+ while (!(event_ref = AST_LIST_REMOVE_HEAD(&event_thread.event_q, entry)))
+ ast_cond_wait(&event_thread.cond, &event_thread.lock);
+ ast_mutex_unlock(&event_thread.lock);
+
+ host_event_type = ntohs(event_ref->event->type);
+
+ /* Subscribers to this specific event first */
+ AST_RWLIST_RDLOCK(&ast_event_subs[host_event_type]);
+ AST_RWLIST_TRAVERSE(&ast_event_subs[host_event_type], sub, entry) {
+ struct ast_event_ie_val *ie_val;
+ AST_LIST_TRAVERSE(&sub->ie_vals, ie_val, entry) {
+ if (ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_EXISTS &&
+ ast_event_get_ie_raw(event_ref->event, ie_val->ie_type)) {
+ continue;
+ } else if (ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_UINT &&
+ ast_event_get_ie_uint(event_ref->event, ie_val->ie_type)
+ == ie_val->payload.uint) {
+ continue;
+ } else if (ie_val->ie_pltype == AST_EVENT_IE_PLTYPE_STR &&
+ !strcmp(ast_event_get_ie_str(event_ref->event, ie_val->ie_type),
+ ie_val->payload.str)) {
+ continue;
+ }
+ break;
+ }
+ if (ie_val)
+ continue;
+ sub->cb(event_ref->event, sub->userdata);
+ }
+ AST_RWLIST_UNLOCK(&ast_event_subs[host_event_type]);
+
+ /* Now to subscribers to all event types */
+ AST_RWLIST_RDLOCK(&ast_event_subs[AST_EVENT_ALL]);
+ AST_RWLIST_TRAVERSE(&ast_event_subs[AST_EVENT_ALL], sub, entry)
+ sub->cb(event_ref->event, sub->userdata);
+ AST_RWLIST_UNLOCK(&ast_event_subs[AST_EVENT_ALL]);
+
+ ast_event_ref_destroy(event_ref);
+ }
+
+ return NULL;
+}
+
+void ast_event_init(void)
+{
+ int i;
+
+ for (i = 0; i < AST_EVENT_TOTAL; i++)
+ AST_RWLIST_HEAD_INIT(&ast_event_subs[i]);
+
+ for (i = 0; i < AST_EVENT_TOTAL; i++)
+ AST_RWLIST_HEAD_INIT(&ast_event_cache[i]);
+
+ ast_cond_init(&event_thread.cond, NULL);
+
+ for (i = 0; i < NUM_EVENT_THREADS; i++) {
+ pthread_t dont_care;
+ ast_pthread_create_background(&dont_care, NULL, ast_event_dispatcher, NULL);
+ }
+}
diff --git a/trunk/main/file.c b/trunk/main/file.c
new file mode 100644
index 000000000..fe51c2df7
--- /dev/null
+++ b/trunk/main/file.c
@@ -0,0 +1,1245 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006, 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.
+ */
+
+/*! \file
+ *
+ * \brief Generic File Format Support.
+ *
+ * \author Mark Spencer <markster@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <dirent.h>
+#include <sys/stat.h>
+
+#include "asterisk/_private.h" /* declare ast_file_init() */
+#include "asterisk/paths.h" /* use ast_config_AST_DATA_DIR */
+#include "asterisk/mod_format.h"
+#include "asterisk/cli.h"
+#include "asterisk/channel.h"
+#include "asterisk/sched.h"
+#include "asterisk/translate.h"
+#include "asterisk/utils.h"
+#include "asterisk/lock.h"
+#include "asterisk/app.h"
+#include "asterisk/pbx.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/module.h"
+
+/*
+ * The following variable controls the layout of localized sound files.
+ * If 0, use the historical layout with prefix just before the filename
+ * (i.e. digits/en/1.gsm , digits/it/1.gsm or default to digits/1.gsm),
+ * if 1 put the prefix at the beginning of the filename
+ * (i.e. en/digits/1.gsm, it/digits/1.gsm or default to digits/1.gsm).
+ * The latter permits a language to be entirely in one directory.
+ */
+int ast_language_is_prefix = 1;
+
+static AST_RWLIST_HEAD_STATIC(formats, ast_format);
+
+int __ast_format_register(const struct ast_format *f, struct ast_module *mod)
+{
+ struct ast_format *tmp;
+
+ AST_RWLIST_WRLOCK(&formats);
+ AST_RWLIST_TRAVERSE(&formats, tmp, list) {
+ if (!strcasecmp(f->name, tmp->name)) {
+ AST_RWLIST_UNLOCK(&formats);
+ ast_log(LOG_WARNING, "Tried to register '%s' format, already registered\n", f->name);
+ return -1;
+ }
+ }
+ if (!(tmp = ast_calloc(1, sizeof(*tmp)))) {
+ AST_RWLIST_UNLOCK(&formats);
+ return -1;
+ }
+ *tmp = *f;
+ tmp->module = mod;
+ if (tmp->buf_size) {
+ /*
+ * Align buf_size properly, rounding up to the machine-specific
+ * alignment for pointers.
+ */
+ struct _test_align { void *a, *b; } p;
+ int align = (char *)&p.b - (char *)&p.a;
+ tmp->buf_size = ((f->buf_size + align - 1)/align)*align;
+ }
+
+ memset(&tmp->list, 0, sizeof(tmp->list));
+
+ AST_RWLIST_INSERT_HEAD(&formats, tmp, list);
+ AST_RWLIST_UNLOCK(&formats);
+ ast_verb(2, "Registered file format %s, extension(s) %s\n", f->name, f->exts);
+
+ return 0;
+}
+
+int ast_format_unregister(const char *name)
+{
+ struct ast_format *tmp;
+ int res = -1;
+
+ AST_RWLIST_WRLOCK(&formats);
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&formats, tmp, list) {
+ if (!strcasecmp(name, tmp->name)) {
+ AST_RWLIST_REMOVE_CURRENT(list);
+ ast_free(tmp);
+ res = 0;
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+ AST_RWLIST_UNLOCK(&formats);
+
+ if (!res)
+ ast_verb(2, "Unregistered format %s\n", name);
+ else
+ ast_log(LOG_WARNING, "Tried to unregister format %s, already unregistered\n", name);
+
+ return res;
+}
+
+int ast_stopstream(struct ast_channel *tmp)
+{
+ ast_channel_lock(tmp);
+
+ /* Stop a running stream if there is one */
+ if (tmp->stream) {
+ ast_closestream(tmp->stream);
+ tmp->stream = NULL;
+ if (tmp->oldwriteformat && ast_set_write_format(tmp, tmp->oldwriteformat))
+ ast_log(LOG_WARNING, "Unable to restore format back to %d\n", tmp->oldwriteformat);
+ }
+ /* Stop the video stream too */
+ if (tmp->vstream != NULL) {
+ ast_closestream(tmp->vstream);
+ tmp->vstream = NULL;
+ }
+
+ ast_channel_unlock(tmp);
+
+ return 0;
+}
+
+int ast_writestream(struct ast_filestream *fs, struct ast_frame *f)
+{
+ int res = -1;
+ int alt = 0;
+ if (f->frametype == AST_FRAME_VIDEO) {
+ if (fs->fmt->format & AST_FORMAT_AUDIO_MASK) {
+ /* This is the audio portion. Call the video one... */
+ if (!fs->vfs && fs->filename) {
+ const char *type = ast_getformatname(f->subclass & ~0x1);
+ fs->vfs = ast_writefile(fs->filename, type, NULL, fs->flags, 0, fs->mode);
+ ast_debug(1, "Opened video output file\n");
+ }
+ if (fs->vfs)
+ return ast_writestream(fs->vfs, f);
+ /* else ignore */
+ return 0;
+ } else {
+ /* Might / might not have mark set */
+ alt = 1;
+ }
+ } else if (f->frametype != AST_FRAME_VOICE) {
+ ast_log(LOG_WARNING, "Tried to write non-voice frame\n");
+ return -1;
+ }
+ if (((fs->fmt->format | alt) & f->subclass) == f->subclass) {
+ res = fs->fmt->write(fs, f);
+ if (res < 0)
+ ast_log(LOG_WARNING, "Natural write failed\n");
+ else if (res > 0)
+ ast_log(LOG_WARNING, "Huh??\n");
+ } else {
+ /* XXX If they try to send us a type of frame that isn't the normal frame, and isn't
+ the one we've setup a translator for, we do the "wrong thing" XXX */
+ if (fs->trans && f->subclass != fs->lastwriteformat) {
+ ast_translator_free_path(fs->trans);
+ fs->trans = NULL;
+ }
+ if (!fs->trans)
+ fs->trans = ast_translator_build_path(fs->fmt->format, f->subclass);
+ if (!fs->trans)
+ ast_log(LOG_WARNING, "Unable to translate to format %s, source format %s\n",
+ fs->fmt->name, ast_getformatname(f->subclass));
+ else {
+ struct ast_frame *trf;
+ fs->lastwriteformat = f->subclass;
+ /* Get the translated frame but don't consume the original in case they're using it on another stream */
+ trf = ast_translate(fs->trans, f, 0);
+ if (trf) {
+ res = fs->fmt->write(fs, trf);
+ if (res)
+ ast_log(LOG_WARNING, "Translated frame write failed\n");
+ } else
+ res = 0;
+ }
+ }
+ return res;
+}
+
+static int copy(const char *infile, const char *outfile)
+{
+ int ifd, ofd, len;
+ char buf[4096]; /* XXX make it lerger. */
+
+ if ((ifd = open(infile, O_RDONLY)) < 0) {
+ ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
+ return -1;
+ }
+ if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, AST_FILE_MODE)) < 0) {
+ ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
+ close(ifd);
+ return -1;
+ }
+ while ( (len = read(ifd, buf, sizeof(buf)) ) ) {
+ int res;
+ if (len < 0) {
+ ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
+ break;
+ }
+ /* XXX handle partial writes */
+ res = write(ofd, buf, len);
+ if (res != len) {
+ ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
+ len = -1; /* error marker */
+ break;
+ }
+ }
+ close(ifd);
+ close(ofd);
+ if (len < 0) {
+ unlink(outfile);
+ return -1; /* error */
+ }
+ return 0; /* success */
+}
+
+/*!
+ * \brief construct a filename. Absolute pathnames are preserved,
+ * relative names are prefixed by the sounds/ directory.
+ * The wav49 suffix is replaced by 'WAV'.
+ * Returns a malloc'ed string to be freed by the caller.
+ */
+static char *build_filename(const char *filename, const char *ext)
+{
+ char *fn = NULL;
+
+ if (!strcmp(ext, "wav49"))
+ ext = "WAV";
+
+ if (filename[0] == '/')
+ asprintf(&fn, "%s.%s", filename, ext);
+ else
+ asprintf(&fn, "%s/sounds/%s.%s",
+ ast_config_AST_DATA_DIR, filename, ext);
+ return fn;
+}
+
+/* compare type against the list 'exts' */
+/* XXX need a better algorithm */
+static int exts_compare(const char *exts, const char *type)
+{
+ char tmp[256];
+ char *stringp = tmp, *ext;
+
+ ast_copy_string(tmp, exts, sizeof(tmp));
+ while ((ext = strsep(&stringp, "|"))) {
+ if (!strcmp(ext, type))
+ return 1;
+ }
+
+ return 0;
+}
+
+static struct ast_filestream *get_filestream(struct ast_format *fmt, FILE *bfile)
+{
+ struct ast_filestream *s;
+
+ int l = sizeof(*s) + fmt->buf_size + fmt->desc_size; /* total allocation size */
+ if ( (s = ast_calloc(1, l)) == NULL)
+ return NULL;
+ s->fmt = fmt;
+ s->f = bfile;
+
+ if (fmt->desc_size)
+ s->_private = ((char *)(s+1)) + fmt->buf_size;
+ if (fmt->buf_size)
+ s->buf = (char *)(s+1);
+ s->fr.src = fmt->name;
+ return s;
+}
+
+/*
+ * Default implementations of open and rewrite.
+ * Only use them if you don't have expensive stuff to do.
+ */
+enum wrap_fn { WRAP_OPEN, WRAP_REWRITE };
+
+static int fn_wrapper(struct ast_filestream *s, const char *comment, enum wrap_fn mode)
+{
+ struct ast_format *f = s->fmt;
+ int ret = -1;
+
+ if (mode == WRAP_OPEN && f->open && f->open(s))
+ ast_log(LOG_WARNING, "Unable to open format %s\n", f->name);
+ else if (mode == WRAP_REWRITE && f->rewrite && f->rewrite(s, comment))
+ ast_log(LOG_WARNING, "Unable to rewrite format %s\n", f->name);
+ else {
+ /* preliminary checks succeed. update usecount */
+ ast_module_ref(f->module);
+ ret = 0;
+ }
+ return ret;
+}
+
+static int rewrite_wrapper(struct ast_filestream *s, const char *comment)
+{
+ return fn_wrapper(s, comment, WRAP_REWRITE);
+}
+
+static int open_wrapper(struct ast_filestream *s)
+{
+ return fn_wrapper(s, NULL, WRAP_OPEN);
+}
+
+enum file_action {
+ ACTION_EXISTS = 1, /* return matching format if file exists, 0 otherwise */
+ ACTION_DELETE, /* delete file, return 0 on success, -1 on error */
+ ACTION_RENAME, /* rename file. return 0 on success, -1 on error */
+ ACTION_OPEN,
+ ACTION_COPY /* copy file. return 0 on success, -1 on error */
+};
+
+/*!
+ * \brief perform various actions on a file. Second argument
+ * arg2 depends on the command:
+ * unused for EXISTS and DELETE
+ * destination file name (const char *) for COPY and RENAME
+ * struct ast_channel * for OPEN
+ * if fmt is NULL, OPEN will return the first matching entry,
+ * whereas other functions will run on all matching entries.
+ */
+static int ast_filehelper(const char *filename, const void *arg2, const char *fmt, const enum file_action action)
+{
+ struct ast_format *f;
+ int res = (action == ACTION_EXISTS) ? 0 : -1;
+
+ AST_RWLIST_RDLOCK(&formats);
+ /* Check for a specific format */
+ AST_RWLIST_TRAVERSE(&formats, f, list) {
+ char *stringp, *ext = NULL;
+
+ if (fmt && !exts_compare(f->exts, fmt))
+ continue;
+
+ /* Look for a file matching the supported extensions.
+ * The file must exist, and for OPEN, must match
+ * one of the formats supported by the channel.
+ */
+ stringp = ast_strdupa(f->exts); /* this is in the stack so does not need to be freed */
+ while ( (ext = strsep(&stringp, "|")) ) {
+ struct stat st;
+ char *fn = build_filename(filename, ext);
+
+ if (fn == NULL)
+ continue;
+
+ if ( stat(fn, &st) ) { /* file not existent */
+ ast_free(fn);
+ continue;
+ }
+ /* for 'OPEN' we need to be sure that the format matches
+ * what the channel can process
+ */
+ if (action == ACTION_OPEN) {
+ struct ast_channel *chan = (struct ast_channel *)arg2;
+ FILE *bfile;
+ struct ast_filestream *s;
+
+ if ( !(chan->writeformat & f->format) &&
+ !(f->format & AST_FORMAT_AUDIO_MASK && fmt)) {
+ ast_free(fn);
+ continue; /* not a supported format */
+ }
+ if ( (bfile = fopen(fn, "r")) == NULL) {
+ ast_free(fn);
+ continue; /* cannot open file */
+ }
+ s = get_filestream(f, bfile);
+ if (!s) {
+ fclose(bfile);
+ ast_free(fn); /* cannot allocate descriptor */
+ continue;
+ }
+ if (open_wrapper(s)) {
+ fclose(bfile);
+ ast_free(fn);
+ ast_free(s);
+ continue; /* cannot run open on file */
+ }
+ /* ok this is good for OPEN */
+ res = 1; /* found */
+ s->lasttimeout = -1;
+ s->fmt = f;
+ s->trans = NULL;
+ s->filename = NULL;
+ if (s->fmt->format & AST_FORMAT_AUDIO_MASK) {
+ if (chan->stream)
+ ast_closestream(chan->stream);
+ chan->stream = s;
+ } else {
+ if (chan->vstream)
+ ast_closestream(chan->vstream);
+ chan->vstream = s;
+ }
+ ast_free(fn);
+ break;
+ }
+ switch (action) {
+ case ACTION_OPEN:
+ break; /* will never get here */
+
+ case ACTION_EXISTS: /* return the matching format */
+ res |= f->format;
+ break;
+
+ case ACTION_DELETE:
+ if ( (res = unlink(fn)) )
+ ast_log(LOG_WARNING, "unlink(%s) failed: %s\n", fn, strerror(errno));
+ break;
+
+ case ACTION_RENAME:
+ case ACTION_COPY: {
+ char *nfn = build_filename((const char *)arg2, ext);
+ if (!nfn)
+ ast_log(LOG_WARNING, "Out of memory\n");
+ else {
+ res = action == ACTION_COPY ? copy(fn, nfn) : rename(fn, nfn);
+ if (res)
+ ast_log(LOG_WARNING, "%s(%s,%s) failed: %s\n",
+ action == ACTION_COPY ? "copy" : "rename",
+ fn, nfn, strerror(errno));
+ ast_free(nfn);
+ }
+ }
+ break;
+
+ default:
+ ast_log(LOG_WARNING, "Unknown helper %d\n", action);
+ }
+ ast_free(fn);
+ }
+ }
+ AST_RWLIST_UNLOCK(&formats);
+ return res;
+}
+
+/*!
+ * \brief helper routine to locate a file with a given format
+ * and language preference.
+ * Try preflang, preflang with stripped '_' suffix, or NULL.
+ * In the standard asterisk, language goes just before the last component.
+ * In an alternative configuration, the language should be a prefix
+ * to the actual filename.
+ *
+ * The last parameter(s) point to a buffer of sufficient size,
+ * which on success is filled with the matching filename.
+ */
+static int fileexists_core(const char *filename, const char *fmt, const char *preflang,
+ char *buf, int buflen)
+{
+ int res = -1;
+ int langlen; /* length of language string */
+ const char *c = strrchr(filename, '/');
+ int offset = c ? c - filename + 1 : 0; /* points right after the last '/' */
+
+ if (preflang == NULL)
+ preflang = "";
+ langlen = strlen(preflang);
+
+ if (buflen < langlen + strlen(filename) + 2) {
+ ast_log(LOG_WARNING, "buffer too small\n");
+ buf[0] = '\0'; /* set to empty */
+ buf = alloca(langlen + strlen(filename) + 2); /* room for everything */
+ }
+ if (buf == NULL)
+ return 0;
+ buf[0] = '\0';
+ for (;;) {
+ if (ast_language_is_prefix) { /* new layout */
+ if (langlen) {
+ strcpy(buf, preflang);
+ buf[langlen] = '/';
+ strcpy(buf + langlen + 1, filename);
+ } else
+ strcpy(buf, filename); /* first copy the full string */
+ } else { /* old layout */
+ strcpy(buf, filename); /* first copy the full string */
+ if (langlen) {
+ /* insert the language and suffix if needed */
+ strcpy(buf + offset, preflang);
+ sprintf(buf + offset + langlen, "/%s", filename + offset);
+ }
+ }
+ res = ast_filehelper(buf, NULL, fmt, ACTION_EXISTS);
+ if (res > 0) /* found format */
+ break;
+ if (langlen == 0) /* no more formats */
+ break;
+ if (preflang[langlen] == '_') /* we are on the local suffix */
+ langlen = 0; /* try again with no language */
+ else
+ langlen = (c = strchr(preflang, '_')) ? c - preflang : 0;
+ }
+ return res;
+}
+
+struct ast_filestream *ast_openstream(struct ast_channel *chan, const char *filename, const char *preflang)
+{
+ return ast_openstream_full(chan, filename, preflang, 0);
+}
+
+struct ast_filestream *ast_openstream_full(struct ast_channel *chan, const char *filename, const char *preflang, int asis)
+{
+ /*
+ * Use fileexists_core() to find a file in a compatible
+ * language and format, set up a suitable translator,
+ * and open the stream.
+ */
+ int fmts, res, buflen;
+ char *buf;
+
+ if (!asis) {
+ /* do this first, otherwise we detect the wrong writeformat */
+ ast_stopstream(chan);
+ if (chan->generator)
+ ast_deactivate_generator(chan);
+ }
+ if (preflang == NULL)
+ preflang = "";
+ buflen = strlen(preflang) + strlen(filename) + 2;
+ buf = alloca(buflen);
+ if (buf == NULL)
+ return NULL;
+ fmts = fileexists_core(filename, NULL, preflang, buf, buflen);
+ if (fmts > 0)
+ fmts &= AST_FORMAT_AUDIO_MASK;
+ if (fmts < 1) {
+ ast_log(LOG_WARNING, "File %s does not exist in any format\n", filename);
+ return NULL;
+ }
+ chan->oldwriteformat = chan->writeformat;
+ /* Set the channel to a format we can work with */
+ res = ast_set_write_format(chan, fmts);
+ res = ast_filehelper(buf, chan, NULL, ACTION_OPEN);
+ if (res >= 0)
+ return chan->stream;
+ return NULL;
+}
+
+struct ast_filestream *ast_openvstream(struct ast_channel *chan, const char *filename, const char *preflang)
+{
+ /* As above, but for video. But here we don't have translators
+ * so we must enforce a format.
+ */
+ unsigned int format;
+ char *buf;
+ int buflen;
+
+ if (preflang == NULL)
+ preflang = "";
+ buflen = strlen(preflang) + strlen(filename) + 2;
+ buf = alloca(buflen);
+ if (buf == NULL)
+ return NULL;
+
+ for (format = AST_FORMAT_AUDIO_MASK + 1; format <= AST_FORMAT_VIDEO_MASK; format = format << 1) {
+ int fd;
+ const char *fmt;
+
+ if (!(chan->nativeformats & format))
+ continue;
+ fmt = ast_getformatname(format);
+ if ( fileexists_core(filename, fmt, preflang, buf, buflen) < 1) /* no valid format */
+ continue;
+ fd = ast_filehelper(buf, chan, fmt, ACTION_OPEN);
+ if (fd >= 0)
+ return chan->vstream;
+ ast_log(LOG_WARNING, "File %s has video but couldn't be opened\n", filename);
+ }
+ return NULL;
+}
+
+struct ast_frame *ast_readframe(struct ast_filestream *s)
+{
+ struct ast_frame *f = NULL;
+ int whennext = 0;
+ if (s && s->fmt)
+ f = s->fmt->read(s, &whennext);
+ return f;
+}
+
+enum fsread_res {
+ FSREAD_FAILURE,
+ FSREAD_SUCCESS_SCHED,
+ FSREAD_SUCCESS_NOSCHED,
+};
+
+static int ast_fsread_audio(const void *data);
+
+static enum fsread_res ast_readaudio_callback(struct ast_filestream *s)
+{
+ int whennext = 0;
+
+ while (!whennext) {
+ struct ast_frame *fr;
+
+ if (s->orig_chan_name && strcasecmp(s->owner->name, s->orig_chan_name))
+ goto return_failure;
+
+ fr = s->fmt->read(s, &whennext);
+ if (!fr /* stream complete */ || ast_write(s->owner, fr) /* error writing */) {
+ if (fr)
+ ast_log(LOG_WARNING, "Failed to write frame\n");
+ goto return_failure;
+ }
+ }
+ if (whennext != s->lasttimeout) {
+#ifdef HAVE_ZAPTEL
+ if (s->owner->timingfd > -1)
+ ast_settimeout(s->owner, whennext, ast_fsread_audio, s);
+ else
+#endif
+ s->owner->streamid = ast_sched_add(s->owner->sched, whennext/8, ast_fsread_audio, s);
+ s->lasttimeout = whennext;
+ return FSREAD_SUCCESS_NOSCHED;
+ }
+ return FSREAD_SUCCESS_SCHED;
+
+return_failure:
+ s->owner->streamid = -1;
+#ifdef HAVE_ZAPTEL
+ ast_settimeout(s->owner, 0, NULL, NULL);
+#endif
+ return FSREAD_FAILURE;
+}
+
+static int ast_fsread_audio(const void *data)
+{
+ struct ast_filestream *fs = (struct ast_filestream *)data;
+ enum fsread_res res;
+
+ res = ast_readaudio_callback(fs);
+
+ if (res == FSREAD_SUCCESS_SCHED)
+ return 1;
+
+ return 0;
+}
+
+static int ast_fsread_video(const void *data);
+
+static enum fsread_res ast_readvideo_callback(struct ast_filestream *s)
+{
+ int whennext = 0;
+
+ while (!whennext) {
+ struct ast_frame *fr = s->fmt->read(s, &whennext);
+ if (!fr || ast_write(s->owner, fr)) { /* no stream or error, as above */
+ if (fr)
+ ast_log(LOG_WARNING, "Failed to write frame\n");
+ s->owner->vstreamid = -1;
+ return FSREAD_FAILURE;
+ }
+ }
+
+ if (whennext != s->lasttimeout) {
+ s->owner->vstreamid = ast_sched_add(s->owner->sched, whennext / 8,
+ ast_fsread_video, s);
+ s->lasttimeout = whennext;
+ return FSREAD_SUCCESS_NOSCHED;
+ }
+
+ return FSREAD_SUCCESS_SCHED;
+}
+
+static int ast_fsread_video(const void *data)
+{
+ struct ast_filestream *fs = (struct ast_filestream *)data;
+ enum fsread_res res;
+
+ res = ast_readvideo_callback(fs);
+
+ if (res == FSREAD_SUCCESS_SCHED)
+ return 1;
+
+ return 0;
+}
+
+int ast_applystream(struct ast_channel *chan, struct ast_filestream *s)
+{
+ s->owner = chan;
+ return 0;
+}
+
+int ast_playstream(struct ast_filestream *s)
+{
+ enum fsread_res res;
+
+ if (s->fmt->format & AST_FORMAT_AUDIO_MASK)
+ res = ast_readaudio_callback(s);
+ else
+ res = ast_readvideo_callback(s);
+
+ return (res == FSREAD_FAILURE) ? -1 : 0;
+}
+
+int ast_seekstream(struct ast_filestream *fs, off_t sample_offset, int whence)
+{
+ return fs->fmt->seek(fs, sample_offset, whence);
+}
+
+int ast_truncstream(struct ast_filestream *fs)
+{
+ return fs->fmt->trunc(fs);
+}
+
+off_t ast_tellstream(struct ast_filestream *fs)
+{
+ return fs->fmt->tell(fs);
+}
+
+int ast_stream_fastforward(struct ast_filestream *fs, off_t ms)
+{
+ return ast_seekstream(fs, ms * DEFAULT_SAMPLES_PER_MS, SEEK_CUR);
+}
+
+int ast_stream_rewind(struct ast_filestream *fs, off_t ms)
+{
+ return ast_seekstream(fs, -ms * DEFAULT_SAMPLES_PER_MS, SEEK_CUR);
+}
+
+int ast_closestream(struct ast_filestream *f)
+{
+ char *cmd = NULL;
+ size_t size = 0;
+ /* Stop a running stream if there is one */
+ if (f->owner) {
+ if (f->fmt->format & AST_FORMAT_AUDIO_MASK) {
+ f->owner->stream = NULL;
+ if (f->owner->streamid > -1)
+ ast_sched_del(f->owner->sched, f->owner->streamid);
+ f->owner->streamid = -1;
+#ifdef HAVE_ZAPTEL
+ ast_settimeout(f->owner, 0, NULL, NULL);
+#endif
+ } else {
+ f->owner->vstream = NULL;
+ if (f->owner->vstreamid > -1)
+ ast_sched_del(f->owner->sched, f->owner->vstreamid);
+ f->owner->vstreamid = -1;
+ }
+ }
+ /* destroy the translator on exit */
+ if (f->trans)
+ ast_translator_free_path(f->trans);
+
+ if (f->realfilename && f->filename) {
+ size = strlen(f->filename) + strlen(f->realfilename) + 15;
+ cmd = alloca(size);
+ memset(cmd,0,size);
+ snprintf(cmd,size,"/bin/mv -f %s %s",f->filename,f->realfilename);
+ ast_safe_system(cmd);
+ }
+
+ if (f->filename)
+ ast_free(f->filename);
+ if (f->realfilename)
+ ast_free(f->realfilename);
+ if (f->fmt->close)
+ f->fmt->close(f);
+ fclose(f->f);
+ if (f->vfs)
+ ast_closestream(f->vfs);
+ if (f->orig_chan_name)
+ free((void *) f->orig_chan_name);
+ ast_module_unref(f->fmt->module);
+ ast_free(f);
+ return 0;
+}
+
+
+/*
+ * Look the various language-specific places where a file could exist.
+ */
+int ast_fileexists(const char *filename, const char *fmt, const char *preflang)
+{
+ char *buf;
+ int buflen;
+
+ if (preflang == NULL)
+ preflang = "";
+ buflen = strlen(preflang) + strlen(filename) + 2; /* room for everything */
+ buf = alloca(buflen);
+ if (buf == NULL)
+ return 0;
+ return fileexists_core(filename, fmt, preflang, buf, buflen);
+}
+
+int ast_filedelete(const char *filename, const char *fmt)
+{
+ return ast_filehelper(filename, NULL, fmt, ACTION_DELETE);
+}
+
+int ast_filerename(const char *filename, const char *filename2, const char *fmt)
+{
+ return ast_filehelper(filename, filename2, fmt, ACTION_RENAME);
+}
+
+int ast_filecopy(const char *filename, const char *filename2, const char *fmt)
+{
+ return ast_filehelper(filename, filename2, fmt, ACTION_COPY);
+}
+
+int ast_streamfile(struct ast_channel *chan, const char *filename, const char *preflang)
+{
+ struct ast_filestream *fs;
+ struct ast_filestream *vfs=NULL;
+ char fmt[256];
+
+ fs = ast_openstream(chan, filename, preflang);
+ if (fs)
+ vfs = ast_openvstream(chan, filename, preflang);
+ if (vfs) {
+ ast_debug(1, "Ooh, found a video stream, too, format %s\n", ast_getformatname(vfs->fmt->format));
+ }
+ if (fs){
+ int res;
+ if (ast_test_flag(chan, AST_FLAG_MASQ_NOSTREAM))
+ fs->orig_chan_name = ast_strdup(chan->name);
+ if (ast_applystream(chan, fs))
+ return -1;
+ if (vfs && ast_applystream(chan, vfs))
+ return -1;
+ res = ast_playstream(fs);
+ if (!res && vfs)
+ res = ast_playstream(vfs);
+ ast_verb(3, "<%s> Playing '%s.%s' (language '%s')\n", chan->name, filename, ast_getformatname(chan->writeformat), preflang ? preflang : "default");
+
+ return res;
+ }
+ ast_log(LOG_WARNING, "Unable to open %s (format %s): %s\n", filename, ast_getformatname_multiple(fmt, sizeof(fmt), chan->nativeformats), strerror(errno));
+ return -1;
+}
+
+struct ast_filestream *ast_readfile(const char *filename, const char *type, const char *comment, int flags, int check, mode_t mode)
+{
+ FILE *bfile;
+ struct ast_format *f;
+ struct ast_filestream *fs = NULL;
+ char *fn;
+
+ AST_RWLIST_RDLOCK(&formats);
+
+ AST_RWLIST_TRAVERSE(&formats, f, list) {
+ fs = NULL;
+ if (!exts_compare(f->exts, type))
+ continue;
+
+ fn = build_filename(filename, type);
+ errno = 0;
+ bfile = fopen(fn, "r");
+ if (!bfile || (fs = get_filestream(f, bfile)) == NULL ||
+ open_wrapper(fs) ) {
+ ast_log(LOG_WARNING, "Unable to open %s\n", fn);
+ if (fs)
+ ast_free(fs);
+ if (bfile)
+ fclose(bfile);
+ ast_free(fn);
+ continue;
+ }
+ /* found it */
+ fs->trans = NULL;
+ fs->fmt = f;
+ fs->flags = flags;
+ fs->mode = mode;
+ fs->filename = ast_strdup(filename);
+ fs->vfs = NULL;
+ break;
+ }
+
+ AST_RWLIST_UNLOCK(&formats);
+ if (!fs)
+ ast_log(LOG_WARNING, "No such format '%s'\n", type);
+
+ return fs;
+}
+
+struct ast_filestream *ast_writefile(const char *filename, const char *type, const char *comment, int flags, int check, mode_t mode)
+{
+ int fd, myflags = 0;
+ /* compiler claims this variable can be used before initialization... */
+ FILE *bfile = NULL;
+ struct ast_format *f;
+ struct ast_filestream *fs = NULL;
+ char *buf = NULL;
+ size_t size = 0;
+ int format_found = 0;
+
+ AST_RWLIST_RDLOCK(&formats);
+
+ /* set the O_TRUNC flag if and only if there is no O_APPEND specified */
+ /* We really can't use O_APPEND as it will break WAV header updates */
+ if (flags & O_APPEND) {
+ flags &= ~O_APPEND;
+ } else {
+ myflags = O_TRUNC;
+ }
+
+ myflags |= O_WRONLY | O_CREAT;
+
+ /* XXX need to fix this - we should just do the fopen,
+ * not open followed by fdopen()
+ */
+ AST_RWLIST_TRAVERSE(&formats, f, list) {
+ char *fn, *orig_fn = NULL;
+ if (fs)
+ break;
+
+ if (!exts_compare(f->exts, type))
+ continue;
+ else
+ format_found = 1;
+
+ fn = build_filename(filename, type);
+ fd = open(fn, flags | myflags, mode);
+ if (fd > -1) {
+ /* fdopen() the resulting file stream */
+ bfile = fdopen(fd, ((flags | myflags) & O_RDWR) ? "w+" : "w");
+ if (!bfile) {
+ ast_log(LOG_WARNING, "Whoa, fdopen failed: %s!\n", strerror(errno));
+ close(fd);
+ fd = -1;
+ }
+ }
+
+ if (ast_opt_cache_record_files && (fd > -1)) {
+ char *c;
+
+ fclose(bfile); /* this also closes fd */
+ /*
+ We touch orig_fn just as a place-holder so other things (like vmail) see the file is there.
+ What we are really doing is writing to record_cache_dir until we are done then we will mv the file into place.
+ */
+ orig_fn = ast_strdupa(fn);
+ for (c = fn; *c; c++)
+ if (*c == '/')
+ *c = '_';
+
+ size = strlen(fn) + strlen(record_cache_dir) + 2;
+ buf = alloca(size);
+ strcpy(buf, record_cache_dir);
+ strcat(buf, "/");
+ strcat(buf, fn);
+ ast_free(fn);
+ fn = buf;
+ fd = open(fn, flags | myflags, mode);
+ if (fd > -1) {
+ /* fdopen() the resulting file stream */
+ bfile = fdopen(fd, ((flags | myflags) & O_RDWR) ? "w+" : "w");
+ if (!bfile) {
+ ast_log(LOG_WARNING, "Whoa, fdopen failed: %s!\n", strerror(errno));
+ close(fd);
+ fd = -1;
+ }
+ }
+ }
+ if (fd > -1) {
+ errno = 0;
+ fs = get_filestream(f, bfile);
+ if (!fs || rewrite_wrapper(fs, comment)) {
+ ast_log(LOG_WARNING, "Unable to rewrite %s\n", fn);
+ close(fd);
+ if (orig_fn) {
+ unlink(fn);
+ unlink(orig_fn);
+ }
+ if (fs)
+ ast_free(fs);
+ fs = NULL;
+ continue;
+ }
+ fs->trans = NULL;
+ fs->fmt = f;
+ fs->flags = flags;
+ fs->mode = mode;
+ if (orig_fn) {
+ fs->realfilename = ast_strdup(orig_fn);
+ fs->filename = ast_strdup(fn);
+ } else {
+ fs->realfilename = NULL;
+ fs->filename = ast_strdup(filename);
+ }
+ fs->vfs = NULL;
+ /* If truncated, we'll be at the beginning; if not truncated, then append */
+ f->seek(fs, 0, SEEK_END);
+ } else if (errno != EEXIST) {
+ ast_log(LOG_WARNING, "Unable to open file %s: %s\n", fn, strerror(errno));
+ if (orig_fn)
+ unlink(orig_fn);
+ }
+ /* if buf != NULL then fn is already free and pointing to it */
+ if (!buf)
+ ast_free(fn);
+ }
+
+ AST_RWLIST_UNLOCK(&formats);
+
+ if (!format_found)
+ ast_log(LOG_WARNING, "No such format '%s'\n", type);
+
+ return fs;
+}
+
+/*!
+ * \brief the core of all waitstream() functions
+ */
+static int waitstream_core(struct ast_channel *c, const char *breakon,
+ const char *forward, const char *rewind, int skip_ms,
+ int audiofd, int cmdfd, const char *context)
+{
+ const char *orig_chan_name = NULL;
+ int err = 0;
+
+ if (!breakon)
+ breakon = "";
+ if (!forward)
+ forward = "";
+ if (!rewind)
+ rewind = "";
+
+ /* Switch the channel to end DTMF frame only. waitstream_core doesn't care about the start of DTMF. */
+ ast_set_flag(c, AST_FLAG_END_DTMF_ONLY);
+
+ if (ast_test_flag(c, AST_FLAG_MASQ_NOSTREAM))
+ orig_chan_name = ast_strdupa(c->name);
+
+ while (c->stream) {
+ int res;
+ int ms;
+
+ if (orig_chan_name && strcasecmp(orig_chan_name, c->name)) {
+ ast_stopstream(c);
+ err = 1;
+ break;
+ }
+
+ ms = ast_sched_wait(c->sched);
+
+ if (ms < 0 && !c->timingfunc) {
+ ast_stopstream(c);
+ break;
+ }
+ if (ms < 0)
+ ms = 1000;
+ if (cmdfd < 0) {
+ res = ast_waitfor(c, ms);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Select failed (%s)\n", strerror(errno));
+ ast_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
+ return res;
+ }
+ } else {
+ int outfd;
+ struct ast_channel *rchan = ast_waitfor_nandfds(&c, 1, &cmdfd, (cmdfd > -1) ? 1 : 0, NULL, &outfd, &ms);
+ if (!rchan && (outfd < 0) && (ms)) {
+ /* Continue */
+ if (errno == EINTR)
+ continue;
+ ast_log(LOG_WARNING, "Wait failed (%s)\n", strerror(errno));
+ ast_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
+ return -1;
+ } else if (outfd > -1) { /* this requires cmdfd set */
+ /* The FD we were watching has something waiting */
+ ast_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
+ return 1;
+ }
+ /* if rchan is set, it is 'c' */
+ res = rchan ? 1 : 0; /* map into 'res' values */
+ }
+ if (res > 0) {
+ struct ast_frame *fr = ast_read(c);
+ if (!fr) {
+ ast_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
+ return -1;
+ }
+ switch (fr->frametype) {
+ case AST_FRAME_DTMF_END:
+ if (context) {
+ const char exten[2] = { fr->subclass, '\0' };
+ if (ast_exists_extension(c, context, exten, 1, c->cid.cid_num)) {
+ res = fr->subclass;
+ ast_frfree(fr);
+ ast_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
+ return res;
+ }
+ } else {
+ res = fr->subclass;
+ if (strchr(forward,res)) {
+ ast_stream_fastforward(c->stream, skip_ms);
+ } else if (strchr(rewind,res)) {
+ ast_stream_rewind(c->stream, skip_ms);
+ } else if (strchr(breakon, res)) {
+ ast_frfree(fr);
+ ast_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
+ return res;
+ }
+ }
+ break;
+ case AST_FRAME_CONTROL:
+ switch (fr->subclass) {
+ case AST_CONTROL_HANGUP:
+ case AST_CONTROL_BUSY:
+ case AST_CONTROL_CONGESTION:
+ ast_frfree(fr);
+ ast_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
+ return -1;
+ case AST_CONTROL_RINGING:
+ case AST_CONTROL_ANSWER:
+ case AST_CONTROL_VIDUPDATE:
+ case AST_CONTROL_HOLD:
+ case AST_CONTROL_UNHOLD:
+ /* Unimportant */
+ break;
+ default:
+ ast_log(LOG_WARNING, "Unexpected control subclass '%d'\n", fr->subclass);
+ }
+ break;
+ case AST_FRAME_VOICE:
+ /* Write audio if appropriate */
+ if (audiofd > -1)
+ write(audiofd, fr->data, fr->datalen);
+ default:
+ /* Ignore all others */
+ break;
+ }
+ ast_frfree(fr);
+ }
+ ast_sched_runq(c->sched);
+ }
+
+ ast_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
+
+ return (err || c->_softhangup) ? -1 : 0;
+}
+
+int ast_waitstream_fr(struct ast_channel *c, const char *breakon, const char *forward, const char *rewind, int ms)
+{
+ return waitstream_core(c, breakon, forward, rewind, ms,
+ -1 /* no audiofd */, -1 /* no cmdfd */, NULL /* no context */);
+}
+
+int ast_waitstream(struct ast_channel *c, const char *breakon)
+{
+ return waitstream_core(c, breakon, NULL, NULL, 0, -1, -1, NULL);
+}
+
+int ast_waitstream_full(struct ast_channel *c, const char *breakon, int audiofd, int cmdfd)
+{
+ return waitstream_core(c, breakon, NULL, NULL, 0,
+ audiofd, cmdfd, NULL /* no context */);
+}
+
+int ast_waitstream_exten(struct ast_channel *c, const char *context)
+{
+ /* Waitstream, with return in the case of a valid 1 digit extension */
+ /* in the current or specified context being pressed */
+
+ if (!context)
+ context = c->context;
+ return waitstream_core(c, NULL, NULL, NULL, 0,
+ -1, -1, context);
+}
+
+/*
+ * if the file name is non-empty, try to play it.
+ * Return 0 if success, -1 if error, digit if interrupted by a digit.
+ * If digits == "" then we can simply check for non-zero.
+ */
+int ast_stream_and_wait(struct ast_channel *chan, const char *file, const char *digits)
+{
+ int res = 0;
+ if (!ast_strlen_zero(file)) {
+ res = ast_streamfile(chan, file, chan->language);
+ if (!res)
+ res = ast_waitstream(chan, digits);
+ }
+ return res;
+}
+
+static char *handle_cli_core_show_file_formats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+#define FORMAT "%-10s %-10s %-20s\n"
+#define FORMAT2 "%-10s %-10s %-20s\n"
+ struct ast_format *f;
+ int count_fmt = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show file formats";
+ e->usage =
+ "Usage: core show file formats\n"
+ " Displays currently registered file formats (if any).\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+
+ ast_cli(a->fd, FORMAT, "Format", "Name", "Extensions");
+ ast_cli(a->fd, FORMAT, "------", "----", "----------");
+
+ AST_RWLIST_RDLOCK(&formats);
+ AST_RWLIST_TRAVERSE(&formats, f, list) {
+ ast_cli(a->fd, FORMAT2, ast_getformatname(f->format), f->name, f->exts);
+ count_fmt++;
+ }
+ AST_RWLIST_UNLOCK(&formats);
+ ast_cli(a->fd, "%d file formats registered.\n", count_fmt);
+ return CLI_SUCCESS;
+#undef FORMAT
+#undef FORMAT2
+}
+
+struct ast_cli_entry cli_file[] = {
+ AST_CLI_DEFINE(handle_cli_core_show_file_formats, "Displays file formats")
+};
+
+int ast_file_init(void)
+{
+ ast_cli_register_multiple(cli_file, sizeof(cli_file) / sizeof(struct ast_cli_entry));
+ return 0;
+}
diff --git a/trunk/main/fixedjitterbuf.c b/trunk/main/fixedjitterbuf.c
new file mode 100644
index 000000000..8a885b8e6
--- /dev/null
+++ b/trunk/main/fixedjitterbuf.c
@@ -0,0 +1,347 @@
+/*
+ * Copyright (C) 2005, Attractel OOD
+ *
+ * Contributors:
+ * Slav Klenov <slav@securax.org>
+ *
+ * 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.
+ *
+ * A license has been granted to Digium (via disclaimer) for the use of
+ * this code.
+ */
+
+/*! \file
+ *
+ * \brief Jitterbuffering algorithm.
+ *
+ * \author Slav Klenov <slav@securax.org>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <assert.h>
+
+#include "asterisk/utils.h"
+#include "fixedjitterbuf.h"
+
+#undef FIXED_JB_DEBUG
+
+#ifdef FIXED_JB_DEBUG
+#define ASSERT(a)
+#else
+#define ASSERT(a) assert(a)
+#endif
+
+/*! \brief private fixed_jb structure */
+struct fixed_jb
+{
+ struct fixed_jb_frame *frames;
+ struct fixed_jb_frame *tail;
+ struct fixed_jb_conf conf;
+ long rxcore;
+ long delay;
+ long next_delivery;
+ int force_resynch;
+};
+
+
+static struct fixed_jb_frame *alloc_jb_frame(struct fixed_jb *jb);
+static void release_jb_frame(struct fixed_jb *jb, struct fixed_jb_frame *frame);
+static void get_jb_head(struct fixed_jb *jb, struct fixed_jb_frame *frame);
+static int resynch_jb(struct fixed_jb *jb, void *data, long ms, long ts, long now);
+
+static inline struct fixed_jb_frame *alloc_jb_frame(struct fixed_jb *jb)
+{
+ return ast_calloc(1, sizeof(*jb));
+}
+
+static inline void release_jb_frame(struct fixed_jb *jb, struct fixed_jb_frame *frame)
+{
+ ast_free(frame);
+}
+
+static void get_jb_head(struct fixed_jb *jb, struct fixed_jb_frame *frame)
+{
+ struct fixed_jb_frame *fr;
+
+ /* unlink the frame */
+ fr = jb->frames;
+ jb->frames = fr->next;
+ if (jb->frames) {
+ jb->frames->prev = NULL;
+ } else {
+ /* the jb is empty - update tail */
+ jb->tail = NULL;
+ }
+
+ /* update next */
+ jb->next_delivery = fr->delivery + fr->ms;
+
+ /* copy the destination */
+ memcpy(frame, fr, sizeof(struct fixed_jb_frame));
+
+ /* and release the frame */
+ release_jb_frame(jb, fr);
+}
+
+
+struct fixed_jb *fixed_jb_new(struct fixed_jb_conf *conf)
+{
+ struct fixed_jb *jb;
+
+ if (!(jb = ast_calloc(1, sizeof(*jb))))
+ return NULL;
+
+ /* First copy our config */
+ memcpy(&jb->conf, conf, sizeof(struct fixed_jb_conf));
+
+ /* we dont need the passed config anymore - continue working with the saved one */
+ conf = &jb->conf;
+
+ /* validate the configuration */
+ if (conf->jbsize < 1)
+ conf->jbsize = FIXED_JB_SIZE_DEFAULT;
+
+ if (conf->resync_threshold < 1)
+ conf->resync_threshold = FIXED_JB_RESYNCH_THRESHOLD_DEFAULT;
+
+ /* Set the constant delay to the jitterbuf */
+ jb->delay = conf->jbsize;
+
+ return jb;
+}
+
+
+void fixed_jb_destroy(struct fixed_jb *jb)
+{
+ /* jitterbuf MUST be empty before it can be destroyed */
+ ASSERT(jb->frames == NULL);
+
+ ast_free(jb);
+}
+
+
+static int resynch_jb(struct fixed_jb *jb, void *data, long ms, long ts, long now)
+{
+ long diff, offset;
+ struct fixed_jb_frame *frame;
+
+ /* If jb is empty, just reinitialize the jb */
+ if (!jb->frames) {
+ /* debug check: tail should also be NULL */
+ ASSERT(jb->tail == NULL);
+
+ return fixed_jb_put_first(jb, data, ms, ts, now);
+ }
+
+ /* Adjust all jb state just as the new frame is with delivery = the delivery of the last
+ frame (e.g. this one with max delivery) + the length of the last frame. */
+
+ /* Get the diff in timestamps */
+ diff = ts - jb->tail->ts;
+
+ /* Ideally this should be just the length of the last frame. The deviation is the desired
+ offset */
+ offset = diff - jb->tail->ms;
+
+ /* Do we really need to resynch, or this is just a frame for dropping? */
+ if (!jb->force_resynch && (offset < jb->conf.resync_threshold && offset > -jb->conf.resync_threshold))
+ return FIXED_JB_DROP;
+
+ /* Reset the force resynch flag */
+ jb->force_resynch = 0;
+
+ /* apply the offset to the jb state */
+ jb->rxcore -= offset;
+ frame = jb->frames;
+ while (frame) {
+ frame->ts += offset;
+ frame = frame->next;
+ }
+
+ /* now jb_put() should add the frame at a last position */
+ return fixed_jb_put(jb, data, ms, ts, now);
+}
+
+
+void fixed_jb_set_force_resynch(struct fixed_jb *jb)
+{
+ jb->force_resynch = 1;
+}
+
+
+int fixed_jb_put_first(struct fixed_jb *jb, void *data, long ms, long ts, long now)
+{
+ /* this is our first frame - set the base of the receivers time */
+ jb->rxcore = now - ts;
+
+ /* init next for a first time - it should be the time the first frame should be played */
+ jb->next_delivery = now + jb->delay;
+
+ /* put the frame */
+ return fixed_jb_put(jb, data, ms, ts, now);
+}
+
+
+int fixed_jb_put(struct fixed_jb *jb, void *data, long ms, long ts, long now)
+{
+ struct fixed_jb_frame *frame, *next, *newframe;
+ long delivery;
+
+ /* debug check the validity of the input params */
+ ASSERT(data != NULL);
+ /* do not allow frames shorter than 2 ms */
+ ASSERT(ms >= 2);
+ ASSERT(ts >= 0);
+ ASSERT(now >= 0);
+
+ delivery = jb->rxcore + jb->delay + ts;
+
+ /* check if the new frame is not too late */
+ if (delivery < jb->next_delivery) {
+ /* should drop the frame, but let first resynch_jb() check if this is not a jump in ts, or
+ the force resynch flag was not set. */
+ return resynch_jb(jb, data, ms, ts, now);
+ }
+
+ /* what if the delivery time is bigger than next + delay? Seems like a frame for the future.
+ However, allow more resync_threshold ms in advance */
+ if (delivery > jb->next_delivery + jb->delay + jb->conf.resync_threshold) {
+ /* should drop the frame, but let first resynch_jb() check if this is not a jump in ts, or
+ the force resynch flag was not set. */
+ return resynch_jb(jb, data, ms, ts, now);
+ }
+
+ /* find the right place in the frames list, sorted by delivery time */
+ frame = jb->tail;
+ while (frame && frame->delivery > delivery) {
+ frame = frame->prev;
+ }
+
+ /* Check if the new delivery time is not covered already by the chosen frame */
+ if (frame && (frame->delivery == delivery ||
+ delivery < frame->delivery + frame->ms ||
+ (frame->next && delivery + ms > frame->next->delivery)))
+ {
+ /* TODO: Should we check for resynch here? Be careful to do not allow threshold smaller than
+ the size of the jb */
+
+ /* should drop the frame, but let first resynch_jb() check if this is not a jump in ts, or
+ the force resynch flag was not set. */
+ return resynch_jb(jb, data, ms, ts, now);
+ }
+
+ /* Reset the force resynch flag */
+ jb->force_resynch = 0;
+
+ /* Get a new frame */
+ newframe = alloc_jb_frame(jb);
+ newframe->data = data;
+ newframe->ts = ts;
+ newframe->ms = ms;
+ newframe->delivery = delivery;
+
+ /* and insert it right on place */
+ if (frame) {
+ next = frame->next;
+ frame->next = newframe;
+ if (next) {
+ newframe->next = next;
+ next->prev = newframe;
+ } else {
+ /* insert after the last frame - should update tail */
+ jb->tail = newframe;
+ newframe->next = NULL;
+ }
+ newframe->prev = frame;
+
+ return FIXED_JB_OK;
+ } else if (!jb->frames) {
+ /* the frame list is empty or thats just the first frame ever */
+ /* tail should also be NULL is that case */
+ ASSERT(jb->tail == NULL);
+ jb->frames = jb->tail = newframe;
+ newframe->next = NULL;
+ newframe->prev = NULL;
+
+ return FIXED_JB_OK;
+ } else {
+ /* insert on a first position - should update frames head */
+ newframe->next = jb->frames;
+ newframe->prev = NULL;
+ jb->frames->prev = newframe;
+ jb->frames = newframe;
+
+ return FIXED_JB_OK;
+ }
+}
+
+
+int fixed_jb_get(struct fixed_jb *jb, struct fixed_jb_frame *frame, long now, long interpl)
+{
+ ASSERT(now >= 0);
+ ASSERT(interpl >= 2);
+
+ if (now < jb->next_delivery) {
+ /* too early for the next frame */
+ return FIXED_JB_NOFRAME;
+ }
+
+ /* Is the jb empty? */
+ if (!jb->frames) {
+ /* should interpolate a frame */
+ /* update next */
+ jb->next_delivery += interpl;
+
+ return FIXED_JB_INTERP;
+ }
+
+ /* Isn't it too late for the first frame available in the jb? */
+ if (now > jb->frames->delivery + jb->frames->ms) {
+ /* yes - should drop this frame and update next to point the next frame (get_jb_head() does it) */
+ get_jb_head(jb, frame);
+
+ return FIXED_JB_DROP;
+ }
+
+ /* isn't it too early to play the first frame available? */
+ if (now < jb->frames->delivery) {
+ /* yes - should interpolate one frame */
+ /* update next */
+ jb->next_delivery += interpl;
+
+ return FIXED_JB_INTERP;
+ }
+
+ /* we have a frame for playing now (get_jb_head() updates next) */
+ get_jb_head(jb, frame);
+
+ return FIXED_JB_OK;
+}
+
+
+long fixed_jb_next(struct fixed_jb *jb)
+{
+ return jb->next_delivery;
+}
+
+
+int fixed_jb_remove(struct fixed_jb *jb, struct fixed_jb_frame *frameout)
+{
+ if (!jb->frames)
+ return FIXED_JB_NOFRAME;
+
+ get_jb_head(jb, frameout);
+
+ return FIXED_JB_OK;
+}
diff --git a/trunk/main/fixedjitterbuf.h b/trunk/main/fixedjitterbuf.h
new file mode 100644
index 000000000..541e99d2d
--- /dev/null
+++ b/trunk/main/fixedjitterbuf.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2005, Attractel OOD
+ *
+ * Contributors:
+ * Slav Klenov <slav@securax.org>
+ *
+ * Copyright on this file is disclaimed to Digium for inclusion in Asterisk
+ *
+ * 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 Jitterbuffering algorithm.
+ *
+ */
+
+#ifndef _FIXEDJITTERBUF_H_
+#define _FIXEDJITTERBUF_H_
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+
+/* return codes */
+enum {
+ FIXED_JB_OK,
+ FIXED_JB_DROP,
+ FIXED_JB_INTERP,
+ FIXED_JB_NOFRAME
+};
+
+
+/* defaults */
+#define FIXED_JB_SIZE_DEFAULT 200
+#define FIXED_JB_RESYNCH_THRESHOLD_DEFAULT 1000
+
+
+/* jb configuration properties */
+struct fixed_jb_conf
+{
+ long jbsize;
+ long resync_threshold;
+};
+
+
+struct fixed_jb_frame
+{
+ void *data;
+ long ts;
+ long ms;
+ long delivery;
+ struct fixed_jb_frame *next;
+ struct fixed_jb_frame *prev;
+};
+
+
+struct fixed_jb;
+
+
+/* jb interface */
+
+struct fixed_jb * fixed_jb_new(struct fixed_jb_conf *conf);
+
+void fixed_jb_destroy(struct fixed_jb *jb);
+
+int fixed_jb_put_first(struct fixed_jb *jb, void *data, long ms, long ts, long now);
+
+int fixed_jb_put(struct fixed_jb *jb, void *data, long ms, long ts, long now);
+
+int fixed_jb_get(struct fixed_jb *jb, struct fixed_jb_frame *frame, long now, long interpl);
+
+long fixed_jb_next(struct fixed_jb *jb);
+
+int fixed_jb_remove(struct fixed_jb *jb, struct fixed_jb_frame *frameout);
+
+void fixed_jb_set_force_resynch(struct fixed_jb *jb);
+
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* _FIXEDJITTERBUF_H_ */
diff --git a/trunk/main/frame.c b/trunk/main/frame.c
new file mode 100644
index 000000000..3100636c5
--- /dev/null
+++ b/trunk/main/frame.c
@@ -0,0 +1,1513 @@
+/*
+ * 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.
+ */
+
+/*! \file
+ *
+ * \brief Frame and codec manipulation routines
+ *
+ * \author Mark Spencer <markster@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/_private.h"
+#include "asterisk/lock.h"
+#include "asterisk/frame.h"
+#include "asterisk/channel.h"
+#include "asterisk/cli.h"
+#include "asterisk/term.h"
+#include "asterisk/utils.h"
+#include "asterisk/threadstorage.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/translate.h"
+
+#ifdef TRACE_FRAMES
+static int headers;
+static AST_LIST_HEAD_STATIC(headerlist, ast_frame);
+#endif
+
+#if !defined(LOW_MEMORY)
+static void frame_cache_cleanup(void *data);
+
+/*! \brief A per-thread cache of frame headers */
+AST_THREADSTORAGE_CUSTOM(frame_cache, NULL, frame_cache_cleanup);
+
+/*!
+ * \brief Maximum ast_frame cache size
+ *
+ * In most cases where the frame header cache will be useful, the size
+ * of the cache will stay very small. However, it is not always the case that
+ * the same thread that allocates the frame will be the one freeing them, so
+ * sometimes a thread will never have any frames in its cache, or the cache
+ * will never be pulled from. For the latter case, we limit the maximum size.
+ */
+#define FRAME_CACHE_MAX_SIZE 10
+
+/*! \brief This is just so ast_frames, a list head struct for holding a list of
+ * ast_frame structures, is defined. */
+AST_LIST_HEAD_NOLOCK(ast_frames, ast_frame);
+
+struct ast_frame_cache {
+ struct ast_frames list;
+ size_t size;
+};
+#endif
+
+#define SMOOTHER_SIZE 8000
+
+enum frame_type {
+ TYPE_HIGH, /* 0x0 */
+ TYPE_LOW, /* 0x1 */
+ TYPE_SILENCE, /* 0x2 */
+ TYPE_DONTSEND /* 0x3 */
+};
+
+#define TYPE_MASK 0x3
+
+struct ast_smoother {
+ int size;
+ int format;
+ int readdata;
+ int optimizablestream;
+ int flags;
+ float samplesperbyte;
+ struct ast_frame f;
+ struct timeval delivery;
+ char data[SMOOTHER_SIZE];
+ char framedata[SMOOTHER_SIZE + AST_FRIENDLY_OFFSET];
+ struct ast_frame *opt;
+ int len;
+};
+
+/*! \brief Definition of supported media formats (codecs) */
+static struct ast_format_list AST_FORMAT_LIST[] = {
+ { AST_FORMAT_G723_1 , "g723", 8000, "G.723.1", 20, 30, 300, 30, 30 }, /*!< G723.1 */
+ { AST_FORMAT_GSM, "gsm", 8000, "GSM", 33, 20, 300, 20, 20 }, /*!< codec_gsm.c */
+ { AST_FORMAT_ULAW, "ulaw", 8000, "G.711 u-law", 80, 10, 150, 10, 20 }, /*!< codec_ulaw.c */
+ { AST_FORMAT_ALAW, "alaw", 8000, "G.711 A-law", 80, 10, 150, 10, 20 }, /*!< codec_alaw.c */
+ { AST_FORMAT_G726, "g726", 8000, "G.726 RFC3551", 40, 10, 300, 10, 20 }, /*!< codec_g726.c */
+ { AST_FORMAT_ADPCM, "adpcm" , 8000, "ADPCM", 40, 10, 300, 10, 20 }, /*!< codec_adpcm.c */
+ { AST_FORMAT_SLINEAR, "slin", 8000, "16 bit Signed Linear PCM", 160, 10, 70, 10, 20, AST_SMOOTHER_FLAG_BE }, /*!< Signed linear */
+ { AST_FORMAT_LPC10, "lpc10", 8000, "LPC10", 7, 20, 20, 20, 20 }, /*!< codec_lpc10.c */
+ { AST_FORMAT_G729A, "g729", 8000, "G.729A", 10, 10, 230, 10, 20, AST_SMOOTHER_FLAG_G729 }, /*!< Binary commercial distribution */
+ { AST_FORMAT_SPEEX, "speex", 8000, "SpeeX", 10, 10, 60, 10, 20 }, /*!< codec_speex.c */
+ { AST_FORMAT_ILBC, "ilbc", 8000, "iLBC", 50, 30, 30, 30, 30 }, /*!< codec_ilbc.c */ /* inc=30ms - workaround */
+ { AST_FORMAT_G726_AAL2, "g726aal2", 8000, "G.726 AAL2", 40, 10, 300, 10, 20 }, /*!< codec_g726.c */
+ { AST_FORMAT_G722, "g722", 16000, "G722", 80, 10, 150, 10, 20 }, /*!< codec_g722.c */
+ { AST_FORMAT_SLINEAR16, "slin16", 16000, "16 bit Signed Linear PCM (16kHz)", 320, 10, 70, 10, 20 }, /*!< Signed linear (16kHz) */
+ { AST_FORMAT_JPEG, "jpeg", 0, "JPEG image"}, /*!< See format_jpeg.c */
+ { AST_FORMAT_PNG, "png", 0, "PNG image"}, /*!< PNG Image format */
+ { AST_FORMAT_H261, "h261", 0, "H.261 Video" }, /*!< H.261 Video Passthrough */
+ { AST_FORMAT_H263, "h263", 0, "H.263 Video" }, /*!< H.263 Passthrough support, see format_h263.c */
+ { AST_FORMAT_H263_PLUS, "h263p", 0, "H.263+ Video" }, /*!< H.263plus passthrough support See format_h263.c */
+ { AST_FORMAT_H264, "h264", 0, "H.264 Video" }, /*!< Passthrough support, see format_h263.c */
+ { AST_FORMAT_MP4_VIDEO, "mpeg4", 0, "MPEG4 Video" }, /*!< Passthrough support for MPEG4 */
+ { AST_FORMAT_T140, "t140", 0, "Passthrough T.140 Realtime Text" }, /*!< Passthrough support for T.140 Realtime Text */
+};
+
+struct ast_frame ast_null_frame = { AST_FRAME_NULL, };
+
+void ast_smoother_reset(struct ast_smoother *s, int size)
+{
+ memset(s, 0, sizeof(*s));
+ s->size = size;
+}
+
+struct ast_smoother *ast_smoother_new(int size)
+{
+ struct ast_smoother *s;
+ if (size < 1)
+ return NULL;
+ if ((s = ast_malloc(sizeof(*s))))
+ ast_smoother_reset(s, size);
+ return s;
+}
+
+int ast_smoother_get_flags(struct ast_smoother *s)
+{
+ return s->flags;
+}
+
+void ast_smoother_set_flags(struct ast_smoother *s, int flags)
+{
+ s->flags = flags;
+}
+
+int ast_smoother_test_flag(struct ast_smoother *s, int flag)
+{
+ return (s->flags & flag);
+}
+
+int __ast_smoother_feed(struct ast_smoother *s, struct ast_frame *f, int swap)
+{
+ if (f->frametype != AST_FRAME_VOICE) {
+ ast_log(LOG_WARNING, "Huh? Can't smooth a non-voice frame!\n");
+ return -1;
+ }
+ if (!s->format) {
+ s->format = f->subclass;
+ s->samplesperbyte = (float)f->samples / (float)f->datalen;
+ } else if (s->format != f->subclass) {
+ ast_log(LOG_WARNING, "Smoother was working on %d format frames, now trying to feed %d?\n", s->format, f->subclass);
+ return -1;
+ }
+ if (s->len + f->datalen > SMOOTHER_SIZE) {
+ ast_log(LOG_WARNING, "Out of smoother space\n");
+ return -1;
+ }
+ if (((f->datalen == s->size) || ((f->datalen < 10) && (s->flags & AST_SMOOTHER_FLAG_G729)))
+ && !s->opt && (f->offset >= AST_MIN_OFFSET)) {
+ if (!s->len) {
+ /* Optimize by sending the frame we just got
+ on the next read, thus eliminating the douple
+ copy */
+ if (swap)
+ ast_swapcopy_samples(f->data, f->data, f->samples);
+ s->opt = f;
+ return 0;
+ } else {
+ s->optimizablestream++;
+ if (s->optimizablestream > 10) {
+ /* For the past 10 rounds, we have input and output
+ frames of the correct size for this smoother, yet
+ we were unable to optimize because there was still
+ some cruft left over. Lets just drop the cruft so
+ we can move to a fully optimized path */
+ if (swap)
+ ast_swapcopy_samples(f->data, f->data, f->samples);
+ s->len = 0;
+ s->opt = f;
+ return 0;
+ }
+ }
+ } else
+ s->optimizablestream = 0;
+ if (s->flags & AST_SMOOTHER_FLAG_G729) {
+ if (s->len % 10) {
+ ast_log(LOG_NOTICE, "Dropping extra frame of G.729 since we already have a VAD frame at the end\n");
+ return 0;
+ }
+ }
+ if (swap)
+ ast_swapcopy_samples(s->data+s->len, f->data, f->samples);
+ else
+ memcpy(s->data + s->len, f->data, f->datalen);
+ /* If either side is empty, reset the delivery time */
+ if (!s->len || ast_tvzero(f->delivery) || ast_tvzero(s->delivery)) /* XXX really ? */
+ s->delivery = f->delivery;
+ s->len += f->datalen;
+ return 0;
+}
+
+struct ast_frame *ast_smoother_read(struct ast_smoother *s)
+{
+ struct ast_frame *opt;
+ int len;
+
+ /* IF we have an optimization frame, send it */
+ if (s->opt) {
+ if (s->opt->offset < AST_FRIENDLY_OFFSET)
+ ast_log(LOG_WARNING, "Returning a frame of inappropriate offset (%d).\n",
+ s->opt->offset);
+ opt = s->opt;
+ s->opt = NULL;
+ return opt;
+ }
+
+ /* Make sure we have enough data */
+ if (s->len < s->size) {
+ /* Or, if this is a G.729 frame with VAD on it, send it immediately anyway */
+ if (!((s->flags & AST_SMOOTHER_FLAG_G729) && (s->size % 10)))
+ return NULL;
+ }
+ len = s->size;
+ if (len > s->len)
+ len = s->len;
+ /* Make frame */
+ s->f.frametype = AST_FRAME_VOICE;
+ s->f.subclass = s->format;
+ s->f.data = s->framedata + AST_FRIENDLY_OFFSET;
+ s->f.offset = AST_FRIENDLY_OFFSET;
+ s->f.datalen = len;
+ /* Samples will be improper given VAD, but with VAD the concept really doesn't even exist */
+ s->f.samples = len * s->samplesperbyte; /* XXX rounding */
+ s->f.delivery = s->delivery;
+ /* Fill Data */
+ memcpy(s->f.data, s->data, len);
+ s->len -= len;
+ /* Move remaining data to the front if applicable */
+ if (s->len) {
+ /* In principle this should all be fine because if we are sending
+ G.729 VAD, the next timestamp will take over anyawy */
+ memmove(s->data, s->data + len, s->len);
+ if (!ast_tvzero(s->delivery)) {
+ /* If we have delivery time, increment it, otherwise, leave it at 0 */
+ s->delivery = ast_tvadd(s->delivery, ast_samp2tv(s->f.samples, 8000));
+ }
+ }
+ /* Return frame */
+ return &s->f;
+}
+
+void ast_smoother_free(struct ast_smoother *s)
+{
+ ast_free(s);
+}
+
+static struct ast_frame *ast_frame_header_new(void)
+{
+ struct ast_frame *f;
+
+#if !defined(LOW_MEMORY)
+ struct ast_frame_cache *frames;
+
+ if ((frames = ast_threadstorage_get(&frame_cache, sizeof(*frames)))) {
+ if ((f = AST_LIST_REMOVE_HEAD(&frames->list, frame_list))) {
+ size_t mallocd_len = f->mallocd_hdr_len;
+ memset(f, 0, sizeof(*f));
+ f->mallocd_hdr_len = mallocd_len;
+ f->mallocd = AST_MALLOCD_HDR;
+ frames->size--;
+ return f;
+ }
+ }
+ if (!(f = ast_calloc_cache(1, sizeof(*f))))
+ return NULL;
+#else
+ if (!(f = ast_calloc(1, sizeof(*f))))
+ return NULL;
+#endif
+
+ f->mallocd_hdr_len = sizeof(*f);
+#ifdef TRACE_FRAMES
+ AST_LIST_LOCK(&headerlist);
+ headers++;
+ AST_LIST_INSERT_HEAD(&headerlist, f, frame_list);
+ AST_LIST_UNLOCK(&headerlist);
+#endif
+
+ return f;
+}
+
+#if !defined(LOW_MEMORY)
+static void frame_cache_cleanup(void *data)
+{
+ struct ast_frame_cache *frames = data;
+ struct ast_frame *f;
+
+ while ((f = AST_LIST_REMOVE_HEAD(&frames->list, frame_list)))
+ ast_free(f);
+
+ ast_free(frames);
+}
+#endif
+
+void ast_frame_free(struct ast_frame *fr, int cache)
+{
+ if (ast_test_flag(fr, AST_FRFLAG_FROM_TRANSLATOR))
+ ast_translate_frame_freed(fr);
+
+ if (!fr->mallocd)
+ return;
+
+#if !defined(LOW_MEMORY)
+ if (cache && fr->mallocd == AST_MALLOCD_HDR) {
+ /* Cool, only the header is malloc'd, let's just cache those for now
+ * to keep things simple... */
+ struct ast_frame_cache *frames;
+
+ if ((frames = ast_threadstorage_get(&frame_cache, sizeof(*frames)))
+ && frames->size < FRAME_CACHE_MAX_SIZE) {
+ AST_LIST_INSERT_HEAD(&frames->list, fr, frame_list);
+ frames->size++;
+ return;
+ }
+ }
+#endif
+
+ if (fr->mallocd & AST_MALLOCD_DATA) {
+ if (fr->data)
+ ast_free(fr->data - fr->offset);
+ }
+ if (fr->mallocd & AST_MALLOCD_SRC) {
+ if (fr->src)
+ ast_free((char *)fr->src);
+ }
+ if (fr->mallocd & AST_MALLOCD_HDR) {
+#ifdef TRACE_FRAMES
+ AST_LIST_LOCK(&headerlist);
+ headers--;
+ AST_LIST_REMOVE(&headerlist, fr, frame_list);
+ AST_LIST_UNLOCK(&headerlist);
+#endif
+ ast_free(fr);
+ }
+}
+
+/*!
+ * \brief 'isolates' a frame by duplicating non-malloc'ed components
+ * (header, src, data).
+ * On return all components are malloc'ed
+ */
+struct ast_frame *ast_frisolate(struct ast_frame *fr)
+{
+ struct ast_frame *out;
+ void *newdata;
+
+ ast_clear_flag(fr, AST_FRFLAG_FROM_TRANSLATOR);
+
+ if (!(fr->mallocd & AST_MALLOCD_HDR)) {
+ /* Allocate a new header if needed */
+ if (!(out = ast_frame_header_new()))
+ return NULL;
+ out->frametype = fr->frametype;
+ out->subclass = fr->subclass;
+ out->datalen = fr->datalen;
+ out->samples = fr->samples;
+ out->offset = fr->offset;
+ out->data = fr->data;
+ /* Copy the timing data */
+ ast_copy_flags(out, fr, AST_FRFLAG_HAS_TIMING_INFO);
+ if (ast_test_flag(fr, AST_FRFLAG_HAS_TIMING_INFO)) {
+ out->ts = fr->ts;
+ out->len = fr->len;
+ out->seqno = fr->seqno;
+ }
+ } else
+ out = fr;
+
+ if (!(fr->mallocd & AST_MALLOCD_SRC)) {
+ if (fr->src) {
+ if (!(out->src = ast_strdup(fr->src))) {
+ if (out != fr)
+ ast_free(out);
+ return NULL;
+ }
+ }
+ } else
+ out->src = fr->src;
+
+ if (!(fr->mallocd & AST_MALLOCD_DATA)) {
+ if (!(newdata = ast_malloc(fr->datalen + AST_FRIENDLY_OFFSET))) {
+ if (out->src != fr->src)
+ ast_free((void *) out->src);
+ if (out != fr)
+ ast_free(out);
+ return NULL;
+ }
+ newdata += AST_FRIENDLY_OFFSET;
+ out->offset = AST_FRIENDLY_OFFSET;
+ out->datalen = fr->datalen;
+ memcpy(newdata, fr->data, fr->datalen);
+ out->data = newdata;
+ }
+
+ out->mallocd = AST_MALLOCD_HDR | AST_MALLOCD_SRC | AST_MALLOCD_DATA;
+
+ return out;
+}
+
+struct ast_frame *ast_frdup(const struct ast_frame *f)
+{
+ struct ast_frame *out = NULL;
+ int len, srclen = 0;
+ void *buf = NULL;
+
+#if !defined(LOW_MEMORY)
+ struct ast_frame_cache *frames;
+#endif
+
+ /* Start with standard stuff */
+ len = sizeof(*out) + AST_FRIENDLY_OFFSET + f->datalen;
+ /* If we have a source, add space for it */
+ /*
+ * XXX Watch out here - if we receive a src which is not terminated
+ * properly, we can be easily attacked. Should limit the size we deal with.
+ */
+ if (f->src)
+ srclen = strlen(f->src);
+ if (srclen > 0)
+ len += srclen + 1;
+
+#if !defined(LOW_MEMORY)
+ if ((frames = ast_threadstorage_get(&frame_cache, sizeof(*frames)))) {
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&frames->list, out, frame_list) {
+ if (out->mallocd_hdr_len >= len) {
+ size_t mallocd_len = out->mallocd_hdr_len;
+
+ AST_LIST_REMOVE_CURRENT(frame_list);
+ memset(out, 0, sizeof(*out));
+ out->mallocd_hdr_len = mallocd_len;
+ buf = out;
+ frames->size--;
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ }
+#endif
+
+ if (!buf) {
+ if (!(buf = ast_calloc_cache(1, len)))
+ return NULL;
+ out = buf;
+ out->mallocd_hdr_len = len;
+ }
+
+ out->frametype = f->frametype;
+ out->subclass = f->subclass;
+ out->datalen = f->datalen;
+ out->samples = f->samples;
+ out->delivery = f->delivery;
+ /* Set us as having malloc'd header only, so it will eventually
+ get freed. */
+ out->mallocd = AST_MALLOCD_HDR;
+ out->offset = AST_FRIENDLY_OFFSET;
+ if (out->datalen) {
+ out->data = buf + sizeof(*out) + AST_FRIENDLY_OFFSET;
+ memcpy(out->data, f->data, out->datalen);
+ }
+ if (srclen > 0) {
+ out->src = buf + sizeof(*out) + AST_FRIENDLY_OFFSET + f->datalen;
+ /* Must have space since we allocated for it */
+ strcpy((char *)out->src, f->src);
+ }
+ ast_copy_flags(out, f, AST_FRFLAG_HAS_TIMING_INFO);
+ out->ts = f->ts;
+ out->len = f->len;
+ out->seqno = f->seqno;
+ return out;
+}
+
+void ast_swapcopy_samples(void *dst, const void *src, int samples)
+{
+ int i;
+ unsigned short *dst_s = dst;
+ const unsigned short *src_s = src;
+
+ for (i = 0; i < samples; i++)
+ dst_s[i] = (src_s[i]<<8) | (src_s[i]>>8);
+}
+
+
+struct ast_format_list *ast_get_format_list_index(int index)
+{
+ return &AST_FORMAT_LIST[index];
+}
+
+struct ast_format_list *ast_get_format_list(size_t *size)
+{
+ *size = (sizeof(AST_FORMAT_LIST) / sizeof(AST_FORMAT_LIST[0]));
+ return AST_FORMAT_LIST;
+}
+
+char* ast_getformatname(int format)
+{
+ int x;
+ char *ret = "unknown";
+ for (x = 0; x < sizeof(AST_FORMAT_LIST) / sizeof(AST_FORMAT_LIST[0]); x++) {
+ if (AST_FORMAT_LIST[x].bits == format) {
+ ret = AST_FORMAT_LIST[x].name;
+ break;
+ }
+ }
+ return ret;
+}
+
+char *ast_getformatname_multiple(char *buf, size_t size, int format)
+{
+ int x;
+ unsigned len;
+ char *start, *end = buf;
+
+ if (!size)
+ return buf;
+ snprintf(end, size, "0x%x (", format);
+ len = strlen(end);
+ end += len;
+ size -= len;
+ start = end;
+ for (x = 0; x < sizeof(AST_FORMAT_LIST) / sizeof(AST_FORMAT_LIST[0]); x++) {
+ if (AST_FORMAT_LIST[x].bits & format) {
+ snprintf(end, size,"%s|",AST_FORMAT_LIST[x].name);
+ len = strlen(end);
+ end += len;
+ size -= len;
+ }
+ }
+ if (start == end)
+ ast_copy_string(start, "nothing)", size);
+ else if (size > 1)
+ *(end -1) = ')';
+ return buf;
+}
+
+static struct ast_codec_alias_table {
+ char *alias;
+ char *realname;
+} ast_codec_alias_table[] = {
+ { "slinear", "slin"},
+ { "slinear16", "slin16"},
+ { "g723.1", "g723"},
+};
+
+static const char *ast_expand_codec_alias(const char *in)
+{
+ int x;
+
+ for (x = 0; x < sizeof(ast_codec_alias_table) / sizeof(ast_codec_alias_table[0]); x++) {
+ if (!strcmp(in,ast_codec_alias_table[x].alias))
+ return ast_codec_alias_table[x].realname;
+ }
+ return in;
+}
+
+int ast_getformatbyname(const char *name)
+{
+ int x, all, format = 0;
+
+ all = strcasecmp(name, "all") ? 0 : 1;
+ for (x = 0; x < sizeof(AST_FORMAT_LIST) / sizeof(AST_FORMAT_LIST[0]); x++) {
+ if (all ||
+ !strcasecmp(AST_FORMAT_LIST[x].name,name) ||
+ !strcasecmp(AST_FORMAT_LIST[x].name,ast_expand_codec_alias(name))) {
+ format |= AST_FORMAT_LIST[x].bits;
+ if (!all)
+ break;
+ }
+ }
+
+ return format;
+}
+
+char *ast_codec2str(int codec)
+{
+ int x;
+ char *ret = "unknown";
+ for (x = 0; x < sizeof(AST_FORMAT_LIST) / sizeof(AST_FORMAT_LIST[0]); x++) {
+ if (AST_FORMAT_LIST[x].bits == codec) {
+ ret = AST_FORMAT_LIST[x].desc;
+ break;
+ }
+ }
+ return ret;
+}
+
+static char *show_codecs(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int i, found=0;
+ char hex[25];
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show codecs [audio|video|image]";
+ e->usage =
+ "Usage: core show codecs [audio|video|image]\n"
+ " Displays codec mapping\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if ((a->argc < 3) || (a->argc > 4))
+ return CLI_SHOWUSAGE;
+
+ if (!ast_opt_dont_warn)
+ ast_cli(a->fd, "Disclaimer: this command is for informational purposes only.\n"
+ "\tIt does not indicate anything about your configuration.\n");
+
+ ast_cli(a->fd, "%11s %9s %10s TYPE %8s %s\n","INT","BINARY","HEX","NAME","DESC");
+ ast_cli(a->fd, "--------------------------------------------------------------------------------\n");
+ if ((a->argc == 3) || (!strcasecmp(a->argv[3],"audio"))) {
+ found = 1;
+ for (i=0;i<13;i++) {
+ snprintf(hex,25,"(0x%x)",1<<i);
+ ast_cli(a->fd, "%11u (1 << %2d) %10s audio %8s (%s)\n",1 << i,i,hex,ast_getformatname(1<<i),ast_codec2str(1<<i));
+ }
+ }
+
+ if ((a->argc == 3) || (!strcasecmp(a->argv[3],"image"))) {
+ found = 1;
+ for (i=16;i<18;i++) {
+ snprintf(hex,25,"(0x%x)",1<<i);
+ ast_cli(a->fd, "%11u (1 << %2d) %10s image %8s (%s)\n",1 << i,i,hex,ast_getformatname(1<<i),ast_codec2str(1<<i));
+ }
+ }
+
+ if ((a->argc == 3) || (!strcasecmp(a->argv[3],"video"))) {
+ found = 1;
+ for (i=18;i<22;i++) {
+ snprintf(hex,25,"(0x%x)",1<<i);
+ ast_cli(a->fd, "%11u (1 << %2d) %10s video %8s (%s)\n",1 << i,i,hex,ast_getformatname(1<<i),ast_codec2str(1<<i));
+ }
+ }
+
+ if (!found)
+ return CLI_SHOWUSAGE;
+ else
+ return CLI_SUCCESS;
+}
+
+static char *show_codec_n(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int codec, i, found=0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show codec";
+ e->usage =
+ "Usage: core show codec <number>\n"
+ " Displays codec mapping\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+
+ if (sscanf(a->argv[3],"%d",&codec) != 1)
+ return CLI_SHOWUSAGE;
+
+ for (i = 0; i < 32; i++)
+ if (codec & (1 << i)) {
+ found = 1;
+ ast_cli(a->fd, "%11u (1 << %2d) %s\n",1 << i,i,ast_codec2str(1<<i));
+ }
+
+ if (!found)
+ ast_cli(a->fd, "Codec %d not found\n", codec);
+
+ return CLI_SUCCESS;
+}
+
+/*! Dump a frame for debugging purposes */
+void ast_frame_dump(const char *name, struct ast_frame *f, char *prefix)
+{
+ const char noname[] = "unknown";
+ char ftype[40] = "Unknown Frametype";
+ char cft[80];
+ char subclass[40] = "Unknown Subclass";
+ char csub[80];
+ char moreinfo[40] = "";
+ char cn[60];
+ char cp[40];
+ char cmn[40];
+
+ if (!name)
+ name = noname;
+
+
+ if (!f) {
+ ast_verbose("%s [ %s (NULL) ] [%s]\n",
+ term_color(cp, prefix, COLOR_BRMAGENTA, COLOR_BLACK, sizeof(cp)),
+ term_color(cft, "HANGUP", COLOR_BRRED, COLOR_BLACK, sizeof(cft)),
+ term_color(cn, name, COLOR_YELLOW, COLOR_BLACK, sizeof(cn)));
+ return;
+ }
+ /* XXX We should probably print one each of voice and video when the format changes XXX */
+ if (f->frametype == AST_FRAME_VOICE)
+ return;
+ if (f->frametype == AST_FRAME_VIDEO)
+ return;
+ switch(f->frametype) {
+ case AST_FRAME_DTMF_BEGIN:
+ strcpy(ftype, "DTMF Begin");
+ subclass[0] = f->subclass;
+ subclass[1] = '\0';
+ break;
+ case AST_FRAME_DTMF_END:
+ strcpy(ftype, "DTMF End");
+ subclass[0] = f->subclass;
+ subclass[1] = '\0';
+ break;
+ case AST_FRAME_CONTROL:
+ strcpy(ftype, "Control");
+ switch(f->subclass) {
+ case AST_CONTROL_HANGUP:
+ strcpy(subclass, "Hangup");
+ break;
+ case AST_CONTROL_RING:
+ strcpy(subclass, "Ring");
+ break;
+ case AST_CONTROL_RINGING:
+ strcpy(subclass, "Ringing");
+ break;
+ case AST_CONTROL_ANSWER:
+ strcpy(subclass, "Answer");
+ break;
+ case AST_CONTROL_BUSY:
+ strcpy(subclass, "Busy");
+ break;
+ case AST_CONTROL_TAKEOFFHOOK:
+ strcpy(subclass, "Take Off Hook");
+ break;
+ case AST_CONTROL_OFFHOOK:
+ strcpy(subclass, "Line Off Hook");
+ break;
+ case AST_CONTROL_CONGESTION:
+ strcpy(subclass, "Congestion");
+ break;
+ case AST_CONTROL_FLASH:
+ strcpy(subclass, "Flash");
+ break;
+ case AST_CONTROL_WINK:
+ strcpy(subclass, "Wink");
+ break;
+ case AST_CONTROL_OPTION:
+ strcpy(subclass, "Option");
+ break;
+ case AST_CONTROL_RADIO_KEY:
+ strcpy(subclass, "Key Radio");
+ break;
+ case AST_CONTROL_RADIO_UNKEY:
+ strcpy(subclass, "Unkey Radio");
+ break;
+ case -1:
+ strcpy(subclass, "Stop generators");
+ break;
+ default:
+ snprintf(subclass, sizeof(subclass), "Unknown control '%d'", f->subclass);
+ }
+ break;
+ case AST_FRAME_NULL:
+ strcpy(ftype, "Null Frame");
+ strcpy(subclass, "N/A");
+ break;
+ case AST_FRAME_IAX:
+ /* Should never happen */
+ strcpy(ftype, "IAX Specific");
+ snprintf(subclass, sizeof(subclass), "IAX Frametype %d", f->subclass);
+ break;
+ case AST_FRAME_TEXT:
+ strcpy(ftype, "Text");
+ strcpy(subclass, "N/A");
+ ast_copy_string(moreinfo, f->data, sizeof(moreinfo));
+ break;
+ case AST_FRAME_IMAGE:
+ strcpy(ftype, "Image");
+ snprintf(subclass, sizeof(subclass), "Image format %s\n", ast_getformatname(f->subclass));
+ break;
+ case AST_FRAME_HTML:
+ strcpy(ftype, "HTML");
+ switch(f->subclass) {
+ case AST_HTML_URL:
+ strcpy(subclass, "URL");
+ ast_copy_string(moreinfo, f->data, sizeof(moreinfo));
+ break;
+ case AST_HTML_DATA:
+ strcpy(subclass, "Data");
+ break;
+ case AST_HTML_BEGIN:
+ strcpy(subclass, "Begin");
+ break;
+ case AST_HTML_END:
+ strcpy(subclass, "End");
+ break;
+ case AST_HTML_LDCOMPLETE:
+ strcpy(subclass, "Load Complete");
+ break;
+ case AST_HTML_NOSUPPORT:
+ strcpy(subclass, "No Support");
+ break;
+ case AST_HTML_LINKURL:
+ strcpy(subclass, "Link URL");
+ ast_copy_string(moreinfo, f->data, sizeof(moreinfo));
+ break;
+ case AST_HTML_UNLINK:
+ strcpy(subclass, "Unlink");
+ break;
+ case AST_HTML_LINKREJECT:
+ strcpy(subclass, "Link Reject");
+ break;
+ default:
+ snprintf(subclass, sizeof(subclass), "Unknown HTML frame '%d'\n", f->subclass);
+ break;
+ }
+ break;
+ case AST_FRAME_MODEM:
+ strcpy(ftype, "Modem");
+ switch (f->subclass) {
+ case AST_MODEM_T38:
+ strcpy(subclass, "T.38");
+ break;
+ case AST_MODEM_V150:
+ strcpy(subclass, "V.150");
+ break;
+ default:
+ snprintf(subclass, sizeof(subclass), "Unknown MODEM frame '%d'\n", f->subclass);
+ break;
+ }
+ break;
+ default:
+ snprintf(ftype, sizeof(ftype), "Unknown Frametype '%d'", f->frametype);
+ }
+ if (!ast_strlen_zero(moreinfo))
+ ast_verbose("%s [ TYPE: %s (%d) SUBCLASS: %s (%d) '%s' ] [%s]\n",
+ term_color(cp, prefix, COLOR_BRMAGENTA, COLOR_BLACK, sizeof(cp)),
+ term_color(cft, ftype, COLOR_BRRED, COLOR_BLACK, sizeof(cft)),
+ f->frametype,
+ term_color(csub, subclass, COLOR_BRCYAN, COLOR_BLACK, sizeof(csub)),
+ f->subclass,
+ term_color(cmn, moreinfo, COLOR_BRGREEN, COLOR_BLACK, sizeof(cmn)),
+ term_color(cn, name, COLOR_YELLOW, COLOR_BLACK, sizeof(cn)));
+ else
+ ast_verbose("%s [ TYPE: %s (%d) SUBCLASS: %s (%d) ] [%s]\n",
+ term_color(cp, prefix, COLOR_BRMAGENTA, COLOR_BLACK, sizeof(cp)),
+ term_color(cft, ftype, COLOR_BRRED, COLOR_BLACK, sizeof(cft)),
+ f->frametype,
+ term_color(csub, subclass, COLOR_BRCYAN, COLOR_BLACK, sizeof(csub)),
+ f->subclass,
+ term_color(cn, name, COLOR_YELLOW, COLOR_BLACK, sizeof(cn)));
+}
+
+
+#ifdef TRACE_FRAMES
+static char *show_frame_stats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_frame *f;
+ int x=1;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show frame stats";
+ e->usage =
+ "Usage: core show frame stats\n"
+ " Displays debugging statistics from framer\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+ AST_LIST_LOCK(&headerlist);
+ ast_cli(a->fd, " Framer Statistics \n");
+ ast_cli(a->fd, "---------------------------\n");
+ ast_cli(a->fd, "Total allocated headers: %d\n", headers);
+ ast_cli(a->fd, "Queue Dump:\n");
+ AST_LIST_TRAVERSE(&headerlist, f, frame_list)
+ ast_cli(a->fd, "%d. Type %d, subclass %d from %s\n", x++, f->frametype, f->subclass, f->src ? f->src : "<Unknown>");
+ AST_LIST_UNLOCK(&headerlist);
+ return CLI_SUCCESS;
+}
+#endif
+
+/* Builtin Asterisk CLI-commands for debugging */
+static struct ast_cli_entry my_clis[] = {
+ AST_CLI_DEFINE(show_codecs, "Displays a list of codecs"),
+ AST_CLI_DEFINE(show_codec_n, "Shows a specific codec"),
+#ifdef TRACE_FRAMES
+ AST_CLI_DEFINE(show_frame_stats, "Shows frame statistics"),
+#endif
+};
+
+int init_framer(void)
+{
+ ast_cli_register_multiple(my_clis, sizeof(my_clis) / sizeof(struct ast_cli_entry));
+ return 0;
+}
+
+void ast_codec_pref_convert(struct ast_codec_pref *pref, char *buf, size_t size, int right)
+{
+ int x, differential = (int) 'A', mem;
+ char *from, *to;
+
+ if (right) {
+ from = pref->order;
+ to = buf;
+ mem = size;
+ } else {
+ to = pref->order;
+ from = buf;
+ mem = 32;
+ }
+
+ memset(to, 0, mem);
+ for (x = 0; x < 32 ; x++) {
+ if (!from[x])
+ break;
+ to[x] = right ? (from[x] + differential) : (from[x] - differential);
+ }
+}
+
+int ast_codec_pref_string(struct ast_codec_pref *pref, char *buf, size_t size)
+{
+ int x, codec;
+ size_t total_len, slen;
+ char *formatname;
+
+ memset(buf,0,size);
+ total_len = size;
+ buf[0] = '(';
+ total_len--;
+ for(x = 0; x < 32 ; x++) {
+ if (total_len <= 0)
+ break;
+ if (!(codec = ast_codec_pref_index(pref,x)))
+ break;
+ if ((formatname = ast_getformatname(codec))) {
+ slen = strlen(formatname);
+ if (slen > total_len)
+ break;
+ strncat(buf,formatname,total_len);
+ total_len -= slen;
+ }
+ if (total_len && x < 31 && ast_codec_pref_index(pref , x + 1)) {
+ strncat(buf,"|",total_len);
+ total_len--;
+ }
+ }
+ if (total_len) {
+ strncat(buf,")",total_len);
+ total_len--;
+ }
+
+ return size - total_len;
+}
+
+int ast_codec_pref_index(struct ast_codec_pref *pref, int index)
+{
+ int slot = 0;
+
+
+ if ((index >= 0) && (index < sizeof(pref->order))) {
+ slot = pref->order[index];
+ }
+
+ return slot ? AST_FORMAT_LIST[slot-1].bits : 0;
+}
+
+/*! \brief Remove codec from pref list */
+void ast_codec_pref_remove(struct ast_codec_pref *pref, int format)
+{
+ struct ast_codec_pref oldorder;
+ int x, y = 0;
+ int slot;
+ int size;
+
+ if (!pref->order[0])
+ return;
+
+ memcpy(&oldorder, pref, sizeof(oldorder));
+ memset(pref, 0, sizeof(*pref));
+
+ for (x = 0; x < sizeof(AST_FORMAT_LIST) / sizeof(AST_FORMAT_LIST[0]); x++) {
+ slot = oldorder.order[x];
+ size = oldorder.framing[x];
+ if (! slot)
+ break;
+ if (AST_FORMAT_LIST[slot-1].bits != format) {
+ pref->order[y] = slot;
+ pref->framing[y++] = size;
+ }
+ }
+
+}
+
+/*! \brief Append codec to list */
+int ast_codec_pref_append(struct ast_codec_pref *pref, int format)
+{
+ int x, newindex = 0;
+
+ ast_codec_pref_remove(pref, format);
+
+ for (x = 0; x < sizeof(AST_FORMAT_LIST) / sizeof(AST_FORMAT_LIST[0]); x++) {
+ if (AST_FORMAT_LIST[x].bits == format) {
+ newindex = x + 1;
+ break;
+ }
+ }
+
+ if (newindex) {
+ for (x = 0; x < sizeof(AST_FORMAT_LIST) / sizeof(AST_FORMAT_LIST[0]); x++) {
+ if (!pref->order[x]) {
+ pref->order[x] = newindex;
+ break;
+ }
+ }
+ }
+
+ return x;
+}
+
+/*! \brief Prepend codec to list */
+void ast_codec_pref_prepend(struct ast_codec_pref *pref, int format, int only_if_existing)
+{
+ int x, newindex = 0;
+
+ /* First step is to get the codecs "index number" */
+ for (x = 0; x < ARRAY_LEN(AST_FORMAT_LIST); x++) {
+ if (AST_FORMAT_LIST[x].bits == format) {
+ newindex = x + 1;
+ break;
+ }
+ }
+ /* Done if its unknown */
+ if (!newindex)
+ return;
+
+ /* Now find any existing occurrence, or the end */
+ for (x = 0; x < 32; x++) {
+ if (!pref->order[x] || pref->order[x] == newindex)
+ break;
+ }
+
+ if (only_if_existing && !pref->order[x])
+ return;
+
+ /* Move down to make space to insert - either all the way to the end,
+ or as far as the existing location (which will be overwritten) */
+ for (; x > 0; x--) {
+ pref->order[x] = pref->order[x - 1];
+ pref->framing[x] = pref->framing[x - 1];
+ }
+
+ /* And insert the new entry */
+ pref->order[0] = newindex;
+ pref->framing[0] = 0; /* ? */
+}
+
+/*! \brief Set packet size for codec */
+int ast_codec_pref_setsize(struct ast_codec_pref *pref, int format, int framems)
+{
+ int x, index = -1;
+
+ for (x = 0; x < sizeof(AST_FORMAT_LIST) / sizeof(AST_FORMAT_LIST[0]); x++) {
+ if (AST_FORMAT_LIST[x].bits == format) {
+ index = x;
+ break;
+ }
+ }
+
+ if (index < 0)
+ return -1;
+
+ /* size validation */
+ if (!framems)
+ framems = AST_FORMAT_LIST[index].def_ms;
+
+ if (AST_FORMAT_LIST[index].inc_ms && framems % AST_FORMAT_LIST[index].inc_ms) /* avoid division by zero */
+ framems -= framems % AST_FORMAT_LIST[index].inc_ms;
+
+ if (framems < AST_FORMAT_LIST[index].min_ms)
+ framems = AST_FORMAT_LIST[index].min_ms;
+
+ if (framems > AST_FORMAT_LIST[index].max_ms)
+ framems = AST_FORMAT_LIST[index].max_ms;
+
+
+ for (x = 0; x < sizeof(AST_FORMAT_LIST) / sizeof(AST_FORMAT_LIST[0]); x++) {
+ if (pref->order[x] == (index + 1)) {
+ pref->framing[x] = framems;
+ break;
+ }
+ }
+
+ return x;
+}
+
+/*! \brief Get packet size for codec */
+struct ast_format_list ast_codec_pref_getsize(struct ast_codec_pref *pref, int format)
+{
+ int x, index = -1, framems = 0;
+ struct ast_format_list fmt = { 0, };
+
+ for (x = 0; x < sizeof(AST_FORMAT_LIST) / sizeof(AST_FORMAT_LIST[0]); x++) {
+ if (AST_FORMAT_LIST[x].bits == format) {
+ fmt = AST_FORMAT_LIST[x];
+ index = x;
+ break;
+ }
+ }
+
+ for (x = 0; x < sizeof(AST_FORMAT_LIST) / sizeof(AST_FORMAT_LIST[0]); x++) {
+ if (pref->order[x] == (index + 1)) {
+ framems = pref->framing[x];
+ break;
+ }
+ }
+
+ /* size validation */
+ if (!framems)
+ framems = AST_FORMAT_LIST[index].def_ms;
+
+ if (AST_FORMAT_LIST[index].inc_ms && framems % AST_FORMAT_LIST[index].inc_ms) /* avoid division by zero */
+ framems -= framems % AST_FORMAT_LIST[index].inc_ms;
+
+ if (framems < AST_FORMAT_LIST[index].min_ms)
+ framems = AST_FORMAT_LIST[index].min_ms;
+
+ if (framems > AST_FORMAT_LIST[index].max_ms)
+ framems = AST_FORMAT_LIST[index].max_ms;
+
+ fmt.cur_ms = framems;
+
+ return fmt;
+}
+
+/*! \brief Pick a codec */
+int ast_codec_choose(struct ast_codec_pref *pref, int formats, int find_best)
+{
+ int x, ret = 0, slot;
+
+ for (x = 0; x < sizeof(AST_FORMAT_LIST) / sizeof(AST_FORMAT_LIST[0]); x++) {
+ slot = pref->order[x];
+
+ if (!slot)
+ break;
+ if (formats & AST_FORMAT_LIST[slot-1].bits) {
+ ret = AST_FORMAT_LIST[slot-1].bits;
+ break;
+ }
+ }
+ if (ret & AST_FORMAT_AUDIO_MASK)
+ return ret;
+
+ ast_debug(4, "Could not find preferred codec - %s\n", find_best ? "Going for the best codec" : "Returning zero codec");
+
+ return find_best ? ast_best_codec(formats) : 0;
+}
+
+int ast_parse_allow_disallow(struct ast_codec_pref *pref, int *mask, const char *list, int allowing)
+{
+ int errors = 0;
+ char *parse = NULL, *this = NULL, *psize = NULL;
+ int format = 0, framems = 0;
+
+ parse = ast_strdupa(list);
+ while ((this = strsep(&parse, ","))) {
+ framems = 0;
+ if ((psize = strrchr(this, ':'))) {
+ *psize++ = '\0';
+ ast_debug(1, "Packetization for codec: %s is %s\n", this, psize);
+ framems = atoi(psize);
+ if (framems < 0) {
+ framems = 0;
+ errors++;
+ ast_log(LOG_WARNING, "Bad packetization value for codec %s\n", this);
+ }
+ }
+ if (!(format = ast_getformatbyname(this))) {
+ ast_log(LOG_WARNING, "Cannot %s unknown format '%s'\n", allowing ? "allow" : "disallow", this);
+ errors++;
+ continue;
+ }
+
+ if (mask) {
+ if (allowing)
+ *mask |= format;
+ else
+ *mask &= ~format;
+ }
+
+ /* Set up a preference list for audio. Do not include video in preferences
+ since we can not transcode video and have to use whatever is offered
+ */
+ if (pref && (format & AST_FORMAT_AUDIO_MASK)) {
+ if (strcasecmp(this, "all")) {
+ if (allowing) {
+ ast_codec_pref_append(pref, format);
+ ast_codec_pref_setsize(pref, format, framems);
+ }
+ else
+ ast_codec_pref_remove(pref, format);
+ } else if (!allowing) {
+ memset(pref, 0, sizeof(*pref));
+ }
+ }
+ }
+ return errors;
+}
+
+static int g723_len(unsigned char buf)
+{
+ enum frame_type type = buf & TYPE_MASK;
+
+ switch(type) {
+ case TYPE_DONTSEND:
+ return 0;
+ break;
+ case TYPE_SILENCE:
+ return 4;
+ break;
+ case TYPE_HIGH:
+ return 24;
+ break;
+ case TYPE_LOW:
+ return 20;
+ break;
+ default:
+ ast_log(LOG_WARNING, "Badly encoded frame (%d)\n", type);
+ }
+ return -1;
+}
+
+static int g723_samples(unsigned char *buf, int maxlen)
+{
+ int pos = 0;
+ int samples = 0;
+ int res;
+ while(pos < maxlen) {
+ res = g723_len(buf[pos]);
+ if (res <= 0)
+ break;
+ samples += 240;
+ pos += res;
+ }
+ return samples;
+}
+
+static unsigned char get_n_bits_at(unsigned char *data, int n, int bit)
+{
+ int byte = bit / 8; /* byte containing first bit */
+ int rem = 8 - (bit % 8); /* remaining bits in first byte */
+ unsigned char ret = 0;
+
+ if (n <= 0 || n > 8)
+ return 0;
+
+ if (rem < n) {
+ ret = (data[byte] << (n - rem));
+ ret |= (data[byte + 1] >> (8 - n + rem));
+ } else {
+ ret = (data[byte] >> (rem - n));
+ }
+
+ return (ret & (0xff >> (8 - n)));
+}
+
+static int speex_get_wb_sz_at(unsigned char *data, int len, int bit)
+{
+ static int SpeexWBSubModeSz[] = {
+ 0, 36, 112, 192,
+ 352, 0, 0, 0 };
+ int off = bit;
+ unsigned char c;
+
+ /* skip up to two wideband frames */
+ if (((len * 8 - off) >= 5) &&
+ get_n_bits_at(data, 1, off)) {
+ c = get_n_bits_at(data, 3, off + 1);
+ off += SpeexWBSubModeSz[c];
+
+ if (((len * 8 - off) >= 5) &&
+ get_n_bits_at(data, 1, off)) {
+ c = get_n_bits_at(data, 3, off + 1);
+ off += SpeexWBSubModeSz[c];
+
+ if (((len * 8 - off) >= 5) &&
+ get_n_bits_at(data, 1, off)) {
+ ast_log(LOG_WARNING, "Encountered corrupt speex frame; too many wideband frames in a row.\n");
+ return -1;
+ }
+ }
+
+ }
+ return off - bit;
+}
+
+static int speex_samples(unsigned char *data, int len)
+{
+ static int SpeexSubModeSz[] = {
+ 5, 43, 119, 160,
+ 220, 300, 364, 492,
+ 79, 0, 0, 0,
+ 0, 0, 0, 0 };
+ static int SpeexInBandSz[] = {
+ 1, 1, 4, 4,
+ 4, 4, 4, 4,
+ 8, 8, 16, 16,
+ 32, 32, 64, 64 };
+ int bit = 0;
+ int cnt = 0;
+ int off;
+ unsigned char c;
+
+ while ((len * 8 - bit) >= 5) {
+ /* skip wideband frames */
+ off = speex_get_wb_sz_at(data, len, bit);
+ if (off < 0) {
+ ast_log(LOG_WARNING, "Had error while reading wideband frames for speex samples\n");
+ break;
+ }
+ bit += off;
+
+ if ((len * 8 - bit) < 5) {
+ ast_log(LOG_WARNING, "Not enough bits remaining after wide band for speex samples.\n");
+ break;
+ }
+
+ /* get control bits */
+ c = get_n_bits_at(data, 5, bit);
+ bit += 5;
+
+ if (c == 15) {
+ /* terminator */
+ break;
+ } else if (c == 14) {
+ /* in-band signal; next 4 bits contain signal id */
+ c = get_n_bits_at(data, 4, bit);
+ bit += 4;
+ bit += SpeexInBandSz[c];
+ } else if (c == 13) {
+ /* user in-band; next 5 bits contain msg len */
+ c = get_n_bits_at(data, 5, bit);
+ bit += 5;
+ bit += c * 8;
+ } else if (c > 8) {
+ /* unknown */
+ break;
+ } else {
+ /* skip number bits for submode (less the 5 control bits) */
+ bit += SpeexSubModeSz[c] - 5;
+ cnt += 160; /* new frame */
+ }
+ }
+ return cnt;
+}
+
+int ast_codec_get_samples(struct ast_frame *f)
+{
+ int samples=0;
+ switch(f->subclass) {
+ case AST_FORMAT_SPEEX:
+ samples = speex_samples(f->data, f->datalen);
+ break;
+ case AST_FORMAT_G723_1:
+ samples = g723_samples(f->data, f->datalen);
+ break;
+ case AST_FORMAT_ILBC:
+ samples = 240 * (f->datalen / 50);
+ break;
+ case AST_FORMAT_GSM:
+ samples = 160 * (f->datalen / 33);
+ break;
+ case AST_FORMAT_G729A:
+ samples = f->datalen * 8;
+ break;
+ case AST_FORMAT_SLINEAR:
+ case AST_FORMAT_SLINEAR16:
+ samples = f->datalen / 2;
+ break;
+ case AST_FORMAT_LPC10:
+ /* assumes that the RTP packet contains one LPC10 frame */
+ samples = 22 * 8;
+ samples += (((char *)(f->data))[7] & 0x1) * 8;
+ break;
+ case AST_FORMAT_ULAW:
+ case AST_FORMAT_ALAW:
+ samples = f->datalen;
+ break;
+ case AST_FORMAT_G722:
+ case AST_FORMAT_ADPCM:
+ case AST_FORMAT_G726:
+ case AST_FORMAT_G726_AAL2:
+ samples = f->datalen * 2;
+ break;
+ default:
+ ast_log(LOG_WARNING, "Unable to calculate samples for format %s\n", ast_getformatname(f->subclass));
+ }
+ return samples;
+}
+
+int ast_codec_get_len(int format, int samples)
+{
+ int len = 0;
+
+ /* XXX Still need speex, g723, and lpc10 XXX */
+ switch(format) {
+ case AST_FORMAT_G723_1:
+ len = (samples / 240) * 20;
+ break;
+ case AST_FORMAT_ILBC:
+ len = (samples / 240) * 50;
+ break;
+ case AST_FORMAT_GSM:
+ len = (samples / 160) * 33;
+ break;
+ case AST_FORMAT_G729A:
+ len = samples / 8;
+ break;
+ case AST_FORMAT_SLINEAR:
+ case AST_FORMAT_SLINEAR16:
+ len = samples * 2;
+ break;
+ case AST_FORMAT_ULAW:
+ case AST_FORMAT_ALAW:
+ len = samples;
+ break;
+ case AST_FORMAT_G722:
+ case AST_FORMAT_ADPCM:
+ case AST_FORMAT_G726:
+ case AST_FORMAT_G726_AAL2:
+ len = samples / 2;
+ break;
+ default:
+ ast_log(LOG_WARNING, "Unable to calculate sample length for format %s\n", ast_getformatname(format));
+ }
+
+ return len;
+}
+
+int ast_frame_adjust_volume(struct ast_frame *f, int adjustment)
+{
+ int count;
+ short *fdata = f->data;
+ short adjust_value = abs(adjustment);
+
+ if ((f->frametype != AST_FRAME_VOICE) || (f->subclass != AST_FORMAT_SLINEAR))
+ return -1;
+
+ if (!adjustment)
+ return 0;
+
+ for (count = 0; count < f->samples; count++) {
+ if (adjustment > 0) {
+ ast_slinear_saturated_multiply(&fdata[count], &adjust_value);
+ } else if (adjustment < 0) {
+ ast_slinear_saturated_divide(&fdata[count], &adjust_value);
+ }
+ }
+
+ return 0;
+}
+
+int ast_frame_slinear_sum(struct ast_frame *f1, struct ast_frame *f2)
+{
+ int count;
+ short *data1, *data2;
+
+ if ((f1->frametype != AST_FRAME_VOICE) || (f1->subclass != AST_FORMAT_SLINEAR))
+ return -1;
+
+ if ((f2->frametype != AST_FRAME_VOICE) || (f2->subclass != AST_FORMAT_SLINEAR))
+ return -1;
+
+ if (f1->samples != f2->samples)
+ return -1;
+
+ for (count = 0, data1 = f1->data, data2 = f2->data;
+ count < f1->samples;
+ count++, data1++, data2++)
+ ast_slinear_saturated_add(data1, data2);
+
+ return 0;
+}
diff --git a/trunk/main/fskmodem.c b/trunk/main/fskmodem.c
new file mode 100644
index 000000000..e59024834
--- /dev/null
+++ b/trunk/main/fskmodem.c
@@ -0,0 +1,360 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * Includes code and algorithms from the Zapata library.
+ *
+ * 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 FSK Modulator/Demodulator
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \arg Includes code and algorithms from the Zapata library.
+ *
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/fskmodem.h"
+
+#define NBW 2
+#define BWLIST {75,800}
+#define NF 6
+#define FLIST {1400,1800,1200,2200,1300,2100}
+
+#define STATE_SEARCH_STARTBIT 0
+#define STATE_SEARCH_STARTBIT2 1
+#define STATE_SEARCH_STARTBIT3 2
+#define STATE_GET_BYTE 3
+
+static inline int iget_sample(short **buffer, int *len)
+{
+ int retval;
+ retval = (int) **buffer;
+ (*buffer)++;
+ (*len)--;
+ return retval;
+}
+
+#define IGET_SAMPLE iget_sample(&buffer, len)
+/*! \brief Coefficients for input filters
+ * Coefficients table, generated by program "mkfilter"
+ * mkfilter is part of the zapatatelephony.org distribution
+ * Format: coef[IDX_FREC][IDX_BW][IDX_COEF]
+ * IDX_COEF = 0 => 1/GAIN
+ * IDX_COEF = 1-6 => Coefficientes y[n]
+*/
+static double coef_in[NF][NBW][8]={
+ { { 1.8229206611e-04,-7.8997325866e-01,2.2401819940e+00,-4.6751353581e+00,5.5080745712e+00,-5.0571565772e+00,2.6215820004e+00,0.0000000000e+00,
+ }, { 9.8532175289e-02,-5.6297236492e-02,3.3146713415e-01,-9.2239200436e-01,1.4844365184e+00,-2.0183258642e+00,2.0074154497e+00,0.0000000000e+00,
+ }, }, { { 1.8229206610e-04,-7.8997325866e-01,7.7191410839e-01,-2.8075643964e+00,1.6948618347e+00,-3.0367273700e+00,9.0333559408e-01,0.0000000000e+00,
+ }, { 9.8531161839e-02,-5.6297236492e-02,1.1421579050e-01,-4.8122536483e-01,4.0121072432e-01,-7.4834487567e-01,6.9170822332e-01,0.0000000000e+00,
+ }, }, { { 1.8229206611e-04,-7.8997325866e-01,2.9003821430e+00,-6.1082779024e+00,7.7169345751e+00,-6.6075999680e+00,3.3941838836e+00,0.0000000000e+00,
+ }, { 9.8539686961e-02,-5.6297236492e-02,4.2915323820e-01,-1.2609358633e+00,2.2399213250e+00,-2.9928879142e+00,2.5990173742e+00,0.0000000000e+00,
+ }, }, { { 1.8229206610e-04,-7.8997325866e-01,-7.7191410839e-01,-2.8075643964e+00,-1.6948618347e+00,-3.0367273700e+00,-9.0333559408e-01,0.0000000000e+00,
+ }, { 9.8531161839e-02,-5.6297236492e-02,-1.1421579050e-01,-4.8122536483e-01,-4.0121072432e-01,-7.4834487567e-01,-6.9170822332e-01,0.0000000000e+00,
+ }, }, { { 1.8229206611e-04,-7.8997325866e-01,2.5782298908e+00,-5.3629717478e+00,6.5890882172e+00,-5.8012914776e+00,3.0171839130e+00,0.0000000000e+00,
+ }, { 9.8534230718e-02,-5.6297236492e-02,3.8148618075e-01,-1.0848760410e+00,1.8441165168e+00,-2.4860666655e+00,2.3103384142e+00,0.0000000000e+00,
+ }, }, { { 1.8229206610e-04,-7.8997325866e-01,-3.8715051001e-01,-2.6192408538e+00,-8.3977994034e-01,-2.8329897913e+00,-4.5306444352e-01,0.0000000000e+00,
+ }, { 9.8531160936e-02,-5.6297236492e-02,-5.7284484199e-02,-4.3673866734e-01,-1.9564766257e-01,-6.2028156584e-01,-3.4692356122e-01,0.0000000000e+00,
+ }, },
+};
+
+/*! \brief Coefficients for output filter
+ * Coefficients table, generated by program "mkfilter"
+ * Format: coef[IDX_BW][IDX_COEF]
+ * IDX_COEF = 0 => 1/GAIN
+ * IDX_COEF = 1-6 => Coefficientes y[n]
+*/
+static double coef_out[NBW][8]={
+ { 1.3868644653e-08,-6.3283665042e-01,4.0895057217e+00,-1.1020074592e+01,1.5850766191e+01,-1.2835109292e+01,5.5477477340e+00,0.0000000000e+00,
+ }, { 3.1262119724e-03,-7.8390522307e-03,8.5209627801e-02,-4.0804129163e-01,1.1157139955e+00,-1.8767603680e+00,1.8916395224e+00,0.0000000000e+00
+ },
+};
+
+/*! Integer Pass Band demodulator filter */
+static inline int ibpdfilter(struct filter_struct * fs, int in)
+{
+ int i,j;
+ int s;
+ int64_t s_interim;
+
+ /* integer filter */
+ s = in * fs->icoefs[0];
+ fs->ixv[(fs->ip+6)&7] = s;
+
+ s= (fs->ixv[fs->ip] + fs->ixv[(fs->ip+6)&7]) +
+ 6 * (fs->ixv[(fs->ip+1)&7] + fs->ixv[(fs->ip+5)&7]) +
+ 15 * (fs->ixv[(fs->ip+2)&7] + fs->ixv[(fs->ip+4)&7]) +
+ 20 * fs->ixv[(fs->ip+3)&7];
+
+ for (i=1, j=fs->ip; i < 7; i++,j++) {
+ /* Promote operation to 64 bit to prevent overflow that occurred in 32 bit) */
+ s_interim = (int64_t)(fs->iyv[j & 7]) *
+ (int64_t)(fs->icoefs[i]) /
+ (int64_t)(1024);
+ s += (int) s_interim;
+ }
+ fs->iyv[ j & 7] = s;
+ fs->ip++;
+ fs->ip &= 7;
+ return s;
+}
+
+/*! Integer Band Pass filter */
+static inline int ibpfilter(struct filter_struct * fs, int in)
+{
+ int i, j;
+ int s;
+ int64_t s_interim;
+
+ /* integer filter */
+ s = in * fs->icoefs[0] / 256;
+ fs->ixv[(fs->ip+6) & 7] = s;
+
+ s = (fs->ixv[(fs->ip+6) & 7] - fs->ixv[fs->ip])
+ + 3 * (fs->ixv[(fs->ip+2) & 7] - fs->ixv[(fs->ip+4) & 7]);
+
+ for (i=1,j=fs->ip; i < 7; i++,j++) {
+ s_interim = (int64_t)(fs->iyv[j&7]) *
+ (int64_t)(fs->icoefs[i]) /
+ (int64_t)(256);
+ s+= (int) s_interim;
+ }
+ fs->iyv[j&7]=s;
+ fs->ip++; fs->ip &= 7;
+ return s;
+}
+
+static inline int idemodulator(fsk_data *fskd, int *retval, int x)
+{
+ int is, im, id;
+ int ilin2;
+
+ is = ibpfilter(&fskd->space_filter, x);
+ im = ibpfilter(&fskd->mark_filter, x);
+
+ ilin2 = ((im * im) - (is * is))/(256 * 256);
+
+ id = ibpdfilter(&fskd->demod_filter, ilin2);
+
+ *retval = id;
+ return 0;
+}
+
+static int get_bit_raw(fsk_data *fskd, short *buffer, int *len)
+{
+ /* This function implements a DPLL to synchronize with the bits */
+ int f;
+
+ int ix;
+ /* PLL coeffs are set up in callerid_new */
+ for (f = 0;;){
+ if (idemodulator(fskd, &ix, IGET_SAMPLE)) return(-1);
+ if ((ix * fskd->xi0)<0) { /* Transicion */
+ if (!f) {
+ if (fskd->icont<(fskd->pllispb2))
+ fskd->icont+=fskd->pllids;
+ else
+ fskd->icont-=fskd->pllids;
+ f = 1;
+ }
+ }
+ fskd->xi0=ix;
+ fskd->icont+=32;
+ if (fskd->icont>fskd->pllispb) {
+ fskd->icont-=fskd->pllispb;
+ break;
+ }
+ }
+ f=(ix>0)?0x80:0;
+ return f;
+}
+
+int fskmodem_init(fsk_data *fskd)
+{
+ int i;
+
+ fskd->space_filter.ip = 0;
+ fskd->mark_filter.ip = 0;
+ fskd->demod_filter.ip = 0;
+
+ for ( i = 0 ; i < 7 ; i++ ) {
+ fskd->space_filter.icoefs[i] =
+ coef_in[fskd->f_space_idx][fskd->bw][i] * 256;
+ fskd->space_filter.ixv[i] = 0;;
+ fskd->space_filter.iyv[i] = 0;;
+
+ fskd->mark_filter.icoefs[i] =
+ coef_in[fskd->f_mark_idx][fskd->bw][i] * 256;
+ fskd->mark_filter.ixv[i] = 0;;
+ fskd->mark_filter.iyv[i] = 0;;
+
+ fskd->demod_filter.icoefs[i] =
+ coef_out[fskd->bw][i] * 1024;
+ fskd->demod_filter.ixv[i] = 0;;
+ fskd->demod_filter.iyv[i] = 0;;
+ }
+ return 0;
+}
+
+int fsk_serial(fsk_data *fskd, short *buffer, int *len, int *outbyte)
+{
+ int a;
+ int i, j, n1, r;
+ int samples = 0;
+ int olen;
+ int beginlen = *len;
+ int beginlenx;
+
+ switch (fskd->state) {
+ /* Pick up where we left off */
+ case STATE_SEARCH_STARTBIT2:
+ goto search_startbit2;
+ case STATE_SEARCH_STARTBIT3:
+ goto search_startbit3;
+ case STATE_GET_BYTE:
+ goto getbyte;
+ }
+ /* We await for start bit */
+ do {
+ /* this was jesus's nice, reasonable, working (at least with RTTY) code
+ to look for the beginning of the start bit. Unfortunately, since TTY/TDD's
+ just start sending a start bit with nothing preceding it at the beginning
+ of a transmission (what a LOSING design), we cant do it this elegantly */
+ /* NOT USED
+ if (demodulator(zap,&x1))
+ return -1;
+ for(;;) {
+ if (demodulator(zap,&x2))
+ return -1;
+ if (x1>0 && x2<0) break;
+ x1=x2;
+ }
+ */
+ /* this is now the imprecise, losing, but functional code to detect the
+ beginning of a start bit in the TDD sceanario. It just looks for sufficient
+ level to maybe, perhaps, guess, maybe that its maybe the beginning of
+ a start bit, perhaps. This whole thing stinks! */
+ beginlenx=beginlen; /* just to avoid unused war warnings */
+ if (idemodulator(fskd, &fskd->xi1, IGET_SAMPLE))
+ return -1;
+ samples++;
+ for(;;) {
+search_startbit2:
+ if (*len <= 0) {
+ fskd->state = STATE_SEARCH_STARTBIT2;
+ return 0;
+ }
+ samples++;
+ if (idemodulator(fskd,&fskd->xi2,IGET_SAMPLE))
+ return -1;
+#if 0
+ printf("xi2 = %d ", fskd->xi2);
+#endif
+ if (fskd->xi2 < 512)
+ break;
+ }
+search_startbit3:
+ /* We await for 0.5 bits before using DPLL */
+ i=fskd->ispb/2;
+ if (*len < i) {
+ fskd->state = STATE_SEARCH_STARTBIT3;
+ return 0;
+ }
+ for (; i>0; i--) {
+ if (idemodulator(fskd, &fskd->xi1, IGET_SAMPLE))
+ return(-1);
+#if 0
+ printf("xi1 = %d ", fskd->xi1);
+#endif
+ samples++;
+ }
+
+ /* x1 must be negative (start bit confirmation) */
+
+ } while (fskd->xi1>0);
+ fskd->state = STATE_GET_BYTE;
+
+getbyte:
+
+ /* Need at least 80 samples (for 1200) or
+ 1320 (for 45.5) to be sure we'll have a byte */
+ if (fskd->nbit < 8) {
+ if (*len < 1320)
+ return 0;
+ } else {
+ if (*len < 80)
+ return 0;
+ }
+
+ /* Now we read the data bits */
+ j = fskd->nbit;
+ for (a = n1 = 0; j; j--) {
+ olen = *len;
+ i = get_bit_raw(fskd, buffer, len);
+ buffer += (olen - *len);
+ if (i == -1)
+ return -1;
+ if (i)
+ n1++;
+ a >>= 1;
+ a |= i;
+ }
+ j = 8 - fskd->nbit;
+ a >>= j;
+
+ /* We read parity bit (if exists) and check parity */
+ if (fskd->parity) {
+ olen = *len;
+ i = get_bit_raw(fskd, buffer, len);
+ buffer += (olen - *len);
+ if (i == -1)
+ return -1;
+ if (i)
+ n1++;
+ if (fskd->parity == 1) { /* parity=1 (even) */
+ if (n1 & 1)
+ a |= 0x100; /* error */
+ } else { /* parity=2 (odd) */
+ if (!(n1 & 1))
+ a |= 0x100; /* error */
+ }
+ }
+
+ /* We read STOP bits. All of them must be 1 */
+
+ for (j = fskd->instop; j; j--) {
+ r = get_bit_raw(fskd, buffer, len);
+ if (r == -1)
+ return -1;
+ if (!r)
+ a |= 0x200;
+ }
+
+ /* And finally we return
+ * Bit 8 : Parity error
+ * Bit 9 : Framming error
+ */
+
+ *outbyte = a;
+ fskd->state = STATE_SEARCH_STARTBIT;
+ return 1;
+}
diff --git a/trunk/main/global_datastores.c b/trunk/main/global_datastores.c
new file mode 100644
index 000000000..9b87b2cb4
--- /dev/null
+++ b/trunk/main/global_datastores.c
@@ -0,0 +1,83 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2007, Digium, Inc.
+ *
+ * Mark Michelson <mmichelson@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
+ *
+ * \brief globally-accessible datastore information and callbacks
+ *
+ * \author Mark Michelson <mmichelson@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/global_datastores.h"
+#include "asterisk/linkedlists.h"
+
+static void dialed_interface_destroy(void *data)
+{
+ struct ast_dialed_interface *di = NULL;
+ AST_LIST_HEAD(, ast_dialed_interface) *dialed_interface_list = data;
+
+ if (!dialed_interface_list)
+ return;
+
+ AST_LIST_LOCK(dialed_interface_list);
+ while ((di = AST_LIST_REMOVE_HEAD(dialed_interface_list, list)))
+ ast_free(di);
+ AST_LIST_UNLOCK(dialed_interface_list);
+
+ AST_LIST_HEAD_DESTROY(dialed_interface_list);
+ ast_free(dialed_interface_list);
+}
+
+static void *dialed_interface_duplicate(void *data)
+{
+ struct ast_dialed_interface *di = NULL;
+ AST_LIST_HEAD(, ast_dialed_interface) *old_list;
+ AST_LIST_HEAD(, ast_dialed_interface) *new_list = NULL;
+
+ if(!(old_list = data))
+ return NULL;
+
+ if(!(new_list = ast_calloc(1, sizeof(*new_list))))
+ return NULL;
+
+ AST_LIST_HEAD_INIT(new_list);
+ AST_LIST_LOCK(old_list);
+ AST_LIST_TRAVERSE(old_list, di, list) {
+ struct ast_dialed_interface *di2 = ast_calloc(1, sizeof(*di2) + strlen(di->interface));
+ if(!di2) {
+ AST_LIST_UNLOCK(old_list);
+ dialed_interface_destroy(new_list);
+ return NULL;
+ }
+ strcpy(di2->interface, di->interface);
+ AST_LIST_INSERT_TAIL(new_list, di2, list);
+ }
+ AST_LIST_UNLOCK(old_list);
+
+ return new_list;
+}
+
+const struct ast_datastore_info dialed_interface_info = {
+ .type ="dialed-interface",
+ .destroy = dialed_interface_destroy,
+ .duplicate = dialed_interface_duplicate,
+};
diff --git a/trunk/main/hashtab.c b/trunk/main/hashtab.c
new file mode 100644
index 000000000..82a28ee10
--- /dev/null
+++ b/trunk/main/hashtab.c
@@ -0,0 +1,805 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2007, Digium, Inc.
+ *
+ * Steve Murphy <murf@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
+ *
+ * \brief code to implement generic hash tables
+ *
+ * \author Steve Murphy <murf@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision")
+
+#include <ctype.h>
+
+#include "asterisk/lock.h"
+#include "asterisk/frame.h"
+#include "asterisk/channel.h"
+#include "asterisk/cli.h"
+#include "asterisk/term.h"
+#include "asterisk/utils.h"
+#include "asterisk/threadstorage.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/hashtab.h"
+
+static void ast_hashtab_resize( struct ast_hashtab *tab);
+static void *ast_hashtab_lookup_internal(struct ast_hashtab *tab, const void *obj, unsigned int h);
+
+/* some standard, default routines for general use */
+
+int ast_hashtab_compare_strings(const void *a, const void *b)
+{
+ return strcmp((char*)a,(char*)b);
+}
+
+int ast_hashtab_compare_strings_nocase(const void *a, const void *b)
+{
+ return strcasecmp((const char*)a,(const char*)b);
+}
+
+int ast_hashtab_compare_ints(const void *a, const void *b)
+{
+ int ai = *((int *) a);
+ int bi = *((int *) b);
+
+ if (ai < bi)
+ return -1;
+
+ return !(ai == bi);
+}
+
+int ast_hashtab_compare_shorts(const void *a, const void *b)
+{
+ short as = *((short *) a);
+ short bs = *((short *) b);
+
+ if (as < bs)
+ return -1;
+
+ return !(as == bs);
+}
+
+int ast_hashtab_resize_java(struct ast_hashtab *tab)
+{
+ double loadfactor = (double) tab->hash_tab_elements / (double) tab->hash_tab_size;
+
+ return (loadfactor > 0.75);
+}
+
+int ast_hashtab_resize_tight(struct ast_hashtab *tab)
+{
+ return (tab->hash_tab_elements > tab->hash_tab_size); /* this is quicker than division */
+}
+
+int ast_hashtab_resize_none(struct ast_hashtab *tab) /* always return 0 -- no resizing */
+{
+ return 0;
+}
+
+int ast_is_prime(int num)
+{
+ int tnum, limit;
+
+ if (!(num & 0x1)) /* even number -- not prime */
+ return 0;
+
+ /* Loop through ODD numbers starting with 3 */
+
+ tnum = 3;
+ limit = num;
+ while (tnum < limit) {
+ if (!(num % tnum))
+ return 0;
+
+ /* really, we only need to check sqrt(num) numbers */
+ limit = num / tnum;
+
+ /* we only check odd numbers */
+ tnum = tnum + 2;
+ }
+
+ /* if we made it through the loop, the number is a prime */
+
+ return 1;
+}
+
+int ast_hashtab_newsize_java(struct ast_hashtab *tab)
+{
+ int i = (tab->hash_tab_size << 1); /* multiply by two */
+
+ while (!ast_is_prime(i))
+ i++;
+
+ return i;
+}
+
+int ast_hashtab_newsize_tight(struct ast_hashtab *tab)
+{
+ int x = (tab->hash_tab_size << 1);
+ int i = (tab->hash_tab_size + x);
+
+ while (!ast_is_prime(i))
+ i++;
+
+ return i;
+}
+
+int ast_hashtab_newsize_none(struct ast_hashtab *tab) /* always return current size -- no resizing */
+{
+ return tab->hash_tab_size;
+}
+
+unsigned int ast_hashtab_hash_string(const void *obj)
+{
+ unsigned char *str = (unsigned char *) obj;
+ unsigned int total;
+
+ for (total = 0; *str; str++)
+ {
+ unsigned int tmp = total;
+ total <<= 1; /* multiply by 2 */
+ total += tmp; /* multiply by 3 */
+ total <<= 2; /* multiply by 12 */
+ total += tmp; /* multiply by 13 */
+
+ total += ((unsigned int)(*str));
+ }
+ return total;
+}
+
+unsigned int ast_hashtab_hash_string_sax(const void *obj) /* from Josh */
+{
+ unsigned char *str = (unsigned char *) obj;
+ unsigned int total = 0, c = 0;
+
+ while ((c = *str++))
+ total ^= (total << 5) + (total >> 2) + (total << 10) + c;
+
+ return total;
+}
+
+unsigned int ast_hashtab_hash_string_nocase(const void *obj)
+{
+ unsigned char *str = (unsigned char*)obj;
+ unsigned int total;
+
+ for (total = 0; *str; str++) {
+ unsigned int tmp = total;
+ unsigned int charval = toupper(*str);
+
+ /* hopefully, the following is faster than multiplication by 7 */
+ /* why do I go to this bother? A good compiler will do this
+ anyway, if I say total *= 13 */
+ /* BTW, tried *= 7, and it doesn't do as well in spreading things around! */
+ total <<= 1; /* multiply by 2 */
+ total += tmp; /* multiply by 3 */
+ total <<= 2; /* multiply by 12 */
+ total += tmp; /* multiply by 13 */
+
+ total += (charval);
+ }
+
+ return total;
+}
+
+unsigned int ast_hashtab_hash_int(const int x)
+{
+ return x;
+}
+
+unsigned int ast_hashtab_hash_short(const short x)
+{
+ /* hmmmm.... modulus is best < 65535 !! */
+ return x;
+}
+
+struct ast_hashtab *ast_hashtab_create(int initial_buckets,
+ int (*compare)(const void *a, const void *b),
+ int (*resize)(struct ast_hashtab *),
+ int (*newsize)(struct ast_hashtab *tab),
+ unsigned int (*hash)(const void *obj),
+ int do_locking)
+{
+ struct ast_hashtab *ht;
+
+ if (!(ht = ast_calloc(1, sizeof(*ht))))
+ return NULL;
+
+ while (!ast_is_prime(initial_buckets)) /* make sure this is prime */
+ initial_buckets++;
+
+ if (!(ht->array = ast_calloc(initial_buckets, sizeof(*(ht->array))))) {
+ free(ht);
+ return NULL;
+ }
+
+ ht->hash_tab_size = initial_buckets;
+ ht->compare = compare;
+ ht->resize = resize;
+ ht->newsize = newsize;
+ ht->hash = hash;
+ ht->do_locking = do_locking;
+
+ if (do_locking)
+ ast_rwlock_init(&ht->lock);
+
+ if (!ht->resize)
+ ht->resize = ast_hashtab_resize_java;
+
+ if (!ht->newsize)
+ ht->newsize = ast_hashtab_newsize_java;
+
+ return ht;
+}
+
+struct ast_hashtab *ast_hashtab_dup(struct ast_hashtab *tab, void *(*obj_dup_func)(const void *obj))
+{
+ struct ast_hashtab *ht;
+ unsigned int i;
+
+ if (!(ht = ast_calloc(1, sizeof(*ht))))
+ return NULL;
+
+ if (!(ht->array = ast_calloc(tab->hash_tab_size, sizeof(*(ht->array))))) {
+ free(ht);
+ return NULL;
+ }
+
+ ht->hash_tab_size = tab->hash_tab_size;
+ ht->compare = tab->compare;
+ ht->resize = tab->resize;
+ ht->newsize = tab->newsize;
+ ht->hash = tab->hash;
+ ht->do_locking = tab->do_locking;
+
+ if (ht->do_locking)
+ ast_rwlock_init(&ht->lock);
+
+ /* now, dup the objects in the buckets and get them into the table */
+ /* the fast way is to use the existing array index, and not have to hash
+ the objects again */
+ for (i = 0; i < ht->hash_tab_size; i++) {
+ struct ast_hashtab_bucket *b = tab->array[i];
+ while (b) {
+ void *newobj = (*obj_dup_func)(b->object);
+ if (newobj)
+ ast_hashtab_insert_immediate_bucket(ht, newobj, i);
+ b = b->next;
+ }
+ }
+
+ return ht;
+}
+
+static void tlist_del_item(struct ast_hashtab_bucket **head, struct ast_hashtab_bucket *item)
+{
+ /* item had better be in the list! or suffer the weirdness that occurs, later! */
+ if (*head == item) { /* first item in the list */
+ *head = item->tnext;
+ if (item->tnext)
+ item->tnext->tprev = NULL;
+ } else {
+ /* short circuit stuff */
+ item->tprev->tnext = item->tnext;
+ if (item->tnext)
+ item->tnext->tprev = item->tprev;
+ }
+}
+
+static void tlist_add_head(struct ast_hashtab_bucket **head, struct ast_hashtab_bucket *item)
+{
+ if (*head) {
+ item->tnext = *head;
+ item->tprev = NULL;
+ (*head)->tprev = item;
+ *head = item;
+ } else {
+ /* the list is empty */
+ *head = item;
+ item->tprev = NULL;
+ item->tnext = NULL;
+ }
+}
+
+/* user-controlled hashtab locking. Create a hashtab without locking, then call the
+ following locking routines yourself to lock the table between threads. */
+
+void ast_hashtab_wrlock(struct ast_hashtab *tab)
+{
+ ast_rwlock_wrlock(&tab->lock);
+}
+
+void ast_hashtab_rdlock(struct ast_hashtab *tab)
+{
+ ast_rwlock_rdlock(&tab->lock);
+}
+
+void ast_hashtab_initlock(struct ast_hashtab *tab)
+{
+ ast_rwlock_init(&tab->lock);
+}
+
+void ast_hashtab_destroylock(struct ast_hashtab *tab)
+{
+ ast_rwlock_destroy(&tab->lock);
+}
+
+void ast_hashtab_unlock(struct ast_hashtab *tab)
+{
+ ast_rwlock_unlock(&tab->lock);
+}
+
+void ast_hashtab_destroy( struct ast_hashtab *tab, void (*objdestroyfunc)(void *obj))
+{
+ /* this func will free the hash table and all its memory. It
+ doesn't touch the objects stored in it */
+ if (tab) {
+
+ if (tab->do_locking)
+ ast_rwlock_wrlock(&tab->lock);
+
+ if (tab->array) {
+ /* go thru and destroy the buckets */
+ struct ast_hashtab_bucket *t;
+ int i;
+
+ while (tab->tlist) {
+ t = tab->tlist;
+ if (t->object && objdestroyfunc)
+ (*objdestroyfunc)((void *) t->object); /* I cast this because I'm not going to MOD it, I'm going to DESTROY it */
+
+ tlist_del_item(&(tab->tlist), tab->tlist);
+ free(t);
+ }
+
+ for (i = 0; i < tab->hash_tab_size; i++)
+ tab->array[i] = NULL; /* not totally necc., but best to destroy old ptrs */
+
+ free(tab->array);
+ }
+ if (tab->do_locking) {
+ ast_rwlock_unlock(&tab->lock);
+ ast_rwlock_destroy(&tab->lock);
+ }
+ free(tab);
+ }
+}
+
+int ast_hashtab_insert_immediate(struct ast_hashtab *tab, const void *obj)
+{
+ unsigned int h;
+ int res=0;
+
+ if (!tab || !obj)
+ return res;
+
+ if (tab->do_locking)
+ ast_rwlock_wrlock(&tab->lock);
+
+ h = (*tab->hash)(obj) % tab->hash_tab_size;
+
+ res = ast_hashtab_insert_immediate_bucket(tab,obj,h);
+
+ if (tab->do_locking)
+ ast_rwlock_unlock(&tab->lock);
+
+ return res;
+}
+
+int ast_hashtab_insert_immediate_bucket(struct ast_hashtab *tab, const void *obj, unsigned int h)
+{
+ int c;
+ struct ast_hashtab_bucket *b;
+
+ if (!tab || !obj)
+ return 0;
+
+ for (c = 0, b = tab->array[h]; b; b= b->next)
+ c++;
+
+ if (c + 1 > tab->largest_bucket_size)
+ tab->largest_bucket_size = c + 1;
+
+ if (!(b = ast_calloc(1, sizeof(*b))))
+ return 0;
+
+ b->object = obj;
+ b->next = tab->array[h];
+ tab->array[h] = b;
+
+ if (b->next)
+ b->next->prev = b;
+
+ tlist_add_head(&(tab->tlist), b);
+ tab->hash_tab_elements++;
+
+ if ((*tab->resize)(tab))
+ ast_hashtab_resize(tab);
+
+ return 1;
+}
+
+int ast_hashtab_insert_safe(struct ast_hashtab *tab, const void *obj)
+{
+ /* check to see if the element is already there; insert only if
+ it is not there. */
+ /* will force a resize if the resize func returns 1 */
+ /* returns 1 on success, 0 if there's a problem, or it's already there. */
+ unsigned int bucket = 0;
+
+ if (tab->do_locking)
+ ast_rwlock_wrlock(&tab->lock);
+
+ if (!ast_hashtab_lookup_bucket(tab, obj, &bucket)) {
+ int ret2 = ast_hashtab_insert_immediate_bucket(tab, obj, bucket);
+
+ if (tab->do_locking)
+ ast_rwlock_unlock(&tab->lock);
+
+ return ret2;
+ }
+
+ if (tab->do_locking)
+ ast_rwlock_unlock(&tab->lock);
+
+ return 0;
+}
+
+void *ast_hashtab_lookup(struct ast_hashtab *tab, const void *obj)
+{
+ /* lookup this object in the hash table. return a ptr if found, or NULL if not */
+ unsigned int h;
+ void *ret;
+
+ if (!tab || !obj)
+ return 0;
+
+ if (tab->do_locking)
+ ast_rwlock_rdlock(&tab->lock);
+
+ h = (*tab->hash)(obj) % tab->hash_tab_size;
+
+ ret = ast_hashtab_lookup_internal(tab,obj,h);
+
+ if (tab->do_locking)
+ ast_rwlock_unlock(&tab->lock);
+
+ return ret;
+}
+
+
+void *ast_hashtab_lookup_with_hash(struct ast_hashtab *tab, const void *obj, unsigned int hashval)
+{
+ /* lookup this object in the hash table. return a ptr if found, or NULL if not */
+ unsigned int h;
+ void *ret;
+
+ if (!tab || !obj)
+ return 0;
+
+ if (tab->do_locking)
+ ast_rwlock_rdlock(&tab->lock);
+
+ h = hashval % tab->hash_tab_size;
+
+ ret = ast_hashtab_lookup_internal(tab,obj,h);
+
+ if (tab->do_locking)
+ ast_rwlock_unlock(&tab->lock);
+
+ return ret;
+}
+
+void *ast_hashtab_lookup_bucket(struct ast_hashtab *tab, const void *obj, unsigned int *bucket)
+{
+ /* lookup this object in the hash table. return a ptr if found, or NULL if not */
+ unsigned int h;
+ void *ret;
+
+ if (!tab || !obj)
+ return 0;
+
+ h = (*tab->hash)(obj) % tab->hash_tab_size;
+
+ ret = ast_hashtab_lookup_internal(tab,obj,h);
+
+ *bucket = h;
+
+ return ret;
+}
+
+static void *ast_hashtab_lookup_internal(struct ast_hashtab *tab, const void *obj, unsigned int h)
+{
+ struct ast_hashtab_bucket *b;
+
+ for (b = tab->array[h]; b; b = b->next) {
+ if (!(*tab->compare)(obj,b->object)) {
+ return (void*) b->object; /* I can't touch obj in this func, but the outside world is welcome to */
+ }
+ }
+
+ return NULL;
+}
+
+void ast_hashtab_get_stats( struct ast_hashtab *tab, int *biggest_bucket_size, int *resize_count, int *num_objects, int *num_buckets)
+{
+ /* returns key stats for the table */
+ if (tab->do_locking)
+ ast_rwlock_rdlock(&tab->lock);
+ *biggest_bucket_size = tab->largest_bucket_size;
+ *resize_count = tab->resize_count;
+ *num_objects = tab->hash_tab_elements;
+ *num_buckets = tab->hash_tab_size;
+ if (tab->do_locking)
+ ast_rwlock_unlock(&tab->lock);
+}
+
+ /* this function returns the number of elements stored in the hashtab */
+int ast_hashtab_size( struct ast_hashtab *tab)
+{
+ return tab->hash_tab_elements;
+}
+
+ /* this function returns the size of the bucket array in the hashtab */
+int ast_hashtab_capacity( struct ast_hashtab *tab)
+{
+ return tab->hash_tab_size;
+}
+
+
+
+/* the insert operation calls this, and is wrlock'd when it does. */
+/* if you want to call it, you should set the wrlock yourself */
+
+
+static void ast_hashtab_resize( struct ast_hashtab *tab)
+{
+ /* this function is called either internally, when the resize func returns 1, or
+ externally by the user to force a resize of the hash table */
+ int newsize = (*tab->newsize)(tab), i, c;
+ unsigned int h;
+ struct ast_hashtab_bucket *b,*bn;
+
+ /* Since we keep a DLL of all the buckets in tlist,
+ all we have to do is free the array, malloc a new one,
+ and then go thru the tlist array and reassign them into
+ the bucket arrayj.
+ */
+ for (i = 0; i < tab->hash_tab_size; i++) { /* don't absolutely have to do this, but
+ why leave ptrs laying around */
+ tab->array[i] = 0; /* erase old ptrs */
+ }
+ free(tab->array);
+ if (!(tab->array = ast_calloc(newsize, sizeof(*(tab->array)))))
+ return;
+
+ /* now sort the buckets into their rightful new slots */
+ tab->resize_count++;
+ tab->hash_tab_size = newsize;
+ tab->largest_bucket_size = 0;
+
+ for (b = tab->tlist; b; b = bn) {
+ b->prev = 0;
+ bn = b->tnext;
+ h = (*tab->hash)(b->object) % tab->hash_tab_size;
+ b->next = tab->array[h];
+ if (b->next)
+ b->next->prev = b;
+ tab->array[h] = b;
+ }
+ /* recalc the largest bucket size */
+ for (i = 0; i < tab->hash_tab_size; i++) {
+ for (c = 0, b = tab->array[i]; b; b = b->next)
+ c++;
+ if (c > tab->largest_bucket_size)
+ tab->largest_bucket_size = c;
+ }
+}
+
+struct ast_hashtab_iter *ast_hashtab_start_traversal(struct ast_hashtab *tab)
+{
+ /* returns an iterator */
+ struct ast_hashtab_iter *it;
+
+ if (!(it = ast_calloc(1, sizeof(*it))))
+ return NULL;
+
+ it->next = tab->tlist;
+ it->tab = tab;
+ if (tab->do_locking)
+ ast_rwlock_rdlock(&tab->lock);
+
+ return it;
+}
+
+/* use this function to get a write lock */
+struct ast_hashtab_iter *ast_hashtab_start_write_traversal(struct ast_hashtab *tab)
+{
+ /* returns an iterator */
+ struct ast_hashtab_iter *it;
+
+ if (!(it = ast_calloc(1, sizeof(*it))))
+ return NULL;
+
+ it->next = tab->tlist;
+ it->tab = tab;
+ if (tab->do_locking)
+ ast_rwlock_wrlock(&tab->lock);
+
+ return it;
+}
+
+void ast_hashtab_end_traversal(struct ast_hashtab_iter *it)
+{
+ if (it->tab->do_locking)
+ ast_rwlock_unlock(&it->tab->lock);
+ free(it);
+}
+
+void *ast_hashtab_next(struct ast_hashtab_iter *it)
+{
+ /* returns the next object in the list, advances iter one step */
+ struct ast_hashtab_bucket *retval;
+
+ if (it && it->next) { /* there's a next in the bucket list */
+ retval = it->next;
+ it->next = retval->tnext;
+ return (void *) retval->object;
+ }
+
+ return NULL;
+}
+
+static void *ast_hashtab_remove_object_internal(struct ast_hashtab *tab, struct ast_hashtab_bucket *b, int h)
+{
+ const void *obj2;
+
+ if (b->prev)
+ b->prev->next = b->next;
+ else
+ tab->array[h] = b->next;
+
+ if (b->next)
+ b->next->prev = b->prev;
+
+ tlist_del_item(&(tab->tlist), b);
+
+ obj2 = b->object;
+ b->object = b->next = (void*)2;
+ free(b); /* free up the hashbucket */
+ tab->hash_tab_elements--;
+#ifdef DEBUG
+ {
+ int c2;
+ struct ast_hashtab_bucket *b2;
+ /* do a little checking */
+ for (c2 = 0, b2 = tab->tlist; b2; b2 = b2->tnext) {
+ c2++;
+ }
+ if (c2 != tab->hash_tab_elements) {
+ printf("Hey! we didn't delete right! there are %d elements in the list, and we expected %d\n",
+ c2, tab->hash_tab_elements);
+ }
+ for (c2 = 0, b2 = tab->tlist; b2; b2 = b2->tnext) {
+ unsigned int obj3 = (unsigned long) obj2;
+ unsigned int b3 = (unsigned long) b;
+ if (b2->object == obj2)
+ printf("Hey-- you've still got a bucket pointing at ht_element %x\n", obj3);
+ if (b2->next == b)
+ printf("Hey-- you've still got a bucket with next ptr pointing to deleted bucket %x\n", b3);
+ if (b2->prev == b)
+ printf("Hey-- you've still got a bucket with prev ptr pointing to deleted bucket %x\n", b3);
+ if (b2->tprev == b)
+ printf("Hey-- you've still got a bucket with tprev ptr pointing to deleted bucket %x\n", b3);
+ if (b2->tnext == b)
+ printf("Hey-- you've still got a bucket with tnext ptr pointing to deleted bucket %x\n", b3);
+ }
+ }
+#endif
+ return (void *) obj2; /* inside this code, the obj's are untouchable, but outside, they aren't */
+}
+
+void *ast_hashtab_remove_object_via_lookup(struct ast_hashtab *tab, void *obj)
+{
+ /* looks up the object; removes the corresponding bucket */
+ const void *obj2;
+
+ if (!tab || !obj)
+ return 0;
+
+ if (tab->do_locking)
+ ast_rwlock_wrlock(&tab->lock);
+
+ obj2 = ast_hashtab_remove_object_via_lookup_nolock(tab,obj);
+
+ if (tab->do_locking)
+ ast_rwlock_unlock(&tab->lock);
+
+ return (void *)obj2;
+}
+
+void *ast_hashtab_remove_object_via_lookup_nolock(struct ast_hashtab *tab, void *obj)
+{
+ /* looks up the object; removes the corresponding bucket */
+ unsigned int h;
+ struct ast_hashtab_bucket *b;
+
+ if (!tab || !obj)
+ return 0;
+
+ h = (*tab->hash)(obj) % tab->hash_tab_size;
+ for (b = tab->array[h]; b; b = b->next) {
+
+ if (!(*tab->compare)(obj, b->object)) {
+ const void *obj2;
+
+ obj2 = ast_hashtab_remove_object_internal(tab, b, h);
+
+ return (void *) obj2; /* inside this code, the obj's are untouchable, but outside, they aren't */
+ }
+ }
+
+ return 0;
+}
+
+void *ast_hashtab_remove_this_object(struct ast_hashtab *tab, void *obj)
+{
+ /* looks up the object by hash and then comparing pts in bucket list instead of
+ calling the compare routine; removes the bucket -- a slightly cheaper operation */
+ /* looks up the object; removes the corresponding bucket */
+ const void *obj2;
+
+ if (!tab || !obj)
+ return 0;
+
+ if (tab->do_locking)
+ ast_rwlock_wrlock(&tab->lock);
+
+ obj2 = ast_hashtab_remove_this_object_nolock(tab,obj);
+
+ if (tab->do_locking)
+ ast_rwlock_unlock(&tab->lock);
+
+ return (void *)obj2;
+}
+
+void *ast_hashtab_remove_this_object_nolock(struct ast_hashtab *tab, void *obj)
+{
+ /* looks up the object by hash and then comparing pts in bucket list instead of
+ calling the compare routine; removes the bucket -- a slightly cheaper operation */
+ /* looks up the object; removes the corresponding bucket */
+ unsigned int h;
+ struct ast_hashtab_bucket *b;
+
+ if (!tab || !obj)
+ return 0;
+
+ h = (*tab->hash)(obj) % tab->hash_tab_size;
+ for (b = tab->array[h]; b; b = b->next) {
+
+ if (obj == b->object) {
+ const void *obj2;
+ obj2 = ast_hashtab_remove_object_internal(tab, b, h);
+
+ return (void *) obj2; /* inside this code, the obj's are untouchable, but outside, they aren't */
+ }
+ }
+
+ return 0;
+}
diff --git a/trunk/main/http.c b/trunk/main/http.c
new file mode 100644
index 000000000..6e8021fa2
--- /dev/null
+++ b/trunk/main/http.c
@@ -0,0 +1,1118 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006, 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.
+ */
+
+/*!
+ * \file
+ * \brief http server for AMI access
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * This program implements a tiny http server
+ * and was inspired by micro-httpd by Jef Poskanzer
+ *
+ * \ref AstHTTP - AMI over the http protocol
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/paths.h" /* use ast_config_AST_DATA_DIR */
+#include "asterisk/network.h"
+#include <time.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/signal.h>
+#include <fcntl.h>
+
+#include "minimime/mm.h"
+
+#include "asterisk/cli.h"
+#include "asterisk/tcptls.h"
+#include "asterisk/http.h"
+#include "asterisk/utils.h"
+#include "asterisk/strings.h"
+#include "asterisk/config.h"
+#include "asterisk/stringfields.h"
+#include "asterisk/version.h"
+#include "asterisk/manager.h"
+
+#define MAX_PREFIX 80
+#define DEFAULT_PREFIX "/asterisk"
+
+/* See http.h for more information about the SSL implementation */
+#if defined(HAVE_OPENSSL) && (defined(HAVE_FUNOPEN) || defined(HAVE_FOPENCOOKIE))
+#define DO_SSL /* comment in/out if you want to support ssl */
+#endif
+
+static struct ast_tls_config http_tls_cfg;
+
+static void *httpd_helper_thread(void *arg);
+
+/*!
+ * we have up to two accepting threads, one for http, one for https
+ */
+static struct server_args http_desc = {
+ .accept_fd = -1,
+ .master = AST_PTHREADT_NULL,
+ .tls_cfg = NULL,
+ .poll_timeout = -1,
+ .name = "http server",
+ .accept_fn = server_root,
+ .worker_fn = httpd_helper_thread,
+};
+
+static struct server_args https_desc = {
+ .accept_fd = -1,
+ .master = AST_PTHREADT_NULL,
+ .tls_cfg = &http_tls_cfg,
+ .poll_timeout = -1,
+ .name = "https server",
+ .accept_fn = server_root,
+ .worker_fn = httpd_helper_thread,
+};
+
+static AST_RWLIST_HEAD_STATIC(uris, ast_http_uri); /*!< list of supported handlers */
+
+struct ast_http_post_mapping {
+ AST_RWLIST_ENTRY(ast_http_post_mapping) entry;
+ char *from;
+ char *to;
+};
+
+static AST_RWLIST_HEAD_STATIC(post_mappings, ast_http_post_mapping);
+
+/* all valid URIs must be prepended by the string in prefix. */
+static char prefix[MAX_PREFIX];
+static int enablestatic;
+
+/*! \brief Limit the kinds of files we're willing to serve up */
+static struct {
+ const char *ext;
+ const char *mtype;
+} mimetypes[] = {
+ { "png", "image/png" },
+ { "jpg", "image/jpeg" },
+ { "js", "application/x-javascript" },
+ { "wav", "audio/x-wav" },
+ { "mp3", "audio/mpeg" },
+ { "svg", "image/svg+xml" },
+ { "svgz", "image/svg+xml" },
+ { "gif", "image/gif" },
+};
+
+struct http_uri_redirect {
+ AST_LIST_ENTRY(http_uri_redirect) entry;
+ char *dest;
+ char target[0];
+};
+
+static AST_RWLIST_HEAD_STATIC(uri_redirects, http_uri_redirect);
+
+static const char *ftype2mtype(const char *ftype, char *wkspace, int wkspacelen)
+{
+ int x;
+ if (ftype) {
+ for (x=0;x<sizeof(mimetypes) / sizeof(mimetypes[0]); x++) {
+ if (!strcasecmp(ftype, mimetypes[x].ext))
+ return mimetypes[x].mtype;
+ }
+ }
+ snprintf(wkspace, wkspacelen, "text/%s", ftype ? ftype : "plain");
+ return wkspace;
+}
+
+static struct ast_str *static_callback(struct server_instance *ser, const char *uri, struct ast_variable *vars, int *status, char **title, int *contentlength)
+{
+ char *path;
+ char *ftype;
+ const char *mtype;
+ char wkspace[80];
+ struct stat st;
+ int len;
+ int fd;
+ struct timeval tv = ast_tvnow();
+ char buf[256];
+ struct ast_tm tm;
+
+ /* Yuck. I'm not really sold on this, but if you don't deliver static content it makes your configuration
+ substantially more challenging, but this seems like a rather irritating feature creep on Asterisk. */
+ if (!enablestatic || ast_strlen_zero(uri))
+ goto out403;
+ /* Disallow any funny filenames at all */
+ if ((uri[0] < 33) || strchr("./|~@#$%^&*() \t", uri[0]))
+ goto out403;
+ if (strstr(uri, "/.."))
+ goto out403;
+
+ if ((ftype = strrchr(uri, '.')))
+ ftype++;
+ mtype = ftype2mtype(ftype, wkspace, sizeof(wkspace));
+
+ /* Cap maximum length */
+ len = strlen(uri) + strlen(ast_config_AST_DATA_DIR) + strlen("/static-http/") + 5;
+ if (len > 1024)
+ goto out403;
+
+ path = alloca(len);
+ sprintf(path, "%s/static-http/%s", ast_config_AST_DATA_DIR, uri);
+ if (stat(path, &st))
+ goto out404;
+ if (S_ISDIR(st.st_mode))
+ goto out404;
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ goto out403;
+
+ ast_strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S %Z", ast_localtime(&tv, &tm, "GMT"));
+ fprintf(ser->f, "HTTP/1.1 200 OK\r\n"
+ "Server: Asterisk/%s\r\n"
+ "Date: %s\r\n"
+ "Connection: close\r\n"
+ "Cache-Control: no-cache, no-store\r\n"
+ "Content-Length: %d\r\n"
+ "Content-type: %s\r\n\r\n",
+ ast_get_version(), buf, (int) st.st_size, mtype);
+
+ while ((len = read(fd, buf, sizeof(buf))) > 0)
+ fwrite(buf, 1, len, ser->f);
+
+ close(fd);
+ return NULL;
+
+out404:
+ *status = 404;
+ *title = ast_strdup("Not Found");
+ return ast_http_error(404, "Not Found", NULL, "Nothing to see here. Move along.");
+
+out403:
+ *status = 403;
+ *title = ast_strdup("Access Denied");
+ return ast_http_error(403, "Access Denied", NULL, "Sorry, I cannot let you do that, Dave.");
+}
+
+
+static struct ast_str *httpstatus_callback(struct server_instance *ser, const char *uri, struct ast_variable *vars, int *status, char **title, int *contentlength)
+{
+ struct ast_str *out = ast_str_create(512);
+ struct ast_variable *v;
+
+ if (out == NULL)
+ return out;
+
+ ast_str_append(&out, 0,
+ "\r\n"
+ "<title>Asterisk HTTP Status</title>\r\n"
+ "<body bgcolor=\"#ffffff\">\r\n"
+ "<table bgcolor=\"#f1f1f1\" align=\"center\"><tr><td bgcolor=\"#e0e0ff\" colspan=\"2\" width=\"500\">\r\n"
+ "<h2>&nbsp;&nbsp;Asterisk&trade; HTTP Status</h2></td></tr>\r\n");
+
+ ast_str_append(&out, 0, "<tr><td><i>Prefix</i></td><td><b>%s</b></td></tr>\r\n", prefix);
+ ast_str_append(&out, 0, "<tr><td><i>Bind Address</i></td><td><b>%s</b></td></tr>\r\n",
+ ast_inet_ntoa(http_desc.oldsin.sin_addr));
+ ast_str_append(&out, 0, "<tr><td><i>Bind Port</i></td><td><b>%d</b></td></tr>\r\n",
+ ntohs(http_desc.oldsin.sin_port));
+ if (http_tls_cfg.enabled)
+ ast_str_append(&out, 0, "<tr><td><i>SSL Bind Port</i></td><td><b>%d</b></td></tr>\r\n",
+ ntohs(https_desc.oldsin.sin_port));
+ ast_str_append(&out, 0, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
+ for (v = vars; v; v = v->next) {
+ if (strncasecmp(v->name, "cookie_", 7))
+ ast_str_append(&out, 0, "<tr><td><i>Submitted Variable '%s'</i></td><td>%s</td></tr>\r\n", v->name, v->value);
+ }
+ ast_str_append(&out, 0, "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
+ for (v = vars; v; v = v->next) {
+ if (!strncasecmp(v->name, "cookie_", 7))
+ ast_str_append(&out, 0, "<tr><td><i>Cookie '%s'</i></td><td>%s</td></tr>\r\n", v->name, v->value);
+ }
+ ast_str_append(&out, 0, "</table><center><font size=\"-1\"><i>Asterisk and Digium are registered trademarks of Digium, Inc.</i></font></center></body>\r\n");
+ return out;
+}
+
+static struct ast_http_uri statusuri = {
+ .callback = httpstatus_callback,
+ .description = "Asterisk HTTP General Status",
+ .uri = "httpstatus",
+ .has_subtree = 0,
+};
+
+static struct ast_http_uri staticuri = {
+ .callback = static_callback,
+ .description = "Asterisk HTTP Static Delivery",
+ .uri = "static",
+ .has_subtree = 1,
+ .static_content = 1,
+};
+
+struct ast_str *ast_http_error(int status, const char *title, const char *extra_header, const char *text)
+{
+ struct ast_str *out = ast_str_create(512);
+ if (out == NULL)
+ return out;
+ ast_str_set(&out, 0,
+ "Content-type: text/html\r\n"
+ "%s"
+ "\r\n"
+ "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
+ "<html><head>\r\n"
+ "<title>%d %s</title>\r\n"
+ "</head><body>\r\n"
+ "<h1>%s</h1>\r\n"
+ "<p>%s</p>\r\n"
+ "<hr />\r\n"
+ "<address>Asterisk Server</address>\r\n"
+ "</body></html>\r\n",
+ (extra_header ? extra_header : ""), status, title, title, text);
+ return out;
+}
+
+/*! \brief
+ * Link the new uri into the list.
+ *
+ * They are sorted by length of
+ * the string, not alphabetically. Duplicate entries are not replaced,
+ * but the insertion order (using <= and not just <) makes sure that
+ * more recent insertions hide older ones.
+ * On a lookup, we just scan the list and stop at the first matching entry.
+ */
+int ast_http_uri_link(struct ast_http_uri *urih)
+{
+ struct ast_http_uri *uri;
+ int len = strlen(urih->uri);
+
+ AST_RWLIST_WRLOCK(&uris);
+
+ if ( AST_RWLIST_EMPTY(&uris) || strlen(AST_RWLIST_FIRST(&uris)->uri) <= len ) {
+ AST_RWLIST_INSERT_HEAD(&uris, urih, entry);
+ AST_RWLIST_UNLOCK(&uris);
+ return 0;
+ }
+
+ AST_RWLIST_TRAVERSE(&uris, uri, entry) {
+ if ( AST_RWLIST_NEXT(uri, entry)
+ && strlen(AST_RWLIST_NEXT(uri, entry)->uri) <= len ) {
+ AST_RWLIST_INSERT_AFTER(&uris, uri, urih, entry);
+ AST_RWLIST_UNLOCK(&uris);
+ return 0;
+ }
+ }
+
+ AST_RWLIST_INSERT_TAIL(&uris, urih, entry);
+
+ AST_RWLIST_UNLOCK(&uris);
+
+ return 0;
+}
+
+void ast_http_uri_unlink(struct ast_http_uri *urih)
+{
+ AST_RWLIST_WRLOCK(&uris);
+ AST_RWLIST_REMOVE(&uris, urih, entry);
+ AST_RWLIST_UNLOCK(&uris);
+}
+
+/*! \note This assumes that the post_mappings list is locked */
+static struct ast_http_post_mapping *find_post_mapping(const char *uri)
+{
+ struct ast_http_post_mapping *post_map;
+
+ if (!ast_strlen_zero(prefix) && strncmp(prefix, uri, strlen(prefix))) {
+ ast_debug(1, "URI %s does not have prefix %s\n", uri, prefix);
+ return NULL;
+ }
+
+ uri += strlen(prefix);
+ if (*uri == '/')
+ uri++;
+
+ AST_RWLIST_TRAVERSE(&post_mappings, post_map, entry) {
+ if (!strcmp(uri, post_map->from))
+ return post_map;
+ }
+
+ return NULL;
+}
+
+static int get_filename(struct mm_mimepart *part, char *fn, size_t fn_len)
+{
+ const char *filename;
+
+ filename = mm_content_getdispositionparambyname(part->type, "filename");
+
+ if (ast_strlen_zero(filename))
+ return -1;
+
+ ast_copy_string(fn, filename, fn_len);
+
+ return 0;
+}
+
+static void post_raw(struct mm_mimepart *part, const char *post_dir, const char *fn)
+{
+ char filename[PATH_MAX];
+ FILE *f;
+ const char *body;
+ size_t body_len;
+
+ snprintf(filename, sizeof(filename), "%s/%s", post_dir, fn);
+
+ ast_debug(1, "Posting raw data to %s\n", filename);
+
+ if (!(f = fopen(filename, "w"))) {
+ ast_log(LOG_WARNING, "Unable to open %s for writing file from a POST!\n", filename);
+ return;
+ }
+
+ if (!(body = mm_mimepart_getbody(part, 0))) {
+ ast_debug(1, "Couldn't get the mimepart body\n");
+ fclose(f);
+ return;
+ }
+ body_len = mm_mimepart_getlength(part);
+
+ ast_debug(1, "Body length is %ld\n", (long int)body_len);
+
+ fwrite(body, 1, body_len, f);
+
+ fclose(f);
+}
+
+static struct ast_str *handle_post(struct server_instance *ser, char *uri,
+ int *status, char **title, int *contentlength, struct ast_variable *headers,
+ struct ast_variable *cookies)
+{
+ char buf;
+ FILE *f;
+ size_t res;
+ struct ast_variable *var;
+ int content_len = 0;
+ MM_CTX *ctx;
+ int mm_res, i;
+ struct ast_http_post_mapping *post_map;
+ const char *post_dir;
+ unsigned long ident = 0;
+
+ for (var = cookies; var; var = var->next) {
+ if (strcasecmp(var->name, "mansession_id"))
+ continue;
+
+ if (sscanf(var->value, "%lx", &ident) != 1) {
+ *status = 400;
+ *title = ast_strdup("Bad Request");
+ return ast_http_error(400, "Bad Request", NULL, "The was an error parsing the request.");
+ }
+
+ if (!astman_verify_session_writepermissions(ident, EVENT_FLAG_CONFIG)) {
+ *status = 401;
+ *title = ast_strdup("Unauthorized");
+ return ast_http_error(401, "Unauthorized", NULL, "You are not authorized to make this request.");
+ }
+
+ break;
+ }
+ if (!var) {
+ *status = 401;
+ *title = ast_strdup("Unauthorized");
+ return ast_http_error(401, "Unauthorized", NULL, "You are not authorized to make this request.");
+ }
+
+ if (!(f = tmpfile()))
+ return NULL;
+
+ for (var = headers; var; var = var->next) {
+ if (!strcasecmp(var->name, "Content-Length")) {
+ if ((sscanf(var->value, "%u", &content_len)) != 1) {
+ ast_log(LOG_ERROR, "Invalid Content-Length in POST request!\n");
+ fclose(f);
+ return NULL;
+ }
+ ast_debug(1, "Got a Content-Length of %d\n", content_len);
+ } else if (!strcasecmp(var->name, "Content-Type"))
+ fprintf(f, "Content-Type: %s\r\n\r\n", var->value);
+ }
+
+ while ((res = fread(&buf, 1, 1, ser->f))) {
+ fwrite(&buf, 1, 1, f);
+ content_len--;
+ if (!content_len)
+ break;
+ }
+
+ if (fseek(f, SEEK_SET, 0)) {
+ ast_debug(1, "Failed to seek temp file back to beginning.\n");
+ fclose(f);
+ return NULL;
+ }
+
+ AST_RWLIST_RDLOCK(&post_mappings);
+ if (!(post_map = find_post_mapping(uri))) {
+ ast_debug(1, "%s is not a valid URI for POST\n", uri);
+ AST_RWLIST_UNLOCK(&post_mappings);
+ fclose(f);
+ *status = 404;
+ *title = ast_strdup("Not Found");
+ return ast_http_error(404, "Not Found", NULL, "The requested URL was not found on this server.");
+ }
+ post_dir = ast_strdupa(post_map->to);
+ post_map = NULL;
+ AST_RWLIST_UNLOCK(&post_mappings);
+
+ ast_debug(1, "Going to post files to dir %s\n", post_dir);
+
+ if (!(ctx = mm_context_new())) {
+ fclose(f);
+ return NULL;
+ }
+
+ mm_res = mm_parse_fileptr(ctx, f, MM_PARSE_LOOSE, 0);
+ fclose(f);
+ if (mm_res == -1) {
+ ast_log(LOG_ERROR, "Error parsing MIME data\n");
+ mm_context_free(ctx);
+ *status = 400;
+ *title = ast_strdup("Bad Request");
+ return ast_http_error(400, "Bad Request", NULL, "The was an error parsing the request.");
+ }
+
+ mm_res = mm_context_countparts(ctx);
+ if (!mm_res) {
+ ast_log(LOG_ERROR, "Invalid MIME data, found no parts!\n");
+ mm_context_free(ctx);
+ *status = 400;
+ *title = ast_strdup("Bad Request");
+ return ast_http_error(400, "Bad Request", NULL, "The was an error parsing the request.");
+ }
+
+ if (option_debug) {
+ if (mm_context_iscomposite(ctx))
+ ast_debug(1, "Found %d MIME parts\n", mm_res - 1);
+ else
+ ast_debug(1, "We have a flat (not multi-part) message\n");
+ }
+
+ for (i = 1; i < mm_res; i++) {
+ struct mm_mimepart *part;
+ char fn[PATH_MAX];
+
+ if (!(part = mm_context_getpart(ctx, i))) {
+ ast_debug(1, "Failed to get mime part num %d\n", i);
+ continue;
+ }
+
+ if (get_filename(part, fn, sizeof(fn))) {
+ ast_debug(1, "Failed to retrieve a filename for part num %d\n", i);
+ continue;
+ }
+
+ if (!part->type) {
+ ast_debug(1, "This part has no content struct?\n");
+ continue;
+ }
+
+ /* XXX This assumes the MIME part body is not encoded! */
+ post_raw(part, post_dir, fn);
+ }
+
+ mm_context_free(ctx);
+
+ *status = 200;
+ *title = ast_strdup("OK");
+ return ast_http_error(200, "OK", NULL, "File successfully uploaded.");
+}
+
+static struct ast_str *handle_uri(struct server_instance *ser, char *uri, int *status,
+ char **title, int *contentlength, struct ast_variable **cookies,
+ unsigned int *static_content)
+{
+ char *c;
+ struct ast_str *out = NULL;
+ char *params = uri;
+ struct ast_http_uri *urih=NULL;
+ int l;
+ struct ast_variable *vars=NULL, *v, *prev = NULL;
+ struct http_uri_redirect *redirect;
+
+ strsep(&params, "?");
+ /* Extract arguments from the request and store them in variables. */
+ if (params) {
+ char *var, *val;
+
+ while ((val = strsep(&params, "&"))) {
+ var = strsep(&val, "=");
+ if (val)
+ ast_uri_decode(val);
+ else
+ val = "";
+ ast_uri_decode(var);
+ if ((v = ast_variable_new(var, val, ""))) {
+ if (vars)
+ prev->next = v;
+ else
+ vars = v;
+ prev = v;
+ }
+ }
+ }
+ /*
+ * Append the cookies to the variables (the only reason to have them
+ * at the end is to avoid another pass of the cookies list to find
+ * the tail).
+ */
+ if (prev)
+ prev->next = *cookies;
+ else
+ vars = *cookies;
+ *cookies = NULL;
+ ast_uri_decode(uri);
+
+ AST_RWLIST_RDLOCK(&uri_redirects);
+ AST_RWLIST_TRAVERSE(&uri_redirects, redirect, entry) {
+ if (!strcasecmp(uri, redirect->target)) {
+ char buf[512];
+ snprintf(buf, sizeof(buf), "Location: %s\r\n", redirect->dest);
+ out = ast_http_error(302, "Moved Temporarily", buf,
+ "There is no spoon...");
+ *status = 302;
+ *title = ast_strdup("Moved Temporarily");
+ break;
+ }
+ }
+ AST_RWLIST_UNLOCK(&uri_redirects);
+ if (redirect)
+ goto cleanup;
+
+ /* We want requests to start with the prefix and '/' */
+ l = strlen(prefix);
+ if (l && !strncasecmp(uri, prefix, l) && uri[l] == '/') {
+ uri += l + 1;
+ /* scan registered uris to see if we match one. */
+ AST_RWLIST_RDLOCK(&uris);
+ AST_RWLIST_TRAVERSE(&uris, urih, entry) {
+ l = strlen(urih->uri);
+ c = uri + l; /* candidate */
+ if (strncasecmp(urih->uri, uri, l) /* no match */
+ || (*c && *c != '/')) /* substring */
+ continue;
+ if (*c == '/')
+ c++;
+ if (!*c || urih->has_subtree) {
+ uri = c;
+ break;
+ }
+ }
+ if (!urih)
+ AST_RWLIST_UNLOCK(&uris);
+ }
+ if (urih) {
+ if (urih->static_content)
+ *static_content = 1;
+ out = urih->callback(ser, uri, vars, status, title, contentlength);
+ AST_RWLIST_UNLOCK(&uris);
+ } else {
+ out = ast_http_error(404, "Not Found", NULL,
+ "The requested URL was not found on this server.");
+ *status = 404;
+ *title = ast_strdup("Not Found");
+ }
+
+cleanup:
+ ast_variables_destroy(vars);
+ return out;
+}
+
+#ifdef DO_SSL
+#if defined(HAVE_FUNOPEN)
+#define HOOK_T int
+#define LEN_T int
+#else
+#define HOOK_T ssize_t
+#define LEN_T size_t
+#endif
+/*!
+ * replacement read/write functions for SSL support.
+ * We use wrappers rather than SSL_read/SSL_write directly so
+ * we can put in some debugging.
+ */
+/*static HOOK_T ssl_read(void *cookie, char *buf, LEN_T len)
+{
+ int i = SSL_read(cookie, buf, len-1);
+#if 0
+ if (i >= 0)
+ buf[i] = '\0';
+ ast_verbose("ssl read size %d returns %d <%s>\n", (int)len, i, buf);
+#endif
+ return i;
+}
+
+static HOOK_T ssl_write(void *cookie, const char *buf, LEN_T len)
+{
+#if 0
+ char *s = alloca(len+1);
+ strncpy(s, buf, len);
+ s[len] = '\0';
+ ast_verbose("ssl write size %d <%s>\n", (int)len, s);
+#endif
+ return SSL_write(cookie, buf, len);
+}
+
+static int ssl_close(void *cookie)
+{
+ close(SSL_get_fd(cookie));
+ SSL_shutdown(cookie);
+ SSL_free(cookie);
+ return 0;
+}*/
+#endif /* DO_SSL */
+
+static void *httpd_helper_thread(void *data)
+{
+ char buf[4096];
+ char cookie[4096];
+ struct server_instance *ser = data;
+ struct ast_variable *var, *prev=NULL, *vars=NULL, *headers = NULL;
+ char *uri, *title=NULL;
+ int status = 200, contentlength = 0;
+ struct ast_str *out = NULL;
+ unsigned int static_content = 0;
+
+ if (!fgets(buf, sizeof(buf), ser->f))
+ goto done;
+
+ uri = ast_skip_nonblanks(buf); /* Skip method */
+ if (*uri)
+ *uri++ = '\0';
+
+ uri = ast_skip_blanks(uri); /* Skip white space */
+
+ if (*uri) { /* terminate at the first blank */
+ char *c = ast_skip_nonblanks(uri);
+ if (*c)
+ *c = '\0';
+ }
+
+ /* process "Cookie: " lines */
+ while (fgets(cookie, sizeof(cookie), ser->f)) {
+ char *vname, *vval;
+ int l;
+
+ /* Trim trailing characters */
+ ast_trim_blanks(cookie);
+ if (ast_strlen_zero(cookie))
+ break;
+ if (strncasecmp(cookie, "Cookie: ", 8)) {
+ char *name, *value;
+
+ value = ast_strdupa(cookie);
+ name = strsep(&value, ":");
+ if (!value)
+ continue;
+ value = ast_skip_blanks(value);
+ if (ast_strlen_zero(value))
+ continue;
+ var = ast_variable_new(name, value, "");
+ if (!var)
+ continue;
+ var->next = headers;
+ headers = var;
+ continue;
+ }
+
+ /* TODO - The cookie parsing code below seems to work
+ in IE6 and FireFox 1.5. However, it is not entirely
+ correct, and therefore may not work in all
+ circumstances.
+ For more details see RFC 2109 and RFC 2965 */
+
+ /* FireFox cookie strings look like:
+ Cookie: mansession_id="********"
+ InternetExplorer's look like:
+ Cookie: $Version="1"; mansession_id="********" */
+
+ /* If we got a FireFox cookie string, the name's right
+ after "Cookie: " */
+ vname = ast_skip_blanks(cookie + 8);
+
+ /* If we got an IE cookie string, we need to skip to
+ past the version to get to the name */
+ if (*vname == '$') {
+ strsep(&vname, ";");
+ if (!vname) /* no name ? */
+ continue;
+ vname = ast_skip_blanks(vname);
+ }
+ vval = strchr(vname, '=');
+ if (!vval)
+ continue;
+ /* Ditch the = and the quotes */
+ *vval++ = '\0';
+ if (*vval)
+ vval++;
+ if ( (l = strlen(vval)) )
+ vval[l - 1] = '\0'; /* trim trailing quote */
+ var = ast_variable_new(vname, vval, "");
+ if (var) {
+ if (prev)
+ prev->next = var;
+ else
+ vars = var;
+ prev = var;
+ }
+ }
+
+ if (!*uri)
+ out = ast_http_error(400, "Bad Request", NULL, "Invalid Request");
+ else if (!strcasecmp(buf, "post"))
+ out = handle_post(ser, uri, &status, &title, &contentlength, headers, vars);
+ else if (strcasecmp(buf, "get"))
+ out = ast_http_error(501, "Not Implemented", NULL,
+ "Attempt to use unimplemented / unsupported method");
+ else /* try to serve it */
+ out = handle_uri(ser, uri, &status, &title, &contentlength, &vars, &static_content);
+
+ /* If they aren't mopped up already, clean up the cookies */
+ if (vars)
+ ast_variables_destroy(vars);
+ /* Clean up all the header information pulled as well */
+ if (headers)
+ ast_variables_destroy(headers);
+
+ if (out) {
+ struct timeval tv = ast_tvnow();
+ char timebuf[256];
+ struct ast_tm tm;
+
+ ast_strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S %Z", ast_localtime(&tv, &tm, "GMT"));
+ fprintf(ser->f, "HTTP/1.1 %d %s\r\n"
+ "Server: Asterisk/%s\r\n"
+ "Date: %s\r\n"
+ "Connection: close\r\n"
+ "%s",
+ status, title ? title : "OK", ast_get_version(), timebuf,
+ static_content ? "" : "Cache-Control: no-cache, no-store\r\n");
+ /* We set the no-cache headers only for dynamic content.
+ * If you want to make sure the static file you requested is not from cache,
+ * append a random variable to your GET request. Ex: 'something.html?r=109987734'
+ */
+ if (!contentlength) { /* opaque body ? just dump it hoping it is properly formatted */
+ fprintf(ser->f, "%s", out->str);
+ } else {
+ char *tmp = strstr(out->str, "\r\n\r\n");
+
+ if (tmp) {
+ fprintf(ser->f, "Content-length: %d\r\n", contentlength);
+ /* first write the header, then the body */
+ fwrite(out->str, 1, (tmp + 4 - out->str), ser->f);
+ fwrite(tmp + 4, 1, contentlength, ser->f);
+ }
+ }
+ ast_free(out);
+ }
+ if (title)
+ ast_free(title);
+
+done:
+ fclose(ser->f);
+ ast_free(ser);
+ return NULL;
+}
+
+/*!
+ * \brief Add a new URI redirect
+ * The entries in the redirect list are sorted by length, just like the list
+ * of URI handlers.
+ */
+static void add_redirect(const char *value)
+{
+ char *target, *dest;
+ struct http_uri_redirect *redirect, *cur;
+ unsigned int target_len;
+ unsigned int total_len;
+
+ dest = ast_strdupa(value);
+ dest = ast_skip_blanks(dest);
+ target = strsep(&dest, " ");
+ target = ast_skip_blanks(target);
+ target = strsep(&target, " "); /* trim trailing whitespace */
+
+ if (!dest) {
+ ast_log(LOG_WARNING, "Invalid redirect '%s'\n", value);
+ return;
+ }
+
+ target_len = strlen(target) + 1;
+ total_len = sizeof(*redirect) + target_len + strlen(dest) + 1;
+
+ if (!(redirect = ast_calloc(1, total_len)))
+ return;
+
+ redirect->dest = redirect->target + target_len;
+ strcpy(redirect->target, target);
+ strcpy(redirect->dest, dest);
+
+ AST_RWLIST_WRLOCK(&uri_redirects);
+
+ target_len--; /* So we can compare directly with strlen() */
+ if ( AST_RWLIST_EMPTY(&uri_redirects)
+ || strlen(AST_RWLIST_FIRST(&uri_redirects)->target) <= target_len ) {
+ AST_RWLIST_INSERT_HEAD(&uri_redirects, redirect, entry);
+ AST_RWLIST_UNLOCK(&uri_redirects);
+ return;
+ }
+
+ AST_RWLIST_TRAVERSE(&uri_redirects, cur, entry) {
+ if ( AST_RWLIST_NEXT(cur, entry)
+ && strlen(AST_RWLIST_NEXT(cur, entry)->target) <= target_len ) {
+ AST_RWLIST_INSERT_AFTER(&uri_redirects, cur, redirect, entry);
+ AST_RWLIST_UNLOCK(&uri_redirects);
+ return;
+ }
+ }
+
+ AST_RWLIST_INSERT_TAIL(&uri_redirects, redirect, entry);
+
+ AST_RWLIST_UNLOCK(&uri_redirects);
+}
+
+static void destroy_post_mapping(struct ast_http_post_mapping *post_map)
+{
+ if (post_map->from)
+ ast_free(post_map->from);
+ if (post_map->to)
+ ast_free(post_map->to);
+ ast_free(post_map);
+}
+
+static void destroy_post_mappings(void)
+{
+ struct ast_http_post_mapping *post_map;
+
+ AST_RWLIST_WRLOCK(&post_mappings);
+ while ((post_map = AST_RWLIST_REMOVE_HEAD(&post_mappings, entry)))
+ destroy_post_mapping(post_map);
+ AST_RWLIST_UNLOCK(&post_mappings);
+}
+
+static void add_post_mapping(const char *from, const char *to)
+{
+ struct ast_http_post_mapping *post_map;
+
+ if (!(post_map = ast_calloc(1, sizeof(*post_map))))
+ return;
+
+ if (!(post_map->from = ast_strdup(from))) {
+ destroy_post_mapping(post_map);
+ return;
+ }
+
+ if (!(post_map->to = ast_strdup(to))) {
+ destroy_post_mapping(post_map);
+ return;
+ }
+
+ AST_RWLIST_WRLOCK(&post_mappings);
+ AST_RWLIST_INSERT_TAIL(&post_mappings, post_map, entry);
+ AST_RWLIST_UNLOCK(&post_mappings);
+}
+
+static int __ast_http_load(int reload)
+{
+ struct ast_config *cfg;
+ struct ast_variable *v;
+ int enabled=0;
+ int newenablestatic=0;
+ struct hostent *hp;
+ struct ast_hostent ahp;
+ char newprefix[MAX_PREFIX];
+ int have_sslbindaddr = 0;
+ struct http_uri_redirect *redirect;
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+
+ if ((cfg = ast_config_load("http.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
+ return 0;
+
+ /* default values */
+ memset(&http_desc.sin, 0, sizeof(http_desc.sin));
+ http_desc.sin.sin_port = htons(8088);
+
+ memset(&https_desc.sin, 0, sizeof(https_desc.sin));
+ https_desc.sin.sin_port = htons(8089);
+
+ strcpy(newprefix, DEFAULT_PREFIX);
+
+ http_tls_cfg.enabled = 0;
+ if (http_tls_cfg.certfile)
+ ast_free(http_tls_cfg.certfile);
+ http_tls_cfg.certfile = ast_strdup(AST_CERTFILE);
+ if (http_tls_cfg.cipher)
+ ast_free(http_tls_cfg.cipher);
+ http_tls_cfg.cipher = ast_strdup("");
+
+ AST_RWLIST_WRLOCK(&uri_redirects);
+ while ((redirect = AST_RWLIST_REMOVE_HEAD(&uri_redirects, entry)))
+ ast_free(redirect);
+ AST_RWLIST_UNLOCK(&uri_redirects);
+
+ destroy_post_mappings();
+
+ if (cfg) {
+ v = ast_variable_browse(cfg, "general");
+ for (; v; v = v->next) {
+ if (!strcasecmp(v->name, "enabled"))
+ enabled = ast_true(v->value);
+ else if (!strcasecmp(v->name, "sslenable"))
+ http_tls_cfg.enabled = ast_true(v->value);
+ else if (!strcasecmp(v->name, "sslbindport"))
+ https_desc.sin.sin_port = htons(atoi(v->value));
+ else if (!strcasecmp(v->name, "sslcert")) {
+ ast_free(http_tls_cfg.certfile);
+ http_tls_cfg.certfile = ast_strdup(v->value);
+ } else if (!strcasecmp(v->name, "sslcipher")) {
+ ast_free(http_tls_cfg.cipher);
+ http_tls_cfg.cipher = ast_strdup(v->value);
+ }
+ else if (!strcasecmp(v->name, "enablestatic"))
+ newenablestatic = ast_true(v->value);
+ else if (!strcasecmp(v->name, "bindport"))
+ http_desc.sin.sin_port = htons(atoi(v->value));
+ else if (!strcasecmp(v->name, "sslbindaddr")) {
+ if ((hp = ast_gethostbyname(v->value, &ahp))) {
+ memcpy(&https_desc.sin.sin_addr, hp->h_addr, sizeof(https_desc.sin.sin_addr));
+ have_sslbindaddr = 1;
+ } else {
+ ast_log(LOG_WARNING, "Invalid bind address '%s'\n", v->value);
+ }
+ } else if (!strcasecmp(v->name, "bindaddr")) {
+ if ((hp = ast_gethostbyname(v->value, &ahp))) {
+ memcpy(&http_desc.sin.sin_addr, hp->h_addr, sizeof(http_desc.sin.sin_addr));
+ } else {
+ ast_log(LOG_WARNING, "Invalid bind address '%s'\n", v->value);
+ }
+ } else if (!strcasecmp(v->name, "prefix")) {
+ if (!ast_strlen_zero(v->value)) {
+ newprefix[0] = '/';
+ ast_copy_string(newprefix + 1, v->value, sizeof(newprefix) - 1);
+ } else {
+ newprefix[0] = '\0';
+ }
+ } else if (!strcasecmp(v->name, "redirect")) {
+ add_redirect(v->value);
+ } else {
+ ast_log(LOG_WARNING, "Ignoring unknown option '%s' in http.conf\n", v->name);
+ }
+ }
+
+ for (v = ast_variable_browse(cfg, "post_mappings"); v; v = v->next)
+ add_post_mapping(v->name, v->value);
+
+ ast_config_destroy(cfg);
+ }
+ if (!have_sslbindaddr)
+ https_desc.sin.sin_addr = http_desc.sin.sin_addr;
+ if (enabled)
+ http_desc.sin.sin_family = https_desc.sin.sin_family = AF_INET;
+ if (strcmp(prefix, newprefix))
+ ast_copy_string(prefix, newprefix, sizeof(prefix));
+ enablestatic = newenablestatic;
+ server_start(&http_desc);
+ if (ssl_setup(https_desc.tls_cfg))
+ server_start(&https_desc);
+
+ return 0;
+}
+
+static char *handle_show_http(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_http_uri *urih;
+ struct http_uri_redirect *redirect;
+ struct ast_http_post_mapping *post_map;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "http show status";
+ e->usage =
+ "Usage: http show status\n"
+ " Lists status of internal HTTP engine\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+ ast_cli(a->fd, "HTTP Server Status:\n");
+ ast_cli(a->fd, "Prefix: %s\n", prefix);
+ if (!http_desc.oldsin.sin_family)
+ ast_cli(a->fd, "Server Disabled\n\n");
+ else {
+ ast_cli(a->fd, "Server Enabled and Bound to %s:%d\n\n",
+ ast_inet_ntoa(http_desc.oldsin.sin_addr),
+ ntohs(http_desc.oldsin.sin_port));
+ if (http_tls_cfg.enabled)
+ ast_cli(a->fd, "HTTPS Server Enabled and Bound to %s:%d\n\n",
+ ast_inet_ntoa(https_desc.oldsin.sin_addr),
+ ntohs(https_desc.oldsin.sin_port));
+ }
+
+ ast_cli(a->fd, "Enabled URI's:\n");
+ AST_RWLIST_RDLOCK(&uris);
+ if (AST_RWLIST_EMPTY(&uris)) {
+ ast_cli(a->fd, "None.\n");
+ } else {
+ AST_RWLIST_TRAVERSE(&uris, urih, entry)
+ ast_cli(a->fd, "%s/%s%s => %s\n", prefix, urih->uri, (urih->has_subtree ? "/..." : "" ), urih->description);
+ }
+ AST_RWLIST_UNLOCK(&uris);
+
+ ast_cli(a->fd, "\nEnabled Redirects:\n");
+ AST_RWLIST_RDLOCK(&uri_redirects);
+ AST_RWLIST_TRAVERSE(&uri_redirects, redirect, entry)
+ ast_cli(a->fd, " %s => %s\n", redirect->target, redirect->dest);
+ if (AST_RWLIST_EMPTY(&uri_redirects))
+ ast_cli(a->fd, " None.\n");
+ AST_RWLIST_UNLOCK(&uri_redirects);
+
+
+ ast_cli(a->fd, "\nPOST mappings:\n");
+ AST_RWLIST_RDLOCK(&post_mappings);
+ AST_LIST_TRAVERSE(&post_mappings, post_map, entry)
+ ast_cli(a->fd, "%s/%s => %s\n", prefix, post_map->from, post_map->to);
+ ast_cli(a->fd, "%s\n", AST_LIST_EMPTY(&post_mappings) ? "None.\n" : "");
+ AST_RWLIST_UNLOCK(&post_mappings);
+
+ return CLI_SUCCESS;
+}
+
+int ast_http_reload(void)
+{
+ return __ast_http_load(1);
+}
+
+static struct ast_cli_entry cli_http[] = {
+ AST_CLI_DEFINE(handle_show_http, "Display HTTP server status"),
+};
+
+int ast_http_init(void)
+{
+ mm_library_init();
+ mm_codec_registerdefaultcodecs();
+
+ ast_http_uri_link(&statusuri);
+ ast_http_uri_link(&staticuri);
+ ast_cli_register_multiple(cli_http, sizeof(cli_http) / sizeof(struct ast_cli_entry));
+
+ return __ast_http_load(0);
+}
diff --git a/trunk/main/image.c b/trunk/main/image.c
new file mode 100644
index 000000000..7096311a2
--- /dev/null
+++ b/trunk/main/image.c
@@ -0,0 +1,208 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006, 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.
+ */
+
+/*! \file
+ *
+ * \brief Image Management
+ *
+ * \author Mark Spencer <markster@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <signal.h>
+
+#include "asterisk/paths.h" /* use ast_config_AST_DATA_DIR */
+#include "asterisk/sched.h"
+#include "asterisk/channel.h"
+#include "asterisk/file.h"
+#include "asterisk/image.h"
+#include "asterisk/translate.h"
+#include "asterisk/cli.h"
+#include "asterisk/lock.h"
+
+/* XXX Why don't we just use the formats struct for this? */
+static AST_RWLIST_HEAD_STATIC(imagers, ast_imager);
+
+int ast_image_register(struct ast_imager *img)
+{
+ AST_RWLIST_WRLOCK(&imagers);
+ AST_RWLIST_INSERT_HEAD(&imagers, img, list);
+ AST_RWLIST_UNLOCK(&imagers);
+ ast_verb(2, "Registered format '%s' (%s)\n", img->name, img->desc);
+ return 0;
+}
+
+void ast_image_unregister(struct ast_imager *img)
+{
+ AST_RWLIST_WRLOCK(&imagers);
+ img = AST_RWLIST_REMOVE(&imagers, img, list);
+ AST_RWLIST_UNLOCK(&imagers);
+
+ if (img)
+ ast_verb(2, "Unregistered format '%s' (%s)\n", img->name, img->desc);
+}
+
+int ast_supports_images(struct ast_channel *chan)
+{
+ if (!chan || !chan->tech)
+ return 0;
+ if (!chan->tech->send_image)
+ return 0;
+ return 1;
+}
+
+static int file_exists(char *filename)
+{
+ int res;
+ struct stat st;
+ res = stat(filename, &st);
+ if (!res)
+ return st.st_size;
+ return 0;
+}
+
+static void make_filename(char *buf, int len, char *filename, const char *preflang, char *ext)
+{
+ if (filename[0] == '/') {
+ if (!ast_strlen_zero(preflang))
+ snprintf(buf, len, "%s-%s.%s", filename, preflang, ext);
+ else
+ snprintf(buf, len, "%s.%s", filename, ext);
+ } else {
+ if (!ast_strlen_zero(preflang))
+ snprintf(buf, len, "%s/%s/%s-%s.%s", ast_config_AST_DATA_DIR, "images", filename, preflang, ext);
+ else
+ snprintf(buf, len, "%s/%s/%s.%s", ast_config_AST_DATA_DIR, "images", filename, ext);
+ }
+}
+
+struct ast_frame *ast_read_image(char *filename, const char *preflang, int format)
+{
+ struct ast_imager *i;
+ char buf[256];
+ char tmp[80];
+ char *e;
+ struct ast_imager *found = NULL;
+ int fd;
+ int len=0;
+ struct ast_frame *f = NULL;
+
+ AST_RWLIST_RDLOCK(&imagers);
+ AST_RWLIST_TRAVERSE(&imagers, i, list) {
+ if (i->format & format) {
+ char *stringp=NULL;
+ ast_copy_string(tmp, i->exts, sizeof(tmp));
+ stringp=tmp;
+ e = strsep(&stringp, "|");
+ while (e) {
+ make_filename(buf, sizeof(buf), filename, preflang, e);
+ if ((len = file_exists(buf))) {
+ found = i;
+ break;
+ }
+ make_filename(buf, sizeof(buf), filename, NULL, e);
+ if ((len = file_exists(buf))) {
+ found = i;
+ break;
+ }
+ e = strsep(&stringp, "|");
+ }
+ }
+ if (found)
+ break;
+ }
+
+ if (found) {
+ fd = open(buf, O_RDONLY);
+ if (fd > -1) {
+ if (!found->identify || found->identify(fd)) {
+ /* Reset file pointer */
+ lseek(fd, 0, SEEK_SET);
+ f = found->read_image(fd,len);
+ } else
+ ast_log(LOG_WARNING, "%s does not appear to be a %s file\n", buf, found->name);
+ close(fd);
+ } else
+ ast_log(LOG_WARNING, "Unable to open '%s': %s\n", buf, strerror(errno));
+ } else
+ ast_log(LOG_WARNING, "Image file '%s' not found\n", filename);
+
+ AST_RWLIST_UNLOCK(&imagers);
+
+ return f;
+}
+
+int ast_send_image(struct ast_channel *chan, char *filename)
+{
+ struct ast_frame *f;
+ int res = -1;
+ if (chan->tech->send_image) {
+ f = ast_read_image(filename, chan->language, -1);
+ if (f) {
+ res = chan->tech->send_image(chan, f);
+ ast_frfree(f);
+ }
+ }
+ return res;
+}
+
+static char *handle_core_show_image_formats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+#define FORMAT "%10s %10s %50s %10s\n"
+#define FORMAT2 "%10s %10s %50s %10s\n"
+ struct ast_imager *i;
+ int count_fmt = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show image formats";
+ e->usage =
+ "Usage: core show image formats\n"
+ " Displays currently registered image formats (if any).\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+ ast_cli(a->fd, FORMAT, "Name", "Extensions", "Description", "Format");
+ ast_cli(a->fd, FORMAT, "----", "----------", "-----------", "------");
+ AST_RWLIST_RDLOCK(&imagers);
+ AST_RWLIST_TRAVERSE(&imagers, i, list) {
+ ast_cli(a->fd, FORMAT2, i->name, i->exts, i->desc, ast_getformatname(i->format));
+ count_fmt++;
+ }
+ AST_RWLIST_UNLOCK(&imagers);
+ ast_cli(a->fd, "\n%d image format%s registered.\n", count_fmt, count_fmt == 1 ? "" : "s");
+ return CLI_SUCCESS;
+}
+
+struct ast_cli_entry cli_image[] = {
+ AST_CLI_DEFINE(handle_core_show_image_formats, "Displays image formats")
+};
+
+int ast_image_init(void)
+{
+ ast_cli_register_multiple(cli_image, ARRAY_LEN(cli_image));
+ return 0;
+}
diff --git a/trunk/main/indications.c b/trunk/main/indications.c
new file mode 100644
index 000000000..e555d0357
--- /dev/null
+++ b/trunk/main/indications.c
@@ -0,0 +1,598 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2002, Pauline Middelink
+ *
+ *
+ * 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 Tone Management
+ *
+ * \author Pauline Middelink <middelink@polyware.nl>
+ *
+ * This set of function allow us to play a list of tones on a channel.
+ * Each element has two frequencies, which are mixed together and a
+ * duration. For silence both frequencies can be set to 0.
+ * The playtones can be given as a comma separated string.
+ *
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <math.h>
+
+#include "asterisk/lock.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/indications.h"
+#include "asterisk/frame.h"
+#include "asterisk/channel.h"
+#include "asterisk/utils.h"
+
+static int midi_tohz[128] = {
+ 8,8,9,9,10,10,11,12,12,13,14,
+ 15,16,17,18,19,20,21,23,24,25,
+ 27,29,30,32,34,36,38,41,43,46,
+ 48,51,55,58,61,65,69,73,77,82,
+ 87,92,97,103,110,116,123,130,138,146,
+ 155,164,174,184,195,207,220,233,246,261,
+ 277,293,311,329,349,369,391,415,440,466,
+ 493,523,554,587,622,659,698,739,783,830,
+ 880,932,987,1046,1108,1174,1244,1318,1396,1479,
+ 1567,1661,1760,1864,1975,2093,2217,2349,2489,2637,
+ 2793,2959,3135,3322,3520,3729,3951,4186,4434,4698,
+ 4978,5274,5587,5919,6271,6644,7040,7458,7902,8372,
+ 8869,9397,9956,10548,11175,11839,12543
+ };
+
+struct playtones_item {
+ int fac1;
+ int init_v2_1;
+ int init_v3_1;
+ int fac2;
+ int init_v2_2;
+ int init_v3_2;
+ int modulate;
+ int duration;
+};
+
+struct playtones_def {
+ int vol;
+ int reppos;
+ int nitems;
+ int interruptible;
+ struct playtones_item *items;
+};
+
+struct playtones_state {
+ int vol;
+ int v1_1;
+ int v2_1;
+ int v3_1;
+ int v1_2;
+ int v2_2;
+ int v3_2;
+ int reppos;
+ int nitems;
+ struct playtones_item *items;
+ int npos;
+ int oldnpos;
+ int pos;
+ int origwfmt;
+ struct ast_frame f;
+ unsigned char offset[AST_FRIENDLY_OFFSET];
+ short data[4000];
+};
+
+static void playtones_release(struct ast_channel *chan, void *params)
+{
+ struct playtones_state *ps = params;
+
+ if (chan)
+ ast_set_write_format(chan, ps->origwfmt);
+ if (ps->items)
+ ast_free(ps->items);
+
+ ast_free(ps);
+}
+
+static void * playtones_alloc(struct ast_channel *chan, void *params)
+{
+ struct playtones_def *pd = params;
+ struct playtones_state *ps = NULL;
+
+ if (!(ps = ast_calloc(1, sizeof(*ps))))
+ return NULL;
+
+ ps->origwfmt = chan->writeformat;
+
+ if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) {
+ ast_log(LOG_WARNING, "Unable to set '%s' to signed linear format (write)\n", chan->name);
+ playtones_release(NULL, ps);
+ ps = NULL;
+ } else {
+ ps->vol = pd->vol;
+ ps->reppos = pd->reppos;
+ ps->nitems = pd->nitems;
+ ps->items = pd->items;
+ ps->oldnpos = -1;
+ }
+
+ /* Let interrupts interrupt :) */
+ if (pd->interruptible)
+ ast_set_flag(chan, AST_FLAG_WRITE_INT);
+ else
+ ast_clear_flag(chan, AST_FLAG_WRITE_INT);
+
+ return ps;
+}
+
+static int playtones_generator(struct ast_channel *chan, void *data, int len, int samples)
+{
+ struct playtones_state *ps = data;
+ struct playtones_item *pi;
+ int x;
+ /* we need to prepare a frame with 16 * timelen samples as we're
+ * generating SLIN audio
+ */
+ len = samples * 2;
+ if (len > sizeof(ps->data) / 2 - 1) {
+ ast_log(LOG_WARNING, "Can't generate that much data!\n");
+ return -1;
+ }
+ memset(&ps->f, 0, sizeof(ps->f));
+
+ pi = &ps->items[ps->npos];
+ if (ps->oldnpos != ps->npos) {
+ /* Load new parameters */
+ ps->v1_1 = 0;
+ ps->v2_1 = pi->init_v2_1;
+ ps->v3_1 = pi->init_v3_1;
+ ps->v1_2 = 0;
+ ps->v2_2 = pi->init_v2_2;
+ ps->v3_2 = pi->init_v3_2;
+ ps->oldnpos = ps->npos;
+ }
+ for (x=0;x<len/2;x++) {
+ ps->v1_1 = ps->v2_1;
+ ps->v2_1 = ps->v3_1;
+ ps->v3_1 = (pi->fac1 * ps->v2_1 >> 15) - ps->v1_1;
+
+ ps->v1_2 = ps->v2_2;
+ ps->v2_2 = ps->v3_2;
+ ps->v3_2 = (pi->fac2 * ps->v2_2 >> 15) - ps->v1_2;
+ if (pi->modulate) {
+ int p;
+ p = ps->v3_2 - 32768;
+ if (p < 0) p = -p;
+ p = ((p * 9) / 10) + 1;
+ ps->data[x] = (ps->v3_1 * p) >> 15;
+ } else
+ ps->data[x] = ps->v3_1 + ps->v3_2;
+ }
+
+ ps->f.frametype = AST_FRAME_VOICE;
+ ps->f.subclass = AST_FORMAT_SLINEAR;
+ ps->f.datalen = len;
+ ps->f.samples = samples;
+ ps->f.offset = AST_FRIENDLY_OFFSET;
+ ps->f.data = ps->data;
+ ps->f.delivery.tv_sec = 0;
+ ps->f.delivery.tv_usec = 0;
+ ast_write(chan, &ps->f);
+
+ ps->pos += x;
+ if (pi->duration && ps->pos >= pi->duration * 8) { /* item finished? */
+ ps->pos = 0; /* start new item */
+ ps->npos++;
+ if (ps->npos >= ps->nitems) { /* last item? */
+ if (ps->reppos == -1) /* repeat set? */
+ return -1;
+ ps->npos = ps->reppos; /* redo from top */
+ }
+ }
+ return 0;
+}
+
+static struct ast_generator playtones = {
+ alloc: playtones_alloc,
+ release: playtones_release,
+ generate: playtones_generator,
+};
+
+int ast_playtones_start(struct ast_channel *chan, int vol, const char *playlst, int interruptible)
+{
+ char *s, *data = ast_strdupa(playlst); /* cute */
+ struct playtones_def d = { vol, -1, 0, 1, NULL};
+ char *stringp;
+ char *separator;
+
+ if (vol < 1)
+ d.vol = 7219; /* Default to -8db */
+
+ d.interruptible = interruptible;
+
+ stringp=data;
+ /* the stringp/data is not null here */
+ /* check if the data is separated with '|' or with ',' by default */
+ if (strchr(stringp,'|'))
+ separator = "|";
+ else
+ separator = ",";
+ s = strsep(&stringp,separator);
+ while (s && *s) {
+ int freq1, freq2, time, modulate=0, midinote=0;
+
+ if (s[0]=='!')
+ s++;
+ else if (d.reppos == -1)
+ d.reppos = d.nitems;
+ if (sscanf(s, "%d+%d/%d", &freq1, &freq2, &time) == 3) {
+ /* f1+f2/time format */
+ } else if (sscanf(s, "%d+%d", &freq1, &freq2) == 2) {
+ /* f1+f2 format */
+ time = 0;
+ } else if (sscanf(s, "%d*%d/%d", &freq1, &freq2, &time) == 3) {
+ /* f1*f2/time format */
+ modulate = 1;
+ } else if (sscanf(s, "%d*%d", &freq1, &freq2) == 2) {
+ /* f1*f2 format */
+ time = 0;
+ modulate = 1;
+ } else if (sscanf(s, "%d/%d", &freq1, &time) == 2) {
+ /* f1/time format */
+ freq2 = 0;
+ } else if (sscanf(s, "%d", &freq1) == 1) {
+ /* f1 format */
+ freq2 = 0;
+ time = 0;
+ } else if (sscanf(s, "M%d+M%d/%d", &freq1, &freq2, &time) == 3) {
+ /* Mf1+Mf2/time format */
+ midinote = 1;
+ } else if (sscanf(s, "M%d+M%d", &freq1, &freq2) == 2) {
+ /* Mf1+Mf2 format */
+ time = 0;
+ midinote = 1;
+ } else if (sscanf(s, "M%d*M%d/%d", &freq1, &freq2, &time) == 3) {
+ /* Mf1*Mf2/time format */
+ modulate = 1;
+ midinote = 1;
+ } else if (sscanf(s, "M%d*M%d", &freq1, &freq2) == 2) {
+ /* Mf1*Mf2 format */
+ time = 0;
+ modulate = 1;
+ midinote = 1;
+ } else if (sscanf(s, "M%d/%d", &freq1, &time) == 2) {
+ /* Mf1/time format */
+ freq2 = -1;
+ midinote = 1;
+ } else if (sscanf(s, "M%d", &freq1) == 1) {
+ /* Mf1 format */
+ freq2 = -1;
+ time = 0;
+ midinote = 1;
+ } else {
+ ast_log(LOG_WARNING,"%s: tone component '%s' of '%s' is no good\n",chan->name,s,playlst);
+ return -1;
+ }
+
+ if (midinote) {
+ /* midi notes must be between 0 and 127 */
+ if ((freq1 >= 0) && (freq1 <= 127))
+ freq1 = midi_tohz[freq1];
+ else
+ freq1 = 0;
+
+ if ((freq2 >= 0) && (freq2 <= 127))
+ freq2 = midi_tohz[freq2];
+ else
+ freq2 = 0;
+ }
+
+ if (!(d.items = ast_realloc(d.items, (d.nitems + 1) * sizeof(*d.items)))) {
+ return -1;
+ }
+ d.items[d.nitems].fac1 = 2.0 * cos(2.0 * M_PI * (freq1 / 8000.0)) * 32768.0;
+ d.items[d.nitems].init_v2_1 = sin(-4.0 * M_PI * (freq1 / 8000.0)) * d.vol;
+ d.items[d.nitems].init_v3_1 = sin(-2.0 * M_PI * (freq1 / 8000.0)) * d.vol;
+
+ d.items[d.nitems].fac2 = 2.0 * cos(2.0 * M_PI * (freq2 / 8000.0)) * 32768.0;
+ d.items[d.nitems].init_v2_2 = sin(-4.0 * M_PI * (freq2 / 8000.0)) * d.vol;
+ d.items[d.nitems].init_v3_2 = sin(-2.0 * M_PI * (freq2 / 8000.0)) * d.vol;
+ d.items[d.nitems].duration = time;
+ d.items[d.nitems].modulate = modulate;
+ d.nitems++;
+
+ s = strsep(&stringp,separator);
+ }
+
+ if (ast_activate_generator(chan, &playtones, &d)) {
+ ast_free(d.items);
+ return -1;
+ }
+ return 0;
+}
+
+void ast_playtones_stop(struct ast_channel *chan)
+{
+ ast_deactivate_generator(chan);
+}
+
+/*--------------------------------------------*/
+
+static AST_RWLIST_HEAD_STATIC(tone_zones, ind_tone_zone);
+static struct ind_tone_zone *current_tonezone;
+
+struct ind_tone_zone *ast_walk_indications(const struct ind_tone_zone *cur)
+{
+ struct ind_tone_zone *tz = NULL;
+
+ AST_RWLIST_RDLOCK(&tone_zones);
+ /* If cur is not NULL, then we have to iterate through - otherwise just return the first entry */
+ if (cur) {
+ AST_RWLIST_TRAVERSE(&tone_zones, tz, list) {
+ if (tz == cur)
+ break;
+ }
+ tz = AST_RWLIST_NEXT(tz, list);
+ } else {
+ tz = AST_RWLIST_FIRST(&tone_zones);
+ }
+ AST_RWLIST_UNLOCK(&tone_zones);
+
+ return tz;
+}
+
+/* Set global indication country */
+int ast_set_indication_country(const char *country)
+{
+ struct ind_tone_zone *zone = NULL;
+
+ /* If no country is specified or we are unable to find the zone, then return not found */
+ if (!country || !(zone = ast_get_indication_zone(country)))
+ return 1;
+
+ ast_verb(3, "Setting default indication country to '%s'\n", country);
+
+ /* Protect the current tonezone using the tone_zones lock as well */
+ AST_RWLIST_WRLOCK(&tone_zones);
+ current_tonezone = zone;
+ AST_RWLIST_UNLOCK(&tone_zones);
+
+ /* Zone was found */
+ return 0;
+}
+
+/* locate tone_zone, given the country. if country == NULL, use the default country */
+struct ind_tone_zone *ast_get_indication_zone(const char *country)
+{
+ struct ind_tone_zone *tz = NULL;
+ int alias_loop = 0;
+
+ AST_RWLIST_RDLOCK(&tone_zones);
+
+ if (!country) {
+ if (current_tonezone)
+ tz = current_tonezone;
+ else
+ tz = AST_LIST_FIRST(&tone_zones);
+ } else {
+ do {
+ AST_RWLIST_TRAVERSE(&tone_zones, tz, list) {
+ if (!strcasecmp(tz->country, country))
+ break;
+ }
+ if (!tz)
+ break;
+ /* If this is an alias then we have to search yet again otherwise we have found the zonezone */
+ if (tz->alias && tz->alias[0])
+ country = tz->alias;
+ else
+ break;
+ } while ((++alias_loop < 20) && tz);
+ }
+
+ AST_RWLIST_UNLOCK(&tone_zones);
+
+ /* If we reached the maximum loops to find the proper country via alias, print out a notice */
+ if (alias_loop == 20)
+ ast_log(LOG_NOTICE, "Alias loop for '%s' is bonkers\n", country);
+
+ return tz;
+}
+
+/* locate a tone_zone_sound, given the tone_zone. if tone_zone == NULL, use the default tone_zone */
+struct ind_tone_zone_sound *ast_get_indication_tone(const struct ind_tone_zone *zone, const char *indication)
+{
+ struct ind_tone_zone_sound *ts = NULL;
+
+ AST_RWLIST_RDLOCK(&tone_zones);
+
+ /* If no zone is already specified we need to try to pick one */
+ if (!zone) {
+ if (current_tonezone) {
+ zone = current_tonezone;
+ } else if (!(zone = AST_LIST_FIRST(&tone_zones))) {
+ /* No zone has been found ;( */
+ AST_RWLIST_UNLOCK(&tone_zones);
+ return NULL;
+ }
+ }
+
+ /* Look through list of tones in the zone searching for the right one */
+ for (ts = zone->tones; ts; ts = ts->next) {
+ if (!strcasecmp(ts->name, indication))
+ break;
+ }
+
+ AST_RWLIST_UNLOCK(&tone_zones);
+
+ return ts;
+}
+
+/* helper function to delete a tone_zone in its entirety */
+static inline void free_zone(struct ind_tone_zone* zone)
+{
+ while (zone->tones) {
+ struct ind_tone_zone_sound *tmp = zone->tones->next;
+ ast_free((void *)zone->tones->name);
+ ast_free((void *)zone->tones->data);
+ ast_free(zone->tones);
+ zone->tones = tmp;
+ }
+
+ if (zone->ringcadence)
+ ast_free(zone->ringcadence);
+
+ ast_free(zone);
+}
+
+/*--------------------------------------------*/
+
+/* add a new country, if country exists, it will be replaced. */
+int ast_register_indication_country(struct ind_tone_zone *zone)
+{
+ struct ind_tone_zone *tz = NULL;
+
+ AST_RWLIST_WRLOCK(&tone_zones);
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&tone_zones, tz, list) {
+ /* If this is not the same zone, then just continue to the next entry */
+ if (strcasecmp(zone->country, tz->country))
+ continue;
+ /* If this zone we are going to remove is the current default then make the new zone the default */
+ if (tz == current_tonezone)
+ current_tonezone = zone;
+ /* Remove from the linked list */
+ AST_RWLIST_REMOVE_CURRENT(list);
+ /* Finally free the zone itself */
+ free_zone(tz);
+ break;
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+
+ /* Add zone to the list */
+ AST_RWLIST_INSERT_TAIL(&tone_zones, zone, list);
+
+ /* It's all over. */
+ AST_RWLIST_UNLOCK(&tone_zones);
+
+ ast_verb(3, "Registered indication country '%s'\n", zone->country);
+
+ return 0;
+}
+
+/* remove an existing country and all its indications, country must exist.
+ * Also, all countries which are an alias for the specified country are removed. */
+int ast_unregister_indication_country(const char *country)
+{
+ struct ind_tone_zone *tz = NULL;
+ int res = -1;
+
+ AST_RWLIST_WRLOCK(&tone_zones);
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&tone_zones, tz, list) {
+ if (country && (strcasecmp(country, tz->country) && strcasecmp(country, tz->alias)))
+ continue;
+ /* If this tonezone is the current default then unset it */
+ if (tz == current_tonezone) {
+ ast_log(LOG_NOTICE,"Removed default indication country '%s'\n", tz->country);
+ current_tonezone = NULL;
+ }
+ /* Remove from the list */
+ AST_RWLIST_REMOVE_CURRENT(list);
+ ast_verb(3, "Unregistered indication country '%s'\n", tz->country);
+ free_zone(tz);
+ res = 0;
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+ AST_RWLIST_UNLOCK(&tone_zones);
+
+ return res;
+}
+
+/* add a new indication to a tone_zone. tone_zone must exist. if the indication already
+ * exists, it will be replaced. */
+int ast_register_indication(struct ind_tone_zone *zone, const char *indication, const char *tonelist)
+{
+ struct ind_tone_zone_sound *ts, *ps;
+
+ /* is it an alias? stop */
+ if (zone->alias[0])
+ return -1;
+
+ AST_RWLIST_WRLOCK(&tone_zones);
+ for (ps=NULL,ts=zone->tones; ts; ps=ts,ts=ts->next) {
+ if (strcasecmp(indication,ts->name)==0) {
+ /* indication already there, replace */
+ ast_free((void*)ts->name);
+ ast_free((void*)ts->data);
+ break;
+ }
+ }
+ if (!ts) {
+ /* not there, we have to add */
+ if (!(ts = ast_malloc(sizeof(*ts)))) {
+ AST_RWLIST_UNLOCK(&tone_zones);
+ return -2;
+ }
+ ts->next = NULL;
+ }
+ if (!(ts->name = ast_strdup(indication)) || !(ts->data = ast_strdup(tonelist))) {
+ AST_RWLIST_UNLOCK(&tone_zones);
+ return -2;
+ }
+ if (ps)
+ ps->next = ts;
+ else
+ zone->tones = ts;
+ AST_RWLIST_UNLOCK(&tone_zones);
+ return 0;
+}
+
+/* remove an existing country's indication. Both country and indication must exist */
+int ast_unregister_indication(struct ind_tone_zone *zone, const char *indication)
+{
+ struct ind_tone_zone_sound *ts,*ps = NULL, *tmp;
+ int res = -1;
+
+ /* is it an alias? stop */
+ if (zone->alias[0])
+ return -1;
+
+ AST_RWLIST_WRLOCK(&tone_zones);
+ ts = zone->tones;
+ while (ts) {
+ if (strcasecmp(indication,ts->name)==0) {
+ /* indication found */
+ tmp = ts->next;
+ if (ps)
+ ps->next = tmp;
+ else
+ zone->tones = tmp;
+ ast_free((void*)ts->name);
+ ast_free((void*)ts->data);
+ ast_free(ts);
+ ts = tmp;
+ res = 0;
+ }
+ else {
+ /* next zone please */
+ ps = ts;
+ ts = ts->next;
+ }
+ }
+ /* indication not found, goodbye */
+ AST_RWLIST_UNLOCK(&tone_zones);
+ return res;
+}
diff --git a/trunk/main/io.c b/trunk/main/io.c
new file mode 100644
index 000000000..68b73d26c
--- /dev/null
+++ b/trunk/main/io.c
@@ -0,0 +1,381 @@
+/*
+ * 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.
+ */
+
+/*! \file
+ *
+ * \brief I/O Managment (Derived from Cheops-NG)
+ *
+ * \author Mark Spencer <markster@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <termios.h>
+#include <sys/ioctl.h>
+
+#include "asterisk/io.h"
+#include "asterisk/utils.h"
+
+#ifdef DEBUG_IO
+#define DEBUG DEBUG_M
+#else
+#define DEBUG(a)
+#endif
+
+/*! \brief
+ * Kept for each file descriptor
+ */
+struct io_rec {
+ ast_io_cb callback; /*!< What is to be called */
+ void *data; /*!< Data to be passed */
+ int *id; /*!< ID number */
+};
+
+/* These two arrays are keyed with
+ the same index. it's too bad that
+ pollfd doesn't have a callback field
+ or something like that. They grow as
+ needed, by GROW_SHRINK_SIZE structures
+ at once */
+
+#define GROW_SHRINK_SIZE 512
+
+/*! \brief Global IO variables are now in a struct in order to be
+ made threadsafe */
+struct io_context {
+ struct pollfd *fds; /*!< Poll structure */
+ struct io_rec *ior; /*!< Associated I/O records */
+ unsigned int fdcnt; /*!< First available fd */
+ unsigned int maxfdcnt; /*!< Maximum available fd */
+ int current_ioc; /*!< Currently used io callback */
+ int needshrink; /*!< Whether something has been deleted */
+};
+
+/*! \brief Create an I/O context */
+struct io_context *io_context_create(void)
+{
+ struct io_context *tmp = NULL;
+
+ if (!(tmp = ast_malloc(sizeof(*tmp))))
+ return NULL;
+
+ tmp->needshrink = 0;
+ tmp->fdcnt = 0;
+ tmp->maxfdcnt = GROW_SHRINK_SIZE/2;
+ tmp->current_ioc = -1;
+
+ if (!(tmp->fds = ast_calloc(1, (GROW_SHRINK_SIZE / 2) * sizeof(*tmp->fds)))) {
+ ast_free(tmp);
+ tmp = NULL;
+ } else {
+ if (!(tmp->ior = ast_calloc(1, (GROW_SHRINK_SIZE / 2) * sizeof(*tmp->ior)))) {
+ ast_free(tmp->fds);
+ ast_free(tmp);
+ tmp = NULL;
+ }
+ }
+
+ return tmp;
+}
+
+void io_context_destroy(struct io_context *ioc)
+{
+ /* Free associated memory with an I/O context */
+ if (ioc->fds)
+ ast_free(ioc->fds);
+ if (ioc->ior)
+ ast_free(ioc->ior);
+
+ ast_free(ioc);
+}
+
+/*! \brief
+ * Grow the size of our arrays.
+ * \return 0 on success or -1 on failure
+ */
+static int io_grow(struct io_context *ioc)
+{
+ void *tmp;
+
+ DEBUG(ast_debug(1, "io_grow()\n"));
+
+ ioc->maxfdcnt += GROW_SHRINK_SIZE;
+
+ if ((tmp = ast_realloc(ioc->ior, (ioc->maxfdcnt + 1) * sizeof(*ioc->ior)))) {
+ ioc->ior = tmp;
+ if ((tmp = ast_realloc(ioc->fds, (ioc->maxfdcnt + 1) * sizeof(*ioc->fds)))) {
+ ioc->fds = tmp;
+ } else {
+ /*
+ * Failed to allocate enough memory for the pollfd. Not
+ * really any need to shrink back the iorec's as we'll
+ * probably want to grow them again soon when more memory
+ * is available, and then they'll already be the right size
+ */
+ ioc->maxfdcnt -= GROW_SHRINK_SIZE;
+ return -1;
+ }
+ } else {
+ /*
+ * Memory allocation failure. We return to the old size, and
+ * return a failure
+ */
+ ioc->maxfdcnt -= GROW_SHRINK_SIZE;
+ return -1;
+ }
+
+ return 0;
+}
+
+/*! \brief
+ * Add a new I/O entry for this file descriptor
+ * with the given event mask, to call callback with
+ * data as an argument.
+ * \return Returns NULL on failure.
+ */
+int *ast_io_add(struct io_context *ioc, int fd, ast_io_cb callback, short events, void *data)
+{
+ int *ret;
+
+ DEBUG(ast_debug(1, "ast_io_add()\n"));
+
+ if (ioc->fdcnt >= ioc->maxfdcnt) {
+ /*
+ * We don't have enough space for this entry. We need to
+ * reallocate maxfdcnt poll fd's and io_rec's, or back out now.
+ */
+ if (io_grow(ioc))
+ return NULL;
+ }
+
+ /*
+ * At this point, we've got sufficiently large arrays going
+ * and we can make an entry for it in the pollfd and io_r
+ * structures.
+ */
+ ioc->fds[ioc->fdcnt].fd = fd;
+ ioc->fds[ioc->fdcnt].events = events;
+ ioc->fds[ioc->fdcnt].revents = 0;
+ ioc->ior[ioc->fdcnt].callback = callback;
+ ioc->ior[ioc->fdcnt].data = data;
+
+ if (!(ioc->ior[ioc->fdcnt].id = ast_malloc(sizeof(*ioc->ior[ioc->fdcnt].id)))) {
+ /* Bonk if we couldn't allocate an int */
+ return NULL;
+ }
+
+ *(ioc->ior[ioc->fdcnt].id) = ioc->fdcnt;
+ ret = ioc->ior[ioc->fdcnt].id;
+ ioc->fdcnt++;
+
+ return ret;
+}
+
+int *ast_io_change(struct io_context *ioc, int *id, int fd, ast_io_cb callback, short events, void *data)
+{
+ /* If this id exceeds our file descriptor count it doesn't exist here */
+ if (*id > ioc->fdcnt)
+ return NULL;
+
+ if (fd > -1)
+ ioc->fds[*id].fd = fd;
+ if (callback)
+ ioc->ior[*id].callback = callback;
+ if (events)
+ ioc->fds[*id].events = events;
+ if (data)
+ ioc->ior[*id].data = data;
+
+ return id;
+}
+
+static int io_shrink(struct io_context *ioc)
+{
+ int getfrom, putto = 0;
+
+ /*
+ * Bring the fields from the very last entry to cover over
+ * the entry we are removing, then decrease the size of the
+ * arrays by one.
+ */
+ for (getfrom = 0; getfrom < ioc->fdcnt; getfrom++) {
+ if (ioc->ior[getfrom].id) {
+ /* In use, save it */
+ if (getfrom != putto) {
+ ioc->fds[putto] = ioc->fds[getfrom];
+ ioc->ior[putto] = ioc->ior[getfrom];
+ *(ioc->ior[putto].id) = putto;
+ }
+ putto++;
+ }
+ }
+ ioc->fdcnt = putto;
+ ioc->needshrink = 0;
+ /* FIXME: We should free some memory if we have lots of unused
+ io structs */
+ return 0;
+}
+
+int ast_io_remove(struct io_context *ioc, int *_id)
+{
+ int x;
+
+ if (!_id) {
+ ast_log(LOG_WARNING, "Asked to remove NULL?\n");
+ return -1;
+ }
+
+ for (x = 0; x < ioc->fdcnt; x++) {
+ if (ioc->ior[x].id == _id) {
+ /* Free the int immediately and set to NULL so we know it's unused now */
+ ast_free(ioc->ior[x].id);
+ ioc->ior[x].id = NULL;
+ ioc->fds[x].events = 0;
+ ioc->fds[x].revents = 0;
+ ioc->needshrink = 1;
+ if (ioc->current_ioc == -1)
+ io_shrink(ioc);
+ return 0;
+ }
+ }
+
+ ast_log(LOG_NOTICE, "Unable to remove unknown id %p\n", _id);
+
+ return -1;
+}
+
+/*! \brief
+ * Make the poll call, and call
+ * the callbacks for anything that needs
+ * to be handled
+ */
+int ast_io_wait(struct io_context *ioc, int howlong)
+{
+ int res, x, origcnt;
+
+ DEBUG(ast_debug(1, "ast_io_wait()\n"));
+
+ if ((res = poll(ioc->fds, ioc->fdcnt, howlong)) <= 0)
+ return res;
+
+ /* At least one event tripped */
+ origcnt = ioc->fdcnt;
+ for (x = 0; x < origcnt; x++) {
+ /* Yes, it is possible for an entry to be deleted and still have an
+ event waiting if it occurs after the original calling id */
+ if (ioc->fds[x].revents && ioc->ior[x].id) {
+ /* There's an event waiting */
+ ioc->current_ioc = *ioc->ior[x].id;
+ if (ioc->ior[x].callback) {
+ if (!ioc->ior[x].callback(ioc->ior[x].id, ioc->fds[x].fd, ioc->fds[x].revents, ioc->ior[x].data)) {
+ /* Time to delete them since they returned a 0 */
+ ast_io_remove(ioc, ioc->ior[x].id);
+ }
+ }
+ ioc->current_ioc = -1;
+ }
+ }
+
+ if (ioc->needshrink)
+ io_shrink(ioc);
+
+ return res;
+}
+
+void ast_io_dump(struct io_context *ioc)
+{
+ /*
+ * Print some debugging information via
+ * the logger interface
+ */
+ int x;
+
+ ast_debug(1, "Asterisk IO Dump: %d entries, %d max entries\n", ioc->fdcnt, ioc->maxfdcnt);
+ ast_debug(1, "================================================\n");
+ ast_debug(1, "| ID FD Callback Data Events |\n");
+ ast_debug(1, "+------+------+-----------+-----------+--------+\n");
+ for (x = 0; x < ioc->fdcnt; x++) {
+ ast_debug(1, "| %.4d | %.4d | %p | %p | %.6x |\n",
+ *ioc->ior[x].id,
+ ioc->fds[x].fd,
+ ioc->ior[x].callback,
+ ioc->ior[x].data,
+ ioc->fds[x].events);
+ }
+ ast_debug(1, "================================================\n");
+}
+
+/* Unrelated I/O functions */
+
+int ast_hide_password(int fd)
+{
+ struct termios tios;
+ int res;
+ int old;
+ if (!isatty(fd))
+ return -1;
+ res = tcgetattr(fd, &tios);
+ if (res < 0)
+ return -1;
+ old = tios.c_lflag & (ECHO | ECHONL);
+ tios.c_lflag &= ~ECHO;
+ tios.c_lflag |= ECHONL;
+ res = tcsetattr(fd, TCSAFLUSH, &tios);
+ if (res < 0)
+ return -1;
+ return old;
+}
+
+int ast_restore_tty(int fd, int oldstate)
+{
+ int res;
+ struct termios tios;
+ if (oldstate < 0)
+ return 0;
+ res = tcgetattr(fd, &tios);
+ if (res < 0)
+ return -1;
+ tios.c_lflag &= ~(ECHO | ECHONL);
+ tios.c_lflag |= oldstate;
+ res = tcsetattr(fd, TCSAFLUSH, &tios);
+ if (res < 0)
+ return -1;
+ return 0;
+}
+
+int ast_get_termcols(int fd)
+{
+ struct winsize win;
+ int cols = 0;
+
+ if (!isatty(fd))
+ return -1;
+
+ if ( ioctl(fd, TIOCGWINSZ, &win) != -1 ) {
+ if ( !cols && win.ws_col > 0 )
+ cols = (int) win.ws_col;
+ } else {
+ /* assume 80 characters if the ioctl fails for some reason */
+ cols = 80;
+ }
+
+ return cols;
+}
+
diff --git a/trunk/main/jitterbuf.c b/trunk/main/jitterbuf.c
new file mode 100644
index 000000000..27b93a659
--- /dev/null
+++ b/trunk/main/jitterbuf.c
@@ -0,0 +1,844 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2004-2005, Horizon Wimba, Inc.
+ *
+ * Contributors:
+ * Steve Kann <stevek@stevek.com>
+ *
+ * A license has been granted to Digium (via disclaimer) for the use of
+ * this code.
+ *
+ * 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 jitterbuf: an application-independent jitterbuffer
+ * \author Steve Kann <stevek@stevek.com>
+ *
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "jitterbuf.h"
+#include "asterisk/utils.h"
+
+/*! define these here, just for ancient compiler systems */
+#define JB_LONGMAX 2147483647L
+#define JB_LONGMIN (-JB_LONGMAX - 1L)
+
+#define jb_warn(...) (warnf ? warnf(__VA_ARGS__) : (void)0)
+#define jb_err(...) (errf ? errf(__VA_ARGS__) : (void)0)
+#define jb_dbg(...) (dbgf ? dbgf(__VA_ARGS__) : (void)0)
+
+#ifdef DEEP_DEBUG
+#define jb_dbg2(...) (dbgf ? dbgf(__VA_ARGS__) : (void)0)
+#else
+#define jb_dbg2(...) ((void)0)
+#endif
+
+static jb_output_function_t warnf, errf, dbgf;
+
+void jb_setoutput(jb_output_function_t err, jb_output_function_t warn, jb_output_function_t dbg)
+{
+ errf = err;
+ warnf = warn;
+ dbgf = dbg;
+}
+
+static void increment_losspct(jitterbuf *jb)
+{
+ jb->info.losspct = (100000 + 499 * jb->info.losspct)/500;
+}
+
+static void decrement_losspct(jitterbuf *jb)
+{
+ jb->info.losspct = (499 * jb->info.losspct)/500;
+}
+
+void jb_reset(jitterbuf *jb)
+{
+ /* only save settings */
+ jb_conf s = jb->info.conf;
+ memset(jb, 0, sizeof(*jb));
+ jb->info.conf = s;
+
+ /* initialize length, using the default value */
+ jb->info.current = jb->info.target = jb->info.conf.target_extra = JB_TARGET_EXTRA;
+ jb->info.silence_begin_ts = -1;
+}
+
+jitterbuf * jb_new()
+{
+ jitterbuf *jb;
+
+ if (!(jb = ast_malloc(sizeof(*jb))))
+ return NULL;
+
+ jb_reset(jb);
+
+ jb_dbg2("jb_new() = %x\n", jb);
+ return jb;
+}
+
+void jb_destroy(jitterbuf *jb)
+{
+ jb_frame *frame;
+ jb_dbg2("jb_destroy(%x)\n", jb);
+
+ /* free all the frames on the "free list" */
+ frame = jb->free;
+ while (frame != NULL) {
+ jb_frame *next = frame->next;
+ ast_free(frame);
+ frame = next;
+ }
+
+ /* free ourselves! */
+ ast_free(jb);
+}
+
+
+
+#if 0
+static int longcmp(const void *a, const void *b)
+{
+ return *(long *)a - *(long *)b;
+}
+#endif
+
+/*! \brief simple history manipulation
+ \note maybe later we can make the history buckets variable size, or something? */
+/* drop parameter determines whether we will drop outliers to minimize
+ * delay */
+static int history_put(jitterbuf *jb, long ts, long now, long ms)
+{
+ long delay = now - (ts - jb->info.resync_offset);
+ long threshold = 2 * jb->info.jitter + jb->info.conf.resync_threshold;
+ long kicked;
+
+ /* don't add special/negative times to history */
+ if (ts <= 0)
+ return 0;
+
+ /* check for drastic change in delay */
+ if (jb->info.conf.resync_threshold != -1) {
+ if (abs(delay - jb->info.last_delay) > threshold) {
+ jb->info.cnt_delay_discont++;
+ if (jb->info.cnt_delay_discont > 3) {
+ /* resync the jitterbuffer */
+ jb->info.cnt_delay_discont = 0;
+ jb->hist_ptr = 0;
+ jb->hist_maxbuf_valid = 0;
+
+ jb_warn("Resyncing the jb. last_delay %ld, this delay %ld, threshold %ld, new offset %ld\n", jb->info.last_delay, delay, threshold, ts - now);
+ jb->info.resync_offset = ts - now;
+ jb->info.last_delay = delay = 0; /* after resync, frame is right on time */
+ } else {
+ return -1;
+ }
+ } else {
+ jb->info.last_delay = delay;
+ jb->info.cnt_delay_discont = 0;
+ }
+ }
+
+ kicked = jb->history[jb->hist_ptr % JB_HISTORY_SZ];
+
+ jb->history[(jb->hist_ptr++) % JB_HISTORY_SZ] = delay;
+
+ /* optimization; the max/min buffers don't need to be recalculated, if this packet's
+ * entry doesn't change them. This happens if this packet is not involved, _and_ any packet
+ * that got kicked out of the history is also not involved
+ * We do a number of comparisons, but it's probably still worthwhile, because it will usually
+ * succeed, and should be a lot faster than going through all 500 packets in history */
+ if (!jb->hist_maxbuf_valid)
+ return 0;
+
+ /* don't do this until we've filled history
+ * (reduces some edge cases below) */
+ if (jb->hist_ptr < JB_HISTORY_SZ)
+ goto invalidate;
+
+ /* if the new delay would go into min */
+ if (delay < jb->hist_minbuf[JB_HISTORY_MAXBUF_SZ-1])
+ goto invalidate;
+
+ /* or max.. */
+ if (delay > jb->hist_maxbuf[JB_HISTORY_MAXBUF_SZ-1])
+ goto invalidate;
+
+ /* or the kicked delay would be in min */
+ if (kicked <= jb->hist_minbuf[JB_HISTORY_MAXBUF_SZ-1])
+ goto invalidate;
+
+ if (kicked >= jb->hist_maxbuf[JB_HISTORY_MAXBUF_SZ-1])
+ goto invalidate;
+
+ /* if we got here, we don't need to invalidate, 'cause this delay didn't
+ * affect things */
+ return 0;
+ /* end optimization */
+
+
+invalidate:
+ jb->hist_maxbuf_valid = 0;
+ return 0;
+}
+
+static void history_calc_maxbuf(jitterbuf *jb)
+{
+ int i,j;
+
+ if (jb->hist_ptr == 0)
+ return;
+
+
+ /* initialize maxbuf/minbuf to the latest value */
+ for (i=0;i<JB_HISTORY_MAXBUF_SZ;i++) {
+/*
+ * jb->hist_maxbuf[i] = jb->history[(jb->hist_ptr-1) % JB_HISTORY_SZ];
+ * jb->hist_minbuf[i] = jb->history[(jb->hist_ptr-1) % JB_HISTORY_SZ];
+ */
+ jb->hist_maxbuf[i] = JB_LONGMIN;
+ jb->hist_minbuf[i] = JB_LONGMAX;
+ }
+
+ /* use insertion sort to populate maxbuf */
+ /* we want it to be the top "n" values, in order */
+
+ /* start at the beginning, or JB_HISTORY_SZ frames ago */
+ i = (jb->hist_ptr > JB_HISTORY_SZ) ? (jb->hist_ptr - JB_HISTORY_SZ) : 0;
+
+ for (;i<jb->hist_ptr;i++) {
+ long toins = jb->history[i % JB_HISTORY_SZ];
+
+ /* if the maxbuf should get this */
+ if (toins > jb->hist_maxbuf[JB_HISTORY_MAXBUF_SZ-1]) {
+
+ /* insertion-sort it into the maxbuf */
+ for (j=0;j<JB_HISTORY_MAXBUF_SZ;j++) {
+ /* found where it fits */
+ if (toins > jb->hist_maxbuf[j]) {
+ /* move over */
+ memmove(jb->hist_maxbuf + j + 1, jb->hist_maxbuf + j, (JB_HISTORY_MAXBUF_SZ - (j + 1)) * sizeof(jb->hist_maxbuf[0]));
+ /* insert */
+ jb->hist_maxbuf[j] = toins;
+
+ break;
+ }
+ }
+ }
+
+ /* if the minbuf should get this */
+ if (toins < jb->hist_minbuf[JB_HISTORY_MAXBUF_SZ-1]) {
+
+ /* insertion-sort it into the maxbuf */
+ for (j=0;j<JB_HISTORY_MAXBUF_SZ;j++) {
+ /* found where it fits */
+ if (toins < jb->hist_minbuf[j]) {
+ /* move over */
+ memmove(jb->hist_minbuf + j + 1, jb->hist_minbuf + j, (JB_HISTORY_MAXBUF_SZ - (j + 1)) * sizeof(jb->hist_minbuf[0]));
+ /* insert */
+ jb->hist_minbuf[j] = toins;
+
+ break;
+ }
+ }
+ }
+
+ if (0) {
+ int k;
+ fprintf(stderr, "toins = %ld\n", toins);
+ fprintf(stderr, "maxbuf =");
+ for (k=0;k<JB_HISTORY_MAXBUF_SZ;k++)
+ fprintf(stderr, "%ld ", jb->hist_maxbuf[k]);
+ fprintf(stderr, "\nminbuf =");
+ for (k=0;k<JB_HISTORY_MAXBUF_SZ;k++)
+ fprintf(stderr, "%ld ", jb->hist_minbuf[k]);
+ fprintf(stderr, "\n");
+ }
+ }
+
+ jb->hist_maxbuf_valid = 1;
+}
+
+static void history_get(jitterbuf *jb)
+{
+ long max, min, jitter;
+ int index;
+ int count;
+
+ if (!jb->hist_maxbuf_valid)
+ history_calc_maxbuf(jb);
+
+ /* count is how many items in history we're examining */
+ count = (jb->hist_ptr < JB_HISTORY_SZ) ? jb->hist_ptr : JB_HISTORY_SZ;
+
+ /* index is the "n"ths highest/lowest that we'll look for */
+ index = count * JB_HISTORY_DROPPCT / 100;
+
+ /* sanity checks for index */
+ if (index > (JB_HISTORY_MAXBUF_SZ - 1))
+ index = JB_HISTORY_MAXBUF_SZ - 1;
+
+
+ if (index < 0) {
+ jb->info.min = 0;
+ jb->info.jitter = 0;
+ return;
+ }
+
+ max = jb->hist_maxbuf[index];
+ min = jb->hist_minbuf[index];
+
+ jitter = max - min;
+
+ /* these debug stmts compare the difference between looking at the absolute jitter, and the
+ * values we get by throwing away the outliers */
+ /*
+ fprintf(stderr, "[%d] min=%d, max=%d, jitter=%d\n", index, min, max, jitter);
+ fprintf(stderr, "[%d] min=%d, max=%d, jitter=%d\n", 0, jb->hist_minbuf[0], jb->hist_maxbuf[0], jb->hist_maxbuf[0]-jb->hist_minbuf[0]);
+ */
+
+ jb->info.min = min;
+ jb->info.jitter = jitter;
+}
+
+/* returns 1 if frame was inserted into head of queue, 0 otherwise */
+static int queue_put(jitterbuf *jb, void *data, const enum jb_frame_type type, long ms, long ts)
+{
+ jb_frame *frame;
+ jb_frame *p;
+ int head = 0;
+ long resync_ts = ts - jb->info.resync_offset;
+
+ if ((frame = jb->free)) {
+ jb->free = frame->next;
+ } else if (!(frame = ast_malloc(sizeof(*frame)))) {
+ jb_err("cannot allocate frame\n");
+ return 0;
+ }
+
+ jb->info.frames_cur++;
+
+ frame->data = data;
+ frame->ts = resync_ts;
+ frame->ms = ms;
+ frame->type = type;
+
+ /*
+ * frames are a circular list, jb-frames points to to the lowest ts,
+ * jb->frames->prev points to the highest ts
+ */
+
+ if (!jb->frames) { /* queue is empty */
+ jb->frames = frame;
+ frame->next = frame;
+ frame->prev = frame;
+ head = 1;
+ } else if (resync_ts < jb->frames->ts) {
+ frame->next = jb->frames;
+ frame->prev = jb->frames->prev;
+
+ frame->next->prev = frame;
+ frame->prev->next = frame;
+
+ /* frame is out of order */
+ jb->info.frames_ooo++;
+
+ jb->frames = frame;
+ head = 1;
+ } else {
+ p = jb->frames;
+
+ /* frame is out of order */
+ if (resync_ts < p->prev->ts) jb->info.frames_ooo++;
+
+ while (resync_ts < p->prev->ts && p->prev != jb->frames)
+ p = p->prev;
+
+ frame->next = p;
+ frame->prev = p->prev;
+
+ frame->next->prev = frame;
+ frame->prev->next = frame;
+ }
+ return head;
+}
+
+static long queue_next(jitterbuf *jb)
+{
+ if (jb->frames)
+ return jb->frames->ts;
+ else
+ return -1;
+}
+
+static long queue_last(jitterbuf *jb)
+{
+ if (jb->frames)
+ return jb->frames->prev->ts;
+ else
+ return -1;
+}
+
+static jb_frame *_queue_get(jitterbuf *jb, long ts, int all)
+{
+ jb_frame *frame;
+ frame = jb->frames;
+
+ if (!frame)
+ return NULL;
+
+ /*jb_warn("queue_get: ASK %ld FIRST %ld\n", ts, frame->ts); */
+
+ if (all || ts >= frame->ts) {
+ /* remove this frame */
+ frame->prev->next = frame->next;
+ frame->next->prev = frame->prev;
+
+ if (frame->next == frame)
+ jb->frames = NULL;
+ else
+ jb->frames = frame->next;
+
+
+ /* insert onto "free" single-linked list */
+ frame->next = jb->free;
+ jb->free = frame;
+
+ jb->info.frames_cur--;
+
+ /* we return the frame pointer, even though it's on free list,
+ * but caller must copy data */
+ return frame;
+ }
+
+ return NULL;
+}
+
+static jb_frame *queue_get(jitterbuf *jb, long ts)
+{
+ return _queue_get(jb,ts,0);
+}
+
+static jb_frame *queue_getall(jitterbuf *jb)
+{
+ return _queue_get(jb,0,1);
+}
+
+#if 0
+/* some diagnostics */
+static void jb_dbginfo(jitterbuf *jb)
+{
+ if (dbgf == NULL)
+ return;
+
+ jb_dbg("\njb info: fin=%ld fout=%ld flate=%ld flost=%ld fdrop=%ld fcur=%ld\n",
+ jb->info.frames_in, jb->info.frames_out, jb->info.frames_late, jb->info.frames_lost, jb->info.frames_dropped, jb->info.frames_cur);
+
+ jb_dbg("jitter=%ld current=%ld target=%ld min=%ld sil=%d len=%d len/fcur=%ld\n",
+ jb->info.jitter, jb->info.current, jb->info.target, jb->info.min, jb->info.silence_begin_ts, jb->info.current - jb->info.min,
+ jb->info.frames_cur ? (jb->info.current - jb->info.min)/jb->info.frames_cur : -8);
+ if (jb->info.frames_in > 0)
+ jb_dbg("jb info: Loss PCT = %ld%%, Late PCT = %ld%%\n",
+ jb->info.frames_lost * 100/(jb->info.frames_in + jb->info.frames_lost),
+ jb->info.frames_late * 100/jb->info.frames_in);
+ jb_dbg("jb info: queue %d -> %d. last_ts %d (queue len: %d) last_ms %d\n",
+ queue_next(jb),
+ queue_last(jb),
+ jb->info.next_voice_ts,
+ queue_last(jb) - queue_next(jb),
+ jb->info.last_voice_ms);
+}
+#endif
+
+#ifdef DEEP_DEBUG
+static void jb_chkqueue(jitterbuf *jb)
+{
+ int i=0;
+ jb_frame *p = jb->frames;
+
+ if (!p) {
+ return;
+ }
+
+ do {
+ if (p->next == NULL) {
+ jb_err("Queue is BROKEN at item [%d]", i);
+ }
+ i++;
+ p=p->next;
+ } while (p->next != jb->frames);
+}
+
+static void jb_dbgqueue(jitterbuf *jb)
+{
+ int i=0;
+ jb_frame *p = jb->frames;
+
+ jb_dbg("queue: ");
+
+ if (!p) {
+ jb_dbg("EMPTY\n");
+ return;
+ }
+
+ do {
+ jb_dbg("[%d]=%ld ", i++, p->ts);
+ p=p->next;
+ } while (p->next != jb->frames);
+
+ jb_dbg("\n");
+}
+#endif
+
+enum jb_return_code jb_put(jitterbuf *jb, void *data, const enum jb_frame_type type, long ms, long ts, long now)
+{
+ long numts;
+
+ jb_dbg2("jb_put(%x,%x,%ld,%ld,%ld)\n", jb, data, ms, ts, now);
+
+ jb->info.frames_in++;
+
+ if (jb->frames && jb->dropem)
+ return JB_DROP;
+ jb->dropem = 0;
+
+ if (type == JB_TYPE_VOICE) {
+ /* presently, I'm only adding VOICE frames to history and drift calculations; mostly because with the
+ * IAX integrations, I'm sending retransmitted control frames with their awkward timestamps through */
+ if (history_put(jb,ts,now,ms))
+ return JB_DROP;
+ }
+ numts = 0;
+ if (jb->frames)
+ numts = jb->frames->prev->ts - jb->frames->ts;
+ if (numts >= jb->info.conf.max_jitterbuf) {
+ ast_debug(1, "Attempting to exceed Jitterbuf max %ld timeslots\n",
+ jb->info.conf.max_jitterbuf);
+ jb->dropem = 1;
+ return JB_DROP;
+ }
+ /* if put into head of queue, caller needs to reschedule */
+ if (queue_put(jb,data,type,ms,ts)) {
+ return JB_SCHED;
+ }
+ return JB_OK;
+}
+
+
+static enum jb_return_code _jb_get(jitterbuf *jb, jb_frame *frameout, long now, long interpl)
+{
+ jb_frame *frame;
+ long diff;
+ static int dbg_cnt = 0;
+
+ /*if ((now - jb_next(jb)) > 2 * jb->info.last_voice_ms) jb_warn("SCHED: %ld", (now - jb_next(jb))); */
+ /* get jitter info */
+ history_get(jb);
+
+ if (dbg_cnt && dbg_cnt % 50 == 0) {
+ jb_dbg("\n");
+ }
+ dbg_cnt++;
+
+ /* target */
+ jb->info.target = jb->info.jitter + jb->info.min + jb->info.conf.target_extra;
+
+ /* if a hard clamp was requested, use it */
+ if ((jb->info.conf.max_jitterbuf) && ((jb->info.target - jb->info.min) > jb->info.conf.max_jitterbuf)) {
+ jb_dbg("clamping target from %d to %d\n", (jb->info.target - jb->info.min), jb->info.conf.max_jitterbuf);
+ jb->info.target = jb->info.min + jb->info.conf.max_jitterbuf;
+ }
+
+ diff = jb->info.target - jb->info.current;
+
+ /* jb_warn("diff = %d lms=%d last = %d now = %d\n", diff, */
+ /* jb->info.last_voice_ms, jb->info.last_adjustment, now); */
+
+ /* let's work on non-silent case first */
+ if (!jb->info.silence_begin_ts) {
+ /* we want to grow */
+ if ((diff > 0) &&
+ /* we haven't grown in the delay length */
+ (((jb->info.last_adjustment + JB_ADJUST_DELAY) < now) ||
+ /* we need to grow more than the "length" we have left */
+ (diff > queue_last(jb) - queue_next(jb)) ) ) {
+ /* grow by interp frame length */
+ jb->info.current += interpl;
+ jb->info.next_voice_ts += interpl;
+ jb->info.last_voice_ms = interpl;
+ jb->info.last_adjustment = now;
+ jb->info.cnt_contig_interp++;
+ if (jb->info.conf.max_contig_interp && jb->info.cnt_contig_interp >= jb->info.conf.max_contig_interp) {
+ jb->info.silence_begin_ts = jb->info.next_voice_ts - jb->info.current;
+ }
+ jb_dbg("G");
+ return JB_INTERP;
+ }
+
+ frame = queue_get(jb, jb->info.next_voice_ts - jb->info.current);
+
+ /* not a voice frame; just return it. */
+ if (frame && frame->type != JB_TYPE_VOICE) {
+ if (frame->type == JB_TYPE_SILENCE) {
+ jb->info.silence_begin_ts = frame->ts;
+ jb->info.cnt_contig_interp = 0;
+ }
+
+ *frameout = *frame;
+ jb->info.frames_out++;
+ jb_dbg("o");
+ return JB_OK;
+ }
+
+
+ /* voice frame is later than expected */
+ if (frame && frame->ts + jb->info.current < jb->info.next_voice_ts) {
+ if (frame->ts + jb->info.current > jb->info.next_voice_ts - jb->info.last_voice_ms) {
+ /* either we interpolated past this frame in the last jb_get */
+ /* or the frame is still in order, but came a little too quick */
+ *frameout = *frame;
+ /* reset expectation for next frame */
+ jb->info.next_voice_ts = frame->ts + jb->info.current + frame->ms;
+ jb->info.frames_out++;
+ decrement_losspct(jb);
+ jb->info.cnt_contig_interp = 0;
+ jb_dbg("v");
+ return JB_OK;
+ } else {
+ /* voice frame is late */
+ *frameout = *frame;
+ jb->info.frames_out++;
+ decrement_losspct(jb);
+ jb->info.frames_late++;
+ jb->info.frames_lost--;
+ jb_dbg("l");
+ /*jb_warn("\nlate: wanted=%ld, this=%ld, next=%ld\n", jb->info.next_voice_ts - jb->info.current, frame->ts, queue_next(jb));
+ jb_warninfo(jb); */
+ return JB_DROP;
+ }
+ }
+
+ /* keep track of frame sizes, to allow for variable sized-frames */
+ if (frame && frame->ms > 0) {
+ jb->info.last_voice_ms = frame->ms;
+ }
+
+ /* we want to shrink; shrink at 1 frame / 500ms */
+ /* unless we don't have a frame, then shrink 1 frame */
+ /* every 80ms (though perhaps we can shrink even faster */
+ /* in this case) */
+ if (diff < -jb->info.conf.target_extra &&
+ ((!frame && jb->info.last_adjustment + 80 < now) ||
+ (jb->info.last_adjustment + 500 < now))) {
+
+ jb->info.last_adjustment = now;
+ jb->info.cnt_contig_interp = 0;
+
+ if (frame) {
+ *frameout = *frame;
+ /* shrink by frame size we're throwing out */
+ jb->info.current -= frame->ms;
+ jb->info.frames_out++;
+ decrement_losspct(jb);
+ jb->info.frames_dropped++;
+ jb_dbg("s");
+ return JB_DROP;
+ } else {
+ /* shrink by last_voice_ms */
+ jb->info.current -= jb->info.last_voice_ms;
+ jb->info.frames_lost++;
+ increment_losspct(jb);
+ jb_dbg("S");
+ return JB_NOFRAME;
+ }
+ }
+
+ /* lost frame */
+ if (!frame) {
+ /* this is a bit of a hack for now, but if we're close to
+ * target, and we find a missing frame, it makes sense to
+ * grow, because the frame might just be a bit late;
+ * otherwise, we presently get into a pattern where we return
+ * INTERP for the lost frame, then it shows up next, and we
+ * throw it away because it's late */
+ /* I've recently only been able to replicate this using
+ * iaxclient talking to app_echo on asterisk. In this case,
+ * my outgoing packets go through asterisk's (old)
+ * jitterbuffer, and then might get an unusual increasing delay
+ * there if it decides to grow?? */
+ /* Update: that might have been a different bug, that has been fixed..
+ * But, this still seemed like a good idea, except that it ended up making a single actual
+ * lost frame get interpolated two or more times, when there was "room" to grow, so it might
+ * be a bit of a bad idea overall */
+ /*if (diff > -1 * jb->info.last_voice_ms) {
+ jb->info.current += jb->info.last_voice_ms;
+ jb->info.last_adjustment = now;
+ jb_warn("g");
+ return JB_INTERP;
+ } */
+ jb->info.frames_lost++;
+ increment_losspct(jb);
+ jb->info.next_voice_ts += interpl;
+ jb->info.last_voice_ms = interpl;
+ jb->info.cnt_contig_interp++;
+ if (jb->info.conf.max_contig_interp && jb->info.cnt_contig_interp >= jb->info.conf.max_contig_interp) {
+ jb->info.silence_begin_ts = jb->info.next_voice_ts - jb->info.current;
+ }
+ jb_dbg("L");
+ return JB_INTERP;
+ }
+
+ /* normal case; return the frame, increment stuff */
+ *frameout = *frame;
+ jb->info.next_voice_ts += frame->ms;
+ jb->info.frames_out++;
+ jb->info.cnt_contig_interp = 0;
+ decrement_losspct(jb);
+ jb_dbg("v");
+ return JB_OK;
+ } else {
+ /* TODO: after we get the non-silent case down, we'll make the
+ * silent case -- basically, we'll just grow and shrink faster
+ * here, plus handle next_voice_ts a bit differently */
+
+ /* to disable silent special case altogether, just uncomment this: */
+ /* jb->info.silence_begin_ts = 0; */
+
+ /* shrink interpl len every 10ms during silence */
+ if (diff < -jb->info.conf.target_extra &&
+ jb->info.last_adjustment + 10 <= now) {
+ jb->info.current -= interpl;
+ jb->info.last_adjustment = now;
+ }
+
+ frame = queue_get(jb, now - jb->info.current);
+ if (!frame) {
+ return JB_NOFRAME;
+ } else if (frame->type != JB_TYPE_VOICE) {
+ /* normal case; in silent mode, got a non-voice frame */
+ *frameout = *frame;
+ jb->info.frames_out++;
+ return JB_OK;
+ }
+ if (frame->ts < jb->info.silence_begin_ts) {
+ /* voice frame is late */
+ *frameout = *frame;
+ jb->info.frames_out++;
+ decrement_losspct(jb);
+ jb->info.frames_late++;
+ jb->info.frames_lost--;
+ jb_dbg("l");
+ /*jb_warn("\nlate: wanted=%ld, this=%ld, next=%ld\n", jb->info.next_voice_ts - jb->info.current, frame->ts, queue_next(jb));
+ jb_warninfo(jb); */
+ return JB_DROP;
+ } else {
+ /* voice frame */
+ /* try setting current to target right away here */
+ jb->info.current = jb->info.target;
+ jb->info.silence_begin_ts = 0;
+ jb->info.next_voice_ts = frame->ts + jb->info.current + frame->ms;
+ jb->info.last_voice_ms = frame->ms;
+ jb->info.frames_out++;
+ decrement_losspct(jb);
+ *frameout = *frame;
+ jb_dbg("V");
+ return JB_OK;
+ }
+ }
+}
+
+long jb_next(jitterbuf *jb)
+{
+ if (jb->info.silence_begin_ts) {
+ if (jb->frames) {
+ long next = queue_next(jb);
+ history_get(jb);
+ /* shrink during silence */
+ if (jb->info.target - jb->info.current < -jb->info.conf.target_extra)
+ return jb->info.last_adjustment + 10;
+ return next + jb->info.target;
+ }
+ else
+ return JB_LONGMAX;
+ } else {
+ return jb->info.next_voice_ts;
+ }
+}
+
+enum jb_return_code jb_get(jitterbuf *jb, jb_frame *frameout, long now, long interpl)
+{
+ enum jb_return_code ret = _jb_get(jb, frameout, now, interpl);
+#if 0
+ static int lastts=0;
+ int thists = ((ret == JB_OK) || (ret == JB_DROP)) ? frameout->ts : 0;
+ jb_warn("jb_get(%x,%x,%ld) = %d (%d)\n", jb, frameout, now, ret, thists);
+ if (thists && thists < lastts) jb_warn("XXXX timestamp roll-back!!!\n");
+ lastts = thists;
+#endif
+ if (ret == JB_INTERP)
+ frameout->ms = jb->info.last_voice_ms;
+
+ return ret;
+}
+
+enum jb_return_code jb_getall(jitterbuf *jb, jb_frame *frameout)
+{
+ jb_frame *frame;
+ frame = queue_getall(jb);
+
+ if (!frame) {
+ return JB_NOFRAME;
+ }
+
+ *frameout = *frame;
+ return JB_OK;
+}
+
+
+enum jb_return_code jb_getinfo(jitterbuf *jb, jb_info *stats)
+{
+
+ history_get(jb);
+
+ *stats = jb->info;
+
+ return JB_OK;
+}
+
+enum jb_return_code jb_setconf(jitterbuf *jb, jb_conf *conf)
+{
+ /* take selected settings from the struct */
+
+ jb->info.conf.max_jitterbuf = conf->max_jitterbuf;
+ jb->info.conf.resync_threshold = conf->resync_threshold;
+ jb->info.conf.max_contig_interp = conf->max_contig_interp;
+
+ /* -1 indicates use of the default JB_TARGET_EXTRA value */
+ jb->info.conf.target_extra = ( conf->target_extra == -1 )
+ ? JB_TARGET_EXTRA
+ : conf->target_extra
+ ;
+
+ /* update these to match new target_extra setting */
+ jb->info.current = jb->info.conf.target_extra;
+ jb->info.target = jb->info.conf.target_extra;
+
+ return JB_OK;
+}
+
+
diff --git a/trunk/main/libresample/LICENSE.txt b/trunk/main/libresample/LICENSE.txt
new file mode 100644
index 000000000..4ccd6ccfd
--- /dev/null
+++ b/trunk/main/libresample/LICENSE.txt
@@ -0,0 +1,463 @@
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations
+below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control
+compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply, and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License
+may add an explicit geographical distribution limitation excluding those
+countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/trunk/main/libresample/Makefile.asterisk b/trunk/main/libresample/Makefile.asterisk
new file mode 100644
index 000000000..815671a2f
--- /dev/null
+++ b/trunk/main/libresample/Makefile.asterisk
@@ -0,0 +1,11 @@
+include $(ASTTOPDIR)/Makefile.moddir_rules
+
+ASTCFLAGS+= -Isrc -Iinclude
+
+libresample.a: src/resample.o src/resamplesubs.o src/filterkit.o
+ $(ECHO_PREFIX) echo " [AR] $^ -> $@"
+ $(CMD_PREFIX) $(AR) cr $@ $^
+ $(CMD_PREFIX) $(RANLIB) $@
+
+clean::
+ rm -f src/*.o libresample.a
diff --git a/trunk/main/libresample/Makefile.in b/trunk/main/libresample/Makefile.in
new file mode 100644
index 000000000..8d17d19b4
--- /dev/null
+++ b/trunk/main/libresample/Makefile.in
@@ -0,0 +1,52 @@
+# Run configure to generate Makefile from Makefile.in on
+# any system supported by GNU autoconf. For all other
+# systems, use this file as a template to create a
+# working Makefile.
+
+CC = @CC@
+CFLAGS = @CFLAGS@ -Wall
+
+LIBS = @LIBS@ -lm
+
+AR = @AR@
+RANLIB = @RANLIB@
+
+OBJS = \
+ src/resample.c.o \
+ src/resamplesubs.c.o \
+ src/filterkit.c.o
+
+TARGETS = @TARGETS@
+
+all: $(TARGETS)
+
+libresample.a: $(OBJS) Makefile
+ $(AR) ruv libresample.a $(OBJS)
+ ranlib libresample.a
+
+tests/testresample: libresample.a tests/testresample.c
+ $(CC) -o tests/testresample \
+ $(CFLAGS) tests/testresample.c \
+ libresample.a $(LIBS)
+
+tests/compareresample: libresample.a tests/compareresample.c
+ $(CC) -o tests/compareresample \
+ $(CFLAGS) tests/compareresample.c \
+ libresample.a -lsamplerate $(LIBS)
+
+tests/resample-sndfile: libresample.a tests/resample-sndfile.c
+ $(CC) -o tests/resample-sndfile \
+ $(CFLAGS) tests/resample-sndfile.c \
+ libresample.a -lsndfile $(LIBS)
+
+clean:
+ rm -f $(TARGETS) $(OBJS)
+
+distclean: clean
+ rm -f Makefile
+ rm -f config.status config.cache config.log src/config.h
+ rm -f *~ src/*~ tests/*~ include/*~
+
+%.c.o: %.c Makefile include/libresample.h \
+ src/resample_defs.h src/filterkit.h src/config.h
+ $(CC) -c $(CFLAGS) $< -o $@
diff --git a/trunk/main/libresample/README.txt b/trunk/main/libresample/README.txt
new file mode 100644
index 000000000..14be45b83
--- /dev/null
+++ b/trunk/main/libresample/README.txt
@@ -0,0 +1,84 @@
+libresample
+
+Real-time library interface by Dominic Mazzoni
+
+Based on resample-1.7:
+ http://www-ccrma.stanford.edu/~jos/resample/
+
+License: LGPL - see the file LICENSE.txt for more information
+
+History:
+
+This library is not the highest-quality resampling library
+available, nor is it the most flexible, nor is it the
+fastest. But it is pretty good in all of these regards, and
+it is quite portable. The best resampling library I am aware
+of is libsamplerate by Erik de Castro Lopo. It's small, fast,
+and very high quality. However, it uses the GPL for its
+license (with commercial options available) and I needed
+a more free library. So I wrote this library, using
+the LGPL resample-1.7 library by Julius Smith as a basis.
+
+Resample-1.7 is a fixed-point resampler, and as a result
+has only limited precision. I rewrote it to use single-precision
+floating-point arithmetic instead and increased the number
+of filter coefficients between time steps significantly.
+On modern processors it can resample in real time even
+with this extra overhead.
+
+Resample-1.7 was designed to read and write from files, so
+I removed all of that code and replaced it with an API that
+lets you pass samples in small chunks. It should be easy
+to link to resample-1.7 as a library.
+
+Changes in version 0.1.3:
+
+* Fixed two bugs that were causing subtle problems
+ on Intel x86 processors due to differences in roundoff errors.
+
+* Prefixed most function names with lrs and changed header file
+ from resample.h to libresample.h, to avoid namespace
+ collisions with existing programs and libraries.
+
+* Added resample_dup (thanks to Glenn Maynard)
+
+* Argument to resample_get_filter_width takes a const void *
+ (thanks to Glenn Maynard)
+
+* resample-sndfile clips output to -1...1 (thanks to Glenn Maynard)
+
+Usage notes:
+
+- If the output buffer you pass is too small, resample_process
+ may not use any input samples because its internal output
+ buffer is too full to process any more. So do not assume
+ that it is an error just because no input samples were
+ consumed. Just keep passing valid output buffers.
+
+- Given a resampling factor f > 1, and a number of input
+ samples n, the number of output samples should be between
+ floor(n - f) and ceil(n + f). In other words, if you
+ resample 1000 samples at a factor of 8, the number of
+ output samples might be between 7992 and 8008. Do not
+ assume that it will be exactly 8000. If you need exactly
+ 8000 outputs, pad the input with extra zeros as necessary.
+
+License and warranty:
+
+All of the files in this package are Copyright 2003 by Dominic
+Mazzoni <dominic@minorninth.com>. This library was based heavily
+on Resample-1.7, Copyright 1994-2002 by Julius O. Smith III
+<jos@ccrma.stanford.edu>, all rights reserved.
+
+Permission to use and copy is granted subject to the terms of the
+"GNU Lesser General Public License" (LGPL) as published by the
+Free Software Foundation; either version 2.1 of the License,
+or any later version. In addition, Julius O. Smith III requests
+that a copy of any modified files be sent by email to
+jos@ccrma.stanford.edu so that he may incorporate them into the
+CCRMA version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
diff --git a/trunk/main/libresample/config.guess b/trunk/main/libresample/config.guess
new file mode 100755
index 000000000..e8c6fc0c3
--- /dev/null
+++ b/trunk/main/libresample/config.guess
@@ -0,0 +1,1432 @@
+#! /bin/sh
+# Attempt to guess a canonical system name.
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+# 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+
+timestamp='2004-01-05'
+
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Originally written by Per Bothner <per@bothner.com>.
+# Please send patches to <config-patches@gnu.org>. Submit a context
+# diff and a properly formatted ChangeLog entry.
+#
+# This script attempts to guess a canonical system name similar to
+# config.sub. If it succeeds, it prints the system name on stdout, and
+# exits with 0. Otherwise, it exits with 1.
+#
+# The plan is that this can be called by configure scripts if you
+# don't specify an explicit build system type.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION]
+
+Output the configuration name of the system \`$me' is run on.
+
+Operation modes:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.guess ($timestamp)
+
+Originally written by Per Bothner.
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit 0 ;;
+ --version | -v )
+ echo "$version" ; exit 0 ;;
+ --help | --h* | -h )
+ echo "$usage"; exit 0 ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help" >&2
+ exit 1 ;;
+ * )
+ break ;;
+ esac
+done
+
+if test $# != 0; then
+ echo "$me: too many arguments$help" >&2
+ exit 1
+fi
+
+trap 'exit 1' 1 2 15
+
+# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
+# compiler to aid in system detection is discouraged as it requires
+# temporary files to be created and, as you can see below, it is a
+# headache to deal with in a portable fashion.
+
+# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
+# use `HOST_CC' if defined, but it is deprecated.
+
+# Portable tmp directory creation inspired by the Autoconf team.
+
+set_cc_for_build='
+trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ;
+trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ;
+: ${TMPDIR=/tmp} ;
+ { tmp=`(umask 077 && mktemp -d -q "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
+ { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } ||
+ { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } ||
+ { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ;
+dummy=$tmp/dummy ;
+tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ;
+case $CC_FOR_BUILD,$HOST_CC,$CC in
+ ,,) echo "int x;" > $dummy.c ;
+ for c in cc gcc c89 c99 ; do
+ if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then
+ CC_FOR_BUILD="$c"; break ;
+ fi ;
+ done ;
+ if test x"$CC_FOR_BUILD" = x ; then
+ CC_FOR_BUILD=no_compiler_found ;
+ fi
+ ;;
+ ,,*) CC_FOR_BUILD=$CC ;;
+ ,*,*) CC_FOR_BUILD=$HOST_CC ;;
+esac ;'
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 1994-08-24)
+if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
+ PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
+ *:NetBSD:*:*)
+ # NetBSD (nbsd) targets should (where applicable) match one or
+ # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*,
+ # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently
+ # switched to ELF, *-*-netbsd* would select the old
+ # object file format. This provides both forward
+ # compatibility and a consistent mechanism for selecting the
+ # object file format.
+ #
+ # Note: NetBSD doesn't particularly care about the vendor
+ # portion of the name. We always set it to "unknown".
+ sysctl="sysctl -n hw.machine_arch"
+ UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \
+ /usr/sbin/$sysctl 2>/dev/null || echo unknown)`
+ case "${UNAME_MACHINE_ARCH}" in
+ armeb) machine=armeb-unknown ;;
+ arm*) machine=arm-unknown ;;
+ sh3el) machine=shl-unknown ;;
+ sh3eb) machine=sh-unknown ;;
+ *) machine=${UNAME_MACHINE_ARCH}-unknown ;;
+ esac
+ # The Operating System including object format, if it has switched
+ # to ELF recently, or will in the future.
+ case "${UNAME_MACHINE_ARCH}" in
+ arm*|i386|m68k|ns32k|sh3*|sparc|vax)
+ eval $set_cc_for_build
+ if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep __ELF__ >/dev/null
+ then
+ # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
+ # Return netbsd for either. FIX?
+ os=netbsd
+ else
+ os=netbsdelf
+ fi
+ ;;
+ *)
+ os=netbsd
+ ;;
+ esac
+ # The OS release
+ # Debian GNU/NetBSD machines have a different userland, and
+ # thus, need a distinct triplet. However, they do not need
+ # kernel version information, so it can be replaced with a
+ # suitable tag, in the style of linux-gnu.
+ case "${UNAME_VERSION}" in
+ Debian*)
+ release='-gnu'
+ ;;
+ *)
+ release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+ ;;
+ esac
+ # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
+ # contains redundant information, the shorter form:
+ # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+ echo "${machine}-${os}${release}"
+ exit 0 ;;
+ amiga:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ arc:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ hp300:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mac68k:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ macppc:OpenBSD:*:*)
+ echo powerpc-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mvme68k:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mvme88k:OpenBSD:*:*)
+ echo m88k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ mvmeppc:OpenBSD:*:*)
+ echo powerpc-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ pegasos:OpenBSD:*:*)
+ echo powerpc-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ pmax:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ sgi:OpenBSD:*:*)
+ echo mipseb-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ sun3:OpenBSD:*:*)
+ echo m68k-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ wgrisc:OpenBSD:*:*)
+ echo mipsel-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ *:OpenBSD:*:*)
+ echo ${UNAME_MACHINE}-unknown-openbsd${UNAME_RELEASE}
+ exit 0 ;;
+ alpha:OSF1:*:*)
+ if test $UNAME_RELEASE = "V4.0"; then
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+ fi
+ # According to Compaq, /usr/sbin/psrinfo has been available on
+ # OSF/1 and Tru64 systems produced since 1995. I hope that
+ # covers most systems running today. This code pipes the CPU
+ # types through head -n 1, so we only detect the type of CPU 0.
+ ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1`
+ case "$ALPHA_CPU_TYPE" in
+ "EV4 (21064)")
+ UNAME_MACHINE="alpha" ;;
+ "EV4.5 (21064)")
+ UNAME_MACHINE="alpha" ;;
+ "LCA4 (21066/21068)")
+ UNAME_MACHINE="alpha" ;;
+ "EV5 (21164)")
+ UNAME_MACHINE="alphaev5" ;;
+ "EV5.6 (21164A)")
+ UNAME_MACHINE="alphaev56" ;;
+ "EV5.6 (21164PC)")
+ UNAME_MACHINE="alphapca56" ;;
+ "EV5.7 (21164PC)")
+ UNAME_MACHINE="alphapca57" ;;
+ "EV6 (21264)")
+ UNAME_MACHINE="alphaev6" ;;
+ "EV6.7 (21264A)")
+ UNAME_MACHINE="alphaev67" ;;
+ "EV6.8CB (21264C)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.8AL (21264B)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.8CX (21264D)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.9A (21264/EV69A)")
+ UNAME_MACHINE="alphaev69" ;;
+ "EV7 (21364)")
+ UNAME_MACHINE="alphaev7" ;;
+ "EV7.9 (21364A)")
+ UNAME_MACHINE="alphaev79" ;;
+ esac
+ # A Vn.n version is a released version.
+ # A Tn.n version is a released field test version.
+ # A Xn.n version is an unreleased experimental baselevel.
+ # 1.2 uses "1.2" for uname -r.
+ echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[VTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+ exit 0 ;;
+ Alpha*:OpenVMS:*:*)
+ echo alpha-hp-vms
+ exit 0 ;;
+ Alpha\ *:Windows_NT*:*)
+ # How do we know it's Interix rather than the generic POSIX subsystem?
+ # Should we change UNAME_MACHINE based on the output of uname instead
+ # of the specific Alpha model?
+ echo alpha-pc-interix
+ exit 0 ;;
+ 21064:Windows_NT:50:3)
+ echo alpha-dec-winnt3.5
+ exit 0 ;;
+ Amiga*:UNIX_System_V:4.0:*)
+ echo m68k-unknown-sysv4
+ exit 0;;
+ *:[Aa]miga[Oo][Ss]:*:*)
+ echo ${UNAME_MACHINE}-unknown-amigaos
+ exit 0 ;;
+ *:[Mm]orph[Oo][Ss]:*:*)
+ echo ${UNAME_MACHINE}-unknown-morphos
+ exit 0 ;;
+ *:OS/390:*:*)
+ echo i370-ibm-openedition
+ exit 0 ;;
+ *:OS400:*:*)
+ echo powerpc-ibm-os400
+ exit 0 ;;
+ arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+ echo arm-acorn-riscix${UNAME_RELEASE}
+ exit 0;;
+ SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+ echo hppa1.1-hitachi-hiuxmpp
+ exit 0;;
+ Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+ # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+ if test "`(/bin/universe) 2>/dev/null`" = att ; then
+ echo pyramid-pyramid-sysv3
+ else
+ echo pyramid-pyramid-bsd
+ fi
+ exit 0 ;;
+ NILE*:*:*:dcosx)
+ echo pyramid-pyramid-svr4
+ exit 0 ;;
+ DRS?6000:unix:4.0:6*)
+ echo sparc-icl-nx6
+ exit 0 ;;
+ DRS?6000:UNIX_SV:4.2*:7*)
+ case `/usr/bin/uname -p` in
+ sparc) echo sparc-icl-nx7 && exit 0 ;;
+ esac ;;
+ sun4H:SunOS:5.*:*)
+ echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+ echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ i86pc:SunOS:5.*:*)
+ echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ sun4*:SunOS:6*:*)
+ # According to config.sub, this is the proper way to canonicalize
+ # SunOS6. Hard to guess exactly what SunOS6 will be like, but
+ # it's likely to be more like Solaris than SunOS4.
+ echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ sun4*:SunOS:*:*)
+ case "`/usr/bin/arch -k`" in
+ Series*|S4*)
+ UNAME_RELEASE=`uname -v`
+ ;;
+ esac
+ # Japanese Language versions have a version number like `4.1.3-JL'.
+ echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
+ exit 0 ;;
+ sun3*:SunOS:*:*)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ exit 0 ;;
+ sun*:*:4.2BSD:*)
+ UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+ test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
+ case "`/bin/arch`" in
+ sun3)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ ;;
+ sun4)
+ echo sparc-sun-sunos${UNAME_RELEASE}
+ ;;
+ esac
+ exit 0 ;;
+ aushp:SunOS:*:*)
+ echo sparc-auspex-sunos${UNAME_RELEASE}
+ exit 0 ;;
+ # The situation for MiNT is a little confusing. The machine name
+ # can be virtually everything (everything which is not
+ # "atarist" or "atariste" at least should have a processor
+ # > m68000). The system name ranges from "MiNT" over "FreeMiNT"
+ # to the lowercase version "mint" (or "freemint"). Finally
+ # the system name "TOS" denotes a system which is actually not
+ # MiNT. But MiNT is downward compatible to TOS, so this should
+ # be no problem.
+ atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit 0 ;;
+ atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit 0 ;;
+ *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit 0 ;;
+ milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+ echo m68k-milan-mint${UNAME_RELEASE}
+ exit 0 ;;
+ hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+ echo m68k-hades-mint${UNAME_RELEASE}
+ exit 0 ;;
+ *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+ echo m68k-unknown-mint${UNAME_RELEASE}
+ exit 0 ;;
+ powerpc:machten:*:*)
+ echo powerpc-apple-machten${UNAME_RELEASE}
+ exit 0 ;;
+ RISC*:Mach:*:*)
+ echo mips-dec-mach_bsd4.3
+ exit 0 ;;
+ RISC*:ULTRIX:*:*)
+ echo mips-dec-ultrix${UNAME_RELEASE}
+ exit 0 ;;
+ VAX*:ULTRIX*:*:*)
+ echo vax-dec-ultrix${UNAME_RELEASE}
+ exit 0 ;;
+ 2020:CLIX:*:* | 2430:CLIX:*:*)
+ echo clipper-intergraph-clix${UNAME_RELEASE}
+ exit 0 ;;
+ mips:*:*:UMIPS | mips:*:*:RISCos)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+#ifdef __cplusplus
+#include <stdio.h> /* for printf() prototype */
+ int main (int argc, char *argv[]) {
+#else
+ int main (argc, argv) int argc; char *argv[]; {
+#endif
+ #if defined (host_mips) && defined (MIPSEB)
+ #if defined (SYSTYPE_SYSV)
+ printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_SVR4)
+ printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+ printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
+ #endif
+ #endif
+ exit (-1);
+ }
+EOF
+ $CC_FOR_BUILD -o $dummy $dummy.c \
+ && $dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \
+ && exit 0
+ echo mips-mips-riscos${UNAME_RELEASE}
+ exit 0 ;;
+ Motorola:PowerMAX_OS:*:*)
+ echo powerpc-motorola-powermax
+ exit 0 ;;
+ Motorola:*:4.3:PL8-*)
+ echo powerpc-harris-powermax
+ exit 0 ;;
+ Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
+ echo powerpc-harris-powermax
+ exit 0 ;;
+ Night_Hawk:Power_UNIX:*:*)
+ echo powerpc-harris-powerunix
+ exit 0 ;;
+ m88k:CX/UX:7*:*)
+ echo m88k-harris-cxux7
+ exit 0 ;;
+ m88k:*:4*:R4*)
+ echo m88k-motorola-sysv4
+ exit 0 ;;
+ m88k:*:3*:R3*)
+ echo m88k-motorola-sysv3
+ exit 0 ;;
+ AViiON:dgux:*:*)
+ # DG/UX returns AViiON for all architectures
+ UNAME_PROCESSOR=`/usr/bin/uname -p`
+ if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
+ then
+ if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
+ [ ${TARGET_BINARY_INTERFACE}x = x ]
+ then
+ echo m88k-dg-dgux${UNAME_RELEASE}
+ else
+ echo m88k-dg-dguxbcs${UNAME_RELEASE}
+ fi
+ else
+ echo i586-dg-dgux${UNAME_RELEASE}
+ fi
+ exit 0 ;;
+ M88*:DolphinOS:*:*) # DolphinOS (SVR3)
+ echo m88k-dolphin-sysv3
+ exit 0 ;;
+ M88*:*:R3*:*)
+ # Delta 88k system running SVR3
+ echo m88k-motorola-sysv3
+ exit 0 ;;
+ XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+ echo m88k-tektronix-sysv3
+ exit 0 ;;
+ Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+ echo m68k-tektronix-bsd
+ exit 0 ;;
+ *:IRIX*:*:*)
+ echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
+ exit 0 ;;
+ ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+ echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id
+ exit 0 ;; # Note that: echo "'`uname -s`'" gives 'AIX '
+ i*86:AIX:*:*)
+ echo i386-ibm-aix
+ exit 0 ;;
+ ia64:AIX:*:*)
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+ fi
+ echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
+ exit 0 ;;
+ *:AIX:2:3)
+ if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <sys/systemcfg.h>
+
+ main()
+ {
+ if (!__power_pc())
+ exit(1);
+ puts("powerpc-ibm-aix3.2.5");
+ exit(0);
+ }
+EOF
+ $CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0
+ echo rs6000-ibm-aix3.2.5
+ elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+ echo rs6000-ibm-aix3.2.4
+ else
+ echo rs6000-ibm-aix3.2
+ fi
+ exit 0 ;;
+ *:AIX:*:[45])
+ IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
+ if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
+ IBM_ARCH=rs6000
+ else
+ IBM_ARCH=powerpc
+ fi
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+ fi
+ echo ${IBM_ARCH}-ibm-aix${IBM_REV}
+ exit 0 ;;
+ *:AIX:*:*)
+ echo rs6000-ibm-aix
+ exit 0 ;;
+ ibmrt:4.4BSD:*|romp-ibm:BSD:*)
+ echo romp-ibm-bsd4.4
+ exit 0 ;;
+ ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and
+ echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to
+ exit 0 ;; # report: romp-ibm BSD 4.3
+ *:BOSX:*:*)
+ echo rs6000-bull-bosx
+ exit 0 ;;
+ DPX/2?00:B.O.S.:*:*)
+ echo m68k-bull-sysv3
+ exit 0 ;;
+ 9000/[34]??:4.3bsd:1.*:*)
+ echo m68k-hp-bsd
+ exit 0 ;;
+ hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+ echo m68k-hp-bsd4.4
+ exit 0 ;;
+ 9000/[34678]??:HP-UX:*:*)
+ HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+ case "${UNAME_MACHINE}" in
+ 9000/31? ) HP_ARCH=m68000 ;;
+ 9000/[34]?? ) HP_ARCH=m68k ;;
+ 9000/[678][0-9][0-9])
+ if [ -x /usr/bin/getconf ]; then
+ sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+ sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+ case "${sc_cpu_version}" in
+ 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
+ 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
+ 532) # CPU_PA_RISC2_0
+ case "${sc_kernel_bits}" in
+ 32) HP_ARCH="hppa2.0n" ;;
+ 64) HP_ARCH="hppa2.0w" ;;
+ '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20
+ esac ;;
+ esac
+ fi
+ if [ "${HP_ARCH}" = "" ]; then
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+
+ #define _HPUX_SOURCE
+ #include <stdlib.h>
+ #include <unistd.h>
+
+ int main ()
+ {
+ #if defined(_SC_KERNEL_BITS)
+ long bits = sysconf(_SC_KERNEL_BITS);
+ #endif
+ long cpu = sysconf (_SC_CPU_VERSION);
+
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+ case CPU_PA_RISC2_0:
+ #if defined(_SC_KERNEL_BITS)
+ switch (bits)
+ {
+ case 64: puts ("hppa2.0w"); break;
+ case 32: puts ("hppa2.0n"); break;
+ default: puts ("hppa2.0"); break;
+ } break;
+ #else /* !defined(_SC_KERNEL_BITS) */
+ puts ("hppa2.0"); break;
+ #endif
+ default: puts ("hppa1.0"); break;
+ }
+ exit (0);
+ }
+EOF
+ (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
+ test -z "$HP_ARCH" && HP_ARCH=hppa
+ fi ;;
+ esac
+ if [ ${HP_ARCH} = "hppa2.0w" ]
+ then
+ # avoid double evaluation of $set_cc_for_build
+ test -n "$CC_FOR_BUILD" || eval $set_cc_for_build
+ if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E -) | grep __LP64__ >/dev/null
+ then
+ HP_ARCH="hppa2.0w"
+ else
+ HP_ARCH="hppa64"
+ fi
+ fi
+ echo ${HP_ARCH}-hp-hpux${HPUX_REV}
+ exit 0 ;;
+ ia64:HP-UX:*:*)
+ HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+ echo ia64-hp-hpux${HPUX_REV}
+ exit 0 ;;
+ 3050*:HI-UX:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <unistd.h>
+ int
+ main ()
+ {
+ long cpu = sysconf (_SC_CPU_VERSION);
+ /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+ true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct
+ results, however. */
+ if (CPU_IS_PA_RISC (cpu))
+ {
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+ default: puts ("hppa-hitachi-hiuxwe2"); break;
+ }
+ }
+ else if (CPU_IS_HP_MC68K (cpu))
+ puts ("m68k-hitachi-hiuxwe2");
+ else puts ("unknown-hitachi-hiuxwe2");
+ exit (0);
+ }
+EOF
+ $CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0
+ echo unknown-hitachi-hiuxwe2
+ exit 0 ;;
+ 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
+ echo hppa1.1-hp-bsd
+ exit 0 ;;
+ 9000/8??:4.3bsd:*:*)
+ echo hppa1.0-hp-bsd
+ exit 0 ;;
+ *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
+ echo hppa1.0-hp-mpeix
+ exit 0 ;;
+ hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
+ echo hppa1.1-hp-osf
+ exit 0 ;;
+ hp8??:OSF1:*:*)
+ echo hppa1.0-hp-osf
+ exit 0 ;;
+ i*86:OSF1:*:*)
+ if [ -x /usr/sbin/sysversion ] ; then
+ echo ${UNAME_MACHINE}-unknown-osf1mk
+ else
+ echo ${UNAME_MACHINE}-unknown-osf1
+ fi
+ exit 0 ;;
+ parisc*:Lites*:*:*)
+ echo hppa1.1-hp-lites
+ exit 0 ;;
+ C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+ echo c1-convex-bsd
+ exit 0 ;;
+ C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit 0 ;;
+ C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+ echo c34-convex-bsd
+ exit 0 ;;
+ C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+ echo c38-convex-bsd
+ exit 0 ;;
+ C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+ echo c4-convex-bsd
+ exit 0 ;;
+ CRAY*Y-MP:*:*:*)
+ echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ CRAY*[A-Z]90:*:*:*)
+ echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
+ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
+ -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ CRAY*TS:*:*:*)
+ echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ CRAY*T3E:*:*:*)
+ echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ CRAY*SV1:*:*:*)
+ echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ *:UNICOS/mp:*:*)
+ echo nv1-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit 0 ;;
+ F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+ FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+ FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+ FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
+ echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit 0 ;;
+ 5000:UNIX_System_V:4.*:*)
+ FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+ FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'`
+ echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit 0 ;;
+ i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+ echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
+ exit 0 ;;
+ sparc*:BSD/OS:*:*)
+ echo sparc-unknown-bsdi${UNAME_RELEASE}
+ exit 0 ;;
+ *:BSD/OS:*:*)
+ echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
+ exit 0 ;;
+ *:FreeBSD:*:*)
+ # Determine whether the default compiler uses glibc.
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <features.h>
+ #if __GLIBC__ >= 2
+ LIBC=gnu
+ #else
+ LIBC=
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=`
+ # GNU/KFreeBSD systems have a "k" prefix to indicate we are using
+ # FreeBSD's kernel, but not the complete OS.
+ case ${LIBC} in gnu) kernel_only='k' ;; esac
+ echo ${UNAME_MACHINE}-unknown-${kernel_only}freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`${LIBC:+-$LIBC}
+ exit 0 ;;
+ i*:CYGWIN*:*)
+ echo ${UNAME_MACHINE}-pc-cygwin
+ exit 0 ;;
+ i*:MINGW*:*)
+ echo ${UNAME_MACHINE}-pc-mingw32
+ exit 0 ;;
+ i*:PW*:*)
+ echo ${UNAME_MACHINE}-pc-pw32
+ exit 0 ;;
+ x86:Interix*:[34]*)
+ echo i586-pc-interix${UNAME_RELEASE}|sed -e 's/\..*//'
+ exit 0 ;;
+ [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
+ echo i${UNAME_MACHINE}-pc-mks
+ exit 0 ;;
+ i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
+ # How do we know it's Interix rather than the generic POSIX subsystem?
+ # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
+ # UNAME_MACHINE based on the output of uname instead of i386?
+ echo i586-pc-interix
+ exit 0 ;;
+ i*:UWIN*:*)
+ echo ${UNAME_MACHINE}-pc-uwin
+ exit 0 ;;
+ p*:CYGWIN*:*)
+ echo powerpcle-unknown-cygwin
+ exit 0 ;;
+ prep*:SunOS:5.*:*)
+ echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit 0 ;;
+ *:GNU:*:*)
+ # the GNU system
+ echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+ exit 0 ;;
+ *:GNU/*:*:*)
+ # other systems with GNU libc and userland
+ echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu
+ exit 0 ;;
+ i*86:Minix:*:*)
+ echo ${UNAME_MACHINE}-pc-minix
+ exit 0 ;;
+ arm*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ cris:Linux:*:*)
+ echo cris-axis-linux-gnu
+ exit 0 ;;
+ ia64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ m68*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ mips:Linux:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #undef CPU
+ #undef mips
+ #undef mipsel
+ #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+ CPU=mipsel
+ #else
+ #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+ CPU=mips
+ #else
+ CPU=
+ #endif
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=`
+ test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0
+ ;;
+ mips64:Linux:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #undef CPU
+ #undef mips64
+ #undef mips64el
+ #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+ CPU=mips64el
+ #else
+ #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+ CPU=mips64
+ #else
+ CPU=
+ #endif
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=`
+ test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0
+ ;;
+ ppc:Linux:*:*)
+ echo powerpc-unknown-linux-gnu
+ exit 0 ;;
+ ppc64:Linux:*:*)
+ echo powerpc64-unknown-linux-gnu
+ exit 0 ;;
+ alpha:Linux:*:*)
+ case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
+ EV5) UNAME_MACHINE=alphaev5 ;;
+ EV56) UNAME_MACHINE=alphaev56 ;;
+ PCA56) UNAME_MACHINE=alphapca56 ;;
+ PCA57) UNAME_MACHINE=alphapca56 ;;
+ EV6) UNAME_MACHINE=alphaev6 ;;
+ EV67) UNAME_MACHINE=alphaev67 ;;
+ EV68*) UNAME_MACHINE=alphaev68 ;;
+ esac
+ objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null
+ if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
+ echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
+ exit 0 ;;
+ parisc:Linux:*:* | hppa:Linux:*:*)
+ # Look for CPU level
+ case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+ PA7*) echo hppa1.1-unknown-linux-gnu ;;
+ PA8*) echo hppa2.0-unknown-linux-gnu ;;
+ *) echo hppa-unknown-linux-gnu ;;
+ esac
+ exit 0 ;;
+ parisc64:Linux:*:* | hppa64:Linux:*:*)
+ echo hppa64-unknown-linux-gnu
+ exit 0 ;;
+ s390:Linux:*:* | s390x:Linux:*:*)
+ echo ${UNAME_MACHINE}-ibm-linux
+ exit 0 ;;
+ sh64*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ sh*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ sparc:Linux:*:* | sparc64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-gnu
+ exit 0 ;;
+ x86_64:Linux:*:*)
+ echo x86_64-unknown-linux-gnu
+ exit 0 ;;
+ i*86:Linux:*:*)
+ # The BFD linker knows what the default object file format is, so
+ # first see if it will tell us. cd to the root directory to prevent
+ # problems with other programs or directories called `ld' in the path.
+ # Set LC_ALL=C to ensure ld outputs messages in English.
+ ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \
+ | sed -ne '/supported targets:/!d
+ s/[ ][ ]*/ /g
+ s/.*supported targets: *//
+ s/ .*//
+ p'`
+ case "$ld_supported_targets" in
+ elf32-i386)
+ TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu"
+ ;;
+ a.out-i386-linux)
+ echo "${UNAME_MACHINE}-pc-linux-gnuaout"
+ exit 0 ;;
+ coff-i386)
+ echo "${UNAME_MACHINE}-pc-linux-gnucoff"
+ exit 0 ;;
+ "")
+ # Either a pre-BFD a.out linker (linux-gnuoldld) or
+ # one that does not give us useful --help.
+ echo "${UNAME_MACHINE}-pc-linux-gnuoldld"
+ exit 0 ;;
+ esac
+ # Determine whether the default compiler is a.out or elf
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <features.h>
+ #ifdef __ELF__
+ # ifdef __GLIBC__
+ # if __GLIBC__ >= 2
+ LIBC=gnu
+ # else
+ LIBC=gnulibc1
+ # endif
+ # else
+ LIBC=gnulibc1
+ # endif
+ #else
+ #ifdef __INTEL_COMPILER
+ LIBC=gnu
+ #else
+ LIBC=gnuaout
+ #endif
+ #endif
+ #ifdef __dietlibc__
+ LIBC=dietlibc
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=`
+ test x"${LIBC}" != x && echo "${UNAME_MACHINE}-pc-linux-${LIBC}" && exit 0
+ test x"${TENTATIVE}" != x && echo "${TENTATIVE}" && exit 0
+ ;;
+ i*86:DYNIX/ptx:4*:*)
+ # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+ # earlier versions are messed up and put the nodename in both
+ # sysname and nodename.
+ echo i386-sequent-sysv4
+ exit 0 ;;
+ i*86:UNIX_SV:4.2MP:2.*)
+ # Unixware is an offshoot of SVR4, but it has its own version
+ # number series starting with 2...
+ # I am not positive that other SVR4 systems won't match this,
+ # I just have to hope. -- rms.
+ # Use sysv4.2uw... so that sysv4* matches it.
+ echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
+ exit 0 ;;
+ i*86:OS/2:*:*)
+ # If we were able to find `uname', then EMX Unix compatibility
+ # is probably installed.
+ echo ${UNAME_MACHINE}-pc-os2-emx
+ exit 0 ;;
+ i*86:XTS-300:*:STOP)
+ echo ${UNAME_MACHINE}-unknown-stop
+ exit 0 ;;
+ i*86:atheos:*:*)
+ echo ${UNAME_MACHINE}-unknown-atheos
+ exit 0 ;;
+ i*86:syllable:*:*)
+ echo ${UNAME_MACHINE}-pc-syllable
+ exit 0 ;;
+ i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*)
+ echo i386-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ i*86:*DOS:*:*)
+ echo ${UNAME_MACHINE}-pc-msdosdjgpp
+ exit 0 ;;
+ i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
+ UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
+ if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+ echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
+ else
+ echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
+ fi
+ exit 0 ;;
+ i*86:*:5:[78]*)
+ case `/bin/uname -X | grep "^Machine"` in
+ *486*) UNAME_MACHINE=i486 ;;
+ *Pentium) UNAME_MACHINE=i586 ;;
+ *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
+ esac
+ echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
+ exit 0 ;;
+ i*86:*:3.2:*)
+ if test -f /usr/options/cb.name; then
+ UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+ echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
+ elif /bin/uname -X 2>/dev/null >/dev/null ; then
+ UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
+ (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
+ (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
+ && UNAME_MACHINE=i586
+ (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
+ && UNAME_MACHINE=i686
+ (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
+ && UNAME_MACHINE=i686
+ echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
+ else
+ echo ${UNAME_MACHINE}-pc-sysv32
+ fi
+ exit 0 ;;
+ pc:*:*:*)
+ # Left here for compatibility:
+ # uname -m prints for DJGPP always 'pc', but it prints nothing about
+ # the processor, so we play safe by assuming i386.
+ echo i386-pc-msdosdjgpp
+ exit 0 ;;
+ Intel:Mach:3*:*)
+ echo i386-pc-mach3
+ exit 0 ;;
+ paragon:*:*:*)
+ echo i860-intel-osf1
+ exit 0 ;;
+ i860:*:4.*:*) # i860-SVR4
+ if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+ echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
+ else # Add other i860-SVR4 vendors below as they are discovered.
+ echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4
+ fi
+ exit 0 ;;
+ mini*:CTIX:SYS*5:*)
+ # "miniframe"
+ echo m68010-convergent-sysv
+ exit 0 ;;
+ mc68k:UNIX:SYSTEM5:3.51m)
+ echo m68k-convergent-sysv
+ exit 0 ;;
+ M680?0:D-NIX:5.3:*)
+ echo m68k-diab-dnix
+ exit 0 ;;
+ M68*:*:R3V[567]*:*)
+ test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;;
+ 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0)
+ OS_REL=''
+ test -r /etc/.relid \
+ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && echo i486-ncr-sysv4.3${OS_REL} && exit 0
+ /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+ && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;;
+ 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && echo i486-ncr-sysv4 && exit 0 ;;
+ m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+ echo m68k-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ mc68030:UNIX_System_V:4.*:*)
+ echo m68k-atari-sysv4
+ exit 0 ;;
+ TSUNAMI:LynxOS:2.*:*)
+ echo sparc-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ rs6000:LynxOS:2.*:*)
+ echo rs6000-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*)
+ echo powerpc-unknown-lynxos${UNAME_RELEASE}
+ exit 0 ;;
+ SM[BE]S:UNIX_SV:*:*)
+ echo mips-dde-sysv${UNAME_RELEASE}
+ exit 0 ;;
+ RM*:ReliantUNIX-*:*:*)
+ echo mips-sni-sysv4
+ exit 0 ;;
+ RM*:SINIX-*:*:*)
+ echo mips-sni-sysv4
+ exit 0 ;;
+ *:SINIX-*:*:*)
+ if uname -p 2>/dev/null >/dev/null ; then
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ echo ${UNAME_MACHINE}-sni-sysv4
+ else
+ echo ns32k-sni-sysv
+ fi
+ exit 0 ;;
+ PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+ # says <Richard.M.Bartel@ccMail.Census.GOV>
+ echo i586-unisys-sysv4
+ exit 0 ;;
+ *:UNIX_System_V:4*:FTX*)
+ # From Gerald Hewes <hewes@openmarket.com>.
+ # How about differentiating between stratus architectures? -djm
+ echo hppa1.1-stratus-sysv4
+ exit 0 ;;
+ *:*:*:FTX*)
+ # From seanf@swdc.stratus.com.
+ echo i860-stratus-sysv4
+ exit 0 ;;
+ *:VOS:*:*)
+ # From Paul.Green@stratus.com.
+ echo hppa1.1-stratus-vos
+ exit 0 ;;
+ mc68*:A/UX:*:*)
+ echo m68k-apple-aux${UNAME_RELEASE}
+ exit 0 ;;
+ news*:NEWS-OS:6*:*)
+ echo mips-sony-newsos6
+ exit 0 ;;
+ R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+ if [ -d /usr/nec ]; then
+ echo mips-nec-sysv${UNAME_RELEASE}
+ else
+ echo mips-unknown-sysv${UNAME_RELEASE}
+ fi
+ exit 0 ;;
+ BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.
+ echo powerpc-be-beos
+ exit 0 ;;
+ BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only.
+ echo powerpc-apple-beos
+ exit 0 ;;
+ BePC:BeOS:*:*) # BeOS running on Intel PC compatible.
+ echo i586-pc-beos
+ exit 0 ;;
+ SX-4:SUPER-UX:*:*)
+ echo sx4-nec-superux${UNAME_RELEASE}
+ exit 0 ;;
+ SX-5:SUPER-UX:*:*)
+ echo sx5-nec-superux${UNAME_RELEASE}
+ exit 0 ;;
+ SX-6:SUPER-UX:*:*)
+ echo sx6-nec-superux${UNAME_RELEASE}
+ exit 0 ;;
+ Power*:Rhapsody:*:*)
+ echo powerpc-apple-rhapsody${UNAME_RELEASE}
+ exit 0 ;;
+ *:Rhapsody:*:*)
+ echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
+ exit 0 ;;
+ *:Darwin:*:*)
+ case `uname -p` in
+ *86) UNAME_PROCESSOR=i686 ;;
+ powerpc) UNAME_PROCESSOR=powerpc ;;
+ esac
+ echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
+ exit 0 ;;
+ *:procnto*:*:* | *:QNX:[0123456789]*:*)
+ UNAME_PROCESSOR=`uname -p`
+ if test "$UNAME_PROCESSOR" = "x86"; then
+ UNAME_PROCESSOR=i386
+ UNAME_MACHINE=pc
+ fi
+ echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE}
+ exit 0 ;;
+ *:QNX:*:4*)
+ echo i386-pc-qnx
+ exit 0 ;;
+ NSR-?:NONSTOP_KERNEL:*:*)
+ echo nsr-tandem-nsk${UNAME_RELEASE}
+ exit 0 ;;
+ *:NonStop-UX:*:*)
+ echo mips-compaq-nonstopux
+ exit 0 ;;
+ BS2000:POSIX*:*:*)
+ echo bs2000-siemens-sysv
+ exit 0 ;;
+ DS/*:UNIX_System_V:*:*)
+ echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
+ exit 0 ;;
+ *:Plan9:*:*)
+ # "uname -m" is not consistent, so use $cputype instead. 386
+ # is converted to i386 for consistency with other x86
+ # operating systems.
+ if test "$cputype" = "386"; then
+ UNAME_MACHINE=i386
+ else
+ UNAME_MACHINE="$cputype"
+ fi
+ echo ${UNAME_MACHINE}-unknown-plan9
+ exit 0 ;;
+ *:TOPS-10:*:*)
+ echo pdp10-unknown-tops10
+ exit 0 ;;
+ *:TENEX:*:*)
+ echo pdp10-unknown-tenex
+ exit 0 ;;
+ KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+ echo pdp10-dec-tops20
+ exit 0 ;;
+ XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+ echo pdp10-xkl-tops20
+ exit 0 ;;
+ *:TOPS-20:*:*)
+ echo pdp10-unknown-tops20
+ exit 0 ;;
+ *:ITS:*:*)
+ echo pdp10-unknown-its
+ exit 0 ;;
+ SEI:*:*:SEIUX)
+ echo mips-sei-seiux${UNAME_RELEASE}
+ exit 0 ;;
+ *:DRAGONFLY:*:*)
+ echo ${UNAME_MACHINE}-unknown-dragonfly${UNAME_RELEASE}
+ exit 0 ;;
+esac
+
+#echo '(No uname command or uname output not recognized.)' 1>&2
+#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
+
+eval $set_cc_for_build
+cat >$dummy.c <<EOF
+#ifdef _SEQUENT_
+# include <sys/types.h>
+# include <sys/utsname.h>
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+ /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed,
+ I don't know.... */
+ printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+ printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+ "4"
+#else
+ ""
+#endif
+ ); exit (0);
+#endif
+#endif
+
+#if defined (__arm) && defined (__acorn) && defined (__unix)
+ printf ("arm-acorn-riscix"); exit (0);
+#endif
+
+#if defined (hp300) && !defined (hpux)
+ printf ("m68k-hp-bsd\n"); exit (0);
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+ int version;
+ version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+ if (version < 4)
+ printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+ else
+ printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+ exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+ printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+ printf ("ns32k-encore-mach\n"); exit (0);
+#else
+ printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+ printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+ printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+ printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+ struct utsname un;
+
+ uname(&un);
+
+ if (strncmp(un.version, "V2", 2) == 0) {
+ printf ("i386-sequent-ptx2\n"); exit (0);
+ }
+ if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+ printf ("i386-sequent-ptx1\n"); exit (0);
+ }
+ printf ("i386-sequent-ptx\n"); exit (0);
+
+#endif
+
+#if defined (vax)
+# if !defined (ultrix)
+# include <sys/param.h>
+# if defined (BSD)
+# if BSD == 43
+ printf ("vax-dec-bsd4.3\n"); exit (0);
+# else
+# if BSD == 199006
+ printf ("vax-dec-bsd4.3reno\n"); exit (0);
+# else
+ printf ("vax-dec-bsd\n"); exit (0);
+# endif
+# endif
+# else
+ printf ("vax-dec-bsd\n"); exit (0);
+# endif
+# else
+ printf ("vax-dec-ultrix\n"); exit (0);
+# endif
+#endif
+
+#if defined (alliant) && defined (i860)
+ printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+ exit (1);
+}
+EOF
+
+$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && $dummy && exit 0
+
+# Apollos put the system type in the environment.
+
+test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; }
+
+# Convex versions that predate uname can use getsysinfo(1)
+
+if [ -x /usr/convex/getsysinfo ]
+then
+ case `getsysinfo -f cpu_type` in
+ c1*)
+ echo c1-convex-bsd
+ exit 0 ;;
+ c2*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit 0 ;;
+ c34*)
+ echo c34-convex-bsd
+ exit 0 ;;
+ c38*)
+ echo c38-convex-bsd
+ exit 0 ;;
+ c4*)
+ echo c4-convex-bsd
+ exit 0 ;;
+ esac
+fi
+
+cat >&2 <<EOF
+$0: unable to guess system type
+
+This script, last modified $timestamp, has failed to recognize
+the operating system you are using. It is advised that you
+download the most up to date version of the config scripts from
+
+ ftp://ftp.gnu.org/pub/gnu/config/
+
+If the version you run ($0) is already up to date, please
+send the following data and any information you think might be
+pertinent to <config-patches@gnu.org> in order to provide the needed
+information to handle your system.
+
+config.guess timestamp = $timestamp
+
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null`
+
+hostinfo = `(hostinfo) 2>/dev/null`
+/bin/universe = `(/bin/universe) 2>/dev/null`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null`
+/bin/arch = `(/bin/arch) 2>/dev/null`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
+
+UNAME_MACHINE = ${UNAME_MACHINE}
+UNAME_RELEASE = ${UNAME_RELEASE}
+UNAME_SYSTEM = ${UNAME_SYSTEM}
+UNAME_VERSION = ${UNAME_VERSION}
+EOF
+
+exit 1
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/trunk/main/libresample/config.sub b/trunk/main/libresample/config.sub
new file mode 100755
index 000000000..463186dbf
--- /dev/null
+++ b/trunk/main/libresample/config.sub
@@ -0,0 +1,1537 @@
+#! /bin/sh
+# Configuration validation subroutine script.
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+# 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+
+timestamp='2004-01-05'
+
+# This file is (in principle) common to ALL GNU software.
+# The presence of a machine in this file suggests that SOME GNU software
+# can handle that machine. It does not imply ALL GNU software can.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Please send patches to <config-patches@gnu.org>. Submit a context
+# diff and a properly formatted ChangeLog entry.
+#
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support. The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION] CPU-MFR-OPSYS
+ $0 [OPTION] ALIAS
+
+Canonicalize a configuration name.
+
+Operation modes:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.sub ($timestamp)
+
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit 0 ;;
+ --version | -v )
+ echo "$version" ; exit 0 ;;
+ --help | --h* | -h )
+ echo "$usage"; exit 0 ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help"
+ exit 1 ;;
+
+ *local*)
+ # First pass through any local machine types.
+ echo $1
+ exit 0;;
+
+ * )
+ break ;;
+ esac
+done
+
+case $# in
+ 0) echo "$me: missing argument$help" >&2
+ exit 1;;
+ 1) ;;
+ *) echo "$me: too many arguments$help" >&2
+ exit 1;;
+esac
+
+# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
+# Here we must recognize all the valid KERNEL-OS combinations.
+maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
+case $maybe_os in
+ nto-qnx* | linux-gnu* | linux-dietlibc | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | \
+ kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | storm-chaos* | os2-emx* | rtmk-nova*)
+ os=-$maybe_os
+ basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
+ ;;
+ *)
+ basic_machine=`echo $1 | sed 's/-[^-]*$//'`
+ if [ $basic_machine != $1 ]
+ then os=`echo $1 | sed 's/.*-/-/'`
+ else os=; fi
+ ;;
+esac
+
+### Let's recognize common machines as not being operating systems so
+### that things like config.sub decstation-3100 work. We also
+### recognize some manufacturers as not being operating systems, so we
+### can provide default operating systems below.
+case $os in
+ -sun*os*)
+ # Prevent following clause from handling this invalid input.
+ ;;
+ -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
+ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
+ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
+ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
+ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
+ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
+ -apple | -axis)
+ os=
+ basic_machine=$1
+ ;;
+ -sim | -cisco | -oki | -wec | -winbond)
+ os=
+ basic_machine=$1
+ ;;
+ -scout)
+ ;;
+ -wrs)
+ os=-vxworks
+ basic_machine=$1
+ ;;
+ -chorusos*)
+ os=-chorusos
+ basic_machine=$1
+ ;;
+ -chorusrdb)
+ os=-chorusrdb
+ basic_machine=$1
+ ;;
+ -hiux*)
+ os=-hiuxwe2
+ ;;
+ -sco5)
+ os=-sco3.2v5
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco4)
+ os=-sco3.2v4
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco3.2.[4-9]*)
+ os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco3.2v[4-9]*)
+ # Don't forget version if it is 3.2v4 or newer.
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco*)
+ os=-sco3.2v2
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -udk*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -isc)
+ os=-isc2.2
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -clix*)
+ basic_machine=clipper-intergraph
+ ;;
+ -isc*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -lynx*)
+ os=-lynxos
+ ;;
+ -ptx*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
+ ;;
+ -windowsnt*)
+ os=`echo $os | sed -e 's/windowsnt/winnt/'`
+ ;;
+ -psos*)
+ os=-psos
+ ;;
+ -mint | -mint[0-9]*)
+ basic_machine=m68k-atari
+ os=-mint
+ ;;
+esac
+
+# Decode aliases for certain CPU-COMPANY combinations.
+case $basic_machine in
+ # Recognize the basic CPU types without company name.
+ # Some are omitted here because they have special meanings below.
+ 1750a | 580 \
+ | a29k \
+ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
+ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
+ | am33_2.0 \
+ | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \
+ | c4x | clipper \
+ | d10v | d30v | dlx | dsp16xx \
+ | fr30 | frv \
+ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+ | i370 | i860 | i960 | ia64 \
+ | ip2k | iq2000 \
+ | m32r | m68000 | m68k | m88k | mcore \
+ | mips | mipsbe | mipseb | mipsel | mipsle \
+ | mips16 \
+ | mips64 | mips64el \
+ | mips64vr | mips64vrel \
+ | mips64orion | mips64orionel \
+ | mips64vr4100 | mips64vr4100el \
+ | mips64vr4300 | mips64vr4300el \
+ | mips64vr5000 | mips64vr5000el \
+ | mipsisa32 | mipsisa32el \
+ | mipsisa32r2 | mipsisa32r2el \
+ | mipsisa64 | mipsisa64el \
+ | mipsisa64r2 | mipsisa64r2el \
+ | mipsisa64sb1 | mipsisa64sb1el \
+ | mipsisa64sr71k | mipsisa64sr71kel \
+ | mipstx39 | mipstx39el \
+ | mn10200 | mn10300 \
+ | msp430 \
+ | ns16k | ns32k \
+ | openrisc | or32 \
+ | pdp10 | pdp11 | pj | pjl \
+ | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
+ | pyramid \
+ | sh | sh[1234] | sh[23]e | sh[34]eb | shbe | shle | sh[1234]le | sh3ele \
+ | sh64 | sh64le \
+ | sparc | sparc64 | sparc86x | sparclet | sparclite | sparcv9 | sparcv9b \
+ | strongarm \
+ | tahoe | thumb | tic4x | tic80 | tron \
+ | v850 | v850e \
+ | we32k \
+ | x86 | xscale | xstormy16 | xtensa \
+ | z8k)
+ basic_machine=$basic_machine-unknown
+ ;;
+ m6811 | m68hc11 | m6812 | m68hc12)
+ # Motorola 68HC11/12.
+ basic_machine=$basic_machine-unknown
+ os=-none
+ ;;
+ m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
+ ;;
+
+ # We use `pc' rather than `unknown'
+ # because (1) that's what they normally are, and
+ # (2) the word "unknown" tends to confuse beginning users.
+ i*86 | x86_64)
+ basic_machine=$basic_machine-pc
+ ;;
+ # Object if more than one company name word.
+ *-*-*)
+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+ exit 1
+ ;;
+ # Recognize the basic CPU types with company name.
+ 580-* \
+ | a29k-* \
+ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
+ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
+ | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
+ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \
+ | avr-* \
+ | bs2000-* \
+ | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \
+ | clipper-* | cydra-* \
+ | d10v-* | d30v-* | dlx-* \
+ | elxsi-* \
+ | f30[01]-* | f700-* | fr30-* | frv-* | fx80-* \
+ | h8300-* | h8500-* \
+ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
+ | i*86-* | i860-* | i960-* | ia64-* \
+ | ip2k-* | iq2000-* \
+ | m32r-* \
+ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
+ | m88110-* | m88k-* | mcore-* \
+ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
+ | mips16-* \
+ | mips64-* | mips64el-* \
+ | mips64vr-* | mips64vrel-* \
+ | mips64orion-* | mips64orionel-* \
+ | mips64vr4100-* | mips64vr4100el-* \
+ | mips64vr4300-* | mips64vr4300el-* \
+ | mips64vr5000-* | mips64vr5000el-* \
+ | mipsisa32-* | mipsisa32el-* \
+ | mipsisa32r2-* | mipsisa32r2el-* \
+ | mipsisa64-* | mipsisa64el-* \
+ | mipsisa64r2-* | mipsisa64r2el-* \
+ | mipsisa64sb1-* | mipsisa64sb1el-* \
+ | mipsisa64sr71k-* | mipsisa64sr71kel-* \
+ | mipstx39-* | mipstx39el-* \
+ | msp430-* \
+ | none-* | np1-* | nv1-* | ns16k-* | ns32k-* \
+ | orion-* \
+ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
+ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
+ | pyramid-* \
+ | romp-* | rs6000-* \
+ | sh-* | sh[1234]-* | sh[23]e-* | sh[34]eb-* | shbe-* \
+ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
+ | sparc-* | sparc64-* | sparc86x-* | sparclet-* | sparclite-* \
+ | sparcv9-* | sparcv9b-* | strongarm-* | sv1-* | sx?-* \
+ | tahoe-* | thumb-* \
+ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
+ | tron-* \
+ | v850-* | v850e-* | vax-* \
+ | we32k-* \
+ | x86-* | x86_64-* | xps100-* | xscale-* | xstormy16-* \
+ | xtensa-* \
+ | ymp-* \
+ | z8k-*)
+ ;;
+ # Recognize the various machine names and aliases which stand
+ # for a CPU type and a company and sometimes even an OS.
+ 386bsd)
+ basic_machine=i386-unknown
+ os=-bsd
+ ;;
+ 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+ basic_machine=m68000-att
+ ;;
+ 3b*)
+ basic_machine=we32k-att
+ ;;
+ a29khif)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ adobe68k)
+ basic_machine=m68010-adobe
+ os=-scout
+ ;;
+ alliant | fx80)
+ basic_machine=fx80-alliant
+ ;;
+ altos | altos3068)
+ basic_machine=m68k-altos
+ ;;
+ am29k)
+ basic_machine=a29k-none
+ os=-bsd
+ ;;
+ amd64)
+ basic_machine=x86_64-pc
+ ;;
+ amd64-*)
+ basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ amdahl)
+ basic_machine=580-amdahl
+ os=-sysv
+ ;;
+ amiga | amiga-*)
+ basic_machine=m68k-unknown
+ ;;
+ amigaos | amigados)
+ basic_machine=m68k-unknown
+ os=-amigaos
+ ;;
+ amigaunix | amix)
+ basic_machine=m68k-unknown
+ os=-sysv4
+ ;;
+ apollo68)
+ basic_machine=m68k-apollo
+ os=-sysv
+ ;;
+ apollo68bsd)
+ basic_machine=m68k-apollo
+ os=-bsd
+ ;;
+ aux)
+ basic_machine=m68k-apple
+ os=-aux
+ ;;
+ balance)
+ basic_machine=ns32k-sequent
+ os=-dynix
+ ;;
+ c90)
+ basic_machine=c90-cray
+ os=-unicos
+ ;;
+ convex-c1)
+ basic_machine=c1-convex
+ os=-bsd
+ ;;
+ convex-c2)
+ basic_machine=c2-convex
+ os=-bsd
+ ;;
+ convex-c32)
+ basic_machine=c32-convex
+ os=-bsd
+ ;;
+ convex-c34)
+ basic_machine=c34-convex
+ os=-bsd
+ ;;
+ convex-c38)
+ basic_machine=c38-convex
+ os=-bsd
+ ;;
+ cray | j90)
+ basic_machine=j90-cray
+ os=-unicos
+ ;;
+ crds | unos)
+ basic_machine=m68k-crds
+ ;;
+ cris | cris-* | etrax*)
+ basic_machine=cris-axis
+ ;;
+ da30 | da30-*)
+ basic_machine=m68k-da30
+ ;;
+ decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
+ basic_machine=mips-dec
+ ;;
+ decsystem10* | dec10*)
+ basic_machine=pdp10-dec
+ os=-tops10
+ ;;
+ decsystem20* | dec20*)
+ basic_machine=pdp10-dec
+ os=-tops20
+ ;;
+ delta | 3300 | motorola-3300 | motorola-delta \
+ | 3300-motorola | delta-motorola)
+ basic_machine=m68k-motorola
+ ;;
+ delta88)
+ basic_machine=m88k-motorola
+ os=-sysv3
+ ;;
+ dpx20 | dpx20-*)
+ basic_machine=rs6000-bull
+ os=-bosx
+ ;;
+ dpx2* | dpx2*-bull)
+ basic_machine=m68k-bull
+ os=-sysv3
+ ;;
+ ebmon29k)
+ basic_machine=a29k-amd
+ os=-ebmon
+ ;;
+ elxsi)
+ basic_machine=elxsi-elxsi
+ os=-bsd
+ ;;
+ encore | umax | mmax)
+ basic_machine=ns32k-encore
+ ;;
+ es1800 | OSE68k | ose68k | ose | OSE)
+ basic_machine=m68k-ericsson
+ os=-ose
+ ;;
+ fx2800)
+ basic_machine=i860-alliant
+ ;;
+ genix)
+ basic_machine=ns32k-ns
+ ;;
+ gmicro)
+ basic_machine=tron-gmicro
+ os=-sysv
+ ;;
+ go32)
+ basic_machine=i386-pc
+ os=-go32
+ ;;
+ h3050r* | hiux*)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ h8300hms)
+ basic_machine=h8300-hitachi
+ os=-hms
+ ;;
+ h8300xray)
+ basic_machine=h8300-hitachi
+ os=-xray
+ ;;
+ h8500hms)
+ basic_machine=h8500-hitachi
+ os=-hms
+ ;;
+ harris)
+ basic_machine=m88k-harris
+ os=-sysv3
+ ;;
+ hp300-*)
+ basic_machine=m68k-hp
+ ;;
+ hp300bsd)
+ basic_machine=m68k-hp
+ os=-bsd
+ ;;
+ hp300hpux)
+ basic_machine=m68k-hp
+ os=-hpux
+ ;;
+ hp3k9[0-9][0-9] | hp9[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hp9k2[0-9][0-9] | hp9k31[0-9])
+ basic_machine=m68000-hp
+ ;;
+ hp9k3[2-9][0-9])
+ basic_machine=m68k-hp
+ ;;
+ hp9k6[0-9][0-9] | hp6[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hp9k7[0-79][0-9] | hp7[0-79][0-9])
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k78[0-9] | hp78[0-9])
+ # FIXME: really hppa2.0-hp
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+ # FIXME: really hppa2.0-hp
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[0-9][13679] | hp8[0-9][13679])
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[0-9][0-9] | hp8[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hppa-next)
+ os=-nextstep3
+ ;;
+ hppaosf)
+ basic_machine=hppa1.1-hp
+ os=-osf
+ ;;
+ hppro)
+ basic_machine=hppa1.1-hp
+ os=-proelf
+ ;;
+ i370-ibm* | ibm*)
+ basic_machine=i370-ibm
+ ;;
+# I'm not sure what "Sysv32" means. Should this be sysv3.2?
+ i*86v32)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv32
+ ;;
+ i*86v4*)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv4
+ ;;
+ i*86v)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv
+ ;;
+ i*86sol2)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-solaris2
+ ;;
+ i386mach)
+ basic_machine=i386-mach
+ os=-mach
+ ;;
+ i386-vsta | vsta)
+ basic_machine=i386-unknown
+ os=-vsta
+ ;;
+ iris | iris4d)
+ basic_machine=mips-sgi
+ case $os in
+ -irix*)
+ ;;
+ *)
+ os=-irix4
+ ;;
+ esac
+ ;;
+ isi68 | isi)
+ basic_machine=m68k-isi
+ os=-sysv
+ ;;
+ m88k-omron*)
+ basic_machine=m88k-omron
+ ;;
+ magnum | m3230)
+ basic_machine=mips-mips
+ os=-sysv
+ ;;
+ merlin)
+ basic_machine=ns32k-utek
+ os=-sysv
+ ;;
+ mingw32)
+ basic_machine=i386-pc
+ os=-mingw32
+ ;;
+ miniframe)
+ basic_machine=m68000-convergent
+ ;;
+ *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
+ basic_machine=m68k-atari
+ os=-mint
+ ;;
+ mips3*-*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
+ ;;
+ mips3*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
+ ;;
+ mmix*)
+ basic_machine=mmix-knuth
+ os=-mmixware
+ ;;
+ monitor)
+ basic_machine=m68k-rom68k
+ os=-coff
+ ;;
+ morphos)
+ basic_machine=powerpc-unknown
+ os=-morphos
+ ;;
+ msdos)
+ basic_machine=i386-pc
+ os=-msdos
+ ;;
+ mvs)
+ basic_machine=i370-ibm
+ os=-mvs
+ ;;
+ ncr3000)
+ basic_machine=i486-ncr
+ os=-sysv4
+ ;;
+ netbsd386)
+ basic_machine=i386-unknown
+ os=-netbsd
+ ;;
+ netwinder)
+ basic_machine=armv4l-rebel
+ os=-linux
+ ;;
+ news | news700 | news800 | news900)
+ basic_machine=m68k-sony
+ os=-newsos
+ ;;
+ news1000)
+ basic_machine=m68030-sony
+ os=-newsos
+ ;;
+ news-3600 | risc-news)
+ basic_machine=mips-sony
+ os=-newsos
+ ;;
+ necv70)
+ basic_machine=v70-nec
+ os=-sysv
+ ;;
+ next | m*-next )
+ basic_machine=m68k-next
+ case $os in
+ -nextstep* )
+ ;;
+ -ns2*)
+ os=-nextstep2
+ ;;
+ *)
+ os=-nextstep3
+ ;;
+ esac
+ ;;
+ nh3000)
+ basic_machine=m68k-harris
+ os=-cxux
+ ;;
+ nh[45]000)
+ basic_machine=m88k-harris
+ os=-cxux
+ ;;
+ nindy960)
+ basic_machine=i960-intel
+ os=-nindy
+ ;;
+ mon960)
+ basic_machine=i960-intel
+ os=-mon960
+ ;;
+ nonstopux)
+ basic_machine=mips-compaq
+ os=-nonstopux
+ ;;
+ np1)
+ basic_machine=np1-gould
+ ;;
+ nv1)
+ basic_machine=nv1-cray
+ os=-unicosmp
+ ;;
+ nsr-tandem)
+ basic_machine=nsr-tandem
+ ;;
+ op50n-* | op60c-*)
+ basic_machine=hppa1.1-oki
+ os=-proelf
+ ;;
+ or32 | or32-*)
+ basic_machine=or32-unknown
+ os=-coff
+ ;;
+ os400)
+ basic_machine=powerpc-ibm
+ os=-os400
+ ;;
+ OSE68000 | ose68000)
+ basic_machine=m68000-ericsson
+ os=-ose
+ ;;
+ os68k)
+ basic_machine=m68k-none
+ os=-os68k
+ ;;
+ pa-hitachi)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ paragon)
+ basic_machine=i860-intel
+ os=-osf
+ ;;
+ pbd)
+ basic_machine=sparc-tti
+ ;;
+ pbb)
+ basic_machine=m68k-tti
+ ;;
+ pc532 | pc532-*)
+ basic_machine=ns32k-pc532
+ ;;
+ pentium | p5 | k5 | k6 | nexgen | viac3)
+ basic_machine=i586-pc
+ ;;
+ pentiumpro | p6 | 6x86 | athlon | athlon_*)
+ basic_machine=i686-pc
+ ;;
+ pentiumii | pentium2 | pentiumiii | pentium3)
+ basic_machine=i686-pc
+ ;;
+ pentium4)
+ basic_machine=i786-pc
+ ;;
+ pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
+ basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentiumpro-* | p6-* | 6x86-* | athlon-*)
+ basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
+ basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentium4-*)
+ basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pn)
+ basic_machine=pn-gould
+ ;;
+ power) basic_machine=power-ibm
+ ;;
+ ppc) basic_machine=powerpc-unknown
+ ;;
+ ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppcle | powerpclittle | ppc-le | powerpc-little)
+ basic_machine=powerpcle-unknown
+ ;;
+ ppcle-* | powerpclittle-*)
+ basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppc64) basic_machine=powerpc64-unknown
+ ;;
+ ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppc64le | powerpc64little | ppc64-le | powerpc64-little)
+ basic_machine=powerpc64le-unknown
+ ;;
+ ppc64le-* | powerpc64little-*)
+ basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ps2)
+ basic_machine=i386-ibm
+ ;;
+ pw32)
+ basic_machine=i586-unknown
+ os=-pw32
+ ;;
+ rom68k)
+ basic_machine=m68k-rom68k
+ os=-coff
+ ;;
+ rm[46]00)
+ basic_machine=mips-siemens
+ ;;
+ rtpc | rtpc-*)
+ basic_machine=romp-ibm
+ ;;
+ s390 | s390-*)
+ basic_machine=s390-ibm
+ ;;
+ s390x | s390x-*)
+ basic_machine=s390x-ibm
+ ;;
+ sa29200)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ sb1)
+ basic_machine=mipsisa64sb1-unknown
+ ;;
+ sb1el)
+ basic_machine=mipsisa64sb1el-unknown
+ ;;
+ sei)
+ basic_machine=mips-sei
+ os=-seiux
+ ;;
+ sequent)
+ basic_machine=i386-sequent
+ ;;
+ sh)
+ basic_machine=sh-hitachi
+ os=-hms
+ ;;
+ sh64)
+ basic_machine=sh64-unknown
+ ;;
+ sparclite-wrs | simso-wrs)
+ basic_machine=sparclite-wrs
+ os=-vxworks
+ ;;
+ sps7)
+ basic_machine=m68k-bull
+ os=-sysv2
+ ;;
+ spur)
+ basic_machine=spur-unknown
+ ;;
+ st2000)
+ basic_machine=m68k-tandem
+ ;;
+ stratus)
+ basic_machine=i860-stratus
+ os=-sysv4
+ ;;
+ sun2)
+ basic_machine=m68000-sun
+ ;;
+ sun2os3)
+ basic_machine=m68000-sun
+ os=-sunos3
+ ;;
+ sun2os4)
+ basic_machine=m68000-sun
+ os=-sunos4
+ ;;
+ sun3os3)
+ basic_machine=m68k-sun
+ os=-sunos3
+ ;;
+ sun3os4)
+ basic_machine=m68k-sun
+ os=-sunos4
+ ;;
+ sun4os3)
+ basic_machine=sparc-sun
+ os=-sunos3
+ ;;
+ sun4os4)
+ basic_machine=sparc-sun
+ os=-sunos4
+ ;;
+ sun4sol2)
+ basic_machine=sparc-sun
+ os=-solaris2
+ ;;
+ sun3 | sun3-*)
+ basic_machine=m68k-sun
+ ;;
+ sun4)
+ basic_machine=sparc-sun
+ ;;
+ sun386 | sun386i | roadrunner)
+ basic_machine=i386-sun
+ ;;
+ sv1)
+ basic_machine=sv1-cray
+ os=-unicos
+ ;;
+ symmetry)
+ basic_machine=i386-sequent
+ os=-dynix
+ ;;
+ t3e)
+ basic_machine=alphaev5-cray
+ os=-unicos
+ ;;
+ t90)
+ basic_machine=t90-cray
+ os=-unicos
+ ;;
+ tic54x | c54x*)
+ basic_machine=tic54x-unknown
+ os=-coff
+ ;;
+ tic55x | c55x*)
+ basic_machine=tic55x-unknown
+ os=-coff
+ ;;
+ tic6x | c6x*)
+ basic_machine=tic6x-unknown
+ os=-coff
+ ;;
+ tx39)
+ basic_machine=mipstx39-unknown
+ ;;
+ tx39el)
+ basic_machine=mipstx39el-unknown
+ ;;
+ toad1)
+ basic_machine=pdp10-xkl
+ os=-tops20
+ ;;
+ tower | tower-32)
+ basic_machine=m68k-ncr
+ ;;
+ tpf)
+ basic_machine=s390x-ibm
+ os=-tpf
+ ;;
+ udi29k)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ ultra3)
+ basic_machine=a29k-nyu
+ os=-sym1
+ ;;
+ v810 | necv810)
+ basic_machine=v810-nec
+ os=-none
+ ;;
+ vaxv)
+ basic_machine=vax-dec
+ os=-sysv
+ ;;
+ vms)
+ basic_machine=vax-dec
+ os=-vms
+ ;;
+ vpp*|vx|vx-*)
+ basic_machine=f301-fujitsu
+ ;;
+ vxworks960)
+ basic_machine=i960-wrs
+ os=-vxworks
+ ;;
+ vxworks68)
+ basic_machine=m68k-wrs
+ os=-vxworks
+ ;;
+ vxworks29k)
+ basic_machine=a29k-wrs
+ os=-vxworks
+ ;;
+ w65*)
+ basic_machine=w65-wdc
+ os=-none
+ ;;
+ w89k-*)
+ basic_machine=hppa1.1-winbond
+ os=-proelf
+ ;;
+ xps | xps100)
+ basic_machine=xps100-honeywell
+ ;;
+ ymp)
+ basic_machine=ymp-cray
+ os=-unicos
+ ;;
+ z8k-*-coff)
+ basic_machine=z8k-unknown
+ os=-sim
+ ;;
+ none)
+ basic_machine=none-none
+ os=-none
+ ;;
+
+# Here we handle the default manufacturer of certain CPU types. It is in
+# some cases the only manufacturer, in others, it is the most popular.
+ w89k)
+ basic_machine=hppa1.1-winbond
+ ;;
+ op50n)
+ basic_machine=hppa1.1-oki
+ ;;
+ op60c)
+ basic_machine=hppa1.1-oki
+ ;;
+ romp)
+ basic_machine=romp-ibm
+ ;;
+ rs6000)
+ basic_machine=rs6000-ibm
+ ;;
+ vax)
+ basic_machine=vax-dec
+ ;;
+ pdp10)
+ # there are many clones, so DEC is not a safe bet
+ basic_machine=pdp10-unknown
+ ;;
+ pdp11)
+ basic_machine=pdp11-dec
+ ;;
+ we32k)
+ basic_machine=we32k-att
+ ;;
+ sh3 | sh4 | sh[34]eb | sh[1234]le | sh[23]ele)
+ basic_machine=sh-unknown
+ ;;
+ sh64)
+ basic_machine=sh64-unknown
+ ;;
+ sparc | sparcv9 | sparcv9b)
+ basic_machine=sparc-sun
+ ;;
+ cydra)
+ basic_machine=cydra-cydrome
+ ;;
+ orion)
+ basic_machine=orion-highlevel
+ ;;
+ orion105)
+ basic_machine=clipper-highlevel
+ ;;
+ mac | mpw | mac-mpw)
+ basic_machine=m68k-apple
+ ;;
+ pmac | pmac-mpw)
+ basic_machine=powerpc-apple
+ ;;
+ *-unknown)
+ # Make sure to match an already-canonicalized machine name.
+ ;;
+ *)
+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $basic_machine in
+ *-digital*)
+ basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
+ ;;
+ *-commodore*)
+ basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
+ ;;
+ *)
+ ;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if [ x"$os" != x"" ]
+then
+case $os in
+ # First match some system type aliases
+ # that might get confused with valid system types.
+ # -solaris* is a basic system type, with this one exception.
+ -solaris1 | -solaris1.*)
+ os=`echo $os | sed -e 's|solaris1|sunos4|'`
+ ;;
+ -solaris)
+ os=-solaris2
+ ;;
+ -svr4*)
+ os=-sysv4
+ ;;
+ -unixware*)
+ os=-sysv4.2uw
+ ;;
+ -gnu/linux*)
+ os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
+ ;;
+ # First accept the basic system types.
+ # The portable systems comes first.
+ # Each alternative MUST END IN A *, to match a version number.
+ # -sysv* is not here because it comes later, after sysvr4.
+ -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
+ | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\
+ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \
+ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
+ | -aos* \
+ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
+ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
+ | -hiux* | -386bsd* | -knetbsd* | -netbsd* | -openbsd* | -kfreebsd* | -freebsd* | -riscix* \
+ | -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
+ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
+ | -chorusos* | -chorusrdb* \
+ | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+ | -mingw32* | -linux-gnu* | -linux-uclibc* | -uxpv* | -beos* | -mpeix* | -udk* \
+ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
+ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
+ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
+ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
+ | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
+ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly*)
+ # Remember, each alternative MUST END IN *, to match a version number.
+ ;;
+ -qnx*)
+ case $basic_machine in
+ x86-* | i*86-*)
+ ;;
+ *)
+ os=-nto$os
+ ;;
+ esac
+ ;;
+ -nto-qnx*)
+ ;;
+ -nto*)
+ os=`echo $os | sed -e 's|nto|nto-qnx|'`
+ ;;
+ -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
+ | -windows* | -osx | -abug | -netware* | -os9* | -beos* \
+ | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
+ ;;
+ -mac*)
+ os=`echo $os | sed -e 's|mac|macos|'`
+ ;;
+ -linux-dietlibc)
+ os=-linux-dietlibc
+ ;;
+ -linux*)
+ os=`echo $os | sed -e 's|linux|linux-gnu|'`
+ ;;
+ -sunos5*)
+ os=`echo $os | sed -e 's|sunos5|solaris2|'`
+ ;;
+ -sunos6*)
+ os=`echo $os | sed -e 's|sunos6|solaris3|'`
+ ;;
+ -opened*)
+ os=-openedition
+ ;;
+ -os400*)
+ os=-os400
+ ;;
+ -wince*)
+ os=-wince
+ ;;
+ -osfrose*)
+ os=-osfrose
+ ;;
+ -osf*)
+ os=-osf
+ ;;
+ -utek*)
+ os=-bsd
+ ;;
+ -dynix*)
+ os=-bsd
+ ;;
+ -acis*)
+ os=-aos
+ ;;
+ -atheos*)
+ os=-atheos
+ ;;
+ -syllable*)
+ os=-syllable
+ ;;
+ -386bsd)
+ os=-bsd
+ ;;
+ -ctix* | -uts*)
+ os=-sysv
+ ;;
+ -nova*)
+ os=-rtmk-nova
+ ;;
+ -ns2 )
+ os=-nextstep2
+ ;;
+ -nsk*)
+ os=-nsk
+ ;;
+ # Preserve the version number of sinix5.
+ -sinix5.*)
+ os=`echo $os | sed -e 's|sinix|sysv|'`
+ ;;
+ -sinix*)
+ os=-sysv4
+ ;;
+ -tpf*)
+ os=-tpf
+ ;;
+ -triton*)
+ os=-sysv3
+ ;;
+ -oss*)
+ os=-sysv3
+ ;;
+ -svr4)
+ os=-sysv4
+ ;;
+ -svr3)
+ os=-sysv3
+ ;;
+ -sysvr4)
+ os=-sysv4
+ ;;
+ # This must come after -sysvr4.
+ -sysv*)
+ ;;
+ -ose*)
+ os=-ose
+ ;;
+ -es1800*)
+ os=-ose
+ ;;
+ -xenix)
+ os=-xenix
+ ;;
+ -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+ os=-mint
+ ;;
+ -aros*)
+ os=-aros
+ ;;
+ -kaos*)
+ os=-kaos
+ ;;
+ -none)
+ ;;
+ *)
+ # Get rid of the `-' at the beginning of $os.
+ os=`echo $os | sed 's/[^-]*-//'`
+ echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system. Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+case $basic_machine in
+ *-acorn)
+ os=-riscix1.2
+ ;;
+ arm*-rebel)
+ os=-linux
+ ;;
+ arm*-semi)
+ os=-aout
+ ;;
+ c4x-* | tic4x-*)
+ os=-coff
+ ;;
+ # This must come before the *-dec entry.
+ pdp10-*)
+ os=-tops20
+ ;;
+ pdp11-*)
+ os=-none
+ ;;
+ *-dec | vax-*)
+ os=-ultrix4.2
+ ;;
+ m68*-apollo)
+ os=-domain
+ ;;
+ i386-sun)
+ os=-sunos4.0.2
+ ;;
+ m68000-sun)
+ os=-sunos3
+ # This also exists in the configure program, but was not the
+ # default.
+ # os=-sunos4
+ ;;
+ m68*-cisco)
+ os=-aout
+ ;;
+ mips*-cisco)
+ os=-elf
+ ;;
+ mips*-*)
+ os=-elf
+ ;;
+ or32-*)
+ os=-coff
+ ;;
+ *-tti) # must be before sparc entry or we get the wrong os.
+ os=-sysv3
+ ;;
+ sparc-* | *-sun)
+ os=-sunos4.1.1
+ ;;
+ *-be)
+ os=-beos
+ ;;
+ *-ibm)
+ os=-aix
+ ;;
+ *-wec)
+ os=-proelf
+ ;;
+ *-winbond)
+ os=-proelf
+ ;;
+ *-oki)
+ os=-proelf
+ ;;
+ *-hp)
+ os=-hpux
+ ;;
+ *-hitachi)
+ os=-hiux
+ ;;
+ i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+ os=-sysv
+ ;;
+ *-cbm)
+ os=-amigaos
+ ;;
+ *-dg)
+ os=-dgux
+ ;;
+ *-dolphin)
+ os=-sysv3
+ ;;
+ m68k-ccur)
+ os=-rtu
+ ;;
+ m88k-omron*)
+ os=-luna
+ ;;
+ *-next )
+ os=-nextstep
+ ;;
+ *-sequent)
+ os=-ptx
+ ;;
+ *-crds)
+ os=-unos
+ ;;
+ *-ns)
+ os=-genix
+ ;;
+ i370-*)
+ os=-mvs
+ ;;
+ *-next)
+ os=-nextstep3
+ ;;
+ *-gould)
+ os=-sysv
+ ;;
+ *-highlevel)
+ os=-bsd
+ ;;
+ *-encore)
+ os=-bsd
+ ;;
+ *-sgi)
+ os=-irix
+ ;;
+ *-siemens)
+ os=-sysv4
+ ;;
+ *-masscomp)
+ os=-rtu
+ ;;
+ f30[01]-fujitsu | f700-fujitsu)
+ os=-uxpv
+ ;;
+ *-rom68k)
+ os=-coff
+ ;;
+ *-*bug)
+ os=-coff
+ ;;
+ *-apple)
+ os=-macos
+ ;;
+ *-atari*)
+ os=-mint
+ ;;
+ *)
+ os=-none
+ ;;
+esac
+fi
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer. We pick the logical manufacturer.
+vendor=unknown
+case $basic_machine in
+ *-unknown)
+ case $os in
+ -riscix*)
+ vendor=acorn
+ ;;
+ -sunos*)
+ vendor=sun
+ ;;
+ -aix*)
+ vendor=ibm
+ ;;
+ -beos*)
+ vendor=be
+ ;;
+ -hpux*)
+ vendor=hp
+ ;;
+ -mpeix*)
+ vendor=hp
+ ;;
+ -hiux*)
+ vendor=hitachi
+ ;;
+ -unos*)
+ vendor=crds
+ ;;
+ -dgux*)
+ vendor=dg
+ ;;
+ -luna*)
+ vendor=omron
+ ;;
+ -genix*)
+ vendor=ns
+ ;;
+ -mvs* | -opened*)
+ vendor=ibm
+ ;;
+ -os400*)
+ vendor=ibm
+ ;;
+ -ptx*)
+ vendor=sequent
+ ;;
+ -tpf*)
+ vendor=ibm
+ ;;
+ -vxsim* | -vxworks* | -windiss*)
+ vendor=wrs
+ ;;
+ -aux*)
+ vendor=apple
+ ;;
+ -hms*)
+ vendor=hitachi
+ ;;
+ -mpw* | -macos*)
+ vendor=apple
+ ;;
+ -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+ vendor=atari
+ ;;
+ -vos*)
+ vendor=stratus
+ ;;
+ esac
+ basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
+ ;;
+esac
+
+echo $basic_machine$os
+exit 0
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/trunk/main/libresample/configure b/trunk/main/libresample/configure
new file mode 100755
index 000000000..638280399
--- /dev/null
+++ b/trunk/main/libresample/configure
@@ -0,0 +1,4552 @@
+#! /bin/sh
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.59.
+#
+# Copyright (C) 2003 Free Software Foundation, Inc.
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## --------------------- ##
+## M4sh Initialization. ##
+## --------------------- ##
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
+ set -o posix
+fi
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+ as_unset=unset
+else
+ as_unset=false
+fi
+
+
+# Work around bugs in pre-3.0 UWIN ksh.
+$as_unset ENV MAIL MAILPATH
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+for as_var in \
+ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+ LC_TELEPHONE LC_TIME
+do
+ if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+ eval $as_var=C; export $as_var
+ else
+ $as_unset $as_var
+ fi
+done
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)$' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; }
+ /^X\/\(\/\/\)$/{ s//\1/; q; }
+ /^X\/\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+
+
+# PATH needs CR, and LINENO needs CR and PATH.
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ echo "#! /bin/sh" >conf$$.sh
+ echo "exit 0" >>conf$$.sh
+ chmod +x conf$$.sh
+ if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+ PATH_SEPARATOR=';'
+ else
+ PATH_SEPARATOR=:
+ fi
+ rm -f conf$$.sh
+fi
+
+
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x$as_lineno_3" = "x$as_lineno_2" || {
+ # Find who we are. Look in the path if we contain no path at all
+ # relative or not.
+ case $0 in
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+
+ ;;
+ esac
+ # We did not find ourselves, most probably we were run as `sh COMMAND'
+ # in which case we are not to be found in the path.
+ if test "x$as_myself" = x; then
+ as_myself=$0
+ fi
+ if test ! -f "$as_myself"; then
+ { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2
+ { (exit 1); exit 1; }; }
+ fi
+ case $CONFIG_SHELL in
+ '')
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for as_base in sh bash ksh sh5; do
+ case $as_dir in
+ /*)
+ if ("$as_dir/$as_base" -c '
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then
+ $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; }
+ $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; }
+ CONFIG_SHELL=$as_dir/$as_base
+ export CONFIG_SHELL
+ exec "$CONFIG_SHELL" "$0" ${1+"$@"}
+ fi;;
+ esac
+ done
+done
+;;
+ esac
+
+ # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+ # uniformly replaced by the line number. The first 'sed' inserts a
+ # line-number line before each line; the second 'sed' does the real
+ # work. The second script uses 'N' to pair each line-number line
+ # with the numbered line, and appends trailing '-' during
+ # substitution so that $LINENO is not a special case at line end.
+ # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+ # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-)
+ sed '=' <$as_myself |
+ sed '
+ N
+ s,$,-,
+ : loop
+ s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3,
+ t loop
+ s,-$,,
+ s,^['$as_cr_digits']*\n,,
+ ' >$as_me.lineno &&
+ chmod +x $as_me.lineno ||
+ { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2
+ { (exit 1); exit 1; }; }
+
+ # Don't try to exec as it changes $[0], causing all sort of problems
+ # (the dirname of $[0] is not the place where we might find the
+ # original and so on. Autoconf is especially sensible to this).
+ . ./$as_me.lineno
+ # Exit status is that of the last command.
+ exit
+}
+
+
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+ *c*,-n*) ECHO_N= ECHO_C='
+' ECHO_T=' ' ;;
+ *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;;
+ *) ECHO_N= ECHO_C='\c' ECHO_T= ;;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+ # We could just check for DJGPP; but this test a) works b) is more generic
+ # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04).
+ if test -f conf$$.exe; then
+ # Don't use ln at all; we don't have any links
+ as_ln_s='cp -p'
+ else
+ as_ln_s='ln -s'
+ fi
+elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+else
+ as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.file
+
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p=:
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+as_executable_p="test -f"
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.
+as_nl='
+'
+IFS=" $as_nl"
+
+# CDPATH.
+$as_unset CDPATH
+
+
+# Name of the host.
+# hostname on some systems (SVR3.2, Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+exec 6>&1
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_config_libobj_dir=.
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+# Maximum number of lines to put in a shell here document.
+# This variable seems obsolete. It should probably be removed, and
+# only ac_max_sed_lines should be used.
+: ${ac_max_here_lines=38}
+
+# Identity of this package.
+PACKAGE_NAME=
+PACKAGE_TARNAME=
+PACKAGE_VERSION=
+PACKAGE_STRING=
+PACKAGE_BUGREPORT=
+
+ac_unique_file="src/resample.c"
+# Factoring default headers for most tests.
+ac_includes_default="\
+#include <stdio.h>
+#if HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#if HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#if STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# if HAVE_STDLIB_H
+# include <stdlib.h>
+# endif
+#endif
+#if HAVE_STRING_H
+# if !STDC_HEADERS && HAVE_MEMORY_H
+# include <memory.h>
+# endif
+# include <string.h>
+#endif
+#if HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
+#else
+# if HAVE_STDINT_H
+# include <stdint.h>
+# endif
+#endif
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif"
+
+ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT RANLIB ac_ct_RANLIB AR TARGETS CPP EGREP LIBOBJS LTLIBOBJS'
+ac_subst_files=''
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datadir='${prefix}/share'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+libdir='${exec_prefix}/lib'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+infodir='${prefix}/info'
+mandir='${prefix}/man'
+
+ac_prev=
+for ac_option
+do
+ # If the previous option needs an argument, assign it.
+ if test -n "$ac_prev"; then
+ eval "$ac_prev=\$ac_option"
+ ac_prev=
+ continue
+ fi
+
+ ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'`
+
+ # Accept the important Cygnus configure options, so we can diagnose typos.
+
+ case $ac_option in
+
+ -bindir | --bindir | --bindi | --bind | --bin | --bi)
+ ac_prev=bindir ;;
+ -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+ bindir=$ac_optarg ;;
+
+ -build | --build | --buil | --bui | --bu)
+ ac_prev=build_alias ;;
+ -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+ build_alias=$ac_optarg ;;
+
+ -cache-file | --cache-file | --cache-fil | --cache-fi \
+ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+ ac_prev=cache_file ;;
+ -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+ cache_file=$ac_optarg ;;
+
+ --config-cache | -C)
+ cache_file=config.cache ;;
+
+ -datadir | --datadir | --datadi | --datad | --data | --dat | --da)
+ ac_prev=datadir ;;
+ -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
+ | --da=*)
+ datadir=$ac_optarg ;;
+
+ -disable-* | --disable-*)
+ ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+ { (exit 1); exit 1; }; }
+ ac_feature=`echo $ac_feature | sed 's/-/_/g'`
+ eval "enable_$ac_feature=no" ;;
+
+ -enable-* | --enable-*)
+ ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+ { (exit 1); exit 1; }; }
+ ac_feature=`echo $ac_feature | sed 's/-/_/g'`
+ case $ac_option in
+ *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;;
+ *) ac_optarg=yes ;;
+ esac
+ eval "enable_$ac_feature='$ac_optarg'" ;;
+
+ -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+ | --exec | --exe | --ex)
+ ac_prev=exec_prefix ;;
+ -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+ | --exec=* | --exe=* | --ex=*)
+ exec_prefix=$ac_optarg ;;
+
+ -gas | --gas | --ga | --g)
+ # Obsolete; use --with-gas.
+ with_gas=yes ;;
+
+ -help | --help | --hel | --he | -h)
+ ac_init_help=long ;;
+ -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+ ac_init_help=recursive ;;
+ -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+ ac_init_help=short ;;
+
+ -host | --host | --hos | --ho)
+ ac_prev=host_alias ;;
+ -host=* | --host=* | --hos=* | --ho=*)
+ host_alias=$ac_optarg ;;
+
+ -includedir | --includedir | --includedi | --included | --include \
+ | --includ | --inclu | --incl | --inc)
+ ac_prev=includedir ;;
+ -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+ | --includ=* | --inclu=* | --incl=* | --inc=*)
+ includedir=$ac_optarg ;;
+
+ -infodir | --infodir | --infodi | --infod | --info | --inf)
+ ac_prev=infodir ;;
+ -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+ infodir=$ac_optarg ;;
+
+ -libdir | --libdir | --libdi | --libd)
+ ac_prev=libdir ;;
+ -libdir=* | --libdir=* | --libdi=* | --libd=*)
+ libdir=$ac_optarg ;;
+
+ -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+ | --libexe | --libex | --libe)
+ ac_prev=libexecdir ;;
+ -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+ | --libexe=* | --libex=* | --libe=*)
+ libexecdir=$ac_optarg ;;
+
+ -localstatedir | --localstatedir | --localstatedi | --localstated \
+ | --localstate | --localstat | --localsta | --localst \
+ | --locals | --local | --loca | --loc | --lo)
+ ac_prev=localstatedir ;;
+ -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+ | --localstate=* | --localstat=* | --localsta=* | --localst=* \
+ | --locals=* | --local=* | --loca=* | --loc=* | --lo=*)
+ localstatedir=$ac_optarg ;;
+
+ -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+ ac_prev=mandir ;;
+ -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+ mandir=$ac_optarg ;;
+
+ -nfp | --nfp | --nf)
+ # Obsolete; use --without-fp.
+ with_fp=no ;;
+
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c | -n)
+ no_create=yes ;;
+
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+ no_recursion=yes ;;
+
+ -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+ | --oldin | --oldi | --old | --ol | --o)
+ ac_prev=oldincludedir ;;
+ -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+ oldincludedir=$ac_optarg ;;
+
+ -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+ ac_prev=prefix ;;
+ -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+ prefix=$ac_optarg ;;
+
+ -program-prefix | --program-prefix | --program-prefi | --program-pref \
+ | --program-pre | --program-pr | --program-p)
+ ac_prev=program_prefix ;;
+ -program-prefix=* | --program-prefix=* | --program-prefi=* \
+ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+ program_prefix=$ac_optarg ;;
+
+ -program-suffix | --program-suffix | --program-suffi | --program-suff \
+ | --program-suf | --program-su | --program-s)
+ ac_prev=program_suffix ;;
+ -program-suffix=* | --program-suffix=* | --program-suffi=* \
+ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+ program_suffix=$ac_optarg ;;
+
+ -program-transform-name | --program-transform-name \
+ | --program-transform-nam | --program-transform-na \
+ | --program-transform-n | --program-transform- \
+ | --program-transform | --program-transfor \
+ | --program-transfo | --program-transf \
+ | --program-trans | --program-tran \
+ | --progr-tra | --program-tr | --program-t)
+ ac_prev=program_transform_name ;;
+ -program-transform-name=* | --program-transform-name=* \
+ | --program-transform-nam=* | --program-transform-na=* \
+ | --program-transform-n=* | --program-transform-=* \
+ | --program-transform=* | --program-transfor=* \
+ | --program-transfo=* | --program-transf=* \
+ | --program-trans=* | --program-tran=* \
+ | --progr-tra=* | --program-tr=* | --program-t=*)
+ program_transform_name=$ac_optarg ;;
+
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ silent=yes ;;
+
+ -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+ ac_prev=sbindir ;;
+ -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+ | --sbi=* | --sb=*)
+ sbindir=$ac_optarg ;;
+
+ -sharedstatedir | --sharedstatedir | --sharedstatedi \
+ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+ | --sharedst | --shareds | --shared | --share | --shar \
+ | --sha | --sh)
+ ac_prev=sharedstatedir ;;
+ -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+ | --sha=* | --sh=*)
+ sharedstatedir=$ac_optarg ;;
+
+ -site | --site | --sit)
+ ac_prev=site ;;
+ -site=* | --site=* | --sit=*)
+ site=$ac_optarg ;;
+
+ -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+ ac_prev=srcdir ;;
+ -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+ srcdir=$ac_optarg ;;
+
+ -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+ | --syscon | --sysco | --sysc | --sys | --sy)
+ ac_prev=sysconfdir ;;
+ -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+ sysconfdir=$ac_optarg ;;
+
+ -target | --target | --targe | --targ | --tar | --ta | --t)
+ ac_prev=target_alias ;;
+ -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+ target_alias=$ac_optarg ;;
+
+ -v | -verbose | --verbose | --verbos | --verbo | --verb)
+ verbose=yes ;;
+
+ -version | --version | --versio | --versi | --vers | -V)
+ ac_init_version=: ;;
+
+ -with-* | --with-*)
+ ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid package name: $ac_package" >&2
+ { (exit 1); exit 1; }; }
+ ac_package=`echo $ac_package| sed 's/-/_/g'`
+ case $ac_option in
+ *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;;
+ *) ac_optarg=yes ;;
+ esac
+ eval "with_$ac_package='$ac_optarg'" ;;
+
+ -without-* | --without-*)
+ ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid package name: $ac_package" >&2
+ { (exit 1); exit 1; }; }
+ ac_package=`echo $ac_package | sed 's/-/_/g'`
+ eval "with_$ac_package=no" ;;
+
+ --x)
+ # Obsolete; use --with-x.
+ with_x=yes ;;
+
+ -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+ | --x-incl | --x-inc | --x-in | --x-i)
+ ac_prev=x_includes ;;
+ -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+ x_includes=$ac_optarg ;;
+
+ -x-libraries | --x-libraries | --x-librarie | --x-librari \
+ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+ ac_prev=x_libraries ;;
+ -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+ x_libraries=$ac_optarg ;;
+
+ -*) { echo "$as_me: error: unrecognized option: $ac_option
+Try \`$0 --help' for more information." >&2
+ { (exit 1); exit 1; }; }
+ ;;
+
+ *=*)
+ ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid variable name: $ac_envvar" >&2
+ { (exit 1); exit 1; }; }
+ ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`
+ eval "$ac_envvar='$ac_optarg'"
+ export $ac_envvar ;;
+
+ *)
+ # FIXME: should be removed in autoconf 3.0.
+ echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+ expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+ echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+ : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}
+ ;;
+
+ esac
+done
+
+if test -n "$ac_prev"; then
+ ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+ { echo "$as_me: error: missing argument to $ac_option" >&2
+ { (exit 1); exit 1; }; }
+fi
+
+# Be sure to have absolute paths.
+for ac_var in exec_prefix prefix
+do
+ eval ac_val=$`echo $ac_var`
+ case $ac_val in
+ [\\/$]* | ?:[\\/]* | NONE | '' ) ;;
+ *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+ { (exit 1); exit 1; }; };;
+ esac
+done
+
+# Be sure to have absolute paths.
+for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \
+ localstatedir libdir includedir oldincludedir infodir mandir
+do
+ eval ac_val=$`echo $ac_var`
+ case $ac_val in
+ [\\/$]* | ?:[\\/]* ) ;;
+ *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+ { (exit 1); exit 1; }; };;
+ esac
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+ if test "x$build_alias" = x; then
+ cross_compiling=maybe
+ echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host.
+ If a cross compiler is detected then cross compile mode will be used." >&2
+ elif test "x$build_alias" != "x$host_alias"; then
+ cross_compiling=yes
+ fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+ ac_srcdir_defaulted=yes
+ # Try the directory containing this script, then its parent.
+ ac_confdir=`(dirname "$0") 2>/dev/null ||
+$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$0" : 'X\(//\)[^/]' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X"$0" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+ /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+ /^X\(\/\/\)$/{ s//\1/; q; }
+ /^X\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+ srcdir=$ac_confdir
+ if test ! -r $srcdir/$ac_unique_file; then
+ srcdir=..
+ fi
+else
+ ac_srcdir_defaulted=no
+fi
+if test ! -r $srcdir/$ac_unique_file; then
+ if test "$ac_srcdir_defaulted" = yes; then
+ { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2
+ { (exit 1); exit 1; }; }
+ else
+ { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2
+ { (exit 1); exit 1; }; }
+ fi
+fi
+(cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null ||
+ { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2
+ { (exit 1); exit 1; }; }
+srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'`
+ac_env_build_alias_set=${build_alias+set}
+ac_env_build_alias_value=$build_alias
+ac_cv_env_build_alias_set=${build_alias+set}
+ac_cv_env_build_alias_value=$build_alias
+ac_env_host_alias_set=${host_alias+set}
+ac_env_host_alias_value=$host_alias
+ac_cv_env_host_alias_set=${host_alias+set}
+ac_cv_env_host_alias_value=$host_alias
+ac_env_target_alias_set=${target_alias+set}
+ac_env_target_alias_value=$target_alias
+ac_cv_env_target_alias_set=${target_alias+set}
+ac_cv_env_target_alias_value=$target_alias
+ac_env_CC_set=${CC+set}
+ac_env_CC_value=$CC
+ac_cv_env_CC_set=${CC+set}
+ac_cv_env_CC_value=$CC
+ac_env_CFLAGS_set=${CFLAGS+set}
+ac_env_CFLAGS_value=$CFLAGS
+ac_cv_env_CFLAGS_set=${CFLAGS+set}
+ac_cv_env_CFLAGS_value=$CFLAGS
+ac_env_LDFLAGS_set=${LDFLAGS+set}
+ac_env_LDFLAGS_value=$LDFLAGS
+ac_cv_env_LDFLAGS_set=${LDFLAGS+set}
+ac_cv_env_LDFLAGS_value=$LDFLAGS
+ac_env_CPPFLAGS_set=${CPPFLAGS+set}
+ac_env_CPPFLAGS_value=$CPPFLAGS
+ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set}
+ac_cv_env_CPPFLAGS_value=$CPPFLAGS
+ac_env_CPP_set=${CPP+set}
+ac_env_CPP_value=$CPP
+ac_cv_env_CPP_set=${CPP+set}
+ac_cv_env_CPP_value=$CPP
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+ # Omit some internal or obsolete options to make the list less imposing.
+ # This message is too long to be a string in the A/UX 3.1 sh.
+ cat <<_ACEOF
+\`configure' configures this package to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE. See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+ -h, --help display this help and exit
+ --help=short display options specific to this package
+ --help=recursive display the short help of all the included packages
+ -V, --version display version information and exit
+ -q, --quiet, --silent do not print \`checking...' messages
+ --cache-file=FILE cache test results in FILE [disabled]
+ -C, --config-cache alias for \`--cache-file=config.cache'
+ -n, --no-create do not create output files
+ --srcdir=DIR find the sources in DIR [configure dir or \`..']
+
+_ACEOF
+
+ cat <<_ACEOF
+Installation directories:
+ --prefix=PREFIX install architecture-independent files in PREFIX
+ [$ac_default_prefix]
+ --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX
+ [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+ --bindir=DIR user executables [EPREFIX/bin]
+ --sbindir=DIR system admin executables [EPREFIX/sbin]
+ --libexecdir=DIR program executables [EPREFIX/libexec]
+ --datadir=DIR read-only architecture-independent data [PREFIX/share]
+ --sysconfdir=DIR read-only single-machine data [PREFIX/etc]
+ --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
+ --localstatedir=DIR modifiable single-machine data [PREFIX/var]
+ --libdir=DIR object code libraries [EPREFIX/lib]
+ --includedir=DIR C header files [PREFIX/include]
+ --oldincludedir=DIR C header files for non-gcc [/usr/include]
+ --infodir=DIR info documentation [PREFIX/info]
+ --mandir=DIR man documentation [PREFIX/man]
+_ACEOF
+
+ cat <<\_ACEOF
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+
+ cat <<\_ACEOF
+
+Some influential environment variables:
+ CC C compiler command
+ CFLAGS C compiler flags
+ LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a
+ nonstandard directory <lib dir>
+ CPPFLAGS C/C++ preprocessor flags, e.g. -I<include dir> if you have
+ headers in a nonstandard directory <include dir>
+ CPP C preprocessor
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+_ACEOF
+fi
+
+if test "$ac_init_help" = "recursive"; then
+ # If there are subdirs, report their specific --help.
+ ac_popdir=`pwd`
+ for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+ test -d $ac_dir || continue
+ ac_builddir=.
+
+if test "$ac_dir" != .; then
+ ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+ # A "../" for each directory in $ac_dir_suffix.
+ ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
+else
+ ac_dir_suffix= ac_top_builddir=
+fi
+
+case $srcdir in
+ .) # No --srcdir option. We are building in place.
+ ac_srcdir=.
+ if test -z "$ac_top_builddir"; then
+ ac_top_srcdir=.
+ else
+ ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
+ fi ;;
+ [\\/]* | ?:[\\/]* ) # Absolute path.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir ;;
+ *) # Relative path.
+ ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_builddir$srcdir ;;
+esac
+
+# Do not use `cd foo && pwd` to compute absolute paths, because
+# the directories may not exist.
+case `pwd` in
+.) ac_abs_builddir="$ac_dir";;
+*)
+ case "$ac_dir" in
+ .) ac_abs_builddir=`pwd`;;
+ [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
+ *) ac_abs_builddir=`pwd`/"$ac_dir";;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_builddir=${ac_top_builddir}.;;
+*)
+ case ${ac_top_builddir}. in
+ .) ac_abs_top_builddir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
+ *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_srcdir=$ac_srcdir;;
+*)
+ case $ac_srcdir in
+ .) ac_abs_srcdir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
+ *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_srcdir=$ac_top_srcdir;;
+*)
+ case $ac_top_srcdir in
+ .) ac_abs_top_srcdir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
+ *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
+ esac;;
+esac
+
+ cd $ac_dir
+ # Check for guested configure; otherwise get Cygnus style configure.
+ if test -f $ac_srcdir/configure.gnu; then
+ echo
+ $SHELL $ac_srcdir/configure.gnu --help=recursive
+ elif test -f $ac_srcdir/configure; then
+ echo
+ $SHELL $ac_srcdir/configure --help=recursive
+ elif test -f $ac_srcdir/configure.ac ||
+ test -f $ac_srcdir/configure.in; then
+ echo
+ $ac_configure --help
+ else
+ echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+ fi
+ cd "$ac_popdir"
+ done
+fi
+
+test -n "$ac_init_help" && exit 0
+if $ac_init_version; then
+ cat <<\_ACEOF
+
+Copyright (C) 2003 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+ exit 0
+fi
+exec 5>config.log
+cat >&5 <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by $as_me, which was
+generated by GNU Autoconf 2.59. Invocation command line was
+
+ $ $0 $@
+
+_ACEOF
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown`
+
+/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+hostinfo = `(hostinfo) 2>/dev/null || echo unknown`
+/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown`
+/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ echo "PATH: $as_dir"
+done
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_sep=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+ for ac_arg
+ do
+ case $ac_arg in
+ -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ continue ;;
+ *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*)
+ ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ esac
+ case $ac_pass in
+ 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;;
+ 2)
+ ac_configure_args1="$ac_configure_args1 '$ac_arg'"
+ if test $ac_must_keep_next = true; then
+ ac_must_keep_next=false # Got value, back to normal.
+ else
+ case $ac_arg in
+ *=* | --config-cache | -C | -disable-* | --disable-* \
+ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+ | -with-* | --with-* | -without-* | --without-* | --x)
+ case "$ac_configure_args0 " in
+ "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+ esac
+ ;;
+ -* ) ac_must_keep_next=true ;;
+ esac
+ fi
+ ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'"
+ # Get rid of the leading space.
+ ac_sep=" "
+ ;;
+ esac
+ done
+done
+$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; }
+$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; }
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log. We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Be sure not to use single quotes in there, as some shells,
+# such as our DU 5.0 friend, will then `close' the trap.
+trap 'exit_status=$?
+ # Save into config.log some information that might help in debugging.
+ {
+ echo
+
+ cat <<\_ASBOX
+## ---------------- ##
+## Cache variables. ##
+## ---------------- ##
+_ASBOX
+ echo
+ # The following way of writing the cache mishandles newlines in values,
+{
+ (set) 2>&1 |
+ case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in
+ *ac_space=\ *)
+ sed -n \
+ "s/'"'"'/'"'"'\\\\'"'"''"'"'/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p"
+ ;;
+ *)
+ sed -n \
+ "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p"
+ ;;
+ esac;
+}
+ echo
+
+ cat <<\_ASBOX
+## ----------------- ##
+## Output variables. ##
+## ----------------- ##
+_ASBOX
+ echo
+ for ac_var in $ac_subst_vars
+ do
+ eval ac_val=$`echo $ac_var`
+ echo "$ac_var='"'"'$ac_val'"'"'"
+ done | sort
+ echo
+
+ if test -n "$ac_subst_files"; then
+ cat <<\_ASBOX
+## ------------- ##
+## Output files. ##
+## ------------- ##
+_ASBOX
+ echo
+ for ac_var in $ac_subst_files
+ do
+ eval ac_val=$`echo $ac_var`
+ echo "$ac_var='"'"'$ac_val'"'"'"
+ done | sort
+ echo
+ fi
+
+ if test -s confdefs.h; then
+ cat <<\_ASBOX
+## ----------- ##
+## confdefs.h. ##
+## ----------- ##
+_ASBOX
+ echo
+ sed "/^$/d" confdefs.h | sort
+ echo
+ fi
+ test "$ac_signal" != 0 &&
+ echo "$as_me: caught signal $ac_signal"
+ echo "$as_me: exit $exit_status"
+ } >&5
+ rm -f core *.core &&
+ rm -rf conftest* confdefs* conf$$* $ac_clean_files &&
+ exit $exit_status
+ ' 0
+for ac_signal in 1 2 13 15; do
+ trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -rf conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo >confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer explicitly selected file to automatically selected ones.
+if test -z "$CONFIG_SITE"; then
+ if test "x$prefix" != xNONE; then
+ CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
+ else
+ CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+ fi
+fi
+for ac_site_file in $CONFIG_SITE; do
+ if test -r "$ac_site_file"; then
+ { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5
+echo "$as_me: loading site script $ac_site_file" >&6;}
+ sed 's/^/| /' "$ac_site_file" >&5
+ . "$ac_site_file"
+ fi
+done
+
+if test -r "$cache_file"; then
+ # Some versions of bash will fail to source /dev/null (special
+ # files actually), so we avoid doing that.
+ if test -f "$cache_file"; then
+ { echo "$as_me:$LINENO: loading cache $cache_file" >&5
+echo "$as_me: loading cache $cache_file" >&6;}
+ case $cache_file in
+ [\\/]* | ?:[\\/]* ) . $cache_file;;
+ *) . ./$cache_file;;
+ esac
+ fi
+else
+ { echo "$as_me:$LINENO: creating cache $cache_file" >&5
+echo "$as_me: creating cache $cache_file" >&6;}
+ >$cache_file
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in `(set) 2>&1 |
+ sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do
+ eval ac_old_set=\$ac_cv_env_${ac_var}_set
+ eval ac_new_set=\$ac_env_${ac_var}_set
+ eval ac_old_val="\$ac_cv_env_${ac_var}_value"
+ eval ac_new_val="\$ac_env_${ac_var}_value"
+ case $ac_old_set,$ac_new_set in
+ set,)
+ { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,set)
+ { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,);;
+ *)
+ if test "x$ac_old_val" != "x$ac_new_val"; then
+ { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5
+echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+ { echo "$as_me:$LINENO: former value: $ac_old_val" >&5
+echo "$as_me: former value: $ac_old_val" >&2;}
+ { echo "$as_me:$LINENO: current value: $ac_new_val" >&5
+echo "$as_me: current value: $ac_new_val" >&2;}
+ ac_cache_corrupted=:
+ fi;;
+ esac
+ # Pass precious variables to config.status.
+ if test "$ac_new_set" = set; then
+ case $ac_new_val in
+ *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*)
+ ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+ *) ac_arg=$ac_var=$ac_new_val ;;
+ esac
+ case " $ac_configure_args " in
+ *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy.
+ *) ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+ esac
+ fi
+done
+if $ac_cache_corrupted; then
+ { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5
+echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+ { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5
+echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="${ac_tool_prefix}gcc"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+ ac_ct_CC=$CC
+ # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_CC="gcc"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+ CC=$ac_ct_CC
+else
+ CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="${ac_tool_prefix}cc"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+ ac_ct_CC=$CC
+ # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_CC="cc"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+ CC=$ac_ct_CC
+else
+ CC="$ac_cv_prog_CC"
+fi
+
+fi
+if test -z "$CC"; then
+ # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+ ac_prog_rejected=yes
+ continue
+ fi
+ ac_cv_prog_CC="cc"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+if test $ac_prog_rejected = yes; then
+ # We found a bogon in the path, so make sure we never use it.
+ set dummy $ac_cv_prog_CC
+ shift
+ if test $# != 0; then
+ # We chose a different compiler from the bogus one.
+ # However, it has the same basename, so the bogon will be chosen
+ # first if we set CC to just the basename; use the full file name.
+ shift
+ ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+ fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ for ac_prog in cl
+ do
+ # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+ test -n "$CC" && break
+ done
+fi
+if test -z "$CC"; then
+ ac_ct_CC=$CC
+ for ac_prog in cl
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_CC="$ac_prog"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+ test -n "$ac_ct_CC" && break
+done
+
+ CC=$ac_ct_CC
+fi
+
+fi
+
+
+test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&5
+echo "$as_me: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }
+
+# Provide some information about the compiler.
+echo "$as_me:$LINENO:" \
+ "checking for C compiler version" >&5
+ac_compiler=`set X $ac_compile; echo $2`
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version </dev/null >&5\"") >&5
+ (eval $ac_compiler --version </dev/null >&5) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v </dev/null >&5\"") >&5
+ (eval $ac_compiler -v </dev/null >&5) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V </dev/null >&5\"") >&5
+ (eval $ac_compiler -V </dev/null >&5) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+echo "$as_me:$LINENO: checking for C compiler default output file name" >&5
+echo $ECHO_N "checking for C compiler default output file name... $ECHO_C" >&6
+ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+if { (eval echo "$as_me:$LINENO: \"$ac_link_default\"") >&5
+ (eval $ac_link_default) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ # Find the output, starting from the most likely. This scheme is
+# not robust to junk in `.', hence go to wildcards (a.*) only as a last
+# resort.
+
+# Be careful to initialize this variable, since it used to be cached.
+# Otherwise an old cache value of `no' led to `EXEEXT = no' in a Makefile.
+ac_cv_exeext=
+# b.out is created by i960 compilers.
+for ac_file in a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out
+do
+ test -f "$ac_file" || continue
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj )
+ ;;
+ conftest.$ac_ext )
+ # This is the source file.
+ ;;
+ [ab].out )
+ # We found the default executable, but exeext='' is most
+ # certainly right.
+ break;;
+ *.* )
+ ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+ # FIXME: I believe we export ac_cv_exeext for Libtool,
+ # but it would be cool to find out if it's true. Does anybody
+ # maintain Libtool? --akim.
+ export ac_cv_exeext
+ break;;
+ * )
+ break;;
+ esac
+done
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { echo "$as_me:$LINENO: error: C compiler cannot create executables
+See \`config.log' for more details." >&5
+echo "$as_me: error: C compiler cannot create executables
+See \`config.log' for more details." >&2;}
+ { (exit 77); exit 77; }; }
+fi
+
+ac_exeext=$ac_cv_exeext
+echo "$as_me:$LINENO: result: $ac_file" >&5
+echo "${ECHO_T}$ac_file" >&6
+
+# Check the compiler produces executables we can run. If not, either
+# the compiler is broken, or we cross compile.
+echo "$as_me:$LINENO: checking whether the C compiler works" >&5
+echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6
+# FIXME: These cross compiler hacks should be removed for Autoconf 3.0
+# If not cross compiling, check that we can run a simple program.
+if test "$cross_compiling" != yes; then
+ if { ac_try='./$ac_file'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ cross_compiling=no
+ else
+ if test "$cross_compiling" = maybe; then
+ cross_compiling=yes
+ else
+ { { echo "$as_me:$LINENO: error: cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+ fi
+fi
+echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+
+rm -f a.out a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+# Check the compiler produces executables we can run. If not, either
+# the compiler is broken, or we cross compile.
+echo "$as_me:$LINENO: checking whether we are cross compiling" >&5
+echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6
+echo "$as_me:$LINENO: result: $cross_compiling" >&5
+echo "${ECHO_T}$cross_compiling" >&6
+
+echo "$as_me:$LINENO: checking for suffix of executables" >&5
+echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+ test -f "$ac_file" || continue
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;;
+ *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+ export ac_cv_exeext
+ break;;
+ * ) break;;
+ esac
+done
+else
+ { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+rm -f conftest$ac_cv_exeext
+echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5
+echo "${ECHO_T}$ac_cv_exeext" >&6
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+echo "$as_me:$LINENO: checking for suffix of object files" >&5
+echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6
+if test "${ac_cv_objext+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; then
+ for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg ) ;;
+ *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+ break;;
+ esac
+done
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_objext" >&5
+echo "${ECHO_T}$ac_cv_objext" >&6
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5
+echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6
+if test "${ac_cv_c_compiler_gnu+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+#ifndef __GNUC__
+ choke me
+#endif
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_compiler_gnu=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_compiler_gnu=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5
+echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6
+GCC=`test $ac_compiler_gnu = yes && echo yes`
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+CFLAGS="-g"
+echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5
+echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6
+if test "${ac_cv_prog_cc_g+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_prog_cc_g=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_prog_cc_g=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5
+echo "${ECHO_T}$ac_cv_prog_cc_g" >&6
+if test "$ac_test_CFLAGS" = set; then
+ CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+ if test "$GCC" = yes; then
+ CFLAGS="-g -O2"
+ else
+ CFLAGS="-g"
+ fi
+else
+ if test "$GCC" = yes; then
+ CFLAGS="-O2"
+ else
+ CFLAGS=
+ fi
+fi
+echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5
+echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6
+if test "${ac_cv_prog_cc_stdc+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_prog_cc_stdc=no
+ac_save_CC=$CC
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+ char **p;
+ int i;
+{
+ return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+ char *s;
+ va_list v;
+ va_start (v,p);
+ s = g (p, va_arg (v,int));
+ va_end (v);
+ return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has
+ function prototypes and stuff, but not '\xHH' hex character constants.
+ These don't provoke an error unfortunately, instead are silently treated
+ as 'x'. The following induces an error, until -std1 is added to get
+ proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an
+ array size at least. It's necessary to write '\x00'==0 to get something
+ that's true only with -std1. */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1];
+ ;
+ return 0;
+}
+_ACEOF
+# Don't try gcc -ansi; that turns off useful extensions and
+# breaks some systems' header files.
+# AIX -qlanglvl=ansi
+# Ultrix and OSF/1 -std1
+# HP-UX 10.20 and later -Ae
+# HP-UX older versions -Aa -D_HPUX_SOURCE
+# SVR4 -Xc -D__EXTENSIONS__
+for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+ CC="$ac_save_CC $ac_arg"
+ rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_prog_cc_stdc=$ac_arg
+break
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext
+done
+rm -f conftest.$ac_ext conftest.$ac_objext
+CC=$ac_save_CC
+
+fi
+
+case "x$ac_cv_prog_cc_stdc" in
+ x|xno)
+ echo "$as_me:$LINENO: result: none needed" >&5
+echo "${ECHO_T}none needed" >&6 ;;
+ *)
+ echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5
+echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6
+ CC="$CC $ac_cv_prog_cc_stdc" ;;
+esac
+
+# Some people use a C++ compiler to compile C. Since we use `exit',
+# in C++ we need to declare it. In case someone uses the same compiler
+# for both compiling C and C++ we need to have the C++ compiler decide
+# the declaration of exit, since it's the most demanding environment.
+cat >conftest.$ac_ext <<_ACEOF
+#ifndef __cplusplus
+ choke me
+#endif
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ for ac_declaration in \
+ '' \
+ 'extern "C" void std::exit (int) throw (); using std::exit;' \
+ 'extern "C" void std::exit (int); using std::exit;' \
+ 'extern "C" void exit (int) throw ();' \
+ 'extern "C" void exit (int);' \
+ 'void exit (int);'
+do
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_declaration
+#include <stdlib.h>
+int
+main ()
+{
+exit (42);
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ :
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+continue
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_declaration
+int
+main ()
+{
+exit (42);
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ break
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+done
+rm -f conftest*
+if test -n "$ac_declaration"; then
+ echo '#ifdef __cplusplus' >>confdefs.h
+ echo $ac_declaration >>confdefs.h
+ echo '#endif' >>confdefs.h
+fi
+
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ranlib; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_RANLIB+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$RANLIB"; then
+ ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+fi
+fi
+RANLIB=$ac_cv_prog_RANLIB
+if test -n "$RANLIB"; then
+ echo "$as_me:$LINENO: result: $RANLIB" >&5
+echo "${ECHO_T}$RANLIB" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_RANLIB"; then
+ ac_ct_RANLIB=$RANLIB
+ # Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if test -n "$ac_ct_RANLIB"; then
+ ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_RANLIB="ranlib"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+ test -z "$ac_cv_prog_ac_ct_RANLIB" && ac_cv_prog_ac_ct_RANLIB=":"
+fi
+fi
+ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB
+if test -n "$ac_ct_RANLIB"; then
+ echo "$as_me:$LINENO: result: $ac_ct_RANLIB" >&5
+echo "${ECHO_T}$ac_ct_RANLIB" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+ RANLIB=$ac_ct_RANLIB
+else
+ RANLIB="$ac_cv_prog_RANLIB"
+fi
+
+
+# Extract the first word of "ar", so it can be a program name with args.
+set dummy ar; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_path_AR+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ case $AR in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_AR="$AR" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_path_AR="$as_dir/$ac_word$ac_exec_ext"
+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+done
+
+ test -z "$ac_cv_path_AR" && ac_cv_path_AR="no"
+ ;;
+esac
+fi
+AR=$ac_cv_path_AR
+
+if test -n "$AR"; then
+ echo "$as_me:$LINENO: result: $AR" >&5
+echo "${ECHO_T}$AR" >&6
+else
+ echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+if [ $AR = "no" ] ; then
+ { { echo "$as_me:$LINENO: error: \"Could not find ar - needed to create a library\"" >&5
+echo "$as_me: error: \"Could not find ar - needed to create a library\"" >&2;}
+ { (exit 1); exit 1; }; };
+fi
+
+
+TARGETS="libresample.a tests/testresample"
+
+
+echo "$as_me:$LINENO: checking for sf_open in -lsndfile" >&5
+echo $ECHO_N "checking for sf_open in -lsndfile... $ECHO_C" >&6
+if test "${ac_cv_lib_sndfile_sf_open+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lsndfile $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char sf_open ();
+int
+main ()
+{
+sf_open ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_lib_sndfile_sf_open=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_sndfile_sf_open=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_sndfile_sf_open" >&5
+echo "${ECHO_T}$ac_cv_lib_sndfile_sf_open" >&6
+if test $ac_cv_lib_sndfile_sf_open = yes; then
+ have_libsndfile=yes
+else
+ have_libsndfile=no
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5
+echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+ CPP=
+fi
+if test -z "$CPP"; then
+ if test "${ac_cv_prog_CPP+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ # Double quotes because CPP needs to be expanded
+ for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
+ do
+ ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ # <limits.h> exists even on freestanding compilers.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+ Syntax error
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+ (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null; then
+ if test -s conftest.err; then
+ ac_cpp_err=$ac_c_preproc_warn_flag
+ ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+ else
+ ac_cpp_err=
+ fi
+else
+ ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+ :
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether non-existent headers
+ # can be detected and how.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <ac_nonexistent.h>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+ (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null; then
+ if test -s conftest.err; then
+ ac_cpp_err=$ac_c_preproc_warn_flag
+ ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+ else
+ ac_cpp_err=
+ fi
+else
+ ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+ # Broken: success on invalid input.
+continue
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+ break
+fi
+
+ done
+ ac_cv_prog_CPP=$CPP
+
+fi
+ CPP=$ac_cv_prog_CPP
+else
+ ac_cv_prog_CPP=$CPP
+fi
+echo "$as_me:$LINENO: result: $CPP" >&5
+echo "${ECHO_T}$CPP" >&6
+ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ # <limits.h> exists even on freestanding compilers.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+ Syntax error
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+ (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null; then
+ if test -s conftest.err; then
+ ac_cpp_err=$ac_c_preproc_warn_flag
+ ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+ else
+ ac_cpp_err=
+ fi
+else
+ ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+ :
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether non-existent headers
+ # can be detected and how.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <ac_nonexistent.h>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+ (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null; then
+ if test -s conftest.err; then
+ ac_cpp_err=$ac_c_preproc_warn_flag
+ ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+ else
+ ac_cpp_err=
+ fi
+else
+ ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+ # Broken: success on invalid input.
+continue
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+ :
+else
+ { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details." >&5
+echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+echo "$as_me:$LINENO: checking for egrep" >&5
+echo $ECHO_N "checking for egrep... $ECHO_C" >&6
+if test "${ac_cv_prog_egrep+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ if echo a | (grep -E '(a|b)') >/dev/null 2>&1
+ then ac_cv_prog_egrep='grep -E'
+ else ac_cv_prog_egrep='egrep'
+ fi
+fi
+echo "$as_me:$LINENO: result: $ac_cv_prog_egrep" >&5
+echo "${ECHO_T}$ac_cv_prog_egrep" >&6
+ EGREP=$ac_cv_prog_egrep
+
+
+echo "$as_me:$LINENO: checking for ANSI C header files" >&5
+echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6
+if test "${ac_cv_header_stdc+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_header_stdc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_header_stdc=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+ # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "memchr" >/dev/null 2>&1; then
+ :
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "free" >/dev/null 2>&1; then
+ :
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+ if test "$cross_compiling" = yes; then
+ :
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <ctype.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+ (('a' <= (c) && (c) <= 'i') \
+ || ('j' <= (c) && (c) <= 'r') \
+ || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+ int i;
+ for (i = 0; i < 256; i++)
+ if (XOR (islower (i), ISLOWER (i))
+ || toupper (i) != TOUPPER (i))
+ exit(2);
+ exit (0);
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ :
+else
+ echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_header_stdc=no
+fi
+rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+fi
+fi
+echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5
+echo "${ECHO_T}$ac_cv_header_stdc" >&6
+if test $ac_cv_header_stdc = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define STDC_HEADERS 1
+_ACEOF
+
+fi
+
+# On IRIX 5.3, sys/types and inttypes.h are conflicting.
+
+
+
+
+
+
+
+
+
+for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \
+ inttypes.h stdint.h unistd.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ eval "$as_ac_Header=yes"
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+eval "$as_ac_Header=no"
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+if test "${ac_cv_header_sndfile_h+set}" = set; then
+ echo "$as_me:$LINENO: checking for sndfile.h" >&5
+echo $ECHO_N "checking for sndfile.h... $ECHO_C" >&6
+if test "${ac_cv_header_sndfile_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: $ac_cv_header_sndfile_h" >&5
+echo "${ECHO_T}$ac_cv_header_sndfile_h" >&6
+else
+ # Is the header compilable?
+echo "$as_me:$LINENO: checking sndfile.h usability" >&5
+echo $ECHO_N "checking sndfile.h usability... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <sndfile.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking sndfile.h presence" >&5
+echo $ECHO_N "checking sndfile.h presence... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sndfile.h>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+ (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null; then
+ if test -s conftest.err; then
+ ac_cpp_err=$ac_c_preproc_warn_flag
+ ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+ else
+ ac_cpp_err=
+ fi
+else
+ ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: sndfile.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: sndfile.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sndfile.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: sndfile.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: sndfile.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: sndfile.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sndfile.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: sndfile.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sndfile.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: sndfile.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sndfile.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: sndfile.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sndfile.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: sndfile.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sndfile.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: sndfile.h: in the future, the compiler will take precedence" >&2;}
+ (
+ cat <<\_ASBOX
+## ------------------------------------------ ##
+## Report this to the AC_PACKAGE_NAME lists. ##
+## ------------------------------------------ ##
+_ASBOX
+ ) |
+ sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+echo "$as_me:$LINENO: checking for sndfile.h" >&5
+echo $ECHO_N "checking for sndfile.h... $ECHO_C" >&6
+if test "${ac_cv_header_sndfile_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_sndfile_h=$ac_header_preproc
+fi
+echo "$as_me:$LINENO: result: $ac_cv_header_sndfile_h" >&5
+echo "${ECHO_T}$ac_cv_header_sndfile_h" >&6
+
+fi
+if test $ac_cv_header_sndfile_h = yes; then
+ :
+else
+ have_libsndfile=no
+fi
+
+
+
+if [ $have_libsndfile = "yes" ] ; then
+ TARGETS="$TARGETS tests/resample-sndfile"
+fi
+
+echo "$as_me:$LINENO: checking for src_simple in -lsamplerate" >&5
+echo $ECHO_N "checking for src_simple in -lsamplerate... $ECHO_C" >&6
+if test "${ac_cv_lib_samplerate_src_simple+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lsamplerate $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any gcc2 internal prototype to avoid an error. */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char src_simple ();
+int
+main ()
+{
+src_simple ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+ (eval $ac_link) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest$ac_exeext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_cv_lib_samplerate_src_simple=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_samplerate_src_simple=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_samplerate_src_simple" >&5
+echo "${ECHO_T}$ac_cv_lib_samplerate_src_simple" >&6
+if test $ac_cv_lib_samplerate_src_simple = yes; then
+ have_libsamplerate=yes
+else
+ have_libsamplerate=no
+fi
+
+if test "${ac_cv_header_samplerate_h+set}" = set; then
+ echo "$as_me:$LINENO: checking for samplerate.h" >&5
+echo $ECHO_N "checking for samplerate.h... $ECHO_C" >&6
+if test "${ac_cv_header_samplerate_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: $ac_cv_header_samplerate_h" >&5
+echo "${ECHO_T}$ac_cv_header_samplerate_h" >&6
+else
+ # Is the header compilable?
+echo "$as_me:$LINENO: checking samplerate.h usability" >&5
+echo $ECHO_N "checking samplerate.h usability... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <samplerate.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking samplerate.h presence" >&5
+echo $ECHO_N "checking samplerate.h presence... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <samplerate.h>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+ (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null; then
+ if test -s conftest.err; then
+ ac_cpp_err=$ac_c_preproc_warn_flag
+ ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+ else
+ ac_cpp_err=
+ fi
+else
+ ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: samplerate.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: samplerate.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: samplerate.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: samplerate.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: samplerate.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: samplerate.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: samplerate.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: samplerate.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: samplerate.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: samplerate.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: samplerate.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: samplerate.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: samplerate.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: samplerate.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: samplerate.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: samplerate.h: in the future, the compiler will take precedence" >&2;}
+ (
+ cat <<\_ASBOX
+## ------------------------------------------ ##
+## Report this to the AC_PACKAGE_NAME lists. ##
+## ------------------------------------------ ##
+_ASBOX
+ ) |
+ sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+echo "$as_me:$LINENO: checking for samplerate.h" >&5
+echo $ECHO_N "checking for samplerate.h... $ECHO_C" >&6
+if test "${ac_cv_header_samplerate_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_samplerate_h=$ac_header_preproc
+fi
+echo "$as_me:$LINENO: result: $ac_cv_header_samplerate_h" >&5
+echo "${ECHO_T}$ac_cv_header_samplerate_h" >&6
+
+fi
+if test $ac_cv_header_samplerate_h = yes; then
+ :
+else
+ have_libsamplerate=no
+fi
+
+
+
+if [ $have_libsamplerate = "yes" ] ; then
+ TARGETS="$TARGETS tests/compareresample"
+fi
+
+
+for ac_header in inttypes.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+ echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+else
+ # Is the header compilable?
+echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <$ac_header>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+ (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null; then
+ if test -s conftest.err; then
+ ac_cpp_err=$ac_c_preproc_warn_flag
+ ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+ else
+ ac_cpp_err=
+ fi
+else
+ ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+ (
+ cat <<\_ASBOX
+## ------------------------------------------ ##
+## Report this to the AC_PACKAGE_NAME lists. ##
+## ------------------------------------------ ##
+_ASBOX
+ ) |
+ sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ eval "$as_ac_Header=\$ac_header_preproc"
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+ cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+ ac_config_headers="$ac_config_headers src/config.h:src/configtemplate.h"
+
+ ac_config_files="$ac_config_files Makefile"
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems. If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, don't put newlines in cache variables' values.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+{
+ (set) 2>&1 |
+ case `(ac_space=' '; set | grep ac_space) 2>&1` in
+ *ac_space=\ *)
+ # `set' does not quote correctly, so add quotes (double-quote
+ # substitution turns \\\\ into \\, and sed turns \\ into \).
+ sed -n \
+ "s/'/'\\\\''/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+ ;;
+ *)
+ # `set' quotes correctly as required by POSIX, so do not add quotes.
+ sed -n \
+ "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p"
+ ;;
+ esac;
+} |
+ sed '
+ t clear
+ : clear
+ s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+ t end
+ /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+ : end' >>confcache
+if diff $cache_file confcache >/dev/null 2>&1; then :; else
+ if test -w $cache_file; then
+ test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file"
+ cat confcache >$cache_file
+ else
+ echo "not updating unwritable cache $cache_file"
+ fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# VPATH may cause trouble with some makes, so we remove $(srcdir),
+# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+ ac_vpsub='/^[ ]*VPATH[ ]*=/{
+s/:*\$(srcdir):*/:/;
+s/:*\${srcdir}:*/:/;
+s/:*@srcdir@:*/:/;
+s/^\([^=]*=[ ]*\):*/\1/;
+s/:*$//;
+s/^[^=]*=[ ]*$//;
+}'
+fi
+
+DEFS=-DHAVE_CONFIG_H
+
+ac_libobjs=
+ac_ltlibobjs=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+ # 1. Remove the extension, and $U if already installed.
+ ac_i=`echo "$ac_i" |
+ sed 's/\$U\././;s/\.o$//;s/\.obj$//'`
+ # 2. Add them.
+ ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext"
+ ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+
+: ${CONFIG_STATUS=./config.status}
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5
+echo "$as_me: creating $CONFIG_STATUS" >&6;}
+cat >$CONFIG_STATUS <<_ACEOF
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+SHELL=\${CONFIG_SHELL-$SHELL}
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+## --------------------- ##
+## M4sh Initialization. ##
+## --------------------- ##
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
+ set -o posix
+fi
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+ as_unset=unset
+else
+ as_unset=false
+fi
+
+
+# Work around bugs in pre-3.0 UWIN ksh.
+$as_unset ENV MAIL MAILPATH
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+for as_var in \
+ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+ LC_TELEPHONE LC_TIME
+do
+ if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+ eval $as_var=C; export $as_var
+ else
+ $as_unset $as_var
+ fi
+done
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)$' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; }
+ /^X\/\(\/\/\)$/{ s//\1/; q; }
+ /^X\/\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+
+
+# PATH needs CR, and LINENO needs CR and PATH.
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ echo "#! /bin/sh" >conf$$.sh
+ echo "exit 0" >>conf$$.sh
+ chmod +x conf$$.sh
+ if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+ PATH_SEPARATOR=';'
+ else
+ PATH_SEPARATOR=:
+ fi
+ rm -f conf$$.sh
+fi
+
+
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x$as_lineno_3" = "x$as_lineno_2" || {
+ # Find who we are. Look in the path if we contain no path at all
+ # relative or not.
+ case $0 in
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+
+ ;;
+ esac
+ # We did not find ourselves, most probably we were run as `sh COMMAND'
+ # in which case we are not to be found in the path.
+ if test "x$as_myself" = x; then
+ as_myself=$0
+ fi
+ if test ! -f "$as_myself"; then
+ { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5
+echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+ case $CONFIG_SHELL in
+ '')
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for as_base in sh bash ksh sh5; do
+ case $as_dir in
+ /*)
+ if ("$as_dir/$as_base" -c '
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then
+ $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; }
+ $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; }
+ CONFIG_SHELL=$as_dir/$as_base
+ export CONFIG_SHELL
+ exec "$CONFIG_SHELL" "$0" ${1+"$@"}
+ fi;;
+ esac
+ done
+done
+;;
+ esac
+
+ # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+ # uniformly replaced by the line number. The first 'sed' inserts a
+ # line-number line before each line; the second 'sed' does the real
+ # work. The second script uses 'N' to pair each line-number line
+ # with the numbered line, and appends trailing '-' during
+ # substitution so that $LINENO is not a special case at line end.
+ # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+ # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-)
+ sed '=' <$as_myself |
+ sed '
+ N
+ s,$,-,
+ : loop
+ s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3,
+ t loop
+ s,-$,,
+ s,^['$as_cr_digits']*\n,,
+ ' >$as_me.lineno &&
+ chmod +x $as_me.lineno ||
+ { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5
+echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;}
+ { (exit 1); exit 1; }; }
+
+ # Don't try to exec as it changes $[0], causing all sort of problems
+ # (the dirname of $[0] is not the place where we might find the
+ # original and so on. Autoconf is especially sensible to this).
+ . ./$as_me.lineno
+ # Exit status is that of the last command.
+ exit
+}
+
+
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+ *c*,-n*) ECHO_N= ECHO_C='
+' ECHO_T=' ' ;;
+ *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;;
+ *) ECHO_N= ECHO_C='\c' ECHO_T= ;;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+ # We could just check for DJGPP; but this test a) works b) is more generic
+ # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04).
+ if test -f conf$$.exe; then
+ # Don't use ln at all; we don't have any links
+ as_ln_s='cp -p'
+ else
+ as_ln_s='ln -s'
+ fi
+elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+else
+ as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.file
+
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p=:
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+as_executable_p="test -f"
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.
+as_nl='
+'
+IFS=" $as_nl"
+
+# CDPATH.
+$as_unset CDPATH
+
+exec 6>&1
+
+# Open the log real soon, to keep \$[0] and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling. Logging --version etc. is OK.
+exec 5>>config.log
+{
+ echo
+ sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+} >&5
+cat >&5 <<_CSEOF
+
+This file was extended by $as_me, which was
+generated by GNU Autoconf 2.59. Invocation command line was
+
+ CONFIG_FILES = $CONFIG_FILES
+ CONFIG_HEADERS = $CONFIG_HEADERS
+ CONFIG_LINKS = $CONFIG_LINKS
+ CONFIG_COMMANDS = $CONFIG_COMMANDS
+ $ $0 $@
+
+_CSEOF
+echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5
+echo >&5
+_ACEOF
+
+# Files that config.status was made for.
+if test -n "$ac_config_files"; then
+ echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_headers"; then
+ echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_links"; then
+ echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_commands"; then
+ echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+ac_cs_usage="\
+\`$as_me' instantiates files from templates according to the
+current configuration.
+
+Usage: $0 [OPTIONS] [FILE]...
+
+ -h, --help print this help, then exit
+ -V, --version print version number, then exit
+ -q, --quiet do not print progress messages
+ -d, --debug don't remove temporary files
+ --recheck update $as_me by reconfiguring in the same conditions
+ --file=FILE[:TEMPLATE]
+ instantiate the configuration file FILE
+ --header=FILE[:TEMPLATE]
+ instantiate the configuration header FILE
+
+Configuration files:
+$config_files
+
+Configuration headers:
+$config_headers
+
+Report bugs to <bug-autoconf@gnu.org>."
+_ACEOF
+
+cat >>$CONFIG_STATUS <<_ACEOF
+ac_cs_version="\\
+config.status
+configured by $0, generated by GNU Autoconf 2.59,
+ with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\"
+
+Copyright (C) 2003 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+srcdir=$srcdir
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+# If no file are specified by the user, then we need to provide default
+# value. By we need to know if files were specified by the user.
+ac_need_defaults=:
+while test $# != 0
+do
+ case $1 in
+ --*=*)
+ ac_option=`expr "x$1" : 'x\([^=]*\)='`
+ ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'`
+ ac_shift=:
+ ;;
+ -*)
+ ac_option=$1
+ ac_optarg=$2
+ ac_shift=shift
+ ;;
+ *) # This is not an option, so the user has probably given explicit
+ # arguments.
+ ac_option=$1
+ ac_need_defaults=false;;
+ esac
+
+ case $ac_option in
+ # Handling of the options.
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+ -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+ ac_cs_recheck=: ;;
+ --version | --vers* | -V )
+ echo "$ac_cs_version"; exit 0 ;;
+ --he | --h)
+ # Conflict between --help and --header
+ { { echo "$as_me:$LINENO: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&5
+echo "$as_me: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&2;}
+ { (exit 1); exit 1; }; };;
+ --help | --hel | -h )
+ echo "$ac_cs_usage"; exit 0 ;;
+ --debug | --d* | -d )
+ debug=: ;;
+ --file | --fil | --fi | --f )
+ $ac_shift
+ CONFIG_FILES="$CONFIG_FILES $ac_optarg"
+ ac_need_defaults=false;;
+ --header | --heade | --head | --hea )
+ $ac_shift
+ CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg"
+ ac_need_defaults=false;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil | --si | --s)
+ ac_cs_silent=: ;;
+
+ # This is an error.
+ -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&5
+echo "$as_me: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&2;}
+ { (exit 1); exit 1; }; } ;;
+
+ *) ac_config_targets="$ac_config_targets $1" ;;
+
+ esac
+ shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+ exec 6>/dev/null
+ ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+if \$ac_cs_recheck; then
+ echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6
+ exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+fi
+
+_ACEOF
+
+
+
+
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+for ac_config_target in $ac_config_targets
+do
+ case "$ac_config_target" in
+ # Handling of arguments.
+ "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+ "src/config.h" ) CONFIG_HEADERS="$CONFIG_HEADERS src/config.h:src/configtemplate.h" ;;
+ *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5
+echo "$as_me: error: invalid argument: $ac_config_target" >&2;}
+ { (exit 1); exit 1; }; };;
+ esac
+done
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used. Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+ test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+ test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
+fi
+
+# Have a temporary directory for convenience. Make it in the build tree
+# simply because there is no reason to put it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Create a temporary directory, and hook for its removal unless debugging.
+$debug ||
+{
+ trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0
+ trap '{ (exit 1); exit 1; }' 1 2 13 15
+}
+
+# Create a (secure) tmp directory for tmp files.
+
+{
+ tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` &&
+ test -n "$tmp" && test -d "$tmp"
+} ||
+{
+ tmp=./confstat$$-$RANDOM
+ (umask 077 && mkdir $tmp)
+} ||
+{
+ echo "$me: cannot create a temporary directory in ." >&2
+ { (exit 1); exit 1; }
+}
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<_ACEOF
+
+#
+# CONFIG_FILES section.
+#
+
+# No need to generate the scripts if there are no CONFIG_FILES.
+# This happens for instance when ./config.status config.h
+if test -n "\$CONFIG_FILES"; then
+ # Protect against being on the right side of a sed subst in config.status.
+ sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g;
+ s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF
+s,@SHELL@,$SHELL,;t t
+s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t
+s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t
+s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t
+s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t
+s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t
+s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t
+s,@exec_prefix@,$exec_prefix,;t t
+s,@prefix@,$prefix,;t t
+s,@program_transform_name@,$program_transform_name,;t t
+s,@bindir@,$bindir,;t t
+s,@sbindir@,$sbindir,;t t
+s,@libexecdir@,$libexecdir,;t t
+s,@datadir@,$datadir,;t t
+s,@sysconfdir@,$sysconfdir,;t t
+s,@sharedstatedir@,$sharedstatedir,;t t
+s,@localstatedir@,$localstatedir,;t t
+s,@libdir@,$libdir,;t t
+s,@includedir@,$includedir,;t t
+s,@oldincludedir@,$oldincludedir,;t t
+s,@infodir@,$infodir,;t t
+s,@mandir@,$mandir,;t t
+s,@build_alias@,$build_alias,;t t
+s,@host_alias@,$host_alias,;t t
+s,@target_alias@,$target_alias,;t t
+s,@DEFS@,$DEFS,;t t
+s,@ECHO_C@,$ECHO_C,;t t
+s,@ECHO_N@,$ECHO_N,;t t
+s,@ECHO_T@,$ECHO_T,;t t
+s,@LIBS@,$LIBS,;t t
+s,@CC@,$CC,;t t
+s,@CFLAGS@,$CFLAGS,;t t
+s,@LDFLAGS@,$LDFLAGS,;t t
+s,@CPPFLAGS@,$CPPFLAGS,;t t
+s,@ac_ct_CC@,$ac_ct_CC,;t t
+s,@EXEEXT@,$EXEEXT,;t t
+s,@OBJEXT@,$OBJEXT,;t t
+s,@RANLIB@,$RANLIB,;t t
+s,@ac_ct_RANLIB@,$ac_ct_RANLIB,;t t
+s,@AR@,$AR,;t t
+s,@TARGETS@,$TARGETS,;t t
+s,@CPP@,$CPP,;t t
+s,@EGREP@,$EGREP,;t t
+s,@LIBOBJS@,$LIBOBJS,;t t
+s,@LTLIBOBJS@,$LTLIBOBJS,;t t
+CEOF
+
+_ACEOF
+
+ cat >>$CONFIG_STATUS <<\_ACEOF
+ # Split the substitutions into bite-sized pieces for seds with
+ # small command number limits, like on Digital OSF/1 and HP-UX.
+ ac_max_sed_lines=48
+ ac_sed_frag=1 # Number of current file.
+ ac_beg=1 # First line for current file.
+ ac_end=$ac_max_sed_lines # Line after last line for current file.
+ ac_more_lines=:
+ ac_sed_cmds=
+ while $ac_more_lines; do
+ if test $ac_beg -gt 1; then
+ sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
+ else
+ sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
+ fi
+ if test ! -s $tmp/subs.frag; then
+ ac_more_lines=false
+ else
+ # The purpose of the label and of the branching condition is to
+ # speed up the sed processing (if there are no `@' at all, there
+ # is no need to browse any of the substitutions).
+ # These are the two extra sed commands mentioned above.
+ (echo ':t
+ /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed
+ if test -z "$ac_sed_cmds"; then
+ ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed"
+ else
+ ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed"
+ fi
+ ac_sed_frag=`expr $ac_sed_frag + 1`
+ ac_beg=$ac_end
+ ac_end=`expr $ac_end + $ac_max_sed_lines`
+ fi
+ done
+ if test -z "$ac_sed_cmds"; then
+ ac_sed_cmds=cat
+ fi
+fi # test -n "$CONFIG_FILES"
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue
+ # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+ case $ac_file in
+ - | *:- | *:-:* ) # input from stdin
+ cat >$tmp/stdin
+ ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+ ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+ *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+ ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+ * ) ac_file_in=$ac_file.in ;;
+ esac
+
+ # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories.
+ ac_dir=`(dirname "$ac_file") 2>/dev/null ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$ac_file" : 'X\(//\)[^/]' \| \
+ X"$ac_file" : 'X\(//\)$' \| \
+ X"$ac_file" : 'X\(/\)' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X"$ac_file" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+ /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+ /^X\(\/\/\)$/{ s//\1/; q; }
+ /^X\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+ { if $as_mkdir_p; then
+ mkdir -p "$ac_dir"
+ else
+ as_dir="$ac_dir"
+ as_dirs=
+ while test ! -d "$as_dir"; do
+ as_dirs="$as_dir $as_dirs"
+ as_dir=`(dirname "$as_dir") 2>/dev/null ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_dir" : 'X\(//\)[^/]' \| \
+ X"$as_dir" : 'X\(//\)$' \| \
+ X"$as_dir" : 'X\(/\)' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X"$as_dir" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+ /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+ /^X\(\/\/\)$/{ s//\1/; q; }
+ /^X\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+ done
+ test ! -n "$as_dirs" || mkdir $as_dirs
+ fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5
+echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;}
+ { (exit 1); exit 1; }; }; }
+
+ ac_builddir=.
+
+if test "$ac_dir" != .; then
+ ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+ # A "../" for each directory in $ac_dir_suffix.
+ ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
+else
+ ac_dir_suffix= ac_top_builddir=
+fi
+
+case $srcdir in
+ .) # No --srcdir option. We are building in place.
+ ac_srcdir=.
+ if test -z "$ac_top_builddir"; then
+ ac_top_srcdir=.
+ else
+ ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
+ fi ;;
+ [\\/]* | ?:[\\/]* ) # Absolute path.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir ;;
+ *) # Relative path.
+ ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_builddir$srcdir ;;
+esac
+
+# Do not use `cd foo && pwd` to compute absolute paths, because
+# the directories may not exist.
+case `pwd` in
+.) ac_abs_builddir="$ac_dir";;
+*)
+ case "$ac_dir" in
+ .) ac_abs_builddir=`pwd`;;
+ [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
+ *) ac_abs_builddir=`pwd`/"$ac_dir";;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_builddir=${ac_top_builddir}.;;
+*)
+ case ${ac_top_builddir}. in
+ .) ac_abs_top_builddir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
+ *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_srcdir=$ac_srcdir;;
+*)
+ case $ac_srcdir in
+ .) ac_abs_srcdir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
+ *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_srcdir=$ac_top_srcdir;;
+*)
+ case $ac_top_srcdir in
+ .) ac_abs_top_srcdir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
+ *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
+ esac;;
+esac
+
+
+
+ # Let's still pretend it is `configure' which instantiates (i.e., don't
+ # use $as_me), people would be surprised to read:
+ # /* config.h. Generated by config.status. */
+ if test x"$ac_file" = x-; then
+ configure_input=
+ else
+ configure_input="$ac_file. "
+ fi
+ configure_input=$configure_input"Generated from `echo $ac_file_in |
+ sed 's,.*/,,'` by configure."
+
+ # First look for the input files in the build tree, otherwise in the
+ # src tree.
+ ac_file_inputs=`IFS=:
+ for f in $ac_file_in; do
+ case $f in
+ -) echo $tmp/stdin ;;
+ [\\/$]*)
+ # Absolute (can't be DOS-style, as IFS=:)
+ test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+ { (exit 1); exit 1; }; }
+ echo "$f";;
+ *) # Relative
+ if test -f "$f"; then
+ # Build tree
+ echo "$f"
+ elif test -f "$srcdir/$f"; then
+ # Source tree
+ echo "$srcdir/$f"
+ else
+ # /dev/null tree
+ { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+ { (exit 1); exit 1; }; }
+ fi;;
+ esac
+ done` || { (exit 1); exit 1; }
+
+ if test x"$ac_file" != x-; then
+ { echo "$as_me:$LINENO: creating $ac_file" >&5
+echo "$as_me: creating $ac_file" >&6;}
+ rm -f "$ac_file"
+ fi
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+ sed "$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s,@configure_input@,$configure_input,;t t
+s,@srcdir@,$ac_srcdir,;t t
+s,@abs_srcdir@,$ac_abs_srcdir,;t t
+s,@top_srcdir@,$ac_top_srcdir,;t t
+s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t
+s,@builddir@,$ac_builddir,;t t
+s,@abs_builddir@,$ac_abs_builddir,;t t
+s,@top_builddir@,$ac_top_builddir,;t t
+s,@abs_top_builddir@,$ac_abs_top_builddir,;t t
+" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out
+ rm -f $tmp/stdin
+ if test x"$ac_file" != x-; then
+ mv $tmp/out $ac_file
+ else
+ cat $tmp/out
+ rm -f $tmp/out
+ fi
+
+done
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+#
+# CONFIG_HEADER section.
+#
+
+# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where
+# NAME is the cpp macro being defined and VALUE is the value it is being given.
+#
+# ac_d sets the value in "#define NAME VALUE" lines.
+ac_dA='s,^\([ ]*\)#\([ ]*define[ ][ ]*\)'
+ac_dB='[ ].*$,\1#\2'
+ac_dC=' '
+ac_dD=',;t'
+# ac_u turns "#undef NAME" without trailing blanks into "#define NAME VALUE".
+ac_uA='s,^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)'
+ac_uB='$,\1#\2define\3'
+ac_uC=' '
+ac_uD=',;t'
+
+for ac_file in : $CONFIG_HEADERS; do test "x$ac_file" = x: && continue
+ # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+ case $ac_file in
+ - | *:- | *:-:* ) # input from stdin
+ cat >$tmp/stdin
+ ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+ ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+ *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+ ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+ * ) ac_file_in=$ac_file.in ;;
+ esac
+
+ test x"$ac_file" != x- && { echo "$as_me:$LINENO: creating $ac_file" >&5
+echo "$as_me: creating $ac_file" >&6;}
+
+ # First look for the input files in the build tree, otherwise in the
+ # src tree.
+ ac_file_inputs=`IFS=:
+ for f in $ac_file_in; do
+ case $f in
+ -) echo $tmp/stdin ;;
+ [\\/$]*)
+ # Absolute (can't be DOS-style, as IFS=:)
+ test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+ { (exit 1); exit 1; }; }
+ # Do quote $f, to prevent DOS paths from being IFS'd.
+ echo "$f";;
+ *) # Relative
+ if test -f "$f"; then
+ # Build tree
+ echo "$f"
+ elif test -f "$srcdir/$f"; then
+ # Source tree
+ echo "$srcdir/$f"
+ else
+ # /dev/null tree
+ { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+ { (exit 1); exit 1; }; }
+ fi;;
+ esac
+ done` || { (exit 1); exit 1; }
+ # Remove the trailing spaces.
+ sed 's/[ ]*$//' $ac_file_inputs >$tmp/in
+
+_ACEOF
+
+# Transform confdefs.h into two sed scripts, `conftest.defines' and
+# `conftest.undefs', that substitutes the proper values into
+# config.h.in to produce config.h. The first handles `#define'
+# templates, and the second `#undef' templates.
+# And first: Protect against being on the right side of a sed subst in
+# config.status. Protect against being in an unquoted here document
+# in config.status.
+rm -f conftest.defines conftest.undefs
+# Using a here document instead of a string reduces the quoting nightmare.
+# Putting comments in sed scripts is not portable.
+#
+# `end' is used to avoid that the second main sed command (meant for
+# 0-ary CPP macros) applies to n-ary macro definitions.
+# See the Autoconf documentation for `clear'.
+cat >confdef2sed.sed <<\_ACEOF
+s/[\\&,]/\\&/g
+s,[\\$`],\\&,g
+t clear
+: clear
+s,^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*\)\(([^)]*)\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1\2${ac_dC}\3${ac_dD},gp
+t end
+s,^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD},gp
+: end
+_ACEOF
+# If some macros were called several times there might be several times
+# the same #defines, which is useless. Nevertheless, we may not want to
+# sort them, since we want the *last* AC-DEFINE to be honored.
+uniq confdefs.h | sed -n -f confdef2sed.sed >conftest.defines
+sed 's/ac_d/ac_u/g' conftest.defines >conftest.undefs
+rm -f confdef2sed.sed
+
+# This sed command replaces #undef with comments. This is necessary, for
+# example, in the case of _POSIX_SOURCE, which is predefined and required
+# on some systems where configure will not decide to define it.
+cat >>conftest.undefs <<\_ACEOF
+s,^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*,/* & */,
+_ACEOF
+
+# Break up conftest.defines because some shells have a limit on the size
+# of here documents, and old seds have small limits too (100 cmds).
+echo ' # Handle all the #define templates only if necessary.' >>$CONFIG_STATUS
+echo ' if grep "^[ ]*#[ ]*define" $tmp/in >/dev/null; then' >>$CONFIG_STATUS
+echo ' # If there are no defines, we may have an empty if/fi' >>$CONFIG_STATUS
+echo ' :' >>$CONFIG_STATUS
+rm -f conftest.tail
+while grep . conftest.defines >/dev/null
+do
+ # Write a limited-size here document to $tmp/defines.sed.
+ echo ' cat >$tmp/defines.sed <<CEOF' >>$CONFIG_STATUS
+ # Speed up: don't consider the non `#define' lines.
+ echo '/^[ ]*#[ ]*define/!b' >>$CONFIG_STATUS
+ # Work around the forget-to-reset-the-flag bug.
+ echo 't clr' >>$CONFIG_STATUS
+ echo ': clr' >>$CONFIG_STATUS
+ sed ${ac_max_here_lines}q conftest.defines >>$CONFIG_STATUS
+ echo 'CEOF
+ sed -f $tmp/defines.sed $tmp/in >$tmp/out
+ rm -f $tmp/in
+ mv $tmp/out $tmp/in
+' >>$CONFIG_STATUS
+ sed 1,${ac_max_here_lines}d conftest.defines >conftest.tail
+ rm -f conftest.defines
+ mv conftest.tail conftest.defines
+done
+rm -f conftest.defines
+echo ' fi # grep' >>$CONFIG_STATUS
+echo >>$CONFIG_STATUS
+
+# Break up conftest.undefs because some shells have a limit on the size
+# of here documents, and old seds have small limits too (100 cmds).
+echo ' # Handle all the #undef templates' >>$CONFIG_STATUS
+rm -f conftest.tail
+while grep . conftest.undefs >/dev/null
+do
+ # Write a limited-size here document to $tmp/undefs.sed.
+ echo ' cat >$tmp/undefs.sed <<CEOF' >>$CONFIG_STATUS
+ # Speed up: don't consider the non `#undef'
+ echo '/^[ ]*#[ ]*undef/!b' >>$CONFIG_STATUS
+ # Work around the forget-to-reset-the-flag bug.
+ echo 't clr' >>$CONFIG_STATUS
+ echo ': clr' >>$CONFIG_STATUS
+ sed ${ac_max_here_lines}q conftest.undefs >>$CONFIG_STATUS
+ echo 'CEOF
+ sed -f $tmp/undefs.sed $tmp/in >$tmp/out
+ rm -f $tmp/in
+ mv $tmp/out $tmp/in
+' >>$CONFIG_STATUS
+ sed 1,${ac_max_here_lines}d conftest.undefs >conftest.tail
+ rm -f conftest.undefs
+ mv conftest.tail conftest.undefs
+done
+rm -f conftest.undefs
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+ # Let's still pretend it is `configure' which instantiates (i.e., don't
+ # use $as_me), people would be surprised to read:
+ # /* config.h. Generated by config.status. */
+ if test x"$ac_file" = x-; then
+ echo "/* Generated by configure. */" >$tmp/config.h
+ else
+ echo "/* $ac_file. Generated by configure. */" >$tmp/config.h
+ fi
+ cat $tmp/in >>$tmp/config.h
+ rm -f $tmp/in
+ if test x"$ac_file" != x-; then
+ if diff $ac_file $tmp/config.h >/dev/null 2>&1; then
+ { echo "$as_me:$LINENO: $ac_file is unchanged" >&5
+echo "$as_me: $ac_file is unchanged" >&6;}
+ else
+ ac_dir=`(dirname "$ac_file") 2>/dev/null ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$ac_file" : 'X\(//\)[^/]' \| \
+ X"$ac_file" : 'X\(//\)$' \| \
+ X"$ac_file" : 'X\(/\)' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X"$ac_file" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+ /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+ /^X\(\/\/\)$/{ s//\1/; q; }
+ /^X\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+ { if $as_mkdir_p; then
+ mkdir -p "$ac_dir"
+ else
+ as_dir="$ac_dir"
+ as_dirs=
+ while test ! -d "$as_dir"; do
+ as_dirs="$as_dir $as_dirs"
+ as_dir=`(dirname "$as_dir") 2>/dev/null ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_dir" : 'X\(//\)[^/]' \| \
+ X"$as_dir" : 'X\(//\)$' \| \
+ X"$as_dir" : 'X\(/\)' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X"$as_dir" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+ /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+ /^X\(\/\/\)$/{ s//\1/; q; }
+ /^X\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+ done
+ test ! -n "$as_dirs" || mkdir $as_dirs
+ fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5
+echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;}
+ { (exit 1); exit 1; }; }; }
+
+ rm -f $ac_file
+ mv $tmp/config.h $ac_file
+ fi
+ else
+ cat $tmp/config.h
+ rm -f $tmp/config.h
+ fi
+done
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+{ (exit 0); exit 0; }
+_ACEOF
+chmod +x $CONFIG_STATUS
+ac_clean_files=$ac_clean_files_save
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded. So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status. When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+ ac_cs_success=:
+ ac_config_status_args=
+ test "$silent" = yes &&
+ ac_config_status_args="$ac_config_status_args --quiet"
+ exec 5>/dev/null
+ $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+ exec 5>>config.log
+ # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+ # would make configure fail if this is the last instruction.
+ $ac_cs_success || { (exit 1); exit 1; }
+fi
+
+
+echo ""
+
+if [ $have_libsamplerate = "yes" ] ; then
+ echo "Configured to build tests/resample-sndfile using libsndfile"
+ echo ""
+else
+ echo "Could not find libsndfile - needed if you want to"
+ echo "compile tests/resample-sndfile"
+ echo ""
+fi
+
+if [ $have_libsamplerate = "yes" ] ; then
+ echo "Configured to build tests/compareresample to compare against"
+ echo "Erik de Castro Lopo's libsamplerate library."
+ echo ""
+else
+ echo "Could not find libsamplerate - only needed if you want to"
+ echo "compile tests/compareresample to compare their performance."
+ echo ""
+fi
+
+echo "Type 'configure --help' to see options."
+echo ""
+echo "Type 'make' to build libresample and tests."
diff --git a/trunk/main/libresample/configure.in b/trunk/main/libresample/configure.in
new file mode 100644
index 000000000..e676b69f2
--- /dev/null
+++ b/trunk/main/libresample/configure.in
@@ -0,0 +1,68 @@
+dnl
+dnl libresample configure.in script
+dnl
+dnl Dominic Mazzoni
+dnl
+
+dnl Require autoconf >= 2.13
+AC_PREREQ(2.13)
+
+dnl Init autoconf and make sure configure is being called
+dnl from the right directory
+AC_INIT([src/resample.c])
+
+dnl Checks for programs.
+AC_PROG_CC
+AC_PROG_RANLIB
+
+AC_PATH_PROG(AR, ar, no)
+if [[ $AR = "no" ]] ; then
+ AC_MSG_ERROR("Could not find ar - needed to create a library");
+fi
+
+AC_SUBST(TARGETS)
+TARGETS="libresample.a tests/testresample"
+
+AC_CHECK_LIB(sndfile, sf_open, have_libsndfile=yes, have_libsndfile=no)
+AC_CHECK_HEADER(sndfile.h, , have_libsndfile=no)
+
+if [[ $have_libsndfile = "yes" ]] ; then
+ TARGETS="$TARGETS tests/resample-sndfile"
+fi
+
+AC_CHECK_LIB(samplerate, src_simple, have_libsamplerate=yes, have_libsamplerate=no)
+AC_CHECK_HEADER(samplerate.h, , have_libsamplerate=no)
+
+if [[ $have_libsamplerate = "yes" ]] ; then
+ TARGETS="$TARGETS tests/compareresample"
+fi
+
+AC_CHECK_HEADERS(inttypes.h)
+
+AC_CONFIG_HEADER(src/config.h:src/configtemplate.h)
+AC_OUTPUT([Makefile])
+
+echo ""
+
+if [[ $have_libsamplerate = "yes" ]] ; then
+ echo "Configured to build tests/resample-sndfile using libsndfile"
+ echo ""
+else
+ echo "Could not find libsndfile - needed if you want to"
+ echo "compile tests/resample-sndfile"
+ echo ""
+fi
+
+if [[ $have_libsamplerate = "yes" ]] ; then
+ echo "Configured to build tests/compareresample to compare against"
+ echo "Erik de Castro Lopo's libsamplerate library."
+ echo ""
+else
+ echo "Could not find libsamplerate - only needed if you want to"
+ echo "compile tests/compareresample to compare their performance."
+ echo ""
+fi
+
+echo "Type 'configure --help' to see options."
+echo ""
+echo "Type 'make' to build libresample and tests."
diff --git a/trunk/main/libresample/include/libresample.h b/trunk/main/libresample/include/libresample.h
new file mode 100644
index 000000000..e5ce4851a
--- /dev/null
+++ b/trunk/main/libresample/include/libresample.h
@@ -0,0 +1,120 @@
+/**********************************************************************
+
+ resample.h
+
+ Real-time library interface by Dominic Mazzoni
+
+ Based on resample-1.7:
+ http://www-ccrma.stanford.edu/~jos/resample/
+
+ License: LGPL - see the file LICENSE.txt for more information
+
+**********************************************************************/
+
+/*!
+ * \file
+ * \brief libresample API
+ * \author Dominic Mazzoni
+ */
+
+#ifndef LIBRESAMPLE_INCLUDED
+#define LIBRESAMPLE_INCLUDED
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*!
+ * \brief Create a resampler
+ *
+ * \arg highQuality Set this argument to non-zero to enable higher quality
+ * resampling.
+ * \arg minFactor This is the minimum resampling factor that will be used for
+ * this resampler. The resampling factor is calculated in the following
+ * way: ( from sample rate / to sample rate ).
+ * \arg maxFactor This is the maximum resampling factor that will be used for
+ * this resampler.
+ *
+ * Use this function to create a new resampler that will maintain state
+ * information about the stream of audio being resampled.
+ *
+ * \return A handle to a new resampler
+ */
+void *resample_open(int highQuality,
+ double minFactor,
+ double maxFactor);
+
+/*!
+ * \brief Duplicate a resampler
+ *
+ * \arg handle the resampler to duplicate
+ *
+ * \return A new handle to a resampler, initialized with the same parameters
+ * used to create the original resampler.
+ */
+void *resample_dup(const void *handle);
+
+/*!
+ * \brief Get filter width for resampler
+ *
+ * \arg handle the resampler
+ *
+ * \return the filter width.
+ */
+int resample_get_filter_width(const void *handle);
+
+/*!
+ * \brief Resample a chunk of audio
+ *
+ * \arg handle the resampler
+ * \arg factor the resampling factor. This factor should be calculated as
+ * ( from sample rate / to sample rate ). So, for converting from 8 kHz
+ * to 16 kHz, this value would be 2.0.
+ * \arg inBuffer the input buffer for audio to resample.
+ * \arg inBufferLen the number of samples in the input buffer
+ * \arg lastFlag Set this argument to non-zero if the data in the input buffer
+ * is known to be the end of a stream. This would be used if you're
+ * resampling a file, for example.
+ * \arg inBufferUsed This is an output parameter that indicates how many
+ * samples were consumed from the input buffer. Generally, this function
+ * is called in a loop until you know that the entire input buffer has
+ * been consumed, as it may take multiple calls to complete.
+ * \arg outBuffer This is the output buffer. This function will write the
+ * resampled audio into this buffer.
+ * \arg outBufferLen This parameter specifies how many samples there is room
+ * for in the output buffer.
+ *
+ * This is the main function used for resampling audio. It should be called
+ * in a loop until all of the data from the input buffer is consumed, or the
+ * output buffer has been filled.
+ *
+ * \return the number of samples written to the output buffer. If the return
+ * value is equal to the value provided in the outBufferLen parameter,
+ * then the output buffer has been filled.
+ */
+int resample_process(void *handle,
+ double factor,
+ float *inBuffer,
+ int inBufferLen,
+ int lastFlag,
+ int *inBufferUsed,
+ float *outBuffer,
+ int outBufferLen);
+
+/*!
+ * \brief Close a resampler
+ *
+ * \arg handle the resampler to close
+ *
+ * Use this function to release a handle to a resampler that was created using
+ * either resample_open() or resample_dup().
+ *
+ * \return nothing.
+ */
+void resample_close(void *handle);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif /* __cplusplus */
+
+#endif /* LIBRESAMPLE_INCLUDED */
diff --git a/trunk/main/libresample/install-sh b/trunk/main/libresample/install-sh
new file mode 100755
index 000000000..e9de23842
--- /dev/null
+++ b/trunk/main/libresample/install-sh
@@ -0,0 +1,251 @@
+#!/bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5 (mit/util/scripts/install.sh).
+#
+# Copyright 1991 by the Massachusetts Institute of Technology
+#
+# Permission to use, copy, modify, distribute, and sell this software and its
+# documentation for any purpose is hereby granted without fee, provided that
+# the above copyright notice appear in all copies and that both that
+# copyright notice and this permission notice appear in supporting
+# documentation, and that the name of M.I.T. not be used in advertising or
+# publicity pertaining to distribution of the software without specific,
+# written prior permission. M.I.T. makes no representations about the
+# suitability of this software for any purpose. It is provided "as is"
+# without express or implied warranty.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch. It can only install one file at a time, a restriction
+# shared with many OS's install programs.
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+transformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+ case $1 in
+ -c) instcmd="$cpprog"
+ shift
+ continue;;
+
+ -d) dir_arg=true
+ shift
+ continue;;
+
+ -m) chmodcmd="$chmodprog $2"
+ shift
+ shift
+ continue;;
+
+ -o) chowncmd="$chownprog $2"
+ shift
+ shift
+ continue;;
+
+ -g) chgrpcmd="$chgrpprog $2"
+ shift
+ shift
+ continue;;
+
+ -s) stripcmd="$stripprog"
+ shift
+ continue;;
+
+ -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+ shift
+ continue;;
+
+ -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+ shift
+ continue;;
+
+ *) if [ x"$src" = x ]
+ then
+ src=$1
+ else
+ # this colon is to work around a 386BSD /bin/sh bug
+ :
+ dst=$1
+ fi
+ shift
+ continue;;
+ esac
+done
+
+if [ x"$src" = x ]
+then
+ echo "install: no input file specified"
+ exit 1
+else
+ true
+fi
+
+if [ x"$dir_arg" != x ]; then
+ dst=$src
+ src=""
+
+ if [ -d $dst ]; then
+ instcmd=:
+ chmodcmd=""
+ else
+ instcmd=mkdir
+ fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad
+# if $src (and thus $dsttmp) contains '*'.
+
+ if [ -f $src -o -d $src ]
+ then
+ true
+ else
+ echo "install: $src does not exist"
+ exit 1
+ fi
+
+ if [ x"$dst" = x ]
+ then
+ echo "install: no destination specified"
+ exit 1
+ else
+ true
+ fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+ if [ -d $dst ]
+ then
+ dst="$dst"/`basename $src`
+ else
+ true
+ fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+# this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+ pathcomp="${pathcomp}${1}"
+ shift
+
+ if [ ! -d "${pathcomp}" ] ;
+ then
+ $mkdirprog "${pathcomp}"
+ else
+ true
+ fi
+
+ pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+ $doit $instcmd $dst &&
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+ if [ x"$transformarg" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ dstfile=`basename $dst $transformbasename |
+ sed $transformarg`$transformbasename
+ fi
+
+# don't allow the sed command to completely eliminate the filename
+
+ if [ x"$dstfile" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ true
+ fi
+
+# Make a temp file name in the proper directory.
+
+ dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+ $doit $instcmd $src $dsttmp &&
+
+ trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing. If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+ $doit $rmcmd -f $dstdir/$dstfile &&
+ $doit $mvcmd $dsttmp $dstdir/$dstfile
+
+fi &&
+
+
+exit 0
diff --git a/trunk/main/libresample/src/configtemplate.h b/trunk/main/libresample/src/configtemplate.h
new file mode 100644
index 000000000..94ae1cea9
--- /dev/null
+++ b/trunk/main/libresample/src/configtemplate.h
@@ -0,0 +1,7 @@
+/* Run configure to generate config.h automatically on any
+ system supported by GNU autoconf. For all other systems,
+ use this file as a template to create config.h
+*/
+
+#undef HAVE_INTTYPES_H
+
diff --git a/trunk/main/libresample/src/filterkit.c b/trunk/main/libresample/src/filterkit.c
new file mode 100644
index 000000000..bc92285f2
--- /dev/null
+++ b/trunk/main/libresample/src/filterkit.c
@@ -0,0 +1,215 @@
+/**********************************************************************
+
+ resamplesubs.c
+
+ Real-time library interface by Dominic Mazzoni
+
+ Based on resample-1.7:
+ http://www-ccrma.stanford.edu/~jos/resample/
+
+ License: LGPL - see the file LICENSE.txt for more information
+
+ This file provides Kaiser-windowed low-pass filter support,
+ including a function to create the filter coefficients, and
+ two functions to apply the filter at a particular point.
+
+**********************************************************************/
+
+/* Definitions */
+#include "resample_defs.h"
+
+#include "filterkit.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+
+/* LpFilter()
+ *
+ * reference: "Digital Filters, 2nd edition"
+ * R.W. Hamming, pp. 178-179
+ *
+ * Izero() computes the 0th order modified bessel function of the first kind.
+ * (Needed to compute Kaiser window).
+ *
+ * LpFilter() computes the coeffs of a Kaiser-windowed low pass filter with
+ * the following characteristics:
+ *
+ * c[] = array in which to store computed coeffs
+ * frq = roll-off frequency of filter
+ * N = Half the window length in number of coeffs
+ * Beta = parameter of Kaiser window
+ * Num = number of coeffs before 1/frq
+ *
+ * Beta trades the rejection of the lowpass filter against the transition
+ * width from passband to stopband. Larger Beta means a slower
+ * transition and greater stopband rejection. See Rabiner and Gold
+ * (Theory and Application of DSP) under Kaiser windows for more about
+ * Beta. The following table from Rabiner and Gold gives some feel
+ * for the effect of Beta:
+ *
+ * All ripples in dB, width of transition band = D*N where N = window length
+ *
+ * BETA D PB RIP SB RIP
+ * 2.120 1.50 +-0.27 -30
+ * 3.384 2.23 0.0864 -40
+ * 4.538 2.93 0.0274 -50
+ * 5.658 3.62 0.00868 -60
+ * 6.764 4.32 0.00275 -70
+ * 7.865 5.0 0.000868 -80
+ * 8.960 5.7 0.000275 -90
+ * 10.056 6.4 0.000087 -100
+ */
+
+#define IzeroEPSILON 1E-21 /* Max error acceptable in Izero */
+
+static double Izero(double x)
+{
+ double sum, u, halfx, temp;
+ int n;
+
+ sum = u = n = 1;
+ halfx = x/2.0;
+ do {
+ temp = halfx/(double)n;
+ n += 1;
+ temp *= temp;
+ u *= temp;
+ sum += u;
+ } while (u >= IzeroEPSILON*sum);
+ return(sum);
+}
+
+void lrsLpFilter(double c[], int N, double frq, double Beta, int Num)
+{
+ double IBeta, temp, temp1, inm1;
+ int i;
+
+ /* Calculate ideal lowpass filter impulse response coefficients: */
+ c[0] = 2.0*frq;
+ for (i=1; i<N; i++) {
+ temp = PI*(double)i/(double)Num;
+ c[i] = sin(2.0*temp*frq)/temp; /* Analog sinc function, cutoff = frq */
+ }
+
+ /*
+ * Calculate and Apply Kaiser window to ideal lowpass filter.
+ * Note: last window value is IBeta which is NOT zero.
+ * You're supposed to really truncate the window here, not ramp
+ * it to zero. This helps reduce the first sidelobe.
+ */
+ IBeta = 1.0/Izero(Beta);
+ inm1 = 1.0/((double)(N-1));
+ for (i=1; i<N; i++) {
+ temp = (double)i * inm1;
+ temp1 = 1.0 - temp*temp;
+ temp1 = (temp1<0? 0: temp1); /* make sure it's not negative since
+ we're taking the square root - this
+ happens on Pentium 4's due to tiny
+ roundoff errors */
+ c[i] *= Izero(Beta*sqrt(temp1)) * IBeta;
+ }
+}
+
+float lrsFilterUp(float Imp[], /* impulse response */
+ float ImpD[], /* impulse response deltas */
+ UWORD Nwing, /* len of one wing of filter */
+ BOOL Interp, /* Interpolate coefs using deltas? */
+ float *Xp, /* Current sample */
+ double Ph, /* Phase */
+ int Inc) /* increment (1 for right wing or -1 for left) */
+{
+ float *Hp, *Hdp = NULL, *End;
+ double a = 0;
+ float v, t;
+
+ Ph *= Npc; /* Npc is number of values per 1/delta in impulse response */
+
+ v = 0.0; /* The output value */
+ Hp = &Imp[(int)Ph];
+ End = &Imp[Nwing];
+ if (Interp) {
+ Hdp = &ImpD[(int)Ph];
+ a = Ph - floor(Ph); /* fractional part of Phase */
+ }
+
+ if (Inc == 1) /* If doing right wing... */
+ { /* ...drop extra coeff, so when Ph is */
+ End--; /* 0.5, we don't do too many mult's */
+ if (Ph == 0) /* If the phase is zero... */
+ { /* ...then we've already skipped the */
+ Hp += Npc; /* first sample, so we must also */
+ Hdp += Npc; /* skip ahead in Imp[] and ImpD[] */
+ }
+ }
+
+ if (Interp)
+ while (Hp < End) {
+ t = *Hp; /* Get filter coeff */
+ t += (*Hdp)*a; /* t is now interp'd filter coeff */
+ Hdp += Npc; /* Filter coeff differences step */
+ t *= *Xp; /* Mult coeff by input sample */
+ v += t; /* The filter output */
+ Hp += Npc; /* Filter coeff step */
+ Xp += Inc; /* Input signal step. NO CHECK ON BOUNDS */
+ }
+ else
+ while (Hp < End) {
+ t = *Hp; /* Get filter coeff */
+ t *= *Xp; /* Mult coeff by input sample */
+ v += t; /* The filter output */
+ Hp += Npc; /* Filter coeff step */
+ Xp += Inc; /* Input signal step. NO CHECK ON BOUNDS */
+ }
+
+ return v;
+}
+
+float lrsFilterUD(float Imp[], /* impulse response */
+ float ImpD[], /* impulse response deltas */
+ UWORD Nwing, /* len of one wing of filter */
+ BOOL Interp, /* Interpolate coefs using deltas? */
+ float *Xp, /* Current sample */
+ double Ph, /* Phase */
+ int Inc, /* increment (1 for right wing or -1 for left) */
+ double dhb) /* filter sampling period */
+{
+ float a;
+ float *Hp, *Hdp, *End;
+ float v, t;
+ double Ho;
+
+ v = 0.0; /* The output value */
+ Ho = Ph*dhb;
+ End = &Imp[Nwing];
+ if (Inc == 1) /* If doing right wing... */
+ { /* ...drop extra coeff, so when Ph is */
+ End--; /* 0.5, we don't do too many mult's */
+ if (Ph == 0) /* If the phase is zero... */
+ Ho += dhb; /* ...then we've already skipped the */
+ } /* first sample, so we must also */
+ /* skip ahead in Imp[] and ImpD[] */
+
+ if (Interp)
+ while ((Hp = &Imp[(int)Ho]) < End) {
+ t = *Hp; /* Get IR sample */
+ Hdp = &ImpD[(int)Ho]; /* get interp bits from diff table*/
+ a = Ho - floor(Ho); /* a is logically between 0 and 1 */
+ t += (*Hdp)*a; /* t is now interp'd filter coeff */
+ t *= *Xp; /* Mult coeff by input sample */
+ v += t; /* The filter output */
+ Ho += dhb; /* IR step */
+ Xp += Inc; /* Input signal step. NO CHECK ON BOUNDS */
+ }
+ else
+ while ((Hp = &Imp[(int)Ho]) < End) {
+ t = *Hp; /* Get IR sample */
+ t *= *Xp; /* Mult coeff by input sample */
+ v += t; /* The filter output */
+ Ho += dhb; /* IR step */
+ Xp += Inc; /* Input signal step. NO CHECK ON BOUNDS */
+ }
+
+ return v;
+}
diff --git a/trunk/main/libresample/src/filterkit.h b/trunk/main/libresample/src/filterkit.h
new file mode 100644
index 000000000..9df0ae869
--- /dev/null
+++ b/trunk/main/libresample/src/filterkit.h
@@ -0,0 +1,28 @@
+/**********************************************************************
+
+ resamplesubs.c
+
+ Real-time library interface by Dominic Mazzoni
+
+ Based on resample-1.7:
+ http://www-ccrma.stanford.edu/~jos/resample/
+
+ License: LGPL - see the file LICENSE.txt for more information
+
+**********************************************************************/
+
+/* Definitions */
+#include "resample_defs.h"
+
+/*
+ * FilterUp() - Applies a filter to a given sample when up-converting.
+ * FilterUD() - Applies a filter to a given sample when up- or down-
+ */
+
+float lrsFilterUp(float Imp[], float ImpD[], UWORD Nwing, BOOL Interp,
+ float *Xp, double Ph, int Inc);
+
+float lrsFilterUD(float Imp[], float ImpD[], UWORD Nwing, BOOL Interp,
+ float *Xp, double Ph, int Inc, double dhb);
+
+void lrsLpFilter(double c[], int N, double frq, double Beta, int Num);
diff --git a/trunk/main/libresample/src/resample.c b/trunk/main/libresample/src/resample.c
new file mode 100644
index 000000000..85ff75f76
--- /dev/null
+++ b/trunk/main/libresample/src/resample.c
@@ -0,0 +1,347 @@
+/**********************************************************************
+
+ resample.c
+
+ Real-time library interface by Dominic Mazzoni
+
+ Based on resample-1.7:
+ http://www-ccrma.stanford.edu/~jos/resample/
+
+ License: LGPL - see the file LICENSE.txt for more information
+
+ This is the main source file, implementing all of the API
+ functions and handling all of the buffering logic.
+
+**********************************************************************/
+
+/* External interface */
+#include "../include/libresample.h"
+
+/* Definitions */
+#include "resample_defs.h"
+
+#include "filterkit.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+
+typedef struct {
+ float *Imp;
+ float *ImpD;
+ float LpScl;
+ UWORD Nmult;
+ UWORD Nwing;
+ double minFactor;
+ double maxFactor;
+ UWORD XSize;
+ float *X;
+ UWORD Xp; /* Current "now"-sample pointer for input */
+ UWORD Xread; /* Position to put new samples */
+ UWORD Xoff;
+ UWORD YSize;
+ float *Y;
+ UWORD Yp;
+ double Time;
+} rsdata;
+
+void *resample_dup(const void * handle)
+{
+ const rsdata *cpy = (const rsdata *)handle;
+ rsdata *hp = (rsdata *)malloc(sizeof(rsdata));
+
+ hp->minFactor = cpy->minFactor;
+ hp->maxFactor = cpy->maxFactor;
+ hp->Nmult = cpy->Nmult;
+ hp->LpScl = cpy->LpScl;
+ hp->Nwing = cpy->Nwing;
+
+ hp->Imp = (float *)malloc(hp->Nwing * sizeof(float));
+ memcpy(hp->Imp, cpy->Imp, hp->Nwing * sizeof(float));
+ hp->ImpD = (float *)malloc(hp->Nwing * sizeof(float));
+ memcpy(hp->ImpD, cpy->ImpD, hp->Nwing * sizeof(float));
+
+ hp->Xoff = cpy->Xoff;
+ hp->XSize = cpy->XSize;
+ hp->X = (float *)malloc((hp->XSize + hp->Xoff) * sizeof(float));
+ memcpy(hp->X, cpy->X, (hp->XSize + hp->Xoff) * sizeof(float));
+ hp->Xp = cpy->Xp;
+ hp->Xread = cpy->Xread;
+ hp->YSize = cpy->YSize;
+ hp->Y = (float *)malloc(hp->YSize * sizeof(float));
+ memcpy(hp->Y, cpy->Y, hp->YSize * sizeof(float));
+ hp->Yp = cpy->Yp;
+ hp->Time = cpy->Time;
+
+ return (void *)hp;
+}
+
+void *resample_open(int highQuality, double minFactor, double maxFactor)
+{
+ double *Imp64;
+ double Rolloff, Beta;
+ rsdata *hp;
+ UWORD Xoff_min, Xoff_max;
+ int i;
+
+ /* Just exit if we get invalid factors */
+ if (minFactor <= 0.0 || maxFactor <= 0.0 || maxFactor < minFactor) {
+ #if defined(DEBUG)
+ fprintf(stderr,
+ "libresample: "
+ "minFactor and maxFactor must be positive real numbers,\n"
+ "and maxFactor should be larger than minFactor.\n");
+ #endif
+ return 0;
+ }
+
+ hp = (rsdata *)malloc(sizeof(rsdata));
+
+ hp->minFactor = minFactor;
+ hp->maxFactor = maxFactor;
+
+ if (highQuality)
+ hp->Nmult = 35;
+ else
+ hp->Nmult = 11;
+
+ hp->LpScl = 1.0;
+ hp->Nwing = Npc*(hp->Nmult-1)/2; /* # of filter coeffs in right wing */
+
+ Rolloff = 0.90;
+ Beta = 6;
+
+ Imp64 = (double *)malloc(hp->Nwing * sizeof(double));
+
+ lrsLpFilter(Imp64, hp->Nwing, 0.5*Rolloff, Beta, Npc);
+
+ hp->Imp = (float *)malloc(hp->Nwing * sizeof(float));
+ hp->ImpD = (float *)malloc(hp->Nwing * sizeof(float));
+ for(i=0; i<hp->Nwing; i++)
+ hp->Imp[i] = Imp64[i];
+
+ /* Storing deltas in ImpD makes linear interpolation
+ of the filter coefficients faster */
+ for (i=0; i<hp->Nwing-1; i++)
+ hp->ImpD[i] = hp->Imp[i+1] - hp->Imp[i];
+
+ /* Last coeff. not interpolated */
+ hp->ImpD[hp->Nwing-1] = - hp->Imp[hp->Nwing-1];
+
+ free(Imp64);
+
+ /* Calc reach of LP filter wing (plus some creeping room) */
+ Xoff_min = ((hp->Nmult+1)/2.0) * MAX(1.0, 1.0/minFactor) + 10;
+ Xoff_max = ((hp->Nmult+1)/2.0) * MAX(1.0, 1.0/maxFactor) + 10;
+ hp->Xoff = MAX(Xoff_min, Xoff_max);
+
+ /* Make the inBuffer size at least 4096, but larger if necessary
+ in order to store the minimum reach of the LP filter and then some.
+ Then allocate the buffer an extra Xoff larger so that
+ we can zero-pad up to Xoff zeros at the end when we reach the
+ end of the input samples. */
+ hp->XSize = MAX(2*hp->Xoff+10, 4096);
+ hp->X = (float *)malloc((hp->XSize + hp->Xoff) * sizeof(float));
+ hp->Xp = hp->Xoff;
+ hp->Xread = hp->Xoff;
+
+ /* Need Xoff zeros at begining of X buffer */
+ for(i=0; i<hp->Xoff; i++)
+ hp->X[i]=0;
+
+ /* Make the outBuffer long enough to hold the entire processed
+ output of one inBuffer */
+ hp->YSize = (int)(((double)hp->XSize)*maxFactor+2.0);
+ hp->Y = (float *)malloc(hp->YSize * sizeof(float));
+ hp->Yp = 0;
+
+ hp->Time = (double)hp->Xoff; /* Current-time pointer for converter */
+
+ return (void *)hp;
+}
+
+int resample_get_filter_width(const void *handle)
+{
+ const rsdata *hp = (const rsdata *)handle;
+ return hp->Xoff;
+}
+
+int resample_process(void *handle,
+ double factor,
+ float *inBuffer,
+ int inBufferLen,
+ int lastFlag,
+ int *inBufferUsed, /* output param */
+ float *outBuffer,
+ int outBufferLen)
+{
+ rsdata *hp = (rsdata *)handle;
+ float *Imp = hp->Imp;
+ float *ImpD = hp->ImpD;
+ float LpScl = hp->LpScl;
+ UWORD Nwing = hp->Nwing;
+ BOOL interpFilt = FALSE; /* TRUE means interpolate filter coeffs */
+ int outSampleCount;
+ UWORD Nout, Ncreep, Nreuse;
+ int Nx;
+ int i, len;
+
+ #if defined(DEBUG)
+ fprintf(stderr, "resample_process: in=%d, out=%d lastFlag=%d\n",
+ inBufferLen, outBufferLen, lastFlag);
+ #endif
+
+ /* Initialize inBufferUsed and outSampleCount to 0 */
+ *inBufferUsed = 0;
+ outSampleCount = 0;
+
+ if (factor < hp->minFactor || factor > hp->maxFactor) {
+ #if defined(DEBUG)
+ fprintf(stderr,
+ "libresample: factor %f is not between "
+ "minFactor=%f and maxFactor=%f",
+ factor, hp->minFactor, hp->maxFactor);
+ #endif
+ return -1;
+ }
+
+ /* Start by copying any samples still in the Y buffer to the output
+ buffer */
+ if (hp->Yp && (outBufferLen-outSampleCount)>0) {
+ len = MIN(outBufferLen-outSampleCount, hp->Yp);
+ for(i=0; i<len; i++)
+ outBuffer[outSampleCount+i] = hp->Y[i];
+ outSampleCount += len;
+ for(i=0; i<hp->Yp-len; i++)
+ hp->Y[i] = hp->Y[i+len];
+ hp->Yp -= len;
+ }
+
+ /* If there are still output samples left, return now - we need
+ the full output buffer available to us... */
+ if (hp->Yp)
+ return outSampleCount;
+
+ /* Account for increased filter gain when using factors less than 1 */
+ if (factor < 1)
+ LpScl = LpScl*factor;
+
+ for(;;) {
+
+ /* This is the maximum number of samples we can process
+ per loop iteration */
+
+ #if defined(DEBUG)
+ printf("XSize: %d Xoff: %d Xread: %d Xp: %d lastFlag: %d\n",
+ hp->XSize, hp->Xoff, hp->Xread, hp->Xp, lastFlag);
+ #endif
+
+ /* Copy as many samples as we can from the input buffer into X */
+ len = hp->XSize - hp->Xread;
+
+ if (len >= (inBufferLen - (*inBufferUsed)))
+ len = (inBufferLen - (*inBufferUsed));
+
+ for(i=0; i<len; i++)
+ hp->X[hp->Xread + i] = inBuffer[(*inBufferUsed) + i];
+
+ *inBufferUsed += len;
+ hp->Xread += len;
+
+ if (lastFlag && (*inBufferUsed == inBufferLen)) {
+ /* If these are the last samples, zero-pad the
+ end of the input buffer and make sure we process
+ all the way to the end */
+ Nx = hp->Xread - hp->Xoff;
+ for(i=0; i<hp->Xoff; i++)
+ hp->X[hp->Xread + i] = 0;
+ }
+ else
+ Nx = hp->Xread - 2 * hp->Xoff;
+
+ #if defined(DEBUG)
+ fprintf(stderr, "new len=%d Nx=%d\n", len, Nx);
+ #endif
+
+ if (Nx <= 0)
+ break;
+
+ /* Resample stuff in input buffer */
+ if (factor >= 1) { /* SrcUp() is faster if we can use it */
+ Nout = lrsSrcUp(hp->X, hp->Y, factor, &hp->Time, Nx,
+ Nwing, LpScl, Imp, ImpD, interpFilt);
+ }
+ else {
+ Nout = lrsSrcUD(hp->X, hp->Y, factor, &hp->Time, Nx,
+ Nwing, LpScl, Imp, ImpD, interpFilt);
+ }
+
+ #if defined(DEBUG)
+ printf("Nout: %d\n", Nout);
+ #endif
+
+ hp->Time -= Nx; /* Move converter Nx samples back in time */
+ hp->Xp += Nx; /* Advance by number of samples processed */
+
+ /* Calc time accumulation in Time */
+ Ncreep = (int)(hp->Time) - hp->Xoff;
+ if (Ncreep) {
+ hp->Time -= Ncreep; /* Remove time accumulation */
+ hp->Xp += Ncreep; /* and add it to read pointer */
+ }
+
+ /* Copy part of input signal that must be re-used */
+ Nreuse = hp->Xread - (hp->Xp - hp->Xoff);
+
+ for (i=0; i<Nreuse; i++)
+ hp->X[i] = hp->X[i + (hp->Xp - hp->Xoff)];
+
+ #if defined(DEBUG)
+ printf("New Xread=%d\n", Nreuse);
+ #endif
+
+ hp->Xread = Nreuse; /* Pos in input buff to read new data into */
+ hp->Xp = hp->Xoff;
+
+ /* Check to see if output buff overflowed (shouldn't happen!) */
+ if (Nout > hp->YSize) {
+ #if defined(DEBUG)
+ printf("Nout: %d YSize: %d\n", Nout, hp->YSize);
+ #endif
+ fprintf(stderr, "libresample: Output array overflow!\n");
+ return -1;
+ }
+
+ hp->Yp = Nout;
+
+ /* Copy as many samples as possible to the output buffer */
+ if (hp->Yp && (outBufferLen-outSampleCount)>0) {
+ len = MIN(outBufferLen-outSampleCount, hp->Yp);
+ for(i=0; i<len; i++)
+ outBuffer[outSampleCount+i] = hp->Y[i];
+ outSampleCount += len;
+ for(i=0; i<hp->Yp-len; i++)
+ hp->Y[i] = hp->Y[i+len];
+ hp->Yp -= len;
+ }
+
+ /* If there are still output samples left, return now,
+ since we need the full output buffer available */
+ if (hp->Yp)
+ break;
+ }
+
+ return outSampleCount;
+}
+
+void resample_close(void *handle)
+{
+ rsdata *hp = (rsdata *)handle;
+ free(hp->X);
+ free(hp->Y);
+ free(hp->Imp);
+ free(hp->ImpD);
+ free(hp);
+}
+
diff --git a/trunk/main/libresample/src/resample_defs.h b/trunk/main/libresample/src/resample_defs.h
new file mode 100644
index 000000000..a0e7afc37
--- /dev/null
+++ b/trunk/main/libresample/src/resample_defs.h
@@ -0,0 +1,88 @@
+/**********************************************************************
+
+ resample_defs.h
+
+ Real-time library interface by Dominic Mazzoni
+
+ Based on resample-1.7:
+ http://www-ccrma.stanford.edu/~jos/resample/
+
+ License: LGPL - see the file LICENSE.txt for more information
+
+**********************************************************************/
+
+#ifndef __RESAMPLE_DEFS__
+#define __RESAMPLE_DEFS__
+
+#if 0
+#if !defined(WIN32) && !defined(__CYGWIN__)
+#include "config.h"
+#endif
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef PI
+#define PI (3.14159265358979232846)
+#endif
+
+#ifndef PI2
+#define PI2 (6.28318530717958465692)
+#endif
+
+#define D2R (0.01745329348) /* (2*pi)/360 */
+#define R2D (57.29577951) /* 360/(2*pi) */
+
+#ifndef MAX
+#define MAX(x,y) ((x)>(y) ?(x):(y))
+#endif
+#ifndef MIN
+#define MIN(x,y) ((x)<(y) ?(x):(y))
+#endif
+
+#ifndef ABS
+#define ABS(x) ((x)<0 ?(-(x)):(x))
+#endif
+
+#ifndef SGN
+#define SGN(x) ((x)<0 ?(-1):((x)==0?(0):(1)))
+#endif
+
+#if HAVE_INTTYPES_H
+ #include <inttypes.h>
+ typedef char BOOL;
+ typedef int32_t WORD;
+ typedef uint32_t UWORD;
+#else
+ typedef char BOOL;
+ typedef int WORD;
+ typedef unsigned int UWORD;
+#endif
+
+#ifdef DEBUG
+#define INLINE
+#else
+#define INLINE inline
+#endif
+
+/* Accuracy */
+
+#define Npc 4096
+
+/* Function prototypes */
+
+int lrsSrcUp(float X[], float Y[], double factor, double *Time,
+ UWORD Nx, UWORD Nwing, float LpScl,
+ float Imp[], float ImpD[], BOOL Interp);
+
+int lrsSrcUD(float X[], float Y[], double factor, double *Time,
+ UWORD Nx, UWORD Nwing, float LpScl,
+ float Imp[], float ImpD[], BOOL Interp);
+
+#endif
diff --git a/trunk/main/libresample/src/resamplesubs.c b/trunk/main/libresample/src/resamplesubs.c
new file mode 100644
index 000000000..c3c095dc0
--- /dev/null
+++ b/trunk/main/libresample/src/resamplesubs.c
@@ -0,0 +1,123 @@
+/**********************************************************************
+
+ resamplesubs.c
+
+ Real-time library interface by Dominic Mazzoni
+
+ Based on resample-1.7:
+ http://www-ccrma.stanford.edu/~jos/resample/
+
+ License: LGPL - see the file LICENSE.txt for more information
+
+ This file provides the routines that do sample-rate conversion
+ on small arrays, calling routines from filterkit.
+
+**********************************************************************/
+
+/* Definitions */
+#include "resample_defs.h"
+
+#include "filterkit.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+
+/* Sampling rate up-conversion only subroutine;
+ * Slightly faster than down-conversion;
+ */
+int lrsSrcUp(float X[],
+ float Y[],
+ double factor,
+ double *TimePtr,
+ UWORD Nx,
+ UWORD Nwing,
+ float LpScl,
+ float Imp[],
+ float ImpD[],
+ BOOL Interp)
+{
+ float *Xp, *Ystart;
+ float v;
+
+ double CurrentTime = *TimePtr;
+ double dt; /* Step through input signal */
+ double endTime; /* When Time reaches EndTime, return to user */
+
+ dt = 1.0/factor; /* Output sampling period */
+
+ Ystart = Y;
+ endTime = CurrentTime + Nx;
+ while (CurrentTime < endTime)
+ {
+ double LeftPhase = CurrentTime-floor(CurrentTime);
+ double RightPhase = 1.0 - LeftPhase;
+
+ Xp = &X[(int)CurrentTime]; /* Ptr to current input sample */
+ /* Perform left-wing inner product */
+ v = lrsFilterUp(Imp, ImpD, Nwing, Interp, Xp,
+ LeftPhase, -1);
+ /* Perform right-wing inner product */
+ v += lrsFilterUp(Imp, ImpD, Nwing, Interp, Xp+1,
+ RightPhase, 1);
+
+ v *= LpScl; /* Normalize for unity filter gain */
+
+ *Y++ = v; /* Deposit output */
+ CurrentTime += dt; /* Move to next sample by time increment */
+ }
+
+ *TimePtr = CurrentTime;
+ return (Y - Ystart); /* Return the number of output samples */
+}
+
+/* Sampling rate conversion subroutine */
+
+int lrsSrcUD(float X[],
+ float Y[],
+ double factor,
+ double *TimePtr,
+ UWORD Nx,
+ UWORD Nwing,
+ float LpScl,
+ float Imp[],
+ float ImpD[],
+ BOOL Interp)
+{
+ float *Xp, *Ystart;
+ float v;
+
+ double CurrentTime = (*TimePtr);
+ double dh; /* Step through filter impulse response */
+ double dt; /* Step through input signal */
+ double endTime; /* When Time reaches EndTime, return to user */
+
+ dt = 1.0/factor; /* Output sampling period */
+
+ dh = MIN(Npc, factor*Npc); /* Filter sampling period */
+
+ Ystart = Y;
+ endTime = CurrentTime + Nx;
+ while (CurrentTime < endTime)
+ {
+ double LeftPhase = CurrentTime-floor(CurrentTime);
+ double RightPhase = 1.0 - LeftPhase;
+
+ Xp = &X[(int)CurrentTime]; /* Ptr to current input sample */
+ /* Perform left-wing inner product */
+ v = lrsFilterUD(Imp, ImpD, Nwing, Interp, Xp,
+ LeftPhase, -1, dh);
+ /* Perform right-wing inner product */
+ v += lrsFilterUD(Imp, ImpD, Nwing, Interp, Xp+1,
+ RightPhase, 1, dh);
+
+ v *= LpScl; /* Normalize for unity filter gain */
+ *Y++ = v; /* Deposit output */
+
+ CurrentTime += dt; /* Move to next sample by time increment */
+ }
+
+ *TimePtr = CurrentTime;
+ return (Y - Ystart); /* Return the number of output samples */
+}
diff --git a/trunk/main/libresample/tests/compareresample.c b/trunk/main/libresample/tests/compareresample.c
new file mode 100644
index 000000000..8773c9d4e
--- /dev/null
+++ b/trunk/main/libresample/tests/compareresample.c
@@ -0,0 +1,183 @@
+/**********************************************************************
+
+ compareresample.c
+
+ Real-time library interface by Dominic Mazzoni
+
+ Based on resample-1.7:
+ http://www-ccrma.stanford.edu/~jos/resample/
+
+ License: LGPL - see the file LICENSE.txt for more information
+
+**********************************************************************/
+
+#include "../include/libresample.h"
+
+#include <samplerate.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include <sys/time.h>
+
+#define MIN(A, B) (A) < (B)? (A) : (B)
+
+void dostat(char *name, float *d1, float *d2, int len)
+{
+ int i;
+ double sum, sumsq, err, rmserr;
+
+ sum = 0.0;
+ sumsq = 0.0;
+ for(i=0; i<len; i++) {
+ double diff = d1[i] - d2[i];
+ sum += fabs(diff);
+ sumsq += diff * diff;
+ }
+ err = sum / len;
+ rmserr = sqrt(sumsq / len);
+ printf(" %s: Avg err: %f RMS err: %f\n", name, err, rmserr);
+}
+
+void runtest(float *src, int srclen,
+ float *ans, int anslen,
+ double factor)
+{
+ struct timeval tv0, tv1;
+ int dstlen = (int)(srclen * factor);
+ float *dst_rs = (float *)malloc((dstlen+100) * sizeof(float));
+ float *dst_rabbit = (float *)malloc((dstlen+100) * sizeof(float));
+ void *handle;
+ SRC_DATA rabbit;
+ double deltat;
+ int srcblocksize = srclen;
+ int dstblocksize = dstlen;
+ int i, out, out_rabbit, o, srcused;
+ int statlen, srcpos;
+
+ /* do resample */
+
+ for(i=0; i<dstlen+100; i++)
+ dst_rs[i] = -99.0;
+
+ gettimeofday(&tv0, NULL);
+
+ handle = resample_open(1, factor, factor);
+ out = 0;
+ srcpos = 0;
+ for(;;) {
+ int srcBlock = MIN(srclen-srcpos, srcblocksize);
+ int lastFlag = (srcBlock == srclen-srcpos);
+
+ o = resample_process(handle, factor,
+ &src[srcpos], srcBlock,
+ lastFlag, &srcused,
+ &dst_rs[out], MIN(dstlen-out, dstblocksize));
+ srcpos += srcused;
+ if (o >= 0)
+ out += o;
+ if (o < 0 || (o == 0 && srcpos == srclen))
+ break;
+ }
+ resample_close(handle);
+
+ gettimeofday(&tv1, NULL);
+ deltat =
+ (tv1.tv_sec + tv1.tv_usec * 0.000001) -
+ (tv0.tv_sec + tv0.tv_usec * 0.000001);
+
+ if (o < 0) {
+ printf("Error: resample_process returned an error: %d\n", o);
+ }
+
+ if (out <= 0) {
+ printf("Error: resample_process returned %d samples\n", out);
+ free(dst_rs);
+ return;
+ }
+
+ printf(" resample: %.3f seconds, %d outputs\n", deltat, out);
+
+ /* do rabbit (Erik's libsamplerate) */
+
+ for(i=0; i<dstlen+100; i++)
+ dst_rabbit[i] = -99.0;
+
+ rabbit.data_in = src;
+ rabbit.data_out = dst_rabbit;
+ rabbit.input_frames = srclen;
+ rabbit.output_frames = dstlen;
+ rabbit.input_frames_used = 0;
+ rabbit.output_frames_gen = 0;
+ rabbit.end_of_input = 1;
+ rabbit.src_ratio = factor;
+
+ gettimeofday(&tv0, NULL);
+
+ /* src_simple(&rabbit, SRC_SINC_BEST_QUALITY, 1); */
+ src_simple(&rabbit, SRC_SINC_FASTEST, 1);
+ /* src_simple(&rabbit, SRC_LINEAR, 1); */
+
+ gettimeofday(&tv1, NULL);
+ deltat =
+ (tv1.tv_sec + tv1.tv_usec * 0.000001) -
+ (tv0.tv_sec + tv0.tv_usec * 0.000001);
+
+ out_rabbit = rabbit.output_frames_gen;
+
+ printf(" rabbit : %.3f seconds, %d outputs\n",
+ deltat, out_rabbit);
+
+ statlen = MIN(out, out_rabbit);
+ if (anslen > 0)
+ statlen = MIN(statlen, anslen);
+
+ if (ans) {
+ dostat("resample ", dst_rs, ans, statlen);
+ dostat("rabbit ", dst_rabbit, ans, statlen);
+ }
+ dostat( "RS vs rabbit", dst_rs, dst_rabbit, statlen);
+
+ free(dst_rs);
+ free(dst_rabbit);
+}
+
+int main(int argc, char **argv)
+{
+ int i, srclen;
+ float *src, *ans;
+
+ printf("\n*** sin wave, factor = 1.0 *** \n\n");
+ srclen = 100000;
+ src = malloc(srclen * sizeof(float));
+ for(i=0; i<srclen; i++)
+ src[i] = sin(i/100.0);
+
+ runtest(src, srclen, src, srclen, 1.0);
+
+ printf("\n*** sin wave, factor = 0.25 *** \n\n");
+ srclen = 100000;
+ for(i=0; i<srclen; i++)
+ src[i] = sin(i/100.0);
+ ans = malloc((srclen/4) * sizeof(float));
+ for(i=0; i<srclen/4; i++)
+ ans[i] = sin(i/25.0);
+
+ runtest(src, srclen, ans, srclen/4, 0.25);
+ free(ans);
+
+ printf("\n*** sin wave, factor = 4.0 *** \n\n");
+ srclen = 20000;
+ for(i=0; i<srclen; i++)
+ src[i] = sin(i/100.0);
+ ans = malloc((srclen*4) * sizeof(float));
+ for(i=0; i<srclen*4; i++)
+ ans[i] = sin(i/400.0);
+
+ runtest(src, srclen, ans, srclen*4, 4.0);
+ free(ans);
+ free(src);
+
+ return 0;
+}
diff --git a/trunk/main/libresample/tests/resample-sndfile.c b/trunk/main/libresample/tests/resample-sndfile.c
new file mode 100644
index 000000000..e780228c1
--- /dev/null
+++ b/trunk/main/libresample/tests/resample-sndfile.c
@@ -0,0 +1,213 @@
+/**********************************************************************
+
+ resample-sndfile.c
+
+ Written by Dominic Mazzoni
+
+ Based on resample-1.7:
+ http://www-ccrma.stanford.edu/~jos/resample/
+
+ License: LGPL - see the file LICENSE.txt for more information
+
+**********************************************************************/
+
+#include "../include/libresample.h"
+
+#include <sndfile.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include <sys/time.h>
+
+#define MIN(A, B) (A) < (B)? (A) : (B)
+
+void usage(char *progname)
+{
+ fprintf(stderr, "Usage: %s -by <ratio> <input> <output>\n", progname);
+ fprintf(stderr, " %s -to <rate> <input> <output>\n", progname);
+ fprintf(stderr, "\n");
+ exit(-1);
+}
+
+int main(int argc, char **argv)
+{
+ SNDFILE *srcfile, *dstfile;
+ SF_INFO srcinfo, dstinfo;
+ SF_FORMAT_INFO formatinfo;
+ char *extension;
+ void **handle;
+ int channels;
+ int srclen, dstlen;
+ float *src, *srci;
+ float *dst, *dsti;
+ double ratio = 0.0;
+ double srcrate;
+ double dstrate = 0.0;
+ struct timeval tv0, tv1;
+ double deltat;
+ int numformats;
+ int pos, bufferpos, outcount;
+ int i, c;
+
+ if (argc != 5)
+ usage(argv[0]);
+
+ if (!strcmp(argv[1], "-by")) {
+ ratio = atof(argv[2]);
+ if (ratio <= 0.0) {
+ fprintf(stderr, "Ratio of %f is illegal\n", ratio);
+ usage(argv[0]);
+ }
+ }
+ else if (!strcmp(argv[1], "-to")) {
+ dstrate = atof(argv[2]);
+ if (dstrate < 10.0 || dstrate > 100000.0) {
+ fprintf(stderr, "Sample rate of %f is illegal\n", dstrate);
+ usage(argv[0]);
+ }
+ }
+ else
+ usage(argv[0]);
+
+ memset(&srcinfo, 0, sizeof(srcinfo));
+ memset(&dstinfo, 0, sizeof(dstinfo));
+ srcfile = sf_open(argv[3], SFM_READ, &srcinfo);
+ if (!srcfile) {
+ fprintf(stderr, "%s", sf_strerror(NULL));
+ exit(-1);
+ }
+
+ srcrate = srcinfo.samplerate;
+ if (dstrate == 0.0)
+ dstrate = srcrate * ratio;
+ else
+ ratio = dstrate / srcrate;
+
+ channels = srcinfo.channels;
+
+ /* figure out format of destination file */
+
+ extension = strstr(argv[4], ".");
+ if (extension) {
+ extension++;
+ sf_command(NULL, SFC_GET_FORMAT_MAJOR_COUNT,
+ &numformats, sizeof(numformats));
+ for(i=0; i<numformats; i++) {
+ memset(&formatinfo, 0, sizeof(formatinfo));
+ formatinfo.format = i;
+ sf_command(NULL, SFC_GET_FORMAT_MAJOR,
+ &formatinfo, sizeof(formatinfo));
+ if (!strcmp(formatinfo.extension, extension)) {
+ printf("Using %s for output format.\n", formatinfo.name);
+ dstinfo.format = formatinfo.format |
+ (srcinfo.format & SF_FORMAT_SUBMASK);
+ break;
+ }
+ }
+ }
+
+ if (!dstinfo.format) {
+ if (extension)
+ printf("Warning: output format (%s) not recognized, "
+ "using same as input format.\n",
+ extension);
+ dstinfo.format = srcinfo.format;
+ }
+
+ dstinfo.samplerate = (int)(dstrate + 0.5);
+ dstinfo.channels = channels;
+
+ dstfile = sf_open(argv[4], SFM_WRITE, &dstinfo);
+ if (!dstfile) {
+ fprintf(stderr, "%s", sf_strerror(NULL));
+ exit(-1);
+ }
+
+ printf("Source: %s (%d frames, %.2f Hz)\n",
+ argv[3], (int)srcinfo.frames, srcrate);
+ printf("Destination: %s (%.2f Hz, ratio=%.5f)\n", argv[4],
+ dstrate, ratio);
+
+ srclen = 4096;
+ dstlen = (srclen * ratio + 1000);
+ srci = (float *)malloc(srclen * channels * sizeof(float));
+ dsti = (float *)malloc(dstlen * channels * sizeof(float));
+ src = (float *)malloc(srclen * sizeof(float));
+ dst = (float *)malloc(dstlen * sizeof(float));
+
+ handle = (void **)malloc(channels * sizeof(void *));
+ for(c=0; c<channels; c++)
+ handle[c] = resample_open(1, ratio, ratio);
+
+ gettimeofday(&tv0, NULL);
+
+ pos = 0;
+ bufferpos = 0;
+ outcount = 0;
+ while(pos < srcinfo.frames) {
+ int block = MIN(srclen-bufferpos, srcinfo.frames-pos);
+ int lastFlag = (pos+block == srcinfo.frames);
+ int inUsed, inUsed2=0, out=0, out2=0;
+
+ sf_readf_float(srcfile, &srci[bufferpos*channels], block);
+ block += bufferpos;
+
+ for(c=0; c<channels; c++) {
+ for(i=0; i<block; i++)
+ src[i] = srci[i*channels+c];
+
+ inUsed = 0;
+ out = resample_process(handle[c], ratio, src, block, lastFlag,
+ &inUsed, dst, dstlen);
+ if (c==0) {
+ inUsed2 = inUsed;
+ out2 = out;
+ }
+ else {
+ if (inUsed2 != inUsed || out2 != out) {
+ fprintf(stderr, "Fatal error: channels out of sync!\n");
+ exit(-1);
+ }
+ }
+
+ for(i=0; i<out; i++)
+ {
+ if(dst[i] <= -1)
+ dsti[i*channels+c] = -1;
+ else if(dst[i] >= 1)
+ dsti[i*channels+c] = 1;
+ else
+ dsti[i*channels+c] = dst[i];
+ }
+ }
+
+ sf_writef_float(dstfile, dsti, out);
+
+ bufferpos = block - inUsed;
+ for(i=0; i<bufferpos*channels; i++)
+ srci[i] = srci[i+(inUsed*channels)];
+ pos += inUsed;
+ outcount += out;
+ }
+
+ sf_close(srcfile);
+ sf_close(dstfile);
+
+ gettimeofday(&tv1, NULL);
+ deltat =
+ (tv1.tv_sec + tv1.tv_usec * 0.000001) -
+ (tv0.tv_sec + tv0.tv_usec * 0.000001);
+
+ printf("Elapsed time: %.3f seconds\n", deltat);
+ printf("%d frames written to output file\n", outcount);
+
+ free(src);
+ free(srci);
+ free(dst);
+ free(dsti);
+
+ exit(0);
+}
diff --git a/trunk/main/libresample/tests/testresample.c b/trunk/main/libresample/tests/testresample.c
new file mode 100644
index 000000000..a59aa8bfd
--- /dev/null
+++ b/trunk/main/libresample/tests/testresample.c
@@ -0,0 +1,182 @@
+/**********************************************************************
+
+ testresample.c
+
+ Real-time library interface by Dominic Mazzoni
+
+ Based on resample-1.7:
+ http://www-ccrma.stanford.edu/~jos/resample/
+
+ License: LGPL - see the file LICENSE.txt for more information
+
+**********************************************************************/
+
+#include "../include/libresample.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#define MIN(A, B) (A) < (B)? (A) : (B)
+
+void runtest(int srclen, double freq, double factor,
+ int srcblocksize, int dstblocksize)
+{
+ int expectedlen = (int)(srclen * factor);
+ int dstlen = expectedlen + 1000;
+ float *src = (float *)malloc((srclen+100) * sizeof(float));
+ float *dst = (float *)malloc((dstlen+100) * sizeof(float));
+ void *handle;
+ double sum, sumsq, err, rmserr;
+ int i, out, o, srcused, errcount, rangecount;
+ int statlen, srcpos, lendiff;
+ int fwidth;
+
+ printf("-- srclen: %d sin freq: %.1f factor: %.3f srcblk: %d dstblk: %d\n",
+ srclen, freq, factor, srcblocksize, dstblocksize);
+
+ for(i=0; i<srclen; i++)
+ src[i] = sin(i/freq);
+ for(i=srclen; i<srclen+100; i++)
+ src[i] = -99.0;
+
+ for(i=0; i<dstlen+100; i++)
+ dst[i] = -99.0;
+
+ handle = resample_open(1, factor, factor);
+ fwidth = resample_get_filter_width(handle);
+ out = 0;
+ srcpos = 0;
+ for(;;) {
+ int srcBlock = MIN(srclen-srcpos, srcblocksize);
+ int lastFlag = (srcBlock == srclen-srcpos);
+
+ o = resample_process(handle, factor,
+ &src[srcpos], srcBlock,
+ lastFlag, &srcused,
+ &dst[out], MIN(dstlen-out, dstblocksize));
+ srcpos += srcused;
+ if (o >= 0)
+ out += o;
+ if (o < 0 || (o == 0 && srcpos == srclen))
+ break;
+ }
+ resample_close(handle);
+
+ if (o < 0) {
+ printf("Error: resample_process returned an error: %d\n", o);
+ }
+
+ if (out <= 0) {
+ printf("Error: resample_process returned %d samples\n", out);
+ free(src);
+ free(dst);
+ return;
+ }
+
+ lendiff = abs(out - expectedlen);
+ if (lendiff > (int)(2*factor + 1.0)) {
+ printf(" Expected ~%d, got %d samples out\n",
+ expectedlen, out);
+ }
+
+ sum = 0.0;
+ sumsq = 0.0;
+ errcount = 0.0;
+
+ /* Don't compute statistics on all output values; the last few
+ are guaranteed to be off because it's based on far less
+ interpolation. */
+ statlen = out - fwidth;
+
+ for(i=0; i<statlen; i++) {
+ double diff = sin((i/freq)/factor) - dst[i];
+ if (fabs(diff) > 0.05) {
+ if (errcount == 0)
+ printf(" First error at i=%d: expected %.3f, got %.3f\n",
+ i, sin((i/freq)/factor), dst[i]);
+ errcount++;
+ }
+ sum += fabs(diff);
+ sumsq += diff * diff;
+ }
+
+ rangecount = 0;
+ for(i=0; i<statlen; i++) {
+ if (dst[i] < -1.01 || dst[i] > 1.01) {
+ if (rangecount == 0)
+ printf(" Error at i=%d: value is %.3f\n", i, dst[i]);
+ rangecount++;
+ }
+ }
+ if (rangecount > 1)
+ printf(" At least %d samples were out of range\n", rangecount);
+
+ if (errcount > 0) {
+ i = out - 1;
+ printf(" i=%d: expected %.3f, got %.3f\n",
+ i, sin((i/freq)/factor), dst[i]);
+ printf(" At least %d samples had significant error.\n", errcount);
+ }
+ err = sum / statlen;
+ rmserr = sqrt(sumsq / statlen);
+ printf(" Out: %d samples Avg err: %f RMS err: %f\n", out, err, rmserr);
+ free(src);
+ free(dst);
+}
+
+int main(int argc, char **argv)
+{
+ int i, srclen, dstlen, ifreq;
+ double factor;
+
+ printf("\n*** Vary source block size*** \n\n");
+ srclen = 10000;
+ ifreq = 100;
+ for(i=0; i<20; i++) {
+ factor = ((rand() % 16) + 1) / 4.0;
+ dstlen = (int)(srclen * factor + 10);
+ runtest(srclen, (double)ifreq, factor, 64, dstlen);
+ runtest(srclen, (double)ifreq, factor, 32, dstlen);
+ runtest(srclen, (double)ifreq, factor, 8, dstlen);
+ runtest(srclen, (double)ifreq, factor, 2, dstlen);
+ runtest(srclen, (double)ifreq, factor, srclen, dstlen);
+ }
+
+ printf("\n*** Vary dest block size ***\n\n");
+ srclen = 10000;
+ ifreq = 100;
+ for(i=0; i<20; i++) {
+ factor = ((rand() % 16) + 1) / 4.0;
+ runtest(srclen, (double)ifreq, factor, srclen, 32);
+ dstlen = (int)(srclen * factor + 10);
+ runtest(srclen, (double)ifreq, factor, srclen, dstlen);
+ }
+
+ printf("\n*** Resample factor 1.0, testing different srclen ***\n\n");
+ ifreq = 40;
+ for(i=0; i<100; i++) {
+ srclen = (rand() % 30000) + 10;
+ dstlen = (int)(srclen + 10);
+ runtest(srclen, (double)ifreq, 1.0, srclen, dstlen);
+ }
+
+ printf("\n*** Resample factor 1.0, testing different sin freq ***\n\n");
+ srclen = 10000;
+ for(i=0; i<100; i++) {
+ ifreq = ((int)rand() % 10000) + 1;
+ dstlen = (int)(srclen * 10);
+ runtest(srclen, (double)ifreq, 1.0, srclen, dstlen);
+ }
+
+ printf("\n*** Resample with different factors ***\n\n");
+ srclen = 10000;
+ ifreq = 100;
+ for(i=0; i<100; i++) {
+ factor = ((rand() % 64) + 1) / 4.0;
+ dstlen = (int)(srclen * factor + 10);
+ runtest(srclen, (double)ifreq, factor, srclen, dstlen);
+ }
+
+ return 0;
+}
diff --git a/trunk/main/libresample/win/libresample.dsp b/trunk/main/libresample/win/libresample.dsp
new file mode 100644
index 000000000..4ebb51e46
--- /dev/null
+++ b/trunk/main/libresample/win/libresample.dsp
@@ -0,0 +1,116 @@
+# Microsoft Developer Studio Project File - Name="libresample" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Static Library" 0x0104
+
+CFG=libresample - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "libresample.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "libresample.mak" CFG="libresample - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "libresample - Win32 Release" (based on "Win32 (x86) Static Library")
+!MESSAGE "libresample - Win32 Debug" (based on "Win32 (x86) Static Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "libresample - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo /out:"libresample.lib"
+
+!ELSEIF "$(CFG)" == "libresample - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo /out:"libresampled.lib"
+
+!ENDIF
+
+# Begin Target
+
+# Name "libresample - Win32 Release"
+# Name "libresample - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=..\src\filterkit.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\resample.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\resamplesubs.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=..\src\filterkit.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\libresample.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\resample_defs.h
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/trunk/main/libresample/win/libresample.vcproj b/trunk/main/libresample/win/libresample.vcproj
new file mode 100644
index 000000000..c23845f47
--- /dev/null
+++ b/trunk/main/libresample/win/libresample.vcproj
@@ -0,0 +1,192 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="7.10"
+ Name="libresample"
+ SccProjectName=""
+ SccLocalPath="">
+ <Platforms>
+ <Platform
+ Name="Win32"/>
+ </Platforms>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory=".\Debug"
+ IntermediateDirectory=".\Debug"
+ ConfigurationType="4"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="FALSE"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions="WIN32;_DEBUG;_LIB"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="1"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderFile=".\Debug/libresample.pch"
+ AssemblerListingLocation=".\Debug/"
+ ObjectFile=".\Debug/"
+ ProgramDataBaseFileName=".\Debug/"
+ WarningLevel="3"
+ SuppressStartupBanner="TRUE"
+ DebugInformationFormat="4"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="libresampled.lib"
+ SuppressStartupBanner="TRUE"/>
+ <Tool
+ Name="VCMIDLTool"/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="_DEBUG"
+ Culture="1033"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory=".\Release"
+ IntermediateDirectory=".\Release"
+ ConfigurationType="4"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="FALSE"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/O2"
+ Optimization="2"
+ InlineFunctionExpansion="1"
+ OptimizeForProcessor="3"
+ PreprocessorDefinitions="WIN32;NDEBUG;_LIB"
+ StringPooling="TRUE"
+ RuntimeLibrary="0"
+ EnableFunctionLevelLinking="TRUE"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderFile=".\Release/libresample.pch"
+ AssemblerListingLocation=".\Release/"
+ ObjectFile=".\Release/"
+ ProgramDataBaseFileName=".\Release/"
+ WarningLevel="3"
+ SuppressStartupBanner="TRUE"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="libresample.lib"
+ SuppressStartupBanner="TRUE"/>
+ <Tool
+ Name="VCMIDLTool"/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1033"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat">
+ <File
+ RelativePath="..\src\filterkit.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions=""
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\src\resample.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions=""
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\src\resamplesubs.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions=""
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl">
+ <File
+ RelativePath="..\src\filterkit.h">
+ </File>
+ <File
+ RelativePath="..\include\libresample.h">
+ </File>
+ <File
+ RelativePath="..\src\resample_defs.h">
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/trunk/main/loader.c b/trunk/main/loader.c
new file mode 100644
index 000000000..1d0797720
--- /dev/null
+++ b/trunk/main/loader.c
@@ -0,0 +1,1006 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ * Kevin P. Fleming <kpfleming@digium.com>
+ * Luigi Rizzo <rizzo@icir.org>
+ *
+ * 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 Module Loader
+ * \author Mark Spencer <markster@digium.com>
+ * \author Kevin P. Fleming <kpfleming@digium.com>
+ * \author Luigi Rizzo <rizzo@icir.org>
+ * - See ModMngMnt
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/_private.h"
+#include "asterisk/paths.h" /* use ast_config_AST_MODULE_DIR */
+#include <dirent.h>
+
+#include "asterisk/linkedlists.h"
+#include "asterisk/module.h"
+#include "asterisk/config.h"
+#include "asterisk/channel.h"
+#include "asterisk/term.h"
+#include "asterisk/manager.h"
+#include "asterisk/cdr.h"
+#include "asterisk/enum.h"
+#include "asterisk/rtp.h"
+#include "asterisk/http.h"
+#include "asterisk/lock.h"
+
+#ifdef DLFCNCOMPAT
+#include "asterisk/dlfcn-compat.h"
+#else
+#include <dlfcn.h>
+#endif
+
+#include "asterisk/md5.h"
+#include "asterisk/utils.h"
+
+#ifndef RTLD_NOW
+#define RTLD_NOW 0
+#endif
+
+#ifndef RTLD_LOCAL
+#define RTLD_LOCAL 0
+#endif
+
+struct ast_module_user {
+ struct ast_channel *chan;
+ AST_LIST_ENTRY(ast_module_user) entry;
+};
+
+AST_LIST_HEAD(module_user_list, ast_module_user);
+
+static unsigned char expected_key[] =
+{ 0x87, 0x76, 0x79, 0x35, 0x23, 0xea, 0x3a, 0xd3,
+ 0x25, 0x2a, 0xbb, 0x35, 0x87, 0xe4, 0x22, 0x24 };
+
+static char buildopt_sum[33] = AST_BUILDOPT_SUM;
+
+static unsigned int embedding = 1; /* we always start out by registering embedded modules,
+ since they are here before we dlopen() any
+ */
+
+struct ast_module {
+ const struct ast_module_info *info;
+ void *lib; /* the shared lib, or NULL if embedded */
+ int usecount; /* the number of 'users' currently in this module */
+ struct module_user_list users; /* the list of users in the module */
+ struct {
+ unsigned int running:1;
+ unsigned int declined:1;
+ } flags;
+ AST_LIST_ENTRY(ast_module) entry;
+ char resource[0];
+};
+
+static AST_LIST_HEAD_STATIC(module_list, ast_module);
+
+/*
+ * module_list is cleared by its constructor possibly after
+ * we start accumulating embedded modules, so we need to
+ * use another list (without the lock) to accumulate them.
+ * Then we update the main list when embedding is done.
+ */
+static struct module_list embedded_module_list;
+
+struct loadupdate {
+ int (*updater)(void);
+ AST_LIST_ENTRY(loadupdate) entry;
+};
+
+static AST_LIST_HEAD_STATIC(updaters, loadupdate);
+
+AST_MUTEX_DEFINE_STATIC(reloadlock);
+
+/* when dynamic modules are being loaded, ast_module_register() will
+ need to know what filename the module was loaded from while it
+ is being registered
+*/
+struct ast_module *resource_being_loaded;
+
+/* XXX: should we check for duplicate resource names here? */
+
+void ast_module_register(const struct ast_module_info *info)
+{
+ struct ast_module *mod;
+
+ if (embedding) {
+ if (!(mod = ast_calloc(1, sizeof(*mod) + strlen(info->name) + 1)))
+ return;
+ strcpy(mod->resource, info->name);
+ } else {
+ mod = resource_being_loaded;
+ }
+
+ mod->info = info;
+ AST_LIST_HEAD_INIT(&mod->users);
+
+ /* during startup, before the loader has been initialized,
+ there are no threads, so there is no need to take the lock
+ on this list to manipulate it. it is also possible that it
+ might be unsafe to use the list lock at that point... so
+ let's avoid it altogether
+ */
+ if (embedding) {
+ AST_LIST_INSERT_TAIL(&embedded_module_list, mod, entry);
+ } else {
+ AST_LIST_LOCK(&module_list);
+ /* it is paramount that the new entry be placed at the tail of
+ the list, otherwise the code that uses dlopen() to load
+ dynamic modules won't be able to find out if the module it
+ just opened was registered or failed to load
+ */
+ AST_LIST_INSERT_TAIL(&module_list, mod, entry);
+ AST_LIST_UNLOCK(&module_list);
+ }
+
+ /* give the module a copy of its own handle, for later use in registrations and the like */
+ *((struct ast_module **) &(info->self)) = mod;
+}
+
+void ast_module_unregister(const struct ast_module_info *info)
+{
+ struct ast_module *mod = NULL;
+
+ /* it is assumed that the users list in the module structure
+ will already be empty, or we cannot have gotten to this
+ point
+ */
+ AST_LIST_LOCK(&module_list);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&module_list, mod, entry) {
+ if (mod->info == info) {
+ AST_LIST_REMOVE_CURRENT(entry);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ AST_LIST_UNLOCK(&module_list);
+
+ if (mod) {
+ AST_LIST_HEAD_DESTROY(&mod->users);
+ ast_free(mod);
+ }
+}
+
+struct ast_module_user *__ast_module_user_add(struct ast_module *mod,
+ struct ast_channel *chan)
+{
+ struct ast_module_user *u = ast_calloc(1, sizeof(*u));
+
+ if (!u)
+ return NULL;
+
+ u->chan = chan;
+
+ AST_LIST_LOCK(&mod->users);
+ AST_LIST_INSERT_HEAD(&mod->users, u, entry);
+ AST_LIST_UNLOCK(&mod->users);
+
+ ast_atomic_fetchadd_int(&mod->usecount, +1);
+
+ ast_update_use_count();
+
+ return u;
+}
+
+void __ast_module_user_remove(struct ast_module *mod, struct ast_module_user *u)
+{
+ AST_LIST_LOCK(&mod->users);
+ AST_LIST_REMOVE(&mod->users, u, entry);
+ AST_LIST_UNLOCK(&mod->users);
+ ast_atomic_fetchadd_int(&mod->usecount, -1);
+ ast_free(u);
+
+ ast_update_use_count();
+}
+
+void __ast_module_user_hangup_all(struct ast_module *mod)
+{
+ struct ast_module_user *u;
+
+ AST_LIST_LOCK(&mod->users);
+ while ((u = AST_LIST_REMOVE_HEAD(&mod->users, entry))) {
+ ast_softhangup(u->chan, AST_SOFTHANGUP_APPUNLOAD);
+ ast_atomic_fetchadd_int(&mod->usecount, -1);
+ ast_free(u);
+ }
+ AST_LIST_UNLOCK(&mod->users);
+
+ ast_update_use_count();
+}
+
+/*! \note
+ * In addition to modules, the reload command handles some extra keywords
+ * which are listed here together with the corresponding handlers.
+ * This table is also used by the command completion code.
+ */
+static struct reload_classes {
+ const char *name;
+ int (*reload_fn)(void);
+} reload_classes[] = { /* list in alpha order, longest match first for cli completion */
+ { "cdr", ast_cdr_engine_reload },
+ { "dnsmgr", dnsmgr_reload },
+ { "extconfig", read_config_maps },
+ { "enum", ast_enum_reload },
+ { "manager", reload_manager },
+ { "rtp", ast_rtp_reload },
+ { "http", ast_http_reload },
+ { "logger", logger_reload },
+ { NULL, NULL }
+};
+
+static int printdigest(const unsigned char *d)
+{
+ int x, pos;
+ char buf[256]; /* large enough so we don't have to worry */
+
+ for (pos = 0, x = 0; x < 16; x++)
+ pos += sprintf(buf + pos, " %02x", *d++);
+
+ ast_debug(1, "Unexpected signature:%s\n", buf);
+
+ return 0;
+}
+
+static int key_matches(const unsigned char *key1, const unsigned char *key2)
+{
+ int x;
+
+ for (x = 0; x < 16; x++) {
+ if (key1[x] != key2[x])
+ return 0;
+ }
+
+ return 1;
+}
+
+static int verify_key(const unsigned char *key)
+{
+ struct MD5Context c;
+ unsigned char digest[16];
+
+ MD5Init(&c);
+ MD5Update(&c, key, strlen((char *)key));
+ MD5Final(digest, &c);
+
+ if (key_matches(expected_key, digest))
+ return 0;
+
+ printdigest(digest);
+
+ return -1;
+}
+
+static int resource_name_match(const char *name1_in, const char *name2_in)
+{
+ char *name1 = (char *) name1_in;
+ char *name2 = (char *) name2_in;
+
+ /* trim off any .so extensions */
+ if (!strcasecmp(name1 + strlen(name1) - 3, ".so")) {
+ name1 = ast_strdupa(name1);
+ name1[strlen(name1) - 3] = '\0';
+ }
+ if (!strcasecmp(name2 + strlen(name2) - 3, ".so")) {
+ name2 = ast_strdupa(name2);
+ name2[strlen(name2) - 3] = '\0';
+ }
+
+ return strcasecmp(name1, name2);
+}
+
+static struct ast_module *find_resource(const char *resource, int do_lock)
+{
+ struct ast_module *cur;
+
+ if (do_lock)
+ AST_LIST_LOCK(&module_list);
+
+ AST_LIST_TRAVERSE(&module_list, cur, entry) {
+ if (!resource_name_match(resource, cur->resource))
+ break;
+ }
+
+ if (do_lock)
+ AST_LIST_UNLOCK(&module_list);
+
+ return cur;
+}
+
+#ifdef LOADABLE_MODULES
+static void unload_dynamic_module(struct ast_module *mod)
+{
+ void *lib = mod->lib;
+
+ /* WARNING: the structure pointed to by mod is going to
+ disappear when this operation succeeds, so we can't
+ dereference it */
+
+ if (lib)
+ while (!dlclose(lib));
+}
+
+static struct ast_module *load_dynamic_module(const char *resource_in, unsigned int global_symbols_only)
+{
+ char fn[PATH_MAX] = "";
+ void *lib = NULL;
+ struct ast_module *mod;
+ unsigned int wants_global;
+ int space; /* room needed for the descriptor */
+ int missing_so = 0;
+
+ space = sizeof(*resource_being_loaded) + strlen(resource_in) + 1;
+ if (strcasecmp(resource_in + strlen(resource_in) - 3, ".so")) {
+ missing_so = 1;
+ space += 3; /* room for the extra ".so" */
+ }
+
+ snprintf(fn, sizeof(fn), "%s/%s%s", ast_config_AST_MODULE_DIR, resource_in, missing_so ? ".so" : "");
+
+ /* make a first load of the module in 'quiet' mode... don't try to resolve
+ any symbols, and don't export any symbols. this will allow us to peek into
+ the module's info block (if available) to see what flags it has set */
+
+ resource_being_loaded = ast_calloc(1, space);
+ if (!resource_being_loaded)
+ return NULL;
+ strcpy(resource_being_loaded->resource, resource_in);
+ if (missing_so)
+ strcat(resource_being_loaded->resource, ".so");
+
+ if (!(lib = dlopen(fn, RTLD_LAZY | RTLD_LOCAL))) {
+ ast_log(LOG_WARNING, "Error loading module '%s': %s\n", resource_in, dlerror());
+ ast_free(resource_being_loaded);
+ return NULL;
+ }
+
+ /* the dlopen() succeeded, let's find out if the module
+ registered itself */
+ /* note that this will only work properly as long as
+ ast_module_register() (which is called by the module's
+ constructor) places the new module at the tail of the
+ module_list
+ */
+ if (resource_being_loaded != (mod = AST_LIST_LAST(&module_list))) {
+ ast_log(LOG_WARNING, "Module '%s' did not register itself during load\n", resource_in);
+ /* no, it did not, so close it and return */
+ while (!dlclose(lib));
+ /* note that the module's destructor will call ast_module_unregister(),
+ which will free the structure we allocated in resource_being_loaded */
+ return NULL;
+ }
+
+ wants_global = ast_test_flag(mod->info, AST_MODFLAG_GLOBAL_SYMBOLS);
+
+ /* if we are being asked only to load modules that provide global symbols,
+ and this one does not, then close it and return */
+ if (global_symbols_only && !wants_global) {
+ while (!dlclose(lib));
+ return NULL;
+ }
+
+ /* if the system supports RTLD_NOLOAD, we can just 'promote' the flags
+ on the already-opened library to what we want... if not, we have to
+ close it and start over
+ */
+#if defined(HAVE_RTLD_NOLOAD) && !defined(__Darwin__)
+ if (!dlopen(fn, RTLD_NOLOAD | (wants_global ? RTLD_LAZY | RTLD_GLOBAL : RTLD_NOW | RTLD_LOCAL))) {
+ ast_log(LOG_WARNING, "Unable to promote flags on module '%s': %s\n", resource_in, dlerror());
+ while (!dlclose(lib));
+ ast_free(resource_being_loaded);
+ return NULL;
+ }
+#else
+ while (!dlclose(lib));
+ resource_being_loaded = NULL;
+
+ /* start the load process again */
+ resource_being_loaded = ast_calloc(1, space);
+ if (!resource_being_loaded)
+ return NULL;
+ strcpy(resource_being_loaded->resource, resource_in);
+ if (missing_so)
+ strcat(resource_being_loaded->resource, ".so");
+
+ if (!(lib = dlopen(fn, wants_global ? RTLD_LAZY | RTLD_GLOBAL : RTLD_NOW | RTLD_LOCAL))) {
+ ast_log(LOG_WARNING, "Error loading module '%s': %s\n", resource_in, dlerror());
+ ast_free(resource_being_loaded);
+ return NULL;
+ }
+
+ /* since the module was successfully opened, and it registered itself
+ the previous time we did that, we're going to assume it worked this
+ time too :) */
+#endif
+
+ AST_LIST_LAST(&module_list)->lib = lib;
+ resource_being_loaded = NULL;
+
+ return AST_LIST_LAST(&module_list);
+}
+#endif
+
+void ast_module_shutdown(void)
+{
+ struct ast_module *mod;
+ AST_LIST_HEAD_NOLOCK_STATIC(local_module_list, ast_module);
+
+ /* We have to call the unload() callbacks in reverse order that the modules
+ * exist in the module list so it is the reverse order of how they were
+ * loaded. */
+
+ AST_LIST_LOCK(&module_list);
+ while ((mod = AST_LIST_REMOVE_HEAD(&module_list, entry)))
+ AST_LIST_INSERT_HEAD(&local_module_list, mod, entry);
+ AST_LIST_UNLOCK(&module_list);
+
+ while ((mod = AST_LIST_REMOVE_HEAD(&local_module_list, entry))) {
+ if (mod->info->unload)
+ mod->info->unload();
+ /* Since this should only be called when shutting down "gracefully",
+ * all channels should be down before we get to this point, meaning
+ * there will be no module users left. */
+ AST_LIST_HEAD_DESTROY(&mod->users);
+ free(mod);
+ }
+}
+
+int ast_unload_resource(const char *resource_name, enum ast_module_unload_mode force)
+{
+ struct ast_module *mod;
+ int res = -1;
+ int error = 0;
+
+ AST_LIST_LOCK(&module_list);
+
+ if (!(mod = find_resource(resource_name, 0))) {
+ AST_LIST_UNLOCK(&module_list);
+ return 0;
+ }
+
+ if (!(mod->flags.running || mod->flags.declined))
+ error = 1;
+
+ if (!error && (mod->usecount > 0)) {
+ if (force)
+ ast_log(LOG_WARNING, "Warning: Forcing removal of module '%s' with use count %d\n",
+ resource_name, mod->usecount);
+ else {
+ ast_log(LOG_WARNING, "Soft unload failed, '%s' has use count %d\n", resource_name,
+ mod->usecount);
+ error = 1;
+ }
+ }
+
+ if (!error) {
+ __ast_module_user_hangup_all(mod);
+ res = mod->info->unload();
+
+ if (res) {
+ ast_log(LOG_WARNING, "Firm unload failed for %s\n", resource_name);
+ if (force <= AST_FORCE_FIRM)
+ error = 1;
+ else
+ ast_log(LOG_WARNING, "** Dangerous **: Unloading resource anyway, at user request\n");
+ }
+ }
+
+ if (!error)
+ mod->flags.running = mod->flags.declined = 0;
+
+ AST_LIST_UNLOCK(&module_list);
+
+ if (!error && !mod->lib && mod->info && mod->info->restore_globals)
+ mod->info->restore_globals();
+
+#ifdef LOADABLE_MODULES
+ if (!error)
+ unload_dynamic_module(mod);
+#endif
+
+ if (!error)
+ ast_update_use_count();
+
+ return res;
+}
+
+char *ast_module_helper(const char *line, const char *word, int pos, int state, int rpos, int needsreload)
+{
+ struct ast_module *cur;
+ int i, which=0, l = strlen(word);
+ char *ret = NULL;
+
+ if (pos != rpos)
+ return NULL;
+
+ AST_LIST_LOCK(&module_list);
+ AST_LIST_TRAVERSE(&module_list, cur, entry) {
+ if (!strncasecmp(word, cur->resource, l) &&
+ (cur->info->reload || !needsreload) &&
+ ++which > state) {
+ ret = ast_strdup(cur->resource);
+ break;
+ }
+ }
+ AST_LIST_UNLOCK(&module_list);
+
+ if (!ret) {
+ for (i=0; !ret && reload_classes[i].name; i++) {
+ if (!strncasecmp(word, reload_classes[i].name, l) && ++which > state)
+ ret = ast_strdup(reload_classes[i].name);
+ }
+ }
+
+ return ret;
+}
+
+int ast_module_reload(const char *name)
+{
+ struct ast_module *cur;
+ int res = 0; /* return value. 0 = not found, others, see below */
+ int i;
+
+ if (ast_mutex_trylock(&reloadlock)) {
+ ast_verbose("The previous reload command didn't finish yet\n");
+ return -1; /* reload already in progress */
+ }
+ ast_lastreloadtime = ast_tvnow();
+
+ /* Call "predefined" reload here first */
+ for (i = 0; reload_classes[i].name; i++) {
+ if (!name || !strcasecmp(name, reload_classes[i].name)) {
+ reload_classes[i].reload_fn(); /* XXX should check error ? */
+ res = 2; /* found and reloaded */
+ }
+ }
+
+ if (name && res) {
+ ast_mutex_unlock(&reloadlock);
+ return res;
+ }
+
+ AST_LIST_LOCK(&module_list);
+ AST_LIST_TRAVERSE(&module_list, cur, entry) {
+ const struct ast_module_info *info = cur->info;
+
+ if (name && resource_name_match(name, cur->resource))
+ continue;
+
+ if (!(cur->flags.running || cur->flags.declined))
+ continue;
+
+ if (!info->reload) { /* cannot be reloaded */
+ if (res < 1) /* store result if possible */
+ res = 1; /* 1 = no reload() method */
+ continue;
+ }
+
+ res = 2;
+ ast_verb(3, "Reloading module '%s' (%s)\n", cur->resource, info->description);
+ info->reload();
+ }
+ AST_LIST_UNLOCK(&module_list);
+
+ ast_mutex_unlock(&reloadlock);
+
+ return res;
+}
+
+static unsigned int inspect_module(const struct ast_module *mod)
+{
+ if (!mod->info->description) {
+ ast_log(LOG_WARNING, "Module '%s' does not provide a description.\n", mod->resource);
+ return 1;
+ }
+
+ if (!mod->info->key) {
+ ast_log(LOG_WARNING, "Module '%s' does not provide a license key.\n", mod->resource);
+ return 1;
+ }
+
+ if (verify_key((unsigned char *) mod->info->key)) {
+ ast_log(LOG_WARNING, "Module '%s' did not provide a valid license key.\n", mod->resource);
+ return 1;
+ }
+
+ if (!ast_strlen_zero(mod->info->buildopt_sum) &&
+ strcmp(buildopt_sum, mod->info->buildopt_sum)) {
+ ast_log(LOG_WARNING, "Module '%s' was not compiled with the same compile-time options as this version of Asterisk.\n", mod->resource);
+ ast_log(LOG_WARNING, "Module '%s' will not be initialized as it may cause instability.\n", mod->resource);
+ return 1;
+ }
+
+ return 0;
+}
+
+static enum ast_module_load_result load_resource(const char *resource_name, unsigned int global_symbols_only)
+{
+ struct ast_module *mod;
+ enum ast_module_load_result res = AST_MODULE_LOAD_SUCCESS;
+ char tmp[256];
+
+ if ((mod = find_resource(resource_name, 0))) {
+ if (mod->flags.running) {
+ ast_log(LOG_WARNING, "Module '%s' already exists.\n", resource_name);
+ return AST_MODULE_LOAD_DECLINE;
+ }
+ if (global_symbols_only && !ast_test_flag(mod->info, AST_MODFLAG_GLOBAL_SYMBOLS))
+ return AST_MODULE_LOAD_SKIP;
+ } else {
+#ifdef LOADABLE_MODULES
+ if (!(mod = load_dynamic_module(resource_name, global_symbols_only))) {
+ /* don't generate a warning message during load_modules() */
+ if (!global_symbols_only) {
+ ast_log(LOG_WARNING, "Module '%s' could not be loaded.\n", resource_name);
+ return AST_MODULE_LOAD_DECLINE;
+ } else {
+ return AST_MODULE_LOAD_SKIP;
+ }
+ }
+#else
+ ast_log(LOG_WARNING, "Module '%s' could not be loaded.\n", resource_name);
+ return AST_MODULE_LOAD_DECLINE;
+#endif
+ }
+
+ if (inspect_module(mod)) {
+ ast_log(LOG_WARNING, "Module '%s' could not be loaded.\n", resource_name);
+#ifdef LOADABLE_MODULES
+ unload_dynamic_module(mod);
+#endif
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ if (!mod->lib && mod->info->backup_globals()) {
+ ast_log(LOG_WARNING, "Module '%s' was unable to backup its global data.\n", resource_name);
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ mod->flags.declined = 0;
+
+ if (mod->info->load)
+ res = mod->info->load();
+
+ switch (res) {
+ case AST_MODULE_LOAD_SUCCESS:
+ if (!ast_fully_booted) {
+ ast_verb(1, "%s => (%s)\n", resource_name, term_color(tmp, mod->info->description, COLOR_BROWN, COLOR_BLACK, sizeof(tmp)));
+ if (ast_opt_console && !option_verbose)
+ ast_verbose( ".");
+ } else {
+ ast_verb(1, "Loaded %s => (%s)\n", resource_name, mod->info->description);
+ }
+
+ mod->flags.running = 1;
+
+ ast_update_use_count();
+ break;
+ case AST_MODULE_LOAD_DECLINE:
+ mod->flags.declined = 1;
+ break;
+ case AST_MODULE_LOAD_FAILURE:
+ break;
+ case AST_MODULE_LOAD_SKIP:
+ /* modules should never return this value */
+ break;
+ }
+
+ return res;
+}
+
+int ast_load_resource(const char *resource_name)
+{
+ AST_LIST_LOCK(&module_list);
+ load_resource(resource_name, 0);
+ AST_LIST_UNLOCK(&module_list);
+
+ return 0;
+}
+
+struct load_order_entry {
+ char *resource;
+ AST_LIST_ENTRY(load_order_entry) entry;
+};
+
+AST_LIST_HEAD_NOLOCK(load_order, load_order_entry);
+
+static struct load_order_entry *add_to_load_order(const char *resource, struct load_order *load_order)
+{
+ struct load_order_entry *order;
+
+ AST_LIST_TRAVERSE(load_order, order, entry) {
+ if (!resource_name_match(order->resource, resource))
+ return NULL;
+ }
+
+ if (!(order = ast_calloc(1, sizeof(*order))))
+ return NULL;
+
+ order->resource = ast_strdup(resource);
+ AST_LIST_INSERT_TAIL(load_order, order, entry);
+
+ return order;
+}
+int load_modules(unsigned int preload_only)
+{
+ struct ast_config *cfg;
+ struct ast_module *mod;
+ struct load_order_entry *order;
+ struct ast_variable *v;
+ unsigned int load_count;
+ struct load_order load_order;
+ int res = 0;
+ struct ast_flags config_flags = { 0 };
+ int modulecount = 0;
+#ifdef LOADABLE_MODULES
+ struct dirent *dirent;
+ DIR *dir;
+#endif
+
+ /* all embedded modules have registered themselves by now */
+ embedding = 0;
+
+ ast_verb(1, "Asterisk Dynamic Loader Starting:\n");
+
+ AST_LIST_HEAD_INIT_NOLOCK(&load_order);
+
+ AST_LIST_LOCK(&module_list);
+
+ if (embedded_module_list.first) {
+ module_list.first = embedded_module_list.first;
+ module_list.last = embedded_module_list.last;
+ embedded_module_list.first = NULL;
+ }
+
+ if (!(cfg = ast_config_load(AST_MODULE_CONFIG, config_flags))) {
+ ast_log(LOG_WARNING, "No '%s' found, no modules will be loaded.\n", AST_MODULE_CONFIG);
+ goto done;
+ }
+
+ /* first, find all the modules we have been explicitly requested to load */
+ for (v = ast_variable_browse(cfg, "modules"); v; v = v->next) {
+ if (!strcasecmp(v->name, preload_only ? "preload" : "load"))
+ add_to_load_order(v->value, &load_order);
+ }
+
+ /* check if 'autoload' is on */
+ if (!preload_only && ast_true(ast_variable_retrieve(cfg, "modules", "autoload"))) {
+ /* if so, first add all the embedded modules that are not already running to the load order */
+ AST_LIST_TRAVERSE(&module_list, mod, entry) {
+ /* if it's not embedded, skip it */
+ if (mod->lib)
+ continue;
+
+ if (mod->flags.running)
+ continue;
+
+ order = add_to_load_order(mod->resource, &load_order);
+ }
+
+#ifdef LOADABLE_MODULES
+ /* if we are allowed to load dynamic modules, scan the directory for
+ for all available modules and add them as well */
+ if ((dir = opendir(ast_config_AST_MODULE_DIR))) {
+ while ((dirent = readdir(dir))) {
+ int ld = strlen(dirent->d_name);
+
+ /* Must end in .so to load it. */
+
+ if (ld < 4)
+ continue;
+
+ if (strcasecmp(dirent->d_name + ld - 3, ".so"))
+ continue;
+
+ /* if there is already a module by this name in the module_list,
+ skip this file */
+ if (find_resource(dirent->d_name, 0))
+ continue;
+
+ add_to_load_order(dirent->d_name, &load_order);
+ }
+
+ closedir(dir);
+ } else {
+ if (!ast_opt_quiet)
+ ast_log(LOG_WARNING, "Unable to open modules directory '%s'.\n",
+ ast_config_AST_MODULE_DIR);
+ }
+#endif
+ }
+
+ /* now scan the config for any modules we are prohibited from loading and
+ remove them from the load order */
+ for (v = ast_variable_browse(cfg, "modules"); v; v = v->next) {
+ if (strcasecmp(v->name, "noload"))
+ continue;
+
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&load_order, order, entry) {
+ if (!resource_name_match(order->resource, v->value)) {
+ AST_LIST_REMOVE_CURRENT(entry);
+ ast_free(order->resource);
+ ast_free(order);
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ }
+
+ /* we are done with the config now, all the information we need is in the
+ load_order list */
+ ast_config_destroy(cfg);
+
+ load_count = 0;
+ AST_LIST_TRAVERSE(&load_order, order, entry)
+ load_count++;
+
+ if (load_count)
+ ast_log(LOG_NOTICE, "%d modules will be loaded.\n", load_count);
+
+ /* first, load only modules that provide global symbols */
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&load_order, order, entry) {
+ switch (load_resource(order->resource, 1)) {
+ case AST_MODULE_LOAD_SUCCESS:
+ modulecount++;
+ case AST_MODULE_LOAD_DECLINE:
+ AST_LIST_REMOVE_CURRENT(entry);
+ ast_free(order->resource);
+ ast_free(order);
+ break;
+ case AST_MODULE_LOAD_FAILURE:
+ res = -1;
+ goto done;
+ case AST_MODULE_LOAD_SKIP:
+ /* try again later */
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+
+ /* now load everything else */
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&load_order, order, entry) {
+ switch (load_resource(order->resource, 0)) {
+ case AST_MODULE_LOAD_SUCCESS:
+ modulecount++;
+ case AST_MODULE_LOAD_DECLINE:
+ AST_LIST_REMOVE_CURRENT(entry);
+ ast_free(order->resource);
+ ast_free(order);
+ break;
+ case AST_MODULE_LOAD_FAILURE:
+ res = -1;
+ goto done;
+ case AST_MODULE_LOAD_SKIP:
+ /* should not happen */
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+
+done:
+ while ((order = AST_LIST_REMOVE_HEAD(&load_order, entry))) {
+ ast_free(order->resource);
+ ast_free(order);
+ }
+
+ AST_LIST_UNLOCK(&module_list);
+
+ /* Tell manager clients that are aggressive at logging in that we're done
+ loading modules. If there's a DNS problem in chan_sip, we might not
+ even reach this */
+ manager_event(EVENT_FLAG_SYSTEM, "ModuleLoadReport", "ModuleLoadStatus: Done\r\nModuleSelection: %s\r\nModuleCount: %d\r\n", preload_only ? "Preload" : "All", modulecount);
+
+ return res;
+}
+
+void ast_update_use_count(void)
+{
+ /* Notify any module monitors that the use count for a
+ resource has changed */
+ struct loadupdate *m;
+
+ AST_LIST_LOCK(&module_list);
+ AST_LIST_TRAVERSE(&updaters, m, entry)
+ m->updater();
+ AST_LIST_UNLOCK(&module_list);
+}
+
+int ast_update_module_list(int (*modentry)(const char *module, const char *description, int usecnt, const char *like),
+ const char *like)
+{
+ struct ast_module *cur;
+ int unlock = -1;
+ int total_mod_loaded = 0;
+
+ if (AST_LIST_TRYLOCK(&module_list))
+ unlock = 0;
+
+ AST_LIST_TRAVERSE(&module_list, cur, entry) {
+ total_mod_loaded += modentry(cur->resource, cur->info->description, cur->usecount, like);
+ }
+
+ if (unlock)
+ AST_LIST_UNLOCK(&module_list);
+
+ return total_mod_loaded;
+}
+
+/*! \brief Check if module exists */
+int ast_module_check(const char *name)
+{
+ struct ast_module *cur;
+
+ if (ast_strlen_zero(name))
+ return 0; /* FALSE */
+
+ cur = find_resource(name, 1);
+
+ return (cur != NULL);
+}
+
+
+int ast_loader_register(int (*v)(void))
+{
+ struct loadupdate *tmp;
+
+ if (!(tmp = ast_malloc(sizeof(*tmp))))
+ return -1;
+
+ tmp->updater = v;
+ AST_LIST_LOCK(&module_list);
+ AST_LIST_INSERT_HEAD(&updaters, tmp, entry);
+ AST_LIST_UNLOCK(&module_list);
+
+ return 0;
+}
+
+int ast_loader_unregister(int (*v)(void))
+{
+ struct loadupdate *cur;
+
+ AST_LIST_LOCK(&module_list);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&updaters, cur, entry) {
+ if (cur->updater == v) {
+ AST_LIST_REMOVE_CURRENT(entry);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ AST_LIST_UNLOCK(&module_list);
+
+ return cur ? 0 : -1;
+}
+
+struct ast_module *ast_module_ref(struct ast_module *mod)
+{
+ ast_atomic_fetchadd_int(&mod->usecount, +1);
+ ast_update_use_count();
+
+ return mod;
+}
+
+void ast_module_unref(struct ast_module *mod)
+{
+ ast_atomic_fetchadd_int(&mod->usecount, -1);
+ ast_update_use_count();
+}
diff --git a/trunk/main/logger.c b/trunk/main/logger.c
new file mode 100644
index 000000000..252b620b6
--- /dev/null
+++ b/trunk/main/logger.c
@@ -0,0 +1,1203 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006, 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.
+ */
+
+/*! \file
+ *
+ * \brief Asterisk Logger
+ *
+ * Logging routines
+ *
+ * \author Mark Spencer <markster@digium.com>
+ */
+
+/*
+ * define _ASTERISK_LOGGER_H to prevent the inclusion of logger.h;
+ * it redefines LOG_* which we need to define syslog_level_map.
+ * Later, we force the inclusion of logger.h again.
+ */
+#define _ASTERISK_LOGGER_H
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/_private.h"
+#include "asterisk/paths.h" /* use ast_config_AST_LOG_DIR */
+#include <signal.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#if ((defined(AST_DEVMODE)) && (defined(linux)))
+#include <execinfo.h>
+#define MAX_BACKTRACE_FRAMES 20
+#endif
+
+#define SYSLOG_NAMES /* so we can map syslog facilities names to their numeric values,
+ from <syslog.h> which is included by logger.h */
+#include <syslog.h>
+
+static int syslog_level_map[] = {
+ LOG_DEBUG,
+ LOG_INFO, /* arbitrary equivalent of LOG_EVENT */
+ LOG_NOTICE,
+ LOG_WARNING,
+ LOG_ERR,
+ LOG_DEBUG,
+ LOG_DEBUG
+};
+
+#define SYSLOG_NLEVELS sizeof(syslog_level_map) / sizeof(int)
+
+#undef _ASTERISK_LOGGER_H /* now include logger.h */
+#include "asterisk/logger.h"
+#include "asterisk/lock.h"
+#include "asterisk/channel.h"
+#include "asterisk/config.h"
+#include "asterisk/term.h"
+#include "asterisk/cli.h"
+#include "asterisk/utils.h"
+#include "asterisk/manager.h"
+#include "asterisk/threadstorage.h"
+#include "asterisk/strings.h"
+#include "asterisk/pbx.h"
+
+#if defined(__linux__) && !defined(__NR_gettid)
+#include <asm/unistd.h>
+#endif
+
+#if defined(__linux__) && defined(__NR_gettid)
+#define GETTID() syscall(__NR_gettid)
+#else
+#define GETTID() getpid()
+#endif
+
+static char dateformat[256] = "%b %e %T"; /* Original Asterisk Format */
+
+static char queue_log_name[256] = QUEUELOG;
+static char exec_after_rotate[256] = "";
+
+static int filesize_reload_needed;
+static int global_logmask = -1;
+
+enum rotatestrategy {
+ SEQUENTIAL = 1 << 0, /* Original method - create a new file, in order */
+ ROTATE = 1 << 1, /* Rotate all files, such that the oldest file has the highest suffix */
+ TIMESTAMP = 1 << 2, /* Append the epoch timestamp onto the end of the archived file */
+} rotatestrategy = SEQUENTIAL;
+
+static struct {
+ unsigned int queue_log:1;
+ unsigned int event_log:1;
+} logfiles = { 1, 1 };
+
+static char hostname[MAXHOSTNAMELEN];
+
+enum logtypes {
+ LOGTYPE_SYSLOG,
+ LOGTYPE_FILE,
+ LOGTYPE_CONSOLE,
+};
+
+struct logchannel {
+ int logmask; /* What to log to this channel */
+ int disabled; /* If this channel is disabled or not */
+ int facility; /* syslog facility */
+ enum logtypes type; /* Type of log channel */
+ FILE *fileptr; /* logfile logging file pointer */
+ char filename[256]; /* Filename */
+ AST_LIST_ENTRY(logchannel) list;
+};
+
+static AST_RWLIST_HEAD_STATIC(logchannels, logchannel);
+
+enum logmsgtypes {
+ LOGMSG_NORMAL = 0,
+ LOGMSG_VERBOSE,
+};
+
+struct logmsg {
+ enum logmsgtypes type;
+ char date[256];
+ int level;
+ const char *file;
+ int line;
+ const char *function;
+ AST_LIST_ENTRY(logmsg) list;
+ char str[0];
+};
+
+static AST_LIST_HEAD_STATIC(logmsgs, logmsg);
+static pthread_t logthread = AST_PTHREADT_NULL;
+static ast_cond_t logcond;
+static int close_logger_thread;
+
+static FILE *eventlog;
+static FILE *qlog;
+
+/*! \brief Logging channels used in the Asterisk logging system */
+static char *levels[] = {
+ "DEBUG",
+ "EVENT",
+ "NOTICE",
+ "WARNING",
+ "ERROR",
+ "VERBOSE",
+ "DTMF"
+};
+
+/*! \brief Colors used in the console for logging */
+static int colors[] = {
+ COLOR_BRGREEN,
+ COLOR_BRBLUE,
+ COLOR_YELLOW,
+ COLOR_BRRED,
+ COLOR_RED,
+ COLOR_GREEN,
+ COLOR_BRGREEN
+};
+
+AST_THREADSTORAGE(verbose_buf);
+#define VERBOSE_BUF_INIT_SIZE 256
+
+AST_THREADSTORAGE(log_buf);
+#define LOG_BUF_INIT_SIZE 256
+
+static int make_components(const char *s, int lineno)
+{
+ char *w;
+ int res = 0;
+ char *stringp = ast_strdupa(s);
+
+ while ((w = strsep(&stringp, ","))) {
+ w = ast_skip_blanks(w);
+ if (!strcasecmp(w, "error"))
+ res |= (1 << __LOG_ERROR);
+ else if (!strcasecmp(w, "warning"))
+ res |= (1 << __LOG_WARNING);
+ else if (!strcasecmp(w, "notice"))
+ res |= (1 << __LOG_NOTICE);
+ else if (!strcasecmp(w, "event"))
+ res |= (1 << __LOG_EVENT);
+ else if (!strcasecmp(w, "debug"))
+ res |= (1 << __LOG_DEBUG);
+ else if (!strcasecmp(w, "verbose"))
+ res |= (1 << __LOG_VERBOSE);
+ else if (!strcasecmp(w, "dtmf"))
+ res |= (1 << __LOG_DTMF);
+ else {
+ fprintf(stderr, "Logfile Warning: Unknown keyword '%s' at line %d of logger.conf\n", w, lineno);
+ }
+ }
+
+ return res;
+}
+
+static struct logchannel *make_logchannel(const char *channel, const char *components, int lineno)
+{
+ struct logchannel *chan;
+ char *facility;
+#ifndef SOLARIS
+ CODE *cptr;
+#endif
+
+ if (ast_strlen_zero(channel) || !(chan = ast_calloc(1, sizeof(*chan))))
+ return NULL;
+
+ if (!strcasecmp(channel, "console")) {
+ chan->type = LOGTYPE_CONSOLE;
+ } else if (!strncasecmp(channel, "syslog", 6)) {
+ /*
+ * syntax is:
+ * syslog.facility => level,level,level
+ */
+ facility = strchr(channel, '.');
+ if (!facility++ || !facility) {
+ facility = "local0";
+ }
+
+#ifndef SOLARIS
+ /*
+ * Walk through the list of facilitynames (defined in sys/syslog.h)
+ * to see if we can find the one we have been given
+ */
+ chan->facility = -1;
+ cptr = facilitynames;
+ while (cptr->c_name) {
+ if (!strcasecmp(facility, cptr->c_name)) {
+ chan->facility = cptr->c_val;
+ break;
+ }
+ cptr++;
+ }
+#else
+ chan->facility = -1;
+ if (!strcasecmp(facility, "kern"))
+ chan->facility = LOG_KERN;
+ else if (!strcasecmp(facility, "USER"))
+ chan->facility = LOG_USER;
+ else if (!strcasecmp(facility, "MAIL"))
+ chan->facility = LOG_MAIL;
+ else if (!strcasecmp(facility, "DAEMON"))
+ chan->facility = LOG_DAEMON;
+ else if (!strcasecmp(facility, "AUTH"))
+ chan->facility = LOG_AUTH;
+ else if (!strcasecmp(facility, "SYSLOG"))
+ chan->facility = LOG_SYSLOG;
+ else if (!strcasecmp(facility, "LPR"))
+ chan->facility = LOG_LPR;
+ else if (!strcasecmp(facility, "NEWS"))
+ chan->facility = LOG_NEWS;
+ else if (!strcasecmp(facility, "UUCP"))
+ chan->facility = LOG_UUCP;
+ else if (!strcasecmp(facility, "CRON"))
+ chan->facility = LOG_CRON;
+ else if (!strcasecmp(facility, "LOCAL0"))
+ chan->facility = LOG_LOCAL0;
+ else if (!strcasecmp(facility, "LOCAL1"))
+ chan->facility = LOG_LOCAL1;
+ else if (!strcasecmp(facility, "LOCAL2"))
+ chan->facility = LOG_LOCAL2;
+ else if (!strcasecmp(facility, "LOCAL3"))
+ chan->facility = LOG_LOCAL3;
+ else if (!strcasecmp(facility, "LOCAL4"))
+ chan->facility = LOG_LOCAL4;
+ else if (!strcasecmp(facility, "LOCAL5"))
+ chan->facility = LOG_LOCAL5;
+ else if (!strcasecmp(facility, "LOCAL6"))
+ chan->facility = LOG_LOCAL6;
+ else if (!strcasecmp(facility, "LOCAL7"))
+ chan->facility = LOG_LOCAL7;
+#endif /* Solaris */
+
+ if (0 > chan->facility) {
+ fprintf(stderr, "Logger Warning: bad syslog facility in logger.conf\n");
+ ast_free(chan);
+ return NULL;
+ }
+
+ chan->type = LOGTYPE_SYSLOG;
+ snprintf(chan->filename, sizeof(chan->filename), "%s", channel);
+ openlog("asterisk", LOG_PID, chan->facility);
+ } else {
+ if (channel[0] == '/') {
+ if (!ast_strlen_zero(hostname)) {
+ snprintf(chan->filename, sizeof(chan->filename) - 1,"%s.%s", channel, hostname);
+ } else {
+ ast_copy_string(chan->filename, channel, sizeof(chan->filename));
+ }
+ }
+
+ if (!ast_strlen_zero(hostname)) {
+ snprintf(chan->filename, sizeof(chan->filename), "%s/%s.%s", ast_config_AST_LOG_DIR, channel, hostname);
+ } else {
+ snprintf(chan->filename, sizeof(chan->filename), "%s/%s", ast_config_AST_LOG_DIR, channel);
+ }
+ chan->fileptr = fopen(chan->filename, "a");
+ if (!chan->fileptr) {
+ /* Can't log here, since we're called with a lock */
+ fprintf(stderr, "Logger Warning: Unable to open log file '%s': %s\n", chan->filename, strerror(errno));
+ }
+ chan->type = LOGTYPE_FILE;
+ }
+ chan->logmask = make_components(components, lineno);
+ return chan;
+}
+
+static void init_logger_chain(int reload, int locked)
+{
+ struct logchannel *chan;
+ struct ast_config *cfg;
+ struct ast_variable *var;
+ const char *s;
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+
+ if ((cfg = ast_config_load("logger.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
+ return;
+
+ /* delete our list of log channels */
+ if (!locked)
+ AST_RWLIST_WRLOCK(&logchannels);
+ while ((chan = AST_RWLIST_REMOVE_HEAD(&logchannels, list)))
+ ast_free(chan);
+ if (!locked)
+ AST_RWLIST_UNLOCK(&logchannels);
+
+ global_logmask = 0;
+ errno = 0;
+ /* close syslog */
+ closelog();
+
+ /* If no config file, we're fine, set default options. */
+ if (!cfg) {
+ if (errno)
+ fprintf(stderr, "Unable to open logger.conf: %s; default settings will be used.\n", strerror(errno));
+ else
+ fprintf(stderr, "Errors detected in logger.conf: see above; default settings will be used.\n");
+ if (!(chan = ast_calloc(1, sizeof(*chan))))
+ return;
+ chan->type = LOGTYPE_CONSOLE;
+ chan->logmask = 28; /*warning,notice,error */
+ if (!locked)
+ AST_RWLIST_WRLOCK(&logchannels);
+ AST_RWLIST_INSERT_HEAD(&logchannels, chan, list);
+ if (!locked)
+ AST_RWLIST_UNLOCK(&logchannels);
+ global_logmask |= chan->logmask;
+ return;
+ }
+
+ if ((s = ast_variable_retrieve(cfg, "general", "appendhostname"))) {
+ if (ast_true(s)) {
+ if (gethostname(hostname, sizeof(hostname) - 1)) {
+ ast_copy_string(hostname, "unknown", sizeof(hostname));
+ fprintf(stderr, "What box has no hostname???\n");
+ }
+ } else
+ hostname[0] = '\0';
+ } else
+ hostname[0] = '\0';
+ if ((s = ast_variable_retrieve(cfg, "general", "dateformat")))
+ ast_copy_string(dateformat, s, sizeof(dateformat));
+ else
+ ast_copy_string(dateformat, "%b %e %T", sizeof(dateformat));
+ if ((s = ast_variable_retrieve(cfg, "general", "queue_log")))
+ logfiles.queue_log = ast_true(s);
+ if ((s = ast_variable_retrieve(cfg, "general", "event_log")))
+ logfiles.event_log = ast_true(s);
+ if ((s = ast_variable_retrieve(cfg, "general", "queue_log_name")))
+ ast_copy_string(queue_log_name, s, sizeof(queue_log_name));
+ if ((s = ast_variable_retrieve(cfg, "general", "exec_after_rotate")))
+ ast_copy_string(exec_after_rotate, s, sizeof(exec_after_rotate));
+ if ((s = ast_variable_retrieve(cfg, "general", "rotatestrategy"))) {
+ if (strcasecmp(s, "timestamp") == 0)
+ rotatestrategy = TIMESTAMP;
+ else if (strcasecmp(s, "rotate") == 0)
+ rotatestrategy = ROTATE;
+ else if (strcasecmp(s, "sequential") == 0)
+ rotatestrategy = SEQUENTIAL;
+ else
+ fprintf(stderr, "Unknown rotatestrategy: %s\n", s);
+ } else {
+ if ((s = ast_variable_retrieve(cfg, "general", "rotatetimestamp"))) {
+ rotatestrategy = ast_true(s) ? TIMESTAMP : SEQUENTIAL;
+ fprintf(stderr, "rotatetimestamp option has been deprecated. Please use rotatestrategy instead.\n");
+ }
+ }
+
+ if (!locked)
+ AST_RWLIST_WRLOCK(&logchannels);
+ var = ast_variable_browse(cfg, "logfiles");
+ for (; var; var = var->next) {
+ if (!(chan = make_logchannel(var->name, var->value, var->lineno)))
+ continue;
+ AST_RWLIST_INSERT_HEAD(&logchannels, chan, list);
+ global_logmask |= chan->logmask;
+ }
+ if (!locked)
+ AST_RWLIST_UNLOCK(&logchannels);
+
+ ast_config_destroy(cfg);
+}
+
+void ast_queue_log(const char *queuename, const char *callid, const char *agent, const char *event, const char *fmt, ...)
+{
+ va_list ap;
+ char qlog_msg[8192];
+ int qlog_len;
+ char time_str[16];
+
+ if (ast_check_realtime("queue_log")) {
+ va_start(ap, fmt);
+ vsnprintf(qlog_msg, sizeof(qlog_msg), fmt, ap);
+ va_end(ap);
+ snprintf(time_str, sizeof(time_str), "%ld", (long)time(NULL));
+ ast_store_realtime("queue_log", "time", time_str,
+ "callid", callid,
+ "queuename", queuename,
+ "agent", agent,
+ "event", event,
+ "data", qlog_msg,
+ NULL);
+ } else {
+ if (qlog) {
+ va_start(ap, fmt);
+ qlog_len = snprintf(qlog_msg, sizeof(qlog_msg), "%ld|%s|%s|%s|%s|", (long)time(NULL), callid, queuename, agent, event);
+ vsnprintf(qlog_msg + qlog_len, sizeof(qlog_msg) - qlog_len, fmt, ap);
+ va_end(ap);
+ }
+ AST_RWLIST_RDLOCK(&logchannels);
+ if (qlog) {
+ fprintf(qlog, "%s\n", qlog_msg);
+ fflush(qlog);
+ }
+ AST_RWLIST_UNLOCK(&logchannels);
+ }
+}
+
+static int rotate_file(const char *filename)
+{
+ char old[PATH_MAX];
+ char new[PATH_MAX];
+ int x, y, which, found, res = 0, fd;
+ char *suffixes[4] = { "", ".gz", ".bz2", ".Z" };
+
+ switch (rotatestrategy) {
+ case SEQUENTIAL:
+ for (x = 0; ; x++) {
+ snprintf(new, sizeof(new), "%s.%d", filename, x);
+ fd = open(new, O_RDONLY);
+ if (fd > -1)
+ close(fd);
+ else
+ break;
+ }
+ if (rename(filename, new)) {
+ fprintf(stderr, "Unable to rename file '%s' to '%s'\n", filename, new);
+ res = -1;
+ }
+ break;
+ case TIMESTAMP:
+ snprintf(new, sizeof(new), "%s.%ld", filename, (long)time(NULL));
+ if (rename(filename, new)) {
+ fprintf(stderr, "Unable to rename file '%s' to '%s'\n", filename, new);
+ res = -1;
+ }
+ break;
+ case ROTATE:
+ /* Find the next empty slot, including a possible suffix */
+ for (x = 0; ; x++) {
+ found = 0;
+ for (which = 0; which < sizeof(suffixes) / sizeof(suffixes[0]); which++) {
+ snprintf(new, sizeof(new), "%s.%d%s", filename, x, suffixes[which]);
+ fd = open(new, O_RDONLY);
+ if (fd > -1)
+ close(fd);
+ else {
+ found = 1;
+ break;
+ }
+ }
+ if (!found)
+ break;
+ }
+
+ /* Found an empty slot */
+ for (y = x; y > -1; y--) {
+ for (which = 0; which < sizeof(suffixes) / sizeof(suffixes[0]); which++) {
+ snprintf(old, sizeof(old), "%s.%d%s", filename, y - 1, suffixes[which]);
+ fd = open(old, O_RDONLY);
+ if (fd > -1) {
+ /* Found the right suffix */
+ close(fd);
+ snprintf(new, sizeof(new), "%s.%d%s", filename, y, suffixes[which]);
+ if (rename(old, new)) {
+ fprintf(stderr, "Unable to rename file '%s' to '%s'\n", old, new);
+ res = -1;
+ }
+ break;
+ }
+ }
+ }
+
+ /* Finally, rename the current file */
+ snprintf(new, sizeof(new), "%s.0", filename);
+ if (rename(filename, new)) {
+ fprintf(stderr, "Unable to rename file '%s' to '%s'\n", filename, new);
+ res = -1;
+ }
+ }
+
+ if (!ast_strlen_zero(exec_after_rotate)) {
+ struct ast_channel *c = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Logger/rotate");
+ char buf[512];
+ pbx_builtin_setvar_helper(c, "filename", filename);
+ pbx_substitute_variables_helper(c, exec_after_rotate, buf, sizeof(buf));
+ system(buf);
+ ast_channel_free(c);
+ }
+ return res;
+}
+
+static int reload_logger(int rotate)
+{
+ char old[PATH_MAX] = "";
+ int event_rotate = rotate, queue_rotate = rotate;
+ struct logchannel *f;
+ int res = 0;
+ struct stat st;
+
+ AST_RWLIST_WRLOCK(&logchannels);
+
+ if (eventlog) {
+ if (rotate < 0) {
+ /* Check filesize - this one typically doesn't need an auto-rotate */
+ snprintf(old, sizeof(old), "%s/%s", ast_config_AST_LOG_DIR, EVENTLOG);
+ if (stat(old, &st) != 0 || st.st_size > 0x40000000) { /* Arbitrarily, 1 GB */
+ fclose(eventlog);
+ eventlog = NULL;
+ } else
+ event_rotate = 0;
+ } else {
+ fclose(eventlog);
+ eventlog = NULL;
+ }
+ } else
+ event_rotate = 0;
+
+ if (qlog) {
+ if (rotate < 0) {
+ /* Check filesize - this one typically doesn't need an auto-rotate */
+ snprintf(old, sizeof(old), "%s/%s", ast_config_AST_LOG_DIR, queue_log_name);
+ if (stat(old, &st) != 0 || st.st_size > 0x40000000) { /* Arbitrarily, 1 GB */
+ fclose(qlog);
+ qlog = NULL;
+ } else
+ event_rotate = 0;
+ } else {
+ fclose(qlog);
+ qlog = NULL;
+ }
+ } else
+ queue_rotate = 0;
+ qlog = NULL;
+
+ ast_mkdir(ast_config_AST_LOG_DIR, 0777);
+
+ AST_RWLIST_TRAVERSE(&logchannels, f, list) {
+ if (f->disabled) {
+ f->disabled = 0; /* Re-enable logging at reload */
+ manager_event(EVENT_FLAG_SYSTEM, "LogChannel", "Channel: %s\r\nEnabled: Yes\r\n", f->filename);
+ }
+ if (f->fileptr && (f->fileptr != stdout) && (f->fileptr != stderr)) {
+ fclose(f->fileptr); /* Close file */
+ f->fileptr = NULL;
+ if (rotate)
+ rotate_file(f->filename);
+ }
+ }
+
+ filesize_reload_needed = 0;
+
+ init_logger_chain(rotate ? 0 : 1 /* reload */, 1 /* locked */);
+
+ if (logfiles.event_log) {
+ snprintf(old, sizeof(old), "%s/%s", ast_config_AST_LOG_DIR, EVENTLOG);
+ if (event_rotate)
+ rotate_file(old);
+
+ eventlog = fopen(old, "a");
+ if (eventlog) {
+ ast_log(LOG_EVENT, "Restarted Asterisk Event Logger\n");
+ ast_verb(1, "Asterisk Event Logger restarted\n");
+ } else {
+ ast_log(LOG_ERROR, "Unable to create event log: %s\n", strerror(errno));
+ res = -1;
+ }
+ }
+
+ if (logfiles.queue_log) {
+ snprintf(old, sizeof(old), "%s/%s", ast_config_AST_LOG_DIR, queue_log_name);
+ if (queue_rotate)
+ rotate_file(old);
+
+ qlog = fopen(old, "a");
+ if (qlog) {
+ AST_RWLIST_UNLOCK(&logchannels);
+ ast_queue_log("NONE", "NONE", "NONE", "CONFIGRELOAD", "%s", "");
+ AST_RWLIST_WRLOCK(&logchannels);
+ ast_log(LOG_EVENT, "Restarted Asterisk Queue Logger\n");
+ ast_verb(1, "Asterisk Queue Logger restarted\n");
+ } else {
+ ast_log(LOG_ERROR, "Unable to create queue log: %s\n", strerror(errno));
+ res = -1;
+ }
+ }
+
+ AST_RWLIST_UNLOCK(&logchannels);
+
+ return res;
+}
+
+/*! \brief Reload the logger module without rotating log files (also used from loader.c during
+ a full Asterisk reload) */
+int logger_reload(void)
+{
+ if(reload_logger(0))
+ return RESULT_FAILURE;
+ return RESULT_SUCCESS;
+}
+
+static char *handle_logger_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "logger reload";
+ e->usage =
+ "Usage: logger reload\n"
+ " Reloads the logger subsystem state. Use after restarting syslogd(8) if you are using syslog logging.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (reload_logger(0)) {
+ ast_cli(a->fd, "Failed to reload the logger\n");
+ return CLI_FAILURE;
+ }
+ return CLI_SUCCESS;
+}
+
+static char *handle_logger_rotate(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "logger rotate";
+ e->usage =
+ "Usage: logger rotate\n"
+ " Rotates and Reopens the log files.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (reload_logger(1)) {
+ ast_cli(a->fd, "Failed to reload the logger and rotate log files\n");
+ return CLI_FAILURE;
+ }
+ return CLI_SUCCESS;
+}
+
+/*! \brief CLI command to show logging system configuration */
+static char *handle_logger_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+#define FORMATL "%-35.35s %-8.8s %-9.9s "
+ struct logchannel *chan;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "logger show channels";
+ e->usage =
+ "Usage: logger show channels\n"
+ " List configured logger channels.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ ast_cli(a->fd,FORMATL, "Channel", "Type", "Status");
+ ast_cli(a->fd, "Configuration\n");
+ ast_cli(a->fd,FORMATL, "-------", "----", "------");
+ ast_cli(a->fd, "-------------\n");
+ AST_RWLIST_RDLOCK(&logchannels);
+ AST_RWLIST_TRAVERSE(&logchannels, chan, list) {
+ ast_cli(a->fd, FORMATL, chan->filename, chan->type==LOGTYPE_CONSOLE ? "Console" : (chan->type==LOGTYPE_SYSLOG ? "Syslog" : "File"),
+ chan->disabled ? "Disabled" : "Enabled");
+ ast_cli(a->fd, " - ");
+ if (chan->logmask & (1 << __LOG_DEBUG))
+ ast_cli(a->fd, "Debug ");
+ if (chan->logmask & (1 << __LOG_DTMF))
+ ast_cli(a->fd, "DTMF ");
+ if (chan->logmask & (1 << __LOG_VERBOSE))
+ ast_cli(a->fd, "Verbose ");
+ if (chan->logmask & (1 << __LOG_WARNING))
+ ast_cli(a->fd, "Warning ");
+ if (chan->logmask & (1 << __LOG_NOTICE))
+ ast_cli(a->fd, "Notice ");
+ if (chan->logmask & (1 << __LOG_ERROR))
+ ast_cli(a->fd, "Error ");
+ if (chan->logmask & (1 << __LOG_EVENT))
+ ast_cli(a->fd, "Event ");
+ ast_cli(a->fd, "\n");
+ }
+ AST_RWLIST_UNLOCK(&logchannels);
+ ast_cli(a->fd, "\n");
+
+ return CLI_SUCCESS;
+}
+
+struct verb {
+ void (*verboser)(const char *string);
+ AST_LIST_ENTRY(verb) list;
+};
+
+static AST_RWLIST_HEAD_STATIC(verbosers, verb);
+
+static struct ast_cli_entry cli_logger[] = {
+ AST_CLI_DEFINE(handle_logger_show_channels, "List configured log channels"),
+ AST_CLI_DEFINE(handle_logger_reload, "Reopens the log files"),
+ AST_CLI_DEFINE(handle_logger_rotate, "Rotates and reopens the log files")
+};
+
+static int handle_SIGXFSZ(int sig)
+{
+ /* Indicate need to reload */
+ filesize_reload_needed = 1;
+ return 0;
+}
+
+static void ast_log_vsyslog(int level, const char *file, int line, const char *function, char *str)
+{
+ char buf[BUFSIZ];
+
+ if (level >= SYSLOG_NLEVELS) {
+ /* we are locked here, so cannot ast_log() */
+ fprintf(stderr, "ast_log_vsyslog called with bogus level: %d\n", level);
+ return;
+ }
+
+ if (level == __LOG_VERBOSE) {
+ snprintf(buf, sizeof(buf), "VERBOSE[%ld]: %s", (long)GETTID(), str);
+ level = __LOG_DEBUG;
+ } else if (level == __LOG_DTMF) {
+ snprintf(buf, sizeof(buf), "DTMF[%ld]: %s", (long)GETTID(), str);
+ level = __LOG_DEBUG;
+ } else {
+ snprintf(buf, sizeof(buf), "%s[%ld]: %s:%d in %s: %s",
+ levels[level], (long)GETTID(), file, line, function, str);
+ }
+
+ term_strip(buf, buf, strlen(buf) + 1);
+ syslog(syslog_level_map[level], "%s", buf);
+}
+
+/*! \brief Print a normal log message to the channels */
+static void logger_print_normal(struct logmsg *logmsg)
+{
+ struct logchannel *chan = NULL;
+ char buf[BUFSIZ];
+
+ AST_RWLIST_RDLOCK(&logchannels);
+
+ if (logfiles.event_log && logmsg->level == __LOG_EVENT) {
+ fprintf(eventlog, "%s asterisk[%ld]: %s", logmsg->date, (long)getpid(), logmsg->str);
+ fflush(eventlog);
+ AST_RWLIST_UNLOCK(&logchannels);
+ return;
+ }
+
+ if (!AST_RWLIST_EMPTY(&logchannels)) {
+ AST_RWLIST_TRAVERSE(&logchannels, chan, list) {
+ /* If the channel is disabled, then move on to the next one */
+ if (chan->disabled)
+ continue;
+ /* Check syslog channels */
+ if (chan->type == LOGTYPE_SYSLOG && (chan->logmask & (1 << logmsg->level))) {
+ ast_log_vsyslog(logmsg->level, logmsg->file, logmsg->line, logmsg->function, logmsg->str);
+ /* Console channels */
+ } else if (chan->type == LOGTYPE_CONSOLE && (chan->logmask & (1 << logmsg->level))) {
+ char linestr[128];
+ char tmp1[80], tmp2[80], tmp3[80], tmp4[80];
+
+ /* If the level is verbose, then skip it */
+ if (logmsg->level == __LOG_VERBOSE)
+ continue;
+
+ /* Turn the numerical line number into a string */
+ snprintf(linestr, sizeof(linestr), "%d", logmsg->line);
+ /* Build string to print out */
+ snprintf(buf, sizeof(buf), "[%s] %s[%ld]: %s:%s %s: %s",
+ logmsg->date,
+ term_color(tmp1, levels[logmsg->level], colors[logmsg->level], 0, sizeof(tmp1)),
+ (long)GETTID(),
+ term_color(tmp2, logmsg->file, COLOR_BRWHITE, 0, sizeof(tmp2)),
+ term_color(tmp3, linestr, COLOR_BRWHITE, 0, sizeof(tmp3)),
+ term_color(tmp4, logmsg->function, COLOR_BRWHITE, 0, sizeof(tmp4)),
+ logmsg->str);
+ /* Print out */
+ ast_console_puts_mutable(buf);
+ /* File channels */
+ } else if (chan->type == LOGTYPE_FILE && (chan->logmask & (1 << logmsg->level))) {
+ int res = 0;
+
+ /* If no file pointer exists, skip it */
+ if (!chan->fileptr)
+ continue;
+
+ /* Print out to the file */
+ res = fprintf(chan->fileptr, "[%s] %s[%ld] %s: %s",
+ logmsg->date, levels[logmsg->level], (long)GETTID(), logmsg->file, logmsg->str);
+ if (res <= 0 && !ast_strlen_zero(logmsg->str)) {
+ fprintf(stderr, "**** Asterisk Logging Error: ***********\n");
+ if (errno == ENOMEM || errno == ENOSPC)
+ fprintf(stderr, "Asterisk logging error: Out of disk space, can't log to log file %s\n", chan->filename);
+ else
+ fprintf(stderr, "Logger Warning: Unable to write to log file '%s': %s (disabled)\n", chan->filename, strerror(errno));
+ manager_event(EVENT_FLAG_SYSTEM, "LogChannel", "Channel: %s\r\nEnabled: No\r\nReason: %d - %s\r\n", chan->filename, errno, strerror(errno));
+ chan->disabled = 1;
+ } else if (res > 0) {
+ fflush(chan->fileptr);
+ }
+ }
+ }
+ } else if (logmsg->level != __LOG_VERBOSE) {
+ fputs(logmsg->str, stdout);
+ }
+
+ AST_RWLIST_UNLOCK(&logchannels);
+
+ /* If we need to reload because of the file size, then do so */
+ if (filesize_reload_needed) {
+ reload_logger(-1);
+ ast_log(LOG_EVENT, "Rotated Logs Per SIGXFSZ (Exceeded file size limit)\n");
+ if (option_verbose)
+ ast_verbose("Rotated Logs Per SIGXFSZ (Exceeded file size limit)\n");
+ }
+
+ return;
+}
+
+/*! \brief Print a verbose message to the verbosers */
+static void logger_print_verbose(struct logmsg *logmsg)
+{
+ struct verb *v = NULL;
+
+ /* Iterate through the list of verbosers and pass them the log message string */
+ AST_RWLIST_RDLOCK(&verbosers);
+ AST_RWLIST_TRAVERSE(&verbosers, v, list)
+ v->verboser(logmsg->str);
+ AST_RWLIST_UNLOCK(&verbosers);
+
+ return;
+}
+
+/*! \brief Actual logging thread */
+static void *logger_thread(void *data)
+{
+ struct logmsg *next = NULL, *msg = NULL;
+
+ for (;;) {
+ /* We lock the message list, and see if any message exists... if not we wait on the condition to be signalled */
+ AST_LIST_LOCK(&logmsgs);
+ if (AST_LIST_EMPTY(&logmsgs))
+ ast_cond_wait(&logcond, &logmsgs.lock);
+ next = AST_LIST_FIRST(&logmsgs);
+ AST_LIST_HEAD_INIT_NOLOCK(&logmsgs);
+ AST_LIST_UNLOCK(&logmsgs);
+
+ /* Otherwise go through and process each message in the order added */
+ while ((msg = next)) {
+ /* Get the next entry now so that we can free our current structure later */
+ next = AST_LIST_NEXT(msg, list);
+
+ /* Depending on the type, send it to the proper function */
+ if (msg->type == LOGMSG_NORMAL)
+ logger_print_normal(msg);
+ else if (msg->type == LOGMSG_VERBOSE)
+ logger_print_verbose(msg);
+
+ /* Free the data since we are done */
+ ast_free(msg);
+ }
+
+ /* If we should stop, then stop */
+ if (close_logger_thread)
+ break;
+ }
+
+ return NULL;
+}
+
+int init_logger(void)
+{
+ char tmp[256];
+ int res = 0;
+
+ /* auto rotate if sig SIGXFSZ comes a-knockin */
+ (void) signal(SIGXFSZ,(void *) handle_SIGXFSZ);
+
+ /* start logger thread */
+ ast_cond_init(&logcond, NULL);
+ if (ast_pthread_create(&logthread, NULL, logger_thread, NULL) < 0) {
+ ast_cond_destroy(&logcond);
+ return -1;
+ }
+
+ /* register the logger cli commands */
+ ast_cli_register_multiple(cli_logger, sizeof(cli_logger) / sizeof(struct ast_cli_entry));
+
+ ast_mkdir(ast_config_AST_LOG_DIR, 0777);
+
+ /* create log channels */
+ init_logger_chain(0 /* reload */, 0 /* locked */);
+
+ /* create the eventlog */
+ if (logfiles.event_log) {
+ snprintf(tmp, sizeof(tmp), "%s/%s", ast_config_AST_LOG_DIR, EVENTLOG);
+ eventlog = fopen(tmp, "a");
+ if (eventlog) {
+ ast_log(LOG_EVENT, "Started Asterisk Event Logger\n");
+ if (option_verbose)
+ ast_verbose("Asterisk Event Logger Started %s\n", tmp);
+ } else {
+ ast_log(LOG_ERROR, "Unable to create event log: %s\n", strerror(errno));
+ res = -1;
+ }
+ }
+
+ if (logfiles.queue_log) {
+ snprintf(tmp, sizeof(tmp), "%s/%s", ast_config_AST_LOG_DIR, queue_log_name);
+ qlog = fopen(tmp, "a");
+ ast_queue_log("NONE", "NONE", "NONE", "QUEUESTART", "%s", "");
+ }
+ return res;
+}
+
+void close_logger(void)
+{
+ struct logchannel *f = NULL;
+
+ /* Stop logger thread */
+ AST_LIST_LOCK(&logmsgs);
+ close_logger_thread = 1;
+ ast_cond_signal(&logcond);
+ AST_LIST_UNLOCK(&logmsgs);
+
+ if (logthread != AST_PTHREADT_NULL)
+ pthread_join(logthread, NULL);
+
+ AST_RWLIST_WRLOCK(&logchannels);
+
+ if (eventlog) {
+ fclose(eventlog);
+ eventlog = NULL;
+ }
+
+ if (qlog) {
+ fclose(qlog);
+ qlog = NULL;
+ }
+
+ AST_RWLIST_TRAVERSE(&logchannels, f, list) {
+ if (f->fileptr && (f->fileptr != stdout) && (f->fileptr != stderr)) {
+ fclose(f->fileptr);
+ f->fileptr = NULL;
+ }
+ }
+
+ closelog(); /* syslog */
+
+ AST_RWLIST_UNLOCK(&logchannels);
+
+ return;
+}
+
+/*!
+ * \brief send log messages to syslog and/or the console
+ */
+void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...)
+{
+ struct logmsg *logmsg = NULL;
+ struct ast_str *buf = NULL;
+ struct ast_tm tm;
+ struct timeval tv = ast_tvnow();
+ int res = 0;
+ va_list ap;
+
+ if (!(buf = ast_str_thread_get(&log_buf, LOG_BUF_INIT_SIZE)))
+ return;
+
+ if (AST_RWLIST_EMPTY(&logchannels)) {
+ /*
+ * we don't have the logger chain configured yet,
+ * so just log to stdout
+ */
+ if (level != __LOG_VERBOSE) {
+ int res;
+ va_start(ap, fmt);
+ res = ast_str_set_va(&buf, BUFSIZ, fmt, ap); /* XXX BUFSIZ ? */
+ va_end(ap);
+ if (res != AST_DYNSTR_BUILD_FAILED) {
+ term_filter_escapes(buf->str);
+ fputs(buf->str, stdout);
+ }
+ }
+ return;
+ }
+
+ /* don't display LOG_DEBUG messages unless option_verbose _or_ option_debug
+ are non-zero; LOG_DEBUG messages can still be displayed if option_debug
+ is zero, if option_verbose is non-zero (this allows for 'level zero'
+ LOG_DEBUG messages to be displayed, if the logmask on any channel
+ allows it)
+ */
+ if (!option_verbose && !option_debug && (level == __LOG_DEBUG))
+ return;
+
+ /* Ignore anything that never gets logged anywhere */
+ if (!(global_logmask & (1 << level)))
+ return;
+
+ /* Build string */
+ va_start(ap, fmt);
+ res = ast_str_set_va(&buf, BUFSIZ, fmt, ap);
+ va_end(ap);
+
+ /* If the build failed, then abort and free this structure */
+ if (res == AST_DYNSTR_BUILD_FAILED)
+ return;
+
+ /* Create a new logging message */
+ if (!(logmsg = ast_calloc(1, sizeof(*logmsg) + res + 1)))
+ return;
+
+ /* Copy string over */
+ strcpy(logmsg->str, buf->str);
+
+ /* Set type to be normal */
+ logmsg->type = LOGMSG_NORMAL;
+
+ /* Create our date/time */
+ ast_localtime(&tv, &tm, NULL);
+ ast_strftime(logmsg->date, sizeof(logmsg->date), dateformat, &tm);
+
+ /* Copy over data */
+ logmsg->level = level;
+ logmsg->file = file;
+ logmsg->line = line;
+ logmsg->function = function;
+
+ /* If the logger thread is active, append it to the tail end of the list - otherwise skip that step */
+ if (logthread != AST_PTHREADT_NULL) {
+ AST_LIST_LOCK(&logmsgs);
+ AST_LIST_INSERT_TAIL(&logmsgs, logmsg, list);
+ ast_cond_signal(&logcond);
+ AST_LIST_UNLOCK(&logmsgs);
+ } else {
+ logger_print_normal(logmsg);
+ ast_free(logmsg);
+ }
+
+ return;
+}
+
+void ast_backtrace(void)
+{
+#ifdef linux
+#ifdef AST_DEVMODE
+ int count=0, i=0;
+ void **addresses;
+ char **strings;
+
+ if ((addresses = ast_calloc(MAX_BACKTRACE_FRAMES, sizeof(*addresses)))) {
+ count = backtrace(addresses, MAX_BACKTRACE_FRAMES);
+ if ((strings = backtrace_symbols(addresses, count))) {
+ ast_debug(1, "Got %d backtrace record%c\n", count, count != 1 ? 's' : ' ');
+ for (i=0; i < count ; i++) {
+#if __WORDSIZE == 32
+ ast_log(LOG_DEBUG, "#%d: [%08X] %s\n", i, (unsigned int)addresses[i], strings[i]);
+#elif __WORDSIZE == 64
+ ast_log(LOG_DEBUG, "#%d: [%016lX] %s\n", i, (unsigned long)addresses[i], strings[i]);
+#endif
+ }
+ free(strings);
+ } else {
+ ast_debug(1, "Could not allocate memory for backtrace\n");
+ }
+ ast_free(addresses);
+ }
+#else
+ ast_log(LOG_WARNING, "Must run configure with '--enable-dev-mode' for stack backtraces.\n");
+#endif
+#else /* ndef linux */
+ ast_log(LOG_WARNING, "Inline stack backtraces are only available on the Linux platform.\n");
+#endif
+}
+
+void ast_verbose(const char *fmt, ...)
+{
+ struct logmsg *logmsg = NULL;
+ struct ast_str *buf = NULL;
+ int res = 0;
+ va_list ap;
+
+ if (!(buf = ast_str_thread_get(&verbose_buf, VERBOSE_BUF_INIT_SIZE)))
+ return;
+
+ if (ast_opt_timestamp) {
+ struct timeval tv;
+ struct ast_tm tm;
+ char date[40];
+ char *datefmt;
+
+ tv = ast_tvnow();
+ ast_localtime(&tv, &tm, NULL);
+ ast_strftime(date, sizeof(date), dateformat, &tm);
+ datefmt = alloca(strlen(date) + 3 + strlen(fmt) + 1);
+ sprintf(datefmt, "[%s] %s", date, fmt);
+ fmt = datefmt;
+ }
+
+ /* Build string */
+ va_start(ap, fmt);
+ res = ast_str_set_va(&buf, 0, fmt, ap);
+ va_end(ap);
+
+ /* If the build failed then we can drop this allocated message */
+ if (res == AST_DYNSTR_BUILD_FAILED)
+ return;
+
+ if (!(logmsg = ast_calloc(1, sizeof(*logmsg) + res + 1)))
+ return;
+
+ strcpy(logmsg->str, buf->str);
+
+ ast_log(LOG_VERBOSE, logmsg->str);
+
+ /* Set type */
+ logmsg->type = LOGMSG_VERBOSE;
+
+ /* Add to the list and poke the thread if possible */
+ if (logthread != AST_PTHREADT_NULL) {
+ AST_LIST_LOCK(&logmsgs);
+ AST_LIST_INSERT_TAIL(&logmsgs, logmsg, list);
+ ast_cond_signal(&logcond);
+ AST_LIST_UNLOCK(&logmsgs);
+ } else {
+ logger_print_verbose(logmsg);
+ ast_free(logmsg);
+ }
+}
+
+int ast_register_verbose(void (*v)(const char *string))
+{
+ struct verb *verb;
+
+ if (!(verb = ast_malloc(sizeof(*verb))))
+ return -1;
+
+ verb->verboser = v;
+
+ AST_RWLIST_WRLOCK(&verbosers);
+ AST_RWLIST_INSERT_HEAD(&verbosers, verb, list);
+ AST_RWLIST_UNLOCK(&verbosers);
+
+ return 0;
+}
+
+int ast_unregister_verbose(void (*v)(const char *string))
+{
+ struct verb *cur;
+
+ AST_RWLIST_WRLOCK(&verbosers);
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&verbosers, cur, list) {
+ if (cur->verboser == v) {
+ AST_RWLIST_REMOVE_CURRENT(list);
+ ast_free(cur);
+ break;
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+ AST_RWLIST_UNLOCK(&verbosers);
+
+ return cur ? 0 : -1;
+}
diff --git a/trunk/main/manager.c b/trunk/main/manager.c
new file mode 100644
index 000000000..c9e69eb5b
--- /dev/null
+++ b/trunk/main/manager.c
@@ -0,0 +1,3768 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006, 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.
+ */
+
+/*! \file
+ *
+ * \brief The Asterisk Management Interface - AMI
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \extref OpenSSL http://www.openssl.org - for AMI/SSL
+ *
+ * At the moment this file contains a number of functions, namely:
+ *
+ * - data structures storing AMI state
+ * - AMI-related API functions, used by internal asterisk components
+ * - handlers for AMI-related CLI functions
+ * - handlers for AMI functions (available through the AMI socket)
+ * - the code for the main AMI listener thread and individual session threads
+ * - the http handlers invoked for AMI-over-HTTP by the threads in main/http.c
+ *
+ * \ref amiconf
+ */
+
+/*! \addtogroup Group_AMI AMI functions
+*/
+/*! @{
+ Doxygen group */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/_private.h"
+#include "asterisk/paths.h" /* use various ast_config_AST_* */
+#include <ctype.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <sys/mman.h>
+
+#include "asterisk/channel.h"
+#include "asterisk/file.h"
+#include "asterisk/manager.h"
+#include "asterisk/module.h"
+#include "asterisk/config.h"
+#include "asterisk/callerid.h"
+#include "asterisk/lock.h"
+#include "asterisk/cli.h"
+#include "asterisk/app.h"
+#include "asterisk/pbx.h"
+#include "asterisk/md5.h"
+#include "asterisk/acl.h"
+#include "asterisk/utils.h"
+#include "asterisk/tcptls.h"
+#include "asterisk/http.h"
+#include "asterisk/version.h"
+#include "asterisk/threadstorage.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/version.h"
+#include "asterisk/term.h"
+#include "asterisk/astobj2.h"
+
+/*!
+ * Linked list of events.
+ * Global events are appended to the list by append_event().
+ * The usecount is the number of stored pointers to the element,
+ * excluding the list pointers. So an element that is only in
+ * the list has a usecount of 0, not 1.
+ *
+ * Clients have a pointer to the last event processed, and for each
+ * of these clients we track the usecount of the elements.
+ * If we have a pointer to an entry in the list, it is safe to navigate
+ * it forward because elements will not be deleted, but only appended.
+ * The worst that can happen is seeing the pointer still NULL.
+ *
+ * When the usecount of an element drops to 0, and the element is the
+ * first in the list, we can remove it. Removal is done within the
+ * main thread, which is woken up for the purpose.
+ *
+ * For simplicity of implementation, we make sure the list is never empty.
+ */
+struct eventqent {
+ int usecount; /*!< # of clients who still need the event */
+ int category;
+ unsigned int seq; /*!< sequence number */
+ AST_LIST_ENTRY(eventqent) eq_next;
+ char eventdata[1]; /*!< really variable size, allocated by append_event() */
+};
+
+static AST_LIST_HEAD_STATIC(all_events, eventqent);
+
+static int displayconnects = 1;
+static int allowmultiplelogin = 1;
+static int timestampevents;
+static int httptimeout = 60;
+static int manager_enabled = 0;
+static int webmanager_enabled = 0;
+
+static int block_sockets;
+static int num_sessions;
+
+static int manager_debug; /*!< enable some debugging code in the manager */
+
+/*! \brief
+ * Descriptor for a manager session, either on the AMI socket or over HTTP.
+ *
+ * \note
+ * AMI session have managerid == 0; the entry is created upon a connect,
+ * and destroyed with the socket.
+ * HTTP sessions have managerid != 0, the value is used as a search key
+ * to lookup sessions (using the mansession_id cookie).
+ */
+static const char *command_blacklist[] = {
+ "module load",
+ "module unload",
+};
+
+struct mansession {
+ pthread_t ms_t; /*!< Execution thread, basically useless */
+ ast_mutex_t __lock; /*!< Thread lock -- don't use in action callbacks, it's already taken care of */
+ /* XXX need to document which fields it is protecting */
+ struct sockaddr_in sin; /*!< address we are connecting from */
+ FILE *f; /*!< fdopen() on the underlying fd */
+ int fd; /*!< descriptor used for output. Either the socket (AMI) or a temporary file (HTTP) */
+ int inuse; /*!< number of HTTP sessions using this entry */
+ int needdestroy; /*!< Whether an HTTP session should be destroyed */
+ pthread_t waiting_thread; /*!< Sleeping thread using this descriptor */
+ unsigned long managerid; /*!< Unique manager identifier, 0 for AMI sessions */
+ time_t sessionstart; /*!< Session start time */
+ time_t sessiontimeout; /*!< Session timeout if HTTP */
+ char username[80]; /*!< Logged in username */
+ char challenge[10]; /*!< Authentication challenge */
+ int authenticated; /*!< Authentication status */
+ int readperm; /*!< Authorization for reading */
+ int writeperm; /*!< Authorization for writing */
+ char inbuf[1025]; /*!< Buffer */
+ /* we use the extra byte to add a '\0' and simplify parsing */
+ int inlen; /*!< number of buffered bytes */
+ int send_events; /*!< XXX what ? */
+ struct eventqent *last_ev; /*!< last event processed. */
+ int writetimeout; /*!< Timeout for ast_carefulwrite() */
+ AST_LIST_ENTRY(mansession) list;
+};
+
+#define NEW_EVENT(m) (AST_LIST_NEXT(m->last_ev, eq_next))
+
+static AST_LIST_HEAD_STATIC(sessions, mansession);
+
+/*! \brief user descriptor, as read from the config file.
+ *
+ * \note It is still missing some fields -- e.g. we can have multiple permit and deny
+ * lines which are not supported here, and readperm/writeperm/writetimeout
+ * are not stored.
+ */
+struct ast_manager_user {
+ char username[80];
+ char *secret;
+ struct ast_ha *ha; /*!< ACL setting */
+ int readperm; /*! Authorization for reading */
+ int writeperm; /*! Authorization for writing */
+ int writetimeout; /*! Per user Timeout for ast_carefulwrite() */
+ int displayconnects; /*!< XXX unused */
+ int keep; /*!< mark entries created on a reload */
+ AST_RWLIST_ENTRY(ast_manager_user) list;
+};
+
+/*! \brief list of users found in the config file */
+static AST_RWLIST_HEAD_STATIC(users, ast_manager_user);
+
+/*! \brief list of actions registered */
+static AST_RWLIST_HEAD_STATIC(actions, manager_action);
+
+/*! \brief list of hooks registered */
+static AST_RWLIST_HEAD_STATIC(manager_hooks, manager_custom_hook);
+
+/*! \brief Add a custom hook to be called when an event is fired */
+void ast_manager_register_hook(struct manager_custom_hook *hook)
+{
+ AST_RWLIST_WRLOCK(&manager_hooks);
+ AST_RWLIST_INSERT_TAIL(&manager_hooks, hook, list);
+ AST_RWLIST_UNLOCK(&manager_hooks);
+ return;
+}
+
+/*! \brief Delete a custom hook to be called when an event is fired */
+void ast_manager_unregister_hook(struct manager_custom_hook *hook)
+{
+ AST_RWLIST_WRLOCK(&manager_hooks);
+ AST_RWLIST_REMOVE(&manager_hooks, hook, list);
+ AST_RWLIST_UNLOCK(&manager_hooks);
+ return;
+}
+
+/*! \brief
+ * Event list management functions.
+ * We assume that the event list always has at least one element,
+ * and the delete code will not remove the last entry even if the
+ *
+ */
+#if 0
+static time_t __deb(time_t start, const char *msg)
+{
+ time_t now = time(NULL);
+ ast_verbose("%4d th %p %s\n", (int)(now % 3600), pthread_self(), msg);
+ if (start != 0 && now - start > 5)
+ ast_verbose("+++ WOW, %s took %d seconds\n", msg, (int)(now - start));
+ return now;
+}
+
+static void LOCK_EVENTS(void)
+{
+ time_t start = __deb(0, "about to lock events");
+ AST_LIST_LOCK(&all_events);
+ __deb(start, "done lock events");
+}
+
+static void UNLOCK_EVENTS(void)
+{
+ __deb(0, "about to unlock events");
+ AST_LIST_UNLOCK(&all_events);
+}
+
+static void LOCK_SESS(void)
+{
+ time_t start = __deb(0, "about to lock sessions");
+ AST_LIST_LOCK(&sessions);
+ __deb(start, "done lock sessions");
+}
+
+static void UNLOCK_SESS(void)
+{
+ __deb(0, "about to unlock sessions");
+ AST_LIST_UNLOCK(&sessions);
+}
+#endif
+
+int check_manager_enabled()
+{
+ return manager_enabled;
+}
+
+int check_webmanager_enabled()
+{
+ return (webmanager_enabled && manager_enabled);
+}
+
+/*!
+ * Grab a reference to the last event, update usecount as needed.
+ * Can handle a NULL pointer.
+ */
+static struct eventqent *grab_last(void)
+{
+ struct eventqent *ret;
+
+ AST_LIST_LOCK(&all_events);
+ ret = AST_LIST_LAST(&all_events);
+ /* the list is never empty now, but may become so when
+ * we optimize it in the future, so be prepared.
+ */
+ if (ret)
+ ast_atomic_fetchadd_int(&ret->usecount, 1);
+ AST_LIST_UNLOCK(&all_events);
+ return ret;
+}
+
+/*!
+ * Purge unused events. Remove elements from the head
+ * as long as their usecount is 0 and there is a next element.
+ */
+static void purge_events(void)
+{
+ struct eventqent *ev;
+
+ AST_LIST_LOCK(&all_events);
+ while ( (ev = AST_LIST_FIRST(&all_events)) &&
+ ev->usecount == 0 && AST_LIST_NEXT(ev, eq_next)) {
+ AST_LIST_REMOVE_HEAD(&all_events, eq_next);
+ ast_free(ev);
+ }
+ AST_LIST_UNLOCK(&all_events);
+}
+
+/*!
+ * helper functions to convert back and forth between
+ * string and numeric representation of set of flags
+ */
+static struct permalias {
+ int num;
+ char *label;
+} perms[] = {
+ { EVENT_FLAG_SYSTEM, "system" },
+ { EVENT_FLAG_CALL, "call" },
+ { EVENT_FLAG_LOG, "log" },
+ { EVENT_FLAG_VERBOSE, "verbose" },
+ { EVENT_FLAG_COMMAND, "command" },
+ { EVENT_FLAG_AGENT, "agent" },
+ { EVENT_FLAG_USER, "user" },
+ { EVENT_FLAG_CONFIG, "config" },
+ { EVENT_FLAG_DTMF, "dtmf" },
+ { EVENT_FLAG_REPORTING, "reporting" },
+ { EVENT_FLAG_CDR, "cdr" },
+ { EVENT_FLAG_DIALPLAN, "dialplan" },
+ { -1, "all" },
+ { 0, "none" },
+};
+
+/*! \brief Convert authority code to a list of options */
+static char *authority_to_str(int authority, struct ast_str **res)
+{
+ int i;
+ char *sep = "";
+
+ (*res)->used = 0;
+ for (i = 0; i < (sizeof(perms) / sizeof(perms[0])) - 1; i++) {
+ if (authority & perms[i].num) {
+ ast_str_append(res, 0, "%s%s", sep, perms[i].label);
+ sep = ",";
+ }
+ }
+
+ if ((*res)->used == 0) /* replace empty string with something sensible */
+ ast_str_append(res, 0, "<none>");
+
+ return (*res)->str;
+}
+
+/*! Tells you if smallstr exists inside bigstr
+ which is delim by delim and uses no buf or stringsep
+ ast_instring("this|that|more","this",'|') == 1;
+
+ feel free to move this to app.c -anthm */
+static int ast_instring(const char *bigstr, const char *smallstr, const char delim)
+{
+ const char *val = bigstr, *next;
+
+ do {
+ if ((next = strchr(val, delim))) {
+ if (!strncmp(val, smallstr, (next - val)))
+ return 1;
+ else
+ continue;
+ } else
+ return !strcmp(smallstr, val);
+ } while (*(val = (next + 1)));
+
+ return 0;
+}
+
+static int get_perm(const char *instr)
+{
+ int x = 0, ret = 0;
+
+ if (!instr)
+ return 0;
+
+ for (x = 0; x < (sizeof(perms) / sizeof(perms[0])); x++) {
+ if (ast_instring(instr, perms[x].label, ','))
+ ret |= perms[x].num;
+ }
+
+ return ret;
+}
+
+/*!
+ * A number returns itself, false returns 0, true returns all flags,
+ * other strings return the flags that are set.
+ */
+static int strings_to_mask(const char *string)
+{
+ const char *p;
+
+ if (ast_strlen_zero(string))
+ return -1;
+
+ for (p = string; *p; p++)
+ if (*p < '0' || *p > '9')
+ break;
+ if (!p) /* all digits */
+ return atoi(string);
+ if (ast_false(string))
+ return 0;
+ if (ast_true(string)) { /* all permissions */
+ int x, ret = 0;
+ for (x=0; x<sizeof(perms) / sizeof(perms[0]); x++)
+ ret |= perms[x].num;
+ return ret;
+ }
+ return get_perm(string);
+}
+
+static int check_manager_session_inuse(const char *name)
+{
+ struct mansession *session = NULL;
+
+ AST_LIST_LOCK(&sessions);
+ AST_LIST_TRAVERSE(&sessions, session, list) {
+ if (!strcasecmp(session->username, name))
+ break;
+ }
+ AST_LIST_UNLOCK(&sessions);
+
+ return session ? 1 : 0;
+}
+
+
+/*!
+ * lookup an entry in the list of registered users.
+ * must be called with the list lock held.
+ */
+static struct ast_manager_user *get_manager_by_name_locked(const char *name)
+{
+ struct ast_manager_user *user = NULL;
+
+ AST_RWLIST_TRAVERSE(&users, user, list)
+ if (!strcasecmp(user->username, name))
+ break;
+ return user;
+}
+
+/*! \brief Get displayconnects config option.
+ * \param s manager session to get parameter from.
+ * \return displayconnects config option value.
+ */
+static int manager_displayconnects (struct mansession *s)
+{
+ struct ast_manager_user *user = NULL;
+ int ret = 0;
+
+ AST_RWLIST_RDLOCK(&users);
+ if ((user = get_manager_by_name_locked (s->username)))
+ ret = user->displayconnects;
+ AST_RWLIST_UNLOCK(&users);
+
+ return ret;
+}
+
+static char *handle_showmancmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct manager_action *cur;
+ struct ast_str *authority;
+ int num, l, which;
+ char *ret = NULL;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "manager show command";
+ e->usage =
+ "Usage: manager show command <actionname>\n"
+ " Shows the detailed description for a specific Asterisk manager interface command.\n";
+ return NULL;
+ case CLI_GENERATE:
+ l = strlen(a->word);
+ which = 0;
+ AST_RWLIST_RDLOCK(&actions);
+ AST_RWLIST_TRAVERSE(&actions, cur, list) {
+ if (!strncasecmp(a->word, cur->action, l) && ++which > a->n) {
+ ret = ast_strdup(cur->action);
+ break; /* make sure we exit even if ast_strdup() returns NULL */
+ }
+ }
+ AST_RWLIST_UNLOCK(&actions);
+ return ret;
+ }
+ authority = ast_str_alloca(80);
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+
+ AST_RWLIST_RDLOCK(&actions);
+ AST_RWLIST_TRAVERSE(&actions, cur, list) {
+ for (num = 3; num < a->argc; num++) {
+ if (!strcasecmp(cur->action, a->argv[num])) {
+ ast_cli(a->fd, "Action: %s\nSynopsis: %s\nPrivilege: %s\n%s\n",
+ cur->action, cur->synopsis,
+ authority_to_str(cur->authority, &authority),
+ S_OR(cur->description, ""));
+ }
+ }
+ }
+ AST_RWLIST_UNLOCK(&actions);
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_mandebug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "manager debug [on|off]";
+ e->usage = "Usage: manager debug [on|off]\n Show, enable, disable debugging of the manager code.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (a->argc == 2)
+ ast_cli(a->fd, "manager debug is %s\n", manager_debug? "on" : "off");
+ else if (a->argc == 3) {
+ if (!strcasecmp(a->argv[2], "on"))
+ manager_debug = 1;
+ else if (!strcasecmp(a->argv[2], "off"))
+ manager_debug = 0;
+ else
+ return CLI_SHOWUSAGE;
+ }
+ return CLI_SUCCESS;
+}
+
+static char *handle_showmanager(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_manager_user *user = NULL;
+ int l, which;
+ char *ret = NULL;
+ struct ast_str *rauthority = ast_str_alloca(80);
+ struct ast_str *wauthority = ast_str_alloca(80);
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "manager show user";
+ e->usage =
+ " Usage: manager show user <user>\n"
+ " Display all information related to the manager user specified.\n";
+ return NULL;
+ case CLI_GENERATE:
+ l = strlen(a->word);
+ which = 0;
+ if (a->pos != 3)
+ return NULL;
+ AST_RWLIST_RDLOCK(&users);
+ AST_RWLIST_TRAVERSE(&users, user, list) {
+ if ( !strncasecmp(a->word, user->username, l) && ++which > a->n ) {
+ ret = ast_strdup(user->username);
+ break;
+ }
+ }
+ AST_RWLIST_UNLOCK(&users);
+ return ret;
+ }
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+
+ AST_RWLIST_RDLOCK(&users);
+
+ if (!(user = get_manager_by_name_locked(a->argv[3]))) {
+ ast_cli(a->fd, "There is no manager called %s\n", a->argv[3]);
+ AST_RWLIST_UNLOCK(&users);
+ return CLI_SUCCESS;
+ }
+
+ ast_cli(a->fd,"\n");
+ ast_cli(a->fd,
+ " username: %s\n"
+ " secret: %s\n"
+ " acl: %s\n"
+ " read perm: %s\n"
+ " write perm: %s\n"
+ "displayconnects: %s\n",
+ (user->username ? user->username : "(N/A)"),
+ (user->secret ? "<Set>" : "(N/A)"),
+ (user->ha ? "yes" : "no"),
+ authority_to_str(user->readperm, &rauthority),
+ authority_to_str(user->writeperm, &wauthority),
+ (user->displayconnects ? "yes" : "no"));
+
+ AST_RWLIST_UNLOCK(&users);
+
+ return CLI_SUCCESS;
+}
+
+
+static char *handle_showmanagers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_manager_user *user = NULL;
+ int count_amu = 0;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "manager show users";
+ e->usage =
+ "Usage: manager show users\n"
+ " Prints a listing of all managers that are currently configured on that\n"
+ " system.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+
+ AST_RWLIST_RDLOCK(&users);
+
+ /* If there are no users, print out something along those lines */
+ if (AST_RWLIST_EMPTY(&users)) {
+ ast_cli(a->fd, "There are no manager users.\n");
+ AST_RWLIST_UNLOCK(&users);
+ return CLI_SUCCESS;
+ }
+
+ ast_cli(a->fd, "\nusername\n--------\n");
+
+ AST_RWLIST_TRAVERSE(&users, user, list) {
+ ast_cli(a->fd, "%s\n", user->username);
+ count_amu++;
+ }
+
+ AST_RWLIST_UNLOCK(&users);
+
+ ast_cli(a->fd,"-------------------\n");
+ ast_cli(a->fd,"%d manager users configured.\n", count_amu);
+
+ return CLI_SUCCESS;
+}
+
+
+/*! \brief CLI command manager list commands */
+static char *handle_showmancmds(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct manager_action *cur;
+ struct ast_str *authority;
+ static const char *format = " %-15.15s %-15.15s %-55.55s\n";
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "manager show commands";
+ e->usage =
+ "Usage: manager show commands\n"
+ " Prints a listing of all the available Asterisk manager interface commands.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ authority = ast_str_alloca(80);
+ ast_cli(a->fd, format, "Action", "Privilege", "Synopsis");
+ ast_cli(a->fd, format, "------", "---------", "--------");
+
+ AST_RWLIST_RDLOCK(&actions);
+ AST_RWLIST_TRAVERSE(&actions, cur, list)
+ ast_cli(a->fd, format, cur->action, authority_to_str(cur->authority, &authority), cur->synopsis);
+ AST_RWLIST_UNLOCK(&actions);
+
+ return CLI_SUCCESS;
+}
+
+/*! \brief CLI command manager list connected */
+static char *handle_showmanconn(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct mansession *s;
+ time_t now = time(NULL);
+ static const char *format = " %-15.15s %-15.15s %-10.10s %-10.10s %-8.8s %-8.8s %-5.5s %-5.5s\n";
+ static const char *format2 = " %-15.15s %-15.15s %-10d %-10d %-8d %-8d %-5.5d %-5.5d\n";
+ int count = 0;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "manager show connected";
+ e->usage =
+ "Usage: manager show connected\n"
+ " Prints a listing of the users that are currently connected to the\n"
+ "Asterisk manager interface.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ ast_cli(a->fd, format, "Username", "IP Address", "Start", "Elapsed", "FileDes", "HttpCnt", "Read", "Write");
+
+ AST_LIST_LOCK(&sessions);
+ AST_LIST_TRAVERSE(&sessions, s, list) {
+ ast_cli(a->fd, format2, s->username, ast_inet_ntoa(s->sin.sin_addr), (int)(s->sessionstart), (int)(now - s->sessionstart), s->fd, s->inuse, s->readperm, s->writeperm);
+ count++;
+ }
+ AST_LIST_UNLOCK(&sessions);
+
+ ast_cli(a->fd, "%d users connected.\n", count);
+
+ return CLI_SUCCESS;
+}
+
+/*! \brief CLI command manager list eventq */
+/* Should change to "manager show connected" */
+static char *handle_showmaneventq(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct eventqent *s;
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "manager show eventq";
+ e->usage =
+ "Usage: manager show eventq\n"
+ " Prints a listing of all events pending in the Asterisk manger\n"
+ "event queue.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ AST_LIST_LOCK(&all_events);
+ AST_LIST_TRAVERSE(&all_events, s, eq_next) {
+ ast_cli(a->fd, "Usecount: %d\n",s->usecount);
+ ast_cli(a->fd, "Category: %d\n", s->category);
+ ast_cli(a->fd, "Event:\n%s", s->eventdata);
+ }
+ AST_LIST_UNLOCK(&all_events);
+
+ return CLI_SUCCESS;
+}
+
+/*! \brief CLI command manager reload */
+static char *handle_manager_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "manager reload";
+ e->usage =
+ "Usage: manager reload\n"
+ " Reloads the manager configuration.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ if (a->argc > 2)
+ return CLI_SHOWUSAGE;
+ reload_manager();
+ return CLI_SUCCESS;
+}
+
+
+static struct ast_cli_entry cli_manager[] = {
+ AST_CLI_DEFINE(handle_showmancmd, "Show a manager interface command"),
+ AST_CLI_DEFINE(handle_showmancmds, "List manager interface commands"),
+ AST_CLI_DEFINE(handle_showmanconn, "List connected manager interface users"),
+ AST_CLI_DEFINE(handle_showmaneventq, "List manager interface queued events"),
+ AST_CLI_DEFINE(handle_showmanagers, "List configured manager users"),
+ AST_CLI_DEFINE(handle_showmanager, "Display information on a specific manager user"),
+ AST_CLI_DEFINE(handle_mandebug, "Show, enable, disable debugging of the manager code"),
+ AST_CLI_DEFINE(handle_manager_reload, "Reload manager configurations"),
+};
+
+/*
+ * Decrement the usecount for the event; if it goes to zero,
+ * (why check for e->next ?) wakeup the
+ * main thread, which is in charge of freeing the record.
+ * Returns the next record.
+ */
+static struct eventqent *unref_event(struct eventqent *e)
+{
+ ast_atomic_fetchadd_int(&e->usecount, -1);
+ return AST_LIST_NEXT(e, eq_next);
+}
+
+static void ref_event(struct eventqent *e)
+{
+ ast_atomic_fetchadd_int(&e->usecount, 1);
+}
+
+/*
+ * destroy a session, leaving the usecount
+ */
+static void free_session(struct mansession *s)
+{
+ struct eventqent *eqe = s->last_ev;
+ if (s->f != NULL)
+ fclose(s->f);
+ ast_mutex_destroy(&s->__lock);
+ ast_free(s);
+ unref_event(eqe);
+}
+
+static void destroy_session(struct mansession *s)
+{
+ AST_LIST_LOCK(&sessions);
+ AST_LIST_REMOVE(&sessions, s, list);
+ ast_atomic_fetchadd_int(&num_sessions, -1);
+ free_session(s);
+ AST_LIST_UNLOCK(&sessions);
+}
+
+const char *astman_get_header(const struct message *m, char *var)
+{
+ int x, l = strlen(var);
+
+ for (x = 0; x < m->hdrcount; x++) {
+ const char *h = m->headers[x];
+ if (!strncasecmp(var, h, l) && h[l] == ':' && h[l+1] == ' ')
+ return h + l + 2;
+ }
+
+ return "";
+}
+
+struct ast_variable *astman_get_variables(const struct message *m)
+{
+ int varlen, x, y;
+ struct ast_variable *head = NULL, *cur;
+
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(vars)[32];
+ );
+
+ varlen = strlen("Variable: ");
+
+ for (x = 0; x < m->hdrcount; x++) {
+ char *parse, *var, *val;
+
+ if (strncasecmp("Variable: ", m->headers[x], varlen))
+ continue;
+ parse = ast_strdupa(m->headers[x] + varlen);
+
+ AST_STANDARD_APP_ARGS(args, parse);
+ if (!args.argc)
+ continue;
+ for (y = 0; y < args.argc; y++) {
+ if (!args.vars[y])
+ continue;
+ var = val = ast_strdupa(args.vars[y]);
+ strsep(&val, "=");
+ if (!val || ast_strlen_zero(var))
+ continue;
+ cur = ast_variable_new(var, val, "");
+ cur->next = head;
+ head = cur;
+ }
+ }
+
+ return head;
+}
+
+/*!
+ * helper function to send a string to the socket.
+ * Return -1 on error (e.g. buffer full).
+ */
+static int send_string(struct mansession *s, char *string)
+{
+ int len = strlen(string); /* residual length */
+ char *src = string;
+ struct timeval start = ast_tvnow();
+ int n = 0;
+
+ for (;;) {
+ int elapsed;
+ struct pollfd fd;
+ n = fwrite(src, 1, len, s->f); /* try to write the string, non blocking */
+ if (n == len /* ok */ || n < 0 /* error */)
+ break;
+ len -= n; /* skip already written data */
+ src += n;
+ fd.fd = s->fd;
+ fd.events = POLLOUT;
+ n = -1; /* error marker */
+ elapsed = ast_tvdiff_ms(ast_tvnow(), start);
+ if (elapsed > s->writetimeout)
+ break;
+ if (poll(&fd, 1, s->writetimeout - elapsed) < 1)
+ break;
+ }
+ fflush(s->f);
+ return n < 0 ? -1 : 0;
+}
+
+/*!
+ * \brief thread local buffer for astman_append
+ *
+ * \note This can not be defined within the astman_append() function
+ * because it declares a couple of functions that get used to
+ * initialize the thread local storage key.
+ */
+AST_THREADSTORAGE(astman_append_buf);
+/*! \brief initial allocated size for the astman_append_buf */
+#define ASTMAN_APPEND_BUF_INITSIZE 256
+
+/*!
+ * utility functions for creating AMI replies
+ */
+void astman_append(struct mansession *s, const char *fmt, ...)
+{
+ va_list ap;
+ struct ast_str *buf;
+
+ if (!(buf = ast_str_thread_get(&astman_append_buf, ASTMAN_APPEND_BUF_INITSIZE)))
+ return;
+
+ va_start(ap, fmt);
+ ast_str_set_va(&buf, 0, fmt, ap);
+ va_end(ap);
+
+ if (s->f != NULL)
+ send_string(s, buf->str);
+ else
+ ast_verbose("fd == -1 in astman_append, should not happen\n");
+}
+
+/*! \note NOTE: XXX this comment is unclear and possibly wrong.
+ Callers of astman_send_error(), astman_send_response() or astman_send_ack() must EITHER
+ hold the session lock _or_ be running in an action callback (in which case s->busy will
+ be non-zero). In either of these cases, there is no need to lock-protect the session's
+ fd, since no other output will be sent (events will be queued), and no input will
+ be read until either the current action finishes or get_input() obtains the session
+ lock.
+ */
+
+/*! \brief send a response with an optional message,
+ * and terminate it with an empty line.
+ * m is used only to grab the 'ActionID' field.
+ *
+ * Use the explicit constant MSG_MOREDATA to remove the empty line.
+ * XXX MSG_MOREDATA should go to a header file.
+ */
+#define MSG_MOREDATA ((char *)astman_send_response)
+static void astman_send_response_full(struct mansession *s, const struct message *m, char *resp, char *msg, char *listflag)
+{
+ const char *id = astman_get_header(m,"ActionID");
+
+ astman_append(s, "Response: %s\r\n", resp);
+ if (!ast_strlen_zero(id))
+ astman_append(s, "ActionID: %s\r\n", id);
+ if (listflag)
+ astman_append(s, "Eventlist: %s\r\n", listflag); /* Start, complete, cancelled */
+ if (msg == MSG_MOREDATA)
+ return;
+ else if (msg)
+ astman_append(s, "Message: %s\r\n\r\n", msg);
+ else
+ astman_append(s, "\r\n");
+}
+
+void astman_send_response(struct mansession *s, const struct message *m, char *resp, char *msg)
+{
+ astman_send_response_full(s, m, resp, msg, NULL);
+}
+
+void astman_send_error(struct mansession *s, const struct message *m, char *error)
+{
+ astman_send_response_full(s, m, "Error", error, NULL);
+}
+
+void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
+{
+ astman_send_response_full(s, m, "Success", msg, NULL);
+}
+
+static void astman_start_ack(struct mansession *s, const struct message *m)
+{
+ astman_send_response_full(s, m, "Success", MSG_MOREDATA, NULL);
+}
+
+void astman_send_listack(struct mansession *s, const struct message *m, char *msg, char *listflag)
+{
+ astman_send_response_full(s, m, "Success", msg, listflag);
+}
+
+
+/*! \brief
+ Rather than braindead on,off this now can also accept a specific int mask value
+ or a ',' delim list of mask strings (the same as manager.conf) -anthm
+*/
+static int set_eventmask(struct mansession *s, const char *eventmask)
+{
+ int maskint = strings_to_mask(eventmask);
+
+ ast_mutex_lock(&s->__lock);
+ if (maskint >= 0)
+ s->send_events = maskint;
+ ast_mutex_unlock(&s->__lock);
+
+ return maskint;
+}
+
+/*
+ * Here we start with action_ handlers for AMI actions,
+ * and the internal functions used by them.
+ * Generally, the handlers are called action_foo()
+ */
+
+/* helper function for action_login() */
+static int authenticate(struct mansession *s, const struct message *m)
+{
+ const char *username = astman_get_header(m, "Username");
+ const char *password = astman_get_header(m, "Secret");
+ int error = -1;
+ struct ast_manager_user *user = NULL;
+
+ if (ast_strlen_zero(username)) /* missing username */
+ return -1;
+
+ /* locate user in locked state */
+ AST_RWLIST_WRLOCK(&users);
+
+ if (!(user = get_manager_by_name_locked(username))) {
+ ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(s->sin.sin_addr), username);
+ } else if (user->ha && !ast_apply_ha(user->ha, &(s->sin))) {
+ ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), username);
+ } else if (!strcasecmp(astman_get_header(m, "AuthType"), "MD5")) {
+ const char *key = astman_get_header(m, "Key");
+ if (!ast_strlen_zero(key) && !ast_strlen_zero(s->challenge) && user->secret) {
+ int x;
+ int len = 0;
+ char md5key[256] = "";
+ struct MD5Context md5;
+ unsigned char digest[16];
+
+ MD5Init(&md5);
+ MD5Update(&md5, (unsigned char *) s->challenge, strlen(s->challenge));
+ MD5Update(&md5, (unsigned char *) user->secret, strlen(user->secret));
+ MD5Final(digest, &md5);
+ for (x=0; x<16; x++)
+ len += sprintf(md5key + len, "%2.2x", digest[x]);
+ if (!strcmp(md5key, key))
+ error = 0;
+ } else {
+ ast_debug(1, "MD5 authentication is not possible. challenge: '%s'\n",
+ S_OR(s->challenge, ""));
+ }
+ } else if (password && user->secret && !strcmp(password, user->secret))
+ error = 0;
+
+ if (error) {
+ ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), username);
+ AST_RWLIST_UNLOCK(&users);
+ return -1;
+ }
+
+ /* auth complete */
+
+ ast_copy_string(s->username, username, sizeof(s->username));
+ s->readperm = user->readperm;
+ s->writeperm = user->writeperm;
+ s->writetimeout = user->writetimeout;
+ s->sessionstart = time(NULL);
+ set_eventmask(s, astman_get_header(m, "Events"));
+
+ AST_RWLIST_UNLOCK(&users);
+ return 0;
+}
+
+/*! \brief Manager PING */
+static char mandescr_ping[] =
+"Description: A 'Ping' action will ellicit a 'Pong' response. Used to keep the\n"
+" manager connection open.\n"
+"Variables: NONE\n";
+
+static int action_ping(struct mansession *s, const struct message *m)
+{
+ astman_send_response(s, m, "Success", "Ping: Pong\r\n");
+ return 0;
+}
+
+static char mandescr_getconfig[] =
+"Description: A 'GetConfig' action will dump the contents of a configuration\n"
+"file by category and contents.\n"
+"Variables:\n"
+" Filename: Configuration filename (e.g. foo.conf)\n";
+
+static int action_getconfig(struct mansession *s, const struct message *m)
+{
+ struct ast_config *cfg;
+ const char *fn = astman_get_header(m, "Filename");
+ int catcount = 0;
+ int lineno = 0;
+ char *category=NULL;
+ struct ast_variable *v;
+ struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
+
+ if (ast_strlen_zero(fn)) {
+ astman_send_error(s, m, "Filename not specified");
+ return 0;
+ }
+ if (!(cfg = ast_config_load(fn, config_flags))) {
+ astman_send_error(s, m, "Config file not found");
+ return 0;
+ }
+ astman_start_ack(s, m);
+ while ((category = ast_category_browse(cfg, category))) {
+ lineno = 0;
+ astman_append(s, "Category-%06d: %s\r\n", catcount, category);
+ for (v = ast_variable_browse(cfg, category); v; v = v->next)
+ astman_append(s, "Line-%06d-%06d: %s=%s\r\n", catcount, lineno++, v->name, v->value);
+ catcount++;
+ }
+ ast_config_destroy(cfg);
+ astman_append(s, "\r\n");
+
+ return 0;
+}
+
+/*! The amount of space in out must be at least ( 2 * strlen(in) + 1 ) */
+static void json_escape(char *out, const char *in)
+{
+ for (; *in; in++) {
+ if (*in == '\\' || *in == '\"')
+ *out++ = '\\';
+ *out++ = *in;
+ }
+ *out = '\0';
+}
+
+static char mandescr_getconfigjson[] =
+"Description: A 'GetConfigJSON' action will dump the contents of a configuration\n"
+"file by category and contents in JSON format. This only makes sense to be used\n"
+"using rawman over the HTTP interface.\n"
+"Variables:\n"
+" Filename: Configuration filename (e.g. foo.conf)\n";
+
+static int action_getconfigjson(struct mansession *s, const struct message *m)
+{
+ struct ast_config *cfg;
+ const char *fn = astman_get_header(m, "Filename");
+ char *category = NULL;
+ struct ast_variable *v;
+ int comma1 = 0;
+ char *buf = NULL;
+ unsigned int buf_len = 0;
+ struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
+
+ if (ast_strlen_zero(fn)) {
+ astman_send_error(s, m, "Filename not specified");
+ return 0;
+ }
+
+ if (!(cfg = ast_config_load(fn, config_flags))) {
+ astman_send_error(s, m, "Config file not found");
+ return 0;
+ }
+
+ buf_len = 512;
+ buf = alloca(buf_len);
+
+ astman_start_ack(s, m);
+ astman_append(s, "JSON: {");
+ while ((category = ast_category_browse(cfg, category))) {
+ int comma2 = 0;
+ if (buf_len < 2 * strlen(category) + 1) {
+ buf_len *= 2;
+ buf = alloca(buf_len);
+ }
+ json_escape(buf, category);
+ astman_append(s, "%s\"%s\":[", comma1 ? "," : "", buf);
+ if (!comma1)
+ comma1 = 1;
+ for (v = ast_variable_browse(cfg, category); v; v = v->next) {
+ if (comma2)
+ astman_append(s, ",");
+ if (buf_len < 2 * strlen(v->name) + 1) {
+ buf_len *= 2;
+ buf = alloca(buf_len);
+ }
+ json_escape(buf, v->name);
+ astman_append(s, "\"%s", buf);
+ if (buf_len < 2 * strlen(v->value) + 1) {
+ buf_len *= 2;
+ buf = alloca(buf_len);
+ }
+ json_escape(buf, v->value);
+ astman_append(s, "%s\"", buf);
+ if (!comma2)
+ comma2 = 1;
+ }
+ astman_append(s, "]");
+ }
+ astman_append(s, "}\r\n\r\n");
+
+ ast_config_destroy(cfg);
+
+ return 0;
+}
+
+/* helper function for action_updateconfig */
+static void handle_updates(struct mansession *s, const struct message *m, struct ast_config *cfg, const char *dfn)
+{
+ int x;
+ char hdr[40];
+ const char *action, *cat, *var, *value, *match;
+ struct ast_category *category;
+ struct ast_variable *v;
+
+ for (x = 0; x < 100000; x++) {
+ unsigned int object = 0;
+
+ snprintf(hdr, sizeof(hdr), "Action-%06d", x);
+ action = astman_get_header(m, hdr);
+ if (ast_strlen_zero(action))
+ break;
+ snprintf(hdr, sizeof(hdr), "Cat-%06d", x);
+ cat = astman_get_header(m, hdr);
+ snprintf(hdr, sizeof(hdr), "Var-%06d", x);
+ var = astman_get_header(m, hdr);
+ snprintf(hdr, sizeof(hdr), "Value-%06d", x);
+ value = astman_get_header(m, hdr);
+ if (!ast_strlen_zero(value) && *value == '>') {
+ object = 1;
+ value++;
+ }
+ snprintf(hdr, sizeof(hdr), "Match-%06d", x);
+ match = astman_get_header(m, hdr);
+ if (!strcasecmp(action, "newcat")) {
+ if (!ast_strlen_zero(cat)) {
+ category = ast_category_new(cat, dfn, 99999);
+ if (category) {
+ ast_category_append(cfg, category);
+ }
+ }
+ } else if (!strcasecmp(action, "renamecat")) {
+ if (!ast_strlen_zero(cat) && !ast_strlen_zero(value)) {
+ category = ast_category_get(cfg, cat);
+ if (category)
+ ast_category_rename(category, value);
+ }
+ } else if (!strcasecmp(action, "delcat")) {
+ if (!ast_strlen_zero(cat))
+ ast_category_delete(cfg, cat);
+ } else if (!strcasecmp(action, "update")) {
+ if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) && (category = ast_category_get(cfg, cat)))
+ ast_variable_update(category, var, value, match, object);
+ } else if (!strcasecmp(action, "delete")) {
+ if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) && (category = ast_category_get(cfg, cat)))
+ ast_variable_delete(category, var, match);
+ } else if (!strcasecmp(action, "append")) {
+ if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) &&
+ (category = ast_category_get(cfg, cat)) &&
+ (v = ast_variable_new(var, value, dfn))){
+ if (object || (match && !strcasecmp(match, "object")))
+ v->object = 1;
+ ast_variable_append(category, v);
+ }
+ }
+ }
+}
+
+static char mandescr_updateconfig[] =
+"Description: A 'UpdateConfig' action will modify, create, or delete\n"
+"configuration elements in Asterisk configuration files.\n"
+"Variables (X's represent 6 digit number beginning with 000000):\n"
+" SrcFilename: Configuration filename to read(e.g. foo.conf)\n"
+" DstFilename: Configuration filename to write(e.g. foo.conf)\n"
+" Reload: Whether or not a reload should take place (or name of specific module)\n"
+" Action-XXXXXX: Action to Take (NewCat,RenameCat,DelCat,Update,Delete,Append)\n"
+" Cat-XXXXXX: Category to operate on\n"
+" Var-XXXXXX: Variable to work on\n"
+" Value-XXXXXX: Value to work on\n"
+" Match-XXXXXX: Extra match required to match line\n";
+
+static int action_updateconfig(struct mansession *s, const struct message *m)
+{
+ struct ast_config *cfg;
+ const char *sfn = astman_get_header(m, "SrcFilename");
+ const char *dfn = astman_get_header(m, "DstFilename");
+ int res;
+ const char *rld = astman_get_header(m, "Reload");
+ struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS | CONFIG_FLAG_NOCACHE };
+
+ if (ast_strlen_zero(sfn) || ast_strlen_zero(dfn)) {
+ astman_send_error(s, m, "Filename not specified");
+ return 0;
+ }
+ if (!(cfg = ast_config_load(sfn, config_flags))) {
+ astman_send_error(s, m, "Config file not found");
+ return 0;
+ }
+ handle_updates(s, m, cfg, dfn);
+ ast_include_rename(cfg, sfn, dfn); /* change the include references from dfn to sfn, so things match up */
+ res = config_text_file_save(dfn, cfg, "Manager");
+ ast_config_destroy(cfg);
+ if (res) {
+ astman_send_error(s, m, "Save of config failed");
+ return 0;
+ }
+ astman_send_ack(s, m, NULL);
+ if (!ast_strlen_zero(rld)) {
+ if (ast_true(rld))
+ rld = NULL;
+ ast_module_reload(rld);
+ }
+ return 0;
+}
+
+/*! \brief Manager WAITEVENT */
+static char mandescr_waitevent[] =
+"Description: A 'WaitEvent' action will ellicit a 'Success' response. Whenever\n"
+"a manager event is queued. Once WaitEvent has been called on an HTTP manager\n"
+"session, events will be generated and queued.\n"
+"Variables: \n"
+" Timeout: Maximum time (in seconds) to wait for events, -1 means forever.\n";
+
+static int action_waitevent(struct mansession *s, const struct message *m)
+{
+ const char *timeouts = astman_get_header(m, "Timeout");
+ int timeout = -1;
+ int x;
+ int needexit = 0;
+ const char *id = astman_get_header(m,"ActionID");
+ char idText[256] = "";
+
+ if (!ast_strlen_zero(id))
+ snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
+
+ if (!ast_strlen_zero(timeouts)) {
+ sscanf(timeouts, "%i", &timeout);
+ if (timeout < -1)
+ timeout = -1;
+ /* XXX maybe put an upper bound, or prevent the use of 0 ? */
+ }
+
+ ast_mutex_lock(&s->__lock);
+ if (s->waiting_thread != AST_PTHREADT_NULL)
+ pthread_kill(s->waiting_thread, SIGURG);
+
+ if (s->managerid) { /* AMI-over-HTTP session */
+ /*
+ * Make sure the timeout is within the expire time of the session,
+ * as the client will likely abort the request if it does not see
+ * data coming after some amount of time.
+ */
+ time_t now = time(NULL);
+ int max = s->sessiontimeout - now - 10;
+
+ if (max < 0) /* We are already late. Strange but possible. */
+ max = 0;
+ if (timeout < 0 || timeout > max)
+ timeout = max;
+ if (!s->send_events) /* make sure we record events */
+ s->send_events = -1;
+ }
+ ast_mutex_unlock(&s->__lock);
+
+ /* XXX should this go inside the lock ? */
+ s->waiting_thread = pthread_self(); /* let new events wake up this thread */
+ ast_debug(1, "Starting waiting for an event!\n");
+
+ for (x=0; x < timeout || timeout < 0; x++) {
+ ast_mutex_lock(&s->__lock);
+ if (NEW_EVENT(s))
+ needexit = 1;
+ /* We can have multiple HTTP session point to the same mansession entry.
+ * The way we deal with it is not very nice: newcomers kick out the previous
+ * HTTP session. XXX this needs to be improved.
+ */
+ if (s->waiting_thread != pthread_self())
+ needexit = 1;
+ if (s->needdestroy)
+ needexit = 1;
+ ast_mutex_unlock(&s->__lock);
+ if (needexit)
+ break;
+ if (s->managerid == 0) { /* AMI session */
+ if (ast_wait_for_input(s->fd, 1000))
+ break;
+ } else { /* HTTP session */
+ sleep(1);
+ }
+ }
+ ast_debug(1, "Finished waiting for an event!\n");
+ ast_mutex_lock(&s->__lock);
+ if (s->waiting_thread == pthread_self()) {
+ struct eventqent *eqe;
+ astman_send_response(s, m, "Success", "Waiting for Event completed.");
+ while ( (eqe = NEW_EVENT(s)) ) {
+ ref_event(eqe);
+ if (((s->readperm & eqe->category) == eqe->category) &&
+ ((s->send_events & eqe->category) == eqe->category)) {
+ astman_append(s, "%s", eqe->eventdata);
+ }
+ s->last_ev = unref_event(s->last_ev);
+ }
+ astman_append(s,
+ "Event: WaitEventComplete\r\n"
+ "%s"
+ "\r\n", idText);
+ s->waiting_thread = AST_PTHREADT_NULL;
+ } else {
+ ast_debug(1, "Abandoning event request!\n");
+ }
+ ast_mutex_unlock(&s->__lock);
+ return 0;
+}
+
+static char mandescr_listcommands[] =
+"Description: Returns the action name and synopsis for every\n"
+" action that is available to the user\n"
+"Variables: NONE\n";
+
+/*! \note The actionlock is read-locked by the caller of this function */
+static int action_listcommands(struct mansession *s, const struct message *m)
+{
+ struct manager_action *cur;
+ struct ast_str *temp = ast_str_alloca(BUFSIZ); /* XXX very large ? */
+
+ astman_start_ack(s, m);
+ AST_RWLIST_TRAVERSE(&actions, cur, list) {
+ if (s->writeperm & cur->authority || cur->authority == 0)
+ astman_append(s, "%s: %s (Priv: %s)\r\n",
+ cur->action, cur->synopsis, authority_to_str(cur->authority, &temp));
+ }
+ astman_append(s, "\r\n");
+
+ return 0;
+}
+
+static char mandescr_events[] =
+"Description: Enable/Disable sending of events to this manager\n"
+" client.\n"
+"Variables:\n"
+" EventMask: 'on' if all events should be sent,\n"
+" 'off' if no events should be sent,\n"
+" 'system,call,log' to select which flags events should have to be sent.\n";
+
+static int action_events(struct mansession *s, const struct message *m)
+{
+ const char *mask = astman_get_header(m, "EventMask");
+ int res;
+
+ res = set_eventmask(s, mask);
+ if (res > 0)
+ astman_send_response(s, m, "Success", "Events: On\r\n");
+ else if (res == 0)
+ astman_send_response(s, m, "Success", "Events: Off\r\n");
+
+ return 0;
+}
+
+static char mandescr_logoff[] =
+"Description: Logoff this manager session\n"
+"Variables: NONE\n";
+
+static int action_logoff(struct mansession *s, const struct message *m)
+{
+ astman_send_response(s, m, "Goodbye", "Thanks for all the fish.");
+ return -1;
+}
+
+static int action_login(struct mansession *s, const struct message *m)
+{
+ if (authenticate(s, m)) {
+ sleep(1);
+ astman_send_error(s, m, "Authentication failed");
+ return -1;
+ }
+ s->authenticated = 1;
+ if (manager_displayconnects(s))
+ ast_verb(2, "%sManager '%s' logged on from %s\n", (s->managerid ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
+ ast_log(LOG_EVENT, "%sManager '%s' logged on from %s\n", (s->managerid ? "HTTP " : ""), s->username, ast_inet_ntoa(s->sin.sin_addr));
+ astman_send_ack(s, m, "Authentication accepted");
+ return 0;
+}
+
+static int action_challenge(struct mansession *s, const struct message *m)
+{
+ const char *authtype = astman_get_header(m, "AuthType");
+
+ if (!strcasecmp(authtype, "MD5")) {
+ if (ast_strlen_zero(s->challenge))
+ snprintf(s->challenge, sizeof(s->challenge), "%ld", ast_random());
+ ast_mutex_lock(&s->__lock);
+ astman_start_ack(s, m);
+ astman_append(s, "Challenge: %s\r\n\r\n", s->challenge);
+ ast_mutex_unlock(&s->__lock);
+ } else {
+ astman_send_error(s, m, "Must specify AuthType");
+ }
+ return 0;
+}
+
+static char mandescr_hangup[] =
+"Description: Hangup a channel\n"
+"Variables: \n"
+" Channel: The channel name to be hungup\n";
+
+static int action_hangup(struct mansession *s, const struct message *m)
+{
+ struct ast_channel *c = NULL;
+ const char *name = astman_get_header(m, "Channel");
+ if (ast_strlen_zero(name)) {
+ astman_send_error(s, m, "No channel specified");
+ return 0;
+ }
+ c = ast_get_channel_by_name_locked(name);
+ if (!c) {
+ astman_send_error(s, m, "No such channel");
+ return 0;
+ }
+ ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
+ ast_channel_unlock(c);
+ astman_send_ack(s, m, "Channel Hungup");
+ return 0;
+}
+
+static char mandescr_setvar[] =
+"Description: Set a global or local channel variable.\n"
+"Variables: (Names marked with * are required)\n"
+" Channel: Channel to set variable for\n"
+" *Variable: Variable name\n"
+" *Value: Value\n";
+
+static int action_setvar(struct mansession *s, const struct message *m)
+{
+ struct ast_channel *c = NULL;
+ const char *name = astman_get_header(m, "Channel");
+ const char *varname = astman_get_header(m, "Variable");
+ const char *varval = astman_get_header(m, "Value");
+
+ if (ast_strlen_zero(varname)) {
+ astman_send_error(s, m, "No variable specified");
+ return 0;
+ }
+
+ if (!ast_strlen_zero(name)) {
+ c = ast_get_channel_by_name_locked(name);
+ if (!c) {
+ astman_send_error(s, m, "No such channel");
+ return 0;
+ }
+ }
+
+ pbx_builtin_setvar_helper(c, varname, S_OR(varval, ""));
+
+ if (c)
+ ast_channel_unlock(c);
+
+ astman_send_ack(s, m, "Variable Set");
+
+ return 0;
+}
+
+static char mandescr_getvar[] =
+"Description: Get the value of a global or local channel variable.\n"
+"Variables: (Names marked with * are required)\n"
+" Channel: Channel to read variable from\n"
+" *Variable: Variable name\n"
+" ActionID: Optional Action id for message matching.\n";
+
+static int action_getvar(struct mansession *s, const struct message *m)
+{
+ struct ast_channel *c = NULL;
+ const char *name = astman_get_header(m, "Channel");
+ const char *varname = astman_get_header(m, "Variable");
+ char *varval;
+ char workspace[1024] = "";
+
+ if (ast_strlen_zero(varname)) {
+ astman_send_error(s, m, "No variable specified");
+ return 0;
+ }
+
+ if (!ast_strlen_zero(name)) {
+ c = ast_get_channel_by_name_locked(name);
+ if (!c) {
+ astman_send_error(s, m, "No such channel");
+ return 0;
+ }
+ }
+
+ if (varname[strlen(varname) - 1] == ')') {
+ ast_func_read(c, (char *) varname, workspace, sizeof(workspace));
+ varval = workspace;
+ } else {
+ pbx_retrieve_variable(c, varname, &varval, workspace, sizeof(workspace), NULL);
+ }
+
+ if (c)
+ ast_channel_unlock(c);
+ astman_start_ack(s, m);
+ astman_append(s, "Variable: %s\r\nValue: %s\r\n\r\n", varname, varval);
+
+ return 0;
+}
+
+
+/*! \brief Manager "status" command to show channels */
+/* Needs documentation... */
+static int action_status(struct mansession *s, const struct message *m)
+{
+ const char *name = astman_get_header(m,"Channel");
+ struct ast_channel *c;
+ char bridge[256];
+ struct timeval now = ast_tvnow();
+ long elapsed_seconds = 0;
+ int channels = 0;
+ int all = ast_strlen_zero(name); /* set if we want all channels */
+ const char *id = astman_get_header(m,"ActionID");
+ char idText[256] = "";
+
+ if (!ast_strlen_zero(id))
+ snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
+
+ if (all)
+ c = ast_channel_walk_locked(NULL);
+ else {
+ c = ast_get_channel_by_name_locked(name);
+ if (!c) {
+ astman_send_error(s, m, "No such channel");
+ return 0;
+ }
+ }
+ astman_send_ack(s, m, "Channel status will follow");
+
+ /* if we look by name, we break after the first iteration */
+ while (c) {
+ channels++;
+ if (c->_bridge)
+ snprintf(bridge, sizeof(bridge), "BridgedChannel: %s\r\nBridgedUniqueid: %s\r\n", c->_bridge->name, c->_bridge->uniqueid);
+ else
+ bridge[0] = '\0';
+ if (c->pbx) {
+ if (c->cdr) {
+ elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
+ }
+ astman_append(s,
+ "Event: Status\r\n"
+ "Privilege: Call\r\n"
+ "Channel: %s\r\n"
+ "CallerIDNum: %s\r\n"
+ "CallerIDName: %s\r\n"
+ "Accountcode: %s\r\n"
+ "ChannelState: %d\r\n"
+ "ChannelStateDesc: %s\r\n"
+ "Context: %s\r\n"
+ "Extension: %s\r\n"
+ "Priority: %d\r\n"
+ "Seconds: %ld\r\n"
+ "%s"
+ "Uniqueid: %s\r\n"
+ "%s"
+ "\r\n",
+ c->name,
+ S_OR(c->cid.cid_num, ""),
+ S_OR(c->cid.cid_name, ""),
+ c->accountcode,
+ c->_state,
+ ast_state2str(c->_state), c->context,
+ c->exten, c->priority, (long)elapsed_seconds, bridge, c->uniqueid, idText);
+ } else {
+ astman_append(s,
+ "Event: Status\r\n"
+ "Privilege: Call\r\n"
+ "Channel: %s\r\n"
+ "CallerIDNum: %s\r\n"
+ "CallerIDName: %s\r\n"
+ "Account: %s\r\n"
+ "State: %s\r\n"
+ "%s"
+ "Uniqueid: %s\r\n"
+ "%s"
+ "\r\n",
+ c->name,
+ S_OR(c->cid.cid_num, "<unknown>"),
+ S_OR(c->cid.cid_name, "<unknown>"),
+ c->accountcode,
+ ast_state2str(c->_state), bridge, c->uniqueid, idText);
+ }
+ ast_channel_unlock(c);
+ if (!all)
+ break;
+ c = ast_channel_walk_locked(c);
+ }
+ astman_append(s,
+ "Event: StatusComplete\r\n"
+ "%s"
+ "Items: %d\r\n"
+ "\r\n",idText, channels);
+ return 0;
+}
+
+static char mandescr_sendtext[] =
+"Description: Sends A Text Message while in a call.\n"
+"Variables: (Names marked with * are required)\n"
+" *Channel: Channel to send message to\n"
+" *Message: Message to send\n"
+" ActionID: Optional Action id for message matching.\n";
+
+static int action_sendtext(struct mansession *s, const struct message *m)
+{
+ struct ast_channel *c = NULL;
+ const char *name = astman_get_header(m, "Channel");
+ const char *textmsg = astman_get_header(m, "Message");
+ int res = 0;
+
+ if (ast_strlen_zero(name)) {
+ astman_send_error(s, m, "No channel specified");
+ return 0;
+ }
+
+ if (ast_strlen_zero(textmsg)) {
+ astman_send_error(s, m, "No Message specified");
+ return 0;
+ }
+
+ c = ast_get_channel_by_name_locked(name);
+ if (!c) {
+ astman_send_error(s, m, "No such channel");
+ return 0;
+ }
+
+ res = ast_sendtext(c, textmsg);
+ ast_channel_unlock(c);
+
+ if (res > 0)
+ astman_send_ack(s, m, "Success");
+ else
+ astman_send_error(s, m, "Failure");
+
+ return res;
+}
+
+static char mandescr_redirect[] =
+"Description: Redirect (transfer) a call.\n"
+"Variables: (Names marked with * are required)\n"
+" *Channel: Channel to redirect\n"
+" ExtraChannel: Second call leg to transfer (optional)\n"
+" *Exten: Extension to transfer to\n"
+" *Context: Context to transfer to\n"
+" *Priority: Priority to transfer to\n"
+" ActionID: Optional Action id for message matching.\n";
+
+/*! \brief action_redirect: The redirect manager command */
+static int action_redirect(struct mansession *s, const struct message *m)
+{
+ const char *name = astman_get_header(m, "Channel");
+ const char *name2 = astman_get_header(m, "ExtraChannel");
+ const char *exten = astman_get_header(m, "Exten");
+ const char *context = astman_get_header(m, "Context");
+ const char *priority = astman_get_header(m, "Priority");
+ struct ast_channel *chan, *chan2 = NULL;
+ int pi = 0;
+ int res;
+
+ if (ast_strlen_zero(name)) {
+ astman_send_error(s, m, "Channel not specified");
+ return 0;
+ }
+ if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
+ if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
+ astman_send_error(s, m, "Invalid priority\n");
+ return 0;
+ }
+ }
+ /* XXX watch out, possible deadlock - we are trying to get two channels!!! */
+ chan = ast_get_channel_by_name_locked(name);
+ if (!chan) {
+ char buf[BUFSIZ];
+ snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
+ astman_send_error(s, m, buf);
+ return 0;
+ }
+ if (ast_check_hangup(chan)) {
+ astman_send_error(s, m, "Redirect failed, channel not up.\n");
+ ast_channel_unlock(chan);
+ return 0;
+ }
+ if (!ast_strlen_zero(name2))
+ chan2 = ast_get_channel_by_name_locked(name2);
+ if (chan2 && ast_check_hangup(chan2)) {
+ astman_send_error(s, m, "Redirect failed, extra channel not up.\n");
+ ast_channel_unlock(chan);
+ ast_channel_unlock(chan2);
+ return 0;
+ }
+ res = ast_async_goto(chan, context, exten, pi);
+ if (!res) {
+ if (!ast_strlen_zero(name2)) {
+ if (chan2)
+ res = ast_async_goto(chan2, context, exten, pi);
+ else
+ res = -1;
+ if (!res)
+ astman_send_ack(s, m, "Dual Redirect successful");
+ else
+ astman_send_error(s, m, "Secondary redirect failed");
+ } else
+ astman_send_ack(s, m, "Redirect successful");
+ } else
+ astman_send_error(s, m, "Redirect failed");
+ if (chan)
+ ast_channel_unlock(chan);
+ if (chan2)
+ ast_channel_unlock(chan2);
+ return 0;
+}
+
+static char mandescr_command[] =
+"Description: Run a CLI command.\n"
+"Variables: (Names marked with * are required)\n"
+" *Command: Asterisk CLI command to run\n"
+" ActionID: Optional Action id for message matching.\n";
+
+/*! \brief Manager command "command" - execute CLI command */
+static int action_command(struct mansession *s, const struct message *m)
+{
+ const char *cmd = astman_get_header(m, "Command");
+ const char *id = astman_get_header(m, "ActionID");
+ char *buf, *final_buf;
+ char template[] = "/tmp/ast-ami-XXXXXX"; /* template for temporary file */
+ int fd = mkstemp(template), i = 0;
+ off_t l;
+
+ for (i = 0; i < sizeof(command_blacklist) / sizeof(command_blacklist[0]); i++) {
+ if (!strncmp(cmd, command_blacklist[i], strlen(command_blacklist[i]))) {
+ astman_send_error(s, m, "Command blacklisted");
+ return 0;
+ }
+ }
+
+ astman_append(s, "Response: Follows\r\nPrivilege: Command\r\n");
+ if (!ast_strlen_zero(id))
+ astman_append(s, "ActionID: %s\r\n", id);
+ /* FIXME: Wedge a ActionID response in here, waiting for later changes */
+ ast_cli_command(fd, cmd); /* XXX need to change this to use a FILE * */
+ l = lseek(fd, 0, SEEK_END); /* how many chars available */
+
+ /* This has a potential to overflow the stack. Hence, use the heap. */
+ buf = ast_calloc(1, l + 1);
+ final_buf = ast_calloc(1, l + 1);
+ if (buf) {
+ lseek(fd, 0, SEEK_SET);
+ read(fd, buf, l);
+ buf[l] = '\0';
+ if (final_buf) {
+ term_strip(final_buf, buf, l);
+ final_buf[l] = '\0';
+ }
+ astman_append(s, S_OR(final_buf, buf));
+ ast_free(buf);
+ }
+ close(fd);
+ unlink(template);
+ astman_append(s, "--END COMMAND--\r\n\r\n");
+ if (final_buf)
+ ast_free(final_buf);
+ return 0;
+}
+
+/* helper function for originate */
+struct fast_originate_helper {
+ char tech[AST_MAX_EXTENSION];
+ char data[AST_MAX_EXTENSION];
+ int timeout;
+ char app[AST_MAX_APP];
+ char appdata[AST_MAX_EXTENSION];
+ char cid_name[AST_MAX_EXTENSION];
+ char cid_num[AST_MAX_EXTENSION];
+ char context[AST_MAX_CONTEXT];
+ char exten[AST_MAX_EXTENSION];
+ char idtext[AST_MAX_EXTENSION];
+ char account[AST_MAX_ACCOUNT_CODE];
+ int priority;
+ struct ast_variable *vars;
+};
+
+static void *fast_originate(void *data)
+{
+ struct fast_originate_helper *in = data;
+ int res;
+ int reason = 0;
+ struct ast_channel *chan = NULL;
+ char requested_channel[AST_CHANNEL_NAME];
+
+ if (!ast_strlen_zero(in->app)) {
+ res = ast_pbx_outgoing_app(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->app, in->appdata, &reason, 1,
+ S_OR(in->cid_num, NULL),
+ S_OR(in->cid_name, NULL),
+ in->vars, in->account, &chan);
+ } else {
+ res = ast_pbx_outgoing_exten(in->tech, AST_FORMAT_SLINEAR, in->data, in->timeout, in->context, in->exten, in->priority, &reason, 1,
+ S_OR(in->cid_num, NULL),
+ S_OR(in->cid_name, NULL),
+ in->vars, in->account, &chan);
+ }
+
+ if (!chan)
+ snprintf(requested_channel, AST_CHANNEL_NAME, "%s/%s", in->tech, in->data);
+ /* Tell the manager what happened with the channel */
+ manager_event(EVENT_FLAG_CALL, "OriginateResponse",
+ "%s"
+ "Response: %s\r\n"
+ "Channel: %s\r\n"
+ "Context: %s\r\n"
+ "Exten: %s\r\n"
+ "Reason: %d\r\n"
+ "Uniqueid: %s\r\n"
+ "CallerIDNum: %s\r\n"
+ "CallerIDName: %s\r\n",
+ in->idtext, res ? "Failure" : "Success", chan ? chan->name : requested_channel, in->context, in->exten, reason,
+ chan ? chan->uniqueid : "<null>",
+ S_OR(in->cid_num, "<unknown>"),
+ S_OR(in->cid_name, "<unknown>")
+ );
+
+ /* Locked by ast_pbx_outgoing_exten or ast_pbx_outgoing_app */
+ if (chan)
+ ast_channel_unlock(chan);
+ ast_free(in);
+ return NULL;
+}
+
+static char mandescr_originate[] =
+"Description: Generates an outgoing call to a Extension/Context/Priority or\n"
+" Application/Data\n"
+"Variables: (Names marked with * are required)\n"
+" *Channel: Channel name to call\n"
+" Exten: Extension to use (requires 'Context' and 'Priority')\n"
+" Context: Context to use (requires 'Exten' and 'Priority')\n"
+" Priority: Priority to use (requires 'Exten' and 'Context')\n"
+" Application: Application to use\n"
+" Data: Data to use (requires 'Application')\n"
+" Timeout: How long to wait for call to be answered (in ms)\n"
+" CallerID: Caller ID to be set on the outgoing channel\n"
+" Variable: Channel variable to set, multiple Variable: headers are allowed\n"
+" Account: Account code\n"
+" Async: Set to 'true' for fast origination\n";
+
+static int action_originate(struct mansession *s, const struct message *m)
+{
+ const char *name = astman_get_header(m, "Channel");
+ const char *exten = astman_get_header(m, "Exten");
+ const char *context = astman_get_header(m, "Context");
+ const char *priority = astman_get_header(m, "Priority");
+ const char *timeout = astman_get_header(m, "Timeout");
+ const char *callerid = astman_get_header(m, "CallerID");
+ const char *account = astman_get_header(m, "Account");
+ const char *app = astman_get_header(m, "Application");
+ const char *appdata = astman_get_header(m, "Data");
+ const char *async = astman_get_header(m, "Async");
+ const char *id = astman_get_header(m, "ActionID");
+ struct ast_variable *vars = astman_get_variables(m);
+ char *tech, *data;
+ char *l = NULL, *n = NULL;
+ int pi = 0;
+ int res;
+ int to = 30000;
+ int reason = 0;
+ char tmp[256];
+ char tmp2[256];
+
+ pthread_t th;
+ if (!name) {
+ astman_send_error(s, m, "Channel not specified");
+ return 0;
+ }
+ if (!ast_strlen_zero(priority) && (sscanf(priority, "%d", &pi) != 1)) {
+ if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
+ astman_send_error(s, m, "Invalid priority\n");
+ return 0;
+ }
+ }
+ if (!ast_strlen_zero(timeout) && (sscanf(timeout, "%d", &to) != 1)) {
+ astman_send_error(s, m, "Invalid timeout\n");
+ return 0;
+ }
+ ast_copy_string(tmp, name, sizeof(tmp));
+ tech = tmp;
+ data = strchr(tmp, '/');
+ if (!data) {
+ astman_send_error(s, m, "Invalid channel\n");
+ return 0;
+ }
+ *data++ = '\0';
+ ast_copy_string(tmp2, callerid, sizeof(tmp2));
+ ast_callerid_parse(tmp2, &n, &l);
+ if (n) {
+ if (ast_strlen_zero(n))
+ n = NULL;
+ }
+ if (l) {
+ ast_shrink_phone_number(l);
+ if (ast_strlen_zero(l))
+ l = NULL;
+ }
+ if (ast_true(async)) {
+ struct fast_originate_helper *fast = ast_calloc(1, sizeof(*fast));
+ if (!fast) {
+ res = -1;
+ } else {
+ if (!ast_strlen_zero(id))
+ snprintf(fast->idtext, sizeof(fast->idtext), "ActionID: %s\r\n", id);
+ ast_copy_string(fast->tech, tech, sizeof(fast->tech));
+ ast_copy_string(fast->data, data, sizeof(fast->data));
+ ast_copy_string(fast->app, app, sizeof(fast->app));
+ ast_copy_string(fast->appdata, appdata, sizeof(fast->appdata));
+ if (l)
+ ast_copy_string(fast->cid_num, l, sizeof(fast->cid_num));
+ if (n)
+ ast_copy_string(fast->cid_name, n, sizeof(fast->cid_name));
+ fast->vars = vars;
+ ast_copy_string(fast->context, context, sizeof(fast->context));
+ ast_copy_string(fast->exten, exten, sizeof(fast->exten));
+ ast_copy_string(fast->account, account, sizeof(fast->account));
+ fast->timeout = to;
+ fast->priority = pi;
+ if (ast_pthread_create_detached(&th, NULL, fast_originate, fast)) {
+ res = -1;
+ } else {
+ res = 0;
+ }
+ }
+ } else if (!ast_strlen_zero(app)) {
+ res = ast_pbx_outgoing_app(tech, AST_FORMAT_SLINEAR, data, to, app, appdata, &reason, 1, l, n, vars, account, NULL);
+ } else {
+ if (exten && context && pi)
+ res = ast_pbx_outgoing_exten(tech, AST_FORMAT_SLINEAR, data, to, context, exten, pi, &reason, 1, l, n, vars, account, NULL);
+ else {
+ astman_send_error(s, m, "Originate with 'Exten' requires 'Context' and 'Priority'");
+ return 0;
+ }
+ }
+ if (!res)
+ astman_send_ack(s, m, "Originate successfully queued");
+ else
+ astman_send_error(s, m, "Originate failed");
+ return 0;
+}
+
+/*! \brief Help text for manager command mailboxstatus
+ */
+static char mandescr_mailboxstatus[] =
+"Description: Checks a voicemail account for status.\n"
+"Variables: (Names marked with * are required)\n"
+" *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
+" ActionID: Optional ActionID for message matching.\n"
+"Returns number of messages.\n"
+" Message: Mailbox Status\n"
+" Mailbox: <mailboxid>\n"
+" Waiting: <count>\n"
+"\n";
+
+static int action_mailboxstatus(struct mansession *s, const struct message *m)
+{
+ const char *mailbox = astman_get_header(m, "Mailbox");
+ int ret;
+
+ if (ast_strlen_zero(mailbox)) {
+ astman_send_error(s, m, "Mailbox not specified");
+ return 0;
+ }
+ ret = ast_app_has_voicemail(mailbox, NULL);
+ astman_start_ack(s, m);
+ astman_append(s, "Message: Mailbox Status\r\n"
+ "Mailbox: %s\r\n"
+ "Waiting: %d\r\n\r\n", mailbox, ret);
+ return 0;
+}
+
+static char mandescr_mailboxcount[] =
+"Description: Checks a voicemail account for new messages.\n"
+"Variables: (Names marked with * are required)\n"
+" *Mailbox: Full mailbox ID <mailbox>@<vm-context>\n"
+" ActionID: Optional ActionID for message matching.\n"
+"Returns number of new and old messages.\n"
+" Message: Mailbox Message Count\n"
+" Mailbox: <mailboxid>\n"
+" NewMessages: <count>\n"
+" OldMessages: <count>\n"
+"\n";
+static int action_mailboxcount(struct mansession *s, const struct message *m)
+{
+ const char *mailbox = astman_get_header(m, "Mailbox");
+ int newmsgs = 0, oldmsgs = 0;
+
+ if (ast_strlen_zero(mailbox)) {
+ astman_send_error(s, m, "Mailbox not specified");
+ return 0;
+ }
+ ast_app_inboxcount(mailbox, &newmsgs, &oldmsgs);
+ astman_start_ack(s, m);
+ astman_append(s, "Message: Mailbox Message Count\r\n"
+ "Mailbox: %s\r\n"
+ "NewMessages: %d\r\n"
+ "OldMessages: %d\r\n"
+ "\r\n",
+ mailbox, newmsgs, oldmsgs);
+ return 0;
+}
+
+static char mandescr_extensionstate[] =
+"Description: Report the extension state for given extension.\n"
+" If the extension has a hint, will use devicestate to check\n"
+" the status of the device connected to the extension.\n"
+"Variables: (Names marked with * are required)\n"
+" *Exten: Extension to check state on\n"
+" *Context: Context for extension\n"
+" ActionId: Optional ID for this transaction\n"
+"Will return an \"Extension Status\" message.\n"
+"The response will include the hint for the extension and the status.\n";
+
+static int action_extensionstate(struct mansession *s, const struct message *m)
+{
+ const char *exten = astman_get_header(m, "Exten");
+ const char *context = astman_get_header(m, "Context");
+ char hint[256] = "";
+ int status;
+ if (ast_strlen_zero(exten)) {
+ astman_send_error(s, m, "Extension not specified");
+ return 0;
+ }
+ if (ast_strlen_zero(context))
+ context = "default";
+ status = ast_extension_state(NULL, context, exten);
+ ast_get_hint(hint, sizeof(hint) - 1, NULL, 0, NULL, context, exten);
+ astman_start_ack(s, m);
+ astman_append(s, "Message: Extension Status\r\n"
+ "Exten: %s\r\n"
+ "Context: %s\r\n"
+ "Hint: %s\r\n"
+ "Status: %d\r\n\r\n",
+ exten, context, hint, status);
+ return 0;
+}
+
+static char mandescr_timeout[] =
+"Description: Hangup a channel after a certain time.\n"
+"Variables: (Names marked with * are required)\n"
+" *Channel: Channel name to hangup\n"
+" *Timeout: Maximum duration of the call (sec)\n"
+"Acknowledges set time with 'Timeout Set' message\n";
+
+static int action_timeout(struct mansession *s, const struct message *m)
+{
+ struct ast_channel *c;
+ const char *name = astman_get_header(m, "Channel");
+ int timeout = atoi(astman_get_header(m, "Timeout"));
+
+ if (ast_strlen_zero(name)) {
+ astman_send_error(s, m, "No channel specified");
+ return 0;
+ }
+ if (!timeout) {
+ astman_send_error(s, m, "No timeout specified");
+ return 0;
+ }
+ c = ast_get_channel_by_name_locked(name);
+ if (!c) {
+ astman_send_error(s, m, "No such channel");
+ return 0;
+ }
+ ast_channel_setwhentohangup(c, timeout);
+ ast_channel_unlock(c);
+ astman_send_ack(s, m, "Timeout Set");
+ return 0;
+}
+
+/*!
+ * Send any applicable events to the client listening on this socket.
+ * Wait only for a finite time on each event, and drop all events whether
+ * they are successfully sent or not.
+ */
+static int process_events(struct mansession *s)
+{
+ int ret = 0;
+
+ ast_mutex_lock(&s->__lock);
+ if (s->f != NULL) {
+ struct eventqent *eqe;
+
+ while ( (eqe = NEW_EVENT(s)) ) {
+ ref_event(eqe);
+ if (!ret && s->authenticated &&
+ (s->readperm & eqe->category) == eqe->category &&
+ (s->send_events & eqe->category) == eqe->category) {
+ if (send_string(s, eqe->eventdata) < 0)
+ ret = -1; /* don't send more */
+ }
+ s->last_ev = unref_event(s->last_ev);
+ }
+ }
+ ast_mutex_unlock(&s->__lock);
+ return ret;
+}
+
+static char mandescr_userevent[] =
+"Description: Send an event to manager sessions.\n"
+"Variables: (Names marked with * are required)\n"
+" *UserEvent: EventStringToSend\n"
+" Header1: Content1\n"
+" HeaderN: ContentN\n";
+
+static int action_userevent(struct mansession *s, const struct message *m)
+{
+ const char *event = astman_get_header(m, "UserEvent");
+ char body[2048] = "";
+ int x, bodylen = 0;
+ for (x = 0; x < m->hdrcount; x++) {
+ if (strncasecmp("UserEvent:", m->headers[x], strlen("UserEvent:"))) {
+ ast_copy_string(body + bodylen, m->headers[x], sizeof(body) - bodylen - 3);
+ bodylen += strlen(m->headers[x]);
+ ast_copy_string(body + bodylen, "\r\n", 3);
+ bodylen += 2;
+ }
+ }
+
+ manager_event(EVENT_FLAG_USER, "UserEvent", "UserEvent: %s\r\n%s", event, body);
+ return 0;
+}
+
+static char mandescr_coresettings[] =
+"Description: Query for Core PBX settings.\n"
+"Variables: (Names marked with * are optional)\n"
+" *ActionID: ActionID of this transaction\n";
+
+/*! \brief Show PBX core settings information */
+static int action_coresettings(struct mansession *s, const struct message *m)
+{
+ const char *actionid = astman_get_header(m, "ActionID");
+ char idText[150] = "";
+
+ if (!ast_strlen_zero(actionid))
+ snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
+
+ astman_append(s, "Response: Success\r\n"
+ "%s"
+ "AMIversion: %s\r\n"
+ "AsteriskVersion: %s\r\n"
+ "SystemName: %s\r\n"
+ "CoreMaxCalls: %d\r\n"
+ "CoreMaxLoadAvg: %f\r\n"
+ "CoreRunUser: %s\r\n"
+ "CoreRunGroup: %s\r\n"
+ "CoreMaxFilehandles: %d\r\n"
+ "CoreRealTimeEnabled: %s\r\n"
+ "CoreCDRenabled: %s\r\n"
+ "CoreHTTPenabled: %s\r\n"
+ ,
+ idText,
+ AMI_VERSION,
+ ast_get_version(),
+ ast_config_AST_SYSTEM_NAME,
+ option_maxcalls,
+ option_maxload,
+ ast_config_AST_RUN_USER,
+ ast_config_AST_RUN_GROUP,
+ option_maxfiles,
+ ast_realtime_enabled() ? "Yes" : "No",
+ check_cdr_enabled() ? "Yes" : "No",
+ check_webmanager_enabled() ? "Yes" : "No"
+ );
+ return 0;
+}
+
+static char mandescr_corestatus[] =
+"Description: Query for Core PBX status.\n"
+"Variables: (Names marked with * are optional)\n"
+" *ActionID: ActionID of this transaction\n";
+
+/*! \brief Show PBX core status information */
+static int action_corestatus(struct mansession *s, const struct message *m)
+{
+ const char *actionid = astman_get_header(m, "ActionID");
+ char idText[150];
+ char startuptime[150];
+ char reloadtime[150];
+ struct ast_tm tm;
+
+ if (!ast_strlen_zero(actionid))
+ snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
+
+ ast_localtime(&ast_startuptime, &tm, NULL);
+ ast_strftime(startuptime, sizeof(startuptime), "%H:%M:%S", &tm);
+ ast_localtime(&ast_lastreloadtime, &tm, NULL);
+ ast_strftime(reloadtime, sizeof(reloadtime), "%H:%M:%S", &tm);
+
+ astman_append(s, "Response: Success\r\n"
+ "%s"
+ "CoreStartupTime: %s\r\n"
+ "CoreReloadTime: %s\r\n"
+ "CoreCurrentCalls: %d\r\n"
+ "",
+ idText,
+ startuptime,
+ reloadtime,
+ ast_active_channels()
+ );
+ return 0;
+}
+
+static char mandescr_reload[] =
+"Description: Send a reload event.\n"
+"Variables: (Names marked with * are optional)\n"
+" *ActionID: ActionID of this transaction\n"
+" *Module: Name of the module to reload\n";
+
+/*! \brief Send a reload event */
+static int action_reload(struct mansession *s, const struct message *m)
+{
+ const char *actionid = astman_get_header(m, "ActionID");
+ const char *module = astman_get_header(m, "Module");
+ int res = ast_module_reload(S_OR(module, NULL));
+ char idText[80] = "";
+
+ if (!ast_strlen_zero(actionid))
+ snprintf(idText, sizeof(idText), "ActionID: %s\r\n", actionid);
+ if (res == 2)
+ astman_append(s, "Response: Success\r\n%s", idText);
+ else
+ astman_send_error(s, m, s == 0 ? "No such module" : "Module does not support reload");
+ return 0;
+}
+
+static char mandescr_coreshowchannels[] =
+"Description: List currently defined channels and some information\n"
+" about them.\n"
+"Variables:\n"
+" ActionID: Optional Action id for message matching.\n";
+
+/*! \brief Manager command "CoreShowChannels" - List currently defined channels
+ * and some information about them. */
+static int action_coreshowchannels(struct mansession *s, const struct message *m)
+{
+ const char *actionid = astman_get_header(m, "ActionID");
+ char actionidtext[256] = "";
+ struct ast_channel *c = NULL;
+ int numchans = 0;
+ int duration, durh, durm, durs;
+
+ if (!ast_strlen_zero(actionid))
+ snprintf(actionidtext, sizeof(actionidtext), "ActionID: %s\r\n", actionid);
+
+ astman_send_listack(s, m, "Channels will follow", "start");
+
+ while ((c = ast_channel_walk_locked(c)) != NULL) {
+ struct ast_channel *bc = ast_bridged_channel(c);
+ char durbuf[10] = "";
+
+ if (c->cdr && !ast_tvzero(c->cdr->start)) {
+ duration = (int)(ast_tvdiff_ms(ast_tvnow(), c->cdr->start) / 1000);
+ durh = duration / 3600;
+ durm = (duration % 3600) / 60;
+ durs = duration % 60;
+ snprintf(durbuf, sizeof(durbuf), "%02d:%02d:%02d", durh, durm, durs);
+ }
+
+ astman_append(s,
+ "Channel: %s\r\n"
+ "UniqueID: %s\r\n"
+ "Context: %s\r\n"
+ "Extension: %s\r\n"
+ "Priority: %d\r\n"
+ "ChannelState: %d\r\n"
+ "ChannelStateDesc: %s\r\n"
+ "Application: %s\r\n"
+ "ApplicationData: %s\r\n"
+ "CallerIDnum: %s\r\n"
+ "Duration: %s\r\n"
+ "AccountCode: %s\r\n"
+ "BridgedChannel: %s\r\n"
+ "BridgedUniqueID: %s\r\n"
+ "\r\n", c->name, c->uniqueid, c->context, c->exten, c->priority, c->_state, ast_state2str(c->_state),
+ c->appl ? c->appl : "", c->data ? S_OR(c->data, ""): "",
+ S_OR(c->cid.cid_num, ""), durbuf, S_OR(c->accountcode, ""), bc ? bc->name : "", bc ? bc->uniqueid : "");
+ ast_channel_unlock(c);
+ numchans++;
+ }
+
+ astman_append(s,
+ "Event: CoreShowChannelsComplete\r\n"
+ "EventList: Complete\r\n"
+ "ListItems: %d\r\n"
+ "%s"
+ "\r\n", numchans, actionidtext);
+
+ return 0;
+}
+
+static char mandescr_modulecheck[] =
+"Description: Checks if Asterisk module is loaded\n"
+"Variables: \n"
+" ActionID: <id> Action ID for this transaction. Will be returned.\n"
+" Module: <name> Asterisk module name (not including extension)\n"
+"\n"
+"Will return Success/Failure\n"
+"For success returns, the module revision number is included.\n";
+
+/* Manager function to check if module is loaded */
+static int manager_modulecheck(struct mansession *s, const struct message *m)
+{
+ int res;
+ const char *module = astman_get_header(m, "Module");
+ const char *id = astman_get_header(m,"ActionID");
+ char idText[BUFSIZ];
+ const char *version;
+ char filename[BUFSIZ/2];
+ char *cut;
+
+ snprintf(filename, sizeof(filename), module);
+ if ((cut = strchr(filename, '.'))) {
+ *cut = '\0';
+ } else {
+ cut = filename + strlen(filename);
+ }
+ sprintf(cut, ".so");
+ ast_log(LOG_DEBUG, "**** ModuleCheck .so file %s\n", filename);
+ res = ast_module_check(filename);
+ if (!res) {
+ astman_send_error(s, m, "Module not loaded");
+ return 0;
+ }
+ sprintf(cut, ".c");
+ ast_log(LOG_DEBUG, "**** ModuleCheck .c file %s\n", filename);
+ version = ast_file_version_find(filename);
+
+ if (!ast_strlen_zero(id))
+ snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
+ astman_append(s, "Response: Success\r\n%s", idText);
+ astman_append(s, "Version: %s\r\n\r\n", version ? version : "");
+ return 0;
+}
+
+static char mandescr_moduleload[] =
+"Description: Loads, unloads or reloads an Asterisk module in a running system.\n"
+"Variables: \n"
+" ActionID: <id> Action ID for this transaction. Will be returned.\n"
+" Module: <name> Asterisk module name (including .so extension)\n"
+" or subsystem identifier:\n"
+" cdr, enum, dnsmgr, extconfig, manager, rtp, http\n"
+" LoadType: load | unload | reload\n"
+" The operation to be done on module\n"
+" If no module is specified for a reload loadtype, all modules are reloaded";
+
+static int manager_moduleload(struct mansession *s, const struct message *m)
+{
+ int res;
+ const char *module = astman_get_header(m, "Module");
+ const char *loadtype = astman_get_header(m, "LoadType");
+
+ if (!loadtype || strlen(loadtype) == 0)
+ astman_send_error(s, m, "Incomplete ModuleLoad action.");
+ if ((!module || strlen(module) == 0) && strcasecmp(loadtype, "reload") != 0)
+ astman_send_error(s, m, "Need module name");
+
+ if (!strcasecmp(loadtype, "load")) {
+ res = ast_load_resource(module);
+ if (res)
+ astman_send_error(s, m, "Could not load module.");
+ else
+ astman_send_ack(s, m, "Module loaded.");
+ } else if (!strcasecmp(loadtype, "unload")) {
+ res = ast_unload_resource(module, AST_FORCE_SOFT);
+ if (res)
+ astman_send_error(s, m, "Could not unload module.");
+ else
+ astman_send_ack(s, m, "Module unloaded.");
+ } else if (!strcasecmp(loadtype, "reload")) {
+ if (module != NULL) {
+ res = ast_module_reload(module);
+ if (res == 0)
+ astman_send_error(s, m, "No such module.");
+ else if (res == 1)
+ astman_send_error(s, m, "Module does not support reload action.");
+ else
+ astman_send_ack(s, m, "Module reloaded.");
+ } else {
+ ast_module_reload(NULL); /* Reload all modules */
+ astman_send_ack(s, m, "All modules reloaded");
+ }
+ } else
+ astman_send_error(s, m, "Incomplete ModuleLoad action.");
+ return 0;
+}
+
+/*
+ * Done with the action handlers here, we start with the code in charge
+ * of accepting connections and serving them.
+ * accept_thread() forks a new thread for each connection, session_do(),
+ * which in turn calls get_input() repeatedly until a full message has
+ * been accumulated, and then invokes process_message() to pass it to
+ * the appropriate handler.
+ */
+
+/*
+ * Process an AMI message, performing desired action.
+ * Return 0 on success, -1 on error that require the session to be destroyed.
+ */
+static int process_message(struct mansession *s, const struct message *m)
+{
+ char action[80] = "";
+ int ret = 0;
+ struct manager_action *tmp;
+ const char *user = astman_get_header(m, "Username");
+
+ ast_copy_string(action, astman_get_header(m, "Action"), sizeof(action));
+ ast_debug(1, "Manager received command '%s'\n", action);
+
+ if (ast_strlen_zero(action)) {
+ ast_mutex_lock(&s->__lock);
+ astman_send_error(s, m, "Missing action in request");
+ ast_mutex_unlock(&s->__lock);
+ return 0;
+ }
+
+ if (!s->authenticated && strcasecmp(action, "Login") && strcasecmp(action, "Logoff") && strcasecmp(action, "Challenge")) {
+ ast_mutex_lock(&s->__lock);
+ astman_send_error(s, m, "Permission denied");
+ ast_mutex_unlock(&s->__lock);
+ return 0;
+ }
+
+ if (!allowmultiplelogin && !s->authenticated && user &&
+ (!strcasecmp(action, "Login") || !strcasecmp(action, "Challenge"))) {
+ if (check_manager_session_inuse(user)) {
+ sleep(1);
+ ast_mutex_lock(&s->__lock);
+ astman_send_error(s, m, "Login Already In Use");
+ ast_mutex_unlock(&s->__lock);
+ return -1;
+ }
+ }
+
+ AST_RWLIST_RDLOCK(&actions);
+ AST_RWLIST_TRAVERSE(&actions, tmp, list) {
+ if (strcasecmp(action, tmp->action))
+ continue;
+ if (s->writeperm & tmp->authority || tmp->authority == 0)
+ ret = tmp->func(s, m);
+ else
+ astman_send_error(s, m, "Permission denied");
+ break;
+ }
+ AST_RWLIST_UNLOCK(&actions);
+
+ if (!tmp) {
+ char buf[BUFSIZ];
+ snprintf(buf, sizeof(buf), "Invalid/unknown command: %s. Use Action: ListCommands to show available commands.", action);
+ ast_mutex_lock(&s->__lock);
+ astman_send_error(s, m, buf);
+ ast_mutex_unlock(&s->__lock);
+ }
+ if (ret)
+ return ret;
+ /* Once done with our message, deliver any pending events */
+ return process_events(s);
+}
+
+/*!
+ * Read one full line (including crlf) from the manager socket.
+ * \note \verbatim
+ * \r\n is the only valid terminator for the line.
+ * (Note that, later, '\0' will be considered as the end-of-line marker,
+ * so everything between the '\0' and the '\r\n' will not be used).
+ * Also note that we assume output to have at least "maxlen" space.
+ * \endverbatim
+ */
+static int get_input(struct mansession *s, char *output)
+{
+ int res, x;
+ int maxlen = sizeof(s->inbuf) - 1;
+ char *src = s->inbuf;
+
+ /*
+ * Look for \r\n within the buffer. If found, copy to the output
+ * buffer and return, trimming the \r\n (not used afterwards).
+ */
+ for (x = 0; x < s->inlen; x++) {
+ int cr; /* set if we have \r */
+ if (src[x] == '\r' && x+1 < s->inlen && src[x+1] == '\n')
+ cr = 2; /* Found. Update length to include \r\n */
+ else if (src[x] == '\n')
+ cr = 1; /* also accept \n only */
+ else
+ continue;
+ memmove(output, src, x); /*... but trim \r\n */
+ output[x] = '\0'; /* terminate the string */
+ x += cr; /* number of bytes used */
+ s->inlen -= x; /* remaining size */
+ memmove(src, src + x, s->inlen); /* remove used bytes */
+ return 1;
+ }
+ if (s->inlen >= maxlen) {
+ /* no crlf found, and buffer full - sorry, too long for us */
+ ast_log(LOG_WARNING, "Dumping long line with no return from %s: %s\n", ast_inet_ntoa(s->sin.sin_addr), src);
+ s->inlen = 0;
+ }
+ res = 0;
+ while (res == 0) {
+ /* XXX do we really need this locking ? */
+ ast_mutex_lock(&s->__lock);
+ s->waiting_thread = pthread_self();
+ ast_mutex_unlock(&s->__lock);
+
+ res = ast_wait_for_input(s->fd, -1); /* return 0 on timeout ? */
+
+ ast_mutex_lock(&s->__lock);
+ s->waiting_thread = AST_PTHREADT_NULL;
+ ast_mutex_unlock(&s->__lock);
+ }
+ if (res < 0) {
+ /* If we get a signal from some other thread (typically because
+ * there are new events queued), return 0 to notify the caller.
+ */
+ if (errno == EINTR)
+ return 0;
+ ast_log(LOG_WARNING, "poll() returned error: %s\n", strerror(errno));
+ return -1;
+ }
+ ast_mutex_lock(&s->__lock);
+ res = fread(src + s->inlen, 1, maxlen - s->inlen, s->f);
+ if (res < 1)
+ res = -1; /* error return */
+ else {
+ s->inlen += res;
+ src[s->inlen] = '\0';
+ res = 0;
+ }
+ ast_mutex_unlock(&s->__lock);
+ return res;
+}
+
+static int do_message(struct mansession *s)
+{
+ struct message m = { 0 };
+ char header_buf[sizeof(s->inbuf)] = { '\0' };
+ int res;
+
+ for (;;) {
+ /* Check if any events are pending and do them if needed */
+ if (process_events(s))
+ return -1;
+ res = get_input(s, header_buf);
+ if (res == 0) {
+ continue;
+ } else if (res > 0) {
+ if (ast_strlen_zero(header_buf))
+ return process_message(s, &m) ? -1 : 0;
+ else if (m.hdrcount < (AST_MAX_MANHEADERS - 1))
+ m.headers[m.hdrcount++] = ast_strdupa(header_buf);
+ } else {
+ return res;
+ }
+ }
+}
+
+/*! \brief The body of the individual manager session.
+ * Call get_input() to read one line at a time
+ * (or be woken up on new events), collect the lines in a
+ * message until found an empty line, and execute the request.
+ * In any case, deliver events asynchronously through process_events()
+ * (called from here if no line is available, or at the end of
+ * process_message(). )
+ */
+static void *session_do(void *data)
+{
+ struct server_instance *ser = data;
+ struct mansession *s = ast_calloc(1, sizeof(*s));
+ int flags;
+ int res;
+
+ if (s == NULL)
+ goto done;
+
+ s->writetimeout = 100;
+ s->waiting_thread = AST_PTHREADT_NULL;
+
+ flags = fcntl(ser->fd, F_GETFL);
+ if (!block_sockets) /* make sure socket is non-blocking */
+ flags |= O_NONBLOCK;
+ else
+ flags &= ~O_NONBLOCK;
+ fcntl(ser->fd, F_SETFL, flags);
+
+ ast_mutex_init(&s->__lock);
+ s->send_events = -1;
+ /* these fields duplicate those in the 'ser' structure */
+ s->fd = ser->fd;
+ s->f = ser->f;
+ s->sin = ser->requestor;
+
+ AST_LIST_LOCK(&sessions);
+ AST_LIST_INSERT_HEAD(&sessions, s, list);
+ ast_atomic_fetchadd_int(&num_sessions, 1);
+ AST_LIST_UNLOCK(&sessions);
+ /* Hook to the tail of the event queue */
+ s->last_ev = grab_last();
+ s->f = ser->f;
+ astman_append(s, "Asterisk Call Manager/%s\r\n", AMI_VERSION); /* welcome prompt */
+ for (;;) {
+ if ((res = do_message(s)) < 0)
+ break;
+ }
+ /* session is over, explain why and terminate */
+ if (s->authenticated) {
+ if (manager_displayconnects(s))
+ ast_verb(2, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
+ ast_log(LOG_EVENT, "Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
+ } else {
+ if (displayconnects)
+ ast_verb(2, "Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
+ ast_log(LOG_EVENT, "Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
+ }
+
+ /* It is possible under certain circumstances for this session thread
+ to complete its work and exit *before* the thread that created it
+ has finished executing the ast_pthread_create_background() function.
+ If this occurs, some versions of glibc appear to act in a buggy
+ fashion and attempt to write data into memory that it thinks belongs
+ to the thread but is in fact not owned by the thread (or may have
+ been freed completely).
+
+ Causing this thread to yield to other threads at least one time
+ appears to work around this bug.
+ */
+ usleep(1);
+
+ destroy_session(s);
+
+done:
+ ast_free(ser);
+ return NULL;
+}
+
+/*! \brief remove at most n_max stale session from the list. */
+static void purge_sessions(int n_max)
+{
+ struct mansession *s;
+ time_t now = time(NULL);
+
+ AST_LIST_LOCK(&sessions);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, s, list) {
+ if (s->sessiontimeout && (now > s->sessiontimeout) && !s->inuse) {
+ AST_LIST_REMOVE_CURRENT(list);
+ ast_atomic_fetchadd_int(&num_sessions, -1);
+ if (s->authenticated && (VERBOSITY_ATLEAST(2)) && manager_displayconnects(s)) {
+ ast_verb(2, "HTTP Manager '%s' timed out from %s\n",
+ s->username, ast_inet_ntoa(s->sin.sin_addr));
+ }
+ free_session(s); /* XXX outside ? */
+ if (--n_max <= 0)
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ AST_LIST_UNLOCK(&sessions);
+}
+
+/*
+ * events are appended to a queue from where they
+ * can be dispatched to clients.
+ */
+static int append_event(const char *str, int category)
+{
+ struct eventqent *tmp = ast_malloc(sizeof(*tmp) + strlen(str));
+ static int seq; /* sequence number */
+
+ if (!tmp)
+ return -1;
+
+ /* need to init all fields, because ast_malloc() does not */
+ tmp->usecount = 0;
+ tmp->category = category;
+ tmp->seq = ast_atomic_fetchadd_int(&seq, 1);
+ AST_LIST_NEXT(tmp, eq_next) = NULL;
+ strcpy(tmp->eventdata, str);
+
+ AST_LIST_LOCK(&all_events);
+ AST_LIST_INSERT_TAIL(&all_events, tmp, eq_next);
+ AST_LIST_UNLOCK(&all_events);
+
+ return 0;
+}
+
+/* XXX see if can be moved inside the function */
+AST_THREADSTORAGE(manager_event_buf);
+#define MANAGER_EVENT_BUF_INITSIZE 256
+
+/*! \brief manager_event: Send AMI event to client */
+int __manager_event(int category, const char *event,
+ const char *file, int line, const char *func, const char *fmt, ...)
+{
+ struct mansession *s;
+ struct manager_custom_hook *hook;
+ struct ast_str *auth = ast_str_alloca(80);
+ const char *cat_str;
+ va_list ap;
+ struct timeval now;
+ struct ast_str *buf;
+
+ /* Abort if there aren't any manager sessions */
+ if (!num_sessions)
+ return 0;
+
+ if (!(buf = ast_str_thread_get(&manager_event_buf, MANAGER_EVENT_BUF_INITSIZE)))
+ return -1;
+
+ cat_str = authority_to_str(category, &auth);
+ ast_str_set(&buf, 0,
+ "Event: %s\r\nPrivilege: %s\r\n",
+ event, cat_str);
+
+ if (timestampevents) {
+ now = ast_tvnow();
+ ast_str_append(&buf, 0,
+ "Timestamp: %ld.%06lu\r\n",
+ now.tv_sec, (unsigned long) now.tv_usec);
+ }
+ if (manager_debug) {
+ static int seq;
+ ast_str_append(&buf, 0,
+ "SequenceNumber: %d\r\n",
+ ast_atomic_fetchadd_int(&seq, 1));
+ ast_str_append(&buf, 0,
+ "File: %s\r\nLine: %d\r\nFunc: %s\r\n", file, line, func);
+ }
+
+ va_start(ap, fmt);
+ ast_str_append_va(&buf, 0, fmt, ap);
+ va_end(ap);
+
+ ast_str_append(&buf, 0, "\r\n");
+
+ append_event(buf->str, category);
+
+ /* Wake up any sleeping sessions */
+ AST_LIST_LOCK(&sessions);
+ AST_LIST_TRAVERSE(&sessions, s, list) {
+ ast_mutex_lock(&s->__lock);
+ if (s->waiting_thread != AST_PTHREADT_NULL)
+ pthread_kill(s->waiting_thread, SIGURG);
+ ast_mutex_unlock(&s->__lock);
+ }
+ AST_LIST_UNLOCK(&sessions);
+
+ AST_RWLIST_RDLOCK(&manager_hooks);
+ AST_RWLIST_TRAVERSE(&manager_hooks, hook, list) {
+ hook->helper(category, event, buf->str);
+ }
+ AST_RWLIST_UNLOCK(&manager_hooks);
+
+ return 0;
+}
+
+/*
+ * support functions to register/unregister AMI action handlers,
+ */
+int ast_manager_unregister(char *action)
+{
+ struct manager_action *cur;
+
+ AST_RWLIST_WRLOCK(&actions);
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&actions, cur, list) {
+ if (!strcasecmp(action, cur->action)) {
+ AST_RWLIST_REMOVE_CURRENT(list);
+ ast_free(cur);
+ ast_verb(2, "Manager unregistered action %s\n", action);
+ break;
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+ AST_RWLIST_UNLOCK(&actions);
+
+ return 0;
+}
+
+static int manager_state_cb(char *context, char *exten, int state, void *data)
+{
+ /* Notify managers of change */
+ char hint[BUFSIZ];
+ ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, context, exten);
+
+ manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nHint: %s\r\nStatus: %d\r\n", exten, context, hint, state);
+ return 0;
+}
+
+static int ast_manager_register_struct(struct manager_action *act)
+{
+ struct manager_action *cur, *prev = NULL;
+
+ AST_RWLIST_WRLOCK(&actions);
+ AST_RWLIST_TRAVERSE(&actions, cur, list) {
+ int ret = strcasecmp(cur->action, act->action);
+ if (ret == 0) {
+ ast_log(LOG_WARNING, "Manager: Action '%s' already registered\n", act->action);
+ AST_RWLIST_UNLOCK(&actions);
+ return -1;
+ }
+ if (ret > 0) { /* Insert these alphabetically */
+ prev = cur;
+ break;
+ }
+ }
+
+ if (prev)
+ AST_RWLIST_INSERT_AFTER(&actions, prev, act, list);
+ else
+ AST_RWLIST_INSERT_HEAD(&actions, act, list);
+
+ ast_verb(2, "Manager registered action %s\n", act->action);
+
+ AST_RWLIST_UNLOCK(&actions);
+
+ return 0;
+}
+
+/*! \brief register a new command with manager, including online help. This is
+ the preferred way to register a manager command */
+int ast_manager_register2(const char *action, int auth, int (*func)(struct mansession *s, const struct message *m), const char *synopsis, const char *description)
+{
+ struct manager_action *cur = NULL;
+
+ if (!(cur = ast_calloc(1, sizeof(*cur))))
+ return -1;
+
+ cur->action = action;
+ cur->authority = auth;
+ cur->func = func;
+ cur->synopsis = synopsis;
+ cur->description = description;
+
+ ast_manager_register_struct(cur);
+
+ return 0;
+}
+/*! @}
+ END Doxygen group */
+
+/*
+ * The following are support functions for AMI-over-http.
+ * The common entry point is generic_http_callback(),
+ * which extracts HTTP header and URI fields and reformats
+ * them into AMI messages, locates a proper session
+ * (using the mansession_id Cookie or GET variable),
+ * and calls process_message() as for regular AMI clients.
+ * When done, the output (which goes to a temporary file)
+ * is read back into a buffer and reformatted as desired,
+ * then fed back to the client over the original socket.
+ */
+
+enum output_format {
+ FORMAT_RAW,
+ FORMAT_HTML,
+ FORMAT_XML,
+};
+
+static char *contenttype[] = {
+ [FORMAT_RAW] = "plain",
+ [FORMAT_HTML] = "html",
+ [FORMAT_XML] = "xml",
+};
+
+/*!
+ * locate an http session in the list. The search key (ident) is
+ * the value of the mansession_id cookie (0 is not valid and means
+ * a session on the AMI socket).
+ */
+static struct mansession *find_session(unsigned long ident)
+{
+ struct mansession *s;
+
+ if (ident == 0)
+ return NULL;
+
+ AST_LIST_LOCK(&sessions);
+ AST_LIST_TRAVERSE(&sessions, s, list) {
+ ast_mutex_lock(&s->__lock);
+ if (s->managerid == ident && !s->needdestroy) {
+ ast_atomic_fetchadd_int(&s->inuse, 1);
+ break;
+ }
+ ast_mutex_unlock(&s->__lock);
+ }
+ AST_LIST_UNLOCK(&sessions);
+
+ return s;
+}
+
+int astman_verify_session_readpermissions(unsigned long ident, int perm)
+{
+ int result = 0;
+ struct mansession *s;
+
+ AST_LIST_LOCK(&sessions);
+ AST_LIST_TRAVERSE(&sessions, s, list) {
+ ast_mutex_lock(&s->__lock);
+ if ((s->managerid == ident) && (s->readperm & perm)) {
+ result = 1;
+ ast_mutex_unlock(&s->__lock);
+ break;
+ }
+ ast_mutex_unlock(&s->__lock);
+ }
+ AST_LIST_UNLOCK(&sessions);
+ return result;
+}
+
+int astman_verify_session_writepermissions(unsigned long ident, int perm)
+{
+ int result = 0;
+ struct mansession *s;
+
+ AST_LIST_LOCK(&sessions);
+ AST_LIST_TRAVERSE(&sessions, s, list) {
+ ast_mutex_lock(&s->__lock);
+ if ((s->managerid == ident) && (s->writeperm & perm)) {
+ result = 1;
+ ast_mutex_unlock(&s->__lock);
+ break;
+ }
+ ast_mutex_unlock(&s->__lock);
+ }
+ AST_LIST_UNLOCK(&sessions);
+ return result;
+}
+
+/*
+ * convert to xml with various conversion:
+ * mode & 1 -> lowercase;
+ * mode & 2 -> replace non-alphanumeric chars with underscore
+ */
+static void xml_copy_escape(struct ast_str **out, const char *src, int mode)
+{
+ /* store in a local buffer to avoid calling ast_str_append too often */
+ char buf[256];
+ char *dst = buf;
+ int space = sizeof(buf);
+ /* repeat until done and nothing to flush */
+ for ( ; *src || dst != buf ; src++) {
+ if (*src == '\0' || space < 10) { /* flush */
+ *dst++ = '\0';
+ ast_str_append(out, 0, "%s", buf);
+ dst = buf;
+ space = sizeof(buf);
+ if (*src == '\0')
+ break;
+ }
+
+ if ( (mode & 2) && !isalnum(*src)) {
+ *dst++ = '_';
+ space--;
+ continue;
+ }
+ switch (*src) {
+ case '<':
+ strcpy(dst, "&lt;");
+ dst += 4;
+ space -= 4;
+ break;
+ case '>':
+ strcpy(dst, "&gt;");
+ dst += 4;
+ space -= 4;
+ break;
+ case '\"':
+ strcpy(dst, "&quot;");
+ dst += 6;
+ space -= 6;
+ break;
+ case '\'':
+ strcpy(dst, "&apos;");
+ dst += 6;
+ space -= 6;
+ break;
+ case '&':
+ strcpy(dst, "&amp;");
+ dst += 5;
+ space -= 5;
+ break;
+
+ default:
+ *dst++ = mode ? tolower(*src) : *src;
+ space--;
+ }
+ }
+}
+
+struct variable_count {
+ char *varname;
+ int count;
+};
+
+static int compress_char(char c)
+{
+ c &= 0x7f;
+ if (c < 32)
+ return 0;
+ else if (c >= 'a' && c <= 'z')
+ return c - 64;
+ else if (c > 'z')
+ return '_';
+ else
+ return c - 32;
+}
+
+static int variable_count_hash_fn(const void *vvc, const int flags)
+{
+ const struct variable_count *vc = vvc;
+ int res = 0, i;
+ for (i = 0; i < 5; i++) {
+ if (vc->varname[i] == '\0')
+ break;
+ res += compress_char(vc->varname[i]) << (i * 6);
+ }
+ return res;
+}
+
+static int variable_count_cmp_fn(void *obj, void *vstr, int flags)
+{
+ /* Due to the simplicity of struct variable_count, it makes no difference
+ * if you pass in objects or strings, the same operation applies. This is
+ * due to the fact that the hash occurs on the first element, which means
+ * the address of both the struct and the string are exactly the same. */
+ struct variable_count *vc = obj;
+ char *str = vstr;
+ return !strcmp(vc->varname, str) ? CMP_MATCH : 0;
+}
+
+/*! \brief Convert the input into XML or HTML.
+ * The input is supposed to be a sequence of lines of the form
+ * Name: value
+ * optionally followed by a blob of unformatted text.
+ * A blank line is a section separator. Basically, this is a
+ * mixture of the format of Manager Interface and CLI commands.
+ * The unformatted text is considered as a single value of a field
+ * named 'Opaque-data'.
+ *
+ * At the moment the output format is the following (but it may
+ * change depending on future requirements so don't count too
+ * much on it when writing applications):
+ *
+ * General: the unformatted text is used as a value of
+ * XML output: to be completed
+ *
+ * \verbatim
+ * Each section is within <response type="object" id="xxx">
+ * where xxx is taken from ajaxdest variable or defaults to unknown
+ * Each row is reported as an attribute Name="value" of an XML
+ * entity named from the variable ajaxobjtype, default to "generic"
+ * \endverbatim
+ *
+ * HTML output:
+ * each Name-value pair is output as a single row of a two-column table.
+ * Sections (blank lines in the input) are separated by a <HR>
+ *
+ */
+static void xml_translate(struct ast_str **out, char *in, struct ast_variable *vars, enum output_format format)
+{
+ struct ast_variable *v;
+ const char *dest = NULL;
+ char *var, *val;
+ const char *objtype = NULL;
+ int in_data = 0; /* parsing data */
+ int inobj = 0;
+ int xml = (format == FORMAT_XML);
+ struct variable_count *vc = NULL;
+ struct ao2_container *vco = NULL;
+
+ for (v = vars; v; v = v->next) {
+ if (!dest && !strcasecmp(v->name, "ajaxdest"))
+ dest = v->value;
+ else if (!objtype && !strcasecmp(v->name, "ajaxobjtype"))
+ objtype = v->value;
+ }
+ if (!dest)
+ dest = "unknown";
+ if (!objtype)
+ objtype = "generic";
+
+ /* we want to stop when we find an empty line */
+ while (in && *in) {
+ val = strsep(&in, "\r\n"); /* mark start and end of line */
+ if (in && *in == '\n') /* remove trailing \n if any */
+ in++;
+ ast_trim_blanks(val);
+ ast_debug(5, "inobj %d in_data %d line <%s>\n", inobj, in_data, val);
+ if (ast_strlen_zero(val)) {
+ if (in_data) { /* close data */
+ ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
+ in_data = 0;
+ }
+ ast_str_append(out, 0, xml ? " /></response>\n" :
+ "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
+ inobj = 0;
+ ao2_ref(vco, -1);
+ vco = NULL;
+ continue;
+ }
+
+ /* we expect Name: value lines */
+ if (in_data) {
+ var = NULL;
+ } else {
+ var = strsep(&val, ":");
+ if (val) { /* found the field name */
+ val = ast_skip_blanks(val);
+ ast_trim_blanks(var);
+ } else { /* field name not found, move to opaque mode */
+ val = var;
+ var = "Opaque-data";
+ }
+ }
+
+ if (!inobj) {
+ if (xml)
+ ast_str_append(out, 0, "<response type='object' id='%s'><%s", dest, objtype);
+ else
+ ast_str_append(out, 0, "<body>\n");
+ vco = ao2_container_alloc(37, variable_count_hash_fn, variable_count_cmp_fn);
+ inobj = 1;
+ }
+
+ if (!in_data) { /* build appropriate line start */
+ ast_str_append(out, 0, xml ? " " : "<tr><td>");
+ if ((vc = ao2_find(vco, var, 0)))
+ vc->count++;
+ else {
+ /* Create a new entry for this one */
+ vc = ao2_alloc(sizeof(*vc), NULL);
+ vc->varname = var;
+ vc->count = 1;
+ ao2_link(vco, vc);
+ }
+ xml_copy_escape(out, var, xml ? 1 | 2 : 0);
+ if (vc->count > 1)
+ ast_str_append(out, 0, "-%d", vc->count);
+ ao2_ref(vc, -1);
+ ast_str_append(out, 0, xml ? "='" : "</td><td>");
+ if (!strcmp(var, "Opaque-data"))
+ in_data = 1;
+ }
+ xml_copy_escape(out, val, 0); /* data field */
+ if (!in_data)
+ ast_str_append(out, 0, xml ? "'" : "</td></tr>\n");
+ else
+ ast_str_append(out, 0, xml ? "\n" : "<br>\n");
+ }
+ if (inobj) {
+ ast_str_append(out, 0, xml ? " /></response>\n" :
+ "<tr><td colspan=\"2\"><hr></td></tr>\r\n");
+ ao2_ref(vco, -1);
+ }
+}
+
+static struct ast_str *generic_http_callback(enum output_format format,
+ struct sockaddr_in *requestor, const char *uri,
+ struct ast_variable *params, int *status,
+ char **title, int *contentlength)
+{
+ struct mansession *s = NULL;
+ unsigned long ident = 0; /* invalid, so find_session will fail if not set through the cookie */
+ int blastaway = 0;
+ struct ast_variable *v;
+ char template[] = "/tmp/ast-http-XXXXXX"; /* template for temporary file */
+ struct ast_str *out = NULL;
+ struct message m = { 0 };
+ unsigned int x;
+ size_t hdrlen;
+
+ for (v = params; v; v = v->next) {
+ if (!strcasecmp(v->name, "mansession_id")) {
+ sscanf(v->value, "%lx", &ident);
+ break;
+ }
+ }
+
+ if (!(s = find_session(ident))) {
+ /* Create new session.
+ * While it is not in the list we don't need any locking
+ */
+ if (!(s = ast_calloc(1, sizeof(*s)))) {
+ *status = 500;
+ goto generic_callback_out;
+ }
+ s->sin = *requestor;
+ s->fd = -1;
+ s->waiting_thread = AST_PTHREADT_NULL;
+ s->send_events = 0;
+ ast_mutex_init(&s->__lock);
+ ast_mutex_lock(&s->__lock);
+ s->inuse = 1;
+ s->managerid = rand() | 1; /* make sure it is non-zero */
+ s->last_ev = grab_last();
+ AST_LIST_LOCK(&sessions);
+ AST_LIST_INSERT_HEAD(&sessions, s, list);
+ ast_atomic_fetchadd_int(&num_sessions, 1);
+ AST_LIST_UNLOCK(&sessions);
+ }
+
+ ast_mutex_unlock(&s->__lock);
+
+ if (!(out = ast_str_create(1024))) {
+ *status = 500;
+ goto generic_callback_out;
+ }
+
+ s->fd = mkstemp(template); /* create a temporary file for command output */
+ unlink(template);
+ s->f = fdopen(s->fd, "w+");
+
+ for (x = 0, v = params; v && (x < AST_MAX_MANHEADERS); x++, v = v->next) {
+ hdrlen = strlen(v->name) + strlen(v->value) + 3;
+ m.headers[m.hdrcount] = alloca(hdrlen);
+ snprintf((char *) m.headers[m.hdrcount], hdrlen, "%s: %s", v->name, v->value);
+ m.hdrcount = x + 1;
+ }
+
+ if (process_message(s, &m)) {
+ if (s->authenticated) {
+ if (manager_displayconnects(s))
+ ast_verb(2, "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
+ ast_log(LOG_EVENT, "HTTP Manager '%s' logged off from %s\n", s->username, ast_inet_ntoa(s->sin.sin_addr));
+ } else {
+ if (displayconnects)
+ ast_verb(2, "HTTP Connect attempt from '%s' unable to authenticate\n", ast_inet_ntoa(s->sin.sin_addr));
+ ast_log(LOG_EVENT, "HTTP Failed attempt from %s\n", ast_inet_ntoa(s->sin.sin_addr));
+ }
+ s->needdestroy = 1;
+ }
+
+ ast_str_append(&out, 0,
+ "Content-type: text/%s\r\n"
+ "Cache-Control: no-cache;\r\n"
+ "Set-Cookie: mansession_id=\"%08lx\"; Version=\"1\"; Max-Age=%d\r\n"
+ "\r\n",
+ contenttype[format],
+ s->managerid, httptimeout);
+
+ if (format == FORMAT_XML) {
+ ast_str_append(&out, 0, "<ajax-response>\n");
+ } else if (format == FORMAT_HTML) {
+
+#define ROW_FMT "<tr><td colspan=\"2\" bgcolor=\"#f1f1ff\">%s</td></tr>\r\n"
+#define TEST_STRING \
+ "<form action=\"manager\">action: <input name=\"action\"> cmd <input name=\"command\"><br> \
+ user <input name=\"username\"> pass <input type=\"password\" name=\"secret\"><br> \
+ <input type=\"submit\"></form>"
+
+ ast_str_append(&out, 0, "<title>Asterisk&trade; Manager Interface</title>");
+ ast_str_append(&out, 0, "<body bgcolor=\"#ffffff\"><table align=center bgcolor=\"#f1f1f1\" width=\"500\">\r\n");
+ ast_str_append(&out, 0, ROW_FMT, "<h1>Manager Tester</h1>");
+ ast_str_append(&out, 0, ROW_FMT, TEST_STRING);
+ }
+
+ if (s->f != NULL) { /* have temporary output */
+ char *buf;
+ size_t l = ftell(s->f);
+
+ if (l) {
+ if ((buf = mmap(NULL, l, PROT_READ | PROT_WRITE, MAP_SHARED, s->fd, 0))) {
+ if (format == FORMAT_XML || format == FORMAT_HTML)
+ xml_translate(&out, buf, params, format);
+ else
+ ast_str_append(&out, 0, buf);
+ munmap(buf, l);
+ }
+ } else if (format == FORMAT_XML || format == FORMAT_HTML) {
+ xml_translate(&out, "", params, format);
+ }
+ fclose(s->f);
+ s->f = NULL;
+ s->fd = -1;
+ }
+
+ if (format == FORMAT_XML) {
+ ast_str_append(&out, 0, "</ajax-response>\n");
+ } else if (format == FORMAT_HTML)
+ ast_str_append(&out, 0, "</table></body>\r\n");
+
+ ast_mutex_lock(&s->__lock);
+ /* Reset HTTP timeout. If we're not authenticated, keep it extremely short */
+ s->sessiontimeout = time(NULL) + ((s->authenticated || httptimeout < 5) ? httptimeout : 5);
+
+ if (s->needdestroy) {
+ if (s->inuse == 1) {
+ ast_debug(1, "Need destroy, doing it now!\n");
+ blastaway = 1;
+ } else {
+ ast_debug(1, "Need destroy, but can't do it yet!\n");
+ if (s->waiting_thread != AST_PTHREADT_NULL)
+ pthread_kill(s->waiting_thread, SIGURG);
+ s->inuse--;
+ }
+ } else
+ s->inuse--;
+ ast_mutex_unlock(&s->__lock);
+
+ if (blastaway)
+ destroy_session(s);
+generic_callback_out:
+ if (*status != 200)
+ return ast_http_error(500, "Server Error", NULL, "Internal Server Error (out of memory)\n");
+ return out;
+}
+
+static struct ast_str *manager_http_callback(struct server_instance *ser, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
+{
+ return generic_http_callback(FORMAT_HTML, &ser->requestor, uri, params, status, title, contentlength);
+}
+
+static struct ast_str *mxml_http_callback(struct server_instance *ser, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
+{
+ return generic_http_callback(FORMAT_XML, &ser->requestor, uri, params, status, title, contentlength);
+}
+
+static struct ast_str *rawman_http_callback(struct server_instance *ser, const char *uri, struct ast_variable *params, int *status, char **title, int *contentlength)
+{
+ return generic_http_callback(FORMAT_RAW, &ser->requestor, uri, params, status, title, contentlength);
+}
+
+struct ast_http_uri rawmanuri = {
+ .description = "Raw HTTP Manager Event Interface",
+ .uri = "rawman",
+ .has_subtree = 0,
+ .callback = rawman_http_callback,
+};
+
+struct ast_http_uri manageruri = {
+ .description = "HTML Manager Event Interface",
+ .uri = "manager",
+ .has_subtree = 0,
+ .callback = manager_http_callback,
+};
+
+struct ast_http_uri managerxmluri = {
+ .description = "XML Manager Event Interface",
+ .uri = "mxml",
+ .has_subtree = 0,
+ .callback = mxml_http_callback,
+};
+
+static int registered = 0;
+static int webregged = 0;
+
+/*! \brief cleanup code called at each iteration of server_root,
+ * guaranteed to happen every 5 seconds at most
+ */
+static void purge_old_stuff(void *data)
+{
+ purge_sessions(1);
+ purge_events();
+}
+
+struct ast_tls_config ami_tls_cfg;
+static struct server_args ami_desc = {
+ .accept_fd = -1,
+ .master = AST_PTHREADT_NULL,
+ .tls_cfg = NULL,
+ .poll_timeout = 5000, /* wake up every 5 seconds */
+ .periodic_fn = purge_old_stuff,
+ .name = "AMI server",
+ .accept_fn = server_root, /* thread doing the accept() */
+ .worker_fn = session_do, /* thread handling the session */
+};
+
+static struct server_args amis_desc = {
+ .accept_fd = -1,
+ .master = AST_PTHREADT_NULL,
+ .tls_cfg = &ami_tls_cfg,
+ .poll_timeout = -1, /* the other does the periodic cleanup */
+ .name = "AMI TLS server",
+ .accept_fn = server_root, /* thread doing the accept() */
+ .worker_fn = session_do, /* thread handling the session */
+};
+
+static int __init_manager(int reload)
+{
+ struct ast_config *ucfg = NULL, *cfg = NULL;
+ const char *val;
+ char *cat = NULL;
+ int newhttptimeout = 60;
+ int have_sslbindaddr = 0;
+ struct hostent *hp;
+ struct ast_hostent ahp;
+ struct ast_manager_user *user = NULL;
+ struct ast_variable *var;
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+
+ if (!registered) {
+ /* Register default actions */
+ ast_manager_register2("Ping", 0, action_ping, "Keepalive command", mandescr_ping);
+ ast_manager_register2("Events", 0, action_events, "Control Event Flow", mandescr_events);
+ ast_manager_register2("Logoff", 0, action_logoff, "Logoff Manager", mandescr_logoff);
+ ast_manager_register2("Login", 0, action_login, "Login Manager", NULL);
+ ast_manager_register2("Challenge", 0, action_challenge, "Generate Challenge for MD5 Auth", NULL);
+ ast_manager_register2("Hangup", EVENT_FLAG_SYSTEM | EVENT_FLAG_CALL, action_hangup, "Hangup Channel", mandescr_hangup);
+ ast_manager_register("Status", EVENT_FLAG_SYSTEM | EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_status, "Lists channel status" );
+ ast_manager_register2("Setvar", EVENT_FLAG_CALL, action_setvar, "Set Channel Variable", mandescr_setvar );
+ ast_manager_register2("Getvar", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_getvar, "Gets a Channel Variable", mandescr_getvar );
+ ast_manager_register2("GetConfig", EVENT_FLAG_SYSTEM | EVENT_FLAG_CONFIG, action_getconfig, "Retrieve configuration", mandescr_getconfig);
+ ast_manager_register2("GetConfigJSON", EVENT_FLAG_SYSTEM | EVENT_FLAG_CONFIG, action_getconfigjson, "Retrieve configuration (JSON format)", mandescr_getconfigjson);
+ ast_manager_register2("UpdateConfig", EVENT_FLAG_CONFIG, action_updateconfig, "Update basic configuration", mandescr_updateconfig);
+ ast_manager_register2("Redirect", EVENT_FLAG_CALL, action_redirect, "Redirect (transfer) a call", mandescr_redirect );
+ ast_manager_register2("Originate", EVENT_FLAG_CALL, action_originate, "Originate Call", mandescr_originate);
+ ast_manager_register2("Command", EVENT_FLAG_COMMAND, action_command, "Execute Asterisk CLI Command", mandescr_command );
+ ast_manager_register2("ExtensionState", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_extensionstate, "Check Extension Status", mandescr_extensionstate );
+ ast_manager_register2("AbsoluteTimeout", EVENT_FLAG_SYSTEM | EVENT_FLAG_CALL, action_timeout, "Set Absolute Timeout", mandescr_timeout );
+ ast_manager_register2("MailboxStatus", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_mailboxstatus, "Check Mailbox", mandescr_mailboxstatus );
+ ast_manager_register2("MailboxCount", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_mailboxcount, "Check Mailbox Message Count", mandescr_mailboxcount );
+ ast_manager_register2("ListCommands", 0, action_listcommands, "List available manager commands", mandescr_listcommands);
+ ast_manager_register2("SendText", EVENT_FLAG_CALL, action_sendtext, "Send text message to channel", mandescr_sendtext);
+ ast_manager_register2("UserEvent", EVENT_FLAG_USER, action_userevent, "Send an arbitrary event", mandescr_userevent);
+ ast_manager_register2("WaitEvent", 0, action_waitevent, "Wait for an event to occur", mandescr_waitevent);
+ ast_manager_register2("CoreSettings", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, action_coresettings, "Show PBX core settings (version etc)", mandescr_coresettings);
+ ast_manager_register2("CoreStatus", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, action_corestatus, "Show PBX core status variables", mandescr_corestatus);
+ ast_manager_register2("Reload", EVENT_FLAG_CONFIG | EVENT_FLAG_SYSTEM, action_reload, "Send a reload event", mandescr_reload);
+ ast_manager_register2("CoreShowChannels", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, action_coreshowchannels, "List currently active channels", mandescr_coreshowchannels);
+ ast_manager_register2("ModuleLoad", EVENT_FLAG_SYSTEM, manager_moduleload, "Module management", mandescr_moduleload);
+ ast_manager_register2("ModuleCheck", EVENT_FLAG_SYSTEM, manager_modulecheck, "Check if module is loaded", mandescr_modulecheck);
+
+ ast_cli_register_multiple(cli_manager, sizeof(cli_manager) / sizeof(struct ast_cli_entry));
+ ast_extension_state_add(NULL, NULL, manager_state_cb, NULL);
+ registered = 1;
+ /* Append placeholder event so master_eventq never runs dry */
+ append_event("Event: Placeholder\r\n\r\n", 0);
+ }
+ if ((cfg = ast_config_load("manager.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
+ return 0;
+
+ displayconnects = 1;
+ if (!cfg) {
+ ast_log(LOG_NOTICE, "Unable to open AMI configuration manager.conf. Asterisk management interface (AMI) disabled.\n");
+ return 0;
+ }
+
+ /* default values */
+ memset(&ami_desc.sin, 0, sizeof(struct sockaddr_in));
+ memset(&amis_desc.sin, 0, sizeof(amis_desc.sin));
+ amis_desc.sin.sin_port = htons(5039);
+ ami_desc.sin.sin_port = htons(DEFAULT_MANAGER_PORT);
+
+ ami_tls_cfg.enabled = 0;
+ if (ami_tls_cfg.certfile)
+ ast_free(ami_tls_cfg.certfile);
+ ami_tls_cfg.certfile = ast_strdup(AST_CERTFILE);
+ if (ami_tls_cfg.cipher)
+ ast_free(ami_tls_cfg.cipher);
+ ami_tls_cfg.cipher = ast_strdup("");
+
+ for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
+ val = var->value;
+ if (!strcasecmp(var->name, "sslenable"))
+ ami_tls_cfg.enabled = ast_true(val);
+ else if (!strcasecmp(var->name, "sslbindport"))
+ amis_desc.sin.sin_port = htons(atoi(val));
+ else if (!strcasecmp(var->name, "sslbindaddr")) {
+ if ((hp = ast_gethostbyname(val, &ahp))) {
+ memcpy(&amis_desc.sin.sin_addr, hp->h_addr, sizeof(amis_desc.sin.sin_addr));
+ have_sslbindaddr = 1;
+ } else {
+ ast_log(LOG_WARNING, "Invalid bind address '%s'\n", val);
+ }
+ } else if (!strcasecmp(var->name, "sslcert")) {
+ ast_free(ami_tls_cfg.certfile);
+ ami_tls_cfg.certfile = ast_strdup(val);
+ } else if (!strcasecmp(var->name, "sslcipher")) {
+ ast_free(ami_tls_cfg.cipher);
+ ami_tls_cfg.cipher = ast_strdup(val);
+ } else if (!strcasecmp(var->name, "enabled")) {
+ manager_enabled = ast_true(val);
+ } else if (!strcasecmp(var->name, "block-sockets")) {
+ block_sockets = ast_true(val);
+ } else if (!strcasecmp(var->name, "webenabled")) {
+ webmanager_enabled = ast_true(val);
+ } else if (!strcasecmp(var->name, "port")) {
+ ami_desc.sin.sin_port = htons(atoi(val));
+ } else if (!strcasecmp(var->name, "bindaddr")) {
+ if (!inet_aton(val, &ami_desc.sin.sin_addr)) {
+ ast_log(LOG_WARNING, "Invalid address '%s' specified, using 0.0.0.0\n", val);
+ memset(&ami_desc.sin.sin_addr, 0, sizeof(ami_desc.sin.sin_addr));
+ }
+ } else if (!strcasecmp(var->name, "allowmultiplelogin")) {
+ allowmultiplelogin = ast_true(val);
+ } else if (!strcasecmp(var->name, "displayconnects")) {
+ displayconnects = ast_true(val);
+ } else if (!strcasecmp(var->name, "timestampevents")) {
+ timestampevents = ast_true(val);
+ } else if (!strcasecmp(var->name, "debug")) {
+ manager_debug = ast_true(val);
+ } else if (!strcasecmp(var->name, "httptimeout")) {
+ newhttptimeout = atoi(val);
+ } else {
+ ast_log(LOG_NOTICE, "Invalid keyword <%s> = <%s> in manager.conf [general]\n",
+ var->name, val);
+ }
+ }
+
+ if (manager_enabled)
+ ami_desc.sin.sin_family = AF_INET;
+ if (!have_sslbindaddr)
+ amis_desc.sin.sin_addr = ami_desc.sin.sin_addr;
+ if (ami_tls_cfg.enabled)
+ amis_desc.sin.sin_family = AF_INET;
+
+
+ AST_RWLIST_WRLOCK(&users);
+
+ /* First, get users from users.conf */
+ ucfg = ast_config_load("users.conf", config_flags);
+ if (ucfg && (ucfg != CONFIG_STATUS_FILEUNCHANGED)) {
+ const char *hasmanager;
+ int genhasmanager = ast_true(ast_variable_retrieve(ucfg, "general", "hasmanager"));
+
+ while ((cat = ast_category_browse(ucfg, cat))) {
+ if (!strcasecmp(cat, "general"))
+ continue;
+
+ hasmanager = ast_variable_retrieve(ucfg, cat, "hasmanager");
+ if ((!hasmanager && genhasmanager) || ast_true(hasmanager)) {
+ const char *user_secret = ast_variable_retrieve(ucfg, cat, "secret");
+ const char *user_read = ast_variable_retrieve(ucfg, cat, "read");
+ const char *user_write = ast_variable_retrieve(ucfg, cat, "write");
+ const char *user_displayconnects = ast_variable_retrieve(ucfg, cat, "displayconnects");
+ const char *user_writetimeout = ast_variable_retrieve(ucfg, cat, "writetimeout");
+
+ /* Look for an existing entry,
+ * if none found - create one and add it to the list
+ */
+ if (!(user = get_manager_by_name_locked(cat))) {
+ if (!(user = ast_calloc(1, sizeof(*user))))
+ break;
+
+ /* Copy name over */
+ ast_copy_string(user->username, cat, sizeof(user->username));
+ /* Insert into list */
+ AST_LIST_INSERT_TAIL(&users, user, list);
+ user->ha = NULL;
+ user->readperm = -1;
+ user->writeperm = -1;
+ /* Default displayconnect from [general] */
+ user->displayconnects = displayconnects;
+ user->writetimeout = 100;
+ }
+
+ if (!user_secret)
+ user_secret = ast_variable_retrieve(ucfg, "general", "secret");
+ if (!user_read)
+ user_read = ast_variable_retrieve(ucfg, "general", "read");
+ if (!user_write)
+ user_write = ast_variable_retrieve(ucfg, "general", "write");
+ if (!user_displayconnects)
+ user_displayconnects = ast_variable_retrieve(ucfg, "general", "displayconnects");
+ if (!user_writetimeout)
+ user_writetimeout = ast_variable_retrieve(ucfg, "general", "writetimeout");
+
+ if (!ast_strlen_zero(user_secret)) {
+ if (user->secret)
+ ast_free(user->secret);
+ user->secret = ast_strdup(user_secret);
+ }
+
+ if (user_read)
+ user->readperm = get_perm(user_read);
+ if (user_write)
+ user->writeperm = get_perm(user_write);
+ if (user_displayconnects)
+ user->displayconnects = ast_true(user_displayconnects);
+
+ if (user_writetimeout) {
+ int val = atoi(user_writetimeout);
+ if (val < 100)
+ ast_log(LOG_WARNING, "Invalid writetimeout value '%s' at users.conf line %d\n", var->value, var->lineno);
+ else
+ user->writetimeout = val;
+ }
+ }
+ }
+ ast_config_destroy(ucfg);
+ }
+
+ /* cat is NULL here in any case */
+
+ while ((cat = ast_category_browse(cfg, cat))) {
+ struct ast_ha *oldha;
+
+ if (!strcasecmp(cat, "general"))
+ continue;
+
+ /* Look for an existing entry, if none found - create one and add it to the list */
+ if (!(user = get_manager_by_name_locked(cat))) {
+ if (!(user = ast_calloc(1, sizeof(*user))))
+ break;
+ /* Copy name over */
+ ast_copy_string(user->username, cat, sizeof(user->username));
+
+ user->ha = NULL;
+ user->readperm = 0;
+ user->writeperm = 0;
+ /* Default displayconnect from [general] */
+ user->displayconnects = displayconnects;
+ user->writetimeout = 100;
+
+ /* Insert into list */
+ AST_RWLIST_INSERT_TAIL(&users, user, list);
+ }
+
+ /* Make sure we keep this user and don't destroy it during cleanup */
+ user->keep = 1;
+ oldha = user->ha;
+ user->ha = NULL;
+
+ var = ast_variable_browse(cfg, cat);
+ for (; var; var = var->next) {
+ if (!strcasecmp(var->name, "secret")) {
+ if (user->secret)
+ ast_free(user->secret);
+ user->secret = ast_strdup(var->value);
+ } else if (!strcasecmp(var->name, "deny") ||
+ !strcasecmp(var->name, "permit")) {
+ user->ha = ast_append_ha(var->name, var->value, user->ha, NULL);
+ } else if (!strcasecmp(var->name, "read") ) {
+ user->readperm = get_perm(var->value);
+ } else if (!strcasecmp(var->name, "write") ) {
+ user->writeperm = get_perm(var->value);
+ } else if (!strcasecmp(var->name, "displayconnects") ) {
+ user->displayconnects = ast_true(var->value);
+ } else if (!strcasecmp(var->name, "writetimeout")) {
+ int val = atoi(var->value);
+ if (val < 100)
+ ast_log(LOG_WARNING, "Invalid writetimeout value '%s' at line %d\n", var->value, var->lineno);
+ else
+ user->writetimeout = val;
+ } else
+ ast_debug(1, "%s is an unknown option.\n", var->name);
+ }
+ ast_free_ha(oldha);
+ }
+ ast_config_destroy(cfg);
+
+ /* Perform cleanup - essentially prune out old users that no longer exist */
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&users, user, list) {
+ if (user->keep) { /* valid record. clear flag for the next round */
+ user->keep = 0;
+ continue;
+ }
+ /* We do not need to keep this user so take them out of the list */
+ AST_RWLIST_REMOVE_CURRENT(list);
+ /* Free their memory now */
+ if (user->secret)
+ ast_free(user->secret);
+ ast_free_ha(user->ha);
+ ast_free(user);
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+
+ AST_RWLIST_UNLOCK(&users);
+
+ if (webmanager_enabled && manager_enabled) {
+ if (!webregged) {
+ ast_http_uri_link(&rawmanuri);
+ ast_http_uri_link(&manageruri);
+ ast_http_uri_link(&managerxmluri);
+ webregged = 1;
+ }
+ } else {
+ if (webregged) {
+ ast_http_uri_unlink(&rawmanuri);
+ ast_http_uri_unlink(&manageruri);
+ ast_http_uri_unlink(&managerxmluri);
+ webregged = 0;
+ }
+ }
+
+ if (newhttptimeout > 0)
+ httptimeout = newhttptimeout;
+
+ manager_event(EVENT_FLAG_SYSTEM, "Reload", "Module: Manager\r\nStatus: %s\r\nMessage: Manager reload Requested\r\n", manager_enabled ? "Enabled" : "Disabled");
+
+ server_start(&ami_desc);
+ if (ssl_setup(amis_desc.tls_cfg))
+ server_start(&amis_desc);
+ return 0;
+}
+
+int init_manager(void)
+{
+ return __init_manager(0);
+}
+
+int reload_manager(void)
+{
+ return __init_manager(1);
+}
diff --git a/trunk/main/md5.c b/trunk/main/md5.c
new file mode 100644
index 000000000..594c5eff5
--- /dev/null
+++ b/trunk/main/md5.c
@@ -0,0 +1,265 @@
+
+/*!\file
+\brief MD5 checksum routines used for authentication. Not covered by GPL, but
+ in the public domain as per the copyright below */
+
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/endian.h"
+#include "asterisk/md5.h"
+
+# if __BYTE_ORDER == __BIG_ENDIAN
+# define HIGHFIRST 1
+# endif
+#ifndef HIGHFIRST
+#define byteReverse(buf, len) /* Nothing */
+#else
+void byteReverse(unsigned char *buf, unsigned longs);
+
+#ifndef ASM_MD5
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+void byteReverse(unsigned char *buf, unsigned longs)
+{
+ uint32_t t;
+ do {
+ t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+ ((unsigned) buf[1] << 8 | buf[0]);
+ *(uint32_t *) buf = t;
+ buf += 4;
+ } while (--longs);
+}
+#endif
+#endif
+
+/*
+ * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void MD5Init(struct MD5Context *ctx)
+{
+ ctx->buf[0] = 0x67452301;
+ ctx->buf[1] = 0xefcdab89;
+ ctx->buf[2] = 0x98badcfe;
+ ctx->buf[3] = 0x10325476;
+
+ ctx->bits[0] = 0;
+ ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len)
+{
+ uint32_t t;
+
+ /* Update bitcount */
+
+ t = ctx->bits[0];
+ if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t)
+ ctx->bits[1]++; /* Carry from low to high */
+ ctx->bits[1] += len >> 29;
+
+ t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
+
+ /* Handle any leading odd-sized chunks */
+
+ if (t) {
+ unsigned char *p = (unsigned char *) ctx->in + t;
+
+ t = 64 - t;
+ if (len < t) {
+ memcpy(p, buf, len);
+ return;
+ }
+ memcpy(p, buf, t);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+ buf += t;
+ len -= t;
+ }
+ /* Process data in 64-byte chunks */
+
+ while (len >= 64) {
+ memcpy(ctx->in, buf, 64);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+ buf += 64;
+ len -= 64;
+ }
+
+ /* Handle any remaining bytes of data. */
+
+ memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
+{
+ unsigned count;
+ unsigned char *p;
+
+ /* Compute number of bytes mod 64 */
+ count = (ctx->bits[0] >> 3) & 0x3F;
+
+ /* Set the first char of padding to 0x80. This is safe since there is
+ always at least one byte free */
+ p = ctx->in + count;
+ *p++ = 0x80;
+
+ /* Bytes of padding needed to make 64 bytes */
+ count = 64 - 1 - count;
+
+ /* Pad out to 56 mod 64 */
+ if (count < 8) {
+ /* Two lots of padding: Pad the first block to 64 bytes */
+ memset(p, 0, count);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+
+ /* Now fill the next block with 56 bytes */
+ memset(ctx->in, 0, 56);
+ } else {
+ /* Pad block to 56 bytes */
+ memset(p, 0, count - 8);
+ }
+ byteReverse(ctx->in, 14);
+
+ /* Append length in bits and transform */
+ ((uint32_t *) ctx->in)[14] = ctx->bits[0];
+ ((uint32_t *) ctx->in)[15] = ctx->bits[1];
+
+ MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+ byteReverse((unsigned char *) ctx->buf, 4);
+ memcpy(digest, ctx->buf, 16);
+ memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */
+}
+
+#ifndef ASM_MD5
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+ ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data. MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+void MD5Transform(uint32_t buf[4], uint32_t const in[16])
+{
+ register uint32_t a, b, c, d;
+
+ a = buf[0];
+ b = buf[1];
+ c = buf[2];
+ d = buf[3];
+
+ MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+ MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+ MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+ MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+ MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+ MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+ MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+ MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+ MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+ MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+ MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+ MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+ MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+ MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+ MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+ MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+ MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+ MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+ MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+ MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+ MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+ MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+ MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+ MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+ MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+ MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+ MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+ MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+ MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+ MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+ MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+ MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+ MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+ MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+ MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+ MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+ MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+ MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+ MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+ MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+ MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+ MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+ MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+ MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+ MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+ MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+ MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+ MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+ MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+ MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+ MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+ MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+ MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+ MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+ MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+ MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+ MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+ MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+ MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+ MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+ MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+ MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+ MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+ MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+
+#endif
diff --git a/trunk/main/minimime/Make.conf b/trunk/main/minimime/Make.conf
new file mode 100644
index 000000000..de149932a
--- /dev/null
+++ b/trunk/main/minimime/Make.conf
@@ -0,0 +1,7 @@
+CC=gcc
+PREFIX=/usr
+LIBNAME=libmmime.so.0.0
+HAVE_STRLCAT=
+HAVE_STRLCPY=
+INSTALL=/usr/bin/install
+HAVE_DEBUG=1
diff --git a/trunk/main/minimime/Makefile b/trunk/main/minimime/Makefile
new file mode 100644
index 000000000..555d17a04
--- /dev/null
+++ b/trunk/main/minimime/Makefile
@@ -0,0 +1,67 @@
+#
+# Asterisk -- A telephony toolkit for Linux.
+#
+# Makefile for resource modules
+#
+# Copyright (C) 2007, Digium, Inc.
+#
+# This program is free software, distributed under the terms of
+# the GNU General Public License
+#
+
+-include $(ASTTOPDIR)/menuselect.makeopts $(ASTTOPDIR)/menuselect.makedeps
+
+include $(ASTTOPDIR)/Makefile.moddir_rules
+
+LIBMMIME:=libmmime.a
+MM_SRCS= \
+ mimeparser.tab.c \
+ mimeparser.yy.c \
+ mm_init.c \
+ mm_base64.c \
+ mm_codecs.c \
+ mm_contenttype.c \
+ mm_context.c \
+ mm_envelope.c \
+ mm_error.c \
+ mm_header.c \
+ mm_mem.c \
+ mm_mimepart.c \
+ mm_mimeutil.c \
+ mm_param.c \
+ mm_parse.c \
+ mm_util.c
+
+MM_OBJS:=$(MM_SRCS:%.c=%.o)
+MM_HDRS:=mm.h mm_util.h
+
+# Use weaker error checking because we have some automatically generated
+# files. However just mask out -Werror, because other warnings below:
+# -Wundef -Wstrict-prototypes -Wmissing-declarations
+# -Wmissing-prototypes
+# may actually be important and spot out real bugs.
+ASTCFLAGS:=$(filter-out -Werror,$(ASTCFLAGS))
+
+ASTCFLAGS+=-std=c99
+
+all: $(LIBMMIME)
+
+$(LIBMMIME): $(MM_OBJS)
+ $(ECHO_PREFIX) echo " [AR] $^ -> $@"
+ $(CMD_PREFIX) $(AR) cr $@ $^
+ $(CMD_PREFIX) $(RANLIB) $@
+
+mimeparser.yy.c:
+ flex -Pmimeparser_yy -omimeparser.yy.c mimeparser.l
+
+mimeparser.tab.c:
+ bison -d -pmimeparser_yy -omimeparser.tab.c mimeparser.y
+
+clean::
+ rm -f $(LIBMMIME) *.o
+
+.PHONY: clean all
+
+ifneq ($(wildcard .*.d),)
+ include .*.d
+endif
diff --git a/trunk/main/minimime/mimeparser.h b/trunk/main/minimime/mimeparser.h
new file mode 100644
index 000000000..f6bfbaad7
--- /dev/null
+++ b/trunk/main/minimime/mimeparser.h
@@ -0,0 +1,76 @@
+#ifndef _MIMEPARSER_H_INCLUDED
+#define _MIMEPARSER_H_INCLUDED
+
+#include "mm.h"
+
+struct s_position
+{
+ size_t opaque_start;
+ size_t start;
+ size_t end;
+};
+
+struct lexer_state
+{
+ int header_state;
+ int lineno;
+ size_t current_pos;
+ int condition;
+
+ int is_envelope;
+
+ size_t message_len;
+ size_t buffer_length;
+
+ /* temporary marker variables */
+ size_t body_opaque_start;
+ size_t body_start;
+ size_t body_end;
+ size_t preamble_start;
+ size_t preamble_end;
+ size_t postamble_start;
+ size_t postamble_end;
+
+ char *boundary_string;
+ char *endboundary_string;
+ const char *message_buffer;
+};
+
+
+struct parser_state
+{
+ MM_CTX *ctx;
+ struct mm_mimepart *envelope;
+ struct mm_mimepart *temppart;
+ struct mm_mimepart *current_mimepart;
+ struct mm_content *ctype;
+ int parsemode;
+ int have_contenttype;
+ int debug;
+ int mime_parts;
+ struct lexer_state lstate;
+};
+
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void* yyscan_t;
+#endif
+
+#include "mimeparser.tab.h"
+
+/**
+ * Prototypes for functions used by the parser routines
+ */
+int count_lines(char *);
+int dprintf2(struct parser_state *, const char *, ...);
+int mimeparser_yyparse(struct parser_state *, void *);
+int mimeparser_yylex(YYSTYPE *, void *);
+int mimeparser_yyerror(struct parser_state *, void *, const char *);
+int mimeparser_yylex_init(yyscan_t* scanner);
+int mimeparser_yylex_destroy(yyscan_t yyscanner);
+void reset_lexer_state(void *yyscanner, struct parser_state *pstate);
+int PARSER_initialize(struct parser_state *pstate, yyscan_t scanner);
+void PARSER_setbuffer(const char *string, yyscan_t scanner);
+void PARSER_setfp(FILE *fp, yyscan_t scanner);
+
+#endif /* ! _MIMEPARSER_H_INCLUDED */
diff --git a/trunk/main/minimime/mimeparser.l b/trunk/main/minimime/mimeparser.l
new file mode 100644
index 000000000..19d42cf3a
--- /dev/null
+++ b/trunk/main/minimime/mimeparser.l
@@ -0,0 +1,484 @@
+%{
+/*
+ * Copyright (c) 2004 Jann Fischer. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/**
+ * This is a lexer file for parsing MIME compatible messages. It is intended
+ * to satisfy at least RFC 2045 (Format of Internet Message Bodies). It still
+ * has quite a few problems:
+ *
+ * - The parsing could probably be done in a more elegant way
+ * - I don't know what performance impact REJECT has on the parser
+ */
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "mimeparser.h"
+#include "mimeparser.tab.h"
+
+#define NAMEOF(v) #v
+/* BC() is a debug wrapper for lex' BEGIN() macro */
+#define BC(x) do { \
+ struct lexer_state *lstate = yyget_extra(yyscanner); \
+ BEGIN(x); \
+ lstate->condition = x; \
+} while(0);
+
+#define ZERO(x) memset(x, '\0', sizeof(x))
+
+#define PREALLOC_BUFFER 100000
+#undef YY_BUF_SIZE
+#define YY_BUF_SIZE 65536
+
+enum header_states
+{
+ STATE_MAIL = 0,
+ STATE_CTYPE,
+ STATE_CDISP,
+ STATE_CENC,
+ STATE_MIME
+};
+
+
+
+%}
+
+%option reentrant
+%option yylineno
+%option bison-bridge
+
+%s headers
+%s header
+%s headervalue
+%s tspecialvalue
+%s comment
+%s body
+%s postamble
+%s preamble
+%s boundary
+%s endboundary
+%s endoffile
+
+STRING [a-zA-Z0-9\-\.\_]
+TSPECIAL [a-zA-Z0-9)(<>@,;:/\-.=_\+'? ]
+TSPECIAL_LITE [a-zA-Z0-9)(<>@,-._+'?\[\]]
+
+%%
+
+<INITIAL,headers>^[a-zA-Z]+[a-zA-Z0-9\-\_]* {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+
+ yylval_param->string=strdup(yytext);
+ lstate->current_pos += yyleng;
+ BC(header);
+
+ /* Depending on what header we are processing, we enter a different
+ * state and return a different value.
+ */
+ if (!strcasecmp(yytext, "Content-Type")) {
+ lstate->header_state = STATE_CTYPE;
+ return CONTENTTYPE_HEADER;
+ } else if (!strcasecmp(yytext, "Content-Transfer-Encoding")) {
+ lstate->header_state = STATE_CENC;
+ return CONTENTENCODING_HEADER;
+ } else if (!strcasecmp(yytext, "Content-Disposition")) {
+ lstate->header_state = STATE_CDISP;
+ return CONTENTDISPOSITION_HEADER;
+ } else if (!strcasecmp(yytext, "MIME-Version")) {
+ lstate->header_state = STATE_MAIL;
+ return MIMEVERSION_HEADER;
+ } else {
+ lstate->header_state = STATE_MAIL;
+ return MAIL_HEADER;
+ }
+}
+
+<INITIAL,headers>. {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ /* dprintf2("Unknown header char: %c\n", *yytext); */
+ lstate->current_pos += yyleng;
+ return ANY;
+}
+
+<headers>^(\r\n|\n) {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ lstate->lineno++;
+
+ lstate->current_pos += yyleng;
+
+ /* This marks the end of headers. Depending on whether we are in the
+ * envelope currently we need to parse either a body or the preamble
+ * now.
+ */
+ if (lstate->is_envelope == 0 || lstate->boundary_string == NULL) {
+ BC(body);
+ lstate->body_start = lstate->current_pos;
+ } else {
+ lstate->is_envelope = 0;
+ lstate->preamble_start = lstate->current_pos;
+ BC(preamble);
+ }
+
+ return ENDOFHEADERS;
+}
+
+<header>\: {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ BC(headervalue);
+ lstate->current_pos += yyleng;
+ return COLON;
+}
+
+<header>(\r\n|\n) {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ BC(headers);
+ /* dprintf2("Invalid header, returning EOL\n"); */
+ lstate->current_pos += yyleng;
+ return EOL;
+}
+
+<headervalue>(\n|\r\n)[\ \t]+ {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ lstate->current_pos += yyleng;
+}
+
+<headervalue>.+|(.+(\n|\r\n)[\ \t]+.+)+ {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ if (lstate->header_state != STATE_MAIL && lstate->header_state != STATE_CENC) {
+ REJECT;
+ }
+ lstate->current_pos += yyleng;
+ while (*yytext && isspace(*yytext)) yytext++;
+ /* Do we actually have a header value? */
+ if (*yytext == '\0') {
+ yylval_param->string = strdup("");
+ } else {
+ yylval_param->string=strdup(yytext);
+ lstate->lineno += count_lines(yytext);
+ }
+ return WORD;
+}
+
+<headervalue,tspecialvalue>(\r\n|\n) {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ /* marks the end of one header line */
+ lstate->lineno++;
+ BC(headers);
+ lstate->current_pos += yyleng;
+ return EOL;
+}
+
+<headervalue>;|;(\r\n|\n)[\ \t]+ {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ lstate->lineno += count_lines(yytext);
+ lstate->current_pos += yyleng;
+ return SEMICOLON;
+}
+
+<headervalue>\= {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ lstate->current_pos += yyleng;
+ return EQUAL;
+}
+
+<headervalue>\" {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ BC(tspecialvalue);
+ lstate->current_pos += yyleng;
+ return *yytext;
+}
+
+<headervalue>{STRING}+|{TSPECIAL_LITE}+ {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ yylval_param->string=strdup(yytext);
+ lstate->lineno += count_lines(yytext);
+ lstate->current_pos += yyleng;
+ return WORD;
+}
+
+<headervalue>[\ |\t]+ {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ lstate->current_pos += yyleng;
+}
+
+<tspecialvalue>{TSPECIAL}+ {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ lstate->lineno += count_lines(yytext);
+ yylval_param->string=strdup(yytext);
+ lstate->current_pos += yyleng;
+ return TSPECIAL;
+}
+
+<tspecialvalue>\" {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ BC(headervalue);
+ lstate->current_pos += yyleng;
+ return *yytext;
+}
+
+<body>^\-\-{TSPECIAL}+\-\- {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ /**
+ * Make sure we only catch matching boundaries, and not other lines
+ * that begin and end with two dashes. If we have catched a valid
+ * end boundary, which actually ends a body, we save the current
+ * position, put the token back on the input stream and let the
+ * endboundary condition parse the actual token.
+ */
+ if (lstate->endboundary_string != NULL) {
+ if (strcmp(lstate->endboundary_string, yytext)) {
+ /* dprintf2("YYTEXT != end_boundary: '%s'\n", yytext); */
+ REJECT;
+ } else {
+ lstate->current_pos += yyleng;
+ /* dprintf2("YYTEXT == lstate->end_boundary: '%s'\n", yytext); */
+ if (lstate->body_start) {
+ yylval_param->position.opaque_start =
+ lstate->body_opaque_start;
+ yylval_param->position.start = lstate->body_start;
+ yylval_param->position.end = lstate->current_pos - yyleng;
+ lstate->body_opaque_start = 0;
+ lstate->body_start = 0;
+ lstate->body_end = 0;
+ yyless(0);
+ BC(endboundary);
+ return BODY;
+ }
+ }
+ } else {
+ }
+
+ REJECT;
+}
+
+<body,preamble>^\-\-{TSPECIAL}+ {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ /**
+ * Make sure we only catch matching boundaries, and not other lines
+ * that begin with two dashes.
+ */
+ if (lstate->boundary_string != NULL) {
+ if (strcmp(lstate->boundary_string, yytext)) {
+ /* dprintf2("YYTEXT != boundary: '%s'\n", yytext);*/
+ REJECT;
+ } else {
+ /* dprintf2("YYTEXT == boundary: '%s'\n", yytext);*/
+ if (lstate->body_start) {
+ yylval_param->position.opaque_start = lstate->body_opaque_start;
+ yylval_param->position.start = lstate->body_start;
+ yylval_param->position.end = lstate->current_pos;
+ lstate->body_opaque_start = 0;
+ lstate->body_start = 0;
+ lstate->body_end = 0;
+ yyless(0);
+ BC(boundary);
+ return BODY;
+ } else if (lstate->preamble_start) {
+ yylval_param->position.start = lstate->preamble_start;
+ yylval_param->position.end = lstate->current_pos;
+ lstate->preamble_start = lstate->preamble_end = 0;
+ yyless(0);
+ BC(boundary);
+ return PREAMBLE;
+ } else {
+ BC(boundary);
+ yylval_param->string = strdup(yytext);
+ lstate->current_pos += yyleng;
+ return(BOUNDARY);
+ }
+ }
+ } else {
+ }
+
+ REJECT;
+}
+
+<body>(\r\n|\n) {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ lstate->current_pos += yyleng;
+ lstate->lineno++;
+}
+
+<body>\r {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ lstate->current_pos += yyleng;
+ /* dprintf2("stray CR in body...\n"); */
+}
+
+<body>[^\r\n]+ {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ lstate->current_pos += yyleng;
+}
+
+<body><<EOF>> {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ if (lstate->boundary_string == NULL && lstate->body_start) {
+ yylval_param->position.opaque_start = 0;
+ yylval_param->position.start = lstate->body_start;
+ yylval_param->position.end = lstate->current_pos;
+ lstate->body_start = 0;
+ return BODY;
+ } else if (lstate->body_start) {
+ return POSTAMBLE;
+ }
+ yyterminate();
+}
+
+<preamble,postamble>(\r\n|\n) {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ /* dprintf2("Preamble CR/LF at line %d\n", lineno); */
+ lstate->lineno++;
+ lstate->current_pos += yyleng;
+}
+
+<boundary>[^\r\n]+ {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ yylval_param->string = strdup(yytext);
+ lstate->current_pos += yyleng;
+ return BOUNDARY;
+}
+
+<endboundary>[^\r\n]+ {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ yylval_param->string = strdup(yytext);
+ lstate->current_pos += yyleng;
+ return ENDBOUNDARY;
+}
+
+<boundary>(\r\n|\n) {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ BC(headers);
+ lstate->lineno++;
+ lstate->current_pos += yyleng;
+ lstate->body_opaque_start = lstate->current_pos;
+ return EOL;
+}
+
+<endboundary>(\r\n|\n) {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ BC(postamble);
+ lstate->lineno++;
+ lstate->current_pos += yyleng;
+}
+
+<preamble>. {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ lstate->current_pos += yyleng;
+}
+
+
+<postamble>. {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ lstate->current_pos += yyleng;
+}
+
+(\r\n|\n) {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ lstate->lineno++;
+ lstate->current_pos += yyleng;
+ return EOL;
+}
+
+. {
+ struct lexer_state *lstate = yyget_extra(yyscanner);
+ lstate->current_pos += yyleng;
+ return((int)*yytext);
+}
+
+
+%%
+
+void reset_lexer_state(void *yyscanner, struct parser_state *pstate)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ struct lexer_state *lstate = &(pstate->lstate);
+
+ yyset_extra((void*)lstate, yyscanner);
+ BEGIN(0);
+ lstate->header_state = STATE_MAIL;
+ lstate->lineno = 0;
+ lstate->current_pos = 1;
+ lstate->condition = 0;
+
+ lstate->is_envelope = 1;
+
+ lstate->message_len = 0;
+ lstate->buffer_length = 0;
+
+ /* temporary marker variables */
+ lstate->body_opaque_start = 0;
+ lstate->body_start = 0;
+ lstate->body_end = 0;
+ lstate->preamble_start = 0;
+ lstate->preamble_end = 0;
+ lstate->postamble_start = 0;
+ lstate->postamble_end = 0;
+}
+
+void
+PARSER_setbuffer(const char *string, yyscan_t scanner)
+{
+ struct lexer_state *lstate = yyget_extra(scanner);
+ lstate->message_buffer = string;
+ yy_scan_string(string, scanner);
+}
+
+void
+PARSER_setfp(FILE *fp, yyscan_t scanner)
+{
+ /* looks like a bug in bison 2.2a -- the wrong code is generated for yyset_in !! */
+ struct yyguts_t * yyg = (struct yyguts_t*) scanner;
+ yyg->yyin_r = fp;
+
+ if (0) {
+ /* This is just to make a compiler warning go away */
+ yyunput(0, NULL, scanner);
+ }
+}
+
+/**
+ * Counts how many lines a given string represents in the message (in case of
+ * folded header values, for example, or a message body).
+ */
+int
+count_lines(char *txt)
+{
+ char *o;
+ int line;
+
+ line = 0;
+
+ for (o = txt; *o != '\0'; o++)
+ if (*o == '\n')
+ line++;
+
+ return line;
+}
diff --git a/trunk/main/minimime/mimeparser.tab.c b/trunk/main/minimime/mimeparser.tab.c
new file mode 100644
index 000000000..ef9a4a3fd
--- /dev/null
+++ b/trunk/main/minimime/mimeparser.tab.c
@@ -0,0 +1,2339 @@
+/* A Bison parser, made by GNU Bison 2.3. */
+
+/* Skeleton implementation for Bison's Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+ Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+ simplifying the original so-called "semantic" parser. */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+ infringing on user name space. This should be done even for local
+ variables, as they might otherwise be expanded by user macros.
+ There are some unavoidable exceptions within include files to
+ define necessary library symbols; they are noted "INFRINGES ON
+ USER NAME SPACE" below. */
+
+/* Identify Bison output. */
+#define YYBISON 1
+
+/* Bison version. */
+#define YYBISON_VERSION "2.3"
+
+/* Skeleton name. */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers. */
+#define YYPURE 1
+
+/* Using locations. */
+#define YYLSP_NEEDED 0
+
+/* Substitute the variable and function names. */
+#define yyparse mimeparser_yyparse
+#define yylex mimeparser_yylex
+#define yyerror mimeparser_yyerror
+#define yylval mimeparser_yylval
+#define yychar mimeparser_yychar
+#define yydebug mimeparser_yydebug
+#define yynerrs mimeparser_yynerrs
+
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ ANY = 258,
+ COLON = 259,
+ DASH = 260,
+ DQUOTE = 261,
+ ENDOFHEADERS = 262,
+ EOL = 263,
+ EOM = 264,
+ EQUAL = 265,
+ MIMEVERSION_HEADER = 266,
+ SEMICOLON = 267,
+ CONTENTDISPOSITION_HEADER = 268,
+ CONTENTENCODING_HEADER = 269,
+ CONTENTTYPE_HEADER = 270,
+ MAIL_HEADER = 271,
+ HEADERVALUE = 272,
+ BOUNDARY = 273,
+ ENDBOUNDARY = 274,
+ CONTENTTYPE_VALUE = 275,
+ TSPECIAL = 276,
+ WORD = 277,
+ BODY = 278,
+ PREAMBLE = 279,
+ POSTAMBLE = 280
+ };
+#endif
+/* Tokens. */
+#define ANY 258
+#define COLON 259
+#define DASH 260
+#define DQUOTE 261
+#define ENDOFHEADERS 262
+#define EOL 263
+#define EOM 264
+#define EQUAL 265
+#define MIMEVERSION_HEADER 266
+#define SEMICOLON 267
+#define CONTENTDISPOSITION_HEADER 268
+#define CONTENTENCODING_HEADER 269
+#define CONTENTTYPE_HEADER 270
+#define MAIL_HEADER 271
+#define HEADERVALUE 272
+#define BOUNDARY 273
+#define ENDBOUNDARY 274
+#define CONTENTTYPE_VALUE 275
+#define TSPECIAL 276
+#define WORD 277
+#define BODY 278
+#define PREAMBLE 279
+#define POSTAMBLE 280
+
+
+
+
+/* Copy the first part of user declarations. */
+#line 1 "mimeparser.y"
+
+/*
+ * Copyright (c) 2004 Jann Fischer. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/**
+ * These are the grammatic definitions in yacc syntax to parse MIME conform
+ * messages.
+ *
+ * TODO:
+ * - honour parse flags passed to us (partly done)
+ * - parse Content-Disposition header (partly done)
+ * - parse Content-Encoding header
+ */
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+
+#include "mimeparser.h"
+#include "mm.h"
+#include "mm_internal.h"
+
+int set_boundary(char *,struct parser_state *);
+int mimeparser_yywrap(void);
+void reset_environ(struct parser_state *pstate);
+int PARSER_initialize(struct parser_state *pstate, void *yyscanner);
+
+static char *PARSE_readmessagepart(size_t, size_t, size_t, size_t *,yyscan_t, struct parser_state *);
+FILE *mimeparser_yyget_in (yyscan_t yyscanner );
+
+
+
+/* Enabling traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+/* Enabling verbose error messages. */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+/* Enabling the token table. */
+#ifndef YYTOKEN_TABLE
+# define YYTOKEN_TABLE 0
+#endif
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+#line 67 "mimeparser.y"
+{
+ int number;
+ char *string;
+ struct s_position position;
+}
+/* Line 187 of yacc.c. */
+#line 220 "mimeparser.tab.c"
+ YYSTYPE;
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+
+
+/* Copy the second part of user declarations. */
+
+
+/* Line 216 of yacc.c. */
+#line 233 "mimeparser.tab.c"
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#elif (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+typedef signed char yytype_int8;
+#else
+typedef short int yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+# define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+# define YYSIZE_T size_t
+# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# else
+# define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if defined(YYENABLE_NLS)
+# if ENABLE_NLS
+# include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+# define YY_(msgid) dgettext ("bison-runtime", msgid)
+# endif
+# endif
+# ifndef YY_
+# define YY_(msgid) msgid
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E. */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(e) ((void) (e))
+#else
+# define YYUSE(e) /* empty */
+#endif
+
+/* Identity function, used to suppress warnings about constant conditions. */
+#ifndef lint
+# define YYID(n) (n)
+#else
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static int
+YYID (int i)
+#else
+static int
+YYID (i)
+ int i;
+#endif
+{
+ return i;
+}
+#endif
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols. */
+
+# ifdef YYSTACK_USE_ALLOCA
+# if YYSTACK_USE_ALLOCA
+# ifdef __GNUC__
+# define YYSTACK_ALLOC __builtin_alloca
+# elif defined __BUILTIN_VA_ARG_INCR
+# include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+# elif defined _AIX
+# define YYSTACK_ALLOC __alloca
+# elif defined _MSC_VER
+# include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+# define alloca _alloca
+# else
+# define YYSTACK_ALLOC alloca
+# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef _STDLIB_H
+# define _STDLIB_H 1
+# endif
+# endif
+# endif
+# endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+ /* Pacify GCC's `empty if-body' warning. */
+# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0))
+# ifndef YYSTACK_ALLOC_MAXIMUM
+ /* The OS might guarantee only one guard page at the bottom of the stack,
+ and a page size can be as small as 4096 bytes. So we cannot safely
+ invoke alloca (N) if N exceeds 4096. Use a slightly smaller number
+ to allow for a few compiler-allocated temporary stack slots. */
+# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+# endif
+# else
+# define YYSTACK_ALLOC YYMALLOC
+# define YYSTACK_FREE YYFREE
+# ifndef YYSTACK_ALLOC_MAXIMUM
+# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+# endif
+# if (defined __cplusplus && ! defined _STDLIB_H \
+ && ! ((defined YYMALLOC || defined malloc) \
+ && (defined YYFREE || defined free)))
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef _STDLIB_H
+# define _STDLIB_H 1
+# endif
+# endif
+# ifndef YYMALLOC
+# define YYMALLOC malloc
+# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# ifndef YYFREE
+# define YYFREE free
+# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+ && (! defined __cplusplus \
+ || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member. */
+union yyalloc
+{
+ yytype_int16 yyss;
+ YYSTYPE yyvs;
+ };
+
+/* The size of the maximum gap between one aligned stack and the next. */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+ N elements. */
+# define YYSTACK_BYTES(N) \
+ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \
+ + YYSTACK_GAP_MAXIMUM)
+
+/* Copy COUNT objects from FROM to TO. The source and destination do
+ not overlap. */
+# ifndef YYCOPY
+# if defined __GNUC__ && 1 < __GNUC__
+# define YYCOPY(To, From, Count) \
+ __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+# else
+# define YYCOPY(To, From, Count) \
+ do \
+ { \
+ YYSIZE_T yyi; \
+ for (yyi = 0; yyi < (Count); yyi++) \
+ (To)[yyi] = (From)[yyi]; \
+ } \
+ while (YYID (0))
+# endif
+# endif
+
+/* Relocate STACK from its old location to the new one. The
+ local variables YYSIZE and YYSTACKSIZE give the old and new number of
+ elements in the stack, and YYPTR gives the new location of the
+ stack. Advance YYPTR to a properly aligned location for the next
+ stack. */
+# define YYSTACK_RELOCATE(Stack) \
+ do \
+ { \
+ YYSIZE_T yynewbytes; \
+ YYCOPY (&yyptr->Stack, Stack, yysize); \
+ Stack = &yyptr->Stack; \
+ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+ yyptr += yynewbytes / sizeof (*yyptr); \
+ } \
+ while (YYID (0))
+
+#endif
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL 26
+/* YYLAST -- Last index in YYTABLE. */
+#define YYLAST 61
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS 28
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS 29
+/* YYNRULES -- Number of rules. */
+#define YYNRULES 50
+/* YYNRULES -- Number of states. */
+#define YYNSTATES 83
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
+#define YYUNDEFTOK 2
+#define YYMAXUTOK 280
+
+#define YYTRANSLATE(YYX) \
+ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */
+static const yytype_uint8 yytranslate[] =
+{
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 27, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 26, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+ YYRHS. */
+static const yytype_uint8 yyprhs[] =
+{
+ 0, 0, 3, 5, 7, 8, 15, 18, 21, 23,
+ 25, 27, 28, 30, 31, 34, 36, 40, 42, 44,
+ 46, 48, 50, 52, 57, 61, 66, 72, 77, 83,
+ 85, 90, 95, 98, 101, 103, 107, 111, 114, 116,
+ 120, 123, 125, 129, 133, 135, 137, 141, 143, 146,
+ 148
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS. */
+static const yytype_int8 yyrhs[] =
+{
+ 29, 0, -1, 30, -1, 32, -1, -1, 33, 34,
+ 31, 36, 55, 35, -1, 33, 56, -1, 38, 33,
+ -1, 53, -1, 38, -1, 24, -1, -1, 25, -1,
+ -1, 36, 37, -1, 37, -1, 54, 33, 56, -1,
+ 39, -1, 40, -1, 41, -1, 43, -1, 44, -1,
+ 45, -1, 16, 4, 22, 8, -1, 16, 4, 8,
+ -1, 15, 4, 47, 8, -1, 15, 4, 47, 48,
+ 8, -1, 13, 4, 42, 8, -1, 13, 4, 42,
+ 49, 8, -1, 22, -1, 14, 4, 22, 8, -1,
+ 11, 4, 22, 8, -1, 46, 8, -1, 46, 3,
+ -1, 3, -1, 22, 26, 22, -1, 12, 50, 48,
+ -1, 12, 50, -1, 12, -1, 12, 51, 49, -1,
+ 12, 51, -1, 12, -1, 22, 10, 52, -1, 22,
+ 10, 52, -1, 22, -1, 21, -1, 27, 21, 27,
+ -1, 7, -1, 18, 8, -1, 19, -1, 23, -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
+static const yytype_uint16 yyrline[] =
+{
+ 0, 112, 112, 114, 119, 118, 131, 139, 141, 165,
+ 169, 184, 188, 191, 195, 197, 201, 216, 218, 228,
+ 230, 232, 234, 248, 255, 274, 282, 292, 298, 306,
+ 329, 336, 343, 347, 349, 353, 362, 364, 366, 380,
+ 382, 384, 398, 429, 443, 449, 464, 472, 479, 498,
+ 517
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+ First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+static const char *const yytname[] =
+{
+ "$end", "error", "$undefined", "ANY", "COLON", "DASH", "DQUOTE",
+ "ENDOFHEADERS", "EOL", "EOM", "EQUAL", "MIMEVERSION_HEADER", "SEMICOLON",
+ "CONTENTDISPOSITION_HEADER", "CONTENTENCODING_HEADER",
+ "CONTENTTYPE_HEADER", "MAIL_HEADER", "HEADERVALUE", "BOUNDARY",
+ "ENDBOUNDARY", "CONTENTTYPE_VALUE", "TSPECIAL", "WORD", "BODY",
+ "PREAMBLE", "POSTAMBLE", "'/'", "'\"'", "$accept", "message",
+ "multipart_message", "@1", "singlepart_message", "headers", "preamble",
+ "postamble", "mimeparts", "mimepart", "header", "mail_header",
+ "contenttype_header", "contentdisposition_header", "content_disposition",
+ "contentencoding_header", "mimeversion_header", "invalid_header", "any",
+ "mimetype", "contenttype_parameters", "content_disposition_parameters",
+ "contenttype_parameter", "content_disposition_parameter",
+ "contenttype_parameter_value", "end_headers", "boundary", "endboundary",
+ "body", 0
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+ token YYLEX-NUM. */
+static const yytype_uint16 yytoknum[] =
+{
+ 0, 256, 257, 258, 259, 260, 261, 262, 263, 264,
+ 265, 266, 267, 268, 269, 270, 271, 272, 273, 274,
+ 275, 276, 277, 278, 279, 280, 47, 34
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
+static const yytype_uint8 yyr1[] =
+{
+ 0, 28, 29, 29, 31, 30, 32, 33, 33, 33,
+ 34, 34, 35, 35, 36, 36, 37, 38, 38, 38,
+ 38, 38, 38, 39, 39, 40, 40, 41, 41, 42,
+ 43, 44, 45, 46, 46, 47, 48, 48, 48, 49,
+ 49, 49, 50, 51, 52, 52, 52, 53, 54, 55,
+ 56
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
+static const yytype_uint8 yyr2[] =
+{
+ 0, 2, 1, 1, 0, 6, 2, 2, 1, 1,
+ 1, 0, 1, 0, 2, 1, 3, 1, 1, 1,
+ 1, 1, 1, 4, 3, 4, 5, 4, 5, 1,
+ 4, 4, 2, 2, 1, 3, 3, 2, 1, 3,
+ 2, 1, 3, 3, 1, 1, 3, 1, 2, 1,
+ 1
+};
+
+/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
+ STATE-NUM when YYTABLE doesn't specify something else to do. Zero
+ means the default is an error. */
+static const yytype_uint8 yydefact[] =
+{
+ 0, 34, 47, 0, 0, 0, 0, 0, 0, 2,
+ 3, 11, 9, 17, 18, 19, 20, 21, 22, 0,
+ 8, 0, 0, 0, 0, 0, 1, 50, 10, 4,
+ 6, 7, 33, 32, 0, 29, 0, 0, 0, 0,
+ 24, 0, 0, 31, 27, 41, 0, 30, 0, 25,
+ 38, 0, 23, 0, 0, 15, 0, 0, 40, 28,
+ 35, 0, 37, 26, 48, 49, 14, 13, 0, 0,
+ 39, 0, 36, 12, 5, 16, 45, 44, 0, 43,
+ 42, 0, 46
+};
+
+/* YYDEFGOTO[NTERM-NUM]. */
+static const yytype_int8 yydefgoto[] =
+{
+ -1, 8, 9, 42, 10, 11, 29, 74, 54, 55,
+ 12, 13, 14, 15, 36, 16, 17, 18, 19, 39,
+ 51, 46, 62, 58, 79, 20, 56, 67, 30
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ STATE-NUM. */
+#define YYPACT_NINF -20
+static const yytype_int8 yypact[] =
+{
+ 3, -20, -20, 17, 21, 22, 23, 24, 5, -20,
+ -20, -11, 3, -20, -20, -20, -20, -20, -20, 1,
+ -20, 7, 8, 9, 10, -7, -20, -20, -20, -20,
+ -20, -20, -20, -20, 25, -20, -1, 26, 11, 12,
+ -20, 27, 18, -20, -20, 16, 31, -20, 19, -20,
+ 20, 32, -20, 35, 4, -20, 3, 36, 33, -20,
+ -20, 37, 38, -20, -20, -20, -20, 28, 29, -19,
+ -20, -19, -20, -20, -20, -20, -20, -20, 30, -20,
+ -20, 34, -20
+};
+
+/* YYPGOTO[NTERM-NUM]. */
+static const yytype_int8 yypgoto[] =
+{
+ -20, -20, -20, -20, -20, -12, -20, -20, -20, -6,
+ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20,
+ -13, -4, -20, -20, -16, -20, -20, -20, -10
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If
+ positive, shift that token. If negative, reduce the rule which
+ number is the opposite. If zero, do what YYDEFACT says.
+ If YYTABLE_NINF, syntax error. */
+#define YYTABLE_NINF -1
+static const yytype_uint8 yytable[] =
+{
+ 31, 40, 76, 77, 32, 26, 1, 44, 78, 33,
+ 2, 45, 27, 28, 3, 41, 4, 5, 6, 7,
+ 49, 21, 53, 65, 50, 22, 23, 24, 25, 34,
+ 35, 37, 38, 43, 47, 52, 53, 48, 57, 59,
+ 63, 60, 61, 64, 68, 45, 69, 71, 66, 72,
+ 50, 81, 27, 73, 70, 80, 0, 0, 75, 0,
+ 0, 82
+};
+
+static const yytype_int8 yycheck[] =
+{
+ 12, 8, 21, 22, 3, 0, 3, 8, 27, 8,
+ 7, 12, 23, 24, 11, 22, 13, 14, 15, 16,
+ 8, 4, 18, 19, 12, 4, 4, 4, 4, 22,
+ 22, 22, 22, 8, 8, 8, 18, 26, 22, 8,
+ 8, 22, 22, 8, 56, 12, 10, 10, 54, 62,
+ 12, 21, 23, 25, 58, 71, -1, -1, 68, -1,
+ -1, 27
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+ symbol of state STATE-NUM. */
+static const yytype_uint8 yystos[] =
+{
+ 0, 3, 7, 11, 13, 14, 15, 16, 29, 30,
+ 32, 33, 38, 39, 40, 41, 43, 44, 45, 46,
+ 53, 4, 4, 4, 4, 4, 0, 23, 24, 34,
+ 56, 33, 3, 8, 22, 22, 42, 22, 22, 47,
+ 8, 22, 31, 8, 8, 12, 49, 8, 26, 8,
+ 12, 48, 8, 18, 36, 37, 54, 22, 51, 8,
+ 22, 22, 50, 8, 8, 19, 37, 55, 33, 10,
+ 49, 10, 48, 25, 35, 56, 21, 22, 27, 52,
+ 52, 21, 27
+};
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY (-2)
+#define YYEOF 0
+
+#define YYACCEPT goto yyacceptlab
+#define YYABORT goto yyabortlab
+#define YYERROR goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror. This remains here temporarily
+ to ease the transition to the new meaning of YYERROR, for GCC.
+ Once GCC version 2 has supplanted version 1, this can go. */
+
+#define YYFAIL goto yyerrlab
+
+#define YYRECOVERING() (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value) \
+do \
+ if (yychar == YYEMPTY && yylen == 1) \
+ { \
+ yychar = (Token); \
+ yylval = (Value); \
+ yytoken = YYTRANSLATE (yychar); \
+ YYPOPSTACK (1); \
+ goto yybackup; \
+ } \
+ else \
+ { \
+ yyerror (pstate, yyscanner, YY_("syntax error: cannot back up")); \
+ YYERROR; \
+ } \
+while (YYID (0))
+
+
+#define YYTERROR 1
+#define YYERRCODE 256
+
+
+/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
+ If N is 0, then set CURRENT to the empty location which ends
+ the previous symbol: RHS[0] (always defined). */
+
+#define YYRHSLOC(Rhs, K) ((Rhs)[K])
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N) \
+ do \
+ if (YYID (N)) \
+ { \
+ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \
+ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \
+ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \
+ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \
+ } \
+ else \
+ { \
+ (Current).first_line = (Current).last_line = \
+ YYRHSLOC (Rhs, 0).last_line; \
+ (Current).first_column = (Current).last_column = \
+ YYRHSLOC (Rhs, 0).last_column; \
+ } \
+ while (YYID (0))
+#endif
+
+
+/* YY_LOCATION_PRINT -- Print the location on the stream.
+ This macro was not mandated originally: define only if we know
+ we won't break user code: when these are the locations we know. */
+
+#ifndef YY_LOCATION_PRINT
+# if defined(YYLTYPE_IS_TRIVIAL)
+# define YY_LOCATION_PRINT(File, Loc) \
+ fprintf (File, "%d.%d-%d.%d", \
+ (Loc).first_line, (Loc).first_column, \
+ (Loc).last_line, (Loc).last_column)
+# else
+# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+# endif
+#endif
+
+
+/* YYLEX -- calling `yylex' with the right arguments. */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (&yylval, YYLEX_PARAM)
+#else
+# define YYLEX yylex (&yylval, yyscanner)
+#endif
+
+/* Enable debugging if requested. */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+# include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+# define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args) \
+do { \
+ if (yydebug) \
+ YYFPRINTF Args; \
+} while (YYID (0))
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \
+do { \
+ if (yydebug) \
+ { \
+ YYFPRINTF (stderr, "%s ", Title); \
+ yy_symbol_print (stderr, \
+ Type, Value, pstate, yyscanner); \
+ YYFPRINTF (stderr, "\n"); \
+ } \
+} while (YYID (0))
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, struct parser_state *pstate, void *yyscanner)
+#else
+static void
+yy_symbol_value_print (yyoutput, yytype, yyvaluep, pstate, yyscanner)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+ struct parser_state *pstate;
+ void *yyscanner;
+#endif
+{
+ if (!yyvaluep)
+ return;
+ YYUSE (pstate);
+ YYUSE (yyscanner);
+# ifdef YYPRINT
+ if (yytype < YYNTOKENS)
+ YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# else
+ YYUSE (yyoutput);
+# endif
+ switch (yytype)
+ {
+ default:
+ break;
+ }
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, struct parser_state *pstate, void *yyscanner)
+#else
+static void
+yy_symbol_print (yyoutput, yytype, yyvaluep, pstate, yyscanner)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+ struct parser_state *pstate;
+ void *yyscanner;
+#endif
+{
+ if (yytype < YYNTOKENS)
+ YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+ else
+ YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+ yy_symbol_value_print (yyoutput, yytype, yyvaluep, pstate, yyscanner);
+ YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included). |
+`------------------------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_stack_print (yytype_int16 *bottom, yytype_int16 *top)
+#else
+static void
+yy_stack_print (bottom, top)
+ yytype_int16 *bottom;
+ yytype_int16 *top;
+#endif
+{
+ YYFPRINTF (stderr, "Stack now");
+ for (; bottom <= top; ++bottom)
+ YYFPRINTF (stderr, " %d", *bottom);
+ YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top) \
+do { \
+ if (yydebug) \
+ yy_stack_print ((Bottom), (Top)); \
+} while (YYID (0))
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced. |
+`------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_reduce_print (YYSTYPE *yyvsp, int yyrule, struct parser_state *pstate, void *yyscanner)
+#else
+static void
+yy_reduce_print (yyvsp, yyrule, pstate, yyscanner)
+ YYSTYPE *yyvsp;
+ int yyrule;
+ struct parser_state *pstate;
+ void *yyscanner;
+#endif
+{
+ int yynrhs = yyr2[yyrule];
+ int yyi;
+ unsigned long int yylno = yyrline[yyrule];
+ YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+ yyrule - 1, yylno);
+ /* The symbols being reduced. */
+ for (yyi = 0; yyi < yynrhs; yyi++)
+ {
+ fprintf (stderr, " $%d = ", yyi + 1);
+ yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
+ &(yyvsp[(yyi + 1) - (yynrhs)])
+ , pstate, yyscanner);
+ fprintf (stderr, "\n");
+ }
+}
+
+# define YY_REDUCE_PRINT(Rule) \
+do { \
+ if (yydebug) \
+ yy_reduce_print (yyvsp, Rule, pstate, yyscanner); \
+} while (YYID (0))
+
+/* Nonzero means print parse trace. It is left uninitialized so that
+ multiple parsers can coexist. */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks. */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+ if the built-in stack extension method is used).
+
+ Do not make this value too large; the results are undefined if
+ YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+ evaluated with infinite-precision integer arithmetic. */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+# if defined __GLIBC__ && defined _STRING_H
+# define yystrlen strlen
+# else
+/* Return the length of YYSTR. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static YYSIZE_T
+yystrlen (const char *yystr)
+#else
+static YYSIZE_T
+yystrlen (yystr)
+ const char *yystr;
+#endif
+{
+ YYSIZE_T yylen;
+ for (yylen = 0; yystr[yylen]; yylen++)
+ continue;
+ return yylen;
+}
+# endif
+# endif
+
+# ifndef yystpcpy
+# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+# define yystpcpy stpcpy
+# else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+ YYDEST. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+#else
+static char *
+yystpcpy (yydest, yysrc)
+ char *yydest;
+ const char *yysrc;
+#endif
+{
+ char *yyd = yydest;
+ const char *yys = yysrc;
+
+ while ((*yyd++ = *yys++) != '\0')
+ continue;
+
+ return yyd - 1;
+}
+# endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+ quotes and backslashes, so that it's suitable for yyerror. The
+ heuristic is that double-quoting is unnecessary unless the string
+ contains an apostrophe, a comma, or backslash (other than
+ backslash-backslash). YYSTR is taken from yytname. If YYRES is
+ null, do not copy; instead, return the length of what the result
+ would have been. */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+ if (*yystr == '"')
+ {
+ YYSIZE_T yyn = 0;
+ char const *yyp = yystr;
+
+ for (;;)
+ switch (*++yyp)
+ {
+ case '\'':
+ case ',':
+ goto do_not_strip_quotes;
+
+ case '\\':
+ if (*++yyp != '\\')
+ goto do_not_strip_quotes;
+ /* Fall through. */
+ default:
+ if (yyres)
+ yyres[yyn] = *yyp;
+ yyn++;
+ break;
+
+ case '"':
+ if (yyres)
+ yyres[yyn] = '\0';
+ return yyn;
+ }
+ do_not_strip_quotes: ;
+ }
+
+ if (! yyres)
+ return yystrlen (yystr);
+
+ return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+/* Copy into YYRESULT an error message about the unexpected token
+ YYCHAR while in state YYSTATE. Return the number of bytes copied,
+ including the terminating null byte. If YYRESULT is null, do not
+ copy anything; just return the number of bytes that would be
+ copied. As a special case, return 0 if an ordinary "syntax error"
+ message will do. Return YYSIZE_MAXIMUM if overflow occurs during
+ size calculation. */
+static YYSIZE_T
+yysyntax_error (char *yyresult, int yystate, int yychar)
+{
+ int yyn = yypact[yystate];
+
+ if (! (YYPACT_NINF < yyn && yyn <= YYLAST))
+ return 0;
+ else
+ {
+ int yytype = YYTRANSLATE (yychar);
+ YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]);
+ YYSIZE_T yysize = yysize0;
+ YYSIZE_T yysize1;
+ int yysize_overflow = 0;
+ enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+ int yyx;
+
+# if 0
+ /* This is so xgettext sees the translatable formats that are
+ constructed on the fly. */
+ YY_("syntax error, unexpected %s");
+ YY_("syntax error, unexpected %s, expecting %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s or %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s");
+# endif
+ char *yyfmt;
+ char const *yyf;
+ static char const yyunexpected[] = "syntax error, unexpected %s";
+ static char const yyexpecting[] = ", expecting %s";
+ static char const yyor[] = " or %s";
+ char yyformat[sizeof yyunexpected
+ + sizeof yyexpecting - 1
+ + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2)
+ * (sizeof yyor - 1))];
+ char const *yyprefix = yyexpecting;
+
+ /* Start YYX at -YYN if negative to avoid negative indexes in
+ YYCHECK. */
+ int yyxbegin = yyn < 0 ? -yyn : 0;
+
+ /* Stay within bounds of both yycheck and yytname. */
+ int yychecklim = YYLAST - yyn + 1;
+ int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+ int yycount = 1;
+
+ yyarg[0] = yytname[yytype];
+ yyfmt = yystpcpy (yyformat, yyunexpected);
+
+ for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+ {
+ if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+ {
+ yycount = 1;
+ yysize = yysize0;
+ yyformat[sizeof yyunexpected - 1] = '\0';
+ break;
+ }
+ yyarg[yycount++] = yytname[yyx];
+ yysize1 = yysize + yytnamerr (0, yytname[yyx]);
+ yysize_overflow |= (yysize1 < yysize);
+ yysize = yysize1;
+ yyfmt = yystpcpy (yyfmt, yyprefix);
+ yyprefix = yyor;
+ }
+
+ yyf = YY_(yyformat);
+ yysize1 = yysize + yystrlen (yyf);
+ yysize_overflow |= (yysize1 < yysize);
+ yysize = yysize1;
+
+ if (yysize_overflow)
+ return YYSIZE_MAXIMUM;
+
+ if (yyresult)
+ {
+ /* Avoid sprintf, as that infringes on the user's name space.
+ Don't have undefined behavior even if the translation
+ produced a string with the wrong number of "%s"s. */
+ char *yyp = yyresult;
+ int yyi = 0;
+ while ((*yyp = *yyf) != '\0')
+ {
+ if (*yyp == '%' && yyf[1] == 's' && yyi < yycount)
+ {
+ yyp += yytnamerr (yyp, yyarg[yyi++]);
+ yyf += 2;
+ }
+ else
+ {
+ yyp++;
+ yyf++;
+ }
+ }
+ }
+ return yysize;
+ }
+}
+#endif /* YYERROR_VERBOSE */
+
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol. |
+`-----------------------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, struct parser_state *pstate, void *yyscanner)
+#else
+static void
+yydestruct (yymsg, yytype, yyvaluep, pstate, yyscanner)
+ const char *yymsg;
+ int yytype;
+ YYSTYPE *yyvaluep;
+ struct parser_state *pstate;
+ void *yyscanner;
+#endif
+{
+ YYUSE (yyvaluep);
+ YYUSE (pstate);
+ YYUSE (yyscanner);
+
+ if (!yymsg)
+ yymsg = "Deleting";
+ YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+ switch (yytype)
+ {
+
+ default:
+ break;
+ }
+}
+
+
+/* Prevent warnings from -Wmissing-prototypes. */
+
+#ifdef YYPARSE_PARAM
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void *YYPARSE_PARAM);
+#else
+int yyparse ();
+#endif
+#else /* ! YYPARSE_PARAM */
+#if defined __STDC__ || defined __cplusplus
+int yyparse (struct parser_state *pstate, void *yyscanner);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+
+
+
+
+
+/*----------.
+| yyparse. |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void *YYPARSE_PARAM)
+#else
+int
+yyparse (YYPARSE_PARAM)
+ void *YYPARSE_PARAM;
+#endif
+#else /* ! YYPARSE_PARAM */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (struct parser_state *pstate, void *yyscanner)
+#else
+int
+yyparse (pstate, yyscanner)
+ struct parser_state *pstate;
+ void *yyscanner;
+#endif
+#endif
+{
+ /* The look-ahead symbol. */
+int yychar;
+
+/* The semantic value of the look-ahead symbol. */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far. */
+int yynerrs;
+
+ int yystate;
+ int yyn;
+ int yyresult;
+ /* Number of tokens to shift before error messages enabled. */
+ int yyerrstatus;
+ /* Look-ahead token as an internal (translated) token number. */
+ int yytoken = 0;
+#if YYERROR_VERBOSE
+ /* Buffer for error messages, and its allocated size. */
+ char yymsgbuf[128];
+ char *yymsg = yymsgbuf;
+ YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+ /* Three stacks and their tools:
+ `yyss': related to states,
+ `yyvs': related to semantic values,
+ `yyls': related to locations.
+
+ Refer to the stacks thru separate pointers, to allow yyoverflow
+ to reallocate them elsewhere. */
+
+ /* The state stack. */
+ yytype_int16 yyssa[YYINITDEPTH];
+ yytype_int16 *yyss = yyssa;
+ yytype_int16 *yyssp;
+
+ /* The semantic value stack. */
+ YYSTYPE yyvsa[YYINITDEPTH];
+ YYSTYPE *yyvs = yyvsa;
+ YYSTYPE *yyvsp;
+
+
+
+#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N))
+
+ YYSIZE_T yystacksize = YYINITDEPTH;
+
+ /* The variables used to return semantic value and location from the
+ action routines. */
+ YYSTYPE yyval;
+
+
+ /* The number of symbols on the RHS of the reduced rule.
+ Keep to zero when no symbol should be popped. */
+ int yylen = 0;
+
+ YYDPRINTF ((stderr, "Starting parse\n"));
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+
+ /* Initialize stack pointers.
+ Waste one element of value and location stack
+ so that they stay on the same level as the state stack.
+ The wasted elements are never initialized. */
+
+ yyssp = yyss;
+ yyvsp = yyvs;
+
+ goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate. |
+`------------------------------------------------------------*/
+ yynewstate:
+ /* In all cases, when you get here, the value and location stacks
+ have just been pushed. So pushing a state here evens the stacks. */
+ yyssp++;
+
+ yysetstate:
+ *yyssp = yystate;
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ {
+ /* Get the current used size of the three stacks, in elements. */
+ YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ {
+ /* Give user a chance to reallocate the stack. Use copies of
+ these so that the &'s don't force the real ones into
+ memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ yytype_int16 *yyss1 = yyss;
+
+
+ /* Each stack pointer address is followed by the size of the
+ data in use in that stack, in bytes. This used to be a
+ conditional around just the two extra args, but that might
+ be undefined if yyoverflow is a macro. */
+ yyoverflow (YY_("memory exhausted"),
+ &yyss1, yysize * sizeof (*yyssp),
+ &yyvs1, yysize * sizeof (*yyvsp),
+
+ &yystacksize);
+
+ yyss = yyss1;
+ yyvs = yyvs1;
+ }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+ goto yyexhaustedlab;
+# else
+ /* Extend the stack our own way. */
+ if (YYMAXDEPTH <= yystacksize)
+ goto yyexhaustedlab;
+ yystacksize *= 2;
+ if (YYMAXDEPTH < yystacksize)
+ yystacksize = YYMAXDEPTH;
+
+ {
+ yytype_int16 *yyss1 = yyss;
+ union yyalloc *yyptr =
+ (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+ if (! yyptr)
+ goto yyexhaustedlab;
+ YYSTACK_RELOCATE (yyss);
+ YYSTACK_RELOCATE (yyvs);
+
+# undef YYSTACK_RELOCATE
+ if (yyss1 != yyssa)
+ YYSTACK_FREE (yyss1);
+ }
+# endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + yysize - 1;
+ yyvsp = yyvs + yysize - 1;
+
+
+ YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+ (unsigned long int) yystacksize));
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ YYABORT;
+ }
+
+ YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+ goto yybackup;
+
+/*-----------.
+| yybackup. |
+`-----------*/
+yybackup:
+
+ /* Do appropriate processing given the current state. Read a
+ look-ahead token if we need one and don't already have one. */
+
+ /* First try to decide what to do without reference to look-ahead token. */
+ yyn = yypact[yystate];
+ if (yyn == YYPACT_NINF)
+ goto yydefault;
+
+ /* Not known => get a look-ahead token if don't already have one. */
+
+ /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */
+ if (yychar == YYEMPTY)
+ {
+ YYDPRINTF ((stderr, "Reading a token: "));
+ yychar = YYLEX;
+ }
+
+ if (yychar <= YYEOF)
+ {
+ yychar = yytoken = YYEOF;
+ YYDPRINTF ((stderr, "Now at end of input.\n"));
+ }
+ else
+ {
+ yytoken = YYTRANSLATE (yychar);
+ YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+ }
+
+ /* If the proper action on seeing token YYTOKEN is to reduce or to
+ detect an error, take that action. */
+ yyn += yytoken;
+ if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+ goto yydefault;
+ yyn = yytable[yyn];
+ if (yyn <= 0)
+ {
+ if (yyn == 0 || yyn == YYTABLE_NINF)
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ /* Count tokens shifted since error; after three, turn off error
+ status. */
+ if (yyerrstatus)
+ yyerrstatus--;
+
+ /* Shift the look-ahead token. */
+ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+ /* Discard the shifted token unless it is eof. */
+ if (yychar != YYEOF)
+ yychar = YYEMPTY;
+
+ yystate = yyn;
+ *++yyvsp = yylval;
+
+ goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state. |
+`-----------------------------------------------------------*/
+yydefault:
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+ goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction. |
+`-----------------------------*/
+yyreduce:
+ /* yyn is the number of a rule to reduce with. */
+ yylen = yyr2[yyn];
+
+ /* If YYLEN is nonzero, implement the default value of the action:
+ `$$ = $1'.
+
+ Otherwise, the following line sets YYVAL to garbage.
+ This behavior is undocumented and Bison
+ users should not rely upon it. Assigning to YYVAL
+ unconditionally makes the parser a bit smaller, and it avoids a
+ GCC warning that YYVAL may be used uninitialized. */
+ yyval = yyvsp[1-yylen];
+
+
+ YY_REDUCE_PRINT (yyn);
+ switch (yyn)
+ {
+ case 4:
+#line 119 "mimeparser.y"
+ {
+ mm_context_attachpart(pstate->ctx, pstate->current_mimepart);
+ pstate->current_mimepart = mm_mimepart_new();
+ pstate->have_contenttype = 0;
+ ;}
+ break;
+
+ case 5:
+#line 125 "mimeparser.y"
+ {
+ dprintf2(pstate,"This was a multipart message\n");
+ ;}
+ break;
+
+ case 6:
+#line 132 "mimeparser.y"
+ {
+ dprintf2(pstate,"This was a single part message\n");
+ mm_context_attachpart(pstate->ctx, pstate->current_mimepart);
+ ;}
+ break;
+
+ case 8:
+#line 142 "mimeparser.y"
+ {
+ /* If we did not find a Content-Type header for the current
+ * MIME part (or envelope), we create one and attach it.
+ * According to the RFC, a type of "text/plain" and a
+ * charset of "us-ascii" can be assumed.
+ */
+ struct mm_content *ct;
+ struct mm_param *param;
+
+ if (!pstate->have_contenttype) {
+ ct = mm_content_new();
+ mm_content_settype(ct, "text/plain");
+
+ param = mm_param_new();
+ param->name = xstrdup("charset");
+ param->value = xstrdup("us-ascii");
+
+ mm_content_attachtypeparam(ct, param);
+ mm_mimepart_attachcontenttype(pstate->current_mimepart, ct);
+ }
+ pstate->have_contenttype = 0;
+ ;}
+ break;
+
+ case 10:
+#line 170 "mimeparser.y"
+ {
+ char *preamble;
+ size_t offset;
+
+ if ((yyvsp[(1) - (1)].position).start != (yyvsp[(1) - (1)].position).end) {
+ preamble = PARSE_readmessagepart(0, (yyvsp[(1) - (1)].position).start, (yyvsp[(1) - (1)].position).end,
+ &offset,yyscanner,pstate);
+ if (preamble == NULL) {
+ return(-1);
+ }
+ pstate->ctx->preamble = preamble;
+ dprintf2(pstate,"PREAMBLE:\n%s\n", preamble);
+ }
+ ;}
+ break;
+
+ case 12:
+#line 189 "mimeparser.y"
+ {
+ ;}
+ break;
+
+ case 16:
+#line 202 "mimeparser.y"
+ {
+
+ if (mm_context_attachpart(pstate->ctx, pstate->current_mimepart) == -1) {
+ mm_errno = MM_ERROR_ERRNO;
+ return(-1);
+ }
+
+ pstate->temppart = mm_mimepart_new();
+ pstate->current_mimepart = pstate->temppart;
+ pstate->mime_parts++;
+ ;}
+ break;
+
+ case 18:
+#line 219 "mimeparser.y"
+ {
+ pstate->have_contenttype = 1;
+ if (mm_content_iscomposite(pstate->envelope->type)) {
+ pstate->ctx->messagetype = MM_MSGTYPE_MULTIPART;
+ } else {
+ pstate->ctx->messagetype = MM_MSGTYPE_FLAT;
+ }
+ ;}
+ break;
+
+ case 22:
+#line 235 "mimeparser.y"
+ {
+ if (pstate->parsemode != MM_PARSE_LOOSE) {
+ mm_errno = MM_ERROR_PARSE;
+ mm_error_setmsg("invalid header encountered");
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(-1);
+ } else {
+ /* TODO: attach MM_WARNING_INVHDR */
+ }
+ ;}
+ break;
+
+ case 23:
+#line 249 "mimeparser.y"
+ {
+ struct mm_mimeheader *hdr;
+ hdr = mm_mimeheader_generate((yyvsp[(1) - (4)].string), (yyvsp[(3) - (4)].string));
+ mm_mimepart_attachheader(pstate->current_mimepart, hdr);
+ ;}
+ break;
+
+ case 24:
+#line 256 "mimeparser.y"
+ {
+ struct mm_mimeheader *hdr;
+
+ if (pstate->parsemode != MM_PARSE_LOOSE) {
+ mm_errno = MM_ERROR_MIME;
+ mm_error_setmsg("invalid header encountered");
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(-1);
+ } else {
+ /* TODO: attach MM_WARNING_INVHDR */
+ }
+
+ hdr = mm_mimeheader_generate((yyvsp[(1) - (3)].string), xstrdup(""));
+ mm_mimepart_attachheader(pstate->current_mimepart, hdr);
+ ;}
+ break;
+
+ case 25:
+#line 275 "mimeparser.y"
+ {
+ mm_content_settype(pstate->ctype, "%s", (yyvsp[(3) - (4)].string));
+ mm_mimepart_attachcontenttype(pstate->current_mimepart, pstate->ctype);
+ dprintf2(pstate,"Content-Type -> %s\n", (yyvsp[(3) - (4)].string));
+ pstate->ctype = mm_content_new();
+ ;}
+ break;
+
+ case 26:
+#line 283 "mimeparser.y"
+ {
+ mm_content_settype(pstate->ctype, "%s", (yyvsp[(3) - (5)].string));
+ mm_mimepart_attachcontenttype(pstate->current_mimepart, pstate->ctype);
+ dprintf2(pstate,"Content-Type (P) -> %s\n", (yyvsp[(3) - (5)].string));
+ pstate->ctype = mm_content_new();
+ ;}
+ break;
+
+ case 27:
+#line 293 "mimeparser.y"
+ {
+ dprintf2(pstate,"Content-Disposition -> %s\n", (yyvsp[(3) - (4)].string));
+ pstate->ctype->disposition_type = xstrdup((yyvsp[(3) - (4)].string));
+ ;}
+ break;
+
+ case 28:
+#line 299 "mimeparser.y"
+ {
+ dprintf2(pstate,"Content-Disposition (P) -> %s; params\n", (yyvsp[(3) - (5)].string));
+ pstate->ctype->disposition_type = xstrdup((yyvsp[(3) - (5)].string));
+ ;}
+ break;
+
+ case 29:
+#line 307 "mimeparser.y"
+ {
+ /*
+ * According to RFC 2183, the content disposition value may
+ * only be "inline", "attachment" or an extension token. We
+ * catch invalid values here if we are not in loose parsing
+ * mode.
+ */
+ if (strcasecmp((yyvsp[(1) - (1)].string), "inline") && strcasecmp((yyvsp[(1) - (1)].string), "attachment")
+ && strncasecmp((yyvsp[(1) - (1)].string), "X-", 2)) {
+ if (pstate->parsemode != MM_PARSE_LOOSE) {
+ mm_errno = MM_ERROR_MIME;
+ mm_error_setmsg("invalid content-disposition");
+ return(-1);
+ }
+ } else {
+ /* TODO: attach MM_WARNING_INVHDR */
+ }
+ (yyval.string) = (yyvsp[(1) - (1)].string);
+ ;}
+ break;
+
+ case 30:
+#line 330 "mimeparser.y"
+ {
+ dprintf2(pstate,"Content-Transfer-Encoding -> %s\n", (yyvsp[(3) - (4)].string));
+ ;}
+ break;
+
+ case 31:
+#line 337 "mimeparser.y"
+ {
+ dprintf2(pstate,"MIME-Version -> '%s'\n", (yyvsp[(3) - (4)].string));
+ ;}
+ break;
+
+ case 35:
+#line 354 "mimeparser.y"
+ {
+ char type[255];
+ snprintf(type, sizeof(type), "%s/%s", (yyvsp[(1) - (3)].string), (yyvsp[(3) - (3)].string));
+ (yyval.string) = type;
+ ;}
+ break;
+
+ case 38:
+#line 367 "mimeparser.y"
+ {
+ if (pstate->parsemode != MM_PARSE_LOOSE) {
+ mm_errno = MM_ERROR_MIME;
+ mm_error_setmsg("invalid Content-Type header");
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(-1);
+ } else {
+ /* TODO: attach MM_WARNING_INVHDR */
+ }
+ ;}
+ break;
+
+ case 41:
+#line 385 "mimeparser.y"
+ {
+ if (pstate->parsemode != MM_PARSE_LOOSE) {
+ mm_errno = MM_ERROR_MIME;
+ mm_error_setmsg("invalid Content-Disposition header");
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(-1);
+ } else {
+ /* TODO: attach MM_WARNING_INVHDR */
+ }
+ ;}
+ break;
+
+ case 42:
+#line 399 "mimeparser.y"
+ {
+ struct mm_param *param;
+ param = mm_param_new();
+
+ dprintf2(pstate,"Param: '%s', Value: '%s'\n", (yyvsp[(1) - (3)].string), (yyvsp[(3) - (3)].string));
+
+ /* Catch an eventual boundary identifier */
+ if (!strcasecmp((yyvsp[(1) - (3)].string), "boundary")) {
+ if (pstate->lstate.boundary_string == NULL) {
+ set_boundary((yyvsp[(3) - (3)].string),pstate);
+ } else {
+ if (pstate->parsemode != MM_PARSE_LOOSE) {
+ mm_errno = MM_ERROR_MIME;
+ mm_error_setmsg("duplicate boundary "
+ "found");
+ return -1;
+ } else {
+ /* TODO: attach MM_WARNING_DUPPARAM */
+ }
+ }
+ }
+
+ param->name = xstrdup((yyvsp[(1) - (3)].string));
+ param->value = xstrdup((yyvsp[(3) - (3)].string));
+
+ mm_content_attachtypeparam(pstate->ctype, param);
+ ;}
+ break;
+
+ case 43:
+#line 430 "mimeparser.y"
+ {
+ struct mm_param *param;
+ param = mm_param_new();
+
+ param->name = xstrdup((yyvsp[(1) - (3)].string));
+ param->value = xstrdup((yyvsp[(3) - (3)].string));
+
+ mm_content_attachdispositionparam(pstate->ctype, param);
+
+ ;}
+ break;
+
+ case 44:
+#line 444 "mimeparser.y"
+ {
+ dprintf2(pstate,"contenttype_param_val: WORD=%s\n", (yyvsp[(1) - (1)].string));
+ (yyval.string) = (yyvsp[(1) - (1)].string);
+ ;}
+ break;
+
+ case 45:
+#line 450 "mimeparser.y"
+ {
+ dprintf2(pstate,"contenttype_param_val: TSPECIAL\n");
+ /* For broken MIME implementation */
+ if (pstate->parsemode != MM_PARSE_LOOSE) {
+ mm_errno = MM_ERROR_MIME;
+ mm_error_setmsg("tspecial without quotes");
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(-1);
+ } else {
+ /* TODO: attach MM_WARNING_INVAL */
+ }
+ (yyval.string) = (yyvsp[(1) - (1)].string);
+ ;}
+ break;
+
+ case 46:
+#line 465 "mimeparser.y"
+ {
+ dprintf2(pstate,"contenttype_param_val: \"TSPECIAL\"\n" );
+ (yyval.string) = (yyvsp[(2) - (3)].string);
+ ;}
+ break;
+
+ case 47:
+#line 473 "mimeparser.y"
+ {
+ dprintf2(pstate,"End of headers at line %d\n", pstate->lstate.lineno);
+ ;}
+ break;
+
+ case 48:
+#line 480 "mimeparser.y"
+ {
+ if (pstate->lstate.boundary_string == NULL) {
+ mm_errno = MM_ERROR_PARSE;
+ mm_error_setmsg("internal incosistency");
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(-1);
+ }
+ if (strcmp(pstate->lstate.boundary_string, (yyvsp[(1) - (2)].string))) {
+ mm_errno = MM_ERROR_PARSE;
+ mm_error_setmsg("invalid boundary: '%s' (%d)", (yyvsp[(1) - (2)].string), strlen((yyvsp[(1) - (2)].string)));
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(-1);
+ }
+ dprintf2(pstate,"New MIME part... (%s)\n", (yyvsp[(1) - (2)].string));
+ ;}
+ break;
+
+ case 49:
+#line 499 "mimeparser.y"
+ {
+ if (pstate->lstate.endboundary_string == NULL) {
+ mm_errno = MM_ERROR_PARSE;
+ mm_error_setmsg("internal incosistency");
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(-1);
+ }
+ if (strcmp(pstate->lstate.endboundary_string, (yyvsp[(1) - (1)].string))) {
+ mm_errno = MM_ERROR_PARSE;
+ mm_error_setmsg("invalid end boundary: %s", (yyvsp[(1) - (1)].string));
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(-1);
+ }
+ dprintf2(pstate,"End of MIME message\n");
+ ;}
+ break;
+
+ case 50:
+#line 518 "mimeparser.y"
+ {
+ char *body;
+ size_t offset;
+
+ dprintf2(pstate,"BODY (%d/%d), SIZE %d\n", (yyvsp[(1) - (1)].position).start, (yyvsp[(1) - (1)].position).end, (yyvsp[(1) - (1)].position).end - (yyvsp[(1) - (1)].position).start);
+
+ body = PARSE_readmessagepart((yyvsp[(1) - (1)].position).opaque_start, (yyvsp[(1) - (1)].position).start, (yyvsp[(1) - (1)].position).end,
+ &offset,yyscanner,pstate);
+
+ if (body == NULL) {
+ return(-1);
+ }
+ pstate->current_mimepart->opaque_body = body;
+ pstate->current_mimepart->body = body + offset;
+ pstate->current_mimepart->opaque_length = (yyvsp[(1) - (1)].position).end - (yyvsp[(1) - (1)].position).start - 2 + offset;
+ pstate->current_mimepart->length = pstate->current_mimepart->opaque_length - offset;
+ ;}
+ break;
+
+
+/* Line 1267 of yacc.c. */
+#line 1913 "mimeparser.tab.c"
+ default: break;
+ }
+ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+
+ *++yyvsp = yyval;
+
+
+ /* Now `shift' the result of the reduction. Determine what state
+ that goes to, based on the state we popped back to and the rule
+ number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+ if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTOKENS];
+
+ goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+ /* If not already recovering from an error, report this error. */
+ if (!yyerrstatus)
+ {
+ ++yynerrs;
+#if ! YYERROR_VERBOSE
+ yyerror (pstate, yyscanner, YY_("syntax error"));
+#else
+ {
+ YYSIZE_T yysize = yysyntax_error (0, yystate, yychar);
+ if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM)
+ {
+ YYSIZE_T yyalloc = 2 * yysize;
+ if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM))
+ yyalloc = YYSTACK_ALLOC_MAXIMUM;
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+ yymsg = (char *) YYSTACK_ALLOC (yyalloc);
+ if (yymsg)
+ yymsg_alloc = yyalloc;
+ else
+ {
+ yymsg = yymsgbuf;
+ yymsg_alloc = sizeof yymsgbuf;
+ }
+ }
+
+ if (0 < yysize && yysize <= yymsg_alloc)
+ {
+ (void) yysyntax_error (yymsg, yystate, yychar);
+ yyerror (pstate, yyscanner, yymsg);
+ }
+ else
+ {
+ yyerror (pstate, yyscanner, YY_("syntax error"));
+ if (yysize != 0)
+ goto yyexhaustedlab;
+ }
+ }
+#endif
+ }
+
+
+
+ if (yyerrstatus == 3)
+ {
+ /* If just tried and failed to reuse look-ahead token after an
+ error, discard it. */
+
+ if (yychar <= YYEOF)
+ {
+ /* Return failure if at end of input. */
+ if (yychar == YYEOF)
+ YYABORT;
+ }
+ else
+ {
+ yydestruct ("Error: discarding",
+ yytoken, &yylval, pstate, yyscanner);
+ yychar = YYEMPTY;
+ }
+ }
+
+ /* Else will try to reuse look-ahead token after shifting the error
+ token. */
+ goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR. |
+`---------------------------------------------------*/
+yyerrorlab:
+
+ /* Pacify compilers like GCC when the user code never invokes
+ YYERROR and the label yyerrorlab therefore never appears in user
+ code. */
+ if (/*CONSTCOND*/ 0)
+ goto yyerrorlab;
+
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYERROR. */
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+ yystate = *yyssp;
+ goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR. |
+`-------------------------------------------------------------*/
+yyerrlab1:
+ yyerrstatus = 3; /* Each real token shifted decrements this. */
+
+ for (;;)
+ {
+ yyn = yypact[yystate];
+ if (yyn != YYPACT_NINF)
+ {
+ yyn += YYTERROR;
+ if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+ {
+ yyn = yytable[yyn];
+ if (0 < yyn)
+ break;
+ }
+ }
+
+ /* Pop the current state because it cannot handle the error token. */
+ if (yyssp == yyss)
+ YYABORT;
+
+
+ yydestruct ("Error: popping",
+ yystos[yystate], yyvsp, pstate, yyscanner);
+ YYPOPSTACK (1);
+ yystate = *yyssp;
+ YY_STACK_PRINT (yyss, yyssp);
+ }
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ *++yyvsp = yylval;
+
+
+ /* Shift the error token. */
+ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here. |
+`-------------------------------------*/
+yyacceptlab:
+ yyresult = 0;
+ goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here. |
+`-----------------------------------*/
+yyabortlab:
+ yyresult = 1;
+ goto yyreturn;
+
+#ifndef yyoverflow
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here. |
+`-------------------------------------------------*/
+yyexhaustedlab:
+ yyerror (pstate, yyscanner, YY_("memory exhausted"));
+ yyresult = 2;
+ /* Fall through. */
+#endif
+
+yyreturn:
+ if (yychar != YYEOF && yychar != YYEMPTY)
+ yydestruct ("Cleanup: discarding lookahead",
+ yytoken, &yylval, pstate, yyscanner);
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYABORT or YYACCEPT. */
+ YYPOPSTACK (yylen);
+ YY_STACK_PRINT (yyss, yyssp);
+ while (yyssp != yyss)
+ {
+ yydestruct ("Cleanup: popping",
+ yystos[*yyssp], yyvsp, pstate, yyscanner);
+ YYPOPSTACK (1);
+ }
+#ifndef yyoverflow
+ if (yyss != yyssa)
+ YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+#endif
+ /* Make sure YYID is used. */
+ return YYID (yyresult);
+}
+
+
+#line 537 "mimeparser.y"
+
+
+/*
+ * This function gets the specified part from the currently parsed message.
+ */
+static char *
+PARSE_readmessagepart(size_t opaque_start, size_t real_start, size_t end,
+ size_t *offset, yyscan_t yyscanner, struct parser_state *pstate)
+{
+ size_t body_size;
+ size_t current;
+ size_t start;
+ char *body;
+
+ /* calculate start and offset markers for the opaque and
+ * header stripped body message.
+ */
+ if (opaque_start > 0) {
+ /* Multipart message */
+ if (real_start) {
+ if (real_start < opaque_start) {
+ mm_errno = MM_ERROR_PARSE;
+ mm_error_setmsg("internal incosistency (S:%d/O:%d)",
+ real_start,
+ opaque_start);
+ return(NULL);
+ }
+ start = opaque_start;
+ *offset = real_start - start;
+ /* Flat message */
+ } else {
+ start = opaque_start;
+ *offset = 0;
+ }
+ } else {
+ start = real_start;
+ *offset = 0;
+ }
+
+ /* The next three cases should NOT happen anytime */
+ if (end <= start) {
+ mm_errno = MM_ERROR_PARSE;
+ mm_error_setmsg("internal incosistency,2");
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(NULL);
+ }
+ if (start < *offset) {
+ mm_errno = MM_ERROR_PARSE;
+ mm_error_setmsg("internal incosistency, S:%d,O:%d,L:%d", start, offset, pstate->lstate.lineno);
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(NULL);
+ }
+ if (start < 0 || end < 0) {
+ mm_errno = MM_ERROR_PARSE;
+ mm_error_setmsg("internal incosistency,4");
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(NULL);
+ }
+
+ /* XXX: do we want to enforce a maximum body size? make it a
+ * parser option? */
+
+ /* Read in the body message */
+ body_size = end - start;
+
+ if (body_size < 1) {
+ mm_errno = MM_ERROR_PARSE;
+ mm_error_setmsg("size of body cannot be < 1");
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(NULL);
+ }
+
+ body = (char *)malloc(body_size + 1);
+ if (body == NULL) {
+ mm_errno = MM_ERROR_ERRNO;
+ return(NULL);
+ }
+
+ /* Get the message body either from a stream or a memory
+ * buffer.
+ */
+ if (mimeparser_yyget_in(yyscanner) != NULL) {
+ FILE *x = mimeparser_yyget_in(yyscanner);
+ current = ftell(x);
+ fseek(x, start - 1, SEEK_SET);
+ fread(body, body_size - 1, 1, x);
+ fseek(x, current, SEEK_SET);
+ } else if (pstate->lstate.message_buffer != NULL) {
+ strlcpy(body, pstate->lstate.message_buffer + start - 1, body_size);
+ }
+
+ return(body);
+
+}
+
+int
+yyerror(struct parser_state *pstate, void *yyscanner, const char *str)
+{
+ mm_errno = MM_ERROR_PARSE;
+ mm_error_setmsg("%s", str);
+ mm_error_setlineno(pstate->lstate.lineno);
+ return -1;
+}
+
+int
+mimeparser_yywrap(void)
+{
+ return 1;
+}
+
+/**
+ * Sets the boundary value for the current message
+ */
+int
+set_boundary(char *str, struct parser_state *pstate)
+{
+ size_t blen;
+
+ blen = strlen(str);
+
+ pstate->lstate.boundary_string = (char *)malloc(blen + 3);
+ pstate->lstate.endboundary_string = (char *)malloc(blen + 5);
+
+ if (pstate->lstate.boundary_string == NULL || pstate->lstate.endboundary_string == NULL) {
+ if (pstate->lstate.boundary_string != NULL) {
+ free(pstate->lstate.boundary_string);
+ }
+ if (pstate->lstate.endboundary_string != NULL) {
+ free(pstate->lstate.endboundary_string);
+ }
+ return -1;
+ }
+
+ pstate->ctx->boundary = xstrdup(str);
+
+ snprintf(pstate->lstate.boundary_string, blen + 3, "--%s", str);
+ snprintf(pstate->lstate.endboundary_string, blen + 5, "--%s--", str);
+
+ return 0;
+}
+
+/**
+ * Debug printf()
+ */
+int
+dprintf2(struct parser_state *pstate, const char *fmt, ...)
+{
+ va_list ap;
+ char *msg;
+ if (pstate->debug == 0) return 1;
+
+ va_start(ap, fmt);
+ vasprintf(&msg, fmt, ap);
+ va_end(ap);
+
+ fprintf(stderr, "%s", msg);
+ free(msg);
+
+ return 0;
+
+}
+
+void reset_environ(struct parser_state *pstate)
+{
+ pstate->lstate.lineno = 0;
+ pstate->lstate.boundary_string = NULL;
+ pstate->lstate.endboundary_string = NULL;
+ pstate->lstate.message_buffer = NULL;
+ pstate->mime_parts = 0;
+ pstate->debug = 0;
+ pstate->envelope = NULL;
+ pstate->temppart = NULL;
+ pstate->ctype = NULL;
+ pstate->current_mimepart = NULL;
+
+ pstate->have_contenttype = 0;
+}
+/**
+ * Initializes the parser engine.
+ */
+int
+PARSER_initialize(struct parser_state *pstate, void *yyscanner)
+{
+ void reset_lexer_state(void *yyscanner, struct parser_state *);
+#if 0
+ if (pstate->ctx != NULL) {
+ xfree(pstate->ctx);
+ pstate->ctx = NULL;
+ }
+ if (pstate->envelope != NULL) {
+ xfree(pstate->envelope);
+ pstate->envelope = NULL;
+ }
+ if (pstate->ctype != NULL) {
+ xfree(pstate->ctype);
+ pstate->ctype = NULL;
+ }
+#endif
+ /* yydebug = 1; */
+ reset_environ(pstate);
+ reset_lexer_state(yyscanner,pstate);
+
+ pstate->envelope = mm_mimepart_new();
+ pstate->current_mimepart = pstate->envelope;
+ pstate->ctype = mm_content_new();
+
+ pstate->have_contenttype = 0;
+
+ return 1;
+}
+
+
+
diff --git a/trunk/main/minimime/mimeparser.tab.h b/trunk/main/minimime/mimeparser.tab.h
new file mode 100644
index 000000000..c19731fd2
--- /dev/null
+++ b/trunk/main/minimime/mimeparser.tab.h
@@ -0,0 +1,112 @@
+/* A Bison parser, made by GNU Bison 2.3. */
+
+/* Skeleton interface for Bison's Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+ Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ ANY = 258,
+ COLON = 259,
+ DASH = 260,
+ DQUOTE = 261,
+ ENDOFHEADERS = 262,
+ EOL = 263,
+ EOM = 264,
+ EQUAL = 265,
+ MIMEVERSION_HEADER = 266,
+ SEMICOLON = 267,
+ CONTENTDISPOSITION_HEADER = 268,
+ CONTENTENCODING_HEADER = 269,
+ CONTENTTYPE_HEADER = 270,
+ MAIL_HEADER = 271,
+ HEADERVALUE = 272,
+ BOUNDARY = 273,
+ ENDBOUNDARY = 274,
+ CONTENTTYPE_VALUE = 275,
+ TSPECIAL = 276,
+ WORD = 277,
+ BODY = 278,
+ PREAMBLE = 279,
+ POSTAMBLE = 280
+ };
+#endif
+/* Tokens. */
+#define ANY 258
+#define COLON 259
+#define DASH 260
+#define DQUOTE 261
+#define ENDOFHEADERS 262
+#define EOL 263
+#define EOM 264
+#define EQUAL 265
+#define MIMEVERSION_HEADER 266
+#define SEMICOLON 267
+#define CONTENTDISPOSITION_HEADER 268
+#define CONTENTENCODING_HEADER 269
+#define CONTENTTYPE_HEADER 270
+#define MAIL_HEADER 271
+#define HEADERVALUE 272
+#define BOUNDARY 273
+#define ENDBOUNDARY 274
+#define CONTENTTYPE_VALUE 275
+#define TSPECIAL 276
+#define WORD 277
+#define BODY 278
+#define PREAMBLE 279
+#define POSTAMBLE 280
+
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+#line 67 "mimeparser.y"
+{
+ int number;
+ char *string;
+ struct s_position position;
+}
+/* Line 1489 of yacc.c. */
+#line 105 "mimeparser.tab.h"
+ YYSTYPE;
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+
+
diff --git a/trunk/main/minimime/mimeparser.y b/trunk/main/minimime/mimeparser.y
new file mode 100644
index 000000000..7c32a290d
--- /dev/null
+++ b/trunk/main/minimime/mimeparser.y
@@ -0,0 +1,748 @@
+%{
+/*
+ * Copyright (c) 2004 Jann Fischer. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/**
+ * These are the grammatic definitions in yacc syntax to parse MIME conform
+ * messages.
+ *
+ * TODO:
+ * - honour parse flags passed to us (partly done)
+ * - parse Content-Disposition header (partly done)
+ * - parse Content-Encoding header
+ */
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+
+#include "mimeparser.h"
+#include "mm.h"
+#include "mm_internal.h"
+
+int set_boundary(char *,struct parser_state *);
+int mimeparser_yywrap(void);
+void reset_environ(struct parser_state *pstate);
+int PARSER_initialize(struct parser_state *pstate, void *yyscanner);
+
+static char *PARSE_readmessagepart(size_t, size_t, size_t, size_t *,yyscan_t, struct parser_state *);
+FILE *mimeparser_yyget_in (yyscan_t yyscanner );
+
+%}
+
+%pure-parser
+%parse-param {struct parser_state *pstate}
+%parse-param {void *yyscanner}
+%lex-param {void *yyscanner}
+
+%union
+{
+ int number;
+ char *string;
+ struct s_position position;
+}
+
+%token ANY
+%token COLON
+%token DASH
+%token DQUOTE
+%token ENDOFHEADERS
+%token EOL
+%token EOM
+%token EQUAL
+%token MIMEVERSION_HEADER
+%token SEMICOLON
+
+%token <string> CONTENTDISPOSITION_HEADER
+%token <string> CONTENTENCODING_HEADER
+%token <string> CONTENTTYPE_HEADER
+%token <string> MAIL_HEADER
+%token <string> HEADERVALUE
+%token <string> BOUNDARY
+%token <string> ENDBOUNDARY
+%token <string> CONTENTTYPE_VALUE
+%token <string> TSPECIAL
+%token <string> WORD
+
+%token <position> BODY
+%token <position> PREAMBLE
+%token <position> POSTAMBLE
+
+%type <string> content_disposition
+%type <string> contenttype_parameter_value
+%type <string> mimetype
+%type <string> body
+
+%start message
+
+%%
+
+/* This is a parser for a MIME-conform message, which is in either single
+ * part or multi part format.
+ */
+message :
+ multipart_message
+ |
+ singlepart_message
+ ;
+
+multipart_message:
+ headers preamble
+ {
+ mm_context_attachpart(pstate->ctx, pstate->current_mimepart);
+ pstate->current_mimepart = mm_mimepart_new();
+ pstate->have_contenttype = 0;
+ }
+ mimeparts endboundary postamble
+ {
+ dprintf2(pstate,"This was a multipart message\n");
+ }
+ ;
+
+singlepart_message:
+ headers body
+ {
+ dprintf2(pstate,"This was a single part message\n");
+ mm_context_attachpart(pstate->ctx, pstate->current_mimepart);
+ }
+ ;
+
+headers :
+ header headers
+ |
+ end_headers
+ {
+ /* If we did not find a Content-Type header for the current
+ * MIME part (or envelope), we create one and attach it.
+ * According to the RFC, a type of "text/plain" and a
+ * charset of "us-ascii" can be assumed.
+ */
+ struct mm_content *ct;
+ struct mm_param *param;
+
+ if (!pstate->have_contenttype) {
+ ct = mm_content_new();
+ mm_content_settype(ct, "text/plain");
+
+ param = mm_param_new();
+ param->name = xstrdup("charset");
+ param->value = xstrdup("us-ascii");
+
+ mm_content_attachtypeparam(ct, param);
+ mm_mimepart_attachcontenttype(pstate->current_mimepart, ct);
+ }
+ pstate->have_contenttype = 0;
+ }
+ |
+ header
+ ;
+
+preamble:
+ PREAMBLE
+ {
+ char *preamble;
+ size_t offset;
+
+ if ($1.start != $1.end) {
+ preamble = PARSE_readmessagepart(0, $1.start, $1.end,
+ &offset,yyscanner,pstate);
+ if (preamble == NULL) {
+ return(-1);
+ }
+ pstate->ctx->preamble = preamble;
+ dprintf2(pstate,"PREAMBLE:\n%s\n", preamble);
+ }
+ }
+ |
+ ;
+
+postamble:
+ POSTAMBLE
+ {
+ }
+ |
+ ;
+
+mimeparts:
+ mimeparts mimepart
+ |
+ mimepart
+ ;
+
+mimepart:
+ boundary headers body
+ {
+
+ if (mm_context_attachpart(pstate->ctx, pstate->current_mimepart) == -1) {
+ mm_errno = MM_ERROR_ERRNO;
+ return(-1);
+ }
+
+ pstate->temppart = mm_mimepart_new();
+ pstate->current_mimepart = pstate->temppart;
+ pstate->mime_parts++;
+ }
+ ;
+
+header :
+ mail_header
+ |
+ contenttype_header
+ {
+ pstate->have_contenttype = 1;
+ if (mm_content_iscomposite(pstate->envelope->type)) {
+ pstate->ctx->messagetype = MM_MSGTYPE_MULTIPART;
+ } else {
+ pstate->ctx->messagetype = MM_MSGTYPE_FLAT;
+ }
+ }
+ |
+ contentdisposition_header
+ |
+ contentencoding_header
+ |
+ mimeversion_header
+ |
+ invalid_header
+ {
+ if (pstate->parsemode != MM_PARSE_LOOSE) {
+ mm_errno = MM_ERROR_PARSE;
+ mm_error_setmsg("invalid header encountered");
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(-1);
+ } else {
+ /* TODO: attach MM_WARNING_INVHDR */
+ }
+ }
+ ;
+
+mail_header:
+ MAIL_HEADER COLON WORD EOL
+ {
+ struct mm_mimeheader *hdr;
+ hdr = mm_mimeheader_generate($1, $3);
+ mm_mimepart_attachheader(pstate->current_mimepart, hdr);
+ }
+ |
+ MAIL_HEADER COLON EOL
+ {
+ struct mm_mimeheader *hdr;
+
+ if (pstate->parsemode != MM_PARSE_LOOSE) {
+ mm_errno = MM_ERROR_MIME;
+ mm_error_setmsg("invalid header encountered");
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(-1);
+ } else {
+ /* TODO: attach MM_WARNING_INVHDR */
+ }
+
+ hdr = mm_mimeheader_generate($1, xstrdup(""));
+ mm_mimepart_attachheader(pstate->current_mimepart, hdr);
+ }
+ ;
+
+contenttype_header:
+ CONTENTTYPE_HEADER COLON mimetype EOL
+ {
+ mm_content_settype(pstate->ctype, "%s", $3);
+ mm_mimepart_attachcontenttype(pstate->current_mimepart, pstate->ctype);
+ dprintf2(pstate,"Content-Type -> %s\n", $3);
+ pstate->ctype = mm_content_new();
+ }
+ |
+ CONTENTTYPE_HEADER COLON mimetype contenttype_parameters EOL
+ {
+ mm_content_settype(pstate->ctype, "%s", $3);
+ mm_mimepart_attachcontenttype(pstate->current_mimepart, pstate->ctype);
+ dprintf2(pstate,"Content-Type (P) -> %s\n", $3);
+ pstate->ctype = mm_content_new();
+ }
+ ;
+
+contentdisposition_header:
+ CONTENTDISPOSITION_HEADER COLON content_disposition EOL
+ {
+ dprintf2(pstate,"Content-Disposition -> %s\n", $3);
+ pstate->ctype->disposition_type = xstrdup($3);
+ }
+ |
+ CONTENTDISPOSITION_HEADER COLON content_disposition content_disposition_parameters EOL
+ {
+ dprintf2(pstate,"Content-Disposition (P) -> %s; params\n", $3);
+ pstate->ctype->disposition_type = xstrdup($3);
+ }
+ ;
+
+content_disposition:
+ WORD
+ {
+ /*
+ * According to RFC 2183, the content disposition value may
+ * only be "inline", "attachment" or an extension token. We
+ * catch invalid values here if we are not in loose parsing
+ * mode.
+ */
+ if (strcasecmp($1, "inline") && strcasecmp($1, "attachment")
+ && strncasecmp($1, "X-", 2)) {
+ if (pstate->parsemode != MM_PARSE_LOOSE) {
+ mm_errno = MM_ERROR_MIME;
+ mm_error_setmsg("invalid content-disposition");
+ return(-1);
+ }
+ } else {
+ /* TODO: attach MM_WARNING_INVHDR */
+ }
+ $$ = $1;
+ }
+ ;
+
+contentencoding_header:
+ CONTENTENCODING_HEADER COLON WORD EOL
+ {
+ dprintf2(pstate,"Content-Transfer-Encoding -> %s\n", $3);
+ }
+ ;
+
+mimeversion_header:
+ MIMEVERSION_HEADER COLON WORD EOL
+ {
+ dprintf2(pstate,"MIME-Version -> '%s'\n", $3);
+ }
+ ;
+
+invalid_header:
+ any EOL
+ ;
+
+any:
+ any ANY
+ |
+ ANY
+ ;
+
+mimetype:
+ WORD '/' WORD
+ {
+ char type[255];
+ snprintf(type, sizeof(type), "%s/%s", $1, $3);
+ $$ = type;
+ }
+ ;
+
+contenttype_parameters:
+ SEMICOLON contenttype_parameter contenttype_parameters
+ |
+ SEMICOLON contenttype_parameter
+ |
+ SEMICOLON
+ {
+ if (pstate->parsemode != MM_PARSE_LOOSE) {
+ mm_errno = MM_ERROR_MIME;
+ mm_error_setmsg("invalid Content-Type header");
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(-1);
+ } else {
+ /* TODO: attach MM_WARNING_INVHDR */
+ }
+ }
+ ;
+
+content_disposition_parameters:
+ SEMICOLON content_disposition_parameter content_disposition_parameters
+ |
+ SEMICOLON content_disposition_parameter
+ |
+ SEMICOLON
+ {
+ if (pstate->parsemode != MM_PARSE_LOOSE) {
+ mm_errno = MM_ERROR_MIME;
+ mm_error_setmsg("invalid Content-Disposition header");
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(-1);
+ } else {
+ /* TODO: attach MM_WARNING_INVHDR */
+ }
+ }
+ ;
+
+contenttype_parameter:
+ WORD EQUAL contenttype_parameter_value
+ {
+ struct mm_param *param;
+ param = mm_param_new();
+
+ dprintf2(pstate,"Param: '%s', Value: '%s'\n", $1, $3);
+
+ /* Catch an eventual boundary identifier */
+ if (!strcasecmp($1, "boundary")) {
+ if (pstate->lstate.boundary_string == NULL) {
+ set_boundary($3,pstate);
+ } else {
+ if (pstate->parsemode != MM_PARSE_LOOSE) {
+ mm_errno = MM_ERROR_MIME;
+ mm_error_setmsg("duplicate boundary "
+ "found");
+ return -1;
+ } else {
+ /* TODO: attach MM_WARNING_DUPPARAM */
+ }
+ }
+ }
+
+ param->name = xstrdup($1);
+ param->value = xstrdup($3);
+
+ mm_content_attachtypeparam(pstate->ctype, param);
+ }
+ ;
+
+content_disposition_parameter:
+ WORD EQUAL contenttype_parameter_value
+ {
+ struct mm_param *param;
+ param = mm_param_new();
+
+ param->name = xstrdup($1);
+ param->value = xstrdup($3);
+
+ mm_content_attachdispositionparam(pstate->ctype, param);
+
+ }
+ ;
+
+contenttype_parameter_value:
+ WORD
+ {
+ dprintf2(pstate,"contenttype_param_val: WORD=%s\n", $1);
+ $$ = $1;
+ }
+ |
+ TSPECIAL
+ {
+ dprintf2(pstate,"contenttype_param_val: TSPECIAL\n");
+ /* For broken MIME implementation */
+ if (pstate->parsemode != MM_PARSE_LOOSE) {
+ mm_errno = MM_ERROR_MIME;
+ mm_error_setmsg("tspecial without quotes");
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(-1);
+ } else {
+ /* TODO: attach MM_WARNING_INVAL */
+ }
+ $$ = $1;
+ }
+ |
+ '"' TSPECIAL '"'
+ {
+ dprintf2(pstate,"contenttype_param_val: \"TSPECIAL\"\n" );
+ $$ = $2;
+ }
+ ;
+
+end_headers :
+ ENDOFHEADERS
+ {
+ dprintf2(pstate,"End of headers at line %d\n", pstate->lstate.lineno);
+ }
+ ;
+
+boundary :
+ BOUNDARY EOL
+ {
+ if (pstate->lstate.boundary_string == NULL) {
+ mm_errno = MM_ERROR_PARSE;
+ mm_error_setmsg("internal incosistency");
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(-1);
+ }
+ if (strcmp(pstate->lstate.boundary_string, $1)) {
+ mm_errno = MM_ERROR_PARSE;
+ mm_error_setmsg("invalid boundary: '%s' (%d)", $1, strlen($1));
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(-1);
+ }
+ dprintf2(pstate,"New MIME part... (%s)\n", $1);
+ }
+ ;
+
+endboundary :
+ ENDBOUNDARY
+ {
+ if (pstate->lstate.endboundary_string == NULL) {
+ mm_errno = MM_ERROR_PARSE;
+ mm_error_setmsg("internal incosistency");
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(-1);
+ }
+ if (strcmp(pstate->lstate.endboundary_string, $1)) {
+ mm_errno = MM_ERROR_PARSE;
+ mm_error_setmsg("invalid end boundary: %s", $1);
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(-1);
+ }
+ dprintf2(pstate,"End of MIME message\n");
+ }
+ ;
+
+body:
+ BODY
+ {
+ char *body;
+ size_t offset;
+
+ dprintf2(pstate,"BODY (%d/%d), SIZE %d\n", $1.start, $1.end, $1.end - $1.start);
+
+ body = PARSE_readmessagepart($1.opaque_start, $1.start, $1.end,
+ &offset,yyscanner,pstate);
+
+ if (body == NULL) {
+ return(-1);
+ }
+ pstate->current_mimepart->opaque_body = body;
+ pstate->current_mimepart->body = body + offset;
+ pstate->current_mimepart->opaque_length = $1.end - $1.start - 2 + offset;
+ pstate->current_mimepart->length = pstate->current_mimepart->opaque_length - offset;
+ }
+ ;
+
+%%
+
+/*
+ * This function gets the specified part from the currently parsed message.
+ */
+static char *
+PARSE_readmessagepart(size_t opaque_start, size_t real_start, size_t end,
+ size_t *offset, yyscan_t yyscanner, struct parser_state *pstate)
+{
+ size_t body_size;
+ size_t current;
+ size_t start;
+ char *body;
+
+ /* calculate start and offset markers for the opaque and
+ * header stripped body message.
+ */
+ if (opaque_start > 0) {
+ /* Multipart message */
+ if (real_start) {
+ if (real_start < opaque_start) {
+ mm_errno = MM_ERROR_PARSE;
+ mm_error_setmsg("internal incosistency (S:%d/O:%d)",
+ real_start,
+ opaque_start);
+ return(NULL);
+ }
+ start = opaque_start;
+ *offset = real_start - start;
+ /* Flat message */
+ } else {
+ start = opaque_start;
+ *offset = 0;
+ }
+ } else {
+ start = real_start;
+ *offset = 0;
+ }
+
+ /* The next three cases should NOT happen anytime */
+ if (end <= start) {
+ mm_errno = MM_ERROR_PARSE;
+ mm_error_setmsg("internal incosistency,2");
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(NULL);
+ }
+ if (start < *offset) {
+ mm_errno = MM_ERROR_PARSE;
+ mm_error_setmsg("internal incosistency, S:%d,O:%d,L:%d", start, offset, pstate->lstate.lineno);
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(NULL);
+ }
+ if (start < 0 || end < 0) {
+ mm_errno = MM_ERROR_PARSE;
+ mm_error_setmsg("internal incosistency,4");
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(NULL);
+ }
+
+ /* XXX: do we want to enforce a maximum body size? make it a
+ * parser option? */
+
+ /* Read in the body message */
+ body_size = end - start;
+
+ if (body_size < 1) {
+ mm_errno = MM_ERROR_PARSE;
+ mm_error_setmsg("size of body cannot be < 1");
+ mm_error_setlineno(pstate->lstate.lineno);
+ return(NULL);
+ }
+
+ body = (char *)malloc(body_size + 1);
+ if (body == NULL) {
+ mm_errno = MM_ERROR_ERRNO;
+ return(NULL);
+ }
+
+ /* Get the message body either from a stream or a memory
+ * buffer.
+ */
+ if (mimeparser_yyget_in(yyscanner) != NULL) {
+ FILE *x = mimeparser_yyget_in(yyscanner);
+ current = ftell(x);
+ fseek(x, start - 1, SEEK_SET);
+ fread(body, body_size - 1, 1, x);
+ fseek(x, current, SEEK_SET);
+ } else if (pstate->lstate.message_buffer != NULL) {
+ strlcpy(body, pstate->lstate.message_buffer + start - 1, body_size);
+ }
+
+ return(body);
+
+}
+
+int
+yyerror(struct parser_state *pstate, void *yyscanner, const char *str)
+{
+ mm_errno = MM_ERROR_PARSE;
+ mm_error_setmsg("%s", str);
+ mm_error_setlineno(pstate->lstate.lineno);
+ return -1;
+}
+
+int
+mimeparser_yywrap(void)
+{
+ return 1;
+}
+
+/**
+ * Sets the boundary value for the current message
+ */
+int
+set_boundary(char *str, struct parser_state *pstate)
+{
+ size_t blen;
+
+ blen = strlen(str);
+
+ pstate->lstate.boundary_string = (char *)malloc(blen + 3);
+ pstate->lstate.endboundary_string = (char *)malloc(blen + 5);
+
+ if (pstate->lstate.boundary_string == NULL || pstate->lstate.endboundary_string == NULL) {
+ if (pstate->lstate.boundary_string != NULL) {
+ free(pstate->lstate.boundary_string);
+ }
+ if (pstate->lstate.endboundary_string != NULL) {
+ free(pstate->lstate.endboundary_string);
+ }
+ return -1;
+ }
+
+ pstate->ctx->boundary = xstrdup(str);
+
+ snprintf(pstate->lstate.boundary_string, blen + 3, "--%s", str);
+ snprintf(pstate->lstate.endboundary_string, blen + 5, "--%s--", str);
+
+ return 0;
+}
+
+/**
+ * Debug printf()
+ */
+int
+dprintf2(struct parser_state *pstate, const char *fmt, ...)
+{
+ va_list ap;
+ char *msg;
+ if (pstate->debug == 0) return 1;
+
+ va_start(ap, fmt);
+ vasprintf(&msg, fmt, ap);
+ va_end(ap);
+
+ fprintf(stderr, "%s", msg);
+ free(msg);
+
+ return 0;
+
+}
+
+void reset_environ(struct parser_state *pstate)
+{
+ pstate->lstate.lineno = 0;
+ pstate->lstate.boundary_string = NULL;
+ pstate->lstate.endboundary_string = NULL;
+ pstate->lstate.message_buffer = NULL;
+ pstate->mime_parts = 0;
+ pstate->debug = 0;
+ pstate->envelope = NULL;
+ pstate->temppart = NULL;
+ pstate->ctype = NULL;
+ pstate->current_mimepart = NULL;
+
+ pstate->have_contenttype = 0;
+}
+/**
+ * Initializes the parser engine.
+ */
+int
+PARSER_initialize(struct parser_state *pstate, void *yyscanner)
+{
+ void reset_lexer_state(void *yyscanner, struct parser_state *);
+#if 0
+ if (pstate->ctx != NULL) {
+ xfree(pstate->ctx);
+ pstate->ctx = NULL;
+ }
+ if (pstate->envelope != NULL) {
+ xfree(pstate->envelope);
+ pstate->envelope = NULL;
+ }
+ if (pstate->ctype != NULL) {
+ xfree(pstate->ctype);
+ pstate->ctype = NULL;
+ }
+#endif
+ /* yydebug = 1; */
+ reset_environ(pstate);
+ reset_lexer_state(yyscanner,pstate);
+
+ pstate->envelope = mm_mimepart_new();
+ pstate->current_mimepart = pstate->envelope;
+ pstate->ctype = mm_content_new();
+
+ pstate->have_contenttype = 0;
+
+ return 1;
+}
+
+
diff --git a/trunk/main/minimime/mimeparser.yy.c b/trunk/main/minimime/mimeparser.yy.c
new file mode 100644
index 000000000..4cf454c7c
--- /dev/null
+++ b/trunk/main/minimime/mimeparser.yy.c
@@ -0,0 +1,2627 @@
+#line 2 "mimeparser.yy.c"
+
+#line 4 "mimeparser.yy.c"
+
+#define YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 33
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types.
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t;
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+#endif /* ! C99 */
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX (4294967295U)
+#endif
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else /* ! __cplusplus */
+
+#if __STDC__
+
+#define YY_USE_CONST
+
+#endif /* __STDC__ */
+#endif /* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index. If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* An opaque pointer. */
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void* yyscan_t;
+#endif
+
+/* For convenience, these vars (plus the bison vars far below)
+ are macros in the reentrant scanner. */
+#define yyin yyg->yyin_r
+#define yyout yyg->yyout_r
+#define yyextra yyg->yyextra_r
+#define yyleng yyg->yyleng_r
+#define yytext yyg->yytext_r
+#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno)
+#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column)
+#define yy_flex_debug yyg->yy_flex_debug_r
+
+int mimeparser_yylex_init (yyscan_t* scanner);
+
+/* Enter a start condition. This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN yyg->yy_start = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state. The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START ((yyg->yy_start - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE mimeparser_yyrestart(yyin ,yyscanner )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#define YY_BUF_SIZE 16384
+#endif
+
+/* The state buf must be large enough to hold one state per character in the main buffer.
+ */
+#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+ /* Note: We specifically omit the test for yy_rule_can_match_eol because it requires
+ * access to the local variable yy_act. Since yyless() is a macro, it would break
+ * existing scanners that call yyless() from OUTSIDE mimeparser_yylex.
+ * One obvious solution it to make yy_act a global. I tried that, and saw
+ * a 5% performance hit in a non-yylineno scanner, because yy_act is
+ * normally declared as a register variable-- so it is not worth it.
+ */
+ #define YY_LESS_LINENO(n) \
+ do { \
+ int yyl;\
+ for ( yyl = n; yyl < yyleng; ++yyl )\
+ if ( yytext[yyl] == '\n' )\
+ --yylineno;\
+ }while(0)
+
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ *yy_cp = yyg->yy_hold_char; \
+ YY_RESTORE_YY_MORE_OFFSET \
+ yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+ YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+ } \
+ while ( 0 )
+
+#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner )
+
+/* The following is because we cannot portably get our hands on size_t
+ * (without autoconf's help, which isn't available because we want
+ * flex-generated scanners to compile on their own).
+ */
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef unsigned int yy_size_t;
+#endif
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+ {
+ FILE *yy_input_file;
+
+ char *yy_ch_buf; /* input buffer */
+ char *yy_buf_pos; /* current position in input buffer */
+
+ /* Size of input buffer in bytes, not including room for EOB
+ * characters.
+ */
+ yy_size_t yy_buf_size;
+
+ /* Number of characters read into yy_ch_buf, not including EOB
+ * characters.
+ */
+ int yy_n_chars;
+
+ /* Whether we "own" the buffer - i.e., we know we created it,
+ * and can realloc() it to grow it, and should free() it to
+ * delete it.
+ */
+ int yy_is_our_buffer;
+
+ /* Whether this is an "interactive" input source; if so, and
+ * if we're using stdio for input, then we want to use getc()
+ * instead of fread(), to make sure we stop fetching input after
+ * each newline.
+ */
+ int yy_is_interactive;
+
+ /* Whether we're considered to be at the beginning of a line.
+ * If so, '^' rules will be active on the next match, otherwise
+ * not.
+ */
+ int yy_at_bol;
+
+ int yy_bs_lineno; /**< The line count. */
+ int yy_bs_column; /**< The column count. */
+
+ /* Whether to try to fill the input buffer when we reach the
+ * end of it.
+ */
+ int yy_fill_buffer;
+
+ int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+ /* When an EOF's been seen but there's still some text to process
+ * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+ * shouldn't try reading from the input source any more. We might
+ * still have a bunch of tokens to match, though, because of
+ * possible backing-up.
+ *
+ * When we actually see the EOF, we change the status to "new"
+ * (via mimeparser_yyrestart()), so that the user can continue scanning by
+ * just pointing yyin at a new input file.
+ */
+#define YY_BUFFER_EOF_PENDING 2
+
+ };
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \
+ ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \
+ : NULL)
+
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top]
+
+void mimeparser_yyrestart (FILE *input_file ,yyscan_t yyscanner );
+void mimeparser_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
+YY_BUFFER_STATE mimeparser_yy_create_buffer (FILE *file,int size ,yyscan_t yyscanner );
+void mimeparser_yy_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
+void mimeparser_yy_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
+void mimeparser_yypush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
+void mimeparser_yypop_buffer_state (yyscan_t yyscanner );
+
+static void mimeparser_yyensure_buffer_stack (yyscan_t yyscanner );
+static void mimeparser_yy_load_buffer_state (yyscan_t yyscanner );
+static void mimeparser_yy_init_buffer (YY_BUFFER_STATE b,FILE *file ,yyscan_t yyscanner );
+
+#define YY_FLUSH_BUFFER mimeparser_yy_flush_buffer(YY_CURRENT_BUFFER ,yyscanner)
+
+YY_BUFFER_STATE mimeparser_yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner );
+YY_BUFFER_STATE mimeparser_yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner );
+YY_BUFFER_STATE mimeparser_yy_scan_bytes (yyconst char *bytes,int len ,yyscan_t yyscanner );
+
+void *mimeparser_yyalloc (yy_size_t ,yyscan_t yyscanner );
+void *mimeparser_yyrealloc (void *,yy_size_t ,yyscan_t yyscanner );
+void mimeparser_yyfree (void * ,yyscan_t yyscanner );
+
+#define yy_new_buffer mimeparser_yy_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){ \
+ mimeparser_yyensure_buffer_stack (yyscanner); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ mimeparser_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+ }
+
+#define yy_set_bol(at_bol) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){\
+ mimeparser_yyensure_buffer_stack (yyscanner); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ mimeparser_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+ }
+
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* Begin user sect3 */
+
+typedef unsigned char YY_CHAR;
+
+typedef int yy_state_type;
+
+#define yytext_ptr yytext_r
+
+static yy_state_type yy_get_previous_state (yyscan_t yyscanner );
+static yy_state_type yy_try_NUL_trans (yy_state_type current_state ,yyscan_t yyscanner);
+static int yy_get_next_buffer (yyscan_t yyscanner );
+static void yy_fatal_error (yyconst char msg[] ,yyscan_t yyscanner );
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+ yyg->yytext_ptr = yy_bp; \
+ yyleng = (size_t) (yy_cp - yy_bp); \
+ yyg->yy_hold_char = *yy_cp; \
+ *yy_cp = '\0'; \
+ yyg->yy_c_buf_p = yy_cp;
+
+#define YY_NUM_RULES 30
+#define YY_END_OF_BUFFER 31
+/* This struct is not used in this scanner,
+ but its presence is necessary. */
+struct yy_trans_info
+ {
+ flex_int32_t yy_verify;
+ flex_int32_t yy_nxt;
+ };
+static yyconst flex_int16_t yy_acclist[179] =
+ { 0,
+ 31, 2, 29, 30, 28, 30, 2, 29, 30, 1,
+ 2, 29, 30, 3, 28, 30, 2, 29, 30, 29,
+ 30, 5, 28, 30, 29, 30, 4, 29, 30, 7,
+ 29, 30, 7, 13, 29, 30, 8, 28, 30, 7,
+ 29, 30, 7, 11, 29, 30, 7, 12, 29, 30,
+ 7, 12, 29, 30, 7, 9, 29, 30, 7, 10,
+ 29, 30, 8, 28, 30, 29, 30, 14, 29, 30,
+ 15, 29, 30, 29, 30, 20, 29, 30, 18, 28,
+ 30, 19, 29, 30, 20, 29, 30, 27, 29, 30,
+ 21, 28, 30, 27, 29, 30, 26, 29, 30, 26,
+
+ 29, 30, 26, 29, 30, 22, 29, 30, 24, 28,
+ 30, 29, 30, 23, 29, 30, 25, 28, 30, 29,
+ 30, 28, 1, 1, 3, 28, 5, 28, 7, 7,
+ 7, 13, 6, 8, 28, 7, 12, 7, 12, 7,
+ 8, 28, 14, 20, 18, 28, 20, 21, 28, 22,
+ 24, 28, 23, 25, 28, 6, 9, 17, 20, 17,
+ 7, 7, 6, 7, 7, 9, 17, 20, 7, 7,
+ 6, 7, 7, 9, 16, 17, 20, 7
+ } ;
+
+static yyconst flex_int16_t yy_accept[112] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 2, 5, 7, 10, 14,
+ 17, 20, 22, 25, 27, 30, 33, 37, 40, 43,
+ 47, 51, 55, 59, 63, 66, 68, 71, 74, 76,
+ 79, 82, 85, 88, 91, 94, 97, 100, 103, 106,
+ 109, 112, 114, 117, 120, 122, 123, 124, 125, 127,
+ 129, 130, 130, 131, 133, 134, 136, 138, 140, 140,
+ 141, 143, 144, 145, 147, 148, 150, 150, 151, 153,
+ 154, 156, 156, 156, 157, 158, 158, 160, 161, 162,
+
+ 163, 165, 167, 169, 170, 171, 173, 175, 178, 179,
+ 179
+ } ;
+
+static yyconst flex_int32_t yy_ec[256] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 3,
+ 1, 1, 4, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 5, 1, 6, 1, 1, 1, 1, 7, 7,
+ 7, 1, 7, 7, 8, 9, 10, 11, 11, 11,
+ 11, 11, 11, 11, 11, 11, 11, 12, 13, 7,
+ 14, 7, 7, 7, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+ 16, 1, 16, 1, 11, 1, 15, 15, 15, 15,
+
+ 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 1, 17, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1
+ } ;
+
+static yyconst flex_int32_t yy_meta[18] =
+ { 0,
+ 1, 1, 2, 3, 4, 1, 4, 5, 4, 4,
+ 5, 4, 4, 4, 5, 1, 1
+ } ;
+
+static yyconst flex_int16_t yy_base[128] =
+ { 0,
+ 0, 2, 4, 15, 28, 38, 50, 0, 67, 0,
+ 6, 8, 10, 82, 12, 17, 19, 88, 21, 23,
+ 25, 30, 32, 34, 228, 253, 253, 224, 208, 253,
+ 219, 253, 253, 165, 253, 40, 95, 43, 43, 84,
+ 110, 119, 90, 98, 253, 155, 0, 253, 153, 0,
+ 253, 148, 131, 253, 253, 132, 253, 128, 116, 0,
+ 253, 119, 0, 253, 108, 253, 0, 80, 253, 253,
+ 100, 103, 103, 0, 118, 127, 0, 0, 131, 106,
+ 253, 0, 0, 253, 136, 253, 0, 0, 253, 0,
+ 253, 138, 139, 143, 144, 145, 153, 0, 0, 155,
+
+ 160, 161, 81, 112, 169, 162, 173, 41, 173, 253,
+ 178, 183, 188, 193, 198, 203, 208, 34, 213, 215,
+ 220, 225, 230, 235, 237, 242, 247
+ } ;
+
+static yyconst flex_int16_t yy_def[128] =
+ { 0,
+ 111, 111, 111, 111, 112, 112, 110, 7, 110, 9,
+ 112, 112, 113, 113, 114, 114, 115, 115, 116, 116,
+ 117, 117, 112, 112, 110, 110, 110, 110, 118, 110,
+ 110, 110, 110, 110, 110, 119, 119, 110, 119, 119,
+ 119, 41, 119, 119, 110, 110, 120, 110, 110, 121,
+ 110, 110, 121, 110, 110, 110, 110, 110, 110, 122,
+ 110, 110, 123, 110, 110, 110, 118, 118, 110, 110,
+ 119, 110, 119, 37, 110, 110, 41, 42, 110, 119,
+ 110, 120, 121, 110, 124, 110, 125, 122, 110, 123,
+ 110, 126, 110, 126, 126, 110, 124, 125, 127, 127,
+
+ 127, 127, 97, 127, 127, 105, 105, 97, 127, 0,
+ 110, 110, 110, 110, 110, 110, 110, 110, 110, 110,
+ 110, 110, 110, 110, 110, 110, 110
+ } ;
+
+static yyconst flex_int16_t yy_nxt[271] =
+ { 0,
+ 110, 110, 27, 28, 27, 28, 27, 28, 27, 49,
+ 27, 49, 51, 52, 55, 56, 29, 30, 31, 55,
+ 56, 55, 58, 61, 62, 61, 62, 64, 65, 29,
+ 33, 34, 64, 65, 27, 49, 27, 49, 67, 35,
+ 33, 34, 72, 73, 75, 76, 73, 75, 108, 35,
+ 36, 37, 38, 39, 37, 40, 41, 42, 42, 36,
+ 42, 36, 43, 44, 42, 41, 37, 32, 32, 45,
+ 46, 47, 48, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 32, 32, 51, 52, 72, 73, 108, 53,
+ 55, 58, 79, 80, 68, 59, 74, 72, 73, 74,
+
+ 72, 73, 72, 73, 92, 93, 73, 92, 96, 73,
+ 91, 74, 72, 73, 72, 109, 77, 77, 77, 75,
+ 77, 89, 75, 87, 77, 77, 78, 78, 94, 78,
+ 86, 94, 95, 78, 86, 95, 83, 83, 85, 100,
+ 92, 83, 100, 92, 101, 102, 95, 101, 102, 95,
+ 84, 83, 83, 83, 83, 66, 105, 81, 83, 105,
+ 103, 106, 107, 106, 106, 107, 106, 70, 83, 83,
+ 105, 72, 109, 105, 107, 93, 109, 107, 26, 26,
+ 26, 26, 26, 32, 32, 32, 32, 32, 50, 50,
+ 50, 50, 50, 54, 54, 54, 54, 54, 57, 57,
+
+ 57, 57, 57, 60, 60, 60, 60, 60, 63, 63,
+ 63, 63, 63, 71, 71, 71, 71, 71, 82, 82,
+ 83, 69, 68, 83, 83, 88, 66, 110, 88, 88,
+ 90, 110, 110, 90, 90, 97, 110, 110, 97, 97,
+ 98, 98, 99, 110, 99, 99, 99, 104, 110, 104,
+ 104, 104, 25, 110, 110, 110, 110, 110, 110, 110,
+ 110, 110, 110, 110, 110, 110, 110, 110, 110, 110
+ } ;
+
+static yyconst flex_int16_t yy_chk[271] =
+ { 0,
+ 0, 0, 1, 1, 2, 2, 3, 3, 11, 11,
+ 12, 12, 13, 13, 15, 15, 2, 4, 4, 16,
+ 16, 17, 17, 19, 19, 20, 20, 21, 21, 4,
+ 5, 5, 22, 22, 23, 23, 24, 24, 118, 5,
+ 6, 6, 36, 36, 38, 39, 39, 38, 108, 6,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 14, 14, 40, 40, 103, 14,
+ 18, 18, 43, 43, 68, 18, 37, 37, 37, 37,
+
+ 44, 44, 71, 71, 72, 73, 73, 72, 80, 80,
+ 65, 37, 41, 41, 104, 104, 41, 41, 41, 75,
+ 41, 62, 75, 59, 41, 41, 42, 42, 76, 42,
+ 58, 76, 79, 42, 56, 79, 85, 85, 53, 92,
+ 93, 85, 92, 93, 94, 95, 96, 94, 95, 96,
+ 52, 85, 85, 97, 97, 49, 100, 46, 97, 100,
+ 97, 101, 102, 106, 101, 102, 106, 34, 97, 97,
+ 105, 105, 105, 105, 107, 109, 109, 107, 111, 111,
+ 111, 111, 111, 112, 112, 112, 112, 112, 113, 113,
+ 113, 113, 113, 114, 114, 114, 114, 114, 115, 115,
+
+ 115, 115, 115, 116, 116, 116, 116, 116, 117, 117,
+ 117, 117, 117, 119, 119, 119, 119, 119, 120, 120,
+ 121, 31, 29, 121, 121, 122, 28, 25, 122, 122,
+ 123, 0, 0, 123, 123, 124, 0, 0, 124, 124,
+ 125, 125, 126, 0, 126, 126, 126, 127, 0, 127,
+ 127, 127, 110, 110, 110, 110, 110, 110, 110, 110,
+ 110, 110, 110, 110, 110, 110, 110, 110, 110, 110
+ } ;
+
+/* Table of booleans, true if rule could match eol. */
+static yyconst flex_int32_t yy_rule_can_match_eol[31] =
+ { 0,
+0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
+ 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, };
+
+#define REJECT \
+{ \
+*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ \
+yy_cp = yyg->yy_full_match; /* restore poss. backed-over text */ \
+++yyg->yy_lp; \
+goto find_rule; \
+}
+
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+#line 1 "mimeparser.l"
+#line 2 "mimeparser.l"
+/*
+ * Copyright (c) 2004 Jann Fischer. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/**
+ * This is a lexer file for parsing MIME compatible messages. It is intended
+ * to satisfy at least RFC 2045 (Format of Internet Message Bodies). It still
+ * has quite a few problems:
+ *
+ * - The parsing could probably be done in a more elegant way
+ * - I don't know what performance impact REJECT has on the parser
+ */
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "mimeparser.h"
+#include "mimeparser.tab.h"
+
+#define NAMEOF(v) #v
+/* BC() is a debug wrapper for lex' BEGIN() macro */
+#define BC(x) do { \
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner); \
+ BEGIN(x); \
+ lstate->condition = x; \
+} while(0);
+
+#define ZERO(x) memset(x, '\0', sizeof(x))
+
+#define PREALLOC_BUFFER 100000
+#undef YY_BUF_SIZE
+#define YY_BUF_SIZE 65536
+
+enum header_states
+{
+ STATE_MAIL = 0,
+ STATE_CTYPE,
+ STATE_CDISP,
+ STATE_CENC,
+ STATE_MIME
+};
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#line 658 "mimeparser.yy.c"
+
+#define INITIAL 0
+#define headers 1
+#define header 2
+#define headervalue 3
+#define tspecialvalue 4
+#define comment 5
+#define body 6
+#define postamble 7
+#define preamble 8
+#define boundary 9
+#define endboundary 10
+#define endoffile 11
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+/* Holds the entire state of the reentrant scanner. */
+struct yyguts_t
+ {
+
+ /* User-defined. Not touched by flex. */
+ YY_EXTRA_TYPE yyextra_r;
+
+ /* The rest are the same as the globals declared in the non-reentrant scanner. */
+ FILE *yyin_r, *yyout_r;
+ size_t yy_buffer_stack_top; /**< index of top of stack. */
+ size_t yy_buffer_stack_max; /**< capacity of stack. */
+ YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */
+ char yy_hold_char;
+ int yy_n_chars;
+ int yyleng_r;
+ char *yy_c_buf_p;
+ int yy_init;
+ int yy_start;
+ int yy_did_buffer_switch_on_eof;
+ int yy_start_stack_ptr;
+ int yy_start_stack_depth;
+ int *yy_start_stack;
+ yy_state_type yy_last_accepting_state;
+ char* yy_last_accepting_cpos;
+
+ int yylineno_r;
+ int yy_flex_debug_r;
+
+ yy_state_type *yy_state_buf;
+ yy_state_type *yy_state_ptr;
+ char *yy_full_match;
+ int yy_lp;
+
+ char *yytext_r;
+ int yy_more_flag;
+ int yy_more_len;
+
+ YYSTYPE * yylval_r;
+
+ }; /* end struct yyguts_t */
+
+static int yy_init_globals (yyscan_t yyscanner );
+
+ /* This must go here because YYSTYPE and YYLTYPE are included
+ * from bison output in section 1.*/
+ # define yylval yyg->yylval_r
+
+/* Accessor methods to globals.
+ These are made visible to non-reentrant scanners for convenience. */
+
+int mimeparser_yylex_destroy (yyscan_t yyscanner );
+
+int mimeparser_yyget_debug (yyscan_t yyscanner );
+
+void mimeparser_yyset_debug (int debug_flag ,yyscan_t yyscanner );
+
+YY_EXTRA_TYPE mimeparser_yyget_extra (yyscan_t yyscanner );
+
+void mimeparser_yyset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner );
+
+FILE *mimeparser_yyget_in (yyscan_t yyscanner );
+
+void mimeparser_yyset_in (FILE * in_str ,yyscan_t yyscanner );
+
+FILE *mimeparser_yyget_out (yyscan_t yyscanner );
+
+void mimeparser_yyset_out (FILE * out_str ,yyscan_t yyscanner );
+
+int mimeparser_yyget_leng (yyscan_t yyscanner );
+
+char *mimeparser_yyget_text (yyscan_t yyscanner );
+
+int mimeparser_yyget_lineno (yyscan_t yyscanner );
+
+int mimeparser_yyget_column (yyscan_t yyscanner);
+
+void mimeparser_yyset_lineno (int line_number ,yyscan_t yyscanner );
+
+void mimeparser_yyset_column (int column_no , yyscan_t yyscanner);
+
+YYSTYPE * mimeparser_yyget_lval (yyscan_t yyscanner );
+
+void mimeparser_yyset_lval (YYSTYPE * yylval_param ,yyscan_t yyscanner );
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int mimeparser_yywrap (yyscan_t yyscanner );
+#else
+extern int mimeparser_yywrap (yyscan_t yyscanner );
+#endif
+#endif
+
+ static void yyunput (int c,char *buf_ptr ,yyscan_t yyscanner);
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner);
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner);
+#endif
+
+#ifndef YY_NO_INPUT
+
+#ifdef __cplusplus
+static int yyinput (yyscan_t yyscanner );
+#else
+static int input (yyscan_t yyscanner );
+#endif
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO (void) fwrite( yytext, yyleng, 1, yyout )
+#endif
+
+/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
+ { \
+ int c = '*'; \
+ size_t n; \
+ for ( n = 0; n < max_size && \
+ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
+ buf[n] = (char) c; \
+ if ( c == '\n' ) \
+ buf[n++] = (char) c; \
+ if ( c == EOF && ferror( yyin ) ) \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ result = n; \
+ } \
+ else \
+ { \
+ errno=0; \
+ while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \
+ { \
+ if( errno != EINTR) \
+ { \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ break; \
+ } \
+ errno=0; \
+ clearerr(yyin); \
+ } \
+ }\
+\
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner)
+#endif
+
+/* end tables serialization structures and prototypes */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int mimeparser_yylex \
+ (YYSTYPE * yylval_param ,yyscan_t yyscanner);
+
+#define YY_DECL int mimeparser_yylex \
+ (YYSTYPE * yylval_param , yyscan_t yyscanner)
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+ if ( yyleng > 0 ) \
+ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = \
+ (yytext[yyleng - 1] == '\n'); \
+ YY_USER_ACTION
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp, *yy_bp;
+ register int yy_act;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+#line 94 "mimeparser.l"
+
+
+#line 909 "mimeparser.yy.c"
+
+ yylval = yylval_param;
+
+ if ( !yyg->yy_init )
+ {
+ yyg->yy_init = 1;
+
+#ifdef YY_USER_INIT
+ YY_USER_INIT;
+#endif
+
+ /* Create the reject buffer large enough to save one state per allowed character. */
+ if ( ! yyg->yy_state_buf )
+ yyg->yy_state_buf = (yy_state_type *)mimeparser_yyalloc(YY_STATE_BUF_SIZE ,yyscanner);
+
+ if ( ! yyg->yy_start )
+ yyg->yy_start = 1; /* first start state */
+
+ if ( ! yyin )
+ yyin = stdin;
+
+ if ( ! yyout )
+ yyout = stdout;
+
+ if ( ! YY_CURRENT_BUFFER ) {
+ mimeparser_yyensure_buffer_stack (yyscanner);
+ YY_CURRENT_BUFFER_LVALUE =
+ mimeparser_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner);
+ }
+
+ mimeparser_yy_load_buffer_state(yyscanner );
+ }
+
+ while ( 1 ) /* loops until end-of-file is reached */
+ {
+ yy_cp = yyg->yy_c_buf_p;
+
+ /* Support of yytext. */
+ *yy_cp = yyg->yy_hold_char;
+
+ /* yy_bp points to the position in yy_ch_buf of the start of
+ * the current run.
+ */
+ yy_bp = yy_cp;
+
+ yy_current_state = yyg->yy_start;
+ yy_current_state += YY_AT_BOL();
+
+ yyg->yy_state_ptr = yyg->yy_state_buf;
+ *yyg->yy_state_ptr++ = yy_current_state;
+
+yy_match:
+ do
+ {
+ register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 111 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ *yyg->yy_state_ptr++ = yy_current_state;
+ ++yy_cp;
+ }
+ while ( yy_base[yy_current_state] != 253 );
+
+yy_find_action:
+ yy_current_state = *--yyg->yy_state_ptr;
+ yyg->yy_lp = yy_accept[yy_current_state];
+find_rule: /* we branch to this label when backing up */
+ for ( ; ; ) /* until we find what rule we matched */
+ {
+ if ( yyg->yy_lp && yyg->yy_lp < yy_accept[yy_current_state + 1] )
+ {
+ yy_act = yy_acclist[yyg->yy_lp];
+ {
+ yyg->yy_full_match = yy_cp;
+ break;
+ }
+ }
+ --yy_cp;
+ yy_current_state = *--yyg->yy_state_ptr;
+ yyg->yy_lp = yy_accept[yy_current_state];
+ }
+
+ YY_DO_BEFORE_ACTION;
+
+ if ( yy_act != YY_END_OF_BUFFER && yy_rule_can_match_eol[yy_act] )
+ {
+ int yyl;
+ for ( yyl = 0; yyl < yyleng; ++yyl )
+ if ( yytext[yyl] == '\n' )
+
+ do{ yylineno++;
+ yycolumn=0;
+ }while(0)
+;
+ }
+
+do_action: /* This label is used only to access EOF actions. */
+
+ switch ( yy_act )
+ { /* beginning of action switch */
+case 1:
+YY_RULE_SETUP
+#line 96 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+
+ yylval_param->string=strdup(yytext);
+ lstate->current_pos += yyleng;
+ BC(header);
+
+ /* Depending on what header we are processing, we enter a different
+ * state and return a different value.
+ */
+ if (!strcasecmp(yytext, "Content-Type")) {
+ lstate->header_state = STATE_CTYPE;
+ return CONTENTTYPE_HEADER;
+ } else if (!strcasecmp(yytext, "Content-Transfer-Encoding")) {
+ lstate->header_state = STATE_CENC;
+ return CONTENTENCODING_HEADER;
+ } else if (!strcasecmp(yytext, "Content-Disposition")) {
+ lstate->header_state = STATE_CDISP;
+ return CONTENTDISPOSITION_HEADER;
+ } else if (!strcasecmp(yytext, "MIME-Version")) {
+ lstate->header_state = STATE_MAIL;
+ return MIMEVERSION_HEADER;
+ } else {
+ lstate->header_state = STATE_MAIL;
+ return MAIL_HEADER;
+ }
+}
+ YY_BREAK
+case 2:
+YY_RULE_SETUP
+#line 124 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ /* dprintf2("Unknown header char: %c\n", *yytext); */
+ lstate->current_pos += yyleng;
+ return ANY;
+}
+ YY_BREAK
+case 3:
+/* rule 3 can match eol */
+YY_RULE_SETUP
+#line 131 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ lstate->lineno++;
+
+ lstate->current_pos += yyleng;
+
+ /* This marks the end of headers. Depending on whether we are in the
+ * envelope currently we need to parse either a body or the preamble
+ * now.
+ */
+ if (lstate->is_envelope == 0 || lstate->boundary_string == NULL) {
+ BC(body);
+ lstate->body_start = lstate->current_pos;
+ } else {
+ lstate->is_envelope = 0;
+ lstate->preamble_start = lstate->current_pos;
+ BC(preamble);
+ }
+
+ return ENDOFHEADERS;
+}
+ YY_BREAK
+case 4:
+YY_RULE_SETUP
+#line 153 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ BC(headervalue);
+ lstate->current_pos += yyleng;
+ return COLON;
+}
+ YY_BREAK
+case 5:
+/* rule 5 can match eol */
+YY_RULE_SETUP
+#line 160 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ BC(headers);
+ /* dprintf2("Invalid header, returning EOL\n"); */
+ lstate->current_pos += yyleng;
+ return EOL;
+}
+ YY_BREAK
+case 6:
+/* rule 6 can match eol */
+YY_RULE_SETUP
+#line 168 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ lstate->current_pos += yyleng;
+}
+ YY_BREAK
+case 7:
+/* rule 7 can match eol */
+YY_RULE_SETUP
+#line 173 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ if (lstate->header_state != STATE_MAIL && lstate->header_state != STATE_CENC) {
+ REJECT;
+ }
+ lstate->current_pos += yyleng;
+ while (*yytext && isspace(*yytext)) yytext++;
+ /* Do we actually have a header value? */
+ if (*yytext == '\0') {
+ yylval_param->string = strdup("");
+ } else {
+ yylval_param->string=strdup(yytext);
+ lstate->lineno += count_lines(yytext);
+ }
+ return WORD;
+}
+ YY_BREAK
+case 8:
+/* rule 8 can match eol */
+YY_RULE_SETUP
+#line 190 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ /* marks the end of one header line */
+ lstate->lineno++;
+ BC(headers);
+ lstate->current_pos += yyleng;
+ return EOL;
+}
+ YY_BREAK
+case 9:
+/* rule 9 can match eol */
+YY_RULE_SETUP
+#line 199 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ lstate->lineno += count_lines(yytext);
+ lstate->current_pos += yyleng;
+ return SEMICOLON;
+}
+ YY_BREAK
+case 10:
+YY_RULE_SETUP
+#line 206 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ lstate->current_pos += yyleng;
+ return EQUAL;
+}
+ YY_BREAK
+case 11:
+YY_RULE_SETUP
+#line 212 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ BC(tspecialvalue);
+ lstate->current_pos += yyleng;
+ return *yytext;
+}
+ YY_BREAK
+case 12:
+YY_RULE_SETUP
+#line 219 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ yylval_param->string=strdup(yytext);
+ lstate->lineno += count_lines(yytext);
+ lstate->current_pos += yyleng;
+ return WORD;
+}
+ YY_BREAK
+case 13:
+YY_RULE_SETUP
+#line 227 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ lstate->current_pos += yyleng;
+}
+ YY_BREAK
+case 14:
+YY_RULE_SETUP
+#line 232 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ lstate->lineno += count_lines(yytext);
+ yylval_param->string=strdup(yytext);
+ lstate->current_pos += yyleng;
+ return TSPECIAL;
+}
+ YY_BREAK
+case 15:
+YY_RULE_SETUP
+#line 240 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ BC(headervalue);
+ lstate->current_pos += yyleng;
+ return *yytext;
+}
+ YY_BREAK
+case 16:
+YY_RULE_SETUP
+#line 247 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ /**
+ * Make sure we only catch matching boundaries, and not other lines
+ * that begin and end with two dashes. If we have catched a valid
+ * end boundary, which actually ends a body, we save the current
+ * position, put the token back on the input stream and let the
+ * endboundary condition parse the actual token.
+ */
+ if (lstate->endboundary_string != NULL) {
+ if (strcmp(lstate->endboundary_string, yytext)) {
+ /* dprintf2("YYTEXT != end_boundary: '%s'\n", yytext); */
+ REJECT;
+ } else {
+ lstate->current_pos += yyleng;
+ /* dprintf2("YYTEXT == lstate->end_boundary: '%s'\n", yytext); */
+ if (lstate->body_start) {
+ yylval_param->position.opaque_start =
+ lstate->body_opaque_start;
+ yylval_param->position.start = lstate->body_start;
+ yylval_param->position.end = lstate->current_pos - yyleng;
+ lstate->body_opaque_start = 0;
+ lstate->body_start = 0;
+ lstate->body_end = 0;
+ yyless(0);
+ BC(endboundary);
+ return BODY;
+ }
+ }
+ } else {
+ }
+
+ REJECT;
+}
+ YY_BREAK
+case 17:
+YY_RULE_SETUP
+#line 282 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ /**
+ * Make sure we only catch matching boundaries, and not other lines
+ * that begin with two dashes.
+ */
+ if (lstate->boundary_string != NULL) {
+ if (strcmp(lstate->boundary_string, yytext)) {
+ /* dprintf2("YYTEXT != boundary: '%s'\n", yytext);*/
+ REJECT;
+ } else {
+ /* dprintf2("YYTEXT == boundary: '%s'\n", yytext);*/
+ if (lstate->body_start) {
+ yylval_param->position.opaque_start = lstate->body_opaque_start;
+ yylval_param->position.start = lstate->body_start;
+ yylval_param->position.end = lstate->current_pos;
+ lstate->body_opaque_start = 0;
+ lstate->body_start = 0;
+ lstate->body_end = 0;
+ yyless(0);
+ BC(boundary);
+ return BODY;
+ } else if (lstate->preamble_start) {
+ yylval_param->position.start = lstate->preamble_start;
+ yylval_param->position.end = lstate->current_pos;
+ lstate->preamble_start = lstate->preamble_end = 0;
+ yyless(0);
+ BC(boundary);
+ return PREAMBLE;
+ } else {
+ BC(boundary);
+ yylval_param->string = strdup(yytext);
+ lstate->current_pos += yyleng;
+ return(BOUNDARY);
+ }
+ }
+ } else {
+ }
+
+ REJECT;
+}
+ YY_BREAK
+case 18:
+/* rule 18 can match eol */
+YY_RULE_SETUP
+#line 324 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ lstate->current_pos += yyleng;
+ lstate->lineno++;
+}
+ YY_BREAK
+case 19:
+YY_RULE_SETUP
+#line 330 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ lstate->current_pos += yyleng;
+ /* dprintf2("stray CR in body...\n"); */
+}
+ YY_BREAK
+case 20:
+YY_RULE_SETUP
+#line 336 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ lstate->current_pos += yyleng;
+}
+ YY_BREAK
+case YY_STATE_EOF(body):
+#line 341 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ if (lstate->boundary_string == NULL && lstate->body_start) {
+ yylval_param->position.opaque_start = 0;
+ yylval_param->position.start = lstate->body_start;
+ yylval_param->position.end = lstate->current_pos;
+ lstate->body_start = 0;
+ return BODY;
+ } else if (lstate->body_start) {
+ return POSTAMBLE;
+ }
+ yyterminate();
+}
+ YY_BREAK
+case 21:
+/* rule 21 can match eol */
+YY_RULE_SETUP
+#line 355 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ /* dprintf2("Preamble CR/LF at line %d\n", lineno); */
+ lstate->lineno++;
+ lstate->current_pos += yyleng;
+}
+ YY_BREAK
+case 22:
+YY_RULE_SETUP
+#line 362 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ yylval_param->string = strdup(yytext);
+ lstate->current_pos += yyleng;
+ return BOUNDARY;
+}
+ YY_BREAK
+case 23:
+YY_RULE_SETUP
+#line 369 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ yylval_param->string = strdup(yytext);
+ lstate->current_pos += yyleng;
+ return ENDBOUNDARY;
+}
+ YY_BREAK
+case 24:
+/* rule 24 can match eol */
+YY_RULE_SETUP
+#line 376 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ BC(headers);
+ lstate->lineno++;
+ lstate->current_pos += yyleng;
+ lstate->body_opaque_start = lstate->current_pos;
+ return EOL;
+}
+ YY_BREAK
+case 25:
+/* rule 25 can match eol */
+YY_RULE_SETUP
+#line 385 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ BC(postamble);
+ lstate->lineno++;
+ lstate->current_pos += yyleng;
+}
+ YY_BREAK
+case 26:
+YY_RULE_SETUP
+#line 392 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ lstate->current_pos += yyleng;
+}
+ YY_BREAK
+case 27:
+YY_RULE_SETUP
+#line 398 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ lstate->current_pos += yyleng;
+}
+ YY_BREAK
+case 28:
+/* rule 28 can match eol */
+YY_RULE_SETUP
+#line 403 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ lstate->lineno++;
+ lstate->current_pos += yyleng;
+ return EOL;
+}
+ YY_BREAK
+case 29:
+YY_RULE_SETUP
+#line 410 "mimeparser.l"
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(yyscanner);
+ lstate->current_pos += yyleng;
+ return((int)*yytext);
+}
+ YY_BREAK
+case 30:
+YY_RULE_SETUP
+#line 417 "mimeparser.l"
+ECHO;
+ YY_BREAK
+#line 1438 "mimeparser.yy.c"
+ case YY_STATE_EOF(INITIAL):
+ case YY_STATE_EOF(headers):
+ case YY_STATE_EOF(header):
+ case YY_STATE_EOF(headervalue):
+ case YY_STATE_EOF(tspecialvalue):
+ case YY_STATE_EOF(comment):
+ case YY_STATE_EOF(postamble):
+ case YY_STATE_EOF(preamble):
+ case YY_STATE_EOF(boundary):
+ case YY_STATE_EOF(endboundary):
+ case YY_STATE_EOF(endoffile):
+ yyterminate();
+
+ case YY_END_OF_BUFFER:
+ {
+ /* Amount of text matched not including the EOB char. */
+ int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1;
+
+ /* Undo the effects of YY_DO_BEFORE_ACTION. */
+ *yy_cp = yyg->yy_hold_char;
+ YY_RESTORE_YY_MORE_OFFSET
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+ {
+ /* We're scanning a new file or input source. It's
+ * possible that this happened because the user
+ * just pointed yyin at a new source and called
+ * mimeparser_yylex(). If so, then we have to assure
+ * consistency between YY_CURRENT_BUFFER and our
+ * globals. Here is the right place to do so, because
+ * this is the first action (other than possibly a
+ * back-up) that will match for the new input source.
+ */
+ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+ }
+
+ /* Note that here we test for yy_c_buf_p "<=" to the position
+ * of the first EOB in the buffer, since yy_c_buf_p will
+ * already have been incremented past the NUL character
+ * (since all states make transitions on EOB to the
+ * end-of-buffer state). Contrast this with the test
+ * in input().
+ */
+ if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] )
+ { /* This was really a NUL. */
+ yy_state_type yy_next_state;
+
+ yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( yyscanner );
+
+ /* Okay, we're now positioned to make the NUL
+ * transition. We couldn't have
+ * yy_get_previous_state() go ahead and do it
+ * for us because it doesn't know how to deal
+ * with the possibility of jamming (and we don't
+ * want to build jamming into it because then it
+ * will run more slowly).
+ */
+
+ yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner);
+
+ yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+
+ if ( yy_next_state )
+ {
+ /* Consume the NUL. */
+ yy_cp = ++yyg->yy_c_buf_p;
+ yy_current_state = yy_next_state;
+ goto yy_match;
+ }
+
+ else
+ {
+ yy_cp = yyg->yy_c_buf_p;
+ goto yy_find_action;
+ }
+ }
+
+ else switch ( yy_get_next_buffer( yyscanner ) )
+ {
+ case EOB_ACT_END_OF_FILE:
+ {
+ yyg->yy_did_buffer_switch_on_eof = 0;
+
+ if ( mimeparser_yywrap(yyscanner ) )
+ {
+ /* Note: because we've taken care in
+ * yy_get_next_buffer() to have set up
+ * yytext, we can now set up
+ * yy_c_buf_p so that if some total
+ * hoser (like flex itself) wants to
+ * call the scanner after we return the
+ * YY_NULL, it'll still work - another
+ * YY_NULL will get returned.
+ */
+ yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ;
+
+ yy_act = YY_STATE_EOF(YY_START);
+ goto do_action;
+ }
+
+ else
+ {
+ if ( ! yyg->yy_did_buffer_switch_on_eof )
+ YY_NEW_FILE;
+ }
+ break;
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ yyg->yy_c_buf_p =
+ yyg->yytext_ptr + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( yyscanner );
+
+ yy_cp = yyg->yy_c_buf_p;
+ yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+ goto yy_match;
+
+ case EOB_ACT_LAST_MATCH:
+ yyg->yy_c_buf_p =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars];
+
+ yy_current_state = yy_get_previous_state( yyscanner );
+
+ yy_cp = yyg->yy_c_buf_p;
+ yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+ goto yy_find_action;
+ }
+ break;
+ }
+
+ default:
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--no action found" );
+ } /* end of action switch */
+ } /* end of scanning one token */
+} /* end of mimeparser_yylex */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ * EOB_ACT_LAST_MATCH -
+ * EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ * EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+ register char *source = yyg->yytext_ptr;
+ register int number_to_move, i;
+ int ret_val;
+
+ if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] )
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--end of buffer missed" );
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+ { /* Don't try to fill the buffer, so this is an EOF. */
+ if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 )
+ {
+ /* We matched a single character, the EOB, so
+ * treat this as a final EOF.
+ */
+ return EOB_ACT_END_OF_FILE;
+ }
+
+ else
+ {
+ /* We matched some text prior to the EOB, first
+ * process it.
+ */
+ return EOB_ACT_LAST_MATCH;
+ }
+ }
+
+ /* Try to read more data. */
+
+ /* First move last chars to start of buffer. */
+ number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr) - 1;
+
+ for ( i = 0; i < number_to_move; ++i )
+ *(dest++) = *(source++);
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+ /* don't do the read, it's not guaranteed to return an EOF,
+ * just force an EOF
+ */
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0;
+
+ else
+ {
+ int num_to_read =
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+ while ( num_to_read <= 0 )
+ { /* Not enough room in the buffer - grow it. */
+
+ YY_FATAL_ERROR(
+"input buffer overflow, can't enlarge buffer because scanner uses REJECT" );
+
+ }
+
+ if ( num_to_read > YY_READ_BUF_SIZE )
+ num_to_read = YY_READ_BUF_SIZE;
+
+ /* Read in more data. */
+ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+ yyg->yy_n_chars, (size_t) num_to_read );
+
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+ }
+
+ if ( yyg->yy_n_chars == 0 )
+ {
+ if ( number_to_move == YY_MORE_ADJ )
+ {
+ ret_val = EOB_ACT_END_OF_FILE;
+ mimeparser_yyrestart(yyin ,yyscanner);
+ }
+
+ else
+ {
+ ret_val = EOB_ACT_LAST_MATCH;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+ YY_BUFFER_EOF_PENDING;
+ }
+ }
+
+ else
+ ret_val = EOB_ACT_CONTINUE_SCAN;
+
+ yyg->yy_n_chars += number_to_move;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR;
+
+ yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+ return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+ static yy_state_type yy_get_previous_state (yyscan_t yyscanner)
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ yy_current_state = yyg->yy_start;
+ yy_current_state += YY_AT_BOL();
+
+ yyg->yy_state_ptr = yyg->yy_state_buf;
+ *yyg->yy_state_ptr++ = yy_current_state;
+
+ for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp )
+ {
+ register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 111 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ *yyg->yy_state_ptr++ = yy_current_state;
+ }
+
+ return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ * next_state = yy_try_NUL_trans( current_state );
+ */
+ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner)
+{
+ register int yy_is_jam;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */
+
+ register YY_CHAR yy_c = 1;
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 111 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ yy_is_jam = (yy_current_state == 110);
+ if ( ! yy_is_jam )
+ *yyg->yy_state_ptr++ = yy_current_state;
+
+ return yy_is_jam ? 0 : yy_current_state;
+}
+
+ static void yyunput (int c, register char * yy_bp , yyscan_t yyscanner)
+{
+ register char *yy_cp;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ yy_cp = yyg->yy_c_buf_p;
+
+ /* undo effects of setting up yytext */
+ *yy_cp = yyg->yy_hold_char;
+
+ if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+ { /* need to shift things up to make room */
+ /* +2 for EOB chars. */
+ register int number_to_move = yyg->yy_n_chars + 2;
+ register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2];
+ register char *source =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move];
+
+ while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+ *--dest = *--source;
+
+ yy_cp += (int) (dest - source);
+ yy_bp += (int) (dest - source);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars =
+ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_buf_size;
+
+ if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+ YY_FATAL_ERROR( "flex scanner push-back overflow" );
+ }
+
+ *--yy_cp = (char) c;
+
+ if ( c == '\n' ){
+ --yylineno;
+ }
+
+ yyg->yytext_ptr = yy_bp;
+ yyg->yy_hold_char = *yy_cp;
+ yyg->yy_c_buf_p = yy_cp;
+}
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+ static int yyinput (yyscan_t yyscanner)
+#else
+ static int input (yyscan_t yyscanner)
+#endif
+
+{
+ int c;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ *yyg->yy_c_buf_p = yyg->yy_hold_char;
+
+ if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR )
+ {
+ /* yy_c_buf_p now points to the character we want to return.
+ * If this occurs *before* the EOB characters, then it's a
+ * valid NUL; if not, then we've hit the end of the buffer.
+ */
+ if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] )
+ /* This was really a NUL. */
+ *yyg->yy_c_buf_p = '\0';
+
+ else
+ { /* need more input */
+ int offset = yyg->yy_c_buf_p - yyg->yytext_ptr;
+ ++yyg->yy_c_buf_p;
+
+ switch ( yy_get_next_buffer( yyscanner ) )
+ {
+ case EOB_ACT_LAST_MATCH:
+ /* This happens because yy_g_n_b()
+ * sees that we've accumulated a
+ * token and flags that we need to
+ * try matching the token before
+ * proceeding. But for input(),
+ * there's no matching to consider.
+ * So convert the EOB_ACT_LAST_MATCH
+ * to EOB_ACT_END_OF_FILE.
+ */
+
+ /* Reset buffer status. */
+ mimeparser_yyrestart(yyin ,yyscanner);
+
+ /*FALLTHROUGH*/
+
+ case EOB_ACT_END_OF_FILE:
+ {
+ if ( mimeparser_yywrap(yyscanner ) )
+ return EOF;
+
+ if ( ! yyg->yy_did_buffer_switch_on_eof )
+ YY_NEW_FILE;
+#ifdef __cplusplus
+ return yyinput(yyscanner);
+#else
+ return input(yyscanner);
+#endif
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ yyg->yy_c_buf_p = yyg->yytext_ptr + offset;
+ break;
+ }
+ }
+ }
+
+ c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */
+ *yyg->yy_c_buf_p = '\0'; /* preserve yytext */
+ yyg->yy_hold_char = *++yyg->yy_c_buf_p;
+
+ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = (c == '\n');
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_at_bol )
+
+ do{ yylineno++;
+ yycolumn=0;
+ }while(0)
+;
+
+ return c;
+}
+#endif /* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ * @param yyscanner The scanner object.
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+ void mimeparser_yyrestart (FILE * input_file , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if ( ! YY_CURRENT_BUFFER ){
+ mimeparser_yyensure_buffer_stack (yyscanner);
+ YY_CURRENT_BUFFER_LVALUE =
+ mimeparser_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner);
+ }
+
+ mimeparser_yy_init_buffer(YY_CURRENT_BUFFER,input_file ,yyscanner);
+ mimeparser_yy_load_buffer_state(yyscanner );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ * @param yyscanner The scanner object.
+ */
+ void mimeparser_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* TODO. We should be able to replace this entire function body
+ * with
+ * mimeparser_yypop_buffer_state();
+ * mimeparser_yypush_buffer_state(new_buffer);
+ */
+ mimeparser_yyensure_buffer_stack (yyscanner);
+ if ( YY_CURRENT_BUFFER == new_buffer )
+ return;
+
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *yyg->yy_c_buf_p = yyg->yy_hold_char;
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p;
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+ }
+
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+ mimeparser_yy_load_buffer_state(yyscanner );
+
+ /* We don't actually know whether we did this switch during
+ * EOF (mimeparser_yywrap()) processing, but the only time this flag
+ * is looked at is after mimeparser_yywrap() is called, so it's safe
+ * to go ahead and always set it.
+ */
+ yyg->yy_did_buffer_switch_on_eof = 1;
+}
+
+static void mimeparser_yy_load_buffer_state (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+ yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+ yyg->yy_hold_char = *yyg->yy_c_buf_p;
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ * @param yyscanner The scanner object.
+ * @return the allocated buffer state.
+ */
+ YY_BUFFER_STATE mimeparser_yy_create_buffer (FILE * file, int size , yyscan_t yyscanner)
+{
+ YY_BUFFER_STATE b;
+
+ b = (YY_BUFFER_STATE) mimeparser_yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in mimeparser_yy_create_buffer()" );
+
+ b->yy_buf_size = size;
+
+ /* yy_ch_buf has to be 2 characters longer than the size given because
+ * we need to put in 2 end-of-buffer characters.
+ */
+ b->yy_ch_buf = (char *) mimeparser_yyalloc(b->yy_buf_size + 2 ,yyscanner );
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in mimeparser_yy_create_buffer()" );
+
+ b->yy_is_our_buffer = 1;
+
+ mimeparser_yy_init_buffer(b,file ,yyscanner);
+
+ return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with mimeparser_yy_create_buffer()
+ * @param yyscanner The scanner object.
+ */
+ void mimeparser_yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if ( ! b )
+ return;
+
+ if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+ if ( b->yy_is_our_buffer )
+ mimeparser_yyfree((void *) b->yy_ch_buf ,yyscanner );
+
+ mimeparser_yyfree((void *) b ,yyscanner );
+}
+
+#ifndef __cplusplus
+extern int isatty (int );
+#endif /* __cplusplus */
+
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a mimeparser_yyrestart() or at EOF.
+ */
+ static void mimeparser_yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner)
+
+{
+ int oerrno = errno;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ mimeparser_yy_flush_buffer(b ,yyscanner);
+
+ b->yy_input_file = file;
+ b->yy_fill_buffer = 1;
+
+ /* If b is the current buffer, then mimeparser_yy_init_buffer was _probably_
+ * called from mimeparser_yyrestart() or through yy_get_next_buffer.
+ * In that case, we don't want to reset the lineno or column.
+ */
+ if (b != YY_CURRENT_BUFFER){
+ b->yy_bs_lineno = 1;
+ b->yy_bs_column = 0;
+ }
+
+ b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+
+ errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ * @param yyscanner The scanner object.
+ */
+ void mimeparser_yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ if ( ! b )
+ return;
+
+ b->yy_n_chars = 0;
+
+ /* We always need two end-of-buffer characters. The first causes
+ * a transition to the end-of-buffer state. The second causes
+ * a jam in that state.
+ */
+ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+ b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+ b->yy_buf_pos = &b->yy_ch_buf[0];
+
+ b->yy_at_bol = 1;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ if ( b == YY_CURRENT_BUFFER )
+ mimeparser_yy_load_buffer_state(yyscanner );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ * the current state. This function will allocate the stack
+ * if necessary.
+ * @param new_buffer The new state.
+ * @param yyscanner The scanner object.
+ */
+void mimeparser_yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ if (new_buffer == NULL)
+ return;
+
+ mimeparser_yyensure_buffer_stack(yyscanner);
+
+ /* This block is copied from mimeparser_yy_switch_to_buffer. */
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *yyg->yy_c_buf_p = yyg->yy_hold_char;
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p;
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+ }
+
+ /* Only push if top exists. Otherwise, replace top. */
+ if (YY_CURRENT_BUFFER)
+ yyg->yy_buffer_stack_top++;
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+ /* copied from mimeparser_yy_switch_to_buffer. */
+ mimeparser_yy_load_buffer_state(yyscanner );
+ yyg->yy_did_buffer_switch_on_eof = 1;
+}
+
+/** Removes and deletes the top of the stack, if present.
+ * The next element becomes the new top.
+ * @param yyscanner The scanner object.
+ */
+void mimeparser_yypop_buffer_state (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ if (!YY_CURRENT_BUFFER)
+ return;
+
+ mimeparser_yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner);
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ if (yyg->yy_buffer_stack_top > 0)
+ --yyg->yy_buffer_stack_top;
+
+ if (YY_CURRENT_BUFFER) {
+ mimeparser_yy_load_buffer_state(yyscanner );
+ yyg->yy_did_buffer_switch_on_eof = 1;
+ }
+}
+
+/* Allocates the stack if it does not exist.
+ * Guarantees space for at least one push.
+ */
+static void mimeparser_yyensure_buffer_stack (yyscan_t yyscanner)
+{
+ int num_to_alloc;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if (!yyg->yy_buffer_stack) {
+
+ /* First allocation is just for 2 elements, since we don't know if this
+ * scanner will even need a stack. We use 2 instead of 1 to avoid an
+ * immediate realloc on the next call.
+ */
+ num_to_alloc = 1;
+ yyg->yy_buffer_stack = (struct yy_buffer_state**)mimeparser_yyalloc
+ (num_to_alloc * sizeof(struct yy_buffer_state*)
+ , yyscanner);
+
+ memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+
+ yyg->yy_buffer_stack_max = num_to_alloc;
+ yyg->yy_buffer_stack_top = 0;
+ return;
+ }
+
+ if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){
+
+ /* Increase the buffer to prepare for a possible push. */
+ int grow_size = 8 /* arbitrary grow size */;
+
+ num_to_alloc = yyg->yy_buffer_stack_max + grow_size;
+ yyg->yy_buffer_stack = (struct yy_buffer_state**)mimeparser_yyrealloc
+ (yyg->yy_buffer_stack,
+ num_to_alloc * sizeof(struct yy_buffer_state*)
+ , yyscanner);
+
+ /* zero only the new slots.*/
+ memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*));
+ yyg->yy_buffer_stack_max = num_to_alloc;
+ }
+}
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE mimeparser_yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner)
+{
+ YY_BUFFER_STATE b;
+
+ if ( size < 2 ||
+ base[size-2] != YY_END_OF_BUFFER_CHAR ||
+ base[size-1] != YY_END_OF_BUFFER_CHAR )
+ /* They forgot to leave room for the EOB's. */
+ return 0;
+
+ b = (YY_BUFFER_STATE) mimeparser_yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in mimeparser_yy_scan_buffer()" );
+
+ b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */
+ b->yy_buf_pos = b->yy_ch_buf = base;
+ b->yy_is_our_buffer = 0;
+ b->yy_input_file = 0;
+ b->yy_n_chars = b->yy_buf_size;
+ b->yy_is_interactive = 0;
+ b->yy_at_bol = 1;
+ b->yy_fill_buffer = 0;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ mimeparser_yy_switch_to_buffer(b ,yyscanner );
+
+ return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to mimeparser_yylex() will
+ * scan from a @e copy of @a str.
+ * @param yystr a NUL-terminated string to scan
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ * mimeparser_yy_scan_bytes() instead.
+ */
+YY_BUFFER_STATE mimeparser_yy_scan_string (yyconst char * yystr , yyscan_t yyscanner)
+{
+
+ return mimeparser_yy_scan_bytes(yystr,strlen(yystr) ,yyscanner);
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to mimeparser_yylex() will
+ * scan from a @e copy of @a bytes.
+ * @param bytes the byte buffer to scan
+ * @param len the number of bytes in the buffer pointed to by @a bytes.
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE mimeparser_yy_scan_bytes (yyconst char * yybytes, int _yybytes_len , yyscan_t yyscanner)
+{
+ YY_BUFFER_STATE b;
+ char *buf;
+ yy_size_t n;
+ int i;
+
+ /* Get memory for full buffer, including space for trailing EOB's. */
+ n = _yybytes_len + 2;
+ buf = (char *) mimeparser_yyalloc(n ,yyscanner );
+ if ( ! buf )
+ YY_FATAL_ERROR( "out of dynamic memory in mimeparser_yy_scan_bytes()" );
+
+ for ( i = 0; i < _yybytes_len; ++i )
+ buf[i] = yybytes[i];
+
+ buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
+
+ b = mimeparser_yy_scan_buffer(buf,n ,yyscanner);
+ if ( ! b )
+ YY_FATAL_ERROR( "bad buffer in mimeparser_yy_scan_bytes()" );
+
+ /* It's okay to grow etc. this buffer, and we should throw it
+ * away when we're done.
+ */
+ b->yy_is_our_buffer = 1;
+
+ return b;
+}
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yy_fatal_error (yyconst char* msg , yyscan_t yyscanner)
+{
+ (void) fprintf( stderr, "%s\n", msg );
+ exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ yytext[yyleng] = yyg->yy_hold_char; \
+ yyg->yy_c_buf_p = yytext + yyless_macro_arg; \
+ yyg->yy_hold_char = *yyg->yy_c_buf_p; \
+ *yyg->yy_c_buf_p = '\0'; \
+ yyleng = yyless_macro_arg; \
+ } \
+ while ( 0 )
+
+/* Accessor methods (get/set functions) to struct members. */
+
+/** Get the user-defined data for this scanner.
+ * @param yyscanner The scanner object.
+ */
+YY_EXTRA_TYPE mimeparser_yyget_extra (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyextra;
+}
+
+/** Get the current line number.
+ * @param yyscanner The scanner object.
+ */
+int mimeparser_yyget_lineno (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if (! YY_CURRENT_BUFFER)
+ return 0;
+
+ return yylineno;
+}
+
+/** Get the current column number.
+ * @param yyscanner The scanner object.
+ */
+int mimeparser_yyget_column (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if (! YY_CURRENT_BUFFER)
+ return 0;
+
+ return yycolumn;
+}
+
+/** Get the input stream.
+ * @param yyscanner The scanner object.
+ */
+FILE *mimeparser_yyget_in (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyin;
+}
+
+/** Get the output stream.
+ * @param yyscanner The scanner object.
+ */
+FILE *mimeparser_yyget_out (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyout;
+}
+
+/** Get the length of the current token.
+ * @param yyscanner The scanner object.
+ */
+int mimeparser_yyget_leng (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyleng;
+}
+
+/** Get the current token.
+ * @param yyscanner The scanner object.
+ */
+
+char *mimeparser_yyget_text (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yytext;
+}
+
+/** Set the user-defined data. This data is never touched by the scanner.
+ * @param user_defined The data to be associated with this scanner.
+ * @param yyscanner The scanner object.
+ */
+void mimeparser_yyset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyextra = user_defined ;
+}
+
+/** Set the current line number.
+ * @param line_number
+ * @param yyscanner The scanner object.
+ */
+void mimeparser_yyset_lineno (int line_number , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* lineno is only valid if an input buffer exists. */
+ if (! YY_CURRENT_BUFFER )
+ yy_fatal_error( "mimeparser_yyset_lineno called with no buffer" , yyscanner);
+
+ yylineno = line_number;
+}
+
+/** Set the current column.
+ * @param line_number
+ * @param yyscanner The scanner object.
+ */
+void mimeparser_yyset_column (int column_no , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* column is only valid if an input buffer exists. */
+ if (! YY_CURRENT_BUFFER )
+ yy_fatal_error( "mimeparser_yyset_column called with no buffer" , yyscanner);
+
+ yycolumn = column_no;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param in_str A readable stream.
+ * @param yyscanner The scanner object.
+ * @see mimeparser_yy_switch_to_buffer
+ */
+void mimeparser_yyset_in (FILE * in_str , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyin = in_str ;
+}
+
+void mimeparser_yyset_out (FILE * out_str , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyout = out_str ;
+}
+
+int mimeparser_yyget_debug (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yy_flex_debug;
+}
+
+void mimeparser_yyset_debug (int bdebug , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yy_flex_debug = bdebug ;
+}
+
+/* Accessor methods for yylval and yylloc */
+
+YYSTYPE * mimeparser_yyget_lval (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yylval;
+}
+
+void mimeparser_yyset_lval (YYSTYPE * yylval_param , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yylval = yylval_param;
+}
+
+/* User-visible API */
+
+/* mimeparser_yylex_init is special because it creates the scanner itself, so it is
+ * the ONLY reentrant function that doesn't take the scanner as the last argument.
+ * That's why we explicitly handle the declaration, instead of using our macros.
+ */
+
+int mimeparser_yylex_init(yyscan_t* ptr_yy_globals)
+
+{
+ if (ptr_yy_globals == NULL){
+ errno = EINVAL;
+ return 1;
+ }
+
+ *ptr_yy_globals = (yyscan_t) mimeparser_yyalloc ( sizeof( struct yyguts_t ), NULL );
+
+ if (*ptr_yy_globals == NULL){
+ errno = ENOMEM;
+ return 1;
+ }
+
+ /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */
+ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t));
+
+ return yy_init_globals ( *ptr_yy_globals );
+}
+
+static int yy_init_globals (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ /* Initialization is the same as for the non-reentrant scanner.
+ * This function is called from mimeparser_yylex_destroy(), so don't allocate here.
+ */
+
+ yyg->yy_buffer_stack = 0;
+ yyg->yy_buffer_stack_top = 0;
+ yyg->yy_buffer_stack_max = 0;
+ yyg->yy_c_buf_p = (char *) 0;
+ yyg->yy_init = 0;
+ yyg->yy_start = 0;
+
+ yyg->yy_start_stack_ptr = 0;
+ yyg->yy_start_stack_depth = 0;
+ yyg->yy_start_stack = NULL;
+
+ yyg->yy_state_buf = 0;
+ yyg->yy_state_ptr = 0;
+ yyg->yy_full_match = 0;
+ yyg->yy_lp = 0;
+
+/* Defined in main.c */
+#ifdef YY_STDINIT
+ yyin = stdin;
+ yyout = stdout;
+#else
+ yyin = (FILE *) 0;
+ yyout = (FILE *) 0;
+#endif
+
+ /* For future reference: Set errno on error, since we are called by
+ * mimeparser_yylex_init()
+ */
+ return 0;
+}
+
+/* mimeparser_yylex_destroy is for both reentrant and non-reentrant scanners. */
+int mimeparser_yylex_destroy (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* Pop the buffer stack, destroying each element. */
+ while(YY_CURRENT_BUFFER){
+ mimeparser_yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ mimeparser_yypop_buffer_state(yyscanner);
+ }
+
+ /* Destroy the stack itself. */
+ mimeparser_yyfree(yyg->yy_buffer_stack ,yyscanner);
+ yyg->yy_buffer_stack = NULL;
+
+ /* Destroy the start condition stack. */
+ mimeparser_yyfree(yyg->yy_start_stack ,yyscanner );
+ yyg->yy_start_stack = NULL;
+
+ mimeparser_yyfree ( yyg->yy_state_buf , yyscanner);
+ yyg->yy_state_buf = NULL;
+
+ /* Reset the globals. This is important in a non-reentrant scanner so the next time
+ * mimeparser_yylex() is called, initialization will occur. */
+ yy_init_globals( yyscanner);
+
+ /* Destroy the main struct (reentrant only). */
+ mimeparser_yyfree ( yyscanner , yyscanner );
+ yyscanner = NULL;
+ return 0;
+}
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, yyconst char * s2, int n , yyscan_t yyscanner)
+{
+ register int i;
+ for ( i = 0; i < n; ++i )
+ s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * s , yyscan_t yyscanner)
+{
+ register int n;
+ for ( n = 0; s[n]; ++n )
+ ;
+
+ return n;
+}
+#endif
+
+void *mimeparser_yyalloc (yy_size_t size , yyscan_t yyscanner)
+{
+ return (void *) malloc( size );
+}
+
+void *mimeparser_yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner)
+{
+ /* The cast to (char *) in the following accommodates both
+ * implementations that use char* generic pointers, and those
+ * that use void* generic pointers. It works with the latter
+ * because both ANSI C and C++ allow castless assignment from
+ * any pointer type to void*, and deal with argument conversions
+ * as though doing an assignment.
+ */
+ return (void *) realloc( (char *) ptr, size );
+}
+
+void mimeparser_yyfree (void * ptr , yyscan_t yyscanner)
+{
+ free( (char *) ptr ); /* see mimeparser_yyrealloc() for (char *) cast */
+}
+
+#define YYTABLES_NAME "yytables"
+
+#line 417 "mimeparser.l"
+
+
+
+void reset_lexer_state(void *yyscanner, struct parser_state *pstate)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ struct lexer_state *lstate = &(pstate->lstate);
+
+ mimeparser_yyset_extra((void*)lstate,yyscanner);
+ BEGIN(0);
+ lstate->header_state = STATE_MAIL;
+ lstate->lineno = 0;
+ lstate->current_pos = 1;
+ lstate->condition = 0;
+
+ lstate->is_envelope = 1;
+
+ lstate->message_len = 0;
+ lstate->buffer_length = 0;
+
+ /* temporary marker variables */
+ lstate->body_opaque_start = 0;
+ lstate->body_start = 0;
+ lstate->body_end = 0;
+ lstate->preamble_start = 0;
+ lstate->preamble_end = 0;
+ lstate->postamble_start = 0;
+ lstate->postamble_end = 0;
+}
+
+void
+PARSER_setbuffer(const char *string, yyscan_t scanner)
+{
+ struct lexer_state *lstate = mimeparser_yyget_extra(scanner);
+ lstate->message_buffer = string;
+ mimeparser_yy_scan_string(string,scanner);
+}
+
+void
+PARSER_setfp(FILE *fp, yyscan_t scanner)
+{
+ /* looks like a bug in bison 2.2a -- the wrong code is generated for mimeparser_yyset_in !! */
+ struct yyguts_t * yyg = (struct yyguts_t*) scanner;
+ yyg->yyin_r = fp;
+
+ if (0) {
+ /* This is just to make a compiler warning go away */
+ yyunput(0, NULL, scanner);
+ }
+}
+
+/**
+ * Counts how many lines a given string represents in the message (in case of
+ * folded header values, for example, or a message body).
+ */
+int
+count_lines(char *txt)
+{
+ char *o;
+ int line;
+
+ line = 0;
+
+ for (o = txt; *o != '\0'; o++)
+ if (*o == '\n')
+ line++;
+
+ return line;
+}
+
diff --git a/trunk/main/minimime/minimime.c b/trunk/main/minimime/minimime.c
new file mode 100644
index 000000000..1b324585c
--- /dev/null
+++ b/trunk/main/minimime/minimime.c
@@ -0,0 +1,244 @@
+/*
+ * Copyright (c) 2004 Jann Fischer. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * MiniMIME test program
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <getopt.h>
+
+#include "mm.h"
+
+void
+usage(void)
+{
+ fprintf(stderr,
+ "MiniMIME test suite\n"
+ "Usage: ./minimime [-m] <filename>\n\n"
+ " -m : use memory based scanning\n\n"
+ );
+ exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+ MM_CTX *ctx;
+ struct mm_mimeheader *header, *lastheader;
+ struct mm_warning *lastwarning;
+ struct mm_mimepart *part;
+ struct mm_content *ct;
+ int parts, i;
+ struct stat st;
+ int fd;
+ char *buf;
+ int scan_mode = 0;
+
+ lastheader = NULL;
+
+ while ((i = getopt(argc, argv, "m")) != -1) {
+ switch(i) {
+ case 'm':
+ scan_mode = 1;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ usage();
+ }
+
+#ifdef __HAVE_LEAK_DETECTION
+ /* Initialize memory leak detection if compiled in */
+ MM_leakd_init();
+#endif
+
+ /* Initialize MiniMIME library */
+ mm_library_init();
+
+ /* Register all default codecs (base64/qp) */
+ mm_codec_registerdefaultcodecs();
+
+ do {
+ /* Create a new context */
+ ctx = mm_context_new();
+
+ /* Parse a file into our context */
+ if (scan_mode == 0) {
+ i = mm_parse_file(ctx, argv[0], MM_PARSE_LOOSE, 0);
+ } else {
+ if (stat(argv[0], &st) == -1) {
+ fprintf(stderr, "INFO: stat");
+ }
+
+ if ((fd = open(argv[0], O_RDONLY)) == -1) {
+ fdprintf(stderr, "INFO: open");
+ }
+
+ buf = (char *)malloc(st.st_size);
+ if (buf == NULL) {
+ fdprintf(stderr, "INFO: malloc");
+ }
+
+ if (read(fd, buf, st.st_size) != st.st_size) {
+ fdprintf(stderr, "INFO: read");
+ }
+
+ close(fd);
+ buf[st.st_size] = '\0';
+
+ i = mm_parse_mem(ctx, buf, MM_PARSE_LOOSE, 0);
+ }
+
+ if (i == -1 || mm_errno != MM_ERROR_NONE) {
+ printf("ERROR: %s at line %d\n", mm_error_string(), mm_error_lineno());
+ exit(1);
+ }
+
+ /* Get the number of MIME parts */
+ parts = mm_context_countparts(ctx);
+ if (parts == 0) {
+ printf("ERROR: got zero MIME parts, huh\n");
+ exit(1);
+ } else {
+ if (mm_context_iscomposite(ctx)) {
+ printf("Got %d MIME parts\n", parts - 1);
+ } else {
+ printf("Flat message (not multipart)\n");
+ }
+ }
+
+ /* Get the main MIME part */
+ part = mm_context_getpart(ctx, 0);
+ if (part == NULL) {
+ fprintf(stderr, "Could not get envelope part\n");
+ exit(1);
+ }
+
+ printf("Printing envelope headers:\n");
+ /* Print all headers */
+ if (mm_mimepart_headers_start(part, &lastheader) == -1) {
+ fprintf(stderr, "No headers in envelope\n");
+ exit(1);
+ }
+ while ((header = mm_mimepart_headers_next(part, &lastheader)) != NULL) {
+ printf("%s: %s\n", header->name, header->value);
+ }
+
+ printf("%s\n", mm_content_tostring(part->type));
+ printf("\n");
+
+ ct = part->type;
+ assert(ct != NULL);
+
+ if (mm_context_iscomposite(ctx) == 0) {
+ printf("Printing body part for FLAT message:\n");
+ part = mm_context_getpart(ctx, 0);
+ printf("%s", part->body);
+ }
+
+ /* Loop through all MIME parts beginning with 1 */
+ for (i = 1; i < mm_context_countparts(ctx); i++) {
+ char *decoded;
+
+ printf("Printing headers for MIME part %d\n", i);
+
+ /* Get the current MIME entity */
+ part = mm_context_getpart(ctx, i);
+ if (part == NULL) {
+ fprintf(stderr, "Should have %d parts but "
+ "couldn't retrieve part %d",
+ mm_context_countparts(ctx), i);
+ exit(1);
+ }
+
+ /* Print all headers */
+ if (mm_mimepart_headers_start(part, &lastheader) == -1) {
+ printf("Ups no headers\n");
+ }
+ while ((header = mm_mimepart_headers_next(part, &lastheader)) != NULL) {
+ printf("%s: %s\n", header->name, header->value);
+ }
+
+ printf("Part Type: %s\n", mm_content_tostring(part->type));
+
+ /* Print MIME part body */
+ printf("\nPrinting message BODY:\n%s\n", (char *)part->opaque_body);
+ decoded = mm_mimepart_decode(part);
+ if (decoded != NULL) {
+ printf("DECODED:\n%s\n", decoded);
+ free(decoded);
+ }
+ }
+
+ /* Print out all warnings that we might have received */
+ if (mm_context_haswarnings(ctx) > 0) {
+ lastwarning = NULL;
+ fprintf(stderr, "WARNINGS:\n");
+#if 0
+ while ((warning = mm_warning_next(ctx, &lastwarning))
+ != NULL) {
+ fprintf(stderr, " -> %s\n", warning->message);
+ }
+#endif
+ }
+
+ printf("ENVELOPE:\n");
+
+ do {
+ char *env;
+ size_t env_len;
+
+ mm_context_flatten(ctx, &env, &env_len, 0);
+ printf("%s", env);
+
+ } while (0);
+
+ mm_context_free(ctx);
+ ctx = NULL;
+
+#ifdef __HAVE_LEAK_DETECTION
+ MM_leakd_printallocated();
+#endif
+
+ } while (0);
+
+ return 0;
+}
diff --git a/trunk/main/minimime/mm.h b/trunk/main/minimime/mm.h
new file mode 100644
index 000000000..449cd8f72
--- /dev/null
+++ b/trunk/main/minimime/mm.h
@@ -0,0 +1,367 @@
+/*
+ * $Id$
+ *
+ * MiniMIME - a library for handling MIME messages
+ *
+ * Copyright (C) 2003 Jann Fischer <rezine@mistrust.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of the contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _MM_H_INCLUDED
+#define _MM_H_INCLUDED
+
+#include "asterisk.h"
+#include "mm_queue.h"
+#include "mm_mem.h"
+
+#define MM_MIME_LINELEN 998
+#define MM_BASE64_LINELEN 76
+
+TAILQ_HEAD(mm_mimeheaders, mm_mimeheader);
+TAILQ_HEAD(mm_mimeparts, mm_mimepart);
+TAILQ_HEAD(mm_params, mm_param);
+SLIST_HEAD(mm_codecs, mm_codec);
+SLIST_HEAD(mm_warnings, mm_warning);
+
+/*
+ * Parser modes
+ */
+enum mm_parsemodes
+{
+ /** Parse loosely, accept some MIME quirks */
+ MM_PARSE_LOOSE = 0,
+ /** Parse as strict as possible */
+ MM_PARSE_STRICT
+};
+
+/*
+ * Available parser flags
+ */
+enum mm_parseflags
+{
+ MM_PARSE_NONE = (1L << 0),
+ MM_PARSE_STRIPCOMMENTS = (1L << 1)
+};
+
+/*
+ * Enumeration of MIME encodings
+ */
+enum mm_encoding
+{
+ MM_ENCODING_NONE = 0,
+ MM_ENCODING_BASE64,
+ MM_ENCODING_QUOTEDPRINTABLE,
+ MM_ENCODING_UNKNOWN
+};
+
+/*
+ * Message type
+ */
+enum mm_messagetype
+{
+ /** Flat message */
+ MM_MSGTYPE_FLAT = 0,
+ /** Composite message */
+ MM_MSGTYPE_MULTIPART
+};
+
+/*
+ * Enumeration of error categories
+ */
+enum mm_errors
+{
+ MM_ERROR_NONE = 0,
+ MM_ERROR_UNDEF,
+ MM_ERROR_ERRNO,
+ MM_ERROR_PARSE,
+ MM_ERROR_MIME,
+ MM_ERROR_CODEC,
+ MM_ERROR_PROGRAM
+};
+
+enum mm_warning_ids
+{
+ MM_WARN_NONE = 0,
+ MM_WARN_PARSE,
+ MM_WARN_MIME,
+ MM_WARN_CODEC
+};
+
+enum mm_addressfields {
+ MM_ADDR_TO = 0,
+ MM_ADDR_CC,
+ MM_ADDR_BCC,
+ MM_ADDR_FROM,
+ MM_ADDR_SENDER,
+ MM_ADDR_REPLY_TO
+};
+
+enum mm_flatten_flags {
+ MM_FLATTEN_NONE = 0,
+ MM_FLATTEN_SKIPENVELOPE = (1L << 1),
+ MM_FLATTEN_OPAQUE = (1L << 2),
+ MM_FLATTEN_NOPREAMBLE = (1L << 3)
+};
+
+/*
+ * More information about an error
+ */
+struct mm_error_data
+{
+ int error_id;
+ int error_where;
+ int lineno;
+ char error_msg[128];
+};
+
+extern int mm_errno;
+extern struct mm_error_data mm_error;
+
+enum mm_warning_code
+{
+ MM_WARNING_NONE = 0,
+ MM_WARNING_INVHDR,
+};
+
+/*
+ * A parser warning
+ */
+struct mm_warning
+{
+ enum mm_warning_code warning;
+ uint32_t lineno;
+ SLIST_ENTRY(mm_warning) next;
+};
+
+/*
+ * Representation of a MiniMIME codec object
+ */
+struct mm_codec
+{
+ enum mm_encoding id;
+ char *encoding;
+
+ char *(*encoder)(char *, uint32_t);
+ char *(*decoder)(char *);
+
+ SLIST_ENTRY(mm_codec) next;
+};
+
+/*
+ * Representation of a MIME Content-Type parameter
+ */
+struct mm_param
+{
+ char *name;
+ char *value;
+
+ TAILQ_ENTRY(mm_param) next;
+};
+
+/*
+ * Representation of a mail or MIME header field
+ */
+struct mm_mimeheader
+{
+ char *name;
+ char *value;
+
+ struct mm_params params;
+
+ TAILQ_ENTRY(mm_mimeheader) next;
+};
+
+/*
+ * Representation of a MIME Content-Type object
+ */
+struct mm_content
+{
+ char *maintype;
+ char *subtype;
+ char *disposition_type;
+
+ struct mm_params type_params;
+ struct mm_params disposition_params;
+
+ char *encstring;
+ enum mm_encoding encoding;
+};
+
+/*
+ * Representation of a MIME part
+ */
+struct mm_mimepart
+{
+ struct mm_mimeheaders headers;
+
+ size_t opaque_length;
+ char *opaque_body;
+
+ size_t length;
+ char *body;
+
+ struct mm_content *type;
+
+ TAILQ_ENTRY(mm_mimepart) next;
+};
+
+/*
+ * Represantation of a MiniMIME context
+ */
+struct mm_context
+{
+ struct mm_mimeparts parts;
+ enum mm_messagetype messagetype;
+ struct mm_warnings warnings;
+ struct mm_codecs codecs;
+ char *boundary;
+ char *preamble;
+ size_t max_message_size;
+};
+
+typedef struct mm_context MM_CTX;
+typedef struct mm_context mm_ctx_t;
+
+char *mm_unquote(const char *);
+char *mm_uncomment(const char *);
+char *mm_stripchars(char *, char *);
+char *mm_addchars(char *, char *, uint16_t);
+int mm_gendate(char **);
+void mm_striptrailing(char **, const char *);
+int mm_mimeutil_genboundary(char *, size_t, char **);
+
+int mm_library_init(void);
+int mm_library_isinitialized(void);
+
+int mm_parse_mem(MM_CTX *, const char *, int, int);
+int mm_parse_file(MM_CTX *, const char *, int, int);
+int mm_parse_fileptr(MM_CTX *, FILE *, int, int);
+
+MM_CTX *mm_context_new(void);
+void mm_context_free(MM_CTX *);
+int mm_context_attachpart(MM_CTX *, struct mm_mimepart *);
+int mm_context_deletepart(MM_CTX *, int, int);
+int mm_context_countparts(MM_CTX *);
+struct mm_mimepart *mm_context_getpart(MM_CTX *, int);
+int mm_context_iscomposite(MM_CTX *);
+int mm_context_haswarnings(MM_CTX *);
+int mm_context_flatten(MM_CTX *, char **, size_t *, int);
+
+int mm_envelope_getheaders(MM_CTX *, char **, size_t *);
+int mm_envelope_setheader(MM_CTX *, const char *, const char *, ...);
+
+struct mm_mimeheader *mm_mimeheader_new(void);
+void mm_mimeheader_free(struct mm_mimeheader *);
+struct mm_mimeheader *mm_mimeheader_generate(const char *, const char *);
+int mm_mimeheader_uncomment(struct mm_mimeheader *);
+int mm_mimeheader_uncommentbyname(struct mm_mimepart *, const char *);
+int mm_mimeheader_uncommentall(struct mm_mimepart *);
+int mm_mimeheader_tostring(struct mm_mimeheader *);
+char *mm_mimeheader_getparambyname(struct mm_mimeheader *hdr, const char *name);
+int mm_mimeheader_attachparam(struct mm_mimeheader *hdr, struct mm_param *param);
+
+struct mm_mimepart *mm_mimepart_new(void);
+void mm_mimepart_free(struct mm_mimepart *);
+int mm_mimepart_attachheader(struct mm_mimepart *, struct mm_mimeheader *);
+int mm_mimepart_countheaders(struct mm_mimepart *part);
+int mm_mimepart_countheaderbyname(struct mm_mimepart *, const char *);
+struct mm_mimeheader *mm_mimepart_getheaderbyname(struct mm_mimepart *, const char *, int);
+const char *mm_mimepart_getheadervalue(struct mm_mimepart *, const char *, int);
+int mm_mimepart_headers_start(struct mm_mimepart *, struct mm_mimeheader **);
+struct mm_mimeheader *mm_mimepart_headers_next(struct mm_mimepart *, struct mm_mimeheader **);
+char *mm_mimepart_decode(struct mm_mimepart *);
+struct mm_content *mm_mimepart_getcontent(struct mm_mimepart *);
+size_t mm_mimepart_getlength(struct mm_mimepart *);
+char *mm_mimepart_getbody(struct mm_mimepart *, int);
+void mm_mimepart_attachcontenttype(struct mm_mimepart *, struct mm_content *);
+int mm_mimepart_setdefaultcontenttype(struct mm_mimepart *, int);
+int mm_mimepart_flatten(struct mm_mimepart *, char **, size_t *, int);
+struct mm_mimepart *mm_mimepart_fromfile(const char *);
+
+struct mm_content *mm_content_new(void);
+void mm_content_free(struct mm_content *);
+int mm_content_attachtypeparam(struct mm_content *, struct mm_param *);
+int mm_content_attachdispositionparam(struct mm_content *, struct mm_param *);
+struct mm_content *mm_content_parse(const char *, int);
+char *mm_content_gettypeparambyname(struct mm_content *, const char *);
+char *mm_content_getdispositionparambyname(struct mm_content *, const char *);
+struct mm_param *mm_content_gettypeparamobjbyname(struct mm_content *, const char *);
+struct mm_param *mm_content_getdispositionparamobjbyname(struct mm_content *, const char *);
+int mm_content_setmaintype(struct mm_content *, char *, int);
+int mm_content_setsubtype(struct mm_content *, char *, int);
+int mm_content_settype(struct mm_content *, const char *, ...);
+int mm_content_setdispositiontype(struct mm_content *ct, char *value, int copy);
+char *mm_content_getmaintype(struct mm_content *);
+char *mm_content_getsubtype(struct mm_content *);
+char *mm_content_gettype(struct mm_content *);
+char *mm_content_getdispositiontype(struct mm_content *ct);
+int mm_content_iscomposite(struct mm_content *);
+int mm_content_isvalidencoding(const char *);
+int mm_content_setencoding(struct mm_content *, const char *);
+char *mm_content_typeparamstostring(struct mm_content *);
+char *mm_content_dispositionparamstostring(struct mm_content *);
+char *mm_content_tostring(struct mm_content *);
+
+struct mm_param *mm_param_new(void);
+void mm_param_free(struct mm_param *);
+
+char *mm_flatten_mimepart(struct mm_mimepart *);
+char *mm_flatten_context(MM_CTX *);
+
+int mm_codec_isregistered(const char *);
+int mm_codec_hasdecoder(const char *);
+int mm_codec_hasencoder(const char *);
+int mm_codec_register(const char *, char *(*encoder)(char *, uint32_t), char *(*decoder)(char *));
+int mm_codec_unregister(const char *);
+int mm_codec_unregisterall(void);
+void mm_codec_registerdefaultcodecs(void);
+
+char *mm_base64_decode(char *);
+char *mm_base64_encode(char *, uint32_t);
+
+void mm_error_init(void);
+void mm_error_setmsg(const char *, ...);
+void mm_error_setlineno(int lineno);
+char *mm_error_string(void);
+int mm_error_lineno(void);
+
+void mm_warning_add(MM_CTX *, int, const char *, ...);
+struct mm_warning *mm_warning_next(MM_CTX *, struct mm_warning **);
+
+#ifndef HAVE_STRLCPY
+size_t strlcpy(char *, const char *, size_t);
+#endif /* ! HAVE_STRLCPY */
+#ifndef HAVE_STRLCAT
+size_t strlcat(char *, const char *, size_t);
+#endif /* ! HAVE_STRLCAT */
+
+#define MM_ISINIT() do { \
+ assert(mm_library_isinitialized() == 1); \
+} while (0);
+
+#endif /* ! _MM_H_INCLUDED */
diff --git a/trunk/main/minimime/mm_base64.c b/trunk/main/minimime/mm_base64.c
new file mode 100644
index 000000000..ee6dc183b
--- /dev/null
+++ b/trunk/main/minimime/mm_base64.c
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2003 Jann Fischer <jfi@openbsd.de>
+ * All rights reserved.
+ *
+ * XXX: This piece of software is not nearly MIME compatible as it should be.
+ *
+ * This is based on third-party code, see the copyright notice below.
+ *
+ */
+
+/* $Id$ */
+
+/***********************************************************
+ Copyright 1998 by Carnegie Mellon University
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Carnegie Mellon
+University not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.
+
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
+ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+******************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "mm_internal.h"
+
+#define XX 127
+
+static int _mm_base64_decode(char *);
+static char *_mm_base64_encode(char *, uint32_t);
+
+/*
+ * Tables for encoding/decoding base64
+ */
+static const char basis_64[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static const char index_64[256] = {
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,62, XX,XX,XX,63,
+ 52,53,54,55, 56,57,58,59, 60,61,XX,XX, XX,XX,XX,XX,
+ XX, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
+ 15,16,17,18, 19,20,21,22, 23,24,25,XX, XX,XX,XX,XX,
+ XX,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
+ 41,42,43,44, 45,46,47,48, 49,50,51,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+};
+#define CHAR64(c) (index_64[(unsigned char)(c)])
+
+/*
+ * mm_base64_decode()
+ *
+ * Decodes the data pointed to by 'data' from the BASE64 encoding to the data
+ * format it was encoded from. Returns a pointer to a string on success or
+ * NULL on error. The string returned needs to be freed by the caller at some
+ * later point.
+ *
+ */
+char *
+mm_base64_decode(char *data)
+{
+ char *buf;
+
+ assert(data != NULL);
+
+ buf = mm_stripchars(data, "\r\n");
+ assert(buf != NULL);
+
+ _mm_base64_decode(buf);
+ assert(buf != NULL);
+ return(buf);
+}
+
+/*
+ * mm_base64_encode()
+ *
+ * Encodes the data pointed to by 'data', which is of the length specified in
+ * 'len' to the BASE64 format. Returns a pointer to a string containing the
+ * BASE64 encoding, whose lines are broken at the MIME recommended linelength
+ * of 76 characters. If an error occured, returns NULL. The string returned
+ * needs to be freed by the caller at some later point.
+ *
+ */
+char *
+mm_base64_encode(char *data, uint32_t len) {
+ char *buf;
+ char *ret;
+
+ assert(data != NULL);
+
+ buf = _mm_base64_encode(data, len);
+ assert(buf != NULL);
+
+ ret = mm_addchars(buf, "\r\n", MM_BASE64_LINELEN);
+ xfree(buf);
+ assert(ret != NULL);
+ return ret;
+}
+
+/*
+ * Decode in-place the base64 data in 'input'. Returns the length
+ * of the decoded data, or -1 if there was an error.
+ */
+static int
+_mm_base64_decode(char *input)
+{
+ uint32_t len = 0;
+ unsigned char *output = (unsigned char *)input;
+ int c1, c2, c3, c4;
+
+ while (*input) {
+ c1 = *input++;
+ if (CHAR64(c1) == XX) return -1;
+ c2 = *input++;
+ if (CHAR64(c2) == XX) return -1;
+ c3 = *input++;
+ if (c3 != '=' && CHAR64(c3) == XX) return -1;
+ c4 = *input++;
+ if (c4 != '=' && CHAR64(c4) == XX) return -1;
+ *output++ = (CHAR64(c1) << 2) | (CHAR64(c2) >> 4);
+ ++len;
+ if (c3 == '=') break;
+ *output++ = ((CHAR64(c2) << 4) & 0xf0) | (CHAR64(c3) >> 2);
+ ++len;
+ if (c4 == '=') break;
+ *output++ = ((CHAR64(c3) << 6) & 0xc0) | CHAR64(c4);
+ ++len;
+ }
+ *output = 0;
+
+ return len;
+}
+
+/*
+ * Encode the given binary string of length 'len' and return Base64
+ * in a char buffer. It allocates the space for buffer.
+ * caller must free the space.
+ */
+static char *
+_mm_base64_encode(char *data, uint32_t len)
+{
+ char *buf;
+ uint32_t buflen;
+ int c1;
+ int c2;
+ int c3;
+ uint32_t maxbuf;
+
+ buflen = 0;
+
+#ifdef RUBBISH
+ maxbuf = len*4/3 + 1; /* size after expantion */
+#endif
+ maxbuf = len*2 + 20; /* size after expantion */
+
+ buf = (char *)xmalloc(maxbuf);
+
+ while (len && buflen < (maxbuf - 6)) {
+
+ c1 = (unsigned char)*data++;
+ buf[buflen++] = basis_64[c1>>2];
+
+ if (--len == 0) c2 = 0;
+ else c2 = (unsigned char)*data++;
+ buf[buflen++] = basis_64[((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4)];
+
+ if (len == 0) {
+ buf[buflen++] = '=';
+ buf[buflen++] = '=';
+ break;
+ }
+
+ if (--len == 0) c3 = 0;
+ else c3 = (unsigned char)*data++;
+
+ buf[buflen++] = basis_64[((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6)];
+ if (len == 0) {
+ buf[buflen++] = '=';
+
+ break;
+ }
+
+ --len;
+ buf[buflen++] = basis_64[c3 & 0x3F];
+ }
+
+ buf[buflen]=0;
+ return buf;
+}
diff --git a/trunk/main/minimime/mm_codecs.c b/trunk/main/minimime/mm_codecs.c
new file mode 100644
index 000000000..23f94aeba
--- /dev/null
+++ b/trunk/main/minimime/mm_codecs.c
@@ -0,0 +1,250 @@
+/*
+ * $Id$
+ *
+ * MiniMIME - a library for handling MIME messages
+ *
+ * Copyright (C) 2003 Jann Fischer <rezine@mistrust.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of the contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <assert.h>
+
+#include "mm_internal.h"
+#include "mm_util.h"
+
+extern struct mm_codecs codecs;
+
+/** @file mm_codecs.c
+ *
+ * This module contains functions to manipulate MiniMIME codecs
+ *
+ */
+
+/** @defgroup codecs Manipulating MiniMIME codecs */
+
+/** @{
+ * @name Codec manipulation
+ */
+
+/**
+ * Looks up whether a context has an decoder installed for a given encoding
+ *
+ * @param encoding The encoding specifier to look up
+ * @return 1 if a decoder is installed or 0 if not
+ * @ingroup codecs
+ */
+int
+mm_codec_hasdecoder(const char *encoding)
+{
+ struct mm_codec *codec;
+
+ assert(encoding != NULL);
+
+ SLIST_FOREACH(codec, &codecs, next) {
+ assert(codec->encoding != NULL);
+ if (!strcasecmp(codec->encoding, encoding)) {
+ if (codec->decoder != NULL)
+ return 1;
+ else
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Looks up whether a context has an encoder installed for a given encoding
+ *
+ * @param ctx A valid MIME context
+ * @param encoding The encoding specifier to look up
+ * @return 1 if an encoder is installed or 0 if not
+ * @ingroup codecs
+ */
+int
+mm_codec_hasencoder(const char *encoding)
+{
+ struct mm_codec *codec;
+
+ assert(encoding != NULL);
+
+ SLIST_FOREACH(codec, &codecs, next) {
+ assert(codec->encoding != NULL);
+ if (!strcasecmp(codec->encoding, encoding)) {
+ if (codec->encoder != NULL)
+ return 1;
+ else
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Looks up whether a codec for a given encoding is installed to a context
+ *
+ * @param encoding The encoding specifier to look up
+ * @return 1 if a codec was found or 0 if not
+ * @ingroup codecs
+ */
+int
+mm_codec_isregistered(const char *encoding)
+{
+ struct mm_codec *codec;
+
+ assert(encoding != NULL);
+
+ SLIST_FOREACH(codec, &codecs, next) {
+ if (!strcasecmp(codec->encoding, encoding)) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Registers a codec with the MiniMIME library
+ *
+ * @param encoding The encoding specifier for which to register the codec
+ * @param encoder The encoder function for this encoding
+ * @param decoder The decoder function for this encoding
+ * @return 1 if successfull or 0 if not
+ * @ingroup codecs
+ *
+ * This function registers a codec for a given MiniMIME context. The codec
+ * may provide an decoder, an encoder or both (but not none). If there is
+ * a codec already installed for this encoding, the function will puke.
+ */
+int
+mm_codec_register(const char *encoding,
+ char *(*encoder)(char *data, uint32_t i),
+ char *(*decoder)(char *data))
+{
+ struct mm_codec *codec;
+
+ assert(encoding != NULL);
+
+ assert(mm_codec_isregistered(encoding) != 1);
+
+ codec = (struct mm_codec *)xmalloc(sizeof(struct mm_codec));
+
+ codec->encoding = xstrdup(encoding);
+ codec->encoder = encoder;
+ codec->decoder = decoder;
+
+ if (SLIST_EMPTY(&codecs)) {
+ SLIST_INSERT_HEAD(&codecs, codec, next);
+ return 1;
+ } else {
+ struct mm_codec *lcodec, *tcodec;
+ tcodec = NULL;
+ SLIST_FOREACH(lcodec, &codecs, next) {
+ if (lcodec != NULL)
+ tcodec = lcodec;
+ }
+ assert(tcodec != NULL);
+ SLIST_INSERT_AFTER(tcodec, codec, next);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * Unregisters a MiniMIME codec
+ *
+ * @param encoding The encoding specifier which to unregister
+ * @return 0 if unregistered successfully, or -1 if there was no such codec
+ * @ingroup codecs
+ */
+int
+mm_codec_unregister(const char *encoding)
+{
+ struct mm_codec *codec;
+
+ assert(encoding != NULL);
+
+ SLIST_FOREACH(codec, &codecs, next) {
+ if (!strcasecmp(codec->encoding, encoding)) {
+ xfree(codec->encoding);
+ xfree(codec);
+ codec = NULL;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+/**
+ * Unregisters all codecs within a context
+ *
+ * @param ctx A valid MiniMIME context
+ * @return 0 if all codecs were unregistered successfully or -1 if an error
+ * occured.
+ * @note Foobar
+ */
+int
+mm_codec_unregisterall(void)
+{
+ struct mm_codec *codec;
+
+ SLIST_FOREACH(codec, &codecs, next) {
+ if (mm_codec_unregister(codec->encoding) == -1) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Registers the default codecs to a MiniMIME context
+ *
+ * This functions registers the codecs for the following encodings to a
+ * MiniMIME context:
+ *
+ * - Base64
+ * - (TODO:) Quoted-Printable
+ */
+void
+mm_codec_registerdefaultcodecs(void)
+{
+ mm_codec_register("base64", mm_base64_encode, mm_base64_decode);
+}
+
+
+/** @} */
diff --git a/trunk/main/minimime/mm_contenttype.c b/trunk/main/minimime/mm_contenttype.c
new file mode 100644
index 000000000..27fbc0c12
--- /dev/null
+++ b/trunk/main/minimime/mm_contenttype.c
@@ -0,0 +1,759 @@
+/*
+ * $Id$
+ *
+ * MiniMIME - a library for handling MIME messages
+ *
+ * Copyright (C) 2003 Jann Fischer <rezine@mistrust.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of the contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include "mm_internal.h"
+#include "mm_util.h"
+
+/* This file is documented using Doxygen */
+
+/**
+ * @file mm_contenttype.c
+ *
+ * This module contains functions for manipulating Content-Type objects.
+ */
+
+/** @defgroup contenttype Accessing and manipulating Content-Type objects */
+
+struct mm_encoding_mappings {
+ const char *idstring;
+ int type;
+};
+
+static struct mm_encoding_mappings mm_content_enctypes[] = {
+ { "Base64", MM_ENCODING_BASE64 },
+ { "Quoted-Printable", MM_ENCODING_QUOTEDPRINTABLE },
+ { NULL, - 1},
+};
+
+static const char *mm_composite_maintypes[] = {
+ "multipart",
+ "message",
+ NULL,
+};
+
+static const char *mm_composite_encodings[] = {
+ "7bit",
+ "8bit",
+ "binary",
+ NULL,
+};
+
+/** @{
+ * @name Functions for manipulating Content objects
+ */
+
+/**
+ * Creates a new object to hold a Content representation.
+ * The allocated memory must later be freed using mm_content_free()
+ *
+ * @return An object representing a MIME Content-Type
+ * @see mm_content_free
+ * @ingroup contenttype
+ */
+struct mm_content *
+mm_content_new(void)
+{
+ struct mm_content *ct;
+
+ ct = (struct mm_content *)xmalloc(sizeof(struct mm_content));
+
+ ct->maintype = NULL;
+ ct->subtype = NULL;
+
+ TAILQ_INIT(&ct->type_params);
+ TAILQ_INIT(&ct->disposition_params);
+
+ ct->encoding = MM_ENCODING_NONE;
+ ct->encstring = NULL;
+
+ return ct;
+}
+
+/**
+ * Releases all memory associated with an Content object
+ *
+ * @param ct A Content-Type object
+ * @return Nothing
+ * @ingroup contenttype
+ */
+void
+mm_content_free(struct mm_content *ct)
+{
+ struct mm_param *param;
+
+ assert(ct != NULL);
+
+ if (ct->maintype != NULL) {
+ xfree(ct->maintype);
+ ct->maintype = NULL;
+ }
+ if (ct->subtype != NULL) {
+ xfree(ct->subtype);
+ ct->subtype = NULL;
+ }
+ if (ct->encstring != NULL) {
+ xfree(ct->encstring);
+ ct->encstring = NULL;
+ }
+
+ TAILQ_FOREACH(param, &ct->type_params, next) {
+ TAILQ_REMOVE(&ct->type_params, param, next);
+ mm_param_free(param);
+ }
+ TAILQ_FOREACH(param, &ct->disposition_params, next) {
+ TAILQ_REMOVE(&ct->disposition_params, param, next);
+ mm_param_free(param);
+ }
+
+ xfree(ct);
+}
+
+/**
+ * Attaches a content-type parameter to a Content object
+ *
+ * @param ct The target Content object
+ * @param param The Content-Type parameter which to attach
+ * @return 0 on success and -1 on failure
+ * @ingroup contenttype
+ */
+int
+mm_content_attachtypeparam(struct mm_content *ct, struct mm_param *param)
+{
+ assert(ct != NULL);
+ assert(param != NULL);
+
+ if (TAILQ_EMPTY(&ct->type_params)) {
+ TAILQ_INSERT_HEAD(&ct->type_params, param, next);
+ } else {
+ TAILQ_INSERT_TAIL(&ct->type_params, param, next);
+ }
+
+ return 0;
+}
+
+
+/**
+ * Attaches a content-disposition parameter to a Content-Disposition object
+ *
+ * @param ct The target Content object
+ * @param param The Content-Type parameter which to attach
+ * @return 0 on success and -1 on failure
+ * @ingroup contenttype
+ */
+int
+mm_content_attachdispositionparam(struct mm_content *ct, struct mm_param *param)
+{
+ assert(ct != NULL);
+ assert(param != NULL);
+
+ if (TAILQ_EMPTY(&ct->disposition_params)) {
+ TAILQ_INSERT_HEAD(&ct->disposition_params, param, next);
+ } else {
+ TAILQ_INSERT_TAIL(&ct->disposition_params, param, next);
+ }
+
+ return 0;
+}
+
+
+/**
+ * Gets a Content-Type parameter value from a Content object.
+ *
+ * @param ct the Content object
+ * @param name the name of the parameter to retrieve
+ * @return The value of the parameter on success or a NULL pointer on failure
+ * @ingroup contenttype
+ */
+char *
+mm_content_gettypeparambyname(struct mm_content *ct, const char *name)
+{
+ struct mm_param *param;
+
+ assert(ct != NULL);
+
+ TAILQ_FOREACH(param, &ct->type_params, next) {
+ if (!strcasecmp(param->name, name)) {
+ return param->value;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * Gets a Content-Disposition parameter value from a Content object.
+ *
+ * @param ct the Content object
+ * @param name the name of the parameter to retrieve
+ * @return The value of the parameter on success or a NULL pointer on failure
+ * @ingroup contenttype
+ */
+char *
+mm_content_getdispositionparambyname(struct mm_content *ct, const char *name)
+{
+ struct mm_param *param;
+
+ assert(ct != NULL);
+
+ TAILQ_FOREACH(param, &ct->disposition_params, next) {
+ if (!strcasecmp(param->name, name)) {
+ return param->value;
+ }
+ }
+
+ return NULL;
+}
+
+struct mm_param *
+mm_content_gettypeparamobjbyname(struct mm_content *ct, const char *name)
+{
+ struct mm_param *param;
+
+ assert(ct != NULL);
+
+ TAILQ_FOREACH(param, &ct->type_params, next) {
+ if (!strcasecmp(param->name, name)) {
+ return param;
+ }
+ }
+
+ return NULL;
+}
+
+struct mm_param *
+mm_content_getdispositionparamobjbyname(struct mm_content *ct, const char *name)
+{
+ struct mm_param *param;
+
+ assert(ct != NULL);
+
+ TAILQ_FOREACH(param, &ct->disposition_params, next) {
+ if (!strcasecmp(param->name, name)) {
+ return param;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * Sets the MIME main Content-Type for a MIME Content object
+ *
+ * @param ct The MIME Content object
+ * @param value The value which to set the main type to
+ * @param copy Whether to make a copy of the value (original value must be
+ * freed afterwards to prevent memory leaks).
+ */
+int
+mm_content_setmaintype(struct mm_content *ct, char *value, int copy)
+{
+ assert(ct != NULL);
+ assert(value != NULL);
+
+ if (copy) {
+ /**
+ * @bug The xfree() call could lead to undesirable results.
+ * Do we really need it?
+ */
+ if (ct->maintype != NULL) {
+ xfree(ct->maintype);
+ }
+ ct->maintype = xstrdup(value);
+ } else {
+ ct->maintype = value;
+ }
+
+ return 0;
+}
+
+/**
+ * Retrieves the main MIME Content-Type stored in a Content object
+ *
+ * @param ct A valid Content object
+ * @returns A pointer to the string representing the main type
+ * @ingroup contenttype
+ */
+char *
+mm_content_getmaintype(struct mm_content *ct)
+{
+ assert(ct != NULL);
+ assert(ct->maintype != NULL);
+
+ return ct->maintype;
+}
+
+/**
+ * Sets the MIME Content-Disposition type for a MIME Content object
+ *
+ * @param ct The MIME Content object
+ * @param value The value which to set the main type to
+ * @param copy Whether to make a copy of the value (original value must be
+ * freed afterwards to prevent memory leaks).
+ */
+int
+mm_content_setdispositiontype(struct mm_content *ct, char *value, int copy)
+{
+ assert(ct != NULL);
+ assert(value != NULL);
+
+ if (copy) {
+ /**
+ * @bug The xfree() call could lead to undesirable results.
+ * Do we really need it?
+ */
+ if (ct->disposition_type != NULL) {
+ xfree(ct->disposition_type);
+ }
+ ct->disposition_type = xstrdup(value);
+ } else {
+ ct->disposition_type = value;
+ }
+
+ return 0;
+}
+
+/**
+ * Retrieves the Content-Disposition MIME type stored in a Content object
+ *
+ * @param ct A valid Content-Type object
+ * @returns A pointer to the string representing the main type
+ * @ingroup contenttype
+ */
+char *
+mm_content_getdispositiontype(struct mm_content *ct)
+{
+ assert(ct != NULL);
+ assert(ct->disposition_type != NULL);
+
+ return ct->disposition_type;
+}
+
+/**
+ * Retrieves the sub MIME Content-Type stored in a Content object
+ *
+ * @param ct A valid Content-Type object
+ * @return A pointer to the string holding the current sub MIME type
+ * @ingroup contenttype
+ */
+char *
+mm_content_getsubtype(struct mm_content *ct)
+{
+ assert(ct != NULL);
+ assert(ct->subtype != NULL);
+
+ return ct->subtype;
+}
+
+/**
+ * Sets the MIME sub Content-Type for a MIME Content object
+ *
+ * @param ct The MIME Content-Type object
+ * @param value The value which to set the sub type to
+ * @param copy Whether to make a copy of the value (original value must be
+ * freed afterwards to prevent memory leaks).
+ */
+int
+mm_content_setsubtype(struct mm_content *ct, char *value, int copy)
+{
+ assert(ct != NULL);
+ assert(value != NULL);
+
+ if (copy) {
+ /**
+ * @bug The xfree() call could lead to undesirable results.
+ * Do we really need it?
+ */
+ if (ct->subtype != NULL) {
+ xfree(ct->subtype);
+ }
+ ct->subtype = xstrdup(value);
+ } else {
+ ct->subtype = value;
+ }
+
+ return 0;
+}
+
+int
+mm_content_settype(struct mm_content *ct, const char *fmt, ...)
+{
+ char *maint, *subt;
+ char buf[512], *parse;
+ va_list ap;
+
+ mm_errno = MM_ERROR_NONE;
+
+ va_start(ap, fmt);
+ /* Make sure no truncation occurs */
+ if (vsnprintf(buf, sizeof buf, fmt, ap) > sizeof buf) {
+ mm_errno = MM_ERROR_ERRNO;
+ mm_error_setmsg("Input string too long");
+ return -1;
+ }
+ va_end(ap);
+
+ parse = buf;
+ maint = strsep(&parse, "/");
+ if (maint == NULL) {
+ mm_errno = MM_ERROR_PARSE;
+ mm_error_setmsg("Invalid type specifier: %s", buf);
+ return -1;
+ }
+ ct->maintype = xstrdup(maint);
+
+ subt = strsep(&parse, "");
+ if (subt == NULL) {
+ mm_errno = MM_ERROR_PARSE;
+ mm_error_setmsg("Invalid type specifier: %s", buf);
+ return -1;
+ }
+ ct->subtype = xstrdup(subt);
+
+ return 0;
+}
+
+/**
+ * Checks whether the Content-Type represents a composite message or not
+ *
+ * @param ct A valid Content-Type object
+ * @returns 1 if the Content-Type object represents a composite message or
+ * 0 if not.
+ */
+int
+mm_content_iscomposite(struct mm_content *ct)
+{
+ int i;
+
+ for (i = 0; mm_composite_maintypes[i] != NULL; i++) {
+ if (!strcasecmp(ct->maintype, mm_composite_maintypes[i])) {
+ return 1;
+ }
+ }
+
+ /* Not found */
+ return 0;
+}
+
+/**
+ * Verifies whether a string represents a valid encoding or not.
+ *
+ * @param encoding The string to verify
+ * @return 1 if the encoding string is valid or 0 if not
+ *
+ */
+int
+mm_content_isvalidencoding(const char *encoding)
+{
+ int i;
+
+ for (i = 0; mm_composite_encodings[i] != NULL; i++) {
+ if (!strcasecmp(encoding, mm_composite_encodings[i])) {
+ return 1;
+ }
+ }
+
+ /* Not found */
+ return 0;
+}
+
+/**
+ * Set the encoding of a MIME entitity according to a mapping table
+ *
+ * @param ct A valid content type object
+ * @param encoding A string representing the content encoding
+ * @return 0 if successfull or -1 if not (i.e. unknown content encoding)
+ */
+int
+mm_content_setencoding(struct mm_content *ct, const char *encoding)
+{
+ int i;
+
+ assert(ct != NULL);
+ assert(encoding != NULL);
+
+ for (i = 0; mm_content_enctypes[i].idstring != NULL; i++) {
+ if (!strcasecmp(mm_content_enctypes[i].idstring, encoding)) {
+ ct->encoding = mm_content_enctypes[i].type;
+ ct->encstring = xstrdup(encoding);
+ return 0;
+ }
+ }
+
+ /* If we didn't find a mapping, set the encoding to unknown */
+ ct->encoding = MM_ENCODING_UNKNOWN;
+ ct->encstring = NULL;
+ return 1;
+}
+
+/**
+ * Gets the numerical ID of a content encoding identifier
+ *
+ * @param ct A valid Content Type object
+ * @param encoding A string representing the content encoding identifier
+ * @return The numerical ID of the content encoding
+ */
+#if 0
+int
+mm_content_getencoding(struct mm_content *ct, const char *encoding)
+{
+ int i;
+
+ assert(ct != NULL);
+
+ for (i = 0; mm_content_enctypes[i].idstring != NULL; i++) {
+ if (!strcasecmp(mm_content_enctypes[i].idstring, encoding)) {
+ return mm_content_enctypes[i].type;
+ }
+ }
+
+ /* Not found */
+ return MM_ENCODING_UNKNOWN;
+}
+#endif
+
+/**
+ * Constructs a MIME conform string of Content-Type parameters.
+ *
+ * @param ct A valid Content Type object
+ * @return A pointer to a string representing the Content-Type parameters
+ * in MIME terminology, or NULL if either the Content-Type object
+ * is invalid, has no parameters or no memory could be allocated.
+ *
+ * This function constructs a MIME conform string including all the parameters
+ * associated with the given Content-Type object. It should NOT be used if
+ * you need an opaque copy of the current MIME part (e.g. for PGP purposes).
+ */
+char *
+mm_content_typeparamstostring(struct mm_content *ct)
+{
+ size_t size, new_size;
+ struct mm_param *param;
+ char *param_string, *cur_param;
+ char *buf;
+
+ size = 1;
+ param_string = NULL;
+ cur_param = NULL;
+
+ param_string = (char *) xmalloc(size);
+ *param_string = '\0';
+
+ /* Concatenate all Content-Type parameters attached to the current
+ * Content-Type object to a single string.
+ */
+ TAILQ_FOREACH(param, &ct->type_params, next) {
+ if (asprintf(&cur_param, "; %s=\"%s\"", param->name,
+ param->value) == -1) {
+ goto cleanup;
+ }
+
+ new_size = size + strlen(cur_param) + 1;
+
+ if (new_size < 0 || new_size > 1000) {
+ size = 0;
+ goto cleanup;
+ }
+
+ buf = (char *) xrealloc(param_string, new_size);
+ if (buf == NULL) {
+ size = 0;
+ goto cleanup;
+ }
+
+ param_string = buf;
+ size = new_size;
+ strlcat(param_string, cur_param, size);
+
+ xfree(cur_param);
+ cur_param = NULL;
+ }
+
+ return param_string;
+
+cleanup:
+ if (param_string != NULL) {
+ xfree(param_string);
+ param_string = NULL;
+ }
+ if (cur_param != NULL) {
+ xfree(cur_param);
+ cur_param = NULL;
+ }
+ return NULL;
+}
+
+/**
+ * Constructs a MIME conformant string of Content-Disposition parameters.
+ *
+ * @param ct A valid Content object
+ * @return A pointer to a string representing the Content-Disposition parameters
+ * in MIME terminology, or NULL if either the Content object
+ * is invalid, has no Disposition parameters or no memory could be allocated.
+ *
+ * This function constructs a MIME conforming string including all the parameters
+ * associated with the given Content-Disposition object. It should NOT be used if
+ * you need an opaque copy of the current MIME part (e.g. for PGP purposes).
+ */
+char *
+mm_content_dispositionparamstostring(struct mm_content *ct)
+{
+ size_t size, new_size;
+ struct mm_param *param;
+ char *param_string, *cur_param;
+ char *buf;
+
+ size = 1;
+ param_string = NULL;
+ cur_param = NULL;
+
+ param_string = (char *) xmalloc(size);
+ *param_string = '\0';
+
+ /* Concatenate all Content-Disposition parameters attached to the current
+ * Content object to a single string.
+ */
+ TAILQ_FOREACH(param, &ct->disposition_params, next) {
+ if (asprintf(&cur_param, "; %s=\"%s\"", param->name,
+ param->value) == -1) {
+ goto cleanup;
+ }
+
+ new_size = size + strlen(cur_param) + 1;
+
+ if (new_size < 0 || new_size > 1000) {
+ size = 0;
+ goto cleanup;
+ }
+
+ buf = (char *) xrealloc(param_string, new_size);
+ if (buf == NULL) {
+ size = 0;
+ goto cleanup;
+ }
+
+ param_string = buf;
+ size = new_size;
+ strlcat(param_string, cur_param, size);
+
+ xfree(cur_param);
+ cur_param = NULL;
+ }
+
+ return param_string;
+
+cleanup:
+ if (param_string != NULL) {
+ xfree(param_string);
+ param_string = NULL;
+ }
+ if (cur_param != NULL) {
+ xfree(cur_param);
+ cur_param = NULL;
+ }
+ return NULL;
+}
+
+/**
+ * Creates a Content-Type header according to the object given
+ *
+ * @param ct A valid Content-Type object
+ *
+ */
+char *
+mm_content_tostring(struct mm_content *ct)
+{
+ char *paramstring;
+ char *buf;
+ char *headerstring;
+ size_t size;
+
+ paramstring = NULL;
+ headerstring = NULL;
+ buf = NULL;
+
+ if (ct == NULL) {
+ return NULL;
+ }
+ if (ct->maintype == NULL || ct->subtype == NULL) {
+ return NULL;
+ }
+
+ size = strlen(ct->maintype) + strlen(ct->subtype) + 2;
+ headerstring = (char *)xmalloc(size);
+ snprintf(headerstring, size, "%s/%s", ct->maintype, ct->subtype);
+
+ paramstring = mm_content_typeparamstostring(ct);
+ if (paramstring == NULL) {
+ goto cleanup;
+ }
+
+ size += strlen(paramstring) + strlen("Content-Type: ") + 1;
+ buf = (char *)malloc(size);
+ if (buf == NULL) {
+ goto cleanup;
+ }
+
+ snprintf(buf, size, "Content-Type: %s%s", headerstring, paramstring);
+
+ xfree(headerstring);
+ xfree(paramstring);
+
+ headerstring = NULL;
+ paramstring = NULL;
+
+ return buf;
+
+cleanup:
+ if (paramstring != NULL) {
+ xfree(paramstring);
+ paramstring = NULL;
+ }
+ if (headerstring != NULL) {
+ xfree(headerstring);
+ headerstring = NULL;
+ }
+ if (buf != NULL) {
+ xfree(buf);
+ buf = NULL;
+ }
+ return NULL;
+}
+
+/** @} */
diff --git a/trunk/main/minimime/mm_context.c b/trunk/main/minimime/mm_context.c
new file mode 100644
index 000000000..d67c1e6b3
--- /dev/null
+++ b/trunk/main/minimime/mm_context.c
@@ -0,0 +1,614 @@
+/*
+ * $Id$
+ *
+ * MiniMIME - a library for handling MIME messages
+ *
+ * Copyright (C) 2003 Jann Fischer <rezine@mistrust.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of the contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <assert.h>
+
+#include "mm_internal.h"
+
+/** @file mm_context.c
+ *
+ * Modules for manipulating MiniMIME contexts
+ */
+
+/** @defgroup context Accessing and manipulating MIME contexts
+ *
+ * Each message in MiniMIME is represented by a so called ``context''. A
+ * context holds all necessary information given about a MIME message, such
+ * as the envelope, all MIME parts etc.
+ */
+
+/** @{
+ * @name Manipulating MiniMIME contexts
+ */
+
+/**
+ * Creates a new MiniMIME context object.
+ *
+ * @return a new MiniMIME context object
+ * @see mm_context_free
+ *
+ * This function creates a new MiniMIME context, which will hold a message.
+ * The memory needed is allocated dynamically and should later be free'd
+ * using mm_context_free().
+ *
+ * Before a context can be created, the MiniMIME library needs to be
+ * initialized properly using mm_library_init().
+ *
+ */
+MM_CTX *
+mm_context_new(void)
+{
+ MM_CTX *ctx;
+
+ MM_ISINIT();
+
+ ctx = (MM_CTX *)xmalloc(sizeof(MM_CTX));
+ ctx->messagetype = MM_MSGTYPE_FLAT; /* This is the default */
+ ctx->boundary = NULL;
+ ctx->preamble = xstrdup("This is a message in MIME format, generated "
+ "by MiniMIME 0.1");
+
+ TAILQ_INIT(&ctx->parts);
+ SLIST_INIT(&ctx->warnings);
+
+ return ctx;
+}
+
+/**
+ * Releases a MiniMIME context object
+ *
+ * @param ctx A valid MiniMIME context
+ * @see mm_context_new
+ *
+ * This function releases all memory associated with MiniMIME context object
+ * that was created using mm_context_new(). It will also release all memory
+ * used for the MIME parts attached, and their specific properties (such as
+ * Content-Type information, headers, and the body data).
+ */
+void
+mm_context_free(MM_CTX *ctx)
+{
+ struct mm_mimepart *part;
+ struct mm_warning *warning, *nxt;
+
+ assert(ctx != NULL);
+
+ TAILQ_FOREACH(part, &ctx->parts, next) {
+ TAILQ_REMOVE(&ctx->parts, part, next);
+ mm_mimepart_free(part);
+ }
+
+ if (ctx->boundary != NULL) {
+ xfree(ctx->boundary);
+ ctx->boundary = NULL;
+ }
+
+ if (ctx->preamble != NULL) {
+ xfree(ctx->preamble);
+ ctx->preamble = NULL;
+ }
+
+ for (warning = SLIST_FIRST(&ctx->warnings);
+ warning != SLIST_END(&ctx->warnings);
+ warning = nxt) {
+ nxt = SLIST_NEXT(warning, next);
+ SLIST_REMOVE(&ctx->warnings, warning, mm_warning, next);
+ xfree(warning);
+ warning = NULL;
+ }
+
+ xfree(ctx);
+ ctx = NULL;
+}
+
+/**
+ * Attaches a MIME part object to a MiniMIME context.
+ *
+ * @param ctx the MiniMIME context
+ * @param part the MIME part object to attach
+ * @return 0 on success or -1 on failure. Sets mm_errno on failure.
+ *
+ * This function attaches a MIME part to a context, appending it to the end
+ * of the message.
+ *
+ * The MIME part should be initialized before attaching it using
+ * mm_mimepart_new().
+ */
+int
+mm_context_attachpart(MM_CTX *ctx, struct mm_mimepart *part)
+{
+ assert(ctx != NULL);
+ assert(part != NULL);
+
+ if (TAILQ_EMPTY(&ctx->parts)) {
+ TAILQ_INSERT_HEAD(&ctx->parts, part, next);
+ } else {
+ TAILQ_INSERT_TAIL(&ctx->parts, part, next);
+ }
+
+ return 0;
+}
+
+/**
+ * Attaches a MIME part object to a MiniMIME context at a given position
+ *
+ * @param ctx A valid MiniMIME context
+ * @param part The MIME part object to attach
+ * @param pos After which part to attach the object
+ * @return 0 on success or -1 if the given position is invalid
+ * @see mm_context_attachpart
+ *
+ * This function attaches a MIME part object after a given position in the
+ * specified context. If the position is invalid (out of range), the part
+ * will not get attached to the message and the function returns -1. If
+ * the index was in range, the MIME part will get attached after the MIME
+ * part at the given position, moving any possible following MIME parts one
+ * down the hierarchy.
+ */
+#if 0
+int
+mm_context_attachpart_after(MM_CTX *ctx, struct mm_mimepart *part, int pos)
+{
+ struct mm_mimepart *p;
+ int where;
+
+ where = 0;
+ p = NULL;
+
+ TAILQ_FOREACH(part, &ctx->parts, next) {
+ if (where == pos) {
+ p = part;
+ }
+ }
+
+ if (p == NULL) {
+ return(-1);
+ }
+
+ TAILQ_INSERT_AFTER(&ctx->parts, p, part, next);
+
+ return(0);
+}
+#endif
+
+/**
+ * Deletes a MIME part object from a MiniMIME context
+ *
+ * @param ctx A valid MiniMIME context object
+ * @param which The number of the MIME part object to delete
+ * @param freemem Whether to free the memory associated with the MIME part
+ * object
+ * @return 0 on success or -1 on failure. Sets mm_errno on failure.
+ *
+ * This function deletes a MIME part from a given context. The MIME part to
+ * delete is specified as numerical index by the parameter ``which''. If the
+ * parameter ``freemem'' is set to anything greater than 0, the memory that
+ * is associated will be free'd by using mm_mimepart_free(), otherwise the
+ * memory is left untouched (if you still have a pointer to the MIME part
+ * around).
+ */
+int
+mm_context_deletepart(MM_CTX *ctx, int which, int freemem)
+{
+ struct mm_mimepart *part;
+ int cur;
+
+ assert(ctx != NULL);
+ assert(which >= 0);
+
+ cur = 0;
+
+ TAILQ_FOREACH(part, &ctx->parts, next) {
+ if (cur == which) {
+ TAILQ_REMOVE(&ctx->parts, part, next);
+ if (freemem)
+ mm_mimepart_free(part);
+ return 0;
+ }
+ cur++;
+ }
+
+ return -1;
+}
+
+/**
+ * Counts the number of attached MIME part objects in a given MiniMIME context
+ *
+ * @param ctx The MiniMIME context
+ * @returns The number of attached MIME part objects
+ */
+int
+mm_context_countparts(MM_CTX *ctx)
+{
+ int count;
+ struct mm_mimepart *part;
+
+ assert(ctx != NULL);
+
+ count = 0;
+
+ if (TAILQ_EMPTY(&ctx->parts)) {
+ return 0;
+ } else {
+ TAILQ_FOREACH(part, &ctx->parts, next) {
+ count++;
+ }
+ }
+
+ assert(count > -1);
+
+ return count;
+}
+
+/**
+ * Gets a specified MIME part object from a MimeMIME context
+ *
+ * @param ctx The MiniMIME context
+ * @param which The number of the MIME part object to retrieve
+ * @returns The requested MIME part object on success or a NULL pointer if
+ * there is no such part.
+ */
+struct mm_mimepart *
+mm_context_getpart(MM_CTX *ctx, int which)
+{
+ struct mm_mimepart *part;
+ int cur;
+
+ assert(ctx != NULL);
+
+ cur = 0;
+
+ TAILQ_FOREACH(part, &ctx->parts, next) {
+ if (cur == which) {
+ return part;
+ }
+ cur++;
+ }
+
+ return NULL;
+}
+
+/**
+ * Checks whether a given context represents a composite (multipart) message
+ *
+ * @param ctx A valid MiniMIME context object
+ * @return 1 if the context is a composite message or 0 if it's flat
+ *
+ */
+int
+mm_context_iscomposite(MM_CTX *ctx)
+{
+ if (ctx->messagetype == MM_MSGTYPE_MULTIPART) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/**
+ * Checks whether there are any warnings associated with a given context
+ *
+ * @param ctx A valid MiniMIME context
+ * @return 1 if there are warnings associated with the context, otherwise 0
+ */
+int
+mm_context_haswarnings(MM_CTX *ctx)
+{
+ if (SLIST_EMPTY(&ctx->warnings)) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+/**
+ * Generates a generic boundary string for a given context
+ *
+ * @param ctx A valid MiniMIME context
+ * @return 0 on success or -1 on failure
+ *
+ * This function generates a default boundary string for the given context.
+ * If there is already a boundary for the context, the memory will be free()'d.
+ */
+#if 0
+int
+mm_context_generateboundary(MM_CTX *ctx)
+{
+ char *boundary;
+ struct mm_mimepart *part;
+ struct mm_param *param;
+
+ if (mm_mimeutil_genboundary("++MiniMIME++", 20, &boundary) == -1) {
+ return(-1);
+ }
+
+ if (ctx->boundary != NULL) {
+ xfree(ctx->boundary);
+ ctx->boundary = NULL;
+ }
+
+ /* If we already have an envelope, make sure that we also justify the
+ * "boundary" parameter of the envelope.
+ */
+ part = mm_context_getpart(ctx, 0);
+ if (part == NULL) {
+ return(0);
+ }
+ if (part->type != NULL) {
+ param = mm_content_gettypeparamobjbyname(part->type, "boundary");
+ if (param == NULL) {
+ param = mm_param_new();
+ param->name = xstrdup("boundary");
+ param->value = xstrdup(boundary);
+ mm_content_attachtypeparam(part->type, param);
+ } else {
+ if (param->value != NULL) {
+ xfree(param->value);
+ param->value = NULL;
+ }
+ param->value = xstrdup(boundary);
+ }
+ }
+
+ ctx->boundary = boundary;
+ return(0);
+}
+#endif
+
+/**
+ * Sets a preamble for the given MiniMIME context
+ *
+ * @param ctx A valid MiniMIME context
+ * @param preamble The preamble to set
+ * @return 0 on success or -1 on failure
+ *
+ * This function sets the MIME preamble (the text between the end of envelope
+ * headers and the beginning of the first MIME part) for a given context
+ * object. If preamble is a NULL-pointer then the preamble will be deleted,
+ * and the currently associated memory will be free automagically.
+ */
+#if 0
+int
+mm_context_setpreamble(MM_CTX *ctx, char *preamble)
+{
+ if (ctx == NULL)
+ return(-1);
+
+ if (preamble == NULL) {
+ if (ctx->preamble != NULL) {
+ xfree(ctx->preamble);
+ }
+ ctx->preamble = NULL;
+ } else {
+ ctx->preamble = xstrdup(preamble);
+ }
+ return(0);
+}
+#endif
+
+#if 0
+char *
+mm_context_getpreamble(MM_CTX *ctx)
+{
+ if (ctx == NULL)
+ return(NULL);
+
+ return(ctx->preamble);
+}
+#endif
+
+/**
+ * Creates an ASCII message of the specified context
+ *
+ * @param ctx A valid MiniMIME context object
+ * @param flat Where to store the message
+ * @param flags Flags that affect the flattening process
+ *
+ * This function ``flattens'' a MiniMIME context, that is, it creates an ASCII
+ * represantation of the message the context contains. The flags can be a
+ * bitwise combination of the following constants:
+ *
+ * - MM_FLATTEN_OPAQUE : use opaque MIME parts when flattening
+ * - MM_FLATTEN_SKIPENVELOPE : do not flatten the envelope part
+ *
+ * Great care is taken to not produce invalid MIME output.
+ */
+#if 0
+int
+mm_context_flatten(MM_CTX *ctx, char **flat, size_t *length, int flags)
+{
+ struct mm_mimepart *part;
+ char *message;
+ char *flatpart;
+ char *buf;
+ char *envelope_headers;
+ size_t message_size;
+ size_t tmp_size;
+ char envelope;
+
+ mm_errno = MM_ERROR_NONE;
+ envelope = 1;
+
+ message = NULL;
+ message_size = 0;
+
+ if (ctx->boundary == NULL) {
+ if (mm_context_iscomposite(ctx)) {
+ mm_context_generateboundary(ctx);
+ }
+ }
+
+ TAILQ_FOREACH(part, &ctx->parts, next) {
+ if (envelope) {
+ if (flags & MM_FLATTEN_SKIPENVELOPE) {
+ envelope = 0;
+ if ((message = (char *) malloc(1)) == NULL) {
+ mm_errno = MM_ERROR_ERRNO;
+ goto cleanup;
+ }
+ *message = '\0';
+ continue;
+ }
+
+ if (part->type == NULL && mm_context_countparts(ctx) > 1) {
+ if (mm_mimepart_setdefaultcontenttype(part, 1)
+ == -1) {
+ goto cleanup;
+ }
+ if (mm_context_generateboundary(ctx) == -1) {
+ goto cleanup;
+ }
+ ctx->messagetype = MM_MSGTYPE_MULTIPART;
+ }
+
+ if (mm_envelope_getheaders(ctx, &envelope_headers,
+ &tmp_size) == -1) {
+ return -1;
+ }
+
+ message = envelope_headers;
+ message_size = tmp_size;
+ envelope = 0;
+
+ if (ctx->preamble != NULL
+ && mm_context_iscomposite(ctx)
+ && !(flags & MM_FLATTEN_NOPREAMBLE)) {
+ tmp_size += strlen(ctx->preamble)
+ + (strlen("\r\n") * 2);
+ buf = (char *)xrealloc(message, tmp_size);
+ if (buf == NULL) {
+ goto cleanup;
+ }
+ message_size += tmp_size;
+ message = buf;
+ strlcat(message, "\r\n", message_size);
+ strlcat(message, ctx->preamble, message_size);
+ strlcat(message, "\r\n", message_size);
+ }
+ } else {
+ /* Enforce Content-Type if none exist */
+ if (part->type == NULL) {
+ if (mm_mimepart_setdefaultcontenttype(part, 0)
+ == -1) {
+ goto cleanup;
+ }
+ }
+
+ /* Append a boundary if necessary */
+ if (ctx->boundary != NULL) {
+ tmp_size = strlen(ctx->boundary) +
+ (strlen("\r\n") * 2) + strlen("--");
+
+ if (tmp_size < 1) {
+ return(-1);
+ }
+ if (message_size + tmp_size < 1) {
+ return(-1);
+ }
+
+ buf = (char *)xrealloc(message, message_size
+ + tmp_size);
+ if (buf == NULL) {
+ goto cleanup;
+ }
+ message_size += tmp_size;
+ message = buf;
+ strlcat(message, "\r\n", message_size);
+ strlcat(message, "--", message_size);
+ strlcat(message, ctx->boundary, message_size);
+ strlcat(message, "\r\n", message_size);
+ }
+
+ if (mm_mimepart_flatten(part, &flatpart, &tmp_size,
+ (flags & MM_FLATTEN_OPAQUE)) == -1) {
+ goto cleanup;
+ }
+
+ if (tmp_size < 1) {
+ goto cleanup;
+ }
+
+ buf = (char *) xrealloc(message, message_size
+ + tmp_size);
+ if (buf == NULL) {
+ goto cleanup;
+ }
+
+ message_size += tmp_size;
+ message = buf;
+
+ strlcat(message, flatpart, message_size);
+ xfree(flatpart);
+ flatpart = NULL;
+ }
+ }
+
+ /* Append end boundary */
+ if (ctx->boundary != NULL && mm_context_iscomposite(ctx)) {
+ tmp_size = strlen(ctx->boundary) + (strlen("\r\n") * 2)
+ + (strlen("--") * 2);
+ buf = (char *)xrealloc(message, message_size + tmp_size);
+ if (buf == NULL) {
+ goto cleanup;
+ }
+
+ message_size += tmp_size;
+ message = buf;
+ if (message[strlen(message)-1] != 13)
+ strlcat(message, "\r", message_size);
+ strlcat(message, "\n", message_size);
+ strlcat(message, "--", message_size);
+ strlcat(message, ctx->boundary, message_size);
+ strlcat(message, "--", message_size);
+ strlcat(message, "\r\n", message_size);
+ }
+
+ *flat = message;
+ *length = message_size;
+
+ return 0;
+
+cleanup:
+ if (message != NULL) {
+ xfree(message);
+ message = NULL;
+ }
+ return -1;
+}
+#endif
+
+/** @} */
diff --git a/trunk/main/minimime/mm_envelope.c b/trunk/main/minimime/mm_envelope.c
new file mode 100644
index 000000000..7400d3de2
--- /dev/null
+++ b/trunk/main/minimime/mm_envelope.c
@@ -0,0 +1,271 @@
+/*
+ * $Id$
+ *
+ * MiniMIME - a library for handling MIME messages
+ *
+ * Copyright (C) 2003 Jann Fischer <rezine@mistrust.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of the contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include "mm_internal.h"
+#include "mm_util.h"
+
+/** @file mm_envelope.c
+ *
+ * This module contains functions for accessing a message's envelope. This
+ * are mainly wrapper functions for easy access.
+ */
+
+/** @defgroup envelope Accessing and manipulating a message's envelope
+ */
+
+/** @{
+ * @name Accessing and manipulating a message's envelope
+ */
+
+/**
+ * Gets an ASCII representation of all envelope headers
+ *
+ * @param ctx A valid MiniMIME context
+ * @param result Where to store the resulting ASCII headers
+ * @param length Where to store the length of the result
+ * @returns 0 on success or -1 on failure.
+ * @note Sets mm_errno on failure
+ *
+ * This is mainly a convinience function. It constructs an ASCII representation
+ * from all of the message's envelope headers and stores the result in headers.
+ * Memory is allocated dynamically, and the total length of the result is
+ * stored in length. This function takes care that the output is MIME conform,
+ * and folds long lines according to the MIME standard at position 78 of the
+ * string. It also nicely formats all MIME related header fields, such as
+ * the Content-Type header.
+ *
+ * Since the memory needed to store the result is allocated dynamically, one
+ * should take care of freeing it again when it's not needed anymore. If an
+ * error occurs, *result will be set to NULL, *length will be set to zero
+ * and mm_errno will be set to a reasonable value.
+ *
+ */
+int
+mm_envelope_getheaders(MM_CTX *ctx, char **result, size_t *length)
+{
+ struct mm_mimepart *part;
+ struct mm_mimeheader *hdr;
+ char *buf, *hdrbuf;
+ size_t headers_length, tmp_length;
+
+ headers_length = 1;
+ buf = NULL;
+
+ part = mm_context_getpart(ctx, 0);
+ if (part == NULL) {
+ return -1;
+ }
+
+ /* Initialize our buffer */
+ if ((buf = (char *)xmalloc(headers_length)) == NULL) {
+ mm_errno = MM_ERROR_ERRNO;
+ goto cleanup;
+ }
+ *buf = '\0';
+
+ /* Store each envelope header */
+ TAILQ_FOREACH(hdr, &part->headers, next) {
+ tmp_length = strlen(hdr->name) + strlen(hdr->value)
+ + strlen(": \r\n");
+ hdrbuf = (char *) xrealloc(buf, headers_length + tmp_length);
+ if (hdrbuf == NULL) {
+ mm_errno = MM_ERROR_ERRNO;
+ goto cleanup;
+ }
+
+ headers_length += tmp_length;
+ buf = hdrbuf;
+
+ strlcat(buf, hdr->name, headers_length);
+ strlcat(buf, ": ", headers_length);
+ strlcat(buf, hdr->value, headers_length);
+ strlcat(buf, "\r\n", headers_length);
+ }
+
+ /* Construct and store MIME headers */
+ if (part->type != NULL) {
+ char *typebuf;
+ typebuf = mm_content_tostring(part->type);
+ if (typebuf == NULL) {
+ goto cleanup;
+ }
+ tmp_length = strlen(typebuf) + strlen("\r\n");
+
+ hdrbuf = (char *) xrealloc(buf, headers_length + tmp_length);
+ if (hdrbuf == NULL) {
+ mm_errno = MM_ERROR_ERRNO;
+ goto cleanup;
+ }
+
+ headers_length += tmp_length;
+ buf = hdrbuf;
+
+ strlcat(buf, typebuf, headers_length);
+ strlcat(buf, "\r\n", headers_length);
+ }
+
+ *result = buf;
+ *length = headers_length;
+
+ return 0;
+
+cleanup:
+ if (buf != NULL) {
+ xfree(buf);
+ buf = NULL;
+ }
+ *result = NULL;
+ *length = 0;
+ return -1;
+}
+
+/**
+ * Sets a header field in the envelope
+ *
+ * @param ctx A valid MiniMIME context
+ * @param name The name of the header field to set
+ * @param fmt A format string specifying the value of the header field
+ * @return 0 on success or -1 on failure
+ *
+ * This function generates a new MIME header and attaches it to the first
+ * MIME part (the envelope) found in the given context. If no part is
+ * attached already, the function will return an error. The function will
+ * store a copy of ``name'' as the header's name field, and dynamically
+ * allocate the memory needed to build the format string.
+ */
+int
+mm_envelope_setheader(MM_CTX *ctx, const char *name, const char *fmt, ...)
+{
+ va_list ap;
+ char *buf;
+ struct mm_mimeheader *hdr;
+ struct mm_mimepart *part;
+
+ part = mm_context_getpart(ctx, 0);
+ if (part == NULL) {
+ return(-1);
+ }
+
+ hdr = mm_mimeheader_new();
+ if (hdr == NULL) {
+ return(-1);
+ }
+
+ hdr->name = xstrdup(name);
+
+ va_start(ap, fmt);
+ if (vasprintf(&buf, fmt, ap) == -1) {
+ goto cleanup;
+ }
+ va_end(ap);
+
+ hdr->value = buf;
+
+ if (mm_mimepart_attachheader(part, hdr) == -1) {
+ goto cleanup;
+ }
+
+ return(0);
+
+cleanup:
+ if (hdr != NULL) {
+ if (hdr->name != NULL) {
+ xfree(hdr->name);
+ hdr->name = NULL;
+ }
+ if (hdr->value != NULL) {
+ xfree(hdr->value);
+ hdr->value = NULL;
+ }
+ }
+ return(-1);
+}
+
+/**
+ * Gets the list of recipients for a MIME message
+ *
+ * @param ctx A valid MiniMIME context
+ * @param result Where to store the result
+ * @param length Where to store the length of the result
+ * @returns 0 on success or -1 on error
+ * @note Sets mm_errno on error
+ *
+ * This functions gets the list of recipients for a given MIME message. It
+ * does so by concatenating the "From" and "Cc" header fields, and storing
+ * the results in recipients. The memory needed to store the result is
+ * allocated dynamically, and the total length of the result is stored in
+ * length.
+ *
+ * One should take care to free() the result once it's not needed anymore.
+ */
+#if 0
+int
+mm_envelope_getrecipients(MM_CTX *ctx, char **result, size_t *length)
+{
+ struct mm_mimepart *part;
+ struct mm_mimeheader *to, *cc;
+ size_t recipients_length = 0;
+
+ part = mm_context_getpart(ctx, 0);
+ if (part == NULL) {
+ return -1;
+ }
+
+ to = mm_mimepart_getheaderbyname(part, "From", 0);
+ cc = mm_mimepart_getheaderbyname(part, "Cc", 0);
+
+ if (to == NULL || cc == NULL) {
+ *result = NULL;
+ *length = 0;
+ return -1;
+ }
+
+ if (to != NULL) {
+ recipients_length += strlen(to->value);
+ }
+ if (cc != NULL) {
+ recipients_length += strlen(cc->value);
+ }
+
+ return 0;
+}
+#endif
+
+/** @} */
diff --git a/trunk/main/minimime/mm_error.c b/trunk/main/minimime/mm_error.c
new file mode 100644
index 000000000..5b27bf724
--- /dev/null
+++ b/trunk/main/minimime/mm_error.c
@@ -0,0 +1,123 @@
+/*
+ * $Id$
+ *
+ * MiniMIME - a library for handling MIME messages
+ *
+ * Copyright (C) 2003 Jann Fischer <rezine@mistrust.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of the contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+
+#include "mm_internal.h"
+#include "mm_util.h"
+
+/** @file mm_error.c
+ *
+ * This module contains functions for MiniMIME error information/manipulation
+ */
+
+/** @defgroup error MiniMIME error functions */
+
+/**
+ * Initializes the global error object
+ *
+ * @ingroup error
+ *
+ * This function initializes the global error object mm_error. This must be
+ * done when the library is initialized, and is automatically called from
+ * mm_init_library().
+ */
+void
+mm_error_init(void)
+{
+ mm_error.error_id = 0;
+ mm_error.error_where = 0;
+ mm_error.lineno = 0;
+ memset(&mm_error.error_msg, '\0', sizeof(mm_error.error_msg));
+}
+
+/**
+ * Sets a descriptive error message
+ *
+ * @param fmt The error message as format string
+ * @ingroup error
+ *
+ * This function is called from the various MiniMIME modules in case an
+ * error occured. Should never be called by the user.
+ */
+void
+mm_error_setmsg(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(mm_error.error_msg, sizeof(mm_error.error_msg), fmt, ap);
+ va_end(ap);
+
+}
+
+void
+mm_error_setlineno(int lineno)
+{
+ mm_error.lineno = lineno;
+}
+
+/**
+ * Retrieves the current error message
+ *
+ * @return The currently set error message
+ * @ingroup error
+ *
+ * This function can be used to retrieve a descriptive error message for the
+ * current error, much like strerror() function of libc. When this function
+ * is called without an error being set, it returns the string "No error".
+ * The string returned does not need to be freed, since it is not dynamically
+ * allocated by the library.
+ */
+char *
+mm_error_string(void)
+{
+ if (mm_errno != MM_ERROR_ERRNO && mm_error.error_msg[0] == '\0') {
+ return "No error";
+ } else if (mm_errno == MM_ERROR_ERRNO) {
+ return strerror(errno);
+ } else {
+ return mm_error.error_msg;
+ }
+}
+
+int
+mm_error_lineno(void)
+{
+ return mm_error.lineno;
+}
diff --git a/trunk/main/minimime/mm_header.c b/trunk/main/minimime/mm_header.c
new file mode 100644
index 000000000..827c19cac
--- /dev/null
+++ b/trunk/main/minimime/mm_header.c
@@ -0,0 +1,213 @@
+/*
+ * $Id$
+ *
+ * MiniMIME - a library for handling MIME messages
+ *
+ * Copyright (C) 2003 Jann Fischer <rezine@mistrust.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of the contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include "mm_internal.h"
+#include "mm_util.h"
+
+/** @file mm_header.c
+ *
+ * This module contains functions for manipulating MIME headers
+ */
+
+/**
+ * Creates a new MIME header object
+ *
+ * @return A new and initialized MIME header object
+ * @see mm_mimeheader_free
+ *
+ * This function creates and initializes a new MIME header object, which must
+ * later be freed using mm_mimeheader_free()
+ */
+struct mm_mimeheader *
+mm_mimeheader_new(void)
+{
+ struct mm_mimeheader *header;
+
+ header = (struct mm_mimeheader *)xmalloc(sizeof(struct mm_mimeheader));
+
+ header->name = NULL;
+ header->value = NULL;
+ TAILQ_INIT(&header->params);
+
+ return header;
+}
+
+/**
+ * Frees a MIME header object
+ *
+ * @param header The MIME header object which to free
+ */
+void
+mm_mimeheader_free(struct mm_mimeheader *header)
+{
+ struct mm_param *param;
+ assert(header != NULL);
+
+ if (header->name != NULL) {
+ xfree(header->name);
+ header->name = NULL;
+ }
+ if (header->value != NULL) {
+ xfree(header->value);
+ header->value = NULL;
+ }
+
+ TAILQ_FOREACH(param, &header->params, next) {
+ TAILQ_REMOVE(&header->params, param, next);
+ mm_param_free(param);
+ }
+xfree(header);
+ header = NULL;
+}
+
+/**
+ * Creates a new MIME header, but does no checks whatsoever (create as-is)
+ */
+struct mm_mimeheader *
+mm_mimeheader_generate(const char *name, const char *value)
+{
+ struct mm_mimeheader *header;
+
+ header = mm_mimeheader_new();
+
+ header->name = xstrdup(name);
+ header->value = xstrdup(value);
+
+ return header;
+}
+
+/**
+ * Attaches a parameter to a MimeHeader object
+ *
+ * @param hdr The target MimeHeader object
+ * @param param The parameter to attach
+ * @return 0 on success and -1 on failure
+ * @ingroup mimeheader
+ */
+int
+mm_mimeheader_attachparam(struct mm_mimeheader *hdr, struct mm_param *param)
+{
+ assert(hdr != NULL);
+ assert(param != NULL);
+
+ if (TAILQ_EMPTY(&hdr->params)) {
+ TAILQ_INSERT_HEAD(&hdr->params, param, next);
+ } else {
+ TAILQ_INSERT_TAIL(&hdr->params, param, next);
+ }
+
+ return 0;
+}
+
+
+/**
+ * Gets a parameter value from a MimeHeader object.
+ *
+ * @param hdr the MimeHeader object
+ * @param name the name of the parameter to retrieve
+ * @return The value of the parameter on success or a NULL pointer on failure
+ * @ingroup mimeheader
+ */
+char *
+mm_mimeheader_getparambyname(struct mm_mimeheader *hdr, const char *name)
+{
+ struct mm_param *param;
+
+ assert(hdr != NULL);
+
+ TAILQ_FOREACH(param, &hdr->params, next) {
+ if (!strcasecmp(param->name, name)) {
+ return param->value;
+ }
+ }
+
+ return NULL;
+}
+
+int
+mm_mimeheader_uncomment(struct mm_mimeheader *header)
+{
+ char *new;
+
+ assert(header != NULL);
+ assert(header->name != NULL);
+ assert(header->value != NULL);
+
+ new = mm_uncomment(header->value);
+ if (new == NULL)
+ return -1;
+
+ xfree(header->value);
+ header->value = new;
+
+ return 0;
+}
+
+int
+mm_mimeheader_uncommentbyname(struct mm_mimepart *part, const char *name)
+{
+ struct mm_mimeheader *header;
+
+ TAILQ_FOREACH(header, &part->headers, next) {
+ if (!strcasecmp(header->name, name)) {
+ return mm_mimeheader_uncomment(header);
+ }
+ }
+
+ /* Not found */
+ return -1;
+}
+
+int
+mm_mimeheader_uncommentall(struct mm_mimepart *part)
+{
+ struct mm_mimeheader *header;
+ int ret, r;
+
+ ret = 0;
+
+ TAILQ_FOREACH(header, &part->headers, next) {
+ if ((r = mm_mimeheader_uncomment(header)) == -1) {
+ ret = -1;
+ }
+ }
+
+ return ret;
+}
diff --git a/trunk/main/minimime/mm_init.c b/trunk/main/minimime/mm_init.c
new file mode 100644
index 000000000..2ec37db6b
--- /dev/null
+++ b/trunk/main/minimime/mm_init.c
@@ -0,0 +1,65 @@
+/*
+ * $Id$
+ *
+ * MiniMIME - a library for handling MIME messages
+ *
+ * Copyright (C) 2003 Jann Fischer <rezine@mistrust.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of the contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "mm_internal.h"
+
+int mm_errno;
+struct mm_error_data mm_error;
+static int mm_initialized;
+struct mm_codecs codecs;
+
+int
+mm_library_init(void)
+{
+ assert(mm_initialized != 1);
+
+ mm_errno = MM_ERROR_NONE;
+ mm_initialized = 1;
+
+ SLIST_INIT(&codecs);
+
+ mm_error_init();
+
+ return 0;
+}
+
+int
+mm_library_isinitialized(void)
+{
+ return mm_initialized;
+}
diff --git a/trunk/main/minimime/mm_internal.h b/trunk/main/minimime/mm_internal.h
new file mode 100644
index 000000000..155591487
--- /dev/null
+++ b/trunk/main/minimime/mm_internal.h
@@ -0,0 +1,65 @@
+/*
+ * $Id$
+ *
+ * MiniMIME - a library for handling MIME messages
+ *
+ * Copyright (C) 2003 Jann Fischer <rezine@mistrust.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of the contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @file mm_internal.h
+ * Data definitions for MiniMIME
+ */
+#ifndef _MM_INTERNAL_H_INCLUDED
+#define _MM_INTERNAL_H_INCLUDED
+
+#include "mm.h"
+
+#define debugp(m, ...) do { \
+ fprintf(stderr, "%s:%d:: ", __FILE__, __LINE__); \
+ fprintf(stderr, m, ## __VA_ARGS__); \
+ fprintf(stderr, "\n"); \
+ fflush(stderr); \
+} while (0);
+
+/**
+ * @{
+ * @name Utility functions
+ */
+#ifndef __HAVE_LEAK_DETECTION
+void *xmalloc(size_t);
+void *xrealloc(void *, size_t);
+void xfree(void *);
+char *xstrdup(const char *);
+#endif
+
+char *xstrsep(char **, const char *);
+
+/* THIS FILE IS INTENTIONALLY LEFT BLANK */
+
+#endif /* ! _MM_INTERNAL_H_INCLUDED */
diff --git a/trunk/main/minimime/mm_mem.c b/trunk/main/minimime/mm_mem.c
new file mode 100644
index 000000000..6c915e564
--- /dev/null
+++ b/trunk/main/minimime/mm_mem.c
@@ -0,0 +1,170 @@
+/*
+ * $Id$
+ *
+ * MiniMIME - a library for handling MIME messages
+ *
+ * Copyright (C) 2003 Jann Fischer <rezine@mistrust.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of the contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <sys/types.h>
+#include <sys/param.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
+
+#include "mm_internal.h"
+
+#ifdef __HAVE_LEAK_DETECTION
+# include "mm_mem.h"
+
+static struct MM_chunks chunks;
+
+void *
+MM_malloc(size_t size, char *filename, int line)
+{
+ struct MM_mem_chunk *chunk;
+ void *pointer;
+
+ pointer = malloc(size);
+ if (pointer == NULL)
+ fdprintf(stderr, "INFO: malloc");
+
+ chunk = (struct MM_mem_chunk *)malloc(sizeof(struct MM_mem_chunk));
+ if (chunk == NULL)
+ fdprintf(stderr, "INFO: malloc");
+
+ chunk->address = pointer;
+ chunk->size = size;
+ chunk->filename = filename;
+ chunk->line = line;
+
+ TAILQ_INSERT_TAIL(&chunks, chunk, next);
+
+ return pointer;
+}
+
+char *
+MM_strdup(const char *s, char *filename, int line)
+{
+ char *r;
+
+ r = (char *)MM_malloc(strlen(s)+1, filename, line);
+ strlcpy(r, s, strlen(s) + 1);
+ if (strlen(r) != strlen(s)) {
+ debugp("%d:%d", strlen(s), strlen(r));
+ }
+ return r;
+
+}
+
+void *
+MM_realloc(void *p, size_t new_size, char *filename, int line)
+{
+ void *r;
+ void *a;
+ struct MM_mem_chunk *chunk;
+ struct MM_mem_chunk *last;
+
+ a = p;
+ chunk = NULL;
+ last = NULL;
+
+ assert(new_size > 0);
+
+ TAILQ_FOREACH(chunk, &chunks, next) {
+ if (chunk->address == p) {
+ last = chunk;
+ }
+ }
+
+ if (last == NULL) {
+ debugp("MM_realloc: did not find chunk at %p (%s:%d) "
+ ", creating new", p, filename, line);
+ return MM_malloc(new_size, filename, line);
+ }
+
+ r = realloc(p, new_size);
+ if (r == NULL)
+ return NULL;
+
+ last->address = r;
+ last->size = new_size;
+ last->filename = filename;
+ last->line = line;
+
+ return r;
+}
+
+void
+MM_free(void *pointer, char *filename, int line, char *name)
+{
+ struct MM_mem_chunk *chunk, *nxt;
+
+ for (chunk = TAILQ_FIRST(&chunks); chunk != TAILQ_END(&chunks);
+ chunk = nxt) {
+ nxt = TAILQ_NEXT(&chunks, next);
+ if (chunk->address == pointer) {
+ TAILQ_REMOVE(&chunks, chunk, next);
+ free(chunk->address);
+ free(chunk);
+ return;
+ }
+ }
+
+ debugp("FREE: did not find storage %s (at %p), %s:%d", name, pointer,
+ filename, line);
+}
+
+void
+MM_leakd_flush(void)
+{
+ debugp("flushing memory informations");
+ while (!TAILQ_EMPTY(&chunks))
+ SLIST_REMOVE_HEAD(&chunks, next);
+}
+
+void
+MM_leakd_printallocated(void)
+{
+ struct MM_mem_chunk *chunk;
+ debugp("printing dynamic memory allocations");
+ TAILQ_FOREACH(chunk, &chunks, next) {
+ debugp(" chunk: %p (alloc'ed at %s:%d, size %d)\n",
+ chunk->address, chunk->filename, chunk->line, chunk->size);
+ }
+}
+
+void
+MM_leakd_init(void)
+{
+ TAILQ_INIT(&chunks);
+}
+
+#endif /* !__HAVE_LEAK_DETECTOR */
diff --git a/trunk/main/minimime/mm_mem.h b/trunk/main/minimime/mm_mem.h
new file mode 100644
index 000000000..34840d033
--- /dev/null
+++ b/trunk/main/minimime/mm_mem.h
@@ -0,0 +1,32 @@
+#ifndef __MEM_H
+#define __MEM_H
+
+#ifdef __HAVE_LEAK_DETECTION
+
+#define NAMEOF(v) #v
+#define xmalloc(x) MM_malloc(x, __FILE__, __LINE__)
+#define xfree(x) MM_free(x, __FILE__, __LINE__, NAMEOF(x))
+#define xstrdup(x) MM_strdup(x, __FILE__, __LINE__)
+#define xrealloc(x, y) MM_realloc(x, y, __FILE__, __LINE__)
+
+TAILQ_HEAD(MM_chunks, MM_mem_chunk);
+
+struct MM_mem_chunk {
+ void *address;
+ const char *filename;
+ uint32_t line;
+ size_t size;
+
+ TAILQ_ENTRY(MM_mem_chunk) next;
+};
+
+void *MM_malloc(size_t, char *, int);
+void *MM_realloc(void *, size_t, char *, int);
+void MM_free(void *, char *, int, char *);
+char *MM_strdup(const char *, char *, int);
+void MM_leakd_init(void);
+void MM_leakd_printallocated(void);
+void MM_leakd_flush(void);
+
+#endif /* __HAVE_LEAK_DETECTION */
+#endif /* ! HAVE_MEM_H */
diff --git a/trunk/main/minimime/mm_mimepart.c b/trunk/main/minimime/mm_mimepart.c
new file mode 100644
index 000000000..631debae0
--- /dev/null
+++ b/trunk/main/minimime/mm_mimepart.c
@@ -0,0 +1,659 @@
+/*
+ * $Id$
+ *
+ * MiniMIME - a library for handling MIME messages
+ *
+ * Copyright (C) 2003 Jann Fischer <rezine@mistrust.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of the contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include "mm_internal.h"
+
+/** @file mm_mimepart.c
+ *
+ * This module contains functions for manipulating MIME header objects.
+ */
+
+/** @defgroup mimepart Accessing and manipulating MIME parts
+ *
+ * MIME parts, also called entities, represent the structure of a MIME
+ * message. ``Normal'' internet messages have only a single part, and
+ * are called ``flat'' messages. Multipart messages have more then one
+ * part, and each MIME part can have it's own subset of headers.
+ *
+ * Provided here are functions to easily access all informations from
+ * a MIME part, including their specific headers and bodies.
+ */
+
+/** @{
+ * @name Creating and destroying MIME parts
+ */
+
+/**
+ * Allocates memory for a new mm_mimepart structure and initializes it.
+ *
+ * @return A pointer to a struct of type mm_mimeheader or NULL on failure
+ * @see mm_mimepart_free
+ * @note The memory must be freed by using mm_mimepart_free() later on.
+ */
+struct mm_mimepart *
+mm_mimepart_new(void)
+{
+ struct mm_mimepart *part;
+
+ part = (struct mm_mimepart *)xmalloc(sizeof(struct mm_mimepart));
+
+ TAILQ_INIT(&part->headers);
+
+ part->opaque_length = 0;
+ part->opaque_body = NULL;
+
+ part->length = 0;
+ part->body = NULL;
+
+ part->type = NULL;
+
+ return part;
+}
+
+/**
+ * Creates a MIME part from a file
+ *
+ * @param filename The name of the file to create the MIME part from
+ * @return A pointer to a new MIME part object
+ *
+ * This function creates a new MIME part object from a file. The object should
+ * be freed using mm_mimepart_free() later on. This function does NOT set the
+ * Content-Type and neither does any encoding work.
+ */
+struct mm_mimepart *
+mm_mimepart_fromfile(const char *filename)
+{
+ int fd;
+ char *data;
+ size_t r;
+ struct stat st;
+ struct mm_mimepart *part;
+
+ mm_errno = MM_ERROR_NONE;
+
+ if ((fd = open(filename, O_RDONLY)) == -1) {
+ mm_errno = MM_ERROR_ERRNO;
+ return NULL;
+ }
+
+ if ((stat(filename, &st)) == -1) {
+ mm_errno = MM_ERROR_ERRNO;
+ close(fd);
+ return NULL;
+ }
+
+ data = xmalloc(st.st_size);
+ r = read(fd, data, st.st_size);
+ if (r != st.st_size) {
+ mm_errno = MM_ERROR_ERRNO;
+ close(fd);
+ return(NULL);
+ }
+
+ data[r] = '\0';
+ close(fd);
+
+ part = mm_mimepart_new();
+ part->length = r;
+ part->body = data;
+
+ return part;
+}
+
+
+/**
+ * Frees all memory allocated by a mm_mimepart object.
+ *
+ * @param part A pointer to an allocated mm_mimepart object
+ * @see mm_mimepart_new
+ */
+void
+mm_mimepart_free(struct mm_mimepart *part)
+{
+ struct mm_mimeheader *header;
+
+ assert(part != NULL);
+
+ TAILQ_FOREACH(header, &part->headers, next) {
+ mm_mimeheader_free(header);
+ TAILQ_REMOVE(&part->headers, header, next);
+ }
+
+ if (part->opaque_body != NULL) {
+ xfree(part->opaque_body);
+ part->opaque_body = NULL;
+ part->body = NULL;
+ } else if (part->body != NULL) {
+ xfree(part->body);
+ part->body = NULL;
+ }
+
+ if (part->type != NULL) {
+ mm_content_free(part->type);
+ part->type = NULL;
+ }
+
+ xfree(part);
+ part = NULL;
+}
+
+/** @} */
+
+/** @{
+ * @name Accessing the MIME part's mail header
+ */
+
+/**
+ * Attaches a mm_mimeheader object to a MIME part
+ *
+ * @param part A valid MIME part object
+ * @param header A valid MIME header object
+ * @return 0 if successfull or -1 if the header could not be attached
+ */
+int
+mm_mimepart_attachheader(struct mm_mimepart *part, struct mm_mimeheader *header)
+{
+ assert(part != NULL);
+ assert(header != NULL);
+
+ if (TAILQ_EMPTY(&part->headers)) {
+ TAILQ_INSERT_HEAD(&part->headers, header, next);
+ } else {
+ TAILQ_INSERT_TAIL(&part->headers, header, next);
+ }
+
+ return(0);
+}
+
+/**
+ * Retrieves the number of MIME headers available in a MIME part
+ *
+ * @param part A valid MIME part object
+ * @return The number of MIME headers within the MIME part
+ */
+int
+mm_mimepart_countheaders(struct mm_mimepart *part)
+{
+ int found;
+ struct mm_mimeheader *header;
+
+ assert(part != NULL);
+
+ found = 0;
+
+ TAILQ_FOREACH(header, &part->headers, next) {
+ found++;
+ }
+
+ return found;
+}
+
+/**
+ * Retrieves the number of MIME headers with a given name in a MIME part
+ *
+ * @param part A valid MIME part object
+ * @param name The name of the MIME header which to count for
+ * @return The number of MIME headers within the MIME part
+ */
+int
+mm_mimepart_countheaderbyname(struct mm_mimepart *part, const char *name)
+{
+ int found;
+ struct mm_mimeheader *header;
+
+ assert(part != NULL);
+
+ found = 0;
+
+ TAILQ_FOREACH(header, &part->headers, next) {
+ if (strcasecmp(header->name, name) == 0) {
+ found++;
+ }
+ }
+
+ return found;
+}
+
+/**
+ * Get a MIME header object from a MIME part
+ *
+ * @param part A valid MIME part object
+ * @param name The name of the MIME header which to retrieve
+ * @param idx Which header field to get (in case of multiple headers of the
+ * same name).
+ * @return A pointer to the requested MIME header on success, or NULL if there
+ * either isn't a header with the requested name or idx is out of
+ * range.
+ */
+struct mm_mimeheader *
+mm_mimepart_getheaderbyname(struct mm_mimepart *part, const char *name, int idx)
+{
+ struct mm_mimeheader *header;
+ int curidx;
+
+ curidx = 0;
+
+ TAILQ_FOREACH(header, &part->headers, next) {
+ if (!strcasecmp(header->name, name)) {
+ if (curidx == idx)
+ return header;
+ else
+ curidx++;
+ }
+ }
+
+ /* Not found */
+ return NULL;
+}
+
+/**
+ * Gets the value of a MIME header object
+ *
+ * @param part A valid MIME part object
+ * @param name The name of the header field to get the value from
+ * @param idx The index of the header field to get, in case there are multiple
+ * headers with the same name.
+ * @return A pointer to the requested value on success, or NULL if there either
+ * isn't a header with the requested name or idx is out of range.
+ *
+ */
+const char *
+mm_mimepart_getheadervalue(struct mm_mimepart *part, const char *name, int idx)
+{
+ struct mm_mimeheader *header;
+
+ header = mm_mimepart_getheaderbyname(part, name, idx);
+ if (header == NULL)
+ return NULL;
+ else
+ return header->value;
+}
+
+/**
+ * Initializes a header loop for a given MIME part
+ *
+ * @param part A valid MIME part object
+ * @param id The address of a MIME header object (to allow reentrance)
+ * @return 0 on success or -1 on failure
+ * @see mm_mimepart_headers_next
+ *
+ * Looping through headers can be done in the following way:
+ *
+ * @code
+ * struct mm_mimeheader *header, *lheader;
+ *
+ * mm_mimepart_headers_start(part, &lheader);
+ *
+ * while ((header = mm_mimepart_headers_next(part, &lheader)) != NULL) {
+ * printf("%s: %s\n", header->name, header->value);
+ * }
+ *
+ * @endcode
+ *
+ * For convienience, the macro mm_mimepart_headers_foreach() can be used to
+ * loop through headers in a one-shot manner.
+ */
+int
+mm_mimepart_headers_start(struct mm_mimepart *part, struct mm_mimeheader **id)
+{
+ assert(part != NULL);
+
+ if (TAILQ_EMPTY(&part->headers)) {
+ return -1;
+ }
+ *id = NULL;
+ return 0;
+}
+
+/**
+ * Returns the next MIME header of a given MIME part object
+ *
+ * @param part A valid MIME part object
+ * @param id A previously initialized MIME header object
+ * @return A pointer to the MIME header object or NULL if end of headers was
+ * reached.
+ * @see mm_mimepart_headers_start
+ */
+struct mm_mimeheader *
+mm_mimepart_headers_next(struct mm_mimepart *part, struct mm_mimeheader **id)
+{
+ struct mm_mimeheader *header;
+
+ assert(part != NULL);
+
+ if (*id == NULL) {
+ header = TAILQ_FIRST(&part->headers);
+ } else {
+ header = TAILQ_NEXT(*id, next);
+ }
+ *id = header;
+
+ return header;
+}
+
+/** @} */
+
+/** @{
+ * @name Accessing and manipulating the MIME part's body
+ */
+
+/**
+ * Gets the pointer to the MIME part's body data
+ *
+ * @param part A valid MIME part object
+ * @param opaque Whether to get the opaque part or not
+ * @return A pointer to the MIME part's body
+ * @see mm_mimepart_setbody
+ *
+ */
+char *
+mm_mimepart_getbody(struct mm_mimepart *part, int opaque)
+{
+ assert(part != NULL);
+
+ if (opaque)
+ return part->opaque_body;
+ else
+ return part->body;
+}
+
+/**
+ * Sets the MIME part's body data
+ *
+ * @param part A valid MIME part object
+ * @param data A pointer to the data which to set
+ * @see mm_mimepart_getbody
+ *
+ * This functions sets the body data for a given MIME part. The string pointed
+ * to by data must be NUL-terminated. The data is copied into the MIME part's
+ * body, and thus, the memory pointed to by data can be freed after the
+ * operation.
+ */
+#if 0
+void
+mm_mimepart_setbody(struct mm_mimepart *part, const char *data, int opaque)
+{
+ assert(part != NULL);
+ assert(data != NULL);
+
+ if (opaque) {
+ part->opaque_body = xstrdup(data);
+ part->body = part->opaque_body;
+ } else {
+ part->body = xstrdup(data);
+ }
+ part->length = strlen(data);
+}
+#endif
+
+/**
+ * Gets the length of a given MIME part object
+ *
+ * @param part A valid MIME part object
+ * @returns The size of the part's body in byte.
+ *
+ * This function returns the total length of the given MIME part's body. The
+ * length does not include the headers of the MIME parts. If the function
+ * returns 0, no body part is set currently.
+ */
+size_t
+mm_mimepart_getlength(struct mm_mimepart *part)
+{
+ assert(part != NULL);
+
+ return part->length;
+}
+
+
+/**
+ * Decodes a MIME part according to it's encoding using MiniMIME codecs
+ *
+ * @param A valid MIME part object
+ * @return 0 if the MIME part could be successfully decoded or -1 if not
+ * @note Sets mm_errno on error
+ *
+ * This function decodes the body of a MIME part with a registered decoder
+ * according to it's Content-Transfer-Encoding header field.
+ */
+char *
+mm_mimepart_decode(struct mm_mimepart *part)
+{
+ extern struct mm_codecs codecs;
+ struct mm_codec *codec;
+ void *decoded;
+
+ assert(part != NULL);
+ assert(part->type != NULL);
+
+ decoded = NULL;
+
+ /* No encoding associated */
+ if (part->type->encstring == NULL)
+ return NULL;
+
+ /* Loop through codecs and find a suitable one */
+ SLIST_FOREACH(codec, &codecs, next) {
+ if (!strcasecmp(part->type->encstring, codec->encoding)) {
+ decoded = codec->decoder((char *)part->body);
+ break;
+ }
+ }
+
+ return decoded;
+}
+
+/**
+ * Creates an ASCII representation of the given MIME part
+ *
+ * @param part A valid MIME part object
+ * @param result Where to store the result
+ * @param length Where to store the length of the result
+ * @param opaque Whether to use the opaque MIME part
+ * @returtn 0 on success or -1 on error.
+ * @see mm_context_flatten
+ *
+ * This function creates an ASCII representation of a given MIME part. It will
+ * dynamically allocate the memory needed and stores the result in the memory
+ * region pointed to by result. The length of the result will be stored in
+ * length. If opaque is set to 1, mm_mimepart_flatten will store an opaque
+ * version of the MIME part in result, which means no headers will be created
+ * or sanitized. This is particulary useful if the part is digitally signed by
+ * e.g. PGP, and the signature spans the header fields of the part in question.
+ *
+ */
+int
+mm_mimepart_flatten(struct mm_mimepart *part, char **result, size_t *length,
+ int opaque)
+{
+ size_t part_length;
+ char *buf;
+ char *ct_hdr;
+
+ *result = NULL;
+ *length = 0;
+ buf = NULL;
+ ct_hdr = NULL;
+ part_length = 0;
+
+ if (opaque && part->opaque_body != NULL) {
+ part_length = strlen(part->opaque_body);
+ *result = xstrdup(part->opaque_body);
+ *length = part_length;
+ return(0);
+ } else {
+ if (part->type == NULL) {
+ return(-1);
+ }
+
+ ct_hdr = mm_content_tostring(part->type);
+ if (ct_hdr == NULL) {
+ return(-1);
+ }
+
+ part_length += strlen(ct_hdr) + 2;
+ part_length += strlen("\r\n") * 2;
+ part_length += strlen(part->body);
+
+ if (part_length < 0) {
+ goto cleanup;
+ }
+
+ buf = (char *) xmalloc(part_length);
+ if (buf == NULL) {
+ goto cleanup;
+ }
+
+ snprintf(buf, part_length,
+ "%s\r\n\r\n%s\r\n",
+ ct_hdr,
+ part->body);
+
+ xfree(ct_hdr);
+ ct_hdr = NULL;
+
+ *result = buf;
+ *length = part_length;
+ }
+
+ return(0);
+
+cleanup:
+ if (ct_hdr != NULL) {
+ xfree(ct_hdr);
+ ct_hdr = NULL;
+ }
+ if (buf != NULL) {
+ xfree(buf);
+ buf = NULL;
+ }
+
+ *result = NULL;
+ *length = 0;
+
+ return -1;
+}
+
+/**
+ * Sets the default Content-Type for a given MIME part
+ *
+ * @param part A valid MIME part object
+ * @param part Whether the Content-Type should be for composite or not
+ * @return 0 on success or -1 on failure
+ *
+ * This function sets a default Content-Type according to RFC 2045 with a value
+ * of "text/plain; charset="us-ascii"". This function should only be used if
+ * the MIME part in question does not have a valid Content-Type specification.
+ */
+int
+mm_mimepart_setdefaultcontenttype(struct mm_mimepart *part, int composite)
+{
+ struct mm_content *type;
+ struct mm_param *param;
+
+ if (part == NULL) {
+ return(-1);
+ }
+
+ if (part->type != NULL) {
+ mm_content_free(part->type);
+ part->type = NULL;
+ }
+
+ type = mm_content_new();
+ if (composite) {
+ type->maintype = xstrdup("multipart");
+ type->subtype = xstrdup("mixed");
+ } else {
+ type->maintype = xstrdup("text");
+ type->subtype = xstrdup("plain");
+ param = mm_param_new();
+ param->name = xstrdup("charset");
+ param->value = xstrdup("us-ascii");
+ mm_content_attachtypeparam(type, param);
+ }
+
+ mm_mimepart_attachcontenttype(part, type);
+
+ return (0);
+}
+
+/** @{
+ * @name Accessing the MIME part's Content-Type information
+ */
+
+/**
+ * Attaches a context type object to a MIME part
+ *
+ * @param part A valid MIME part object
+ * @param ct The content type object to attach
+ * @return Nothing
+ *
+ * This function attaches a Content-Type object to a MIME part. It does not
+ * care whether the Content-Type suites the actual content in the MIME part,
+ * so the programmer should take care of that.
+ */
+void
+mm_mimepart_attachcontenttype(struct mm_mimepart *part, struct mm_content *ct)
+{
+ part->type = ct;
+}
+
+/**
+ * Gets the Content-Type of a given MIME part object
+ *
+ * @param part A valid MIME part object
+ * @return The Content-Type object of the specified MIME part
+ *
+ * This function returns a pointer to the Content-Type object of the given
+ * MIME part. This pointer might be set to NULL, indicating that there is
+ * no Content-Type object for the given MIME part currently.
+ */
+struct mm_content *
+mm_mimepart_getcontent(struct mm_mimepart *part)
+{
+ assert(part != NULL);
+
+ return part->type;
+}
+
+/** @} */
diff --git a/trunk/main/minimime/mm_mimeutil.c b/trunk/main/minimime/mm_mimeutil.c
new file mode 100644
index 000000000..4a5cc35e9
--- /dev/null
+++ b/trunk/main/minimime/mm_mimeutil.c
@@ -0,0 +1,137 @@
+/*
+ * $Id$
+ *
+ * MiniMIME - a library for handling MIME messages
+ *
+ * Copyright (C) 2004 Jann Fischer <rezine@mistrust.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of the contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <sys/time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <assert.h>
+
+#include "mm_internal.h"
+
+#define MM_DATE_LENGTH 50
+
+static const char boundary_charset[] =
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.=";
+
+/** @file mm_mimeutil.c
+ *
+ * This module contains various MIME related utility functions.
+ */
+
+/** @defgroup mimeutil MIME related utility functions */
+
+/**
+ * Generates an RFC 2822 conform date string
+ *
+ * @param timezone Whether to include timezone information
+ * @returns A pointer to the actual date string
+ * @note The pointer returned must be freed some time
+ *
+ * This function generates an RFC 2822 conform date string to use in message
+ * headers. It allocates memory to hold the string and returns a pointer to
+ * it. The generated date is in the format (example):
+ *
+ * Thu, 25 December 2003 16:35:22 +0100 (CET)
+ *
+ * This function dynamically allocates memory and returns a pointer to it.
+ * This memory should be released with free() once not needed anymore.
+ */
+#if 0
+int
+mm_mimeutil_gendate(char **result)
+{
+ time_t curtime;
+ struct tm *curtm;
+
+ if (result != NULL) {
+ curtime = time(NULL);
+ curtm = localtime(&curtime);
+ if ((*result = (char *) malloc(MM_DATE_LENGTH)) == NULL) {
+ return(-1);
+ }
+ return(strftime(*result, MM_DATE_LENGTH,
+ "%a, %d %b %G %T %z (%Z)", curtm));
+ } else {
+ return(-1);
+ }
+}
+#endif
+
+int
+mm_mimeutil_genboundary(char *prefix, size_t length, char **result)
+{
+ size_t total;
+ size_t preflen;
+ struct timeval curtm;
+ int i;
+ int pos;
+
+ total = 0;
+ preflen = 0;
+
+ if (result == NULL) {
+ return(-1);
+ }
+ *result = NULL;
+
+ gettimeofday(&curtm, NULL);
+ srandom(curtm.tv_usec);
+
+ if (prefix != NULL) {
+ total = strlen(prefix);
+ preflen = total;
+ }
+
+ total += length;
+
+ if ((*result = (char *) xmalloc(total + 1)) == NULL) {
+ mm_errno = MM_ERROR_ERRNO;
+ return(-1);
+ }
+
+ *result = '\0';
+
+ if (prefix != NULL) {
+ strlcat(*result, prefix, total);
+ }
+
+ for (i = 0; i < length - 1; i++) {
+ pos = random() % strlen(boundary_charset);
+ *result[i + preflen] = boundary_charset[pos];
+ }
+ *result[total] = '\0';
+
+ return (0);
+}
diff --git a/trunk/main/minimime/mm_param.c b/trunk/main/minimime/mm_param.c
new file mode 100644
index 000000000..ee4147bf5
--- /dev/null
+++ b/trunk/main/minimime/mm_param.c
@@ -0,0 +1,225 @@
+/*
+ * $Id$
+ *
+ * MiniMIME - a library for handling MIME messages
+ *
+ * Copyright (C) 2003 Jann Fischer <rezine@mistrust.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of the contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include "mm_internal.h"
+#include "mm_util.h"
+
+/**
+ * @file mm_param.c
+ *
+ * Functions to manipulate MIME parameters
+ */
+
+/** @defgroup param Accessing and manipulating MIME parameters */
+
+/** @{
+ *
+ * @name Functions for manipulating MIME parameters
+ *
+ * MIME parameters are properties attached to certain MIME headers, such as
+ * Content-Type and Content-Disposition. MIME parameters have a textual
+ * representations as in <i>name=value</i>. They contain important information
+ * about the MIME structure of a message, such as the boundary string used,
+ * which charset was used to encode the message and so on. This module
+ * provides simple to use functions to query or set MIME parameters.
+ *
+ * Each MIME header may hold an arbitrary amount of such parameters, which
+ * are delimeted by each other with a semicolon.
+ */
+
+/**
+ * Creates a new object to hold a MIME parameter.
+ *
+ * @return An object representing a MIME parameter
+ * @see mm_param_free
+ * @note The allocated memory must later be freed using mm_param_free()
+ */
+struct mm_param *
+mm_param_new(void)
+{
+ struct mm_param *param;
+
+ param = (struct mm_param *)xmalloc(sizeof(struct mm_param));
+
+ param->name = NULL;
+ param->value = NULL;
+
+ return param;
+}
+
+/**
+ * Releases all memory associated with a MIME parameter object.
+ *
+ * @param param A valid MIME parameter object to be freed
+ * @return Nothing
+ * @see mm_param_new
+ */
+void
+mm_param_free(struct mm_param *param)
+{
+ assert(param != NULL);
+
+ if (param->name != NULL) {
+ xfree(param->name);
+ param->name = NULL;
+ }
+ if (param->value != NULL) {
+ xfree(param->value);
+ param->value = NULL;
+ }
+ xfree(param);
+}
+
+/**
+ * Generates a new Content-Type parameter with the given name and value
+ *
+ * @param name The name of the MIME parameter
+ * @param value The value of the MIME parameter
+ * @returns A new MIME parameter object
+ * @see mm_param_free
+ * @see mm_param_new
+ *
+ * This function generates a new MIME parameter, with the name
+ * and value given as the arguments. The needed memory for the operation
+ * is allocated dynamically. It stores a copy of name and value in the
+ * actual object, so the memory holding the arguments can safely be
+ * freed after successfull return of this function.
+ */
+#if 0
+struct mm_param *
+mm_param_generate(const char *name, const char *value)
+{
+ struct mm_param *param;
+
+ param = mm_param_new();
+
+ param->name = xstrdup(name);
+ param->value = xstrdup(value);
+
+ return param;
+}
+#endif
+
+/**
+ * Sets the name of the given MIME parameter
+ *
+ * @param param A valid MIME parameter object
+ * @param name The new name of the parameter
+ * @param copy If set to > 0, copy the value stored in name
+ * @returns The address of the previous name for passing to free()
+ */
+#if 0
+char *
+mm_param_setname(struct mm_param *param, const char *name, int copy)
+{
+ char *retadr;
+ assert(param != NULL);
+
+ retadr = param->name;
+
+ if (copy)
+ param->name = xstrdup(name);
+ else
+ param->name = (char *)name;
+
+ return retadr;
+}
+#endif
+
+/**
+ * Sets the value of the given MIME parameter
+ *
+ * @param param A valid MIME parameter object
+ * @param name The new value for the parameter
+ * @param copy If set to > 0, copy the value stored in value
+ * @returns The address of the previous value for passing to free()
+ */
+#if 0
+char *
+mm_param_setvalue(struct mm_param *param, const char *value, int copy)
+{
+ char *retadr;
+ assert(param != NULL);
+
+ retadr = param->value;
+
+ if (copy)
+ param->value = xstrdup(value);
+ else
+ param->value = (char *)value;
+
+ return retadr;
+}
+#endif
+
+/**
+ * Gets the name of a MIME parameter object
+ *
+ * @param param A valid MIME parameter object
+ * @returns The name of the MIME parameter
+ */
+#if 0
+const char *
+mm_param_getname(struct mm_param *param)
+{
+ assert(param != NULL);
+ return param->name;
+}
+#endif
+
+/**
+ * Gets the value of a MIME parameter object
+ *
+ * @param param A valid MIME parameter object
+ * @returns The value of the MIME parameter
+ */
+#if 0
+const char *
+mm_param_getvalue(struct mm_param *param)
+{
+ assert(param != NULL);
+ return param->value;
+}
+#endif
+
+/** @} */
diff --git a/trunk/main/minimime/mm_parse.c b/trunk/main/minimime/mm_parse.c
new file mode 100644
index 000000000..4223d99e0
--- /dev/null
+++ b/trunk/main/minimime/mm_parse.c
@@ -0,0 +1,168 @@
+/*
+ * $Id$
+ *
+ * MiniMIME - a library for handling MIME messages
+ *
+ * Copyright (C) 2003 Jann Fischer <rezine@mistrust.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of the contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include "mm_internal.h"
+#include "mm_util.h"
+
+#include "mimeparser.h"
+#include "mimeparser.tab.h"
+
+/** @file mm_parse.c
+ *
+ * Functions to parse MIME messages
+ */
+
+/**
+ * Parses a NUL-terminated string into a MiniMIME context
+ *
+ * @param ctx A valid MiniMIME context object
+ * @param text The NUL-terminated string to parse
+ * @param parsemode The parsemode
+ * @param flags The flags to pass to the parser
+ * @returns 0 on success or -1 on failure
+ * @note Sets mm_errno if an error occurs
+ *
+ * This function parses a MIME message, stored in the memory region pointed to
+ * by text (must be NUL-terminated) according to the parseflags and stores the
+ * results in the MiniMIME context specified by ctx.
+ *
+ * The following modes can be used to specify how the message should be
+ * parsed:
+ *
+ * - MM_PARSE_STRICT: Do not tolerate MIME violations
+ * - MM_PARSE_LOOSE: Tolerate as much MIME violations as possible
+ *
+ * The context needs to be initialized before using mm_context_new() and may
+ * be freed using mm_context_free().
+ */
+int
+mm_parse_mem(MM_CTX *ctx, const char *text, int parsemode, int flags)
+{
+ void *yyscanner;
+ int res;
+ struct parser_state pstate;
+
+ pstate.ctx = ctx;
+ pstate.parsemode = parsemode;
+
+ mimeparser_yylex_init(&yyscanner);
+ PARSER_initialize(&pstate, yyscanner);
+
+ PARSER_setbuffer(text, yyscanner);
+ PARSER_setfp(NULL, yyscanner);
+
+ res = mimeparser_yyparse(&pstate,yyscanner);
+ mimeparser_yylex_destroy(yyscanner);
+ return res;
+}
+
+/**
+ * Parses a file into a MiniMIME context
+ *
+ * @param ctx A valid MiniMIME context object
+ * @param filename The name of the file to parse
+ * @param parsemode The parsemode
+ * @param flags The flags to pass to the parser
+ * @returns 0 on success or -1 on failure
+ * @note Sets mm_errno if an error occurs
+ *
+ * This function parses a MIME message, stored in the filesystem according to
+ * the parseflags and stores the results in the MiniMIME context specified by
+ * ctx.
+ *
+ * The following modes can be used to specify how the message should be
+ * parsed:
+ *
+ * - MM_PARSE_STRICT: Do not tolerate MIME violations
+ * - MM_PARSE_LOOSE: Tolerate as much MIME violations as possible
+ *
+ * The context needs to be initialized before using mm_context_new() and may
+ * be freed using mm_context_free().
+ */
+int
+mm_parse_file(MM_CTX *ctx, const char *filename, int parsemode, int flags)
+{
+ FILE *fp;
+ int res;
+ void *yyscanner;
+ struct parser_state pstate;
+
+ mimeparser_yylex_init(&yyscanner);
+
+ if ((fp = fopen(filename, "r")) == NULL) {
+ mm_errno = MM_ERROR_ERRNO;
+ return -1;
+ }
+
+ PARSER_setfp(fp,yyscanner);
+ PARSER_initialize(&pstate, yyscanner);
+
+ pstate.ctx = ctx;
+ pstate.parsemode = parsemode;
+
+ res = mimeparser_yyparse(&pstate,yyscanner);
+ mimeparser_yylex_destroy(yyscanner);
+ fclose(fp);
+ return res;
+}
+
+int
+mm_parse_fileptr(MM_CTX *ctx, FILE *f, int parsemode, int flags)
+{
+ int res;
+ void *yyscanner;
+ struct parser_state pstate;
+
+ mimeparser_yylex_init(&yyscanner);
+
+ PARSER_setfp(f, yyscanner);
+ PARSER_initialize(&pstate, yyscanner);
+
+ pstate.ctx = ctx;
+ pstate.parsemode = parsemode;
+
+ res = mimeparser_yyparse(&pstate,yyscanner);
+ mimeparser_yylex_destroy(yyscanner);
+
+ return res;
+}
diff --git a/trunk/main/minimime/mm_queue.h b/trunk/main/minimime/mm_queue.h
new file mode 100644
index 000000000..893e2fae2
--- /dev/null
+++ b/trunk/main/minimime/mm_queue.h
@@ -0,0 +1,508 @@
+/* $OpenBSD: queue.h,v 1.25 2004/04/08 16:08:21 henning Exp $ */
+/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */
+
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)queue.h 8.5 (Berkeley) 8/20/94
+ */
+
+#ifndef _SYS_QUEUE_H_
+#define _SYS_QUEUE_H_
+
+/*
+ * This file defines five types of data structures: singly-linked lists,
+ * lists, simple queues, tail queues, and circular queues.
+ *
+ *
+ * A singly-linked list is headed by a single forward pointer. The elements
+ * are singly linked for minimum space and pointer manipulation overhead at
+ * the expense of O(n) removal for arbitrary elements. New elements can be
+ * added to the list after an existing element or at the head of the list.
+ * Elements being removed from the head of the list should use the explicit
+ * macro for this purpose for optimum efficiency. A singly-linked list may
+ * only be traversed in the forward direction. Singly-linked lists are ideal
+ * for applications with large datasets and few or no removals or for
+ * implementing a LIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may only be traversed in the forward direction.
+ *
+ * A simple queue is headed by a pair of pointers, one the head of the
+ * list and the other to the tail of the list. The elements are singly
+ * linked to save space, so elements can only be removed from the
+ * head of the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the
+ * list. A simple queue may only be traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * A circle queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the list.
+ * A circle queue may be traversed in either direction, but has a more
+ * complex end of list detection.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ */
+
+/*
+ * Singly-linked List definitions.
+ */
+#define SLIST_HEAD(name, type) \
+struct name { \
+ struct type *slh_first; /* first element */ \
+}
+
+#define SLIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define SLIST_ENTRY(type) \
+struct { \
+ struct type *sle_next; /* next element */ \
+}
+
+/*
+ * Singly-linked List access methods.
+ */
+#define SLIST_FIRST(head) ((head)->slh_first)
+#define SLIST_END(head) NULL
+#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head))
+#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
+
+#define SLIST_FOREACH(var, head, field) \
+ for((var) = SLIST_FIRST(head); \
+ (var) != SLIST_END(head); \
+ (var) = SLIST_NEXT(var, field))
+
+#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \
+ for ((varp) = &SLIST_FIRST((head)); \
+ ((var) = *(varp)) != SLIST_END(head); \
+ (varp) = &SLIST_NEXT((var), field))
+
+/*
+ * Singly-linked List functions.
+ */
+#define SLIST_INIT(head) { \
+ SLIST_FIRST(head) = SLIST_END(head); \
+}
+
+#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
+ (elm)->field.sle_next = (slistelm)->field.sle_next; \
+ (slistelm)->field.sle_next = (elm); \
+} while (0)
+
+#define SLIST_INSERT_HEAD(head, elm, field) do { \
+ (elm)->field.sle_next = (head)->slh_first; \
+ (head)->slh_first = (elm); \
+} while (0)
+
+#define SLIST_REMOVE_NEXT(head, elm, field) do { \
+ (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \
+} while (0)
+
+#define SLIST_REMOVE_HEAD(head, field) do { \
+ (head)->slh_first = (head)->slh_first->field.sle_next; \
+} while (0)
+
+#define SLIST_REMOVE(head, elm, type, field) do { \
+ if ((head)->slh_first == (elm)) { \
+ SLIST_REMOVE_HEAD((head), field); \
+ } \
+ else { \
+ struct type *curelm = (head)->slh_first; \
+ while( curelm->field.sle_next != (elm) ) \
+ curelm = curelm->field.sle_next; \
+ curelm->field.sle_next = \
+ curelm->field.sle_next->field.sle_next; \
+ } \
+} while (0)
+
+/*
+ * List definitions.
+ */
+#define LIST_HEAD(name, type) \
+struct name { \
+ struct type *lh_first; /* first element */ \
+}
+
+#define LIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define LIST_ENTRY(type) \
+struct { \
+ struct type *le_next; /* next element */ \
+ struct type **le_prev; /* address of previous next element */ \
+}
+
+/*
+ * List access methods
+ */
+#define LIST_FIRST(head) ((head)->lh_first)
+#define LIST_END(head) NULL
+#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head))
+#define LIST_NEXT(elm, field) ((elm)->field.le_next)
+
+#define LIST_FOREACH(var, head, field) \
+ for((var) = LIST_FIRST(head); \
+ (var)!= LIST_END(head); \
+ (var) = LIST_NEXT(var, field))
+
+/*
+ * List functions.
+ */
+#define LIST_INIT(head) do { \
+ LIST_FIRST(head) = LIST_END(head); \
+} while (0)
+
+#define LIST_INSERT_AFTER(listelm, elm, field) do { \
+ if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
+ (listelm)->field.le_next->field.le_prev = \
+ &(elm)->field.le_next; \
+ (listelm)->field.le_next = (elm); \
+ (elm)->field.le_prev = &(listelm)->field.le_next; \
+} while (0)
+
+#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.le_prev = (listelm)->field.le_prev; \
+ (elm)->field.le_next = (listelm); \
+ *(listelm)->field.le_prev = (elm); \
+ (listelm)->field.le_prev = &(elm)->field.le_next; \
+} while (0)
+
+#define LIST_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.le_next = (head)->lh_first) != NULL) \
+ (head)->lh_first->field.le_prev = &(elm)->field.le_next;\
+ (head)->lh_first = (elm); \
+ (elm)->field.le_prev = &(head)->lh_first; \
+} while (0)
+
+#define LIST_REMOVE(elm, field) do { \
+ if ((elm)->field.le_next != NULL) \
+ (elm)->field.le_next->field.le_prev = \
+ (elm)->field.le_prev; \
+ *(elm)->field.le_prev = (elm)->field.le_next; \
+} while (0)
+
+#define LIST_REPLACE(elm, elm2, field) do { \
+ if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \
+ (elm2)->field.le_next->field.le_prev = \
+ &(elm2)->field.le_next; \
+ (elm2)->field.le_prev = (elm)->field.le_prev; \
+ *(elm2)->field.le_prev = (elm2); \
+} while (0)
+
+/*
+ * Simple queue definitions.
+ */
+#define SIMPLEQ_HEAD(name, type) \
+struct name { \
+ struct type *sqh_first; /* first element */ \
+ struct type **sqh_last; /* addr of last next element */ \
+}
+
+#define SIMPLEQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).sqh_first }
+
+#define SIMPLEQ_ENTRY(type) \
+struct { \
+ struct type *sqe_next; /* next element */ \
+}
+
+/*
+ * Simple queue access methods.
+ */
+#define SIMPLEQ_FIRST(head) ((head)->sqh_first)
+#define SIMPLEQ_END(head) NULL
+#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))
+#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
+
+#define SIMPLEQ_FOREACH(var, head, field) \
+ for((var) = SIMPLEQ_FIRST(head); \
+ (var) != SIMPLEQ_END(head); \
+ (var) = SIMPLEQ_NEXT(var, field))
+
+/*
+ * Simple queue functions.
+ */
+#define SIMPLEQ_INIT(head) do { \
+ (head)->sqh_first = NULL; \
+ (head)->sqh_last = &(head)->sqh_first; \
+} while (0)
+
+#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+ (head)->sqh_first = (elm); \
+} while (0)
+
+#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.sqe_next = NULL; \
+ *(head)->sqh_last = (elm); \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+} while (0)
+
+#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+ (listelm)->field.sqe_next = (elm); \
+} while (0)
+
+#define SIMPLEQ_REMOVE_HEAD(head, elm, field) do { \
+ if (((head)->sqh_first = (elm)->field.sqe_next) == NULL) \
+ (head)->sqh_last = &(head)->sqh_first; \
+} while (0)
+
+/*
+ * Tail queue definitions.
+ */
+#define TAILQ_HEAD(name, type) \
+struct name { \
+ struct type *tqh_first; /* first element */ \
+ struct type **tqh_last; /* addr of last next element */ \
+}
+
+#define TAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).tqh_first }
+
+#define TAILQ_ENTRY(type) \
+struct { \
+ struct type *tqe_next; /* next element */ \
+ struct type **tqe_prev; /* address of previous next element */ \
+}
+
+/*
+ * tail queue access methods
+ */
+#define TAILQ_FIRST(head) ((head)->tqh_first)
+#define TAILQ_END(head) NULL
+#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+#define TAILQ_LAST(head, headname) \
+ (*(((struct headname *)((head)->tqh_last))->tqh_last))
+/* XXX */
+#define TAILQ_PREV(elm, headname, field) \
+ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+#define TAILQ_EMPTY(head) \
+ (TAILQ_FIRST(head) == TAILQ_END(head))
+
+#define TAILQ_FOREACH(var, head, field) \
+ for((var) = TAILQ_FIRST(head); \
+ (var) != TAILQ_END(head); \
+ (var) = TAILQ_NEXT(var, field))
+
+#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
+ for((var) = TAILQ_LAST(head, headname); \
+ (var) != TAILQ_END(head); \
+ (var) = TAILQ_PREV(var, headname, field))
+
+/*
+ * Tail queue functions.
+ */
+#define TAILQ_INIT(head) do { \
+ (head)->tqh_first = NULL; \
+ (head)->tqh_last = &(head)->tqh_first; \
+} while (0)
+
+#define TAILQ_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
+ (head)->tqh_first->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (head)->tqh_first = (elm); \
+ (elm)->field.tqe_prev = &(head)->tqh_first; \
+} while (0)
+
+#define TAILQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.tqe_next = NULL; \
+ (elm)->field.tqe_prev = (head)->tqh_last; \
+ *(head)->tqh_last = (elm); \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+} while (0)
+
+#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
+ (elm)->field.tqe_next->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (listelm)->field.tqe_next = (elm); \
+ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
+} while (0)
+
+#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
+ (elm)->field.tqe_next = (listelm); \
+ *(listelm)->field.tqe_prev = (elm); \
+ (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
+} while (0)
+
+#define TAILQ_REMOVE(head, elm, field) do { \
+ if (((elm)->field.tqe_next) != NULL) \
+ (elm)->field.tqe_next->field.tqe_prev = \
+ (elm)->field.tqe_prev; \
+ else \
+ (head)->tqh_last = (elm)->field.tqe_prev; \
+ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \
+} while (0)
+
+#define TAILQ_REPLACE(head, elm, elm2, field) do { \
+ if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \
+ (elm2)->field.tqe_next->field.tqe_prev = \
+ &(elm2)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm2)->field.tqe_next; \
+ (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
+ *(elm2)->field.tqe_prev = (elm2); \
+} while (0)
+
+/*
+ * Circular queue definitions.
+ */
+#define CIRCLEQ_HEAD(name, type) \
+struct name { \
+ struct type *cqh_first; /* first element */ \
+ struct type *cqh_last; /* last element */ \
+}
+
+#define CIRCLEQ_HEAD_INITIALIZER(head) \
+ { CIRCLEQ_END(&head), CIRCLEQ_END(&head) }
+
+#define CIRCLEQ_ENTRY(type) \
+struct { \
+ struct type *cqe_next; /* next element */ \
+ struct type *cqe_prev; /* previous element */ \
+}
+
+/*
+ * Circular queue access methods
+ */
+#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
+#define CIRCLEQ_LAST(head) ((head)->cqh_last)
+#define CIRCLEQ_END(head) ((void *)(head))
+#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next)
+#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev)
+#define CIRCLEQ_EMPTY(head) \
+ (CIRCLEQ_FIRST(head) == CIRCLEQ_END(head))
+
+#define CIRCLEQ_FOREACH(var, head, field) \
+ for((var) = CIRCLEQ_FIRST(head); \
+ (var) != CIRCLEQ_END(head); \
+ (var) = CIRCLEQ_NEXT(var, field))
+
+#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
+ for((var) = CIRCLEQ_LAST(head); \
+ (var) != CIRCLEQ_END(head); \
+ (var) = CIRCLEQ_PREV(var, field))
+
+/*
+ * Circular queue functions.
+ */
+#define CIRCLEQ_INIT(head) do { \
+ (head)->cqh_first = CIRCLEQ_END(head); \
+ (head)->cqh_last = CIRCLEQ_END(head); \
+} while (0)
+
+#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ (elm)->field.cqe_next = (listelm)->field.cqe_next; \
+ (elm)->field.cqe_prev = (listelm); \
+ if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (listelm)->field.cqe_next->field.cqe_prev = (elm); \
+ (listelm)->field.cqe_next = (elm); \
+} while (0)
+
+#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
+ (elm)->field.cqe_next = (listelm); \
+ (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
+ if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (listelm)->field.cqe_prev->field.cqe_next = (elm); \
+ (listelm)->field.cqe_prev = (elm); \
+} while (0)
+
+#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
+ (elm)->field.cqe_next = (head)->cqh_first; \
+ (elm)->field.cqe_prev = CIRCLEQ_END(head); \
+ if ((head)->cqh_last == CIRCLEQ_END(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (head)->cqh_first->field.cqe_prev = (elm); \
+ (head)->cqh_first = (elm); \
+} while (0)
+
+#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.cqe_next = CIRCLEQ_END(head); \
+ (elm)->field.cqe_prev = (head)->cqh_last; \
+ if ((head)->cqh_first == CIRCLEQ_END(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (head)->cqh_last->field.cqe_next = (elm); \
+ (head)->cqh_last = (elm); \
+} while (0)
+
+#define CIRCLEQ_REMOVE(head, elm, field) do { \
+ if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \
+ (head)->cqh_last = (elm)->field.cqe_prev; \
+ else \
+ (elm)->field.cqe_next->field.cqe_prev = \
+ (elm)->field.cqe_prev; \
+ if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \
+ (head)->cqh_first = (elm)->field.cqe_next; \
+ else \
+ (elm)->field.cqe_prev->field.cqe_next = \
+ (elm)->field.cqe_next; \
+} while (0)
+
+#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \
+ if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \
+ CIRCLEQ_END(head)) \
+ (head).cqh_last = (elm2); \
+ else \
+ (elm2)->field.cqe_next->field.cqe_prev = (elm2); \
+ if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \
+ CIRCLEQ_END(head)) \
+ (head).cqh_first = (elm2); \
+ else \
+ (elm2)->field.cqe_prev->field.cqe_next = (elm2); \
+} while (0)
+
+#endif /* !_SYS_QUEUE_H_ */
diff --git a/trunk/main/minimime/mm_util.c b/trunk/main/minimime/mm_util.c
new file mode 100644
index 000000000..90debcb6e
--- /dev/null
+++ b/trunk/main/minimime/mm_util.c
@@ -0,0 +1,412 @@
+/*
+ * $Id$
+ *
+ * MiniMIME - a library for handling MIME messages
+ *
+ * Copyright (C) 2003 Jann Fischer <rezine@mistrust.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of the contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <assert.h>
+
+#include "mm_internal.h"
+
+/** @file mm_util.c
+ *
+ * This module contains utility functions for the MiniMIME library
+ */
+
+/** @defgroup util General purpose utility functions */
+
+#ifndef __HAVE_LEAK_DETECTION
+/**
+ * Allocates a block of memory
+ *
+ * @param size The size of the memory region to allocate
+ * @return A pointer to the allocated memory region
+ * @ingroup util
+ *
+ * xmalloc() calls abort() if either the size argument is negative or the
+ * requested memory amount could not be allocated via an assert() call.
+ */
+void *
+xmalloc(size_t size)
+{
+ void *p;
+
+ assert(size > 0);
+ p = malloc(size);
+ assert(p != NULL);
+
+ return p;
+}
+
+/**
+ * realloc() wrapper
+ *
+ * @param p Pointer to a memory region which should be reallocated
+ * @param size The new size of the memory region
+ * @return A pointer to the reallocated memory region
+ * @ingroup util
+ *
+ * xrealloc() is a wrapper around realloc() which calls abort() if either the
+ * size argument is negative or the requested memory amount could not be
+ * allocated.
+ */
+void *
+xrealloc(void *p, size_t size)
+{
+ void *n;
+
+ assert(size > 0);
+ n = realloc(p, size);
+ assert(n != NULL);
+
+ return n;
+}
+
+char *
+xstrdup(const char *str)
+{
+ char *p;
+
+ assert(str != NULL);
+ p = strdup(str);
+ assert(p != NULL);
+
+ return p;
+}
+
+void
+xfree(void *p)
+{
+ assert(p != NULL);
+ free(p);
+ p = NULL;
+ assert(p == NULL);
+}
+#endif /* ! __HAVE_LEAK_DETECTION */
+
+/**
+ * Unquotes a string
+ *
+ * @param string The quoted string to unquote
+ * @return A pointer to the unquoted string
+ * @ingroup util
+ *
+ * This function unquotes a string. That is, it returns a pointer to a newly
+ * allocated memory region in which the unquoted string is stored. Only
+ * leading and trailing double-qoutes are removed. The string needs to be
+ * freed when it is not needed anymore.
+ */
+char *
+mm_unquote(const char *string)
+{
+ char *ret;
+
+ if (string[0] != '\"' || string[strlen(string)-1] != '\"')
+ return xstrdup(string);
+
+ ret = xstrdup(string + 1);
+ ret[strlen(ret)-1] = '\0';
+
+ return ret;
+}
+
+
+/**
+ * Removes MIME comments from a string
+ *
+ * @param string The string to uncomment
+ * @return A pointer to the uncommented string or NULL on error. Sets mm_errno.
+ * @ingroup util
+ *
+ * This function removes MIME comments from a string (included in parantheses).
+ * It returns a pointer to a newly allocated memory region in which the
+ * uncommented string is stored. The returned string needs to be freed when
+ * it's not used anymore.
+ */
+char *
+mm_uncomment(const char *string)
+{
+ char *buf, *new, *orig, *token;
+ size_t new_size;
+ int found;
+ int open;
+
+ assert(string != NULL);
+
+ new_size = strlen(string) + 1;
+ new = NULL;
+ buf = NULL;
+ orig = NULL;
+ found = 0;
+ open = 0;
+ mm_errno = MM_ERROR_NONE;
+
+ buf = xstrdup(string);
+ orig = buf;
+
+ while (*buf != '\0') {
+ if (*buf == '(') {
+ open++;
+ new_size--;
+ found++;
+ } else if (*buf == ')') {
+ open--;
+ new_size--;
+ } else {
+ if (open)
+ new_size--;
+ }
+ buf++;
+ }
+
+ if (open != 0) {
+ mm_errno = MM_ERROR_PARSE;
+ mm_error_setmsg("Uncommenting: parantheses are unbalanced");
+ goto cleanup;
+ }
+
+ if (!found) {
+ new = orig;
+ return orig;
+ }
+
+ new = xmalloc(new_size + 1);
+ *new = '\0';
+ buf = orig;
+ token = buf;
+
+ /* Tokenize our string by parentheses, and copy the portions which are
+ * not commented to our destination.
+ */
+ open = 0;
+ while (*buf != '\0') {
+ if (*buf == '(') {
+ if (!open) {
+ *buf = '\0';
+ strlcat(new, token, new_size);
+ token = buf+1;
+ }
+ open++;
+ }
+ if (*buf == ')') {
+ open--;
+ token = buf + 1;
+ }
+ buf++;
+ }
+
+ strlcat(new, token, new_size);
+
+cleanup:
+ if (orig != NULL) {
+ xfree(orig);
+ orig = NULL;
+ }
+
+ if (mm_errno != MM_ERROR_NONE) {
+ if (new != NULL) {
+ xfree(new);
+ new = NULL;
+ }
+ return NULL;
+ } else {
+ return new;
+ }
+}
+
+/**
+ * separate strings
+ *
+ * @param stringp A pointer to the string being splitted
+ * @param delim The delimeter string
+ * @ingroup util
+ *
+ * This function works similar to strsep(), with the difference that delim is
+ * treated as a whole.
+ */
+char *
+xstrsep(char **stringp, const char *delim)
+{
+ char *p;
+ char *s;
+ char *r;
+
+ if (*stringp == NULL || *stringp == '\0')
+ return NULL;
+
+ p = *stringp;
+
+ if ((s = strstr(p, delim)) == NULL) {
+ r = p;
+ while (*p != '\0')
+ p++;
+ *stringp = NULL;
+ return r;
+ } else {
+ r = p;
+ p += strlen(p) - strlen(s);
+ *p = '\0';
+ *stringp = p + strlen(delim);
+ return r;
+ }
+}
+
+/**
+ * Strips a given character set from a string
+ *
+ * @param input The string which to strip
+ * @param strip The character set to strip off
+ * @return A copy of the original string with all chars stripped
+ * @ingroup util
+ */
+char *
+mm_stripchars(char *input, char *strip)
+{
+ char *output, *orig;
+ int i, j, chars;
+
+ assert(input != NULL);
+ assert(strip != '\0');
+
+ chars = 0;
+ orig = input;
+
+ while (*orig != '\0') {
+ for (i = 0; i < strlen(strip); i++) {
+ if (*orig == strip[i]) {
+ chars++;
+ break;
+ }
+ }
+ orig++;
+ }
+
+ /* If we have not found any char in the input, return a dup of the orig
+ string */
+ if (chars == 0)
+ return(xstrdup(input));
+
+ output = (char *)xmalloc(strlen(input) - chars);
+ orig = output;
+
+ for (i = 0; i < strlen(input); i++) {
+ int stripc;
+ stripc = 0;
+ for (j = 0; j < strlen(strip); j++) {
+ if (input[i] == strip[j]) {
+ stripc = 1;
+ break;
+ }
+ }
+ if (stripc == 0) {
+ *output = input[i];
+ output++;
+ }
+ }
+
+ *output = '\0';
+
+ return(orig);
+}
+
+/**
+ * Adds characters to a string at given positions
+ *
+ * @param input The string to which to add characters
+ * @param add The character string to add
+ * @param linelength The position where to add the character
+ * @return A copy of the string with characters added
+ * @ingroup util
+ *
+ * This function adds the characters add at each linelength positions and
+ * returns this new string.
+ */
+char *
+mm_addchars(char *input, char *add, uint16_t linelength)
+{
+ uint32_t len;
+ uint32_t i;
+ uint32_t l;
+ uint32_t j;
+ uint16_t addcrlf;
+ char *output;
+ char *orig;
+
+ len = strlen(input);
+ if (len <= linelength)
+ return(xstrdup(input));
+
+ addcrlf = len / linelength;
+
+ output = (char *)xmalloc(len + (addcrlf * strlen(add)));
+ orig = output;
+
+ for (i = 0, l = 0; i < len; i++, l++) {
+ if (l == linelength) {
+ for (j = 0; j < strlen(add); j++) {
+ *output = add[j];
+ output++;
+ }
+ l = 0;
+ }
+ *output = input[i];
+ output++;
+ }
+
+ *output = '\0';
+ output = orig;
+
+ return(orig);
+}
+
+void
+mm_striptrailing(char **what, const char *charset)
+{
+ size_t eos, i, hit;
+ char *str;
+
+ str = *what;
+ for (eos = strlen(str)-1; eos >= 0; eos--) {
+ hit = 0;
+ for (i = 0; i < strlen(charset); i++) {
+ if (str[eos] == charset[i]) {
+ str[eos] = '\0';
+ hit = 1;
+ break;
+ }
+ }
+ if (!hit)
+ break;
+ }
+}
diff --git a/trunk/main/minimime/mm_util.h b/trunk/main/minimime/mm_util.h
new file mode 100644
index 000000000..b7058657d
--- /dev/null
+++ b/trunk/main/minimime/mm_util.h
@@ -0,0 +1,50 @@
+/*
+ * $Id$
+ *
+ * MiniMIME - a library for handling MIME messages
+ *
+ * Copyright (C) 2003 Jann Fischer <rezine@mistrust.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef __MM_UTIL_H
+#define __MM_UTIL_H
+
+#define STRIP_TRAILING(str, charset) do { \
+ size_t eos, i, hit; \
+ for (eos = strlen(str); eos > 0; eos--) { \
+ hit = 0; \
+ for (i = 0; i <= strlen(charset); i++) { \
+ if (str[eos] == charset[i]) {\
+ str[eos] = '\0'; \
+ hit = 1; \
+ break; \
+ } \
+ } \
+ if (!hit) \
+ break; \
+ } \
+} while (0);
+
+#endif /* ! __MM_UTIL_H */
diff --git a/trunk/main/minimime/mm_warnings.c b/trunk/main/minimime/mm_warnings.c
new file mode 100644
index 000000000..c9b8a7daf
--- /dev/null
+++ b/trunk/main/minimime/mm_warnings.c
@@ -0,0 +1,99 @@
+/*
+ * $Id$
+ *
+ * MiniMIME - a library for handling MIME messages
+ *
+ * Copyright (C) 2003 Jann Fischer <rezine@mistrust.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the author nor the names of the contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include "mm_internal.h"
+
+/**
+ * Attaches a warning message to a context
+ *
+ * @param ctx A valid MiniMIME context object
+ * @param type The type of the warning
+ * @param fmt The warning message as format string
+ */
+void
+mm_warning_add(MM_CTX *ctx, int type, const char *fmt, ...)
+{
+ struct mm_warning *warning;
+ char buf[1024];
+ va_list ap;
+
+ assert(ctx != NULL);
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof buf, fmt, ap);
+ va_end(ap);
+
+ warning = (struct mm_warning *)xmalloc(sizeof(struct mm_warning));
+ warning->message = xstrdup(buf);
+ warning->type = type;
+
+ if (SLIST_EMPTY(&ctx->warnings)) {
+ SLIST_INSERT_HEAD(&ctx->warnings, warning, next);
+ } else {
+ struct mm_warning *last, *after;
+
+ after = NULL;
+
+ SLIST_FOREACH(last, &ctx->warnings, next) {
+ if (last != NULL) {
+ after = last;
+ }
+ }
+
+ assert(after != NULL);
+
+ SLIST_INSERT_AFTER(after, warning, next);
+ }
+}
+
+struct mm_warning *
+mm_warning_next(MM_CTX *ctx, struct mm_warning **last)
+{
+ struct mm_warning *warning;
+
+ if (*last == NULL) {
+ warning = SLIST_FIRST(&ctx->warnings);
+ } else {
+ warning = SLIST_NEXT(*last, next);
+ }
+
+ *last = warning;
+ return warning;
+}
diff --git a/trunk/main/minimime/strlcat.c b/trunk/main/minimime/strlcat.c
new file mode 100644
index 000000000..2d2da0413
--- /dev/null
+++ b/trunk/main/minimime/strlcat.c
@@ -0,0 +1,70 @@
+/* $OpenBSD: strlcat.c,v 1.9 2003/03/14 14:35:29 millert Exp $ */
+
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char *rcsid = "$OpenBSD: strlcat.c,v 1.9 2003/03/14 14:35:29 millert Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <string.h>
+
+/*
+ * Appends src to string dst of size siz (unlike strncat, siz is the
+ * full size of dst, not space left). At most siz-1 characters
+ * will be copied. Always NUL terminates (unless siz <= strlen(dst)).
+ * Returns strlen(src) + MIN(siz, strlen(initial dst)).
+ * If retval >= siz, truncation occurred.
+ */
+size_t
+strlcat(char *dst, const char *src, size_t siz)
+{
+ register char *d = dst;
+ register const char *s = src;
+ register size_t n = siz;
+ size_t dlen;
+
+ /* Find the end of dst and adjust bytes left but don't go past end */
+ while (n-- != 0 && *d != '\0')
+ d++;
+ dlen = d - dst;
+ n = siz - dlen;
+
+ if (n == 0)
+ return(dlen + strlen(s));
+ while (*s != '\0') {
+ if (n != 1) {
+ *d++ = *s;
+ n--;
+ }
+ s++;
+ }
+ *d = '\0';
+
+ return(dlen + (s - src)); /* count does not include NUL */
+}
diff --git a/trunk/main/minimime/strlcpy.c b/trunk/main/minimime/strlcpy.c
new file mode 100644
index 000000000..94785a309
--- /dev/null
+++ b/trunk/main/minimime/strlcpy.c
@@ -0,0 +1,66 @@
+/* $OpenBSD: strlcpy.c,v 1.6 2003/03/14 14:35:29 millert Exp $ */
+
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char *rcsid = "$OpenBSD: strlcpy.c,v 1.6 2003/03/14 14:35:29 millert Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <string.h>
+
+/*
+ * Copy src to string dst of size siz. At most siz-1 characters
+ * will be copied. Always NUL terminates (unless siz == 0).
+ * Returns strlen(src); if retval >= siz, truncation occurred.
+ */
+size_t
+strlcpy(char *dst, const char *src, size_t siz)
+{
+ register char *d = dst;
+ register const char *s = src;
+ register size_t n = siz;
+
+ /* Copy as many bytes as will fit */
+ if (n != 0 && --n != 0) {
+ do {
+ if ((*d++ = *s++) == 0)
+ break;
+ } while (--n != 0);
+ }
+
+ /* Not enough room in dst, add NUL and traverse rest of src */
+ if (n == 0) {
+ if (siz != 0)
+ *d = '\0'; /* NUL-terminate dst */
+ while (*s++)
+ ;
+ }
+
+ return(s - src - 1); /* count does not include NUL */
+}
diff --git a/trunk/main/minimime/sys/mm_queue.h b/trunk/main/minimime/sys/mm_queue.h
new file mode 100644
index 000000000..c85bb240c
--- /dev/null
+++ b/trunk/main/minimime/sys/mm_queue.h
@@ -0,0 +1,503 @@
+/* $OpenBSD: queue.h,v 1.22 2001/06/23 04:39:35 angelos Exp $ */
+/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */
+
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)queue.h 8.5 (Berkeley) 8/20/94
+ */
+
+#ifndef _SYS_QUEUE_H_
+#define _SYS_QUEUE_H_
+
+/*
+ * This file defines five types of data structures: singly-linked lists,
+ * lists, simple queues, tail queues, and circular queues.
+ *
+ *
+ * A singly-linked list is headed by a single forward pointer. The elements
+ * are singly linked for minimum space and pointer manipulation overhead at
+ * the expense of O(n) removal for arbitrary elements. New elements can be
+ * added to the list after an existing element or at the head of the list.
+ * Elements being removed from the head of the list should use the explicit
+ * macro for this purpose for optimum efficiency. A singly-linked list may
+ * only be traversed in the forward direction. Singly-linked lists are ideal
+ * for applications with large datasets and few or no removals or for
+ * implementing a LIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may only be traversed in the forward direction.
+ *
+ * A simple queue is headed by a pair of pointers, one the head of the
+ * list and the other to the tail of the list. The elements are singly
+ * linked to save space, so elements can only be removed from the
+ * head of the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the
+ * list. A simple queue may only be traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * A circle queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the list.
+ * A circle queue may be traversed in either direction, but has a more
+ * complex end of list detection.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ */
+
+/*
+ * Singly-linked List definitions.
+ */
+#define SLIST_HEAD(name, type) \
+struct name { \
+ struct type *slh_first; /* first element */ \
+}
+
+#define SLIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define SLIST_ENTRY(type) \
+struct { \
+ struct type *sle_next; /* next element */ \
+}
+
+/*
+ * Singly-linked List access methods.
+ */
+#define SLIST_FIRST(head) ((head)->slh_first)
+#define SLIST_END(head) NULL
+#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head))
+#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
+
+#define SLIST_FOREACH(var, head, field) \
+ for((var) = SLIST_FIRST(head); \
+ (var) != SLIST_END(head); \
+ (var) = SLIST_NEXT(var, field))
+
+/*
+ * Singly-linked List functions.
+ */
+#define SLIST_INIT(head) { \
+ SLIST_FIRST(head) = SLIST_END(head); \
+}
+
+#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
+ (elm)->field.sle_next = (slistelm)->field.sle_next; \
+ (slistelm)->field.sle_next = (elm); \
+} while (0)
+
+#define SLIST_INSERT_HEAD(head, elm, field) do { \
+ (elm)->field.sle_next = (head)->slh_first; \
+ (head)->slh_first = (elm); \
+} while (0)
+
+#define SLIST_REMOVE_HEAD(head, field) do { \
+ (head)->slh_first = (head)->slh_first->field.sle_next; \
+} while (0)
+
+#define SLIST_REMOVE(head, elm, type, field) do { \
+ if ((head)->slh_first == (elm)) { \
+ SLIST_REMOVE_HEAD((head), field); \
+ } \
+ else { \
+ struct type *curelm = (head)->slh_first; \
+ while( curelm->field.sle_next != (elm) ) \
+ curelm = curelm->field.sle_next; \
+ curelm->field.sle_next = \
+ curelm->field.sle_next->field.sle_next; \
+ } \
+} while (0)
+
+/*
+ * List definitions.
+ */
+#define LIST_HEAD(name, type) \
+struct name { \
+ struct type *lh_first; /* first element */ \
+}
+
+#define LIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define LIST_ENTRY(type) \
+struct { \
+ struct type *le_next; /* next element */ \
+ struct type **le_prev; /* address of previous next element */ \
+}
+
+/*
+ * List access methods
+ */
+#define LIST_FIRST(head) ((head)->lh_first)
+#define LIST_END(head) NULL
+#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head))
+#define LIST_NEXT(elm, field) ((elm)->field.le_next)
+
+#define LIST_FOREACH(var, head, field) \
+ for((var) = LIST_FIRST(head); \
+ (var)!= LIST_END(head); \
+ (var) = LIST_NEXT(var, field))
+
+/*
+ * List functions.
+ */
+#define LIST_INIT(head) do { \
+ LIST_FIRST(head) = LIST_END(head); \
+} while (0)
+
+#define LIST_INSERT_AFTER(listelm, elm, field) do { \
+ if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
+ (listelm)->field.le_next->field.le_prev = \
+ &(elm)->field.le_next; \
+ (listelm)->field.le_next = (elm); \
+ (elm)->field.le_prev = &(listelm)->field.le_next; \
+} while (0)
+
+#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.le_prev = (listelm)->field.le_prev; \
+ (elm)->field.le_next = (listelm); \
+ *(listelm)->field.le_prev = (elm); \
+ (listelm)->field.le_prev = &(elm)->field.le_next; \
+} while (0)
+
+#define LIST_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.le_next = (head)->lh_first) != NULL) \
+ (head)->lh_first->field.le_prev = &(elm)->field.le_next;\
+ (head)->lh_first = (elm); \
+ (elm)->field.le_prev = &(head)->lh_first; \
+} while (0)
+
+#define LIST_REMOVE(elm, field) do { \
+ if ((elm)->field.le_next != NULL) \
+ (elm)->field.le_next->field.le_prev = \
+ (elm)->field.le_prev; \
+ *(elm)->field.le_prev = (elm)->field.le_next; \
+} while (0)
+
+#define LIST_REPLACE(elm, elm2, field) do { \
+ if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \
+ (elm2)->field.le_next->field.le_prev = \
+ &(elm2)->field.le_next; \
+ (elm2)->field.le_prev = (elm)->field.le_prev; \
+ *(elm2)->field.le_prev = (elm2); \
+} while (0)
+
+/*
+ * Simple queue definitions.
+ */
+#define SIMPLEQ_HEAD(name, type) \
+struct name { \
+ struct type *sqh_first; /* first element */ \
+ struct type **sqh_last; /* addr of last next element */ \
+}
+
+#define SIMPLEQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).sqh_first }
+
+#define SIMPLEQ_ENTRY(type) \
+struct { \
+ struct type *sqe_next; /* next element */ \
+}
+
+/*
+ * Simple queue access methods.
+ */
+#define SIMPLEQ_FIRST(head) ((head)->sqh_first)
+#define SIMPLEQ_END(head) NULL
+#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))
+#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
+
+#define SIMPLEQ_FOREACH(var, head, field) \
+ for((var) = SIMPLEQ_FIRST(head); \
+ (var) != SIMPLEQ_END(head); \
+ (var) = SIMPLEQ_NEXT(var, field))
+
+/*
+ * Simple queue functions.
+ */
+#define SIMPLEQ_INIT(head) do { \
+ (head)->sqh_first = NULL; \
+ (head)->sqh_last = &(head)->sqh_first; \
+} while (0)
+
+#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+ (head)->sqh_first = (elm); \
+} while (0)
+
+#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.sqe_next = NULL; \
+ *(head)->sqh_last = (elm); \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+} while (0)
+
+#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+ (listelm)->field.sqe_next = (elm); \
+} while (0)
+
+#define SIMPLEQ_REMOVE_HEAD(head, elm, field) do { \
+ if (((head)->sqh_first = (elm)->field.sqe_next) == NULL) \
+ (head)->sqh_last = &(head)->sqh_first; \
+} while (0)
+
+/*
+ * Tail queue definitions.
+ */
+#define TAILQ_HEAD(name, type) \
+struct name { \
+ struct type *tqh_first; /* first element */ \
+ struct type **tqh_last; /* addr of last next element */ \
+}
+
+#define TAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).tqh_first }
+
+#define TAILQ_ENTRY(type) \
+struct { \
+ struct type *tqe_next; /* next element */ \
+ struct type **tqe_prev; /* address of previous next element */ \
+}
+
+/*
+ * tail queue access methods
+ */
+#define TAILQ_FIRST(head) ((head)->tqh_first)
+#define TAILQ_END(head) NULL
+#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+#define TAILQ_LAST(head, headname) \
+ (*(((struct headname *)((head)->tqh_last))->tqh_last))
+/* XXX */
+#define TAILQ_PREV(elm, headname, field) \
+ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+#define TAILQ_EMPTY(head) \
+ (TAILQ_FIRST(head) == TAILQ_END(head))
+
+#define TAILQ_FOREACH(var, head, field) \
+ for((var) = TAILQ_FIRST(head); \
+ (var) != TAILQ_END(head); \
+ (var) = TAILQ_NEXT(var, field))
+
+#define TAILQ_FOREACH_REVERSE(var, head, field, headname) \
+ for((var) = TAILQ_LAST(head, headname); \
+ (var) != TAILQ_END(head); \
+ (var) = TAILQ_PREV(var, headname, field))
+
+/*
+ * Tail queue functions.
+ */
+#define TAILQ_INIT(head) do { \
+ (head)->tqh_first = NULL; \
+ (head)->tqh_last = &(head)->tqh_first; \
+} while (0)
+
+#define TAILQ_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
+ (head)->tqh_first->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (head)->tqh_first = (elm); \
+ (elm)->field.tqe_prev = &(head)->tqh_first; \
+} while (0)
+
+#define TAILQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.tqe_next = NULL; \
+ (elm)->field.tqe_prev = (head)->tqh_last; \
+ *(head)->tqh_last = (elm); \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+} while (0)
+
+#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
+ (elm)->field.tqe_next->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (listelm)->field.tqe_next = (elm); \
+ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
+} while (0)
+
+#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
+ (elm)->field.tqe_next = (listelm); \
+ *(listelm)->field.tqe_prev = (elm); \
+ (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
+} while (0)
+
+#define TAILQ_REMOVE(head, elm, field) do { \
+ if (((elm)->field.tqe_next) != NULL) \
+ (elm)->field.tqe_next->field.tqe_prev = \
+ (elm)->field.tqe_prev; \
+ else \
+ (head)->tqh_last = (elm)->field.tqe_prev; \
+ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \
+} while (0)
+
+#define TAILQ_REPLACE(head, elm, elm2, field) do { \
+ if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \
+ (elm2)->field.tqe_next->field.tqe_prev = \
+ &(elm2)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm2)->field.tqe_next; \
+ (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
+ *(elm2)->field.tqe_prev = (elm2); \
+} while (0)
+
+/*
+ * Circular queue definitions.
+ */
+#define CIRCLEQ_HEAD(name, type) \
+struct name { \
+ struct type *cqh_first; /* first element */ \
+ struct type *cqh_last; /* last element */ \
+}
+
+#define CIRCLEQ_HEAD_INITIALIZER(head) \
+ { CIRCLEQ_END(&head), CIRCLEQ_END(&head) }
+
+#define CIRCLEQ_ENTRY(type) \
+struct { \
+ struct type *cqe_next; /* next element */ \
+ struct type *cqe_prev; /* previous element */ \
+}
+
+/*
+ * Circular queue access methods
+ */
+#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
+#define CIRCLEQ_LAST(head) ((head)->cqh_last)
+#define CIRCLEQ_END(head) ((void *)(head))
+#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next)
+#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev)
+#define CIRCLEQ_EMPTY(head) \
+ (CIRCLEQ_FIRST(head) == CIRCLEQ_END(head))
+
+#define CIRCLEQ_FOREACH(var, head, field) \
+ for((var) = CIRCLEQ_FIRST(head); \
+ (var) != CIRCLEQ_END(head); \
+ (var) = CIRCLEQ_NEXT(var, field))
+
+#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
+ for((var) = CIRCLEQ_LAST(head); \
+ (var) != CIRCLEQ_END(head); \
+ (var) = CIRCLEQ_PREV(var, field))
+
+/*
+ * Circular queue functions.
+ */
+#define CIRCLEQ_INIT(head) do { \
+ (head)->cqh_first = CIRCLEQ_END(head); \
+ (head)->cqh_last = CIRCLEQ_END(head); \
+} while (0)
+
+#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ (elm)->field.cqe_next = (listelm)->field.cqe_next; \
+ (elm)->field.cqe_prev = (listelm); \
+ if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (listelm)->field.cqe_next->field.cqe_prev = (elm); \
+ (listelm)->field.cqe_next = (elm); \
+} while (0)
+
+#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
+ (elm)->field.cqe_next = (listelm); \
+ (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
+ if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (listelm)->field.cqe_prev->field.cqe_next = (elm); \
+ (listelm)->field.cqe_prev = (elm); \
+} while (0)
+
+#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
+ (elm)->field.cqe_next = (head)->cqh_first; \
+ (elm)->field.cqe_prev = CIRCLEQ_END(head); \
+ if ((head)->cqh_last == CIRCLEQ_END(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (head)->cqh_first->field.cqe_prev = (elm); \
+ (head)->cqh_first = (elm); \
+} while (0)
+
+#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.cqe_next = CIRCLEQ_END(head); \
+ (elm)->field.cqe_prev = (head)->cqh_last; \
+ if ((head)->cqh_first == CIRCLEQ_END(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (head)->cqh_last->field.cqe_next = (elm); \
+ (head)->cqh_last = (elm); \
+} while (0)
+
+#define CIRCLEQ_REMOVE(head, elm, field) do { \
+ if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \
+ (head)->cqh_last = (elm)->field.cqe_prev; \
+ else \
+ (elm)->field.cqe_next->field.cqe_prev = \
+ (elm)->field.cqe_prev; \
+ if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \
+ (head)->cqh_first = (elm)->field.cqe_next; \
+ else \
+ (elm)->field.cqe_prev->field.cqe_next = \
+ (elm)->field.cqe_next; \
+} while (0)
+
+#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \
+ if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \
+ CIRCLEQ_END(head)) \
+ (head).cqh_last = (elm2); \
+ else \
+ (elm2)->field.cqe_next->field.cqe_prev = (elm2); \
+ if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \
+ CIRCLEQ_END(head)) \
+ (head).cqh_first = (elm2); \
+ else \
+ (elm2)->field.cqe_prev->field.cqe_next = (elm2); \
+} while (0)
+
+#endif /* !_SYS_QUEUE_H_ */
diff --git a/trunk/main/minimime/test.sh b/trunk/main/minimime/test.sh
new file mode 100755
index 000000000..1beca0b74
--- /dev/null
+++ b/trunk/main/minimime/test.sh
@@ -0,0 +1,54 @@
+#!/bin/sh
+# MiniMIME test cases
+
+[ ! -x ./tests/parse -o ! -x ./tests/create ] && {
+ echo "You need to compile the test suite first to accomplish tests"
+ exit 1
+}
+
+LD_LIBRARY_PATH=${PWD}
+export LD_LIBRARY_PATH
+
+DIRECTORY=${1:-tests/messages}
+FILES=${2:-"*"}
+
+TESTS=0
+F_ERRORS=0
+F_INVALID=""
+M_ERRORS=0
+M_INVALID=""
+for f in ${DIRECTORY}/${FILES}; do
+ if [ -f "${f}" ]; then
+ TESTS=$((TESTS + 2))
+ echo -n "Running PARSER test for $f (file)... "
+ output=`./tests/parse $f 2>&1`
+ [ $? != 0 ] && {
+ echo "FAILED ($output)"
+ F_ERRORS=$((F_ERRORS + 1))
+ F_INVALID="${F_INVALID} ${f} "
+ } || {
+ echo "PASSED"
+ }
+ echo -n "Running PARSER test for $f (memory)... "
+ output=`./tests/parse -m $f 2>&1`
+ [ $? != 0 ] && {
+ echo "FAILED ($output)"
+ M_ERRORS=$((M_ERRORS + 1))
+ M_INVALID="${M_INVALID} ${f} "
+ } || {
+ echo "PASSED"
+ }
+ fi
+done
+
+echo "Ran a total of ${TESTS} tests"
+
+if [ ${F_ERRORS} -gt 0 ]; then
+ echo "!! ${F_ERRORS} messages had errors in file based parsing"
+ echo "-> ${F_INVALID}"
+fi
+if [ ${M_ERRORS} -gt 0 ]; then
+ echo "!! ${F_ERRORS} messages had errors in memory based parsing"
+fi
+
+unset LD_LIBRARY_PATH
diff --git a/trunk/main/minimime/tests/Makefile b/trunk/main/minimime/tests/Makefile
new file mode 100644
index 000000000..ae460c6b2
--- /dev/null
+++ b/trunk/main/minimime/tests/Makefile
@@ -0,0 +1,18 @@
+BINARIES=parse create
+CFLAGS=-Wall -ggdb -g3 -I..
+LDFLAGS=-L..
+LIBS=-lmmime
+CC=gcc
+
+all: parse create
+
+parse: parse.o
+ $(CC) -o parse parse.o $(LDFLAGS) $(LIBS)
+
+create: create.o
+ $(CC) -o create create.o $(LDFLAGS) $(LIBS)
+
+clean:
+ rm -f $(BINARIES)
+ rm -f *.o
+ rm -f *.core
diff --git a/trunk/main/minimime/tests/create.c b/trunk/main/minimime/tests/create.c
new file mode 100644
index 000000000..c881f17f7
--- /dev/null
+++ b/trunk/main/minimime/tests/create.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2004 Jann Fischer. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * MiniMIME test program - create.c
+ *
+ * Creates a MIME message of the given MIME parts
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <getopt.h>
+
+#include "mm.h"
+
+const char *progname;
+
+void
+usage(void)
+{
+ fprintf(stderr,
+ "MiniMIME test suite\n"
+ "USAGE: %s <part> [<part_2>[<part_N>[...]]]\n",
+ progname
+ );
+}
+
+void
+print_error(void)
+{
+ fprintf(stderr, "ERROR: %s\n", mm_error_string());
+}
+
+int
+main(int argc, char **argv)
+{
+ MM_CTX *ctx;
+ struct mm_mimepart *part;
+ char *data;
+ size_t length;
+ int i;
+
+ progname = argv[0];
+
+ if (argc < 2) {
+ usage();
+ exit(1);
+ }
+
+ mm_library_init();
+
+ ctx = mm_context_new();
+
+ part = mm_mimepart_new();
+ mm_context_attachpart(ctx, part);
+ mm_envelope_setheader(ctx, "From", "foo@bar.com");
+
+ for (i=1; i < argc; i++) {
+ part = mm_mimepart_fromfile(argv[i]);
+ if (part == NULL) {
+ print_error();
+ exit(1);
+ }
+ mm_context_attachpart(ctx, part);
+ }
+
+ if (mm_context_flatten(ctx, &data, &length, 0) == -1) {
+ print_error();
+ exit(1);
+ }
+
+ printf("%s", data);
+
+ exit(0);
+}
diff --git a/trunk/main/minimime/tests/messages/test1.txt b/trunk/main/minimime/tests/messages/test1.txt
new file mode 100644
index 000000000..2c24b1972
--- /dev/null
+++ b/trunk/main/minimime/tests/messages/test1.txt
@@ -0,0 +1,50 @@
+Return-Path: <rezine@hannover.ccc.de>
+X-Original-To: test@mistrust.net
+Delivered-To: rezine@hannover.ccc.de
+Received: from thinktank.niedersachsen.de (thinktank.niedersachsen.de [195.37.192.218])
+ (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits))
+ (Client did not present a certificate)
+ by gost.hannover.ccc.de (Postfix) with ESMTP id 90EDEBBF2
+ for <test@mistrust.net>; Sun, 24 Aug 2003 16:05:29 +0200 (CEST)
+Date: Sun, 24 Aug 2003 15:49:15 +0200
+From: Jann Fischer <rezine@hannover.ccc.de>
+To: test@mistrust.net
+Subject: Test
+Message-Id: <20030824154915.12cb3f85.rezine@hannover.ccc.de>
+Organization: Chaos Computer Club Hannover
+X-Mailer: Who-Cares 5.23
+Mime-Version: 1.0
+Content-Type: multipart/encrypted; protocol="application/pgp-encrypted";
+ boundary="=.2S1ZDSX8ir3lbt"
+
+--=.2S1ZDSX8ir3lbt
+Content-Type: application/pgp-encrypted
+
+Version: 1
+
+--=.2S1ZDSX8ir3lbt
+Content-Type: application/octet-stream
+
+-----BEGIN PGP MESSAGE-----
+Version: GnuPG v1.2.2 (OpenBSD)
+
+hQEOA3TvLJ6KBZLBEAP/cl4DiRH5+8S7/kP2BIVdDavHJ9cwHh8awGoyddhMKPJ3
+2558r8MKT0Etjpo649O5WUvT5Z2Jcp12+dTPlAC1kvoIjNNk8+Oe3JCREz/pXYnm
+5ANSCThVYSS34jppgT3NsqiV8sQK3e+Nq/NY7SoKVAV37L0fU4HHozcDZfXqOLsE
+AJgfxjRjjEazPHmgTTu8Pnt5gmlxyP35Yy0pl+gJmboG3Cn5WBcD/rrQf8oiwrB6
+Vak2Hk9TNU7hDO2IRolz4wUfkId47SK31PdhDLBnNPWn6LNWHd+G4hI97e+xeqLW
+dpG7Li5CdP0gfuHx2ux9Y5buWVVtqPhdDUlRaIBfM7Fu0sENAeREANAtdPHn0yTf
+V4T5NvImY3gXgLST5wNm3Ft+4nIDZrcnSy04x4faTLFBOcY95W0O1omILHyN5Ste
+Le5NhXhQRKyl6ebXtIvEOsJOK4NT6JaUF20l4yvgf0AnetG9Pbzc37mRqmE6Fb8O
+h/De3iqw7dexaQc+LaD3XTmvPyyDK2aI4cXOdc9WOzrWR7+9iEiY32SFsQWMRMZJ
+GdKkGk22K2p7MPFaU3MHQ3Af+WCN4mRW8SurFxH1379Y5e1IPfTeL6OBkj8hHilX
+Y+Y7523ADiStJsONIZPBXJVhZ/VAJ+jL+T1/Xht10VsJcWAY8A9tP+jNgyg8dh+J
+JgWVchQOZipdftYwR7w5GkhL2Nc5NYBJBg4DFd9g2nnwuzaAKYO5kMTzEmm9KOYq
+0DC5ukok4SGDwWPUIogNHmaSnFr723hYuJC7DwSxHXVG3VxxF78u1gzEnImOWRsf
+1RzGb7b8Lf7Rj98H5cNiZ55BXAmidjm7WghCLsT2GvxviqQoRIJ2h/WHM0Bl2v3F
+Dpa3N01p2NIIgQLRoXXyBCZTwGOH4y9nBj5PU7vzzSrMweHHt1BwHXcqItCyWFXX
+2tj4//Dyw3Lw/L5xGxYRP1Q=
+=fSLd
+-----END PGP MESSAGE-----
+
+--=.2S1ZDSX8ir3lbt--
diff --git a/trunk/main/minimime/tests/messages/test2.txt b/trunk/main/minimime/tests/messages/test2.txt
new file mode 100644
index 000000000..2c24b1972
--- /dev/null
+++ b/trunk/main/minimime/tests/messages/test2.txt
@@ -0,0 +1,50 @@
+Return-Path: <rezine@hannover.ccc.de>
+X-Original-To: test@mistrust.net
+Delivered-To: rezine@hannover.ccc.de
+Received: from thinktank.niedersachsen.de (thinktank.niedersachsen.de [195.37.192.218])
+ (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits))
+ (Client did not present a certificate)
+ by gost.hannover.ccc.de (Postfix) with ESMTP id 90EDEBBF2
+ for <test@mistrust.net>; Sun, 24 Aug 2003 16:05:29 +0200 (CEST)
+Date: Sun, 24 Aug 2003 15:49:15 +0200
+From: Jann Fischer <rezine@hannover.ccc.de>
+To: test@mistrust.net
+Subject: Test
+Message-Id: <20030824154915.12cb3f85.rezine@hannover.ccc.de>
+Organization: Chaos Computer Club Hannover
+X-Mailer: Who-Cares 5.23
+Mime-Version: 1.0
+Content-Type: multipart/encrypted; protocol="application/pgp-encrypted";
+ boundary="=.2S1ZDSX8ir3lbt"
+
+--=.2S1ZDSX8ir3lbt
+Content-Type: application/pgp-encrypted
+
+Version: 1
+
+--=.2S1ZDSX8ir3lbt
+Content-Type: application/octet-stream
+
+-----BEGIN PGP MESSAGE-----
+Version: GnuPG v1.2.2 (OpenBSD)
+
+hQEOA3TvLJ6KBZLBEAP/cl4DiRH5+8S7/kP2BIVdDavHJ9cwHh8awGoyddhMKPJ3
+2558r8MKT0Etjpo649O5WUvT5Z2Jcp12+dTPlAC1kvoIjNNk8+Oe3JCREz/pXYnm
+5ANSCThVYSS34jppgT3NsqiV8sQK3e+Nq/NY7SoKVAV37L0fU4HHozcDZfXqOLsE
+AJgfxjRjjEazPHmgTTu8Pnt5gmlxyP35Yy0pl+gJmboG3Cn5WBcD/rrQf8oiwrB6
+Vak2Hk9TNU7hDO2IRolz4wUfkId47SK31PdhDLBnNPWn6LNWHd+G4hI97e+xeqLW
+dpG7Li5CdP0gfuHx2ux9Y5buWVVtqPhdDUlRaIBfM7Fu0sENAeREANAtdPHn0yTf
+V4T5NvImY3gXgLST5wNm3Ft+4nIDZrcnSy04x4faTLFBOcY95W0O1omILHyN5Ste
+Le5NhXhQRKyl6ebXtIvEOsJOK4NT6JaUF20l4yvgf0AnetG9Pbzc37mRqmE6Fb8O
+h/De3iqw7dexaQc+LaD3XTmvPyyDK2aI4cXOdc9WOzrWR7+9iEiY32SFsQWMRMZJ
+GdKkGk22K2p7MPFaU3MHQ3Af+WCN4mRW8SurFxH1379Y5e1IPfTeL6OBkj8hHilX
+Y+Y7523ADiStJsONIZPBXJVhZ/VAJ+jL+T1/Xht10VsJcWAY8A9tP+jNgyg8dh+J
+JgWVchQOZipdftYwR7w5GkhL2Nc5NYBJBg4DFd9g2nnwuzaAKYO5kMTzEmm9KOYq
+0DC5ukok4SGDwWPUIogNHmaSnFr723hYuJC7DwSxHXVG3VxxF78u1gzEnImOWRsf
+1RzGb7b8Lf7Rj98H5cNiZ55BXAmidjm7WghCLsT2GvxviqQoRIJ2h/WHM0Bl2v3F
+Dpa3N01p2NIIgQLRoXXyBCZTwGOH4y9nBj5PU7vzzSrMweHHt1BwHXcqItCyWFXX
+2tj4//Dyw3Lw/L5xGxYRP1Q=
+=fSLd
+-----END PGP MESSAGE-----
+
+--=.2S1ZDSX8ir3lbt--
diff --git a/trunk/main/minimime/tests/messages/test3.txt b/trunk/main/minimime/tests/messages/test3.txt
new file mode 100644
index 000000000..082b6fb61
--- /dev/null
+++ b/trunk/main/minimime/tests/messages/test3.txt
@@ -0,0 +1,12 @@
+From: Jann Fischer <rezine@criminology.de>
+To: cipherlist <cipherlist@mistrust.net>
+Subject: Foobar
+Date: blahblah
+MIME-Version: 1.0 (MiniMIME)
+Content-Type: multipart/mixed; boundary="abcd"
+
+--abcd
+Content-Type: plain/text;
+
+This is a test :->
+--abcd--
diff --git a/trunk/main/minimime/tests/messages/test4.txt b/trunk/main/minimime/tests/messages/test4.txt
new file mode 100644
index 000000000..a08246939
--- /dev/null
+++ b/trunk/main/minimime/tests/messages/test4.txt
@@ -0,0 +1,168 @@
+X-Envelope-From: <511-bounces@hannover.ccc.de>
+X-Envelope-To: <rezine@criminology.de>
+X-Delivery-Time: 1070263752
+Received: from gost.hannover.ccc.de (hannover.ccc.de [62.48.71.164])
+ by mailin.webmailer.de (8.12.10/8.12.10) with ESMTP id hB17TAUR020052
+ for <rezine@criminology.de>; Mon, 1 Dec 2003 08:29:10 +0100 (MET)
+Received: from localhost.hannover.ccc.de (unknown [127.0.0.1])
+ by gost.hannover.ccc.de (Postfix) with ESMTP
+ id 092C8BC81; Mon, 1 Dec 2003 08:29:23 +0100 (CET)
+X-Original-To: 511@hannover.ccc.de
+Delivered-To: 511@hannover.ccc.de
+Received: from sbapp3 (unknown [211.157.36.9])
+ by gost.hannover.ccc.de (Postfix) with ESMTP id 3F93ABC7C
+ for <511@hannover.ccc.de>; Mon, 1 Dec 2003 08:29:12 +0100 (CET)
+From: "Vanessa Lintner" <reply@seekercenter.net>
+To: 511@hannover.ccc.de
+Date: Mon, 1 Dec 2003 15:30:57 +0800
+X-Priority: 3
+X-Library: Indy 8.0.25
+Message-Id: <20031201072912.3F93ABC7C@gost.hannover.ccc.de>
+Subject: [CCC511] http://lists.hannover.ccc.de
+X-BeenThere: 511@hannover.ccc.de
+X-Mailman-Version: 2.1.2
+Precedence: list
+Reply-To: Vanessa Lintner <vanessa@seekercenter.net>,
+ Oeffentliche Mailingliste des C3H <511@hannover.ccc.de>
+List-Id: Oeffentliche Mailingliste des C3H <511.hannover.ccc.de>
+List-Unsubscribe: <http://hannover.ccc.de/mailman/listinfo/511>,
+ <mailto:511-request@hannover.ccc.de?subject=unsubscribe>
+List-Post: <mailto:511@hannover.ccc.de>
+List-Help: <mailto:511-request@hannover.ccc.de?subject=help>
+List-Subscribe: <http://hannover.ccc.de/mailman/listinfo/511>,
+ <mailto:511-request@hannover.ccc.de?subject=subscribe>
+Content-Type: multipart/mixed; boundary="===============14807035762661644=="
+Sender: 511-bounces@hannover.ccc.de
+Errors-To: 511-bounces@hannover.ccc.de
+
+--===============14807035762661644==
+Content-Type: text/html;
+
+<html>
+<head>
+<title></title>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+<style type="text/css">
+.stbtm {
+ BACKGROUND-COLOR:#cecbde; BORDER-BOTTOM: #665b8e 1px solid; BORDER-LEFT: #ffffff 1px solid; BORDER-RIGHT: #665b8e 1px solid; BORDER-TOP: #ffffff 1px solid; COLOR: #000000; FONT-SIZE: 12pt; HEIGHT: 26px; WIDTH: 120px; clip: rect( )}
+.stedit {
+ background-color:#484C68; white-space: nowrap; border: #000000; BORDER-BOTTOM: #ffffff 1px solid; BORDER-LEFT: #ffffff 1px solid; BORDER-RIGHT: #ffffff 1px solid; BORDER-TOP: #ffffff 1px solid; FONT-SIZE: 10pt; color: #CCCCCC; font-weight: bold}
+
+</style>
+</head>
+<BODY leftMargin=0 onload="" topMargin=0 marginheight="0" marginwidth="0" bgcolor="#FFFFFF">
+ <table border="0" cellspacing="0" cellpadding="0" width="580">
+ <tr>
+ <td width="20" rowspan="2">&nbsp;</td>
+ <td colspan="3">
+ <table border="0" cellspacing="0" cellpadding="0" align="left" width="560">
+ <tr>
+ <td width="330" height="307">
+ <table width="330" border="0" cellspacing="0" cellpadding="0" background="http://www.imagespool.com/skbmp/letter_01.gif" height="307">
+ <tr>
+ <td>
+ <p> <font face=Arial size=2> </font> <font face=Arial size=2><font face="Verdana, Arial, Helvetica, sans-serif" color="#000000">Hello,<br>
+ <br>
+ I have visited <a href='http://lists.hannover.ccc.de'>lists.hannover.ccc.de</a>
+ and noticed that your website is not listed on some search
+ engines. I am sure that through our service the number of
+ people who visit your website will definitely increase.
+ <a target=_blank href="http://www.seekercenter.net/index.php">SeekerCenter</a>
+ is a unique technology that instantly submits your website
+ to over 500,000 search engines and directories -- a really
+ low-cost and effective way to advertise your site. For more
+ details please go to <a target=_blank href="http://www.seekercenter.net/index.php">SeekerCenter.net</a>.<br>
+ <br>
+ Give your website maximum exposure today!<br>
+ Looking forward to hearing from you.<br>
+ <br>
+ </font></font>
+ <table border=0 width=100%>
+ <tr>
+ <td width=50%> <font face="Arial" color="#000000" size="2">Best
+ Regards,<br>
+ Vanessa Lintner<br>
+ Sales &amp; Marketing <br>
+ <a target=_blank href="http://www.seekercenter.net/index.php">www.SeekerCenter.net</a></font>
+ <TD><td width=50%>
+ <div align="center" valign=middle>
+ <form target=_blank action=http://www.seekercenter.net method=POST>
+ <input type="submit" name="Submit" value="Signup Now!!!" class="stbtm">
+ </form>
+ </div>
+ </TD>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+ </td>
+ <td width="250" height="64" valign="middle">
+ <table width="230" border="0" cellspacing="0" cellpadding="0">
+ <tr>
+ <td colspan="3" height="2"></td>
+ </tr>
+ <tr>
+ <td colspan="3"><img src="http://report.imagespool.com/report_email.php?s=1&e=511@hannover.ccc.de" border=0 width=0 height=0>
+ <p><img src="http://www.imagespool.com/skbmp/letter_04.gif" height="12"></p>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="3"><img src="http://www.imagespool.com/skbmp/letter_05.gif" height="127"><img src="http://ww2.imagespool.com/1/9/b/0r066.jpg" width="177" height="127"><img src="http://www.imagespool.com/skbmp/letter_07.gif" width="33" height="127"></td>
+ </tr>
+ <tr>
+ <td colspan="3" height="92" background="http://www.imagespool.com/skbmp/letter_08.gif" valign="bottom">
+ <table width="230" border="0" cellspacing="0" cellpadding="0" height="92">
+ <tr>
+ <td width="36" height="43">&nbsp;</td>
+ <td width="157" height="43">&nbsp;</td>
+ <td width="134" height="43">&nbsp;</td>
+ </tr>
+ <tr>
+ <td width="36" height="2">&nbsp;</td>
+ <td width="157" height="2">&nbsp;</td>
+ <td width="134" height="2">&nbsp;</td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ <tr> </tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="3">
+ <table width="560" border="0" cellspacing="0" cellpadding="1" bordercolor="0">
+ <tr>
+ <td>&nbsp;</td>
+ </tr>
+ <tr>
+ <td bgcolor="#EFEFEF"><font face="Verdana, Arial, Helvetica, sans-serif" size="1">You
+ are receiving this email because you opted-in to receive special
+ offers through a partner website. If you feel that you received
+ this email in error or do not wish to receive additional special
+ offers, please enter your email address here and click the button
+ of &quot;Remove Me&quot;: <a href="http://www.seekercenter.net/remove.php?email=511@hannover.ccc.de">
+ <img src="http://www.imagespool.com/skbmp/removeme.gif" width="73" height="17" border="0"></a>
+ </font></td>
+ </tr>
+ </table>
+ </td>
+ </tr>
+ </table>
+</body>
+</html>
+
+--===============14807035762661644==
+Content-Type: text/plain; charset="iso-8859-1"
+MIME-Version: 1.0
+Content-Transfer-Encoding: quoted-printable
+Content-Disposition: inline
+
+_______________________________________________
+511 mailing list
+511@hannover.ccc.de
+--===============14807035762661644==--
diff --git a/trunk/main/minimime/tests/messages/test5.txt b/trunk/main/minimime/tests/messages/test5.txt
new file mode 100644
index 000000000..5e4cdb17d
--- /dev/null
+++ b/trunk/main/minimime/tests/messages/test5.txt
@@ -0,0 +1,44 @@
+Return-Path: <rezine@criminology.de>
+X-Original-To: rezine@mistrust.net
+Delivered-To: rezine@hannover.ccc.de
+Received: from thinktank.niedersachsen.de (thinktank.niedersachsen.de [195.37.192.218])
+ (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits))
+ (Client did not present a certificate)
+ by gost.hannover.ccc.de (Postfix) with ESMTP id 79E9BBC7C
+ for <rezine@mistrust.net>; Wed, 24 Dec 2003 13:35:36 +0100 (CET)
+Received: from thinktank.niedersachsen.de (localhost [127.0.0.1])
+ by thinktank.niedersachsen.de (8.12.9/8.12.2) with SMTP id hBOCZBFU029588
+ for <rezine@mistrust.net>; Wed, 24 Dec 2003 13:35:11 +0100 (CET)
+Date: Wed, 24 Dec 2003 13:35:11 +0100
+From: Jann Fischer <rezine@criminology.de>
+To: rezine@mistrust.net
+Subject: Test
+Message-Id: <20031224133511.5f4b6d9b.rezine@criminology.de>
+X-Mailer: Who Cares 5.23
+Mime-Version: 1.0
+Content-Type: multipart/mixed;
+ boundary="Multipart_Wed__24_Dec_2003_13:35:11_+0100_00148800"
+
+This is a multi-part message in MIME format.
+
+--Multipart_Wed__24_Dec_2003_13:35:11_+0100_00148800
+Content-Type: text/plain; charset=US-ASCII
+Content-Transfer-Encoding: 7bit
+
+Test
+
+--
+Be careful who you follow.
+0x6D839821 | FA8C 3663 9906 D8C3 AC16 F7C4 66E0 F351 6D83 9821
+
+--Multipart_Wed__24_Dec_2003_13:35:11_+0100_00148800
+Content-Type: application/octet-stream;
+ name="bar.c"
+Content-Disposition: attachment;
+ filename="bar.c"
+Content-Transfer-Encoding: base64
+
+I2luY2x1ZGUgPHN0ZGlvLmg+Cgp2b2lkCm1haW4oaW50IGFyZ2MsIGNoYXIgKiphcmd2KQp7CgkJ
+cHJpbnRmKCIlc1xuIiwgYXJndlswXSk7Cn0K
+
+--Multipart_Wed__24_Dec_2003_13:35:11_+0100_00148800--
diff --git a/trunk/main/minimime/tests/messages/test6.txt b/trunk/main/minimime/tests/messages/test6.txt
new file mode 100644
index 000000000..fb4e7a14c
--- /dev/null
+++ b/trunk/main/minimime/tests/messages/test6.txt
@@ -0,0 +1,12 @@
+From: Me
+Date: Foobar
+To: There
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary="abcde"
+
+--abcde
+Content-Type: text/plain
+
+Blah blah
+Blah
+--abcde--
diff --git a/trunk/main/minimime/tests/messages/test7.txt b/trunk/main/minimime/tests/messages/test7.txt
new file mode 100644
index 000000000..1cda11e3f
--- /dev/null
+++ b/trunk/main/minimime/tests/messages/test7.txt
@@ -0,0 +1,64 @@
+Return-Path: MAILER-DAEMON
+Received: from chaos.verfassungsschutz.de (localhost [IPv6:::1])
+ by chaos.verfassungsschutz.de (8.12.7/8.12.2) with ESMTP id h2EKV1oM031761
+ for <jfi@chaos.verfassungsschutz.de>; Fri, 14 Mar 2003 21:31:18 +0100 (CET)
+Received: from localhost (localhost)
+ by chaos.verfassungsschutz.de (8.12.7/8.12.2/Submit) id h2BNU1vr029177;
+ Wed, 12 Mar 2003 00:35:01 +0100 (CET)
+Date: Wed, 12 Mar 2003 00:35:01 +0100 (CET)
+From: Mail Delivery Subsystem <MAILER-DAEMON@chaos.verfassungsschutz.de>
+Message-Id: <200303112335.h2BNU1vr029177@chaos.verfassungsschutz.de>
+To: jfi@chaos.verfassungsschutz.de
+MIME-Version: 1.0
+Content-Type: multipart/report; report-type=delivery-status;
+ boundary="h2BNU1vr029177.1047425701/chaos.verfassungsschutz.de"
+Subject: Warning: could not send message for past 4 hours
+Auto-Submitted: auto-generated (warning-timeout)
+
+This is a MIME-encapsulated message
+
+--h2BNU1vr029177.1047425701/chaos.verfassungsschutz.de
+
+ **********************************************
+ ** THIS IS A WARNING MESSAGE ONLY **
+ ** YOU DO NOT NEED TO RESEND YOUR MESSAGE **
+ **********************************************
+
+The original message was received at Tue, 11 Mar 2003 20:18:36 +0100 (CET)
+from jfi@localhost
+
+ ----- Transcript of session follows -----
+451 4.4.1 reply: read error from localhost
+rezine@kommunism.us... Deferred: Connection timed out with localhost
+Warning: message still undelivered after 4 hours
+Will keep trying until message is 5 days old
+
+--h2BNU1vr029177.1047425701/chaos.verfassungsschutz.de
+Content-Type: message/delivery-status
+
+Reporting-MTA: dns; chaos.verfassungsschutz.de
+Arrival-Date: Tue, 11 Mar 2003 20:18:36 +0100 (CET)
+
+Final-Recipient: RFC822; rezine@kommunism.us
+Action: delayed
+Status: 4.4.2
+Last-Attempt-Date: Wed, 12 Mar 2003 00:35:01 +0100 (CET)
+Will-Retry-Until: Sun, 16 Mar 2003 20:18:36 +0100 (CET)
+
+--h2BNU1vr029177.1047425701/chaos.verfassungsschutz.de
+Content-Type: message/rfc822
+
+Return-Path: <jfi>
+Received: (from jfi@localhost)
+ by chaos.verfassungsschutz.de (8.12.7/8.12.2/Submit) id h2BJIawm025679
+ for rezine@kommunism.us; Tue, 11 Mar 2003 20:18:36 +0100 (CET)
+Date: Tue, 11 Mar 2003 20:18:36 +0100 (CET)
+From: Jann Fischer <jfi>
+Message-Id: <200303111918.h2BJIawm025679@chaos.verfassungsschutz.de>
+To: rezine@kommunism.us
+Subject: Test
+
+Test
+
+--h2BNU1vr029177.1047425701/chaos.verfassungsschutz.de--
+
diff --git a/trunk/main/minimime/tests/parse.c b/trunk/main/minimime/tests/parse.c
new file mode 100644
index 000000000..3d3bdf028
--- /dev/null
+++ b/trunk/main/minimime/tests/parse.c
@@ -0,0 +1,230 @@
+/*
+ * Copyright (c) 2004 Jann Fischer. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * MiniMIME test program - parse.c
+ *
+ * Parses any given messages
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <getopt.h>
+
+#include "mm.h"
+
+const char *progname;
+
+void
+usage(void)
+{
+ fprintf(stderr,
+ "MiniMIME test suite\n"
+ "Usage: %s [-m] <filename>\n\n"
+ " -m : use memory based scanning\n\n",
+ progname
+ );
+ exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+ MM_CTX *ctx;
+ struct mm_mimeheader *header, *lastheader = NULL;
+ struct mm_mimepart *part;
+ struct mm_content *ct;
+ int parts, i;
+ struct stat st;
+ int fd;
+ char *buf;
+ int scan_mode = 0;
+
+ progname = strdup(argv[0]);
+
+ while ((i = getopt(argc, argv, "m")) != -1) {
+ switch(i) {
+ case 'm':
+ scan_mode = 1;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ usage();
+ }
+
+#ifdef __HAVE_LEAK_DETECTION
+ /* Initialize memory leak detection if compiled in */
+ MM_leakd_init();
+#endif
+
+ /* Initialize MiniMIME library */
+ mm_library_init();
+
+ /* Register all default codecs (base64/qp) */
+ mm_codec_registerdefaultcodecs();
+
+ do {
+ /* Create a new context */
+ ctx = mm_context_new();
+
+ /* Parse a file into our context */
+ if (scan_mode == 0) {
+ i = mm_parse_file(ctx, argv[0], MM_PARSE_LOOSE, 0);
+ } else {
+ if (stat(argv[0], &st) == -1) {
+ err(1, "stat");
+ }
+
+ if ((fd = open(argv[0], O_RDONLY)) == -1) {
+ err(1, "open");
+ }
+
+ buf = (char *)malloc(st.st_size);
+ if (buf == NULL) {
+ err(1, "malloc");
+ }
+
+ if (read(fd, buf, st.st_size) != st.st_size) {
+ err(1, "read");
+ }
+
+ close(fd);
+ buf[st.st_size] = '\0';
+
+ i = mm_parse_mem(ctx, buf, MM_PARSE_LOOSE, 0);
+ }
+
+ if (i == -1 || mm_errno != MM_ERROR_NONE) {
+ printf("ERROR: %s at line %d\n", mm_error_string(), mm_error_lineno());
+ exit(1);
+ }
+
+ /* Get the number of MIME parts */
+ parts = mm_context_countparts(ctx);
+ if (parts == 0) {
+ printf("ERROR: got zero MIME parts, huh\n");
+ exit(1);
+ } else {
+ if (mm_context_iscomposite(ctx)) {
+ printf("Got %d MIME parts\n", parts - 1);
+ } else {
+ printf("Flat message (not multipart)\n");
+ }
+ }
+
+ /* Get the main MIME part */
+ part = mm_context_getpart(ctx, 0);
+ if (part == NULL) {
+ fprintf(stderr, "Could not get envelope part\n");
+ exit(1);
+ }
+
+ printf("Printing envelope headers:\n");
+ /* Print all headers */
+ lastheader = NULL;
+ while ((header = mm_mimepart_headers_next(part, &lastheader)) != NULL)
+ printf("%s: %s\n", header->name, header->value);
+
+ printf("%s\n", mm_content_tostring(part->type));
+ printf("\n");
+
+ ct = part->type;
+ assert(ct != NULL);
+
+ if (mm_context_iscomposite(ctx) == 0) {
+ printf("Printing body part for FLAT message:\n");
+ part = mm_context_getpart(ctx, 0);
+ printf("%s", part->body);
+ }
+
+ /* Loop through all MIME parts beginning with 1 */
+ for (i = 1; i < mm_context_countparts(ctx); i++) {
+ char *decoded;
+
+ printf("Printing headers for MIME part %d\n", i);
+
+ /* Get the current MIME entity */
+ part = mm_context_getpart(ctx, i);
+ if (part == NULL) {
+ fprintf(stderr, "Should have %d parts but "
+ "couldn't retrieve part %d",
+ mm_context_countparts(ctx), i);
+ exit(1);
+ }
+
+ /* Print all headers */
+ lastheader = NULL;
+ while ((header = mm_mimepart_headers_next(part, &lastheader)) != NULL)
+ printf("%s: %s\n", header->name, header->value);
+
+ printf("%s\n", mm_content_tostring(part->type));
+
+ /* Print MIME part body */
+ printf("\nPRINTING MESSAGE BODY (%d):\n%s\n", i, part->opaque_body);
+ decoded = mm_mimepart_decode(part);
+ if (decoded != NULL) {
+ printf("DECODED:\n%s\n", decoded);
+ free(decoded);
+ }
+ }
+
+ printf("RECONSTRUCTED MESSAGE:\n");
+
+ do {
+ char *env;
+ size_t env_len;
+
+ mm_context_flatten(ctx, &env, &env_len, 0);
+ printf("%s", env);
+ free(env);
+
+ } while (0);
+
+ mm_context_free(ctx);
+ ctx = NULL;
+
+#ifdef __HAVE_LEAK_DETECTION
+ MM_leakd_printallocated();
+#endif
+
+ } while (0);
+
+ return 0;
+}
diff --git a/trunk/main/netsock.c b/trunk/main/netsock.c
new file mode 100644
index 000000000..27def180c
--- /dev/null
+++ b/trunk/main/netsock.c
@@ -0,0 +1,208 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Kevin P. Fleming <kpfleming@digium.com>
+ * 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.
+ */
+
+/*! \file
+ *
+ * \brief Network socket handling
+ *
+ * \author Kevin P. Fleming <kpfleming@digium.com>
+ * \author Mark Spencer <markster@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#if defined (SOLARIS)
+#include <sys/sockio.h>
+#endif
+
+#include "asterisk/netsock.h"
+#include "asterisk/utils.h"
+
+struct ast_netsock {
+ ASTOBJ_COMPONENTS(struct ast_netsock);
+ struct sockaddr_in bindaddr;
+ int sockfd;
+ int *ioref;
+ struct io_context *ioc;
+ void *data;
+};
+
+struct ast_netsock_list {
+ ASTOBJ_CONTAINER_COMPONENTS(struct ast_netsock);
+ struct io_context *ioc;
+};
+
+static void ast_netsock_destroy(struct ast_netsock *netsock)
+{
+ ast_io_remove(netsock->ioc, netsock->ioref);
+ close(netsock->sockfd);
+ ast_free(netsock);
+}
+
+struct ast_netsock_list *ast_netsock_list_alloc(void)
+{
+ return ast_calloc(1, sizeof(struct ast_netsock_list));
+}
+
+int ast_netsock_init(struct ast_netsock_list *list)
+{
+ memset(list, 0, sizeof(*list));
+ ASTOBJ_CONTAINER_INIT(list);
+
+ return 0;
+}
+
+int ast_netsock_release(struct ast_netsock_list *list)
+{
+ ASTOBJ_CONTAINER_DESTROYALL(list, ast_netsock_destroy);
+ ASTOBJ_CONTAINER_DESTROY(list);
+
+ return 0;
+}
+
+struct ast_netsock *ast_netsock_find(struct ast_netsock_list *list,
+ struct sockaddr_in *sa)
+{
+ struct ast_netsock *sock = NULL;
+
+ ASTOBJ_CONTAINER_TRAVERSE(list, !sock, {
+ ASTOBJ_RDLOCK(iterator);
+ if (!inaddrcmp(&iterator->bindaddr, sa))
+ sock = iterator;
+ ASTOBJ_UNLOCK(iterator);
+ });
+
+ return sock;
+}
+
+struct ast_netsock *ast_netsock_bindaddr(struct ast_netsock_list *list, struct io_context *ioc, struct sockaddr_in *bindaddr, int tos, int cos, ast_io_cb callback, void *data)
+{
+ int netsocket = -1;
+ int *ioref;
+
+ struct ast_netsock *ns;
+ const int reuseFlag = 1;
+
+ /* Make a UDP socket */
+ netsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
+
+ if (netsocket < 0) {
+ ast_log(LOG_ERROR, "Unable to create network socket: %s\n", strerror(errno));
+ return NULL;
+ }
+ if (setsockopt(netsocket, SOL_SOCKET, SO_REUSEADDR, (char *)&reuseFlag, sizeof reuseFlag) < 0) {
+ ast_log(LOG_WARNING, "Error setting SO_REUSEADDR on sockfd '%d'\n", netsocket);
+ }
+ if (bind(netsocket,(struct sockaddr *)bindaddr, sizeof(struct sockaddr_in))) {
+ ast_log(LOG_ERROR, "Unable to bind to %s port %d: %s\n", ast_inet_ntoa(bindaddr->sin_addr), ntohs(bindaddr->sin_port), strerror(errno));
+ close(netsocket);
+ return NULL;
+ }
+
+ ast_netsock_set_qos(netsocket, tos, cos, "IAX2");
+
+ ast_enable_packet_fragmentation(netsocket);
+
+ if (!(ns = ast_calloc(1, sizeof(*ns)))) {
+ close(netsocket);
+ return NULL;
+ }
+
+ /* Establish I/O callback for socket read */
+ if (!(ioref = ast_io_add(ioc, netsocket, callback, AST_IO_IN, ns))) {
+ close(netsocket);
+ ast_free(ns);
+ return NULL;
+ }
+ ASTOBJ_INIT(ns);
+ ns->ioref = ioref;
+ ns->ioc = ioc;
+ ns->sockfd = netsocket;
+ ns->data = data;
+ memcpy(&ns->bindaddr, bindaddr, sizeof(ns->bindaddr));
+ ASTOBJ_CONTAINER_LINK(list, ns);
+
+ return ns;
+}
+
+int ast_netsock_set_qos(int netsocket, int tos, int cos, const char *desc)
+{
+ int res;
+
+ if ((res = setsockopt(netsocket, IPPROTO_IP, IP_TOS, &tos, sizeof(tos))))
+ ast_log(LOG_WARNING, "Unable to set %s TOS to %d, may be you have no root privileges\n", desc, tos);
+ else if (tos)
+ ast_verb(2, "Using %s TOS bits %d\n", desc, tos);
+
+#if defined(linux)
+ if (setsockopt(netsocket, SOL_SOCKET, SO_PRIORITY, &cos, sizeof(cos)))
+ ast_log(LOG_WARNING, "Unable to set %s CoS to %d\n", desc, cos);
+ else if (cos)
+ ast_verb(2, "Using %s CoS mark %d\n", desc, cos);
+#endif
+
+ return res;
+}
+
+
+struct ast_netsock *ast_netsock_bind(struct ast_netsock_list *list, struct io_context *ioc, const char *bindinfo, int defaultport, int tos, int cos, ast_io_cb callback, void *data)
+{
+ struct sockaddr_in sin;
+ char *tmp;
+ char *host;
+ char *port;
+ int portno;
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(defaultport);
+ tmp = ast_strdupa(bindinfo);
+
+ host = strsep(&tmp, ":");
+ port = tmp;
+
+ if (port && ((portno = atoi(port)) > 0))
+ sin.sin_port = htons(portno);
+
+ inet_aton(host, &sin.sin_addr);
+
+ return ast_netsock_bindaddr(list, ioc, &sin, tos, cos, callback, data);
+}
+
+int ast_netsock_sockfd(const struct ast_netsock *ns)
+{
+ return ns ? ns-> sockfd : -1;
+}
+
+const struct sockaddr_in *ast_netsock_boundaddr(const struct ast_netsock *ns)
+{
+ return &(ns->bindaddr);
+}
+
+void *ast_netsock_data(const struct ast_netsock *ns)
+{
+ return ns->data;
+}
+
+void ast_netsock_unref(struct ast_netsock *ns)
+{
+ ASTOBJ_UNREF(ns, ast_netsock_destroy);
+}
diff --git a/trunk/main/pbx.c b/trunk/main/pbx.c
new file mode 100644
index 000000000..3820c4615
--- /dev/null
+++ b/trunk/main/pbx.c
@@ -0,0 +1,7800 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006, 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.
+ */
+
+/*! \file
+ *
+ * \brief Core PBX routines.
+ *
+ * \author Mark Spencer <markster@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/_private.h"
+#include "asterisk/paths.h" /* use ast_config_AST_SYSTEM_NAME */
+#include <ctype.h>
+#include <time.h>
+#include <sys/time.h>
+#if defined(HAVE_SYSINFO)
+#include <sys/sysinfo.h>
+#endif
+#if defined(SOLARIS)
+#include <sys/loadavg.h>
+#endif
+
+#include "asterisk/lock.h"
+#include "asterisk/cli.h"
+#include "asterisk/pbx.h"
+#include "asterisk/channel.h"
+#include "asterisk/file.h"
+#include "asterisk/callerid.h"
+#include "asterisk/cdr.h"
+#include "asterisk/config.h"
+#include "asterisk/term.h"
+#include "asterisk/manager.h"
+#include "asterisk/ast_expr.h"
+#include "asterisk/linkedlists.h"
+#define SAY_STUBS /* generate declarations and stubs for say methods */
+#include "asterisk/say.h"
+#include "asterisk/utils.h"
+#include "asterisk/causes.h"
+#include "asterisk/musiconhold.h"
+#include "asterisk/app.h"
+#include "asterisk/devicestate.h"
+#include "asterisk/stringfields.h"
+#include "asterisk/event.h"
+#include "asterisk/hashtab.h"
+#include "asterisk/module.h"
+#include "asterisk/indications.h"
+
+/*!
+ * \note I M P O R T A N T :
+ *
+ * The speed of extension handling will likely be among the most important
+ * aspects of this PBX. The switching scheme as it exists right now isn't
+ * terribly bad (it's O(N+M), where N is the # of extensions and M is the avg #
+ * of priorities, but a constant search time here would be great ;-)
+ *
+ * A new algorithm to do searching based on a 'compiled' pattern tree is introduced
+ * here, and shows a fairly flat (constant) search time, even for over
+ * 1000 patterns. Might Still needs some work-- there are some fine points of the matching
+ * spec about tie-breaking based on the characters in character sets, but this
+ * should be do-able via the weight system currently being used.
+ *
+ * Also, using a hash table for context/priority name lookup can help prevent
+ * the find_extension routines from absorbing exponential cpu cycles. I've tested
+ * find_extension with red-black trees, which have O(log2(n)) speed. Right now,
+ * I'm using hash tables, which do searches (ideally) in O(1) time.
+ *
+ */
+
+#ifdef LOW_MEMORY
+#define EXT_DATA_SIZE 256
+#else
+#define EXT_DATA_SIZE 8192
+#endif
+
+#define SWITCH_DATA_LENGTH 256
+
+#define VAR_BUF_SIZE 4096
+
+#define VAR_NORMAL 1
+#define VAR_SOFTTRAN 2
+#define VAR_HARDTRAN 3
+
+#define BACKGROUND_SKIP (1 << 0)
+#define BACKGROUND_NOANSWER (1 << 1)
+#define BACKGROUND_MATCHEXTEN (1 << 2)
+#define BACKGROUND_PLAYBACK (1 << 3)
+
+AST_APP_OPTIONS(background_opts, {
+ AST_APP_OPTION('s', BACKGROUND_SKIP),
+ AST_APP_OPTION('n', BACKGROUND_NOANSWER),
+ AST_APP_OPTION('m', BACKGROUND_MATCHEXTEN),
+ AST_APP_OPTION('p', BACKGROUND_PLAYBACK),
+});
+
+#define WAITEXTEN_MOH (1 << 0)
+#define WAITEXTEN_DIALTONE (1 << 1)
+
+AST_APP_OPTIONS(waitexten_opts, {
+ AST_APP_OPTION_ARG('m', WAITEXTEN_MOH, 0),
+ AST_APP_OPTION_ARG('d', WAITEXTEN_DIALTONE, 0),
+});
+
+struct ast_context;
+struct ast_app;
+
+/*!
+ \brief ast_exten: An extension
+ The dialplan is saved as a linked list with each context
+ having it's own linked list of extensions - one item per
+ priority.
+*/
+struct ast_exten {
+ char *exten; /*!< Extension name */
+ int matchcid; /*!< Match caller id ? */
+ const char *cidmatch; /*!< Caller id to match for this extension */
+ int priority; /*!< Priority */
+ const char *label; /*!< Label */
+ struct ast_context *parent; /*!< The context this extension belongs to */
+ const char *app; /*!< Application to execute */
+ struct ast_app *cached_app; /*!< Cached location of application */
+ void *data; /*!< Data to use (arguments) */
+ void (*datad)(void *); /*!< Data destructor */
+ struct ast_exten *peer; /*!< Next higher priority with our extension */
+ struct ast_hashtab *peer_tree; /*!< Priorities list in tree form -- only on the head of the peer list */
+ struct ast_hashtab *peer_label_tree; /*!< labeled priorities in the peer list -- only on the head of the peer list */
+ const char *registrar; /*!< Registrar */
+ struct ast_exten *next; /*!< Extension with a greater ID */
+ char stuff[0];
+};
+
+/*! \brief ast_include: include= support in extensions.conf */
+struct ast_include {
+ const char *name;
+ const char *rname; /*!< Context to include */
+ const char *registrar; /*!< Registrar */
+ int hastime; /*!< If time construct exists */
+ struct ast_timing timing; /*!< time construct */
+ struct ast_include *next; /*!< Link them together */
+ char stuff[0];
+};
+
+/*! \brief ast_sw: Switch statement in extensions.conf */
+struct ast_sw {
+ char *name;
+ const char *registrar; /*!< Registrar */
+ char *data; /*!< Data load */
+ int eval;
+ AST_LIST_ENTRY(ast_sw) list;
+ char *tmpdata;
+ char stuff[0];
+};
+
+/*! \brief ast_ignorepat: Ignore patterns in dial plan */
+struct ast_ignorepat {
+ const char *registrar;
+ struct ast_ignorepat *next;
+ const char pattern[0];
+};
+
+/*! \brief match_char: forms a syntax tree for quick matching of extension patterns */
+struct match_char
+{
+ int is_pattern; /* the pattern started with '_' */
+ int deleted; /* if this is set, then... don't return it */
+ char *x; /* the pattern itself-- matches a single char */
+ int specificity; /* simply the strlen of x, or 10 for X, 9 for Z, and 8 for N; and '.' and '!' will add 11 ? */
+ struct match_char *alt_char;
+ struct match_char *next_char;
+ struct ast_exten *exten; /* attached to last char of a pattern for exten */
+};
+
+struct scoreboard /* make sure all fields are 0 before calling new_find_extension */
+{
+ int total_specificity;
+ int total_length;
+ char last_char; /* set to ! or . if they are the end of the pattern */
+ int canmatch; /* if the string to match was just too short */
+ struct match_char *node;
+ struct ast_exten *canmatch_exten;
+ struct ast_exten *exten;
+};
+
+/*! \brief ast_context: An extension context */
+struct ast_context {
+ ast_rwlock_t lock; /*!< A lock to prevent multiple threads from clobbering the context */
+ struct ast_exten *root; /*!< The root of the list of extensions */
+ struct ast_hashtab *root_tree; /*!< For exact matches on the extensions in the pattern tree, and for traversals of the pattern_tree */
+ struct match_char *pattern_tree; /*!< A tree to speed up extension pattern matching */
+ struct ast_context *next; /*!< Link them together */
+ struct ast_include *includes; /*!< Include other contexts */
+ struct ast_ignorepat *ignorepats; /*!< Patterns for which to continue playing dialtone */
+ const char *registrar; /*!< Registrar */
+ AST_LIST_HEAD_NOLOCK(, ast_sw) alts; /*!< Alternative switches */
+ ast_mutex_t macrolock; /*!< A lock to implement "exclusive" macros - held whilst a call is executing in the macro */
+ char name[0]; /*!< Name of the context */
+};
+
+
+/*! \brief ast_app: A registered application */
+struct ast_app {
+ int (*execute)(struct ast_channel *chan, void *data);
+ const char *synopsis; /*!< Synopsis text for 'show applications' */
+ const char *description; /*!< Description (help text) for 'show application &lt;name&gt;' */
+ AST_RWLIST_ENTRY(ast_app) list; /*!< Next app in list */
+ struct ast_module *module; /*!< Module this app belongs to */
+ char name[0]; /*!< Name of the application */
+};
+
+/*! \brief ast_state_cb: An extension state notify register item */
+struct ast_state_cb {
+ int id;
+ void *data;
+ ast_state_cb_type callback;
+ struct ast_state_cb *next;
+};
+
+/*! \brief Structure for dial plan hints
+
+ \note Hints are pointers from an extension in the dialplan to one or
+ more devices (tech/name)
+ - See \ref AstExtState
+*/
+struct ast_hint {
+ struct ast_exten *exten; /*!< Extension */
+ int laststate; /*!< Last known state */
+ struct ast_state_cb *callbacks; /*!< Callback list for this extension */
+ AST_RWLIST_ENTRY(ast_hint) list;/*!< Pointer to next hint in list */
+};
+
+static const struct cfextension_states {
+ int extension_state;
+ const char * const text;
+} extension_states[] = {
+ { AST_EXTENSION_NOT_INUSE, "Idle" },
+ { AST_EXTENSION_INUSE, "InUse" },
+ { AST_EXTENSION_BUSY, "Busy" },
+ { AST_EXTENSION_UNAVAILABLE, "Unavailable" },
+ { AST_EXTENSION_RINGING, "Ringing" },
+ { AST_EXTENSION_INUSE | AST_EXTENSION_RINGING, "InUse&Ringing" },
+ { AST_EXTENSION_ONHOLD, "Hold" },
+ { AST_EXTENSION_INUSE | AST_EXTENSION_ONHOLD, "InUse&Hold" }
+};
+
+struct statechange {
+ AST_LIST_ENTRY(statechange) entry;
+ char dev[0];
+};
+
+/*!
+ * \brief Data used by the device state thread
+ */
+static struct {
+ /*! Set to 1 to stop the thread */
+ unsigned int stop:1;
+ /*! The device state monitoring thread */
+ pthread_t thread;
+ /*! Lock for the state change queue */
+ ast_mutex_t lock;
+ /*! Condition for the state change queue */
+ ast_cond_t cond;
+ /*! Queue of state changes */
+ AST_LIST_HEAD_NOLOCK(, statechange) state_change_q;
+} device_state = {
+ .thread = AST_PTHREADT_NULL,
+};
+
+struct pbx_exception {
+ AST_DECLARE_STRING_FIELDS(
+ AST_STRING_FIELD(context); /*!< Context associated with this exception */
+ AST_STRING_FIELD(exten); /*!< Exten associated with this exception */
+ AST_STRING_FIELD(reason); /*!< The exception reason */
+ );
+
+ int priority; /*!< Priority associated with this exception */
+};
+
+static int pbx_builtin_answer(struct ast_channel *, void *);
+static int pbx_builtin_goto(struct ast_channel *, void *);
+static int pbx_builtin_hangup(struct ast_channel *, void *);
+static int pbx_builtin_background(struct ast_channel *, void *);
+static int pbx_builtin_wait(struct ast_channel *, void *);
+static int pbx_builtin_waitexten(struct ast_channel *, void *);
+static int pbx_builtin_keepalive(struct ast_channel *, void *);
+static int pbx_builtin_resetcdr(struct ast_channel *, void *);
+static int pbx_builtin_setamaflags(struct ast_channel *, void *);
+static int pbx_builtin_ringing(struct ast_channel *, void *);
+static int pbx_builtin_progress(struct ast_channel *, void *);
+static int pbx_builtin_congestion(struct ast_channel *, void *);
+static int pbx_builtin_busy(struct ast_channel *, void *);
+static int pbx_builtin_noop(struct ast_channel *, void *);
+static int pbx_builtin_gotoif(struct ast_channel *, void *);
+static int pbx_builtin_gotoiftime(struct ast_channel *, void *);
+static int pbx_builtin_execiftime(struct ast_channel *, void *);
+static int pbx_builtin_saynumber(struct ast_channel *, void *);
+static int pbx_builtin_saydigits(struct ast_channel *, void *);
+static int pbx_builtin_saycharacters(struct ast_channel *, void *);
+static int pbx_builtin_sayphonetic(struct ast_channel *, void *);
+static int matchcid(const char *cidpattern, const char *callerid);
+int pbx_builtin_setvar(struct ast_channel *, void *);
+void log_match_char_tree(struct match_char *node, char *prefix); /* for use anywhere */
+static int pbx_builtin_setvar_multiple(struct ast_channel *, void *);
+static int pbx_builtin_importvar(struct ast_channel *, void *);
+static void set_ext_pri(struct ast_channel *c, const char *exten, int pri);
+static void new_find_extension(const char *str, struct scoreboard *score, struct match_char *tree, int length, int spec, const char *callerid);
+static struct match_char *already_in_tree(struct match_char *current, char *pat);
+static struct match_char *add_exten_to_pattern_tree(struct ast_context *con, struct ast_exten *e1, int findonly);
+static struct match_char *add_pattern_node(struct ast_context *con, struct match_char *current, char *pattern, int is_pattern, int already, int specificity);
+static void create_match_char_tree(struct ast_context *con);
+static struct ast_exten *get_canmatch_exten(struct match_char *node);
+static void destroy_pattern_tree(struct match_char *pattern_tree);
+static int hashtab_compare_contexts(const void *ah_a, const void *ah_b);
+static int hashtab_compare_extens(const void *ha_a, const void *ah_b);
+static int hashtab_compare_exten_numbers(const void *ah_a, const void *ah_b);
+static int hashtab_compare_exten_labels(const void *ah_a, const void *ah_b);
+static unsigned int hashtab_hash_contexts(const void *obj);
+static unsigned int hashtab_hash_extens(const void *obj);
+static unsigned int hashtab_hash_priority(const void *obj);
+static unsigned int hashtab_hash_labels(const void *obj);
+
+/* labels, contexts are case sensitive priority numbers are ints */
+static int hashtab_compare_contexts(const void *ah_a, const void *ah_b)
+{
+ const struct ast_context *ac = ah_a;
+ const struct ast_context *bc = ah_b;
+ /* assume context names are registered in a string table! */
+ return strcmp(ac->name, bc->name);
+}
+
+static int hashtab_compare_extens(const void *ah_a, const void *ah_b)
+{
+ const struct ast_exten *ac = ah_a;
+ const struct ast_exten *bc = ah_b;
+ int x = strcmp(ac->exten, bc->exten);
+ if (x) /* if exten names are diff, then return */
+ return x;
+ /* but if they are the same, do the cidmatch values match? */
+ if (ac->matchcid && bc->matchcid) {
+ return strcmp(ac->cidmatch,bc->cidmatch);
+ } else if (!ac->matchcid && !bc->matchcid) {
+ return 0; /* if there's no matchcid on either side, then this is a match */
+ } else {
+ return 1; /* if there's matchcid on one but not the other, they are different */
+ }
+}
+
+static int hashtab_compare_exten_numbers(const void *ah_a, const void *ah_b)
+{
+ const struct ast_exten *ac = ah_a;
+ const struct ast_exten *bc = ah_b;
+ return ac->priority != bc->priority;
+}
+
+static int hashtab_compare_exten_labels(const void *ah_a, const void *ah_b)
+{
+ const struct ast_exten *ac = ah_a;
+ const struct ast_exten *bc = ah_b;
+ return strcmp(ac->label, bc->label);
+}
+
+static unsigned int hashtab_hash_contexts(const void *obj)
+{
+ const struct ast_context *ac = obj;
+ return ast_hashtab_hash_string(ac->name);
+}
+
+static unsigned int hashtab_hash_extens(const void *obj)
+{
+ const struct ast_exten *ac = obj;
+ unsigned int x = ast_hashtab_hash_string(ac->exten);
+ unsigned int y = 0;
+ if (ac->matchcid)
+ y = ast_hashtab_hash_string(ac->cidmatch);
+ return x+y;
+}
+
+static unsigned int hashtab_hash_priority(const void *obj)
+{
+ const struct ast_exten *ac = obj;
+ return ast_hashtab_hash_int(ac->priority);
+}
+
+static unsigned int hashtab_hash_labels(const void *obj)
+{
+ const struct ast_exten *ac = obj;
+ return ast_hashtab_hash_string(ac->label);
+}
+
+
+AST_RWLOCK_DEFINE_STATIC(globalslock);
+static struct varshead globals = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
+
+static int autofallthrough = 1;
+static int extenpatternmatchnew = 0;
+
+/*! \brief Subscription for device state change events */
+static struct ast_event_sub *device_state_sub;
+
+AST_MUTEX_DEFINE_STATIC(maxcalllock);
+static int countcalls;
+static int totalcalls;
+
+static AST_RWLIST_HEAD_STATIC(acf_root, ast_custom_function);
+
+/*! \brief Declaration of builtin applications */
+static struct pbx_builtin {
+ char name[AST_MAX_APP];
+ int (*execute)(struct ast_channel *chan, void *data);
+ char *synopsis;
+ char *description;
+} builtins[] =
+{
+ /* These applications are built into the PBX core and do not
+ need separate modules */
+
+ { "Answer", pbx_builtin_answer,
+ "Answer a channel if ringing",
+ " Answer([delay]): If the call has not been answered, this application will\n"
+ "answer it. Otherwise, it has no effect on the call. If a delay is specified,\n"
+ "Asterisk will wait this number of milliseconds before returning to\n"
+ "the dialplan after answering the call.\n"
+ },
+
+ { "BackGround", pbx_builtin_background,
+ "Play an audio file while waiting for digits of an extension to go to.",
+ " Background(filename1[&filename2...][,options[,langoverride][,context]]):\n"
+ "This application will play the given list of files (do not put extension)\n"
+ "while waiting for an extension to be dialed by the calling channel. To\n"
+ "continue waiting for digits after this application has finished playing\n"
+ "files, the WaitExten application should be used. The 'langoverride' option\n"
+ "explicitly specifies which language to attempt to use for the requested sound\n"
+ "files. If a 'context' is specified, this is the dialplan context that this\n"
+ "application will use when exiting to a dialed extension."
+ " If one of the requested sound files does not exist, call processing will be\n"
+ "terminated.\n"
+ " Options:\n"
+ " s - Causes the playback of the message to be skipped\n"
+ " if the channel is not in the 'up' state (i.e. it\n"
+ " hasn't been answered yet). If this happens, the\n"
+ " application will return immediately.\n"
+ " n - Don't answer the channel before playing the files.\n"
+ " m - Only break if a digit hit matches a one digit\n"
+ " extension in the destination context.\n"
+ "This application sets the following channel variable upon completion:\n"
+ " BACKGROUNDSTATUS The status of the background attempt as a text string, one of\n"
+ " SUCCESS | FAILED\n"
+ },
+
+ { "Busy", pbx_builtin_busy,
+ "Indicate the Busy condition",
+ " Busy([timeout]): This application will indicate the busy condition to\n"
+ "the calling channel. If the optional timeout is specified, the calling channel\n"
+ "will be hung up after the specified number of seconds. Otherwise, this\n"
+ "application will wait until the calling channel hangs up.\n"
+ },
+
+ { "Congestion", pbx_builtin_congestion,
+ "Indicate the Congestion condition",
+ " Congestion([timeout]): This application will indicate the congestion\n"
+ "condition to the calling channel. If the optional timeout is specified, the\n"
+ "calling channel will be hung up after the specified number of seconds.\n"
+ "Otherwise, this application will wait until the calling channel hangs up.\n"
+ },
+
+ { "ExecIfTime", pbx_builtin_execiftime,
+ "Conditional application execution based on the current time",
+ " ExecIfTime(<times>,<weekdays>,<mdays>,<months>?appname[(appargs)]):\n"
+ "This application will execute the specified dialplan application, with optional\n"
+ "arguments, if the current time matches the given time specification.\n"
+ },
+
+ { "Goto", pbx_builtin_goto,
+ "Jump to a particular priority, extension, or context",
+ " Goto([[context,]extension,]priority): This application will set the current\n"
+ "context, extension, and priority in the channel structure. After it completes, the\n"
+ "pbx engine will continue dialplan execution at the specified location.\n"
+ "If no specific extension, or extension and context, are specified, then this\n"
+ "application will just set the specified priority of the current extension.\n"
+ " At least a priority is required as an argument, or the goto will return a -1,\n"
+ "and the channel and call will be terminated.\n"
+ " If the location that is put into the channel information is bogus, and asterisk cannot\n"
+ "find that location in the dialplan,\n"
+ "then the execution engine will try to find and execute the code in the 'i' (invalid)\n"
+ "extension in the current context. If that does not exist, it will try to execute the\n"
+ "'h' extension. If either or neither the 'h' or 'i' extensions have been defined, the\n"
+ "channel is hung up, and the execution of instructions on the channel is terminated.\n"
+ "What this means is that, for example, you specify a context that does not exist, then\n"
+ "it will not be possible to find the 'h' or 'i' extensions, and the call will terminate!\n"
+ },
+
+ { "GotoIf", pbx_builtin_gotoif,
+ "Conditional goto",
+ " GotoIf(condition?[labeliftrue]:[labeliffalse]): This application will set the current\n"
+ "context, extension, and priority in the channel structure based on the evaluation of\n"
+ "the given condition. After this application completes, the\n"
+ "pbx engine will continue dialplan execution at the specified location in the dialplan.\n"
+ "The channel will continue at\n"
+ "'labeliftrue' if the condition is true, or 'labeliffalse' if the condition is\n"
+ "false. The labels are specified with the same syntax as used within the Goto\n"
+ "application. If the label chosen by the condition is omitted, no jump is\n"
+ "performed, and the execution passes to the next instruction.\n"
+ "If the target location is bogus, and does not exist, the execution engine will try \n"
+ "to find and execute the code in the 'i' (invalid)\n"
+ "extension in the current context. If that does not exist, it will try to execute the\n"
+ "'h' extension. If either or neither the 'h' or 'i' extensions have been defined, the\n"
+ "channel is hung up, and the execution of instructions on the channel is terminated.\n"
+ "Remember that this command can set the current context, and if the context specified\n"
+ "does not exist, then it will not be able to find any 'h' or 'i' extensions there, and\n"
+ "the channel and call will both be terminated!\n"
+ },
+
+ { "GotoIfTime", pbx_builtin_gotoiftime,
+ "Conditional Goto based on the current time",
+ " GotoIfTime(<times>,<weekdays>,<mdays>,<months>?[[context,]exten,]priority):\n"
+ "This application will set the context, extension, and priority in the channel structure\n"
+ "if the current time matches the given time specification. Otherwise, nothing is done.\n"
+ "Further information on the time specification can be found in examples\n"
+ "illustrating how to do time-based context includes in the dialplan.\n"
+ "If the target jump location is bogus, the same actions would be taken as for Goto.\n"
+ },
+
+ { "ImportVar", pbx_builtin_importvar,
+ "Import a variable from a channel into a new variable",
+ " ImportVar(newvar=channelname,variable): This application imports a variable\n"
+ "from the specified channel (as opposed to the current one) and stores it as\n"
+ "a variable in the current channel (the channel that is calling this\n"
+ "application). Variables created by this application have the same inheritance\n"
+ "properties as those created with the Set application. See the documentation for\n"
+ "Set for more information.\n"
+ },
+
+ { "Hangup", pbx_builtin_hangup,
+ "Hang up the calling channel",
+ " Hangup([causecode]): This application will hang up the calling channel.\n"
+ "If a causecode is given the channel's hangup cause will be set to the given\n"
+ "value.\n"
+ },
+
+ { "NoOp", pbx_builtin_noop,
+ "Do Nothing (No Operation)",
+ " NoOp(): This application does nothing. However, it is useful for debugging\n"
+ "purposes. Any text that is provided as arguments to this application can be\n"
+ "viewed at the Asterisk CLI. This method can be used to see the evaluations of\n"
+ "variables or functions without having any effect. Alternatively, see the\n"
+ "Verbose() application for finer grain control of output at custom verbose levels.\n"
+ },
+
+ { "Progress", pbx_builtin_progress,
+ "Indicate progress",
+ " Progress(): This application will request that in-band progress information\n"
+ "be provided to the calling channel.\n"
+ },
+
+ { "RaiseException", pbx_builtin_raise_exception,
+ "Handle an exceptional condition",
+ " RaiseException(<reason>): This application will jump to the \"e\" extension\n"
+ "in the current context, setting the dialplan function EXCEPTION(). If the \"e\"\n"
+ "extension does not exist, the call will hangup.\n"
+ },
+
+ { "ResetCDR", pbx_builtin_resetcdr,
+ "Resets the Call Data Record",
+ " ResetCDR([options]): This application causes the Call Data Record to be\n"
+ "reset.\n"
+ " Options:\n"
+ " w -- Store the current CDR record before resetting it.\n"
+ " a -- Store any stacked records.\n"
+ " v -- Save CDR variables.\n"
+ },
+
+ { "Ringing", pbx_builtin_ringing,
+ "Indicate ringing tone",
+ " Ringing(): This application will request that the channel indicate a ringing\n"
+ "tone to the user.\n"
+ },
+
+ { "SayAlpha", pbx_builtin_saycharacters,
+ "Say Alpha",
+ " SayAlpha(string): This application will play the sounds that correspond to\n"
+ "the letters of the given string.\n"
+ },
+
+ { "SayDigits", pbx_builtin_saydigits,
+ "Say Digits",
+ " SayDigits(digits): This application will play the sounds that correspond\n"
+ "to the digits of the given number. This will use the language that is currently\n"
+ "set for the channel. See the LANGUAGE function for more information on setting\n"
+ "the language for the channel.\n"
+ },
+
+ { "SayNumber", pbx_builtin_saynumber,
+ "Say Number",
+ " SayNumber(digits[,gender]): This application will play the sounds that\n"
+ "correspond to the given number. Optionally, a gender may be specified.\n"
+ "This will use the language that is currently set for the channel. See the\n"
+ "LANGUAGE function for more information on setting the language for the channel.\n"
+ },
+
+ { "SayPhonetic", pbx_builtin_sayphonetic,
+ "Say Phonetic",
+ " SayPhonetic(string): This application will play the sounds from the phonetic\n"
+ "alphabet that correspond to the letters in the given string.\n"
+ },
+
+ { "Set", pbx_builtin_setvar,
+ "Set channel variable or function value",
+ " Set(name=value)\n"
+ "This function can be used to set the value of channel variables or dialplan\n"
+ "functions. When setting variables, if the variable name is prefixed with _,\n"
+ "the variable will be inherited into channels created from the current\n"
+ "channel. If the variable name is prefixed with __, the variable will be\n"
+ "inherited into channels created from the current channel and all children\n"
+ "channels.\n"
+ },
+
+ { "MSet", pbx_builtin_setvar_multiple,
+ "Set channel variable(s) or function value(s)",
+ " MSet(name1=value1,name2=value2,...)\n"
+ "This function can be used to set the value of channel variables or dialplan\n"
+ "functions. When setting variables, if the variable name is prefixed with _,\n"
+ "the variable will be inherited into channels created from the current\n"
+ "channel. If the variable name is prefixed with __, the variable will be\n"
+ "inherited into channels created from the current channel and all children\n"
+ "channels.\n\n"
+ "MSet behaves in a similar fashion to the way Set worked in 1.2/1.4 and is thus\n"
+ "prone to doing things that you may not expect. Avoid its use if possible.\n"
+ },
+
+ { "SetAMAFlags", pbx_builtin_setamaflags,
+ "Set the AMA Flags",
+ " SetAMAFlags([flag]): This application will set the channel's AMA Flags for\n"
+ " billing purposes.\n"
+ },
+
+ { "Wait", pbx_builtin_wait,
+ "Waits for some time",
+ " Wait(seconds): This application waits for a specified number of seconds.\n"
+ "Then, dialplan execution will continue at the next priority.\n"
+ " Note that the seconds can be passed with fractions of a second. For example,\n"
+ "'1.5' will ask the application to wait for 1.5 seconds.\n"
+ },
+
+ { "WaitExten", pbx_builtin_waitexten,
+ "Waits for an extension to be entered",
+ " WaitExten([seconds][,options]): This application waits for the user to enter\n"
+ "a new extension for a specified number of seconds.\n"
+ " Note that the seconds can be passed with fractions of a second. For example,\n"
+ "'1.5' will ask the application to wait for 1.5 seconds.\n"
+ " Options:\n"
+ " m[(x)] - Provide music on hold to the caller while waiting for an extension.\n"
+ " Optionally, specify the class for music on hold within parenthesis.\n"
+ },
+
+ { "KeepAlive", pbx_builtin_keepalive,
+ "returns AST_PBX_KEEPALIVE value",
+ " KeepAlive(): This application is chiefly meant for internal use with Gosubs.\n"
+ "Please do not run it alone from the dialplan!\n"
+ },
+
+};
+
+static struct ast_context *contexts;
+static struct ast_hashtab *contexts_tree = NULL;
+
+AST_RWLOCK_DEFINE_STATIC(conlock); /*!< Lock for the ast_context list */
+
+static AST_RWLIST_HEAD_STATIC(apps, ast_app);
+
+static AST_RWLIST_HEAD_STATIC(switches, ast_switch);
+
+static int stateid = 1;
+/* WARNING:
+ When holding this list's lock, do _not_ do anything that will cause conlock
+ to be taken, unless you _already_ hold it. The ast_merge_contexts_and_delete
+ function will take the locks in conlock/hints order, so any other
+ paths that require both locks must also take them in that order.
+*/
+static AST_RWLIST_HEAD_STATIC(hints, ast_hint);
+struct ast_state_cb *statecbs;
+
+/*
+ \note This function is special. It saves the stack so that no matter
+ how many times it is called, it returns to the same place */
+int pbx_exec(struct ast_channel *c, /*!< Channel */
+ struct ast_app *app, /*!< Application */
+ void *data) /*!< Data for execution */
+{
+ int res;
+ struct ast_module_user *u = NULL;
+ const char *saved_c_appl;
+ const char *saved_c_data;
+
+ if (c->cdr && !ast_check_hangup(c))
+ ast_cdr_setapp(c->cdr, app->name, data);
+
+ /* save channel values */
+ saved_c_appl= c->appl;
+ saved_c_data= c->data;
+
+ c->appl = app->name;
+ c->data = data;
+ if (app->module)
+ u = __ast_module_user_add(app->module, c);
+ res = app->execute(c, data);
+ if (app->module && u)
+ __ast_module_user_remove(app->module, u);
+ /* restore channel values */
+ c->appl = saved_c_appl;
+ c->data = saved_c_data;
+ return res;
+}
+
+
+/*! Go no deeper than this through includes (not counting loops) */
+#define AST_PBX_MAX_STACK 128
+
+/*! \brief Find application handle in linked list
+ */
+struct ast_app *pbx_findapp(const char *app)
+{
+ struct ast_app *tmp;
+
+ AST_RWLIST_RDLOCK(&apps);
+ AST_RWLIST_TRAVERSE(&apps, tmp, list) {
+ if (!strcasecmp(tmp->name, app))
+ break;
+ }
+ AST_RWLIST_UNLOCK(&apps);
+
+ return tmp;
+}
+
+static struct ast_switch *pbx_findswitch(const char *sw)
+{
+ struct ast_switch *asw;
+
+ AST_RWLIST_RDLOCK(&switches);
+ AST_RWLIST_TRAVERSE(&switches, asw, list) {
+ if (!strcasecmp(asw->name, sw))
+ break;
+ }
+ AST_RWLIST_UNLOCK(&switches);
+
+ return asw;
+}
+
+static inline int include_valid(struct ast_include *i)
+{
+ if (!i->hastime)
+ return 1;
+
+ return ast_check_timing(&(i->timing));
+}
+
+static void pbx_destroy(struct ast_pbx *p)
+{
+ ast_free(p);
+}
+
+/* form a tree that fully describes all the patterns in a context's extensions
+ * in this tree, a "node" consists of a series of match_char structs linked in a chain
+ * via the alt_char pointers. More than one pattern can share the same parts of the
+ * tree as other extensions with the same pattern to that point. The algorithm to
+ * find which pattern best matches a string, would follow **all** matching paths. As
+ * complete matches are found, a "max" match record would be updated if the match first involves
+ * a longer string, then, on a tie, a smaller total of specificity. This can be accomplished
+ * by recursive calls affecting a shared scoreboard.
+ * As and example, consider these 4 extensions:
+ * (a) NXXNXXXXXX
+ * (b) 307754XXXX
+ * (c) fax
+ * (d) NXXXXXXXXX
+ *
+ * In the above, between (a) and (d), (a) is a more specific pattern than (d), and would win over
+ * most numbers. For all numbers beginning with 307754, (b) should always win.
+ *
+ * These pattern should form a tree that looks like this:
+ * { "N" } --next--> { "X" } --next--> { "X" } --next--> { "N" } --next--> { "X" } ... blah ... --> { "X" exten_match: (a) }
+ * | |
+ * | |alt
+ * |alt |
+ * | { "X" } --next--> { "X" } ... blah ... --> { "X" exten_match: (d) }
+ * |
+ * { "3" } --next--> { "0" } --next--> { "7" } --next--> { "7" } --next--> { "5" } ... blah ... --> { "X" exten_match: (b) }
+ * |
+ * |alt
+ * |
+ * { "f" } --next--> { "a" } --next--> { "x" exten_match: (c) }
+ *
+ * In the above, I could easily turn "N" into "23456789", but I think that a quick "if( *z >= '2' && *z <= '9' )" might take
+ * fewer CPU cycles than a call to index("23456789",*z), where *z is the char to match...
+ *
+ * traversal is pretty simple: one routine merely traverses the alt list, and for each match in the pattern, it calls itself
+ * on the corresponding next pointer, incrementing also the pointer of the string to be matched, and passing the total specificity and length.
+ * We pass a pointer to a scoreboard down through, also.
+ * When an exten_match pointer is set, or a '.' or a '!' is encountered, we update the scoreboard only if the length is greater, or in case
+ * of equal length, if the specificity is lower, and return. Hope the limit on stack depth won't be a problem... this routine should
+ * be pretty lean as far a stack usage goes. Any non-match terminates the recursion down a branch.
+ *
+ * In the above example, with the number "3077549999" as the pattern, the traversor should match extensions a, b and d. All are
+ * of length 10; but they have total specificities of 96, 46, and 98, respectively. (b) wins with its lower specificity number!
+ *
+ * Just how much time this algorithm might save over a plain linear traversal over all possible patterns is unknown. But, it should
+ * be pretty close to optimal for this sort of overall algorithm.
+ *
+ * */
+
+/* you only really update the scoreboard, if the new score is BETTER than the
+ * one stored there. ignore it otherwise.
+ */
+
+
+static void update_scoreboard(struct scoreboard *board, int length, int spec, struct ast_exten *exten, char last, const char *callerid, int deleted, struct match_char *node)
+{
+ /* doing a matchcid() check here would be easy and cheap, but...
+ unfortunately, it only works if an extension can have only one
+ cid to match. That's not real life. CID patterns need to exist
+ in the tree for this sort of thing to work properly. */
+
+ /* if this extension is marked as deleted, then skip this -- if it never shows
+ on the scoreboard, it will never be found */
+ if (deleted)
+ return;
+ if (length > board->total_length) {
+ board->total_specificity = spec;
+ board->total_length = length;
+ board->exten = exten;
+ board->last_char = last;
+ board->node = node;
+ } else if (length == board->total_length && spec < board->total_specificity) {
+ board->total_specificity = spec;
+ board->total_length = length;
+ board->exten = exten;
+ board->last_char = last;
+ board->node = node;
+ }
+}
+
+void log_match_char_tree(struct match_char *node, char *prefix)
+{
+ char my_prefix[1024];
+ char extenstr[40];
+
+ extenstr[0] = 0;
+ if (node && node->exten && node->exten)
+ sprintf(extenstr,"(%p)",node->exten);
+
+ if (strlen(node->x) > 1 )
+ ast_log(LOG_DEBUG,"%s[%s]:%c:%c:%d:%s%s%s\n", prefix, node->x, node->is_pattern ? 'Y':'N', node->deleted? 'D':'-', node->specificity, node->exten? "EXTEN:":"", node->exten ? node->exten->exten : "", extenstr);
+ else
+ ast_log(LOG_DEBUG,"%s%s:%c:%c:%d:%s%s%s\n", prefix, node->x, node->is_pattern ? 'Y':'N', node->deleted? 'D':'-', node->specificity, node->exten? "EXTEN:":"", node->exten ? node->exten->exten : "", extenstr);
+ strcpy(my_prefix,prefix);
+ strcat(my_prefix,"+ ");
+ if (node->next_char)
+ log_match_char_tree(node->next_char, my_prefix);
+ if (node->alt_char)
+ log_match_char_tree(node->alt_char, prefix);
+}
+
+static void cli_match_char_tree(struct match_char *node, char *prefix, int fd)
+{
+ char my_prefix[1024];
+ char extenstr[40];
+
+ extenstr[0] = 0;
+ if (node && node->exten && node->exten)
+ sprintf(extenstr,"(%p)",node->exten);
+
+ if (strlen(node->x) > 1)
+ ast_cli(fd, "%s[%s]:%c:%c:%d:%s%s%s\n", prefix, node->x, node->is_pattern ? 'Y':'N', node->deleted ? 'D' : '-', node->specificity, node->exten? "EXTEN:":"", node->exten ? node->exten->exten : "", extenstr);
+ else
+ ast_cli(fd, "%s%s:%c:%c:%d:%s%s%s\n", prefix, node->x, node->is_pattern ? 'Y':'N', node->deleted ? 'D' : '-', node->specificity, node->exten? "EXTEN:":"", node->exten ? node->exten->exten : "", extenstr);
+ strcpy(my_prefix,prefix);
+ strcat(my_prefix,"+ ");
+ if (node->next_char)
+ cli_match_char_tree(node->next_char, my_prefix, fd);
+ if (node->alt_char)
+ cli_match_char_tree(node->alt_char, prefix, fd);
+}
+
+static struct ast_exten *get_canmatch_exten(struct match_char *node)
+{
+ /* find the exten at the end of the rope */
+ struct match_char *node2 = node;
+ for (node2 = node; node2; node2 = node2->next_char)
+ if (node2->exten)
+ return node2->exten;
+ return 0;
+}
+
+static struct ast_exten *trie_find_next_match(struct match_char *node)
+{
+ struct match_char *m3;
+ struct match_char *m4;
+ struct ast_exten *e3;
+
+ if (node && node->x[0] == '.' && !node->x[1]) /* dot and ! will ALWAYS be next match in a matchmore */
+ return node->exten;
+
+ if (node && node->x[0] == '!' && !node->x[1])
+ return node->exten;
+
+ if (!node || !node->next_char)
+ return NULL;
+
+ m3 = node->next_char;
+
+ if (m3->exten)
+ return m3->exten;
+ for(m4=m3->alt_char; m4; m4 = m4->alt_char) {
+ if (m4->exten)
+ return m4->exten;
+ }
+ for(m4=m3; m4; m4 = m4->alt_char) {
+ e3 = trie_find_next_match(m3);
+ if (e3)
+ return e3;
+ }
+ return NULL;
+}
+
+static void new_find_extension(const char *str, struct scoreboard *score, struct match_char *tree, int length, int spec, const char *callerid)
+{
+ struct match_char *p; /* note minimal stack storage requirements */
+#ifdef DEBUG_THIS
+ if (tree)
+ ast_log(LOG_NOTICE,"new_find_extension called with %s on (sub)tree %s\n", str, tree->x);
+ else
+ ast_log(LOG_NOTICE,"new_find_extension called with %s on (sub)tree NULL\n", str);
+#endif
+ for (p=tree; p; p=p->alt_char) {
+ if (p->x[0] == 'N' && p->x[1] == 0 && *str >= '2' && *str <= '9' ) {
+ if (p->exten && !(*(str+1))) /* if a shorter pattern matches along the way, might as well report it */
+ update_scoreboard(score, length+1, spec+p->specificity, p->exten,0,callerid, p->deleted, p);
+
+ if (p->next_char && ( *(str+1) || (p->next_char->x[0] == '/' && p->next_char->x[1] == 0))) {
+ if (*(str+1))
+ new_find_extension(str+1, score, p->next_char, length+1, spec+p->specificity, callerid);
+ else
+ new_find_extension("/", score, p->next_char, length+1, spec+p->specificity, callerid);
+ } else if (p->next_char && !*(str+1)) {
+ score->canmatch = 1;
+ score->canmatch_exten = get_canmatch_exten(p);
+ } else {
+ return;
+ }
+ } else if (p->x[0] == 'Z' && p->x[1] == 0 && *str >= '1' && *str <= '9' ) {
+ if (p->exten && !(*(str+1))) /* if a shorter pattern matches along the way, might as well report it */
+ update_scoreboard(score, length+1, spec+p->specificity, p->exten,0,callerid, p->deleted,p);
+
+ if (p->next_char && ( *(str+1) || (p->next_char->x[0] == '/' && p->next_char->x[1] == 0))) {
+ if (*(str+1))
+ new_find_extension(str+1, score, p->next_char, length+1, spec+p->specificity, callerid);
+ else
+ new_find_extension("/", score, p->next_char, length+1, spec+p->specificity, callerid);
+ } else if (p->next_char && !*(str+1)) {
+ score->canmatch = 1;
+ score->canmatch_exten = get_canmatch_exten(p);
+ } else {
+ return;
+ }
+ } else if (p->x[0] == 'X' && p->x[1] == 0 && *str >= '0' && *str <= '9' ) {
+ if (p->exten && !(*(str+1))) /* if a shorter pattern matches along the way, might as well report it */
+ update_scoreboard(score, length+1, spec+p->specificity, p->exten,0,callerid, p->deleted,p);
+
+ if (p->next_char && ( *(str+1) || (p->next_char->x[0] == '/' && p->next_char->x[1] == 0))) {
+ if (*(str+1))
+ new_find_extension(str+1, score, p->next_char, length+1, spec+p->specificity, callerid);
+ else
+ new_find_extension("/", score, p->next_char, length+1, spec+p->specificity, callerid);
+ } else if (p->next_char && !*(str+1)) {
+ score->canmatch = 1;
+ score->canmatch_exten = get_canmatch_exten(p);
+ } else {
+ return;
+ }
+ } else if (p->x[0] == '.' && p->x[1] == 0) {
+ /* how many chars will the . match against? */
+ int i = 0;
+ const char *str2 = str;
+ while (*str2++) {
+ i++;
+ }
+ if (p->exten && !(*(str+1)))
+ update_scoreboard(score, length+i, spec+(i*p->specificity), p->exten, '.', callerid, p->deleted, p);
+ if (p->next_char && p->next_char->x[0] == '/' && p->next_char->x[1] == 0) {
+ new_find_extension("/", score, p->next_char, length+i, spec+(p->specificity*i), callerid);
+ }
+ return;
+ } else if (p->x[0] == '!' && p->x[1] == 0) {
+ /* how many chars will the . match against? */
+ int i = 0;
+ const char *str2 = str;
+ while (*str2++) {
+ i++;
+ }
+ if (p->exten && !(*(str+1)))
+ update_scoreboard(score, length+1, spec+(p->specificity*i), p->exten, '!', callerid, p->deleted, p);
+ if (p->next_char && p->next_char->x[0] == '/' && p->next_char->x[1] == 0) {
+ new_find_extension("/", score, p->next_char, length+i, spec+(p->specificity*i), callerid);
+ }
+ return;
+ } else if (p->x[0] == '/' && p->x[1] == 0) {
+ /* the pattern in the tree includes the cid match! */
+ if (p->next_char && callerid && *callerid) {
+ new_find_extension(callerid, score, p->next_char, length+1, spec, callerid);
+ }
+ } else if (index(p->x, *str)) {
+ if (p->exten && !(*(str+1))) /* if a shorter pattern matches along the way, might as well report it */
+ update_scoreboard(score, length+1, spec+p->specificity, p->exten,0,callerid, p->deleted, p);
+
+
+ if (p->next_char && ( *(str+1) || (p->next_char->x[0] == '/' && p->next_char->x[1] == 0))) {
+ if (*(str+1)) {
+ new_find_extension(str+1, score, p->next_char, length+1, spec+p->specificity, callerid);
+ } else {
+ new_find_extension("/", score, p->next_char, length+1, spec+p->specificity, callerid);
+ }
+ } else if (p->next_char && !*(str+1)) {
+ score->canmatch = 1;
+ score->canmatch_exten = get_canmatch_exten(p);
+ } else {
+ return;
+ }
+ }
+ }
+}
+
+/* the algorithm for forming the extension pattern tree is also a bit simple; you
+ * traverse all the extensions in a context, and for each char of the extension,
+ * you see if it exists in the tree; if it doesn't, you add it at the appropriate
+ * spot. What more can I say? At the end of the list, you cap it off by adding the
+ * address of the extension involved. Duplicate patterns will be complained about.
+ *
+ * Ideally, this would be done for each context after it is created and fully
+ * filled. It could be done as a finishing step after extensions.conf or .ael is
+ * loaded, or it could be done when the first search is encountered. It should only
+ * have to be done once, until the next unload or reload.
+ *
+ * I guess forming this pattern tree would be analogous to compiling a regex.
+ */
+
+static struct match_char *already_in_tree(struct match_char *current, char *pat)
+{
+ struct match_char *t;
+ if (!current)
+ return 0;
+ for (t=current; t; t=t->alt_char) {
+ if (strcmp(pat,t->x) == 0) /* uh, we may want to sort exploded [] contents to make matching easy */
+ return t;
+ }
+ return 0;
+}
+
+static struct match_char *add_pattern_node(struct ast_context *con, struct match_char *current, char *pattern, int is_pattern, int already, int specificity)
+{
+ struct match_char *m = ast_calloc(1,sizeof(struct match_char));
+ m->x = ast_strdup(pattern);
+ m->is_pattern = is_pattern;
+ if (specificity == 1 && is_pattern && pattern[0] == 'N')
+ m->specificity = 98;
+ else if (specificity == 1 && is_pattern && pattern[0] == 'Z')
+ m->specificity = 99;
+ else if (specificity == 1 && is_pattern && pattern[0] == 'X')
+ m->specificity = 100;
+ else if (specificity == 1 && is_pattern && pattern[0] == '.')
+ m->specificity = 200;
+ else if (specificity == 1 && is_pattern && pattern[0] == '!')
+ m->specificity = 200;
+ else
+ m->specificity = specificity;
+
+ if (!con->pattern_tree) {
+ con->pattern_tree = m;
+ } else {
+ if (already) { /* switch to the new regime (traversing vs appending)*/
+ m->alt_char = current->alt_char;
+ current->alt_char = m;
+ } else {
+ if (current->next_char) {
+ m->alt_char = current->next_char->alt_char;
+ current->next_char = m;
+ } else {
+ current->next_char = m;
+ }
+ }
+ }
+ return m;
+}
+
+static struct match_char *add_exten_to_pattern_tree(struct ast_context *con, struct ast_exten *e1, int findonly)
+{
+ struct match_char *m1=0,*m2=0;
+ int specif;
+ int already;
+ int pattern = 0;
+ char buf[256];
+ char extenbuf[512];
+ char *s1 = extenbuf;
+ int l1 = strlen(e1->exten) + strlen(e1->cidmatch) + 2;
+
+
+ strncpy(extenbuf,e1->exten,sizeof(extenbuf));
+ if (e1->matchcid && l1 <= sizeof(extenbuf)) {
+ strcat(extenbuf,"/");
+ strcat(extenbuf,e1->cidmatch);
+ } else if (l1 > sizeof(extenbuf)) {
+ ast_log(LOG_ERROR,"The pattern %s/%s is too big to deal with: it will be ignored! Disaster!\n", e1->exten, e1->cidmatch);
+ return 0;
+ }
+#ifdef NEED_DEBUG
+ ast_log(LOG_DEBUG,"Adding exten %s%c%s to tree\n", s1, e1->matchcid? '/':' ', e1->matchcid? e1->cidmatch : "");
+#endif
+ m1 = con->pattern_tree; /* each pattern starts over at the root of the pattern tree */
+ already = 1;
+
+ if ( *s1 == '_') {
+ pattern = 1;
+ s1++;
+ }
+ while( *s1 ) {
+ if (pattern && *s1 == '[' && *(s1-1) != '\\') {
+ char *s2 = buf;
+ buf[0] = 0;
+ s1++; /* get past the '[' */
+ while (*s1 != ']' && *(s1-1) != '\\' ) {
+ if (*s1 == '\\') {
+ if (*(s1+1) == ']') {
+ *s2++ = ']';
+ s1++;s1++;
+ } else if (*(s1+1) == '\\') {
+ *s2++ = '\\';
+ s1++;s1++;
+ } else if (*(s1+1) == '-') {
+ *s2++ = '-';
+ s1++; s1++;
+ } else if (*(s1+1) == '[') {
+ *s2++ = '[';
+ s1++; s1++;
+ }
+ } else if (*s1 == '-') { /* remember to add some error checking to all this! */
+ char s3 = *(s1-1);
+ char s4 = *(s1+1);
+ for (s3++; s3 <= s4; s3++) {
+ *s2++ = s3;
+ }
+ s1++; s1++;
+ } else {
+ *s2++ = *s1++;
+ }
+ }
+ *s2 = 0; /* null terminate the exploded range */
+ specif = strlen(buf);
+ } else {
+
+ if (*s1 == '\\') {
+ s1++;
+ buf[0] = *s1;
+ } else {
+ if (pattern) {
+ if (*s1 == 'n') /* make sure n,x,z patterns are canonicalized to N,X,Z */
+ *s1 = 'N';
+ else if (*s1 == 'x')
+ *s1 = 'X';
+ else if (*s1 == 'z')
+ *s1 = 'Z';
+ }
+ buf[0] = *s1;
+ }
+ buf[1] = 0;
+ specif = 1;
+ }
+ m2 = 0;
+ if (already && (m2=already_in_tree(m1,buf)) && m2->next_char) {
+ if (!(*(s1+1))) { /* if this is the end of the pattern, but not the end of the tree, then mark this node with the exten...
+ a shorter pattern might win if the longer one doesn't match */
+ m2->exten = e1;
+ m2->deleted = 0;
+ }
+ m1 = m2->next_char; /* m1 points to the node to compare against */
+ } else {
+ if (m2) {
+ if (findonly)
+ return m2;
+ m1 = m2;
+ } else {
+ if (findonly)
+ return m1;
+ m1 = add_pattern_node(con, m1, buf, pattern, already,specif); /* m1 is the node just added */
+ }
+
+ if (!(*(s1+1))) {
+ m1->deleted = 0;
+ m1->exten = e1;
+ }
+
+ already = 0;
+ }
+ s1++; /* advance to next char */
+ }
+ return m1;
+}
+
+static void create_match_char_tree(struct ast_context *con)
+{
+ struct ast_hashtab_iter *t1;
+ struct ast_exten *e1;
+#ifdef NEED_DEBUG
+ int biggest_bucket, resizes, numobjs, numbucks;
+
+ ast_log(LOG_DEBUG,"Creating Extension Trie for context %s\n", con->name);
+ ast_hashtab_get_stats(con->root_tree, &biggest_bucket, &resizes, &numobjs, &numbucks);
+ ast_log(LOG_DEBUG,"This tree has %d objects in %d bucket lists, longest list=%d objects, and has resized %d times\n",
+ numobjs, numbucks, biggest_bucket, resizes);
+#endif
+ t1 = ast_hashtab_start_traversal(con->root_tree);
+ while( (e1 = ast_hashtab_next(t1)) ) {
+ if (e1->exten)
+ add_exten_to_pattern_tree(con, e1, 0);
+ else
+ ast_log(LOG_ERROR,"Attempt to create extension with no extension name.\n");
+ }
+ ast_hashtab_end_traversal(t1);
+}
+
+static void destroy_pattern_tree(struct match_char *pattern_tree) /* pattern tree is a simple binary tree, sort of, so the proper way to destroy it is... recursively! */
+{
+ /* destroy all the alternates */
+ if (pattern_tree->alt_char) {
+ destroy_pattern_tree(pattern_tree->alt_char);
+ pattern_tree->alt_char = 0;
+ }
+ /* destroy all the nexts */
+ if (pattern_tree->next_char) {
+ destroy_pattern_tree(pattern_tree->next_char);
+ pattern_tree->next_char = 0;
+ }
+ pattern_tree->exten = 0; /* never hurts to make sure there's no pointers laying around */
+ if (pattern_tree->x)
+ free(pattern_tree->x);
+ free(pattern_tree);
+}
+
+/*
+ * Special characters used in patterns:
+ * '_' underscore is the leading character of a pattern.
+ * In other position it is treated as a regular char.
+ * ' ' '-' space and '-' are separator and ignored.
+ * . one or more of any character. Only allowed at the end of
+ * a pattern.
+ * ! zero or more of anything. Also impacts the result of CANMATCH
+ * and MATCHMORE. Only allowed at the end of a pattern.
+ * In the core routine, ! causes a match with a return code of 2.
+ * In turn, depending on the search mode: (XXX check if it is implemented)
+ * - E_MATCH retuns 1 (does match)
+ * - E_MATCHMORE returns 0 (no match)
+ * - E_CANMATCH returns 1 (does match)
+ *
+ * / should not appear as it is considered the separator of the CID info.
+ * XXX at the moment we may stop on this char.
+ *
+ * X Z N match ranges 0-9, 1-9, 2-9 respectively.
+ * [ denotes the start of a set of character. Everything inside
+ * is considered literally. We can have ranges a-d and individual
+ * characters. A '[' and '-' can be considered literally if they
+ * are just before ']'.
+ * XXX currently there is no way to specify ']' in a range, nor \ is
+ * considered specially.
+ *
+ * When we compare a pattern with a specific extension, all characters in the extension
+ * itself are considered literally with the only exception of '-' which is considered
+ * as a separator and thus ignored.
+ * XXX do we want to consider space as a separator as well ?
+ * XXX do we want to consider the separators in non-patterns as well ?
+ */
+
+/*!
+ * \brief helper functions to sort extensions and patterns in the desired way,
+ * so that more specific patterns appear first.
+ *
+ * ext_cmp1 compares individual characters (or sets of), returning
+ * an int where bits 0-7 are the ASCII code of the first char in the set,
+ * while bit 8-15 are the cardinality of the set minus 1.
+ * This way more specific patterns (smaller cardinality) appear first.
+ * Wildcards have a special value, so that we can directly compare them to
+ * sets by subtracting the two values. In particular:
+ * 0x000xx one character, xx
+ * 0x0yyxx yy character set starting with xx
+ * 0x10000 '.' (one or more of anything)
+ * 0x20000 '!' (zero or more of anything)
+ * 0x30000 NUL (end of string)
+ * 0x40000 error in set.
+ * The pointer to the string is advanced according to needs.
+ * NOTES:
+ * 1. the empty set is equivalent to NUL.
+ * 2. given that a full set has always 0 as the first element,
+ * we could encode the special cases as 0xffXX where XX
+ * is 1, 2, 3, 4 as used above.
+ */
+static int ext_cmp1(const char **p)
+{
+ uint32_t chars[8];
+ int c, cmin = 0xff, count = 0;
+ const char *end;
+
+ /* load, sign extend and advance pointer until we find
+ * a valid character.
+ */
+ while ( (c = *(*p)++) && (c == ' ' || c == '-') )
+ ; /* ignore some characters */
+
+ /* always return unless we have a set of chars */
+ switch (c) {
+ default: /* ordinary character */
+ return 0x0000 | (c & 0xff);
+
+ case 'N': /* 2..9 */
+ return 0x0700 | '2' ;
+
+ case 'X': /* 0..9 */
+ return 0x0900 | '0';
+
+ case 'Z': /* 1..9 */
+ return 0x0800 | '1';
+
+ case '.': /* wildcard */
+ return 0x10000;
+
+ case '!': /* earlymatch */
+ return 0x20000; /* less specific than NULL */
+
+ case '\0': /* empty string */
+ *p = NULL;
+ return 0x30000;
+
+ case '[': /* pattern */
+ break;
+ }
+ /* locate end of set */
+ end = strchr(*p, ']');
+
+ if (end == NULL) {
+ ast_log(LOG_WARNING, "Wrong usage of [] in the extension\n");
+ return 0x40000; /* XXX make this entry go last... */
+ }
+
+ bzero(chars, sizeof(chars)); /* clear all chars in the set */
+ for (; *p < end ; (*p)++) {
+ unsigned char c1, c2; /* first-last char in range */
+ c1 = (unsigned char)((*p)[0]);
+ if (*p + 2 < end && (*p)[1] == '-') { /* this is a range */
+ c2 = (unsigned char)((*p)[2]);
+ *p += 2; /* skip a total of 3 chars */
+ } else /* individual character */
+ c2 = c1;
+ if (c1 < cmin)
+ cmin = c1;
+ for (; c1 <= c2; c1++) {
+ uint32_t mask = 1 << (c1 % 32);
+ if ( (chars[ c1 / 32 ] & mask) == 0)
+ count += 0x100;
+ chars[ c1 / 32 ] |= mask;
+ }
+ }
+ (*p)++;
+ return count == 0 ? 0x30000 : (count | cmin);
+}
+
+/*!
+ * \brief the full routine to compare extensions in rules.
+ */
+static int ext_cmp(const char *a, const char *b)
+{
+ /* make sure non-patterns come first.
+ * If a is not a pattern, it either comes first or
+ * we use strcmp to compare the strings.
+ */
+ int ret = 0;
+
+ if (a[0] != '_')
+ return (b[0] == '_') ? -1 : strcmp(a, b);
+
+ /* Now we know a is a pattern; if b is not, a comes first */
+ if (b[0] != '_')
+ return 1;
+#if 0 /* old mode for ext matching */
+ return strcmp(a, b);
+#endif
+ /* ok we need full pattern sorting routine */
+ while (!ret && a && b)
+ ret = ext_cmp1(&a) - ext_cmp1(&b);
+ if (ret == 0)
+ return 0;
+ else
+ return (ret > 0) ? 1 : -1;
+}
+
+int ast_extension_cmp(const char *a, const char *b)
+{
+ return ext_cmp(a, b);
+}
+
+/*!
+ * \internal
+ * \brief used ast_extension_{match|close}
+ * mode is as follows:
+ * E_MATCH success only on exact match
+ * E_MATCHMORE success only on partial match (i.e. leftover digits in pattern)
+ * E_CANMATCH either of the above.
+ * \retval 0 on no-match
+ * \retval 1 on match
+ * \retval 2 on early match.
+ */
+
+static int _extension_match_core(const char *pattern, const char *data, enum ext_match_t mode)
+{
+ mode &= E_MATCH_MASK; /* only consider the relevant bits */
+
+ if ( (mode == E_MATCH) && (pattern[0] == '_') && (strcasecmp(pattern,data)==0) ) /* note: if this test is left out, then _x. will not match _x. !!! */
+ return 1;
+
+ if (pattern[0] != '_') { /* not a pattern, try exact or partial match */
+ int ld = strlen(data), lp = strlen(pattern);
+
+ if (lp < ld) /* pattern too short, cannot match */
+ return 0;
+ /* depending on the mode, accept full or partial match or both */
+ if (mode == E_MATCH)
+ return !strcmp(pattern, data); /* 1 on match, 0 on fail */
+ if (ld == 0 || !strncasecmp(pattern, data, ld)) /* partial or full match */
+ return (mode == E_MATCHMORE) ? lp > ld : 1; /* XXX should consider '!' and '/' ? */
+ else
+ return 0;
+ }
+ pattern++; /* skip leading _ */
+ /*
+ * XXX below we stop at '/' which is a separator for the CID info. However we should
+ * not store '/' in the pattern at all. When we insure it, we can remove the checks.
+ */
+ while (*data && *pattern && *pattern != '/') {
+ const char *end;
+
+ if (*data == '-') { /* skip '-' in data (just a separator) */
+ data++;
+ continue;
+ }
+ switch (toupper(*pattern)) {
+ case '[': /* a range */
+ end = strchr(pattern+1, ']'); /* XXX should deal with escapes ? */
+ if (end == NULL) {
+ ast_log(LOG_WARNING, "Wrong usage of [] in the extension\n");
+ return 0; /* unconditional failure */
+ }
+ for (pattern++; pattern != end; pattern++) {
+ if (pattern+2 < end && pattern[1] == '-') { /* this is a range */
+ if (*data >= pattern[0] && *data <= pattern[2])
+ break; /* match found */
+ else {
+ pattern += 2; /* skip a total of 3 chars */
+ continue;
+ }
+ } else if (*data == pattern[0])
+ break; /* match found */
+ }
+ if (pattern == end)
+ return 0;
+ pattern = end; /* skip and continue */
+ break;
+ case 'N':
+ if (*data < '2' || *data > '9')
+ return 0;
+ break;
+ case 'X':
+ if (*data < '0' || *data > '9')
+ return 0;
+ break;
+ case 'Z':
+ if (*data < '1' || *data > '9')
+ return 0;
+ break;
+ case '.': /* Must match, even with more digits */
+ return 1;
+ case '!': /* Early match */
+ return 2;
+ case ' ':
+ case '-': /* Ignore these in patterns */
+ data--; /* compensate the final data++ */
+ break;
+ default:
+ if (*data != *pattern)
+ return 0;
+ }
+ data++;
+ pattern++;
+ }
+ if (*data) /* data longer than pattern, no match */
+ return 0;
+ /*
+ * match so far, but ran off the end of the data.
+ * Depending on what is next, determine match or not.
+ */
+ if (*pattern == '\0' || *pattern == '/') /* exact match */
+ return (mode == E_MATCHMORE) ? 0 : 1; /* this is a failure for E_MATCHMORE */
+ else if (*pattern == '!') /* early match */
+ return 2;
+ else /* partial match */
+ return (mode == E_MATCH) ? 0 : 1; /* this is a failure for E_MATCH */
+}
+
+/*
+ * Wrapper around _extension_match_core() to do performance measurement
+ * using the profiling code.
+ */
+static int extension_match_core(const char *pattern, const char *data, enum ext_match_t mode)
+{
+ int i;
+ static int prof_id = -2; /* marker for 'unallocated' id */
+ if (prof_id == -2)
+ prof_id = ast_add_profile("ext_match", 0);
+ ast_mark(prof_id, 1);
+ i = _extension_match_core(pattern, data, mode);
+ ast_mark(prof_id, 0);
+ return i;
+}
+
+int ast_extension_match(const char *pattern, const char *data)
+{
+ return extension_match_core(pattern, data, E_MATCH);
+}
+
+int ast_extension_close(const char *pattern, const char *data, int needmore)
+{
+ if (needmore != E_MATCHMORE && needmore != E_CANMATCH)
+ ast_log(LOG_WARNING, "invalid argument %d\n", needmore);
+ return extension_match_core(pattern, data, needmore);
+}
+
+struct fake_context /* this struct is purely for matching in the hashtab */
+{
+ ast_rwlock_t lock;
+ struct ast_exten *root;
+ struct ast_hashtab *root_tree;
+ struct match_char *pattern_tree;
+ struct ast_context *next;
+ struct ast_include *includes;
+ struct ast_ignorepat *ignorepats;
+ const char *registrar;
+ AST_LIST_HEAD_NOLOCK(, ast_sw) alts;
+ ast_mutex_t macrolock;
+ char name[256];
+};
+
+struct ast_context *ast_context_find(const char *name)
+{
+ struct ast_context *tmp = NULL;
+ struct fake_context item;
+ strncpy(item.name,name,256);
+ ast_rdlock_contexts();
+ if( contexts_tree ) {
+ tmp = ast_hashtab_lookup(contexts_tree,&item);
+ } else {
+ while ( (tmp = ast_walk_contexts(tmp)) ) {
+ if (!name || !strcasecmp(name, tmp->name))
+ break;
+ }
+ }
+ ast_unlock_contexts();
+ return tmp;
+}
+
+#define STATUS_NO_CONTEXT 1
+#define STATUS_NO_EXTENSION 2
+#define STATUS_NO_PRIORITY 3
+#define STATUS_NO_LABEL 4
+#define STATUS_SUCCESS 5
+
+static int matchcid(const char *cidpattern, const char *callerid)
+{
+ /* If the Caller*ID pattern is empty, then we're matching NO Caller*ID, so
+ failing to get a number should count as a match, otherwise not */
+
+ if (ast_strlen_zero(callerid))
+ return ast_strlen_zero(cidpattern) ? 1 : 0;
+
+ return ast_extension_match(cidpattern, callerid);
+}
+
+struct ast_exten *pbx_find_extension(struct ast_channel *chan,
+ struct ast_context *bypass, struct pbx_find_info *q,
+ const char *context, const char *exten, int priority,
+ const char *label, const char *callerid, enum ext_match_t action)
+{
+ int x, res;
+ struct ast_context *tmp=0;
+ struct ast_exten *e=0, *eroot=0;
+ struct ast_include *i = 0;
+ struct ast_sw *sw = 0;
+ struct ast_exten pattern = {0};
+ struct scoreboard score = {0};
+
+ pattern.label = label;
+ pattern.priority = priority;
+#ifdef NEED_DEBUG
+ ast_log(LOG_NOTICE,"Looking for cont/ext/prio/label/action = %s/%s/%d/%s/%d\n", context, exten, priority, label, (int)action);
+#endif
+ /* Initialize status if appropriate */
+ if (q->stacklen == 0) {
+ q->status = STATUS_NO_CONTEXT;
+ q->swo = NULL;
+ q->data = NULL;
+ q->foundcontext = NULL;
+ } else if (q->stacklen >= AST_PBX_MAX_STACK) {
+ ast_log(LOG_WARNING, "Maximum PBX stack exceeded\n");
+ return NULL;
+ }
+
+ /* Check first to see if we've already been checked */
+ for (x = 0; x < q->stacklen; x++) {
+ if (!strcasecmp(q->incstack[x], context))
+ return NULL;
+ }
+
+ if (bypass) /* bypass means we only look there */
+ tmp = bypass;
+ else { /* look in contexts */
+ struct fake_context item;
+ strncpy(item.name,context,256);
+ tmp = ast_hashtab_lookup(contexts_tree,&item);
+#ifdef NOTNOW
+ tmp = NULL;
+ while ((tmp = ast_walk_contexts(tmp)) ) {
+ if (!strcmp(tmp->name, context))
+ break;
+ }
+#endif
+ if (!tmp)
+ return NULL;
+
+ }
+
+ if (q->status < STATUS_NO_EXTENSION)
+ q->status = STATUS_NO_EXTENSION;
+
+ /* Do a search for matching extension */
+
+ eroot = NULL;
+ score.total_specificity = 0;
+ score.exten = 0;
+ score.total_length = 0;
+ if (!tmp->pattern_tree && tmp->root_tree)
+ {
+ create_match_char_tree(tmp);
+#ifdef NEED_DEBUG
+ ast_log(LOG_DEBUG,"Tree Created in context %s:\n", context);
+ log_match_char_tree(tmp->pattern_tree," ");
+#endif
+ }
+#ifdef NEED_DEBUG
+ ast_log(LOG_NOTICE,"The Trie we are searching in:\n");
+ log_match_char_tree(tmp->pattern_tree, ":: ");
+#endif
+
+ if (extenpatternmatchnew) {
+ new_find_extension(exten, &score, tmp->pattern_tree, 0, 0, callerid);
+ eroot = score.exten;
+
+ if (score.last_char == '!' && action == E_MATCHMORE) {
+ /* We match an extension ending in '!'.
+ * The decision in this case is final and is NULL (no match).
+ */
+#ifdef NEED_DEBUG
+ ast_log(LOG_NOTICE,"Returning MATCHMORE NULL with exclamation point.\n");
+#endif
+ return NULL;
+ }
+
+ if (!eroot && (action == E_CANMATCH || action == E_MATCHMORE) && score.canmatch_exten) {
+ q->status = STATUS_SUCCESS;
+#ifdef NEED_DEBUG
+ ast_log(LOG_NOTICE,"Returning CANMATCH exten %s\n", score.canmatch_exten->exten);
+#endif
+ return score.canmatch_exten;
+ }
+
+ if ((action == E_MATCHMORE || action == E_CANMATCH) && eroot) {
+ if (score.node) {
+ struct ast_exten *z = trie_find_next_match(score.node);
+#ifdef NEED_DEBUG
+ if (z)
+ ast_log(LOG_NOTICE,"Returning CANMATCH/MATCHMORE next_match exten %s\n", z->exten);
+ else
+ ast_log(LOG_NOTICE,"Returning CANMATCH/MATCHMORE next_match exten NULL\n");
+#endif
+ return z;
+ }
+#ifdef NEED_DEBUG
+ ast_log(LOG_NOTICE,"Returning CANMATCH/MATCHMORE NULL (no next_match)\n");
+#endif
+ return NULL; /* according to the code, complete matches are null matches in MATCHMORE mode */
+ }
+
+ if (eroot) {
+ /* found entry, now look for the right priority */
+ if (q->status < STATUS_NO_PRIORITY)
+ q->status = STATUS_NO_PRIORITY;
+ e = NULL;
+ if (action == E_FINDLABEL && label ) {
+ if (q->status < STATUS_NO_LABEL)
+ q->status = STATUS_NO_LABEL;
+ e = ast_hashtab_lookup(eroot->peer_label_tree, &pattern);
+ } else {
+ e = ast_hashtab_lookup(eroot->peer_tree, &pattern);
+ }
+ if (e) { /* found a valid match */
+ q->status = STATUS_SUCCESS;
+ q->foundcontext = context;
+#ifdef NEED_DEBUG
+ ast_log(LOG_NOTICE,"Returning complete match of exten %s\n", e->exten);
+#endif
+ return e;
+ }
+ }
+ } else {
+
+ /* scan the list trying to match extension and CID */
+ eroot = NULL;
+ while ( (eroot = ast_walk_context_extensions(tmp, eroot)) ) {
+ int match = extension_match_core(eroot->exten, exten, action);
+ /* 0 on fail, 1 on match, 2 on earlymatch */
+
+ if (!match || (eroot->matchcid && !matchcid(eroot->cidmatch, callerid)))
+ continue; /* keep trying */
+ if (match == 2 && action == E_MATCHMORE) {
+ /* We match an extension ending in '!'.
+ * The decision in this case is final and is NULL (no match).
+ */
+ return NULL;
+ }
+ /* found entry, now look for the right priority */
+ if (q->status < STATUS_NO_PRIORITY)
+ q->status = STATUS_NO_PRIORITY;
+ e = NULL;
+ if (action == E_FINDLABEL && label ) {
+ if (q->status < STATUS_NO_LABEL)
+ q->status = STATUS_NO_LABEL;
+ e = ast_hashtab_lookup(eroot->peer_label_tree, &pattern);
+ } else {
+ e = ast_hashtab_lookup(eroot->peer_tree, &pattern);
+ }
+#ifdef NOTNOW
+ while ( (e = ast_walk_extension_priorities(eroot, e)) ) {
+ /* Match label or priority */
+ if (action == E_FINDLABEL) {
+ if (q->status < STATUS_NO_LABEL)
+ q->status = STATUS_NO_LABEL;
+ if (label && e->label && !strcmp(label, e->label))
+ break; /* found it */
+ } else if (e->priority == priority) {
+ break; /* found it */
+ } /* else keep searching */
+ }
+#endif
+ if (e) { /* found a valid match */
+ q->status = STATUS_SUCCESS;
+ q->foundcontext = context;
+ return e;
+ }
+ }
+ }
+
+
+ /* Check alternative switches */
+ AST_LIST_TRAVERSE(&tmp->alts, sw, list) {
+ struct ast_switch *asw = pbx_findswitch(sw->name);
+ ast_switch_f *aswf = NULL;
+ char *datap;
+
+ if (!asw) {
+ ast_log(LOG_WARNING, "No such switch '%s'\n", sw->name);
+ continue;
+ }
+ /* Substitute variables now */
+
+ if (sw->eval)
+ pbx_substitute_variables_helper(chan, sw->data, sw->tmpdata, SWITCH_DATA_LENGTH - 1);
+
+ /* equivalent of extension_match_core() at the switch level */
+ if (action == E_CANMATCH)
+ aswf = asw->canmatch;
+ else if (action == E_MATCHMORE)
+ aswf = asw->matchmore;
+ else /* action == E_MATCH */
+ aswf = asw->exists;
+ datap = sw->eval ? sw->tmpdata : sw->data;
+ if (!aswf)
+ res = 0;
+ else {
+ if (chan)
+ ast_autoservice_start(chan);
+ res = aswf(chan, context, exten, priority, callerid, datap);
+ if (chan)
+ ast_autoservice_stop(chan);
+ }
+ if (res) { /* Got a match */
+ q->swo = asw;
+ q->data = datap;
+ q->foundcontext = context;
+ /* XXX keep status = STATUS_NO_CONTEXT ? */
+ return NULL;
+ }
+ }
+ q->incstack[q->stacklen++] = tmp->name; /* Setup the stack */
+ /* Now try any includes we have in this context */
+ for (i = tmp->includes; i; i = i->next) {
+ if (include_valid(i)) {
+ if ((e = pbx_find_extension(chan, bypass, q, i->rname, exten, priority, label, callerid, action))) {
+#ifdef NEED_DEBUG
+ ast_log(LOG_NOTICE,"Returning recursive match of %s\n", e->exten);
+#endif
+ return e;
+ }
+ if (q->swo)
+ return NULL;
+ }
+ }
+ return NULL;
+}
+
+/*!
+ * \brief extract offset:length from variable name.
+ * \return 1 if there is a offset:length part, which is
+ * trimmed off (values go into variables)
+ */
+static int parse_variable_name(char *var, int *offset, int *length, int *isfunc)
+{
+ int parens=0;
+
+ *offset = 0;
+ *length = INT_MAX;
+ *isfunc = 0;
+ for (; *var; var++) {
+ if (*var == '(') {
+ (*isfunc)++;
+ parens++;
+ } else if (*var == ')') {
+ parens--;
+ } else if (*var == ':' && parens == 0) {
+ *var++ = '\0';
+ sscanf(var, "%d:%d", offset, length);
+ return 1; /* offset:length valid */
+ }
+ }
+ return 0;
+}
+
+/*!
+ *\brief takes a substring. It is ok to call with value == workspace.
+ * \param value
+ * \param offset < 0 means start from the end of the string and set the beginning
+ * to be that many characters back.
+ * \param length is the length of the substring, a value less than 0 means to leave
+ * that many off the end.
+ * \param workspace
+ * \param workspace_len
+ * Always return a copy in workspace.
+ */
+static char *substring(const char *value, int offset, int length, char *workspace, size_t workspace_len)
+{
+ char *ret = workspace;
+ int lr; /* length of the input string after the copy */
+
+ ast_copy_string(workspace, value, workspace_len); /* always make a copy */
+
+ lr = strlen(ret); /* compute length after copy, so we never go out of the workspace */
+
+ /* Quick check if no need to do anything */
+ if (offset == 0 && length >= lr) /* take the whole string */
+ return ret;
+
+ if (offset < 0) { /* translate negative offset into positive ones */
+ offset = lr + offset;
+ if (offset < 0) /* If the negative offset was greater than the length of the string, just start at the beginning */
+ offset = 0;
+ }
+
+ /* too large offset result in empty string so we know what to return */
+ if (offset >= lr)
+ return ret + lr; /* the final '\0' */
+
+ ret += offset; /* move to the start position */
+ if (length >= 0 && length < lr - offset) /* truncate if necessary */
+ ret[length] = '\0';
+ else if (length < 0) {
+ if (lr > offset - length) /* After we remove from the front and from the rear, is there anything left? */
+ ret[lr + length - offset] = '\0';
+ else
+ ret[0] = '\0';
+ }
+
+ return ret;
+}
+
+/*! \brief Support for Asterisk built-in variables in the dialplan
+
+\note See also
+ - \ref AstVar Channel variables
+ - \ref AstCauses The HANGUPCAUSE variable
+ */
+void pbx_retrieve_variable(struct ast_channel *c, const char *var, char **ret, char *workspace, int workspacelen, struct varshead *headp)
+{
+ const char not_found = '\0';
+ char *tmpvar;
+ const char *s; /* the result */
+ int offset, length;
+ int i, need_substring;
+ struct varshead *places[2] = { headp, &globals }; /* list of places where we may look */
+
+ if (c) {
+ ast_channel_lock(c);
+ places[0] = &c->varshead;
+ }
+ /*
+ * Make a copy of var because parse_variable_name() modifies the string.
+ * Then if called directly, we might need to run substring() on the result;
+ * remember this for later in 'need_substring', 'offset' and 'length'
+ */
+ tmpvar = ast_strdupa(var); /* parse_variable_name modifies the string */
+ need_substring = parse_variable_name(tmpvar, &offset, &length, &i /* ignored */);
+
+ /*
+ * Look first into predefined variables, then into variable lists.
+ * Variable 's' points to the result, according to the following rules:
+ * s == &not_found (set at the beginning) means that we did not find a
+ * matching variable and need to look into more places.
+ * If s != &not_found, s is a valid result string as follows:
+ * s = NULL if the variable does not have a value;
+ * you typically do this when looking for an unset predefined variable.
+ * s = workspace if the result has been assembled there;
+ * typically done when the result is built e.g. with an snprintf(),
+ * so we don't need to do an additional copy.
+ * s != workspace in case we have a string, that needs to be copied
+ * (the ast_copy_string is done once for all at the end).
+ * Typically done when the result is already available in some string.
+ */
+ s = &not_found; /* default value */
+ if (c) { /* This group requires a valid channel */
+ /* Names with common parts are looked up a piece at a time using strncmp. */
+ if (!strncmp(var, "CALL", 4)) {
+ if (!strncmp(var + 4, "ING", 3)) {
+ if (!strcmp(var + 7, "PRES")) { /* CALLINGPRES */
+ snprintf(workspace, workspacelen, "%d", c->cid.cid_pres);
+ s = workspace;
+ } else if (!strcmp(var + 7, "ANI2")) { /* CALLINGANI2 */
+ snprintf(workspace, workspacelen, "%d", c->cid.cid_ani2);
+ s = workspace;
+ } else if (!strcmp(var + 7, "TON")) { /* CALLINGTON */
+ snprintf(workspace, workspacelen, "%d", c->cid.cid_ton);
+ s = workspace;
+ } else if (!strcmp(var + 7, "TNS")) { /* CALLINGTNS */
+ snprintf(workspace, workspacelen, "%d", c->cid.cid_tns);
+ s = workspace;
+ }
+ }
+ } else if (!strcmp(var, "HINT")) {
+ s = ast_get_hint(workspace, workspacelen, NULL, 0, c, c->context, c->exten) ? workspace : NULL;
+ } else if (!strcmp(var, "HINTNAME")) {
+ s = ast_get_hint(NULL, 0, workspace, workspacelen, c, c->context, c->exten) ? workspace : NULL;
+ } else if (!strcmp(var, "EXTEN")) {
+ s = c->exten;
+ } else if (!strcmp(var, "CONTEXT")) {
+ s = c->context;
+ } else if (!strcmp(var, "PRIORITY")) {
+ snprintf(workspace, workspacelen, "%d", c->priority);
+ s = workspace;
+ } else if (!strcmp(var, "CHANNEL")) {
+ s = c->name;
+ } else if (!strcmp(var, "UNIQUEID")) {
+ s = c->uniqueid;
+ } else if (!strcmp(var, "HANGUPCAUSE")) {
+ snprintf(workspace, workspacelen, "%d", c->hangupcause);
+ s = workspace;
+ }
+ }
+ if (s == &not_found) { /* look for more */
+ if (!strcmp(var, "EPOCH")) {
+ snprintf(workspace, workspacelen, "%u",(int)time(NULL));
+ s = workspace;
+ } else if (!strcmp(var, "SYSTEMNAME")) {
+ s = ast_config_AST_SYSTEM_NAME;
+ }
+ }
+ /* if not found, look into chanvars or global vars */
+ for (i = 0; s == &not_found && i < (sizeof(places) / sizeof(places[0])); i++) {
+ struct ast_var_t *variables;
+ if (!places[i])
+ continue;
+ if (places[i] == &globals)
+ ast_rwlock_rdlock(&globalslock);
+ AST_LIST_TRAVERSE(places[i], variables, entries) {
+ if (strcasecmp(ast_var_name(variables), var)==0) {
+ s = ast_var_value(variables);
+ break;
+ }
+ }
+ if (places[i] == &globals)
+ ast_rwlock_unlock(&globalslock);
+ }
+ if (s == &not_found || s == NULL)
+ *ret = NULL;
+ else {
+ if (s != workspace)
+ ast_copy_string(workspace, s, workspacelen);
+ *ret = workspace;
+ if (need_substring)
+ *ret = substring(*ret, offset, length, workspace, workspacelen);
+ }
+
+ if (c)
+ ast_channel_unlock(c);
+}
+
+static void exception_store_free(void *data)
+{
+ struct pbx_exception *exception = data;
+ ast_string_field_free_memory(exception);
+ ast_free(exception);
+}
+
+static struct ast_datastore_info exception_store_info = {
+ .type = "EXCEPTION",
+ .destroy = exception_store_free,
+};
+
+int pbx_builtin_raise_exception(struct ast_channel *chan, void *vreason)
+{
+ const char *reason = vreason;
+ struct ast_datastore *ds = ast_channel_datastore_find(chan, &exception_store_info, NULL);
+ struct pbx_exception *exception = NULL;
+
+ if (!ds) {
+ ds = ast_channel_datastore_alloc(&exception_store_info, NULL);
+ if (!ds)
+ return -1;
+ exception = ast_calloc(1, sizeof(struct pbx_exception));
+ if (!exception) {
+ ast_channel_datastore_free(ds);
+ return -1;
+ }
+ if (ast_string_field_init(exception, 128)) {
+ ast_free(exception);
+ ast_channel_datastore_free(ds);
+ return -1;
+ }
+ ds->data = exception;
+ ast_channel_datastore_add(chan, ds);
+ } else
+ exception = ds->data;
+
+ ast_string_field_set(exception, reason, reason);
+ ast_string_field_set(exception, context, chan->context);
+ ast_string_field_set(exception, exten, chan->exten);
+ exception->priority = chan->priority;
+ set_ext_pri(chan, "e", 0);
+ return 0;
+}
+
+static int acf_exception_read(struct ast_channel *chan, const char *name, char *data, char *buf, size_t buflen)
+{
+ struct ast_datastore *ds = ast_channel_datastore_find(chan, &exception_store_info, NULL);
+ struct pbx_exception *exception = NULL;
+ if (!ds || !ds->data)
+ return -1;
+ exception = ds->data;
+ if (!strcasecmp(data, "REASON"))
+ ast_copy_string(buf, exception->reason, buflen);
+ else if (!strcasecmp(data, "CONTEXT"))
+ ast_copy_string(buf, exception->context, buflen);
+ else if (!strncasecmp(data, "EXTEN", 5))
+ ast_copy_string(buf, exception->exten, buflen);
+ else if (!strcasecmp(data, "PRIORITY"))
+ snprintf(buf, buflen, "%d", exception->priority);
+ else
+ return -1;
+ return 0;
+}
+
+static struct ast_custom_function exception_function = {
+ .name = "EXCEPTION",
+ .synopsis = "Retrieve the details of the current dialplan exception",
+ .desc =
+"The following fields are available for retrieval:\n"
+" reason INVALID, ERROR, RESPONSETIMEOUT, ABSOLUTETIMEOUT, or custom\n"
+" value set by the RaiseException() application\n"
+" context The context executing when the exception occurred\n"
+" exten The extension executing when the exception occurred\n"
+" priority The numeric priority executing when the exception occurred\n",
+ .syntax = "EXCEPTION(<field>)",
+ .read = acf_exception_read,
+};
+
+static char *handle_show_functions(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_custom_function *acf;
+ int count_acf = 0;
+ int like = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show functions [like]";
+ e->usage =
+ "Usage: core show functions [like <text>]\n"
+ " List builtin functions, optionally only those matching a given string\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc == 5 && (!strcmp(a->argv[3], "like")) ) {
+ like = 1;
+ } else if (a->argc != 3) {
+ return CLI_SHOWUSAGE;
+ }
+
+ ast_cli(a->fd, "%s Custom Functions:\n--------------------------------------------------------------------------------\n", like ? "Matching" : "Installed");
+
+ AST_RWLIST_RDLOCK(&acf_root);
+ AST_RWLIST_TRAVERSE(&acf_root, acf, acflist) {
+ if (!like || strstr(acf->name, a->argv[4])) {
+ count_acf++;
+ ast_cli(a->fd, "%-20.20s %-35.35s %s\n", acf->name, acf->syntax, acf->synopsis);
+ }
+ }
+ AST_RWLIST_UNLOCK(&acf_root);
+
+ ast_cli(a->fd, "%d %scustom functions installed.\n", count_acf, like ? "matching " : "");
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_show_function(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_custom_function *acf;
+ /* Maximum number of characters added by terminal coloring is 22 */
+ char infotitle[64 + AST_MAX_APP + 22], syntitle[40], destitle[40];
+ char info[64 + AST_MAX_APP], *synopsis = NULL, *description = NULL;
+ char stxtitle[40], *syntax = NULL;
+ int synopsis_size, description_size, syntax_size;
+ char *ret = NULL;
+ int which = 0;
+ int wordlen;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show function";
+ e->usage =
+ "Usage: core show function <function>\n"
+ " Describe a particular dialplan function.\n";
+ return NULL;
+ case CLI_GENERATE:
+ wordlen = strlen(a->word);
+ /* case-insensitive for convenience in this 'complete' function */
+ AST_RWLIST_RDLOCK(&acf_root);
+ AST_RWLIST_TRAVERSE(&acf_root, acf, acflist) {
+ if (!strncasecmp(a->word, acf->name, wordlen) && ++which > a->n) {
+ ret = ast_strdup(acf->name);
+ break;
+ }
+ }
+ AST_RWLIST_UNLOCK(&acf_root);
+
+ return ret;
+ }
+
+ if (a->argc < 4)
+ return CLI_SHOWUSAGE;
+
+ if (!(acf = ast_custom_function_find(a->argv[3]))) {
+ ast_cli(a->fd, "No function by that name registered.\n");
+ return CLI_FAILURE;
+
+ }
+
+ if (acf->synopsis)
+ synopsis_size = strlen(acf->synopsis) + 23;
+ else
+ synopsis_size = strlen("Not available") + 23;
+ synopsis = alloca(synopsis_size);
+
+ if (acf->desc)
+ description_size = strlen(acf->desc) + 23;
+ else
+ description_size = strlen("Not available") + 23;
+ description = alloca(description_size);
+
+ if (acf->syntax)
+ syntax_size = strlen(acf->syntax) + 23;
+ else
+ syntax_size = strlen("Not available") + 23;
+ syntax = alloca(syntax_size);
+
+ snprintf(info, 64 + AST_MAX_APP, "\n -= Info about function '%s' =- \n\n", acf->name);
+ term_color(infotitle, info, COLOR_MAGENTA, 0, 64 + AST_MAX_APP + 22);
+ term_color(stxtitle, "[Syntax]\n", COLOR_MAGENTA, 0, 40);
+ term_color(syntitle, "[Synopsis]\n", COLOR_MAGENTA, 0, 40);
+ term_color(destitle, "[Description]\n", COLOR_MAGENTA, 0, 40);
+ term_color(syntax,
+ acf->syntax ? acf->syntax : "Not available",
+ COLOR_CYAN, 0, syntax_size);
+ term_color(synopsis,
+ acf->synopsis ? acf->synopsis : "Not available",
+ COLOR_CYAN, 0, synopsis_size);
+ term_color(description,
+ acf->desc ? acf->desc : "Not available",
+ COLOR_CYAN, 0, description_size);
+
+ ast_cli(a->fd,"%s%s%s\n\n%s%s\n\n%s%s\n", infotitle, stxtitle, syntax, syntitle, synopsis, destitle, description);
+
+ return CLI_SUCCESS;
+}
+
+struct ast_custom_function *ast_custom_function_find(const char *name)
+{
+ struct ast_custom_function *acf = NULL;
+
+ AST_RWLIST_RDLOCK(&acf_root);
+ AST_RWLIST_TRAVERSE(&acf_root, acf, acflist) {
+ if (!strcmp(name, acf->name))
+ break;
+ }
+ AST_RWLIST_UNLOCK(&acf_root);
+
+ return acf;
+}
+
+int ast_custom_function_unregister(struct ast_custom_function *acf)
+{
+ struct ast_custom_function *cur;
+
+ if (!acf)
+ return -1;
+
+ AST_RWLIST_WRLOCK(&acf_root);
+ if ((cur = AST_RWLIST_REMOVE(&acf_root, acf, acflist)))
+ ast_verb(2, "Unregistered custom function %s\n", cur->name);
+ AST_RWLIST_UNLOCK(&acf_root);
+
+ return cur ? 0 : -1;
+}
+
+int __ast_custom_function_register(struct ast_custom_function *acf, struct ast_module *mod)
+{
+ struct ast_custom_function *cur;
+ char tmps[80];
+
+ if (!acf)
+ return -1;
+
+ acf->mod = mod;
+
+ AST_RWLIST_WRLOCK(&acf_root);
+
+ AST_RWLIST_TRAVERSE(&acf_root, cur, acflist) {
+ if (!strcmp(acf->name, cur->name)) {
+ ast_log(LOG_ERROR, "Function %s already registered.\n", acf->name);
+ AST_RWLIST_UNLOCK(&acf_root);
+ return -1;
+ }
+ }
+
+ /* Store in alphabetical order */
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&acf_root, cur, acflist) {
+ if (strcasecmp(acf->name, cur->name) < 0) {
+ AST_RWLIST_INSERT_BEFORE_CURRENT(acf, acflist);
+ break;
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+ if (!cur)
+ AST_RWLIST_INSERT_TAIL(&acf_root, acf, acflist);
+
+ AST_RWLIST_UNLOCK(&acf_root);
+
+ ast_verb(2, "Registered custom function '%s'\n", term_color(tmps, acf->name, COLOR_BRCYAN, 0, sizeof(tmps)));
+
+ return 0;
+}
+
+/*! \brief return a pointer to the arguments of the function,
+ * and terminates the function name with '\\0'
+ */
+static char *func_args(char *function)
+{
+ char *args = strchr(function, '(');
+
+ if (!args)
+ ast_log(LOG_WARNING, "Function doesn't contain parentheses. Assuming null argument.\n");
+ else {
+ char *p;
+ *args++ = '\0';
+ if ((p = strrchr(args, ')')) )
+ *p = '\0';
+ else
+ ast_log(LOG_WARNING, "Can't find trailing parenthesis?\n");
+ }
+ return args;
+}
+
+int ast_func_read(struct ast_channel *chan, const char *function, char *workspace, size_t len)
+{
+ char *copy = ast_strdupa(function);
+ char *args = func_args(copy);
+ struct ast_custom_function *acfptr = ast_custom_function_find(copy);
+
+ if (acfptr == NULL)
+ ast_log(LOG_ERROR, "Function %s not registered\n", copy);
+ else if (!acfptr->read)
+ ast_log(LOG_ERROR, "Function %s cannot be read\n", copy);
+ else {
+ int res;
+ struct ast_module_user *u = NULL;
+ if (acfptr->mod)
+ u = __ast_module_user_add(acfptr->mod, chan);
+ res = acfptr->read(chan, copy, args, workspace, len);
+ if (acfptr->mod && u)
+ __ast_module_user_remove(acfptr->mod, u);
+ return res;
+ }
+ return -1;
+}
+
+int ast_func_write(struct ast_channel *chan, const char *function, const char *value)
+{
+ char *copy = ast_strdupa(function);
+ char *args = func_args(copy);
+ struct ast_custom_function *acfptr = ast_custom_function_find(copy);
+
+ if (acfptr == NULL)
+ ast_log(LOG_ERROR, "Function %s not registered\n", copy);
+ else if (!acfptr->write)
+ ast_log(LOG_ERROR, "Function %s cannot be written to\n", copy);
+ else {
+ int res;
+ struct ast_module_user *u = NULL;
+ if (acfptr->mod)
+ u = __ast_module_user_add(acfptr->mod, chan);
+ res = acfptr->write(chan, copy, args, value);
+ if (acfptr->mod && u)
+ __ast_module_user_remove(acfptr->mod, u);
+ return res;
+ }
+
+ return -1;
+}
+
+static void pbx_substitute_variables_helper_full(struct ast_channel *c, struct varshead *headp, const char *cp1, char *cp2, int count)
+{
+ /* Substitutes variables into cp2, based on string cp1, cp2 NO LONGER NEEDS TO BE ZEROED OUT!!!! */
+ char *cp4;
+ const char *tmp, *whereweare;
+ int length, offset, offset2, isfunction;
+ char *workspace = NULL;
+ char *ltmp = NULL, *var = NULL;
+ char *nextvar, *nextexp, *nextthing;
+ char *vars, *vare;
+ int pos, brackets, needsub, len;
+
+ *cp2 = 0; /* just in case nothing ends up there */
+ whereweare=tmp=cp1;
+ while (!ast_strlen_zero(whereweare) && count) {
+ /* Assume we're copying the whole remaining string */
+ pos = strlen(whereweare);
+ nextvar = NULL;
+ nextexp = NULL;
+ nextthing = strchr(whereweare, '$');
+ if (nextthing) {
+ switch (nextthing[1]) {
+ case '{':
+ nextvar = nextthing;
+ pos = nextvar - whereweare;
+ break;
+ case '[':
+ nextexp = nextthing;
+ pos = nextexp - whereweare;
+ break;
+ default:
+ pos = 1;
+ }
+ }
+
+ if (pos) {
+ /* Can't copy more than 'count' bytes */
+ if (pos > count)
+ pos = count;
+
+ /* Copy that many bytes */
+ memcpy(cp2, whereweare, pos);
+
+ count -= pos;
+ cp2 += pos;
+ whereweare += pos;
+ *cp2 = 0;
+ }
+
+ if (nextvar) {
+ /* We have a variable. Find the start and end, and determine
+ if we are going to have to recursively call ourselves on the
+ contents */
+ vars = vare = nextvar + 2;
+ brackets = 1;
+ needsub = 0;
+
+ /* Find the end of it */
+ while (brackets && *vare) {
+ if ((vare[0] == '$') && (vare[1] == '{')) {
+ needsub++;
+ } else if (vare[0] == '{') {
+ brackets++;
+ } else if (vare[0] == '}') {
+ brackets--;
+ } else if ((vare[0] == '$') && (vare[1] == '['))
+ needsub++;
+ vare++;
+ }
+ if (brackets)
+ ast_log(LOG_NOTICE, "Error in extension logic (missing '}')\n");
+ len = vare - vars - 1;
+
+ /* Skip totally over variable string */
+ whereweare += (len + 3);
+
+ if (!var)
+ var = alloca(VAR_BUF_SIZE);
+
+ /* Store variable name (and truncate) */
+ ast_copy_string(var, vars, len + 1);
+
+ /* Substitute if necessary */
+ if (needsub) {
+ if (!ltmp)
+ ltmp = alloca(VAR_BUF_SIZE);
+
+ pbx_substitute_variables_helper_full(c, headp, var, ltmp, VAR_BUF_SIZE - 1);
+ vars = ltmp;
+ } else {
+ vars = var;
+ }
+
+ if (!workspace)
+ workspace = alloca(VAR_BUF_SIZE);
+
+ workspace[0] = '\0';
+
+ parse_variable_name(vars, &offset, &offset2, &isfunction);
+ if (isfunction) {
+ /* Evaluate function */
+ if (c || !headp)
+ cp4 = ast_func_read(c, vars, workspace, VAR_BUF_SIZE) ? NULL : workspace;
+ else {
+ struct varshead old;
+ struct ast_channel *c = ast_channel_alloc(0, 0, "", "", "", "", "", 0, "Bogus/%p", vars);
+ if (c) {
+ memcpy(&old, &c->varshead, sizeof(old));
+ memcpy(&c->varshead, headp, sizeof(c->varshead));
+ cp4 = ast_func_read(c, vars, workspace, VAR_BUF_SIZE) ? NULL : workspace;
+ /* Don't deallocate the varshead that was passed in */
+ memcpy(&c->varshead, &old, sizeof(c->varshead));
+ ast_channel_free(c);
+ } else
+ ast_log(LOG_ERROR, "Unable to allocate bogus channel for variable substitution. Function results may be blank.\n");
+ }
+ ast_debug(1, "Function result is '%s'\n", cp4 ? cp4 : "(null)");
+ } else {
+ /* Retrieve variable value */
+ pbx_retrieve_variable(c, vars, &cp4, workspace, VAR_BUF_SIZE, headp);
+ }
+ if (cp4) {
+ cp4 = substring(cp4, offset, offset2, workspace, VAR_BUF_SIZE);
+
+ length = strlen(cp4);
+ if (length > count)
+ length = count;
+ memcpy(cp2, cp4, length);
+ count -= length;
+ cp2 += length;
+ *cp2 = 0;
+ }
+ } else if (nextexp) {
+ /* We have an expression. Find the start and end, and determine
+ if we are going to have to recursively call ourselves on the
+ contents */
+ vars = vare = nextexp + 2;
+ brackets = 1;
+ needsub = 0;
+
+ /* Find the end of it */
+ while (brackets && *vare) {
+ if ((vare[0] == '$') && (vare[1] == '[')) {
+ needsub++;
+ brackets++;
+ vare++;
+ } else if (vare[0] == '[') {
+ brackets++;
+ } else if (vare[0] == ']') {
+ brackets--;
+ } else if ((vare[0] == '$') && (vare[1] == '{')) {
+ needsub++;
+ vare++;
+ }
+ vare++;
+ }
+ if (brackets)
+ ast_log(LOG_NOTICE, "Error in extension logic (missing ']')\n");
+ len = vare - vars - 1;
+
+ /* Skip totally over expression */
+ whereweare += (len + 3);
+
+ if (!var)
+ var = alloca(VAR_BUF_SIZE);
+
+ /* Store variable name (and truncate) */
+ ast_copy_string(var, vars, len + 1);
+
+ /* Substitute if necessary */
+ if (needsub) {
+ if (!ltmp)
+ ltmp = alloca(VAR_BUF_SIZE);
+
+ pbx_substitute_variables_helper_full(c, headp, var, ltmp, VAR_BUF_SIZE - 1);
+ vars = ltmp;
+ } else {
+ vars = var;
+ }
+
+ length = ast_expr(vars, cp2, count, c);
+
+ if (length) {
+ ast_debug(1, "Expression result is '%s'\n", cp2);
+ count -= length;
+ cp2 += length;
+ *cp2 = 0;
+ }
+ }
+ }
+}
+
+void pbx_substitute_variables_helper(struct ast_channel *c, const char *cp1, char *cp2, int count)
+{
+ pbx_substitute_variables_helper_full(c, (c) ? &c->varshead : NULL, cp1, cp2, count);
+}
+
+void pbx_substitute_variables_varshead(struct varshead *headp, const char *cp1, char *cp2, int count)
+{
+ pbx_substitute_variables_helper_full(NULL, headp, cp1, cp2, count);
+}
+
+static void pbx_substitute_variables(char *passdata, int datalen, struct ast_channel *c, struct ast_exten *e)
+{
+ const char *tmp;
+
+ /* Nothing more to do */
+ if (!e->data)
+ return;
+
+ /* No variables or expressions in e->data, so why scan it? */
+ if ((!(tmp = strchr(e->data, '$'))) || (!strstr(tmp, "${") && !strstr(tmp, "$["))) {
+ ast_copy_string(passdata, e->data, datalen);
+ return;
+ }
+
+ pbx_substitute_variables_helper(c, e->data, passdata, datalen - 1);
+}
+
+/*!
+ * \brief The return value depends on the action:
+ *
+ * E_MATCH, E_CANMATCH, E_MATCHMORE require a real match,
+ * and return 0 on failure, -1 on match;
+ * E_FINDLABEL maps the label to a priority, and returns
+ * the priority on success, ... XXX
+ * E_SPAWN, spawn an application,
+ *
+ * \retval 0 on success.
+ * \retval -1 on failure.
+ *
+ * \note The channel is auto-serviced in this function, because doing an extension
+ * match may block for a long time. For example, if the lookup has to use a network
+ * dialplan switch, such as DUNDi or IAX2, it may take a while. However, the channel
+ * auto-service code will queue up any important signalling frames to be processed
+ * after this is done.
+ */
+static int pbx_extension_helper(struct ast_channel *c, struct ast_context *con,
+ const char *context, const char *exten, int priority,
+ const char *label, const char *callerid, enum ext_match_t action, int *found, int combined_find_spawn)
+{
+ struct ast_exten *e;
+ struct ast_app *app;
+ int res;
+ struct pbx_find_info q = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */
+ char passdata[EXT_DATA_SIZE];
+
+ int matching_action = (action == E_MATCH || action == E_CANMATCH || action == E_MATCHMORE);
+
+ ast_rdlock_contexts();
+ if (found)
+ *found = 0;
+
+ e = pbx_find_extension(c, con, &q, context, exten, priority, label, callerid, action);
+ if (e) {
+ if (found)
+ *found = 1;
+ if (matching_action) {
+ ast_unlock_contexts();
+ return -1; /* success, we found it */
+ } else if (action == E_FINDLABEL) { /* map the label to a priority */
+ res = e->priority;
+ ast_unlock_contexts();
+ return res; /* the priority we were looking for */
+ } else { /* spawn */
+ if (!e->cached_app)
+ e->cached_app = pbx_findapp(e->app);
+ app = e->cached_app;
+ ast_unlock_contexts();
+ if (!app) {
+ ast_log(LOG_WARNING, "No application '%s' for extension (%s, %s, %d)\n", e->app, context, exten, priority);
+ return -1;
+ }
+ if (c->context != context)
+ ast_copy_string(c->context, context, sizeof(c->context));
+ if (c->exten != exten)
+ ast_copy_string(c->exten, exten, sizeof(c->exten));
+ c->priority = priority;
+ pbx_substitute_variables(passdata, sizeof(passdata), c, e);
+ ast_debug(1, "Launching '%s'\n", app->name);
+ if (VERBOSITY_ATLEAST(3)) {
+ char tmp[80], tmp2[80], tmp3[EXT_DATA_SIZE];
+ ast_verb(3, "Executing [%s@%s:%d] %s(\"%s\", \"%s\") %s\n",
+ exten, context, priority,
+ term_color(tmp, app->name, COLOR_BRCYAN, 0, sizeof(tmp)),
+ term_color(tmp2, c->name, COLOR_BRMAGENTA, 0, sizeof(tmp2)),
+ term_color(tmp3, passdata, COLOR_BRMAGENTA, 0, sizeof(tmp3)),
+ "in new stack");
+ }
+ manager_event(EVENT_FLAG_DIALPLAN, "Newexten",
+ "Channel: %s\r\n"
+ "Context: %s\r\n"
+ "Extension: %s\r\n"
+ "Priority: %d\r\n"
+ "Application: %s\r\n"
+ "AppData: %s\r\n"
+ "Uniqueid: %s\r\n",
+ c->name, c->context, c->exten, c->priority, app->name, passdata, c->uniqueid);
+ return pbx_exec(c, app, passdata); /* 0 on success, -1 on failure */
+ }
+ } else if (q.swo) { /* not found here, but in another switch */
+ ast_unlock_contexts();
+ if (matching_action) {
+ return -1;
+ } else {
+ if (!q.swo->exec) {
+ ast_log(LOG_WARNING, "No execution engine for switch %s\n", q.swo->name);
+ res = -1;
+ }
+ return q.swo->exec(c, q.foundcontext ? q.foundcontext : context, exten, priority, callerid, q.data);
+ }
+ } else { /* not found anywhere, see what happened */
+ ast_unlock_contexts();
+ switch (q.status) {
+ case STATUS_NO_CONTEXT:
+ if (!matching_action && !combined_find_spawn)
+ ast_log(LOG_NOTICE, "Cannot find extension context '%s'\n", context);
+ break;
+ case STATUS_NO_EXTENSION:
+ if (!matching_action && !combined_find_spawn)
+ ast_log(LOG_NOTICE, "Cannot find extension '%s' in context '%s'\n", exten, context);
+ break;
+ case STATUS_NO_PRIORITY:
+ if (!matching_action && !combined_find_spawn)
+ ast_log(LOG_NOTICE, "No such priority %d in extension '%s' in context '%s'\n", priority, exten, context);
+ break;
+ case STATUS_NO_LABEL:
+ if (context && !combined_find_spawn)
+ ast_log(LOG_NOTICE, "No such label '%s' in extension '%s' in context '%s'\n", label, exten, context);
+ break;
+ default:
+ ast_debug(1, "Shouldn't happen!\n");
+ }
+
+ return (matching_action) ? 0 : -1;
+ }
+}
+
+/*! \brief Find hint for given extension in context */
+static struct ast_exten *ast_hint_extension(struct ast_channel *c, const char *context, const char *exten)
+{
+ struct ast_exten *e;
+ struct pbx_find_info q = { .stacklen = 0 }; /* the rest is set in pbx_find_context */
+
+ ast_rdlock_contexts();
+ e = pbx_find_extension(c, NULL, &q, context, exten, PRIORITY_HINT, NULL, "", E_MATCH);
+ ast_unlock_contexts();
+
+ return e;
+}
+
+/*! \brief Check state of extension by using hints */
+static int ast_extension_state2(struct ast_exten *e)
+{
+ char hint[AST_MAX_EXTENSION];
+ char *cur, *rest;
+ int allunavailable = 1, allbusy = 1, allfree = 1, allonhold = 1;
+ int busy = 0, inuse = 0, ring = 0;
+
+ if (!e)
+ return -1;
+
+ ast_copy_string(hint, ast_get_extension_app(e), sizeof(hint));
+
+ rest = hint; /* One or more devices separated with a & character */
+ while ( (cur = strsep(&rest, "&")) ) {
+ int res = ast_device_state(cur);
+ switch (res) {
+ case AST_DEVICE_NOT_INUSE:
+ allunavailable = 0;
+ allbusy = 0;
+ allonhold = 0;
+ break;
+ case AST_DEVICE_INUSE:
+ inuse = 1;
+ allunavailable = 0;
+ allfree = 0;
+ allonhold = 0;
+ break;
+ case AST_DEVICE_RINGING:
+ ring = 1;
+ allunavailable = 0;
+ allfree = 0;
+ allonhold = 0;
+ break;
+ case AST_DEVICE_RINGINUSE:
+ inuse = 1;
+ ring = 1;
+ allunavailable = 0;
+ allfree = 0;
+ allonhold = 0;
+ break;
+ case AST_DEVICE_ONHOLD:
+ allunavailable = 0;
+ allfree = 0;
+ break;
+ case AST_DEVICE_BUSY:
+ allunavailable = 0;
+ allfree = 0;
+ allonhold = 0;
+ busy = 1;
+ break;
+ case AST_DEVICE_UNAVAILABLE:
+ case AST_DEVICE_INVALID:
+ allbusy = 0;
+ allfree = 0;
+ allonhold = 0;
+ break;
+ default:
+ allunavailable = 0;
+ allbusy = 0;
+ allfree = 0;
+ allonhold = 0;
+ }
+ }
+
+ if (!inuse && ring)
+ return AST_EXTENSION_RINGING;
+ if (inuse && ring)
+ return (AST_EXTENSION_INUSE | AST_EXTENSION_RINGING);
+ if (inuse)
+ return AST_EXTENSION_INUSE;
+ if (allfree)
+ return AST_EXTENSION_NOT_INUSE;
+ if (allonhold)
+ return AST_EXTENSION_ONHOLD;
+ if (allbusy)
+ return AST_EXTENSION_BUSY;
+ if (allunavailable)
+ return AST_EXTENSION_UNAVAILABLE;
+ if (busy)
+ return AST_EXTENSION_INUSE;
+
+ return AST_EXTENSION_NOT_INUSE;
+}
+
+/*! \brief Return extension_state as string */
+const char *ast_extension_state2str(int extension_state)
+{
+ int i;
+
+ for (i = 0; (i < (sizeof(extension_states) / sizeof(extension_states[0]))); i++) {
+ if (extension_states[i].extension_state == extension_state)
+ return extension_states[i].text;
+ }
+ return "Unknown";
+}
+
+/*! \brief Check extension state for an extension by using hint */
+int ast_extension_state(struct ast_channel *c, const char *context, const char *exten)
+{
+ struct ast_exten *e;
+
+ e = ast_hint_extension(c, context, exten); /* Do we have a hint for this extension ? */
+ if (!e)
+ return -1; /* No hint, return -1 */
+
+ return ast_extension_state2(e); /* Check all devices in the hint */
+}
+
+static void handle_statechange(const char *device)
+{
+ struct ast_hint *hint;
+
+ AST_RWLIST_RDLOCK(&hints);
+
+ AST_RWLIST_TRAVERSE(&hints, hint, list) {
+ struct ast_state_cb *cblist;
+ char buf[AST_MAX_EXTENSION];
+ char *parse = buf;
+ char *cur;
+ int state;
+
+ ast_copy_string(buf, ast_get_extension_app(hint->exten), sizeof(buf));
+ while ( (cur = strsep(&parse, "&")) ) {
+ if (!strcasecmp(cur, device))
+ break;
+ }
+ if (!cur)
+ continue;
+
+ /* Get device state for this hint */
+ state = ast_extension_state2(hint->exten);
+
+ if ((state == -1) || (state == hint->laststate))
+ continue;
+
+ /* Device state changed since last check - notify the watchers */
+
+ /* For general callbacks */
+ for (cblist = statecbs; cblist; cblist = cblist->next)
+ cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data);
+
+ /* For extension callbacks */
+ for (cblist = hint->callbacks; cblist; cblist = cblist->next)
+ cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data);
+
+ hint->laststate = state; /* record we saw the change */
+ }
+
+ AST_RWLIST_UNLOCK(&hints);
+}
+
+static int statechange_queue(const char *dev)
+{
+ struct statechange *sc;
+
+ if (!(sc = ast_calloc(1, sizeof(*sc) + strlen(dev) + 1)))
+ return 0;
+
+ strcpy(sc->dev, dev);
+
+ ast_mutex_lock(&device_state.lock);
+ AST_LIST_INSERT_TAIL(&device_state.state_change_q, sc, entry);
+ ast_cond_signal(&device_state.cond);
+ ast_mutex_unlock(&device_state.lock);
+
+ return 0;
+}
+
+static void *device_state_thread(void *data)
+{
+ struct statechange *sc;
+
+ while (!device_state.stop) {
+ ast_mutex_lock(&device_state.lock);
+ while (!(sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry))) {
+ ast_cond_wait(&device_state.cond, &device_state.lock);
+ /* Check to see if we were woken up to see the request to stop */
+ if (device_state.stop) {
+ ast_mutex_unlock(&device_state.lock);
+ return NULL;
+ }
+ }
+ ast_mutex_unlock(&device_state.lock);
+
+ handle_statechange(sc->dev);
+
+ ast_free(sc);
+ }
+
+ return NULL;
+}
+
+/*! \brief Add watcher for extension states */
+int ast_extension_state_add(const char *context, const char *exten,
+ ast_state_cb_type callback, void *data)
+{
+ struct ast_hint *hint;
+ struct ast_state_cb *cblist;
+ struct ast_exten *e;
+
+ /* If there's no context and extension: add callback to statecbs list */
+ if (!context && !exten) {
+ AST_RWLIST_WRLOCK(&hints);
+
+ for (cblist = statecbs; cblist; cblist = cblist->next) {
+ if (cblist->callback == callback) {
+ cblist->data = data;
+ AST_RWLIST_UNLOCK(&hints);
+ return 0;
+ }
+ }
+
+ /* Now insert the callback */
+ if (!(cblist = ast_calloc(1, sizeof(*cblist)))) {
+ AST_RWLIST_UNLOCK(&hints);
+ return -1;
+ }
+ cblist->id = 0;
+ cblist->callback = callback;
+ cblist->data = data;
+
+ cblist->next = statecbs;
+ statecbs = cblist;
+
+ AST_RWLIST_UNLOCK(&hints);
+ return 0;
+ }
+
+ if (!context || !exten)
+ return -1;
+
+ /* This callback type is for only one hint, so get the hint */
+ e = ast_hint_extension(NULL, context, exten);
+ if (!e) {
+ return -1;
+ }
+
+ /* Find the hint in the list of hints */
+ AST_RWLIST_WRLOCK(&hints);
+
+ AST_RWLIST_TRAVERSE(&hints, hint, list) {
+ if (hint->exten == e)
+ break;
+ }
+
+ if (!hint) {
+ /* We have no hint, sorry */
+ AST_RWLIST_UNLOCK(&hints);
+ return -1;
+ }
+
+ /* Now insert the callback in the callback list */
+ if (!(cblist = ast_calloc(1, sizeof(*cblist)))) {
+ AST_RWLIST_UNLOCK(&hints);
+ return -1;
+ }
+ cblist->id = stateid++; /* Unique ID for this callback */
+ cblist->callback = callback; /* Pointer to callback routine */
+ cblist->data = data; /* Data for the callback */
+
+ cblist->next = hint->callbacks;
+ hint->callbacks = cblist;
+
+ AST_RWLIST_UNLOCK(&hints);
+ return cblist->id;
+}
+
+/*! \brief Remove a watcher from the callback list */
+int ast_extension_state_del(int id, ast_state_cb_type callback)
+{
+ struct ast_state_cb **p_cur = NULL; /* address of pointer to us */
+ int ret = -1;
+
+ if (!id && !callback)
+ return -1;
+
+ AST_RWLIST_WRLOCK(&hints);
+
+ if (!id) { /* id == 0 is a callback without extension */
+ for (p_cur = &statecbs; *p_cur; p_cur = &(*p_cur)->next) {
+ if ((*p_cur)->callback == callback)
+ break;
+ }
+ } else { /* callback with extension, find the callback based on ID */
+ struct ast_hint *hint;
+ AST_RWLIST_TRAVERSE(&hints, hint, list) {
+ for (p_cur = &hint->callbacks; *p_cur; p_cur = &(*p_cur)->next) {
+ if ((*p_cur)->id == id)
+ break;
+ }
+ if (*p_cur) /* found in the inner loop */
+ break;
+ }
+ }
+ if (p_cur && *p_cur) {
+ struct ast_state_cb *cur = *p_cur;
+ *p_cur = cur->next;
+ ast_free(cur);
+ ret = 0;
+ }
+ AST_RWLIST_UNLOCK(&hints);
+ return ret;
+}
+
+/*! \brief Add hint to hint list, check initial extension state */
+static int ast_add_hint(struct ast_exten *e)
+{
+ struct ast_hint *hint;
+
+ if (!e)
+ return -1;
+
+ AST_RWLIST_WRLOCK(&hints);
+
+ /* Search if hint exists, do nothing */
+ AST_RWLIST_TRAVERSE(&hints, hint, list) {
+ if (hint->exten == e) {
+ AST_RWLIST_UNLOCK(&hints);
+ ast_debug(2, "HINTS: Not re-adding existing hint %s: %s\n", ast_get_extension_name(e), ast_get_extension_app(e));
+ return -1;
+ }
+ }
+
+ ast_debug(2, "HINTS: Adding hint %s: %s\n", ast_get_extension_name(e), ast_get_extension_app(e));
+
+ if (!(hint = ast_calloc(1, sizeof(*hint)))) {
+ AST_RWLIST_UNLOCK(&hints);
+ return -1;
+ }
+ /* Initialize and insert new item at the top */
+ hint->exten = e;
+ hint->laststate = ast_extension_state2(e);
+ AST_RWLIST_INSERT_HEAD(&hints, hint, list);
+
+ AST_RWLIST_UNLOCK(&hints);
+ return 0;
+}
+
+/*! \brief Change hint for an extension */
+static int ast_change_hint(struct ast_exten *oe, struct ast_exten *ne)
+{
+ struct ast_hint *hint;
+ int res = -1;
+
+ AST_RWLIST_WRLOCK(&hints);
+ AST_RWLIST_TRAVERSE(&hints, hint, list) {
+ if (hint->exten == oe) {
+ hint->exten = ne;
+ res = 0;
+ break;
+ }
+ }
+ AST_RWLIST_UNLOCK(&hints);
+
+ return res;
+}
+
+/*! \brief Remove hint from extension */
+static int ast_remove_hint(struct ast_exten *e)
+{
+ /* Cleanup the Notifys if hint is removed */
+ struct ast_hint *hint;
+ struct ast_state_cb *cblist, *cbprev;
+ int res = -1;
+
+ if (!e)
+ return -1;
+
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&hints, hint, list) {
+ if (hint->exten == e) {
+ cbprev = NULL;
+ cblist = hint->callbacks;
+ while (cblist) {
+ /* Notify with -1 and remove all callbacks */
+ cbprev = cblist;
+ cblist = cblist->next;
+ cbprev->callback(hint->exten->parent->name, hint->exten->exten, AST_EXTENSION_DEACTIVATED, cbprev->data);
+ ast_free(cbprev);
+ }
+ hint->callbacks = NULL;
+ AST_RWLIST_REMOVE_CURRENT(list);
+ ast_free(hint);
+ res = 0;
+ break;
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+
+ return res;
+}
+
+
+/*! \brief Get hint for channel */
+int ast_get_hint(char *hint, int hintsize, char *name, int namesize, struct ast_channel *c, const char *context, const char *exten)
+{
+ struct ast_exten *e = ast_hint_extension(c, context, exten);
+
+ if (e) {
+ if (hint)
+ ast_copy_string(hint, ast_get_extension_app(e), hintsize);
+ if (name) {
+ const char *tmp = ast_get_extension_app_data(e);
+ if (tmp)
+ ast_copy_string(name, tmp, namesize);
+ }
+ return -1;
+ }
+ return 0;
+}
+
+int ast_exists_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
+{
+ return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_MATCH, 0, 0);
+}
+
+int ast_findlabel_extension(struct ast_channel *c, const char *context, const char *exten, const char *label, const char *callerid)
+{
+ return pbx_extension_helper(c, NULL, context, exten, 0, label, callerid, E_FINDLABEL, 0, 0);
+}
+
+int ast_findlabel_extension2(struct ast_channel *c, struct ast_context *con, const char *exten, const char *label, const char *callerid)
+{
+ return pbx_extension_helper(c, con, NULL, exten, 0, label, callerid, E_FINDLABEL, 0, 0);
+}
+
+int ast_canmatch_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
+{
+ return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_CANMATCH, 0, 0);
+}
+
+int ast_matchmore_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
+{
+ return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_MATCHMORE, 0, 0);
+}
+
+int ast_spawn_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid, int *found, int combined_find_spawn)
+{
+ return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, E_SPAWN, found, combined_find_spawn);
+}
+
+/*! helper function to set extension and priority */
+static void set_ext_pri(struct ast_channel *c, const char *exten, int pri)
+{
+ ast_channel_lock(c);
+ ast_copy_string(c->exten, exten, sizeof(c->exten));
+ c->priority = pri;
+ ast_channel_unlock(c);
+}
+
+/*!
+ * \brief collect digits from the channel into the buffer.
+ * \retval 0 on timeout or done.
+ * \retval -1 on error.
+*/
+static int collect_digits(struct ast_channel *c, int waittime, char *buf, int buflen, int pos)
+{
+ int digit;
+
+ buf[pos] = '\0'; /* make sure it is properly terminated */
+ while (ast_matchmore_extension(c, c->context, buf, 1, c->cid.cid_num)) {
+ /* As long as we're willing to wait, and as long as it's not defined,
+ keep reading digits until we can't possibly get a right answer anymore. */
+ digit = ast_waitfordigit(c, waittime * 1000);
+ if (c->_softhangup == AST_SOFTHANGUP_ASYNCGOTO) {
+ c->_softhangup = 0;
+ } else {
+ if (!digit) /* No entry */
+ break;
+ if (digit < 0) /* Error, maybe a hangup */
+ return -1;
+ if (pos < buflen - 1) { /* XXX maybe error otherwise ? */
+ buf[pos++] = digit;
+ buf[pos] = '\0';
+ }
+ waittime = c->pbx->dtimeout;
+ }
+ }
+ return 0;
+}
+
+static int __ast_pbx_run(struct ast_channel *c)
+{
+ int found = 0; /* set if we find at least one match */
+ int res = 0;
+ int autoloopflag;
+ int error = 0; /* set an error conditions */
+
+ /* A little initial setup here */
+ if (c->pbx) {
+ ast_log(LOG_WARNING, "%s already has PBX structure??\n", c->name);
+ /* XXX and now what ? */
+ ast_free(c->pbx);
+ }
+ if (!(c->pbx = ast_calloc(1, sizeof(*c->pbx))))
+ return -1;
+ if (c->amaflags) {
+ if (!c->cdr) {
+ c->cdr = ast_cdr_alloc();
+ if (!c->cdr) {
+ ast_log(LOG_WARNING, "Unable to create Call Detail Record\n");
+ ast_free(c->pbx);
+ return -1;
+ }
+ ast_cdr_init(c->cdr, c);
+ }
+ }
+ /* Set reasonable defaults */
+ c->pbx->rtimeout = 10;
+ c->pbx->dtimeout = 5;
+
+ autoloopflag = ast_test_flag(c, AST_FLAG_IN_AUTOLOOP); /* save value to restore at the end */
+ ast_set_flag(c, AST_FLAG_IN_AUTOLOOP);
+
+ /* Start by trying whatever the channel is set to */
+ if (!ast_exists_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) {
+ /* If not successful fall back to 's' */
+ ast_verb(2, "Starting %s at %s,%s,%d failed so falling back to exten 's'\n", c->name, c->context, c->exten, c->priority);
+ /* XXX the original code used the existing priority in the call to
+ * ast_exists_extension(), and reset it to 1 afterwards.
+ * I believe the correct thing is to set it to 1 immediately.
+ */
+ set_ext_pri(c, "s", 1);
+ if (!ast_exists_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) {
+ /* JK02: And finally back to default if everything else failed */
+ ast_verb(2, "Starting %s at %s,%s,%d still failed so falling back to context 'default'\n", c->name, c->context, c->exten, c->priority);
+ ast_copy_string(c->context, "default", sizeof(c->context));
+ }
+ }
+ if (c->cdr && ast_tvzero(c->cdr->start))
+ ast_cdr_start(c->cdr);
+ for (;;) {
+ char dst_exten[256]; /* buffer to accumulate digits */
+ int pos = 0; /* XXX should check bounds */
+ int digit = 0;
+
+ /* loop on priorities in this context/exten */
+ while ( !(res = ast_spawn_extension(c, c->context, c->exten, c->priority, c->cid.cid_num, &found,1))) {
+ if (c->_softhangup == AST_SOFTHANGUP_TIMEOUT && ast_exists_extension(c, c->context, "T", 1, c->cid.cid_num)) {
+ set_ext_pri(c, "T", 0); /* 0 will become 1 with the c->priority++; at the end */
+ /* If the AbsoluteTimeout is not reset to 0, we'll get an infinite loop */
+ c->whentohangup = 0;
+ c->_softhangup &= ~AST_SOFTHANGUP_TIMEOUT;
+ } else if (c->_softhangup == AST_SOFTHANGUP_TIMEOUT && ast_exists_extension(c, c->context, "e", 1, c->cid.cid_num)) {
+ pbx_builtin_raise_exception(c, "ABSOLUTETIMEOUT");
+ /* If the AbsoluteTimeout is not reset to 0, we'll get an infinite loop */
+ c->whentohangup = 0;
+ c->_softhangup &= ~AST_SOFTHANGUP_TIMEOUT;
+ } else if (ast_check_hangup(c)) {
+ ast_debug(1, "Extension %s, priority %d returned normally even though call was hung up\n",
+ c->exten, c->priority);
+ error = 1;
+ break;
+ }
+ c->priority++;
+ } /* end while - from here on we can use 'break' to go out */
+ if (found && res) {
+ /* Something bad happened, or a hangup has been requested. */
+ if (strchr("0123456789ABCDEF*#", res)) {
+ ast_debug(1, "Oooh, got something to jump out with ('%c')!\n", res);
+ pos = 0;
+ dst_exten[pos++] = digit = res;
+ dst_exten[pos] = '\0';
+ } else if (res == AST_PBX_KEEPALIVE) {
+ ast_debug(1, "Spawn extension (%s,%s,%d) exited KEEPALIVE on '%s'\n", c->context, c->exten, c->priority, c->name);
+ ast_verb(2, "Spawn extension (%s, %s, %d) exited KEEPALIVE on '%s'\n", c->context, c->exten, c->priority, c->name);
+ error = 1;
+ } else {
+ ast_debug(1, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
+ ast_verb(2, "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
+
+ if ((res == AST_PBX_ERROR) && ast_exists_extension(c, c->context, "e", 1, c->cid.cid_num)) {
+ /* if we are already on the 'e' exten, don't jump to it again */
+ if (!strcmp(c->exten, "e")) {
+ ast_verb(2, "Spawn extension (%s, %s, %d) exited ERROR while already on 'e' exten on '%s'\n", c->context, c->exten, c->priority, c->name);
+ error = 1;
+ } else {
+ pbx_builtin_raise_exception(c, "ERROR");
+ continue;
+ }
+ }
+
+ if (c->_softhangup == AST_SOFTHANGUP_ASYNCGOTO) {
+ c->_softhangup = 0;
+ } else if (c->_softhangup == AST_SOFTHANGUP_TIMEOUT) {
+ /* atimeout, nothing bad */
+ } else {
+ if (c->cdr)
+ ast_cdr_update(c);
+ error = 1;
+ break;
+ }
+ }
+ }
+ if (error)
+ break;
+
+ /*!\note
+ * We get here on a failure of some kind: non-existing extension or
+ * hangup. We have options, here. We can either catch the failure
+ * and continue, or we can drop out entirely. */
+
+ if (!ast_exists_extension(c, c->context, c->exten, 1, c->cid.cid_num)) {
+ /*!\note
+ * If there is no match at priority 1, it is not a valid extension anymore.
+ * Try to continue at "i" (for invalid) or "e" (for exception) or exit if
+ * neither exist.
+ */
+ if (ast_exists_extension(c, c->context, "i", 1, c->cid.cid_num)) {
+ ast_verb(3, "Sent into invalid extension '%s' in context '%s' on %s\n", c->exten, c->context, c->name);
+ pbx_builtin_setvar_helper(c, "INVALID_EXTEN", c->exten);
+ set_ext_pri(c, "i", 1);
+ } else if (ast_exists_extension(c, c->context, "e", 1, c->cid.cid_num)) {
+ pbx_builtin_raise_exception(c, "INVALID");
+ } else {
+ ast_log(LOG_WARNING, "Channel '%s' sent into invalid extension '%s' in context '%s', but no invalid handler\n",
+ c->name, c->exten, c->context);
+ error = 1; /* we know what to do with it */
+ break;
+ }
+ } else if (c->_softhangup == AST_SOFTHANGUP_TIMEOUT) {
+ /* If we get this far with AST_SOFTHANGUP_TIMEOUT, then we know that the "T" extension is next. */
+ c->_softhangup = 0;
+ } else { /* keypress received, get more digits for a full extension */
+ int waittime = 0;
+ if (digit)
+ waittime = c->pbx->dtimeout;
+ else if (!autofallthrough)
+ waittime = c->pbx->rtimeout;
+ if (!waittime) {
+ const char *status = pbx_builtin_getvar_helper(c, "DIALSTATUS");
+ if (!status)
+ status = "UNKNOWN";
+ ast_verb(3, "Auto fallthrough, channel '%s' status is '%s'\n", c->name, status);
+ if (!strcasecmp(status, "CONGESTION"))
+ res = pbx_builtin_congestion(c, "10");
+ else if (!strcasecmp(status, "CHANUNAVAIL"))
+ res = pbx_builtin_congestion(c, "10");
+ else if (!strcasecmp(status, "BUSY"))
+ res = pbx_builtin_busy(c, "10");
+ error = 1; /* XXX disable message */
+ break; /* exit from the 'for' loop */
+ }
+
+ if (collect_digits(c, waittime, dst_exten, sizeof(dst_exten), pos))
+ break;
+ if (ast_exists_extension(c, c->context, dst_exten, 1, c->cid.cid_num)) /* Prepare the next cycle */
+ set_ext_pri(c, dst_exten, 1);
+ else {
+ /* No such extension */
+ if (!ast_strlen_zero(dst_exten)) {
+ /* An invalid extension */
+ if (ast_exists_extension(c, c->context, "i", 1, c->cid.cid_num)) {
+ ast_verb(3, "Invalid extension '%s' in context '%s' on %s\n", dst_exten, c->context, c->name);
+ pbx_builtin_setvar_helper(c, "INVALID_EXTEN", dst_exten);
+ set_ext_pri(c, "i", 1);
+ } else if (ast_exists_extension(c, c->context, "e", 1, c->cid.cid_num)) {
+ pbx_builtin_raise_exception(c, "INVALID");
+ } else {
+ ast_log(LOG_WARNING, "Invalid extension '%s', but no rule 'i' in context '%s'\n", dst_exten, c->context);
+ found = 1; /* XXX disable message */
+ break;
+ }
+ } else {
+ /* A simple timeout */
+ if (ast_exists_extension(c, c->context, "t", 1, c->cid.cid_num)) {
+ ast_verb(3, "Timeout on %s\n", c->name);
+ set_ext_pri(c, "t", 1);
+ } else if (ast_exists_extension(c, c->context, "e", 1, c->cid.cid_num)) {
+ pbx_builtin_raise_exception(c, "RESPONSETIMEOUT");
+ } else {
+ ast_log(LOG_WARNING, "Timeout, but no rule 't' in context '%s'\n", c->context);
+ found = 1; /* XXX disable message */
+ break;
+ }
+ }
+ }
+ if (c->cdr) {
+ ast_verb(2, "CDR updated on %s\n",c->name);
+ ast_cdr_update(c);
+ }
+ }
+ }
+ if (!found && !error)
+ ast_log(LOG_WARNING, "Don't know what to do with '%s'\n", c->name);
+ if (res != AST_PBX_KEEPALIVE)
+ ast_softhangup(c, c->hangupcause ? c->hangupcause : AST_CAUSE_NORMAL_CLEARING);
+ if ((res != AST_PBX_KEEPALIVE) && ast_exists_extension(c, c->context, "h", 1, c->cid.cid_num)) {
+ if (c->cdr && ast_opt_end_cdr_before_h_exten)
+ ast_cdr_end(c->cdr);
+ set_ext_pri(c, "h", 1);
+ while ((res = ast_spawn_extension(c, c->context, c->exten, c->priority, c->cid.cid_num, &found, 1)) == 0) {
+ c->priority++;
+ }
+ if (found && res) {
+ /* Something bad happened, or a hangup has been requested. */
+ ast_debug(1, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
+ ast_verb(2, "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
+ }
+ }
+ ast_set2_flag(c, autoloopflag, AST_FLAG_IN_AUTOLOOP);
+
+ pbx_destroy(c->pbx);
+ c->pbx = NULL;
+ if (res != AST_PBX_KEEPALIVE)
+ ast_hangup(c);
+ return 0;
+}
+
+/*!
+ * \brief Increase call count for channel
+ * \retval 0 on success
+ * \retval non-zero if a configured limit (maxcalls, maxload, minmemfree) was reached
+*/
+static int increase_call_count(const struct ast_channel *c)
+{
+ int failed = 0;
+ double curloadavg;
+#if defined(HAVE_SYSINFO)
+ long curfreemem;
+ struct sysinfo sys_info;
+#endif
+
+ ast_mutex_lock(&maxcalllock);
+ if (option_maxcalls) {
+ if (countcalls >= option_maxcalls) {
+ ast_log(LOG_NOTICE, "Maximum call limit of %d calls exceeded by '%s'!\n", option_maxcalls, c->name);
+ failed = -1;
+ }
+ }
+ if (option_maxload) {
+ getloadavg(&curloadavg, 1);
+ if (curloadavg >= option_maxload) {
+ ast_log(LOG_NOTICE, "Maximum loadavg limit of %f load exceeded by '%s' (currently %f)!\n", option_maxload, c->name, curloadavg);
+ failed = -1;
+ }
+ }
+#if defined(HAVE_SYSINFO)
+ if (option_minmemfree) {
+ if (!sysinfo(&sys_info)) {
+ /* make sure that the free system memory is above the configured low watermark
+ * convert the amount of freeram from mem_units to MB */
+ curfreemem = sys_info.freeram / sys_info.mem_unit;
+ curfreemem /= 1024*1024;
+ if (curfreemem < option_minmemfree) {
+ ast_log(LOG_WARNING, "Available system memory (~%ldMB) is below the configured low watermark (%ldMB)\n", curfreemem, option_minmemfree);
+ failed = -1;
+ }
+ }
+ }
+#endif
+
+ if (!failed) {
+ countcalls++;
+ totalcalls++;
+ }
+ ast_mutex_unlock(&maxcalllock);
+
+ return failed;
+}
+
+static void decrease_call_count(void)
+{
+ ast_mutex_lock(&maxcalllock);
+ if (countcalls > 0)
+ countcalls--;
+ ast_mutex_unlock(&maxcalllock);
+}
+
+static void destroy_exten(struct ast_exten *e)
+{
+ if (e->priority == PRIORITY_HINT)
+ ast_remove_hint(e);
+
+ if (e->peer_tree)
+ ast_hashtab_destroy(e->peer_tree,0);
+ if (e->peer_label_tree)
+ ast_hashtab_destroy(e->peer_label_tree, 0);
+ if (e->datad)
+ e->datad(e->data);
+ ast_free(e);
+}
+
+static void *pbx_thread(void *data)
+{
+ /* Oh joyeous kernel, we're a new thread, with nothing to do but
+ answer this channel and get it going.
+ */
+ /* NOTE:
+ The launcher of this function _MUST_ increment 'countcalls'
+ before invoking the function; it will be decremented when the
+ PBX has finished running on the channel
+ */
+ struct ast_channel *c = data;
+
+ __ast_pbx_run(c);
+ decrease_call_count();
+
+ pthread_exit(NULL);
+
+ return NULL;
+}
+
+enum ast_pbx_result ast_pbx_start(struct ast_channel *c)
+{
+ pthread_t t;
+
+ if (!c) {
+ ast_log(LOG_WARNING, "Asked to start thread on NULL channel?\n");
+ return AST_PBX_FAILED;
+ }
+
+ if (increase_call_count(c))
+ return AST_PBX_CALL_LIMIT;
+
+ /* Start a new thread, and get something handling this channel. */
+ if (ast_pthread_create_detached(&t, NULL, pbx_thread, c)) {
+ ast_log(LOG_WARNING, "Failed to create new channel thread\n");
+ return AST_PBX_FAILED;
+ }
+
+ return AST_PBX_SUCCESS;
+}
+
+enum ast_pbx_result ast_pbx_run(struct ast_channel *c)
+{
+ enum ast_pbx_result res = AST_PBX_SUCCESS;
+
+ if (increase_call_count(c))
+ return AST_PBX_CALL_LIMIT;
+
+ res = __ast_pbx_run(c);
+ decrease_call_count();
+
+ return res;
+}
+
+int ast_active_calls(void)
+{
+ return countcalls;
+}
+
+int ast_processed_calls(void)
+{
+ return totalcalls;
+}
+
+int pbx_set_autofallthrough(int newval)
+{
+ int oldval = autofallthrough;
+ autofallthrough = newval;
+ return oldval;
+}
+
+int pbx_set_extenpatternmatchnew(int newval)
+{
+ int oldval = extenpatternmatchnew;
+ extenpatternmatchnew = newval;
+ return oldval;
+}
+
+/*!
+ * \brief lookup for a context with a given name,
+ * \retval with conlock held if found.
+ * \retval NULL if not found.
+*/
+static struct ast_context *find_context_locked(const char *context)
+{
+ struct ast_context *c = NULL;
+ struct fake_context item;
+ strncpy(item.name, context, 256);
+ ast_rdlock_contexts();
+ c = ast_hashtab_lookup(contexts_tree,&item);
+
+#ifdef NOTNOW
+
+ while ( (c = ast_walk_contexts(c)) ) {
+ if (!strcmp(ast_get_context_name(c), context))
+ return c;
+ }
+#endif
+ if (!c)
+ ast_unlock_contexts();
+
+ return c;
+}
+
+/*!
+ * \brief Remove included contexts.
+ * This function locks contexts list by &conlist, search for the right context
+ * structure, leave context list locked and call ast_context_remove_include2
+ * which removes include, unlock contexts list and return ...
+*/
+int ast_context_remove_include(const char *context, const char *include, const char *registrar)
+{
+ int ret = -1;
+ struct ast_context *c = find_context_locked(context);
+
+ if (c) {
+ /* found, remove include from this context ... */
+ ret = ast_context_remove_include2(c, include, registrar);
+ ast_unlock_contexts();
+ }
+ return ret;
+}
+
+/*!
+ * \brief Locks context, remove included contexts, unlocks context.
+ * When we call this function, &conlock lock must be locked, because when
+ * we giving *con argument, some process can remove/change this context
+ * and after that there can be segfault.
+ *
+ * \retval 0 on success.
+ * \retval -1 on failure.
+ */
+int ast_context_remove_include2(struct ast_context *con, const char *include, const char *registrar)
+{
+ struct ast_include *i, *pi = NULL;
+ int ret = -1;
+
+ ast_wrlock_context(con);
+
+ /* find our include */
+ for (i = con->includes; i; pi = i, i = i->next) {
+ if (!strcmp(i->name, include) &&
+ (!registrar || !strcmp(i->registrar, registrar))) {
+ /* remove from list */
+ if (pi)
+ pi->next = i->next;
+ else
+ con->includes = i->next;
+ /* free include and return */
+ ast_free(i);
+ ret = 0;
+ break;
+ }
+ }
+
+ ast_unlock_context(con);
+
+ return ret;
+}
+
+/*!
+ * \note This function locks contexts list by &conlist, search for the rigt context
+ * structure, leave context list locked and call ast_context_remove_switch2
+ * which removes switch, unlock contexts list and return ...
+ */
+int ast_context_remove_switch(const char *context, const char *sw, const char *data, const char *registrar)
+{
+ int ret = -1; /* default error return */
+ struct ast_context *c = find_context_locked(context);
+
+ if (c) {
+ /* remove switch from this context ... */
+ ret = ast_context_remove_switch2(c, sw, data, registrar);
+ ast_unlock_contexts();
+ }
+ return ret;
+}
+
+/*!
+ * \brief This function locks given context, removes switch, unlock context and
+ * return.
+ * \note When we call this function, &conlock lock must be locked, because when
+ * we giving *con argument, some process can remove/change this context
+ * and after that there can be segfault.
+ *
+ */
+int ast_context_remove_switch2(struct ast_context *con, const char *sw, const char *data, const char *registrar)
+{
+ struct ast_sw *i;
+ int ret = -1;
+
+ ast_wrlock_context(con);
+
+ /* walk switches */
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&con->alts, i, list) {
+ if (!strcmp(i->name, sw) && !strcmp(i->data, data) &&
+ (!registrar || !strcmp(i->registrar, registrar))) {
+ /* found, remove from list */
+ AST_LIST_REMOVE_CURRENT(list);
+ ast_free(i); /* free switch and return */
+ ret = 0;
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+
+ ast_unlock_context(con);
+
+ return ret;
+}
+
+/*
+ * \note This functions lock contexts list, search for the right context,
+ * call ast_context_remove_extension2, unlock contexts list and return.
+ * In this function we are using
+ */
+int ast_context_remove_extension(const char *context, const char *extension, int priority, const char *registrar)
+{
+ int ret = -1; /* default error return */
+ struct ast_context *c = find_context_locked(context);
+
+ if (c) { /* ... remove extension ... */
+ ret = ast_context_remove_extension2(c, extension, priority, registrar);
+ ast_unlock_contexts();
+ }
+ return ret;
+}
+
+/*!
+ * \brief This functionc locks given context, search for the right extension and
+ * fires out all peer in this extensions with given priority. If priority
+ * is set to 0, all peers are removed. After that, unlock context and
+ * return.
+ * \note When do you want to call this function, make sure that &conlock is locked,
+ * because some process can handle with your *con context before you lock
+ * it.
+ *
+ */
+int ast_context_remove_extension2(struct ast_context *con, const char *extension, int priority, const char *registrar)
+{
+ struct ast_exten *exten, *prev_exten = NULL;
+ struct ast_exten *peer;
+ struct ast_exten ex, *exten2, *exten3;
+ char dummy_name[1024];
+
+ ast_wrlock_context(con);
+
+ /* Handle this is in the new world */
+
+#ifdef NEED_DEBUG
+ ast_log(LOG_NOTICE,"Removing %s/%s/%d from trees, registrar=%s\n", con->name, extension, priority, registrar);
+#endif
+ /* find this particular extension */
+ ex.exten = dummy_name;
+ ex.matchcid = 0;
+ ast_copy_string(dummy_name,extension, sizeof(dummy_name));
+ exten = ast_hashtab_lookup(con->root_tree, &ex);
+ if (exten) {
+ if (priority == 0)
+ {
+ exten2 = ast_hashtab_remove_this_object(con->root_tree, exten);
+ if (!exten2)
+ ast_log(LOG_ERROR,"Trying to delete the exten %s from context %s, but could not remove from the root_tree\n", extension, con->name);
+ if (con->pattern_tree) {
+
+ struct match_char *x = add_exten_to_pattern_tree(con, exten, 1);
+
+ if (x->exten) { /* this test for safety purposes */
+ x->deleted = 1; /* with this marked as deleted, it will never show up in the scoreboard, and therefore never be found */
+ x->exten = 0; /* get rid of what will become a bad pointer */
+ } else {
+ ast_log(LOG_WARNING,"Trying to delete an exten from a context, but the pattern tree node returned isn't a full extension\n");
+ }
+ }
+ } else {
+ ex.priority = priority;
+ exten2 = ast_hashtab_lookup(exten->peer_tree, &ex);
+ if (exten2) {
+
+ if (exten2->label) { /* if this exten has a label, remove that, too */
+ exten3 = ast_hashtab_remove_this_object(exten->peer_label_tree,exten2);
+ if (!exten3)
+ ast_log(LOG_ERROR,"Did not remove this priority label (%d/%s) from the peer_label_tree of context %s, extension %s!\n", priority, exten2->label, con->name, exten2->exten);
+ }
+
+ exten3 = ast_hashtab_remove_this_object(exten->peer_tree, exten2);
+ if (!exten3)
+ ast_log(LOG_ERROR,"Did not remove this priority (%d) from the peer_tree of context %s, extension %s!\n", priority, con->name, exten2->exten);
+ if (ast_hashtab_size(exten->peer_tree) == 0) {
+ /* well, if the last priority of an exten is to be removed,
+ then, the extension is removed, too! */
+ exten3 = ast_hashtab_remove_this_object(con->root_tree, exten);
+ if (!exten3)
+ ast_log(LOG_ERROR,"Did not remove this exten (%s) from the context root_tree (%s) (priority %d)\n", exten->exten, con->name, priority);
+ if (con->pattern_tree) {
+ struct match_char *x = add_exten_to_pattern_tree(con, exten, 1);
+ if (x->exten) { /* this test for safety purposes */
+ x->deleted = 1; /* with this marked as deleted, it will never show up in the scoreboard, and therefore never be found */
+ x->exten = 0; /* get rid of what will become a bad pointer */
+ }
+ }
+ }
+ } else {
+ ast_log(LOG_ERROR,"Could not find priority %d of exten %s in context %s!\n",
+ priority, exten->exten, con->name);
+ }
+ }
+ } else {
+ /* hmmm? this exten is not in this pattern tree? */
+ ast_log(LOG_WARNING,"Cannot find extension %s in root_tree in context %s\n",
+ extension, con->name);
+ }
+#ifdef NEED_DEBUG
+ if (con->pattern_tree) {
+ ast_log(LOG_NOTICE,"match char tree after exten removal:\n");
+ log_match_char_tree(con->pattern_tree, " ");
+ }
+#endif
+
+
+ /* scan the extension list to find matching extension-registrar */
+ for (exten = con->root; exten; prev_exten = exten, exten = exten->next) {
+ if (!strcmp(exten->exten, extension) &&
+ (!registrar || !strcmp(exten->registrar, registrar)))
+ break;
+ }
+ if (!exten) {
+ /* we can't find right extension */
+ ast_unlock_context(con);
+ return -1;
+ }
+
+ /* should we free all peers in this extension? (priority == 0)? */
+ if (priority == 0) {
+ /* remove this extension from context list */
+ if (prev_exten)
+ prev_exten->next = exten->next;
+ else
+ con->root = exten->next;
+
+ /* fire out all peers */
+ while ( (peer = exten) ) {
+ exten = peer->peer; /* prepare for next entry */
+ destroy_exten(peer);
+ }
+ } else {
+ /* scan the priority list to remove extension with exten->priority == priority */
+ struct ast_exten *previous_peer = NULL;
+
+ for (peer = exten; peer; previous_peer = peer, peer = peer->peer) {
+ if (peer->priority == priority &&
+ (!registrar || !strcmp(peer->registrar, registrar) ))
+ break; /* found our priority */
+ }
+ if (!peer) { /* not found */
+ ast_unlock_context(con);
+ return -1;
+ }
+ /* we are first priority extension? */
+ if (!previous_peer) {
+ /*
+ * We are first in the priority chain, so must update the extension chain.
+ * The next node is either the next priority or the next extension
+ */
+ struct ast_exten *next_node = peer->peer ? peer->peer : peer->next;
+ if (next_node && next_node == peer->peer) {
+ next_node->peer_tree = exten->peer_tree; /* move the priority hash tabs over */
+ exten->peer_tree = 0;
+ next_node->peer_label_tree = exten->peer_label_tree;
+ exten->peer_label_tree = 0;
+ }
+ if (!prev_exten) { /* change the root... */
+ con->root = next_node;
+ } else {
+ prev_exten->next = next_node; /* unlink */
+ }
+ if (peer->peer) { /* XXX update the new head of the pri list */
+ peer->peer->next = peer->next;
+ }
+
+ } else { /* easy, we are not first priority in extension */
+ previous_peer->peer = peer->peer;
+ }
+
+ /* now, free whole priority extension */
+ destroy_exten(peer);
+ /* XXX should we return -1 ? */
+ }
+ ast_unlock_context(con);
+ return 0;
+}
+
+
+/*!
+ * \note This function locks contexts list by &conlist, searches for the right context
+ * structure, and locks the macrolock mutex in that context.
+ * macrolock is used to limit a macro to be executed by one call at a time.
+ */
+int ast_context_lockmacro(const char *context)
+{
+ struct ast_context *c = NULL;
+ int ret = -1;
+ struct fake_context item;
+
+ ast_rdlock_contexts();
+
+ strncpy(item.name,context,256);
+ c = ast_hashtab_lookup(contexts_tree,&item);
+ if (c)
+ ret = 0;
+
+
+#ifdef NOTNOW
+
+ while ((c = ast_walk_contexts(c))) {
+ if (!strcmp(ast_get_context_name(c), context)) {
+ ret = 0;
+ break;
+ }
+ }
+
+#endif
+ ast_unlock_contexts();
+
+ /* if we found context, lock macrolock */
+ if (ret == 0)
+ ret = ast_mutex_lock(&c->macrolock);
+
+ return ret;
+}
+
+/*!
+ * \note This function locks contexts list by &conlist, searches for the right context
+ * structure, and unlocks the macrolock mutex in that context.
+ * macrolock is used to limit a macro to be executed by one call at a time.
+ */
+int ast_context_unlockmacro(const char *context)
+{
+ struct ast_context *c = NULL;
+ int ret = -1;
+ struct fake_context item;
+
+ ast_rdlock_contexts();
+
+ strncpy(item.name, context, 256);
+ c = ast_hashtab_lookup(contexts_tree,&item);
+ if (c)
+ ret = 0;
+#ifdef NOTNOW
+
+ while ((c = ast_walk_contexts(c))) {
+ if (!strcmp(ast_get_context_name(c), context)) {
+ ret = 0;
+ break;
+ }
+ }
+
+#endif
+ ast_unlock_contexts();
+
+ /* if we found context, unlock macrolock */
+ if (ret == 0)
+ ret = ast_mutex_unlock(&c->macrolock);
+
+ return ret;
+}
+
+/*! \brief Dynamically register a new dial plan application */
+int ast_register_application2(const char *app, int (*execute)(struct ast_channel *, void *), const char *synopsis, const char *description, void *mod)
+{
+ struct ast_app *tmp, *cur = NULL;
+ char tmps[80];
+ int length, res;
+
+ AST_RWLIST_WRLOCK(&apps);
+ AST_RWLIST_TRAVERSE(&apps, tmp, list) {
+ if (!(res = strcasecmp(app, tmp->name))) {
+ ast_log(LOG_WARNING, "Already have an application '%s'\n", app);
+ AST_RWLIST_UNLOCK(&apps);
+ return -1;
+ } else if (res < 0)
+ break;
+ }
+
+ length = sizeof(*tmp) + strlen(app) + 1;
+
+ if (!(tmp = ast_calloc(1, length))) {
+ AST_RWLIST_UNLOCK(&apps);
+ return -1;
+ }
+
+ strcpy(tmp->name, app);
+ tmp->execute = execute;
+ tmp->synopsis = synopsis;
+ tmp->description = description;
+ tmp->module = mod;
+
+ /* Store in alphabetical order */
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&apps, cur, list) {
+ if (strcasecmp(tmp->name, cur->name) < 0) {
+ AST_RWLIST_INSERT_BEFORE_CURRENT(tmp, list);
+ break;
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+ if (!cur)
+ AST_RWLIST_INSERT_TAIL(&apps, tmp, list);
+
+ ast_verb(2, "Registered application '%s'\n", term_color(tmps, tmp->name, COLOR_BRCYAN, 0, sizeof(tmps)));
+
+ AST_RWLIST_UNLOCK(&apps);
+
+ return 0;
+}
+
+/*
+ * Append to the list. We don't have a tail pointer because we need
+ * to scan the list anyways to check for duplicates during insertion.
+ */
+int ast_register_switch(struct ast_switch *sw)
+{
+ struct ast_switch *tmp;
+
+ AST_RWLIST_WRLOCK(&switches);
+ AST_RWLIST_TRAVERSE(&switches, tmp, list) {
+ if (!strcasecmp(tmp->name, sw->name)) {
+ AST_RWLIST_UNLOCK(&switches);
+ ast_log(LOG_WARNING, "Switch '%s' already found\n", sw->name);
+ return -1;
+ }
+ }
+ AST_RWLIST_INSERT_TAIL(&switches, sw, list);
+ AST_RWLIST_UNLOCK(&switches);
+
+ return 0;
+}
+
+void ast_unregister_switch(struct ast_switch *sw)
+{
+ AST_RWLIST_WRLOCK(&switches);
+ AST_RWLIST_REMOVE(&switches, sw, list);
+ AST_RWLIST_UNLOCK(&switches);
+}
+
+/*
+ * Help for CLI commands ...
+ */
+
+/*
+ * \brief 'show application' CLI command implementation function...
+ */
+static char *handle_show_application(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_app *aa;
+ int app, no_registered_app = 1;
+ char *ret = NULL;
+ int which = 0;
+ int wordlen;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show application";
+ e->usage =
+ "Usage: core show application <application> [<application> [<application> [...]]]\n"
+ " Describes a particular application.\n";
+ return NULL;
+ case CLI_GENERATE:
+ /*
+ * There is a possibility to show informations about more than one
+ * application at one time. You can type 'show application Dial Echo' and
+ * you will see informations about these two applications ...
+ */
+ wordlen = strlen(a->word);
+ /* return the n-th [partial] matching entry */
+ AST_RWLIST_RDLOCK(&apps);
+ AST_RWLIST_TRAVERSE(&apps, aa, list) {
+ if (!strncasecmp(a->word, aa->name, wordlen) && ++which > a->n) {
+ ret = ast_strdup(aa->name);
+ break;
+ }
+ }
+ AST_RWLIST_UNLOCK(&apps);
+
+ return ret;
+ }
+
+ if (a->argc < 4)
+ return CLI_SHOWUSAGE;
+
+ /* ... go through all applications ... */
+ AST_RWLIST_RDLOCK(&apps);
+ AST_RWLIST_TRAVERSE(&apps, aa, list) {
+ /* ... compare this application name with all arguments given
+ * to 'show application' command ... */
+ for (app = 3; app < a->argc; app++) {
+ if (!strcasecmp(aa->name, a->argv[app])) {
+ /* Maximum number of characters added by terminal coloring is 22 */
+ char infotitle[64 + AST_MAX_APP + 22], syntitle[40], destitle[40];
+ char info[64 + AST_MAX_APP], *synopsis = NULL, *description = NULL;
+ int synopsis_size, description_size;
+
+ no_registered_app = 0;
+
+ if (aa->synopsis)
+ synopsis_size = strlen(aa->synopsis) + 23;
+ else
+ synopsis_size = strlen("Not available") + 23;
+ synopsis = alloca(synopsis_size);
+
+ if (aa->description)
+ description_size = strlen(aa->description) + 23;
+ else
+ description_size = strlen("Not available") + 23;
+ description = alloca(description_size);
+
+ if (synopsis && description) {
+ snprintf(info, 64 + AST_MAX_APP, "\n -= Info about application '%s' =- \n\n", aa->name);
+ term_color(infotitle, info, COLOR_MAGENTA, 0, 64 + AST_MAX_APP + 22);
+ term_color(syntitle, "[Synopsis]\n", COLOR_MAGENTA, 0, 40);
+ term_color(destitle, "[Description]\n", COLOR_MAGENTA, 0, 40);
+ term_color(synopsis,
+ aa->synopsis ? aa->synopsis : "Not available",
+ COLOR_CYAN, 0, synopsis_size);
+ term_color(description,
+ aa->description ? aa->description : "Not available",
+ COLOR_CYAN, 0, description_size);
+
+ ast_cli(a->fd,"%s%s%s\n\n%s%s\n", infotitle, syntitle, synopsis, destitle, description);
+ } else {
+ /* ... one of our applications, show info ...*/
+ ast_cli(a->fd,"\n -= Info about application '%s' =- \n\n"
+ "[Synopsis]\n %s\n\n"
+ "[Description]\n%s\n",
+ aa->name,
+ aa->synopsis ? aa->synopsis : "Not available",
+ aa->description ? aa->description : "Not available");
+ }
+ }
+ }
+ }
+ AST_RWLIST_UNLOCK(&apps);
+
+ /* we found at least one app? no? */
+ if (no_registered_app) {
+ ast_cli(a->fd, "Your application(s) is (are) not registered\n");
+ return CLI_FAILURE;
+ }
+
+ return CLI_SUCCESS;
+}
+
+/*! \brief handle_show_hints: CLI support for listing registered dial plan hints */
+static char *handle_show_hints(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_hint *hint;
+ int num = 0;
+ int watchers;
+ struct ast_state_cb *watcher;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show hints";
+ e->usage =
+ "Usage: core show hints\n"
+ " List registered hints\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ AST_RWLIST_RDLOCK(&hints);
+ if (AST_RWLIST_EMPTY(&hints)) {
+ ast_cli(a->fd, "There are no registered dialplan hints\n");
+ AST_RWLIST_UNLOCK(&hints);
+ return CLI_SUCCESS;
+ }
+ /* ... we have hints ... */
+ ast_cli(a->fd, "\n -= Registered Asterisk Dial Plan Hints =-\n");
+ AST_RWLIST_TRAVERSE(&hints, hint, list) {
+ watchers = 0;
+ for (watcher = hint->callbacks; watcher; watcher = watcher->next)
+ watchers++;
+ ast_cli(a->fd, " %20s@%-20.20s: %-20.20s State:%-15.15s Watchers %2d\n",
+ ast_get_extension_name(hint->exten),
+ ast_get_context_name(ast_get_extension_context(hint->exten)),
+ ast_get_extension_app(hint->exten),
+ ast_extension_state2str(hint->laststate), watchers);
+ num++;
+ }
+ ast_cli(a->fd, "----------------\n");
+ ast_cli(a->fd, "- %d hints registered\n", num);
+ AST_RWLIST_UNLOCK(&hints);
+ return CLI_SUCCESS;
+}
+
+/*! \brief autocomplete for CLI command 'core show hint' */
+static char *complete_core_show_hint(const char *line, const char *word, int pos, int state)
+{
+ struct ast_hint *hint;
+ char *ret = NULL;
+ int which = 0;
+ int wordlen;
+
+ if (pos != 3)
+ return NULL;
+
+ wordlen = strlen(word);
+
+ AST_RWLIST_RDLOCK(&hints);
+ /* walk through all hints */
+ AST_RWLIST_TRAVERSE(&hints, hint, list) {
+ if (!strncasecmp(word, ast_get_extension_name(hint->exten), wordlen) && ++which > state) {
+ ret = ast_strdup(ast_get_extension_name(hint->exten));
+ break;
+ }
+ }
+ AST_RWLIST_UNLOCK(&hints);
+
+ return ret;
+}
+
+/*! \brief handle_show_hint: CLI support for listing registered dial plan hint */
+static char *handle_show_hint(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_hint *hint;
+ int watchers;
+ int num = 0, extenlen;
+ struct ast_state_cb *watcher;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show hint";
+ e->usage =
+ "Usage: core show hint <exten>\n"
+ " List registered hint\n";
+ return NULL;
+ case CLI_GENERATE:
+ return complete_core_show_hint(a->line, a->word, a->pos, a->n);
+ }
+
+ if (a->argc < 4)
+ return CLI_SHOWUSAGE;
+
+ AST_RWLIST_RDLOCK(&hints);
+ if (AST_RWLIST_EMPTY(&hints)) {
+ ast_cli(a->fd, "There are no registered dialplan hints\n");
+ AST_RWLIST_UNLOCK(&hints);
+ return CLI_SUCCESS;
+ }
+ extenlen = strlen(a->argv[3]);
+ AST_RWLIST_TRAVERSE(&hints, hint, list) {
+ if (!strncasecmp(ast_get_extension_name(hint->exten), a->argv[3], extenlen)) {
+ watchers = 0;
+ for (watcher = hint->callbacks; watcher; watcher = watcher->next)
+ watchers++;
+ ast_cli(a->fd, " %20s@%-20.20s: %-20.20s State:%-15.15s Watchers %2d\n",
+ ast_get_extension_name(hint->exten),
+ ast_get_context_name(ast_get_extension_context(hint->exten)),
+ ast_get_extension_app(hint->exten),
+ ast_extension_state2str(hint->laststate), watchers);
+ num++;
+ }
+ }
+ AST_RWLIST_UNLOCK(&hints);
+ if (!num)
+ ast_cli(a->fd, "No hints matching extension %s\n", a->argv[3]);
+ else
+ ast_cli(a->fd, "%d hint%s matching extension %s\n", num, (num!=1 ? "s":""), a->argv[3]);
+ return CLI_SUCCESS;
+}
+
+
+/*! \brief handle_show_switches: CLI support for listing registered dial plan switches */
+static char *handle_show_switches(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_switch *sw;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show switches";
+ e->usage =
+ "Usage: core show switches\n"
+ " List registered switches\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ AST_RWLIST_RDLOCK(&switches);
+
+ if (AST_RWLIST_EMPTY(&switches)) {
+ AST_RWLIST_UNLOCK(&switches);
+ ast_cli(a->fd, "There are no registered alternative switches\n");
+ return CLI_SUCCESS;
+ }
+
+ ast_cli(a->fd, "\n -= Registered Asterisk Alternative Switches =-\n");
+ AST_RWLIST_TRAVERSE(&switches, sw, list)
+ ast_cli(a->fd, "%s: %s\n", sw->name, sw->description);
+
+ AST_RWLIST_UNLOCK(&switches);
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_show_applications(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_app *aa;
+ int like = 0, describing = 0;
+ int total_match = 0; /* Number of matches in like clause */
+ int total_apps = 0; /* Number of apps registered */
+ static char* choices[] = { "like", "describing", NULL };
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show applications [like|describing]";
+ e->usage =
+ "Usage: core show applications [{like|describing} <text>]\n"
+ " List applications which are currently available.\n"
+ " If 'like', <text> will be a substring of the app name\n"
+ " If 'describing', <text> will be a substring of the description\n";
+ return NULL;
+ case CLI_GENERATE:
+ return (a->pos != 3) ? NULL : ast_cli_complete(a->word, choices, a->n);
+ }
+
+ AST_RWLIST_RDLOCK(&apps);
+
+ if (AST_RWLIST_EMPTY(&apps)) {
+ ast_cli(a->fd, "There are no registered applications\n");
+ AST_RWLIST_UNLOCK(&apps);
+ return CLI_SUCCESS;
+ }
+
+ /* core list applications like <keyword> */
+ if ((a->argc == 5) && (!strcmp(a->argv[3], "like"))) {
+ like = 1;
+ } else if ((a->argc > 4) && (!strcmp(a->argv[3], "describing"))) {
+ describing = 1;
+ }
+
+ /* core list applications describing <keyword1> [<keyword2>] [...] */
+ if ((!like) && (!describing)) {
+ ast_cli(a->fd, " -= Registered Asterisk Applications =-\n");
+ } else {
+ ast_cli(a->fd, " -= Matching Asterisk Applications =-\n");
+ }
+
+ AST_RWLIST_TRAVERSE(&apps, aa, list) {
+ int printapp = 0;
+ total_apps++;
+ if (like) {
+ if (strcasestr(aa->name, a->argv[4])) {
+ printapp = 1;
+ total_match++;
+ }
+ } else if (describing) {
+ if (aa->description) {
+ /* Match all words on command line */
+ int i;
+ printapp = 1;
+ for (i = 4; i < a->argc; i++) {
+ if (!strcasestr(aa->description, a->argv[i])) {
+ printapp = 0;
+ } else {
+ total_match++;
+ }
+ }
+ }
+ } else {
+ printapp = 1;
+ }
+
+ if (printapp) {
+ ast_cli(a->fd," %20s: %s\n", aa->name, aa->synopsis ? aa->synopsis : "<Synopsis not available>");
+ }
+ }
+ if ((!like) && (!describing)) {
+ ast_cli(a->fd, " -= %d Applications Registered =-\n",total_apps);
+ } else {
+ ast_cli(a->fd, " -= %d Applications Matching =-\n",total_match);
+ }
+
+ AST_RWLIST_UNLOCK(&apps);
+
+ return CLI_SUCCESS;
+}
+
+/*
+ * 'show dialplan' CLI command implementation functions ...
+ */
+static char *complete_show_dialplan_context(const char *line, const char *word, int pos,
+ int state)
+{
+ struct ast_context *c = NULL;
+ char *ret = NULL;
+ int which = 0;
+ int wordlen;
+
+ /* we are do completion of [exten@]context on second position only */
+ if (pos != 2)
+ return NULL;
+
+ ast_rdlock_contexts();
+
+ wordlen = strlen(word);
+
+ /* walk through all contexts and return the n-th match */
+ while ( (c = ast_walk_contexts(c)) ) {
+ if (!strncasecmp(word, ast_get_context_name(c), wordlen) && ++which > state) {
+ ret = ast_strdup(ast_get_context_name(c));
+ break;
+ }
+ }
+
+ ast_unlock_contexts();
+
+ return ret;
+}
+
+/*! \brief Counters for the show dialplan manager command */
+struct dialplan_counters {
+ int total_items;
+ int total_context;
+ int total_exten;
+ int total_prio;
+ int context_existence;
+ int extension_existence;
+};
+
+/*! \brief helper function to print an extension */
+static void print_ext(struct ast_exten *e, char * buf, int buflen)
+{
+ int prio = ast_get_extension_priority(e);
+ if (prio == PRIORITY_HINT) {
+ snprintf(buf, buflen, "hint: %s",
+ ast_get_extension_app(e));
+ } else {
+ snprintf(buf, buflen, "%d. %s(%s)",
+ prio, ast_get_extension_app(e),
+ (!ast_strlen_zero(ast_get_extension_app_data(e)) ? (char *)ast_get_extension_app_data(e) : ""));
+ }
+}
+
+/* XXX not verified */
+static int show_dialplan_helper(int fd, const char *context, const char *exten, struct dialplan_counters *dpc, struct ast_include *rinclude, int includecount, const char *includes[])
+{
+ struct ast_context *c = NULL;
+ int res = 0, old_total_exten = dpc->total_exten;
+
+ ast_rdlock_contexts();
+
+ /* walk all contexts ... */
+ while ( (c = ast_walk_contexts(c)) ) {
+ struct ast_exten *e;
+ struct ast_include *i;
+ struct ast_ignorepat *ip;
+ char buf[256], buf2[256];
+ int context_info_printed = 0;
+
+ if (context && strcmp(ast_get_context_name(c), context))
+ continue; /* skip this one, name doesn't match */
+
+ dpc->context_existence = 1;
+
+ ast_rdlock_context(c);
+
+ /* are we looking for exten too? if yes, we print context
+ * only if we find our extension.
+ * Otherwise print context even if empty ?
+ * XXX i am not sure how the rinclude is handled.
+ * I think it ought to go inside.
+ */
+ if (!exten) {
+ dpc->total_context++;
+ ast_cli(fd, "[ Context '%s' created by '%s' ]\n",
+ ast_get_context_name(c), ast_get_context_registrar(c));
+ context_info_printed = 1;
+ }
+
+ /* walk extensions ... */
+ e = NULL;
+ while ( (e = ast_walk_context_extensions(c, e)) ) {
+ struct ast_exten *p;
+
+ if (exten && !ast_extension_match(ast_get_extension_name(e), exten))
+ continue; /* skip, extension match failed */
+
+ dpc->extension_existence = 1;
+
+ /* may we print context info? */
+ if (!context_info_printed) {
+ dpc->total_context++;
+ if (rinclude) { /* TODO Print more info about rinclude */
+ ast_cli(fd, "[ Included context '%s' created by '%s' ]\n",
+ ast_get_context_name(c), ast_get_context_registrar(c));
+ } else {
+ ast_cli(fd, "[ Context '%s' created by '%s' ]\n",
+ ast_get_context_name(c), ast_get_context_registrar(c));
+ }
+ context_info_printed = 1;
+ }
+ dpc->total_prio++;
+
+ /* write extension name and first peer */
+ if (e->matchcid)
+ snprintf(buf, sizeof(buf), "'%s' (CID match '%s') => ", ast_get_extension_name(e), e->cidmatch);
+ else
+ snprintf(buf, sizeof(buf), "'%s' =>", ast_get_extension_name(e));
+
+ print_ext(e, buf2, sizeof(buf2));
+
+ ast_cli(fd, " %-17s %-45s [%s]\n", buf, buf2,
+ ast_get_extension_registrar(e));
+
+ dpc->total_exten++;
+ /* walk next extension peers */
+ p = e; /* skip the first one, we already got it */
+ while ( (p = ast_walk_extension_priorities(e, p)) ) {
+ const char *el = ast_get_extension_label(p);
+ dpc->total_prio++;
+ if (el)
+ snprintf(buf, sizeof(buf), " [%s]", el);
+ else
+ buf[0] = '\0';
+ print_ext(p, buf2, sizeof(buf2));
+
+ ast_cli(fd," %-17s %-45s [%s]\n", buf, buf2,
+ ast_get_extension_registrar(p));
+ }
+ }
+
+ /* walk included and write info ... */
+ i = NULL;
+ while ( (i = ast_walk_context_includes(c, i)) ) {
+ snprintf(buf, sizeof(buf), "'%s'", ast_get_include_name(i));
+ if (exten) {
+ /* Check all includes for the requested extension */
+ if (includecount >= AST_PBX_MAX_STACK) {
+ ast_log(LOG_NOTICE, "Maximum include depth exceeded!\n");
+ } else {
+ int dupe=0;
+ int x;
+ for (x=0;x<includecount;x++) {
+ if (!strcasecmp(includes[x], ast_get_include_name(i))) {
+ dupe++;
+ break;
+ }
+ }
+ if (!dupe) {
+ includes[includecount] = ast_get_include_name(i);
+ show_dialplan_helper(fd, ast_get_include_name(i), exten, dpc, i, includecount + 1, includes);
+ } else {
+ ast_log(LOG_WARNING, "Avoiding circular include of %s within %s\n", ast_get_include_name(i), context);
+ }
+ }
+ } else {
+ ast_cli(fd, " Include => %-45s [%s]\n",
+ buf, ast_get_include_registrar(i));
+ }
+ }
+
+ /* walk ignore patterns and write info ... */
+ ip = NULL;
+ while ( (ip = ast_walk_context_ignorepats(c, ip)) ) {
+ const char *ipname = ast_get_ignorepat_name(ip);
+ char ignorepat[AST_MAX_EXTENSION];
+ snprintf(buf, sizeof(buf), "'%s'", ipname);
+ snprintf(ignorepat, sizeof(ignorepat), "_%s.", ipname);
+ if (!exten || ast_extension_match(ignorepat, exten)) {
+ ast_cli(fd, " Ignore pattern => %-45s [%s]\n",
+ buf, ast_get_ignorepat_registrar(ip));
+ }
+ }
+ if (!rinclude) {
+ struct ast_sw *sw = NULL;
+ while ( (sw = ast_walk_context_switches(c, sw)) ) {
+ snprintf(buf, sizeof(buf), "'%s/%s'",
+ ast_get_switch_name(sw),
+ ast_get_switch_data(sw));
+ ast_cli(fd, " Alt. Switch => %-45s [%s]\n",
+ buf, ast_get_switch_registrar(sw));
+ }
+ }
+
+ if (option_debug && c->pattern_tree)
+ {
+ ast_cli(fd,"\r\n In-mem exten Trie for Fast Extension Pattern Matching:\r\n\r\n");
+
+ ast_cli(fd,"\r\n Explanation: Node Contents Format = <char(s) to match>:<pattern?>:<specif>:[matched extension]\r\n");
+ ast_cli(fd, " Where <char(s) to match> is a set of chars, any one of which should match the current character\r\n");
+ ast_cli(fd, " <pattern?>: Y if this a pattern match (eg. _XZN[5-7]), N otherwise\r\n");
+ ast_cli(fd, " <specif>: an assigned 'exactness' number for this matching char. The lower the number, the more exact the match\r\n");
+ ast_cli(fd, " [matched exten]: If all chars matched to this point, which extension this matches. In form: EXTEN:<exten string> \r\n");
+ ast_cli(fd, " In general, you match a trie node to a string character, from left to right. All possible matching chars\r\n");
+ ast_cli(fd, " are in a string vertically, separated by an unbroken string of '+' characters.\r\n\r\n");
+ cli_match_char_tree(c->pattern_tree, " ", fd);
+ }
+
+ ast_unlock_context(c);
+
+ /* if we print something in context, make an empty line */
+ if (context_info_printed)
+ ast_cli(fd, "\r\n");
+ }
+ ast_unlock_contexts();
+
+ return (dpc->total_exten == old_total_exten) ? -1 : res;
+}
+
+static char *handle_show_dialplan(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char *exten = NULL, *context = NULL;
+ /* Variables used for different counters */
+ struct dialplan_counters counters;
+ const char *incstack[AST_PBX_MAX_STACK];
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "dialplan show";
+ e->usage =
+ "Usage: dialplan show [exten@][context]\n"
+ " Show dialplan\n";
+ return NULL;
+ case CLI_GENERATE:
+ return complete_show_dialplan_context(a->line, a->word, a->pos, a->n);
+ }
+
+ memset(&counters, 0, sizeof(counters));
+
+ if (a->argc != 2 && a->argc != 3)
+ return CLI_SHOWUSAGE;
+
+ /* we obtain [exten@]context? if yes, split them ... */
+ if (a->argc == 3) {
+ if (strchr(a->argv[2], '@')) { /* split into exten & context */
+ context = ast_strdupa(a->argv[2]);
+ exten = strsep(&context, "@");
+ /* change empty strings to NULL */
+ if (ast_strlen_zero(exten))
+ exten = NULL;
+ } else { /* no '@' char, only context given */
+ context = a->argv[2];
+ }
+ if (ast_strlen_zero(context))
+ context = NULL;
+ }
+ /* else Show complete dial plan, context and exten are NULL */
+ show_dialplan_helper(a->fd, context, exten, &counters, NULL, 0, incstack);
+
+ /* check for input failure and throw some error messages */
+ if (context && !counters.context_existence) {
+ ast_cli(a->fd, "There is no existence of '%s' context\n", context);
+ return CLI_FAILURE;
+ }
+
+ if (exten && !counters.extension_existence) {
+ if (context)
+ ast_cli(a->fd, "There is no existence of %s@%s extension\n",
+ exten, context);
+ else
+ ast_cli(a->fd,
+ "There is no existence of '%s' extension in all contexts\n",
+ exten);
+ return CLI_FAILURE;
+ }
+
+ ast_cli(a->fd,"-= %d %s (%d %s) in %d %s. =-\n",
+ counters.total_exten, counters.total_exten == 1 ? "extension" : "extensions",
+ counters.total_prio, counters.total_prio == 1 ? "priority" : "priorities",
+ counters.total_context, counters.total_context == 1 ? "context" : "contexts");
+
+ /* everything ok */
+ return CLI_SUCCESS;
+}
+
+/*! \brief Send ack once */
+static void manager_dpsendack(struct mansession *s, const struct message *m)
+{
+ astman_send_listack(s, m, "DialPlan list will follow", "start");
+}
+
+/*! \brief Show dialplan extensions
+ * XXX this function is similar but not exactly the same as the CLI's
+ * show dialplan. Must check whether the difference is intentional or not.
+ */
+static int manager_show_dialplan_helper(struct mansession *s, const struct message *m,
+ const char *actionidtext, const char *context,
+ const char *exten, struct dialplan_counters *dpc,
+ struct ast_include *rinclude)
+{
+ struct ast_context *c;
+ int res=0, old_total_exten = dpc->total_exten;
+
+ if (ast_strlen_zero(exten))
+ exten = NULL;
+ if (ast_strlen_zero(context))
+ context = NULL;
+
+ ast_debug(3, "manager_show_dialplan: Context: -%s- Extension: -%s-\n", context, exten);
+
+ /* try to lock contexts */
+ if (ast_rdlock_contexts()) {
+ astman_send_error(s, m, "Failed to lock contexts\r\n");
+ ast_log(LOG_WARNING, "Failed to lock contexts list for manager: listdialplan\n");
+ return -1;
+ }
+
+ c = NULL; /* walk all contexts ... */
+ while ( (c = ast_walk_contexts(c)) ) {
+ struct ast_exten *e;
+ struct ast_include *i;
+ struct ast_ignorepat *ip;
+
+ if (context && strcmp(ast_get_context_name(c), context) != 0)
+ continue; /* not the name we want */
+
+ dpc->context_existence = 1;
+
+ ast_debug(3, "manager_show_dialplan: Found Context: %s \n", ast_get_context_name(c));
+
+ if (ast_rdlock_context(c)) { /* failed to lock */
+ ast_debug(3, "manager_show_dialplan: Failed to lock context\n");
+ continue;
+ }
+
+ /* XXX note- an empty context is not printed */
+ e = NULL; /* walk extensions in context */
+ while ( (e = ast_walk_context_extensions(c, e)) ) {
+ struct ast_exten *p;
+
+ /* looking for extension? is this our extension? */
+ if (exten && !ast_extension_match(ast_get_extension_name(e), exten)) {
+ /* not the one we are looking for, continue */
+ ast_debug(3, "manager_show_dialplan: Skipping extension %s\n", ast_get_extension_name(e));
+ continue;
+ }
+ ast_debug(3, "manager_show_dialplan: Found Extension: %s \n", ast_get_extension_name(e));
+
+ dpc->extension_existence = 1;
+
+ /* may we print context info? */
+ dpc->total_context++;
+ dpc->total_exten++;
+
+ p = NULL; /* walk next extension peers */
+ while ( (p = ast_walk_extension_priorities(e, p)) ) {
+ int prio = ast_get_extension_priority(p);
+
+ dpc->total_prio++;
+ if (!dpc->total_items++)
+ manager_dpsendack(s, m);
+ astman_append(s, "Event: ListDialplan\r\n%s", actionidtext);
+ astman_append(s, "Context: %s\r\nExtension: %s\r\n", ast_get_context_name(c), ast_get_extension_name(e) );
+
+ /* XXX maybe make this conditional, if p != e ? */
+ if (ast_get_extension_label(p))
+ astman_append(s, "ExtensionLabel: %s\r\n", ast_get_extension_label(p));
+
+ if (prio == PRIORITY_HINT) {
+ astman_append(s, "Priority: hint\r\nApplication: %s\r\n", ast_get_extension_app(p));
+ } else {
+ astman_append(s, "Priority: %d\r\nApplication: %s\r\nAppData: %s\r\n", prio, ast_get_extension_app(p), (char *) ast_get_extension_app_data(p));
+ }
+ astman_append(s, "Registrar: %s\r\n\r\n", ast_get_extension_registrar(e));
+ }
+ }
+
+ i = NULL; /* walk included and write info ... */
+ while ( (i = ast_walk_context_includes(c, i)) ) {
+ if (exten) {
+ /* Check all includes for the requested extension */
+ manager_show_dialplan_helper(s, m, actionidtext, ast_get_include_name(i), exten, dpc, i);
+ } else {
+ if (!dpc->total_items++)
+ manager_dpsendack(s, m);
+ astman_append(s, "Event: ListDialplan\r\n%s", actionidtext);
+ astman_append(s, "Context: %s\r\nIncludeContext: %s\r\nRegistrar: %s\r\n", ast_get_context_name(c), ast_get_include_name(i), ast_get_include_registrar(i));
+ astman_append(s, "\r\n");
+ ast_debug(3, "manager_show_dialplan: Found Included context: %s \n", ast_get_include_name(i));
+ }
+ }
+
+ ip = NULL; /* walk ignore patterns and write info ... */
+ while ( (ip = ast_walk_context_ignorepats(c, ip)) ) {
+ const char *ipname = ast_get_ignorepat_name(ip);
+ char ignorepat[AST_MAX_EXTENSION];
+
+ snprintf(ignorepat, sizeof(ignorepat), "_%s.", ipname);
+ if (!exten || ast_extension_match(ignorepat, exten)) {
+ if (!dpc->total_items++)
+ manager_dpsendack(s, m);
+ astman_append(s, "Event: ListDialplan\r\n%s", actionidtext);
+ astman_append(s, "Context: %s\r\nIgnorePattern: %s\r\nRegistrar: %s\r\n", ast_get_context_name(c), ipname, ast_get_ignorepat_registrar(ip));
+ astman_append(s, "\r\n");
+ }
+ }
+ if (!rinclude) {
+ struct ast_sw *sw = NULL;
+ while ( (sw = ast_walk_context_switches(c, sw)) ) {
+ if (!dpc->total_items++)
+ manager_dpsendack(s, m);
+ astman_append(s, "Event: ListDialplan\r\n%s", actionidtext);
+ astman_append(s, "Context: %s\r\nSwitch: %s/%s\r\nRegistrar: %s\r\n", ast_get_context_name(c), ast_get_switch_name(sw), ast_get_switch_data(sw), ast_get_switch_registrar(sw));
+ astman_append(s, "\r\n");
+ ast_debug(3, "manager_show_dialplan: Found Switch : %s \n", ast_get_switch_name(sw));
+ }
+ }
+
+ ast_unlock_context(c);
+ }
+ ast_unlock_contexts();
+
+ if (dpc->total_exten == old_total_exten) {
+ ast_debug(3, "manager_show_dialplan: Found nothing new\n");
+ /* Nothing new under the sun */
+ return -1;
+ } else {
+ return res;
+ }
+}
+
+/*! \brief Manager listing of dial plan */
+static int manager_show_dialplan(struct mansession *s, const struct message *m)
+{
+ const char *exten, *context;
+ const char *id = astman_get_header(m, "ActionID");
+ char idtext[256];
+ int res;
+
+ /* Variables used for different counters */
+ struct dialplan_counters counters;
+
+ if (!ast_strlen_zero(id))
+ snprintf(idtext, sizeof(idtext), "ActionID: %s\r\n", id);
+ else
+ idtext[0] = '\0';
+
+ memset(&counters, 0, sizeof(counters));
+
+ exten = astman_get_header(m, "Extension");
+ context = astman_get_header(m, "Context");
+
+ res = manager_show_dialplan_helper(s, m, idtext, context, exten, &counters, NULL);
+
+ if (context && !counters.context_existence) {
+ char errorbuf[BUFSIZ];
+
+ snprintf(errorbuf, sizeof(errorbuf), "Did not find context %s\r\n", context);
+ astman_send_error(s, m, errorbuf);
+ return 0;
+ }
+ if (exten && !counters.extension_existence) {
+ char errorbuf[BUFSIZ];
+
+ if (context)
+ snprintf(errorbuf, sizeof(errorbuf), "Did not find extension %s@%s\r\n", exten, context);
+ else
+ snprintf(errorbuf, sizeof(errorbuf), "Did not find extension %s in any context\r\n", exten);
+ astman_send_error(s, m, errorbuf);
+ return 0;
+ }
+
+ manager_event(EVENT_FLAG_CONFIG, "ShowDialPlanComplete",
+ "EventList: Complete\r\n"
+ "ListItems: %d\r\n"
+ "ListExtensions: %d\r\n"
+ "ListPriorities: %d\r\n"
+ "ListContexts: %d\r\n"
+ "%s"
+ "\r\n", counters.total_items, counters.total_exten, counters.total_prio, counters.total_context, idtext);
+
+ /* everything ok */
+ return 0;
+}
+
+static char mandescr_show_dialplan[] =
+"Description: Show dialplan contexts and extensions.\n"
+"Be aware that showing the full dialplan may take a lot of capacity\n"
+"Variables: \n"
+" ActionID: <id> Action ID for this AMI transaction (optional)\n"
+" Extension: <extension> Extension (Optional)\n"
+" Context: <context> Context (Optional)\n"
+"\n";
+
+
+/*! \brief CLI support for listing global variables in a parseable way */
+static char *handle_show_globals(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int i = 0;
+ struct ast_var_t *newvariable;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show globals";
+ e->usage =
+ "Usage: core show globals\n"
+ " List current global dialplan variables and their values\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ ast_rwlock_rdlock(&globalslock);
+ AST_LIST_TRAVERSE (&globals, newvariable, entries) {
+ i++;
+ ast_cli(a->fd, " %s=%s\n", ast_var_name(newvariable), ast_var_value(newvariable));
+ }
+ ast_rwlock_unlock(&globalslock);
+ ast_cli(a->fd, "\n -- %d variables\n", i);
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_set_global(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core set global";
+ e->usage =
+ "Usage: core set global <name> <value>\n"
+ " Set global dialplan variable <name> to <value>\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != e->args + 2)
+ return CLI_SHOWUSAGE;
+
+ pbx_builtin_setvar_helper(NULL, a->argv[3], a->argv[4]);
+ ast_cli(a->fd, "\n -- Global variable %s set to %s\n", a->argv[3], a->argv[4]);
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_set_chanvar(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct ast_channel *chan;
+ const char *chan_name, *var_name, *var_value;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core set chanvar";
+ e->usage =
+ "Usage: core set chanvar <channel> <varname> <value>\n"
+ " Set channel variable <varname> to <value>\n";
+ return NULL;
+ case CLI_GENERATE:
+ return ast_complete_channels(a->line, a->word, a->pos, a->n, 3);
+ }
+
+ if (a->argc != e->args + 3)
+ return CLI_SHOWUSAGE;
+
+ chan_name = a->argv[e->args];
+ var_name = a->argv[e->args + 1];
+ var_value = a->argv[e->args + 2];
+
+ if (!(chan = ast_get_channel_by_name_locked(chan_name))) {
+ ast_cli(a->fd, "Channel '%s' not found\n", chan_name);
+ return CLI_FAILURE;
+ }
+
+ pbx_builtin_setvar_helper(chan, var_name, var_value);
+
+ ast_channel_unlock(chan);
+
+ ast_cli(a->fd, "\n -- Channel variable '%s' set to '%s' for '%s'\n",
+ var_name, var_value, chan_name);
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_set_extenpatternmatchnew(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int oldval = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "dialplan set extenpatternmatchnew true";
+ e->usage =
+ "Usage: dialplan set extenpatternmatchnew true|false\n"
+ " Use the NEW extension pattern matching algorithm, true or false.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+
+ oldval = pbx_set_extenpatternmatchnew(1);
+
+ if (oldval)
+ ast_cli(a->fd, "\n -- Still using the NEW pattern match algorithm for extension names in the dialplan.\n");
+ else
+ ast_cli(a->fd, "\n -- Switched to using the NEW pattern match algorithm for extension names in the dialplan.\n");
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_unset_extenpatternmatchnew(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ int oldval = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "dialplan set extenpatternmatchnew false";
+ e->usage =
+ "Usage: dialplan set extenpatternmatchnew true|false\n"
+ " Use the NEW extension pattern matching algorithm, true or false.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+
+ oldval = pbx_set_extenpatternmatchnew(0);
+
+ if (!oldval)
+ ast_cli(a->fd, "\n -- Still using the OLD pattern match algorithm for extension names in the dialplan.\n");
+ else
+ ast_cli(a->fd, "\n -- Switched to using the OLD pattern match algorithm for extension names in the dialplan.\n");
+
+ return CLI_SUCCESS;
+}
+
+/*
+ * CLI entries for upper commands ...
+ */
+static struct ast_cli_entry pbx_cli[] = {
+ AST_CLI_DEFINE(handle_show_applications, "Shows registered dialplan applications"),
+ AST_CLI_DEFINE(handle_show_functions, "Shows registered dialplan functions"),
+ AST_CLI_DEFINE(handle_show_switches, "Show alternative switches"),
+ AST_CLI_DEFINE(handle_show_hints, "Show dialplan hints"),
+ AST_CLI_DEFINE(handle_show_hint, "Show dialplan hint"),
+ AST_CLI_DEFINE(handle_show_globals, "Show global dialplan variables"),
+ AST_CLI_DEFINE(handle_show_function, "Describe a specific dialplan function"),
+ AST_CLI_DEFINE(handle_show_application, "Describe a specific dialplan application"),
+ AST_CLI_DEFINE(handle_set_global, "Set global dialplan variable"),
+ AST_CLI_DEFINE(handle_set_chanvar, "Set a channel variable"),
+ AST_CLI_DEFINE(handle_show_dialplan, "Show dialplan"),
+ AST_CLI_DEFINE(handle_unset_extenpatternmatchnew, "Use the Old extension pattern matching algorithm."),
+ AST_CLI_DEFINE(handle_set_extenpatternmatchnew, "Use the New extension pattern matching algorithm."),
+};
+
+static void unreference_cached_app(struct ast_app *app)
+{
+ struct ast_context *context = NULL;
+ struct ast_exten *eroot = NULL, *e = NULL;
+
+ ast_rdlock_contexts();
+ while ((context = ast_walk_contexts(context))) {
+ while ((eroot = ast_walk_context_extensions(context, eroot))) {
+ while ((e = ast_walk_extension_priorities(eroot, e))) {
+ if (e->cached_app == app)
+ e->cached_app = NULL;
+ }
+ }
+ }
+ ast_unlock_contexts();
+
+ return;
+}
+
+int ast_unregister_application(const char *app)
+{
+ struct ast_app *tmp;
+
+ AST_RWLIST_WRLOCK(&apps);
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&apps, tmp, list) {
+ if (!strcasecmp(app, tmp->name)) {
+ unreference_cached_app(tmp);
+ AST_RWLIST_REMOVE_CURRENT(list);
+ ast_verb(2, "Unregistered application '%s'\n", tmp->name);
+ ast_free(tmp);
+ break;
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+ AST_RWLIST_UNLOCK(&apps);
+
+ return tmp ? 0 : -1;
+}
+
+static struct ast_context *__ast_context_create(struct ast_context **extcontexts, const char *name, const char *registrar, int existsokay)
+{
+ struct ast_context *tmp, **local_contexts;
+ struct fake_context search;
+ int length = sizeof(struct ast_context) + strlen(name) + 1;
+
+ if (!contexts_tree) {
+ contexts_tree = ast_hashtab_create(17,
+ hashtab_compare_contexts,
+ ast_hashtab_resize_java,
+ ast_hashtab_newsize_java,
+ hashtab_hash_contexts,
+ 0);
+ }
+
+ if (!extcontexts) {
+ ast_rdlock_contexts();
+ local_contexts = &contexts;
+ strncpy(search.name,name,sizeof(search.name));
+ tmp = ast_hashtab_lookup(contexts_tree, &search);
+ if (!existsokay && tmp) {
+ ast_log(LOG_WARNING, "Tried to register context '%s', already in use\n", name);
+ }
+ ast_unlock_contexts();
+ if (tmp)
+ return tmp;
+ } else { /* local contexts just in a linked list; search there for the new context; slow, linear search, but not frequent */
+ local_contexts = extcontexts;
+ for (tmp = *local_contexts; tmp; tmp = tmp->next) {
+ if (!strcasecmp(tmp->name, name)) {
+ if (!existsokay) {
+ ast_log(LOG_WARNING, "Tried to register context '%s', already in use\n", name);
+ tmp = NULL;
+ }
+ return tmp;
+ }
+ }
+ }
+
+ if ((tmp = ast_calloc(1, length))) {
+ ast_rwlock_init(&tmp->lock);
+ ast_mutex_init(&tmp->macrolock);
+ strcpy(tmp->name, name);
+ tmp->root = NULL;
+ tmp->root_tree = NULL;
+ tmp->registrar = registrar;
+ tmp->includes = NULL;
+ tmp->ignorepats = NULL;
+ }
+ if (!extcontexts) {
+ ast_wrlock_contexts();
+ tmp->next = *local_contexts;
+ *local_contexts = tmp;
+ ast_hashtab_insert_safe(contexts_tree, tmp); /*put this context into the tree */
+ ast_unlock_contexts();
+ } else {
+ tmp->next = *local_contexts;
+ *local_contexts = tmp;
+ }
+ ast_debug(1, "Registered context '%s'\n", tmp->name);
+ ast_verb(3, "Registered extension context '%s'\n", tmp->name);
+ return tmp;
+}
+
+struct ast_context *ast_context_create(struct ast_context **extcontexts, const char *name, const char *registrar)
+{
+ return __ast_context_create(extcontexts, name, registrar, 0);
+}
+
+struct ast_context *ast_context_find_or_create(struct ast_context **extcontexts, const char *name, const char *registrar)
+{
+ return __ast_context_create(extcontexts, name, registrar, 1);
+}
+void __ast_context_destroy(struct ast_context *con, const char *registrar);
+
+struct store_hint {
+ char *context;
+ char *exten;
+ struct ast_state_cb *callbacks;
+ int laststate;
+ AST_LIST_ENTRY(store_hint) list;
+ char data[1];
+};
+
+AST_LIST_HEAD(store_hints, store_hint);
+
+/* XXX this does not check that multiple contexts are merged */
+void ast_merge_contexts_and_delete(struct ast_context **extcontexts, const char *registrar)
+{
+ struct ast_context *tmp, *lasttmp = NULL;
+ struct store_hints store = AST_LIST_HEAD_INIT_VALUE;
+ struct store_hint *this;
+ struct ast_hint *hint;
+ struct ast_exten *exten;
+ int length;
+ struct ast_state_cb *thiscb, *prevcb;
+
+ /* it is very important that this function hold the hint list lock _and_ the conlock
+ during its operation; not only do we need to ensure that the list of contexts
+ and extensions does not change, but also that no hint callbacks (watchers) are
+ added or removed during the merge/delete process
+
+ in addition, the locks _must_ be taken in this order, because there are already
+ other code paths that use this order
+ */
+ ast_wrlock_contexts();
+ AST_RWLIST_WRLOCK(&hints);
+
+ /* preserve all watchers for hints associated with this registrar */
+ AST_RWLIST_TRAVERSE(&hints, hint, list) {
+ if (hint->callbacks && !strcmp(registrar, hint->exten->parent->registrar)) {
+ length = strlen(hint->exten->exten) + strlen(hint->exten->parent->name) + 2 + sizeof(*this);
+ if (!(this = ast_calloc(1, length)))
+ continue;
+ this->callbacks = hint->callbacks;
+ hint->callbacks = NULL;
+ this->laststate = hint->laststate;
+ this->context = this->data;
+ strcpy(this->data, hint->exten->parent->name);
+ this->exten = this->data + strlen(this->context) + 1;
+ strcpy(this->exten, hint->exten->exten);
+ AST_LIST_INSERT_HEAD(&store, this, list);
+ }
+ }
+
+ tmp = *extcontexts;
+ if (registrar) {
+ /* XXX remove previous contexts from same registrar */
+ ast_debug(1, "must remove any reg %s\n", registrar);
+ __ast_context_destroy(NULL,registrar);
+ while (tmp) {
+ lasttmp = tmp;
+ tmp = tmp->next;
+ }
+ } else {
+ /* XXX remove contexts with the same name */
+ while (tmp) {
+ ast_log(LOG_WARNING, "must remove %s reg %s\n", tmp->name, tmp->registrar);
+ __ast_context_destroy(tmp,tmp->registrar);
+ lasttmp = tmp;
+ tmp = tmp->next;
+ }
+ }
+ tmp = *extcontexts;
+ while (tmp) {
+ ast_hashtab_insert_safe(contexts_tree, tmp); /*put this context into the tree */
+ tmp = tmp->next;
+ }
+ if (lasttmp) {
+ lasttmp->next = contexts;
+ contexts = *extcontexts;
+ *extcontexts = NULL;
+ } else
+ ast_log(LOG_WARNING, "Requested contexts didn't get merged\n");
+
+ /* restore the watchers for hints that can be found; notify those that
+ cannot be restored
+ */
+ while ((this = AST_LIST_REMOVE_HEAD(&store, list))) {
+ struct pbx_find_info q = { .stacklen = 0 };
+ exten = pbx_find_extension(NULL, NULL, &q, this->context, this->exten, PRIORITY_HINT, NULL, "", E_MATCH);
+ /* Find the hint in the list of hints */
+ AST_RWLIST_TRAVERSE(&hints, hint, list) {
+ if (hint->exten == exten)
+ break;
+ }
+ if (!exten || !hint) {
+ /* this hint has been removed, notify the watchers */
+ prevcb = NULL;
+ thiscb = this->callbacks;
+ while (thiscb) {
+ prevcb = thiscb;
+ thiscb = thiscb->next;
+ prevcb->callback(this->context, this->exten, AST_EXTENSION_REMOVED, prevcb->data);
+ ast_free(prevcb);
+ }
+ } else {
+ thiscb = this->callbacks;
+ while (thiscb->next)
+ thiscb = thiscb->next;
+ thiscb->next = hint->callbacks;
+ hint->callbacks = this->callbacks;
+ hint->laststate = this->laststate;
+ }
+ ast_free(this);
+ }
+
+ AST_RWLIST_UNLOCK(&hints);
+ ast_unlock_contexts();
+
+ return;
+}
+
+/*
+ * errno values
+ * EBUSY - can't lock
+ * ENOENT - no existence of context
+ */
+int ast_context_add_include(const char *context, const char *include, const char *registrar)
+{
+ int ret = -1;
+ struct ast_context *c = find_context_locked(context);
+
+ if (c) {
+ ret = ast_context_add_include2(c, include, registrar);
+ ast_unlock_contexts();
+ }
+ return ret;
+}
+
+/*! \brief Helper for get_range.
+ * return the index of the matching entry, starting from 1.
+ * If names is not supplied, try numeric values.
+ */
+static int lookup_name(const char *s, char *const names[], int max)
+{
+ int i;
+
+ if (names) {
+ for (i = 0; names[i]; i++) {
+ if (!strcasecmp(s, names[i]))
+ return i+1;
+ }
+ } else if (sscanf(s, "%d", &i) == 1 && i >= 1 && i <= max) {
+ return i;
+ }
+ return 0; /* error return */
+}
+
+/*! \brief helper function to return a range up to max (7, 12, 31 respectively).
+ * names, if supplied, is an array of names that should be mapped to numbers.
+ */
+static unsigned get_range(char *src, int max, char *const names[], const char *msg)
+{
+ int s, e; /* start and ending position */
+ unsigned int mask = 0;
+
+ /* Check for whole range */
+ if (ast_strlen_zero(src) || !strcmp(src, "*")) {
+ s = 0;
+ e = max - 1;
+ } else {
+ /* Get start and ending position */
+ char *c = strchr(src, '-');
+ if (c)
+ *c++ = '\0';
+ /* Find the start */
+ s = lookup_name(src, names, max);
+ if (!s) {
+ ast_log(LOG_WARNING, "Invalid %s '%s', assuming none\n", msg, src);
+ return 0;
+ }
+ s--;
+ if (c) { /* find end of range */
+ e = lookup_name(c, names, max);
+ if (!e) {
+ ast_log(LOG_WARNING, "Invalid end %s '%s', assuming none\n", msg, c);
+ return 0;
+ }
+ e--;
+ } else
+ e = s;
+ }
+ /* Fill the mask. Remember that ranges are cyclic */
+ mask = 1 << e; /* initialize with last element */
+ while (s != e) {
+ if (s >= max) {
+ s = 0;
+ mask |= (1 << s);
+ } else {
+ mask |= (1 << s);
+ s++;
+ }
+ }
+ return mask;
+}
+
+/*! \brief store a bitmask of valid times, one bit each 2 minute */
+static void get_timerange(struct ast_timing *i, char *times)
+{
+ char *e;
+ int x;
+ int s1, s2;
+ int e1, e2;
+ /* int cth, ctm; */
+
+ /* start disabling all times, fill the fields with 0's, as they may contain garbage */
+ memset(i->minmask, 0, sizeof(i->minmask));
+
+ /* 2-minutes per bit, since the mask has only 32 bits :( */
+ /* Star is all times */
+ if (ast_strlen_zero(times) || !strcmp(times, "*")) {
+ for (x=0; x<24; x++)
+ i->minmask[x] = 0x3fffffff; /* 30 bits */
+ return;
+ }
+ /* Otherwise expect a range */
+ e = strchr(times, '-');
+ if (!e) {
+ ast_log(LOG_WARNING, "Time range is not valid. Assuming no restrictions based on time.\n");
+ return;
+ }
+ *e++ = '\0';
+ /* XXX why skip non digits ? */
+ while (*e && !isdigit(*e))
+ e++;
+ if (!*e) {
+ ast_log(LOG_WARNING, "Invalid time range. Assuming no restrictions based on time.\n");
+ return;
+ }
+ if (sscanf(times, "%d:%d", &s1, &s2) != 2) {
+ ast_log(LOG_WARNING, "%s isn't a time. Assuming no restrictions based on time.\n", times);
+ return;
+ }
+ if (sscanf(e, "%d:%d", &e1, &e2) != 2) {
+ ast_log(LOG_WARNING, "%s isn't a time. Assuming no restrictions based on time.\n", e);
+ return;
+ }
+ /* XXX this needs to be optimized */
+#if 1
+ s1 = s1 * 30 + s2/2;
+ if ((s1 < 0) || (s1 >= 24*30)) {
+ ast_log(LOG_WARNING, "%s isn't a valid start time. Assuming no time.\n", times);
+ return;
+ }
+ e1 = e1 * 30 + e2/2;
+ if ((e1 < 0) || (e1 >= 24*30)) {
+ ast_log(LOG_WARNING, "%s isn't a valid end time. Assuming no time.\n", e);
+ return;
+ }
+ /* Go through the time and enable each appropriate bit */
+ for (x=s1;x != e1;x = (x + 1) % (24 * 30)) {
+ i->minmask[x/30] |= (1 << (x % 30));
+ }
+ /* Do the last one */
+ i->minmask[x/30] |= (1 << (x % 30));
+#else
+ for (cth=0; cth<24; cth++) {
+ /* Initialize masks to blank */
+ i->minmask[cth] = 0;
+ for (ctm=0; ctm<30; ctm++) {
+ if (
+ /* First hour with more than one hour */
+ (((cth == s1) && (ctm >= s2)) &&
+ ((cth < e1)))
+ /* Only one hour */
+ || (((cth == s1) && (ctm >= s2)) &&
+ ((cth == e1) && (ctm <= e2)))
+ /* In between first and last hours (more than 2 hours) */
+ || ((cth > s1) &&
+ (cth < e1))
+ /* Last hour with more than one hour */
+ || ((cth > s1) &&
+ ((cth == e1) && (ctm <= e2)))
+ )
+ i->minmask[cth] |= (1 << (ctm / 2));
+ }
+ }
+#endif
+ /* All done */
+ return;
+}
+
+static char *days[] =
+{
+ "sun",
+ "mon",
+ "tue",
+ "wed",
+ "thu",
+ "fri",
+ "sat",
+ NULL,
+};
+
+static char *months[] =
+{
+ "jan",
+ "feb",
+ "mar",
+ "apr",
+ "may",
+ "jun",
+ "jul",
+ "aug",
+ "sep",
+ "oct",
+ "nov",
+ "dec",
+ NULL,
+};
+
+int ast_build_timing(struct ast_timing *i, const char *info_in)
+{
+ char info_save[256];
+ char *info;
+
+ /* Check for empty just in case */
+ if (ast_strlen_zero(info_in))
+ return 0;
+ /* make a copy just in case we were passed a static string */
+ ast_copy_string(info_save, info_in, sizeof(info_save));
+ info = info_save;
+ /* Assume everything except time */
+ i->monthmask = 0xfff; /* 12 bits */
+ i->daymask = 0x7fffffffU; /* 31 bits */
+ i->dowmask = 0x7f; /* 7 bits */
+ /* on each call, use strsep() to move info to the next argument */
+ get_timerange(i, strsep(&info, "|,"));
+ if (info)
+ i->dowmask = get_range(strsep(&info, "|,"), 7, days, "day of week");
+ if (info)
+ i->daymask = get_range(strsep(&info, "|,"), 31, NULL, "day");
+ if (info)
+ i->monthmask = get_range(strsep(&info, "|,"), 12, months, "month");
+ return 1;
+}
+
+int ast_check_timing(const struct ast_timing *i)
+{
+ struct ast_tm tm;
+ struct timeval tv = ast_tvnow();
+
+ ast_localtime(&tv, &tm, NULL);
+
+ /* If it's not the right month, return */
+ if (!(i->monthmask & (1 << tm.tm_mon)))
+ return 0;
+
+ /* If it's not that time of the month.... */
+ /* Warning, tm_mday has range 1..31! */
+ if (!(i->daymask & (1 << (tm.tm_mday-1))))
+ return 0;
+
+ /* If it's not the right day of the week */
+ if (!(i->dowmask & (1 << tm.tm_wday)))
+ return 0;
+
+ /* Sanity check the hour just to be safe */
+ if ((tm.tm_hour < 0) || (tm.tm_hour > 23)) {
+ ast_log(LOG_WARNING, "Insane time...\n");
+ return 0;
+ }
+
+ /* Now the tough part, we calculate if it fits
+ in the right time based on min/hour */
+ if (!(i->minmask[tm.tm_hour] & (1 << (tm.tm_min / 2))))
+ return 0;
+
+ /* If we got this far, then we're good */
+ return 1;
+}
+
+/*
+ * errno values
+ * ENOMEM - out of memory
+ * EBUSY - can't lock
+ * EEXIST - already included
+ * EINVAL - there is no existence of context for inclusion
+ */
+int ast_context_add_include2(struct ast_context *con, const char *value,
+ const char *registrar)
+{
+ struct ast_include *new_include;
+ char *c;
+ struct ast_include *i, *il = NULL; /* include, include_last */
+ int length;
+ char *p;
+
+ length = sizeof(struct ast_include);
+ length += 2 * (strlen(value) + 1);
+
+ /* allocate new include structure ... */
+ if (!(new_include = ast_calloc(1, length)))
+ return -1;
+ /* Fill in this structure. Use 'p' for assignments, as the fields
+ * in the structure are 'const char *'
+ */
+ p = new_include->stuff;
+ new_include->name = p;
+ strcpy(p, value);
+ p += strlen(value) + 1;
+ new_include->rname = p;
+ strcpy(p, value);
+ /* Strip off timing info, and process if it is there */
+ if ( (c = strchr(p, ',')) ) {
+ *c++ = '\0';
+ new_include->hastime = ast_build_timing(&(new_include->timing), c);
+ }
+ new_include->next = NULL;
+ new_include->registrar = registrar;
+
+ ast_wrlock_context(con);
+
+ /* ... go to last include and check if context is already included too... */
+ for (i = con->includes; i; i = i->next) {
+ if (!strcasecmp(i->name, new_include->name)) {
+ ast_free(new_include);
+ ast_unlock_context(con);
+ errno = EEXIST;
+ return -1;
+ }
+ il = i;
+ }
+
+ /* ... include new context into context list, unlock, return */
+ if (il)
+ il->next = new_include;
+ else
+ con->includes = new_include;
+ ast_verb(3, "Including context '%s' in context '%s'\n", new_include->name, ast_get_context_name(con));
+
+ ast_unlock_context(con);
+
+ return 0;
+}
+
+/*
+ * errno values
+ * EBUSY - can't lock
+ * ENOENT - no existence of context
+ */
+int ast_context_add_switch(const char *context, const char *sw, const char *data, int eval, const char *registrar)
+{
+ int ret = -1;
+ struct ast_context *c = find_context_locked(context);
+
+ if (c) { /* found, add switch to this context */
+ ret = ast_context_add_switch2(c, sw, data, eval, registrar);
+ ast_unlock_contexts();
+ }
+ return ret;
+}
+
+/*
+ * errno values
+ * ENOMEM - out of memory
+ * EBUSY - can't lock
+ * EEXIST - already included
+ * EINVAL - there is no existence of context for inclusion
+ */
+int ast_context_add_switch2(struct ast_context *con, const char *value,
+ const char *data, int eval, const char *registrar)
+{
+ struct ast_sw *new_sw;
+ struct ast_sw *i;
+ int length;
+ char *p;
+
+ length = sizeof(struct ast_sw);
+ length += strlen(value) + 1;
+ if (data)
+ length += strlen(data);
+ length++;
+ if (eval) {
+ /* Create buffer for evaluation of variables */
+ length += SWITCH_DATA_LENGTH;
+ length++;
+ }
+
+ /* allocate new sw structure ... */
+ if (!(new_sw = ast_calloc(1, length)))
+ return -1;
+ /* ... fill in this structure ... */
+ p = new_sw->stuff;
+ new_sw->name = p;
+ strcpy(new_sw->name, value);
+ p += strlen(value) + 1;
+ new_sw->data = p;
+ if (data) {
+ strcpy(new_sw->data, data);
+ p += strlen(data) + 1;
+ } else {
+ strcpy(new_sw->data, "");
+ p++;
+ }
+ if (eval)
+ new_sw->tmpdata = p;
+ new_sw->eval = eval;
+ new_sw->registrar = registrar;
+
+ /* ... try to lock this context ... */
+ ast_wrlock_context(con);
+
+ /* ... go to last sw and check if context is already swd too... */
+ AST_LIST_TRAVERSE(&con->alts, i, list) {
+ if (!strcasecmp(i->name, new_sw->name) && !strcasecmp(i->data, new_sw->data)) {
+ ast_free(new_sw);
+ ast_unlock_context(con);
+ errno = EEXIST;
+ return -1;
+ }
+ }
+
+ /* ... sw new context into context list, unlock, return */
+ AST_LIST_INSERT_TAIL(&con->alts, new_sw, list);
+
+ ast_verb(3, "Including switch '%s/%s' in context '%s'\n", new_sw->name, new_sw->data, ast_get_context_name(con));
+
+ ast_unlock_context(con);
+
+ return 0;
+}
+
+/*
+ * EBUSY - can't lock
+ * ENOENT - there is not context existence
+ */
+int ast_context_remove_ignorepat(const char *context, const char *ignorepat, const char *registrar)
+{
+ int ret = -1;
+ struct ast_context *c = find_context_locked(context);
+
+ if (c) {
+ ret = ast_context_remove_ignorepat2(c, ignorepat, registrar);
+ ast_unlock_contexts();
+ }
+ return ret;
+}
+
+int ast_context_remove_ignorepat2(struct ast_context *con, const char *ignorepat, const char *registrar)
+{
+ struct ast_ignorepat *ip, *ipl = NULL;
+
+ ast_wrlock_context(con);
+
+ for (ip = con->ignorepats; ip; ip = ip->next) {
+ if (!strcmp(ip->pattern, ignorepat) &&
+ (!registrar || (registrar == ip->registrar))) {
+ if (ipl) {
+ ipl->next = ip->next;
+ ast_free(ip);
+ } else {
+ con->ignorepats = ip->next;
+ ast_free(ip);
+ }
+ ast_unlock_context(con);
+ return 0;
+ }
+ ipl = ip;
+ }
+
+ ast_unlock_context(con);
+ errno = EINVAL;
+ return -1;
+}
+
+/*
+ * EBUSY - can't lock
+ * ENOENT - there is no existence of context
+ */
+int ast_context_add_ignorepat(const char *context, const char *value, const char *registrar)
+{
+ int ret = -1;
+ struct ast_context *c = find_context_locked(context);
+
+ if (c) {
+ ret = ast_context_add_ignorepat2(c, value, registrar);
+ ast_unlock_contexts();
+ }
+ return ret;
+}
+
+int ast_context_add_ignorepat2(struct ast_context *con, const char *value, const char *registrar)
+{
+ struct ast_ignorepat *ignorepat, *ignorepatc, *ignorepatl = NULL;
+ int length;
+ length = sizeof(struct ast_ignorepat);
+ length += strlen(value) + 1;
+ if (!(ignorepat = ast_calloc(1, length)))
+ return -1;
+ /* The cast to char * is because we need to write the initial value.
+ * The field is not supposed to be modified otherwise
+ */
+ strcpy((char *)ignorepat->pattern, value);
+ ignorepat->next = NULL;
+ ignorepat->registrar = registrar;
+ ast_wrlock_context(con);
+ for (ignorepatc = con->ignorepats; ignorepatc; ignorepatc = ignorepatc->next) {
+ ignorepatl = ignorepatc;
+ if (!strcasecmp(ignorepatc->pattern, value)) {
+ /* Already there */
+ ast_unlock_context(con);
+ errno = EEXIST;
+ return -1;
+ }
+ }
+ if (ignorepatl)
+ ignorepatl->next = ignorepat;
+ else
+ con->ignorepats = ignorepat;
+ ast_unlock_context(con);
+ return 0;
+
+}
+
+int ast_ignore_pattern(const char *context, const char *pattern)
+{
+ struct ast_context *con = ast_context_find(context);
+ if (con) {
+ struct ast_ignorepat *pat;
+ for (pat = con->ignorepats; pat; pat = pat->next) {
+ if (ast_extension_match(pat->pattern, pattern))
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * EBUSY - can't lock
+ * ENOENT - no existence of context
+ *
+ */
+int ast_add_extension(const char *context, int replace, const char *extension,
+ int priority, const char *label, const char *callerid,
+ const char *application, void *data, void (*datad)(void *), const char *registrar)
+{
+ int ret = -1;
+ struct ast_context *c = find_context_locked(context);
+
+ if (c) {
+ ret = ast_add_extension2(c, replace, extension, priority, label, callerid,
+ application, data, datad, registrar);
+ ast_unlock_contexts();
+ }
+ return ret;
+}
+
+int ast_explicit_goto(struct ast_channel *chan, const char *context, const char *exten, int priority)
+{
+ if (!chan)
+ return -1;
+
+ ast_channel_lock(chan);
+
+ if (!ast_strlen_zero(context))
+ ast_copy_string(chan->context, context, sizeof(chan->context));
+ if (!ast_strlen_zero(exten))
+ ast_copy_string(chan->exten, exten, sizeof(chan->exten));
+ if (priority > -1) {
+ chan->priority = priority;
+ /* see flag description in channel.h for explanation */
+ if (ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP))
+ chan->priority--;
+ }
+
+ ast_channel_unlock(chan);
+
+ return 0;
+}
+
+int ast_async_goto(struct ast_channel *chan, const char *context, const char *exten, int priority)
+{
+ int res = 0;
+
+ ast_channel_lock(chan);
+
+ if (chan->pbx) { /* This channel is currently in the PBX */
+ ast_explicit_goto(chan, context, exten, priority);
+ ast_softhangup_nolock(chan, AST_SOFTHANGUP_ASYNCGOTO);
+ } else {
+ /* In order to do it when the channel doesn't really exist within
+ the PBX, we have to make a new channel, masquerade, and start the PBX
+ at the new location */
+ struct ast_channel *tmpchan = ast_channel_alloc(0, chan->_state, 0, 0, chan->accountcode, chan->exten, chan->context, chan->amaflags, "AsyncGoto/%s", chan->name);
+ if (chan->cdr) {
+ tmpchan->cdr = ast_cdr_dup(chan->cdr);
+ }
+ if (!tmpchan)
+ res = -1;
+ else {
+ /* Make formats okay */
+ tmpchan->readformat = chan->readformat;
+ tmpchan->writeformat = chan->writeformat;
+ /* Setup proper location */
+ ast_explicit_goto(tmpchan,
+ S_OR(context, chan->context), S_OR(exten, chan->exten), priority);
+
+ /* Masquerade into temp channel */
+ ast_channel_masquerade(tmpchan, chan);
+
+ /* Grab the locks and get going */
+ ast_channel_lock(tmpchan);
+ ast_do_masquerade(tmpchan);
+ ast_channel_unlock(tmpchan);
+ /* Start the PBX going on our stolen channel */
+ if (ast_pbx_start(tmpchan)) {
+ ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmpchan->name);
+ ast_hangup(tmpchan);
+ res = -1;
+ }
+ }
+ }
+ ast_channel_unlock(chan);
+ return res;
+}
+
+int ast_async_goto_by_name(const char *channame, const char *context, const char *exten, int priority)
+{
+ struct ast_channel *chan;
+ int res = -1;
+
+ chan = ast_get_channel_by_name_locked(channame);
+ if (chan) {
+ res = ast_async_goto(chan, context, exten, priority);
+ ast_channel_unlock(chan);
+ }
+ return res;
+}
+
+/*! \brief copy a string skipping whitespace */
+static int ext_strncpy(char *dst, const char *src, int len)
+{
+ int count=0;
+
+ while (*src && (count < len - 1)) {
+ switch (*src) {
+ case ' ':
+ /* otherwise exten => [a-b],1,... doesn't work */
+ /* case '-': */
+ /* Ignore */
+ break;
+ default:
+ *dst = *src;
+ dst++;
+ }
+ src++;
+ count++;
+ }
+ *dst = '\0';
+
+ return count;
+}
+
+/*!
+ * \brief add the extension in the priority chain.
+ * \retval 0 on success.
+ * \retval -1 on failure.
+*/
+static int add_pri(struct ast_context *con, struct ast_exten *tmp,
+ struct ast_exten *el, struct ast_exten *e, int replace)
+{
+ struct ast_exten *ep;
+ struct ast_exten *eh=e;
+
+ for (ep = NULL; e ; ep = e, e = e->peer) {
+ if (e->priority >= tmp->priority)
+ break;
+ }
+ if (!e) { /* go at the end, and ep is surely set because the list is not empty */
+ ast_hashtab_insert_safe(eh->peer_tree, tmp);
+ if (tmp->label)
+ ast_hashtab_insert_safe(eh->peer_label_tree, tmp);
+ ep->peer = tmp;
+ return 0; /* success */
+ }
+ if (e->priority == tmp->priority) {
+ /* Can't have something exactly the same. Is this a
+ replacement? If so, replace, otherwise, bonk. */
+ if (!replace) {
+ ast_log(LOG_WARNING, "Unable to register extension '%s', priority %d in '%s', already in use\n", tmp->exten, tmp->priority, con->name);
+ if (tmp->datad)
+ tmp->datad(tmp->data);
+ ast_free(tmp);
+ return -1;
+ }
+ /* we are replacing e, so copy the link fields and then update
+ * whoever pointed to e to point to us
+ */
+ tmp->next = e->next; /* not meaningful if we are not first in the peer list */
+ tmp->peer = e->peer; /* always meaningful */
+ if (ep) { /* We're in the peer list, just insert ourselves */
+ ast_hashtab_remove_object_via_lookup(eh->peer_tree,e);
+ if (e->label)
+ ast_hashtab_remove_object_via_lookup(eh->peer_label_tree,e);
+ ast_hashtab_insert_safe(eh->peer_tree,tmp);
+ if (tmp->label)
+ ast_hashtab_insert_safe(eh->peer_label_tree,tmp);
+ ep->peer = tmp;
+ } else if (el) { /* We're the first extension. Take over e's functions */
+ struct match_char *x = add_exten_to_pattern_tree(con, e, 1);
+ tmp->peer_tree = e->peer_tree;
+ tmp->peer_label_tree = e->peer_label_tree;
+ ast_hashtab_remove_object_via_lookup(tmp->peer_tree,e);
+ ast_hashtab_insert_safe(tmp->peer_tree,tmp);
+ if (e->label)
+ ast_hashtab_remove_object_via_lookup(tmp->peer_label_tree,e);
+ if (tmp->label)
+ ast_hashtab_insert_safe(tmp->peer_label_tree,tmp);
+ ast_hashtab_remove_object_via_lookup(con->root_tree, e);
+ ast_hashtab_insert_safe(con->root_tree, tmp);
+ el->next = tmp;
+ /* The pattern trie points to this exten; replace the pointer,
+ and all will be well */
+
+ if (x->exten) { /* this test for safety purposes */
+ x->exten = tmp; /* replace what would become a bad pointer */
+ } else {
+ ast_log(LOG_ERROR,"Trying to delete an exten from a context, but the pattern tree node returned isn't an extension\n");
+ }
+ } else { /* We're the very first extension. */
+ struct match_char *x = add_exten_to_pattern_tree(con, e, 1);
+ ast_hashtab_remove_object_via_lookup(con->root_tree,e);
+ ast_hashtab_insert_safe(con->root_tree,tmp);
+ tmp->peer_tree = e->peer_tree;
+ tmp->peer_label_tree = e->peer_label_tree;
+ ast_hashtab_remove_object_via_lookup(tmp->peer_tree,e);
+ ast_hashtab_insert_safe(tmp->peer_tree,tmp);
+ if (e->label)
+ ast_hashtab_remove_object_via_lookup(tmp->peer_label_tree,e);
+ if (tmp->label)
+ ast_hashtab_insert_safe(tmp->peer_label_tree,tmp);
+ ast_hashtab_remove_object_via_lookup(con->root_tree, e);
+ ast_hashtab_insert_safe(con->root_tree, tmp);
+ con->root = tmp;
+ /* The pattern trie points to this exten; replace the pointer,
+ and all will be well */
+ if (x->exten) { /* this test for safety purposes */
+ x->exten = tmp; /* replace what would become a bad pointer */
+ } else {
+ ast_log(LOG_ERROR,"Trying to delete an exten from a context, but the pattern tree node returned isn't an extension\n");
+ }
+ }
+ if (tmp->priority == PRIORITY_HINT)
+ ast_change_hint(e,tmp);
+ /* Destroy the old one */
+ if (e->datad)
+ e->datad(e->data);
+ ast_free(e);
+ } else { /* Slip ourselves in just before e */
+ tmp->peer = e;
+ tmp->next = e->next; /* extension chain, or NULL if e is not the first extension */
+ if (ep) { /* Easy enough, we're just in the peer list */
+ if (tmp->label)
+ ast_hashtab_insert_safe(eh->peer_label_tree, tmp);
+ ast_hashtab_insert_safe(eh->peer_tree, tmp);
+ ep->peer = tmp;
+ } else { /* we are the first in some peer list, so link in the ext list */
+ tmp->peer_tree = e->peer_tree;
+ tmp->peer_label_tree = e ->peer_label_tree;
+ e->peer_tree = 0;
+ e->peer_label_tree = 0;
+ ast_hashtab_insert_safe(tmp->peer_tree,tmp);
+ if (tmp->label) {
+ ast_hashtab_insert_safe(tmp->peer_label_tree,tmp);
+ }
+ ast_hashtab_remove_object_via_lookup(con->root_tree,e);
+ ast_hashtab_insert_safe(con->root_tree,tmp);
+ if (el)
+ el->next = tmp; /* in the middle... */
+ else
+ con->root = tmp; /* ... or at the head */
+ e->next = NULL; /* e is no more at the head, so e->next must be reset */
+ }
+ /* And immediately return success. */
+ if (tmp->priority == PRIORITY_HINT)
+ ast_add_hint(tmp);
+ }
+ return 0;
+}
+
+/*! \brief
+ * Main interface to add extensions to the list for out context.
+ *
+ * We sort extensions in order of matching preference, so that we can
+ * stop the search as soon as we find a suitable match.
+ * This ordering also takes care of wildcards such as '.' (meaning
+ * "one or more of any character") and '!' (which is 'earlymatch',
+ * meaning "zero or more of any character" but also impacts the
+ * return value from CANMATCH and EARLYMATCH.
+ *
+ * The extension match rules defined in the devmeeting 2006.05.05 are
+ * quite simple: WE SELECT THE LONGEST MATCH.
+ * In detail, "longest" means the number of matched characters in
+ * the extension. In case of ties (e.g. _XXX and 333) in the length
+ * of a pattern, we give priority to entries with the smallest cardinality
+ * (e.g, [5-9] comes before [2-8] before the former has only 5 elements,
+ * while the latter has 7, etc.
+ * In case of same cardinality, the first element in the range counts.
+ * If we still have a tie, any final '!' will make this as a possibly
+ * less specific pattern.
+ *
+ * EBUSY - can't lock
+ * EEXIST - extension with the same priority exist and no replace is set
+ *
+ */
+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)
+{
+ /*
+ * Sort extensions (or patterns) according to the rules indicated above.
+ * These are implemented by the function ext_cmp()).
+ * All priorities for the same ext/pattern/cid are kept in a list,
+ * using the 'peer' field as a link field..
+ */
+ struct ast_exten *tmp, *tmp2, *e, *el = NULL;
+ int res;
+ int length;
+ char *p;
+ char expand_buf[VAR_BUF_SIZE];
+ struct ast_exten dummy_exten = {0};
+ char dummy_name[1024];
+
+ /* if we are adding a hint, and there are global variables, and the hint
+ contains variable references, then expand them
+ */
+ ast_rwlock_rdlock(&globalslock);
+ if (priority == PRIORITY_HINT && AST_LIST_FIRST(&globals) && strstr(application, "${")) {
+ pbx_substitute_variables_varshead(&globals, application, expand_buf, sizeof(expand_buf));
+ application = expand_buf;
+ }
+ ast_rwlock_unlock(&globalslock);
+
+ length = sizeof(struct ast_exten);
+ length += strlen(extension) + 1;
+ length += strlen(application) + 1;
+ if (label)
+ length += strlen(label) + 1;
+ if (callerid)
+ length += strlen(callerid) + 1;
+ else
+ length ++; /* just the '\0' */
+
+ /* Be optimistic: Build the extension structure first */
+ if (!(tmp = ast_calloc(1, length)))
+ return -1;
+
+ /* use p as dst in assignments, as the fields are const char * */
+ p = tmp->stuff;
+ if (label) {
+ tmp->label = p;
+ strcpy(p, label);
+ p += strlen(label) + 1;
+ }
+ tmp->exten = p;
+ p += ext_strncpy(p, extension, strlen(extension) + 1) + 1;
+ tmp->priority = priority;
+ tmp->cidmatch = p; /* but use p for assignments below */
+ if (callerid) {
+ p += ext_strncpy(p, callerid, strlen(callerid) + 1) + 1;
+ tmp->matchcid = 1;
+ } else {
+ *p++ = '\0';
+ tmp->matchcid = 0;
+ }
+ tmp->app = p;
+ strcpy(p, application);
+ tmp->parent = con;
+ tmp->data = data;
+ tmp->datad = datad;
+ tmp->registrar = registrar;
+
+ ast_wrlock_context(con);
+
+ if (con->pattern_tree) { /* usually, on initial load, the pattern_tree isn't formed until the first find_exten; so if we are adding
+ an extension, and the trie exists, then we need to incrementally add this pattern to it. */
+ strncpy(dummy_name,extension,sizeof(dummy_name));
+ dummy_exten.exten = dummy_name;
+ dummy_exten.matchcid = 0;
+ dummy_exten.cidmatch = 0;
+ tmp2 = ast_hashtab_lookup(con->root_tree,&dummy_exten);
+ if (!tmp2) {
+ /* hmmm, not in the trie; */
+ add_exten_to_pattern_tree(con, tmp, 0);
+ ast_hashtab_insert_safe(con->root_tree, tmp); /* for the sake of completeness */
+ }
+ }
+ res = 0; /* some compilers will think it is uninitialized otherwise */
+ for (e = con->root; e; el = e, e = e->next) { /* scan the extension list */
+ res = ext_cmp(e->exten, extension);
+ if (res == 0) { /* extension match, now look at cidmatch */
+ if (!e->matchcid && !tmp->matchcid)
+ res = 0;
+ else if (tmp->matchcid && !e->matchcid)
+ res = 1;
+ else if (e->matchcid && !tmp->matchcid)
+ res = -1;
+ else
+ res = strcasecmp(e->cidmatch, tmp->cidmatch);
+ }
+ if (res >= 0)
+ break;
+ }
+ if (e && res == 0) { /* exact match, insert in the pri chain */
+ res = add_pri(con, tmp, el, e, replace);
+ ast_unlock_context(con);
+ if (res < 0) {
+ errno = EEXIST; /* XXX do we care ? */
+ return 0; /* XXX should we return -1 maybe ? */
+ }
+ } else {
+ /*
+ * not an exact match, this is the first entry with this pattern,
+ * so insert in the main list right before 'e' (if any)
+ */
+ tmp->next = e;
+ if (el) { /* there is another exten already in this context */
+ el->next = tmp;
+ tmp->peer_tree = ast_hashtab_create(13,
+ hashtab_compare_exten_numbers,
+ ast_hashtab_resize_java,
+ ast_hashtab_newsize_java,
+ hashtab_hash_priority,
+ 0);
+ tmp->peer_label_tree = ast_hashtab_create(7,
+ hashtab_compare_exten_labels,
+ ast_hashtab_resize_java,
+ ast_hashtab_newsize_java,
+ hashtab_hash_labels,
+ 0);
+ if (label)
+ ast_hashtab_insert_safe(tmp->peer_label_tree,tmp);
+ ast_hashtab_insert_safe(tmp->peer_tree, tmp);
+
+ } else { /* this is the first exten in this context */
+ if (!con->root_tree)
+ con->root_tree = ast_hashtab_create(27,
+ hashtab_compare_extens,
+ ast_hashtab_resize_java,
+ ast_hashtab_newsize_java,
+ hashtab_hash_extens,
+ 0);
+ con->root = tmp;
+ con->root->peer_tree = ast_hashtab_create(13,
+ hashtab_compare_exten_numbers,
+ ast_hashtab_resize_java,
+ ast_hashtab_newsize_java,
+ hashtab_hash_priority,
+ 0);
+ con->root->peer_label_tree = ast_hashtab_create(7,
+ hashtab_compare_exten_labels,
+ ast_hashtab_resize_java,
+ ast_hashtab_newsize_java,
+ hashtab_hash_labels,
+ 0);
+ if (label)
+ ast_hashtab_insert_safe(con->root->peer_label_tree,tmp);
+ ast_hashtab_insert_safe(con->root->peer_tree, tmp);
+ }
+ ast_hashtab_insert_safe(con->root_tree, tmp);
+ ast_unlock_context(con);
+ if (tmp->priority == PRIORITY_HINT)
+ ast_add_hint(tmp);
+ }
+ if (option_debug) {
+ if (tmp->matchcid) {
+ ast_debug(1, "Added extension '%s' priority %d (CID match '%s') to %s\n",
+ tmp->exten, tmp->priority, tmp->cidmatch, con->name);
+ } else {
+ ast_debug(1, "Added extension '%s' priority %d to %s\n",
+ tmp->exten, tmp->priority, con->name);
+ }
+ }
+
+ if (tmp->matchcid) {
+ ast_verb(3, "Added extension '%s' priority %d (CID match '%s')to %s\n",
+ tmp->exten, tmp->priority, tmp->cidmatch, con->name);
+ } else {
+ ast_verb(3, "Added extension '%s' priority %d to %s\n",
+ tmp->exten, tmp->priority, con->name);
+ }
+
+ return 0;
+}
+
+struct async_stat {
+ pthread_t p;
+ struct ast_channel *chan;
+ char context[AST_MAX_CONTEXT];
+ char exten[AST_MAX_EXTENSION];
+ int priority;
+ int timeout;
+ char app[AST_MAX_EXTENSION];
+ char appdata[1024];
+};
+
+static void *async_wait(void *data)
+{
+ struct async_stat *as = data;
+ struct ast_channel *chan = as->chan;
+ int timeout = as->timeout;
+ int res;
+ struct ast_frame *f;
+ struct ast_app *app;
+
+ while (timeout && (chan->_state != AST_STATE_UP)) {
+ res = ast_waitfor(chan, timeout);
+ if (res < 1)
+ break;
+ if (timeout > -1)
+ timeout = res;
+ f = ast_read(chan);
+ if (!f)
+ break;
+ if (f->frametype == AST_FRAME_CONTROL) {
+ if ((f->subclass == AST_CONTROL_BUSY) ||
+ (f->subclass == AST_CONTROL_CONGESTION) ) {
+ ast_frfree(f);
+ break;
+ }
+ }
+ ast_frfree(f);
+ }
+ if (chan->_state == AST_STATE_UP) {
+ if (!ast_strlen_zero(as->app)) {
+ app = pbx_findapp(as->app);
+ if (app) {
+ ast_verb(3, "Launching %s(%s) on %s\n", as->app, as->appdata, chan->name);
+ pbx_exec(chan, app, as->appdata);
+ } else
+ ast_log(LOG_WARNING, "No such application '%s'\n", as->app);
+ } else {
+ if (!ast_strlen_zero(as->context))
+ ast_copy_string(chan->context, as->context, sizeof(chan->context));
+ if (!ast_strlen_zero(as->exten))
+ ast_copy_string(chan->exten, as->exten, sizeof(chan->exten));
+ if (as->priority > 0)
+ chan->priority = as->priority;
+ /* Run the PBX */
+ if (ast_pbx_run(chan)) {
+ ast_log(LOG_ERROR, "Failed to start PBX on %s\n", chan->name);
+ } else {
+ /* PBX will have taken care of this */
+ chan = NULL;
+ }
+ }
+ }
+ ast_free(as);
+ if (chan)
+ ast_hangup(chan);
+ return NULL;
+}
+
+/*!
+ * \brief Function to post an empty cdr after a spool call fails.
+ * \note This function posts an empty cdr for a failed spool call
+*/
+static int ast_pbx_outgoing_cdr_failed(void)
+{
+ /* allocate a channel */
+ struct ast_channel *chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0);
+
+ if (!chan)
+ return -1; /* failure */
+
+ if (!chan->cdr) {
+ /* allocation of the cdr failed */
+ ast_channel_free(chan); /* free the channel */
+ return -1; /* return failure */
+ }
+
+ /* allocation of the cdr was successful */
+ ast_cdr_init(chan->cdr, chan); /* initialize our channel's cdr */
+ ast_cdr_start(chan->cdr); /* record the start and stop time */
+ ast_cdr_end(chan->cdr);
+ ast_cdr_failed(chan->cdr); /* set the status to failed */
+ ast_cdr_detach(chan->cdr); /* post and free the record */
+ ast_channel_free(chan); /* free the channel */
+
+ return 0; /* success */
+}
+
+int ast_pbx_outgoing_exten(const char *type, int format, void *data, int timeout, const char *context, const char *exten, int priority, int *reason, int sync, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **channel)
+{
+ struct ast_channel *chan;
+ struct async_stat *as;
+ int res = -1, cdr_res = -1;
+ struct outgoing_helper oh;
+
+ if (sync) {
+ oh.context = context;
+ oh.exten = exten;
+ oh.priority = priority;
+ oh.cid_num = cid_num;
+ oh.cid_name = cid_name;
+ oh.account = account;
+ oh.vars = vars;
+ oh.parent_channel = NULL;
+
+ chan = __ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name, &oh);
+ if (channel) {
+ *channel = chan;
+ if (chan)
+ ast_channel_lock(chan);
+ }
+ if (chan) {
+ if (chan->_state == AST_STATE_UP) {
+ res = 0;
+ ast_verb(4, "Channel %s was answered.\n", chan->name);
+
+ if (sync > 1) {
+ if (channel)
+ ast_channel_unlock(chan);
+ if (ast_pbx_run(chan)) {
+ ast_log(LOG_ERROR, "Unable to run PBX on %s\n", chan->name);
+ if (channel)
+ *channel = NULL;
+ ast_hangup(chan);
+ res = -1;
+ }
+ } else {
+ if (ast_pbx_start(chan)) {
+ ast_log(LOG_ERROR, "Unable to start PBX on %s\n", chan->name);
+ if (channel) {
+ *channel = NULL;
+ ast_channel_unlock(chan);
+ }
+ ast_hangup(chan);
+ res = -1;
+ }
+ }
+ } else {
+ ast_verb(4, "Channel %s was never answered.\n", chan->name);
+
+ if (chan->cdr) { /* update the cdr */
+ /* here we update the status of the call, which sould be busy.
+ * if that fails then we set the status to failed */
+ if (ast_cdr_disposition(chan->cdr, chan->hangupcause))
+ ast_cdr_failed(chan->cdr);
+ }
+
+ if (channel) {
+ *channel = NULL;
+ ast_channel_unlock(chan);
+ }
+ ast_hangup(chan);
+ }
+ }
+
+ if (res < 0) { /* the call failed for some reason */
+ if (*reason == 0) { /* if the call failed (not busy or no answer)
+ * update the cdr with the failed message */
+ cdr_res = ast_pbx_outgoing_cdr_failed();
+ if (cdr_res != 0) {
+ res = cdr_res;
+ goto outgoing_exten_cleanup;
+ }
+ }
+
+ /* create a fake channel and execute the "failed" extension (if it exists) within the requested context */
+ /* check if "failed" exists */
+ if (ast_exists_extension(chan, context, "failed", 1, NULL)) {
+ chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "OutgoingSpoolFailed");
+ if (chan) {
+ char failed_reason[4] = "";
+ if (!ast_strlen_zero(context))
+ ast_copy_string(chan->context, context, sizeof(chan->context));
+ set_ext_pri(chan, "failed", 1);
+ ast_set_variables(chan, vars);
+ snprintf(failed_reason, sizeof(failed_reason), "%d", *reason);
+ pbx_builtin_setvar_helper(chan, "REASON", failed_reason);
+ if (account)
+ ast_cdr_setaccount(chan, account);
+ ast_pbx_run(chan);
+ }
+ }
+ }
+ } else {
+ if (!(as = ast_calloc(1, sizeof(*as)))) {
+ res = -1;
+ goto outgoing_exten_cleanup;
+ }
+ chan = ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name);
+ if (channel) {
+ *channel = chan;
+ if (chan)
+ ast_channel_lock(chan);
+ }
+ if (!chan) {
+ ast_free(as);
+ res = -1;
+ goto outgoing_exten_cleanup;
+ }
+ as->chan = chan;
+ ast_copy_string(as->context, context, sizeof(as->context));
+ set_ext_pri(as->chan, exten, priority);
+ as->timeout = timeout;
+ ast_set_variables(chan, vars);
+ if (account)
+ ast_cdr_setaccount(chan, account);
+ if (ast_pthread_create_detached(&as->p, NULL, async_wait, as)) {
+ ast_log(LOG_WARNING, "Failed to start async wait\n");
+ ast_free(as);
+ if (channel) {
+ *channel = NULL;
+ ast_channel_unlock(chan);
+ }
+ ast_hangup(chan);
+ res = -1;
+ goto outgoing_exten_cleanup;
+ }
+ res = 0;
+ }
+outgoing_exten_cleanup:
+ ast_variables_destroy(vars);
+ return res;
+}
+
+struct app_tmp {
+ char app[256];
+ char data[256];
+ struct ast_channel *chan;
+ pthread_t t;
+};
+
+/*! \brief run the application and free the descriptor once done */
+static void *ast_pbx_run_app(void *data)
+{
+ struct app_tmp *tmp = data;
+ struct ast_app *app;
+ app = pbx_findapp(tmp->app);
+ if (app) {
+ ast_verb(4, "Launching %s(%s) on %s\n", tmp->app, tmp->data, tmp->chan->name);
+ pbx_exec(tmp->chan, app, tmp->data);
+ } else
+ ast_log(LOG_WARNING, "No such application '%s'\n", tmp->app);
+ ast_hangup(tmp->chan);
+ ast_free(tmp);
+ return NULL;
+}
+
+int ast_pbx_outgoing_app(const char *type, int format, void *data, int timeout, const char *app, const char *appdata, int *reason, int sync, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **locked_channel)
+{
+ struct ast_channel *chan;
+ struct app_tmp *tmp;
+ int res = -1, cdr_res = -1;
+ struct outgoing_helper oh;
+
+ memset(&oh, 0, sizeof(oh));
+ oh.vars = vars;
+ oh.account = account;
+
+ if (locked_channel)
+ *locked_channel = NULL;
+ if (ast_strlen_zero(app)) {
+ res = -1;
+ goto outgoing_app_cleanup;
+ }
+ if (sync) {
+ chan = __ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name, &oh);
+ if (chan) {
+ if (chan->cdr) { /* check if the channel already has a cdr record, if not give it one */
+ ast_log(LOG_WARNING, "%s already has a call record??\n", chan->name);
+ } else {
+ chan->cdr = ast_cdr_alloc(); /* allocate a cdr for the channel */
+ if (!chan->cdr) {
+ /* allocation of the cdr failed */
+ ast_free(chan->pbx);
+ res = -1;
+ goto outgoing_app_cleanup;
+ }
+ /* allocation of the cdr was successful */
+ ast_cdr_init(chan->cdr, chan); /* initialize our channel's cdr */
+ ast_cdr_start(chan->cdr);
+ }
+ ast_set_variables(chan, vars);
+ if (account)
+ ast_cdr_setaccount(chan, account);
+ if (chan->_state == AST_STATE_UP) {
+ res = 0;
+ ast_verb(4, "Channel %s was answered.\n", chan->name);
+ tmp = ast_calloc(1, sizeof(*tmp));
+ if (!tmp)
+ res = -1;
+ else {
+ ast_copy_string(tmp->app, app, sizeof(tmp->app));
+ if (appdata)
+ ast_copy_string(tmp->data, appdata, sizeof(tmp->data));
+ tmp->chan = chan;
+ if (sync > 1) {
+ if (locked_channel)
+ ast_channel_unlock(chan);
+ ast_pbx_run_app(tmp);
+ } else {
+ if (locked_channel)
+ ast_channel_lock(chan);
+ if (ast_pthread_create_detached(&tmp->t, NULL, ast_pbx_run_app, tmp)) {
+ ast_log(LOG_WARNING, "Unable to spawn execute thread on %s: %s\n", chan->name, strerror(errno));
+ ast_free(tmp);
+ if (locked_channel)
+ ast_channel_unlock(chan);
+ ast_hangup(chan);
+ res = -1;
+ } else {
+ if (locked_channel)
+ *locked_channel = chan;
+ }
+ }
+ }
+ } else {
+ ast_verb(4, "Channel %s was never answered.\n", chan->name);
+ if (chan->cdr) { /* update the cdr */
+ /* here we update the status of the call, which sould be busy.
+ * if that fails then we set the status to failed */
+ if (ast_cdr_disposition(chan->cdr, chan->hangupcause))
+ ast_cdr_failed(chan->cdr);
+ }
+ ast_hangup(chan);
+ }
+ }
+
+ if (res < 0) { /* the call failed for some reason */
+ if (*reason == 0) { /* if the call failed (not busy or no answer)
+ * update the cdr with the failed message */
+ cdr_res = ast_pbx_outgoing_cdr_failed();
+ if (cdr_res != 0) {
+ res = cdr_res;
+ goto outgoing_app_cleanup;
+ }
+ }
+ }
+
+ } else {
+ struct async_stat *as;
+ if (!(as = ast_calloc(1, sizeof(*as)))) {
+ res = -1;
+ goto outgoing_app_cleanup;
+ }
+ chan = __ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name, &oh);
+ if (!chan) {
+ ast_free(as);
+ res = -1;
+ goto outgoing_app_cleanup;
+ }
+ as->chan = chan;
+ ast_copy_string(as->app, app, sizeof(as->app));
+ if (appdata)
+ ast_copy_string(as->appdata, appdata, sizeof(as->appdata));
+ as->timeout = timeout;
+ ast_set_variables(chan, vars);
+ if (account)
+ ast_cdr_setaccount(chan, account);
+ /* Start a new thread, and get something handling this channel. */
+ if (locked_channel)
+ ast_channel_lock(chan);
+ if (ast_pthread_create_detached(&as->p, NULL, async_wait, as)) {
+ ast_log(LOG_WARNING, "Failed to start async wait\n");
+ ast_free(as);
+ if (locked_channel)
+ ast_channel_unlock(chan);
+ ast_hangup(chan);
+ res = -1;
+ goto outgoing_app_cleanup;
+ } else {
+ if (locked_channel)
+ *locked_channel = chan;
+ }
+ res = 0;
+ }
+outgoing_app_cleanup:
+ ast_variables_destroy(vars);
+ return res;
+}
+
+void __ast_context_destroy(struct ast_context *con, const char *registrar)
+{
+ struct ast_context *tmp, *tmpl=NULL;
+ struct ast_include *tmpi;
+ struct ast_sw *sw;
+ struct ast_exten *e, *el, *en;
+ struct ast_ignorepat *ipi;
+
+ for (tmp = contexts; tmp; ) {
+ struct ast_context *next; /* next starting point */
+ for (; tmp; tmpl = tmp, tmp = tmp->next) {
+ ast_debug(1, "check ctx %s %s\n", tmp->name, tmp->registrar);
+ if ( (!registrar || !strcasecmp(registrar, tmp->registrar)) &&
+ (!con || !strcasecmp(tmp->name, con->name)) )
+ break; /* found it */
+ }
+ if (!tmp) /* not found, we are done */
+ break;
+ ast_wrlock_context(tmp);
+ ast_debug(1, "delete ctx %s %s\n", tmp->name, tmp->registrar);
+ ast_hashtab_remove_this_object(contexts_tree,tmp);
+
+ next = tmp->next;
+ if (tmpl)
+ tmpl->next = next;
+ else
+ contexts = next;
+ /* Okay, now we're safe to let it go -- in a sense, we were
+ ready to let it go as soon as we locked it. */
+ ast_unlock_context(tmp);
+ for (tmpi = tmp->includes; tmpi; ) { /* Free includes */
+ struct ast_include *tmpil = tmpi;
+ tmpi = tmpi->next;
+ ast_free(tmpil);
+ }
+ for (ipi = tmp->ignorepats; ipi; ) { /* Free ignorepats */
+ struct ast_ignorepat *ipl = ipi;
+ ipi = ipi->next;
+ ast_free(ipl);
+ }
+ /* destroy the hash tabs */
+ if (tmp->root_tree) {
+ ast_hashtab_destroy(tmp->root_tree, 0);
+ }
+ /* and destroy the pattern tree */
+ if (tmp->pattern_tree)
+ destroy_pattern_tree(tmp->pattern_tree);
+
+ while ((sw = AST_LIST_REMOVE_HEAD(&tmp->alts, list)))
+ ast_free(sw);
+ for (e = tmp->root; e;) {
+ for (en = e->peer; en;) {
+ el = en;
+ en = en->peer;
+ destroy_exten(el);
+ }
+ el = e;
+ e = e->next;
+ destroy_exten(el);
+ }
+ ast_rwlock_destroy(&tmp->lock);
+ ast_free(tmp);
+ /* if we have a specific match, we are done, otherwise continue */
+ tmp = con ? NULL : next;
+ }
+}
+
+void ast_context_destroy(struct ast_context *con, const char *registrar)
+{
+ ast_wrlock_contexts();
+ __ast_context_destroy(con,registrar);
+ ast_unlock_contexts();
+}
+
+static void wait_for_hangup(struct ast_channel *chan, void *data)
+{
+ int res;
+ struct ast_frame *f;
+ double waitsec;
+ int waittime;
+
+ if (ast_strlen_zero(data) || (sscanf(data, "%lg", &waitsec) != 1) || (waitsec < 0))
+ waitsec = -1;
+ if (waitsec > -1) {
+ waittime = waitsec * 1000.0;
+ ast_safe_sleep(chan, waittime);
+ } else do {
+ res = ast_waitfor(chan, -1);
+ if (res < 0)
+ return;
+ f = ast_read(chan);
+ if (f)
+ ast_frfree(f);
+ } while(f);
+}
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_progress(struct ast_channel *chan, void *data)
+{
+ ast_indicate(chan, AST_CONTROL_PROGRESS);
+ return 0;
+}
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_ringing(struct ast_channel *chan, void *data)
+{
+ ast_indicate(chan, AST_CONTROL_RINGING);
+ return 0;
+}
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_busy(struct ast_channel *chan, void *data)
+{
+ ast_indicate(chan, AST_CONTROL_BUSY);
+ /* Don't change state of an UP channel, just indicate
+ busy in audio */
+ if (chan->_state != AST_STATE_UP)
+ ast_setstate(chan, AST_STATE_BUSY);
+ wait_for_hangup(chan, data);
+ return -1;
+}
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_congestion(struct ast_channel *chan, void *data)
+{
+ ast_indicate(chan, AST_CONTROL_CONGESTION);
+ /* Don't change state of an UP channel, just indicate
+ congestion in audio */
+ if (chan->_state != AST_STATE_UP)
+ ast_setstate(chan, AST_STATE_BUSY);
+ wait_for_hangup(chan, data);
+ return -1;
+}
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_answer(struct ast_channel *chan, void *data)
+{
+ int delay = 0;
+
+ if ((chan->_state != AST_STATE_UP) && !ast_strlen_zero(data))
+ delay = atoi(data);
+
+ return __ast_answer(chan, delay);
+}
+
+static int pbx_builtin_keepalive(struct ast_channel *chan, void *data)
+{
+ return AST_PBX_KEEPALIVE;
+}
+
+AST_APP_OPTIONS(resetcdr_opts, {
+ AST_APP_OPTION('w', AST_CDR_FLAG_POSTED),
+ AST_APP_OPTION('a', AST_CDR_FLAG_LOCKED),
+ AST_APP_OPTION('v', AST_CDR_FLAG_KEEP_VARS),
+});
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_resetcdr(struct ast_channel *chan, void *data)
+{
+ char *args;
+ struct ast_flags flags = { 0 };
+
+ if (!ast_strlen_zero(data)) {
+ args = ast_strdupa(data);
+ ast_app_parse_options(resetcdr_opts, &flags, NULL, args);
+ }
+
+ ast_cdr_reset(chan->cdr, &flags);
+
+ return 0;
+}
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_setamaflags(struct ast_channel *chan, void *data)
+{
+ /* Copy the AMA Flags as specified */
+ ast_cdr_setamaflags(chan, data ? data : "");
+ return 0;
+}
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_hangup(struct ast_channel *chan, void *data)
+{
+ if (!ast_strlen_zero(data)) {
+ int cause;
+ char *endptr;
+
+ if ((cause = ast_str2cause(data)) > -1) {
+ chan->hangupcause = cause;
+ return -1;
+ }
+
+ cause = strtol((const char *) data, &endptr, 10);
+ if (cause != 0 || (data != endptr)) {
+ chan->hangupcause = cause;
+ return -1;
+ }
+
+ ast_log(LOG_NOTICE, "Invalid cause given to Hangup(): \"%s\"\n", (char *) data);
+ }
+
+ if (!chan->hangupcause) {
+ chan->hangupcause = AST_CAUSE_NORMAL_CLEARING;
+ }
+
+ return -1;
+}
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_gotoiftime(struct ast_channel *chan, void *data)
+{
+ int res=0;
+ char *s, *ts;
+ struct ast_timing timing;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "GotoIfTime requires an argument:\n <time range>,<days of week>,<days of month>,<months>?[[context,]extension,]priority\n");
+ return -1;
+ }
+
+ ts = s = ast_strdupa(data);
+
+ /* Separate the Goto path */
+ strsep(&ts, "?");
+
+ /* struct ast_include include contained garbage here, fixed by zeroing it on get_timerange */
+ if (ast_build_timing(&timing, s) && ast_check_timing(&timing))
+ res = pbx_builtin_goto(chan, ts);
+
+ return res;
+}
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_execiftime(struct ast_channel *chan, void *data)
+{
+ char *s, *appname;
+ struct ast_timing timing;
+ struct ast_app *app;
+ static const char *usage = "ExecIfTime requires an argument:\n <time range>,<days of week>,<days of month>,<months>?<appname>[(<appargs>)]";
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "%s\n", usage);
+ return -1;
+ }
+
+ appname = ast_strdupa(data);
+
+ s = strsep(&appname, "?"); /* Separate the timerange and application name/data */
+ if (!appname) { /* missing application */
+ ast_log(LOG_WARNING, "%s\n", usage);
+ return -1;
+ }
+
+ if (!ast_build_timing(&timing, s)) {
+ ast_log(LOG_WARNING, "Invalid Time Spec: %s\nCorrect usage: %s\n", s, usage);
+ return -1;
+ }
+
+ if (!ast_check_timing(&timing)) /* outside the valid time window, just return */
+ return 0;
+
+ /* now split appname(appargs) */
+ if ((s = strchr(appname, '('))) {
+ char *e;
+ *s++ = '\0';
+ if ((e = strrchr(s, ')')))
+ *e = '\0';
+ else
+ ast_log(LOG_WARNING, "Failed to find closing parenthesis\n");
+ }
+
+
+ if ((app = pbx_findapp(appname))) {
+ return pbx_exec(chan, app, S_OR(s, ""));
+ } else {
+ ast_log(LOG_WARNING, "Cannot locate application %s\n", appname);
+ return -1;
+ }
+}
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_wait(struct ast_channel *chan, void *data)
+{
+ double s;
+ int ms;
+
+ /* Wait for "n" seconds */
+ if (data && (s = atof(data)) > 0.0) {
+ ms = s * 1000.0;
+ return ast_safe_sleep(chan, ms);
+ }
+ return 0;
+}
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_waitexten(struct ast_channel *chan, void *data)
+{
+ int ms, res;
+ double s;
+ struct ast_flags flags = {0};
+ char *opts[1] = { NULL };
+ char *parse;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(timeout);
+ AST_APP_ARG(options);
+ );
+
+ if (!ast_strlen_zero(data)) {
+ parse = ast_strdupa(data);
+ AST_STANDARD_APP_ARGS(args, parse);
+ } else
+ memset(&args, 0, sizeof(args));
+
+ if (args.options)
+ ast_app_parse_options(waitexten_opts, &flags, opts, args.options);
+
+ if (ast_test_flag(&flags, WAITEXTEN_MOH) && !opts[0] ) {
+ ast_log(LOG_WARNING, "The 'm' option has been specified for WaitExten without a class.\n");
+ } else if (ast_test_flag(&flags, WAITEXTEN_MOH)) {
+ ast_indicate_data(chan, AST_CONTROL_HOLD, opts[0], strlen(opts[0]));
+ } else if (ast_test_flag(&flags, WAITEXTEN_DIALTONE)) {
+ const struct ind_tone_zone_sound *ts = ast_get_indication_tone(chan->zone, "dial");
+ if (ts)
+ ast_playtones_start(chan, 0, ts->data, 0);
+ else
+ ast_tonepair_start(chan, 350, 440, 0, 0);
+ }
+ /* Wait for "n" seconds */
+ if (args.timeout && (s = atof(args.timeout)) > 0)
+ ms = s * 1000.0;
+ else if (chan->pbx)
+ ms = chan->pbx->rtimeout * 1000;
+ else
+ ms = 10000;
+
+ res = ast_waitfordigit(chan, ms);
+ if (!res) {
+ if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 1, chan->cid.cid_num)) {
+ ast_verb(3, "Timeout on %s, continuing...\n", chan->name);
+ } else if (ast_exists_extension(chan, chan->context, "t", 1, chan->cid.cid_num)) {
+ ast_verb(3, "Timeout on %s, going to 't'\n", chan->name);
+ set_ext_pri(chan, "t", 0); /* XXX is the 0 correct ? */
+ } else {
+ ast_log(LOG_WARNING, "Timeout but no rule 't' in context '%s'\n", chan->context);
+ res = -1;
+ }
+ }
+
+ if (ast_test_flag(&flags, WAITEXTEN_MOH))
+ ast_indicate(chan, AST_CONTROL_UNHOLD);
+ else if (ast_test_flag(&flags, WAITEXTEN_DIALTONE))
+ ast_playtones_stop(chan);
+
+ return res;
+}
+
+/*!
+ * \ingroup applications
+ */
+static int pbx_builtin_background(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+ int mres = 0;
+ struct ast_flags flags = {0};
+ char *parse;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(filename);
+ AST_APP_ARG(options);
+ AST_APP_ARG(lang);
+ AST_APP_ARG(context);
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Background requires an argument (filename)\n");
+ return -1;
+ }
+
+ parse = ast_strdupa(data);
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (ast_strlen_zero(args.lang))
+ args.lang = (char *)chan->language; /* XXX this is const */
+
+ if (ast_strlen_zero(args.context))
+ args.context = chan->context;
+
+ if (args.options) {
+ if (!strcasecmp(args.options, "skip"))
+ flags.flags = BACKGROUND_SKIP;
+ else if (!strcasecmp(args.options, "noanswer"))
+ flags.flags = BACKGROUND_NOANSWER;
+ else
+ ast_app_parse_options(background_opts, &flags, NULL, args.options);
+ }
+
+ /* Answer if need be */
+ if (chan->_state != AST_STATE_UP) {
+ if (ast_test_flag(&flags, BACKGROUND_SKIP)) {
+ goto done;
+ } else if (!ast_test_flag(&flags, BACKGROUND_NOANSWER)) {
+ res = ast_answer(chan);
+ }
+ }
+
+ if (!res) {
+ char *back = args.filename;
+ char *front;
+
+ ast_stopstream(chan); /* Stop anything playing */
+ /* Stream the list of files */
+ while (!res && (front = strsep(&back, "&")) ) {
+ if ( (res = ast_streamfile(chan, front, args.lang)) ) {
+ ast_log(LOG_WARNING, "ast_streamfile failed on %s for %s\n", chan->name, (char*)data);
+ res = 0;
+ mres = 1;
+ break;
+ }
+ if (ast_test_flag(&flags, BACKGROUND_PLAYBACK)) {
+ res = ast_waitstream(chan, "");
+ } else if (ast_test_flag(&flags, BACKGROUND_MATCHEXTEN)) {
+ res = ast_waitstream_exten(chan, args.context);
+ } else {
+ res = ast_waitstream(chan, AST_DIGIT_ANY);
+ }
+ ast_stopstream(chan);
+ }
+ }
+ if (args.context != chan->context && res) {
+ snprintf(chan->exten, sizeof(chan->exten), "%c", res);
+ ast_copy_string(chan->context, args.context, sizeof(chan->context));
+ chan->priority = 0;
+ res = 0;
+ }
+done:
+ pbx_builtin_setvar_helper(chan, "BACKGROUNDSTATUS", mres ? "FAILED" : "SUCCESS");
+ return res;
+}
+
+/*! Goto
+ * \ingroup applications
+ */
+static int pbx_builtin_goto(struct ast_channel *chan, void *data)
+{
+ int res = ast_parseable_goto(chan, data);
+ if (!res)
+ ast_verb(3, "Goto (%s,%s,%d)\n", chan->context, chan->exten, chan->priority + 1);
+ return res;
+}
+
+
+int pbx_builtin_serialize_variables(struct ast_channel *chan, struct ast_str **buf)
+{
+ struct ast_var_t *variables;
+ const char *var, *val;
+ int total = 0;
+
+ if (!chan)
+ return 0;
+
+ (*buf)->used = 0;
+ (*buf)->str[0] = '\0';
+
+ ast_channel_lock(chan);
+
+ AST_LIST_TRAVERSE(&chan->varshead, variables, entries) {
+ if ((var = ast_var_name(variables)) && (val = ast_var_value(variables))
+ /* && !ast_strlen_zero(var) && !ast_strlen_zero(val) */
+ ) {
+ if (ast_str_append(buf, 0, "%s=%s\n", var, val) < 0) {
+ ast_log(LOG_ERROR, "Data Buffer Size Exceeded!\n");
+ break;
+ } else
+ total++;
+ } else
+ break;
+ }
+
+ ast_channel_unlock(chan);
+
+ return total;
+}
+
+const char *pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
+{
+ struct ast_var_t *variables;
+ const char *ret = NULL;
+ int i;
+ struct varshead *places[2] = { NULL, &globals };
+
+ if (!name)
+ return NULL;
+
+ if (chan) {
+ ast_channel_lock(chan);
+ places[0] = &chan->varshead;
+ }
+
+ for (i = 0; i < 2; i++) {
+ if (!places[i])
+ continue;
+ if (places[i] == &globals)
+ ast_rwlock_rdlock(&globalslock);
+ AST_LIST_TRAVERSE(places[i], variables, entries) {
+ if (!strcmp(name, ast_var_name(variables))) {
+ ret = ast_var_value(variables);
+ break;
+ }
+ }
+ if (places[i] == &globals)
+ ast_rwlock_unlock(&globalslock);
+ if (ret)
+ break;
+ }
+
+ if (chan)
+ ast_channel_unlock(chan);
+
+ return ret;
+}
+
+void pbx_builtin_pushvar_helper(struct ast_channel *chan, const char *name, const char *value)
+{
+ struct ast_var_t *newvariable;
+ struct varshead *headp;
+
+ if (name[strlen(name)-1] == ')') {
+ char *function = ast_strdupa(name);
+
+ ast_log(LOG_WARNING, "Cannot push a value onto a function\n");
+ ast_func_write(chan, function, value);
+ return;
+ }
+
+ if (chan) {
+ ast_channel_lock(chan);
+ headp = &chan->varshead;
+ } else {
+ ast_rwlock_wrlock(&globalslock);
+ headp = &globals;
+ }
+
+ if (value) {
+ if (headp == &globals)
+ ast_verb(2, "Setting global variable '%s' to '%s'\n", name, value);
+ newvariable = ast_var_assign(name, value);
+ AST_LIST_INSERT_HEAD(headp, newvariable, entries);
+ }
+
+ if (chan)
+ ast_channel_unlock(chan);
+ else
+ ast_rwlock_unlock(&globalslock);
+}
+
+void pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
+{
+ struct ast_var_t *newvariable;
+ struct varshead *headp;
+ const char *nametail = name;
+
+ if (name[strlen(name) - 1] == ')') {
+ char *function = ast_strdupa(name);
+
+ ast_func_write(chan, function, value);
+ return;
+ }
+
+ if (chan) {
+ ast_channel_lock(chan);
+ headp = &chan->varshead;
+ } else {
+ ast_rwlock_wrlock(&globalslock);
+ headp = &globals;
+ }
+
+ /* For comparison purposes, we have to strip leading underscores */
+ if (*nametail == '_') {
+ nametail++;
+ if (*nametail == '_')
+ nametail++;
+ }
+
+ AST_LIST_TRAVERSE (headp, newvariable, entries) {
+ if (strcasecmp(ast_var_name(newvariable), nametail) == 0) {
+ /* there is already such a variable, delete it */
+ AST_LIST_REMOVE(headp, newvariable, entries);
+ ast_var_delete(newvariable);
+ break;
+ }
+ }
+
+ if (value) {
+ if (headp == &globals)
+ ast_verb(2, "Setting global variable '%s' to '%s'\n", name, value);
+ newvariable = ast_var_assign(name, value);
+ AST_LIST_INSERT_HEAD(headp, newvariable, entries);
+ manager_event(EVENT_FLAG_DIALPLAN, "VarSet",
+ "Channel: %s\r\n"
+ "Variable: %s\r\n"
+ "Value: %s\r\n"
+ "Uniqueid: %s\r\n",
+ chan ? chan->name : "none", name, value,
+ chan ? chan->uniqueid : "none");
+ }
+
+ if (chan)
+ ast_channel_unlock(chan);
+ else
+ ast_rwlock_unlock(&globalslock);
+}
+
+int pbx_builtin_setvar(struct ast_channel *chan, void *data)
+{
+ char *name, *value, *mydata;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Set requires one variable name/value pair.\n");
+ return 0;
+ }
+
+ mydata = ast_strdupa(data);
+ name = strsep(&mydata, "=");
+ value = mydata;
+ if (strchr(name, ' '))
+ ast_log(LOG_WARNING, "Please avoid unnecessary spaces on variables as it may lead to unexpected results ('%s' set to '%s').\n", name, mydata);
+
+ pbx_builtin_setvar_helper(chan, name, value);
+ return(0);
+}
+
+static int pbx_builtin_setvar_multiple(struct ast_channel *chan, void *vdata)
+{
+ char *data;
+ int x;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(pair)[24];
+ );
+ AST_DECLARE_APP_ARGS(pair,
+ AST_APP_ARG(name);
+ AST_APP_ARG(value);
+ );
+
+ if (ast_strlen_zero(vdata) || !chan) {
+ ast_log(LOG_WARNING, "MSet requires at least one variable name/value pair.\n");
+ return 0;
+ }
+
+ data = ast_strdupa(vdata);
+ AST_STANDARD_APP_ARGS(args, data);
+
+ for (x = 0; x < args.argc; x++) {
+ AST_NONSTANDARD_APP_ARGS(pair, args.pair[x], '=');
+ if (pair.argc == 2) {
+ pbx_builtin_setvar_helper(chan, pair.name, pair.value);
+ if (strchr(pair.name, ' '))
+ ast_log(LOG_WARNING, "Please avoid unnecessary spaces on variables as it may lead to unexpected results ('%s' set to '%s').\n", pair.name, pair.value);
+ } else {
+ ast_log(LOG_WARNING, "MSet: ignoring entry '%s' with no '=' (in %s@%s:%d\n", pair.name, chan->exten, chan->context, chan->priority);
+ }
+ }
+
+ return 0;
+}
+
+int pbx_builtin_importvar(struct ast_channel *chan, void *data)
+{
+ char *name;
+ char *value;
+ char *channel;
+ char tmp[VAR_BUF_SIZE];
+ static int deprecation_warning = 0;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Ignoring, since there is no variable to set\n");
+ return 0;
+ }
+ tmp[0] = 0;
+ if (!deprecation_warning) {
+ ast_log(LOG_WARNING, "ImportVar is deprecated. Please use Set(varname=${IMPORT(channel,variable)}) instead.\n");
+ deprecation_warning = 1;
+ }
+
+ value = ast_strdupa(data);
+ name = strsep(&value,"=");
+ channel = strsep(&value,",");
+ if (channel && value && name) { /*! \todo XXX should do !ast_strlen_zero(..) of the args ? */
+ struct ast_channel *chan2 = ast_get_channel_by_name_locked(channel);
+ if (chan2) {
+ char *s = alloca(strlen(value) + 4);
+ if (s) {
+ sprintf(s, "${%s}", value);
+ pbx_substitute_variables_helper(chan2, s, tmp, sizeof(tmp) - 1);
+ }
+ ast_channel_unlock(chan2);
+ }
+ pbx_builtin_setvar_helper(chan, name, tmp);
+ }
+
+ return(0);
+}
+
+static int pbx_builtin_noop(struct ast_channel *chan, void *data)
+{
+ return 0;
+}
+
+void pbx_builtin_clear_globals(void)
+{
+ struct ast_var_t *vardata;
+
+ ast_rwlock_wrlock(&globalslock);
+ while ((vardata = AST_LIST_REMOVE_HEAD(&globals, entries)))
+ ast_var_delete(vardata);
+ ast_rwlock_unlock(&globalslock);
+}
+
+int pbx_checkcondition(const char *condition)
+{
+ if (ast_strlen_zero(condition)) /* NULL or empty strings are false */
+ return 0;
+ else if (*condition >= '0' && *condition <= '9') /* Numbers are evaluated for truth */
+ return atoi(condition);
+ else /* Strings are true */
+ return 1;
+}
+
+static int pbx_builtin_gotoif(struct ast_channel *chan, void *data)
+{
+ char *condition, *branch1, *branch2, *branch;
+ char *stringp;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "Ignoring, since there is no variable to check\n");
+ return 0;
+ }
+
+ stringp = ast_strdupa(data);
+ condition = strsep(&stringp,"?");
+ branch1 = strsep(&stringp,":");
+ branch2 = strsep(&stringp,"");
+ branch = pbx_checkcondition(condition) ? branch1 : branch2;
+
+ if (ast_strlen_zero(branch)) {
+ ast_debug(1, "Not taking any branch\n");
+ return 0;
+ }
+
+ return pbx_builtin_goto(chan, branch);
+}
+
+static int pbx_builtin_saynumber(struct ast_channel *chan, void *data)
+{
+ char tmp[256];
+ char *number = tmp;
+ char *options;
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "SayNumber requires an argument (number)\n");
+ return -1;
+ }
+ ast_copy_string(tmp, data, sizeof(tmp));
+ strsep(&number, ",");
+ options = strsep(&number, ",");
+ if (options) {
+ if ( strcasecmp(options, "f") && strcasecmp(options, "m") &&
+ strcasecmp(options, "c") && strcasecmp(options, "n") ) {
+ ast_log(LOG_WARNING, "SayNumber gender option is either 'f', 'm', 'c' or 'n'\n");
+ return -1;
+ }
+ }
+ return ast_say_number(chan, atoi(tmp), "", chan->language, options);
+}
+
+static int pbx_builtin_saydigits(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+
+ if (data)
+ res = ast_say_digit_str(chan, data, "", chan->language);
+ return res;
+}
+
+static int pbx_builtin_saycharacters(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+
+ if (data)
+ res = ast_say_character_str(chan, data, "", chan->language);
+ return res;
+}
+
+static int pbx_builtin_sayphonetic(struct ast_channel *chan, void *data)
+{
+ int res = 0;
+
+ if (data)
+ res = ast_say_phonetic_str(chan, data, "", chan->language);
+ return res;
+}
+
+static void device_state_cb(const struct ast_event *event, void *unused)
+{
+ const char *device;
+
+ device = ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE);
+ if (ast_strlen_zero(device)) {
+ ast_log(LOG_ERROR, "Received invalid event that had no device IE\n");
+ return;
+ }
+
+ statechange_queue(device);
+}
+
+int load_pbx(void)
+{
+ int x;
+
+ /* Initialize the PBX */
+ ast_verb(1, "Asterisk PBX Core Initializing\n");
+ ast_verb(1, "Registering builtin applications:\n");
+
+ ast_cli_register_multiple(pbx_cli, sizeof(pbx_cli) / sizeof(struct ast_cli_entry));
+ __ast_custom_function_register(&exception_function, NULL);
+
+ /* Register builtin applications */
+ for (x=0; x<sizeof(builtins) / sizeof(struct pbx_builtin); x++) {
+ ast_verb(1, "[%s]\n", builtins[x].name);
+ if (ast_register_application2(builtins[x].name, builtins[x].execute, builtins[x].synopsis, builtins[x].description, NULL)) {
+ ast_log(LOG_ERROR, "Unable to register builtin application '%s'\n", builtins[x].name);
+ return -1;
+ }
+ }
+
+ /* Register manager application */
+ ast_manager_register2("ShowDialPlan", EVENT_FLAG_CONFIG | EVENT_FLAG_REPORTING, manager_show_dialplan, "List dialplan", mandescr_show_dialplan);
+
+ ast_mutex_init(&device_state.lock);
+ ast_cond_init(&device_state.cond, NULL);
+ ast_pthread_create(&device_state.thread, NULL, device_state_thread, NULL);
+
+ if (!(device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE, device_state_cb, NULL,
+ AST_EVENT_IE_END))) {
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Lock context list functions ...
+ */
+int ast_wrlock_contexts()
+{
+ return ast_rwlock_wrlock(&conlock);
+}
+
+int ast_rdlock_contexts()
+{
+ return ast_rwlock_rdlock(&conlock);
+}
+
+int ast_unlock_contexts()
+{
+ return ast_rwlock_unlock(&conlock);
+}
+
+/*
+ * Lock context ...
+ */
+int ast_wrlock_context(struct ast_context *con)
+{
+ return ast_rwlock_wrlock(&con->lock);
+}
+
+int ast_rdlock_context(struct ast_context *con)
+{
+ return ast_rwlock_rdlock(&con->lock);
+}
+
+int ast_unlock_context(struct ast_context *con)
+{
+ return ast_rwlock_unlock(&con->lock);
+}
+
+/*
+ * Name functions ...
+ */
+const char *ast_get_context_name(struct ast_context *con)
+{
+ return con ? con->name : NULL;
+}
+
+struct ast_context *ast_get_extension_context(struct ast_exten *exten)
+{
+ return exten ? exten->parent : NULL;
+}
+
+const char *ast_get_extension_name(struct ast_exten *exten)
+{
+ return exten ? exten->exten : NULL;
+}
+
+const char *ast_get_extension_label(struct ast_exten *exten)
+{
+ return exten ? exten->label : NULL;
+}
+
+const char *ast_get_include_name(struct ast_include *inc)
+{
+ return inc ? inc->name : NULL;
+}
+
+const char *ast_get_ignorepat_name(struct ast_ignorepat *ip)
+{
+ return ip ? ip->pattern : NULL;
+}
+
+int ast_get_extension_priority(struct ast_exten *exten)
+{
+ return exten ? exten->priority : -1;
+}
+
+/*
+ * Registrar info functions ...
+ */
+const char *ast_get_context_registrar(struct ast_context *c)
+{
+ return c ? c->registrar : NULL;
+}
+
+const char *ast_get_extension_registrar(struct ast_exten *e)
+{
+ return e ? e->registrar : NULL;
+}
+
+const char *ast_get_include_registrar(struct ast_include *i)
+{
+ return i ? i->registrar : NULL;
+}
+
+const char *ast_get_ignorepat_registrar(struct ast_ignorepat *ip)
+{
+ return ip ? ip->registrar : NULL;
+}
+
+int ast_get_extension_matchcid(struct ast_exten *e)
+{
+ return e ? e->matchcid : 0;
+}
+
+const char *ast_get_extension_cidmatch(struct ast_exten *e)
+{
+ return e ? e->cidmatch : NULL;
+}
+
+const char *ast_get_extension_app(struct ast_exten *e)
+{
+ return e ? e->app : NULL;
+}
+
+void *ast_get_extension_app_data(struct ast_exten *e)
+{
+ return e ? e->data : NULL;
+}
+
+const char *ast_get_switch_name(struct ast_sw *sw)
+{
+ return sw ? sw->name : NULL;
+}
+
+const char *ast_get_switch_data(struct ast_sw *sw)
+{
+ return sw ? sw->data : NULL;
+}
+
+const char *ast_get_switch_registrar(struct ast_sw *sw)
+{
+ return sw ? sw->registrar : NULL;
+}
+
+/*
+ * Walking functions ...
+ */
+struct ast_context *ast_walk_contexts(struct ast_context *con)
+{
+ return con ? con->next : contexts;
+}
+
+struct ast_exten *ast_walk_context_extensions(struct ast_context *con,
+ struct ast_exten *exten)
+{
+ if (!exten)
+ return con ? con->root : NULL;
+ else
+ return exten->next;
+}
+
+struct ast_sw *ast_walk_context_switches(struct ast_context *con,
+ struct ast_sw *sw)
+{
+ if (!sw)
+ return con ? AST_LIST_FIRST(&con->alts) : NULL;
+ else
+ return AST_LIST_NEXT(sw, list);
+}
+
+struct ast_exten *ast_walk_extension_priorities(struct ast_exten *exten,
+ struct ast_exten *priority)
+{
+ return priority ? priority->peer : exten;
+}
+
+struct ast_include *ast_walk_context_includes(struct ast_context *con,
+ struct ast_include *inc)
+{
+ if (!inc)
+ return con ? con->includes : NULL;
+ else
+ return inc->next;
+}
+
+struct ast_ignorepat *ast_walk_context_ignorepats(struct ast_context *con,
+ struct ast_ignorepat *ip)
+{
+ if (!ip)
+ return con ? con->ignorepats : NULL;
+ else
+ return ip->next;
+}
+
+int ast_context_verify_includes(struct ast_context *con)
+{
+ struct ast_include *inc = NULL;
+ int res = 0;
+
+ while ( (inc = ast_walk_context_includes(con, inc)) )
+ if (!ast_context_find(inc->rname)) {
+ res = -1;
+ ast_log(LOG_WARNING, "Context '%s' tries to include nonexistent context '%s'\n",
+ ast_get_context_name(con), inc->rname);
+ }
+ return res;
+}
+
+
+static int __ast_goto_if_exists(struct ast_channel *chan, const char *context, const char *exten, int priority, int async)
+{
+ int (*goto_func)(struct ast_channel *chan, const char *context, const char *exten, int priority);
+
+ if (!chan)
+ return -2;
+
+ if (context == NULL)
+ context = chan->context;
+ if (exten == NULL)
+ exten = chan->exten;
+
+ goto_func = (async) ? ast_async_goto : ast_explicit_goto;
+ if (ast_exists_extension(chan, context, exten, priority, chan->cid.cid_num))
+ return goto_func(chan, context, exten, priority);
+ else
+ return -3;
+}
+
+int ast_goto_if_exists(struct ast_channel *chan, const char* context, const char *exten, int priority)
+{
+ return __ast_goto_if_exists(chan, context, exten, priority, 0);
+}
+
+int ast_async_goto_if_exists(struct ast_channel *chan, const char * context, const char *exten, int priority)
+{
+ return __ast_goto_if_exists(chan, context, exten, priority, 1);
+}
+
+int ast_parseable_goto(struct ast_channel *chan, const char *goto_string)
+{
+ char *exten, *pri, *context;
+ char *stringp;
+ int ipri;
+ int mode = 0;
+
+ if (ast_strlen_zero(goto_string)) {
+ ast_log(LOG_WARNING, "Goto requires an argument ([[context,]extension,]priority)\n");
+ return -1;
+ }
+ stringp = ast_strdupa(goto_string);
+ context = strsep(&stringp, ","); /* guaranteed non-null */
+ exten = strsep(&stringp, ",");
+ pri = strsep(&stringp, ",");
+ if (!exten) { /* Only a priority in this one */
+ pri = context;
+ exten = NULL;
+ context = NULL;
+ } else if (!pri) { /* Only an extension and priority in this one */
+ pri = exten;
+ exten = context;
+ context = NULL;
+ }
+ if (*pri == '+') {
+ mode = 1;
+ pri++;
+ } else if (*pri == '-') {
+ mode = -1;
+ pri++;
+ }
+ if (sscanf(pri, "%d", &ipri) != 1) {
+ if ((ipri = ast_findlabel_extension(chan, context ? context : chan->context, exten ? exten : chan->exten,
+ pri, chan->cid.cid_num)) < 1) {
+ ast_log(LOG_WARNING, "Priority '%s' must be a number > 0, or valid label\n", pri);
+ return -1;
+ } else
+ mode = 0;
+ }
+ /* At this point we have a priority and maybe an extension and a context */
+
+ if (mode)
+ ipri = chan->priority + (ipri * mode);
+
+ ast_explicit_goto(chan, context, exten, ipri);
+ ast_cdr_update(chan);
+ return 0;
+
+}
diff --git a/trunk/main/plc.c b/trunk/main/plc.c
new file mode 100644
index 000000000..ef549ca2c
--- /dev/null
+++ b/trunk/main/plc.c
@@ -0,0 +1,248 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2004 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * This version may be optionally licenced under the GNU LGPL licence.
+ *
+ * A license has been granted to Digium (via disclaimer) for the use of
+ * this code.
+ */
+
+/*! \file
+ *
+ * \brief SpanDSP - a series of DSP components for telephony
+ *
+ * \author Steve Underwood <steveu@coppice.org>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <math.h>
+
+#include "asterisk/plc.h"
+
+#if !defined(FALSE)
+#define FALSE 0
+#endif
+#if !defined(TRUE)
+#define TRUE (!FALSE)
+#endif
+
+#if !defined(INT16_MAX)
+#define INT16_MAX (32767)
+#define INT16_MIN (-32767-1)
+#endif
+
+/* We do a straight line fade to zero volume in 50ms when we are filling in for missing data. */
+#define ATTENUATION_INCREMENT 0.0025 /* Attenuation per sample */
+
+#define ms_to_samples(t) (((t)*DEFAULT_SAMPLE_RATE)/1000)
+
+static inline int16_t fsaturate(double damp)
+{
+ if (damp > 32767.0)
+ return INT16_MAX;
+ if (damp < -32768.0)
+ return INT16_MIN;
+ return (int16_t) rint(damp);
+}
+
+static void save_history(plc_state_t *s, int16_t *buf, int len)
+{
+ if (len >= PLC_HISTORY_LEN) {
+ /* Just keep the last part of the new data, starting at the beginning of the buffer */
+ memcpy(s->history, buf + len - PLC_HISTORY_LEN, sizeof(int16_t) * PLC_HISTORY_LEN);
+ s->buf_ptr = 0;
+ return;
+ }
+ if (s->buf_ptr + len > PLC_HISTORY_LEN) {
+ /* Wraps around - must break into two sections */
+ memcpy(s->history + s->buf_ptr, buf, sizeof(int16_t) * (PLC_HISTORY_LEN - s->buf_ptr));
+ len -= (PLC_HISTORY_LEN - s->buf_ptr);
+ memcpy(s->history, buf + (PLC_HISTORY_LEN - s->buf_ptr), sizeof(int16_t)*len);
+ s->buf_ptr = len;
+ return;
+ }
+ /* Can use just one section */
+ memcpy(s->history + s->buf_ptr, buf, sizeof(int16_t)*len);
+ s->buf_ptr += len;
+}
+
+/*- End of function --------------------------------------------------------*/
+
+static void normalise_history(plc_state_t *s)
+{
+ int16_t tmp[PLC_HISTORY_LEN];
+
+ if (s->buf_ptr == 0)
+ return;
+ memcpy(tmp, s->history, sizeof(int16_t)*s->buf_ptr);
+ memcpy(s->history, s->history + s->buf_ptr, sizeof(int16_t) * (PLC_HISTORY_LEN - s->buf_ptr));
+ memcpy(s->history + PLC_HISTORY_LEN - s->buf_ptr, tmp, sizeof(int16_t) * s->buf_ptr);
+ s->buf_ptr = 0;
+}
+
+/*- End of function --------------------------------------------------------*/
+
+static int __inline__ amdf_pitch(int min_pitch, int max_pitch, int16_t amp[], int len)
+{
+ int i;
+ int j;
+ int acc;
+ int min_acc;
+ int pitch;
+
+ pitch = min_pitch;
+ min_acc = INT_MAX;
+ for (i = max_pitch; i <= min_pitch; i++) {
+ acc = 0;
+ for (j = 0; j < len; j++)
+ acc += abs(amp[i + j] - amp[j]);
+ if (acc < min_acc) {
+ min_acc = acc;
+ pitch = i;
+ }
+ }
+ return pitch;
+}
+
+/*- End of function --------------------------------------------------------*/
+
+int plc_rx(plc_state_t *s, int16_t amp[], int len)
+{
+ int i;
+ int pitch_overlap;
+ float old_step;
+ float new_step;
+ float old_weight;
+ float new_weight;
+ float gain;
+
+ if (s->missing_samples) {
+ /* Although we have a real signal, we need to smooth it to fit well
+ with the synthetic signal we used for the previous block */
+
+ /* The start of the real data is overlapped with the next 1/4 cycle
+ of the synthetic data. */
+ pitch_overlap = s->pitch >> 2;
+ if (pitch_overlap > len)
+ pitch_overlap = len;
+ gain = 1.0 - s->missing_samples*ATTENUATION_INCREMENT;
+ if (gain < 0.0)
+ gain = 0.0;
+ new_step = 1.0/pitch_overlap;
+ old_step = new_step*gain;
+ new_weight = new_step;
+ old_weight = (1.0 - new_step)*gain;
+ for (i = 0; i < pitch_overlap; i++) {
+ amp[i] = fsaturate(old_weight * s->pitchbuf[s->pitch_offset] + new_weight * amp[i]);
+ if (++s->pitch_offset >= s->pitch)
+ s->pitch_offset = 0;
+ new_weight += new_step;
+ old_weight -= old_step;
+ if (old_weight < 0.0)
+ old_weight = 0.0;
+ }
+ s->missing_samples = 0;
+ }
+ save_history(s, amp, len);
+ return len;
+}
+
+/*- End of function --------------------------------------------------------*/
+
+int plc_fillin(plc_state_t *s, int16_t amp[], int len)
+{
+ int i;
+ int pitch_overlap;
+ float old_step;
+ float new_step;
+ float old_weight;
+ float new_weight;
+ float gain;
+ int16_t *orig_amp;
+ int orig_len;
+
+ orig_amp = amp;
+ orig_len = len;
+ if (s->missing_samples == 0) {
+ /* As the gap in real speech starts we need to assess the last known pitch,
+ and prepare the synthetic data we will use for fill-in */
+ normalise_history(s);
+ s->pitch = amdf_pitch(PLC_PITCH_MIN, PLC_PITCH_MAX, s->history + PLC_HISTORY_LEN - CORRELATION_SPAN - PLC_PITCH_MIN, CORRELATION_SPAN);
+ /* We overlap a 1/4 wavelength */
+ pitch_overlap = s->pitch >> 2;
+ /* Cook up a single cycle of pitch, using a single of the real signal with 1/4
+ cycle OLA'ed to make the ends join up nicely */
+ /* The first 3/4 of the cycle is a simple copy */
+ for (i = 0; i < s->pitch - pitch_overlap; i++)
+ s->pitchbuf[i] = s->history[PLC_HISTORY_LEN - s->pitch + i];
+ /* The last 1/4 of the cycle is overlapped with the end of the previous cycle */
+ new_step = 1.0/pitch_overlap;
+ new_weight = new_step;
+ for ( ; i < s->pitch; i++) {
+ s->pitchbuf[i] = s->history[PLC_HISTORY_LEN - s->pitch + i] * (1.0 - new_weight) + s->history[PLC_HISTORY_LEN - 2 * s->pitch + i]*new_weight;
+ new_weight += new_step;
+ }
+ /* We should now be ready to fill in the gap with repeated, decaying cycles
+ of what is in pitchbuf */
+
+ /* We need to OLA the first 1/4 wavelength of the synthetic data, to smooth
+ it into the previous real data. To avoid the need to introduce a delay
+ in the stream, reverse the last 1/4 wavelength, and OLA with that. */
+ gain = 1.0;
+ new_step = 1.0 / pitch_overlap;
+ old_step = new_step;
+ new_weight = new_step;
+ old_weight = 1.0 - new_step;
+ for (i = 0; i < pitch_overlap; i++) {
+ amp[i] = fsaturate(old_weight * s->history[PLC_HISTORY_LEN - 1 - i] + new_weight * s->pitchbuf[i]);
+ new_weight += new_step;
+ old_weight -= old_step;
+ if (old_weight < 0.0)
+ old_weight = 0.0;
+ }
+ s->pitch_offset = i;
+ } else {
+ gain = 1.0 - s->missing_samples*ATTENUATION_INCREMENT;
+ i = 0;
+ }
+ for ( ; gain > 0.0 && i < len; i++) {
+ amp[i] = s->pitchbuf[s->pitch_offset] * gain;
+ gain -= ATTENUATION_INCREMENT;
+ if (++s->pitch_offset >= s->pitch)
+ s->pitch_offset = 0;
+ }
+ for ( ; i < len; i++)
+ amp[i] = 0;
+ s->missing_samples += orig_len;
+ save_history(s, amp, len);
+ return len;
+}
+
+/*- End of function --------------------------------------------------------*/
+
+plc_state_t *plc_init(plc_state_t *s)
+{
+ memset(s, 0, sizeof(*s));
+ return s;
+}
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/
diff --git a/trunk/main/poll.c b/trunk/main/poll.c
new file mode 100644
index 000000000..bd283866d
--- /dev/null
+++ b/trunk/main/poll.c
@@ -0,0 +1,306 @@
+/*---------------------------------------------------------------------------*\
+ $Id$
+
+ NAME
+
+ poll - select(2)-based poll() emulation function for BSD systems.
+
+ SYNOPSIS
+ #include "poll.h"
+
+ struct pollfd
+ {
+ int fd;
+ short events;
+ short revents;
+ }
+
+ int poll (struct pollfd *pArray, unsigned long n_fds, int timeout)
+
+ DESCRIPTION
+
+ This file, and the accompanying "poll.h", implement the System V
+ poll(2) system call for BSD systems (which typically do not provide
+ poll()). Poll() provides a method for multiplexing input and output
+ on multiple open file descriptors; in traditional BSD systems, that
+ capability is provided by select(). While the semantics of select()
+ differ from those of poll(), poll() can be readily emulated in terms
+ of select() -- which is how this function is implemented.
+
+ REFERENCES
+ Stevens, W. Richard. Unix Network Programming. Prentice-Hall, 1990.
+
+ NOTES
+ 1. This software requires an ANSI C compiler.
+
+ LICENSE
+
+ This software is released under the following license:
+
+ Copyright (c) 1995-2002 Brian M. Clapper
+ All rights reserved.
+
+ Redistribution and use in source and binary forms are
+ permitted provided that: (1) source distributions retain
+ this entire copyright notice and comment; (2) modifications
+ made to the software are prominently mentioned, and a copy
+ of the original software (or a pointer to its location) are
+ included; and (3) distributions including binaries display
+ the following acknowledgement: "This product includes
+ software developed by Brian M. Clapper <bmc@clapper.org>"
+ in the documentation or other materials provided with the
+ distribution. The name of the author may not be used to
+ endorse or promote products derived from this software
+ without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS
+ OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ PARTICULAR PURPOSE.
+
+ Effectively, this means you can do what you want with the software
+ except remove this notice or take advantage of the author's name.
+ If you modify the software and redistribute your modified version,
+ you must indicate that your version is a modification of the
+ original, and you must provide either a pointer to or a copy of the
+ original.
+\*---------------------------------------------------------------------------*/
+
+
+/*---------------------------------------------------------------------------*\
+ Includes
+\*---------------------------------------------------------------------------*/
+
+#include <unistd.h> /* standard Unix definitions */
+#include <sys/types.h> /* system types */
+#include <sys/time.h> /* time definitions */
+#include <assert.h> /* assertion macros */
+#include <string.h> /* string functions */
+
+#include "asterisk/poll-compat.h" /* this package */
+
+/*---------------------------------------------------------------------------*\
+ Macros
+\*---------------------------------------------------------------------------*/
+
+#ifndef MAX
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+#endif
+
+
+/*---------------------------------------------------------------------------*\
+ Private Functions
+\*---------------------------------------------------------------------------*/
+
+static int map_poll_spec
+#if __STDC__ > 0
+ (struct pollfd *pArray,
+ unsigned long n_fds,
+ fd_set *pReadSet,
+ fd_set *pWriteSet,
+ fd_set *pExceptSet)
+#else
+ (pArray, n_fds, pReadSet, pWriteSet, pExceptSet)
+ struct pollfd *pArray;
+ unsigned long n_fds;
+ fd_set *pReadSet;
+ fd_set *pWriteSet;
+ fd_set *pExceptSet;
+#endif
+{
+ register unsigned long i; /* loop control */
+ register struct pollfd *pCur; /* current array element */
+ register int max_fd = -1; /* return value */
+
+ /*
+ Map the poll() structures into the file descriptor sets required
+ by select().
+ */
+ for (i = 0, pCur = pArray; i < n_fds; i++, pCur++)
+ {
+ /* Skip any bad FDs in the array. */
+
+ if (pCur->fd < 0)
+ continue;
+
+ if (pCur->events & POLLIN)
+ {
+ /* "Input Ready" notification desired. */
+ FD_SET (pCur->fd, pReadSet);
+ }
+
+ if (pCur->events & POLLOUT)
+ {
+ /* "Output Possible" notification desired. */
+ FD_SET (pCur->fd, pWriteSet);
+ }
+
+ if (pCur->events & POLLPRI)
+ {
+ /*
+ "Exception Occurred" notification desired. (Exceptions
+ include out of band data.
+ */
+ FD_SET (pCur->fd, pExceptSet);
+ }
+
+ max_fd = MAX (max_fd, pCur->fd);
+ }
+
+ return max_fd;
+}
+
+static struct timeval *map_timeout
+#if __STDC__ > 0
+ (int poll_timeout, struct timeval *pSelTimeout)
+#else
+ (poll_timeout, pSelTimeout)
+ int poll_timeout;
+ struct timeval *pSelTimeout;
+#endif
+{
+ struct timeval *pResult;
+
+ /*
+ Map the poll() timeout value into a select() timeout. The possible
+ values of the poll() timeout value, and their meanings, are:
+
+ VALUE MEANING
+
+ -1 wait indefinitely (until signal occurs)
+ 0 return immediately, don't block
+ >0 wait specified number of milliseconds
+
+ select() uses a "struct timeval", which specifies the timeout in
+ seconds and microseconds, so the milliseconds value has to be mapped
+ accordingly.
+ */
+
+ assert (pSelTimeout != (struct timeval *) NULL);
+
+ switch (poll_timeout)
+ {
+ case -1:
+ /*
+ A NULL timeout structure tells select() to wait indefinitely.
+ */
+ pResult = (struct timeval *) NULL;
+ break;
+
+ case 0:
+ /*
+ "Return immediately" (test) is specified by all zeros in
+ a timeval structure.
+ */
+ pSelTimeout->tv_sec = 0;
+ pSelTimeout->tv_usec = 0;
+ pResult = pSelTimeout;
+ break;
+
+ default:
+ /* Wait the specified number of milliseconds. */
+ pSelTimeout->tv_sec = poll_timeout / 1000; /* get seconds */
+ poll_timeout %= 1000; /* remove seconds */
+ pSelTimeout->tv_usec = poll_timeout * 1000; /* get microseconds */
+ pResult = pSelTimeout;
+ break;
+ }
+
+
+ return pResult;
+}
+
+static void map_select_results
+#if __STDC__ > 0
+ (struct pollfd *pArray,
+ unsigned long n_fds,
+ fd_set *pReadSet,
+ fd_set *pWriteSet,
+ fd_set *pExceptSet)
+#else
+ (pArray, n_fds, pReadSet, pWriteSet, pExceptSet)
+ struct pollfd *pArray;
+ unsigned long n_fds;
+ fd_set *pReadSet;
+ fd_set *pWriteSet;
+ fd_set *pExceptSet;
+#endif
+{
+ register unsigned long i; /* loop control */
+ register struct pollfd *pCur; /* current array element */
+
+ for (i = 0, pCur = pArray; i < n_fds; i++, pCur++)
+ {
+ /* Skip any bad FDs in the array. */
+
+ if (pCur->fd < 0)
+ continue;
+
+ /* Exception events take priority over input events. */
+
+ pCur->revents = 0;
+ if (FD_ISSET (pCur->fd, pExceptSet))
+ pCur->revents |= POLLPRI;
+
+ else if (FD_ISSET (pCur->fd, pReadSet))
+ pCur->revents |= POLLIN;
+
+ if (FD_ISSET (pCur->fd, pWriteSet))
+ pCur->revents |= POLLOUT;
+ }
+
+ return;
+}
+
+/*---------------------------------------------------------------------------*\
+ Public Functions
+\*---------------------------------------------------------------------------*/
+
+int poll
+
+#if __STDC__ > 0
+ (struct pollfd *pArray, unsigned long n_fds, int timeout)
+#else
+ (pArray, n_fds, timeout)
+ struct pollfd *pArray;
+ unsigned long n_fds;
+ int timeout;
+#endif
+
+{
+ fd_set read_descs; /* input file descs */
+ fd_set write_descs; /* output file descs */
+ fd_set except_descs; /* exception descs */
+ struct timeval stime; /* select() timeout value */
+ int ready_descriptors; /* function result */
+ int max_fd; /* maximum fd value */
+ struct timeval *pTimeout; /* actually passed */
+
+ FD_ZERO (&read_descs);
+ FD_ZERO (&write_descs);
+ FD_ZERO (&except_descs);
+
+ assert (pArray != (struct pollfd *) NULL);
+
+ /* Map the poll() file descriptor list in the select() data structures. */
+
+ max_fd = map_poll_spec (pArray, n_fds,
+ &read_descs, &write_descs, &except_descs);
+
+ /* Map the poll() timeout value in the select() timeout structure. */
+
+ pTimeout = map_timeout (timeout, &stime);
+
+ /* Make the select() call. */
+
+ ready_descriptors = select (max_fd + 1, &read_descs, &write_descs,
+ &except_descs, pTimeout);
+
+ if (ready_descriptors >= 0)
+ {
+ map_select_results (pArray, n_fds,
+ &read_descs, &write_descs, &except_descs);
+ }
+
+ return ready_descriptors;
+}
diff --git a/trunk/main/privacy.c b/trunk/main/privacy.c
new file mode 100644
index 000000000..dfb197f38
--- /dev/null
+++ b/trunk/main/privacy.c
@@ -0,0 +1,112 @@
+/*
+ * 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.
+ */
+
+/*! \file
+ *
+ * \brief Privacy Routines
+ *
+ * \author Mark Spencer <markster@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/time.h>
+#include <signal.h>
+#include <dirent.h>
+
+#include "asterisk/channel.h"
+#include "asterisk/file.h"
+#include "asterisk/app.h"
+#include "asterisk/dsp.h"
+#include "asterisk/astdb.h"
+#include "asterisk/callerid.h"
+#include "asterisk/privacy.h"
+#include "asterisk/utils.h"
+#include "asterisk/lock.h"
+
+int ast_privacy_check(char *dest, char *cid)
+{
+ char tmp[256] = "";
+ char *trimcid = "";
+ char *n, *l;
+ int res;
+ char key[256], result[256];
+ if (cid)
+ ast_copy_string(tmp, cid, sizeof(tmp));
+ ast_callerid_parse(tmp, &n, &l);
+ if (l) {
+ ast_shrink_phone_number(l);
+ trimcid = l;
+ }
+ snprintf(key, sizeof(key), "%s/%s", dest, trimcid);
+ res = ast_db_get("privacy", key, result, sizeof(result));
+ if (!res) {
+ if (!strcasecmp(result, "allow"))
+ return AST_PRIVACY_ALLOW;
+ if (!strcasecmp(result, "deny"))
+ return AST_PRIVACY_DENY;
+ if (!strcasecmp(result, "kill"))
+ return AST_PRIVACY_KILL;
+ if (!strcasecmp(result, "torture"))
+ return AST_PRIVACY_TORTURE;
+ }
+ return AST_PRIVACY_UNKNOWN;
+}
+
+int ast_privacy_reset(char *dest)
+{
+ if (!dest)
+ return -1;
+ return ast_db_deltree("privacy", dest);
+}
+
+int ast_privacy_set(char *dest, char *cid, int status)
+{
+ char tmp[256] = "";
+ char *trimcid = "";
+ char *n, *l;
+ int res;
+ char key[256];
+ if (cid)
+ ast_copy_string(tmp, cid, sizeof(tmp));
+ ast_callerid_parse(tmp, &n, &l);
+ if (l) {
+ ast_shrink_phone_number(l);
+ trimcid = l;
+ }
+ if (ast_strlen_zero(trimcid)) {
+ /* Don't store anything for empty Caller*ID */
+ return 0;
+ }
+ snprintf(key, sizeof(key), "%s/%s", dest, trimcid);
+ if (status == AST_PRIVACY_UNKNOWN)
+ res = ast_db_del("privacy", key);
+ else if (status == AST_PRIVACY_ALLOW)
+ res = ast_db_put("privacy", key, "allow");
+ else if (status == AST_PRIVACY_DENY)
+ res = ast_db_put("privacy", key, "deny");
+ else if (status == AST_PRIVACY_KILL)
+ res = ast_db_put("privacy", key, "kill");
+ else if (status == AST_PRIVACY_TORTURE)
+ res = ast_db_put("privacy", key, "torture");
+ else
+ res = -1;
+ return res;
+}
diff --git a/trunk/main/rtp.c b/trunk/main/rtp.c
new file mode 100644
index 000000000..874f8ddb2
--- /dev/null
+++ b/trunk/main/rtp.c
@@ -0,0 +1,4093 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006, 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.
+ */
+
+/*!
+ * \file
+ *
+ * \brief Supports RTP and RTCP with Symmetric RTP support for NAT traversal.
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \note RTP is defined in RFC 3550.
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/time.h>
+#include <signal.h>
+#include <fcntl.h>
+
+#include "asterisk/rtp.h"
+#include "asterisk/frame.h"
+#include "asterisk/channel.h"
+#include "asterisk/acl.h"
+#include "asterisk/config.h"
+#include "asterisk/lock.h"
+#include "asterisk/utils.h"
+#include "asterisk/netsock.h"
+#include "asterisk/cli.h"
+#include "asterisk/manager.h"
+#include "asterisk/unaligned.h"
+
+#define MAX_TIMESTAMP_SKEW 640
+
+#define RTP_SEQ_MOD (1<<16) /*!< A sequence number can't be more than 16 bits */
+#define RTCP_DEFAULT_INTERVALMS 5000 /*!< Default milli-seconds between RTCP reports we send */
+#define RTCP_MIN_INTERVALMS 500 /*!< Min milli-seconds between RTCP reports we send */
+#define RTCP_MAX_INTERVALMS 60000 /*!< Max milli-seconds between RTCP reports we send */
+
+#define RTCP_PT_FUR 192
+#define RTCP_PT_SR 200
+#define RTCP_PT_RR 201
+#define RTCP_PT_SDES 202
+#define RTCP_PT_BYE 203
+#define RTCP_PT_APP 204
+
+#define RTP_MTU 1200
+
+#define DEFAULT_DTMF_TIMEOUT 3000 /*!< samples */
+
+static int dtmftimeout = DEFAULT_DTMF_TIMEOUT;
+
+static int rtpstart; /*!< First port for RTP sessions (set in rtp.conf) */
+static int rtpend; /*!< Last port for RTP sessions (set in rtp.conf) */
+static int rtpdebug; /*!< Are we debugging? */
+static int rtcpdebug; /*!< Are we debugging RTCP? */
+static int rtcpstats; /*!< Are we debugging RTCP? */
+static int rtcpinterval = RTCP_DEFAULT_INTERVALMS; /*!< Time between rtcp reports in millisecs */
+static int stundebug; /*!< Are we debugging stun? */
+static struct sockaddr_in rtpdebugaddr; /*!< Debug packets to/from this host */
+static struct sockaddr_in rtcpdebugaddr; /*!< Debug RTCP packets to/from this host */
+#ifdef SO_NO_CHECK
+static int nochecksums;
+#endif
+
+/* Uncomment this to enable more intense native bridging, but note: this is currently buggy */
+/* #define P2P_INTENSE */
+
+/*!
+ * \brief Structure representing a RTP session.
+ *
+ * RTP session is defined on page 9 of RFC 3550: "An association among a set of participants communicating with RTP. A participant may be involved in multiple RTP sessions at the same time [...]"
+ *
+ */
+/*! \brief The value of each payload format mapping: */
+struct rtpPayloadType {
+ int isAstFormat; /*!< whether the following code is an AST_FORMAT */
+ int code;
+};
+
+
+/*! \brief RTP session description */
+struct ast_rtp {
+ int s;
+ struct ast_frame f;
+ unsigned char rawdata[8192 + AST_FRIENDLY_OFFSET];
+ unsigned int ssrc; /*!< Synchronization source, RFC 3550, page 10. */
+ unsigned int themssrc; /*!< Their SSRC */
+ unsigned int rxssrc;
+ unsigned int lastts;
+ unsigned int lastrxts;
+ unsigned int lastividtimestamp;
+ unsigned int lastovidtimestamp;
+ unsigned int lastitexttimestamp;
+ unsigned int lastotexttimestamp;
+ unsigned int lasteventseqn;
+ int lastrxseqno; /*!< Last received sequence number */
+ unsigned short seedrxseqno; /*!< What sequence number did they start with?*/
+ unsigned int seedrxts; /*!< What RTP timestamp did they start with? */
+ unsigned int rxcount; /*!< How many packets have we received? */
+ unsigned int rxoctetcount; /*!< How many octets have we received? should be rxcount *160*/
+ unsigned int txcount; /*!< How many packets have we sent? */
+ unsigned int txoctetcount; /*!< How many octets have we sent? (txcount*160)*/
+ unsigned int cycles; /*!< Shifted count of sequence number cycles */
+ double rxjitter; /*!< Interarrival jitter at the moment */
+ double rxtransit; /*!< Relative transit time for previous packet */
+ int lasttxformat;
+ int lastrxformat;
+
+ int rtptimeout; /*!< RTP timeout time (negative or zero means disabled, negative value means temporarily disabled) */
+ int rtpholdtimeout; /*!< RTP timeout when on hold (negative or zero means disabled, negative value means temporarily disabled). */
+ int rtpkeepalive; /*!< Send RTP comfort noice packets for keepalive */
+
+ /* DTMF Reception Variables */
+ char resp;
+ unsigned int lastevent;
+ int dtmfcount;
+ unsigned int dtmfsamples;
+ /* DTMF Transmission Variables */
+ unsigned int lastdigitts;
+ char sending_digit; /*!< boolean - are we sending digits */
+ char send_digit; /*!< digit we are sending */
+ int send_payload;
+ int send_duration;
+ int nat;
+ unsigned int flags;
+ struct sockaddr_in us; /*!< Socket representation of the local endpoint. */
+ struct sockaddr_in them; /*!< Socket representation of the remote endpoint. */
+ struct timeval rxcore;
+ struct timeval txcore;
+ double drxcore; /*!< The double representation of the first received packet */
+ struct timeval lastrx; /*!< timeval when we last received a packet */
+ struct timeval dtmfmute;
+ struct ast_smoother *smoother;
+ int *ioid;
+ unsigned short seqno; /*!< Sequence number, RFC 3550, page 13. */
+ unsigned short rxseqno;
+ struct sched_context *sched;
+ struct io_context *io;
+ void *data;
+ ast_rtp_callback callback;
+#ifdef P2P_INTENSE
+ ast_mutex_t bridge_lock;
+#endif
+ struct rtpPayloadType current_RTP_PT[MAX_RTP_PT];
+ int rtp_lookup_code_cache_isAstFormat; /*!< a cache for the result of rtp_lookup_code(): */
+ int rtp_lookup_code_cache_code;
+ int rtp_lookup_code_cache_result;
+ struct ast_rtcp *rtcp;
+ struct ast_codec_pref pref;
+ struct ast_rtp *bridged; /*!< Who we are Packet bridged to */
+};
+
+/* Forward declarations */
+static int ast_rtcp_write(const void *data);
+static void timeval2ntp(struct timeval tv, unsigned int *msw, unsigned int *lsw);
+static int ast_rtcp_write_sr(const void *data);
+static int ast_rtcp_write_rr(const void *data);
+static unsigned int ast_rtcp_calc_interval(struct ast_rtp *rtp);
+static int ast_rtp_senddigit_continuation(struct ast_rtp *rtp);
+int ast_rtp_senddigit_end(struct ast_rtp *rtp, char digit);
+
+#define FLAG_3389_WARNING (1 << 0)
+#define FLAG_NAT_ACTIVE (3 << 1)
+#define FLAG_NAT_INACTIVE (0 << 1)
+#define FLAG_NAT_INACTIVE_NOWARN (1 << 1)
+#define FLAG_HAS_DTMF (1 << 3)
+#define FLAG_P2P_SENT_MARK (1 << 4)
+#define FLAG_P2P_NEED_DTMF (1 << 5)
+#define FLAG_CALLBACK_MODE (1 << 6)
+#define FLAG_DTMF_COMPENSATE (1 << 7)
+#define FLAG_HAS_STUN (1 << 8)
+
+/*!
+ * \brief Structure defining an RTCP session.
+ *
+ * The concept "RTCP session" is not defined in RFC 3550, but since
+ * this structure is analogous to ast_rtp, which tracks a RTP session,
+ * it is logical to think of this as a RTCP session.
+ *
+ * RTCP packet is defined on page 9 of RFC 3550.
+ *
+ */
+struct ast_rtcp {
+ int s; /*!< Socket */
+ struct sockaddr_in us; /*!< Socket representation of the local endpoint. */
+ struct sockaddr_in them; /*!< Socket representation of the remote endpoint. */
+ unsigned int soc; /*!< What they told us */
+ unsigned int spc; /*!< What they told us */
+ unsigned int themrxlsr; /*!< The middle 32 bits of the NTP timestamp in the last received SR*/
+ struct timeval rxlsr; /*!< Time when we got their last SR */
+ struct timeval txlsr; /*!< Time when we sent or last SR*/
+ unsigned int expected_prior; /*!< no. packets in previous interval */
+ unsigned int received_prior; /*!< no. packets received in previous interval */
+ int schedid; /*!< Schedid returned from ast_sched_add() to schedule RTCP-transmissions*/
+ unsigned int rr_count; /*!< number of RRs we've sent, not including report blocks in SR's */
+ unsigned int sr_count; /*!< number of SRs we've sent */
+ unsigned int lastsrtxcount; /*!< Transmit packet count when last SR sent */
+ double accumulated_transit; /*!< accumulated a-dlsr-lsr */
+ double rtt; /*!< Last reported rtt */
+ unsigned int reported_jitter; /*!< The contents of their last jitter entry in the RR */
+ unsigned int reported_lost; /*!< Reported lost packets in their RR */
+ char quality[AST_MAX_USER_FIELD];
+ double maxrxjitter;
+ double minrxjitter;
+ double maxrtt;
+ double minrtt;
+ int sendfur;
+};
+
+/*!
+ * \brief STUN support code
+ *
+ * This code provides some support for doing STUN transactions.
+ * Eventually it should be moved elsewhere as other protocols
+ * than RTP can benefit from it - e.g. SIP.
+ * STUN is described in RFC3489 and it is based on the exchange
+ * of UDP packets between a client and one or more servers to
+ * determine the externally visible address (and port) of the client
+ * once it has gone through the NAT boxes that connect it to the
+ * outside.
+ * The simplest request packet is just the header defined in
+ * struct stun_header, and from the response we may just look at
+ * one attribute, STUN_MAPPED_ADDRESS, that we find in the response.
+ * By doing more transactions with different server addresses we
+ * may determine more about the behaviour of the NAT boxes, of
+ * course - the details are in the RFC.
+ *
+ * All STUN packets start with a simple header made of a type,
+ * length (excluding the header) and a 16-byte random transaction id.
+ * Following the header we may have zero or more attributes, each
+ * structured as a type, length and a value (whose format depends
+ * on the type, but often contains addresses).
+ * Of course all fields are in network format.
+ */
+
+typedef struct { unsigned int id[4]; } __attribute__((packed)) stun_trans_id;
+
+struct stun_header {
+ unsigned short msgtype;
+ unsigned short msglen;
+ stun_trans_id id;
+ unsigned char ies[0];
+} __attribute__((packed));
+
+struct stun_attr {
+ unsigned short attr;
+ unsigned short len;
+ unsigned char value[0];
+} __attribute__((packed));
+
+/*
+ * The format normally used for addresses carried by STUN messages.
+ */
+struct stun_addr {
+ unsigned char unused;
+ unsigned char family;
+ unsigned short port;
+ unsigned int addr;
+} __attribute__((packed));
+
+#define STUN_IGNORE (0)
+#define STUN_ACCEPT (1)
+
+/*! \brief STUN message types
+ * 'BIND' refers to transactions used to determine the externally
+ * visible addresses. 'SEC' refers to transactions used to establish
+ * a session key for subsequent requests.
+ * 'SEC' functionality is not supported here.
+ */
+
+#define STUN_BINDREQ 0x0001
+#define STUN_BINDRESP 0x0101
+#define STUN_BINDERR 0x0111
+#define STUN_SECREQ 0x0002
+#define STUN_SECRESP 0x0102
+#define STUN_SECERR 0x0112
+
+/*! \brief Basic attribute types in stun messages.
+ * Messages can also contain custom attributes (codes above 0x7fff)
+ */
+#define STUN_MAPPED_ADDRESS 0x0001
+#define STUN_RESPONSE_ADDRESS 0x0002
+#define STUN_CHANGE_REQUEST 0x0003
+#define STUN_SOURCE_ADDRESS 0x0004
+#define STUN_CHANGED_ADDRESS 0x0005
+#define STUN_USERNAME 0x0006
+#define STUN_PASSWORD 0x0007
+#define STUN_MESSAGE_INTEGRITY 0x0008
+#define STUN_ERROR_CODE 0x0009
+#define STUN_UNKNOWN_ATTRIBUTES 0x000a
+#define STUN_REFLECTED_FROM 0x000b
+
+/*! \brief helper function to print message names */
+static const char *stun_msg2str(int msg)
+{
+ switch (msg) {
+ case STUN_BINDREQ:
+ return "Binding Request";
+ case STUN_BINDRESP:
+ return "Binding Response";
+ case STUN_BINDERR:
+ return "Binding Error Response";
+ case STUN_SECREQ:
+ return "Shared Secret Request";
+ case STUN_SECRESP:
+ return "Shared Secret Response";
+ case STUN_SECERR:
+ return "Shared Secret Error Response";
+ }
+ return "Non-RFC3489 Message";
+}
+
+/*! \brief helper function to print attribute names */
+static const char *stun_attr2str(int msg)
+{
+ switch (msg) {
+ case STUN_MAPPED_ADDRESS:
+ return "Mapped Address";
+ case STUN_RESPONSE_ADDRESS:
+ return "Response Address";
+ case STUN_CHANGE_REQUEST:
+ return "Change Request";
+ case STUN_SOURCE_ADDRESS:
+ return "Source Address";
+ case STUN_CHANGED_ADDRESS:
+ return "Changed Address";
+ case STUN_USERNAME:
+ return "Username";
+ case STUN_PASSWORD:
+ return "Password";
+ case STUN_MESSAGE_INTEGRITY:
+ return "Message Integrity";
+ case STUN_ERROR_CODE:
+ return "Error Code";
+ case STUN_UNKNOWN_ATTRIBUTES:
+ return "Unknown Attributes";
+ case STUN_REFLECTED_FROM:
+ return "Reflected From";
+ }
+ return "Non-RFC3489 Attribute";
+}
+
+/*! \brief here we store credentials extracted from a message */
+struct stun_state {
+ const char *username;
+ const char *password;
+};
+
+static int stun_process_attr(struct stun_state *state, struct stun_attr *attr)
+{
+ if (stundebug)
+ ast_verbose("Found STUN Attribute %s (%04x), length %d\n",
+ stun_attr2str(ntohs(attr->attr)), ntohs(attr->attr), ntohs(attr->len));
+ switch (ntohs(attr->attr)) {
+ case STUN_USERNAME:
+ state->username = (const char *) (attr->value);
+ break;
+ case STUN_PASSWORD:
+ state->password = (const char *) (attr->value);
+ break;
+ default:
+ if (stundebug)
+ ast_verbose("Ignoring STUN attribute %s (%04x), length %d\n",
+ stun_attr2str(ntohs(attr->attr)), ntohs(attr->attr), ntohs(attr->len));
+ }
+ return 0;
+}
+
+/*! \brief append a string to an STUN message */
+static void append_attr_string(struct stun_attr **attr, int attrval, const char *s, int *len, int *left)
+{
+ int size = sizeof(**attr) + strlen(s);
+ if (*left > size) {
+ (*attr)->attr = htons(attrval);
+ (*attr)->len = htons(strlen(s));
+ memcpy((*attr)->value, s, strlen(s));
+ (*attr) = (struct stun_attr *)((*attr)->value + strlen(s));
+ *len += size;
+ *left -= size;
+ }
+}
+
+/*! \brief append an address to an STUN message */
+static void append_attr_address(struct stun_attr **attr, int attrval, struct sockaddr_in *sin, int *len, int *left)
+{
+ int size = sizeof(**attr) + 8;
+ struct stun_addr *addr;
+ if (*left > size) {
+ (*attr)->attr = htons(attrval);
+ (*attr)->len = htons(8);
+ addr = (struct stun_addr *)((*attr)->value);
+ addr->unused = 0;
+ addr->family = 0x01;
+ addr->port = sin->sin_port;
+ addr->addr = sin->sin_addr.s_addr;
+ (*attr) = (struct stun_attr *)((*attr)->value + 8);
+ *len += size;
+ *left -= size;
+ }
+}
+
+/*! \brief wrapper to send an STUN message */
+static int stun_send(int s, struct sockaddr_in *dst, struct stun_header *resp)
+{
+ return sendto(s, resp, ntohs(resp->msglen) + sizeof(*resp), 0,
+ (struct sockaddr *)dst, sizeof(*dst));
+}
+
+/*! \brief helper function to generate a random request id */
+static void stun_req_id(struct stun_header *req)
+{
+ int x;
+ for (x = 0; x < 4; x++)
+ req->id.id[x] = ast_random();
+}
+
+size_t ast_rtp_alloc_size(void)
+{
+ return sizeof(struct ast_rtp);
+}
+
+/*! \brief callback type to be invoked on stun responses. */
+typedef int (stun_cb_f)(struct stun_attr *attr, void *arg);
+
+/*! \brief handle an incoming STUN message.
+ *
+ * Do some basic sanity checks on packet size and content,
+ * try to extract a bit of information, and possibly reply.
+ * At the moment this only processes BIND requests, and returns
+ * the externally visible address of the request.
+ * If a callback is specified, invoke it with the attribute.
+ */
+static int stun_handle_packet(int s, struct sockaddr_in *src,
+ unsigned char *data, size_t len, stun_cb_f *stun_cb, void *arg)
+{
+ struct stun_header *hdr = (struct stun_header *)data;
+ struct stun_attr *attr;
+ struct stun_state st;
+ int ret = STUN_IGNORE;
+ int x;
+
+ /* On entry, 'len' is the length of the udp payload. After the
+ * initial checks it becomes the size of unprocessed options,
+ * while 'data' is advanced accordingly.
+ */
+ if (len < sizeof(struct stun_header)) {
+ ast_debug(1, "Runt STUN packet (only %d, wanting at least %d)\n", (int) len, (int) sizeof(struct stun_header));
+ return -1;
+ }
+ len -= sizeof(struct stun_header);
+ data += sizeof(struct stun_header);
+ x = ntohs(hdr->msglen); /* len as advertised in the message */
+ if (stundebug)
+ ast_verbose("STUN Packet, msg %s (%04x), length: %d\n", stun_msg2str(ntohs(hdr->msgtype)), ntohs(hdr->msgtype), x);
+ if (x > len) {
+ ast_debug(1, "Scrambled STUN packet length (got %d, expecting %d)\n", x, (int)len);
+ } else
+ len = x;
+ memset(&st, 0, sizeof(st));
+ while (len) {
+ if (len < sizeof(struct stun_attr)) {
+ ast_debug(1, "Runt Attribute (got %d, expecting %d)\n", (int)len, (int) sizeof(struct stun_attr));
+ break;
+ }
+ attr = (struct stun_attr *)data;
+ /* compute total attribute length */
+ x = ntohs(attr->len) + sizeof(struct stun_attr);
+ if (x > len) {
+ ast_debug(1, "Inconsistent Attribute (length %d exceeds remaining msg len %d)\n", x, (int)len);
+ break;
+ }
+ if (stun_cb)
+ stun_cb(attr, arg);
+ if (stun_process_attr(&st, attr)) {
+ ast_debug(1, "Failed to handle attribute %s (%04x)\n", stun_attr2str(ntohs(attr->attr)), ntohs(attr->attr));
+ break;
+ }
+ /* Clear attribute id: in case previous entry was a string,
+ * this will act as the terminator for the string.
+ */
+ attr->attr = 0;
+ data += x;
+ len -= x;
+ }
+ /* Null terminate any string.
+ * XXX NOTE, we write past the size of the buffer passed by the
+ * caller, so this is potentially dangerous. The only thing that
+ * saves us is that usually we read the incoming message in a
+ * much larger buffer in the struct ast_rtp
+ */
+ *data = '\0';
+
+ /* Now prepare to generate a reply, which at the moment is done
+ * only for properly formed (len == 0) STUN_BINDREQ messages.
+ */
+ if (len == 0) {
+ unsigned char respdata[1024];
+ struct stun_header *resp = (struct stun_header *)respdata;
+ int resplen = 0; /* len excluding header */
+ int respleft = sizeof(respdata) - sizeof(struct stun_header);
+
+ resp->id = hdr->id;
+ resp->msgtype = 0;
+ resp->msglen = 0;
+ attr = (struct stun_attr *)resp->ies;
+ switch (ntohs(hdr->msgtype)) {
+ case STUN_BINDREQ:
+ if (stundebug)
+ ast_verbose("STUN Bind Request, username: %s\n",
+ st.username ? st.username : "<none>");
+ if (st.username)
+ append_attr_string(&attr, STUN_USERNAME, st.username, &resplen, &respleft);
+ append_attr_address(&attr, STUN_MAPPED_ADDRESS, src, &resplen, &respleft);
+ resp->msglen = htons(resplen);
+ resp->msgtype = htons(STUN_BINDRESP);
+ stun_send(s, src, resp);
+ ret = STUN_ACCEPT;
+ break;
+ default:
+ if (stundebug)
+ ast_verbose("Dunno what to do with STUN message %04x (%s)\n", ntohs(hdr->msgtype), stun_msg2str(ntohs(hdr->msgtype)));
+ }
+ }
+ return ret;
+}
+
+/*! \brief Extract the STUN_MAPPED_ADDRESS from the stun response.
+ * This is used as a callback for stun_handle_response
+ * when called from ast_stun_request.
+ */
+static int stun_get_mapped(struct stun_attr *attr, void *arg)
+{
+ struct stun_addr *addr = (struct stun_addr *)(attr + 1);
+ struct sockaddr_in *sa = (struct sockaddr_in *)arg;
+
+ if (ntohs(attr->attr) != STUN_MAPPED_ADDRESS || ntohs(attr->len) != 8)
+ return 1; /* not us. */
+ sa->sin_port = addr->port;
+ sa->sin_addr.s_addr = addr->addr;
+ return 0;
+}
+
+/*! \brief Generic STUN request
+ * Send a generic stun request to the server specified,
+ * possibly waiting for a reply and filling the 'reply' field with
+ * the externally visible address. Note that in this case the request
+ * will be blocking.
+ * (Note, the interface may change slightly in the future).
+ *
+ * \param s the socket used to send the request
+ * \param dst the address of the STUN server
+ * \param username if non null, add the username in the request
+ * \param answer if non null, the function waits for a response and
+ * puts here the externally visible address.
+ * \return 0 on success, other values on error.
+ */
+int ast_stun_request(int s, struct sockaddr_in *dst,
+ const char *username, struct sockaddr_in *answer)
+{
+ struct stun_header *req;
+ unsigned char reqdata[1024];
+ int reqlen, reqleft;
+ struct stun_attr *attr;
+ int res = 0;
+ int retry;
+
+ req = (struct stun_header *)reqdata;
+ stun_req_id(req);
+ reqlen = 0;
+ reqleft = sizeof(reqdata) - sizeof(struct stun_header);
+ req->msgtype = 0;
+ req->msglen = 0;
+ attr = (struct stun_attr *)req->ies;
+ if (username)
+ append_attr_string(&attr, STUN_USERNAME, username, &reqlen, &reqleft);
+ req->msglen = htons(reqlen);
+ req->msgtype = htons(STUN_BINDREQ);
+ for (retry = 0; retry < 3; retry++) { /* XXX make retries configurable */
+ /* send request, possibly wait for reply */
+ unsigned char reply_buf[1024];
+ fd_set rfds;
+ struct timeval to = { 3, 0 }; /* timeout, make it configurable */
+ struct sockaddr_in src;
+ socklen_t srclen;
+
+ res = stun_send(s, dst, req);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "ast_stun_request send #%d failed error %d, retry\n",
+ retry, res);
+ continue;
+ }
+ if (answer == NULL)
+ break;
+ FD_ZERO(&rfds);
+ FD_SET(s, &rfds);
+ res = ast_select(s + 1, &rfds, NULL, NULL, &to);
+ if (res <= 0) /* timeout or error */
+ continue;
+ bzero(&src, sizeof(src));
+ srclen = sizeof(src);
+ /* XXX pass -1 in the size, because stun_handle_packet might
+ * write past the end of the buffer.
+ */
+ res = recvfrom(s, reply_buf, sizeof(reply_buf) - 1,
+ 0, (struct sockaddr *)&src, &srclen);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "ast_stun_request recvfrom #%d failed error %d, retry\n",
+ retry, res);
+ continue;
+ }
+ bzero(answer, sizeof(struct sockaddr_in));
+ stun_handle_packet(s, &src, reply_buf, res,
+ stun_get_mapped, answer);
+ res = 0; /* signal regular exit */
+ break;
+ }
+ return res;
+}
+
+/*! \brief send a STUN BIND request to the given destination.
+ * Optionally, add a username if specified.
+ */
+void ast_rtp_stun_request(struct ast_rtp *rtp, struct sockaddr_in *suggestion, const char *username)
+{
+ ast_stun_request(rtp->s, suggestion, username, NULL);
+}
+
+/*! \brief List of current sessions */
+static AST_RWLIST_HEAD_STATIC(protos, ast_rtp_protocol);
+
+static void timeval2ntp(struct timeval tv, unsigned int *msw, unsigned int *lsw)
+{
+ unsigned int sec, usec, frac;
+ sec = tv.tv_sec + 2208988800u; /* Sec between 1900 and 1970 */
+ usec = tv.tv_usec;
+ frac = (usec << 12) + (usec << 8) - ((usec * 3650) >> 6);
+ *msw = sec;
+ *lsw = frac;
+}
+
+int ast_rtp_fd(struct ast_rtp *rtp)
+{
+ return rtp->s;
+}
+
+int ast_rtcp_fd(struct ast_rtp *rtp)
+{
+ if (rtp->rtcp)
+ return rtp->rtcp->s;
+ return -1;
+}
+
+unsigned int ast_rtcp_calc_interval(struct ast_rtp *rtp)
+{
+ unsigned int interval;
+ /*! \todo XXX Do a more reasonable calculation on this one
+ * Look in RFC 3550 Section A.7 for an example*/
+ interval = rtcpinterval;
+ return interval;
+}
+
+/* \brief Put RTP timeout timers on hold during another transaction, like T.38 */
+void ast_rtp_set_rtptimers_onhold(struct ast_rtp *rtp)
+{
+ rtp->rtptimeout = (-1) * rtp->rtptimeout;
+ rtp->rtpholdtimeout = (-1) * rtp->rtpholdtimeout;
+}
+
+/*! \brief Set rtp timeout */
+void ast_rtp_set_rtptimeout(struct ast_rtp *rtp, int timeout)
+{
+ rtp->rtptimeout = timeout;
+}
+
+/*! \brief Set rtp hold timeout */
+void ast_rtp_set_rtpholdtimeout(struct ast_rtp *rtp, int timeout)
+{
+ rtp->rtpholdtimeout = timeout;
+}
+
+/*! \brief set RTP keepalive interval */
+void ast_rtp_set_rtpkeepalive(struct ast_rtp *rtp, int period)
+{
+ rtp->rtpkeepalive = period;
+}
+
+/*! \brief Get rtp timeout */
+int ast_rtp_get_rtptimeout(struct ast_rtp *rtp)
+{
+ if (rtp->rtptimeout < 0) /* We're not checking, but remembering the setting (during T.38 transmission) */
+ return 0;
+ return rtp->rtptimeout;
+}
+
+/*! \brief Get rtp hold timeout */
+int ast_rtp_get_rtpholdtimeout(struct ast_rtp *rtp)
+{
+ if (rtp->rtptimeout < 0) /* We're not checking, but remembering the setting (during T.38 transmission) */
+ return 0;
+ return rtp->rtpholdtimeout;
+}
+
+/*! \brief Get RTP keepalive interval */
+int ast_rtp_get_rtpkeepalive(struct ast_rtp *rtp)
+{
+ return rtp->rtpkeepalive;
+}
+
+void ast_rtp_set_data(struct ast_rtp *rtp, void *data)
+{
+ rtp->data = data;
+}
+
+void ast_rtp_set_callback(struct ast_rtp *rtp, ast_rtp_callback callback)
+{
+ rtp->callback = callback;
+}
+
+void ast_rtp_setnat(struct ast_rtp *rtp, int nat)
+{
+ rtp->nat = nat;
+}
+
+int ast_rtp_getnat(struct ast_rtp *rtp)
+{
+ return ast_test_flag(rtp, FLAG_NAT_ACTIVE);
+}
+
+void ast_rtp_setdtmf(struct ast_rtp *rtp, int dtmf)
+{
+ ast_set2_flag(rtp, dtmf ? 1 : 0, FLAG_HAS_DTMF);
+}
+
+void ast_rtp_setdtmfcompensate(struct ast_rtp *rtp, int compensate)
+{
+ ast_set2_flag(rtp, compensate ? 1 : 0, FLAG_DTMF_COMPENSATE);
+}
+
+void ast_rtp_setstun(struct ast_rtp *rtp, int stun_enable)
+{
+ ast_set2_flag(rtp, stun_enable ? 1 : 0, FLAG_HAS_STUN);
+}
+
+static void rtp_bridge_lock(struct ast_rtp *rtp)
+{
+#ifdef P2P_INTENSE
+ ast_mutex_lock(&rtp->bridge_lock);
+#endif
+ return;
+}
+
+static void rtp_bridge_unlock(struct ast_rtp *rtp)
+{
+#ifdef P2P_INTENSE
+ ast_mutex_unlock(&rtp->bridge_lock);
+#endif
+ return;
+}
+
+static struct ast_frame *send_dtmf(struct ast_rtp *rtp, enum ast_frame_type type)
+{
+ if (((ast_test_flag(rtp, FLAG_DTMF_COMPENSATE) && type == AST_FRAME_DTMF_END) ||
+ (type == AST_FRAME_DTMF_BEGIN)) && ast_tvcmp(ast_tvnow(), rtp->dtmfmute) < 0) {
+ ast_debug(1, "Ignore potential DTMF echo from '%s'\n", ast_inet_ntoa(rtp->them.sin_addr));
+ rtp->resp = 0;
+ rtp->dtmfsamples = 0;
+ return &ast_null_frame;
+ }
+ ast_debug(1, "Sending dtmf: %d (%c), at %s\n", rtp->resp, rtp->resp, ast_inet_ntoa(rtp->them.sin_addr));
+ if (rtp->resp == 'X') {
+ rtp->f.frametype = AST_FRAME_CONTROL;
+ rtp->f.subclass = AST_CONTROL_FLASH;
+ } else {
+ rtp->f.frametype = type;
+ rtp->f.subclass = rtp->resp;
+ }
+ rtp->f.datalen = 0;
+ rtp->f.samples = 0;
+ rtp->f.mallocd = 0;
+ rtp->f.src = "RTP";
+ return &rtp->f;
+
+}
+
+static inline int rtp_debug_test_addr(struct sockaddr_in *addr)
+{
+ if (rtpdebug == 0)
+ return 0;
+ if (rtpdebugaddr.sin_addr.s_addr) {
+ if (((ntohs(rtpdebugaddr.sin_port) != 0)
+ && (rtpdebugaddr.sin_port != addr->sin_port))
+ || (rtpdebugaddr.sin_addr.s_addr != addr->sin_addr.s_addr))
+ return 0;
+ }
+ return 1;
+}
+
+static inline int rtcp_debug_test_addr(struct sockaddr_in *addr)
+{
+ if (rtcpdebug == 0)
+ return 0;
+ if (rtcpdebugaddr.sin_addr.s_addr) {
+ if (((ntohs(rtcpdebugaddr.sin_port) != 0)
+ && (rtcpdebugaddr.sin_port != addr->sin_port))
+ || (rtcpdebugaddr.sin_addr.s_addr != addr->sin_addr.s_addr))
+ return 0;
+ }
+ return 1;
+}
+
+
+static struct ast_frame *process_cisco_dtmf(struct ast_rtp *rtp, unsigned char *data, int len)
+{
+ unsigned int event;
+ char resp = 0;
+ struct ast_frame *f = NULL;
+ unsigned char seq;
+ unsigned int flags;
+ unsigned int power;
+
+ /* We should have at least 4 bytes in RTP data */
+ if (len < 4)
+ return f;
+
+ /* The format of Cisco RTP DTMF packet looks like next:
+ +0 - sequence number of DTMF RTP packet (begins from 1,
+ wrapped to 0)
+ +1 - set of flags
+ +1 (bit 0) - flaps by different DTMF digits delimited by audio
+ or repeated digit without audio???
+ +2 (+4,+6,...) - power level? (rises from 0 to 32 at begin of tone
+ then falls to 0 at its end)
+ +3 (+5,+7,...) - detected DTMF digit (0..9,*,#,A-D,...)
+ Repeated DTMF information (bytes 4/5, 6/7) is history shifted right
+ by each new packet and thus provides some redudancy.
+
+ Sample of Cisco RTP DTMF packet is (all data in hex):
+ 19 07 00 02 12 02 20 02
+ showing end of DTMF digit '2'.
+
+ The packets
+ 27 07 00 02 0A 02 20 02
+ 28 06 20 02 00 02 0A 02
+ shows begin of new digit '2' with very short pause (20 ms) after
+ previous digit '2'. Bit +1.0 flips at begin of new digit.
+
+ Cisco RTP DTMF packets comes as replacement of audio RTP packets
+ so its uses the same sequencing and timestamping rules as replaced
+ audio packets. Repeat interval of DTMF packets is 20 ms and not rely
+ on audio framing parameters. Marker bit isn't used within stream of
+ DTMFs nor audio stream coming immediately after DTMF stream. Timestamps
+ are not sequential at borders between DTMF and audio streams,
+ */
+
+ seq = data[0];
+ flags = data[1];
+ power = data[2];
+ event = data[3] & 0x1f;
+
+ if (option_debug > 2 || rtpdebug)
+ ast_debug(0, "Cisco DTMF Digit: %02x (len=%d, seq=%d, flags=%02x, power=%d, history count=%d)\n", event, len, seq, flags, power, (len - 4) / 2);
+ if (event < 10) {
+ resp = '0' + event;
+ } else if (event < 11) {
+ resp = '*';
+ } else if (event < 12) {
+ resp = '#';
+ } else if (event < 16) {
+ resp = 'A' + (event - 12);
+ } else if (event < 17) {
+ resp = 'X';
+ }
+ if ((!rtp->resp && power) || (rtp->resp && (rtp->resp != resp))) {
+ rtp->resp = resp;
+ /* Why we should care on DTMF compensation at reception? */
+ if (!ast_test_flag(rtp, FLAG_DTMF_COMPENSATE)) {
+ f = send_dtmf(rtp, AST_FRAME_DTMF_BEGIN);
+ rtp->dtmfsamples = 0;
+ }
+ } else if ((rtp->resp == resp) && !power) {
+ f = send_dtmf(rtp, AST_FRAME_DTMF_END);
+ f->samples = rtp->dtmfsamples * 8;
+ rtp->resp = 0;
+ } else if (rtp->resp == resp)
+ rtp->dtmfsamples += 20 * 8;
+ rtp->dtmfcount = dtmftimeout;
+ return f;
+}
+
+/*!
+ * \brief Process RTP DTMF and events according to RFC 2833.
+ *
+ * RFC 2833 is "RTP Payload for DTMF Digits, Telephony Tones and Telephony Signals".
+ *
+ * \param rtp
+ * \param data
+ * \param len
+ * \param seqno
+ * \param timestamp
+ * \returns
+ */
+static struct ast_frame *process_rfc2833(struct ast_rtp *rtp, unsigned char *data, int len, unsigned int seqno, unsigned int timestamp)
+{
+ unsigned int event;
+ unsigned int event_end;
+ unsigned int samples;
+ char resp = 0;
+ struct ast_frame *f = NULL;
+
+ /* Figure out event, event end, and samples */
+ event = ntohl(*((unsigned int *)(data)));
+ event >>= 24;
+ event_end = ntohl(*((unsigned int *)(data)));
+ event_end <<= 8;
+ event_end >>= 24;
+ samples = ntohl(*((unsigned int *)(data)));
+ samples &= 0xFFFF;
+
+ /* Print out debug if turned on */
+ if (rtpdebug || option_debug > 2)
+ ast_debug(0, "- RTP 2833 Event: %08x (len = %d)\n", event, len);
+
+ /* Figure out what digit was pressed */
+ if (event < 10) {
+ resp = '0' + event;
+ } else if (event < 11) {
+ resp = '*';
+ } else if (event < 12) {
+ resp = '#';
+ } else if (event < 16) {
+ resp = 'A' + (event - 12);
+ } else if (event < 17) { /* Event 16: Hook flash */
+ resp = 'X';
+ } else {
+ /* Not a supported event */
+ ast_log(LOG_DEBUG, "Ignoring RTP 2833 Event: %08x. Not a DTMF Digit.\n", event);
+ return &ast_null_frame;
+ }
+
+ if (ast_test_flag(rtp, FLAG_DTMF_COMPENSATE)) {
+ if ((rtp->lastevent != timestamp) || (rtp->resp && rtp->resp != resp)) {
+ rtp->resp = resp;
+ f = send_dtmf(rtp, AST_FRAME_DTMF_END);
+ f->len = 0;
+ rtp->lastevent = timestamp;
+ }
+ } else {
+ if ((!(rtp->resp) && (!(event_end & 0x80))) || (rtp->resp && rtp->resp != resp)) {
+ rtp->resp = resp;
+ f = send_dtmf(rtp, AST_FRAME_DTMF_BEGIN);
+ } else if ((event_end & 0x80) && (rtp->lastevent != seqno) && rtp->resp) {
+ f = send_dtmf(rtp, AST_FRAME_DTMF_END);
+ f->len = ast_tvdiff_ms(ast_samp2tv(samples, 8000), ast_tv(0, 0)); /* XXX hard coded 8kHz */
+ rtp->resp = 0;
+ rtp->lastevent = seqno;
+ }
+ }
+
+ rtp->dtmfcount = dtmftimeout;
+ rtp->dtmfsamples = samples;
+
+ return f;
+}
+
+/*!
+ * \brief Process Comfort Noise RTP.
+ *
+ * This is incomplete at the moment.
+ *
+*/
+static struct ast_frame *process_rfc3389(struct ast_rtp *rtp, unsigned char *data, int len)
+{
+ struct ast_frame *f = NULL;
+ /* Convert comfort noise into audio with various codecs. Unfortunately this doesn't
+ totally help us out becuase we don't have an engine to keep it going and we are not
+ guaranteed to have it every 20ms or anything */
+ if (rtpdebug)
+ ast_debug(0, "- RTP 3389 Comfort noise event: Level %d (len = %d)\n", rtp->lastrxformat, len);
+
+ if (!(ast_test_flag(rtp, FLAG_3389_WARNING))) {
+ ast_log(LOG_NOTICE, "Comfort noise support incomplete in Asterisk (RFC 3389). Please turn off on client if possible. Client IP: %s\n",
+ ast_inet_ntoa(rtp->them.sin_addr));
+ ast_set_flag(rtp, FLAG_3389_WARNING);
+ }
+
+ /* Must have at least one byte */
+ if (!len)
+ return NULL;
+ if (len < 24) {
+ rtp->f.data = rtp->rawdata + AST_FRIENDLY_OFFSET;
+ rtp->f.datalen = len - 1;
+ rtp->f.offset = AST_FRIENDLY_OFFSET;
+ memcpy(rtp->f.data, data + 1, len - 1);
+ } else {
+ rtp->f.data = NULL;
+ rtp->f.offset = 0;
+ rtp->f.datalen = 0;
+ }
+ rtp->f.frametype = AST_FRAME_CNG;
+ rtp->f.subclass = data[0] & 0x7f;
+ rtp->f.datalen = len - 1;
+ rtp->f.samples = 0;
+ rtp->f.delivery.tv_usec = rtp->f.delivery.tv_sec = 0;
+ f = &rtp->f;
+ return f;
+}
+
+static int rtpread(int *id, int fd, short events, void *cbdata)
+{
+ struct ast_rtp *rtp = cbdata;
+ struct ast_frame *f;
+ f = ast_rtp_read(rtp);
+ if (f) {
+ if (rtp->callback)
+ rtp->callback(rtp, f, rtp->data);
+ }
+ return 1;
+}
+
+struct ast_frame *ast_rtcp_read(struct ast_rtp *rtp)
+{
+ socklen_t len;
+ int position, i, packetwords;
+ int res;
+ struct sockaddr_in sin;
+ unsigned int rtcpdata[8192 + AST_FRIENDLY_OFFSET];
+ unsigned int *rtcpheader;
+ int pt;
+ struct timeval now;
+ unsigned int length;
+ int rc;
+ double rttsec;
+ uint64_t rtt = 0;
+ unsigned int dlsr;
+ unsigned int lsr;
+ unsigned int msw;
+ unsigned int lsw;
+ unsigned int comp;
+ struct ast_frame *f = &ast_null_frame;
+
+ if (!rtp || !rtp->rtcp)
+ return &ast_null_frame;
+
+ len = sizeof(sin);
+
+ res = recvfrom(rtp->rtcp->s, rtcpdata + AST_FRIENDLY_OFFSET, sizeof(rtcpdata) - sizeof(unsigned int) * AST_FRIENDLY_OFFSET,
+ 0, (struct sockaddr *)&sin, &len);
+ rtcpheader = (unsigned int *)(rtcpdata + AST_FRIENDLY_OFFSET);
+
+ if (res < 0) {
+ if (errno == EBADF)
+ CRASH;
+ if (errno != EAGAIN) {
+ ast_log(LOG_WARNING, "RTCP Read error: %s. Hanging up.\n", strerror(errno));
+ return NULL;
+ }
+ return &ast_null_frame;
+ }
+
+ packetwords = res / 4;
+
+ if (rtp->nat) {
+ /* Send to whoever sent to us */
+ if ((rtp->rtcp->them.sin_addr.s_addr != sin.sin_addr.s_addr) ||
+ (rtp->rtcp->them.sin_port != sin.sin_port)) {
+ memcpy(&rtp->rtcp->them, &sin, sizeof(rtp->rtcp->them));
+ if (option_debug || rtpdebug)
+ ast_debug(0, "RTCP NAT: Got RTCP from other end. Now sending to address %s:%d\n", ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port));
+ }
+ }
+
+ ast_debug(1, "Got RTCP report of %d bytes\n", res);
+
+ /* Process a compound packet */
+ position = 0;
+ while (position < packetwords) {
+ i = position;
+ length = ntohl(rtcpheader[i]);
+ pt = (length & 0xff0000) >> 16;
+ rc = (length & 0x1f000000) >> 24;
+ length &= 0xffff;
+
+ if ((i + length) > packetwords) {
+ if (option_debug || rtpdebug)
+ ast_log(LOG_DEBUG, "RTCP Read too short\n");
+ return &ast_null_frame;
+ }
+
+ if (rtcp_debug_test_addr(&sin)) {
+ ast_verbose("\n\nGot RTCP from %s:%d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
+ ast_verbose("PT: %d(%s)\n", pt, (pt == 200) ? "Sender Report" : (pt == 201) ? "Receiver Report" : (pt == 192) ? "H.261 FUR" : "Unknown");
+ ast_verbose("Reception reports: %d\n", rc);
+ ast_verbose("SSRC of sender: %u\n", rtcpheader[i + 1]);
+ }
+
+ i += 2; /* Advance past header and ssrc */
+
+ switch (pt) {
+ case RTCP_PT_SR:
+ gettimeofday(&rtp->rtcp->rxlsr,NULL); /* To be able to populate the dlsr */
+ rtp->rtcp->spc = ntohl(rtcpheader[i+3]);
+ rtp->rtcp->soc = ntohl(rtcpheader[i + 4]);
+ rtp->rtcp->themrxlsr = ((ntohl(rtcpheader[i]) & 0x0000ffff) << 16) | ((ntohl(rtcpheader[i + 1]) & 0xffff0000) >> 16); /* Going to LSR in RR*/
+
+ if (rtcp_debug_test_addr(&sin)) {
+ ast_verbose("NTP timestamp: %lu.%010lu\n", (unsigned long) ntohl(rtcpheader[i]), (unsigned long) ntohl(rtcpheader[i + 1]) * 4096);
+ ast_verbose("RTP timestamp: %lu\n", (unsigned long) ntohl(rtcpheader[i + 2]));
+ ast_verbose("SPC: %lu\tSOC: %lu\n", (unsigned long) ntohl(rtcpheader[i + 3]), (unsigned long) ntohl(rtcpheader[i + 4]));
+ }
+ i += 5;
+ if (rc < 1)
+ break;
+ /* Intentional fall through */
+ case RTCP_PT_RR:
+ /* Don't handle multiple reception reports (rc > 1) yet */
+ /* Calculate RTT per RFC */
+ gettimeofday(&now, NULL);
+ timeval2ntp(now, &msw, &lsw);
+ if (ntohl(rtcpheader[i + 4]) && ntohl(rtcpheader[i + 5])) { /* We must have the LSR && DLSR */
+ comp = ((msw & 0xffff) << 16) | ((lsw & 0xffff0000) >> 16);
+ lsr = ntohl(rtcpheader[i + 4]);
+ dlsr = ntohl(rtcpheader[i + 5]);
+ rtt = comp - lsr - dlsr;
+
+ /* Convert end to end delay to usec (keeping the calculation in 64bit space)
+ sess->ee_delay = (eedelay * 1000) / 65536; */
+ if (rtt < 4294) {
+ rtt = (rtt * 1000000) >> 16;
+ } else {
+ rtt = (rtt * 1000) >> 16;
+ rtt *= 1000;
+ }
+ rtt = rtt / 1000.;
+ rttsec = rtt / 1000.;
+
+ if (comp - dlsr >= lsr) {
+ rtp->rtcp->accumulated_transit += rttsec;
+ rtp->rtcp->rtt = rttsec;
+ if (rtp->rtcp->maxrtt<rttsec)
+ rtp->rtcp->maxrtt = rttsec;
+ if (rtp->rtcp->minrtt>rttsec)
+ rtp->rtcp->minrtt = rttsec;
+ } else if (rtcp_debug_test_addr(&sin)) {
+ ast_verbose("Internal RTCP NTP clock skew detected: "
+ "lsr=%u, now=%u, dlsr=%u (%d:%03dms), "
+ "diff=%d\n",
+ lsr, comp, dlsr, dlsr / 65536,
+ (dlsr % 65536) * 1000 / 65536,
+ dlsr - (comp - lsr));
+ }
+ }
+
+ rtp->rtcp->reported_jitter = ntohl(rtcpheader[i + 3]);
+ rtp->rtcp->reported_lost = ntohl(rtcpheader[i + 1]) & 0xffffff;
+ if (rtcp_debug_test_addr(&sin)) {
+ ast_verbose(" Fraction lost: %ld\n", (((long) ntohl(rtcpheader[i + 1]) & 0xff000000) >> 24));
+ ast_verbose(" Packets lost so far: %d\n", rtp->rtcp->reported_lost);
+ ast_verbose(" Highest sequence number: %ld\n", (long) (ntohl(rtcpheader[i + 2]) & 0xffff));
+ ast_verbose(" Sequence number cycles: %ld\n", (long) (ntohl(rtcpheader[i + 2]) & 0xffff) >> 16);
+ ast_verbose(" Interarrival jitter: %u\n", rtp->rtcp->reported_jitter);
+ ast_verbose(" Last SR(our NTP): %lu.%010lu\n",(unsigned long) ntohl(rtcpheader[i + 4]) >> 16,((unsigned long) ntohl(rtcpheader[i + 4]) << 16) * 4096);
+ ast_verbose(" DLSR: %4.4f (sec)\n",ntohl(rtcpheader[i + 5])/65536.0);
+ if (rtt)
+ ast_verbose(" RTT: %lu(sec)\n", (unsigned long) rtt);
+ }
+ if (rtt) {
+ manager_event(EVENT_FLAG_REPORTING, "RTCPReceived", "From %s:%d\r\n"
+ "PT: %d(%s)\r\n"
+ "ReceptionReports: %d\r\n"
+ "SenderSSRC: %u\r\n"
+ "FractionLost: %ld\r\n"
+ "PacketsLost: %d\r\n"
+ "HighestSequence: %ld\r\n"
+ "SequenceNumberCycles: %ld\r\n"
+ "IAJitter: %u\r\n"
+ "LastSR: %lu.%010lu\r\n"
+ "DLSR: %4.4f(sec)\r\n"
+ "RTT: %llu(sec)\r\n",
+ ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port),
+ pt, (pt == 200) ? "Sender Report" : (pt == 201) ? "Receiver Report" : (pt == 192) ? "H.261 FUR" : "Unknown",
+ rc,
+ rtcpheader[i + 1],
+ (((long) ntohl(rtcpheader[i + 1]) & 0xff000000) >> 24),
+ rtp->rtcp->reported_lost,
+ (long) (ntohl(rtcpheader[i + 2]) & 0xffff),
+ (long) (ntohl(rtcpheader[i + 2]) & 0xffff) >> 16,
+ rtp->rtcp->reported_jitter,
+ (unsigned long) ntohl(rtcpheader[i + 4]) >> 16, ((unsigned long) ntohl(rtcpheader[i + 4]) << 16) * 4096,
+ ntohl(rtcpheader[i + 5])/65536.0,
+ (unsigned long long)rtt);
+ } else {
+ manager_event(EVENT_FLAG_REPORTING, "RTCPReceived", "From %s:%d\r\n"
+ "PT: %d(%s)\r\n"
+ "ReceptionReports: %d\r\n"
+ "SenderSSRC: %u\r\n"
+ "FractionLost: %ld\r\n"
+ "PacketsLost: %d\r\n"
+ "HighestSequence: %ld\r\n"
+ "SequenceNumberCycles: %ld\r\n"
+ "IAJitter: %u\r\n"
+ "LastSR: %lu.%010lu\r\n"
+ "DLSR: %4.4f(sec)\r\n",
+ ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port),
+ pt, (pt == 200) ? "Sender Report" : (pt == 201) ? "Receiver Report" : (pt == 192) ? "H.261 FUR" : "Unknown",
+ rc,
+ rtcpheader[i + 1],
+ (((long) ntohl(rtcpheader[i + 1]) & 0xff000000) >> 24),
+ rtp->rtcp->reported_lost,
+ (long) (ntohl(rtcpheader[i + 2]) & 0xffff),
+ (long) (ntohl(rtcpheader[i + 2]) & 0xffff) >> 16,
+ rtp->rtcp->reported_jitter,
+ (unsigned long) ntohl(rtcpheader[i + 4]) >> 16,
+ ((unsigned long) ntohl(rtcpheader[i + 4]) << 16) * 4096,
+ ntohl(rtcpheader[i + 5])/65536.0);
+ }
+ break;
+ case RTCP_PT_FUR:
+ if (rtcp_debug_test_addr(&sin))
+ ast_verbose("Received an RTCP Fast Update Request\n");
+ rtp->f.frametype = AST_FRAME_CONTROL;
+ rtp->f.subclass = AST_CONTROL_VIDUPDATE;
+ rtp->f.datalen = 0;
+ rtp->f.samples = 0;
+ rtp->f.mallocd = 0;
+ rtp->f.src = "RTP";
+ f = &rtp->f;
+ break;
+ case RTCP_PT_SDES:
+ if (rtcp_debug_test_addr(&sin))
+ ast_verbose("Received an SDES from %s:%d\n", ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port));
+ break;
+ case RTCP_PT_BYE:
+ if (rtcp_debug_test_addr(&sin))
+ ast_verbose("Received a BYE from %s:%d\n", ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port));
+ break;
+ default:
+ ast_debug(1, "Unknown RTCP packet (pt=%d) received from %s:%d\n", pt, ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port));
+ break;
+ }
+ position += (length + 1);
+ }
+
+ return f;
+}
+
+static void calc_rxstamp(struct timeval *tv, struct ast_rtp *rtp, unsigned int timestamp, int mark)
+{
+ struct timeval now;
+ double transit;
+ double current_time;
+ double d;
+ double dtv;
+ double prog;
+
+ if ((!rtp->rxcore.tv_sec && !rtp->rxcore.tv_usec) || mark) {
+ gettimeofday(&rtp->rxcore, NULL);
+ rtp->drxcore = (double) rtp->rxcore.tv_sec + (double) rtp->rxcore.tv_usec / 1000000;
+ /* map timestamp to a real time */
+ rtp->seedrxts = timestamp; /* Their RTP timestamp started with this */
+ rtp->rxcore.tv_sec -= timestamp / 8000;
+ rtp->rxcore.tv_usec -= (timestamp % 8000) * 125;
+ /* Round to 0.1ms for nice, pretty timestamps */
+ rtp->rxcore.tv_usec -= rtp->rxcore.tv_usec % 100;
+ if (rtp->rxcore.tv_usec < 0) {
+ /* Adjust appropriately if necessary */
+ rtp->rxcore.tv_usec += 1000000;
+ rtp->rxcore.tv_sec -= 1;
+ }
+ }
+
+ gettimeofday(&now,NULL);
+ /* rxcore is the mapping between the RTP timestamp and _our_ real time from gettimeofday() */
+ tv->tv_sec = rtp->rxcore.tv_sec + timestamp / 8000;
+ tv->tv_usec = rtp->rxcore.tv_usec + (timestamp % 8000) * 125;
+ if (tv->tv_usec >= 1000000) {
+ tv->tv_usec -= 1000000;
+ tv->tv_sec += 1;
+ }
+ prog = (double)((timestamp-rtp->seedrxts)/8000.);
+ dtv = (double)rtp->drxcore + (double)(prog);
+ current_time = (double)now.tv_sec + (double)now.tv_usec/1000000;
+ transit = current_time - dtv;
+ d = transit - rtp->rxtransit;
+ rtp->rxtransit = transit;
+ if (d<0)
+ d=-d;
+ rtp->rxjitter += (1./16.) * (d - rtp->rxjitter);
+ if (rtp->rtcp && rtp->rxjitter > rtp->rtcp->maxrxjitter)
+ rtp->rtcp->maxrxjitter = rtp->rxjitter;
+ if (rtp->rtcp && rtp->rxjitter < rtp->rtcp->minrxjitter)
+ rtp->rtcp->minrxjitter = rtp->rxjitter;
+}
+
+/*! \brief Perform a Packet2Packet RTP write */
+static int bridge_p2p_rtp_write(struct ast_rtp *rtp, struct ast_rtp *bridged, unsigned int *rtpheader, int len, int hdrlen)
+{
+ int res = 0, payload = 0, bridged_payload = 0, mark;
+ struct rtpPayloadType rtpPT;
+ int reconstruct = ntohl(rtpheader[0]);
+
+ /* Get fields from packet */
+ payload = (reconstruct & 0x7f0000) >> 16;
+ mark = (((reconstruct & 0x800000) >> 23) != 0);
+
+ /* Check what the payload value should be */
+ rtpPT = ast_rtp_lookup_pt(rtp, payload);
+
+ /* If the payload coming in is not one of the negotiated ones then send it to the core, this will cause formats to change and the bridge to break */
+ if (!bridged->current_RTP_PT[payload].code)
+ return -1;
+
+ /* If the payload is DTMF, and we are listening for DTMF - then feed it into the core */
+ if (ast_test_flag(rtp, FLAG_P2P_NEED_DTMF) && !rtpPT.isAstFormat && rtpPT.code == AST_RTP_DTMF)
+ return -1;
+
+ /* Otherwise adjust bridged payload to match */
+ bridged_payload = ast_rtp_lookup_code(bridged, rtpPT.isAstFormat, rtpPT.code);
+
+ /* If the mark bit has not been sent yet... do it now */
+ if (!ast_test_flag(rtp, FLAG_P2P_SENT_MARK)) {
+ mark = 1;
+ ast_set_flag(rtp, FLAG_P2P_SENT_MARK);
+ }
+
+ /* Reconstruct part of the packet */
+ reconstruct &= 0xFF80FFFF;
+ reconstruct |= (bridged_payload << 16);
+ reconstruct |= (mark << 23);
+ rtpheader[0] = htonl(reconstruct);
+
+ /* Send the packet back out */
+ res = sendto(bridged->s, (void *)rtpheader, len, 0, (struct sockaddr *)&bridged->them, sizeof(bridged->them));
+ if (res < 0) {
+ if (!bridged->nat || (bridged->nat && (ast_test_flag(bridged, FLAG_NAT_ACTIVE) == FLAG_NAT_ACTIVE))) {
+ ast_debug(1, "RTP Transmission error of packet to %s:%d: %s\n", ast_inet_ntoa(bridged->them.sin_addr), ntohs(bridged->them.sin_port), strerror(errno));
+ } else if (((ast_test_flag(bridged, FLAG_NAT_ACTIVE) == FLAG_NAT_INACTIVE) || rtpdebug) && !ast_test_flag(bridged, FLAG_NAT_INACTIVE_NOWARN)) {
+ if (option_debug || rtpdebug)
+ ast_debug(0, "RTP NAT: Can't write RTP to private address %s:%d, waiting for other end to send audio...\n", ast_inet_ntoa(bridged->them.sin_addr), ntohs(bridged->them.sin_port));
+ ast_set_flag(bridged, FLAG_NAT_INACTIVE_NOWARN);
+ }
+ return 0;
+ } else if (rtp_debug_test_addr(&bridged->them))
+ ast_verbose("Sent RTP P2P packet to %s:%u (type %-2.2d, len %-6.6u)\n", ast_inet_ntoa(bridged->them.sin_addr), ntohs(bridged->them.sin_port), bridged_payload, len - hdrlen);
+
+ return 0;
+}
+
+struct ast_frame *ast_rtp_read(struct ast_rtp *rtp)
+{
+ int res;
+ struct sockaddr_in sin;
+ socklen_t len;
+ unsigned int seqno;
+ int version;
+ int payloadtype;
+ int hdrlen = 12;
+ int padding;
+ int mark;
+ int ext;
+ int cc;
+ unsigned int ssrc;
+ unsigned int timestamp;
+ unsigned int *rtpheader;
+ struct rtpPayloadType rtpPT;
+ struct ast_rtp *bridged = NULL;
+
+ /* If time is up, kill it */
+ if (rtp->sending_digit)
+ ast_rtp_senddigit_continuation(rtp);
+
+ len = sizeof(sin);
+
+ /* Cache where the header will go */
+ res = recvfrom(rtp->s, rtp->rawdata + AST_FRIENDLY_OFFSET, sizeof(rtp->rawdata) - AST_FRIENDLY_OFFSET,
+ 0, (struct sockaddr *)&sin, &len);
+
+ rtpheader = (unsigned int *)(rtp->rawdata + AST_FRIENDLY_OFFSET);
+ if (res < 0) {
+ if (errno == EBADF)
+ CRASH;
+ if (errno != EAGAIN) {
+ ast_log(LOG_WARNING, "RTP Read error: %s. Hanging up.\n", strerror(errno));
+ return NULL;
+ }
+ return &ast_null_frame;
+ }
+
+ if (res < hdrlen) {
+ ast_log(LOG_WARNING, "RTP Read too short\n");
+ return &ast_null_frame;
+ }
+
+ /* Get fields */
+ seqno = ntohl(rtpheader[0]);
+
+ /* Check RTP version */
+ version = (seqno & 0xC0000000) >> 30;
+ if (!version) {
+ /* If the two high bits are 0, this might be a
+ * STUN message, so process it. stun_handle_packet()
+ * answers to requests, and it returns STUN_ACCEPT
+ * if the request is valid.
+ */
+ if ((stun_handle_packet(rtp->s, &sin, rtp->rawdata + AST_FRIENDLY_OFFSET, res, NULL, NULL) == STUN_ACCEPT) &&
+ (!rtp->them.sin_port && !rtp->them.sin_addr.s_addr)) {
+ memcpy(&rtp->them, &sin, sizeof(rtp->them));
+ }
+ return &ast_null_frame;
+ }
+
+#if 0 /* Allow to receive RTP stream with closed transmission path */
+ /* If we don't have the other side's address, then ignore this */
+ if (!rtp->them.sin_addr.s_addr || !rtp->them.sin_port)
+ return &ast_null_frame;
+#endif
+
+ /* Send to whoever send to us if NAT is turned on */
+ if (rtp->nat) {
+ if ((rtp->them.sin_addr.s_addr != sin.sin_addr.s_addr) ||
+ (rtp->them.sin_port != sin.sin_port)) {
+ rtp->them = sin;
+ if (rtp->rtcp) {
+ memcpy(&rtp->rtcp->them, &sin, sizeof(rtp->rtcp->them));
+ rtp->rtcp->them.sin_port = htons(ntohs(rtp->them.sin_port)+1);
+ }
+ rtp->rxseqno = 0;
+ ast_set_flag(rtp, FLAG_NAT_ACTIVE);
+ if (option_debug || rtpdebug)
+ ast_debug(0, "RTP NAT: Got audio from other end. Now sending to address %s:%d\n", ast_inet_ntoa(rtp->them.sin_addr), ntohs(rtp->them.sin_port));
+ }
+ }
+
+ /* If we are bridged to another RTP stream, send direct */
+ if ((bridged = ast_rtp_get_bridged(rtp)) && !bridge_p2p_rtp_write(rtp, bridged, rtpheader, res, hdrlen))
+ return &ast_null_frame;
+
+ if (version != 2)
+ return &ast_null_frame;
+
+ payloadtype = (seqno & 0x7f0000) >> 16;
+ padding = seqno & (1 << 29);
+ mark = seqno & (1 << 23);
+ ext = seqno & (1 << 28);
+ cc = (seqno & 0xF000000) >> 24;
+ seqno &= 0xffff;
+ timestamp = ntohl(rtpheader[1]);
+ ssrc = ntohl(rtpheader[2]);
+
+ if (!mark && rtp->rxssrc && rtp->rxssrc != ssrc) {
+ if (option_debug || rtpdebug)
+ ast_debug(0, "Forcing Marker bit, because SSRC has changed\n");
+ mark = 1;
+ }
+
+ rtp->rxssrc = ssrc;
+
+ if (padding) {
+ /* Remove padding bytes */
+ res -= rtp->rawdata[AST_FRIENDLY_OFFSET + res - 1];
+ }
+
+ if (cc) {
+ /* CSRC fields present */
+ hdrlen += cc*4;
+ }
+
+ if (ext) {
+ /* RTP Extension present */
+ hdrlen += (ntohl(rtpheader[hdrlen/4]) & 0xffff) << 2;
+ hdrlen += 4;
+ if (option_debug) {
+ int profile;
+ profile = (ntohl(rtpheader[3]) & 0xffff0000) >> 16;
+ if (profile == 0x505a)
+ ast_debug(1, "Found Zfone extension in RTP stream - zrtp - not supported.\n");
+ else
+ ast_debug(1, "Found unknown RTP Extensions %x\n", profile);
+ }
+ }
+
+ if (res < hdrlen) {
+ ast_log(LOG_WARNING, "RTP Read too short (%d, expecting %d)\n", res, hdrlen);
+ return &ast_null_frame;
+ }
+
+ rtp->rxcount++; /* Only count reasonably valid packets, this'll make the rtcp stats more accurate */
+
+ if (rtp->rxcount==1) {
+ /* This is the first RTP packet successfully received from source */
+ rtp->seedrxseqno = seqno;
+ }
+
+ /* Do not schedule RR if RTCP isn't run */
+ if (rtp->rtcp && rtp->rtcp->them.sin_addr.s_addr && rtp->rtcp->schedid < 1) {
+ /* Schedule transmission of Receiver Report */
+ rtp->rtcp->schedid = ast_sched_add(rtp->sched, ast_rtcp_calc_interval(rtp), ast_rtcp_write, rtp);
+ }
+ if ( (int)rtp->lastrxseqno - (int)seqno > 100) /* if so it would indicate that the sender cycled; allow for misordering */
+ rtp->cycles += RTP_SEQ_MOD;
+
+ rtp->lastrxseqno = seqno;
+
+ if (rtp->themssrc==0)
+ rtp->themssrc = ntohl(rtpheader[2]); /* Record their SSRC to put in future RR */
+
+ if (rtp_debug_test_addr(&sin))
+ ast_verbose("Got RTP packet from %s:%u (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u)\n",
+ ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), payloadtype, seqno, timestamp,res - hdrlen);
+
+ rtpPT = ast_rtp_lookup_pt(rtp, payloadtype);
+ if (!rtpPT.isAstFormat) {
+ struct ast_frame *f = NULL;
+
+ /* This is special in-band data that's not one of our codecs */
+ if (rtpPT.code == AST_RTP_DTMF) {
+ /* It's special -- rfc2833 process it */
+ if (rtp_debug_test_addr(&sin)) {
+ unsigned char *data;
+ unsigned int event;
+ unsigned int event_end;
+ unsigned int duration;
+ data = rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen;
+ event = ntohl(*((unsigned int *)(data)));
+ event >>= 24;
+ event_end = ntohl(*((unsigned int *)(data)));
+ event_end <<= 8;
+ event_end >>= 24;
+ duration = ntohl(*((unsigned int *)(data)));
+ duration &= 0xFFFF;
+ ast_verbose("Got RTP RFC2833 from %s:%u (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u, mark %d, event %08x, end %d, duration %-5.5d) \n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), payloadtype, seqno, timestamp, res - hdrlen, (mark?1:0), event, ((event_end & 0x80)?1:0), duration);
+ }
+ f = process_rfc2833(rtp, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen, seqno, timestamp);
+ } else if (rtpPT.code == AST_RTP_CISCO_DTMF) {
+ /* It's really special -- process it the Cisco way */
+ if (rtp->lastevent <= seqno || (rtp->lastevent >= 65530 && seqno <= 6)) {
+ f = process_cisco_dtmf(rtp, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen);
+ rtp->lastevent = seqno;
+ }
+ } else if (rtpPT.code == AST_RTP_CN) {
+ /* Comfort Noise */
+ f = process_rfc3389(rtp, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen);
+ } else {
+ ast_log(LOG_NOTICE, "Unknown RTP codec %d received from '%s'\n", payloadtype, ast_inet_ntoa(rtp->them.sin_addr));
+ }
+ return f ? f : &ast_null_frame;
+ }
+ rtp->lastrxformat = rtp->f.subclass = rtpPT.code;
+ rtp->f.frametype = (rtp->f.subclass & AST_FORMAT_AUDIO_MASK) ? AST_FRAME_VOICE : (rtp->f.subclass & AST_FORMAT_VIDEO_MASK) ? AST_FRAME_VIDEO : AST_FRAME_TEXT;
+
+ if (!rtp->lastrxts)
+ rtp->lastrxts = timestamp;
+
+ rtp->rxseqno = seqno;
+
+ /* Record received timestamp as last received now */
+ rtp->lastrxts = timestamp;
+
+ rtp->f.mallocd = 0;
+ rtp->f.datalen = res - hdrlen;
+ rtp->f.data = rtp->rawdata + hdrlen + AST_FRIENDLY_OFFSET;
+ rtp->f.offset = hdrlen + AST_FRIENDLY_OFFSET;
+ rtp->f.seqno = seqno;
+ if (rtp->f.subclass & AST_FORMAT_AUDIO_MASK) {
+ rtp->f.samples = ast_codec_get_samples(&rtp->f);
+ if (rtp->f.subclass == AST_FORMAT_SLINEAR)
+ ast_frame_byteswap_be(&rtp->f);
+ calc_rxstamp(&rtp->f.delivery, rtp, timestamp, mark);
+ /* Add timing data to let ast_generic_bridge() put the frame into a jitterbuf */
+ ast_set_flag(&rtp->f, AST_FRFLAG_HAS_TIMING_INFO);
+ rtp->f.ts = timestamp / 8;
+ rtp->f.len = rtp->f.samples / 8;
+ } else if(rtp->f.subclass & AST_FORMAT_VIDEO_MASK) {
+ /* Video -- samples is # of samples vs. 90000 */
+ if (!rtp->lastividtimestamp)
+ rtp->lastividtimestamp = timestamp;
+ rtp->f.samples = timestamp - rtp->lastividtimestamp;
+ rtp->lastividtimestamp = timestamp;
+ rtp->f.delivery.tv_sec = 0;
+ rtp->f.delivery.tv_usec = 0;
+ /* Pass the RTP marker bit as bit 0 in the subclass field.
+ * This is ok because subclass is actually a bitmask, and
+ * the low bits represent audio formats, that are not
+ * involved here since we deal with video.
+ */
+ if (mark)
+ rtp->f.subclass |= 0x1;
+ } else {
+ /* TEXT -- samples is # of samples vs. 1000 */
+ if (!rtp->lastitexttimestamp)
+ rtp->lastitexttimestamp = timestamp;
+ rtp->f.samples = timestamp - rtp->lastitexttimestamp;
+ rtp->lastitexttimestamp = timestamp;
+ rtp->f.delivery.tv_sec = 0;
+ rtp->f.delivery.tv_usec = 0;
+ }
+ rtp->f.src = "RTP";
+ return &rtp->f;
+}
+
+/* The following array defines the MIME Media type (and subtype) for each
+ of our codecs, or RTP-specific data type. */
+static struct {
+ struct rtpPayloadType payloadType;
+ char* type;
+ char* subtype;
+} mimeTypes[] = {
+ {{1, AST_FORMAT_G723_1}, "audio", "G723"},
+ {{1, AST_FORMAT_GSM}, "audio", "GSM"},
+ {{1, AST_FORMAT_ULAW}, "audio", "PCMU"},
+ {{1, AST_FORMAT_ULAW}, "audio", "G711U"},
+ {{1, AST_FORMAT_ALAW}, "audio", "PCMA"},
+ {{1, AST_FORMAT_ALAW}, "audio", "G711A"},
+ {{1, AST_FORMAT_G726}, "audio", "G726-32"},
+ {{1, AST_FORMAT_ADPCM}, "audio", "DVI4"},
+ {{1, AST_FORMAT_SLINEAR}, "audio", "L16"},
+ {{1, AST_FORMAT_LPC10}, "audio", "LPC"},
+ {{1, AST_FORMAT_G729A}, "audio", "G729"},
+ {{1, AST_FORMAT_G729A}, "audio", "G729A"},
+ {{1, AST_FORMAT_SPEEX}, "audio", "speex"},
+ {{1, AST_FORMAT_ILBC}, "audio", "iLBC"},
+ {{1, AST_FORMAT_G722}, "audio", "G722"},
+ {{1, AST_FORMAT_G726_AAL2}, "audio", "AAL2-G726-32"},
+ {{0, AST_RTP_DTMF}, "audio", "telephone-event"},
+ {{0, AST_RTP_CISCO_DTMF}, "audio", "cisco-telephone-event"},
+ {{0, AST_RTP_CN}, "audio", "CN"},
+ {{1, AST_FORMAT_JPEG}, "video", "JPEG"},
+ {{1, AST_FORMAT_PNG}, "video", "PNG"},
+ {{1, AST_FORMAT_H261}, "video", "H261"},
+ {{1, AST_FORMAT_H263}, "video", "H263"},
+ {{1, AST_FORMAT_H263_PLUS}, "video", "h263-1998"},
+ {{1, AST_FORMAT_H264}, "video", "H264"},
+ {{1, AST_FORMAT_MP4_VIDEO}, "video", "MP4V-ES"},
+ {{1, AST_FORMAT_T140}, "text", "T140"},
+};
+
+/*!
+ * \brief Mapping between Asterisk codecs and rtp payload types
+ *
+ * Static (i.e., well-known) RTP payload types for our "AST_FORMAT..."s:
+ * also, our own choices for dynamic payload types. This is our master
+ * table for transmission
+ *
+ * See http://www.iana.org/assignments/rtp-parameters for a list of
+ * assigned values
+ */
+static struct rtpPayloadType static_RTP_PT[MAX_RTP_PT] = {
+ [0] = {1, AST_FORMAT_ULAW},
+#ifdef USE_DEPRECATED_G726
+ [2] = {1, AST_FORMAT_G726}, /* Technically this is G.721, but if Cisco can do it, so can we... */
+#endif
+ [3] = {1, AST_FORMAT_GSM},
+ [4] = {1, AST_FORMAT_G723_1},
+ [5] = {1, AST_FORMAT_ADPCM}, /* 8 kHz */
+ [6] = {1, AST_FORMAT_ADPCM}, /* 16 kHz */
+ [7] = {1, AST_FORMAT_LPC10},
+ [8] = {1, AST_FORMAT_ALAW},
+ [9] = {1, AST_FORMAT_G722},
+ [10] = {1, AST_FORMAT_SLINEAR}, /* 2 channels */
+ [11] = {1, AST_FORMAT_SLINEAR}, /* 1 channel */
+ [13] = {0, AST_RTP_CN},
+ [16] = {1, AST_FORMAT_ADPCM}, /* 11.025 kHz */
+ [17] = {1, AST_FORMAT_ADPCM}, /* 22.050 kHz */
+ [18] = {1, AST_FORMAT_G729A},
+ [19] = {0, AST_RTP_CN}, /* Also used for CN */
+ [26] = {1, AST_FORMAT_JPEG},
+ [31] = {1, AST_FORMAT_H261},
+ [34] = {1, AST_FORMAT_H263},
+ [97] = {1, AST_FORMAT_ILBC},
+ [98] = {1, AST_FORMAT_H263_PLUS},
+ [99] = {1, AST_FORMAT_H264},
+ [101] = {0, AST_RTP_DTMF},
+ [102] = {1, AST_FORMAT_T140}, /* Real time text chat */
+ [103] = {1, AST_FORMAT_H263_PLUS},
+ [104] = {1, AST_FORMAT_MP4_VIDEO},
+ [110] = {1, AST_FORMAT_SPEEX},
+ [111] = {1, AST_FORMAT_G726},
+ [112] = {1, AST_FORMAT_G726_AAL2},
+ [121] = {0, AST_RTP_CISCO_DTMF}, /* Must be type 121 */
+};
+
+void ast_rtp_pt_clear(struct ast_rtp* rtp)
+{
+ int i;
+
+ if (!rtp)
+ return;
+
+ rtp_bridge_lock(rtp);
+
+ for (i = 0; i < MAX_RTP_PT; ++i) {
+ rtp->current_RTP_PT[i].isAstFormat = 0;
+ rtp->current_RTP_PT[i].code = 0;
+ }
+
+ rtp->rtp_lookup_code_cache_isAstFormat = 0;
+ rtp->rtp_lookup_code_cache_code = 0;
+ rtp->rtp_lookup_code_cache_result = 0;
+
+ rtp_bridge_unlock(rtp);
+}
+
+void ast_rtp_pt_default(struct ast_rtp* rtp)
+{
+ int i;
+
+ rtp_bridge_lock(rtp);
+
+ /* Initialize to default payload types */
+ for (i = 0; i < MAX_RTP_PT; ++i) {
+ rtp->current_RTP_PT[i].isAstFormat = static_RTP_PT[i].isAstFormat;
+ rtp->current_RTP_PT[i].code = static_RTP_PT[i].code;
+ }
+
+ rtp->rtp_lookup_code_cache_isAstFormat = 0;
+ rtp->rtp_lookup_code_cache_code = 0;
+ rtp->rtp_lookup_code_cache_result = 0;
+
+ rtp_bridge_unlock(rtp);
+}
+
+void ast_rtp_pt_copy(struct ast_rtp *dest, struct ast_rtp *src)
+{
+ unsigned int i;
+
+ rtp_bridge_lock(dest);
+ rtp_bridge_lock(src);
+
+ for (i=0; i < MAX_RTP_PT; ++i) {
+ dest->current_RTP_PT[i].isAstFormat =
+ src->current_RTP_PT[i].isAstFormat;
+ dest->current_RTP_PT[i].code =
+ src->current_RTP_PT[i].code;
+ }
+ dest->rtp_lookup_code_cache_isAstFormat = 0;
+ dest->rtp_lookup_code_cache_code = 0;
+ dest->rtp_lookup_code_cache_result = 0;
+
+ rtp_bridge_unlock(src);
+ rtp_bridge_unlock(dest);
+}
+
+/*! \brief Get channel driver interface structure */
+static struct ast_rtp_protocol *get_proto(struct ast_channel *chan)
+{
+ struct ast_rtp_protocol *cur = NULL;
+
+ AST_RWLIST_RDLOCK(&protos);
+ AST_RWLIST_TRAVERSE(&protos, cur, list) {
+ if (cur->type == chan->tech->type)
+ break;
+ }
+ AST_RWLIST_UNLOCK(&protos);
+
+ return cur;
+}
+
+int ast_rtp_early_bridge(struct ast_channel *c0, struct ast_channel *c1)
+{
+ // dest = c0, src = c1
+ struct ast_rtp *destp = NULL, *srcp = NULL; /* Audio RTP Channels */
+ struct ast_rtp *vdestp = NULL, *vsrcp = NULL; /* Video RTP channels */
+ struct ast_rtp *tdestp = NULL, *tsrcp = NULL; /* Text RTP channels */
+ struct ast_rtp_protocol *destpr = NULL, *srcpr = NULL;
+ enum ast_rtp_get_result audio_dest_res = AST_RTP_GET_FAILED, video_dest_res = AST_RTP_GET_FAILED, text_dest_res = AST_RTP_GET_FAILED;
+ enum ast_rtp_get_result audio_src_res = AST_RTP_GET_FAILED, video_src_res = AST_RTP_GET_FAILED, text_src_res = AST_RTP_GET_FAILED;
+ int srccodec, destcodec, nat_active = 0;
+
+ /* Lock channels */
+ ast_channel_lock(c0);
+ if (c1) {
+ while (ast_channel_trylock(c1)) {
+ ast_channel_unlock(c0);
+ usleep(1);
+ ast_channel_lock(c0);
+ }
+ }
+
+ /* Find channel driver interfaces */
+ destpr = get_proto(c0);
+ if (c1)
+ srcpr = get_proto(c1);
+ if (!destpr) {
+ ast_debug(1, "Channel '%s' has no RTP, not doing anything\n", c0->name);
+ ast_channel_unlock(c0);
+ if (c1)
+ ast_channel_unlock(c1);
+ return -1;
+ }
+ if (!srcpr) {
+ ast_debug(1, "Channel '%s' has no RTP, not doing anything\n", c1 ? c1->name : "<unspecified>");
+ ast_channel_unlock(c0);
+ if (c1)
+ ast_channel_unlock(c1);
+ return -1;
+ }
+
+ /* Get audio, video and text interface (if native bridge is possible) */
+ audio_dest_res = destpr->get_rtp_info(c0, &destp);
+ video_dest_res = destpr->get_vrtp_info ? destpr->get_vrtp_info(c0, &vdestp) : AST_RTP_GET_FAILED;
+ text_dest_res = destpr->get_trtp_info ? destpr->get_trtp_info(c0, &tdestp) : AST_RTP_GET_FAILED;
+ if (srcpr) {
+ audio_src_res = srcpr->get_rtp_info(c1, &srcp);
+ video_src_res = srcpr->get_vrtp_info ? srcpr->get_vrtp_info(c1, &vsrcp) : AST_RTP_GET_FAILED;
+ text_src_res = srcpr->get_trtp_info ? srcpr->get_trtp_info(c1, &tsrcp) : AST_RTP_GET_FAILED;
+ }
+
+ /* Check if bridge is still possible (In SIP canreinvite=no stops this, like NAT) */
+ if (audio_dest_res != AST_RTP_TRY_NATIVE) {
+ /* Somebody doesn't want to play... */
+ ast_channel_unlock(c0);
+ if (c1)
+ ast_channel_unlock(c1);
+ return -1;
+ }
+ if (audio_src_res == AST_RTP_TRY_NATIVE && srcpr->get_codec)
+ srccodec = srcpr->get_codec(c1);
+ else
+ srccodec = 0;
+ if (audio_dest_res == AST_RTP_TRY_NATIVE && destpr->get_codec)
+ destcodec = destpr->get_codec(c0);
+ else
+ destcodec = 0;
+ /* Ensure we have at least one matching codec */
+ if (!(srccodec & destcodec)) {
+ ast_channel_unlock(c0);
+ if (c1)
+ ast_channel_unlock(c1);
+ return 0;
+ }
+ /* Consider empty media as non-existant */
+ if (audio_src_res == AST_RTP_TRY_NATIVE && !srcp->them.sin_addr.s_addr)
+ srcp = NULL;
+ if (srcp && (srcp->nat || ast_test_flag(srcp, FLAG_NAT_ACTIVE)))
+ nat_active = 1;
+ /* Bridge media early */
+ if (destpr->set_rtp_peer(c0, srcp, vsrcp, tsrcp, srccodec, nat_active))
+ ast_log(LOG_WARNING, "Channel '%s' failed to setup early bridge to '%s'\n", c0->name, c1 ? c1->name : "<unspecified>");
+ ast_channel_unlock(c0);
+ if (c1)
+ ast_channel_unlock(c1);
+ ast_debug(1, "Setting early bridge SDP of '%s' with that of '%s'\n", c0->name, c1 ? c1->name : "<unspecified>");
+ return 0;
+}
+
+int ast_rtp_make_compatible(struct ast_channel *dest, struct ast_channel *src, int media)
+{
+ struct ast_rtp *destp = NULL, *srcp = NULL; /* Audio RTP Channels */
+ struct ast_rtp *vdestp = NULL, *vsrcp = NULL; /* Video RTP channels */
+ struct ast_rtp *tdestp = NULL, *tsrcp = NULL; /* Text RTP channels */
+ struct ast_rtp_protocol *destpr = NULL, *srcpr = NULL;
+ enum ast_rtp_get_result audio_dest_res = AST_RTP_GET_FAILED, video_dest_res = AST_RTP_GET_FAILED, text_dest_res = AST_RTP_GET_FAILED;
+ enum ast_rtp_get_result audio_src_res = AST_RTP_GET_FAILED, video_src_res = AST_RTP_GET_FAILED, text_src_res = AST_RTP_GET_FAILED;
+ int srccodec, destcodec;
+
+ /* Lock channels */
+ ast_channel_lock(dest);
+ while (ast_channel_trylock(src)) {
+ ast_channel_unlock(dest);
+ usleep(1);
+ ast_channel_lock(dest);
+ }
+
+ /* Find channel driver interfaces */
+ if (!(destpr = get_proto(dest))) {
+ ast_debug(1, "Channel '%s' has no RTP, not doing anything\n", dest->name);
+ ast_channel_unlock(dest);
+ ast_channel_unlock(src);
+ return 0;
+ }
+ if (!(srcpr = get_proto(src))) {
+ ast_debug(1, "Channel '%s' has no RTP, not doing anything\n", src->name);
+ ast_channel_unlock(dest);
+ ast_channel_unlock(src);
+ return 0;
+ }
+
+ /* Get audio and video interface (if native bridge is possible) */
+ audio_dest_res = destpr->get_rtp_info(dest, &destp);
+ video_dest_res = destpr->get_vrtp_info ? destpr->get_vrtp_info(dest, &vdestp) : AST_RTP_GET_FAILED;
+ text_dest_res = destpr->get_trtp_info ? destpr->get_trtp_info(dest, &tdestp) : AST_RTP_GET_FAILED;
+ audio_src_res = srcpr->get_rtp_info(src, &srcp);
+ video_src_res = srcpr->get_vrtp_info ? srcpr->get_vrtp_info(src, &vsrcp) : AST_RTP_GET_FAILED;
+ text_src_res = srcpr->get_trtp_info ? srcpr->get_trtp_info(src, &tsrcp) : AST_RTP_GET_FAILED;
+
+ /* Ensure we have at least one matching codec */
+ if (srcpr->get_codec)
+ srccodec = srcpr->get_codec(src);
+ else
+ srccodec = 0;
+ if (destpr->get_codec)
+ destcodec = destpr->get_codec(dest);
+ else
+ destcodec = 0;
+
+ /* Check if bridge is still possible (In SIP canreinvite=no stops this, like NAT) */
+ if (audio_dest_res != AST_RTP_TRY_NATIVE || audio_src_res != AST_RTP_TRY_NATIVE || !(srccodec & destcodec)) {
+ /* Somebody doesn't want to play... */
+ ast_channel_unlock(dest);
+ ast_channel_unlock(src);
+ return 0;
+ }
+ ast_rtp_pt_copy(destp, srcp);
+ if (vdestp && vsrcp)
+ ast_rtp_pt_copy(vdestp, vsrcp);
+ if (tdestp && tsrcp)
+ ast_rtp_pt_copy(tdestp, tsrcp);
+ if (media) {
+ /* Bridge early */
+ if (destpr->set_rtp_peer(dest, srcp, vsrcp, tsrcp, srccodec, ast_test_flag(srcp, FLAG_NAT_ACTIVE)))
+ ast_log(LOG_WARNING, "Channel '%s' failed to setup early bridge to '%s'\n", dest->name, src->name);
+ }
+ ast_channel_unlock(dest);
+ ast_channel_unlock(src);
+ ast_debug(1, "Seeded SDP of '%s' with that of '%s'\n", dest->name, src->name);
+ return 1;
+}
+
+/*! \brief Make a note of a RTP payload type that was seen in a SDP "m=" line.
+ * By default, use the well-known value for this type (although it may
+ * still be set to a different value by a subsequent "a=rtpmap:" line)
+ */
+void ast_rtp_set_m_type(struct ast_rtp* rtp, int pt)
+{
+ if (pt < 0 || pt > MAX_RTP_PT || static_RTP_PT[pt].code == 0)
+ return; /* bogus payload type */
+
+ rtp_bridge_lock(rtp);
+ rtp->current_RTP_PT[pt] = static_RTP_PT[pt];
+ rtp_bridge_unlock(rtp);
+}
+
+/*! \brief remove setting from payload type list if the rtpmap header indicates
+ an unknown media type */
+void ast_rtp_unset_m_type(struct ast_rtp* rtp, int pt)
+{
+ rtp_bridge_lock(rtp);
+ rtp->current_RTP_PT[pt].isAstFormat = 0;
+ rtp->current_RTP_PT[pt].code = 0;
+ rtp_bridge_unlock(rtp);
+}
+
+/*! \brief Make a note of a RTP payload type (with MIME type) that was seen in
+ * an SDP "a=rtpmap:" line.
+ * \return 0 if the MIME type was found and set, -1 if it wasn't found
+ */
+int ast_rtp_set_rtpmap_type(struct ast_rtp *rtp, int pt,
+ char *mimeType, char *mimeSubtype,
+ enum ast_rtp_options options)
+{
+ unsigned int i;
+ int found = 0;
+
+ if (pt < 0 || pt > MAX_RTP_PT)
+ return -1; /* bogus payload type */
+
+ rtp_bridge_lock(rtp);
+
+ for (i = 0; i < sizeof(mimeTypes)/sizeof(mimeTypes[0]); ++i) {
+ if (strcasecmp(mimeSubtype, mimeTypes[i].subtype) == 0 &&
+ strcasecmp(mimeType, mimeTypes[i].type) == 0) {
+ found = 1;
+ rtp->current_RTP_PT[pt] = mimeTypes[i].payloadType;
+ if ((mimeTypes[i].payloadType.code == AST_FORMAT_G726) &&
+ mimeTypes[i].payloadType.isAstFormat &&
+ (options & AST_RTP_OPT_G726_NONSTANDARD))
+ rtp->current_RTP_PT[pt].code = AST_FORMAT_G726_AAL2;
+ break;
+ }
+ }
+
+ rtp_bridge_unlock(rtp);
+
+ return (found ? 0 : -1);
+}
+
+/*! \brief Return the union of all of the codecs that were set by rtp_set...() calls
+ * They're returned as two distinct sets: AST_FORMATs, and AST_RTPs */
+void ast_rtp_get_current_formats(struct ast_rtp* rtp,
+ int* astFormats, int* nonAstFormats)
+{
+ int pt;
+
+ rtp_bridge_lock(rtp);
+
+ *astFormats = *nonAstFormats = 0;
+ for (pt = 0; pt < MAX_RTP_PT; ++pt) {
+ if (rtp->current_RTP_PT[pt].isAstFormat) {
+ *astFormats |= rtp->current_RTP_PT[pt].code;
+ } else {
+ *nonAstFormats |= rtp->current_RTP_PT[pt].code;
+ }
+ }
+
+ rtp_bridge_unlock(rtp);
+
+ return;
+}
+
+struct rtpPayloadType ast_rtp_lookup_pt(struct ast_rtp* rtp, int pt)
+{
+ struct rtpPayloadType result;
+
+ result.isAstFormat = result.code = 0;
+
+ if (pt < 0 || pt > MAX_RTP_PT)
+ return result; /* bogus payload type */
+
+ /* Start with negotiated codecs */
+ rtp_bridge_lock(rtp);
+ result = rtp->current_RTP_PT[pt];
+ rtp_bridge_unlock(rtp);
+
+ /* If it doesn't exist, check our static RTP type list, just in case */
+ if (!result.code)
+ result = static_RTP_PT[pt];
+
+ return result;
+}
+
+/*! \brief Looks up an RTP code out of our *static* outbound list */
+int ast_rtp_lookup_code(struct ast_rtp* rtp, const int isAstFormat, const int code)
+{
+ int pt = 0;
+
+ rtp_bridge_lock(rtp);
+
+ if (isAstFormat == rtp->rtp_lookup_code_cache_isAstFormat &&
+ code == rtp->rtp_lookup_code_cache_code) {
+ /* Use our cached mapping, to avoid the overhead of the loop below */
+ pt = rtp->rtp_lookup_code_cache_result;
+ rtp_bridge_unlock(rtp);
+ return pt;
+ }
+
+ /* Check the dynamic list first */
+ for (pt = 0; pt < MAX_RTP_PT; ++pt) {
+ if (rtp->current_RTP_PT[pt].code == code && rtp->current_RTP_PT[pt].isAstFormat == isAstFormat) {
+ rtp->rtp_lookup_code_cache_isAstFormat = isAstFormat;
+ rtp->rtp_lookup_code_cache_code = code;
+ rtp->rtp_lookup_code_cache_result = pt;
+ rtp_bridge_unlock(rtp);
+ return pt;
+ }
+ }
+
+ /* Then the static list */
+ for (pt = 0; pt < MAX_RTP_PT; ++pt) {
+ if (static_RTP_PT[pt].code == code && static_RTP_PT[pt].isAstFormat == isAstFormat) {
+ rtp->rtp_lookup_code_cache_isAstFormat = isAstFormat;
+ rtp->rtp_lookup_code_cache_code = code;
+ rtp->rtp_lookup_code_cache_result = pt;
+ rtp_bridge_unlock(rtp);
+ return pt;
+ }
+ }
+
+ rtp_bridge_unlock(rtp);
+
+ return -1;
+}
+
+const char *ast_rtp_lookup_mime_subtype(const int isAstFormat, const int code,
+ enum ast_rtp_options options)
+{
+ unsigned int i;
+
+ for (i = 0; i < sizeof(mimeTypes)/sizeof(mimeTypes[0]); ++i) {
+ if ((mimeTypes[i].payloadType.code == code) && (mimeTypes[i].payloadType.isAstFormat == isAstFormat)) {
+ if (isAstFormat &&
+ (code == AST_FORMAT_G726_AAL2) &&
+ (options & AST_RTP_OPT_G726_NONSTANDARD))
+ return "G726-32";
+ else
+ return mimeTypes[i].subtype;
+ }
+ }
+
+ return "";
+}
+
+char *ast_rtp_lookup_mime_multiple(char *buf, size_t size, const int capability,
+ const int isAstFormat, enum ast_rtp_options options)
+{
+ int format;
+ unsigned len;
+ char *end = buf;
+ char *start = buf;
+
+ if (!buf || !size)
+ return NULL;
+
+ snprintf(end, size, "0x%x (", capability);
+
+ len = strlen(end);
+ end += len;
+ size -= len;
+ start = end;
+
+ for (format = 1; format < AST_RTP_MAX; format <<= 1) {
+ if (capability & format) {
+ const char *name = ast_rtp_lookup_mime_subtype(isAstFormat, format, options);
+
+ snprintf(end, size, "%s|", name);
+ len = strlen(end);
+ end += len;
+ size -= len;
+ }
+ }
+
+ if (start == end)
+ ast_copy_string(start, "nothing)", size);
+ else if (size > 1)
+ *(end -1) = ')';
+
+ return buf;
+}
+
+/*! \brief Open RTP or RTCP socket for a session.
+ * Print a message on failure.
+ */
+static int rtp_socket(const char *type)
+{
+ int s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ if (type == NULL)
+ type = "RTP/RTCP";
+ ast_log(LOG_WARNING, "Unable to allocate %s socket: %s\n", type, strerror(errno));
+ } else {
+ long flags = fcntl(s, F_GETFL);
+ fcntl(s, F_SETFL, flags | O_NONBLOCK);
+#ifdef SO_NO_CHECK
+ if (nochecksums)
+ setsockopt(s, SOL_SOCKET, SO_NO_CHECK, &nochecksums, sizeof(nochecksums));
+#endif
+ }
+ return s;
+}
+
+/*!
+ * \brief Initialize a new RTCP session.
+ *
+ * \returns The newly initialized RTCP session.
+ */
+static struct ast_rtcp *ast_rtcp_new(void)
+{
+ struct ast_rtcp *rtcp;
+
+ if (!(rtcp = ast_calloc(1, sizeof(*rtcp))))
+ return NULL;
+ rtcp->s = rtp_socket("RTCP");
+ rtcp->us.sin_family = AF_INET;
+ rtcp->them.sin_family = AF_INET;
+
+ if (rtcp->s < 0) {
+ ast_free(rtcp);
+ return NULL;
+ }
+
+ return rtcp;
+}
+
+/*!
+ * \brief Initialize a new RTP structure.
+ *
+ */
+void ast_rtp_new_init(struct ast_rtp *rtp)
+{
+#ifdef P2P_INTENSE
+ ast_mutex_init(&rtp->bridge_lock);
+#endif
+
+ rtp->them.sin_family = AF_INET;
+ rtp->us.sin_family = AF_INET;
+ rtp->ssrc = ast_random();
+ rtp->seqno = ast_random() & 0xffff;
+ ast_set_flag(rtp, FLAG_HAS_DTMF);
+
+ return;
+}
+
+struct ast_rtp *ast_rtp_new_with_bindaddr(struct sched_context *sched, struct io_context *io, int rtcpenable, int callbackmode, struct in_addr addr)
+{
+ struct ast_rtp *rtp;
+ int x;
+ int startplace;
+
+ if (!(rtp = ast_calloc(1, sizeof(*rtp))))
+ return NULL;
+
+ ast_rtp_new_init(rtp);
+
+ rtp->s = rtp_socket("RTP");
+ if (rtp->s < 0)
+ goto fail;
+ if (sched && rtcpenable) {
+ rtp->sched = sched;
+ rtp->rtcp = ast_rtcp_new();
+ }
+
+ /*
+ * Try to bind the RTP port, x, and possibly the RTCP port, x+1 as well.
+ * Start from a random (even, by RTP spec) port number, and
+ * iterate until success or no ports are available.
+ * Note that the requirement of RTP port being even, or RTCP being the
+ * next one, cannot be enforced in presence of a NAT box because the
+ * mapping is not under our control.
+ */
+ x = (ast_random() % (rtpend-rtpstart)) + rtpstart;
+ x = x & ~1; /* make it an even number */
+ startplace = x; /* remember the starting point */
+ /* this is constant across the loop */
+ rtp->us.sin_addr = addr;
+ if (rtp->rtcp)
+ rtp->rtcp->us.sin_addr = addr;
+ for (;;) {
+ rtp->us.sin_port = htons(x);
+ if (!bind(rtp->s, (struct sockaddr *)&rtp->us, sizeof(rtp->us))) {
+ /* bind succeeded, if no rtcp then we are done */
+ if (!rtp->rtcp)
+ break;
+ /* have rtcp, try to bind it */
+ rtp->rtcp->us.sin_port = htons(x + 1);
+ if (!bind(rtp->rtcp->s, (struct sockaddr *)&rtp->rtcp->us, sizeof(rtp->rtcp->us)))
+ break; /* success again, we are really done */
+ /*
+ * RTCP bind failed, so close and recreate the
+ * already bound RTP socket for the next round.
+ */
+ close(rtp->s);
+ rtp->s = rtp_socket("RTP");
+ if (rtp->s < 0)
+ goto fail;
+ }
+ /*
+ * If we get here, there was an error in one of the bind()
+ * calls, so make sure it is nothing unexpected.
+ */
+ if (errno != EADDRINUSE) {
+ /* We got an error that wasn't expected, abort! */
+ ast_log(LOG_ERROR, "Unexpected bind error: %s\n", strerror(errno));
+ goto fail;
+ }
+ /*
+ * One of the ports is in use. For the next iteration,
+ * increment by two and handle wraparound.
+ * If we reach the starting point, then declare failure.
+ */
+ x += 2;
+ if (x > rtpend)
+ x = (rtpstart + 1) & ~1;
+ if (x == startplace) {
+ ast_log(LOG_ERROR, "No RTP ports remaining. Can't setup media stream for this call.\n");
+ goto fail;
+ }
+ }
+ rtp->sched = sched;
+ rtp->io = io;
+ if (callbackmode) {
+ rtp->ioid = ast_io_add(rtp->io, rtp->s, rtpread, AST_IO_IN, rtp);
+ ast_set_flag(rtp, FLAG_CALLBACK_MODE);
+ }
+ ast_rtp_pt_default(rtp);
+ return rtp;
+
+fail:
+ if (rtp->s >= 0)
+ close(rtp->s);
+ if (rtp->rtcp) {
+ close(rtp->rtcp->s);
+ ast_free(rtp->rtcp);
+ }
+ ast_free(rtp);
+ return NULL;
+}
+
+struct ast_rtp *ast_rtp_new(struct sched_context *sched, struct io_context *io, int rtcpenable, int callbackmode)
+{
+ struct in_addr ia;
+
+ memset(&ia, 0, sizeof(ia));
+ return ast_rtp_new_with_bindaddr(sched, io, rtcpenable, callbackmode, ia);
+}
+
+int ast_rtp_setqos(struct ast_rtp *rtp, int tos, int cos, char *desc)
+{
+ return ast_netsock_set_qos(rtp->s, tos, cos, desc);
+}
+
+void ast_rtp_set_peer(struct ast_rtp *rtp, struct sockaddr_in *them)
+{
+ rtp->them.sin_port = them->sin_port;
+ rtp->them.sin_addr = them->sin_addr;
+ if (rtp->rtcp) {
+ rtp->rtcp->them.sin_port = htons(ntohs(them->sin_port) + 1);
+ rtp->rtcp->them.sin_addr = them->sin_addr;
+ }
+ rtp->rxseqno = 0;
+}
+
+int ast_rtp_get_peer(struct ast_rtp *rtp, struct sockaddr_in *them)
+{
+ if ((them->sin_family != AF_INET) ||
+ (them->sin_port != rtp->them.sin_port) ||
+ (them->sin_addr.s_addr != rtp->them.sin_addr.s_addr)) {
+ them->sin_family = AF_INET;
+ them->sin_port = rtp->them.sin_port;
+ them->sin_addr = rtp->them.sin_addr;
+ return 1;
+ }
+ return 0;
+}
+
+void ast_rtp_get_us(struct ast_rtp *rtp, struct sockaddr_in *us)
+{
+ *us = rtp->us;
+}
+
+struct ast_rtp *ast_rtp_get_bridged(struct ast_rtp *rtp)
+{
+ struct ast_rtp *bridged = NULL;
+
+ rtp_bridge_lock(rtp);
+ bridged = rtp->bridged;
+ rtp_bridge_unlock(rtp);
+
+ return bridged;
+}
+
+void ast_rtp_stop(struct ast_rtp *rtp)
+{
+ if (rtp->rtcp && rtp->rtcp->schedid > 0) {
+ ast_sched_del(rtp->sched, rtp->rtcp->schedid);
+ rtp->rtcp->schedid = -1;
+ }
+
+ memset(&rtp->them.sin_addr, 0, sizeof(rtp->them.sin_addr));
+ memset(&rtp->them.sin_port, 0, sizeof(rtp->them.sin_port));
+ if (rtp->rtcp) {
+ memset(&rtp->rtcp->them.sin_addr, 0, sizeof(rtp->rtcp->them.sin_addr));
+ memset(&rtp->rtcp->them.sin_port, 0, sizeof(rtp->rtcp->them.sin_port));
+ }
+
+ ast_clear_flag(rtp, FLAG_P2P_SENT_MARK);
+}
+
+void ast_rtp_reset(struct ast_rtp *rtp)
+{
+ memset(&rtp->rxcore, 0, sizeof(rtp->rxcore));
+ memset(&rtp->txcore, 0, sizeof(rtp->txcore));
+ memset(&rtp->dtmfmute, 0, sizeof(rtp->dtmfmute));
+ rtp->lastts = 0;
+ rtp->lastdigitts = 0;
+ rtp->lastrxts = 0;
+ rtp->lastividtimestamp = 0;
+ rtp->lastovidtimestamp = 0;
+ rtp->lastitexttimestamp = 0;
+ rtp->lastotexttimestamp = 0;
+ rtp->lasteventseqn = 0;
+ rtp->lastevent = 0;
+ rtp->lasttxformat = 0;
+ rtp->lastrxformat = 0;
+ rtp->dtmfcount = 0;
+ rtp->dtmfsamples = 0;
+ rtp->seqno = 0;
+ rtp->rxseqno = 0;
+}
+
+char *ast_rtp_get_quality(struct ast_rtp *rtp, struct ast_rtp_quality *qual)
+{
+ /*
+ *ssrc our ssrc
+ *themssrc their ssrc
+ *lp lost packets
+ *rxjitter our calculated jitter(rx)
+ *rxcount no. received packets
+ *txjitter reported jitter of the other end
+ *txcount transmitted packets
+ *rlp remote lost packets
+ *rtt round trip time
+ */
+
+ if (qual && rtp) {
+ qual->local_ssrc = rtp->ssrc;
+ qual->local_jitter = rtp->rxjitter;
+ qual->local_count = rtp->rxcount;
+ qual->remote_ssrc = rtp->themssrc;
+ qual->remote_count = rtp->txcount;
+ if (rtp->rtcp) {
+ qual->local_lostpackets = rtp->rtcp->expected_prior - rtp->rtcp->received_prior;
+ qual->remote_lostpackets = rtp->rtcp->reported_lost;
+ qual->remote_jitter = rtp->rtcp->reported_jitter / 65536.0;
+ qual->rtt = rtp->rtcp->rtt;
+ }
+ }
+ if (rtp->rtcp) {
+ snprintf(rtp->rtcp->quality, sizeof(rtp->rtcp->quality),
+ "ssrc=%u;themssrc=%u;lp=%u;rxjitter=%f;rxcount=%u;txjitter=%f;txcount=%u;rlp=%u;rtt=%f",
+ rtp->ssrc,
+ rtp->themssrc,
+ rtp->rtcp->expected_prior - rtp->rtcp->received_prior,
+ rtp->rxjitter,
+ rtp->rxcount,
+ (double)rtp->rtcp->reported_jitter / 65536.0,
+ rtp->txcount,
+ rtp->rtcp->reported_lost,
+ rtp->rtcp->rtt);
+ return rtp->rtcp->quality;
+ } else
+ return "<Unknown> - RTP/RTCP has already been destroyed";
+}
+
+void ast_rtp_destroy(struct ast_rtp *rtp)
+{
+ if (rtcp_debug_test_addr(&rtp->them) || rtcpstats) {
+ /*Print some info on the call here */
+ ast_verbose(" RTP-stats\n");
+ ast_verbose("* Our Receiver:\n");
+ ast_verbose(" SSRC: %u\n", rtp->themssrc);
+ ast_verbose(" Received packets: %u\n", rtp->rxcount);
+ ast_verbose(" Lost packets: %u\n", rtp->rtcp->expected_prior - rtp->rtcp->received_prior);
+ ast_verbose(" Jitter: %.4f\n", rtp->rxjitter);
+ ast_verbose(" Transit: %.4f\n", rtp->rxtransit);
+ ast_verbose(" RR-count: %u\n", rtp->rtcp->rr_count);
+ ast_verbose("* Our Sender:\n");
+ ast_verbose(" SSRC: %u\n", rtp->ssrc);
+ ast_verbose(" Sent packets: %u\n", rtp->txcount);
+ ast_verbose(" Lost packets: %u\n", rtp->rtcp->reported_lost);
+ ast_verbose(" Jitter: %u\n", rtp->rtcp->reported_jitter / (unsigned int)65536.0);
+ ast_verbose(" SR-count: %u\n", rtp->rtcp->sr_count);
+ ast_verbose(" RTT: %f\n", rtp->rtcp->rtt);
+ }
+
+ manager_event(EVENT_FLAG_REPORTING, "RTPReceiverStat", "SSRC: %u\r\n"
+ "ReceivedPackets: %u\r\n"
+ "LostPackets: %u\r\n"
+ "Jitter: %.4f\r\n"
+ "Transit: %.4f\r\n"
+ "RRCount: %u\r\n",
+ rtp->themssrc,
+ rtp->rxcount,
+ rtp->rtcp->expected_prior - rtp->rtcp->received_prior,
+ rtp->rxjitter,
+ rtp->rxtransit,
+ rtp->rtcp->rr_count);
+ manager_event(EVENT_FLAG_REPORTING, "RTPSenderStat", "SSRC: %u\r\n"
+ "SentPackets: %u\r\n"
+ "LostPackets: %u\r\n"
+ "Jitter: %u\r\n"
+ "SRCount: %u\r\n"
+ "RTT: %f\r\n",
+ rtp->ssrc,
+ rtp->txcount,
+ rtp->rtcp->reported_lost,
+ rtp->rtcp->reported_jitter,
+ rtp->rtcp->sr_count,
+ rtp->rtcp->rtt);
+ if (rtp->smoother)
+ ast_smoother_free(rtp->smoother);
+ if (rtp->ioid)
+ ast_io_remove(rtp->io, rtp->ioid);
+ if (rtp->s > -1)
+ close(rtp->s);
+ if (rtp->rtcp) {
+ if (rtp->rtcp->schedid > 0)
+ ast_sched_del(rtp->sched, rtp->rtcp->schedid);
+ close(rtp->rtcp->s);
+ ast_free(rtp->rtcp);
+ rtp->rtcp=NULL;
+ }
+#ifdef P2P_INTENSE
+ ast_mutex_destroy(&rtp->bridge_lock);
+#endif
+ ast_free(rtp);
+}
+
+static unsigned int calc_txstamp(struct ast_rtp *rtp, struct timeval *delivery)
+{
+ struct timeval t;
+ long ms;
+ if (ast_tvzero(rtp->txcore)) {
+ rtp->txcore = ast_tvnow();
+ /* Round to 20ms for nice, pretty timestamps */
+ rtp->txcore.tv_usec -= rtp->txcore.tv_usec % 20000;
+ }
+ /* Use previous txcore if available */
+ t = (delivery && !ast_tvzero(*delivery)) ? *delivery : ast_tvnow();
+ ms = ast_tvdiff_ms(t, rtp->txcore);
+ if (ms < 0)
+ ms = 0;
+ /* Use what we just got for next time */
+ rtp->txcore = t;
+ return (unsigned int) ms;
+}
+
+/*! \brief Send begin frames for DTMF */
+int ast_rtp_senddigit_begin(struct ast_rtp *rtp, char digit)
+{
+ unsigned int *rtpheader;
+ int hdrlen = 12, res = 0, i = 0, payload = 0;
+ char data[256];
+
+ if ((digit <= '9') && (digit >= '0'))
+ digit -= '0';
+ else if (digit == '*')
+ digit = 10;
+ else if (digit == '#')
+ digit = 11;
+ else if ((digit >= 'A') && (digit <= 'D'))
+ digit = digit - 'A' + 12;
+ else if ((digit >= 'a') && (digit <= 'd'))
+ digit = digit - 'a' + 12;
+ else {
+ ast_log(LOG_WARNING, "Don't know how to represent '%c'\n", digit);
+ return 0;
+ }
+
+ /* If we have no peer, return immediately */
+ if (!rtp->them.sin_addr.s_addr || !rtp->them.sin_port)
+ return 0;
+
+ payload = ast_rtp_lookup_code(rtp, 0, AST_RTP_DTMF);
+
+ rtp->dtmfmute = ast_tvadd(ast_tvnow(), ast_tv(0, 500000));
+ rtp->send_duration = 160;
+
+ /* Get a pointer to the header */
+ rtpheader = (unsigned int *)data;
+ rtpheader[0] = htonl((2 << 30) | (1 << 23) | (payload << 16) | (rtp->seqno));
+ rtpheader[1] = htonl(rtp->lastdigitts);
+ rtpheader[2] = htonl(rtp->ssrc);
+
+ for (i = 0; i < 2; i++) {
+ rtpheader[3] = htonl((digit << 24) | (0xa << 16) | (rtp->send_duration));
+ res = sendto(rtp->s, (void *) rtpheader, hdrlen + 4, 0, (struct sockaddr *) &rtp->them, sizeof(rtp->them));
+ if (res < 0)
+ ast_log(LOG_ERROR, "RTP Transmission error to %s:%u: %s\n",
+ ast_inet_ntoa(rtp->them.sin_addr),
+ ntohs(rtp->them.sin_port), strerror(errno));
+ if (rtp_debug_test_addr(&rtp->them))
+ ast_verbose("Sent RTP DTMF packet to %s:%u (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u)\n",
+ ast_inet_ntoa(rtp->them.sin_addr),
+ ntohs(rtp->them.sin_port), payload, rtp->seqno, rtp->lastdigitts, res - hdrlen);
+ /* Increment sequence number */
+ rtp->seqno++;
+ /* Increment duration */
+ rtp->send_duration += 160;
+ /* Clear marker bit and set seqno */
+ rtpheader[0] = htonl((2 << 30) | (payload << 16) | (rtp->seqno));
+ }
+
+ /* Since we received a begin, we can safely store the digit and disable any compensation */
+ rtp->sending_digit = 1;
+ rtp->send_digit = digit;
+ rtp->send_payload = payload;
+
+ return 0;
+}
+
+/*! \brief Send continuation frame for DTMF */
+static int ast_rtp_senddigit_continuation(struct ast_rtp *rtp)
+{
+ unsigned int *rtpheader;
+ int hdrlen = 12, res = 0;
+ char data[256];
+
+ if (!rtp->them.sin_addr.s_addr || !rtp->them.sin_port)
+ return 0;
+
+ /* Setup packet to send */
+ rtpheader = (unsigned int *)data;
+ rtpheader[0] = htonl((2 << 30) | (1 << 23) | (rtp->send_payload << 16) | (rtp->seqno));
+ rtpheader[1] = htonl(rtp->lastdigitts);
+ rtpheader[2] = htonl(rtp->ssrc);
+ rtpheader[3] = htonl((rtp->send_digit << 24) | (0xa << 16) | (rtp->send_duration));
+ rtpheader[0] = htonl((2 << 30) | (rtp->send_payload << 16) | (rtp->seqno));
+
+ /* Transmit */
+ res = sendto(rtp->s, (void *) rtpheader, hdrlen + 4, 0, (struct sockaddr *) &rtp->them, sizeof(rtp->them));
+ if (res < 0)
+ ast_log(LOG_ERROR, "RTP Transmission error to %s:%d: %s\n",
+ ast_inet_ntoa(rtp->them.sin_addr),
+ ntohs(rtp->them.sin_port), strerror(errno));
+ if (rtp_debug_test_addr(&rtp->them))
+ ast_verbose("Sent RTP DTMF packet to %s:%u (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u)\n",
+ ast_inet_ntoa(rtp->them.sin_addr),
+ ntohs(rtp->them.sin_port), rtp->send_payload, rtp->seqno, rtp->lastdigitts, res - hdrlen);
+
+ /* Increment sequence number */
+ rtp->seqno++;
+ /* Increment duration */
+ rtp->send_duration += 160;
+
+ return 0;
+}
+
+/*! \brief Send end packets for DTMF */
+int ast_rtp_senddigit_end(struct ast_rtp *rtp, char digit)
+{
+ unsigned int *rtpheader;
+ int hdrlen = 12, res = 0, i = 0;
+ char data[256];
+
+ /* If no address, then bail out */
+ if (!rtp->them.sin_addr.s_addr || !rtp->them.sin_port)
+ return 0;
+
+ if ((digit <= '9') && (digit >= '0'))
+ digit -= '0';
+ else if (digit == '*')
+ digit = 10;
+ else if (digit == '#')
+ digit = 11;
+ else if ((digit >= 'A') && (digit <= 'D'))
+ digit = digit - 'A' + 12;
+ else if ((digit >= 'a') && (digit <= 'd'))
+ digit = digit - 'a' + 12;
+ else {
+ ast_log(LOG_WARNING, "Don't know how to represent '%c'\n", digit);
+ return 0;
+ }
+
+ rtp->dtmfmute = ast_tvadd(ast_tvnow(), ast_tv(0, 500000));
+
+ rtpheader = (unsigned int *)data;
+ rtpheader[0] = htonl((2 << 30) | (1 << 23) | (rtp->send_payload << 16) | (rtp->seqno));
+ rtpheader[1] = htonl(rtp->lastdigitts);
+ rtpheader[2] = htonl(rtp->ssrc);
+ rtpheader[3] = htonl((digit << 24) | (0xa << 16) | (rtp->send_duration));
+ /* Set end bit */
+ rtpheader[3] |= htonl((1 << 23));
+ rtpheader[0] = htonl((2 << 30) | (rtp->send_payload << 16) | (rtp->seqno));
+ /* Send 3 termination packets */
+ for (i = 0; i < 3; i++) {
+ res = sendto(rtp->s, (void *) rtpheader, hdrlen + 4, 0, (struct sockaddr *) &rtp->them, sizeof(rtp->them));
+ if (res < 0)
+ ast_log(LOG_ERROR, "RTP Transmission error to %s:%d: %s\n",
+ ast_inet_ntoa(rtp->them.sin_addr),
+ ntohs(rtp->them.sin_port), strerror(errno));
+ if (rtp_debug_test_addr(&rtp->them))
+ ast_verbose("Sent RTP DTMF packet to %s:%u (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u)\n",
+ ast_inet_ntoa(rtp->them.sin_addr),
+ ntohs(rtp->them.sin_port), rtp->send_payload, rtp->seqno, rtp->lastdigitts, res - hdrlen);
+ }
+ rtp->sending_digit = 0;
+ rtp->send_digit = 0;
+ /* Increment lastdigitts */
+ rtp->lastdigitts += 960;
+ rtp->seqno++;
+
+ return res;
+}
+
+/*! \brief Public function: Send an H.261 fast update request, some devices need this rather than SIP XML */
+int ast_rtcp_send_h261fur(void *data)
+{
+ struct ast_rtp *rtp = data;
+ int res;
+
+ rtp->rtcp->sendfur = 1;
+ res = ast_rtcp_write(data);
+
+ return res;
+}
+
+/*! \brief Send RTCP sender's report */
+static int ast_rtcp_write_sr(const void *data)
+{
+ struct ast_rtp *rtp = (struct ast_rtp *)data;
+ int res;
+ int len = 0;
+ struct timeval now;
+ unsigned int now_lsw;
+ unsigned int now_msw;
+ unsigned int *rtcpheader;
+ unsigned int lost;
+ unsigned int extended;
+ unsigned int expected;
+ unsigned int expected_interval;
+ unsigned int received_interval;
+ int lost_interval;
+ int fraction;
+ struct timeval dlsr;
+ char bdata[512];
+
+ /* Commented condition is always not NULL if rtp->rtcp is not NULL */
+ if (!rtp || !rtp->rtcp/* || (&rtp->rtcp->them.sin_addr == 0)*/)
+ return 0;
+
+ if (!rtp->rtcp->them.sin_addr.s_addr) { /* This'll stop rtcp for this rtp session */
+ ast_verbose("RTCP SR transmission error, rtcp halted\n");
+ if (rtp->rtcp->schedid > 0)
+ ast_sched_del(rtp->sched, rtp->rtcp->schedid);
+ rtp->rtcp->schedid = -1;
+ return 0;
+ }
+
+ gettimeofday(&now, NULL);
+ timeval2ntp(now, &now_msw, &now_lsw); /* fill thses ones in from utils.c*/
+ rtcpheader = (unsigned int *)bdata;
+ rtcpheader[1] = htonl(rtp->ssrc); /* Our SSRC */
+ rtcpheader[2] = htonl(now_msw); /* now, MSW. gettimeofday() + SEC_BETWEEN_1900_AND_1970*/
+ rtcpheader[3] = htonl(now_lsw); /* now, LSW */
+ rtcpheader[4] = htonl(rtp->lastts); /* FIXME shouldn't be that, it should be now */
+ rtcpheader[5] = htonl(rtp->txcount); /* No. packets sent */
+ rtcpheader[6] = htonl(rtp->txoctetcount); /* No. bytes sent */
+ len += 28;
+
+ extended = rtp->cycles + rtp->lastrxseqno;
+ expected = extended - rtp->seedrxseqno + 1;
+ if (rtp->rxcount > expected)
+ expected += rtp->rxcount - expected;
+ lost = expected - rtp->rxcount;
+ expected_interval = expected - rtp->rtcp->expected_prior;
+ rtp->rtcp->expected_prior = expected;
+ received_interval = rtp->rxcount - rtp->rtcp->received_prior;
+ rtp->rtcp->received_prior = rtp->rxcount;
+ lost_interval = expected_interval - received_interval;
+ if (expected_interval == 0 || lost_interval <= 0)
+ fraction = 0;
+ else
+ fraction = (lost_interval << 8) / expected_interval;
+ timersub(&now, &rtp->rtcp->rxlsr, &dlsr);
+ rtcpheader[7] = htonl(rtp->themssrc);
+ rtcpheader[8] = htonl(((fraction & 0xff) << 24) | (lost & 0xffffff));
+ rtcpheader[9] = htonl((rtp->cycles) | ((rtp->lastrxseqno & 0xffff)));
+ rtcpheader[10] = htonl((unsigned int)(rtp->rxjitter * 65536.));
+ rtcpheader[11] = htonl(rtp->rtcp->themrxlsr);
+ rtcpheader[12] = htonl((((dlsr.tv_sec * 1000) + (dlsr.tv_usec / 1000)) * 65536) / 1000);
+ len += 24;
+
+ rtcpheader[0] = htonl((2 << 30) | (1 << 24) | (RTCP_PT_SR << 16) | ((len/4)-1));
+
+ if (rtp->rtcp->sendfur) {
+ rtcpheader[13] = htonl((2 << 30) | (0 << 24) | (RTCP_PT_FUR << 16) | 1);
+ rtcpheader[14] = htonl(rtp->ssrc); /* Our SSRC */
+ len += 8;
+ rtp->rtcp->sendfur = 0;
+ }
+
+ /* Insert SDES here. Probably should make SDES text equal to mimetypes[code].type (not subtype 'cos */
+ /* it can change mid call, and SDES can't) */
+ rtcpheader[len/4] = htonl((2 << 30) | (1 << 24) | (RTCP_PT_SDES << 16) | 2);
+ rtcpheader[(len/4)+1] = htonl(rtp->ssrc); /* Our SSRC */
+ rtcpheader[(len/4)+2] = htonl(0x01 << 24); /* Empty for the moment */
+ len += 12;
+
+ res = sendto(rtp->rtcp->s, (unsigned int *)rtcpheader, len, 0, (struct sockaddr *)&rtp->rtcp->them, sizeof(rtp->rtcp->them));
+ if (res < 0) {
+ ast_log(LOG_ERROR, "RTCP SR transmission error to %s:%d, rtcp halted %s\n",ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port), strerror(errno));
+ if (rtp->rtcp->schedid > 0)
+ ast_sched_del(rtp->sched, rtp->rtcp->schedid);
+ rtp->rtcp->schedid = -1;
+ return 0;
+ }
+
+ /* FIXME Don't need to get a new one */
+ gettimeofday(&rtp->rtcp->txlsr, NULL);
+ rtp->rtcp->sr_count++;
+
+ rtp->rtcp->lastsrtxcount = rtp->txcount;
+
+ if (rtcp_debug_test_addr(&rtp->rtcp->them)) {
+ ast_verbose("* Sent RTCP SR to %s:%d\n", ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port));
+ ast_verbose(" Our SSRC: %u\n", rtp->ssrc);
+ ast_verbose(" Sent(NTP): %u.%010u\n", (unsigned int)now.tv_sec, (unsigned int)now.tv_usec*4096);
+ ast_verbose(" Sent(RTP): %u\n", rtp->lastts);
+ ast_verbose(" Sent packets: %u\n", rtp->txcount);
+ ast_verbose(" Sent octets: %u\n", rtp->txoctetcount);
+ ast_verbose(" Report block:\n");
+ ast_verbose(" Fraction lost: %u\n", fraction);
+ ast_verbose(" Cumulative loss: %u\n", lost);
+ ast_verbose(" IA jitter: %.4f\n", rtp->rxjitter);
+ ast_verbose(" Their last SR: %u\n", rtp->rtcp->themrxlsr);
+ ast_verbose(" DLSR: %4.4f (sec)\n\n", (double)(ntohl(rtcpheader[12])/65536.0));
+ }
+ manager_event(EVENT_FLAG_REPORTING, "RTCPSent", "To %s:%d\r\n"
+ "OurSSRC: %u\r\n"
+ "SentNTP: %u.%010u\r\n"
+ "SentRTP: %u\r\n"
+ "SentPackets: %u\r\n"
+ "SentOctets: %u\r\n"
+ "ReportBlock:\r\n"
+ "FractionLost: %u\r\n"
+ "CumulativeLoss: %u\r\n"
+ "IAJitter: %.4f\r\n"
+ "TheirLastSR: %u\r\n"
+ "DLSR: %4.4f (sec)\r\n",
+ ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port),
+ rtp->ssrc,
+ (unsigned int)now.tv_sec, (unsigned int)now.tv_usec*4096,
+ rtp->lastts,
+ rtp->txcount,
+ rtp->txoctetcount,
+ fraction,
+ lost,
+ rtp->rxjitter,
+ rtp->rtcp->themrxlsr,
+ (double)(ntohl(rtcpheader[12])/65536.0));
+ return res;
+}
+
+/*! \brief Send RTCP recipient's report */
+static int ast_rtcp_write_rr(const void *data)
+{
+ struct ast_rtp *rtp = (struct ast_rtp *)data;
+ int res;
+ int len = 32;
+ unsigned int lost;
+ unsigned int extended;
+ unsigned int expected;
+ unsigned int expected_interval;
+ unsigned int received_interval;
+ int lost_interval;
+ struct timeval now;
+ unsigned int *rtcpheader;
+ char bdata[1024];
+ struct timeval dlsr;
+ int fraction;
+
+ if (!rtp || !rtp->rtcp || (&rtp->rtcp->them.sin_addr == 0))
+ return 0;
+
+ if (!rtp->rtcp->them.sin_addr.s_addr) {
+ ast_log(LOG_ERROR, "RTCP RR transmission error, rtcp halted\n");
+ if (rtp->rtcp->schedid > 0)
+ ast_sched_del(rtp->sched, rtp->rtcp->schedid);
+ rtp->rtcp->schedid = -1;
+ return 0;
+ }
+
+ extended = rtp->cycles + rtp->lastrxseqno;
+ expected = extended - rtp->seedrxseqno + 1;
+ lost = expected - rtp->rxcount;
+ expected_interval = expected - rtp->rtcp->expected_prior;
+ rtp->rtcp->expected_prior = expected;
+ received_interval = rtp->rxcount - rtp->rtcp->received_prior;
+ rtp->rtcp->received_prior = rtp->rxcount;
+ lost_interval = expected_interval - received_interval;
+ if (expected_interval == 0 || lost_interval <= 0)
+ fraction = 0;
+ else
+ fraction = (lost_interval << 8) / expected_interval;
+ gettimeofday(&now, NULL);
+ timersub(&now, &rtp->rtcp->rxlsr, &dlsr);
+ rtcpheader = (unsigned int *)bdata;
+ rtcpheader[0] = htonl((2 << 30) | (1 << 24) | (RTCP_PT_RR << 16) | ((len/4)-1));
+ rtcpheader[1] = htonl(rtp->ssrc);
+ rtcpheader[2] = htonl(rtp->themssrc);
+ rtcpheader[3] = htonl(((fraction & 0xff) << 24) | (lost & 0xffffff));
+ rtcpheader[4] = htonl((rtp->cycles) | ((rtp->lastrxseqno & 0xffff)));
+ rtcpheader[5] = htonl((unsigned int)(rtp->rxjitter * 65536.));
+ rtcpheader[6] = htonl(rtp->rtcp->themrxlsr);
+ rtcpheader[7] = htonl((((dlsr.tv_sec * 1000) + (dlsr.tv_usec / 1000)) * 65536) / 1000);
+
+ if (rtp->rtcp->sendfur) {
+ rtcpheader[8] = htonl((2 << 30) | (0 << 24) | (RTCP_PT_FUR << 16) | 1); /* Header from page 36 in RFC 3550 */
+ rtcpheader[9] = htonl(rtp->ssrc); /* Our SSRC */
+ len += 8;
+ rtp->rtcp->sendfur = 0;
+ }
+
+ /*! \note Insert SDES here. Probably should make SDES text equal to mimetypes[code].type (not subtype 'cos
+ it can change mid call, and SDES can't) */
+ rtcpheader[len/4] = htonl((2 << 30) | (1 << 24) | (RTCP_PT_SDES << 16) | 2);
+ rtcpheader[(len/4)+1] = htonl(rtp->ssrc); /* Our SSRC */
+ rtcpheader[(len/4)+2] = htonl(0x01 << 24); /* Empty for the moment */
+ len += 12;
+
+ res = sendto(rtp->rtcp->s, (unsigned int *)rtcpheader, len, 0, (struct sockaddr *)&rtp->rtcp->them, sizeof(rtp->rtcp->them));
+
+ if (res < 0) {
+ ast_log(LOG_ERROR, "RTCP RR transmission error, rtcp halted: %s\n",strerror(errno));
+ /* Remove the scheduler */
+ if (rtp->rtcp->schedid > 0)
+ ast_sched_del(rtp->sched, rtp->rtcp->schedid);
+ rtp->rtcp->schedid = -1;
+ return 0;
+ }
+
+ rtp->rtcp->rr_count++;
+
+ if (rtcp_debug_test_addr(&rtp->rtcp->them)) {
+ ast_verbose("\n* Sending RTCP RR to %s:%d\n"
+ " Our SSRC: %u\nTheir SSRC: %u\niFraction lost: %d\nCumulative loss: %u\n"
+ " IA jitter: %.4f\n"
+ " Their last SR: %u\n"
+ " DLSR: %4.4f (sec)\n\n",
+ ast_inet_ntoa(rtp->rtcp->them.sin_addr),
+ ntohs(rtp->rtcp->them.sin_port),
+ rtp->ssrc, rtp->themssrc, fraction, lost,
+ rtp->rxjitter,
+ rtp->rtcp->themrxlsr,
+ (double)(ntohl(rtcpheader[7])/65536.0));
+ }
+
+ return res;
+}
+
+/*! \brief Write and RTCP packet to the far end
+ * \note Decide if we are going to send an SR (with Reception Block) or RR
+ * RR is sent if we have not sent any rtp packets in the previous interval */
+static int ast_rtcp_write(const void *data)
+{
+ struct ast_rtp *rtp = (struct ast_rtp *)data;
+ int res;
+
+ if (!rtp || !rtp->rtcp)
+ return 0;
+
+ if (rtp->txcount > rtp->rtcp->lastsrtxcount)
+ res = ast_rtcp_write_sr(data);
+ else
+ res = ast_rtcp_write_rr(data);
+
+ return res;
+}
+
+/*! \brief generate comfort noice (CNG) */
+int ast_rtp_sendcng(struct ast_rtp *rtp, int level)
+{
+ unsigned int *rtpheader;
+ int hdrlen = 12;
+ int res;
+ int payload;
+ char data[256];
+ level = 127 - (level & 0x7f);
+ payload = ast_rtp_lookup_code(rtp, 0, AST_RTP_CN);
+
+ /* If we have no peer, return immediately */
+ if (!rtp->them.sin_addr.s_addr)
+ return 0;
+
+ rtp->dtmfmute = ast_tvadd(ast_tvnow(), ast_tv(0, 500000));
+
+ /* Get a pointer to the header */
+ rtpheader = (unsigned int *)data;
+ rtpheader[0] = htonl((2 << 30) | (1 << 23) | (payload << 16) | (rtp->seqno++));
+ rtpheader[1] = htonl(rtp->lastts);
+ rtpheader[2] = htonl(rtp->ssrc);
+ data[12] = level;
+ if (rtp->them.sin_port && rtp->them.sin_addr.s_addr) {
+ res = sendto(rtp->s, (void *)rtpheader, hdrlen + 1, 0, (struct sockaddr *)&rtp->them, sizeof(rtp->them));
+ if (res <0)
+ ast_log(LOG_ERROR, "RTP Comfort Noise Transmission error to %s:%d: %s\n", ast_inet_ntoa(rtp->them.sin_addr), ntohs(rtp->them.sin_port), strerror(errno));
+ if (rtp_debug_test_addr(&rtp->them))
+ ast_verbose("Sent Comfort Noise RTP packet to %s:%u (type %d, seq %u, ts %u, len %d)\n"
+ , ast_inet_ntoa(rtp->them.sin_addr), ntohs(rtp->them.sin_port), payload, rtp->seqno, rtp->lastts,res - hdrlen);
+
+ }
+ return 0;
+}
+
+/*! \brief Write RTP packet with audio or video media frames into UDP packet */
+static int ast_rtp_raw_write(struct ast_rtp *rtp, struct ast_frame *f, int codec)
+{
+ unsigned char *rtpheader;
+ int hdrlen = 12;
+ int res;
+ unsigned int ms;
+ int pred;
+ int mark = 0;
+
+ ms = calc_txstamp(rtp, &f->delivery);
+ /* Default prediction */
+ if (f->subclass & AST_FORMAT_AUDIO_MASK) {
+ pred = rtp->lastts + f->samples;
+
+ /* Re-calculate last TS */
+ rtp->lastts = rtp->lastts + ms * 8;
+ if (ast_tvzero(f->delivery)) {
+ /* If this isn't an absolute delivery time, Check if it is close to our prediction,
+ and if so, go with our prediction */
+ if (abs(rtp->lastts - pred) < MAX_TIMESTAMP_SKEW)
+ rtp->lastts = pred;
+ else {
+ ast_debug(3, "Difference is %d, ms is %d\n", abs(rtp->lastts - pred), ms);
+ mark = 1;
+ }
+ }
+ } else if(f->subclass & AST_FORMAT_VIDEO_MASK) {
+ mark = f->subclass & 0x1;
+ pred = rtp->lastovidtimestamp + f->samples;
+ /* Re-calculate last TS */
+ rtp->lastts = rtp->lastts + ms * 90;
+ /* If it's close to our prediction, go for it */
+ if (ast_tvzero(f->delivery)) {
+ if (abs(rtp->lastts - pred) < 7200) {
+ rtp->lastts = pred;
+ rtp->lastovidtimestamp += f->samples;
+ } else {
+ ast_debug(3, "Difference is %d, ms is %d (%d), pred/ts/samples %d/%d/%d\n", abs(rtp->lastts - pred), ms, ms * 90, rtp->lastts, pred, f->samples);
+ rtp->lastovidtimestamp = rtp->lastts;
+ }
+ }
+ } else {
+ pred = rtp->lastotexttimestamp + f->samples;
+ /* Re-calculate last TS */
+ rtp->lastts = rtp->lastts + ms * 90;
+ /* If it's close to our prediction, go for it */
+ if (ast_tvzero(f->delivery)) {
+ if (abs(rtp->lastts - pred) < 7200) {
+ rtp->lastts = pred;
+ rtp->lastotexttimestamp += f->samples;
+ } else {
+ ast_debug(3, "Difference is %d, ms is %d (%d), pred/ts/samples %d/%d/%d\n", abs(rtp->lastts - pred), ms, ms * 90, rtp->lastts, pred, f->samples);
+ rtp->lastotexttimestamp = rtp->lastts;
+ }
+ }
+ }
+ /* If the timestamp for non-digit packets has moved beyond the timestamp
+ for digits, update the digit timestamp.
+ */
+ if (rtp->lastts > rtp->lastdigitts)
+ rtp->lastdigitts = rtp->lastts;
+
+ if (ast_test_flag(f, AST_FRFLAG_HAS_TIMING_INFO))
+ rtp->lastts = f->ts * 8;
+
+ /* Get a pointer to the header */
+ rtpheader = (unsigned char *)(f->data - hdrlen);
+
+ put_unaligned_uint32(rtpheader, htonl((2 << 30) | (codec << 16) | (rtp->seqno) | (mark << 23)));
+ put_unaligned_uint32(rtpheader + 4, htonl(rtp->lastts));
+ put_unaligned_uint32(rtpheader + 8, htonl(rtp->ssrc));
+
+ if (rtp->them.sin_port && rtp->them.sin_addr.s_addr) {
+ res = sendto(rtp->s, (void *)rtpheader, f->datalen + hdrlen, 0, (struct sockaddr *)&rtp->them, sizeof(rtp->them));
+ if (res <0) {
+ if (!rtp->nat || (rtp->nat && (ast_test_flag(rtp, FLAG_NAT_ACTIVE) == FLAG_NAT_ACTIVE))) {
+ ast_debug(1, "RTP Transmission error of packet %d to %s:%d: %s\n", rtp->seqno, ast_inet_ntoa(rtp->them.sin_addr), ntohs(rtp->them.sin_port), strerror(errno));
+ } else if (((ast_test_flag(rtp, FLAG_NAT_ACTIVE) == FLAG_NAT_INACTIVE) || rtpdebug) && !ast_test_flag(rtp, FLAG_NAT_INACTIVE_NOWARN)) {
+ /* Only give this error message once if we are not RTP debugging */
+ if (option_debug || rtpdebug)
+ ast_debug(0, "RTP NAT: Can't write RTP to private address %s:%d, waiting for other end to send audio...\n", ast_inet_ntoa(rtp->them.sin_addr), ntohs(rtp->them.sin_port));
+ ast_set_flag(rtp, FLAG_NAT_INACTIVE_NOWARN);
+ }
+ } else {
+ rtp->txcount++;
+ rtp->txoctetcount +=(res - hdrlen);
+
+ if (rtp->rtcp && rtp->rtcp->schedid < 1)
+ rtp->rtcp->schedid = ast_sched_add(rtp->sched, ast_rtcp_calc_interval(rtp), ast_rtcp_write, rtp);
+ }
+
+ if (rtp_debug_test_addr(&rtp->them))
+ ast_verbose("Sent RTP packet to %s:%u (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u)\n",
+ ast_inet_ntoa(rtp->them.sin_addr), ntohs(rtp->them.sin_port), codec, rtp->seqno, rtp->lastts,res - hdrlen);
+ }
+
+ rtp->seqno++;
+
+ return 0;
+}
+
+int ast_rtp_codec_setpref(struct ast_rtp *rtp, struct ast_codec_pref *prefs)
+{
+ int x;
+ for (x = 0; x < 32; x++) { /* Ugly way */
+ rtp->pref.order[x] = prefs->order[x];
+ rtp->pref.framing[x] = prefs->framing[x];
+ }
+ if (rtp->smoother)
+ ast_smoother_free(rtp->smoother);
+ rtp->smoother = NULL;
+ return 0;
+}
+
+struct ast_codec_pref *ast_rtp_codec_getpref(struct ast_rtp *rtp)
+{
+ return &rtp->pref;
+}
+
+int ast_rtp_codec_getformat(int pt)
+{
+ if (pt < 0 || pt > MAX_RTP_PT)
+ return 0; /* bogus payload type */
+
+ if (static_RTP_PT[pt].isAstFormat)
+ return static_RTP_PT[pt].code;
+ else
+ return 0;
+}
+
+int ast_rtp_write(struct ast_rtp *rtp, struct ast_frame *_f)
+{
+ struct ast_frame *f;
+ int codec;
+ int hdrlen = 12;
+ int subclass;
+
+
+ /* If we have no peer, return immediately */
+ if (!rtp->them.sin_addr.s_addr)
+ return 0;
+
+ /* If there is no data length, return immediately */
+ if (!_f->datalen)
+ return 0;
+
+ /* Make sure we have enough space for RTP header */
+ if ((_f->frametype != AST_FRAME_VOICE) && (_f->frametype != AST_FRAME_VIDEO) && (_f->frametype != AST_FRAME_TEXT)) {
+ ast_log(LOG_WARNING, "RTP can only send voice, video and text\n");
+ return -1;
+ }
+
+ /* The bottom bit of a video subclass contains the marker bit */
+ subclass = _f->subclass;
+ if (_f->frametype == AST_FRAME_VIDEO)
+ subclass &= ~0x1;
+
+ codec = ast_rtp_lookup_code(rtp, 1, subclass);
+ if (codec < 0) {
+ ast_log(LOG_WARNING, "Don't know how to send format %s packets with RTP\n", ast_getformatname(_f->subclass));
+ return -1;
+ }
+
+ if (rtp->lasttxformat != subclass) {
+ /* New format, reset the smoother */
+ ast_debug(1, "Ooh, format changed from %s to %s\n", ast_getformatname(rtp->lasttxformat), ast_getformatname(subclass));
+ rtp->lasttxformat = subclass;
+ if (rtp->smoother)
+ ast_smoother_free(rtp->smoother);
+ rtp->smoother = NULL;
+ }
+
+ if (!rtp->smoother && subclass != AST_FORMAT_SPEEX && subclass != AST_FORMAT_G723_1) {
+ struct ast_format_list fmt = ast_codec_pref_getsize(&rtp->pref, subclass);
+ if (fmt.inc_ms) { /* if codec parameters is set / avoid division by zero */
+ if (!(rtp->smoother = ast_smoother_new((fmt.cur_ms * fmt.fr_len) / fmt.inc_ms))) {
+ ast_log(LOG_WARNING, "Unable to create smoother: format: %d ms: %d len: %d\n", subclass, fmt.cur_ms, ((fmt.cur_ms * fmt.fr_len) / fmt.inc_ms));
+ return -1;
+ }
+ if (fmt.flags)
+ ast_smoother_set_flags(rtp->smoother, fmt.flags);
+ ast_debug(1, "Created smoother: format: %d ms: %d len: %d\n", subclass, fmt.cur_ms, ((fmt.cur_ms * fmt.fr_len) / fmt.inc_ms));
+ }
+ }
+ if (rtp->smoother) {
+ if (ast_smoother_test_flag(rtp->smoother, AST_SMOOTHER_FLAG_BE)) {
+ ast_smoother_feed_be(rtp->smoother, _f);
+ } else {
+ ast_smoother_feed(rtp->smoother, _f);
+ }
+
+ while ((f = ast_smoother_read(rtp->smoother)) && (f->data))
+ ast_rtp_raw_write(rtp, f, codec);
+ } else {
+ /* Don't buffer outgoing frames; send them one-per-packet: */
+ if (_f->offset < hdrlen)
+ f = ast_frdup(_f); /*! \bug XXX this might never be free'd. Why do we do this? */
+ else
+ f = _f;
+ if (f->data)
+ ast_rtp_raw_write(rtp, f, codec);
+ if (f != _f)
+ ast_frfree(f);
+ }
+
+ return 0;
+}
+
+/*! \brief Unregister interface to channel driver */
+void ast_rtp_proto_unregister(struct ast_rtp_protocol *proto)
+{
+ AST_RWLIST_WRLOCK(&protos);
+ AST_RWLIST_REMOVE(&protos, proto, list);
+ AST_RWLIST_UNLOCK(&protos);
+}
+
+/*! \brief Register interface to channel driver */
+int ast_rtp_proto_register(struct ast_rtp_protocol *proto)
+{
+ struct ast_rtp_protocol *cur;
+
+ AST_RWLIST_WRLOCK(&protos);
+ AST_RWLIST_TRAVERSE(&protos, cur, list) {
+ if (!strcmp(cur->type, proto->type)) {
+ ast_log(LOG_WARNING, "Tried to register same protocol '%s' twice\n", cur->type);
+ AST_RWLIST_UNLOCK(&protos);
+ return -1;
+ }
+ }
+ AST_RWLIST_INSERT_HEAD(&protos, proto, list);
+ AST_RWLIST_UNLOCK(&protos);
+
+ return 0;
+}
+
+/*! \brief Bridge loop for true native bridge (reinvite) */
+static enum ast_bridge_result bridge_native_loop(struct ast_channel *c0, struct ast_channel *c1, struct ast_rtp *p0, struct ast_rtp *p1, struct ast_rtp *vp0, struct ast_rtp *vp1, struct ast_rtp *tp0, struct ast_rtp *tp1, struct ast_rtp_protocol *pr0, struct ast_rtp_protocol *pr1, int codec0, int codec1, int timeoutms, int flags, struct ast_frame **fo, struct ast_channel **rc, void *pvt0, void *pvt1)
+{
+ struct ast_frame *fr = NULL;
+ struct ast_channel *who = NULL, *other = NULL, *cs[3] = {NULL, };
+ int oldcodec0 = codec0, oldcodec1 = codec1;
+ struct sockaddr_in ac1 = {0,}, vac1 = {0,}, tac1 = {0,}, ac0 = {0,}, vac0 = {0,}, tac0 = {0,};
+ struct sockaddr_in t1 = {0,}, vt1 = {0,}, tt1 = {0,}, t0 = {0,}, vt0 = {0,}, tt0 = {0,};
+
+ /* Set it up so audio goes directly between the two endpoints */
+
+ /* Test the first channel */
+ if (!(pr0->set_rtp_peer(c0, p1, vp1, tp1, codec1, ast_test_flag(p1, FLAG_NAT_ACTIVE)))) {
+ ast_rtp_get_peer(p1, &ac1);
+ if (vp1)
+ ast_rtp_get_peer(vp1, &vac1);
+ if (tp1)
+ ast_rtp_get_peer(tp1, &tac1);
+ } else
+ ast_log(LOG_WARNING, "Channel '%s' failed to talk to '%s'\n", c0->name, c1->name);
+
+ /* Test the second channel */
+ if (!(pr1->set_rtp_peer(c1, p0, vp0, tp0, codec0, ast_test_flag(p0, FLAG_NAT_ACTIVE)))) {
+ ast_rtp_get_peer(p0, &ac0);
+ if (vp0)
+ ast_rtp_get_peer(vp0, &vac0);
+ if (tp0)
+ ast_rtp_get_peer(tp0, &tac0);
+ } else
+ ast_log(LOG_WARNING, "Channel '%s' failed to talk to '%s'\n", c1->name, c0->name);
+
+ /* Now we can unlock and move into our loop */
+ ast_channel_unlock(c0);
+ ast_channel_unlock(c1);
+
+ ast_poll_channel_add(c0, c1);
+
+ /* Throw our channels into the structure and enter the loop */
+ cs[0] = c0;
+ cs[1] = c1;
+ cs[2] = NULL;
+ for (;;) {
+ /* Check if anything changed */
+ if ((c0->tech_pvt != pvt0) ||
+ (c1->tech_pvt != pvt1) ||
+ (c0->masq || c0->masqr || c1->masq || c1->masqr) ||
+ (c0->monitor || c0->audiohooks || c1->monitor || c1->audiohooks)) {
+ ast_debug(1, "Oooh, something is weird, backing out\n");
+ if (c0->tech_pvt == pvt0)
+ if (pr0->set_rtp_peer(c0, NULL, NULL, NULL, 0, 0))
+ ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c0->name);
+ if (c1->tech_pvt == pvt1)
+ if (pr1->set_rtp_peer(c1, NULL, NULL, NULL, 0, 0))
+ ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c1->name);
+ ast_poll_channel_del(c0, c1);
+ return AST_BRIDGE_RETRY;
+ }
+
+ /* Check if they have changed their address */
+ ast_rtp_get_peer(p1, &t1);
+ if (vp1)
+ ast_rtp_get_peer(vp1, &vt1);
+ if (tp1)
+ ast_rtp_get_peer(tp1, &tt1);
+ if (pr1->get_codec)
+ codec1 = pr1->get_codec(c1);
+ ast_rtp_get_peer(p0, &t0);
+ if (vp0)
+ ast_rtp_get_peer(vp0, &vt0);
+ if (tp0)
+ ast_rtp_get_peer(tp0, &tt0);
+ if (pr0->get_codec)
+ codec0 = pr0->get_codec(c0);
+ if ((inaddrcmp(&t1, &ac1)) ||
+ (vp1 && inaddrcmp(&vt1, &vac1)) ||
+ (tp1 && inaddrcmp(&tt1, &tac1)) ||
+ (codec1 != oldcodec1)) {
+ ast_debug(2, "Oooh, '%s' changed end address to %s:%d (format %d)\n",
+ c1->name, ast_inet_ntoa(t1.sin_addr), ntohs(t1.sin_port), codec1);
+ ast_debug(2, "Oooh, '%s' changed end vaddress to %s:%d (format %d)\n",
+ c1->name, ast_inet_ntoa(vt1.sin_addr), ntohs(vt1.sin_port), codec1);
+ ast_debug(2, "Oooh, '%s' changed end taddress to %s:%d (format %d)\n",
+ c1->name, ast_inet_ntoa(tt1.sin_addr), ntohs(tt1.sin_port), codec1);
+ ast_debug(2, "Oooh, '%s' was %s:%d/(format %d)\n",
+ c1->name, ast_inet_ntoa(ac1.sin_addr), ntohs(ac1.sin_port), oldcodec1);
+ ast_debug(2, "Oooh, '%s' was %s:%d/(format %d)\n",
+ c1->name, ast_inet_ntoa(vac1.sin_addr), ntohs(vac1.sin_port), oldcodec1);
+ ast_debug(2, "Oooh, '%s' was %s:%d/(format %d)\n",
+ c1->name, ast_inet_ntoa(tac1.sin_addr), ntohs(tac1.sin_port), oldcodec1);
+ if (pr0->set_rtp_peer(c0, t1.sin_addr.s_addr ? p1 : NULL, vt1.sin_addr.s_addr ? vp1 : NULL, tt1.sin_addr.s_addr ? tp1 : NULL, codec1, ast_test_flag(p1, FLAG_NAT_ACTIVE)))
+ ast_log(LOG_WARNING, "Channel '%s' failed to update to '%s'\n", c0->name, c1->name);
+ memcpy(&ac1, &t1, sizeof(ac1));
+ memcpy(&vac1, &vt1, sizeof(vac1));
+ memcpy(&tac1, &tt1, sizeof(tac1));
+ oldcodec1 = codec1;
+ }
+ if ((inaddrcmp(&t0, &ac0)) ||
+ (vp0 && inaddrcmp(&vt0, &vac0)) ||
+ (tp0 && inaddrcmp(&tt0, &tac0))) {
+ ast_debug(2, "Oooh, '%s' changed end address to %s:%d (format %d)\n",
+ c0->name, ast_inet_ntoa(t0.sin_addr), ntohs(t0.sin_port), codec0);
+ ast_debug(2, "Oooh, '%s' was %s:%d/(format %d)\n",
+ c0->name, ast_inet_ntoa(ac0.sin_addr), ntohs(ac0.sin_port), oldcodec0);
+ if (pr1->set_rtp_peer(c1, t0.sin_addr.s_addr ? p0 : NULL, vt0.sin_addr.s_addr ? vp0 : NULL, tt0.sin_addr.s_addr ? tp0 : NULL, codec0, ast_test_flag(p0, FLAG_NAT_ACTIVE)))
+ ast_log(LOG_WARNING, "Channel '%s' failed to update to '%s'\n", c1->name, c0->name);
+ memcpy(&ac0, &t0, sizeof(ac0));
+ memcpy(&vac0, &vt0, sizeof(vac0));
+ memcpy(&tac0, &tt0, sizeof(tac0));
+ oldcodec0 = codec0;
+ }
+
+ /* Wait for frame to come in on the channels */
+ if (!(who = ast_waitfor_n(cs, 2, &timeoutms))) {
+ if (!timeoutms) {
+ if (pr0->set_rtp_peer(c0, NULL, NULL, NULL, 0, 0))
+ ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c0->name);
+ if (pr1->set_rtp_peer(c1, NULL, NULL, NULL, 0, 0))
+ ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c1->name);
+ return AST_BRIDGE_RETRY;
+ }
+ ast_debug(1, "Ooh, empty read...\n");
+ if (ast_check_hangup(c0) || ast_check_hangup(c1))
+ break;
+ continue;
+ }
+ fr = ast_read(who);
+ other = (who == c0) ? c1 : c0;
+ if (!fr || ((fr->frametype == AST_FRAME_DTMF_BEGIN || fr->frametype == AST_FRAME_DTMF_END) &&
+ (((who == c0) && (flags & AST_BRIDGE_DTMF_CHANNEL_0)) ||
+ ((who == c1) && (flags & AST_BRIDGE_DTMF_CHANNEL_1))))) {
+ /* Break out of bridge */
+ *fo = fr;
+ *rc = who;
+ ast_debug(1, "Oooh, got a %s\n", fr ? "digit" : "hangup");
+ if (c0->tech_pvt == pvt0)
+ if (pr0->set_rtp_peer(c0, NULL, NULL, NULL, 0, 0))
+ ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c0->name);
+ if (c1->tech_pvt == pvt1)
+ if (pr1->set_rtp_peer(c1, NULL, NULL, NULL, 0, 0))
+ ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c1->name);
+ ast_poll_channel_del(c0, c1);
+ return AST_BRIDGE_COMPLETE;
+ } else if ((fr->frametype == AST_FRAME_CONTROL) && !(flags & AST_BRIDGE_IGNORE_SIGS)) {
+ if ((fr->subclass == AST_CONTROL_HOLD) ||
+ (fr->subclass == AST_CONTROL_UNHOLD) ||
+ (fr->subclass == AST_CONTROL_VIDUPDATE)) {
+ if (fr->subclass == AST_CONTROL_HOLD) {
+ /* If we someone went on hold we want the other side to reinvite back to us */
+ if (who == c0)
+ pr1->set_rtp_peer(c1, NULL, NULL, NULL, 0, 0);
+ else
+ pr0->set_rtp_peer(c0, NULL, NULL, NULL, 0, 0);
+ } else if (fr->subclass == AST_CONTROL_UNHOLD) {
+ /* If they went off hold they should go back to being direct */
+ if (who == c0)
+ pr1->set_rtp_peer(c1, p0, vp0, tp0, codec0, ast_test_flag(p0, FLAG_NAT_ACTIVE));
+ else
+ pr0->set_rtp_peer(c0, p1, vp1, tp1, codec1, ast_test_flag(p1, FLAG_NAT_ACTIVE));
+ }
+ /* Update local address information */
+ ast_rtp_get_peer(p0, &t0);
+ memcpy(&ac0, &t0, sizeof(ac0));
+ ast_rtp_get_peer(p1, &t1);
+ memcpy(&ac1, &t1, sizeof(ac1));
+ /* Update codec information */
+ if (pr0->get_codec && c0->tech_pvt)
+ oldcodec0 = codec0 = pr0->get_codec(c0);
+ if (pr1->get_codec && c1->tech_pvt)
+ oldcodec1 = codec1 = pr1->get_codec(c1);
+ ast_indicate_data(other, fr->subclass, fr->data, fr->datalen);
+ ast_frfree(fr);
+ } else {
+ *fo = fr;
+ *rc = who;
+ ast_debug(1, "Got a FRAME_CONTROL (%d) frame on channel %s\n", fr->subclass, who->name);
+ return AST_BRIDGE_COMPLETE;
+ }
+ } else {
+ if ((fr->frametype == AST_FRAME_DTMF_BEGIN) ||
+ (fr->frametype == AST_FRAME_DTMF_END) ||
+ (fr->frametype == AST_FRAME_VOICE) ||
+ (fr->frametype == AST_FRAME_VIDEO) ||
+ (fr->frametype == AST_FRAME_IMAGE) ||
+ (fr->frametype == AST_FRAME_HTML) ||
+ (fr->frametype == AST_FRAME_MODEM) ||
+ (fr->frametype == AST_FRAME_TEXT)) {
+ ast_write(other, fr);
+ }
+ ast_frfree(fr);
+ }
+ /* Swap priority */
+#ifndef HAVE_EPOLL
+ cs[2] = cs[0];
+ cs[0] = cs[1];
+ cs[1] = cs[2];
+#endif
+ }
+
+ ast_poll_channel_del(c0, c1);
+
+ if (pr0->set_rtp_peer(c0, NULL, NULL, NULL, 0, 0))
+ ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c0->name);
+ if (pr1->set_rtp_peer(c1, NULL, NULL, NULL, 0, 0))
+ ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c1->name);
+
+ return AST_BRIDGE_FAILED;
+}
+
+/*! \brief P2P RTP Callback */
+#ifdef P2P_INTENSE
+static int p2p_rtp_callback(int *id, int fd, short events, void *cbdata)
+{
+ int res = 0, hdrlen = 12;
+ struct sockaddr_in sin;
+ socklen_t len;
+ unsigned int *header;
+ struct ast_rtp *rtp = cbdata, *bridged = NULL;
+
+ if (!rtp)
+ return 1;
+
+ len = sizeof(sin);
+ if ((res = recvfrom(fd, rtp->rawdata + AST_FRIENDLY_OFFSET, sizeof(rtp->rawdata) - AST_FRIENDLY_OFFSET, 0, (struct sockaddr *)&sin, &len)) < 0)
+ return 1;
+
+ header = (unsigned int *)(rtp->rawdata + AST_FRIENDLY_OFFSET);
+
+ /* If NAT support is turned on, then see if we need to change their address */
+ if ((rtp->nat) &&
+ ((rtp->them.sin_addr.s_addr != sin.sin_addr.s_addr) ||
+ (rtp->them.sin_port != sin.sin_port))) {
+ rtp->them = sin;
+ rtp->rxseqno = 0;
+ ast_set_flag(rtp, FLAG_NAT_ACTIVE);
+ if (option_debug || rtpdebug)
+ ast_debug(0, "P2P RTP NAT: Got audio from other end. Now sending to address %s:%d\n", ast_inet_ntoa(rtp->them.sin_addr), ntohs(rtp->them.sin_port));
+ }
+
+ /* Write directly out to other RTP stream if bridged */
+ if ((bridged = ast_rtp_get_bridged(rtp)))
+ bridge_p2p_rtp_write(rtp, bridged, header, res, hdrlen);
+
+ return 1;
+}
+
+/*! \brief Helper function to switch a channel and RTP stream into callback mode */
+static int p2p_callback_enable(struct ast_channel *chan, struct ast_rtp *rtp, int **iod)
+{
+ /* If we need DTMF, are looking for STUN, or we have no IO structure then we can't do direct callback */
+ if (ast_test_flag(rtp, FLAG_P2P_NEED_DTMF) || ast_test_flag(rtp, FLAG_HAS_STUN) || !rtp->io)
+ return 0;
+
+ /* If the RTP structure is already in callback mode, remove it temporarily */
+ if (rtp->ioid) {
+ ast_io_remove(rtp->io, rtp->ioid);
+ rtp->ioid = NULL;
+ }
+
+ /* Steal the file descriptors from the channel */
+ chan->fds[0] = -1;
+
+ /* Now, fire up callback mode */
+ iod[0] = ast_io_add(rtp->io, ast_rtp_fd(rtp), p2p_rtp_callback, AST_IO_IN, rtp);
+
+ return 1;
+}
+#else
+static int p2p_callback_enable(struct ast_channel *chan, struct ast_rtp *rtp, int **iod)
+{
+ return 0;
+}
+#endif
+
+/*! \brief Helper function to switch a channel and RTP stream out of callback mode */
+static int p2p_callback_disable(struct ast_channel *chan, struct ast_rtp *rtp, int **iod)
+{
+ ast_channel_lock(chan);
+
+ /* Remove the callback from the IO context */
+ ast_io_remove(rtp->io, iod[0]);
+
+ /* Restore file descriptors */
+ chan->fds[0] = ast_rtp_fd(rtp);
+ ast_channel_unlock(chan);
+
+ /* Restore callback mode if previously used */
+ if (ast_test_flag(rtp, FLAG_CALLBACK_MODE))
+ rtp->ioid = ast_io_add(rtp->io, ast_rtp_fd(rtp), rtpread, AST_IO_IN, rtp);
+
+ return 0;
+}
+
+/*! \brief Helper function that sets what an RTP structure is bridged to */
+static void p2p_set_bridge(struct ast_rtp *rtp0, struct ast_rtp *rtp1)
+{
+ rtp_bridge_lock(rtp0);
+ rtp0->bridged = rtp1;
+ rtp_bridge_unlock(rtp0);
+
+ return;
+}
+
+/*! \brief Bridge loop for partial native bridge (packet2packet)
+
+ In p2p mode, Asterisk is a very basic RTP proxy, just forwarding whatever
+ rtp/rtcp we get in to the channel.
+ \note this currently only works for Audio
+*/
+static enum ast_bridge_result bridge_p2p_loop(struct ast_channel *c0, struct ast_channel *c1, struct ast_rtp *p0, struct ast_rtp *p1, int timeoutms, int flags, struct ast_frame **fo, struct ast_channel **rc, void *pvt0, void *pvt1)
+{
+ struct ast_frame *fr = NULL;
+ struct ast_channel *who = NULL, *other = NULL, *cs[3] = {NULL, };
+ int *p0_iod[2] = {NULL, NULL}, *p1_iod[2] = {NULL, NULL};
+ int p0_callback = 0, p1_callback = 0;
+ enum ast_bridge_result res = AST_BRIDGE_FAILED;
+
+ /* Okay, setup each RTP structure to do P2P forwarding */
+ ast_clear_flag(p0, FLAG_P2P_SENT_MARK);
+ p2p_set_bridge(p0, p1);
+ ast_clear_flag(p1, FLAG_P2P_SENT_MARK);
+ p2p_set_bridge(p1, p0);
+
+ /* Activate callback modes if possible */
+ p0_callback = p2p_callback_enable(c0, p0, &p0_iod[0]);
+ p1_callback = p2p_callback_enable(c1, p1, &p1_iod[0]);
+
+ /* Now let go of the channel locks and be on our way */
+ ast_channel_unlock(c0);
+ ast_channel_unlock(c1);
+
+ ast_poll_channel_add(c0, c1);
+
+ /* Go into a loop forwarding frames until we don't need to anymore */
+ cs[0] = c0;
+ cs[1] = c1;
+ cs[2] = NULL;
+ for (;;) {
+ /* If the underlying formats have changed force this bridge to break */
+ if ((c0->rawreadformat != c1->rawwriteformat) || (c1->rawreadformat != c0->rawwriteformat)) {
+ ast_debug(3, "p2p-rtp-bridge: Oooh, formats changed, backing out\n");
+ res = AST_BRIDGE_FAILED_NOWARN;
+ break;
+ }
+ /* Check if anything changed */
+ if ((c0->tech_pvt != pvt0) ||
+ (c1->tech_pvt != pvt1) ||
+ (c0->masq || c0->masqr || c1->masq || c1->masqr) ||
+ (c0->monitor || c0->audiohooks || c1->monitor || c1->audiohooks)) {
+ ast_debug(3, "p2p-rtp-bridge: Oooh, something is weird, backing out\n");
+ /* If a masquerade needs to happen we have to try to read in a frame so that it actually happens. Without this we risk being called again and going into a loop */
+ if ((c0->masq || c0->masqr) && (fr = ast_read(c0)))
+ ast_frfree(fr);
+ if ((c1->masq || c1->masqr) && (fr = ast_read(c1)))
+ ast_frfree(fr);
+ res = AST_BRIDGE_RETRY;
+ break;
+ }
+ /* Wait on a channel to feed us a frame */
+ if (!(who = ast_waitfor_n(cs, 2, &timeoutms))) {
+ if (!timeoutms) {
+ res = AST_BRIDGE_RETRY;
+ break;
+ }
+ if (option_debug > 2)
+ ast_log(LOG_NOTICE, "p2p-rtp-bridge: Ooh, empty read...\n");
+ if (ast_check_hangup(c0) || ast_check_hangup(c1))
+ break;
+ continue;
+ }
+ /* Read in frame from channel */
+ fr = ast_read(who);
+ other = (who == c0) ? c1 : c0;
+ /* Depending on the frame we may need to break out of our bridge */
+ if (!fr || ((fr->frametype == AST_FRAME_DTMF_BEGIN || fr->frametype == AST_FRAME_DTMF_END) &&
+ ((who == c0) && (flags & AST_BRIDGE_DTMF_CHANNEL_0)) |
+ ((who == c1) && (flags & AST_BRIDGE_DTMF_CHANNEL_1)))) {
+ /* Record received frame and who */
+ *fo = fr;
+ *rc = who;
+ ast_debug(3, "p2p-rtp-bridge: Ooh, got a %s\n", fr ? "digit" : "hangup");
+ res = AST_BRIDGE_COMPLETE;
+ break;
+ } else if ((fr->frametype == AST_FRAME_CONTROL) && !(flags & AST_BRIDGE_IGNORE_SIGS)) {
+ if ((fr->subclass == AST_CONTROL_HOLD) ||
+ (fr->subclass == AST_CONTROL_UNHOLD) ||
+ (fr->subclass == AST_CONTROL_VIDUPDATE)) {
+ /* If we are going on hold, then break callback mode and P2P bridging */
+ if (fr->subclass == AST_CONTROL_HOLD) {
+ if (p0_callback)
+ p0_callback = p2p_callback_disable(c0, p0, &p0_iod[0]);
+ if (p1_callback)
+ p1_callback = p2p_callback_disable(c1, p1, &p1_iod[0]);
+ p2p_set_bridge(p0, NULL);
+ p2p_set_bridge(p1, NULL);
+ } else if (fr->subclass == AST_CONTROL_UNHOLD) {
+ /* If we are off hold, then go back to callback mode and P2P bridging */
+ ast_clear_flag(p0, FLAG_P2P_SENT_MARK);
+ p2p_set_bridge(p0, p1);
+ ast_clear_flag(p1, FLAG_P2P_SENT_MARK);
+ p2p_set_bridge(p1, p0);
+ p0_callback = p2p_callback_enable(c0, p0, &p0_iod[0]);
+ p1_callback = p2p_callback_enable(c1, p1, &p1_iod[0]);
+ }
+ ast_indicate_data(other, fr->subclass, fr->data, fr->datalen);
+ ast_frfree(fr);
+ } else {
+ *fo = fr;
+ *rc = who;
+ ast_debug(3, "p2p-rtp-bridge: Got a FRAME_CONTROL (%d) frame on channel %s\n", fr->subclass, who->name);
+ res = AST_BRIDGE_COMPLETE;
+ break;
+ }
+ } else {
+ if ((fr->frametype == AST_FRAME_DTMF_BEGIN) ||
+ (fr->frametype == AST_FRAME_DTMF_END) ||
+ (fr->frametype == AST_FRAME_VOICE) ||
+ (fr->frametype == AST_FRAME_VIDEO) ||
+ (fr->frametype == AST_FRAME_IMAGE) ||
+ (fr->frametype == AST_FRAME_HTML) ||
+ (fr->frametype == AST_FRAME_MODEM) ||
+ (fr->frametype == AST_FRAME_TEXT)) {
+ ast_write(other, fr);
+ }
+
+ ast_frfree(fr);
+ }
+ /* Swap priority */
+#ifndef HAVE_EPOLL
+ cs[2] = cs[0];
+ cs[0] = cs[1];
+ cs[1] = cs[2];
+#endif
+ }
+
+ /* If we are totally avoiding the core, then restore our link to it */
+ if (p0_callback)
+ p0_callback = p2p_callback_disable(c0, p0, &p0_iod[0]);
+ if (p1_callback)
+ p1_callback = p2p_callback_disable(c1, p1, &p1_iod[0]);
+
+ /* Break out of the direct bridge */
+ p2p_set_bridge(p0, NULL);
+ p2p_set_bridge(p1, NULL);
+
+ ast_poll_channel_del(c0, c1);
+
+ return res;
+}
+
+/*! \page AstRTPbridge The Asterisk RTP bridge
+ The RTP bridge is called from the channel drivers that are using the RTP
+ subsystem in Asterisk - like SIP, H.323 and Jingle/Google Talk.
+
+ This bridge aims to offload the Asterisk server by setting up
+ the media stream directly between the endpoints, keeping the
+ signalling in Asterisk.
+
+ It checks with the channel driver, using a callback function, if
+ there are possibilities for a remote bridge.
+
+ If this fails, the bridge hands off to the core bridge. Reasons
+ can be NAT support needed, DTMF features in audio needed by
+ the PBX for transfers or spying/monitoring on channels.
+
+ If transcoding is needed - we can't do a remote bridge.
+ If only NAT support is needed, we're using Asterisk in
+ RTP proxy mode with the p2p RTP bridge, basically
+ forwarding incoming audio packets to the outbound
+ stream on a network level.
+
+ References:
+ - ast_rtp_bridge()
+ - ast_channel_early_bridge()
+ - ast_channel_bridge()
+ - rtp.c
+ - rtp.h
+*/
+/*! \brief Bridge calls. If possible and allowed, initiate
+ re-invite so the peers exchange media directly outside
+ of Asterisk.
+*/
+enum ast_bridge_result ast_rtp_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc, int timeoutms)
+{
+ struct ast_rtp *p0 = NULL, *p1 = NULL; /* Audio RTP Channels */
+ struct ast_rtp *vp0 = NULL, *vp1 = NULL; /* Video RTP channels */
+ struct ast_rtp *tp0 = NULL, *tp1 = NULL; /* Text RTP channels */
+ struct ast_rtp_protocol *pr0 = NULL, *pr1 = NULL;
+ enum ast_rtp_get_result audio_p0_res = AST_RTP_GET_FAILED, video_p0_res = AST_RTP_GET_FAILED, text_p0_res = AST_RTP_GET_FAILED;
+ enum ast_rtp_get_result audio_p1_res = AST_RTP_GET_FAILED, video_p1_res = AST_RTP_GET_FAILED, text_p1_res = AST_RTP_GET_FAILED;
+ enum ast_bridge_result res = AST_BRIDGE_FAILED;
+ int codec0 = 0, codec1 = 0;
+ void *pvt0 = NULL, *pvt1 = NULL;
+
+ /* Lock channels */
+ ast_channel_lock(c0);
+ while (ast_channel_trylock(c1)) {
+ ast_channel_unlock(c0);
+ usleep(1);
+ ast_channel_lock(c0);
+ }
+
+ /* Find channel driver interfaces */
+ if (!(pr0 = get_proto(c0))) {
+ ast_log(LOG_WARNING, "Can't find native functions for channel '%s'\n", c0->name);
+ ast_channel_unlock(c0);
+ ast_channel_unlock(c1);
+ return AST_BRIDGE_FAILED;
+ }
+ if (!(pr1 = get_proto(c1))) {
+ ast_log(LOG_WARNING, "Can't find native functions for channel '%s'\n", c1->name);
+ ast_channel_unlock(c0);
+ ast_channel_unlock(c1);
+ return AST_BRIDGE_FAILED;
+ }
+
+ /* Get channel specific interface structures */
+ pvt0 = c0->tech_pvt;
+ pvt1 = c1->tech_pvt;
+
+ /* Get audio and video interface (if native bridge is possible) */
+ audio_p0_res = pr0->get_rtp_info(c0, &p0);
+ video_p0_res = pr0->get_vrtp_info ? pr0->get_vrtp_info(c0, &vp0) : AST_RTP_GET_FAILED;
+ text_p0_res = pr0->get_trtp_info ? pr0->get_trtp_info(c0, &vp0) : AST_RTP_GET_FAILED;
+ audio_p1_res = pr1->get_rtp_info(c1, &p1);
+ video_p1_res = pr1->get_vrtp_info ? pr1->get_vrtp_info(c1, &vp1) : AST_RTP_GET_FAILED;
+ text_p1_res = pr1->get_trtp_info ? pr1->get_trtp_info(c1, &vp1) : AST_RTP_GET_FAILED;
+
+ /* If we are carrying video, and both sides are not reinviting... then fail the native bridge */
+ if (video_p0_res != AST_RTP_GET_FAILED && (audio_p0_res != AST_RTP_TRY_NATIVE || video_p0_res != AST_RTP_TRY_NATIVE))
+ audio_p0_res = AST_RTP_GET_FAILED;
+ if (video_p1_res != AST_RTP_GET_FAILED && (audio_p1_res != AST_RTP_TRY_NATIVE || video_p1_res != AST_RTP_TRY_NATIVE))
+ audio_p1_res = AST_RTP_GET_FAILED;
+
+ /* Check if a bridge is possible (partial/native) */
+ if (audio_p0_res == AST_RTP_GET_FAILED || audio_p1_res == AST_RTP_GET_FAILED) {
+ /* Somebody doesn't want to play... */
+ ast_channel_unlock(c0);
+ ast_channel_unlock(c1);
+ return AST_BRIDGE_FAILED_NOWARN;
+ }
+
+ /* If we need to feed DTMF frames into the core then only do a partial native bridge */
+ if (ast_test_flag(p0, FLAG_HAS_DTMF) && (flags & AST_BRIDGE_DTMF_CHANNEL_0)) {
+ ast_set_flag(p0, FLAG_P2P_NEED_DTMF);
+ audio_p0_res = AST_RTP_TRY_PARTIAL;
+ }
+
+ if (ast_test_flag(p1, FLAG_HAS_DTMF) && (flags & AST_BRIDGE_DTMF_CHANNEL_1)) {
+ ast_set_flag(p1, FLAG_P2P_NEED_DTMF);
+ audio_p1_res = AST_RTP_TRY_PARTIAL;
+ }
+
+ /* If both sides are not using the same method of DTMF transmission
+ * (ie: one is RFC2833, other is INFO... then we can not do direct media.
+ * --------------------------------------------------
+ * | DTMF Mode | HAS_DTMF | Accepts Begin Frames |
+ * |-----------|------------|-----------------------|
+ * | Inband | False | True |
+ * | RFC2833 | True | True |
+ * | SIP INFO | False | False |
+ * --------------------------------------------------
+ * However, if DTMF from both channels is being monitored by the core, then
+ * we can still do packet-to-packet bridging, because passing through the
+ * core will handle DTMF mode translation.
+ */
+ if ( (ast_test_flag(p0, FLAG_HAS_DTMF) != ast_test_flag(p1, FLAG_HAS_DTMF)) ||
+ (!c0->tech->send_digit_begin != !c1->tech->send_digit_begin)) {
+ if (!ast_test_flag(p0, FLAG_P2P_NEED_DTMF) || !ast_test_flag(p1, FLAG_P2P_NEED_DTMF)) {
+ ast_channel_unlock(c0);
+ ast_channel_unlock(c1);
+ return AST_BRIDGE_FAILED_NOWARN;
+ }
+ audio_p0_res = AST_RTP_TRY_PARTIAL;
+ audio_p1_res = AST_RTP_TRY_PARTIAL;
+ }
+
+ /* If the core will need to compensate and the P2P bridge will need to feed up DTMF frames then we can not reliably do so yet, so do not P2P bridge */
+ if ((audio_p0_res == AST_RTP_TRY_PARTIAL && ast_test_flag(p0, FLAG_P2P_NEED_DTMF) && ast_test_flag(p0, FLAG_DTMF_COMPENSATE)) ||
+ (audio_p1_res == AST_RTP_TRY_PARTIAL && ast_test_flag(p1, FLAG_P2P_NEED_DTMF) && ast_test_flag(p1, FLAG_DTMF_COMPENSATE))) {
+ ast_channel_unlock(c0);
+ ast_channel_unlock(c1);
+ return AST_BRIDGE_FAILED_NOWARN;
+ }
+
+ /* Get codecs from both sides */
+ codec0 = pr0->get_codec ? pr0->get_codec(c0) : 0;
+ codec1 = pr1->get_codec ? pr1->get_codec(c1) : 0;
+ if (codec0 && codec1 && !(codec0 & codec1)) {
+ /* Hey, we can't do native bridging if both parties speak different codecs */
+ ast_debug(3, "Channel codec0 = %d is not codec1 = %d, cannot native bridge in RTP.\n", codec0, codec1);
+ ast_channel_unlock(c0);
+ ast_channel_unlock(c1);
+ return AST_BRIDGE_FAILED_NOWARN;
+ }
+
+ /* If either side can only do a partial bridge, then don't try for a true native bridge */
+ if (audio_p0_res == AST_RTP_TRY_PARTIAL || audio_p1_res == AST_RTP_TRY_PARTIAL) {
+ struct ast_format_list fmt0, fmt1;
+
+ /* In order to do Packet2Packet bridging both sides must be in the same rawread/rawwrite */
+ if (c0->rawreadformat != c1->rawwriteformat || c1->rawreadformat != c0->rawwriteformat) {
+ ast_debug(1, "Cannot packet2packet bridge - raw formats are incompatible\n");
+ ast_channel_unlock(c0);
+ ast_channel_unlock(c1);
+ return AST_BRIDGE_FAILED_NOWARN;
+ }
+ /* They must also be using the same packetization */
+ fmt0 = ast_codec_pref_getsize(&p0->pref, c0->rawreadformat);
+ fmt1 = ast_codec_pref_getsize(&p1->pref, c1->rawreadformat);
+ if (fmt0.cur_ms != fmt1.cur_ms) {
+ ast_debug(1, "Cannot packet2packet bridge - packetization settings prevent it\n");
+ ast_channel_unlock(c0);
+ ast_channel_unlock(c1);
+ return AST_BRIDGE_FAILED_NOWARN;
+ }
+
+ ast_verb(3, "Packet2Packet bridging %s and %s\n", c0->name, c1->name);
+ res = bridge_p2p_loop(c0, c1, p0, p1, timeoutms, flags, fo, rc, pvt0, pvt1);
+ } else {
+ ast_verb(3, "Native bridging %s and %s\n", c0->name, c1->name);
+ res = bridge_native_loop(c0, c1, p0, p1, vp0, vp1, tp0, tp1, pr0, pr1, codec0, codec1, timeoutms, flags, fo, rc, pvt0, pvt1);
+ }
+
+ return res;
+}
+
+static char *rtp_do_debug_ip(struct ast_cli_args *a)
+{
+ struct hostent *hp;
+ struct ast_hostent ahp;
+ int port = 0;
+ char *p, *arg;
+
+ arg = a->argv[3];
+ p = strstr(arg, ":");
+ if (p) {
+ *p = '\0';
+ p++;
+ port = atoi(p);
+ }
+ hp = ast_gethostbyname(arg, &ahp);
+ if (hp == NULL) {
+ ast_cli(a->fd, "Lookup failed for '%s'\n", arg);
+ return CLI_FAILURE;
+ }
+ rtpdebugaddr.sin_family = AF_INET;
+ memcpy(&rtpdebugaddr.sin_addr, hp->h_addr, sizeof(rtpdebugaddr.sin_addr));
+ rtpdebugaddr.sin_port = htons(port);
+ if (port == 0)
+ ast_cli(a->fd, "RTP Debugging Enabled for IP: %s\n", ast_inet_ntoa(rtpdebugaddr.sin_addr));
+ else
+ ast_cli(a->fd, "RTP Debugging Enabled for IP: %s:%d\n", ast_inet_ntoa(rtpdebugaddr.sin_addr), port);
+ rtpdebug = 1;
+ return CLI_SUCCESS;
+}
+
+static char *rtcp_do_debug_ip(struct ast_cli_args *a)
+{
+ struct hostent *hp;
+ struct ast_hostent ahp;
+ int port = 0;
+ char *p, *arg;
+
+ arg = a->argv[3];
+ p = strstr(arg, ":");
+ if (p) {
+ *p = '\0';
+ p++;
+ port = atoi(p);
+ }
+ hp = ast_gethostbyname(arg, &ahp);
+ if (hp == NULL) {
+ ast_cli(a->fd, "Lookup failed for '%s'\n", arg);
+ return CLI_FAILURE;
+ }
+ rtcpdebugaddr.sin_family = AF_INET;
+ memcpy(&rtcpdebugaddr.sin_addr, hp->h_addr, sizeof(rtcpdebugaddr.sin_addr));
+ rtcpdebugaddr.sin_port = htons(port);
+ if (port == 0)
+ ast_cli(a->fd, "RTCP Debugging Enabled for IP: %s\n", ast_inet_ntoa(rtcpdebugaddr.sin_addr));
+ else
+ ast_cli(a->fd, "RTCP Debugging Enabled for IP: %s:%d\n", ast_inet_ntoa(rtcpdebugaddr.sin_addr), port);
+ rtcpdebug = 1;
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_rtp_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "rtp debug [off|ip]";
+ e->usage =
+ "Usage: rtp debug [off]|[ip host[:port]]\n"
+ " Enable/Disable dumping of all RTP packets. If 'ip' is\n"
+ " specified, limit the dumped packets to those to and from\n"
+ " the specified 'host' with optional port.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc < 2 || a->argc > 4)
+ return CLI_SHOWUSAGE;
+ if (a->argc == 2) {
+ rtpdebug = 1;
+ memset(&rtpdebugaddr, 0, sizeof(rtpdebugaddr));
+ ast_cli(a->fd, "RTP Debugging Enabled\n");
+ } else if (a->argc == 3) {
+ if (strncasecmp(a->argv[2], "off", 3))
+ return CLI_SHOWUSAGE;
+ rtpdebug = 0;
+ ast_cli(a->fd, "RTP Debugging Disabled\n");
+ } else {
+ if (strncasecmp(a->argv[2], "ip", 2))
+ return CLI_SHOWUSAGE;
+ return rtp_do_debug_ip(a);
+ }
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_rtcp_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "rtcp debug [off|ip]";
+ e->usage =
+ "Usage: rtcp debug [off]|[ip host[:port]]\n"
+ " Enable/Disable dumping of all RTCP packets. If 'ip' is\n"
+ " specified, limit the dumped packets to those to and from\n"
+ " the specified 'host' with optional port.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc < 2 || a->argc > 4)
+ return CLI_SHOWUSAGE;
+ if (a->argc == 2) {
+ rtcpdebug = 1;
+ memset(&rtcpdebugaddr, 0, sizeof(rtcpdebugaddr));
+ ast_cli(a->fd, "RTCP Debugging Enabled\n");
+ } else if (a->argc == 3) {
+ if (strncasecmp(a->argv[2], "off", 3))
+ return CLI_SHOWUSAGE;
+ rtcpdebug = 0;
+ ast_cli(a->fd, "RTCP Debugging Disabled\n");
+ } else {
+ if (strncasecmp(a->argv[2], "ip", 2))
+ return CLI_SHOWUSAGE;
+ return rtcp_do_debug_ip(a);
+ }
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_rtcp_stats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "rtcp stats [off]";
+ e->usage =
+ "Usage: rtcp stats [off]\n"
+ " Enable/Disable dumping of RTCP stats.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc < 2 || a->argc > 3)
+ return CLI_SHOWUSAGE;
+ if (a->argc == 3 && strncasecmp(a->argv[2], "off", 3))
+ return CLI_SHOWUSAGE;
+
+ rtcpstats = (a->argc == 3) ? 0 : 1;
+ ast_cli(a->fd, "RTCP Stats %s\n", rtcpstats ? "Enabled" : "Disabled");
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_stun_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "stun debug [off]";
+ e->usage =
+ "Usage: stun debug [off]\n"
+ " Enable/Disable STUN (Simple Traversal of UDP through NATs)\n"
+ " debugging\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc < 2 || a->argc > 3)
+ return CLI_SHOWUSAGE;
+ if (a->argc == 3 && strncasecmp(a->argv[2], "off", 3))
+ return CLI_SHOWUSAGE;
+
+ stundebug = (a->argc == 3) ? 0 : 1;
+ ast_cli(a->fd, "STUN Debugging %s\n", stundebug ? "Enabled" : "Disabled");
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli_rtp[] = {
+ AST_CLI_DEFINE(handle_cli_rtp_debug, "Enable/Disable RTP debugging"),
+ AST_CLI_DEFINE(handle_cli_rtcp_debug, "Enable/Disable RTCP debugging"),
+ AST_CLI_DEFINE(handle_cli_rtcp_stats, "Enable/Disable RTCP stats"),
+ AST_CLI_DEFINE(handle_cli_stun_debug, "Enable/Disable STUN debugging"),
+};
+
+static int __ast_rtp_reload(int reload)
+{
+ struct ast_config *cfg;
+ const char *s;
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+
+ if ((cfg = ast_config_load("rtp.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
+ return 0;
+
+ rtpstart = 5000;
+ rtpend = 31000;
+ dtmftimeout = DEFAULT_DTMF_TIMEOUT;
+ if (cfg) {
+ if ((s = ast_variable_retrieve(cfg, "general", "rtpstart"))) {
+ rtpstart = atoi(s);
+ if (rtpstart < 1024)
+ rtpstart = 1024;
+ if (rtpstart > 65535)
+ rtpstart = 65535;
+ }
+ if ((s = ast_variable_retrieve(cfg, "general", "rtpend"))) {
+ rtpend = atoi(s);
+ if (rtpend < 1024)
+ rtpend = 1024;
+ if (rtpend > 65535)
+ rtpend = 65535;
+ }
+ if ((s = ast_variable_retrieve(cfg, "general", "rtcpinterval"))) {
+ rtcpinterval = atoi(s);
+ if (rtcpinterval == 0)
+ rtcpinterval = 0; /* Just so we're clear... it's zero */
+ if (rtcpinterval < RTCP_MIN_INTERVALMS)
+ rtcpinterval = RTCP_MIN_INTERVALMS; /* This catches negative numbers too */
+ if (rtcpinterval > RTCP_MAX_INTERVALMS)
+ rtcpinterval = RTCP_MAX_INTERVALMS;
+ }
+ if ((s = ast_variable_retrieve(cfg, "general", "rtpchecksums"))) {
+#ifdef SO_NO_CHECK
+ if (ast_false(s))
+ nochecksums = 1;
+ else
+ nochecksums = 0;
+#else
+ if (ast_false(s))
+ ast_log(LOG_WARNING, "Disabling RTP checksums is not supported on this operating system!\n");
+#endif
+ }
+ if ((s = ast_variable_retrieve(cfg, "general", "dtmftimeout"))) {
+ dtmftimeout = atoi(s);
+ if ((dtmftimeout < 0) || (dtmftimeout > 20000)) {
+ ast_log(LOG_WARNING, "DTMF timeout of '%d' outside range, using default of '%d' instead\n",
+ dtmftimeout, DEFAULT_DTMF_TIMEOUT);
+ dtmftimeout = DEFAULT_DTMF_TIMEOUT;
+ };
+ }
+ ast_config_destroy(cfg);
+ }
+ if (rtpstart >= rtpend) {
+ ast_log(LOG_WARNING, "Unreasonable values for RTP start/end port in rtp.conf\n");
+ rtpstart = 5000;
+ rtpend = 31000;
+ }
+ ast_verb(2, "RTP Allocating from port range %d -> %d\n", rtpstart, rtpend);
+ return 0;
+}
+
+int ast_rtp_reload(void)
+{
+ return __ast_rtp_reload(1);
+}
+
+/*! \brief Initialize the RTP system in Asterisk */
+void ast_rtp_init(void)
+{
+ ast_cli_register_multiple(cli_rtp, sizeof(cli_rtp) / sizeof(struct ast_cli_entry));
+ __ast_rtp_reload(0);
+}
+
diff --git a/trunk/main/say.c b/trunk/main/say.c
new file mode 100644
index 000000000..da660d6fc
--- /dev/null
+++ b/trunk/main/say.c
@@ -0,0 +1,7461 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ * George Konstantoulakis <gkon@inaccessnetworks.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 Say numbers and dates (maybe words one day too)
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \note 12-16-2004 : Support for Greek added by InAccess Networks (work funded by HOL, www.hol.gr) George Konstantoulakis <gkon@inaccessnetworks.com>
+ *
+ * \note 2007-02-08 : Support for Georgian added by Alexander Shaduri <ashaduri@gmail.com>,
+ * Next Generation Networks (NGN).
+ * \note 2007-03-20 : Support for Thai added by Dome C. <dome@tel.co.th>,
+ * IP Crossing Co.,Ltd.
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <netinet/in.h>
+#include <time.h>
+#include <ctype.h>
+#include <math.h>
+
+#ifdef SOLARIS
+#include <iso/limits_iso.h>
+#endif
+
+#include "asterisk/file.h"
+#include "asterisk/channel.h"
+#include "asterisk/say.h"
+#include "asterisk/lock.h"
+#include "asterisk/localtime.h"
+#include "asterisk/utils.h"
+
+/* Forward declaration */
+static int wait_file(struct ast_channel *chan, const char *ints, const char *file, const char *lang);
+
+
+static int say_character_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
+{
+ const char *fn;
+ char fnbuf[10], asciibuf[20] = "letters/ascii";
+ char ltr;
+ int num = 0;
+ int res = 0;
+
+ while (str[num] && !res) {
+ fn = NULL;
+ switch (str[num]) {
+ case ('*'):
+ fn = "digits/star";
+ break;
+ case ('#'):
+ fn = "digits/pound";
+ break;
+ case ('!'):
+ fn = "letters/exclaimation-point";
+ break;
+ case ('@'):
+ fn = "letters/at";
+ break;
+ case ('$'):
+ fn = "letters/dollar";
+ break;
+ case ('-'):
+ fn = "letters/dash";
+ break;
+ case ('.'):
+ fn = "letters/dot";
+ break;
+ case ('='):
+ fn = "letters/equals";
+ break;
+ case ('+'):
+ fn = "letters/plus";
+ break;
+ case ('/'):
+ fn = "letters/slash";
+ break;
+ case (' '):
+ fn = "letters/space";
+ break;
+ case ('0'):
+ case ('1'):
+ case ('2'):
+ case ('3'):
+ case ('4'):
+ case ('5'):
+ case ('6'):
+ case ('7'):
+ case ('8'):
+ case ('9'):
+ strcpy(fnbuf, "digits/X");
+ fnbuf[7] = str[num];
+ fn = fnbuf;
+ break;
+ default:
+ ltr = str[num];
+ if ('A' <= ltr && ltr <= 'Z') ltr += 'a' - 'A'; /* file names are all lower-case */
+ strcpy(fnbuf, "letters/X");
+ fnbuf[8] = ltr;
+ fn = fnbuf;
+ }
+ if ((fn && ast_fileexists(fn, NULL, lang) > 0) ||
+ (snprintf(asciibuf + 13, sizeof(asciibuf) - 13, "%d", str[num]) > 0 && ast_fileexists(asciibuf, NULL, lang) > 0 && (fn = asciibuf))) {
+ res = ast_streamfile(chan, fn, lang);
+ if (!res) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ res = ast_waitstream(chan, ints);
+ }
+ ast_stopstream(chan);
+ }
+ num++;
+ }
+
+ return res;
+}
+
+static int say_phonetic_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
+{
+ const char *fn;
+ char fnbuf[256];
+ char ltr;
+ int num = 0;
+ int res = 0;
+
+ while (str[num] && !res) {
+ fn = NULL;
+ switch (str[num]) {
+ case ('*'):
+ fn = "digits/star";
+ break;
+ case ('#'):
+ fn = "digits/pound";
+ break;
+ case ('!'):
+ fn = "letters/exclaimation-point";
+ break;
+ case ('@'):
+ fn = "letters/at";
+ break;
+ case ('$'):
+ fn = "letters/dollar";
+ break;
+ case ('-'):
+ fn = "letters/dash";
+ break;
+ case ('.'):
+ fn = "letters/dot";
+ break;
+ case ('='):
+ fn = "letters/equals";
+ break;
+ case ('+'):
+ fn = "letters/plus";
+ break;
+ case ('/'):
+ fn = "letters/slash";
+ break;
+ case (' '):
+ fn = "letters/space";
+ break;
+ case ('0'):
+ case ('1'):
+ case ('2'):
+ case ('3'):
+ case ('4'):
+ case ('5'):
+ case ('6'):
+ case ('7'):
+ case ('8'):
+ strcpy(fnbuf, "digits/X");
+ fnbuf[7] = str[num];
+ fn = fnbuf;
+ break;
+ default: /* '9' falls here... */
+ ltr = str[num];
+ if ('A' <= ltr && ltr <= 'Z') ltr += 'a' - 'A'; /* file names are all lower-case */
+ strcpy(fnbuf, "phonetic/X_p");
+ fnbuf[9] = ltr;
+ fn = fnbuf;
+ }
+ if (fn && ast_fileexists(fn, NULL, lang) > 0) {
+ res = ast_streamfile(chan, fn, lang);
+ if (!res) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ res = ast_waitstream(chan, ints);
+ }
+ ast_stopstream(chan);
+ }
+ num++;
+ }
+
+ return res;
+}
+
+static int say_digit_str_full(struct ast_channel *chan, const char *str, const char *ints, const char *lang, int audiofd, int ctrlfd)
+{
+ const char *fn;
+ char fnbuf[256];
+ int num = 0;
+ int res = 0;
+
+ while (str[num] && !res) {
+ fn = NULL;
+ switch (str[num]) {
+ case ('*'):
+ fn = "digits/star";
+ break;
+ case ('#'):
+ fn = "digits/pound";
+ break;
+ case ('-'):
+ fn = "digits/minus";
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ strcpy(fnbuf, "digits/X");
+ fnbuf[7] = str[num];
+ fn = fnbuf;
+ break;
+ }
+ if (fn && ast_fileexists(fn, NULL, lang) > 0) {
+ res = ast_streamfile(chan, fn, lang);
+ if (!res) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ res = ast_waitstream(chan, ints);
+ }
+ ast_stopstream(chan);
+ }
+ num++;
+ }
+
+ return res;
+}
+
+/* Forward declarations */
+/*! \page Def_syntaxlang Asterisk Language Syntaxes supported
+ \note Not really language codes.
+ For these language codes, Asterisk will change the syntax when
+ saying numbers (and in some cases dates and voicemail messages
+ as well)
+ \arg \b da - Danish
+ \arg \b de - German
+ \arg \b en - English (US)
+ \arg \b en_GB - English (British)
+ \arg \b es - Spanish, Mexican
+ \arg \b fr - French
+ \arg \b he - Hebrew
+ \arg \b it - Italian
+ \arg \b nl - Dutch
+ \arg \b no - Norwegian
+ \arg \b pl - Polish
+ \arg \b pt - Portuguese
+ \arg \b pt_BR - Portuguese (Brazil)
+ \arg \b se - Swedish
+ \arg \b tw - Taiwanese / Chinese
+ \arg \b ru - Russian
+ \arg \b ge - Georgian
+ \arg \b hu - Hungarian
+
+ \par Gender:
+ For Some languages the numbers differ for gender and plural.
+ \arg Use the option argument 'f' for female, 'm' for male and 'n' for neuter in languages like Portuguese, French, Spanish and German.
+ \arg use the option argument 'c' is for commune and 'n' for neuter gender in nordic languages like Danish, Swedish and Norwegian.
+ use the option argument 'p' for plural enumerations like in German
+
+ Date/Time functions currently have less languages supported than saynumber().
+
+ \todo Note that in future, we need to move to a model where we can differentiate further - e.g. between en_US & en_UK
+
+ See contrib/i18n.testsuite.conf for some examples of the different syntaxes
+
+ \par Portuguese
+ Portuguese sound files needed for Time/Date functions:
+ pt-ah
+ pt-ao
+ pt-de
+ pt-e
+ pt-ora
+ pt-meianoite
+ pt-meiodia
+ pt-sss
+
+ \par Spanish
+ Spanish sound files needed for Time/Date functions:
+ es-de
+ es-el
+
+ \par Italian
+ Italian sound files needed for Time/Date functions:
+ ore-una
+ ore-mezzanotte
+
+*/
+
+/* Forward declarations of language specific variants of ast_say_number_full */
+static int ast_say_number_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
+static int ast_say_number_full_cz(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
+static int ast_say_number_full_da(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
+static int ast_say_number_full_de(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
+static int ast_say_number_full_en_GB(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
+static int ast_say_number_full_es(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
+static int ast_say_number_full_fr(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
+static int ast_say_number_full_he(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
+static int ast_say_number_full_it(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
+static int ast_say_number_full_nl(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
+static int ast_say_number_full_no(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
+static int ast_say_number_full_pl(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
+static int ast_say_number_full_pt(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
+static int ast_say_number_full_se(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
+static int ast_say_number_full_tw(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
+static int ast_say_number_full_gr(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
+static int ast_say_number_full_ru(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
+static int ast_say_number_full_ge(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
+static int ast_say_number_full_hu(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
+static int ast_say_number_full_th(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
+
+/* Forward declarations of language specific variants of ast_say_enumeration_full */
+static int ast_say_enumeration_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd);
+static int ast_say_enumeration_full_da(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
+static int ast_say_enumeration_full_de(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd);
+
+/* Forward declarations of ast_say_date, ast_say_datetime and ast_say_time functions */
+static int ast_say_date_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_date_da(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_date_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_date_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_date_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_date_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_date_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_date_ge(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_date_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_date_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+
+static int ast_say_date_with_format_en(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
+static int ast_say_date_with_format_da(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
+static int ast_say_date_with_format_de(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
+static int ast_say_date_with_format_es(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
+static int ast_say_date_with_format_he(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
+static int ast_say_date_with_format_fr(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
+static int ast_say_date_with_format_it(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
+static int ast_say_date_with_format_nl(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
+static int ast_say_date_with_format_pl(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
+static int ast_say_date_with_format_pt(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
+static int ast_say_date_with_format_tw(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
+static int ast_say_date_with_format_gr(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
+static int ast_say_date_with_format_th(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone);
+
+static int ast_say_time_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_time_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_time_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_time_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_time_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_time_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_time_tw(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_time_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_time_ge(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_time_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_time_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+
+static int ast_say_datetime_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_datetime_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_datetime_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_datetime_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_datetime_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_datetime_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_datetime_tw(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_datetime_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_datetime_ge(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_datetime_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_datetime_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+
+static int ast_say_datetime_from_now_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_datetime_from_now_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_datetime_from_now_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+static int ast_say_datetime_from_now_ge(struct ast_channel *chan, time_t t, const char *ints, const char *lang);
+
+static int wait_file(struct ast_channel *chan, const char *ints, const char *file, const char *lang)
+{
+ int res;
+ if ((res = ast_streamfile(chan, file, lang)))
+ ast_log(LOG_WARNING, "Unable to play message %s\n", file);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ return res;
+}
+
+/*! \brief ast_say_number_full: call language-specific functions */
+/* Called from AGI */
+static int say_number_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
+{
+ if (!strcasecmp(language,"en") ) { /* English syntax */
+ return(ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd));
+ } else if (!strcasecmp(language, "cz") ) { /* Czech syntax */
+ return(ast_say_number_full_cz(chan, num, ints, language, options, audiofd, ctrlfd));
+ } else if (!strcasecmp(language, "da") ) { /* Danish syntax */
+ return(ast_say_number_full_da(chan, num, ints, language, options, audiofd, ctrlfd));
+ } else if (!strcasecmp(language, "de") ) { /* German syntax */
+ return(ast_say_number_full_de(chan, num, ints, language, options, audiofd, ctrlfd));
+ } else if (!strcasecmp(language, "en_GB") ) { /* British syntax */
+ return(ast_say_number_full_en_GB(chan, num, ints, language, audiofd, ctrlfd));
+ } else if (!strcasecmp(language, "no") ) { /* Norwegian syntax */
+ return(ast_say_number_full_no(chan, num, ints, language, options, audiofd, ctrlfd));
+ } else if (!strcasecmp(language, "es") || !strcasecmp(language, "mx")) { /* Spanish syntax */
+ return(ast_say_number_full_es(chan, num, ints, language, options, audiofd, ctrlfd));
+ } else if (!strcasecmp(language, "fr") ) { /* French syntax */
+ return(ast_say_number_full_fr(chan, num, ints, language, options, audiofd, ctrlfd));
+ } else if (!strcasecmp(language, "he") ) { /* Hebrew syntax */
+ return(ast_say_number_full_he(chan, num, ints, language, options, audiofd, ctrlfd));
+ } else if (!strcasecmp(language, "hu") ) { /* Hungarian syntax */
+ return(ast_say_number_full_hu(chan, num, ints, language, audiofd, ctrlfd));
+ } else if (!strcasecmp(language, "it") ) { /* Italian syntax */
+ return(ast_say_number_full_it(chan, num, ints, language, audiofd, ctrlfd));
+ } else if (!strcasecmp(language, "nl") ) { /* Dutch syntax */
+ return(ast_say_number_full_nl(chan, num, ints, language, audiofd, ctrlfd));
+ } else if (!strcasecmp(language, "pl") ) { /* Polish syntax */
+ return(ast_say_number_full_pl(chan, num, ints, language, options, audiofd, ctrlfd));
+ } else if (!strcasecmp(language, "pt") || !strcasecmp(language, "pt_BR")) { /* Portuguese syntax */
+ return(ast_say_number_full_pt(chan, num, ints, language, options, audiofd, ctrlfd));
+ } else if (!strcasecmp(language, "se") ) { /* Swedish syntax */
+ return(ast_say_number_full_se(chan, num, ints, language, options, audiofd, ctrlfd));
+ } else if (!strcasecmp(language, "tw") || !strcasecmp(language, "zh") ) { /* Taiwanese / Chinese syntax */
+ return(ast_say_number_full_tw(chan, num, ints, language, audiofd, ctrlfd));
+ } else if (!strcasecmp(language, "gr") ) { /* Greek syntax */
+ return(ast_say_number_full_gr(chan, num, ints, language, audiofd, ctrlfd));
+ } else if (!strcasecmp(language, "ru") ) { /* Russian syntax */
+ return(ast_say_number_full_ru(chan, num, ints, language, options, audiofd, ctrlfd));
+ } else if (!strcasecmp(language, "th") ) { /* Thai syntax */
+ return(ast_say_number_full_th(chan, num, ints, language, audiofd, ctrlfd));
+ } else if (!strcasecmp(language, "ge") ) { /* Georgian syntax */
+ return(ast_say_number_full_ge(chan, num, ints, language, options, audiofd, ctrlfd));
+ }
+
+ /* Default to english */
+ return(ast_say_number_full_en(chan, num, ints, language, audiofd, ctrlfd));
+}
+
+/*! \brief ast_say_number_full_en: English syntax */
+/* This is the default syntax, if no other syntax defined in this file is used */
+static int ast_say_number_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
+{
+ int res = 0;
+ int playh = 0;
+ char fn[256] = "";
+ if (!num)
+ return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
+
+ while (!res && (num || playh)) {
+ if (num < 0) {
+ ast_copy_string(fn, "digits/minus", sizeof(fn));
+ if ( num > INT_MIN ) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ } else if (playh) {
+ ast_copy_string(fn, "digits/hundred", sizeof(fn));
+ playh = 0;
+ } else if (num < 20) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num < 100) {
+ snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
+ num %= 10;
+ } else {
+ if (num < 1000){
+ snprintf(fn, sizeof(fn), "digits/%d", (num/100));
+ playh++;
+ num %= 100;
+ } else {
+ if (num < 1000000) { /* 1,000,000 */
+ res = ast_say_number_full_en(chan, num / 1000, ints, language, audiofd, ctrlfd);
+ if (res)
+ return res;
+ num %= 1000;
+ snprintf(fn, sizeof(fn), "digits/thousand");
+ } else {
+ if (num < 1000000000) { /* 1,000,000,000 */
+ res = ast_say_number_full_en(chan, num / 1000000, ints, language, audiofd, ctrlfd);
+ if (res)
+ return res;
+ num %= 1000000;
+ ast_copy_string(fn, "digits/million", sizeof(fn));
+ } else {
+ ast_debug(1, "Number '%d' is too big for me\n", num);
+ res = -1;
+ }
+ }
+ }
+ }
+ if (!res) {
+ if (!ast_streamfile(chan, fn, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ res = ast_waitstream(chan, ints);
+ }
+ ast_stopstream(chan);
+ }
+ }
+ return res;
+}
+
+static int exp10_int(int power)
+{
+ int x, res= 1;
+ for (x=0;x<power;x++)
+ res *= 10;
+ return res;
+}
+
+/*! \brief ast_say_number_full_cz: Czech syntax */
+/* files needed:
+ * 1m,2m - gender male
+ * 1w,2w - gender female
+ * 3,4,...,20
+ * 30,40,...,90
+ *
+ * hundereds - 100 - sto, 200 - 2ste, 300,400 3,4sta, 500,600,...,900 5,6,...9set
+ *
+ * for each number 10^(3n + 3) exist 3 files represented as:
+ * 1 tousand = jeden tisic = 1_E3
+ * 2,3,4 tousands = dva,tri,ctyri tisice = 2-3_E3
+ * 5,6,... tousands = pet,sest,... tisic = 5_E3
+ *
+ * million = _E6
+ * miliard = _E9
+ * etc...
+ *
+ * tousand, milion are gender male, so 1 and 2 is 1m 2m
+ * miliard is gender female, so 1 and 2 is 1w 2w
+ */
+static int ast_say_number_full_cz(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
+{
+ int res = 0;
+ int playh = 0;
+ char fn[256] = "";
+
+ int hundered = 0;
+ int left = 0;
+ int length = 0;
+
+ /* options - w = woman, m = man, n = neutral. Defaultl is woman */
+ if (!options)
+ options = "w";
+
+ if (!num)
+ return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
+
+ while (!res && (num || playh)) {
+ if (num < 0) {
+ ast_copy_string(fn, "digits/minus", sizeof(fn));
+ if ( num > INT_MIN ) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ } else if (num < 3 ) {
+ snprintf(fn, sizeof(fn), "digits/%d%c",num,options[0]);
+ playh = 0;
+ num = 0;
+ } else if (num < 20) {
+ snprintf(fn, sizeof(fn), "digits/%d",num);
+ playh = 0;
+ num = 0;
+ } else if (num < 100) {
+ snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
+ num %= 10;
+ } else if (num < 1000) {
+ hundered = num / 100;
+ if ( hundered == 1 ) {
+ ast_copy_string(fn, "digits/1sto", sizeof(fn));
+ } else if ( hundered == 2 ) {
+ ast_copy_string(fn, "digits/2ste", sizeof(fn));
+ } else {
+ res = ast_say_number_full_cz(chan,hundered,ints,language,options,audiofd,ctrlfd);
+ if (res)
+ return res;
+ if (hundered == 3 || hundered == 4) {
+ ast_copy_string(fn, "digits/sta", sizeof(fn));
+ } else if ( hundered > 4 ) {
+ ast_copy_string(fn, "digits/set", sizeof(fn));
+ }
+ }
+ num -= (hundered * 100);
+ } else { /* num > 1000 */
+ length = (int)log10(num)+1;
+ while ( (length % 3 ) != 1 ) {
+ length--;
+ }
+ left = num / (exp10_int(length-1));
+ if ( left == 2 ) {
+ switch (length-1) {
+ case 9: options = "w"; /* 1,000,000,000 gender female */
+ break;
+ default : options = "m"; /* others are male */
+ }
+ }
+ if ( left > 1 ) { /* we dont say "one thousand" but only thousand */
+ res = ast_say_number_full_cz(chan,left,ints,language,options,audiofd,ctrlfd);
+ if (res)
+ return res;
+ }
+ if ( left >= 5 ) { /* >= 5 have the same declesion */
+ snprintf(fn, sizeof(fn), "digits/5_E%d",length-1);
+ } else if ( left >= 2 && left <= 4 ) {
+ snprintf(fn, sizeof(fn), "digits/2-4_E%d",length-1);
+ } else { /* left == 1 */
+ snprintf(fn, sizeof(fn), "digits/1_E%d",length-1);
+ }
+ num -= left * (exp10_int(length-1));
+ }
+ if (!res) {
+ if (!ast_streamfile(chan, fn, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1)) {
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ } else {
+ res = ast_waitstream(chan, ints);
+ }
+ }
+ ast_stopstream(chan);
+ }
+ }
+ return res;
+}
+
+/*! \brief ast_say_number_full_da: Danish syntax */
+/* New files:
+ In addition to English, the following sounds are required: "1N", "millions", "and" and "1-and" through "9-and"
+ */
+static int ast_say_number_full_da(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
+{
+ int res = 0;
+ int playh = 0;
+ int playa = 0;
+ int cn = 1; /* +1 = commune; -1 = neuter */
+ char fn[256] = "";
+ if (!num)
+ return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
+
+ if (options && !strncasecmp(options, "n",1)) cn = -1;
+
+ while (!res && (num || playh || playa )) {
+ /* The grammar for Danish numbers is the same as for English except
+ * for the following:
+ * - 1 exists in both commune ("en", file "1N") and neuter ("et", file "1")
+ * - numbers 20 through 99 are said in reverse order, i.e. 21 is
+ * "one-and twenty" and 68 is "eight-and sixty".
+ * - "million" is different in singular and plural form
+ * - numbers > 1000 with zero as the third digit from last have an
+ * "and" before the last two digits, i.e. 2034 is "two thousand and
+ * four-and thirty" and 1000012 is "one million and twelve".
+ */
+ if (num < 0) {
+ ast_copy_string(fn, "digits/minus", sizeof(fn));
+ if ( num > INT_MIN ) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ } else if (playh) {
+ ast_copy_string(fn, "digits/hundred", sizeof(fn));
+ playh = 0;
+ } else if (playa) {
+ ast_copy_string(fn, "digits/and", sizeof(fn));
+ playa = 0;
+ } else if (num == 1 && cn == -1) {
+ ast_copy_string(fn, "digits/1N", sizeof(fn));
+ num = 0;
+ } else if (num < 20) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num < 100) {
+ int ones = num % 10;
+ if (ones) {
+ snprintf(fn, sizeof(fn), "digits/%d-and", ones);
+ num -= ones;
+ } else {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ }
+ } else {
+ if (num < 1000) {
+ int hundreds = num / 100;
+ if (hundreds == 1)
+ ast_copy_string(fn, "digits/1N", sizeof(fn));
+ else
+ snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
+
+ playh++;
+ num -= 100 * hundreds;
+ if (num)
+ playa++;
+
+ } else {
+ if (num < 1000000) {
+ res = ast_say_number_full_da(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
+ if (res)
+ return res;
+ num = num % 1000;
+ ast_copy_string(fn, "digits/thousand", sizeof(fn));
+ } else {
+ if (num < 1000000000) {
+ int millions = num / 1000000;
+ res = ast_say_number_full_da(chan, millions, ints, language, "c", audiofd, ctrlfd);
+ if (res)
+ return res;
+ if (millions == 1)
+ ast_copy_string(fn, "digits/million", sizeof(fn));
+ else
+ ast_copy_string(fn, "digits/millions", sizeof(fn));
+ num = num % 1000000;
+ } else {
+ ast_debug(1, "Number '%d' is too big for me\n", num);
+ res = -1;
+ }
+ }
+ if (num && num < 100)
+ playa++;
+ }
+ }
+ if (!res) {
+ if (!ast_streamfile(chan, fn, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ res = ast_waitstream(chan, ints);
+ }
+ ast_stopstream(chan);
+ }
+ }
+ return res;
+}
+
+/*! \brief ast_say_number_full_de: German syntax */
+/* New files:
+ In addition to English, the following sounds are required:
+ "millions"
+ "1-and" through "9-and"
+ "1F" (eine)
+ "1N" (ein)
+ NB "1" is recorded as 'eins'
+ */
+static int ast_say_number_full_de(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
+{
+ int res = 0, t = 0;
+ int mf = 1; /* +1 = male and neuter; -1 = female */
+ char fn[256] = "";
+ char fna[256] = "";
+ if (!num)
+ return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
+
+ if (options && (!strncasecmp(options, "f",1)))
+ mf = -1;
+
+ while (!res && num) {
+ /* The grammar for German numbers is the same as for English except
+ * for the following:
+ * - numbers 20 through 99 are said in reverse order, i.e. 21 is
+ * "one-and twenty" and 68 is "eight-and sixty".
+ * - "one" varies according to gender
+ * - 100 is 'hundert', however all other instances are 'ein hundert'
+ * - 1000 is 'tausend', however all other instances are 'ein tausend'
+ * - 1000000 is always 'eine million'
+ * - "million" is different in singular and plural form
+ */
+ if (num < 0) {
+ ast_copy_string(fn, "digits/minus", sizeof(fn));
+ if ( num > INT_MIN ) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ } else if (num < 100 && t) {
+ ast_copy_string(fn, "digits/and", sizeof(fn));
+ t = 0;
+ } else if (num == 1 && mf == -1) {
+ snprintf(fn, sizeof(fn), "digits/%dF", num);
+ num = 0;
+ } else if (num < 20) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num < 100) {
+ int ones = num % 10;
+ if (ones) {
+ snprintf(fn, sizeof(fn), "digits/%d-and", ones);
+ num -= ones;
+ } else {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ }
+ } else if (num == 100 && t == 0) {
+ ast_copy_string(fn, "digits/hundred", sizeof(fn));
+ num = 0;
+ } else if (num < 1000) {
+ int hundreds = num / 100;
+ num = num % 100;
+ if (hundreds == 1) {
+ ast_copy_string(fn, "digits/1N", sizeof(fn));
+ } else {
+ snprintf(fn, sizeof(fn), "digits/%d", hundreds);
+ }
+ ast_copy_string(fna, "digits/hundred", sizeof(fna));
+ t = 1;
+ } else if (num == 1000 && t == 0) {
+ ast_copy_string(fn, "digits/thousand", sizeof(fn));
+ num = 0;
+ } else if (num < 1000000) {
+ int thousands = num / 1000;
+ num = num % 1000;
+ t = 1;
+ if (thousands == 1) {
+ ast_copy_string(fn, "digits/1N", sizeof(fn));
+ ast_copy_string(fna, "digits/thousand", sizeof(fna));
+ } else {
+ res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
+ if (res)
+ return res;
+ ast_copy_string(fn, "digits/thousand", sizeof(fn));
+ }
+ } else if (num < 1000000000) {
+ int millions = num / 1000000;
+ num = num % 1000000;
+ t = 1;
+ if (millions == 1) {
+ ast_copy_string(fn, "digits/1F", sizeof(fn));
+ ast_copy_string(fna, "digits/million", sizeof(fna));
+ } else {
+ res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
+ if (res)
+ return res;
+ ast_copy_string(fn, "digits/millions", sizeof(fn));
+ }
+ } else if (num <= INT_MAX) {
+ int billions = num / 1000000000;
+ num = num % 1000000000;
+ t = 1;
+ if (billions == 1) {
+ ast_copy_string(fn, "digits/1F", sizeof(fn));
+ ast_copy_string(fna, "digits/milliard", sizeof(fna));
+ } else {
+ res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
+ if (res) {
+ return res;
+ }
+ ast_copy_string(fn, "digits/milliards", sizeof(fn));
+ }
+ } else {
+ ast_debug(1, "Number '%d' is too big for me\n", num);
+ res = -1;
+ }
+ if (!res) {
+ if (!ast_streamfile(chan, fn, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ res = ast_waitstream(chan, ints);
+ }
+ ast_stopstream(chan);
+ if (!res) {
+ if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ res = ast_waitstream(chan, ints);
+ }
+ ast_stopstream(chan);
+ strcpy(fna, "");
+ }
+ }
+ }
+ return res;
+}
+
+/*! \brief ast_say_number_full_en_GB: British and Norwegian syntax */
+/* New files:
+ In addition to American English, the following sounds are required: "and"
+ */
+static int ast_say_number_full_en_GB(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
+{
+ int res = 0;
+ int playh = 0;
+ int playa = 0;
+ char fn[256] = "";
+ if (!num)
+ return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
+
+ while (!res && (num || playh || playa )) {
+ if (num < 0) {
+ ast_copy_string(fn, "digits/minus", sizeof(fn));
+ if ( num > INT_MIN ) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ } else if (playh) {
+ ast_copy_string(fn, "digits/hundred", sizeof(fn));
+ playh = 0;
+ } else if (playa) {
+ ast_copy_string(fn, "digits/and", sizeof(fn));
+ playa = 0;
+ } else if (num < 20) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num < 100) {
+ snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
+ num %= 10;
+ } else if (num < 1000) {
+ int hundreds = num / 100;
+ snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
+
+ playh++;
+ num -= 100 * hundreds;
+ if (num)
+ playa++;
+ } else if (num < 1000000) {
+ res = ast_say_number_full_en_GB(chan, num / 1000, ints, language, audiofd, ctrlfd);
+ if (res)
+ return res;
+ ast_copy_string(fn, "digits/thousand", sizeof(fn));
+ num %= 1000;
+ if (num && num < 100)
+ playa++;
+ } else if (num < 1000000000) {
+ int millions = num / 1000000;
+ res = ast_say_number_full_en_GB(chan, millions, ints, language, audiofd, ctrlfd);
+ if (res)
+ return res;
+ ast_copy_string(fn, "digits/million", sizeof(fn));
+ num %= 1000000;
+ if (num && num < 100)
+ playa++;
+ } else {
+ ast_debug(1, "Number '%d' is too big for me\n", num);
+ res = -1;
+ }
+
+ if (!res) {
+ if (!ast_streamfile(chan, fn, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ res = ast_waitstream(chan, ints);
+ }
+ ast_stopstream(chan);
+ }
+ }
+ return res;
+}
+
+/*! \brief ast_say_number_full_es: Spanish syntax */
+/* New files:
+ Requires a few new audios:
+ 1F.gsm: feminine 'una'
+ 21.gsm thru 29.gsm, cien.gsm, mil.gsm, millon.gsm, millones.gsm, 100.gsm, 200.gsm, 300.gsm, 400.gsm, 500.gsm, 600.gsm, 700.gsm, 800.gsm, 900.gsm, y.gsm
+ */
+static int ast_say_number_full_es(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
+{
+ int res = 0;
+ int playa = 0;
+ int mf = 0; /* +1 = male; -1 = female */
+ char fn[256] = "";
+ if (!num)
+ return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
+
+ if (options) {
+ if (!strncasecmp(options, "f",1))
+ mf = -1;
+ else if (!strncasecmp(options, "m", 1))
+ mf = 1;
+ }
+
+ while (!res && num) {
+ if (num < 0) {
+ ast_copy_string(fn, "digits/minus", sizeof(fn));
+ if ( num > INT_MIN ) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ } else if (playa) {
+ ast_copy_string(fn, "digits/and", sizeof(fn));
+ playa = 0;
+ } else if (num == 1) {
+ if (mf < 0)
+ snprintf(fn, sizeof(fn), "digits/%dF", num);
+ else if (mf > 0)
+ snprintf(fn, sizeof(fn), "digits/%dM", num);
+ else
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num < 31) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num < 100) {
+ snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
+ num %= 10;
+ if (num)
+ playa++;
+ } else if (num == 100) {
+ ast_copy_string(fn, "digits/100", sizeof(fn));
+ num = 0;
+ } else if (num < 200) {
+ ast_copy_string(fn, "digits/100-and", sizeof(fn));
+ num -= 100;
+ } else {
+ if (num < 1000) {
+ snprintf(fn, sizeof(fn), "digits/%d", (num/100)*100);
+ num %= 100;
+ } else if (num < 2000) {
+ num %= 1000;
+ ast_copy_string(fn, "digits/thousand", sizeof(fn));
+ } else {
+ if (num < 1000000) {
+ res = ast_say_number_full_es(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
+ if (res)
+ return res;
+ num %= 1000;
+ ast_copy_string(fn, "digits/thousand", sizeof(fn));
+ } else {
+ if (num < 2147483640) {
+ if ((num/1000000) == 1) {
+ res = ast_say_number_full_es(chan, num / 1000000, ints, language, "M", audiofd, ctrlfd);
+ if (res)
+ return res;
+ ast_copy_string(fn, "digits/million", sizeof(fn));
+ } else {
+ res = ast_say_number_full_es(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
+ if (res)
+ return res;
+ ast_copy_string(fn, "digits/millions", sizeof(fn));
+ }
+ num %= 1000000;
+ } else {
+ ast_debug(1, "Number '%d' is too big for me\n", num);
+ res = -1;
+ }
+ }
+ }
+ }
+
+ if (!res) {
+ if (!ast_streamfile(chan, fn, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ res = ast_waitstream(chan, ints);
+ }
+ ast_stopstream(chan);
+
+ }
+
+ }
+ return res;
+}
+
+/*! \brief ast_say_number_full_fr: French syntax */
+/* Extra sounds needed:
+ 1F: feminin 'une'
+ et: 'and' */
+static int ast_say_number_full_fr(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
+{
+ int res = 0;
+ int playh = 0;
+ int playa = 0;
+ int mf = 1; /* +1 = male; -1 = female */
+ char fn[256] = "";
+ if (!num)
+ return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
+
+ if (options && !strncasecmp(options, "f",1))
+ mf = -1;
+
+ while (!res && (num || playh || playa)) {
+ if (num < 0) {
+ ast_copy_string(fn, "digits/minus", sizeof(fn));
+ if ( num > INT_MIN ) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ } else if (playh) {
+ ast_copy_string(fn, "digits/hundred", sizeof(fn));
+ playh = 0;
+ } else if (playa) {
+ ast_copy_string(fn, "digits/et", sizeof(fn));
+ playa = 0;
+ } else if (num == 1) {
+ if (mf < 0)
+ snprintf(fn, sizeof(fn), "digits/%dF", num);
+ else
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num < 21) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num < 70) {
+ snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
+ if ((num % 10) == 1) playa++;
+ num = num % 10;
+ } else if (num < 80) {
+ ast_copy_string(fn, "digits/60", sizeof(fn));
+ if ((num % 10) == 1) playa++;
+ num -= 60;
+ } else if (num < 100) {
+ ast_copy_string(fn, "digits/80", sizeof(fn));
+ num = num - 80;
+ } else if (num < 200) {
+ ast_copy_string(fn, "digits/hundred", sizeof(fn));
+ num = num - 100;
+ } else if (num < 1000) {
+ snprintf(fn, sizeof(fn), "digits/%d", (num/100));
+ playh++;
+ num = num % 100;
+ } else if (num < 2000) {
+ ast_copy_string(fn, "digits/thousand", sizeof(fn));
+ num = num - 1000;
+ } else if (num < 1000000) {
+ res = ast_say_number_full_fr(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
+ if (res)
+ return res;
+ ast_copy_string(fn, "digits/thousand", sizeof(fn));
+ num = num % 1000;
+ } else if (num < 1000000000) {
+ res = ast_say_number_full_fr(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
+ if (res)
+ return res;
+ ast_copy_string(fn, "digits/million", sizeof(fn));
+ num = num % 1000000;
+ } else {
+ ast_debug(1, "Number '%d' is too big for me\n", num);
+ res = -1;
+ }
+ if (!res) {
+ if (!ast_streamfile(chan, fn, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ res = ast_waitstream(chan, ints);
+ }
+ ast_stopstream(chan);
+ }
+ }
+ return res;
+}
+
+
+
+/*! \brief ast_say_number_full_he: Hebrew syntax */
+/* Extra sounds needed:
+ 1F: feminin 'one'
+ ve: 'and'
+ 1hundred: 1 hundred
+ 2hundred: 2 hundreds
+ 2thousands: 2 thousand
+ thousands: plural of 'thousand'
+ 3sF 'Smichut forms (female)
+ 4sF
+ 5sF
+ 6sF
+ 7sF
+ 8sF
+ 9sF
+ 3s 'Smichut' forms (male)
+ 4s
+ 5s
+ 6s
+ 7s
+ 9s
+ 10s
+ 11s
+ 12s
+ 13s
+ 14s
+ 15s
+ 16s
+ 17s
+ 18s
+ 19s
+
+TODO: 've' should sometimed be 'hu':
+* before 'shtaym' (2, F)
+* before 'shnaym' (2, M)
+* before 'shlosha' (3, M)
+* before 'shmone' (8, M)
+* before 'shlosim' (30)
+* before 'shmonim' (80)
+
+What about:
+'sheva' (7, F)?
+'tesha' (9, F)?
+*/
+#define SAY_NUM_BUF_SIZE 256
+static int ast_say_number_full_he(struct ast_channel *chan, int num,
+ const char *ints, const char *language, const char *options,
+ int audiofd, int ctrlfd)
+{
+ int res = 0;
+ int state = 0; /* no need to save anything */
+ int mf = 1; /* +1 = Masculin; -1 = Feminin */
+ char fn[SAY_NUM_BUF_SIZE] = "";
+ ast_verb(3, "ast_say_digits_full: started. "
+ "num: %d, options=\"%s\"\n",
+ num, options
+ );
+ if (!num)
+ return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
+
+ if (options && !strncasecmp(options, "f",1))
+ mf = -1;
+
+ /* Do we have work to do? */
+ while (!res && (num || (state>0) )) {
+ /* first type of work: play a second sound. In this loop
+ * we can only play one sound file at a time. Thus playing
+ * a second one requires repeating the loop just for the
+ * second file. The variable 'state' remembers where we were.
+ * state==0 is the normal mode and it means that we continue
+ * to check if the number num has yet anything left.
+ */
+ ast_verb(3, "ast_say_digits_full: num: %d, "
+ "state=%d, options=\"%s\", mf=%d\n",
+ num, state, options, mf
+ );
+ if (state==1) {
+ ast_copy_string(fn, "digits/hundred", sizeof(fn));
+ state = 0;
+ } else if (state==2) {
+ ast_copy_string(fn, "digits/ve", sizeof(fn));
+ state = 0;
+ } else if (state==3) {
+ ast_copy_string(fn, "digits/thousands", sizeof(fn));
+ state=0;
+ } else if (num <21) {
+ if (mf < 0)
+ snprintf(fn, sizeof(fn), "digits/%dF", num);
+ else
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num < 100) {
+ snprintf(fn, sizeof(fn), "digits/%d", (num/10)*10);
+ num = num % 10;
+ if (num>0) state=2;
+ } else if (num < 200) {
+ ast_copy_string(fn, "digits/1hundred", sizeof(fn));
+ num = num - 100;
+ state=2;
+ } else if (num < 300) {
+ ast_copy_string(fn, "digits/2hundred", sizeof(fn));
+ num = num - 200;
+ state=2;
+ } else if (num < 1000) {
+ snprintf(fn, sizeof(fn), "digits/%d", (num/100));
+ state=1;
+ num = num % 100;
+ } else if (num < 2000) {
+ ast_copy_string(fn, "digits/thousand", sizeof(fn));
+ num = num - 1000;
+ } else if (num < 3000) {
+ ast_copy_string(fn, "digits/2thousand", sizeof(fn));
+ num = num - 2000;
+ if (num>0) state=2;
+ } else if (num < 20000) {
+ snprintf(fn, sizeof(fn), "digits/%ds",(num/1000));
+ num = num % 1000;
+ state=3;
+ } else if (num < 1000000) {
+ res = ast_say_number_full_he(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
+ if (res)
+ return res;
+ ast_copy_string(fn, "digits/thousand", sizeof(fn));
+ num = num % 1000;
+ } else if (num < 1000000000) {
+ res = ast_say_number_full_he(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
+ if (res)
+ return res;
+ ast_copy_string(fn, "digits/million", sizeof(fn));
+ num = num % 1000000;
+ } else {
+ ast_debug(1, "Number '%d' is too big for me\n", num);
+ res = -1;
+ }
+ if (!res) {
+ if (!ast_streamfile(chan, fn, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ res = ast_waitstream(chan, ints);
+ }
+ ast_stopstream(chan);
+ }
+ }
+ return res;
+}
+
+/*! \brief ast_say_number_full_hu: Hungarian syntax */
+/* Extra sounds need:
+ 10en: "tizen"
+ 20on: "huszon"
+*/
+static int ast_say_number_full_hu(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
+{
+ int res = 0;
+ int playh = 0;
+ char fn[256] = "";
+ if (!num)
+ return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
+
+ /*
+ Hungarian support
+ like english, except numbers up to 29 are from 2 words.
+ 10 and first word of 1[1-9] and 20 and first word of 2[1-9] are different.
+ */
+
+ while(!res && (num || playh)) {
+ if (num < 0) {
+ ast_copy_string(fn, "digits/minus", sizeof(fn));
+ if ( num > INT_MIN ) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ } else if (playh) {
+ ast_copy_string(fn, "digits/hundred", sizeof(fn));
+ playh = 0;
+ } else if (num < 11 || num == 20) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num < 20) {
+ ast_copy_string(fn, "digits/10en", sizeof(fn));
+ num -= 10;
+ } else if (num < 30) {
+ ast_copy_string(fn, "digits/20on", sizeof(fn));
+ num -= 20;
+ } else if (num < 100) {
+ snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
+ num %= 10;
+ } else {
+ if (num < 1000){
+ snprintf(fn, sizeof(fn), "digits/%d", (num/100));
+ playh++;
+ num %= 100;
+ } else {
+ if (num < 1000000) { /* 1,000,000 */
+ res = ast_say_number_full_hu(chan, num / 1000, ints, language, audiofd, ctrlfd);
+ if (res)
+ return res;
+ num %= 1000;
+ ast_copy_string(fn, "digits/thousand", sizeof(fn));
+ } else {
+ if (num < 1000000000) { /* 1,000,000,000 */
+ res = ast_say_number_full_hu(chan, num / 1000000, ints, language, audiofd, ctrlfd);
+ if (res)
+ return res;
+ num %= 1000000;
+ ast_copy_string(fn, "digits/million", sizeof(fn));
+ } else {
+ ast_debug(1, "Number '%d' is too big for me\n", num);
+ res = -1;
+ }
+ }
+ }
+ }
+ if (!res) {
+ if(!ast_streamfile(chan, fn, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ res = ast_waitstream(chan, ints);
+ }
+ ast_stopstream(chan);
+ }
+ }
+ return res;
+}
+
+/*! \brief ast_say_number_full_it: Italian */
+static int ast_say_number_full_it(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
+{
+ int res = 0;
+ int playh = 0;
+ int tempnum = 0;
+ char fn[256] = "";
+
+ if (!num)
+ return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
+
+ /*
+ Italian support
+
+ Like english, numbers up to 20 are a single 'word', and others
+ compound, but with exceptions.
+ For example 21 is not twenty-one, but there is a single word in 'it'.
+ Idem for 28 (ie when a the 2nd part of a compund number
+ starts with a vowel)
+
+ There are exceptions also for hundred, thousand and million.
+ In english 100 = one hundred, 200 is two hundred.
+ In italian 100 = cento , like to say hundred (without one),
+ 200 and more are like english.
+
+ Same applies for thousand:
+ 1000 is one thousand in en, 2000 is two thousand.
+ In it we have 1000 = mille , 2000 = 2 mila
+
+ For million(s) we use the plural, if more than one
+ Also, one million is abbreviated in it, like on-million,
+ or 'un milione', not 'uno milione'.
+ So the right file is provided.
+ */
+
+ while (!res && (num || playh)) {
+ if (num < 0) {
+ ast_copy_string(fn, "digits/minus", sizeof(fn));
+ if ( num > INT_MIN ) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ } else if (playh) {
+ ast_copy_string(fn, "digits/hundred", sizeof(fn));
+ playh = 0;
+ } else if (num < 20) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num == 21) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num == 28) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num == 31) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num == 38) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num == 41) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num == 48) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num == 51) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num == 58) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num == 61) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num == 68) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num == 71) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num == 78) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num == 81) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num == 88) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num == 91) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num == 98) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num < 100) {
+ snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
+ num %= 10;
+ } else {
+ if (num < 1000) {
+ if ((num / 100) > 1) {
+ snprintf(fn, sizeof(fn), "digits/%d", (num/100));
+ playh++;
+ } else {
+ ast_copy_string(fn, "digits/hundred", sizeof(fn));
+ }
+ num %= 100;
+ } else {
+ if (num < 1000000) { /* 1,000,000 */
+ if ((num/1000) > 1)
+ res = ast_say_number_full_it(chan, num / 1000, ints, language, audiofd, ctrlfd);
+ if (res)
+ return res;
+ tempnum = num;
+ num %= 1000;
+ if ((tempnum / 1000) < 2)
+ ast_copy_string(fn, "digits/thousand", sizeof(fn));
+ else /* for 1000 it says mille, for >1000 (eg 2000) says mila */
+ ast_copy_string(fn, "digits/thousands", sizeof(fn));
+ } else {
+ if (num < 1000000000) { /* 1,000,000,000 */
+ if ((num / 1000000) > 1)
+ res = ast_say_number_full_it(chan, num / 1000000, ints, language, audiofd, ctrlfd);
+ if (res)
+ return res;
+ tempnum = num;
+ num %= 1000000;
+ if ((tempnum / 1000000) < 2)
+ ast_copy_string(fn, "digits/million", sizeof(fn));
+ else
+ ast_copy_string(fn, "digits/millions", sizeof(fn));
+ } else {
+ ast_debug(1, "Number '%d' is too big for me\n", num);
+ res = -1;
+ }
+ }
+ }
+ }
+ if (!res) {
+ if (!ast_streamfile(chan, fn, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ res = ast_waitstream(chan, ints);
+ }
+ ast_stopstream(chan);
+ }
+ }
+ return res;
+}
+
+/*! \brief ast_say_number_full_nl: dutch syntax */
+/* New files: digits/nl-en
+ */
+static int ast_say_number_full_nl(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
+{
+ int res = 0;
+ int playh = 0;
+ int units = 0;
+ char fn[256] = "";
+ if (!num)
+ return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
+ while (!res && (num || playh )) {
+ if (num < 0) {
+ ast_copy_string(fn, "digits/minus", sizeof(fn));
+ if ( num > INT_MIN ) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ } else if (playh) {
+ ast_copy_string(fn, "digits/hundred", sizeof(fn));
+ playh = 0;
+ } else if (num < 20) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num < 100) {
+ units = num % 10;
+ if (units > 0) {
+ res = ast_say_number_full_nl(chan, units, ints, language, audiofd, ctrlfd);
+ if (res)
+ return res;
+ num = num - units;
+ ast_copy_string(fn, "digits/nl-en", sizeof(fn));
+ } else {
+ snprintf(fn, sizeof(fn), "digits/%d", num - units);
+ num = 0;
+ }
+ } else if (num < 200) {
+ /* hundred, not one-hundred */
+ ast_copy_string(fn, "digits/hundred", sizeof(fn));
+ num %= 100;
+ } else if (num < 1000) {
+ snprintf(fn, sizeof(fn), "digits/%d", num / 100);
+ playh++;
+ num %= 100;
+ } else {
+ if (num < 1100) {
+ /* thousand, not one-thousand */
+ num %= 1000;
+ ast_copy_string(fn, "digits/thousand", sizeof(fn));
+ } else if (num < 10000) { /* 1,100 to 9,9999 */
+ res = ast_say_number_full_nl(chan, num / 100, ints, language, audiofd, ctrlfd);
+ if (res)
+ return res;
+ num %= 100;
+ ast_copy_string(fn, "digits/hundred", sizeof(fn));
+ } else {
+ if (num < 1000000) { /* 1,000,000 */
+ res = ast_say_number_full_nl(chan, num / 1000, ints, language, audiofd, ctrlfd);
+ if (res)
+ return res;
+ num %= 1000;
+ ast_copy_string(fn, "digits/thousand", sizeof(fn));
+ } else {
+ if (num < 1000000000) { /* 1,000,000,000 */
+ res = ast_say_number_full_nl(chan, num / 1000000, ints, language, audiofd, ctrlfd);
+ if (res)
+ return res;
+ num %= 1000000;
+ ast_copy_string(fn, "digits/million", sizeof(fn));
+ } else {
+ ast_debug(1, "Number '%d' is too big for me\n", num);
+ res = -1;
+ }
+ }
+ }
+ }
+
+ if (!res) {
+ if (!ast_streamfile(chan, fn, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ res = ast_waitstream(chan, ints);
+ }
+ ast_stopstream(chan);
+ }
+ }
+ return res;
+}
+
+/*! \brief ast_say_number_full_no: Norwegian syntax */
+/* New files:
+ In addition to American English, the following sounds are required: "and", "1N"
+ */
+static int ast_say_number_full_no(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
+{
+ int res = 0;
+ int playh = 0;
+ int playa = 0;
+ int cn = 1; /* +1 = commune; -1 = neuter */
+ char fn[256] = "";
+
+ if (!num)
+ return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
+
+ if (options && !strncasecmp(options, "n",1)) cn = -1;
+
+ while (!res && (num || playh || playa )) {
+ /* The grammar for Norwegian numbers is the same as for English except
+ * for the following:
+ * - 1 exists in both commune ("en", file "1") and neuter ("ett", file "1N")
+ * "and" before the last two digits, i.e. 2034 is "two thousand and
+ * thirty-four" and 1000012 is "one million and twelve".
+ */
+ if (num < 0) {
+ ast_copy_string(fn, "digits/minus", sizeof(fn));
+ if ( num > INT_MIN ) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ } else if (playh) {
+ ast_copy_string(fn, "digits/hundred", sizeof(fn));
+ playh = 0;
+ } else if (playa) {
+ ast_copy_string(fn, "digits/and", sizeof(fn));
+ playa = 0;
+ } else if (num == 1 && cn == -1) {
+ ast_copy_string(fn, "digits/1N", sizeof(fn));
+ num = 0;
+ } else if (num < 20) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num < 100) {
+ snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
+ num %= 10;
+ } else if (num < 1000) {
+ int hundreds = num / 100;
+ if (hundreds == 1)
+ ast_copy_string(fn, "digits/1N", sizeof(fn));
+ else
+ snprintf(fn, sizeof(fn), "digits/%d", (num / 100));
+
+ playh++;
+ num -= 100 * hundreds;
+ if (num)
+ playa++;
+ } else if (num < 1000000) {
+ res = ast_say_number_full_no(chan, num / 1000, ints, language, "n", audiofd, ctrlfd);
+ if (res)
+ return res;
+ ast_copy_string(fn, "digits/thousand", sizeof(fn));
+ num %= 1000;
+ if (num && num < 100)
+ playa++;
+ } else if (num < 1000000000) {
+ int millions = num / 1000000;
+ res = ast_say_number_full_no(chan, millions, ints, language, "c", audiofd, ctrlfd);
+ if (res)
+ return res;
+ ast_copy_string(fn, "digits/million", sizeof(fn));
+ num %= 1000000;
+ if (num && num < 100)
+ playa++;
+ } else {
+ ast_debug(1, "Number '%d' is too big for me\n", num);
+ res = -1;
+ }
+
+ if (!res) {
+ if (!ast_streamfile(chan, fn, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ res = ast_waitstream(chan, ints);
+ }
+ ast_stopstream(chan);
+ }
+ }
+ return res;
+}
+
+typedef struct {
+ char *separator_dziesiatek;
+ char *cyfry[10];
+ char *cyfry2[10];
+ char *setki[10];
+ char *dziesiatki[10];
+ char *nastki[10];
+ char *rzedy[3][3];
+} odmiana;
+
+static char *pl_rzad_na_tekst(odmiana *odm, int i, int rzad)
+{
+ if (rzad==0)
+ return "";
+
+ if (i==1)
+ return odm->rzedy[rzad - 1][0];
+ if ((i > 21 || i < 11) && i%10 > 1 && i%10 < 5)
+ return odm->rzedy[rzad - 1][1];
+ else
+ return odm->rzedy[rzad - 1][2];
+}
+
+static char* pl_append(char* buffer, char* str)
+{
+ strcpy(buffer, str);
+ buffer += strlen(str);
+ return buffer;
+}
+
+static void pl_odtworz_plik(struct ast_channel *chan, const char *language, int audiofd, int ctrlfd, const char *ints, char *fn)
+{
+ char file_name[255] = "digits/";
+ strcat(file_name, fn);
+ ast_debug(1, "Trying to play: %s\n", file_name);
+ if (!ast_streamfile(chan, file_name, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ ast_waitstream(chan, ints);
+ }
+ ast_stopstream(chan);
+}
+
+static void powiedz(struct ast_channel *chan, const char *language, int audiofd, int ctrlfd, const char *ints, odmiana *odm, int rzad, int i)
+{
+ /* Initialise variables to allow compilation on Debian-stable, etc */
+ int m1000E6 = 0;
+ int i1000E6 = 0;
+ int m1000E3 = 0;
+ int i1000E3 = 0;
+ int m1000 = 0;
+ int i1000 = 0;
+ int m100 = 0;
+ int i100 = 0;
+
+ if (i == 0 && rzad > 0) {
+ return;
+ }
+ if (i == 0) {
+ pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry[0]);
+ return;
+ }
+
+ m1000E6 = i % 1000000000;
+ i1000E6 = i / 1000000000;
+
+ powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+3, i1000E6);
+
+ m1000E3 = m1000E6 % 1000000;
+ i1000E3 = m1000E6 / 1000000;
+
+ powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+2, i1000E3);
+
+ m1000 = m1000E3 % 1000;
+ i1000 = m1000E3 / 1000;
+
+ powiedz(chan, language, audiofd, ctrlfd, ints, odm, rzad+1, i1000);
+
+ m100 = m1000 % 100;
+ i100 = m1000 / 100;
+
+ if (i100>0)
+ pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->setki[i100]);
+
+ if ( m100 > 0 && m100 <=9 ) {
+ if (m1000>0)
+ pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry2[m100]);
+ else
+ pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry[m100]);
+ } else if (m100 % 10 == 0) {
+ pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->dziesiatki[m100 / 10]);
+ } else if (m100 <= 19 ) {
+ pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->nastki[m100 % 10]);
+ } else if (m100 != 0) {
+ if (odm->separator_dziesiatek[0]==' ') {
+ pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->dziesiatki[m100 / 10]);
+ pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, odm->cyfry2[m100 % 10]);
+ } else {
+ char buf[10];
+ char *b = buf;
+ b = pl_append(b, odm->dziesiatki[m100 / 10]);
+ b = pl_append(b, odm->separator_dziesiatek);
+ b = pl_append(b, odm->cyfry2[m100 % 10]);
+ pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, buf);
+ }
+ }
+
+ if (rzad > 0) {
+ pl_odtworz_plik(chan, language, audiofd, ctrlfd, ints, pl_rzad_na_tekst(odm, i, rzad));
+ }
+}
+
+/* ast_say_number_full_pl: Polish syntax */
+static int ast_say_number_full_pl(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
+/*
+Sounds needed:
+0 zero
+1 jeden
+10 dziesiec
+100 sto
+1000 tysiac
+1000000 milion
+1000000000 miliard
+1000000000.2 miliardy
+1000000000.5 miliardow
+1000000.2 miliony
+1000000.5 milionow
+1000.2 tysiace
+1000.5 tysiecy
+100m stu
+10m dziesieciu
+11 jedenascie
+11m jedenastu
+12 dwanascie
+12m dwunastu
+13 trzynascie
+13m trzynastu
+14 czternascie
+14m czternastu
+15 pietnascie
+15m pietnastu
+16 szesnascie
+16m szesnastu
+17 siedemnascie
+17m siedemnastu
+18 osiemnascie
+18m osiemnastu
+19 dziewietnascie
+19m dziewietnastu
+1z jedna
+2 dwa
+20 dwadziescia
+200 dwiescie
+200m dwustu
+20m dwudziestu
+2-1m dwaj
+2-2m dwoch
+2z dwie
+3 trzy
+30 trzydziesci
+300 trzysta
+300m trzystu
+30m trzydziestu
+3-1m trzej
+3-2m trzech
+4 cztery
+40 czterdziesci
+400 czterysta
+400m czterystu
+40m czterdziestu
+4-1m czterej
+4-2m czterech
+5 piec
+50 piecdziesiat
+500 piecset
+500m pieciuset
+50m piedziesieciu
+5m pieciu
+6 szesc
+60 szescdziesiat
+600 szescset
+600m szesciuset
+60m szescdziesieciu
+6m szesciu
+7 siedem
+70 siedemdziesiat
+700 siedemset
+700m siedmiuset
+70m siedemdziesieciu
+7m siedmiu
+8 osiem
+80 osiemdziesiat
+800 osiemset
+800m osmiuset
+80m osiemdziesieciu
+8m osmiu
+9 dziewiec
+90 dziewiecdziesiat
+900 dziewiecset
+900m dziewieciuset
+90m dziewiedziesieciu
+9m dziewieciu
+and combinations of eg.: 20_1, 30m_3m, etc...
+
+*/
+{
+ char *zenski_cyfry[] = {"0","1z", "2z", "3", "4", "5", "6", "7", "8", "9"};
+
+ char *zenski_cyfry2[] = {"0","1", "2z", "3", "4", "5", "6", "7", "8", "9"};
+
+ char *meski_cyfry[] = {"0","1", "2-1m", "3-1m", "4-1m", "5m", /*"2-1mdwaj"*/ "6m", "7m", "8m", "9m"};
+
+ char *meski_cyfry2[] = {"0","1", "2-2m", "3-2m", "4-2m", "5m", "6m", "7m", "8m", "9m"};
+
+ char *meski_setki[] = {"", "100m", "200m", "300m", "400m", "500m", "600m", "700m", "800m", "900m"};
+
+ char *meski_dziesiatki[] = {"", "10m", "20m", "30m", "40m", "50m", "60m", "70m", "80m", "90m"};
+
+ char *meski_nastki[] = {"", "11m", "12m", "13m", "14m", "15m", "16m", "17m", "18m", "19m"};
+
+ char *nijaki_cyfry[] = {"0","1", "2", "3", "4", "5", "6", "7", "8", "9"};
+
+ char *nijaki_cyfry2[] = {"0","1", "2", "3", "4", "5", "6", "7", "8", "9"};
+
+ char *nijaki_setki[] = {"", "100", "200", "300", "400", "500", "600", "700", "800", "900"};
+
+ char *nijaki_dziesiatki[] = {"", "10", "20", "30", "40", "50", "60", "70", "80", "90"};
+
+ char *nijaki_nastki[] = {"", "11", "12", "13", "14", "15", "16", "17", "18", "19"};
+
+ char *rzedy[][3] = { {"1000", "1000.2", "1000.5"}, {"1000000", "1000000.2", "1000000.5"}, {"1000000000", "1000000000.2", "1000000000.5"}};
+
+ /* Initialise variables to allow compilation on Debian-stable, etc */
+ odmiana *o;
+
+ static odmiana *odmiana_nieosobowa = NULL;
+ static odmiana *odmiana_meska = NULL;
+ static odmiana *odmiana_zenska = NULL;
+
+ if (odmiana_nieosobowa == NULL) {
+ odmiana_nieosobowa = ast_malloc(sizeof(*odmiana_nieosobowa));
+
+ odmiana_nieosobowa->separator_dziesiatek = " ";
+
+ memcpy(odmiana_nieosobowa->cyfry, nijaki_cyfry, sizeof(odmiana_nieosobowa->cyfry));
+ memcpy(odmiana_nieosobowa->cyfry2, nijaki_cyfry2, sizeof(odmiana_nieosobowa->cyfry));
+ memcpy(odmiana_nieosobowa->setki, nijaki_setki, sizeof(odmiana_nieosobowa->setki));
+ memcpy(odmiana_nieosobowa->dziesiatki, nijaki_dziesiatki, sizeof(odmiana_nieosobowa->dziesiatki));
+ memcpy(odmiana_nieosobowa->nastki, nijaki_nastki, sizeof(odmiana_nieosobowa->nastki));
+ memcpy(odmiana_nieosobowa->rzedy, rzedy, sizeof(odmiana_nieosobowa->rzedy));
+ }
+
+ if (odmiana_zenska == NULL) {
+ odmiana_zenska = ast_malloc(sizeof(*odmiana_zenska));
+
+ odmiana_zenska->separator_dziesiatek = " ";
+
+ memcpy(odmiana_zenska->cyfry, zenski_cyfry, sizeof(odmiana_zenska->cyfry));
+ memcpy(odmiana_zenska->cyfry2, zenski_cyfry2, sizeof(odmiana_zenska->cyfry));
+ memcpy(odmiana_zenska->setki, nijaki_setki, sizeof(odmiana_zenska->setki));
+ memcpy(odmiana_zenska->dziesiatki, nijaki_dziesiatki, sizeof(odmiana_zenska->dziesiatki));
+ memcpy(odmiana_zenska->nastki, nijaki_nastki, sizeof(odmiana_zenska->nastki));
+ memcpy(odmiana_zenska->rzedy, rzedy, sizeof(odmiana_zenska->rzedy));
+ }
+
+ if (odmiana_meska == NULL) {
+ odmiana_meska = ast_malloc(sizeof(*odmiana_meska));
+
+ odmiana_meska->separator_dziesiatek = " ";
+
+ memcpy(odmiana_meska->cyfry, meski_cyfry, sizeof(odmiana_meska->cyfry));
+ memcpy(odmiana_meska->cyfry2, meski_cyfry2, sizeof(odmiana_meska->cyfry));
+ memcpy(odmiana_meska->setki, meski_setki, sizeof(odmiana_meska->setki));
+ memcpy(odmiana_meska->dziesiatki, meski_dziesiatki, sizeof(odmiana_meska->dziesiatki));
+ memcpy(odmiana_meska->nastki, meski_nastki, sizeof(odmiana_meska->nastki));
+ memcpy(odmiana_meska->rzedy, rzedy, sizeof(odmiana_meska->rzedy));
+ }
+
+ if (options) {
+ if (strncasecmp(options, "f", 1) == 0)
+ o = odmiana_zenska;
+ else if (strncasecmp(options, "m", 1) == 0)
+ o = odmiana_meska;
+ else
+ o = odmiana_nieosobowa;
+ } else
+ o = odmiana_nieosobowa;
+
+ powiedz(chan, language, audiofd, ctrlfd, ints, o, 0, num);
+ return 0;
+}
+
+/* ast_say_number_full_pt: Portuguese syntax */
+/* Extra sounds needed: */
+/* For feminin all sound files end with F */
+/* 100E for 100+ something */
+/* 1000000S for plural */
+/* pt-e for 'and' */
+static int ast_say_number_full_pt(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
+{
+ int res = 0;
+ int playh = 0;
+ int mf = 1; /* +1 = male; -1 = female */
+ char fn[256] = "";
+
+ if (!num)
+ return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
+
+ if (options && !strncasecmp(options, "f",1))
+ mf = -1;
+
+ while (!res && num ) {
+ if (num < 0) {
+ ast_copy_string(fn, "digits/minus", sizeof(fn));
+ if ( num > INT_MIN ) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ } else if (num < 20) {
+ if ((num == 1 || num == 2) && (mf < 0))
+ snprintf(fn, sizeof(fn), "digits/%dF", num);
+ else
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num < 100) {
+ snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
+ if (num % 10)
+ playh = 1;
+ num = num % 10;
+ } else if (num < 1000) {
+ if (num == 100)
+ ast_copy_string(fn, "digits/100", sizeof(fn));
+ else if (num < 200)
+ ast_copy_string(fn, "digits/100E", sizeof(fn));
+ else {
+ if (mf < 0 && num > 199)
+ snprintf(fn, sizeof(fn), "digits/%dF", (num / 100) * 100);
+ else
+ snprintf(fn, sizeof(fn), "digits/%d", (num / 100) * 100);
+ if (num % 100)
+ playh = 1;
+ }
+ num = num % 100;
+ } else if (num < 1000000) {
+ if (num > 1999) {
+ res = ast_say_number_full_pt(chan, (num / 1000) * mf, ints, language, options, audiofd, ctrlfd);
+ if (res)
+ return res;
+ }
+ ast_copy_string(fn, "digits/1000", sizeof(fn));
+ if ((num % 1000) && ((num % 1000) < 100 || !(num % 100)))
+ playh = 1;
+ num = num % 1000;
+ } else if (num < 1000000000) {
+ res = ast_say_number_full_pt(chan, (num / 1000000), ints, language, options, audiofd, ctrlfd );
+ if (res)
+ return res;
+ if (num < 2000000)
+ ast_copy_string(fn, "digits/1000000", sizeof(fn));
+ else
+ ast_copy_string(fn, "digits/1000000S", sizeof(fn));
+
+ if ((num % 1000000) &&
+ /* no thousands */
+ ((!((num / 1000) % 1000) && ((num % 1000) < 100 || !(num % 100))) ||
+ /* no hundreds and below */
+ (!(num % 1000) && (((num / 1000) % 1000) < 100 || !((num / 1000) % 100))) ) )
+ playh = 1;
+ num = num % 1000000;
+ } else {
+ /* number is too big */
+ ast_log(LOG_WARNING, "Number '%d' is too big to say.", num);
+ res = -1;
+ }
+ if (!res) {
+ if (!ast_streamfile(chan, fn, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ res = ast_waitstream(chan, ints);
+ }
+ ast_stopstream(chan);
+ }
+ if (!res && playh) {
+ res = wait_file(chan, ints, "digits/pt-e", language);
+ ast_stopstream(chan);
+ playh = 0;
+ }
+ }
+ return res;
+}
+
+/*! \brief ast_say_number_full_se: Swedish syntax */
+static int ast_say_number_full_se(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
+{
+ int res = 0;
+ int playh = 0;
+ char fn[256] = "";
+ int cn = 1; /* +1 = commune; -1 = neuter */
+ if (!num)
+ return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
+ if (options && !strncasecmp(options, "n",1)) cn = -1;
+
+ while (!res && (num || playh)) {
+ if (num < 0) {
+ ast_copy_string(fn, "digits/minus", sizeof(fn));
+ if ( num > INT_MIN ) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ } else if (playh) {
+ ast_copy_string(fn, "digits/hundred", sizeof(fn));
+ playh = 0;
+ } else if (num < 20) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num < 100) {
+ snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
+ num %= 10;
+ } else if (num == 1 && cn == -1) { /* En eller ett? */
+ ast_copy_string(fn, "digits/1N", sizeof(fn));
+ num = 0;
+ } else {
+ if (num < 1000){
+ snprintf(fn, sizeof(fn), "digits/%d", (num/100));
+ playh++;
+ num %= 100;
+ } else {
+ if (num < 1000000) { /* 1,000,000 */
+ res = ast_say_number_full_se(chan, num / 1000, ints, language, options, audiofd, ctrlfd);
+ if (res) {
+ return res;
+ }
+ num %= 1000;
+ ast_copy_string(fn, "digits/thousand", sizeof(fn));
+ } else {
+ if (num < 1000000000) { /* 1,000,000,000 */
+ res = ast_say_number_full_se(chan, num / 1000000, ints, language, options, audiofd, ctrlfd);
+ if (res) {
+ return res;
+ }
+ num %= 1000000;
+ ast_copy_string(fn, "digits/million", sizeof(fn));
+ } else {
+ ast_debug(1, "Number '%d' is too big for me\n", num);
+ res = -1;
+ }
+ }
+ }
+ }
+ if (!res) {
+ if (!ast_streamfile(chan, fn, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ res = ast_waitstream(chan, ints);
+ ast_stopstream(chan);
+ }
+ }
+ }
+ return res;
+}
+
+/*! \brief ast_say_number_full_tw: Taiwanese / Chinese syntax */
+static int ast_say_number_full_tw(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
+{
+ int res = 0;
+ int playh = 0;
+ char fn[256] = "";
+ if (!num)
+ return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
+
+ while (!res && (num || playh)) {
+ if (num < 0) {
+ ast_copy_string(fn, "digits/minus", sizeof(fn));
+ if ( num > INT_MIN ) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ } else if (playh) {
+ ast_copy_string(fn, "digits/hundred", sizeof(fn));
+ playh = 0;
+ } else if (num < 10) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num < 100) {
+ snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
+ num %= 10;
+ } else {
+ if (num < 1000){
+ snprintf(fn, sizeof(fn), "digits/%d", (num/100));
+ playh++;
+ num %= 100;
+ } else {
+ if (num < 1000000) { /* 1,000,000 */
+ res = ast_say_number_full_tw(chan, num / 1000, ints, language, audiofd, ctrlfd);
+ if (res)
+ return res;
+ num %= 1000;
+ ast_copy_string(fn, "digits/thousand", sizeof(fn));
+ } else {
+ if (num < 1000000000) { /* 1,000,000,000 */
+ res = ast_say_number_full_tw(chan, num / 1000000, ints, language, audiofd, ctrlfd);
+ if (res)
+ return res;
+ num %= 1000000;
+ ast_copy_string(fn, "digits/million", sizeof(fn));
+ } else {
+ ast_debug(1, "Number '%d' is too big for me\n", num);
+ res = -1;
+ }
+ }
+ }
+ }
+ if (!res) {
+ if (!ast_streamfile(chan, fn, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ res = ast_waitstream(chan, ints);
+ }
+ ast_stopstream(chan);
+ }
+ }
+ return res;
+}
+
+
+/*! \brief determine last digits for thousands/millions (ru) */
+static int get_lastdigits_ru(int num) {
+ if (num < 20) {
+ return num;
+ } else if (num < 100) {
+ return get_lastdigits_ru(num % 10);
+ } else if (num < 1000) {
+ return get_lastdigits_ru(num % 100);
+ }
+ return 0; /* number too big */
+}
+
+
+/*! \brief ast_say_number_full_ru: Russian syntax */
+/*! \brief additional files:
+ n00.gsm (one hundred, two hundred, ...)
+ thousand.gsm
+ million.gsm
+ thousands-i.gsm (tisyachi)
+ million-a.gsm (milliona)
+ thousands.gsm
+ millions.gsm
+ 1f.gsm (odna)
+ 2f.gsm (dve)
+
+ where 'n' from 1 to 9
+*/
+static int ast_say_number_full_ru(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
+{
+ int res = 0;
+ int lastdigits = 0;
+ char fn[256] = "";
+ if (!num)
+ return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
+
+ while (!res && (num)) {
+ if (num < 0) {
+ ast_copy_string(fn, "digits/minus", sizeof(fn));
+ if ( num > INT_MIN ) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ } else if (num < 20) {
+ if (options && strlen(options) == 1 && num < 3) {
+ snprintf(fn, sizeof(fn), "digits/%d%s", num, options);
+ } else {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ }
+ num = 0;
+ } else if (num < 100) {
+ snprintf(fn, sizeof(fn), "digits/%d", num - (num % 10));
+ num %= 10;
+ } else if (num < 1000){
+ snprintf(fn, sizeof(fn), "digits/%d", num - (num % 100));
+ num %= 100;
+ } else if (num < 1000000) { /* 1,000,000 */
+ lastdigits = get_lastdigits_ru(num / 1000);
+ /* say thousands */
+ if (lastdigits < 3) {
+ res = ast_say_number_full_ru(chan, num / 1000, ints, language, "f", audiofd, ctrlfd);
+ } else {
+ res = ast_say_number_full_ru(chan, num / 1000, ints, language, NULL, audiofd, ctrlfd);
+ }
+ if (res)
+ return res;
+ if (lastdigits == 1) {
+ ast_copy_string(fn, "digits/thousand", sizeof(fn));
+ } else if (lastdigits > 1 && lastdigits < 5) {
+ ast_copy_string(fn, "digits/thousands-i", sizeof(fn));
+ } else {
+ ast_copy_string(fn, "digits/thousands", sizeof(fn));
+ }
+ num %= 1000;
+ } else if (num < 1000000000) { /* 1,000,000,000 */
+ lastdigits = get_lastdigits_ru(num / 1000000);
+ /* say millions */
+ res = ast_say_number_full_ru(chan, num / 1000000, ints, language, NULL, audiofd, ctrlfd);
+ if (res)
+ return res;
+ if (lastdigits == 1) {
+ ast_copy_string(fn, "digits/million", sizeof(fn));
+ } else if (lastdigits > 1 && lastdigits < 5) {
+ ast_copy_string(fn, "digits/million-a", sizeof(fn));
+ } else {
+ ast_copy_string(fn, "digits/millions", sizeof(fn));
+ }
+ num %= 1000000;
+ } else {
+ ast_debug(1, "Number '%d' is too big for me\n", num);
+ res = -1;
+ }
+ if (!res) {
+ if (!ast_streamfile(chan, fn, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ res = ast_waitstream(chan, ints);
+ }
+ ast_stopstream(chan);
+ }
+ }
+ return res;
+}
+
+static int ast_say_number_full_th(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
+{
+ int res = 0;
+ int playh = 0;
+ char fn[256] = "";
+ if (!num)
+ return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
+
+ while(!res && (num || playh)) {
+ if (num < 0) {
+ ast_copy_string(fn, "digits/lop", sizeof(fn));
+ if ( num > INT_MIN ) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ } else if (playh) {
+ ast_copy_string(fn, "digits/roi", sizeof(fn));
+ playh = 0;
+ } else if (num < 100) {
+ if ((num <= 20) || ((num % 10) == 1)) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else {
+ snprintf(fn, sizeof(fn), "digits/%d", (num / 10) * 10);
+ num %= 10;
+ }
+ } else if (num < 1000) {
+ snprintf(fn, sizeof(fn), "digits/%d", (num/100));
+ playh++;
+ num %= 100;
+ } else if (num < 10000) { /* 10,000 */
+ res = ast_say_number_full_th(chan, num / 1000, ints, language, audiofd, ctrlfd);
+ if (res)
+ return res;
+ num %= 1000;
+ ast_copy_string(fn, "digits/pan", sizeof(fn));
+ } else if (num < 100000) { /* 100,000 */
+ res = ast_say_number_full_th(chan, num / 10000, ints, language, audiofd, ctrlfd);
+ if (res)
+ return res;
+ num %= 10000;
+ ast_copy_string(fn, "digits/muan", sizeof(fn));
+ } else if (num < 1000000) { /* 1,000,000 */
+ res = ast_say_number_full_th(chan, num / 100000, ints, language, audiofd, ctrlfd);
+ if (res)
+ return res;
+ num %= 100000;
+ ast_copy_string(fn, "digits/san", sizeof(fn));
+ } else {
+ res = ast_say_number_full_th(chan, num / 1000000, ints, language, audiofd, ctrlfd);
+ if (res)
+ return res;
+ num %= 1000000;
+ ast_copy_string(fn, "digits/larn", sizeof(fn));
+ }
+ if (!res) {
+ if(!ast_streamfile(chan, fn, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ res = ast_waitstream(chan, ints);
+ }
+ ast_stopstream(chan);
+ }
+ }
+ return res;
+}
+
+/*! \brief ast_say_enumeration_full: call language-specific functions */
+/* Called from AGI */
+static int say_enumeration_full(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
+{
+ if (!strcasecmp(language,"en") ) { /* English syntax */
+ return(ast_say_enumeration_full_en(chan, num, ints, language, audiofd, ctrlfd));
+ } else if (!strcasecmp(language, "da") ) { /* Danish syntax */
+ return(ast_say_enumeration_full_da(chan, num, ints, language, options, audiofd, ctrlfd));
+ } else if (!strcasecmp(language, "de") ) { /* German syntax */
+ return(ast_say_enumeration_full_de(chan, num, ints, language, options, audiofd, ctrlfd));
+ }
+
+ /* Default to english */
+ return(ast_say_enumeration_full_en(chan, num, ints, language, audiofd, ctrlfd));
+}
+
+/*! \brief ast_say_enumeration_full_en: English syntax */
+/* This is the default syntax, if no other syntax defined in this file is used */
+static int ast_say_enumeration_full_en(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd)
+{
+ int res = 0, t = 0;
+ char fn[256] = "";
+
+ while (!res && num) {
+ if (num < 0) {
+ ast_copy_string(fn, "digits/minus", sizeof(fn)); /* kind of senseless for enumerations, but our best effort for error checking */
+ if ( num > INT_MIN ) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ } else if (num < 20) {
+ snprintf(fn, sizeof(fn), "digits/h-%d", num);
+ num = 0;
+ } else if (num < 100) {
+ int tens = num / 10;
+ num = num % 10;
+ if (num == 0) {
+ snprintf(fn, sizeof(fn), "digits/h-%d", (tens * 10));
+ } else {
+ snprintf(fn, sizeof(fn), "digits/%d", (tens * 10));
+ }
+ } else if (num < 1000) {
+ int hundreds = num / 100;
+ num = num % 100;
+ if (hundreds > 1 || t == 1) {
+ res = ast_say_number_full_en(chan, hundreds, ints, language, audiofd, ctrlfd);
+ }
+ if (res)
+ return res;
+ if (num) {
+ ast_copy_string(fn, "digits/hundred", sizeof(fn));
+ } else {
+ ast_copy_string(fn, "digits/h-hundred", sizeof(fn));
+ }
+ } else if (num < 1000000) {
+ int thousands = num / 1000;
+ num = num % 1000;
+ if (thousands > 1 || t == 1) {
+ res = ast_say_number_full_en(chan, thousands, ints, language, audiofd, ctrlfd);
+ }
+ if (res)
+ return res;
+ if (num) {
+ ast_copy_string(fn, "digits/thousand", sizeof(fn));
+ } else {
+ ast_copy_string(fn, "digits/h-thousand", sizeof(fn));
+ }
+ t = 1;
+ } else if (num < 1000000000) {
+ int millions = num / 1000000;
+ num = num % 1000000;
+ t = 1;
+ res = ast_say_number_full_en(chan, millions, ints, language, audiofd, ctrlfd);
+ if (res)
+ return res;
+ if (num) {
+ ast_copy_string(fn, "digits/million", sizeof(fn));
+ } else {
+ ast_copy_string(fn, "digits/h-million", sizeof(fn));
+ }
+ } else if (num < INT_MAX) {
+ int billions = num / 1000000000;
+ num = num % 1000000000;
+ t = 1;
+ res = ast_say_number_full_en(chan, billions, ints, language, audiofd, ctrlfd);
+ if (res)
+ return res;
+ if (num) {
+ ast_copy_string(fn, "digits/billion", sizeof(fn));
+ } else {
+ ast_copy_string(fn, "digits/h-billion", sizeof(fn));
+ }
+ } else if (num == INT_MAX) {
+ ast_copy_string(fn, "digits/h-last", sizeof(fn));
+ num = 0;
+ } else {
+ ast_debug(1, "Number '%d' is too big for me\n", num);
+ res = -1;
+ }
+
+ if (!res) {
+ if (!ast_streamfile(chan, fn, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1)) {
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ } else {
+ res = ast_waitstream(chan, ints);
+ }
+ }
+ ast_stopstream(chan);
+ }
+ }
+ return res;
+}
+
+/*! \brief ast_say_enumeration_full_da: Danish syntax */
+static int ast_say_enumeration_full_da(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
+{
+ /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender; 'p' plural */
+ int res = 0, t = 0;
+ char fn[256] = "", fna[256] = "";
+ char *gender;
+
+ if (options && !strncasecmp(options, "f",1)) {
+ gender = "F";
+ } else if (options && !strncasecmp(options, "n",1)) {
+ gender = "N";
+ } else {
+ gender = "";
+ }
+
+ if (!num)
+ return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
+
+ while (!res && num) {
+ if (num < 0) {
+ ast_copy_string(fn, "digits/minus", sizeof(fn)); /* kind of senseless for enumerations, but our best effort for error checking */
+ if ( num > INT_MIN ) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ } else if (num < 100 && t) {
+ ast_copy_string(fn, "digits/and", sizeof(fn));
+ t = 0;
+ } else if (num < 20) {
+ snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
+ num = 0;
+ } else if (num < 100) {
+ int ones = num % 10;
+ if (ones) {
+ snprintf(fn, sizeof(fn), "digits/%d-and", ones);
+ num -= ones;
+ } else {
+ snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
+ num = 0;
+ }
+ } else if (num == 100 && t == 0) {
+ snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
+ num = 0;
+ } else if (num < 1000) {
+ int hundreds = num / 100;
+ num = num % 100;
+ if (hundreds == 1) {
+ ast_copy_string(fn, "digits/1N", sizeof(fn));
+ } else {
+ snprintf(fn, sizeof(fn), "digits/%d", hundreds);
+ }
+ if (num) {
+ ast_copy_string(fna, "digits/hundred", sizeof(fna));
+ } else {
+ snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
+ }
+ t = 1;
+ } else if (num < 1000000) {
+ int thousands = num / 1000;
+ num = num % 1000;
+ if (thousands == 1) {
+ if (num) {
+ ast_copy_string(fn, "digits/1N", sizeof(fn));
+ ast_copy_string(fna, "digits/thousand", sizeof(fna));
+ } else {
+ if (t) {
+ ast_copy_string(fn, "digits/1N", sizeof(fn));
+ snprintf(fna, sizeof(fna), "digits/h-thousand%s", gender);
+ } else {
+ snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
+ }
+ }
+ } else {
+ res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
+ if (res) {
+ return res;
+ }
+ if (num) {
+ ast_copy_string(fn, "digits/thousand", sizeof(fn));
+ } else {
+ snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
+ }
+ }
+ t = 1;
+ } else if (num < 1000000000) {
+ int millions = num / 1000000;
+ num = num % 1000000;
+ if (millions == 1) {
+ if (num) {
+ ast_copy_string(fn, "digits/1F", sizeof(fn));
+ ast_copy_string(fna, "digits/million", sizeof(fna));
+ } else {
+ ast_copy_string(fn, "digits/1N", sizeof(fn));
+ snprintf(fna, sizeof(fna), "digits/h-million%s", gender);
+ }
+ } else {
+ res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
+ if (res) {
+ return res;
+ }
+ if (num) {
+ ast_copy_string(fn, "digits/millions", sizeof(fn));
+ } else {
+ snprintf(fn, sizeof(fn), "digits/h-million%s", gender);
+ }
+ }
+ t = 1;
+ } else if (num < INT_MAX) {
+ int billions = num / 1000000000;
+ num = num % 1000000000;
+ if (billions == 1) {
+ if (num) {
+ ast_copy_string(fn, "digits/1F", sizeof(fn));
+ ast_copy_string(fna, "digits/milliard", sizeof(fna));
+ } else {
+ ast_copy_string(fn, "digits/1N", sizeof(fn));
+ snprintf(fna, sizeof(fna), "digits/h-milliard%s", gender);
+ }
+ } else {
+ res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
+ if (res)
+ return res;
+ if (num) {
+ ast_copy_string(fn, "digits/milliards", sizeof(fna));
+ } else {
+ snprintf(fn, sizeof(fna), "digits/h-milliard%s", gender);
+ }
+ }
+ t = 1;
+ } else if (num == INT_MAX) {
+ snprintf(fn, sizeof(fn), "digits/h-last%s", gender);
+ num = 0;
+ } else {
+ ast_debug(1, "Number '%d' is too big for me\n", num);
+ res = -1;
+ }
+
+ if (!res) {
+ if (!ast_streamfile(chan, fn, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ res = ast_waitstream(chan, ints);
+ }
+ ast_stopstream(chan);
+ if (!res) {
+ if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1)) {
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ } else {
+ res = ast_waitstream(chan, ints);
+ }
+ }
+ ast_stopstream(chan);
+ strcpy(fna, "");
+ }
+ }
+ }
+ return res;
+}
+
+/*! \brief ast_say_enumeration_full_de: German syntax */
+static int ast_say_enumeration_full_de(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
+{
+ /* options can be: '' or 'm' male gender; 'f' female gender; 'n' neuter gender; 'p' plural */
+ int res = 0, t = 0;
+ char fn[256] = "", fna[256] = "";
+ char *gender;
+
+ if (options && !strncasecmp(options, "f",1)) {
+ gender = "F";
+ } else if (options && !strncasecmp(options, "n",1)) {
+ gender = "N";
+ } else {
+ gender = "";
+ }
+
+ if (!num)
+ return ast_say_digits_full(chan, 0,ints, language, audiofd, ctrlfd);
+
+ while (!res && num) {
+ if (num < 0) {
+ ast_copy_string(fn, "digits/minus", sizeof(fn)); /* kind of senseless for enumerations, but our best effort for error checking */
+ if ( num > INT_MIN ) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ } else if (num < 100 && t) {
+ ast_copy_string(fn, "digits/and", sizeof(fn));
+ t = 0;
+ } else if (num < 20) {
+ snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
+ num = 0;
+ } else if (num < 100) {
+ int ones = num % 10;
+ if (ones) {
+ snprintf(fn, sizeof(fn), "digits/%d-and", ones);
+ num -= ones;
+ } else {
+ snprintf(fn, sizeof(fn), "digits/h-%d%s", num, gender);
+ num = 0;
+ }
+ } else if (num == 100 && t == 0) {
+ snprintf(fn, sizeof(fn), "digits/h-hundred%s", gender);
+ num = 0;
+ } else if (num < 1000) {
+ int hundreds = num / 100;
+ num = num % 100;
+ if (hundreds == 1) {
+ ast_copy_string(fn, "digits/1N", sizeof(fn));
+ } else {
+ snprintf(fn, sizeof(fn), "digits/%d", hundreds);
+ }
+ if (num) {
+ ast_copy_string(fna, "digits/hundred", sizeof(fna));
+ } else {
+ snprintf(fna, sizeof(fna), "digits/h-hundred%s", gender);
+ }
+ t = 1;
+ } else if (num < 1000000) {
+ int thousands = num / 1000;
+ num = num % 1000;
+ if (thousands == 1) {
+ if (num) {
+ ast_copy_string(fn, "digits/1N", sizeof(fn));
+ ast_copy_string(fna, "digits/thousand", sizeof(fna));
+ } else {
+ if (t) {
+ ast_copy_string(fn, "digits/1N", sizeof(fn));
+ snprintf(fna, sizeof(fna), "digits/h-thousand%s", gender);
+ } else {
+ snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
+ }
+ }
+ } else {
+ res = ast_say_number_full_de(chan, thousands, ints, language, options, audiofd, ctrlfd);
+ if (res) {
+ return res;
+ }
+ if (num) {
+ ast_copy_string(fn, "digits/thousand", sizeof(fn));
+ } else {
+ snprintf(fn, sizeof(fn), "digits/h-thousand%s", gender);
+ }
+ }
+ t = 1;
+ } else if (num < 1000000000) {
+ int millions = num / 1000000;
+ num = num % 1000000;
+ if (millions == 1) {
+ if (num) {
+ ast_copy_string(fn, "digits/1F", sizeof(fn));
+ ast_copy_string(fna, "digits/million", sizeof(fna));
+ } else {
+ ast_copy_string(fn, "digits/1N", sizeof(fn));
+ snprintf(fna, sizeof(fna), "digits/h-million%s", gender);
+ }
+ } else {
+ res = ast_say_number_full_de(chan, millions, ints, language, options, audiofd, ctrlfd);
+ if (res) {
+ return res;
+ }
+ if (num) {
+ ast_copy_string(fn, "digits/millions", sizeof(fn));
+ } else {
+ snprintf(fn, sizeof(fn), "digits/h-million%s", gender);
+ }
+ }
+ t = 1;
+ } else if (num < INT_MAX) {
+ int billions = num / 1000000000;
+ num = num % 1000000000;
+ if (billions == 1) {
+ if (num) {
+ ast_copy_string(fn, "digits/1F", sizeof(fn));
+ ast_copy_string(fna, "digits/milliard", sizeof(fna));
+ } else {
+ ast_copy_string(fn, "digits/1N", sizeof(fn));
+ snprintf(fna, sizeof(fna), "digits/h-milliard%s", gender);
+ }
+ } else {
+ res = ast_say_number_full_de(chan, billions, ints, language, options, audiofd, ctrlfd);
+ if (res)
+ return res;
+ if (num) {
+ ast_copy_string(fn, "digits/milliards", sizeof(fna));
+ } else {
+ snprintf(fn, sizeof(fna), "digits/h-milliard%s", gender);
+ }
+ }
+ t = 1;
+ } else if (num == INT_MAX) {
+ snprintf(fn, sizeof(fn), "digits/h-last%s", gender);
+ num = 0;
+ } else {
+ ast_debug(1, "Number '%d' is too big for me\n", num);
+ res = -1;
+ }
+
+ if (!res) {
+ if (!ast_streamfile(chan, fn, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ res = ast_waitstream(chan, ints);
+ }
+ ast_stopstream(chan);
+ if (!res) {
+ if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1)) {
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ } else {
+ res = ast_waitstream(chan, ints);
+ }
+ }
+ ast_stopstream(chan);
+ strcpy(fna, "");
+ }
+ }
+ }
+ return res;
+}
+
+static int say_date(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ if (!strcasecmp(lang, "en") ) { /* English syntax */
+ return(ast_say_date_en(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "da") ) { /* Danish syntax */
+ return(ast_say_date_da(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "de") ) { /* German syntax */
+ return(ast_say_date_de(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "fr") ) { /* French syntax */
+ return(ast_say_date_fr(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "hu") ) { /* Hungarian syntax */
+ return(ast_say_date_hu(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
+ return(ast_say_date_nl(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "pt") || !strcasecmp(lang, "pt_BR")) { /* Portuguese syntax */
+ return(ast_say_date_pt(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "gr") ) { /* Greek syntax */
+ return(ast_say_date_gr(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "th") ) { /* Thai syntax */
+ return(ast_say_date_th(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "ge") ) { /* Georgian syntax */
+ return(ast_say_date_ge(chan, t, ints, lang));
+ }
+
+ /* Default to English */
+ return(ast_say_date_en(chan, t, ints, lang));
+}
+
+/* English syntax */
+int ast_say_date_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct ast_tm tm;
+ struct timeval tv = { t, 0 };
+ char fn[256];
+ int res = 0;
+ ast_localtime(&tv, &tm, NULL);
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (!res)
+ res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ if (!res)
+ res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
+ return res;
+}
+
+/* Danish syntax */
+int ast_say_date_da(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ char fn[256];
+ int res = 0;
+ ast_localtime(&tv, &tm, NULL);
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (!res)
+ res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char * ) NULL);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (!res) {
+ /* Year */
+ int year = tm.tm_year + 1900;
+ if (year > 1999) { /* year 2000 and later */
+ res = ast_say_number(chan, year, ints, lang, (char *) NULL);
+ } else {
+ if (year < 1100) {
+ /* I'm not going to handle 1100 and prior */
+ /* We'll just be silent on the year, instead of bombing out. */
+ } else {
+ /* year 1100 to 1999. will anybody need this?!? */
+ snprintf(fn,sizeof(fn), "digits/%d", (year / 100) );
+ res = wait_file(chan, ints, fn, lang);
+ if (!res) {
+ res = wait_file(chan,ints, "digits/hundred", lang);
+ if (!res && year % 100 != 0) {
+ res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
+ }
+ }
+ }
+ }
+ }
+ return res;
+}
+
+/* German syntax */
+int ast_say_date_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ char fn[256];
+ int res = 0;
+ ast_localtime(&tv, &tm, NULL);
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (!res)
+ res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char * ) NULL);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (!res) {
+ /* Year */
+ int year = tm.tm_year + 1900;
+ if (year > 1999) { /* year 2000 and later */
+ res = ast_say_number(chan, year, ints, lang, (char *) NULL);
+ } else {
+ if (year < 1100) {
+ /* I'm not going to handle 1100 and prior */
+ /* We'll just be silent on the year, instead of bombing out. */
+ } else {
+ /* year 1100 to 1999. will anybody need this?!? */
+ /* say 1967 as 'neunzehn hundert sieben und sechzig' */
+ snprintf(fn,sizeof(fn), "digits/%d", (year / 100) );
+ res = wait_file(chan, ints, fn, lang);
+ if (!res) {
+ res = wait_file(chan,ints, "digits/hundred", lang);
+ if (!res && year % 100 != 0) {
+ res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
+ }
+ }
+ }
+ }
+ }
+ return res;
+}
+
+/* Hungarian syntax */
+int ast_say_date_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ char fn[256];
+ int res = 0;
+ ast_localtime(&tv, &tm, NULL);
+
+ if (!res)
+ res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (!res)
+ ast_say_number(chan, tm.tm_mday , ints, lang, (char *) NULL);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ return res;
+}
+
+/* French syntax */
+int ast_say_date_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ char fn[256];
+ int res = 0;
+ ast_localtime(&tv, &tm, NULL);
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (!res)
+ res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (!res)
+ res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
+ return res;
+}
+
+/* Dutch syntax */
+int ast_say_date_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ char fn[256];
+ int res = 0;
+ ast_localtime(&tv, &tm, NULL);
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (!res)
+ res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ if (!res)
+ res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
+ return res;
+}
+
+/* Thai syntax */
+int ast_say_date_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ char fn[256];
+ int res = 0;
+ ast_localtime(&tv, &tm, NULL);
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
+ res = ast_streamfile(chan, fn, lang);
+ ast_copy_string(fn, "digits/tee", sizeof(fn));
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (!res)
+ res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ if (!res) {
+ ast_copy_string(fn, "digits/duan", sizeof(fn));
+ res = ast_streamfile(chan, fn, lang);
+ snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (!res){
+ ast_copy_string(fn, "digits/posor", sizeof(fn));
+ res = ast_streamfile(chan, fn, lang);
+ res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
+ }
+ return res;
+}
+
+/* Portuguese syntax */
+int ast_say_date_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ char fn[256];
+ int res = 0;
+
+ ast_localtime(&tv, &tm, NULL);
+ snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
+ if (!res)
+ res = wait_file(chan, ints, fn, lang);
+ if (!res)
+ res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
+ if (!res)
+ res = wait_file(chan, ints, "digits/pt-de", lang);
+ snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
+ if (!res)
+ res = wait_file(chan, ints, fn, lang);
+ if (!res)
+ res = wait_file(chan, ints, "digits/pt-de", lang);
+ if (!res)
+ res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
+
+ return res;
+}
+
+static int say_date_with_format(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
+{
+ if (!strcasecmp(lang, "en") ) { /* English syntax */
+ return(ast_say_date_with_format_en(chan, time, ints, lang, format, timezone));
+ } else if (!strcasecmp(lang, "da") ) { /* Danish syntax */
+ return(ast_say_date_with_format_da(chan, time, ints, lang, format, timezone));
+ } else if (!strcasecmp(lang, "de") ) { /* German syntax */
+ return(ast_say_date_with_format_de(chan, time, ints, lang, format, timezone));
+ } else if (!strcasecmp(lang, "es") || !strcasecmp(lang, "mx")) { /* Spanish syntax */
+ return(ast_say_date_with_format_es(chan, time, ints, lang, format, timezone));
+ } else if (!strcasecmp(lang, "he")) { /* Hebrew syntax */
+ return(ast_say_date_with_format_he(chan, time, ints, lang, format, timezone));
+ } else if (!strcasecmp(lang, "fr") ) { /* French syntax */
+ return(ast_say_date_with_format_fr(chan, time, ints, lang, format, timezone));
+ } else if (!strcasecmp(lang, "it") ) { /* Italian syntax */
+ return(ast_say_date_with_format_it(chan, time, ints, lang, format, timezone));
+ } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
+ return(ast_say_date_with_format_nl(chan, time, ints, lang, format, timezone));
+ } else if (!strcasecmp(lang, "pl") ) { /* Polish syntax */
+ return(ast_say_date_with_format_pl(chan, time, ints, lang, format, timezone));
+ } else if (!strcasecmp(lang, "pt") || !strcasecmp(lang, "pt_BR")) { /* Portuguese syntax */
+ return(ast_say_date_with_format_pt(chan, time, ints, lang, format, timezone));
+ } else if (!strcasecmp(lang, "tw") || !strcasecmp(lang, "zh") ) { /* Taiwanese / Chinese syntax */
+ return(ast_say_date_with_format_tw(chan, time, ints, lang, format, timezone));
+ } else if (!strcasecmp(lang, "th") ) { /* Thai syntax */
+ return(ast_say_date_with_format_th(chan, time, ints, lang, format, timezone));
+ } else if (!strcasecmp(lang, "gr") ) { /* Greek syntax */
+ return(ast_say_date_with_format_gr(chan, time, ints, lang, format, timezone));
+ }
+
+ /* Default to English */
+ return(ast_say_date_with_format_en(chan, time, ints, lang, format, timezone));
+}
+
+/* English syntax */
+int ast_say_date_with_format_en(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
+{
+ struct timeval tv = { time, 0 };
+ struct ast_tm tm;
+ int res=0, offset, sndoffset;
+ char sndfile[256], nextmsg[256];
+
+ if (format == NULL)
+ format = "ABdY 'digits/at' IMp";
+
+ ast_localtime(&tv, &tm, timezone);
+
+ for (offset=0 ; format[offset] != '\0' ; offset++) {
+ ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
+ switch (format[offset]) {
+ /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
+ case '\'':
+ /* Literal name of a sound file */
+ sndoffset=0;
+ for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
+ sndfile[sndoffset] = format[offset];
+ sndfile[sndoffset] = '\0';
+ res = wait_file(chan,ints,sndfile,lang);
+ break;
+ case 'A':
+ case 'a':
+ /* Sunday - Saturday */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'B':
+ case 'b':
+ case 'h':
+ /* January - December */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'm':
+ /* Month enumerated */
+ res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, (char *) NULL);
+ break;
+ case 'd':
+ case 'e':
+ /* First - Thirtyfirst */
+ res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, (char *) NULL);
+ break;
+ case 'Y':
+ /* Year */
+ if (tm.tm_year > 99) {
+ res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
+ } else if (tm.tm_year < 1) {
+ /* I'm not going to handle 1900 and prior */
+ /* We'll just be silent on the year, instead of bombing out. */
+ } else {
+ res = wait_file(chan, ints, "digits/19", lang);
+ if (!res) {
+ if (tm.tm_year <= 9) {
+ /* 1901 - 1909 */
+ res = wait_file(chan,ints, "digits/oh", lang);
+ }
+
+ res |= ast_say_number(chan, tm.tm_year, ints, lang, (char *) NULL);
+ }
+ }
+ break;
+ case 'I':
+ case 'l':
+ /* 12-Hour */
+ if (tm.tm_hour == 0)
+ ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
+ else if (tm.tm_hour > 12)
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
+ else
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'H':
+ case 'k':
+ /* 24-Hour */
+ if (format[offset] == 'H') {
+ /* e.g. oh-eight */
+ if (tm.tm_hour < 10) {
+ res = wait_file(chan,ints, "digits/oh",lang);
+ }
+ } else {
+ /* e.g. eight */
+ if (tm.tm_hour == 0) {
+ res = wait_file(chan,ints, "digits/oh",lang);
+ }
+ }
+ if (!res) {
+ if (tm.tm_hour != 0) {
+ int remainder = tm.tm_hour;
+ if (tm.tm_hour > 20) {
+ res = wait_file(chan,ints, "digits/20",lang);
+ remainder -= 20;
+ }
+ if (!res) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", remainder);
+ res = wait_file(chan,ints,nextmsg,lang);
+ }
+ }
+ }
+ break;
+ case 'M':
+ case 'N':
+ /* Minute */
+ if (tm.tm_min == 0) {
+ if (format[offset] == 'M') {
+ res = wait_file(chan, ints, "digits/oclock", lang);
+ } else {
+ res = wait_file(chan, ints, "digits/hundred", lang);
+ }
+ } else if (tm.tm_min < 10) {
+ res = wait_file(chan,ints, "digits/oh",lang);
+ if (!res) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min);
+ res = wait_file(chan,ints,nextmsg,lang);
+ }
+ } else {
+ res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
+ }
+ break;
+ case 'P':
+ case 'p':
+ /* AM/PM */
+ if (tm.tm_hour > 11)
+ ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
+ else
+ ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'Q':
+ /* Shorthand for "Today", "Yesterday", or ABdY */
+ /* XXX As emphasized elsewhere, this should the native way in your
+ * language to say the date, with changes in what you say, depending
+ * upon how recent the date is. XXX */
+ {
+ struct timeval now = ast_tvnow();
+ struct ast_tm tmnow;
+ time_t beg_today;
+
+ gettimeofday(&now,NULL);
+ ast_localtime(&now, &tmnow, timezone);
+ /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
+ /* In any case, it saves not having to do ast_mktime() */
+ beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
+ if (beg_today < time) {
+ /* Today */
+ res = wait_file(chan,ints, "digits/today",lang);
+ } else if (beg_today - 86400 < time) {
+ /* Yesterday */
+ res = wait_file(chan,ints, "digits/yesterday",lang);
+ } else if (beg_today - 86400 * 6 < time) {
+ /* Within the last week */
+ res = ast_say_date_with_format_en(chan, time, ints, lang, "A", timezone);
+ } else if (beg_today - 2628000 < time) {
+ /* Less than a month ago - "Sunday, October third" */
+ res = ast_say_date_with_format_en(chan, time, ints, lang, "ABd", timezone);
+ } else if (beg_today - 15768000 < time) {
+ /* Less than 6 months ago - "August seventh" */
+ res = ast_say_date_with_format_en(chan, time, ints, lang, "Bd", timezone);
+ } else {
+ /* More than 6 months ago - "April nineteenth two thousand three" */
+ res = ast_say_date_with_format_en(chan, time, ints, lang, "BdY", timezone);
+ }
+ }
+ break;
+ case 'q':
+ /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
+ /* XXX As emphasized elsewhere, this should the native way in your
+ * language to say the date, with changes in what you say, depending
+ * upon how recent the date is. XXX */
+ {
+ struct timeval now;
+ struct ast_tm tmnow;
+ time_t beg_today;
+
+ now = ast_tvnow();
+ ast_localtime(&now, &tmnow, timezone);
+ /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
+ /* In any case, it saves not having to do ast_mktime() */
+ beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
+ if (beg_today < time) {
+ /* Today */
+ } else if ((beg_today - 86400) < time) {
+ /* Yesterday */
+ res = wait_file(chan,ints, "digits/yesterday",lang);
+ } else if (beg_today - 86400 * 6 < time) {
+ /* Within the last week */
+ res = ast_say_date_with_format_en(chan, time, ints, lang, "A", timezone);
+ } else if (beg_today - 2628000 < time) {
+ /* Less than a month ago - "Sunday, October third" */
+ res = ast_say_date_with_format_en(chan, time, ints, lang, "ABd", timezone);
+ } else if (beg_today - 15768000 < time) {
+ /* Less than 6 months ago - "August seventh" */
+ res = ast_say_date_with_format_en(chan, time, ints, lang, "Bd", timezone);
+ } else {
+ /* More than 6 months ago - "April nineteenth two thousand three" */
+ res = ast_say_date_with_format_en(chan, time, ints, lang, "BdY", timezone);
+ }
+ }
+ break;
+ case 'R':
+ res = ast_say_date_with_format_en(chan, time, ints, lang, "HM", timezone);
+ break;
+ case 'S':
+ /* Seconds */
+ if (tm.tm_sec == 0) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
+ res = wait_file(chan,ints,nextmsg,lang);
+ } else if (tm.tm_sec < 10) {
+ res = wait_file(chan,ints, "digits/oh",lang);
+ if (!res) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
+ res = wait_file(chan,ints,nextmsg,lang);
+ }
+ } else {
+ res = ast_say_number(chan, tm.tm_sec, ints, lang, (char *) NULL);
+ }
+ break;
+ case 'T':
+ res = ast_say_date_with_format_en(chan, time, ints, lang, "HMS", timezone);
+ break;
+ case ' ':
+ case ' ':
+ /* Just ignore spaces and tabs */
+ break;
+ default:
+ /* Unknown character */
+ ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
+ }
+ /* Jump out on DTMF */
+ if (res) {
+ break;
+ }
+ }
+ return res;
+}
+
+/* Danish syntax */
+int ast_say_date_with_format_da(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
+{
+ struct timeval tv = { time, 0 };
+ struct ast_tm tm;
+ int res=0, offset, sndoffset;
+ char sndfile[256], nextmsg[256];
+
+ if (!format)
+ format = "A dBY HMS";
+
+ ast_localtime(&tv, &tm, timezone);
+
+ for (offset=0 ; format[offset] != '\0' ; offset++) {
+ ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
+ switch (format[offset]) {
+ /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
+ case '\'':
+ /* Literal name of a sound file */
+ sndoffset=0;
+ for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
+ sndfile[sndoffset] = format[offset];
+ sndfile[sndoffset] = '\0';
+ res = wait_file(chan,ints,sndfile,lang);
+ break;
+ case 'A':
+ case 'a':
+ /* Sunday - Saturday */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'B':
+ case 'b':
+ case 'h':
+ /* January - December */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'm':
+ /* Month enumerated */
+ res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, "m");
+ break;
+ case 'd':
+ case 'e':
+ /* First - Thirtyfirst */
+ res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, "m");
+ break;
+ case 'Y':
+ /* Year */
+ {
+ int year = tm.tm_year + 1900;
+ if (year > 1999) { /* year 2000 and later */
+ res = ast_say_number(chan, year, ints, lang, (char *) NULL);
+ } else {
+ if (year < 1100) {
+ /* I'm not going to handle 1100 and prior */
+ /* We'll just be silent on the year, instead of bombing out. */
+ } else {
+ /* year 1100 to 1999. will anybody need this?!? */
+ /* say 1967 as 'nineteen hundred seven and sixty' */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", (year / 100) );
+ res = wait_file(chan,ints,nextmsg,lang);
+ if (!res) {
+ res = wait_file(chan,ints, "digits/hundred",lang);
+ if (!res && year % 100 != 0) {
+ res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
+ }
+ }
+ }
+ }
+ }
+ break;
+ case 'I':
+ case 'l':
+ /* 12-Hour */
+ res = wait_file(chan,ints,"digits/oclock",lang);
+ if (tm.tm_hour == 0)
+ ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
+ else if (tm.tm_hour > 12)
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
+ else
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
+ if (!res) {
+ res = wait_file(chan,ints,nextmsg,lang);
+ }
+ break;
+ case 'H':
+ /* 24-Hour, single digit hours preceeded by "oh" (0) */
+ if (tm.tm_hour < 10 && tm.tm_hour > 0) {
+ res = wait_file(chan,ints, "digits/0",lang);
+ }
+ /* FALLTRHU */
+ case 'k':
+ /* 24-Hour */
+ res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
+ break;
+ case 'M':
+ /* Minute */
+ if (tm.tm_min > 0 || format[offset+ 1 ] == 'S' ) { /* zero 'digits/0' only if seconds follow (kind of a hack) */
+ res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
+ }
+ if ( !res && format[offset + 1] == 'S' ) { /* minutes only if seconds follow (kind of a hack) */
+ if (tm.tm_min == 1) {
+ res = wait_file(chan,ints,"digits/minute",lang);
+ } else {
+ res = wait_file(chan,ints,"digits/minutes",lang);
+ }
+ }
+ break;
+ case 'P':
+ case 'p':
+ /* AM/PM */
+ if (tm.tm_hour > 11)
+ ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
+ else
+ ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'Q':
+ /* Shorthand for "Today", "Yesterday", or AdBY */
+ /* XXX As emphasized elsewhere, this should the native way in your
+ * language to say the date, with changes in what you say, depending
+ * upon how recent the date is. XXX */
+ {
+ struct timeval now = ast_tvnow();
+ struct ast_tm tmnow;
+ time_t beg_today;
+
+ ast_localtime(&now, &tmnow, timezone);
+ /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
+ /* In any case, it saves not having to do ast_mktime() */
+ beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
+ if (beg_today < time) {
+ /* Today */
+ res = wait_file(chan,ints, "digits/today",lang);
+ } else if (beg_today - 86400 < time) {
+ /* Yesterday */
+ res = wait_file(chan,ints, "digits/yesterday",lang);
+ } else {
+ res = ast_say_date_with_format_da(chan, time, ints, lang, "AdBY", timezone);
+ }
+ }
+ break;
+ case 'q':
+ /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
+ /* XXX As emphasized elsewhere, this should the native way in your
+ * language to say the date, with changes in what you say, depending
+ * upon how recent the date is. XXX */
+ {
+ struct timeval now = ast_tvnow();
+ struct ast_tm tmnow;
+ time_t beg_today;
+
+ ast_localtime(&now, &tmnow, timezone);
+ /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
+ /* In any case, it saves not having to do ast_mktime() */
+ beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
+ if (beg_today < time) {
+ /* Today */
+ } else if ((beg_today - 86400) < time) {
+ /* Yesterday */
+ res = wait_file(chan,ints, "digits/yesterday",lang);
+ } else if (beg_today - 86400 * 6 < time) {
+ /* Within the last week */
+ res = ast_say_date_with_format_da(chan, time, ints, lang, "A", timezone);
+ } else {
+ res = ast_say_date_with_format_da(chan, time, ints, lang, "AdBY", timezone);
+ }
+ }
+ break;
+ case 'R':
+ res = ast_say_date_with_format_da(chan, time, ints, lang, "HM", timezone);
+ break;
+ case 'S':
+ /* Seconds */
+ res = wait_file(chan,ints, "digits/and",lang);
+ if (!res) {
+ res = ast_say_number(chan, tm.tm_sec, ints, lang, "f");
+ if (!res) {
+ res = wait_file(chan,ints, "digits/seconds",lang);
+ }
+ }
+ break;
+ case 'T':
+ res = ast_say_date_with_format_da(chan, time, ints, lang, "HMS", timezone);
+ break;
+ case ' ':
+ case ' ':
+ /* Just ignore spaces and tabs */
+ break;
+ default:
+ /* Unknown character */
+ ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
+ }
+ /* Jump out on DTMF */
+ if (res) {
+ break;
+ }
+ }
+ return res;
+}
+
+/* German syntax */
+int ast_say_date_with_format_de(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
+{
+ struct timeval tv = { time, 0 };
+ struct ast_tm tm;
+ int res=0, offset, sndoffset;
+ char sndfile[256], nextmsg[256];
+
+ if (!format)
+ format = "A dBY HMS";
+
+ ast_localtime(&tv, &tm, timezone);
+
+ for (offset=0 ; format[offset] != '\0' ; offset++) {
+ ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
+ switch (format[offset]) {
+ /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
+ case '\'':
+ /* Literal name of a sound file */
+ sndoffset=0;
+ for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
+ sndfile[sndoffset] = format[offset];
+ sndfile[sndoffset] = '\0';
+ res = wait_file(chan,ints,sndfile,lang);
+ break;
+ case 'A':
+ case 'a':
+ /* Sunday - Saturday */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'B':
+ case 'b':
+ case 'h':
+ /* January - December */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'm':
+ /* Month enumerated */
+ res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, "m");
+ break;
+ case 'd':
+ case 'e':
+ /* First - Thirtyfirst */
+ res = ast_say_enumeration(chan, tm.tm_mday, ints, lang, "m");
+ break;
+ case 'Y':
+ /* Year */
+ {
+ int year = tm.tm_year + 1900;
+ if (year > 1999) { /* year 2000 and later */
+ res = ast_say_number(chan, year, ints, lang, (char *) NULL);
+ } else {
+ if (year < 1100) {
+ /* I'm not going to handle 1100 and prior */
+ /* We'll just be silent on the year, instead of bombing out. */
+ } else {
+ /* year 1100 to 1999. will anybody need this?!? */
+ /* say 1967 as 'neunzehn hundert sieben und sechzig' */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", (year / 100) );
+ res = wait_file(chan,ints,nextmsg,lang);
+ if (!res) {
+ res = wait_file(chan,ints, "digits/hundred",lang);
+ if (!res && year % 100 != 0) {
+ res = ast_say_number(chan, (year % 100), ints, lang, (char *) NULL);
+ }
+ }
+ }
+ }
+ }
+ break;
+ case 'I':
+ case 'l':
+ /* 12-Hour */
+ if (tm.tm_hour == 0)
+ ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
+ else if (tm.tm_hour > 12)
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
+ else
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
+ res = wait_file(chan,ints,nextmsg,lang);
+ if (!res) {
+ res = wait_file(chan,ints,"digits/oclock",lang);
+ }
+ break;
+ case 'H':
+ case 'k':
+ /* 24-Hour */
+ res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
+ if (!res) {
+ res = wait_file(chan,ints,"digits/oclock",lang);
+ }
+ break;
+ case 'M':
+ /* Minute */
+ if (tm.tm_min > 0 || format[offset+ 1 ] == 'S' ) { /* zero 'digits/0' only if seconds follow (kind of a hack) */
+ res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
+ }
+ if ( !res && format[offset + 1] == 'S' ) { /* minutes only if seconds follow (kind of a hack) */
+ if (tm.tm_min == 1) {
+ res = wait_file(chan,ints,"digits/minute",lang);
+ } else {
+ res = wait_file(chan,ints,"digits/minutes",lang);
+ }
+ }
+ break;
+ case 'P':
+ case 'p':
+ /* AM/PM */
+ if (tm.tm_hour > 11)
+ ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
+ else
+ ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'Q':
+ /* Shorthand for "Today", "Yesterday", or AdBY */
+ /* XXX As emphasized elsewhere, this should the native way in your
+ * language to say the date, with changes in what you say, depending
+ * upon how recent the date is. XXX */
+ {
+ struct timeval now = ast_tvnow();
+ struct ast_tm tmnow;
+ time_t beg_today;
+
+ ast_localtime(&now, &tmnow, timezone);
+ /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
+ /* In any case, it saves not having to do ast_mktime() */
+ beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
+ if (beg_today < time) {
+ /* Today */
+ res = wait_file(chan,ints, "digits/today",lang);
+ } else if (beg_today - 86400 < time) {
+ /* Yesterday */
+ res = wait_file(chan,ints, "digits/yesterday",lang);
+ } else {
+ res = ast_say_date_with_format_de(chan, time, ints, lang, "AdBY", timezone);
+ }
+ }
+ break;
+ case 'q':
+ /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
+ /* XXX As emphasized elsewhere, this should the native way in your
+ * language to say the date, with changes in what you say, depending
+ * upon how recent the date is. XXX */
+ {
+ struct timeval now = ast_tvnow();
+ struct ast_tm tmnow;
+ time_t beg_today;
+
+ ast_localtime(&now, &tmnow, timezone);
+ /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
+ /* In any case, it saves not having to do ast_mktime() */
+ beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
+ if (beg_today < time) {
+ /* Today */
+ } else if ((beg_today - 86400) < time) {
+ /* Yesterday */
+ res = wait_file(chan,ints, "digits/yesterday",lang);
+ } else if (beg_today - 86400 * 6 < time) {
+ /* Within the last week */
+ res = ast_say_date_with_format_de(chan, time, ints, lang, "A", timezone);
+ } else {
+ res = ast_say_date_with_format_de(chan, time, ints, lang, "AdBY", timezone);
+ }
+ }
+ break;
+ case 'R':
+ res = ast_say_date_with_format_de(chan, time, ints, lang, "HM", timezone);
+ break;
+ case 'S':
+ /* Seconds */
+ res = wait_file(chan,ints, "digits/and",lang);
+ if (!res) {
+ res = ast_say_number(chan, tm.tm_sec, ints, lang, "f");
+ if (!res) {
+ res = wait_file(chan,ints, "digits/seconds",lang);
+ }
+ }
+ break;
+ case 'T':
+ res = ast_say_date_with_format_de(chan, time, ints, lang, "HMS", timezone);
+ break;
+ case ' ':
+ case ' ':
+ /* Just ignore spaces and tabs */
+ break;
+ default:
+ /* Unknown character */
+ ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
+ }
+ /* Jump out on DTMF */
+ if (res) {
+ break;
+ }
+ }
+ return res;
+}
+
+/* Thai syntax */
+int ast_say_date_with_format_th(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
+{
+ struct timeval tv = { time, 0 };
+ struct ast_tm tm;
+ int res=0, offset, sndoffset;
+ char sndfile[256], nextmsg[256];
+
+ if (format == NULL)
+ format = "a 'digits/tee' e 'digits/duan' hY I 'digits/naliga' M 'digits/natee'";
+
+ ast_localtime(&tv, &tm, timezone);
+
+ for (offset=0 ; format[offset] != '\0' ; offset++) {
+ ast_log(LOG_DEBUG, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
+ switch (format[offset]) {
+ /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
+ case '\'':
+ /* Literal name of a sound file */
+ sndoffset=0;
+ for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
+ sndfile[sndoffset] = format[offset];
+ sndfile[sndoffset] = '\0';
+ res = wait_file(chan,ints,sndfile,lang);
+ break;
+ case 'A':
+ case 'a':
+ /* Sunday - Saturday */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'B':
+ case 'b':
+ case 'h':
+ /* January - December */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'm':
+ /* Month enumerated */
+ res = ast_say_number(chan, (tm.tm_mon + 1), ints, lang, (char *) NULL);
+ break;
+ case 'd':
+ case 'e':
+ /* First - Thirtyfirst */
+ res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
+ break;
+ case 'Y':
+ /* Year */
+ res = ast_say_number(chan, tm.tm_year + 1900 + 543, ints, lang, (char *) NULL);
+ break;
+ case 'I':
+ case 'l':
+ /* 12-Hour */
+ if (tm.tm_hour == 0)
+ ast_copy_string(nextmsg, "digits/24", sizeof(nextmsg));
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'H':
+ case 'k':
+ /* 24-Hour */
+ if (tm.tm_hour == 0)
+ ast_copy_string(nextmsg, "digits/24", sizeof(nextmsg));
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'M':
+ case 'N':
+ res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
+ break;
+ case 'P':
+ case 'p':
+ break;
+ case 'Q':
+ /* Shorthand for "Today", "Yesterday", or ABdY */
+ /* XXX As emphasized elsewhere, this should the native way in your
+ * language to say the date, with changes in what you say, depending
+ * upon how recent the date is. XXX */
+ {
+ struct timeval now = ast_tvnow();
+ struct ast_tm tmnow;
+ time_t beg_today;
+
+ ast_localtime(&now, &tmnow, timezone);
+ /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
+ /* In any case, it saves not having to do ast_mktime() */
+ beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
+ if (beg_today < time) {
+ /* Today */
+ res = wait_file(chan,ints, "digits/today",lang);
+ } else if (beg_today - 86400 < time) {
+ /* Yesterday */
+ res = wait_file(chan,ints, "digits/yesterday",lang);
+ } else if (beg_today - 86400 * 6 < time) {
+ /* Within the last week */
+ res = ast_say_date_with_format_en(chan, time, ints, lang, "A", timezone);
+ } else if (beg_today - 2628000 < time) {
+ /* Less than a month ago - "Sunday, October third" */
+ res = ast_say_date_with_format_en(chan, time, ints, lang, "ABd", timezone);
+ } else if (beg_today - 15768000 < time) {
+ /* Less than 6 months ago - "August seventh" */
+ res = ast_say_date_with_format_en(chan, time, ints, lang, "Bd", timezone);
+ } else {
+ /* More than 6 months ago - "April nineteenth two thousand three" */
+ res = ast_say_date_with_format_en(chan, time, ints, lang, "BdY", timezone);
+ }
+ }
+ break;
+ case 'q':
+ /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
+ /* XXX As emphasized elsewhere, this should the native way in your
+ * language to say the date, with changes in what you say, depending
+ * upon how recent the date is. XXX */
+ {
+ struct timeval now = ast_tvnow();
+ struct ast_tm tmnow;
+ time_t beg_today;
+
+ ast_localtime(&now, &tmnow, timezone);
+ /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
+ /* In any case, it saves not having to do ast_mktime() */
+ beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
+ if (beg_today < time) {
+ /* Today */
+ } else if ((beg_today - 86400) < time) {
+ /* Yesterday */
+ res = wait_file(chan,ints, "digits/yesterday",lang);
+ } else if (beg_today - 86400 * 6 < time) {
+ /* Within the last week */
+ res = ast_say_date_with_format_en(chan, time, ints, lang, "A", timezone);
+ } else if (beg_today - 2628000 < time) {
+ /* Less than a month ago - "Sunday, October third" */
+ res = ast_say_date_with_format_en(chan, time, ints, lang, "ABd", timezone);
+ } else if (beg_today - 15768000 < time) {
+ /* Less than 6 months ago - "August seventh" */
+ res = ast_say_date_with_format_en(chan, time, ints, lang, "Bd", timezone);
+ } else {
+ /* More than 6 months ago - "April nineteenth two thousand three" */
+ res = ast_say_date_with_format_en(chan, time, ints, lang, "BdY", timezone);
+ }
+ }
+ break;
+ case 'R':
+ res = ast_say_date_with_format_en(chan, time, ints, lang, "HM", timezone);
+ break;
+ case 'S':
+ res = ast_say_number(chan, tm.tm_sec, ints, lang, (char *) NULL);
+ break;
+ case 'T':
+ res = ast_say_date_with_format_en(chan, time, ints, lang, "HMS", timezone);
+ break;
+ case ' ':
+ case ' ':
+ /* Just ignore spaces and tabs */
+ break;
+ default:
+ /* Unknown character */
+ ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
+ }
+ /* Jump out on DTMF */
+ if (res) {
+ break;
+ }
+ }
+ return res;
+}
+
+/* TODO: this probably is not the correct format for doxygen remarks */
+
+/** ast_say_date_with_format_he Say formatted date in Hebrew
+ *
+ * \ref ast_say_date_with_format_en for the details of the options
+ *
+ * Changes from the English version:
+ *
+ * * don't replicate in here the logic of ast_say_number_full_he
+ *
+ * * year is always 4-digit (because it's simpler)
+ *
+ * * added c, x, and X. Mainly for my tests
+ *
+ * * The standard "long" format used in Hebrew is AdBY, rather than ABdY
+ *
+ * TODO:
+ * * A "ha" is missing in the standard date format, before the 'd'.
+ * * The numbers of 3000--19000 are not handled well
+ **/
+#define IL_DATE_STR "AdBY"
+#define IL_TIME_STR "IMp"
+#define IL_DATE_STR_FULL IL_DATE_STR " 'digits/at' " IL_TIME_STR
+int ast_say_date_with_format_he(struct ast_channel *chan, time_t time,
+ const char *ints, const char *lang, const char *format,
+ const char *timezone)
+{
+ /* TODO: This whole function is cut&paste from
+ * ast_say_date_with_format_en . Is that considered acceptable?
+ **/
+ struct timeval tv = { time, 0 };
+ struct ast_tm tm;
+ int res=0, offset, sndoffset;
+ char sndfile[256], nextmsg[256];
+
+ if (!format)
+ format = IL_DATE_STR_FULL;
+
+ ast_localtime(&tv, &tm, timezone);
+
+ for (offset=0 ; format[offset] != '\0' ; offset++) {
+ ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
+ switch (format[offset]) {
+ /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
+ case '\'':
+ /* Literal name of a sound file */
+ sndoffset=0;
+ for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
+ sndfile[sndoffset] = format[offset];
+ sndfile[sndoffset] = '\0';
+ res = wait_file(chan,ints,sndfile,lang);
+ break;
+ case 'A':
+ case 'a':
+ /* Sunday - Saturday */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'B':
+ case 'b':
+ case 'h':
+ /* January - December */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'd':
+ case 'e': /* Day of the month */
+ /* I'm not sure exactly what the parameters
+ * audiofd and ctrlfd to
+ * ast_say_number_full_he mean, but it seems
+ * safe to pass -1 there.
+ *
+ * At least in one of the pathes :-(
+ */
+ res = ast_say_number_full_he(chan, tm.tm_mday,
+ ints, lang, "m", -1, -1
+ );
+ break;
+ case 'Y': /* Year */
+ res = ast_say_number_full_he(chan, tm.tm_year+1900,
+ ints, lang, "f", -1, -1
+ );
+ break;
+ case 'I':
+ case 'l': /* 12-Hour */
+ {
+ int hour = tm.tm_hour;
+ hour = hour%12;
+ if (hour == 0) hour=12;
+
+ res = ast_say_number_full_he(chan, hour,
+ ints, lang, "f", -1, -1
+ );
+ }
+ break;
+ case 'H':
+ case 'k': /* 24-Hour */
+ /* With 'H' there is an 'oh' after a single-
+ * digit hour */
+ if ((format[offset] == 'H') &&
+ (tm.tm_hour <10)&&(tm.tm_hour>0)
+ ) { /* e.g. oh-eight */
+ res = wait_file(chan,ints, "digits/oh",lang);
+ }
+
+ res = ast_say_number_full_he(chan, tm.tm_hour,
+ ints, lang, "f", -1, -1
+ );
+ break;
+ case 'M': /* Minute */
+ res = ast_say_number_full_he(chan, tm.tm_min,
+ ints, lang,"f", -1, -1
+ );
+ break;
+ case 'P':
+ case 'p':
+ /* AM/PM */
+ if (tm.tm_hour > 11)
+ ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
+ else
+ ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'Q':
+ /* Shorthand for "Today", "Yesterday", or "date" */
+ case 'q':
+ /* Shorthand for "" (today), "Yesterday", A
+ * (weekday), or "date" */
+ /* XXX As emphasized elsewhere, this should the native way in your
+ * language to say the date, with changes in what you say, depending
+ * upon how recent the date is. XXX */
+ {
+ struct timeval now = ast_tvnow();
+ struct ast_tm tmnow;
+ time_t beg_today;
+ char todo = format[offset]; /* The letter to format*/
+
+ ast_localtime(&now, &tmnow, timezone);
+ /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
+ /* In any case, it saves not having to do ast_mktime() */
+ beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
+ if (beg_today < time) {
+ /* Today */
+ if (todo == 'Q') {
+ res = wait_file(chan,
+ ints,
+ "digits/today",
+ lang);
+ }
+ } else if (beg_today - 86400 < time) {
+ /* Yesterday */
+ res = wait_file(chan,ints, "digits/yesterday",lang);
+ } else if ((todo != 'Q') &&
+ (beg_today - 86400 * 6 < time))
+ {
+ /* Within the last week */
+ res = ast_say_date_with_format_he(chan,
+ time, ints, lang,
+ "A", timezone);
+ } else {
+ res = ast_say_date_with_format_he(chan,
+ time, ints, lang,
+ IL_DATE_STR, timezone);
+ }
+ }
+ break;
+ case 'R':
+ res = ast_say_date_with_format_he(chan, time, ints, lang, "HM", timezone);
+ break;
+ case 'S': /* Seconds */
+ res = ast_say_number_full_he(chan, tm.tm_sec,
+ ints, lang, "f", -1, -1
+ );
+ break;
+ case 'T':
+ res = ast_say_date_with_format_he(chan, time, ints, lang, "HMS", timezone);
+ break;
+ /* c, x, and X seem useful for testing. Not sure
+ * if thiey're good for the general public */
+ case 'c':
+ res = ast_say_date_with_format_he(chan, time,
+ ints, lang, IL_DATE_STR_FULL, timezone);
+ break;
+ case 'x':
+ res = ast_say_date_with_format_he(chan, time,
+ ints, lang, IL_DATE_STR, timezone);
+ break;
+ case 'X': /* Currently not locale-dependent...*/
+ res = ast_say_date_with_format_he(chan, time,
+ ints, lang, IL_TIME_STR, timezone);
+ break;
+ case ' ':
+ case ' ':
+ /* Just ignore spaces and tabs */
+ break;
+ default:
+ /* Unknown character */
+ ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
+ }
+ /* Jump out on DTMF */
+ if (res) {
+ break;
+ }
+ }
+ return res;
+}
+
+
+/* Spanish syntax */
+int ast_say_date_with_format_es(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
+{
+ struct timeval tv = { time, 0 };
+ struct ast_tm tm;
+ int res=0, offset, sndoffset;
+ char sndfile[256], nextmsg[256];
+
+ if (format == NULL)
+ format = "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y 'digits/at' IMp";
+
+ ast_localtime(&tv, &tm, timezone);
+
+ for (offset=0 ; format[offset] != '\0' ; offset++) {
+ ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
+ switch (format[offset]) {
+ /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
+ case '\'':
+ /* Literal name of a sound file */
+ sndoffset=0;
+ for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
+ sndfile[sndoffset] = format[offset];
+ sndfile[sndoffset] = '\0';
+ snprintf(nextmsg,sizeof(nextmsg), "%s", sndfile);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'A':
+ case 'a':
+ /* Sunday - Saturday */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'B':
+ case 'b':
+ case 'h':
+ /* January - December */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'm':
+ /* First - Twelfth */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'd':
+ case 'e':
+ /* First - Thirtyfirst */
+ res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
+ break;
+ case 'Y':
+ /* Year */
+ res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
+ break;
+ case 'I':
+ case 'l':
+ /* 12-Hour */
+ if (tm.tm_hour == 0)
+ ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
+ else if (tm.tm_hour > 12)
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
+ else
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'H':
+ case 'k':
+ /* 24-Hour */
+ res = ast_say_number(chan, tm.tm_hour, ints, lang, NULL);
+ break;
+ case 'M':
+ /* Minute */
+ res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
+ break;
+ case 'P':
+ case 'p':
+ /* AM/PM */
+ if (tm.tm_hour > 18)
+ res = wait_file(chan, ints, "digits/p-m", lang);
+ else if (tm.tm_hour > 12)
+ res = wait_file(chan, ints, "digits/afternoon", lang);
+ else if (tm.tm_hour)
+ res = wait_file(chan, ints, "digits/a-m", lang);
+ break;
+ case 'Q':
+ /* Shorthand for "Today", "Yesterday", or ABdY */
+ /* XXX As emphasized elsewhere, this should the native way in your
+ * language to say the date, with changes in what you say, depending
+ * upon how recent the date is. XXX */
+ {
+ struct timeval now = ast_tvnow();
+ struct ast_tm tmnow;
+ time_t beg_today;
+
+ ast_localtime(&now, &tmnow, timezone);
+ /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
+ /* In any case, it saves not having to do ast_mktime() */
+ beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
+ if (beg_today < time) {
+ /* Today */
+ res = wait_file(chan,ints, "digits/today",lang);
+ } else if (beg_today - 86400 < time) {
+ /* Yesterday */
+ res = wait_file(chan,ints, "digits/yesterday",lang);
+ } else {
+ res = ast_say_date_with_format_es(chan, time, ints, lang, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", timezone);
+ }
+ }
+ break;
+ case 'q':
+ /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
+ /* XXX As emphasized elsewhere, this should the native way in your
+ * language to say the date, with changes in what you say, depending
+ * upon how recent the date is. XXX */
+ {
+ struct timeval now = ast_tvnow();
+ struct ast_tm tmnow;
+ time_t beg_today;
+
+ ast_localtime(&now, &tmnow, timezone);
+ /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
+ /* In any case, it saves not having to do ast_mktime() */
+ beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
+ if (beg_today < time) {
+ /* Today */
+ res = wait_file(chan,ints, "digits/today",lang);
+ } else if ((beg_today - 86400) < time) {
+ /* Yesterday */
+ res = wait_file(chan,ints, "digits/yesterday",lang);
+ } else if (beg_today - 86400 * 6 < time) {
+ /* Within the last week */
+ res = ast_say_date_with_format_es(chan, time, ints, lang, "A", timezone);
+ } else {
+ res = ast_say_date_with_format_es(chan, time, ints, lang, "'digits/es-el' Ad 'digits/es-de' B 'digits/es-de' Y", timezone);
+ }
+ }
+ break;
+ case 'R':
+ res = ast_say_date_with_format_es(chan, time, ints, lang, "H 'digits/y' M", timezone);
+ break;
+ case 'S':
+ /* Seconds */
+ if (tm.tm_sec == 0) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
+ res = wait_file(chan,ints,nextmsg,lang);
+ } else if (tm.tm_sec < 10) {
+ res = wait_file(chan,ints, "digits/oh",lang);
+ if (!res) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
+ res = wait_file(chan,ints,nextmsg,lang);
+ }
+ } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
+ res = wait_file(chan,ints,nextmsg,lang);
+ } else {
+ int ten, one;
+ ten = (tm.tm_sec / 10) * 10;
+ one = (tm.tm_sec % 10);
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
+ res = wait_file(chan,ints,nextmsg,lang);
+ if (!res) {
+ /* Fifty, not fifty-zero */
+ if (one != 0) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
+ res = wait_file(chan,ints,nextmsg,lang);
+ }
+ }
+ }
+ break;
+ case 'T':
+ res = ast_say_date_with_format_es(chan, time, ints, lang, "HMS", timezone);
+ break;
+ case ' ':
+ case ' ':
+ /* Just ignore spaces and tabs */
+ break;
+ default:
+ /* Unknown character */
+ ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
+ }
+ /* Jump out on DTMF */
+ if (res) {
+ break;
+ }
+ }
+ return res;
+}
+
+/* French syntax
+oclock = heure
+*/
+int ast_say_date_with_format_fr(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
+{
+ struct timeval tv = { time, 0 };
+ struct ast_tm tm;
+ int res=0, offset, sndoffset;
+ char sndfile[256], nextmsg[256];
+
+ if (format == NULL)
+ format = "AdBY 'digits/at' IMp";
+
+ ast_localtime(&tv, &tm, timezone);
+
+ for (offset=0 ; format[offset] != '\0' ; offset++) {
+ ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
+ switch (format[offset]) {
+ /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
+ case '\'':
+ /* Literal name of a sound file */
+ sndoffset=0;
+ for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
+ sndfile[sndoffset] = format[offset];
+ sndfile[sndoffset] = '\0';
+ res = wait_file(chan,ints,sndfile,lang);
+ break;
+ case 'A':
+ case 'a':
+ /* Sunday - Saturday */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'B':
+ case 'b':
+ case 'h':
+ /* January - December */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'm':
+ /* First - Twelfth */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'd':
+ case 'e':
+ /* First */
+ if (tm.tm_mday == 1) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mday);
+ res = wait_file(chan,ints,nextmsg,lang);
+ } else {
+ res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
+ }
+ break;
+ case 'Y':
+ /* Year */
+ if (tm.tm_year > 99) {
+ res = wait_file(chan,ints, "digits/2",lang);
+ if (!res) {
+ res = wait_file(chan,ints, "digits/thousand",lang);
+ }
+ if (tm.tm_year > 100) {
+ if (!res) {
+ res = ast_say_number(chan, tm.tm_year - 100, ints, lang, (char * ) NULL);
+ }
+ }
+ } else {
+ if (tm.tm_year < 1) {
+ /* I'm not going to handle 1900 and prior */
+ /* We'll just be silent on the year, instead of bombing out. */
+ } else {
+ res = wait_file(chan,ints, "digits/thousand",lang);
+ if (!res) {
+ wait_file(chan,ints, "digits/9",lang);
+ wait_file(chan,ints, "digits/hundred",lang);
+ res = ast_say_number(chan, tm.tm_year, ints, lang, (char * ) NULL);
+ }
+ }
+ }
+ break;
+ case 'I':
+ case 'l':
+ /* 12-Hour */
+ if (tm.tm_hour == 0)
+ ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
+ else if (tm.tm_hour > 12)
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
+ else
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
+ res = wait_file(chan,ints,nextmsg,lang);
+ if (!res)
+ res = wait_file(chan,ints, "digits/oclock",lang);
+ break;
+ case 'H':
+ case 'k':
+ /* 24-Hour */
+ res = ast_say_number(chan, tm.tm_hour, ints, lang, (char * ) NULL);
+ if (!res)
+ res = wait_file(chan,ints, "digits/oclock",lang);
+ break;
+ case 'M':
+ /* Minute */
+ if (tm.tm_min == 0) {
+ break;
+ }
+ res = ast_say_number(chan, tm.tm_min, ints, lang, (char * ) NULL);
+ break;
+ case 'P':
+ case 'p':
+ /* AM/PM */
+ if (tm.tm_hour > 11)
+ ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
+ else
+ ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'Q':
+ /* Shorthand for "Today", "Yesterday", or AdBY */
+ /* XXX As emphasized elsewhere, this should the native way in your
+ * language to say the date, with changes in what you say, depending
+ * upon how recent the date is. XXX */
+ {
+ struct timeval now = ast_tvnow();
+ struct ast_tm tmnow;
+ time_t beg_today;
+
+ ast_localtime(&now, &tmnow, timezone);
+ /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
+ /* In any case, it saves not having to do ast_mktime() */
+ beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
+ if (beg_today < time) {
+ /* Today */
+ res = wait_file(chan,ints, "digits/today",lang);
+ } else if (beg_today - 86400 < time) {
+ /* Yesterday */
+ res = wait_file(chan,ints, "digits/yesterday",lang);
+ } else {
+ res = ast_say_date_with_format_fr(chan, time, ints, lang, "AdBY", timezone);
+ }
+ }
+ break;
+ case 'q':
+ /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
+ /* XXX As emphasized elsewhere, this should the native way in your
+ * language to say the date, with changes in what you say, depending
+ * upon how recent the date is. XXX */
+ {
+ struct timeval now = ast_tvnow();
+ struct ast_tm tmnow;
+ time_t beg_today;
+
+ ast_localtime(&now, &tmnow, timezone);
+ /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
+ /* In any case, it saves not having to do ast_mktime() */
+ beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
+ if (beg_today < time) {
+ /* Today */
+ } else if ((beg_today - 86400) < time) {
+ /* Yesterday */
+ res = wait_file(chan,ints, "digits/yesterday",lang);
+ } else if (beg_today - 86400 * 6 < time) {
+ /* Within the last week */
+ res = ast_say_date_with_format_fr(chan, time, ints, lang, "A", timezone);
+ } else {
+ res = ast_say_date_with_format_fr(chan, time, ints, lang, "AdBY", timezone);
+ }
+ }
+ break;
+ case 'R':
+ res = ast_say_date_with_format_fr(chan, time, ints, lang, "HM", timezone);
+ break;
+ case 'S':
+ /* Seconds */
+ res = ast_say_number(chan, tm.tm_sec, ints, lang, (char * ) NULL);
+ if (!res) {
+ res = wait_file(chan,ints, "digits/second",lang);
+ }
+ break;
+ case 'T':
+ res = ast_say_date_with_format_fr(chan, time, ints, lang, "HMS", timezone);
+ break;
+ case ' ':
+ case ' ':
+ /* Just ignore spaces and tabs */
+ break;
+ default:
+ /* Unknown character */
+ ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
+ }
+ /* Jump out on DTMF */
+ if (res) {
+ break;
+ }
+ }
+ return res;
+}
+
+int ast_say_date_with_format_it(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
+{
+ struct timeval tv = { time, 0 };
+ struct ast_tm tm;
+ int res=0, offset, sndoffset;
+ char sndfile[256], nextmsg[256];
+
+ if (format == NULL)
+ format = "AdB 'digits/at' IMp";
+
+ ast_localtime(&tv, &tm, timezone);
+
+ for (offset=0 ; format[offset] != '\0' ; offset++) {
+ ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
+ switch (format[offset]) {
+ /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
+ case '\'':
+ /* Literal name of a sound file */
+ sndoffset=0;
+ for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
+ sndfile[sndoffset] = format[offset];
+ sndfile[sndoffset] = '\0';
+ res = wait_file(chan,ints,sndfile,lang);
+ break;
+ case 'A':
+ case 'a':
+ /* Sunday - Saturday */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'B':
+ case 'b':
+ case 'h':
+ /* January - December */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'm':
+ /* First - Twelfth */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'd':
+ case 'e':
+ /* First day of the month is spelled as ordinal */
+ if (tm.tm_mday == 1) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mday);
+ res = wait_file(chan,ints,nextmsg,lang);
+ } else {
+ if (!res) {
+ res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
+ }
+ }
+ break;
+ case 'Y':
+ /* Year */
+ if (tm.tm_year > 99) {
+ res = wait_file(chan,ints, "digits/ore-2000",lang);
+ if (tm.tm_year > 100) {
+ if (!res) {
+ /* This works until the end of 2021 */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year - 100);
+ res = wait_file(chan,ints,nextmsg,lang);
+ }
+ }
+ } else {
+ if (tm.tm_year < 1) {
+ /* I'm not going to handle 1900 and prior */
+ /* We'll just be silent on the year, instead of bombing out. */
+ } else {
+ res = wait_file(chan,ints, "digits/ore-1900",lang);
+ if ((!res) && (tm.tm_year != 0)) {
+ if (tm.tm_year <= 21) {
+ /* 1910 - 1921 */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
+ res = wait_file(chan,ints,nextmsg,lang);
+ } else {
+ /* 1922 - 1999, but sounds badly in 1928, 1931, 1938, etc... */
+ int ten, one;
+ ten = tm.tm_year / 10;
+ one = tm.tm_year % 10;
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten * 10);
+ res = wait_file(chan,ints,nextmsg,lang);
+ if (!res) {
+ if (one != 0) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
+ res = wait_file(chan,ints,nextmsg,lang);
+ }
+ }
+ }
+ }
+ }
+ }
+ break;
+ case 'I':
+ case 'l':
+ /* 12-Hour */
+ if (tm.tm_hour == 0)
+ ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
+ else if (tm.tm_hour > 12)
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
+ else
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'H':
+ case 'k':
+ /* 24-Hour */
+ if (tm.tm_hour == 0) {
+ res = wait_file(chan,ints, "digits/ore-mezzanotte",lang);
+ } else if (tm.tm_hour == 1) {
+ res = wait_file(chan,ints, "digits/ore-una",lang);
+ } else {
+ res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
+ }
+ break;
+ case 'M':
+ /* Minute */
+ res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
+ break;
+ case 'P':
+ case 'p':
+ /* AM/PM */
+ if (tm.tm_hour > 11)
+ ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
+ else
+ ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'Q':
+ /* Shorthand for "Today", "Yesterday", or ABdY */
+ /* XXX As emphasized elsewhere, this should the native way in your
+ * language to say the date, with changes in what you say, depending
+ * upon how recent the date is. XXX */
+ {
+ struct timeval now = ast_tvnow();
+ struct ast_tm tmnow;
+ time_t beg_today;
+
+ ast_localtime(&now, &tmnow, timezone);
+ /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
+ /* In any case, it saves not having to do ast_mktime() */
+ beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
+ if (beg_today < time) {
+ /* Today */
+ res = wait_file(chan,ints, "digits/today",lang);
+ } else if (beg_today - 86400 < time) {
+ /* Yesterday */
+ res = wait_file(chan,ints, "digits/yesterday",lang);
+ } else {
+ res = ast_say_date_with_format_it(chan, time, ints, lang, "AdB", timezone);
+ }
+ }
+ break;
+ case 'q':
+ /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
+ {
+ struct timeval now = ast_tvnow();
+ struct ast_tm tmnow;
+ time_t beg_today;
+
+ ast_localtime(&now, &tmnow, timezone);
+ /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
+ /* In any case, it saves not having to do ast_mktime() */
+ beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
+ if (beg_today < time) {
+ /* Today */
+ } else if ((beg_today - 86400) < time) {
+ /* Yesterday */
+ res = wait_file(chan,ints, "digits/yesterday",lang);
+ } else if (beg_today - 86400 * 6 < time) {
+ /* Within the last week */
+ res = ast_say_date_with_format_it(chan, time, ints, lang, "A", timezone);
+ } else {
+ res = ast_say_date_with_format_it(chan, time, ints, lang, "AdB", timezone);
+ }
+ }
+ break;
+ case 'R':
+ res = ast_say_date_with_format_it(chan, time, ints, lang, "HM", timezone);
+ break;
+ case 'S':
+ /* Seconds */
+ if (tm.tm_sec == 0) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
+ res = wait_file(chan,ints,nextmsg,lang);
+ } else if (tm.tm_sec < 10) {
+ res = wait_file(chan,ints, "digits/oh",lang);
+ if (!res) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
+ res = wait_file(chan,ints,nextmsg,lang);
+ }
+ } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
+ res = wait_file(chan,ints,nextmsg,lang);
+ } else {
+ int ten, one;
+ ten = (tm.tm_sec / 10) * 10;
+ one = (tm.tm_sec % 10);
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
+ res = wait_file(chan,ints,nextmsg,lang);
+ if (!res) {
+ /* Fifty, not fifty-zero */
+ if (one != 0) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
+ res = wait_file(chan,ints,nextmsg,lang);
+ }
+ }
+ }
+ break;
+ case 'T':
+ res = ast_say_date_with_format_it(chan, time, ints, lang, "HMS", timezone);
+ break;
+ case ' ':
+ case ' ':
+ /* Just ignore spaces and tabs */
+ break;
+ default:
+ /* Unknown character */
+ ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
+ }
+ /* Jump out on DTMF */
+ if (res) {
+ break;
+ }
+ }
+ return res;
+}
+
+/* Dutch syntax */
+int ast_say_date_with_format_nl(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
+{
+ struct timeval tv = { time, 0 };
+ struct ast_tm tm;
+ int res=0, offset, sndoffset;
+ char sndfile[256], nextmsg[256];
+
+ if (format == NULL)
+ format = "ABdY 'digits/at' IMp";
+
+ ast_localtime(&tv, &tm, timezone);
+
+ for (offset=0 ; format[offset] != '\0' ; offset++) {
+ ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
+ switch (format[offset]) {
+ /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
+ case '\'':
+ /* Literal name of a sound file */
+ sndoffset=0;
+ for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
+ sndfile[sndoffset] = format[offset];
+ sndfile[sndoffset] = '\0';
+ res = wait_file(chan,ints,sndfile,lang);
+ break;
+ case 'A':
+ case 'a':
+ /* Sunday - Saturday */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'B':
+ case 'b':
+ case 'h':
+ /* January - December */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'm':
+ /* First - Twelfth */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'd':
+ case 'e':
+ /* First - Thirtyfirst */
+ res = ast_say_number(chan, tm.tm_mday, ints, lang, NULL);
+ break;
+ case 'Y':
+ /* Year */
+ if (tm.tm_year > 99) {
+ res = wait_file(chan,ints, "digits/2",lang);
+ if (!res) {
+ res = wait_file(chan,ints, "digits/thousand",lang);
+ }
+ if (tm.tm_year > 100) {
+ if (!res) {
+ /* This works until the end of 2020 */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year - 100);
+ res = wait_file(chan,ints,nextmsg,lang);
+ }
+ }
+ } else {
+ if (tm.tm_year < 1) {
+ /* I'm not going to handle 1900 and prior */
+ /* We'll just be silent on the year, instead of bombing out. */
+ } else {
+ res = wait_file(chan,ints, "digits/19",lang);
+ if (!res) {
+ if (tm.tm_year <= 9) {
+ /* 1901 - 1909 */
+ res = wait_file(chan,ints, "digits/oh",lang);
+ if (!res) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
+ res = wait_file(chan,ints,nextmsg,lang);
+ }
+ } else if (tm.tm_year <= 20) {
+ /* 1910 - 1920 */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
+ res = wait_file(chan,ints,nextmsg,lang);
+ } else {
+ /* 1921 - 1999 */
+ int ten, one;
+ ten = tm.tm_year / 10;
+ one = tm.tm_year % 10;
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten * 10);
+ res = wait_file(chan,ints,nextmsg,lang);
+ if (!res) {
+ if (one != 0) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
+ res = wait_file(chan,ints,nextmsg,lang);
+ }
+ }
+ }
+ }
+ }
+ }
+ break;
+ case 'I':
+ case 'l':
+ /* 12-Hour */
+ if (tm.tm_hour == 0)
+ ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
+ else if (tm.tm_hour > 12)
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
+ else
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'H':
+ case 'k':
+ /* 24-Hour */
+ res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
+ if (!res) {
+ res = wait_file(chan,ints, "digits/nl-uur",lang);
+ }
+ break;
+ case 'M':
+ /* Minute */
+ res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
+ break;
+ case 'P':
+ case 'p':
+ /* AM/PM */
+ if (tm.tm_hour > 11)
+ ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
+ else
+ ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'Q':
+ /* Shorthand for "Today", "Yesterday", or ABdY */
+ /* XXX As emphasized elsewhere, this should the native way in your
+ * language to say the date, with changes in what you say, depending
+ * upon how recent the date is. XXX */
+ {
+ struct timeval now = ast_tvnow();
+ struct ast_tm tmnow;
+ time_t beg_today;
+
+ ast_localtime(&now, &tmnow, timezone);
+ /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
+ /* In any case, it saves not having to do ast_mktime() */
+ beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
+ if (beg_today < time) {
+ /* Today */
+ res = wait_file(chan,ints, "digits/today",lang);
+ } else if (beg_today - 86400 < time) {
+ /* Yesterday */
+ res = wait_file(chan,ints, "digits/yesterday",lang);
+ } else {
+ res = ast_say_date_with_format_nl(chan, time, ints, lang, "ABdY", timezone);
+ }
+ }
+ break;
+ case 'q':
+ /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
+ {
+ struct timeval now = ast_tvnow();
+ struct ast_tm tmnow;
+ time_t beg_today;
+
+ ast_localtime(&now, &tmnow, timezone);
+ /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
+ /* In any case, it saves not having to do ast_mktime() */
+ beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
+ if (beg_today < time) {
+ /* Today */
+ } else if ((beg_today - 86400) < time) {
+ /* Yesterday */
+ res = wait_file(chan,ints, "digits/yesterday",lang);
+ } else if (beg_today - 86400 * 6 < time) {
+ /* Within the last week */
+ res = ast_say_date_with_format_nl(chan, time, ints, lang, "A", timezone);
+ } else {
+ res = ast_say_date_with_format_nl(chan, time, ints, lang, "ABdY", timezone);
+ }
+ }
+ break;
+ case 'R':
+ res = ast_say_date_with_format_nl(chan, time, ints, lang, "HM", timezone);
+ break;
+ case 'S':
+ /* Seconds */
+ res = ast_say_number(chan, tm.tm_sec, ints, lang, (char *) NULL);
+ break;
+ case 'T':
+ res = ast_say_date_with_format_nl(chan, time, ints, lang, "HMS", timezone);
+ break;
+ case ' ':
+ case ' ':
+ /* Just ignore spaces and tabs */
+ break;
+ default:
+ /* Unknown character */
+ ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
+ }
+ /* Jump out on DTMF */
+ if (res) {
+ break;
+ }
+ }
+ return res;
+}
+
+/* Polish syntax */
+int ast_say_date_with_format_pl(struct ast_channel *chan, time_t thetime, const char *ints, const char *lang, const char *format, const char *timezone)
+{
+ struct timeval tv = { thetime, 0 };
+ struct ast_tm tm;
+ int res=0, offset, sndoffset;
+ char sndfile[256], nextmsg[256];
+
+ ast_localtime(&tv, &tm, timezone);
+
+ for (offset = 0 ; format[offset] != '\0' ; offset++) {
+ int remainder;
+ ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
+ switch (format[offset]) {
+ /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
+ case '\'':
+ /* Literal name of a sound file */
+ sndoffset = 0;
+ for (sndoffset = 0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
+ sndfile[sndoffset] = format[offset];
+ sndfile[sndoffset] = '\0';
+ res = wait_file(chan, ints, sndfile, lang);
+ break;
+ case 'A':
+ case 'a':
+ /* Sunday - Saturday */
+ snprintf(nextmsg, sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
+ res = wait_file(chan, ints, nextmsg, lang);
+ break;
+ case 'B':
+ case 'b':
+ case 'h':
+ /* January - December */
+ snprintf(nextmsg, sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
+ res = wait_file(chan, ints, nextmsg, lang);
+ break;
+ case 'm':
+ /* Month enumerated */
+ res = ast_say_enumeration(chan, (tm.tm_mon + 1), ints, lang, NULL);
+ break;
+ case 'd':
+ case 'e':
+ /* First - Thirtyfirst */
+ remainder = tm.tm_mday;
+ if (tm.tm_mday > 30) {
+ res = wait_file(chan, ints, "digits/h-30", lang);
+ remainder -= 30;
+ }
+ if (tm.tm_mday > 20 && tm.tm_mday < 30) {
+ res = wait_file(chan, ints, "digits/h-20", lang);
+ remainder -= 20;
+ }
+ if (!res) {
+ snprintf(nextmsg, sizeof(nextmsg), "digits/h-%d", remainder);
+ res = wait_file(chan, ints, nextmsg, lang);
+ }
+ break;
+ case 'Y':
+ /* Year */
+ if (tm.tm_year > 100) {
+ res = wait_file(chan, ints, "digits/2", lang);
+ if (!res)
+ res = wait_file(chan, ints, "digits/1000.2",lang);
+ if (tm.tm_year > 100) {
+ if (!res)
+ res = ast_say_enumeration(chan, tm.tm_year - 100, ints, lang, NULL);
+ }
+ } else if (tm.tm_year == 100) {
+ res = wait_file(chan, ints, "digits/h-2000", lang);
+ } else {
+ if (tm.tm_year < 1) {
+ /* I'm not going to handle 1900 and prior */
+ /* We'll just be silent on the year, instead of bombing out. */
+ break;
+ } else {
+ res = wait_file(chan, ints, "digits/1000", lang);
+ if (!res) {
+ wait_file(chan, ints, "digits/900", lang);
+ res = ast_say_enumeration(chan, tm.tm_year, ints, lang, NULL);
+ }
+ }
+ }
+ if (!res)
+ wait_file(chan, ints, "digits/year", lang);
+ break;
+ case 'I':
+ case 'l':
+ /* 12-Hour */
+ if (tm.tm_hour == 0)
+ ast_copy_string(nextmsg, "digits/t-12", sizeof(nextmsg));
+ else if (tm.tm_hour > 12)
+ snprintf(nextmsg, sizeof(nextmsg), "digits/t-%d", tm.tm_hour - 12);
+ else
+ snprintf(nextmsg, sizeof(nextmsg), "digits/t-%d", tm.tm_hour);
+
+ res = wait_file(chan, ints, nextmsg, lang);
+ break;
+ case 'H':
+ case 'k':
+ /* 24-Hour */
+ if (tm.tm_hour != 0) {
+ snprintf(nextmsg, sizeof(nextmsg), "digits/t-%d", tm.tm_hour);
+ res = wait_file(chan, ints, nextmsg, lang);
+ } else
+ res = wait_file(chan, ints, "digits/t-24", lang);
+ break;
+ case 'M':
+ case 'N':
+ /* Minute */
+ if (tm.tm_min == 0) {
+ if (format[offset] == 'M') {
+ res = wait_file(chan, ints, "digits/oclock", lang);
+ } else {
+ res = wait_file(chan, ints, "digits/100", lang);
+ }
+ } else
+ res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
+ break;
+ case 'P':
+ case 'p':
+ /* AM/PM */
+ if (tm.tm_hour > 11)
+ ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
+ else
+ ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
+ res = wait_file(chan, ints, nextmsg, lang);
+ break;
+ case 'Q':
+ /* Shorthand for "Today", "Yesterday", or AdBY */
+ {
+ struct timeval now = ast_tvnow();
+ struct ast_tm tmnow;
+ time_t beg_today;
+
+ ast_localtime(&now, &tmnow, timezone);
+ /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
+ /* In any case, it saves not having to do ast_mktime() */
+ beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
+ if (beg_today < thetime) {
+ /* Today */
+ res = wait_file(chan, ints, "digits/today", lang);
+ } else if (beg_today - 86400 < thetime) {
+ /* Yesterday */
+ res = wait_file(chan, ints, "digits/yesterday", lang);
+ } else {
+ res = ast_say_date_with_format(chan, thetime, ints, lang, "AdBY", timezone);
+ }
+ }
+ break;
+ case 'q':
+ /* Shorthand for "" (today), "Yesterday", A (weekday), or AdBY */
+ {
+ struct timeval now = ast_tvnow();
+ struct ast_tm tmnow;
+ time_t beg_today;
+
+ ast_localtime(&now, &tmnow, timezone);
+ /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
+ /* In any case, it saves not having to do ast_mktime() */
+ beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
+ if (beg_today < thetime) {
+ /* Today */
+ } else if ((beg_today - 86400) < thetime) {
+ /* Yesterday */
+ res = wait_file(chan, ints, "digits/yesterday", lang);
+ } else if (beg_today - 86400 * 6 < thetime) {
+ /* Within the last week */
+ res = ast_say_date_with_format(chan, thetime, ints, lang, "A", timezone);
+ } else {
+ res = ast_say_date_with_format(chan, thetime, ints, lang, "AdBY", timezone);
+ }
+ }
+ break;
+ case 'R':
+ res = ast_say_date_with_format(chan, thetime, ints, lang, "HM", timezone);
+ break;
+ case 'S':
+ /* Seconds */
+ res = wait_file(chan, ints, "digits/and", lang);
+ if (!res) {
+ if (tm.tm_sec == 1) {
+ res = wait_file(chan, ints, "digits/1z", lang);
+ if (!res)
+ res = wait_file(chan, ints, "digits/second-a", lang);
+ } else {
+ res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
+ if (!res) {
+ int ten, one;
+ ten = tm.tm_sec / 10;
+ one = tm.tm_sec % 10;
+
+ if (one > 1 && one < 5 && ten != 1)
+ res = wait_file(chan,ints, "digits/seconds",lang);
+ else
+ res = wait_file(chan,ints, "digits/second",lang);
+ }
+ }
+ }
+ break;
+ case 'T':
+ res = ast_say_date_with_format(chan, thetime, ints, lang, "HMS", timezone);
+ break;
+ case ' ':
+ case ' ':
+ /* Just ignore spaces and tabs */
+ break;
+ default:
+ /* Unknown character */
+ ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
+ }
+ /* Jump out on DTMF */
+ if (res)
+ break;
+ }
+ return res;
+}
+
+/* Portuguese syntax */
+int ast_say_date_with_format_pt(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
+{
+ struct timeval tv = { time, 0 };
+ struct ast_tm tm;
+ int res=0, offset, sndoffset;
+ char sndfile[256], nextmsg[256];
+
+ if (format == NULL)
+ format = "Ad 'digits/pt-de' B 'digits/pt-de' Y I 'digits/pt-e' Mp";
+
+ ast_localtime(&tv, &tm, timezone);
+
+ for (offset=0 ; format[offset] != '\0' ; offset++) {
+ ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
+ switch (format[offset]) {
+ /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
+ case '\'':
+ /* Literal name of a sound file */
+ sndoffset=0;
+ for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
+ sndfile[sndoffset] = format[offset];
+ sndfile[sndoffset] = '\0';
+ snprintf(nextmsg,sizeof(nextmsg), "%s", sndfile);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'A':
+ case 'a':
+ /* Sunday - Saturday */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'B':
+ case 'b':
+ case 'h':
+ /* January - December */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'm':
+ /* First - Twelfth */
+ if (!strcasecmp(lang, "pt_BR")) {
+ res = ast_say_number(chan, tm.tm_mon+1, ints, lang, (char *) NULL);
+ } else {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/h-%d", tm.tm_mon +1);
+ res = wait_file(chan,ints,nextmsg,lang);
+ }
+ break;
+ case 'd':
+ case 'e':
+ /* First - Thirtyfirst */
+ res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
+ break;
+ case 'Y':
+ /* Year */
+ res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
+ break;
+ case 'I':
+ case 'l':
+ /* 12-Hour */
+ if (!strcasecmp(lang, "pt_BR")) {
+ if (tm.tm_hour == 0) {
+ if (format[offset] == 'I')
+ res = wait_file(chan, ints, "digits/pt-a", lang);
+ if (!res)
+ res = wait_file(chan, ints, "digits/pt-meianoite", lang);
+ } else if (tm.tm_hour == 12) {
+ if (format[offset] == 'I')
+ res = wait_file(chan, ints, "digits/pt-ao", lang);
+ if (!res)
+ res = wait_file(chan, ints, "digits/pt-meiodia", lang);
+ } else {
+ if (format[offset] == 'I') {
+ if ((tm.tm_hour % 12) != 1)
+ res = wait_file(chan, ints, "digits/pt-as", lang);
+ else
+ res = wait_file(chan, ints, "digits/pt-a", lang);
+ }
+ if (!res)
+ res = ast_say_number(chan, (tm.tm_hour % 12), ints, lang, "f");
+ }
+ } else {
+ if (tm.tm_hour == 0) {
+ if (format[offset] == 'I')
+ res = wait_file(chan, ints, "digits/pt-ah", lang);
+ if (!res)
+ res = wait_file(chan, ints, "digits/pt-meianoite", lang);
+ }
+ else if (tm.tm_hour == 12) {
+ if (format[offset] == 'I')
+ res = wait_file(chan, ints, "digits/pt-ao", lang);
+ if (!res)
+ res = wait_file(chan, ints, "digits/pt-meiodia", lang);
+ }
+ else {
+ if (format[offset] == 'I') {
+ res = wait_file(chan, ints, "digits/pt-ah", lang);
+ if ((tm.tm_hour % 12) != 1)
+ if (!res)
+ res = wait_file(chan, ints, "digits/pt-sss", lang);
+ }
+ if (!res)
+ res = ast_say_number(chan, (tm.tm_hour % 12), ints, lang, "f");
+ }
+ }
+ break;
+ case 'H':
+ case 'k':
+ /* 24-Hour */
+ if (!strcasecmp(lang, "pt_BR")) {
+ res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
+ if ((!res) && (format[offset] == 'H')) {
+ if (tm.tm_hour > 1) {
+ res = wait_file(chan,ints,"digits/hours",lang);
+ } else {
+ res = wait_file(chan,ints,"digits/hour",lang);
+ }
+ }
+ } else {
+ res = ast_say_number(chan, -tm.tm_hour, ints, lang, NULL);
+ if (!res) {
+ if (tm.tm_hour != 0) {
+ int remainder = tm.tm_hour;
+ if (tm.tm_hour > 20) {
+ res = wait_file(chan,ints, "digits/20",lang);
+ remainder -= 20;
+ }
+ if (!res) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", remainder);
+ res = wait_file(chan,ints,nextmsg,lang);
+ }
+ }
+ }
+ }
+ break;
+ case 'M':
+ /* Minute */
+ if (!strcasecmp(lang, "pt_BR")) {
+ res = ast_say_number(chan, tm.tm_min, ints, lang, NULL);
+ if (!res) {
+ if (tm.tm_min > 1) {
+ res = wait_file(chan,ints,"digits/minutes",lang);
+ } else {
+ res = wait_file(chan,ints,"digits/minute",lang);
+ }
+ }
+ } else {
+ if (tm.tm_min == 0) {
+ res = wait_file(chan, ints, "digits/pt-hora", lang);
+ if (tm.tm_hour != 1)
+ if (!res)
+ res = wait_file(chan, ints, "digits/pt-sss", lang);
+ } else {
+ res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
+ }
+ }
+ break;
+ case 'P':
+ case 'p':
+ /* AM/PM */
+ if (!strcasecmp(lang, "pt_BR")) {
+ if ((tm.tm_hour != 0) && (tm.tm_hour != 12)) {
+ res = wait_file(chan, ints, "digits/pt-da", lang);
+ if (!res) {
+ if ((tm.tm_hour >= 0) && (tm.tm_hour < 12))
+ res = wait_file(chan, ints, "digits/morning", lang);
+ else if ((tm.tm_hour >= 12) && (tm.tm_hour < 18))
+ res = wait_file(chan, ints, "digits/afternoon", lang);
+ else res = wait_file(chan, ints, "digits/night", lang);
+ }
+ }
+ } else {
+ if (tm.tm_hour > 12)
+ res = wait_file(chan, ints, "digits/p-m", lang);
+ else if (tm.tm_hour && tm.tm_hour < 12)
+ res = wait_file(chan, ints, "digits/a-m", lang);
+ }
+ break;
+ case 'Q':
+ /* Shorthand for "Today", "Yesterday", or ABdY */
+ /* XXX As emphasized elsewhere, this should the native way in your
+ * language to say the date, with changes in what you say, depending
+ * upon how recent the date is. XXX */
+ {
+ struct timeval now = ast_tvnow();
+ struct ast_tm tmnow;
+ time_t beg_today;
+
+ ast_localtime(&now, &tmnow, timezone);
+ /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
+ /* In any case, it saves not having to do ast_mktime() */
+ beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
+ if (beg_today < time) {
+ /* Today */
+ res = wait_file(chan,ints, "digits/today",lang);
+ } else if (beg_today - 86400 < time) {
+ /* Yesterday */
+ res = wait_file(chan,ints, "digits/yesterday",lang);
+ } else {
+ res = ast_say_date_with_format_pt(chan, time, ints, lang, "Ad 'digits/pt-de' B 'digits/pt-de' Y", timezone);
+ }
+ }
+ break;
+ case 'q':
+ /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
+ /* XXX As emphasized elsewhere, this should the native way in your
+ * language to say the date, with changes in what you say, depending
+ * upon how recent the date is. XXX */
+ {
+ struct timeval now = ast_tvnow();
+ struct ast_tm tmnow;
+ time_t beg_today;
+
+ ast_localtime(&now, &tmnow, timezone);
+ /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
+ /* In any case, it saves not having to do ast_mktime() */
+ beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
+ if (beg_today < time) {
+ /* Today */
+ } else if ((beg_today - 86400) < time) {
+ /* Yesterday */
+ res = wait_file(chan,ints, "digits/yesterday",lang);
+ } else if (beg_today - 86400 * 6 < time) {
+ /* Within the last week */
+ res = ast_say_date_with_format_pt(chan, time, ints, lang, "A", timezone);
+ } else {
+ res = ast_say_date_with_format_pt(chan, time, ints, lang, "Ad 'digits/pt-de' B 'digits/pt-de' Y", timezone);
+ }
+ }
+ break;
+ case 'R':
+ res = ast_say_date_with_format_pt(chan, time, ints, lang, "H 'digits/pt-e' M", timezone);
+ break;
+ case 'S':
+ /* Seconds */
+ if (!strcasecmp(lang, "pt_BR")) {
+ res = ast_say_number(chan, tm.tm_sec, ints, lang, NULL);
+ if (!res) {
+ if (tm.tm_sec > 1) {
+ res = wait_file(chan,ints,"digits/seconds",lang);
+ } else {
+ res = wait_file(chan,ints,"digits/second",lang);
+ }
+ }
+ } else {
+ if (tm.tm_sec == 0) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
+ res = wait_file(chan,ints,nextmsg,lang);
+ } else if (tm.tm_sec < 10) {
+ res = wait_file(chan,ints, "digits/oh",lang);
+ if (!res) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
+ res = wait_file(chan,ints,nextmsg,lang);
+ }
+ } else if ((tm.tm_sec < 21) || (tm.tm_sec % 10 == 0)) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
+ res = wait_file(chan,ints,nextmsg,lang);
+ } else {
+ int ten, one;
+ ten = (tm.tm_sec / 10) * 10;
+ one = (tm.tm_sec % 10);
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", ten);
+ res = wait_file(chan,ints,nextmsg,lang);
+ if (!res) {
+ /* Fifty, not fifty-zero */
+ if (one != 0) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", one);
+ res = wait_file(chan,ints,nextmsg,lang);
+ }
+ }
+ }
+ }
+ break;
+ case 'T':
+ res = ast_say_date_with_format_pt(chan, time, ints, lang, "HMS", timezone);
+ break;
+ case ' ':
+ case ' ':
+ /* Just ignore spaces and tabs */
+ break;
+ default:
+ /* Unknown character */
+ ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
+ }
+ /* Jump out on DTMF */
+ if (res) {
+ break;
+ }
+ }
+ return res;
+}
+
+/* Taiwanese / Chinese syntax */
+int ast_say_date_with_format_tw(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
+{
+ struct timeval tv = { time, 0 };
+ struct ast_tm tm;
+ int res=0, offset, sndoffset;
+ char sndfile[256], nextmsg[256];
+
+ if (format == NULL)
+ format = "YBdAkM";
+
+ ast_localtime(&tv, &tm, timezone);
+
+ for (offset=0 ; format[offset] != '\0' ; offset++) {
+ ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
+ switch (format[offset]) {
+ /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
+ case '\'':
+ /* Literal name of a sound file */
+ sndoffset=0;
+ for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
+ sndfile[sndoffset] = format[offset];
+ sndfile[sndoffset] = '\0';
+ res = wait_file(chan,ints,sndfile,lang);
+ break;
+ case 'A':
+ case 'a':
+ /* Sunday - Saturday */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'B':
+ case 'b':
+ case 'h':
+ case 'm':
+ /* January - December */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'd':
+ case 'e':
+ /* First - Thirtyfirst */
+ if (!(tm.tm_mday % 10) || (tm.tm_mday < 10)) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_mday);
+ res = wait_file(chan,ints,nextmsg,lang);
+ } else {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_mday - (tm.tm_mday % 10));
+ res = wait_file(chan,ints,nextmsg,lang);
+ if (!res) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_mday % 10);
+ res = wait_file(chan,ints,nextmsg,lang);
+ }
+ }
+ if (!res) res = wait_file(chan,ints,"digits/day",lang);
+ break;
+ case 'Y':
+ /* Year */
+ if (tm.tm_year > 99) {
+ res = wait_file(chan,ints, "digits/2",lang);
+ if (!res) {
+ res = wait_file(chan,ints, "digits/thousand",lang);
+ }
+ if (tm.tm_year > 100) {
+ if (!res) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", (tm.tm_year - 100) / 10);
+ res = wait_file(chan,ints,nextmsg,lang);
+ if (!res) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", (tm.tm_year - 100) % 10);
+ res = wait_file(chan,ints,nextmsg,lang);
+ }
+ }
+ }
+ if (!res) {
+ res = wait_file(chan,ints, "digits/year",lang);
+ }
+ } else {
+ if (tm.tm_year < 1) {
+ /* I'm not going to handle 1900 and prior */
+ /* We'll just be silent on the year, instead of bombing out. */
+ } else {
+ res = wait_file(chan,ints, "digits/1",lang);
+ if (!res) {
+ res = wait_file(chan,ints, "digits/9",lang);
+ }
+ if (!res) {
+ if (tm.tm_year <= 9) {
+ /* 1901 - 1909 */
+ res = wait_file(chan,ints, "digits/0",lang);
+ if (!res) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year);
+ res = wait_file(chan,ints,nextmsg,lang);
+ }
+ } else {
+ /* 1910 - 1999 */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year / 10);
+ res = wait_file(chan,ints,nextmsg,lang);
+ if (!res) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_year % 10);
+ res = wait_file(chan,ints,nextmsg,lang);
+ }
+ }
+ }
+ }
+ if (!res) {
+ res = wait_file(chan,ints, "digits/year",lang);
+ }
+ }
+ break;
+ case 'I':
+ case 'l':
+ /* 12-Hour */
+ if (tm.tm_hour == 0)
+ ast_copy_string(nextmsg, "digits/12", sizeof(nextmsg));
+ else if (tm.tm_hour > 12)
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - 12);
+ else
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
+ res = wait_file(chan,ints,nextmsg,lang);
+ if (!res) {
+ res = wait_file(chan,ints, "digits/oclock",lang);
+ }
+ break;
+ case 'H':
+ if (tm.tm_hour < 10) {
+ res = wait_file(chan, ints, "digits/0", lang);
+ }
+ case 'k':
+ /* 24-Hour */
+ if (!(tm.tm_hour % 10) || tm.tm_hour < 10) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour);
+ res = wait_file(chan,ints,nextmsg,lang);
+ } else {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour - (tm.tm_hour % 10));
+ res = wait_file(chan,ints,nextmsg,lang);
+ if (!res) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_hour % 10);
+ res = wait_file(chan,ints,nextmsg,lang);
+ }
+ }
+ if (!res) {
+ res = wait_file(chan,ints, "digits/oclock",lang);
+ }
+ break;
+ case 'M':
+ /* Minute */
+ if (!(tm.tm_min % 10) || tm.tm_min < 10) {
+ if (tm.tm_min < 10) {
+ res = wait_file(chan, ints, "digits/0", lang);
+ }
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min);
+ res = wait_file(chan,ints,nextmsg,lang);
+ } else {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min - (tm.tm_min % 10));
+ res = wait_file(chan,ints,nextmsg,lang);
+ if (!res) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_min % 10);
+ res = wait_file(chan,ints,nextmsg,lang);
+ }
+ }
+ if (!res) {
+ res = wait_file(chan,ints, "digits/minute",lang);
+ }
+ break;
+ case 'P':
+ case 'p':
+ /* AM/PM */
+ if (tm.tm_hour > 11)
+ ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
+ else
+ ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'Q':
+ /* Shorthand for "Today", "Yesterday", or ABdY */
+ /* XXX As emphasized elsewhere, this should the native way in your
+ * language to say the date, with changes in what you say, depending
+ * upon how recent the date is. XXX */
+ {
+ struct timeval now = ast_tvnow();
+ struct ast_tm tmnow;
+ time_t beg_today;
+
+ ast_localtime(&now, &tmnow, timezone);
+ /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
+ /* In any case, it saves not having to do ast_mktime() */
+ beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
+ if (beg_today < time) {
+ /* Today */
+ res = wait_file(chan,ints, "digits/today",lang);
+ } else if (beg_today - 86400 < time) {
+ /* Yesterday */
+ res = wait_file(chan,ints, "digits/yesterday",lang);
+ } else {
+ res = ast_say_date_with_format_tw(chan, time, ints, lang, "YBdA", timezone);
+ }
+ }
+ break;
+ case 'q':
+ /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
+ /* XXX As emphasized elsewhere, this should the native way in your
+ * language to say the date, with changes in what you say, depending
+ * upon how recent the date is. XXX */
+ {
+ struct timeval now = ast_tvnow();
+ struct ast_tm tmnow;
+ time_t beg_today;
+
+ ast_localtime(&now, &tmnow, timezone);
+ /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
+ /* In any case, it saves not having to do ast_mktime() */
+ beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
+ if (beg_today < time) {
+ /* Today */
+ } else if ((beg_today - 86400) < time) {
+ /* Yesterday */
+ res = wait_file(chan,ints, "digits/yesterday",lang);
+ } else if (beg_today - 86400 * 6 < time) {
+ /* Within the last week */
+ res = ast_say_date_with_format_tw(chan, time, ints, lang, "A", timezone);
+ } else {
+ res = ast_say_date_with_format_tw(chan, time, ints, lang, "YBdA", timezone);
+ }
+ }
+ break;
+ case 'R':
+ res = ast_say_date_with_format_tw(chan, time, ints, lang, "kM", timezone);
+ break;
+ case 'S':
+ /* Seconds */
+ if (!(tm.tm_sec % 10) || tm.tm_sec < 10) {
+ if (tm.tm_sec < 10) {
+ res = wait_file(chan, ints, "digits/0", lang);
+ }
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec);
+ res = wait_file(chan,ints,nextmsg,lang);
+ } else {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec - (tm.tm_sec % 10));
+ res = wait_file(chan,ints,nextmsg,lang);
+ if (!res) {
+ snprintf(nextmsg,sizeof(nextmsg), "digits/%d", tm.tm_sec % 10);
+ res = wait_file(chan,ints,nextmsg,lang);
+ }
+ }
+ if (!res) {
+ res = wait_file(chan,ints, "digits/second",lang);
+ }
+ break;
+ case 'T':
+ res = ast_say_date_with_format_tw(chan, time, ints, lang, "HMS", timezone);
+ break;
+ case ' ':
+ case ' ':
+ /* Just ignore spaces and tabs */
+ break;
+ default:
+ /* Unknown character */
+ ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
+ }
+ /* Jump out on DTMF */
+ if (res) {
+ break;
+ }
+ }
+ return res;
+}
+
+static int say_time(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ if (!strcasecmp(lang, "en") ) { /* English syntax */
+ return(ast_say_time_en(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "de") ) { /* German syntax */
+ return(ast_say_time_de(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "fr") ) { /* French syntax */
+ return(ast_say_time_fr(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "hu") ) { /* Hungarian syntax */
+ return(ast_say_time_hu(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
+ return(ast_say_time_nl(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "pt") ) { /* Portuguese syntax */
+ return(ast_say_time_pt(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "pt_BR") ) { /* Brazilian Portuguese syntax */
+ return(ast_say_time_pt_BR(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "tw") ) { /* Taiwanese syntax */
+ } else if (!strcasecmp(lang, "tw") || !strcasecmp(lang, "zh") ) { /* Taiwanese / Chinese syntax */
+ return(ast_say_time_tw(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "gr") ) { /* Greek syntax */
+ return(ast_say_time_gr(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "th") ) {
+ return(ast_say_time_th(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "ge") ) { /* Georgian syntax */
+ return(ast_say_time_ge(chan, t, ints, lang));
+ }
+
+ /* Default to English */
+ return(ast_say_time_en(chan, t, ints, lang));
+}
+
+/* English syntax */
+int ast_say_time_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ int res = 0;
+ int hour, pm=0;
+
+ ast_localtime(&tv, &tm, NULL);
+ hour = tm.tm_hour;
+ if (!hour)
+ hour = 12;
+ else if (hour == 12)
+ pm = 1;
+ else if (hour > 12) {
+ hour -= 12;
+ pm = 1;
+ }
+ if (!res)
+ res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
+
+ if (tm.tm_min > 9) {
+ if (!res)
+ res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
+ } else if (tm.tm_min) {
+ if (!res)
+ res = ast_streamfile(chan, "digits/oh", lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ if (!res)
+ res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
+ } else {
+ if (!res)
+ res = ast_streamfile(chan, "digits/oclock", lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (pm) {
+ if (!res)
+ res = ast_streamfile(chan, "digits/p-m", lang);
+ } else {
+ if (!res)
+ res = ast_streamfile(chan, "digits/a-m", lang);
+ }
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ return res;
+}
+
+/* German syntax */
+int ast_say_time_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ int res = 0;
+
+ ast_localtime(&tv, &tm, NULL);
+ if (!res)
+ res = ast_say_number(chan, tm.tm_hour, ints, lang, "n");
+ if (!res)
+ res = ast_streamfile(chan, "digits/oclock", lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ if (!res)
+ if (tm.tm_min > 0)
+ res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
+ return res;
+}
+
+/* Hungarian syntax */
+int ast_say_time_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ int res = 0;
+
+ ast_localtime(&tv, &tm, NULL);
+ if (!res)
+ res = ast_say_number(chan, tm.tm_hour, ints, lang, "n");
+ if (!res)
+ res = ast_streamfile(chan, "digits/oclock", lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ if (!res)
+ if (tm.tm_min > 0) {
+ res = ast_say_number(chan, tm.tm_min, ints, lang, "f");
+ if (!res)
+ res = ast_streamfile(chan, "digits/minute", lang);
+ }
+ return res;
+}
+
+/* French syntax */
+int ast_say_time_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ int res = 0;
+
+ ast_localtime(&tv, &tm, NULL);
+
+ res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
+ if (!res)
+ res = ast_streamfile(chan, "digits/oclock", lang);
+ if (tm.tm_min) {
+ if (!res)
+ res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
+ }
+ return res;
+}
+
+/* Dutch syntax */
+int ast_say_time_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ int res = 0;
+
+ ast_localtime(&tv, &tm, NULL);
+ if (!res)
+ res = ast_say_number(chan, tm.tm_hour, ints, lang, (char *) NULL);
+ if (!res)
+ res = ast_streamfile(chan, "digits/nl-uur", lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ if (!res)
+ if (tm.tm_min > 0)
+ res = ast_say_number(chan, tm.tm_min, ints, lang, NULL);
+ return res;
+}
+
+/* Portuguese syntax */
+int ast_say_time_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ int res = 0;
+ int hour;
+
+ ast_localtime(&tv, &tm, NULL);
+ hour = tm.tm_hour;
+ if (!res)
+ res = ast_say_number(chan, hour, ints, lang, "f");
+ if (tm.tm_min) {
+ if (!res)
+ res = wait_file(chan, ints, "digits/pt-e", lang);
+ if (!res)
+ res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
+ } else {
+ if (!res)
+ res = wait_file(chan, ints, "digits/pt-hora", lang);
+ if (tm.tm_hour != 1)
+ if (!res)
+ res = wait_file(chan, ints, "digits/pt-sss", lang);
+ }
+ if (!res)
+ res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
+ return res;
+}
+
+/* Brazilian Portuguese syntax */
+int ast_say_time_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ int res = 0;
+
+ ast_localtime(&tv, &tm, NULL);
+
+ res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
+ if (!res) {
+ if (tm.tm_hour > 1)
+ res = wait_file(chan, ints, "digits/hours", lang);
+ else
+ res = wait_file(chan, ints, "digits/hour", lang);
+ }
+ if ((!res) && (tm.tm_min)) {
+ res = wait_file(chan, ints, "digits/pt-e", lang);
+ if (!res)
+ res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
+ if (!res) {
+ if (tm.tm_min > 1)
+ res = wait_file(chan, ints, "digits/minutes", lang);
+ else
+ res = wait_file(chan, ints, "digits/minute", lang);
+ }
+ }
+ return res;
+}
+
+/* Thai syntax */
+int ast_say_time_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ int res = 0;
+ int hour;
+ ast_localtime(&tv, &tm, NULL);
+ hour = tm.tm_hour;
+ if (!hour)
+ hour = 24;
+ if (!res)
+ res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
+ if (!res)
+ res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
+ return res;
+}
+
+/* Taiwanese / Chinese syntax */
+int ast_say_time_tw(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ int res = 0;
+ int hour, pm=0;
+
+ ast_localtime(&tv, &tm, NULL);
+ hour = tm.tm_hour;
+ if (!hour)
+ hour = 12;
+ else if (hour == 12)
+ pm = 1;
+ else if (hour > 12) {
+ hour -= 12;
+ pm = 1;
+ }
+ if (pm) {
+ if (!res)
+ res = ast_streamfile(chan, "digits/p-m", lang);
+ } else {
+ if (!res)
+ res = ast_streamfile(chan, "digits/a-m", lang);
+ }
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ if (!res)
+ res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
+ if (!res)
+ res = ast_streamfile(chan, "digits/oclock", lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ if (!res)
+ res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
+ if (!res)
+ res = ast_streamfile(chan, "digits/minute", lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ return res;
+}
+
+static int say_datetime(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ if (!strcasecmp(lang, "en") ) { /* English syntax */
+ return(ast_say_datetime_en(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "de") ) { /* German syntax */
+ return(ast_say_datetime_de(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "fr") ) { /* French syntax */
+ return(ast_say_datetime_fr(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "nl") ) { /* Dutch syntax */
+ return(ast_say_datetime_nl(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "hu") ) { /* Hungarian syntax */
+ return(ast_say_datetime_hu(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "pt") ) { /* Portuguese syntax */
+ return(ast_say_datetime_pt(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "pt_BR") ) { /* Brazilian Portuguese syntax */
+ return(ast_say_datetime_pt_BR(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "tw") || !strcasecmp(lang, "zh") ) { /* Taiwanese / Chinese syntax */
+ return(ast_say_datetime_tw(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "gr") ) { /* Greek syntax */
+ return(ast_say_datetime_gr(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "th") ) { /* Thai syntax */
+ return(ast_say_datetime_th(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "ge") ) { /* Georgian syntax */
+ return(ast_say_datetime_ge(chan, t, ints, lang));
+ }
+
+ /* Default to English */
+ return(ast_say_datetime_en(chan, t, ints, lang));
+}
+
+/* English syntax */
+int ast_say_datetime_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ char fn[256];
+ int res = 0;
+ int hour, pm=0;
+
+ ast_localtime(&tv, &tm, NULL);
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (!res)
+ res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
+
+ hour = tm.tm_hour;
+ if (!hour)
+ hour = 12;
+ else if (hour == 12)
+ pm = 1;
+ else if (hour > 12) {
+ hour -= 12;
+ pm = 1;
+ }
+ if (!res)
+ res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
+
+ if (tm.tm_min > 9) {
+ if (!res)
+ res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
+ } else if (tm.tm_min) {
+ if (!res)
+ res = ast_streamfile(chan, "digits/oh", lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ if (!res)
+ res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
+ } else {
+ if (!res)
+ res = ast_streamfile(chan, "digits/oclock", lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (pm) {
+ if (!res)
+ res = ast_streamfile(chan, "digits/p-m", lang);
+ } else {
+ if (!res)
+ res = ast_streamfile(chan, "digits/a-m", lang);
+ }
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ if (!res)
+ res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
+ return res;
+}
+
+/* German syntax */
+int ast_say_datetime_de(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ int res = 0;
+
+ ast_localtime(&tv, &tm, NULL);
+ res = ast_say_date(chan, t, ints, lang);
+ if (!res)
+ ast_say_time(chan, t, ints, lang);
+ return res;
+
+}
+
+/* Hungarian syntax */
+int ast_say_datetime_hu(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ int res = 0;
+
+ ast_localtime(&tv, &tm, NULL);
+ res = ast_say_date(chan, t, ints, lang);
+ if (!res)
+ ast_say_time(chan, t, ints, lang);
+ return res;
+}
+
+/* French syntax */
+int ast_say_datetime_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ char fn[256];
+ int res = 0;
+
+ ast_localtime(&tv, &tm, NULL);
+
+ if (!res)
+ res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
+
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+
+ if (!res)
+ res = ast_say_number(chan, tm.tm_hour, ints, lang, "f");
+ if (!res)
+ res = ast_streamfile(chan, "digits/oclock", lang);
+ if (tm.tm_min > 0) {
+ if (!res)
+ res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
+ }
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ if (!res)
+ res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
+ return res;
+}
+
+/* Dutch syntax */
+int ast_say_datetime_nl(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ int res = 0;
+
+ ast_localtime(&tv, &tm, NULL);
+ res = ast_say_date(chan, t, ints, lang);
+ if (!res) {
+ res = ast_streamfile(chan, "digits/nl-om", lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (!res)
+ ast_say_time(chan, t, ints, lang);
+ return res;
+}
+
+/* Portuguese syntax */
+int ast_say_datetime_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ char fn[256];
+ int res = 0;
+ int hour, pm=0;
+
+ ast_localtime(&tv, &tm, NULL);
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (!res)
+ res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
+
+ hour = tm.tm_hour;
+ if (!hour)
+ hour = 12;
+ else if (hour == 12)
+ pm = 1;
+ else if (hour > 12) {
+ hour -= 12;
+ pm = 1;
+ }
+ if (!res)
+ res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
+
+ if (tm.tm_min > 9) {
+ if (!res)
+ res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
+ } else if (tm.tm_min) {
+ if (!res)
+ res = ast_streamfile(chan, "digits/oh", lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ if (!res)
+ res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
+ } else {
+ if (!res)
+ res = ast_streamfile(chan, "digits/oclock", lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (pm) {
+ if (!res)
+ res = ast_streamfile(chan, "digits/p-m", lang);
+ } else {
+ if (!res)
+ res = ast_streamfile(chan, "digits/a-m", lang);
+ }
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ if (!res)
+ res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
+ return res;
+}
+
+/* Brazilian Portuguese syntax */
+int ast_say_datetime_pt_BR(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ int res = 0;
+
+ ast_localtime(&tv, &tm, NULL);
+ res = ast_say_date(chan, t, ints, lang);
+ if (!res)
+ res = ast_say_time(chan, t, ints, lang);
+ return res;
+}
+
+/* Thai syntax */
+int ast_say_datetime_th(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ char fn[256];
+ int res = 0;
+ int hour;
+ ast_localtime(&tv, &tm, NULL);
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (!res){
+ ast_copy_string(fn, "digits/posor", sizeof(fn));
+ res = ast_streamfile(chan, fn, lang);
+ res = ast_say_number(chan, tm.tm_year + 1900 + 543, ints, lang, (char *) NULL);
+ }
+ if (!res)
+ res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
+
+ hour = tm.tm_hour;
+ if (!hour)
+ hour = 24;
+ if (!res){
+ ast_copy_string(fn, "digits/wela", sizeof(fn));
+ res = ast_streamfile(chan, fn, lang);
+ }
+ if (!res)
+ res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
+ if (!res)
+ res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
+ return res;
+}
+
+/* Taiwanese / Chinese syntax */
+int ast_say_datetime_tw(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ char fn[256];
+ int res = 0;
+ int hour, pm=0;
+
+ ast_localtime(&tv, &tm, NULL);
+ if (!res)
+ res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (!res)
+ res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+
+ hour = tm.tm_hour;
+ if (!hour)
+ hour = 12;
+ else if (hour == 12)
+ pm = 1;
+ else if (hour > 12) {
+ hour -= 12;
+ pm = 1;
+ }
+ if (pm) {
+ if (!res)
+ res = ast_streamfile(chan, "digits/p-m", lang);
+ } else {
+ if (!res)
+ res = ast_streamfile(chan, "digits/a-m", lang);
+ }
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ if (!res)
+ res = ast_say_number(chan, hour, ints, lang, (char *) NULL);
+ if (!res)
+ res = ast_streamfile(chan, "digits/oclock", lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ if (!res)
+ res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
+ if (!res)
+ res = ast_streamfile(chan, "digits/minute", lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ return res;
+}
+
+static int say_datetime_from_now(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ if (!strcasecmp(lang, "en") ) { /* English syntax */
+ return(ast_say_datetime_from_now_en(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "fr") ) { /* French syntax */
+ return(ast_say_datetime_from_now_fr(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "pt") || !strcasecmp(lang, "pt_BR")) { /* Portuguese syntax */
+ return(ast_say_datetime_from_now_pt(chan, t, ints, lang));
+ } else if (!strcasecmp(lang, "ge") ) { /* Georgian syntax */
+ return(ast_say_datetime_from_now_ge(chan, t, ints, lang));
+ }
+
+ /* Default to English */
+ return(ast_say_datetime_from_now_en(chan, t, ints, lang));
+}
+
+/* English syntax */
+int ast_say_datetime_from_now_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ int res=0;
+ struct timeval nowtv = ast_tvnow(), tv = { t, 0 };
+ int daydiff;
+ struct ast_tm tm;
+ struct ast_tm now;
+ char fn[256];
+
+ ast_localtime(&tv, &tm, NULL);
+ ast_localtime(&nowtv, &now, NULL);
+ daydiff = now.tm_yday - tm.tm_yday;
+ if ((daydiff < 0) || (daydiff > 6)) {
+ /* Day of month and month */
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (!res)
+ res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
+
+ } else if (daydiff) {
+ /* Just what day of the week */
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ } /* Otherwise, it was today */
+ if (!res)
+ res = ast_say_time(chan, t, ints, lang);
+ return res;
+}
+
+/* French syntax */
+int ast_say_datetime_from_now_fr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ int res=0;
+ struct timeval nowtv = ast_tvnow(), tv = { t, 0 };
+ int daydiff;
+ struct ast_tm tm;
+ struct ast_tm now;
+ char fn[256];
+
+ ast_localtime(&tv, &tm, NULL);
+ ast_localtime(&nowtv, &now, NULL);
+ daydiff = now.tm_yday - tm.tm_yday;
+ if ((daydiff < 0) || (daydiff > 6)) {
+ /* Day of month and month */
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (!res)
+ res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
+
+ } else if (daydiff) {
+ /* Just what day of the week */
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ } /* Otherwise, it was today */
+ if (!res)
+ res = ast_say_time(chan, t, ints, lang);
+ return res;
+}
+
+/* Portuguese syntax */
+int ast_say_datetime_from_now_pt(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ int res=0;
+ int daydiff;
+ struct ast_tm tm;
+ struct ast_tm now;
+ struct timeval nowtv = ast_tvnow(), tv = { t, 0 };
+ char fn[256];
+
+ ast_localtime(&tv, &tm, NULL);
+ ast_localtime(&nowtv, &now, NULL);
+ daydiff = now.tm_yday - tm.tm_yday;
+ if ((daydiff < 0) || (daydiff > 6)) {
+ /* Day of month and month */
+ if (!res)
+ res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
+ if (!res)
+ res = wait_file(chan, ints, "digits/pt-de", lang);
+ snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
+ if (!res)
+ res = wait_file(chan, ints, fn, lang);
+
+ } else if (daydiff) {
+ /* Just what day of the week */
+ snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
+ if (!res)
+ res = wait_file(chan, ints, fn, lang);
+ } /* Otherwise, it was today */
+ if (!strcasecmp(lang, "pt_BR")) {
+ if (tm.tm_hour > 1) {
+ ast_copy_string(fn, "digits/pt-as", sizeof(fn));
+ } else {
+ ast_copy_string(fn, "digits/pt-a", sizeof(fn));
+ }
+ if (!res)
+ res = wait_file(chan, ints, fn, lang);
+ } else {
+ ast_copy_string(fn, "digits/pt-ah", sizeof(fn));
+ if (!res)
+ res = wait_file(chan, ints, fn, lang);
+ if (tm.tm_hour != 1)
+ if (!res)
+ res = wait_file(chan, ints, "digits/pt-sss", lang);
+ if (!res)
+ res = ast_say_time(chan, t, ints, lang);
+ }
+ return res;
+}
+
+
+/*********************************** GREEK SUPPORT ***************************************/
+
+
+/*
+ * digits/female-[1..4] : "Mia, dyo , treis, tessereis"
+ */
+static int gr_say_number_female(int num, struct ast_channel *chan, const char *ints, const char *lang){
+ int tmp;
+ int left;
+ int res;
+ char fn[256] = "";
+
+ /* ast_debug(1, "\n\n Saying number female %s %d \n\n",lang, num); */
+ if (num < 5) {
+ snprintf(fn, sizeof(fn), "digits/female-%d", num);
+ res = wait_file(chan, ints, fn, lang);
+ } else if (num < 13) {
+ res = ast_say_number(chan, num, ints, lang, (char *) NULL);
+ } else if (num <100 ) {
+ tmp = (num/10) * 10;
+ left = num - tmp;
+ snprintf(fn, sizeof(fn), "digits/%d", tmp);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ if (left)
+ gr_say_number_female(left, chan, ints, lang);
+
+ } else {
+ return -1;
+ }
+ return res;
+}
+
+
+
+/*
+ * A list of the files that you need to create
+ -> digits/xilia = "xilia"
+ -> digits/myrio = "ekatomyrio"
+ -> digits/thousands = "xiliades"
+ -> digits/millions = "ektatomyria"
+ -> digits/[1..12] :: A pronunciation of th digits form 1 to 12 e.g. "tria"
+ -> digits/[10..100] :: A pronunciation of the tens from 10 to 90
+ e,g 80 = "ogdonta"
+ Here we must note that we use digits/tens/100 to utter "ekato"
+ and digits/hundred-100 to utter "ekaton"
+ -> digits/hundred-[100...1000] :: A pronunciation of hundreds from 100 to 1000 e.g 400 =
+ "terakosia". Here again we use hundreds/1000 for "xilia"
+ and digits/thousnds for "xiliades"
+*/
+
+static int ast_say_number_full_gr(struct ast_channel *chan, int num, const char *ints, const char *language,int audiofd, int ctrlfd)
+{
+ int res = 0;
+ char fn[256] = "";
+ int i=0;
+
+
+ if (!num) {
+ ast_copy_string(fn, "digits/0", sizeof(fn));
+ res = ast_streamfile(chan, fn, chan->language);
+ if (!res)
+ return ast_waitstream(chan, ints);
+ }
+
+ while (!res && num ) {
+ i++;
+ if (num < 13) {
+ snprintf(fn, sizeof(fn), "digits/%d", num);
+ num = 0;
+ } else if (num <= 100) {
+ /* 13 < num <= 100 */
+ snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10);
+ num %= 10;
+ } else if (num < 200) {
+ /* 100 < num < 200 */
+ snprintf(fn, sizeof(fn), "digits/hundred-100");
+ num %= 100;
+ } else if (num < 1000) {
+ /* 200 < num < 1000 */
+ snprintf(fn, sizeof(fn), "digits/hundred-%d", (num/100)*100);
+ num %= 100;
+ } else if (num < 2000){
+ snprintf(fn, sizeof(fn), "digits/xilia");
+ num %= 1000;
+ } else {
+ /* num > 1000 */
+ if (num < 1000000) {
+ res = ast_say_number_full_gr(chan, (num / 1000), ints, chan->language, audiofd, ctrlfd);
+ if (res)
+ return res;
+ num %= 1000;
+ snprintf(fn, sizeof(fn), "digits/thousands");
+ } else {
+ if (num < 1000000000) { /* 1,000,000,000 */
+ res = ast_say_number_full_gr(chan, (num / 1000000), ints, chan->language ,audiofd, ctrlfd);
+ if (res)
+ return res;
+ num %= 1000000;
+ snprintf(fn, sizeof(fn), "digits/millions");
+ } else {
+ ast_debug(1, "Number '%d' is too big for me\n", num);
+ res = -1;
+ }
+ }
+ }
+ if (!res) {
+ if (!ast_streamfile(chan, fn, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ res = ast_waitstream(chan, ints);
+ }
+ ast_stopstream(chan);
+ }
+ }
+ return res;
+}
+
+
+/*
+ * The format is weekday - day - month -year
+ *
+ * A list of the files that you need to create
+ * digits/day-[1..7] : "Deytera .. Paraskeyh"
+ * digits/months/1..12 : "Ianouariou .. Dekembriou"
+ Attention the months are in
+ "gekinh klhsh"
+ */
+
+
+static int ast_say_date_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct ast_tm tm;
+ struct timeval tv = { t, 0 };
+
+ char fn[256];
+ int res = 0;
+
+
+ ast_localtime(&tv, &tm, NULL);
+ /* W E E K - D A Y */
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ /* D A Y */
+ if (!res) {
+ gr_say_number_female(tm.tm_mday, chan, ints, lang);
+ }
+ /* M O N T H */
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ /* Y E A R */
+ if (!res)
+ res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
+ return res;
+}
+
+
+
+/* A list of the files that you need to create
+ * digits/female/1..4 : "Mia, dyo , treis, tesseris "
+ * digits/kai : "KAI"
+ * didgits : "h wra"
+ * digits/p-m : "meta meshmbrias"
+ * digits/a-m : "pro meshmbrias"
+ */
+
+static int ast_say_time_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ int res = 0;
+ int hour, pm=0;
+
+ ast_localtime(&tv, &tm, NULL);
+ hour = tm.tm_hour;
+
+ if (!hour)
+ hour = 12;
+ else if (hour == 12)
+ pm = 1;
+ else if (hour > 12) {
+ hour -= 12;
+ pm = 1;
+ }
+
+ res = gr_say_number_female(hour, chan, ints, lang);
+ if (tm.tm_min) {
+ if (!res)
+ res = ast_streamfile(chan, "digits/kai", lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ if (!res)
+ res = ast_say_number(chan, tm.tm_min, ints, lang, (char *) NULL);
+ } else {
+ if (!res)
+ res = ast_streamfile(chan, "digits/hwra", lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ if (pm) {
+ if (!res)
+ res = ast_streamfile(chan, "digits/p-m", lang);
+ } else {
+ if (!res)
+ res = ast_streamfile(chan, "digits/a-m", lang);
+ }
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ return res;
+}
+
+
+
+static int ast_say_datetime_gr(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ char fn[256];
+ int res = 0;
+
+ ast_localtime(&tv, &tm, NULL);
+
+ /* W E E K - D A Y */
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ /* D A Y */
+ if (!res) {
+ gr_say_number_female(tm.tm_mday, chan, ints, lang);
+ }
+ /* M O N T H */
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+
+ res = ast_say_time_gr(chan, t, ints, lang);
+ return res;
+}
+
+static int ast_say_date_with_format_gr(struct ast_channel *chan, time_t time, const char *ints, const char *lang, const char *format, const char *timezone)
+{
+ struct timeval tv = { time, 0 };
+ struct ast_tm tm;
+ int res=0, offset, sndoffset;
+ char sndfile[256], nextmsg[256];
+
+ if (!format)
+ format = "AdBY 'digits/at' IMp";
+
+ ast_localtime(&tv, &tm, timezone);
+
+ for (offset=0 ; format[offset] != '\0' ; offset++) {
+ ast_debug(1, "Parsing %c (offset %d) in %s\n", format[offset], offset, format);
+ switch (format[offset]) {
+ /* NOTE: if you add more options here, please try to be consistent with strftime(3) */
+ case '\'':
+ /* Literal name of a sound file */
+ sndoffset=0;
+ for (sndoffset=0 ; (format[++offset] != '\'') && (sndoffset < 256) ; sndoffset++)
+ sndfile[sndoffset] = format[offset];
+ sndfile[sndoffset] = '\0';
+ res = wait_file(chan,ints,sndfile,lang);
+ break;
+ case 'A':
+ case 'a':
+ /* Sunday - Saturday */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/day-%d", tm.tm_wday);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'B':
+ case 'b':
+ case 'h':
+ /* January - December */
+ snprintf(nextmsg,sizeof(nextmsg), "digits/mon-%d", tm.tm_mon);
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'd':
+ case 'e':
+ /* first - thirtyfirst */
+ gr_say_number_female(tm.tm_mday, chan, ints, lang);
+ break;
+ case 'Y':
+ /* Year */
+
+ ast_say_number_full_gr(chan, 1900+tm.tm_year, ints, chan->language, -1, -1);
+ break;
+ case 'I':
+ case 'l':
+ /* 12-Hour */
+ if (tm.tm_hour == 0)
+ gr_say_number_female(12, chan, ints, lang);
+ else if (tm.tm_hour > 12)
+ gr_say_number_female(tm.tm_hour - 12, chan, ints, lang);
+ else
+ gr_say_number_female(tm.tm_hour, chan, ints, lang);
+ break;
+ case 'H':
+ case 'k':
+ /* 24-Hour */
+ gr_say_number_female(tm.tm_hour, chan, ints, lang);
+ break;
+ case 'M':
+ /* Minute */
+ if (tm.tm_min) {
+ if (!res)
+ res = ast_streamfile(chan, "digits/kai", lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ if (!res)
+ res = ast_say_number_full_gr(chan, tm.tm_min, ints, lang, -1, -1);
+ } else {
+ if (!res)
+ res = ast_streamfile(chan, "digits/oclock", lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ break;
+ case 'P':
+ case 'p':
+ /* AM/PM */
+ if (tm.tm_hour > 11)
+ ast_copy_string(nextmsg, "digits/p-m", sizeof(nextmsg));
+ else
+ ast_copy_string(nextmsg, "digits/a-m", sizeof(nextmsg));
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'Q':
+ /* Shorthand for "Today", "Yesterday", or ABdY */
+ /* XXX As emphasized elsewhere, this should the native way in your
+ * language to say the date, with changes in what you say, depending
+ * upon how recent the date is. XXX */
+ {
+ struct timeval now = ast_tvnow();
+ struct ast_tm tmnow;
+ time_t beg_today;
+
+ ast_localtime(&now, &tmnow, timezone);
+ /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
+ /* In any case, it saves not having to do ast_mktime() */
+ beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
+ if (beg_today < time) {
+ /* Today */
+ res = wait_file(chan,ints, "digits/today",lang);
+ } else if (beg_today - 86400 < time) {
+ /* Yesterday */
+ res = wait_file(chan,ints, "digits/yesterday",lang);
+ } else {
+ res = ast_say_date_with_format_gr(chan, time, ints, lang, "AdBY", timezone);
+ }
+ }
+ break;
+ case 'q':
+ /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
+ /* XXX As emphasized elsewhere, this should the native way in your
+ * language to say the date, with changes in what you say, depending
+ * upon how recent the date is. XXX */
+ {
+ struct timeval now = ast_tvnow();
+ struct ast_tm tmnow;
+ time_t beg_today;
+
+ ast_localtime(&now, &tmnow, timezone);
+ /* This might be slightly off, if we transcend a leap second, but never more off than 1 second */
+ /* In any case, it saves not having to do ast_mktime() */
+ beg_today = now.tv_sec - (tmnow.tm_hour * 3600) - (tmnow.tm_min * 60) - (tmnow.tm_sec);
+ if (beg_today < time) {
+ /* Today */
+ } else if ((beg_today - 86400) < time) {
+ /* Yesterday */
+ res = wait_file(chan,ints, "digits/yesterday",lang);
+ } else if (beg_today - 86400 * 6 < time) {
+ /* Within the last week */
+ res = ast_say_date_with_format_gr(chan, time, ints, lang, "A", timezone);
+ } else {
+ res = ast_say_date_with_format_gr(chan, time, ints, lang, "AdBY", timezone);
+ }
+ }
+ break;
+ case 'R':
+ res = ast_say_date_with_format_gr(chan, time, ints, lang, "HM", timezone);
+ break;
+ case 'S':
+ /* Seconds */
+ ast_copy_string(nextmsg, "digits/kai", sizeof(nextmsg));
+ res = wait_file(chan,ints,nextmsg,lang);
+ if (!res)
+ res = ast_say_number_full_gr(chan, tm.tm_sec, ints, lang, -1, -1);
+ if (!res)
+ ast_copy_string(nextmsg, "digits/seconds", sizeof(nextmsg));
+ res = wait_file(chan,ints,nextmsg,lang);
+ break;
+ case 'T':
+ res = ast_say_date_with_format_gr(chan, time, ints, lang, "HMS", timezone);
+ break;
+ case ' ':
+ case ' ':
+ /* Just ignore spaces and tabs */
+ break;
+ default:
+ /* Unknown character */
+ ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c at pos %d\n", format, format[offset], offset);
+ }
+ /* Jump out on DTMF */
+ if (res) {
+ break;
+ }
+ }
+ return res;
+}
+
+
+
+
+/*********************************** Georgian Support ***************************************/
+
+
+/*
+ Convert a number into a semi-localized string. Only for Georgian.
+ res must be of at least 256 bytes, preallocated.
+ The output corresponds to Georgian spoken numbers, so
+ it may be either converted to real words by applying a direct conversion
+ table, or played just by substituting the entities with played files.
+
+ Output may consist of the following tokens (separated by spaces):
+ 0, minus.
+ 1-9, 1_-9_. (erti, ori, sami, otxi, ... . erti, or, sam, otx, ...).
+ 10-19.
+ 20, 40, 60, 80, 20_, 40_, 60_, 80_. (oci, ormoci, ..., ocda, ormocda, ...).
+ 100, 100_, 200, 200_, ..., 900, 900_. (asi, as, orasi, oras, ...).
+ 1000, 1000_. (atasi, atas).
+ 1000000, 1000000_. (milioni, milion).
+ 1000000000, 1000000000_. (miliardi, miliard).
+
+ To be able to play the sounds, each of the above tokens needs
+ a corresponding sound file. (e.g. 200_.gsm).
+*/
+static char* ast_translate_number_ge(int num, char* res, int res_len)
+{
+ char buf[256];
+ int digit = 0;
+ int remainder = 0;
+
+
+ if (num < 0) {
+ strncat(res, "minus ", res_len - strlen(res) - 1);
+ if ( num > INT_MIN ) {
+ num = -num;
+ } else {
+ num = 0;
+ }
+ }
+
+
+ /* directly read the numbers */
+ if (num <= 20 || num == 40 || num == 60 || num == 80 || num == 100) {
+ snprintf(buf, sizeof(buf), "%d", num);
+ strncat(res, buf, res_len - strlen(res) - 1);
+ return res;
+ }
+
+
+ if (num < 40) { /* ocda... */
+ strncat(res, "20_ ", res_len - strlen(res) - 1);
+ return ast_translate_number_ge(num - 20, res, res_len);
+ }
+
+ if (num < 60) { /* ormocda... */
+ strncat(res, "40_ ", res_len - strlen(res) - 1);
+ return ast_translate_number_ge(num - 40, res, res_len);
+ }
+
+ if (num < 80) { /* samocda... */
+ strncat(res, "60_ ", res_len - strlen(res) - 1);
+ return ast_translate_number_ge(num - 60, res, res_len);
+ }
+
+ if (num < 100) { /* otxmocda... */
+ strncat(res, "80_ ", res_len - strlen(res) - 1);
+ return ast_translate_number_ge(num - 80, res, res_len);
+ }
+
+
+ if (num < 1000) { /* as, oras, samas, ..., cxraas. asi, orasi, ..., cxraasi. */
+ remainder = num % 100;
+ digit = (num - remainder) / 100;
+
+ if (remainder == 0) {
+ snprintf(buf, sizeof(buf), "%d", num);
+ strncat(res, buf, res_len - strlen(res) - 1);
+ return res;
+ } else {
+ snprintf(buf, sizeof(buf), "%d_ ", digit*100);
+ strncat(res, buf, res_len - strlen(res) - 1);
+ return ast_translate_number_ge(remainder, res, res_len);
+ }
+ }
+
+
+ if (num == 1000) {
+ strncat(res, "1000", res_len - strlen(res) - 1);
+ return res;
+ }
+
+
+ if (num < 1000000) {
+ remainder = num % 1000;
+ digit = (num - remainder) / 1000;
+
+ if (remainder == 0) {
+ ast_translate_number_ge(digit, res, res_len);
+ strncat(res, " 1000", res_len - strlen(res) - 1);
+ return res;
+ }
+
+ if (digit == 1) {
+ strncat(res, "1000_ ", res_len - strlen(res) - 1);
+ return ast_translate_number_ge(remainder, res, res_len);
+ }
+
+ ast_translate_number_ge(digit, res, res_len);
+ strncat(res, " 1000_ ", res_len - strlen(res) - 1);
+ return ast_translate_number_ge(remainder, res, res_len);
+
+ }
+
+
+ if (num == 1000000) {
+ strncat(res, "1 1000000", res_len - strlen(res) - 1);
+ return res;
+ }
+
+
+ if (num < 1000000000) {
+ remainder = num % 1000000;
+ digit = (num - remainder) / 1000000;
+
+ if (remainder == 0) {
+ ast_translate_number_ge(digit, res, res_len);
+ strncat(res, " 1000000", res_len - strlen(res) - 1);
+ return res;
+ }
+
+ ast_translate_number_ge(digit, res, res_len);
+ strncat(res, " 1000000_ ", res_len - strlen(res) - 1);
+ return ast_translate_number_ge(remainder, res, res_len);
+
+ }
+
+
+ if (num == 1000000000) {
+ strncat(res, "1 1000000000", res_len - strlen(res) - 1);
+ return res;
+ }
+
+
+ if (num > 1000000000) {
+ remainder = num % 1000000000;
+ digit = (num - remainder) / 1000000000;
+
+ if (remainder == 0) {
+ ast_translate_number_ge(digit, res, res_len);
+ strncat(res, " 1000000000", res_len - strlen(res) - 1);
+ return res;
+ }
+
+ ast_translate_number_ge(digit, res, res_len);
+ strncat(res, " 1000000000_ ", res_len - strlen(res) - 1);
+ return ast_translate_number_ge(remainder, res, res_len);
+
+ }
+
+ return res;
+
+}
+
+
+
+/*! \brief ast_say_number_full_ge: Georgian syntax */
+static int ast_say_number_full_ge(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd)
+{
+ int res = 0;
+ char fn[512] = "";
+ char* s = 0;
+ const char* remainder = fn;
+
+ if (!num)
+ return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd);
+
+
+ ast_translate_number_ge(num, fn, 512);
+
+
+
+ while (res == 0 && (s = strstr(remainder, " "))) {
+ size_t len = s - remainder;
+ char* new_string = ast_malloc(len + 1 + strlen("digits/"));
+
+ sprintf(new_string, "digits/");
+ strncat(new_string, remainder, len); /* we can't sprintf() it, it's not null-terminated. */
+/* new_string[len + strlen("digits/")] = '\0'; */
+
+ if (!ast_streamfile(chan, new_string, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ res = ast_waitstream(chan, ints);
+ }
+ ast_stopstream(chan);
+
+ ast_free(new_string);
+
+ remainder = s + 1; /* position just after the found space char. */
+ while (*remainder == ' ') /* skip multiple spaces */
+ remainder++;
+ }
+
+
+ /* the last chunk. */
+ if (res == 0 && *remainder) {
+
+ char* new_string = ast_malloc(strlen(remainder) + 1 + strlen("digits/"));
+ sprintf(new_string, "digits/%s", remainder);
+
+ if (!ast_streamfile(chan, new_string, language)) {
+ if ((audiofd > -1) && (ctrlfd > -1))
+ res = ast_waitstream_full(chan, ints, audiofd, ctrlfd);
+ else
+ res = ast_waitstream(chan, ints);
+ }
+ ast_stopstream(chan);
+
+ ast_free(new_string);
+
+ }
+
+
+ return res;
+
+}
+
+
+
+/*
+Georgian support for date/time requires the following files (*.gsm):
+
+mon-1, mon-2, ... (ianvari, tebervali, ...)
+day-1, day-2, ... (orshabati, samshabati, ...)
+saati_da
+tsuti
+tslis
+*/
+
+
+
+/* Georgian syntax. e.g. "oriatas xuti tslis 5 noemberi". */
+static int ast_say_date_ge(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ char fn[256];
+ int res = 0;
+ ast_localtime(&tv, &tm, NULL);
+
+ if (!res)
+ res = ast_say_number(chan, tm.tm_year + 1900, ints, lang, (char *) NULL);
+
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/tslis %d", tm.tm_wday);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+
+ if (!res) {
+ res = ast_say_number(chan, tm.tm_mday, ints, lang, (char * ) NULL);
+/* if (!res)
+ res = ast_waitstream(chan, ints);
+*/
+ }
+
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ return res;
+
+}
+
+
+
+
+
+/* Georgian syntax. e.g. "otxi saati da eqvsi tsuti" */
+static int ast_say_time_ge(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ int res = 0;
+
+ ast_localtime(&tv, &tm, NULL);
+
+ res = ast_say_number(chan, tm.tm_hour, ints, lang, (char*)NULL);
+ if (!res) {
+ res = ast_streamfile(chan, "digits/saati_da", lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+
+ if (tm.tm_min) {
+ if (!res) {
+ res = ast_say_number(chan, tm.tm_min, ints, lang, (char*)NULL);
+
+ if (!res) {
+ res = ast_streamfile(chan, "digits/tsuti", lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ }
+ }
+ return res;
+}
+
+
+
+/* Georgian syntax. Say date, then say time. */
+static int ast_say_datetime_ge(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ struct timeval tv = { t, 0 };
+ struct ast_tm tm;
+ int res = 0;
+
+ ast_localtime(&tv, &tm, NULL);
+ res = ast_say_date(chan, t, ints, lang);
+ if (!res)
+ ast_say_time(chan, t, ints, lang);
+ return res;
+
+}
+
+
+
+
+/* Georgian syntax */
+static int ast_say_datetime_from_now_ge(struct ast_channel *chan, time_t t, const char *ints, const char *lang)
+{
+ int res=0;
+ int daydiff;
+ struct ast_tm tm;
+ struct ast_tm now;
+ struct timeval tv = { t, 0 }, nowt = ast_tvnow();
+ char fn[256];
+
+ ast_localtime(&tv, &tm, NULL);
+ ast_localtime(&nowt, &now, NULL);
+ daydiff = now.tm_yday - tm.tm_yday;
+ if ((daydiff < 0) || (daydiff > 6)) {
+ /* Day of month and month */
+ if (!res)
+ res = ast_say_number(chan, tm.tm_mday, ints, lang, (char *) NULL);
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/mon-%d", tm.tm_mon);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+
+ } else if (daydiff) {
+ /* Just what day of the week */
+ if (!res) {
+ snprintf(fn, sizeof(fn), "digits/day-%d", tm.tm_wday);
+ res = ast_streamfile(chan, fn, lang);
+ if (!res)
+ res = ast_waitstream(chan, ints);
+ }
+ } /* Otherwise, it was today */
+ if (!res)
+ res = ast_say_time(chan, t, ints, lang);
+
+ return res;
+}
+
+
+
+/*
+ * remap the 'say' functions to use those in this file
+ */
+static void __attribute__((constructor)) __say_init(void)
+{
+ ast_say_number_full = say_number_full;
+ ast_say_enumeration_full = say_enumeration_full;
+ ast_say_digit_str_full = say_digit_str_full;
+ ast_say_character_str_full = say_character_str_full;
+ ast_say_phonetic_str_full = say_phonetic_str_full;
+ ast_say_datetime = say_datetime;
+ ast_say_time = say_time;
+ ast_say_date = say_date;
+ ast_say_datetime_from_now = say_datetime_from_now;
+ ast_say_date_with_format = say_date_with_format;
+}
diff --git a/trunk/main/sched.c b/trunk/main/sched.c
new file mode 100644
index 000000000..3aeeb9d82
--- /dev/null
+++ b/trunk/main/sched.c
@@ -0,0 +1,406 @@
+/*
+ * 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.
+ */
+
+/*! \file
+ *
+ * \brief Scheduler Routines (from cheops-NG)
+ *
+ * \author Mark Spencer <markster@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#ifdef DEBUG_SCHEDULER
+#define DEBUG(a) do { \
+ if (option_debug) \
+ DEBUG_M(a) \
+ } while (0)
+#else
+#define DEBUG(a)
+#endif
+
+#include <sys/time.h>
+
+#include "asterisk/sched.h"
+#include "asterisk/channel.h"
+#include "asterisk/lock.h"
+#include "asterisk/utils.h"
+#include "asterisk/linkedlists.h"
+
+struct sched {
+ AST_LIST_ENTRY(sched) list;
+ int id; /*!< ID number of event */
+ struct timeval when; /*!< Absolute time event should take place */
+ int resched; /*!< When to reschedule */
+ int variable; /*!< Use return value from callback to reschedule */
+ const void *data; /*!< Data */
+ ast_sched_cb callback; /*!< Callback */
+};
+
+struct sched_context {
+ ast_mutex_t lock;
+ unsigned int eventcnt; /*!< Number of events processed */
+ unsigned int schedcnt; /*!< Number of outstanding schedule events */
+ AST_LIST_HEAD_NOLOCK(, sched) schedq; /*!< Schedule entry and main queue */
+
+#ifdef SCHED_MAX_CACHE
+ AST_LIST_HEAD_NOLOCK(, sched) schedc; /*!< Cache of unused schedule structures and how many */
+ unsigned int schedccnt;
+#endif
+};
+
+struct sched_context *sched_context_create(void)
+{
+ struct sched_context *tmp;
+
+ if (!(tmp = ast_calloc(1, sizeof(*tmp))))
+ return NULL;
+
+ ast_mutex_init(&tmp->lock);
+ tmp->eventcnt = 1;
+
+ return tmp;
+}
+
+void sched_context_destroy(struct sched_context *con)
+{
+ struct sched *s;
+
+ ast_mutex_lock(&con->lock);
+
+#ifdef SCHED_MAX_CACHE
+ /* Eliminate the cache */
+ while ((s = AST_LIST_REMOVE_HEAD(&con->schedc, list)))
+ ast_free(s);
+#endif
+
+ /* And the queue */
+ while ((s = AST_LIST_REMOVE_HEAD(&con->schedq, list)))
+ ast_free(s);
+
+ /* And the context */
+ ast_mutex_unlock(&con->lock);
+ ast_mutex_destroy(&con->lock);
+ ast_free(con);
+}
+
+static struct sched *sched_alloc(struct sched_context *con)
+{
+ struct sched *tmp;
+
+ /*
+ * We keep a small cache of schedule entries
+ * to minimize the number of necessary malloc()'s
+ */
+#ifdef SCHED_MAX_CACHE
+ if ((tmp = AST_LIST_REMOVE_HEAD(&con->schedc, list)))
+ con->schedccnt--;
+ else
+#endif
+ tmp = ast_calloc(1, sizeof(*tmp));
+
+ return tmp;
+}
+
+static void sched_release(struct sched_context *con, struct sched *tmp)
+{
+ /*
+ * Add to the cache, or just free() if we
+ * already have too many cache entries
+ */
+
+#ifdef SCHED_MAX_CACHE
+ if (con->schedccnt < SCHED_MAX_CACHE) {
+ AST_LIST_INSERT_HEAD(&con->schedc, tmp, list);
+ con->schedccnt++;
+ } else
+#endif
+ ast_free(tmp);
+}
+
+/*! \brief
+ * Return the number of milliseconds
+ * until the next scheduled event
+ */
+int ast_sched_wait(struct sched_context *con)
+{
+ int ms;
+
+ DEBUG(ast_debug(1, "ast_sched_wait()\n"));
+
+ ast_mutex_lock(&con->lock);
+ if (AST_LIST_EMPTY(&con->schedq)) {
+ ms = -1;
+ } else {
+ ms = ast_tvdiff_ms(AST_LIST_FIRST(&con->schedq)->when, ast_tvnow());
+ if (ms < 0)
+ ms = 0;
+ }
+ ast_mutex_unlock(&con->lock);
+
+ return ms;
+}
+
+
+/*! \brief
+ * Take a sched structure and put it in the
+ * queue, such that the soonest event is
+ * first in the list.
+ */
+static void schedule(struct sched_context *con, struct sched *s)
+{
+
+ struct sched *cur = NULL;
+
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&con->schedq, cur, list) {
+ if (ast_tvcmp(s->when, cur->when) == -1) {
+ AST_LIST_INSERT_BEFORE_CURRENT(s, list);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ if (!cur)
+ AST_LIST_INSERT_TAIL(&con->schedq, s, list);
+
+ con->schedcnt++;
+}
+
+/*! \brief
+ * given the last event *tv and the offset in milliseconds 'when',
+ * computes the next value,
+ */
+static int sched_settime(struct timeval *tv, int when)
+{
+ struct timeval now = ast_tvnow();
+
+ /*ast_debug(1, "TV -> %lu,%lu\n", tv->tv_sec, tv->tv_usec);*/
+ if (ast_tvzero(*tv)) /* not supplied, default to now */
+ *tv = now;
+ *tv = ast_tvadd(*tv, ast_samp2tv(when, 1000));
+ if (ast_tvcmp(*tv, now) < 0) {
+ ast_debug(1, "Request to schedule in the past?!?!\n");
+ *tv = now;
+ }
+ return 0;
+}
+
+int ast_sched_replace_variable(int old_id, struct sched_context *con, int when, ast_sched_cb callback, const void *data, int variable)
+{
+ /* 0 means the schedule item is new; do not delete */
+ if (old_id > 0)
+ ast_sched_del(con, old_id);
+ return ast_sched_add_variable(con, when, callback, data, variable);
+}
+
+/*! \brief
+ * Schedule callback(data) to happen when ms into the future
+ */
+int ast_sched_add_variable(struct sched_context *con, int when, ast_sched_cb callback, const void *data, int variable)
+{
+ struct sched *tmp;
+ int res = -1;
+ DEBUG(ast_debug(1, "ast_sched_add()\n"));
+ if (!when) {
+ ast_log(LOG_NOTICE, "Scheduled event in 0 ms?\n");
+ return -1;
+ }
+ ast_mutex_lock(&con->lock);
+ if ((tmp = sched_alloc(con))) {
+ tmp->id = con->eventcnt++;
+ tmp->callback = callback;
+ tmp->data = data;
+ tmp->resched = when;
+ tmp->variable = variable;
+ tmp->when = ast_tv(0, 0);
+ if (sched_settime(&tmp->when, when)) {
+ sched_release(con, tmp);
+ } else {
+ schedule(con, tmp);
+ res = tmp->id;
+ }
+ }
+#ifdef DUMP_SCHEDULER
+ /* Dump contents of the context while we have the lock so nothing gets screwed up by accident. */
+ if (option_debug)
+ ast_sched_dump(con);
+#endif
+ ast_mutex_unlock(&con->lock);
+ return res;
+}
+
+int ast_sched_replace(int old_id, struct sched_context *con, int when, ast_sched_cb callback, const void *data)
+{
+ if (old_id > -1)
+ ast_sched_del(con, old_id);
+ return ast_sched_add(con, when, callback, data);
+}
+
+int ast_sched_add(struct sched_context *con, int when, ast_sched_cb callback, const void *data)
+{
+ return ast_sched_add_variable(con, when, callback, data, 0);
+}
+
+/*! \brief
+ * Delete the schedule entry with number
+ * "id". It's nearly impossible that there
+ * would be two or more in the list with that
+ * id.
+ */
+int ast_sched_del(struct sched_context *con, int id)
+{
+ struct sched *s;
+
+ DEBUG(ast_debug(1, "ast_sched_del()\n"));
+
+ ast_mutex_lock(&con->lock);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&con->schedq, s, list) {
+ if (s->id == id) {
+ AST_LIST_REMOVE_CURRENT(list);
+ con->schedcnt--;
+ sched_release(con, s);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+
+#ifdef DUMP_SCHEDULER
+ /* Dump contents of the context while we have the lock so nothing gets screwed up by accident. */
+ if (option_debug)
+ ast_sched_dump(con);
+#endif
+ ast_mutex_unlock(&con->lock);
+
+ if (!s) {
+ ast_debug(1, "Attempted to delete nonexistent schedule entry %d!\n", id);
+#ifdef DO_CRASH
+ CRASH;
+#endif
+ return -1;
+ }
+
+ return 0;
+}
+
+/*! \brief Dump the contents of the scheduler to LOG_DEBUG */
+void ast_sched_dump(const struct sched_context *con)
+{
+ struct sched *q;
+ struct timeval tv = ast_tvnow();
+#ifdef SCHED_MAX_CACHE
+ ast_debug(1, "Asterisk Schedule Dump (%d in Q, %d Total, %d Cache)\n", con->schedcnt, con->eventcnt - 1, con->schedccnt);
+#else
+ ast_debug(1, "Asterisk Schedule Dump (%d in Q, %d Total)\n", con->schedcnt, con->eventcnt - 1);
+#endif
+
+ ast_debug(1, "=============================================================\n");
+ ast_debug(1, "|ID Callback Data Time (sec:ms) |\n");
+ ast_debug(1, "+-----+-----------------+-----------------+-----------------+\n");
+ AST_LIST_TRAVERSE(&con->schedq, q, list) {
+ struct timeval delta = ast_tvsub(q->when, tv);
+
+ ast_debug(1, "|%.4d | %-15p | %-15p | %.6ld : %.6ld |\n",
+ q->id,
+ q->callback,
+ q->data,
+ delta.tv_sec,
+ (long int)delta.tv_usec);
+ }
+ ast_debug(1, "=============================================================\n");
+}
+
+/*! \brief
+ * Launch all events which need to be run at this time.
+ */
+int ast_sched_runq(struct sched_context *con)
+{
+ struct sched *current;
+ struct timeval tv;
+ int numevents;
+ int res;
+
+ DEBUG(ast_debug(1, "ast_sched_runq()\n"));
+
+ ast_mutex_lock(&con->lock);
+
+ for (numevents = 0; !AST_LIST_EMPTY(&con->schedq); numevents++) {
+ /* schedule all events which are going to expire within 1ms.
+ * We only care about millisecond accuracy anyway, so this will
+ * help us get more than one event at one time if they are very
+ * close together.
+ */
+ tv = ast_tvadd(ast_tvnow(), ast_tv(0, 1000));
+ if (ast_tvcmp(AST_LIST_FIRST(&con->schedq)->when, tv) != -1)
+ break;
+
+ current = AST_LIST_REMOVE_HEAD(&con->schedq, list);
+ con->schedcnt--;
+
+ /*
+ * At this point, the schedule queue is still intact. We
+ * have removed the first event and the rest is still there,
+ * so it's permissible for the callback to add new events, but
+ * trying to delete itself won't work because it isn't in
+ * the schedule queue. If that's what it wants to do, it
+ * should return 0.
+ */
+
+ ast_mutex_unlock(&con->lock);
+ res = current->callback(current->data);
+ ast_mutex_lock(&con->lock);
+
+ if (res) {
+ /*
+ * If they return non-zero, we should schedule them to be
+ * run again.
+ */
+ if (sched_settime(&current->when, current->variable? res : current->resched)) {
+ sched_release(con, current);
+ } else
+ schedule(con, current);
+ } else {
+ /* No longer needed, so release it */
+ sched_release(con, current);
+ }
+ }
+
+ ast_mutex_unlock(&con->lock);
+
+ return numevents;
+}
+
+long ast_sched_when(struct sched_context *con,int id)
+{
+ struct sched *s;
+ long secs = -1;
+ DEBUG(ast_debug(1, "ast_sched_when()\n"));
+
+ ast_mutex_lock(&con->lock);
+ AST_LIST_TRAVERSE(&con->schedq, s, list) {
+ if (s->id == id)
+ break;
+ }
+ if (s) {
+ struct timeval now = ast_tvnow();
+ secs = s->when.tv_sec - now.tv_sec;
+ }
+ ast_mutex_unlock(&con->lock);
+
+ return secs;
+}
diff --git a/trunk/main/sha1.c b/trunk/main/sha1.c
new file mode 100644
index 000000000..4226ecf63
--- /dev/null
+++ b/trunk/main/sha1.c
@@ -0,0 +1,337 @@
+/*! \file
+ *
+ * \brief Based on the RFC 3174
+ *
+ * Full Copyright Statement
+ *
+ * Copyright (C) The Internet Society (2001). All Rights Reserved.
+ *
+ * This document and translations of it may be copied and furnished to
+ * others, and derivative works that comment on or otherwise explain it
+ * or assist in its implementation may be prepared, copied, published
+ * and distributed, in whole or in part, without restriction of any
+ * kind, provided that the above copyright notice and this paragraph are
+ * included on all such copies and derivative works. However, this
+ * document itself may not be modified in any way, such as by removing
+ * the copyright notice or references to the Internet Society or other
+ * Internet organizations, except as needed for the purpose of
+ * developing Internet standards in which case the procedures for
+ * copyrights defined in the Internet Standards process must be
+ * followed, or as required to translate it into languages other than
+ * English.
+ *
+ * The limited permissions granted above are perpetual and will not be
+ * revoked by the Internet Society or its successors or assigns.
+
+ * This document and the information contained herein is provided on an
+ * "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+ * TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ *
+ *
+ * Description:
+ * This file implements the Secure Hashing Algorithm 1 as
+ * defined in FIPS PUB 180-1 published April 17, 1995.
+ *
+ * The SHA-1, produces a 160-bit message digest for a given
+ * data stream. It should take about 2**n steps to find a
+ * message with the same digest as a given message and
+ * 2**(n/2) to find any two messages with the same digest,
+ * when n is the digest size in bits. Therefore, this
+ * algorithm can serve as a means of providing a
+ * "fingerprint" for a message.
+ *
+ * Portability Issues:
+ * SHA-1 is defined in terms of 32-bit "words". This code
+ * uses <stdint.h> (included via "sha1.h" to define 32 and 8
+ * bit unsigned integer types. If your C compiler does not
+ * support 32 bit unsigned integers, this code is not
+ * appropriate.
+ *
+ * Caveats:
+ * SHA-1 is designed to work with messages less than 2^64 bits
+ * long. Although SHA-1 allows a message digest to be generated
+ * for messages of any number of bits less than 2^64, this
+ * implementation only works with messages with a length that is
+ * a multiple of the size of an 8-bit character.
+ *
+ */
+
+#include "asterisk.h"
+#include "asterisk/sha1.h"
+
+/*! Define the SHA1 circular left shift macro */
+#define SHA1CircularShift(bits,word) \
+ (((word) << (bits)) | ((word) >> (32-(bits))))
+
+/* Local Function Prototyptes */
+void SHA1PadMessage(SHA1Context *);
+void SHA1ProcessMessageBlock(SHA1Context *);
+
+/*!
+ * \brief SHA1Reset
+ * \param context the context to be reset.
+ * This function will initialize the SHA1Context in preparation
+ * for computing a new SHA1 message digest.
+ * \return sha Error Code.
+ */
+int SHA1Reset(SHA1Context *context)
+{
+ if (!context) {
+ return shaNull;
+ }
+
+ context->Length_Low = 0;
+ context->Length_High = 0;
+ context->Message_Block_Index = 0;
+
+ context->Intermediate_Hash[0] = 0x67452301;
+ context->Intermediate_Hash[1] = 0xEFCDAB89;
+ context->Intermediate_Hash[2] = 0x98BADCFE;
+ context->Intermediate_Hash[3] = 0x10325476;
+ context->Intermediate_Hash[4] = 0xC3D2E1F0;
+
+ context->Computed = 0;
+ context->Corrupted = 0;
+
+ return shaSuccess;
+}
+
+/*!
+ * \brief SHA1Result
+ * \param context [in/out] The context to use to calculate the SHA-1 hash.
+ * \param Message_Digest [out] Where the digest is returned.
+ * This function will return the 160-bit message digest into the
+ * Message_Digest array provided by the caller.
+ * \note The first octet of hash is stored in the 0th element,
+ * the last octet of hash in the 19th element.
+ * \return sha Error Code.
+ */
+int SHA1Result( SHA1Context *context,
+ uint8_t Message_Digest[SHA1HashSize])
+{
+ int i;
+
+ if (!context || !Message_Digest) {
+ return shaNull;
+ }
+
+ if (context->Corrupted) {
+ return context->Corrupted;
+ }
+
+ if (!context->Computed) {
+ SHA1PadMessage(context);
+ for (i = 0; i < 64; ++i) {
+ /* message may be sensitive, clear it out */
+ context->Message_Block[i] = 0;
+ }
+ context->Length_Low = 0; /* and clear length */
+ context->Length_High = 0;
+ context->Computed = 1;
+ }
+
+ for (i = 0; i < SHA1HashSize; ++i) {
+ Message_Digest[i] = context->Intermediate_Hash[i >> 2] >> 8 * ( 3 - ( i & 0x03 ) );
+ }
+
+ return shaSuccess;
+}
+
+/*!
+ * \brief SHA1Input
+ * \param context [in/out] The SHA context to update
+ * \param message_array [in] An array of characters representing the next portion of
+ * the message.
+ * \param length [in] The length of the message in message_array.
+ * This function accepts an array of octets as the next portion
+ * of the message.
+ * \return sha Error Code.
+ */
+int SHA1Input(SHA1Context *context, const uint8_t *message_array, unsigned length)
+{
+ if (!length) {
+ return shaSuccess;
+ }
+
+ if (!context || !message_array) {
+ return shaNull;
+ }
+
+ if (context->Computed) {
+ context->Corrupted = shaStateError;
+ return shaStateError;
+ }
+
+ if (context->Corrupted) {
+ return context->Corrupted;
+ }
+
+ while (length-- && !context->Corrupted) {
+ context->Message_Block[context->Message_Block_Index++] = (*message_array & 0xFF);
+
+ context->Length_Low += 8;
+ if (context->Length_Low == 0) {
+ context->Length_High++;
+ if (context->Length_High == 0) {
+ /* Message is too long */
+ context->Corrupted = 1;
+ }
+ }
+
+ if (context->Message_Block_Index == 64) {
+ SHA1ProcessMessageBlock(context);
+ }
+
+ message_array++;
+ }
+
+ return shaSuccess;
+}
+
+/*!
+ * \brief Process the next 512 bits of the message stored in the Message_Block array.
+ * \param context [in/out] The SHA context to update
+ * \note Many of the variable names in this code, especially the
+ * single character names, were used because those were the
+ * names used in the publication.
+ * \returns nothing.
+ */
+void SHA1ProcessMessageBlock(SHA1Context *context)
+{
+ const uint32_t K[] = { /* Constants defined in SHA-1 */
+ 0x5A827999,
+ 0x6ED9EBA1,
+ 0x8F1BBCDC,
+ 0xCA62C1D6
+ };
+ int t; /* Loop counter */
+ uint32_t temp; /* Temporary word value */
+ uint32_t W[80]; /* Word sequence */
+ uint32_t A, B, C, D, E; /* Word buffers */
+
+ /*
+ * Initialize the first 16 words in the array W
+ */
+ for (t = 0; t < 16; t++) {
+ W[t] = context->Message_Block[t * 4] << 24;
+ W[t] |= context->Message_Block[t * 4 + 1] << 16;
+ W[t] |= context->Message_Block[t * 4 + 2] << 8;
+ W[t] |= context->Message_Block[t * 4 + 3];
+ }
+
+ for (t = 16; t < 80; t++) {
+ W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
+ }
+
+ A = context->Intermediate_Hash[0];
+ B = context->Intermediate_Hash[1];
+ C = context->Intermediate_Hash[2];
+ D = context->Intermediate_Hash[3];
+ E = context->Intermediate_Hash[4];
+
+ for (t = 0; t < 20; t++) {
+ temp = SHA1CircularShift(5,A) + ((B & C) | ((~B) & D)) + E + W[t] + K[0];
+ E = D;
+ D = C;
+ C = SHA1CircularShift(30,B);
+ B = A;
+ A = temp;
+ }
+
+ for (t = 20; t < 40; t++) {
+ temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1];
+ E = D;
+ D = C;
+ C = SHA1CircularShift(30,B);
+ B = A;
+ A = temp;
+ }
+
+ for (t = 40; t < 60; t++) {
+ temp = SHA1CircularShift(5,A) + ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
+ E = D;
+ D = C;
+ C = SHA1CircularShift(30,B);
+ B = A;
+ A = temp;
+ }
+
+ for (t = 60; t < 80; t++) {
+ temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3];
+ E = D;
+ D = C;
+ C = SHA1CircularShift(30,B);
+ B = A;
+ A = temp;
+ }
+
+ context->Intermediate_Hash[0] += A;
+ context->Intermediate_Hash[1] += B;
+ context->Intermediate_Hash[2] += C;
+ context->Intermediate_Hash[3] += D;
+ context->Intermediate_Hash[4] += E;
+
+ context->Message_Block_Index = 0;
+}
+
+
+/*!
+ * \brief Pad message to be 512 bits.
+ * \param context [in/out] The context to pad.
+ *
+ * According to the standard, the message must be padded to an even
+ * 512 bits. The first padding bit must be a '1'. The last 64
+ * bits represent the length of the original message. All bits in
+ * between should be 0. This function will pad the message
+ * according to those rules by filling the Message_Block array
+ * accordingly. It will also call the ProcessMessageBlock function
+ * provided appropriately. When it returns, it can be assumed that
+ * the message digest has been computed.
+ *
+ * \returns nothing.
+ */
+
+void SHA1PadMessage(SHA1Context *context)
+{
+ /*
+ * Check to see if the current message block is too small to hold
+ * the initial padding bits and length. If so, we will pad the
+ * block, process it, and then continue padding into a second
+ * block.
+ */
+ if (context->Message_Block_Index > 55) {
+ context->Message_Block[context->Message_Block_Index++] = 0x80;
+ while (context->Message_Block_Index < 64) {
+ context->Message_Block[context->Message_Block_Index++] = 0;
+ }
+
+ SHA1ProcessMessageBlock(context);
+
+ while (context->Message_Block_Index < 56) {
+ context->Message_Block[context->Message_Block_Index++] = 0;
+ }
+ } else {
+ context->Message_Block[context->Message_Block_Index++] = 0x80;
+ while (context->Message_Block_Index < 56) {
+ context->Message_Block[context->Message_Block_Index++] = 0;
+ }
+ }
+
+ /*
+ * Store the message length as the last 8 octets
+ */
+ context->Message_Block[56] = context->Length_High >> 24;
+ context->Message_Block[57] = context->Length_High >> 16;
+ context->Message_Block[58] = context->Length_High >> 8;
+ context->Message_Block[59] = context->Length_High;
+ context->Message_Block[60] = context->Length_Low >> 24;
+ context->Message_Block[61] = context->Length_Low >> 16;
+ context->Message_Block[62] = context->Length_Low >> 8;
+ context->Message_Block[63] = context->Length_Low;
+
+ SHA1ProcessMessageBlock(context);
+}
diff --git a/trunk/main/slinfactory.c b/trunk/main/slinfactory.c
new file mode 100644
index 000000000..25d232a37
--- /dev/null
+++ b/trunk/main/slinfactory.c
@@ -0,0 +1,165 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2005, Anthony Minessale II.
+ *
+ * Anthony Minessale <anthmct@yahoo.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 A machine to gather up arbitrary frames and convert them
+ * to raw slinear on demand.
+ *
+ * \author Anthony Minessale <anthmct@yahoo.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/frame.h"
+#include "asterisk/slinfactory.h"
+#include "asterisk/translate.h"
+
+void ast_slinfactory_init(struct ast_slinfactory *sf)
+{
+ memset(sf, 0, sizeof(*sf));
+ sf->offset = sf->hold;
+}
+
+void ast_slinfactory_destroy(struct ast_slinfactory *sf)
+{
+ struct ast_frame *f;
+
+ if (sf->trans) {
+ ast_translator_free_path(sf->trans);
+ sf->trans = NULL;
+ }
+
+ while ((f = AST_LIST_REMOVE_HEAD(&sf->queue, frame_list)))
+ ast_frfree(f);
+}
+
+int ast_slinfactory_feed(struct ast_slinfactory *sf, struct ast_frame *f)
+{
+ struct ast_frame *begin_frame = f, *duped_frame = NULL, *frame_ptr;
+ unsigned int x;
+
+ if (f->subclass != AST_FORMAT_SLINEAR) {
+ if (sf->trans && f->subclass != sf->format) {
+ ast_translator_free_path(sf->trans);
+ sf->trans = NULL;
+ }
+ if (!sf->trans) {
+ if (!(sf->trans = ast_translator_build_path(AST_FORMAT_SLINEAR, f->subclass))) {
+ ast_log(LOG_WARNING, "Cannot build a path from %s to slin\n", ast_getformatname(f->subclass));
+ return 0;
+ }
+ sf->format = f->subclass;
+ }
+ if (!(begin_frame = ast_translate(sf->trans, f, 0)) || !(duped_frame = ast_frdup(begin_frame)))
+ return 0;
+ } else {
+ if (sf->trans) {
+ ast_translator_free_path(sf->trans);
+ sf->trans = NULL;
+ }
+ if (!(duped_frame = ast_frdup(f)))
+ return 0;
+ }
+
+ x = 0;
+ AST_LIST_TRAVERSE(&sf->queue, frame_ptr, frame_list)
+ x++;
+
+ AST_LIST_INSERT_TAIL(&sf->queue, duped_frame, frame_list);
+
+ sf->size += duped_frame->samples;
+
+ return x;
+}
+
+int ast_slinfactory_read(struct ast_slinfactory *sf, short *buf, size_t samples)
+{
+ struct ast_frame *frame_ptr;
+ unsigned int sofar = 0, ineed, remain;
+ short *frame_data, *offset = buf;
+
+ while (sofar < samples) {
+ ineed = samples - sofar;
+
+ if (sf->holdlen) {
+ if ((sofar + sf->holdlen) <= ineed) {
+ memcpy(offset, sf->hold, sf->holdlen * sizeof(*offset));
+ sofar += sf->holdlen;
+ offset += sf->holdlen;
+ sf->holdlen = 0;
+ sf->offset = sf->hold;
+ } else {
+ remain = sf->holdlen - ineed;
+ memcpy(offset, sf->offset, ineed * sizeof(*offset));
+ sofar += ineed;
+ sf->offset += ineed;
+ sf->holdlen = remain;
+ }
+ continue;
+ }
+
+ if ((frame_ptr = AST_LIST_REMOVE_HEAD(&sf->queue, frame_list))) {
+ frame_data = frame_ptr->data;
+
+ if ((sofar + frame_ptr->samples) <= ineed) {
+ memcpy(offset, frame_data, frame_ptr->samples * sizeof(*offset));
+ sofar += frame_ptr->samples;
+ offset += frame_ptr->samples;
+ } else {
+ remain = frame_ptr->samples - ineed;
+ memcpy(offset, frame_data, ineed * sizeof(*offset));
+ sofar += ineed;
+ frame_data += ineed;
+ memcpy(sf->hold, frame_data, remain * sizeof(*offset));
+ sf->holdlen = remain;
+ }
+ ast_frfree(frame_ptr);
+ } else {
+ break;
+ }
+ }
+
+ sf->size -= sofar;
+ return sofar;
+}
+
+unsigned int ast_slinfactory_available(const struct ast_slinfactory *sf)
+{
+ return sf->size;
+}
+
+void ast_slinfactory_flush(struct ast_slinfactory *sf)
+{
+ struct ast_frame *fr = NULL;
+
+ if (sf->trans) {
+ ast_translator_free_path(sf->trans);
+ sf->trans = NULL;
+ }
+
+ while ((fr = AST_LIST_REMOVE_HEAD(&sf->queue, frame_list)))
+ ast_frfree(fr);
+
+ sf->size = sf->holdlen = 0;
+ sf->offset = sf->hold;
+
+ return;
+}
diff --git a/trunk/main/srv.c b/trunk/main/srv.c
new file mode 100644
index 000000000..3950e255f
--- /dev/null
+++ b/trunk/main/srv.c
@@ -0,0 +1,238 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * Funding provided by nic.at
+ *
+ * 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 DNS SRV Record Lookup Support for Asterisk
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \arg See also \ref AstENUM
+ *
+ * \note Funding provided by nic.at
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#ifdef __APPLE__
+#if __APPLE_CC__ >= 1495
+#include <arpa/nameser_compat.h>
+#endif
+#endif
+#include <resolv.h>
+
+#include "asterisk/channel.h"
+#include "asterisk/srv.h"
+#include "asterisk/dns.h"
+#include "asterisk/utils.h"
+#include "asterisk/linkedlists.h"
+
+#ifdef __APPLE__
+#undef T_SRV
+#define T_SRV 33
+#endif
+
+struct srv_entry {
+ unsigned short priority;
+ unsigned short weight;
+ unsigned short port;
+ unsigned int weight_sum;
+ AST_LIST_ENTRY(srv_entry) list;
+ char host[1];
+};
+
+struct srv_context {
+ unsigned int have_weights:1;
+ AST_LIST_HEAD_NOLOCK(srv_entries, srv_entry) entries;
+};
+
+static int parse_srv(unsigned char *answer, int len, unsigned char *msg, struct srv_entry **result)
+{
+ struct srv {
+ unsigned short priority;
+ unsigned short weight;
+ unsigned short port;
+ } __attribute__ ((__packed__)) *srv = (struct srv *) answer;
+
+ int res = 0;
+ char repl[256] = "";
+ struct srv_entry *entry;
+
+ if (len < sizeof(*srv))
+ return -1;
+
+ answer += sizeof(*srv);
+ len -= sizeof(*srv);
+
+ if ((res = dn_expand(msg, answer + len, answer, repl, sizeof(repl) - 1)) <= 0) {
+ ast_log(LOG_WARNING, "Failed to expand hostname\n");
+ return -1;
+ }
+
+ /* the magic value "." for the target domain means that this service
+ is *NOT* available at the domain we searched */
+ if (!strcmp(repl, "."))
+ return -1;
+
+ if (!(entry = ast_calloc(1, sizeof(*entry) + strlen(repl))))
+ return -1;
+
+ entry->priority = ntohs(srv->priority);
+ entry->weight = ntohs(srv->weight);
+ entry->port = ntohs(srv->port);
+ strcpy(entry->host, repl);
+
+ *result = entry;
+
+ return 0;
+}
+
+static int srv_callback(void *context, unsigned char *answer, int len, unsigned char *fullanswer)
+{
+ struct srv_context *c = (struct srv_context *) context;
+ struct srv_entry *entry = NULL;
+ struct srv_entry *current;
+
+ if (parse_srv(answer, len, fullanswer, &entry))
+ return -1;
+
+ if (entry->weight)
+ c->have_weights = 1;
+
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&c->entries, current, list) {
+ /* insert this entry just before the first existing
+ entry with a higher priority */
+ if (current->priority <= entry->priority)
+ continue;
+
+ AST_LIST_INSERT_BEFORE_CURRENT(entry, list);
+ entry = NULL;
+ break;
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+
+ /* if we didn't find a place to insert the entry before an existing
+ entry, then just add it to the end */
+ if (entry)
+ AST_LIST_INSERT_TAIL(&c->entries, entry, list);
+
+ return 1;
+}
+
+/* Do the bizarre SRV record weight-handling algorithm
+ involving sorting and random number generation...
+
+ See RFC 2782 if you want know why this code does this
+*/
+static void process_weights(struct srv_context *context)
+{
+ struct srv_entry *current;
+ struct srv_entries newlist = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
+
+ while (AST_LIST_FIRST(&context->entries)) {
+ unsigned int random_weight;
+ unsigned int weight_sum;
+ unsigned short cur_priority = AST_LIST_FIRST(&context->entries)->priority;
+ struct srv_entries temp_list = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
+ weight_sum = 0;
+
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&context->entries, current, list) {
+ if (current->priority != cur_priority)
+ break;
+
+ AST_LIST_MOVE_CURRENT(&temp_list, list);
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+
+ while (AST_LIST_FIRST(&temp_list)) {
+ weight_sum = 0;
+ AST_LIST_TRAVERSE(&temp_list, current, list)
+ current->weight_sum = weight_sum += current->weight;
+
+ /* if all the remaining entries have weight == 0,
+ then just append them to the result list and quit */
+ if (weight_sum == 0) {
+ AST_LIST_APPEND_LIST(&newlist, &temp_list, list);
+ break;
+ }
+
+ random_weight = 1 + (unsigned int) ((float) weight_sum * (ast_random() / ((float) RAND_MAX + 1.0)));
+
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&temp_list, current, list) {
+ if (current->weight < random_weight)
+ continue;
+
+ AST_LIST_MOVE_CURRENT(&newlist, list);
+ break;
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ }
+
+ }
+
+ /* now that the new list has been ordered,
+ put it in place */
+
+ AST_LIST_APPEND_LIST(&context->entries, &newlist, list);
+}
+
+int ast_get_srv(struct ast_channel *chan, char *host, int hostlen, int *port, const char *service)
+{
+ struct srv_context context = { .entries = AST_LIST_HEAD_NOLOCK_INIT_VALUE };
+ struct srv_entry *current;
+ int ret;
+
+ if (chan && ast_autoservice_start(chan) < 0)
+ return -1;
+
+ ret = ast_search_dns(&context, service, C_IN, T_SRV, srv_callback);
+
+ if (context.have_weights)
+ process_weights(&context);
+
+ if (chan)
+ ret |= ast_autoservice_stop(chan);
+
+ /* TODO: there could be a "." entry in the returned list of
+ answers... if so, this requires special handling */
+
+ /* the list of entries will be sorted in the proper selection order
+ already, so we just need the first one (if any) */
+
+ if ((ret > 0) && (current = AST_LIST_REMOVE_HEAD(&context.entries, list))) {
+ ast_copy_string(host, current->host, hostlen);
+ *port = current->port;
+ ast_free(current);
+ ast_verb(3, "ast_get_srv: SRV lookup for '%s' mapped to host %s, port %d\n",
+ service, host, *port);
+ } else {
+ host[0] = '\0';
+ *port = -1;
+ }
+
+ while ((current = AST_LIST_REMOVE_HEAD(&context.entries, list)))
+ ast_free(current);
+
+ return ret;
+}
diff --git a/trunk/main/stdtime/Makefile b/trunk/main/stdtime/Makefile
new file mode 100644
index 000000000..cbe3c48f7
--- /dev/null
+++ b/trunk/main/stdtime/Makefile
@@ -0,0 +1,29 @@
+OBJS=localtime.o
+
+all: libtime.a
+
+libtime.a: $(OBJS)
+ ar rv $@ $(OBJS)
+ ranlib $@
+
+install:
+
+uninstall:
+
+clean-depend:
+ rm -f .depend
+
+clean: clean-depend
+ rm -f libtime.a *.o test
+
+depend: .depend
+
+.depend:
+ ../build_tools/mkdep $(CFLAGS) *.c
+
+test: test.c
+ ${CC} ${CFLAGS} -o test test.c
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
diff --git a/trunk/main/stdtime/localtime.c b/trunk/main/stdtime/localtime.c
new file mode 100644
index 000000000..2d60beefd
--- /dev/null
+++ b/trunk/main/stdtime/localtime.c
@@ -0,0 +1,1820 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * Most of this code is in the public domain, so clarified as of
+ * June 5, 1996 by Arthur David Olson (arthur_david_olson@nih.gov).
+ *
+ * All modifications to this code to abstract timezones away from
+ * the environment are by Tilghman Lesher, <tlesher@vcch.com>, with
+ * the copyright assigned to Digium.
+ *
+ * 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
+ *
+ * Multi-timezone Localtime code
+ *
+ * The original source from this file may be obtained from ftp://elsie.nci.nih.gov/pub/
+ */
+
+/*
+** This file is in the public domain, so clarified as of
+** 1996-06-05 by Arthur David Olson.
+*/
+
+/*
+** Leap second handling from Bradley White.
+** POSIX-style TZ environment variable handling from Guy Harris.
+*/
+
+/* #define DEBUG */
+
+/*LINTLIBRARY*/
+
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <float.h>
+
+#include "private.h"
+#include "tzfile.h"
+
+#include "asterisk/lock.h"
+#include "asterisk/localtime.h"
+#include "asterisk/strings.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/utils.h"
+
+#ifndef lint
+#ifndef NOID
+static char __attribute__((unused)) elsieid[] = "@(#)localtime.c 8.5";
+#endif /* !defined NOID */
+#endif /* !defined lint */
+
+#ifndef TZ_ABBR_MAX_LEN
+#define TZ_ABBR_MAX_LEN 16
+#endif /* !defined TZ_ABBR_MAX_LEN */
+
+#ifndef TZ_ABBR_CHAR_SET
+#define TZ_ABBR_CHAR_SET \
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 :+-._"
+#endif /* !defined TZ_ABBR_CHAR_SET */
+
+#ifndef TZ_ABBR_ERR_CHAR
+#define TZ_ABBR_ERR_CHAR '_'
+#endif /* !defined TZ_ABBR_ERR_CHAR */
+
+/*
+** SunOS 4.1.1 headers lack O_BINARY.
+*/
+
+#ifdef O_BINARY
+#define OPEN_MODE (O_RDONLY | O_BINARY)
+#endif /* defined O_BINARY */
+#ifndef O_BINARY
+#define OPEN_MODE O_RDONLY
+#endif /* !defined O_BINARY */
+
+static const char gmt[] = "GMT";
+static const struct timeval WRONG = { 0, 0 };
+
+/*! \note
+ * The DST rules to use if TZ has no rules and we can't load TZDEFRULES.
+ * We default to US rules as of 1999-08-17.
+ * POSIX 1003.1 section 8.1.1 says that the default DST rules are
+ * implementation dependent; for historical reasons, US rules are a
+ * common default.
+ */
+#ifndef TZDEFRULESTRING
+#define TZDEFRULESTRING ",M4.1.0,M10.5.0"
+#endif /* !defined TZDEFDST */
+
+/*!< \brief time type information */
+struct ttinfo { /* time type information */
+ long tt_gmtoff; /* UTC offset in seconds */
+ int tt_isdst; /* used to set tm_isdst */
+ int tt_abbrind; /* abbreviation list index */
+ int tt_ttisstd; /* TRUE if transition is std time */
+ int tt_ttisgmt; /* TRUE if transition is UTC */
+};
+
+/*! \brief leap second information */
+struct lsinfo { /* leap second information */
+ time_t ls_trans; /* transition time */
+ long ls_corr; /* correction to apply */
+};
+
+#define BIGGEST(a, b) (((a) > (b)) ? (a) : (b))
+
+#ifdef TZNAME_MAX
+#define MY_TZNAME_MAX TZNAME_MAX
+#endif /* defined TZNAME_MAX */
+#ifndef TZNAME_MAX
+#define MY_TZNAME_MAX 255
+#endif /* !defined TZNAME_MAX */
+#ifndef TZ_STRLEN_MAX
+#define TZ_STRLEN_MAX 255
+#endif /* !defined TZ_STRLEN_MAX */
+
+struct state {
+ /*! Name of the file that this references */
+ char name[TZ_STRLEN_MAX + 1];
+ int leapcnt;
+ int timecnt;
+ int typecnt;
+ int charcnt;
+ int goback;
+ int goahead;
+ time_t ats[TZ_MAX_TIMES];
+ unsigned char types[TZ_MAX_TIMES];
+ struct ttinfo ttis[TZ_MAX_TYPES];
+ char chars[BIGGEST(BIGGEST(TZ_MAX_CHARS + 1, sizeof gmt),
+ (2 * (MY_TZNAME_MAX + 1)))];
+ struct lsinfo lsis[TZ_MAX_LEAPS];
+ AST_LIST_ENTRY(state) list;
+};
+
+struct rule {
+ int r_type; /* type of rule--see below */
+ int r_day; /* day number of rule */
+ int r_week; /* week number of rule */
+ int r_mon; /* month number of rule */
+ long r_time; /* transition time of rule */
+};
+
+#define JULIAN_DAY 0 /* Jn - Julian day */
+#define DAY_OF_YEAR 1 /* n - day of year */
+#define MONTH_NTH_DAY_OF_WEEK 2 /* Mm.n.d - month, week, day of week */
+
+/*
+** Prototypes for static functions.
+*/
+
+static long detzcode P((const char * codep));
+static time_t detzcode64 P((const char * codep));
+static int differ_by_repeat P((time_t t1, time_t t0));
+static const char * getzname P((const char * strp));
+static const char * getqzname P((const char * strp, const int delim));
+static const char * getnum P((const char * strp, int * nump, int min,
+ int max));
+static const char * getsecs P((const char * strp, long * secsp));
+static const char * getoffset P((const char * strp, long * offsetp));
+static const char * getrule P((const char * strp, struct rule * rulep));
+static int gmtload P((struct state * sp));
+static struct ast_tm * gmtsub P((const struct timeval * timep, long offset,
+ struct ast_tm * tmp));
+static struct ast_tm * localsub P((const struct timeval * timep, long offset,
+ struct ast_tm * tmp, const struct state *sp));
+static int increment_overflow P((int * number, int delta));
+static int leaps_thru_end_of P((int y));
+static int long_increment_overflow P((long * number, int delta));
+static int long_normalize_overflow P((long * tensptr,
+ int * unitsptr, const int base));
+static int normalize_overflow P((int * tensptr, int * unitsptr,
+ const int base));
+static struct timeval time1 P((struct ast_tm * tmp,
+ struct ast_tm * (*funcp) P((const struct timeval *,
+ long, struct ast_tm *, const struct state *sp)),
+ long offset, const struct state *sp));
+static struct timeval time2 P((struct ast_tm *tmp,
+ struct ast_tm * (*funcp) P((const struct timeval *,
+ long, struct ast_tm*, const struct state *sp)),
+ long offset, int * okayp, const struct state *sp));
+static struct timeval time2sub P((struct ast_tm *tmp,
+ struct ast_tm * (*funcp) (const struct timeval *,
+ long, struct ast_tm*, const struct state *sp),
+ long offset, int * okayp, int do_norm_secs, const struct state *sp));
+static struct ast_tm * timesub P((const struct timeval * timep, long offset,
+ const struct state * sp, struct ast_tm * tmp));
+static int tmcomp P((const struct ast_tm * atmp,
+ const struct ast_tm * btmp));
+static time_t transtime P((time_t janfirst, int year,
+ const struct rule * rulep, long offset));
+static int tzload P((const char * name, struct state * sp,
+ int doextend));
+static int tzparse P((const char * name, struct state * sp,
+ int lastditch));
+
+static AST_LIST_HEAD_STATIC(zonelist, state);
+
+#ifndef TZ_STRLEN_MAX
+#define TZ_STRLEN_MAX 255
+#endif /* !defined TZ_STRLEN_MAX */
+
+/*! \note
+** Section 4.12.3 of X3.159-1989 requires that
+** Except for the strftime function, these functions [asctime,
+** ctime, gmtime, localtime] return values in one of two static
+** objects: a broken-down time structure and an array of char.
+** Thanks to Paul Eggert for noting this.
+*/
+
+static long detzcode(const char * const codep)
+{
+ long result;
+ int i;
+
+ result = (codep[0] & 0x80) ? ~0L : 0;
+ for (i = 0; i < 4; ++i)
+ result = (result << 8) | (codep[i] & 0xff);
+ return result;
+}
+
+static time_t detzcode64(const char * const codep)
+{
+ time_t result;
+ int i;
+
+ result = (codep[0] & 0x80) ? (~(int_fast64_t) 0) : 0;
+ for (i = 0; i < 8; ++i)
+ result = result * 256 + (codep[i] & 0xff);
+ return result;
+}
+
+static int differ_by_repeat(const time_t t1, const time_t t0)
+{
+ const long long at1 = t1, at0 = t0;
+ if (TYPE_INTEGRAL(time_t) &&
+ TYPE_BIT(time_t) - TYPE_SIGNED(time_t) < SECSPERREPEAT_BITS)
+ return 0;
+ return at1 - at0 == SECSPERREPEAT;
+}
+
+static int tzload(const char *name, struct state * const sp, const int doextend)
+{
+ const char * p;
+ int i;
+ int fid;
+ int stored;
+ int nread;
+ union {
+ struct tzhead tzhead;
+ char buf[2 * sizeof(struct tzhead) +
+ 2 * sizeof *sp +
+ 4 * TZ_MAX_TIMES];
+ } u;
+
+ if (name == NULL && (name = TZDEFAULT) == NULL)
+ return -1;
+ {
+ int doaccess;
+ /*
+ ** Section 4.9.1 of the C standard says that
+ ** "FILENAME_MAX expands to an integral constant expression
+ ** that is the size needed for an array of char large enough
+ ** to hold the longest file name string that the implementation
+ ** guarantees can be opened."
+ */
+ char fullname[FILENAME_MAX + 1];
+
+ if (name[0] == ':')
+ ++name;
+ doaccess = name[0] == '/';
+ if (!doaccess) {
+ if ((p = TZDIR) == NULL)
+ return -1;
+ if ((strlen(p) + strlen(name) + 1) >= sizeof fullname)
+ return -1;
+ (void) strcpy(fullname, p);
+ (void) strcat(fullname, "/");
+ (void) strcat(fullname, name);
+ /*
+ ** Set doaccess if '.' (as in "../") shows up in name.
+ */
+ if (strchr(name, '.') != NULL)
+ doaccess = TRUE;
+ name = fullname;
+ }
+ if (doaccess && access(name, R_OK) != 0)
+ return -1;
+ if ((fid = open(name, OPEN_MODE)) == -1)
+ return -1;
+ }
+ nread = read(fid, u.buf, sizeof u.buf);
+ if (close(fid) < 0 || nread <= 0)
+ return -1;
+ for (stored = 4; stored <= 8; stored *= 2) {
+ int ttisstdcnt;
+ int ttisgmtcnt;
+
+ ttisstdcnt = (int) detzcode(u.tzhead.tzh_ttisstdcnt);
+ ttisgmtcnt = (int) detzcode(u.tzhead.tzh_ttisgmtcnt);
+ sp->leapcnt = (int) detzcode(u.tzhead.tzh_leapcnt);
+ sp->timecnt = (int) detzcode(u.tzhead.tzh_timecnt);
+ sp->typecnt = (int) detzcode(u.tzhead.tzh_typecnt);
+ sp->charcnt = (int) detzcode(u.tzhead.tzh_charcnt);
+ p = u.tzhead.tzh_charcnt + sizeof u.tzhead.tzh_charcnt;
+ if (sp->leapcnt < 0 || sp->leapcnt > TZ_MAX_LEAPS ||
+ sp->typecnt <= 0 || sp->typecnt > TZ_MAX_TYPES ||
+ sp->timecnt < 0 || sp->timecnt > TZ_MAX_TIMES ||
+ sp->charcnt < 0 || sp->charcnt > TZ_MAX_CHARS ||
+ (ttisstdcnt != sp->typecnt && ttisstdcnt != 0) ||
+ (ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0))
+ return -1;
+ if (nread - (p - u.buf) <
+ sp->timecnt * stored + /* ats */
+ sp->timecnt + /* types */
+ sp->typecnt * 6 + /* ttinfos */
+ sp->charcnt + /* chars */
+ sp->leapcnt * (stored + 4) + /* lsinfos */
+ ttisstdcnt + /* ttisstds */
+ ttisgmtcnt) /* ttisgmts */
+ return -1;
+ for (i = 0; i < sp->timecnt; ++i) {
+ sp->ats[i] = (stored == 4) ?
+ detzcode(p) : detzcode64(p);
+ p += stored;
+ }
+ for (i = 0; i < sp->timecnt; ++i) {
+ sp->types[i] = (unsigned char) *p++;
+ if (sp->types[i] >= sp->typecnt)
+ return -1;
+ }
+ for (i = 0; i < sp->typecnt; ++i) {
+ struct ttinfo * ttisp;
+
+ ttisp = &sp->ttis[i];
+ ttisp->tt_gmtoff = detzcode(p);
+ p += 4;
+ ttisp->tt_isdst = (unsigned char) *p++;
+ if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1)
+ return -1;
+ ttisp->tt_abbrind = (unsigned char) *p++;
+ if (ttisp->tt_abbrind < 0 ||
+ ttisp->tt_abbrind > sp->charcnt)
+ return -1;
+ }
+ for (i = 0; i < sp->charcnt; ++i)
+ sp->chars[i] = *p++;
+ sp->chars[i] = '\0'; /* ensure '\0' at end */
+ for (i = 0; i < sp->leapcnt; ++i) {
+ struct lsinfo * lsisp;
+
+ lsisp = &sp->lsis[i];
+ lsisp->ls_trans = (stored == 4) ?
+ detzcode(p) : detzcode64(p);
+ p += stored;
+ lsisp->ls_corr = detzcode(p);
+ p += 4;
+ }
+ for (i = 0; i < sp->typecnt; ++i) {
+ struct ttinfo * ttisp;
+
+ ttisp = &sp->ttis[i];
+ if (ttisstdcnt == 0)
+ ttisp->tt_ttisstd = FALSE;
+ else {
+ ttisp->tt_ttisstd = *p++;
+ if (ttisp->tt_ttisstd != TRUE &&
+ ttisp->tt_ttisstd != FALSE)
+ return -1;
+ }
+ }
+ for (i = 0; i < sp->typecnt; ++i) {
+ struct ttinfo * ttisp;
+
+ ttisp = &sp->ttis[i];
+ if (ttisgmtcnt == 0)
+ ttisp->tt_ttisgmt = FALSE;
+ else {
+ ttisp->tt_ttisgmt = *p++;
+ if (ttisp->tt_ttisgmt != TRUE &&
+ ttisp->tt_ttisgmt != FALSE)
+ return -1;
+ }
+ }
+ /*
+ ** Out-of-sort ats should mean we're running on a
+ ** signed time_t system but using a data file with
+ ** unsigned values (or vice versa).
+ */
+ for (i = 0; i < sp->timecnt - 2; ++i)
+ if (sp->ats[i] > sp->ats[i + 1]) {
+ ++i;
+ if (TYPE_SIGNED(time_t)) {
+ /*
+ ** Ignore the end (easy).
+ */
+ sp->timecnt = i;
+ } else {
+ /*
+ ** Ignore the beginning (harder).
+ */
+ int j;
+
+ for (j = 0; j + i < sp->timecnt; ++j) {
+ sp->ats[j] = sp->ats[j + i];
+ sp->types[j] = sp->types[j + i];
+ }
+ sp->timecnt = j;
+ }
+ break;
+ }
+ /*
+ ** If this is an old file, we're done.
+ */
+ if (u.tzhead.tzh_version[0] == '\0')
+ break;
+ nread -= p - u.buf;
+ for (i = 0; i < nread; ++i)
+ u.buf[i] = p[i];
+ /*
+ ** If this is a narrow integer time_t system, we're done.
+ */
+ if (stored >= (int) sizeof(time_t) && TYPE_INTEGRAL(time_t))
+ break;
+ }
+ if (doextend && nread > 2 &&
+ u.buf[0] == '\n' && u.buf[nread - 1] == '\n' &&
+ sp->typecnt + 2 <= TZ_MAX_TYPES) {
+ struct state ts;
+ int result;
+
+ u.buf[nread - 1] = '\0';
+ result = tzparse(&u.buf[1], &ts, FALSE);
+ if (result == 0 && ts.typecnt == 2 &&
+ sp->charcnt + ts.charcnt <= TZ_MAX_CHARS) {
+ for (i = 0; i < 2; ++i)
+ ts.ttis[i].tt_abbrind +=
+ sp->charcnt;
+ for (i = 0; i < ts.charcnt; ++i)
+ sp->chars[sp->charcnt++] =
+ ts.chars[i];
+ i = 0;
+ while (i < ts.timecnt &&
+ ts.ats[i] <=
+ sp->ats[sp->timecnt - 1])
+ ++i;
+ while (i < ts.timecnt &&
+ sp->timecnt < TZ_MAX_TIMES) {
+ sp->ats[sp->timecnt] =
+ ts.ats[i];
+ sp->types[sp->timecnt] =
+ sp->typecnt +
+ ts.types[i];
+ ++sp->timecnt;
+ ++i;
+ }
+ sp->ttis[sp->typecnt++] = ts.ttis[0];
+ sp->ttis[sp->typecnt++] = ts.ttis[1];
+ }
+ }
+ i = 2 * YEARSPERREPEAT;
+ sp->goback = sp->goahead = sp->timecnt > i;
+ sp->goback = sp->goback && sp->types[i] == sp->types[0] &&
+ differ_by_repeat(sp->ats[i], sp->ats[0]);
+ sp->goahead = sp->goahead &&
+ sp->types[sp->timecnt - 1] == sp->types[sp->timecnt - 1 - i] &&
+ differ_by_repeat(sp->ats[sp->timecnt - 1],
+ sp->ats[sp->timecnt - 1 - i]);
+ return 0;
+}
+
+static const int mon_lengths[2][MONSPERYEAR] = {
+ { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
+ { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
+};
+
+static const int year_lengths[2] = {
+ DAYSPERNYEAR, DAYSPERLYEAR
+};
+
+/*! \brief
+** Given a pointer into a time zone string, scan until a character that is not
+** a valid character in a zone name is found. Return a pointer to that
+** character.
+*/
+
+static const char * getzname(const char *strp)
+{
+ char c;
+
+ while ((c = *strp) != '\0' && !is_digit(c) && c != ',' && c != '-' &&
+ c != '+')
+ ++strp;
+ return strp;
+}
+
+/*! \brief
+** Given a pointer into an extended time zone string, scan until the ending
+** delimiter of the zone name is located. Return a pointer to the delimiter.
+**
+** As with getzname above, the legal character set is actually quite
+** restricted, with other characters producing undefined results.
+** We don't do any checking here; checking is done later in common-case code.
+*/
+
+static const char * getqzname(const char *strp, const int delim)
+{
+ int c;
+
+ while ((c = *strp) != '\0' && c != delim)
+ ++strp;
+ return strp;
+}
+
+/*! \brief
+** Given a pointer into a time zone string, extract a number from that string.
+** Check that the number is within a specified range; if it is not, return
+** NULL.
+** Otherwise, return a pointer to the first character not part of the number.
+*/
+
+static const char *getnum(const char *strp, int *nump, const int min, const int max)
+{
+ char c;
+ int num;
+
+ if (strp == NULL || !is_digit(c = *strp))
+ return NULL;
+ num = 0;
+ do {
+ num = num * 10 + (c - '0');
+ if (num > max)
+ return NULL; /* illegal value */
+ c = *++strp;
+ } while (is_digit(c));
+ if (num < min)
+ return NULL; /* illegal value */
+ *nump = num;
+ return strp;
+}
+
+/*! \brief
+** Given a pointer into a time zone string, extract a number of seconds,
+** in hh[:mm[:ss]] form, from the string.
+** If any error occurs, return NULL.
+** Otherwise, return a pointer to the first character not part of the number
+** of seconds.
+*/
+
+static const char *getsecs(const char *strp, long * const secsp)
+{
+ int num;
+
+ /*
+ ** `HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like
+ ** "M10.4.6/26", which does not conform to Posix,
+ ** but which specifies the equivalent of
+ ** ``02:00 on the first Sunday on or after 23 Oct''.
+ */
+ strp = getnum(strp, &num, 0, HOURSPERDAY * DAYSPERWEEK - 1);
+ if (strp == NULL)
+ return NULL;
+ *secsp = num * (long) SECSPERHOUR;
+ if (*strp == ':') {
+ ++strp;
+ strp = getnum(strp, &num, 0, MINSPERHOUR - 1);
+ if (strp == NULL)
+ return NULL;
+ *secsp += num * SECSPERMIN;
+ if (*strp == ':') {
+ ++strp;
+ /* `SECSPERMIN' allows for leap seconds. */
+ strp = getnum(strp, &num, 0, SECSPERMIN);
+ if (strp == NULL)
+ return NULL;
+ *secsp += num;
+ }
+ }
+ return strp;
+}
+
+/*! \brief
+** Given a pointer into a time zone string, extract an offset, in
+** [+-]hh[:mm[:ss]] form, from the string.
+** If any error occurs, return NULL.
+** Otherwise, return a pointer to the first character not part of the time.
+*/
+
+static const char *getoffset(const char *strp, long *offsetp)
+{
+ int neg = 0;
+
+ if (*strp == '-') {
+ neg = 1;
+ ++strp;
+ } else if (*strp == '+')
+ ++strp;
+ strp = getsecs(strp, offsetp);
+ if (strp == NULL)
+ return NULL; /* illegal time */
+ if (neg)
+ *offsetp = -*offsetp;
+ return strp;
+}
+
+/*! \brief
+** Given a pointer into a time zone string, extract a rule in the form
+** date[/time]. See POSIX section 8 for the format of "date" and "time".
+** If a valid rule is not found, return NULL.
+** Otherwise, return a pointer to the first character not part of the rule.
+*/
+
+static const char *getrule(const char *strp, struct rule *rulep)
+{
+ if (*strp == 'J') {
+ /*
+ ** Julian day.
+ */
+ rulep->r_type = JULIAN_DAY;
+ ++strp;
+ strp = getnum(strp, &rulep->r_day, 1, DAYSPERNYEAR);
+ } else if (*strp == 'M') {
+ /*
+ ** Month, week, day.
+ */
+ rulep->r_type = MONTH_NTH_DAY_OF_WEEK;
+ ++strp;
+ strp = getnum(strp, &rulep->r_mon, 1, MONSPERYEAR);
+ if (strp == NULL)
+ return NULL;
+ if (*strp++ != '.')
+ return NULL;
+ strp = getnum(strp, &rulep->r_week, 1, 5);
+ if (strp == NULL)
+ return NULL;
+ if (*strp++ != '.')
+ return NULL;
+ strp = getnum(strp, &rulep->r_day, 0, DAYSPERWEEK - 1);
+ } else if (is_digit(*strp)) {
+ /*
+ ** Day of year.
+ */
+ rulep->r_type = DAY_OF_YEAR;
+ strp = getnum(strp, &rulep->r_day, 0, DAYSPERLYEAR - 1);
+ } else return NULL; /* invalid format */
+ if (strp == NULL)
+ return NULL;
+ if (*strp == '/') {
+ /*
+ ** Time specified.
+ */
+ ++strp;
+ strp = getsecs(strp, &rulep->r_time);
+ } else rulep->r_time = 2 * SECSPERHOUR; /* default = 2:00:00 */
+ return strp;
+}
+
+/*! \brief
+** Given the Epoch-relative time of January 1, 00:00:00 UTC, in a year, the
+** year, a rule, and the offset from UTC at the time that rule takes effect,
+** calculate the Epoch-relative time that rule takes effect.
+*/
+
+static time_t transtime(const time_t janfirst, const int year, const struct rule *rulep, const long offset)
+{
+ int leapyear;
+ time_t value;
+ int i;
+ int d, m1, yy0, yy1, yy2, dow;
+
+ INITIALIZE(value);
+ leapyear = isleap(year);
+ switch (rulep->r_type) {
+
+ case JULIAN_DAY:
+ /*
+ ** Jn - Julian day, 1 == January 1, 60 == March 1 even in leap
+ ** years.
+ ** In non-leap years, or if the day number is 59 or less, just
+ ** add SECSPERDAY times the day number-1 to the time of
+ ** January 1, midnight, to get the day.
+ */
+ value = janfirst + (rulep->r_day - 1) * SECSPERDAY;
+ if (leapyear && rulep->r_day >= 60)
+ value += SECSPERDAY;
+ break;
+
+ case DAY_OF_YEAR:
+ /*
+ ** n - day of year.
+ ** Just add SECSPERDAY times the day number to the time of
+ ** January 1, midnight, to get the day.
+ */
+ value = janfirst + rulep->r_day * SECSPERDAY;
+ break;
+
+ case MONTH_NTH_DAY_OF_WEEK:
+ /*
+ ** Mm.n.d - nth "dth day" of month m.
+ */
+ value = janfirst;
+ for (i = 0; i < rulep->r_mon - 1; ++i)
+ value += mon_lengths[leapyear][i] * SECSPERDAY;
+
+ /*
+ ** Use Zeller's Congruence to get day-of-week of first day of
+ ** month.
+ */
+ m1 = (rulep->r_mon + 9) % 12 + 1;
+ yy0 = (rulep->r_mon <= 2) ? (year - 1) : year;
+ yy1 = yy0 / 100;
+ yy2 = yy0 % 100;
+ dow = ((26 * m1 - 2) / 10 +
+ 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7;
+ if (dow < 0)
+ dow += DAYSPERWEEK;
+
+ /*
+ ** "dow" is the day-of-week of the first day of the month. Get
+ ** the day-of-month (zero-origin) of the first "dow" day of the
+ ** month.
+ */
+ d = rulep->r_day - dow;
+ if (d < 0)
+ d += DAYSPERWEEK;
+ for (i = 1; i < rulep->r_week; ++i) {
+ if (d + DAYSPERWEEK >=
+ mon_lengths[leapyear][rulep->r_mon - 1])
+ break;
+ d += DAYSPERWEEK;
+ }
+
+ /*
+ ** "d" is the day-of-month (zero-origin) of the day we want.
+ */
+ value += d * SECSPERDAY;
+ break;
+ }
+
+ /*
+ ** "value" is the Epoch-relative time of 00:00:00 UTC on the day in
+ ** question. To get the Epoch-relative time of the specified local
+ ** time on that day, add the transition time and the current offset
+ ** from UTC.
+ */
+ return value + rulep->r_time + offset;
+}
+
+/*! \note
+** Given a POSIX section 8-style TZ string, fill in the rule tables as
+** appropriate.
+*/
+
+static int tzparse(const char *name, struct state *sp, const int lastditch)
+{
+ const char * stdname;
+ const char * dstname;
+ size_t stdlen;
+ size_t dstlen;
+ long stdoffset;
+ long dstoffset;
+ time_t * atp;
+ unsigned char * typep;
+ char * cp;
+ int load_result;
+
+ INITIALIZE(dstname);
+ stdname = name;
+ if (lastditch) {
+ stdlen = strlen(name); /* length of standard zone name */
+ name += stdlen;
+ if (stdlen >= sizeof sp->chars)
+ stdlen = (sizeof sp->chars) - 1;
+ stdoffset = 0;
+ } else {
+ if (*name == '<') {
+ name++;
+ stdname = name;
+ name = getqzname(name, '>');
+ if (*name != '>')
+ return -1;
+ stdlen = name - stdname;
+ name++;
+ } else {
+ name = getzname(name);
+ stdlen = name - stdname;
+ }
+ if (*name == '\0')
+ return -1;
+ name = getoffset(name, &stdoffset);
+ if (name == NULL)
+ return -1;
+ }
+ load_result = tzload(TZDEFRULES, sp, FALSE);
+ if (load_result != 0)
+ sp->leapcnt = 0; /* so, we're off a little */
+ if (*name != '\0') {
+ if (*name == '<') {
+ dstname = ++name;
+ name = getqzname(name, '>');
+ if (*name != '>')
+ return -1;
+ dstlen = name - dstname;
+ name++;
+ } else {
+ dstname = name;
+ name = getzname(name);
+ dstlen = name - dstname; /* length of DST zone name */
+ }
+ if (*name != '\0' && *name != ',' && *name != ';') {
+ name = getoffset(name, &dstoffset);
+ if (name == NULL)
+ return -1;
+ } else dstoffset = stdoffset - SECSPERHOUR;
+ if (*name == '\0' && load_result != 0)
+ name = TZDEFRULESTRING;
+ if (*name == ',' || *name == ';') {
+ struct rule start;
+ struct rule end;
+ int year;
+ time_t janfirst;
+ time_t starttime;
+ time_t endtime;
+
+ ++name;
+ if ((name = getrule(name, &start)) == NULL)
+ return -1;
+ if (*name++ != ',')
+ return -1;
+ if ((name = getrule(name, &end)) == NULL)
+ return -1;
+ if (*name != '\0')
+ return -1;
+ sp->typecnt = 2; /* standard time and DST */
+ /*
+ ** Two transitions per year, from EPOCH_YEAR forward.
+ */
+ sp->ttis[0].tt_gmtoff = -dstoffset;
+ sp->ttis[0].tt_isdst = 1;
+ sp->ttis[0].tt_abbrind = stdlen + 1;
+ sp->ttis[1].tt_gmtoff = -stdoffset;
+ sp->ttis[1].tt_isdst = 0;
+ sp->ttis[1].tt_abbrind = 0;
+ atp = sp->ats;
+ typep = sp->types;
+ janfirst = 0;
+ sp->timecnt = 0;
+ for (year = EPOCH_YEAR;
+ sp->timecnt + 2 <= TZ_MAX_TIMES;
+ ++year) {
+ time_t newfirst;
+
+ starttime = transtime(janfirst, year, &start,
+ stdoffset);
+ endtime = transtime(janfirst, year, &end,
+ dstoffset);
+ if (starttime > endtime) {
+ *atp++ = endtime;
+ *typep++ = 1; /* DST ends */
+ *atp++ = starttime;
+ *typep++ = 0; /* DST begins */
+ } else {
+ *atp++ = starttime;
+ *typep++ = 0; /* DST begins */
+ *atp++ = endtime;
+ *typep++ = 1; /* DST ends */
+ }
+ sp->timecnt += 2;
+ newfirst = janfirst;
+ newfirst += year_lengths[isleap(year)] *
+ SECSPERDAY;
+ if (newfirst <= janfirst)
+ break;
+ janfirst = newfirst;
+ }
+ } else {
+ long theirstdoffset;
+ long theirdstoffset;
+ long theiroffset;
+ int isdst;
+ int i;
+ int j;
+
+ if (*name != '\0')
+ return -1;
+ /*
+ ** Initial values of theirstdoffset and theirdstoffset.
+ */
+ theirstdoffset = 0;
+ for (i = 0; i < sp->timecnt; ++i) {
+ j = sp->types[i];
+ if (!sp->ttis[j].tt_isdst) {
+ theirstdoffset =
+ -sp->ttis[j].tt_gmtoff;
+ break;
+ }
+ }
+ theirdstoffset = 0;
+ for (i = 0; i < sp->timecnt; ++i) {
+ j = sp->types[i];
+ if (sp->ttis[j].tt_isdst) {
+ theirdstoffset =
+ -sp->ttis[j].tt_gmtoff;
+ break;
+ }
+ }
+ /*
+ ** Initially we're assumed to be in standard time.
+ */
+ isdst = FALSE;
+ theiroffset = theirstdoffset;
+ /*
+ ** Now juggle transition times and types
+ ** tracking offsets as you do.
+ */
+ for (i = 0; i < sp->timecnt; ++i) {
+ j = sp->types[i];
+ sp->types[i] = sp->ttis[j].tt_isdst;
+ if (sp->ttis[j].tt_ttisgmt) {
+ /* No adjustment to transition time */
+ } else {
+ /*
+ ** If summer time is in effect, and the
+ ** transition time was not specified as
+ ** standard time, add the summer time
+ ** offset to the transition time;
+ ** otherwise, add the standard time
+ ** offset to the transition time.
+ */
+ /*
+ ** Transitions from DST to DDST
+ ** will effectively disappear since
+ ** POSIX provides for only one DST
+ ** offset.
+ */
+ if (isdst && !sp->ttis[j].tt_ttisstd) {
+ sp->ats[i] += dstoffset -
+ theirdstoffset;
+ } else {
+ sp->ats[i] += stdoffset -
+ theirstdoffset;
+ }
+ }
+ theiroffset = -sp->ttis[j].tt_gmtoff;
+ if (sp->ttis[j].tt_isdst)
+ theirdstoffset = theiroffset;
+ else theirstdoffset = theiroffset;
+ }
+ /*
+ ** Finally, fill in ttis.
+ ** ttisstd and ttisgmt need not be handled.
+ */
+ sp->ttis[0].tt_gmtoff = -stdoffset;
+ sp->ttis[0].tt_isdst = FALSE;
+ sp->ttis[0].tt_abbrind = 0;
+ sp->ttis[1].tt_gmtoff = -dstoffset;
+ sp->ttis[1].tt_isdst = TRUE;
+ sp->ttis[1].tt_abbrind = stdlen + 1;
+ sp->typecnt = 2;
+ }
+ } else {
+ dstlen = 0;
+ sp->typecnt = 1; /* only standard time */
+ sp->timecnt = 0;
+ sp->ttis[0].tt_gmtoff = -stdoffset;
+ sp->ttis[0].tt_isdst = 0;
+ sp->ttis[0].tt_abbrind = 0;
+ }
+ sp->charcnt = stdlen + 1;
+ if (dstlen != 0)
+ sp->charcnt += dstlen + 1;
+ if ((size_t) sp->charcnt > sizeof sp->chars)
+ return -1;
+ cp = sp->chars;
+ (void) strncpy(cp, stdname, stdlen);
+ cp += stdlen;
+ *cp++ = '\0';
+ if (dstlen != 0) {
+ (void) strncpy(cp, dstname, dstlen);
+ *(cp + dstlen) = '\0';
+ }
+ return 0;
+}
+
+static int gmtload(struct state *sp)
+{
+ if (tzload(gmt, sp, TRUE) != 0)
+ return tzparse(gmt, sp, TRUE);
+ else
+ return -1;
+}
+
+static const struct state *ast_tzset(const char *zone)
+{
+ struct state *sp;
+
+ if (ast_strlen_zero(zone))
+ zone = "/etc/localtime";
+
+ AST_LIST_LOCK(&zonelist);
+ AST_LIST_TRAVERSE(&zonelist, sp, list) {
+ if (!strcmp(sp->name, zone)) {
+ AST_LIST_UNLOCK(&zonelist);
+ return sp;
+ }
+ }
+ AST_LIST_UNLOCK(&zonelist);
+
+ if (!(sp = ast_calloc(1, sizeof *sp)))
+ return NULL;
+
+ if (tzload(zone, sp, TRUE) != 0) {
+ if (zone[0] == ':' || tzparse(zone, sp, FALSE) != 0)
+ (void) gmtload(sp);
+ }
+ ast_copy_string(sp->name, zone, sizeof(sp->name));
+ AST_LIST_LOCK(&zonelist);
+ AST_LIST_INSERT_TAIL(&zonelist, sp, list);
+ AST_LIST_UNLOCK(&zonelist);
+ return sp;
+}
+
+/*! \note
+** The easy way to behave "as if no library function calls" localtime
+** is to not call it--so we drop its guts into "localsub", which can be
+** freely called. (And no, the PANS doesn't require the above behavior--
+** but it *is* desirable.)
+**
+** The unused offset argument is for the benefit of mktime variants.
+*/
+
+static struct ast_tm *localsub(const struct timeval *timep, const long offset, struct ast_tm *tmp, const struct state *sp)
+{
+ const struct ttinfo * ttisp;
+ int i;
+ struct ast_tm * result;
+ struct timeval t;
+ memcpy(&t, timep, sizeof(t));
+
+ if (sp == NULL)
+ return gmtsub(timep, offset, tmp);
+ if ((sp->goback && t.tv_sec < sp->ats[0]) ||
+ (sp->goahead && t.tv_sec > sp->ats[sp->timecnt - 1])) {
+ struct timeval newt = t;
+ time_t seconds;
+ time_t tcycles;
+ int_fast64_t icycles;
+
+ if (t.tv_sec < sp->ats[0])
+ seconds = sp->ats[0] - t.tv_sec;
+ else seconds = t.tv_sec - sp->ats[sp->timecnt - 1];
+ --seconds;
+ tcycles = seconds / YEARSPERREPEAT / AVGSECSPERYEAR;
+ ++tcycles;
+ icycles = tcycles;
+ if (tcycles - icycles >= 1 || icycles - tcycles >= 1)
+ return NULL;
+ seconds = icycles;
+ seconds *= YEARSPERREPEAT;
+ seconds *= AVGSECSPERYEAR;
+ if (t.tv_sec < sp->ats[0])
+ newt.tv_sec += seconds;
+ else newt.tv_sec -= seconds;
+ if (newt.tv_sec < sp->ats[0] ||
+ newt.tv_sec > sp->ats[sp->timecnt - 1])
+ return NULL; /* "cannot happen" */
+ result = localsub(&newt, offset, tmp, sp);
+ if (result == tmp) {
+ time_t newy;
+
+ newy = tmp->tm_year;
+ if (t.tv_sec < sp->ats[0])
+ newy -= icycles * YEARSPERREPEAT;
+ else
+ newy += icycles * YEARSPERREPEAT;
+ tmp->tm_year = newy;
+ if (tmp->tm_year != newy)
+ return NULL;
+ }
+ return result;
+ }
+ if (sp->timecnt == 0 || t.tv_sec < sp->ats[0]) {
+ i = 0;
+ while (sp->ttis[i].tt_isdst) {
+ if (++i >= sp->typecnt) {
+ i = 0;
+ break;
+ }
+ }
+ } else {
+ int lo = 1;
+ int hi = sp->timecnt;
+
+ while (lo < hi) {
+ int mid = (lo + hi) >> 1;
+
+ if (t.tv_sec < sp->ats[mid])
+ hi = mid;
+ else
+ lo = mid + 1;
+ }
+ i = (int) sp->types[lo - 1];
+ }
+ ttisp = &sp->ttis[i];
+ /*
+ ** To get (wrong) behavior that's compatible with System V Release 2.0
+ ** you'd replace the statement below with
+ ** t += ttisp->tt_gmtoff;
+ ** timesub(&t, 0L, sp, tmp);
+ */
+ result = timesub(&t, ttisp->tt_gmtoff, sp, tmp);
+ tmp->tm_isdst = ttisp->tt_isdst;
+ tmp->tm_gmtoff = ttisp->tt_gmtoff;
+#ifdef TM_ZONE
+ tmp->TM_ZONE = &sp->chars[ttisp->tt_abbrind];
+#endif /* defined TM_ZONE */
+ tmp->tm_usec = timep->tv_usec;
+ return result;
+}
+
+struct ast_tm *ast_localtime(const struct timeval *timep, struct ast_tm *tmp, const char *zone)
+{
+ const struct state *sp = ast_tzset(zone);
+ memset(tmp, 0, sizeof(*tmp));
+ return sp ? localsub(timep, 0L, tmp, sp) : NULL;
+}
+
+/*
+** This function provides informaton about daylight savings time
+** for the given timezone. This includes whether it can determine
+** if daylight savings is used for this timezone, the UTC times for
+** when daylight savings transitions, and the offset in seconds from
+** UTC.
+*/
+
+void ast_get_dst_info(const time_t * const timep, int *dst_enabled, time_t *dst_start, time_t *dst_end, int *gmt_off, const char * const zone)
+{
+ int i;
+ int transition1 = -1;
+ int transition2 = -1;
+ time_t seconds;
+ int bounds_exceeded = 0;
+ time_t t = *timep;
+ const struct state *sp;
+
+ if (NULL == dst_enabled)
+ return;
+ *dst_enabled = 0;
+
+ if (NULL == dst_start || NULL == dst_end || NULL == gmt_off)
+ return;
+
+ *gmt_off = 0;
+
+ sp = ast_tzset(zone);
+ if (NULL == sp)
+ return;
+
+ /* If the desired time exceeds the bounds of the defined time transitions
+ * then give give up on determining DST info and simply look for gmt offset
+ * This requires that I adjust the given time using increments of Gregorian
+ * repeats to place the time within the defined time transitions in the
+ * timezone structure.
+ */
+ if ((sp->goback && t < sp->ats[0]) ||
+ (sp->goahead && t > sp->ats[sp->timecnt - 1])) {
+ time_t tcycles;
+ int_fast64_t icycles;
+
+ if (t < sp->ats[0])
+ seconds = sp->ats[0] - t;
+ else seconds = t - sp->ats[sp->timecnt - 1];
+ --seconds;
+ tcycles = seconds / YEARSPERREPEAT / AVGSECSPERYEAR;
+ ++tcycles;
+ icycles = tcycles;
+ if (tcycles - icycles >= 1 || icycles - tcycles >= 1)
+ return;
+ seconds = icycles;
+ seconds *= YEARSPERREPEAT;
+ seconds *= AVGSECSPERYEAR;
+ if (t < sp->ats[0])
+ t += seconds;
+ else
+ t -= seconds;
+
+ if (t < sp->ats[0] || t > sp->ats[sp->timecnt - 1])
+ return; /* "cannot happen" */
+
+ bounds_exceeded = 1;
+ }
+
+ if (sp->timecnt == 0 || t < sp->ats[0]) {
+ /* I have no transition times or I'm before time */
+ *dst_enabled = 0;
+ /* Find where I can get gmtoff */
+ i = 0;
+ while (sp->ttis[i].tt_isdst)
+ if (++i >= sp->typecnt) {
+ i = 0;
+ break;
+ }
+ *gmt_off = sp->ttis[i].tt_gmtoff;
+ return;
+ }
+
+ for (i = 1; i < sp->timecnt; ++i) {
+ if (t < sp->ats[i]) {
+ transition1 = sp->types[i - 1];
+ transition2 = sp->types[i];
+ break;
+ }
+ }
+ /* if I found transition times that do not bounded the given time and these correspond to
+ or the bounding zones do not reflect a changes in day light savings, then I do not have dst active */
+ if (i >= sp->timecnt || 0 > transition1 || 0 > transition2 ||
+ (sp->ttis[transition1].tt_isdst == sp->ttis[transition2].tt_isdst)) {
+ *dst_enabled = 0;
+ *gmt_off = sp->ttis[sp->types[sp->timecnt -1]].tt_gmtoff;
+ } else {
+ /* I have valid daylight savings information. */
+ if(sp->ttis[transition2].tt_isdst)
+ *gmt_off = sp->ttis[transition1].tt_gmtoff;
+ else
+ *gmt_off = sp->ttis[transition2].tt_gmtoff;
+
+ /* If I adjusted the time earlier, indicate that the dst is invalid */
+ if (!bounds_exceeded) {
+ *dst_enabled = 1;
+ /* Determine which of the bounds is the start of daylight savings and which is the end */
+ if(sp->ttis[transition2].tt_isdst) {
+ *dst_start = sp->ats[i];
+ *dst_end = sp->ats[i -1];
+ } else {
+ *dst_start = sp->ats[i -1];
+ *dst_end = sp->ats[i];
+ }
+ }
+ }
+ return;
+}
+
+/*
+** gmtsub is to gmtime as localsub is to localtime.
+*/
+
+static struct ast_tm *gmtsub(const struct timeval *timep, const long offset, struct ast_tm *tmp)
+{
+ struct ast_tm * result;
+ struct state *sp;
+
+ AST_LIST_LOCK(&zonelist);
+ AST_LIST_TRAVERSE(&zonelist, sp, list) {
+ if (!strcmp(sp->name, "UTC"))
+ break;
+ }
+
+ if (!sp) {
+ if (!(sp = (struct state *) ast_calloc(1, sizeof *sp)))
+ return NULL;
+ gmtload(sp);
+ AST_LIST_INSERT_TAIL(&zonelist, sp, list);
+ }
+ AST_LIST_UNLOCK(&zonelist);
+
+ result = timesub(timep, offset, sp, tmp);
+#ifdef TM_ZONE
+ /*
+ ** Could get fancy here and deliver something such as
+ ** "UTC+xxxx" or "UTC-xxxx" if offset is non-zero,
+ ** but this is no time for a treasure hunt.
+ */
+ if (offset != 0)
+ tmp->TM_ZONE = " ";
+ else
+ tmp->TM_ZONE = sp->chars;
+#endif /* defined TM_ZONE */
+ return result;
+}
+
+/*! \brief
+** Return the number of leap years through the end of the given year
+** where, to make the math easy, the answer for year zero is defined as zero.
+*/
+
+static int leaps_thru_end_of(const int y)
+{
+ return (y >= 0) ? (y / 4 - y / 100 + y / 400) :
+ -(leaps_thru_end_of(-(y + 1)) + 1);
+}
+
+static struct ast_tm *timesub(const struct timeval *timep, const long offset, const struct state *sp, struct ast_tm *tmp)
+{
+ const struct lsinfo * lp;
+ time_t tdays;
+ int idays; /* unsigned would be so 2003 */
+ long rem;
+ int y;
+ const int * ip;
+ long corr;
+ int hit;
+ int i;
+ long seconds;
+
+
+ corr = 0;
+ hit = 0;
+ i = (sp == NULL) ? 0 : sp->leapcnt;
+ while (--i >= 0) {
+ lp = &sp->lsis[i];
+ if (timep->tv_sec >= lp->ls_trans) {
+ if (timep->tv_sec == lp->ls_trans) {
+ hit = ((i == 0 && lp->ls_corr > 0) ||
+ lp->ls_corr > sp->lsis[i - 1].ls_corr);
+ if (hit)
+ while (i > 0 &&
+ sp->lsis[i].ls_trans ==
+ sp->lsis[i - 1].ls_trans + 1 &&
+ sp->lsis[i].ls_corr ==
+ sp->lsis[i - 1].ls_corr + 1) {
+ ++hit;
+ --i;
+ }
+ }
+ corr = lp->ls_corr;
+ break;
+ }
+ }
+ y = EPOCH_YEAR;
+ tdays = timep->tv_sec / SECSPERDAY;
+ rem = timep->tv_sec - tdays * SECSPERDAY;
+ while (tdays < 0 || tdays >= year_lengths[isleap(y)]) {
+ int newy;
+ time_t tdelta;
+ int idelta;
+ int leapdays;
+
+ tdelta = tdays / DAYSPERLYEAR;
+ idelta = tdelta;
+ if (tdelta - idelta >= 1 || idelta - tdelta >= 1)
+ return NULL;
+ if (idelta == 0)
+ idelta = (tdays < 0) ? -1 : 1;
+ newy = y;
+ if (increment_overflow(&newy, idelta))
+ return NULL;
+ leapdays = leaps_thru_end_of(newy - 1) -
+ leaps_thru_end_of(y - 1);
+ tdays -= ((time_t) newy - y) * DAYSPERNYEAR;
+ tdays -= leapdays;
+ y = newy;
+ }
+
+ seconds = tdays * SECSPERDAY + 0.5;
+ tdays = seconds / SECSPERDAY;
+ rem += seconds - tdays * SECSPERDAY;
+
+ /*
+ ** Given the range, we can now fearlessly cast...
+ */
+ idays = tdays;
+ rem += offset - corr;
+ while (rem < 0) {
+ rem += SECSPERDAY;
+ --idays;
+ }
+ while (rem >= SECSPERDAY) {
+ rem -= SECSPERDAY;
+ ++idays;
+ }
+ while (idays < 0) {
+ if (increment_overflow(&y, -1))
+ return NULL;
+ idays += year_lengths[isleap(y)];
+ }
+ while (idays >= year_lengths[isleap(y)]) {
+ idays -= year_lengths[isleap(y)];
+ if (increment_overflow(&y, 1))
+ return NULL;
+ }
+ tmp->tm_year = y;
+ if (increment_overflow(&tmp->tm_year, -TM_YEAR_BASE))
+ return NULL;
+ tmp->tm_yday = idays;
+ /*
+ ** The "extra" mods below avoid overflow problems.
+ */
+ tmp->tm_wday = EPOCH_WDAY +
+ ((y - EPOCH_YEAR) % DAYSPERWEEK) *
+ (DAYSPERNYEAR % DAYSPERWEEK) +
+ leaps_thru_end_of(y - 1) -
+ leaps_thru_end_of(EPOCH_YEAR - 1) +
+ idays;
+ tmp->tm_wday %= DAYSPERWEEK;
+ if (tmp->tm_wday < 0)
+ tmp->tm_wday += DAYSPERWEEK;
+ tmp->tm_hour = (int) (rem / SECSPERHOUR);
+ rem %= SECSPERHOUR;
+ tmp->tm_min = (int) (rem / SECSPERMIN);
+ /*
+ ** A positive leap second requires a special
+ ** representation. This uses "... ??:59:60" et seq.
+ */
+ tmp->tm_sec = (int) (rem % SECSPERMIN) + hit;
+ ip = mon_lengths[isleap(y)];
+ for (tmp->tm_mon = 0; idays >= ip[tmp->tm_mon]; ++(tmp->tm_mon))
+ idays -= ip[tmp->tm_mon];
+ tmp->tm_mday = (int) (idays + 1);
+ tmp->tm_isdst = 0;
+#ifdef TM_GMTOFF
+ tmp->TM_GMTOFF = offset;
+#endif /* defined TM_GMTOFF */
+ tmp->tm_usec = timep->tv_usec;
+ return tmp;
+}
+
+/*! \note
+** Adapted from code provided by Robert Elz, who writes:
+** The "best" way to do mktime I think is based on an idea of Bob
+** Kridle's (so its said...) from a long time ago.
+** It does a binary search of the time_t space. Since time_t's are
+** just 32 bits, its a max of 32 iterations (even at 64 bits it
+** would still be very reasonable).
+*/
+
+/*! \brief
+** Simplified normalize logic courtesy Paul Eggert.
+*/
+
+static int increment_overflow(int *number, int delta)
+{
+ int number0;
+
+ number0 = *number;
+ *number += delta;
+ return (*number < number0) != (delta < 0);
+}
+
+static int long_increment_overflow(long *number, int delta)
+{
+ long number0;
+
+ number0 = *number;
+ *number += delta;
+ return (*number < number0) != (delta < 0);
+}
+
+static int normalize_overflow(int *tensptr, int *unitsptr, const int base)
+{
+ int tensdelta;
+
+ tensdelta = (*unitsptr >= 0) ?
+ (*unitsptr / base) :
+ (-1 - (-1 - *unitsptr) / base);
+ *unitsptr -= tensdelta * base;
+ return increment_overflow(tensptr, tensdelta);
+}
+
+static int long_normalize_overflow(long *tensptr, int *unitsptr, const int base)
+{
+ int tensdelta;
+
+ tensdelta = (*unitsptr >= 0) ?
+ (*unitsptr / base) :
+ (-1 - (-1 - *unitsptr) / base);
+ *unitsptr -= tensdelta * base;
+ return long_increment_overflow(tensptr, tensdelta);
+}
+
+static int tmcomp(const struct ast_tm *atmp, const struct ast_tm *btmp)
+{
+ int result;
+
+ if ((result = (atmp->tm_year - btmp->tm_year)) == 0 &&
+ (result = (atmp->tm_mon - btmp->tm_mon)) == 0 &&
+ (result = (atmp->tm_mday - btmp->tm_mday)) == 0 &&
+ (result = (atmp->tm_hour - btmp->tm_hour)) == 0 &&
+ (result = (atmp->tm_min - btmp->tm_min)) == 0 &&
+ (result = (atmp->tm_sec - btmp->tm_sec)) == 0)
+ result = atmp->tm_usec - btmp->tm_usec;
+ return result;
+}
+
+static struct timeval time2sub(struct ast_tm *tmp, struct ast_tm * (* const funcp) (const struct timeval *, long, struct ast_tm *, const struct state *), const long offset, int *okayp, const int do_norm_secs, const struct state *sp)
+{
+ int dir;
+ int i, j;
+ int saved_seconds;
+ long li;
+ time_t lo;
+ time_t hi;
+ long y;
+ struct timeval newt = { 0, 0 };
+ struct timeval t = { 0, 0 };
+ struct ast_tm yourtm, mytm;
+
+ *okayp = FALSE;
+ yourtm = *tmp;
+ if (do_norm_secs) {
+ if (normalize_overflow(&yourtm.tm_min, &yourtm.tm_sec,
+ SECSPERMIN))
+ return WRONG;
+ }
+ if (normalize_overflow(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR))
+ return WRONG;
+ if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY))
+ return WRONG;
+ y = yourtm.tm_year;
+ if (long_normalize_overflow(&y, &yourtm.tm_mon, MONSPERYEAR))
+ return WRONG;
+ /*
+ ** Turn y into an actual year number for now.
+ ** It is converted back to an offset from TM_YEAR_BASE later.
+ */
+ if (long_increment_overflow(&y, TM_YEAR_BASE))
+ return WRONG;
+ while (yourtm.tm_mday <= 0) {
+ if (long_increment_overflow(&y, -1))
+ return WRONG;
+ li = y + (1 < yourtm.tm_mon);
+ yourtm.tm_mday += year_lengths[isleap(li)];
+ }
+ while (yourtm.tm_mday > DAYSPERLYEAR) {
+ li = y + (1 < yourtm.tm_mon);
+ yourtm.tm_mday -= year_lengths[isleap(li)];
+ if (long_increment_overflow(&y, 1))
+ return WRONG;
+ }
+ for ( ; ; ) {
+ i = mon_lengths[isleap(y)][yourtm.tm_mon];
+ if (yourtm.tm_mday <= i)
+ break;
+ yourtm.tm_mday -= i;
+ if (++yourtm.tm_mon >= MONSPERYEAR) {
+ yourtm.tm_mon = 0;
+ if (long_increment_overflow(&y, 1))
+ return WRONG;
+ }
+ }
+ if (long_increment_overflow(&y, -TM_YEAR_BASE))
+ return WRONG;
+ yourtm.tm_year = y;
+ if (yourtm.tm_year != y)
+ return WRONG;
+ if (yourtm.tm_sec >= 0 && yourtm.tm_sec < SECSPERMIN)
+ saved_seconds = 0;
+ else if (y + TM_YEAR_BASE < EPOCH_YEAR) {
+ /*
+ ** We can't set tm_sec to 0, because that might push the
+ ** time below the minimum representable time.
+ ** Set tm_sec to 59 instead.
+ ** This assumes that the minimum representable time is
+ ** not in the same minute that a leap second was deleted from,
+ ** which is a safer assumption than using 58 would be.
+ */
+ if (increment_overflow(&yourtm.tm_sec, 1 - SECSPERMIN))
+ return WRONG;
+ saved_seconds = yourtm.tm_sec;
+ yourtm.tm_sec = SECSPERMIN - 1;
+ } else {
+ saved_seconds = yourtm.tm_sec;
+ yourtm.tm_sec = 0;
+ }
+ /*
+ ** Do a binary search (this works whatever time_t's type is).
+ */
+ if (!TYPE_SIGNED(time_t)) {
+ lo = 0;
+ hi = lo - 1;
+ } else if (!TYPE_INTEGRAL(time_t)) {
+ if (sizeof(time_t) > sizeof(float))
+ hi = (time_t) DBL_MAX;
+ else hi = (time_t) FLT_MAX;
+ lo = -hi;
+ } else {
+ lo = 1;
+ for (i = 0; i < (int) TYPE_BIT(time_t) - 1; ++i)
+ lo *= 2;
+ hi = -(lo + 1);
+ }
+ for ( ; ; ) {
+ t.tv_sec = lo / 2 + hi / 2;
+ if (t.tv_sec < lo)
+ t.tv_sec = lo;
+ else if (t.tv_sec > hi)
+ t.tv_sec = hi;
+ if ((*funcp)(&t, offset, &mytm, sp) == NULL) {
+ /*
+ ** Assume that t is too extreme to be represented in
+ ** a struct ast_tm; arrange things so that it is less
+ ** extreme on the next pass.
+ */
+ dir = (t.tv_sec > 0) ? 1 : -1;
+ } else dir = tmcomp(&mytm, &yourtm);
+ if (dir != 0) {
+ if (t.tv_sec == lo) {
+ ++t.tv_sec;
+ if (t.tv_sec <= lo)
+ return WRONG;
+ ++lo;
+ } else if (t.tv_sec == hi) {
+ --t.tv_sec;
+ if (t.tv_sec >= hi)
+ return WRONG;
+ --hi;
+ }
+ if (lo > hi)
+ return WRONG;
+ if (dir > 0)
+ hi = t.tv_sec;
+ else lo = t.tv_sec;
+ continue;
+ }
+ if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst)
+ break;
+ /*
+ ** Right time, wrong type.
+ ** Hunt for right time, right type.
+ ** It's okay to guess wrong since the guess
+ ** gets checked.
+ */
+ /*
+ ** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's.
+ */
+ for (i = sp->typecnt - 1; i >= 0; --i) {
+ if (sp->ttis[i].tt_isdst != yourtm.tm_isdst)
+ continue;
+ for (j = sp->typecnt - 1; j >= 0; --j) {
+ if (sp->ttis[j].tt_isdst == yourtm.tm_isdst)
+ continue;
+ newt.tv_sec = t.tv_sec + sp->ttis[j].tt_gmtoff -
+ sp->ttis[i].tt_gmtoff;
+ if ((*funcp)(&newt, offset, &mytm, sp) == NULL)
+ continue;
+ if (tmcomp(&mytm, &yourtm) != 0)
+ continue;
+ if (mytm.tm_isdst != yourtm.tm_isdst)
+ continue;
+ /*
+ ** We have a match.
+ */
+ t = newt;
+ goto label;
+ }
+ }
+ return WRONG;
+ }
+label:
+ newt.tv_sec = t.tv_sec + saved_seconds;
+ if ((newt.tv_sec < t.tv_sec) != (saved_seconds < 0))
+ return WRONG;
+ t.tv_sec = newt.tv_sec;
+ if ((*funcp)(&t, offset, tmp, sp))
+ *okayp = TRUE;
+ return t;
+}
+
+static struct timeval time2(struct ast_tm *tmp, struct ast_tm * (* const funcp) (const struct timeval *, long, struct ast_tm*, const struct state *sp), const long offset, int *okayp, const struct state *sp)
+{
+ struct timeval t;
+
+ /*! \note
+ ** First try without normalization of seconds
+ ** (in case tm_sec contains a value associated with a leap second).
+ ** If that fails, try with normalization of seconds.
+ */
+ t = time2sub(tmp, funcp, offset, okayp, FALSE, sp);
+ return *okayp ? t : time2sub(tmp, funcp, offset, okayp, TRUE, sp);
+}
+
+static struct timeval time1(struct ast_tm *tmp, struct ast_tm * (* const funcp) (const struct timeval *, long, struct ast_tm *, const struct state *), const long offset, const struct state *sp)
+{
+ struct timeval t;
+ int samei, otheri;
+ int sameind, otherind;
+ int i;
+ int nseen;
+ int seen[TZ_MAX_TYPES];
+ int types[TZ_MAX_TYPES];
+ int okay;
+
+ if (tmp->tm_isdst > 1)
+ tmp->tm_isdst = 1;
+ t = time2(tmp, funcp, offset, &okay, sp);
+#ifdef PCTS
+ /*
+ ** PCTS code courtesy Grant Sullivan.
+ */
+ if (okay)
+ return t;
+ if (tmp->tm_isdst < 0)
+ tmp->tm_isdst = 0; /* reset to std and try again */
+#endif /* defined PCTS */
+#ifndef PCTS
+ if (okay || tmp->tm_isdst < 0)
+ return t;
+#endif /* !defined PCTS */
+ /*
+ ** We're supposed to assume that somebody took a time of one type
+ ** and did some math on it that yielded a "struct ast_tm" that's bad.
+ ** We try to divine the type they started from and adjust to the
+ ** type they need.
+ */
+ if (sp == NULL)
+ return WRONG;
+ for (i = 0; i < sp->typecnt; ++i)
+ seen[i] = FALSE;
+ nseen = 0;
+ for (i = sp->timecnt - 1; i >= 0; --i)
+ if (!seen[sp->types[i]]) {
+ seen[sp->types[i]] = TRUE;
+ types[nseen++] = sp->types[i];
+ }
+ for (sameind = 0; sameind < nseen; ++sameind) {
+ samei = types[sameind];
+ if (sp->ttis[samei].tt_isdst != tmp->tm_isdst)
+ continue;
+ for (otherind = 0; otherind < nseen; ++otherind) {
+ otheri = types[otherind];
+ if (sp->ttis[otheri].tt_isdst == tmp->tm_isdst)
+ continue;
+ tmp->tm_sec += sp->ttis[otheri].tt_gmtoff -
+ sp->ttis[samei].tt_gmtoff;
+ tmp->tm_isdst = !tmp->tm_isdst;
+ t = time2(tmp, funcp, offset, &okay, sp);
+ if (okay)
+ return t;
+ tmp->tm_sec -= sp->ttis[otheri].tt_gmtoff -
+ sp->ttis[samei].tt_gmtoff;
+ tmp->tm_isdst = !tmp->tm_isdst;
+ }
+ }
+ return WRONG;
+}
+
+struct timeval ast_mktime(struct ast_tm *tmp, const char *zone)
+{
+ const struct state *sp;
+ if (!(sp = ast_tzset(zone)))
+ return WRONG;
+ return time1(tmp, localsub, 0L, sp);
+}
+
+int ast_strftime(char *buf, size_t len, const char *tmp, const struct ast_tm *tm)
+{
+ size_t fmtlen = strlen(tmp) + 1;
+ char *format = ast_calloc(1, fmtlen), *fptr = format, *newfmt;
+ int decimals = -1, i, res;
+ long fraction;
+
+ if (!format)
+ return -1;
+ for (; *tmp; tmp++) {
+ if (*tmp == '%') {
+ switch (tmp[1]) {
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ if (tmp[2] != 'q')
+ goto defcase;
+ decimals = tmp[1] - '0';
+ tmp++;
+ /* Fall through */
+ case 'q': /* Milliseconds */
+ if (decimals == -1)
+ decimals = 3;
+
+ /* Juggle some memory to fit the item */
+ newfmt = ast_realloc(format, fmtlen + decimals);
+ if (!newfmt) {
+ ast_free(format);
+ return -1;
+ }
+ fptr = fptr - format + newfmt;
+ format = newfmt;
+ fmtlen += decimals;
+
+ /* Reduce the fraction of time to the accuracy needed */
+ for (i = 6, fraction = tm->tm_usec; i > decimals; i--)
+ fraction /= 10;
+ fptr += sprintf(fptr, "%0*ld", decimals, fraction);
+
+ /* Reset, in case more than one 'q' specifier exists */
+ decimals = -1;
+ tmp++;
+ break;
+ default:
+ goto defcase;
+ }
+ } else
+defcase: *fptr++ = *tmp;
+ }
+ *fptr = '\0';
+#undef strftime
+ res = (int)strftime(buf, len, format, (struct tm *)tm);
+ ast_free(format);
+ return res;
+}
+
diff --git a/trunk/main/stdtime/private.h b/trunk/main/stdtime/private.h
new file mode 100644
index 000000000..11793088d
--- /dev/null
+++ b/trunk/main/stdtime/private.h
@@ -0,0 +1,358 @@
+#ifndef PRIVATE_H
+
+#define PRIVATE_H
+
+/*
+** This file is in the public domain, so clarified as of
+** 1996-06-05 by Arthur David Olson.
+*/
+
+/*
+** This header is for use ONLY with the time conversion code.
+** There is no guarantee that it will remain unchanged,
+** or that it will remain at all.
+** Do NOT copy it to any system include directory.
+** Thank you!
+*/
+
+/*
+** ID
+*/
+
+#ifndef lint
+#ifndef NOID
+static char __attribute__((unused)) privatehid[] = "@(#)private.h 8.3";
+#endif /* !defined NOID */
+#endif /* !defined lint */
+
+#define GRANDPARENTED "Local time zone must be set--see zic manual page"
+
+/*
+** Defaults for preprocessor symbols.
+** You can override these in your C compiler options, e.g. `-DHAVE_ADJTIME=0'.
+*/
+
+#ifndef HAVE_ADJTIME
+#define HAVE_ADJTIME 1
+#endif /* !defined HAVE_ADJTIME */
+
+#ifndef HAVE_GETTEXT
+#define HAVE_GETTEXT 0
+#endif /* !defined HAVE_GETTEXT */
+
+#ifndef HAVE_INCOMPATIBLE_CTIME_R
+#define HAVE_INCOMPATIBLE_CTIME_R 0
+#endif /* !defined INCOMPATIBLE_CTIME_R */
+
+#ifndef HAVE_SETTIMEOFDAY
+#define HAVE_SETTIMEOFDAY 3
+#endif /* !defined HAVE_SETTIMEOFDAY */
+
+#ifndef HAVE_STRERROR
+#define HAVE_STRERROR 1
+#endif /* !defined HAVE_STRERROR */
+
+#ifndef HAVE_SYMLINK
+#define HAVE_SYMLINK 1
+#endif /* !defined HAVE_SYMLINK */
+
+#ifndef HAVE_SYS_STAT_H
+#define HAVE_SYS_STAT_H 1
+#endif /* !defined HAVE_SYS_STAT_H */
+
+#ifndef HAVE_SYS_WAIT_H
+#define HAVE_SYS_WAIT_H 1
+#endif /* !defined HAVE_SYS_WAIT_H */
+
+#ifndef HAVE_UNISTD_H
+#define HAVE_UNISTD_H 1
+#endif /* !defined HAVE_UNISTD_H */
+
+#ifndef HAVE_UTMPX_H
+#define HAVE_UTMPX_H 0
+#endif /* !defined HAVE_UTMPX_H */
+
+#ifndef LOCALE_HOME
+#define LOCALE_HOME "/usr/lib/locale"
+#endif /* !defined LOCALE_HOME */
+
+#if HAVE_INCOMPATIBLE_CTIME_R
+#define asctime_r _incompatible_asctime_r
+#define ctime_r _incompatible_ctime_r
+#endif /* HAVE_INCOMPATIBLE_CTIME_R */
+
+/*
+** Nested includes
+*/
+
+#include "sys/types.h" /* for time_t */
+#include "stdio.h"
+#include "errno.h"
+#include "string.h"
+#include "limits.h" /* for CHAR_BIT et al. */
+#include "time.h"
+#include "stdlib.h"
+
+#if HAVE_GETTEXT
+#include "libintl.h"
+#endif /* HAVE_GETTEXT */
+
+#if HAVE_SYS_WAIT_H
+#include <sys/wait.h> /* for WIFEXITED and WEXITSTATUS */
+#endif /* HAVE_SYS_WAIT_H */
+
+#ifndef WIFEXITED
+#define WIFEXITED(status) (((status) & 0xff) == 0)
+#endif /* !defined WIFEXITED */
+#ifndef WEXITSTATUS
+#define WEXITSTATUS(status) (((status) >> 8) & 0xff)
+#endif /* !defined WEXITSTATUS */
+
+#if HAVE_UNISTD_H
+#include "unistd.h" /* for F_OK and R_OK */
+#endif /* HAVE_UNISTD_H */
+
+#if !HAVE_UNISTD_H
+#ifndef F_OK
+#define F_OK 0
+#endif /* !defined F_OK */
+#ifndef R_OK
+#define R_OK 4
+#endif /* !defined R_OK */
+#endif /* !HAVE_UNISTD_H */
+
+/* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX. */
+#define is_digit(c) ((unsigned)(c) - '0' <= 9)
+
+/*
+** Define HAVE_STDINT_H's default value here, rather than at the
+** start, since __GLIBC__'s value depends on previously-included
+** files.
+** (glibc 2.1 and later have stdint.h, even with pre-C99 compilers.)
+*/
+#ifndef HAVE_STDINT_H
+#define HAVE_STDINT_H \
+ (199901 <= __STDC_VERSION__ || \
+ 2 < (__GLIBC__ + (0 < __GLIBC_MINOR__)))
+#endif /* !defined HAVE_STDINT_H */
+
+#if HAVE_STDINT_H
+#include "stdint.h"
+#endif /* !HAVE_STDINT_H */
+
+#ifndef INT_FAST64_MAX
+/* Pre-C99 GCC compilers define __LONG_LONG_MAX__ instead of LLONG_MAX. */
+#if defined LLONG_MAX || defined __LONG_LONG_MAX__
+typedef long long int_fast64_t;
+#else /* ! (defined LLONG_MAX || defined __LONG_LONG_MAX__) */
+#if (LONG_MAX >> 31) < 0xffffffff
+Please use a compiler that supports a 64-bit integer type (or wider);
+you may need to compile with "-DHAVE_STDINT_H".
+#endif /* (LONG_MAX >> 31) < 0xffffffff */
+typedef long int_fast64_t;
+#endif /* ! (defined LLONG_MAX || defined __LONG_LONG_MAX__) */
+#endif /* !defined INT_FAST64_MAX */
+
+#ifndef INT32_MAX
+#define INT32_MAX 0x7fffffff
+#endif /* !defined INT32_MAX */
+#ifndef INT32_MIN
+#define INT32_MIN (-1 - INT32_MAX)
+#endif /* !defined INT32_MIN */
+
+/*
+** Workarounds for compilers/systems.
+*/
+
+/*
+** If your compiler lacks prototypes, "#define P(x) ()".
+*/
+
+#ifndef P
+#define P(x) x
+#endif /* !defined P */
+
+/*
+** SunOS 4.1.1 headers lack EXIT_SUCCESS.
+*/
+
+#ifndef EXIT_SUCCESS
+#define EXIT_SUCCESS 0
+#endif /* !defined EXIT_SUCCESS */
+
+/*
+** SunOS 4.1.1 headers lack EXIT_FAILURE.
+*/
+
+#ifndef EXIT_FAILURE
+#define EXIT_FAILURE 1
+#endif /* !defined EXIT_FAILURE */
+
+/*
+** SunOS 4.1.1 headers lack FILENAME_MAX.
+*/
+
+#ifndef FILENAME_MAX
+
+#ifndef MAXPATHLEN
+#ifdef unix
+#include "sys/param.h"
+#endif /* defined unix */
+#endif /* !defined MAXPATHLEN */
+
+#ifdef MAXPATHLEN
+#define FILENAME_MAX MAXPATHLEN
+#endif /* defined MAXPATHLEN */
+#ifndef MAXPATHLEN
+#define FILENAME_MAX 1024 /* Pure guesswork */
+#endif /* !defined MAXPATHLEN */
+
+#endif /* !defined FILENAME_MAX */
+
+/*
+** SunOS 4.1.1 libraries lack remove.
+*/
+
+#ifndef remove
+extern int unlink P((const char * filename));
+#define remove unlink
+#endif /* !defined remove */
+
+/*
+** Some ancient errno.h implementations don't declare errno.
+** But some newer errno.h implementations define it as a macro.
+** Fix the former without affecting the latter.
+*/
+
+#ifndef errno
+extern int errno;
+#endif /* !defined errno */
+
+/*
+** Private function declarations.
+*/
+
+char * icalloc P((int nelem, int elsize));
+char * icatalloc P((char * old, const char * new));
+char * icpyalloc P((const char * string));
+char * imalloc P((int n));
+void * irealloc P((void * pointer, int size));
+void icfree P((char * pointer));
+void ifree P((char * pointer));
+const char * scheck P((const char * string, const char * format));
+
+/*
+** Finally, some convenience items.
+*/
+
+#ifndef TRUE
+#define TRUE 1
+#endif /* !defined TRUE */
+
+#ifndef FALSE
+#define FALSE 0
+#endif /* !defined FALSE */
+
+#ifndef TYPE_BIT
+#define TYPE_BIT(type) (sizeof (type) * CHAR_BIT)
+#endif /* !defined TYPE_BIT */
+
+#ifndef TYPE_SIGNED
+#define TYPE_SIGNED(type) (((type) -1) < 0)
+#endif /* !defined TYPE_SIGNED */
+
+/*
+** Since the definition of TYPE_INTEGRAL contains floating point numbers,
+** it cannot be used in preprocessor directives.
+*/
+
+#ifndef TYPE_INTEGRAL
+#define TYPE_INTEGRAL(type) (((type) 0.5) != 0.5)
+#endif /* !defined TYPE_INTEGRAL */
+
+#ifndef INT_STRLEN_MAXIMUM
+/*
+** 302 / 1000 is log10(2.0) rounded up.
+** Subtract one for the sign bit if the type is signed;
+** add one for integer division truncation;
+** add one more for a minus sign if the type is signed.
+*/
+#define INT_STRLEN_MAXIMUM(type) \
+ ((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 1000 + \
+ 1 + TYPE_SIGNED(type))
+#endif /* !defined INT_STRLEN_MAXIMUM */
+
+/*
+** INITIALIZE(x)
+*/
+
+#ifndef GNUC_or_lint
+#ifdef lint
+#define GNUC_or_lint
+#endif /* defined lint */
+#ifndef lint
+#ifdef __GNUC__
+#define GNUC_or_lint
+#endif /* defined __GNUC__ */
+#endif /* !defined lint */
+#endif /* !defined GNUC_or_lint */
+
+#ifndef INITIALIZE
+#ifdef GNUC_or_lint
+#define INITIALIZE(x) ((x) = 0)
+#endif /* defined GNUC_or_lint */
+#ifndef GNUC_or_lint
+#define INITIALIZE(x)
+#endif /* !defined GNUC_or_lint */
+#endif /* !defined INITIALIZE */
+
+/*
+** For the benefit of GNU folk...
+** `_(MSGID)' uses the current locale's message library string for MSGID.
+** The default is to use gettext if available, and use MSGID otherwise.
+*/
+
+#ifndef _
+#if HAVE_GETTEXT
+#define _(msgid) gettext(msgid)
+#else /* !HAVE_GETTEXT */
+#define _(msgid) msgid
+#endif /* !HAVE_GETTEXT */
+#endif /* !defined _ */
+
+#ifndef TZ_DOMAIN
+#define TZ_DOMAIN "tz"
+#endif /* !defined TZ_DOMAIN */
+
+#if HAVE_INCOMPATIBLE_CTIME_R
+#undef asctime_r
+#undef ctime_r
+char *asctime_r P((struct tm const *, char *));
+char *ctime_r P((time_t const *, char *));
+#endif /* HAVE_INCOMPATIBLE_CTIME_R */
+
+#ifndef YEARSPERREPEAT
+#define YEARSPERREPEAT 400 /* years before a Gregorian repeat */
+#endif /* !defined YEARSPERREPEAT */
+
+/*
+** The Gregorian year averages 365.2425 days, which is 31556952 seconds.
+*/
+
+#ifndef AVGSECSPERYEAR
+#define AVGSECSPERYEAR 31556952L
+#endif /* !defined AVGSECSPERYEAR */
+
+#ifndef SECSPERREPEAT
+#define SECSPERREPEAT ((int_fast64_t) YEARSPERREPEAT * (int_fast64_t) AVGSECSPERYEAR)
+#endif /* !defined SECSPERREPEAT */
+
+#ifndef SECSPERREPEAT_BITS
+#define SECSPERREPEAT_BITS 34 /* ceil(log2(SECSPERREPEAT)) */
+#endif /* !defined SECSPERREPEAT_BITS */
+
+/*
+** UNIX was a registered trademark of The Open Group in 2003.
+*/
+
+#endif /* !defined PRIVATE_H */
diff --git a/trunk/main/stdtime/test.c b/trunk/main/stdtime/test.c
new file mode 100644
index 000000000..9e8ce45da
--- /dev/null
+++ b/trunk/main/stdtime/test.c
@@ -0,0 +1,21 @@
+/*! \file
+ \brief Testing localtime functionality */
+
+#include "localtime.c"
+#include <sys/time.h>
+#include <stdio.h>
+
+int main(int argc, char **argv) {
+ struct timeval tv;
+ struct tm tm;
+ char *zone[4] = { "America/New_York", "America/Chicago", "America/Denver", "America/Los_Angeles" };
+ int i;
+
+ gettimeofday(&tv,NULL);
+
+ for (i=0;i<4;i++) {
+ ast_localtime(&tv.tv_sec,&tm,zone[i]);
+ printf("Localtime at %s is %04d/%02d/%02d %02d:%02d:%02d\n",zone[i],tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
+ }
+ return 0;
+}
diff --git a/trunk/main/stdtime/tzfile.h b/trunk/main/stdtime/tzfile.h
new file mode 100644
index 000000000..9201b967f
--- /dev/null
+++ b/trunk/main/stdtime/tzfile.h
@@ -0,0 +1,184 @@
+#ifndef TZFILE_H
+
+#define TZFILE_H
+
+/*
+** This file is in the public domain, so clarified as of
+** 1996-06-05 by Arthur David Olson.
+*/
+
+/*
+** This header is for use ONLY with the time conversion code.
+** There is no guarantee that it will remain unchanged,
+** or that it will remain at all.
+** Do NOT copy it to any system include directory.
+** Thank you!
+*/
+
+/*
+** ID
+*/
+
+#ifndef lint
+#ifndef NOID
+static char __attribute__((unused)) tzfilehid[] = "@(#)tzfile.h 8.1";
+#endif /* !defined NOID */
+#endif /* !defined lint */
+
+/*
+** Information about time zone files.
+*/
+
+#ifndef TZDIR
+#ifdef SOLARIS
+#define TZDIR "/usr/share/lib/zoneinfo"
+#else
+#define TZDIR "/usr/share/zoneinfo"
+#endif /* defined SOLARIS */
+#endif /* !defined TZDIR */
+
+#ifndef TZDEFAULT
+#define TZDEFAULT "localtime"
+#endif /* !defined TZDEFAULT */
+
+#ifndef TZDEFRULES
+#define TZDEFRULES "posixrules"
+#endif /* !defined TZDEFRULES */
+
+/*
+** Each file begins with. . .
+*/
+
+#define TZ_MAGIC "TZif"
+
+struct tzhead {
+ char tzh_magic[4]; /* TZ_MAGIC */
+ char tzh_version[1]; /* '\0' or '2' as of 2005 */
+ char tzh_reserved[15]; /* reserved--must be zero */
+ char tzh_ttisgmtcnt[4]; /* coded number of trans. time flags */
+ char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */
+ char tzh_leapcnt[4]; /* coded number of leap seconds */
+ char tzh_timecnt[4]; /* coded number of transition times */
+ char tzh_typecnt[4]; /* coded number of local time types */
+ char tzh_charcnt[4]; /* coded number of abbr. chars */
+};
+
+/*
+** . . .followed by. . .
+**
+** tzh_timecnt (char [4])s coded transition times a la time(2)
+** tzh_timecnt (unsigned char)s types of local time starting at above
+** tzh_typecnt repetitions of
+** one (char [4]) coded UTC offset in seconds
+** one (unsigned char) used to set tm_isdst
+** one (unsigned char) that's an abbreviation list index
+** tzh_charcnt (char)s '\0'-terminated zone abbreviations
+** tzh_leapcnt repetitions of
+** one (char [4]) coded leap second transition times
+** one (char [4]) total correction after above
+** tzh_ttisstdcnt (char)s indexed by type; if TRUE, transition
+** time is standard time, if FALSE,
+** transition time is wall clock time
+** if absent, transition times are
+** assumed to be wall clock time
+** tzh_ttisgmtcnt (char)s indexed by type; if TRUE, transition
+** time is UTC, if FALSE,
+** transition time is local time
+** if absent, transition times are
+** assumed to be local time
+*/
+
+/*
+** If tzh_version is '2' or greater, the above is followed by a second instance
+** of tzhead and a second instance of the data in which each coded transition
+** time uses 8 rather than 4 chars,
+** then a POSIX-TZ-environment-variable-style string for use in handling
+** instants after the last transition time stored in the file
+** (with nothing between the newlines if there is no POSIX representation for
+** such instants).
+*/
+
+/*
+** In the current implementation, "tzset()" refuses to deal with files that
+** exceed any of the limits below.
+*/
+
+#ifndef TZ_MAX_TIMES
+#define TZ_MAX_TIMES 1200
+#endif /* !defined TZ_MAX_TIMES */
+
+#ifndef TZ_MAX_TYPES
+#ifndef NOSOLAR
+#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */
+#endif /* !defined NOSOLAR */
+#ifdef NOSOLAR
+/*
+** Must be at least 14 for Europe/Riga as of Jan 12 1995,
+** as noted by Earl Chew.
+*/
+#define TZ_MAX_TYPES 20 /* Maximum number of local time types */
+#endif /* !defined NOSOLAR */
+#endif /* !defined TZ_MAX_TYPES */
+
+#ifndef TZ_MAX_CHARS
+#define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */
+ /* (limited by what unsigned chars can hold) */
+#endif /* !defined TZ_MAX_CHARS */
+
+#ifndef TZ_MAX_LEAPS
+#define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */
+#endif /* !defined TZ_MAX_LEAPS */
+
+#define SECSPERMIN 60
+#define MINSPERHOUR 60
+#define HOURSPERDAY 24
+#define DAYSPERWEEK 7
+#define DAYSPERNYEAR 365
+#define DAYSPERLYEAR 366
+#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
+#define SECSPERDAY ((long) SECSPERHOUR * HOURSPERDAY)
+#define MONSPERYEAR 12
+
+#define TM_SUNDAY 0
+#define TM_MONDAY 1
+#define TM_TUESDAY 2
+#define TM_WEDNESDAY 3
+#define TM_THURSDAY 4
+#define TM_FRIDAY 5
+#define TM_SATURDAY 6
+
+#define TM_JANUARY 0
+#define TM_FEBRUARY 1
+#define TM_MARCH 2
+#define TM_APRIL 3
+#define TM_MAY 4
+#define TM_JUNE 5
+#define TM_JULY 6
+#define TM_AUGUST 7
+#define TM_SEPTEMBER 8
+#define TM_OCTOBER 9
+#define TM_NOVEMBER 10
+#define TM_DECEMBER 11
+
+#define TM_YEAR_BASE 1900
+
+#define EPOCH_YEAR 1970
+#define EPOCH_WDAY TM_THURSDAY
+
+#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
+
+/*
+** Since everything in isleap is modulo 400 (or a factor of 400), we know that
+** isleap(y) == isleap(y % 400)
+** and so
+** isleap(a + b) == isleap((a + b) % 400)
+** or
+** isleap(a + b) == isleap(a % 400 + b % 400)
+** This is true even if % means modulo rather than Fortran remainder
+** (which is allowed by C89 but not C99).
+** We use this to avoid addition overflow problems.
+*/
+
+#define isleap_sum(a, b) isleap((a) % 400 + (b) % 400)
+
+#endif /* !defined TZFILE_H */
diff --git a/trunk/main/strcompat.c b/trunk/main/strcompat.c
new file mode 100644
index 000000000..37ee407cd
--- /dev/null
+++ b/trunk/main/strcompat.c
@@ -0,0 +1,463 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006, Digium, Inc.
+ *
+ * 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 Compatibility functions for strsep and strtoq missing on Solaris
+ */
+
+#include "asterisk.h"
+
+#include <ctype.h>
+
+#ifndef HAVE_STRSEP
+char *strsep(char **str, const char *delims)
+{
+ char *token;
+
+ if (!*str) {
+ /* No more tokens */
+ return NULL;
+ }
+
+ token = *str;
+ while (**str != '\0') {
+ if (strchr(delims, **str)) {
+ **str = '\0';
+ (*str)++;
+ return token;
+ }
+ (*str)++;
+ }
+
+ /* There is no other token */
+ *str = NULL;
+
+ return token;
+}
+#endif
+
+#ifndef HAVE_SETENV
+int setenv(const char *name, const char *value, int overwrite)
+{
+ unsigned char *buf;
+ int buflen;
+
+ buflen = strlen(name) + strlen(value) + 2;
+ buf = alloca(buflen);
+
+ if (!overwrite && getenv(name))
+ return 0;
+
+ snprintf(buf, buflen, "%s=%s", name, value);
+
+ return putenv(buf);
+}
+#endif
+
+#ifndef HAVE_UNSETENV
+int unsetenv(const char *name)
+{
+ return setenv(name, "", 0);
+}
+#endif
+
+#ifndef HAVE_STRCASESTR
+static char *upper(const char *orig, char *buf, int bufsize)
+{
+ int i = 0;
+
+ while (i < (bufsize - 1) && orig[i]) {
+ buf[i] = toupper(orig[i]);
+ i++;
+ }
+
+ buf[i] = '\0';
+
+ return buf;
+}
+
+char *strcasestr(const char *haystack, const char *needle)
+{
+ char *u1, *u2;
+ int u1len = strlen(haystack) + 1, u2len = strlen(needle) + 1;
+
+ u1 = alloca(u1len);
+ u2 = alloca(u2len);
+ if (u1 && u2) {
+ char *offset;
+ if (u2len > u1len) {
+ /* Needle bigger than haystack */
+ return NULL;
+ }
+ offset = strstr(upper(haystack, u1, u1len), upper(needle, u2, u2len));
+ if (offset) {
+ /* Return the offset into the original string */
+ return ((char *)((unsigned long)haystack + (unsigned long)(offset - u1)));
+ } else {
+ return NULL;
+ }
+ } else {
+ return NULL;
+ }
+}
+#endif /* !HAVE_STRCASESTR */
+
+#ifndef HAVE_STRNLEN
+size_t strnlen(const char *s, size_t n)
+{
+ size_t len;
+
+ for (len = 0; len < n; len++)
+ if (s[len] == '\0')
+ break;
+
+ return len;
+}
+#endif /* !HAVE_STRNLEN */
+
+#if !defined(HAVE_STRNDUP) && !defined(__AST_DEBUG_MALLOC)
+char *strndup(const char *s, size_t n)
+{
+ size_t len = strnlen(s, n);
+ char *new = malloc(len + 1);
+
+ if (!new)
+ return NULL;
+
+ new[len] = '\0';
+ return memcpy(new, s, len);
+}
+#endif /* !defined(HAVE_STRNDUP) && !defined(__AST_DEBUG_MALLOC) */
+
+#if !defined(HAVE_VASPRINTF) && !defined(__AST_DEBUG_MALLOC)
+int vasprintf(char **strp, const char *fmt, va_list ap)
+{
+ int size;
+ va_list ap2;
+ char s;
+
+ *strp = NULL;
+ va_copy(ap2, ap);
+ size = vsnprintf(&s, 1, fmt, ap2);
+ va_end(ap2);
+ *strp = malloc(size + 1);
+ if (!*strp)
+ return -1;
+ vsnprintf(*strp, size + 1, fmt, ap);
+
+ return size;
+}
+#endif /* !defined(HAVE_VASPRINTF) && !defined(__AST_DEBUG_MALLOC) */
+
+/*
+ * Based on Code from bsd-asprintf from OpenSSH
+ * Copyright (c) 2004 Darren Tucker.
+ *
+ * Based originally on asprintf.c from OpenBSD:
+ * Copyright (c) 1997 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#if !defined(HAVE_ASPRINTF) && !defined(__AST_DEBUG_MALLOC)
+int asprintf(char **str, const char *fmt, ...)
+{
+ va_list ap;
+ int ret;
+
+ *str = NULL;
+ va_start(ap, fmt);
+ ret = vasprintf(str, fmt, ap);
+ va_end(ap);
+
+ return ret;
+}
+#endif /* !defined(HAVE_ASPRINTF) && !defined(__AST_DEBUG_MALLOC) */
+
+#ifndef HAVE_STRTOQ
+#ifndef LONG_MIN
+#define LONG_MIN (-9223372036854775807L-1L)
+ /* min value of a "long int" */
+#endif
+#ifndef LONG_MAX
+#define LONG_MAX 9223372036854775807L
+ /* max value of a "long int" */
+#endif
+
+/*! \brief
+ * Convert a string to a quad integer.
+ *
+ * \note Ignores `locale' stuff. Assumes that the upper and lower case
+ * alphabets and digits are each contiguous.
+ */
+uint64_t strtoq(const char *nptr, char **endptr, int base)
+{
+ const char *s;
+ uint64_t acc;
+ unsigned char c;
+ uint64_t qbase, cutoff;
+ int neg, any, cutlim;
+
+ /*
+ * Skip white space and pick up leading +/- sign if any.
+ * If base is 0, allow 0x for hex and 0 for octal, else
+ * assume decimal; if base is already 16, allow 0x.
+ */
+ s = nptr;
+ do {
+ c = *s++;
+ } while (isspace(c));
+ if (c == '-') {
+ neg = 1;
+ c = *s++;
+ } else {
+ neg = 0;
+ if (c == '+')
+ c = *s++;
+ }
+ if ((base == 0 || base == 16) &&
+ c == '\0' && (*s == 'x' || *s == 'X')) {
+ c = s[1];
+ s += 2;
+ base = 16;
+ }
+ if (base == 0)
+ base = c == '\0' ? 8 : 10;
+
+ /*
+ * Compute the cutoff value between legal numbers and illegal
+ * numbers. That is the largest legal value, divided by the
+ * base. An input number that is greater than this value, if
+ * followed by a legal input character, is too big. One that
+ * is equal to this value may be valid or not; the limit
+ * between valid and invalid numbers is then based on the last
+ * digit. For instance, if the range for quads is
+ * [-9223372036854775808..9223372036854775807] and the input base
+ * is 10, cutoff will be set to 922337203685477580 and cutlim to
+ * either 7 (neg==0) or 8 (neg==1), meaning that if we have
+ * accumulated a value > 922337203685477580, or equal but the
+ * next digit is > 7 (or 8), the number is too big, and we will
+ * return a range error.
+ *
+ * Set any if any `digits' consumed; make it negative to indicate
+ * overflow.
+ */
+ qbase = (unsigned)base;
+ cutoff = neg ? (uint64_t)-(LONG_MIN + LONG_MAX) + LONG_MAX : LONG_MAX;
+ cutlim = cutoff % qbase;
+ cutoff /= qbase;
+ for (acc = 0, any = 0;; c = *s++) {
+ if (!isascii(c))
+ break;
+ if (isdigit(c))
+ c -= '\0';
+ else if (isalpha(c))
+ c -= isupper(c) ? 'A' - 10 : 'a' - 10;
+ else
+ break;
+ if (c >= base)
+ break;
+ if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
+ any = -1;
+ else {
+ any = 1;
+ acc *= qbase;
+ acc += c;
+ }
+ }
+ if (any < 0) {
+ acc = neg ? LONG_MIN : LONG_MAX;
+ } else if (neg)
+ acc = -acc;
+ if (endptr != 0)
+ *((const char **)endptr) = any ? s - 1 : nptr;
+ return acc;
+}
+#endif /* !HAVE_STRTOQ */
+
+#ifndef HAVE_GETLOADAVG
+#ifdef linux
+/*! \brief Alternative method of getting load avg on Linux only */
+int getloadavg(double *list, int nelem)
+{
+ FILE *LOADAVG;
+ double avg[3] = { 0.0, 0.0, 0.0 };
+ int i, res = -1;
+
+ if ((LOADAVG = fopen("/proc/loadavg", "r"))) {
+ fscanf(LOADAVG, "%lf %lf %lf", &avg[0], &avg[1], &avg[2]);
+ res = 0;
+ fclose(LOADAVG);
+ }
+
+ for (i = 0; (i < nelem) && (i < 3); i++) {
+ list[i] = avg[i];
+ }
+
+ return res;
+}
+#else /* !linux */
+/*! \brief Return something that won't cancel the call, but still return -1, in case
+ * we correct the implementation to check return value */
+int getloadavg(double *list, int nelem)
+{
+ int i;
+
+ for (i = 0; i < nelem; i++) {
+ list[i] = 0.1;
+ }
+ return -1;
+}
+#endif /* linux */
+#endif /* !HAVE_GETLOADAVG */
+
+
+/*
+ * For strlcat()
+ *
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Appends src to string dst of size siz (unlike strncat, siz is the
+ * full size of dst, not space left). At most siz-1 characters
+ * will be copied. Always NUL terminates (unless siz <= strlen(dst)).
+ * Returns strlen(src) + MIN(siz, strlen(initial dst)).
+ * If retval >= siz, truncation occurred.
+ */
+#ifndef HAVE_STRLCAT
+size_t strlcat(char *dst, const char *src, size_t siz)
+{
+ register char *d = dst;
+ register const char *s = src;
+ register size_t n = siz;
+ size_t dlen;
+
+ /* Find the end of dst and adjust bytes left but don't go past end */
+ while (n-- != 0 && *d != '\0')
+ d++;
+ dlen = d - dst;
+ n = siz - dlen;
+
+ if (n == 0)
+ return dlen + strlen(s);
+
+ while (*s != '\0') {
+ if (n != 1) {
+ *d++ = *s;
+ n--;
+ }
+ s++;
+ }
+ *d = '\0';
+
+ return dlen + (s - src); /* count does not include NUL */
+}
+#endif /* HAVE_STRLCAT */
+
+/*
+ * For strlcpy()
+ *
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Copy src to string dst of size siz. At most siz-1 characters
+ * will be copied. Always NUL terminates (unless siz == 0).
+ * Returns strlen(src); if retval >= siz, truncation occurred.
+ */
+#ifndef HAVE_STRLCPY
+size_t strlcpy(char *dst, const char *src, size_t siz)
+{
+ register char *d = dst;
+ register const char *s = src;
+ register size_t n = siz;
+
+ /* Copy as many bytes as will fit */
+ if (n != 0 && --n != 0) {
+ do {
+ if ((*d++ = *s++) == 0)
+ break;
+ } while (--n != 0);
+ }
+
+ /* Not enough room in dst, add NUL and traverse rest of src */
+ if (n == 0) {
+ if (siz != 0)
+ *d = '\0'; /* NUL-terminate dst */
+ while (*s++)
+ ;
+ }
+
+ return s - src - 1; /* count does not include NUL */
+}
+#endif /* HAVE_STRLCPY */
diff --git a/trunk/main/tcptls.c b/trunk/main/tcptls.c
new file mode 100644
index 000000000..27faa0c18
--- /dev/null
+++ b/trunk/main/tcptls.c
@@ -0,0 +1,452 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2007 - 2008, Digium, Inc.
+ *
+ * Luigi Rizzo (TCP and TLS server code)
+ * Brett Bryant <brettbryant@gmail.com> (updated for client support)
+ *
+ * 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 Code to support TCP and TLS server/client
+ *
+ * \author Luigi Rizzo
+ * \author Brett Bryant <brettbryant@gmail.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#include <sys/signal.h>
+
+#include "asterisk/compat.h"
+#include "asterisk/tcptls.h"
+#include "asterisk/http.h"
+#include "asterisk/utils.h"
+#include "asterisk/strings.h"
+#include "asterisk/options.h"
+#include "asterisk/manager.h"
+
+/*!
+ * replacement read/write functions for SSL support.
+ * We use wrappers rather than SSL_read/SSL_write directly so
+ * we can put in some debugging.
+ */
+
+#ifdef DO_SSL
+static HOOK_T ssl_read(void *cookie, char *buf, LEN_T len)
+{
+ int i = SSL_read(cookie, buf, len-1);
+#if 0
+ if (i >= 0)
+ buf[i] = '\0';
+ ast_verbose("ssl read size %d returns %d <%s>\n", (int)len, i, buf);
+#endif
+ return i;
+}
+
+static HOOK_T ssl_write(void *cookie, const char *buf, LEN_T len)
+{
+#if 0
+ char *s = alloca(len+1);
+ strncpy(s, buf, len);
+ s[len] = '\0';
+ ast_verbose("ssl write size %d <%s>\n", (int)len, s);
+#endif
+ return SSL_write(cookie, buf, len);
+}
+
+static int ssl_close(void *cookie)
+{
+ close(SSL_get_fd(cookie));
+ SSL_shutdown(cookie);
+ SSL_free(cookie);
+ return 0;
+}
+#endif /* DO_SSL */
+
+HOOK_T server_read(struct server_instance *ser, void *buf, size_t count)
+{
+ if (!ser->ssl)
+ return read(ser->fd, buf, count);
+#ifdef DO_SSL
+ else
+ return ssl_read(ser->ssl, buf, count);
+#endif
+}
+
+HOOK_T server_write(struct server_instance *ser, void *buf, size_t count)
+{
+ if (!ser->ssl)
+ return write(ser->fd, buf, count);
+#ifdef DO_SSL
+ else
+ return ssl_write(ser->ssl, buf, count);
+#endif
+}
+
+void *server_root(void *data)
+{
+ struct server_args *desc = data;
+ int fd;
+ struct sockaddr_in sin;
+ socklen_t sinlen;
+ struct server_instance *ser;
+ pthread_t launched;
+
+ for (;;) {
+ int i, flags;
+
+ if (desc->periodic_fn)
+ desc->periodic_fn(desc);
+ i = ast_wait_for_input(desc->accept_fd, desc->poll_timeout);
+ if (i <= 0)
+ continue;
+ sinlen = sizeof(sin);
+ fd = accept(desc->accept_fd, (struct sockaddr *)&sin, &sinlen);
+ if (fd < 0) {
+ if ((errno != EAGAIN) && (errno != EINTR))
+ ast_log(LOG_WARNING, "Accept failed: %s\n", strerror(errno));
+ continue;
+ }
+ ser = ast_calloc(1, sizeof(*ser));
+ if (!ser) {
+ ast_log(LOG_WARNING, "No memory for new session: %s\n", strerror(errno));
+ close(fd);
+ continue;
+ }
+ flags = fcntl(fd, F_GETFL);
+ fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
+ ser->fd = fd;
+ ser->parent = desc;
+ memcpy(&ser->requestor, &sin, sizeof(ser->requestor));
+
+ ser->client = 0;
+
+ if (ast_pthread_create_detached_background(&launched, NULL, ast_make_file_from_fd, ser)) {
+ ast_log(LOG_WARNING, "Unable to launch helper thread: %s\n", strerror(errno));
+ close(ser->fd);
+ ast_free(ser);
+ }
+ }
+ return NULL;
+}
+
+static int __ssl_setup(struct ast_tls_config *cfg, int client)
+{
+#ifndef DO_SSL
+ cfg->enabled = 0;
+ return 0;
+#else
+ if (!cfg->enabled)
+ return 0;
+
+ SSL_load_error_strings();
+ SSLeay_add_ssl_algorithms();
+
+ if (!(cfg->ssl_ctx = SSL_CTX_new( client ? SSLv23_client_method() : SSLv23_server_method() ))) {
+ ast_log(LOG_DEBUG, "Sorry, SSL_CTX_new call returned null...\n");
+ cfg->enabled = 0;
+ return 0;
+ }
+ if (!ast_strlen_zero(cfg->certfile)) {
+ if (SSL_CTX_use_certificate_file(cfg->ssl_ctx, cfg->certfile, SSL_FILETYPE_PEM) == 0 ||
+ SSL_CTX_use_PrivateKey_file(cfg->ssl_ctx, cfg->certfile, SSL_FILETYPE_PEM) == 0 ||
+ SSL_CTX_check_private_key(cfg->ssl_ctx) == 0 ) {
+ if (!client) {
+ /* Clients don't need a certificate, but if its setup we can use it */
+ ast_verbose("ssl cert error <%s>", cfg->certfile);
+ sleep(2);
+ cfg->enabled = 0;
+ return 0;
+ }
+ }
+ }
+ if (!ast_strlen_zero(cfg->cipher)) {
+ if (SSL_CTX_set_cipher_list(cfg->ssl_ctx, cfg->cipher) == 0 ) {
+ if (!client) {
+ ast_verbose("ssl cipher error <%s>", cfg->cipher);
+ sleep(2);
+ cfg->enabled = 0;
+ return 0;
+ }
+ }
+ }
+ if (!ast_strlen_zero(cfg->cafile) || !ast_strlen_zero(cfg->capath)) {
+ if (SSL_CTX_load_verify_locations(cfg->ssl_ctx, S_OR(cfg->cafile, NULL), S_OR(cfg->capath,NULL)) == 0)
+ ast_verbose("ssl CA file(%s)/path(%s) error\n", cfg->cafile, cfg->capath);
+ }
+
+ ast_verbose("ssl cert ok\n");
+ return 1;
+#endif
+}
+
+int ssl_setup(struct ast_tls_config *cfg)
+{
+ return __ssl_setup(cfg, 0);
+}
+
+/*! A generic client routine for a TCP client
+ * and starts a thread for handling accept()
+ */
+struct server_instance *client_start(struct server_args *desc)
+{
+ int flags;
+ struct server_instance *ser = NULL;
+
+ /* Do nothing if nothing has changed */
+ if(!memcmp(&desc->oldsin, &desc->sin, sizeof(desc->oldsin))) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Nothing changed in %s\n", desc->name);
+ return NULL;
+ }
+
+ desc->oldsin = desc->sin;
+
+ if (desc->accept_fd != -1)
+ close(desc->accept_fd);
+
+ desc->accept_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (desc->accept_fd < 0) {
+ ast_log(LOG_WARNING, "Unable to allocate socket for %s: %s\n",
+ desc->name, strerror(errno));
+ return NULL;
+ }
+
+ if (connect(desc->accept_fd, (const struct sockaddr *)&desc->sin, sizeof(desc->sin))) {
+ ast_log(LOG_NOTICE, "Unable to connect %s to %s:%d: %s\n",
+ desc->name,
+ ast_inet_ntoa(desc->sin.sin_addr), ntohs(desc->sin.sin_port),
+ strerror(errno));
+ goto error;
+ }
+
+ if (!(ser = ast_calloc(1, sizeof(*ser))))
+ goto error;
+
+ flags = fcntl(desc->accept_fd, F_GETFL);
+ fcntl(desc->accept_fd, F_SETFL, flags & ~O_NONBLOCK);
+
+ ser->fd = desc->accept_fd;
+ ser->parent = desc;
+ ser->parent->worker_fn = NULL;
+ memcpy(&ser->requestor, &desc->sin, sizeof(ser->requestor));
+
+ ser->client = 1;
+
+ if (desc->tls_cfg) {
+ desc->tls_cfg->enabled = 1;
+ __ssl_setup(desc->tls_cfg, 1);
+ }
+
+ if (!ast_make_file_from_fd(ser))
+ goto error;
+
+ return ser;
+
+error:
+ close(desc->accept_fd);
+ desc->accept_fd = -1;
+ if (ser)
+ ast_free(ser);
+ return NULL;
+}
+
+/*!
+ * This is a generic (re)start routine for a TCP server,
+ * which does the socket/bind/listen and starts a thread for handling
+ * accept().
+ */
+
+void server_start(struct server_args *desc)
+{
+ int flags;
+ int x = 1;
+
+ /* Do nothing if nothing has changed */
+ if (!memcmp(&desc->oldsin, &desc->sin, sizeof(desc->oldsin))) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Nothing changed in %s\n", desc->name);
+ return;
+ }
+
+ desc->oldsin = desc->sin;
+
+ /* Shutdown a running server if there is one */
+ if (desc->master != AST_PTHREADT_NULL) {
+ pthread_cancel(desc->master);
+ pthread_kill(desc->master, SIGURG);
+ pthread_join(desc->master, NULL);
+ }
+
+ if (desc->accept_fd != -1)
+ close(desc->accept_fd);
+
+ /* If there's no new server, stop here */
+ if (desc->sin.sin_family == 0)
+ return;
+
+ desc->accept_fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (desc->accept_fd < 0) {
+ ast_log(LOG_WARNING, "Unable to allocate socket for %s: %s\n",
+ desc->name, strerror(errno));
+ return;
+ }
+
+ setsockopt(desc->accept_fd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
+ if (bind(desc->accept_fd, (struct sockaddr *)&desc->sin, sizeof(desc->sin))) {
+ ast_log(LOG_NOTICE, "Unable to bind %s to %s:%d: %s\n",
+ desc->name,
+ ast_inet_ntoa(desc->sin.sin_addr), ntohs(desc->sin.sin_port),
+ strerror(errno));
+ goto error;
+ }
+ if (listen(desc->accept_fd, 10)) {
+ ast_log(LOG_NOTICE, "Unable to listen for %s!\n", desc->name);
+ goto error;
+ }
+ flags = fcntl(desc->accept_fd, F_GETFL);
+ fcntl(desc->accept_fd, F_SETFL, flags | O_NONBLOCK);
+ if (ast_pthread_create_background(&desc->master, NULL, desc->accept_fn, desc)) {
+ ast_log(LOG_NOTICE, "Unable to launch %s on %s:%d: %s\n",
+ desc->name,
+ ast_inet_ntoa(desc->sin.sin_addr), ntohs(desc->sin.sin_port),
+ strerror(errno));
+ goto error;
+ }
+ return;
+
+error:
+ close(desc->accept_fd);
+ desc->accept_fd = -1;
+}
+
+void server_stop(struct server_args *desc)
+{
+ /* Shutdown a running server if there is one */
+ if (desc->master != AST_PTHREADT_NULL) {
+ pthread_cancel(desc->master);
+ pthread_kill(desc->master, SIGURG);
+ pthread_join(desc->master, NULL);
+ }
+ if (desc->accept_fd != -1)
+ close(desc->accept_fd);
+ desc->accept_fd = -1;
+}
+
+/*!
+* creates a FILE * from the fd passed by the accept thread.
+* This operation is potentially expensive (certificate verification),
+* so we do it in the child thread context.
+*/
+void *ast_make_file_from_fd(void *data)
+{
+ struct server_instance *ser = data;
+ int (*ssl_setup)(SSL *) = (ser->client) ? SSL_connect : SSL_accept;
+ int ret;
+ char err[256];
+
+ /*
+ * open a FILE * as appropriate.
+ */
+ if (!ser->parent->tls_cfg)
+ ser->f = fdopen(ser->fd, "w+");
+#ifdef DO_SSL
+ else if ( (ser->ssl = SSL_new(ser->parent->tls_cfg->ssl_ctx)) ) {
+ SSL_set_fd(ser->ssl, ser->fd);
+ if ((ret = ssl_setup(ser->ssl)) <= 0) {
+ if(option_verbose > 1)
+ ast_verbose(VERBOSE_PREFIX_2 "Problem setting up ssl connection: %s\n", ERR_error_string(ERR_get_error(), err));
+ } else {
+#if defined(HAVE_FUNOPEN) /* the BSD interface */
+ ser->f = funopen(ser->ssl, ssl_read, ssl_write, NULL, ssl_close);
+
+#elif defined(HAVE_FOPENCOOKIE) /* the glibc/linux interface */
+ static const cookie_io_functions_t cookie_funcs = {
+ ssl_read, ssl_write, NULL, ssl_close
+ };
+ ser->f = fopencookie(ser->ssl, "w+", cookie_funcs);
+#else
+ /* could add other methods here */
+ ast_log(LOG_WARNING, "no ser->f methods attempted!");
+#endif
+ if ((ser->client && !ast_test_flag(&ser->parent->tls_cfg->flags, AST_SSL_DONT_VERIFY_SERVER))
+ || (!ser->client && ast_test_flag(&ser->parent->tls_cfg->flags, AST_SSL_VERIFY_CLIENT))) {
+ X509 *peer;
+ long res;
+ peer = SSL_get_peer_certificate(ser->ssl);
+ if (!peer)
+ ast_log(LOG_WARNING, "No peer certificate\n");
+ res = SSL_get_verify_result(ser->ssl);
+ if (res != X509_V_OK)
+ ast_log(LOG_WARNING, "Certificate did not verify: %s\n", X509_verify_cert_error_string(res));
+ if (!ast_test_flag(&ser->parent->tls_cfg->flags, AST_SSL_IGNORE_COMMON_NAME)) {
+ ASN1_STRING *str;
+ unsigned char *str2;
+ X509_NAME *name = X509_get_subject_name(peer);
+ int pos = -1;
+ int found = 0;
+
+ for (;;) {
+ /* Walk the certificate to check all available "Common Name" */
+ /* XXX Probably should do a gethostbyname on the hostname and compare that as well */
+ pos = X509_NAME_get_index_by_NID(name, NID_commonName, pos);
+ if (pos < 0)
+ break;
+ str = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, pos));
+ ASN1_STRING_to_UTF8(&str2, str);
+ if (str2) {
+ if (!strcasecmp(ser->parent->hostname, (char *) str2))
+ found = 1;
+ ast_log(LOG_DEBUG, "SSL Common Name compare s1='%s' s2='%s'\n", ser->parent->hostname, str2);
+ OPENSSL_free(str2);
+ }
+ if (found)
+ break;
+ }
+ if (!found) {
+ ast_log(LOG_WARNING, "Certificate common name did not match (%s)\n", ser->parent->hostname);
+ if (peer)
+ X509_free(peer);
+ fclose(ser->f);
+ return NULL;
+ }
+ }
+ if (peer)
+ X509_free(peer);
+ }
+ }
+ if (!ser->f) /* no success opening descriptor stacking */
+ SSL_free(ser->ssl);
+ }
+#endif /* DO_SSL */
+
+ if (!ser->f) {
+ close(ser->fd);
+ ast_log(LOG_WARNING, "FILE * open failed!\n");
+ ast_free(ser);
+ return NULL;
+ }
+
+ if (ser && ser->parent->worker_fn)
+ return ser->parent->worker_fn(ser);
+ else
+ return ser;
+}
diff --git a/trunk/main/tdd.c b/trunk/main/tdd.c
new file mode 100644
index 000000000..13dda7e11
--- /dev/null
+++ b/trunk/main/tdd.c
@@ -0,0 +1,338 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * Includes code and algorithms from the Zapata library.
+ *
+ * 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 TTY/TDD Generation support
+ *
+ * \author Mark Spencer <markster@digium.com>
+ *
+ * \note Includes code and algorithms from the Zapata library.
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <time.h>
+#include <math.h>
+#include <ctype.h>
+
+#include "asterisk/ulaw.h"
+#include "asterisk/tdd.h"
+#include "asterisk/fskmodem.h"
+#include "ecdisa.h"
+
+struct tdd_state {
+ fsk_data fskd;
+ char rawdata[256];
+ short oldstuff[4096];
+ int oldlen;
+ int pos;
+ int modo;
+ int mode;
+ int charnum;
+};
+
+static float dr[4], di[4];
+static float tddsb = 176.0; /* 45.5 baud */
+
+#define TDD_SPACE 1800.0 /* 1800 hz for "0" */
+#define TDD_MARK 1400.0 /* 1400 hz for "1" */
+
+static int tdd_decode_baudot(struct tdd_state *tdd,unsigned char data) /* covert baudot into ASCII */
+{
+ static char ltrs[32] = { '<','E','\n','A',' ','S','I','U',
+ '\n','D','R','J','N','F','C','K',
+ 'T','Z','L','W','H','Y','P','Q',
+ 'O','B','G','^','M','X','V','^' };
+ static char figs[32] = { '<','3','\n','-',' ','\'','8','7',
+ '\n','$','4','\'',',','!',':','(',
+ '5','\"',')','2','=','6','0','1',
+ '9','?','+','^','.','/',';','^' };
+ int d = 0; /* return 0 if not decodeable */
+ switch (data) {
+ case 0x1f:
+ tdd->modo = 0;
+ break;
+ case 0x1b:
+ tdd->modo = 1;
+ break;
+ default:
+ if (tdd->modo == 0)
+ d = ltrs[data];
+ else
+ d = figs[data];
+ break;
+ }
+ return d;
+}
+
+void tdd_init(void)
+{
+ /* Initialize stuff for inverse FFT */
+ dr[0] = cos(TDD_SPACE * 2.0 * M_PI / 8000.0);
+ di[0] = sin(TDD_SPACE * 2.0 * M_PI / 8000.0);
+ dr[1] = cos(TDD_MARK * 2.0 * M_PI / 8000.0);
+ di[1] = sin(TDD_MARK * 2.0 * M_PI / 8000.0);
+}
+
+struct tdd_state *tdd_new(void)
+{
+ struct tdd_state *tdd;
+ tdd = calloc(1, sizeof(*tdd));
+ if (tdd) {
+ tdd->fskd.ispb = 176; /* 45.5 baud */
+ /* Set up for 45.5 / 8000 freq *32 to allow ints */
+ tdd->fskd.pllispb = (int)((8000 * 32 * 2) / 90);
+ tdd->fskd.pllids = tdd->fskd.pllispb/32;
+ tdd->fskd.pllispb2 = tdd->fskd.pllispb/2;
+ tdd->fskd.hdlc = 0; /* Async */
+ tdd->fskd.nbit = 5; /* 5 bits */
+ tdd->fskd.instop = 1; /* integer rep of 1.5 stop bits */
+ tdd->fskd.parity = 0; /* No parity */
+ tdd->fskd.bw=0; /* Filter 75 Hz */
+ tdd->fskd.f_mark_idx = 0; /* 1400 Hz */
+ tdd->fskd.f_space_idx = 1; /* 1800 Hz */
+ tdd->fskd.xi0 = 0;
+ tdd->fskd.state = 0;
+ tdd->pos = 0;
+ tdd->mode = 0;
+ tdd->charnum = 0;
+ fskmodem_init(&tdd->fskd);
+ } else
+ ast_log(LOG_WARNING, "Out of memory\n");
+ return tdd;
+}
+
+int ast_tdd_gen_ecdisa(unsigned char *outbuf, int len)
+{
+ int pos = 0;
+ int cnt;
+ while (len) {
+ cnt = len > sizeof(ecdisa) ? sizeof(ecdisa) : len;
+ memcpy(outbuf + pos, ecdisa, cnt);
+ pos += cnt;
+ len -= cnt;
+ }
+ return 0;
+}
+
+int tdd_feed(struct tdd_state *tdd, unsigned char *ubuf, int len)
+{
+ int mylen = len;
+ int olen;
+ int b = 'X';
+ int res;
+ int c,x;
+ short *buf = calloc(1, 2 * len + tdd->oldlen);
+ short *obuf = buf;
+ if (!buf) {
+ ast_log(LOG_WARNING, "Out of memory\n");
+ return -1;
+ }
+ memcpy(buf, tdd->oldstuff, tdd->oldlen);
+ mylen += tdd->oldlen/2;
+ for (x = 0; x < len; x++)
+ buf[x + tdd->oldlen / 2] = AST_MULAW(ubuf[x]);
+ c = res = 0;
+ while (mylen >= 1320) { /* has to have enough to work on */
+ olen = mylen;
+ res = fsk_serial(&tdd->fskd, buf, &mylen, &b);
+ if (mylen < 0) {
+ ast_log(LOG_ERROR, "fsk_serial made mylen < 0 (%d) (olen was %d)\n", mylen, olen);
+ free(obuf);
+ return -1;
+ }
+ buf += (olen - mylen);
+ if (res < 0) {
+ ast_log(LOG_NOTICE, "fsk_serial failed\n");
+ free(obuf);
+ return -1;
+ }
+ if (res == 1) {
+ /* Ignore invalid bytes */
+ if (b > 0x7f)
+ continue;
+ c = tdd_decode_baudot(tdd,b);
+ if ((c < 1) || (c > 126))
+ continue; /* if not valid */
+ break;
+ }
+ }
+ if (mylen) {
+ memcpy(tdd->oldstuff, buf, mylen * 2);
+ tdd->oldlen = mylen * 2;
+ } else
+ tdd->oldlen = 0;
+ free(obuf);
+ if (res) {
+ tdd->mode = 2;
+/* put it in mode where it
+ reliably puts teleprinter in correct shift mode */
+ return(c);
+ }
+ return 0;
+}
+
+void tdd_free(struct tdd_state *tdd)
+{
+ free(tdd);
+}
+
+static inline float tdd_getcarrier(float *cr, float *ci, int bit)
+{
+ /* Move along. There's nothing to see here... */
+ float t;
+ t = *cr * dr[bit] - *ci * di[bit];
+ *ci = *cr * di[bit] + *ci * dr[bit];
+ *cr = t;
+
+ t = 2.0 - (*cr * *cr + *ci * *ci);
+ *cr *= t;
+ *ci *= t;
+ return *cr;
+}
+
+#define PUT_BYTE(a) do { \
+ *(buf++) = (a); \
+ bytes++; \
+} while(0)
+
+#define PUT_AUDIO_SAMPLE(y) do { \
+ int index = (short)(rint(8192.0 * (y))); \
+ *(buf++) = AST_LIN2MU(index); \
+ bytes++; \
+} while(0)
+
+#define PUT_TDD_MARKMS do { \
+ int x; \
+ for (x=0;x<8;x++) \
+ PUT_AUDIO_SAMPLE(tdd_getcarrier(&cr, &ci, 1)); \
+} while(0)
+
+#define PUT_TDD_BAUD(bit) do { \
+ while (scont < tddsb) { \
+ PUT_AUDIO_SAMPLE(tdd_getcarrier(&cr, &ci, bit)); \
+ scont += 1.0; \
+ } \
+ scont -= tddsb; \
+} while(0)
+
+#define PUT_TDD_STOP do { \
+ while (scont < (tddsb * 1.5)) { \
+ PUT_AUDIO_SAMPLE(tdd_getcarrier(&cr, &ci, 1)); \
+ scont += 1.0; \
+ } \
+ scont -= (tddsb * 1.5); \
+} while(0)
+
+
+#define PUT_TDD(byte) do { \
+ int z; \
+ unsigned char b = (byte); \
+ PUT_TDD_BAUD(0); /* Start bit */ \
+ for (z = 0; z < 5; z++) { \
+ PUT_TDD_BAUD(b & 1); \
+ b >>= 1; \
+ } \
+ PUT_TDD_STOP; /* Stop bit */ \
+} while(0);
+
+/*! Generate TDD hold tone */
+int tdd_gen_holdtone(unsigned char *buf)
+{
+ int bytes=0;
+ float scont=0.0,cr=1.0,ci=0.0;
+ while(scont < tddsb*10.0) {
+ PUT_AUDIO_SAMPLE(tdd_getcarrier(&cr, &ci, 1));
+ scont += 1.0;
+ }
+ return bytes;
+}
+
+int tdd_generate(struct tdd_state *tdd, unsigned char *buf, const char *str)
+{
+ int bytes=0;
+ int i,x;
+ char c;
+ /*! Baudot letters */
+ static unsigned char lstr[31] = "\000E\nA SIU\rDRJNFCKTZLWHYPQOBG\000MXV";
+ /*! Baudot figures */
+ static unsigned char fstr[31] = "\0003\n- \00787\r$4',!:(5\")2\0006019?+\000./;";
+ /* Initial carriers (real/imaginary) */
+ float cr = 1.0;
+ float ci = 0.0;
+ float scont = 0.0;
+
+ for(x = 0; str[x]; x++) {
+ /* Do synch for each 72th character */
+ if ( (tdd->charnum++) % 72 == 0)
+ PUT_TDD(tdd->mode ? 27 /* FIGS */ : 31 /* LTRS */);
+
+ c = toupper(str[x]);
+#if 0
+ printf("%c",c); fflush(stdout);
+#endif
+ if (c == 0) { /* send null */
+ PUT_TDD(0);
+ continue;
+ }
+ if (c == '\r') { /* send c/r */
+ PUT_TDD(8);
+ continue;
+ }
+ if (c == '\n') { /* send c/r and l/f */
+ PUT_TDD(8);
+ PUT_TDD(2);
+ continue;
+ }
+ if (c == ' ') { /* send space */
+ PUT_TDD(4);
+ continue;
+ }
+ for (i = 0; i < 31; i++) {
+ if (lstr[i] == c)
+ break;
+ }
+ if (i < 31) { /* if we found it */
+ if (tdd->mode) { /* if in figs mode, change it */
+ PUT_TDD(31); /* Send LTRS */
+ tdd->mode = 0;
+ }
+ PUT_TDD(i);
+ continue;
+ }
+ for (i = 0; i < 31; i++) {
+ if (fstr[i] == c)
+ break;
+ }
+ if (i < 31) { /* if we found it */
+ if (tdd->mode != 1) { /* if in ltrs mode, change it */
+ PUT_TDD(27); /* send FIGS */
+ tdd->mode = 1;
+ }
+ PUT_TDD(i); /* send byte */
+ continue;
+ }
+ }
+ return bytes;
+}
+
diff --git a/trunk/main/term.c b/trunk/main/term.c
new file mode 100644
index 000000000..ea005aa31
--- /dev/null
+++ b/trunk/main/term.c
@@ -0,0 +1,297 @@
+/*
+ * 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.
+ */
+
+/*! \file
+ *
+ * \brief Terminal Routines
+ *
+ * \author Mark Spencer <markster@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/_private.h"
+#include <sys/time.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "asterisk/term.h"
+#include "asterisk/lock.h"
+#include "asterisk/utils.h"
+
+static int vt100compat;
+
+static char prepdata[80] = "";
+static char enddata[80] = "";
+static char quitdata[80] = "";
+
+static const char *termpath[] = {
+ "/usr/share/terminfo",
+ "/usr/local/share/misc/terminfo",
+ "/usr/lib/terminfo",
+ NULL
+ };
+
+/* Ripped off from Ross Ridge, but it's public domain code (libmytinfo) */
+static short convshort(char *s)
+{
+ register int a,b;
+
+ a = (int) s[0] & 0377;
+ b = (int) s[1] & 0377;
+
+ if (a == 0377 && b == 0377)
+ return -1;
+ if (a == 0376 && b == 0377)
+ return -2;
+
+ return a + b * 256;
+}
+
+int ast_term_init(void)
+{
+ char *term = getenv("TERM");
+ char termfile[256] = "";
+ char buffer[512] = "";
+ int termfd = -1, parseokay = 0, i;
+
+ if (!term)
+ return 0;
+ if (!ast_opt_console || ast_opt_no_color || !ast_opt_no_fork)
+ return 0;
+
+ for (i=0 ;; i++) {
+ if (termpath[i] == NULL) {
+ break;
+ }
+ snprintf(termfile, sizeof(termfile), "%s/%c/%s", termpath[i], *term, term);
+ termfd = open(termfile, O_RDONLY);
+ if (termfd > -1) {
+ break;
+ }
+ }
+ if (termfd > -1) {
+ int actsize = read(termfd, buffer, sizeof(buffer) - 1);
+ short sz_names = convshort(buffer + 2);
+ short sz_bools = convshort(buffer + 4);
+ short n_nums = convshort(buffer + 6);
+
+ /* if ((sz_names + sz_bools) & 1)
+ sz_bools++; */
+
+ if (sz_names + sz_bools + n_nums < actsize) {
+ /* Offset 13 is defined in /usr/include/term.h, though we do not
+ * include it here, as it conflicts with include/asterisk/term.h */
+ short max_colors = convshort(buffer + 12 + sz_names + sz_bools + 13 * 2);
+ if (max_colors > 0) {
+ vt100compat = 1;
+ }
+ parseokay = 1;
+ }
+ close(termfd);
+ }
+
+ if (!parseokay) {
+ /* These comparisons should not be substrings nor case-insensitive, as
+ * terminal types are very particular about how they treat suffixes and
+ * capitalization. For example, terminal type 'linux-m' does NOT
+ * support color, while 'linux' does. Not even all vt100* terminals
+ * support color, either (e.g. 'vt100+fnkeys'). */
+ if (!strcmp(term, "linux")) {
+ vt100compat = 1;
+ } else if (!strcmp(term, "xterm")) {
+ vt100compat = 1;
+ } else if (!strcmp(term, "xterm-color")) {
+ vt100compat = 1;
+ } else if (!strncmp(term, "Eterm", 5)) {
+ /* Both entries which start with Eterm support color */
+ vt100compat = 1;
+ } else if (!strcmp(term, "vt100")) {
+ vt100compat = 1;
+ } else if (!strncmp(term, "crt", 3)) {
+ /* Both crt terminals support color */
+ vt100compat = 1;
+ }
+ }
+
+ if (vt100compat) {
+ /* Make commands show up in nice colors */
+ snprintf(prepdata, sizeof(prepdata), "%c[%d;%d;%dm", ESC, ATTR_BRIGHT, COLOR_BROWN, COLOR_BLACK + 10);
+ snprintf(enddata, sizeof(enddata), "%c[%d;%d;%dm", ESC, ATTR_RESET, COLOR_WHITE, COLOR_BLACK + 10);
+ snprintf(quitdata, sizeof(quitdata), "%c[0m", ESC);
+ }
+ return 0;
+}
+
+char *term_color(char *outbuf, const char *inbuf, int fgcolor, int bgcolor, int maxout)
+{
+ int attr=0;
+ char tmp[40];
+ if (!vt100compat) {
+ ast_copy_string(outbuf, inbuf, maxout);
+ return outbuf;
+ }
+ if (!fgcolor && !bgcolor) {
+ ast_copy_string(outbuf, inbuf, maxout);
+ return outbuf;
+ }
+ if ((fgcolor & 128) && (bgcolor & 128)) {
+ /* Can't both be highlighted */
+ ast_copy_string(outbuf, inbuf, maxout);
+ return outbuf;
+ }
+ if (!bgcolor)
+ bgcolor = COLOR_BLACK;
+
+ if (bgcolor) {
+ bgcolor &= ~128;
+ bgcolor += 10;
+ }
+ if (fgcolor & 128) {
+ attr = ATTR_BRIGHT;
+ fgcolor &= ~128;
+ }
+ if (fgcolor && bgcolor) {
+ snprintf(tmp, sizeof(tmp), "%d;%d", fgcolor, bgcolor);
+ } else if (bgcolor) {
+ snprintf(tmp, sizeof(tmp), "%d", bgcolor);
+ } else if (fgcolor) {
+ snprintf(tmp, sizeof(tmp), "%d", fgcolor);
+ }
+ if (attr) {
+ snprintf(outbuf, maxout, "%c[%d;%sm%s%c[0;%d;%dm", ESC, attr, tmp, inbuf, ESC, COLOR_WHITE, COLOR_BLACK + 10);
+ } else {
+ snprintf(outbuf, maxout, "%c[%sm%s%c[0;%d;%dm", ESC, tmp, inbuf, ESC, COLOR_WHITE, COLOR_BLACK + 10);
+ }
+ return outbuf;
+}
+
+char *term_color_code(char *outbuf, int fgcolor, int bgcolor, int maxout)
+{
+ int attr=0;
+ char tmp[40];
+ if ((!vt100compat) || (!fgcolor && !bgcolor)) {
+ *outbuf = '\0';
+ return outbuf;
+ }
+ if ((fgcolor & 128) && (bgcolor & 128)) {
+ /* Can't both be highlighted */
+ *outbuf = '\0';
+ return outbuf;
+ }
+ if (!bgcolor)
+ bgcolor = COLOR_BLACK;
+
+ if (bgcolor) {
+ bgcolor &= ~128;
+ bgcolor += 10;
+ }
+ if (fgcolor & 128) {
+ attr = ATTR_BRIGHT;
+ fgcolor &= ~128;
+ }
+ if (fgcolor && bgcolor) {
+ snprintf(tmp, sizeof(tmp), "%d;%d", fgcolor, bgcolor);
+ } else if (bgcolor) {
+ snprintf(tmp, sizeof(tmp), "%d", bgcolor);
+ } else if (fgcolor) {
+ snprintf(tmp, sizeof(tmp), "%d", fgcolor);
+ }
+ if (attr) {
+ snprintf(outbuf, maxout, "%c[%d;%sm", ESC, attr, tmp);
+ } else {
+ snprintf(outbuf, maxout, "%c[%sm", ESC, tmp);
+ }
+ return outbuf;
+}
+
+char *term_strip(char *outbuf, char *inbuf, int maxout)
+{
+ char *outbuf_ptr = outbuf, *inbuf_ptr = inbuf;
+
+ while (outbuf_ptr < outbuf + maxout) {
+ switch (*inbuf_ptr) {
+ case ESC:
+ while (*inbuf_ptr && (*inbuf_ptr != 'm'))
+ inbuf_ptr++;
+ break;
+ default:
+ *outbuf_ptr = *inbuf_ptr;
+ outbuf_ptr++;
+ }
+ if (! *inbuf_ptr)
+ break;
+ inbuf_ptr++;
+ }
+ return outbuf;
+}
+
+char *term_prompt(char *outbuf, const char *inbuf, int maxout)
+{
+ if (!vt100compat) {
+ ast_copy_string(outbuf, inbuf, maxout);
+ return outbuf;
+ }
+ snprintf(outbuf, maxout, "%c[%d;%d;%dm%c%c[%d;%d;%dm%s",
+ ESC, ATTR_BRIGHT, COLOR_BLUE, COLOR_BLACK + 10,
+ inbuf[0],
+ ESC, 0, COLOR_WHITE, COLOR_BLACK + 10,
+ inbuf + 1);
+ return outbuf;
+}
+
+/* filter escape sequences */
+void term_filter_escapes(char *line)
+{
+ int i;
+ int len = strlen(line);
+
+ for (i = 0; i < len; i++) {
+ if (line[i] != ESC)
+ continue;
+ if ((i < (len - 2)) &&
+ (line[i + 1] == 0x5B)) {
+ switch (line[i + 2]) {
+ case 0x30:
+ case 0x31:
+ case 0x33:
+ continue;
+ }
+ }
+ /* replace ESC with a space */
+ line[i] = ' ';
+ }
+}
+
+char *term_prep(void)
+{
+ return prepdata;
+}
+
+char *term_end(void)
+{
+ return enddata;
+}
+
+char *term_quit(void)
+{
+ return quitdata;
+}
diff --git a/trunk/main/threadstorage.c b/trunk/main/threadstorage.c
new file mode 100644
index 000000000..ae1719ff8
--- /dev/null
+++ b/trunk/main/threadstorage.c
@@ -0,0 +1,239 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Kevin P. Fleming <kpfleming@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
+ *
+ * \brief Debugging support for thread-local-storage objects
+ *
+ * \author Kevin P. Fleming <kpfleming@digium.com>
+ */
+
+#include "asterisk.h"
+#include "asterisk/_private.h"
+
+#if !defined(DEBUG_THREADLOCALS)
+
+void threadstorage_init(void)
+{
+}
+
+#else /* !defined(DEBUG_THREADLOCALS) */
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/strings.h"
+#include "asterisk/utils.h"
+#include "asterisk/threadstorage.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/cli.h"
+
+struct tls_object {
+ void *key;
+ size_t size;
+ const char *file;
+ const char *function;
+ unsigned int line;
+ pthread_t thread;
+ AST_LIST_ENTRY(tls_object) entry;
+};
+
+static AST_LIST_HEAD_NOLOCK_STATIC(tls_objects, tls_object);
+AST_MUTEX_DEFINE_STATIC_NOTRACKING(threadstoragelock);
+
+
+void __ast_threadstorage_object_add(void *key, size_t len, const char *file, const char *function, unsigned int line)
+{
+ struct tls_object *to;
+
+ if (!(to = ast_calloc(1, sizeof(*to))))
+ return;
+
+ to->key = key;
+ to->size = len;
+ to->file = file;
+ to->function = function;
+ to->line = line;
+ to->thread = pthread_self();
+
+ ast_mutex_lock(&threadstoragelock);
+ AST_LIST_INSERT_TAIL(&tls_objects, to, entry);
+ ast_mutex_unlock(&threadstoragelock);
+}
+
+void __ast_threadstorage_object_remove(void *key)
+{
+ struct tls_object *to;
+
+ ast_mutex_lock(&threadstoragelock);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&tls_objects, to, entry) {
+ if (to->key == key) {
+ AST_LIST_REMOVE_CURRENT(entry);
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ ast_mutex_unlock(&threadstoragelock);
+ if (to)
+ ast_free(to);
+}
+
+void __ast_threadstorage_object_replace(void *key_old, void *key_new, size_t len)
+{
+ struct tls_object *to;
+
+ ast_mutex_lock(&threadstoragelock);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&tls_objects, to, entry) {
+ if (to->key == key_old) {
+ to->key = key_new;
+ to->size = len;
+ break;
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ ast_mutex_unlock(&threadstoragelock);
+}
+
+static char *handle_cli_threadstorage_show_allocations(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char *fn = NULL;
+ size_t len = 0;
+ unsigned int count = 0;
+ struct tls_object *to;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "threadstorage show allocations";
+ e->usage =
+ "Usage: threadstorage show allocations [<file>]\n"
+ " Dumps a list of all thread-specific memory allocations,\n"
+ " optionally limited to those from a specific file\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc > 4)
+ return CLI_SHOWUSAGE;
+
+ if (a->argc > 3)
+ fn = a->argv[3];
+
+ ast_mutex_lock(&threadstoragelock);
+
+ AST_LIST_TRAVERSE(&tls_objects, to, entry) {
+ if (fn && strcasecmp(to->file, fn))
+ continue;
+
+ ast_cli(a->fd, "%10d bytes allocated in %20s at line %5d of %25s (thread %p)\n",
+ (int) to->size, to->function, to->line, to->file, (void *) to->thread);
+ len += to->size;
+ count++;
+ }
+
+ ast_mutex_unlock(&threadstoragelock);
+
+ ast_cli(a->fd, "%10d bytes allocated in %d allocation%s\n", (int) len, count, count > 1 ? "s" : "");
+
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_threadstorage_show_summary(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char *fn = NULL;
+ size_t len = 0;
+ unsigned int count = 0;
+ struct tls_object *to;
+ struct file {
+ const char *name;
+ size_t len;
+ unsigned int count;
+ AST_LIST_ENTRY(file) entry;
+ } *file;
+ AST_LIST_HEAD_NOLOCK_STATIC(file_summary, file);
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "threadstorage show summary";
+ e->usage =
+ "Usage: threadstorage show summary [<file>]\n"
+ " Summarizes thread-specific memory allocations by file, or optionally\n"
+ " by function, if a file is specified\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc > 4)
+ return CLI_SHOWUSAGE;
+
+ if (a->argc > 3)
+ fn = a->argv[3];
+
+ ast_mutex_lock(&threadstoragelock);
+
+ AST_LIST_TRAVERSE(&tls_objects, to, entry) {
+ if (fn && strcasecmp(to->file, fn))
+ continue;
+
+ AST_LIST_TRAVERSE(&file_summary, file, entry) {
+ if ((!fn && (file->name == to->file)) || (fn && (file->name == to->function)))
+ break;
+ }
+
+ if (!file) {
+ file = alloca(sizeof(*file));
+ memset(file, 0, sizeof(*file));
+ file->name = fn ? to->function : to->file;
+ AST_LIST_INSERT_TAIL(&file_summary, file, entry);
+ }
+
+ file->len += to->size;
+ file->count++;
+ }
+
+ ast_mutex_unlock(&threadstoragelock);
+
+ AST_LIST_TRAVERSE(&file_summary, file, entry) {
+ len += file->len;
+ count += file->count;
+ if (fn) {
+ ast_cli(a->fd, "%10d bytes in %d allocation%ss in function %s\n",
+ (int) file->len, file->count, file->count > 1 ? "s" : "", file->name);
+ } else {
+ ast_cli(a->fd, "%10d bytes in %d allocation%s in file %s\n",
+ (int) file->len, file->count, file->count > 1 ? "s" : "", file->name);
+ }
+ }
+
+ ast_cli(a->fd, "%10d bytes allocated in %d allocation%s\n", (int) len, count, count > 1 ? "s" : "");
+
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli[] = {
+ AST_CLI_DEFINE(handle_cli_threadstorage_show_allocations, "Display outstanding thread local storage allocations"),
+ AST_CLI_DEFINE(handle_cli_threadstorage_show_summary, "Summarize outstanding memory allocations")
+};
+
+void threadstorage_init(void)
+{
+ ast_cli_register_multiple(cli, sizeof(cli) / sizeof(cli[0]));
+}
+
+#endif /* !defined(DEBUG_THREADLOCALS) */
+
diff --git a/trunk/main/translate.c b/trunk/main/translate.c
new file mode 100644
index 000000000..1af88ecfb
--- /dev/null
+++ b/trunk/main/translate.c
@@ -0,0 +1,904 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006, 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.
+ */
+
+/*! \file
+ *
+ * \brief Translate via the use of pseudo channels
+ *
+ * \author Mark Spencer <markster@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include "asterisk/lock.h"
+#include "asterisk/channel.h"
+#include "asterisk/translate.h"
+#include "asterisk/module.h"
+#include "asterisk/frame.h"
+#include "asterisk/sched.h"
+#include "asterisk/cli.h"
+#include "asterisk/term.h"
+
+#define MAX_RECALC 1000 /* max sample recalc */
+
+/*! \brief the list of translators */
+static AST_RWLIST_HEAD_STATIC(translators, ast_translator);
+
+struct translator_path {
+ struct ast_translator *step; /*!< Next step translator */
+ unsigned int cost; /*!< Complete cost to destination */
+ unsigned int multistep; /*!< Multiple conversions required for this translation */
+};
+
+/*! \brief a matrix that, for any pair of supported formats,
+ * indicates the total cost of translation and the first step.
+ * The full path can be reconstricted iterating on the matrix
+ * until step->dstfmt == desired_format.
+ *
+ * Array indexes are 'src' and 'dest', in that order.
+ *
+ * Note: the lock in the 'translators' list is also used to protect
+ * this structure.
+ */
+static struct translator_path tr_matrix[MAX_FORMAT][MAX_FORMAT];
+
+/*! \todo
+ * TODO: sample frames for each supported input format.
+ * We build this on the fly, by taking an SLIN frame and using
+ * the existing converter to play with it.
+ */
+
+/*! \brief returns the index of the lowest bit set */
+static force_inline int powerof(unsigned int d)
+{
+ int x = ffs(d);
+
+ if (x)
+ return x - 1;
+
+ ast_log(LOG_WARNING, "No bits set? %d\n", d);
+
+ return -1;
+}
+
+/*
+ * wrappers around the translator routines.
+ */
+
+/*!
+ * \brief Allocate the descriptor, required outbuf space,
+ * and possibly also plc and desc.
+ */
+static void *newpvt(struct ast_translator *t)
+{
+ struct ast_trans_pvt *pvt;
+ int len;
+ int useplc = t->plc_samples > 0 && t->useplc; /* cache, because it can change on the fly */
+ char *ofs;
+
+ /*
+ * compute the required size adding private descriptor,
+ * plc, buffer, AST_FRIENDLY_OFFSET.
+ */
+ len = sizeof(*pvt) + t->desc_size;
+ if (useplc)
+ len += sizeof(plc_state_t);
+ if (t->buf_size)
+ len += AST_FRIENDLY_OFFSET + t->buf_size;
+ pvt = ast_calloc(1, len);
+ if (!pvt)
+ return NULL;
+ pvt->t = t;
+ ofs = (char *)(pvt + 1); /* pointer to data space */
+ if (t->desc_size) { /* first comes the descriptor */
+ pvt->pvt = ofs;
+ ofs += t->desc_size;
+ }
+ if (useplc) { /* then plc state */
+ pvt->plc = (plc_state_t *)ofs;
+ ofs += sizeof(plc_state_t);
+ }
+ if (t->buf_size) /* finally buffer and header */
+ pvt->outbuf = ofs + AST_FRIENDLY_OFFSET;
+ /* call local init routine, if present */
+ if (t->newpvt && t->newpvt(pvt)) {
+ ast_free(pvt);
+ return NULL;
+ }
+ ast_module_ref(t->module);
+ return pvt;
+}
+
+static void destroy(struct ast_trans_pvt *pvt)
+{
+ struct ast_translator *t = pvt->t;
+
+ if (ast_test_flag(&pvt->f, AST_FRFLAG_FROM_TRANSLATOR)) {
+ /* If this flag is still set, that means that the translation path has
+ * been torn down, while we still have a frame out there being used.
+ * When ast_frfree() gets called on that frame, this ast_trans_pvt
+ * will get destroyed, too. */
+
+ pvt->destroy = 1;
+
+ return;
+ }
+
+ if (t->destroy)
+ t->destroy(pvt);
+ ast_free(pvt);
+ ast_module_unref(t->module);
+}
+
+/*! \brief framein wrapper, deals with plc and bound checks. */
+static int framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
+{
+ int16_t *dst = (int16_t *)pvt->outbuf;
+ int ret;
+ int samples = pvt->samples; /* initial value */
+
+ /* Copy the last in jb timing info to the pvt */
+ ast_copy_flags(&pvt->f, f, AST_FRFLAG_HAS_TIMING_INFO);
+ pvt->f.ts = f->ts;
+ pvt->f.len = f->len;
+ pvt->f.seqno = f->seqno;
+
+ if (f->samples == 0) {
+ ast_log(LOG_WARNING, "no samples for %s\n", pvt->t->name);
+ }
+ if (pvt->t->buffer_samples) { /* do not pass empty frames to callback */
+ if (f->datalen == 0) { /* perform PLC with nominal framesize of 20ms/160 samples */
+ if (pvt->plc) {
+ int l = pvt->t->plc_samples;
+ if (pvt->samples + l > pvt->t->buffer_samples) {
+ ast_log(LOG_WARNING, "Out of buffer space\n");
+ return -1;
+ }
+ l = plc_fillin(pvt->plc, dst + pvt->samples, l);
+ pvt->samples += l;
+ pvt->datalen = pvt->samples * 2; /* SLIN has 2bytes for 1sample */
+ }
+ /* We don't want generic PLC. If the codec has native PLC, then do that */
+ if (!pvt->t->native_plc)
+ return 0;
+ }
+ if (pvt->samples + f->samples > pvt->t->buffer_samples) {
+ ast_log(LOG_WARNING, "Out of buffer space\n");
+ return -1;
+ }
+ }
+ /* we require a framein routine, wouldn't know how to do
+ * it otherwise.
+ */
+ ret = pvt->t->framein(pvt, f);
+ /* possibly store data for plc */
+ if (!ret && pvt->plc) {
+ int l = pvt->t->plc_samples;
+ if (pvt->samples < l)
+ l = pvt->samples;
+ plc_rx(pvt->plc, dst + pvt->samples - l, l);
+ }
+ /* diagnostic ... */
+ if (pvt->samples == samples)
+ ast_log(LOG_WARNING, "%s did not update samples %d\n",
+ pvt->t->name, pvt->samples);
+ return ret;
+}
+
+/*! \brief generic frameout routine.
+ * If samples and datalen are 0, take whatever is in pvt
+ * and reset them, otherwise take the values in the caller and
+ * leave alone the pvt values.
+ */
+struct ast_frame *ast_trans_frameout(struct ast_trans_pvt *pvt,
+ int datalen, int samples)
+{
+ struct ast_frame *f = &pvt->f;
+
+ if (samples)
+ f->samples = samples;
+ else {
+ if (pvt->samples == 0)
+ return NULL;
+ f->samples = pvt->samples;
+ pvt->samples = 0;
+ }
+ if (datalen)
+ f->datalen = datalen;
+ else {
+ f->datalen = pvt->datalen;
+ pvt->datalen = 0;
+ }
+
+ f->frametype = AST_FRAME_VOICE;
+ f->subclass = 1 << (pvt->t->dstfmt);
+ f->mallocd = 0;
+ f->offset = AST_FRIENDLY_OFFSET;
+ f->src = pvt->t->name;
+ f->data = pvt->outbuf;
+
+ ast_set_flag(f, AST_FRFLAG_FROM_TRANSLATOR);
+
+ return f;
+}
+
+static struct ast_frame *default_frameout(struct ast_trans_pvt *pvt)
+{
+ return ast_trans_frameout(pvt, 0, 0);
+}
+
+/* end of callback wrappers and helpers */
+
+void ast_translator_free_path(struct ast_trans_pvt *p)
+{
+ struct ast_trans_pvt *pn = p;
+ while ( (p = pn) ) {
+ pn = p->next;
+ destroy(p);
+ }
+}
+
+/*! \brief Build a chain of translators based upon the given source and dest formats */
+struct ast_trans_pvt *ast_translator_build_path(int dest, int source)
+{
+ struct ast_trans_pvt *head = NULL, *tail = NULL;
+
+ source = powerof(source);
+ dest = powerof(dest);
+
+ AST_RWLIST_RDLOCK(&translators);
+
+ while (source != dest) {
+ struct ast_trans_pvt *cur;
+ struct ast_translator *t = tr_matrix[source][dest].step;
+ if (!t) {
+ ast_log(LOG_WARNING, "No translator path from %s to %s\n",
+ ast_getformatname(source), ast_getformatname(dest));
+ AST_RWLIST_UNLOCK(&translators);
+ return NULL;
+ }
+ if (!(cur = newpvt(t))) {
+ ast_log(LOG_WARNING, "Failed to build translator step from %d to %d\n", source, dest);
+ if (head)
+ ast_translator_free_path(head);
+ AST_RWLIST_UNLOCK(&translators);
+ return NULL;
+ }
+ if (!head)
+ head = cur;
+ else
+ tail->next = cur;
+ tail = cur;
+ cur->nextin = cur->nextout = ast_tv(0, 0);
+ /* Keep going if this isn't the final destination */
+ source = cur->t->dstfmt;
+ }
+
+ AST_RWLIST_UNLOCK(&translators);
+ return head;
+}
+
+static inline int format_rate(int format)
+{
+ if (format == AST_FORMAT_G722 || format == AST_FORMAT_SLINEAR16)
+ return 16000;
+
+ return 8000;
+}
+
+/*! \brief do the actual translation */
+struct ast_frame *ast_translate(struct ast_trans_pvt *path, struct ast_frame *f, int consume)
+{
+ struct ast_trans_pvt *p = path;
+ struct ast_frame *out = f;
+ struct timeval delivery;
+ int has_timing_info;
+ long ts;
+ long len;
+ int seqno;
+
+ has_timing_info = ast_test_flag(f, AST_FRFLAG_HAS_TIMING_INFO);
+ ts = f->ts;
+ len = f->len;
+ seqno = f->seqno;
+
+ /* XXX hmmm... check this below */
+ if (!ast_tvzero(f->delivery)) {
+ if (!ast_tvzero(path->nextin)) {
+ /* Make sure this is in line with what we were expecting */
+ if (!ast_tveq(path->nextin, f->delivery)) {
+ /* The time has changed between what we expected and this
+ most recent time on the new packet. If we have a
+ valid prediction adjust our output time appropriately */
+ if (!ast_tvzero(path->nextout)) {
+ path->nextout = ast_tvadd(path->nextout,
+ ast_tvsub(f->delivery, path->nextin));
+ }
+ path->nextin = f->delivery;
+ }
+ } else {
+ /* This is our first pass. Make sure the timing looks good */
+ path->nextin = f->delivery;
+ path->nextout = f->delivery;
+ }
+ /* Predict next incoming sample */
+ path->nextin = ast_tvadd(path->nextin, ast_samp2tv(f->samples, format_rate(f->subclass)));
+ }
+ delivery = f->delivery;
+ for ( ; out && p ; p = p->next) {
+ framein(p, out);
+ out = p->t->frameout(p);
+ }
+ if (consume)
+ ast_frfree(f);
+ if (out == NULL)
+ return NULL;
+ /* we have a frame, play with times */
+ if (!ast_tvzero(delivery)) {
+ /* Regenerate prediction after a discontinuity */
+ if (ast_tvzero(path->nextout))
+ path->nextout = ast_tvnow();
+
+ /* Use next predicted outgoing timestamp */
+ out->delivery = path->nextout;
+
+ /* Predict next outgoing timestamp from samples in this
+ frame. */
+ path->nextout = ast_tvadd(path->nextout, ast_samp2tv(out->samples, format_rate(out->subclass)));
+ } else {
+ out->delivery = ast_tv(0, 0);
+ ast_set2_flag(out, has_timing_info, AST_FRFLAG_HAS_TIMING_INFO);
+ if (has_timing_info) {
+ out->ts = ts;
+ out->len = len;
+ out->seqno = seqno;
+ }
+ }
+ /* Invalidate prediction if we're entering a silence period */
+ if (out->frametype == AST_FRAME_CNG)
+ path->nextout = ast_tv(0, 0);
+ return out;
+}
+
+/*! \brief compute the cost of a single translation step */
+static void calc_cost(struct ast_translator *t, int seconds)
+{
+ int num_samples = 0;
+ struct ast_trans_pvt *pvt;
+ struct rusage start;
+ struct rusage end;
+ int cost;
+ int out_rate = format_rate(t->dstfmt);
+
+ if (!seconds)
+ seconds = 1;
+
+ /* If they don't make samples, give them a terrible score */
+ if (!t->sample) {
+ ast_log(LOG_WARNING, "Translator '%s' does not produce sample frames.\n", t->name);
+ t->cost = 999999;
+ return;
+ }
+
+ pvt = newpvt(t);
+ if (!pvt) {
+ ast_log(LOG_WARNING, "Translator '%s' appears to be broken and will probably fail.\n", t->name);
+ t->cost = 999999;
+ return;
+ }
+
+ getrusage(RUSAGE_SELF, &start);
+
+ /* Call the encoder until we've processed the required number of samples */
+ while (num_samples < seconds * out_rate) {
+ struct ast_frame *f = t->sample();
+ if (!f) {
+ ast_log(LOG_WARNING, "Translator '%s' failed to produce a sample frame.\n", t->name);
+ destroy(pvt);
+ t->cost = 999999;
+ return;
+ }
+ framein(pvt, f);
+ ast_frfree(f);
+ while ((f = t->frameout(pvt))) {
+ num_samples += f->samples;
+ ast_frfree(f);
+ }
+ }
+
+ getrusage(RUSAGE_SELF, &end);
+
+ cost = ((end.ru_utime.tv_sec - start.ru_utime.tv_sec) * 1000000) + end.ru_utime.tv_usec - start.ru_utime.tv_usec;
+ cost += ((end.ru_stime.tv_sec - start.ru_stime.tv_sec) * 1000000) + end.ru_stime.tv_usec - start.ru_stime.tv_usec;
+
+ destroy(pvt);
+
+ t->cost = cost / seconds;
+
+ if (!t->cost)
+ t->cost = 1;
+}
+
+/*!
+ * \brief rebuild a translation matrix.
+ * \note This function expects the list of translators to be locked
+*/
+static void rebuild_matrix(int samples)
+{
+ struct ast_translator *t;
+ int x; /* source format index */
+ int y; /* intermediate format index */
+ int z; /* destination format index */
+
+ ast_debug(1, "Resetting translation matrix\n");
+
+ bzero(tr_matrix, sizeof(tr_matrix));
+
+ /* first, compute all direct costs */
+ AST_RWLIST_TRAVERSE(&translators, t, list) {
+ if (!t->active)
+ continue;
+
+ x = t->srcfmt;
+ z = t->dstfmt;
+
+ if (samples)
+ calc_cost(t, samples);
+
+ if (!tr_matrix[x][z].step || t->cost < tr_matrix[x][z].cost) {
+ tr_matrix[x][z].step = t;
+ tr_matrix[x][z].cost = t->cost;
+ }
+ }
+
+ /*
+ * For each triple x, y, z of distinct formats, check if there is
+ * a path from x to z through y which is cheaper than what is
+ * currently known, and in case, update the matrix.
+ * Repeat until the matrix is stable.
+ */
+ for (;;) {
+ int changed = 0;
+ for (x = 0; x < MAX_FORMAT; x++) { /* source format */
+ for (y=0; y < MAX_FORMAT; y++) { /* intermediate format */
+ if (x == y) /* skip ourselves */
+ continue;
+
+ for (z=0; z<MAX_FORMAT; z++) { /* dst format */
+ int newcost;
+
+ if (z == x || z == y) /* skip null conversions */
+ continue;
+ if (!tr_matrix[x][y].step) /* no path from x to y */
+ continue;
+ if (!tr_matrix[y][z].step) /* no path from y to z */
+ continue;
+ newcost = tr_matrix[x][y].cost + tr_matrix[y][z].cost;
+ if (tr_matrix[x][z].step && newcost >= tr_matrix[x][z].cost)
+ continue; /* x->y->z is more expensive than
+ * the existing path */
+ /* ok, we can get from x to z via y with a cost that
+ is the sum of the transition from x to y and
+ from y to z */
+
+ tr_matrix[x][z].step = tr_matrix[x][y].step;
+ tr_matrix[x][z].cost = newcost;
+ tr_matrix[x][z].multistep = 1;
+ ast_debug(3, "Discovered %d cost path from %s to %s, via %d\n", tr_matrix[x][z].cost, ast_getformatname(x), ast_getformatname(z), y);
+ changed++;
+ }
+ }
+ }
+ if (!changed)
+ break;
+ }
+}
+
+static char *handle_cli_core_show_translation(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+#define SHOW_TRANS 16
+ int x, y, z;
+ int curlen = 0, longest = 0;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show translation [recalc]";
+ e->usage =
+ "Usage: core show translation [recalc [<recalc seconds>]]\n"
+ " Displays known codec translators and the cost associated\n"
+ " with each conversion. If the argument 'recalc' is supplied along\n"
+ " with optional number of seconds to test a new test will be performed\n"
+ " as the chart is being displayed.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc > 5)
+ return CLI_SHOWUSAGE;
+
+ if (a->argv[3] && !strcasecmp(a->argv[3], "recalc")) {
+ z = a->argv[4] ? atoi(a->argv[4]) : 1;
+
+ if (z <= 0) {
+ ast_cli(a->fd, " Recalc must be greater than 0. Defaulting to 1.\n");
+ z = 1;
+ }
+
+ if (z > MAX_RECALC) {
+ ast_cli(a->fd, " Maximum limit of recalc exceeded by %d, truncating value to %d\n", z - MAX_RECALC, MAX_RECALC);
+ z = MAX_RECALC;
+ }
+ ast_cli(a->fd, " Recalculating Codec Translation (number of sample seconds: %d)\n\n", z);
+ AST_RWLIST_WRLOCK(&translators);
+ rebuild_matrix(z);
+ AST_RWLIST_UNLOCK(&translators);
+ } else if (a->argc > 3)
+ return CLI_SHOWUSAGE;
+
+ AST_RWLIST_RDLOCK(&translators);
+
+ ast_cli(a->fd, " Translation times between formats (in microseconds) for one second of data\n");
+ ast_cli(a->fd, " Source Format (Rows) Destination Format (Columns)\n\n");
+ /* Get the length of the longest (usable?) codec name, so we know how wide the left side should be */
+ for (x = 0; x < SHOW_TRANS; x++) {
+ curlen = strlen(ast_getformatname(1 << (x)));
+ if (curlen > longest)
+ longest = curlen;
+ }
+ for (x = -1; x < SHOW_TRANS; x++) {
+ struct ast_str *out = ast_str_alloca(120);
+ /*Go ahead and move to next iteration if dealing with an unknown codec*/
+ if(x >= 0 && !strcmp(ast_getformatname(1 << (x)), "unknown"))
+ continue;
+ ast_str_set(&out, -1, " ");
+ for (y = -1; y < SHOW_TRANS; y++) {
+ /*Go ahead and move to next iteration if dealing with an unknown codec*/
+ if (y >= 0 && !strcmp(ast_getformatname(1 << (y)), "unknown"))
+ continue;
+ if (y >= 0)
+ curlen = strlen(ast_getformatname(1 << (y)));
+ if (curlen < 5)
+ curlen = 5;
+ if (x >= 0 && y >= 0 && tr_matrix[x][y].step) {
+ /* XXX 99999 is a little hackish
+ We don't want this number being larger than the shortest (or current) codec
+ For now, that is "gsm" */
+ ast_str_append(&out, -1, "%*d", curlen + 1, tr_matrix[x][y].cost > 99999 ? 0 : tr_matrix[x][y].cost);
+ } else if (x == -1 && y >= 0) {
+ /* Top row - use a dynamic size */
+ ast_str_append(&out, -1, "%*s", curlen + 1, ast_getformatname(1 << (y)) );
+ } else if (y == -1 && x >= 0) {
+ /* Left column - use a static size. */
+ ast_str_append(&out, -1, "%*s", longest, ast_getformatname(1 << (x)) );
+ } else if (x >= 0 && y >= 0) {
+ ast_str_append(&out, -1, "%*s", curlen + 1, "-");
+ } else {
+ ast_str_append(&out, -1, "%*s", longest, "");
+ }
+ }
+ ast_str_append(&out, -1, "\n");
+ ast_cli(a->fd, out->str);
+ }
+ AST_RWLIST_UNLOCK(&translators);
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli_translate[] = {
+ AST_CLI_DEFINE(handle_cli_core_show_translation, "Display translation matrix")
+};
+
+/*! \brief register codec translator */
+int __ast_register_translator(struct ast_translator *t, struct ast_module *mod)
+{
+ static int added_cli = 0;
+ struct ast_translator *u;
+ char tmp[80];
+
+ if (!mod) {
+ ast_log(LOG_WARNING, "Missing module pointer, you need to supply one\n");
+ return -1;
+ }
+
+ if (!t->buf_size) {
+ ast_log(LOG_WARNING, "empty buf size, you need to supply one\n");
+ return -1;
+ }
+
+ t->module = mod;
+
+ t->srcfmt = powerof(t->srcfmt);
+ t->dstfmt = powerof(t->dstfmt);
+ t->active = 1;
+
+ if (t->plc_samples) {
+ if (t->buffer_samples < t->plc_samples) {
+ ast_log(LOG_WARNING, "plc_samples %d buffer_samples %d\n",
+ t->plc_samples, t->buffer_samples);
+ return -1;
+ }
+ if (t->dstfmt != powerof(AST_FORMAT_SLINEAR))
+ ast_log(LOG_WARNING, "plc_samples %d format %x\n",
+ t->plc_samples, t->dstfmt);
+ }
+ if (t->srcfmt >= MAX_FORMAT) {
+ ast_log(LOG_WARNING, "Source format %s is larger than MAX_FORMAT\n", ast_getformatname(t->srcfmt));
+ return -1;
+ }
+
+ if (t->dstfmt >= MAX_FORMAT) {
+ ast_log(LOG_WARNING, "Destination format %s is larger than MAX_FORMAT\n", ast_getformatname(t->dstfmt));
+ return -1;
+ }
+
+ if (t->buf_size) {
+ /*
+ * Align buf_size properly, rounding up to the machine-specific
+ * alignment for pointers.
+ */
+ struct _test_align { void *a, *b; } p;
+ int align = (char *)&p.b - (char *)&p.a;
+
+ t->buf_size = ((t->buf_size + align - 1) / align) * align;
+ }
+
+ if (t->frameout == NULL)
+ t->frameout = default_frameout;
+
+ calc_cost(t, 1);
+
+ ast_verb(2, "Registered translator '%s' from format %s to %s, cost %d\n",
+ term_color(tmp, t->name, COLOR_MAGENTA, COLOR_BLACK, sizeof(tmp)),
+ ast_getformatname(1 << t->srcfmt), ast_getformatname(1 << t->dstfmt), t->cost);
+
+ if (!added_cli) {
+ ast_cli_register_multiple(cli_translate, sizeof(cli_translate) / sizeof(struct ast_cli_entry));
+ added_cli++;
+ }
+
+ AST_RWLIST_WRLOCK(&translators);
+
+ /* find any existing translators that provide this same srcfmt/dstfmt,
+ and put this one in order based on cost */
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&translators, u, list) {
+ if ((u->srcfmt == t->srcfmt) &&
+ (u->dstfmt == t->dstfmt) &&
+ (u->cost > t->cost)) {
+ AST_RWLIST_INSERT_BEFORE_CURRENT(t, list);
+ t = NULL;
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+
+ /* if no existing translator was found for this format combination,
+ add it to the beginning of the list */
+ if (t)
+ AST_RWLIST_INSERT_HEAD(&translators, t, list);
+
+ rebuild_matrix(0);
+
+ AST_RWLIST_UNLOCK(&translators);
+
+ return 0;
+}
+
+/*! \brief unregister codec translator */
+int ast_unregister_translator(struct ast_translator *t)
+{
+ char tmp[80];
+ struct ast_translator *u;
+ int found = 0;
+
+ AST_RWLIST_WRLOCK(&translators);
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&translators, u, list) {
+ if (u == t) {
+ AST_RWLIST_REMOVE_CURRENT(list);
+ ast_verb(2, "Unregistered translator '%s' from format %s to %s\n", term_color(tmp, t->name, COLOR_MAGENTA, COLOR_BLACK, sizeof(tmp)), ast_getformatname(1 << t->srcfmt), ast_getformatname(1 << t->dstfmt));
+ found = 1;
+ break;
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END;
+
+ if (found)
+ rebuild_matrix(0);
+
+ AST_RWLIST_UNLOCK(&translators);
+
+ return (u ? 0 : -1);
+}
+
+void ast_translator_activate(struct ast_translator *t)
+{
+ AST_RWLIST_WRLOCK(&translators);
+ t->active = 1;
+ rebuild_matrix(0);
+ AST_RWLIST_UNLOCK(&translators);
+}
+
+void ast_translator_deactivate(struct ast_translator *t)
+{
+ AST_RWLIST_WRLOCK(&translators);
+ t->active = 0;
+ rebuild_matrix(0);
+ AST_RWLIST_UNLOCK(&translators);
+}
+
+/*! \brief Calculate our best translator source format, given costs, and a desired destination */
+int ast_translator_best_choice(int *dst, int *srcs)
+{
+ int x,y;
+ int best = -1;
+ int bestdst = 0;
+ int cur, cursrc;
+ int besttime = INT_MAX;
+ int beststeps = INT_MAX;
+ int common = ((*dst) & (*srcs)) & AST_FORMAT_AUDIO_MASK; /* are there common formats ? */
+
+ if (common) { /* yes, pick one and return */
+ for (cur = 1, y = 0; y <= MAX_AUDIO_FORMAT; cur <<= 1, y++) {
+ if (cur & common) /* guaranteed to find one */
+ break;
+ }
+ /* We are done, this is a common format to both. */
+ *srcs = *dst = cur;
+ return 0;
+ } else { /* No, we will need to translate */
+ AST_RWLIST_RDLOCK(&translators);
+ for (cur = 1, y = 0; y <= MAX_AUDIO_FORMAT; cur <<= 1, y++) {
+ if (! (cur & *dst))
+ continue;
+ for (cursrc = 1, x = 0; x <= MAX_AUDIO_FORMAT; cursrc <<= 1, x++) {
+ if (!(*srcs & cursrc) || !tr_matrix[x][y].step ||
+ tr_matrix[x][y].cost > besttime)
+ continue; /* not existing or no better */
+ if (tr_matrix[x][y].cost < besttime ||
+ tr_matrix[x][y].multistep < beststeps) {
+ /* better than what we have so far */
+ best = cursrc;
+ bestdst = cur;
+ besttime = tr_matrix[x][y].cost;
+ beststeps = tr_matrix[x][y].multistep;
+ }
+ }
+ }
+ AST_RWLIST_UNLOCK(&translators);
+ if (best > -1) {
+ *srcs = best;
+ *dst = bestdst;
+ best = 0;
+ }
+ return best;
+ }
+}
+
+unsigned int ast_translate_path_steps(unsigned int dest, unsigned int src)
+{
+ unsigned int res = -1;
+
+ /* convert bitwise format numbers into array indices */
+ src = powerof(src);
+ dest = powerof(dest);
+
+ AST_RWLIST_RDLOCK(&translators);
+
+ if (tr_matrix[src][dest].step)
+ res = tr_matrix[src][dest].multistep + 1;
+
+ AST_RWLIST_UNLOCK(&translators);
+
+ return res;
+}
+
+unsigned int ast_translate_available_formats(unsigned int dest, unsigned int src)
+{
+ unsigned int res = dest;
+ unsigned int x;
+ unsigned int src_audio = src & AST_FORMAT_AUDIO_MASK;
+ unsigned int src_video = src & AST_FORMAT_VIDEO_MASK;
+
+ /* if we don't have a source format, we just have to try all
+ possible destination formats */
+ if (!src)
+ return dest;
+
+ /* If we have a source audio format, get its format index */
+ if (src_audio)
+ src_audio = powerof(src_audio);
+
+ /* If we have a source video format, get its format index */
+ if (src_video)
+ src_video = powerof(src_video);
+
+ AST_RWLIST_RDLOCK(&translators);
+
+ /* For a given source audio format, traverse the list of
+ known audio formats to determine whether there exists
+ a translation path from the source format to the
+ destination format. */
+ for (x = 1; src_audio && (x & AST_FORMAT_AUDIO_MASK); x <<= 1) {
+ /* if this is not a desired format, nothing to do */
+ if (!dest & x)
+ continue;
+
+ /* if the source is supplying this format, then
+ we can leave it in the result */
+ if (src & x)
+ continue;
+
+ /* if we don't have a translation path from the src
+ to this format, remove it from the result */
+ if (!tr_matrix[src_audio][powerof(x)].step) {
+ res &= ~x;
+ continue;
+ }
+
+ /* now check the opposite direction */
+ if (!tr_matrix[powerof(x)][src_audio].step)
+ res &= ~x;
+ }
+
+ /* For a given source video format, traverse the list of
+ known video formats to determine whether there exists
+ a translation path from the source format to the
+ destination format. */
+ for (; src_video && (x & AST_FORMAT_VIDEO_MASK); x <<= 1) {
+ /* if this is not a desired format, nothing to do */
+ if (!dest & x)
+ continue;
+
+ /* if the source is supplying this format, then
+ we can leave it in the result */
+ if (src & x)
+ continue;
+
+ /* if we don't have a translation path from the src
+ to this format, remove it from the result */
+ if (!tr_matrix[src_video][powerof(x)].step) {
+ res &= ~x;
+ continue;
+ }
+
+ /* now check the opposite direction */
+ if (!tr_matrix[powerof(x)][src_video].step)
+ res &= ~x;
+ }
+
+ AST_RWLIST_UNLOCK(&translators);
+
+ return res;
+}
+
+void ast_translate_frame_freed(struct ast_frame *fr)
+{
+ struct ast_trans_pvt *pvt;
+
+ ast_clear_flag(fr, AST_FRFLAG_FROM_TRANSLATOR);
+
+ pvt = (struct ast_trans_pvt *) (((char *) fr) - offsetof(struct ast_trans_pvt, f));
+
+ if (!pvt->destroy)
+ return;
+
+ destroy(pvt);
+}
diff --git a/trunk/main/udptl.c b/trunk/main/udptl.c
new file mode 100644
index 000000000..12de3fd53
--- /dev/null
+++ b/trunk/main/udptl.c
@@ -0,0 +1,1273 @@
+/*
+ * Asterisk -- A telephony toolkit for Linux.
+ *
+ * UDPTL support for T.38
+ *
+ * Copyright (C) 2005, Steve Underwood, partly based on RTP code which is
+ * Copyright (C) 1999-2006, Digium, Inc.
+ *
+ * Steve Underwood <steveu@coppice.org>
+ *
+ * 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.
+ *
+ * A license has been granted to Digium (via disclaimer) for the use of
+ * this code.
+ */
+
+/*!
+ * \file
+ *
+ * \brief UDPTL support for T.38 faxing
+ *
+ *
+ * \author Mark Spencer <markster@digium.com>, Steve Underwood <steveu@coppice.org>
+ *
+ * \page T38fax_udptl T38 fax passhtrough :: UDPTL
+ *
+ * Asterisk supports T.38 fax passthrough. Asterisk will not be a client, server
+ * or any form of gateway. Currently fax passthrough is only implemented in the
+ * SIP channel for strict SIP to SIP calls. If you are using chan_local or chan_agent
+ * as a proxy channel, T.38 passthrough will not work.
+ *
+ * UDPTL is handled very much like RTP. It can be reinvited to go directly between
+ * the endpoints, without involving Asterisk in the media stream.
+ *
+ * \b References:
+ * - chan_sip.c
+ * - udptl.c
+ */
+
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/time.h>
+#include <signal.h>
+#include <fcntl.h>
+
+#include "asterisk/udptl.h"
+#include "asterisk/frame.h"
+#include "asterisk/channel.h"
+#include "asterisk/acl.h"
+#include "asterisk/config.h"
+#include "asterisk/lock.h"
+#include "asterisk/utils.h"
+#include "asterisk/netsock.h"
+#include "asterisk/cli.h"
+#include "asterisk/unaligned.h"
+
+#define UDPTL_MTU 1200
+
+#if !defined(FALSE)
+#define FALSE 0
+#endif
+#if !defined(TRUE)
+#define TRUE (!FALSE)
+#endif
+
+static int udptlstart;
+static int udptlend;
+static int udptldebug; /*!< Are we debugging? */
+static struct sockaddr_in udptldebugaddr; /*!< Debug packets to/from this host */
+#ifdef SO_NO_CHECK
+static int nochecksums;
+#endif
+static int udptlfectype;
+static int udptlfecentries;
+static int udptlfecspan;
+static int udptlmaxdatagram;
+
+#define LOCAL_FAX_MAX_DATAGRAM 400
+#define MAX_FEC_ENTRIES 5
+#define MAX_FEC_SPAN 5
+
+#define UDPTL_BUF_MASK 15
+
+typedef struct {
+ int buf_len;
+ uint8_t buf[LOCAL_FAX_MAX_DATAGRAM];
+} udptl_fec_tx_buffer_t;
+
+typedef struct {
+ int buf_len;
+ uint8_t buf[LOCAL_FAX_MAX_DATAGRAM];
+ int fec_len[MAX_FEC_ENTRIES];
+ uint8_t fec[MAX_FEC_ENTRIES][LOCAL_FAX_MAX_DATAGRAM];
+ int fec_span;
+ int fec_entries;
+} udptl_fec_rx_buffer_t;
+
+/*! \brief Structure for an UDPTL session */
+struct ast_udptl {
+ int fd;
+ char resp;
+ struct ast_frame f[16];
+ unsigned char rawdata[8192 + AST_FRIENDLY_OFFSET];
+ unsigned int lasteventseqn;
+ int nat;
+ int flags;
+ struct sockaddr_in us;
+ struct sockaddr_in them;
+ int *ioid;
+ struct sched_context *sched;
+ struct io_context *io;
+ void *data;
+ ast_udptl_callback callback;
+ int udptl_offered_from_local;
+
+ /*! This option indicates the error correction scheme used in transmitted UDPTL
+ packets. */
+ int error_correction_scheme;
+
+ /*! This option indicates the number of error correction entries transmitted in
+ UDPTL packets. */
+ int error_correction_entries;
+
+ /*! This option indicates the span of the error correction entries in transmitted
+ UDPTL packets (FEC only). */
+ int error_correction_span;
+
+ /*! This option indicates the maximum size of a UDPTL packet that can be accepted by
+ the remote device. */
+ int far_max_datagram_size;
+
+ /*! This option indicates the maximum size of a UDPTL packet that we are prepared to
+ accept. */
+ int local_max_datagram_size;
+
+ int verbose;
+
+ struct sockaddr_in far;
+
+ int tx_seq_no;
+ int rx_seq_no;
+ int rx_expected_seq_no;
+
+ udptl_fec_tx_buffer_t tx[UDPTL_BUF_MASK + 1];
+ udptl_fec_rx_buffer_t rx[UDPTL_BUF_MASK + 1];
+};
+
+static AST_RWLIST_HEAD_STATIC(protos, ast_udptl_protocol);
+
+static int udptl_rx_packet(struct ast_udptl *s, uint8_t *buf, int len);
+static int udptl_build_packet(struct ast_udptl *s, uint8_t *buf, uint8_t *ifp, int ifp_len);
+
+static inline int udptl_debug_test_addr(struct sockaddr_in *addr)
+{
+ if (udptldebug == 0)
+ return 0;
+ if (udptldebugaddr.sin_addr.s_addr) {
+ if (((ntohs(udptldebugaddr.sin_port) != 0)
+ && (udptldebugaddr.sin_port != addr->sin_port))
+ || (udptldebugaddr.sin_addr.s_addr != addr->sin_addr.s_addr))
+ return 0;
+ }
+ return 1;
+}
+
+static int decode_length(uint8_t *buf, int limit, int *len, int *pvalue)
+{
+ if ((buf[*len] & 0x80) == 0) {
+ if (*len >= limit)
+ return -1;
+ *pvalue = buf[*len];
+ (*len)++;
+ return 0;
+ }
+ if ((buf[*len] & 0x40) == 0) {
+ if (*len >= limit - 1)
+ return -1;
+ *pvalue = (buf[*len] & 0x3F) << 8;
+ (*len)++;
+ *pvalue |= buf[*len];
+ (*len)++;
+ return 0;
+ }
+ if (*len >= limit)
+ return -1;
+ *pvalue = (buf[*len] & 0x3F) << 14;
+ (*len)++;
+ /* Indicate we have a fragment */
+ return 1;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int decode_open_type(uint8_t *buf, int limit, int *len, const uint8_t **p_object, int *p_num_octets)
+{
+ int octet_cnt;
+ int octet_idx;
+ int stat;
+ int i;
+ const uint8_t **pbuf;
+
+ for (octet_idx = 0, *p_num_octets = 0; ; octet_idx += octet_cnt) {
+ if ((stat = decode_length(buf, limit, len, &octet_cnt)) < 0)
+ return -1;
+ if (octet_cnt > 0) {
+ *p_num_octets += octet_cnt;
+
+ pbuf = &p_object[octet_idx];
+ i = 0;
+ /* Make sure the buffer contains at least the number of bits requested */
+ if ((*len + octet_cnt) > limit)
+ return -1;
+
+ *pbuf = &buf[*len];
+ *len += octet_cnt;
+ }
+ if (stat == 0)
+ break;
+ }
+ return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int encode_length(uint8_t *buf, int *len, int value)
+{
+ int multiplier;
+
+ if (value < 0x80) {
+ /* 1 octet */
+ buf[*len] = value;
+ (*len)++;
+ return value;
+ }
+ if (value < 0x4000) {
+ /* 2 octets */
+ /* Set the first bit of the first octet */
+ buf[*len] = ((0x8000 | value) >> 8) & 0xFF;
+ (*len)++;
+ buf[*len] = value & 0xFF;
+ (*len)++;
+ return value;
+ }
+ /* Fragmentation */
+ multiplier = (value < 0x10000) ? (value >> 14) : 4;
+ /* Set the first 2 bits of the octet */
+ buf[*len] = 0xC0 | multiplier;
+ (*len)++;
+ return multiplier << 14;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int encode_open_type(uint8_t *buf, int *len, const uint8_t *data, int num_octets)
+{
+ int enclen;
+ int octet_idx;
+ uint8_t zero_byte;
+
+ /* If open type is of zero length, add a single zero byte (10.1) */
+ if (num_octets == 0) {
+ zero_byte = 0;
+ data = &zero_byte;
+ num_octets = 1;
+ }
+ /* Encode the open type */
+ for (octet_idx = 0; ; num_octets -= enclen, octet_idx += enclen) {
+ if ((enclen = encode_length(buf, len, num_octets)) < 0)
+ return -1;
+ if (enclen > 0) {
+ memcpy(&buf[*len], &data[octet_idx], enclen);
+ *len += enclen;
+ }
+ if (enclen >= num_octets)
+ break;
+ }
+
+ return 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int udptl_rx_packet(struct ast_udptl *s, uint8_t *buf, int len)
+{
+ int stat;
+ int stat2;
+ int i;
+ int j;
+ int k;
+ int l;
+ int m;
+ int x;
+ int limit;
+ int which;
+ int ptr;
+ int count;
+ int total_count;
+ int seq_no;
+ const uint8_t *ifp;
+ const uint8_t *data;
+ int ifp_len;
+ int repaired[16];
+ const uint8_t *bufs[16];
+ int lengths[16];
+ int span;
+ int entries;
+ int ifp_no;
+
+ ptr = 0;
+ ifp_no = 0;
+ memset(&s->f[0], 0, sizeof(s->f[0]));
+
+ /* Decode seq_number */
+ if (ptr + 2 > len)
+ return -1;
+ seq_no = (buf[0] << 8) | buf[1];
+ ptr += 2;
+
+ /* Break out the primary packet */
+ if ((stat = decode_open_type(buf, len, &ptr, &ifp, &ifp_len)) != 0)
+ return -1;
+ /* Decode error_recovery */
+ if (ptr + 1 > len)
+ return -1;
+ if ((buf[ptr++] & 0x80) == 0) {
+ /* Secondary packet mode for error recovery */
+ if (seq_no > s->rx_seq_no) {
+ /* We received a later packet than we expected, so we need to check if we can fill in the gap from the
+ secondary packets. */
+ total_count = 0;
+ do {
+ if ((stat2 = decode_length(buf, len, &ptr, &count)) < 0)
+ return -1;
+ for (i = 0; i < count; i++) {
+ if ((stat = decode_open_type(buf, len, &ptr, &bufs[total_count + i], &lengths[total_count + i])) != 0)
+ return -1;
+ }
+ total_count += count;
+ }
+ while (stat2 > 0);
+ /* Step through in reverse order, so we go oldest to newest */
+ for (i = total_count; i > 0; i--) {
+ if (seq_no - i >= s->rx_seq_no) {
+ /* This one wasn't seen before */
+ /* Decode the secondary IFP packet */
+ //fprintf(stderr, "Secondary %d, len %d\n", seq_no - i, lengths[i - 1]);
+ s->f[ifp_no].frametype = AST_FRAME_MODEM;
+ s->f[ifp_no].subclass = AST_MODEM_T38;
+
+ s->f[ifp_no].mallocd = 0;
+ s->f[ifp_no].seqno = seq_no - i;
+ s->f[ifp_no].datalen = lengths[i - 1];
+ s->f[ifp_no].data = (uint8_t *) bufs[i - 1];
+ s->f[ifp_no].offset = 0;
+ s->f[ifp_no].src = "UDPTL";
+ if (ifp_no > 0)
+ AST_LIST_NEXT(&s->f[ifp_no - 1], frame_list) = &s->f[ifp_no];
+ AST_LIST_NEXT(&s->f[ifp_no], frame_list) = NULL;
+ ifp_no++;
+ }
+ }
+ }
+ }
+ else
+ {
+ /* FEC mode for error recovery */
+ /* Our buffers cannot tolerate overlength IFP packets in FEC mode */
+ if (ifp_len > LOCAL_FAX_MAX_DATAGRAM)
+ return -1;
+ /* Update any missed slots in the buffer */
+ for ( ; seq_no > s->rx_seq_no; s->rx_seq_no++) {
+ x = s->rx_seq_no & UDPTL_BUF_MASK;
+ s->rx[x].buf_len = -1;
+ s->rx[x].fec_len[0] = 0;
+ s->rx[x].fec_span = 0;
+ s->rx[x].fec_entries = 0;
+ }
+
+ x = seq_no & UDPTL_BUF_MASK;
+
+ memset(repaired, 0, sizeof(repaired));
+
+ /* Save the new IFP packet */
+ memcpy(s->rx[x].buf, ifp, ifp_len);
+ s->rx[x].buf_len = ifp_len;
+ repaired[x] = TRUE;
+
+ /* Decode the FEC packets */
+ /* The span is defined as an unconstrained integer, but will never be more
+ than a small value. */
+ if (ptr + 2 > len)
+ return -1;
+ if (buf[ptr++] != 1)
+ return -1;
+ span = buf[ptr++];
+ s->rx[x].fec_span = span;
+
+ /* The number of entries is defined as a length, but will only ever be a small
+ value. Treat it as such. */
+ if (ptr + 1 > len)
+ return -1;
+ entries = buf[ptr++];
+ s->rx[x].fec_entries = entries;
+
+ /* Decode the elements */
+ for (i = 0; i < entries; i++) {
+ if ((stat = decode_open_type(buf, len, &ptr, &data, &s->rx[x].fec_len[i])) != 0)
+ return -1;
+ if (s->rx[x].fec_len[i] > LOCAL_FAX_MAX_DATAGRAM)
+ return -1;
+
+ /* Save the new FEC data */
+ memcpy(s->rx[x].fec[i], data, s->rx[x].fec_len[i]);
+#if 0
+ fprintf(stderr, "FEC: ");
+ for (j = 0; j < s->rx[x].fec_len[i]; j++)
+ fprintf(stderr, "%02X ", data[j]);
+ fprintf(stderr, "\n");
+#endif
+ }
+
+ /* See if we can reconstruct anything which is missing */
+ /* TODO: this does not comprehensively hunt back and repair everything that is possible */
+ for (l = x; l != ((x - (16 - span*entries)) & UDPTL_BUF_MASK); l = (l - 1) & UDPTL_BUF_MASK) {
+ if (s->rx[l].fec_len[0] <= 0)
+ continue;
+ for (m = 0; m < s->rx[l].fec_entries; m++) {
+ limit = (l + m) & UDPTL_BUF_MASK;
+ for (which = -1, k = (limit - s->rx[l].fec_span * s->rx[l].fec_entries) & UDPTL_BUF_MASK; k != limit; k = (k + s->rx[l].fec_entries) & UDPTL_BUF_MASK) {
+ if (s->rx[k].buf_len <= 0)
+ which = (which == -1) ? k : -2;
+ }
+ if (which >= 0) {
+ /* Repairable */
+ for (j = 0; j < s->rx[l].fec_len[m]; j++) {
+ s->rx[which].buf[j] = s->rx[l].fec[m][j];
+ for (k = (limit - s->rx[l].fec_span * s->rx[l].fec_entries) & UDPTL_BUF_MASK; k != limit; k = (k + s->rx[l].fec_entries) & UDPTL_BUF_MASK)
+ s->rx[which].buf[j] ^= (s->rx[k].buf_len > j) ? s->rx[k].buf[j] : 0;
+ }
+ s->rx[which].buf_len = s->rx[l].fec_len[m];
+ repaired[which] = TRUE;
+ }
+ }
+ }
+ /* Now play any new packets forwards in time */
+ for (l = (x + 1) & UDPTL_BUF_MASK, j = seq_no - UDPTL_BUF_MASK; l != x; l = (l + 1) & UDPTL_BUF_MASK, j++) {
+ if (repaired[l]) {
+ //fprintf(stderr, "Fixed packet %d, len %d\n", j, l);
+ s->f[ifp_no].frametype = AST_FRAME_MODEM;
+ s->f[ifp_no].subclass = AST_MODEM_T38;
+
+ s->f[ifp_no].mallocd = 0;
+ s->f[ifp_no].seqno = j;
+ s->f[ifp_no].datalen = s->rx[l].buf_len;
+ s->f[ifp_no].data = s->rx[l].buf;
+ s->f[ifp_no].offset = 0;
+ s->f[ifp_no].src = "UDPTL";
+ if (ifp_no > 0)
+ AST_LIST_NEXT(&s->f[ifp_no - 1], frame_list) = &s->f[ifp_no];
+ AST_LIST_NEXT(&s->f[ifp_no], frame_list) = NULL;
+ ifp_no++;
+ }
+ }
+ }
+
+ /* If packets are received out of sequence, we may have already processed this packet from the error
+ recovery information in a packet already received. */
+ if (seq_no >= s->rx_seq_no) {
+ /* Decode the primary IFP packet */
+ s->f[ifp_no].frametype = AST_FRAME_MODEM;
+ s->f[ifp_no].subclass = AST_MODEM_T38;
+
+ s->f[ifp_no].mallocd = 0;
+ s->f[ifp_no].seqno = seq_no;
+ s->f[ifp_no].datalen = ifp_len;
+ s->f[ifp_no].data = (uint8_t *) ifp;
+ s->f[ifp_no].offset = 0;
+ s->f[ifp_no].src = "UDPTL";
+ if (ifp_no > 0)
+ AST_LIST_NEXT(&s->f[ifp_no - 1], frame_list) = &s->f[ifp_no];
+ AST_LIST_NEXT(&s->f[ifp_no], frame_list) = NULL;
+
+ ifp_no++;
+ }
+
+ s->rx_seq_no = seq_no + 1;
+ return ifp_no;
+}
+/*- End of function --------------------------------------------------------*/
+
+static int udptl_build_packet(struct ast_udptl *s, uint8_t *buf, uint8_t *ifp, int ifp_len)
+{
+ uint8_t fec[LOCAL_FAX_MAX_DATAGRAM];
+ int i;
+ int j;
+ int seq;
+ int entry;
+ int entries;
+ int span;
+ int m;
+ int len;
+ int limit;
+ int high_tide;
+
+ seq = s->tx_seq_no & 0xFFFF;
+
+ /* Map the sequence number to an entry in the circular buffer */
+ entry = seq & UDPTL_BUF_MASK;
+
+ /* We save the message in a circular buffer, for generating FEC or
+ redundancy sets later on. */
+ s->tx[entry].buf_len = ifp_len;
+ memcpy(s->tx[entry].buf, ifp, ifp_len);
+
+ /* Build the UDPTLPacket */
+
+ len = 0;
+ /* Encode the sequence number */
+ buf[len++] = (seq >> 8) & 0xFF;
+ buf[len++] = seq & 0xFF;
+
+ /* Encode the primary IFP packet */
+ if (encode_open_type(buf, &len, ifp, ifp_len) < 0)
+ return -1;
+
+ /* Encode the appropriate type of error recovery information */
+ switch (s->error_correction_scheme)
+ {
+ case UDPTL_ERROR_CORRECTION_NONE:
+ /* Encode the error recovery type */
+ buf[len++] = 0x00;
+ /* The number of entries will always be zero, so it is pointless allowing
+ for the fragmented case here. */
+ if (encode_length(buf, &len, 0) < 0)
+ return -1;
+ break;
+ case UDPTL_ERROR_CORRECTION_REDUNDANCY:
+ /* Encode the error recovery type */
+ buf[len++] = 0x00;
+ if (s->tx_seq_no > s->error_correction_entries)
+ entries = s->error_correction_entries;
+ else
+ entries = s->tx_seq_no;
+ /* The number of entries will always be small, so it is pointless allowing
+ for the fragmented case here. */
+ if (encode_length(buf, &len, entries) < 0)
+ return -1;
+ /* Encode the elements */
+ for (i = 0; i < entries; i++) {
+ j = (entry - i - 1) & UDPTL_BUF_MASK;
+ if (encode_open_type(buf, &len, s->tx[j].buf, s->tx[j].buf_len) < 0)
+ return -1;
+ }
+ break;
+ case UDPTL_ERROR_CORRECTION_FEC:
+ span = s->error_correction_span;
+ entries = s->error_correction_entries;
+ if (seq < s->error_correction_span*s->error_correction_entries) {
+ /* In the initial stages, wind up the FEC smoothly */
+ entries = seq/s->error_correction_span;
+ if (seq < s->error_correction_span)
+ span = 0;
+ }
+ /* Encode the error recovery type */
+ buf[len++] = 0x80;
+ /* Span is defined as an inconstrained integer, which it dumb. It will only
+ ever be a small value. Treat it as such. */
+ buf[len++] = 1;
+ buf[len++] = span;
+ /* The number of entries is defined as a length, but will only ever be a small
+ value. Treat it as such. */
+ buf[len++] = entries;
+ for (m = 0; m < entries; m++) {
+ /* Make an XOR'ed entry the maximum length */
+ limit = (entry + m) & UDPTL_BUF_MASK;
+ high_tide = 0;
+ for (i = (limit - span*entries) & UDPTL_BUF_MASK; i != limit; i = (i + entries) & UDPTL_BUF_MASK) {
+ if (high_tide < s->tx[i].buf_len) {
+ for (j = 0; j < high_tide; j++)
+ fec[j] ^= s->tx[i].buf[j];
+ for ( ; j < s->tx[i].buf_len; j++)
+ fec[j] = s->tx[i].buf[j];
+ high_tide = s->tx[i].buf_len;
+ } else {
+ for (j = 0; j < s->tx[i].buf_len; j++)
+ fec[j] ^= s->tx[i].buf[j];
+ }
+ }
+ if (encode_open_type(buf, &len, fec, high_tide) < 0)
+ return -1;
+ }
+ break;
+ }
+
+ if (s->verbose)
+ fprintf(stderr, "\n");
+
+ s->tx_seq_no++;
+ return len;
+}
+
+int ast_udptl_fd(struct ast_udptl *udptl)
+{
+ return udptl->fd;
+}
+
+void ast_udptl_set_data(struct ast_udptl *udptl, void *data)
+{
+ udptl->data = data;
+}
+
+void ast_udptl_set_callback(struct ast_udptl *udptl, ast_udptl_callback callback)
+{
+ udptl->callback = callback;
+}
+
+void ast_udptl_setnat(struct ast_udptl *udptl, int nat)
+{
+ udptl->nat = nat;
+}
+
+static int udptlread(int *id, int fd, short events, void *cbdata)
+{
+ struct ast_udptl *udptl = cbdata;
+ struct ast_frame *f;
+
+ if ((f = ast_udptl_read(udptl))) {
+ if (udptl->callback)
+ udptl->callback(udptl, f, udptl->data);
+ }
+ return 1;
+}
+
+struct ast_frame *ast_udptl_read(struct ast_udptl *udptl)
+{
+ int res;
+ struct sockaddr_in sin;
+ socklen_t len;
+ uint16_t seqno = 0;
+ uint16_t *udptlheader;
+
+ len = sizeof(sin);
+
+ /* Cache where the header will go */
+ res = recvfrom(udptl->fd,
+ udptl->rawdata + AST_FRIENDLY_OFFSET,
+ sizeof(udptl->rawdata) - AST_FRIENDLY_OFFSET,
+ 0,
+ (struct sockaddr *) &sin,
+ &len);
+ udptlheader = (uint16_t *)(udptl->rawdata + AST_FRIENDLY_OFFSET);
+ if (res < 0) {
+ if (errno != EAGAIN)
+ ast_log(LOG_WARNING, "UDPTL read error: %s\n", strerror(errno));
+ if (errno == EBADF)
+ CRASH;
+ return &ast_null_frame;
+ }
+
+ /* Ignore if the other side hasn't been given an address yet. */
+ if (!udptl->them.sin_addr.s_addr || !udptl->them.sin_port)
+ return &ast_null_frame;
+
+ if (udptl->nat) {
+ /* Send to whoever sent to us */
+ if ((udptl->them.sin_addr.s_addr != sin.sin_addr.s_addr) ||
+ (udptl->them.sin_port != sin.sin_port)) {
+ memcpy(&udptl->them, &sin, sizeof(udptl->them));
+ ast_debug(1, "UDPTL NAT: Using address %s:%d\n", ast_inet_ntoa(udptl->them.sin_addr), ntohs(udptl->them.sin_port));
+ }
+ }
+
+ if (udptl_debug_test_addr(&sin)) {
+ ast_verb(1, "Got UDPTL packet from %s:%d (type %d, seq %d, len %d)\n",
+ ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), 0, seqno, res);
+ }
+#if 0
+ printf("Got UDPTL packet from %s:%d (seq %d, len = %d)\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), seqno, res);
+#endif
+ if (udptl_rx_packet(udptl, udptl->rawdata + AST_FRIENDLY_OFFSET, res) < 1)
+ return &ast_null_frame;
+
+ return &udptl->f[0];
+}
+
+void ast_udptl_offered_from_local(struct ast_udptl* udptl, int local)
+{
+ if (udptl)
+ udptl->udptl_offered_from_local = local;
+ else
+ ast_log(LOG_WARNING, "udptl structure is null\n");
+}
+
+int ast_udptl_get_error_correction_scheme(struct ast_udptl* udptl)
+{
+ if (udptl)
+ return udptl->error_correction_scheme;
+ else {
+ ast_log(LOG_WARNING, "udptl structure is null\n");
+ return -1;
+ }
+}
+
+void ast_udptl_set_error_correction_scheme(struct ast_udptl* udptl, int ec)
+{
+ if (udptl) {
+ switch (ec) {
+ case UDPTL_ERROR_CORRECTION_FEC:
+ udptl->error_correction_scheme = UDPTL_ERROR_CORRECTION_FEC;
+ break;
+ case UDPTL_ERROR_CORRECTION_REDUNDANCY:
+ udptl->error_correction_scheme = UDPTL_ERROR_CORRECTION_REDUNDANCY;
+ break;
+ case UDPTL_ERROR_CORRECTION_NONE:
+ udptl->error_correction_scheme = UDPTL_ERROR_CORRECTION_NONE;
+ break;
+ default:
+ ast_log(LOG_WARNING, "error correction parameter invalid\n");
+ };
+ } else
+ ast_log(LOG_WARNING, "udptl structure is null\n");
+}
+
+int ast_udptl_get_local_max_datagram(struct ast_udptl* udptl)
+{
+ if (udptl)
+ return udptl->local_max_datagram_size;
+ else {
+ ast_log(LOG_WARNING, "udptl structure is null\n");
+ return -1;
+ }
+}
+
+int ast_udptl_get_far_max_datagram(struct ast_udptl* udptl)
+{
+ if (udptl)
+ return udptl->far_max_datagram_size;
+ else {
+ ast_log(LOG_WARNING, "udptl structure is null\n");
+ return -1;
+ }
+}
+
+void ast_udptl_set_local_max_datagram(struct ast_udptl* udptl, int max_datagram)
+{
+ if (udptl)
+ udptl->local_max_datagram_size = max_datagram;
+ else
+ ast_log(LOG_WARNING, "udptl structure is null\n");
+}
+
+void ast_udptl_set_far_max_datagram(struct ast_udptl* udptl, int max_datagram)
+{
+ if (udptl)
+ udptl->far_max_datagram_size = max_datagram;
+ else
+ ast_log(LOG_WARNING, "udptl structure is null\n");
+}
+
+struct ast_udptl *ast_udptl_new_with_bindaddr(struct sched_context *sched, struct io_context *io, int callbackmode, struct in_addr addr)
+{
+ struct ast_udptl *udptl;
+ int x;
+ int startplace;
+ int i;
+ long int flags;
+
+ if (!(udptl = ast_calloc(1, sizeof(*udptl))))
+ return NULL;
+
+ if (udptlfectype == 2)
+ udptl->error_correction_scheme = UDPTL_ERROR_CORRECTION_FEC;
+ else if (udptlfectype == 1)
+ udptl->error_correction_scheme = UDPTL_ERROR_CORRECTION_REDUNDANCY;
+ else
+ udptl->error_correction_scheme = UDPTL_ERROR_CORRECTION_NONE;
+ udptl->error_correction_span = udptlfecspan;
+ udptl->error_correction_entries = udptlfecentries;
+
+ udptl->far_max_datagram_size = udptlmaxdatagram;
+ udptl->local_max_datagram_size = udptlmaxdatagram;
+
+ memset(&udptl->rx, 0, sizeof(udptl->rx));
+ memset(&udptl->tx, 0, sizeof(udptl->tx));
+ for (i = 0; i <= UDPTL_BUF_MASK; i++) {
+ udptl->rx[i].buf_len = -1;
+ udptl->tx[i].buf_len = -1;
+ }
+
+ udptl->them.sin_family = AF_INET;
+ udptl->us.sin_family = AF_INET;
+
+ if ((udptl->fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ ast_free(udptl);
+ ast_log(LOG_WARNING, "Unable to allocate socket: %s\n", strerror(errno));
+ return NULL;
+ }
+ flags = fcntl(udptl->fd, F_GETFL);
+ fcntl(udptl->fd, F_SETFL, flags | O_NONBLOCK);
+#ifdef SO_NO_CHECK
+ if (nochecksums)
+ setsockopt(udptl->fd, SOL_SOCKET, SO_NO_CHECK, &nochecksums, sizeof(nochecksums));
+#endif
+ /* Find us a place */
+ x = (ast_random() % (udptlend - udptlstart)) + udptlstart;
+ startplace = x;
+ for (;;) {
+ udptl->us.sin_port = htons(x);
+ udptl->us.sin_addr = addr;
+ if (bind(udptl->fd, (struct sockaddr *) &udptl->us, sizeof(udptl->us)) == 0)
+ break;
+ if (errno != EADDRINUSE) {
+ ast_log(LOG_WARNING, "Unexpected bind error: %s\n", strerror(errno));
+ close(udptl->fd);
+ ast_free(udptl);
+ return NULL;
+ }
+ if (++x > udptlend)
+ x = udptlstart;
+ if (x == startplace) {
+ ast_log(LOG_WARNING, "No UDPTL ports remaining\n");
+ close(udptl->fd);
+ ast_free(udptl);
+ return NULL;
+ }
+ }
+ if (io && sched && callbackmode) {
+ /* Operate this one in a callback mode */
+ udptl->sched = sched;
+ udptl->io = io;
+ udptl->ioid = ast_io_add(udptl->io, udptl->fd, udptlread, AST_IO_IN, udptl);
+ }
+ return udptl;
+}
+
+struct ast_udptl *ast_udptl_new(struct sched_context *sched, struct io_context *io, int callbackmode)
+{
+ struct in_addr ia;
+ memset(&ia, 0, sizeof(ia));
+ return ast_udptl_new_with_bindaddr(sched, io, callbackmode, ia);
+}
+
+int ast_udptl_setqos(struct ast_udptl *udptl, int tos, int cos)
+{
+ return ast_netsock_set_qos(udptl->fd, tos, cos, "UDPTL");
+}
+
+void ast_udptl_set_peer(struct ast_udptl *udptl, struct sockaddr_in *them)
+{
+ udptl->them.sin_port = them->sin_port;
+ udptl->them.sin_addr = them->sin_addr;
+}
+
+void ast_udptl_get_peer(struct ast_udptl *udptl, struct sockaddr_in *them)
+{
+ them->sin_family = AF_INET;
+ them->sin_port = udptl->them.sin_port;
+ them->sin_addr = udptl->them.sin_addr;
+}
+
+void ast_udptl_get_us(struct ast_udptl *udptl, struct sockaddr_in *us)
+{
+ memcpy(us, &udptl->us, sizeof(udptl->us));
+}
+
+void ast_udptl_stop(struct ast_udptl *udptl)
+{
+ memset(&udptl->them.sin_addr, 0, sizeof(udptl->them.sin_addr));
+ memset(&udptl->them.sin_port, 0, sizeof(udptl->them.sin_port));
+}
+
+void ast_udptl_destroy(struct ast_udptl *udptl)
+{
+ if (udptl->ioid)
+ ast_io_remove(udptl->io, udptl->ioid);
+ if (udptl->fd > -1)
+ close(udptl->fd);
+ ast_free(udptl);
+}
+
+int ast_udptl_write(struct ast_udptl *s, struct ast_frame *f)
+{
+ int seq;
+ int len;
+ int res;
+ uint8_t buf[LOCAL_FAX_MAX_DATAGRAM];
+
+ /* If we have no peer, return immediately */
+ if (s->them.sin_addr.s_addr == INADDR_ANY)
+ return 0;
+
+ /* If there is no data length, return immediately */
+ if (f->datalen == 0)
+ return 0;
+
+ if (f->frametype != AST_FRAME_MODEM) {
+ ast_log(LOG_WARNING, "UDPTL can only send T.38 data\n");
+ return -1;
+ }
+
+ /* Save seq_no for debug output because udptl_build_packet increments it */
+ seq = s->tx_seq_no & 0xFFFF;
+
+ /* Cook up the UDPTL packet, with the relevant EC info. */
+ len = udptl_build_packet(s, buf, f->data, f->datalen);
+
+ if (len > 0 && s->them.sin_port && s->them.sin_addr.s_addr) {
+ if ((res = sendto(s->fd, buf, len, 0, (struct sockaddr *) &s->them, sizeof(s->them))) < 0)
+ ast_log(LOG_NOTICE, "UDPTL Transmission error to %s:%d: %s\n", ast_inet_ntoa(s->them.sin_addr), ntohs(s->them.sin_port), strerror(errno));
+#if 0
+ printf("Sent %d bytes of UDPTL data to %s:%d\n", res, ast_inet_ntoa(udptl->them.sin_addr), ntohs(udptl->them.sin_port));
+#endif
+ if (udptl_debug_test_addr(&s->them))
+ ast_verb(1, "Sent UDPTL packet to %s:%d (type %d, seq %d, len %d)\n",
+ ast_inet_ntoa(s->them.sin_addr),
+ ntohs(s->them.sin_port), 0, seq, len);
+ }
+
+ return 0;
+}
+
+void ast_udptl_proto_unregister(struct ast_udptl_protocol *proto)
+{
+ AST_RWLIST_WRLOCK(&protos);
+ AST_RWLIST_REMOVE(&protos, proto, list);
+ AST_RWLIST_UNLOCK(&protos);
+}
+
+int ast_udptl_proto_register(struct ast_udptl_protocol *proto)
+{
+ struct ast_udptl_protocol *cur;
+
+ AST_RWLIST_WRLOCK(&protos);
+ AST_RWLIST_TRAVERSE(&protos, cur, list) {
+ if (cur->type == proto->type) {
+ ast_log(LOG_WARNING, "Tried to register same protocol '%s' twice\n", cur->type);
+ AST_RWLIST_UNLOCK(&protos);
+ return -1;
+ }
+ }
+ AST_RWLIST_INSERT_TAIL(&protos, proto, list);
+ AST_RWLIST_UNLOCK(&protos);
+ return 0;
+}
+
+static struct ast_udptl_protocol *get_proto(struct ast_channel *chan)
+{
+ struct ast_udptl_protocol *cur = NULL;
+
+ AST_RWLIST_RDLOCK(&protos);
+ AST_RWLIST_TRAVERSE(&protos, cur, list) {
+ if (cur->type == chan->tech->type)
+ break;
+ }
+ AST_RWLIST_UNLOCK(&protos);
+
+ return cur;
+}
+
+int ast_udptl_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc)
+{
+ struct ast_frame *f;
+ struct ast_channel *who;
+ struct ast_channel *cs[3];
+ struct ast_udptl *p0;
+ struct ast_udptl *p1;
+ struct ast_udptl_protocol *pr0;
+ struct ast_udptl_protocol *pr1;
+ struct sockaddr_in ac0;
+ struct sockaddr_in ac1;
+ struct sockaddr_in t0;
+ struct sockaddr_in t1;
+ void *pvt0;
+ void *pvt1;
+ int to;
+
+ ast_channel_lock(c0);
+ while (ast_channel_trylock(c1)) {
+ ast_channel_unlock(c0);
+ usleep(1);
+ ast_channel_lock(c0);
+ }
+ pr0 = get_proto(c0);
+ pr1 = get_proto(c1);
+ if (!pr0) {
+ ast_log(LOG_WARNING, "Can't find native functions for channel '%s'\n", c0->name);
+ ast_channel_unlock(c0);
+ ast_channel_unlock(c1);
+ return -1;
+ }
+ if (!pr1) {
+ ast_log(LOG_WARNING, "Can't find native functions for channel '%s'\n", c1->name);
+ ast_channel_unlock(c0);
+ ast_channel_unlock(c1);
+ return -1;
+ }
+ pvt0 = c0->tech_pvt;
+ pvt1 = c1->tech_pvt;
+ p0 = pr0->get_udptl_info(c0);
+ p1 = pr1->get_udptl_info(c1);
+ if (!p0 || !p1) {
+ /* Somebody doesn't want to play... */
+ ast_channel_unlock(c0);
+ ast_channel_unlock(c1);
+ return -2;
+ }
+ if (pr0->set_udptl_peer(c0, p1)) {
+ ast_log(LOG_WARNING, "Channel '%s' failed to talk to '%s'\n", c0->name, c1->name);
+ } else {
+ /* Store UDPTL peer */
+ ast_udptl_get_peer(p1, &ac1);
+ }
+ if (pr1->set_udptl_peer(c1, p0))
+ ast_log(LOG_WARNING, "Channel '%s' failed to talk back to '%s'\n", c1->name, c0->name);
+ else {
+ /* Store UDPTL peer */
+ ast_udptl_get_peer(p0, &ac0);
+ }
+ ast_channel_unlock(c0);
+ ast_channel_unlock(c1);
+ cs[0] = c0;
+ cs[1] = c1;
+ cs[2] = NULL;
+ for (;;) {
+ if ((c0->tech_pvt != pvt0) ||
+ (c1->tech_pvt != pvt1) ||
+ (c0->masq || c0->masqr || c1->masq || c1->masqr)) {
+ ast_debug(1, "Oooh, something is weird, backing out\n");
+ /* Tell it to try again later */
+ return -3;
+ }
+ to = -1;
+ ast_udptl_get_peer(p1, &t1);
+ ast_udptl_get_peer(p0, &t0);
+ if (inaddrcmp(&t1, &ac1)) {
+ ast_debug(1, "Oooh, '%s' changed end address to %s:%d\n",
+ c1->name, ast_inet_ntoa(t1.sin_addr), ntohs(t1.sin_port));
+ ast_debug(1, "Oooh, '%s' was %s:%d\n",
+ c1->name, ast_inet_ntoa(ac1.sin_addr), ntohs(ac1.sin_port));
+ memcpy(&ac1, &t1, sizeof(ac1));
+ }
+ if (inaddrcmp(&t0, &ac0)) {
+ ast_debug(1, "Oooh, '%s' changed end address to %s:%d\n",
+ c0->name, ast_inet_ntoa(t0.sin_addr), ntohs(t0.sin_port));
+ ast_debug(1, "Oooh, '%s' was %s:%d\n",
+ c0->name, ast_inet_ntoa(ac0.sin_addr), ntohs(ac0.sin_port));
+ memcpy(&ac0, &t0, sizeof(ac0));
+ }
+ who = ast_waitfor_n(cs, 2, &to);
+ if (!who) {
+ ast_debug(1, "Ooh, empty read...\n");
+ /* check for hangup / whentohangup */
+ if (ast_check_hangup(c0) || ast_check_hangup(c1))
+ break;
+ continue;
+ }
+ f = ast_read(who);
+ if (!f) {
+ *fo = f;
+ *rc = who;
+ ast_debug(1, "Oooh, got a %s\n", f ? "digit" : "hangup");
+ /* That's all we needed */
+ return 0;
+ } else {
+ if (f->frametype == AST_FRAME_MODEM) {
+ /* Forward T.38 frames if they happen upon us */
+ if (who == c0) {
+ ast_write(c1, f);
+ } else if (who == c1) {
+ ast_write(c0, f);
+ }
+ }
+ ast_frfree(f);
+ }
+ /* Swap priority. Not that it's a big deal at this point */
+ cs[2] = cs[0];
+ cs[0] = cs[1];
+ cs[1] = cs[2];
+ }
+ return -1;
+}
+
+static char *handle_cli_udptl_debug_ip(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct hostent *hp;
+ struct ast_hostent ahp;
+ int port;
+ char *p;
+ char *arg;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "udptl debug ip";
+ e->usage =
+ "Usage: udptl debug [ip host[:port]]\n"
+ " Enable dumping of all UDPTL packets to and from host.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ port = 0;
+
+ if (a->argc != 4)
+ return CLI_SHOWUSAGE;
+ arg = a->argv[3];
+ p = strstr(arg, ":");
+ if (p) {
+ *p = '\0';
+ p++;
+ port = atoi(p);
+ }
+ hp = ast_gethostbyname(arg, &ahp);
+ if (hp == NULL)
+ return CLI_SHOWUSAGE;
+ udptldebugaddr.sin_family = AF_INET;
+ memcpy(&udptldebugaddr.sin_addr, hp->h_addr, sizeof(udptldebugaddr.sin_addr));
+ udptldebugaddr.sin_port = htons(port);
+ if (port == 0)
+ ast_cli(a->fd, "UDPTL Debugging Enabled for IP: %s\n", ast_inet_ntoa(udptldebugaddr.sin_addr));
+ else
+ ast_cli(a->fd, "UDPTL Debugging Enabled for IP: %s:%d\n", ast_inet_ntoa(udptldebugaddr.sin_addr), port);
+ udptldebug = 1;
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_udptl_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "udptl debug";
+ e->usage =
+ "Usage: udptl debug\n"
+ " Enable dumping of all UDPTL packets.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 2)
+ return CLI_SHOWUSAGE;
+
+ udptldebug = 1;
+ memset(&udptldebugaddr, 0, sizeof(udptldebugaddr));
+
+ ast_cli(a->fd, "UDPTL Debugging Enabled\n");
+ return CLI_SUCCESS;
+}
+
+static char *handle_cli_udptl_debug_off(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "udptl debug off";
+ e->usage =
+ "Usage: udptl debug off\n"
+ " Disable dumping of all UDPTL packets.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+
+ udptldebug = 0;
+
+ ast_cli(a->fd, "UDPTL Debugging Disabled\n");
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli_udptl[] = {
+ AST_CLI_DEFINE(handle_cli_udptl_debug, "Enable UDPTL debugging"),
+ AST_CLI_DEFINE(handle_cli_udptl_debug_ip, "Enable UDPTL debugging on IP"),
+ AST_CLI_DEFINE(handle_cli_udptl_debug_off, "Disable UDPTL debugging")
+};
+
+static void __ast_udptl_reload(int reload)
+{
+ struct ast_config *cfg;
+ const char *s;
+ struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+
+ if ((cfg = ast_config_load("udptl.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
+ return;
+
+ udptlstart = 4500;
+ udptlend = 4999;
+ udptlfectype = 0;
+ udptlfecentries = 0;
+ udptlfecspan = 0;
+ udptlmaxdatagram = 0;
+
+ if (cfg) {
+ if ((s = ast_variable_retrieve(cfg, "general", "udptlstart"))) {
+ udptlstart = atoi(s);
+ if (udptlstart < 1024)
+ udptlstart = 1024;
+ if (udptlstart > 65535)
+ udptlstart = 65535;
+ }
+ if ((s = ast_variable_retrieve(cfg, "general", "udptlend"))) {
+ udptlend = atoi(s);
+ if (udptlend < 1024)
+ udptlend = 1024;
+ if (udptlend > 65535)
+ udptlend = 65535;
+ }
+ if ((s = ast_variable_retrieve(cfg, "general", "udptlchecksums"))) {
+#ifdef SO_NO_CHECK
+ if (ast_false(s))
+ nochecksums = 1;
+ else
+ nochecksums = 0;
+#else
+ if (ast_false(s))
+ ast_log(LOG_WARNING, "Disabling UDPTL checksums is not supported on this operating system!\n");
+#endif
+ }
+ if ((s = ast_variable_retrieve(cfg, "general", "T38FaxUdpEC"))) {
+ if (strcmp(s, "t38UDPFEC") == 0)
+ udptlfectype = 2;
+ else if (strcmp(s, "t38UDPRedundancy") == 0)
+ udptlfectype = 1;
+ }
+ if ((s = ast_variable_retrieve(cfg, "general", "T38FaxMaxDatagram"))) {
+ udptlmaxdatagram = atoi(s);
+ if (udptlmaxdatagram < 0)
+ udptlmaxdatagram = 0;
+ if (udptlmaxdatagram > LOCAL_FAX_MAX_DATAGRAM)
+ udptlmaxdatagram = LOCAL_FAX_MAX_DATAGRAM;
+ }
+ if ((s = ast_variable_retrieve(cfg, "general", "UDPTLFECentries"))) {
+ udptlfecentries = atoi(s);
+ if (udptlfecentries < 0)
+ udptlfecentries = 0;
+ if (udptlfecentries > MAX_FEC_ENTRIES)
+ udptlfecentries = MAX_FEC_ENTRIES;
+ }
+ if ((s = ast_variable_retrieve(cfg, "general", "UDPTLFECspan"))) {
+ udptlfecspan = atoi(s);
+ if (udptlfecspan < 0)
+ udptlfecspan = 0;
+ if (udptlfecspan > MAX_FEC_SPAN)
+ udptlfecspan = MAX_FEC_SPAN;
+ }
+ ast_config_destroy(cfg);
+ }
+ if (udptlstart >= udptlend) {
+ ast_log(LOG_WARNING, "Unreasonable values for UDPTL start/end\n");
+ udptlstart = 4500;
+ udptlend = 4999;
+ }
+ ast_verb(2, "UDPTL allocating from port range %d -> %d\n", udptlstart, udptlend);
+}
+
+void ast_udptl_reload(void)
+{
+ __ast_udptl_reload(1);
+}
+
+void ast_udptl_init(void)
+{
+ ast_cli_register_multiple(cli_udptl, sizeof(cli_udptl) / sizeof(struct ast_cli_entry));
+ __ast_udptl_reload(0);
+}
diff --git a/trunk/main/ulaw.c b/trunk/main/ulaw.c
new file mode 100644
index 000000000..18a118f60
--- /dev/null
+++ b/trunk/main/ulaw.c
@@ -0,0 +1,246 @@
+/*
+ * 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.
+ */
+
+/*! \file
+ *
+ * \brief u-Law to Signed linear conversion
+ *
+ * \author Mark Spencer <markster@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/ulaw.h"
+
+#if 0
+/* ZEROTRAP is the military recommendation to improve the encryption
+ * of u-Law traffic. It is irrelevant with modern encryption systems
+ * like AES, and will simply degrade the signal quality.
+ * ZEROTRAP is not implemented in AST_LIN2MU and so the coding table
+ * tests will fail if you use it */
+#define ZEROTRAP /*!< turn on the trap as per the MIL-STD */
+#endif
+
+#define BIAS 0x84 /*!< define the add-in bias for 16 bit samples */
+#define CLIP 32635
+
+#ifndef G711_NEW_ALGORITHM
+
+unsigned char __ast_lin2mu[16384];
+short __ast_mulaw[256];
+
+static unsigned char linear2ulaw(short sample)
+{
+ static int exp_lut[256] = {
+ 0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
+ 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 };
+ int sign, exponent, mantissa;
+ unsigned char ulawbyte;
+
+ /* Get the sample into sign-magnitude. */
+ sign = (sample >> 8) & 0x80; /* set aside the sign */
+ if (sign != 0)
+ sample = -sample; /* get magnitude */
+ if (sample > CLIP)
+ sample = CLIP; /* clip the magnitude */
+
+ /* Convert from 16 bit linear to ulaw. */
+ sample = sample + BIAS;
+ exponent = exp_lut[(sample >> 7) & 0xFF];
+ mantissa = (sample >> (exponent + 3)) & 0x0F;
+ ulawbyte = ~(sign | (exponent << 4) | mantissa);
+
+#ifdef ZEROTRAP
+ if (ulawbyte == 0)
+ ulawbyte = 0x02; /* optional CCITT trap */
+#endif
+
+ return ulawbyte;
+}
+
+#else
+
+unsigned char __ast_lin2mu[AST_ULAW_TAB_SIZE];
+short __ast_mulaw[256];
+
+static unsigned char linear2ulaw(short sample, int full_coding)
+{
+ static const unsigned exp_lut[256] = {
+ 0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
+ 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 };
+ unsigned sign, exponent, mantissa, mag;
+ unsigned char ulawbyte;
+
+ /* Get the sample into sign-magnitude. */
+ ast_ulaw_get_sign_mag(sample, &sign, &mag);
+ if (mag > CLIP)
+ mag = CLIP; /* clip the magnitude */
+
+ sign = (sample >> 8) & 0x80; /* set aside the sign */
+ if (sign != 0)
+ sample = -sample; /* get magnitude */
+ if (sample > CLIP)
+ sample = CLIP; /* clip the magnitude */
+
+ /* Convert from 16 bit linear to ulaw. */
+ mag += BIAS;
+ exponent = exp_lut[(mag >> 7) & 0xFF];
+ mantissa = (mag >> (exponent + 3)) & 0x0F;
+
+ if (full_coding) {
+ /* full encoding, with sign and xform */
+ ulawbyte = ~(sign | (exponent << 4) | mantissa);
+#ifdef ZEROTRAP
+ if (ulawbyte == 0)
+ ulawbyte = 0x02; /* optional CCITT trap */
+#endif
+ } else {
+ /* half-cooked coding -- mantissa+exponent only (for lookup tab) */
+ ulawbyte = (exponent << 4) | mantissa;
+ }
+
+ return ulawbyte;
+}
+
+static inline short ulaw2linear(unsigned char ulawbyte)
+{
+ unsigned exponent, mantissa;
+ short sample;
+ static const short etab[]={0,132,396,924,1980,4092,8316,16764};
+
+ ulawbyte = ~ulawbyte;
+ exponent = (ulawbyte & 0x70) >> 4;
+ mantissa = ulawbyte & 0x0f;
+ sample = mantissa << (exponent + 3);
+ sample += etab[exponent];
+ if (ulawbyte & 0x80)
+ sample = -sample;
+ return sample;
+}
+#endif
+
+/*!
+ * \brief Set up mu-law conversion table
+ */
+void ast_ulaw_init(void)
+{
+ int i;
+
+ /*
+ * Set up mu-law conversion table
+ */
+#ifndef G711_NEW_ALGORITHM
+ for (i = 0;i < 256;i++) {
+ short mu,e,f,y;
+ static short etab[]={0,132,396,924,1980,4092,8316,16764};
+
+ mu = 255-i;
+ e = (mu & 0x70)/16;
+ f = mu & 0x0f;
+ y = f * (1 << (e + 3));
+ y += etab[e];
+ if (mu & 0x80) y = -y;
+ __ast_mulaw[i] = y;
+ }
+ /* set up the reverse (mu-law) conversion table */
+ for (i = -32768; i < 32768; i++) {
+ __ast_lin2mu[((unsigned short)i) >> 2] = linear2ulaw(i);
+ }
+#else
+
+ for (i = 0; i < 256; i++) {
+ __ast_mulaw[i] = ulaw2linear(i);
+ }
+ /* set up the reverse (mu-law) conversion table */
+ for (i = 0; i <= 32768; i += AST_ULAW_STEP) {
+ AST_LIN2MU_LOOKUP(i) = linear2ulaw(i, 0 /* half-cooked */);
+ }
+#endif
+
+#ifdef TEST_CODING_TABLES
+ for (i = -32768; i < 32768; ++i) {
+#ifndef G711_NEW_ALGORITHM
+ unsigned char e1 = linear2ulaw(i);
+#else
+ unsigned char e1 = linear2ulaw(i, 1);
+#endif
+ short d1 = ulaw2linear(e1);
+ unsigned char e2 = AST_LIN2MU(i);
+ short d2 = ulaw2linear(e2);
+ short d3 = AST_MULAW(e1);
+
+ if (e1 != e2 || d1 != d3 || d2 != d3) {
+ ast_log(LOG_WARNING, "u-Law coding tables test failed on %d: e1=%u, e2=%u, d1=%d, d2=%d\n",
+ i, (unsigned)e1, (unsigned)e2, (int)d1, (int)d2);
+ }
+ }
+ ast_log(LOG_NOTICE, "u-Law coding table test complete.\n");
+#endif /* TEST_CODING_TABLES */
+
+#ifdef TEST_TANDEM_TRANSCODING
+ /* tandem transcoding test */
+ for (i = -32768; i < 32768; ++i) {
+ unsigned char e1 = AST_LIN2MU(i);
+ short d1 = AST_MULAW(e1);
+ unsigned char e2 = AST_LIN2MU(d1);
+ short d2 = AST_MULAW(e2);
+ unsigned char e3 = AST_LIN2MU(d2);
+ short d3 = AST_MULAW(e3);
+
+ if (i < 0 && e1 == 0x7f && e2 == 0xff && e3 == 0xff)
+ continue; /* known and normal negative 0 case */
+
+ if (e1 != e2 || e2 != e3 || d1 != d2 || d2 != d3) {
+ ast_log(LOG_WARNING, "u-Law tandem transcoding test failed on %d: e1=%u, e2=%u, d1=%d, d2=%d, d3=%d\n",
+ i, (unsigned)e1, (unsigned)e2, (int)d1, (int)d2, (int)d3);
+ }
+ }
+ ast_log(LOG_NOTICE, "u-Law tandem transcoding test complete.\n");
+#endif /* TEST_TANDEM_TRANSCODING */
+}
+
diff --git a/trunk/main/utils.c b/trunk/main/utils.c
new file mode 100644
index 000000000..1b91781fa
--- /dev/null
+++ b/trunk/main/utils.c
@@ -0,0 +1,1557 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006, Digium, Inc.
+ *
+ * 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 Utility functions
+ *
+ * \note These are important for portability and security,
+ * so please use them in favour of other routines.
+ * Please consult the CODING GUIDELINES for more information.
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <ctype.h>
+#include <sys/stat.h>
+
+#ifdef HAVE_DEV_URANDOM
+#include <fcntl.h>
+#endif
+
+#include "asterisk/network.h"
+
+#define AST_API_MODULE /* ensure that inlinable API functions will be built in lock.h if required */
+#include "asterisk/lock.h"
+#include "asterisk/io.h"
+#include "asterisk/md5.h"
+#include "asterisk/sha1.h"
+#include "asterisk/cli.h"
+#include "asterisk/linkedlists.h"
+
+#define AST_API_MODULE /* ensure that inlinable API functions will be built in this module if required */
+#include "asterisk/strings.h"
+
+#define AST_API_MODULE /* ensure that inlinable API functions will be built in this module if required */
+#include "asterisk/time.h"
+
+#define AST_API_MODULE /* ensure that inlinable API functions will be built in this module if required */
+#include "asterisk/stringfields.h"
+
+#define AST_API_MODULE /* ensure that inlinable API functions will be built in this module if required */
+#include "asterisk/utils.h"
+
+#define AST_API_MODULE
+#include "asterisk/threadstorage.h"
+
+static char base64[64];
+static char b2a[256];
+
+AST_THREADSTORAGE(inet_ntoa_buf);
+
+#if !defined(HAVE_GETHOSTBYNAME_R_5) && !defined(HAVE_GETHOSTBYNAME_R_6)
+
+#define ERANGE 34 /*!< duh? ERANGE value copied from web... */
+#undef gethostbyname
+
+AST_MUTEX_DEFINE_STATIC(__mutex);
+
+/*! \brief Reentrant replacement for gethostbyname for BSD-based systems.
+\note This
+routine is derived from code originally written and placed in the public
+domain by Enzo Michelangeli <em@em.no-ip.com> */
+
+static int gethostbyname_r (const char *name, struct hostent *ret, char *buf,
+ size_t buflen, struct hostent **result,
+ int *h_errnop)
+{
+ int hsave;
+ struct hostent *ph;
+ ast_mutex_lock(&__mutex); /* begin critical area */
+ hsave = h_errno;
+
+ ph = gethostbyname(name);
+ *h_errnop = h_errno; /* copy h_errno to *h_herrnop */
+ if (ph == NULL) {
+ *result = NULL;
+ } else {
+ char **p, **q;
+ char *pbuf;
+ int nbytes=0;
+ int naddr=0, naliases=0;
+ /* determine if we have enough space in buf */
+
+ /* count how many addresses */
+ for (p = ph->h_addr_list; *p != 0; p++) {
+ nbytes += ph->h_length; /* addresses */
+ nbytes += sizeof(*p); /* pointers */
+ naddr++;
+ }
+ nbytes += sizeof(*p); /* one more for the terminating NULL */
+
+ /* count how many aliases, and total length of strings */
+ for (p = ph->h_aliases; *p != 0; p++) {
+ nbytes += (strlen(*p)+1); /* aliases */
+ nbytes += sizeof(*p); /* pointers */
+ naliases++;
+ }
+ nbytes += sizeof(*p); /* one more for the terminating NULL */
+
+ /* here nbytes is the number of bytes required in buffer */
+ /* as a terminator must be there, the minimum value is ph->h_length */
+ if (nbytes > buflen) {
+ *result = NULL;
+ ast_mutex_unlock(&__mutex); /* end critical area */
+ return ERANGE; /* not enough space in buf!! */
+ }
+
+ /* There is enough space. Now we need to do a deep copy! */
+ /* Allocation in buffer:
+ from [0] to [(naddr-1) * sizeof(*p)]:
+ pointers to addresses
+ at [naddr * sizeof(*p)]:
+ NULL
+ from [(naddr+1) * sizeof(*p)] to [(naddr+naliases) * sizeof(*p)] :
+ pointers to aliases
+ at [(naddr+naliases+1) * sizeof(*p)]:
+ NULL
+ then naddr addresses (fixed length), and naliases aliases (asciiz).
+ */
+
+ *ret = *ph; /* copy whole structure (not its address!) */
+
+ /* copy addresses */
+ q = (char **)buf; /* pointer to pointers area (type: char **) */
+ ret->h_addr_list = q; /* update pointer to address list */
+ pbuf = buf + ((naddr + naliases + 2) * sizeof(*p)); /* skip that area */
+ for (p = ph->h_addr_list; *p != 0; p++) {
+ memcpy(pbuf, *p, ph->h_length); /* copy address bytes */
+ *q++ = pbuf; /* the pointer is the one inside buf... */
+ pbuf += ph->h_length; /* advance pbuf */
+ }
+ *q++ = NULL; /* address list terminator */
+
+ /* copy aliases */
+ ret->h_aliases = q; /* update pointer to aliases list */
+ for (p = ph->h_aliases; *p != 0; p++) {
+ strcpy(pbuf, *p); /* copy alias strings */
+ *q++ = pbuf; /* the pointer is the one inside buf... */
+ pbuf += strlen(*p); /* advance pbuf */
+ *pbuf++ = 0; /* string terminator */
+ }
+ *q++ = NULL; /* terminator */
+
+ strcpy(pbuf, ph->h_name); /* copy alias strings */
+ ret->h_name = pbuf;
+ pbuf += strlen(ph->h_name); /* advance pbuf */
+ *pbuf++ = 0; /* string terminator */
+
+ *result = ret; /* and let *result point to structure */
+
+ }
+ h_errno = hsave; /* restore h_errno */
+ ast_mutex_unlock(&__mutex); /* end critical area */
+
+ return (*result == NULL); /* return 0 on success, non-zero on error */
+}
+
+
+#endif
+
+/*! \brief Re-entrant (thread safe) version of gethostbyname that replaces the
+ standard gethostbyname (which is not thread safe)
+*/
+struct hostent *ast_gethostbyname(const char *host, struct ast_hostent *hp)
+{
+ int res;
+ int herrno;
+ int dots=0;
+ const char *s;
+ struct hostent *result = NULL;
+ /* Although it is perfectly legitimate to lookup a pure integer, for
+ the sake of the sanity of people who like to name their peers as
+ integers, we break with tradition and refuse to look up a
+ pure integer */
+ s = host;
+ res = 0;
+ while (s && *s) {
+ if (*s == '.')
+ dots++;
+ else if (!isdigit(*s))
+ break;
+ s++;
+ }
+ if (!s || !*s) {
+ /* Forge a reply for IP's to avoid octal IP's being interpreted as octal */
+ if (dots != 3)
+ return NULL;
+ memset(hp, 0, sizeof(struct ast_hostent));
+ hp->hp.h_addrtype = AF_INET;
+ hp->hp.h_addr_list = (void *) hp->buf;
+ hp->hp.h_addr = hp->buf + sizeof(void *);
+ if (inet_pton(AF_INET, host, hp->hp.h_addr) > 0)
+ return &hp->hp;
+ return NULL;
+
+ }
+#ifdef HAVE_GETHOSTBYNAME_R_5
+ result = gethostbyname_r(host, &hp->hp, hp->buf, sizeof(hp->buf), &herrno);
+
+ if (!result || !hp->hp.h_addr_list || !hp->hp.h_addr_list[0])
+ return NULL;
+#else
+ res = gethostbyname_r(host, &hp->hp, hp->buf, sizeof(hp->buf), &result, &herrno);
+
+ if (res || !result || !hp->hp.h_addr_list || !hp->hp.h_addr_list[0])
+ return NULL;
+#endif
+ return &hp->hp;
+}
+
+
+
+AST_MUTEX_DEFINE_STATIC(test_lock);
+AST_MUTEX_DEFINE_STATIC(test_lock2);
+static pthread_t test_thread;
+static int lock_count = 0;
+static int test_errors = 0;
+
+/*! \brief This is a regression test for recursive mutexes.
+ test_for_thread_safety() will return 0 if recursive mutex locks are
+ working properly, and non-zero if they are not working properly. */
+static void *test_thread_body(void *data)
+{
+ ast_mutex_lock(&test_lock);
+ lock_count += 10;
+ if (lock_count != 10)
+ test_errors++;
+ ast_mutex_lock(&test_lock);
+ lock_count += 10;
+ if (lock_count != 20)
+ test_errors++;
+ ast_mutex_lock(&test_lock2);
+ ast_mutex_unlock(&test_lock);
+ lock_count -= 10;
+ if (lock_count != 10)
+ test_errors++;
+ ast_mutex_unlock(&test_lock);
+ lock_count -= 10;
+ ast_mutex_unlock(&test_lock2);
+ if (lock_count != 0)
+ test_errors++;
+ return NULL;
+}
+
+int test_for_thread_safety(void)
+{
+ ast_mutex_lock(&test_lock2);
+ ast_mutex_lock(&test_lock);
+ lock_count += 1;
+ ast_mutex_lock(&test_lock);
+ lock_count += 1;
+ ast_pthread_create(&test_thread, NULL, test_thread_body, NULL);
+ usleep(100);
+ if (lock_count != 2)
+ test_errors++;
+ ast_mutex_unlock(&test_lock);
+ lock_count -= 1;
+ usleep(100);
+ if (lock_count != 1)
+ test_errors++;
+ ast_mutex_unlock(&test_lock);
+ lock_count -= 1;
+ if (lock_count != 0)
+ test_errors++;
+ ast_mutex_unlock(&test_lock2);
+ usleep(100);
+ if (lock_count != 0)
+ test_errors++;
+ pthread_join(test_thread, NULL);
+ return(test_errors); /* return 0 on success. */
+}
+
+/*! \brief Produce 32 char MD5 hash of value. */
+void ast_md5_hash(char *output, char *input)
+{
+ struct MD5Context md5;
+ unsigned char digest[16];
+ char *ptr;
+ int x;
+
+ MD5Init(&md5);
+ MD5Update(&md5, (unsigned char *)input, strlen(input));
+ MD5Final(digest, &md5);
+ ptr = output;
+ for (x = 0; x < 16; x++)
+ ptr += sprintf(ptr, "%2.2x", digest[x]);
+}
+
+/*! \brief Produce 40 char SHA1 hash of value. */
+void ast_sha1_hash(char *output, char *input)
+{
+ struct SHA1Context sha;
+ char *ptr;
+ int x;
+ uint8_t Message_Digest[20];
+
+ SHA1Reset(&sha);
+
+ SHA1Input(&sha, (const unsigned char *) input, strlen(input));
+
+ SHA1Result(&sha, Message_Digest);
+ ptr = output;
+ for (x = 0; x < 20; x++)
+ ptr += sprintf(ptr, "%2.2x", Message_Digest[x]);
+}
+
+/*! \brief decode BASE64 encoded text */
+int ast_base64decode(unsigned char *dst, const char *src, int max)
+{
+ int cnt = 0;
+ unsigned int byte = 0;
+ unsigned int bits = 0;
+ int incnt = 0;
+ while (*src && (cnt < max)) {
+ /* Shift in 6 bits of input */
+ byte <<= 6;
+ byte |= (b2a[(int)(*src)]) & 0x3f;
+ bits += 6;
+ src++;
+ incnt++;
+ /* If we have at least 8 bits left over, take that character
+ off the top */
+ if (bits >= 8) {
+ bits -= 8;
+ *dst = (byte >> bits) & 0xff;
+ dst++;
+ cnt++;
+ }
+ }
+ /* Dont worry about left over bits, they're extra anyway */
+ return cnt;
+}
+
+/*! \brief encode text to BASE64 coding */
+int ast_base64encode_full(char *dst, const unsigned char *src, int srclen, int max, int linebreaks)
+{
+ int cnt = 0;
+ int col = 0;
+ unsigned int byte = 0;
+ int bits = 0;
+ int cntin = 0;
+ /* Reserve space for null byte at end of string */
+ max--;
+ while ((cntin < srclen) && (cnt < max)) {
+ byte <<= 8;
+ byte |= *(src++);
+ bits += 8;
+ cntin++;
+ if ((bits == 24) && (cnt + 4 <= max)) {
+ *dst++ = base64[(byte >> 18) & 0x3f];
+ *dst++ = base64[(byte >> 12) & 0x3f];
+ *dst++ = base64[(byte >> 6) & 0x3f];
+ *dst++ = base64[byte & 0x3f];
+ cnt += 4;
+ col += 4;
+ bits = 0;
+ byte = 0;
+ }
+ if (linebreaks && (cnt < max) && (col == 64)) {
+ *dst++ = '\n';
+ cnt++;
+ col = 0;
+ }
+ }
+ if (bits && (cnt + 4 <= max)) {
+ /* Add one last character for the remaining bits,
+ padding the rest with 0 */
+ byte <<= 24 - bits;
+ *dst++ = base64[(byte >> 18) & 0x3f];
+ *dst++ = base64[(byte >> 12) & 0x3f];
+ if (bits == 16)
+ *dst++ = base64[(byte >> 6) & 0x3f];
+ else
+ *dst++ = '=';
+ *dst++ = '=';
+ cnt += 4;
+ }
+ if (linebreaks && (cnt < max)) {
+ *dst++ = '\n';
+ cnt++;
+ }
+ *dst = '\0';
+ return cnt;
+}
+
+int ast_base64encode(char *dst, const unsigned char *src, int srclen, int max)
+{
+ return ast_base64encode_full(dst, src, srclen, max, 0);
+}
+
+static void base64_init(void)
+{
+ int x;
+ memset(b2a, -1, sizeof(b2a));
+ /* Initialize base-64 Conversion table */
+ for (x = 0; x < 26; x++) {
+ /* A-Z */
+ base64[x] = 'A' + x;
+ b2a['A' + x] = x;
+ /* a-z */
+ base64[x + 26] = 'a' + x;
+ b2a['a' + x] = x + 26;
+ /* 0-9 */
+ if (x < 10) {
+ base64[x + 52] = '0' + x;
+ b2a['0' + x] = x + 52;
+ }
+ }
+ base64[62] = '+';
+ base64[63] = '/';
+ b2a[(int)'+'] = 62;
+ b2a[(int)'/'] = 63;
+}
+
+/*! \brief ast_uri_encode: Turn text string to URI-encoded %XX version
+\note At this point, we're converting from ISO-8859-x (8-bit), not UTF8
+ as in the SIP protocol spec
+ If doreserved == 1 we will convert reserved characters also.
+ RFC 2396, section 2.4
+ outbuf needs to have more memory allocated than the instring
+ to have room for the expansion. Every char that is converted
+ is replaced by three ASCII characters.
+
+ Note: The doreserved option is needed for replaces header in
+ SIP transfers.
+*/
+char *ast_uri_encode(const char *string, char *outbuf, int buflen, int doreserved)
+{
+ char *reserved = ";/?:@&=+$, "; /* Reserved chars */
+
+ const char *ptr = string; /* Start with the string */
+ char *out = NULL;
+ char *buf = NULL;
+
+ ast_copy_string(outbuf, string, buflen);
+
+ /* If there's no characters to convert, just go through and don't do anything */
+ while (*ptr) {
+ if ((*ptr < 32 || (unsigned char) *ptr) > 127 || (doreserved && strchr(reserved, *ptr)) ) {
+ /* Oops, we need to start working here */
+ if (!buf) {
+ buf = outbuf;
+ out = buf + (ptr - string) ; /* Set output ptr */
+ }
+ out += sprintf(out, "%%%02x", (unsigned char) *ptr);
+ } else if (buf) {
+ *out = *ptr; /* Continue copying the string */
+ out++;
+ }
+ ptr++;
+ }
+ if (buf)
+ *out = '\0';
+ return outbuf;
+}
+
+/*! \brief ast_uri_decode: Decode SIP URI, URN, URL (overwrite the string) */
+void ast_uri_decode(char *s)
+{
+ char *o;
+ unsigned int tmp;
+
+ for (o = s; *s; s++, o++) {
+ if (*s == '%' && strlen(s) > 2 && sscanf(s + 1, "%2x", &tmp) == 1) {
+ /* have '%', two chars and correct parsing */
+ *o = tmp;
+ s += 2; /* Will be incremented once more when we break out */
+ } else /* all other cases, just copy */
+ *o = *s;
+ }
+ *o = '\0';
+}
+
+/*! \brief ast_inet_ntoa: Recursive thread safe replacement of inet_ntoa */
+const char *ast_inet_ntoa(struct in_addr ia)
+{
+ char *buf;
+
+ if (!(buf = ast_threadstorage_get(&inet_ntoa_buf, INET_ADDRSTRLEN)))
+ return "";
+
+ return inet_ntop(AF_INET, &ia, buf, INET_ADDRSTRLEN);
+}
+
+#ifdef HAVE_DEV_URANDOM
+static int dev_urandom_fd;
+#endif
+
+#ifndef __linux__
+#undef pthread_create /* For ast_pthread_create function only */
+#endif /* !__linux__ */
+
+#if !defined(LOW_MEMORY)
+
+#ifdef DEBUG_THREADS
+
+/*! \brief A reasonable maximum number of locks a thread would be holding ... */
+#define AST_MAX_LOCKS 64
+
+/* Allow direct use of pthread_mutex_t and friends */
+#undef pthread_mutex_t
+#undef pthread_mutex_lock
+#undef pthread_mutex_unlock
+#undef pthread_mutex_init
+#undef pthread_mutex_destroy
+
+/*!
+ * \brief Keep track of which locks a thread holds
+ *
+ * There is an instance of this struct for every active thread
+ */
+struct thr_lock_info {
+ /*! The thread's ID */
+ pthread_t thread_id;
+ /*! The thread name which includes where the thread was started */
+ const char *thread_name;
+ /*! This is the actual container of info for what locks this thread holds */
+ struct {
+ const char *file;
+ int line_num;
+ const char *func;
+ const char *lock_name;
+ void *lock_addr;
+ int times_locked;
+ enum ast_lock_type type;
+ /*! This thread is waiting on this lock */
+ int pending:2;
+ } locks[AST_MAX_LOCKS];
+ /*! This is the number of locks currently held by this thread.
+ * The index (num_locks - 1) has the info on the last one in the
+ * locks member */
+ unsigned int num_locks;
+ /*! Protects the contents of the locks member
+ * Intentionally not ast_mutex_t */
+ pthread_mutex_t lock;
+ AST_LIST_ENTRY(thr_lock_info) entry;
+};
+
+/*!
+ * \brief Locked when accessing the lock_infos list
+ */
+AST_MUTEX_DEFINE_STATIC(lock_infos_lock);
+/*!
+ * \brief A list of each thread's lock info
+ */
+static AST_LIST_HEAD_NOLOCK_STATIC(lock_infos, thr_lock_info);
+
+/*!
+ * \brief Destroy a thread's lock info
+ *
+ * This gets called automatically when the thread stops
+ */
+static void lock_info_destroy(void *data)
+{
+ struct thr_lock_info *lock_info = data;
+
+ pthread_mutex_lock(&lock_infos_lock.mutex);
+ AST_LIST_REMOVE(&lock_infos, lock_info, entry);
+ pthread_mutex_unlock(&lock_infos_lock.mutex);
+
+ pthread_mutex_destroy(&lock_info->lock);
+ if (lock_info->thread_name)
+ free((void *) lock_info->thread_name);
+ free(lock_info);
+}
+
+/*!
+ * \brief The thread storage key for per-thread lock info
+ */
+AST_THREADSTORAGE_CUSTOM(thread_lock_info, NULL, lock_info_destroy);
+
+void ast_store_lock_info(enum ast_lock_type type, const char *filename,
+ int line_num, const char *func, const char *lock_name, void *lock_addr)
+{
+ struct thr_lock_info *lock_info;
+ int i;
+
+ if (!(lock_info = ast_threadstorage_get(&thread_lock_info, sizeof(*lock_info))))
+ return;
+
+ pthread_mutex_lock(&lock_info->lock);
+
+ for (i = 0; i < lock_info->num_locks; i++) {
+ if (lock_info->locks[i].lock_addr == lock_addr) {
+ lock_info->locks[i].times_locked++;
+ pthread_mutex_unlock(&lock_info->lock);
+ return;
+ }
+ }
+
+ if (lock_info->num_locks == AST_MAX_LOCKS) {
+ /* Can't use ast_log here, because it will cause infinite recursion */
+ fprintf(stderr, "XXX ERROR XXX A thread holds more locks than '%d'."
+ " Increase AST_MAX_LOCKS!\n", AST_MAX_LOCKS);
+ pthread_mutex_unlock(&lock_info->lock);
+ return;
+ }
+
+ lock_info->locks[i].file = filename;
+ lock_info->locks[i].line_num = line_num;
+ lock_info->locks[i].func = func;
+ lock_info->locks[i].lock_name = lock_name;
+ lock_info->locks[i].lock_addr = lock_addr;
+ lock_info->locks[i].times_locked = 1;
+ lock_info->locks[i].type = type;
+ lock_info->locks[i].pending = 1;
+ lock_info->num_locks++;
+
+ pthread_mutex_unlock(&lock_info->lock);
+}
+
+void ast_mark_lock_acquired(void)
+{
+ struct thr_lock_info *lock_info;
+
+ if (!(lock_info = ast_threadstorage_get(&thread_lock_info, sizeof(*lock_info))))
+ return;
+
+ pthread_mutex_lock(&lock_info->lock);
+ lock_info->locks[lock_info->num_locks - 1].pending = 0;
+ pthread_mutex_unlock(&lock_info->lock);
+}
+
+void ast_mark_lock_failed(void)
+{
+ struct thr_lock_info *lock_info;
+
+ if (!(lock_info = ast_threadstorage_get(&thread_lock_info, sizeof(*lock_info))))
+ return;
+
+ pthread_mutex_lock(&lock_info->lock);
+ lock_info->locks[lock_info->num_locks - 1].pending = -1;
+ lock_info->locks[lock_info->num_locks - 1].times_locked--;
+ pthread_mutex_unlock(&lock_info->lock);
+}
+
+void ast_remove_lock_info(void *lock_addr)
+{
+ struct thr_lock_info *lock_info;
+ int i = 0;
+
+ if (!(lock_info = ast_threadstorage_get(&thread_lock_info, sizeof(*lock_info))))
+ return;
+
+ pthread_mutex_lock(&lock_info->lock);
+
+ for (i = lock_info->num_locks - 1; i >= 0; i--) {
+ if (lock_info->locks[i].lock_addr == lock_addr)
+ break;
+ }
+
+ if (i == -1) {
+ /* Lock not found :( */
+ pthread_mutex_unlock(&lock_info->lock);
+ return;
+ }
+
+ if (lock_info->locks[i].times_locked > 1) {
+ lock_info->locks[i].times_locked--;
+ pthread_mutex_unlock(&lock_info->lock);
+ return;
+ }
+
+ if (i < lock_info->num_locks - 1) {
+ /* Not the last one ... *should* be rare! */
+ memmove(&lock_info->locks[i], &lock_info->locks[i + 1],
+ (lock_info->num_locks - (i + 1)) * sizeof(lock_info->locks[0]));
+ }
+
+ lock_info->num_locks--;
+
+ pthread_mutex_unlock(&lock_info->lock);
+}
+
+static const char *locktype2str(enum ast_lock_type type)
+{
+ switch (type) {
+ case AST_MUTEX:
+ return "MUTEX";
+ case AST_RDLOCK:
+ return "RDLOCK";
+ case AST_WRLOCK:
+ return "WRLOCK";
+ }
+
+ return "UNKNOWN";
+}
+
+static char *handle_show_locks(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct thr_lock_info *lock_info;
+ struct ast_str *str;
+
+ if (!(str = ast_str_create(4096)))
+ return CLI_FAILURE;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "core show locks";
+ e->usage =
+ "Usage: core show locks\n"
+ " This command is for lock debugging. It prints out which locks\n"
+ "are owned by each active thread.\n";
+ return NULL;
+
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ ast_str_append(&str, 0, "\n"
+ "=======================================================================\n"
+ "=== Currently Held Locks ==============================================\n"
+ "=======================================================================\n"
+ "===\n"
+ "=== <file> <line num> <function> <lock name> <lock addr> (times locked)\n"
+ "===\n");
+
+ if (!str)
+ return CLI_FAILURE;
+
+ pthread_mutex_lock(&lock_infos_lock.mutex);
+ AST_LIST_TRAVERSE(&lock_infos, lock_info, entry) {
+ int i;
+ ast_str_append(&str, 0, "=== Thread ID: %d (%s)\n", (int) lock_info->thread_id,
+ lock_info->thread_name);
+ pthread_mutex_lock(&lock_info->lock);
+ for (i = 0; str && i < lock_info->num_locks; i++) {
+ int j;
+ ast_mutex_t *lock;
+
+ ast_str_append(&str, 0, "=== ---> %sLock #%d (%s): %s %d %s %s %p (%d)\n",
+ lock_info->locks[i].pending > 0 ? "Waiting for " :
+ lock_info->locks[i].pending < 0 ? "Tried and failed to get " : "", i,
+ lock_info->locks[i].file,
+ locktype2str(lock_info->locks[i].type),
+ lock_info->locks[i].line_num,
+ lock_info->locks[i].func, lock_info->locks[i].lock_name,
+ lock_info->locks[i].lock_addr,
+ lock_info->locks[i].times_locked);
+
+ if (!lock_info->locks[i].pending || lock_info->locks[i].pending == -1)
+ continue;
+
+ /* We only have further details for mutexes right now */
+ if (lock_info->locks[i].type != AST_MUTEX)
+ continue;
+
+ lock = lock_info->locks[i].lock_addr;
+
+ ast_reentrancy_lock(lock);
+ for (j = 0; str && j < lock->reentrancy; j++) {
+ ast_str_append(&str, 0, "=== --- ---> Locked Here: %s line %d (%s)\n",
+ lock->file[j], lock->lineno[j], lock->func[j]);
+ }
+ ast_reentrancy_unlock(lock);
+ }
+ pthread_mutex_unlock(&lock_info->lock);
+ if (!str)
+ break;
+ ast_str_append(&str, 0, "=== -------------------------------------------------------------------\n"
+ "===\n");
+ if (!str)
+ break;
+ }
+ pthread_mutex_unlock(&lock_infos_lock.mutex);
+
+ if (!str)
+ return CLI_FAILURE;
+
+ ast_str_append(&str, 0, "=======================================================================\n"
+ "\n");
+
+ if (!str)
+ return CLI_FAILURE;
+
+ ast_cli(a->fd, "%s", str->str);
+
+ ast_free(str);
+
+ return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry utils_cli[] = {
+ AST_CLI_DEFINE(handle_show_locks, "Show which locks are held by which thread"),
+};
+
+#endif /* DEBUG_THREADS */
+
+/*
+ * support for 'show threads'. The start routine is wrapped by
+ * dummy_start(), so that ast_register_thread() and
+ * ast_unregister_thread() know the thread identifier.
+ */
+struct thr_arg {
+ void *(*start_routine)(void *);
+ void *data;
+ char *name;
+};
+
+/*
+ * on OS/X, pthread_cleanup_push() and pthread_cleanup_pop()
+ * are odd macros which start and end a block, so they _must_ be
+ * used in pairs (the latter with a '1' argument to call the
+ * handler on exit.
+ * On BSD we don't need this, but we keep it for compatibility.
+ */
+static void *dummy_start(void *data)
+{
+ void *ret;
+ struct thr_arg a = *((struct thr_arg *) data); /* make a local copy */
+#ifdef DEBUG_THREADS
+ struct thr_lock_info *lock_info;
+ pthread_mutexattr_t mutex_attr;
+#endif
+
+ /* note that even though data->name is a pointer to allocated memory,
+ we are not freeing it here because ast_register_thread is going to
+ keep a copy of the pointer and then ast_unregister_thread will
+ free the memory
+ */
+ ast_free(data);
+ ast_register_thread(a.name);
+ pthread_cleanup_push(ast_unregister_thread, (void *) pthread_self());
+
+#ifdef DEBUG_THREADS
+ if (!(lock_info = ast_threadstorage_get(&thread_lock_info, sizeof(*lock_info))))
+ return NULL;
+
+ lock_info->thread_id = pthread_self();
+ lock_info->thread_name = strdup(a.name);
+
+ pthread_mutexattr_init(&mutex_attr);
+ pthread_mutexattr_settype(&mutex_attr, AST_MUTEX_KIND);
+ pthread_mutex_init(&lock_info->lock, &mutex_attr);
+ pthread_mutexattr_destroy(&mutex_attr);
+
+ pthread_mutex_lock(&lock_infos_lock.mutex); /* Intentionally not the wrapper */
+ AST_LIST_INSERT_TAIL(&lock_infos, lock_info, entry);
+ pthread_mutex_unlock(&lock_infos_lock.mutex); /* Intentionally not the wrapper */
+#endif /* DEBUG_THREADS */
+
+ ret = a.start_routine(a.data);
+
+ pthread_cleanup_pop(1);
+
+ return ret;
+}
+
+#endif /* !LOW_MEMORY */
+
+int ast_pthread_create_stack(pthread_t *thread, pthread_attr_t *attr, void *(*start_routine)(void *),
+ void *data, size_t stacksize, const char *file, const char *caller,
+ int line, const char *start_fn)
+{
+#if !defined(LOW_MEMORY)
+ struct thr_arg *a;
+#endif
+
+ if (!attr) {
+ attr = alloca(sizeof(*attr));
+ pthread_attr_init(attr);
+ }
+
+#ifdef __linux__
+ /* On Linux, pthread_attr_init() defaults to PTHREAD_EXPLICIT_SCHED,
+ which is kind of useless. Change this here to
+ PTHREAD_INHERIT_SCHED; that way the -p option to set realtime
+ priority will propagate down to new threads by default.
+ This does mean that callers cannot set a different priority using
+ PTHREAD_EXPLICIT_SCHED in the attr argument; instead they must set
+ the priority afterwards with pthread_setschedparam(). */
+ if ((errno = pthread_attr_setinheritsched(attr, PTHREAD_INHERIT_SCHED)))
+ ast_log(LOG_WARNING, "pthread_attr_setinheritsched: %s\n", strerror(errno));
+#endif
+
+ if (!stacksize)
+ stacksize = AST_STACKSIZE;
+
+ if ((errno = pthread_attr_setstacksize(attr, stacksize ? stacksize : AST_STACKSIZE)))
+ ast_log(LOG_WARNING, "pthread_attr_setstacksize: %s\n", strerror(errno));
+
+#if !defined(LOW_MEMORY)
+ if ((a = ast_malloc(sizeof(*a)))) {
+ a->start_routine = start_routine;
+ a->data = data;
+ start_routine = dummy_start;
+ asprintf(&a->name, "%-20s started at [%5d] %s %s()",
+ start_fn, line, file, caller);
+ data = a;
+ }
+#endif /* !LOW_MEMORY */
+
+ return pthread_create(thread, attr, start_routine, data); /* We're in ast_pthread_create, so it's okay */
+}
+
+
+int ast_pthread_create_detached_stack(pthread_t *thread, pthread_attr_t *attr, void *(*start_routine)(void *),
+ void *data, size_t stacksize, const char *file, const char *caller,
+ int line, const char *start_fn)
+{
+ unsigned char attr_destroy = 0;
+ int res;
+
+ if (!attr) {
+ attr = alloca(sizeof(*attr));
+ pthread_attr_init(attr);
+ attr_destroy = 1;
+ }
+
+ if ((errno = pthread_attr_setdetachstate(attr, PTHREAD_CREATE_DETACHED)))
+ ast_log(LOG_WARNING, "pthread_attr_setdetachstate: %s\n", strerror(errno));
+
+ res = ast_pthread_create_stack(thread, attr, start_routine, data,
+ stacksize, file, caller, line, start_fn);
+
+ if (attr_destroy)
+ pthread_attr_destroy(attr);
+
+ return res;
+}
+
+int ast_wait_for_input(int fd, int ms)
+{
+ struct pollfd pfd[1];
+ memset(pfd, 0, sizeof(pfd));
+ pfd[0].fd = fd;
+ pfd[0].events = POLLIN|POLLPRI;
+ return poll(pfd, 1, ms);
+}
+
+/*!
+ * Try to write string, but wait no more than ms milliseconds before timing out.
+ *
+ * \note The code assumes that the file descriptor has NONBLOCK set,
+ * so there is only one system call made to do a write, unless we actually
+ * have a need to wait. This way, we get better performance.
+ * If the descriptor is blocking, all assumptions on the guaranteed
+ * detail do not apply anymore.
+ * Also note that in the current implementation, the delay is per-write,
+ * so you still have no guarantees, anyways.
+ * Fortunately the routine is only used in a few places (cli.c, manager.c,
+ * res_agi.c) so it is reasonably easy to check how it behaves there.
+ *
+ * XXX We either need to fix the code, or fix the documentation.
+ */
+int ast_carefulwrite(int fd, char *s, int len, int timeoutms)
+{
+ /* Try to write string, but wait no more than ms milliseconds
+ before timing out */
+ int res = 0;
+ struct pollfd fds[1];
+ while (len) {
+ res = write(fd, s, len);
+ if ((res < 0) && (errno != EAGAIN)) {
+ return -1;
+ }
+ if (res < 0)
+ res = 0;
+ len -= res;
+ s += res;
+ res = 0;
+ if (len) {
+ fds[0].fd = fd;
+ fds[0].events = POLLOUT;
+ /* Wait until writable again */
+ res = poll(fds, 1, timeoutms);
+ if (res < 1)
+ return -1;
+ }
+ }
+ return res;
+}
+
+char *ast_strip_quoted(char *s, const char *beg_quotes, const char *end_quotes)
+{
+ char *e;
+ char *q;
+
+ s = ast_strip(s);
+ if ((q = strchr(beg_quotes, *s)) && *q != '\0') {
+ e = s + strlen(s) - 1;
+ if (*e == *(end_quotes + (q - beg_quotes))) {
+ s++;
+ *e = '\0';
+ }
+ }
+
+ return s;
+}
+
+char *ast_unescape_semicolon(char *s)
+{
+ char *e;
+ char *work = s;
+
+ while ((e = strchr(work, ';'))) {
+ if ((e > work) && (*(e-1) == '\\')) {
+ memmove(e - 1, e, strlen(e) + 1);
+ work = e;
+ } else {
+ work = e + 1;
+ }
+ }
+
+ return s;
+}
+
+/* !\brief unescape some C sequences in place, return pointer to the original string.
+ */
+char *ast_unescape_c(char *src)
+{
+ char c, *ret, *dst;
+
+ if (src == NULL)
+ return NULL;
+ for (ret = dst = src; (c = *src++); *dst++ = c ) {
+ if (c != '\\')
+ continue; /* copy char at the end of the loop */
+ switch ((c = *src++)) {
+ case '\0': /* special, trailing '\' */
+ c = '\\';
+ break;
+ case 'b': /* backspace */
+ c = '\b';
+ break;
+ case 'f': /* form feed */
+ c = '\f';
+ break;
+ case 'n':
+ c = '\n';
+ break;
+ case 'r':
+ c = '\r';
+ break;
+ case 't':
+ c = '\t';
+ break;
+ }
+ /* default, use the char literally */
+ }
+ *dst = '\0';
+ return ret;
+}
+
+int ast_build_string_va(char **buffer, size_t *space, const char *fmt, va_list ap)
+{
+ int result;
+
+ if (!buffer || !*buffer || !space || !*space)
+ return -1;
+
+ result = vsnprintf(*buffer, *space, fmt, ap);
+
+ if (result < 0)
+ return -1;
+ else if (result > *space)
+ result = *space;
+
+ *buffer += result;
+ *space -= result;
+ return 0;
+}
+
+int ast_build_string(char **buffer, size_t *space, const char *fmt, ...)
+{
+ va_list ap;
+ int result;
+
+ va_start(ap, fmt);
+ result = ast_build_string_va(buffer, space, fmt, ap);
+ va_end(ap);
+
+ return result;
+}
+
+int ast_true(const char *s)
+{
+ if (ast_strlen_zero(s))
+ return 0;
+
+ /* Determine if this is a true value */
+ if (!strcasecmp(s, "yes") ||
+ !strcasecmp(s, "true") ||
+ !strcasecmp(s, "y") ||
+ !strcasecmp(s, "t") ||
+ !strcasecmp(s, "1") ||
+ !strcasecmp(s, "on"))
+ return -1;
+
+ return 0;
+}
+
+int ast_false(const char *s)
+{
+ if (ast_strlen_zero(s))
+ return 0;
+
+ /* Determine if this is a false value */
+ if (!strcasecmp(s, "no") ||
+ !strcasecmp(s, "false") ||
+ !strcasecmp(s, "n") ||
+ !strcasecmp(s, "f") ||
+ !strcasecmp(s, "0") ||
+ !strcasecmp(s, "off"))
+ return -1;
+
+ return 0;
+}
+
+#define ONE_MILLION 1000000
+/*
+ * put timeval in a valid range. usec is 0..999999
+ * negative values are not allowed and truncated.
+ */
+static struct timeval tvfix(struct timeval a)
+{
+ if (a.tv_usec >= ONE_MILLION) {
+ ast_log(LOG_WARNING, "warning too large timestamp %ld.%ld\n",
+ a.tv_sec, (long int) a.tv_usec);
+ a.tv_sec += a.tv_usec / ONE_MILLION;
+ a.tv_usec %= ONE_MILLION;
+ } else if (a.tv_usec < 0) {
+ ast_log(LOG_WARNING, "warning negative timestamp %ld.%ld\n",
+ a.tv_sec, (long int) a.tv_usec);
+ a.tv_usec = 0;
+ }
+ return a;
+}
+
+struct timeval ast_tvadd(struct timeval a, struct timeval b)
+{
+ /* consistency checks to guarantee usec in 0..999999 */
+ a = tvfix(a);
+ b = tvfix(b);
+ a.tv_sec += b.tv_sec;
+ a.tv_usec += b.tv_usec;
+ if (a.tv_usec >= ONE_MILLION) {
+ a.tv_sec++;
+ a.tv_usec -= ONE_MILLION;
+ }
+ return a;
+}
+
+struct timeval ast_tvsub(struct timeval a, struct timeval b)
+{
+ /* consistency checks to guarantee usec in 0..999999 */
+ a = tvfix(a);
+ b = tvfix(b);
+ a.tv_sec -= b.tv_sec;
+ a.tv_usec -= b.tv_usec;
+ if (a.tv_usec < 0) {
+ a.tv_sec-- ;
+ a.tv_usec += ONE_MILLION;
+ }
+ return a;
+}
+#undef ONE_MILLION
+
+/*! \brief glibc puts a lock inside random(3), so that the results are thread-safe.
+ * BSD libc (and others) do not. */
+
+#ifndef linux
+AST_MUTEX_DEFINE_STATIC(randomlock);
+#endif
+
+long int ast_random(void)
+{
+ long int res;
+#ifdef HAVE_DEV_URANDOM
+ if (dev_urandom_fd >= 0) {
+ int read_res = read(dev_urandom_fd, &res, sizeof(res));
+ if (read_res > 0) {
+ long int rm = RAND_MAX;
+ res = res < 0 ? ~res : res;
+ rm++;
+ return res % rm;
+ }
+ }
+#endif
+#ifdef linux
+ res = random();
+#else
+ ast_mutex_lock(&randomlock);
+ res = random();
+ ast_mutex_unlock(&randomlock);
+#endif
+ return res;
+}
+
+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 ast_join(char *s, size_t len, char * const w[])
+{
+ int x, ofs = 0;
+ const char *src;
+
+ /* Join words into a string */
+ if (!s)
+ return;
+ for (x = 0; ofs < len && w[x]; x++) {
+ if (x > 0)
+ s[ofs++] = ' ';
+ for (src = w[x]; *src && ofs < len; src++)
+ s[ofs++] = *src;
+ }
+ if (ofs == len)
+ ofs--;
+ s[ofs] = '\0';
+}
+
+/*
+ * stringfields support routines.
+ */
+
+const char __ast_string_field_empty[] = ""; /*!< the empty string */
+
+/*! \brief add a new block to the pool.
+ * We can only allocate from the topmost pool, so the
+ * fields in *mgr reflect the size of that only.
+ */
+static int add_string_pool(struct ast_string_field_mgr *mgr,
+ struct ast_string_field_pool **pool_head, size_t size)
+{
+ struct ast_string_field_pool *pool;
+
+ if (!(pool = ast_calloc(1, sizeof(*pool) + size)))
+ return -1;
+
+ pool->prev = *pool_head;
+ *pool_head = pool;
+ mgr->size = size;
+ mgr->used = 0;
+
+ return 0;
+}
+
+/*
+ * This is an internal API, code should not use it directly.
+ * It initializes all fields as empty, then uses 'size' for 3 functions:
+ * size > 0 means initialize the pool list with a pool of given size.
+ * This must be called right after allocating the object.
+ * size = 0 means release all pools except the most recent one.
+ * This is useful to e.g. reset an object to the initial value.
+ * size < 0 means release all pools.
+ * This must be done before destroying the object.
+ */
+int __ast_string_field_init(struct ast_string_field_mgr *mgr,
+ struct ast_string_field_pool **pool_head, int size)
+{
+ const char **p = (const char **)pool_head + 1;
+ struct ast_string_field_pool *cur = *pool_head;
+
+ /* clear fields - this is always necessary */
+ while ((struct ast_string_field_mgr *)p != mgr)
+ *p++ = __ast_string_field_empty;
+ if (size > 0) { /* allocate the initial pool */
+ *pool_head = NULL;
+ return add_string_pool(mgr, pool_head, size);
+ }
+ if (size < 0) { /* reset all pools */
+ *pool_head = NULL;
+ } else { /* preserve the first pool */
+ if (cur == NULL) {
+ ast_log(LOG_WARNING, "trying to reset empty pool\n");
+ return -1;
+ }
+ cur = cur->prev;
+ (*pool_head)->prev = NULL;
+ mgr->used = 0;
+ }
+ while (cur) {
+ struct ast_string_field_pool *prev = cur->prev;
+ ast_free(cur);
+ cur = prev;
+ }
+ return 0;
+}
+
+ast_string_field __ast_string_field_alloc_space(struct ast_string_field_mgr *mgr,
+ struct ast_string_field_pool **pool_head, size_t needed)
+{
+ char *result = NULL;
+ size_t space = mgr->size - mgr->used;
+
+ if (__builtin_expect(needed > space, 0)) {
+ size_t new_size = mgr->size * 2;
+
+ while (new_size < needed)
+ new_size *= 2;
+
+ if (add_string_pool(mgr, pool_head, new_size))
+ return NULL;
+ }
+
+ result = (*pool_head)->base + mgr->used;
+ mgr->used += needed;
+ return result;
+}
+
+void __ast_string_field_ptr_build_va(struct ast_string_field_mgr *mgr,
+ struct ast_string_field_pool **pool_head,
+ const ast_string_field *ptr, const char *format, va_list ap1, va_list ap2)
+{
+ size_t needed;
+ char *dst = (*pool_head)->base + mgr->used;
+ const char **p = (const char **)ptr;
+ size_t space = mgr->size - mgr->used;
+
+ /* try to write using available space */
+ needed = vsnprintf(dst, space, format, ap1) + 1;
+
+ va_end(ap1);
+
+ if (needed > space) { /* if it fails, reallocate */
+ size_t new_size = mgr->size * 2;
+
+ while (new_size < needed)
+ new_size *= 2;
+
+ if (add_string_pool(mgr, pool_head, new_size))
+ return;
+
+ dst = (*pool_head)->base + mgr->used;
+ vsprintf(dst, format, ap2);
+ }
+
+ *p = dst;
+ mgr->used += needed;
+}
+
+void __ast_string_field_ptr_build(struct ast_string_field_mgr *mgr,
+ struct ast_string_field_pool **pool_head,
+ const ast_string_field *ptr, const char *format, ...)
+{
+ va_list ap1, ap2;
+
+ va_start(ap1, format);
+ va_start(ap2, format); /* va_copy does not exist on FreeBSD */
+
+ __ast_string_field_ptr_build_va(mgr, pool_head, ptr, format, ap1, ap2);
+
+ va_end(ap1);
+ va_end(ap2);
+}
+/* end of stringfields support */
+
+AST_MUTEX_DEFINE_STATIC(fetchadd_m); /* used for all fetc&add ops */
+
+int ast_atomic_fetchadd_int_slow(volatile int *p, int v)
+{
+ int ret;
+ ast_mutex_lock(&fetchadd_m);
+ ret = *p;
+ *p += v;
+ ast_mutex_unlock(&fetchadd_m);
+ return ret;
+}
+
+/*! \brief
+ * get values from config variables.
+ */
+int ast_get_timeval(const char *src, struct timeval *dst, struct timeval _default, int *consumed)
+{
+ long double dtv = 0.0;
+ int scanned;
+
+ if (dst == NULL)
+ return -1;
+
+ *dst = _default;
+
+ if (ast_strlen_zero(src))
+ return -1;
+
+ /* only integer at the moment, but one day we could accept more formats */
+ if (sscanf(src, "%Lf%n", &dtv, &scanned) > 0) {
+ dst->tv_sec = dtv;
+ dst->tv_usec = (dtv - dst->tv_sec) * 1000000.0;
+ if (consumed)
+ *consumed = scanned;
+ return 0;
+ } else
+ return -1;
+}
+
+/*! \brief
+ * get values from config variables.
+ */
+int ast_get_time_t(const char *src, time_t *dst, time_t _default, int *consumed)
+{
+ long t;
+ int scanned;
+
+ if (dst == NULL)
+ return -1;
+
+ *dst = _default;
+
+ if (ast_strlen_zero(src))
+ return -1;
+
+ /* only integer at the moment, but one day we could accept more formats */
+ if (sscanf(src, "%ld%n", &t, &scanned) == 1) {
+ *dst = t;
+ if (consumed)
+ *consumed = scanned;
+ return 0;
+ } else
+ return -1;
+}
+
+/*!
+ * core handler for dynamic strings.
+ * This is not meant to be called directly, but rather through the
+ * various wrapper macros
+ * ast_str_set(...)
+ * ast_str_append(...)
+ * ast_str_set_va(...)
+ * ast_str_append_va(...)
+ */
+int __ast_str_helper(struct ast_str **buf, size_t max_len,
+ int append, const char *fmt, va_list ap)
+{
+ int res, need;
+ int offset = (append && (*buf)->len) ? (*buf)->used : 0;
+
+ if (max_len < 0)
+ max_len = (*buf)->len; /* don't exceed the allocated space */
+ /*
+ * Ask vsnprintf how much space we need. Remember that vsnprintf
+ * does not count the final '\0' so we must add 1.
+ */
+ res = vsnprintf((*buf)->str + offset, (*buf)->len - offset, fmt, ap);
+
+ need = res + offset + 1;
+ /*
+ * If there is not enough space and we are below the max length,
+ * reallocate the buffer and return a message telling to retry.
+ */
+ if (need > (*buf)->len && (max_len == 0 || (*buf)->len < max_len) ) {
+ if (max_len && max_len < need) /* truncate as needed */
+ need = max_len;
+ else if (max_len == 0) /* if unbounded, give more room for next time */
+ need += 16 + need/4;
+ if (0) /* debugging */
+ ast_verbose("extend from %d to %d\n", (int)(*buf)->len, need);
+ if (ast_str_make_space(buf, need)) {
+ ast_verbose("failed to extend from %d to %d\n", (int)(*buf)->len, need);
+ return AST_DYNSTR_BUILD_FAILED;
+ }
+ (*buf)->str[offset] = '\0'; /* Truncate the partial write. */
+
+ /* va_end() and va_start() must be done before calling
+ * vsnprintf() again. */
+ return AST_DYNSTR_BUILD_RETRY;
+ }
+ /* update space used, keep in mind the truncation */
+ (*buf)->used = (res + offset > (*buf)->len) ? (*buf)->len : res + offset;
+
+ return res;
+}
+
+void ast_enable_packet_fragmentation(int sock)
+{
+#if defined(HAVE_IP_MTU_DISCOVER)
+ int val = IP_PMTUDISC_DONT;
+
+ if (setsockopt(sock, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val)))
+ ast_log(LOG_WARNING, "Unable to disable PMTU discovery. Large UDP packets may fail to be delivered when sent from this socket.\n");
+#endif /* HAVE_IP_MTU_DISCOVER */
+}
+
+int ast_mkdir(const char *path, int mode)
+{
+ char *ptr;
+ int len = strlen(path), count = 0, x, piececount = 0;
+ char *tmp = ast_strdupa(path);
+ char **pieces;
+ char *fullpath = alloca(len + 1);
+ int res = 0;
+
+ for (ptr = tmp; *ptr; ptr++) {
+ if (*ptr == '/')
+ count++;
+ }
+
+ /* Count the components to the directory path */
+ pieces = alloca(count * sizeof(*pieces));
+ for (ptr = tmp; *ptr; ptr++) {
+ if (*ptr == '/') {
+ *ptr = '\0';
+ pieces[piececount++] = ptr + 1;
+ }
+ }
+
+ *fullpath = '\0';
+ for (x = 0; x < piececount; x++) {
+ /* This looks funky, but the buffer is always ideally-sized, so it's fine. */
+ strcat(fullpath, "/");
+ strcat(fullpath, pieces[x]);
+ res = mkdir(fullpath, mode);
+ if (res && errno != EEXIST)
+ return errno;
+ }
+ return 0;
+}
+
+int ast_utils_init(void)
+{
+#ifdef HAVE_DEV_URANDOM
+ dev_urandom_fd = open("/dev/urandom", O_RDONLY);
+#endif
+ base64_init();
+#ifdef DEBUG_THREADS
+ ast_cli_register_multiple(utils_cli, sizeof(utils_cli) / sizeof(utils_cli[0]));
+#endif
+ return 0;
+}